From d5e6b6f8cc404ee5d3c1a08c6d2ef63fa96862fa Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 21 Mar 2025 19:42:17 +0100 Subject: [PATCH 0001/1900] core: simplify `Extend` for tuples This is an alternative to #137400. The current macro is incredibly complicated and introduces subtle bugs like calling the `extend_one` of the individual collections in backwards order. This PR drastically simplifies the macro by removing recursion and moving the specialization out of the macro. It also fixes the ordering issue described above (I've stolen the test of the new behaviour from #137400). Additionally, the 1-tuple is now special-cased to allow taking advantage of the well-optimized `Extend` implementations of the individual collection. --- library/core/src/iter/traits/collect.rs | 432 +++++++++++++----------- 1 file changed, 236 insertions(+), 196 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 97bb21c8a36e8..b4ab81d15a330 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -459,234 +459,274 @@ impl Extend<()> for () { fn extend_one(&mut self, _item: ()) {} } -macro_rules! spec_tuple_impl { - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - ) => { - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(fake_variadic)] - #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ - 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ - 1.85.0."] - => ($ty_name, $var_name, $extend_ty_name, $cnt), - ); - }; - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, - $trait_names:ident, $default_fn_names:ident, $cnts:tt - ), - )* - ) => { - spec_tuple_impl!( - $( - ( - $ty_names, $var_names, $extend_ty_names, - $trait_names, $default_fn_names, $cnts - ), - )* - ); - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(hidden)] - => ( - $ty_name, $var_name, $extend_ty_name, $cnt - ), - $( - ( - $ty_names, $var_names, $extend_ty_names, $cnts - ), - )* - ); - }; - ( - $trait_name:ident, $default_fn_name:ident, #[$meta:meta] - $(#[$doctext:meta])? => $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt - ), - )* - ) => { - #[$meta] - $(#[$doctext])? - #[stable(feature = "extend_for_tuple", since = "1.56.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> Extend<($($ty_names,)*)> for ($($extend_ty_names,)*) - where - $($extend_ty_names: Extend<$ty_names>,)* - { - /// Allows to `extend` a tuple of collections that also implement `Extend`. - /// - /// See also: [`Iterator::unzip`] - /// - /// # Examples - /// ``` - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend([(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, [0, 2, 4, 6]); - /// assert_eq!(tuple.1, [1, 3, 5, 7]); - /// - /// // also allows for arbitrarily nested tuples as elements - /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); - /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); - /// - /// let (a, (b, c)) = nested_tuple; - /// assert_eq!(a, [1, 4, 7]); - /// assert_eq!(b, [2, 5, 8]); - /// assert_eq!(c, [3, 6, 9]); - /// ``` - fn extend>(&mut self, into_iter: T) { - let ($($var_names,)*) = self; - let iter = into_iter.into_iter(); - $trait_name::extend(iter, $($var_names,)*); - } +/// This trait is implemented for tuples up to twelve items long. The `impl`s for +/// 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in 1.85.0. +#[doc(fake_variadic)] // the other implementations are below. +#[stable(feature = "extend_for_tuple", since = "1.56.0")] +impl Extend<(T,)> for (ExtendT,) +where + ExtendT: Extend, +{ + /// Allows to `extend` a tuple of collections that also implement `Extend`. + /// + /// See also: [`Iterator::unzip`] + /// + /// # Examples + /// ``` + /// // Example given for a 2-tuple, but 1- through 12-tuples are supported + /// let mut tuple = (vec![0], vec![1]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); + /// + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); + /// + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); + /// ``` + fn extend>(&mut self, iter: I) { + self.0.extend(iter.into_iter().map(|t| t.0)); + } - fn extend_one(&mut self, item: ($($ty_names,)*)) { - $(self.$cnts.extend_one(item.$cnts);)* - } + fn extend_one(&mut self, item: (T,)) { + self.0.extend_one(item.0) + } - fn extend_reserve(&mut self, additional: usize) { - $(self.$cnts.extend_reserve(additional);)* - } + fn extend_reserve(&mut self, additional: usize) { + self.0.extend_reserve(additional) + } - unsafe fn extend_one_unchecked(&mut self, item: ($($ty_names,)*)) { - // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. - unsafe { - $(self.$cnts.extend_one_unchecked(item.$cnts);)* - } - } - } + unsafe fn extend_one_unchecked(&mut self, item: (T,)) { + // SAFETY: the caller guarantees all preconditions. + unsafe { self.0.extend_one_unchecked(item.0) } + } +} - trait $trait_name<$($ty_names),*> { - fn extend(self, $($var_names: &mut $ty_names,)*); - } +/// This implementation turns an iterator of tuples into a tuple of types which implement +/// [`Default`] and [`Extend`]. +/// +/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] +/// implementations: +/// +/// ```rust +/// # fn main() -> Result<(), core::num::ParseIntError> { +/// let string = "1,2,123,4"; +/// +/// // Example given for a 2-tuple, but 1- through 12-tuples are supported +/// let (numbers, lengths): (Vec<_>, Vec<_>) = string +/// .split(',') +/// .map(|s| s.parse().map(|n: u32| (n, s.len()))) +/// .collect::>()?; +/// +/// assert_eq!(numbers, [1, 2, 123, 4]); +/// assert_eq!(lengths, [1, 1, 3, 1]); +/// # Ok(()) } +/// ``` +#[doc(fake_variadic)] // the other implementations are below. +#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] +impl FromIterator<(T,)> for (ExtendT,) +where + ExtendT: Default + Extend, +{ + fn from_iter>(iter: Iter) -> Self { + let mut res = ExtendT::default(); + res.extend(iter.into_iter().map(|t| t.0)); + (res,) + } +} - fn $default_fn_name<$($ty_names,)* $($extend_ty_names,)*>( - iter: impl Iterator, - $($var_names: &mut $extend_ty_names,)* - ) where - $($extend_ty_names: Extend<$ty_names>,)* - { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - move |(), ($($extend_ty_names,)*)| { - $($var_names.extend_one($extend_ty_names);)* - } - } +/// An implementation of [`extend`](Extend::extend) that calls `extend_one` or +/// `extend_one_unchecked` for each element of the iterator. +fn default_extend(collection: &mut ExtendT, iter: I) +where + ExtendT: Extend, + I: IntoIterator, +{ + // Specialize on `TrustedLen` and call `extend_one_unchecked` where + // applicable. + trait SpecExtend { + fn extend(&mut self, iter: I); + } + // Extracting these to separate functions avoid monomorphising the closures + // for every iterator type. + fn extender(collection: &mut ExtendT) -> impl FnMut(T) + use<'_, ExtendT, T> + where + ExtendT: Extend, + { + move |item| collection.extend_one(item) + } + + unsafe fn unchecked_extender( + collection: &mut ExtendT, + ) -> impl FnMut(T) + use<'_, ExtendT, T> + where + ExtendT: Extend, + { + // SAFETY: we make sure that there is enough space at the callsite of + // this function. + move |item| unsafe { collection.extend_one_unchecked(item) } + } + + impl SpecExtend for ExtendT + where + ExtendT: Extend, + I: Iterator, + { + default fn extend(&mut self, iter: I) { let (lower_bound, _) = iter.size_hint(); if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* + self.extend_reserve(lower_bound); } - iter.fold((), extend($($var_names,)*)); + iter.for_each(extender(self)) } + } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: Iterator, - { - default fn extend(self, $($var_names: &mut $extend_ty_names),*) { - $default_fn_name(self, $($var_names),*); + impl SpecExtend for ExtendT + where + ExtendT: Extend, + I: TrustedLen, + { + fn extend(&mut self, iter: I) { + let (lower_bound, upper_bound) = iter.size_hint(); + if lower_bound > 0 { + self.extend_reserve(lower_bound); + } + + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + iter.for_each(extender(self)) + } else { + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is + // `TrustedLen` so its `size_hint` is exact. + iter.for_each(unsafe { unchecked_extender(self) }) } } + } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter + SpecExtend::extend(collection, iter.into_iter()); +} + +// Implements `Extend` and `FromIterator` for tuples with length larger than one. +macro_rules! impl_extend_tuple { + ($(($ty:tt, $extend_ty:tt, $index:tt)),+) => { + #[doc(hidden)] + #[stable(feature = "extend_for_tuple", since = "1.56.0")] + impl<$($ty,)+ $($extend_ty,)+> Extend<($($ty,)+)> for ($($extend_ty,)+) where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: TrustedLen, + $($extend_ty: Extend<$ty>,)+ { - fn extend(self, $($var_names: &mut $extend_ty_names,)*) { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - // SAFETY: We reserve enough space for the `size_hint`, and the iterator is - // `TrustedLen` so its `size_hint` is exact. - move |(), ($($extend_ty_names,)*)| unsafe { - $($var_names.extend_one_unchecked($extend_ty_names);)* - } - } + fn extend>(&mut self, iter: T) { + default_extend(self, iter) + } - let (lower_bound, upper_bound) = self.size_hint(); + fn extend_one(&mut self, item: ($($ty,)+)) { + $(self.$index.extend_one(item.$index);)+ + } - if upper_bound.is_none() { - // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. - $default_fn_name(self, $($var_names,)*); - return; - } + fn extend_reserve(&mut self, additional: usize) { + $(self.$index.extend_reserve(additional);)+ + } - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* + unsafe fn extend_one_unchecked(&mut self, item: ($($ty,)+)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + $(self.$index.extend_one_unchecked(item.$index);)+ } - - self.fold((), extend($($var_names,)*)); } } - /// This implementation turns an iterator of tuples into a tuple of types which implement - /// [`Default`] and [`Extend`]. - /// - /// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] - /// implementations: - /// - /// ```rust - /// # fn main() -> Result<(), core::num::ParseIntError> { - /// let string = "1,2,123,4"; - /// - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let (numbers, lengths): (Vec<_>, Vec<_>) = string - /// .split(',') - /// .map(|s| s.parse().map(|n: u32| (n, s.len()))) - /// .collect::>()?; - /// - /// assert_eq!(numbers, [1, 2, 123, 4]); - /// assert_eq!(lengths, [1, 1, 3, 1]); - /// # Ok(()) } - /// ``` - #[$meta] - $(#[$doctext])? + #[doc(hidden)] #[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*) + impl<$($ty,)+ $($extend_ty,)+> FromIterator<($($ty,)+)> for ($($extend_ty,)+) where - $($ty_names: Default + Extend<$extend_ty_names>,)* + $($extend_ty: Default + Extend<$ty>,)+ { - fn from_iter>(iter: Iter) -> Self { - let mut res = <($($ty_names,)*)>::default(); + fn from_iter>(iter: Iter) -> Self { + let mut res = Self::default(); res.extend(iter); - res } } - }; } -spec_tuple_impl!( - (L, l, EL, TraitL, default_extend_tuple_l, 11), - (K, k, EK, TraitK, default_extend_tuple_k, 10), - (J, j, EJ, TraitJ, default_extend_tuple_j, 9), - (I, i, EI, TraitI, default_extend_tuple_i, 8), - (H, h, EH, TraitH, default_extend_tuple_h, 7), - (G, g, EG, TraitG, default_extend_tuple_g, 6), - (F, f, EF, TraitF, default_extend_tuple_f, 5), - (E, e, EE, TraitE, default_extend_tuple_e, 4), - (D, d, ED, TraitD, default_extend_tuple_d, 3), - (C, c, EC, TraitC, default_extend_tuple_c, 2), - (B, b, EB, TraitB, default_extend_tuple_b, 1), - (A, a, EA, TraitA, default_extend_tuple_a, 0), +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3), (E, ExE, 4)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3), (E, ExE, 4), (F, ExF, 5)); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9), + (K, ExK, 10) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9), + (K, ExK, 10), + (L, ExL, 11) ); From af66ece8221eb0a20fa17407182f6d01739bf8c9 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sat, 22 Feb 2025 01:09:51 +0100 Subject: [PATCH 0002/1900] Add tests for `Extend<(T, U)> for (ExtendT, ExtendU)` ordering of side-effects to `coretest`. --- .../coretests/tests/iter/traits/iterator.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index e31d2e15b6d7e..5ef1f797ae55d 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; +use core::iter::zip; use core::num::NonZero; /// A wrapper struct that implements `Eq` and `Ord` based on the wrapped @@ -642,6 +644,26 @@ fn test_collect_for_tuples() { assert!(e.2 == d); } +#[test] +fn test_extend_for_tuple_side_effects_order() { + struct TrackingExtender<'a, T>(&'static str, &'a RefCell)>>, Vec); + impl Extend for TrackingExtender<'_, T> { + fn extend>(&mut self, i: I) { + let items = Vec::from_iter(i); + self.1.borrow_mut().push((self.0, items.clone())); + self.2.extend(items); + } + } + + let effects = RefCell::new(vec![]); + let l = TrackingExtender("l", &effects, vec![]); + let r = TrackingExtender("r", &effects, vec![]); + let mut p = ((l, r), ()); + p.extend(zip([(1, 2), (3, 4)], [(), ()])); + let effects = effects.into_inner(); + assert_eq!(effects, [("l", vec![1]), ("r", vec![2]), ("l", vec![3]), ("r", vec![4])]); +} + // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe}; From a22c8ed6311979d292e835e5a5498d0e3cc95dc0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sat, 12 Apr 2025 16:33:38 -0700 Subject: [PATCH 0003/1900] zkVM: Fix env::ArgsOs The zkVM implementation of `env::ArgsOs` incorrectly reports the full length even after having iterated. Instead, use a range approach which works out to be simpler. Also, implement more iterator methods like the other platforms in #139847. --- library/std/src/sys/args/zkvm.rs | 71 ++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index 194ba7159d459..e4b1ad540ac04 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,18 +1,19 @@ use crate::ffi::OsString; use crate::fmt; +use crate::num::NonZero; use crate::sys::os_str; use crate::sys::pal::{WORD_SIZE, abi}; use crate::sys_common::FromInner; +#[derive(Clone)] pub struct Args { - i_forward: usize, - i_back: usize, - count: usize, + front: usize, + back: usize, } pub fn args() -> Args { let count = unsafe { abi::sys_argc() }; - Args { i_forward: 0, i_back: 0, count } + Args { front: 0, back: count } } impl Args { @@ -38,44 +39,80 @@ impl Args { } } +impl !Send for Args {} +impl !Sync for Args {} + impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.clone()).finish() } } impl Iterator for Args { type Item = OsString; + #[inline] fn next(&mut self) -> Option { - if self.i_forward >= self.count - self.i_back { + if self.front == self.back { None } else { - let arg = Self::argv(self.i_forward); - self.i_forward += 1; + let arg = Self::argv(self.front); + self.front += 1; Some(arg) } } + #[inline] fn size_hint(&self) -> (usize, Option) { - (self.count, Some(self.count)) + let len = self.len(); + (len, Some(len)) } -} -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.count + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.front += step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) } } impl DoubleEndedIterator for Args { + #[inline] fn next_back(&mut self) -> Option { - if self.i_back >= self.count - self.i_forward { + if self.back == self.front { None } else { - let arg = Self::argv(self.count - 1 - self.i_back); - self.i_back += 1; - Some(arg) + self.back -= 1; + Some(Self::argv(self.back)) } } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.back -= step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.back - self.front + } + + #[inline] + fn is_empty(&self) -> bool { + self.front == self.back + } } From 00095982a0d2f6c8bfb5cb2706338ad2b14304f5 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Tue, 15 Apr 2025 02:41:51 -0700 Subject: [PATCH 0004/1900] zkVM: Cache args from host Retrieve argc/argv from the host once, on demand when the first `env::ArgsOs` is constructed, and globally cache it. Copy each argument to an `OsString` while iterating. --- library/std/src/sys/args/zkvm.rs | 86 ++++++++++++-------------------- 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index e4b1ad540ac04..d26bf1eaff91f 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,26 +1,20 @@ -use crate::ffi::OsString; -use crate::fmt; +use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; -use crate::sys::os_str; +use crate::sync::OnceLock; use crate::sys::pal::{WORD_SIZE, abi}; -use crate::sys_common::FromInner; - -#[derive(Clone)] -pub struct Args { - front: usize, - back: usize, -} +use crate::{fmt, ptr, slice}; pub fn args() -> Args { - let count = unsafe { abi::sys_argc() }; - Args { front: 0, back: count } + Args { iter: ARGS.get_or_init(|| get_args()).iter() } } -impl Args { - /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc - /// and will not return if the index is out of bounds. - fn argv(i: usize) -> OsString { - let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; +fn get_args() -> Vec<&'static OsStr> { + let argc = unsafe { abi::sys_argc() }; + let mut args = Vec::with_capacity(argc); + + for i in 0..argc { + // Get the size of the argument then the data. + let arg_len = unsafe { abi::sys_argv(ptr::null_mut(), 0, i) }; let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; let words = unsafe { abi::sys_alloc_words(arg_len_words) }; @@ -28,15 +22,16 @@ impl Args { let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; debug_assert_eq!(arg_len, arg_len2); - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let arg_bytes: &[u8] = - unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; - OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) + let arg_bytes = unsafe { slice::from_raw_parts(words.cast(), arg_len) }; + args.push(unsafe { OsStr::from_encoded_bytes_unchecked(arg_bytes) }); } + args +} + +static ARGS: OnceLock> = OnceLock::new(); + +pub struct Args { + iter: slice::Iter<'static, &'static OsStr>, } impl !Send for Args {} @@ -44,75 +39,56 @@ impl !Sync for Args {} impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() + self.iter.as_slice().fmt(f) } } impl Iterator for Args { type Item = OsString; - #[inline] fn next(&mut self) -> Option { - if self.front == self.back { - None - } else { - let arg = Self::argv(self.front); - self.front += 1; - Some(arg) - } + self.iter.next().map(|arg| arg.to_os_string()) } #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) + self.iter.size_hint() } #[inline] fn count(self) -> usize { - self.len() + self.iter.len() } - #[inline] - fn last(mut self) -> Option { - self.next_back() + fn last(self) -> Option { + self.iter.last().map(|arg| arg.to_os_string()) } #[inline] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.front += step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_by(n) } } impl DoubleEndedIterator for Args { - #[inline] fn next_back(&mut self) -> Option { - if self.back == self.front { - None - } else { - self.back -= 1; - Some(Self::argv(self.back)) - } + self.iter.next_back().map(|arg| arg.to_os_string()) } #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.back -= step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_back_by(n) } } impl ExactSizeIterator for Args { #[inline] fn len(&self) -> usize { - self.back - self.front + self.iter.len() } #[inline] fn is_empty(&self) -> bool { - self.front == self.back + self.iter.is_empty() } } From 8569bb2576e19ddefc69340ed47744ad15401d46 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Fri, 23 May 2025 13:05:50 +0000 Subject: [PATCH 0005/1900] Add `FromIterator` impls for `ascii::Char`s to `String`s --- library/alloc/src/string.rs | 30 +++++++++++++++++++ ...stion-when-stmt-and-expr-span-equal.stderr | 2 ++ 2 files changed, 32 insertions(+) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 37614a7ca4571..b383d4d1ae517 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2373,6 +2373,28 @@ impl<'a> FromIterator> for String { } } +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl FromIterator for String { + fn from_iter>(iter: T) -> Self { + let buf = iter.into_iter().map(core::ascii::Char::to_u8).collect(); + // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type + // only contains ASCII values (0x00-0x7F), which are valid UTF-8. + unsafe { String::from_utf8_unchecked(buf) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl<'a> FromIterator<&'a core::ascii::Char> for String { + fn from_iter>(iter: T) -> Self { + let buf = iter.into_iter().copied().map(core::ascii::Char::to_u8).collect(); + // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type + // only contains ASCII values (0x00-0x7F), which are valid UTF-8. + unsafe { String::from_utf8_unchecked(buf) } + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Extend for String { @@ -3200,6 +3222,14 @@ impl<'a> FromIterator for Cow<'a, str> { } } +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl<'a> FromIterator for Cow<'a, str> { + fn from_iter>(it: T) -> Self { + Cow::Owned(FromIterator::from_iter(it)) + } +} + #[stable(feature = "from_string_for_vec_u8", since = "1.14.0")] impl From for Vec { /// Converts the given [`String`] to a vector [`Vec`] that holds values of type [`u8`]. diff --git a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr index 4b83bfff86333..aa96159aacf57 100644 --- a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr +++ b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr @@ -21,9 +21,11 @@ LL | .collect::(); | = help: the trait `FromIterator<()>` is not implemented for `String` = help: the following other types implement trait `FromIterator`: + `String` implements `FromIterator<&Char>` `String` implements `FromIterator<&char>` `String` implements `FromIterator<&str>` `String` implements `FromIterator>` + `String` implements `FromIterator` `String` implements `FromIterator>` `String` implements `FromIterator` `String` implements `FromIterator` From ea2ea62fd1577cc67dc61d6b1b40e5f7427ed704 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 4 Jun 2025 13:40:50 +0200 Subject: [PATCH 0006/1900] Note that using `enumerate()` will swap the arguments The autofix now: - includes the swapping of index and element in the closure in which the content will be consumed; - notes, with applicability `MaybeIncorrect` (because it will be), that the element and the index will be swapped. --- .../src/methods/range_zip_with_len.rs | 96 +++++++++++++++++-- clippy_utils/src/sym.rs | 4 + tests/ui/range.fixed | 16 +++- tests/ui/range.rs | 16 +++- tests/ui/range.stderr | 29 +++++- tests/ui/range_unfixable.rs | 14 +++ tests/ui/range_unfixable.stderr | 12 +++ 7 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 tests/ui/range_unfixable.rs create mode 100644 tests/ui/range_unfixable.stderr diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 3a5e321720865..e13df18333e46 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; @@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) { - span_lint_and_sugg( + span_lint_and_then( cx, RANGE_ZIP_WITH_LEN, expr.span, "using `.zip()` with a range and `.len()`", - "try", - format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")), - Applicability::MachineApplicable, + |diag| { + // If the iterator content is consumed by a pattern with exactly two elements, swap + // the order of those elements. Otherwise, the suggestion will be marked as + // `Applicability::MaybeIncorrect` (because it will be), and a note will be added + // to the diagnostic to underline the swapping of the index and the content. + let pat = methods_pattern(cx, expr).or_else(|| for_loop_pattern(cx, expr)); + let invert_bindings = if let Some(pat) = pat + && pat.span.eq_ctxt(expr.span) + && let PatKind::Tuple([first, second], _) = pat.kind + { + Some((first.span, second.span)) + } else { + None + }; + let mut app = Applicability::MachineApplicable; + let mut suggestions = vec![( + expr.span, + format!( + "{}.iter().enumerate()", + snippet_with_applicability(cx, recv.span, "_", &mut app) + ), + )]; + if let Some((left, right)) = invert_bindings + && let Some(snip_left) = left.get_source_text(cx) + && let Some(snip_right) = right.get_source_text(cx) + { + suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); + } else { + app = Applicability::MaybeIncorrect; + } + diag.multipart_suggestion("use", suggestions, app); + if app != Applicability::MachineApplicable { + diag.note("the order of the element and the index will be swapped"); + } + }, ); } } + +/// If `expr` is the argument of a `for` loop, return the loop pattern. +fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let Node::Expr(ancestor_expr) = node + && let Some(for_loop) = higher::ForLoop::hir(ancestor_expr) + && for_loop.arg.hir_id == expr.hir_id + { + Some(for_loop.pat) + } else { + None + } + }) +} + +/// If `expr` is the receiver of an `Iterator` method which consumes the iterator elements and feed +/// them to a closure, return the pattern of the closure. +fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind + && recv.hir_id == expr.hir_id + && matches!( + method.ident.name, + sym::all + | sym::any + | sym::filter_map + | sym::find_map + | sym::flat_map + | sym::for_each + | sym::is_partitioned + | sym::is_sorted_by_key + | sym::map + | sym::map_while + | sym::position + | sym::rposition + | sym::try_for_each + ) + && let ExprKind::Closure(closure) = arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let [param] = body.params + { + Some(param.pat) + } else { + None + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index a544954931bad..1fe9e62fae30c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -191,8 +191,10 @@ generate! { is_none, is_none_or, is_ok, + is_partitioned, is_some, is_some_and, + is_sorted_by_key, isqrt, itertools, join, @@ -210,6 +212,7 @@ generate! { map_continue, map_or, map_or_else, + map_while, match_indices, matches, max, @@ -349,6 +352,7 @@ generate! { trim_start, trim_start_matches, truncate, + try_for_each, unreachable_pub, unsafe_removed_from_name, unused, diff --git a/tests/ui/range.fixed b/tests/ui/range.fixed index 0e951d88091b7..c8e654600045e 100644 --- a/tests/ui/range.fixed +++ b/tests/ui/range.fixed @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().enumerate(); //~^ range_zip_with_len + //~v range_zip_with_len + for (i, e) in v1.iter().enumerate() { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().enumerate().for_each(|(i, e)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.rs b/tests/ui/range.rs index 5343801647434..352d517eabdd7 100644 --- a/tests/ui/range.rs +++ b/tests/ui/range.rs @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().zip(0..v1.len()); //~^ range_zip_with_len + //~v range_zip_with_len + for (e, i) in v1.iter().zip(0..v1.len()) { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index 798ce1842d8b5..b0a852a69fc5a 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -2,10 +2,35 @@ error: using `.zip()` with a range and `.len()` --> tests/ui/range.rs:6:14 | LL | let _x = v1.iter().zip(0..v1.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v1.iter().enumerate()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` | + = note: the order of the element and the index will be swapped = note: `-D clippy::range-zip-with-len` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` -error: aborting due to 1 previous error +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:10:19 + | +LL | for (e, i) in v1.iter().zip(0..v1.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - for (e, i) in v1.iter().zip(0..v1.len()) { +LL + for (i, e) in v1.iter().enumerate() { + | + +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:16:5 + | +LL | v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - v1.iter().zip(0..v1.len()).for_each(|(e, i)| { +LL + v1.iter().enumerate().for_each(|(i, e)| { + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/range_unfixable.rs b/tests/ui/range_unfixable.rs new file mode 100644 index 0000000000000..259be23fa1e5a --- /dev/null +++ b/tests/ui/range_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix +#![allow(clippy::useless_vec)] +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; + + // Do not autofix, `filter()` would not consume the iterator. + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); +} diff --git a/tests/ui/range_unfixable.stderr b/tests/ui/range_unfixable.stderr new file mode 100644 index 0000000000000..fb03ea2c05f6b --- /dev/null +++ b/tests/ui/range_unfixable.stderr @@ -0,0 +1,12 @@ +error: using `.zip()` with a range and `.len()` + --> tests/ui/range_unfixable.rs:10:5 + | +LL | v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` + | + = note: the order of the element and the index will be swapped + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` + +error: aborting due to 1 previous error + From 4acf3baecda9c5080bcfdc1ebb4560fbb2a4567e Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:18:05 -0700 Subject: [PATCH 0007/1900] libtest: expose --fail-fast --- library/test/src/cli.rs | 4 +++- src/doc/rustc/src/tests/index.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 8840714a66238..d19141b8c2c73 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -57,6 +57,7 @@ fn optgroups() -> getopts::Options { .optflag("", "test", "Run tests and not benchmarks") .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") + .optflag("", "fail-fast", "Don't start new tests after the first failure") .optflag("h", "help", "Display this message") .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( @@ -270,6 +271,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); + let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); @@ -307,7 +309,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { skip, time_options, options, - fail_fast: false, + fail_fast, }; Ok(test_opts) diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 12de69a4c9eec..7033184e52aa9 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -158,6 +158,16 @@ unstable-options` flag. See [tracking issue The following options affect how tests are executed. +#### `--fail-fast` + +Stops tests after the first failure. + +If running tests in parallel (which is the default), then tests that have already been started on +other threads will be allowed to run to completion before the process exits. + +Note that when running tests in parallel, the test execution order is non-deterministic: +if multiple tests would fail, the first failure encountered will be reported. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From 547c72901ad145b40d7271e3ddef2b4ac0c53619 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:27:53 -0700 Subject: [PATCH 0008/1900] Make fail-fast unstable --- library/test/src/cli.rs | 2 +- src/doc/rustc/src/tests/index.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index d19141b8c2c73..ea91ebc4e9875 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -262,6 +262,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { // Unstable flags let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process"); let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); + let fail_fast = unstable_optflag!(matches, allow_unstable, "fail-fast"); let time_options = get_time_options(&matches, allow_unstable)?; let shuffle = get_shuffle(&matches, allow_unstable)?; let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?; @@ -271,7 +272,6 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); - let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 7033184e52aa9..ab96fbcd3178e 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -168,6 +168,8 @@ other threads will be allowed to run to completion before the process exits. Note that when running tests in parallel, the test execution order is non-deterministic: if multiple tests would fail, the first failure encountered will be reported. +⚠️ 🚧 This requires the `-Z unstable-options` flag. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From c40e004a6bf4750c4bd03945852f2d5388d30d3b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 2 Jul 2025 07:25:46 +0200 Subject: [PATCH 0009/1900] gai: remove typo from test file, and add a reference id --- .../generic_arg_infer/{parend_infer.rs => paren_infer.rs} | 1 + 1 file changed, 1 insertion(+) rename tests/ui/const-generics/generic_arg_infer/{parend_infer.rs => paren_infer.rs} (94%) diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs similarity index 94% rename from tests/ui/const-generics/generic_arg_infer/parend_infer.rs rename to tests/ui/const-generics/generic_arg_infer/paren_infer.rs index 9d7df8016cb1e..869683b705669 100644 --- a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs +++ b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ reference: items.generics.const.inferred struct Foo; From 81e8c6797cc3c94134d6fce11ddb38e03be8c85a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Jul 2025 07:18:56 -0400 Subject: [PATCH 0010/1900] `redundant_clone`: split iterator checks into `redundant_iter_cloned` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/iter_overeager_cloned.rs | 5 ++-- clippy_lints/src/methods/mod.rs | 26 +++++++++++++++++++ tests/ui/iter_overeager_cloned.fixed | 12 ++++----- tests/ui/iter_overeager_cloned.rs | 12 ++++----- tests/ui/iter_overeager_cloned.stderr | 4 +-- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92fbdc767bd4..8d770ad2a3386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6304,6 +6304,7 @@ Released 2018-09-13 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards +[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c3f8e02b4c067..233a1c42841b4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -449,6 +449,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REDUNDANT_AS_STR_INFO, + crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f5fe4316eb0d3..d43b22c2bd517 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::{Symbol, sym}; -use super::ITER_OVEREAGER_CLONED; -use crate::redundant_clone::REDUNDANT_CLONE; +use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; #[derive(Clone, Copy)] pub(super) enum Op<'a> { @@ -96,7 +95,7 @@ pub(super) fn check<'tcx>( } let (lint, msg, trailing_clone) = match op { - Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""), + Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""), Op::LaterCloned | Op::FixClosure(_, _) => ( ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f2dabdd343875..c68caea89bbdc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4565,6 +4565,31 @@ declare_clippy_lint! { "hardcoded localhost IP address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Iterator::cloned` where the original value could be used + /// instead. + /// + /// ### Why is this bad? + /// It is not always possible for the compiler to eliminate useless allocations and + /// deallocations generated by redundant `clone()`s. + /// + /// ### Example + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().cloned().map(|x| x.len()); + /// ``` + /// Use instead: + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().map(|x| x.len()); + /// ``` + #[clippy::version = "1.90.0"] + pub REDUNDANT_ITER_CLONED, + perf, + "detects redundant calls to `Iterator::cloned`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4744,6 +4769,7 @@ impl_lint_pass!(Methods => [ IO_OTHER_ERROR, SWAP_WITH_TEMPORARY, IP_CONSTANT, + REDUNDANT_ITER_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index b0e548f179093..4171f19469a4d 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().take(2).cloned().collect(); //~^ iter_overeager_cloned @@ -77,19 +77,19 @@ fn main() { } let _ = vec.iter().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index cedf62a6b4730..fe6aba24dd3e2 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().cloned().take(2).collect(); //~^ iter_overeager_cloned @@ -78,19 +78,19 @@ fn main() { } let _ = vec.iter().cloned().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 1616dec95b792..f234d19e4aaaa 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -25,8 +25,8 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | | | help: try: `.count()` | - = note: `-D clippy::redundant-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` + = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:21:21 From 175afd76180d47b655377768a87a55f8581686f0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Mon, 21 Apr 2025 23:27:01 -0700 Subject: [PATCH 0011/1900] Stabilize `new_zeroed_alloc` --- compiler/rustc_index/src/bit_set.rs | 4 ++-- compiler/rustc_index/src/lib.rs | 2 +- library/alloc/src/boxed.rs | 16 ++-------------- library/alloc/src/rc.rs | 16 ++-------------- library/alloc/src/sync.rs | 14 ++------------ library/std/src/lib.rs | 1 - 6 files changed, 9 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 645d95b1dba99..0b3bc8963a3bd 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -638,7 +638,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; @@ -704,7 +704,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index cc680838e7e7b..9d647209c6f89 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] +#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] -#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] // tidy-alphabetical-end pub mod bit_set; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3d..173d0103c119b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -290,8 +290,6 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let zero = Box::::new_zeroed(); /// let zero = unsafe { zero.assume_init() }; /// @@ -301,7 +299,7 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) @@ -358,7 +356,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit() -> Result>, AllocError> { Box::try_new_uninit_in(Global) @@ -384,7 +381,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed() -> Result>, AllocError> { Box::try_new_zeroed_in(Global) @@ -463,7 +459,6 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] #[must_use] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_in(alloc: A) -> Box, A> where A: Allocator, @@ -496,7 +491,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -532,7 +526,6 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_in(alloc: A) -> Box, A> where @@ -570,7 +563,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -660,8 +652,6 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let values = Box::<[u32]>::new_zeroed_slice(3); /// let values = unsafe { values.assume_init() }; /// @@ -670,7 +660,7 @@ impl Box<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } @@ -785,7 +775,6 @@ impl Box<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) } @@ -813,7 +802,6 @@ impl Box<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71f3..0b5c9240db0f1 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -515,8 +515,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let zero = Rc::::new_zeroed(); @@ -527,7 +525,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Rc> { unsafe { @@ -589,7 +587,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -622,7 +619,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -690,7 +686,6 @@ impl Rc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Rc, A> { unsafe { @@ -728,7 +723,6 @@ impl Rc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Rc, A> { unsafe { @@ -873,7 +867,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -912,7 +905,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1054,8 +1046,6 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let values = Rc::<[u32]>::new_zeroed_slice(3); @@ -1066,7 +1056,7 @@ impl Rc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { @@ -1129,7 +1119,6 @@ impl Rc<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { Rc::from_ptr_in(Rc::allocate_for_slice_in(len, &alloc), alloc) } @@ -1158,7 +1147,6 @@ impl Rc<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f44..4090a04f21a9f 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -516,8 +516,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let zero = Arc::::new_zeroed(); @@ -529,7 +527,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Arc> { unsafe { @@ -603,7 +601,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -636,7 +633,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -703,7 +699,6 @@ impl Arc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Arc, A> { unsafe { @@ -741,7 +736,6 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Arc, A> { unsafe { @@ -927,7 +921,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -966,7 +959,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1197,8 +1189,6 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let values = Arc::<[u32]>::new_zeroed_slice(3); @@ -1210,7 +1200,7 @@ impl Arc<[T]> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 311b2cb932392..2c0061dc40320 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -376,7 +376,6 @@ #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] -#![feature(new_zeroed_alloc)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] From 064825e734e9472925664ad681e055602ea62397 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Fri, 1 Aug 2025 20:13:48 +0200 Subject: [PATCH 0012/1900] Add suggestion to `cast_sign_loss` and `cast_possible_wrap` using the `cast_{un,}signed()` methods --- clippy_lints/src/casts/cast_possible_wrap.rs | 30 ++++++- clippy_lints/src/casts/cast_sign_loss.rs | 29 ++++++- clippy_lints/src/casts/mod.rs | 4 +- clippy_lints/src/casts/utils.rs | 32 ++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/cast.stderr | 82 ++++++++++---------- 6 files changed, 131 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda933..5a8b3e6d9a411 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,4 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -16,7 +19,14 @@ enum EmitState { LintOnPtrSize(u64), } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_op: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: Msrv, +) { let (Some(from_nbits), Some(to_nbits)) = ( utils::int_ty_to_nbits(cx.tcx, cast_from), utils::int_ty_to_nbits(cx.tcx, cast_to), @@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); } + + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a70bd88619195..757d53e394ff5 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -2,15 +2,18 @@ use std::convert::Infallible; use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext, sym}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::Symbol; -use super::CAST_SIGN_LOSS; +use super::{CAST_SIGN_LOSS, utils}; /// A list of methods that can never return a negative value. /// Includes methods that panic rather than returning a negative value. @@ -42,13 +45,33 @@ pub(super) fn check<'cx>( cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>, + msrv: Msrv, ) { if should_lint(cx, cast_op, cast_from, cast_to) { - span_lint( + span_lint_and_then( cx, CAST_SIGN_LOSS, expr.span, format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + |diag| { + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } + }, ); } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 37accff5eaa81..4df2d81ea3554 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -887,9 +887,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { - cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index d846d78b9ee78..8251e48da5cdb 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -60,3 +60,35 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 { neg_bits.max(pos_bits).into() } } + +pub(super) enum CastTo { + Signed, + Unsigned, +} +/// Returns `Some` if the type cast is between 2 integral types that differ +/// only in signedness, otherwise `None`. The value of `Some` is which +/// signedness is casted to. +pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { + if !cast_from.is_integral() || !cast_to.is_integral() { + return None; + } + if cast_from.is_signed() == cast_to.is_signed() { + return None; + } + if as_uint_ty(cast_from) != as_uint_ty(cast_to) { + return None; + } + + if cast_to.is_signed() { + Some(CastTo::Signed) + } else { + Some(CastTo::Unsigned) + } +} +fn as_uint_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Uint(uint_ty) => Some(*uint_ty), + ty::Int(int_ty) => Some(int_ty.to_unsigned()), + _ => None, + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 24ed4c3a8beca..a38550a7b5520 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 1cb30d956679a..13e0a5583cdc6 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation From 0eb16ea97b5161da7874f772e20e49f02af20189 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sat, 2 Aug 2025 00:16:08 +0200 Subject: [PATCH 0013/1900] Add test to ensure that suggestions are only emitted if MSRV supports it --- tests/ui/cast.rs | 13 +++++++++++++ tests/ui/cast.stderr | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 525be8216500a..fab02bf7b24e4 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -569,3 +569,16 @@ fn issue12721() { (255 % 999999u64) as u8; //~^ cast_possible_truncation } + +mod issue14150 { + #[clippy::msrv = "1.87"] + fn msrv_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } + #[clippy::msrv = "1.86"] + fn msrv_doesnt_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 13e0a5583cdc6..f131369df2414 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -752,5 +752,17 @@ LL - (255 % 999999u64) as u8; LL + u8::try_from(255 % 999999u64); | -error: aborting due to 92 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:576:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:581:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ + +error: aborting due to 94 previous errors From 4e286bb8fb305d6ba864ff8b2c806b4fd189657a Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sun, 3 Aug 2025 13:44:56 +0200 Subject: [PATCH 0014/1900] Apply suggestions from code review --- clippy_lints/src/casts/cast_possible_wrap.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/casts/utils.rs | 23 +----- tests/ui/cast.stderr | 84 ++++++++++---------- 4 files changed, 47 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 5a8b3e6d9a411..9eaa6e4cf26e4 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -108,7 +108,7 @@ pub(super) fn check( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 757d53e394ff5..f870d27b796e8 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -66,7 +66,7 @@ pub(super) fn check<'cx>( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 8251e48da5cdb..707fc2a8eed35 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -69,26 +69,9 @@ pub(super) enum CastTo { /// only in signedness, otherwise `None`. The value of `Some` is which /// signedness is casted to. pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { - if !cast_from.is_integral() || !cast_to.is_integral() { - return None; - } - if cast_from.is_signed() == cast_to.is_signed() { - return None; - } - if as_uint_ty(cast_from) != as_uint_ty(cast_to) { - return None; - } - - if cast_to.is_signed() { - Some(CastTo::Signed) - } else { - Some(CastTo::Unsigned) - } -} -fn as_uint_ty(ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Uint(uint_ty) => Some(*uint_ty), - ty::Int(int_ty) => Some(int_ty.to_unsigned()), + match (cast_from.kind(), cast_to.kind()) { + (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), + (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), _ => None, } } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index f131369df2414..8c48855123f93 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` + | ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` + | ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation @@ -756,7 +756,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:576:13 | LL | _ = 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:581:13 From b0bbfd1bb75794f6664334f60fa2bcf064ea3bd3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 4 Aug 2025 01:53:10 +0800 Subject: [PATCH 0015/1900] Add assignment type analysis for ide-completion --- .../ide-completion/src/context/analysis.rs | 32 ++++++++++++ .../ide-completion/src/context/tests.rs | 50 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ea5fb39338b2e..67fc79e8032c1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -562,6 +562,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { + let token = prev_assign_token_at_whitespace(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -632,6 +633,17 @@ fn expected_type_and_name<'db>( .map(TypeInfo::original); (ty, None) }, + ast::BinExpr(it) => { + if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() { + let ty = it.lhs() + .and_then(|lhs| sema.type_of_expr(&lhs)) + .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs))) + .map(TypeInfo::original); + (ty, None) + } else { + (None, None) + } + }, ast::ArgList(_) => { cov_mark::hit!(expected_type_fn_param); ActiveParameter::at_token( @@ -1870,3 +1882,23 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { } None } + +fn prev_assign_token_at_whitespace(mut token: SyntaxToken) -> SyntaxToken { + while token.kind() == SyntaxKind::WHITESPACE + && let Some(prev) = token.prev_token() + && let T![=] + | T![+=] + | T![/=] + | T![*=] + | T![%=] + | T![>>=] + | T![<<=] + | T![-=] + | T![|=] + | T![&=] + | T![^=] = prev.kind() + { + token = prev + } + token +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 75c20968e1e5f..7a8c70f190efc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -434,3 +434,53 @@ fn f(thing: u32) -> &u32 { expect!["ty: u32, name: ?"], ); } + +#[test] +fn expected_type_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + x = $0; +} +"#, + expect![[r#"ty: &'_ mut State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0; + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign_at_block_end() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0 + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} From b5a4e5d73fb7fd47caa1b22ffe8527c13f895c21 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:24:22 +0500 Subject: [PATCH 0016/1900] remove gates --- library/alloc/src/collections/btree/map/entry.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index ea8fa363c3805..b16aad0bb03dd 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -275,7 +275,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); @@ -284,7 +283,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// assert_eq!(entry.key(), &"poneyland"); /// ``` #[inline] - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { @@ -383,7 +382,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// use std::collections::btree_map::Entry; /// @@ -395,7 +393,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// } /// assert_eq!(map["poneyland"], 37); /// ``` - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { let handle = match self.handle { None => { From bf500ec822cb27ec82a259c3edc0cba86e7b1ac4 Mon Sep 17 00:00:00 2001 From: skewb1k Date: Mon, 4 Aug 2025 14:11:06 +0300 Subject: [PATCH 0017/1900] chore: fix crates/ide/src/folding_ranges.rs file perms 755 -> 644 --- src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs old mode 100755 new mode 100644 From 1d817dc7a782e011797de3225037241eaabdf631 Mon Sep 17 00:00:00 2001 From: skewb1k Date: Mon, 4 Aug 2025 14:42:45 +0300 Subject: [PATCH 0018/1900] fix(hover): unify horizontal rule formatting to `---` Replaces all `___` with `---` in hover documentation markups. Both styles are valid per the GitHub Flavored Markdown spec, but `---` is less ambiguous and already more widely used in rust-analyzer --- .../crates/ide/src/hover/render.rs | 8 +- .../crates/ide/src/hover/tests.rs | 76 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 51b5900e8155a..653465e9ed5ba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -361,7 +361,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option { let backtick_len = value.chars().filter(|c| *c == '`').count(); @@ -1026,7 +1026,7 @@ fn type_info( if let Some(extra) = render_notable_trait(db, ¬able_traits(db, &original), edition, display_target) { - desc.push_str("\n___\n"); + desc.push_str("\n---\n"); desc.push_str(&extra); }; desc.into() @@ -1094,7 +1094,7 @@ fn closure_ty( |_| None, |_| None, ) { - format_to!(markup, "\n___\n{layout}"); + format_to!(markup, "\n---\n{layout}"); } format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c5480217a91e2..187fd1222e89b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -357,7 +357,7 @@ fn main() { ```rust impl Fn(i32) -> i32 ``` - ___ + --- size = 8, align = 8, niches = 1 ## Captures @@ -380,7 +380,7 @@ fn main() { ```rust impl Fn(i32) -> i32 ``` - ___ + --- size = 0, align = 1 ## Captures @@ -414,7 +414,7 @@ fn main() { ```rust impl FnOnce() ``` - ___ + --- size = 16 (0x10), align = 8, niches = 1 ## Captures @@ -443,7 +443,7 @@ fn main() { ```rust impl FnMut() ``` - ___ + --- size = 8, align = 8, niches = 1 ## Captures @@ -468,7 +468,7 @@ fn main() { ```rust impl FnOnce() -> S2 ``` - ___ + --- size = 8, align = 8, niches = 1 Coerced to: &impl FnOnce() -> S2 @@ -6829,7 +6829,7 @@ fn hover_lint() { ``` arithmetic_overflow ``` - ___ + --- arithmetic operation overflows "#]], @@ -6841,7 +6841,7 @@ fn hover_lint() { ``` arithmetic_overflow ``` - ___ + --- arithmetic operation overflows "#]], @@ -6857,7 +6857,7 @@ fn hover_clippy_lint() { ``` clippy::almost_swapped ``` - ___ + --- Checks for `foo = bar; bar = foo` sequences. "#]], @@ -6869,7 +6869,7 @@ fn hover_clippy_lint() { ``` clippy::almost_swapped ``` - ___ + --- Checks for `foo = bar; bar = foo` sequences. "#]], @@ -8567,7 +8567,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` 🦀🦀\A ` "#]], @@ -8583,7 +8583,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` 🦀\u{1f980}\\\x41 ` "#]], @@ -8605,7 +8605,7 @@ fsdghs"; ```rust &'static str ``` - ___ + --- value of literal (truncated up to newline): ` 🦀\u{1f980}\\\x41 ` "#]], @@ -8625,7 +8625,7 @@ fn main() { ```rust &'static {unknown} ``` - ___ + --- value of literal: ` 🦀🦀\A ` "#]], @@ -8644,7 +8644,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ```` `[^`]*` ```` "#]], @@ -8659,7 +8659,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: `` ` `` "#]], @@ -8674,7 +8674,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` ` "#]], @@ -8690,7 +8690,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` Hello World ` "#]], @@ -8710,7 +8710,7 @@ fn main() { ```rust &'static [u8; 5] ``` - ___ + --- value of literal: ` [240, 159, 166, 128, 92] ` "#]], @@ -8726,7 +8726,7 @@ fn main() { ```rust &'static [u8; 18] ``` - ___ + --- value of literal: ` [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] ` "#]], @@ -8746,7 +8746,7 @@ fn main() { ```rust u8 ``` - ___ + --- value of literal: ` 0xF0 ` "#]], @@ -8762,7 +8762,7 @@ fn main() { ```rust u8 ``` - ___ + --- value of literal: ` 0x5C ` "#]], @@ -8782,7 +8782,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` A ` "#]], @@ -8798,7 +8798,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` \ ` "#]], @@ -8814,7 +8814,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` 🦀 ` "#]], @@ -8834,7 +8834,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 1 (bits: 0x3FF0000000000000) ` "#]], @@ -8850,7 +8850,7 @@ fn main() { ```rust f16 ``` - ___ + --- value of literal: ` 1 (bits: 0x3C00) ` "#]], @@ -8866,7 +8866,7 @@ fn main() { ```rust f32 ``` - ___ + --- value of literal: ` 1 (bits: 0x3F800000) ` "#]], @@ -8882,7 +8882,7 @@ fn main() { ```rust f128 ``` - ___ + --- value of literal: ` 1 (bits: 0x3FFF0000000000000000000000000000) ` "#]], @@ -8898,7 +8898,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 134000000000000 (bits: 0x42DE77D399980000) ` "#]], @@ -8914,7 +8914,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 1523527134274733600000000 (bits: 0x44F429E9249F629B) ` "#]], @@ -8930,7 +8930,7 @@ fn main() { ```rust f64 ``` - ___ + --- invalid literal: invalid float literal "#]], @@ -8950,7 +8950,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) ` "#]], @@ -8966,7 +8966,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) ` "#]], @@ -8982,7 +8982,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 306328611 (0x12423423|0b10010010000100011010000100011) ` "#]], @@ -8998,7 +8998,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 255 (0xFF|0b11111111) ` "#]], @@ -9014,7 +9014,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 5349 (0x14E5|0b1010011100101) ` "#]], @@ -9030,7 +9030,7 @@ fn main() { ```rust i32 ``` - ___ + --- invalid literal: number too large to fit in target type "#]], @@ -9186,7 +9186,7 @@ fn main() { ```rust S ``` - ___ + --- Implements notable traits: `Future`, `Iterator`, `Notable`"#]], ); } From 6bc1d4d2aa996918838e2e9e39290756a37b8cc0 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Mon, 4 Aug 2025 14:38:17 +0900 Subject: [PATCH 0019/1900] remvoe add_attr edit_in_place.rs because it use ted. --- .../src/handlers/convert_bool_to_enum.rs | 37 ++++----- .../src/handlers/convert_closure_to_fn.rs | 1 + .../src/handlers/convert_from_to_tryfrom.rs | 1 + .../src/handlers/extract_function.rs | 1 + .../src/handlers/extract_module.rs | 2 + .../src/handlers/extract_type_alias.rs | 5 +- .../src/handlers/extract_variable.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 + .../src/handlers/generate_delegate_trait.rs | 79 +++++++++++++++---- .../src/handlers/generate_derive.rs | 25 ++++-- .../src/handlers/generate_fn_type_alias.rs | 1 + .../src/handlers/generate_function.rs | 3 +- .../src/handlers/generate_getter_or_setter.rs | 2 + .../ide-assists/src/handlers/generate_impl.rs | 1 + .../ide-assists/src/handlers/generate_new.rs | 1 + .../generate_single_field_struct_from.rs | 10 ++- .../src/handlers/promote_local_to_const.rs | 2 +- .../src/handlers/unmerge_imports.rs | 10 +-- .../crates/ide-assists/src/utils.rs | 26 ++---- .../crates/ide-db/src/imports/insert_use.rs | 2 +- .../crates/syntax/src/ast/edit_in_place.rs | 22 ------ .../crates/syntax/src/ast/make.rs | 38 +++++++-- .../src/ast/syntax_factory/constructors.rs | 27 +++++-- .../crates/syntax/src/syntax_editor.rs | 1 + 24 files changed, 185 insertions(+), 116 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index f73b8c4fd0f19..c208c8bf789a0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -13,12 +13,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, - ast::{ - self, HasName, - edit::IndentLevel, - edit_in_place::{AttrsOwnerEdit, Indent}, - make, - }, + ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, }; use crate::{ @@ -506,18 +501,6 @@ fn node_to_insert_before(target_node: SyntaxNode) -> SyntaxNode { } fn make_bool_enum(make_pub: bool) -> ast::Enum { - let enum_def = make::enum_( - if make_pub { Some(make::visibility_pub()) } else { None }, - make::name("Bool"), - None, - None, - make::variant_list(vec![ - make::variant(None, make::name("True"), None, None), - make::variant(None, make::name("False"), None, None), - ]), - ) - .clone_for_update(); - let derive_eq = make::attr_outer(make::meta_token_tree( make::ext::ident_path("derive"), make::token_tree( @@ -529,11 +512,19 @@ fn make_bool_enum(make_pub: bool) -> ast::Enum { NodeOrToken::Token(make::tokens::ident("Eq")), ], ), - )) - .clone_for_update(); - enum_def.add_attr(derive_eq); - - enum_def + )); + make::enum_( + [derive_eq], + if make_pub { Some(make::visibility_pub()) } else { None }, + make::name("Bool"), + None, + None, + make::variant_list(vec![ + make::variant(None, make::name("True"), None, None), + make::variant(None, make::name("False"), None, None), + ]), + ) + .clone_for_update() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 916bb67ebb405..5f526ec899404 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -235,6 +235,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) Some(make::ret_type(make::ty(&ret_ty))) }; let mut fn_ = make::fn_( + None, None, closure_name_or_default.clone(), closure_type_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index f1cc3d90b9c56..7d8b763d8b87b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -96,6 +96,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> } let error_type = ast::AssocItem::TypeAlias(make::ty_alias( + None, "Error", None, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 890b8dd64126e..d88e3311bd7bf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -1641,6 +1641,7 @@ fn format_function( let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); make::fn_( + None, None, fun_name, generic_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index c6a6b97df8245..da91d0ac28097 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -585,6 +585,7 @@ impl Module { if (def_out_sel || !is_item) && use_stmt_not_in_sel { let use_ = make::use_( + None, None, make::use_tree(make::join_paths(use_tree_paths), None, None, false), ); @@ -599,6 +600,7 @@ impl Module { let super_path = make::ext::ident_path("super"); let node_path = make::ext::ident_path(&node_syntax.to_string()); let use_ = make::use_( + None, None, make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 79f22381952ae..7f93506685e18 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -69,8 +69,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None))) - .clone_for_update(); + let ty_alias = + make::ty_alias(None, "Type", generic_params, None, None, Some((ty, None))) + .clone_for_update(); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c9c1969b9e023..bd88e8b09ced0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -200,7 +200,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } ExtractionKind::Constant => { let ast_ty = make.ty(&ty_string); - ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer)) + ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer)) .into() } ExtractionKind::Static => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 2c81e2883a34a..d8a2e038d33c2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -155,6 +155,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ret_type = method_source.ret_type(); let f = make::fn_( + None, vis, fn_name, type_params, @@ -195,6 +196,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let assoc_item_list = make::assoc_item_list(Some(vec![item])); let impl_def = make::impl_( + None, ty_params, ty_args, make::ty_path(make::ext::ident_path(name)), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index e96250f3c50a5..e87dde5b8e427 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -20,7 +20,6 @@ use syntax::{ HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred, edit::{self, AstNodeEdit}, - edit_in_place::AttrsOwnerEdit, make, }, ted::{self, Position}, @@ -266,6 +265,7 @@ fn generate_impl( let bound_params = bound_def.generic_param_list(); let delegate = make::impl_trait( + None, delegee.is_unsafe(db), bound_params.clone(), bound_params.map(|params| params.to_generic_args()), @@ -379,6 +379,7 @@ fn generate_impl( let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl let delegate = make::impl_trait( + None, trait_.is_unsafe(db), trait_gen_params, trait_gen_args, @@ -652,8 +653,7 @@ fn process_assoc_item( qual_path_ty: ast::Path, base_name: &str, ) -> Option { - let attrs = item.attrs(); - let assoc = match item { + match item { AssocItem::Const(c) => const_assoc_item(c, qual_path_ty), AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name), AssocItem::MacroCall(_) => { @@ -662,18 +662,7 @@ fn process_assoc_item( None } AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty), - }; - if let Some(assoc) = &assoc { - attrs.for_each(|attr| { - assoc.add_attr(attr.clone()); - // fix indentations - if let Some(tok) = attr.syntax().next_sibling_or_token() { - let pos = Position::after(tok); - ted::insert(pos, make::tokens::whitespace(" ")); - } - }) } - assoc } fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option { @@ -687,6 +676,7 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let inner = make::item_const( + item.attrs(), item.visibility(), item.name()?, item.ty()?, @@ -755,6 +745,7 @@ fn func_assoc_item( let body = make::block_expr(vec![], Some(call.into())).clone_for_update(); let func = make::fn_( + item.attrs(), item.visibility(), item.name()?, item.generic_param_list(), @@ -779,13 +770,14 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option::t; + + #[cfg(not(test))] + type t = ::t; +} +"#, + ); + } + #[test] fn assoc_items_attributes_mutably_cloned() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 73a69c82fbcdd..06fef4af22382 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,6 +1,8 @@ use syntax::{ + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, T, - ast::{self, AstNode, HasAttrs, edit_in_place::AttrsOwnerEdit, make}, + ast::{self, AstNode, HasAttrs, edit::IndentLevel, make}, + syntax_editor::{Element, Position}, }; use crate::{AssistContext, AssistId, Assists}; @@ -48,8 +50,20 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt )) .clone_for_update(); - let nominal = edit.make_mut(nominal); - nominal.add_attr(derive.clone()); + let mut editor = edit.make_editor(nominal.syntax()); + let indent = IndentLevel::from_node(nominal.syntax()); + let after_attrs_and_comments = nominal + .syntax() + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .map_or(Position::first_child_of(nominal.syntax()), Position::before); + editor.insert_all( + after_attrs_and_comments, + vec![ + derive.syntax().syntax_element(), + make::tokens::whitespace(&format!("\n{indent}")).syntax_element(), + ], + ); let delimiter = derive .meta() @@ -58,8 +72,9 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt .expect("failed to get token tree out of Meta") .r_paren_token() .expect("make::attr_outer was expected to have a R_PAREN"); - - edit.add_tabstop_before_token(cap, delimiter); + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(delimiter, tabstop_before); + edit.add_file_edits(ctx.vfs_file_id(), editor); } Some(_) => { // Just move the cursor. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 3c327a63b0f0b..0b7eca2290f62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -94,6 +94,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) // Insert new alias let ty_alias = make::ty_alias( + None, &alias_name, generic_params, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 613b32fcc1653..efdb20c1e9317 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -189,7 +189,7 @@ fn add_func_to_accumulator( ))); // FIXME: adt may have generic params. - let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); + let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update(); func.indent(IndentLevel(1)); impl_.get_or_create_assoc_item_list().add_item(func.into()); @@ -365,6 +365,7 @@ impl FunctionBuilder { Visibility::Pub => Some(make::visibility_pub()), }; let fn_def = make::fn_( + None, visibility, self.fn_name, self.generic_param_list, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 807b9194b2df7..e42d0ed1b00b0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -263,6 +263,7 @@ fn generate_getter_from_info( let body = make::block_expr([], Some(body)); make::fn_( + None, strukt.visibility(), fn_name, None, @@ -299,6 +300,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI // Make the setter fn make::fn_( + None, strukt.visibility(), fn_name, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index b38ee6f7dce8e..77eb8efc6f6af 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -174,6 +174,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> let make_impl_ = |body| { make::impl_trait( + None, trait_.unsafe_token().is_some(), None, trait_gen_args.clone(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 351f134612f00..ac4a54a89ef1f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -134,6 +134,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self"))); let fn_ = make::fn_( + None, strukt.visibility(), make::name("new"), None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4e95ceb2e853e..6076d5cb5cedf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -6,13 +6,12 @@ use ide_db::{ }; use syntax::{ TokenText, - ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent}, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit, edit_in_place::Indent}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::add_cfg_attrs_to, }; // Assist: generate_single_field_struct_from @@ -89,6 +88,7 @@ pub(crate) fn generate_single_field_struct_from( let body = make::block_expr([], Some(constructor)); let fn_ = make::fn_( + None, None, make::name("from"), None, @@ -110,8 +110,12 @@ pub(crate) fn generate_single_field_struct_from( .clone_for_update(); fn_.indent(1.into()); + let cfg_attrs = strukt + .attrs() + .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); let impl_ = make::impl_trait( + cfg_attrs, false, None, trait_gen_args, @@ -128,8 +132,6 @@ pub(crate) fn generate_single_field_struct_from( impl_.get_or_create_assoc_item_list().add_item(fn_.into()); - add_cfg_attrs_to(&strukt, &impl_); - impl_.reindent_to(indent); builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}")); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 603be4d66733d..547d3686e3909 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -88,7 +88,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer); + let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { let tabstop = edit.make_tabstop_before(cap); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index c066f41ca47b7..accb5c28d6ed3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -1,9 +1,6 @@ use syntax::{ AstNode, SyntaxKind, - ast::{ - self, HasAttrs, HasVisibility, edit::IndentLevel, edit_in_place::AttrsOwnerEdit, make, - syntax_factory::SyntaxFactory, - }, + ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::{Element, Position, Removable}, }; @@ -46,13 +43,10 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| { let make = SyntaxFactory::with_mappings(); let new_use = make.use_( + use_.attrs(), use_.visibility(), make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), ); - // Add any attributes that are present on the use tree - use_.attrs().for_each(|attr| { - new_use.add_attr(attr.clone_for_update()); - }); let mut editor = builder.make_editor(use_.syntax()); // Remove the use tree from the current use item diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 91aac9cf7b608..20e0302b57d70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -736,8 +736,11 @@ fn generate_impl_inner( generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - let impl_ = match trait_ { + let cfg_attrs = + adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); + match trait_ { Some(trait_) => make::impl_trait( + cfg_attrs, is_unsafe, None, None, @@ -750,26 +753,9 @@ fn generate_impl_inner( adt.where_clause(), body, ), - None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body), - } - .clone_for_update(); - - // Copy any cfg attrs from the original adt - add_cfg_attrs_to(adt, &impl_); - - impl_ -} - -pub(crate) fn add_cfg_attrs_to(from: &T, to: &U) -where - T: HasAttrs, - U: AttrsOwnerEdit, -{ - let cfg_attrs = - from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); - for attr in cfg_attrs { - to.add_attr(attr.clone_for_update()); + None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), } + .clone_for_update() } pub(crate) fn add_method_to_adt( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 08cd8f28608ca..b174adfd7e448 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -194,7 +194,7 @@ fn insert_use_with_alias_option( use_tree = use_tree.clone_for_update(); use_tree.wrap_in_tree_list(); } - let use_item = make::use_(None, use_tree).clone_for_update(); + let use_item = make::use_(None, None, use_tree).clone_for_update(); for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index b50ce6442432d..160f000387b3d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -273,28 +273,6 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { } } } - - fn add_attr(&self, attr: ast::Attr) { - add_attr(self.syntax(), attr); - - fn add_attr(node: &SyntaxNode, attr: ast::Attr) { - let indent = IndentLevel::from_node(node); - attr.reindent_to(indent); - - let after_attrs_and_comments = node - .children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) - .map_or(Position::first_child_of(node), Position::before); - - ted::insert_all( - after_attrs_and_comments, - vec![ - attr.syntax().clone().into(), - make::tokens::whitespace(&format!("\n{indent}")).into(), - ], - ) - } - } } impl AttrsOwnerEdit for T {} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index daeb79cf081dc..c5ca609760187 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -190,6 +190,7 @@ fn ty_from_text(text: &str) -> ast::Type { } pub fn ty_alias( + attrs: impl IntoIterator, ident: &str, generic_param_list: Option, type_param_bounds: Option, @@ -200,6 +201,7 @@ pub fn ty_alias( let assignment_where = assignment_where.flatten(); quote! { TypeAlias { + #(#attrs "\n")* [type] " " Name { [IDENT ident] } #generic_param_list @@ -277,12 +279,16 @@ fn merge_where_clause( } pub fn impl_( + attrs: impl IntoIterator, generic_params: Option, generic_args: Option, path_type: ast::Type, where_clause: Option, body: Option, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); + let gen_args = generic_args.map_or_else(String::new, |it| it.to_string()); let gen_params = generic_params.map_or_else(String::new, |it| it.to_string()); @@ -295,10 +301,11 @@ pub fn impl_( }; let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); - ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) + ast_from_text(&format!("{attrs}impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) } pub fn impl_trait( + attrs: impl IntoIterator, is_unsafe: bool, trait_gen_params: Option, trait_gen_args: Option, @@ -311,6 +318,8 @@ pub fn impl_trait( ty_where_clause: Option, body: Option, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let is_unsafe = if is_unsafe { "unsafe " } else { "" }; let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default(); @@ -334,7 +343,7 @@ pub fn impl_trait( let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); ast_from_text(&format!( - "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" + "{attrs}{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" )) } @@ -452,12 +461,18 @@ pub fn use_tree_list(use_trees: impl IntoIterator) -> ast:: ast_from_text(&format!("use {{{use_trees}}};")) } -pub fn use_(visibility: Option, use_tree: ast::UseTree) -> ast::Use { +pub fn use_( + attrs: impl IntoIterator, + visibility: Option, + use_tree: ast::UseTree, +) -> ast::Use { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}use {use_tree};")) + ast_from_text(&format!("{attrs}{visibility}use {use_tree};")) } pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr { @@ -946,16 +961,19 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { } pub fn item_const( + attrs: impl IntoIterator, visibility: Option, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}const {name}: {ty} = {expr};")) + ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};")) } pub fn item_static( @@ -1162,6 +1180,7 @@ pub fn variant( } pub fn fn_( + attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, @@ -1174,6 +1193,8 @@ pub fn fn_( is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let type_params = match type_params { Some(type_params) => format!("{type_params}"), None => "".into(), @@ -1197,7 +1218,7 @@ pub fn fn_( let gen_literal = if is_gen { "gen " } else { "" }; ast_from_text(&format!( - "{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } pub fn struct_( @@ -1217,12 +1238,15 @@ pub fn struct_( } pub fn enum_( + attrs: impl IntoIterator, visibility: Option, enum_name: ast::Name, generic_param_list: Option, where_clause: Option, variant_list: ast::VariantList, ) -> ast::Enum { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), @@ -1232,7 +1256,7 @@ pub fn enum_( let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default(); ast_from_text(&format!( - "{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" + "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" )) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 738a26fed5d82..8bf27f967482b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -2,8 +2,8 @@ use crate::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ - self, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, - HasVisibility, RangeItem, make, + self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, + HasTypeBounds, HasVisibility, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -107,8 +107,13 @@ impl SyntaxFactory { ast } - pub fn use_(&self, visibility: Option, use_tree: ast::UseTree) -> ast::Use { - make::use_(visibility, use_tree).clone_for_update() + pub fn use_( + &self, + attrs: impl IntoIterator, + visibility: Option, + use_tree: ast::UseTree, + ) -> ast::Use { + make::use_(attrs, visibility, use_tree).clone_for_update() } pub fn use_tree( @@ -840,16 +845,20 @@ impl SyntaxFactory { pub fn item_const( &self, + attrs: impl IntoIterator, visibility: Option, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { - let ast = make::item_const(visibility.clone(), name.clone(), ty.clone(), expr.clone()) - .clone_for_update(); + let (attrs, attrs_input) = iterator_input(attrs); + let ast = + make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()) + .clone_for_update(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node( visibility.syntax().clone(), @@ -1067,6 +1076,7 @@ impl SyntaxFactory { pub fn item_enum( &self, + attrs: impl IntoIterator, visibility: Option, name: ast::Name, generic_param_list: Option, @@ -1074,6 +1084,7 @@ impl SyntaxFactory { variant_list: ast::VariantList, ) -> ast::Enum { let ast = make::enum_( + attrs, visibility.clone(), name.clone(), generic_param_list.clone(), @@ -1182,6 +1193,7 @@ impl SyntaxFactory { pub fn fn_( &self, + attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, @@ -1194,7 +1206,9 @@ impl SyntaxFactory { is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let (attrs, input) = iterator_input(attrs); let ast = make::fn_( + attrs, visibility.clone(), fn_name.clone(), type_params.clone(), @@ -1210,6 +1224,7 @@ impl SyntaxFactory { if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node( diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 18f5015e9eabd..0b358878fcf23 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -618,6 +618,7 @@ mod tests { #[test] fn test_replace_token_in_parent() { let parent_fn = make::fn_( + None, None, make::name("it"), None, From 0ef8c6f2add02bf66108f3b80c204ad2b2a2c7ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 Aug 2025 17:41:00 +0300 Subject: [PATCH 0020/1900] Correctly goto `From` impl when on `into()` even when the call is inside a macro Descend into macros first. --- .../crates/ide/src/goto_definition.rs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 84e41277390ff..f768d4b68f469 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -83,14 +83,14 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new(original_token.text_range(), navs)); } - if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) { - return Some(RangeInfo::new(original_token.text_range(), navs)); - } - let navs = sema .descend_into_macros_no_opaque(original_token.clone(), false) .into_iter() .filter_map(|token| { + if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { + return Some(navs); + } + let parent = token.value.parent()?; let token_file_id = token.file_id; @@ -3275,6 +3275,32 @@ impl From for B { } } +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn into_call_to_from_definition_within_macro() { + check( + r#" +//- proc_macros: identity +//- minicore: from +struct A; + +struct B; + +impl From for B { + fn from(value: A) -> Self { + //^^^^ + B + } +} + +#[proc_macros::identity] fn f() { let a = A; let b: B = a.into$0(); From 0e44b50dbef4c8ac2520576543fcbfff7d6d99e6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 4 Aug 2025 22:44:55 +0800 Subject: [PATCH 0021/1900] Add if..else completions in LetStmt and ArgList Example === ```rust let x = $0; ``` Old completions: ```rust let x = if $1 { $0 }; ``` This PR current completions: ```rust let x = if $1 { $2 } else { $0 }; ``` --- .../ide-completion/src/completions/expr.rs | 9 +- .../ide-completion/src/completions/keyword.rs | 116 ++++++++++++++++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 2 + 4 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 2133291b1de15..a84927f6e2c0f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path( after_if_expr, in_condition, incomplete_let, + in_value, ref ref_expr_parent, after_amp, ref is_func_update, @@ -361,10 +362,16 @@ pub(crate) fn complete_expr_path( add_keyword("loop", "loop {\n $0\n}"); if in_match_guard { add_keyword("if", "if $0"); + } else if in_value { + add_keyword("if", "if $1 {\n $2\n} else {\n $0\n}"); } else { add_keyword("if", "if $1 {\n $0\n}"); } - add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + if in_value { + add_keyword("if let", "if let $1 = $2 {\n $3\n} else {\n $0\n}"); + } else { + add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + } add_keyword("for", "for $1 in $2 {\n $0\n}"); add_keyword("true", "true"); add_keyword("false", "false"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index 64bb1fce6ba02..aea4e119f2076 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -238,6 +238,8 @@ fn main() { r#" fn main() { let x = if $1 { + $2 +} else { $0 }; let y = 92; @@ -335,6 +337,120 @@ fn main() { ) } + #[test] + fn if_completion_in_parameter() { + check_edit( + "if", + r" +fn main() { + foo($0) +} +", + r" +fn main() { + foo(if $1 { + $2 +} else { + $0 +}) +} +", + ); + + check_edit( + "if", + r" +fn main() { + foo($0, 2) +} +", + r" +fn main() { + foo(if $1 { + $2 +} else { + $0 +}, 2) +} +", + ); + + check_edit( + "if", + r" +fn main() { + foo(2, $0) +} +", + r" +fn main() { + foo(2, if $1 { + $2 +} else { + $0 +}) +} +", + ); + + check_edit( + "if let", + r" +fn main() { + foo(2, $0) +} +", + r" +fn main() { + foo(2, if let $1 = $2 { + $3 +} else { + $0 +}) +} +", + ); + } + + #[test] + fn if_completion_in_let_statement() { + check_edit( + "if", + r" +fn main() { + let x = $0; +} +", + r" +fn main() { + let x = if $1 { + $2 +} else { + $0 +}; +} +", + ); + + check_edit( + "if let", + r" +fn main() { + let x = $0; +} +", + r" +fn main() { + let x = if let $1 = $2 { + $3 +} else { + $0 +}; +} +", + ); + } + #[test] fn completes_let_in_block() { check_edit( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index cfd7f80d40b30..bb6b13b8fb96b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, + pub(crate) in_value: bool, pub(crate) ref_expr_parent: Option, pub(crate) after_amp: bool, /// The surrounding RecordExpression we are completing a functional update diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ea5fb39338b2e..59077237e7534 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1245,6 +1245,7 @@ fn classify_name_ref<'db>( .parent() .and_then(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()); + let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { @@ -1265,6 +1266,7 @@ fn classify_name_ref<'db>( is_func_update, innermost_ret_ty, self_param, + in_value, incomplete_let, impl_, in_match_guard, From c91f9f094025d84f9b2e123b17309c2242607247 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 5 Aug 2025 00:36:14 +0900 Subject: [PATCH 0022/1900] remove `ted` from replace_named_generic_with_impl.rs --- .../replace_named_generic_with_impl.rs | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 3cd7b58f4ddd4..df7057835c346 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -7,11 +7,8 @@ use ide_db::{ }; use syntax::{ AstNode, - ast::{ - self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, - make::impl_trait_type, - }, - match_ast, ted, + ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, make}, + match_ast, }; use crate::{AssistContext, AssistId, Assists}; @@ -74,26 +71,31 @@ pub(crate) fn replace_named_generic_with_impl( "Replace named generic with impl trait", target, |edit| { - let type_param = edit.make_mut(type_param); - let fn_ = edit.make_mut(fn_); - - let path_types_to_replace = path_types_to_replace - .into_iter() - .map(|param| edit.make_mut(param)) - .collect::>(); + let mut editor = edit.make_editor(type_param.syntax()); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { - generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); - if generic_params.generic_params().count() == 0 { - ted::remove(generic_params.syntax()); + let params: Vec = generic_params + .clone() + .generic_params() + .filter(|it| it.syntax() != type_param.syntax()) + .collect(); + if params.is_empty() { + editor.delete(generic_params.syntax()); + } else { + let new_generic_param_list = make::generic_param_list(params); + editor.replace( + generic_params.syntax(), + new_generic_param_list.syntax().clone_for_update(), + ); } } - let new_bounds = impl_trait_type(type_bound_list); + let new_bounds = make::impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { - ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); + editor.replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); } + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } From 67d099bf2f64715e37ee7efe1cf21c14f0d93316 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 5 Aug 2025 01:16:39 +0900 Subject: [PATCH 0023/1900] Migrate `expand_glob_import` assist to use `SyntaxEditor` --- .../ide-assists/src/handlers/expand_glob_import.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 66552dd65f567..4b6d0b37854c0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -9,7 +9,6 @@ use stdx::never; use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, ast::{self, Use, UseTree, VisibilityKind, make}, - ted, }; use crate::{ @@ -165,8 +164,6 @@ fn build_expanded_import( let filtered_defs = if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; - let use_tree = builder.make_mut(use_tree); - let names_to_import = find_names_to_import(filtered_defs, imported_defs); let expanded = make::use_tree_list(names_to_import.iter().map(|n| { let path = make::ext::ident_path( @@ -176,22 +173,24 @@ fn build_expanded_import( })) .clone_for_update(); + let mut editor = builder.make_editor(use_tree.syntax()); match use_tree.star_token() { Some(star) => { let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1; if needs_braces { - ted::replace(star, expanded.syntax()) + editor.replace(star, expanded.syntax()) } else { let without_braces = expanded .syntax() .children_with_tokens() .filter(|child| !matches!(child.kind(), T!['{'] | T!['}'])) .collect(); - ted::replace_with_many(star, without_braces) + editor.replace_with_many(star, without_braces) } } None => never!(), } + builder.add_file_edits(ctx.vfs_file_id(), editor); } fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind { From 069c953bdf94e9a75662157953b44e8ef7e215a2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 5 Aug 2025 00:10:21 +0300 Subject: [PATCH 0024/1900] Allow renaming when there are multiple definitions (due to macros) even when some cannot rename --- .../rust-analyzer/crates/ide/src/rename.rs | 124 ++++++++++-------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index aea4ae0fd9702..8922a8eb48580 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -27,6 +27,27 @@ pub use ide_db::rename::RenameError; type RenameResult = Result; +/// This is similar to `collect::, _>>`, but unlike it, it succeeds if there is *any* `Ok` item. +fn ok_if_any(iter: impl Iterator>) -> Result, E> { + let mut err = None; + let oks = iter + .filter_map(|item| match item { + Ok(it) => Some(it), + Err(it) => { + err = Some(it); + None + } + }) + .collect::>(); + if !oks.is_empty() { + Ok(oks) + } else if let Some(err) = err { + Err(err) + } else { + Ok(Vec::new()) + } +} + /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is /// being targeted for a rename. pub(crate) fn prepare_rename( @@ -95,58 +116,57 @@ pub(crate) fn rename( alias_fallback(syntax, position, &new_name.display(db, edition).to_string()); let ops: RenameResult> = match alias_fallback { - Some(_) => defs - // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can - // properly find "direct" usages/references. - .map(|(.., def, new_name, _)| { - match kind { - IdentifierKind::Ident => (), - IdentifierKind::Lifetime => { - bail!("Cannot alias reference to a lifetime identifier") - } - IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), - IdentifierKind::LowercaseSelf => { - bail!("Cannot rename alias reference to `self`") - } - }; - let mut usages = def.usages(&sema).all(); - - // FIXME: hack - removes the usage that triggered this rename operation. - match usages.references.get_mut(&file_id).and_then(|refs| { - refs.iter() - .position(|ref_| ref_.range.contains_inclusive(position.offset)) - .map(|idx| refs.remove(idx)) - }) { - Some(_) => (), - None => never!(), - }; - - let mut source_change = SourceChange::default(); - source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { - ( - position.file_id, - source_edit_from_references(db, refs, def, &new_name, edition), - ) - })); - - Ok(source_change) - }) - .collect(), - None => defs - .map(|(.., def, new_name, rename_def)| { - if let Definition::Local(local) = def { - if let Some(self_param) = local.as_self_param(sema.db) { - cov_mark::hit!(rename_self_to_param); - return rename_self_to_param(&sema, local, self_param, &new_name, kind); - } - if kind == IdentifierKind::LowercaseSelf { - cov_mark::hit!(rename_to_self); - return rename_to_self(&sema, local); - } + Some(_) => ok_if_any( + defs + // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can + // properly find "direct" usages/references. + .map(|(.., def, new_name, _)| { + match kind { + IdentifierKind::Ident => (), + IdentifierKind::Lifetime => { + bail!("Cannot alias reference to a lifetime identifier") + } + IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), + IdentifierKind::LowercaseSelf => { + bail!("Cannot rename alias reference to `self`") + } + }; + let mut usages = def.usages(&sema).all(); + + // FIXME: hack - removes the usage that triggered this rename operation. + match usages.references.get_mut(&file_id).and_then(|refs| { + refs.iter() + .position(|ref_| ref_.range.contains_inclusive(position.offset)) + .map(|idx| refs.remove(idx)) + }) { + Some(_) => (), + None => never!(), + }; + + let mut source_change = SourceChange::default(); + source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { + ( + position.file_id, + source_edit_from_references(db, refs, def, &new_name, edition), + ) + })); + + Ok(source_change) + }), + ), + None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| { + if let Definition::Local(local) = def { + if let Some(self_param) = local.as_self_param(sema.db) { + cov_mark::hit!(rename_self_to_param); + return rename_self_to_param(&sema, local, self_param, &new_name, kind); } - def.rename(&sema, new_name.as_str(), rename_def) - }) - .collect(), + if kind == IdentifierKind::LowercaseSelf { + cov_mark::hit!(rename_to_self); + return rename_to_self(&sema, local); + } + } + def.rename(&sema, new_name.as_str(), rename_def) + })), }; ops?.into_iter() @@ -320,7 +340,7 @@ fn find_definitions( }) }); - let res: RenameResult> = symbols.filter_map(Result::transpose).collect(); + let res: RenameResult> = ok_if_any(symbols.filter_map(Result::transpose)); match res { Ok(v) => { // remove duplicates, comparing `Definition`s From d064aca3c4bb3a5b0e14b267bd859864257f2e6e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 5 Aug 2025 01:04:21 +0300 Subject: [PATCH 0025/1900] Do not remove the original token when descending into derives This caused rename to remove both, because it couldn't rename the derive-expanded one. I spent some time trying to create a test for this, before giving up. But I checked manually that this works. --- .../rust-analyzer/crates/hir/src/semantics.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d207305b4c61f..05f06f97fdc26 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1241,29 +1241,27 @@ impl<'db> SemanticsImpl<'db> { adt, )) })?; - let mut res = None; for (_, derive_attr, derives) in derives { // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! - res = res.or(process_expansion_for_token( - ctx, - &mut stack, - derive_attr, - )); + process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { - res = res - .or(process_expansion_for_token(ctx, &mut stack, derive)); + process_expansion_for_token(ctx, &mut stack, derive); } } // remove all tokens that are within the derives expansion filter_duplicates(tokens, adt.syntax().text_range()); - Some(res) + Some(()) }); // if we found derives, we can early exit. There is no way we can be in any // macro call at this point given we are not in a token tree - if let Some(res) = res { - return res; + if let Some(()) = res { + // Note: derives do not remap the original token. Furthermore, we want + // the original token to be before the derives in the list, because if they + // upmap to the same token and we deduplicate them (e.g. in rename), we + // want the original token to remain, not the derive. + return None; } } // Then check for token trees, that means we are either in a function-like macro or From a0ca79e2eff61e4aa5d9d31f7d7e008c6587ba5d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Aug 2025 10:45:54 +0200 Subject: [PATCH 0026/1900] Slim down compile time artifact progress reports --- .../crates/project-model/src/build_dependencies.rs | 8 ++------ .../rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 5bea74bed7ed2..203173c11be40 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -347,9 +347,7 @@ impl WorkspaceBuildScripts { match message { Message::BuildScriptExecuted(mut message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: build script {name} run" - )); + progress(format!("build script {name} run")); let cfgs = { let mut acc = Vec::new(); for cfg in &message.cfgs { @@ -380,9 +378,7 @@ impl WorkspaceBuildScripts { } Message::CompilerArtifact(message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: proc-macro {name} built" - )); + progress(format!("proc-macro {name} built")); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 61c758d5e86e1..f5932d8cff026 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -812,7 +812,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Building build-artifacts", state, msg, None, None); + self.report_progress("Building compile-time-deps", state, msg, None, None); } } Task::LoadProcMacros(progress) => { From b24866e619e83d1dae63ef1c2e16a91cc8e9c260 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 5 Aug 2025 20:54:38 +0800 Subject: [PATCH 0027/1900] Change prev whitespace to prev trivia --- .../crates/ide-completion/src/context/analysis.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 67fc79e8032c1..6c2edc3849982 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -562,7 +562,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_assign_token_at_whitespace(token.clone()); + let token = prev_assign_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -1883,8 +1883,8 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { None } -fn prev_assign_token_at_whitespace(mut token: SyntaxToken) -> SyntaxToken { - while token.kind() == SyntaxKind::WHITESPACE +fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { + while token.kind().is_trivia() && let Some(prev) = token.prev_token() && let T![=] | T![+=] From 6a20b6d0221e4a311d61ad4f39827d6c30dca48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?vin=C3=ADcius=20x?= Date: Mon, 4 Aug 2025 13:13:07 -0300 Subject: [PATCH 0028/1900] fix external docs for exported macros add test --- .../rust-analyzer/crates/ide/src/doc_links.rs | 3 ++- .../crates/ide/src/doc_links/tests.rs | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index a5d9a10d2e5fe..d47cc65079384 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -390,7 +390,8 @@ fn get_doc_links( let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); - if let Some(path) = mod_path_of_def(db, target) { + let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db)); + if append_mod && let Some(path) = mod_path_of_def(db, target) { web_url = join_url(https://github.com/web_url,%20&path); local_url = join_url(https://github.com/local_url,%20&path); } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 6af156fa668f5..c2f08f8ca2e6b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -414,6 +414,30 @@ fn foo() { ) } +#[test] +fn external_docs_macro_export() { + check_external_docs( + r#" +//- /lib.rs crate:foo +pub mod inner { + #[macro_export] + macro_rules! my_macro { + () => {}; + } +} + +//- /main.rs crate:bar deps:foo +fn main() { + foo::my_m$0acro!(); +} + "#, + Some("/home/user/project"), + Some(expect![[r#"https://docs.rs/foo/*/foo/macro.my_macro.html"#]]), + Some(expect![[r#"file:///home/user/project/doc/foo/macro.my_macro.html"#]]), + Some("/sysroot"), + ); +} + #[test] fn doc_links_items_simple() { check_doc_links( From 0aa105740c1f9341a816e7f34ec94a4855f6158a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 Aug 2025 17:37:33 +0800 Subject: [PATCH 0029/1900] Add remove simple dbg stmt for remove_dbg Remove only contain literals dbg statement ```rust fn foo() { let n = 2; $0dbg!(3); dbg!(2.6); dbg!(1, 2.5); dbg!('x'); dbg!(&n); dbg!(n); // needless comment dbg!("foo");$0 } ``` -> ```rust fn foo() { // needless comment } ``` Old: ```rust fn foo() { 3; 2.6; (1, 2.5); 'x'; &n; n; // needless comment "foo"; } ``` --- .../ide-assists/src/handlers/remove_dbg.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 9356d02706c93..414f6746d4404 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -112,6 +112,16 @@ fn compute_dbg_replacement( } } } + // dbg!(2, 'x', &x, x, ...); + exprs if ast::ExprStmt::can_cast(parent.kind()) && exprs.iter().all(pure_expr) => { + let mut replace = vec![parent.clone().into()]; + if let Some(prev_sibling) = parent.prev_sibling_or_token() + && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE + { + replace.push(prev_sibling); + } + (replace, None) + } // dbg!(expr0) [expr] => { // dbg!(expr, &parent); @@ -163,6 +173,20 @@ fn compute_dbg_replacement( }) } +fn pure_expr(expr: &ast::Expr) -> bool { + match_ast! { + match (expr.syntax()) { + ast::Literal(_) => true, + ast::RefExpr(it) => { + matches!(it.expr(), Some(ast::Expr::PathExpr(p)) + if p.path().and_then(|p| p.as_single_name_ref()).is_some()) + }, + ast::PathExpr(it) => it.path().and_then(|it| it.as_single_name_ref()).is_some(), + _ => false, + } + } +} + fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { if let ast::Expr::MacroExpr(mac) = &expanded { // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree @@ -231,6 +255,32 @@ mod tests { check("dbg!{$01 + 1}", "1 + 1"); } + #[test] + fn test_remove_simple_dbg_statement() { + check_assist( + remove_dbg, + r#" +fn foo() { + let n = 2; + $0dbg!(3); + dbg!(2.6); + dbg!(1, 2.5); + dbg!('x'); + dbg!(&n); + dbg!(n); + // needless comment + dbg!("foo");$0 +} +"#, + r#" +fn foo() { + let n = 2; + // needless comment +} +"#, + ); + } + #[test] fn test_remove_dbg_not_applicable() { check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}"); From d339009cbfc59f04be15a9ca6c8f2d9ff98aa0f7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Aug 2025 17:30:18 +0200 Subject: [PATCH 0030/1900] Report the incorrect payload when failing to deserialize lsp messages --- src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs | 4 ++-- .../rust-analyzer/crates/ide/src/inlay_hints/param_name.rs | 6 +++++- src/tools/rust-analyzer/lib/lsp-server/src/msg.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 7a8514c47af95..8c2a2f6f72f5e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -228,9 +228,9 @@ fn hints( chaining::hints(hints, famous_defs, config, display_target, &expr); adjustment::hints(hints, famous_defs, config, display_target, &expr); match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, ast::Expr::from(it)), + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, famous_defs, config, ast::Expr::from(it)) + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { closure_captures::hints(hints, famous_defs, config, it.clone()); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index ec0a4c46c7fec..754707784055a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,7 +7,7 @@ use std::iter::zip; use either::Either; -use hir::Semantics; +use hir::{EditionedFileId, Semantics}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use stdx::to_lower_snake_case; @@ -19,6 +19,7 @@ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -39,6 +40,9 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; + if range.file_id != file_id { + return None; + } let param_name = p.name(sema.db)?; Some((p, param_name, arg, range)) }) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 399d674e41d25..b20337fdbfff0 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -175,7 +175,7 @@ impl Message { let msg = match serde_json::from_str(&text) { Ok(msg) => msg, Err(e) => { - return Err(invalid_data!("malformed LSP payload: {:?}", e)); + return Err(invalid_data!("malformed LSP payload `{e:?}`: {text:?}")); } }; From 51c6272baee4e60a525959c2fc9b96103ad5de74 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Aug 2025 17:46:39 +0200 Subject: [PATCH 0031/1900] Fix non-lsp compliant `Response` definition --- src/tools/rust-analyzer/Cargo.lock | 8 ++-- src/tools/rust-analyzer/Cargo.toml | 38 +++++++++---------- .../rust-analyzer/lib/lsp-server/Cargo.toml | 6 +-- .../rust-analyzer/lib/lsp-server/src/msg.rs | 6 +-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5a29379ba4818..80a311a74abe4 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1294,7 +1294,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" dependencies = [ "anyhow", "crossbeam-channel", @@ -1310,9 +1310,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9462c4dc73e17f971ec1f171d44bfffb72e65a130117233388a0ebc7ec5656f9" +checksum = "7d6ada348dbc2703cbe7637b2dda05cff84d3da2819c24abcb305dd613e0ba2e" dependencies = [ "crossbeam-channel", "log", @@ -2007,7 +2007,7 @@ dependencies = [ "intern", "itertools 0.14.0", "load-cargo", - "lsp-server 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e7cf0212bf2a8..01a13a39f7b40 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -100,7 +100,7 @@ ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } # in-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.8" } +lsp-server = { version = "0.7.9" } # non-local crates anyhow = "1.0.98" @@ -125,11 +125,11 @@ memmap2 = "0.9.5" nohash-hasher = "0.2.0" oorandom = "11.1.5" object = { version = "0.36.7", default-features = false, features = [ - "std", - "read_core", - "elf", - "macho", - "pe", + "std", + "read_core", + "elf", + "macho", + "pe", ] } process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" @@ -139,9 +139,9 @@ rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.23.0", default-features = true, features = [ - "rayon", - "salsa_unstable", - "macros", + "rayon", + "salsa_unstable", + "macros", ] } salsa-macros = "0.23.0" semver = "1.0.26" @@ -151,9 +151,9 @@ serde_json = "1.0.140" rustc-hash = "2.1.1" rustc-literal-escaper = "0.0.4" smallvec = { version = "1.15.1", features = [ - "const_new", - "union", - "const_generics", + "const_new", + "union", + "const_generics", ] } smol_str = "0.3.2" temp-dir = "0.1.16" @@ -161,12 +161,12 @@ text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" tracing-subscriber = { version = "0.3.19", default-features = false, features = [ - "registry", - "fmt", - "local-time", - "std", - "time", - "tracing-log", + "registry", + "fmt", + "local-time", + "std", + "time", + "tracing-log", ] } triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" @@ -176,7 +176,7 @@ xshell = "0.2.7" dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] } # We need to freeze the version of the crate, as it needs to match with dashmap hashbrown = { version = "0.14.*", features = [ - "inline-more", + "inline-more", ], default-features = false } [workspace.lints.rust] diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 1fc1da50a0a03..f56a0de616376 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -16,9 +16,9 @@ crossbeam-channel.workspace = true [dev-dependencies] lsp-types = "=0.95" ctrlc = "3.4.7" -anyhow.workspace = true +anyhow.workspace = true rustc-hash.workspace = true -toolchain.workspace = true +toolchain.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 399d674e41d25..0b8f8da4c7d07 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -84,9 +84,9 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub result: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub error: Option, } @@ -94,7 +94,7 @@ pub struct Response { pub struct ResponseError { pub code: i32, pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub data: Option, } From 92b8797a0968de124d120a77252b6df4939a1fce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:11:37 +0000 Subject: [PATCH 0032/1900] Bump tmp from 0.2.3 to 0.2.4 in /editors/code Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.3 to 0.2.4. - [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md) - [Commits](https://github.com/raszi/node-tmp/compare/v0.2.3...v0.2.4) --- updated-dependencies: - dependency-name: tmp dependency-version: 0.2.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 534c24be52e8d..1dc11ee4e4b81 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -5588,9 +5588,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "dev": true, "license": "MIT", "engines": { From 95440527af50ed95e1c4f9fcf7b8a5ebf7a992a7 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Sun, 3 Aug 2025 23:32:35 +0900 Subject: [PATCH 0033/1900] In extract_module.rs, generate ast::Module instead of String --- .../src/handlers/extract_module.rs | 175 +++++++++++------- .../crates/syntax/src/ast/make.rs | 17 ++ 2 files changed, 126 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index da91d0ac28097..e783b8693456c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -1,6 +1,5 @@ -use std::iter; +use std::ops::RangeInclusive; -use either::Either; use hir::{HasSource, ModuleSource}; use ide_db::{ FileId, FxHashMap, FxHashSet, @@ -82,7 +81,15 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti curr_parent_module = ast::Module::cast(mod_syn_opt); } - let mut module = extract_target(&node, ctx.selection_trimmed())?; + let selection_range = ctx.selection_trimmed(); + let (mut module, module_text_range) = if let Some(item) = ast::Item::cast(node.clone()) { + let module = extract_single_target(&item); + (module, node.text_range()) + } else { + let (module, range) = extract_child_target(&node, selection_range)?; + let module_text_range = range.start().text_range().cover(range.end().text_range()); + (module, module_text_range) + }; if module.body_items.is_empty() { return None; } @@ -92,7 +99,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti acc.add( AssistId::refactor_extract("extract_module"), "Extract Module", - module.text_range, + module_text_range, |builder| { //This takes place in three steps: // @@ -110,17 +117,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti //for change_visibility and usages for first point mentioned above in the process let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = - module.get_usages_and_record_fields(ctx); + module.get_usages_and_record_fields(ctx, module_text_range); builder.edit_file(ctx.vfs_file_id()); use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); }); - let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); + let import_items = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); + let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string(); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -157,15 +164,12 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { - for import_path_text_range in import_paths_to_be_removed { - if module.text_range.intersect(import_path_text_range).is_some() { - module.text_range = module.text_range.cover(import_path_text_range); - } else { - builder.delete(import_path_text_range); + for import_item in import_items { + if !module_text_range.contains_range(import_item) { + builder.delete(import_item); } } - - builder.replace(module.text_range, module_def) + builder.replace(module_text_range, module_def) } }, ) @@ -173,38 +177,50 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn generate_module_def( parent_impl: &Option, - module: &mut Module, + module: Module, old_indent: IndentLevel, -) -> String { - let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { - (Either::Left(module.body_items.iter()), old_indent + 2) +) -> ast::Module { + let Module { name, body_items, use_items } = module; + let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let assoc_items = body_items + .into_iter() + .map(|item| item.syntax().clone()) + .filter_map(ast::AssocItem::cast) + .map(|it| it.indent(IndentLevel(1))) + .collect_vec(); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list)); + // Add the import for enum/struct corresponding to given impl block + let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); + let mut module_body_items = use_items; + module_body_items.insert(0, use_impl); + module_body_items.push(ast::Item::Impl(impl_)); + module_body_items } else { - (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + [use_items, body_items].concat() }; - let mut body = items_to_be_processed - .map(|item| item.indent(IndentLevel(1))) - .map(|item| format!("{new_item_indent}{item}")) - .join("\n\n"); + let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); + let module_body = make::item_list(Some(items)); - if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { - let impl_indent = old_indent + 1; - body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + let module_name = make::name(name); + make::mod_(module_name, Some(module_body)).indent(old_indent) +} - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items.iter() { - body = format!("{impl_indent}{item}\n\n{body}"); - } - } +fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { + let super_path = make::ext::ident_path("super"); + let node_path = make::ext::ident_path(&node_syntax.to_string()); + let use_ = make::use_( + None, + None, + make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), + ); - let module_name = module.name; - format!("mod {module_name} {{\n{body}\n{old_indent}}}") + ast::Item::from(use_) } #[derive(Debug)] struct Module { - text_range: TextRange, name: &'static str, /// All items except use items. body_items: Vec, @@ -214,22 +230,37 @@ struct Module { use_items: Vec, } -fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option { +fn extract_single_target(node: &ast::Item) -> Module { + let (body_items, use_items) = if matches!(node, ast::Item::Use(_)) { + (Vec::new(), vec![node.clone()]) + } else { + (vec![node.clone()], Vec::new()) + }; + let name = "modname"; + Module { name, body_items, use_items } +} + +fn extract_child_target( + node: &SyntaxNode, + selection_range: TextRange, +) -> Option<(Module, RangeInclusive)> { let selected_nodes = node .children() .filter(|node| selection_range.contains_range(node.text_range())) - .chain(iter::once(node.clone())); - let (use_items, body_items) = selected_nodes .filter_map(ast::Item::cast) - .partition(|item| matches!(item, ast::Item::Use(..))); - - Some(Module { text_range: selection_range, name: "modname", body_items, use_items }) + .collect_vec(); + let start = selected_nodes.first()?.syntax().clone(); + let end = selected_nodes.last()?.syntax().clone(); + let (use_items, body_items): (Vec, Vec) = + selected_nodes.into_iter().partition(|item| matches!(item, ast::Item::Use(..))); + Some((Module { name: "modname", body_items, use_items }, start..=end)) } impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, ) -> (FxHashMap>, Vec, FxHashMap) { let mut adt_fields = Vec::new(); @@ -247,7 +278,7 @@ impl Module { ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx, replace_range,node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -281,30 +312,30 @@ impl Module { ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -318,6 +349,7 @@ impl Module { fn expand_and_group_usages_file_wise( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap>, use_stmts_to_be_inserted: &mut FxHashMap, @@ -327,7 +359,7 @@ impl Module { syntax::NodeOrToken::Node(node) => node, syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic }; - let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let out_of_sel = |node: &SyntaxNode| !replace_range.contains_range(node.text_range()); let mut use_stmts_set = FxHashSet::default(); for (file_id, refs) in node_def.usages(&ctx.sema).all() { @@ -527,7 +559,8 @@ impl Module { // mod -> ust_stmt transversal // true | false -> super import insertion // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } None => {} } @@ -556,7 +589,8 @@ impl Module { use_tree_paths = Some(use_tree_str); } else if def_in_mod && def_out_sel { - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } } @@ -596,20 +630,6 @@ impl Module { import_path_to_be_removed } - fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item { - let super_path = make::ext::ident_path("super"); - let node_path = make::ext::ident_path(&node_syntax.to_string()); - let use_ = make::use_( - None, - None, - make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), - ); - - let item = ast::Item::from(use_); - self.use_items.insert(0, item.clone()); - item - } - fn process_use_stmt_for_import_resolve( &self, use_stmt: Option, @@ -1424,10 +1444,10 @@ $0fn foo(x: B) {}$0 struct B {} mod modname { - use super::B; - use super::A; + use super::B; + impl A { pub(crate) fn foo(x: B) {} } @@ -1739,4 +1759,27 @@ fn main() { "#, ); } + + #[test] + fn test_miss_select_item() { + check_assist( + extract_module, + r#" +mod foo { + mod $0bar { + fn foo(){}$0 + } +} +"#, + r#" +mod foo { + mod modname { + pub(crate) mod bar { + fn foo(){} + } + } +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index c5ca609760187..9897fd0941570 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -231,6 +231,23 @@ pub fn ty_fn_ptr>( } } +pub fn item_list(body: Option>) -> ast::ItemList { + let is_break_braces = body.is_some(); + let body_newline = if is_break_braces { "\n" } else { "" }; + let body_indent = if is_break_braces { " " } else { "" }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), + None => String::new(), + }; + ast_from_text(&format!("mod C {{{body_newline}{body_indent}{body}{body_newline}}}")) +} + +pub fn mod_(name: ast::Name, body: Option) -> ast::Module { + let body = body.map_or(";".to_owned(), |body| format!(" {body}")); + ast_from_text(&format!("mod {name}{body}")) +} + pub fn assoc_item_list(body: Option>) -> ast::AssocItemList { let is_break_braces = body.is_some(); let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() }; From 47cc8b75f0148b1c4f6f95571fbfda7b2d8b3e9d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:12:26 +0200 Subject: [PATCH 0034/1900] Enable warning logs by default --- src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index ab045e0bf9ff1..7602d379c0df9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -160,9 +160,9 @@ fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { rust_analyzer::tracing::Config { writer, - // Deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually + // Deliberately enable all `warn` logs if the user has not set RA_LOG, as there is usually // useful information in there for debugging. - filter: env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()), + filter: env::var("RA_LOG").ok().unwrap_or_else(|| "warn".to_owned()), chalk_filter: env::var("CHALK_DEBUG").ok(), profile_filter: env::var("RA_PROFILE").ok(), json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(), From 1a73720b8051a533b402ac67e35f84d405ccb28d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:14:22 +0200 Subject: [PATCH 0035/1900] Disable error log for position clamping, its too noisy due to ease of triggering --- .../crates/rust-analyzer/src/lsp/from_proto.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs index 02757616d4ffd..333826a1790e4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs @@ -40,12 +40,13 @@ pub(crate) fn offset( })?; let col = TextSize::from(line_col.col); let clamped_len = col.min(line_range.len()); - if clamped_len < col { - tracing::error!( - "Position {line_col:?} column exceeds line length {}, clamping it", - u32::from(line_range.len()), - ); - } + // FIXME: The cause for this is likely our request retrying. Commented out as this log is just too chatty and very easy to trigger. + // if clamped_len < col { + // tracing::error!( + // "Position {line_col:?} column exceeds line length {}, clamping it", + // u32::from(line_range.len()), + // ); + // } Ok(line_range.start() + clamped_len) } From 5b9e6f1b75f14d916bc3d43c21e1e845cc049dbf Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 8 Aug 2025 23:48:12 +0800 Subject: [PATCH 0036/1900] Add write! and writeln! to minicore --- .../crates/test-utils/src/minicore.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7b719b5dec754..c1f2b08be942c 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -70,6 +70,7 @@ //! tuple: //! unpin: sized //! unsize: sized +//! write: fmt //! todo: panic //! unimplemented: panic //! column: @@ -1769,6 +1770,26 @@ mod macros { } // endregion:panic + // region:write + #[macro_export] + macro_rules! write { + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args!($($arg)*)) + }; + } + + #[macro_export] + #[allow_internal_unstable(format_args_nl)] + macro_rules! writeln { + ($dst:expr $(,)?) => { + $crate::write!($dst, "\n") + }; + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args_nl!($($arg)*)) + }; + } + // endregion:write + // region:assert #[macro_export] #[rustc_builtin_macro] From bb4fded8a35a961b749846df45f6caf55d98e5d3 Mon Sep 17 00:00:00 2001 From: BenjaminBrienen Date: Fri, 8 Aug 2025 23:46:28 +0200 Subject: [PATCH 0037/1900] remove duplicate field in Debug --- src/tools/rust-analyzer/crates/project-model/src/workspace.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 5b36e10fd6925..00032bcf1d2e4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -157,7 +157,6 @@ impl fmt::Debug for ProjectWorkspace { .field("file", &file) .field("cargo_script", &cargo_script.is_some()) .field("n_sysroot_crates", &sysroot.num_packages()) - .field("cargo_script", &cargo_script.is_some()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) From e3d99ddcbc010de4942df34e6d5491c4b664152c Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 9 Aug 2025 14:53:34 +0900 Subject: [PATCH 0038/1900] fix: generate function by indet token --- .../src/handlers/generate_function.rs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index efdb20c1e9317..88ed6f9ce124d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -316,7 +316,7 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(&name.text()); + let fn_name = make::name(name.ident_token()?.text()); let mut necessary_generic_params = FxHashSet::default(); necessary_generic_params.extend(receiver_ty.generic_params(ctx.db())); let params = fn_args( @@ -3131,4 +3131,32 @@ fn main() { "#, ) } + + #[test] + fn no_generate_method_by_keyword() { + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.super$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.Self$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.self$0(); +} + "#, + ); + } } From eb2bbbb9134ae3fc4d043ea12b3546b73cd90efd Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 1 Aug 2025 14:29:21 +0000 Subject: [PATCH 0039/1900] Implement next trait solver --- src/tools/rust-analyzer/Cargo.lock | 83 +- src/tools/rust-analyzer/Cargo.toml | 11 +- .../crates/hir-def/src/lang_item.rs | 7 +- .../crates/hir-def/src/signatures.rs | 6 +- .../rust-analyzer/crates/hir-ty/Cargo.toml | 10 +- .../crates/hir-ty/src/autoderef.rs | 4 +- .../crates/hir-ty/src/chalk_db.rs | 610 +---- .../crates/hir-ty/src/chalk_ext.rs | 9 +- .../crates/hir-ty/src/consteval/tests.rs | 3 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 256 ++ .../rust-analyzer/crates/hir-ty/src/db.rs | 159 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../crates/hir-ty/src/dyn_compatibility.rs | 41 +- .../hir-ty/src/dyn_compatibility/tests.rs | 25 +- .../crates/hir-ty/src/generics.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 107 +- .../crates/hir-ty/src/infer/closure.rs | 9 +- .../crates/hir-ty/src/infer/coerce.rs | 31 +- .../crates/hir-ty/src/infer/expr.rs | 11 +- .../crates/hir-ty/src/infer/unify.rs | 192 +- .../crates/hir-ty/src/interner.rs | 25 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 364 +-- .../crates/hir-ty/src/layout/adt.rs | 46 +- .../crates/hir-ty/src/layout/tests.rs | 3 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 133 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 49 +- .../crates/hir-ty/src/lower_nextsolver.rs | 1609 ++++++++++++ .../hir-ty/src/lower_nextsolver/path.rs | 1459 +++++++++++ .../crates/hir-ty/src/method_resolution.rs | 135 +- .../crates/hir-ty/src/mir/eval/tests.rs | 5 +- .../crates/hir-ty/src/mir/lower.rs | 4 +- .../crates/hir-ty/src/next_solver.rs | 46 + .../crates/hir-ty/src/next_solver/abi.rs | 68 + .../crates/hir-ty/src/next_solver/consts.rs | 404 +++ .../crates/hir-ty/src/next_solver/def_id.rs | 96 + .../crates/hir-ty/src/next_solver/fold.rs | 129 + .../crates/hir-ty/src/next_solver/fulfill.rs | 229 ++ .../hir-ty/src/next_solver/generic_arg.rs | 525 ++++ .../crates/hir-ty/src/next_solver/generics.rs | 147 ++ .../crates/hir-ty/src/next_solver/infer/at.rs | 333 +++ .../infer/canonical/instantiate.rs | 106 + .../src/next_solver/infer/canonical/mod.rs | 156 ++ .../hir-ty/src/next_solver/infer/context.rs | 316 +++ .../hir-ty/src/next_solver/infer/mod.rs | 1066 ++++++++ .../src/next_solver/infer/opaque_types/mod.rs | 56 + .../next_solver/infer/opaque_types/table.rs | 166 ++ .../infer/region_constraints/mod.rs | 640 +++++ .../next_solver/infer/relate/generalize.rs | 720 ++++++ .../next_solver/infer/relate/higher_ranked.rs | 89 + .../src/next_solver/infer/relate/mod.rs | 13 + .../hir-ty/src/next_solver/infer/resolve.rs | 62 + .../src/next_solver/infer/snapshot/mod.rs | 111 + .../next_solver/infer/snapshot/undo_log.rs | 198 ++ .../hir-ty/src/next_solver/infer/traits.rs | 175 ++ .../src/next_solver/infer/type_variable.rs | 268 ++ .../hir-ty/src/next_solver/infer/unify_key.rs | 176 ++ .../crates/hir-ty/src/next_solver/interner.rs | 2173 +++++++++++++++++ .../crates/hir-ty/src/next_solver/ir_print.rs | 267 ++ .../crates/hir-ty/src/next_solver/mapping.rs | 1368 +++++++++++ .../crates/hir-ty/src/next_solver/opaques.rs | 167 ++ .../hir-ty/src/next_solver/predicate.rs | 894 +++++++ .../crates/hir-ty/src/next_solver/project.rs | 3 + .../next_solver/project/solve_normalize.rs | 264 ++ .../crates/hir-ty/src/next_solver/region.rs | 332 +++ .../crates/hir-ty/src/next_solver/solver.rs | 289 +++ .../crates/hir-ty/src/next_solver/ty.rs | 943 +++++++ .../crates/hir-ty/src/next_solver/util.rs | 1064 ++++++++ .../rust-analyzer/crates/hir-ty/src/tests.rs | 23 +- .../hir-ty/src/tests/closure_captures.rs | 3 +- .../crates/hir-ty/src/tests/coercion.rs | 16 +- .../crates/hir-ty/src/tests/incremental.rs | 20 +- .../crates/hir-ty/src/tests/macros.rs | 24 +- .../hir-ty/src/tests/method_resolution.rs | 34 +- .../crates/hir-ty/src/tests/regression.rs | 37 +- .../crates/hir-ty/src/tests/simple.rs | 4 +- .../crates/hir-ty/src/tests/traits.rs | 100 +- .../rust-analyzer/crates/hir-ty/src/tls.rs | 2 + .../rust-analyzer/crates/hir-ty/src/traits.rs | 252 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 5 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 172 +- .../handlers/generate_from_impl_for_enum.rs | 24 +- .../generate_single_field_struct_from.rs | 17 +- .../crates/ide-assists/src/lib.rs | 9 +- .../crates/ide-assists/src/tests.rs | 9 +- .../ide-completion/src/completions/dot.rs | 7 +- .../crates/ide-completion/src/lib.rs | 5 +- .../crates/ide-completion/src/tests.rs | 4 +- .../ide-completion/src/tests/flyimport.rs | 39 +- .../ide-completion/src/tests/special.rs | 3 + .../src/handlers/moved_out_of_ref.rs | 4 +- .../crates/ide-diagnostics/src/lib.rs | 12 +- .../crates/ide-diagnostics/src/tests.rs | 3 + .../crates/ide-ssr/src/resolving.rs | 26 +- .../crates/ide/src/doc_links/tests.rs | 7 +- .../crates/ide/src/goto_definition.rs | 4 +- .../crates/ide/src/goto_implementation.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 5 +- .../crates/ide/src/hover/render.rs | 3 +- .../crates/ide/src/hover/tests.rs | 3 + .../crates/ide/src/inlay_hints.rs | 6 +- .../crates/ide/src/inlay_hints/adjustment.rs | 10 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 2 +- .../crates/ide/src/signature_help.rs | 4 +- .../crates/ide/src/syntax_highlighting.rs | 22 +- .../test_data/highlight_general.html | 12 +- .../crates/intern/src/symbol/symbols.rs | 5 + .../crates/profile/src/stop_watch.rs | 8 +- .../crates/query-group-macro/src/queries.rs | 3 +- .../crates/test-utils/src/minicore.rs | 15 +- 109 files changed, 19140 insertions(+), 1309 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 80a311a74abe4..f72e698f60631 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -259,9 +259,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chalk-derive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb4899682de915ca7c0b025bdd0a3d34c75fe12184122fda6805a7baddaa293c" +checksum = "9ea9b1e80910f66ae87c772247591432032ef3f6a67367ff17f8343db05beafa" dependencies = [ "proc-macro2", "quote", @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a37d2ab99352b4caca135061e7b4ac67024b648c28ed0b787feec4bea4caed" +checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8" dependencies = [ "bitflags 2.9.1", "chalk-derive", @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c855be60e646664bc37c2496d3dc81ca5ef60520930e5e0f0057a0575aff6c19" +checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4" dependencies = [ "chalk-derive", "chalk-ir", @@ -294,9 +294,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "477ac6cdfd2013e9f93b09b036c2b607a67b2e728f4777b8422d55a79e9e3a34" +checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927" dependencies = [ "chalk-derive", "chalk-ir", @@ -445,6 +445,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive-where" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_arbitrary" version = "1.4.1" @@ -696,6 +707,7 @@ dependencies = [ "indexmap", "intern", "itertools 0.14.0", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "smallvec", "span", @@ -705,6 +717,8 @@ dependencies = [ "test-fixture", "test-utils", "tracing", + "tracing-subscriber", + "tracing-tree", "triomphe", "tt", ] @@ -801,8 +815,11 @@ dependencies = [ "project-model", "query-group-macro", "ra-ap-rustc_abi", + "ra-ap-rustc_ast_ir", "ra-ap-rustc_index", + "ra-ap-rustc_next_trait_solver", "ra-ap-rustc_pattern_analysis", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "rustc_apfloat", "salsa", @@ -1856,6 +1873,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "ra-ap-rustc_ast_ir" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80" + [[package]] name = "ra-ap-rustc_hashes" version = "0.123.0" @@ -1908,6 +1931,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ra-ap-rustc_next_trait_solver" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441" +dependencies = [ + "derive-where", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir", + "ra-ap-rustc_type_ir_macros", + "tracing", +] + [[package]] name = "ra-ap-rustc_parse_format" version = "0.121.0" @@ -1931,6 +1967,37 @@ dependencies = [ "tracing", ] +[[package]] +name = "ra-ap-rustc_type_ir" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b" +dependencies = [ + "bitflags 2.9.1", + "derive-where", + "ena", + "indexmap", + "ra-ap-rustc_ast_ir", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir_macros", + "rustc-hash 2.1.1", + "smallvec", + "thin-vec", + "tracing", +] + +[[package]] +name = "ra-ap-rustc_type_ir_macros" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "rayon" version = "1.10.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 01a13a39f7b40..bb8444ed73422 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -94,6 +94,9 @@ ra-ap-rustc_parse_format = { version = "0.121", default-features = false } ra-ap-rustc_index = { version = "0.123", default-features = false } ra-ap-rustc_abi = { version = "0.123", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.123", default-features = false } +ra-ap-rustc_type_ir = { version = "0.123", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false } # local crates that aren't published to crates.io. These should not have versions. @@ -108,10 +111,10 @@ arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-solve = { version = "0.103.0", default-features = false } -chalk-ir = "0.103.0" -chalk-recursive = { version = "0.103.0", default-features = false } -chalk-derive = "0.103.0" +chalk-solve = { version = "0.104.0", default-features = false } +chalk-ir = "0.104.0" +chalk-recursive = { version = "0.104.0", default-features = false } +chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" dot = "0.1.4" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index d431f2140165e..a0be69cb2f96e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -383,12 +383,17 @@ language_item_table! { AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - AsyncFnOnceOutput, sym::async_fn_once_output,async_fn_once_output, Target::AssocTy, GenericRequirement::None; + CallRefFuture, sym::call_ref_future, call_ref_future_ty, Target::AssocTy, GenericRequirement::None; + CallOnceFuture, sym::call_once_future, call_once_future_ty, Target::AssocTy, GenericRequirement::None; + AsyncFnOnceOutput, sym::async_fn_once_output, async_fn_once_output_ty, Target::AssocTy, GenericRequirement::None; + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); + CoroutineReturn, sym::coroutine_return, coroutine_return_ty, Target::AssocTy, GenericRequirement::None; + CoroutineYield, sym::coroutine_yield, coroutine_yield_ty, Target::AssocTy, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 92e610b36acd0..598b2c0314a94 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -395,7 +395,7 @@ impl ImplSignature { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub struct TraitFlags: u8 { + pub struct TraitFlags: u16 { const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; const FUNDAMENTAL = 1 << 2; const UNSAFE = 1 << 3; @@ -403,6 +403,7 @@ bitflags::bitflags! { const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5; const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; const RUSTC_PAREN_SUGAR = 1 << 7; + const COINDUCTIVE = 1 << 8; } } @@ -436,6 +437,9 @@ impl TraitSignature { if attrs.by_key(sym::rustc_paren_sugar).exists() { flags |= TraitFlags::RUSTC_PAREN_SUGAR; } + if attrs.by_key(sym::rustc_coinductive).exists() { + flags |= TraitFlags::COINDUCTIVE; + } let mut skip_array_during_method_dispatch = attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists(); let mut skip_boxed_slice_during_method_dispatch = false; diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 7cc0a26d37c80..64182110e1862 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -40,7 +40,14 @@ salsa-macros.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true ra-ap-rustc_pattern_analysis.workspace = true +ra-ap-rustc_ast_ir.workspace = true +ra-ap-rustc_type_ir.workspace = true +ra-ap-rustc_next_trait_solver.workspace = true +# These moved to dev deps if `setup_tracing` was a macro and dependents also +# included these +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps stdx.workspace = true @@ -53,9 +60,6 @@ span.workspace = true [dev-dependencies] expect-test = "1.5.1" -tracing.workspace = true -tracing-subscriber.workspace = true -tracing-tree.workspace = true project-model.workspace = true # local deps diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 26ca7fb9a15ec..4ea0156e1248a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -225,7 +225,9 @@ pub(crate) fn deref_by_trait( // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); let implements_goal: Goal = trait_ref.cast(Interner); - table.try_obligation(implements_goal.clone())?; + if table.try_obligation(implements_goal.clone()).no_solution() { + return None; + } table.register_obligation(implements_goal); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 3ba7c93d4fb76..f523a5e8bfa05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,47 +1,36 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use core::ops; -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::sync::Arc; -use hir_expand::name::Name; -use intern::sym; -use span::Edition; use tracing::debug; -use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; +use chalk_ir::{cast::Caster, fold::shift::Shift}; +use chalk_solve::rust_ir::{self, WellKnownTrait}; use base_db::Crate; use hir_def::{ - AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, - TypeAliasId, VariantId, - hir::Movability, + AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, + VariantId, lang_item::LangItem, signatures::{ImplFlags, StructFlags, TraitFlags}, }; use crate::{ - AliasEq, AliasTy, BoundVar, DebruijnIndex, Interner, ProjectionTy, ProjectionTyExt, - QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - WhereClause, - db::{HirDatabase, InternedCoroutine}, - from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause, + Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, generics::generics, lower::LifetimeElisionKind, - make_binders, make_single_type_binders, + make_binders, mapping::{ToChalk, TypeAliasAsValue, from_chalk}, - method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, to_assoc_type_id, to_chalk_trait_id, - traits::ChalkContext, - utils::ClosureSubst, - wrap_empty_binders, }; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum; pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum; pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum; -pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; @@ -52,551 +41,6 @@ pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; pub(crate) type Variances = chalk_ir::Variances; -impl chalk_solve::RustIrDatabase for ChalkContext<'_> { - fn associated_ty_data(&self, id: AssocTypeId) -> Arc { - self.db.associated_ty_data(from_assoc_type_id(id)) - } - fn associated_ty_from_impl( - &self, - impl_id: chalk_ir::ImplId, - assoc_type_id: chalk_ir::AssocTypeId, - ) -> Option> { - let alias_id = from_assoc_type_id(assoc_type_id); - let trait_sig = self.db.type_alias_signature(alias_id); - hir_def::ImplId::from_chalk(self.db, impl_id).impl_items(self.db).items.iter().find_map( - |(name, item)| match item { - AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => { - Some(TypeAliasAsValue(*alias).to_chalk(self.db)) - } - _ => None, - }, - ) - } - fn trait_datum(&self, trait_id: TraitId) -> Arc { - self.db.trait_datum(self.krate, trait_id) - } - fn adt_datum(&self, struct_id: AdtId) -> Arc { - self.db.adt_datum(self.krate, struct_id) - } - fn adt_repr(&self, _struct_id: AdtId) -> Arc> { - // FIXME: keep track of these - Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None }) - } - fn discriminant_type(&self, ty: chalk_ir::Ty) -> chalk_ir::Ty { - if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner) - && let hir_def::AdtId::EnumId(e) = id.0 - { - let enum_data = self.db.enum_signature(e); - let ty = enum_data.repr.unwrap_or_default().discr_type(); - return chalk_ir::TyKind::Scalar(match ty { - hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize), - false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(match size { - hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8, - hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16, - hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32, - hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64, - hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128, - }), - false => chalk_ir::Scalar::Uint(match size { - hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8, - hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16, - hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32, - hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64, - hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128, - }), - }, - }) - .intern(Interner); - } - chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner) - } - fn impl_datum(&self, impl_id: ImplId) -> Arc { - self.db.impl_datum(self.krate, impl_id) - } - - fn fn_def_datum( - &self, - fn_def_id: chalk_ir::FnDefId, - ) -> Arc> { - self.db.fn_def_datum(from_chalk(self.db, fn_def_id)) - } - - fn impls_for_trait( - &self, - trait_id: TraitId, - parameters: &[chalk_ir::GenericArg], - binders: &CanonicalVarKinds, - ) -> Vec { - debug!("impls_for_trait {:?}", trait_id); - let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id); - - let ty: Ty = parameters[0].assert_ty_ref(Interner).clone(); - - fn binder_kind( - ty: &Ty, - binders: &CanonicalVarKinds, - ) -> Option { - if let TyKind::BoundVar(bv) = ty.kind(Interner) { - let binders = binders.as_slice(Interner); - if bv.debruijn == DebruijnIndex::INNERMOST - && let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind - { - return Some(tk); - } - } - None - } - - let self_ty_fp = TyFingerprint::for_trait_impl(&ty); - let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { - Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, - Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, - _ => self_ty_fp.as_slice(), - }; - - let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); - - let mut result = vec![]; - if fps.is_empty() { - debug!("Unrestricted search for {:?} impls...", trait_); - _ = self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(impls.for_trait(trait_).map(id_to_chalk)); - ControlFlow::Continue(()) - }); - } else { - _ = - self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(fps.iter().flat_map(move |fp| { - impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) - })); - ControlFlow::Continue(()) - }); - }; - - debug!("impls_for_trait returned {} impls", result.len()); - result - } - - fn impl_provided_for(&self, auto_trait_id: TraitId, kind: &chalk_ir::TyKind) -> bool { - debug!("impl_provided_for {:?}, {:?}", auto_trait_id, kind); - - let trait_id = from_chalk_trait_id(auto_trait_id); - let self_ty = kind.clone().intern(Interner); - // We cannot filter impls by `TyFingerprint` for the following types: - let self_ty_fp = match kind { - // because we need to find any impl whose Self type is a ref with the same mutability - // (we don't care about the inner type). - TyKind::Ref(..) => None, - // because we need to find any impl whose Self type is a tuple with the same arity. - TyKind::Tuple(..) => None, - _ => TyFingerprint::for_trait_impl(&self_ty), - }; - - let check_kind = |impl_id| { - let impl_self_ty = self.db.impl_self_ty(impl_id); - // NOTE(skip_binders): it's safe to skip binders here as we don't check substitutions. - let impl_self_kind = impl_self_ty.skip_binders().kind(Interner); - - match (kind, impl_self_kind) { - (TyKind::Adt(id_a, _), TyKind::Adt(id_b, _)) => id_a == id_b, - (TyKind::AssociatedType(id_a, _), TyKind::AssociatedType(id_b, _)) => id_a == id_b, - (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => scalar_a == scalar_b, - (TyKind::Error, TyKind::Error) - | (TyKind::Str, TyKind::Str) - | (TyKind::Slice(_), TyKind::Slice(_)) - | (TyKind::Never, TyKind::Never) - | (TyKind::Array(_, _), TyKind::Array(_, _)) => true, - (TyKind::Tuple(arity_a, _), TyKind::Tuple(arity_b, _)) => arity_a == arity_b, - (TyKind::OpaqueType(id_a, _), TyKind::OpaqueType(id_b, _)) => id_a == id_b, - (TyKind::FnDef(id_a, _), TyKind::FnDef(id_b, _)) => id_a == id_b, - (TyKind::Ref(id_a, _, _), TyKind::Ref(id_b, _, _)) - | (TyKind::Raw(id_a, _), TyKind::Raw(id_b, _)) => id_a == id_b, - (TyKind::Closure(id_a, _), TyKind::Closure(id_b, _)) => id_a == id_b, - (TyKind::Coroutine(id_a, _), TyKind::Coroutine(id_b, _)) - | (TyKind::CoroutineWitness(id_a, _), TyKind::CoroutineWitness(id_b, _)) => { - id_a == id_b - } - (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => id_a == id_b, - (_, _) => false, - } - }; - - if let Some(fp) = self_ty_fp { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait_and_self_ty(trait_id, fp).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } else { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait(trait_id).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } - .is_break() - } - - fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { - self.db.associated_ty_value(self.krate, id) - } - - fn custom_clauses(&self) -> Vec> { - vec![] - } - fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec { - // We don't do coherence checking (yet) - unimplemented!() - } - fn interner(&self) -> Interner { - Interner - } - fn well_known_trait_id( - &self, - well_known_trait: WellKnownTrait, - ) -> Option> { - let lang_item = lang_item_from_well_known_trait(well_known_trait); - let trait_ = lang_item.resolve_trait(self.db, self.krate)?; - Some(to_chalk_trait_id(trait_)) - } - fn well_known_assoc_type_id( - &self, - assoc_type: rust_ir::WellKnownAssocType, - ) -> Option> { - let lang_item = match assoc_type { - rust_ir::WellKnownAssocType::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, - }; - let alias = lang_item.resolve_type_alias(self.db, self.krate)?; - Some(to_assoc_type_id(alias)) - } - - fn program_clauses_for_env( - &self, - environment: &chalk_ir::Environment, - ) -> chalk_ir::ProgramClauses { - self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone()) - } - - fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { - let full_id = self.db.lookup_intern_impl_trait_id(id.into()); - let bound = match full_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = self - .db - .return_type_impl_traits(func) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = self - .db - .type_alias_impl_traits(alias) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { - if let Some((future_trait, future_output)) = - LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| { - let alias = trait_ - .trait_items(self.db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - Some((trait_, alias)) - }) - { - // Making up Symbol’s value as variable is void: AsyncBlock: - // - // |--------------------OpaqueTyDatum-------------------| - // |-------------OpaqueTyDatumBound--------------| - // for [Future, Future::Output = T] - // ^1 ^0 ^0 ^0 ^1 - let impl_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(future_trait), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - let mut binder = vec![]; - binder.push(crate::wrap_empty_binders(impl_bound)); - let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate); - if let Some(sized_trait_) = sized_trait { - let sized_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(sized_trait_), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - binder.push(crate::wrap_empty_binders(sized_bound)); - } - let proj_bound = WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(future_output), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - ), - }), - // The parameter of the opaque type. - ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }) - .intern(Interner), - }); - binder.push(crate::wrap_empty_binders(proj_bound)); - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(binder), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } else { - // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. - let bound = OpaqueTyDatumBound { - bounds: chalk_ir::Binders::empty(Interner, vec![]), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } - } - }; - - Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound }) - } - - fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { - // FIXME: actually provide the hidden type; it is relevant for auto traits - TyKind::Error.intern(Interner) - } - - // object safety was renamed to dyn-compatibility but still remains here in chalk. - // This will be removed since we are going to migrate to next-gen trait solver. - fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { - let trait_ = from_chalk_trait_id(trait_id); - crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none() - } - - fn closure_kind( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> rust_ir::ClosureKind { - // Fn is the closure kind that implements all three traits - rust_ir::ClosureKind::Fn - } - fn closure_inputs_and_output( - &self, - _closure_id: chalk_ir::ClosureId, - substs: &chalk_ir::Substitution, - ) -> chalk_ir::Binders> { - let sig_ty = ClosureSubst(substs).sig_ty(); - let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); - let io = rust_ir::FnDefInputsAndOutputDatum { - argument_types: sig.params().to_vec(), - return_type: sig.ret().clone(), - }; - chalk_ir::Binders::empty(Interner, io.shifted_in(Interner)) - } - fn closure_upvars( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> chalk_ir::Binders> { - let ty = TyBuilder::unit(); - chalk_ir::Binders::empty(Interner, ty) - } - fn closure_fn_substitution( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> chalk_ir::Substitution { - Substitution::empty(Interner) - } - - fn trait_name(&self, trait_id: chalk_ir::TraitId) -> String { - let id = from_chalk_trait_id(trait_id); - self.db.trait_signature(id).name.display(self.db, self.edition()).to_string() - } - fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { - let edition = self.edition(); - match adt_id { - hir_def::AdtId::StructId(id) => { - self.db.struct_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::EnumId(id) => { - self.db.enum_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::UnionId(id) => { - self.db.union_signature(id).name.display(self.db, edition).to_string() - } - } - } - fn adt_size_align(&self, _id: chalk_ir::AdtId) -> Arc { - // FIXME - Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false)) - } - fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { - let id = self.db.associated_ty_data(from_assoc_type_id(assoc_ty_id)).name; - self.db.type_alias_signature(id).name.display(self.db, self.edition()).to_string() - } - fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { - format!("Opaque_{:?}", opaque_ty_id.0) - } - fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId) -> String { - format!("fn_{:?}", fn_def_id.0) - } - fn coroutine_datum( - &self, - id: chalk_ir::CoroutineId, - ) -> Arc> { - let InternedCoroutine(parent, expr) = self.db.lookup_intern_coroutine(id.into()); - - // We fill substitution with unknown type, because we only need to know whether the generic - // params are types or consts to build `Binders` and those being filled up are for - // `resume_type`, `yield_type`, and `return_type` of the coroutine in question. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - - let len = subst.len(Interner); - let input_output = rust_ir::CoroutineInputOutputDatum { - resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 3)) - .intern(Interner), - yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 2)) - .intern(Interner), - return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 1)) - .intern(Interner), - // FIXME: calculate upvars - upvars: vec![], - }; - - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let input_output = crate::make_type_and_const_binders(it, input_output); - - let movability = match self.db.body(parent)[expr] { - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(movability), - .. - } => movability, - _ => unreachable!("non coroutine expression interned as coroutine"), - }; - let movability = match movability { - Movability::Static => rust_ir::Movability::Static, - Movability::Movable => rust_ir::Movability::Movable, - }; - - Arc::new(rust_ir::CoroutineDatum { movability, input_output }) - } - fn coroutine_witness_datum( - &self, - id: chalk_ir::CoroutineId, - ) -> Arc> { - // FIXME: calculate inner types - let inner_types = - rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) }; - - let InternedCoroutine(parent, _) = self.db.lookup_intern_coroutine(id.into()); - // See the comment in `coroutine_datum()` for unknown types. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let inner_types = crate::make_type_and_const_binders(it, inner_types); - - Arc::new(rust_ir::CoroutineWitnessDatum { inner_types }) - } - - fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { - &self.db - } -} - -impl ChalkContext<'_> { - fn edition(&self) -> Edition { - self.krate.data(self.db).edition - } - - fn for_trait_impls( - &self, - trait_id: hir_def::TraitId, - self_ty_fp: Option, - mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, - ) -> ControlFlow<()> { - // Note: Since we're using `impls_for_trait` and `impl_provided_for`, - // only impls where the trait can be resolved should ever reach Chalk. - // `impl_datum` relies on that and will panic if the trait can't be resolved. - let in_deps = self.db.trait_impls_in_deps(self.krate); - let in_self = self.db.trait_impls_in_crate(self.krate); - let trait_module = trait_id.module(self.db); - let type_module = match self_ty_fp { - Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db)), - Some(TyFingerprint::ForeignType(type_id)) => { - Some(from_foreign_def_id(type_id).module(self.db)) - } - Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db)), - _ => None, - }; - - let mut def_blocks = - [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; - - let block_impls = iter::successors(self.block, |&block_id| { - cov_mark::hit!(block_local_impls); - block_id.loc(self.db).module.containing_block() - }) - .inspect(|&block_id| { - // make sure we don't search the same block twice - def_blocks.iter_mut().for_each(|block| { - if *block == Some(block_id) { - *block = None; - } - }); - }) - .filter_map(|block_id| self.db.trait_impls_in_block(block_id)); - f(&in_self)?; - for it in in_deps.iter().map(ops::Deref::deref) { - f(it)?; - } - for it in block_impls { - f(&it)?; - } - for it in def_blocks.into_iter().flatten().filter_map(|it| self.db.trait_impls_in_block(it)) - { - f(&it)?; - } - ControlFlow::Continue(()) - } -} - impl chalk_ir::UnificationDatabase for &dyn HirDatabase { fn fn_def_variance( &self, @@ -610,15 +54,6 @@ impl chalk_ir::UnificationDatabase for &dyn HirDatabase { } } -pub(crate) fn program_clauses_for_chalk_env_query( - db: &dyn HirDatabase, - krate: Crate, - block: Option, - environment: chalk_ir::Environment, -) -> chalk_ir::ProgramClauses { - chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment) -} - pub(crate) fn associated_ty_data_query( db: &dyn HirDatabase, type_alias: TypeAliasId, @@ -749,31 +184,6 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option { }) } -fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem { - match trait_ { - WellKnownTrait::Clone => LangItem::Clone, - WellKnownTrait::CoerceUnsized => LangItem::CoerceUnsized, - WellKnownTrait::Copy => LangItem::Copy, - WellKnownTrait::DiscriminantKind => LangItem::DiscriminantKind, - WellKnownTrait::DispatchFromDyn => LangItem::DispatchFromDyn, - WellKnownTrait::Drop => LangItem::Drop, - WellKnownTrait::Fn => LangItem::Fn, - WellKnownTrait::FnMut => LangItem::FnMut, - WellKnownTrait::FnOnce => LangItem::FnOnce, - WellKnownTrait::AsyncFn => LangItem::AsyncFn, - WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut, - WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce, - WellKnownTrait::Coroutine => LangItem::Coroutine, - WellKnownTrait::Sized => LangItem::Sized, - WellKnownTrait::Tuple => LangItem::Tuple, - WellKnownTrait::Unpin => LangItem::Unpin, - WellKnownTrait::Unsize => LangItem::Unsize, - WellKnownTrait::Pointee => LangItem::PointeeTrait, - WellKnownTrait::FnPtr => LangItem::FnPtrTrait, - WellKnownTrait::Future => LangItem::Future, - } -} - pub(crate) fn adt_datum_query( db: &dyn HirDatabase, krate: Crate, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 836cc96233eb8..8fa1aff0ef636 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -14,10 +14,9 @@ use hir_def::{ use crate::{ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, generics::generics, mapping::ToChalk, to_chalk_trait_id, - utils::ClosureSubst, + QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, + WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, }; pub trait TyExt { @@ -371,7 +370,7 @@ impl TyExt for Ty { value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(crate_id, None, goal).is_some() + !db.trait_solve(crate_id, None, goal).no_solution() } fn equals_ctor(&self, other: &Ty) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 6449a4dc7e8c6..22b152fe034a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -11,7 +11,7 @@ use test_utils::skip_slow_tests; use crate::{ Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase, - display::DisplayTarget, mir::pad16, test_db::TestDB, + display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB, }; use super::{ @@ -116,6 +116,7 @@ fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { } fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result { + let _tracing = setup_tracing(); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs new file mode 100644 index 0000000000000..da4aff54de378 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -0,0 +1,256 @@ +//! Constant evaluation details +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +#![allow(unused)] + +use base_db::Crate; +use hir_def::{ + EnumVariantId, GeneralConstId, + expr_store::{Body, HygieneId, path::Path}, + hir::{Expr, ExprId}, + resolver::{Resolver, ValueNs}, + type_ref::LiteralConstRef, +}; +use hir_expand::Lookup; +use rustc_type_ir::{ + UnevaluatedConst, + inherent::{IntoKind, SliceLike}, +}; +use stdx::never; +use triomphe::Arc; + +use crate::{ + ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, + consteval::ConstEvalError, + db::HirDatabase, + generics::Generics, + infer::InferenceContext, + next_solver::{ + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamConst, SolverDefId, Ty, ValueConst, + mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder}, + }, +}; + +use super::mir::{interpret_mir, lower_to_mir, pad16}; + +pub(crate) fn path_to_const<'a, 'g>( + db: &'a dyn HirDatabase, + resolver: &Resolver<'a>, + path: &Path, + args: impl FnOnce() -> &'g Generics, + expected_ty: Ty<'a>, +) -> Option> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let args = args(); + match args + .type_or_const_param(p.into()) + .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) + { + Some((idx, _param)) => { + Some(Const::new_param(interner, ParamConst { index: idx as u32 })) + } + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + None + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(interner, []); + Some(Const::new( + interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } +} + +pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> { + Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) +} + +pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { + unknown_const(ty).into() +} + +/// Interns a constant scalar with the given type +pub fn intern_const_ref<'a>( + db: &'a dyn HirDatabase, + value: &LiteralConstRef, + ty: Ty<'a>, + krate: Crate, +) -> Const<'a> { + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate)); + let kind = match value { + LiteralConstRef::Int(i) => { + // FIXME: We should handle failure of layout better. + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::UInt(i) => { + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(Box::new([*b as u8]), MemoryMap::default()), + )), + LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()), + )), + LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }; + Const::new(interner, kind) +} + +/// Interns a possibly-unknown target usize +pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { + intern_const_ref( + db, + &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), + krate, + ) +} + +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_usize(db, &ec) + } + ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_isize(db, &ec) + } + ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub(crate) fn const_eval_discriminant_variant( + db: &dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result { + let interner = DbInterner::new_with(db, None, None); + let def = variant_id.into(); + let body = db.body(def); + let loc = variant_id.lookup(db); + if matches!(body[body.body_expr], Expr::Missing) { + let prev_idx = loc.index.checked_sub(1); + let value = match prev_idx { + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + loc.parent.enum_variants(db).variants[prev_idx as usize].0, + )? + } + _ => 0, + }; + return Ok(value); + } + + let repr = db.enum_signature(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, mir_body, false, None)?.0?; + let c = c.to_nextsolver(interner); + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; + Ok(c) +} + +// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should +// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here +// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. +pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> { + let interner = DbInterner::new_with(ctx.db, None, None); + let infer = ctx.clone().resolve_all(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + r + } + if has_closure(ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr].clone().to_nextsolver(interner)); + } + if let Expr::Path(p) = &ctx.body[expr] { + let resolver = &ctx.resolver; + if let Some(c) = path_to_const( + ctx.db, + resolver, + p, + || ctx.generics(), + infer[expr].to_nextsolver(interner), + ) { + return c; + } + } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + { + return result.to_nextsolver(interner); + } + unknown_const(infer[expr].to_nextsolver(interner)) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index b3d46845c443a..18b8db21161fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -17,7 +17,7 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, + Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, drop::DropGlue, @@ -26,6 +26,7 @@ use crate::{ lower::{Diagnostics, GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + traits::NextTraitSolveResult, }; #[query_group::query_group] @@ -295,24 +296,162 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Ty; #[salsa::invoke(crate::traits::trait_solve_query)] + #[salsa::transparent] fn trait_solve( &self, krate: Crate, block: Option, goal: crate::Canonical>, - ) -> Option; - - #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] - fn program_clauses_for_chalk_env( - &self, - krate: Crate, - block: Option, - env: chalk_ir::Environment, - ) -> chalk_ir::ProgramClauses; + ) -> NextTraitSolveResult; #[salsa::invoke(crate::drop::has_drop_glue)] #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] fn has_drop_glue(&self, ty: Ty, env: Arc) -> DropGlue; + + // next trait solver + + #[salsa::invoke(crate::layout::layout_of_adt_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)] + fn layout_of_adt_ns<'db>( + &'db self, + def: AdtId, + args: crate::next_solver::GenericArgs<'db>, + trait_env: Arc, + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] + fn layout_of_ty_ns<'db>( + &'db self, + ty: crate::next_solver::Ty<'db>, + env: Arc, + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::lower_nextsolver::ty_query)] + #[salsa::transparent] + fn ty_ns<'db>( + &'db self, + def: TyDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] + fn type_for_type_alias_with_diagnostics_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] + fn impl_self_ty_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] + #[salsa::transparent] + fn impl_self_ty_ns<'db>( + &'db self, + def: ImplId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + // FIXME: Make this a non-interned query. + #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] + fn const_param_ty_with_diagnostics_ns<'db>( + &'db self, + def: ConstParamId, + ) -> (crate::next_solver::Ty<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] + #[salsa::transparent] + fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] + fn impl_trait_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> Option<( + crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, + Diagnostics, + )>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] + #[salsa::transparent] + fn impl_trait_ns<'db>( + &'db self, + def: ImplId, + ) -> Option>>; + + #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] + fn field_types_with_diagnostics_ns<'db>( + &'db self, + var: VariantId, + ) -> ( + Arc< + ArenaMap< + LocalFieldId, + crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, + >, + >, + Diagnostics, + ); + + #[salsa::invoke(crate::lower_nextsolver::field_types_query)] + #[salsa::transparent] + fn field_types_ns<'db>( + &'db self, + var: VariantId, + ) -> Arc< + ArenaMap>>, + >; + + #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] + fn callable_item_signature_ns<'db>( + &'db self, + def: CallableDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)] + fn return_type_impl_traits_ns<'db>( + &'db self, + def: FunctionId, + ) -> Option>>>; + + #[salsa::invoke(crate::lower_nextsolver::type_alias_impl_traits)] + fn type_alias_impl_traits_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> Option>>>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_for_param_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_predicates_for_param_cycle_result)] + fn generic_predicates_for_param_ns<'db>( + &'db self, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_query)] + fn generic_predicates_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke( + crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query + )] + fn generic_predicates_without_parent_with_diagnostics_ns<'db>( + &'db self, + def: GenericDefId, + ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] + #[salsa::transparent] + fn generic_predicates_without_parent_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 5577be890da34..a7e942d92442c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -187,7 +187,7 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc) -> bool { value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(env.krate, env.block, goal).is_some() + db.trait_solve(env.krate, env.block, goal).certain() } pub(crate) fn has_drop_glue_cycle_result( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 6294d683e6c02..2d21947ec60fc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -16,8 +16,8 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, - ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, + OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::{generics, trait_self_param_idx}, @@ -510,6 +510,8 @@ fn receiver_is_dispatchable( trait_id: to_chalk_trait_id(unsize_did), substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), }); + let unsized_predicate = + Binders::empty(Interner, unsized_predicate.cast::(Interner)); let trait_predicate = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: Substitution::from_iter( @@ -518,18 +520,32 @@ fn receiver_is_dispatchable( .chain(placeholder_subst.iter(Interner).skip(1).cloned()), ), }); + let trait_predicate = Binders::empty(Interner, trait_predicate.cast::(Interner)); let generic_predicates = &*db.generic_predicates(func.into()); - let clauses = std::iter::once(unsized_predicate) - .chain(std::iter::once(trait_predicate)) - .chain(generic_predicates.iter().map(|pred| { - pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 - })) - .map(|pred| { - pred.cast::>(Interner).into_from_env_clause(Interner) - }); - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( + generic_predicates.iter().map(|pred| { + pred.clone() + .substitute(Interner, &placeholder_subst) + .map(|g| g.cast::(Interner)) + }), + ); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + goals.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env: chalk_ir::Environment = chalk_ir::Environment { clauses }; let obligation = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(dispatch_from_dyn_did), @@ -541,9 +557,8 @@ fn receiver_is_dispatchable( let mut table = chalk_solve::infer::InferenceTable::::new(); let canonicalized = table.canonicalize(Interner, in_env); - let solution = db.trait_solve(krate, None, canonicalized.quantified); - matches!(solution, Some(Solution::Unique(_))) + db.trait_solve(krate, None, canonicalized.quantified).certain() } fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs index 5078e8cfaa8b9..4ffa455084971 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -56,18 +56,21 @@ fn check_dyn_compatibility<'a>( continue; }; let mut osvs = FxHashSet::default(); - _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { - osvs.insert(match osv { - DynCompatibilityViolation::SizedSelf => SizedSelf, - DynCompatibilityViolation::SelfReferential => SelfReferential, - DynCompatibilityViolation::Method(_, mvc) => Method(mvc), - DynCompatibilityViolation::AssocConst(_) => AssocConst, - DynCompatibilityViolation::GAT(_) => GAT, - DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { - HasNonCompatibleSuperTrait - } + let db = &db; + salsa::attach(db, || { + _ = dyn_compatibility_with_callback(db, trait_id, &mut |osv| { + osvs.insert(match osv { + DynCompatibilityViolation::SizedSelf => SizedSelf, + DynCompatibilityViolation::SelfReferential => SelfReferential, + DynCompatibilityViolation::Method(_, mvc) => Method(mvc), + DynCompatibilityViolation::AssocConst(_) => AssocConst, + DynCompatibilityViolation::GAT(_) => GAT, + DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { + HasNonCompatibleSuperTrait + } + }); + ControlFlow::Continue(()) }); - ControlFlow::Continue(()) }); assert_eq!(osvs, expected, "dyn-compatibility violations for `{name}` do not match;"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index f14872e68c3f5..e6470e5272d05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -165,6 +165,21 @@ impl Generics { (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) } + pub(crate) fn type_or_const_param( + &self, + param: TypeOrConstParamId, + ) -> Option<(usize, TypeOrConstParamData)> { + let idx = self.find_type_or_const_param(param)?; + self.iter().nth(idx).and_then(|p| { + let data = match p.1 { + GenericParamDataRef::TypeParamData(p) => p.clone().into(), + GenericParamDataRef::ConstParamData(p) => p.clone().into(), + _ => return None, + }; + Some((idx, data)) + }) + } + pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { self.find_type_or_const_param(param) } @@ -272,7 +287,7 @@ pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> O } } -fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { +pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 86345b23364d3..7a11ec660d04b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -88,52 +88,54 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { - let _p = tracing::info_span!("infer_query").entered(); - let resolver = def.resolver(db); - let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); - - match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), - DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), + crate::next_solver::with_new_cache(|| { + let _p = tracing::info_span!("infer_query").entered(); + let resolver = def.resolver(db); + let body = db.body(def); + let mut ctx = InferenceContext::new(db, def, &body, resolver); + + match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); + } + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), + DefWithBodyId::VariantId(v) => { + ctx.return_ty = TyBuilder::builtin( + match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, }, - }, - ); + ); + } } - } - ctx.infer_body(); + ctx.infer_body(); - ctx.infer_mut_body(); + ctx.infer_mut_body(); - ctx.infer_closures(); + ctx.infer_closures(); - Arc::new(ctx.resolve_all()) + Arc::new(ctx.resolve_all()) + }) } pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc { @@ -144,6 +146,7 @@ pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc, ty: Ty) -> Ty { // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only // works for the type case, so we check array unconditionally. Remove the array part @@ -1077,14 +1080,22 @@ impl<'db> InferenceContext<'db> { // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. // So, it suffices to check for params and return types. - if self - .return_ty - .data(Interner) - .flags - .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) - { - tait_candidates.insert(self.return_ty.clone()); - } + fold_tys( + self.return_ty.clone(), + |ty, _| { + match ty.kind(Interner) { + TyKind::OpaqueType(..) + | TyKind::Alias(AliasTy::Opaque(..)) + | TyKind::InferenceVar(..) => { + tait_candidates.insert(self.return_ty.clone()); + } + _ => {} + } + ty + }, + DebruijnIndex::INNERMOST, + ); + self.make_tait_coercion_table(tait_candidates.iter()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 8024c1a9a4e92..d8fc20e8741cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -3,12 +3,13 @@ use std::{cmp, convert::Infallible, mem, ops::ControlFlow}; use chalk_ir::{ - BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, + BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind, cast::Cast, fold::{FallibleTypeFolder, Shift, TypeFoldable}, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use either::Either; +use hir_def::Lookup; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, expr_store::path::Path, @@ -19,8 +20,8 @@ use hir_def::{ item_tree::FieldsShape, lang_item::LangItem, resolver::ValueNs, + type_ref::TypeRefId, }; -use hir_def::{Lookup, type_ref::TypeRefId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; @@ -30,8 +31,8 @@ use syntax::utils::is_raw_identifier; use crate::{ Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, - DynTyExt, FnAbi, FnPointer, FnSig, GenericArg, Interner, OpaqueTy, ProjectionTy, - ProjectionTyExt, Substitution, Ty, TyBuilder, TyExt, WhereClause, + DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, + Substitution, Ty, TyBuilder, TyExt, WhereClause, db::{HirDatabase, InternedClosure, InternedCoroutine}, error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, generics::Generics, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 761a2564aa799..631a91bac481b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -13,14 +13,15 @@ use stdx::always; use triomphe::Arc; use crate::{ - Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, + traits::NextTraitSolveResult, utils::ClosureSubst, }; @@ -715,13 +716,18 @@ impl InferenceTable<'_> { // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the // rest for later. Also, there's some logic about sized type variables. // Need to find out in what cases this is necessary - let solution = self - .db - .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner)) - .ok_or(TypeError)?; + let solution = self.db.trait_solve( + krate, + self.trait_env.block, + canonicalized.value.clone().cast(Interner), + ); match solution { - Solution::Unique(v) => { + // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique` + // was. Chalk's unique guidance at least guarantees that the real solution + // is some "subset" of the solutions matching the guidance, but the + // substs for `Certainty::No` don't have that same guarantee (I think). + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { @@ -731,13 +737,12 @@ impl InferenceTable<'_> { }, ); } - Solution::Ambig(Guidance::Definite(subst)) => { - // FIXME need to record an obligation here - canonicalized.apply_solution(self, subst) + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => { + return Err(TypeError); } - // FIXME actually we maybe should also accept unknown guidance here - _ => return Err(TypeError), - }; + } + let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; let adjustments = match reborrow { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 16fc2bfc0631f..261c02386822d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -278,6 +278,7 @@ impl InferenceContext<'_> { } } + #[tracing::instrument(level = "debug", skip(self, is_read), ret)] fn infer_expr_inner( &mut self, tgt_expr: ExprId, @@ -286,7 +287,9 @@ impl InferenceContext<'_> { ) -> Ty { self.db.unwind_if_revision_cancelled(); - let ty = match &self.body[tgt_expr] { + let expr = &self.body[tgt_expr]; + tracing::trace!(?expr); + let ty = match expr { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { let expected = &expected.adjust_for_branches(&mut self.table); @@ -1949,7 +1952,11 @@ impl InferenceContext<'_> { sig.ret().clone(), sig.is_varargs, ), - None => ((self.err_ty(), Vec::new()), self.err_ty(), true), + None => { + let formal_receiver_ty = self.table.new_type_var(); + let ret_ty = self.table.new_type_var(); + ((formal_receiver_ty, Vec::new()), ret_ty, true) + } }; self.unify(&formal_receiver_ty, &receiver_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c07755535f2a6..a709aebfa9c1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -19,11 +19,13 @@ use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, - Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, - to_chalk_trait_id, traits::FnTrait, + GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, + OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, + TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, + db::HirDatabase, + fold_generic_args, fold_tys_and_consts, to_chalk_trait_id, + traits::{FnTrait, NextTraitSolveResult}, }; impl InferenceContext<'_> { @@ -116,9 +118,12 @@ impl> Canonicalized { // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); + tracing::debug!("unifying {:?} {:?}", var, ty); ctx.unify(var.assert_ty_ref(Interner), &ty); } else { - let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner)); + let v = new_vars.apply(v.clone(), Interner); + tracing::debug!("try_unifying {:?} {:?}", var, v); + let _ = ctx.try_unify(var, &v); } } } @@ -326,6 +331,7 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. + #[tracing::instrument(skip(self), ret)] pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where T: HasInterner + TypeFoldable, @@ -333,12 +339,88 @@ impl<'a> InferenceTable<'a> { fold_tys_and_consts( ty, |e, _| match e { - Either::Left(ty) => Either::Left(match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - self.normalize_projection_ty(proj_ty.clone()) - } - _ => ty, - }), + Either::Left(ty) => { + let ty = self.resolve_ty_shallow(&ty); + tracing::debug!(?ty); + Either::Left(match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + let ty = self.normalize_projection_ty(proj_ty.clone()); + self.resolve_ty_shallow(&ty) + } + TyKind::AssociatedType(id, subst) => { + if ty.data(Interner).flags.intersects( + chalk_ir::TypeFlags::HAS_TY_INFER + | chalk_ir::TypeFlags::HAS_CT_INFER, + ) { + return Either::Left(ty); + } + let var = self.new_type_var(); + let proj_ty = chalk_ir::ProjectionTy { + associated_ty_id: *id, + substitution: subst.clone(), + }; + let normalize = chalk_ir::Normalize { + alias: AliasTy::Projection(proj_ty), + ty: var.clone(), + }; + let goal = chalk_ir::Goal::new( + Interner, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize( + normalize, + )), + ); + let in_env = InEnvironment::new(&self.trait_env.env, goal); + + let canonicalized = { + let result = + self.var_unification_table.canonicalize(Interner, in_env); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + let solution = self.db.trait_solve( + self.trait_env.krate, + self.trait_env.block, + canonicalized.value.clone(), + ); + if let NextTraitSolveResult::Certain(canonical_subst) = solution { + // This is not great :) But let's just assert this for now and come back to it later. + if canonical_subst.value.subst.len(Interner) != 1 { + ty + } else { + let normalized = canonical_subst.value.subst.as_slice(Interner) + [0] + .assert_ty_ref(Interner); + match normalized.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + if id == &proj_ty.associated_ty_id + && subst == &proj_ty.substitution + { + ty + } else { + normalized.clone() + } + } + TyKind::AssociatedType(new_id, new_subst) => { + if new_id == id && new_subst == subst { + ty + } else { + normalized.clone() + } + } + _ => normalized.clone(), + } + } + } else { + ty + } + } + _ => ty, + }) + } Either::Right(c) => Either::Right(match &c.data(Interner).value { chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { crate::ConstScalar::UnevaluatedConst(c_id, subst) => { @@ -588,7 +670,6 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - #[tracing::instrument(skip_all)] pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, @@ -606,7 +687,7 @@ impl<'a> InferenceTable<'a> { }; result.goals.iter().all(|goal| { let canonicalized = self.canonicalize_with_free_vars(goal.clone()); - self.try_resolve_obligation(&canonicalized).is_some() + self.try_resolve_obligation(&canonicalized).certain() }) } @@ -633,6 +714,9 @@ impl<'a> InferenceTable<'a> { /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { + return ty.clone(); + } self.resolve_obligations_as_possible(); self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) } @@ -662,7 +746,8 @@ impl<'a> InferenceTable<'a> { /// Checks an obligation without registering it. Useful mostly to check /// whether a trait *might* be implemented before deciding to 'lock in' the /// choice (during e.g. method resolution or deref). - pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option { + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); @@ -674,10 +759,45 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(in_env) } + #[tracing::instrument(level = "debug", skip(self))] fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let canonicalized = self.canonicalize_with_free_vars(goal); + match goal.goal.data(Interner) { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), + )) => { + if ty.inference_var(Interner).is_some() { + match alias { + chalk_ir::AliasTy::Opaque(opaque) => { + if self.unify( + &chalk_ir::TyKind::OpaqueType( + opaque.opaque_ty_id, + opaque.substitution.clone(), + ) + .intern(Interner), + ty, + ) { + return; + } + } + _ => {} + } + } + } + _ => {} + } + let canonicalized = { + let result = self.var_unification_table.canonicalize(Interner, goal); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + tracing::debug!(?canonicalized); let solution = self.try_resolve_obligation(&canonicalized); - if matches!(solution, Some(Solution::Ambig(_))) { + tracing::debug!(?solution); + if solution.uncertain() { self.pending_obligations.push(canonicalized); } } @@ -694,7 +814,9 @@ impl<'a> InferenceTable<'a> { mem::swap(&mut self.pending_obligations, &mut obligations); for canonicalized in obligations.drain(..) { + tracing::debug!(obligation = ?canonicalized); if !self.check_changed(&canonicalized) { + tracing::debug!("not changed"); self.pending_obligations.push(canonicalized); continue; } @@ -799,37 +921,36 @@ impl<'a> InferenceTable<'a> { }) } + #[tracing::instrument(level = "debug", skip(self))] fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized>, - ) -> Option> { + ) -> NextTraitSolveResult { let solution = self.db.trait_solve( self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), ); + tracing::debug!(?solution, ?canonicalized); match &solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { - binders: canonical_subst.binders.clone(), - // FIXME: handle constraints - value: canonical_subst.value.subst.clone(), + binders: v.binders.clone(), + // FIXME handle constraints + value: v.value.subst.clone(), }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.clone()); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(v) => { + canonicalized.apply_solution(self, v.clone()); } + NextTraitSolveResult::NoSolution => {} } + solution } @@ -896,7 +1017,10 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() + if !self + .db + .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) + .no_solution() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); @@ -909,10 +1033,10 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self + if !self .db .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .is_some() + .no_solution() { return Some((fn_x, arg_tys, return_ty)); } @@ -1032,7 +1156,7 @@ impl<'a> InferenceTable<'a> { substitution: Substitution::from1(Interner, ty), }); let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); - matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + self.try_obligation(goal).certain() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs index fecb3f4242a92..57ef5523b4332 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs @@ -2,11 +2,10 @@ //! representation of the various objects Chalk deals with (types, goals etc.). use crate::{ - AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, - Constraint, Constraints, FnAbi, FnDefId, GenericArg, GenericArgData, Goal, GoalData, Goals, - InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseData, - ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, Ty, - TyData, TyKind, VariableKind, VariableKinds, chalk_db, tls, + AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, FnAbi, + FnDefId, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, Lifetime, LifetimeData, + OpaqueTy, OpaqueTyId, ProgramClause, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, Ty, TyKind, VariableKind, chalk_db, tls, }; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; use hir_def::TypeAliasId; @@ -15,11 +14,19 @@ use smallvec::SmallVec; use std::fmt; use triomphe::Arc; +type TyData = chalk_ir::TyData; +type VariableKinds = chalk_ir::VariableKinds; +type Goals = chalk_ir::Goals; +type ProgramClauseData = chalk_ir::ProgramClauseData; +type Constraint = chalk_ir::Constraint; +type Constraints = chalk_ir::Constraints; +type ProgramClauses = chalk_ir::ProgramClauses; + #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Interner; -#[derive(PartialEq, Eq, Hash)] -pub struct InternedWrapper(T); +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapper(pub(crate) T); impl fmt::Debug for InternedWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -27,6 +34,9 @@ impl fmt::Debug for InternedWrapper { } } +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug(pub(crate) T); + impl std::ops::Deref for InternedWrapper { type Target = T; @@ -124,6 +134,7 @@ impl chalk_ir::interner::Interner for Interner { fmt: &mut fmt::Formatter<'_>, ) -> Option { tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) + .or_else(|| Some(fmt.write_str("ProjectionTy"))) } fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 107da6a5af6d6..819bd583deaed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -2,33 +2,43 @@ use std::fmt; -use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ - LocalFieldId, StructId, - layout::{ - Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive, - ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange, - }, + AdtId, LocalFieldId, StructId, + layout::{LayoutCalculatorError, LayoutData}, }; use la_arena::{Idx, RawIdx}; -use rustc_abi::AddressSpace; -use rustc_index::IndexVec; +use rustc_abi::{ + AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, + TargetDataLayout, WrappingRange, +}; +use rustc_index::IndexVec; +use rustc_type_ir::{ + FloatTy, IntTy, UintTy, + inherent::{IntoKind, SliceLike}, +}; use triomphe::Arc; use crate::{ - Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, - consteval::try_const_usize, - db::{HirDatabase, InternedClosure}, - infer::normalize, - utils::ClosureSubst, + TraitEnvironment, + consteval_nextsolver::try_const_usize, + db::HirDatabase, + next_solver::{ + DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, + infer::{DbInternerInferExt, traits::ObligationCause}, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + project::solve_normalize::deeply_normalize, + }, }; -pub(crate) use self::adt::layout_of_adt_cycle_result; -pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; +pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result}; +pub use self::{ + adt::{layout_of_adt_ns_query, layout_of_adt_query}, + target::target_data_layout_query, +}; -mod adt; -mod target; +pub(crate) mod adt; +pub(crate) mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -119,11 +129,12 @@ impl<'a> LayoutCx<'a> { } } -fn layout_of_simd_ty( - db: &dyn HirDatabase, +// FIXME: move this to the `rustc_abi`. +fn layout_of_simd_ty<'db>( + db: &'db dyn HirDatabase, id: StructId, repr_packed: bool, - subst: &Substitution, + args: &GenericArgs<'db>, env: Arc, dl: &TargetDataLayout, ) -> Result, LayoutError> { @@ -132,18 +143,18 @@ fn layout_of_simd_ty( // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). - let fields = db.field_types(id.into()); + let fields = db.field_types_ns(id.into()); let mut fields = fields.iter(); let Some(TyKind::Array(e_ty, e_len)) = fields .next() .filter(|_| fields.next().is_none()) - .map(|f| f.1.clone().substitute(Interner, subst).kind(Interner).clone()) + .map(|f| (*f.1).instantiate(DbInterner::new_with(db, None, None), args).kind()) else { return Err(LayoutError::InvalidSimdType); }; let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; - let e_ly = db.layout_of_ty(e_ty, env)?; + let e_ly = db.layout_of_ty_ns(e_ty, env)?; let cx = LayoutCx::new(dl); Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) @@ -151,108 +162,122 @@ fn layout_of_simd_ty( pub fn layout_of_ty_query( db: &dyn HirDatabase, - ty: Ty, + ty: crate::Ty, trait_env: Arc, ) -> Result, LayoutError> { let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); + db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_ty_ns_query<'db>( + db: &'db dyn HirDatabase, + ty: Ty<'db>, + trait_env: Arc, +) -> Result, LayoutError> { + let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; let dl = &*target; let cx = LayoutCx::new(dl); - let ty = normalize(db, trait_env.clone(), ty); - let kind = ty.kind(Interner); - let result = match kind { - TyKind::Adt(AdtId(def), subst) => { - if let hir_def::AdtId::StructId(s) = def { - let data = db.struct_signature(*s); - let repr = data.repr.unwrap_or_default(); - if repr.simd() { - return layout_of_simd_ty(db, *s, repr.packed(), subst, trait_env, &target); + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = ObligationCause::dummy(); + let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); + let result = match ty.kind() { + TyKind::Adt(def, args) => { + match def.inner().id { + hir_def::AdtId::StructId(s) => { + let data = db.struct_signature(s); + let repr = data.repr.unwrap_or_default(); + if repr.simd() { + return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target); + } } - }; - return db.layout_of_adt(*def, subst.clone(), trait_env); + _ => {} + } + return db.layout_of_adt_ns(def.inner().id, args, trait_env); } - TyKind::Scalar(s) => match s { - chalk_ir::Scalar::Bool => Layout::scalar( - dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I8, false), - valid_range: WrappingRange { start: 0, end: 1 }, - }, - ), - chalk_ir::Scalar::Char => Layout::scalar( - dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I32, false), - valid_range: WrappingRange { start: 0, end: 0x10FFFF }, - }, - ), - chalk_ir::Scalar::Int(i) => Layout::scalar( + TyKind::Bool => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I8, false), + valid_range: WrappingRange { start: 0, end: 1 }, + }, + ), + TyKind::Char => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I32, false), + valid_range: WrappingRange { start: 0, end: 0x10FFFF }, + }, + ), + TyKind::Int(i) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Int( - match i { - IntTy::Isize => dl.ptr_sized_integer(), - IntTy::I8 => Integer::I8, - IntTy::I16 => Integer::I16, - IntTy::I32 => Integer::I32, - IntTy::I64 => Integer::I64, - IntTy::I128 => Integer::I128, - }, - true, - ), + Primitive::Int( + match i { + IntTy::Isize => dl.ptr_sized_integer(), + IntTy::I8 => Integer::I8, + IntTy::I16 => Integer::I16, + IntTy::I32 => Integer::I32, + IntTy::I64 => Integer::I64, + IntTy::I128 => Integer::I128, + }, + true, ), ), - chalk_ir::Scalar::Uint(i) => Layout::scalar( + ), + TyKind::Uint(i) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Int( - match i { - UintTy::Usize => dl.ptr_sized_integer(), - UintTy::U8 => Integer::I8, - UintTy::U16 => Integer::I16, - UintTy::U32 => Integer::I32, - UintTy::U64 => Integer::I64, - UintTy::U128 => Integer::I128, - }, - false, - ), + Primitive::Int( + match i { + UintTy::Usize => dl.ptr_sized_integer(), + UintTy::U8 => Integer::I8, + UintTy::U16 => Integer::I16, + UintTy::U32 => Integer::I32, + UintTy::U64 => Integer::I64, + UintTy::U128 => Integer::I128, + }, + false, ), ), - chalk_ir::Scalar::Float(f) => Layout::scalar( + ), + TyKind::Float(f) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Float(match f { - FloatTy::F16 => Float::F16, - FloatTy::F32 => Float::F32, - FloatTy::F64 => Float::F64, - FloatTy::F128 => Float::F128, - }), - ), + Primitive::Float(match f { + FloatTy::F16 => Float::F16, + FloatTy::F32 => Float::F32, + FloatTy::F64 => Float::F64, + FloatTy::F128 => Float::F128, + }), ), - }, - TyKind::Tuple(len, tys) => { - let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + ), + TyKind::Tuple(tys) => { + let kind = + if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; let fields = tys - .iter(Interner) - .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) + .iter() + .map(|k| db.layout_of_ty_ns(k, trait_env.clone())) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.calc.univariant(&fields, &ReprOptions::default(), kind)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty(element.clone(), trait_env)?; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { - let element = db.layout_of_ty(element.clone(), trait_env)?; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, None)? } TyKind::Str => { @@ -260,18 +285,21 @@ pub fn layout_of_ty_query( cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)? } // Potentially-wide pointers. - TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { + TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); - if matches!(ty.kind(Interner), TyKind::Ref(..)) { + if matches!(ty.kind(), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } + // FIXME(next-solver) // let pointee = tcx.normalize_erasing_regions(param_env, pointee); // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { - // return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr))); + // return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); // } - let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone()); + let unsized_part = struct_tail_erasing_lifetimes(db, pointee); + // FIXME(next-solver) + /* if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) { unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { associated_ty_id: *id, @@ -280,11 +308,12 @@ pub fn layout_of_ty_query( .intern(Interner); } unsized_part = normalize(db, trait_env, unsized_part); - let metadata = match unsized_part.kind(Interner) { + */ + let metadata = match unsized_part.kind() { TyKind::Slice(_) | TyKind::Str => { scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } - TyKind::Dyn(..) => { + TyKind::Dynamic(..) => { let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); vtable.valid_range_mut().start = 1; vtable @@ -299,97 +328,110 @@ pub fn layout_of_ty_query( LayoutData::scalar_pair(dl, data_ptr, metadata) } TyKind::Never => LayoutData::never_type(dl), - TyKind::FnDef(..) | TyKind::Dyn(_) | TyKind::Foreign(_) => { - let sized = matches!(kind, TyKind::FnDef(..)); - LayoutData::unit(dl, sized) - } - TyKind::Function(_) => { + TyKind::FnDef(..) => LayoutData::unit(dl, true), + TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false), + TyKind::FnPtr(..) => { let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::OpaqueType(opaque_ty_id, _) => { - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - return Err(LayoutError::NotImplemented); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - return Err(LayoutError::NotImplemented); + TyKind::Alias(_, ty) => match ty.def_id { + SolverDefId::TypeAliasId(_) => { + return Err(LayoutError::HasPlaceholder); + } + SolverDefId::InternedOpaqueTyId(opaque) => { + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = db.infer(func.into()); + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); + } + crate::ImplTraitId::TypeAliasImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + return Err(LayoutError::NotImplemented); + } } } - } - TyKind::Closure(c, subst) => { - let InternedClosure(def, _) = db.lookup_intern_closure((*c).into()); - let infer = db.infer(def); - let (captures, _) = infer.closure_info(c); + _ => unreachable!(), + }, + TyKind::Closure(c, args) => { + let id = match c { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let def = db.lookup_intern_closure(id); + let infer = db.infer(def.0); + let (captures, _) = infer.closure_info(&id.into()); let fields = captures .iter() .map(|it| { - db.layout_of_ty( - it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), - trait_env.clone(), - ) + let ty = + convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) + .instantiate(interner, args); + db.layout_of_ty_ns(ty, trait_env.clone()) }) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)? } - TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => { + + TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::CoroutineClosure(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Error => return Err(LayoutError::HasErrorType), - TyKind::AssociatedType(id, subst) => { - // Try again with `TyKind::Alias` to normalize the associated type. - // Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used - // in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle, - // which we will recover from with an error. - let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone(), - })) - .intern(Interner); - return db.layout_of_ty(ty, trait_env); + + TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + return Err(LayoutError::NotImplemented); + } + + TyKind::Error(_) => return Err(LayoutError::HasErrorType), + TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => { + return Err(LayoutError::HasPlaceholder); } - TyKind::Alias(_) - | TyKind::Placeholder(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), }; Ok(Arc::new(result)) } pub(crate) fn layout_of_ty_cycle_result( _: &dyn HirDatabase, - _: Ty, + _: crate::Ty, + _: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +pub(crate) fn layout_of_ty_ns_cycle_result<'db>( + _: &dyn HirDatabase, + _: Ty<'db>, _: Arc, ) -> Result, LayoutError> { Err(LayoutError::RecursiveTypeWithoutIndirection) } -fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { - match pointee.kind(Interner) { - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), ref subst) => { - let data = i.fields(db); +fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { + match pointee.kind() { + TyKind::Adt(def, args) => { + let struct_id = match def.inner().id { + AdtId::StructId(id) => id, + _ => return pointee, + }; + let data = struct_id.fields(db); let mut it = data.fields().iter().rev(); match it.next() { Some((f, _)) => { - let last_field_ty = field_ty(db, i.into(), f, subst); + let last_field_ty = field_ty(db, struct_id.into(), f, &args); struct_tail_erasing_lifetimes(db, last_field_ty) } None => pointee, } } - TyKind::Tuple(_, subst) => { - if let Some(last_field_ty) = - subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) - { - struct_tail_erasing_lifetimes(db, last_field_ty.clone()) + TyKind::Tuple(tys) => { + if let Some(last_field_ty) = tys.iter().last() { + struct_tail_erasing_lifetimes(db, last_field_ty) } else { pointee } @@ -398,13 +440,13 @@ fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { } } -fn field_ty( - db: &dyn HirDatabase, +fn field_ty<'a>( + db: &'a dyn HirDatabase, def: hir_def::VariantId, fd: LocalFieldId, - subst: &Substitution, -) -> Ty { - db.field_types(def)[fd].clone().substitute(Interner, subst) + args: &GenericArgs<'a>, +) -> Ty<'a> { + db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args) } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 3f310c26ec14a..2fa01b6b41abc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -4,10 +4,10 @@ use std::{cmp, ops::Bound}; use hir_def::{ AdtId, VariantId, - layout::{Integer, ReprOptions, TargetDataLayout}, signatures::{StructFlags, VariantFields}, }; use intern::sym; +use rustc_abi::{Integer, ReprOptions, TargetDataLayout}; use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -15,16 +15,25 @@ use triomphe::Arc; use crate::{ Substitution, TraitEnvironment, db::HirDatabase, - layout::{Layout, LayoutError, field_ty}, + layout::{Layout, LayoutCx, LayoutError, field_ty}, + next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver}, }; -use super::LayoutCx; - pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, trait_env: Arc, +) -> Result, LayoutError> { + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_adt_ns_query<'db>( + db: &'db dyn HirDatabase, + def: AdtId, + args: GenericArgs<'db>, + trait_env: Arc, ) -> Result, LayoutError> { let krate = trait_env.krate; let Ok(target) = db.target_data_layout(krate) else { @@ -35,7 +44,7 @@ pub fn layout_of_adt_query( let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone())) + .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone())) .collect::, _>>() }; let (variants, repr, is_special_no_niche) = match def { @@ -96,6 +105,24 @@ pub fn layout_of_adt_query( Ok(Arc::new(result)) } +pub(crate) fn layout_of_adt_cycle_result( + _: &dyn HirDatabase, + _: AdtId, + _: Substitution, + _: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +pub(crate) fn layout_of_adt_ns_cycle_result<'db>( + _: &'db dyn HirDatabase, + _def: AdtId, + _args: GenericArgs<'db>, + _trait_env: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) { let attrs = db.attrs(def.into()); let get = |name| { @@ -120,15 +147,6 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end)) } -pub(crate) fn layout_of_adt_cycle_result( - _: &dyn HirDatabase, - _: AdtId, - _: Substitution, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - /// Finds the appropriate Integer type and signedness for the given /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b3bc226ec93c7..93f2e123dca58 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -11,6 +11,7 @@ use crate::{ Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, + setup_tracing, test_db::TestDB, }; @@ -29,6 +30,7 @@ fn eval_goal( #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}", @@ -97,6 +99,7 @@ fn eval_expr( #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index e787fd9b1e584..2b0dc931eaf4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -21,6 +21,24 @@ extern crate rustc_pattern_analysis; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_ast_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_ast_ir as rustc_ast_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_next_trait_solver; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; + mod builder; mod chalk_db; mod chalk_ext; @@ -29,13 +47,16 @@ mod infer; mod inhabitedness; mod interner; mod lower; +mod lower_nextsolver; mod mapping; +pub mod next_solver; mod target_feature; mod tls; mod utils; pub mod autoderef; pub mod consteval; +pub mod consteval_nextsolver; pub mod db; pub mod diagnostics; pub mod display; @@ -57,7 +78,7 @@ mod variance; use std::hash::Hash; use chalk_ir::{ - NoSolution, + NoSolution, VariableKinds, fold::{Shift, TypeFoldable}, interner::HasInterner, }; @@ -121,9 +142,9 @@ pub type ClosureId = chalk_ir::ClosureId; pub type OpaqueTyId = chalk_ir::OpaqueTyId; pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; -pub type VariableKind = chalk_ir::VariableKind; -pub type VariableKinds = chalk_ir::VariableKinds; pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds; + +pub(crate) type VariableKind = chalk_ir::VariableKind; /// Represents generic parameters and an item bound by them. When the item has parent, the binders /// also contain the generic parameters for its parent. See chalk's documentation for details. /// @@ -145,52 +166,45 @@ pub type GenericArgData = chalk_ir::GenericArgData; pub type Ty = chalk_ir::Ty; pub type TyKind = chalk_ir::TyKind; pub type TypeFlags = chalk_ir::TypeFlags; -pub type DynTy = chalk_ir::DynTy; +pub(crate) type DynTy = chalk_ir::DynTy; pub type FnPointer = chalk_ir::FnPointer; -// pub type FnSubst = chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor -pub use chalk_ir::FnSubst; -pub type ProjectionTy = chalk_ir::ProjectionTy; +pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor + pub type AliasTy = chalk_ir::AliasTy; -pub type OpaqueTy = chalk_ir::OpaqueTy; -pub type InferenceVar = chalk_ir::InferenceVar; -pub type Lifetime = chalk_ir::Lifetime; -pub type LifetimeData = chalk_ir::LifetimeData; -pub type LifetimeOutlives = chalk_ir::LifetimeOutlives; +pub type ProjectionTy = chalk_ir::ProjectionTy; +pub(crate) type OpaqueTy = chalk_ir::OpaqueTy; +pub(crate) type InferenceVar = chalk_ir::InferenceVar; + +pub(crate) type Lifetime = chalk_ir::Lifetime; +pub(crate) type LifetimeData = chalk_ir::LifetimeData; +pub(crate) type LifetimeOutlives = chalk_ir::LifetimeOutlives; -pub type Const = chalk_ir::Const; -pub type ConstData = chalk_ir::ConstData; pub type ConstValue = chalk_ir::ConstValue; -pub type ConcreteConst = chalk_ir::ConcreteConst; -pub type ChalkTraitId = chalk_ir::TraitId; +pub type Const = chalk_ir::Const; +pub(crate) type ConstData = chalk_ir::ConstData; +pub(crate) type ConcreteConst = chalk_ir::ConcreteConst; + pub type TraitRef = chalk_ir::TraitRef; pub type QuantifiedWhereClause = Binders; -pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses; pub type Canonical = chalk_ir::Canonical; -pub type FnSig = chalk_ir::FnSig; +pub(crate) type ChalkTraitId = chalk_ir::TraitId; +pub(crate) type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses; + +pub(crate) type FnSig = chalk_ir::FnSig; pub type InEnvironment = chalk_ir::InEnvironment; -pub type Environment = chalk_ir::Environment; -pub type DomainGoal = chalk_ir::DomainGoal; -pub type Goal = chalk_ir::Goal; pub type AliasEq = chalk_ir::AliasEq; -pub type Solution = chalk_solve::Solution; -pub type Constraint = chalk_ir::Constraint; -pub type Constraints = chalk_ir::Constraints; -pub type ConstrainedSubst = chalk_ir::ConstrainedSubst; -pub type Guidance = chalk_solve::Guidance; pub type WhereClause = chalk_ir::WhereClause; -pub type CanonicalVarKind = chalk_ir::CanonicalVarKind; -pub type GoalData = chalk_ir::GoalData; -pub type Goals = chalk_ir::Goals; -pub type ProgramClauseData = chalk_ir::ProgramClauseData; -pub type ProgramClause = chalk_ir::ProgramClause; -pub type ProgramClauses = chalk_ir::ProgramClauses; -pub type TyData = chalk_ir::TyData; -pub type Variances = chalk_ir::Variances; +pub(crate) type DomainGoal = chalk_ir::DomainGoal; +pub(crate) type Goal = chalk_ir::Goal; + +pub(crate) type CanonicalVarKind = chalk_ir::CanonicalVarKind; +pub(crate) type GoalData = chalk_ir::GoalData; +pub(crate) type ProgramClause = chalk_ir::ProgramClause; /// A constant can have reference to other things. Memory map job is holding /// the necessary bits of memory of the const eval session to keep the constant @@ -311,30 +325,11 @@ where Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE)) } -pub(crate) fn make_type_and_const_binders>( - which_is_const: impl Iterator>, - value: T, -) -> Binders { - Binders::new( - VariableKinds::from_iter( - Interner, - which_is_const.map(|x| { - if let Some(ty) = x { - chalk_ir::VariableKind::Const(ty) - } else { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - }), - ), - value, - ) -} - pub(crate) fn make_single_type_binders>( value: T, ) -> Binders { Binders::new( - VariableKinds::from_iter( + chalk_ir::VariableKinds::from_iter( Interner, std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)), ), @@ -353,7 +348,7 @@ pub(crate) fn make_binders>( pub(crate) fn variable_kinds_from_iter( db: &dyn HirDatabase, iter: impl Iterator, -) -> VariableKinds { +) -> VariableKinds { VariableKinds::from_iter( Interner, iter.map(|x| match x { @@ -918,7 +913,7 @@ pub fn callable_sig_from_fn_trait( let obligation = InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { table.register_obligation(obligation.goal); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { @@ -929,7 +924,7 @@ pub fn callable_sig_from_fn_trait( environment: trait_env.clone(), }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { let ret_ty = table.resolve_completely(return_ty); let args_ty = table.resolve_completely(args_ty); let params = args_ty @@ -985,7 +980,7 @@ impl TypeVisitor for PlaceholderCollector<'_> { outer_binder: DebruijnIndex, ) -> std::ops::ControlFlow { let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER; - let TyData { kind, flags } = ty.data(Interner); + let chalk_ir::TyData { kind, flags } = ty.data(Interner); if let TyKind::Placeholder(idx) = kind { self.collect(*idx); @@ -1045,3 +1040,25 @@ pub(crate) enum DeclOrigin { pub(crate) struct DeclContext { pub(crate) origin: DeclOrigin, } + +pub fn setup_tracing() -> Option { + use std::env; + use std::sync::LazyLock; + use tracing_subscriber::{Registry, layer::SubscriberExt}; + use tracing_tree::HierarchicalLayer; + + static ENABLE: LazyLock = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); + if !*ENABLE { + return None; + } + + let filter: tracing_subscriber::filter::Targets = + env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); + let layer = HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(false) + .with_indent_amount(2) + .with_writer(std::io::stderr); + let subscriber = Registry::default().with(filter).with(layer); + Some(tracing::subscriber::set_default(subscriber)) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index afee9606bd5f8..7b6b3c3f8181a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -46,9 +46,9 @@ use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, - FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, - LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses, + AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DomainGoal, DynTy, FnAbi, + FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, @@ -81,7 +81,7 @@ impl ImplTraitLoweringState { } } -pub(crate) struct PathDiagnosticCallbackData(TypeRefId); +pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); #[derive(Debug, Clone)] pub enum LifetimeElisionKind { @@ -889,7 +889,7 @@ fn named_associated_type_shorthand_candidates( pub(crate) type Diagnostics = Option>; -fn create_diagnostics(diagnostics: Vec) -> Diagnostics { +pub(crate) fn create_diagnostics(diagnostics: Vec) -> Diagnostics { (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) } @@ -1105,8 +1105,9 @@ pub(crate) fn trait_environment_query( traits_in_scope .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); } - let program_clause: chalk_ir::ProgramClause = pred.cast(Interner); - clauses.push(program_clause.into_from_env_clause(Interner)); + let program_clause: Binders = + pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner)); + clauses.push(program_clause); } } } @@ -1119,7 +1120,10 @@ pub(crate) fn trait_environment_query( let substs = TyBuilder::placeholder_subst(db, trait_id); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; let pred = WhereClause::Implemented(trait_ref); - clauses.push(pred.cast::(Interner).into_from_env_clause(Interner)); + clauses.push(Binders::empty( + Interner, + pred.cast::(Interner).into_from_env_goal(Interner), + )); } let subst = generics.placeholder_subst(db); @@ -1128,15 +1132,30 @@ pub(crate) fn trait_environment_query( if let Some(implicitly_sized_clauses) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) { - clauses.extend( - implicitly_sized_clauses.map(|pred| { - pred.cast::(Interner).into_from_env_clause(Interner) - }), - ); + clauses.extend(implicitly_sized_clauses.map(|pred| { + Binders::empty( + Interner, + pred.into_from_env_goal(Interner).cast::(Interner), + ) + })); }; } - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + clauses.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env = chalk_ir::Environment { clauses }; TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) } @@ -1177,7 +1196,7 @@ pub(crate) fn generic_predicates_without_parent_with_diagnostics_query( } /// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent +/// with a given filter fn generic_predicates_filtered_by( db: &dyn HirDatabase, def: GenericDefId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs new file mode 100644 index 0000000000000..24bda43ca634f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -0,0 +1,1609 @@ +//! Methods for lowering the HIR to types. There are two main cases here: +//! +//! - Lowering a type reference like `&usize` or `Option` to a +//! type: The entry point for this is `TyLoweringContext::lower_ty`. +//! - Building the type for an item: This happens through the `ty` query. +//! +//! This usually involves resolving names, collecting generic arguments etc. +#![allow(unused)] +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +pub(crate) mod path; + +use std::{ + cell::OnceCell, + iter, mem, + ops::{self, Not as _}, +}; + +use base_db::Crate; +use either::Either; +use hir_def::{ + AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId, + TypeOrConstParamId, VariantId, + expr_store::{ + ExpressionStore, + path::{GenericArg, Path}, + }, + hir::generics::{TypeOrConstParamData, WherePredicate}, + lang_item::LangItem, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, + type_ref::{ + ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, + TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, + }, +}; +use hir_expand::name::Name; +use intern::sym; +use la_arena::{Arena, ArenaMap, Idx}; +use path::{PathDiagnosticCallback, PathLoweringContext, builtin}; +use rustc_ast_ir::Mutability; +use rustc_hash::FxHashSet; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::{ + AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, OutlivesPredicate, + TyKind::{self}, + TypeVisitableExt, + inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, +}; +use salsa::plumbing::AsId; +use stdx::never; +use triomphe::Arc; + +use crate::{ + FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, + consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics, trait_self_param_idx}, + lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, + next_solver::{ + AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, + BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, + ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, + TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + }, +}; + +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct ImplTraits<'db> { + pub(crate) impl_traits: Arena>, +} + +#[derive(PartialEq, Eq, Debug, Hash)] +pub(crate) struct ImplTrait<'db> { + pub(crate) predicates: Vec>, +} + +pub(crate) type ImplTraitIdx<'db> = Idx>; + +#[derive(Debug, Default)] +struct ImplTraitLoweringState<'db> { + /// When turning `impl Trait` into opaque types, we have to collect the + /// bounds at the same time to get the IDs correct (without becoming too + /// complicated). + mode: ImplTraitLoweringMode, + // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. + opaque_type_data: Arena>, + param_and_variable_counter: u16, +} +impl<'db> ImplTraitLoweringState<'db> { + fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { + Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum LifetimeElisionKind<'db> { + /// Create a new anonymous lifetime parameter and reference it. + /// + /// If `report_in_path`, report an error when encountering lifetime elision in a path: + /// ```compile_fail + /// struct Foo<'a> { x: &'a () } + /// async fn foo(x: Foo) {} + /// ``` + /// + /// Note: the error should not trigger when the elided lifetime is in a pattern or + /// expression-position path: + /// ``` + /// struct Foo<'a> { x: &'a () } + /// async fn foo(Foo { x: _ }: Foo<'_>) {} + /// ``` + AnonymousCreateParameter { report_in_path: bool }, + + /// Replace all anonymous lifetimes by provided lifetime. + Elided(Region<'db>), + + /// Give a hard error when either `&` or `'_` is written. Used to + /// rule out things like `where T: Foo<'_>`. Does not imply an + /// error on default object bounds (e.g., `Box`). + AnonymousReportError, + + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `only_lint` is enabled). + StaticIfNoLifetimeInScope { only_lint: bool }, + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, + + /// Infer all elided lifetimes. + Infer, +} + +impl<'db> LifetimeElisionKind<'db> { + #[inline] + pub(crate) fn for_const( + interner: DbInterner<'db>, + const_parent: ItemContainerId, + ) -> LifetimeElisionKind<'db> { + match const_parent { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + LifetimeElisionKind::Elided(Region::new_static(interner)) + } + ItemContainerId::ImplId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } + } + ItemContainerId::TraitId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } + } + } + } + + #[inline] + pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> { + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } + } + + #[inline] + pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> { + // FIXME: We should use the elided lifetime here, or `ElisionFailure`. + LifetimeElisionKind::Elided(Region::error(interner)) + } +} + +#[derive(Debug)] +pub(crate) struct TyLoweringContext<'db, 'a> { + pub db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + generics: OnceCell, + in_binders: DebruijnIndex, + impl_trait_mode: ImplTraitLoweringState<'db>, + /// Tracks types with explicit `?Sized` bounds. + pub(crate) unsized_types: FxHashSet>, + pub(crate) diagnostics: Vec, + lifetime_elision: LifetimeElisionKind<'db>, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn new( + db: &'db dyn HirDatabase, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> Self { + let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); + let in_binders = DebruijnIndex::ZERO; + Self { + db, + interner: DbInterner::new_with(db, Some(resolver.krate()), None), + resolver, + def, + generics: Default::default(), + store, + in_binders, + impl_trait_mode, + unsized_types: FxHashSet::default(), + diagnostics: Vec::new(), + lifetime_elision, + } + } + + pub(crate) fn with_debruijn( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + let old_debruijn = mem::replace(&mut self.in_binders, debruijn); + let result = f(self); + self.in_binders = old_debruijn; + result + } + + pub(crate) fn with_shifted_in( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) + } + + pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { + Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } + } + + pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { + self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); + self + } + + pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { + self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub(crate) enum ImplTraitLoweringMode { + /// `impl Trait` gets lowered into an opaque type that doesn't unify with + /// anything except itself. This is used in places where values flow 'out', + /// i.e. for arguments of the function we're currently checking, and return + /// types of functions we're calling. + Opaque, + /// `impl Trait` is disallowed and will be an error. + #[default] + Disallowed, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { + self.lower_ty_ext(type_ref).0 + } + + pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty<'db>) -> Const<'db> { + let const_ref = &self.store[const_ref.expr]; + match const_ref { + hir_def::hir::Expr::Path(path) => { + path_to_const(self.db, self.resolver, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + hir_def::hir::Expr::Literal(literal) => intern_const_ref( + self.db, + &match *literal { + hir_def::hir::Literal::Float(_, _) + | hir_def::hir::Literal::String(_) + | hir_def::hir::Literal::ByteString(_) + | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown, + hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c), + hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b), + hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val), + hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val), + }, + const_type, + self.resolver.krate(), + ), + _ => unknown_const(const_type), + } + } + + pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { + path_to_const(self.db, self.resolver, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + + fn generics(&self) -> &Generics { + self.generics.get_or_init(|| generics(self.db, self.def)) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option) { + let interner = self.interner; + let mut res = None; + let type_ref = &self.store[type_ref_id]; + tracing::debug!(?type_ref); + let ty = match type_ref { + TypeRef::Never => Ty::new(interner, TyKind::Never), + TypeRef::Tuple(inner) => { + let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr)); + Ty::new_tup_from_iter(interner, inner_tys) + } + TypeRef::Path(path) => { + let (ty, res_) = + self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id)); + res = res_; + ty + } + &TypeRef::TypeParam(type_param_id) => { + res = Some(TypeNs::GenericParam(type_param_id)); + + let generics = self.generics(); + let (idx, data) = + generics.type_or_const_param(type_param_id.into()).expect("matching generics"); + let type_data = match data { + TypeOrConstParamData::TypeParamData(ty) => ty, + _ => unreachable!(), + }; + Ty::new_param( + self.interner, + idx as u32, + type_data + .name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |d| d.symbol().clone()), + ) + } + &TypeRef::RawPtr(inner, mutability) => { + let inner_ty = self.lower_ty(inner); + Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability))) + } + TypeRef::Array(array) => { + let inner_ty = self.lower_ty(array.ty); + let const_len = self.lower_const(&array.len, Ty::new_usize(interner)); + Ty::new_array_with_const_len(interner, inner_ty, const_len) + } + &TypeRef::Slice(inner) => { + let inner_ty = self.lower_ty(inner); + Ty::new_slice(interner, inner_ty) + } + TypeRef::Reference(ref_) => { + let inner_ty = self.lower_ty(ref_.ty); + // FIXME: It should infer the eldided lifetimes instead of stubbing with error + let lifetime = ref_ + .lifetime + .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr)); + Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) + } + TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), + TypeRef::Fn(fn_) => { + let substs = self.with_shifted_in( + DebruijnIndex::from_u32(1), + |ctx: &mut TyLoweringContext<'_, '_>| { + Tys::new_from_iter( + interner, + fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), + ) + }, + ); + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { + abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + c_variadic: fn_.is_varargs, + inputs_and_output: substs, + }), + ) + } + TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), + TypeRef::ImplTrait(bounds) => { + match self.impl_trait_mode.mode { + ImplTraitLoweringMode::Opaque => { + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), + }; + + // this dance is to make sure the data is in the right + // place even if we encounter more opaque types while + // lowering the bounds + let idx = self + .impl_trait_mode + .opaque_type_data + .alloc(ImplTrait { predicates: Vec::default() }); + + // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())), + |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())), + ); + let opaque_ty_id: SolverDefId = + self.db.intern_impl_trait_id(impl_trait_id).into(); + + // We don't want to lower the bounds inside the binders + // we're currently in, because they don't end up inside + // those binders. E.g. when we have `impl Trait>`, the `impl OtherTrait` can't refer + // to the self parameter from `impl Trait`, and the + // bounds aren't actually stored nested within each + // other, but separately. So if the `T` refers to a type + // parameter of the outer function, it's just one binder + // away instead of two. + let actual_opaque_type_data = self + .with_debruijn(DebruijnIndex::ZERO, |ctx| { + ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate()) + }); + self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; + + let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); + Ty::new_alias( + self.interner, + AliasTyKind::Opaque, + AliasTy::new_from_args(self.interner, opaque_ty_id, args), + ) + } + ImplTraitLoweringMode::Disallowed => { + // FIXME: report error + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + } + TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), + }; + (ty, res) + } + + /// This is only for `generic_predicates_for_param`, where we can't just + /// lower the self types of the predicates since that could lead to cycles. + /// So we just check here if the `type_ref` resolves to a generic param, and which. + fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { + let type_ref = &self.store[type_ref]; + let path = match type_ref { + TypeRef::Path(path) => path, + &TypeRef::TypeParam(idx) => return Some(idx.into()), + _ => return None, + }; + if path.type_anchor().is_some() { + return None; + } + if path.segments().len() > 1 { + return None; + } + let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) { + Some((it, None, _)) => it, + _ => return None, + }; + match resolution { + TypeNs::GenericParam(param_id) => Some(param_id.into()), + _ => None, + } + } + + #[inline] + fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static, 'db> { + PathDiagnosticCallback { + data: Either::Left(PathDiagnosticCallbackData(type_ref)), + callback: |data, this, diag| { + let type_ref = data.as_ref().left().unwrap().0; + this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + }, + } + } + + #[inline] + fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> { + PathLoweringContext::new( + self, + Self::on_path_diagnostic_callback(path_id.type_ref()), + &self.store[path_id], + ) + } + + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option) { + // Resolve the path (in type namespace) + if let Some(type_ref) = path.type_anchor() { + let (ty, res) = self.lower_ty_ext(type_ref); + let mut ctx = self.at_path(path_id); + return ctx.lower_ty_relative_path(ty, res); + } + + let mut ctx = self.at_path(path_id); + let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() { + Some(it) => it, + None => return (Ty::new_error(self.interner, ErrorGuaranteed), None), + }; + + if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { + // trait object type without dyn + let bound = TypeBound::Path(path_id, TraitBoundModifier::None); + let ty = self.lower_dyn_trait(&[bound]); + return (ty, None); + } + + ctx.lower_partly_resolved_path(resolution, false) + } + + fn lower_trait_ref_from_path( + &mut self, + path_id: PathId, + explicit_self_ty: Ty<'db>, + ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> { + let mut ctx = self.at_path(path_id); + let resolved = match ctx.resolve_path_in_type_ns_fully()? { + // FIXME(trait_alias): We need to handle trait alias here. + TypeNs::TraitId(tr) => tr, + _ => return None, + }; + Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx)) + } + + fn lower_trait_ref( + &mut self, + trait_ref: &HirTraitRef, + explicit_self_ty: Ty<'db>, + ) -> Option> { + self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) + } + + pub(crate) fn lower_where_predicate<'b>( + &'b mut self, + where_predicate: &'b WherePredicate, + ignore_bindings: bool, + generics: &Generics, + predicate_filter: PredicateFilter, + ) -> impl Iterator> + use<'a, 'b, 'db> { + match where_predicate { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound } => { + if let PredicateFilter::SelfTrait = predicate_filter { + let target_type = &self.store[*target]; + let self_type = 'is_self: { + if let TypeRef::Path(path) = target_type + && path.is_self_type() + { + break 'is_self true; + } + if let TypeRef::TypeParam(param) = target_type + && generics[param.local_id()].is_trait_self() + { + break 'is_self true; + } + false + }; + if !self_type { + return Either::Left(Either::Left(iter::empty())); + } + } + let self_ty = self.lower_ty(*target); + Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) + } + &WherePredicate::Lifetime { bound, target } => { + Either::Right(iter::once(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( + self.lower_lifetime(bound), + self.lower_lifetime(target), + )), + )), + )))) + } + } + .into_iter() + } + + pub(crate) fn lower_type_bound<'b>( + &'b mut self, + bound: &'b TypeBound, + self_ty: Ty<'db>, + ignore_bindings: bool, + ) -> impl Iterator> + use<'b, 'a, 'db> { + let interner = self.interner; + let mut assoc_bounds = None; + let mut clause = None; + match bound { + &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { + // FIXME Don't silently drop the hrtb lifetimes here + if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { + // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented + // sized-hierarchy correctly. + let meta_sized = LangItem::MetaSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let pointee_sized = LangItem::PointeeSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) { + // Ignore this bound + } else if pointee_sized + .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) + { + // Regard this as `?Sized` bound + ctx.ty_ctx().unsized_types.insert(self_ty); + } else { + if !ignore_bindings { + assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); + } + clause = Some(Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + ))); + } + } + } + &TypeBound::Path(path, TraitBoundModifier::Maybe) => { + let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate()); + // Don't lower associated type bindings as the only possible relaxed trait bound + // `?Sized` has no of them. + // If we got another trait here ignore the bound completely. + let trait_id = + self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| { + match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + } + }); + if trait_id == sized_trait { + self.unsized_types.insert(self_ty); + } + } + &TypeBound::Lifetime(l) => { + let lifetime = self.lower_lifetime(l); + clause = Some(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( + self_ty, lifetime, + )), + )), + ))); + } + TypeBound::Use(_) | TypeBound::Error => {} + } + clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + } + + fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { + let interner = self.interner; + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + // INVARIANT: The principal trait bound, if present, must come first. Others may be in any + // order but should be in the same order for the same set but possibly different order of + // bounds in the input. + // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. + // These invariants are utilized by `TyExt::dyn_trait()` and chalk. + let mut lifetime = None; + let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut lowered_bounds: Vec< + rustc_type_ir::Binder, ExistentialPredicate>>, + > = Vec::new(); + for b in bounds { + let db = ctx.db; + ctx.lower_type_bound(b, self_ty, false).for_each(|b| { + if let Some(bound) = b + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = + db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait( + ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ), + )) + } + } + rustc_type_ir::ClauseKind::Projection(p) => { + Some(ExistentialPredicate::Projection( + ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + ), + )) + } + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => { + lifetime = Some(outlives_predicate.1); + None + } + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + lowered_bounds.push(bound); + } + }) + } + + let mut multiple_regular_traits = false; + let mut multiple_same_projection = false; + lowered_bounds.sort_unstable_by(|lhs, rhs| { + use std::cmp::Ordering; + match ((*lhs).skip_binder(), (*rhs).skip_binder()) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => { + multiple_regular_traits = true; + // Order doesn't matter - we error + Ordering::Equal + } + ( + ExistentialPredicate::AutoTrait(lhs_id), + ExistentialPredicate::AutoTrait(rhs_id), + ) => { + let lhs_id = match lhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + lhs_id.cmp(&rhs_id) + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, + (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater, + ( + ExistentialPredicate::Projection(lhs), + ExistentialPredicate::Projection(rhs), + ) => { + let lhs_id = match lhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + // We only compare the `associated_ty_id`s. We shouldn't have + // multiple bounds for an associated type in the correct Rust code, + // and if we do, we error out. + if lhs_id == rhs_id { + multiple_same_projection = true; + } + lhs_id.as_id().index().cmp(&rhs_id.as_id().index()) + } + } + }); + + if multiple_regular_traits || multiple_same_projection { + return None; + } + + if !lowered_bounds.first().map_or(false, |b| { + matches!( + b.as_ref().skip_binder(), + ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_) + ) + }) { + return None; + } + + // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the + // bounds. We shouldn't have repeated elements besides auto traits at this point. + lowered_bounds.dedup(); + + Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds)) + }); + + if let Some(bounds) = bounds { + let region = match lifetime { + Some(it) => match it.kind() { + rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound( + self.interner, + db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), + var, + ), + _ => it, + }, + None => Region::new_static(self.interner), + }; + Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn) + } else { + // FIXME: report error + // (additional non-auto traits, associated type rebound, or no resolved trait) + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + + fn lower_impl_trait( + &mut self, + def_id: SolverDefId, + bounds: &[TypeBound], + krate: Crate, + ) -> ImplTrait<'db> { + let interner = self.interner; + cov_mark::hit!(lower_rpit); + let args = GenericArgs::identity_for_item(interner, def_id); + let self_ty = Ty::new_alias( + self.interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + for b in bounds { + predicates.extend(ctx.lower_type_bound(b, self_ty, false)); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(self.db, krate); + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + predicates.shrink_to_fit(); + predicates + }); + ImplTrait { predicates } + } + + pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Region<'db> { + match self.resolver.resolve_lifetime(&self.store[lifetime]) { + Some(resolution) => match resolution { + LifetimeNs::Static => Region::new_static(self.interner), + LifetimeNs::LifetimeParam(id) => { + let idx = match self.generics().lifetime_idx(id) { + None => return Region::error(self.interner), + Some(idx) => idx, + }; + Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 }) + } + }, + None => Region::error(self.interner), + } + } +} + +pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { + match m { + hir_def::type_ref::Mutability::Shared => Mutability::Not, + hir_def::type_ref::Mutability::Mut => Mutability::Mut, + } +} + +fn unknown_const(_ty: Ty<'_>) -> Const<'_> { + Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) +} + +pub(crate) fn impl_trait_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option>> { + db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0) +} + +pub(crate) fn impl_trait_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { + let impl_data = db.impl_signature(impl_id); + let resolver = impl_id.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let self_ty = db.impl_self_ty_ns(impl_id).skip_binder(); + let target_trait = impl_data.target_trait.as_ref()?; + let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); + Some((trait_ref, create_diagnostics(ctx.diagnostics))) +} + +pub(crate) fn return_type_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::FunctionId, +) -> Option>>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_signature(def); + let resolver = def.resolver(db); + let mut ctx_ret = + TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(ret_type) = data.ret_type { + let _ret = ctx_ret.lower_ty(ret_type); + } + let return_type_impl_traits = + ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; + if return_type_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(return_type_impl_traits))) + } +} + +pub(crate) fn type_alias_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>>> { + let data = db.type_alias_signature(def); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(type_ref) = data.ty { + let _ty = ctx.lower_ty(type_ref); + } + let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits))) + } +} + +/// Build the declared type of an item. This depends on the namespace; e.g. for +/// `struct Foo(usize)`, we have two types: The type of the struct itself, and +/// the constructor function `(usize) -> Foo` which lives in the values +/// namespace. +pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + match def { + TyDefId::BuiltinType(it) => EarlyBinder::bind(builtin(interner, it)), + TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( + interner, + AdtDef::new(it, interner), + GenericArgs::identity_for_item(interner, it.into()), + )), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0, + } +} + +pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + t: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let type_alias_data = db.type_alias_signature(t); + let mut diags = None; + let resolver = t.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { + EarlyBinder::bind(Ty::new_foreign(interner, t.into())) + } else { + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + t.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let res = EarlyBinder::bind( + type_alias_data + .ty + .map(|type_ref| ctx.lower_ty(type_ref)) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)), + ); + diags = create_diagnostics(ctx.diagnostics); + res + }; + (inner, diags) +} + +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, + _adt: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn impl_self_ty_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> EarlyBinder<'db, Ty<'db>> { + db.impl_self_ty_with_diagnostics_ns(impl_id).0 +} + +pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let resolver = impl_id.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + + let impl_data = db.impl_signature(impl_id); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let ty = ctx.lower_ty(impl_data.self_ty); + assert!(!ty.has_escaping_bound_vars()); + (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _impl_id: ImplId, +) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { + db.const_param_ty_with_diagnostics_ns(def).0 +} + +// returns None if def is a type arg +pub(crate) fn const_param_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: ConstParamId, +) -> (Ty<'db>, Diagnostics) { + let (parent_data, store) = db.generic_params_and_store(def.parent()); + let data = &parent_data[def.local_id()]; + let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &store, + def.parent(), + LifetimeElisionKind::AnonymousReportError, + ); + let ty = match data { + TypeOrConstParamData::TypeParamData(_) => { + never!(); + Ty::new_error(interner, ErrorGuaranteed) + } + TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), + }; + (ty, create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn field_types_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> Arc>>> { + db.field_types_with_diagnostics_ns(variant_id).0 +} + +/// Build the type of all specific fields of a struct or enum variant. +pub(crate) fn field_types_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> (Arc>>>, Diagnostics) { + let var_data = variant_id.fields(db); + let fields = var_data.fields(); + if fields.is_empty() { + return (Arc::new(ArenaMap::default()), None); + } + + let (resolver, def): (_, GenericDefId) = match variant_id { + VariantId::StructId(it) => (it.resolver(db), it.into()), + VariantId::UnionId(it) => (it.resolver(db), it.into()), + VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), + }; + let mut res = ArenaMap::default(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &var_data.store, + def, + LifetimeElisionKind::AnonymousReportError, + ); + for (field_id, field_data) in var_data.fields().iter() { + res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref))); + } + (Arc::new(res), create_diagnostics(ctx.diagnostics)) +} + +/// This query exists only to be used when resolving short-hand associated types +/// like `T::Item`. +/// +/// See the analogous query in rustc and its comment: +/// +/// This is a query mostly to handle cycles somewhat gracefully; e.g. the +/// following bounds are disallowed: `T: Foo, U: Foo`, but +/// these are fine: `T: Foo, U: Foo<()>`. +#[tracing::instrument(skip(db), ret)] +pub(crate) fn generic_predicates_for_param_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, +) -> GenericPredicates<'db> { + let generics = generics(db, def); + let interner = DbInterner::new_with(db, None, None); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; + if invalid_target { + // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented + // sized-hierarchy correctly. + // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into + // `ctx.unsized_types` + let lower = || -> bool { + match bound { + TypeBound::Path(_, TraitBoundModifier::Maybe) => true, + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { + let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { + return false; + }; + let Some(pointee_sized) = + LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) + else { + return false; + }; + // Lower the path directly with `Resolver` instead of PathLoweringContext` + // to prevent diagnostics duplications. + ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( + |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), + ) + } + _ => false, + } + }(); + if lower { + ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) + .for_each(drop); + } + return false; + } + + match bound { + &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. + let path = &ctx.store[path]; + + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db, path) + else { + return false; + }; + + rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { + let tr = match tr { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + tr.trait_items(db).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name + }) + }) + } + TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, + } + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + if predicate(pred, &mut ctx) { + predicates.extend(ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + )); + } + } + } + + let args = GenericArgs::identity_for_item(interner, def.into()); + if !args.is_empty() { + let explicitly_unsized_tys = ctx.unsized_types; + if let Some(implicitly_sized_predicates) = + implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver) + { + predicates.extend(implicitly_sized_predicates); + }; + } + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) +} + +pub(crate) fn generic_predicates_for_param_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, + _param_id: TypeOrConstParamId, + _assoc_name: Option, +) -> GenericPredicates<'_> { + GenericPredicates(None) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericPredicates<'db>(Option]>>); + +impl<'db> ops::Deref for GenericPredicates<'db> { + type Target = [Clause<'db>]; + + fn deref(&self) -> &Self::Target { + self.0.as_deref().unwrap_or(&[]) + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum PredicateFilter { + SelfTrait, + All, +} + +/// Resolve the where clause(s) of an item with generics. +#[tracing::instrument(skip(db))] +pub(crate) fn generic_predicates_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0 +} + +pub(crate) fn generic_predicates_without_parent_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0 +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> (GenericPredicates<'db>, Diagnostics) { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// with a given filter +#[tracing::instrument(skip(db, filter), ret)] +pub(crate) fn generic_predicates_filtered_by<'db, F>( + db: &'db dyn HirDatabase, + def: GenericDefId, + predicate_filter: PredicateFilter, + filter: F, +) -> (GenericPredicates<'db>, Diagnostics) +where + F: Fn(GenericDefId) -> bool, +{ + let generics = generics(db, def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + if filter(maybe_parent_generics.def()) { + // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake! + // If we use the parent generics + predicates.extend(ctx.lower_where_predicate( + pred, + false, + maybe_parent_generics, + predicate_filter, + )); + } + } + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let (mut generics, mut def_id) = + (crate::next_solver::generics::generics(db, def.into()), def); + loop { + if filter(def_id) { + let self_idx = trait_self_param_idx(db, def_id); + for (idx, p) in generics.own_params.iter().enumerate() { + if let Some(self_idx) = self_idx + && p.index() as usize == self_idx + { + continue; + } + let GenericParamDefKind::Type = p.kind else { + continue; + }; + let idx = idx as u32 + generics.parent_count as u32; + let param_ty = Ty::new_param(interner, idx, p.name.clone()); + if explicitly_unsized_tys.contains(¶m_ty) { + continue; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + predicates.push(clause); + } + } + + if let Some(g) = generics.parent { + generics = crate::next_solver::generics::generics(db, g.into()); + def_id = g; + } else { + break; + } + } + } + + ( + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), + create_diagnostics(ctx.diagnostics), + ) +} + +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. +/// Exception is Self of a trait def. +fn implicitly_sized_clauses<'a, 'subst, 'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + explicitly_unsized_tys: &'a FxHashSet>, + args: &'subst GenericArgs<'db>, + resolver: &Resolver<'db>, +) -> Option> + Captures<'a> + Captures<'subst>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?; + + let trait_self_idx = trait_self_param_idx(db, def); + + Some( + args.iter() + .enumerate() + .filter_map( + move |(idx, generic_arg)| { + if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } + }, + ) + .filter_map(|generic_arg| generic_arg.as_type()) + .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) + .map(move |self_ty| { + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }), + ) +} + +pub(crate) fn make_binders<'db, T: rustc_type_ir::TypeVisitable>>( + interner: DbInterner<'db>, + generics: &Generics, + value: T, +) -> Binder<'db, T> { + Binder::bind_with_vars( + value, + BoundVarKinds::new_from_iter( + interner, + generics.iter_id().map(|x| match x { + hir_def::GenericParamId::ConstParamId(_) => BoundVarKind::Const, + hir_def::GenericParamId::TypeParamId(_) => BoundVarKind::Ty(BoundTyKind::Anon), + hir_def::GenericParamId::LifetimeParamId(_) => { + BoundVarKind::Region(BoundRegionKind::Anon) + } + }), + ), + ) +} + +/// Checks if the provided generic arg matches its expected kind, then lower them via +/// provided closures. Use unknown if there was kind mismatch. +/// +pub(crate) fn lower_generic_arg<'a, 'db, T>( + db: &'db dyn HirDatabase, + kind_id: GenericParamId, + arg: &'a GenericArg, + this: &mut T, + store: &ExpressionStore, + for_type: impl FnOnce(&mut T, TypeRefId) -> Ty<'db> + 'a, + for_const: impl FnOnce(&mut T, &ConstRef, Ty<'db>) -> Const<'db> + 'a, + for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty<'db>) -> Const<'db> + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRefId) -> Region<'db> + 'a, +) -> crate::next_solver::GenericArg<'db> { + let interner = DbInterner::new_with(db, None, None); + let kind = match kind_id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { + let ty = db.const_param_ty(id); + ParamKind::Const(ty) + } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, + }; + match (arg, kind) { + (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).into(), + (GenericArg::Const(c), ParamKind::Const(c_ty)) => { + for_const(this, c, c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).into() + } + (GenericArg::Const(_), ParamKind::Type) => Ty::new_error(interner, ErrorGuaranteed).into(), + (GenericArg::Lifetime(_), ParamKind::Type) => { + Ty::new_error(interner, ErrorGuaranteed).into() + } + (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] { + TypeRef::Path(p) => { + for_const_ty_path_fallback(this, p, c_ty.to_nextsolver(interner)).into() + } + _ => unknown_const_as_generic(c_ty.to_nextsolver(interner)), + }, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => { + unknown_const(c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Type(_), ParamKind::Lifetime) => Region::error(interner).into(), + (GenericArg::Const(_), ParamKind::Lifetime) => Region::error(interner).into(), + } +} + +/// Build the signature of a callable item (function, struct or enum variant). +pub(crate) fn callable_item_signature_query<'db>( + db: &'db dyn HirDatabase, + def: CallableDefId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + match def { + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), + } +} + +fn fn_sig_for_fn<'db>( + db: &'db dyn HirDatabase, + def: FunctionId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let data = db.function_signature(def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx_params = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_params(&data), + ); + let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); + + let ret = match data.ret_type { + Some(ret_type) => { + let mut ctx_ret = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_ret(interner), + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + ctx_ret.lower_ty(ret_type) + } + None => Ty::new_tup(interner, &[]), + }; + + let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + // If/when we track late bound vars, we need to switch this to not be `dummy` + EarlyBinder::bind(rustc_type_ir::Binder::dummy(FnSig { + abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + c_variadic: data.is_varargs(), + safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + inputs_and_output, + })) +} + +fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + let args = GenericArgs::identity_for_item(interner, adt.into()); + let ty = Ty::new_adt(interner, AdtDef::new(adt, interner), args); + EarlyBinder::bind(ty) +} + +fn fn_sig_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let ret = type_for_adt(db, def.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +fn fn_sig_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let parent = def.lookup(db).parent; + let ret = type_for_adt(db, parent.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +pub(crate) fn associated_type_by_name_including_super_traits<'db>( + db: &'db dyn HirDatabase, + trait_ref: TraitRef<'db>, + name: &Name, +) -> Option<(TraitRef<'db>, TypeAliasId)> { + let interner = DbInterner::new_with(db, None, None); + rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { + let trait_id = match t.as_ref().skip_binder().def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; + Some((t.skip_binder(), assoc_type)) + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs new file mode 100644 index 0000000000000..b95bb1130fa20 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -0,0 +1,1459 @@ +//! A wrapper around [`TyLoweringContext`] specifically for lowering paths. + +use std::ops::Deref; + +use either::Either; +use hir_def::{ + AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, + builtin_type::BuiltinType, + expr_store::{ + ExpressionStore, HygieneId, + path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + }, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + }, + resolver::{ResolveValueResult, TypeNs, ValueNs}, + signatures::TraitFlags, + type_ref::{TypeRef, TypeRefId}, +}; +use intern::sym; +use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt, + inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, +}; +use smallvec::{SmallVec, smallvec}; +use stdx::never; + +use crate::{ + GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, + PathLoweringDiagnostic, TyDefId, ValueTyDefId, + consteval_nextsolver::{unknown_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics}, + lower::PathDiagnosticCallbackData, + lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by}, + next_solver::{ + AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, + Region, SolverDefId, TraitRef, Ty, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + }, + primitive, +}; + +use super::{ + ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, + const_param_ty_query, ty_query, +}; + +type CallbackData<'a> = + Either>; + +// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box` +// because of the allocation, so we create a lifetime-less callback, tailored for our needs. +pub(crate) struct PathDiagnosticCallback<'a, 'db> { + pub(crate) data: CallbackData<'a>, + pub(crate) callback: + fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), +} + +pub(crate) struct PathLoweringContext<'a, 'b, 'db> { + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + segments: PathSegments<'a>, + current_segment_idx: usize, + /// Contains the previous segment if `current_segment_idx == segments.len()` + current_or_prev_segment: PathSegment<'a>, +} + +impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { + #[inline] + pub(crate) fn new( + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + ) -> Self { + let segments = path.segments(); + let first_segment = segments.first().unwrap_or(PathSegment::MISSING); + Self { + ctx, + on_diagnostic, + path, + segments, + current_segment_idx: 0, + current_or_prev_segment: first_segment, + } + } + + #[inline] + #[cold] + fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { + (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag); + } + + #[inline] + pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { + self.ctx + } + + #[inline] + fn current_segment_u32(&self) -> u32 { + self.current_segment_idx as u32 + } + + #[inline] + fn skip_resolved_segment(&mut self) { + if !matches!(self.path, Path::LangItem(..)) { + // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it + // point at -1, but I don't feel this is clearer. + self.current_segment_idx += 1; + } + self.update_current_segment(); + } + + #[inline] + fn update_current_segment(&mut self) { + self.current_or_prev_segment = + self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); + } + + #[inline] + pub(crate) fn ignore_last_segment(&mut self) { + self.segments = self.segments.strip_last(); + } + + #[inline] + pub(crate) fn set_current_segment(&mut self, segment: usize) { + self.current_segment_idx = segment; + self.current_or_prev_segment = self + .segments + .get(segment) + .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); + } + + #[inline] + fn with_lifetime_elision( + &mut self, + lifetime_elision: LifetimeElisionKind<'db>, + f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T, + ) -> T { + let old_lifetime_elision = + std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); + let result = f(self); + self.ctx.lifetime_elision = old_lifetime_elision; + result + } + + pub(crate) fn lower_ty_relative_path( + &mut self, + ty: Ty<'db>, + // We need the original resolution to lower `Self::AssocTy` correctly + res: Option, + ) -> (Ty<'db>, Option) { + let remaining_segments = self.segments.len() - self.current_segment_idx; + match remaining_segments { + 0 => (ty, res), + 1 => { + // resolve unselected assoc types + (self.select_associated_type(res), None) + } + _ => { + // FIXME report error (ambiguous associated type) + (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) + } + } + } + + fn prohibit_parenthesized_generic_args(&mut self) -> bool { + if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings { + match generic_args.parenthesized { + GenericArgsParentheses::No => {} + GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + return true; + } + } + } + false + } + + // When calling this, the current segment is the resolved segment (we don't advance it yet). + pub(crate) fn lower_partly_resolved_path( + &mut self, + resolution: TypeNs, + infer_args: bool, + ) -> (Ty<'db>, Option) { + let remaining_segments = self.segments.skip(self.current_segment_idx + 1); + tracing::debug!(?remaining_segments); + let rem_seg_len = remaining_segments.len(); + tracing::debug!(?rem_seg_len); + + let ty = match resolution { + TypeNs::TraitId(trait_) => { + let ty = match remaining_segments.len() { + 1 => { + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + Ty::new_error(self.ctx.interner, ErrorGuaranteed), + ); + tracing::debug!(?trait_ref); + self.skip_resolved_segment(); + let segment = self.current_or_prev_segment; + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let found = + trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); + + tracing::debug!(?found); + match found { + Some(associated_ty) => { + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`trait_ref.substitution`). + let substitution = self.substs_from_path_segment( + associated_ty.into(), + false, + None, + true, + ); + let args = crate::next_solver::GenericArgs::new_from_iter( + self.ctx.interner, + trait_ref + .args + .iter() + .chain(substitution.iter().skip(trait_ref.args.len())), + ); + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ) + } + None => { + // FIXME: report error (associated type not found) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + } + } + 0 => { + // Trait object type without dyn; this should be handled in upstream. See + // `lower_path()`. + stdx::never!("unexpected fully resolved trait path"); + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + _ => { + // FIXME report error (ambiguous associated type) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + }; + return (ty, None); + } + TypeNs::TraitAliasId(_) => { + // FIXME(trait_alias): Implement trait alias. + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + } + TypeNs::GenericParam(param_id) => { + let generics = self.ctx.generics(); + let idx = generics.type_or_const_param_idx(param_id.into()); + match idx { + None => { + never!("no matching generics"); + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + Some(idx) => { + let (pidx, param) = generics.iter().nth(idx).unwrap(); + assert_eq!(pidx, param_id.into()); + let p = match param { + GenericParamDataRef::TypeParamData(p) => p, + _ => unreachable!(), + }; + Ty::new_param( + self.ctx.interner, + idx as u32, + p.name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()), + ) + } + } + } + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(), + TypeNs::AdtSelfType(adt) => { + let args = crate::next_solver::GenericArgs::identity_for_item( + self.ctx.interner, + adt.into(), + ); + Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), args) + } + + TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), + TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args), + TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), + // FIXME: report error + TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + } + }; + + tracing::debug!(?ty); + + self.skip_resolved_segment(); + self.lower_ty_relative_path(ty, Some(resolution)) + } + + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + let mut prohibit_generics_on_resolved = |reason| { + if self.current_or_prev_segment.args_and_bindings.is_some() { + let segment = self.current_segment_u32(); + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment, + reason, + }); + } + }; + + match resolution { + TypeNs::SelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) + } + TypeNs::AdtSelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::BuiltinType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) + } + TypeNs::ModuleId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) + } + TypeNs::AdtId(_) + | TypeNs::EnumVariantId(_) + | TypeNs::TypeAliasId(_) + | TypeNs::TraitId(_) + | TypeNs::TraitAliasId(_) => {} + } + } + + pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option { + let (res, unresolved) = self.resolve_path_in_type_ns()?; + if unresolved.is_some() { + return None; + } + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { + let (resolution, remaining_index, _, prefix_info) = + self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some((resolution, remaining_index)); + } + + let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { + None if prefix_info.enum_variant => { + (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) + } + None => (segments.strip_last(), segments.len() - 1, None), + Some(i) => (segments.take(i - 1), i - 1, None), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + if matches!(self.path, Path::BarePath(..)) { + // Bare paths cannot have generics, so skip them as an optimization. + return Some((resolution, remaining_index)); + } + + for (i, mod_segment) in module_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + self.handle_type_ns_resolution(&resolution); + + Some((resolution, remaining_index)) + } + + pub(crate) fn resolve_path_in_value_ns( + &mut self, + hygiene_id: HygieneId, + ) -> Option { + let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( + self.ctx.db, + self.path, + hygiene_id, + )?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some(res); + } + + let (mod_segments, enum_segment, resolved_segment_idx) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = self.current_segment_u32(); + let resolved_segment = self.current_or_prev_segment; + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx, + reason, + }); + } + }; + + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} + ValueNs::LocalBinding(_) => {} + } + } + ResolveValueResult::Partial(resolution, _, _) => { + self.handle_type_ns_resolution(resolution); + } + }; + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + fn select_associated_type(&mut self, res: Option) -> Ty<'db> { + let interner = self.ctx.interner; + let Some(res) = res else { + return Ty::new_error(self.ctx.interner, ErrorGuaranteed); + }; + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let db = self.ctx.db; + let def = self.ctx.def; + let mut search = |t: TraitRef<'db>| { + let trait_id = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_id: TraitId| { + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id { + if name != assoc_name { + continue; + } + + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`t.substitution`). + let substs = self.substs_from_path_segment(alias.into(), false, None, true); + + let substs = crate::next_solver::GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + return Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, alias.into(), substs), + )); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; + while let Some(trait_def_id) = stack.pop() { + if let Some(alias) = check_trait(trait_def_id) { + return alias; + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_def_id), + PredicateFilter::SelfTrait, + |pred| pred == GenericDefId::TraitId(trait_def_id), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let trait_id = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + _ => continue, + }; + let trait_id = match trait_id { + SolverDefId::TraitId(trait_id) => trait_id, + _ => continue, + }; + stack.push(trait_id); + } + tracing::debug!(?stack); + } + + Ty::new_error(interner, ErrorGuaranteed) + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait_ns(impl_id); + let Some(trait_ref) = trait_ref else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + // see FIXME in `search`. + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = crate::next_solver::GenericArgs::identity_for_item( + interner, + trait_id.into(), + ); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = db.generic_predicates_for_param_ns( + def, + param_id.into(), + Some(segment.name.clone()), + ); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .map(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + } + _ => Ty::new_error(interner, ErrorGuaranteed), + } + } + + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { + let generic_def = match typeable { + TyDefId::BuiltinType(builtinty) => return builtin(self.ctx.interner, builtinty), + TyDefId::AdtId(it) => it.into(), + TyDefId::TypeAliasId(it) => it.into(), + }; + let args = self.substs_from_path_segment(generic_def, infer_args, None, false); + let ty = ty_query(self.ctx.db, typeable); + ty.instantiate(self.ctx.interner, args) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + pub(crate) fn substs_from_path( + &mut self, + // Note that we don't call `db.value_type(resolved)` here, + // `ValueTyDefId` is just a convenient way to pass generics and + // special-case enum variants + resolved: ValueTyDefId, + infer_args: bool, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let interner = self.ctx.interner; + let prev_current_segment_idx = self.current_segment_idx; + let prev_current_segment = self.current_or_prev_segment; + + let generic_def = match resolved { + ValueTyDefId::FunctionId(it) => it.into(), + ValueTyDefId::StructId(it) => it.into(), + ValueTyDefId::UnionId(it) => it.into(), + ValueTyDefId::ConstId(it) => it.into(), + ValueTyDefId::StaticId(_) => { + return crate::next_solver::GenericArgs::new_from_iter(interner, []); + } + ValueTyDefId::EnumVariantId(var) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // FIXME: This isn't strictly correct, enum variants may be used not through the enum + // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result + // available here. The worst that can happen is that we will show some confusing diagnostics to the user, + // if generics exist on the module and they don't match with the variant. + // preferred). See also `def_ids_for_path_segments` in rustc. + // + // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. + // This simplifies the code a bit. + let penultimate_idx = self.current_segment_idx.wrapping_sub(1); + let penultimate = self.segments.get(penultimate_idx); + if let Some(penultimate) = penultimate + && self.current_or_prev_segment.args_and_bindings.is_none() + && penultimate.args_and_bindings.is_some() + { + self.current_segment_idx = penultimate_idx; + self.current_or_prev_segment = penultimate; + } + var.lookup(self.ctx.db).parent.into() + } + }; + let result = self.substs_from_path_segment( + generic_def, + infer_args, + None, + lowering_assoc_type_generics, + ); + self.current_segment_idx = prev_current_segment_idx; + self.current_or_prev_segment = prev_current_segment; + result + } + + pub(crate) fn substs_from_path_segment( + &mut self, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option>, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + + if let Some(args) = self.current_or_prev_segment.args_and_bindings + && args.parenthesized != GenericArgsParentheses::No + { + let prohibit_parens = match def { + GenericDefId::TraitId(trait_) => { + // RTN is prohibited anyways if we got here. + let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; + let is_fn_trait = self + .ctx + .db + .trait_signature(trait_) + .flags + .contains(TraitFlags::RUSTC_PAREN_SUGAR); + is_rtn || !is_fn_trait + } + _ => true, + }; + + if prohibit_parens { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + + return unknown_subst(self.ctx.interner, def); + } + + // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. + lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + } + + self.substs_from_args_and_bindings( + self.current_or_prev_segment.args_and_bindings, + def, + infer_args, + explicit_self_ty, + PathGenericsSource::Segment(self.current_segment_u32()), + lowering_assoc_type_generics, + lifetime_elision, + ) + } + + pub(super) fn substs_from_args_and_bindings( + &mut self, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option>, + generics_source: PathGenericsSource, + lowering_assoc_type_generics: bool, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'c, 'db> { + ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, + generics_source: PathGenericsSource, + } + + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen { + generics_source: self.generics_source, + provided_count, + expected_count, + kind, + def, + }); + } + + fn report_arg_mismatch( + &mut self, + param_id: GenericParamId, + arg_idx: u32, + has_self_arg: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder { + generics_source: self.generics_source, + param_id, + arg_idx, + has_self_arg, + }); + } + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db> { + match (param, arg) { + (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { + self.ctx.ctx.lower_lifetime(*lifetime).into() + } + (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(*type_ref).into() + } + (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + self.ctx + .ctx + .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .into() + } + _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), + } + } + + fn provided_type_like_const( + &mut self, + const_ty: Ty<'db>, + arg: TypeLikeConst<'_>, + ) -> crate::next_solver::Const<'db> { + match arg { + TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), + TypeLikeConst::Infer => unknown_const(const_ty), + } + } + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db> { + let default = || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { + convert_binder_to_early_binder( + self.ctx.ctx.interner, + default.to_nextsolver(self.ctx.ctx.interner), + ) + .instantiate(self.ctx.ctx.interner, preceding_args) + }) + }; + match param { + GenericParamDataRef::LifetimeParamData(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } + GenericParamDataRef::TypeParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamDataRef::ConstParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + } + } + } + + fn parent_arg( + &mut self, + param_id: GenericParamId, + ) -> crate::next_solver::GenericArg<'db> { + match param_id { + GenericParamId::TypeParamId(_) => { + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamId::ConstParamId(const_id) => { + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + } + GenericParamId::LifetimeParamId(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } + } + } + + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath { + generics_source: self.generics_source, + def, + expected_count, + hard_error, + }); + } + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { + generics_source: self.generics_source, + def, + expected_count, + }); + } + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { + generics_source: self.generics_source, + def, + expected_count, + }); + } + } + + substs_from_args_and_bindings( + self.ctx.db, + self.ctx.store, + args_and_bindings, + def, + infer_args, + lifetime_elision, + lowering_assoc_type_generics, + explicit_self_ty, + &mut LowererCtx { ctx: self, generics_source }, + ) + } + + pub(crate) fn lower_trait_ref_from_resolved_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> TraitRef<'db> { + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty); + TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) + } + + fn trait_ref_substs_from_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) + } + + pub(super) fn assoc_type_bindings_from_type_bound<'c>( + mut self, + trait_ref: TraitRef<'db>, + ) -> Option> + use<'a, 'b, 'c, 'db>> { + let interner = self.ctx.interner; + self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { + args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { + let found = associated_type_by_name_including_super_traits( + self.ctx.db, + trait_ref, + &binding.name, + ); + let (super_trait_ref, associated_ty) = match found { + None => return SmallVec::new(), + Some(t) => t, + }; + let args = + self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`super_trait_ref.substitution`). + this.substs_from_args_and_bindings( + binding.args.as_ref(), + associated_ty.into(), + false, // this is not relevant + Some(super_trait_ref.self_ty()), + PathGenericsSource::AssocType { + segment: this.current_segment_u32(), + assoc_type: binding_idx as u32, + }, + false, + this.ctx.lifetime_elision.clone(), + ) + }); + let args = crate::next_solver::GenericArgs::new_from_iter( + interner, + super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), + ); + let projection_term = + AliasTerm::new_from_args(interner, associated_ty.into(), args); + let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( + binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), + ); + if let Some(type_ref) = binding.type_ref { + match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { + let ty = self.ctx.lower_ty(type_ref); + let pred = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection(ProjectionPredicate { + projection_term, + term: ty.into(), + }), + )), + )); + predicates.push(pred); + } + } + } + for bound in binding.bounds.iter() { + predicates.extend(self.ctx.lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), + ), + false, + )); + } + predicates + }) + }) + } +} + +/// A const that were parsed like a type. +pub(crate) enum TypeLikeConst<'a> { + Infer, + Path(&'a Path), +} + +pub(crate) trait GenericArgsLowerer<'db> { + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ); + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); + + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ); + + fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool); + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db>; + + fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) + -> Const<'db>; + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db>; + + fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>; +} + +/// Returns true if there was an error. +fn check_generic_args_len<'db>( + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + def_generics: &Generics, + infer_args: bool, + lifetime_elision: &LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> bool { + let mut had_error = false; + + let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize); + if let Some(args_and_bindings) = args_and_bindings { + let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; + for arg in args_no_self { + match arg { + GenericArg::Lifetime(_) => provided_lifetimes_count += 1, + GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, + } + } + } + + let lifetime_args_len = def_generics.len_lifetimes_self(); + if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics { + // In generic associated types, we never allow inferring the lifetimes. + match lifetime_elision { + &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); + had_error |= report_in_path; + } + LifetimeElisionKind::AnonymousReportError => { + ctx.report_missing_lifetime(def, lifetime_args_len as u32); + had_error = true + } + LifetimeElisionKind::ElisionFailure => { + ctx.report_elision_failure(def, lifetime_args_len as u32); + had_error = true; + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + // FIXME: Check there are other lifetimes in scope, and error/lint. + } + LifetimeElisionKind::Elided(_) => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); + } + LifetimeElisionKind::Infer => { + // Allow eliding lifetimes. + } + } + } else if lifetime_args_len != provided_lifetimes_count { + ctx.report_len_mismatch( + def, + provided_lifetimes_count as u32, + lifetime_args_len as u32, + IncorrectGenericsLenKind::Lifetimes, + ); + had_error = true; + } + + let defaults_count = + def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count(); + let named_type_and_const_params_count = def_generics + .iter_self_type_or_consts() + .filter(|(_, param)| match param { + TypeOrConstParamData::TypeParamData(param) => { + param.provenance == TypeParamProvenance::TypeParamList + } + TypeOrConstParamData::ConstParamData(_) => true, + }) + .count(); + let expected_max = named_type_and_const_params_count; + let expected_min = + if infer_args { 0 } else { named_type_and_const_params_count - defaults_count }; + if provided_types_and_consts_count < expected_min + || expected_max < provided_types_and_consts_count + { + ctx.report_len_mismatch( + def, + provided_types_and_consts_count as u32, + named_type_and_const_params_count as u32, + IncorrectGenericsLenKind::TypesAndConsts, + ); + had_error = true; + } + + had_error +} + +pub(crate) fn substs_from_args_and_bindings<'db>( + db: &'db dyn HirDatabase, + store: &ExpressionStore, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + mut infer_args: bool, + lifetime_elision: LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + explicit_self_ty: Option>, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> crate::next_solver::GenericArgs<'db> { + let interner = DbInterner::new_with(db, None, None); + + tracing::debug!(?args_and_bindings); + + // Order is + // - Parent parameters + // - Optional Self parameter + // - Lifetime parameters + // - Type or Const parameters + let def_generics = generics(db, def); + let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default(); + + // We do not allow inference if there are specified args, i.e. we do not allow partial inference. + let has_non_lifetime_args = + args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); + infer_args &= !has_non_lifetime_args; + + let had_count_error = check_generic_args_len( + args_and_bindings, + def, + &def_generics, + infer_args, + &lifetime_elision, + lowering_assoc_type_generics, + ctx, + ); + + let mut substs = Vec::with_capacity(def_generics.len()); + + substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id))); + + let mut args = args_slice.iter().enumerate().peekable(); + let mut params = def_generics.iter_self().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type); + // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`, + // and lastly infer it. + if let Some(&( + self_param_id, + self_param @ GenericParamDataRef::TypeParamData(TypeParamData { + provenance: TypeParamProvenance::TraitSelf, + .. + }), + )) = params.peek() + { + let self_ty = if has_self_arg { + let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); + ctx.provided_kind(self_param_id, self_param, self_ty) + } else { + explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { + ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) + }) + }; + params.next(); + substs.push(self_ty); + } + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { + (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) + if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => + { + // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here + // we will handle it as if it was specified, instead of inferring it. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + } + (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { + substs.push(ctx.provided_kind(param_id, param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDataRef::LifetimeParamData(_), + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetime. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + force_infer_lt = Some((arg_idx as u32, param_id)); + } + (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + if let Some(konst) = type_looks_like_const(store, *type_ref) { + let GenericParamId::ConstParamId(param_id) = param_id else { + panic!("unmatching param kinds"); + }; + let const_ty = const_param_ty_query(db, param_id); + substs.push(ctx.provided_type_like_const(const_ty, konst).into()); + args.next(); + params.next(); + } else { + // See the `_ => { ... }` branch. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + while args.next().is_some() {} + } + } + _ => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + }, + + (Some(&(_, arg)), None) => { + // We should never be able to reach this point with well-formed input. + // There are two situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + if !had_count_error { + assert!( + matches!(arg, GenericArg::Lifetime(_)), + "the only possible situation here is incorrect lifetime order" + ); + let (provided_arg_idx, param_id) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg); + } + + break; + } + + (None, Some(&(param_id, param))) => { + // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. + let param = if let GenericParamId::LifetimeParamId(_) = param_id { + match &lifetime_elision { + LifetimeElisionKind::ElisionFailure + | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } + | LifetimeElisionKind::AnonymousReportError => { + assert!(had_count_error); + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + Region::new_static(interner).into() + } + LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } + | LifetimeElisionKind::Infer => { + // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here + // (but this will probably be done in hir-def lowering instead). + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + } + } else { + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + }; + substs.push(param); + params.next(); + } + + (None, None) => break, + } + } + + crate::next_solver::GenericArgs::new_from_iter(interner, substs) +} + +fn type_looks_like_const( + store: &ExpressionStore, + type_ref: TypeRefId, +) -> Option> { + // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering + // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently, + // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace + // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable + // in both the type and value namespaces, but I believe we only allow more code. + let type_ref = &store[type_ref]; + match type_ref { + TypeRef::Path(path) => Some(TypeLikeConst::Path(path)), + TypeRef::Placeholder => Some(TypeLikeConst::Infer), + _ => None, + } +} + +fn unknown_subst<'db>( + interner: DbInterner<'db>, + def: impl Into, +) -> crate::next_solver::GenericArgs<'db> { + let params = generics(interner.db(), def.into()); + crate::next_solver::GenericArgs::new_from_iter( + interner, + params.iter_id().map(|id| match id { + GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamId::ConstParamId(id) => { + unknown_const_as_generic(const_param_ty_query(interner.db(), id)) + } + GenericParamId::LifetimeParamId(_) => { + crate::next_solver::Region::error(interner).into() + } + }), + ) +} + +pub(crate) fn builtin<'db>(interner: DbInterner<'db>, builtin: BuiltinType) -> Ty<'db> { + match builtin { + BuiltinType::Char => Ty::new(interner, rustc_type_ir::TyKind::Char), + BuiltinType::Bool => Ty::new_bool(interner), + BuiltinType::Str => Ty::new(interner, rustc_type_ir::TyKind::Str), + BuiltinType::Int(t) => { + let int_ty = match primitive::int_ty_from_builtin(t) { + chalk_ir::IntTy::Isize => rustc_type_ir::IntTy::Isize, + chalk_ir::IntTy::I8 => rustc_type_ir::IntTy::I8, + chalk_ir::IntTy::I16 => rustc_type_ir::IntTy::I16, + chalk_ir::IntTy::I32 => rustc_type_ir::IntTy::I32, + chalk_ir::IntTy::I64 => rustc_type_ir::IntTy::I64, + chalk_ir::IntTy::I128 => rustc_type_ir::IntTy::I128, + }; + Ty::new_int(interner, int_ty) + } + BuiltinType::Uint(t) => { + let uint_ty = match primitive::uint_ty_from_builtin(t) { + chalk_ir::UintTy::Usize => rustc_type_ir::UintTy::Usize, + chalk_ir::UintTy::U8 => rustc_type_ir::UintTy::U8, + chalk_ir::UintTy::U16 => rustc_type_ir::UintTy::U16, + chalk_ir::UintTy::U32 => rustc_type_ir::UintTy::U32, + chalk_ir::UintTy::U64 => rustc_type_ir::UintTy::U64, + chalk_ir::UintTy::U128 => rustc_type_ir::UintTy::U128, + }; + Ty::new_uint(interner, uint_ty) + } + BuiltinType::Float(t) => { + let float_ty = match primitive::float_ty_from_builtin(t) { + chalk_ir::FloatTy::F16 => rustc_type_ir::FloatTy::F16, + chalk_ir::FloatTy::F32 => rustc_type_ir::FloatTy::F32, + chalk_ir::FloatTy::F64 => rustc_type_ir::FloatTy::F64, + chalk_ir::FloatTy::F128 => rustc_type_ir::FloatTy::F128, + }; + Ty::new_float(interner, float_ty) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index b22781e947013..49438151bbff5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -16,22 +16,24 @@ use hir_def::{ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, - VariableKind, WhereClause, + Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, error_lifetime, from_chalk_trait_id, from_foreign_def_id, infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, lang_items::is_box, + next_solver::SolverDefId, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, + traits::NextTraitSolveResult, utils::all_super_traits, }; @@ -43,6 +45,7 @@ pub enum TyFingerprint { Slice, Array, Never, + Ref(Mutability), RawPtr(Mutability), Scalar(Scalar), // These can have user-defined impls: @@ -88,7 +91,7 @@ impl TyFingerprint { TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, - TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), + TyKind::Ref(mutability, _, _) => TyFingerprint::Ref(*mutability), TyKind::Tuple(_, subst) => { let first_ty = subst.interned().first().map(|arg| arg.assert_ty_ref(Interner)); match first_ty { @@ -113,6 +116,94 @@ impl TyFingerprint { }; Some(fp) } + + /// Creates a TyFingerprint for looking up a trait impl. + pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option { + use rustc_type_ir::TyKind; + let fp = match (*ty).kind() { + TyKind::Str => TyFingerprint::Str, + TyKind::Never => TyFingerprint::Never, + TyKind::Slice(..) => TyFingerprint::Slice, + TyKind::Array(..) => TyFingerprint::Array, + TyKind::Int(int) => TyFingerprint::Scalar(Scalar::Int(match int { + rustc_type_ir::IntTy::Isize => IntTy::Isize, + rustc_type_ir::IntTy::I8 => IntTy::I8, + rustc_type_ir::IntTy::I16 => IntTy::I16, + rustc_type_ir::IntTy::I32 => IntTy::I32, + rustc_type_ir::IntTy::I64 => IntTy::I64, + rustc_type_ir::IntTy::I128 => IntTy::I128, + })), + TyKind::Uint(uint) => TyFingerprint::Scalar(Scalar::Uint(match uint { + rustc_type_ir::UintTy::Usize => UintTy::Usize, + rustc_type_ir::UintTy::U8 => UintTy::U8, + rustc_type_ir::UintTy::U16 => UintTy::U16, + rustc_type_ir::UintTy::U32 => UintTy::U32, + rustc_type_ir::UintTy::U64 => UintTy::U64, + rustc_type_ir::UintTy::U128 => UintTy::U128, + })), + TyKind::Float(float) => TyFingerprint::Scalar(Scalar::Float(match float { + rustc_type_ir::FloatTy::F16 => FloatTy::F16, + rustc_type_ir::FloatTy::F32 => FloatTy::F32, + rustc_type_ir::FloatTy::F64 => FloatTy::F64, + rustc_type_ir::FloatTy::F128 => FloatTy::F128, + })), + TyKind::Bool => TyFingerprint::Scalar(Scalar::Bool), + TyKind::Char => TyFingerprint::Scalar(Scalar::Char), + TyKind::Adt(def, _) => TyFingerprint::Adt(def.inner().id), + TyKind::RawPtr(.., mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), + }, + TyKind::Foreign(def) => { + let SolverDefId::ForeignId(def) = def else { unreachable!() }; + TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) + } + TyKind::Dynamic(bounds, _, _) => { + let trait_ref = bounds + .as_slice() + .iter() + .map(|b| (*b).skip_binder()) + .filter_map(|b| match b { + rustc_type_ir::ExistentialPredicate::Trait(t) => Some(t.def_id), + _ => None, + }) + .next()?; + let trait_id = match trait_ref { + SolverDefId::TraitId(id) => id, + _ => panic!("Bad GenericDefId in trait ref"), + }; + TyFingerprint::Dyn(trait_id) + } + TyKind::Ref(_, _, mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::Ref(Mutability::Not), + }, + TyKind::Tuple(tys) => { + let first_ty = tys.as_slice().iter().next(); + match first_ty { + Some(ty) => return TyFingerprint::for_trait_impl_ns(ty), + None => TyFingerprint::Unit, + } + } + TyKind::FnDef(_, _) + | TyKind::Closure(_, _) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Pat(..) + | TyKind::CoroutineClosure(..) => TyFingerprint::Unnameable, + TyKind::FnPtr(sig, _) => { + TyFingerprint::Function(sig.inputs().skip_binder().len() as u32) + } + TyKind::Alias(..) + | TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(_) + | TyKind::Error(_) + | TyKind::Param(..) + | TyKind::UnsafeBinder(..) => return None, + }; + Some(fp) + } } pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ @@ -807,10 +898,13 @@ fn find_matching_impl( let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs) .into_iter() - .map(|b| b.cast(Interner)); - let goal = crate::Goal::all(Interner, wcs); - table.try_obligation(goal.clone())?; - table.register_obligation(goal); + .map(|b| -> Goal { b.cast(Interner) }); + for goal in wcs { + if table.try_obligation(goal.clone()).no_solution() { + return None; + } + table.register_obligation(goal); + } Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) }) @@ -1313,7 +1407,7 @@ fn iterate_trait_method_candidates( }; if !known_implemented { let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); - if db.trait_solve(krate, block, goal.cast(Interner)).is_none() { + if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() { continue 'traits; } } @@ -1496,9 +1590,9 @@ pub(crate) fn resolve_indexing_op( let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); - if db + if !db .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) - .is_some() + .no_solution() { return Some(adj); } @@ -1692,7 +1786,7 @@ fn is_valid_impl_fn_candidate( ); match solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(canonical_subst) => { canonicalized.apply_solution( table, Canonical { @@ -1701,16 +1795,13 @@ fn is_valid_impl_fn_candidate( }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(table, substs); - } - Some(_) => (), - None => return IsValidCandidate::No, + NextTraitSolveResult::Uncertain(..) => {} + NextTraitSolveResult::NoSolution => return IsValidCandidate::No, } } for goal in goals { - if table.try_obligation(goal).is_none() { + if table.try_obligation(goal).no_solution() { return IsValidCandidate::No; } } @@ -1726,9 +1817,7 @@ pub fn implements_trait( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - solution.is_some() + !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution() } pub fn implements_trait_unique( @@ -1738,9 +1827,7 @@ pub fn implements_trait_unique( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - matches!(solution, Some(crate::Solution::Unique(_))) + db.trait_solve(env.krate, env.block, goal.cast(Interner)).certain() } /// This creates Substs for a trait with the given Self type and type variables diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index c1f86960e154c..eb5af58f2ea18 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -5,7 +5,9 @@ use syntax::{TextRange, TextSize}; use test_fixture::WithFixture; use crate::display::DisplayTarget; -use crate::{Interner, Substitution, db::HirDatabase, mir::MirLowerError, test_db::TestDB}; +use crate::{ + Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB, +}; use super::{MirEvalError, interpret_mir}; @@ -49,6 +51,7 @@ fn check_pass_and_stdio( expected_stdout: &str, expected_stderr: &str, ) { + let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); let x = eval_main(&db, file_id); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index eb80e8706fa0c..0bb8e6fe79d65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2163,7 +2163,9 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result = rustc_type_ir::Binder, T>; +pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder, T>; +pub type Canonical<'db, T> = rustc_type_ir::Canonical, T>; +pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues>; +pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind>; +pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput, V>; +pub type AliasTy<'db> = rustc_type_ir::AliasTy>; +pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; +pub type TypingMode<'db> = rustc_type_ir::TypingMode>; + +pub type FxIndexMap = + indexmap::IndexMap>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs new file mode 100644 index 0000000000000..80d1ea4aa4d00 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs @@ -0,0 +1,68 @@ +//! ABI-related things in the next-trait-solver. +use rustc_type_ir::{error::TypeError, relate::Relate}; + +use crate::FnAbi; + +use super::interner::DbInterner; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Safety { + Unsafe, + Safe, +} + +impl<'db> Relate> for Safety { + fn relate>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + if a != b { + Err(TypeError::SafetyMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } else { + Ok(a) + } + } +} + +impl<'db> rustc_type_ir::inherent::Safety> for Safety { + fn safe() -> Self { + Self::Safe + } + + fn is_safe(self) -> bool { + matches!(self, Safety::Safe) + } + + fn prefix_str(self) -> &'static str { + match self { + Self::Unsafe => "unsafe ", + Self::Safe => "", + } + } +} + +impl<'db> Relate> for FnAbi { + fn relate>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + if a == b { + Ok(a) + } else { + Err(TypeError::AbiMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } + } +} + +impl<'db> rustc_type_ir::inherent::Abi> for FnAbi { + fn rust() -> Self { + FnAbi::Rust + } + + fn is_rust(self) -> bool { + // TODO: rustc does not consider `RustCall` to be true here, but Chalk does + matches!(self, FnAbi::Rust | FnAbi::RustCall) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs new file mode 100644 index 0000000000000..5698ff290f748 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -0,0 +1,404 @@ +//! Things related to consts in the next-trait-solver. + +use std::hash::Hash; + +use intern::{Interned, Symbol}; +use rustc_ast_ir::try_visit; +use rustc_ast_ir::visit::VisitorResult; +use rustc_type_ir::{ + BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, WithCachedTypeInfo, + inherent::{IntoKind, PlaceholderLike}, + relate::Relate, +}; + +use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug}; + +use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; + +pub type ConstKind<'db> = rustc_type_ir::ConstKind>; +pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Const<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>, +} + +impl<'db> Const<'db> { + pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + let flags = FlagComputation::for_const_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Const::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) + } + + pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Param(param)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self { + Const::new(interner, ConstKind::Placeholder(placeholder)) + } + + pub fn is_ct_infer(&self) -> bool { + matches!(&self.inner().internee, ConstKind::Infer(_)) + } + + pub fn is_trivially_wf(self) -> bool { + match self.kind() { + ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true, + ConstKind::Infer(_) + | ConstKind::Unevaluated(..) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +pub type PlaceholderConst = Placeholder; + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct ParamConst { + pub index: u32, +} + +impl std::fmt::Debug for ParamConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct ValueConst<'db> { + pub(crate) ty: Ty<'db>, + pub(crate) value: Valtree<'db>, +} + +impl<'db> ValueConst<'db> { + pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self { + let value = Valtree::new(bytes); + ValueConst { ty, value } + } +} + +impl<'db> rustc_type_ir::inherent::ValueConst> for ValueConst<'db> { + fn ty(self) -> Ty<'db> { + self.ty + } + + fn valtree(self) -> Valtree<'db> { + self.value + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for ValueConst<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.ty.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for ValueConst<'db> { + fn fold_with>>(self, folder: &mut F) -> Self { + ValueConst { ty: self.ty.fold_with(folder), value: self.value } + } + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap); + +impl Hash for ConstBytes { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +#[salsa::interned(constructor = new_, debug)] +pub struct Valtree<'db> { + #[returns(ref)] + bytes_: ConstBytes, +} + +impl<'db> Valtree<'db> { + pub fn new(bytes: ConstBytes) -> Self { + salsa::with_attached_database(|db| unsafe { + // SAFETY: ¯\_(ツ)_/¯ + std::mem::transmute(Valtree::new_(db, bytes)) + }) + .unwrap() + } + + pub fn inner(&self) -> &ConstBytes { + salsa::with_attached_database(|db| { + let inner = self.bytes_(db); + // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ExprConst; + +impl rustc_type_ir::inherent::ParamLike for ParamConst { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> IntoKind for Const<'db> { + type Kind = ConstKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable> for Const<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_const(*self) + } +} + +impl<'db> TypeSuperVisitable> for Const<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self.kind() { + ConstKind::Unevaluated(uv) => uv.visit_with(visitor), + ConstKind::Value(v) => v.visit_with(visitor), + ConstKind::Expr(e) => e.visit_with(visitor), + ConstKind::Error(e) => e.visit_with(visitor), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable> for Const<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_const(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_const(self) + } +} + +impl<'db> TypeSuperFoldable> for Const<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), + ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), + ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return Ok(self), + }; + if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) } + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), + ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return self, + }; + if kind != self.kind() { Const::new(folder.cx(), kind) } else { self } + } +} + +impl<'db> Relate> for Const<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.consts(a, b) + } +} + +impl<'db> Flags for Const<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self { + Const::new(interner, ConstKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self { + Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var))) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_unevaluated( + interner: DbInterner<'db>, + uv: rustc_type_ir::UnevaluatedConst>, + ) -> Self { + Const::new(interner, ConstKind::Unevaluated(uv)) + } + + fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self { + Const::new(interner, ConstKind::Expr(expr)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Const::new(interner, ConstKind::Error(guar)) + } + + fn new_placeholder( + interner: DbInterner<'db>, + param: as rustc_type_ir::Interner>::PlaceholderConst, + ) -> Self { + Const::new(interner, ConstKind::Placeholder(param)) + } +} + +impl<'db> PlaceholderLike> for PlaceholderConst { + type Bound = rustc_type_ir::BoundVar; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } +} + +impl<'db> TypeVisitable> for ExprConst { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + // Ensure we get back to this when we fill in the fields + let ExprConst = &self; + V::Result::output() + } +} + +impl<'db> TypeFoldable> for ExprConst { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ExprConst) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ExprConst + } +} + +impl<'db> Relate> for ExprConst { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + // Ensure we get back to this when we fill in the fields + let ExprConst = b; + Ok(a) + } +} + +impl<'db> rustc_type_ir::inherent::ExprConst> for ExprConst { + fn args(self) -> as rustc_type_ir::Interner>::GenericArgs { + // Ensure we get back to this when we fill in the fields + let ExprConst = self; + GenericArgs::default() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs new file mode 100644 index 0000000000000..cfbc10e740e4b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -0,0 +1,96 @@ +//! Definition of `SolverDefId` + +use hir_def::{ + AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, + TraitAliasId, TraitId, TypeAliasId, UnionId, +}; +use rustc_type_ir::inherent; +use stdx::impl_from; + +use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}; + +use super::DbInterner; + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum Ctor { + Struct(StructId), + Enum(EnumVariantId), +} + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum SolverDefId { + AdtId(AdtId), + ConstId(ConstId), + FunctionId(FunctionId), + ImplId(ImplId), + StaticId(StaticId), + TraitAliasId(TraitAliasId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), + ForeignId(TypeAliasId), + InternedClosureId(InternedClosureId), + InternedCoroutineId(InternedCoroutineId), + InternedOpaqueTyId(InternedOpaqueTyId), + Ctor(Ctor), +} + +impl_from!( + AdtId(StructId, EnumId, UnionId), + ConstId, + FunctionId, + ImplId, + StaticId, + TraitAliasId, + TraitId, + TypeAliasId, + InternedClosureId, + InternedCoroutineId, + InternedOpaqueTyId + for SolverDefId +); + +impl From for SolverDefId { + fn from(value: GenericDefId) -> Self { + match value { + GenericDefId::AdtId(adt_id) => SolverDefId::AdtId(adt_id), + GenericDefId::ConstId(const_id) => SolverDefId::ConstId(const_id), + GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id), + GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id), + GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id), + GenericDefId::TraitAliasId(trait_alias_id) => SolverDefId::TraitAliasId(trait_alias_id), + GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id), + GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id), + } + } +} + +impl TryFrom for GenericDefId { + type Error = SolverDefId; + + fn try_from(value: SolverDefId) -> Result { + Ok(match value { + SolverDefId::AdtId(adt_id) => GenericDefId::AdtId(adt_id), + SolverDefId::ConstId(const_id) => GenericDefId::ConstId(const_id), + SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id), + SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id), + SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), + SolverDefId::TraitAliasId(trait_alias_id) => GenericDefId::TraitAliasId(trait_alias_id), + SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), + SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), + SolverDefId::ForeignId(_) => return Err(value), + SolverDefId::InternedClosureId(_) => return Err(value), + SolverDefId::InternedCoroutineId(_) => return Err(value), + SolverDefId::InternedOpaqueTyId(_) => return Err(value), + SolverDefId::Ctor(_) => return Err(value), + }) + } +} + +impl<'db> inherent::DefId> for SolverDefId { + fn as_local(self) -> Option { + Some(self) + } + fn is_local(self) -> bool { + true + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs new file mode 100644 index 0000000000000..3cc1e64b6add0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -0,0 +1,129 @@ +//! Fold impls for the next-trait-solver. + +use rustc_type_ir::{ + BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, + inherent::{IntoKind, Region as _}, +}; + +use super::{ + Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, +}; + +/// A delegate used when instantiating bound vars. +/// +/// Any implementation must make sure that each bound variable always +/// gets mapped to the same result. `BoundVarReplacer` caches by using +/// a `DelayedMap` which does not cache the first few types it encounters. +pub trait BoundVarReplacerDelegate<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; + fn replace_const(&mut self, bv: BoundVar) -> Const<'db>; +} + +/// A simple delegate taking 3 mutable functions. The used functions must +/// always return the same result for each bound variable, no matter how +/// frequently they are called. +pub struct FnMutDelegate<'db, 'a> { + pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), + pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a), +} + +impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + (self.regions)(br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + (self.types)(bt) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + (self.consts)(bv) + } +} + +/// Replaces the escaping bound vars (late bound regions or bound types) in a type. +pub(crate) struct BoundVarReplacer<'db, D> { + interner: DbInterner<'db>, + /// As with `RegionFolder`, represents the index of a binder *just outside* + /// the ones we have visited. + current_index: DebruijnIndex, + + delegate: D, +} + +impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> { + pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self { + BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate } + } +} + +impl<'db, D> TypeFolder> for BoundVarReplacer<'db, D> +where + D: BoundVarReplacerDelegate<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + let ty = self.delegate.replace_ty(bound_ty); + debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32()) + } + _ => { + if !t.has_vars_bound_at_or_above(self.current_index) { + t + } else { + t.super_fold_with(self) + } + } + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => { + let region = self.delegate.replace_region(br); + if let RegionKind::ReBound(debruijn1, br) = region.kind() { + // If the callback returns a bound region, + // that region should always use the INNERMOST + // debruijn index. Then we adjust it to the + // correct depth. + assert_eq!(debruijn1, DebruijnIndex::ZERO); + Region::new_bound(self.interner, debruijn, br) + } else { + region + } + } + _ => r, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { + let ct = self.delegate.replace_const(bound_const); + debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs new file mode 100644 index 0000000000000..007a674ad399a --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -0,0 +1,229 @@ +//! Fulfill loop for next-solver. + +use std::marker::PhantomData; +use std::mem; +use std::ops::ControlFlow; +use std::vec::ExtractIf; + +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_next_trait_solver::solve::{ + GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt, +}; +use rustc_type_ir::Interner; +use rustc_type_ir::inherent::Span as _; +use rustc_type_ir::solve::{Certainty, NoSolution}; + +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations}; +use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode}; + +type PendingObligations<'db> = + Vec<(PredicateObligation<'db>, Option>>)>; + +/// A trait engine using the new trait solver. +/// +/// This is mostly identical to how `evaluate_all` works inside of the +/// solver, except that the requirements are slightly different. +/// +/// Unlike `evaluate_all` it is possible to add new obligations later on +/// and we also have to track diagnostics information by using `Obligation` +/// instead of `Goal`. +/// +/// It is also likely that we want to use slightly different datastructures +/// here as this will have to deal with far more root goals than `evaluate_all`. +pub struct FulfillmentCtxt<'db> { + obligations: ObligationStorage<'db>, + + /// The snapshot in which this context was created. Using the context + /// outside of this snapshot leads to subtle bugs if the snapshot + /// gets rolled back. Because of this we explicitly check that we only + /// use the context in exactly this snapshot. + usable_in_snapshot: usize, +} + +#[derive(Default, Debug)] +struct ObligationStorage<'db> { + /// Obligations which resulted in an overflow in fulfillment itself. + /// + /// We cannot eagerly return these as error so we instead store them here + /// to avoid recomputing them each time `select_where_possible` is called. + /// This also allows us to return the correct `FulfillmentError` for them. + overflowed: Vec>, + pending: PendingObligations<'db>, +} + +impl<'db> ObligationStorage<'db> { + fn register( + &mut self, + obligation: PredicateObligation<'db>, + stalled_on: Option>>, + ) { + self.pending.push((obligation, stalled_on)); + } + + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + + fn clone_pending(&self) -> PredicateObligations<'db> { + let mut obligations: PredicateObligations<'db> = + self.pending.iter().map(|(o, _)| o.clone()).collect(); + obligations.extend(self.overflowed.iter().cloned()); + obligations + } + + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'db>) -> bool, + ) -> PendingObligations<'db> { + let (not_stalled, pending) = + mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o)); + self.pending = pending; + not_stalled + } + + fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'db>) { + infcx.probe(|_| { + // IMPORTANT: we must not use solve any inference variables in the obligations + // as this is all happening inside of a probe. We use a probe to make sure + // we get all obligations involved in the overflow. We pretty much check: if + // we were to do another step of `select_where_possible`, which goals would + // change. + // FIXME: is merged, this can be removed. + self.overflowed.extend( + self.pending + .extract_if(.., |(o, stalled_on)| { + let goal = o.as_goal(); + let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + goal, + Span::dummy(), + stalled_on.take(), + ); + matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) + }) + .map(|(o, _)| o), + ); + }) + } +} + +impl<'db> FulfillmentCtxt<'db> { + pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> { + FulfillmentCtxt { + obligations: Default::default(), + usable_in_snapshot: infcx.num_open_snapshots(), + } + } +} + +impl<'db> FulfillmentCtxt<'db> { + #[tracing::instrument(level = "trace", skip(self, infcx))] + pub(crate) fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'db>, + obligation: PredicateObligation<'db>, + ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.obligations.register(obligation, None); + } + + pub(crate) fn collect_remaining_errors( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + self.obligations + .pending + .drain(..) + .map(|(obligation, _)| NextSolverError::Ambiguity(obligation)) + .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow)) + .collect() + } + + pub(crate) fn select_where_possible( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + let mut errors = Vec::new(); + loop { + let mut any_changed = false; + for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { + if obligation.recursion_depth >= infcx.interner.recursion_limit() { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + + let goal = obligation.as_goal(); + let delegate = <&SolverContext<'db>>::from(infcx); + if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.obligations.register(obligation, None); + } + } + continue; + } + + let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); + let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + Ok(result) => result, + Err(NoSolution) => { + errors.push(NextSolverError::TrueError(obligation)); + continue; + } + }; + + if has_changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; + any_changed = true; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + } + } + + if !any_changed { + break; + } + } + + errors + } + + pub(crate) fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + self.collect_remaining_errors(infcx) + } + + fn has_pending_obligations(&self) -> bool { + self.obligations.has_pending_obligations() + } + + fn pending_obligations(&self) -> PredicateObligations<'db> { + self.obligations.clone_pending() + } +} + +#[derive(Debug)] +pub enum NextSolverError<'db> { + TrueError(PredicateObligation<'db>), + Ambiguity(PredicateObligation<'db>), + Overflow(PredicateObligation<'db>), +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs new file mode 100644 index 0000000000000..85a79923a7295 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -0,0 +1,525 @@ +//! Things related to generic args in the next-trait-solver. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, + GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, + Variance, + inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, + relate::{Relate, VarianceDiagInfo}, +}; +use smallvec::SmallVec; + +use crate::db::HirDatabase; + +use super::{ + Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, + generics::{GenericParamDef, GenericParamDefKind, Generics}, + interned_vec_db, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg<'db> { + Ty(Ty<'db>), + Lifetime(Region<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for GenericArg<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Lifetime(r) => std::fmt::Debug::fmt(r, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Term<'db>) -> Self { + match value { + Term::Ty(ty) => GenericArg::Ty(ty), + Term::Const(c) => GenericArg::Const(c), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum Term<'db> { + Ty(Ty<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for Term<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> Term<'db> { + pub fn expect_type(&self) -> Ty<'db> { + self.as_type().expect("expected a type, but found a const") + } + + pub fn is_trivially_wf(&self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TermKind::Ty(ty) => ty.is_trivially_wf(tcx), + TermKind::Const(ct) => ct.is_trivially_wf(), + } + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Region<'db>) -> Self { + Self::Lifetime(value) + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> IntoKind for GenericArg<'db> { + type Kind = GenericArgKind>; + + fn kind(self) -> Self::Kind { + match self { + GenericArg::Ty(ty) => GenericArgKind::Type(ty), + GenericArg::Lifetime(region) => GenericArgKind::Lifetime(region), + GenericArg::Const(c) => GenericArgKind::Const(c), + } + } +} + +impl<'db> TypeVisitable> for GenericArg<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + GenericArg::Lifetime(lt) => lt.visit_with(visitor), + GenericArg::Ty(ty) => ty.visit_with(visitor), + GenericArg::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable> for GenericArg<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), + GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), + GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with>>(self, folder: &mut F) -> Self { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), + GenericArgKind::Type(ty) => ty.fold_with(folder).into(), + GenericArgKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate> for GenericArg<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + Ok(relation.relate(a_lt, b_lt)?.into()) + } + (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + Ok(relation.relate(a_ty, b_ty)?.into()) + } + (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (GenericArgKind::Lifetime(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Type(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +interned_vec_db!(GenericArgs, GenericArg); + +impl<'db> rustc_type_ir::inherent::GenericArg> for GenericArg<'db> {} + +impl<'db> GenericArgs<'db> { + /// Creates an `GenericArgs` for generic parameter definitions, + /// by calling closures to obtain each kind. + /// The closures get to observe the `GenericArgs` as they're + /// being built, which can be used to correctly + /// replace defaults of generic parameters. + pub fn for_item( + interner: DbInterner<'db>, + def_id: SolverDefId, + mut mk_kind: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let defs = interner.generics_of(def_id); + let count = defs.count(); + let mut args = SmallVec::with_capacity(count); + Self::fill_item(&mut args, interner, defs, &mut mk_kind); + interner.mk_args(&args) + } + + fn fill_item( + args: &mut SmallVec<[GenericArg<'db>; 8]>, + interner: DbInterner<'_>, + defs: Generics, + mk_kind: &mut F, + ) where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let self_len = defs.own_params.len() as u32; + if let Some(def_id) = defs.parent { + let parent_defs = interner.generics_of(def_id.into()); + Self::fill_item(args, interner, parent_defs, mk_kind); + } + Self::fill_single(args, &defs, mk_kind); + } + + fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let start_len = args.len(); + args.reserve(defs.own_params.len()); + for param in &defs.own_params { + let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args); + args.push(kind); + } + } +} + +impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let interner = relation.cx(); + CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + a, + b, + ) + }), + |g| GenericArgs::new_from_iter(interner, g.iter().cloned()), + ) + } +} + +impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs<'db> { + fn as_closure(self) -> ClosureArgs> { + ClosureArgs { args: self } + } + fn as_coroutine(self) -> CoroutineArgs> { + CoroutineArgs { args: self } + } + fn as_coroutine_closure(self) -> CoroutineClosureArgs> { + CoroutineClosureArgs { args: self } + } + fn rebase_onto( + self, + interner: DbInterner<'db>, + source_def_id: as rustc_type_ir::Interner>::DefId, + target: as rustc_type_ir::Interner>::GenericArgs, + ) -> as rustc_type_ir::Interner>::GenericArgs { + let defs = interner.generics_of(source_def_id); + interner.mk_args_from_iter(target.iter().chain(self.iter().skip(defs.count()))) + } + + fn identity_for_item( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + ) -> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind)) + } + + fn extend_with_error( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + original_args: &[ as rustc_type_ir::Interner>::GenericArg], + ) -> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| { + if let Some(arg) = original_args.get(index as usize) { + *arg + } else { + error_for_param_kind(kind, interner) + } + }) + } + fn type_at(self, i: usize) -> as rustc_type_ir::Interner>::Ty { + self.inner() + .get(i) + .and_then(|g| g.as_type()) + .unwrap_or_else(|| Ty::new_error(DbInterner::conjure(), ErrorGuaranteed)) + } + + fn region_at(self, i: usize) -> as rustc_type_ir::Interner>::Region { + self.inner() + .get(i) + .and_then(|g| g.as_region()) + .unwrap_or_else(|| Region::error(DbInterner::conjure())) + } + + fn const_at(self, i: usize) -> as rustc_type_ir::Interner>::Const { + self.inner() + .get(i) + .and_then(|g| g.as_const()) + .unwrap_or_else(|| Const::error(DbInterner::conjure())) + } + + fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts> { + // FIXME: should use `ClosureSubst` when possible + match self.inner().as_slice() { + [parent_args @ .., sig_ty] => { + let interner = DbInterner::conjure(); + // This is stupid, but the next solver expects the first input to actually be a tuple + let sig_ty = match sig_ty.expect_ty().kind() { + TyKind::FnPtr(sig_tys, header) => Ty::new( + interner, + TyKind::FnPtr( + sig_tys.map_bound(|s| { + let inputs = Ty::new_tup_from_iter(interner, s.inputs().iter()); + let output = s.output(); + FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + [inputs, output], + ), + } + }), + header, + ), + ), + _ => unreachable!("sig_ty should be last"), + }; + rustc_type_ir::ClosureArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + closure_sig_as_fn_ptr_ty: sig_ty, + closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => { + unreachable!("unexpected closure sig"); + } + } + } + + fn split_coroutine_closure_args( + self, + ) -> rustc_type_ir::CoroutineClosureArgsParts> { + match self.inner().as_slice() { + [ + parent_args @ .., + closure_kind_ty, + signature_parts_ty, + tupled_upvars_ty, + coroutine_captures_by_ref_ty, + coroutine_witness_ty, + ] => rustc_type_ir::CoroutineClosureArgsParts { + parent_args: GenericArgs::new_from_iter( + DbInterner::conjure(), + parent_args.iter().cloned(), + ), + closure_kind_ty: closure_kind_ty.expect_ty(), + signature_parts_ty: signature_parts_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), + coroutine_witness_ty: coroutine_witness_ty.expect_ty(), + }, + _ => panic!("GenericArgs were likely not for a CoroutineClosure."), + } + } + + fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts> { + let interner = DbInterner::conjure(); + match self.inner().as_slice() { + [parent_args @ .., resume_ty, yield_ty, return_ty] => { + rustc_type_ir::CoroutineArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + kind_ty: Ty::new_unit(interner), + resume_ty: resume_ty.expect_ty(), + yield_ty: yield_ty.expect_ty(), + return_ty: return_ty.expect_ty(), + witness: Ty::new_unit(interner), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => panic!("GenericArgs were likely not for a Coroutine."), + } + } +} + +pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> { + let name = name.clone(); + match kind { + GenericParamDefKind::Lifetime => { + Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into() + } + GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(), + GenericParamDefKind::Const => { + Const::new_param(DbInterner::conjure(), ParamConst { index }).into() + } + } +} + +pub fn error_for_param_kind<'db>( + kind: GenericParamDefKind, + interner: DbInterner<'db>, +) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => Region::error(interner).into(), + GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamDefKind::Const => Const::error(interner).into(), + } +} + +impl<'db> IntoKind for Term<'db> { + type Kind = TermKind>; + + fn kind(self) -> Self::Kind { + match self { + Term::Ty(ty) => TermKind::Ty(ty), + Term::Const(c) => TermKind::Const(c), + } + } +} + +impl<'db> From> for Term<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From> for Term<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> TypeVisitable> for Term<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + Term::Ty(ty) => ty.visit_with(visitor), + Term::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable> for Term<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + match self.kind() { + TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), + TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with>>(self, folder: &mut F) -> Self { + match self.kind() { + TermKind::Ty(ty) => ty.fold_with(folder).into(), + TermKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate> for Term<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + match (a.kind(), b.kind()) { + (TermKind::Ty(a_ty), TermKind::Ty(b_ty)) => Ok(relation.relate(a_ty, b_ty)?.into()), + (TermKind::Const(a_ct), TermKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (TermKind::Ty(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (TermKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +impl<'db> rustc_type_ir::inherent::Term> for Term<'db> {} + +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum TermVid { + Ty(TyVid), + Const(ConstVid), +} + +impl From for TermVid { + fn from(value: TyVid) -> Self { + TermVid::Ty(value) + } +} + +impl From for TermVid { + fn from(value: ConstVid) -> Self { + TermVid::Const(value) + } +} + +impl<'db> DbInterner<'db> { + pub(super) fn mk_args(self, args: &[GenericArg<'db>]) -> GenericArgs<'db> { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + pub(super) fn mk_args_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, GenericArgs<'db>>, + { + T::collect_and_apply(iter, |xs| self.mk_args(xs)) + } + + pub(super) fn check_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) -> bool { + // TODO + true + } + + pub(super) fn debug_assert_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) { + // TODO + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs new file mode 100644 index 0000000000000..48f5e73f2562d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -0,0 +1,147 @@ +//! Things related to generics in the next-trait-solver. + +use hir_def::{ + ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, + TypeOrConstParamId, TypeParamId, + db::DefDatabase, + expr_store::ExpressionStore, + hir::generics::{ + GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, + LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + WherePredicate, + }, +}; +use hir_expand::name::Name; +use intern::Symbol; +use la_arena::Arena; +use rustc_type_ir::inherent::Ty as _; +use triomphe::Arc; + +use crate::{db::HirDatabase, generics::parent_generic_def, next_solver::Ty}; + +use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId}; + +use super::{DbInterner, GenericArg}; + +pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { + let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| { + let name = lt.name.symbol().clone(); + let index = index as u32; + let kind = GenericParamDefKind::Lifetime; + GenericParamDef { name, index, kind } + }; + let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| { + let name = p + .name() + .map(|n| n.symbol().clone()) + .unwrap_or_else(|| Name::missing().symbol().clone()); + let index = (len_lt + index) as u32; + let kind = match p { + TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, + TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, + }; + GenericParamDef { name, index, kind } + }; + let own_params_for_generic_params = |params: &GenericParams| { + if params.trait_self_param().is_some() { + let len_lt = params.len_lifetimes() + 1; + params + .iter_type_or_consts() + .take(1) + .enumerate() + .map(|t| mk_ty(0, (t.0, t.1.1))) + .chain(params.iter_lt().enumerate().map(mk_lt)) + .chain( + params + .iter_type_or_consts() + .skip(1) + .enumerate() + .map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } else { + let len_lt = params.len_lifetimes(); + params + .iter_lt() + .enumerate() + .map(mk_lt) + .chain( + params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } + }; + + let (parent, own_params) = match (def.try_into(), def) { + (Ok(def), _) => { + (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(def))) + } + (_, SolverDefId::InternedOpaqueTyId(id)) => { + match db.lookup_intern_impl_trait_id(id) { + crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { + // The opaque type itself does not have generics - only the parent function + (Some(GenericDefId::FunctionId(function_id)), vec![]) + } + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => ( + None, + own_params_for_generic_params( + &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)), + ), + ), + crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + let param = TypeOrConstParamData::TypeParamData(TypeParamData { + name: None, + default: None, + provenance: TypeParamProvenance::TypeParamList, + }); + // Yes, there is a parent but we don't include it in the generics + (None, vec![mk_ty(0, (0, ¶m))]) + } + } + } + _ => panic!("No generics for {def:?}"), + }; + let parent_generics = parent.map(|def| Box::new(generics(db, def.into()))); + + Generics { + parent, + parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()), + own_params, + } +} + +#[derive(Debug)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub own_params: Vec, +} + +#[derive(Debug)] +pub struct GenericParamDef { + pub(crate) name: Symbol, + //def_id: GenericDefId, + index: u32, + pub(crate) kind: GenericParamDefKind, +} + +impl GenericParamDef { + /// Returns the index of the param on the self generics only + /// (i.e. not including parent generics) + pub fn index(&self) -> u32 { + self.index + } +} + +#[derive(Copy, Clone, Debug)] +pub enum GenericParamDefKind { + Lifetime, + Type, + Const, +} + +impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { + fn count(&self) -> usize { + self.parent_count + self.own_params.len() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs new file mode 100644 index 0000000000000..d64c7ed626eb2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -0,0 +1,333 @@ +//! A nice interface for working with the infcx. The basic idea is to +//! do `infcx.at(cause, param_env)`, which sets the "cause" of the +//! operation as well as the surrounding parameter environment. Then +//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a +//! subtype or equality relationship respectively. The first argument +//! is always the "expected" output from the POV of diagnostics. +//! +//! Examples: +//! ```ignore (fragment) +//! infcx.at(cause, param_env).sub(a, b) +//! // requires that `a <: b`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).sup(a, b) +//! // requires that `b <: a`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).eq(a, b) +//! // requires that `a == b`, with `a` considered the "expected" type +//! ``` +//! For finer-grained control, you can also do use `trace`: +//! ```ignore (fragment) +//! infcx.at(...).trace(a, b).sub(&c, &d) +//! ``` +//! This will set `a` and `b` as the "root" values for +//! error-reporting, but actually operate on `c` and `d`. This is +//! sometimes useful when the types of `c` and `d` are not traceable +//! things. (That system should probably be refactored.) + +use rustc_type_ir::{ + FnSig, GenericArgKind, TypingMode, Variance, + error::ExpectedFound, + inherent::{IntoKind, Span as _}, + relate::{Relate, TypeRelation, solver_relating::RelateExt}, +}; + +use crate::next_solver::{ + AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, + TraitRef, Ty, +}; + +use super::{ + InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs, + traits::{Obligation, ObligationCause}, +}; + +/// Whether we should define opaque types or just treat them opaquely. +/// +/// Currently only used to prevent predicate matching from matching anything +/// against opaque types. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DefineOpaqueTypes { + Yes, + No, +} + +#[derive(Clone, Copy)] +pub struct At<'a, 'db> { + pub infcx: &'a InferCtxt<'db>, + pub cause: &'a ObligationCause, + pub param_env: ParamEnv<'db>, +} + +impl<'db> InferCtxt<'db> { + #[inline] + pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> { + At { infcx: self, cause, param_env } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state. This can be used to "branch off" many tests from the same + /// common state. + pub fn fork(&self) -> Self { + Self { + interner: self.interner, + typing_mode: self.typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state, except possibly changing the intercrate mode. This can be + /// used to "branch off" many tests from the same common state. Used in negative coherence. + pub fn fork_with_typing_mode(&self, typing_mode: TypingMode>) -> Self { + // Unlike `fork`, this invalidates all cache entries as they may depend on the + // typing mode. + + Self { + interner: self.interner, + typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } +} + +pub trait ToTrace<'db>: Relate> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>; +} + +impl<'a, 'db> At<'a, 'db> { + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + pub fn sup( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Contravariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected <: actual`. + pub fn sub( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Covariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected == actual`. + pub fn eq( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + self.eq_trace( + define_opaque_types, + ToTrace::to_trace(self.cause, expected, actual), + expected, + actual, + ) + } + + /// Makes `expected == actual`. + pub fn eq_trace( + self, + define_opaque_types: DefineOpaqueTypes, + trace: TypeTrace<'db>, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: Relate>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Invariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + pub fn relate( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + variance: Variance, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + match variance { + Variance::Covariant => self.sub(define_opaque_types, expected, actual), + Variance::Invariant => self.eq(define_opaque_types, expected, actual), + Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + + // We could make this make sense but it's not readily + // exposed and I don't feel like dealing with it. Note + // that bivariance in general does a bit more than just + // *nothing*, it checks that the types are the same + // "modulo variance" basically. + Variance::Bivariant => panic!("Bivariant given to `relate()`"), + } + } + + fn goals_to_obligations(&self, goals: Vec>>) -> InferOk<'db, ()> { + InferOk { + value: (), + obligations: goals + .into_iter() + .map(|goal| { + Obligation::new( + self.infcx.interner, + self.cause.clone(), + goal.param_env, + goal.predicate, + ) + }) + .collect(), + } + } +} + +impl<'db> ToTrace<'db> for Ty<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for Region<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for Const<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for GenericArg<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { + ValuePairs::Regions(ExpectedFound::new(a, b)) + } + (GenericArgKind::Type(a), GenericArgKind::Type(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + _ => panic!("relating different kinds: {a:?} {b:?}"), + }, + } + } +} + +impl<'db> ToTrace<'db> for Term<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for TraitRef<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for AliasTy<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for AliasTerm<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for FnSig> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))), + } + } +} + +impl<'db> ToTrace<'db> for PolyFnSig<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)), + } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)), + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs new file mode 100644 index 0000000000000..0448f03463e4b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -0,0 +1,106 @@ +//! This module contains code to instantiate new values into a +//! `Canonical<'tcx, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundVar, GenericArgKind, InferTy, TypeFoldable, Upcast, + Variance, + inherent::{IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub trait CanonicalExt<'db, V> { + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable>; + fn instantiate_projected( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable>; +} + +/// FIXME(-Znext-solver): This or public because it is shared with the +/// new trait solver implementation. We should deduplicate canonicalization. +impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable>, + { + self.instantiate_projected(tcx, var_values, |value| value.clone()) + } + + /// Allows one to apply a instantiation to some subset of + /// `self.value`. Invoke `projection_fn` with `self.value` to get + /// a value V that is expressed in terms of the same canonical + /// variables bound in `self` (usually this extracts from subset + /// of `self`). Apply the instantiation `var_values` to this value + /// V, replacing each of the canonical variables. + fn instantiate_projected( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable>, + { + assert_eq!(self.variables.len(), var_values.len()); + let value = projection_fn(&self.value); + instantiate_value(tcx, var_values, value) + } +} + +/// Instantiate the values from `var_values` into `value`. `var_values` +/// must be values for the set of canonical variables that appear in +/// `value`. +pub(super) fn instantiate_value<'db, T>( + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + value: T, +) -> T +where + T: TypeFoldable>, +{ + if var_values.var_values.is_empty() { + value + } else { + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| match var_values[br.var].kind() { + GenericArgKind::Lifetime(l) => l, + r => panic!("{br:?} is a region but value is {r:?}"), + }, + types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() { + GenericArgKind::Type(ty) => ty, + r => panic!("{bound_ty:?} is a type but value is {r:?}"), + }, + consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() { + GenericArgKind::Const(ct) => ct, + c => panic!("{bound_ct:?} is a const but value is {c:?}"), + }, + }; + + tcx.replace_escaping_bound_vars_uncached(value, delegate) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs new file mode 100644 index 0000000000000..4457e1340d584 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -0,0 +1,156 @@ +//! **Canonicalization** is the key to constructing a query in the +//! middle of type inference. Ordinarily, it is not possible to store +//! types from type inference in query keys, because they contain +//! references to inference variables whose lifetimes are too short +//! and so forth. Canonicalizing a value T1 using `canonicalize_query` +//! produces two things: +//! +//! - a value T2 where each unbound inference variable has been +//! replaced with a **canonical variable**; +//! - a map M (of type `CanonicalVarValues`) from those canonical +//! variables back to the original. +//! +//! We can then do queries using T2. These will give back constraints +//! on the canonical variables which can be translated, using the map +//! M, into constraints in our source context. This process of +//! translating the results back is done by the +//! `instantiate_query_result` method. +//! +//! For a more detailed look at what is happening here, check +//! out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, Canonical, CanonicalVarValues, CanonicalVars, Const, DbInterner, GenericArg, + Goal, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, Ty, TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use instantiate::CanonicalExt; +use rustc_index::IndexVec; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy, + TypeFoldable, UniverseIndex, Upcast, Variance, + inherent::{SliceLike, Ty as _}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub mod instantiate; + +impl<'db> InferCtxt<'db> { + /// Creates an instantiation S for the canonical value with fresh inference + /// variables and placeholders then applies it to the canonical value. + /// Returns both the instantiated result *and* the instantiation S. + /// + /// This can be invoked as part of constructing an + /// inference context at the start of a query (see + /// `InferCtxtBuilder::build_with_canonical`). It basically + /// brings the canonical value "into scope" within your new infcx. + /// + /// At the end of processing, the instantiation S (once + /// canonicalized) then represents the values that you computed + /// for each of the canonical inputs to your query. + pub fn instantiate_canonical( + &self, + canonical: &Canonical<'db, T>, + ) -> (T, CanonicalVarValues<'db>) + where + T: TypeFoldable>, + { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + // + // Instantiate the root-universe content into the current universe, + // and create fresh universes for the higher universes. + let universes: IndexVec = std::iter::once(self.universe()) + .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + + let canonical_inference_vars = + self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]); + let result = canonical.instantiate(self.interner, &canonical_inference_vars); + (result, canonical_inference_vars) + } + + /// Given the "infos" about the canonical variables from some + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `instantiate` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( + &self, + variables: CanonicalVars<'db>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> CanonicalVarValues<'db> { + CanonicalVarValues { + var_values: self.interner.mk_args_from_iter( + variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)), + ), + } + } + + /// Given the "info" about a canonical variable, creates a fresh + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + /// + /// FIXME(-Znext-solver): This is public because it's used by the + /// new trait solver which has a different canonicalization routine. + /// We should somehow deduplicate all of this. + pub fn instantiate_canonical_var( + &self, + cv_info: CanonicalVarKind>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> GenericArg<'db> { + match cv_info { + CanonicalVarKind::Ty(ty_kind) => { + let ty = match ty_kind { + CanonicalTyVarKind::General(ui) => { + self.next_ty_var_in_universe(universe_map(ui)) + } + + CanonicalTyVarKind::Int => self.next_int_var(), + + CanonicalTyVarKind::Float => self.next_float_var(), + }; + ty.into() + } + + CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound }; + Ty::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Region(ui) => { + self.next_region_var_in_universe(universe_map(ui)).into() + } + + CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped: crate::next_solver::Placeholder< + crate::next_solver::BoundRegion, + > = PlaceholderRegion { universe: universe_mapped, bound }; + Region::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), + CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound }; + Const::new_placeholder(self.interner, placeholder_mapped).into() + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs new file mode 100644 index 0000000000000..93fd6eeab34e4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -0,0 +1,316 @@ +//! Definition of `InferCtxtLike` from the librarified type layer. + +use rustc_type_ir::{ + ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntTy, IntVarValue, + IntVid, RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, + inherent::{Const as _, IntoKind, Span as _, Ty as _}, + relate::combine::PredicateEmittingRelation, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, ParamEnv, + Region, SolverDefId, Span, Ty, TyKind, + infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, +}; + +use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult, traits::ObligationCause}; + +impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { + type Interner = DbInterner<'db>; + + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn next_trait_solver(&self) -> bool { + true + } + + fn typing_mode(&self) -> TypingMode> { + self.typing_mode() + } + + fn universe(&self) -> UniverseIndex { + self.universe() + } + + fn create_next_universe(&self) -> UniverseIndex { + self.create_next_universe() + } + + fn universe_of_ty(&self, vid: TyVid) -> Option { + self.probe_ty_var(vid).err() + } + + fn universe_of_lt(&self, lt: RegionVid) -> Option { + self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt).err() + } + + fn universe_of_ct(&self, ct: ConstVid) -> Option { + self.probe_const_var(ct).err() + } + + fn root_ty_var(&self, var: TyVid) -> TyVid { + self.root_var(var) + } + + fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.root_const_var(var) + } + + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'db> { + match self.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.interner, self.root_var(vid)), + } + } + + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + self.opportunistic_resolve_int_var(vid) + } + + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + self.opportunistic_resolve_float_var(vid) + } + + fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> Const<'db> { + match self.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => Const::new_var(self.interner, self.root_const_var(vid)), + } + } + + fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> Region<'db> { + self.inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.interner, vid) + } + + fn is_changed_arg(&self, arg: ::GenericArg) -> bool { + match arg.kind() { + GenericArgKind::Lifetime(_) => { + // Lifetimes should not change affect trait selection. + false + } + GenericArgKind::Type(ty) => { + if let TyKind::Infer(infer_ty) = ty.kind() { + match infer_ty { + InferTy::TyVar(vid) => { + !self.probe_ty_var(vid).is_err_and(|_| self.root_var(vid) == vid) + } + InferTy::IntVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.int_unification_table().probe_value(vid), + IntVarValue::Unknown + if inner.int_unification_table().find(vid) == vid + ) + } + InferTy::FloatVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.float_unification_table().probe_value(vid), + FloatVarValue::Unknown + if inner.float_unification_table().find(vid) == vid + ) + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + true + } + } + } else { + true + } + } + GenericArgKind::Const(ct) => { + if let ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + InferConst::Var(vid) => !self + .probe_const_var(vid) + .is_err_and(|_| self.root_const_var(vid) == vid), + InferConst::Fresh(_) => true, + } + } else { + true + } + } + } + } + + fn next_ty_infer(&self) -> Ty<'db> { + self.next_ty_var() + } + + fn next_region_infer(&self) -> ::Region { + self.next_region_var() + } + + fn next_const_infer(&self) -> Const<'db> { + self.next_const_var() + } + + fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { + self.fresh_args_for_item(def_id) + } + + fn instantiate_binder_with_infer> + Clone>( + &self, + value: Binder<'db, T>, + ) -> T { + self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value) + } + + fn enter_forall> + Clone, U>( + &self, + value: Binder<'db, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, f) + } + + fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.inner.borrow_mut().type_variables().equate(a, b); + } + + fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) { + self.inner.borrow_mut().int_unification_table().union(a, b); + } + + fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) { + self.inner.borrow_mut().float_unification_table().union(a, b); + } + + fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) { + self.inner.borrow_mut().const_unification_table().union(a, b); + } + + fn instantiate_ty_var_raw>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::TyVid, + instantiation_variance: rustc_type_ir::Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_ty_var( + relation, + target_is_expected, + target_vid, + instantiation_variance, + source_ty, + ) + } + + fn instantiate_int_var_raw( + &self, + vid: rustc_type_ir::IntVid, + value: rustc_type_ir::IntVarValue, + ) { + self.inner.borrow_mut().int_unification_table().union_value(vid, value); + } + + fn instantiate_float_var_raw( + &self, + vid: rustc_type_ir::FloatVid, + value: rustc_type_ir::FloatVarValue, + ) { + self.inner.borrow_mut().float_unification_table().union_value(vid, value); + } + + fn instantiate_const_var_raw>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct) + } + + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.set_tainted_by_errors(e) + } + + fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + self.shallow_resolve(ty) + } + fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + self.shallow_resolve_const(ct) + } + + fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable>, + { + self.resolve_vars_if_possible(value) + } + + fn probe(&self, probe: impl FnOnce() -> T) -> T { + self.probe(|_| probe()) + } + + fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, span: Span) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup); + } + + fn equate_regions(&self, a: Region<'db>, b: Region<'db>, span: Span) { + self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b); + } + + fn register_ty_outlives(&self, ty: Ty<'db>, r: Region<'db>, span: Span) { + //self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(Span::dummy())); + } + + type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; + + fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries { + self.inner.borrow_mut().opaque_types().num_entries() + } + fn clone_opaque_types_lookup_table(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect() + } + fn clone_duplicate_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .iter_duplicate_entries() + .map(|(k, h)| (k, h.ty)) + .collect() + } + fn clone_opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(prev_entries) + .map(|(k, h)| (k, h.ty)) + .collect() + } + + fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) -> Option> { + self.register_hidden_type_in_storage(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + fn add_duplicate_opaque_type( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) { + self.inner + .borrow_mut() + .opaque_types() + .add_duplicate(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + + fn reset_opaque_types(&self) { + let _ = self.take_opaque_types(); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs new file mode 100644 index 0000000000000..19ae76f7e358f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -0,0 +1,1066 @@ +//! Infer context the next-trait-solver. + +use std::cell::{Cell, RefCell}; +use std::fmt; +use std::sync::Arc; + +pub use BoundRegionConversionTime::*; +pub use at::DefineOpaqueTypes; +use ena::undo_log::UndoLogs; +use ena::unify as ut; +use intern::Symbol; +use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; +use region_constraints::{ + GenericKind, RegionConstraintCollector, RegionConstraintStorage, UndoLog, VarInfos, VerifyBound, +}; +pub use relate::StructurallyRelateAliases; +pub use relate::combine::PredicateEmittingRelation; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::error::{ExpectedFound, TypeError}; +use rustc_type_ir::inherent::{ + Const as _, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Term as _, + Ty as _, +}; +use rustc_type_ir::{ + BoundVar, ClosureKind, ConstVid, FloatTy, FloatVarValue, FloatVid, GenericArgKind, InferConst, + InferTy, IntTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex, +}; +use rustc_type_ir::{TermKind, TypeVisitableExt}; +use rustc_type_ir::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use snapshot::undo_log::InferCtxtUndoLogs; +use tracing::{debug, instrument}; +use traits::{ObligationCause, PredicateObligations}; +use type_variable::TypeVariableOrigin; +use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; + +use crate::next_solver::fold::BoundVarReplacerDelegate; +use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; +use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; + +use super::generics::{GenericParamDef, GenericParamDefKind}; +use super::{ + AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, + DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, + PlaceholderRegion, PolyCoercePredicate, PolyExistentialProjection, PolyExistentialTraitRef, + PolyFnSig, PolyRegionOutlivesPredicate, PolySubtypePredicate, Predicate, Region, SolverDefId, + SubtypePredicate, Term, TraitPredicate, TraitRef, Ty, TyKind, TypingMode, +}; + +pub mod at; +pub mod canonical; +mod context; +mod opaque_types; +pub mod region_constraints; +pub mod relate; +pub mod resolve; +pub(crate) mod snapshot; +pub(crate) mod traits; +mod type_variable; +mod unify_key; + +/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper +/// around `PredicateObligations`, but it has one important property: +/// because `InferOk` is marked with `#[must_use]`, if you have a method +/// `InferCtxt::f` that returns `InferResult<()>` and you call it with +/// `infcx.f()?;` you'll get a warning about the obligations being discarded +/// without use, which is probably unintentional and has been a source of bugs +/// in the past. +#[must_use] +#[derive(Debug)] +pub struct InferOk<'db, T> { + pub value: T, + pub obligations: PredicateObligations<'db>, +} +pub type InferResult<'db, T> = Result, TypeError>>; + +pub(crate) type FixupResult = Result; // "fixup result" + +pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< + ut::InPlace, &'a mut InferCtxtUndoLogs<'db>>, +>; + +/// This type contains all the things within `InferCtxt` that sit within a +/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot +/// operations are hot enough that we want only one call to `borrow_mut` per +/// call to `start_snapshot` and `rollback_to`. +#[derive(Clone)] +pub struct InferCtxtInner<'db> { + pub(crate) undo_log: InferCtxtUndoLogs<'db>, + + /// We instantiate `UnificationTable` with `bounds` because the types + /// that might instantiate a general type variable have an order, + /// represented by its upper and lower bounds. + pub(crate) type_variable_storage: type_variable::TypeVariableStorage<'db>, + + /// Map from const parameter variable to the kind of const it represents. + pub(crate) const_unification_storage: ut::UnificationTableStorage>, + + /// Map from integral variable to the kind of integer it represents. + pub(crate) int_unification_storage: ut::UnificationTableStorage, + + /// Map from floating variable to the kind of float it represents. + pub(crate) float_unification_storage: ut::UnificationTableStorage, + + /// Tracks the set of region variables and the constraints between them. + /// + /// This is initially `Some(_)` but when + /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` + /// -- further attempts to perform unification, etc., may fail if new + /// region constraints would've been added. + pub(crate) region_constraint_storage: Option>, + + /// A set of constraints that regionck must validate. + /// + /// Each constraint has the form `T:'a`, meaning "some type `T` must + /// outlive the lifetime 'a". These constraints derive from + /// instantiated type parameters. So if you had a struct defined + /// like the following: + /// ```ignore (illustrative) + /// struct Foo { ... } + /// ``` + /// In some expression `let x = Foo { ... }`, it will + /// instantiate the type parameter `T` with a fresh type `$0`. At + /// the same time, it will record a region obligation of + /// `$0: 'static`. This will get checked later by regionck. (We + /// can't generally check these things right away because we have + /// to wait until types are resolved.) + /// + /// These are stored in a map keyed to the id of the innermost + /// enclosing fn body / static initializer expression. This is + /// because the location where the obligation was incurred can be + /// relevant with respect to which sublifetime assumptions are in + /// place. The reason that we store under the fn-id, and not + /// something more fine-grained, is so that it is easier for + /// regionck to be sure that it has found *all* the region + /// obligations (otherwise, it's easy to fail to walk to a + /// particular node-id). + /// + /// Before running `resolve_regions_and_report_errors`, the creator + /// of the inference context is expected to invoke + /// [`InferCtxt::process_registered_region_obligations`] + /// for each body-id in this map, which will process the + /// obligations within. This is expected to be done 'late enough' + /// that all type inference variables have been bound and so forth. + pub(crate) region_obligations: Vec>, + + /// Caches for opaque type inference. + pub(crate) opaque_type_storage: OpaqueTypeStorage<'db>, +} + +impl<'db> InferCtxtInner<'db> { + fn new() -> InferCtxtInner<'db> { + InferCtxtInner { + undo_log: InferCtxtUndoLogs::default(), + + type_variable_storage: Default::default(), + const_unification_storage: Default::default(), + int_unification_storage: Default::default(), + float_unification_storage: Default::default(), + region_constraint_storage: Some(Default::default()), + region_obligations: vec![], + opaque_type_storage: Default::default(), + } + } + + #[inline] + pub fn region_obligations(&self) -> &[RegionObligation<'db>] { + &self.region_obligations + } + + #[inline] + fn try_type_variables_probe_ref( + &self, + vid: TyVid, + ) -> Option<&type_variable::TypeVariableValue<'db>> { + // Uses a read-only view of the unification table, this way we don't + // need an undo log. + self.type_variable_storage.eq_relations_ref().try_probe_value(vid) + } + + #[inline] + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'db> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub(crate) fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'db> { + self.opaque_type_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn const_unification_table(&mut self) -> UnificationTable<'_, 'db, ConstVidKey<'db>> { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'db, '_> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) + } +} + +pub struct InferCtxt<'db> { + pub interner: DbInterner<'db>, + + /// The mode of this inference context, see the struct documentation + /// for more details. + typing_mode: TypingMode<'db>, + + pub inner: RefCell>, + + /// When an error occurs, we want to avoid reporting "derived" + /// errors that are due to this original failure. We have this + /// flag that one can set whenever one creates a type-error that + /// is due to an error in a prior pass. + /// + /// Don't read this flag directly, call `is_tainted_by_errors()` + /// and `set_tainted_by_errors()`. + tainted_by_errors: Cell>, + + /// What is the innermost universe we have created? Starts out as + /// `UniverseIndex::root()` but grows from there as we enter + /// universal quantifiers. + /// + /// N.B., at present, we exclude the universal quantifiers on the + /// item we are type-checking, and just consider those names as + /// part of the root universe. So this would only get incremented + /// when we enter into a higher-ranked (`for<..>`) type or trait + /// bound. + universe: Cell, +} + +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ValuePairs<'db> { + Regions(ExpectedFound>), + Terms(ExpectedFound>), + Aliases(ExpectedFound>), + TraitRefs(ExpectedFound>), + PolySigs(ExpectedFound>), + ExistentialTraitRef(ExpectedFound>), + ExistentialProjection(ExpectedFound>), +} + +impl<'db> ValuePairs<'db> { + pub fn ty(&self) -> Option<(Ty<'db>, Ty<'db>)> { + if let ValuePairs::Terms(ExpectedFound { expected, found }) = self + && let Some(expected) = expected.as_type() + && let Some(found) = found.as_type() + { + return Some((expected, found)); + } + None + } +} + +/// The trace designates the path through inference that we took to +/// encounter an error or subtyping constraint. +/// +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug)] +pub struct TypeTrace<'db> { + pub cause: ObligationCause, + pub values: ValuePairs<'db>, +} + +/// Times when we replace bound regions with existentials: +#[derive(Clone, Copy, Debug)] +pub enum BoundRegionConversionTime { + /// when a fn is called + FnCall, + + /// when two higher-ranked types are compared + HigherRankedType, + + /// when projecting an associated type + AssocTypeProjection(SolverDefId), +} + +#[derive(Copy, Clone, Debug)] +pub struct FixupError { + unresolved: TyOrConstInferVar, +} + +impl fmt::Display for FixupError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TyOrConstInferVar::*; + + match self.unresolved { + TyInt(_) => write!( + f, + "cannot determine the type of this integer; \ + add a suffix to specify the type explicitly" + ), + TyFloat(_) => write!( + f, + "cannot determine the type of this number; \ + add a suffix to specify the type explicitly" + ), + Ty(_) => write!(f, "unconstrained type"), + Const(_) => write!(f, "unconstrained const value"), + } + } +} + +/// See the `region_obligations` field for more information. +#[derive(Clone, Debug)] +pub struct RegionObligation<'db> { + pub sub_region: Region<'db>, + pub sup_type: Ty<'db>, +} + +/// Used to configure inference contexts before their creation. +pub struct InferCtxtBuilder<'db> { + interner: DbInterner<'db>, +} + +pub trait DbInternerInferExt<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db>; +} + +impl<'db> DbInternerInferExt<'db> for DbInterner<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db> { + InferCtxtBuilder { interner: self } + } +} + +impl<'db> InferCtxtBuilder<'db> { + /// Given a canonical value `C` as a starting point, create an + /// inference context that contains each of the bound values + /// within instantiated as a fresh variable. The `f` closure is + /// invoked with the new infcx, along with the instantiated value + /// `V` and a instantiation `S`. This instantiation `S` maps from + /// the bound values in `C` to their instantiated values in `V` + /// (in other words, `S(C) = V`). + pub fn build_with_canonical( + mut self, + input: &CanonicalQueryInput<'db, T>, + ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) + where + T: TypeFoldable>, + { + let infcx = self.build(input.typing_mode); + let (value, args) = infcx.instantiate_canonical(&input.canonical); + (infcx, value, args) + } + + pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> { + let InferCtxtBuilder { interner } = *self; + InferCtxt { + interner, + typing_mode, + inner: RefCell::new(InferCtxtInner::new()), + tainted_by_errors: Cell::new(None), + universe: Cell::new(UniverseIndex::ROOT), + } + } +} + +impl<'db> InferOk<'db, ()> { + pub fn into_obligations(self) -> PredicateObligations<'db> { + self.obligations + } +} + +impl<'db> InferCtxt<'db> { + #[inline(always)] + pub fn typing_mode(&self) -> TypingMode<'db> { + self.typing_mode + } + + #[inline(always)] + pub fn typing_mode_unchecked(&self) -> TypingMode<'db> { + self.typing_mode + } + + pub fn unresolved_variables(&self) -> Vec> { + let mut inner = self.inner.borrow_mut(); + let mut vars: Vec> = inner + .type_variables() + .unresolved_variables() + .into_iter() + .map(|t| Ty::new_var(self.interner, t)) + .collect(); + vars.extend( + (0..inner.int_unification_table().len()) + .map(IntVid::from_usize) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_int_var(self.interner, v)), + ); + vars.extend( + (0..inner.float_unification_table().len()) + .map(FloatVid::from_usize) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_float_var(self.interner, v)), + ); + vars + } + + #[instrument(skip(self), level = "debug")] + pub fn sub_regions(&self, a: Region<'db>, b: Region<'db>) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(a, b); + } + + /// Processes a `Coerce` predicate from the fulfillment context. + /// This is NOT the preferred way to handle coercion, which is to + /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). + /// + /// This method here is actually a fallback that winds up being + /// invoked when `FnCtxt::coerce` encounters unresolved type variables + /// and records a coercion predicate. Presently, this method is equivalent + /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up + /// actually requiring `a <: b`. This is of course a valid coercion, + /// but it's not as flexible as `FnCtxt::coerce` would be. + /// + /// (We may refactor this in the future, but there are a number of + /// practical obstacles. Among other things, `FnCtxt::coerce` presently + /// records adjustments that are required on the HIR in order to perform + /// the coercion, and we don't currently have a way to manage that.) + pub fn coerce_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolyCoercePredicate<'db>, + ) -> Result, (TyVid, TyVid)> { + let subtype_predicate = predicate.map_bound(|p| SubtypePredicate { + a_is_expected: false, // when coercing from `a` to `b`, `b` is expected + a: p.a, + b: p.b, + }); + self.subtype_predicate(cause, param_env, subtype_predicate) + } + + pub fn subtype_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolySubtypePredicate<'db>, + ) -> Result, (TyVid, TyVid)> { + // Check for two unresolved inference variables, in which case we can + // make no progress. This is partly a micro-optimization, but it's + // also an opportunity to "sub-unify" the variables. This isn't + // *necessary* to prevent cycles, because they would eventually be sub-unified + // anyhow during generalization, but it helps with diagnostics (we can detect + // earlier that they are sub-unified). + // + // Note that we can just skip the binders here because + // type variables can't (at present, at + // least) capture any of the things bound by this binder. + // + // Note that this sub here is not just for diagnostics - it has semantic + // effects as well. + let r_a = self.shallow_resolve(predicate.skip_binder().a); + let r_b = self.shallow_resolve(predicate.skip_binder().b); + match (r_a.kind(), r_b.kind()) { + (TyKind::Infer(InferTy::TyVar(a_vid)), TyKind::Infer(InferTy::TyVar(b_vid))) => { + return Err((a_vid, b_vid)); + } + _ => {} + } + + self.enter_forall(predicate, |SubtypePredicate { a_is_expected, a, b }| { + if a_is_expected { + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) + } else { + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) + } + }) + } + + pub fn region_outlives_predicate( + &self, + cause: &traits::ObligationCause, + predicate: PolyRegionOutlivesPredicate<'db>, + ) { + self.enter_forall(predicate, |OutlivesPredicate(r_a, r_b)| { + self.sub_regions(r_b, r_a); // `b : a` ==> `a <= b` + }) + } + + /// Number of type variables created so far. + pub fn num_ty_vars(&self) -> usize { + self.inner.borrow_mut().type_variables().num_vars() + } + + pub fn next_ty_var(&self) -> Ty<'db> { + self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) + } + + pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { + let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); + Ty::new_var(self.interner, vid) + } + + pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid { + let origin = TypeVariableOrigin { param_def_id: None }; + self.inner.borrow_mut().type_variables().new_var(universe, origin) + } + + pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> { + let vid = self.next_ty_var_id_in_universe(universe); + Ty::new_var(self.interner, vid) + } + + pub fn next_const_var(&self) -> Const<'db> { + self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) + } + + pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) + .vid; + Const::new_var(self.interner, vid) + } + + pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> { + let origin = ConstVariableOrigin { param_def_id: None }; + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe }) + .vid; + Const::new_var(self.interner, vid) + } + + pub fn next_int_var(&self) -> Ty<'db> { + let next_int_var_id = + self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown); + Ty::new_int_var(self.interner, next_int_var_id) + } + + pub fn next_float_var(&self) -> Ty<'db> { + let next_float_var_id = + self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); + Ty::new_float_var(self.interner, next_float_var_id) + } + + /// Creates a fresh region variable with the next available index. + /// The variable will be created in the maximum universe created + /// thus far, allowing it to name any region created thus far. + pub fn next_region_var(&self) -> Region<'db> { + self.next_region_var_in_universe(self.universe()) + } + + /// Creates a fresh region variable with the next available index + /// in the given universe; typically, you can use + /// `next_region_var` and just use the maximal universe. + pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + let region_var = + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe); + Region::new_var(self.interner, region_var) + } + + pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> { + match term.kind() { + TermKind::Ty(_) => self.next_ty_var().into(), + TermKind::Const(_) => self.next_const_var().into(), + } + } + + /// Return the universe that the region `r` was created in. For + /// most regions (e.g., `'static`, named regions from the user, + /// etc) this is the root universe U0. For inference variables or + /// placeholders, however, it will return the universe which they + /// are associated. + pub fn universe_of_region(&self, r: Region<'db>) -> UniverseIndex { + self.inner.borrow_mut().unwrap_region_constraints().universe(r) + } + + /// Number of region variables created so far. + pub fn num_region_vars(&self) -> usize { + self.inner.borrow_mut().unwrap_region_constraints().num_region_vars() + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] + pub fn next_nll_region_var(&self) -> Region<'db> { + self.next_region_var() + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] + pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + self.next_region_var_in_universe(universe) + } + + fn var_for_def(&self, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => { + // Create a region inference variable for the given + // region parameter definition. + self.next_region_var().into() + } + GenericParamDefKind::Type => { + // Create a type inference variable for the given + // type parameter definition. The generic parameters are + // for actual parameters that may be referred to by + // the default of this type parameter, if it exists. + // e.g., `struct Foo(...);` when + // used in a path such as `Foo::::new()` will + // use an inference variable for `C` with `[T, U]` + // as the generic parameters for the default, `(T, U)`. + let ty_var_id = self + .inner + .borrow_mut() + .type_variables() + .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }); + + Ty::new_var(self.interner, ty_var_id).into() + } + GenericParamDefKind::Const => { + let origin = ConstVariableOrigin { param_def_id: None }; + let const_var_id = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) + .vid; + Const::new_var(self.interner, const_var_id).into() + } + } + } + + /// Given a set of generics defined on a type or impl, returns the generic parameters mapping + /// each type/region parameter to a fresh inference variable. + pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { + GenericArgs::for_item(self.interner, def_id, |name, index, kind, _| { + self.var_for_def(kind, name) + }) + } + + /// Returns `true` if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// reporting errors that often occur as a result of earlier + /// errors, but where it's hard to be 100% sure (e.g., unresolved + /// inference variables, regionck errors). + #[must_use = "this method does not have any side effects"] + pub fn tainted_by_errors(&self) -> Option { + self.tainted_by_errors.get() + } + + /// Set the "tainted by errors" flag to true. We call this when we + /// observe an error from a prior pass. + pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + debug!("set_tainted_by_errors(ErrorGuaranteed)"); + self.tainted_by_errors.set(Some(e)); + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() + } + + #[inline(always)] + pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { + match self.typing_mode_unchecked() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators.contains(&id.into()) + } + TypingMode::Coherence | TypingMode::PostAnalysis => false, + TypingMode::Borrowck { defining_opaque_types } => unimplemented!(), + TypingMode::PostBorrowckAnalysis { defined_opaque_types } => unimplemented!(), + } + } + + /// If `TyVar(vid)` resolves to a type, return that type. Else, return the + /// universe index of `TyVar(vid)`. + pub fn probe_ty_var(&self, vid: TyVid) -> Result, UniverseIndex> { + use self::type_variable::TypeVariableValue; + + match self.inner.borrow_mut().type_variables().probe(vid) { + TypeVariableValue::Known { value } => Ok(value), + TypeVariableValue::Unknown { universe } => Err(universe), + } + } + + pub fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Infer(v) = ty.kind() { + match v { + InferTy::TyVar(v) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(ty, |t| self.shallow_resolve(t)) + } + + InferTy::IntVar(v) => { + match self.inner.borrow_mut().int_unification_table().probe_value(v) { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => ty, + } + } + + InferTy::FloatVar(v) => { + match self.inner.borrow_mut().float_unification_table().probe_value(v) { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => ty, + } + } + + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => ty, + } + } else { + ty + } + } + + pub fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .known() + .unwrap_or(ct), + InferConst::Fresh(_) => ct, + }, + ConstKind::Param(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Unevaluated(_) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => ct, + } + } + + pub fn root_var(&self, var: TyVid) -> TyVid { + self.inner.borrow_mut().type_variables().root_var(var) + } + + pub fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.inner.borrow_mut().const_unification_table().find(var).vid + } + + /// Resolves an int var to a rigid int type, if it was constrained to one, + /// or else the root int var in the unification table. + pub fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.int_unification_table().probe_value(vid); + match value { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => { + Ty::new_int_var(self.interner, inner.int_unification_table().find(vid)) + } + } + } + + /// Resolves a float var to a rigid int type, if it was constrained to one, + /// or else the root float var in the unification table. + pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.float_unification_table().probe_value(vid); + match value { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => { + Ty::new_float_var(self.interner, inner.float_unification_table().find(vid)) + } + } + } + + /// Where possible, replaces type/const variables in + /// `value` with their final value. Note that region variables + /// are unaffected. If a type/const variable has not been unified, it + /// is left as is. This is an idempotent operation that does + /// not affect inference state in any way and so you can do it + /// at will. + pub fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable>, + { + if let Err(guar) = value.error_reported() { + self.set_tainted_by_errors(guar); + } + if !value.has_non_region_infer() { + return value; + } + let mut r = resolve::OpportunisticVarResolver::new(self); + value.fold_with(&mut r) + } + + pub fn probe_const_var(&self, vid: ConstVid) -> Result, UniverseIndex> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + } + } + + // Instantiates the bound variables in a given binder with fresh inference + // variables in the current universe. + // + // Use this method if you'd like to find some generic parameters of the binder's + // variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`] + // that corresponds to your use case, consider whether or not you should + // use [`InferCtxt::enter_forall`] instead. + pub fn instantiate_binder_with_fresh_vars( + &self, + lbrct: BoundRegionConversionTime, + value: Binder<'db, T>, + ) -> T + where + T: TypeFoldable> + Clone, + { + if let Some(inner) = value.clone().no_bound_vars() { + return inner; + } + + let bound_vars = value.clone().bound_vars(); + let mut args = Vec::with_capacity(bound_vars.len()); + + for bound_var_kind in bound_vars { + let arg: GenericArg<'db> = match bound_var_kind { + BoundVarKind::Ty(_) => self.next_ty_var().into(), + BoundVarKind::Region(br) => self.next_region_var().into(), + BoundVarKind::Const => self.next_const_var().into(), + }; + args.push(arg); + } + + struct ToFreshVars<'db> { + args: Vec>, + } + + impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + self.args[br.var.index()].expect_region() + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + self.args[bt.var.index()].expect_ty() + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + self.args[bv.index()].expect_const() + } + } + let delegate = ToFreshVars { args }; + self.interner.replace_bound_vars_uncached(value, delegate) + } + + /// Obtains the latest type of the given closure; this may be a + /// closure in the current function, in which case its + /// `ClosureKind` may not yet be known. + pub fn closure_kind(&self, closure_ty: Ty<'db>) -> Option { + let unresolved_kind_ty = match closure_ty.kind() { + TyKind::Closure(_, args) => args.as_closure().kind_ty(), + TyKind::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(), + _ => panic!("unexpected type {closure_ty:?}"), + }; + let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty); + closure_kind_ty.to_opt_closure_kind() + } + + pub fn universe(&self) -> UniverseIndex { + self.universe.get() + } + + /// Creates and return a fresh universe that extends all previous + /// universes. Updates `self.universe` to that new universe. + pub fn create_next_universe(&self) -> UniverseIndex { + let u = self.universe.get().next_universe(); + debug!("create_next_universe {u:?}"); + self.universe.set(u); + u + } + + /// The returned function is used in a fast path. If it returns `true` the variable is + /// unchanged, `false` indicates that the status is unknown. + #[inline] + pub fn is_ty_infer_var_definitely_unchanged<'a>( + &'a self, + ) -> (impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a) { + // This hoists the borrow/release out of the loop body. + let inner = self.inner.try_borrow(); + + move |infer_var: TyOrConstInferVar| match (infer_var, &inner) { + (TyOrConstInferVar::Ty(ty_var), Ok(inner)) => { + use self::type_variable::TypeVariableValue; + + matches!( + inner.try_type_variables_probe_ref(ty_var), + Some(TypeVariableValue::Unknown { .. }) + ) + } + _ => false, + } + } + + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. + #[inline(always)] + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { + use self::type_variable::TypeVariableValue; + + // If `inlined_probe` returns a `Known` value, it never equals + // `Infer(TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { + TypeVariableValue::Unknown { .. } => false, + TypeVariableValue::Known { .. } => true, + } + } + + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a + // `Int(_)` or `UInt(_)`, which never matches a + // `Infer(_)`. + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_known() + } + + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a + // `Float(_)`, which never matches a `Infer(_)`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + self.inner.borrow_mut().float_unification_table().probe_value(v).is_known() + } + + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ConstKind::Infer(InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v) { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar { + /// Equivalent to `Infer(TyVar(_))`. + Ty(TyVid), + /// Equivalent to `Infer(IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `Infer(FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ConstKind::Infer(InferConst::Var(_))`. + Const(ConstVid), +} + +impl TyOrConstInferVar { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg<'db>(arg: GenericArg<'db>) -> Option { + match arg.kind() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`). + fn maybe_from_ty<'db>(ty: Ty<'db>) -> Option { + match ty.kind() { + TyKind::Infer(InferTy::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + TyKind::Infer(InferTy::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + TyKind::Infer(InferTy::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + fn maybe_from_const<'db>(ct: Const<'db>) -> Option { + match ct.kind() { + ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +impl<'db> TypeTrace<'db> { + pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } + + pub fn trait_refs( + cause: &ObligationCause, + a: TraitRef<'db>, + b: TraitRef<'db>, + ) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + } + + pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +/// Requires that `region` must be equal to one of the regions in `choice_regions`. +/// We often denote this using the syntax: +/// +/// ```text +/// R0 member of [O1..On] +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MemberConstraint<'db> { + /// The `DefId` and args of the opaque type causing this constraint. + /// Used for error reporting. + pub key: OpaqueTypeKey<'db>, + + /// The hidden type in which `member_region` appears: used for error reporting. + pub hidden_ty: Ty<'db>, + + /// The region `R0`. + pub member_region: Region<'db>, + + /// The options `O1..On`. + pub choice_regions: Arc>>, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs new file mode 100644 index 0000000000000..0f68ec8cdb5b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs @@ -0,0 +1,56 @@ +//! Things related to the infer context of the next-trait-solver. + +use std::sync::Arc; + +use tracing::{debug, instrument}; + +use crate::next_solver::{ + Clause, ClauseKind, FxIndexMap, GenericArgs, OpaqueTypeKey, ProjectionPredicate, SolverDefId, + TypingMode, util::BottomUpFolder, +}; + +pub(crate) mod table; + +pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundConstness, BoundVar, Flags, GenericArgKind, InferTy, + Interner, RegionKind, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, Variance, + error::{ExpectedFound, TypeError}, + inherent::{DefId, GenericArgs as _, IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +use super::{InferOk, traits::ObligationCause}; + +#[derive(Copy, Clone, Debug)] +pub struct OpaqueHiddenType<'db> { + pub ty: Ty<'db>, +} + +impl<'db> InferCtxt<'db> { + /// Insert a hidden type into the opaque type storage, making sure + /// it hasn't previously been defined. This does not emit any + /// constraints and it's the responsibility of the caller to make + /// sure that the item bounds of the opaque are checked. + pub fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: OpaqueHiddenType<'db>, + ) -> Option> { + self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs new file mode 100644 index 0000000000000..8ab409d782813 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs @@ -0,0 +1,166 @@ +//! Things related to storage opaques in the infer context of the next-trait-solver. + +use std::ops::Deref; + +use ena::undo_log::UndoLogs; +use tracing::instrument; + +use super::OpaqueHiddenType; +use crate::next_solver::{ + FxIndexMap, OpaqueTypeKey, Ty, + infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}, +}; + +#[derive(Default, Debug, Clone)] +pub(crate) struct OpaqueTypeStorage<'db> { + opaque_types: FxIndexMap, OpaqueHiddenType<'db>>, + duplicate_entries: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, +} + +/// The number of entries in the opaque type storage at a given point. +/// +/// Used to check that we haven't added any new opaque types after checking +/// the opaque types currently in the storage. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct OpaqueTypeStorageEntries { + opaque_types: usize, + duplicate_entries: usize, +} + +impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries { + fn needs_reevaluation(self, canonicalized: usize) -> bool { + self.opaque_types != canonicalized + } +} + +impl<'db> OpaqueTypeStorage<'db> { + #[instrument(level = "debug")] + pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'db>, prev: Option>) { + if let Some(prev) = prev { + *self.opaque_types.get_mut(&key).unwrap() = prev; + } else { + // FIXME(#120456) - is `swap_remove` correct? + match self.opaque_types.swap_remove(&key) { + None => { + panic!("reverted opaque type inference that was never registered: {key:?}") + } + Some(_) => {} + } + } + } + + pub(crate) fn pop_duplicate_entry(&mut self) { + let entry = self.duplicate_entries.pop(); + assert!(entry.is_some()); + } + + pub fn is_empty(&self) -> bool { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.is_empty() && duplicate_entries.is_empty() + } + + pub(crate) fn take_opaque_types( + &mut self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) + } + + pub fn num_entries(&self) -> OpaqueTypeStorageEntries { + OpaqueTypeStorageEntries { + opaque_types: self.opaque_types.len(), + duplicate_entries: self.duplicate_entries.len(), + } + } + + pub fn opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.opaque_types + .iter() + .skip(prev_entries.opaque_types) + .map(|(k, v)| (*k, *v)) + .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied()) + } + + /// Only returns the opaque types from the lookup table. These are used + /// when normalizing opaque types and have a unique key. + /// + /// Outside of canonicalization one should generally use `iter_opaque_types` + /// to also consider duplicate entries. + pub fn iter_lookup_table( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.opaque_types.iter().map(|(k, v)| (*k, *v)) + } + + /// Only returns the opaque types which are stored in `duplicate_entries`. + /// + /// These have to considered when checking all opaque type uses but are e.g. + /// irrelevant for canonical inputs as nested queries never meaningfully + /// accesses them. + pub fn iter_duplicate_entries( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.duplicate_entries.iter().copied() + } + + pub fn iter_opaque_types( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied()) + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> OpaqueTypeTable<'a, 'db> { + OpaqueTypeTable { storage: self, undo_log } + } +} + +impl<'db> Drop for OpaqueTypeStorage<'db> { + fn drop(&mut self) { + if !self.opaque_types.is_empty() { + panic!("{:?}", self.opaque_types) + } + } +} + +pub(crate) struct OpaqueTypeTable<'a, 'db> { + storage: &'a mut OpaqueTypeStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} +impl<'db> Deref for OpaqueTypeTable<'_, 'db> { + type Target = OpaqueTypeStorage<'db>; + fn deref(&self) -> &Self::Target { + self.storage + } +} + +impl<'a, 'db> OpaqueTypeTable<'a, 'db> { + #[instrument(skip(self), level = "debug")] + pub fn register( + &mut self, + key: OpaqueTypeKey<'db>, + hidden_type: OpaqueHiddenType<'db>, + ) -> Option> { + if let Some(entry) = self.storage.opaque_types.get_mut(&key) { + let prev = std::mem::replace(entry, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev))); + return Some(prev.ty); + } + self.storage.opaque_types.insert(key, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, None)); + None + } + + pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>) { + self.storage.duplicate_entries.push((key, hidden_type)); + self.undo_log.push(UndoLog::DuplicateOpaqueType); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs new file mode 100644 index 0000000000000..50549694c3f23 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -0,0 +1,640 @@ +//! See `README.md`. + +use std::ops::Range; +use std::sync::Arc; +use std::{cmp, fmt, mem}; + +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_hash::FxHashMap; +use rustc_index::IndexVec; +use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::{RegionKind, RegionVid, UniverseIndex}; +use tracing::{debug, instrument}; + +use self::CombineMapType::*; +use self::UndoLog::*; +use super::MemberConstraint; +use super::unify_key::RegionVidKey; +use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; +use crate::next_solver::infer::unify_key::RegionVariableValue; +use crate::next_solver::{ + AliasTy, Binder, DbInterner, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, +}; + +#[derive(Clone, Default)] +pub struct RegionConstraintStorage<'db> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + pub(super) var_infos: IndexVec, + + pub(super) data: RegionConstraintData<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'db>, + + /// When we add a R1 == R2 constraint, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when fulfillment, dropck and other such + /// code is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that have been + /// equated but appear distinct. + pub(super) unification_table: ut::UnificationTableStorage>, + + /// a flag set to true when we perform any unifications; this is used + /// to micro-optimize `take_and_reset_data` + any_unifications: bool, +} + +pub struct RegionConstraintCollector<'db, 'a> { + storage: &'a mut RegionConstraintStorage<'db>, + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +pub type VarInfos = IndexVec; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Debug, Default, Clone)] +pub struct RegionConstraintData<'db> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: Vec>, + + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub member_constraints: Vec>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec>, +} + +/// Represents a constraint that influences the inference process. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum Constraint<'db> { + /// A region variable is a subregion of another. + VarSubVar(RegionVid, RegionVid), + + /// A concrete region is a subregion of region variable. + RegSubVar(Region<'db>, RegionVid), + + /// A region variable is a subregion of a concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'db>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'db>, Region<'db>), +} + +impl<'db> Constraint<'db> { + pub fn involves_placeholders(&self) -> bool { + match self { + Constraint::VarSubVar(_, _) => false, + Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(), + Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Verify<'db> { + pub kind: GenericKind<'db>, + pub region: Region<'db>, + pub bound: VerifyBound<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum GenericKind<'db> { + Param(ParamTy), + Placeholder(PlaceholderTy), + Alias(AliasTy<'db>), +} + +/// Describes the things that some `GenericKind` value `G` is known to +/// outlive. Each variant of `VerifyBound` can be thought of as a +/// function: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` +/// where `true` means that the region `min` meets that `G: min`. +/// (False means nothing.) +/// +/// So, for example, if we have the type `T` and we have in scope that +/// `T: 'a` and `T: 'b`, then the verify bound might be: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` +/// This is described with an `AnyRegion('a, 'b)` node. +#[derive(Debug, Clone)] +pub enum VerifyBound<'db> { + /// See [`VerifyIfEq`] docs + IfEq(Binder<'db, VerifyIfEq<'db>>), + + /// Given a region `R`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// R: min + /// } + /// ``` + /// + /// This is used when we can establish that `G: R` -- therefore, + /// if `R: min`, then by transitivity `G: min`. + OutlivedBy(Region<'db>), + + /// Given a region `R`, true if it is `'empty`. + IsEmpty, + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// exists (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet some bound in `B`, that suffices. + /// This is used when all the bounds in `B` are known to apply to `G`. + AnyBound(Vec>), + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// forall (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet *all* bounds in `B`, that suffices. + /// This is used when *some* bound in `B` is known to suffice, but + /// we don't know which. + AllBounds(Vec>), +} + +/// This is a "conditional bound" that checks the result of inference +/// and supplies a bound if it ended up being relevant. It's used in situations +/// like this: +/// +/// ```rust,ignore (pseudo-Rust) +/// fn foo<'a, 'b, T: SomeTrait<'a>> +/// where +/// >::Item: 'b +/// ``` +/// +/// If we have an obligation like `>::Item: 'c`, then +/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds +/// up being equal to `'a`, then the where-clauses on function applies, and +/// in that case we can show `'b: 'c`. But if `'?x` winds up being something +/// else, the bound isn't relevant. +/// +/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account +/// for cases like +/// +/// ```rust,ignore (pseudo-Rust) +/// where for<'a> ::Item: 'a +/// ``` +/// +/// The idea is that we have to find some instantiation of `'a` that can +/// make `>::Item` equal to the final value of `G`, +/// the generic we are checking. +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// exists<'a> { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct VerifyIfEq<'db> { + /// Type which must match the generic `G` + pub ty: Ty<'db>, + + /// Bound that applies if `ty` is equal. + pub bound: Region<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct TwoRegions<'db> { + a: Region<'db>, + b: Region<'db>, +} + +#[derive(Clone, PartialEq)] +pub(crate) enum UndoLog<'db> { + /// We added `RegionVid`. + AddVar(RegionVid), + + /// We added the given `constraint`. + AddConstraint(usize), + + /// We added the given `verify`. + AddVerify(usize), + + /// We added a GLB/LUB "combination variable". + AddCombination(CombineMapType, TwoRegions<'db>), +} + +#[derive(Clone, PartialEq)] +pub(crate) enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'db> = FxHashMap, RegionVid>; + +#[derive(Debug, Clone)] +pub struct RegionVariableInfo { + // FIXME: This is only necessary for `fn take_and_reset_data` and + // `lexical_region_resolve`. We should rework `lexical_region_resolve` + // in the near/medium future anyways and could move the unverse info + // for `fn take_and_reset_data` into a separate table which is + // only populated when needed. + // + // For both of these cases it is fine that this can diverge from the + // actual universe of the variable, which is directly stored in the + // unification table for unknown region variables. At some point we could + // stop emitting bidirectional outlives constraints if equate succeeds. + // This would be currently unsound as it would cause us to drop the universe + // changes in `lexical_region_resolve`. + pub universe: UniverseIndex, +} + +pub(crate) struct RegionSnapshot { + any_unifications: bool, +} + +impl<'db> RegionConstraintStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> RegionConstraintCollector<'db, 'a> { + RegionConstraintCollector { storage: self, undo_log } + } +} + +impl<'db> RegionConstraintCollector<'db, '_> { + pub fn num_region_vars(&self) -> usize { + self.storage.var_infos.len() + } + + pub fn region_constraint_data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'db> { + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintStorage { + var_infos: _, + data, + lubs, + glbs, + unification_table: _, + any_unifications, + } = self.storage; + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + let data = mem::take(data); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + if *any_unifications { + *any_unifications = false; + // Manually inlined `self.unification_table_mut()` as `self` is used in the closure. + ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log) + .reset_unifications(|key| RegionVariableValue::Unknown { + universe: self.storage.var_infos[key.vid].universe, + }); + } + + data + } + + pub fn data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + pub(super) fn start_snapshot(&self) -> RegionSnapshot { + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.storage.any_unifications } + } + + pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + self.storage.any_unifications = snapshot.any_unifications; + } + + pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid { + let vid = self.storage.var_infos.push(RegionVariableInfo { universe }); + + let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); + assert_eq!(vid, u_vid.vid); + self.undo_log.push(AddVar(vid)); + debug!("created new region variable {:?} in {:?}", vid, universe); + vid + } + + fn add_constraint(&mut self, constraint: Constraint<'db>) { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: add_constraint({:?})", constraint); + + let index = self.storage.data.constraints.len(); + self.storage.data.constraints.push(constraint); + self.undo_log.push(AddConstraint(index)); + } + + pub(super) fn make_eqregion(&mut self, a: Region<'db>, b: Region<'db>) { + if a != b { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(a, b); + self.make_subregion(b, a); + + match (a.kind(), b.kind()) { + (RegionKind::ReVar(a), RegionKind::ReVar(b)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, b); + if self.unification_table_mut().unify_var_var(a, b).is_ok() { + self.storage.any_unifications = true; + } + } + (RegionKind::ReVar(vid), _) => { + debug!("make_eqregion: unifying {:?} with {:?}", vid, b); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: b }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, RegionKind::ReVar(vid)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, vid); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: a }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, _) => {} + } + } + } + + #[instrument(skip(self), level = "debug")] + pub(super) fn make_subregion(&mut self, sub: Region<'db>, sup: Region<'db>) { + // cannot add constraints once regions are resolved + + match (sub.kind(), sup.kind()) { + (RegionKind::ReBound(..), _) | (_, RegionKind::ReBound(..)) => { + panic!("cannot relate bound region: {sub:?} <= {sup:?}"); + } + (_, RegionKind::ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (RegionKind::ReVar(sub_id), RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id)); + } + (_, RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id)); + } + (RegionKind::ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup)); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup)); + } + } + } + + /// Resolves a region var to its value in the unification table, if it exists. + /// Otherwise, it is resolved to the root `ReVar` in the table. + pub fn opportunistic_resolve_var( + &mut self, + cx: DbInterner<'db>, + vid: RegionVid, + ) -> Region<'db> { + let mut ut = self.unification_table_mut(); + let root_vid = ut.find(vid).vid; + match ut.probe_value(root_vid) { + RegionVariableValue::Known { value } => value, + RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid), + } + } + + pub fn probe_value(&mut self, vid: RegionVid) -> Result, UniverseIndex> { + match self.unification_table_mut().probe_value(vid) { + RegionVariableValue::Known { value } => Ok(value), + RegionVariableValue::Unknown { universe } => Err(universe), + } + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'db> { + match t { + Glb => &mut self.storage.glbs, + Lub => &mut self.storage.lubs, + } + } + + fn combine_vars( + &mut self, + cx: DbInterner<'db>, + t: CombineMapType, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + let vars = TwoRegions { a, b }; + if let Some(c) = self.combine_map(t.clone()).get(&vars) { + return Region::new_var(cx, *c); + } + let a_universe = self.universe(a); + let b_universe = self.universe(b); + let c_universe = cmp::max(a_universe, b_universe); + let c = self.new_region_var(c_universe); + self.combine_map(t.clone()).insert(vars.clone(), c); + self.undo_log.push(AddCombination(t.clone(), vars)); + let new_r = Region::new_var(cx, c); + for old_r in [a, b] { + match t { + Glb => self.make_subregion(new_r, old_r), + Lub => self.make_subregion(old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn universe(&mut self, region: Region<'db>) -> UniverseIndex { + match region.kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(vid) => match self.probe_value(vid) { + Ok(value) => self.universe(value), + Err(universe) => universe, + }, + RegionKind::ReBound(..) => panic!("universe(): encountered bound region {region:?}"), + } + } + + #[inline] + fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RegionSnapshot") + } +} + +impl<'db> fmt::Debug for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p:?}"), + } + } +} + +impl<'db> fmt::Display for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p}"), + } + } +} + +impl<'db> GenericKind<'db> { + pub fn to_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + GenericKind::Param(ref p) => (*p).to_ty(interner), + GenericKind::Placeholder(ref p) => Ty::new_placeholder(interner, *p), + GenericKind::Alias(ref p) => (*p).to_ty(interner), + } + } +} + +impl<'db> VerifyBound<'db> { + pub fn must_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::OutlivedBy(re) => re.is_static(), + VerifyBound::IsEmpty => false, + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::IsEmpty => false, + VerifyBound::OutlivedBy(_) => false, + VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), + VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'db>) -> VerifyBound<'db> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } +} + +impl<'db> RegionConstraintData<'db> { + /// Returns `true` if this region constraint data contains no constraints, and `false` + /// otherwise. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { constraints, member_constraints, verifys } = self; + constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty() + } +} + +impl<'db> Rollback> for RegionConstraintStorage<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + AddVar(vid) => { + self.var_infos.pop().unwrap(); + assert_eq!(self.var_infos.len(), vid.index()); + } + AddConstraint(index) => { + self.data.constraints.pop().unwrap(); + assert_eq!(self.data.constraints.len(), index); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs new file mode 100644 index 0000000000000..de336c69b3180 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -0,0 +1,720 @@ +//! Type generation code. + +use std::mem; + +use rustc_hash::FxHashMap; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _}; +use rustc_type_ir::relate::VarianceDiagInfo; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind, + TermKind, TyVid, UniverseIndex, Variance, +}; +use rustc_type_ir::{Interner, TypeVisitable, TypeVisitableExt}; +use tracing::{debug, instrument, warn}; + +use super::{ + PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, +}; +use crate::next_solver::infer::type_variable::TypeVariableValue; +use crate::next_solver::infer::unify_key::ConstVariableValue; +use crate::next_solver::infer::{InferCtxt, relate}; +use crate::next_solver::util::MaxUniverse; +use crate::next_solver::{ + AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind, + ProjectionPredicate, Region, SolverDefId, Term, TermVid, Ty, TyKind, TypingMode, + UnevaluatedConst, +}; + +impl<'db> InferCtxt<'db> { + /// The idea is that we should ensure that the type variable `target_vid` + /// is equal to, a subtype of, or a supertype of `source_ty`. + /// + /// For this, we will instantiate `target_vid` with a *generalized* version + /// of `source_ty`. Generalization introduces other inference variables wherever + /// subtyping could occur. This also does the occurs checks, detecting whether + /// instantiating `target_vid` would result in a cyclic type. We eagerly error + /// in this case. + /// + /// This is *not* expected to be used anywhere except for an implementation of + /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all + /// other usecases (i.e. setting the value of a type var). + #[instrument(level = "debug", skip(self, relation))] + pub fn instantiate_ty_var>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: TyVid, + instantiation_variance: Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); + + // Generalize `source_ty` depending on the current variance. As an example, assume + // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference + // variable. + // + // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // region/type inference variables. + // + // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and + // `?1 <: ?3`. + let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + instantiation_variance, + source_ty, + )?; + + // Constrain `b_vid` to the generalized type `generalized_ty`. + if let TyKind::Infer(InferTy::TyVar(generalized_vid)) = generalized_ty.kind() { + self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + } else { + self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + } + + // See the comment on `Generalization::has_unconstrained_ty_var`. + if has_unconstrained_ty_var { + relation.register_predicates([ClauseKind::WellFormed(generalized_ty.into())]); + } + + // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + if generalized_ty.is_ty_var() { + // This happens for cases like `::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + let (lhs, rhs, direction) = match instantiation_variance { + Variance::Invariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) + } + Variance::Covariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Contravariant => { + (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Bivariant => unreachable!("bivariant generalization"), + }; + + relation.register_predicates([PredicateKind::AliasRelate(lhs, rhs, direction)]); + } else { + // NOTE: The `instantiation_variance` is not the same variance as + // used by the relation. When instantiating `b`, `target_is_expected` + // is flipped and the `instantiation_variance` is also flipped. To + // constrain the `generalized_ty` while using the original relation, + // we therefore only have to flip the arguments. + // + // ```ignore (not code) + // ?a rel B + // instantiate_ty_var(?a, B) # expected and variance not flipped + // B' rel B + // ``` + // or + // ```ignore (not code) + // A rel ?b + // instantiate_ty_var(?b, A) # expected and variance flipped + // A rel A' + // ``` + if target_is_expected { + relation.relate(generalized_ty, source_ty)?; + } else { + debug!("flip relation"); + relation.relate(source_ty, generalized_ty)?; + } + } + + Ok(()) + } + + /// Instantiates the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self, relation))] + pub(crate) fn instantiate_const_var>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + Variance::Invariant, + source_ct, + )?; + + debug_assert!(!generalized_ct.is_ct_infer()); + if has_unconstrained_ty_var { + panic!("unconstrained ty var when generalizing `{source_ct:?}`"); + } + + self.inner + .borrow_mut() + .const_unification_table() + .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct }); + + // Make sure that the order is correct when relating the + // generalized const and the source. + if target_is_expected { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + generalized_ct, + source_ct, + )?; + } else { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + source_ct, + generalized_ct, + )?; + } + + Ok(()) + } + + /// Attempts to generalize `source_term` for the type variable `target_vid`. + /// This checks for cycles -- that is, whether `source_term` references `target_vid`. + fn generalize> + Relate>>( + &self, + structurally_relate_aliases: StructurallyRelateAliases, + target_vid: impl Into, + ambient_variance: Variance, + source_term: T, + ) -> RelateResult<'db, Generalization> { + assert!(!source_term.clone().has_escaping_bound_vars()); + let (for_universe, root_vid) = match target_vid.into() { + TermVid::Ty(ty_vid) => { + (self.probe_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid))) + } + TermVid::Const(ct_vid) => ( + self.probe_const_var(ct_vid).unwrap_err(), + TermVid::Const(self.inner.borrow_mut().const_unification_table().find(ct_vid).vid), + ), + }; + + let mut generalizer = Generalizer { + infcx: self, + structurally_relate_aliases, + root_vid, + for_universe, + root_term: source_term.into(), + ambient_variance, + in_alias: false, + cache: Default::default(), + has_unconstrained_ty_var: false, + }; + + let value_may_be_infer = generalizer.relate(source_term, source_term)?; + let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var; + Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var }) + } +} + +/// The "generalizer" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the term `B` -- this replaces +/// all the lifetimes in the term `B` with fresh inference variables. +/// (You can read more about the strategy in this [blog post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// [blog post]: https://is.gd/0hKvIr +struct Generalizer<'me, 'db> { + infcx: &'me InferCtxt<'db>, + + /// Whether aliases should be related structurally. If not, we have to + /// be careful when generalizing aliases. + structurally_relate_aliases: StructurallyRelateAliases, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + root_vid: TermVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + for_universe: UniverseIndex, + + /// The root term (const or type) we're generalizing. Used for cycle errors. + root_term: Term<'db>, + + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: Variance, + + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `::Assoc>::Assoc == ?0`. This equality can + /// hold by either normalizing the outer or the inner associated type. + in_alias: bool, + + cache: FxHashMap<(Ty<'db>, Variance, bool), Ty<'db>>, + + /// See the field `has_unconstrained_ty_var` in `Generalization`. + has_unconstrained_ty_var: bool, +} + +impl<'db> Generalizer<'_, 'db> { + /// Create an error that corresponds to the term kind in `root_term` + fn cyclic_term_error(&self) -> TypeError> { + match self.root_term.kind() { + TermKind::Ty(ty) => TypeError::CyclicTy(ty), + TermKind::Const(ct) => TypeError::CyclicConst(ct), + } + } + + /// Create a new type variable in the universe of the target when + /// generalizing an alias. This has to set `has_unconstrained_ty_var` + /// if we're currently in a bivariant context. + fn next_ty_var_for_alias(&mut self) -> Ty<'db> { + self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant; + self.infcx.next_ty_var_in_universe(self.for_universe) + } + + /// An occurs check failure inside of an alias does not mean + /// that the types definitely don't unify. We may be able + /// to normalize the alias after all. + /// + /// We handle this by lazily equating the alias and generalizing + /// it to an inference variable. In the new solver, we always + /// generalize to an infer var unless the alias contains escaping + /// bound variables. + /// + /// Correctly handling aliases with escaping bound variables is + /// difficult and currently incomplete in two opposite ways: + /// - if we get an occurs check failure in the alias, replace it with a new infer var. + /// This causes us to later emit an alias-relate goal and is incomplete in case the + /// alias normalizes to type containing one of the bound variables. + /// - if the alias contains an inference variable not nameable by `for_universe`, we + /// continue generalizing the alias. This ends up pulling down the universe of the + /// inference variable and is incomplete in case the alias would normalize to a type + /// which does not mention that inference variable. + fn generalize_alias_ty( + &mut self, + alias: AliasTy<'db>, + ) -> Result, TypeError>> { + // We do not eagerly replace aliases with inference variables if they have + // escaping bound vars, see the method comment for details. However, when we + // are inside of an alias with escaping bound vars replacing nested aliases + // with inference variables can cause incorrect ambiguity. + // + // cc trait-system-refactor-initiative#110 + if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { + return Ok(self.next_ty_var_for_alias()); + } + + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(alias, alias) { + Ok(alias) => Ok(alias.to_ty(self.cx())), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + alias.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !alias.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {alias:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.next_ty_var_for_alias()) + } + } + }; + self.in_alias = is_nested_alias; + result + } +} + +impl<'db> TypeRelation> for Generalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn relate_item_args( + &mut self, + item_def_id: SolverDefId, + a_arg: GenericArgs<'db>, + b_arg: GenericArgs<'db>, + ) -> RelateResult<'db, GenericArgs<'db>> { + if self.ambient_variance == Variance::Invariant { + // Avoid fetching the variance if we are in an invariant + // context; no need, and it can induce dependency cycles + // (e.g., #41849). + relate::relate_args_invariantly(self, a_arg, b_arg) + } else { + let tcx = self.cx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_args_with_variances( + self, + item_def_id, + opt_variances, + a_arg, + b_arg, + false, + ) + } + } + + #[instrument(level = "debug", skip(self, variance, b), ret)] + fn relate_with_variance>>( + &mut self, + variance: Variance, + _info: VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'db, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + debug!(?self.ambient_variance, "new ambient variance"); + // Recursive calls to `relate` can overflow the stack. For example a deeper version of + // `ui/associated-consts/issue-93775.rs`. + let r = self.relate(a, b); + self.ambient_variance = old_ambient_variance; + r + } + + #[instrument(level = "debug", skip(self, t2), ret)] + fn tys(&mut self, t: Ty<'db>, t2: Ty<'db>) -> RelateResult<'db, Ty<'db>> { + assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + if let Some(result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) { + return Ok(*result); + } + + // Check to see whether the type we are generalizing references + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. + let g = match t.kind() { + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("unexpected infer type: {t:?}") + } + + TyKind::Infer(InferTy::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let vid = inner.type_variables().root_var(vid); + if TermVid::Ty(vid) == self.root_vid { + // If sub-roots are equal, then `root_vid` and + // `vid` are related via subtyping. + Err(self.cyclic_term_error()) + } else { + let probe = inner.type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe } => { + match self.ambient_variance { + // Invariant: no need to make a fresh type variable + // if we can name the universe. + Variance::Invariant => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + } + + // Bivariant: make a fresh var, but remember that + // it is unconstrained. See the comment in + // `Generalization`. + Variance::Bivariant => self.has_unconstrained_ty_var = true, + + // Co/contravariant: this will be + // sufficiently constrained later on. + Variance::Covariant | Variance::Contravariant => (), + } + + let origin = inner.type_variables().var_origin(vid); + let new_var_id = + inner.type_variables().new_var(self.for_universe, origin); + // If we're in the new solver and create a new inference + // variable inside of an alias we eagerly constrain that + // inference variable to prevent unexpected ambiguity errors. + // + // This is incomplete as it pulls down the universe of the + // original inference variable, even though the alias could + // normalize to a type which does not refer to that type at + // all. I don't expect this to cause unexpected errors in + // practice. + // + // We only need to do so for type and const variables, as + // region variables do not impact normalization, and will get + // correctly constrained by `AliasRelate` later on. + // + // cc trait-system-refactor-initiative#108 + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + inner.type_variables().equate(vid, new_var_id); + } + + debug!("replacing original vid={:?} with new={:?}", vid, new_var_id); + Ok(Ty::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(t) + } + + TyKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(t) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + + TyKind::Alias(_, data) => match self.structurally_relate_aliases { + StructurallyRelateAliases::No => self.generalize_alias_ty(data), + StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t), + }, + + _ => relate::structurally_relate_tys(self, t, t), + }?; + + self.cache.insert((t, self.ambient_variance, self.in_alias), g); + Ok(g) + } + + #[instrument(level = "debug", skip(self, r2), ret)] + fn regions(&mut self, r: Region<'db>, r2: Region<'db>) -> RelateResult<'db, Region<'db>> { + assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match r.kind() { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + RegionKind::ReBound(..) | RegionKind::ReErased => { + return Ok(r); + } + + // It doesn't really matter for correctness if we generalize ReError, + // since we're already on a doomed compilation path. + RegionKind::ReError(_) => { + return Ok(r); + } + + RegionKind::RePlaceholder(..) + | RegionKind::ReVar(..) + | RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReLateParam(..) => { + // see common code below + } + } + + // If we are in an invariant context, we can re-use the region + // as is, unless it happens to be in some universe that we + // can't name. + if let Variance::Invariant = self.ambient_variance { + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } + } + + Ok(self.infcx.next_region_var_in_universe(self.for_universe)) + } + + #[instrument(level = "debug", skip(self, c2), ret)] + fn consts(&mut self, c: Const<'db>, c2: Const<'db>) -> RelateResult<'db, Const<'db>> { + assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match c.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + // If root const vids are equal, then `root_vid` and + // `vid` are related and we'd be inferring an infinitely + // deep const. + if TermVid::Const( + self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid, + ) == self.root_vid + { + return Err(self.cyclic_term_error()); + } + + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + match variable_table.probe_value(vid) { + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + ConstVariableValue::Unknown { origin, universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table + .new_key(ConstVariableValue::Unknown { + origin, + universe: self.for_universe, + }) + .vid; + + // See the comment for type inference variables + // for more details. + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + variable_table.union(vid, new_var_id); + } + Ok(Const::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + // FIXME: Unevaluated constants are also not rigid, so the current + // approach of always relating them structurally is incomplete. + // + // FIXME: remove this branch once `structurally_relate_consts` is fully + // structural. + ConstKind::Unevaluated(UnevaluatedConst { def, args }) => { + let args = self.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + args, + args, + )?; + Ok(Const::new_unevaluated(self.infcx.interner, UnevaluatedConst { def, args })) + } + ConstKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(c) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + _ => relate::structurally_relate_consts(self, c, c), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn binders( + &mut self, + a: Binder<'db, T>, + _: Binder<'db, T>, + ) -> RelateResult<'db, Binder<'db, T>> + where + T: Relate>, + { + let result = self.relate(a.skip_binder(), a.skip_binder())?; + Ok(a.rebind(result)) + } +} + +/// Result from a generalization operation. This includes +/// not only the generalized type, but also a bool flag +/// indicating whether further WF checks are needed. +#[derive(Debug)] +struct Generalization { + /// When generalizing `::Assoc` or + /// `::Assoc>>::Assoc` + /// for `?0` generalization returns an inference + /// variable. + /// + /// This has to be handled wotj care as it can + /// otherwise very easily result in infinite + /// recursion. + pub value_may_be_infer: T, + + /// In general, we do not check whether all types which occur during + /// type checking are well-formed. We only check wf of user-provided types + /// and when actually using a type, e.g. for method calls. + /// + /// This means that when subtyping, we may end up with unconstrained + /// inference variables if a generalized type has bivariant parameters. + /// A parameter may only be bivariant if it is constrained by a projection + /// bound in a where-clause. As an example, imagine a type: + /// + /// struct Foo where A: Iterator { + /// data: A + /// } + /// + /// here, `A` will be covariant, but `B` is unconstrained. + /// + /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`. + /// If we have an input `Foo`, then after generalization we will wind + /// up with a type like `Foo`. When we enforce `Foo <: Foo`, + /// we will wind up with the requirement that `?A <: ?C`, but no particular + /// relationship between `?B` and `?D` (after all, these types may be completely + /// different). If we do nothing else, this may mean that `?D` goes unconstrained + /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases. + pub has_unconstrained_ty_var: bool, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs new file mode 100644 index 0000000000000..bb80c5157109c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -0,0 +1,89 @@ +//! Helper routines for higher-ranked things. See the `doc` module at +//! the end of the file for details. + +use rustc_type_ir::TypeFoldable; +use rustc_type_ir::{BoundVar, UniverseIndex}; +use tracing::{debug, instrument}; + +use super::RelateResult; +use crate::next_solver::fold::FnMutDelegate; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::snapshot::CombinedSnapshot; +use crate::next_solver::{ + Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, + PlaceholderTy, Region, Ty, +}; + +impl<'db> InferCtxt<'db> { + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe. This means that the + /// new placeholders can only be named by inference variables created after + /// this method has been called. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// `fn enter_forall` should be preferred over this method. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self), ret)] + pub fn enter_forall_and_leak_universe(&self, binder: Binder<'db, T>) -> T + where + T: TypeFoldable> + Clone, + { + if let Some(inner) = binder.clone().no_bound_vars() { + return inner; + } + + let next_universe = self.create_next_universe(); + + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| { + Region::new_placeholder( + self.interner, + PlaceholderRegion { universe: next_universe, bound: br }, + ) + }, + types: &mut |bound_ty: BoundTy| { + Ty::new_placeholder( + self.interner, + PlaceholderTy { universe: next_universe, bound: bound_ty }, + ) + }, + consts: &mut |bound_var: BoundVar| { + Const::new_placeholder( + self.interner, + PlaceholderConst { universe: next_universe, bound: bound_var }, + ) + }, + }; + + debug!(?next_universe); + self.interner.replace_bound_vars_uncached(binder, delegate) + } + + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe and then calls the + /// closure `f` with the instantiated value. The new placeholders can only be + /// named by inference variables created inside of the closure `f` or afterwards. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// This method should be preferred over `fn enter_forall_and_leak_universe`. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self, f))] + pub fn enter_forall(&self, forall: Binder<'db, T>, f: impl FnOnce(T) -> U) -> U + where + T: TypeFoldable> + Clone, + { + // FIXME: currently we do nothing to prevent placeholders with the new universe being + // used after exiting `f`. For example region subtyping can result in outlives constraints + // that name placeholders created in this function. Nested goals from type relations can + // also contain placeholders created by this function. + let value = self.enter_forall_and_leak_universe(forall); + debug!(?value); + f(value) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs new file mode 100644 index 0000000000000..836ae39dc5253 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs @@ -0,0 +1,13 @@ +//! This module contains the definitions of most `TypeRelation`s in the type system +//! (except for some relations used for diagnostics and heuristics in the compiler). +//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). + +pub use rustc_type_ir::relate::combine::PredicateEmittingRelation; +pub use rustc_type_ir::relate::*; + +use crate::next_solver::DbInterner; + +mod generalize; +mod higher_ranked; + +pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult, T>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs new file mode 100644 index 0000000000000..84338ade6e354 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs @@ -0,0 +1,62 @@ +//! Things for resolving vars in the infer context of the next-trait-solver. + +use rustc_type_ir::{ + ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap, + inherent::IntoKind, +}; + +use crate::next_solver::{Const, DbInterner, Region, Ty}; + +use super::{FixupError, FixupResult, InferCtxt}; + +/////////////////////////////////////////////////////////////////////////// +// OPPORTUNISTIC VAR RESOLVER + +/// The opportunistic resolver can be used at any time. It simply replaces +/// type/const variables that have been unified with the things they have +/// been unified with (similar to `shallow_resolve`, but deep). This is +/// useful for printing messages etc but also required at various +/// points for correctness. +pub struct OpportunisticVarResolver<'a, 'db> { + infcx: &'a InferCtxt<'db>, + /// We're able to use a cache here as the folder does + /// not have any mutable state. + cache: DelayedMap, Ty<'db>>, +} + +impl<'a, 'db> OpportunisticVarResolver<'a, 'db> { + #[inline] + pub fn new(infcx: &'a InferCtxt<'db>) -> Self { + OpportunisticVarResolver { infcx, cache: Default::default() } + } +} + +impl<'a, 'db> TypeFolder> for OpportunisticVarResolver<'a, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + #[inline] + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_non_region_infer() { + t // micro-optimize -- if there is nothing in this type that this fold affects... + } else if let Some(ty) = self.cache.get(&t) { + *ty + } else { + let shallow = self.infcx.shallow_resolve(t); + let res = shallow.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.has_non_region_infer() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let ct = self.infcx.shallow_resolve_const(ct); + ct.super_fold_with(self) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs new file mode 100644 index 0000000000000..eb426205575ad --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -0,0 +1,111 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use ena::undo_log::UndoLogs; +use rustc_type_ir::UniverseIndex; +use tracing::{debug, instrument}; + +use super::InferCtxt; +use super::region_constraints::RegionSnapshot; + +pub(crate) mod undo_log; + +use undo_log::{Snapshot, UndoLog}; + +#[must_use = "once you start a snapshot, you should always consume it"] +pub struct CombinedSnapshot { + pub(super) undo_snapshot: Snapshot, + region_constraints_snapshot: RegionSnapshot, + universe: UniverseIndex, +} + +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, +} + +impl<'db> InferCtxt<'db> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + } + } + + pub fn in_snapshot(&self) -> bool { + UndoLogs::>::in_snapshot(&self.inner.borrow_mut().undo_log) + } + + pub fn num_open_snapshots(&self) -> usize { + UndoLogs::>::num_open_snapshots(&self.inner.borrow_mut().undo_log) + } + + fn start_snapshot(&self) -> CombinedSnapshot { + debug!("start_snapshot()"); + + let mut inner = self.inner.borrow_mut(); + + CombinedSnapshot { + undo_snapshot: inner.undo_log.start_snapshot(), + region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), + universe: self.universe(), + } + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn rollback_to(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; + + self.universe.set(universe); + + let mut inner = self.inner.borrow_mut(); + inner.rollback_to(undo_snapshot); + inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn commit_from(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = + snapshot; + + self.inner.borrow_mut().commit(undo_snapshot); + } + + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. + #[instrument(skip(self, f), level = "debug")] + pub fn commit_if_ok(&self, f: F) -> Result + where + F: FnOnce(&CombinedSnapshot) -> Result, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok()); + match r { + Ok(_) => { + self.commit_from(snapshot); + } + Err(_) => { + self.rollback_to(snapshot); + } + } + r + } + + /// Execute `f` then unroll any bindings it creates. + #[instrument(skip(self, f), level = "debug")] + pub fn probe(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot) -> R, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + self.rollback_to(snapshot); + r + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs new file mode 100644 index 0000000000000..0fa6421f517de --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -0,0 +1,198 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use std::marker::PhantomData; + +use ena::snapshot_vec as sv; +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_type_ir::FloatVid; +use rustc_type_ir::IntVid; +use tracing::debug; + +use crate::next_solver::OpaqueTypeKey; +use crate::next_solver::infer::opaque_types::OpaqueHiddenType; +use crate::next_solver::infer::unify_key::ConstVidKey; +use crate::next_solver::infer::unify_key::RegionVidKey; +use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable}; +use crate::traits; + +pub struct Snapshot { + pub(crate) undo_len: usize, +} + +/// Records the "undo" data for a single operation that affects some form of inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'db> { + DuplicateOpaqueType, + OpaqueTypes(OpaqueTypeKey<'db>, Option>), + TypeVariables(sv::UndoLog>>), + ConstUnificationTable(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + RegionConstraintCollector(region_constraints::UndoLog<'db>), + RegionUnificationTable(sv::UndoLog>>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor:ident ($ty:ty),)*) => { + $( + impl<'db> From<$ty> for UndoLog<'db> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'db>), + + TypeVariables(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + + ConstUnificationTable(sv::UndoLog>>), + + RegionUnificationTable(sv::UndoLog>>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'db> Rollback> for InferCtxtInner<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(), + UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +#[derive(Clone, Default)] +pub(crate) struct InferCtxtUndoLogs<'db> { + logs: Vec>, + num_open_snapshots: usize, +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertible into an UndoLog (per the From impls above). +impl<'db, T> UndoLogs for InferCtxtUndoLogs<'db> +where + UndoLog<'db>: From, +{ + #[inline] + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + #[inline] + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'db> InferCtxtInner<'db> { + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + self.type_variable_storage.finalize_rollback(); + + if self.undo_log.num_open_snapshots == 1 { + // After the root snapshot the undo log should be empty. + assert!(snapshot.undo_len == 0); + assert!(self.undo_log.logs.is_empty()); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'db> InferCtxtUndoLogs<'db> { + pub(crate) fn start_snapshot(&mut self) -> Snapshot { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len() } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot, + ) -> impl Iterator> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } +} + +impl<'db> std::ops::Index for InferCtxtUndoLogs<'db> { + type Output = UndoLog<'db>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'db> std::ops::IndexMut for InferCtxtUndoLogs<'db> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs new file mode 100644 index 0000000000000..f1df806ab3187 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -0,0 +1,175 @@ +//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +use std::{ + cmp, + hash::{Hash, Hasher}, +}; + +use rustc_type_ir::{ + PredicatePolarity, Upcast, + solve::{Certainty, NoSolution}, +}; + +use crate::next_solver::{ + Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate, + Ty, +}; + +use super::InferCtxt; + +/// The reason why we incurred this obligation; used for error reporting. +/// +/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the +/// best trade-off between keeping the type small (which makes copies cheaper) +/// while not doing too many heap allocations. +/// +/// We do not want to intern this as there are a lot of obligation causes which +/// only live for a short period of time. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ObligationCause { + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: Option, +} + +impl ObligationCause { + #[inline] + pub fn new(body_id: SolverDefId) -> ObligationCause { + ObligationCause { body_id: Some(body_id) } + } + + #[inline(always)] + pub fn dummy() -> ObligationCause { + ObligationCause { body_id: None } + } +} + +/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for +/// which the "impl_source" must be found. The process of finding an "impl_source" is +/// called "resolving" the `Obligation`. This process consists of +/// either identifying an `impl` (e.g., `impl Eq for i32`) that +/// satisfies the obligation, or else finding a bound that is in +/// scope. The eventual result is usually a `Selection` (defined below). +#[derive(Clone, Debug)] +pub struct Obligation<'db, T> { + /// The reason we have to prove this thing. + pub cause: ObligationCause, + + /// The environment in which we should prove this thing. + pub param_env: ParamEnv<'db>, + + /// The thing we are trying to prove. + pub predicate: T, + + /// If we started proving this as a result of trying to prove + /// something else, track the total depth to ensure termination. + /// If this goes over a certain threshold, we abort compilation -- + /// in such cases, we can not say whether or not the predicate + /// holds for certain. Stupid halting problem; such a drag. + pub recursion_depth: usize, +} + +impl<'db, T: Copy> Obligation<'db, T> { + pub fn as_goal(&self) -> Goal<'db, T> { + Goal { param_env: self.param_env, predicate: self.predicate } + } +} + +impl<'db, T: PartialEq> PartialEq> for Obligation<'db, T> { + #[inline] + fn eq(&self, other: &Obligation<'db, T>) -> bool { + // Ignore `cause` and `recursion_depth`. This is a small performance + // win for a few crates, and a huge performance win for the crate in + // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly + // stresses the trait system. + self.param_env == other.param_env && self.predicate == other.predicate + } +} + +impl<'db, T: Eq> Eq for Obligation<'db, T> {} + +impl<'db, T: Hash> Hash for Obligation<'db, T> { + fn hash(&self, state: &mut H) { + // See the comment on `Obligation::eq`. + self.param_env.hash(state); + self.predicate.hash(state); + } +} + +impl<'db, P> From> for Goal<'db, P> { + fn from(value: Obligation<'db, P>) -> Self { + Goal { param_env: value.param_env, predicate: value.predicate } + } +} + +pub type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; +pub type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; + +pub type PredicateObligations<'db> = Vec>; + +impl<'db> PredicateObligation<'db> { + /// Flips the polarity of the inner predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(&self, tcx: DbInterner<'db>) -> Option> { + Some(PredicateObligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: self.predicate.flip_polarity()?, + recursion_depth: self.recursion_depth, + }) + } +} + +impl<'db, O> Obligation<'db, O> { + pub fn new( + tcx: DbInterner<'db>, + cause: ObligationCause, + param_env: ParamEnv<'db>, + predicate: impl Upcast, O>, + ) -> Obligation<'db, O> { + Self::with_depth(tcx, cause, 0, param_env, predicate) + } + + /// We often create nested obligations without setting the correct depth. + /// + /// To deal with this evaluate and fulfill explicitly update the depth + /// of nested obligations using this function. + pub fn set_depth_from_parent(&mut self, parent_depth: usize) { + self.recursion_depth = cmp::max(parent_depth + 1, self.recursion_depth); + } + + pub fn with_depth( + tcx: DbInterner<'db>, + cause: ObligationCause, + recursion_depth: usize, + param_env: ParamEnv<'db>, + predicate: impl Upcast, O>, + ) -> Obligation<'db, O> { + let predicate = predicate.upcast(tcx); + Obligation { cause, param_env, recursion_depth, predicate } + } + + pub fn misc( + tcx: DbInterner<'db>, + body_id: SolverDefId, + param_env: ParamEnv<'db>, + trait_ref: impl Upcast, O>, + ) -> Obligation<'db, O> { + Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref) + } + + pub fn with

m%Wc2r_mQBjet(QI+`v=+sawb7ftVOe;HW<>17 zX>W#H>6-ibS)db3dldiJ*jOjhk~y^D$Dkm72tq?${V<^E%a+7c*^_!Nz z-<4^ao0}^ux1HXnyLxqHWyP=HiwtO9TR(D?;}zSBW~96~el6k4!xyZKP^f3)Td}dR zBBqNm{{ZTbS6rKJJU-f8J*tHvnwOXD61(oet$t)ylM4~b-g?r^v$DE+;b}?{wT5Ph>U;O6H2NKFwRvd4+L+)QgquO?-aurM+-6bZSH1eF6u4p6q8$a&tH^mJx&Rvi#vG-u|TQzWMfid0a4~-#g0TghJuupr6BQb#4NZN4}AsPr(|sb{5M0Zun68DNwZA9`p|Hxc zx~>fJSbe%cO!0}4phyvP6(vxq6YIJk>b%xdh#t`cOGM`bOz+$Y{oF=eEUiolrQV6% zRh5-?YT&A%68r}iD@di~)ZNJIV9M-Tof6KP#%N1T%~bZ)A3M_Q%3PaWOGaf(K4k=d%E-u!U0&j*dmEn)P1ml5X?u zHW`77yMADJ2K;(nIS>a!!R0yIOTo@?{rWvk&Eqo>u<8WI394S-JulO1gI0g|KRR{W z$`^BN-qQWQtd=(aq5bc_EwZdk{+h1}Mg6*55-axjFN84ot4U(Wo|p$YO1QB za!dpgC)Url&A?(ecpi%pJpaDb>zp@WS3R-1vZAKeW!mJImYU*qGMMZnwO^!PZW?4= zdiI<@=Cd4uzDTeZ3J&P7p``IBido!lqnU%cLEEPHeD65TTcS4CYGIe0oFaT~XQ`L} z{7#_gAu?9u@G12JWp{V5b?3##(h8bQscKC5&-A4dMjTVp3wSzCMBjsUnogUjNCq*# z$yl1geJ1IEIbFBC0UH*%_`A7ry43B>*NprYOa1p)Qbe4rb;1R<3%4<}NP;FjRC@qc z_PsJzfp4S;6SU){U>-ABp%cbso*=+1>xE(#*Sd`wm4qQJpEYOChviAXny9l#G~Q_p z+NsLU)^fhfd&%C`)@N&F_s69?rRa>yj8~lIOVbc5#NbVZxQFG>R{n_r;a~?_TOHY@ zC%RSAUGwYOsC+zmc$9S6Z3Sol=~{2hhYw2AqdH9*vWs87Dlar@A1T96X+g`23ngZ% z+e;C3nO!IxacbEV{i7*v5dVaA+1AzPA`Lq_h=3I>^f|4r*syv_MYpQKy5Pjr$z&>w ze_}ioO$8>GgIlQ4O1*PWhU!WG-=98yJQ7Tlly4&UrP)hdE}vh}2b~HJdmDaC!FaT| zF16dM;7qYOUXK;8{)j|_6}W-!r$^l&=6RcD84-IVG}8VSMfXut89g*9J|nn1_R8ja zvL=e+j?a>UNnMhOhmkmk#%TfRq*;W-AU61L9H)1Un_HXCFWL7-=Fl@N^x5oASEeNLMX z@d?%CEzN(iIsO)b&x&rkA+^7zZQFQE$=peO8D;ASY)&m*9lMe?J`J5LeG52U~ALhjhv5 z_T!Y_ynRQD%_W9pb<6PSmTIRzYw%Tr@~xaj3v2QktIho7n35%!l#~N{@a0mQ{AxL^ zV0<2{e>VcGp?#C(Zrw)-30D@Vq-R>fV&$Rbp` zI^U(NS^N6b`eQ-&g9Lr(P0wrMvD5eT?C#PlEuE^Vbl5yI*txRzORDcG&AGelFO2Na zW}Cd)BU>GhwvxHvWX^L19*>yf(HWyJ3yTX>IJ*aOZOkMr?)s{xaJftxHw1VNR5A58 zC@3nodaPMTy(0UhbavVDG-bzeA?@DsWBq&KK!XX!v;N?e*h<=ScQ@7-XN}K$aasX0 zry;_Oso6KhsxEo&JAbSLl~=L3d9=Uv>!a+ zsg_*eA9zZ_CbCh%lbEzaD_EFcq!Si%xVpJ*gfsKoj=O78LvQw3%JsrGw5lvNRxGzX zEdLy+l;!CAtDzNwW_x6gjB7kv7TkC)aP#v6v;V4a-~M9&WhZU;*R2DUs>!`I;TvE2 zjJ_2cY2}cdUh=bV}|R zu&JGL&h2OJMH&>ve@D|SH+z; zaDU!2USclOtLZnN=#lw*{qyZ_E0RBgHlHf~{o9U~k&}-eT4m+oryldH$J^Va5K$md za%maQP!ZT*921yn+E3 zJXSd3;^wyFSoJfuzyDQd=gJD08pl({4U_AR{0s<3M+e{E3x{g)UWjO!GWMzPl7O_w zfxcgB>7A;_f{xqS^pO7&`1d!gK|?b!ePCzn_@`Mm!w*WkH@hTA^P5b>?2)4to^6}n z8$m}pKRqOA;Glp0qTF=n)JP53+8%oKN|g|YPWR`{2W*$P7-hY(Ge7<6N)#joR7#6} zLtw~erJ|^67&fr11%b+nJ`*l$cNwaSYT3l{LAxgcg-LnyBB_!=yh~DU;~4}hIGXrv z)fp+l%s2(cSAcPlrNlQD?@f?RgixSRzigE2j1o#s^>lShX8w|bU)|oy`Pv~FAtf&T z*Mu#4PMWX1d$t6bY&*XHc-VP#&Gpu0X=NO@$zHB7t@AVa;*KN89hIkgrC6Qcj1VN< z4GvYQuM1}+aH}XDDG;;g}t078=*bv znD(9e%)gK^H(WQ=GW>9HrhCHM#BXnRMaKLuT#dxBad^^xu{4ifzQKkAcT2&dj&16C zP`5C@*y6b{yWXpTgPCqBtFUXKzeyo-?L2#S?r(7d9s0{&v)d01M!~JDYCsBj!|i-^ zn%H>tspf1c+k=C@`oXXf7w)dgXHc89^si?r7-V`1j`6Ze@}7e`ydYN7*}3Di6iTh0 zoFL>O%;@_XbJ~U+OfmvIP!52D-$&gOew2DCvi|8+(a?ARL-{Oi1m;FhtTT)GU59|J zK%xyaGk5}1HEa@1PydoeC6nY9NYLEqDd%dX7HE2rr$imNyN-fB7cWar9)+B}>4za0 z`|;sZ`(iXMJm#)G_nb|e`tzEazEBq&zWNQCFGP-CLFxcgG|)GAo7937vijR*Z4-)} z{sfVLvA4aeurA>>R0a2UYA^XI{_?6Prew(7Vz8y94w~(!$?e$EwgFX<1~=EgN4(Au z)stE;KPP^}VfxyBqM+(q;Uz(kZjw~ZZ(iuHBO5#x3_+hsh-R56@ZUP7RL$5)ECIs7 z?=XjN^{K|W5A6(mmzV=RCNGow$Ho^|)IH%#hUz25EGb}XrLx~?yvTRjOx{fCVvz=)?W|;oyU8dK zq*WE*Fc7*^mXRuy#N@vRhz(e6SH8$=@n+p*k43WoO2*B;tF@am=Jiuqkz9h9f2irf z=wqu0_y5DBZ={zO`DNX!tHdH^D-AQ-L9ONIE3C^~7^y1I3B^3$oo9AvRXjdMx~DT? zBFXGr>wEh*63ponJ^JbJ=!~C|`>fE!RFz!*=(7-AZAg85s-*RaVA=9L2U_?Z^9{?d zi%u@sh4w=3PIgmFAnnv=J|saVF*0(s|0`(bb1{l3S_8tvj-%3+-{@-1t}wADEYK~t&emDIYew}J`neNLyNR)7CoCF3^*XbRn&Af&x~g~!5OGL1zx zDB$3iieYPMnana^f*#WvcD*R?jp@W(TCZ@$nbF346~{69(4pZfL^se%*#!VONUs zdM|slcXv^eMsPx)FD{Q41}m6nZR_Rrn(9|S>H0C37dNs#+8Eua1WZ-Dmw`vJySv+Y zo+{z3Q1Wtt0e&%x_j(zRpj)l6%rW^jNX7%}{y}rm53U*J|x8Hr?E$ zyjJf(@Up#b5OX{0w_1n*UH|MmPYvkLWD&1)j3m8&4g&7SQME@91%*-BV^2@@C`uE! zm|5c!;VSKYxkGaOoS*ae;X~G~>M7UG`Eh#xL^4X<#AM!2HUni%mRNFdvv2%kG**CJ zT0+#M@+oJ%%=Nkpkeli^6tW(46xelE*gLH5G2a@Trk)*ZORP`yr(_5JIxlcpnaye2 zhH4LGo1&+(za}?0I+}kkw{|Q!{csi3gc*i#YJR!AJVVyy(2E$+sU(IoVC)Zz(8-sa zwodj9xd8TkPt1A-(Xi8a|LExZCPNjwWXB41$Z>7wUKz`SUvU*4wb=ZN1!ZL`lc8#g z%ABgz<3AaG4L0rsLa><{(W(A37tW9&%>j( zo5fif(9Du<#@U6^Wmd-;5NJou9;-4lqa6dHm6fU){bglyc|8{yM0K~ zX4hZ!-6;E(?NAy-mT(=D7Ry_>>#(}Q?zxiTfA-S~thf-mfv%P>pG_uD)>({zK^fyq zWm3|6N9^6aS4+z7)bYg|^I7E7EuX;NT#BvO)pfHEQh2(bexDs~<^tzNDNteaWM)2QWdOjofVyL#B Tb%jNN=)hF&XxuJAJ$m+EN?yW= literal 15341 zcmdVAWlUXP^fq{rqQ$j1rFe07cPLU^Tio5kkrr2hK%g~Wz6j9Z3JWY+1O$R+ z<*BOatn%57%-+$?)WX_?%-O@+8qCk)ezImV@I{p7W2C8LbAkp>XD6zNy@Rbx;qk;`|E?SOCBtK~e<`lJebqWF zU#7p(*`z*m8uJJqgTC>+BKLhVF+G#wCSmf_Qoltn+PjPu(v7L=?r(YX%7a*Ib(H4n z)X0`}({0pp@33!Qk}ldlaW7Xt$#JYv8PTjY2B)1~DD}d#gKupFO=w`zuw=jWTnbI} zx1#jt5R4D=*WBI4;WG8A(QiL)w#^zQGG0(oEVyy2=p8!rNK#RyM(8HZH3jq8Hk{#? zvz#L>(*`8RZC$S&Z-sO``d;Tcl@BA@dqv?~V3sizeon1fTb2;*^5|t>yMeprzKJnN znwN&VKyfk(=swuON$ZYT4Cfk~Fzc%r5W1tBzVwD7H#{6R7JUcPuC$s1Yan5X@Vh!L z``0ifl`<>(&!L|w1^M{M0~Urx7xF0A`UB^UuUlik%15ZGOl^^A(e9}JX^=!jNjCbx zv-i!)wrNkzc7@6nFrW!mh6Z+Y^qivv%@%qEhoybTTB;!_X zI2!Znj9Xk4%%O~(VYe{GK%HS$9Y^&ak#WCjgSN5sovtaI40cB_R)j{?%!Ei={81jiOh!l#tx3GCFMe~cnJFNbo}Q*$otpxWP~fX4wA#tZSDT- zSMl3n`Ly%ncZI|A!*%z~$qj^3_<8l^7VIiHB6=FH?i4GOlSXUb z;b*Gn%v(|Ly_RBiUH?4UK%0$wgv7(=3Y)c`%EF`9lC_ga;$I-!7!yYGk)K+b5L;P? zk^xD}#X^?Dauj<|JDrgahWWyX+nqLpRq^7g{5U0sRI4HU2fmuDZN1n~ML`ZVyC|tc zQuCIk$gf`I0|E}dzhNQA?v{*r?7`P?vS-`Ge0u%&idtghM(oeIjLBpTgSxb&3)7b7 zR`OCe-?G{*(TZs6s_N`go!@^XEh@!d8gWv^VzT<}SwZ9)QICsiv;y16FvyEu6oF6x+iWO*FClI+wFTF3PZEK@&^FDzyE zZ6{ij`>2~tTYybG%`X3=paN!hMIGJWFF0Rz^UmazAvb^cS(9UX8l41+VmpnymsK7% z|MPfL@?T})R^A`Bao zbkLt$!mGIUYg*-~6xQVQNt;Z^5qUDw=R+{_9h7?B3)qGiYt4kZyk|D{!J5GFTu7M7 zQ8c_vk1qJ|r!=!Iq9_3>w&(dt*zR@s7o^`29 z=i4_((oLVx*Tbe$*5TBDa3*MA+-#?-tv`LfZB>u)q9w)pP4h1s`A>oO`H+C!;2xQI zda)u8ukh?tsj#WiKhcp3?6btmb)7c{waBhgtjpeG{)+Z#B)`YjqgkJoo|vdzhktNl z1TlUz2(C7*Dx*M-G=<_0SGQu3k|a!*6E(DIps7MeH245)}!3hy@vb_Am4!IRA+y?u5M0VIDzR_tNoj zTCA9j8DV{uVjoI>RS2e%b7MTedEM9-#)%E>^D1Bhp=jKf>=R99hZKv2i2oucfm+PK zSW|_DCCb8i&Ol3)v872y)L;^ZI*}8el|5Ctav>r)CV_U-5qwo{otbSW7Mfk|_dum? z#&X+Xcj47|zQ#-uTkCNVy?_IsF2W>d0^;g$2_vWjeXTE4!9=8_+%5lZB2QPxXquu< z#ot|_yr;PP3~z55DWOkEw)6={A>i-Mo8E!ecAnp9D)WWJb{h&pOh}`!GktSV9T@{d z;>+o&_-t9YGs0%b+uY?7dB((Yi5Y~xN*SDMaBUw8By2bfk@Zki2^$mX{GyWF{_t^* zQd{pjvHHVWuOX&k1(bGg=X>!fe~Q%2K6o0(ZA>$!K?-Mz(4e)boD>@ zyRG=R%BXl<%}p#x+)EFq!?tX;5$4~I=IC7D#x1W@qznl9AnEU}dioIK#C3F}C;ib+ zKe8)0^h2%PNq%aG>O@1V6c`|26GdH$|4ty>M8uJgn}?)`zoj0pv8s-qQp0OG;r}M~ zN=Z3Hm0w}V(|vK;BkQ|kuL`}V@9!wzwclOlzIA77=y{ti-yboEDlkN_lo2^|3V5>; z1_SO9+?;TnVV|kF0*l{Si4p{Vf;b^m{B?S>z!`QP(#aHa?8fDNeG`GF_DAcrU<-?o zH3z16_TSFQt1B#_-2lGIM8Wi+9k;x~KH+Eb-mf1*DC0UFI1a>iIK_|7F`_lP31t%( zd#_?U=o2xDkFN%iB0nYbs#Gt9!v6DWmJt@z2=qNQ_%aDM@@mg`!}QyqB`;_Wz69Q? zb8}UzKQ^*KSc^OD_yJhtyl`J48^3P;xgGt93HkR)sAL&_rG@&$X`@@SN^D1r;Kb{Y zp{i=MKdO0o>uQcVdu-u&swn=!GJM7-Wy4Gk*#Pa*=-YAiBHG0RZ9+#({=GhpAF0f> z2hGRgc9gGWPZp^u4o07SM8*s{nGDfLs1O=Tds&0 z-_n}MJeGIwzTq$oVYdlN5TAy8E(_eRaW+bO(tN9aFqV83McJIp@iaxr3oA(XuD_I8 z1HXp+wbDtqc-MY^JF3j2L(j7lW+FhXLTUQmJkfNEgoux3mghf9 zbGk1cJpzUEnaHeO;giQFFFP{h-jhS9Or{$+ya-JRqo`mWf?GK9kaL-?HPRIgQl&pB z#a}r0>))8J=8i>tg((pen{Bvm9J3Y0sEe-m#+DbUO&ledxYZan+{HU4M9s!Am^V+Dl5|TR^vyUYY!z8Hs>Tq!pV^*9`r+14j)i;=+E%U;a;-Q9~Su)viFboV%2# z(4BJkw#gv*SP+e}QA zV0|p($eW%)W$FcI1tMM2h_|i*cd;bQP0|8~K6cbHx{d9=#roD=@7hIcVNgBc3)QG> zvA-pkah-*~qi@Ebd1Op^<#+wvW-c}^-S4By9UO`ZTee{3wjHZMUB~^$8vlb2)5jIW z)q|2JB$0P&bu#NSJqV7#@hlM3Kdk8uV&x%;@1D6&9Eey4PbZ6 z+tT$wj9vDjJLAa1+;fZxk%;uW^N2cwKNQ6dXOt5}xP;o^wKnaOBx4OxH^Yr>%=<0G z3I}U+y*E<|xK&O{=a}OAyz|61)<`|!pE6%7rQh}xBI(K`xU=6LA&oU`+s~GHUm6`E zrPTFs;H68t{muTw?5y-%X+u$Zvh`CNCv4HtsE16c(_12(^q*BzN*5Jo>@Mk)iqHYE zoui4tPJhj_P2h5-_ANNY-@luBZOS@}EE3RXJ=BfqyXo^QVO0-z96e-U#J$Lt61_U7KR~?UTK?1-jB?^0>9w2-ad`&_k{KbFS=4)!=0v z^xE9o7|!6{aW_h~>R#7R(O%|3UPMOzV3T!Cmcq86gS5p;l2f}c>b;j+ojAL=s+_;< zD3r14s}!D*c)kRD+K-Ryn=LkkMfPX)gP3}L(bC5yREfAQ+W*Rb##kX~r!%hd2JyFa zF=HCcl05nl!u+nXCqt_XjLwovHqHXYtY{(T67+)^ho-R><@W&*?@lkRZrXWsRn}`G zJoK;}44(NSm~ITixB_Oq0w?dOc>Pw`Zt|lhVvn!iPNiw%z2emO=Y^L1>!M!E%vj|4 z4b?}9(&IT14$yD*)pKZ?eMf%O|BCk~xd+2LEE#`swA&j>j#qB^$|nao$E40Chju9L^U3B5p9EcLxv+1yC0kgmA?uGQ6tXy*7W#ekNLbdeYPJfIu}Ng~Y58u=!jJeW zB6;kmy$0uvcE=yzJ|mO3p+&dr6hBqNUwpDy`ufraJ+pu3f}ZH??r{YWIFq*S_WYq6DJ?T)Gv$r1LdP(`~&(ya`IAJ6++ zCw}`+|2DXKE_UPV^(1BT-uNqE9r$CG?>@m$x3;*mEmj{vAW*>;Vq!`%Vq*V`b_24V z;S(ny-K~TjqWH1yZ6c~Gx6}%~}|IpId-_bHg2U`95mEBVs z6j4)&S{X=o1GBmPC;!h|%GG3S?ceU*6r_7APVOgy!seupUErdg5W(LP>rJqK_Pz=ON{NbbAb=+6BGZ zd)r*E_bwAnDyIk}F(WhsVKI;_Y4V^69qtbDHKnI~yZVH-?2=6STAOXJ(g@y))gS{{s z2!W76WW+^O-4~Bm+`O?(HgC={D=b?6`q0Xd<-j4d(FHpC7tdD*&`1cV3ZoNxu4;Wy z{P&OBJ>dJg2aR;~clN((SozWjIq%+)w^=VW?mJDz02!RVC!O-G98&f{cU|O)BJjbNYGBw#%CH`eaQ=KtOA_(sT z6uKyFg+a&D&DoZTi3vG5xud&eWl4!c%gXb^o=}veQG{)AY3cs@ub_k33Y`{L8zZAU z)%%vu-Jv>mZHa!$5bQoh!S^5a^aLC>#Sz&~y^dQ>+9&d)8peZkDrOH44-@G%7#aHk32y)F&h+-kw6m zkO~R99WMhRbkuQFOH?s1Fy3=PM86sgjf^nR(3}p4HLV=alxf5e@i_jSEMQ&!7(eja z5r!=I21Sr3e$2Lk$)Noq%j>wLygXq;(Dm;mB|CdU9h@-28DabVF<--yPZW;#^W$}q zLORuw4-to@s=Yn46^cLlCRc`#Uwd=&U1>*KWu@)zaO#-geR;+;3j;$GIy9KyCNVKl zdp{W#78czg zLJ0Zqlf_z#k4{b(I6e{G8=5~p1-zvzRw>EN&DAskUfN+blE!g)d71otnM+ft(_s7b za2r0~(%9H|dTOu!20Y(`jf122glD%@fAqH~TYa$d@1^HxCddd5h-PD1ZNpW=LqlG- zJIOm9?dx`|=_4W{BJWv%SaRDV8}_>ALv(a>SsuHoLGWmF=5ws9tj@f|a<75bhQ3d? zdvf~D(>sn8dM&P7y-|aUb@et&=W)tFuH+vMTfJ;%qq+}Pu1AZPzp)f)Z09hsu(S+b zZn|#%4vToOv9YNbW+J`t!oL4BJz1 zucwESlF~s=A8hhDtHoq~wNc;I;Q|{KRj)a_B|Cu#`mm{!j7*pvS3+!TY)XnW0?1DN zJ%1+hvVHIV{tAMB-cJafpPx^f*Y)=1cY6!WO^zH-t(e99xobQ}3vg>08J91yNGTSN`g~a(S}fQd66(7k&o7zz=SW@YoI@`%{S4*1N6wT!#|c z#twXH#3F}8BTRUzeEjHkIPdUyIX7g?@3!KC2Xq5Q)E?tIX{E@1NtLT@USEQPe=tJ0 z--=sl)>+OpI&2wpCn~#*FK!yd+y;8^)1U9ov z-czfHlB6Jg^GX!>MdiT}!gMDt*z!h~yx0 zlD;J-x(qQiAOw;vVVD{l+f%;$&Sq{o)1|JF}Sm_s)VwU%m0@r{?u!Doc z@;5x7hg_EB^kQIcCB z$as}IVvM;oAi&4P&|D7Z<9eWl5jMOUR$SA6eL8(4u&M)FPb%o$ICp>}akg(Dpj75z zh5oWq(^fQJGjQ(QbP!6mcqed>fisdEUH+$=t&9wKNHDWKJ&7 zFbo8}OBj?I4*PZCT_^~`Xa6)_7dkgDc!((e_hHkE(~a&_cwkg0%i$Cj3k!??9)NIo z-`I~H!Mu!NWn;U)Jm}*GpzO8M^_ribq2b#LfYg|UQAqex#v-7>bzJ}^?}Gok|Nj`wof0#Agy?c8V-ur(=`k#+s%ls|ii(QDjp(tgLkdJEARxfS z#RX{>`~lDk2F7P*{Ao+yOIQT*bvyz>Ld8tpI)H4{%G8Mky>Gr5WM^dwI&A(vsOk`; zpa5ws4H1m|F3p+Fz{F&Cvf7r;W%qQ}M@mmm&vZd}adBbbzVRw=Vjh5-I0`A$H>~+m zF(g#+VYIKnQ}}zMnL5CaBbeIV8T#Uuz@T=Y7TE&N^8spVg|z7!Ger^f6dhsFRwF1~ zZ4r@Qfv=D!CnqZl0z1y1?oZ52OkTnhSUZ=7bpYVo?fxA{oB1CbB}G|j0$#3i{7)d! zbA9d#2FQnli;HdPWlWE04TEl@{lm?ff#0LckPW4f&~uF6!xq~ZKR_@e88D#l5&_W0 z8#dG744xVn&Ib3h&A(Gc*@8Z4sqhHoWNiSORoC%~f*v3_>@cSQOmTSBu;$vbQjRQ) z5TO1oKAz3*$@?~PFN$k*{>NvS188*7?;M&Hvkj}B2j^A9i@Yx}w$b%U9iA*$IAW>8 z?>UXbYMZmPx3?FR2v2?zTm*0fxwN=gv(>$tzwc^NjwzGZC7c5qfqXoW({{Ph^+?+z zx*|V+drDDgr*s(@QagOc@^L#}ahtyjWX{+rBq*Rn!o$NuCZF4&M=$_7K<&-%=w!kE zCjePX=q@<{&;~rO91qxuw5+5=tIf0Sp|8pN_Wb8h(1`&9V{mYAY*w1pFvJ905AhFP zp^K7EnX_ZsW|o-^i1_-ptAu)99csM{ZAZ(QJ4m!VP*YR$eh_h_{*a+Gs3a#Vd+5q1 z2Zt_dP+C=GXKo&Ac>op!V;&G>`?t=xGd~nW*4Nk18dm?X^dGZ$G*y(9lRI*4>Dy8+ zW72a|#y)br2T(xh{*Vo^uhn*?d87M_l%%Boy_iSaDlir(Zui?0(7{uGYZt6#-`MEs z=_x2Eh>AiZ!=rbtjZaUryX;R#5pvO4&4ZY^Jc4 z8nFKDfLtCBuf^@el&3^zkT6cLuGmPSUtrrHSi zwNo1R3hsF5>Vq!_PIDtKE&Wx!EZ+?t_$1>VgFSGT#`nMe`vCDkGE8B0JkCE?Dlir0Kz~0oK@=^VR&pWR- zx3^X3f(V+m zzdvdi%N8;a+-d?81R&3m8RM6j#B9?S0z2Ar+$={WRa|2_A}cR%toL#!YkPZJ=<%)|}8QL>u9zL5HA=1u$n zl?OJT9HDEz+UWXth0poV`0TQ+rRDzqOc_8jL_|cxbdccFLGwE8S(O46fC-rq1#>3M z;m}APe!-L0F1FF$T^+R;4}1gIfQ^Rc>n%LO1`aYJV%)P!yv{G+w5hP9`xAM6-zen( zjcH0+9jA!l|LJ+Tk3k`M+DNjLm>MRIHrW$XheE zU=7fFJp-T^AVUlK+1D&Q^b*=d5^^!}^JkR6fh1@Eh{nNOmEYBZ8A$a$mnl_kSBsz; z$H&K~>H`lLRMOLH0|XI}*hZ<{;*&+EaSWX!&MY8N_8&}lT` z!RQFipaRPXf3h?;Z=A0RlShY;#bSVN(f`N}(aZk*A0_|)tKB8cs$B?yREB_tk7XTi zrh3(sm5X+!Iv%$1OZzA2g_%*BKXq@Nxr@i_2|aH)^Xf4s5A8UDB2+ZBVZV6fx*LxU z?a#W2hl@)5f@mLl z4|yV|%v(BnOw_L6OqG|PP=g)I$iODW!^6TNCd7(QNr_E}MJFZ216?aj%<`$1CYWU2 zZGr1tV}$|q-^+0QsyT{q#-f^%rl!KeD^UwAWd{e5joYpZ2}(Rhc23S}Mvc;(oQ<~~ zD(Iqs9Q!3QvA=uAz)M&u+3w-VcwFJD3T~C&2DR^j=ffC-s00?<2pB=Z|*PkpuUWDm} zzwpB@dUQ-oB0{XGdWH08Vimspf;zi7PEIN+{^k6szt{~-ph$;E^hiaMEY;XOhhb5t zSE$b>qeBPBqCeR_Px3kUAFfuI#SFK%)pT`?bcXwJKmit3I>HH_|#8c z?%vhM%S?~US~~f|8>)DjimQu@B9Jrhs@iW|r;^yr#?E2_!KBQ>V#VrZSesidZU?O9 z!p~U(tRn+5zH1!@CB?@k#N#9$mU^30U}Rytpbj#}yMR z?aVc=&IZTk%3XN%fRxa|_`6KH^hAH=aWvEtdqBIZUk~^7`3h@CU@weFH|`2(8%#t;J&+)OvI;s z>*Ci9)~?7B+=-(^eqJqbqBaFmopRyeBr1&Px*tw@nq7K6&LrCrv1F8huJMDc(Z`Rj zql&}bJLYrsBZPjwCc~t*hIF9M+;P6LTjAOg&qKP(0t9 zv1~IE#`s^KQttZmWzbd?kpW8-`s~L2(+QOgEm^T_M)3CcKX|I;ZdK5@_T3zh;gRo3 z^faCnP8bl|1nn#-0fko{SiJj zxa|AX-(Ao_WoHit%l$lBtZ68$Px5F)5@2mva~C7i_W?b;Q+jH}oxPnMu;5<1*U1O@ zuhhYi@1NAmT;@Ont$XJ0Jh{#C`~*ls2{zZOj{zQY0@?Unu950xlrQ6EI5>p%vW!V8 zqj`8NZd|%wR7mODTDUo)0`9GfH1;k>I3bgWHwj83{CdWBHNzzAKvr zLts;BG@t&mU0$MWY!sH=Up4c}orNIpjpAVA;v^<(=r$^8YpL0|&1Zf{UOGB=|2^b~ zHo1ji!bCw}y=>;MGh9%+-&%WjDC+)dofVy7iv6;KI{RaR=Mu2<uWnHs|>8|W&H8Ss;B;X%9LdJX3v$GNs z5>I17py%&`D+jVUC!;3&|bq@o5O5sNSTm!2IUYfKpRk^24#Nbh?PDVmZI^aAmU z0@t>3e0raa1>DuHNw<<=slDSU$w5Q}E>YH`VM%d&_lVElek~yAwrH&1zO#oCPv7UU z3V5Xu+l5TqG(%)m6w4b0S1+8GjlDN~Oa}WH7#I{yB%rUP2^webp6Bjn%k>O&boib_ zJgD-NyFM4s?Ej0lNK||A+QY-9z?wZIY_7u-x5ID*wCu-!k?ZT95Wh@-^wS1q?)NeB`h2i98BGrIEP7!Y2pRc`TSFfQnXq7(uVG=hupnq5`O0S1?{Dsfe49+u z-vNe!x%v1k*ap_ww%R*x1E!L$yX^1(3X&Pv)Y!>rJ+J&lXGcTB#)0i?HhB;Qv#8I` z`Q@#m-xR)tgoK3NJx3t|7wgxy&{_bt%U|>~m;SLf#td}Ts#!Q(_!0vq1l_Qg*)&&6 zyh}J4zLaHQib4>aIXLQ6^xlkqVg~8O|94t?dI4|x_@{BKm;V4ZT(LDH0geDm6GO9T z{gQHpzhlDwI<{v<1JXh#KiK7gN&{h#z?P`eF#d0(Edntr^~JboqnH4s_TeL7a1VYO z4{fMl*p`jMqr(jeUIa}hL(q<*qT!x5evOQRf`W|9@g~Z$PO?HBRCf@Ags7;^BQStx;G9Ovr0-y^z{Dz{ad`RxA%ANZ&>(Ogs(j_KbVal zT*9@1P$}jJBrAqZlr2yxo3Ru@pWYvjMK6p4yU_*v>J=p=m9w911aivECA= zB?q+{ur5L@EZi#;5_bFPD2h;&H|28&Dd=De0=mrEqi?$vuGhRY2y0Ro0E9yq2G!TD z$z#}N%l3nu!iJg>7zZVe9(?cx#mBRE%Vx&K#rcn+lq6oWw*mHI5mMnHi2c z;5p#H@z}mmm$9hZqHZCBv1n-rfWBg zvn)&ivOpFH5_)Jn>HLM&L`_JT?(HfM4vX6Kld^z=lp3t90w5B*R6ICD?|QP-adb~? zHi`p|L$jsi&|wq9H6(%WXZ@&Md=sO9jKtb+Chgkd(>+Hhq<85t5Hgvx6i$W^D2#we z?(aWGQIqa1NMTg7B&74$vu99VR$Ods0V1)MTLj&ffJc-nh?c49=DtfJy}@@(#|=oT zEhFjK_gM^X=<_>|qVsH@&%VkIh!LU)P~R_??H^qL%>0H%aeu1FmgU=fZ2#k}ZxHZf+fM5t&RQ_++wtPGE-ONFuM8C8U;Gkqt zoV&&r@b-WD4(~$YZ~>O|S0!hcYjmlay9ZFWVQEAZtQIRN?%o)zgC|i9(23g1W2x)0 zpAZ|4&)h974>Vta;GP8BBQNPZb#Utp?8$!)XpUUGBQDN=J79=#*q;m#G-!( z6{?mT`~+%m7RidBgLI^tP6g&HSpQB|mrp$T*SYtHk_9z61BXPZj*^UTm<9;IaT#Cn z+$oE^yxe>Ns`l$jzWg+Juq>uilOBK`T~OxiX}^ENBEmC2G=FZ0B&6SfVgUxBZCW82 z=wmh)_A6PxBI zL4hwL26i0z^cXGr&`I&qSF4Ta?Nrf&g*qNo)T^>4kG-6o6V{$10rdO1d*%*~=eN5X zJ%&lOFH5v4d>vLZ(j`~PZlnA9dOm3>xcfz9eTZGH?iO0TU+RQlXXMwUg)2iq_yR8%BS z!@KF2WX9uVS~1%TAhfn_VR@6(BReK3RC)?uQAtiq|74cY-&(9va*s55&U~;@>Dy=i+_~4+Mo7^eiD3} zJ6QQ?bAQLwA?SG6*uBx1+dYVCQ37*!NzCV|+J|akS;vYh{UK}6&+=-ywA7OF>4BY{ zU3Xhh{e>_g#m1sP&){VboHi#%MJWgwo9#U~{}VqRKrSe7O<$q#6rZ>+D^ib=v>7J^ z{cB#6{OjSv)iN_vN0o1z$M&^0_tCFTnBLRVYb=kUk;5Y7Y>Lqot6B*?vlsaA@t^^} zR9DkTPDZ?yjD+#H`t{(T<_ z@TtMmsfj!ZiJsvgc~FV@GCu$hsVFKebRDWc0@vn`qQeL2Uaswz*|pah41^(>^W2(^ z0+t6>HeU(qT3k$gzT>(e<59-GK3qECY5|p$W;pw z2|c*Kx*)3n;)l!0zIeXT(lwvrZl)p0JLmOrw5M9WV08bkX|4Cq+Lt^RWH}&@zj)X` zz`ulu1$z0VH-{IT5(iuVagxKX3$^N>WIvsoihTauIoi3;H2ZwCa{YX1hZr5%V7+8p zr~PQbTK5nE{at)7B7C>)bnr`r5NOB-)Sp<*R{CAoA-C$=+1sDS{(@O*uv)TAcWu%I zwD`Q{cKa*3zsgQF&&4LaU%fUsbI8xG(7HdqNA_zn2plohX$A+x^!oed#f{s#0Ug9& zfhCaYy-uEX=miloTK-rs{ybXgcqdzXI|TG9HtU?^8C|xvHoAs*_f?^>wrgw$O}jsRuh-P;5+`T2_Vx^{b8MNQ73U zT2`LA9yF4DTF<#+dY5glhnp_6H%yy~#xexr(&a#nbS+n3LT2h{J|3!ia6ADthtFcr zXw7E9Gj8r3G3wqh&lpaY&{{6VEel^!dFx9iCIUTCt~KB5nYm|ev+A3PfbU!BC!8ZG zJbRAEuk5NdsRVfiK+R#`%6f!H)7ut2dD+Ze$EI1o+soy>z5BjCf(SXr*0U?Vs)yUC z@WH{MhosQZUh1s0w#$2B2FH1unw_kFWAsbF-Fzwe_D+M)6c_H=0_`?*4Ay=)-}bq! zT#sw4c^{m)SN`$J{IJ)GXQ1Eg9WplNA>{COPQ~CPS%@t|39WNY)24oJuR~i&)A_m0 zEhIFQ&U_p7i_`P3pywy+k-F9{+VIU>O48k|;q;qM9|2EiJZ~0eWo);jg}o{vX3_Nw zKbV`ggh<%qL7 z9P+svr5&8am=X8pQ|~xYznAkw#`9yUm)I4;g3a-oV?+as+h5=}t&+X)P zcS!;8Vds_=K|gON$4Qv$6+Ywf&h7l)WxlZj_g6wZR+Wx^uMt2`1BNU3dF4@GfQ@?D zXAfo0fEpoCUQ$h8x!UP^)vja7`$0S+J0m$M&G8~-VypLooR@c>uKn>bBm&!K23iyW z9N405_<^?2@#vD>O9BH$RMn`sxctGluhM4igxm9BPhF6ZoSVBCoMPtL5=}HHp3JBG z8G2xh>AP6pE(vdc!wPgECx0;bI8h(f1h6wWzXJRh9S_gSa^qg3apf57XWC-gVFm>t zHW@r#_2sRbvO=i)VxDb+EaZYUUJamqlV58GdTxZc1f>(^z9$DU&)%EW)Mnsx@+C)& zui?KA{fBDdb8mckG}R)v_o1C3cW8%^fgbp++xGk!2z5Z`jBmWl<_=gVr@Wj`W-F|c zLC;U$tp1y4=^%7WAyKsC-8BTGBT!F7&sUhI9SNZsd#zm=(;j8 z%GC`vy$|-&(soU)e0+Q)Ou#vrnwpD(i^53A8`&E7()qN#$fMOvU0ICmf_CPvZK zZDeF Date: Wed, 20 Aug 2025 10:10:56 +0200 Subject: [PATCH 0102/1900] Drop no longer needed feature gates --- library/stdarch/crates/core_arch/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index c58580f641780..7d3cbd55ad85c 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -18,8 +18,6 @@ rustc_attrs, staged_api, doc_cfg, - tbm_target_feature, - sse4a_target_feature, riscv_target_feature, arm_target_feature, mips_target_feature, From 135de7c8df64b8ef85ad348db09b02194779e68e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 14 Aug 2025 11:05:03 +0200 Subject: [PATCH 0103/1900] Use intrinsics for some s390x operations --- .../stdarch/crates/core_arch/src/s390x/vector.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 0ce720a9244cd..9927ff2c62555 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -114,6 +114,9 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vsumqf"] fn vsumqf(a: vector_unsigned_int, b: vector_unsigned_int) -> u128; #[link_name = "llvm.s390.vsumqg"] fn vsumqg(a: vector_unsigned_long_long, b: vector_unsigned_long_long) -> u128; + #[link_name = "llvm.s390.vaccq"] fn vaccq(a: u128, b: u128) -> u128; + #[link_name = "llvm.s390.vacccq"] fn vacccq(a: u128, b: u128, c: u128) -> u128; + #[link_name = "llvm.s390.vscbiq"] fn vscbiq(a: u128, b: u128) -> u128; #[link_name = "llvm.s390.vsbiq"] fn vsbiq(a: u128, b: u128, c: u128) -> u128; #[link_name = "llvm.s390.vsbcbiq"] fn vsbcbiq(a: u128, b: u128, c: u128) -> u128; @@ -4694,7 +4697,9 @@ pub unsafe fn vec_addc_u128( ) -> vector_unsigned_char { let a: u128 = transmute(a); let b: u128 = transmute(b); - transmute(a.overflowing_add(b).1 as u128) + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 + // transmute(a.overflowing_add(b).1 as u128) + transmute(vaccq(a, b)) } /// Vector Add With Carry unsigned 128-bits @@ -4729,8 +4734,10 @@ pub unsafe fn vec_addec_u128( let a: u128 = transmute(a); let b: u128 = transmute(b); let c: u128 = transmute(c); - let (_d, carry) = a.carrying_add(b, c & 1 != 0); - transmute(carry as u128) + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 + // let (_d, carry) = a.carrying_add(b, c & 1 != 0); + // transmute(carry as u128) + transmute(vacccq(a, b, c)) } /// Vector Subtract with Carryout From 4a8b8231b1e7263ae5d8da82ccd5c1490f1db9e7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 10:18:03 +0200 Subject: [PATCH 0104/1900] Add missing avx512vl target features --- .../crates/core_arch/src/x86/avx512fp16.rs | 376 +++++++++--------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs index 8c914803c665d..a86fc7199b83c 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs @@ -17282,14 +17282,14 @@ mod tests { assert_eq_m512h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_load_sh() { let a = _mm_set_sh(1.0); let b = _mm_load_sh(addr_of!(a).cast()); assert_eq_m128h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_load_sh() { let a = _mm_set_sh(1.0); let src = _mm_set_sh(2.); @@ -17299,7 +17299,7 @@ mod tests { assert_eq_m128h(src, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_load_sh() { let a = _mm_set_sh(1.0); let b = _mm_maskz_load_sh(1, addr_of!(a).cast()); @@ -17344,7 +17344,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17353,7 +17353,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17363,7 +17363,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17402,7 +17402,7 @@ mod tests { assert_eq_m512h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_store_sh() { let a = _mm_set_sh(1.0); let mut b = _mm_setzero_ph(); @@ -17410,7 +17410,7 @@ mod tests { assert_eq_m128h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_store_sh() { let a = _mm_set_sh(1.0); let mut b = _mm_setzero_ph(); @@ -17655,7 +17655,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17664,7 +17664,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17681,7 +17681,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17695,7 +17695,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17704,7 +17704,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17717,7 +17717,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17945,7 +17945,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17954,7 +17954,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17971,7 +17971,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17985,7 +17985,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17994,7 +17994,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18007,7 +18007,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18235,7 +18235,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18244,7 +18244,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18261,7 +18261,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18275,7 +18275,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18284,7 +18284,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18297,7 +18297,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18457,7 +18457,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18466,7 +18466,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18483,7 +18483,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18497,7 +18497,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18506,7 +18506,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18519,7 +18519,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18680,7 +18680,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18689,7 +18689,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18701,7 +18701,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18711,7 +18711,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18720,7 +18720,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18730,7 +18730,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18888,7 +18888,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18897,7 +18897,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18909,7 +18909,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18919,7 +18919,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18928,7 +18928,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18938,7 +18938,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19096,7 +19096,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19105,7 +19105,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19115,7 +19115,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19124,7 +19124,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19133,7 +19133,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19145,7 +19145,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19304,7 +19304,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19313,7 +19313,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19323,7 +19323,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19332,7 +19332,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19341,7 +19341,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19353,7 +19353,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19692,7 +19692,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19702,7 +19702,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19715,7 +19715,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19728,7 +19728,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19741,7 +19741,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19751,7 +19751,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19768,7 +19768,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19785,7 +19785,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20002,7 +20002,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20012,7 +20012,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20025,7 +20025,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20038,7 +20038,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20051,7 +20051,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20061,7 +20061,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20078,7 +20078,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20095,7 +20095,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20311,7 +20311,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20321,7 +20321,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20334,7 +20334,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20347,7 +20347,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20360,7 +20360,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20370,7 +20370,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20387,7 +20387,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20404,7 +20404,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20620,7 +20620,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20630,7 +20630,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20643,7 +20643,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20656,7 +20656,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20669,7 +20669,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20930,7 +20930,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20940,7 +20940,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20953,7 +20953,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20966,7 +20966,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20979,7 +20979,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20989,7 +20989,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21006,7 +21006,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21023,7 +21023,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21240,7 +21240,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21250,7 +21250,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21263,7 +21263,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21276,7 +21276,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21289,7 +21289,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21299,7 +21299,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21316,7 +21316,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21333,7 +21333,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21851,7 +21851,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21860,7 +21860,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21873,7 +21873,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21970,7 +21970,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -21979,7 +21979,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -21992,7 +21992,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22127,7 +22127,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22136,7 +22136,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22149,7 +22149,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22161,7 +22161,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22170,7 +22170,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22187,7 +22187,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22338,7 +22338,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22347,7 +22347,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22360,7 +22360,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22372,7 +22372,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22381,7 +22381,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22398,7 +22398,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22549,7 +22549,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22558,7 +22558,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22571,7 +22571,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22583,7 +22583,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22592,7 +22592,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22609,7 +22609,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22746,7 +22746,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22755,7 +22755,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22768,7 +22768,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22780,7 +22780,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22789,7 +22789,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22802,7 +22802,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22958,7 +22958,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22967,7 +22967,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22980,7 +22980,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22992,7 +22992,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23003,7 +23003,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23024,7 +23024,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23167,7 +23167,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23176,7 +23176,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23189,7 +23189,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23201,7 +23201,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23210,7 +23210,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23223,7 +23223,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23372,7 +23372,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23381,7 +23381,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23394,7 +23394,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23406,7 +23406,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23415,7 +23415,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23432,7 +23432,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23576,7 +23576,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23585,7 +23585,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23598,7 +23598,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23610,7 +23610,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23619,7 +23619,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23636,7 +23636,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -24505,7 +24505,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvti32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvti32_sh(a, 10); @@ -24513,7 +24513,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundi32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvt_roundi32_sh::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a, 10); @@ -24645,7 +24645,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtu32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvtu32_sh(a, 10); @@ -24653,7 +24653,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundu32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvt_roundu32_sh::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a, 10); @@ -24711,7 +24711,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvtepi64_ph(a); @@ -24719,7 +24719,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24728,7 +24728,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvtepi64_ph(0b01010101, a); @@ -24736,7 +24736,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvt_roundepi64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -24755,7 +24755,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvt_roundepi64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -24815,7 +24815,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvtepu64_ph(a); @@ -24823,7 +24823,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24832,7 +24832,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvtepu64_ph(0b01010101, a); @@ -24840,7 +24840,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvt_roundepu64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -24848,7 +24848,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24859,7 +24859,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvt_roundepu64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -25005,7 +25005,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25014,7 +25014,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25027,7 +25027,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25039,7 +25039,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25048,7 +25048,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25065,7 +25065,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25129,7 +25129,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_cvtpd_ph(a); @@ -25137,7 +25137,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -25146,7 +25146,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_maskz_cvtpd_ph(0b01010101, a); @@ -25154,7 +25154,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_cvt_roundpd_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -25162,7 +25162,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -25173,7 +25173,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_maskz_cvt_roundpd_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -25183,7 +25183,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25192,7 +25192,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25205,7 +25205,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25217,7 +25217,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25226,7 +25226,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25243,7 +25243,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); From 92f6310890e16790eb44abcb33cffa0f8090e0c3 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 11:00:47 +0200 Subject: [PATCH 0105/1900] Work around selection failure without avx512vl --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 8 ++++++-- library/stdarch/crates/core_arch/src/x86_64/avx512f.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index d53f83c0a10bc..cad8ca0c63ce0 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -49621,7 +49621,9 @@ mod tests { assert_eq_m512i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_mask_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, @@ -49634,7 +49636,9 @@ mod tests { assert_eq_m512i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_maskz_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 934c9e2812c42..a8dd340bd6605 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -5311,7 +5311,9 @@ mod tests { assert_eq_m256i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_mask_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let src = _mm256_set1_epi32(0); @@ -5322,7 +5324,9 @@ mod tests { assert_eq_m256i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_maskz_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let r = _mm512_maskz_cvttpd_epu32(0, a); From 3302e3e09a2f843ba7a20102f0f5f18909bc0b47 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 11:12:26 +0200 Subject: [PATCH 0106/1900] Adjust immediate for vrndscalepd tests The immediate here encodes both the rounding mode (in the low bits) and the scale (in the high bits). Make sure the scale is non-zero. --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index cad8ca0c63ce0..0803f2f81b215 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -5823,7 +5823,7 @@ pub fn _mm512_maskz_roundscale_pd(k: __mmask8, a: __m512d) -> _ #[inline] #[target_feature(enable = "avx512f,avx512vl")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 0))] +#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 16))] #[rustc_legacy_const_generics(1)] pub fn _mm256_roundscale_pd(a: __m256d) -> __m256d { unsafe { @@ -5897,7 +5897,7 @@ pub fn _mm256_maskz_roundscale_pd(k: __mmask8, a: __m256d) -> _ #[inline] #[target_feature(enable = "avx512f,avx512vl")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 0))] +#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 16))] #[rustc_legacy_const_generics(1)] pub fn _mm_roundscale_pd(a: __m128d) -> __m128d { unsafe { From fa163a1fcaa1d46a62d4a19b9354da5583dbf5d1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:02:12 +0200 Subject: [PATCH 0107/1900] s390x: define `unpack_low` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 9927ff2c62555..2969516c9ae44 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -169,13 +169,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vpklsfs"] fn vpklsfs(a: vector_unsigned_int, b: vector_unsigned_int) -> PackedTuple; #[link_name = "llvm.s390.vpklsgs"] fn vpklsgs(a: vector_unsigned_long_long, b: vector_unsigned_long_long) -> PackedTuple; - #[link_name = "llvm.s390.vuplb"] fn vuplb (a: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vuplhw"] fn vuplhw (a: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vuplf"] fn vuplf (a: vector_signed_int) -> vector_signed_long_long; - #[link_name = "llvm.s390.vupllb"] fn vupllb (a: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vupllh"] fn vupllh (a: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vupllf"] fn vupllf (a: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vavgb"] fn vavgb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vavgh"] fn vavgh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_short; #[link_name = "llvm.s390.vavgf"] fn vavgf(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int; @@ -2583,8 +2576,14 @@ mod sealed { unsafe fn vec_unpackl(self) -> Self::Result; } - // FIXME(llvm): a shuffle + simd_as does not currently optimize into a single instruction like - // unpachk above. Tracked in https://github.com/llvm/llvm-project/issues/129576. + // NOTE: `vuplh` is used for "unpack logical high", hence `vuplhw`. + impl_vec_unpack!(unpack_low vuplb vector_signed_char i8x8 vector_signed_short 8); + impl_vec_unpack!(unpack_low vuplhw vector_signed_short i16x4 vector_signed_int 4); + impl_vec_unpack!(unpack_low vuplf vector_signed_int i32x2 vector_signed_long_long 2); + + impl_vec_unpack!(unpack_low vupllb vector_unsigned_char u8x8 vector_unsigned_short 8); + impl_vec_unpack!(unpack_low vupllh vector_unsigned_short u16x4 vector_unsigned_int 4); + impl_vec_unpack!(unpack_low vupllf vector_unsigned_int u32x2 vector_unsigned_long_long 2); impl_vec_trait! {[VectorUnpackl vec_unpackl] vuplb (vector_signed_char) -> vector_signed_short} impl_vec_trait! {[VectorUnpackl vec_unpackl] vuplhw (vector_signed_short) -> vector_signed_int} From c5ec0960f0a1d78584b4957dd71613bdbcb91de8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:03:04 +0200 Subject: [PATCH 0108/1900] s390x: add `assert_instr` for `vec_round` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 2969516c9ae44..066ae1607316d 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -1194,10 +1194,8 @@ mod sealed { test_impl! { vec_roundc_f32 (a: vector_float) -> vector_float [nearbyint_v4f32, "vector-enhancements-1" vfisb] } test_impl! { vec_roundc_f64 (a: vector_double) -> vector_double [nearbyint_v2f64, vfidb] } - // FIXME(llvm) llvm trunk already lowers roundeven to vfidb, but rust does not use it yet - // use https://godbolt.org/z/cWq95fexe to check, and enable the instruction test when it works - test_impl! { vec_round_f32 (a: vector_float) -> vector_float [roundeven_v4f32, _] } - test_impl! { vec_round_f64 (a: vector_double) -> vector_double [roundeven_v2f64, _] } + test_impl! { vec_round_f32 (a: vector_float) -> vector_float [roundeven_v4f32, "vector-enhancements-1" vfisb] } + test_impl! { vec_round_f64 (a: vector_double) -> vector_double [roundeven_v2f64, vfidb] } #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorRoundc { From 97d64665b9e4e390cd3cadf403de5cfa67b3216a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:03:41 +0200 Subject: [PATCH 0109/1900] s390x: add `assert_instr` for `vec_extend` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 066ae1607316d..b010b7e38267e 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -3319,8 +3319,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegb))] + #[cfg_attr(test, assert_instr(vsegb))] pub unsafe fn vsegb(a: vector_signed_char) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i8x2>( a, @@ -3331,8 +3330,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegh))] + #[cfg_attr(test, assert_instr(vsegh))] pub unsafe fn vsegh(a: vector_signed_short) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i16x2>( a, @@ -3343,8 +3341,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegf))] + #[cfg_attr(test, assert_instr(vsegf))] pub unsafe fn vsegf(a: vector_signed_int) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i32x2>( a, From 1cda88aca15307dbcd1f6404875999373105a93d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:04:15 +0200 Subject: [PATCH 0110/1900] s390x: implement `vec_mule` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index b010b7e38267e..2af5edb9fb005 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -181,14 +181,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vcksm"] fn vcksm(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmeb"] fn vmeb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vmeh"] fn vmeh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vmef"] fn vmef(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; - - #[link_name = "llvm.s390.vmleb"] fn vmleb(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vmleh"] fn vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmlef"] fn vmlef(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vmob"] fn vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; #[link_name = "llvm.s390.vmoh"] fn vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; #[link_name = "llvm.s390.vmof"] fn vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; @@ -372,6 +364,19 @@ impl ShuffleMask { ShuffleMask(mask) } + const fn even() -> Self { + let mut mask = [0; N]; + let mut i = 0; + let mut index = 0; + while index < N { + mask[index] = i as u32; + + i += 2; + index += 1; + } + ShuffleMask(mask) + } + const fn pack() -> Self { let mut mask = [0; N]; let mut i = 1; @@ -2642,40 +2647,44 @@ mod sealed { unsafe fn vec_mule(self, b: Self) -> Result; } - // FIXME(llvm) sadly this does not yet work https://github.com/llvm/llvm-project/issues/129705 - // #[target_feature(enable = "vector")] - // #[cfg_attr(test, assert_instr(vmleh))] - // unsafe fn vec_vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int { - // let even_a: vector_unsigned_int = simd_as(simd_shuffle::<_, _, u16x4>( - // a, - // a, - // const { ShuffleMask([0, 2, 4, 6]) }, - // )); - // - // let even_b: vector_unsigned_int = simd_as(simd_shuffle::<_, _, u16x4>( - // b, - // b, - // const { ShuffleMask([0, 2, 4, 6]) }, - // )); - // - // simd_mul(even_a, even_b) - // } + macro_rules! impl_vec_mule { + ($instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr($instr))] + unsafe fn $instr(a: $src, b: $src) -> $dst { + let even_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + a, + a, + const { ShuffleMask::<$width>::even() }, + )); + + let even_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + b, + b, + const { ShuffleMask::<$width>::even() }, + )); + + simd_mul(even_a, even_b) + } + }; + } - test_impl! { vec_vmeb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short [ vmeb, vmeb ] } - test_impl! { vec_vmeh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int[ vmeh, vmeh ] } - test_impl! { vec_vmef(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long [ vmef, vmef ] } + impl_vec_mule! { vmeb vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mule! { vmeh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mule! { vmef vector_signed_int i32x2 vector_signed_long_long 2 } - test_impl! { vec_vmleb(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short [ vmleb, vmleb ] } - test_impl! { vec_vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int[ vmleh, vmleh ] } - test_impl! { vec_vmlef(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long [ vmlef, vmlef ] } + impl_vec_mule! { vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mule! { vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mule! { vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } - impl_mul!([VectorMule vec_mule] vec_vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); - impl_mul!([VectorMule vec_mule] vec_vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); - impl_mul!([VectorMule vec_mule] vec_vmef (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); + impl_mul!([VectorMule vec_mule] vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); + impl_mul!([VectorMule vec_mule] vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); + impl_mul!([VectorMule vec_mule] vmef (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); - impl_mul!([VectorMule vec_mule] vec_vmleb (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); - impl_mul!([VectorMule vec_mule] vec_vmleh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); - impl_mul!([VectorMule vec_mule] vec_vmlef (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); + impl_mul!([VectorMule vec_mule] vmleb (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); + impl_mul!([VectorMule vec_mule] vmleh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); + impl_mul!([VectorMule vec_mule] vmlef (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorMulo { From d5cb1c49fa415b512c0616730193467fd5fe2a9a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 14 Aug 2025 18:51:39 +0200 Subject: [PATCH 0111/1900] wasm: use `{read, write}_unaligned` methods --- .../crates/core_arch/src/wasm32/simd128.rs | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/wasm32/simd128.rs b/library/stdarch/crates/core_arch/src/wasm32/simd128.rs index 108bc3125c5f3..c864d6a516e08 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/simd128.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/simd128.rs @@ -141,16 +141,6 @@ unsafe extern "unadjusted" { fn llvm_f64x2_max(x: simd::f64x2, y: simd::f64x2) -> simd::f64x2; } -#[repr(C, packed)] -#[derive(Copy)] -struct Unaligned(T); - -impl Clone for Unaligned { - fn clone(&self) -> Unaligned { - *self - } -} - /// Loads a `v128` vector from the given heap address. /// /// This intrinsic will emit a load with an alignment of 1. While this is @@ -179,7 +169,7 @@ impl Clone for Unaligned { #[doc(alias("v128.load"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn v128_load(m: *const v128) -> v128 { - (*(m as *const Unaligned)).0 + m.read_unaligned() } /// Load eight 8-bit integers and sign extend each one to a 16-bit lane @@ -196,8 +186,8 @@ pub unsafe fn v128_load(m: *const v128) -> v128 { #[doc(alias("v128.load8x8_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i16x8_load_extend_i8x8(m: *const i8) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i16x8>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i16x8>(m).v128() } /// Load eight 8-bit integers and zero extend each one to a 16-bit lane @@ -214,8 +204,8 @@ pub unsafe fn i16x8_load_extend_i8x8(m: *const i8) -> v128 { #[doc(alias("v128.load8x8_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i16x8_load_extend_u8x8(m: *const u8) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u16x8>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u16x8>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -235,8 +225,8 @@ pub use i16x8_load_extend_u8x8 as u16x8_load_extend_u8x8; #[doc(alias("v128.load16x4_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i32x4_load_extend_i16x4(m: *const i16) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i32x4>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i32x4>(m).v128() } /// Load four 16-bit integers and zero extend each one to a 32-bit lane @@ -253,8 +243,8 @@ pub unsafe fn i32x4_load_extend_i16x4(m: *const i16) -> v128 { #[doc(alias("v128.load16x4_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i32x4_load_extend_u16x4(m: *const u16) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u32x4>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u32x4>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -274,8 +264,8 @@ pub use i32x4_load_extend_u16x4 as u32x4_load_extend_u16x4; #[doc(alias("v128.load32x2_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i64x2_load_extend_i32x2(m: *const i32) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i64x2>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i64x2>(m).v128() } /// Load two 32-bit integers and zero extend each one to a 64-bit lane @@ -292,8 +282,8 @@ pub unsafe fn i64x2_load_extend_i32x2(m: *const i32) -> v128 { #[doc(alias("v128.load32x2_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i64x2_load_extend_u32x2(m: *const u32) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u64x2>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u64x2>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -453,7 +443,7 @@ pub unsafe fn v128_load64_zero(m: *const u64) -> v128 { #[doc(alias("v128.store"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn v128_store(m: *mut v128, a: v128) { - *(m as *mut Unaligned) = Unaligned(a); + m.write_unaligned(a) } /// Loads an 8-bit value from `m` and sets lane `L` of `v` to that value. From e1a1b1ded2d14c2bb40d236a93ad629222db4da2 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 21:18:39 +0200 Subject: [PATCH 0112/1900] s390x: implement `vec_mulo` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 2af5edb9fb005..6775225788925 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -181,14 +181,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vcksm"] fn vcksm(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmob"] fn vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vmoh"] fn vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vmof"] fn vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; - - #[link_name = "llvm.s390.vmlob"] fn vmlob(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vmloh"] fn vmloh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmlof"] fn vmlof(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vmhb"] fn vmhb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vmhh"] fn vmhh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_short; #[link_name = "llvm.s390.vmhf"] fn vmhf(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int; @@ -377,7 +369,7 @@ impl ShuffleMask { ShuffleMask(mask) } - const fn pack() -> Self { + const fn odd() -> Self { let mut mask = [0; N]; let mut i = 1; let mut index = 0; @@ -390,6 +382,10 @@ impl ShuffleMask { ShuffleMask(mask) } + const fn pack() -> Self { + Self::odd() + } + const fn unpack_low() -> Self { let mut mask = [0; N]; let mut i = 0; @@ -2647,36 +2643,36 @@ mod sealed { unsafe fn vec_mule(self, b: Self) -> Result; } - macro_rules! impl_vec_mule { - ($instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { + macro_rules! impl_vec_mul_even_odd { + ($mask:ident $instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { #[inline] #[target_feature(enable = "vector")] #[cfg_attr(test, assert_instr($instr))] unsafe fn $instr(a: $src, b: $src) -> $dst { - let even_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( - a, + let elems_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( a, - const { ShuffleMask::<$width>::even() }, + a, // this argument is ignored entirely. + const { ShuffleMask::<$width>::$mask() }, )); - let even_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + let elems_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( b, - b, - const { ShuffleMask::<$width>::even() }, + b, // this argument is ignored entirely. + const { ShuffleMask::<$width>::$mask() }, )); - simd_mul(even_a, even_b) + simd_mul(elems_a, elems_b) } }; } - impl_vec_mule! { vmeb vector_signed_char i8x8 vector_signed_short 8 } - impl_vec_mule! { vmeh vector_signed_short i16x4 vector_signed_int 4 } - impl_vec_mule! { vmef vector_signed_int i32x2 vector_signed_long_long 2 } + impl_vec_mul_even_odd! { even vmeb vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mul_even_odd! { even vmeh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mul_even_odd! { even vmef vector_signed_int i32x2 vector_signed_long_long 2 } - impl_vec_mule! { vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } - impl_vec_mule! { vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } - impl_vec_mule! { vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } + impl_vec_mul_even_odd! { even vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mul_even_odd! { even vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mul_even_odd! { even vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } impl_mul!([VectorMule vec_mule] vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); impl_mul!([VectorMule vec_mule] vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); @@ -2691,21 +2687,21 @@ mod sealed { unsafe fn vec_mulo(self, b: Self) -> Result; } - test_impl! { vec_vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short [ vmob, vmob ] } - test_impl! { vec_vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int[ vmoh, vmoh ] } - test_impl! { vec_vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long [ vmof, vmof ] } + impl_vec_mul_even_odd! { odd vmob vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mul_even_odd! { odd vmoh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mul_even_odd! { odd vmof vector_signed_int i32x2 vector_signed_long_long 2 } - test_impl! { vec_vmlob(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short [ vmlob, vmlob ] } - test_impl! { vec_vmloh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int[ vmloh, vmloh ] } - test_impl! { vec_vmlof(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long [ vmlof, vmlof ] } + impl_vec_mul_even_odd! { odd vmlob vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mul_even_odd! { odd vmloh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mul_even_odd! { odd vmlof vector_unsigned_int u32x2 vector_unsigned_long_long 2 } - impl_mul!([VectorMulo vec_mulo] vec_vmob (vector_signed_char, vector_signed_char) -> vector_signed_short ); - impl_mul!([VectorMulo vec_mulo] vec_vmoh (vector_signed_short, vector_signed_short) -> vector_signed_int); - impl_mul!([VectorMulo vec_mulo] vec_vmof (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); + impl_mul!([VectorMulo vec_mulo] vmob (vector_signed_char, vector_signed_char) -> vector_signed_short ); + impl_mul!([VectorMulo vec_mulo] vmoh (vector_signed_short, vector_signed_short) -> vector_signed_int); + impl_mul!([VectorMulo vec_mulo] vmof (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); - impl_mul!([VectorMulo vec_mulo] vec_vmlob (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); - impl_mul!([VectorMulo vec_mulo] vec_vmloh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); - impl_mul!([VectorMulo vec_mulo] vec_vmlof (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); + impl_mul!([VectorMulo vec_mulo] vmlob (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); + impl_mul!([VectorMulo vec_mulo] vmloh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); + impl_mul!([VectorMulo vec_mulo] vmlof (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorMulh { From dfa95c6fa40374df410d3215d99d697c58f5da50 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:08:51 +0200 Subject: [PATCH 0113/1900] s390x: implement `vec_subc_u128` using `overflowing_sub` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 6775225788925..4e4161dbb7f7f 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -4678,11 +4678,9 @@ pub unsafe fn vec_subc_u128( a: vector_unsigned_char, b: vector_unsigned_char, ) -> vector_unsigned_char { - // FIXME(llvm) sadly this does not work https://github.com/llvm/llvm-project/issues/129608 - // let a: u128 = transmute(a); - // let b: u128 = transmute(b); - // transmute(!a.overflowing_sub(b).1 as u128) - transmute(vscbiq(transmute(a), transmute(b))) + let a: u128 = transmute(a); + let b: u128 = transmute(b); + transmute(!a.overflowing_sub(b).1 as u128) } /// Vector Add Compute Carryout unsigned 128-bits @@ -4714,7 +4712,7 @@ pub unsafe fn vec_adde_u128( let a: u128 = transmute(a); let b: u128 = transmute(b); let c: u128 = transmute(c); - // FIXME(llvm) sadly this does not work + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 // let (d, _carry) = a.carrying_add(b, c & 1 != 0); // transmute(d) transmute(vacq(a, b, c)) From e9162f221a154ff8dbf353275f3288f69c2c431f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 20:00:22 +0200 Subject: [PATCH 0114/1900] s390x: implement `vec_sld` using `fshl` --- .../crates/core_arch/src/s390x/vector.rs | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 4e4161dbb7f7f..9c2941129c869 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -94,8 +94,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vsrlb"] fn vsrlb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vslb"] fn vslb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; - #[link_name = "llvm.s390.vsldb"] fn vsldb(a: i8x16, b: i8x16, c: u32) -> i8x16; - #[link_name = "llvm.s390.vsld"] fn vsld(a: i8x16, b: i8x16, c: u32) -> i8x16; #[link_name = "llvm.s390.vsrd"] fn vsrd(a: i8x16, b: i8x16, c: u32) -> i8x16; #[link_name = "llvm.s390.verimb"] fn verimb(a: vector_signed_char, b: vector_signed_char, c: vector_signed_char, d: i32) -> vector_signed_char; @@ -3484,10 +3482,44 @@ mod sealed { unsafe fn vec_sldb(self, b: Self) -> Self; } - // FIXME(llvm) https://github.com/llvm/llvm-project/issues/129955 - // ideally we could implement this in terms of llvm.fshl.i128 - // #[link_name = "llvm.fshl.i128"] fn fshl_i128(a: u128, b: u128, c: u128) -> u128; - // transmute(fshl_i128(transmute(a), transmute(b), const { C * 8 } )) + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr(vsldb))] + unsafe fn test_vec_sld(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sld::<13>(b) + } + + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr(vsldb))] + unsafe fn test_vec_sldw(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sldw::<3>(b) + } + + #[inline] + #[target_feature(enable = "vector-enhancements-2")] + #[cfg_attr(test, assert_instr(vsld))] + unsafe fn test_vec_sldb(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sldb::<7>(b) + } + + #[inline] + #[target_feature(enable = "vector-enhancements-2")] + #[cfg_attr(test, assert_instr(vsrd))] + unsafe fn test_vec_srdb(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_srdb::<7>(b) + } + + unsafe fn funnel_shl_u128(a: u128, b: u128, c: u128) -> u128 { + #[repr(simd)] + struct Single([u128; 1]); + + transmute(simd_funnel_shl::( + transmute(a), + transmute(b), + transmute(c), + )) + } macro_rules! impl_vec_sld { ($($ty:ident)*) => { @@ -3498,21 +3530,21 @@ mod sealed { #[target_feature(enable = "vector")] unsafe fn vec_sld(self, b: Self) -> Self { static_assert_uimm_bits!(C, 4); - transmute(vsldb(transmute(self), transmute(b), C)) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 8 })) } #[inline] #[target_feature(enable = "vector")] unsafe fn vec_sldw(self, b: Self) -> Self { static_assert_uimm_bits!(C, 2); - transmute(vsldb(transmute(self), transmute(b), const { 4 * C })) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 4 * 8 })) } #[inline] #[target_feature(enable = "vector-enhancements-2")] unsafe fn vec_sldb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); - transmute(vsld(transmute(self), transmute(b), C)) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 })) } } @@ -3523,6 +3555,11 @@ mod sealed { unsafe fn vec_srdb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); transmute(vsrd(transmute(self), transmute(b), C)) + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129955#issuecomment-3207488190 + // LLVM currently rewrites `fshr` to `fshl`, and the logic in the s390x + // backend cannot deal with that yet. + // #[link_name = "llvm.fshr.i128"] fn fshr_i128(a: u128, b: u128, c: u128) -> u128; + // transmute(fshr_i128(transmute(self), transmute(b), const { C as u128 })) } } )* From 45af206618d516571e6f526e4600ebaca92de6c0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 21:12:52 +0200 Subject: [PATCH 0115/1900] s390x: link to a missed optimization --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 9c2941129c869..f6d14c45e0d2b 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -2352,6 +2352,9 @@ mod sealed { unsafe fn vec_packs(self, b: Other) -> Self::Result; } + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/153655 + // Other targets can use a min/max for the saturation + a truncation. + impl_vec_trait! { [VectorPacks vec_packs] vpksh (vector_signed_short, vector_signed_short) -> vector_signed_char } impl_vec_trait! { [VectorPacks vec_packs] vpklsh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_char } impl_vec_trait! { [VectorPacks vec_packs] vpksf (vector_signed_int, vector_signed_int) -> vector_signed_short } From 4a9b73bc98417d2335b7d3d86edf301bdd7be230 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 07:14:55 +0300 Subject: [PATCH 0116/1900] Attach the DB in symbol queries --- .../crates/ide-db/src/symbol_index.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 78ade30c7e3ed..76b647f8e9f2d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -133,23 +133,27 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabase { fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc { let _p = tracing::info_span!("library_symbols").entered(); - let mut symbol_collector = SymbolCollector::new(db); - - db.source_root_crates(source_root_id) - .iter() - .flat_map(|&krate| Crate::from(krate).modules(db)) - // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, - // as the index for a library is not going to really ever change, and we do not want to store each - // the module or crate indices for those in salsa unless we need to. - .for_each(|module| symbol_collector.collect(module)); - - Arc::new(SymbolIndex::new(symbol_collector.finish())) + // We call this without attaching because this runs in parallel, so we need to attach here. + salsa::attach(db, || { + let mut symbol_collector = SymbolCollector::new(db); + + db.source_root_crates(source_root_id) + .iter() + .flat_map(|&krate| Crate::from(krate).modules(db)) + // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, + // as the index for a library is not going to really ever change, and we do not want to store each + // the module or crate indices for those in salsa unless we need to. + .for_each(|module| symbol_collector.collect(module)); + + Arc::new(SymbolIndex::new(symbol_collector.finish())) + }) } fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module))) + // We call this without attaching because this runs in parallel, so we need to attach here. + salsa::attach(db, || Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module)))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { From 73d1aa9e8248d22cc6425714b9f3f44d34257e7a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 08:06:43 +0300 Subject: [PATCH 0117/1900] Attach the DB when mapping the result of `world_symbols()` We call `try_to_nav()` there. --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 874e04702e241..4b93535c89438 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -481,12 +481,15 @@ impl Analysis { // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic // if we were to attach it here. Cancelled::catch(|| { - symbol_index::world_symbols(&self.db, query) - .into_iter() - .filter_map(|s| s.try_to_nav(&self.db)) - .take(limit) - .map(UpmappingResult::call_site) - .collect::>() + let symbols = symbol_index::world_symbols(&self.db, query); + salsa::attach(&self.db, || { + symbols + .into_iter() + .filter_map(|s| s.try_to_nav(&self.db)) + .take(limit) + .map(UpmappingResult::call_site) + .collect::>() + }) }) } From 98bd1d744562947b54174668423bee572ed5b7ee Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 2 Jun 2025 00:02:35 +0200 Subject: [PATCH 0118/1900] use `simd_saturating_{add, sub}` on neon --- .../src/arm_shared/neon/generated.rs | 320 ++---------------- .../spec/neon/arm_shared.spec.yml | 44 +-- 2 files changed, 48 insertions(+), 316 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index fd150bcaf2b7f..22d31d4e25682 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -36978,15 +36978,7 @@ pub fn vqabsq_s32(a: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v8i8")] - fn _vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t; - } - unsafe { _vqadd_s8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s8)"] @@ -37007,15 +36999,7 @@ pub fn vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v16i8")] - fn _vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t; - } - unsafe { _vqaddq_s8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s16)"] @@ -37036,15 +37020,7 @@ pub fn vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v4i16")] - fn _vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t; - } - unsafe { _vqadd_s16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s16)"] @@ -37065,15 +37041,7 @@ pub fn vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v8i16")] - fn _vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t; - } - unsafe { _vqaddq_s16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s32)"] @@ -37094,15 +37062,7 @@ pub fn vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v2i32")] - fn _vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t; - } - unsafe { _vqadd_s32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s32)"] @@ -37123,15 +37083,7 @@ pub fn vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v4i32")] - fn _vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t; - } - unsafe { _vqaddq_s32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s64)"] @@ -37152,15 +37104,7 @@ pub fn vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v1i64")] - fn _vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t; - } - unsafe { _vqadd_s64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s64)"] @@ -37181,15 +37125,7 @@ pub fn vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v2i64")] - fn _vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t; - } - unsafe { _vqaddq_s64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u8)"] @@ -37210,15 +37146,7 @@ pub fn vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v8i8")] - fn _vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t; - } - unsafe { _vqadd_u8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u8)"] @@ -37239,15 +37167,7 @@ pub fn vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v16i8")] - fn _vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t; - } - unsafe { _vqaddq_u8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u16)"] @@ -37268,15 +37188,7 @@ pub fn vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v4i16")] - fn _vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t; - } - unsafe { _vqadd_u16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u16)"] @@ -37297,15 +37209,7 @@ pub fn vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v8i16")] - fn _vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t; - } - unsafe { _vqaddq_u16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u32)"] @@ -37326,15 +37230,7 @@ pub fn vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v2i32")] - fn _vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t; - } - unsafe { _vqadd_u32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u32)"] @@ -37355,15 +37251,7 @@ pub fn vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v4i32")] - fn _vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t; - } - unsafe { _vqaddq_u32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u64)"] @@ -37384,15 +37272,7 @@ pub fn vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v1i64")] - fn _vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t; - } - unsafe { _vqadd_u64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u64)"] @@ -37413,15 +37293,7 @@ pub fn vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v2i64")] - fn _vqaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t; - } - unsafe { _vqaddq_u64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Vector widening saturating doubling multiply accumulate with scalar"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqdmlal_lane_s16)"] @@ -41259,15 +41131,7 @@ pub fn vqshrun_n_s64(a: int64x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v8i8")] - fn _vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t; - } - unsafe { _vqsub_s8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s8)"] @@ -41288,15 +41152,7 @@ pub fn vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v16i8")] - fn _vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t; - } - unsafe { _vqsubq_s8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s16)"] @@ -41317,15 +41173,7 @@ pub fn vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v4i16")] - fn _vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t; - } - unsafe { _vqsub_s16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s16)"] @@ -41346,15 +41194,7 @@ pub fn vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v8i16")] - fn _vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t; - } - unsafe { _vqsubq_s16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s32)"] @@ -41375,15 +41215,7 @@ pub fn vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v2i32")] - fn _vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t; - } - unsafe { _vqsub_s32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s32)"] @@ -41404,15 +41236,7 @@ pub fn vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v4i32")] - fn _vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t; - } - unsafe { _vqsubq_s32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s64)"] @@ -41433,15 +41257,7 @@ pub fn vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v1i64")] - fn _vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t; - } - unsafe { _vqsub_s64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s64)"] @@ -41462,15 +41278,7 @@ pub fn vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v2i64")] - fn _vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t; - } - unsafe { _vqsubq_s64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u8)"] @@ -41491,15 +41299,7 @@ pub fn vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v8i8")] - fn _vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t; - } - unsafe { _vqsub_u8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u8)"] @@ -41520,15 +41320,7 @@ pub fn vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v16i8")] - fn _vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t; - } - unsafe { _vqsubq_u8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u16)"] @@ -41549,15 +41341,7 @@ pub fn vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v4i16")] - fn _vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t; - } - unsafe { _vqsub_u16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u16)"] @@ -41578,15 +41362,7 @@ pub fn vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v8i16")] - fn _vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t; - } - unsafe { _vqsubq_u16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u32)"] @@ -41607,15 +41383,7 @@ pub fn vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v2i32")] - fn _vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t; - } - unsafe { _vqsub_u32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u32)"] @@ -41636,15 +41404,7 @@ pub fn vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v4i32")] - fn _vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t; - } - unsafe { _vqsubq_u32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u64)"] @@ -41665,15 +41425,7 @@ pub fn vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v1i64")] - fn _vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t; - } - unsafe { _vqsub_u64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u64)"] @@ -41694,15 +41446,7 @@ pub fn vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v2i64")] - fn _vqsubq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t; - } - unsafe { _vqsubq_u64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Rounding Add returning High Narrow (high half)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vraddhn_high_s16)"] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index f16a257399bc8..1543ab1f866e8 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -2290,13 +2290,10 @@ intrinsics: - [uint64x1_t, u64, i64] - [uint64x2_t, u64, i64] compose: - - LLVMLink: - name: "uqsub.{neon_type[0]}" - links: - - link: "llvm.aarch64.neon.uqsub.v{neon_type[0].lane}{type[2]}" - arch: aarch64,arm64ec - - link: "llvm.usub.sat.v{neon_type[0].lane}{type[2]}" - arch: arm + - FnCall: + - simd_saturating_sub + - - a + - b - name: "vqsub{neon_type[0].no}" doc: Saturating subtract @@ -2319,13 +2316,10 @@ intrinsics: - [int64x1_t, s64, i64] - [int64x2_t, s64, i64] compose: - - LLVMLink: - name: "sqsub.{neon_type[0]}" - links: - - link: "llvm.aarch64.neon.sqsub.v{neon_type[0].lane}{type[2]}" - arch: aarch64,arm64ec - - link: "llvm.ssub.sat.v{neon_type[0].lane}{type[2]}" - arch: arm + - FnCall: + - simd_saturating_sub + - - a + - b - name: "vhadd{neon_type.no}" doc: Halving add @@ -2541,13 +2535,10 @@ intrinsics: - uint64x1_t - uint64x2_t compose: - - LLVMLink: - name: "uqadd.{neon_type}" - links: - - link: "llvm.aarch64.neon.uqadd.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.uadd.sat.{neon_type}" - arch: arm + - FnCall: + - simd_saturating_add + - - a + - b - name: "vqadd{neon_type.no}" doc: Saturating add @@ -2570,13 +2561,10 @@ intrinsics: - int64x1_t - int64x2_t compose: - - LLVMLink: - name: "sqadd.{neon_type}" - links: - - link: "llvm.aarch64.neon.sqadd.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.sadd.sat.{neon_type}" - arch: arm + - FnCall: + - simd_saturating_add + - - a + - b - name: "vld1{neon_type[1].no}" doc: "Load multiple single-element structures to one, two, three, or four registers" From b5e3230f8f8f66de3f5db05a28ac496fa4a05ad4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 22:40:58 +0300 Subject: [PATCH 0119/1900] Remove unnecessary `salsa::attach()` calls --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 4b93535c89438..cddf5f04f2442 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -461,9 +461,7 @@ impl Analysis { hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - salsa::attach(db, || { - inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) - }) + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) }) } @@ -533,9 +531,7 @@ impl Analysis { let search_scope = AssertUnwindSafe(search_scope); self.with_db(|db| { let _ = &search_scope; - salsa::attach(db, || { - references::find_all_refs(&Semantics::new(db), position, search_scope.0) - }) + references::find_all_refs(&Semantics::new(db), position, search_scope.0) }) } @@ -545,7 +541,7 @@ impl Analysis { config: &HoverConfig, range: FileRange, ) -> Cancellable>> { - self.with_db(|db| salsa::attach(db, || hover::hover(db, range, config))) + self.with_db(|db| hover::hover(db, range, config)) } /// Returns moniker of symbol at position. @@ -553,7 +549,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| salsa::attach(db, || moniker::moniker(db, position))) + self.with_db(|db| moniker::moniker(db, position)) } /// Returns URL(s) for the documentation of the symbol under the cursor. @@ -581,7 +577,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| salsa::attach(db, || call_hierarchy::call_hierarchy(db, position))) + self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) } /// Computes incoming calls for the given file position. @@ -649,7 +645,7 @@ impl Analysis { /// Returns the set of possible targets to run for the current file. pub fn runnables(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| salsa::attach(db, || runnables::runnables(db, file_id))) + self.with_db(|db| runnables::runnables(db, file_id)) } /// Returns the set of tests for the given file position. @@ -672,9 +668,7 @@ impl Analysis { position: FilePosition, ) -> Cancellable>> { self.with_db(|db| { - salsa::attach(db, || { - highlight_related::highlight_related(&Semantics::new(db), config, position) - }) + highlight_related::highlight_related(&Semantics::new(db), config, position) }) } From f0fd9cdffd72659ec6308fa92e81dbb895e18e3e Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 21 Aug 2025 16:21:28 -0400 Subject: [PATCH 0120/1900] implement Extend<{Group, Literal, Punct, Ident}> for TokenStream --- library/proc_macro/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 162b4fdcc8ae2..8178af15b7393 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -377,6 +377,21 @@ impl Extend for TokenStream { } } +macro_rules! extend_items { + ($($item:ident)*) => { + $( + #[stable(feature = "token_stream_extend_tt_items", since = "CURRENT_RUSTC_VERSION")] + impl Extend<$item> for TokenStream { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|i| TokenTree::$item(i))); + } + } + )* + }; +} + +extend_items!(Group Literal Punct Ident); + /// Public implementation details for the `TokenStream` type, such as iterators. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub mod token_stream { From 99576aa3f7f6148480608fb62fa10a38cd64f1fd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 22 Aug 2025 05:24:33 +0900 Subject: [PATCH 0121/1900] fix: Infinite recursion while lowering assoc type bounds from supertraits --- .../crates/hir-ty/src/lower_nextsolver.rs | 6 ++- .../crates/hir-ty/src/tests/regression.rs | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index ce953fdcb8298..d87181f545d88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1756,7 +1756,11 @@ fn named_associated_type_shorthand_candidates<'db, R>( db, GenericDefId::TraitId(trait_def_id), PredicateFilter::SelfTrait, - |pred| pred == GenericDefId::TraitId(trait_def_id), + // We are likely in the midst of lowering generic predicates of `def`. + // So, if we allow `pred == def` we might fall into an infinite recursion. + // Actually, we have already checked for the case `pred == def` above as we started + // with a stack including `trait_id` + |pred| pred != def && pred == GenericDefId::TraitId(trait_def_id), ) .0 .deref() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index fd8c641d86eb0..966433369aa88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2439,3 +2439,56 @@ pub fn null_mut() -> *mut T { "#, ); } + +#[test] +fn issue_20484() { + check_no_mismatches( + r#" +struct Eth; + +trait FullBlockBody { + type Transaction; +} + +impl FullBlockBody for () { + type Transaction = (); +} + +trait NodePrimitives { + type BlockBody; + type SignedTx; +} + +impl NodePrimitives for () { + type BlockBody = (); + type SignedTx = (); +} + +impl NodePrimitives for Eth { + type BlockBody = (); + type SignedTx = (); +} + +trait FullNodePrimitives +where + Self: NodePrimitives>, +{ +} + +impl FullNodePrimitives for T where + T: NodePrimitives>, +{ +} + +fn node(_: N) +where + N: FullNodePrimitives, +{ +} + +fn main() { + node(Eth); +} +"#, + ); +} From b20ddc2d3f029bdbade7d86cdcf021a7cb8c3d8b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 11:38:19 +0800 Subject: [PATCH 0122/1900] Fix indent for move_guard_to_arm_body Input: ```rust fn main() { match 92 { x $0if true && true && true => { { false } }, _ => true } } ``` Old output: ```rust fn main() { match 92 { x => if true && true && true { { { false } } }, _ => true }; } ``` This PR fixed: ```rust fn main() { match 92 { x => if true && true && true { { { false } } }, _ => true } } ``` --- .../ide-assists/src/handlers/move_guard.rs | 70 +++++++++++++++---- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 644d1f6cafefc..6b50718424c72 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -40,28 +40,34 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) return None; } let space_before_guard = guard.syntax().prev_sibling_or_token(); + let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token(); - let guard_condition = guard.condition()?; + let guard_condition = guard.condition()?.reset_indent(); let arm_expr = match_arm.expr()?; - let if_expr = - make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None) - .indent(arm_expr.indent_level()); + let then_branch = make::block_expr(None, Some(arm_expr.reset_indent().indent(1.into()))); + let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level()); let target = guard.syntax().text_range(); acc.add( AssistId::refactor_rewrite("move_guard_to_arm_body"), "Move guard to arm body", target, - |edit| { - match space_before_guard { - Some(element) if element.kind() == WHITESPACE => { - edit.delete(element.text_range()); - } - _ => (), - }; + |builder| { + let mut edit = builder.make_editor(match_arm.syntax()); + if let Some(element) = space_before_guard + && element.kind() == WHITESPACE + { + edit.delete(element); + } + if let Some(element) = space_after_arrow + && element.kind() == WHITESPACE + { + edit.replace(element, make::tokens::single_space()); + } - edit.delete(guard.syntax().text_range()); - edit.replace_ast(arm_expr, if_expr.into()); + edit.delete(guard.syntax()); + edit.replace(arm_expr.syntax(), if_expr.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } @@ -298,6 +304,44 @@ fn main() { ); } + #[test] + fn move_multiline_guard_to_arm_body_works() { + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x $0if true + && true + && true => + { + { + false + } + }, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x => if true + && true + && true { + { + { + false + } + } + }, + _ => true + } +} +"#, + ); + } + #[test] fn move_guard_to_arm_body_works_complex_match() { check_assist( From cf478a02c74aac664cc2e2cfa7ce91e50be1d3a4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 22 Aug 2025 08:58:48 +0200 Subject: [PATCH 0123/1900] Fix panic in syntax_highlighting --- .../crates/ide/src/syntax_highlighting.rs | 4 +- .../ide/src/syntax_highlighting/inject.rs | 52 ++++++++++--------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e3dc3ed3c742a..f98770805a451 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -452,10 +452,10 @@ fn traverse( } hl } - NodeOrToken::Token(token) => { + NodeOrToken::Token(token) => salsa::attach(sema.db, || { highlight::token(sema, token, edition, &is_unsafe_node, tt_level > 0) .zip(Some(None)) - } + }), }; if let Some((mut highlight, binding_hash)) = element { if is_unlinked && highlight.tag == HlTag::UnresolvedReference { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index fb33307249a77..7785891169c64 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -5,7 +5,7 @@ use std::mem; use either::Either; use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; use ide_db::{ - SymbolKind, active_parameter::ActiveParameter, defs::Definition, + SymbolKind, active_parameter::ActiveParameter, base_db::salsa, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence, }; use syntax::{ @@ -126,32 +126,34 @@ pub(super) fn doc_comment( // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { - extract_definitions_from_docs(&docs) - .into_iter() - .filter_map(|(range, link, ns)| { - doc_mapping - .map(range) - .filter(|(mapping, _)| mapping.file_id == src_file_id) - .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { - Some(mapped_range).zip(resolve_doc_path_for_def( - sema.db, - def, - &link, - ns, - attr_id.is_inner_attr(), - )) + salsa::attach(sema.db, || { + extract_definitions_from_docs(&docs) + .into_iter() + .filter_map(|(range, link, ns)| { + doc_mapping + .map(range) + .filter(|(mapping, _)| mapping.file_id == src_file_id) + .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { + Some(mapped_range).zip(resolve_doc_path_for_def( + sema.db, + def, + &link, + ns, + attr_id.is_inner_attr(), + )) + }) + }) + .for_each(|(range, def)| { + hl.add(HlRange { + range, + highlight: module_def_to_hl_tag(def) + | HlMod::Documentation + | HlMod::Injected + | HlMod::IntraDocLink, + binding_hash: None, }) - }) - .for_each(|(range, def)| { - hl.add(HlRange { - range, - highlight: module_def_to_hl_tag(def) - | HlMod::Documentation - | HlMod::Injected - | HlMod::IntraDocLink, - binding_hash: None, }) - }); + }); } // Extract doc-test sources from the docs and calculate highlighting for them. From 3868f8ad5c2ef77fa823dc17da14ba13c2d508f7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 17:06:21 +0800 Subject: [PATCH 0124/1900] fix: convert_integer_literal not on selected `convert_integer_literal` can only convert the first literal, it is not reasonable to apply it when selected Example --- ```rust fn main() { $01+1$0; } ``` **Assist old outputs**: ``` Convert 1 to 0b1 Convert 1 to 0o1 Convert 1 to 0x1 Replace arithmetic with call to checked_* Replace arithmetic with call to saturating_* Replace arithmetic with call to wrapping_* Extract into variable Extract into constant Extract into static Extract into function ``` **Assist this PR outputs**: ``` Replace arithmetic with call to checked_* Replace arithmetic with call to saturating_* Replace arithmetic with call to wrapping_* Extract into variable Extract into constant Extract into static Extract into function ``` --- .../ide-assists/src/handlers/convert_integer_literal.rs | 8 ++++++++ src/tools/rust-analyzer/crates/ide-assists/src/tests.rs | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs index 846f4e9b258ae..bc76ade97f69d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -14,6 +14,9 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // const _: i32 = 0b1010; // ``` pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + if !ctx.has_empty_selection() { + return None; + } let literal = ctx.find_node_at_offset::()?; let literal = match literal.kind() { ast::LiteralKind::IntNumber(it) => it, @@ -265,4 +268,9 @@ mod tests { 111111111111111111111111111111111111111111111111111111111111111111111111$0;"; check_assist_not_applicable(convert_integer_literal, before); } + + #[test] + fn convert_non_empty_selection_literal() { + check_assist_not_applicable(convert_integer_literal, "const _: i32 = $00b1010$0;"); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index f4daabfe915d7..44bf6a4316942 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -456,7 +456,6 @@ pub fn test_some_range(a: int) -> bool { let expected = labels(&assists); expect![[r#" - Convert integer base Extract into... Replace if let with match "#]] @@ -489,7 +488,6 @@ pub fn test_some_range(a: int) -> bool { let expected = labels(&assists); expect![[r#" - Convert integer base Extract into... Replace if let with match "#]] From cd2d2a034d4e88ac6a3635c6a0cf2079eedecc21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:03:55 +0200 Subject: [PATCH 0125/1900] init by copying `ty_search_pat` --- clippy_utils/src/check_proc_macro.rs | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c4a759e919b1b..d569865553aa5 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -13,6 +13,7 @@ //! if the span is not from a `macro_rules` based macro. use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::AttrStyle; use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; use rustc_ast::token::CommentKind; @@ -411,6 +412,47 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } } +fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { + match ty.kind { + TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), + TyKind::FnPtr(fn_ptr) => ( + if fn_ptr.safety.is_unsafe() { + Pat::Str("unsafe") + } else if fn_ptr.abi != ExternAbi::Rust { + Pat::Str("extern") + } else { + Pat::MultiStr(&["fn", "extern"]) + }, + match fn_ptr.decl.output { + FnRetTy::DefaultReturn(_) => { + if let [.., ty] = fn_ptr.decl.inputs { + ty_search_pat(ty).1 + } else { + Pat::Str("(") + } + }, + FnRetTy::Return(ty) => ty_search_pat(ty).1, + }, + ), + TyKind::Never => (Pat::Str("!"), Pat::Str("!")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` + TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), + TyKind::Tup([ty]) => ty_search_pat(ty), + TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), + TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::Path(qpath) => qpath_search_pat(&qpath), + TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { + (Pat::Str("dyn"), Pat::Str("")) + }, + // NOTE: `TraitObject` is incomplete. It will always return true then. + _ => (Pat::Str(""), Pat::Str("")), + } +} + fn ident_search_pat(ident: Ident) -> (Pat, Pat) { (Pat::Sym(ident.name), Pat::Sym(ident.name)) } From a4dbbc9bf1cea10b396eec28e052a5096dc7da29 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:04:49 +0200 Subject: [PATCH 0126/1900] rename recursive function call --- clippy_utils/src/check_proc_macro.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d569865553aa5..1ea68c1c22129 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -413,10 +413,12 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - match ty.kind { + use ast::{FnRetTy, MutTy, TyKind}; + + match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), - TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), - TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), TyKind::FnPtr(fn_ptr) => ( if fn_ptr.safety.is_unsafe() { Pat::Str("unsafe") @@ -428,20 +430,20 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { match fn_ptr.decl.output { FnRetTy::DefaultReturn(_) => { if let [.., ty] = fn_ptr.decl.inputs { - ty_search_pat(ty).1 + ast_ty_search_pat(ty).1 } else { Pat::Str("(") } }, - FnRetTy::Return(ty) => ty_search_pat(ty).1, + FnRetTy::Return(ty) => ast_ty_search_pat(ty).1, }, ), TyKind::Never => (Pat::Str("!"), Pat::Str("!")), // Parenthesis are trimmed from the text before the search patterns are matched. // See: `span_matches_pat` TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), - TyKind::Tup([ty]) => ty_search_pat(ty), - TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), + TyKind::Tup([ty]) => ast_ty_search_pat(ty), + TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), From 2de81f56ec835d45858d0b2d80e8f1d7e1f1d1e3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:06:06 +0200 Subject: [PATCH 0127/1900] Infer is a unit variant --- clippy_utils/src/check_proc_macro.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 1ea68c1c22129..792c546e8ffc0 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -446,7 +446,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), - TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, From 73d04ba29f2db9073cc37bb43e9d0faf921344b2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:06:19 +0200 Subject: [PATCH 0128/1900] fix Path --- clippy_utils/src/check_proc_macro.rs | 51 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 792c546e8ffc0..d0a769e96b726 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -15,7 +15,9 @@ use rustc_abi::ExternAbi; use rustc_ast as ast; use rustc_ast::AttrStyle; -use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; +use rustc_ast::ast::{ + AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy, +}; use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -445,7 +447,52 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([ty]) => ast_ty_search_pat(ty), TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), - TyKind::Path(qpath) => qpath_search_pat(&qpath), + TyKind::Path(qself_path, path) => { + let start = if qself_path.is_some() { + Pat::Str("<") + } else if let Some(first) = path.segments.first() { + ident_search_pat(first.ident).0 + } else { + // this shouldn't be possible, but sure + Pat::Str("") + }; + let end = if let Some(last) = path.segments.last() { + match last.args.as_deref() { + // last `>` in `std::foo::Bar` + Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"), + Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output { + FnRetTy::Default(_) => { + if let Some(last) = par_args.inputs.last() { + // `B` in `(A, B)` -- `)` gets stripped + ast_ty_search_pat(last).1 + } else { + // `(` in `()` -- `)` gets stripped + Pat::Str("(") + } + }, + // `C` in `(A, B) -> C` + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + // last `..` in `(..)` -- `)` gets stripped + Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."), + // `bar` in `std::foo::bar` + None => ident_search_pat(last.ident).1, + } + } else { + // this shouldn't be possible, but sure + #[allow( + clippy::collapsible_else_if, + reason = "we want to keep these cases together, since they are both impossible" + )] + if qself_path.is_some() { + // last `>` in `` + Pat::Str(">") + } else { + Pat::Str("") + } + }; + (start, end) + }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) From baf09e2fa2d6ea3a45b4b03ddbd10566c13f3c4b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:07:19 +0200 Subject: [PATCH 0129/1900] OpaqueDef is called ImplTrait --- clippy_utils/src/check_proc_macro.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d0a769e96b726..a57c7c60cdaed 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -446,7 +446,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), TyKind::Tup([ty]) => ast_ty_search_pat(ty), TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), - TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qself_path, path) => { let start = if qself_path.is_some() { Pat::Str("<") From f5292331c3f6c631b0c5544f8c4401c50d032fb7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:09:26 +0200 Subject: [PATCH 0130/1900] fix Tup --- clippy_utils/src/check_proc_macro.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index a57c7c60cdaed..c34d6c72f4ca5 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -443,9 +443,11 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Never => (Pat::Str("!"), Pat::Str("!")), // Parenthesis are trimmed from the text before the search patterns are matched. // See: `span_matches_pat` - TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), - TyKind::Tup([ty]) => ast_ty_search_pat(ty), - TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + TyKind::Tup(tup) => match &**tup { + [] => (Pat::Str(")"), Pat::Str("(")), + [ty] => ast_ty_search_pat(ty), + [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + }, TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qself_path, path) => { let start = if qself_path.is_some() { From c7b75880dc541b298918928a55e56aec2359f4a5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:09:38 +0200 Subject: [PATCH 0131/1900] fix TraitObject --- clippy_utils/src/check_proc_macro.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c34d6c72f4ca5..2de131f05c26f 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -415,7 +415,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - use ast::{FnRetTy, MutTy, TyKind}; + use ast::{FnRetTy, MutTy, TraitObjectSyntax, TyKind}; match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), @@ -496,10 +496,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (start, end) }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { - (Pat::Str("dyn"), Pat::Str("")) + TyKind::TraitObject(_, trait_obj_syntax) => { + if let TraitObjectSyntax::Dyn = trait_obj_syntax { + (Pat::Str("dyn"), Pat::Str("")) + } else { + // NOTE: `TraitObject` is incomplete. It will always return true then. + (Pat::Str(""), Pat::Str("")) + } }, - // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } } From 742cba339a572a77809dd15bda0714e46b1e64ca Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:21:06 +0200 Subject: [PATCH 0132/1900] fix FnPtr --- clippy_utils/src/check_proc_macro.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 2de131f05c26f..00583d6766e04 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -29,7 +29,7 @@ use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; /// The search pattern to look for. Used by `span_matches_pat` #[derive(Clone)] @@ -415,29 +415,31 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - use ast::{FnRetTy, MutTy, TraitObjectSyntax, TyKind}; + use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind}; match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), TyKind::FnPtr(fn_ptr) => ( - if fn_ptr.safety.is_unsafe() { + if let Safety::Unsafe(_) = fn_ptr.safety { Pat::Str("unsafe") - } else if fn_ptr.abi != ExternAbi::Rust { - Pat::Str("extern") - } else { + } else if let Extern::Explicit(strlit, _) = fn_ptr.ext + && strlit.symbol == sym::rust + { Pat::MultiStr(&["fn", "extern"]) + } else { + Pat::Str("extern") }, - match fn_ptr.decl.output { - FnRetTy::DefaultReturn(_) => { - if let [.., ty] = fn_ptr.decl.inputs { - ast_ty_search_pat(ty).1 + match &fn_ptr.decl.output { + FnRetTy::Default(_) => { + if let [.., param] = &*fn_ptr.decl.inputs { + ast_ty_search_pat(¶m.ty).1 } else { Pat::Str("(") } }, - FnRetTy::Return(ty) => ast_ty_search_pat(ty).1, + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, }, ), TyKind::Never => (Pat::Str("!"), Pat::Str("!")), From 41614fcc88d3e11d61f94172537b9c9840a2a970 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:29:36 +0200 Subject: [PATCH 0133/1900] add Paren --- clippy_utils/src/check_proc_macro.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 00583d6766e04..10f7fe6494365 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -498,6 +498,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (start, end) }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::Paren(ty) => ast_ty_search_pat(ty), TyKind::TraitObject(_, trait_obj_syntax) => { if let TraitObjectSyntax::Dyn = trait_obj_syntax { (Pat::Str("dyn"), Pat::Str("")) From 2bd9f98928e051dbb6e065c64410599de6e7f93c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:34:50 +0200 Subject: [PATCH 0134/1900] add UnsafeBinder to the existing function as well --- clippy_utils/src/check_proc_macro.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 10f7fe6494365..f82c643151e82 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -406,6 +406,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, @@ -499,6 +500,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::Paren(ty) => ast_ty_search_pat(ty), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1), TyKind::TraitObject(_, trait_obj_syntax) => { if let TraitObjectSyntax::Dyn = trait_obj_syntax { (Pat::Str("dyn"), Pat::Str("")) From 0512002f12f3321526237e9026fafd56c2c99a3a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 15:21:04 +0200 Subject: [PATCH 0135/1900] add PinnedRef --- clippy_utils/src/check_proc_macro.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index f82c643151e82..ac4164943b40c 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -421,7 +421,9 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), - TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => { + (Pat::Str("&"), ast_ty_search_pat(ty).1) + }, TyKind::FnPtr(fn_ptr) => ( if let Safety::Unsafe(_) = fn_ptr.safety { Pat::Str("unsafe") From bbd0d46e1995354517f15baafccc84269e9f5476 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 15:35:09 +0200 Subject: [PATCH 0136/1900] add MacCall --- clippy_utils/src/check_proc_macro.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index ac4164943b40c..d4335689c1ea1 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -511,6 +511,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (Pat::Str(""), Pat::Str("")) } }, + TyKind::MacCall(mac_call) => { + let start = if let Some(first) = mac_call.path.segments.first() { + ident_search_pat(first.ident).0 + } else { + Pat::Str("") + }; + (start, Pat::Str("")) + }, _ => (Pat::Str(""), Pat::Str("")), } } From 17c0cabf988a0a68808b5e78d9c3bd0c2ed3d1cb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 12 Aug 2025 15:54:39 +0200 Subject: [PATCH 0137/1900] add ImplicitSelf --- clippy_utils/src/check_proc_macro.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d4335689c1ea1..2f1d380be5669 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -519,7 +519,9 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }; (start, Pat::Str("")) }, - _ => (Pat::Str(""), Pat::Str("")), + + // implicit, so has no contents to match against + TyKind::ImplicitSelf | _ => (Pat::Str(""), Pat::Str("")), } } From 457e0208dfbe916ed441586943195c294cd14162 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 12 Aug 2025 15:55:20 +0200 Subject: [PATCH 0138/1900] add misc kinds: Pat, CVarArgs, Typeof, Dummy, Err --- clippy_utils/src/check_proc_macro.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 2f1d380be5669..649460dd87506 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -521,7 +521,18 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }, // implicit, so has no contents to match against - TyKind::ImplicitSelf | _ => (Pat::Str(""), Pat::Str("")), + TyKind::ImplicitSelf + + // experimental + |TyKind::Pat(..) + + // unused + | TyKind::CVarArgs + | TyKind::Typeof(_) + + // placeholder + | TyKind::Dummy + | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")), } } From 217fc30fb65bdbdc46f17ab1eee9bbd6d9286d21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 16:04:45 +0200 Subject: [PATCH 0139/1900] (finally) implement `WithSearchPat` --- clippy_utils/src/check_proc_macro.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 649460dd87506..47f9d0591e929 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -570,6 +570,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self)); impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; From 2459ad66a48d2d693f4f30a599c28bfc36603333 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:38:50 +0200 Subject: [PATCH 0140/1900] make the lint early-pass --- clippy_lints/src/let_with_type_underscore.rs | 16 ++++++++-------- clippy_lints/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5b0f95ffc377f..24a4c321bdabd 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; -use rustc_hir::{LetStmt, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -26,14 +26,14 @@ declare_clippy_lint! { } declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); -impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { - if let Some(ty) = local.ty // Ensure that it has a type defined - && let TyKind::Infer(()) = &ty.kind // that type is '_' +impl EarlyLintPass for UnderscoreTyped { + fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) { + if let Some(ty) = &local.ty // Ensure that it has a type defined + && let TyKind::Infer = ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && let sm = cx.tcx.sess.source_map() + && let sm = cx.sess().source_map() && !local.span.in_external_macro(sm) - && !is_from_proc_macro(cx, ty) + && !is_from_proc_macro(cx, &**ty) { let span_to_remove = sm .span_extend_to_prev_char_before(ty.span, ':', true) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d468993e74445..10f0faa44088d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -743,7 +743,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); - store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); From c9fbcdcfcdc5290639c2a76929bec1ed07c08f8e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 21:58:49 +0800 Subject: [PATCH 0141/1900] Add let in let-chain completion support Example --- ```rust fn f() { if true && $0 {} } ``` -> ```rust fn f() { if true && let $1 = $0 {} } ``` --- .../crates/ide-completion/src/context/analysis.rs | 10 +++++++--- .../crates/ide-completion/src/tests/expression.rs | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319e..7d6fa553001cd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1171,19 +1171,23 @@ fn classify_name_ref<'db>( Some(res) }; - let is_in_condition = |it: &ast::Expr| { + fn is_in_condition(it: &ast::Expr) -> bool { (|| { let parent = it.syntax().parent()?; if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { Some(expr.condition()? == *it) - } else if let Some(expr) = ast::IfExpr::cast(parent) { + } else if let Some(expr) = ast::IfExpr::cast(parent.clone()) { Some(expr.condition()? == *it) + } else if let Some(expr) = ast::BinExpr::cast(parent) + && expr.op_token()?.kind() == T![&&] + { + Some(is_in_condition(&expr.into())) } else { None } })() .unwrap_or(false) - }; + } let make_path_kind_expr = |expr: ast::Expr| { let it = expr.syntax(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 33f729f016645..07eaeea97e553 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2275,3 +2275,13 @@ fn foo() { "#]], ); } + +#[test] +fn let_in_condition() { + check_edit("let", r#"fn f() { if $0 {} }"#, r#"fn f() { if let $1 = $0 {} }"#); +} + +#[test] +fn let_in_let_chain() { + check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#); +} From 183fbdc89b5f7f3944644a5d3d1c762d7ea35f90 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 Aug 2025 15:52:54 +0800 Subject: [PATCH 0142/1900] Fix `else` completion in `let _ = if x {} $0` --- .../ide-completion/src/context/analysis.rs | 11 +- .../ide-completion/src/tests/expression.rs | 347 ++++++++++++++++++ 2 files changed, 355 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319e..fc2d9e41989b8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -943,9 +943,14 @@ fn classify_name_ref<'db>( }; let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - ast::ExprStmt::cast(prev_sibling.clone()) - .and_then(|it| it.expr()) - .or_else(|| ast::Expr::cast(prev_sibling)) + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, + } + } })(); matches!(prev_expr, Some(ast::Expr::IfExpr(_))) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 33f729f016645..da5dddc6e8d7d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1210,6 +1210,353 @@ fn foo() { if foo {} el$0 { let x = 92; } } sn ppd "#]], ); + check( + r#" +fn foo() { let x = if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0 } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 let y = 92; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0 let y = 92; } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0; } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0; let y = 92; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 else {}; } +"#, + expect![[r#" + fn foo fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); } #[test] From 780319a75ba1ce24c5a4cec37051d9ad0a4f90de Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 23 Aug 2025 09:01:38 -0400 Subject: [PATCH 0143/1900] stabilize unstable `rwlock_downgrade` feature Signed-off-by: Connor Tsui --- library/std/src/sync/nonpoison/rwlock.rs | 5 +---- library/std/src/sync/poison/rwlock.rs | 6 +----- library/std/tests/sync/lib.rs | 1 - 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index eb0aef99cc1e7..0e0d7e058b077 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -638,7 +638,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// /// ``` /// #![feature(nonpoison_rwlock)] - /// #![feature(rwlock_downgrade)] /// /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; /// @@ -657,7 +656,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// /// ``` /// #![feature(nonpoison_rwlock)] - /// #![feature(rwlock_downgrade)] /// /// use std::sync::Arc; /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; @@ -690,8 +688,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// # let final_check = rw.read(); /// # assert_eq!(*final_check, 3); /// ``` - #[unstable(feature = "rwlock_downgrade", issue = "128203")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { let lock = s.lock; diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 0a463f3f9c7e3..1d52992ec2e0a 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -813,8 +813,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. /// /// ``` - /// #![feature(rwlock_downgrade)] - /// /// use std::sync::{RwLock, RwLockWriteGuard}; /// /// let rw = RwLock::new(0); @@ -831,8 +829,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// thread calling `downgrade` and any reads it performs after downgrading. /// /// ``` - /// #![feature(rwlock_downgrade)] - /// /// use std::sync::{Arc, RwLock, RwLockWriteGuard}; /// /// let rw = Arc::new(RwLock::new(1)); @@ -863,7 +859,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// # let final_check = rw.read().unwrap(); /// # assert_eq!(*final_check, 3); /// ``` - #[unstable(feature = "rwlock_downgrade", issue = "128203")] + #[stable(feature = "rwlock_downgrade", since = "CURRENT_RUSTC_VERSION")] pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { let lock = s.lock; diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index f874c2ba38951..d44a4d6935743 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -4,7 +4,6 @@ #![feature(once_cell_try)] #![feature(lock_value_accessors)] #![feature(reentrant_lock)] -#![feature(rwlock_downgrade)] #![feature(std_internals)] #![feature(sync_nonpoison)] #![feature(nonpoison_mutex)] From 4715c6d41a949358e8dd8dd96de0650939c8bf6b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 02:26:37 +0300 Subject: [PATCH 0144/1900] Add an option to remove reborrows from adjustment inlay hints Reborrows are consecutive deref then ref. Make it the default because reborrows are mostly useless to the programmer. Also rename `rust-analyzer.inlayHints.expressionAdjustmentHints.enable: "reborrow"` to `rust-analyzer.inlayHints.expressionAdjustmentHints.enable: "borrows"`, as it's not about reborrows but about any ref/deref and it's confusing with the new setting. --- .../crates/ide/src/inlay_hints.rs | 4 +- .../crates/ide/src/inlay_hints/adjustment.rs | 49 ++++++++++++++++++- .../crates/ide/src/static_index.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + .../crates/rust-analyzer/src/config.rs | 17 +++++-- .../docs/book/src/configuration_generated.md | 11 +++++ .../rust-analyzer/editors/code/package.json | 10 ++++ 7 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index d7d3171664945..cc44ae8fc8b9e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -306,6 +306,7 @@ pub struct InlayHintsConfig { pub generic_parameter_hints: GenericParameterHints, pub chaining_hints: bool, pub adjustment_hints: AdjustmentHints, + pub adjustment_hints_disable_reborrows: bool, pub adjustment_hints_mode: AdjustmentHintsMode, pub adjustment_hints_hide_outside_unsafe: bool, pub closure_return_type_hints: ClosureReturnTypeHints, @@ -430,7 +431,7 @@ pub enum LifetimeElisionHints { #[derive(Clone, Debug, PartialEq, Eq)] pub enum AdjustmentHints { Always, - ReborrowOnly, + BorrowsOnly, Never, } @@ -886,6 +887,7 @@ mod tests { closure_return_type_hints: ClosureReturnTypeHints::Never, closure_capture_hints: false, adjustment_hints: AdjustmentHints::Never, + adjustment_hints_disable_reborrows: false, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, binding_mode_hints: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index e39a5f8889a2c..0fd587a728408 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -47,7 +47,22 @@ pub(super) fn hints( let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); - let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + let mut adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + + if config.adjustment_hints_disable_reborrows { + // Remove consecutive deref-ref, i.e. reborrows. + let mut i = 0; + while i < adjustments.len().saturating_sub(1) { + let [current, next, ..] = &adjustments[i..] else { unreachable!() }; + if matches!(current.kind, Adjust::Deref(None)) + && matches!(next.kind, Adjust::Borrow(AutoBorrow::Ref(_))) + { + adjustments.splice(i..i + 2, []); + } else { + i += 1; + } + } + } if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr { // Don't show unnecessary reborrows for these, they will just repeat the inner ones again @@ -716,6 +731,38 @@ fn hello(it: &&[impl T]) { //^^(&** //^^) } +"#, + ); + } + + #[test] + fn disable_reborrows() { + check_with_config( + InlayHintsConfig { + adjustment_hints: AdjustmentHints::Always, + adjustment_hints_disable_reborrows: true, + ..DISABLED_CONFIG + }, + r#" +#![rustc_coherence_is_core] + +trait ToOwned { + type Owned; + fn to_owned(&self) -> Self::Owned; +} + +struct String; +impl ToOwned for str { + type Owned = String; + fn to_owned(&self) -> Self::Owned { String } +} + +fn a(s: &String) {} + +fn main() { + let s = "".to_owned(); + a(&s) +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 14b6529c61f8c..8c3275df1baab 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -169,6 +169,7 @@ impl StaticIndex<'_> { closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, adjustment_hints: crate::AdjustmentHints::Never, + adjustment_hints_disable_reborrows: true, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, implicit_drop_hints: false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 6c0aa19f57482..b17186f8d7b1e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1137,6 +1137,7 @@ impl flags::AnalysisStats { }, chaining_hints: true, adjustment_hints: ide::AdjustmentHints::Always, + adjustment_hints_disable_reborrows: true, adjustment_hints_mode: ide::AdjustmentHintsMode::Postfix, adjustment_hints_hide_outside_unsafe: false, closure_return_type_hints: ide::ClosureReturnTypeHints::Always, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index d4cd56dc55bf6..c2252185a3aa3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -226,6 +226,14 @@ config_data! { inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, + /// Disable reborrows in expression adjustments inlay hints. + /// + /// Reborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer. + /// + /// Note: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed. + inlayHints_expressionAdjustmentHints_disableReborrows: bool = + true, + /// Show inlay hints for type adjustments. inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, @@ -1888,12 +1896,14 @@ impl Config { AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, AdjustmentHintsDef::Never => match self.inlayHints_reborrowHints_enable() { ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { - ide::AdjustmentHints::ReborrowOnly + ide::AdjustmentHints::BorrowsOnly } ReborrowHintsDef::Never => ide::AdjustmentHints::Never, }, - AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, + AdjustmentHintsDef::Borrows => ide::AdjustmentHints::BorrowsOnly, }, + adjustment_hints_disable_reborrows: *self + .inlayHints_expressionAdjustmentHints_disableReborrows(), adjustment_hints_mode: match self.inlayHints_expressionAdjustmentHints_mode() { AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix, AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix, @@ -2822,7 +2832,8 @@ enum ReborrowHintsDef { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum AdjustmentHintsDef { - Reborrow, + #[serde(alias = "Reborrow")] + Borrows, #[serde(with = "true_or_always")] #[serde(untagged)] Always, diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 6ee956fe0dbf4..9a51212462db5 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -959,6 +959,17 @@ Default: `"never"` Show enum variant discriminant hints. +## rust-analyzer.inlayHints.expressionAdjustmentHints.disableReborrows {#inlayHints.expressionAdjustmentHints.disableReborrows} + +Default: `true` + +Disable reborrows in expression adjustments inlay hints. + +Reborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer. + +Note: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed. + + ## rust-analyzer.inlayHints.expressionAdjustmentHints.enable {#inlayHints.expressionAdjustmentHints.enable} Default: `"never"` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 4975ca858671b..2b2e25e11c88a 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2209,6 +2209,16 @@ } } }, + { + "title": "Inlay Hints", + "properties": { + "rust-analyzer.inlayHints.expressionAdjustmentHints.disableReborrows": { + "markdownDescription": "Disable reborrows in expression adjustments inlay hints.\n\nReborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer.\n\nNote: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Inlay Hints", "properties": { From 5e81912dae790647395834a027263a8c6160fd0b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 17:31:55 +0800 Subject: [PATCH 0145/1900] replace_arith_op not applicable on selected --- .../src/handlers/replace_arith_op.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 6b385a03625b7..440ab4d4604f2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -102,6 +102,9 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> { + if !ctx.has_empty_selection() { + return None; + } let expr = ctx.find_node_at_offset::()?; let op = match expr.op_kind() { @@ -163,7 +166,7 @@ impl ArithKind { #[cfg(test)] mod tests { - use crate::tests::check_assist; + use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; @@ -220,6 +223,18 @@ fn main() { fn main() { let x = 1.wrapping_add(2); } +"#, + ) + } + + #[test] + fn replace_arith_not_applicable_with_non_empty_selection() { + check_assist_not_applicable( + replace_arith_with_checked, + r#" +fn main() { + let x = 1 $0+$0 2; +} "#, ) } From c92af4d23285428c92e67fedb6d73a9a86a90cff Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 14:21:52 +0800 Subject: [PATCH 0146/1900] Add ReturnExpr completion suggest --- .../ide-completion/src/context/analysis.rs | 21 +++++++++-- .../ide-completion/src/context/tests.rs | 36 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319e..19f3d157beb7c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -559,7 +559,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_assign_token_at_trivia(token.clone()); + let token = prev_special_biased_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -724,6 +724,18 @@ fn expected_type_and_name<'db>( let def = sema.to_def(&it); (def.map(|def| def.ret_type(sema.db)), None) }, + ast::ReturnExpr(it) => { + let fn_ = sema.ancestors_with_macros(it.syntax().clone()) + .find_map(Either::::cast); + let ty = fn_.and_then(|f| match f { + Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Right(f) => { + let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; + Some(ty.return_type()) + }, + }); + (ty, None) + }, ast::ClosureExpr(it) => { let ty = sema.type_of_expr(&it.into()); ty.and_then(|ty| ty.original.as_callable(sema.db)) @@ -1868,7 +1880,7 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { None } -fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { +fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { while token.kind().is_trivia() && let Some(prev) = token.prev_token() && let T![=] @@ -1881,7 +1893,10 @@ fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { | T![-=] | T![|=] | T![&=] - | T![^=] = prev.kind() + | T![^=] + | T![return] + | T![break] + | T![continue] = prev.kind() { token = prev } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 7a8c70f190efc..9b1e086b666bc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -484,3 +484,39 @@ fn foo() { expect![[r#"ty: State, name: ?"#]], ); } + +#[test] +fn expected_type_return_expr() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() -> State { + let _: i32 = if true { + 8 + } else { + return $0; + }; +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} + +#[test] +fn expected_type_return_expr_in_closure() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let _f: fn() -> State = || { + let _: i32 = if true { + 8 + } else { + return $0; + }; + }; +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} From 31245cabecfc21c837745ec6253d63d5fc9ac75f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 08:49:40 +0300 Subject: [PATCH 0147/1900] Fix opaque generics The parent generics were incorrectly not considered for TAIT. I'm not convinced we should follow rustc here, also there are items (opaques) with more than 1 parent (opaque -> fn/type alias -> impl/trait) and I'm not sure we properly account for that in all places, but for now I left it as-is. Also fix a bug where lifetimes' indices were incorrect when there is a self param (they started from 0 instead of 1). --- .../crates/hir-ty/src/next_solver/generics.rs | 68 +++++++------------ .../crates/hir-ty/src/next_solver/util.rs | 1 - .../crates/hir-ty/src/tests/regression.rs | 2 + .../hir-ty/src/tests/regression/new_solver.rs | 26 +++++++ 4 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index 48f5e73f2562d..a3ba8eb83453e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -12,7 +12,7 @@ use hir_def::{ }, }; use hir_expand::name::Name; -use intern::Symbol; +use intern::{Symbol, sym}; use la_arena::Arena; use rustc_type_ir::inherent::Ty as _; use triomphe::Arc; @@ -24,18 +24,13 @@ use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, Solver use super::{DbInterner, GenericArg}; pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { - let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| { + let mk_lt = |index, lt: &LifetimeParamData| { let name = lt.name.symbol().clone(); - let index = index as u32; let kind = GenericParamDefKind::Lifetime; GenericParamDef { name, index, kind } }; - let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| { - let name = p - .name() - .map(|n| n.symbol().clone()) - .unwrap_or_else(|| Name::missing().symbol().clone()); - let index = (len_lt + index) as u32; + let mk_ty = |index, p: &TypeOrConstParamData| { + let name = p.name().map(|n| n.symbol().clone()).unwrap_or_else(|| sym::MISSING_NAME); let kind = match p { TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, @@ -43,33 +38,25 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { GenericParamDef { name, index, kind } }; let own_params_for_generic_params = |params: &GenericParams| { - if params.trait_self_param().is_some() { - let len_lt = params.len_lifetimes() + 1; - params - .iter_type_or_consts() - .take(1) - .enumerate() - .map(|t| mk_ty(0, (t.0, t.1.1))) - .chain(params.iter_lt().enumerate().map(mk_lt)) - .chain( - params - .iter_type_or_consts() - .skip(1) - .enumerate() - .map(|t| mk_ty(len_lt, (t.0, t.1.1))), - ) - .collect() - } else { - let len_lt = params.len_lifetimes(); - params - .iter_lt() - .enumerate() - .map(mk_lt) - .chain( - params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))), - ) - .collect() + let mut result = Vec::with_capacity(params.len()); + let mut type_and_consts = params.iter_type_or_consts(); + let mut index = 0; + if let Some(self_param) = params.trait_self_param() { + result.push(mk_ty(0, ¶ms[self_param])); + type_and_consts.next(); + index += 1; } + result.extend(params.iter_lt().map(|(_, data)| { + let lt = mk_lt(index, data); + index += 1; + lt + })); + result.extend(type_and_consts.map(|(_, data)| { + let ty = mk_ty(index, data); + index += 1; + ty + })); + result }; let (parent, own_params) = match (def.try_into(), def) { @@ -82,12 +69,9 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { // The opaque type itself does not have generics - only the parent function (Some(GenericDefId::FunctionId(function_id)), vec![]) } - crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => ( - None, - own_params_for_generic_params( - &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)), - ), - ), + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { + (Some(type_alias_id.into()), Vec::new()) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { let param = TypeOrConstParamData::TypeParamData(TypeParamData { name: None, @@ -95,7 +79,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { provenance: TypeParamProvenance::TypeParamList, }); // Yes, there is a parent but we don't include it in the generics - (None, vec![mk_ty(0, (0, ¶m))]) + (None, vec![mk_ty(0, ¶m)]) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index cedc203f7f5aa..1db02e9eb611c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -685,7 +685,6 @@ pub fn explicit_item_bounds<'db>( LifetimeElisionKind::AnonymousReportError, ); - let trait_args = GenericArgs::identity_for_item(interner, trait_.into()); let item_args = GenericArgs::identity_for_item(interner, def_id); let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 966433369aa88..6a3f2286215f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1,3 +1,5 @@ +mod new_solver; + use expect_test::expect; use super::{check_infer, check_no_mismatches, check_types}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs new file mode 100644 index 0000000000000..059f4ad32a53b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -0,0 +1,26 @@ +use expect_test::expect; + +use super::check_infer; + +#[test] +fn opaque_generics() { + check_infer( + r#" +//- minicore: iterator +pub struct Grid {} + +impl<'a> IntoIterator for &'a Grid { + type Item = &'a (); + + type IntoIter = impl Iterator; + + fn into_iter(self) -> Self::IntoIter { + } +} + "#, + expect![[r#" + 150..154 'self': &'a Grid + 174..181 '{ }': impl Iterator + "#]], + ); +} From 5ddf6846d5659ed28e1ea25e273e4c84082cf730 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 24 Aug 2025 23:17:23 +0900 Subject: [PATCH 0148/1900] fix: Masquerade as nightly cargo when invoking flycheck with `-Zscript` --- src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 233bedec6900a..01ac75c09aee6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -588,6 +588,7 @@ impl FlycheckActor { cmd.arg("--manifest-path"); cmd.arg(manifest_path); if manifest_path.extension() == Some("rs") { + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.arg("-Zscript"); } } From ca42d0775a33ae6f977e4cd42fab17f9aced6a59 Mon Sep 17 00:00:00 2001 From: Emmanuel Ferdman Date: Sun, 24 Aug 2025 08:03:02 -0700 Subject: [PATCH 0149/1900] Fix rust-analyzer-contributors reference Signed-off-by: Emmanuel Ferdman --- src/tools/rust-analyzer/docs/book/src/contributing/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index 57c7a9c5996d1..ad2816b18ac14 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -276,7 +276,7 @@ There are two sets of people with extra permissions: Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention. Don't feel pressured to review assigned PRs though. If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! -* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml). This team has general triaging permissions allowing to label, close and re-open issues. ## Synchronizing subtree changes From 40ff07622e5122aecc2cc03a61152782bc2a0568 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 27 Jul 2025 18:06:59 +0800 Subject: [PATCH 0150/1900] fix: `never_loop` forget to remove break in nested loop --- clippy_lints/src/loops/never_loop.rs | 59 +++++++++++++---- tests/ui/never_loop.rs | 24 +++++++ tests/ui/never_loop.stderr | 97 +++++++++++++++++++++++++++- tests/ui/never_loop_fixable.fixed | 2 +- tests/ui/never_loop_fixable.rs | 2 +- 5 files changed, 170 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 8a253ae5810f6..0ac66fa98650a 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -22,7 +22,10 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::Diverging { ref break_spans } => { + NeverLoopResult::Diverging { + ref break_spans, + ref never_spans, + } => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -34,12 +37,16 @@ pub(super) fn check<'tcx>( { // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not // appropriate. - let app = if !contains_any_break_or_continue(block) && label.is_none() { + let mut app = if !contains_any_break_or_continue(block) && label.is_none() { Applicability::MachineApplicable } else { Applicability::Unspecified }; + if !never_spans.is_empty() { + app = Applicability::HasPlaceholders; + } + let mut suggestions = vec![( for_span.with_hi(iterator.span.hi()), for_to_if_let_sugg(cx, iterator, pat), @@ -51,6 +58,13 @@ pub(super) fn check<'tcx>( suggestions, app, ); + + for span in never_spans { + diag.span_help( + *span, + "this code is unreachable. Consider moving the reachable parts out", + ); + } } }); }, @@ -77,13 +91,16 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool { /// The first two bits of information are in this enum, and the last part is in the /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by /// scope. -#[derive(Clone)] +#[derive(Clone, Debug)] enum NeverLoopResult { /// A continue may occur for the main loop. MayContinueMainLoop, /// We have not encountered any main loop continue, /// but we are diverging (subsequent control flow is not reachable) - Diverging { break_spans: Vec }, + Diverging { + break_spans: Vec, + never_spans: Vec, + }, /// We have not encountered any main loop continue, /// and subsequent control flow is (possibly) reachable Normal, @@ -128,14 +145,18 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult ( NeverLoopResult::Diverging { break_spans: mut break_spans1, + never_spans: mut never_spans1, }, NeverLoopResult::Diverging { break_spans: mut break_spans2, + never_spans: mut never_spans2, }, ) => { break_spans1.append(&mut break_spans2); + never_spans1.append(&mut never_spans2); NeverLoopResult::Diverging { break_spans: break_spans1, + never_spans: never_spans1, } }, } @@ -207,6 +228,8 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { } return vec![stmt.span]; + } else if let Node::Block(_) = cx.tcx.parent_hir_node(expr.hir_id) { + return vec![expr.span]; } vec![] @@ -270,10 +293,13 @@ fn never_loop_expr<'tcx>( ExprKind::Match(e, arms, _) => { let e = never_loop_expr(cx, e, local_labels, main_loop_id); combine_seq(e, || { - arms.iter() - .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| { - combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) - }) + arms.iter().fold( + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + }, + |a, b| combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)), + ) }) }, ExprKind::Block(b, _) => { @@ -296,6 +322,7 @@ fn never_loop_expr<'tcx>( } else { NeverLoopResult::Diverging { break_spans: all_spans_after_expr(cx, expr), + never_spans: vec![], } } }, @@ -306,7 +333,10 @@ fn never_loop_expr<'tcx>( combine_seq(first, || { // checks if break targets a block instead of a loop mark_block_as_reachable(expr, local_labels); - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }) }, ExprKind::Break(dest, e) => { @@ -322,11 +352,15 @@ fn never_loop_expr<'tcx>( } else { all_spans_after_expr(cx, expr) }, + never_spans: vec![], } }) }, ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }), ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { @@ -356,7 +390,10 @@ fn never_loop_expr<'tcx>( }; let result = combine_seq(result, || { if cx.typeck_results().expr_ty(expr).is_never() { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: all_spans_after_expr(cx, expr), + } } else { NeverLoopResult::Normal } diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 48d4b8ad15196..01db64a446c0f 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -498,3 +498,27 @@ fn issue15059() { () } } + +fn issue15350() { + 'bar: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + break 'bar; + } + } + + 'foo: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + loop { + //~^ never_loop + println!("This will still run"); + break 'foo; + } + } + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 54b463266a3a5..4fda06cff4ab9 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -193,6 +193,19 @@ LL | | return; LL | | } | |_____^ | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:440:9 + | +LL | return; + | ^^^^^^^ help: if you need the first element of the iterator, try writing | LL - 'outer: for v in 0..10 { @@ -297,5 +310,87 @@ LL ~ LL ~ | -error: aborting due to 24 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:503:5 + | +LL | / 'bar: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'bar: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:512:5 + | +LL | / 'foo: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'foo: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:517:13 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'foo; +LL | | } + | |_____________^ + +error: aborting due to 29 previous errors diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed index 5bc9ff1bb4df0..00c2af93a28ff 100644 --- a/tests/ui/never_loop_fixable.fixed +++ b/tests/ui/never_loop_fixable.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return)] +#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] fn no_break_or_continue_loop() { if let Some(i) = [1, 2, 3].iter().next() { diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs index 9782bc107e9a6..de85599f094d6 100644 --- a/tests/ui/never_loop_fixable.rs +++ b/tests/ui/never_loop_fixable.rs @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return)] +#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] fn no_break_or_continue_loop() { for i in [1, 2, 3].iter() { From 56bc9c35b9f625154f42f53f4b1e7843f02533ad Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 Aug 2025 16:44:42 +0800 Subject: [PATCH 0151/1900] Fix ExprStmt delete semicolon for toggle_macro_delimiter --- .../src/handlers/toggle_macro_delimiter.rs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 504e12f93df61..bf1546986ed27 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -1,6 +1,6 @@ use ide_db::assists::AssistId; use syntax::{ - AstNode, T, + AstNode, SyntaxToken, T, ast::{self, syntax_factory::SyntaxFactory}, }; @@ -39,7 +39,7 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) let makro = ctx.find_node_at_offset::()?; let cursor_offset = ctx.offset(); - let semicolon = makro.semicolon_token(); + let semicolon = macro_semicolon(&makro); let token_tree = makro.token_tree()?; let ltoken = token_tree.left_delimiter_token()?; @@ -95,6 +95,14 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) ) } +fn macro_semicolon(makro: &ast::MacroCall) -> Option { + makro.semicolon_token().or_else(|| { + let macro_expr = ast::MacroExpr::cast(makro.syntax().parent()?)?; + let expr_stmt = ast::ExprStmt::cast(macro_expr.syntax().parent()?)?; + expr_stmt.semicolon_token() + }) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -119,7 +127,29 @@ macro_rules! sth { sth!{ } "#, - ) + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() { + sth!$0( ); +} + "#, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() { + sth!{ } +} + "#, + ); } #[test] From a0aa1c1360916590f9a10d1b3d8586771c839d38 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 16:44:28 +0300 Subject: [PATCH 0152/1900] Cache trait solving across queries in the same revision Caching trait solving can do a lot to speed. Unfortunately it also consume a huge amount of memory. Therefore, as part of the migration to the new solver Jack Huey disabled caching of trait solving (he made the query transparent). The PR proposes a middle ground: do cache trait solving, but only for the same revision. This allows us to be safe because during a revision the inputs cannot change. The result is hopefully much better performance to features that tend to do a bulk of trait solving, and also repeat the same query (e.g. inference then IDE features). There is another limitation: results are only cached in the same thread, to remove the need for synchronization which will be expensive. More measurements are required to check whether it's better to use a synchronized global cache, or maybe stay with a thread-local cache but batch multiple feature requests (highlighting, inlay hints etc.) of the same file to the same thread. Alongside the actual cache we store the revision, because we need to verify it (we can't eagerly clear caches when incrementing the revision), and also the address of the db to prevent multiple dbs from interleaving (this is mostly relevant in tests, although injected highlighting also uses a new db, therefore maybe it's better to move it to a separate thread). This "games" analysis-stats to both be way faster and use way more memory; the former is because analysis-stats doesn't increment revisions, therefore all queries share the cache and hit ratio is way too good, the latter is because analysis-stats doesn't increment revisions and therefore the cache isn't cleared. Both are not representative of a typical IDE scenario. --- .../rust-analyzer/crates/base-db/src/lib.rs | 28 ++++++- .../crates/hir-def/src/test_db.rs | 21 ++++- .../rust-analyzer/crates/hir-ty/src/infer.rs | 82 +++++++++---------- .../crates/hir-ty/src/mir/lower.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 39 +++++---- .../crates/hir-ty/src/test_db.rs | 23 +++++- .../rust-analyzer/crates/ide-db/src/lib.rs | 9 +- 7 files changed, 140 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index dbf949c470c4d..14544acc11bdf 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,7 +7,12 @@ pub use salsa_macros; mod change; mod input; -use std::{cell::RefCell, hash::BuildHasherDefault, panic, sync::Once}; +use std::{ + cell::RefCell, + hash::BuildHasherDefault, + panic, + sync::{Once, atomic::AtomicUsize}, +}; pub use crate::{ change::FileChange, @@ -328,6 +333,27 @@ pub trait SourceDatabase: salsa::Database { #[doc(hidden)] fn crates_map(&self) -> Arc; + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); +} + +static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Nonce(usize); + +impl Default for Nonce { + #[inline] + fn default() -> Self { + Nonce::new() + } +} + +impl Nonce { + #[inline] + pub fn new() -> Nonce { + Nonce(NEXT_NONCE.fetch_add(1, std::sync::atomic::Ordering::SeqCst)) + } } /// Crate related data shared by the whole workspace. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index e30a5b65a1f79..1e2f354f975cb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -3,7 +3,7 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb, + Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_expand::{InFile, files::FilePosition}; @@ -20,12 +20,12 @@ use crate::{ }; #[salsa_macros::db] -#[derive(Clone)] pub(crate) struct TestDB { storage: salsa::Storage, files: Arc, crates_map: Arc, events: Arc>>>, + nonce: Nonce, } impl Default for TestDB { @@ -44,6 +44,7 @@ impl Default for TestDB { events, files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. @@ -53,6 +54,18 @@ impl Default for TestDB { } } +impl Clone for TestDB { + fn clone(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + crates_map: self.crates_map.clone(), + events: self.events.clone(), + nonce: Nonce::new(), + } + } +} + #[salsa_macros::db] impl salsa::Database for TestDB {} @@ -117,6 +130,10 @@ impl SourceDatabase for TestDB { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } impl TestDB { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index d778cc0e30ed4..71b33a0e9035f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -88,54 +88,52 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { - crate::next_solver::with_new_cache(|| { - let _p = tracing::info_span!("infer_query").entered(); - let resolver = def.resolver(db); - let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); - - match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), - DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), - }, + let _p = tracing::info_span!("infer_query").entered(); + let resolver = def.resolver(db); + let body = db.body(def); + let mut ctx = InferenceContext::new(db, def, &body, resolver); + + match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); + } + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), + DefWithBodyId::VariantId(v) => { + ctx.return_ty = TyBuilder::builtin( + match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), }, - ); - } + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, + }, + ); } + } - ctx.infer_body(); + ctx.infer_body(); - ctx.infer_mut_body(); + ctx.infer_mut_body(); - ctx.infer_closures(); + ctx.infer_closures(); - Arc::new(ctx.resolve_all()) - }) + Arc::new(ctx.resolve_all()) } pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 052be11e433f3..8c03ca939e370 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2168,9 +2168,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result, } -pub(crate) use tls_cache::with_new_cache; mod tls_cache { use crate::db::HirDatabase; use super::DbInterner; + use base_db::Nonce; use rustc_type_ir::search_graph::GlobalCache; + use salsa::Revision; use std::cell::RefCell; - scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell>>); + struct Cache { + cache: GlobalCache>, + revision: Revision, + db_nonce: Nonce, + } - pub(crate) fn with_new_cache(f: impl FnOnce() -> T) -> T { - GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f) + thread_local! { + static GLOBAL_CACHE: RefCell> = const { RefCell::new(None) }; } pub(super) fn with_cache<'db, T>( - _db: &'db dyn HirDatabase, + db: &'db dyn HirDatabase, f: impl FnOnce(&mut GlobalCache>) -> T, ) -> T { - // SAFETY: No idea - let call = move |slot: &RefCell<_>| { + GLOBAL_CACHE.with_borrow_mut(|handle| { + let (db_nonce, revision) = db.nonce_and_revision(); + let handle = match handle { + Some(handle) => { + if handle.revision != revision || db_nonce != handle.db_nonce { + *handle = Cache { cache: GlobalCache::default(), revision, db_nonce }; + } + handle + } + None => handle.insert(Cache { cache: GlobalCache::default(), revision, db_nonce }), + }; + + // SAFETY: No idea f(unsafe { std::mem::transmute::< &mut GlobalCache>, &mut GlobalCache>, - >(&mut *slot.borrow_mut()) + >(&mut handle.cache) }) - }; - if GLOBAL_CACHE.is_set() { - GLOBAL_CACHE.with(call) - } else { - GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call)) - } + }) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 775136dc0cbf7..2a92aa52e0cd2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -3,8 +3,8 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb, SourceDatabase, - SourceRoot, SourceRootId, SourceRootInput, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, + SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; @@ -17,12 +17,12 @@ use test_utils::extract_annotations; use triomphe::Arc; #[salsa_macros::db] -#[derive(Clone)] pub(crate) struct TestDB { storage: salsa::Storage, files: Arc, crates_map: Arc, events: Arc>>>, + nonce: Nonce, } impl Default for TestDB { @@ -41,6 +41,7 @@ impl Default for TestDB { events, files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. @@ -50,6 +51,18 @@ impl Default for TestDB { } } +impl Clone for TestDB { + fn clone(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + crates_map: self.crates_map.clone(), + events: self.events.clone(), + nonce: Nonce::new(), + } + } +} + impl fmt::Debug for TestDB { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TestDB").finish() @@ -109,6 +122,10 @@ impl SourceDatabase for TestDB { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } #[salsa_macros::db] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 9d2474d91da67..44bccd86d8709 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -51,7 +51,7 @@ use salsa::Durability; use std::{fmt, mem::ManuallyDrop}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, RootQueryDb, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, query_group, }; use hir::{ @@ -83,6 +83,7 @@ pub struct RootDatabase { storage: ManuallyDrop>, files: Arc, crates_map: Arc, + nonce: Nonce, } impl std::panic::RefUnwindSafe for RootDatabase {} @@ -102,6 +103,7 @@ impl Clone for RootDatabase { storage: self.storage.clone(), files: self.files.clone(), crates_map: self.crates_map.clone(), + nonce: Nonce::new(), } } } @@ -165,6 +167,10 @@ impl SourceDatabase for RootDatabase { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } impl Default for RootDatabase { @@ -179,6 +185,7 @@ impl RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()), files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; // This needs to be here otherwise `CrateGraphBuilder` will panic. db.set_all_crates(Arc::new(Box::new([]))); From aa49c0b8bb27700ab77fd8cb7231d18f4f6d2e97 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 25 Aug 2025 20:19:33 +0300 Subject: [PATCH 0153/1900] Normalize all types when finishing inference The new solver does not eagerly normalize, but things after inference expect types to be normalized. rustc does the same. Also, I'm afraid other things in r-a don't expect results of the solver to be unnormalized. We'll need to handle that. --- .../crates/hir-ty/src/infer/unify.rs | 3 +++ .../hir-ty/src/tests/regression/new_solver.rs | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index a709aebfa9c1b..bb4782bd41942 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -621,6 +621,9 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { + let t = self.resolve_with_fallback(t, &|_, _, d, _| d); + let t = self.normalize_associated_types_in(t); + // Resolve again, because maybe normalization inserted infer vars. self.resolve_with_fallback(t, &|_, _, d, _| d) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 059f4ad32a53b..20190fbc04564 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -24,3 +24,29 @@ impl<'a> IntoIterator for &'a Grid { "#]], ); } + +#[test] +fn normalization() { + check_infer( + r#" +//- minicore: iterator, iterators +fn main() { + _ = [0i32].into_iter().filter_map(|_n| Some(1i32)); +} + "#, + expect![[r#" + 10..69 '{ ...2)); }': () + 16..17 '_': FilterMap, impl FnMut(i32) -> Option> + 16..66 '_ = [0...1i32))': () + 20..26 '[0i32]': [i32; 1] + 20..38 '[0i32]...iter()': IntoIter + 20..66 '[0i32]...1i32))': FilterMap, impl FnMut(i32) -> Option> + 21..25 '0i32': i32 + 50..65 '|_n| Some(1i32)': impl FnMut(i32) -> Option + 51..53 '_n': i32 + 55..59 'Some': fn Some(i32) -> Option + 55..65 'Some(1i32)': Option + 60..64 '1i32': i32 + "#]], + ); +} From 4b5bb18c57270a5b823235397067ae82c64bf1f1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 25 Aug 2025 20:56:45 +0300 Subject: [PATCH 0154/1900] Don't map Chalk's `Normalize` to next solver's `NormalizesTo` `NormalizesTo` is a private predicate that should not be used outside the solver. For normalization, rustc uses `AliasRelate`, so replace with that. --- .../crates/hir-ty/src/next_solver/mapping.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index cad51fd85f55a..8f6296b145409 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -808,14 +808,24 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal unimplemented!(), }; let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); - let alias = rustc_type_ir::AliasTerm::new( + let alias = Ty::new( interner, - from_assoc_type_id(proj_ty.associated_ty_id).into(), - args, - ); + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Projection, + rustc_type_ir::AliasTy::new( + interner, + from_assoc_type_id(proj_ty.associated_ty_id).into(), + args, + ), + ), + ) + .into(); let term = normalize.ty.to_nextsolver(interner).into(); - let normalizes_to = rustc_type_ir::NormalizesTo { alias, term }; - PredicateKind::NormalizesTo(normalizes_to) + PredicateKind::AliasRelate( + alias, + term, + rustc_type_ir::AliasRelationDirection::Equate, + ) } chalk_ir::DomainGoal::WellFormed(well_formed) => { let term = match well_formed { From 169eb87d6088d0c63884b9faa0287ee474a4101b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 25 Aug 2025 16:06:32 +0500 Subject: [PATCH 0155/1900] Remove expect collapsible_span_lint_calls from span_lint_and_then --- clippy_lints/src/uninit_vec.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index cee4a53f03cbe..51116b5eba9e1 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; @@ -95,16 +95,13 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( + span_lint_and_help( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, + None, + "initialize the buffer or wrap the content in `MaybeUninit`", ); } } else { From 9fd57df6397b0e2b0cd4db32884ed5e5443c7906 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 12 Aug 2025 23:55:29 -0700 Subject: [PATCH 0156/1900] add tests, some with incorrect lifetime extension behavior --- .../format-args-temporary-scopes.e2024.stderr | 15 +++ .../borrowck/format-args-temporary-scopes.rs | 20 +++ .../ui/drop/super-let-tail-expr-drop-order.rs | 122 ++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr create mode 100644 tests/ui/borrowck/format-args-temporary-scopes.rs create mode 100644 tests/ui/drop/super-let-tail-expr-drop-order.rs diff --git a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr new file mode 100644 index 0000000000000..923c929a68d60 --- /dev/null +++ b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr @@ -0,0 +1,15 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/format-args-temporary-scopes.rs:13:25 + | +LL | println!("{:?}", { &temp() }); + | ---^^^^^--- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/format-args-temporary-scopes.rs b/tests/ui/borrowck/format-args-temporary-scopes.rs new file mode 100644 index 0000000000000..e6223d7c55183 --- /dev/null +++ b/tests/ui/borrowck/format-args-temporary-scopes.rs @@ -0,0 +1,20 @@ +//! Test for #145784 as it relates to format arguments: arguments to macros such as `println!` +//! should obey normal temporary scoping rules. +//@ revisions: e2021 e2024 +//@ [e2021] check-pass +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 + +fn temp() {} + +fn main() { + // In Rust 2024, block tail expressions are temporary scopes, so the result of `temp()` is + // dropped after evaluating `&temp()`. + println!("{:?}", { &temp() }); + //[e2024]~^ ERROR: temporary value dropped while borrowed [E0716] + + // In Rust 1.89, `format_args!` extended the lifetime of all extending expressions in its + // arguments when provided with two or more arguments. This caused the result of `temp()` to + // outlive the result of the block, making this compile. + println!("{:?}{:?}", { &temp() }, ()); +} diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs new file mode 100644 index 0000000000000..5aa17bba21f1b --- /dev/null +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -0,0 +1,122 @@ +//! Test for #145784: the argument to `pin!` should be treated as an extending expression if and +//! only if the whole `pin!` invocation is an extending expression. Likewise, since `pin!` is +//! implemented in terms of `super let`, test the same for `super let` initializers. Since the +//! argument to `pin!` and the initializer of `super let` are not temporary drop scopes, this only +//! affects lifetimes in two cases: +//! +//! - Block tail expressions in Rust 2024, which are both extending expressions and temporary drop +//! scopes; treating them as extending expressions within a non-extending `pin!` resulted in borrow +//! expression operands living past the end of the block. +//! +//! - Nested `super let` statements, which can have their binding and temporary lifetimes extended +//! when the block they're in is an extending expression. +//! +//! For more information on extending expressions, see +//! https://doc.rust-lang.org/reference/destructors.html#extending-based-on-expressions +//! +//! For tests that `super let` initializers aren't temporary drop scopes, and tests for +//! lifetime-extended `super let`, see tests/ui/borrowck/super-let-lifetime-and-drop.rs +//@ run-pass +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 + +#![feature(super_let)] + +use std::cell::RefCell; +use std::pin::pin; + +fn main() { + // Test block arguments to `pin!` in non-extending expressions. + // In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries + // should outlive the `pin!` invocation. + // In Rust 2024, block tail expressions are temporary drop scopes, so their temporaries should + // be dropped after evaluating the tail expression within the `pin!` invocation. + // By nesting two `pin!` calls, this ensures non-extended `pin!` doesn't extend an inner `pin!`. + assert_drop_order(1..=3, |o| { + #[cfg(e2021)] + ( + pin!(( + pin!({ &o.log(3) as *const LogDrop<'_> }), + drop(o.log(1)), + )), + drop(o.log(2)), + ); + #[cfg(e2024)] + ( + pin!(( + pin!({ &o.log(3) as *const LogDrop<'_> }), + drop(o.log(1)), + )), + drop(o.log(2)), + ); + }); + + // The same holds for `super let` initializers in non-extending expressions. + assert_drop_order(1..=4, |o| { + #[cfg(e2021)] + ( + { + super let _ = { + super let _ = { &o.log(4) as *const LogDrop<'_> }; + drop(o.log(1)) + }; + drop(o.log(2)) + }, + drop(o.log(3)), + ); + #[cfg(e2024)] + ( + { + super let _ = { + super let _ = { &o.log(4) as *const LogDrop<'_> }; + drop(o.log(1)) + }; + drop(o.log(2)) + }, + drop(o.log(3)), + ); + }); + + // Within an extending expression, the argument to `pin!` is also an extending expression, + // allowing borrow operands in block tail expressions to have extended lifetimes. + assert_drop_order(1..=2, |o| { + let _ = pin!({ &o.log(2) as *const LogDrop<'_> }); + drop(o.log(1)); + }); + + // The same holds for `super let` initializers in extending expressions. + assert_drop_order(1..=2, |o| { + let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; }; + drop(o.log(1)); + }); +} + +// # Test scaffolding... + +struct DropOrder(RefCell>); +struct LogDrop<'o>(&'o DropOrder, u64); + +impl DropOrder { + fn log(&self, n: u64) -> LogDrop<'_> { + LogDrop(self, n) + } +} + +impl<'o> Drop for LogDrop<'o> { + fn drop(&mut self) { + self.0.0.borrow_mut().push(self.1); + } +} + +#[track_caller] +fn assert_drop_order( + ex: impl IntoIterator, + f: impl Fn(&DropOrder), +) { + let order = DropOrder(RefCell::new(Vec::new())); + f(&order); + let order = order.0.into_inner(); + let expected: Vec = ex.into_iter().collect(); + assert_eq!(order, expected); +} From 5fb947f650ef123ab91bb3456161f50f481adf9b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 14:45:16 +0200 Subject: [PATCH 0157/1900] Add test. --- library/coretests/tests/fmt/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index 16f116d25901c..11de632fb70c7 100644 --- a/library/coretests/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -12,6 +12,12 @@ fn test_lifetime() { let a = format_args!("hello {a} {a:?}"); assert_eq!(a.to_string(), "hello hello hello hello hello hello hello"); + // Check that temporaries as arguments are extended. + let b = format_args!("{}", String::new()); + let c = format_args!("{}{}", String::new(), String::new()); + assert_eq!(b.to_string(), ""); + assert_eq!(c.to_string(), ""); + // Without arguments, it should also work in consts. const A: std::fmt::Arguments<'static> = format_args!("hello"); assert_eq!(A.to_string(), "hello"); From aa9c8ceb73c88674a792719dee8bfbcbee22a360 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 14:44:41 +0200 Subject: [PATCH 0158/1900] Remove 1-argument special case from format_args!(). The argument needs to be lifetime-extended, so this special case isn't actually perfectly equivalent to the general case. --- compiler/rustc_ast_lowering/src/format.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index ec9d26eb33f48..2871430030c45 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -487,26 +487,6 @@ fn expand_format_args<'hir>( // Generate: // [] (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[])))) - } else if argmap.len() == 1 && arguments.len() == 1 { - // Only one argument, so we don't need to make the `args` tuple. - // - // Generate: - // super let args = [::new_display(&arg)]; - let args = ctx.arena.alloc_from_iter(argmap.iter().map( - |(&(arg_index, ty), &placeholder_span)| { - let arg = &arguments[arg_index]; - let placeholder_span = - placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt()); - let arg = ctx.lower_expr(&arg.expr); - let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg)); - make_argument(ctx, placeholder_span, ref_arg, ty) - }, - )); - let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args))); - let args_ident = Ident::new(sym::args, macsp); - let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident); - let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args)); - (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id))) } else { // Generate: // super let args = (&arg0, &arg1, &…); From bc13565de8a16c9ba0c0da51787dc60d254c6dfa Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 14:46:03 +0200 Subject: [PATCH 0159/1900] Update tests. --- tests/coverage/issue-83601.cov-map | 34 ++--- tests/coverage/issue-84561.cov-map | 136 +++++++++--------- ...745-avoid-expr-from-macro-expansion.stderr | 6 +- tests/ui/issues/issue-27592.stderr | 12 +- tests/ui/suggestions/issue-97760.stderr | 7 +- tests/ui/unpretty/exhaustive.hir.stdout | 3 +- .../ui/unpretty/flattened-format-args.stdout | 3 +- 7 files changed, 96 insertions(+), 105 deletions(-) diff --git a/tests/coverage/issue-83601.cov-map b/tests/coverage/issue-83601.cov-map index d1d751ff24b86..e42b5591c0fb3 100644 --- a/tests/coverage/issue-83601.cov-map +++ b/tests/coverage/issue-83601.cov-map @@ -1,30 +1,22 @@ Function name: issue_83601::main -Raw bytes (76): 0x[01, 01, 01, 05, 09, 0e, 01, 06, 01, 00, 0a, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 05, 01, 09, 00, 0c, 05, 00, 0f, 00, 15, 05, 01, 05, 00, 0f, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 01, 00, 02] +Raw bytes (74): 0x[01, 01, 00, 0e, 01, 06, 01, 00, 0a, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => $DIR/issue-83601.rs -Number of expressions: 1 -- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +Number of expressions: 0 Number of file 0 mappings: 14 - Code(Counter(0)) at (prev + 6, 1) to (start + 0, 10) - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12) - Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) -- Code(Counter(1)) at (prev + 0, 15) to (start + 0, 21) -- Code(Counter(1)) at (prev + 1, 5) to (start + 0, 15) -- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20) - = (c1 - c2) -- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2) - = (c1 - c2) -Highest counter ID seen: c1 +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 diff --git a/tests/coverage/issue-84561.cov-map b/tests/coverage/issue-84561.cov-map index 2b643ea599ed0..e5bb1afdcc263 100644 --- a/tests/coverage/issue-84561.cov-map +++ b/tests/coverage/issue-84561.cov-map @@ -73,20 +73,20 @@ Number of file 0 mappings: 4 Highest counter ID seen: c0 Function name: issue_84561::test3 -Raw bytes (409): 0x[01, 01, 0a, 0d, 11, 0d, 15, 0d, 19, 1d, 21, 29, 2d, 25, 29, 25, 29, 25, 29, 27, 31, 29, 2d, 4d, 01, 08, 01, 00, 0b, 01, 01, 09, 00, 10, 01, 00, 13, 00, 2e, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 05, 01, 09, 00, 0c, 05, 00, 0f, 00, 15, 05, 01, 05, 00, 0f, 09, 01, 05, 00, 0d, 09, 00, 0e, 00, 14, 09, 01, 05, 00, 0d, 09, 00, 0e, 00, 14, 09, 01, 05, 00, 0d, 09, 00, 0e, 00, 14, 09, 02, 05, 00, 0f, 09, 01, 05, 00, 0f, 09, 01, 05, 00, 0f, 09, 01, 09, 00, 0c, 09, 00, 0f, 00, 15, 09, 01, 05, 00, 0f, 0d, 01, 05, 00, 0f, 0d, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 0d, 01, 05, 00, 0d, 0d, 00, 0e, 00, 14, 0d, 01, 05, 00, 0d, 0d, 00, 0e, 00, 14, 0d, 02, 05, 00, 0f, 00, 00, 20, 00, 24, 00, 00, 29, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 0d, 01, 05, 00, 0f, 00, 05, 09, 00, 0d, 00, 03, 09, 00, 10, 00, 02, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 0d, 04, 09, 00, 10, 0d, 00, 13, 00, 2e, 0d, 02, 05, 00, 0f, 0d, 04, 05, 00, 0f, 0d, 04, 05, 00, 0f, 0d, 04, 09, 00, 0c, 0d, 00, 0f, 00, 15, 0d, 01, 05, 00, 0f, 0d, 04, 08, 00, 0f, 11, 01, 09, 00, 13, 02, 05, 09, 00, 13, 0d, 05, 08, 00, 0f, 15, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 06, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 0d, 03, 05, 00, 0f, 0d, 01, 0c, 00, 13, 19, 01, 0d, 00, 13, 0a, 02, 0d, 00, 13, 1d, 04, 05, 00, 0f, 1d, 02, 0c, 00, 13, 21, 01, 0d, 00, 13, 0e, 02, 0d, 00, 13, 27, 03, 05, 00, 0f, 25, 01, 0c, 00, 13, 29, 01, 0d, 00, 17, 29, 04, 0d, 00, 13, 1e, 02, 0d, 00, 17, 1e, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, 1e, 02, 15, 00, 1b, 2d, 04, 0d, 00, 13, 22, 03, 09, 00, 19, 31, 02, 05, 00, 0f, 31, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02] +Raw bytes (409): 0x[01, 01, 0a, 01, 05, 01, 09, 01, 0d, 11, 15, 1d, 21, 19, 1d, 19, 1d, 19, 1d, 27, 25, 1d, 21, 4d, 01, 08, 01, 00, 0b, 01, 01, 09, 00, 10, 01, 00, 13, 00, 2e, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 02, 05, 00, 0f, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0f, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 02, 05, 00, 0f, 00, 00, 20, 00, 24, 00, 00, 29, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 01, 01, 05, 00, 0f, 00, 05, 09, 00, 0d, 00, 03, 09, 00, 10, 00, 02, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 01, 04, 09, 00, 10, 01, 00, 13, 00, 2e, 01, 02, 05, 00, 0f, 01, 04, 05, 00, 0f, 01, 04, 05, 00, 0f, 01, 04, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 04, 08, 00, 0f, 05, 01, 09, 00, 13, 02, 05, 09, 00, 13, 01, 05, 08, 00, 0f, 09, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 06, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 01, 03, 05, 00, 0f, 01, 01, 0c, 00, 13, 0d, 01, 0d, 00, 13, 0a, 02, 0d, 00, 13, 11, 04, 05, 00, 0f, 11, 02, 0c, 00, 13, 15, 01, 0d, 00, 13, 0e, 02, 0d, 00, 13, 27, 03, 05, 00, 0f, 19, 01, 0c, 00, 13, 1d, 01, 0d, 00, 17, 1d, 04, 0d, 00, 13, 1e, 02, 0d, 00, 17, 1e, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, 1e, 02, 15, 00, 1b, 21, 04, 0d, 00, 13, 22, 03, 09, 00, 19, 25, 02, 05, 00, 0f, 25, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02] Number of files: 1 - file 0 => $DIR/issue-84561.rs Number of expressions: 10 -- expression 0 operands: lhs = Counter(3), rhs = Counter(4) -- expression 1 operands: lhs = Counter(3), rhs = Counter(5) -- expression 2 operands: lhs = Counter(3), rhs = Counter(6) -- expression 3 operands: lhs = Counter(7), rhs = Counter(8) -- expression 4 operands: lhs = Counter(10), rhs = Counter(11) -- expression 5 operands: lhs = Counter(9), rhs = Counter(10) -- expression 6 operands: lhs = Counter(9), rhs = Counter(10) -- expression 7 operands: lhs = Counter(9), rhs = Counter(10) -- expression 8 operands: lhs = Expression(9, Add), rhs = Counter(12) -- expression 9 operands: lhs = Counter(10), rhs = Counter(11) +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(3) +- expression 3 operands: lhs = Counter(4), rhs = Counter(5) +- expression 4 operands: lhs = Counter(7), rhs = Counter(8) +- expression 5 operands: lhs = Counter(6), rhs = Counter(7) +- expression 6 operands: lhs = Counter(6), rhs = Counter(7) +- expression 7 operands: lhs = Counter(6), rhs = Counter(7) +- expression 8 operands: lhs = Expression(9, Add), rhs = Counter(9) +- expression 9 operands: lhs = Counter(7), rhs = Counter(8) Number of file 0 mappings: 77 - Code(Counter(0)) at (prev + 8, 1) to (start + 0, 11) - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 16) @@ -94,85 +94,85 @@ Number of file 0 mappings: 77 - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12) - Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) -- Code(Counter(1)) at (prev + 0, 15) to (start + 0, 21) -- Code(Counter(1)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 13) -- Code(Counter(2)) at (prev + 0, 14) to (start + 0, 20) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 13) -- Code(Counter(2)) at (prev + 0, 14) to (start + 0, 20) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 13) -- Code(Counter(2)) at (prev + 0, 14) to (start + 0, 20) -- Code(Counter(2)) at (prev + 2, 5) to (start + 0, 15) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(2)) at (prev + 1, 9) to (start + 0, 12) -- Code(Counter(2)) at (prev + 0, 15) to (start + 0, 21) -- Code(Counter(2)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) - Code(Zero) at (prev + 0, 32) to (start + 0, 48) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 13) -- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 20) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 13) -- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 20) -- Code(Counter(3)) at (prev + 2, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 15) - Code(Zero) at (prev + 0, 32) to (start + 0, 36) - Code(Zero) at (prev + 0, 41) to (start + 0, 48) - Code(Zero) at (prev + 0, 51) to (start + 0, 65) - Code(Zero) at (prev + 0, 75) to (start + 0, 90) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) - Code(Zero) at (prev + 5, 9) to (start + 0, 13) - Code(Zero) at (prev + 3, 9) to (start + 0, 16) - Code(Zero) at (prev + 2, 13) to (start + 0, 27) - Code(Zero) at (prev + 2, 13) to (start + 0, 28) -- Code(Counter(3)) at (prev + 4, 9) to (start + 0, 16) -- Code(Counter(3)) at (prev + 0, 19) to (start + 0, 46) -- Code(Counter(3)) at (prev + 2, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 4, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 4, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 4, 9) to (start + 0, 12) -- Code(Counter(3)) at (prev + 0, 15) to (start + 0, 21) -- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 4, 8) to (start + 0, 15) -- Code(Counter(4)) at (prev + 1, 9) to (start + 0, 19) +- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 19) to (start + 0, 46) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 12) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 4, 8) to (start + 0, 15) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 19) - Code(Expression(0, Sub)) at (prev + 5, 9) to (start + 0, 19) - = (c3 - c4) -- Code(Counter(3)) at (prev + 5, 8) to (start + 0, 15) -- Code(Counter(5)) at (prev + 1, 9) to (start + 0, 19) + = (c0 - c1) +- Code(Counter(0)) at (prev + 5, 8) to (start + 0, 15) +- Code(Counter(2)) at (prev + 1, 9) to (start + 0, 19) - Code(Zero) at (prev + 3, 13) to (start + 0, 29) - Code(Expression(1, Sub)) at (prev + 3, 9) to (start + 0, 19) - = (c3 - c5) + = (c0 - c2) - Code(Zero) at (prev + 3, 13) to (start + 0, 29) -- Code(Counter(3)) at (prev + 3, 5) to (start + 0, 15) -- Code(Counter(3)) at (prev + 1, 12) to (start + 0, 19) -- Code(Counter(6)) at (prev + 1, 13) to (start + 0, 19) +- Code(Counter(0)) at (prev + 3, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 1, 12) to (start + 0, 19) +- Code(Counter(3)) at (prev + 1, 13) to (start + 0, 19) - Code(Expression(2, Sub)) at (prev + 2, 13) to (start + 0, 19) - = (c3 - c6) -- Code(Counter(7)) at (prev + 4, 5) to (start + 0, 15) -- Code(Counter(7)) at (prev + 2, 12) to (start + 0, 19) -- Code(Counter(8)) at (prev + 1, 13) to (start + 0, 19) + = (c0 - c3) +- Code(Counter(4)) at (prev + 4, 5) to (start + 0, 15) +- Code(Counter(4)) at (prev + 2, 12) to (start + 0, 19) +- Code(Counter(5)) at (prev + 1, 13) to (start + 0, 19) - Code(Expression(3, Sub)) at (prev + 2, 13) to (start + 0, 19) - = (c7 - c8) + = (c4 - c5) - Code(Expression(9, Add)) at (prev + 3, 5) to (start + 0, 15) - = (c10 + c11) -- Code(Counter(9)) at (prev + 1, 12) to (start + 0, 19) -- Code(Counter(10)) at (prev + 1, 13) to (start + 0, 23) -- Code(Counter(10)) at (prev + 4, 13) to (start + 0, 19) + = (c7 + c8) +- Code(Counter(6)) at (prev + 1, 12) to (start + 0, 19) +- Code(Counter(7)) at (prev + 1, 13) to (start + 0, 23) +- Code(Counter(7)) at (prev + 4, 13) to (start + 0, 19) - Code(Expression(7, Sub)) at (prev + 2, 13) to (start + 0, 23) - = (c9 - c10) + = (c6 - c7) - Code(Expression(7, Sub)) at (prev + 1, 20) to (start + 0, 27) - = (c9 - c10) + = (c6 - c7) - Code(Zero) at (prev + 1, 21) to (start + 0, 27) - Code(Expression(7, Sub)) at (prev + 2, 21) to (start + 0, 27) - = (c9 - c10) -- Code(Counter(11)) at (prev + 4, 13) to (start + 0, 19) + = (c6 - c7) +- Code(Counter(8)) at (prev + 4, 13) to (start + 0, 19) - Code(Expression(8, Sub)) at (prev + 3, 9) to (start + 0, 25) - = ((c10 + c11) - c12) -- Code(Counter(12)) at (prev + 2, 5) to (start + 0, 15) -- Code(Counter(12)) at (prev + 3, 9) to (start + 0, 34) + = ((c7 + c8) - c9) +- Code(Counter(9)) at (prev + 2, 5) to (start + 0, 15) +- Code(Counter(9)) at (prev + 3, 9) to (start + 0, 34) - Code(Zero) at (prev + 2, 5) to (start + 0, 15) - Code(Zero) at (prev + 3, 9) to (start + 0, 44) - Code(Zero) at (prev + 2, 1) to (start + 0, 2) -Highest counter ID seen: c12 +Highest counter ID seen: c9 diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr index 3de317d2af6d1..a78941f9e11be 100644 --- a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr @@ -1,10 +1,10 @@ error[E0282]: type annotations needed - --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:22 + --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:5 | LL | println!("{:?}", []); - | ^^ cannot infer type + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-27592.stderr b/tests/ui/issues/issue-27592.stderr index c8649d82d7451..f1de7b9e569da 100644 --- a/tests/ui/issues/issue-27592.stderr +++ b/tests/ui/issues/issue-27592.stderr @@ -1,3 +1,9 @@ +error[E0515]: cannot return reference to temporary value + --> $DIR/issue-27592.rs:16:14 + | +LL | write(|| format_args!("{}", String::from("Hello world"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a reference to data owned by the current function + error[E0515]: cannot return value referencing temporary value --> $DIR/issue-27592.rs:16:14 | @@ -7,12 +13,6 @@ LL | write(|| format_args!("{}", String::from("Hello world"))); | | temporary value created here | returns a value referencing data owned by the current function -error[E0515]: cannot return reference to temporary value - --> $DIR/issue-27592.rs:16:14 - | -LL | write(|| format_args!("{}", String::from("Hello world"))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a reference to data owned by the current function - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr index 1084ea7c9e0e7..c3cf7e13987b6 100644 --- a/tests/ui/suggestions/issue-97760.stderr +++ b/tests/ui/suggestions/issue-97760.stderr @@ -1,11 +1,8 @@ error[E0277]: `::Item` doesn't implement `std::fmt::Display` - --> $DIR/issue-97760.rs:4:20 + --> $DIR/issue-97760.rs:4:19 | LL | println!("{x}"); - | -^- - | || - | |`::Item` cannot be formatted with the default formatter - | required by this formatting parameter + | ^^^ `::Item` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `::Item` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 924fb98ae18a8..48dbb03b82a63 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -398,7 +398,8 @@ mod expressions { let expr; format_arguments::new_const(&[]); { - super let args = [format_argument::new_display(&expr)]; + super let args = (&expr,); + super let args = [format_argument::new_display(args.0)]; format_arguments::new_v1(&[""], &args) }; } diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout index 0792dc10e946b..233c9f1c91bd7 100644 --- a/tests/ui/unpretty/flattened-format-args.stdout +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -11,7 +11,8 @@ fn main() { // Should flatten to println!("a 123 b {x} xyz\n"): { ::std::io::_print({ - super let args = [format_argument::new_display(&x)]; + super let args = (&x,); + super let args = [format_argument::new_display(args.0)]; format_arguments::new_v1(&["a 123 b ", " xyz\n"], &args) }); }; From 663ca24879a6ded2bd1dda8c6da916444f78e850 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 15:54:59 +0200 Subject: [PATCH 0160/1900] Bless clippy tests. --- .../tests/ui/author/macro_in_closure.stdout | 32 ++++++++++++------- .../tests/ui/author/macro_in_loop.stdout | 28 ++++++++++------ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index 49595e2fec257..786c61e0c018e 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -10,34 +10,42 @@ if let StmtKind::Let(local) = stmt.kind && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block1, None) = args[0].kind - && block1.stmts.len() == 1 + && block1.stmts.len() == 2 && let StmtKind::Let(local1) = block1.stmts[0].kind && let Some(init1) = local1.init - && let ExprKind::Array(elements) = init1.kind + && let ExprKind::Tup(elements) = init1.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "args" + && let StmtKind::Let(local2) = block1.stmts[1].kind + && let Some(init2) = local2.init + && let ExprKind::Array(elements1) = init2.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local2.pat.kind + && name1.as_str() == "args" && let Some(trailing_expr) = block1.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit) = elements2[0].kind && let LitKind::Str(s, _) = lit.node && s.as_str() == "" - && let ExprKind::Lit(ref lit1) = elements1[1].kind + && let ExprKind::Lit(ref lit1) = elements2[1].kind && let LitKind::Str(s1, _) = lit1.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind && block.expr.is_none() - && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind - && name1.as_str() == "print_text" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local.pat.kind + && name2.as_str() == "print_text" { // report your lint here } diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index 4fc7b49464db3..80717900b5255 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -20,28 +20,36 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block2, None) = args[0].kind - && block2.stmts.len() == 1 + && block2.stmts.len() == 2 && let StmtKind::Let(local) = block2.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Array(elements) = init.kind + && let ExprKind::Tup(elements) = init.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "args" + && let StmtKind::Let(local1) = block2.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Array(elements1) = init1.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local1.pat.kind + && name2.as_str() == "args" && let Some(trailing_expr) = block2.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit2) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit2) = elements2[0].kind && let LitKind::Str(s, _) = lit2.node && s.as_str() == "" - && let ExprKind::Lit(ref lit3) = elements1[1].kind + && let ExprKind::Lit(ref lit3) = elements2[1].kind && let LitKind::Str(s1, _) = lit3.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind From d43c55be3487277783f9a5445ceeaa01fa558b11 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 15:54:59 +0200 Subject: [PATCH 0161/1900] Bless clippy tests. --- tests/ui/author/macro_in_closure.stdout | 32 +++++++++++++++---------- tests/ui/author/macro_in_loop.stdout | 28 ++++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout index 49595e2fec257..786c61e0c018e 100644 --- a/tests/ui/author/macro_in_closure.stdout +++ b/tests/ui/author/macro_in_closure.stdout @@ -10,34 +10,42 @@ if let StmtKind::Let(local) = stmt.kind && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block1, None) = args[0].kind - && block1.stmts.len() == 1 + && block1.stmts.len() == 2 && let StmtKind::Let(local1) = block1.stmts[0].kind && let Some(init1) = local1.init - && let ExprKind::Array(elements) = init1.kind + && let ExprKind::Tup(elements) = init1.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "args" + && let StmtKind::Let(local2) = block1.stmts[1].kind + && let Some(init2) = local2.init + && let ExprKind::Array(elements1) = init2.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local2.pat.kind + && name1.as_str() == "args" && let Some(trailing_expr) = block1.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit) = elements2[0].kind && let LitKind::Str(s, _) = lit.node && s.as_str() == "" - && let ExprKind::Lit(ref lit1) = elements1[1].kind + && let ExprKind::Lit(ref lit1) = elements2[1].kind && let LitKind::Str(s1, _) = lit1.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind && block.expr.is_none() - && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind - && name1.as_str() == "print_text" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local.pat.kind + && name2.as_str() == "print_text" { // report your lint here } diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout index 4fc7b49464db3..80717900b5255 100644 --- a/tests/ui/author/macro_in_loop.stdout +++ b/tests/ui/author/macro_in_loop.stdout @@ -20,28 +20,36 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block2, None) = args[0].kind - && block2.stmts.len() == 1 + && block2.stmts.len() == 2 && let StmtKind::Let(local) = block2.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Array(elements) = init.kind + && let ExprKind::Tup(elements) = init.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "args" + && let StmtKind::Let(local1) = block2.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Array(elements1) = init1.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local1.pat.kind + && name2.as_str() == "args" && let Some(trailing_expr) = block2.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit2) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit2) = elements2[0].kind && let LitKind::Str(s, _) = lit2.node && s.as_str() == "" - && let ExprKind::Lit(ref lit3) = elements1[1].kind + && let ExprKind::Lit(ref lit3) = elements2[1].kind && let LitKind::Str(s1, _) = lit3.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind From cee25947df4f0d3cb455810b64cd62c16d775109 Mon Sep 17 00:00:00 2001 From: sgasho Date: Wed, 27 Aug 2025 00:06:47 +0900 Subject: [PATCH 0162/1900] fix: Prevent invalid transformation in 'Replace match with if let' assist --- .../src/handlers/replace_if_let_with_match.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index dd244375dc91e..3b815a467bc06 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -328,7 +328,14 @@ fn pick_pattern_and_expr_order( (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) { (true, true) => return None, (true, false) => (pat, guard, expr, expr2), - (false, true) => (pat2, guard2, expr2, expr), + (false, true) => { + // This pattern triggers an invalid transformation. + // See issues #11373, #19443 + if let ast::Pat::IdentPat(_) = pat2 { + return None; + } + (pat2, guard2, expr2, expr) + } _ if is_sad_pat(sema, &pat) => (pat2, guard2, expr2, expr), (false, false) => (pat, guard, expr, expr2), }, @@ -1892,4 +1899,19 @@ fn main() { "#, ) } + + #[test] + fn test_replace_match_with_if_let_not_applicable_pat2_is_ident_pat() { + check_assist_not_applicable( + replace_match_with_if_let, + r" +fn test(a: i32) { + match$0 a { + 1 => code(), + other => code(other), + } +} +", + ) + } } From 83f22cc0f87263c0aa3057d55b994d75c6151834 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 26 Aug 2025 19:33:46 +0300 Subject: [PATCH 0163/1900] Remove `SolverDefId::ForeignId` Replace it with normal `SolverDefId::TypeAliasId`. The split caused a very funny bug where code was getting `TypeAliasId` where it expected `ForeignId`, because `TypeAliasId` had a `From` impl from `hir_def::TypeAliasId` and `ForeignId` had not, plus a careless `into()`. I could've fixed this specific bug but opted to remove the split instead; currently, it just provides more room for bugs, as we don't have typed IDs for the solver anyway, and even when we'll have (hopefully), that doesn't seem like a very useful distinction, for example in hir-def foreign types are just `TypeAliasId` with some flags. Constructing a test for this isn't trivial; the trivial test (creating a foreign type, even proving a trait bound for it) fails to fail before the change, probably because we don't use the new solver everywhere yet so we don't trigger this specific code path. --- src/tools/rust-analyzer/crates/hir-ty/src/display.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/method_resolution.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs | 2 -- .../rust-analyzer/crates/hir-ty/src/next_solver/interner.rs | 1 - .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 4 ++-- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ae0113fcbd7f0..0df727a8e5c04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1473,7 +1473,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } TyKind::Foreign(type_alias) => { let alias = match type_alias { - SolverDefId::ForeignId(id) => id, + SolverDefId::TypeAliasId(id) => id, _ => unreachable!(), }; let type_alias = db.type_alias_signature(alias); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 49438151bbff5..8bd71df7c191d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -155,7 +155,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, TyKind::Foreign(def) => { - let SolverDefId::ForeignId(def) = def else { unreachable!() }; + let SolverDefId::TypeAliasId(def) = def else { unreachable!() }; TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) } TyKind::Dynamic(bounds, _, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 64eab2d6b8185..c9632ddcd4bb5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -26,7 +26,6 @@ pub enum SolverDefId { StaticId(StaticId), TraitId(TraitId), TypeAliasId(TypeAliasId), - ForeignId(TypeAliasId), InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), InternedOpaqueTyId(InternedOpaqueTyId), @@ -73,7 +72,6 @@ impl TryFrom for GenericDefId { SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), - SolverDefId::ForeignId(_) => return Err(value), SolverDefId::InternedClosureId(_) => return Err(value), SolverDefId::InternedCoroutineId(_) => return Err(value), SolverDefId::InternedOpaqueTyId(_) => return Err(value), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 4fe0b54d68462..11c79ff742d6c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1148,7 +1148,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { let container = match def_id { SolverDefId::FunctionId(it) => it.lookup(self.db()).container, SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, - SolverDefId::ForeignId(it) => it.lookup(self.db()).container, SolverDefId::ConstId(it) => it.lookup(self.db()).container, SolverDefId::InternedClosureId(it) => { return self diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 8f6296b145409..de2671e28fb5e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -271,7 +271,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ) } chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( - SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)), + SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), chalk_ir::TyKind::Placeholder(placeholder_index) => { @@ -1262,7 +1262,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Foreign(foreign) => { let def_id = match foreign { - SolverDefId::ForeignId(id) => id, + SolverDefId::TypeAliasId(id) => id, _ => unreachable!(), }; TyKind::Foreign(to_foreign_def_id(def_id)) From ff7081e6e25be74671d2fe775fe25a1015e4b32a Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Tue, 26 Aug 2025 17:10:08 +0000 Subject: [PATCH 0164/1900] Update library/proc_macro/src/lib.rs Co-authored-by: David Tolnay --- library/proc_macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 8178af15b7393..97dc482689781 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -383,7 +383,7 @@ macro_rules! extend_items { #[stable(feature = "token_stream_extend_tt_items", since = "CURRENT_RUSTC_VERSION")] impl Extend<$item> for TokenStream { fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|i| TokenTree::$item(i))); + self.extend(iter.into_iter().map(TokenTree::$item)); } } )* From 28a248f4f3a2252942b46bcd611cf4c207f10926 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 26 Aug 2025 21:46:04 +0300 Subject: [PATCH 0165/1900] In highlight_related, when on an unsafe block, don't highlight unsafe operations of other unsafe blocks --- .../hir-ty/src/diagnostics/unsafe_check.rs | 6 ++--- .../rust-analyzer/crates/hir/src/semantics.rs | 23 +++++++++++++++++-- .../crates/hir/src/source_analyzer.rs | 2 +- .../crates/ide/src/highlight_related.rs | 23 +++++++++++++++---- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 827585e50693a..d6d669258cc1f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -119,11 +119,11 @@ pub fn unsafe_operations( def: DefWithBodyId, body: &Body, current: ExprId, - callback: &mut dyn FnMut(InsideUnsafeBlock), + callback: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock), ) { let mut visitor_callback = |diag| { - if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag { - callback(inside_unsafe_block); + if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, node, .. } = diag { + callback(node, inside_unsafe_block); } }; let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback); diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6d6e08100b044..3acbf81ada089 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -28,13 +28,13 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::AsName, }; -use hir_ty::diagnostics::unsafe_operations_for_body; +use hir_ty::diagnostics::{unsafe_operations, unsafe_operations_for_body}; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{SmallVec, smallvec}; use span::{Edition, FileId, SyntaxContext}; -use stdx::TupleExt; +use stdx::{TupleExt, always}; use syntax::{ AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, @@ -1765,6 +1765,25 @@ impl<'db> SemanticsImpl<'db> { res } + pub fn get_unsafe_ops_for_unsafe_block(&self, block: ast::BlockExpr) -> Vec { + always!(block.unsafe_token().is_some()); + let block = self.wrap_node_infile(ast::Expr::from(block)); + let Some(def) = self.body_for(block.syntax()) else { return Vec::new() }; + let def = def.into(); + let (body, source_map) = self.db.body_with_source_map(def); + let infer = self.db.infer(def); + let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else { + return Vec::new(); + }; + let mut res = Vec::default(); + unsafe_operations(self.db, &infer, def, &body, block, &mut |node, _| { + if let Ok(node) = source_map.expr_or_pat_syntax(node) { + res.push(node); + } + }); + res + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let Some(mac) = self.resolve_macro_call(macro_call) else { return false }; if mac.is_asm_like(self.db) { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index e3070c5f74ce2..dbc2539a9b91c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1283,7 +1283,7 @@ impl<'db> SourceAnalyzer<'db> { { let mut is_unsafe = false; let mut walk_expr = |expr_id| { - unsafe_operations(db, infer, def, body, expr_id, &mut |inside_unsafe_block| { + unsafe_operations(db, infer, def, body, expr_id, &mut |_, inside_unsafe_block| { is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No }) }; diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 9960e79a5380f..af1557f5a9a16 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -805,10 +805,8 @@ pub(crate) fn highlight_unsafe_points( push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); // highlight unsafe operations - if let Some(block) = block_expr - && let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) - { - let unsafe_ops = sema.get_unsafe_ops(body); + if let Some(block) = block_expr { + let unsafe_ops = sema.get_unsafe_ops_for_unsafe_block(block); for unsafe_op in unsafe_ops { push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range())); } @@ -2535,4 +2533,21 @@ fn foo() { "#, ); } + + #[test] + fn different_unsafe_block() { + check( + r#" +fn main() { + unsafe$0 { + // ^^^^^^ + *(0 as *const u8) + // ^^^^^^^^^^^^^^^^^ + }; + unsafe { *(1 as *const u8) }; + unsafe { *(2 as *const u8) }; +} + "#, + ); + } } From ee9986b4a28313429cc50f91ffd30d7491bd59af Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 27 Aug 2025 12:06:52 -0700 Subject: [PATCH 0166/1900] document lifetime extension for `format_args!`'s arguments --- library/core/src/macros/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index ccf41dfb01dd1..c19ce4f90660b 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -951,8 +951,9 @@ pub(crate) mod builtin { /// format string in `format_args!`. /// /// ```rust - /// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); - /// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); + /// let args = format_args!("{} foo {:?}", 1, 2); + /// let debug = format!("{args:?}"); + /// let display = format!("{args}"); /// assert_eq!("1 foo 2", display); /// assert_eq!(display, debug); /// ``` @@ -976,13 +977,17 @@ pub(crate) mod builtin { /// assert_eq!(s, format!("hello {}", "world")); /// ``` /// - /// # Lifetime limitation + /// # Argument lifetimes /// /// Except when no formatting arguments are used, - /// the produced `fmt::Arguments` value borrows temporary values, - /// which means it can only be used within the same expression - /// and cannot be stored for later use. - /// This is a known limitation, see [#92698](https://github.com/rust-lang/rust/issues/92698). + /// the produced `fmt::Arguments` value borrows temporary values. + /// To allow it to be stored for later use, the arguments' lifetimes, as well as those of + /// temporaries they borrow, may be [extended] when `format_args!` appears in the initializer + /// expression of a `let` statement. The syntactic rules used to determine when temporaries' + /// lifetimes are extended are documented in the [Reference]. + /// + /// [extended]: ../reference/destructors.html#temporary-lifetime-extension + /// [Reference]: ../reference/destructors.html#extending-based-on-expressions #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "format_args_macro"] #[allow_internal_unsafe] From f31a378b0a5a612e0d2a91ab1a9ad237d442888c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 01:25:09 +0300 Subject: [PATCH 0167/1900] Attach the db in one more place in highlighting --- .../rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 7785891169c64..abe7be8c68881 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -26,7 +26,8 @@ pub(super) fn ra_fixture( literal: &ast::String, expanded: &ast::String, ) -> Option<()> { - let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?; + let active_parameter = + salsa::attach(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?; let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| { attrs.filter_map(|attr| attr.as_simple_path()).any(|path| { path.segments() From 0a5502208d7c7625f3a4806c422dfed47cc364ba Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 18:54:46 +0300 Subject: [PATCH 0168/1900] Add progress bars to more places in analysis-stats Namely, mir lowering, const eval and IDE things. --- .../rust-analyzer/src/cli/analysis_stats.rs | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index b17186f8d7b1e..d685f160ff098 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -333,7 +333,7 @@ impl flags::AnalysisStats { } if self.run_all_ide_things { - self.run_ide_things(host.analysis(), file_ids.clone()); + self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); } if self.run_term_search { @@ -393,15 +393,27 @@ impl flags::AnalysisStats { } fn run_const_eval(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { + let len = bodies + .iter() + .filter(|body| matches!(body, DefWithBody::Const(_) | DefWithBody::Static(_))) + .count(); + let mut bar = match verbosity { + Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), + _ if self.parallel || self.output.is_some() => ProgressReport::hidden(), + _ => ProgressReport::new(len), + }; + let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; for &b in bodies { + bar.set_message(move || format!("const eval: {}", full_name(db, b, b.module(db)))); let res = match b { DefWithBody::Const(c) => c.eval(db), DefWithBody::Static(s) => s.eval(db), _ => continue, }; + bar.inc(1); all += 1; let Err(error) = res else { continue; @@ -409,10 +421,11 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { let full_name = full_name_of_item(db, b.module(db), b.name(db).unwrap_or(Name::missing())); - println!("Const eval for {full_name} failed due {error:?}"); + bar.println(format!("Const eval for {full_name} failed due {error:?}")); } fail += 1; } + bar.finish_and_clear(); let const_eval_time = sw.elapsed(); eprintln!("{:<20} {}", "Const evaluation:", const_eval_time); eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all)); @@ -662,6 +675,10 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &body_id in bodies { + bar.set_message(move || { + format!("mir lowering: {}", full_name(db, body_id, body_id.module(db))) + }); + bar.inc(1); if matches!(body_id, DefWithBody::Variant(_)) { continue; } @@ -1089,12 +1106,29 @@ impl flags::AnalysisStats { report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms"); } - fn run_ide_things(&self, analysis: Analysis, mut file_ids: Vec) { + fn run_ide_things( + &self, + analysis: Analysis, + mut file_ids: Vec, + db: &RootDatabase, + vfs: &Vfs, + verbosity: Verbosity, + ) { + let len = file_ids.len(); + let create_bar = || match verbosity { + Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), + _ if self.parallel || self.output.is_some() => ProgressReport::hidden(), + _ => ProgressReport::new(len), + }; + file_ids.sort(); file_ids.dedup(); let mut sw = self.stop_watch(); + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("diagnostics: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); _ = analysis.full_diagnostics( &DiagnosticsConfig { enabled: true, @@ -1121,8 +1155,14 @@ impl flags::AnalysisStats { ide::AssistResolveStrategy::All, analysis.editioned_file_id_to_vfs(file_id), ); + bar.inc(1); } + bar.finish_and_clear(); + + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("inlay hints: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); _ = analysis.inlay_hints( &InlayHintsConfig { render_colons: false, @@ -1158,8 +1198,14 @@ impl flags::AnalysisStats { analysis.editioned_file_id_to_vfs(file_id), None, ); + bar.inc(1); } + bar.finish_and_clear(); + + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); analysis .annotations( &AnnotationConfig { @@ -1178,7 +1224,10 @@ impl flags::AnalysisStats { .for_each(|annotation| { _ = analysis.resolve_annotation(annotation); }); + bar.inc(1); } + bar.finish_and_clear(); + let ide_time = sw.elapsed(); eprintln!("{:<20} {} ({} files)", "IDE:", ide_time, file_ids.len()); } From c36b75bdc8634f0d9cba824410df7a8c3785bda4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 19:24:46 +0300 Subject: [PATCH 0169/1900] Don't require a full `InferenceTable` for `CastTy` A DB is enough. --- .../crates/hir-ty/src/infer/cast.rs | 100 ++++++++---------- .../crates/hir-ty/src/mir/lower.rs | 11 +- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 09b983a580d97..43364963eb054 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -6,7 +6,9 @@ use stdx::never; use crate::{ Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, from_chalk_trait_id, + QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + from_chalk_trait_id, infer::{coerce::CoerceNever, unify::InferenceTable}, }; @@ -30,7 +32,7 @@ pub(crate) enum CastTy { } impl CastTy { - pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option { + pub(crate) fn from_ty(db: &dyn HirDatabase, t: &Ty) -> Option { match t.kind(Interner) { TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), @@ -43,8 +45,8 @@ impl CastTy { let (AdtId::EnumId(id), _) = t.as_adt()? else { return None; }; - let enum_data = id.enum_variants(table.db); - if enum_data.is_payload_free(table.db) { Some(Self::Int(Int::CEnum)) } else { None } + let enum_data = id.enum_variants(db); + if enum_data.is_payload_free(db) { Some(Self::Int(Int::CEnum)) } else { None } } TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)), TyKind::Function(_) => Some(Self::FnPtr), @@ -142,58 +144,50 @@ impl CastCheck { where F: FnMut(ExprId, Vec), { - let (t_from, t_cast) = - match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { - (Some(t_from), Some(t_cast)) => (t_from, t_cast), - (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { - TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); - let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); - let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) - { - apply_adjustments(self.source_expr, adj); - } else { - return Err(CastError::IllegalCast); - } - - (CastTy::FnPtr, t_cast) + let (t_from, t_cast) = match ( + CastTy::from_ty(table.db, &self.expr_ty), + CastTy::from_ty(table.db, &self.cast_ty), + ) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { + TyKind::FnDef(..) => { + let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); + let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); + let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) { + apply_adjustments(self.source_expr, adj); + } else { + return Err(CastError::IllegalCast); } - TyKind::Ref(mutbl, _, inner_ty) => { - return match t_cast { - CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { - TyKind::Scalar( - Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_), - ) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float, - ) => Err(CastError::NeedDeref), - - _ => Err(CastError::NeedViaPtr), - }, - // array-ptr-cast - CastTy::Ptr(t, m) => { - let t = table.eagerly_normalize_and_resolve_shallow_in(t); - if !table.is_sized(&t) { - return Err(CastError::IllegalCast); - } - self.check_ref_cast( - table, - inner_ty, - *mutbl, - &t, - m, - apply_adjustments, - ) + + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(mutbl, _, inner_ty) => { + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { + TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) + | TyKind::InferenceVar( + _, + TyVariableKind::Integer | TyVariableKind::Float, + ) => Err(CastError::NeedDeref), + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = table.eagerly_normalize_and_resolve_shallow_in(t); + if !table.is_sized(&t) { + return Err(CastError::IllegalCast); } - _ => Err(CastError::NonScalar), - }; - } - _ => return Err(CastError::NonScalar), - }, + self.check_ref_cast(table, inner_ty, *mutbl, &t, m, apply_adjustments) + } + _ => Err(CastError::NonScalar), + }; + } _ => return Err(CastError::NonScalar), - }; + }, + _ => return Err(CastError::NonScalar), + }; // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 8c03ca939e370..2d53d16c57a30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -31,7 +31,7 @@ use crate::{ display::{DisplayTarget, HirDisplay, hir_display_with_store}, error_lifetime, generics::generics, - infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy, unify::InferenceTable}, + infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, mapping::ToChalk, @@ -948,8 +948,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { - let mut table = InferenceTable::new(self.db, self.env.clone()); - cast_kind(&mut table, &source_ty, &target_ty)? + cast_kind(self.db, &source_ty, &target_ty)? }; Rvalue::Cast(cast_kind, it, target_ty) @@ -2017,9 +2016,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result { - let from = CastTy::from_ty(table, source_ty); - let cast = CastTy::from_ty(table, target_ty); +fn cast_kind(db: &dyn HirDatabase, source_ty: &Ty, target_ty: &Ty) -> Result { + let from = CastTy::from_ty(db, source_ty); + let cast = CastTy::from_ty(db, target_ty); Ok(match (from, cast) { (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { CastKind::PointerExposeAddress From dce4f301042c53f91d85befda03b0405d5922916 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 23:53:55 +0300 Subject: [PATCH 0170/1900] When mapping next-solver's `dyn` type, add `Self` (aka. bound var ^1.0) to auto traits' substitutions Chalk represents dyn types as a list of predicate, the self type should be there. The next solver represents them quite differently. The `Self` was forgotten for the auto trait case. --- .../crates/hir-ty/src/mir/lower.rs | 2 + .../crates/hir-ty/src/mir/lower/tests.rs | 64 +++++++++++++++++++ .../crates/hir-ty/src/next_solver/mapping.rs | 5 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 8c03ca939e370..7fbd496e51f25 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -53,6 +53,8 @@ use super::OperandKind; mod as_place; mod pattern_matching; +#[cfg(test)] +mod tests; #[derive(Debug, Clone)] struct LoopBlocks { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs new file mode 100644 index 0000000000000..1d7a16ed72d07 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -0,0 +1,64 @@ +use hir_def::db::DefDatabase; +use rustc_hash::FxHashMap; +use span::Edition; +use test_fixture::WithFixture; +use triomphe::Arc; + +use crate::{ + db::HirDatabase, + mir::{MirBody, MirLowerError}, + setup_tracing, + test_db::TestDB, +}; + +fn lower_mir( + #[rust_analyzer::rust_fixture] ra_fixture: &str, +) -> FxHashMap, MirLowerError>> { + let _tracing = setup_tracing(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let funcs = scope.declarations().filter_map(|x| match x { + hir_def::ModuleDefId::FunctionId(it) => Some(it), + _ => None, + }); + funcs + .map(|func| { + let name = db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); + let mir = db.mir_body(func.into()); + (name, mir) + }) + .collect() +} + +#[test] +fn dyn_projection_with_auto_traits_regression_next_solver() { + lower_mir( + r#" +//- minicore: sized, send +pub trait Deserializer {} + +pub trait Strictest { + type Object: ?Sized; +} + +impl Strictest for dyn CustomValue { + type Object = dyn CustomValue + Send; +} + +pub trait CustomValue: Send {} + +impl CustomValue for () {} + +struct Box; + +type DeserializeFn = fn(&mut dyn Deserializer) -> Box; + +fn foo() { + (|deserializer| Box::new(())) as DeserializeFn<::Object>; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index de2671e28fb5e..203f030dfd614 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1207,7 +1207,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::TraitId(id) => to_chalk_trait_id(id), _ => unreachable!(), }; - let substitution = chalk_ir::Substitution::empty(Interner); + let substitution = chalk_ir::Substitution::from1( + Interner, + convert_ty_for_result(interner, self_ty), + ); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } From 46727f4cb06c3496a541ee8517a24ef5cb77627c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 20 Aug 2025 15:04:34 +0000 Subject: [PATCH 0171/1900] Stabilize file_as_c_str --- library/core/src/panic/location.rs | 3 ++- tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 7a68d393906cb..5935849344475 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -193,8 +193,9 @@ impl<'a> Location<'a> { /// This is useful for interop with APIs that expect C/C++ `__FILE__` or /// `std::source_location::file_name`, both of which return a nul-terminated `const char*`. #[must_use] - #[unstable(feature = "file_with_nul", issue = "141727")] #[inline] + #[stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] pub const fn file_as_c_str(&self) -> &'a CStr { let filename = self.filename.as_ptr(); diff --git a/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs b/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs index 7902f40b09bdb..698492966adf3 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(file_with_nul)] #[track_caller] const fn assert_file_has_trailing_zero() { From ae648be78350638873248b348f4fca3b9bc916e8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 29 Aug 2025 12:15:41 +0200 Subject: [PATCH 0172/1900] use `llvm.roundeven` on arm --- .../core_arch/src/arm_shared/neon/generated.rs | 12 ++++-------- .../stdarch-gen-arm/spec/neon/arm_shared.spec.yml | 8 ++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index 22d31d4e25682..b5ba792b18aec 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -58256,10 +58256,9 @@ pub fn vrhaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v4f16" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v4f16")] fn _vrndn_f16(a: float16x4_t) -> float16x4_t; } unsafe { _vrndn_f16(a) } @@ -58279,10 +58278,9 @@ pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v8f16" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v8f16")] fn _vrndnq_f16(a: float16x8_t) -> float16x8_t; } unsafe { _vrndnq_f16(a) } @@ -58308,10 +58306,9 @@ pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { pub fn vrndn_f32(a: float32x2_t) -> float32x2_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v2f32" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v2f32")] fn _vrndn_f32(a: float32x2_t) -> float32x2_t; } unsafe { _vrndn_f32(a) } @@ -58337,10 +58334,9 @@ pub fn vrndn_f32(a: float32x2_t) -> float32x2_t { pub fn vrndnq_f32(a: float32x4_t) -> float32x4_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v4f32" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v4f32")] fn _vrndnq_f32(a: float32x4_t) -> float32x4_t; } unsafe { _vrndnq_f32(a) } diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index 1543ab1f866e8..43dd3b9031507 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -2486,9 +2486,7 @@ intrinsics: name: "llvm.frinn.{neon_type}" links: - link: "llvm.roundeven.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.arm.neon.vrintn.{neon_type}" - arch: arm + arch: aarch64,arm64ec,arm - name: "vrndn{neon_type.no}" doc: "Floating-point round to integral, to nearest with ties to even" @@ -2510,9 +2508,7 @@ intrinsics: name: "llvm.frinn.{neon_type}" links: - link: "llvm.roundeven.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.arm.neon.vrintn.{neon_type}" - arch: arm + arch: aarch64,arm64ec,arm - name: "vqadd{neon_type.no}" doc: Saturating add From 00b27f49eb8be37424fb5b21ce5aaac842a1d8b4 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 29 Aug 2025 11:24:34 +0100 Subject: [PATCH 0173/1900] Remove FreeBSD CI It's historically been flaky and is no longer needed now that std_detect has been moved out of this repository. --- library/stdarch/.cirrus.yml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 library/stdarch/.cirrus.yml diff --git a/library/stdarch/.cirrus.yml b/library/stdarch/.cirrus.yml deleted file mode 100644 index a0ecc03b953fd..0000000000000 --- a/library/stdarch/.cirrus.yml +++ /dev/null @@ -1,16 +0,0 @@ -task: - name: x86_64-unknown-freebsd - freebsd_instance: - image_family: freebsd-13-4 - env: - # FIXME(freebsd): FreeBSD has a segfault when `RUST_BACKTRACE` is set - # https://github.com/rust-lang/rust/issues/132185 - RUST_BACKTRACE: "0" - setup_script: - - curl https://sh.rustup.rs -sSf --output rustup.sh - - sh rustup.sh --default-toolchain nightly -y - - . $HOME/.cargo/env - - rustup default nightly - test_script: - - . $HOME/.cargo/env - - cargo build --all From d396d2f79753b7db793a940d8da3ea74d82077d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 20:55:43 +0000 Subject: [PATCH 0174/1900] Bump tracing-subscriber from 0.3.19 to 0.3.20 Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.19 to 0.3.20. - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f72e698f60631..b008aa1c50f88 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2738,9 +2738,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "sharded-slab", "thread_local", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index bb8444ed73422..05ee190480c97 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -163,7 +163,7 @@ temp-dir = "0.1.16" text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" -tracing-subscriber = { version = "0.3.19", default-features = false, features = [ +tracing-subscriber = { version = "0.3.20", default-features = false, features = [ "registry", "fmt", "local-time", From c1fdd6e7a25f0ae7f23184857a69e7db7b822800 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 9 May 2025 21:17:32 +0800 Subject: [PATCH 0175/1900] feat: code actions, generate blanket trait impl --- .../handlers/generate_blanket_trait_impl.rs | 1244 +++++++++++++++++ .../crates/ide-assists/src/lib.rs | 2 + .../crates/ide-assists/src/tests/generated.rs | 40 + 3 files changed, 1286 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs new file mode 100644 index 0000000000000..3100809fc62ff --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -0,0 +1,1244 @@ +use crate::{ + AssistConfig, + assist_context::{AssistContext, Assists}, + utils::add_cfg_attrs_to, +}; +use ide_db::assists::{AssistId, AssistKind, ExprFillDefaultMode}; +use syntax::{ + AstNode, + ast::{ + self, AssocItem, BlockExpr, GenericParam, HasGenericParams, HasName, HasTypeBounds, + HasVisibility as astHasVisibility, + edit_in_place::{AttrsOwnerEdit, Indent}, + make, + }, +}; + +// Assist: generate_blanket_trait_impl +// +// Generate blanket trait implementation. +// +// ``` +// trait $0Foo: ToOwned +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T; +// +// fn print_foo(&self) { +// println!("{}", self.foo()); +// } +// } +// ``` +// -> +// ``` +// trait Foo: ToOwned +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T; +// +// fn print_foo(&self) { +// println!("{}", self.foo()); +// } +// } +// +// $0impl Foo for This +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T { +// todo!() +// } +// } +// ``` +pub(crate) fn generate_blanket_trait_impl( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let name = ctx.find_node_at_offset::()?; + let traitd = ast::Trait::cast(name.syntax().parent()?)?; + + acc.add( + AssistId("generate_blanket_trait_impl", AssistKind::Generate, None), + "Generate blanket trait implementation", + name.syntax().text_range(), + |builder| { + let namety = make::ty_path(make::path_from_text(&name.text())); + let trait_where_clause = traitd.where_clause().map(|it| it.clone_for_update()); + let bounds = traitd.type_bound_list(); + let is_unsafe = traitd.unsafe_token().is_some(); + let thisname = make::name("This"); + let thisty = make::ty_path(make::path_from_text(&thisname.text())); + let indent = traitd.indent_level(); + + let gendecl = make::generic_param_list([GenericParam::TypeParam(make::type_param( + thisname.clone(), + bounds, + ))]); + + let trait_gen_args = + traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); + + if let Some(ref where_clause) = trait_where_clause { + where_clause.reindent_to(0.into()); + } + + let impl_ = make::impl_trait( + is_unsafe, + traitd.generic_param_list(), + trait_gen_args, + Some(gendecl), + None, + false, + namety, + thisty.clone(), + trait_where_clause, + None, + None, + ) + .clone_for_update(); + + if let Some(trait_assoc_list) = traitd.assoc_item_list() { + let assoc_item_list = impl_.get_or_create_assoc_item_list(); + for method in trait_assoc_list.assoc_items() { + let AssocItem::Fn(method) = method else { + continue; + }; + if method.body().is_some() { + continue; + } + let f = todo_fn(&method, ctx.config).clone_for_update(); + f.remove_attrs_and_docs(); + add_cfg_attrs_to(&method, &f); + f.indent(1.into()); + assoc_item_list.add_item(AssocItem::Fn(f)); + } + } + + add_cfg_attrs_to(&traitd, &impl_); + + impl_.reindent_to(indent); + + // FIXME: $0 after the cfg attributes + builder.insert(traitd.syntax().text_range().end(), format!("\n\n{indent}$0{impl_}")); + }, + ); + + Some(()) +} + +fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { + let params = f.param_list().unwrap_or_else(|| make::param_list(None, None)); + make::fn_( + f.visibility(), + f.name().unwrap_or_else(|| make::name("unnamed")), + f.generic_param_list(), + f.where_clause(), + params, + default_block(config), + f.ret_type(), + f.async_token().is_some(), + f.const_token().is_some(), + f.unsafe_token().is_some(), + f.gen_token().is_some(), + ) +} + +fn default_block(config: &AssistConfig) -> BlockExpr { + let expr = match config.expr_fill_default { + ExprFillDefaultMode::Todo => make::ext::expr_todo(), + ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Default => make::ext::expr_todo(), + }; + make::block_expr(None, Some(expr)) +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_gen_blanket_works() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This +where + Self::Owned: Default, +{ + fn foo(&self) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_indent() { + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo: ToOwned + where + Self::Owned: Default, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo: ToOwned + where + Self::Owned: Default, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0impl Foo for This + where + Self::Owned: Default, + { + fn foo(&self) -> T { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0impl Foo for This + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + trait $0Foo: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + trait Foo: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0impl Foo for This + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T { + todo!() + } + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0impl Foo for This { + fn foo(&self) -> T { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + trait $0Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + trait Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0impl Foo for This { + fn foo(&self) -> T { + todo!() + } + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + #[cfg(test)] + trait $0Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + #[cfg(test)] + trait Foo { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + $0#[cfg(test)] + impl Foo for This { + fn foo(&self) -> T { + todo!() + } + } + } +} + "#, + ); + } + + #[test] + fn test_gen_blanket_remove_attribute() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: ToOwned +where + Self::Owned: Default, +{ + #[doc(hidden)] + fn foo(&self) -> T; + + /// foo + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + #[doc(hidden)] + fn foo(&self) -> T; + + /// foo + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This +where + Self::Owned: Default, +{ + fn foo(&self) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_not_gen_type_alias() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: ToOwned +where + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This +where + Self::Owned: Default, +{ + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_no_quick_bound() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: ToOwned, + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo +where + Self: ToOwned, + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This +where + Self: ToOwned, + Self::Owned: Default, +{ + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_no_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This { + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_basic() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This { + fn foo(&self, x: Self::X) -> i32 { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_cfg_attrs() { + check_assist( + generate_blanket_trait_impl, + r#" +#[cfg(test)] +trait $0Foo { + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +#[cfg(test)] +trait Foo { + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0#[cfg(test)] +impl Foo for This { + fn foo(&self, x: i32) -> i32 { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +#[cfg(test)] +trait $0Foo { + /// ... + #[cfg(test)] + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +#[cfg(test)] +trait Foo { + /// ... + #[cfg(test)] + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0#[cfg(test)] +impl Foo for This { + #[cfg(test)] + fn foo(&self, x: i32) -> i32 { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo {} +"#, + r#" +trait Foo {} + +$0impl Foo for This {} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_quick_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Copy {} +"#, + r#" +trait Foo: Copy {} + +$0impl Foo for This {} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo where Self: Copy {} +"#, + r#" +trait Foo where Self: Copy {} + +$0impl Foo for This +where Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_comma() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo where Self: Copy, {} +"#, + r#" +trait Foo where Self: Copy, {} + +$0impl Foo for This +where Self: Copy, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where Self: Copy +{} +"#, + r#" +trait Foo +where Self: Copy +{} + +$0impl Foo for This +where Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline_newline() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy +{} +"#, + r#" +trait Foo +where + Self: Copy +{} + +$0impl Foo for This +where + Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline_newline_comma() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy, +{} +"#, + r#" +trait Foo +where + Self: Copy, +{} + +$0impl Foo for This +where + Self: Copy, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_multiple_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy, + (): Into, +{} +"#, + r#" +trait Foo +where + Self: Copy, + (): Into, +{} + +$0impl Foo for This +where + Self: Copy, + (): Into, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_multiple_bounds_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy + Sync, + (): Into, +{} +"#, + r#" +trait Foo +where + Self: Copy + Sync, + (): Into, +{} + +$0impl Foo for This +where + Self: Copy + Sync, + (): Into, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_generate() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) {} +} +"#, + r#" +trait Foo { + fn foo(&self) {} +} + +$0impl Foo for This {} +"#, + ); + } + + #[test] + fn test_gen_blanket_trait_with_doc() { + check_assist( + generate_blanket_trait_impl, + r#" +/// some docs +trait $0Foo {} +"#, + r#" +/// some docs +trait Foo {} + +$0impl Foo for This {} +"#, + ); + } + + #[test] + fn test_gen_blanket_multiple_method() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self); + fn bar(&self); +} +"#, + r#" +trait Foo { + fn foo(&self); + fn bar(&self); +} + +$0impl Foo for This { + fn foo(&self) { + todo!() + } + + fn bar(&self) { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_generic() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self, value: T) -> T; + fn bar(&self, value: T) -> T { todo!() } +} +"#, + r#" +trait Foo { + fn foo(&self, value: T) -> T; + fn bar(&self, value: T) -> T { todo!() } +} + +$0impl Foo for This { + fn foo(&self, value: T) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetimes() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a> { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a> { + fn foo(&self) -> &'a str; +} + +$0impl<'a, This> Foo<'a> for This { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a: 'static> { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a: 'static> { + fn foo(&self) -> &'a str; +} + +$0impl<'a: 'static, This> Foo<'a> for This { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_quick_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a>: 'a { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a>: 'a { + fn foo(&self) -> &'a str; +} + +$0impl<'a, This: 'a> Foo<'a> for This { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_multiple_lifetimes() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a, 'b> { + fn foo(&self) -> &'a &'b str; +} +"#, + r#" +trait Foo<'a, 'b> { + fn foo(&self) -> &'a &'b str; +} + +$0impl<'a, 'b, This> Foo<'a, 'b> for This { + fn foo(&self) -> &'a &'b str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_bounds_at_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a> +where 'a: 'static, +{ + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a> +where 'a: 'static, +{ + fn foo(&self) -> &'a str; +} + +$0impl<'a, This> Foo<'a> for This +where 'a: 'static, +{ + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_not_on_name() { + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo: $0ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + $0fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + fn $0foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_apply_on_other_impl_block() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +trait Bar {} +impl Bar for i32 {} +"#, + r#" +trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This { + fn foo(&self) -> i32 { + todo!() + } +} + +trait Bar {} +impl Bar for i32 {} +"#, + ); + } + + #[test] + fn test_gen_blanket_apply_on_other_blanket_impl_block() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +trait Bar {} +impl Bar for This {} +"#, + r#" +trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This { + fn foo(&self) -> i32 { + todo!() + } +} + +trait Bar {} +impl Bar for This {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 4682c04732389..be379b396006e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -153,6 +153,7 @@ mod handlers { mod flip_comma; mod flip_or_pattern; mod flip_trait_bound; + mod generate_blanket_trait_impl; mod generate_constant; mod generate_default_from_enum_variant; mod generate_default_from_new; @@ -290,6 +291,7 @@ mod handlers { generate_default_from_enum_variant::generate_default_from_enum_variant, generate_default_from_new::generate_default_from_new, generate_delegate_trait::generate_delegate_trait, + generate_blanket_trait_impl::generate_blanket_trait_impl, generate_derive::generate_derive, generate_documentation_template::generate_doc_example, generate_documentation_template::generate_documentation_template, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 91348be97eb72..22e26c9ee1b5d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1311,6 +1311,46 @@ fn foo() { } ) } +#[test] +fn doctest_generate_blanket_trait_impl() { + check_doc_test( + "generate_blanket_trait_impl", + r#####" +trait $0Foo: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#####, + r#####" +trait Foo: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +$0impl Foo for This +where + Self::Owned: Default, +{ + fn foo(&self) -> T { + todo!() + } +} +"#####, + ) +} + #[test] fn doctest_generate_constant() { check_doc_test( From e9555f9a429270f396fe7f904fbbbe8b1f16637f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 5 Jun 2025 11:04:35 +0800 Subject: [PATCH 0176/1900] Use editor and fix tabstop on attrs --- .../handlers/generate_blanket_trait_impl.rs | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 3100809fc62ff..52bfaedaa4e5e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -8,10 +8,9 @@ use syntax::{ AstNode, ast::{ self, AssocItem, BlockExpr, GenericParam, HasGenericParams, HasName, HasTypeBounds, - HasVisibility as astHasVisibility, - edit_in_place::{AttrsOwnerEdit, Indent}, - make, + HasVisibility as astHasVisibility, edit_in_place::Indent, make, }, + syntax_editor::Position, }; // Assist: generate_blanket_trait_impl @@ -64,6 +63,7 @@ pub(crate) fn generate_blanket_trait_impl( "Generate blanket trait implementation", name.syntax().text_range(), |builder| { + let mut edit = builder.make_editor(traitd.syntax()); let namety = make::ty_path(make::path_from_text(&name.text())); let trait_where_clause = traitd.where_clause().map(|it| it.clone_for_update()); let bounds = traitd.type_bound_list(); @@ -109,7 +109,6 @@ pub(crate) fn generate_blanket_trait_impl( continue; } let f = todo_fn(&method, ctx.config).clone_for_update(); - f.remove_attrs_and_docs(); add_cfg_attrs_to(&method, &f); f.indent(1.into()); assoc_item_list.add_item(AssocItem::Fn(f)); @@ -118,10 +117,21 @@ pub(crate) fn generate_blanket_trait_impl( add_cfg_attrs_to(&traitd, &impl_); - impl_.reindent_to(indent); + impl_.indent(indent); - // FIXME: $0 after the cfg attributes - builder.insert(traitd.syntax().text_range().end(), format!("\n\n{indent}$0{impl_}")); + edit.insert_all( + Position::after(traitd.syntax()), + vec![ + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], + ); + + if let Some(cap) = ctx.config.snippet_cap { + builder.add_tabstop_before_token(cap, impl_.impl_token().unwrap()); + } + + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ); @@ -426,8 +436,8 @@ mod foo { } } - $0#[cfg(test)] - impl Foo for This { + #[cfg(test)] + $0impl Foo for This { fn foo(&self) -> T { todo!() } @@ -667,8 +677,8 @@ trait Foo { } } -$0#[cfg(test)] -impl Foo for This { +#[cfg(test)] +$0impl Foo for This { fn foo(&self, x: i32) -> i32 { todo!() } @@ -701,8 +711,8 @@ trait Foo { } } -$0#[cfg(test)] -impl Foo for This { +#[cfg(test)] +$0impl Foo for This { #[cfg(test)] fn foo(&self, x: i32) -> i32 { todo!() From f4db374e85378adff477285c9dbca17bc83eadb0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 6 Jun 2025 11:25:59 +0800 Subject: [PATCH 0177/1900] Add smart name and auto unsized --- .../handlers/generate_blanket_trait_impl.rs | 366 +++++++++++++++--- .../crates/ide-assists/src/tests/generated.rs | 2 +- 2 files changed, 312 insertions(+), 56 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 52bfaedaa4e5e..32ea37c37f39b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -8,7 +8,7 @@ use syntax::{ AstNode, ast::{ self, AssocItem, BlockExpr, GenericParam, HasGenericParams, HasName, HasTypeBounds, - HasVisibility as astHasVisibility, edit_in_place::Indent, make, + HasVisibility, edit_in_place::Indent, make, }, syntax_editor::Position, }; @@ -42,7 +42,7 @@ use syntax::{ // } // } // -// $0impl Foo for This +// $0impl Foo for This // where // Self::Owned: Default, // { @@ -66,15 +66,15 @@ pub(crate) fn generate_blanket_trait_impl( let mut edit = builder.make_editor(traitd.syntax()); let namety = make::ty_path(make::path_from_text(&name.text())); let trait_where_clause = traitd.where_clause().map(|it| it.clone_for_update()); - let bounds = traitd.type_bound_list(); + let bounds = traitd.type_bound_list().and_then(exlucde_sized); let is_unsafe = traitd.unsafe_token().is_some(); - let thisname = make::name("This"); + let thisname = this_name(&traitd); let thisty = make::ty_path(make::path_from_text(&thisname.text())); let indent = traitd.indent_level(); let gendecl = make::generic_param_list([GenericParam::TypeParam(make::type_param( thisname.clone(), - bounds, + apply_sized(has_sized(&traitd), bounds), ))]); let trait_gen_args = @@ -138,6 +138,122 @@ pub(crate) fn generate_blanket_trait_impl( Some(()) } +fn has_sized(traitd: &ast::Trait) -> bool { + if let Some(sized) = find_bound("Sized", traitd.type_bound_list()) { + sized.question_mark_token().is_none() + } else if let Some(is_sized) = where_clause_sized(traitd.where_clause()) { + is_sized + } else { + contained_owned_self_method(traitd.assoc_item_list()) + } +} + +fn contained_owned_self_method(item_list: Option) -> bool { + item_list.into_iter().flat_map(|assoc_item_list| assoc_item_list.assoc_items()).any(|item| { + match item { + AssocItem::Fn(f) => { + has_owned_self(&f) && where_clause_sized(f.where_clause()).is_none() + } + _ => false, + } + }) +} + +fn has_owned_self(f: &ast::Fn) -> bool { + has_owned_self_param(f) || has_ret_owned_self(f) +} + +fn has_owned_self_param(f: &ast::Fn) -> bool { + f.param_list() + .and_then(|param_list| param_list.self_param()) + .is_some_and(|sp| sp.amp_token().is_none() && sp.colon_token().is_none()) +} + +fn has_ret_owned_self(f: &ast::Fn) -> bool { + f.ret_type() + .and_then(|ret| match ret.ty() { + Some(ast::Type::PathType(ty)) => ty.path(), + _ => None, + }) + .is_some_and(|path| { + path.segment() + .and_then(|seg| seg.name_ref()) + .is_some_and(|name| path.qualifier().is_none() && name.text() == "Self") + }) +} + +fn where_clause_sized(where_clause: Option) -> Option { + where_clause?.predicates().find_map(|pred| { + find_bound("Sized", pred.type_bound_list()) + .map(|bound| bound.question_mark_token().is_none()) + }) +} + +fn apply_sized(has_sized: bool, bounds: Option) -> Option { + if has_sized { + return bounds; + } + let bounds = bounds + .into_iter() + .flat_map(|bounds| bounds.bounds()) + .chain([make::type_bound_text("?Sized")]); + make::type_bound_list(bounds) +} + +fn exlucde_sized(bounds: ast::TypeBoundList) -> Option { + make::type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized"))) +} + +fn this_name(traitd: &ast::Trait) -> ast::Name { + let mut use_t = false; + let mut use_i = false; + let mut use_this = false; + + let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some(); + + traitd + .generic_param_list() + .into_iter() + .flat_map(|param_list| param_list.generic_params()) + .filter_map(|param| match param { + GenericParam::LifetimeParam(_) => None, + GenericParam::ConstParam(cp) => cp.name(), + GenericParam::TypeParam(tp) => tp.name(), + }) + .for_each(|name| match &*name.text() { + "T" => use_t = true, + "I" => use_i = true, + "This" => use_this = true, + _ => (), + }); + + make::name(if has_iter { + if !use_i { + "I" + } else if !use_t { + "T" + } else { + "This" + } + } else if !use_t { + "T" + } else { + "This" + }) +} + +fn find_bound(s: &str, bounds: Option) -> Option { + bounds.into_iter().flat_map(|bounds| bounds.bounds()).find(|bound| ty_bound_is(bound, s)) +} + +fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool { + matches!(bound.ty(), + Some(ast::Type::PathType(ty)) if ty.path() + .and_then(|path| path.segment()) + .and_then(|segment| segment.name_ref()) + .is_some_and(|name| name.text() == s)) +} + fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { let params = f.param_list().unwrap_or_else(|| make::param_list(None, None)); make::fn_( @@ -198,7 +314,7 @@ where } } -$0impl Foo for This +$0impl Foo for This where Self::Owned: Default, { @@ -210,6 +326,146 @@ where ); } + #[test] + fn test_gen_blanket_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator + Sized { + fn foo(mut self) -> Self::Item { + self.next().unwrap() + } +} +"#, + r#" +trait Foo: Iterator + Sized { + fn foo(mut self) -> Self::Item { + self.next().unwrap() + } +} + +$0impl Foo for I {} +"#, + ); + + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(self) -> Self::Item; +} +"#, + r#" +trait Foo: Iterator { + fn foo(self) -> Self::Item; +} + +$0impl Foo for I { + fn foo(self) -> Self::Item { + todo!() + } +} +"#, + ); + + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> Self; +} +"#, + r#" +trait Foo { + fn foo(&self) -> Self; +} + +$0impl Foo for T { + fn foo(&self) -> Self { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_non_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; +} + +$0impl Foo for I { + fn foo(&self) -> Self::Item { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(self) where Self: Sized; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(self) where Self: Sized; +} + +$0impl Foo for I { + fn foo(&self) -> Self::Item { + todo!() + } + + fn each(self) where Self: Sized { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(&self) -> Self where Self: Sized; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(&self) -> Self where Self: Sized; +} + +$0impl Foo for I { + fn foo(&self) -> Self::Item { + todo!() + } + + fn each(&self) -> Self where Self: Sized { + todo!() + } +} +"#, + ); + } + #[test] fn test_gen_blanket_indent() { check_assist( @@ -241,7 +497,7 @@ mod foo { } } - $0impl Foo for This + $0impl Foo for This where Self::Owned: Default, { @@ -283,7 +539,7 @@ mod foo { } } - $0impl Foo for This + $0impl Foo for This where Self::Owned: Default, Self: Send, @@ -329,7 +585,7 @@ mod foo { } } - $0impl Foo for This + $0impl Foo for This where Self::Owned: Default, Self: Send, @@ -347,7 +603,7 @@ mod foo { r#" mod foo { trait $0Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); @@ -358,15 +614,15 @@ mod foo { r#" mod foo { trait Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); } } - $0impl Foo for This { - fn foo(&self) -> T { + $0impl Foo for T { + fn foo(&self) -> i32 { todo!() } } @@ -379,7 +635,7 @@ mod foo { mod foo { mod bar { trait $0Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); @@ -392,15 +648,15 @@ mod foo { mod foo { mod bar { trait Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); } } - $0impl Foo for This { - fn foo(&self) -> T { + $0impl Foo for T { + fn foo(&self) -> i32 { todo!() } } @@ -415,7 +671,7 @@ mod foo { mod bar { #[cfg(test)] trait $0Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); @@ -429,7 +685,7 @@ mod foo { mod bar { #[cfg(test)] trait Foo { - fn foo(&self) -> T; + fn foo(&self) -> i32; fn print_foo(&self) { println!("{}", self.foo()); @@ -437,8 +693,8 @@ mod foo { } #[cfg(test)] - $0impl Foo for This { - fn foo(&self) -> T { + $0impl Foo for T { + fn foo(&self) -> i32 { todo!() } } @@ -480,7 +736,7 @@ where } } -$0impl Foo for This +$0impl Foo for This where Self::Owned: Default, { @@ -524,7 +780,7 @@ where } } -$0impl Foo for This +$0impl Foo for This where Self::Owned: Default, { @@ -570,7 +826,7 @@ where } } -$0impl Foo for This +$0impl Foo for This where Self: ToOwned, Self::Owned: Default, @@ -609,7 +865,7 @@ trait Foo { } } -$0impl Foo for This { +$0impl Foo for This { fn foo(&self, x: Self::X) -> T { todo!() } @@ -644,7 +900,7 @@ trait Foo { } } -$0impl Foo for This { +$0impl Foo for T { fn foo(&self, x: Self::X) -> i32 { todo!() } @@ -678,7 +934,7 @@ trait Foo { } #[cfg(test)] -$0impl Foo for This { +$0impl Foo for T { fn foo(&self, x: i32) -> i32 { todo!() } @@ -712,7 +968,7 @@ trait Foo { } #[cfg(test)] -$0impl Foo for This { +$0impl Foo for T { #[cfg(test)] fn foo(&self, x: i32) -> i32 { todo!() @@ -732,7 +988,7 @@ trait $0Foo {} r#" trait Foo {} -$0impl Foo for This {} +$0impl Foo for T {} "#, ); } @@ -747,7 +1003,7 @@ trait $0Foo: Copy {} r#" trait Foo: Copy {} -$0impl Foo for This {} +$0impl Foo for T {} "#, ); } @@ -762,7 +1018,7 @@ trait $0Foo where Self: Copy {} r#" trait Foo where Self: Copy {} -$0impl Foo for This +$0impl Foo for T where Self: Copy { } @@ -780,7 +1036,7 @@ trait $0Foo where Self: Copy, {} r#" trait Foo where Self: Copy, {} -$0impl Foo for This +$0impl Foo for T where Self: Copy, { } @@ -802,7 +1058,7 @@ trait Foo where Self: Copy {} -$0impl Foo for This +$0impl Foo for T where Self: Copy { } @@ -826,7 +1082,7 @@ where Self: Copy {} -$0impl Foo for This +$0impl Foo for T where Self: Copy { @@ -851,7 +1107,7 @@ where Self: Copy, {} -$0impl Foo for This +$0impl Foo for T where Self: Copy, { @@ -878,7 +1134,7 @@ where (): Into, {} -$0impl Foo for This +$0impl Foo for T where Self: Copy, (): Into, @@ -906,7 +1162,7 @@ where (): Into, {} -$0impl Foo for This +$0impl Foo for T where Self: Copy + Sync, (): Into, @@ -930,7 +1186,7 @@ trait Foo { fn foo(&self) {} } -$0impl Foo for This {} +$0impl Foo for T {} "#, ); } @@ -947,7 +1203,7 @@ trait $0Foo {} /// some docs trait Foo {} -$0impl Foo for This {} +$0impl Foo for T {} "#, ); } @@ -968,7 +1224,7 @@ trait Foo { fn bar(&self); } -$0impl Foo for This { +$0impl Foo for T { fn foo(&self) { todo!() } @@ -987,18 +1243,18 @@ $0impl Foo for This { generate_blanket_trait_impl, r#" trait $0Foo { - fn foo(&self, value: T) -> T; - fn bar(&self, value: T) -> T { todo!() } + fn foo(&self, value: i32) -> i32; + fn bar(&self, value: i32) -> i32 { todo!() } } "#, r#" trait Foo { - fn foo(&self, value: T) -> T; - fn bar(&self, value: T) -> T { todo!() } + fn foo(&self, value: i32) -> i32; + fn bar(&self, value: i32) -> i32 { todo!() } } -$0impl Foo for This { - fn foo(&self, value: T) -> T { +$0impl Foo for T { + fn foo(&self, value: i32) -> i32 { todo!() } } @@ -1020,7 +1276,7 @@ trait Foo<'a> { fn foo(&self) -> &'a str; } -$0impl<'a, This> Foo<'a> for This { +$0impl<'a, T: ?Sized> Foo<'a> for T { fn foo(&self) -> &'a str { todo!() } @@ -1043,7 +1299,7 @@ trait Foo<'a: 'static> { fn foo(&self) -> &'a str; } -$0impl<'a: 'static, This> Foo<'a> for This { +$0impl<'a: 'static, T: ?Sized> Foo<'a> for T { fn foo(&self) -> &'a str { todo!() } @@ -1066,7 +1322,7 @@ trait Foo<'a>: 'a { fn foo(&self) -> &'a str; } -$0impl<'a, This: 'a> Foo<'a> for This { +$0impl<'a, T: 'a + ?Sized> Foo<'a> for T { fn foo(&self) -> &'a str { todo!() } @@ -1089,7 +1345,7 @@ trait Foo<'a, 'b> { fn foo(&self) -> &'a &'b str; } -$0impl<'a, 'b, This> Foo<'a, 'b> for This { +$0impl<'a, 'b, T: ?Sized> Foo<'a, 'b> for T { fn foo(&self) -> &'a &'b str { todo!() } @@ -1116,7 +1372,7 @@ where 'a: 'static, fn foo(&self) -> &'a str; } -$0impl<'a, This> Foo<'a> for This +$0impl<'a, T: ?Sized> Foo<'a> for T where 'a: 'static, { fn foo(&self) -> &'a str { @@ -1203,7 +1459,7 @@ trait Foo { } } -$0impl Foo for This { +$0impl Foo for T { fn foo(&self) -> i32 { todo!() } @@ -1229,7 +1485,7 @@ trait $0Foo { } trait Bar {} -impl Bar for This {} +impl Bar for T {} "#, r#" trait Foo { @@ -1240,14 +1496,14 @@ trait Foo { } } -$0impl Foo for This { +$0impl Foo for T { fn foo(&self) -> i32 { todo!() } } trait Bar {} -impl Bar for This {} +impl Bar for T {} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 22e26c9ee1b5d..54d111aa43929 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1339,7 +1339,7 @@ where } } -$0impl Foo for This +$0impl Foo for This where Self::Owned: Default, { From ee892ad3b245ae2e786ab3f5ffb6578be274342e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 28 Jun 2025 14:54:59 +0800 Subject: [PATCH 0178/1900] Change tabstop to self_ty --- .../handlers/generate_blanket_trait_impl.rs | 86 ++++++++++--------- .../crates/ide-assists/src/tests/generated.rs | 2 +- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 32ea37c37f39b..7904170d37c0d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -42,7 +42,7 @@ use syntax::{ // } // } // -// $0impl Foo for This +// impl Foo for $0This // where // Self::Owned: Default, // { @@ -128,7 +128,9 @@ pub(crate) fn generate_blanket_trait_impl( ); if let Some(cap) = ctx.config.snippet_cap { - builder.add_tabstop_before_token(cap, impl_.impl_token().unwrap()); + if let Some(self_ty) = impl_.self_ty() { + builder.add_tabstop_before(cap, self_ty); + } } builder.add_file_edits(ctx.vfs_file_id(), edit); @@ -314,7 +316,7 @@ where } } -$0impl Foo for This +impl Foo for $0This where Self::Owned: Default, { @@ -344,7 +346,7 @@ trait Foo: Iterator + Sized { } } -$0impl Foo for I {} +impl Foo for $0I {} "#, ); @@ -360,7 +362,7 @@ trait Foo: Iterator { fn foo(self) -> Self::Item; } -$0impl Foo for I { +impl Foo for $0I { fn foo(self) -> Self::Item { todo!() } @@ -380,7 +382,7 @@ trait Foo { fn foo(&self) -> Self; } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self) -> Self { todo!() } @@ -403,7 +405,7 @@ trait Foo: Iterator { fn foo(&self) -> Self::Item; } -$0impl Foo for I { +impl Foo for $0I { fn foo(&self) -> Self::Item { todo!() } @@ -426,7 +428,7 @@ trait Foo: Iterator { fn each(self) where Self: Sized; } -$0impl Foo for I { +impl Foo for $0I { fn foo(&self) -> Self::Item { todo!() } @@ -453,7 +455,7 @@ trait Foo: Iterator { fn each(&self) -> Self where Self: Sized; } -$0impl Foo for I { +impl Foo for $0I { fn foo(&self) -> Self::Item { todo!() } @@ -497,7 +499,7 @@ mod foo { } } - $0impl Foo for This + impl Foo for $0This where Self::Owned: Default, { @@ -539,7 +541,7 @@ mod foo { } } - $0impl Foo for This + impl Foo for $0This where Self::Owned: Default, Self: Send, @@ -585,7 +587,7 @@ mod foo { } } - $0impl Foo for This + impl Foo for $0This where Self::Owned: Default, Self: Send, @@ -621,7 +623,7 @@ mod foo { } } - $0impl Foo for T { + impl Foo for $0T { fn foo(&self) -> i32 { todo!() } @@ -655,7 +657,7 @@ mod foo { } } - $0impl Foo for T { + impl Foo for $0T { fn foo(&self) -> i32 { todo!() } @@ -693,7 +695,7 @@ mod foo { } #[cfg(test)] - $0impl Foo for T { + impl Foo for $0T { fn foo(&self) -> i32 { todo!() } @@ -736,7 +738,7 @@ where } } -$0impl Foo for This +impl Foo for $0This where Self::Owned: Default, { @@ -780,7 +782,7 @@ where } } -$0impl Foo for This +impl Foo for $0This where Self::Owned: Default, { @@ -826,7 +828,7 @@ where } } -$0impl Foo for This +impl Foo for $0This where Self: ToOwned, Self::Owned: Default, @@ -865,7 +867,7 @@ trait Foo { } } -$0impl Foo for This { +impl Foo for $0This { fn foo(&self, x: Self::X) -> T { todo!() } @@ -900,7 +902,7 @@ trait Foo { } } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self, x: Self::X) -> i32 { todo!() } @@ -934,7 +936,7 @@ trait Foo { } #[cfg(test)] -$0impl Foo for T { +impl Foo for $0T { fn foo(&self, x: i32) -> i32 { todo!() } @@ -968,7 +970,7 @@ trait Foo { } #[cfg(test)] -$0impl Foo for T { +impl Foo for $0T { #[cfg(test)] fn foo(&self, x: i32) -> i32 { todo!() @@ -988,7 +990,7 @@ trait $0Foo {} r#" trait Foo {} -$0impl Foo for T {} +impl Foo for $0T {} "#, ); } @@ -1003,7 +1005,7 @@ trait $0Foo: Copy {} r#" trait Foo: Copy {} -$0impl Foo for T {} +impl Foo for $0T {} "#, ); } @@ -1018,7 +1020,7 @@ trait $0Foo where Self: Copy {} r#" trait Foo where Self: Copy {} -$0impl Foo for T +impl Foo for $0T where Self: Copy { } @@ -1036,7 +1038,7 @@ trait $0Foo where Self: Copy, {} r#" trait Foo where Self: Copy, {} -$0impl Foo for T +impl Foo for $0T where Self: Copy, { } @@ -1058,7 +1060,7 @@ trait Foo where Self: Copy {} -$0impl Foo for T +impl Foo for $0T where Self: Copy { } @@ -1082,7 +1084,7 @@ where Self: Copy {} -$0impl Foo for T +impl Foo for $0T where Self: Copy { @@ -1107,7 +1109,7 @@ where Self: Copy, {} -$0impl Foo for T +impl Foo for $0T where Self: Copy, { @@ -1134,7 +1136,7 @@ where (): Into, {} -$0impl Foo for T +impl Foo for $0T where Self: Copy, (): Into, @@ -1162,7 +1164,7 @@ where (): Into, {} -$0impl Foo for T +impl Foo for $0T where Self: Copy + Sync, (): Into, @@ -1186,7 +1188,7 @@ trait Foo { fn foo(&self) {} } -$0impl Foo for T {} +impl Foo for $0T {} "#, ); } @@ -1203,7 +1205,7 @@ trait $0Foo {} /// some docs trait Foo {} -$0impl Foo for T {} +impl Foo for $0T {} "#, ); } @@ -1224,7 +1226,7 @@ trait Foo { fn bar(&self); } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self) { todo!() } @@ -1253,7 +1255,7 @@ trait Foo { fn bar(&self, value: i32) -> i32 { todo!() } } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self, value: i32) -> i32 { todo!() } @@ -1276,7 +1278,7 @@ trait Foo<'a> { fn foo(&self) -> &'a str; } -$0impl<'a, T: ?Sized> Foo<'a> for T { +impl<'a, T: ?Sized> Foo<'a> for $0T { fn foo(&self) -> &'a str { todo!() } @@ -1299,7 +1301,7 @@ trait Foo<'a: 'static> { fn foo(&self) -> &'a str; } -$0impl<'a: 'static, T: ?Sized> Foo<'a> for T { +impl<'a: 'static, T: ?Sized> Foo<'a> for $0T { fn foo(&self) -> &'a str { todo!() } @@ -1322,7 +1324,7 @@ trait Foo<'a>: 'a { fn foo(&self) -> &'a str; } -$0impl<'a, T: 'a + ?Sized> Foo<'a> for T { +impl<'a, T: 'a + ?Sized> Foo<'a> for $0T { fn foo(&self) -> &'a str { todo!() } @@ -1345,7 +1347,7 @@ trait Foo<'a, 'b> { fn foo(&self) -> &'a &'b str; } -$0impl<'a, 'b, T: ?Sized> Foo<'a, 'b> for T { +impl<'a, 'b, T: ?Sized> Foo<'a, 'b> for $0T { fn foo(&self) -> &'a &'b str { todo!() } @@ -1372,7 +1374,7 @@ where 'a: 'static, fn foo(&self) -> &'a str; } -$0impl<'a, T: ?Sized> Foo<'a> for T +impl<'a, T: ?Sized> Foo<'a> for $0T where 'a: 'static, { fn foo(&self) -> &'a str { @@ -1459,7 +1461,7 @@ trait Foo { } } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self) -> i32 { todo!() } @@ -1496,7 +1498,7 @@ trait Foo { } } -$0impl Foo for T { +impl Foo for $0T { fn foo(&self) -> i32 { todo!() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 54d111aa43929..18edb1b8d17c6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1339,7 +1339,7 @@ where } } -$0impl Foo for This +impl Foo for $0This where Self::Owned: Default, { From 71f8c5a49968840fccaf78d6657ebfa2cc222a3a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 28 Jun 2025 15:24:12 +0800 Subject: [PATCH 0179/1900] Use NameGenerator --- .../handlers/generate_blanket_trait_impl.rs | 56 +++++++------------ .../crates/ide-assists/src/tests/generated.rs | 2 +- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 7904170d37c0d..36031f15a043a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -3,7 +3,10 @@ use crate::{ assist_context::{AssistContext, Assists}, utils::add_cfg_attrs_to, }; -use ide_db::assists::{AssistId, AssistKind, ExprFillDefaultMode}; +use ide_db::{ + assists::{AssistId, AssistKind, ExprFillDefaultMode}, + syntax_helpers::suggest_name, +}; use syntax::{ AstNode, ast::{ @@ -42,7 +45,7 @@ use syntax::{ // } // } // -// impl Foo for $0This +// impl Foo for $0T1 // where // Self::Owned: Default, // { @@ -207,13 +210,9 @@ fn exlucde_sized(bounds: ast::TypeBoundList) -> Option { } fn this_name(traitd: &ast::Trait) -> ast::Name { - let mut use_t = false; - let mut use_i = false; - let mut use_this = false; - let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some(); - traitd + let params = traitd .generic_param_list() .into_iter() .flat_map(|param_list| param_list.generic_params()) @@ -222,26 +221,13 @@ fn this_name(traitd: &ast::Trait) -> ast::Name { GenericParam::ConstParam(cp) => cp.name(), GenericParam::TypeParam(tp) => tp.name(), }) - .for_each(|name| match &*name.text() { - "T" => use_t = true, - "I" => use_i = true, - "This" => use_this = true, - _ => (), - }); - - make::name(if has_iter { - if !use_i { - "I" - } else if !use_t { - "T" - } else { - "This" - } - } else if !use_t { - "T" - } else { - "This" - }) + .map(|name| name.to_string()) + .collect::>(); + + let mut name_gen = + suggest_name::NameGenerator::new_with_names(params.iter().map(String::as_str)); + + make::name(&name_gen.suggest_name(if has_iter { "I" } else { "T" })) } fn find_bound(s: &str, bounds: Option) -> Option { @@ -316,7 +302,7 @@ where } } -impl Foo for $0This +impl Foo for $0T1 where Self::Owned: Default, { @@ -499,7 +485,7 @@ mod foo { } } - impl Foo for $0This + impl Foo for $0T1 where Self::Owned: Default, { @@ -541,7 +527,7 @@ mod foo { } } - impl Foo for $0This + impl Foo for $0T1 where Self::Owned: Default, Self: Send, @@ -587,7 +573,7 @@ mod foo { } } - impl Foo for $0This + impl Foo for $0T1 where Self::Owned: Default, Self: Send, @@ -738,7 +724,7 @@ where } } -impl Foo for $0This +impl Foo for $0T1 where Self::Owned: Default, { @@ -782,7 +768,7 @@ where } } -impl Foo for $0This +impl Foo for $0T1 where Self::Owned: Default, { @@ -828,7 +814,7 @@ where } } -impl Foo for $0This +impl Foo for $0T1 where Self: ToOwned, Self::Owned: Default, @@ -867,7 +853,7 @@ trait Foo { } } -impl Foo for $0This { +impl Foo for $0T1 { fn foo(&self, x: Self::X) -> T { todo!() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 18edb1b8d17c6..306812fbe1591 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1339,7 +1339,7 @@ where } } -impl Foo for $0This +impl Foo for $0T1 where Self::Owned: Default, { From c7cc54e15d3df998332a2839cbb42ce429891a91 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 28 Jun 2025 16:05:42 +0800 Subject: [PATCH 0180/1900] Move gen_blanket_trait_impl assist after generates --- src/tools/rust-analyzer/crates/ide-assists/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index be379b396006e..2977f8b8c2e75 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -291,7 +291,6 @@ mod handlers { generate_default_from_enum_variant::generate_default_from_enum_variant, generate_default_from_new::generate_default_from_new, generate_delegate_trait::generate_delegate_trait, - generate_blanket_trait_impl::generate_blanket_trait_impl, generate_derive::generate_derive, generate_documentation_template::generate_doc_example, generate_documentation_template::generate_documentation_template, @@ -310,6 +309,7 @@ mod handlers { generate_new::generate_new, generate_trait_from_impl::generate_trait_from_impl, generate_single_field_struct_from::generate_single_field_struct_from, + generate_blanket_trait_impl::generate_blanket_trait_impl, inline_call::inline_call, inline_call::inline_into_callers, inline_const_as_literal::inline_const_as_literal, From 8402a76f559332a2aafe5285c3b961c82103be41 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 25 Jul 2025 13:57:41 +0800 Subject: [PATCH 0181/1900] Add existing any implement not applicate blanket --- .../handlers/generate_blanket_trait_impl.rs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 36031f15a043a..1ec49f017fa2f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -3,7 +3,9 @@ use crate::{ assist_context::{AssistContext, Assists}, utils::add_cfg_attrs_to, }; +use hir::Semantics; use ide_db::{ + RootDatabase, assists::{AssistId, AssistKind, ExprFillDefaultMode}, syntax_helpers::suggest_name, }; @@ -61,6 +63,11 @@ pub(crate) fn generate_blanket_trait_impl( let name = ctx.find_node_at_offset::()?; let traitd = ast::Trait::cast(name.syntax().parent()?)?; + if existing_any_impl(&traitd, &ctx.sema).is_some() { + cov_mark::hit!(existing_any_impl); + return None; + } + acc.add( AssistId("generate_blanket_trait_impl", AssistKind::Generate, None), "Generate blanket trait implementation", @@ -143,6 +150,16 @@ pub(crate) fn generate_blanket_trait_impl( Some(()) } +fn existing_any_impl(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option { + let db = sema.db; + let traitd = sema.to_def(traitd)?; + traitd + .module(db) + .impl_defs(db) + .into_iter() + .find(|impl_| impl_.trait_(db).is_some_and(|it| it == traitd)) +} + fn has_sized(traitd: &ast::Trait) -> bool { if let Some(sized) = find_bound("Sized", traitd.type_bound_list()) { sized.question_mark_token().is_none() @@ -1422,6 +1439,51 @@ where ); } + #[test] + fn test_gen_blanket_existing_impl() { + cov_mark::check!(existing_any_impl); + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait $0Foo: Default { + fn foo(&self) -> Self; +} +impl Foo for () {} +"#, + ); + } + + #[test] + fn test_gen_blanket_existing_other_impl() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Default { + fn foo(&self) -> Self; +} +trait Bar: Default { + fn bar(&self) -> Self; +} +impl Bar for () {} +"#, + r#" +trait Foo: Default { + fn foo(&self) -> Self; +} + +impl Foo for $0T { + fn foo(&self) -> Self { + todo!() + } +} +trait Bar: Default { + fn bar(&self) -> Self; +} +impl Bar for () {} +"#, + ); + } + #[test] fn test_gen_blanket_apply_on_other_impl_block() { check_assist( From d946b152c2f890198d1949e992590d9c1fb393e9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 25 Jul 2025 20:52:56 +0800 Subject: [PATCH 0182/1900] Improve sized check, from super traits --- .../handlers/generate_blanket_trait_impl.rs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 1ec49f017fa2f..dc8056e90339e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -3,10 +3,11 @@ use crate::{ assist_context::{AssistContext, Assists}, utils::add_cfg_attrs_to, }; -use hir::Semantics; +use hir::{HasCrate, Semantics}; use ide_db::{ RootDatabase, assists::{AssistId, AssistKind, ExprFillDefaultMode}, + famous_defs::FamousDefs, syntax_helpers::suggest_name, }; use syntax::{ @@ -84,7 +85,7 @@ pub(crate) fn generate_blanket_trait_impl( let gendecl = make::generic_param_list([GenericParam::TypeParam(make::type_param( thisname.clone(), - apply_sized(has_sized(&traitd), bounds), + apply_sized(has_sized(&traitd, &ctx.sema), bounds), ))]); let trait_gen_args = @@ -160,16 +161,24 @@ fn existing_any_impl(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> .find(|impl_| impl_.trait_(db).is_some_and(|it| it == traitd)) } -fn has_sized(traitd: &ast::Trait) -> bool { +fn has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> bool { if let Some(sized) = find_bound("Sized", traitd.type_bound_list()) { sized.question_mark_token().is_none() } else if let Some(is_sized) = where_clause_sized(traitd.where_clause()) { is_sized } else { contained_owned_self_method(traitd.assoc_item_list()) + || super_traits_has_sized(traitd, sema) == Some(true) } } +fn super_traits_has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option { + let traitd = sema.to_def(traitd)?; + let sized = FamousDefs(sema, traitd.krate(sema.db)).core_marker_Sized()?; + + Some(traitd.all_supertraits(sema.db).contains(&sized)) +} + fn contained_owned_self_method(item_list: Option) -> bool { item_list.into_iter().flat_map(|assoc_item_list| assoc_item_list.assoc_items()).any(|item| { match item { @@ -394,6 +403,30 @@ impl Foo for $0T { ); } + #[test] + fn test_gen_blanket_super_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +//- minicore: default +trait $0Foo: Default { + fn foo(&self); +} +"#, + r#" +trait Foo: Default { + fn foo(&self); +} + +impl Foo for $0T { + fn foo(&self) { + todo!() + } +} +"#, + ); + } + #[test] fn test_gen_blanket_non_sized() { check_assist( From c3c5f2f9e7b60d2b0b5bca5d9cd330d72da3dc73 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 30 Aug 2025 19:12:15 +0800 Subject: [PATCH 0183/1900] Change add_cfg_attrs_to to make parameter --- .../handlers/generate_blanket_trait_impl.rs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index dc8056e90339e..c25b0bbe7cea0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -1,7 +1,6 @@ use crate::{ AssistConfig, assist_context::{AssistContext, Assists}, - utils::add_cfg_attrs_to, }; use hir::{HasCrate, Semantics}; use ide_db::{ @@ -13,8 +12,8 @@ use ide_db::{ use syntax::{ AstNode, ast::{ - self, AssocItem, BlockExpr, GenericParam, HasGenericParams, HasName, HasTypeBounds, - HasVisibility, edit_in_place::Indent, make, + self, AssocItem, BlockExpr, GenericParam, HasAttrs, HasGenericParams, HasName, + HasTypeBounds, HasVisibility, edit_in_place::Indent, make, }, syntax_editor::Position, }; @@ -96,6 +95,7 @@ pub(crate) fn generate_blanket_trait_impl( } let impl_ = make::impl_trait( + cfg_attrs(&traitd), is_unsafe, traitd.generic_param_list(), trait_gen_args, @@ -120,14 +120,11 @@ pub(crate) fn generate_blanket_trait_impl( continue; } let f = todo_fn(&method, ctx.config).clone_for_update(); - add_cfg_attrs_to(&method, &f); f.indent(1.into()); assoc_item_list.add_item(AssocItem::Fn(f)); } } - add_cfg_attrs_to(&traitd, &impl_); - impl_.indent(indent); edit.insert_all( @@ -138,10 +135,10 @@ pub(crate) fn generate_blanket_trait_impl( ], ); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(self_ty) = impl_.self_ty() { - builder.add_tabstop_before(cap, self_ty); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(self_ty) = impl_.self_ty() + { + builder.add_tabstop_before(cap, self_ty); } builder.add_file_edits(ctx.vfs_file_id(), edit); @@ -271,6 +268,7 @@ fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool { fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { let params = f.param_list().unwrap_or_else(|| make::param_list(None, None)); make::fn_( + cfg_attrs(f), f.visibility(), f.name().unwrap_or_else(|| make::name("unnamed")), f.generic_param_list(), @@ -294,6 +292,10 @@ fn default_block(config: &AssistConfig) -> BlockExpr { make::block_expr(None, Some(expr)) } +fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator { + node.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")) +} + #[cfg(test)] mod test { From 823101e0895d4ce17576a2aa487ba6fe3e38ea69 Mon Sep 17 00:00:00 2001 From: Elliot Roberts Date: Sat, 30 Aug 2025 13:36:29 -0700 Subject: [PATCH 0184/1900] Pass `--target` before `--` for `cargo rustc` --- .../project-model/src/toolchain_info/target_data_layout.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs index a28f468e692d0..f1d99cb8b002c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs @@ -23,14 +23,11 @@ pub fn get( QueryConfig::Cargo(sysroot, cargo_toml, _) => { let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); - cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([ - "--", - "-Z", - "unstable-options", - ]); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); if let Some(target) = target { cmd.args(["--target", target]); } + cmd.args(["--", "-Z", "unstable-options"]); match utf8_stdout(&mut cmd) { Ok(output) => return process(output), Err(e) => { From de47decebbbd8645708a47a2a7c11c58a268ac87 Mon Sep 17 00:00:00 2001 From: Ali Nazzal Date: Sun, 31 Aug 2025 09:51:47 +0300 Subject: [PATCH 0185/1900] docs(rustdoc): document that JS/TS checks run under tidy (eslint, es-check, tsc) --- src/doc/rustc-dev-guide/src/rustdoc.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index 9290fcd3b41cb..4181c2f7f170f 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -60,6 +60,18 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) * Use `./x test tests/rustdoc*` to run the tests using a stage1 rustdoc. * See [Rustdoc internals] for more information about tests. +* Use `./x.py test tidy --extra-checks=js` to run rustdoc’s JavaScript checks (`eslint`, `es-check`, and `tsc`). +> **Note:** `./x.py test tidy` already runs these checks automatically when JS/TS sources changed; `--extra-checks=js` forces them explicitly. + +### JavaScript CI checks + +Rustdoc’s JavaScript and TypeScript are checked during CI by `eslint`, `es-check`, and `tsc` (not by compiletest). These run as part of the `tidy` job. + +```bash +./x.py test tidy --extra-checks=js +``` + +The `--extra-checks=js` flag enables the frontend linting that runs in CI. [`bootstrap.toml`]: ./building/how-to-build-and-run.md From 23f1767c808d759ff683812581ca0e954d72c463 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sun, 31 Aug 2025 17:03:46 +0000 Subject: [PATCH 0186/1900] allow taking address to union field --- .../rustc_mir_build/src/check_unsafety.rs | 15 ++++ .../miri/tests/pass/both_borrows/smallvec.rs | 2 +- tests/ui/union/union-unsafe.rs | 73 +++++++++++++++++++ tests/ui/union/union-unsafe.stderr | 54 +++++++++++--- 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b5e165c75170a..195d45c2c4c49 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -554,6 +554,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_expr(self, &self.thir[arg]); return; } + + // Secondly, we allow raw borrows of union field accesses. Peel + // any of those off, and recurse normally on the LHS, which should + // reject any unsafe operations within. + let mut peeled = arg; + while let ExprKind::Scope { value: arg, .. } = self.thir[peeled].kind + && let ExprKind::Field { lhs, name: _, variant_index: _ } = self.thir[arg].kind + && let ty::Adt(def, _) = &self.thir[lhs].ty.kind() + && def.is_union() + { + peeled = lhs; + } + visit::walk_expr(self, &self.thir[peeled]); + // And return so we don't recurse directly onto the union field access(es). + return; } ExprKind::Deref { arg } => { if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) = diff --git a/src/tools/miri/tests/pass/both_borrows/smallvec.rs b/src/tools/miri/tests/pass/both_borrows/smallvec.rs index f48815e37be34..fa5cfb03de2a9 100644 --- a/src/tools/miri/tests/pass/both_borrows/smallvec.rs +++ b/src/tools/miri/tests/pass/both_borrows/smallvec.rs @@ -25,7 +25,7 @@ impl RawSmallVec { } const fn as_mut_ptr_inline(&mut self) -> *mut T { - (unsafe { &raw mut self.inline }) as *mut T + &raw mut self.inline as *mut T } const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T { diff --git a/tests/ui/union/union-unsafe.rs b/tests/ui/union/union-unsafe.rs index bd3946686be36..beb074f4e8ebc 100644 --- a/tests/ui/union/union-unsafe.rs +++ b/tests/ui/union/union-unsafe.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::mem::ManuallyDrop; +use std::ops::Deref; union U1 { a: u8, @@ -17,6 +18,10 @@ union U4 { a: T, } +union U5 { + a: usize, +} + union URef { p: &'static mut i32, } @@ -31,6 +36,20 @@ fn deref_union_field(mut u: URef) { *(u.p) = 13; //~ ERROR access to union field is unsafe } +union A { + a: usize, + b: &'static &'static B, +} + +union B { + c: usize, +} + +fn raw_deref_union_field(mut u: URef) { + // This is unsafe because we first dereference u.p (reading uninitialized memory) + let _p = &raw const *(u.p); //~ ERROR access to union field is unsafe +} + fn assign_noncopy_union_field(mut u: URefCell) { u.a = (ManuallyDrop::new(RefCell::new(0)), 1); // OK (assignment does not drop) u.a.0 = ManuallyDrop::new(RefCell::new(0)); // OK (assignment does not drop) @@ -57,6 +76,20 @@ fn main() { let a = u1.a; //~ ERROR access to union field is unsafe u1.a = 11; // OK + let mut u2 = U1 { a: 10 }; + let a = &raw mut u2.a; // OK + unsafe { *a = 3 }; + + let mut u3 = U1 { a: 10 }; + let a = std::ptr::addr_of_mut!(u3.a); // OK + unsafe { *a = 14 }; + + let u4 = U5 { a: 2 }; + let vec = vec![1, 2, 3]; + // This is unsafe because we read u4.a (potentially uninitialized memory) + // to use as an array index + let _a = &raw const vec[u4.a]; //~ ERROR access to union field is unsafe + let U1 { a } = u1; //~ ERROR access to union field is unsafe if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe if let Some(U1 { a: 13 }) = Some(u1) {} //~ ERROR access to union field is unsafe @@ -73,4 +106,44 @@ fn main() { let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK u3.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop) *u3.a = String::from("new"); //~ ERROR access to union field is unsafe + + let mut unions = [U1 { a: 1 }, U1 { a: 2 }]; + + // Array indexing + union field raw borrow - should be OK + let ptr = &raw mut unions[0].a; // OK + let ptr2 = &raw const unions[1].a; // OK + + let a = A { a: 0 }; + let _p = &raw const (**a.b).c; //~ ERROR access to union field is unsafe + + arbitrary_deref(); +} + +// regression test for https://github.com/rust-lang/rust/pull/141469#discussion_r2312546218 +fn arbitrary_deref() { + use std::ops::Deref; + + union A { + a: usize, + b: B, + } + + #[derive(Copy, Clone)] + struct B(&'static str); + + impl Deref for B { + type Target = C; + + fn deref(&self) -> &C { + println!("{:?}", self.0); + &C { c: 0 } + } + } + + union C { + c: usize, + } + + let a = A { a: 0 }; + let _p = &raw const (*a.b).c; //~ ERROR access to union field is unsafe } diff --git a/tests/ui/union/union-unsafe.stderr b/tests/ui/union/union-unsafe.stderr index 82b3f897167c7..01f4d95eb649d 100644 --- a/tests/ui/union/union-unsafe.stderr +++ b/tests/ui/union/union-unsafe.stderr @@ -1,5 +1,5 @@ error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:31:6 + --> $DIR/union-unsafe.rs:36:6 | LL | *(u.p) = 13; | ^^^^^ access to union field @@ -7,7 +7,15 @@ LL | *(u.p) = 13; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:43:6 + --> $DIR/union-unsafe.rs:50:26 + | +LL | let _p = &raw const *(u.p); + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:62:6 | LL | *u3.a = T::default(); | ^^^^ access to union field @@ -15,7 +23,7 @@ LL | *u3.a = T::default(); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:49:6 + --> $DIR/union-unsafe.rs:68:6 | LL | *u3.a = T::default(); | ^^^^ access to union field @@ -23,7 +31,7 @@ LL | *u3.a = T::default(); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:57:13 + --> $DIR/union-unsafe.rs:76:13 | LL | let a = u1.a; | ^^^^ access to union field @@ -31,7 +39,15 @@ LL | let a = u1.a; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:60:14 + --> $DIR/union-unsafe.rs:91:29 + | +LL | let _a = &raw const vec[u4.a]; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:93:14 | LL | let U1 { a } = u1; | ^ access to union field @@ -39,7 +55,7 @@ LL | let U1 { a } = u1; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:61:20 + --> $DIR/union-unsafe.rs:94:20 | LL | if let U1 { a: 12 } = u1 {} | ^^ access to union field @@ -47,7 +63,7 @@ LL | if let U1 { a: 12 } = u1 {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:62:25 + --> $DIR/union-unsafe.rs:95:25 | LL | if let Some(U1 { a: 13 }) = Some(u1) {} | ^^ access to union field @@ -55,7 +71,7 @@ LL | if let Some(U1 { a: 13 }) = Some(u1) {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:67:6 + --> $DIR/union-unsafe.rs:100:6 | LL | *u2.a = String::from("new"); | ^^^^ access to union field @@ -63,7 +79,7 @@ LL | *u2.a = String::from("new"); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:71:6 + --> $DIR/union-unsafe.rs:104:6 | LL | *u3.a = 1; | ^^^^ access to union field @@ -71,13 +87,29 @@ LL | *u3.a = 1; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:75:6 + --> $DIR/union-unsafe.rs:108:6 | LL | *u3.a = String::from("new"); | ^^^^ access to union field | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -error: aborting due to 10 previous errors +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:117:28 + | +LL | let _p = &raw const (**a.b).c; + | ^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:148:27 + | +LL | let _p = &raw const (*a.b).c; + | ^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0133`. From a324fe956981f4ad736ffc268f222037fd853e83 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 2 Sep 2025 06:39:32 +0300 Subject: [PATCH 0187/1900] Make sense of the mess that were (are) different kind of generics in the solver To the extent possible. Previously they were confused. Sometimes generic params were treated as `Param` and sometimes as `Placeholder`. A completely redundant (in the new solver) mapping of salsa::Id to ints to intern some info where we could just store it uninterned (not in Chalk though, for some weird reason). Plus fix a cute bug in closure substitution that was caught by the assertions of Chalk but the next solver did not have such assertions. Do we need more assertions? --- .../crates/hir-ty/src/chalk_ext.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 29 ++- .../crates/hir-ty/src/consteval_nextsolver.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 6 +- .../crates/hir-ty/src/display.rs | 46 ++--- .../crates/hir-ty/src/dyn_compatibility.rs | 20 +- .../crates/hir-ty/src/generics.rs | 8 +- .../crates/hir-ty/src/infer/closure.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 12 +- .../crates/hir-ty/src/lower/path.rs | 8 +- .../crates/hir-ty/src/lower_nextsolver.rs | 13 +- .../hir-ty/src/lower_nextsolver/path.rs | 2 + .../crates/hir-ty/src/mapping.rs | 36 +++- .../crates/hir-ty/src/mir/monomorphization.rs | 4 +- .../crates/hir-ty/src/next_solver/consts.rs | 3 + .../hir-ty/src/next_solver/generic_arg.rs | 38 ++-- .../crates/hir-ty/src/next_solver/generics.rs | 63 +++--- .../hir-ty/src/next_solver/infer/mod.rs | 13 +- .../crates/hir-ty/src/next_solver/mapping.rs | 191 ++++++++++-------- .../crates/hir-ty/src/next_solver/region.rs | 3 + .../crates/hir-ty/src/next_solver/ty.rs | 12 +- .../crates/hir-ty/src/variance.rs | 4 +- .../rust-analyzer/crates/hir/src/display.rs | 11 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +- 26 files changed, 324 insertions(+), 233 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 8fa1aff0ef636..33ae099c690ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -306,7 +306,7 @@ impl TyExt for Ty { predicates.map(|it| it.into_value_and_skipped_binders().0) } TyKind::Placeholder(idx) => { - let id = from_placeholder_idx(db, *idx); + let id = from_placeholder_idx(db, *idx).0; let generic_params = db.generic_params(id.parent); let param_data = &generic_params[id.local_id]; match param_data { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index abf97f3d0e302..0f2cc17f563dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -107,25 +107,24 @@ pub(crate) fn path_to_const<'g>( match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { Some(ValueNs::GenericParam(p)) => { let ty = db.const_param_ty(p); + let args = args(); let value = match mode { ParamLoweringMode::Placeholder => { - ConstValue::Placeholder(to_placeholder_idx(db, p.into())) + let idx = args.type_or_const_param_idx(p.into()).unwrap(); + ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32)) } - ParamLoweringMode::Variable => { - let args = args(); - match args.type_or_const_param_idx(p.into()) { - Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - return None; - } + ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) { + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + return None; } - } + }, }; Some(ConstData { ty, value }.intern(Interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 00fc4e5610dce..4f95c9a13acdb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -49,7 +49,7 @@ pub(crate) fn path_to_const<'a, 'g>( .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) { Some((idx, _param)) => { - Some(Const::new_param(interner, ParamConst { index: idx as u32 })) + Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) } None => { never!( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 97754f47233b2..0b7213d785c91 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -410,13 +410,15 @@ fn hir_database_is_dyn_compatible() { #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedTypeOrConstParamId { - pub loc: TypeOrConstParamId, + /// This stores the param and its index. + pub loc: (TypeOrConstParamId, u32), } #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedLifetimeParamId { - pub loc: LifetimeParamId, + /// This stores the param and its index. + pub loc: (LifetimeParamId, u32), } #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0df727a8e5c04..5b8093f6b7246 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -61,9 +61,8 @@ use crate::{ next_solver::{ BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, mapping::{ - ChalkToNextSolver, bound_var_to_lifetime_idx, bound_var_to_type_or_const_param_idx, - convert_args_for_result, convert_const_for_result, convert_region_for_result, - convert_ty_for_result, + ChalkToNextSolver, convert_args_for_result, convert_const_for_result, + convert_region_for_result, convert_ty_for_result, }, }, primitive, to_assoc_type_id, @@ -641,7 +640,7 @@ impl HirDisplay for ProjectionTy { && !f.bounds_formatting_ctx.contains(self) { let db = f.db; - let id = from_placeholder_idx(db, *idx); + let id = from_placeholder_idx(db, *idx).0; let generics = generics(db, id.parent); let substs = generics.placeholder_subst(db); @@ -736,14 +735,14 @@ impl HirDisplay for Const { impl<'db> HirDisplay for crate::next_solver::Const<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.kind() { + rustc_type_ir::ConstKind::Placeholder(_) => write!(f, ""), rustc_type_ir::ConstKind::Bound(db, bound_const) => { write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) } rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), - rustc_type_ir::ConstKind::Placeholder(idx) => { - let id = bound_var_to_type_or_const_param_idx(f.db, idx.bound); - let generics = generics(f.db, id.parent); - let param_data = &generics[id.local_id]; + rustc_type_ir::ConstKind::Param(param) => { + let generics = generics(f.db, param.id.parent()); + let param_data = &generics[param.id.local_id()]; write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; Ok(()) } @@ -765,7 +764,6 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> { } rustc_type_ir::ConstKind::Error(..) => f.write_char('_'), rustc_type_ir::ConstKind::Expr(..) => write!(f, ""), - rustc_type_ir::ConstKind::Param(_) => write!(f, ""), } } } @@ -1178,7 +1176,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { if let TyKind::Ref(l, _, _) = kind { f.write_char('&')?; if f.render_region(l) { - convert_region_for_result(l).hir_fmt(f)?; + convert_region_for_result(interner, l).hir_fmt(f)?; f.write_char(' ')?; } match m { @@ -1611,14 +1609,10 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { write!(f, "{{closure}}")?; } } - TyKind::Placeholder(idx) => { - let placeholder_index = chalk_ir::PlaceholderIndex { - idx: idx.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: idx.universe.as_usize() }, - }; - let id = from_placeholder_idx(db, placeholder_index); - let generics = generics(db, id.parent); - let param_data = &generics[id.local_id]; + TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?, + TyKind::Param(param) => { + let generics = generics(db, param.id.parent()); + let param_data = &generics[param.id.local_id()]; match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { @@ -1634,7 +1628,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { TypeParamProvenance::ArgumentImplTrait => { let substs = generics.placeholder_subst(db); let bounds = db - .generic_predicates(id.parent) + .generic_predicates(param.id.parent()) .iter() .map(|pred| pred.clone().substitute(Interner, &substs)) .filter(|wc| match wc.skip_binders() { @@ -1656,7 +1650,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { WhereClause::LifetimeOutlives(_) => false, }) .collect::>(); - let krate = id.parent.module(db).krate(); + let krate = param.id.parent().module(db).krate(); write_bounds_like_dyn_trait_with_prefix( f, "impl", @@ -1671,7 +1665,6 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } } - TyKind::Param(_) => write!(f, "{{param}}")?, TyKind::Bound(debruijn_index, ty) => { let idx = chalk_ir::BoundVar { debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), @@ -2294,7 +2287,7 @@ impl HirDisplay for LifetimeData { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { LifetimeData::Placeholder(idx) => { - let id = lt_from_placeholder_idx(f.db, *idx); + let id = lt_from_placeholder_idx(f.db, *idx).0; let generics = generics(f.db, id.parent); let param_data = &generics[id.local_id]; write!(f, "{}", param_data.name.display(f.db, f.edition()))?; @@ -2319,10 +2312,9 @@ impl HirDisplay for LifetimeData { impl<'db> HirDisplay for crate::next_solver::Region<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.kind() { - rustc_type_ir::RegionKind::RePlaceholder(idx) => { - let id = bound_var_to_lifetime_idx(f.db, idx.bound.var); - let generics = generics(f.db, id.parent); - let param_data = &generics[id.local_id]; + rustc_type_ir::RegionKind::ReEarlyParam(param) => { + let generics = generics(f.db, param.id.parent); + let param_data = &generics[param.id.local_id]; write!(f, "{}", param_data.name.display(f.db, f.edition()))?; Ok(()) } @@ -2339,7 +2331,7 @@ impl<'db> HirDisplay for crate::next_solver::Region<'db> { } } rustc_type_ir::RegionKind::ReErased => write!(f, "'"), - rustc_type_ir::RegionKind::ReEarlyParam(_) => write!(f, ""), + rustc_type_ir::RegionKind::RePlaceholder(_) => write!(f, ""), rustc_type_ir::RegionKind::ReLateParam(_) => write!(f, ""), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index b0c61c29db0b5..23280b1f3b42b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,10 +2,12 @@ use std::ops::ControlFlow; +use hir_def::hir::generics::LocalTypeOrConstParamId; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; +use hir_def::{TypeOrConstParamId, TypeParamId}; use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -384,7 +386,6 @@ where } // Allow `impl AutoTrait` predicates - let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); if let ClauseKind::Trait(TraitPredicate { trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, @@ -392,11 +393,8 @@ where && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id && let trait_data = db.trait_signature(trait_id) && trait_data.flags.contains(TraitFlags::AUTO) - && pred_trait_ref.self_ty() - == crate::next_solver::Ty::new( - interner, - rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), - ) + && let rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, .. }) = + pred_trait_ref.self_ty().kind() { continue; } @@ -422,9 +420,13 @@ fn receiver_is_dispatchable<'db>( let sig = sig.instantiate_identity(); let interner: DbInterner<'_> = DbInterner::new_with(db, Some(trait_.krate(db)), None); + let self_param_id = TypeParamId::from_unchecked(TypeOrConstParamId { + parent: trait_.into(), + local_id: LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)), + }); let self_param_ty = crate::next_solver::Ty::new( interner, - rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, id: self_param_id }), ); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible @@ -452,7 +454,9 @@ fn receiver_is_dispatchable<'db>( }; // Type `U` - let unsized_self_ty = crate::next_solver::Ty::new_param(interner, u32::MAX, Symbol::empty()); + // FIXME: That seems problematic to fake a generic param like that? + let unsized_self_ty = + crate::next_solver::Ty::new_param(interner, self_param_id, u32::MAX, Symbol::empty()); // `Receiver[Self => U]` let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 412b3ac425028..e179e41b1cbe2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -256,15 +256,15 @@ impl Generics { pub fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution { Substitution::from_iter( Interner, - self.iter_id().map(|id| match id { + self.iter_id().enumerate().map(|(index, id)| match id { GenericParamId::TypeParamId(id) => { - to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner) + to_placeholder_idx(db, id.into(), index as u32).to_ty(Interner).cast(Interner) } - GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into()) + GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into(), index as u32) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), GenericParamId::LifetimeParamId(id) => { - lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner) + lt_to_placeholder_idx(db, id, index as u32).to_lifetime(Interner).cast(Interner) } }), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 38ac2e1170773..9ef046afdb3d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -866,7 +866,7 @@ impl CapturedItemWithoutTy { idx: chalk_ir::PlaceholderIndex, outer_binder: DebruijnIndex, ) -> Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx); + let x = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.type_or_const_param_idx(x) else { return Err(()); }; @@ -878,7 +878,7 @@ impl CapturedItemWithoutTy { idx: chalk_ir::PlaceholderIndex, outer_binder: DebruijnIndex, ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx); + let x = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.type_or_const_param_idx(x) else { return Err(()); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index e2ee8935a8855..f8abb3b7f6fef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -19,14 +19,15 @@ use rustc_type_ir::{ }; use triomphe::Arc; +use crate::utils::ClosureSubst; use crate::{ - TraitEnvironment, + Interner, TraitEnvironment, consteval_nextsolver::try_const_usize, db::HirDatabase, next_solver::{ DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, convert_args_for_result}, project::solve_normalize::deeply_normalize, }, }; @@ -333,9 +334,15 @@ pub fn layout_of_ty_query<'db>( let fields = captures .iter() .map(|it| { - let ty = - convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) - .instantiate(interner, args); + let ty = it + .ty + .clone() + .substitute( + Interner, + ClosureSubst(&convert_args_for_result(interner, args.inner())) + .parent_subst(), + ) + .to_nextsolver(interner); db.layout_of_ty(ty, trait_env.clone()) }) .collect::, _>>()?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 7fdfb205721ec..c16bbb7b99221 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -124,7 +124,7 @@ pub use lower_nextsolver::associated_type_shorthand_candidates; pub use mapping::{ ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, - to_foreign_def_id, to_placeholder_idx, + to_foreign_def_id, to_placeholder_idx, to_placeholder_idx_no_index, }; pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; @@ -1007,7 +1007,7 @@ struct PlaceholderCollector<'db> { impl PlaceholderCollector<'_> { fn collect(&mut self, idx: PlaceholderIndex) { - let id = from_placeholder_idx(self.db, idx); + let id = from_placeholder_idx(self.db, idx).0; self.placeholders.insert(id); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 098c62ba97a29..79f78c545e595 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -340,7 +340,13 @@ impl<'a> TyLoweringContext<'a> { res = Some(TypeNs::GenericParam(type_param_id)); match self.type_param_mode { ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.db, type_param_id.into())) + let generics = self.generics(); + let idx = generics.type_or_const_param_idx(type_param_id.into()).unwrap(); + TyKind::Placeholder(to_placeholder_idx( + self.db, + type_param_id.into(), + idx as u32, + )) } ParamLoweringMode::Variable => { let idx = @@ -777,7 +783,9 @@ impl<'a> TyLoweringContext<'a> { LifetimeNs::Static => static_lifetime(), LifetimeNs::LifetimeParam(id) => match self.type_param_mode { ParamLoweringMode::Placeholder => { - LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) + let generics = self.generics(); + let idx = generics.lifetime_idx(id).unwrap(); + LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id, idx as u32)) } ParamLoweringMode::Variable => { let idx = match self.generics().lifetime_idx(id) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index fb85909d7f41e..b0132e4dcbc46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -222,7 +222,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) + let generics = self.ctx.generics(); + let idx = generics.type_or_const_param_idx(param_id.into()).unwrap(); + TyKind::Placeholder(to_placeholder_idx( + self.ctx.db, + param_id.into(), + idx as u32, + )) } ParamLoweringMode::Variable => { let idx = match self.ctx.generics().type_or_const_param_idx(param_id.into()) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index d87181f545d88..4578922ce3219 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -64,8 +64,7 @@ use crate::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, - TraitPredicate, TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, - mapping::ChalkToNextSolver, + TraitPredicate, TraitRef, Ty, Tys, abi::Safety, mapping::ChalkToNextSolver, }, }; @@ -322,6 +321,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }; Ty::new_param( self.interner, + type_param_id, idx as u32, type_data .name @@ -866,7 +866,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { None => return Region::error(self.interner), Some(idx) => idx, }; - Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 }) + Region::new_early_param( + self.interner, + EarlyParamRegion { index: idx as u32, id }, + ) } }, None => Region::error(self.interner), @@ -1344,11 +1347,11 @@ where { continue; } - let GenericParamDefKind::Type = p.kind else { + let GenericParamId::TypeParamId(param_id) = p.id else { continue; }; let idx = idx as u32 + generics.parent_count as u32; - let param_ty = Ty::new_param(interner, idx, p.name.clone()); + let param_ty = Ty::new_param(interner, param_id, idx, p.name.clone()); if explicitly_unsized_tys.contains(¶m_ty) { continue; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index e3efb38306408..ccdb5a0bdb4a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -281,6 +281,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; Ty::new_param( self.ctx.interner, + param_id, idx as u32, p.name .as_ref() @@ -758,6 +759,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { convert_binder_to_early_binder( self.ctx.ctx.interner, + def, default.to_nextsolver(self.ctx.ctx.interner), ) .instantiate(self.ctx.ctx.interner, preceding_args) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index 448fbdf673655..5125a38825cb8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -104,7 +104,10 @@ pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { FromId::from_id(id.0) } -pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId { +pub fn from_placeholder_idx( + db: &dyn HirDatabase, + idx: PlaceholderIndex, +) -> (TypeOrConstParamId, u32) { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. let interned_id = @@ -112,15 +115,32 @@ pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> Type interned_id.loc(db) } -pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> PlaceholderIndex { - let interned_id = InternedTypeOrConstParamId::new(db, id); +pub fn to_placeholder_idx( + db: &dyn HirDatabase, + id: TypeOrConstParamId, + idx: u32, +) -> PlaceholderIndex { + let interned_id = InternedTypeOrConstParamId::new(db, (id, idx)); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, idx: interned_id.as_id().index() as usize, } } -pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId { +pub fn to_placeholder_idx_no_index( + db: &dyn HirDatabase, + id: TypeOrConstParamId, +) -> PlaceholderIndex { + let index = crate::generics::generics(db, id.parent) + .type_or_const_param_idx(id) + .expect("param not found"); + to_placeholder_idx(db, id, index as u32) +} + +pub fn lt_from_placeholder_idx( + db: &dyn HirDatabase, + idx: PlaceholderIndex, +) -> (LifetimeParamId, u32) { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. let interned_id = @@ -128,8 +148,12 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L interned_id.loc(db) } -pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { - let interned_id = InternedLifetimeParamId::new(db, id); +pub fn lt_to_placeholder_idx( + db: &dyn HirDatabase, + id: LifetimeParamId, + idx: u32, +) -> PlaceholderIndex { + let interned_id = InternedLifetimeParamId::new(db, (id, idx)); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, idx: interned_id.as_id().index() as usize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index d8f443145ca06..b0b922316914b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -99,7 +99,7 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result, Self::Error> { - let it = from_placeholder_idx(self.db, idx); + let it = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; @@ -117,7 +117,7 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result { - let it = from_placeholder_idx(self.db, idx); + let it = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index ce581cfad4b26..cfafc65d18443 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -2,6 +2,7 @@ use std::hash::Hash; +use hir_def::{ConstParamId, TypeOrConstParamId}; use intern::{Interned, Symbol}; use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; @@ -84,6 +85,8 @@ pub type PlaceholderConst = Placeholder; #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct ParamConst { + // FIXME: See `ParamTy`. + pub id: ConstParamId, pub index: u32, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 76186e374608e..d284eb9c6b419 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -1,5 +1,6 @@ //! Things related to generic args in the next-trait-solver. +use hir_def::GenericParamId; use intern::{Interned, Symbol}; use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, @@ -14,7 +15,7 @@ use crate::db::HirDatabase; use super::{ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, - generics::{GenericParamDef, GenericParamDefKind, Generics}, + generics::{GenericParamDef, Generics}, interned_vec_db, }; @@ -203,7 +204,7 @@ impl<'db> GenericArgs<'db> { mut mk_kind: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defs = interner.generics_of(def_id); let count = defs.count(); @@ -218,7 +219,7 @@ impl<'db> GenericArgs<'db> { defs: Generics, mk_kind: &mut F, ) where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let self_len = defs.own_params.len() as u32; if let Some(def_id) = defs.parent { @@ -230,12 +231,12 @@ impl<'db> GenericArgs<'db> { fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let start_len = args.len(); args.reserve(defs.own_params.len()); for param in &defs.own_params { - let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args); + let kind = mk_kind(¶m.name, args.len() as u32, param.id, args); args.push(kind); } } @@ -412,26 +413,25 @@ pub fn mk_param<'db>( interner: DbInterner<'db>, index: u32, name: &Symbol, - kind: GenericParamDefKind, + id: GenericParamId, ) -> GenericArg<'db> { let name = name.clone(); - match kind { - GenericParamDefKind::Lifetime => { - Region::new_early_param(interner, EarlyParamRegion { index }).into() + match id { + GenericParamId::LifetimeParamId(id) => { + Region::new_early_param(interner, EarlyParamRegion { index, id }).into() + } + GenericParamId::TypeParamId(id) => Ty::new_param(interner, id, index, name).into(), + GenericParamId::ConstParamId(id) => { + Const::new_param(interner, ParamConst { index, id }).into() } - GenericParamDefKind::Type => Ty::new_param(interner, index, name).into(), - GenericParamDefKind::Const => Const::new_param(interner, ParamConst { index }).into(), } } -pub fn error_for_param_kind<'db>( - kind: GenericParamDefKind, - interner: DbInterner<'db>, -) -> GenericArg<'db> { - match kind { - GenericParamDefKind::Lifetime => Region::error(interner).into(), - GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(), - GenericParamDefKind::Const => Const::error(interner).into(), +pub fn error_for_param_kind<'db>(id: GenericParamId, interner: DbInterner<'db>) -> GenericArg<'db> { + match id { + GenericParamId::LifetimeParamId(_) => Region::error(interner).into(), + GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamId::ConstParamId(_) => Const::error(interner).into(), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index a3ba8eb83453e..5ec9a18a6c20e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -24,35 +24,40 @@ use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, Solver use super::{DbInterner, GenericArg}; pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { - let mk_lt = |index, lt: &LifetimeParamData| { + let mk_lt = |parent, index, local_id, lt: &LifetimeParamData| { let name = lt.name.symbol().clone(); - let kind = GenericParamDefKind::Lifetime; - GenericParamDef { name, index, kind } + let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }); + GenericParamDef { name, index, id } }; - let mk_ty = |index, p: &TypeOrConstParamData| { + let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| { let name = p.name().map(|n| n.symbol().clone()).unwrap_or_else(|| sym::MISSING_NAME); - let kind = match p { - TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, - TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, + let id = TypeOrConstParamId { parent, local_id }; + let id = match p { + TypeOrConstParamData::TypeParamData(_) => { + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)) + } + TypeOrConstParamData::ConstParamData(_) => { + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)) + } }; - GenericParamDef { name, index, kind } + GenericParamDef { name, index, id } }; - let own_params_for_generic_params = |params: &GenericParams| { + let own_params_for_generic_params = |parent, params: &GenericParams| { let mut result = Vec::with_capacity(params.len()); let mut type_and_consts = params.iter_type_or_consts(); let mut index = 0; if let Some(self_param) = params.trait_self_param() { - result.push(mk_ty(0, ¶ms[self_param])); + result.push(mk_ty(parent, 0, self_param, ¶ms[self_param])); type_and_consts.next(); index += 1; } - result.extend(params.iter_lt().map(|(_, data)| { - let lt = mk_lt(index, data); + result.extend(params.iter_lt().map(|(local_id, data)| { + let lt = mk_lt(parent, index, local_id, data); index += 1; lt })); - result.extend(type_and_consts.map(|(_, data)| { - let ty = mk_ty(index, data); + result.extend(type_and_consts.map(|(local_id, data)| { + let ty = mk_ty(parent, index, local_id, data); index += 1; ty })); @@ -60,9 +65,10 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { }; let (parent, own_params) = match (def.try_into(), def) { - (Ok(def), _) => { - (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(def))) - } + (Ok(def), _) => ( + parent_generic_def(db, def), + own_params_for_generic_params(def, &db.generic_params(def)), + ), (_, SolverDefId::InternedOpaqueTyId(id)) => { match db.lookup_intern_impl_trait_id(id) { crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { @@ -79,7 +85,19 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { provenance: TypeParamProvenance::TypeParamList, }); // Yes, there is a parent but we don't include it in the generics - (None, vec![mk_ty(0, ¶m)]) + // FIXME: It seems utterly sensitive to fake a generic param here. + // Also, what a horrible mess! + ( + None, + vec![mk_ty( + GenericDefId::FunctionId(salsa::plumbing::FromId::from_id(unsafe { + salsa::Id::from_index(salsa::Id::MAX_U32 - 1) + })), + 0, + LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)), + ¶m, + )], + ) } } } @@ -106,7 +124,7 @@ pub struct GenericParamDef { pub(crate) name: Symbol, //def_id: GenericDefId, index: u32, - pub(crate) kind: GenericParamDefKind, + pub(crate) id: GenericParamId, } impl GenericParamDef { @@ -117,13 +135,6 @@ impl GenericParamDef { } } -#[derive(Copy, Clone, Debug)] -pub enum GenericParamDefKind { - Lifetime, - Type, - Const, -} - impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { fn count(&self) -> usize { self.parent_count + self.own_params.len() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 19ae76f7e358f..58832aef89529 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -8,6 +8,7 @@ pub use BoundRegionConversionTime::*; pub use at::DefineOpaqueTypes; use ena::undo_log::UndoLogs; use ena::unify as ut; +use hir_def::GenericParamId; use intern::Symbol; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{ @@ -38,7 +39,7 @@ use crate::next_solver::fold::BoundVarReplacerDelegate; use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; -use super::generics::{GenericParamDef, GenericParamDefKind}; +use super::generics::GenericParamDef; use super::{ AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, @@ -600,14 +601,14 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(universe) } - fn var_for_def(&self, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> { - match kind { - GenericParamDefKind::Lifetime => { + fn var_for_def(&self, id: GenericParamId, name: &Symbol) -> GenericArg<'db> { + match id { + GenericParamId::LifetimeParamId(_) => { // Create a region inference variable for the given // region parameter definition. self.next_region_var().into() } - GenericParamDefKind::Type => { + GenericParamId::TypeParamId(_) => { // Create a type inference variable for the given // type parameter definition. The generic parameters are // for actual parameters that may be referred to by @@ -624,7 +625,7 @@ impl<'db> InferCtxt<'db> { Ty::new_var(self.interner, ty_var_id).into() } - GenericParamDefKind::Const => { + GenericParamId::ConstParamId(_) => { let origin = ConstVariableOrigin { param_def_id: None }; let const_var_id = self .inner diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 203f030dfd614..6755d065e10d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -9,6 +9,7 @@ use hir_def::{ CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, TypeOrConstParamId, TypeParamId, signatures::TraitFlags, }; +use hir_def::{GenericDefId, GenericParamId}; use intern::sym; use rustc_type_ir::{ AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, @@ -35,6 +36,9 @@ use crate::{ }, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, }; +use crate::{ + from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_placeholder_idx, +}; use super::{ BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, @@ -44,47 +48,24 @@ use super::{ Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, }; -pub fn to_placeholder_idx( - db: &dyn HirDatabase, - id: TypeOrConstParamId, - map: impl Fn(BoundVar) -> T, -) -> Placeholder { - let interned_id = InternedTypeOrConstParamId::new(db, id); - Placeholder { - universe: UniverseIndex::ZERO, - bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)), - } -} - -pub fn bound_var_to_type_or_const_param_idx( - db: &dyn HirDatabase, - var: rustc_type_ir::BoundVar, -) -> TypeOrConstParamId { - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); - interned_id.loc(db) -} - -pub fn bound_var_to_lifetime_idx( - db: &dyn HirDatabase, - var: rustc_type_ir::BoundVar, -) -> LifetimeParamId { - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = InternedLifetimeParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); - interned_id.loc(db) -} - +// FIXME: This should urgently go (as soon as we finish the migration off Chalk, that is). pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( interner: DbInterner<'db>, + def: GenericDefId, binder: rustc_type_ir::Binder, T>, ) -> rustc_type_ir::EarlyBinder, T> { - let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO }; + let mut folder = BinderToEarlyBinder { + interner, + debruijn: rustc_type_ir::DebruijnIndex::ZERO, + params: crate::generics::generics(interner.db, def).iter_id().collect(), + }; rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) } struct BinderToEarlyBinder<'db> { interner: DbInterner<'db>, debruijn: rustc_type_ir::DebruijnIndex, + params: Vec, } impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db> { @@ -109,7 +90,13 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db match t.kind() { rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { let var: rustc_type_ir::BoundVar = bound_ty.var(); - Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() })) + let GenericParamId::TypeParamId(id) = self.params[bound_ty.var.as_usize()] else { + unreachable!() + }; + Ty::new( + self.cx(), + rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32(), id }), + ) } _ => t.super_fold_with(self), } @@ -119,10 +106,15 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db match r.kind() { rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { let var: rustc_type_ir::BoundVar = bound_region.var(); + let GenericParamId::LifetimeParamId(id) = self.params[bound_region.var.as_usize()] + else { + unreachable!() + }; Region::new( self.cx(), rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { index: var.as_u32(), + id, }), ) } @@ -133,9 +125,12 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { match c.kind() { rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { + let GenericParamId::ConstParamId(id) = self.params[var.as_usize()] else { + unreachable!() + }; Const::new( self.cx(), - rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32(), id }), ) } _ => c.super_fold_with(self), @@ -274,12 +269,6 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), - chalk_ir::TyKind::Placeholder(placeholder_index) => { - rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon( - placeholder_index.ui.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), - )) - } chalk_ir::TyKind::Dyn(dyn_ty) => { // exists { for<...> ^1.0: ... } let bounds = BoundExistentialPredicates::new_from_iter( @@ -405,6 +394,26 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { rustc_type_ir::TyKind::FnPtr(sig_tys, header) } + // The schema here is quite confusing. + // The new solver, like rustc, uses `Param` and `EarlyBinder` for generic params. It uses `BoundVar` + // and `Placeholder` together with `Binder` for HRTB, which we mostly don't handle. + // Chalk uses `Placeholder` for generic params and `BoundVar` quite liberally, and this is quite a + // problem. `chalk_ir::TyKind::BoundVar` can represent either HRTB or generic params, depending on the + // context. When returned from signature queries, the outer `Binders` represent the generic params. + // But there are also inner `Binders` for HRTB. + // AFAIK there is no way to tell which of the meanings is relevant, so we just use `rustc_type_ir::Bound` + // here, and hope for the best. If you are working with new solver types, therefore, use the new solver + // lower queries. + // Hopefully sooner than later Chalk will be ripped from the codebase and we can avoid that problem. + // For details about the rustc setup, read: https://rustc-dev-guide.rust-lang.org/generic_parameters_summary.html + // and the following chapters. + chalk_ir::TyKind::Placeholder(placeholder_index) => { + let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::TyKind::Param(ParamTy { + id: TypeParamId::from_unchecked(id), + index, + }) + } chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( bound_var.debruijn.to_nextsolver(interner), BoundTy { @@ -440,10 +449,8 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { )) } chalk_ir::LifetimeData::Placeholder(placeholder_index) => { - rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon( - rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32), - rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32), - )) + let (id, index) = lt_from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { id, index }) } chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, @@ -474,10 +481,11 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { )) } chalk_ir::ConstValue::Placeholder(placeholder_index) => { - rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new( - placeholder_index.ui.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), - )) + let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::ConstKind::Param(ParamConst { + id: ConstParamId::from_unchecked(id), + index, + }) } chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { ConstScalar::Bytes(bytes, memory) => { @@ -971,7 +979,7 @@ pub fn convert_args_for_result<'db>( substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); } rustc_type_ir::GenericArgKind::Lifetime(region) => { - let lifetime = convert_region_for_result(region); + let lifetime = convert_region_for_result(interner, region); substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); } rustc_type_ir::GenericArgKind::Const(const_) => { @@ -1074,7 +1082,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let r = convert_region_for_result(r); + let r = convert_region_for_result(interner, r); let ty = convert_ty_for_result(interner, ty); TyKind::Ref(mutability, r, ty) } @@ -1122,17 +1130,23 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::AliasTyKind::Free => unimplemented!(), }, + // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. rustc_type_ir::TyKind::Placeholder(placeholder) => { - let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; - let placeholder_index = - chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; - TyKind::Placeholder(placeholder_index) + unimplemented!( + "A `rustc_type_ir::TyKind::Placeholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::TyKind::Bound` here." + ) } - rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), index: ty.var.as_usize(), }), + rustc_type_ir::TyKind::Param(param) => { + let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); + TyKind::Placeholder(placeholder) + } rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { let num_binders = bound_sig.bound_vars().len(); @@ -1254,7 +1268,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), ); let bounds = chalk_ir::Binders::new(binders, bounds); - let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) }; + let dyn_ty = + chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(interner, region) }; TyKind::Dyn(dyn_ty) } @@ -1316,7 +1331,6 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::CoroutineWitness(id.into(), subst) } - rustc_type_ir::TyKind::Param(_) => unimplemented!(), rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), } .intern(Interner) @@ -1327,13 +1341,16 @@ pub fn convert_const_for_result<'db>( const_: Const<'db>, ) -> crate::Const { let value: chalk_ir::ConstValue = match const_.kind() { - rustc_type_ir::ConstKind::Param(_) => unimplemented!(), rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) } rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { panic!("Vars should not be freshened.") } + rustc_type_ir::ConstKind::Param(param) => { + let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); + chalk_ir::ConstValue::Placeholder(placeholder) + } rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), @@ -1341,10 +1358,12 @@ pub fn convert_const_for_result<'db>( )) } rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { - chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, - idx: placeholder_const.bound.as_usize(), - }) + unimplemented!( + "A `rustc_type_ir::ConstKind::Placeholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::ConstKind::Bound` here." + ) } rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { let id = match unevaluated_const.def { @@ -1381,36 +1400,34 @@ pub fn convert_const_for_result<'db>( chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) } -pub fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { - match region.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), - rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( - Interner, +pub fn convert_region_for_result<'db>( + interner: DbInterner<'db>, + region: Region<'db>, +) -> crate::Lifetime { + let lifetime = match region.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => { + let placeholder = lt_to_placeholder_idx(interner.db, early.id, early.index); + chalk_ir::LifetimeData::Placeholder(placeholder) + } + rustc_type_ir::RegionKind::ReBound(db, bound) => { chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), bound.var.as_usize(), - )), - ), - rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), - rustc_type_ir::RegionKind::ReStatic => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + )) } - rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), - ), - rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { - idx: placeholder.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, - }), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => unimplemented!( + "A `rustc_type_ir::RegionKind::RePlaceholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::RegionKind::ReBound` here." ), - rustc_type_ir::RegionKind::ReErased => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) - } - rustc_type_ir::RegionKind::ReError(_) => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), + rustc_type_ir::RegionKind::ReStatic => chalk_ir::LifetimeData::Static, + rustc_type_ir::RegionKind::ReVar(vid) => { + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())) } - } + rustc_type_ir::RegionKind::ReErased => chalk_ir::LifetimeData::Erased, + rustc_type_ir::RegionKind::ReError(_) => chalk_ir::LifetimeData::Error, + }; + chalk_ir::Lifetime::new(Interner, lifetime) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index c59cdac5f99f6..d6214d991560a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -1,5 +1,6 @@ //! Things related to regions. +use hir_def::LifetimeParamId; use intern::{Interned, Symbol}; use rustc_type_ir::{ BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, @@ -110,6 +111,8 @@ pub type PlaceholderRegion = Placeholder; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct EarlyParamRegion { + // FIXME: See `ParamTy`. + pub id: LifetimeParamId, pub index: u32, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 5ffae981a6094..92f1076e75eca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1,5 +1,6 @@ //! Things related to tys in the next-trait-solver. +use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; @@ -64,8 +65,8 @@ impl<'db> Ty<'db> { .unwrap() } - pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self { - Ty::new(interner, TyKind::Param(ParamTy { index })) + pub fn new_param(interner: DbInterner<'db>, id: TypeParamId, index: u32, name: Symbol) -> Self { + Ty::new(interner, TyKind::Param(ParamTy { id, index })) } pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { @@ -847,12 +848,16 @@ pub type PlaceholderTy = Placeholder; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ParamTy { + // FIXME: I'm not pleased with this. Ideally a `Param` should only know its index - the defining item + // is known from the `EarlyBinder`. This should also be beneficial for memory usage. But code currently + // assumes it can get the definition from `Param` alone - so that's what we got. + pub id: TypeParamId, pub index: u32, } impl ParamTy { pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { - Ty::new_param(interner, self.index, sym::MISSING_NAME.clone()) + Ty::new_param(interner, self.id, self.index, sym::MISSING_NAME.clone()) } } @@ -865,6 +870,7 @@ impl std::fmt::Debug for ParamTy { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct BoundTy { pub var: BoundVar, + // FIXME: This is for diagnostics in rustc, do we really need it? pub kind: BoundTyKind, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index d8a058533f761..a1ebff04bbd47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -344,7 +344,7 @@ impl Context<'_> { // Chalk has no params, so use placeholders for now? TyKind::Placeholder(index) => { - let idx = crate::from_placeholder_idx(self.db, *index); + let idx = crate::from_placeholder_idx(self.db, *index).0; let index = self.generics.type_or_const_param_idx(idx).unwrap(); self.constrain(index, variance); } @@ -445,7 +445,7 @@ impl Context<'_> { ); match region.data(Interner) { LifetimeData::Placeholder(index) => { - let idx = crate::lt_from_placeholder_idx(self.db, *index); + let idx = crate::lt_from_placeholder_idx(self.db, *index).0; let inferred = self.generics.lifetime_idx(idx).unwrap(); self.constrain(inferred, variance); } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index b9c5131ffbb55..833a9ef03065d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -474,8 +474,8 @@ impl HirDisplay for TypeParam { let param_data = ¶ms[self.id.local_id()]; let substs = TyBuilder::placeholder_subst(f.db, self.id.parent()); let krate = self.id.parent().krate(f.db).id; - let ty = - TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())).intern(Interner); + let ty = TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index(f.db, self.id.into())) + .intern(Interner); let predicates = f.db.generic_predicates(self.id.parent()); let predicates = predicates .iter() @@ -528,8 +528,11 @@ impl HirDisplay for TypeParam { f, ":", Either::Left( - &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())) - .intern(Interner), + &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index( + f.db, + self.id.into(), + )) + .intern(Interner), ), &predicates, default_sized, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f03f542e5bce3..46d3a2bcd67ce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4239,8 +4239,8 @@ impl TypeParam { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.parent().resolver(db); - let ty = - TyKind::Placeholder(hir_ty::to_placeholder_idx(db, self.id.into())).intern(Interner); + let ty = TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index(db, self.id.into())) + .intern(Interner); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -5933,7 +5933,7 @@ impl<'db> Type<'db> { pub fn as_type_param(&self, db: &'db dyn HirDatabase) -> Option { match self.ty.kind(Interner) { TyKind::Placeholder(p) => Some(TypeParam { - id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p)), + id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p).0), }), _ => None, } From dd75ad436927ac30bf204953d24b07bff09225bd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 2 Sep 2025 08:15:17 +0300 Subject: [PATCH 0188/1900] Deduplicate methods in completion by function ID and not by name Because duplicates can be found with traits. Worse, the inherent methods could be private, and we'll discover that only later. But even if they're not they're different methods, and its seems worthy to present them all to the user. --- .../ide-completion/src/completions/dot.rs | 15 ++++--- .../ide-completion/src/tests/expression.rs | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 129964f8387a3..b9ef68cc2d2a3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use hir::{Complete, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -237,7 +237,10 @@ fn complete_methods( struct Callback<'a, F> { ctx: &'a CompletionContext<'a>, f: F, - seen_methods: FxHashSet, + // We deliberately deduplicate by function ID and not name, because while inherent methods cannot be + // duplicated, trait methods can. And it is still useful to show all of them (even when there + // is also an inherent method, especially considering that it may be private, and filtered later). + seen_methods: FxHashSet, } impl MethodCandidateCallback for Callback<'_, F> @@ -247,9 +250,7 @@ fn complete_methods( // We don't want to exclude inherent trait methods - that is, methods of traits available from // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } ControlFlow::Continue(()) @@ -265,9 +266,7 @@ fn complete_methods( return ControlFlow::Continue(()); } - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7a0d00444129f..af6ef34761ed1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2632,3 +2632,43 @@ fn let_in_condition() { fn let_in_let_chain() { check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#); } + +#[test] +fn private_inherent_and_public_trait() { + check( + r#" +struct Foo; + +mod private { + impl super::Foo { + fn method(&self) {} + } +} + +trait Trait { + fn method(&self) {} +} +impl Trait for Foo {} + +fn main() { + Foo.$0 +} + "#, + expect![[r#" + me method() (as Trait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} From 18fa6d917cb39758429d47015fc83e72445e2199 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 2 Sep 2025 10:48:42 +0200 Subject: [PATCH 0189/1900] Remove some llvm workarounds --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 8 ++------ library/stdarch/crates/core_arch/src/x86_64/avx512f.rs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 0803f2f81b215..52c6a11a43f0e 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -49621,9 +49621,7 @@ mod tests { assert_eq_m512i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_mask_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, @@ -49636,9 +49634,7 @@ mod tests { assert_eq_m512i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_maskz_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index a8dd340bd6605..934c9e2812c42 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -5311,9 +5311,7 @@ mod tests { assert_eq_m256i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_mask_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let src = _mm256_set1_epi32(0); @@ -5324,9 +5322,7 @@ mod tests { assert_eq_m256i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_maskz_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let r = _mm512_maskz_cvttpd_epu32(0, a); From bb3598e481a752b85872fcbea9dab805907c2d81 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 2 Sep 2025 10:52:49 +0200 Subject: [PATCH 0190/1900] use `qemu-user` instead of `qemu-user-static` for loongarch CI --- .../ci/docker/loongarch64-unknown-linux-gnu/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index b5c6874ca52ed..e803dae1006a0 100644 --- a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -2,11 +2,11 @@ FROM ubuntu:25.10 RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-loongarch64-linux-gnu libc6-dev-loong64-cross ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc \ - CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64-static -cpu max -L /usr/loongarch64-linux-gnu" \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64 -cpu max -L /usr/loongarch64-linux-gnu" \ OBJDUMP=loongarch64-linux-gnu-objdump \ STDARCH_TEST_SKIP_FEATURE=frecipe From 4f0e9e29e344975ddf9ebaf5bb8e71a0a433023e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 3 Sep 2025 01:18:47 +0800 Subject: [PATCH 0191/1900] Fix closure in match not applicable for add_braces Example --- **Not applicable**: ```rust fn foo() { match () { () => { t(|n|$0 n + 100); } } } ``` --- .../ide-assists/src/handlers/add_braces.rs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index 745ae67f30959..5af622eaf28b0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ AstNode, ast::{self, edit_in_place::Indent, syntax_factory::SyntaxFactory}, @@ -59,7 +60,8 @@ enum ParentType { } fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> { - if let Some(match_arm) = ctx.find_node_at_offset::() { + let node = ctx.find_node_at_offset::>()?; + if let Either::Left(match_arm) = &node { let match_arm_expr = match_arm.expr()?; if matches!(match_arm_expr, ast::Expr::BlockExpr(_)) { @@ -67,7 +69,7 @@ fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Exp } return Some((ParentType::MatchArmExpr, match_arm_expr)); - } else if let Some(closure_expr) = ctx.find_node_at_offset::() { + } else if let Either::Right(closure_expr) = &node { let body = closure_expr.body()?; if matches!(body, ast::Expr::BlockExpr(_)) { @@ -105,6 +107,33 @@ fn foo() { ); } + #[test] + fn suggest_add_braces_for_closure_in_match() { + check_assist( + add_braces, + r#" +fn foo() { + match () { + () => { + t(|n|$0 n + 100); + } + } +} +"#, + r#" +fn foo() { + match () { + () => { + t(|n| { + n + 100 + }); + } + } +} +"#, + ); + } + #[test] fn no_assist_for_closures_with_braces() { check_assist_not_applicable( From 46795337322b5852438417549a5fe9dce3f75479 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sat, 30 Aug 2025 05:05:12 +0000 Subject: [PATCH 0192/1900] RISC-V: Lower requirements of `clmul` and `clmulh` They don't need full "Zbc" extension but only its subset: the "Zbkc" extension. Since the compiler implies `zbkc` from `zbc`, it's safe to use `#[target_feature(enable = "zbkc")]`. --- library/stdarch/crates/core_arch/src/riscv_shared/zb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs b/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs index 9472e3c8be9f6..514afd9080920 100644 --- a/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs +++ b/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs @@ -68,7 +68,7 @@ pub fn orc_b(rs: usize) -> usize { /// /// Section: 2.11 #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] -#[target_feature(enable = "zbc")] +#[target_feature(enable = "zbkc")] #[cfg_attr(test, assert_instr(clmul))] #[inline] pub fn clmul(rs1: usize, rs2: usize) -> usize { @@ -93,7 +93,7 @@ pub fn clmul(rs1: usize, rs2: usize) -> usize { /// /// Section: 2.12 #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] -#[target_feature(enable = "zbc")] +#[target_feature(enable = "zbkc")] #[cfg_attr(test, assert_instr(clmulh))] #[inline] pub fn clmulh(rs1: usize, rs2: usize) -> usize { From 5b71113a64d576abd6a14c8d7201ff1164a6c44e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Sep 2025 11:40:46 +0200 Subject: [PATCH 0193/1900] re-balance CI --- src/tools/miri/ci/ci.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index b66530e77b8a8..224ca0ae29948 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -164,12 +164,15 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI MANY_SEEDS=16 TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice - # Custom target JSON file - TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std + # Not officially supported tier 2 + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests + MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests ;; armv7-unknown-linux-gnueabihf) # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + # Custom target JSON file + TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std ;; aarch64-apple-darwin) # Host @@ -181,8 +184,6 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests - MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests ;; i686-pc-windows-msvc) # Host From a880c46f198e9601e70cac9286154286906699ce Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Sep 2025 11:47:30 +0200 Subject: [PATCH 0194/1900] no need to run GC_STRESS more than once for each OS --- src/tools/miri/ci/ci.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 224ca0ae29948..6356e33f61e84 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -137,6 +137,7 @@ function run_tests_minimal { # In particular, fully cover all tier 1 targets. # We also want to run the many-seeds tests on all tier 1 targets. +# We run GC_STRESS only once for each tier 1 OS. case $HOST_TARGET in x86_64-unknown-linux-gnu) # Host @@ -147,7 +148,6 @@ case $HOST_TARGET in ;; i686-unknown-linux-gnu) # Host - # Without GC_STRESS as this is a slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator @@ -159,7 +159,7 @@ case $HOST_TARGET in ;; aarch64-unknown-linux-gnu) # Host - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 2 MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI @@ -170,7 +170,7 @@ case $HOST_TARGET in ;; armv7-unknown-linux-gnueabihf) # Host - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Custom target JSON file TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std ;; @@ -187,7 +187,6 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # Without GC_STRESS as this is a very slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, @@ -196,8 +195,7 @@ case $HOST_TARGET in ;; aarch64-pc-windows-msvc) # Host - # Without GC_STRESS as this is a very slow runner. - MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests ;; From d366f26421125e11bfcec5e4a9f202813cbd3229 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 14 Apr 2025 08:53:50 +0200 Subject: [PATCH 0195/1900] Extract address generator struct from memory allocator. --- .../src/alloc_addresses/address_generator.rs | 78 +++++++++++++++++++ src/tools/miri/src/alloc_addresses/mod.rs | 65 ++++------------ src/tools/miri/src/machine.rs | 4 +- 3 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 src/tools/miri/src/alloc_addresses/address_generator.rs diff --git a/src/tools/miri/src/alloc_addresses/address_generator.rs b/src/tools/miri/src/alloc_addresses/address_generator.rs new file mode 100644 index 0000000000000..3764f5dcb8244 --- /dev/null +++ b/src/tools/miri/src/alloc_addresses/address_generator.rs @@ -0,0 +1,78 @@ +use std::ops::Range; + +use rand::Rng; +use rustc_abi::{Align, Size}; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::{err_exhaust, throw_exhaust}; + +/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple +/// of `align` that is larger or equal to `addr` +fn align_addr(addr: u64, align: u64) -> u64 { + match addr % align { + 0 => addr, + rem => addr.strict_add(align) - rem, + } +} + +/// This provides the logic to generate addresses for memory allocations in a given address range. +#[derive(Debug)] +pub struct AddressGenerator { + /// This is used as a memory address when a new pointer is casted to an integer. It + /// is always larger than any address that was previously made part of a block. + next_base_addr: u64, + /// This is the last address that can be allocated. + end: u64, +} + +impl AddressGenerator { + pub fn new(addr_range: Range) -> Self { + Self { next_base_addr: addr_range.start, end: addr_range.end } + } + + /// Get the remaining range where this `AddressGenerator` can still allocate addresses. + pub fn get_remaining(&self) -> Range { + self.next_base_addr..self.end + } + + /// Generate a new address with the specified size and alignment, using the given Rng to add some randomness. + /// The returned allocation is guaranteed not to overlap with any address ranges given out by the generator before. + /// Returns an error if the allocation request cannot be fulfilled. + pub fn generate<'tcx, R: Rng>( + &mut self, + size: Size, + align: Align, + rng: &mut R, + ) -> InterpResult<'tcx, u64> { + // Leave some space to the previous allocation, to give it some chance to be less aligned. + // We ensure that `(self.next_base_addr + slack) % 16` is uniformly distributed. + let slack = rng.random_range(0..16); + // From next_base_addr + slack, round up to adjust for alignment. + let base_addr = + self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + let base_addr = align_addr(base_addr, align.bytes()); + + // Remember next base address. If this allocation is zero-sized, leave a gap of at + // least 1 to avoid two allocations having the same base address. (The logic in + // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers + // need to be distinguishable!) + self.next_base_addr = base_addr + .checked_add(size.bytes().max(1)) + .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + // Even if `Size` didn't overflow, we might still have filled up the address space. + if self.next_base_addr > self.end { + throw_exhaust!(AddressSpaceFull); + } + interp_ok(base_addr) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_align_addr() { + assert_eq!(align_addr(37, 4), 40); + assert_eq!(align_addr(44, 4), 44); + } +} diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 334503d2994b1..afd8afbaeab07 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -1,15 +1,16 @@ //! This module is responsible for managing the absolute addresses that allocations are located at, //! and for casting between pointers and integers based on those addresses. +mod address_generator; mod reuse_pool; use std::cell::RefCell; -use std::cmp::max; -use rand::Rng; use rustc_abi::{Align, Size}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::ty::TyCtxt; +pub use self::address_generator::AddressGenerator; use self::reuse_pool::ReusePool; use crate::concurrency::VClock; use crate::*; @@ -49,9 +50,8 @@ pub struct GlobalStateInner { /// Whether an allocation has been exposed or not. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// This is used as a memory address when a new pointer is casted to an integer. It - /// is always larger than any address that was previously made part of a block. - next_base_addr: u64, + /// The generator for new addresses in a given range. + address_generator: AddressGenerator, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, } @@ -64,7 +64,7 @@ impl VisitProvenance for GlobalStateInner { prepared_alloc_bytes: _, reuse: _, exposed: _, - next_base_addr: _, + address_generator: _, provenance_mode: _, } = self; // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. @@ -77,14 +77,14 @@ impl VisitProvenance for GlobalStateInner { } impl GlobalStateInner { - pub fn new(config: &MiriConfig, stack_addr: u64) -> Self { + pub fn new<'tcx>(config: &MiriConfig, stack_addr: u64, tcx: TyCtxt<'tcx>) -> Self { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), prepared_alloc_bytes: FxHashMap::default(), reuse: ReusePool::new(config), exposed: FxHashSet::default(), - next_base_addr: stack_addr, + address_generator: AddressGenerator::new(stack_addr..tcx.target_usize_max()), provenance_mode: config.provenance_mode, } } @@ -96,15 +96,6 @@ impl GlobalStateInner { } } -/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple -/// of `align` that is larger or equal to `addr` -fn align_addr(addr: u64, align: u64) -> u64 { - match addr % align { - 0 => addr, - rem => addr.strict_add(align) - rem, - } -} - impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn addr_from_alloc_id_uncached( @@ -194,34 +185,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(reuse_addr) } else { // We have to pick a fresh address. - // Leave some space to the previous allocation, to give it some chance to be less aligned. - // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - let slack = rng.random_range(0..16); - // From next_base_addr + slack, round up to adjust for alignment. - let base_addr = global_state - .next_base_addr - .checked_add(slack) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - let base_addr = align_addr(base_addr, info.align.bytes()); - - // Remember next base address. If this allocation is zero-sized, leave a gap of at - // least 1 to avoid two allocations having the same base address. (The logic in - // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers - // need to be distinguishable!) - global_state.next_base_addr = base_addr - .checked_add(max(info.size.bytes(), 1)) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - // Even if `Size` didn't overflow, we might still have filled up the address space. - if global_state.next_base_addr > this.target_usize_max() { - throw_exhaust!(AddressSpaceFull); - } + let new_addr = + global_state.address_generator.generate(info.size, info.align, &mut rng)?; + // If we filled up more than half the address space, start aggressively reusing // addresses to avoid running out. - if global_state.next_base_addr > u64::try_from(this.target_isize_max()).unwrap() { + let remaining_range = global_state.address_generator.get_remaining(); + if remaining_range.start > remaining_range.end / 2 { global_state.reuse.address_space_shortage(); } - interp_ok(base_addr) + interp_ok(new_addr) } } } @@ -519,14 +493,3 @@ impl<'tcx> MiriMachine<'tcx> { }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_align_addr() { - assert_eq!(align_addr(37, 4), 40); - assert_eq!(align_addr(44, 4), 44); - } -} diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 8f0814a070cbf..9482769a2fc79 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -674,11 +674,13 @@ impl<'tcx> MiriMachine<'tcx> { thread_cpu_affinity .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus)); } + let alloc_addresses = + RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx)); MiriMachine { tcx, borrow_tracker, data_race, - alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)), + alloc_addresses, // `env_vars` depends on a full interpreter so we cannot properly initialize it yet. env_vars: EnvVars::default(), main_fn_ret_place: None, From c1be740564dd4a3aaafca93539ca428223226278 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 14 Apr 2025 08:53:50 +0200 Subject: [PATCH 0196/1900] Implement basic support for running Miri with GenMC. - Implement memory allocation compatible with GenMC. - Extract address generator struct from Miri's allocator. - Support thread creation and joining. - Support atomic load and store. - Support scheduling through GenMC. - Add tests for GenMC mode. - Add clang-format file for C++ code in Miri. - Update genmc-sys crate license to MIT/Apache to match GenMC dependency. - Add documentation for GenMC mode. Note: this commit depends on changes to GenMC not yet upstreamed to its official repository. Co-authored-by: Ralf Jung --- src/tools/miri/Cargo.lock | 24 +- src/tools/miri/Cargo.toml | 2 +- src/tools/miri/doc/genmc.md | 28 +- src/tools/miri/genmc-sys/.clang-format | 52 ++ src/tools/miri/genmc-sys/Cargo.toml | 8 +- src/tools/miri/genmc-sys/build.rs | 42 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 274 +++++++ .../genmc-sys/cpp/include/ResultHandling.hpp | 21 + .../cpp/src/MiriInterface/EventHandling.cpp | 120 +++ .../cpp/src/MiriInterface/Exploration.cpp | 42 + .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 185 +++++ .../src/MiriInterface/ThreadManagement.cpp | 54 ++ src/tools/miri/genmc-sys/src/lib.rs | 297 ++++++- .../miri/genmc-sys/src_cpp/MiriInterface.cpp | 50 -- .../miri/genmc-sys/src_cpp/MiriInterface.hpp | 44 - src/tools/miri/src/alloc_addresses/mod.rs | 19 +- src/tools/miri/src/bin/miri.rs | 22 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 1 + src/tools/miri/src/concurrency/data_race.rs | 69 +- .../miri/src/concurrency/genmc/config.rs | 19 +- src/tools/miri/src/concurrency/genmc/dummy.rs | 120 +-- .../concurrency/genmc/global_allocations.rs | 118 +++ .../miri/src/concurrency/genmc/helper.rs | 97 +++ src/tools/miri/src/concurrency/genmc/mod.rs | 762 ++++++++++++++---- src/tools/miri/src/concurrency/genmc/run.rs | 71 ++ .../miri/src/concurrency/genmc/scheduling.rs | 69 ++ .../src/concurrency/genmc/thread_id_map.rs | 63 ++ src/tools/miri/src/concurrency/init_once.rs | 4 +- src/tools/miri/src/concurrency/mod.rs | 2 +- src/tools/miri/src/concurrency/thread.rs | 53 +- src/tools/miri/src/diagnostics.rs | 17 +- src/tools/miri/src/eval.rs | 30 +- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/machine.rs | 7 +- src/tools/miri/src/shims/foreign_items.rs | 11 +- .../miri/src/shims/unix/linux_like/epoll.rs | 2 +- .../miri/src/shims/unix/linux_like/eventfd.rs | 2 +- .../miri/src/shims/unix/unnamed_socket.rs | 2 +- src/tools/miri/src/shims/windows/sync.rs | 2 +- .../data_race/weak_orderings.rel_rlx.stderr | 19 + .../data_race/weak_orderings.rlx_acq.stderr | 19 + .../data_race/weak_orderings.rlx_rlx.stderr | 19 + .../genmc/fail/data_race/weak_orderings.rs | 40 + .../fail/simple/2w2w_weak.relaxed4.stderr | 15 + .../fail/simple/2w2w_weak.release4.stderr | 15 + .../miri/tests/genmc/fail/simple/2w2w_weak.rs | 73 ++ .../fail/simple/2w2w_weak.sc3_rel1.stderr | 15 + .../miri/tests/genmc/pass/litmus/2cowr.rs | 51 ++ .../miri/tests/genmc/pass/litmus/2cowr.stderr | 2 + .../pass/litmus/2w2w_3sc_1rel.release1.stderr | 2 + .../pass/litmus/2w2w_3sc_1rel.release2.stderr | 2 + .../tests/genmc/pass/litmus/2w2w_3sc_1rel.rs | 54 ++ .../miri/tests/genmc/pass/litmus/2w2w_4rel.rs | 45 ++ .../tests/genmc/pass/litmus/2w2w_4rel.stderr | 2 + .../miri/tests/genmc/pass/litmus/2w2w_4sc.rs | 45 ++ .../tests/genmc/pass/litmus/2w2w_4sc.stderr | 2 + .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 64 ++ .../genmc/pass/litmus/IRIW-acq-sc.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/LB.rs | 47 ++ .../miri/tests/genmc/pass/litmus/LB.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/MP.rs | 47 ++ .../miri/tests/genmc/pass/litmus/MP.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/SB.rs | 47 ++ .../miri/tests/genmc/pass/litmus/SB.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr.rs | 45 ++ .../miri/tests/genmc/pass/litmus/corr.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr0.rs | 46 ++ .../miri/tests/genmc/pass/litmus/corr0.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr1.rs | 58 ++ .../miri/tests/genmc/pass/litmus/corr1.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr2.rs | 70 ++ .../miri/tests/genmc/pass/litmus/corr2.stderr | 2 + .../miri/tests/genmc/pass/litmus/corw.rs | 43 + .../miri/tests/genmc/pass/litmus/corw.stderr | 2 + .../miri/tests/genmc/pass/litmus/cowr.rs | 40 + .../miri/tests/genmc/pass/litmus/cowr.stderr | 2 + .../miri/tests/genmc/pass/litmus/default.rs | 47 ++ .../tests/genmc/pass/litmus/default.stderr | 2 + .../genmc/pass/litmus/detour.join.stderr | 2 + .../genmc/pass/litmus/detour.no_join.stderr | 2 + .../miri/tests/genmc/pass/litmus/detour.rs | 68 ++ .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 54 ++ .../genmc/pass/litmus/fr_w_w_w_reads.stderr | 2 + .../miri/tests/genmc/pass/test_cxx_build.rs | 8 - .../tests/genmc/pass/test_cxx_build.stderr | 5 - src/tools/miri/tests/utils/genmc.rs | 57 ++ 86 files changed, 3572 insertions(+), 429 deletions(-) create mode 100644 src/tools/miri/genmc-sys/.clang-format create mode 100644 src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp create mode 100644 src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp delete mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp delete mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp create mode 100644 src/tools/miri/src/concurrency/genmc/global_allocations.rs create mode 100644 src/tools/miri/src/concurrency/genmc/helper.rs create mode 100644 src/tools/miri/src/concurrency/genmc/run.rs create mode 100644 src/tools/miri/src/concurrency/genmc/scheduling.rs create mode 100644 src/tools/miri/src/concurrency/genmc/thread_id_map.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2cowr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr0.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr0.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr1.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr2.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corw.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corw.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cowr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cowr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/default.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/default.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr delete mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.rs delete mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.stderr create mode 100644 src/tools/miri/tests/utils/genmc.rs diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index ece51f2ba74af..c4280f5e87a6d 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +checksum = "6c64ed3da1c337cbaae7223cdcff8b4dddf698d188cd3eaddd1116f6b0295950" dependencies = [ "cc", "cxxbridge-cmd", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +checksum = "ae0a26a75a05551f5ae3d75b3557543d06682284eaa7419113162d602cb45766" dependencies = [ "cc", "codespan-reporting", @@ -392,9 +392,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +checksum = "952d408b6002b7db4b36da07c682a9cbb34f2db0efa03e976ae50a388414e16c" dependencies = [ "clap", "codespan-reporting", @@ -406,15 +406,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" +checksum = "ccbd201b471c75c6abb6321cace706d1982d270e308b891c11a3262d320f5265" [[package]] name = "cxxbridge-macro" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +checksum = "2bea8b915bbc4cb4288f242aa7ca18b23ecc6965e4d6e7c1b07905e3fe2e0c41" dependencies = [ "indexmap", "proc-macro2", @@ -501,9 +501,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "form_urlencoded" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 91dadf78a2f72..5afde9b23d5cf 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -70,7 +70,7 @@ harness = false [features] default = ["stack-cache", "native-lib"] -genmc = ["dep:genmc-sys"] # this enables a GPL dependency! +genmc = ["dep:genmc-sys"] stack-cache = [] stack-cache-consistency-check = ["stack-cache"] tracing = ["serde_json"] diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 5aabe90b5dab2..fdbf2defe42dc 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -9,9 +9,7 @@ Miri-GenMC integrates that model checker into Miri. ## Usage -**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.** - -For testing/developing Miri-GenMC (while keeping in mind the licensing issues): +For testing/developing Miri-GenMC: - clone the Miri repo. - build Miri-GenMC with `./miri build --features=genmc`. - OR: install Miri-GenMC in the current system with `./miri install --features=genmc` @@ -21,6 +19,21 @@ Basic usage: MIRIFLAGS="-Zmiri-genmc" cargo miri run ``` +Note that `cargo miri test` in GenMC mode is currently not supported. + +### Supported Parameters + +- `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`. + - `quiet`: Disable logging. + - `error`: Print errors. + - `warning`: Print errors and warnings. + - `tip`: Print errors, warnings and tips. + - If Miri is built with debug assertions, there are additional log levels available (downgraded to `tip` without debug assertions): + - `debug1`: Print revisits considered by GenMC. + - `debug2`: Print the execution graph after every memory access. + - `debug3`: Print reads-from values considered by GenMC. + @@ -57,6 +70,15 @@ The process for obtaining them is as follows: If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid formatting the Rust files inside that folder. +### Formatting the C++ code + +For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory. +With `clang-format` installed, run this command to format the c++ files (replace the `-i` with `--dry-run` to just see the changes.): +``` +find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i +``` +NOTE: this is currently not done automatically on pull requests to Miri. + diff --git a/src/tools/miri/genmc-sys/.clang-format b/src/tools/miri/genmc-sys/.clang-format new file mode 100644 index 0000000000000..669d76ea6c9bc --- /dev/null +++ b/src/tools/miri/genmc-sys/.clang-format @@ -0,0 +1,52 @@ +# .clang-format + +BasedOnStyle: LLVM +Standard: c++20 + +ColumnLimit: 100 +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: Empty +BreakBeforeBraces: Attach + +BinPackArguments: false +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: false +AlwaysBreakAfterReturnType: None + +# Force parameters to break and align +AlignAfterOpenBracket: BlockIndent +AllowAllArgumentsOnNextLine: false + +# Spacing around braces and parentheses +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceInEmptyBlock: false +SpacesInContainerLiterals: true +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false + +# Brace spacing for initializers +Cpp11BracedListStyle: false +SpaceBeforeCpp11BracedList: true + +# Import grouping: group standard, external, and project includes. +IncludeBlocks: Regroup +SortIncludes: true + +# Granularity: sort includes per module/file. +IncludeIsMainRegex: '([-_](test|unittest))?$' + +# Miscellaneous +SpaceAfterCStyleCast: true +SpaceBeforeParens: ControlStatements +PointerAlignment: Left +IndentCaseLabels: true +IndentWidth: 4 +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml index 95aef75afc4f5..aa6374b7b373f 100644 --- a/src/tools/miri/genmc-sys/Cargo.toml +++ b/src/tools/miri/genmc-sys/Cargo.toml @@ -1,17 +1,15 @@ [package] authors = ["Miri Team"] -# The parts in this repo are MIT OR Apache-2.0, but we are linking in -# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later. -license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later" +license = "MIT OR Apache-2.0" name = "genmc-sys" version = "0.1.0" edition = "2024" [dependencies] -cxx = { version = "1.0.160", features = ["c++20"] } +cxx = { version = "1.0.173", features = ["c++20"] } [build-dependencies] cc = "1.2.30" cmake = "0.1.54" git2 = { version = "0.20.2", default-features = false, features = ["https"] } -cxx-build = { version = "1.0.160", features = ["parallel"] } +cxx-build = { version = "1.0.173", features = ["parallel"] } diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index f20e0e8d525fc..af42c70431bf0 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -26,9 +26,9 @@ mod downloading { use super::GENMC_DOWNLOAD_PATH; /// The GenMC repository the we get our commit from. - pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git"; + pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab"; + pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; pub(crate) fn download_genmc() -> PathBuf { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); @@ -176,6 +176,11 @@ fn link_to_llvm(config_file: &Path) -> (String, String) { /// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs fn compile_cpp_dependencies(genmc_path: &Path) { + // Give each step a separate build directory to prevent interference. + let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap()); + let genmc_build_dir = out_dir.join("genmc"); + let interface_build_dir = out_dir.join("miri_genmc"); + // Part 1: // Compile the GenMC library using cmake. @@ -186,8 +191,11 @@ fn compile_cpp_dependencies(genmc_path: &Path) { let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); let mut config = cmake::Config::new(cmakelists_path); - config.profile(GENMC_CMAKE_PROFILE); - config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); + config + .always_configure(false) // We don't need to reconfigure on subsequent compilation runs. + .out_dir(genmc_build_dir) + .profile(GENMC_CMAKE_PROFILE) + .define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); // The actual compilation happens here: let genmc_install_dir = config.build(); @@ -210,6 +218,13 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); + let cpp_files = [ + "./cpp/src/MiriInterface/EventHandling.cpp", + "./cpp/src/MiriInterface/Exploration.cpp", + "./cpp/src/MiriInterface/Setup.cpp", + "./cpp/src/MiriInterface/ThreadManagement.cpp", + ]; + let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. if enable_genmc_debug { @@ -223,9 +238,9 @@ fn compile_cpp_dependencies(genmc_path: &Path) { .std("c++23") .include(genmc_include_dir) .include(llvm_include_dirs) - .include("./src_cpp") - .file("./src_cpp/MiriInterface.hpp") - .file("./src_cpp/MiriInterface.cpp") + .include("./cpp/include") + .files(&cpp_files) + .out_dir(interface_build_dir) .compile("genmc_interop"); // Link the Rust-C++ interface library generated by cxx_build: @@ -233,15 +248,8 @@ fn compile_cpp_dependencies(genmc_path: &Path) { } fn main() { - // Make sure we don't accidentally distribute a binary with GPL code. - if option_env!("RUSTC_STAGE").is_some() { - panic!( - "genmc should not be enabled in the rustc workspace since it includes a GPL dependency" - ); - } - // Select which path to use for the GenMC repo: - let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") { + let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") { let genmc_src_path = PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); assert!( @@ -249,6 +257,8 @@ fn main() { "GENMC_SRC_PATH={} does not exist!", genmc_src_path.display() ); + // Rebuild files in the given path change. + println!("cargo::rerun-if-changed={}", genmc_src_path.display()); genmc_src_path } else { downloading::download_genmc() @@ -263,5 +273,5 @@ fn main() { // cloned (since cargo detects that as a file modification). println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}"); println!("cargo::rerun-if-changed=./src"); - println!("cargo::rerun-if-changed=./src_cpp"); + println!("cargo::rerun-if-changed=./cpp"); } diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp new file mode 100644 index 0000000000000..4484f0b73caec --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -0,0 +1,274 @@ +#ifndef GENMC_MIRI_INTERFACE_HPP +#define GENMC_MIRI_INTERFACE_HPP + +// CXX.rs generated headers: +#include "rust/cxx.h" + +// GenMC generated headers: +#include "config.h" + +// Miri `genmc-sys/src_cpp` headers: +#include "ResultHandling.hpp" + +// GenMC headers: +#include "ExecutionGraph/EventLabel.hpp" +#include "Static/ModuleID.hpp" +#include "Support/MemOrdering.hpp" +#include "Support/RMWOps.hpp" +#include "Verification/Config.hpp" +#include "Verification/GenMCDriver.hpp" + +// C++ headers: +#include +#include +#include +#include + +/**** Types available to both Rust and C++ ****/ + +struct GenmcParams; +enum class LogLevel : std::uint8_t; + +struct GenmcScalar; +struct SchedulingResult; +struct LoadResult; +struct StoreResult; + +// GenMC uses `int` for its thread IDs. +using ThreadId = int; + +/// Set the log level for GenMC. +/// +/// # Safety +/// +/// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel` +/// variable. Any GenMC function may read from `logLevel` unsynchronized. +/// The safest way to use this function is to set the log level exactly once before first calling +/// `create_handle`. +/// Never calling this function is safe, GenMC will fall back to its default log level. +/* unsafe */ void set_log_level_raw(LogLevel log_level); + +struct MiriGenmcShim : private GenMCDriver { + + public: + MiriGenmcShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) + : GenMCDriver(std::move(conf), nullptr, mode) {} + + /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`. + /// + /// # Safety + /// + /// This function is marked as unsafe since the `logLevel` global variable is non-atomic. + /// This function should not be called in an unsynchronized way with `set_log_level_raw`, + /// since this function and any methods on the returned `MiriGenmcShim` may read the + /// `logLevel`, causing a data race. The safest way to use these functions is to call + /// `set_log_level_raw` once, and only then start creating handles. There should not be any + /// other (safe) way to create a `MiriGenmcShim`. + /* unsafe */ static auto create_handle(const GenmcParams& params) + -> std::unique_ptr; + + virtual ~MiriGenmcShim() {} + + /**** Execution start/end handling ****/ + + // This function must be called at the start of any execution, before any events are + // reported to GenMC. + void handle_execution_start(); + // This function must be called at the end of any execution, even if an error was found + // during the execution. + // Returns `null`, or a string containing an error message if an error occured. + std::unique_ptr handle_execution_end(); + + /***** Functions for handling events encountered during program execution. *****/ + + /**** Memory access handling ****/ + + [[nodiscard]] LoadResult handle_load( + ThreadId thread_id, + uint64_t address, + uint64_t size, + MemOrdering ord, + GenmcScalar old_val + ); + [[nodiscard]] StoreResult handle_store( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar value, + GenmcScalar old_val, + MemOrdering ord + ); + + /**** Memory (de)allocation ****/ + auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; + void handle_free(ThreadId thread_id, uint64_t address); + + /**** Thread management ****/ + void handle_thread_create(ThreadId thread_id, ThreadId parent_id); + void handle_thread_join(ThreadId thread_id, ThreadId child_id); + void handle_thread_finish(ThreadId thread_id, uint64_t ret_val); + void handle_thread_kill(ThreadId thread_id); + + /***** Exploration related functionality *****/ + + /** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is + * finished, blocked, or can continue. + * Updates the next instruction kind for the given thread id. */ + auto schedule_next(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) + -> SchedulingResult; + + /** + * Check whether there are more executions to explore. + * If there are more executions, this method prepares for the next execution and returns + * `true`. Returns true if there are no more executions to explore. */ + auto is_exploration_done() -> bool { + return GenMCDriver::done(); + } + + /**** Result querying functionality. ****/ + + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + + // Note that CXX.rs doesn't support returning a C++ string to Rust by value, + // it must be behind an indirection like a `unique_ptr` (tested with CXX 1.0.170). + + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with + /// integer) + auto get_blocked_execution_count() const -> uint64_t { + return static_cast(getResult().exploredBlocked); + } + + /// Get the number of executions explored by GenMC (cast into a fixed with integer) + auto get_explored_execution_count() const -> uint64_t { + return static_cast(getResult().explored); + } + + /// Get all messages that GenMC produced (errors, warnings), combined into one string. + auto get_result_message() const -> std::unique_ptr { + return std::make_unique(getResult().message); + } + + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + auto get_error_string() const -> std::unique_ptr { + const auto& result = GenMCDriver::getResult(); + if (result.status.has_value()) + return format_error(result.status.value()); + return nullptr; + } + + private: + /** Increment the event index in the given thread by 1 and return the new event. */ + [[nodiscard]] inline auto inc_pos(ThreadId tid) -> Event { + ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds"); + return ++threads_action_[tid].event; + } + /** Decrement the event index in the given thread by 1 and return the new event. */ + inline auto dec_pos(ThreadId tid) -> Event { + ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds"); + return --threads_action_[tid].event; + } + + /** + * Helper function for loads that need to reset the event counter when no value is returned. + * Same syntax as `GenMCDriver::handleLoad`, but this takes a thread id instead of an Event. + * Automatically calls `inc_pos` and `dec_pos` where needed for the given thread. + */ + template + auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult { + const auto pos = inc_pos(tid); + const auto ret = GenMCDriver::handleLoad(pos, std::forward(params)...); + // If we didn't get a value, we have to reset the index of the current thread. + if (!std::holds_alternative(ret)) { + dec_pos(tid); + } + return ret; + } + + /** + * GenMC uses the term `Action` to refer to a struct of: + * - `ActionKind`, storing whether the next instruction in a thread may be a load + * - `Event`, storing the most recent event index added for a thread + * + * Here we store the "action" for each thread. In particular we use this to assign event + * indices, since GenMC expects us to do that. + */ + std::vector threads_action_; +}; + +/// Get the bit mask that GenMC expects for global memory allocations. +/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the +/// `SAddr::staticMask` constant is consistent between Miri and GenMC, but if +/// https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant +/// directly. +constexpr auto get_global_alloc_static_mask() -> uint64_t { + return SAddr::staticMask; +} + +// CXX.rs generated headers: +// NOTE: this must be included *after* `MiriGenmcShim` and all the other types we use are defined, +// otherwise there will be compilation errors due to missing definitions. +#include "genmc-sys/src/lib.rs.h" + +/**** Result handling ****/ +// NOTE: these must come after the cxx_bridge generated code, since that contains the actual +// definitions of these types. + +namespace GenmcScalarExt { +inline GenmcScalar uninit() { + return GenmcScalar { + /* value: */ 0, + /* is_init: */ false, + }; +} + +inline GenmcScalar from_sval(SVal sval) { + return GenmcScalar { + /* value: */ sval.get(), + /* is_init: */ true, + }; +} + +inline SVal to_sval(GenmcScalar scalar) { + ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); + return SVal(scalar.value); +} +} // namespace GenmcScalarExt + +namespace LoadResultExt { +inline LoadResult no_value() { + return LoadResult { + /* error: */ std::unique_ptr(nullptr), + /* has_value: */ false, + /* read_value: */ GenmcScalarExt::uninit(), + }; +} + +inline LoadResult from_value(SVal read_value) { + return LoadResult { /* error: */ std::unique_ptr(nullptr), + /* has_value: */ true, + /* read_value: */ GenmcScalarExt::from_sval(read_value) }; +} + +inline LoadResult from_error(std::unique_ptr error) { + return LoadResult { /* error: */ std::move(error), + /* has_value: */ false, + /* read_value: */ GenmcScalarExt::uninit() }; +} +} // namespace LoadResultExt + +namespace StoreResultExt { +inline StoreResult ok(bool is_coherence_order_maximal_write) { + return StoreResult { /* error: */ std::unique_ptr(nullptr), + is_coherence_order_maximal_write }; +} + +inline StoreResult from_error(std::unique_ptr error) { + return StoreResult { /* error: */ std::move(error), + /* is_coherence_order_maximal_write: */ false }; +} +} // namespace StoreResultExt + +#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp new file mode 100644 index 0000000000000..189f32e6f513d --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp @@ -0,0 +1,21 @@ +#ifndef GENMC_RESULT_HANDLING_HPP +#define GENMC_RESULT_HANDLING_HPP + +// CXX.rs generated headers: +#include "rust/cxx.h" + +// GenMC headers: +#include "Verification/VerificationError.hpp" + +#include + +/** Information about an error, formatted as a string to avoid having to share an error enum and + * printing functionality with the Rust side. */ +static auto format_error(VerificationError err) -> std::unique_ptr { + auto buf = std::string(); + auto s = llvm::raw_string_ostream(buf); + s << err; + return std::make_unique(s.str()); +} + +#endif /* GENMC_RESULT_HANDLING_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp new file mode 100644 index 0000000000000..7d8f0b16b49ab --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -0,0 +1,120 @@ +/** This file contains functionality related to handling events encountered + * during an execution, such as loads, stores or memory (de)allocation. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "ADT/value_ptr.hpp" +#include "ExecutionGraph/EventLabel.hpp" +#include "ExecutionGraph/LoadAnnotation.hpp" +#include "Runtime/InterpreterEnumAPI.hpp" +#include "Static/ModuleID.hpp" +#include "Support/ASize.hpp" +#include "Support/Error.hpp" +#include "Support/Logger.hpp" +#include "Support/MemAccess.hpp" +#include "Support/RMWOps.hpp" +#include "Support/SAddr.hpp" +#include "Support/SVal.hpp" +#include "Support/ThreadInfo.hpp" +#include "Support/Verbosity.hpp" +#include "Verification/GenMCDriver.hpp" +#include "Verification/MemoryModel.hpp" + +// C++ headers: +#include +#include +#include +#include + +/**** Memory access handling ****/ + +[[nodiscard]] auto MiriGenmcShim::handle_load( + ThreadId thread_id, + uint64_t address, + uint64_t size, + MemOrdering ord, + GenmcScalar old_val +) -> LoadResult { + // `type` is only used for printing. + const auto type = AType::Unsigned; + const auto ret = handle_load_reset_if_none( + thread_id, + ord, + SAddr(address), + ASize(size), + type + ); + + if (const auto* err = std::get_if(&ret)) + return LoadResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&ret); + if (ret_val == nullptr) + ERROR("Unimplemented: load returned unexpected result."); + return LoadResultExt::from_value(*ret_val); +} + +[[nodiscard]] auto MiriGenmcShim::handle_store( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar value, + GenmcScalar old_val, + MemOrdering ord +) -> StoreResult { + const auto pos = inc_pos(thread_id); + const auto ret = GenMCDriver::handleStore( + pos, + ord, + SAddr(address), + ASize(size), + /* type */ AType::Unsigned, // `type` is only used for printing. + GenmcScalarExt::to_sval(value), + EventDeps() + ); + + if (const auto* err = std::get_if(&ret)) + return StoreResultExt::from_error(format_error(*err)); + if (!std::holds_alternative(ret)) + ERROR("store returned unexpected result"); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return StoreResultExt::ok( + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos + ); +} + +/**** Memory (de)allocation ****/ + +auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) + -> uint64_t { + const auto pos = inc_pos(thread_id); + + // These are only used for printing and features Miri-GenMC doesn't support (yet). + const auto storage_duration = StorageDuration::SD_Heap; + // Volatile, as opposed to "persistent" (i.e., non-volatile memory that persists over reboots) + const auto storage_type = StorageType::ST_Volatile; + const auto address_space = AddressSpace::AS_User; + + const SVal ret_val = GenMCDriver::handleMalloc( + pos, + size, + alignment, + storage_duration, + storage_type, + address_space, + EventDeps() + ); + return ret_val.get(); +} + +void MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleFree(pos, SAddr(address), EventDeps()); + // FIXME(genmc): add error handling once GenMC returns errors from `handleFree` +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp new file mode 100644 index 0000000000000..c51b59e865170 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -0,0 +1,42 @@ +/** This file contains functionality related to exploration, such as scheduling. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" + +// C++ headers: +#include + +auto MiriGenmcShim::schedule_next( + const int curr_thread_id, + const ActionKind curr_thread_next_instr_kind +) -> SchedulingResult { + // The current thread is the only one where the `kind` could have changed since we last made + // a scheduling decision. + threads_action_[curr_thread_id].kind = curr_thread_next_instr_kind; + + if (const auto result = GenMCDriver::scheduleNext(threads_action_)) + return SchedulingResult { ExecutionState::Ok, static_cast(result.value()) }; + if (GenMCDriver::isExecutionBlocked()) + return SchedulingResult { ExecutionState::Blocked, 0 }; + return SchedulingResult { ExecutionState::Finished, 0 }; +} + +/**** Execution start/end handling ****/ + +void MiriGenmcShim::handle_execution_start() { + threads_action_.clear(); + threads_action_.push_back(Action(ActionKind::Load, Event::getInit())); + GenMCDriver::handleExecutionStart(); +} + +auto MiriGenmcShim::handle_execution_end() -> std::unique_ptr { + // FIXME(genmc): add error handling once GenMC returns an error here. + GenMCDriver::handleExecutionEnd(); + return {}; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp new file mode 100644 index 0000000000000..7194ed02da432 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -0,0 +1,185 @@ +/** This file contains functionality related to creation of the GenMCDriver, + * including translating settings set by Miri. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" +#include "Verification/InterpreterCallbacks.hpp" + +// C++ headers: +#include + +/** + * Translate the Miri-GenMC `LogLevel` to the GenMC `VerbosityLevel`. + * Downgrade any debug options to `Tip` if `ENABLE_GENMC_DEBUG` is not enabled. + */ +static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel { + switch (log_level) { + case LogLevel::Quiet: + return VerbosityLevel::Quiet; + case LogLevel::Error: + return VerbosityLevel::Error; + case LogLevel::Warning: + return VerbosityLevel::Warning; + case LogLevel::Tip: + return VerbosityLevel::Tip; +#ifdef ENABLE_GENMC_DEBUG + case LogLevel::Debug1Revisits: + return VerbosityLevel::Debug1; + case LogLevel::Debug2MemoryAccesses: + return VerbosityLevel::Debug2; + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Debug3; +#else + // Downgrade to `Tip` if the debug levels are not available. + case LogLevel::Debug1Revisits: + case LogLevel::Debug2MemoryAccesses: + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Tip; +#endif + default: + WARN_ONCE( + "unknown-log-level", + "Unknown `LogLevel`, defaulting to `VerbosityLevel::Tip`." + ); + return VerbosityLevel::Tip; + } +} + +/* unsafe */ void set_log_level_raw(LogLevel log_level) { + // The `logLevel` is a static, non-atomic variable. + // It should never be changed if `MiriGenmcShim` still exists, since any of its methods may read + // the `logLevel`, otherwise it may cause data races. + logLevel = to_genmc_verbosity_level(log_level); +} + +/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params) + -> std::unique_ptr { + auto conf = std::make_shared(); + + // Set whether GenMC should print execution graphs after every explored/blocked execution. + // FIXME(genmc): pass these settings from Miri. + conf->printExecGraphs = false; + conf->printBlockedExecs = false; + + // `1024` is the default value that GenMC uses. + // If any thread has at least this many events, a warning/tip will be printed. + // + // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost + // all programs. The current value is large enough so the warning is not be triggered by any + // reasonable programs. + // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll' + // parameter). + // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for + // Miri. + conf->warnOnGraphSize = 1024 * 1024; + + // We only support the RC11 memory model for Rust. + conf->model = ModelType::RC11; + + // This prints the seed that GenMC picks for randomized scheduling during estimation mode. + conf->printRandomScheduleSeed = params.print_random_schedule_seed; + + // FIXME(genmc): supporting IPR requires annotations for `assume` and `spinloops`. + conf->ipr = false; + // FIXME(genmc): supporting BAM requires `Barrier` support + detecting whether return value + // of barriers are used. + conf->disableBAM = true; + + // Instruction caching could help speed up verification by filling the graph from cache, if + // the list of values read by all load events in a thread have been seen before. Combined + // with not replaying completed threads, this can also reducing the amount of Mir + // interpretation required by Miri. With the current setup, this would be incorrect, since + // Miri doesn't give GenMC the actual values read by non-atomic reads. + conf->instructionCaching = false; + // Many of Miri's checks work under the assumption that threads are only executed in an + // order that could actually happen during a normal execution. Formally, this means that + // replaying an execution needs to respect the po-rf-relation of the executiongraph (po == + // program-order, rf == reads-from). This means, any event in the graph, when replayed, must + // happen after any events that happen before it in the same graph according to the program + // code, and all (non-atomic) reads must happen after the write event they read from. + // + // Not replaying completed threads means any read event from that thread never happens in + // Miri's memory, so this would only work if there are never any non-atomic reads from any + // value written by the skipped thread. + conf->replayCompletedThreads = true; + + // FIXME(genmc): implement symmetry reduction. + ERROR_ON( + params.do_symmetry_reduction, + "Symmetry reduction is currently unsupported in GenMC mode." + ); + conf->symmetryReduction = params.do_symmetry_reduction; + + // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). + conf->schedulePolicy = SchedulePolicy::WF; + + // Set the mode used for this driver, either estimation or verification. + // FIXME(genmc): implement estimation mode. + const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode {}); + + // Running Miri-GenMC without race detection is not supported. + // Disabling this option also changes the behavior of the replay scheduler to only schedule + // at atomic operations, which is required with Miri. This happens because Miri can generate + // multiple GenMC events for a single MIR terminator. Without this option, the scheduler + // might incorrectly schedule an atomic MIR terminator because the first event it creates is + // a non-atomic (e.g., `StorageLive`). + conf->disableRaceDetection = false; + + // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory + // that is allowed to leak and memory that is not. + conf->warnUnfreedMemory = false; + + // FIXME(genmc,error handling): This function currently exits on error, but will return an + // error value in the future. The return value should be checked once this change is made. + checkConfig(*conf); + + // Create the actual driver and Miri-GenMC communication shim. + auto driver = std::make_unique(std::move(conf), mode); + + // FIXME(genmc,HACK): Until a proper solution is implemented in GenMC, these callbacks will + // allow Miri to return information about global allocations and override uninitialized memory + // checks for non-atomic loads (Miri handles those without GenMC, so the error would be wrong). + auto interpreter_callbacks = InterpreterCallbacks { + // Miri already ensures that memory accesses are valid, so this check doesn't matter. + // We check that the address is static, but skip checking if it is part of an actual + // allocation. + /* isStaticallyAllocated: */ [](SAddr addr) { return addr.isStatic(); }, + // FIXME(genmc,error reporting): Once a proper a proper API for passing such information is + // implemented in GenMC, Miri should use it to improve the produced error messages. + /* getStaticName: */ [](SAddr addr) { return "[UNKNOWN STATIC]"; }, + // This function is called to get the initial value stored at the given address. + // + // From a Miri perspective, this API doesn't work very well: most memory starts out + // "uninitialized"; + // only statics have an initial value. And their initial value is just a sequence of bytes, + // but GenMC + // expect this to be already split into separate atomic variables. So we return a dummy + // value. + // This value should never be visible to the interpreted program. + // GenMC does not understand uninitialized memory the same way Miri does, which may cause + // this function to be called. The returned value can be visible to Miri or the user: + // - Printing the execution graph may contain this value in place of uninitialized values. + // FIXME(genmc): NOTE: printing the execution graph is not yet implemented. + // - Non-atomic loads may return this value, but Miri ignores values of non-atomic loads. + // - Atomic loads will *not* see this value once mixed atomic-non-atomic support is added. + // Currently, atomic loads can see this value, unless initialized by an *atomic* store. + // FIXME(genmc): update this comment once mixed atomic-non-atomic support is added. + // + // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the + // initial value getter would return an `optional`, since the memory location may be + // uninitialized. + /* initValGetter: */ [](const AAccess& a) { return SVal(0xDEAD); }, + // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in + // that case. This should no longer be required with proper mixed-size access support. + /* skipUninitLoadChecks: */ [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + }; + driver->setInterpCallbacks(std::move(interpreter_callbacks)); + + return driver; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp new file mode 100644 index 0000000000000..352d27adc3e8b --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp @@ -0,0 +1,54 @@ + +/** This file contains functionality related thread management (creation, finishing, join, etc.) */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" + +// C++ headers: +#include + +void MiriGenmcShim::handle_thread_create(ThreadId thread_id, ThreadId parent_id) { + // NOTE: The threadCreate event happens in the parent: + const auto pos = inc_pos(parent_id); + // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set: + const unsigned fun_id = 0; + const SVal arg = SVal(0); + const ThreadInfo child_info = ThreadInfo { thread_id, parent_id, fun_id, arg }; + + // NOTE: Default memory ordering (`Release`) used here. + const auto child_tid = GenMCDriver::handleThreadCreate(pos, child_info, EventDeps()); + // Sanity check the thread id, which is the index in the `threads_action_` array. + BUG_ON(child_tid != thread_id || child_tid <= 0 || child_tid != threads_action_.size()); + threads_action_.push_back(Action(ActionKind::Load, Event(child_tid, 0))); +} + +void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) { + // The thread join event happens in the parent. + const auto pos = inc_pos(thread_id); + + // NOTE: Default memory ordering (`Acquire`) used here. + const auto ret = GenMCDriver::handleThreadJoin(pos, child_id, EventDeps()); + // If the join failed, decrease the event index again: + if (!std::holds_alternative(ret)) { + dec_pos(thread_id); + } + + // NOTE: Thread return value is ignored, since Miri doesn't need it. +} + +void MiriGenmcShim::handle_thread_finish(ThreadId thread_id, uint64_t ret_val) { + const auto pos = inc_pos(thread_id); + // NOTE: Default memory ordering (`Release`) used here. + GenMCDriver::handleThreadFinish(pos, SVal(ret_val)); +} + +void MiriGenmcShim::handle_thread_kill(ThreadId thread_id) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleThreadKill(pos); +} diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index ab46d729ea167..406c90809fcd5 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -1,17 +1,90 @@ +use std::str::FromStr; +use std::sync::OnceLock; + +pub use cxx::UniquePtr; + pub use self::ffi::*; +/// Defined in "genmc/src/Support/SAddr.hpp". +/// The first bit of all global addresses must be set to `1`. +/// This means the mask, interpreted as an address, is the lower bound of where the global address space starts. +/// +/// FIXME(genmc): rework this if non-64bit support is added to GenMC (the current allocation scheme only allows for 64bit addresses). +/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the constant is consistent between Miri and GenMC, +/// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly. +pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; + +/// GenMC thread ids are C++ type `int`, which is equivalent to Rust's `i32` on most platforms. +/// The main thread always has thread id 0. +pub const GENMC_MAIN_THREAD_ID: i32 = 0; + +/// Changing GenMC's log level is not thread safe, so we limit it to only be set once to prevent any data races. +/// This value will be initialized when the first `MiriGenmcShim` is created. +static GENMC_LOG_LEVEL: OnceLock = OnceLock::new(); + +// Create a new handle to the GenMC model checker. +// The first call to this function determines the log level of GenMC, any future call with a different log level will panic. +pub fn create_genmc_driver_handle( + params: &GenmcParams, + genmc_log_level: LogLevel, +) -> UniquePtr { + // SAFETY: Only setting the GenMC log level once is guaranteed by the `OnceLock`. + // No other place calls `set_log_level_raw`, so the `logLevel` value in GenMC will not change once we initialize it once. + // All functions that use GenMC's `logLevel` can only be accessed in safe Rust through a `MiriGenmcShim`. + // There is no way to get `MiriGenmcShim` other than through `create_handle`, and we only call it *after* setting the log level, preventing any possible data races. + assert_eq!( + &genmc_log_level, + GENMC_LOG_LEVEL.get_or_init(|| { + unsafe { set_log_level_raw(genmc_log_level) }; + genmc_log_level + }), + "Attempt to change the GenMC log level after it was already set" + ); + unsafe { MiriGenmcShim::create_handle(params) } +} + +impl GenmcScalar { + pub const UNINIT: Self = Self { value: 0, is_init: false }; + + pub const fn from_u64(value: u64) -> Self { + Self { value, is_init: true } + } +} + impl Default for GenmcParams { fn default() -> Self { - Self { - print_random_schedule_seed: false, - do_symmetry_reduction: false, - // FIXME(GenMC): Add defaults for remaining parameters - } + Self { print_random_schedule_seed: false, do_symmetry_reduction: false } + } +} + +impl Default for LogLevel { + fn default() -> Self { + // FIXME(genmc): set `Tip` by default once the GenMC tips are relevant to Miri. + Self::Warning + } +} + +impl FromStr for LogLevel { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(match s { + "quiet" => LogLevel::Quiet, + "error" => LogLevel::Error, + "warning" => LogLevel::Warning, + "tip" => LogLevel::Tip, + "debug1" => LogLevel::Debug1Revisits, + "debug2" => LogLevel::Debug2MemoryAccesses, + "debug3" => LogLevel::Debug3ReadsFrom, + _ => return Err("invalid log level"), + }) } } #[cxx::bridge] mod ffi { + /**** Types shared between Miri/Rust and Miri/C++ through cxx_bridge: ****/ + /// Parameters that will be given to GenMC for setting up the model checker. /// (The fields of this struct are visible to both Rust and C++) #[derive(Clone, Debug)] @@ -20,11 +93,221 @@ mod ffi { pub do_symmetry_reduction: bool, // FIXME(GenMC): Add remaining parameters. } + + /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). + /// We add this intermediate type to prevent changes to the GenMC log-level from breaking the Miri + /// build, and to have a stable type for the C++-Rust interface, independent of `ENABLE_GENMC_DEBUG`. + #[derive(Debug)] + enum LogLevel { + /// Disable *all* logging (including error messages on a crash). + Quiet, + /// Log errors. + Error, + /// Log errors and warnings. + Warning, + /// Log errors, warnings and tips. + Tip, + /// Debug print considered revisits. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug1Revisits, + /// Print the execution graph after every memory access. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug2MemoryAccesses, + /// Print reads-from values considered by GenMC. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug3ReadsFrom, + } + + /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. + /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. + #[derive(Debug, Clone, Copy)] + struct GenmcScalar { + value: u64, + is_init: bool, + } + + #[must_use] + #[derive(Debug, Clone, Copy)] + enum ExecutionState { + Ok, + Blocked, + Finished, + } + + #[must_use] + #[derive(Debug)] + struct SchedulingResult { + exec_state: ExecutionState, + next_thread: i32, + } + + #[must_use] + #[derive(Debug)] + struct LoadResult { + /// If not null, contains the error encountered during the handling of the load. + error: UniquePtr, + /// Indicates whether a value was read or not. + has_value: bool, + /// The value that was read. Should not be used if `has_value` is `false`. + read_value: GenmcScalar, + } + + #[must_use] + #[derive(Debug)] + struct StoreResult { + /// If not null, contains the error encountered during the handling of the store. + error: UniquePtr, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + + /**** Types shared between Miri/Rust and GenMC/C++ through cxx_bridge: ****/ + + #[derive(Debug)] + /// Corresponds to GenMC's type with the same name. + /// Should only be modified if changed by GenMC. + enum ActionKind { + /// Any Mir terminator that's atomic and has load semantics. + Load, + /// Anything that's not a `Load`. + NonLoad, + } + + #[derive(Debug)] + /// Corresponds to GenMC's type with the same name. + /// Should only be modified if changed by GenMC. + enum MemOrdering { + NotAtomic = 0, + Relaxed = 1, + // We skip 2 in case we support consume. + Acquire = 3, + Release = 4, + AcquireRelease = 5, + SequentiallyConsistent = 6, + } + + // # Safety + // + // This block is unsafe to allow defining safe methods inside. + // + // `get_global_alloc_static_mask` is safe since it just returns a constant. + // All methods on `MiriGenmcShim` are safe by the correct usage of the two unsafe functions + // `set_log_level_raw` and `MiriGenmcShim::create_handle`. + // See the doc comment on those two functions for their safety requirements. unsafe extern "C++" { include!("MiriInterface.hpp"); - type MiriGenMCShim; + /**** Types shared between Miri/Rust and Miri/C++: ****/ + type MiriGenmcShim; + + /**** Types shared between Miri/Rust and GenMC/C++: ****/ + type ActionKind; + type MemOrdering; + + /// Set the log level for GenMC. + /// + /// # Safety + /// + /// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel` variable. + /// Any GenMC function may read from `logLevel` unsynchronized. + /// The safest way to use this function is to set the log level exactly once before first calling `create_handle`. + /// Never calling this function is safe, GenMC will fall back to its default log level. + unsafe fn set_log_level_raw(log_level: LogLevel); + + /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`. + /// + /// # Safety + /// + /// This function is marked as unsafe since the `logLevel` global variable is non-atomic. + /// This function should not be called in an unsynchronized way with `set_log_level_raw`, since + /// this function and any methods on the returned `MiriGenmcShim` may read the `logLevel`, + /// causing a data race. + /// The safest way to use these functions is to call `set_log_level_raw` once, and only then + /// start creating handles. + /// There should not be any other (safe) way to create a `MiriGenmcShim`. + #[Self = "MiriGenmcShim"] + unsafe fn create_handle(params: &GenmcParams) -> UniquePtr; + /// Get the bit mask that GenMC expects for global memory allocations. + fn get_global_alloc_static_mask() -> u64; + + /// This function must be called at the start of any execution, before any events are reported to GenMC. + fn handle_execution_start(self: Pin<&mut MiriGenmcShim>); + /// This function must be called at the end of any execution, even if an error was found during the execution. + /// Returns `null`, or a string containing an error message if an error occured. + fn handle_execution_end(self: Pin<&mut MiriGenmcShim>) -> UniquePtr; + + /***** Functions for handling events encountered during program execution. *****/ + + /**** Memory access handling ****/ + fn handle_load( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + memory_ordering: MemOrdering, + old_value: GenmcScalar, + ) -> LoadResult; + fn handle_store( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + value: GenmcScalar, + old_value: GenmcScalar, + memory_ordering: MemOrdering, + ) -> StoreResult; + + /**** Memory (de)allocation ****/ + fn handle_malloc( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + size: u64, + alignment: u64, + ) -> u64; + fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64); + + /**** Thread management ****/ + fn handle_thread_create(self: Pin<&mut MiriGenmcShim>, thread_id: i32, parent_id: i32); + fn handle_thread_join(self: Pin<&mut MiriGenmcShim>, thread_id: i32, child_id: i32); + fn handle_thread_finish(self: Pin<&mut MiriGenmcShim>, thread_id: i32, ret_val: u64); + fn handle_thread_kill(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + + /***** Exploration related functionality *****/ + + /// Ask the GenMC scheduler for a new thread to schedule and + /// return whether the execution is finished, blocked, or can continue. + /// Updates the next instruction kind for the given thread id. + fn schedule_next( + self: Pin<&mut MiriGenmcShim>, + curr_thread_id: i32, + curr_thread_next_instr_kind: ActionKind, + ) -> SchedulingResult; + + /// Check whether there are more executions to explore. + /// If there are more executions, this method prepares for the next execution and returns `true`. + fn is_exploration_done(self: Pin<&mut MiriGenmcShim>) -> bool; + + /**** Result querying functionality. ****/ + + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + // Each `GenMCDriver` contains one `VerificationResult`, and each `MiriGenmcShim` contains on `GenMCDriver`. + // GenMC builds up the content of the `struct VerificationResult` over the course of an exploration, + // but it's safe to look at it at any point, since it is only accessible through exactly one `MiriGenmcShim`. + // All these functions for querying the result can be safely called repeatedly and at any time, + // though the results may be incomplete if called before `handle_execution_end`. - fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with integer) + fn get_blocked_execution_count(self: &MiriGenmcShim) -> u64; + /// Get the number of executions explored by GenMC (cast into a fixed with integer) + fn get_explored_execution_count(self: &MiriGenmcShim) -> u64; + /// Get all messages that GenMC produced (errors, warnings), combined into one string. + fn get_result_message(self: &MiriGenmcShim) -> UniquePtr; + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + fn get_error_string(self: &MiriGenmcShim) -> UniquePtr; } } diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp deleted file mode 100644 index 0827bb3d40746..0000000000000 --- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "MiriInterface.hpp" - -#include "genmc-sys/src/lib.rs.h" - -auto MiriGenMCShim::createHandle(const GenmcParams &config) - -> std::unique_ptr -{ - auto conf = std::make_shared(); - - // Miri needs all threads to be replayed, even fully completed ones. - conf->replayCompletedThreads = true; - - // We only support the RC11 memory model for Rust. - conf->model = ModelType::RC11; - - conf->printRandomScheduleSeed = config.print_random_schedule_seed; - - // FIXME(genmc): disable any options we don't support currently: - conf->ipr = false; - conf->disableBAM = true; - conf->instructionCaching = false; - - ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); - conf->symmetryReduction = config.do_symmetry_reduction; - - // FIXME(genmc): Should there be a way to change this option from Miri? - conf->schedulePolicy = SchedulePolicy::WF; - - // FIXME(genmc): implement estimation mode: - conf->estimate = false; - conf->estimationMax = 1000; - const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) - : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); - - // Running Miri-GenMC without race detection is not supported. - // Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri. - // This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option, - // the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`). - conf->disableRaceDetection = false; - - // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory - // that is allowed to leak and memory that is not. - conf->warnUnfreedMemory = false; - - // FIXME(genmc): check config: - // checkConfigOptions(*conf); - - auto driver = std::make_unique(std::move(conf), mode); - return driver; -} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp deleted file mode 100644 index e55522ef41897..0000000000000 --- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef GENMC_MIRI_INTERFACE_HPP -#define GENMC_MIRI_INTERFACE_HPP - -#include "rust/cxx.h" - -#include "config.h" - -#include "Config/Config.hpp" -#include "Verification/GenMCDriver.hpp" - -#include - -/**** Types available to Miri ****/ - -// Config struct defined on the Rust side and translated to C++ by cxx.rs: -struct GenmcParams; - -struct MiriGenMCShim : private GenMCDriver -{ - -public: - MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) - : GenMCDriver(std::move(conf), nullptr, mode) - { - std::cerr << "C++: GenMC handle created!" << std::endl; - } - - virtual ~MiriGenMCShim() - { - std::cerr << "C++: GenMC handle destroyed!" << std::endl; - } - - static std::unique_ptr createHandle(const GenmcParams &config); -}; - -/**** Functions available to Miri ****/ - -// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. -static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr -{ - return MiriGenMCShim::createHandle(config); -} - -#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index afd8afbaeab07..5d94a1f4138a6 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -34,6 +34,8 @@ pub struct GlobalStateInner { /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset /// from the base address, and we need to find the `AllocId` it belongs to. This is not the /// *full* inverse of `base_addr`; dead allocations have been removed. + /// Note that in GenMC mode, dead allocations are *not* removed -- and also, addresses are never + /// reused. This lets us use the address as a cross-execution-stable identifier for an allocation. int_to_ptr_map: Vec<(u64, AllocId)>, /// The base address for each allocation. We cannot put that into /// `AllocExtra` because function pointers also have a base address, and @@ -123,7 +125,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miri's address assignment leaks state across thread boundaries, which is incompatible // with GenMC execution. So we instead let GenMC assign addresses to allocations. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?; + let addr = + genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind)?; return interp_ok(addr); } @@ -180,7 +183,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.active_thread(), ) { if let Some(clock) = clock { - this.acquire_clock(&clock); + this.acquire_clock(&clock)?; } interp_ok(reuse_addr) } else { @@ -242,7 +245,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - debug_assert!(this.is_alloc_live(alloc_id)); + // In GenMC mode, we keep all allocations, so this check doesn't apply there. + if this.machine.data_race.as_genmc_ref().is_none() { + debug_assert!(this.is_alloc_live(alloc_id)); + } Some(alloc_id) } else { None @@ -459,6 +465,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> MiriMachine<'tcx> { pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { + // In GenMC mode, we can't remove dead allocation info since such pointers can + // still be stored in atomics and we need this info to convert GenMC pointers to Miri pointers. + // `global_state.reuse` is also unused so we can just skip this entire function. + if self.data_race.as_genmc_ref().is_some() { + return; + } + let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ae1b25f8857a0..b96359316b921 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -39,7 +39,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use miri::{ BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType, - ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, + ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, run_genmc_mode, }; use rustc_abi::ExternAbi; use rustc_data_structures::sync; @@ -187,9 +187,18 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } if let Some(_genmc_config) = &config.genmc_config { - let _genmc_ctx = Rc::new(GenmcCtx::new(&config)); + let eval_entry_once = |genmc_ctx: Rc| { + miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) + }; + + // FIXME(genmc): add estimation mode here. - todo!("GenMC mode not yet implemented"); + let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| { + tcx.dcx().abort_if_errors(); + rustc_driver::EXIT_FAILURE + }); + + exit(return_code); }; if let Some(many_seeds) = self.many_seeds.take() { @@ -735,9 +744,11 @@ fn main() { // Validate settings for data race detection and GenMC mode. if miri_config.genmc_config.is_some() { if !miri_config.data_race_detector { - fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); + fatal_error!("Cannot disable data race detection in GenMC mode"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); + } else if !miri_config.native_lib.is_empty() { + fatal_error!("native-lib not supported in GenMC mode."); } if miri_config.borrow_tracker.is_some() { eprintln!( @@ -745,6 +756,9 @@ fn main() { ); miri_config.borrow_tracker = None; } + // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway + // would be a NOP in GenMC mode. + miri_config.fixed_scheduling = true; } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 2977efaae04ac..5fe00ab02c4b9 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -740,6 +740,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { if let Some(access) = access { assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. + // FIXME(genmc): Ensure this is still done in GenMC mode. Check for other places where GenMC may need to be informed. if let Some(data_race) = alloc_extra.data_race.as_vclocks_mut() { data_race.write( alloc_id, diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 38d76f5cf73b7..19d37a691d90c 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -719,8 +719,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Only metadata on the location itself is used. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let old_val = None; + let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err(); return genmc_ctx.atomic_load( this, place.ptr().addr(), @@ -751,11 +750,22 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // The program didn't actually do a read, so suppress the memory access hooks. // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! - let old_val = this.run_for_validation_mut(|this| this.read_scalar(dest)).discard_err(); + let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err(); + // Inform GenMC about the atomic store. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - genmc_ctx.atomic_store(this, dest.ptr().addr(), dest.layout.size, val, atomic)?; + if genmc_ctx.atomic_store( + this, + dest.ptr().addr(), + dest.layout.size, + val, + old_val, + atomic, + )? { + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + this.allow_data_races_mut(|this| this.write_scalar(val, dest))?; + } return interp_ok(()); } this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; @@ -779,7 +789,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic rmw operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_rmw_op( this, place.ptr().addr(), @@ -787,8 +796,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { atomic, (op, not), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -818,14 +830,19 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic atomic exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let (old_val, _is_success) = genmc_ctx.atomic_exchange( + let (old_val, new_val) = genmc_ctx.atomic_exchange( this, place.ptr().addr(), place.layout.size, new, atomic, + old, )?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(old_val); } @@ -851,7 +868,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic min/max operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_min_max_op( this, place.ptr().addr(), @@ -860,8 +876,13 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { min, old.layout.backend_repr.is_signed(), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -903,15 +924,12 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; - // Failure ordering cannot be stronger than success ordering, therefore first attempt - // to read with the failure ordering and if successful then try again with the success - // read ordering and write in the success case. // Read as immediate for the sake of `binary_op()` let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; // Inform GenMC about the atomic atomic compare exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let (old, cmpxchg_success) = genmc_ctx.atomic_compare_exchange( + let (old_value, new_value, cmpxchg_success) = genmc_ctx.atomic_compare_exchange( this, place.ptr().addr(), place.layout.size, @@ -920,11 +938,15 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { success, fail, can_fail_spuriously, + old.to_scalar(), )?; - if cmpxchg_success { - this.allow_data_races_mut(|this| this.write_scalar(new, place))?; + + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_value) = new_value { + this.allow_data_races_mut(|this| this.write_scalar(new_value, place))?; } - return interp_ok(Immediate::ScalarPair(old, Scalar::from_bool(cmpxchg_success))); + return interp_ok(Immediate::ScalarPair(old_value, Scalar::from_bool(cmpxchg_success))); } // `binary_op` will bail if either of them is not a scalar. @@ -985,11 +1007,16 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// Acquire the given clock into the current thread, establishing synchronization with /// the moment when that clock snapshot was taken via `release_clock`. - fn acquire_clock(&self, clock: &VClock) { + fn acquire_clock(&self, clock: &VClock) -> InterpResult<'tcx> { let this = self.eval_context_ref(); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(clock, &this.machine.threads); + match &this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Genmc(_genmc_ctx) => + throw_unsup_format!("acquire_clock is not (yet) supported in GenMC mode."), + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.acquire_clock(clock, &this.machine.threads), } + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index c56adab90fe2b..66116d5cf23c7 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,13 +1,14 @@ -use super::GenmcParams; +use genmc_sys::{GenmcParams, LogLevel}; /// Configuration for GenMC mode. /// The `params` field is shared with the C++ side. /// The remaining options are kept on the Rust side. #[derive(Debug, Default, Clone)] pub struct GenmcConfig { + /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, - do_estimation: bool, - // FIXME(GenMC): add remaining options. + /// The log level for GenMC. + pub(super) log_level: LogLevel, } impl GenmcConfig { @@ -29,7 +30,15 @@ impl GenmcConfig { if trimmed_arg.is_empty() { return Ok(()); // this corresponds to "-Zmiri-genmc" } - // FIXME(GenMC): implement remaining parameters. - todo!(); + let genmc_config = genmc_config.as_mut().unwrap(); + let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else { + return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\"")); + }; + if let Some(log_level) = trimmed_arg.strip_prefix("log=") { + genmc_config.log_level = log_level.parse()?; + } else { + return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); + } + Ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 79d27c4be1591..1960ef37cc9f3 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,30 +1,49 @@ -#![allow(unused)] - use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult}; +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; use rustc_middle::mir; +pub use self::run::run_genmc_mode; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, + ThreadId, ThreadManager, VisitProvenance, VisitWith, }; +#[derive(Clone, Copy, Debug)] +pub enum ExitType { + MainThreadFinish, + ExitCalled, +} + #[derive(Debug)] pub struct GenmcCtx {} #[derive(Debug, Default, Clone)] pub struct GenmcConfig {} -impl GenmcCtx { - pub fn new(_miri_config: &MiriConfig) -> Self { - unreachable!() +mod run { + use std::rc::Rc; + + use rustc_middle::ty::TyCtxt; + + use crate::{GenmcCtx, MiriConfig}; + + pub fn run_genmc_mode<'tcx>( + _config: &MiriConfig, + _eval_entry: impl Fn(Rc) -> Option, + _tcx: TyCtxt<'tcx>, + ) -> Option { + unreachable!(); } +} + +impl GenmcCtx { + // We don't provide the `new` function in the dummy module. - pub fn get_stuck_execution_count(&self) -> usize { + pub fn get_blocked_execution_count(&self) -> usize { unreachable!() } - pub fn print_genmc_graph(&self) { + pub fn get_explored_execution_count(&self) -> usize { unreachable!() } @@ -34,17 +53,6 @@ impl GenmcCtx { /**** Memory access handling ****/ - pub(crate) fn handle_execution_start(&self) { - unreachable!() - } - - pub(crate) fn handle_execution_end<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> Result<(), String> { - unreachable!() - } - pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { unreachable!() } @@ -67,8 +75,9 @@ impl GenmcCtx { _address: Size, _size: Size, _value: Scalar, + _old_value: Option, _ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx, bool> { unreachable!() } @@ -76,7 +85,7 @@ impl GenmcCtx { &self, _machine: &MiriMachine<'tcx>, _ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -86,22 +95,24 @@ impl GenmcCtx { _address: Size, _size: Size, _ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), + (_rmw_op, _not): (mir::BinOp, bool), _rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -112,7 +123,8 @@ impl GenmcCtx { _size: Size, _rhs_scalar: Scalar, _ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -126,7 +138,8 @@ impl GenmcCtx { _success: AtomicRwOrd, _fail: AtomicReadOrd, _can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option, bool)> { unreachable!() } @@ -135,7 +148,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -144,7 +157,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -152,7 +165,8 @@ impl GenmcCtx { pub(crate) fn handle_alloc<'tcx>( &self, - _machine: &MiriMachine<'tcx>, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _alloc_id: AllocId, _size: Size, _alignment: Align, _memory_kind: MemoryKind, @@ -163,11 +177,10 @@ impl GenmcCtx { pub(crate) fn handle_dealloc<'tcx>( &self, _machine: &MiriMachine<'tcx>, + _alloc_id: AllocId, _address: Size, - _size: Size, - _align: Align, _kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -176,8 +189,10 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, _threads: &ThreadManager<'tcx>, + _start_routine: crate::Pointer, + _func_arg: &crate::ImmTy<'tcx>, _new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -185,24 +200,26 @@ impl GenmcCtx { &self, _active_thread_id: ThreadId, _child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } - pub(crate) fn handle_thread_stack_empty(&self, _thread_id: ThreadId) { + pub(crate) fn handle_thread_finish<'tcx>(&self, _threads: &ThreadManager<'tcx>) { unreachable!() } - pub(crate) fn handle_thread_finish<'tcx>( + pub(crate) fn handle_exit<'tcx>( &self, - _threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { + _thread: ThreadId, + _exit_code: i32, + _exit_type: ExitType, + ) -> InterpResult<'tcx> { unreachable!() } /**** Scheduling functionality ****/ - pub(crate) fn schedule_thread<'tcx>( + pub fn schedule_thread<'tcx>( &self, _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> InterpResult<'tcx, ThreadId> { @@ -211,6 +228,7 @@ impl GenmcCtx { /**** Blocking instructions ****/ + #[allow(unused)] pub(crate) fn handle_verifier_assume<'tcx>( &self, _machine: &MiriMachine<'tcx>, @@ -229,7 +247,7 @@ impl VisitProvenance for GenmcCtx { impl GenmcConfig { pub fn parse_arg( _genmc_config: &mut Option, - trimmed_arg: &str, + _trimmed_arg: &str, ) -> Result<(), String> { if cfg!(feature = "genmc") { Err(format!("GenMC is disabled in this build of Miri")) @@ -237,8 +255,4 @@ impl GenmcConfig { Err(format!("GenMC is not supported on this target")) } } - - pub fn should_print_graph(&self, _rep: usize) -> bool { - unreachable!() - } } diff --git a/src/tools/miri/src/concurrency/genmc/global_allocations.rs b/src/tools/miri/src/concurrency/genmc/global_allocations.rs new file mode 100644 index 0000000000000..272f8d2484038 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/global_allocations.rs @@ -0,0 +1,118 @@ +use std::collections::hash_map::Entry; +use std::sync::RwLock; + +use genmc_sys::{GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask}; +use rand::SeedableRng; +use rand::rngs::StdRng; +use rustc_const_eval::interpret::{AllocId, AllocInfo, InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashMap; +use tracing::debug; + +use crate::alloc_addresses::AddressGenerator; + +#[derive(Debug)] +struct GlobalStateInner { + /// The base address for each *global* allocation. + base_addr: FxHashMap, + /// We use the same address generator that Miri uses in normal operation. + address_generator: AddressGenerator, + /// The address generator needs an Rng to randomize the offsets between allocations. + /// We don't use the `MiriMachine` Rng since this is global, cross-machine state. + rng: StdRng, +} + +/// Allocator for global memory in GenMC mode. +/// Miri doesn't discover all global allocations statically like LLI does for GenMC. +/// The existing global memory allocator in GenMC doesn't support this, so we take over these allocations. +/// Global allocations need to be in a specific address range, with the lower limit given by the `GENMC_GLOBAL_ADDRESSES_MASK` constant. +/// +/// Every global allocation must have the same addresses across all executions of a single program. +/// Therefore there is only 1 global allocator, and it syncs new globals across executions, even if they are explored in parallel. +#[derive(Debug)] +pub struct GlobalAllocationHandler(RwLock); + +impl GlobalAllocationHandler { + /// Create a new global address generator with a given max address `last_addr` + /// (corresponding to the highest address available on the target platform, unless another limit exists). + /// No addresses higher than this will be allocated. + /// Will panic if the given address limit is too small to allocate any addresses. + pub fn new(last_addr: u64) -> GlobalAllocationHandler { + assert_eq!(GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask()); + assert_ne!(GENMC_GLOBAL_ADDRESSES_MASK, 0); + // FIXME(genmc): Remove if non-64bit targets are supported. + assert!( + GENMC_GLOBAL_ADDRESSES_MASK < last_addr, + "only 64bit platforms are currently supported (highest address {last_addr:#x} <= minimum global address {GENMC_GLOBAL_ADDRESSES_MASK:#x})." + ); + Self(RwLock::new(GlobalStateInner { + base_addr: FxHashMap::default(), + address_generator: AddressGenerator::new(GENMC_GLOBAL_ADDRESSES_MASK..last_addr), + // FIXME(genmc): We could provide a way to changes this seed, to allow for different global addresses. + rng: StdRng::seed_from_u64(0), + })) + } +} + +impl GlobalStateInner { + fn global_allocate_addr<'tcx>( + &mut self, + alloc_id: AllocId, + info: AllocInfo, + ) -> InterpResult<'tcx, u64> { + let entry = match self.base_addr.entry(alloc_id) { + Entry::Occupied(occupied_entry) => { + // Looks like some other thread allocated this for us + // between when we released the read lock and aquired the write lock, + // so we just return that value. + return interp_ok(*occupied_entry.get()); + } + Entry::Vacant(vacant_entry) => vacant_entry, + }; + + // This allocation does not have a base address yet, pick or reuse one. + // We are not in native lib mode (incompatible with GenMC mode), so we control the addresses ourselves. + let new_addr = self.address_generator.generate(info.size, info.align, &mut self.rng)?; + + // Cache the address for future use. + entry.insert(new_addr); + + interp_ok(new_addr) + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Allocate a new address for the given alloc id, or return the cached address. + /// Each alloc id is assigned one unique allocation which will not change if this function is called again with the same alloc id. + fn get_global_allocation_address( + &self, + global_allocation_handler: &GlobalAllocationHandler, + alloc_id: AllocId, + ) -> InterpResult<'tcx, u64> { + let this = self.eval_context_ref(); + let info = this.get_alloc_info(alloc_id); + + let global_state = global_allocation_handler.0.read().unwrap(); + if let Some(base_addr) = global_state.base_addr.get(&alloc_id) { + debug!( + "GenMC: address for global with alloc id {alloc_id:?} was cached: {base_addr} == {base_addr:#x}" + ); + return interp_ok(*base_addr); + } + + // We need to upgrade to a write lock. `std::sync::RwLock` doesn't support this, so we drop the guard and lock again + // Note that another thread might allocate the address while the `RwLock` is unlocked, but we handle this case in the allocation function. + drop(global_state); + let mut global_state = global_allocation_handler.0.write().unwrap(); + // With the write lock, we can safely allocate an address only once per `alloc_id`. + let new_addr = global_state.global_allocate_addr(alloc_id, info)?; + debug!("GenMC: global with alloc id {alloc_id:?} got address: {new_addr} == {new_addr:#x}"); + assert_eq!( + GENMC_GLOBAL_ADDRESSES_MASK, + new_addr & GENMC_GLOBAL_ADDRESSES_MASK, + "Global address allocated outside global address space." + ); + + interp_ok(new_addr) + } +} diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs new file mode 100644 index 0000000000000..b70ca8faadfed --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -0,0 +1,97 @@ +use genmc_sys::MemOrdering; +use rustc_abi::Size; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::ty::ScalarInt; +use tracing::debug; + +use super::GenmcScalar; +use crate::{AtomicReadOrd, AtomicWriteOrd, MiriInterpCx, Scalar, throw_unsup_format}; + +/// Maximum size memory access in bytes that GenMC supports. +pub(super) const MAX_ACCESS_SIZE: u64 = 8; + +/// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. +/// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. +pub fn split_access(address: Size, size: Size) -> impl Iterator { + let start_address = address.bytes(); + let end_address = start_address + size.bytes(); + + let start_address_aligned = start_address.next_multiple_of(MAX_ACCESS_SIZE); + let end_address_aligned = (end_address / MAX_ACCESS_SIZE) * MAX_ACCESS_SIZE; // prev_multiple_of + + debug!( + "GenMC: splitting NA memory access into {MAX_ACCESS_SIZE} byte chunks: {}B + {} * {MAX_ACCESS_SIZE}B + {}B = {size:?}", + start_address_aligned - start_address, + (end_address_aligned - start_address_aligned) / MAX_ACCESS_SIZE, + end_address - end_address_aligned, + ); + + // FIXME(genmc): could make remaining accesses powers-of-2, instead of 1 byte. + let start_chunks = (start_address..start_address_aligned).map(|address| (address, 1)); + let aligned_chunks = (start_address_aligned..end_address_aligned) + .step_by(MAX_ACCESS_SIZE.try_into().unwrap()) + .map(|address| (address, MAX_ACCESS_SIZE)); + let end_chunks = (end_address_aligned..end_address).map(|address| (address, 1)); + + start_chunks.chain(aligned_chunks).chain(end_chunks) +} + +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a Miri `Scalar` to a `GenmcScalar`. +/// To be able to restore pointer provenance from a `GenmcScalar`, the base address of the allocation of the pointer is also stored in the `GenmcScalar`. +/// We cannot use the `AllocId` instead of the base address, since Miri has no control over the `AllocId`, and it may change across executions. +/// Pointers with `Wildcard` provenance are not supported. +pub fn scalar_to_genmc_scalar<'tcx>( + _ecx: &MiriInterpCx<'tcx>, + scalar: Scalar, +) -> InterpResult<'tcx, GenmcScalar> { + interp_ok(match scalar { + rustc_const_eval::interpret::Scalar::Int(scalar_int) => { + // FIXME(genmc): Add u128 support once GenMC supports it. + let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); + GenmcScalar { value, is_init: true } + } + rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) => + throw_unsup_format!( + "FIXME(genmc): Implement sending pointers (with provenance) to GenMC." + ), + }) +} + +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a `GenmcScalar` back into a Miri `Scalar`. +/// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. +pub fn genmc_scalar_to_scalar<'tcx>( + _ecx: &MiriInterpCx<'tcx>, + scalar: GenmcScalar, + size: Size, +) -> InterpResult<'tcx, Scalar> { + // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion. + + // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. + // FIXME(genmc): GenMC should be doing the truncation, not Miri. + let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); + interp_ok(Scalar::Int(value_scalar_int)) +} + +impl AtomicReadOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicReadOrd::Relaxed => MemOrdering::Relaxed, + AtomicReadOrd::Acquire => MemOrdering::Acquire, + AtomicReadOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicWriteOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicWriteOrd::Relaxed => MemOrdering::Relaxed, + AtomicWriteOrd::Release => MemOrdering::Release, + AtomicWriteOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 3617775e27eaf..b5d20a439c01c 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -1,78 +1,171 @@ -#![allow(unused)] // FIXME(GenMC): remove this +use std::cell::{Cell, RefCell}; +use std::sync::Arc; -use std::cell::Cell; - -use genmc_sys::{GenmcParams, createGenmcHandle}; +use genmc_sys::{ + GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, UniquePtr, + create_genmc_driver_handle, +}; use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; -use rustc_middle::mir; - +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; +use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; +// FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). +use tracing::{debug, info}; + +use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; +use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; +use self::thread_id_map::ThreadIdMap; +use crate::concurrency::genmc::helper::split_access; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, + VisitWith, }; mod config; +mod global_allocations; +mod helper; +mod run; +pub(crate) mod scheduling; +mod thread_id_map; pub use self::config::GenmcConfig; +pub use self::run::run_genmc_mode; -// FIXME(GenMC): add fields -pub struct GenmcCtx { - /// Some actions Miri does are allowed to cause data races. - /// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set. +#[derive(Clone, Copy, Debug)] +pub enum ExitType { + MainThreadFinish, + ExitCalled, +} + +/// The exit status of a program. +/// GenMC must store this if a thread exits while any others can still run. +/// The other threads must also be explored before the program is terminated. +#[derive(Clone, Copy, Debug)] +struct ExitStatus { + exit_code: i32, + exit_type: ExitType, +} + +impl ExitStatus { + fn do_leak_check(self) -> bool { + matches!(self.exit_type, ExitType::MainThreadFinish) + } +} + +/// State that is reset at the start of every execution. +#[derive(Debug, Default)] +struct PerExecutionState { + /// Thread id management, such as mapping between Miri `ThreadId` and GenMC's thread ids, or selecting GenMC thread ids. + thread_id_manager: RefCell, + + /// A flag to indicate that we should not forward non-atomic accesses to genmc, e.g. because we + /// are executing an atomic operation. allow_data_races: Cell, + + /// The exit status of the program. We keep running other threads even after `exit` to ensure + /// we cover all possible executions. + /// `None` if no thread has called `exit` and the main thread isn't finished yet. + exit_status: Cell>, } -impl GenmcCtx { - /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig) -> Self { - let genmc_config = miri_config.genmc_config.as_ref().unwrap(); +impl PerExecutionState { + fn reset(&self) { + self.allow_data_races.replace(false); + self.thread_id_manager.borrow_mut().reset(); + self.exit_status.set(None); + } +} - let handle = createGenmcHandle(&genmc_config.params); - assert!(!handle.is_null()); +#[derive(Debug)] +struct GlobalState { + /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. + /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. + global_allocations: GlobalAllocationHandler, +} - eprintln!("Miri: GenMC handle creation successful!"); +impl GlobalState { + fn new(target_usize_max: u64) -> Self { + Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } + } +} - drop(handle); - eprintln!("Miri: Dropping GenMC handle successful!"); +/// The main interface with GenMC. +/// Each `GenmcCtx` owns one `MiriGenmcShim`, which owns one `GenMCDriver` (the GenMC model checker). +/// For each GenMC run (estimation or verification), one or more `GenmcCtx` can be created (one per Miri thread). +/// However, for now, we only ever have one `GenmcCtx` per run. +/// +/// In multithreading, each worker thread has its own `GenmcCtx`, which will have their results combined in the end. +/// FIXME(genmc): implement multithreading. +/// +/// Some data is shared across all `GenmcCtx` in the same run, namely data for global allocation handling. +/// Globals must be allocated in a consistent manner, i.e., each global allocation must have the same address in each execution. +/// +/// Some state is reset between each execution in the same run. +pub struct GenmcCtx { + /// Handle to the GenMC model checker. + handle: RefCell>, - // FIXME(GenMC): implement - std::process::exit(0); + /// State that is reset at the start of every execution. + exec_state: PerExecutionState, + + /// State that persists across executions. + /// All `GenmcCtx` in one verification step share this state. + global_state: Arc, +} + +/// GenMC Context creation and administrative / query actions +impl GenmcCtx { + /// Create a new `GenmcCtx` from a given config. + fn new(miri_config: &MiriConfig, global_state: Arc) -> Self { + let genmc_config = miri_config.genmc_config.as_ref().unwrap(); + let handle = + RefCell::new(create_genmc_driver_handle(&genmc_config.params, genmc_config.log_level)); + Self { handle, exec_state: Default::default(), global_state } } - pub fn get_stuck_execution_count(&self) -> usize { - todo!() + /// Get the number of blocked executions encountered by GenMC. + pub fn get_blocked_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().unwrap().get_blocked_execution_count() } - pub fn print_genmc_graph(&self) { - todo!() + /// Get the number of explored executions encountered by GenMC. + pub fn get_explored_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().unwrap().get_explored_execution_count() } - /// This function determines if we should continue exploring executions or if we are done. - /// - /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. - pub fn is_exploration_done(&self) -> bool { - todo!() + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn try_get_error(&self) -> Option { + let mc = self.handle.borrow(); + mc.as_ref() + .unwrap() + .get_error_string() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) } - /// Inform GenMC that a new program execution has started. - /// This function should be called at the start of every execution. - pub(crate) fn handle_execution_start(&self) { - todo!() + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn get_result_message(&self) -> String { + let mc = self.handle.borrow(); + mc.as_ref() + .unwrap() + .get_result_message() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) + .expect("there should always be a message") } - /// Inform GenMC that the program's execution has ended. + /// This function determines if we should continue exploring executions or if we are done. /// - /// This function must be called even when the execution got stuck (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). - pub(crate) fn handle_execution_end<'tcx>( - &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> Result<(), String> { - todo!() + /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. + pub fn is_exploration_done(&self) -> bool { + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().is_exploration_done() } - /**** Memory access handling ****/ - /// Select whether data race free actions should be allowed. This function should be used carefully! /// /// If `true` is passed, allow for data races to happen without triggering an error, until this function is called again with argument `false`. @@ -83,10 +176,59 @@ impl GenmcCtx { /// # Panics /// If data race free is attempted to be set more than once (i.e., no nesting allowed). pub(super) fn set_ongoing_action_data_race_free(&self, enable: bool) { - let old = self.allow_data_races.replace(enable); + debug!("GenMC: set_ongoing_action_data_race_free ({enable})"); + let old = self.exec_state.allow_data_races.replace(enable); assert_ne!(old, enable, "cannot nest allow_data_races"); } + /// Check whether data races are currently allowed (e.g., for loading values for validation which are not actually loaded by the program). + fn get_alloc_data_races(&self) -> bool { + self.exec_state.allow_data_races.get() + } +} + +/// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions. +impl GenmcCtx { + /// Prepare for the next execution and inform GenMC about it. + /// Must be called before at the start of every execution. + fn prepare_next_execution(&self) { + // Reset per-execution state. + self.exec_state.reset(); + // Inform GenMC about the new execution. + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_execution_start(); + } + + /// Inform GenMC that the program's execution has ended. + /// + /// This function must be called even when the execution is blocked + /// (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`). + /// Don't call this function if an error was found. + /// + /// GenMC detects certain errors only when the execution ends. + /// If an error occured, a string containing a short error description is returned. + /// + /// GenMC currently doesn't return an error in all cases immediately when one happens. + /// This function will also check for those, and return their error description. + /// + /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. + fn handle_execution_end(&self) -> Option { + let mut mc = self.handle.borrow_mut(); + let result = mc.as_mut().unwrap().handle_execution_end(); + result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; + + // GenMC currently does not return an error value immediately in all cases. + // We manually query for any errors here to ensure we don't miss any. + drop(mc); // `try_get_error` needs access to the `RefCell`. + self.try_get_error() + } + + /**** Memory access handling ****/ + + /// Inform GenMC about an atomic load. + /// Returns that value that the load should read. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_load<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -95,89 +237,143 @@ impl GenmcCtx { ordering: AtomicReadOrd, old_val: Option, ) -> InterpResult<'tcx, Scalar> { - assert!(!self.allow_data_races.get()); - todo!() + assert!(!self.get_alloc_data_races(), "atomic load with data race checking disabled."); + let genmc_old_value = if let Some(scalar) = old_val { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; + let read_value = + self.handle_load(&ecx.machine, address, size, ordering.to_genmc(), genmc_old_value)?; + genmc_scalar_to_scalar(ecx, read_value, size) } + /// Inform GenMC about an atomic store. + /// Returns `true` if the stored value should be reflected in Miri's memory. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_store<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, address: Size, size: Size, value: Scalar, + old_value: Option, ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx, bool> { + assert!(!self.get_alloc_data_races(), "atomic store with data race checking disabled."); + let genmc_value = scalar_to_genmc_scalar(ecx, value)?; + let genmc_old_value = if let Some(scalar) = old_value { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; + self.handle_store( + &ecx.machine, + address, + size, + genmc_value, + genmc_old_value, + ordering.to_genmc(), + ) } + /// Inform GenMC about an atomic fence. pub(crate) fn atomic_fence<'tcx>( &self, - machine: &MiriMachine<'tcx>, - ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + _machine: &MiriMachine<'tcx>, + _ordering: AtomicFenceOrd, + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); + throw_unsup_format!("FIXME(genmc): Add support for atomic fences.") } /// Inform GenMC about an atomic read-modify-write operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_rmw_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + (_rmw_op, _not): (mir::BinOp, bool), + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic read-modify-write operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") } /// Inform GenMC about an atomic `min` or `max` operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic min/max operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic min/max.") } + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - rhs_scalar: Scalar, - ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _rhs_scalar: Scalar, + _ordering: AtomicRwOrd, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic swap operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic swap.") } + /// Inform GenMC about an atomic compare-exchange operation. + /// + /// Returns the old value read by the compare exchange, optionally the value that Miri should write back to its memory, and whether the compare-exchange was a success or not. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_compare_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - expected_old_value: Scalar, - new_value: Scalar, - success: AtomicRwOrd, - fail: AtomicReadOrd, - can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _expected_old_value: Scalar, + _new_value: Scalar, + _success: AtomicRwOrd, + _fail: AtomicReadOrd, + _can_fail_spuriously: bool, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option, bool)> { + assert!( + !self.get_alloc_data_races(), + "atomic compare-exchange with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic compare_exchange.") } /// Inform GenMC about a non-atomic memory load @@ -188,40 +384,178 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + debug!( + "GenMC: received memory_load (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.get_alloc_data_races() { + debug!("GenMC: data race checking disabled, ignoring non-atomic load."); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + let handle_load = |address, size| { + // NOTE: Values loaded non-atomically are still handled by Miri, so we discard whatever we get from GenMC + let _read_value = self.handle_load( + machine, + address, + size, + MemOrdering::NotAtomic, + // This value is used to update the co-maximal store event to the same location. + // We don't need to update that store, since if it is ever read by any atomic loads, the value will be updated then. + // We use uninit for lack of a better value, since we don't know whether the location we currently load from is initialized or not. + GenmcScalar::UNINIT, + )?; + interp_ok(()) + }; + + // This load is small enough so GenMC can handle it. + if size.bytes() <= MAX_ACCESS_SIZE { + return handle_load(address, size); + } + + // This load is too big to be a single GenMC access, we have to split it. + // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there. + // Needs proper support on the GenMC side for large and mixed atomic accesses. + for (address, size) in split_access(address, size) { + handle_load(Size::from_bytes(address), Size::from_bytes(size))?; + } + interp_ok(()) } + /// Inform GenMC about a non-atomic memory store + /// + /// NOTE: Unlike for *atomic* stores, we don't provide the actual stored values to GenMC here. pub(crate) fn memory_store<'tcx>( &self, machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + debug!( + "GenMC: received memory_store (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.get_alloc_data_races() { + debug!("GenMC: data race checking disabled, ignoring non-atomic store."); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + let handle_store = |address, size| { + // We always write the the stored values to Miri's memory, whether GenMC says the write is co-maximal or not. + // The GenMC scheduler ensures that replaying an execution happens in porf-respecting order (po := program order, rf: reads-from order). + // This means that for any non-atomic read Miri performs, the corresponding write has already been replayed. + let _is_co_max_write = self.handle_store( + machine, + address, + size, + // We don't know the value that this store will write, but GenMC expects that we give it an actual value. + // Unfortunately, there are situations where this value can actually become visible + // to the program: when there is an atomic load reading from a non-atomic store. + // FIXME(genmc): update once mixed atomic-non-atomic support is added. Afterwards, this value should never be readable. + GenmcScalar::from_u64(0xDEADBEEF), + // This value is used to update the co-maximal store event to the same location. + // This old value cannot be read anymore by any future loads, since we are doing another non-atomic store to the same location. + // Any future load will either see the store we are adding now, or we have a data race (there can only be one possible non-atomic value to read from at any time). + // We use uninit for lack of a better value, since we don't know whether the location we currently write to is initialized or not. + GenmcScalar::UNINIT, + MemOrdering::NotAtomic, + )?; + interp_ok(()) + }; + + // This store is small enough so GenMC can handle it. + if size.bytes() <= MAX_ACCESS_SIZE { + return handle_store(address, size); + } + + // This store is too big to be a single GenMC access, we have to split it. + // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there. + // Needs proper support on the GenMC side for large and mixed atomic accesses. + for (address, size) in split_access(address, size) { + handle_store(Size::from_bytes(address), Size::from_bytes(size))?; + } + interp_ok(()) } /**** Memory (de)allocation ****/ + /// This is also responsible for determining the address of the new allocation. pub(crate) fn handle_alloc<'tcx>( &self, - machine: &MiriMachine<'tcx>, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + alloc_id: AllocId, size: Size, alignment: Align, memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { - todo!() + assert!( + !self.get_alloc_data_races(), + "memory allocation with data race checking disabled." + ); + let machine = &ecx.machine; + if memory_kind == MiriMemoryKind::Global.into() { + return ecx + .get_global_allocation_address(&self.global_state.global_allocations, alloc_id); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte + let genmc_size = size.bytes().max(1); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let chosen_address = pinned_mc.handle_malloc(genmc_tid, genmc_size, alignment.bytes()); + + // Non-global addresses should not be in the global address space or null. + assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); + assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); + // Sanity check the address alignment: + assert!( + chosen_address.is_multiple_of(alignment.bytes()), + "GenMC returned address {chosen_address:#x} with lower alignment than requested ({}).", + alignment.bytes() + ); + + interp_ok(chosen_address) } pub(crate) fn handle_dealloc<'tcx>( &self, machine: &MiriMachine<'tcx>, + alloc_id: AllocId, address: Size, - size: Size, - align: Align, kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + assert_ne!( + kind, + MiriMemoryKind::Global.into(), + "we probably shouldn't try to deallocate global allocations (alloc_id: {alloc_id:?})" + ); + assert!( + !self.get_alloc_data_races(), + "memory deallocation with data race checking disabled." + ); + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + pinned_mc.handle_free(genmc_tid, address.bytes()); + + interp_ok(()) } /**** Thread management ****/ @@ -229,50 +563,95 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, threads: &ThreadManager<'tcx>, + // FIXME(genmc,symmetry reduction): pass info to GenMC + _start_routine: crate::Pointer, + _func_arg: &crate::ImmTy<'tcx>, new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "thread creation with data race checking disabled."); + let mut thread_infos = self.exec_state.thread_id_manager.borrow_mut(); + + let curr_thread_id = threads.active_thread(); + let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); + let genmc_new_tid = thread_infos.add_thread(new_thread_id); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_create(genmc_new_tid, genmc_parent_tid); + + interp_ok(()) } pub(crate) fn handle_thread_join<'tcx>( &self, active_thread_id: ThreadId, child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() - } + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "thread join with data race checking disabled."); + let thread_infos = self.exec_state.thread_id_manager.borrow(); - pub(crate) fn handle_thread_stack_empty(&self, thread_id: ThreadId) { - todo!() - } + let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); + let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); - pub(crate) fn handle_thread_finish<'tcx>( - &self, - threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_join(genmc_curr_tid, genmc_child_tid); + + interp_ok(()) } - /**** Scheduling functionality ****/ + pub(crate) fn handle_thread_finish<'tcx>(&self, threads: &ThreadManager<'tcx>) { + assert!(!self.get_alloc_data_races(), "thread finish with data race checking disabled."); + let curr_thread_id = threads.active_thread(); - /// Ask for a scheduling decision. This should be called before every MIR instruction. - /// - /// GenMC may realize that the execution got stuck, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). - /// - /// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors. - pub(crate) fn schedule_thread<'tcx>( + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); + let mut mc = self.handle.borrow_mut(); + // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 + mc.as_mut().unwrap().handle_thread_finish(genmc_tid, /* ret_val */ 0); + } + + /// Handle a call to `libc::exit` or the exit of the main thread. + /// Unless an error is returned, the program should continue executing (in a different thread, chosen by the next scheduling call). + pub(crate) fn handle_exit<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - assert!(!self.allow_data_races.get()); - todo!() + thread: ThreadId, + exit_code: i32, + exit_type: ExitType, + ) -> InterpResult<'tcx> { + // Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case. + let exit_status = ExitStatus { exit_code, exit_type }; + + if let Some(old_exit_status) = self.exec_state.exit_status.get() { + throw_ub_format!( + "`exit` called twice, first with status {old_exit_status:?}, now with status {exit_status:?}", + ); + } + + // FIXME(genmc): Add a flag to continue exploration even when the program exits with a non-zero exit code. + if exit_code != 0 { + info!("GenMC: 'exit' called with non-zero argument, aborting execution."); + let leak_check = exit_status.do_leak_check(); + throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check }); + } + + if matches!(exit_type, ExitType::ExitCalled) { + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(thread); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_kill(genmc_tid); + } else { + assert_eq!(thread, ThreadId::MAIN_THREAD); + } + // We continue executing now, so we store the exit status. + self.exec_state.exit_status.set(Some(exit_status)); + interp_ok(()) } /**** Blocking instructions ****/ + #[allow(unused)] pub(crate) fn handle_verifier_assume<'tcx>( &self, machine: &MiriMachine<'tcx>, @@ -282,14 +661,119 @@ impl GenmcCtx { } } -impl VisitProvenance for GenmcCtx { - fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // We don't have any tags. +impl GenmcCtx { + /// Inform GenMC about a load (atomic or non-atomic). + /// Returns the value that GenMC wants this load to read. + fn handle_load<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + memory_ordering: MemOrdering, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, GenmcScalar> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > MAX_ACCESS_SIZE { + throw_unsup_format!( + "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes.", + ); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!( + "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", + addr = address.bytes() + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let load_result = pinned_mc.handle_load( + genmc_tid, + address.bytes(), + size.bytes(), + memory_ordering, + genmc_old_value, + ); + + if let Some(error) = load_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + if !load_result.has_value { + // FIXME(GenMC): Implementing certain GenMC optimizations will lead to this. + unimplemented!("GenMC: load returned no value."); + } + + debug!("GenMC: load returned value: {:?}", load_result.read_value); + interp_ok(load_result.read_value) } -} -impl GenmcCtx { - fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx, ()> { + /// Inform GenMC about a store (atomic or non-atomic). + /// Returns true if the store is co-maximal, i.e., it should be written to Miri's memory too. + fn handle_store<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + genmc_value: GenmcScalar, + genmc_old_value: GenmcScalar, + memory_ordering: MemOrdering, + ) -> InterpResult<'tcx, bool> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > MAX_ACCESS_SIZE { + throw_unsup_format!( + "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes." + ); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!( + "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", + addr = address.bytes() + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let store_result = pinned_mc.handle_store( + genmc_tid, + address.bytes(), + size.bytes(), + genmc_value, + genmc_old_value, + memory_ordering, + ); + + if let Some(error) = store_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + interp_ok(store_result.is_coherence_order_maximal_write) + } + + /**** Blocking functionality ****/ + + /// Handle a user thread getting blocked. + /// This may happen due to an manual `assume` statement added by a user + /// or added by some automated program transformation, e.g., for spinloops. + fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { todo!() } } + +impl VisitProvenance for GenmcCtx { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + // We don't have any tags. + } +} diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs new file mode 100644 index 0000000000000..62553297a2024 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -0,0 +1,71 @@ +use std::rc::Rc; +use std::sync::Arc; + +use rustc_middle::ty::TyCtxt; + +use super::GlobalState; +use crate::rustc_const_eval::interpret::PointerArithmetic; +use crate::{GenmcCtx, MiriConfig}; + +/// Do a complete run of the program in GenMC mode. +/// This will call `eval_entry` multiple times, until either: +/// - An error is detected (indicated by a `None` return value) +/// - All possible executions are explored. +/// +/// FIXME(genmc): add estimation mode setting. +pub fn run_genmc_mode<'tcx>( + config: &MiriConfig, + eval_entry: impl Fn(Rc) -> Option, + tcx: TyCtxt<'tcx>, +) -> Option { + // There exists only one `global_state` per full run in GenMC mode. + // It is shared by all `GenmcCtx` in this run. + // FIXME(genmc): implement multithreading once GenMC supports it. + let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); + let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); + + // `rep` is used to report the progress, Miri will panic on wrap-around. + for rep in 0u64.. { + tracing::info!("Miri-GenMC loop {}", rep + 1); + + // Prepare for the next execution and inform GenMC about it. + genmc_ctx.prepare_next_execution(); + + // Execute the program until completion to get the return value, or return if an error happens: + // FIXME(genmc): add an option to allow the user to see the GenMC output message when the verification is done. + let return_code = eval_entry(genmc_ctx.clone())?; + + // We inform GenMC that the execution is complete. If there was an error, we print it. + if let Some(error) = genmc_ctx.handle_execution_end() { + // This can be reached for errors that affect the entire execution, not just a specific event. + // For instance, linearizability checking and liveness checking report their errors this way. + // Neither are supported by Miri-GenMC at the moment though. However, GenMC also + // treats races on deallocation as global errors, so this code path is still reachable. + // Since we don't have any span information for the error at this point, + // we just print GenMC's error message. + eprintln!("(GenMC) Error detected: {error}"); + eprintln!(); + eprintln!("{}", genmc_ctx.get_result_message()); + return None; + } + + // Check if we've explored enough executions: + if !genmc_ctx.is_exploration_done() { + continue; + } + + eprintln!("(GenMC) Verification complete. No errors were detected."); + + let explored_execution_count = genmc_ctx.get_explored_execution_count(); + let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); + + eprintln!("Number of complete executions explored: {explored_execution_count}"); + if blocked_execution_count > 0 { + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + } + + // Return the return code of the last execution. + return Some(return_code); + } + unreachable!() +} diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs new file mode 100644 index 0000000000000..b5c23f2d0845f --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -0,0 +1,69 @@ +use genmc_sys::{ActionKind, ExecutionState}; + +use super::GenmcCtx; +use crate::{ + InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop, +}; + +impl GenmcCtx { + pub(crate) fn schedule_thread<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, ThreadId> { + let thread_manager = &ecx.machine.threads; + let active_thread_id = thread_manager.active_thread(); + + // Determine whether the next instruction in the current thread might be a load. + // This is used for the "writes-first" scheduling in GenMC. + // Scheduling writes before reads can be beneficial for verification performance. + // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. + let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().is_enabled() { + // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. + // Most to all blocking operations have load semantics, since they wait on something to change in another thread, + // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), + // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). + ActionKind::Load + } else { + // This thread is still enabled. If it executes a terminator next, we consider yielding, + // but in all other cases we just keep running this thread since it never makes sense + // to yield before a non-atomic operation. + let Some(frame) = thread_manager.active_thread_stack().last() else { + return interp_ok(active_thread_id); + }; + let either::Either::Left(loc) = frame.current_loc() else { + // We are unwinding, so the next step is definitely not atomic. + return interp_ok(active_thread_id); + }; + let basic_block = &frame.body().basic_blocks[loc.block]; + if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + // Statements can't be atomic. + return interp_ok(active_thread_id); + } + + // FIXME(genmc): determine terminator kind. + ActionKind::Load + }; + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(active_thread_id); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let result = pinned_mc.schedule_next(genmc_tid, curr_thread_next_instr_kind); + // Depending on the exec_state, we either schedule the given thread, or we are finished with this execution. + match result.exec_state { + ExecutionState::Ok => interp_ok(thread_infos.get_miri_tid(result.next_thread)), + ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution), + ExecutionState::Finished => { + let exit_status = self.exec_state.exit_status.get().expect( + "If the execution is finished, we should have a return value from the program.", + ); + throw_machine_stop!(TerminationInfo::Exit { + code: exit_status.exit_code, + leak_check: exit_status.do_leak_check() + }); + } + _ => unreachable!(), + } + } +} diff --git a/src/tools/miri/src/concurrency/genmc/thread_id_map.rs b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs new file mode 100644 index 0000000000000..9676496fe7fea --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs @@ -0,0 +1,63 @@ +use genmc_sys::GENMC_MAIN_THREAD_ID; +use rustc_data_structures::fx::FxHashMap; + +use crate::ThreadId; + +#[derive(Debug)] +pub struct ThreadIdMap { + /// Map from Miri thread IDs to GenMC thread IDs. + /// We assume as little as possible about Miri thread IDs, so we use a map. + miri_to_genmc: FxHashMap, + /// Map from GenMC thread IDs to Miri thread IDs. + /// We control which thread IDs are used, so we choose them in as an incrementing counter. + genmc_to_miri: Vec, // FIXME(genmc): check if this assumption is (and will stay) correct. +} + +impl Default for ThreadIdMap { + fn default() -> Self { + let miri_to_genmc = [(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID)].into_iter().collect(); + let genmc_to_miri = vec![ThreadId::MAIN_THREAD]; + Self { miri_to_genmc, genmc_to_miri } + } +} + +impl ThreadIdMap { + pub fn reset(&mut self) { + self.miri_to_genmc.clear(); + self.miri_to_genmc.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + self.genmc_to_miri.clear(); + self.genmc_to_miri.push(ThreadId::MAIN_THREAD); + } + + #[must_use] + /// Add a new Miri thread to the mapping and dispense a new thread ID for GenMC to use. + pub fn add_thread(&mut self, thread_id: ThreadId) -> i32 { + // NOTE: We select the new thread ids as integers incremented by one (we use the length as the counter). + let next_thread_id = self.genmc_to_miri.len(); + let genmc_tid = next_thread_id.try_into().unwrap(); + // If there is already an entry, we override it. + // This could happen if Miri were to reuse `ThreadId`s, but we assume that if this happens, the previous thread with that id doesn't exist anymore. + self.miri_to_genmc.insert(thread_id, genmc_tid); + self.genmc_to_miri.push(thread_id); + + genmc_tid + } + + #[must_use] + /// Try to get the GenMC thread ID corresponding to a given Miri `ThreadId`. + /// Panics if there is no mapping for the given `ThreadId`. + pub fn get_genmc_tid(&self, thread_id: ThreadId) -> i32 { + *self.miri_to_genmc.get(&thread_id).unwrap() + } + + #[must_use] + /// Get the Miri `ThreadId` corresponding to a given GenMC thread id. + /// Panics if the given thread id isn't valid. + pub fn get_miri_tid(&self, genmc_tid: i32) -> ThreadId { + let index: usize = genmc_tid.try_into().unwrap(); + self.genmc_to_miri + .get(index) + .copied() + .expect("A thread id returned from GenMC should exist.") + } +} diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 165215f9270ce..3fb3d890419f7 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Synchronize with the previous completion of an InitOnce. /// Must only be called after checking that it is complete. #[inline] - fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) { + fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let init_once = init_once_ref.0.borrow(); @@ -152,6 +152,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "observing the completion of incomplete init once" ); - this.acquire_clock(&init_once.clock); + this.acquire_clock(&init_once.clock) } } diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 435615efd9fa7..9dae858592f1f 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{GenmcConfig, GenmcCtx}; +pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, run_genmc_mode}; pub use self::vector_clock::VClock; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index fe1ef86ccd31e..00c5e337b1e9e 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -209,6 +209,11 @@ impl<'tcx> Thread<'tcx> { self.thread_name.as_deref() } + /// Return whether this thread is enabled or not. + pub fn is_enabled(&self) -> bool { + self.state.is_enabled() + } + /// Get the name of the current thread for display purposes; will include thread ID if not set. fn thread_display_name(&self, id: ThreadId) -> String { if let Some(ref thread_name) = self.thread_name { @@ -404,6 +409,7 @@ pub struct ThreadManager<'tcx> { /// A mapping from a thread-local static to the thread specific allocation. thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>, /// A flag that indicates that we should change the active thread. + /// Completely ignored in GenMC mode. yield_active_thread: bool, /// A flag that indicates that we should do round robin scheduling of threads else randomized scheduling is used. fixed_scheduling: bool, @@ -676,13 +682,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { #[inline] fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); - // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling. - // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we - // support pre-main constructors, it can get called there as well. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let thread_id = this.active_thread(); - genmc_ctx.handle_thread_stack_empty(thread_id); - } let mut callback = this .active_thread_mut() .on_stack_empty @@ -703,19 +702,19 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// If GenMC mode is active, the scheduling is instead handled by GenMC. fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> { let this = self.eval_context_mut(); - // In GenMC mode, we let GenMC do the scheduling + + // In GenMC mode, we let GenMC do the scheduling. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { let next_thread_id = genmc_ctx.schedule_thread(this)?; let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; - thread_manager.yield_active_thread = false; assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); return interp_ok(SchedulingAction::ExecuteStep); } - // We are not in GenMC mode, so we control the schedule + // We are not in GenMC mode, so we control the scheduling. let thread_manager = &mut this.machine.threads; let clock = &this.machine.monotonic_clock; let rng = this.machine.rng.get_mut(); @@ -863,7 +862,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { GlobalDataRaceHandler::Vclocks(data_race) => data_race.thread_created(&this.machine.threads, new_thread_id, current_span), GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_create(&this.machine.threads, new_thread_id)?, + genmc_ctx.handle_thread_create( + &this.machine.threads, + start_routine, + &func_arg, + new_thread_id, + )?, } // Write the current thread-id, switch to the next thread later // to treat this write operation as occurring on the current thread. @@ -916,13 +920,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.active_thread_mut(); assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); thread.state = ThreadState::Terminated; - match &mut this.machine.data_race { - GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Vclocks(data_race) => - data_race.thread_terminated(&this.machine.threads), - GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_finish(&this.machine.threads)?, - } + // Deallocate TLS. let gone_thread = this.active_thread(); { @@ -953,6 +951,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } + + match &mut this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.thread_terminated(&this.machine.threads), + GlobalDataRaceHandler::Genmc(genmc_ctx) => { + // Inform GenMC that the thread finished. + // This needs to happen once all accesses to the thread are done, including freeing any TLS statics. + genmc_ctx.handle_thread_finish(&this.machine.threads) + } + } + // Unblock joining threads. let unblock_reason = BlockReason::Join(gone_thread); let threads = &this.machine.threads.threads; @@ -978,6 +988,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); + if timeout.is_some() && this.machine.data_race.as_genmc_ref().is_some() { + panic!("Unimplemented: Timeouts not yet supported in GenMC mode."); + } let timeout = timeout.map(|(clock, anchor, duration)| { let anchor = match clock { TimeoutClock::RealTime => { @@ -1076,6 +1089,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "{:?} blocked on {:?} when trying to join", thread_mgr.active_thread, joined_thread_id ); + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?; + } + // The joined thread is still running, we need to wait for it. // Once we get unblocked, perform the appropriate synchronization and write the return value. let dest = return_dest.clone(); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 9ecbd31c5b9f9..34f560962e662 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -31,8 +31,9 @@ pub enum TerminationInfo { }, Int2PtrWithStrictProvenance, Deadlock, - /// In GenMC mode, an execution can get stuck in certain cases. This is not an error. - GenmcStuckExecution, + /// In GenMC mode, executions can get blocked, which stops the current execution without running any cleanup. + /// No leak checks should be performed if this happens, since they would give false positives. + GenmcBlockedExecution, MultipleSymbolDefinitions { link_name: Symbol, first: SpanData, @@ -77,7 +78,8 @@ impl fmt::Display for TerminationInfo { StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), Deadlock => write!(f, "the evaluated program deadlocked"), - GenmcStuckExecution => write!(f, "GenMC determined that the execution got stuck"), + GenmcBlockedExecution => + write!(f, "GenMC determined that the execution got blocked (this is not an error)"), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -243,11 +245,12 @@ pub fn report_error<'tcx>( labels.push(format!("this thread got stuck here")); None } - GenmcStuckExecution => { - // This case should only happen in GenMC mode. We treat it like a normal program exit. + GenmcBlockedExecution => { + // This case should only happen in GenMC mode. assert!(ecx.machine.data_race.as_genmc_ref().is_some()); - tracing::info!("GenMC: found stuck execution"); - return Some((0, true)); + // The program got blocked by GenMC without finishing the execution. + // No cleanup code was executed, so we don't do any leak checks. + return Some((0, false)); } MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None, }; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 4c531a8d1f526..e6c2163105ad1 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -303,8 +303,21 @@ impl<'tcx> MainThreadState<'tcx> { // to be like a global `static`, so that all memory reached by it is considered to "not leak". this.terminate_active_thread(TlsAllocAction::Leak)?; - // Stop interpreter loop. - throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); + // In GenMC mode, we do not immediately stop execution on main thread exit. + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there's no error, execution will continue (on another thread). + genmc_ctx.handle_exit( + ThreadId::MAIN_THREAD, + exit_code, + crate::concurrency::ExitType::MainThreadFinish, + )?; + } else { + // Stop interpreter loop. + throw_machine_stop!(TerminationInfo::Exit { + code: exit_code, + leak_check: true + }); + } } } interp_ok(Poll::Pending) @@ -505,10 +518,6 @@ pub fn eval_entry<'tcx>( // Copy setting before we move `config`. let ignore_leaks = config.ignore_leaks; - if let Some(genmc_ctx) = &genmc_ctx { - genmc_ctx.handle_execution_start(); - } - let mut ecx = match create_ecx(tcx, entry_id, entry_type, config, genmc_ctx).report_err() { Ok(v) => v, Err(err) => { @@ -532,15 +541,6 @@ pub fn eval_entry<'tcx>( // Show diagnostic, if any. let (return_code, leak_check) = report_error(&ecx, err)?; - // We inform GenMC that the execution is complete. - if let Some(genmc_ctx) = ecx.machine.data_race.as_genmc_ref() - && let Err(error) = genmc_ctx.handle_execution_end(&ecx) - { - // FIXME(GenMC): Improve error reporting. - tcx.dcx().err(format!("GenMC returned an error: \"{error}\"")); - return None; - } - // If we get here there was no fatal error. // Possibly check for memory leaks. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 507d4f7b42896..a9279bbefd8fd 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -133,7 +133,7 @@ pub use crate::concurrency::thread::{ BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, }; -pub use crate::concurrency::{GenmcConfig, GenmcCtx}; +pub use crate::concurrency::{GenmcConfig, GenmcCtx, run_genmc_mode}; pub use crate::data_structures::dedup_range_map::DedupRangeMap; pub use crate::data_structures::mono_hash_map::MonoHashMap; pub use crate::diagnostics::{ diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 9482769a2fc79..4bb6dd2aafe02 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1427,9 +1427,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } match &machine.data_race { GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Genmc(genmc_ctx) => { - genmc_ctx.memory_store(machine, ptr.addr(), range.size)?; - } + GlobalDataRaceHandler::Genmc(genmc_ctx) => + genmc_ctx.memory_store(machine, ptr.addr(), range.size)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &mut alloc_extra.data_race @@ -1465,7 +1464,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { match &machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?, + genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap(); data_race.write( diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 21545b680299c..c6bc29cb8cb47 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -443,13 +443,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there is no error, execution should continue (on a different thread). + genmc_ctx.handle_exit( + this.machine.threads.active_thread(), + code, + crate::concurrency::ExitType::ExitCalled, + )?; + todo!(); // FIXME(genmc): Add a way to return here that is allowed to not do progress (can't use existing EmulateItemResult variants). + } throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } "abort" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() - )) + )); } // Standard C allocation diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index d460abc783dd5..aac880feda40e 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -639,7 +639,7 @@ fn return_ready_list<'tcx>( &des.1, )?; // Synchronize waking thread with the event of interest. - ecx.acquire_clock(&epoll_event_instance.clock); + ecx.acquire_clock(&epoll_event_instance.clock)?; num_of_events = num_of_events.strict_add(1); } else { diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 2d35ef064db8b..ad8ea6410264f 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -299,7 +299,7 @@ fn eventfd_read<'tcx>( ); } else { // Synchronize with all prior `write` calls to this FD. - ecx.acquire_clock(&eventfd.clock.borrow()); + ecx.acquire_clock(&eventfd.clock.borrow())?; // Return old counter value into user-space buffer. ecx.write_int(counter, &buf_place)?; diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 817ddd7954df4..52073b58e70de 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -337,7 +337,7 @@ fn anonsocket_read<'tcx>( // Synchronize with all previous writes to this buffer. // FIXME: this over-synchronizes; a more precise approach would be to // only sync with the writes whose data we will read. - ecx.acquire_clock(&readbuf.clock); + ecx.acquire_clock(&readbuf.clock)?; // Do full read / partial read based on the space available. // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 9165e76b63d14..a893999ef8e52 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -60,7 +60,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { true } InitOnceStatus::Complete => { - this.init_once_observe_completed(init_once_ref); + this.init_once_observe_completed(init_once_ref)?; this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?; this.write_scalar(this.eval_windows("c", "TRUE"), dest)?; true diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr new file mode 100644 index 0000000000000..121ded2a181ca --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr new file mode 100644 index 0000000000000..121ded2a181ca --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr new file mode 100644 index 0000000000000..121ded2a181ca --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs new file mode 100644 index 0000000000000..1568a302f85ad --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: rlx_rlx rlx_acq rel_rlx + +// Translated from GenMC's test `wrong/racy/MP+rel+rlx`, `MP+rlx+acq` and `MP+rlx+rlx`. +// Test if Miri with GenMC can detect the data race on `X`. +// Relaxed orderings on an atomic store-load pair should not synchronize the non-atomic write to X, leading to a data race. + +// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{self, *}; + +use genmc::spawn_pthread_closure; + +static mut X: u64 = 0; +static Y: AtomicUsize = AtomicUsize::new(0); + +const STORE_ORD: Ordering = if cfg!(rel_rlx) { Release } else { Relaxed }; +const LOAD_ORD: Ordering = if cfg!(rlx_acq) { Acquire } else { Relaxed }; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X = 1; + Y.store(1, STORE_ORD); + }); + spawn_pthread_closure(|| { + if Y.load(LOAD_ORD) != 0 { + X = 2; //~ ERROR: Undefined Behavior: Non-atomic race + } + }); + } + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr new file mode 100644 index 0000000000000..e2b114df652f8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr new file mode 100644 index 0000000000000..e2b114df652f8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs new file mode 100644 index 0000000000000..dea2b5f952e7e --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs @@ -0,0 +1,73 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: sc3_rel1 release4 relaxed4 + +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// +// This test has multiple variants using different memory orderings. +// When using any combination of orderings except using all 4 `SeqCst`, the memory model allows the program to result in (X, Y) == (1, 1). +// The "pass" variants only check that we get the expected number of executions (3 for all SC, 4 otherwise), +// and a valid outcome every execution, but do not check that we get all allowed results. +// This "fail" variant ensures we can explore the execution resulting in (1, 1), an incorrect unsafe assumption that the result (1, 1) is impossible. +// +// Miri without GenMC is unable to produce this program execution and thus detect the incorrect assumption, even with `-Zmiri-many-seeds`. +// +// To get good coverage, we test the combination with the strongest orderings allowing this result (3 `SeqCst`, 1 `Release`), +// the weakest orderings (4 `Relaxed`), and one in between (4 `Release`). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::{self, *}; + +use crate::genmc::{join_pthreads, spawn_pthread_closure}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +// Strongest orderings allowing result (1, 1). +#[cfg(seqcst_rel)] +const STORE_ORD_3: Ordering = SeqCst; +#[cfg(seqcst_rel)] +const STORE_ORD_1: Ordering = Release; + +// 4 * `Release`. +#[cfg(acqrel)] +const STORE_ORD_3: Ordering = Release; +#[cfg(acqrel)] +const STORE_ORD_1: Ordering = Release; + +// Weakest orderings (4 * `Relaxed`). +#[cfg(not(any(acqrel, seqcst_rel)))] +const STORE_ORD_3: Ordering = Relaxed; +#[cfg(not(any(acqrel, seqcst_rel)))] +const STORE_ORD_1: Ordering = Relaxed; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, STORE_ORD_3); + Y.store(2, STORE_ORD_3); + }), + spawn_pthread_closure(|| { + Y.store(1, STORE_ORD_1); + X.store(2, STORE_ORD_3); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // We mark the result (1, 1) as unreachable, which is incorrect. + let result = (X.load(Relaxed), Y.load(Relaxed)); + if result == (1, 1) { + // FIXME(genmc): Use `std::process::abort()` once backtraces for that are improved (https://github.com/rust-lang/rust/pull/146118) + std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr new file mode 100644 index 0000000000000..e2b114df652f8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs new file mode 100644 index 0000000000000..d3fdb470ed367 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs @@ -0,0 +1,51 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2CoWR". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = Y.load(Acquire); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + Y.store(1, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs new file mode 100644 index 0000000000000..22fe9524c37f5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: release1 release2 + +// Translated from GenMC's test "2+2W+3sc+rel1" and "2+2W+3sc+rel2" (two variants that swap which store is `Release`). +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }), + // Variant 1: `Release` goes first. + #[cfg(release1)] + spawn_pthread_closure(|| { + Y.store(1, Release); + X.store(2, SeqCst); + }), + // Variant 2: `Release` goes second. + #[cfg(not(release1))] + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs new file mode 100644 index 0000000000000..bbe9995dea0b2 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W". +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + Y.store(1, Release); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs new file mode 100644 index 0000000000000..c5711ba04fce2 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W+4c". +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }), + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + X.store(2, SeqCst); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (2, 1) | (2, 2) | (1, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs new file mode 100644 index 0000000000000..79e7c7feb7aac --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -0,0 +1,64 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "IRIW-acq-sc" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + Y.load(SeqCst); + }), + spawn_pthread_closure(|| { + b = Y.load(Acquire); + c = X.load(SeqCst); + }), + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!( + (a, b, c), + (0, 0, 0) + | (0, 0, 1) + | (0, 1, 0) + | (0, 1, 1) + | (1, 0, 0) + | (1, 0, 1) + | (1, 1, 0) + | (1, 1, 1) + ) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr new file mode 100644 index 0000000000000..c760b44605112 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 16 diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.rs b/src/tools/miri/tests/genmc/pass/litmus/LB.rs new file mode 100644 index 0000000000000..1cee3230b127c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/LB" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = Y.load(Acquire); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + Y.store(1, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 2) | (1, 0)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.rs b/src/tools/miri/tests/genmc/pass/litmus/MP.rs new file mode 100644 index 0000000000000..e245cdd15eef2 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + a = Y.load(Acquire); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.rs b/src/tools/miri/tests/genmc/pass/litmus/SB.rs new file mode 100644 index 0000000000000..e592fe05c4e49 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/SB" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + a = Y.load(Acquire); + }), + spawn_pthread_closure(|| { + Y.store(1, Release); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.rs b/src/tools/miri/tests/genmc/pass/litmus/corr.rs new file mode 100644 index 0000000000000..d6c95100fc241 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr new file mode 100644 index 0000000000000..be75e68fde77d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs new file mode 100644 index 0000000000000..f722131fda846 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs @@ -0,0 +1,46 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR0" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs new file mode 100644 index 0000000000000..a4e8249bac309 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs @@ -0,0 +1,58 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR1" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + c = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (only 0, 1, 2 are allowed): + if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2)) { + std::process::abort(); + } + // The 36 possible program executions can have 21 different results for (a, b, c). + // Of the 27 = 3*3*3 total results for (a, b, c), + // those where `a != 0` and `b == 0` are not allowed by the memory model. + // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0. + if a != 0 && b == 0 { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr new file mode 100644 index 0000000000000..f6d07e9c77b2b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs new file mode 100644 index 0000000000000..2f490d3637797 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs @@ -0,0 +1,70 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR2" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let mut d = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + c = X.load(Acquire); + d = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (only 0, 1, 2 are allowed): + if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2) && matches!(d, 0..=2)) { + std::process::abort(); + } + + // The 72 possible program executions can have 47 different results for (a, b, c, d). + // Of the 81 = 3*3*3*3 total results for (a, b, c, d), + // those where `a != 0` and `b == 0` are not allowed by the memory model. + // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0. + // The same applies to `c, d` in the other thread. + // + // Additionally, if one thread reads `1, 2` or `2, 1`, the other thread cannot see the opposite order. + if a != 0 && b == 0 { + std::process::abort(); + } else if c != 0 && d == 0 { + std::process::abort(); + } else if (a, b) == (1, 2) && (c, d) == (2, 1) { + std::process::abort(); + } else if (a, b) == (2, 1) && (c, d) == (1, 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr new file mode 100644 index 0000000000000..78a90b63feab6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.rs b/src/tools/miri/tests/genmc/pass/litmus/corw.rs new file mode 100644 index 0000000000000..7acc20822a4f4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRW" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = X.load(Acquire); + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (the load cannot read `1`): + if !matches!(a, 0 | 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs new file mode 100644 index 0000000000000..1c51f23a09c66 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoWR" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let mut a = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + a = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (the load cannot read `0`): + if !matches!(a, 1 | 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.rs b/src/tools/miri/tests/genmc/pass/litmus/default.rs new file mode 100644 index 0000000000000..55fb1ac34acb4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/default.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/default" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 1) | (2, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.stderr b/src/tools/miri/tests/genmc/pass/litmus/default.stderr new file mode 100644 index 0000000000000..e0313930282ea --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/default.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr new file mode 100644 index 0000000000000..15017249dc3a7 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 9 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr new file mode 100644 index 0000000000000..15017249dc3a7 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 9 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.rs b/src/tools/miri/tests/genmc/pass/litmus/detour.rs new file mode 100644 index 0000000000000..7136c029bbb54 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.rs @@ -0,0 +1,68 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: join no_join + +// Translated from GenMC's "litmus/detour" test. + +// This test has two revisitions to test whether we get the same result +// independent of whether we join the spawned threads or not. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicI64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicI64 = AtomicI64::new(0); +static Y: AtomicI64 = AtomicI64::new(0); +static Z: AtomicI64 = AtomicI64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + // Make these static so we can exit the main thread while the other threads still run. + // If these are `let mut` like the other tests, this will cause a use-after-free bug. + static mut A: i64 = 1234; + static mut B: i64 = 1234; + static mut C: i64 = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + A = Z.load(Relaxed); + X.store(A.wrapping_sub(1), Relaxed); + B = X.load(Relaxed); + Y.store(B, Relaxed); + }), + spawn_pthread_closure(|| { + C = Y.load(Relaxed); + Z.store(C, Relaxed); + }), + ]; + + // The `no_join` revision doesn't join any of the running threads to test that + // we still explore the same number of executions in that case. + if cfg!(no_join) { + return 0; + } + + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((A, B, C), (0, 1, 0) | (0, -1, 0) | (0, 1, 1) | (0, -1, -1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs new file mode 100644 index 0000000000000..9a08d517b7c60 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "fr+w+w+w+reads" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut result = [1234; 4]; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(2, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(3, Relaxed); + }), + spawn_pthread_closure(|| { + result[0] = X.load(Relaxed); + result[1] = X.load(Relaxed); + result[2] = X.load(Relaxed); + result[3] = X.load(Relaxed); + }), + ]; + + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + for val in result { + if !matches!(val, 0..=3) { + std::process::abort(); + } + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr new file mode 100644 index 0000000000000..3b6ba238f5342 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 210 diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs deleted file mode 100644 index f621bd9114fa4..0000000000000 --- a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@compile-flags: -Zmiri-genmc - -#![no_main] - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr deleted file mode 100644 index 4b7aa824bd122..0000000000000 --- a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr +++ /dev/null @@ -1,5 +0,0 @@ -warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode. -C++: GenMC handle created! -Miri: GenMC handle creation successful! -C++: GenMC handle destroyed! -Miri: Dropping GenMC handle successful! diff --git a/src/tools/miri/tests/utils/genmc.rs b/src/tools/miri/tests/utils/genmc.rs new file mode 100644 index 0000000000000..fb6334bd06086 --- /dev/null +++ b/src/tools/miri/tests/utils/genmc.rs @@ -0,0 +1,57 @@ +#![allow(unused)] + +use std::ffi::c_void; + +use libc::{self, pthread_attr_t, pthread_t}; + +/// Spawn a thread using `pthread_create`, abort the process on any errors. +pub unsafe fn spawn_pthread( + f: extern "C" fn(*mut c_void) -> *mut c_void, + value: *mut c_void, +) -> pthread_t { + let mut thread_id: pthread_t = 0; + + let attr: *const pthread_attr_t = std::ptr::null(); + + if unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } != 0 { + std::process::abort(); + } + thread_id +} + +/// Unsafe because we do *not* check that `F` is `Send + 'static`. +/// That makes it much easier to write tests... +pub unsafe fn spawn_pthread_closure(f: F) -> pthread_t { + let mut thread_id: pthread_t = 0; + let attr: *const pthread_attr_t = std::ptr::null(); + let f = Box::new(f); + extern "C" fn thread_func(f: *mut c_void) -> *mut c_void { + let f = unsafe { Box::from_raw(f as *mut F) }; + f(); + std::ptr::null_mut() + } + if unsafe { + libc::pthread_create( + &raw mut thread_id, + attr, + thread_func::, + Box::into_raw(f) as *mut c_void, + ) + } != 0 + { + std::process::abort(); + } + thread_id +} + +// Join the given pthread, abort the process on any errors. +pub unsafe fn join_pthread(thread_id: pthread_t) { + if unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } != 0 { + std::process::abort(); + } +} + +// Join the `N` given pthreads, abort the process on any errors. +pub unsafe fn join_pthreads(thread_ids: [pthread_t; N]) { + let _ = thread_ids.map(|id| join_pthread(id)); +} From 1bf57df7d72333bb4289d8f7072e8e170ad54ce2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 3 Sep 2025 21:14:19 +0800 Subject: [PATCH 0197/1900] Add applicable on bang `!` for apply_demorgan Example --- ```rust fn f() { $0!(1 || 3 && 4 || 5) } ``` -> ```rust fn f() { !1 && !(3 && 4) && !5 } ``` --- .../src/handlers/apply_demorgan.rs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 753a9e56c35ac..53a0a11998a0f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -6,7 +6,7 @@ use ide_db::{ syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; use syntax::{ - SyntaxKind, T, + NodeOrToken, SyntaxKind, T, ast::{ self, AstNode, Expr::BinExpr, @@ -38,15 +38,27 @@ use crate::{AssistContext, AssistId, Assists, utils::invert_boolean_expression}; // } // ``` pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let mut bin_expr = ctx.find_node_at_offset::()?; + let mut bin_expr = if let Some(not) = ctx.find_token_syntax_at_offset(T![!]) + && let Some(NodeOrToken::Node(next)) = not.next_sibling_or_token() + && let Some(paren) = ast::ParenExpr::cast(next) + && let Some(ast::Expr::BinExpr(bin_expr)) = paren.expr() + { + bin_expr + } else { + let bin_expr = ctx.find_node_at_offset::()?; + let op_range = bin_expr.op_token()?.text_range(); + + // Is the cursor on the expression's logical operator? + if !op_range.contains_range(ctx.selection_trimmed()) { + return None; + } + + bin_expr + }; + let op = bin_expr.op_kind()?; let op_range = bin_expr.op_token()?.text_range(); - // Is the cursor on the expression's logical operator? - if !op_range.contains_range(ctx.selection_trimmed()) { - return None; - } - // Walk up the tree while we have the same binary operator while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) { match parent_expr.op_kind() { @@ -366,6 +378,15 @@ fn f() { !(S <= S || S < S) } ) } + #[test] + fn demorgan_on_not() { + check_assist( + apply_demorgan, + "fn f() { $0!(1 || 3 && 4 || 5) }", + "fn f() { !1 && !(3 && 4) && !5 }", + ) + } + #[test] fn demorgan_keep_pars_for_op_precedence() { check_assist( From 9adc741bc4216d9f7c99ba25946372b7124db8b1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 3 Sep 2025 21:46:06 +0200 Subject: [PATCH 0198/1900] rename primary git branch --- src/doc/rustc-dev-guide/.github/workflows/ci.yml | 2 +- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 2 +- src/doc/rustc-dev-guide/book.toml | 2 +- src/doc/rustc-dev-guide/ci/linkcheck.sh | 2 +- src/doc/rustc-dev-guide/src/rustc-driver/intro.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 2dd695b7a4733..79d03080dce38 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main pull_request: schedule: # Run multiple times a day as the successfull cached links are not checked every time. diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 5ff3118960da6..fd21b149db8f1 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -14,7 +14,7 @@ jobs: github-app-id: ${{ vars.APP_CLIENT_ID }} zulip-stream-id: 196385 zulip-bot-email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" - pr-base-branch: master + pr-base-branch: main branch-name: rustc-pull secrets: zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index daf237ed9081b..efb13101c8de2 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -11,7 +11,7 @@ command = "mdbook-mermaid" [output.html] git-repository-url = "https://github.com/rust-lang/rustc-dev-guide" -edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/master/%7Bpath%7D" +edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/main/%7Bpath%7D" additional-js = [ "mermaid.min.js", "mermaid-init.js", diff --git a/src/doc/rustc-dev-guide/ci/linkcheck.sh b/src/doc/rustc-dev-guide/ci/linkcheck.sh index e5184839be02f..867b427eafbbe 100755 --- a/src/doc/rustc-dev-guide/ci/linkcheck.sh +++ b/src/doc/rustc-dev-guide/ci/linkcheck.sh @@ -32,7 +32,7 @@ elif [ "$GITHUB_EVENT_NAME" = "pull_request" ] ; then # running in PR CI build echo "Checking files changed since $BASE_SHA: $CHANGED_FILES" else # running locally - COMMIT_RANGE=master... + COMMIT_RANGE=main... CHANGED_FILES=$(git diff --name-only $COMMIT_RANGE | sed 's#^src/##' | tr '\n' ' ') FLAGS="-f $CHANGED_FILES" diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index a3684397b2995..32d27d218197d 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -34,7 +34,7 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/ [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html [`Callbacks`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html -[example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-interface-example.rs +[example]: https://github.com/rust-lang/rustc-dev-guide/blob/main/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html [rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html [rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html From 891523d0de6792c132e83c4833b678543ecf5d19 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 28 Aug 2025 07:17:06 +0800 Subject: [PATCH 0199/1900] fix: `read_zero_byte_vec` suggests wrongly inside `let` stmt --- clippy_lints/src/read_zero_byte_vec.rs | 27 ++++++++--- tests/ui/read_zero_byte_vec.rs | 27 +++++++++++ tests/ui/read_zero_byte_vec.stderr | 65 ++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index acd840401c6bb..b8d4e7c4651d2 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::source::snippet; +use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{get_enclosing_block, sym}; use rustc_errors::Applicability; @@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", - format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")), + format!("{ident}.resize({len}, 0);\n{}", " ".repeat(indent)), applicability, ); }, @@ -100,14 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", format!( - "{}.resize({}, 0); {}", - ident, + "{ident}.resize({}, 0);\n{}", snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") + " ".repeat(indent) ), applicability, ); @@ -130,6 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } +fn first_stmt_containing_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx hir::Stmt<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let hir::Node::Stmt(stmt) = node { + Some(stmt) + } else { + None + } + }) +} + struct ReadVecVisitor<'tcx> { local_id: HirId, read_zero_expr: Option<&'tcx Expr<'tcx>>, diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 938d61b68607c..720276cb55488 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -120,3 +120,30 @@ fn allow_works(mut f: F) { } fn main() {} + +fn issue15575() -> usize { + use std::io::Read; + use std::net::TcpListener; + + let listener = TcpListener::bind("127.0.0.1:9010").unwrap(); + let mut stream_and_addr = listener.accept().unwrap(); + let mut buf = Vec::with_capacity(32); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + //~^ read_zero_byte_vec + + use std::fs::File; + let mut f = File::open("foo.txt").unwrap(); + let mut data = Vec::with_capacity(100); + f.read(&mut data).unwrap() + //~^ read_zero_byte_vec +} diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 8f255bc87ab82..8dd74592e4c15 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -2,16 +2,27 @@ error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:22:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` +help: try + | +LL ~ data.resize(20, 0); +LL ~ f.read_exact(&mut data).unwrap(); + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:28:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data2.resize(cap, 0); +LL ~ f.read_exact(&mut data2)?; + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:33:5 @@ -67,5 +78,53 @@ error: reading zero byte data to `Vec` LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:131:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(32, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:136:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:141:32 + | +LL | let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:147:5 + | +LL | f.read(&mut data).unwrap() + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data.resize(100, 0); +LL ~ f.read(&mut data).unwrap() + | + +error: aborting due to 15 previous errors From d254020f3c4adc9d9bfd7902938e77fa0de91cfe Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 00:44:43 +0200 Subject: [PATCH 0200/1900] misc --- clippy_lints/src/matches/collapsible_match.rs | 28 +++++++++---------- tests/ui/collapsible_match.rs | 24 ++++++++-------- tests/ui/collapsible_match.stderr | 12 ++++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..11e79333a6948 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -50,15 +50,17 @@ fn check_arm<'tcx>( if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els, _) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) - // if there are more than two arms, collapsing would be non-trivial - // one of the arms must be "wild-like" - && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) - { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None + IfLetOrMatch::Match(scrutinee, arms, ..) => { + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) + // if there are more than two arms, collapsing would be non-trivial + // one of the arms must be "wild-like" + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } }, } && outer_pat.span.eq_ctxt(inner_scrutinee.span) @@ -68,8 +70,8 @@ fn check_arm<'tcx>( && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) + && let (Some(binding_span), is_innermost_parent_pat_struct) = + find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { (None, None) => true, @@ -77,9 +79,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), } // the binding must not be used in the if guard - && outer_guard.is_none_or( - |e| !is_local_used(cx, e, binding_id) - ) + && outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id)) // ...or anywhere in the inner expression && match inner { IfLetOrMatch::IfLet(_, _, body, els, _) => { diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 8931a3aa09c61..7a9c8d14d13fb 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -304,18 +304,6 @@ pub fn test_2(x: Issue9647) { } } -// https://github.com/rust-lang/rust-clippy/issues/14281 -fn lint_emitted_at_right_node(opt: Option>) { - let n = match opt { - #[expect(clippy::collapsible_match)] - Some(n) => match n { - Ok(n) => n, - _ => return, - }, - None => return, - }; -} - pub fn issue_14155() { let mut arr = ["a", "b", "c"]; if let Some(last) = arr.last() { @@ -357,6 +345,18 @@ pub fn issue_14155() { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 14b1c1b187e4f..ebde46cf8a725 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -251,7 +251,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:322:9 + --> tests/ui/collapsible_match.rs:310:9 | LL | / match *last { LL | | @@ -263,7 +263,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:321:17 + --> tests/ui/collapsible_match.rs:309:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +274,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:332:9 + --> tests/ui/collapsible_match.rs:320:9 | LL | / match &last { LL | | @@ -286,7 +286,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:331:17 + --> tests/ui/collapsible_match.rs:319:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +297,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:342:9 + --> tests/ui/collapsible_match.rs:330:9 | LL | / match &mut last { LL | | @@ -309,7 +309,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:341:17 + --> tests/ui/collapsible_match.rs:329:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` From a212bd48c41fdcad962edc45f2f1570e5771fd0b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 01:17:03 +0200 Subject: [PATCH 0201/1900] misc: put the `: ` in the right place in the struct field suggestion --- clippy_lints/src/matches/collapsible_match.rs | 2 +- tests/ui/collapsible_match.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 11e79333a6948..71d34d705d2b2 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -103,7 +103,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{}: `", snippet(cx, binding_span, "their field name")) } else { String::new() }; diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index ebde46cf8a725..4d81f91ecc896 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -230,7 +230,7 @@ help: the outer pattern can be modified to include the inner pattern LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding LL | if let Some(u) = a { - | ^^^^^^^ with this pattern, prefixed by `a`: + | ^^^^^^^ with this pattern, prefixed by `a: ` error: this `if let` can be collapsed into the outer `if let` --> tests/ui/collapsible_match.rs:299:9 From fba062b0cae9f92d6e61ebbca0fbbbffdf01bc93 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 01:16:03 +0200 Subject: [PATCH 0202/1900] fix(collapsible_match): exclude binding modes from struct field pattern suggestions --- clippy_lints/src/matches/collapsible_match.rs | 17 +++++----- tests/ui/collapsible_match.rs | 21 +++++++++++++ tests/ui/collapsible_match.stderr | 31 ++++++++++++++----- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 71d34d705d2b2..a02e6dd96a1f0 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -13,6 +13,7 @@ use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; +use rustc_span::symbol::Ident; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -70,7 +71,7 @@ fn check_arm<'tcx>( && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) = + && let (Some((binding_ident, binding_span)), is_innermost_parent_pat_struct) = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { @@ -103,7 +104,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}: `", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{binding_ident}: `") } else { String::new() }; @@ -140,16 +141,16 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { - let mut span = None; +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option<(Ident, Span)>, bool) { + let mut binding = None; let mut is_innermost_parent_pat_struct = false; - pat.walk_short(|p| match &p.kind { + pat.walk_short(|p| match p.kind { // ignore OR patterns PatKind::Or(_) => false, - PatKind::Binding(_bm, _, _ident, _) => { + PatKind::Binding(_bm, _, ident, _) => { let found = p.hir_id == hir_id; if found { - span = Some(p.span); + binding = Some((ident, p.span)); } !found }, @@ -158,7 +159,7 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi true }, }); - (span, is_innermost_parent_pat_struct) + (binding, is_innermost_parent_pat_struct) } /// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7a9c8d14d13fb..84f958ee84584 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -304,6 +304,27 @@ pub fn test_2(x: Issue9647) { } } +mod issue_13287 { + enum Token { + Name, + Other, + } + + struct Error { + location: u32, + token: Option, + } + + fn struct_field_pat_with_binding_mode(err: Option) { + if let Some(Error { ref token, .. }) = err { + if let Some(Token::Name) = token { + //~^ collapsible_match + println!("token used as a ref"); + } + } + } +} + pub fn issue_14155() { let mut arr = ["a", "b", "c"]; if let Some(last) = arr.last() { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 4d81f91ecc896..d217948d4ca6c 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -250,8 +250,25 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern +error: this `if let` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:320:13 + | +LL | / if let Some(Token::Name) = token { +LL | | +LL | | println!("token used as a ref"); +LL | | } + | |_____________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:319:29 + | +LL | if let Some(Error { ref token, .. }) = err { + | ^^^^^^^^^ replace this binding +LL | if let Some(Token::Name) = token { + | ^^^^^^^^^^^^^^^^^ with this pattern, prefixed by `token: ` + error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:310:9 + --> tests/ui/collapsible_match.rs:331:9 | LL | / match *last { LL | | @@ -263,7 +280,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:309:17 + --> tests/ui/collapsible_match.rs:330:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +291,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:320:9 + --> tests/ui/collapsible_match.rs:341:9 | LL | / match &last { LL | | @@ -286,7 +303,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:319:17 + --> tests/ui/collapsible_match.rs:340:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +314,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:330:9 + --> tests/ui/collapsible_match.rs:351:9 | LL | / match &mut last { LL | | @@ -309,7 +326,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:329:17 + --> tests/ui/collapsible_match.rs:350:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` @@ -319,5 +336,5 @@ LL | if let Some(mut last) = arr.last_mut() { LL | &mut &mut "a" | &mut &mut "b" => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From d9619ed1c54f7c20b74913982cf0715002380e10 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 4 Sep 2025 04:52:52 +0000 Subject: [PATCH 0203/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 9385c64c95d971329e62917adc4349c8ccdbafe0. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d44488399f8b9..7420b6200967c 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -51ff895062ba60a7cba53f57af928c3fb7b0f2f4 +9385c64c95d971329e62917adc4349c8ccdbafe0 From 78e096015deaf67999aafd1df88f3b08fdce4e4a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 4 Sep 2025 14:57:41 +0800 Subject: [PATCH 0204/1900] Add cfg_attr predicate completion Example --- ```rust #[cfg_attr($0, must_use)] struct Foo; ``` --- .../src/completions/attribute.rs | 2 +- .../ide-completion/src/tests/attribute.rs | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index c542e140df541..e174b0c8922ab 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -70,7 +70,7 @@ pub(crate) fn complete_known_attribute_input( lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); } - ["cfg"] => cfg::complete_cfg(acc, ctx), + ["cfg"] | ["cfg_attr"] => cfg::complete_cfg(acc, ctx), ["macro_use"] => macro_use::complete_macro_use( acc, ctx, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 46a36300459d5..30e1e108c6c40 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -835,6 +835,58 @@ mod cfg { ); } + #[test] + fn inside_cfg_attr() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg_attr($0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg_attr(b$0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg_attr($0, allow(deprecated))] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg_attr(b$0, allow(deprecated))] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + ba true + "#]], + ); + } + #[test] fn cfg_target_endian() { check( From 169013d9eaae1d0b3d4d07058017698837ee09ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 12:10:39 +0300 Subject: [PATCH 0205/1900] Fix typo in config To make it backwards-compatible. --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2252185a3aa3..84f158cdea69f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2832,7 +2832,7 @@ enum ReborrowHintsDef { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum AdjustmentHintsDef { - #[serde(alias = "Reborrow")] + #[serde(alias = "reborrow")] Borrows, #[serde(with = "true_or_always")] #[serde(untagged)] From d5cb7e7c7c0f77c7a7b064584ae44e74acc155ca Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 12:18:22 +0300 Subject: [PATCH 0206/1900] Add a regression test for a fixed new trait solver bug Not sure what exactly fixed it, but why not. --- .../hir-ty/src/tests/regression/new_solver.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 20190fbc04564..e4ee52f45eb75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -1,6 +1,6 @@ use expect_test::expect; -use super::check_infer; +use crate::tests::{check_infer, check_no_mismatches}; #[test] fn opaque_generics() { @@ -50,3 +50,24 @@ fn main() { "#]], ); } + +#[test] +fn regression_20487() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait Foo { + fn bar(&self) -> u32 { + 0xCAFE + } +} + +fn debug(_: &dyn Foo) {} + +impl Foo for i32 {} + +fn main() { + debug(&1); +}"#, + ); +} From 962b0f19bfd7ce46db842a8271e0497cdfdea7a4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 19 Aug 2025 12:48:22 -0500 Subject: [PATCH 0207/1900] docs(style): Specify the frontmatter style Taken from [a style team discussion](https://github.com/rust-lang/style-team/issues/212#issuecomment-3185911143). Assumptions on my part: - I don't need to specify the lack of trailing spaces after the code fence / infostring - We aren't specifying when to include the infostring (one idea being if there is no shebang) - Keep it simple and have a single example instead of showing allowed several variations --- src/doc/style-guide/src/nightly.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/doc/style-guide/src/nightly.md b/src/doc/style-guide/src/nightly.md index 66e7fa3c9f89c..608dbd37192a3 100644 --- a/src/doc/style-guide/src/nightly.md +++ b/src/doc/style-guide/src/nightly.md @@ -5,3 +5,29 @@ This chapter documents style and formatting for nightly-only syntax. The rest of Style and formatting for nightly-only syntax should be removed from this chapter and integrated into the appropriate sections of the style guide at the time of stabilization. There is no guarantee of the stability of this chapter in contrast to the rest of the style guide. Refer to the style team policy for nightly formatting procedure regarding breaking changes to this chapter. + +### Frontmatter + +*Location: Placed before comments and attributes in the [root](index.html).* + +*Tracking issue: [#136889](https://github.com/rust-lang/rust/issues/136889)* + +*Feature gate: `frontmatter`* + +There should be no blank lines between the frontmatter and either the start of the file or a shebang. +There can be zero or one line between the frontmatter and any following content. + +The frontmatter fences should use the minimum number of dashes necessary for the contained content (one more than the longest series of initial dashes in the +content, with a minimum of 3 to be recognized as frontmatter delimiters). +If an infostring is present after the opening fence, there should be one space separating them. +The frontmatter fence lines should not have trailing whitespace. + +```rust +#!/usr/bin/env cargo +--- cargo +[dependencies] +regex = "1" +--- + +fn main() {} +``` From 0976d6ccac3d71befb58ceefd3109864f0eb210d Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 24 Aug 2025 22:48:00 -0700 Subject: [PATCH 0208/1900] don't extend non-extended `super let` initializers' block tail temps --- .../rustc_hir_analysis/src/check/region.rs | 24 +++++++++++++++---- .../format-args-temporary-scopes.e2024.stderr | 14 ++++++++++- .../borrowck/format-args-temporary-scopes.rs | 1 + .../ui/drop/super-let-tail-expr-drop-order.rs | 14 +++++------ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index f5770b7312ddf..58521697b387f 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -467,8 +467,12 @@ fn resolve_local<'tcx>( // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. - if let_kind == LetKind::Super { - if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) { + let extend_initializer = match let_kind { + LetKind::Regular => true, + LetKind::Super + if let Some(scope) = + visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) => + { // This expression was lifetime-extended by a parent let binding. E.g. // // let a = { @@ -481,7 +485,10 @@ fn resolve_local<'tcx>( // Processing of `let a` will have already decided to extend the lifetime of this // `super let` to its own var_scope. We use that scope. visitor.cx.var_parent = scope; - } else { + // Extend temporaries to live in the same scope as the parent `let`'s bindings. + true + } + LetKind::Super => { // This `super let` is not subject to lifetime extension from a parent let binding. E.g. // // identity({ super let x = temp(); &x }).method(); @@ -497,10 +504,17 @@ fn resolve_local<'tcx>( } visitor.cx.var_parent = parent; } + // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in + // the initializer when this `super let` is not itself extended by a parent `let` + // (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and + // later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`. + false } - } + }; - if let Some(expr) = init { + if let Some(expr) = init + && extend_initializer + { record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent); if let Some(pat) = pat { diff --git a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr index 923c929a68d60..506fc6e0965f7 100644 --- a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr +++ b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr @@ -10,6 +10,18 @@ LL | println!("{:?}", { &temp() }); | = note: consider using a `let` binding to create a longer lived value -error: aborting due to 1 previous error +error[E0716]: temporary value dropped while borrowed + --> $DIR/format-args-temporary-scopes.rs:19:29 + | +LL | println!("{:?}{:?}", { &temp() }, ()); + | ---^^^^^--- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/format-args-temporary-scopes.rs b/tests/ui/borrowck/format-args-temporary-scopes.rs index e6223d7c55183..2641058accb31 100644 --- a/tests/ui/borrowck/format-args-temporary-scopes.rs +++ b/tests/ui/borrowck/format-args-temporary-scopes.rs @@ -17,4 +17,5 @@ fn main() { // arguments when provided with two or more arguments. This caused the result of `temp()` to // outlive the result of the block, making this compile. println!("{:?}{:?}", { &temp() }, ()); + //[e2024]~^ ERROR: temporary value dropped while borrowed [E0716] } diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs index 5aa17bba21f1b..8ec9296ed4f1d 100644 --- a/tests/ui/drop/super-let-tail-expr-drop-order.rs +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -45,10 +45,10 @@ fn main() { #[cfg(e2024)] ( pin!(( - pin!({ &o.log(3) as *const LogDrop<'_> }), - drop(o.log(1)), + pin!({ &o.log(1) as *const LogDrop<'_> }), + drop(o.log(2)), )), - drop(o.log(2)), + drop(o.log(3)), ); }); @@ -69,12 +69,12 @@ fn main() { ( { super let _ = { - super let _ = { &o.log(4) as *const LogDrop<'_> }; - drop(o.log(1)) + super let _ = { &o.log(1) as *const LogDrop<'_> }; + drop(o.log(2)) }; - drop(o.log(2)) + drop(o.log(3)) }, - drop(o.log(3)), + drop(o.log(4)), ); }); From d5b5a4a0e11d92a963578d15a9f693e1538caf3f Mon Sep 17 00:00:00 2001 From: dianne Date: Mon, 25 Aug 2025 04:52:52 -0700 Subject: [PATCH 0209/1900] additional tests Co-authored-by: Travis Cross --- .../ui/drop/super-let-tail-expr-drop-order.rs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs index 8ec9296ed4f1d..5b2ecfbb3200c 100644 --- a/tests/ui/drop/super-let-tail-expr-drop-order.rs +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -22,10 +22,13 @@ //@ [e2024] edition: 2024 #![feature(super_let)] +#![allow(unused_braces)] use std::cell::RefCell; use std::pin::pin; +fn f(_: LogDrop<'_>, x: T) -> T { x } + fn main() { // Test block arguments to `pin!` in non-extending expressions. // In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries @@ -90,6 +93,73 @@ fn main() { let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; }; drop(o.log(1)); }); + + // We have extending borrow expressions within an extending block + // expression (within an extending borrow expression) within a + // non-extending expresion within the initializer expression. + #[cfg(e2021)] + { + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), &{ &raw const *&e.log(2) }); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), { + super let v = &{ &raw const *&e.log(2) }; + v + }); + drop(e.log(3)); + }); + } + #[cfg(e2024)] + { + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(2), &{ &raw const *&e.log(1) }); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(2), { + super let v = &{ &raw const *&e.log(1) }; + v + }); + drop(e.log(3)); + }); + } + + // We have extending borrow expressions within a non-extending + // expression within the initializer expression. + // + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), &&raw const *&e.log(2)); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), { + super let v = &&raw const *&e.log(2); + v + }); + drop(e.log(3)); + }); + + // We have extending borrow expressions within an extending block + // expression (within an extending borrow expression) within the + // initializer expression. + // + // These two should be the same. + assert_drop_order(1..=2, |e| { + let _v = &{ &raw const *&e.log(2) }; + drop(e.log(1)); + }); + assert_drop_order(1..=2, |e| { + let _v = { + super let v = &{ &raw const *&e.log(2) }; + v + }; + drop(e.log(1)); + }); } // # Test scaffolding... From 7ff5a3d277939c9a3648f2dc270ff2e9166bcf70 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 22:36:31 +0300 Subject: [PATCH 0210/1900] Upgrade rustc crates The main changes are (there are some other small changes): - Using a specific type for trait IDs in the new solver, allowing us to simplify a lot of code. - Add `BoundConst` similar to `BoundTy` and `BoundRegion` (previously consts used `BoundVar` directly), due to a new trait requirement. --- src/tools/rust-analyzer/Cargo.lock | 77 ++- src/tools/rust-analyzer/Cargo.toml | 16 +- .../crates/hir-ty/src/display.rs | 12 +- .../crates/hir-ty/src/dyn_compatibility.rs | 72 +-- .../crates/hir-ty/src/lower_nextsolver.rs | 64 +-- .../hir-ty/src/lower_nextsolver/path.rs | 5 +- .../crates/hir-ty/src/method_resolution.rs | 6 +- .../crates/hir-ty/src/next_solver/consts.rs | 29 +- .../crates/hir-ty/src/next_solver/def_id.rs | 57 ++ .../crates/hir-ty/src/next_solver/fold.rs | 8 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 2 +- .../hir-ty/src/next_solver/generic_arg.rs | 2 - .../infer/canonical/instantiate.rs | 3 +- .../hir-ty/src/next_solver/infer/mod.rs | 6 +- .../next_solver/infer/relate/higher_ranked.rs | 8 +- .../crates/hir-ty/src/next_solver/interner.rs | 511 +++++++----------- .../crates/hir-ty/src/next_solver/ir_print.rs | 10 +- .../crates/hir-ty/src/next_solver/mapping.rs | 32 +- .../hir-ty/src/next_solver/predicate.rs | 8 +- .../crates/hir-ty/src/next_solver/solver.rs | 8 +- .../crates/hir-ty/src/next_solver/ty.rs | 27 + .../crates/hir-ty/src/next_solver/util.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 5 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 29 +- 24 files changed, 446 insertions(+), 579 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b008aa1c50f88..344e6d101fe35 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1360,7 +1360,7 @@ dependencies = [ "expect-test", "intern", "parser", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "rustc-hash 2.1.1", "smallvec", "span", @@ -1596,8 +1596,8 @@ dependencies = [ "drop_bomb", "edition", "expect-test", - "ra-ap-rustc_lexer 0.123.0", - "rustc-literal-escaper", + "ra-ap-rustc_lexer", + "rustc-literal-escaper 0.0.4", "stdx", "tracing", ] @@ -1717,7 +1717,7 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "span", "syntax-bridge", "temp-dir", @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18c877575c259d127072e9bfc41d985202262fb4d6bfdae3d1252147c2562c2" +checksum = "0c6789d94fb3e6e30d62f55e99a321ba63484a8bb3b4ead338687c9ddc282d28" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80" +checksum = "aaab80bda0f05e9842e3afb7779b0bad0a4b54e0f7ba6deb5705dcf86482811d" [[package]] name = "ra-ap-rustc_hashes" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439ed1df3472443133b66949f81080dff88089b42f825761455463709ee1cad" +checksum = "64bd405e538102b5f699241794b2eefee39d5414c0e4bc72435e91430c51f905" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a24fe0be21be1f8ebc21dcb40129214fb4cefb0f2753f3d46b6dbe656a1a45" +checksum = "521621e271aa03b8433dad5981838278d6cfd7d2d8c9f4eb6d427f1d671f90fc" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "844a27ddcad0116facae2df8e741fd788662cf93dc13029cd864f2b8013b81f9" +checksum = "245e30f2e1fef258913cc548b36f575549c8af31cbc4649929d21deda96ceeb7" dependencies = [ "proc-macro2", "quote", @@ -1911,20 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.121.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22944e31fb91e9b3e75bcbc91e37d958b8c0825a6160927f2856831d2ce83b36" -dependencies = [ - "memchr", - "unicode-properties", - "unicode-xid", -] - -[[package]] -name = "ra-ap-rustc_lexer" -version = "0.123.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b734cfcb577d09877799a22742f1bd398be6c00bc428d9de56d48d11ece5771" +checksum = "a82681f924500e888c860e60ed99e9bf702a219a69374f59116c4261525a2157" dependencies = [ "memchr", "unicode-properties", @@ -1933,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441" +checksum = "0c9ce51f2431fbdc7fabd2d957522b6e27f41f68ec2af74b52a6f4116352ce1a" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1946,19 +1935,19 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.121.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97" +checksum = "adc85ef3fdb6c084bde84857d8948dc66b752129dc8417a8614ce490e99a143f" dependencies = [ - "ra-ap-rustc_lexer 0.121.0", - "rustc-literal-escaper", + "ra-ap-rustc_lexer", + "rustc-literal-escaper 0.0.5", ] [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b0ee1f059b9dea0818c6c7267478926eee95ba4c7dcf89c8db32fa165d3904" +checksum = "3cd81eccf33d9528905d4e5abaa254b3129a6405d6c5f123fed9b73a3d217f35" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1969,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b" +checksum = "11cb0da02853698d9c89e1d1c01657b9969752befd56365e8899d4310e52b373" dependencies = [ "bitflags 2.9.1", "derive-where", @@ -1988,9 +1977,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe" +checksum = "ffc93adeb52c483ede13bee6680466458218243ab479c04fb71bb53925a6e0ff" dependencies = [ "proc-macro2", "quote", @@ -2140,6 +2129,12 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b" +[[package]] +name = "rustc-literal-escaper" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" + [[package]] name = "rustc-stable-hash" version = "0.1.2" @@ -2445,7 +2440,7 @@ dependencies = [ "rayon", "rowan", "rustc-hash 2.1.1", - "rustc-literal-escaper", + "rustc-literal-escaper 0.0.4", "rustc_apfloat", "smol_str", "stdx", @@ -2773,7 +2768,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "intern", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "stdx", "text-size", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 05ee190480c97..f325027ee5871 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.123", default-features = false } -ra-ap-rustc_parse_format = { version = "0.121", default-features = false } -ra-ap-rustc_index = { version = "0.123", default-features = false } -ra-ap-rustc_abi = { version = "0.123", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.123", default-features = false } -ra-ap-rustc_type_ir = { version = "0.123", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false } +ra-ap-rustc_lexer = { version = "0.126", default-features = false } +ra-ap-rustc_parse_format = { version = "0.126", default-features = false } +ra-ap-rustc_index = { version = "0.126", default-features = false } +ra-ap-rustc_abi = { version = "0.126", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.126", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.126", default-features = false } +ra-ap-rustc_type_ir = { version = "0.126", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.126", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5b8093f6b7246..fcb79e9ffb579 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -737,7 +737,7 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> { match self.kind() { rustc_type_ir::ConstKind::Placeholder(_) => write!(f, ""), rustc_type_ir::ConstKind::Bound(db, bound_const) => { - write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) + write!(f, "?{}.{}", db.as_u32(), bound_const.var.as_u32()) } rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), rustc_type_ir::ConstKind::Param(param) => { @@ -1208,10 +1208,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { let contains_impl_fn_ns = |bounds: &[BoundExistentialPredicate<'_>]| { bounds.iter().any(|bound| match bound.skip_binder() { rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ = match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_ = trait_ref.def_id.0; fn_traits(db, trait_).any(|it| it == trait_) } _ => false, @@ -2217,10 +2214,7 @@ impl HirDisplay for TraitRef { impl<'db> HirDisplay for crate::next_solver::TraitRef<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let trait_ = match self.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_ = self.def_id.0; f.start_location_link(trait_.into()); write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; f.end_location_link(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 23280b1f3b42b..d4b3751cf5666 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,12 +2,11 @@ use std::ops::ControlFlow; -use hir_def::hir::generics::LocalTypeOrConstParamId; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, - TypeAliasId, lang_item::LangItem, signatures::TraitFlags, + TypeAliasId, TypeOrConstParamId, TypeParamId, hir::generics::LocalTypeOrConstParamId, + lang_item::LangItem, signatures::TraitFlags, }; -use hir_def::{TypeOrConstParamId, TypeParamId}; use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -22,7 +21,7 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower_nextsolver::associated_ty_item_bounds, next_solver::{ - Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, + Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TraitRef, TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, @@ -56,16 +55,12 @@ pub fn dyn_compatibility( trait_: TraitId, ) -> Option { let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); - for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)) { - let super_trait = match super_trait { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - if let Some(v) = db.dyn_compatibility_of_trait(super_trait) { - return if super_trait == trait_ { + for super_trait in elaborate::supertrait_def_ids(interner, trait_.into()) { + if let Some(v) = db.dyn_compatibility_of_trait(super_trait.0) { + return if super_trait.0 == trait_ { Some(v) } else { - Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)) + Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait.0)) }; } } @@ -82,13 +77,8 @@ where F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, { let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); - for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)).skip(1) - { - let super_trait = match super_trait { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - if db.dyn_compatibility_of_trait(super_trait).is_some() { + for super_trait in elaborate::supertrait_def_ids(interner, trait_.into()).skip(1) { + if db.dyn_compatibility_of_trait(super_trait.0).is_some() { cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?; } } @@ -151,7 +141,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { - if SolverDefId::TraitId(sized) == trait_pred.def_id() + if sized == trait_pred.def_id().0 && let rustc_type_ir::TyKind::Param(param_ty) = trait_pred.trait_ref.self_ty().kind() && param_ty.index == 0 @@ -257,15 +247,9 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable id, - _ => unreachable!(), - }) - .collect(), + elaborate::supertrait_def_ids(interner, self.trait_.into()) + .map(|super_trait| super_trait.0) + .collect(), ) } if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { @@ -390,8 +374,7 @@ where trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, }) = pred - && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id - && let trait_data = db.trait_signature(trait_id) + && let trait_data = db.trait_signature(pred_trait_ref.def_id.0) && trait_data.flags.contains(TraitFlags::AUTO) && let rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, .. }) = pred_trait_ref.self_ty().kind() @@ -464,25 +447,17 @@ fn receiver_is_dispatchable<'db>( let generic_predicates = &*db.generic_predicates_ns(func.into()); // Self: Unsize - let unsize_predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(unsize_did), - [self_param_ty, unsized_self_ty], - ); + let unsize_predicate = + TraitRef::new(interner, unsize_did.into(), [self_param_ty, unsized_self_ty]); // U: Trait - let trait_def_id = SolverDefId::TraitId(trait_); - let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { + let args = GenericArgs::for_item(interner, trait_.into(), |name, index, kind, _| { if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) } }); - let trait_predicate = - crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); + let trait_predicate = TraitRef::new_from_args(interner, trait_.into(), args); - let meta_sized_predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(meta_sized_did), - [unsized_self_ty], - ); + let meta_sized_predicate = + TraitRef::new(interner, meta_sized_did.into(), [unsized_self_ty]); ParamEnv { clauses: Clauses::new_from_iter( @@ -497,11 +472,8 @@ fn receiver_is_dispatchable<'db>( }; // Receiver: DispatchFromDyn U]> - let predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(dispatch_from_dyn_did), - [receiver_ty, unsized_receiver_ty], - ); + let predicate = + TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]); let goal = crate::next_solver::Goal::new(interner, param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 4578922ce3219..c6a8fa81edff5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -590,11 +590,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); let pointee_sized = LangItem::PointeeSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) { + if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Ignore this bound - } else if pointee_sized - .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) - { + } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Regard this as `?Sized` bound ctx.ty_ctx().unsized_types.insert(self_ty); } else { @@ -618,13 +616,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // Don't lower associated type bindings as the only possible relaxed trait bound // `?Sized` has no of them. // If we got another trait here ignore the bound completely. - let trait_id = - self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| { - match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - } - }); + let trait_id = self + .lower_trait_ref_from_path(path, self_ty) + .map(|(trait_ref, _)| trait_ref.def_id.0); if trait_id == sized_trait { self.unsized_types.insert(self_ty); } @@ -668,12 +662,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .map_bound(|c| match c { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; let is_auto = - db.trait_signature(id).flags.contains(TraitFlags::AUTO); + db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); if is_auto { Some(ExistentialPredicate::AutoTrait(t.def_id())) } else { @@ -733,17 +723,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ( ExistentialPredicate::AutoTrait(lhs_id), ExistentialPredicate::AutoTrait(rhs_id), - ) => { - let lhs_id = match lhs_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let rhs_id = match rhs_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - lhs_id.cmp(&rhs_id) - } + ) => lhs_id.0.cmp(&rhs_id.0), (ExistentialPredicate::Trait(_), _) => Ordering::Less, (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, @@ -1195,11 +1175,7 @@ pub(crate) fn generic_predicates_for_param_query<'db>( }; rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { - let tr = match tr { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - tr.trait_items(db).items.iter().any(|(name, item)| { + tr.0.trait_items(db).items.iter().any(|(name, item)| { matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) }) @@ -1629,11 +1605,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( .map_bound(|c| match c { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let is_auto = db.trait_signature(id).flags.contains(TraitFlags::AUTO); + let is_auto = db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); if is_auto { Some(ExistentialPredicate::AutoTrait(t.def_id())) } else { @@ -1677,7 +1649,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( interner, - SolverDefId::TraitId(trait_), + trait_.into(), [] as [crate::next_solver::GenericArg<'_>; 0], ))); bounds.push(sized_clause); @@ -1694,10 +1666,7 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>( ) -> Option<(TraitRef<'db>, TypeAliasId)> { let interner = DbInterner::new_with(db, None, None); rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { - let trait_id = match t.as_ref().skip_binder().def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = t.as_ref().skip_binder().def_id.0; let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; Some((t.skip_binder(), assoc_type)) }) @@ -1727,10 +1696,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( ) -> Option { let db = interner.db; let mut search = |t: TraitRef<'db>| -> Option { - let trait_id = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = t.def_id.0; let mut checked_traits = FxHashSet::default(); let mut check_trait = |trait_id: TraitId| { let name = &db.trait_signature(trait_id).name; @@ -1773,11 +1739,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), _ => continue, }; - let trait_id = match trait_id { - SolverDefId::TraitId(trait_id) => trait_id, - _ => continue, - }; - stack.push(trait_id); + stack.push(trait_id.0); } tracing::debug!(?stack); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index ccdb5a0bdb4a9..7d6734303c48b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -208,10 +208,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { tracing::debug!(?trait_ref); self.skip_resolved_segment(); let segment = self.current_or_prev_segment; - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = trait_ref.def_id.0; let found = trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 8bd71df7c191d..f0be352fdd151 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -168,11 +168,7 @@ impl TyFingerprint { _ => None, }) .next()?; - let trait_id = match trait_ref { - SolverDefId::TraitId(id) => id, - _ => panic!("Bad GenericDefId in trait ref"), - }; - TyFingerprint::Dyn(trait_id) + TyFingerprint::Dyn(trait_ref.0) } TyKind::Ref(_, _, mutability) => match mutability { rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index cfafc65d18443..23789b06e828b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -81,7 +81,7 @@ impl<'db> std::fmt::Debug for InternedWrapperNoDebug; +pub type PlaceholderConst = Placeholder; #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct ParamConst { @@ -304,7 +304,7 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { fn new_bound( interner: DbInterner<'db>, debruijn: rustc_type_ir::DebruijnIndex, - var: BoundVar, + var: BoundConst, ) -> Self { Const::new(interner, ConstKind::Bound(debruijn, var)) } @@ -314,7 +314,7 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { debruijn: rustc_type_ir::DebruijnIndex, var: rustc_type_ir::BoundVar, ) -> Self { - Const::new(interner, ConstKind::Bound(debruijn, var)) + Const::new(interner, ConstKind::Bound(debruijn, BoundConst { var })) } fn new_unevaluated( @@ -340,26 +340,41 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct BoundConst { + pub var: BoundVar, +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundConst { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + var.expect_const() + } +} + impl<'db> PlaceholderLike> for PlaceholderConst { - type Bound = rustc_type_ir::BoundVar; + type Bound = BoundConst; fn universe(self) -> rustc_type_ir::UniverseIndex { self.universe } fn var(self) -> rustc_type_ir::BoundVar { - self.bound + self.bound.var } fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { Placeholder { universe: ui, bound: self.bound } } - fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + fn new(ui: rustc_type_ir::UniverseIndex, var: BoundConst) -> Self { Placeholder { universe: ui, bound: var } } fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { - Placeholder { universe: ui, bound: var } + Placeholder { universe: ui, bound: BoundConst { var } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index c9632ddcd4bb5..8bbc6e3370399 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -88,3 +88,60 @@ impl<'db> inherent::DefId> for SolverDefId { true } } + +macro_rules! declare_id_wrapper { + ($name:ident, $wraps:ident) => { + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name(pub $wraps); + + impl std::fmt::Debug for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + + impl From<$name> for $wraps { + #[inline] + fn from(value: $name) -> $wraps { + value.0 + } + } + + impl From<$wraps> for $name { + #[inline] + fn from(value: $wraps) -> $name { + Self(value) + } + } + + impl From<$name> for SolverDefId { + #[inline] + fn from(value: $name) -> SolverDefId { + value.0.into() + } + } + + impl TryFrom for $name { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result { + match value { + SolverDefId::$wraps(it) => Ok(Self(it)), + _ => Err(()), + } + } + } + + impl<'db> inherent::DefId> for $name { + fn as_local(self) -> Option { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } + } + }; +} + +declare_id_wrapper!(TraitIdWrapper, TraitId); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index 3cc1e64b6add0..405a57d9e898c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -6,6 +6,8 @@ use rustc_type_ir::{ inherent::{IntoKind, Region as _}, }; +use crate::next_solver::BoundConst; + use super::{ Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, }; @@ -18,7 +20,7 @@ use super::{ pub trait BoundVarReplacerDelegate<'db> { fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; - fn replace_const(&mut self, bv: BoundVar) -> Const<'db>; + fn replace_const(&mut self, bv: BoundConst) -> Const<'db>; } /// A simple delegate taking 3 mutable functions. The used functions must @@ -27,7 +29,7 @@ pub trait BoundVarReplacerDelegate<'db> { pub struct FnMutDelegate<'db, 'a> { pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), - pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundConst) -> Const<'db> + 'a), } impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { @@ -37,7 +39,7 @@ impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { (self.types)(bt) } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { (self.consts)(bv) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 007a674ad399a..4258f4c7ac68b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -167,7 +167,7 @@ impl<'db> FulfillmentCtxt<'db> { } let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); - let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(NextSolverError::TrueError(obligation)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index d284eb9c6b419..4e124d07d2b38 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -384,7 +384,6 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< signature_parts_ty: signature_parts_ty.expect_ty(), tupled_upvars_ty: tupled_upvars_ty.expect_ty(), coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), - coroutine_witness_ty: coroutine_witness_ty.expect_ty(), }, _ => panic!("GenericArgs were likely not for a CoroutineClosure."), } @@ -400,7 +399,6 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< resume_ty: resume_ty.expect_ty(), yield_ty: yield_ty.expect_ty(), return_ty: return_ty.expect_ty(), - witness: Ty::new_unit(interner), tupled_upvars_ty: Ty::new_unit(interner), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 0448f03463e4b..6c7a87ef52494 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -6,6 +6,7 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html +use crate::next_solver::BoundConst; use crate::next_solver::{ AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, @@ -95,7 +96,7 @@ where GenericArgKind::Type(ty) => ty, r => panic!("{bound_ty:?} is a type but value is {r:?}"), }, - consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() { + consts: &mut |bound_ct: BoundConst| match var_values[bound_ct.var].kind() { GenericArgKind::Const(ct) => ct, c => panic!("{bound_ct:?} is a const but value is {c:?}"), }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 58832aef89529..585719144ef42 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -37,7 +37,7 @@ use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::next_solver::fold::BoundVarReplacerDelegate; use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; -use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; +use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind}; use super::generics::GenericParamDef; use super::{ @@ -864,8 +864,8 @@ impl<'db> InferCtxt<'db> { fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { self.args[bt.var.index()].expect_ty() } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { - self.args[bv.index()].expect_const() + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + self.args[bv.var.index()].expect_const() } } let delegate = ToFreshVars { args }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs index bb80c5157109c..62028e0e70399 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -10,8 +10,8 @@ use crate::next_solver::fold::FnMutDelegate; use crate::next_solver::infer::InferCtxt; use crate::next_solver::infer::snapshot::CombinedSnapshot; use crate::next_solver::{ - Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, - PlaceholderTy, Region, Ty, + Binder, BoundConst, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, + PlaceholderRegion, PlaceholderTy, Region, Ty, }; impl<'db> InferCtxt<'db> { @@ -50,10 +50,10 @@ impl<'db> InferCtxt<'db> { PlaceholderTy { universe: next_universe, bound: bound_ty }, ) }, - consts: &mut |bound_var: BoundVar| { + consts: &mut |bound: BoundConst| { Const::new_placeholder( self.interner, - PlaceholderConst { universe: next_universe, bound: bound_var }, + PlaceholderConst { universe: next_universe, bound }, ) }, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 6e1a5e9645573..5709beaefc292 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -19,7 +19,7 @@ use rustc_type_ir::error::TypeError; use rustc_type_ir::inherent::{ AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, }; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, @@ -44,9 +44,11 @@ use rustc_type_ir::{ use crate::lower_nextsolver::{self, TyLoweringContext}; use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; +use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ - CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds, + BoundConst, CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, + SolverContext, SolverDefIds, TraitIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -858,10 +860,35 @@ impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { interned_vec_db!(PatList, Pattern); +macro_rules! as_lang_item { + ( + $solver_enum:ident, $var:ident; + + ignore = { + $( $ignore:ident ),* $(,)? + } + + $( $variant:ident ),* $(,)? + ) => {{ + // Ensure exhaustiveness. + if let Some(it) = None::<$solver_enum> { + match it { + $( $solver_enum::$variant => {} )* + $( $solver_enum::$ignore => {} )* + } + } + match $var { + $( LangItem::$variant => Some($solver_enum::$variant), )* + _ => None + } + }}; +} + impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type DefId = SolverDefId; type LocalDefId = SolverDefId; type LocalDefIds = SolverDefIds; + type TraitId = TraitIdWrapper; type Span = Span; type GenericArgs = GenericArgs<'db>; @@ -923,7 +950,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type Const = Const<'db>; type PlaceholderConst = PlaceholderConst; type ParamConst = ParamConst; - type BoundConst = rustc_type_ir::BoundVar; + type BoundConst = BoundConst; type ValueConst = ValueConst<'db>; type ValTree = Valtree<'db>; type ExprConst = ExprConst; @@ -1117,7 +1144,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ); let alias_args = GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len())); - (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args) + (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args) } fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { @@ -1305,11 +1332,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { #[tracing::instrument(skip(self), ret)] fn explicit_super_predicates_of( self, - def_id: Self::DefId, + def_id: Self::TraitId, ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() - .generic_predicates_ns(def_id.try_into().unwrap()) + .generic_predicates_ns(def_id.0.into()) .iter() .cloned() .map(|p| (p, Span::dummy())) @@ -1369,65 +1396,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { false } - fn require_lang_item( - self, - lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, - ) -> Self::DefId { + fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId { let lang_item = match lang_item { - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => { - LangItem::AsyncFnOnceOutput - } - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => { - LangItem::CallOnceFuture - } - rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => { - LangItem::CallRefFuture - } - rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone, - rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy, - rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine, - rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => { - LangItem::CoroutineReturn - } - rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => { - LangItem::CoroutineYield - } - rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct, - rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => { - LangItem::DiscriminantKind - } - rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop, - rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, - rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn, - rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut, - rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce, - rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, - rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future, - rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, - rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator, - rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata, - rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option, - rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, - rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll, - rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized, - rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized, - rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized, - rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => { - LangItem::TransmuteTrait - } - rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple, - rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin, - rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize, - rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => { - unimplemented!() - } + SolverLangItem::AsyncFnKindUpvars => unimplemented!(), + SolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, + SolverLangItem::CallOnceFuture => LangItem::CallOnceFuture, + SolverLangItem::CallRefFuture => LangItem::CallRefFuture, + SolverLangItem::CoroutineReturn => LangItem::CoroutineReturn, + SolverLangItem::CoroutineYield => LangItem::CoroutineYield, + SolverLangItem::DynMetadata => LangItem::DynMetadata, + SolverLangItem::FutureOutput => LangItem::FutureOutput, + SolverLangItem::Metadata => LangItem::Metadata, + SolverLangItem::Option => LangItem::Option, + SolverLangItem::Poll => LangItem::Poll, }; let target = hir_def::lang_item::lang_item( self.db(), @@ -1448,216 +1429,131 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } + fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> TraitIdWrapper { + let lang_item = match lang_item { + SolverTraitLangItem::AsyncFn => LangItem::AsyncFn, + SolverTraitLangItem::AsyncFnKindHelper => unimplemented!(), + SolverTraitLangItem::AsyncFnMut => LangItem::AsyncFnMut, + SolverTraitLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, + SolverTraitLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, + SolverTraitLangItem::AsyncIterator => unimplemented!(), + SolverTraitLangItem::Clone => LangItem::Clone, + SolverTraitLangItem::Copy => LangItem::Copy, + SolverTraitLangItem::Coroutine => LangItem::Coroutine, + SolverTraitLangItem::Destruct => LangItem::Destruct, + SolverTraitLangItem::DiscriminantKind => LangItem::DiscriminantKind, + SolverTraitLangItem::Drop => LangItem::Drop, + SolverTraitLangItem::Fn => LangItem::Fn, + SolverTraitLangItem::FnMut => LangItem::FnMut, + SolverTraitLangItem::FnOnce => LangItem::FnOnce, + SolverTraitLangItem::FnPtrTrait => LangItem::FnPtrTrait, + SolverTraitLangItem::FusedIterator => unimplemented!(), + SolverTraitLangItem::Future => LangItem::Future, + SolverTraitLangItem::Iterator => LangItem::Iterator, + SolverTraitLangItem::PointeeTrait => LangItem::PointeeTrait, + SolverTraitLangItem::Sized => LangItem::Sized, + SolverTraitLangItem::MetaSized => LangItem::MetaSized, + SolverTraitLangItem::PointeeSized => LangItem::PointeeSized, + SolverTraitLangItem::TransmuteTrait => LangItem::TransmuteTrait, + SolverTraitLangItem::Tuple => LangItem::Tuple, + SolverTraitLangItem::Unpin => LangItem::Unpin, + SolverTraitLangItem::Unsize => LangItem::Unsize, + SolverTraitLangItem::BikeshedGuaranteedNoDrop => { + unimplemented!() + } + }; + lang_item + .resolve_trait(self.db(), self.krate.expect("Must have self.krate")) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")) + .into() + } + #[allow(clippy::match_like_matches_macro)] - fn is_lang_item( - self, - def_id: Self::DefId, - lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, - ) -> bool { - use rustc_type_ir::lang_items::TraitSolverLangItem::*; - - // FIXME: derive PartialEq on TraitSolverLangItem - self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) { - (AsyncFn, AsyncFn) => true, - (AsyncFnKindHelper, AsyncFnKindHelper) => true, - (AsyncFnKindUpvars, AsyncFnKindUpvars) => true, - (AsyncFnMut, AsyncFnMut) => true, - (AsyncFnOnce, AsyncFnOnce) => true, - (AsyncFnOnceOutput, AsyncFnOnceOutput) => true, - (AsyncIterator, AsyncIterator) => true, - (CallOnceFuture, CallOnceFuture) => true, - (CallRefFuture, CallRefFuture) => true, - (Clone, Clone) => true, - (Copy, Copy) => true, - (Coroutine, Coroutine) => true, - (CoroutineReturn, CoroutineReturn) => true, - (CoroutineYield, CoroutineYield) => true, - (Destruct, Destruct) => true, - (DiscriminantKind, DiscriminantKind) => true, - (Drop, Drop) => true, - (DynMetadata, DynMetadata) => true, - (Fn, Fn) => true, - (FnMut, FnMut) => true, - (FnOnce, FnOnce) => true, - (FnPtrTrait, FnPtrTrait) => true, - (FusedIterator, FusedIterator) => true, - (Future, Future) => true, - (FutureOutput, FutureOutput) => true, - (Iterator, Iterator) => true, - (Metadata, Metadata) => true, - (Option, Option) => true, - (PointeeTrait, PointeeTrait) => true, - (Poll, Poll) => true, - (Sized, Sized) => true, - (TransmuteTrait, TransmuteTrait) => true, - (Tuple, Tuple) => true, - (Unpin, Unpin) => true, - (Unsize, Unsize) => true, - _ => false, - }) + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { + use SolverLangItem::*; + + // FIXME: derive PartialEq on SolverLangItem + self.as_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } - fn as_lang_item( - self, - def_id: Self::DefId, - ) -> Option { - use rustc_type_ir::lang_items::TraitSolverLangItem; + #[allow(clippy::match_like_matches_macro)] + fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool { + use SolverTraitLangItem::*; + + // FIXME: derive PartialEq on SolverTraitLangItem + self.as_trait_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) + } + fn as_lang_item(self, def_id: Self::DefId) -> Option { let def_id: AttrDefId = match def_id { SolverDefId::TraitId(id) => id.into(), SolverDefId::TypeAliasId(id) => id.into(), + SolverDefId::AdtId(id) => id.into(), _ => panic!("Unexpected SolverDefId in as_lang_item"), }; let lang_item = self.db().lang_attr(def_id)?; - Some(match lang_item { - LangItem::Sized => TraitSolverLangItem::Sized, - LangItem::MetaSized => TraitSolverLangItem::MetaSized, - LangItem::PointeeSized => TraitSolverLangItem::PointeeSized, - LangItem::Unsize => TraitSolverLangItem::Unsize, - LangItem::StructuralPeq => return None, - LangItem::StructuralTeq => return None, - LangItem::Copy => TraitSolverLangItem::Copy, - LangItem::Clone => TraitSolverLangItem::Clone, - LangItem::Sync => return None, - LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind, - LangItem::Discriminant => return None, - LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait, - LangItem::Metadata => TraitSolverLangItem::Metadata, - LangItem::DynMetadata => TraitSolverLangItem::DynMetadata, - LangItem::Freeze => return None, - LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait, - LangItem::FnPtrAddr => return None, - LangItem::Drop => TraitSolverLangItem::Drop, - LangItem::Destruct => TraitSolverLangItem::Destruct, - LangItem::CoerceUnsized => return None, - LangItem::DispatchFromDyn => return None, - LangItem::TransmuteOpts => return None, - LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait, - LangItem::Add => return None, - LangItem::Sub => return None, - LangItem::Mul => return None, - LangItem::Div => return None, - LangItem::Rem => return None, - LangItem::Neg => return None, - LangItem::Not => return None, - LangItem::BitXor => return None, - LangItem::BitAnd => return None, - LangItem::BitOr => return None, - LangItem::Shl => return None, - LangItem::Shr => return None, - LangItem::AddAssign => return None, - LangItem::SubAssign => return None, - LangItem::MulAssign => return None, - LangItem::DivAssign => return None, - LangItem::RemAssign => return None, - LangItem::BitXorAssign => return None, - LangItem::BitAndAssign => return None, - LangItem::BitOrAssign => return None, - LangItem::ShlAssign => return None, - LangItem::ShrAssign => return None, - LangItem::Index => return None, - LangItem::IndexMut => return None, - LangItem::UnsafeCell => return None, - LangItem::VaList => return None, - LangItem::Deref => return None, - LangItem::DerefMut => return None, - LangItem::DerefTarget => return None, - LangItem::Receiver => return None, - LangItem::Fn => TraitSolverLangItem::Fn, - LangItem::FnMut => TraitSolverLangItem::FnMut, - LangItem::FnOnce => TraitSolverLangItem::FnOnce, - LangItem::FnOnceOutput => return None, - LangItem::Future => TraitSolverLangItem::Future, - LangItem::CoroutineState => return None, - LangItem::Coroutine => TraitSolverLangItem::Coroutine, - LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn, - LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield, - LangItem::Unpin => TraitSolverLangItem::Unpin, - LangItem::Pin => return None, - LangItem::PartialEq => return None, - LangItem::PartialOrd => return None, - LangItem::CVoid => return None, - LangItem::Panic => return None, - LangItem::PanicNounwind => return None, - LangItem::PanicFmt => return None, - LangItem::PanicDisplay => return None, - LangItem::ConstPanicFmt => return None, - LangItem::PanicBoundsCheck => return None, - LangItem::PanicMisalignedPointerDereference => return None, - LangItem::PanicInfo => return None, - LangItem::PanicLocation => return None, - LangItem::PanicImpl => return None, - LangItem::PanicCannotUnwind => return None, - LangItem::BeginPanic => return None, - LangItem::FormatAlignment => return None, - LangItem::FormatArgument => return None, - LangItem::FormatArguments => return None, - LangItem::FormatCount => return None, - LangItem::FormatPlaceholder => return None, - LangItem::FormatUnsafeArg => return None, - LangItem::ExchangeMalloc => return None, - LangItem::BoxFree => return None, - LangItem::DropInPlace => return None, - LangItem::AllocLayout => return None, - LangItem::Start => return None, - LangItem::EhPersonality => return None, - LangItem::EhCatchTypeinfo => return None, - LangItem::OwnedBox => return None, - LangItem::PhantomData => return None, - LangItem::ManuallyDrop => return None, - LangItem::MaybeUninit => return None, - LangItem::AlignOffset => return None, - LangItem::Termination => return None, - LangItem::Try => return None, - LangItem::Tuple => TraitSolverLangItem::Tuple, - LangItem::SliceLen => return None, - LangItem::TryTraitFromResidual => return None, - LangItem::TryTraitFromOutput => return None, - LangItem::TryTraitBranch => return None, - LangItem::TryTraitFromYeet => return None, - LangItem::PointerLike => return None, - LangItem::ConstParamTy => return None, - LangItem::Poll => TraitSolverLangItem::Poll, - LangItem::PollReady => return None, - LangItem::PollPending => return None, - LangItem::ResumeTy => return None, - LangItem::GetContext => return None, - LangItem::Context => return None, - LangItem::FuturePoll => return None, - LangItem::FutureOutput => TraitSolverLangItem::FutureOutput, - LangItem::Option => TraitSolverLangItem::Option, - LangItem::OptionSome => return None, - LangItem::OptionNone => return None, - LangItem::ResultOk => return None, - LangItem::ResultErr => return None, - LangItem::ControlFlowContinue => return None, - LangItem::ControlFlowBreak => return None, - LangItem::IntoFutureIntoFuture => return None, - LangItem::IntoIterIntoIter => return None, - LangItem::IteratorNext => return None, - LangItem::Iterator => TraitSolverLangItem::Iterator, - LangItem::PinNewUnchecked => return None, - LangItem::RangeFrom => return None, - LangItem::RangeFull => return None, - LangItem::RangeInclusiveStruct => return None, - LangItem::RangeInclusiveNew => return None, - LangItem::Range => return None, - LangItem::RangeToInclusive => return None, - LangItem::RangeTo => return None, - LangItem::String => return None, - LangItem::CStr => return None, - LangItem::AsyncFn => TraitSolverLangItem::AsyncFn, - LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut, - LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce, - LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, - LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture, - LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture, - LangItem::Ordering => return None, - LangItem::PanicNullPointerDereference => return None, - LangItem::ReceiverTarget => return None, - LangItem::UnsafePinned => return None, - LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, - }) + as_lang_item!( + SolverLangItem, lang_item; + + ignore = { + AsyncFnKindUpvars, + } + + Metadata, + DynMetadata, + CoroutineReturn, + CoroutineYield, + Poll, + FutureOutput, + Option, + AsyncFnOnceOutput, + CallRefFuture, + CallOnceFuture, + AsyncFnOnceOutput, + ) + } + + fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option { + let def_id: AttrDefId = def_id.0.into(); + let lang_item = self.db().lang_attr(def_id)?; + as_lang_item!( + SolverTraitLangItem, lang_item; + + ignore = { + AsyncFnKindHelper, + AsyncIterator, + BikeshedGuaranteedNoDrop, + FusedIterator, + } + + Sized, + MetaSized, + PointeeSized, + Unsize, + Copy, + Clone, + DiscriminantKind, + PointeeTrait, + FnPtrTrait, + Drop, + Destruct, + TransmuteTrait, + Fn, + FnMut, + FnOnce, + Future, + Coroutine, + Unpin, + Tuple, + Iterator, + AsyncFn, + AsyncFnMut, + AsyncFnOnce, + AsyncFnOnceOutput, + AsyncFnOnceOutput, + ) } fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { @@ -1670,15 +1566,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn for_each_relevant_impl( self, - trait_def_id: Self::DefId, + trait_: Self::TraitId, self_ty: Self::Ty, mut f: impl FnMut(Self::DefId), ) { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("for_each_relevant_impl called for non-trait"), - }; - + let trait_ = trait_.0; let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); let fps: &[TyFingerprint] = match self_ty.kind() { TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, @@ -1775,42 +1667,26 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_auto"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_auto(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::AUTO) } - fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_alias"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_alias(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::ALIAS) } - fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none() + fn trait_is_dyn_compatible(self, trait_: Self::TraitId) -> bool { + crate::dyn_compatibility::dyn_compatibility(self.db(), trait_.0).is_none() } - fn trait_is_fundamental(self, def_id: Self::DefId) -> bool { - let trait_ = match def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_fundamental"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_fundamental(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::FUNDAMENTAL) } - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::TraitId) -> bool { // FIXME(next-solver): should check the `TraitFlags` for // the `#[rustc_do_not_implement_via_object]` flag true @@ -1920,12 +1796,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { - let entry = self.map.entry(bv); + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + let entry = self.map.entry(bv.var); let index = entry.index(); let var = BoundVar::from_usize(index); let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); - Const::new_bound(self.interner, DebruijnIndex::ZERO, var) + Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst { var }) } } @@ -2008,24 +1884,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { unimplemented!() } - fn is_default_trait(self, def_id: Self::DefId) -> bool { - self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized)) + fn is_default_trait(self, def_id: Self::TraitId) -> bool { + self.as_trait_lang_item(def_id).map_or(false, |l| matches!(l, SolverTraitLangItem::Sized)) } - fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool { - let id = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE) + fn trait_is_coinductive(self, trait_: Self::TraitId) -> bool { + self.db().trait_signature(trait_.0).flags.contains(TraitFlags::COINDUCTIVE) } - fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { - let id = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE) + fn trait_is_unsafe(self, trait_: Self::TraitId) -> bool { + self.db().trait_signature(trait_.0).flags.contains(TraitFlags::UNSAFE) } fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { @@ -2047,6 +1915,20 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { // FIXME(next-solver) unimplemented!() } + + type Probe = rustc_type_ir::solve::inspect::Probe>; + fn mk_probe(self, probe: rustc_type_ir::solve::inspect::Probe) -> Self::Probe { + probe + } + fn evaluate_root_goal_for_proof_tree_raw( + self, + canonical_goal: rustc_type_ir::solve::CanonicalInput, + ) -> (rustc_type_ir::solve::QueryResult, Self::Probe) { + rustc_next_trait_solver::solve::evaluate_root_goal_for_proof_tree_raw_provider::< + SolverContext<'db>, + Self, + >(self, canonical_goal) + } } impl<'db> DbInterner<'db> { @@ -2072,7 +1954,9 @@ impl<'db> DbInterner<'db> { BoundTy { var: shift_bv(t.var), kind: t.kind }, ) }, - consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)), + consts: &mut |c| { + Const::new_bound(self, DebruijnIndex::ZERO, BoundConst { var: shift_bv(c.var) }) + }, }, ) } @@ -2135,6 +2019,7 @@ macro_rules! TrivialTypeTraversalImpls { TrivialTypeTraversalImpls! { SolverDefId, + TraitIdWrapper, Pattern<'db>, Safety, FnAbi, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 4d32b27132357..407e157564397 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -58,10 +58,7 @@ impl<'db> IrPrint> for DbInterner<'db> { fn print_debug(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { salsa::with_attached_database(|db| { - let trait_ = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Expected trait."), - }; + let trait_ = t.def_id.0; let self_ty = &t.args.as_slice()[0]; let trait_args = &t.args.as_slice()[1..]; if trait_args.is_empty() { @@ -122,10 +119,7 @@ impl<'db> IrPrint> for DbInterner<'db> { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { salsa::with_attached_database(|db| { - let trait_ = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Expected trait."), - }; + let trait_ = t.def_id.0; fmt.write_str(&format!( "ExistentialTraitRef({:?}[{:?}])", db.as_view::().trait_signature(trait_).name.as_str(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 6755d065e10d3..dc54ee7d58785 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -22,6 +22,7 @@ use rustc_type_ir::{ use salsa::plumbing::FromId; use salsa::{Id, plumbing::AsId}; +use crate::next_solver::BoundConst; use crate::{ ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ @@ -125,12 +126,12 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { match c.kind() { rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { - let GenericParamId::ConstParamId(id) = self.params[var.as_usize()] else { + let GenericParamId::ConstParamId(id) = self.params[var.var.as_usize()] else { unreachable!() }; Const::new( self.cx(), - rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32(), id }), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.var.as_u32(), id }), ) } _ => c.super_fold_with(self), @@ -286,11 +287,8 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { .flags .contains(TraitFlags::AUTO) { - ExistentialPredicate::AutoTrait(SolverDefId::TraitId( - trait_id, - )) + ExistentialPredicate::AutoTrait(trait_id.into()) } else { - let def_id = SolverDefId::TraitId(trait_id); let args = GenericArgs::new_from_iter( interner, trait_ref @@ -301,7 +299,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { .map(|a| a.to_nextsolver(interner)), ); let trait_ref = ExistentialTraitRef::new_from_args( - interner, def_id, args, + interner, trait_id.into(), args, ); ExistentialPredicate::Trait(trait_ref) } @@ -473,7 +471,7 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { match &data.value { chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( bound_var.debruijn.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(bound_var.index), + BoundConst { var: rustc_type_ir::BoundVar::from_usize(bound_var.index) }, ), chalk_ir::ConstValue::InferenceVar(inference_var) => { rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( @@ -871,11 +869,7 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { let args = self.substitution.to_nextsolver(interner); - TraitRef::new_from_args( - interner, - SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)), - args, - ) + TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } } @@ -1207,20 +1201,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) trait_ref.def_id, [self_ty.into()].into_iter().chain(trait_ref.args.iter()), ); - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = convert_args_for_result(interner, trait_ref.args.as_slice()); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { - let trait_id = match trait_ { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(trait_.0); let substitution = chalk_ir::Substitution::from1( Interner, convert_ty_for_result(interner, self_ty), @@ -1354,7 +1342,7 @@ pub fn convert_const_for_result<'db>( rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - var.index(), + var.var.index(), )) } rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 50261bb5e3816..c86d3a4aadb22 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -15,6 +15,8 @@ use rustc_type_ir::{ }; use smallvec::{SmallVec, smallvec}; +use crate::next_solver::TraitIdWrapper; + use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>; @@ -72,7 +74,7 @@ interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate); impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> for BoundExistentialPredicates<'db> { - fn principal_def_id(self) -> Option< as rustc_type_ir::Interner>::DefId> { + fn principal_def_id(self) -> Option { self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) } @@ -89,9 +91,7 @@ impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> .transpose() } - fn auto_traits( - self, - ) -> impl IntoIterator as rustc_type_ir::Interner>::DefId> { + fn auto_traits(self) -> impl IntoIterator { self.iter().filter_map(|predicate| match predicate.skip_binder() { ExistentialPredicate::AutoTrait(did) => Some(did), _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 45888ac4d2352..1d0ee0e488dcf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -2,10 +2,10 @@ use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, - lang_items::TraitSolverLangItem, solve::{Certainty, NoSolution}, }; @@ -225,14 +225,14 @@ impl<'db> SolverDelegate for SolverContext<'db> { } if trait_pred.polarity() == PredicatePolarity::Positive { - match self.0.cx().as_lang_item(trait_pred.def_id()) { - Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => { + match self.0.cx().as_trait_lang_item(trait_pred.def_id()) { + Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => { let predicate = self.resolve_vars_if_possible(goal.predicate); if sizedness_fast_path(self.cx(), predicate, goal.param_env) { return Some(Certainty::Yes); } } - Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); // Unlike `Sized` traits, which always prefer the built-in impl, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 92f1076e75eca..4794e2d604509 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -4,6 +4,7 @@ use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::Interner; use rustc_type_ir::{ BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, @@ -19,6 +20,7 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; +use crate::next_solver::GenericArg; use crate::{ db::HirDatabase, interner::InternedWrapperNoDebug, @@ -645,6 +647,31 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) } + fn new_coroutine_witness_for_coroutine( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + coroutine_args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + // HACK: Coroutine witness types are lifetime erased, so they + // never reference any lifetime args from the coroutine. We erase + // the regions here since we may get into situations where a + // coroutine is recursively contained within itself, leading to + // witness types that differ by region args. This means that + // cycle detection in fulfillment will not kick in, which leads + // to unnecessary overflows in async code. See the issue: + // . + let coroutine_args = interner.mk_args_from_iter(coroutine_args.iter().map(|arg| { + match arg { + GenericArg::Ty(_) | GenericArg::Const(_) => arg, + GenericArg::Lifetime(_) => { + crate::next_solver::Region::new(interner, rustc_type_ir::RegionKind::ReErased) + .into() + } + } + })); + Ty::new_coroutine_witness(interner, def_id, coroutine_args) + } + fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self { Ty::new(interner, TyKind::RawPtr(ty, mutbl)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 1db02e9eb611c..50b96a160ed10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -14,7 +14,7 @@ use rustc_type_ir::inherent::{ AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, Ty as _, }; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, @@ -29,8 +29,8 @@ use rustc_type_ir::{InferCtxtLike, TypeFoldable}; use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; use crate::next_solver::infer::InferCtxt; use crate::next_solver::{ - CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, - TypingMode, + BoundConst, CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, + PlaceholderRegion, TypingMode, }; use crate::{ db::HirDatabase, @@ -511,7 +511,7 @@ pub fn apply_args_to_binder<'db, T: TypeFoldable>>( ) -> T { let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); - let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const(); + let consts = &mut |const_: BoundConst| args.as_slice()[const_.var.index()].expect_const(); let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); b.skip_binder().fold_with(&mut instantiate) } @@ -654,7 +654,10 @@ impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { ConstKind::Infer(infer) => { let len = self.vars.len(); let var = *self.vars.entry(c.into()).or_insert(len); - Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var))) + Const::new( + self.cx(), + ConstKind::Bound(self.db, BoundConst { var: BoundVar::from_usize(var) }), + ) } _ => c.super_fold_with(self), } @@ -852,7 +855,7 @@ pub struct PlaceholderReplacer<'a, 'db> { infcx: &'a InferCtxt<'db>, mapped_regions: FxIndexMap, mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_consts: FxIndexMap, universe_indices: &'a [Option], current_index: DebruijnIndex, } @@ -862,7 +865,7 @@ impl<'a, 'db> PlaceholderReplacer<'a, 'db> { infcx: &'a InferCtxt<'db>, mapped_regions: FxIndexMap, mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_consts: FxIndexMap, universe_indices: &'a [Option], value: T, ) -> T { @@ -1026,9 +1029,9 @@ pub fn sizedness_fast_path<'db>( if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder() && trait_pred.polarity == PredicatePolarity::Positive { - let sizedness = match tcx.as_lang_item(trait_pred.def_id()) { - Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized, - Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized, + let sizedness = match tcx.as_trait_lang_item(trait_pred.def_id()) { + Some(SolverTraitLangItem::Sized) => SizedTraitKind::Sized, + Some(SolverTraitLangItem::MetaSized) => SizedTraitKind::MetaSized, _ => return false, }; @@ -1051,7 +1054,10 @@ pub fn sizedness_fast_path<'db>( && clause_pred.self_ty() == trait_pred.self_ty() && (clause_pred.def_id() == trait_pred.def_id() || (sizedness == SizedTraitKind::MetaSized - && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized))) + && tcx.is_trait_lang_item( + clause_pred.def_id(), + SolverTraitLangItem::Sized, + ))) { return true; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 092d4e3a8d9e9..881b1c1a9db26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -212,10 +212,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl rustc_type_ir::ClauseKind::Trait(t) => { let t = rustc_type_ir::EarlyBinder::bind(t).instantiate(interner, trait_ref_args); - let trait_id = match t.def_id() { - crate::next_solver::SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(t.def_id().0); let substitution = convert_args_for_result(interner, t.trait_ref.args.as_slice()); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 46d3a2bcd67ce..6610375451dd0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -86,8 +86,7 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - ClauseKind, DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, - mapping::ChalkToNextSolver, + ClauseKind, DbInterner, GenericArgs, infer::InferCtxt, mapping::ChalkToNextSolver, }, primitive::UintTy, traits::FnTrait, @@ -4251,13 +4250,7 @@ impl TypeParam { db.generic_predicates_for_param_ns(self.id.parent(), self.id.into(), None) .iter() .filter_map(|pred| match &pred.kind().skip_binder() { - ClauseKind::Trait(trait_ref) => { - let trait_ = match trait_ref.def_id() { - SolverDefId::TraitId(t) => t, - _ => unreachable!(), - }; - Some(Trait::from(trait_)) - } + ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, }) .collect() @@ -4515,11 +4508,7 @@ impl Impl { pub fn trait_(self, db: &dyn HirDatabase) -> Option { let trait_ref = db.impl_trait_ns(self.id)?; let id = trait_ref.skip_binder().def_id; - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - Some(Trait { id }) + Some(Trait { id: id.0 }) } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { @@ -4609,11 +4598,7 @@ impl<'db> TraitRef<'db> { } pub fn trait_(&self) -> Trait { - let id = match self.trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - Trait { id } + Trait { id: self.trait_ref.def_id.0 } } pub fn self_ty(&self) -> TypeNs<'_> { @@ -5984,11 +5969,7 @@ impl<'db> TypeNs<'db> { infcx.interner, [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()), ); - let trait_ref = hir_ty::next_solver::TraitRef::new( - infcx.interner, - SolverDefId::TraitId(trait_.id), - args, - ); + let trait_ref = hir_ty::next_solver::TraitRef::new(infcx.interner, trait_.id.into(), args); let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { From 9b0611ff7aeb28bd3fcd3117256a1d9d9c2f1921 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 4 Sep 2025 18:27:27 -0400 Subject: [PATCH 0211/1900] Merge commit 'e9b70454e4c9584be3b22ddabd26b741aeb06c10' into clippy-subtree-update --- .cargo/config.toml | 7 + CHANGELOG.md | 6 + book/src/lint_configuration.md | 14 +- clippy.toml | 6 +- clippy_config/src/conf.rs | 6 +- clippy_lints/Cargo.toml | 4 + .../src/assertions_on_result_states.rs | 27 +- clippy_lints/src/async_yields_async.rs | 60 +++-- clippy_lints/src/bool_comparison.rs | 179 ++++++++++++++ clippy_lints/src/casts/cast_ptr_alignment.rs | 30 +-- .../src/casts/cast_slice_from_raw_parts.rs | 43 +++- clippy_lints/src/casts/mod.rs | 8 +- clippy_lints/src/casts/ptr_as_ptr.rs | 37 +-- clippy_lints/src/casts/ptr_cast_constness.rs | 34 +-- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/derivable_impls.rs | 80 +++++- clippy_lints/src/entry.rs | 67 ++--- clippy_lints/src/eta_reduction.rs | 21 +- clippy_lints/src/float_literal.rs | 45 +++- clippy_lints/src/functions/mod.rs | 2 +- .../src/functions/too_many_arguments.rs | 37 +-- clippy_lints/src/if_then_some_else_none.rs | 32 +-- clippy_lints/src/instant_subtraction.rs | 5 +- clippy_lints/src/large_include_file.rs | 4 +- clippy_lints/src/len_zero.rs | 22 +- clippy_lints/src/lib.rs | 7 +- clippy_lints/src/loops/infinite_loop.rs | 9 +- clippy_lints/src/loops/missing_spin_loop.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 33 ++- .../src/loops/unused_enumerate_index.rs | 5 +- clippy_lints/src/manual_is_ascii_check.rs | 26 +- clippy_lints/src/manual_retain.rs | 10 +- clippy_lints/src/manual_strip.rs | 10 +- clippy_lints/src/map_unit_fn.rs | 21 +- clippy_lints/src/matches/collapsible_match.rs | 49 +++- clippy_lints/src/matches/mod.rs | 11 +- .../matches/significant_drop_in_scrutinee.rs | 9 +- clippy_lints/src/matches/try_err.rs | 40 +-- clippy_lints/src/mem_replace.rs | 7 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 7 +- clippy_lints/src/methods/filter_map.rs | 14 +- .../src/methods/iter_out_of_bounds.rs | 45 ++-- clippy_lints/src/methods/map_flatten.rs | 8 +- clippy_lints/src/methods/map_identity.rs | 96 ++++++-- .../src/methods/option_as_ref_deref.rs | 28 +-- clippy_lints/src/methods/or_then_unwrap.rs | 2 +- .../src/methods/read_line_without_trim.rs | 4 +- .../src/methods/single_char_add_str.rs | 8 +- .../src/methods/suspicious_to_owned.rs | 5 +- .../src/methods/unnecessary_min_or_max.rs | 3 +- .../src/methods/unused_enumerate_index.rs | 7 +- clippy_lints/src/missing_inline.rs | 38 ++- clippy_lints/src/mut_reference.rs | 13 +- clippy_lints/src/needless_bool.rs | 228 +---------------- clippy_lints/src/needless_for_each.rs | 36 ++- clippy_lints/src/non_canonical_impls.rs | 9 +- .../src/non_octal_unix_permissions.rs | 12 +- .../src/operators/assign_op_pattern.rs | 27 +- clippy_lints/src/operators/cmp_owned.rs | 12 +- .../operators/float_equality_without_abs.rs | 5 +- clippy_lints/src/panic_unimplemented.rs | 6 +- clippy_lints/src/redundant_clone.rs | 18 +- clippy_lints/src/semicolon_block.rs | 5 + clippy_lints/src/std_instead_of_core.rs | 2 + clippy_lints/src/strings.rs | 7 +- .../missing_transmute_annotations.rs | 55 ++++- clippy_lints/src/transmute/mod.rs | 2 +- .../transmute/transmute_int_to_non_zero.rs | 53 ++-- clippy_lints/src/types/rc_buffer.rs | 5 +- .../src/types/redundant_allocation.rs | 13 +- clippy_lints/src/unit_types/unit_cmp.rs | 9 +- .../src/unnecessary_owned_empty_strings.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 10 +- clippy_lints/src/unnested_or_patterns.rs | 6 +- clippy_lints/src/unused_io_amount.rs | 4 +- clippy_lints/src/unused_unit.rs | 58 +++-- clippy_lints/src/unwrap_in_result.rs | 206 ++++++++++++---- clippy_lints/src/useless_conversion.rs | 9 +- clippy_lints/src/vec.rs | 232 +++++++++++------- clippy_lints/src/write.rs | 13 +- clippy_lints/src/zombie_processes.rs | 20 +- clippy_utils/README.md | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/higher.rs | 27 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 55 ++++- clippy_utils/src/ty/mod.rs | 9 +- clippy_utils/src/usage.rs | 8 +- rust-toolchain.toml | 2 +- src/driver.rs | 3 +- tests/config-consistency.rs | 30 +++ tests/no-profile-in-cargo-toml.rs | 34 +++ tests/ui-internal/disallow_span_lint.stderr | 4 +- tests/ui-toml/excessive_precision/clippy.toml | 1 + .../excessive_precision.fixed | 38 +++ .../excessive_precision.rs | 38 +++ .../excessive_precision.stderr | 38 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/assertions_on_result_states.fixed | 11 +- tests/ui/assertions_on_result_states.rs | 11 +- tests/ui/assertions_on_result_states.stderr | 10 +- tests/ui/assign_ops.fixed | 39 ++- tests/ui/assign_ops.rs | 41 +++- tests/ui/assign_ops.stderr | 50 +++- tests/ui/async_yields_async.fixed | 39 +++ tests/ui/async_yields_async.rs | 39 +++ tests/ui/async_yields_async.stderr | 47 +++- tests/ui/bool_comparison.fixed | 167 +++++-------- tests/ui/bool_comparison.rs | 167 +++++-------- tests/ui/bool_comparison.stderr | 154 ++++++------ tests/ui/cast_raw_slice_pointer_cast.fixed | 39 ++- tests/ui/cast_raw_slice_pointer_cast.rs | 25 +- tests/ui/cast_raw_slice_pointer_cast.stderr | 66 +++-- .../cast_raw_slice_pointer_cast_no_std.fixed | 55 +++++ .../ui/cast_raw_slice_pointer_cast_no_std.rs | 55 +++++ .../cast_raw_slice_pointer_cast_no_std.stderr | 83 +++++++ tests/ui/collapsible_match.rs | 41 ++++ tests/ui/collapsible_match.stderr | 71 +++++- tests/ui/collapsible_match2.stderr | 2 + tests/ui/derivable_impls.fixed | 38 +++ tests/ui/derivable_impls.rs | 41 ++++ tests/ui/derivable_impls.stderr | 47 +++- tests/ui/derivable_impls_derive_const.fixed | 25 ++ tests/ui/derivable_impls_derive_const.rs | 32 +++ tests/ui/derivable_impls_derive_const.stderr | 46 ++++ tests/ui/doc/doc-fixable.fixed | 1 + tests/ui/doc/doc-fixable.rs | 1 + tests/ui/doc/doc-fixable.stderr | 44 ++-- tests/ui/entry_unfixable.stderr | 5 + tests/ui/eta.fixed | 45 ++++ tests/ui/eta.rs | 45 ++++ tests/ui/eta.stderr | 28 ++- tests/ui/excessive_precision.fixed | 45 +++- tests/ui/excessive_precision.rs | 45 +++- tests/ui/excessive_precision.stderr | 85 +++++-- tests/ui/float_equality_without_abs.rs | 14 +- tests/ui/float_equality_without_abs.stderr | 18 +- tests/ui/if_then_some_else_none.fixed | 41 ++++ tests/ui/if_then_some_else_none.rs | 51 ++++ tests/ui/if_then_some_else_none.stderr | 25 +- tests/ui/infinite_loops.rs | 15 ++ tests/ui/infinite_loops.stderr | 12 +- tests/ui/manual_is_ascii_check.fixed | 4 + tests/ui/manual_is_ascii_check.rs | 4 + tests/ui/manual_is_ascii_check.stderr | 30 ++- tests/ui/map_identity.fixed | 20 +- tests/ui/map_identity.rs | 16 +- tests/ui/map_identity.stderr | 66 +++-- tests/ui/missing_inline.rs | 7 + ...missing_transmute_annotations_unfixable.rs | 29 +++ ...ing_transmute_annotations_unfixable.stderr | 36 +++ tests/ui/mut_reference.fixed | 170 +++++++++++++ tests/ui/mut_reference.rs | 152 ++++++++++-- tests/ui/mut_reference.stderr | 76 +++++- tests/ui/needless_bool/fixable.stderr | 8 +- tests/ui/needless_for_each_unfixable.rs | 19 +- tests/ui/needless_for_each_unfixable.stderr | 38 ++- tests/ui/needless_range_loop.rs | 32 +++ tests/ui/needless_range_loop.stderr | 26 +- tests/ui/nonminimal_bool.rs | 2 - tests/ui/nonminimal_bool.stderr | 50 ++-- tests/ui/option_map_unit_fn_fixable.fixed | 9 + tests/ui/option_map_unit_fn_fixable.rs | 9 + tests/ui/option_map_unit_fn_fixable.stderr | 18 +- tests/ui/or_then_unwrap.fixed | 6 + tests/ui/or_then_unwrap.rs | 6 + tests/ui/or_then_unwrap.stderr | 10 +- tests/ui/panicking_macros.rs | 15 ++ tests/ui/panicking_macros.stderr | 46 ++-- tests/ui/print_literal.fixed | 13 + tests/ui/print_literal.rs | 14 ++ tests/ui/print_literal.stderr | 38 ++- tests/ui/ptr_as_ptr.fixed | 6 + tests/ui/ptr_as_ptr.rs | 6 + tests/ui/ptr_as_ptr.stderr | 8 +- tests/ui/ptr_cast_constness.fixed | 6 + tests/ui/ptr_cast_constness.rs | 6 + tests/ui/ptr_cast_constness.stderr | 8 +- tests/ui/semicolon_inside_block.fixed | 5 + tests/ui/semicolon_inside_block.rs | 5 + ...micolon_inside_block_stmt_expr_attrs.fixed | 11 + .../semicolon_inside_block_stmt_expr_attrs.rs | 11 + ...icolon_inside_block_stmt_expr_attrs.stderr | 16 ++ tests/ui/std_instead_of_core.fixed | 7 + tests/ui/std_instead_of_core.rs | 7 + tests/ui/unit_cmp.rs | 17 ++ tests/ui/unit_cmp.stderr | 20 +- tests/ui/unnecessary_clone.stderr | 12 +- tests/ui/unnested_or_patterns.fixed | 25 +- tests/ui/unnested_or_patterns.rs | 25 +- tests/ui/unnested_or_patterns.stderr | 54 ++-- tests/ui/unused_unit.edition2021.fixed | 27 +- tests/ui/unused_unit.edition2021.stderr | 6 +- tests/ui/unused_unit.edition2024.fixed | 29 ++- tests/ui/unused_unit.rs | 29 ++- tests/ui/unwrap_in_result.rs | 70 +++++- tests/ui/unwrap_in_result.stderr | 132 +++++++--- tests/ui/vec.fixed | 9 + tests/ui/vec.rs | 9 + tests/ui/vec.stderr | 8 +- triagebot.toml | 7 + 202 files changed, 4536 insertions(+), 1641 deletions(-) create mode 100644 clippy_lints/src/bool_comparison.rs create mode 100644 tests/config-consistency.rs create mode 100644 tests/no-profile-in-cargo-toml.rs create mode 100644 tests/ui-toml/excessive_precision/clippy.toml create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.fixed create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.rs create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.stderr create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.fixed create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.rs create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.stderr create mode 100644 tests/ui/derivable_impls_derive_const.fixed create mode 100644 tests/ui/derivable_impls_derive_const.rs create mode 100644 tests/ui/derivable_impls_derive_const.stderr create mode 100644 tests/ui/missing_transmute_annotations_unfixable.rs create mode 100644 tests/ui/missing_transmute_annotations_unfixable.stderr create mode 100644 tests/ui/mut_reference.fixed create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.rs create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr diff --git a/.cargo/config.toml b/.cargo/config.toml index d9c635df5dc79..a09bf95e87bd6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -23,3 +23,10 @@ split-debuginfo = "unpacked" rustflags = ["--remap-path-prefix", "=clippy_dev"] [profile.dev.package.lintcheck] rustflags = ["--remap-path-prefix", "=lintcheck"] + +# quine-mc_cluskey makes up a significant part of the runtime in dogfood +# due to the number of conditions in the clippy_lints crate +# and enabling optimizations for that specific dependency helps a bit +# without increasing total build times. +[profile.dev.package.quine-mc_cluskey] +opt-level = 3 diff --git a/CHANGELOG.md b/CHANGELOG.md index bc60b1c57f7d1..eb2a76a818369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ Current stable, released 2025-08-07 * Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) + [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) ### Enhancements @@ -74,6 +76,9 @@ Current stable, released 2025-08-07 [#14719](https://github.com/rust-lang/rust-clippy/pull/14719) * [`unnecessary_to_owned`] fixed FP when map key is a reference [#14834](https://github.com/rust-lang/rust-clippy/pull/14834) +* [`swap_with_temporary`]: fix false positive leading to different semantics + being suggested, and use the right number of dereferences in suggestion + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) ### ICE Fixes @@ -6707,6 +6712,7 @@ Released 2018-09-13 [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold +[`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 05590ff7b1c9b..c2d080cd96a1d 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -485,6 +485,16 @@ The maximum cognitive complexity a function can have * [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) +## `const-literal-digits-threshold` +The minimum digits a const float literal must have to supress the `excessive_precicion` lint + +**Default Value:** `30` + +--- +**Affected lints:** +* [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision) + + ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. @@ -555,7 +565,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -873,7 +883,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) -* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -881,7 +890,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) -* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) diff --git a/clippy.toml b/clippy.toml index 77573105d86a3..d9bcfd17606e4 100644 --- a/clippy.toml +++ b/clippy.toml @@ -6,12 +6,12 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2ad3f2efcdd7a..2f28f6175ad82 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -33,6 +33,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", + "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", @@ -569,6 +570,9 @@ define_Conf! { /// The maximum cognitive complexity a function can have #[lints(cognitive_complexity)] cognitive_complexity_threshold: u64 = 25, + /// The minimum digits a const float literal must have to supress the `excessive_precicion` lint + #[lints(excessive_precision)] + const_literal_digits_threshold: usize = 30, /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. /// /// Use the Cognitive Complexity lint instead. @@ -775,7 +779,6 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, - option_map_unwrap_or, ptr_as_ptr, question_mark, redundant_field_names, @@ -783,7 +786,6 @@ define_Conf! { repeat_vec_with_capacity, same_item_push, seek_from_current, - seek_rewind, to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index b9e6de79cc05c..70184ee2ca596 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -27,6 +27,10 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(bootstrap)'] + [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 6f2a6a36a38b7..08253b0c4995e 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -3,10 +3,10 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res, sym}; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; @@ -77,17 +77,20 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { _ => return, }; span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; let mut app = Applicability::MachineApplicable; - diag.span_suggestion( - macro_call.span, - "replace with", - format!( - "{}.{replacement}(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + let recv = snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0; + + // `assert!` doesn't return anything, but `Result::unwrap(_err)` does, so we might need to add a + // semicolon to the suggestion to avoid leaking the type + let sugg = match cx.tcx.parent_hir_node(e.hir_id) { + // trailing expr of a block + Node::Block(..) => format!("{recv}.{replacement}();"), + // already has a trailing semicolon + Node::Stmt(..) => format!("{recv}.{replacement}()"), + // this is the last-resort option, because it's rather verbose + _ => format!("{{ {recv}.{replacement}(); }}"), + }; + diag.span_suggestion(macro_call.span, "replace with", sugg, app); }); } } diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 013819b0da8ae..1a10db291cdea 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,8 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::is_expr_async_block; +use clippy_utils::source::walk_span_to_context; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; -use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath}; +use rustc_hir::{ + Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { let expr_ty = typeck_results.expr_ty(body_expr); if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { - let return_expr_span = match &body_expr.kind { - // XXXkhuey there has to be a better way. - ExprKind::Block(block, _) => block.expr.map(|e| e.span), - ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), - _ => None, + let (return_expr, return_expr_span) = match &body_expr.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span), + ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span), + _ => return, }; - if let Some(return_expr_span) = return_expr_span { - span_lint_hir_and_then( - cx, - ASYNC_YIELDS_ASYNC, - body_expr.hir_id, - return_expr_span, - "an async construct yields a type which is itself awaitable", - |db| { - db.span_label(body_expr.span, "outer async construct"); - db.span_label(return_expr_span, "awaitable value not awaited"); - db.span_suggestion( - return_expr_span, - "consider awaiting this value", - format!("{}.await", snippet(cx, return_expr_span, "..")), - Applicability::MaybeIncorrect, - ); - }, - ); + + let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span); + let mut applicability = Applicability::MaybeIncorrect; + let mut return_expr_snip = + Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability); + if !is_expr_async_block(return_expr) { + return_expr_snip = return_expr_snip.maybe_paren(); } + + span_lint_hir_and_then( + cx, + ASYNC_YIELDS_ASYNC, + body_expr.hir_id, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body_expr.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{return_expr_snip}.await"), + applicability, + ); + }, + ); } } } diff --git a/clippy_lints/src/bool_comparison.rs b/clippy_lints/src/bool_comparison.rs new file mode 100644 index 0000000000000..722095909a6fe --- /dev/null +++ b/clippy_lints/src/bool_comparison.rs @@ -0,0 +1,179 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_expn_of, peel_blocks, sym}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions of the form `x == true`, + /// `x != true` and order comparisons such as `x < true` (or vice versa) and + /// suggest using the variable directly. + /// + /// ### Why is this bad? + /// Unnecessary code. + /// + /// ### Example + /// ```rust,ignore + /// if x == true {} + /// if y == false {} + /// ``` + /// use `x` directly: + /// ```rust,ignore + /// if x {} + /// if !y {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub BOOL_COMPARISON, + complexity, + "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" +} + +declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); + +impl<'tcx> LateLintPass<'tcx> for BoolComparison { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node, .. }, left_side, right_side) = e.kind + && is_expn_of(left_side.span, sym::cfg).is_none() + && is_expn_of(right_side.span, sym::cfg).is_none() + && cx.typeck_results().expr_ty(left_side).is_bool() + && cx.typeck_results().expr_ty(right_side).is_bool() + { + let ignore_case = None::<(fn(_) -> _, &str)>; + let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; + match node { + BinOpKind::Eq => { + let true_case = Some((|h| h, "equality checks against true are unnecessary")); + let false_case = Some(( + |h: Sugg<'tcx>| !h, + "equality checks against false can be replaced by a negation", + )); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Ne => { + let true_case = Some(( + |h: Sugg<'tcx>| !h, + "inequality checks against true can be replaced by a negation", + )); + let false_case = Some((|h| h, "inequality checks against false are unnecessary")); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Lt => check_comparison( + cx, + e, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), + "order comparisons between booleans can be simplified", + )), + ), + BinOpKind::Gt => check_comparison( + cx, + e, + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), + "order comparisons between booleans can be simplified", + )), + ), + _ => (), + } + } + } +} + +fn check_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, +) { + if let ExprKind::Binary(_, left_side, right_side) = e.kind { + let mut applicability = Applicability::MachineApplicable; + // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, + // calling `source_callsite` make sure macros are handled correctly, see issue #9907 + let binop_span = left_side.span.source_callsite().to(right_side.span.source_callsite()); + + match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { + (Some(true), None) => left_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(true)) => right_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (Some(false), None) => left_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(false)) => right_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (None, None) => no_literal.map_or((), |(h, m)| { + let left_side = Sugg::hir_with_context(cx, left_side, binop_span.ctxt(), "..", &mut applicability); + let right_side = Sugg::hir_with_context(cx, right_side, binop_span.ctxt(), "..", &mut applicability); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + binop_span, + m, + "try", + h(left_side, right_side).into_string(), + applicability, + ); + }), + _ => (), + } + } +} + +fn suggest_bool_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + span: Span, + expr: &Expr<'_>, + mut app: Applicability, + message: &'static str, + conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, +) { + let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + span, + message, + "try", + conv_hint(hint).into_string(), + app, + ); +} + +fn fetch_bool_expr(expr: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); + } + None +} diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index a1543cabd2f9d..d78da9396faf3 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -8,17 +8,12 @@ use rustc_middle::ty::{self, Ty}; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { - if is_hir_ty_cfg_dependant(cx, cast_to) { - return; - } - let (cast_from, cast_to) = ( - cx.typeck_results().expr_ty(cast_expr), - cx.typeck_results().expr_ty(expr), - ); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); +} + +pub(super) fn check_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind && method_path.ident.name == sym::cast && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args @@ -74,14 +69,13 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { if let ExprKind::Path(path) = &func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some( - sym::ptr_write_unaligned - | sym::ptr_read_unaligned - | sym::intrinsics_unaligned_volatile_load - | sym::intrinsics_unaligned_volatile_store - ) + name, + sym::ptr_write_unaligned + | sym::ptr_read_unaligned + | sym::intrinsics_unaligned_volatile_load + | sym::intrinsics_unaligned_volatile_store ) { true diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 46b0c88d3feda..bce7b4c69cc62 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::{get_parent_expr, is_no_std_crate}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; @@ -42,13 +44,52 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, let mut applicability = Applicability::MachineApplicable; let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; span_lint_and_sugg( cx, CAST_SLICE_FROM_RAW_PARTS, span, format!("casting the result of `{func}` to {cast_to}"), "replace with", - format!("core::ptr::slice_{func}({ptr}, {len})"), + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), + applicability, + ); + } +} + +/// Checks for implicit cast from slice reference to raw slice pointer. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(fun, [ptr_arg, len_arg]) = expr.peel_blocks().kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(rpk) = raw_parts_kind(cx, fun_def_id) + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(..)), + target, + } = borrow + && let ty::RawPtr(pointee_ty, _) = target.kind() + && pointee_ty.is_slice() + && !expr.span.from_expansion() + { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut", + }; + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + expr.span, + format!("implicitly casting the result of `{func}` to `{target}`"), + "replace_with", + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), applicability, ); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index e25df9dd249a6..d2e62ee56e43b 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -873,7 +873,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } char_lit_as_u8::check(cx, expr, cast_from_expr, cast_to); cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv); + cast_ptr_alignment::check(cx, expr, cast_from, cast_to); ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); + ptr_as_ptr::check(cx, expr, cast_from_expr, cast_from, cast_to_hir, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); @@ -911,8 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if self.msrv.meets(cx, msrvs::RAW_REF_OP) { borrow_as_ptr::check_implicit_cast(cx, expr); } - cast_ptr_alignment::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); + if self.msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + cast_slice_from_raw_parts::check_implicit_cast(cx, expr); + } + cast_ptr_alignment::check_cast_method(cx, expr); cast_slice_different_sizes::check(cx, expr, self.msrv); ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 890754090989f..d012380cb762f 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -4,9 +4,9 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use super::PTR_AS_PTR; @@ -26,13 +26,18 @@ impl OmitFollowedCastReason<'_> { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) { - if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind - && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) - && let ty::RawPtr(_, from_mutbl) = cast_from.kind() +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cast_from_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to_hir: &hir::Ty<'_>, + cast_to: Ty<'tcx>, + msrv: Msrv, +) { + if let ty::RawPtr(_, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() - && matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) + && from_mutbl == to_mutbl // The `U` in `pointer::cast` have to be `Sized` // as explained here: https://github.com/rust-lang/rust/issues/60602. && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) @@ -40,7 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) && !is_from_proc_macro(cx, expr) { let mut app = Applicability::MachineApplicable; - let turbofish = match &cast_to_hir_ty.kind { + let turbofish = match &cast_to_hir.kind { TyKind::Infer(()) => String::new(), TyKind::Ptr(mut_ty) => { if matches!(mut_ty.ty.kind, TyKind::Infer(())) { @@ -58,16 +63,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) // following `cast` does not compile because it fails to infer what type is expected // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so // we omit following `cast`: - let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind + let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind && let Some(method_defid) = path.res.opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { - OmitFollowedCastReason::Null(qpath) - } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { - OmitFollowedCastReason::NullMut(qpath) - } else { - OmitFollowedCastReason::None + match cx.tcx.get_diagnostic_name(method_defid) { + Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath), + Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath), + _ => OmitFollowedCastReason::None, } } else { OmitFollowedCastReason::None @@ -78,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app); ("try call directly", format!("{method}{turbofish}()")) } else { - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app); ( "try `pointer::cast`, a safer alternative", diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index c0c0a47f85515..c0f13f5e57823 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -12,26 +13,23 @@ use super::PTR_CAST_CONSTNESS; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &Expr<'_>, - cast_expr: &Expr<'_>, + cast_from_expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>, msrv: Msrv, ) { if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() - && matches!( - (from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) - ) + && from_mutbl != to_mutbl && from_ty == to_ty && !from_ty.has_erased_regions() { - if let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), @@ -53,11 +51,17 @@ pub(super) fn check<'tcx>( if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", + let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind + && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind + && let hir::TyKind::Infer(()) = ptr_ty.ty.kind + { + // `(foo as *const _).cast_mut()` fails method name resolution + // avoid this by `as`-ing the full type + Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from) + } else { + Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app) }; + let constness = to_mutbl.ptr_str(); span_lint_and_sugg( cx, @@ -73,8 +77,8 @@ pub(super) fn check<'tcx>( } pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind - && let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { @@ -84,7 +88,7 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) } && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") { span_lint_and_sugg( diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 8f95c63a854f5..7646aa48b7726 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -23,8 +23,8 @@ declare_clippy_lint! { /// /// ### Known problems /// The true Cognitive Complexity of a method is not something we can - /// calculate using modern technology. This lint has been left in the - /// `nursery` so as to not mislead users into using this lint as a + /// calculate using modern technology. This lint has been left in + /// `restriction` so as to not mislead users into using this lint as a /// measurement tool. /// /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e67e8d9070f2c..d0c7443a4a4b9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,6 +35,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO, crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_comparison::BOOL_COMPARISON_INFO, crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, crate::booleans::NONMINIMAL_BOOL_INFO, crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, @@ -538,7 +539,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, - crate::needless_bool::BOOL_COMPARISON_INFO, crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 7580d6cab66d4..06c2393e0a39f 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -10,7 +10,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; -use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults}; +use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults, VariantDef}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool { } } +fn determine_derive_macro(cx: &LateContext<'_>, is_const: bool) -> Option<&'static str> { + (!is_const) + .then_some("derive") + .or_else(|| cx.tcx.features().enabled(sym::derive_const).then_some("derive_const")) +} + +#[expect(clippy::too_many_arguments)] fn check_struct<'tcx>( cx: &LateContext<'tcx>, item: &'tcx Item<'_>, @@ -93,6 +100,7 @@ fn check_struct<'tcx>( adt_def: AdtDef<'_>, ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, + is_const: bool, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind && let Some(PathSegment { args, .. }) = p.segments.last() @@ -125,14 +133,18 @@ fn check_struct<'tcx>( ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts), ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts), ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)), - _ => false, + _ => return, }; - if should_emit { + if should_emit && let Some(derive_snippet) = determine_derive_macro(cx, is_const) { let struct_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, struct_span).unwrap_or(0); let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation - (struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute + ( + struct_span.shrink_to_lo(), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), + ), // Add the derive attribute ]; span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { @@ -145,11 +157,41 @@ fn check_struct<'tcx>( } } -fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { - if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res - && let variant_id = cx.tcx.parent(id) - && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) +fn extract_enum_variant<'tcx>( + cx: &LateContext<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, +) -> Option<&'tcx VariantDef> { + match &peel_blocks(func_expr).kind { + ExprKind::Path(QPath::Resolved(None, p)) + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let variant_id = cx.tcx.parent(id) + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) => + { + Some(variant_def) + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) + if let TyKind::Path(QPath::Resolved(None, p)) = &ty.kind + && let Res::SelfTyAlias { + is_trait_impl: true, .. + } = p.res + && let variant_ident = segment.ident + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.ident(cx.tcx) == variant_ident) => + { + Some(variant_def) + }, + _ => None, + } +} + +fn check_enum<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, + is_const: bool, +) { + if let Some(variant_def) = extract_enum_variant(cx, func_expr, adt_def) && variant_def.fields.is_empty() && !variant_def.is_field_list_non_exhaustive() { @@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex let variant_span = cx.tcx.def_span(variant_def.def_id); let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + let Some(derive_snippet) = determine_derive_macro(cx, is_const) else { + return; + }; + let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation ( enum_span.shrink_to_lo(), - format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), ), // Add the derive attribute ( variant_span.shrink_to_lo(), @@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && !attrs.iter().any(|attr| attr.doc_str().is_some()) && cx.tcx.hir_attrs(impl_item_hir).is_empty() { + let is_const = of_trait.constness == hir::Constness::Const; if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); + check_struct( + cx, + item, + self_ty, + func_expr, + adt_def, + args, + cx.tcx.typeck_body(*b), + is_const, + ); } else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) { - check_enum(cx, item, func_expr, adt_def); + check_enum(cx, item, func_expr, adt_def, is_const); } } } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 182cb4e46d2bd..a5ec6777b4344 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; @@ -194,7 +194,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if let Some(sugg) = sugg { span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); } else { - span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + span_lint_and_help( + cx, + MAP_ENTRY, + expr.span, + lint_msg, + None, + format!( + "consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.{}.html#entry-api", + map_ty.name() + ), + ); } } } @@ -254,35 +264,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio _ => None, }); - match expr.kind { - ExprKind::MethodCall( - _, + if let ExprKind::MethodCall(_, map, [arg], _) = expr.kind + && let Expr { + kind: ExprKind::AddrOf(_, _, key), + span: key_span, + .. + } = arg + && key_span.eq_ctxt(expr.span) + { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + let expr = ContainsExpr { + negated, map, - [ - Expr { - kind: ExprKind::AddrOf(_, _, key), - span: key_span, - .. - }, - ], - _, - ) if key_span.eq_ctxt(expr.span) => { - let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - let expr = ContainsExpr { - negated, - map, - key, - call_ctxt: expr.span.ctxt(), - }; - if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) { - Some((MapType::BTree, expr)) - } else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) { - Some((MapType::Hash, expr)) - } else { - None - } - }, - _ => None, + key, + call_ctxt: expr.span.ctxt(), + }; + match cx.tcx.get_diagnostic_name(id) { + Some(sym::btreemap_contains_key) => Some((MapType::BTree, expr)), + Some(sym::hashmap_contains_key) => Some((MapType::Hash, expr)), + _ => None, + } + } else { + None } } @@ -311,7 +314,9 @@ struct InsertExpr<'tcx> { fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) { + if let Some(insert) = cx.tcx.get_diagnostic_name(id) + && matches!(insert, sym::btreemap_insert | sym::hashmap_insert) + { Some(InsertExpr { map, key, value }) } else { None diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 0eefc2f610969..2da1c2bad1174 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,6 +12,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -148,10 +149,9 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { return; } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + + let callee_ty_adjustments = typeck.expr_adjustments(callee); + let callee_ty_adjusted = callee_ty_adjustments.last().map_or(callee_ty, |a| a.target); let sig = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => { @@ -230,7 +230,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }, _ => (), } + } else if let n_refs = + callee_ty_adjustments + .iter() + .rev() + .fold(0, |acc, adjustment| match adjustment.kind { + Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(_) if acc > 0 => acc + 1, + _ => acc, + }) + && n_refs > 0 + { + snippet = format!("{}{snippet}", "*".repeat(n_refs)); } + let replace_with = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => cx.tcx.def_descr(*def), _ => "function", diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index ccaf38aee4d88..6178addfff127 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::numeric_literal; +use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use std::fmt; declare_clippy_lint! { @@ -13,6 +14,8 @@ declare_clippy_lint! { /// Checks for float literals with a precision greater /// than that supported by the underlying type. /// + /// The lint is suppressed for literals with over `const_literal_digits_threshold` digits. + /// /// ### Why is this bad? /// Rust will truncate the literal silently. /// @@ -58,7 +61,21 @@ declare_clippy_lint! { "lossy whole number float literals" } -declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); +pub struct FloatLiteral { + const_literal_digits_threshold: usize, +} + +impl_lint_pass!(FloatLiteral => [ + EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL +]); + +impl FloatLiteral { + pub fn new(conf: &'static Conf) -> Self { + Self { + const_literal_digits_threshold: conf.const_literal_digits_threshold, + } + } +} impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { + } else if digits > max as usize && count_digits(&float_str) < digits { + if digits >= self.const_literal_digits_threshold + && matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_)) + { + // If a big enough number of digits is specified and it's a constant + // we assume the user is definining a constant, and excessive precision is ok + return; + } span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", |diag| { + if digits >= self.const_literal_digits_threshold + && let Some(let_stmt) = maybe_let_stmt(cx, expr) + { + diag.span_note(let_stmt.span, "consider making it a `const` item"); + } diag.span_suggestion_verbose( expr.span, "consider changing the type or truncating it to", @@ -196,3 +225,11 @@ impl FloatFormat { } } } + +fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + match parent { + hir::Node::LetStmt(let_stmt) => Some(let_stmt), + _ => None, + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ca5ea90181493..5a40af4219426 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -535,7 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { def_id: LocalDefId, ) { let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_arguments::check_fn(cx, kind, decl, hir_id, def_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, body, span, def_id, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 48d050aa36aa0..6c3c3d354ecc9 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -1,5 +1,7 @@ use rustc_abi::ExternAbi; -use rustc_hir::{self as hir, intravisit}; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; use rustc_lint::LateContext; use rustc_span::Span; @@ -10,39 +12,18 @@ use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: intravisit::FnKind<'_>, + kind: FnKind<'_>, decl: &hir::FnDecl<'_>, - span: Span, hir_id: hir::HirId, + def_id: LocalDefId, too_many_arguments_threshold: u64, ) { // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { + if !is_trait_impl_item(cx, hir_id) // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - .. - }, - ) - | intravisit::FnKind::ItemFn( - _, - _, - hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - ) => check_arg_number( - cx, - decl, - span.with_hi(decl.output.span().hi()), - too_many_arguments_threshold, - ), - _ => {}, - } + && kind.header().is_some_and(|header| header.abi == ExternAbi::Rust) + { + check_arg_number(cx, decl, cx.tcx.def_span(def_id), too_many_arguments_threshold); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 7158f9419c1cc..b50d91f101463 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,16 +2,16 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, + path_res, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ctxt = expr.span.ctxt() - && then_expr.span.ctxt() == ctxt + && !expr.span.from_expansion() + && !then_expr.span.from_expansion() && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) - && !expr.span.in_external_macro(cx.sess().source_map()) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { - "then_some" + sym::then_some } else { - "then" + sym::then }; + let ctxt = expr.span.ctxt(); span_lint_and_then( cx, @@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } let mut app = Applicability::MachineApplicable; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app) .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let method_body = if let Some(first_stmt) = then_block.stmts.first() { - let (block_snippet, _) = - snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); - let closure = if method_name == "then" { "|| " } else { "" }; - format!("{closure} {{ {block_snippet}; {arg_snip} }}") - } else if method_name == "then" { + let method_body = if let Some(first_stmt) = then_block.stmts.first() + && let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt) + { + let block_snippet = + snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app); + let closure = if method_name == sym::then { "|| " } else { "" }; + format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end()) + } else if method_name == sym::then { (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 91f65d0b79ca0..13117f60abd52 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::ty; +use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,8 +107,7 @@ impl LateLintPass<'_> for InstantSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::instant_now, fn_id) + && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) { true } else { diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 8707612fbdd0a..48ce1afc6e69b 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -64,8 +64,8 @@ impl LateLintPass<'_> for LargeIncludeFile { } && len as u64 > self.max_file_size && let Some(macro_call) = root_macro_call_first_node(cx, expr) - && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) - || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) + && let Some(macro_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + && matches!(macro_name, sym::include_bytes_macro | sym::include_str_macro) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 57deb011f2b0e..f44a5fdf715e5 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -355,12 +355,15 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Option(def_id)), + Some(sym::Result) => Some(LenOutput::Result(def_id)), + _ => None, } + && is_first_generic_integral(segment) + { + return Some(res); } return None; @@ -368,11 +371,10 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) - }, - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())) + ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => Some(LenOutput::Option(adt.did())), + Some(sym::Result) => Some(LenOutput::Result(adt.did())), + _ => None, }, _ => None, } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c8594cf35e23e..a89cf3fdc1eee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -84,6 +84,7 @@ mod attrs; mod await_holding_invalid; mod blocks_in_conditions; mod bool_assert_comparison; +mod bool_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; @@ -474,10 +475,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(types::Types::new(conf))); store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); - store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); + store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf))); store.register_late_pass(|_| Box::new(ptr::Ptr)); store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); + store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); @@ -655,7 +656,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); - store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); let attrs = attr_storage.clone(); diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index a71e6963f8ca2..74c0b17801898 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -4,7 +4,8 @@ use hir::intravisit::{Visitor, walk_expr}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::{ - self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprKind, FnRetTy, FnSig, Node, TyKind, + self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnRetTy, + FnSig, Node, TyKind, }; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -73,7 +74,11 @@ fn is_inside_unawaited_async_block(cx: &LateContext<'_>, expr: &Expr<'_>) -> boo if let Node::Expr(Expr { kind: ExprKind::Closure(Closure { - kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)), + kind: + ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block | CoroutineSource::Closure, + )), .. }), .. diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index a9944d64ce2dd..8a2d0036203a2 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,10 +1,10 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::std_or_core; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { @@ -39,8 +39,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' ) = body.kind && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) - && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() - && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + && let callee_ty = cx.typeck_results().expr_ty(callee) + && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 7bb684d65bb42..11edb929d70b2 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; +use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -253,12 +253,38 @@ struct VarVisitor<'a, 'tcx> { impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); + let mut used_cnt = 0; + // It is `true` if all indices are direct + let mut index_used_directly = true; + + // Handle initial index + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + // Handle nested indices + let seqexpr = peel_hir_expr_while(seqexpr, |e| { + if let ExprKind::Index(e, idx, _) = e.kind { + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + Some(e) + } else { + None + } + }); + + match used_cnt { + 0 => return true, + n if n > 1 => self.nonindex = true, // Optimize code like `a[i][i]` + _ => {}, + } + if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name && let QPath::Resolved(None, seqvar) = *seqpath && seqvar.segments.len() == 1 - && is_local_used(self.cx, idx, self.var) { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); @@ -312,7 +338,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind - // a range index op && let Some(trait_id) = self .cx .typeck_results() diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 51e21aa9734ed..13b93d2c0097b 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,12 +1,12 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. @@ -17,8 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && let ty::Adt(base, _) = *ty.kind() - && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did()) + && is_type_diagnostic_item(cx, ty, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index ac8c88f02057b..2eebb2430fd94 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -97,11 +97,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) { - if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { - let range = check_pat(&arm.pat.kind); - check_is_ascii(cx, macro_call.span, recv, &range, None); - } + let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) + && let ExprKind::Match(recv, [arm, ..], _) = expr.kind + { + let recv = peel_ref_operators(cx, recv); + let range = check_pat(&arm.pat.kind); + (recv, macro_call.span, range) } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind && path.ident.name == sym::contains && let Some(higher::Range { @@ -112,10 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg); let range = check_expr_range(start, end); - check_is_ascii(cx, expr.span, arg, &range, ty_sugg); - } + (arg, expr.span, range) + } else { + return; + }; + + let ty_sugg = get_ty_sugg(cx, arg); + check_is_ascii(cx, span, arg, &range, ty_sugg); } } @@ -146,9 +151,8 @@ fn check_is_ascii( CharRange::HexDigit => "is_ascii_hexdigit", CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return, }; - let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), "_", &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); @@ -182,7 +186,7 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { CharRange::Otherwise } }, - PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + PatKind::Range(Some(start), Some(end), RangeEnd::Included) => check_range(start, end), _ => CharRange::Otherwise, } } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 98e8b1f5cf92c..7fb88763e640f 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -123,8 +123,8 @@ fn check_iter( ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id) - || cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id)) + && let Some(copied_name) = cx.tcx.get_diagnostic_name(copied_def_id) + && matches!(copied_name, sym::iter_copied | sym::iter_cloned) && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) @@ -243,9 +243,9 @@ fn make_sugg( } fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { - ACCEPTABLE_METHODS - .iter() - .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id)) + cx.tcx + .get_diagnostic_name(collect_def_id) + .is_some_and(|collect_name| ACCEPTABLE_METHODS.contains(&collect_name)) } fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 6bf43a1c6d47c..07cce4046ca4f 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -75,12 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { && let ExprKind::Path(target_path) = &target_arg.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) { - let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) { - StripKind::Prefix - } else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) { - StripKind::Suffix - } else { - return; + let strip_kind = match cx.tcx.get_diagnostic_name(method_def_id) { + Some(sym::str_starts_with) => StripKind::Prefix, + Some(sym::str_ends_with) => StripKind::Suffix, + _ => return, }; let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index af6a1b07a4929..39e5289c62ae4 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -116,8 +116,10 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// The expression inside a closure may or may not have surrounding braces and /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces -/// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { +/// and semi-colons until is suitable for including in the suggestion template. +/// The `bool` is `true` when the resulting `span` needs to be enclosed in an +/// `unsafe` block. +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<(Span, bool)> { if !is_unit_expression(cx, expr) { return None; } @@ -125,22 +127,24 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option< match expr.kind { hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => { // Calls can't be reduced any more - Some(expr.span) + Some((expr.span, false)) }, hir::ExprKind::Block(block, _) => { + let is_unsafe = matches!(block.rules, hir::BlockCheckMode::UnsafeBlock(_)); match (block.stmts, block.expr.as_ref()) { ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) + .map(|(span, inner_is_unsafe)| (span, inner_is_unsafe || is_unsafe)) }, ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Let(local) => Some(local.span), - hir::StmtKind::Expr(e) => Some(e.span), - hir::StmtKind::Semi(..) => Some(inner_stmt.span), + hir::StmtKind::Let(local) => Some((local.span, is_unsafe)), + hir::StmtKind::Expr(e) => Some((e.span, is_unsafe)), + hir::StmtKind::Semi(..) => Some((inner_stmt.span, is_unsafe)), hir::StmtKind::Item(..) => None, } }, @@ -228,10 +232,11 @@ fn lint_map_unit_fn( let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, msg, |diag| { - if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) { let mut applicability = Applicability::MachineApplicable; + let (prefix_is_unsafe, suffix_is_unsafe) = if is_unsafe { ("unsafe { ", " }") } else { ("", "") }; let suggestion = format!( - "if let {0}({1}) = {2} {{ {3} }}", + "if let {0}({1}) = {2} {{ {prefix_is_unsafe}{3}{suffix_is_unsafe} }}", variant, snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 5b50efad3e44e..aaf559fc4439e 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, + SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, + peel_ref_operators, }; +use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; -pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) { +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv); + check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv); } } } @@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>( pat: &'tcx Pat<'_>, body: &'tcx Expr<'_>, else_expr: Option<&'tcx Expr<'_>>, + let_expr: &'tcx Expr<'_>, msrv: Msrv, ) { - check_arm(cx, false, pat, body, None, else_expr, msrv); + check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } +#[allow(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, + outer_cond: &'tcx Expr<'tcx>, outer_then_body: &'tcx Expr<'tcx>, outer_guard: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, @@ -82,6 +87,9 @@ fn check_arm<'tcx>( }, IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), } + // Check if the inner expression contains any borrows/dereferences + && let ref_types = get_ref_operators(cx, inner_scrutinee) + && let Some(method) = build_ref_method_chain(ref_types) { let msg = format!( "this `{}` can be collapsed into the outer `{}`", @@ -103,6 +111,10 @@ fn check_arm<'tcx>( let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); + if !method.is_empty() { + let outer_cond_msg = format!("use: `{}{}`", snippet(cx, outer_cond.span, ".."), method); + help_span.push_span_label(outer_cond.span, outer_cond_msg); + } diag.span_help( help_span, "the outer pattern can be modified to include the inner pattern", @@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi }); (span, is_innermost_parent_pat_struct) } + +/// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, +/// `.copied()`) based on reference operators +fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option { + let mut req_method_calls = String::new(); + + for ref_operator in expr { + match ref_operator.kind { + ExprKind::AddrOf(BorrowKind::Raw, _, _) => { + return None; + }, + ExprKind::AddrOf(_, m, _) if m.is_mut() => { + req_method_calls.push_str(".as_mut()"); + }, + ExprKind::AddrOf(_, _, _) => { + req_method_calls.push_str(".as_ref()"); + }, + // Deref operator is the only operator that this function should have received + ExprKind::Unary(_, _) => { + req_method_calls.push_str(".copied()"); + }, + _ => (), + } + } + + Some(req_method_calls) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index c128fc40b7334..6f49c5524118d 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1073,7 +1073,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source); } - collapsible_match::check_match(cx, arms, self.msrv); + collapsible_match::check_match(cx, ex, arms, self.msrv); if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); @@ -1137,7 +1137,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { - collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, self.msrv); + collapsible_match::check_if_let( + cx, + if_let.let_pat, + if_let.if_then, + if_let.if_else, + if_let.let_expr, + self.msrv, + ); significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 027dd7ce0534a..81fecc87256c4 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -226,11 +226,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] enum SigDropHolder { /// No values with significant drop present in this expression. /// /// Expressions that we've emitted lints do not count. + #[default] None, /// Some field in this expression references to values with significant drop. /// @@ -244,12 +245,6 @@ enum SigDropHolder { Moved, } -impl Default for SigDropHolder { - fn default() -> Self { - Self::None - } -} - struct SigDropHelper<'a, 'tcx> { cx: &'a LateContext<'tcx>, parent_expr: Option<&'tcx Expr<'tcx>>, diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index ff7769af1df41..af90cb5e67334 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -28,25 +28,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; + let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { + ("Err(", ")", ty) } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; + ("Poll::Ready(Err(", "))", ty) } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; + ("Poll::Ready(Some(Err(", ")))", ty) } else { return; - } + }; span_lint_and_then( cx, @@ -88,8 +78,8 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Result) + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, def.did()) { Some(subst.type_at(1)) } else { @@ -101,11 +91,9 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) - && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()) { - Some(ready_subst.type_at(1)) + let ready_ty = subst.type_at(0); + result_error_type(cx, ready_ty) } else { None } @@ -116,13 +104,9 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()) - && let some_ty = ready_subst.type_at(0) - && let ty::Adt(some_def, some_subst) = some_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, some_def.did()) + && let Some(some_ty) = option_arg_ty(cx, ready_ty) { - Some(some_subst.type_at(1)) + result_error_type(cx, some_ty) } else { None } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 28efd2038b387..e39916f733d59 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -215,7 +215,8 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let repl_name = cx.tcx.get_diagnostic_name(repl_def_id); + if repl_name == Some(sym::mem_uninitialized) { let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( @@ -230,9 +231,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' ), applicability, ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) - && !cx.typeck_results().expr_ty(src).is_primitive() - { + } else if repl_name == Some(sym::mem_zeroed) && !cx.typeck_results().expr_ty(src).is_primitive() { span_lint_and_help( cx, MEM_REPLACE_WITH_UNINIT, diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 96e2de0dc1cbd..65583c6a9811e 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -24,9 +24,10 @@ pub(super) fn check( && let Some(name) = cx.tcx.get_diagnostic_name(adt.did()) { let caller_type = match name { - sym::Rc => "Rc", - sym::Arc => "Arc", - sym::RcWeak | sym::ArcWeak => "Weak", + sym::Rc => "std::rc::Rc", + sym::Arc => "std::sync::Arc", + sym::RcWeak => "std::rc::Weak", + sym::ArcWeak => "std::sync::Weak", _ => return, }; span_lint_and_then( diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 5b8457bdd1640..2da0f8341b17b 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -233,18 +233,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { - Some(Self::IsSome { + match (cx.tcx.get_diagnostic_name(recv_ty.did()), path.ident.name) { + (Some(sym::Option), sym::is_some) => Some(Self::IsSome { receiver, side_effect_expr_span, - }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { - Some(Self::IsOk { + }), + (Some(sym::Result), sym::is_ok) => Some(Self::IsOk { receiver, side_effect_expr_span, - }) - } else { - None + }), + _ => None, } } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index 9a62b719a8fb8..fa8f9d640ee6e 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -24,32 +24,29 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else { return None; }; - let did = adt.did(); - if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) { - // For array::IntoIter, the length is the second generic - // parameter. - substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) - } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did) - && let ExprKind::MethodCall(_, recv, ..) = iter.kind - { - if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { - // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) - len.try_to_target_usize(cx.tcx).map(u128::from) - } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { - match args { - VecArgs::Vec(vec) => vec.len().try_into().ok(), - VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::ArrayIntoIter) => { + // For array::IntoIter, the length is the second generic + // parameter. + substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) + }, + Some(sym::SliceIter) if let ExprKind::MethodCall(_, recv, ..) = iter.kind => { + if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { + // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) + len.try_to_target_usize(cx.tcx).map(u128::from) + } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + match args { + VecArgs::Vec(vec) => vec.len().try_into().ok(), + VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + } + } else { + None } - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) { - Some(0) - } else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) { - Some(1) - } else { - None + }, + Some(sym::IterEmpty) => Some(0), + Some(sym::IterOnce) => Some(1), + _ => None, } } diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index f7bb8c1d696de..750f933330a25 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -50,10 +50,10 @@ fn try_get_caller_ty_name_and_method_name( } } else { if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { - return Some(("Option", "and_then")); - } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { - return Some(("Result", "and_then")); + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => return Some(("Option", "and_then")), + Some(sym::Result) => return Some(("Result", "and_then")), + _ => {}, } } None diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 98def66ca1499..a98cfff8bfbd1 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,14 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; -use rustc_ast::BindingMode; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Node, PatKind}; -use rustc_lint::LateContext; +use rustc_hir::{self as hir, ExprKind, Node, PatKind}; +use rustc_lint::{LateContext, LintContext}; use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; +const MSG: &str = "unnecessary map of the identity function"; + pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -23,26 +25,70 @@ pub(super) fn check( || is_type_diagnostic_item(cx, caller_ty, sym::Result) || is_type_diagnostic_item(cx, caller_ty, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) - && let Some(sugg_span) = expr.span.trim_start(caller.span) + && let Some(call_span) = expr.span.trim_start(caller.span) { - // If the result of `.map(identity)` is used as a mutable reference, - // the caller must not be an immutable binding. - if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() - && let Some(hir_id) = path_to_local(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) - { - return; - } + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_sugg( - cx, - MAP_IDENTITY, - sugg_span, - "unnecessary map of the identity function", - format!("remove the call to `{name}`"), - String::new(), - Applicability::MachineApplicable, - ); + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.multipart_suggestion( + format!("remove the call to `{name}`, and make `{ident}` mutable"), + suggs, + app, + ); + }); + } else { + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; + + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); + + let note = if let Some(method_requiring_mut) = method_requiring_mut { + format!("this must be made mutable to use `{method_requiring_mut}`") + } else { + "this must be made mutable".to_string() + }; + diag.span_note(caller.span, note); + }); + } + } else { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + main_sugg.0, + MSG, + format!("remove the call to `{name}`"), + main_sugg.1, + app, + ); + } } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 63ee922acfa0d..906ead16fd0d1 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -38,17 +38,13 @@ pub(super) fn check( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => { - cx.qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .is_some_and(|fun_def_id| { - cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) - }) - }, + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .and_then(|fun_def_id| cx.tcx.get_diagnostic_name(fun_def_id)) + .is_some_and(|fun_name| { + matches!(fun_name, sym::deref_method | sym::deref_mut_method) || deref_aliases.contains(&fun_name) + }), hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir_body(body); let closure_expr = peel_blocks(closure_body.value); @@ -63,13 +59,11 @@ pub(super) fn check( .map(|x| &x.kind) .collect::>() && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() + && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - cx.tcx.is_diagnostic_item(sym::deref_method, method_did) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) + matches!(method_name, sym::deref_method | sym::deref_mut_method) + || deref_aliases.contains(&method_name) } else { false } diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 3e64e15dc8602..1a760ea733d7f 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -62,7 +62,7 @@ fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: Lang if let ExprKind::Call(some_expr, [arg]) = expr.kind && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { - Some(arg.span) + Some(arg.span.source_callsite()) } else { None } diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 407f2e80aff25..6738bbf0a12b9 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -31,8 +31,8 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { } pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { - if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did()) + let recv_ty = cx.typeck_results().expr_ty(recv); + if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ccdf5529d537a..ef3d7acdc01ef 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -5,10 +5,10 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) { - single_char_push_string::check(cx, expr, receiver, args); - } else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) { - single_char_insert_string::check(cx, expr, receiver, args); + match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), + Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), + _ => {}, } } } diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index ce7aefed01f49..ffc237e3c24cc 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -14,8 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_diag_trait_item(cx, method_def_id, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind() - && cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) + && is_type_diagnostic_item(cx, input_type, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 413881d5ec99e..b87d81b710269 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -22,7 +22,8 @@ pub(super) fn check<'tcx>( let typeck_results = cx.typeck_results(); let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results); if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) + && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index af466fe091c21..af4ade3cc0f7d 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::AdtDef; use rustc_span::{Span, sym}; use crate::loops::UNUSED_ENUMERATE_INDEX; @@ -39,9 +39,8 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; /// * `closure_arg`: The argument to the map function call containing the closure/function to apply pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - // If we call a method on a `std::iter::Enumerate` instance - && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid) + // If we call a method on a `std::iter::Enumerate` instance + if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) // If we are calling a method of the `Iterator` trait && is_trait_method(cx, call_expr, sym::Iterator) // And the map argument is a closure diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index d02952eb48702..28555a6109001 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; @@ -64,14 +64,20 @@ declare_clippy_lint! { "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)" } -fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) { +fn check_missing_inline_attrs( + cx: &LateContext<'_>, + attrs: &[Attribute], + sp: Span, + desc: &'static str, + hir_id: Option, +) { if !find_attr!(attrs, AttributeKind::Inline(..)) { - span_lint( - cx, - MISSING_INLINE_IN_PUBLIC_ITEMS, - sp, - format!("missing `#[inline]` for {desc}"), - ); + let msg = format!("missing `#[inline]` for {desc}"); + if let Some(hir_id) = hir_id { + span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); + } else { + span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); + } } } @@ -103,17 +109,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a function"; let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc); + check_missing_inline_attrs(cx, attrs, it.span, desc, None); }, - hir::ItemKind::Trait( - ref _constness, - ref _is_auto, - ref _unsafe, - _ident, - _generics, - _bounds, - trait_items, - ) => { + hir::ItemKind::Trait(.., trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for &tit in trait_items { @@ -127,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a default trait method"; let item = cx.tcx.hir_trait_item(tit); let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc); + check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); } }, } @@ -182,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc); + check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 31f51b4575405..ec93ef97cfaf7 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -1,4 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; @@ -83,13 +85,18 @@ fn check_arguments<'tcx>( let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - span_lint( + let mut applicability = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); + span_lint_and_sugg( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), + "remove this `mut`", + sugg.to_string(), + applicability, ); } } diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 6ae26156bc44c..854e927aa2f78 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -2,16 +2,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym, + SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call, + peel_blocks, peel_blocks_with_stmt, span_contains_comment, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does @@ -50,31 +48,6 @@ declare_clippy_lint! { "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for expressions of the form `x == true`, - /// `x != true` and order comparisons such as `x < true` (or vice versa) and - /// suggest using the variable directly. - /// - /// ### Why is this bad? - /// Unnecessary code. - /// - /// ### Example - /// ```rust,ignore - /// if x == true {} - /// if y == false {} - /// ``` - /// use `x` directly: - /// ```rust,ignore - /// if x {} - /// if !y {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub BOOL_COMPARISON, - complexity, - "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" -} - declare_clippy_lint! { /// ### What it does /// Checks for expressions of the form `if c { x = true } else { x = false }` @@ -224,201 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } } -declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); - -impl<'tcx> LateLintPass<'tcx> for BoolComparison { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind { - let ignore_case = None::<(fn(_) -> _, &str)>; - let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; - match node { - BinOpKind::Eq => { - let true_case = Some((|h| h, "equality checks against true are unnecessary")); - let false_case = Some(( - |h: Sugg<'tcx>| !h, - "equality checks against false can be replaced by a negation", - )); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Ne => { - let true_case = Some(( - |h: Sugg<'tcx>| !h, - "inequality checks against true can be replaced by a negation", - )); - let false_case = Some((|h| h, "inequality checks against false are unnecessary")); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Lt => check_comparison( - cx, - e, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), - "order comparisons between booleans can be simplified", - )), - ), - BinOpKind::Gt => check_comparison( - cx, - e, - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), - "order comparisons between booleans can be simplified", - )), - ), - _ => (), - } - } - } -} - -struct ExpressionInfoWithSpan { - one_side_is_unary_not: bool, - left_span: Span, - right_span: Span, -} - -fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { - if let ExprKind::Unary(UnOp::Not, operand) = e.kind { - return (true, operand.span); - } - (false, e.span) -} - -fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan { - let left = is_unary_not(left_side); - let right = is_unary_not(right_side); - - ExpressionInfoWithSpan { - one_side_is_unary_not: left.0 != right.0, - left_span: left.1, - right_span: right.1, - } -} - -fn check_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, -) { - if let ExprKind::Binary(op, left_side, right_side) = e.kind { - let (l_ty, r_ty) = ( - cx.typeck_results().expr_ty(left_side), - cx.typeck_results().expr_ty(right_side), - ); - if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { - return; - } - if l_ty.is_bool() && r_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; - // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, - // calling `source_callsite` make sure macros are handled correctly, see issue #9907 - let binop_span = left_side - .span - .source_callsite() - .with_hi(right_side.span.source_callsite().hi()); - - if op.node == BinOpKind::Eq { - let expression_info = one_side_is_unary_not(left_side, right_side); - if expression_info.one_side_is_unary_not { - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - "this comparison might be written more concisely", - "try simplifying it as shown", - format!( - "{} != {}", - snippet_with_applicability( - cx, - expression_info.left_span.source_callsite(), - "..", - &mut applicability - ), - snippet_with_applicability( - cx, - expression_info.right_span.source_callsite(), - "..", - &mut applicability - ) - ), - applicability, - ); - } - } - - match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { - (Some(true), None) => left_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(true)) => right_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (Some(false), None) => left_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(false)) => right_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (None, None) => no_literal.map_or((), |(h, m)| { - let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); - let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - m, - "try simplifying it as shown", - h(left_side, right_side).into_string(), - applicability, - ); - }), - _ => (), - } - } - } -} - -fn suggest_bool_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - span: Span, - expr: &Expr<'_>, - mut app: Applicability, - message: &'static str, - conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, -) { - let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - span, - message, - "try simplifying it as shown", - conv_hint(hint).into_string(), - app, - ); -} - enum Expression { Bool(bool), RetBool(bool), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index a67545e419ce7..3a6ccc2bca998 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind + && let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind && let body = cx.tcx.hir_body(body) // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}` // and suggesting `for … in … { unsafe { } }` is a little ugly. && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..)) { + let mut applicability = Applicability::MachineApplicable; + + // If any closure parameter has an explicit type specified, applying the lint would necessarily + // remove that specification, possibly breaking type inference + if fn_decl + .inputs + .iter() + .any(|input| matches!(input.kind, TyKind::Infer(..))) + { + applicability = Applicability::MaybeIncorrect; + } + let mut ret_collector = RetCollector::default(); ret_collector.visit_expr(body.value); @@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { return; } - let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { - (Applicability::MachineApplicable, None) + let ret_suggs = if ret_collector.spans.is_empty() { + None } else { - ( - Applicability::MaybeIncorrect, - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ), + applicability = Applicability::MaybeIncorrect; + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), ) }; diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 04b092769666c..ba67dc62abbda 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -134,7 +134,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { return; } - if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) + let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); + if trait_name == Some(sym::Clone) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) { @@ -170,12 +171,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { String::new(), Applicability::MaybeIncorrect, ); - - return; } - } - - if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) + } else if trait_name == Some(sym::PartialOrd) && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 23a1622f30fff..cb934466bd890 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -43,13 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name == sym::mode - && matches!( - cx.tcx.get_diagnostic_name(adt.did()), - Some(sym::FsOpenOptions | sym::DirBuilder) - )) - || (path.ident.name == sym::set_mode - && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) + && matches!( + (cx.tcx.get_diagnostic_name(adt.did()), path.ident.name), + (Some(sym::FsOpenOptions | sym::DirBuilder), sym::mode) + | (Some(sym::FsPermissions), sym::set_mode) + ) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) && param diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 7317c62df7fe8..2d303e40bd1c8 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::qualify_min_const_fn::is_stable_const_fn; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, assignee: &'tcx hir::Expr<'_>, e: &'tcx hir::Expr<'_>, - _msrv: Msrv, + msrv: Msrv, ) { if let hir::ExprKind::Binary(op, l, r) = &e.kind { let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { @@ -43,10 +44,28 @@ pub(super) fn check<'tcx>( } } - // Skip if the trait is not stable in const contexts - // FIXME: reintroduce a better check after this is merged back into Clippy + // Skip if the trait or the implementation is not stable in const contexts if is_in_const_context(cx) { - return; + if cx + .tcx + .associated_item_def_ids(trait_id) + .first() + .is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv)) + { + return; + } + + let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::>(); + if impls.is_empty() + || impls.into_iter().any(|impl_id| { + cx.tcx + .associated_item_def_ids(impl_id) + .first() + .is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv)) + }) + { + return; + } } span_lint_and_then( diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 22ec4fe60fb02..604f8f5da0b86 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -47,14 +47,10 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| { - if cx.tcx.is_diagnostic_item(sym::from_str_method, did) { - true - } else if cx.tcx.is_diagnostic_item(sym::from_fn, did) { - !is_copy(cx, typeck.expr_ty(expr)) - } else { - false - } + if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, }) => { (arg, arg.span) diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs index 047a5a0159cb0..17fa8017c9788 100644 --- a/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/clippy_lints/src/operators/float_equality_without_abs.rs @@ -34,10 +34,11 @@ pub(crate) fn check<'tcx>( val_r, ) = lhs.kind - // right hand side matches either f32::EPSILON or f64::EPSILON + // right hand side matches _::EPSILON && let ExprKind::Path(ref epsilon_path) = rhs.kind && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) - && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id))) + && let Some(sym) = cx.tcx.get_diagnostic_name(def_id) + && matches!(sym, sym::f16_epsilon | sym::f32_epsilon | sym::f64_epsilon | sym::f128_epsilon) // values of the subtractions on the left hand side are of the type float && let t_val_l = cx.typeck_results().expr_ty(val_l) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 449d3da763941..43db0085f2e34 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::{is_in_test, is_inside_always_const_context}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) { if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { && let Res::Def(DefKind::Fn, def_id) = expr_path.res && cx.tcx.is_diagnostic_item(sym::panic_any, def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index e57b8cc2d84e3..1d58cdd26d88d 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -96,14 +96,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let (fn_def_id, arg, arg_ty, clone_ret) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); + let fn_name = cx.tcx.get_diagnostic_name(fn_def_id); + let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) - || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) - || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id) - && is_type_lang_item(cx, arg_ty, LangItem::String)); + || fn_name == Some(sym::to_owned_method) + || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); - let from_deref = !from_borrow - && (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id) - || cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id)); + let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); if !from_borrow && !from_deref { continue; @@ -148,8 +147,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { is_call_with_ref_arg(cx, mir, &pred_terminator.kind) && res == cloned && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id) - && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) - || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString)) + && let ty::Adt(pred_arg_def, _) = pred_arg_ty.kind() + && let Some(pred_arg_name) = cx.tcx.get_diagnostic_name(pred_arg_def.did()) + && matches!(pred_arg_name, sym::PathBuf | sym::OsString) { (pred_arg, res) } else { diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index db91c57b18160..1dea8f17c34be 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -155,6 +155,11 @@ impl LateLintPass<'_> for SemicolonBlock { kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() => { + let attrs = cx.tcx.hir_attrs(stmt.hir_id); + if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() { + return; + } + if let Some(tail) = block.expr { self.semicolon_inside_block(cx, block, tail, stmt.span); } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index e9534bc63a691..8c4a50041e67d 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -126,6 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && !is_from_proc_macro(cx, &first_segment.ident) && !matches!(def_kind, DefKind::Macro(_)) && let Some(last_segment) = path.segments.last() + && let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res + && crate_def_id.is_crate_root() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 490e6c974ae10..57d5900b045ea 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -457,7 +457,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { } fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) + matches!( + cx.tcx.get_diagnostic_name(trim_def_id), + Some(sym::str_trim | sym::str_trim_start | sym::str_trim_end) + ) } diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs index 08f36a2ed5d2e..543f3c45e1461 100644 --- a/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,8 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use std::borrow::Cow; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{HasSession, SpanRangeExt as _}; use rustc_errors::Applicability; -use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind}; +use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS; @@ -38,6 +42,7 @@ fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, path: &Path<'tcx>, + arg: &Expr<'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, expr_hir_id: HirId, @@ -68,14 +73,48 @@ pub(super) fn check<'tcx>( } else if is_function_block(cx, expr_hir_id) { return false; } - span_lint_and_sugg( + let span = last.ident.span.with_hi(path.span.hi()); + span_lint_and_then( cx, MISSING_TRANSMUTE_ANNOTATIONS, - last.ident.span.with_hi(path.span.hi()), + span, "transmute used without annotations", - "consider adding missing annotations", - format!("{}::<{from_ty}, {to_ty}>", last.ident), - Applicability::MaybeIncorrect, + |diag| { + let from_ty_no_name = ty_cannot_be_named(from_ty); + let to_ty_no_name = ty_cannot_be_named(to_ty); + if from_ty_no_name || to_ty_no_name { + let to_name = match (from_ty_no_name, to_ty_no_name) { + (true, false) => maybe_name_by_expr(cx, arg.span, "the origin type"), + (false, true) => "the destination type".into(), + _ => "the source and destination types".into(), + }; + diag.help(format!( + "consider giving {to_name} a name, and adding missing type annotations" + )); + } else { + diag.span_suggestion( + span, + "consider adding missing annotations", + format!("{}::<{from_ty}, {to_ty}>", last.ident), + Applicability::MaybeIncorrect, + ); + } + }, ); true } + +fn ty_cannot_be_named(ty: Ty<'_>) -> bool { + matches!( + ty.kind(), + ty::Alias(ty::AliasTyKind::Opaque | ty::AliasTyKind::Inherent, _) + ) +} + +fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(sess, |name| { + (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) + }) + .flatten() + .unwrap_or(default.into()) +} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 1c7bb4314ddf5..5fda388259a61 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) - | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) + | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index f27aaa2fa77aa..2257aa1b73c87 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_NON_ZERO; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -16,35 +16,24 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - let tcx = cx.tcx; - - let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else { - return false; - }; - - if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { - return false; + if let ty::Int(_) | ty::Uint(_) = from_ty.kind() + && let ty::Adt(adt, substs) = to_ty.kind() + && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && let int_ty = substs.type_at(0) + && from_ty == int_ty + { + let arg = Sugg::hir(cx, arg, ".."); + span_lint_and_sugg( + cx, + TRANSMUTE_INT_TO_NON_ZERO, + e.span, + format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), + "consider using", + format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), + Applicability::Unspecified, + ); + true + } else { + false } - - let int_ty = substs.type_at(0); - if from_ty != int_ty { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_NON_ZERO, - e.span, - format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using", - format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), - Applicability::Unspecified, - ); - }, - ); - true } diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index d691f1878b11b..c4fd0fbf87a90 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -11,7 +11,8 @@ use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { let app = Applicability::Unspecified; - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + let name = cx.tcx.get_diagnostic_name(def_id); + if name == Some(sym::Rc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( @@ -56,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ ); return true; } - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + } else if name == Some(sym::Arc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index de3456a8ba5fc..0ba51daf027d1 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -13,14 +13,11 @@ use super::{REDUNDANT_ALLOCATION, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; - let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { - "Box" - } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - "Rc" - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { - "Arc" - } else { - return false; + let outer_sym = match cx.tcx.get_diagnostic_name(def_id) { + _ if Some(def_id) == cx.tcx.lang_items().owned_box() => "Box", + Some(sym::Rc) => "Rc", + Some(sym::Arc) => "Arc", + _ => return false, }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 48b532968cb57..38716519e23b6 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use clippy_utils::sym; +use clippy_utils::{is_unit_expr, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,10 +16,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; - let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return; }; - if !cx.typeck_results().expr_ty(left).is_unit() { + if is_unit_expr(lhs) || is_unit_expr(rhs) { + return; + } + if !cx.typeck_results().expr_ty(lhs).is_unit() { return; } span_lint( diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 7d996775a58c8..28f4884fa3110 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() && inner_str.is_str() { - if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) { + let fun_name = cx.tcx.get_diagnostic_name(fun_def_id); + if fun_name == Some(sym::string_new) { span_lint_and_sugg( cx, UNNECESSARY_OWNED_EMPTY_STRINGS, @@ -51,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { "\"\"".to_owned(), Applicability::MachineApplicable, ); - } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) + } else if fun_name == Some(sym::from_fn) && let [arg] = args && let ExprKind::Lit(spanned) = &arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 54a7efc090ade..849c0b438a5a0 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -107,12 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Get the wrapper and inner types, if can't, abort. let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) { - ("Option", OptionSome, subst.type_at(0)) - } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) { - ("Result", ResultOk, subst.type_at(0)) - } else { - return; + match cx.tcx.get_diagnostic_name(adt_def.did()) { + Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)), + Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)), + _ => return, } } else { return; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 8b278d98a30e0..f3410c98973f5 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -326,7 +326,11 @@ fn extend_with_struct_pat( if idx_1 == idx { // In the case of `k`, we merely require identical field names // so that we will transform into `ident_k: p1_k | p2_k`. - let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + let pos = fps2.iter().position(|fp2| { + // Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }` + !(fp1.is_shorthand && fp2.is_shorthand) + && eq_id(fp1.ident, fp2.ident) + }); pos_in_2.set(pos); pos.is_some() } else { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index f3cd3f1bb286f..af3ad4566c46a 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -89,7 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { { // We don't want to lint inside io::Read or io::Write implementations, as the author has more // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 - if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) { + if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id) + && matches!(trait_name, sym::IoRead | sym::IoWrite) + { return; } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 3811f0fe6b558..d503bd3379b08 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -6,13 +6,13 @@ use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, - Ty, TyKind, + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, PolyTraitRef, Term, Ty, + TyKind, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Pos as _, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -49,19 +49,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { decl: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, span: Span, - def_id: LocalDefId, + _def_id: LocalDefId, ) { if let FnRetTy::Return(hir_ty) = decl.output && is_unit_ty(hir_ty) && !hir_ty.span.from_expansion() && get_def(span) == get_def(hir_ty.span) { - // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure = kind - && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) - && let ExprKind::Closure(closure) = expr.kind - && !closure.bound_generic_params.is_empty() - { + // The explicit `-> ()` in the closure signature might be necessary for multiple reasons: + // - Implicit types in closure signatures are forbidden when `for<...>` is present + // - If the closure body ends with a function call, and that function's return type is generic, the + // `-> ()` could be required for it to be inferred + // + // There could be more reasons to have it, and, in general, we shouldn't discourage the users from + // writing more type annotations than strictly necessary, because it can help readability and + // maintainability + if let FnKind::Closure = kind { return; } @@ -97,16 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce) - && let Some(args) = segments[0].args + if let [segment] = &poly.trait_ref.path.segments + && matches!(segment.ident.name, sym::Fn | sym::FnMut | sym::FnOnce) + && let Some(args) = segment.args && args.parenthesized == GenericArgsParentheses::ParenSugar - && let constraints = &args.constraints - && constraints.len() == 1 - && constraints[0].ident.name == sym::Output - && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && let [constraint] = &args.constraints + && constraint.ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraint.kind && args.span_ext.hi() != poly.span.hi() && !hir_ty.span.from_expansion() && args.span_ext.hi() != hir_ty.span.hi() @@ -160,17 +160,15 @@ fn get_def(span: Span) -> Option { fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty_span.hi()) - .get_source_text(cx) - .map_or((ty_span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { - ( - #[expect(clippy::cast_possible_truncation)] - ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - }); + if let Some(Some(rpos)) = span.with_hi(ty_span.hi()).with_source_text(cx, position_before_rarrow) { + ( + ty_span.with_lo(span.lo() + BytePos::from_usize(rpos)), + Applicability::MachineApplicable, + ) + } else { + (ty_span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( cx, UNUSED_UNIT, diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 7bec212a23ca0..f26647fa34854 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{method_chain_args, return_ty}; -use core::ops::ControlFlow; -use rustc_hir as hir; -use rustc_hir::ImplItemKind; +use clippy_utils::{return_ty, sym}; +use rustc_hir::{ + Body, BodyOwnerKind, Expr, ExprKind, FnSig, ImplItem, ImplItemKind, Item, ItemKind, OwnerId, PathSegment, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Ident, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -57,62 +56,165 @@ declare_clippy_lint! { "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" } -declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); +impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); -impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind - // first check if it's a method or function - // checking if its return type is `result` or `option` - && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option)) - { - lint_impl_body(cx, impl_item.span, impl_item); +#[derive(Clone, Copy, Eq, PartialEq)] +enum OptionOrResult { + Option, + Result, +} + +impl OptionOrResult { + fn with_article(self) -> &'static str { + match self { + Self::Option => "an `Option`", + Self::Result => "a `Result`", } } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind { - let body = cx.tcx.hir_body(body_id); - let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); - let mut result = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { - // check for `expect` - if let Some(arglists) = method_chain_args(e, &[sym::expect]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } - - // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } - - ControlFlow::Continue(()) +struct OptionOrResultFn { + kind: OptionOrResult, + return_ty_span: Option, +} + +#[derive(Default)] +pub struct UnwrapInResult { + fn_stack: Vec>, + current_fn: Option, +} + +impl UnwrapInResult { + fn enter_item(&mut self, cx: &LateContext<'_>, fn_def_id: OwnerId, sig: &FnSig<'_>) { + self.fn_stack.push(self.current_fn.take()); + self.current_fn = is_option_or_result(cx, return_ty(cx, fn_def_id)).map(|kind| OptionOrResultFn { + kind, + return_ty_span: Some(sig.decl.output.span()), }); + } - // if we've found one, lint - if !result.is_empty() { + fn leave_item(&mut self) { + self.current_fn = self.fn_stack.pop().unwrap(); + } +} + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(sig, _) = &impl_item.kind { + self.enter_item(cx, impl_item.owner_id, sig); + } + } + + fn check_impl_item_post(&mut self, _: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { + if let ImplItemKind::Fn(..) = impl_item.kind { + self.leave_item(); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { + has_body: true, sig, .. + } = &item.kind + { + self.enter_item(cx, item.owner_id, sig); + } + } + + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { has_body: true, .. } = item.kind { + self.leave_item(); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let Some(OptionOrResultFn { + kind, + ref mut return_ty_span, + }) = self.current_fn + && let Some((oor, fn_name)) = is_unwrap_or_expect_call(cx, expr) + && oor == kind + { span_lint_and_then( cx, UNWRAP_IN_RESULT, - impl_span, - "used unwrap or expect in a function that returns result or option", - move |diag| { - diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(result, "potential non-recoverable error(s)"); + expr.span, + format!("`{fn_name}` used in a function that returns {}", kind.with_article()), + |diag| { + // Issue the note and help only once per function + if let Some(span) = return_ty_span.take() { + diag.span_note(span, "in this function signature"); + let complement = if kind == OptionOrResult::Result { + " or calling the `.map_err()` method" + } else { + "" + }; + diag.help(format!("consider using the `?` operator{complement}")); + } }, ); } } + + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // When entering a body which is not a function, mask the potential surrounding + // function to not apply the lint. + self.fn_stack.push(self.current_fn.take()); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // Unmask the potential surrounding function. + self.current_fn = self.fn_stack.pop().unwrap(); + } + } +} + +fn is_option_or_result(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + match ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did())) { + Some(sym::Option) => Some(OptionOrResult::Option), + Some(sym::Result) => Some(OptionOrResult::Result), + _ => None, + } +} + +fn is_unwrap_or_expect_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(OptionOrResult, Symbol)> { + if let ExprKind::Call(func, _) = expr.kind + && let ExprKind::Path(QPath::TypeRelative( + hir_ty, + PathSegment { + ident: + Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + )) = func.kind + { + is_option_or_result(cx, cx.typeck_results().node_type(hir_ty.hir_id)).map(|oor| (oor, *name)) + } else if let ExprKind::MethodCall( + PathSegment { + ident: Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + recv, + _, + _, + ) = expr.kind + { + is_option_or_result(cx, cx.typeck_results().expr_ty_adjusted(recv)).map(|oor| (oor, *name)) + } else { + None + } } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index e5b20c0e0a133..70ae982a4458b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -386,12 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ExprKind::Call(path, [arg]) => { if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && !is_ty_alias(qpath) + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); - if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id) + if name == sym::try_from_fn && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() @@ -406,9 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } - - if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_and_consts(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 7b6a25123e85e..52b30ddce12b8 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,4 +1,6 @@ use std::collections::BTreeMap; +use std::collections::btree_map::Entry; +use std::mem; use std::ops::ControlFlow; use clippy_config::Conf; @@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, msrv: Msrv, - span_to_lint_map: BTreeMap>, + /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can + /// emit a warning there or not). + /// + /// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a + /// lint while visiting, because a `vec![]` invocation span can appear multiple times when + /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and + /// another time that does. Consider: + /// ``` + /// macro_rules! m { + /// ($v:expr) => { + /// let a = $v; + /// $v.push(3); + /// } + /// } + /// m!(vec![1, 2]); + /// ``` + /// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing + /// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would + /// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to + /// be a vector. + span_to_state: BTreeMap, allow_in_test: bool, } + impl UselessVec { pub fn new(conf: &'static Conf) -> Self { Self { too_large_for_stack: conf.too_large_for_stack, msrv: conf.msrv, - span_to_lint_map: BTreeMap::new(), + span_to_state: BTreeMap::new(), allow_in_test: conf.allow_useless_vec_in_tests, } } @@ -62,17 +85,28 @@ declare_clippy_lint! { impl_lint_pass!(UselessVec => [USELESS_VEC]); -impl<'tcx> LateLintPass<'tcx> for UselessVec { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { - return; - }; - if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { - return; - } - // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` - let callsite = expr.span.parent_callsite().unwrap_or(expr.span); +/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed. +enum VecState { + Change { + suggest_ty: SuggestedType, + vec_snippet: String, + expr_hir_id: HirId, + }, + NoChange, +} + +enum VecToArray { + /// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or + /// slice). + Possible, + /// Expression must be a `Vec<_>`. Type cannot change. + Impossible, +} +impl UselessVec { + /// Checks if the surrounding environment requires this expression to actually be of type + /// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors. + fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) @@ -100,110 +134,126 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .is_continue(); if only_slice_uses { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, // if the local pattern has a specified type, do not lint. Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible }, // search for `for _ in vec![...]` Node::Expr(Expr { span, .. }) if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => { - let suggest_slice = suggest_type(expr); - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible }, // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` _ => { - let suggest_slice = suggest_type(expr); - if adjusts_to_slice(cx, expr) { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, } } +} + +impl<'tcx> LateLintPass<'tcx> for UselessVec { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + // The `vec![]` or `&vec![]` invocation span. + && let vec_span = expr.span.parent_callsite().unwrap_or(expr.span) + && !vec_span.from_expansion() + { + if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { + return; + } + + match self.expr_usage_requires_vec(cx, expr) { + VecToArray::Possible => { + let suggest_ty = suggest_type(expr); + + // Size and `Copy` checks don't depend on the enclosing usage of the expression + // and don't need to be inserted into the state map. + let vec_snippet = match vec_args { + higher::VecArgs::Repeat(expr, len) => { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) + && let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len) + && let Ok(length) = u64::try_from(length) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + Some(expr.span.source_callsite()), + Some(len.span.source_callsite()), + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Ok(length) = u64::try_from(args.len()) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + args.first().zip(args.last()).map(|(first, last)| { + first.span.source_callsite().to(last.span.source_callsite()) + }), + None, + ) + } else { + return; + } + }, + }; + + if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) { + entry.insert(VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id: expr.hir_id, + }); + } + }, + VecToArray::Impossible => { + self.span_to_state.insert(vec_span, VecState::NoChange); + }, + } + } + } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for (span, lint_opt) in &self.span_to_lint_map { - if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc()); - span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - // If the `vec!` macro contains comment, better not make the suggestion machine - // applicable as it would remove them. - let applicability = if *applicability != Applicability::Unspecified - && let source_map = cx.tcx.sess.source_map() - && span_contains_comment(source_map, *span) - { + for (span, state) in mem::take(&mut self.span_to_state) { + if let VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id, + } = state + { + span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| { + let help_msg = format!("you can use {} directly", suggest_ty.desc()); + // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it + // would remove them. + let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) { Applicability::Unspecified } else { - *applicability + Applicability::MachineApplicable }; - diag.span_suggestion(*span, help_msg, snippet, applicability); + diag.span_suggestion(span, help_msg, vec_snippet, applicability); }); } } } } -impl UselessVec { - fn check_vec_macro<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - vec_args: &higher::VecArgs<'tcx>, - span: Span, - hir_id: HirId, - suggest_slice: SuggestedType, - ) { - if span.from_expansion() { - return; - } - - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { - // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also - if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { - return; - } - - #[expect(clippy::cast_possible_truncation)] - if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { - return; - } - - suggest_slice.snippet(cx, Some(elem.span), Some(len.span)) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - let args_span = if let Some(last) = args.iter().last() { - if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { - return; - } - Some(args[0].span.source_callsite().to(last.span.source_callsite())) - } else { - None - }; - suggest_slice.snippet(cx, args_span, None) - }, - }; - - self.span_to_lint_map.entry(span).or_insert(Some(( - hir_id, - suggest_slice, - snippet, - Applicability::MachineApplicable, - ))); - } -} - #[derive(Copy, Clone)] pub(crate) enum SuggestedType { /// Suggest using a slice `&[..]` / `&mut [..]` @@ -221,11 +271,17 @@ impl SuggestedType { } fn snippet(self, cx: &LateContext<'_>, args_span: Option, len_span: Option) -> String { + // Invariant of the lint as implemented: all spans are from the root context (and as a result, + // always trivially crate-local). + assert!(args_span.is_none_or(|s| !s.from_expansion())); + assert!(len_span.is_none_or(|s| !s.from_expansion())); + let maybe_args = args_span - .and_then(|sp| sp.get_source_text(cx)) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) .map_or(String::new(), |x| x.to_owned()); let maybe_len = len_span - .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}"))) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|st| format!("; {st}")) .unwrap_or_default(); match self { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a15e4e42e71df..c55c5ec2f51ae 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -537,7 +537,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); - if let Some((_, index)) = positional_arg_piece_span(piece) { + if let Some((_, index)) = format_arg_piece_span(piece) { replaced_position.push(index); } @@ -569,16 +569,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { } } -/// Extract Span and its index from the given `piece`, if it's positional argument. -fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { +/// Extract Span and its index from the given `piece` +fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { match piece { FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: - FormatArgPosition { - index: Ok(index), - kind: FormatArgPositionKind::Number, - .. - }, + argument: FormatArgPosition { index: Ok(index), .. }, span: Some(span), .. }) => Some((*span, *index)), diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 6ab94a5221092..a934d2094e004 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,5 +1,6 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; @@ -58,8 +59,8 @@ declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind - && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Child, child_adt.did()) + && let child_ty = cx.typeck_results().expr_ty(expr) + && is_type_diagnostic_item(cx, child_ty, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) @@ -177,8 +178,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, Node::Expr(expr) if let Some(fn_did) = fn_def_id(self.cx, expr) - && (self.cx.tcx.is_diagnostic_item(sym::child_id, fn_did) - || self.cx.tcx.is_diagnostic_item(sym::child_kill, fn_did)) => {}, + && let Some(fn_name) = self.cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::child_id | sym::child_kill) => {}, // Conservatively assume that all other kinds of nodes call `.wait()` somehow. _ => return Break(MaybeWait(ex.span)), @@ -351,9 +352,14 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus /// Checks if the given expression exits the process. fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn_def_id(cx, expr).is_some_and(|fn_did| { - cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || cx.tcx.is_diagnostic_item(sym::process_abort, fn_did) - }) + if let Some(fn_did) = fn_def_id(cx, expr) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::process_exit | sym::process_abort) + { + true + } else { + false + } } #[derive(Debug)] diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2dfe28953d0c1..e01f563c49e73 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-08-22 +nightly-2025-09-04 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 40c00568a3bde..ad69e6eb184e1 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -21,7 +21,7 @@ pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { } /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. -pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn unordered_over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 4e0b00df9508d..bda28a663fb07 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -287,23 +287,22 @@ impl<'a> VecArgs<'a> { && let ExprKind::Path(ref qpath) = fun.kind && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) { - return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 { - // `vec![a, b, c]` case - if let ExprKind::Call(_, [arg]) = &args[0].kind - && let ExprKind::Array(args) = arg.kind + return match (name, args) { + (sym::vec_from_elem, [elem, size]) => { + // `vec![elem; size]` case + Some(VecArgs::Repeat(elem, size)) + }, + (sym::slice_into_vec, [slice]) + if let ExprKind::Call(_, [arg]) = slice.kind + && let ExprKind::Array(args) = arg.kind => { + // `vec![a, b, c]` case Some(VecArgs::Vec(args)) - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() { - Some(VecArgs::Vec(&[])) - } else { - None + }, + (sym::vec_new, []) => Some(VecArgs::Vec(&[])), + _ => None, }; } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 8160443f4132f..b79e15cd7170b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -757,7 +757,7 @@ pub fn both_some_and(l: Option, r: Option, mut pred: impl FnMut(X, Y } /// Checks if two slices are equal as per `eq_fn`. -pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 011c9b2f931ae..14b64eb4d54e8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, - CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, + HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, + OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -126,6 +126,7 @@ use rustc_span::{InnerSpan, Span}; use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; +use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; @@ -308,6 +309,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> cx.tcx.lang_items().get(item) == Some(did) } +/// Checks if `expr` is an empty block or an empty tuple. pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -640,9 +642,8 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); + return Some(adt.did()) == cx.tcx.lang_items().string() + || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name)); } false } @@ -1992,7 +1993,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => { - zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir)) }, (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => { zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) @@ -2004,7 +2005,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< if let ExprKind::Path(ident) = &ident.kind && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir)) + && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir)) { true } else { @@ -2017,10 +2018,8 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< // check ident qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && field_pats.iter().all(|field_pat| { - fields.iter().any(|field| { - field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) - }) + && unordered_over(field_pats, fields, |field_pat, field| { + field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) }) }, _ => false, @@ -2405,6 +2404,24 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> expr } +/// Returns a `Vec` of `Expr`s containing `AddrOf` operators (`&`) or deref operators (`*`) of a +/// given expression. +pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> { + let mut operators = Vec::new(); + peel_hir_expr_while(expr, |expr| match expr.kind { + ExprKind::AddrOf(_, _, e) => { + operators.push(expr); + Some(e) + }, + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => { + operators.push(expr); + Some(e) + }, + _ => None, + }); + operators +} + pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind && let Res::Def(_, def_id) = path.res @@ -3633,3 +3650,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) ) }) } + +/// Checks if the expression is an async block (i.e., `async { ... }`). +pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Closure(Closure { + kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block + )), + .. + }) + ) +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index fafc1d07e51eb..8e302f9d2ad12 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -387,10 +387,7 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// Checks if the type is a reference equals to a diagnostic item pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { - ty::Ref(_, ref_ty, _) => match ref_ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), - _ => false, - }, + ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), _ => false, } } @@ -1378,9 +1375,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() - || ty.is_array() - || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) + ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 76d43feee12ea..6eccbcdb12281 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -144,11 +144,9 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { /// Checks if the given expression is a macro call to `todo!()` or `unimplemented!()`. pub fn is_todo_unimplemented_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - root_macro_call_first_node(cx, expr).is_some_and(|macro_call| { - [sym::todo_macro, sym::unimplemented_macro] - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, macro_call.def_id)) - }) + root_macro_call_first_node(cx, expr) + .and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id)) + .is_some_and(|macro_name| matches!(macro_name, sym::todo_macro | sym::unimplemented_macro)) } /// Checks if the given expression is a stub, i.e., a `todo!()` or `unimplemented!()` expression, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5497e77e8ad17..ec2f24a0a6d84 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-08-22" +channel = "nightly-2025-09-04" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index c4076cbaa77b2..6bddcbfd94ce4 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -330,7 +330,8 @@ pub fn main() { // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached // https://github.com/rust-lang/cargo/issues/14385 - let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print"); + let info_query = has_arg(&orig_args, "-vV") + || arg_value(&orig_args, "--print", |val| val != "crate-root-lint-levels").is_some(); let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { diff --git a/tests/config-consistency.rs b/tests/config-consistency.rs new file mode 100644 index 0000000000000..9e7ca26c7d400 --- /dev/null +++ b/tests/config-consistency.rs @@ -0,0 +1,30 @@ +#![feature(rustc_private)] + +// This test checks that all lints defined in `clippy_config::conf` in `#[lints]` +// attributes exist as Clippy lints. +// +// This test is a no-op if run as part of the compiler test suite +// and will always succeed. + +use std::collections::HashSet; + +#[test] +fn config_consistency() { + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + + let lint_names: HashSet = clippy_lints::declared_lints::LINTS + .iter() + .map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase()) + .collect(); + for conf in clippy_config::get_configuration_metadata() { + for lint in conf.lints { + assert!( + lint_names.contains(*lint), + "Configuration option {} references lint `{lint}` which does not exist", + conf.name + ); + } + } +} diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs new file mode 100644 index 0000000000000..2ad9bfb75dee8 --- /dev/null +++ b/tests/no-profile-in-cargo-toml.rs @@ -0,0 +1,34 @@ +// Check that we do not have `profile.*` sections in our `Cargo.toml` files, +// as this causes warnings when run from the compiler repository which includes +// Clippy in a workspace. +// +// Those sections can be put into `.cargo/config.toml` which will be read +// when commands are issued from the top-level Clippy directory, outside of +// a workspace. + +use std::fs::File; +use std::io::{self, BufRead as _}; +use walkdir::WalkDir; + +#[test] +fn no_profile_in_cargo_toml() { + // This check could parse `Cargo.toml` using a TOML deserializer, but in practice + // profile sections would be added at the beginning of a line as `[profile.*]`, so + // keep it fast and simple. + for entry in WalkDir::new(".") + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) + { + for line in io::BufReader::new(File::open(entry.path()).unwrap()) + .lines() + .map(Result::unwrap) + { + if line.starts_with("[profile.") { + eprintln!("Profile section `{line}` found in file `{}`.", entry.path().display()); + eprintln!("Use `.cargo/config.toml` for profiles specific to the standalone Clippy repository."); + panic!("Profile section found in `Cargo.toml`"); + } + } + } +} diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index f03a745963e00..e9d53c64dd9b5 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead note: the lint level is defined here --> tests/ui-internal/disallow_span_lint.rs:2:9 | @@ -17,7 +17,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead error: aborting due to 2 previous errors diff --git a/tests/ui-toml/excessive_precision/clippy.toml b/tests/ui-toml/excessive_precision/clippy.toml new file mode 100644 index 0000000000000..c7fc230dcb31c --- /dev/null +++ b/tests/ui-toml/excessive_precision/clippy.toml @@ -0,0 +1 @@ +const-literal-digits-threshold = 20 diff --git a/tests/ui-toml/excessive_precision/excessive_precision.fixed b/tests/ui-toml/excessive_precision/excessive_precision.fixed new file mode 100644 index 0000000000000..577bbff2957fb --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.fixed @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/tests/ui-toml/excessive_precision/excessive_precision.rs b/tests/ui-toml/excessive_precision/excessive_precision.rs new file mode 100644 index 0000000000000..121448ed540dc --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.rs @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/tests/ui-toml/excessive_precision/excessive_precision.stderr b/tests/ui-toml/excessive_precision/excessive_precision.stderr new file mode 100644 index 0000000000000..65d33eddef172 --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.stderr @@ -0,0 +1,38 @@ +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::excessive-precision` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]` +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ee77ebd8ece2..20aeb4bb8498e 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -129,6 +130,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -223,6 +225,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names diff --git a/tests/ui/assertions_on_result_states.fixed b/tests/ui/assertions_on_result_states.fixed index 09c1a8d0ed1d5..b2b7318c113f7 100644 --- a/tests/ui/assertions_on_result_states.fixed +++ b/tests/ui/assertions_on_result_states.fixed @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result = Ok(1); res.unwrap_err(); //~^ assertions_on_result_states } + +fn issue9916(res: Result) { + let a = 0; + match a { + 0 => {}, + 1 => { res.unwrap(); }, + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/tests/ui/assertions_on_result_states.rs b/tests/ui/assertions_on_result_states.rs index c63c2502b5377..33f1485326b38 100644 --- a/tests/ui/assertions_on_result_states.rs +++ b/tests/ui/assertions_on_result_states.rs @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result = Ok(1); assert!(res.is_err()) //~^ assertions_on_result_states } + +fn issue9916(res: Result) { + let a = 0; + match a { + 0 => {}, + 1 => assert!(res.is_ok()), + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/tests/ui/assertions_on_result_states.stderr b/tests/ui/assertions_on_result_states.stderr index 3bf811588c696..826f049555cae 100644 --- a/tests/ui/assertions_on_result_states.stderr +++ b/tests/ui/assertions_on_result_states.stderr @@ -38,10 +38,16 @@ LL | assert!(r.is_err()); | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` error: called `assert!` with `Result::is_err` - --> tests/ui/assertions_on_result_states.rs:88:5 + --> tests/ui/assertions_on_result_states.rs:87:5 | LL | assert!(res.is_err()) | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();` -error: aborting due to 7 previous errors +error: called `assert!` with `Result::is_ok` + --> tests/ui/assertions_on_result_states.rs:95:14 + | +LL | 1 => assert!(res.is_ok()), + | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `{ res.unwrap(); }` + +error: aborting due to 8 previous errors diff --git a/tests/ui/assign_ops.fixed b/tests/ui/assign_ops.fixed index 3754b9dfe7443..2046d089d6a6c 100644 --- a/tests/ui/assign_ops.fixed +++ b/tests/ui/assign_ops.fixed @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res += Self::ONE; + //~^ assign_op_pattern + count += 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,7 +104,7 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; @@ -99,8 +112,28 @@ mod issue14871 { while count < value { res = res + Self::ONE; count += 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s += S; + //~^ assign_op_pattern + } } diff --git a/tests/ui/assign_ops.rs b/tests/ui/assign_ops.rs index 0b878d4f490b8..f83a40f554730 100644 --- a/tests/ui/assign_ops.rs +++ b/tests/ui/assign_ops.rs @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res = res + Self::ONE; + //~^ assign_op_pattern + count = count + 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,16 +104,36 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; let mut count = 0; while count < value { res = res + Self::ONE; - count += 1; + count = count + 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s = s + S; + //~^ assign_op_pattern + } } diff --git a/tests/ui/assign_ops.stderr b/tests/ui/assign_ops.stderr index c5e698b3ee118..a4fca04893c46 100644 --- a/tests/ui/assign_ops.stderr +++ b/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:10:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,70 +8,94 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:12:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:14:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:16:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:18:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:20:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:22:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:24:5 + --> tests/ui/assign_ops.rs:25:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:31:5 + --> tests/ui/assign_ops.rs:32:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:37:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:39:5 + --> tests/ui/assign_ops.rs:40:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:52:5 + --> tests/ui/assign_ops.rs:53:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` -error: aborting due to 12 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:91:17 + | +LL | res = res + Self::ONE; + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `res += Self::ONE` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:93:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:114:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:136:9 + | +LL | s = s + S; + | ^^^^^^^^^ help: replace it with: `s += S` + +error: aborting due to 16 previous errors diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index 93c573d30863e..bd1b31f74ee95 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5).await } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5).await }; + //~^ async_yields_async + let y = async { call_bar!().await }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } + } + let _ = async { (CustomFutureType + CustomFutureType).await }; + //~^ async_yields_async +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index 166d522e1c04c..605d2734157cf 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5) } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5) }; + //~^ async_yields_async + let y = async { call_bar!() }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } + } + let _ = async { CustomFutureType + CustomFutureType }; + //~^ async_yields_async +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 17a35076fa3f1..3041cf6578938 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -89,5 +89,50 @@ LL | | CustomFutureType LL | | }; | |_____- outer async construct -error: aborting due to 6 previous errors +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:96:21 + | +LL | let x = async { call_bar!(5) }; + | --^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!(5).await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:98:21 + | +LL | let y = async { call_bar!() }; + | --^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!().await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:90:21 + | +LL | async { bar(5) } + | --^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `bar(5).await` + | outer async construct +... +LL | let y = async { call_bar!() }; + | ----------- in this macro invocation + | + = note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:119:21 + | +LL | let _ = async { CustomFutureType + CustomFutureType }; + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await` + | outer async construct + +error: aborting due to 10 previous errors diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 166abbe549c3d..b0b60104c0b91 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if !x & y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x & !y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if !x & y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x & !y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a != b {}; - //~^ bool_comparison - if a != b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b != a {}; - //~^ bool_comparison - if b != a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) != m!(func)) as usize; + let _ = ((1 < 2) & !m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x & !m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + !x & m!(func) + //~^ bool_comparison + } +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 7265c2b4c5a6f..1b1108d6ce504 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x == true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x == false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x < true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false < x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true > x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x == true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x == false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x < true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false < x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true > x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if x < y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x < y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a == !b {}; - //~^ bool_comparison - if !a == b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b == !a {}; - //~^ bool_comparison - if !b == a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) == !m!(func)) as usize; + let _ = ((1 < 2) > m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x > m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + x < m!(func) + //~^ bool_comparison + } +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index ddbb9ff78edb3..98881a2d20fd3 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,155 +1,143 @@ error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:7:8 + --> tests/ui/bool_comparison.rs:7:16 | -LL | if x == true { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x == true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:13:8 + --> tests/ui/bool_comparison.rs:9:16 | -LL | if x == false { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x == false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:19:8 + --> tests/ui/bool_comparison.rs:11:16 | -LL | if true == x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if true == x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:25:8 + --> tests/ui/bool_comparison.rs:13:16 | -LL | if false == x { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if false == x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:31:8 + --> tests/ui/bool_comparison.rs:15:16 | -LL | if x != true { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x != true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:37:8 + --> tests/ui/bool_comparison.rs:17:16 | -LL | if x != false { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x != false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:43:8 + --> tests/ui/bool_comparison.rs:19:16 | -LL | if true != x { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true != x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:49:8 + --> tests/ui/bool_comparison.rs:21:16 | -LL | if false != x { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false != x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:55:8 + --> tests/ui/bool_comparison.rs:23:16 | -LL | if x < true { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x < true { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:61:8 + --> tests/ui/bool_comparison.rs:25:16 | -LL | if false < x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false < x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:67:8 + --> tests/ui/bool_comparison.rs:27:16 | -LL | if x > false { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x > false { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:73:8 + --> tests/ui/bool_comparison.rs:29:16 | -LL | if true > x { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true > x { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:80:8 + --> tests/ui/bool_comparison.rs:33:16 | -LL | if x < y { - | ^^^^^ help: try simplifying it as shown: `!x & y` +LL | let _ = if x < y { "yes" } else { "no" }; + | ^^^^^ help: try: `!x & y` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:86:8 + --> tests/ui/bool_comparison.rs:35:16 | -LL | if x > y { - | ^^^^^ help: try simplifying it as shown: `x & !y` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:135:8 - | -LL | if a == !b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:137:8 - | -LL | if !a == b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:142:8 - | -LL | if b == !a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:144:8 - | -LL | if !b == a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` +LL | let _ = if x > y { "yes" } else { "no" }; + | ^^^^^ help: try: `x & !y` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:169:8 + --> tests/ui/bool_comparison.rs:92:8 | LL | if false == m!(func) {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:171:8 + --> tests/ui/bool_comparison.rs:94:8 | LL | if m!(func) == false {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:173:8 + --> tests/ui/bool_comparison.rs:96:8 | LL | if true == m!(func) {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:175:8 + --> tests/ui/bool_comparison.rs:98:8 | LL | if m!(func) == true {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:193:14 + --> tests/ui/bool_comparison.rs:116:14 | LL | let _ = ((1 < 2) == false) as usize; - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `1 >= 2` + | ^^^^^^^^^^^^^^^^ help: try: `1 >= 2` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:195:14 + --> tests/ui/bool_comparison.rs:118:14 | LL | let _ = (false == m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:199:14 +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:122:14 + | +LL | let _ = ((1 < 2) > m!(func)) as usize; + | ^^^^^^^^^^^^^^^^^^ help: try: `(1 < 2) & !m!(func)` + +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:144:9 + | +LL | x > m!(func) + | ^^^^^^^^^^^^ help: try: `x & !m!(func)` + +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:149:9 | -LL | let _ = ((1 < 2) == !m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `(1 < 2) != m!(func)` +LL | x < m!(func) + | ^^^^^^^^^^^^ help: try: `!x & m!(func)` -error: aborting due to 25 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/cast_raw_slice_pointer_cast.fixed b/tests/ui/cast_raw_slice_pointer_cast.fixed index bddcb0ebf64e6..3c1cf8845957f 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.fixed +++ b/tests/ui/cast_raw_slice_pointer_cast.fixed @@ -1,30 +1,53 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); let mptr = vec.as_mut_ptr(); - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts { use core::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } { use std::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/tests/ui/cast_raw_slice_pointer_cast.rs b/tests/ui/cast_raw_slice_pointer_cast.rs index 0a1eb276d5e9e..8f57b1f961928 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.rs +++ b/tests/ui/cast_raw_slice_pointer_cast.rs @@ -1,6 +1,7 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); @@ -27,4 +28,26 @@ fn main() { let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/tests/ui/cast_raw_slice_pointer_cast.stderr b/tests/ui/cast_raw_slice_pointer_cast.stderr index 60794a988db7d..328dbafbafe7a 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.stderr +++ b/tests/ui/cast_raw_slice_pointer_cast.stderr @@ -1,47 +1,83 @@ error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:8:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:9:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` | = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` error: casting the result of `from_raw_parts_mut` to *mut [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:10:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:11:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:12:26 + --> tests/ui/cast_raw_slice_pointer_cast.rs:13:26 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:16:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:17:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:19:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:20:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:24:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:25:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:27:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:28:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` -error: aborting due to 7 previous errors +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:34:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:36:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:38:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:46:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:48:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:50:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed b/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed new file mode 100644 index 0000000000000..f71fb8d863c6d --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.rs b/tests/ui/cast_raw_slice_pointer_cast_no_std.rs new file mode 100644 index 0000000000000..743e44c97dc13 --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.rs @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr b/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr new file mode 100644 index 0000000000000..5488fbcfa1ce5 --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr @@ -0,0 +1,83 @@ +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:11:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | + = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` + +error: casting the result of `from_raw_parts_mut` to *mut [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:13:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:15:26 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:19:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:22:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:27:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:30:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:36:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:38:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:40:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:48:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:50:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:52:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 71b82040ff62a..8931a3aa09c61 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -316,6 +316,47 @@ fn lint_emitted_at_right_node(opt: Option>) { }; } +pub fn issue_14155() { + let mut arr = ["a", "b", "c"]; + if let Some(last) = arr.last() { + match *last { + //~^ collapsible_match + "a" | "b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(last) = arr.last() { + match &last { + //~^ collapsible_match + &&"a" | &&"b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(mut last) = arr.last_mut() { + match &mut last { + //~^ collapsible_match + &mut &mut "a" | &mut &mut "b" => { + unimplemented!() + }, + _ => (), + } + } + + const NULL_PTR: *const &'static str = std::ptr::null(); + if let Some(last) = arr.last() { + match &raw const *last { + NULL_PTR => unimplemented!(), + _ => (), + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index c290d84ec2976..14b1c1b187e4f 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -250,5 +250,74 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern -error: aborting due to 13 previous errors +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:322:9 + | +LL | / match *last { +LL | | +LL | | "a" | "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:321:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().copied()` + | | + | replace this binding +... +LL | "a" | "b" => { + | ^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:332:9 + | +LL | / match &last { +LL | | +LL | | &&"a" | &&"b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:331:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().as_ref()` + | | + | replace this binding +... +LL | &&"a" | &&"b" => { + | ^^^^^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:342:9 + | +LL | / match &mut last { +LL | | +LL | | &mut &mut "a" | &mut &mut "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:341:17 + | +LL | if let Some(mut last) = arr.last_mut() { + | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` + | | + | replace this binding +... +LL | &mut &mut "a" | &mut &mut "b" => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern + +error: aborting due to 16 previous errors diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 7b27306375289..2597067099906 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -77,6 +77,8 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> tests/ui/collapsible_match2.rs:54:14 | +LL | match Some(&[1]) { + | ---------- use: `Some(&[1]).copied()` LL | Some(s) => match *s { | ^ replace this binding LL | diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 65bfded38835e..9f9e4e253c3c4 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -326,4 +328,40 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + #[derive(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 4826c5497b4a3..74a793b9a70e6 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -396,4 +398,43 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Self::A + } + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index 0f73ad55a85eb..cd46414cb4a8a 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:20:1 + --> tests/ui/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | @@ -18,7 +18,7 @@ LL ~ struct FooDefault<'a> { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:42:1 + --> tests/ui/derivable_impls.rs:44:1 | LL | / impl std::default::Default for TupleDefault { LL | | @@ -35,7 +35,7 @@ LL ~ struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:95:1 + --> tests/ui/derivable_impls.rs:97:1 | LL | / impl Default for StrDefault<'_> { LL | | @@ -52,7 +52,7 @@ LL ~ struct StrDefault<'a>(&'a str); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:122:1 + --> tests/ui/derivable_impls.rs:124:1 | LL | / impl Default for Y { LL | | @@ -69,7 +69,7 @@ LL ~ struct Y(u32); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:162:1 + --> tests/ui/derivable_impls.rs:164:1 | LL | / impl Default for WithoutSelfCurly { LL | | @@ -86,7 +86,7 @@ LL ~ struct WithoutSelfCurly { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:171:1 + --> tests/ui/derivable_impls.rs:173:1 | LL | / impl Default for WithoutSelfParan { LL | | @@ -103,7 +103,7 @@ LL ~ struct WithoutSelfParan(bool); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:194:1 + --> tests/ui/derivable_impls.rs:196:1 | LL | / impl Default for DirectDefaultDefaultCall { LL | | @@ -119,7 +119,7 @@ LL ~ pub struct DirectDefaultDefaultCall { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:206:1 + --> tests/ui/derivable_impls.rs:208:1 | LL | / impl Default for EquivalentToDefaultDefaultCallVec { LL | | @@ -135,7 +135,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallVec { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:234:1 + --> tests/ui/derivable_impls.rs:236:1 | LL | / impl Default for EquivalentToDefaultDefaultCallLocal { LL | | @@ -151,7 +151,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallLocal { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:274:1 + --> tests/ui/derivable_impls.rs:276:1 | LL | / impl Default for RepeatDefault1 { LL | | @@ -168,7 +168,7 @@ LL ~ pub struct RepeatDefault1 { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:309:1 + --> tests/ui/derivable_impls.rs:311:1 | LL | / impl Default for SimpleEnum { LL | | @@ -187,5 +187,28 @@ LL ~ #[default] LL ~ Bar, | -error: aborting due to 11 previous errors +error: this `impl` can be derived + --> tests/ui/derivable_impls.rs:432:5 + | +LL | / impl Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Self::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 12 previous errors diff --git a/tests/ui/derivable_impls_derive_const.fixed b/tests/ui/derivable_impls_derive_const.fixed new file mode 100644 index 0000000000000..f0d8d2d240924 --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.fixed @@ -0,0 +1,25 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + #[derive_const(Default)] + struct Foo(u64); + + + + #[derive(Copy, Clone)] + #[derive_const(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + +fn main() {} diff --git a/tests/ui/derivable_impls_derive_const.rs b/tests/ui/derivable_impls_derive_const.rs new file mode 100644 index 0000000000000..7d70db1c097d4 --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.rs @@ -0,0 +1,32 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + //~^ derivable_impls + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Bar::A + } + } +} + +fn main() {} diff --git a/tests/ui/derivable_impls_derive_const.stderr b/tests/ui/derivable_impls_derive_const.stderr new file mode 100644 index 0000000000000..196bac185ddaa --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.stderr @@ -0,0 +1,46 @@ +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:11:5 + | +LL | / impl const Default for Foo { +LL | | +LL | | fn default() -> Self { +LL | | Self(0) +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::derivable-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::derivable_impls)]` +help: replace the manual implementation with a derive attribute + | +LL ~ #[derive_const(Default)] +LL ~ struct Foo(u64); +LL | +LL ~ + | + +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:24:5 + | +LL | / impl const Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Bar::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive_const(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 423a73734daab..46695dc929ab7 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 8deffb4210e43..4082fa5b56f4b 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index 98c26e6bec2eb..2a94a8f316587 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:90:5 + --> tests/ui/doc/doc-fixable.rs:91:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:108:5 + --> tests/ui/doc/doc-fixable.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:117:8 + --> tests/ui/doc/doc-fixable.rs:118:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:121:7 + --> tests/ui/doc/doc-fixable.rs:122:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:124:22 + --> tests/ui/doc/doc-fixable.rs:125:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:126:5 + --> tests/ui/doc/doc-fixable.rs:127:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:134:5 + --> tests/ui/doc/doc-fixable.rs:135:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:148:5 + --> tests/ui/doc/doc-fixable.rs:149:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:160:43 + --> tests/ui/doc/doc-fixable.rs:161:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:165:5 + --> tests/ui/doc/doc-fixable.rs:166:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:1 + --> tests/ui/doc/doc-fixable.rs:167:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:174:43 + --> tests/ui/doc/doc-fixable.rs:175:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:179:5 + --> tests/ui/doc/doc-fixable.rs:180:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:1 + --> tests/ui/doc/doc-fixable.rs:181:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:194:5 + --> tests/ui/doc/doc-fixable.rs:195:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:214:22 + --> tests/ui/doc/doc-fixable.rs:215:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:239:34 + --> tests/ui/doc/doc-fixable.rs:240:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:22 + --> tests/ui/doc/doc-fixable.rs:264:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:30 + --> tests/ui/doc/doc-fixable.rs:264:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:268:5 + --> tests/ui/doc/doc-fixable.rs:269:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:275:9 + --> tests/ui/doc/doc-fixable.rs:276:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:280:5 + --> tests/ui/doc/doc-fixable.rs:281:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr index 0197d2ab4cf9c..f92f472512f67 100644 --- a/tests/ui/entry_unfixable.stderr +++ b/tests/ui/entry_unfixable.stderr @@ -10,6 +10,7 @@ LL | | false LL | | } | |_____________^ | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api = note: `-D clippy::map-entry` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` @@ -24,6 +25,8 @@ LL | | } else { LL | | hm.insert(key, true); LL | | } | |_____^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: usage of `contains_key` followed by `insert` on a `HashMap` --> tests/ui/entry_unfixable.rs:80:13 @@ -36,6 +39,8 @@ LL | | let interner = INTERNER.lock().unwrap(); LL | | return Err(interner.resolve(name).unwrap().to_owned()); LL | | } | |_____________^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: aborting due to 3 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 3d2b41b8fb81c..6944a979c05e6 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(**f); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(g); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(***b); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 79d1103410d9d..5bcc1cb26fd78 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(|| f()); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(|| g()); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(|| b()); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index aa32ed1a38ef6..0b401cdea9875 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -215,28 +215,46 @@ LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` error: redundant closure - --> tests/ui/eta.rs:588:14 + --> tests/ui/eta.rs:592:16 + | +LL | accepts_fn(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `**f` + +error: redundant closure + --> tests/ui/eta.rs:596:16 + | +LL | accepts_fn(|| g()); + | ^^^^^^ help: replace the closure with the function itself: `g` + +error: redundant closure + --> tests/ui/eta.rs:609:16 + | +LL | accepts_fn(|| b()); + | ^^^^^^ help: replace the closure with the function itself: `***b` + +error: redundant closure + --> tests/ui/eta.rs:633:14 | LL | .map(|n| MyError::A(n)) | ^^^^^^^^^^^^^^^^^ help: replace the closure with the tuple variant itself: `MyError::A` error: redundant closure - --> tests/ui/eta.rs:585:14 + --> tests/ui/eta.rs:630:14 | LL | .map(|n| S(n)) | ^^^^^^^^ help: replace the closure with the tuple struct itself: `S` error: redundant closure - --> tests/ui/eta.rs:582:14 + --> tests/ui/eta.rs:627:14 | LL | .map(|n| g(n)) | ^^^^^^^^ help: replace the closure with the function itself: `g` error: redundant closure - --> tests/ui/eta.rs:579:14 + --> tests/ui/eta.rs:624:14 | LL | .map(|n| f(n)) | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 39 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/excessive_precision.fixed b/tests/ui/excessive_precision.fixed index 8a8c2e1939c83..8158d4b332acd 100644 --- a/tests/ui/excessive_precision.fixed +++ b/tests/ui/excessive_precision.fixed @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.577_215_664_901_532_9; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/tests/ui/excessive_precision.rs b/tests/ui/excessive_precision.rs index 5dcf55cb92735..7ee6247ee5ac9 100644 --- a/tests/ui/excessive_precision.rs +++ b/tests/ui/excessive_precision.rs @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0000000000000000e+00; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.5772156649015328606065120900824024310421; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index f5eeadf0c8cb0..40806d67487fd 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -1,5 +1,5 @@ error: float has excessive precision - --> tests/ui/excessive_precision.rs:20:26 + --> tests/ui/excessive_precision.rs:27:26 | LL | const BAD32_1: f32 = 0.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + const BAD32_1: f32 = 0.123_456_79_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:22:26 + --> tests/ui/excessive_precision.rs:29:26 | LL | const BAD32_2: f32 = 0.123_456_789; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + const BAD32_2: f32 = 0.123_456_79; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:24:26 + --> tests/ui/excessive_precision.rs:31:26 | LL | const BAD32_3: f32 = 0.100_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + const BAD32_3: f32 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:26:29 + --> tests/ui/excessive_precision.rs:33:29 | LL | const BAD32_EDGE: f32 = 1.000_000_9; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + const BAD32_EDGE: f32 = 1.000_001; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:31:26 + --> tests/ui/excessive_precision.rs:38:26 | LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + const BAD64_3: f64 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:35:22 + --> tests/ui/excessive_precision.rs:42:22 | LL | println!("{:?}", 8.888_888_888_888_888_888_888); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + println!("{:?}", 8.888_888_888_888_89); | error: float has excessive precision - --> tests/ui/excessive_precision.rs:47:22 + --> tests/ui/excessive_precision.rs:54:22 | LL | let bad32: f32 = 1.123_456_789; | ^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + let bad32: f32 = 1.123_456_8; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:49:26 + --> tests/ui/excessive_precision.rs:56:26 | LL | let bad32_suf: f32 = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + let bad32_suf: f32 = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:51:21 + --> tests/ui/excessive_precision.rs:58:21 | LL | let bad32_inf = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + let bad32_inf = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:62:36 + --> tests/ui/excessive_precision.rs:69:36 | LL | let bad_vec32: Vec = vec![0.123_456_789]; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let bad_vec32: Vec = vec![0.123_456_79]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:64:36 + --> tests/ui/excessive_precision.rs:71:36 | LL | let bad_vec64: Vec = vec![0.123_456_789_123_456_789]; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let bad_vec64: Vec = vec![0.123_456_789_123_456_78]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:69:24 + --> tests/ui/excessive_precision.rs:76:24 | LL | let bad_e32: f32 = 1.123_456_788_888e-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + let bad_e32: f32 = 1.123_456_8e-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:73:27 + --> tests/ui/excessive_precision.rs:80:27 | LL | let bad_bige32: f32 = 1.123_456_788_888E-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:86:13 + --> tests/ui/excessive_precision.rs:93:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:90:13 + --> tests/ui/excessive_precision.rs:97:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:101:20 + --> tests/ui/excessive_precision.rs:108:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -192,5 +192,56 @@ LL - const _: f64 = 3.0000000000000000e+00; LL + const _: f64 = 3.0; | -error: aborting due to 16 previous errors +error: float has excessive precision + --> tests/ui/excessive_precision.rs:113:18 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:113:5 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.01234567890123456789012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:115:18 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:115:5 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.01234567890123456789012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:127:17 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:127:5 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let gamma = 0.5772156649015328606065120900824024310421; +LL + let gamma = 0.577_215_664_901_532_9; + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs index a1548db6710d1..e1dd79026839b 100644 --- a/tests/ui/float_equality_without_abs.rs +++ b/tests/ui/float_equality_without_abs.rs @@ -1,8 +1,8 @@ +#![feature(f128)] +#![feature(f16)] #![warn(clippy::float_equality_without_abs)] //@no-rustfix: suggestions cause type ambiguity -// FIXME(f16_f128): add tests for these types when abs is available - pub fn is_roughly_equal(a: f32, b: f32) -> bool { (a - b) < f32::EPSILON //~^ float_equality_without_abs @@ -44,10 +44,20 @@ pub fn main() { let _ = f32::EPSILON > 1.0 - 2.0; //~^ float_equality_without_abs + let _ = (a as f16 - b as f16) < f16::EPSILON; + //~^ float_equality_without_abs + + let _ = (a as f128 - b as f128) < f128::EPSILON; + //~^ float_equality_without_abs + // those are correct + let _ = (a as f16 - b as f16).abs() < f16::EPSILON; let _ = (a - b).abs() < f32::EPSILON; let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + let _ = (a as f128 - b as f128).abs() < f128::EPSILON; + let _ = f16::EPSILON > (a as f16 - b as f16).abs(); let _ = f32::EPSILON > (a - b).abs(); let _ = f64::EPSILON > (a as f64 - b as f64).abs(); + let _ = f128::EPSILON > (a as f128 - b as f128).abs(); } diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr index d4c89ce72ba9b..55a150dead5a4 100644 --- a/tests/ui/float_equality_without_abs.stderr +++ b/tests/ui/float_equality_without_abs.stderr @@ -89,5 +89,21 @@ LL | let _ = f32::EPSILON > 1.0 - 2.0; | | | help: add `.abs()`: `(1.0 - 2.0).abs()` -error: aborting due to 11 previous errors +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:47:13 + | +LL | let _ = (a as f16 - b as f16) < f16::EPSILON; + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f16 - b as f16).abs()` + +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:50:13 + | +LL | let _ = (a as f128 - b as f128) < f128::EPSILON; + | -----------------------^^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f128 - b as f128).abs()` + +error: aborting due to 13 previous errors diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index d14a805b66679..0fd130609aee2 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -165,3 +165,44 @@ mod issue15257 { do_something((i % 2 == 0).then_some(closure_fn)); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + (self.count < 5).then(|| { self.count += 1; self.count }) + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = true.then(|| { mac!(); 42 }); +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index bb0072f31573f..640828aa9bf6b 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -211,3 +211,54 @@ mod issue15257 { }); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + if self.count < 5 { + self.count += 1; + Some(self.count) + } else { + None + } + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = if true { + mac!(); + Some(42) + } else { + None + }; +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index c2e624a0a73b4..58651a0559424 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -116,5 +116,28 @@ LL | | None LL | | }); | |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)` -error: aborting due to 11 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:231:13 + | +LL | / if self.count < 5 { +LL | | self.count += 1; +LL | | Some(self.count) +LL | | } else { +LL | | None +LL | | } + | |_____________^ help: try: `(self.count < 5).then(|| { self.count += 1; self.count })` + +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:249:13 + | +LL | let _ = if true { + | _____________^ +LL | | mac!(); +LL | | Some(42) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try: `true.then(|| { mac!(); 42 })` + +error: aborting due to 13 previous errors diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index 9b8c39331970c..7d01a7fb61fc1 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -521,4 +521,19 @@ mod tokio_spawn_test { } } +mod issue15541 { + async fn good() -> ! { + loop { + std::future::pending().await + } + } + + async fn bad() { + //~v infinite_loop + loop { + std::future::pending().await + } + } +} + fn main() {} diff --git a/tests/ui/infinite_loops.stderr b/tests/ui/infinite_loops.stderr index 4c6b6f725f130..319f1e5012b64 100644 --- a/tests/ui/infinite_loops.stderr +++ b/tests/ui/infinite_loops.stderr @@ -333,5 +333,15 @@ LL | | } | = help: if this is not intended, try adding a `break` or `return` condition in the loop -error: aborting due to 23 previous errors +error: infinite loop detected + --> tests/ui/infinite_loops.rs:533:9 + | +LL | / loop { +LL | | std::future::pending().await +LL | | } + | |_________^ + | + = help: if this is not intended, try adding a `break` or `return` condition in the loop + +error: aborting due to 24 previous errors diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 7b0d190683469..406336dbd095d 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| c.is_ascii_uppercase()); //~^ manual_is_ascii_check + take_while(|c: char| c.is_ascii_uppercase()); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + //~^ manual_is_ascii_check } diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index e4f7fe9f5838a..a624497e75e52 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| ('A'..='Z').contains(&c)); //~^ manual_is_ascii_check + take_while(|c| matches!(c, 'A'..='Z')); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + //~^ manual_is_ascii_check } diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 9fd7f457b4201..cb2548ea73160 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -176,7 +176,19 @@ LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:114:63 + --> tests/ui/manual_is_ascii_check.rs:111:20 + | +LL | take_while(|c| matches!(c, 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - take_while(|c| matches!(c, 'A'..='Z')); +LL + take_while(|c: char| c.is_ascii_uppercase()); + | + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:116:63 | LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,7 +200,7 @@ LL + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ | error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:116:71 + --> tests/ui/manual_is_ascii_check.rs:118:71 | LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,5 +211,17 @@ LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'. LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); | -error: aborting due to 29 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:120:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); +LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | + +error: aborting due to 31 previous errors diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index 6c971ba633848..a10aae8cc1217 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -72,20 +73,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable - let it = [1, 2, 3].into_iter(); - let _ = it.map(|x| x).next(); + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let mut subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index 59dcfcda3b6e6..fc6e018f92484 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -78,20 +79,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well let it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.map(|n| n).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index a50c0d6b87b53..8d19d62cdc641 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,5 +1,5 @@ error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:7:47 + --> tests/ui/map_identity.rs:8:47 | LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | ^^^^^^^^^^^^^^^^^^ help: remove the call to `map` @@ -8,25 +8,25 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_identity)]` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:57 + --> tests/ui/map_identity.rs:10:57 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:29 + --> tests/ui/map_identity.rs:10:29 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:12:32 + --> tests/ui/map_identity.rs:13:32 | LL | let _: Option = Some(3).map(|x| x); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:14:36 + --> tests/ui/map_identity.rs:15:36 | LL | let _: Result = Ok(-3).map(|x| { | ____________________________________^ @@ -36,19 +36,19 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:25:36 + --> tests/ui/map_identity.rs:26:36 | LL | let _: Result = Ok(1).map_err(|a| a); | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:36:22 + --> tests/ui/map_identity.rs:37:22 | LL | let _ = x.clone().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:38:22 + --> tests/ui/map_identity.rs:39:22 | LL | let _ = x.clone().map(|(x, y)| { | ______________________^ @@ -58,76 +58,100 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:42:22 + --> tests/ui/map_identity.rs:43:22 | LL | let _ = x.clone().map(|(x, y)| return (x, y)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:46:22 + --> tests/ui/map_identity.rs:47:22 | LL | let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:76:30 + --> tests/ui/map_identity.rs:77:30 | LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:88:15 + --> tests/ui/map_identity.rs:86:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `it` mutable + | +LL ~ let mut it = [1, 2, 3].into_iter(); +LL ~ let _ = it.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:93:23 + | +LL | let _ = subindex.0.map(|n| n).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `subindex` mutable + | +LL ~ let mut subindex = (index.by_ref().take(3), 42); +LL ~ let _ = subindex.0.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:100:15 | LL | let _ = it.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:93:19 + --> tests/ui/map_identity.rs:106:19 | LL | let _ = { it }.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:105:30 + --> tests/ui/map_identity.rs:119:30 | LL | let _ = x.iter().copied().map(|[x, y]| [x, y]); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:131:26 + --> tests/ui/map_identity.rs:145:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:135:26 + --> tests/ui/map_identity.rs:149:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:143:26 + --> tests/ui/map_identity.rs:157:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:147:26 + --> tests/ui/map_identity.rs:161:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:157:26 + --> tests/ui/map_identity.rs:171:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:161:26 + --> tests/ui/map_identity.rs:175:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index 223c7447975aa..8e937d609512a 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -97,3 +97,10 @@ pub mod issue15301 { println!("Just called a Rust function from Rust!"); } } + +pub mod issue15491 { + pub trait Foo { + #[allow(clippy::missing_inline_in_public_items)] + fn foo(&self) {} + } +} diff --git a/tests/ui/missing_transmute_annotations_unfixable.rs b/tests/ui/missing_transmute_annotations_unfixable.rs new file mode 100644 index 0000000000000..08ba3b791ee76 --- /dev/null +++ b/tests/ui/missing_transmute_annotations_unfixable.rs @@ -0,0 +1,29 @@ +//@no-rustfix + +fn issue14984() { + async fn e() {} + async fn x() -> u32 { + 0 + } + async fn y() -> f32 { + 0.0 + }; + let mut yy = unsafe { std::ptr::read(&y()) }; + yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + let mut zz = 0u8; + zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + yy = unsafe { std::mem::transmute(zz) }; + //~^ missing_transmute_annotations + + fn a() -> impl Sized { + 0u32 + } + + let mut b: f32 = 0.0; + b = unsafe { std::mem::transmute(a()) }; + //~^ missing_transmute_annotations +} diff --git a/tests/ui/missing_transmute_annotations_unfixable.stderr b/tests/ui/missing_transmute_annotations_unfixable.stderr new file mode 100644 index 0000000000000..83efdce13f7e7 --- /dev/null +++ b/tests/ui/missing_transmute_annotations_unfixable.stderr @@ -0,0 +1,36 @@ +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:12:29 + | +LL | yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the source and destination types a name, and adding missing type annotations + = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:16:29 + | +LL | zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the origin type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:19:29 + | +LL | yy = unsafe { std::mem::transmute(zz) }; + | ^^^^^^^^^ + | + = help: consider giving the destination type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:27:28 + | +LL | b = unsafe { std::mem::transmute(a()) }; + | ^^^^^^^^^ + | + = help: consider giving `a()`'s type a name, and adding missing type annotations + +error: aborting due to 4 previous errors + diff --git a/tests/ui/mut_reference.fixed b/tests/ui/mut_reference.fixed new file mode 100644 index 0000000000000..03d854099e644 --- /dev/null +++ b/tests/ui/mut_reference.fixed @@ -0,0 +1,170 @@ +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} + +mod issue11268 { + macro_rules! x { + (1 $f:expr) => { + $f(&mut 1); + }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; + } + + fn f() { + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); + } +} + +struct MyStruct; + +impl MyStruct { + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} +} + +#[warn(clippy::unnecessary_mut_passed)] +fn main() { + // Functions + takes_ref(&42); + //~^ unnecessary_mut_passed + takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&42); + //~^ unnecessary_mut_passed + + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + //~^ unnecessary_mut_passed + + // Methods + let my_struct = MyStruct; + my_struct.takes_ref(&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&42); + //~^ unnecessary_mut_passed + + // No error + + // Functions + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; + as_ptr(&mut 42); + + let a = &mut 42; + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); + + // Methods + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); +} diff --git a/tests/ui/mut_reference.rs b/tests/ui/mut_reference.rs index f664c373cdc3a..80e3f50692770 100644 --- a/tests/ui/mut_reference.rs +++ b/tests/ui/mut_reference.rs @@ -1,60 +1,170 @@ -#![allow(unused_variables, dead_code)] -//@no-rustfix -fn takes_an_immutable_reference(a: &i32) {} -fn takes_a_mutable_reference(a: &mut i32) {} +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} mod issue11268 { macro_rules! x { - ($f:expr) => { + (1 $f:expr) => { $f(&mut 1); }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; } fn f() { - x!(super::takes_an_immutable_reference); - x!(super::takes_a_mutable_reference); + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); } } struct MyStruct; impl MyStruct { - fn takes_an_immutable_reference(&self, a: &i32) {} - - fn takes_a_mutable_reference(&self, a: &mut i32) {} + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} } #[warn(clippy::unnecessary_mut_passed)] fn main() { // Functions - takes_an_immutable_reference(&mut 42); + takes_ref(&mut 42); + //~^ unnecessary_mut_passed + takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&mut 42); //~^ unnecessary_mut_passed - let as_ptr: fn(&i32) = takes_an_immutable_reference; + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&mut &42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&mut &mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; as_ptr(&mut 42); //~^ unnecessary_mut_passed // Methods let my_struct = MyStruct; - my_struct.takes_an_immutable_reference(&mut 42); + my_struct.takes_ref(&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&mut 42); //~^ unnecessary_mut_passed // No error // Functions - takes_an_immutable_reference(&42); - let as_ptr: fn(&i32) = takes_an_immutable_reference; + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; as_ptr(&42); - takes_a_mutable_reference(&mut 42); - let as_ptr: fn(&mut i32) = takes_a_mutable_reference; + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; as_ptr(&mut 42); let a = &mut 42; - takes_an_immutable_reference(a); + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); // Methods - my_struct.takes_an_immutable_reference(&42); - my_struct.takes_a_mutable_reference(&mut 42); - my_struct.takes_an_immutable_reference(a); + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); } diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index 474221329c258..5ecfaa37416ba 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,23 +1,77 @@ -error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:30:34 +error: the function `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:56:15 | -LL | takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +error: the function `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:58:19 + | +LL | takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the function `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:60:22 + | +LL | takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + +error: the function `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:62:21 + | +LL | takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:66:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:69:12 + | +LL | as_ptr(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:72:12 + | +LL | as_ptr(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:34:12 + --> tests/ui/mut_reference.rs:75:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:80:25 + | +LL | my_struct.takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:82:29 + | +LL | my_struct.takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the method `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:84:32 + | +LL | my_struct.takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` -error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:39:44 +error: the method `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:86:31 | -LL | my_struct.takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | my_struct.takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` -error: aborting due to 3 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index 3f117ee5a5021..9404d07ba0e08 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -135,7 +135,7 @@ error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:157:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` @@ -144,19 +144,19 @@ error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:162:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:173:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:175:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal --> tests/ui/needless_bool/fixable.rs:185:12 diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs index 159cbe49828f6..24ce5786b9104 100644 --- a/tests/ui/needless_for_each_unfixable.rs +++ b/tests/ui/needless_for_each_unfixable.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return, clippy::uninlined_format_args)] +#![allow(clippy::needless_return)] fn main() { let v: Vec = Vec::new(); @@ -11,7 +11,22 @@ fn main() { if *v == 10 { return; } else { - println!("{}", v); + println!("{v}"); } }); } + +fn issue9912() { + let mut i = 0; + // Changing this to a `for` loop would break type inference + [].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); + + // Changing this would actually be okay, but we still suggest `MaybeIncorrect`ly + [1i32].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); +} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index 3a3a240c5a3ab..6bc56db68a0ec 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -19,7 +19,7 @@ LL + LL + if *v == 10 { LL + return; LL + } else { -LL + println!("{}", v); +LL + println!("{v}"); LL + } LL + } | @@ -29,5 +29,39 @@ LL - return; LL + continue; | -error: aborting due to 1 previous error +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:22:5 + | +LL | / [].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [].iter() { +LL + +LL + i += 1; +LL + } + | + +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:28:5 + | +LL | / [1i32].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [1i32].iter() { +LL + +LL + i += 1; +LL + } + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 70cf9fa7369ff..ea4591d8b71ac 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -210,3 +210,35 @@ fn needless_loop() { black_box([1, 2, 3, 4, 5, 6, 7, 8][i]); } } + +fn issue_15068() { + let a = vec![vec![0u8; MAX_LEN]; MAX_LEN]; + let b = vec![0u8; MAX_LEN]; + + for i in 0..MAX_LEN { + // no error + let _ = a[0][i]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][0]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][b[i] as usize]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[i][i]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[0][i]; + } +} diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index 23c085f9d3b21..33a519d8a80d5 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -168,5 +168,29 @@ LL - for i in 0..vec.len() { LL + for (i, ) in vec.iter_mut().enumerate() { | -error: aborting due to 14 previous errors +error: the loop variable `i` is used to index `a` + --> tests/ui/needless_range_loop.rs:235:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator and enumerate() + | +LL - for i in 0..MAX_LEN { +LL + for (i, ) in a.iter().enumerate().take(MAX_LEN) { + | + +error: the loop variable `i` is only used to index `a` + --> tests/ui/needless_range_loop.rs:240:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator + | +LL - for i in 0..MAX_LEN { +LL + for in a.iter().take(MAX_LEN) { + | + +error: aborting due to 16 previous errors diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index cacce9a7d1cbd..f03f74dfafe21 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -182,14 +182,12 @@ fn issue_5794() { if !b == true {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if !b != true {} //~^ nonminimal_bool //~| bool_comparison if true == !b {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if true != !b {} //~^ nonminimal_bool //~| bool_comparison diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index c20412974b20f..6a20b9216da52 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -154,98 +154,86 @@ error: this boolean expression can be simplified LL | if !b == true {} | ^^^^^^^^^^ help: try: `b != true` -error: this comparison might be written more concisely +error: equality checks against true are unnecessary --> tests/ui/nonminimal_bool.rs:182:8 | LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b != true` + | ^^^^^^^^^^ help: try: `!b` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` -error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:182:8 - | -LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` - error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} | ^^^^^^^^^^ help: try: `b == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try: `true != b` -error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:189:8 - | -LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `true != b` - error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` + | ^^^^^^^^^^ help: try: `!b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} | ^^^^^^^^^^ help: try: `true == b` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:196:8 + --> tests/ui/nonminimal_bool.rs:194:8 | LL | if !b == !c {} | ^^^^^^^^ help: try: `b == c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:198:8 + --> tests/ui/nonminimal_bool.rs:196:8 | LL | if !b != !c {} | ^^^^^^^^ help: try: `b != c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:214:8 + --> tests/ui/nonminimal_bool.rs:212:8 | LL | if !(a < 2.0 && !b) { | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:233:12 + --> tests/ui/nonminimal_bool.rs:231:12 | LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} | ^^^^^^^^^^ help: try: `S == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!S` + | ^^^^^^^^^^ help: try: `!!S` -error: aborting due to 33 previous errors +error: aborting due to 31 previous errors diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 55c1b8f110ced..340be7c7e932a 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn +} + fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 5ed47e4c60b51..d902c87379b77 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + x.map(|x| unsafe { f(x) }); + //~^ option_map_unit_fn + x.map(|x| unsafe { { f(x) } }); + //~^ option_map_unit_fn +} + fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 3f7abae34eea7..2405aa9a7ccfa 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -153,5 +153,21 @@ LL | option().map(|value| println!("{:?}", value)); | | | help: try: `if let Some(value) = option() { println!("{:?}", value) }` -error: aborting due to 19 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:108:5 + | +LL | x.map(|x| unsafe { f(x) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:110:5 + | +LL | x.map(|x| unsafe { { f(x) } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: aborting due to 21 previous errors diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index ba9beef57afaa..9660b82fe7d61 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option> = None; + let _ = option.unwrap_or(vec!["fallback"]); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index fac90249a243c..c387335211643 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option> = None; + let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint diff --git a/tests/ui/or_then_unwrap.stderr b/tests/ui/or_then_unwrap.stderr index 1160498c6053b..3e66b15edbd65 100644 --- a/tests/ui/or_then_unwrap.stderr +++ b/tests/ui/or_then_unwrap.stderr @@ -14,10 +14,16 @@ LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger l | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` error: found `.or(Some(…)).unwrap()` - --> tests/ui/or_then_unwrap.rs:33:31 + --> tests/ui/or_then_unwrap.rs:33:20 + | +LL | let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or(vec!["fallback"])` + +error: found `.or(Some(…)).unwrap()` + --> tests/ui/or_then_unwrap.rs:39:31 | LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 65854d7eb4bf9..b044be7d54ac5 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -31,6 +31,20 @@ fn panic() { let b = a + 2; } +const fn panic_const() { + let a = 2; + panic!(); + //~^ panic + + panic!("message"); + //~^ panic + + panic!("{} {}", "panic with", "multiple arguments"); + //~^ panic + + let b = a + 2; +} + fn todo() { let a = 2; todo!(); @@ -114,6 +128,7 @@ fn debug_assert_msg() { fn main() { panic(); + panic_const(); todo(); unimplemented(); unreachable(); diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 03e459e4ec6ec..7b767ae0f65bf 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -19,9 +19,27 @@ error: `panic` should not be present in production code LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `todo` should not be present in production code +error: `panic` should not be present in production code --> tests/ui/panicking_macros.rs:36:5 | +LL | panic!(); + | ^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:39:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:42:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> tests/ui/panicking_macros.rs:50:5 + | LL | todo!(); | ^^^^^^^ | @@ -29,19 +47,19 @@ LL | todo!(); = help: to override `-D warnings` add `#[allow(clippy::todo)]` error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:39:5 + --> tests/ui/panicking_macros.rs:53:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:42:5 + --> tests/ui/panicking_macros.rs:56:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:50:5 + --> tests/ui/panicking_macros.rs:64:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -50,19 +68,19 @@ LL | unimplemented!(); = help: to override `-D warnings` add `#[allow(clippy::unimplemented)]` error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:53:5 + --> tests/ui/panicking_macros.rs:67:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:56:5 + --> tests/ui/panicking_macros.rs:70:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:64:5 + --> tests/ui/panicking_macros.rs:78:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -71,40 +89,40 @@ LL | unreachable!(); = help: to override `-D warnings` add `#[allow(clippy::unreachable)]` error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:67:5 + --> tests/ui/panicking_macros.rs:81:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:70:5 + --> tests/ui/panicking_macros.rs:84:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> tests/ui/panicking_macros.rs:78:5 + --> tests/ui/panicking_macros.rs:92:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:81:5 + --> tests/ui/panicking_macros.rs:95:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:84:5 + --> tests/ui/panicking_macros.rs:98:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:87:5 + --> tests/ui/panicking_macros.rs:101:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index ebfe19c700ee6..26139f9b67102 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -105,3 +105,16 @@ fn issue_14930() { println!("Hello x is {0:2$.1$}", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello x is {1:.*}", 5, 0.01); + //~^ print_literal + + println!("Hello x is {:.p$}", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + ); + //~^^ print_literal +} diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 8f3d9be069854..7c4cf028e84cc 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -106,3 +106,17 @@ fn issue_14930() { println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello {} is {2:.*}", "x", 5, 0.01); + //~^ print_literal + + println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello {}: {2} is {3:.*} (which {3} with {1} places)", + "name", 5, "x", 0.01 + ); + //~^^ print_literal +} diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 1c378017d151b..c136f52800f69 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -277,5 +277,41 @@ LL - println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); LL + println!("Hello x is {0:2$.1$}", 0.01, 2, 3); | -error: aborting due to 22 previous errors +error: literal with an empty format string + --> tests/ui/print_literal.rs:111:36 + | +LL | println!("Hello {} is {2:.*}", "x", 5, 0.01); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {2:.*}", "x", 5, 0.01); +LL + println!("Hello x is {1:.*}", 5, 0.01); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:114:36 + | +LL | println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {:.p$}", "x", 0.01, p = 5); +LL + println!("Hello x is {:.p$}", 0.01, p = 5); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:119:9 + | +LL | "name", 5, "x", 0.01 + | ^^^^^^^^^^^^^^ + | +help: try + | +LL - "Hello {}: {2} is {3:.*} (which {3} with {1} places)", +LL + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + | + +error: aborting due to 25 previous errors diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index 78e1ceb480a5f..4457878b6faf1 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b).cast::<()>(); + //~^ ptr_as_ptr +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index 70732cf0a6c16..9124fc912d2c6 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + //~^ ptr_as_ptr +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index c0a2a4b6d204d..af21c1e35f52a 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -199,5 +199,11 @@ error: `as` casting between raw pointers without changing their constness LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::()` -error: aborting due to 33 previous errors +error: `as` casting between raw pointers without changing their constness + --> tests/ui/ptr_as_ptr.rs:235:16 + | +LL | let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `std::ptr::addr_of_mut!(*b).cast::<()>()` + +error: aborting due to 34 previous errors diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 79bfae1f7ebb4..cf57de53d9f3d 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local).cast_const(); //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = (r as *const u32).cast_mut(); + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index f6590dabd5b84..ea53a0fa8c50f 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local) as *const _; //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = r as *const _ as *mut _; + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 0b1644168ff51..4adb5cc5ad7fe 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -89,5 +89,11 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = std::ptr::addr_of_mut!(local) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` -error: aborting due to 14 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:112:26 + | +LL | let _ptr: *mut u32 = r as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(r as *const u32).cast_mut()` + +error: aborting due to 15 previous errors diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7eb53e733ad50..7308e78aae263 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 9fa5b117194d7..467bf4d779f2a 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed b/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed new file mode 100644 index 0000000000000..5b93a91da003a --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0;} + //~^ semicolon_inside_block +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs b/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs new file mode 100644 index 0000000000000..aa2c0cb50299c --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; + //~^ semicolon_inside_block +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr b/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr new file mode 100644 index 0000000000000..5bb91915a5fd8 --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr @@ -0,0 +1,16 @@ +error: consider moving the `;` inside the block for consistent formatting + --> tests/ui/semicolon_inside_block_stmt_expr_attrs.rs:9:5 + | +LL | {0; 0}; + | ^^^^^^^ + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::semicolon_inside_block)]` +help: put the `;` here + | +LL - {0; 0}; +LL + {0; 0;} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 603ab0accb0b6..c27cec558242d 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::(); +} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index b6d4abad9f8fe..7d53f7fc3072b 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::(); +} diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 93f5b87c3d2a7..0a0fe3a1990b0 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -68,3 +68,20 @@ fn main() { } ); } + +fn issue15559() { + fn foo() {} + assert_eq!( + //~^ unit_cmp + { + 1; + }, + foo() + ); + assert_eq!(foo(), foo()); + //~^ unit_cmp + + // don't lint on explicitly written unit expr + assert_eq!(foo(), ()); + assert_ne!((), ContainsUnit(()).0); +} diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 8f26749fd8605..21aa9dce1510f 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -71,5 +71,23 @@ LL | | true; LL | | ); | |_____^ -error: aborting due to 6 previous errors +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:74:5 + | +LL | / assert_eq!( +LL | | +LL | | { +LL | | 1; +LL | | }, +LL | | foo() +LL | | ); + | |_____^ + +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:81:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index a4fdf09cd5dcb..17518e123d12e 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -2,7 +2,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:23:5 | LL | rc.clone(); - | ^^^^^^^^^^ help: try: `Rc::::clone(&rc)` + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` | = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` @@ -11,25 +11,25 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:28:5 | LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `Arc::::clone(&arc)` + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:33:5 | LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `Weak::::clone(&rcweak)` + | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:38:5 | LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `Weak::::clone(&arc_weak)` + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:44:33 | LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `Arc::::clone(&x)` + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` error: using `clone` on type `T` which implements the `Copy` trait --> tests/ui/unnecessary_clone.rs:49:5 @@ -56,7 +56,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:108:14 | LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc::::clone(&try_opt!(Some(rc)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` error: aborting due to 9 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 2081772d06b36..339d4a95084ae 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | x | x): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 6bf8fce36616c..f5c99183b0c51 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | (x | x)): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index c805dc992b1c2..d2b617c322c46 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:16:12 + --> tests/ui/unnested_or_patterns.rs:21:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let box (0 | 2) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:18:12 + --> tests/ui/unnested_or_patterns.rs:23:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:21:12 + --> tests/ui/unnested_or_patterns.rs:26:12 | LL | if let Some(1) | C0 | Some(2) = None {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + if let Some(1 | 2) | C0 = None {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:23:12 + --> tests/ui/unnested_or_patterns.rs:28:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + if let &mut (0 | 2) = &mut 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:25:12 + --> tests/ui/unnested_or_patterns.rs:30:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + if let x @ (0 | 2) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:27:12 + --> tests/ui/unnested_or_patterns.rs:32:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + if let (0, 1 | 2 | 3) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:29:12 + --> tests/ui/unnested_or_patterns.rs:34:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + if let (1 | 2 | 3, 0) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:31:12 + --> tests/ui/unnested_or_patterns.rs:36:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + if let (x, ..) | (x, 1 | 2) = (0, 1) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:33:12 + --> tests/ui/unnested_or_patterns.rs:38:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -109,7 +109,7 @@ LL + if let [0 | 1] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:35:12 + --> tests/ui/unnested_or_patterns.rs:40:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + if let [x, 0 | 1] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:37:12 + --> tests/ui/unnested_or_patterns.rs:42:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + if let [x, 0 | 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:39:12 + --> tests/ui/unnested_or_patterns.rs:44:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + if let [x, ..] | [x, 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:42:12 + --> tests/ui/unnested_or_patterns.rs:47:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + if let TS(0 | 1, x) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:44:12 + --> tests/ui/unnested_or_patterns.rs:49:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:46:12 + --> tests/ui/unnested_or_patterns.rs:51:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:52:12 + --> tests/ui/unnested_or_patterns.rs:53:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:64:12 + --> tests/ui/unnested_or_patterns.rs:65:12 | LL | if let [1] | [53] = [0] {} | ^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + if let [1 | 53] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:70:13 + --> tests/ui/unnested_or_patterns.rs:71:13 | LL | let (0 | (1 | _)) = 0; | ^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + let (0 | 1 | _) = 0; | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:73:16 + --> tests/ui/unnested_or_patterns.rs:74:16 | LL | if let (0 | (1 | _)) = 0 {} | ^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + if let (0 | 1 | _) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:77:20 + --> tests/ui/unnested_or_patterns.rs:78:20 | LL | fn or_in_param((x | (x | x)): i32) {} | ^^^^^^^^^^^^^ @@ -240,5 +240,17 @@ LL - fn or_in_param((x | (x | x)): i32) {} LL + fn or_in_param((x | x | x): i32) {} | -error: aborting due to 20 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:94:12 + | +LL | if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} +LL + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + | + +error: aborting due to 21 previous errors diff --git a/tests/ui/unused_unit.edition2021.fixed b/tests/ui/unused_unit.edition2021.fixed index def8ef86e3c57..8e12bd2c8c7b9 100644 --- a/tests/ui/unused_unit.edition2021.fixed +++ b/tests/ui/unused_unit.edition2021.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] fn bar() { - run(|| { todo!() }); //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unused_unit.edition2021.stderr b/tests/ui/unused_unit.edition2021.stderr index 13cc20d4d7adc..9ad3c2df915ec 100644 --- a/tests/ui/unused_unit.edition2021.stderr +++ b/tests/ui/unused_unit.edition2021.stderr @@ -119,10 +119,10 @@ LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> tests/ui/unused_unit.rs:136:15 + --> tests/ui/unused_unit.rs:131:13 | -LL | run(|| -> () { todo!() }); - | ^^^^^^ help: remove the `-> ()` +LL | fn bar() -> () { + | ^^^^^^ help: remove the `-> ()` error: aborting due to 20 previous errors diff --git a/tests/ui/unused_unit.edition2024.fixed b/tests/ui/unused_unit.edition2024.fixed index f908b958b191e..688d2fe9afa28 100644 --- a/tests/ui/unused_unit.edition2024.fixed +++ b/tests/ui/unused_unit.edition2024.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 7298ec40cc28b..31e980f2655c6 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs index 4e872c67b423e..70c28fe54f37d 100644 --- a/tests/ui/unwrap_in_result.rs +++ b/tests/ui/unwrap_in_result.rs @@ -1,4 +1,5 @@ #![warn(clippy::unwrap_in_result)] +#![allow(clippy::ok_expect)] struct A; @@ -20,10 +21,9 @@ impl A { // should be detected fn bad_divisible_by_3(i_str: String) -> Result { - //~^ unwrap_in_result - // checks whether a string represents a number divisible by 3 let i = i_str.parse::().unwrap(); + //~^ unwrap_in_result if i % 3 == 0 { Ok(true) } else { @@ -32,9 +32,8 @@ impl A { } fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().ok().expect("not a number"); //~^ unwrap_in_result - - let i = i_str.parse::().expect("not a number"); if i % 3 == 0 { return Some(true); } @@ -42,13 +41,66 @@ impl A { } fn in_closure(a: Option) -> Option { - //~^ unwrap_in_result + // No lint inside a closure let c = || a.unwrap(); - Some(c()) + + // But lint outside + let a = c().then_some(true); + let _ = a.unwrap(); + //~^ unwrap_in_result + + None } + + const fn in_const_inside_fn() -> bool { + const A: bool = { + const fn inner(b: Option) -> Option { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `const` + inner(Some(true)).unwrap() + }; + A + } + + fn in_static_inside_fn() -> bool { + static A: bool = { + const fn inner(b: Option) -> Option { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `static` + inner(Some(true)).unwrap() + }; + A + } +} + +macro_rules! mac { + () => { + Option::unwrap(Some(3)) + }; +} + +fn type_relative_unwrap() -> Option<()> { + _ = Option::unwrap(Some(3)); + //~^ unwrap_in_result + + // Do not lint macro output + _ = mac!(); + + None } -fn main() { - A::bad_divisible_by_3("3".to_string()); - A::good_divisible_by_3("3".to_string()); +fn main() -> Result<(), ()> { + A::bad_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + A::good_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + Result::unwrap(A::good_divisible_by_3("3".to_string())); + //~^ unwrap_in_result + Ok(()) } diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr index 5e3eab813e075..804b44246dc70 100644 --- a/tests/ui/unwrap_in_result.stderr +++ b/tests/ui/unwrap_in_result.stderr @@ -1,55 +1,107 @@ -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:22:5 - | -LL | / fn bad_divisible_by_3(i_str: String) -> Result { -... | -LL | | } - | |_____^ - | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:26:17 +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:25:17 | LL | let i = i_str.parse::().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:23:45 + | +LL | fn bad_divisible_by_3(i_str: String) -> Result { + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method = note: `-D clippy::unwrap-in-result` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_in_result)]` -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:34:5 +error: `expect` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:35:17 + | +LL | let i = i_str.parse::().ok().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:34:48 + | +LL | fn example_option_expect(i_str: String) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:49:17 + | +LL | let _ = a.unwrap(); + | ^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:43:39 + | +LL | fn in_closure(a: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:58:22 | -LL | / fn example_option_expect(i_str: String) -> Option { -LL | | -LL | | -LL | | let i = i_str.parse::().expect("not a number"); -... | -LL | | None -LL | | } - | |_____^ +LL | Some(b.unwrap()) + | ^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:37:17 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:57:48 | -LL | let i = i_str.parse::().expect("not a number"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const fn inner(b: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:44:5 +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:71:22 | -LL | / fn in_closure(a: Option) -> Option { -LL | | -LL | | let c = || a.unwrap(); -LL | | Some(c()) -LL | | } - | |_____^ +LL | Some(b.unwrap()) + | ^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:46:20 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:70:48 + | +LL | const fn inner(b: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:89:9 + | +LL | _ = Option::unwrap(Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:88:30 + | +LL | fn type_relative_unwrap() -> Option<()> { + | ^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:99:5 + | +LL | A::bad_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:98:14 + | +LL | fn main() -> Result<(), ()> { + | ^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:101:5 + | +LL | A::good_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:103:5 | -LL | let c = || a.unwrap(); - | ^^^^^^^^^^ +LL | Result::unwrap(A::good_divisible_by_3("3".to_string())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index f360a8afadf61..55742459c92cf 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -242,3 +242,12 @@ fn issue_12101() { for a in &[1, 2] {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &[]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index a779d33557cb4..fbf7131323c3b 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -242,3 +242,12 @@ fn issue_12101() { for a in &(vec![1, 2]) {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &vec![]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 806d6617200ff..d16c8a8944a24 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -127,5 +127,11 @@ error: useless use of `vec!` LL | for a in &(vec![1, 2]) {} | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` -error: aborting due to 21 previous errors +error: useless use of `vec!` + --> tests/ui/vec.rs:250:13 + | +LL | let v = &vec![]; + | ^^^^^^^ help: you can use a slice directly: `&[]` + +error: aborting due to 22 previous errors diff --git a/triagebot.toml b/triagebot.toml index 805baf2af6dd0..7b19f8658c088 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -49,11 +49,18 @@ new_pr = true # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] +# Show differences when a PR is rebased +[range-diff] + +# Amend a review to include a link to what was changed since the review +[review-changes-since] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", + "flip1995", ] [assign.owners] From ec34e8e69fb2e133e6ec0ebc87443f6fa8bda092 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 5 Sep 2025 06:30:28 +0800 Subject: [PATCH 0212/1900] Fix precedence parenthesis for replace_arith_op Example --- ```rust fn main() { let x = 1*x $0+ 2; } ``` **Before this PR**: ```rust fn main() { let x = 1*x.wrapping_add(2); } ``` **After this PR**: ```rust fn main() { let x = (1*x).wrapping_add(2); } ``` --- .../src/handlers/replace_arith_op.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 440ab4d4604f2..94ac1c342d302 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -88,7 +88,11 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> |builder| { let method_name = kind.method_name(op); - builder.replace(range, format!("{lhs}.{method_name}({rhs})")) + if lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix) { + builder.replace(range, format!("({lhs}).{method_name}({rhs})")) + } else { + builder.replace(range, format!("{lhs}.{method_name}({rhs})")) + } }, ) } @@ -227,6 +231,23 @@ fn main() { ) } + #[test] + fn replace_arith_with_wrapping_add_add_parenthesis() { + check_assist( + replace_arith_with_wrapping, + r#" +fn main() { + let x = 1*x $0+ 2; +} +"#, + r#" +fn main() { + let x = (1*x).wrapping_add(2); +} +"#, + ) + } + #[test] fn replace_arith_not_applicable_with_non_empty_selection() { check_assist_not_applicable( From b2eb28da2342e60779a6d78d19c991c99e1b5c37 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Wed, 27 Aug 2025 20:33:50 -0600 Subject: [PATCH 0213/1900] fix: Filter suggestion parts that match existing code --- tests/ui/bool_assert_comparison.stderr | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index f823f08f31dca..72aa6303a2026 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -272,10 +272,8 @@ LL | assert_eq!(a!(), true); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(a!()); +LL - assert_eq!(a!(), true); +LL + assert!(a!()); | error: used `assert_eq!` with a literal bool @@ -286,10 +284,8 @@ LL | assert_eq!(true, b!()); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(b!()); +LL - assert_eq!(true, b!()); +LL + assert!(b!()); | error: used `debug_assert_eq!` with a literal bool From 7ce8e289db72471c41b53664a214ed0e00458f07 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 13 Aug 2025 12:38:11 -0400 Subject: [PATCH 0214/1900] Add `Path::has_trailing_sep` and related methods --- library/std/src/path.rs | 187 +++++++++++++++++++++++++++++++++++++- library/std/tests/path.rs | 38 +++++++- 2 files changed, 221 insertions(+), 4 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 19663e4a9df62..943c40220b848 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1412,6 +1412,99 @@ impl PathBuf { } } + /// Sets whether the path has a trailing [separator](MAIN_SEPARATOR). + /// + /// The value returned by [`has_trailing_sep`](Path::has_trailing_sep) will be equivalent to + /// the provided value if possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(true); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn set_trailing_sep(&mut self, trailing_sep: bool) { + if trailing_sep { self.push_trailing_sep() } else { self.pop_trailing_sep() } + } + + /// Adds a trailing [separator](MAIN_SEPARATOR) to the path. + /// + /// This acts similarly to [`Path::with_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// + /// p = PathBuf::from("dir/"); + /// p.push_trailing_sep(); + /// assert_eq!(p.as_os_str(), OsStr::new("dir/")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn push_trailing_sep(&mut self) { + if !self.has_trailing_sep() { + self.push(""); + } + } + + /// Removes a trailing [separator](MAIN_SEPARATOR) from the path, if possible. + /// + /// This acts similarly to [`Path::trim_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir//"); + /// + /// assert!(p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir//")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.pop_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn pop_trailing_sep(&mut self) { + self.inner.truncate(self.trim_trailing_sep().as_os_str().len()); + } + /// Updates [`self.file_name`] to `file_name`. /// /// If [`self.file_name`] was [`None`], this is equivalent to pushing @@ -1610,7 +1703,7 @@ impl PathBuf { let new = extension.as_encoded_bytes(); if !new.is_empty() { // truncate until right after the file name - // this is necessary for trimming the trailing slash + // this is necessary for trimming the trailing separator let end_file_name = file_name[file_name.len()..].as_ptr().addr(); let start = self.inner.as_encoded_bytes().as_ptr().addr(); self.inner.truncate(end_file_name.wrapping_sub(start)); @@ -2755,6 +2848,94 @@ impl Path { self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) } + /// Checks whether the path ends in a trailing [separator](MAIN_SEPARATOR). + /// + /// This is generally done to ensure that a path is treated as a directory, not a file, + /// although it does not actually guarantee that such a path is a directory on the underlying + /// file system. + /// + /// Despite this behavior, two paths are still considered the same in Rust whether they have a + /// trailing separator or not. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::Path; + /// + /// assert!(Path::new("dir/").has_trailing_sep()); + /// assert!(!Path::new("file.rs").has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn has_trailing_sep(&self) -> bool { + self.as_os_str().as_encoded_bytes().last().copied().is_some_and(is_sep_byte) + } + + /// Ensures that a path has a trailing [separator](MAIN_SEPARATOR), + /// allocating a [`PathBuf`] if necessary. + /// + /// The resulting path will return true for [`has_trailing_sep`](Self::has_trailing_sep). + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").with_trailing_sep().as_os_str(), OsStr::new("dir//")); + /// assert_eq!(Path::new("dir/").with_trailing_sep().as_os_str(), OsStr::new("dir/")); + /// assert!(!Path::new("dir").has_trailing_sep()); + /// assert!(Path::new("dir").with_trailing_sep().has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn with_trailing_sep(&self) -> Cow<'_, Path> { + if self.has_trailing_sep() { Cow::Borrowed(self) } else { Cow::Owned(self.join("")) } + } + + /// Trims a trailing [separator](MAIN_SEPARATOR) from a path, if possible. + /// + /// The resulting path will return false for [`has_trailing_sep`](Self::has_trailing_sep) for + /// most paths. + /// + /// Some paths, like `/`, cannot be trimmed in this way. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir/").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/")); + /// assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn trim_trailing_sep(&self) -> &Path { + if self.has_trailing_sep() && (!self.has_root() || self.parent().is_some()) { + let mut bytes = self.inner.as_encoded_bytes(); + while let Some((last, init)) = bytes.split_last() + && is_sep_byte(*last) + { + bytes = init; + } + + // SAFETY: Trimming trailing ASCII bytes will retain the validity of the string. + Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }) + } else { + self + } + } + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// /// If `path` is absolute, it replaces the current path. @@ -2907,7 +3088,7 @@ impl Path { /// `a/b` all have `a` and `b` as components, but `./a/b` starts with /// an additional [`CurDir`] component. /// - /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// * Trailing separators are normalized away, so `/a/b` and `/a/b/` are equivalent. /// /// Note that no other normalization takes place; in particular, `a/c` /// and `a/b/../c` are distinct, to account for the possibility that `b` @@ -3710,7 +3891,7 @@ impl Error for NormalizeError {} /// /// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics], /// except that it stops short of resolving symlinks. This means it will keep `..` -/// components and trailing slashes. +/// components and trailing separators. /// /// On Windows, for verbatim paths, this will simply return the path as given. For other /// paths, this is currently equivalent to calling diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index fa76c50597b05..1d3337ff935a8 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1,4 +1,9 @@ -#![feature(clone_to_uninit, maybe_uninit_slice, normalize_lexically)] +// tidy-alphabetical-start +#![feature(clone_to_uninit)] +#![feature(maybe_uninit_slice)] +#![feature(normalize_lexically)] +#![feature(path_trailing_sep)] +// tidy-alphabetical-end use std::clone::CloneToUninit; use std::ffi::OsStr; @@ -2532,3 +2537,34 @@ fn normalize_lexically() { fn compare_path_to_str() { assert!(&PathBuf::from("x") == "x"); } + +#[test] +fn test_trim_trailing_sep() { + assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/")); + assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//")); + assert_eq!(Path::new("").trim_trailing_sep().as_os_str(), OsStr::new("")); + assert_eq!(Path::new(".").trim_trailing_sep().as_os_str(), OsStr::new(".")); + assert_eq!(Path::new("./").trim_trailing_sep().as_os_str(), OsStr::new(".")); + assert_eq!(Path::new(".//").trim_trailing_sep().as_os_str(), OsStr::new(".")); + assert_eq!(Path::new("..").trim_trailing_sep().as_os_str(), OsStr::new("..")); + assert_eq!(Path::new("../").trim_trailing_sep().as_os_str(), OsStr::new("..")); + assert_eq!(Path::new("..//").trim_trailing_sep().as_os_str(), OsStr::new("..")); + + #[cfg(any(windows, target_os = "cygwin"))] + { + assert_eq!(Path::new("\\").trim_trailing_sep().as_os_str(), OsStr::new("\\")); + assert_eq!(Path::new("\\\\").trim_trailing_sep().as_os_str(), OsStr::new("\\\\")); + assert_eq!(Path::new("c:/").trim_trailing_sep().as_os_str(), OsStr::new("c:/")); + assert_eq!(Path::new("c://").trim_trailing_sep().as_os_str(), OsStr::new("c://")); + assert_eq!(Path::new("c:./").trim_trailing_sep().as_os_str(), OsStr::new("c:.")); + assert_eq!(Path::new("c:.//").trim_trailing_sep().as_os_str(), OsStr::new("c:.")); + assert_eq!(Path::new("c:../").trim_trailing_sep().as_os_str(), OsStr::new("c:..")); + assert_eq!(Path::new("c:..//").trim_trailing_sep().as_os_str(), OsStr::new("c:..")); + assert_eq!(Path::new("c:\\").trim_trailing_sep().as_os_str(), OsStr::new("c:\\")); + assert_eq!(Path::new("c:\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:\\\\")); + assert_eq!(Path::new("c:.\\").trim_trailing_sep().as_os_str(), OsStr::new("c:.")); + assert_eq!(Path::new("c:.\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:.")); + assert_eq!(Path::new("c:..\\").trim_trailing_sep().as_os_str(), OsStr::new("c:..")); + assert_eq!(Path::new("c:..\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:..")); + } +} From 9a1eb85394e3612213e829ed871eb281079a2364 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 5 Sep 2025 04:53:05 +0000 Subject: [PATCH 0215/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 7420b6200967c..a51d86c3d6f15 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9385c64c95d971329e62917adc4349c8ccdbafe0 +b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7 From 5cadd4541c96edf0caaf1847b3c13b1ced4d5121 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 09:07:08 +0200 Subject: [PATCH 0216/1900] make use of Duration::from_nanos_u128 --- src/tools/miri/src/clock.rs | 9 +-------- src/tools/miri/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 34465e9cac60d..47608f873a481 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -46,14 +46,7 @@ impl Instant { InstantKind::Virtual { nanoseconds: earlier }, ) => { let duration = nanoseconds.saturating_sub(earlier); - // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, - // so we have to implement this ourselves. - // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). - // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. - let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); - // It is impossible for nanosecond to overflow because u32::MAX > 1e9. - let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); - Duration::new(seconds, nanosecond) + Duration::from_nanos_u128(duration) } _ => panic!("all `Instant` must be of the same kind"), } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 0856411b8e836..a00d9525fab54 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -18,6 +18,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] +#![feature(duration_from_nanos_u128)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, From b9b235acb088aa88d96890c930e174ab56abbccd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 5 Sep 2025 15:34:49 +0800 Subject: [PATCH 0217/1900] Fix indent for unresolved_field fixes Examples --- ```rust mod indent { struct Foo {} fn foo() { let foo = Foo{}; foo.bar$0; } } ``` **Before this PR**: ```rust mod indent { struct Foo { bar: () } fn foo() { let foo = Foo{}; foo.bar; } } ``` **After this PR**: ```rust mod indent { struct Foo { bar: () } fn foo() { let foo = Foo{}; foo.bar; } } ``` --- New field list add newline ```rust mod indent { struct Foo; fn foo() { Foo.bar$0; } } ``` **Before this PR**: ```rust mod indent { struct Foo{ bar: () } fn foo() { Foo.bar; } } ``` **After this PR**: ```rust mod indent { struct Foo { bar: (), } fn foo() { Foo.bar; } } ``` --- .../src/handlers/unresolved_field.rs | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 690158989679b..358e0c43c2a93 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -1,5 +1,3 @@ -use std::iter; - use either::Either; use hir::{Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union, db::ExpandDatabase}; use ide_db::text_edit::TextEdit; @@ -194,17 +192,20 @@ fn add_field_to_struct_fix( Some(make::visibility_pub_crate()) }; // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 - let indent = IndentLevel::from_node(struct_syntax.value) + 1; + let indent = IndentLevel::from_node(struct_syntax.value); - let field = make::record_field(visibility, field_name, suggested_type).indent(indent); - let record_field_list = make::record_field_list(iter::once(field)); + let field = + make::record_field(visibility, field_name, suggested_type).indent(indent + 1); // A Unit Struct with no `;` is invalid syntax. We should not suggest this fix. let semi_colon = algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?; if semi_colon.kind() != SyntaxKind::SEMICOLON { return None; } - src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string()); + src_change_builder.replace( + semi_colon.text_range(), + format!(" {{\n{}{field},\n{indent}}}", indent + 1), + ); Some(Assist { id: AssistId::quick_fix("convert-unit-struct-to-record-struct"), @@ -230,7 +231,7 @@ fn record_field_layout( field_list: ast::RecordFieldList, struct_syntax: &SyntaxNode, ) -> Option<(TextSize, String)> { - let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() { + let (offset, needs_comma, indent) = match field_list.fields().last() { Some(record_field) => { let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?; @@ -239,19 +240,22 @@ fn record_field_layout( ( last_field_syntax.text_range().end(), syntax.kind() != SyntaxKind::COMMA, - false, last_field_indent, ) } // Empty Struct. Add a field right before the closing brace None => { let indent = IndentLevel::from_node(struct_syntax) + 1; - let offset = field_list.r_curly_token()?.text_range().start(); - (offset, false, true, indent) + let offset = field_list.l_curly_token()?.text_range().end(); + (offset, false, indent) } }; - let comma = if needs_comma { ",\n" } else { "" }; - let trailing_new_line = if trailing_new_line { "\n" } else { "" }; + let trailing_new_line = if !field_list.syntax().text().contains_char('\n') { + format!("\n{}", field_list.indent_level()) + } else { + String::new() + }; + let comma = if needs_comma { ",\n" } else { "\n" }; let record_field = make::record_field(visibility, name, suggested_type); Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) @@ -377,18 +381,24 @@ fn foo() { fn unresolved_field_fix_on_unit() { check_fix( r#" + mod indent { struct Foo; fn foo() { Foo.bar$0; } + } "#, r#" - struct Foo{ bar: () } + mod indent { + struct Foo { + bar: (), + } fn foo() { Foo.bar; } + } "#, ); } @@ -396,6 +406,7 @@ fn foo() { fn unresolved_field_fix_on_empty() { check_fix( r#" + mod indent { struct Foo{ } @@ -403,8 +414,10 @@ fn foo() { let foo = Foo{}; foo.bar$0; } + } "#, r#" + mod indent { struct Foo{ bar: () } @@ -413,6 +426,32 @@ fn foo() { let foo = Foo{}; foo.bar; } + } + "#, + ); + + check_fix( + r#" + mod indent { + struct Foo {} + + fn foo() { + let foo = Foo{}; + foo.bar$0; + } + } + "#, + r#" + mod indent { + struct Foo { + bar: () + } + + fn foo() { + let foo = Foo{}; + foo.bar; + } + } "#, ); } From 96f385b20aa252629570cadeca16a9e159e6810c Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Thu, 7 Aug 2025 21:27:12 +0200 Subject: [PATCH 0218/1900] RawVecInner: add missing `unsafe` to unsafe fns - RawVecInner::grow_exact causes UB if called with len and additional arguments such that len + additional is less than the current capacity. Indeed, in that case it calls Allocator::grow with a new_layout that is smaller than old_layout, which violates a safety precondition. - All RawVecInner methods for resizing the buffer cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because in that case Allocator::grow/shrink is called with an old_layout that does not fit the allocated block, which violates a safety precondition. - RawVecInner::current_memory might cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because the unchecked_mul might overflow. - Furthermore, these methods cause UB if called with an elem_layout where the size is not a multiple of the alignment. This is because Layout::repeat is used (in layout_array) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in current_memory) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not fit the allocated block, which violates a safety precondition. --- library/alloc/src/raw_vec/mod.rs | 149 +++++++++++++++++++++++------ library/alloc/src/raw_vec/tests.rs | 10 +- 2 files changed, 123 insertions(+), 36 deletions(-) diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index b0027e964e467..fd8283195a385 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -177,6 +177,8 @@ impl RawVec { /// the returned `RawVec`. #[inline] pub(crate) const fn new_in(alloc: A) -> Self { + // Check assumption made in `current_memory` + const { assert!(T::LAYOUT.size() % T::LAYOUT.align() == 0) }; Self { inner: RawVecInner::new_in(alloc, Alignment::of::()), _marker: PhantomData } } @@ -328,7 +330,8 @@ impl RawVec { #[inline] #[track_caller] pub(crate) fn reserve(&mut self, len: usize, additional: usize) { - self.inner.reserve(len, additional, T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.reserve(len, additional, T::LAYOUT) } } /// A specialized version of `self.reserve(len, 1)` which requires the @@ -337,7 +340,8 @@ impl RawVec { #[inline(never)] #[track_caller] pub(crate) fn grow_one(&mut self) { - self.inner.grow_one(T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.grow_one(T::LAYOUT) } } /// The same as `reserve`, but returns on errors instead of panicking or aborting. @@ -346,7 +350,8 @@ impl RawVec { len: usize, additional: usize, ) -> Result<(), TryReserveError> { - self.inner.try_reserve(len, additional, T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.try_reserve(len, additional, T::LAYOUT) } } /// Ensures that the buffer contains at least enough space to hold `len + @@ -369,7 +374,8 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { - self.inner.reserve_exact(len, additional, T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.reserve_exact(len, additional, T::LAYOUT) } } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. @@ -378,7 +384,8 @@ impl RawVec { len: usize, additional: usize, ) -> Result<(), TryReserveError> { - self.inner.try_reserve_exact(len, additional, T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.try_reserve_exact(len, additional, T::LAYOUT) } } /// Shrinks the buffer down to the specified capacity. If the given amount @@ -395,7 +402,8 @@ impl RawVec { #[track_caller] #[inline] pub(crate) fn shrink_to_fit(&mut self, cap: usize) { - self.inner.shrink_to_fit(cap, T::LAYOUT) + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) } } } @@ -518,8 +526,12 @@ impl RawVecInner { &self.alloc } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment #[inline] - fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { + unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { if elem_layout.size() == 0 || self.cap.as_inner() == 0 { None } else { @@ -535,48 +547,67 @@ impl RawVecInner { } } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { + unsafe fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { // Callers expect this function to be very cheap when there is already sufficient capacity. // Therefore, we move all the resizing and error-handling logic from grow_amortized and // handle_reserve behind a call, while making sure that this function is likely to be // inlined as just a comparison and a call if the comparison fails. #[cold] - fn do_reserve_and_handle( + unsafe fn do_reserve_and_handle( slf: &mut RawVecInner, len: usize, additional: usize, elem_layout: Layout, ) { - if let Err(err) = slf.grow_amortized(len, additional, elem_layout) { + // SAFETY: Precondition passed to caller + if let Err(err) = unsafe { slf.grow_amortized(len, additional, elem_layout) } { handle_error(err); } } if self.needs_to_grow(len, additional, elem_layout) { - do_reserve_and_handle(self, len, additional, elem_layout); + unsafe { + do_reserve_and_handle(self, len, additional, elem_layout); + } } } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { + unsafe fn grow_one(&mut self, elem_layout: Layout) { + // SAFETY: Precondition passed to caller + if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } { handle_error(err); } } - fn try_reserve( + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + unsafe fn try_reserve( &mut self, len: usize, additional: usize, elem_layout: Layout, ) -> Result<(), TryReserveError> { if self.needs_to_grow(len, additional, elem_layout) { - self.grow_amortized(len, additional, elem_layout)?; + // SAFETY: Precondition passed to caller + unsafe { + self.grow_amortized(len, additional, elem_layout)?; + } } unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed @@ -585,22 +616,34 @@ impl RawVecInner { Ok(()) } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] #[track_caller] - fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { - if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { + unsafe fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { + // SAFETY: Precondition passed to caller + if let Err(err) = unsafe { self.try_reserve_exact(len, additional, elem_layout) } { handle_error(err); } } - fn try_reserve_exact( + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + unsafe fn try_reserve_exact( &mut self, len: usize, additional: usize, elem_layout: Layout, ) -> Result<(), TryReserveError> { if self.needs_to_grow(len, additional, elem_layout) { - self.grow_exact(len, additional, elem_layout)?; + // SAFETY: Precondition passed to caller + unsafe { + self.grow_exact(len, additional, elem_layout)?; + } } unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed @@ -609,11 +652,16 @@ impl RawVecInner { Ok(()) } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { - if let Err(err) = self.shrink(cap, elem_layout) { + unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { + if let Err(err) = unsafe { self.shrink(cap, elem_layout) } { handle_error(err); } } @@ -632,7 +680,13 @@ impl RawVecInner { self.cap = unsafe { Cap::new_unchecked(cap) }; } - fn grow_amortized( + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - The sum of `len` and `additional` must be greater than or equal to + /// `self.capacity(elem_layout.size())` + unsafe fn grow_amortized( &mut self, len: usize, additional: usize, @@ -657,14 +711,25 @@ impl RawVecInner { let new_layout = layout_array(cap, elem_layout)?; - let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; - // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + // SAFETY: + // - For the `current_memory` call: Precondition passed to caller + // - For the `finish_grow` call: Precondition passed to caller + // + `current_memory` does the right thing + let ptr = + unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? }; + // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } - fn grow_exact( + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - The sum of `len` and `additional` must be greater than or equal to + /// `self.capacity(elem_layout.size())` + unsafe fn grow_exact( &mut self, len: usize, additional: usize, @@ -679,7 +744,12 @@ impl RawVecInner { let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; let new_layout = layout_array(cap, elem_layout)?; - let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // SAFETY: + // - For the `current_memory` call: Precondition passed to caller + // - For the `finish_grow` call: Precondition passed to caller + // + `current_memory` does the right thing + let ptr = + unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? }; // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap); @@ -687,9 +757,14 @@ impl RawVecInner { Ok(()) } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` #[cfg(not(no_global_oom_handling))] #[inline] - fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { + unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); // SAFETY: Just checked this isn't trying to grow unsafe { self.shrink_unchecked(cap, elem_layout) } @@ -711,8 +786,12 @@ impl RawVecInner { cap: usize, elem_layout: Layout, ) -> Result<(), TryReserveError> { - let (ptr, layout) = - if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) }; + // SAFETY: Precondition passed to caller + let (ptr, layout) = if let Some(mem) = unsafe { self.current_memory(elem_layout) } { + mem + } else { + return Ok(()); + }; // If shrinking to 0, deallocate the buffer. We don't reach this point // for the T::IS_ZST case since current_memory() will have returned @@ -748,7 +827,8 @@ impl RawVecInner { /// Ideally this function would take `self` by move, but it cannot because it exists to be /// called from a `Drop` impl. unsafe fn deallocate(&mut self, elem_layout: Layout) { - if let Some((ptr, layout)) = self.current_memory(elem_layout) { + // SAFETY: Precondition passed to caller + if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } { unsafe { self.alloc.deallocate(ptr, layout); } @@ -756,10 +836,17 @@ impl RawVecInner { } } +/// # Safety +/// If `current_memory` matches `Some((ptr, old_layout))`: +/// - `ptr` must denote a block of memory *currently allocated* via `alloc` +/// - `old_layout` must *fit* that block of memory +/// - `new_layout` must have the same alignment as `old_layout` +/// - `new_layout.size()` must be greater than or equal to `old_layout.size()` +/// If `current_memory` is `None`, this function is safe. // not marked inline(never) since we want optimizers to be able to observe the specifics of this // function, see tests/codegen-llvm/vec-reserve-extend.rs. #[cold] -fn finish_grow( +unsafe fn finish_grow( new_layout: Layout, current_memory: Option<(NonNull, Layout)>, alloc: &mut A, diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index 700fa922739d6..15f48c03dc54c 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -85,7 +85,7 @@ struct ZST; fn zst_sanity(v: &RawVec) { assert_eq!(v.capacity(), usize::MAX); assert_eq!(v.ptr(), core::ptr::Unique::::dangling().as_ptr()); - assert_eq!(v.inner.current_memory(T::LAYOUT), None); + assert_eq!(unsafe { v.inner.current_memory(T::LAYOUT) }, None); } #[test] @@ -126,12 +126,12 @@ fn zst() { assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err); zst_sanity(&v); - assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err); - assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(unsafe { v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err); + assert_eq!(unsafe { v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err); zst_sanity(&v); - assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err); - assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(unsafe { v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err); + assert_eq!(unsafe { v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err); zst_sanity(&v); } From 1186db896e2f4013b1f15612b31ed18e2b653228 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 10:28:47 +0200 Subject: [PATCH 0219/1900] atomics: unify handling min/max and the other RMWs --- src/tools/miri/src/concurrency/data_race.rs | 85 +++++-------------- src/tools/miri/src/concurrency/genmc/dummy.rs | 19 +---- src/tools/miri/src/concurrency/genmc/mod.rs | 29 +------ src/tools/miri/src/intrinsics/atomic.rs | 74 ++++++++++------ src/tools/miri/src/intrinsics/mod.rs | 3 + 5 files changed, 83 insertions(+), 127 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 19d37a691d90c..a7822d0d6fa31 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -56,6 +56,7 @@ use super::vector_clock::{VClock, VTimestamp, VectorIdx}; use super::weak_memory::EvalContextExt as _; use crate::concurrency::GlobalDataRaceHandler; use crate::diagnostics::RacingOp; +use crate::intrinsics::AtomicRmwOp; use crate::*; pub type AllocState = VClockAlloc; @@ -778,9 +779,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, place: &MPlaceTy<'tcx>, rhs: &ImmTy<'tcx>, - op: mir::BinOp, - not: bool, - atomic: AtomicRwOrd, + atomic_op: AtomicRmwOp, + ord: AtomicRwOrd, ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; @@ -793,8 +793,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this, place.ptr().addr(), place.layout.size, - atomic, - (op, not), + place.layout.backend_repr.is_signed(), + ord, + atomic_op, rhs.to_scalar(), old.to_scalar(), )?; @@ -804,13 +805,26 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } - let val = this.binary_op(op, &old, rhs)?; - let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val }; + let val = match atomic_op { + AtomicRmwOp::MirOp { op, neg } => { + let val = this.binary_op(op, &old, rhs)?; + if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val } + } + AtomicRmwOp::Max => { + let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?; + if lt { rhs } else { &old }.clone() + } + AtomicRmwOp::Min => { + let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?; + if lt { &old } else { rhs }.clone() + } + }; + this.allow_data_races_mut(|this| this.write_immediate(*val, place))?; - this.validate_atomic_rmw(place, atomic)?; + this.validate_atomic_rmw(place, ord)?; - this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?; + this.buffered_atomic_rmw(val.to_scalar(), place, ord, old.to_scalar())?; interp_ok(old) } @@ -852,59 +866,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { interp_ok(old) } - /// Perform an conditional atomic exchange with a memory place and a new - /// scalar value, the old value is returned. - fn atomic_min_max_scalar( - &mut self, - place: &MPlaceTy<'tcx>, - rhs: ImmTy<'tcx>, - min: bool, - atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx>> { - let this = self.eval_context_mut(); - this.atomic_access_check(place, AtomicAccessType::Rmw)?; - - let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; - - // Inform GenMC about the atomic min/max operation. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let (old_val, new_val) = genmc_ctx.atomic_min_max_op( - this, - place.ptr().addr(), - place.layout.size, - atomic, - min, - old.layout.backend_repr.is_signed(), - rhs.to_scalar(), - old.to_scalar(), - )?; - // The store might be the latest store in coherence order (determined by GenMC). - // If it is, we need to update the value in Miri's memory: - if let Some(new_val) = new_val { - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; - } - return interp_ok(ImmTy::from_scalar(old_val, old.layout)); - } - - let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?; - - #[rustfmt::skip] // rustfmt makes this unreadable - let new_val = if min { - if lt { &old } else { &rhs } - } else { - if lt { &rhs } else { &old } - }; - - this.allow_data_races_mut(|this| this.write_immediate(**new_val, place))?; - - this.validate_atomic_rmw(place, atomic)?; - - this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?; - - // Return the old value. - interp_ok(old) - } - /// Perform an atomic compare and exchange at a given memory location. /// On success an atomic RMW operation is performed and on failure /// only an atomic read occurs. If `can_fail_spuriously` is true, diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 1960ef37cc9f3..7c420961ec54a 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,8 +1,8 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; -use rustc_middle::mir; pub use self::run::run_genmc_mode; +use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, @@ -94,22 +94,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, - _ordering: AtomicRwOrd, - (_rmw_op, _not): (mir::BinOp, bool), - _rhs_scalar: Scalar, - _old_value: Scalar, - ) -> InterpResult<'tcx, (Scalar, Option)> { - unreachable!() - } - - pub(crate) fn atomic_min_max_op<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _ordering: AtomicRwOrd, - _min: bool, _is_signed: bool, + _ordering: AtomicRwOrd, + _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index b5d20a439c01c..26ce0b9c43ac7 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -7,7 +7,7 @@ use genmc_sys::{ }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; -use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; +use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; // FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). use tracing::{debug, info}; @@ -15,6 +15,7 @@ use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; +use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, @@ -298,8 +299,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, + _is_signed: bool, _ordering: AtomicRwOrd, - (_rmw_op, _not): (mir::BinOp, bool), + _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { @@ -310,29 +312,6 @@ impl GenmcCtx { throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") } - /// Inform GenMC about an atomic `min` or `max` operation. - /// - /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. - /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. - pub(crate) fn atomic_min_max_op<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _ordering: AtomicRwOrd, - _min: bool, - _is_signed: bool, - _rhs_scalar: Scalar, - _old_value: Scalar, - ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic min/max operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic min/max.") - } - /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. /// /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index e634125292754..c9f63640b8541 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -5,10 +5,13 @@ use rustc_middle::{mir, ty}; use super::check_intrinsic_arg_count; use crate::*; -pub enum AtomicOp { - /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`, - /// must be a boolean-typed operation). - MirOp(mir::BinOp, bool), +pub enum AtomicRmwOp { + MirOp { + op: mir::BinOp, + /// Indicates whether the result of the operation should be negated (`UnOp::Not`, must be a + /// boolean-typed operation). + neg: bool, + }, Max, Min, } @@ -106,55 +109,85 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "or" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitOr, neg: false }, + rw_ord(ord), + )?; } "xor" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitXor, neg: false }, + rw_ord(ord), + )?; } "and" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: false }, + rw_ord(ord), + )?; } "nand" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: true }, + rw_ord(ord), + )?; } "xadd" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::Add, neg: false }, + rw_ord(ord), + )?; } "xsub" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::Sub, neg: false }, + rw_ord(ord), + )?; } "min" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?; } "umin" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?; } "max" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?; } "umax" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?; } _ => return interp_ok(EmulateItemResult::NotSupported), @@ -222,8 +255,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - atomic_op: AtomicOp, - atomic: AtomicRwOrd, + atomic_op: AtomicRmwOp, + ord: AtomicRwOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -240,14 +273,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { ); } - let old = match atomic_op { - AtomicOp::Min => - this.atomic_min_max_scalar(&place, rhs, /* min */ true, atomic)?, - AtomicOp::Max => - this.atomic_min_max_scalar(&place, rhs, /* min */ false, atomic)?, - AtomicOp::MirOp(op, not) => - this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?, - }; + let old = this.atomic_rmw_op_immediate(&place, &rhs, atomic_op, ord)?; this.write_immediate(*old, dest)?; // old value is returned interp_ok(()) } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 5e46768b0e6cf..f6f9af47d0a74 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -3,6 +3,9 @@ mod atomic; mod simd; +pub use self::atomic::AtomicRmwOp; + +#[rustfmt::skip] // prevent `use` reordering use std::ops::Neg; use rand::Rng; From 69c7652a01a721be007cb9f7ef56f1e483f39fc8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 13:31:01 +0200 Subject: [PATCH 0220/1900] atomic rmw intrinsics: RHS must be an integer --- src/tools/miri/src/intrinsics/atomic.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index c9f63640b8541..9bb0ab70de236 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -264,8 +264,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let place = this.deref_pointer(place)?; let rhs = this.read_immediate(rhs)?; + // The LHS can be a pointer, the RHS must be an integer. if !(place.layout.ty.is_integral() || place.layout.ty.is_raw_ptr()) - || !(rhs.layout.ty.is_integral() || rhs.layout.ty.is_raw_ptr()) + || !rhs.layout.ty.is_integral() { span_bug!( this.cur_span(), From d4cb0b475935d29c18f94676c2d0e9793e95b8e3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Sep 2025 22:32:11 +0200 Subject: [PATCH 0221/1900] clean-up a bit - if-else reordering: having one fewer nesting levels makes it easier to understand imo --- .../src/transmute/transmute_ptr_to_ref.rs | 20 ++++++++----------- tests/ui/transmute_ptr_to_ref.fixed | 10 +++++----- tests/ui/transmute_ptr_to_ref.rs | 10 +++++----- tests/ui/transmute_ptr_to_ref.stderr | 4 ++-- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index e58212fae15cf..44fa2121341dd 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -28,10 +28,9 @@ pub(super) fn check<'tcx>( format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); - let (deref, cast) = if *mutbl == Mutability::Mut { - ("&mut *", "*mut") - } else { - ("&*", "*const") + let (deref, cast) = match mutbl { + Mutability::Mut => ("&mut *", "*mut"), + Mutability::Not => ("&*", "*const"), }; let mut app = Applicability::MachineApplicable; @@ -45,15 +44,12 @@ pub(super) fn check<'tcx>( sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if *from_ptr_ty == *to_ref_ty { - if from_ptr_ty.has_erased_regions() { - if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) - } else { - sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) - .to_string() - } - } else { + if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) + } else { + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))).to_string() } } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 61e3ac2fe88e3..68839fd04c56b 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = &*p; //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => &*x.cast::<&u32>(), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index 48e2f527b554c..b41b7cd217947 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = std::mem::transmute(p); //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => std::mem::transmute(x), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 7685c345c8619..520f804d264a2 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -43,13 +43,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, u8>`) --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, &u8>`) --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; From 07d3e923ac68ba6daf516f4298ae6b00471696f1 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 3 Sep 2025 20:10:43 -0400 Subject: [PATCH 0222/1900] Unstably constify ptr::drop_in_place and related methods --- library/core/src/mem/manually_drop.rs | 7 +++- library/core/src/mem/maybe_uninit.rs | 13 +++++- library/core/src/ptr/mod.rs | 8 +++- library/core/src/ptr/mut_ptr.rs | 8 +++- library/core/src/ptr/non_null.rs | 8 +++- library/coretests/tests/lib.rs | 1 + library/coretests/tests/ptr.rs | 41 ++++++++++++++++++- .../drop_in_place_retag.stderr | 6 ++- .../unaligned_pointers/drop_in_place.stderr | 6 ++- 9 files changed, 84 insertions(+), 14 deletions(-) diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 8868f05f1b98f..334a4b7119a11 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -1,3 +1,4 @@ +use crate::marker::Destruct; use crate::ops::{Deref, DerefMut, DerefPure}; use crate::ptr; @@ -249,7 +250,11 @@ impl ManuallyDrop { /// [pinned]: crate::pin #[stable(feature = "manually_drop", since = "1.20.0")] #[inline] - pub unsafe fn drop(slot: &mut ManuallyDrop) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop(slot: &mut ManuallyDrop) + where + T: [const] Destruct, + { // SAFETY: we are dropping the value pointed to by a mutable reference // which is guaranteed to be valid for writes. // It is up to the caller to make sure that `slot` isn't dropped again. diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c160360cfacf9..8d666e9b130c0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,4 +1,5 @@ use crate::any::type_name; +use crate::marker::Destruct; use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; @@ -714,7 +715,11 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. // Dropping the value in place is safe if that is the case. @@ -1390,7 +1395,11 @@ impl [MaybeUninit] { /// behaviour. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { if !self.is_empty() { // SAFETY: the caller must guarantee that every element of `self` // is initialized and satisfies all invariants of `T`. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b94088cb5679..8c20b5e58b8f2 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -403,7 +403,7 @@ use crate::cmp::Ordering; use crate::intrinsics::const_eval_select; -use crate::marker::{FnPtr, PointeeSized}; +use crate::marker::{Destruct, FnPtr, PointeeSized}; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::{fmt, hash, intrinsics, ub_checks}; @@ -801,7 +801,11 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { #[lang = "drop_in_place"] #[allow(unconditional_recursion)] #[rustc_diagnostic_item = "ptr_drop_in_place"] -pub unsafe fn drop_in_place(to_drop: *mut T) { +#[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] +pub const unsafe fn drop_in_place(to_drop: *mut T) +where + T: [const] Destruct, +{ // Code here does not matter - this is replaced by the // real drop glue by the compiler. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ce6eee4f911ed..1e3bc2939320d 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; use crate::intrinsics::const_eval_select; -use crate::marker::PointeeSized; +use crate::marker::{Destruct, PointeeSized}; use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; @@ -1438,8 +1438,12 @@ impl *mut T { /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] #[inline(always)] - pub unsafe fn drop_in_place(self) { + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { drop_in_place(self) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 10f83120428b9..a762e969b52dc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,5 +1,5 @@ use crate::cmp::Ordering; -use crate::marker::{PointeeSized, Unsize}; +use crate::marker::{Destruct, PointeeSized, Unsize}; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; @@ -1118,7 +1118,11 @@ impl NonNull { /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - pub unsafe fn drop_in_place(self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b5658a9970fee..c8b940cf3728b 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -17,6 +17,7 @@ #![feature(clone_to_uninit)] #![feature(const_convert)] #![feature(const_destruct)] +#![feature(const_drop_in_place)] #![feature(const_eval_select)] #![feature(const_ops)] #![feature(const_option_ops)] diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index c13fb96a67f92..217e370ee8974 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::marker::Freeze; -use core::mem::MaybeUninit; +use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZero; use core::ptr; use core::ptr::*; @@ -1036,3 +1036,42 @@ fn test_ptr_default() { let default = PtrMutDefaultTest::default(); assert!(default.ptr.is_null()); } + +#[test] +fn test_const_drop_in_place() { + const COUNTER: usize = { + let mut counter = 0; + let counter_ptr = &raw mut counter; + + // only exists to make `Drop` indirect impl + #[allow(dead_code)] + struct Test(Dropped); + + struct Dropped(*mut usize); + impl const Drop for Dropped { + fn drop(&mut self) { + unsafe { + *self.0 += 1; + } + } + } + + let mut one = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut two = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut three = ManuallyDrop::new(Test(Dropped(counter_ptr))); + assert!(counter == 0); + unsafe { + ManuallyDrop::drop(&mut one); + } + assert!(counter == 1); + unsafe { + ManuallyDrop::drop(&mut two); + } + assert!(counter == 2); + unsafe { + ManuallyDrop::drop(&mut three); + } + counter + }; + assert_eq!(COUNTER, 3); +} diff --git a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr index ec578db2d0a8e..e586d3f03994f 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: trying to retag from for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of retag at ALLOC[0x0..0x1] +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ this error occurs as part of retag at ALLOC[0x0..0x1] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr index 72aeb0fdf8e49..ea144f206643a 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information From 0dc233fc56a1078c3eb57af9cd8460e2b1763f10 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 6 Sep 2025 06:26:08 +0200 Subject: [PATCH 0223/1900] point to Compiler team docs on Forge --- src/doc/rustc-dev-guide/src/compiler-team.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 6be52833f3964..896d9e6f6d94c 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -1,10 +1,14 @@ # About the compiler team +> NOTE: +> There exists much detail about the team [on Forge], making most of the following obsolete. + rustc is maintained by the [Rust compiler team][team]. The people who belong to this team collectively work to track regressions and implement new features. Members of the Rust compiler team are people who have made significant contributions to rustc and its design. +[on Forge]: https://forge.rust-lang.org/compiler [team]: https://www.rust-lang.org/governance/teams/compiler ## Discussion From a5bb60b19c1113a751c7dac00ea8ae0ac20cd15f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 6 Sep 2025 13:07:53 +0800 Subject: [PATCH 0224/1900] Add allow `else` keyword completion in LetStmt Example --- ```rust fn foo() { let _ = 2 el$0 } ``` -> ```rust fn foo() { let _ = 2 else { $0 }; } ``` --- .../ide-completion/src/completions/expr.rs | 6 +- .../ide-completion/src/completions/keyword.rs | 40 +++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 41 +++-- .../ide-completion/src/tests/expression.rs | 149 ++++++++++++++++++ 5 files changed, 220 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 1972f166134a4..2c7d9e97c44e7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path( after_if_expr, in_condition, incomplete_let, + after_incomplete_let, in_value, ref ref_expr_parent, after_amp, @@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path( add_keyword("let", "let $1 = $0;"); } - if after_if_expr { + if after_if_expr || after_incomplete_let { add_keyword("else", "else {\n $0\n}"); + } + + if after_if_expr { add_keyword("else if", "else if $1 {\n $0\n}"); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index aea4e119f2076..b3d770997ab0c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -247,6 +247,46 @@ fn main() { "#, ); + check_edit( + "else", + r#" +fn main() { + let x = if true { + () + } $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = if true { + () + } else { + $0 +}; + let y = 92; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let x = 2 $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = 2 else { + $0 +}; + let y = 92; +} +"#, + ); + check_edit( "loop", r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 265462d220169..007475688d209 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, + pub(crate) after_incomplete_let: bool, pub(crate) in_value: bool, pub(crate) ref_expr_parent: Option, pub(crate) after_amp: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 17978b4c10798..33b98a33cabf6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -947,25 +947,29 @@ fn classify_name_ref<'db>( None } }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = (|| { - let node = match node.parent().and_then(ast::ExprStmt::cast) { - Some(stmt) => stmt.syntax().clone(), - None => node, - }; - let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; + let prev_expr = |node: SyntaxNode| { + let node = match node.parent().and_then(ast::ExprStmt::cast) { + Some(stmt) => stmt.syntax().clone(), + None => node, + }; + let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - match_ast! { - match prev_sibling { - ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), - ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), - ast::Expr(expr) => Some(expr), - _ => None, - } + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, } - })(); + } + }; + let after_if_expr = |node: SyntaxNode| { + let prev_expr = prev_expr(node); matches!(prev_expr, Some(ast::Expr::IfExpr(_))) }; + let after_incomplete_let = |node: SyntaxNode| { + prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>( }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let after_incomplete_let = after_incomplete_let(it.clone()).is_some(); + let incomplete_expr_stmt = + it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none()); let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .is_some_and(|it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()) + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true); let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); @@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>( self_param, in_value, incomplete_let, + after_incomplete_let, impl_, in_match_guard, }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7a0d00444129f..56fbd91a60fcc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -450,6 +450,155 @@ fn completes_in_let_initializer() { ) } +#[test] +fn completes_let_else() { + check( + r#"fn main() { let _ = 2 $0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + + check( + r#"fn main() { let _ = 2 el$0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 $0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 $0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); +} + #[test] fn completes_after_ref_expr() { check( From 1f8667982014babf392f08d45bb6d7bb1bc27d94 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Sep 2025 23:12:56 +0200 Subject: [PATCH 0225/1900] fix(transmute_ptr_to_ref): don't suggest `.cast` when to-type is DST --- .../src/transmute/transmute_ptr_to_ref.rs | 24 ++++++++- tests/ui/transmute_ptr_to_ref.fixed | 29 +++++++++++ tests/ui/transmute_ptr_to_ref.rs | 29 +++++++++++ tests/ui/transmute_ptr_to_ref.stderr | 50 ++++++++++++++++++- 4 files changed, 130 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 44fa2121341dd..e67ab6a73d269 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -36,7 +36,19 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if msrv.meets(cx, msrvs::POINTER_CAST) { + if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + if from_ptr_ty.has_erased_regions() { + // We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe + // to do because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + } + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,6 +58,16 @@ pub(super) fn check<'tcx>( } else if *from_ptr_ty == *to_ref_ty { if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // 1. We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + // 2. We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe to do + // because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) } else { diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 68839fd04c56b..c130575df9607 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -106,4 +106,33 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + let _: &[u32] = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = &*(ptr as *const [i32]); + //~^ transmute_ptr_to_ref + let _: &[i32] = &*ptr; + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + let _: &[&str] = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index b41b7cd217947..f79d54234a2c9 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -106,4 +106,33 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = core::mem::transmute::<_, &[u32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[u32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = core::mem::transmute::<_, &[i32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[i32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&str] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 520f804d264a2..3f404d295fef0 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -133,5 +133,53 @@ error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32 LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` -error: aborting due to 22 previous errors +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:113:17 + | +LL | let _ = core::mem::transmute::<_, &[u32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:115:25 + | +LL | let _: &[u32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:119:17 + | +LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:121:27 + | +LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:125:17 + | +LL | let _ = core::mem::transmute::<_, &[i32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:127:25 + | +LL | let _: &[i32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:131:17 + | +LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:133:26 + | +LL | let _: &[&str] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: aborting due to 30 previous errors From 4ae4f67e8c4c41b1c46d021be63ddc645d36154a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 11:24:36 +0200 Subject: [PATCH 0226/1900] clean-up a bit Notably, rename `same_type_and_consts` to `same_type_modulo_regions` to be more explicit about the function ignoring lifetimes --- clippy_lints/src/matches/needless_match.rs | 6 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/use_self.rs | 77 ++++++++++------------ clippy_lints/src/useless_conversion.rs | 14 ++-- clippy_utils/src/ty/mod.rs | 32 +++++---- 5 files changed, 64 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index b04db03f8d2e7..3a2097c3df261 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,7 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> // Compare match_expr ty with local in `let local = match match_expr {..}` Node::LetStmt(local) => { let results = cx.typeck_results(); - return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> .instantiate_identity() .output() .skip_binder(); - return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); + return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index e3fc8d8fea7d1..8f5ee390f7222 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::same_type_and_consts; +use clippy_utils::ty::same_type_modulo_regions; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { let input_ty = tck.expr_ty(input).peel_refs(); let output_ty = tck.expr_ty(output).peel_refs(); - !same_type_and_consts(input_ty, output_ty) + !same_type_modulo_regions(input_ty, output_ty) } fn check_op<'tcx>( diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aeda864b7eb5e..2679eeba87850 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,20 +2,21 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty}; +use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, + self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let types_to_skip = generics .params .iter() - .filter_map(|param| match param { - GenericParam { - kind: - GenericParamKind::Const { - ty: Ty { hir_id, .. }, .. - }, - .. - } => Some(*hir_id), + .filter_map(|param| match param.kind { + GenericParamKind::Const { ty, .. } => Some(ty.hir_id), _ => None, }) - .chain(std::iter::once(self_ty.hir_id)) + .chain([self_ty.hir_id]) .collect(); StackItem::Check { impl_id: item.owner_id.def_id, @@ -210,11 +205,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && !types_to_skip.contains(&hir_ty.hir_id) && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() - && same_type_and_consts(ty, impl_ty) + && same_type_modulo_regions(ty, impl_ty) // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that - // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && (!has_lifetime(ty) || same_lifetimes(ty, impl_ty)) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -227,18 +222,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { - } else { - return; - } - match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), - ExprKind::Call(fun, _) => { - if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - check_path(cx, path); - } - }, - ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), - _ => (), + match expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), + ExprKind::Call(fun, _) => { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { + check_path(cx, path); + } + }, + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), + _ => (), + } } } @@ -308,36 +301,32 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { } } -/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns -/// `false`. +/// Checks whether types `a` and `b` have the same lifetime parameters. /// /// This function does not check that types `a` and `b` are the same types. fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; - match (&a.kind(), &b.kind()) { - (&Adt(_, args_a), &Adt(_, args_b)) => { - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - // TODO: Handle inferred lifetimes - (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), - _ => true, - }) + match (a.kind(), b.kind()) { + (Adt(_, args_a), Adt(_, args_b)) => { + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) }, _ => a == b, } } -/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. -fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { +/// Checks whether `ty` has lifetime parameters. +fn has_lifetime(ty: MiddleTy<'_>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; match ty.kind() { - &Adt(_, args) => !args + Adt(_, args) => args .iter() // TODO: Handle inferred lifetimes .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), - _ => true, + _ => false, } } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 70ae982a4458b..5274fdbd1dc21 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() - && same_type_and_consts(from_ty, to_ty) + && same_type_modulo_regions(from_ty, to_ty) { span_lint_and_then( cx, @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + if same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut app).0; span_lint_and_sugg( @@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // If the types are identical then .into_iter() can be removed, unless the type // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. - if same_type_and_consts(a, b) && !is_copy(cx, b) { + if same_type_modulo_regions(a, b) && !is_copy(cx, b) { // Below we check if the parent method call meets the following conditions: // 1. First parameter is `&mut self` (requires mutable reference) // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) @@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { span_lint_and_help( cx, @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( @@ -407,7 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } else if name == sym::from_fn && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad12..8fbaac58288f0 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -513,25 +513,31 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { inner(ty, 0) } -/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, -/// otherwise returns `false` -pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores +/// lifetimes. +/// +/// For example, the function would return `true` for +/// - `u32` and `u32` +/// - `[u8; N]` and `[u8; M]`, if `N=M` +/// - `Option` and `Option`, if `same_type_modulo_regions(T, U)` holds +/// - `&'a str` and `&'b str` +/// +/// and `false` for: +/// - `Result` and `Result` +pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { - same_type_and_consts(type_a, type_b) - }, - _ => true, - }) + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_modulo_regions(type_a, type_b) + }, + _ => true, + }) }, _ => a == b, } From 6812aa3403cc6b8c5a229fc36f0022b06c276e3c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 12:27:19 +0200 Subject: [PATCH 0227/1900] fix(use_self): descend into type's children when looking for lifetimes This unfortunately breaks another test case -- it had only worked because when we checked `!has_lifetime` on `S2>`, we only looked for generic lifetime params on `S2`, forgetting to descend into `S`. One thing that makes this a bit less sad is that this particular test case doesn't come from a reported issue, but rather was added as a miscellaneous check during a kind of unrelated PR. --- clippy_lints/src/use_self.rs | 8 +++++--- tests/ui/use_self.fixed | 18 ++++++++++++++++-- tests/ui/use_self.rs | 16 +++++++++++++++- tests/ui/use_self.stderr | 8 +------- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2679eeba87850..e84641d4d1b97 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -323,10 +323,12 @@ fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { fn has_lifetime(ty: MiddleTy<'_>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; match ty.kind() { - Adt(_, args) => args - .iter() + Adt(_, args) => args.iter().any(|arg| match arg.kind() { // TODO: Handle inferred lifetimes - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), + GenericArgKind::Lifetime(..) => true, + GenericArgKind::Type(ty) => has_lifetime(ty), + _ => false, + }), _ => false, } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index cccb6bffabb71..075e31d202b06 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -530,8 +530,8 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { - Self::new() - //~^ use_self + S2::new() + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 09288677aa715..6fbba0bbc550b 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -531,7 +531,7 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { S2::new() - //~^ use_self + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 781327696ac19..5f65c53ea25c3 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -169,12 +169,6 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> tests/ui/use_self.rs:533:13 - | -LL | S2::new() - | ^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> tests/ui/use_self.rs:571:17 | @@ -259,5 +253,5 @@ error: unnecessary structure name repetition LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 43 previous errors +error: aborting due to 42 previous errors From e7c91ab13ad36e3dfefc4f4f92f4c6e5e445c8f6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 20:49:26 +0200 Subject: [PATCH 0228/1900] remove `has_lifetime` It's now redundant to `same_lifetimes`: if `ty` and `impl_ty` are the same type, and they both have no lifetimes, `same_lifetimes` will return `true` already --- clippy_lints/src/use_self.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e84641d4d1b97..8210320591c62 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -209,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (!has_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && same_lifetimes(ty, impl_ty) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -318,17 +318,3 @@ fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { _ => a == b, } } - -/// Checks whether `ty` has lifetime parameters. -fn has_lifetime(ty: MiddleTy<'_>) -> bool { - use rustc_middle::ty::{Adt, GenericArgKind}; - match ty.kind() { - Adt(_, args) => args.iter().any(|arg| match arg.kind() { - // TODO: Handle inferred lifetimes - GenericArgKind::Lifetime(..) => true, - GenericArgKind::Type(ty) => has_lifetime(ty), - _ => false, - }), - _ => false, - } -} From 4fd9a063855302cf3276c2f6b9349e41d75adb1c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 6 Sep 2025 12:50:57 +0200 Subject: [PATCH 0229/1900] Move `derive.rs` to `derive/mod.rs` Will hopefully make the next commit's diff somewhat easier to understand --- clippy_lints/src/{derive.rs => derive/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{derive.rs => derive/mod.rs} (100%) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive/mod.rs similarity index 100% rename from clippy_lints/src/derive.rs rename to clippy_lints/src/derive/mod.rs From 8a11cf8d3a6366d4d5e2d52e94003018f7ef8181 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 6 Sep 2025 11:43:34 +0200 Subject: [PATCH 0230/1900] Split out each lint into a separate module Best viewed with `git diff --color-moved` --- .../src/derive/derive_ord_xor_partial_ord.rs | 50 +++ .../derive/derive_partial_eq_without_eq.rs | 87 +++++ .../src/derive/derived_hash_with_manual_eq.rs | 49 +++ .../src/derive/expl_impl_clone_on_copy.rs | 65 ++++ clippy_lints/src/derive/mod.rs | 336 +----------------- .../src/derive/unsafe_derive_deserialize.rs | 93 +++++ 6 files changed, 356 insertions(+), 324 deletions(-) create mode 100644 clippy_lints/src/derive/derive_ord_xor_partial_ord.rs create mode 100644 clippy_lints/src/derive/derive_partial_eq_without_eq.rs create mode 100644 clippy_lints/src/derive/derived_hash_with_manual_eq.rs create mode 100644 clippy_lints/src/derive/expl_impl_clone_on_copy.rs create mode 100644 clippy_lints/src/derive/unsafe_derive_deserialize.rs diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs new file mode 100644 index 0000000000000..cbbcb2f7a3bab --- /dev/null +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVE_ORD_XOR_PARTIAL_ORD; + +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); + } +} diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs new file mode 100644 index 0000000000000..ed7881c461ffc --- /dev/null +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::has_non_exhaustive_attr; +use clippy_utils::ty::implements_trait_with_env; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; +use rustc_span::{Span, sym}; + +use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) + && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) + && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let Some(local_def_id) = adt.did().as_local() + // If all of our fields implement `Eq`, we can implement `Eq` too + && adt + .all_fields() + .map(|f| f.ty(cx.tcx, args)) + .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) + { + span_lint_hir_and_then( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + cx.tcx.local_def_id_to_hir_id(local_def_id), + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + |diag| { + diag.span_suggestion( + span.ctxt().outer_expn_data().call_site, + "consider deriving `Eq` as well", + "PartialEq, Eq", + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { + tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() +} + +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. +fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .own_params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let ClauseKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + ClauseKind::Trait(TraitPredicate { + trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), + polarity: ty::PredicatePolarity::Positive, + }) + .upcast(tcx) + }), + ))); + ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env, + } +} diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs new file mode 100644 index 0000000000000..6f36a58025a2b --- /dev/null +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVED_HASH_WITH_MANUAL_EQ; + +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + hash_is_automatically_derived: bool, +) { + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); + } + }, + ); + } + }); + } +} diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs new file mode 100644 index 0000000000000..6b97b4bd6b4d3 --- /dev/null +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::{self as hir, Item}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Ty}; + +use super::EXPL_IMPL_CLONE_ON_COPY; + +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { + return; + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + if ty_adt.did() == adt.did()) + }); + if !has_copy_impl { + return; + } + } else { + return; + } + } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); +} diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index c53a957f6a8b7..1d63394ce37d4 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,20 +1,12 @@ -use std::ops::ControlFlow; - -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, -}; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; + +mod derive_ord_xor_partial_ord; +mod derive_partial_eq_without_eq; +mod derived_hash_with_manual_eq; +mod expl_impl_clone_on_copy; +mod unsafe_derive_deserialize; declare_clippy_lint! { /// ### What it does @@ -209,319 +201,15 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { - check_unsafe_derive_deserialize(cx, item, trait_ref, ty); - check_partial_eq_without_eq(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); } else { - check_copy_clone(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); } } } } - -/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. -fn check_hash_peq<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - hash_is_automatically_derived: bool, -) { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Hash, def_id) - { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); - } - }, - ); - } - }); - } -} - -/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_partial_ord<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - ord_is_automatically_derived: bool, -) { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() - && let Some(def_id) = &trait_ref.trait_def_id() - && *def_id == ord_trait_def_id - { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; - - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); - } - }); - } - }); - } -} - -/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - let clone_id = match cx.tcx.lang_items().clone_trait() { - Some(id) if trait_ref.trait_def_id() == Some(id) => id, - _ => return, - }; - let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { - return; - }; - let (ty_adt, ty_subs) = match *ty.kind() { - // Unions can't derive clone. - ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), - _ => return, - }; - // If the current self type doesn't implement Copy (due to generic constraints), search to see if - // there's a Copy impl for any instance of the adt. - if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) - if ty_adt.did() == adt.did()) - }); - if !has_copy_impl { - return; - } - } else { - return; - } - } - // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for - // this impl. - if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { - return; - } - // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. - // https://github.com/rust-lang/rust-clippy/issues/10188 - if ty_adt.repr().packed() - && ty_subs - .iter() - .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) - { - return; - } - // The presence of `unsafe` fields prevents deriving `Clone` automatically - if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { - return; - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); -} - -/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -fn check_unsafe_derive_deserialize<'tcx>( - cx: &LateContext<'tcx>, - item: &Item<'_>, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { - fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx }; - walk_item(&mut visitor, item).is_break() - } - - if let Some(trait_def_id) = trait_ref.trait_def_id() - && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) - && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) - && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) - && cx - .tcx - .inherent_impls(def.did()) - .iter() - .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)) - { - span_lint_hir_and_then( - cx, - UNSAFE_DERIVE_DESERIALIZE, - adt_hir_id, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - |diag| { - diag.help( - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", - ); - }, - ); - } -} - -struct UnsafeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = nested_filter::All; - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - _: Span, - id: LocalDefId, - ) -> Self::Result { - if let Some(header) = kind.header() - && header.is_unsafe() - { - ControlFlow::Break(()) - } else { - walk_fn(self, kind, decl, body_id, id) - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind - && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - && block - .span - .source_callee() - .and_then(|expr| expr.macro_def_id) - .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) - { - return ControlFlow::Break(()); - } - - walk_expr(self, expr) - } - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - -/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if let ty::Adt(adt, args) = ty.kind() - && cx.tcx.visibility(adt.did()).is_public() - && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) - && !has_non_exhaustive_attr(cx.tcx, *adt) - && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() - // If all of our fields implement `Eq`, we can implement `Eq` too - && adt - .all_fields() - .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) - { - span_lint_hir_and_then( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - |diag| { - diag.span_suggestion( - span.ctxt().outer_expn_data().call_site, - "consider deriving `Eq` as well", - "PartialEq, Eq", - Applicability::MachineApplicable, - ); - }, - ); - } -} - -fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { - tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() -} - -/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. -fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { - // Initial map from generic index to param def. - // Vec<(param_def, needs_eq)> - let mut params = tcx - .generics_of(did) - .own_params - .iter() - .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) - .collect::>(); - - let ty_predicates = tcx.predicates_of(did).predicates; - for (p, _) in ty_predicates { - if let ClauseKind::Trait(p) = p.kind().skip_binder() - && p.trait_ref.def_id == eq_trait_id - && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() - { - // Flag types which already have an `Eq` bound. - params[self_ty.index as usize].1 = false; - } - } - - let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( - params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - ClauseKind::Trait(TraitPredicate { - trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), - polarity: ty::PredicatePolarity::Positive, - }) - .upcast(tcx) - }), - ))); - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env, - } -} diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs new file mode 100644 index 0000000000000..c391e7b62289f --- /dev/null +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -0,0 +1,93 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::{is_lint_allowed, paths}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, sym}; + +use super::UNSAFE_DERIVE_DESERIALIZE; + +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() + } + + if let Some(trait_def_id) = trait_ref.trait_def_id() + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) + .iter() + .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_hir_and_then( + cx, + UNSAFE_DERIVE_DESERIALIZE, + adt_hir_id, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + |diag| { + diag.help( + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); + }, + ); + } +} + +struct UnsafeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, +} + +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + type NestedFilter = nested_filter::All; + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { + if let Some(header) = kind.header() + && header.is_unsafe() + { + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && block + .span + .source_callee() + .and_then(|expr| expr.macro_def_id) + .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) + { + return ControlFlow::Break(()); + } + + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} From 93101b5783b522469320b60926918f308ddae6b6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 14:32:36 +0200 Subject: [PATCH 0231/1900] s390x: use the new `u128::funnel_shl` --- library/stdarch/crates/core_arch/src/lib.rs | 3 ++- .../crates/core_arch/src/s390x/vector.rs | 17 +++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 7d3cbd55ad85c..26a9cb5899183 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -33,7 +33,8 @@ x86_amx_intrinsics, f16, aarch64_unstable_target_feature, - bigint_helper_methods + bigint_helper_methods, + funnel_shifts )] #![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))] #![deny(clippy::missing_inline_in_public_items)] diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index f6d14c45e0d2b..f018344ead12d 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -3513,17 +3513,6 @@ mod sealed { a.vec_srdb::<7>(b) } - unsafe fn funnel_shl_u128(a: u128, b: u128, c: u128) -> u128 { - #[repr(simd)] - struct Single([u128; 1]); - - transmute(simd_funnel_shl::( - transmute(a), - transmute(b), - transmute(c), - )) - } - macro_rules! impl_vec_sld { ($($ty:ident)*) => { $( @@ -3533,21 +3522,21 @@ mod sealed { #[target_feature(enable = "vector")] unsafe fn vec_sld(self, b: Self) -> Self { static_assert_uimm_bits!(C, 4); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 8 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C * 8)) } #[inline] #[target_feature(enable = "vector")] unsafe fn vec_sldw(self, b: Self) -> Self { static_assert_uimm_bits!(C, 2); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 4 * 8 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C * 4 * 8)) } #[inline] #[target_feature(enable = "vector-enhancements-2")] unsafe fn vec_sldb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C)) } } From 13ed4a381229235791618d091ec4d0c729a5b9fc Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 6 Sep 2025 15:45:09 +0000 Subject: [PATCH 0232/1900] uncommented u64 impl --- library/core/src/num/bignum.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index e33f58197bba5..1decbc2a04673 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -59,8 +59,7 @@ impl_full_ops! { u8: add(intrinsics::u8_add_with_overflow), mul/div(u16); u16: add(intrinsics::u16_add_with_overflow), mul/div(u32); u32: add(intrinsics::u32_add_with_overflow), mul/div(u64); - // See RFC #521 for enabling this. - // u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); + u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); } /// Table of powers of 5 representable in digits. Specifically, the largest {u8, u16, u32} value From 24c34d738f00f0f38b88cead5ab869d156513609 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 25 Aug 2025 17:20:37 +0200 Subject: [PATCH 0233/1900] extend the let-chain removes one level of nesting --- clippy_lints/src/future_not_send.rs | 107 ++++++++++++++-------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 3ccfa51ab70b7..2788b89f1439c 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -78,66 +78,65 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) - { - let preds = cx.tcx.explicit_item_self_bounds(def_id); - let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { + && let preds = cx.tcx.explicit_item_self_bounds(def_id) + // If is a Future + && preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { p.as_trait_clause() .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) - }); - if is_future { - let span = decl.output.span(); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, fn_def_id); - ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); + }) + { + let span = decl.output.span(); + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, fn_def_id); + ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); + let send_errors = ocx.select_all_or_error(); - // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top - // level". - // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, - // which is always unconditionally `!Send` for any possible type `T`. - // - // We also allow associated type projections if the self type is either itself a projection or a - // type parameter. - // This is to prevent emitting warnings for e.g. holding a `::Output` across await - // points, where `Fut` is a type parameter. + // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top + // level". + // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, + // which is always unconditionally `!Send` for any possible type `T`. + // + // We also allow associated type projections if the self type is either itself a projection or a + // type parameter. + // This is to prevent emitting warnings for e.g. holding a `::Output` across await + // points, where `Fut` is a type parameter. - let is_send = send_errors.iter().all(|err| { - err.obligation - .predicate - .as_trait_clause() - .map(Binder::skip_binder) - .is_some_and(|pred| { - pred.def_id() == send_trait - && pred.self_ty().has_param() - && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) - }) - }); + let is_send = send_errors.iter().all(|err| { + err.obligation + .predicate + .as_trait_clause() + .map(Binder::skip_binder) + .is_some_and(|pred| { + pred.def_id() == send_trait + && pred.self_ty().has_param() + && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) + }) + }); - if !is_send { - span_lint_and_then( - cx, - FUTURE_NOT_SEND, - span, - "future cannot be sent between threads safely", - |db| { - for FulfillmentError { obligation, .. } in send_errors { - infcx - .err_ctxt() - .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = - obligation.predicate.kind().skip_binder() - { - db.note(format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + if !is_send { + span_lint_and_then( + cx, + FUTURE_NOT_SEND, + span, + "future cannot be sent between threads safely", + |db| { + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { + db.note(format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }, - ); - } + } + }, + ); } } } From fe86ce6bcc07ff199c804a6a2e0a66c8557c5364 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 00:13:40 +0200 Subject: [PATCH 0234/1900] split `any` in two parts --- clippy_lints/src/future_not_send.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 2788b89f1439c..596047977a9b1 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) && let preds = cx.tcx.explicit_item_self_bounds(def_id) // If is a Future - && preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { - p.as_trait_clause() - .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) - }) + && preds + .iter_instantiated_copied(cx.tcx, args) + .filter_map(|(p, _)| p.as_trait_clause()) + .any(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) { let span = decl.output.span(); let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); From 589515bc8a1d4939c4a738e9bc895dcfb217d2d8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:37:15 +0200 Subject: [PATCH 0235/1900] update Cargo.lock --- library/stdarch/Cargo.lock | 217 ++++++++++++++++++++++++++----------- 1 file changed, 152 insertions(+), 65 deletions(-) diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index 9df0791b86523..a10a456acce1d 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -43,29 +43,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "assert-instr-macro" @@ -84,30 +84,31 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clap" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -115,9 +116,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -127,9 +128,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -258,6 +259,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "fnv" version = "1.0.7" @@ -283,9 +290,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -323,12 +330,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -353,7 +360,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -379,9 +386,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linked-hash-map" @@ -391,9 +398,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" @@ -428,9 +435,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -497,9 +504,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -507,9 +514,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -517,9 +524,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -529,9 +536,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -540,9 +547,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rustc-demangle" @@ -593,9 +600,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -715,9 +722,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -774,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags", - "indexmap 2.10.0", + "indexmap 2.11.0", "semver", ] @@ -791,20 +798,35 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -813,14 +835,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -829,48 +868,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -882,18 +969,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", From d70ef4f0a7468fe7fb2dfabf89d8ccbb956a45d9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:38:02 +0200 Subject: [PATCH 0236/1900] move `compare_outputs` implementation into `SupportedArchitectureTest` definition --- .../crates/intrinsic-test/src/arm/mod.rs | 35 +++++++------------ .../crates/intrinsic-test/src/common/mod.rs | 31 +++++++++++++--- .../stdarch/crates/intrinsic-test/src/main.rs | 21 ++++------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 51f5ac4283783..d8f7ae9b30610 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -10,7 +10,6 @@ use std::fs::{self, File}; use rayon::prelude::*; use crate::common::cli::ProcessedCli; -use crate::common::compare::compare_outputs; use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; use crate::common::gen_rust::{ compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, @@ -28,7 +27,17 @@ pub struct ArmArchitectureTest { } impl SupportedArchitectureTest for ArmArchitectureTest { - fn create(cli_options: ProcessedCli) -> Box { + type IntrinsicImpl = ArmIntrinsicType; + + fn cli_options(&self) -> &ProcessedCli { + &self.cli_options + } + + fn intrinsics(&self) -> &[Intrinsic] { + &self.intrinsics + } + + fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.contains("v7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) .expect("Error parsing input file"); @@ -50,10 +59,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .collect::>(); intrinsics.dedup(); - Box::new(Self { + Self { intrinsics, cli_options, - }) + } } fn build_c_file(&self) -> bool { @@ -177,22 +186,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { compile_rust_programs(toolchain, target, linker) } - - fn compare_outputs(&self) -> bool { - if self.cli_options.toolchain.is_some() { - let intrinsics_name_list = self - .intrinsics - .iter() - .map(|i| i.name.clone()) - .collect::>(); - - compare_outputs( - &intrinsics_name_list, - &self.cli_options.runner, - &self.cli_options.target, - ) - } else { - true - } - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 5a57c8027db9b..f1def1e9c0eb0 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,5 +1,7 @@ use cli::ProcessedCli; +use crate::common::{intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition}; + pub mod argument; pub mod cli; pub mod compare; @@ -15,12 +17,33 @@ pub mod values; /// Architectures must support this trait /// to be successfully tested. pub trait SupportedArchitectureTest { - fn create(cli_options: ProcessedCli) -> Box - where - Self: Sized; + type IntrinsicImpl: IntrinsicTypeDefinition; + + fn cli_options(&self) -> &ProcessedCli; + fn intrinsics(&self) -> &[Intrinsic]; + + fn create(cli_options: ProcessedCli) -> Self; + fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; - fn compare_outputs(&self) -> bool; + + fn compare_outputs(&self) -> bool { + if self.cli_options().toolchain.is_some() { + let intrinsics_name_list = self + .intrinsics() + .iter() + .map(|i| i.name.clone()) + .collect::>(); + + compare::compare_outputs( + &intrinsics_name_list, + &self.cli_options().runner, + &self.cli_options().target, + ) + } else { + true + } + } } pub fn chunk_info(intrinsic_count: usize) -> (usize, usize) { diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 538f317a2978b..44d7aafd827fe 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -13,23 +13,16 @@ fn main() { let args: Cli = clap::Parser::parse(); let processed_cli_options = ProcessedCli::new(args); - let test_environment_result: Option> = - match processed_cli_options.target.as_str() { - "aarch64-unknown-linux-gnu" - | "armv7-unknown-linux-gnueabihf" - | "aarch64_be-unknown-linux-gnu" => { - Some(ArmArchitectureTest::create(processed_cli_options)) - } + match processed_cli_options.target.as_str() { + "aarch64-unknown-linux-gnu" + | "armv7-unknown-linux-gnueabihf" + | "aarch64_be-unknown-linux-gnu" => run(ArmArchitectureTest::create(processed_cli_options)), - _ => None, - }; - - if test_environment_result.is_none() { - std::process::exit(0); + _ => std::process::exit(0), } +} - let test_environment = test_environment_result.unwrap(); - +fn run(test_environment: impl SupportedArchitectureTest) { info!("building C binaries"); if !test_environment.build_c_file() { std::process::exit(2); From 6ab097b2453c5247f8d78c7ed5dfd4184b9a1c1d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:42:48 +0200 Subject: [PATCH 0237/1900] move platform headers into `SupportedArchitectureTest` --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 5 +++-- library/stdarch/crates/intrinsic-test/src/common/mod.rs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index d8f7ae9b30610..60e1ebe5f33ba 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -65,9 +65,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } } + const PLATFORM_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + fn build_c_file(&self) -> bool { let c_target = "aarch64"; - let platform_headers = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); @@ -81,7 +82,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .map(|(i, chunk)| { let c_filename = format!("c_programs/mod_{i}.cpp"); let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp(&mut file, notice, c_target, platform_headers, chunk).unwrap(); + write_mod_cpp(&mut file, notice, c_target, Self::PLATFORM_HEADERS, chunk).unwrap(); // compile this cpp file into a .o file. // diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index f1def1e9c0eb0..be08aaecaba19 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -24,6 +24,8 @@ pub trait SupportedArchitectureTest { fn create(cli_options: ProcessedCli) -> Self; + const PLATFORM_HEADERS: &[&str]; + fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; From 916424f38d1b3d763cf19c0392799d7208482573 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:16:40 +0200 Subject: [PATCH 0238/1900] move more constants into `SupportedArchitectureTest` --- .../crates/intrinsic-test/src/arm/config.rs | 13 ++--- .../crates/intrinsic-test/src/arm/mod.rs | 52 +++++++++++++------ .../crates/intrinsic-test/src/common/mod.rs | 16 ++++-- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs index 9a7b37253d1cb..beaca621378d4 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs @@ -1,12 +1,7 @@ -pub fn build_notices(line_prefix: &str) -> String { - format!( - "\ -{line_prefix}This is a transient test file, not intended for distribution. Some aspects of the -{line_prefix}test are derived from a JSON specification, published under the same license as the -{line_prefix}`intrinsic-test` crate.\n -" - ) -} +pub const NOTICE: &str = "\ +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from a JSON specification, published under the same license as the +// `intrinsic-test` crate.\n"; pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& os, poly128_t value) { std::stringstream temp; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 60e1ebe5f33ba..82dc5001adf06 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -5,11 +5,12 @@ mod intrinsic; mod json_parser; mod types; -use std::fs::{self, File}; +use std::fs::File; use rayon::prelude::*; use crate::common::cli::ProcessedCli; +use crate::common::compile_c::CppCompilation; use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; use crate::common::gen_rust::{ compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, @@ -17,7 +18,6 @@ use crate::common::gen_rust::{ use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use crate::common::{SupportedArchitectureTest, chunk_info}; -use config::{AARCH_CONFIGURATIONS, F16_FORMATTING_DEF, POLY128_OSTREAM_DEF, build_notices}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -65,24 +65,40 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } } - const PLATFORM_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const NOTICE: &str = config::NOTICE; + + const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; + + const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; + const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; + + fn cpp_compilation(&self) -> Option { + compile::build_cpp_compilation(&self.cli_options) + } fn build_c_file(&self) -> bool { let c_target = "aarch64"; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); - let cpp_compiler_wrapped = compile::build_cpp_compilation(&self.cli_options); + let cpp_compiler_wrapped = self.cpp_compilation(); - let notice = &build_notices("// "); - fs::create_dir_all("c_programs").unwrap(); - self.intrinsics + std::fs::create_dir_all("c_programs").unwrap(); + self.intrinsics() .par_chunks(chunk_size) .enumerate() .map(|(i, chunk)| { let c_filename = format!("c_programs/mod_{i}.cpp"); let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp(&mut file, notice, c_target, Self::PLATFORM_HEADERS, chunk).unwrap(); + write_mod_cpp( + &mut file, + Self::NOTICE, + c_target, + Self::PLATFORM_C_HEADERS, + chunk, + ) + .unwrap(); // compile this cpp file into a .o file. // @@ -103,8 +119,8 @@ impl SupportedArchitectureTest for ArmArchitectureTest { write_main_cpp( &mut file, c_target, - POLY128_OSTREAM_DEF, - self.intrinsics.iter().map(|i| i.name.as_str()), + Self::PLATFORM_C_DEFINITIONS, + self.intrinsics().iter().map(|i| i.name.as_str()), ) .unwrap(); @@ -149,7 +165,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { write_main_rs( &mut main_rs, chunk_count, - AARCH_CONFIGURATIONS, + Self::PLATFORM_RUST_CFGS, "", self.intrinsics.iter().map(|i| i.name.as_str()), ) @@ -159,7 +175,6 @@ impl SupportedArchitectureTest for ArmArchitectureTest { let toolchain = self.cli_options.toolchain.as_deref(); let linker = self.cli_options.linker.as_deref(); - let notice = &build_notices("// "); self.intrinsics .par_chunks(chunk_size) .enumerate() @@ -170,9 +185,14 @@ impl SupportedArchitectureTest for ArmArchitectureTest { trace!("generating `{rust_filename}`"); let mut file = File::create(rust_filename)?; - let cfg = AARCH_CONFIGURATIONS; - let definitions = F16_FORMATTING_DEF; - write_lib_rs(&mut file, architecture, notice, cfg, definitions, chunk)?; + write_lib_rs( + &mut file, + architecture, + Self::NOTICE, + Self::PLATFORM_RUST_CFGS, + Self::PLATFORM_RUST_DEFINITIONS, + chunk, + )?; let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); trace!("generating `{toml_filename}`"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index be08aaecaba19..13e6c9fe9e28e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,6 +1,8 @@ use cli::ProcessedCli; -use crate::common::{intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition}; +use crate::common::{ + compile_c::CppCompilation, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, +}; pub mod argument; pub mod cli; @@ -17,14 +19,22 @@ pub mod values; /// Architectures must support this trait /// to be successfully tested. pub trait SupportedArchitectureTest { - type IntrinsicImpl: IntrinsicTypeDefinition; + type IntrinsicImpl: IntrinsicTypeDefinition + Sync; fn cli_options(&self) -> &ProcessedCli; fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: ProcessedCli) -> Self; - const PLATFORM_HEADERS: &[&str]; + const NOTICE: &str; + + const PLATFORM_C_HEADERS: &[&str]; + const PLATFORM_C_DEFINITIONS: &str; + + const PLATFORM_RUST_CFGS: &str; + const PLATFORM_RUST_DEFINITIONS: &str; + + fn cpp_compilation(&self) -> Option; fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; From 9d9ca01bfa541e9badd9f34c776b588cfbf0e3ce Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:23:24 +0200 Subject: [PATCH 0239/1900] move `print_result_c` into the inner intrinsic type --- .../intrinsic-test/src/arm/intrinsic.rs | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index fd93eff76e09c..7c31fd4d9dd0a 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -41,18 +41,27 @@ impl IntrinsicDefinition for Intrinsic { /// rust debug output format for the return type. The generated line assumes /// there is an int i in scope which is the current pass number. fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - let lanes = if self.results().num_vectors() > 1 { - (0..self.results().num_vectors()) + self.results().print_result_c(indentation, additional) + } +} + +impl ArmIntrinsicType { + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { + let lanes = if self.num_vectors() > 1 { + (0..self.num_vectors()) .map(|vector| { format!( r#""{ty}(" << {lanes} << ")""#, - ty = self.results().c_single_vector_type(), - lanes = (0..self.results().num_lanes()) + ty = self.c_single_vector_type(), + lanes = (0..self.num_lanes()) .map(move |idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", - cast = self.results().c_promotion(), - lane_fn = self.results().get_lane_function(), + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), lane = idx, vector = vector, ) @@ -63,13 +72,13 @@ impl IntrinsicDefinition for Intrinsic { }) .collect::>() .join(r#" << ", " << "#) - } else if self.results().num_lanes() > 1 { - (0..self.results().num_lanes()) + } else if self.num_lanes() > 1 { + (0..self.num_lanes()) .map(|idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value, {lane})", - cast = self.results().c_promotion(), - lane_fn = self.results().get_lane_function(), + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), lane = idx ) }) @@ -78,28 +87,27 @@ impl IntrinsicDefinition for Intrinsic { } else { format!( "{promote}cast<{cast}>(__return_value)", - cast = match self.results.kind() { - TypeKind::Float if self.results().inner_size() == 16 => "float16_t".to_string(), - TypeKind::Float if self.results().inner_size() == 32 => "float".to_string(), - TypeKind::Float if self.results().inner_size() == 64 => "double".to_string(), - TypeKind::Int(Sign::Signed) => format!("int{}_t", self.results().inner_size()), - TypeKind::Int(Sign::Unsigned) => - format!("uint{}_t", self.results().inner_size()), - TypeKind::Poly => format!("poly{}_t", self.results().inner_size()), + cast = match self.kind() { + TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), + TypeKind::Float if self.inner_size() == 32 => "float".to_string(), + TypeKind::Float if self.inner_size() == 64 => "double".to_string(), + TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), + TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), + TypeKind::Poly => format!("poly{}_t", self.inner_size()), ty => todo!("print_result_c - Unknown type: {:#?}", ty), }, - promote = self.results().c_promotion(), + promote = self.c_promotion(), ) }; format!( r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, - ty = if self.results().is_simd() { - format!("{}(", self.results().c_type()) + ty = if self.is_simd() { + format!("{}(", self.c_type()) } else { String::from("") }, - close = if self.results.is_simd() { ")" } else { "" }, + close = if self.is_simd() { ")" } else { "" }, ) } } From 2ba0a6e48974e3f358c18724ebf673787d72ceea Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:26:50 +0200 Subject: [PATCH 0240/1900] move `print_result_c` into the trait --- .../intrinsic-test/src/arm/intrinsic.rs | 77 +------------------ .../crates/intrinsic-test/src/arm/types.rs | 66 ++++++++++++++++ .../crates/intrinsic-test/src/common/gen_c.rs | 4 +- .../intrinsic-test/src/common/intrinsic.rs | 6 -- .../src/common/intrinsic_helpers.rs | 5 ++ 5 files changed, 75 insertions(+), 83 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 7c31fd4d9dd0a..1928a00f4f382 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -1,7 +1,6 @@ use crate::common::argument::ArgumentList; -use crate::common::indentation::Indentation; use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] @@ -36,78 +35,4 @@ impl IntrinsicDefinition for Intrinsic { fn name(&self) -> String { self.name.clone() } - - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - self.results().print_result_c(indentation, additional) - } -} - -impl ArmIntrinsicType { - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - let lanes = if self.num_vectors() > 1 { - (0..self.num_vectors()) - .map(|vector| { - format!( - r#""{ty}(" << {lanes} << ")""#, - ty = self.c_single_vector_type(), - lanes = (0..self.num_lanes()) - .map(move |idx| -> std::string::String { - format!( - "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", - cast = self.c_promotion(), - lane_fn = self.get_lane_function(), - lane = idx, - vector = vector, - ) - }) - .collect::>() - .join(r#" << ", " << "#) - ) - }) - .collect::>() - .join(r#" << ", " << "#) - } else if self.num_lanes() > 1 { - (0..self.num_lanes()) - .map(|idx| -> std::string::String { - format!( - "{cast}{lane_fn}(__return_value, {lane})", - cast = self.c_promotion(), - lane_fn = self.get_lane_function(), - lane = idx - ) - }) - .collect::>() - .join(r#" << ", " << "#) - } else { - format!( - "{promote}cast<{cast}>(__return_value)", - cast = match self.kind() { - TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), - TypeKind::Float if self.inner_size() == 32 => "float".to_string(), - TypeKind::Float if self.inner_size() == 64 => "double".to_string(), - TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), - TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), - TypeKind::Poly => format!("poly{}_t", self.inner_size()), - ty => todo!("print_result_c - Unknown type: {:#?}", ty), - }, - promote = self.c_promotion(), - ) - }; - - format!( - r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, - ty = if self.is_simd() { - format!("{}(", self.c_type()) - } else { - String::from("") - }, - close = if self.is_simd() { ")" } else { "" }, - ) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 32f8f106ce26d..e86a2c5189f0b 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,6 @@ use super::intrinsic::ArmIntrinsicType; use crate::common::cli::Language; +use crate::common::indentation::Indentation; use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; impl IntrinsicTypeDefinition for ArmIntrinsicType { @@ -98,6 +99,71 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { todo!("get_lane_function IntrinsicType: {:#?}", self) } } + + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { + let lanes = if self.num_vectors() > 1 { + (0..self.num_vectors()) + .map(|vector| { + format!( + r#""{ty}(" << {lanes} << ")""#, + ty = self.c_single_vector_type(), + lanes = (0..self.num_lanes()) + .map(move |idx| -> std::string::String { + format!( + "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), + lane = idx, + vector = vector, + ) + }) + .collect::>() + .join(r#" << ", " << "#) + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } else if self.num_lanes() > 1 { + (0..self.num_lanes()) + .map(|idx| -> std::string::String { + format!( + "{cast}{lane_fn}(__return_value, {lane})", + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), + lane = idx + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } else { + format!( + "{promote}cast<{cast}>(__return_value)", + cast = match self.kind() { + TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), + TypeKind::Float if self.inner_size() == 32 => "float".to_string(), + TypeKind::Float if self.inner_size() == 64 => "double".to_string(), + TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), + TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), + TypeKind::Poly => format!("poly{}_t", self.inner_size()), + ty => todo!("print_result_c - Unknown type: {:#?}", ty), + }, + promote = self.c_promotion(), + ) + }; + + format!( + r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, + ty = if self.is_simd() { + format!("{}(", self.c_type()) + } else { + String::from("") + }, + close = if self.is_simd() { ")" } else { "" }, + ) + } } impl ArmIntrinsicType { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 84755ce525053..bfb77be1d4a24 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -24,7 +24,9 @@ pub fn generate_c_test_loop( loaded_args = intrinsic.arguments().load_values_c(body_indentation), intrinsic_call = intrinsic.name(), args = intrinsic.arguments().as_call_param_c(), - print_result = intrinsic.print_result_c(body_indentation, additional) + print_result = intrinsic + .results() + .print_result_c(body_indentation, additional) ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index bc46ccfbac40c..810681337fd5e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,5 +1,4 @@ use super::argument::ArgumentList; -use super::indentation::Indentation; use super::intrinsic_helpers::{IntrinsicTypeDefinition, TypeKind}; /// An intrinsic @@ -27,11 +26,6 @@ where fn results(&self) -> T; fn name(&self) -> String; - - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, _indentation: Indentation, _additional: &str) -> String; } pub fn format_f16_return_value( diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index f5e84ca97af21..7bc1015a387c1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -325,4 +325,9 @@ pub trait IntrinsicTypeDefinition: Deref { /// can be directly defined in `impl` blocks fn c_single_vector_type(&self) -> String; + + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String; } From 1697f36225310391cb96a2c0616f4596d3ba490f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:32:19 +0200 Subject: [PATCH 0241/1900] remove `trait IntrinsicDefinition` --- .../intrinsic-test/src/arm/intrinsic.rs | 16 ------------- .../crates/intrinsic-test/src/common/gen_c.rs | 23 +++++++++--------- .../intrinsic-test/src/common/gen_rust.rs | 24 ++++++++++--------- .../intrinsic-test/src/common/intrinsic.rs | 19 +++------------ 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 1928a00f4f382..29343bee4c300 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -1,5 +1,3 @@ -use crate::common::argument::ArgumentList; -use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; @@ -22,17 +20,3 @@ impl DerefMut for ArmIntrinsicType { &mut self.data } } - -impl IntrinsicDefinition for Intrinsic { - fn arguments(&self) -> ArgumentList { - self.arguments.clone() - } - - fn results(&self) -> ArmIntrinsicType { - self.results.clone() - } - - fn name(&self) -> String { - self.name.clone() - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index bfb77be1d4a24..20a5f2c63f0e1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,6 +1,7 @@ +use crate::common::intrinsic::Intrinsic; + use super::argument::Argument; use super::indentation::Indentation; -use super::intrinsic::IntrinsicDefinition; use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. @@ -8,7 +9,7 @@ const PASSES: u32 = 20; pub fn generate_c_test_loop( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, additional: &str, passes: u32, @@ -21,18 +22,18 @@ pub fn generate_c_test_loop( {body_indentation}auto __return_value = {intrinsic_call}({args});\n\ {print_result}\n\ {indentation}}}", - loaded_args = intrinsic.arguments().load_values_c(body_indentation), - intrinsic_call = intrinsic.name(), - args = intrinsic.arguments().as_call_param_c(), + loaded_args = intrinsic.arguments.load_values_c(body_indentation), + intrinsic_call = intrinsic.name, + args = intrinsic.arguments.as_call_param_c(), print_result = intrinsic - .results() + .results .print_result_c(body_indentation, additional) ) } pub fn generate_c_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, constraints: &mut (impl Iterator> + Clone), name: String, @@ -65,14 +66,14 @@ pub fn generate_c_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( // Compiles C test programs using specified compiler pub fn create_c_test_function( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { let indentation = Indentation::default(); - writeln!(w, "int run_{}() {{", intrinsic.name())?; + writeln!(w, "int run_{}() {{", intrinsic.name)?; // Define the arrays of arguments. - let arguments = intrinsic.arguments(); + let arguments = &intrinsic.arguments; arguments.gen_arglists_c(w, indentation.nested(), PASSES)?; generate_c_constraint_blocks( @@ -94,7 +95,7 @@ pub fn write_mod_cpp( notice: &str, architecture: &str, platform_headers: &[&str], - intrinsics: &[impl IntrinsicDefinition], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 2a02b8fdff1df..240496f9b765e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -1,8 +1,10 @@ use itertools::Itertools; use std::process::Command; +use crate::common::intrinsic::Intrinsic; + use super::indentation::Indentation; -use super::intrinsic::{IntrinsicDefinition, format_f16_return_value}; +use super::intrinsic::format_f16_return_value; use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. @@ -100,7 +102,7 @@ pub fn write_lib_rs( notice: &str, cfg: &str, definitions: &str, - intrinsics: &[impl IntrinsicDefinition], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; @@ -189,16 +191,16 @@ pub fn compile_rust_programs(toolchain: Option<&str>, target: &str, linker: Opti pub fn generate_rust_test_loop( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, specializations: &[Vec], passes: u32, ) -> std::io::Result<()> { - let intrinsic_name = intrinsic.name(); + let intrinsic_name = &intrinsic.name; // Each function (and each specialization) has its own type. Erase that type with a cast. let mut coerce = String::from("unsafe fn("); - for _ in intrinsic.arguments().iter().filter(|a| !a.has_constraint()) { + for _ in intrinsic.arguments.iter().filter(|a| !a.has_constraint()) { coerce += "_, "; } coerce += ") -> _"; @@ -248,8 +250,8 @@ pub fn generate_rust_test_loop( }}\n\ }}\n\ }}", - loaded_args = intrinsic.arguments().load_values_rust(indentation3), - args = intrinsic.arguments().as_call_param_rust(), + loaded_args = intrinsic.arguments.load_values_rust(indentation3), + args = intrinsic.arguments.as_call_param_rust(), ) } @@ -277,15 +279,15 @@ fn generate_rust_specializations<'a>( // Top-level function to create complete test program pub fn create_rust_test_module( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { - trace!("generating `{}`", intrinsic.name()); + trace!("generating `{}`", intrinsic.name); let indentation = Indentation::default(); - writeln!(w, "pub fn run_{}() {{", intrinsic.name())?; + writeln!(w, "pub fn run_{}() {{", intrinsic.name)?; // Define the arrays of arguments. - let arguments = intrinsic.arguments(); + let arguments = &intrinsic.arguments; arguments.gen_arglists_rust(w, indentation.nested(), PASSES)?; // Define any const generics as `const` items, then generate the actual test loop. diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 810681337fd5e..95276d19b72f9 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -17,27 +17,14 @@ pub struct Intrinsic { pub arch_tags: Vec, } -pub trait IntrinsicDefinition -where - T: IntrinsicTypeDefinition, -{ - fn arguments(&self) -> ArgumentList; - - fn results(&self) -> T; - - fn name(&self) -> String; -} - -pub fn format_f16_return_value( - intrinsic: &dyn IntrinsicDefinition, -) -> String { +pub fn format_f16_return_value(intrinsic: &Intrinsic) -> String { // the `intrinsic-test` crate compares the output of C and Rust intrinsics. Currently, It uses // a string representation of the output value to compare. In C, f16 values are currently printed // as hexadecimal integers. Since https://github.com/rust-lang/rust/pull/127013, rust does print // them as decimal floating point values. To keep the intrinsics tests working, for now, format // vectors containing f16 values like C prints them. - let return_value = match intrinsic.results().kind() { - TypeKind::Float if intrinsic.results().inner_size() == 16 => "debug_f16(__return_value)", + let return_value = match intrinsic.results.kind() { + TypeKind::Float if intrinsic.results.inner_size() == 16 => "debug_f16(__return_value)", _ => "format_args!(\"{__return_value:.150?}\")", }; From ccec2027272c635aaa8713df0943c85df765570e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:35:30 +0200 Subject: [PATCH 0242/1900] move `build_c_file` and `build_rust_file` into `SupportedArchitectureTest` --- .../crates/intrinsic-test/src/arm/mod.rs | 141 +---------------- .../crates/intrinsic-test/src/common/mod.rs | 145 +++++++++++++++++- 2 files changed, 143 insertions(+), 143 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 82dc5001adf06..bbf6a7a5a2d62 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -5,19 +5,11 @@ mod intrinsic; mod json_parser; mod types; -use std::fs::File; - -use rayon::prelude::*; - +use crate::common::SupportedArchitectureTest; use crate::common::cli::ProcessedCli; use crate::common::compile_c::CppCompilation; -use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; -use crate::common::gen_rust::{ - compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, -}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; -use crate::common::{SupportedArchitectureTest, chunk_info}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -76,135 +68,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { fn cpp_compilation(&self) -> Option { compile::build_cpp_compilation(&self.cli_options) } - - fn build_c_file(&self) -> bool { - let c_target = "aarch64"; - - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); - - let cpp_compiler_wrapped = self.cpp_compilation(); - - std::fs::create_dir_all("c_programs").unwrap(); - self.intrinsics() - .par_chunks(chunk_size) - .enumerate() - .map(|(i, chunk)| { - let c_filename = format!("c_programs/mod_{i}.cpp"); - let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp( - &mut file, - Self::NOTICE, - c_target, - Self::PLATFORM_C_HEADERS, - chunk, - ) - .unwrap(); - - // compile this cpp file into a .o file. - // - // This is done because `cpp_compiler_wrapped` is None when - // the --generate-only flag is passed - if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { - let output = cpp_compiler - .compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?; - assert!(output.status.success(), "{output:?}"); - } - - Ok(()) - }) - .collect::>() - .unwrap(); - - let mut file = File::create("c_programs/main.cpp").unwrap(); - write_main_cpp( - &mut file, - c_target, - Self::PLATFORM_C_DEFINITIONS, - self.intrinsics().iter().map(|i| i.name.as_str()), - ) - .unwrap(); - - // This is done because `cpp_compiler_wrapped` is None when - // the --generate-only flag is passed - if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { - // compile this cpp file into a .o file - info!("compiling main.cpp"); - let output = cpp_compiler - .compile_object_file("main.cpp", "intrinsic-test-programs.o") - .unwrap(); - assert!(output.status.success(), "{output:?}"); - - let object_files = (0..chunk_count) - .map(|i| format!("mod_{i}.o")) - .chain(["intrinsic-test-programs.o".to_owned()]); - - let output = cpp_compiler - .link_executable(object_files, "intrinsic-test-programs") - .unwrap(); - assert!(output.status.success(), "{output:?}"); - } - - true - } - - fn build_rust_file(&self) -> bool { - std::fs::create_dir_all("rust_programs/src").unwrap(); - - let architecture = if self.cli_options.target.contains("v7") { - "arm" - } else { - "aarch64" - }; - - let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); - - let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); - write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); - - let mut main_rs = File::create("rust_programs/src/main.rs").unwrap(); - write_main_rs( - &mut main_rs, - chunk_count, - Self::PLATFORM_RUST_CFGS, - "", - self.intrinsics.iter().map(|i| i.name.as_str()), - ) - .unwrap(); - - let target = &self.cli_options.target; - let toolchain = self.cli_options.toolchain.as_deref(); - let linker = self.cli_options.linker.as_deref(); - - self.intrinsics - .par_chunks(chunk_size) - .enumerate() - .map(|(i, chunk)| { - std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; - - let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); - trace!("generating `{rust_filename}`"); - let mut file = File::create(rust_filename)?; - - write_lib_rs( - &mut file, - architecture, - Self::NOTICE, - Self::PLATFORM_RUST_CFGS, - Self::PLATFORM_RUST_DEFINITIONS, - chunk, - )?; - - let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); - trace!("generating `{toml_filename}`"); - let mut file = File::create(toml_filename).unwrap(); - - write_lib_cargo_toml(&mut file, &format!("mod_{i}"))?; - - Ok(()) - }) - .collect::>() - .unwrap(); - - compile_rust_programs(toolchain, target, linker) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 13e6c9fe9e28e..b6589ddaef8ef 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,7 +1,18 @@ +use std::fs::File; + +use rayon::prelude::*; + use cli::ProcessedCli; use crate::common::{ - compile_c::CppCompilation, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, + compile_c::CppCompilation, + gen_c::{write_main_cpp, write_mod_cpp}, + gen_rust::{ + compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, + write_main_rs, + }, + intrinsic::Intrinsic, + intrinsic_helpers::IntrinsicTypeDefinition, }; pub mod argument; @@ -36,8 +47,136 @@ pub trait SupportedArchitectureTest { fn cpp_compilation(&self) -> Option; - fn build_c_file(&self) -> bool; - fn build_rust_file(&self) -> bool; + fn build_c_file(&self) -> bool { + let c_target = "aarch64"; + + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); + + let cpp_compiler_wrapped = self.cpp_compilation(); + + std::fs::create_dir_all("c_programs").unwrap(); + self.intrinsics() + .par_chunks(chunk_size) + .enumerate() + .map(|(i, chunk)| { + let c_filename = format!("c_programs/mod_{i}.cpp"); + let mut file = File::create(&c_filename).unwrap(); + write_mod_cpp( + &mut file, + Self::NOTICE, + c_target, + Self::PLATFORM_C_HEADERS, + chunk, + ) + .unwrap(); + + // compile this cpp file into a .o file. + // + // This is done because `cpp_compiler_wrapped` is None when + // the --generate-only flag is passed + if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { + let output = cpp_compiler + .compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?; + assert!(output.status.success(), "{output:?}"); + } + + Ok(()) + }) + .collect::>() + .unwrap(); + + let mut file = File::create("c_programs/main.cpp").unwrap(); + write_main_cpp( + &mut file, + c_target, + Self::PLATFORM_C_DEFINITIONS, + self.intrinsics().iter().map(|i| i.name.as_str()), + ) + .unwrap(); + + // This is done because `cpp_compiler_wrapped` is None when + // the --generate-only flag is passed + if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { + // compile this cpp file into a .o file + info!("compiling main.cpp"); + let output = cpp_compiler + .compile_object_file("main.cpp", "intrinsic-test-programs.o") + .unwrap(); + assert!(output.status.success(), "{output:?}"); + + let object_files = (0..chunk_count) + .map(|i| format!("mod_{i}.o")) + .chain(["intrinsic-test-programs.o".to_owned()]); + + let output = cpp_compiler + .link_executable(object_files, "intrinsic-test-programs") + .unwrap(); + assert!(output.status.success(), "{output:?}"); + } + + true + } + + fn build_rust_file(&self) -> bool { + std::fs::create_dir_all("rust_programs/src").unwrap(); + + let architecture = if self.cli_options().target.contains("v7") { + "arm" + } else { + "aarch64" + }; + + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); + + let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); + write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); + + let mut main_rs = File::create("rust_programs/src/main.rs").unwrap(); + write_main_rs( + &mut main_rs, + chunk_count, + Self::PLATFORM_RUST_CFGS, + "", + self.intrinsics().iter().map(|i| i.name.as_str()), + ) + .unwrap(); + + let target = &self.cli_options().target; + let toolchain = self.cli_options().toolchain.as_deref(); + let linker = self.cli_options().linker.as_deref(); + + self.intrinsics() + .par_chunks(chunk_size) + .enumerate() + .map(|(i, chunk)| { + std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; + + let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); + trace!("generating `{rust_filename}`"); + let mut file = File::create(rust_filename)?; + + write_lib_rs( + &mut file, + architecture, + Self::NOTICE, + Self::PLATFORM_RUST_CFGS, + Self::PLATFORM_RUST_DEFINITIONS, + chunk, + )?; + + let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); + trace!("generating `{toml_filename}`"); + let mut file = File::create(toml_filename).unwrap(); + + write_lib_cargo_toml(&mut file, &format!("mod_{i}"))?; + + Ok(()) + }) + .collect::>() + .unwrap(); + + compile_rust_programs(toolchain, target, linker) + } fn compare_outputs(&self) -> bool { if self.cli_options().toolchain.is_some() { From 4b549a7330736bad6f7296241c6ce1cdbab4ef60 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 14:10:47 +0200 Subject: [PATCH 0243/1900] move target-specific definitions into constants --- .../crates/intrinsic-test/src/arm/config.rs | 20 +++++++++++++-- .../crates/intrinsic-test/src/arm/mod.rs | 25 ++++++++++--------- .../crates/intrinsic-test/src/common/gen_c.rs | 13 +++------- .../intrinsic-test/src/common/gen_rust.rs | 3 --- .../crates/intrinsic-test/src/common/mod.rs | 13 ++-------- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs index beaca621378d4..72e997de154ab 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs @@ -3,7 +3,15 @@ pub const NOTICE: &str = "\ // test are derived from a JSON specification, published under the same license as the // `intrinsic-test` crate.\n"; -pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& os, poly128_t value) { +pub const POLY128_OSTREAM_DECL: &str = r#" +#ifdef __aarch64__ +std::ostream& operator<<(std::ostream& os, poly128_t value); +#endif +"#; + +pub const POLY128_OSTREAM_DEF: &str = r#" +#ifdef __aarch64__ +std::ostream& operator<<(std::ostream& os, poly128_t value) { std::stringstream temp; do { int n = value % 10; @@ -14,7 +22,9 @@ pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& std::string res(tempstr.rbegin(), tempstr.rend()); os << res; return os; -}"#; +} +#endif +"#; // Format f16 values (and vectors containing them) in a way that is consistent with C. pub const F16_FORMATTING_DEF: &str = r#" @@ -113,4 +123,10 @@ pub const AARCH_CONFIGURATIONS: &str = r#" #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] #![feature(fmt_helpers_for_derive)] #![feature(stdarch_neon_f16)] + +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] +use core::arch::aarch64::*; + +#[cfg(target_arch = "arm")] +use core::arch::arm::*; "#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index bbf6a7a5a2d62..08dc2d38702cd 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -29,6 +29,19 @@ impl SupportedArchitectureTest for ArmArchitectureTest { &self.intrinsics } + const NOTICE: &str = config::NOTICE; + + const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; + const PLATFORM_C_FORWARD_DECLARATIONS: &str = config::POLY128_OSTREAM_DECL; + + const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; + const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; + + fn cpp_compilation(&self) -> Option { + compile::build_cpp_compilation(&self.cli_options) + } + fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.contains("v7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) @@ -56,16 +69,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { cli_options, } } - - const NOTICE: &str = config::NOTICE; - - const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; - const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; - - const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; - const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; - - fn cpp_compilation(&self) -> Option { - compile::build_cpp_compilation(&self.cli_options) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 20a5f2c63f0e1..28902b3dfe981 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -93,8 +93,8 @@ pub fn create_c_test_function( pub fn write_mod_cpp( w: &mut impl std::io::Write, notice: &str, - architecture: &str, platform_headers: &[&str], + forward_declarations: &str, intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; @@ -125,12 +125,7 @@ std::ostream& operator<<(std::ostream& os, float16_t value); "# )?; - writeln!(w, "#ifdef __{architecture}__")?; - writeln!( - w, - "std::ostream& operator<<(std::ostream& os, poly128_t value);" - )?; - writeln!(w, "#endif")?; + writeln!(w, "{}", forward_declarations)?; for intrinsic in intrinsics { create_c_test_function(w, intrinsic)?; @@ -141,7 +136,6 @@ std::ostream& operator<<(std::ostream& os, float16_t value); pub fn write_main_cpp<'a>( w: &mut impl std::io::Write, - architecture: &str, arch_specific_definitions: &str, intrinsics: impl Iterator + Clone, ) -> std::io::Result<()> { @@ -170,9 +164,8 @@ std::ostream& operator<<(std::ostream& os, float16_t value) {{ "# )?; - writeln!(w, "#ifdef __{architecture}__")?; + // NOTE: It's assumed that this value contains the required `ifdef`s. writeln!(w, "{arch_specific_definitions }")?; - writeln!(w, "#endif")?; for intrinsic in intrinsics.clone() { writeln!(w, "extern int run_{intrinsic}(void);")?; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 240496f9b765e..312cbee692a56 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -98,7 +98,6 @@ pub fn write_main_rs<'a>( pub fn write_lib_rs( w: &mut impl std::io::Write, - architecture: &str, notice: &str, cfg: &str, definitions: &str, @@ -117,8 +116,6 @@ pub fn write_lib_rs( writeln!(w, "{cfg}")?; - writeln!(w, "use core_arch::arch::{architecture}::*;")?; - writeln!(w, "{definitions}")?; for intrinsic in intrinsics { diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b6589ddaef8ef..666b3885c147b 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -41,6 +41,7 @@ pub trait SupportedArchitectureTest { const PLATFORM_C_HEADERS: &[&str]; const PLATFORM_C_DEFINITIONS: &str; + const PLATFORM_C_FORWARD_DECLARATIONS: &str; const PLATFORM_RUST_CFGS: &str; const PLATFORM_RUST_DEFINITIONS: &str; @@ -48,8 +49,6 @@ pub trait SupportedArchitectureTest { fn cpp_compilation(&self) -> Option; fn build_c_file(&self) -> bool { - let c_target = "aarch64"; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); let cpp_compiler_wrapped = self.cpp_compilation(); @@ -64,8 +63,8 @@ pub trait SupportedArchitectureTest { write_mod_cpp( &mut file, Self::NOTICE, - c_target, Self::PLATFORM_C_HEADERS, + Self::PLATFORM_C_FORWARD_DECLARATIONS, chunk, ) .unwrap(); @@ -88,7 +87,6 @@ pub trait SupportedArchitectureTest { let mut file = File::create("c_programs/main.cpp").unwrap(); write_main_cpp( &mut file, - c_target, Self::PLATFORM_C_DEFINITIONS, self.intrinsics().iter().map(|i| i.name.as_str()), ) @@ -120,12 +118,6 @@ pub trait SupportedArchitectureTest { fn build_rust_file(&self) -> bool { std::fs::create_dir_all("rust_programs/src").unwrap(); - let architecture = if self.cli_options().target.contains("v7") { - "arm" - } else { - "aarch64" - }; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); @@ -157,7 +149,6 @@ pub trait SupportedArchitectureTest { write_lib_rs( &mut file, - architecture, Self::NOTICE, Self::PLATFORM_RUST_CFGS, Self::PLATFORM_RUST_DEFINITIONS, From f8180c3c429794b87968932166e8c8333f682956 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 15:17:25 +0200 Subject: [PATCH 0244/1900] extract to a separate file --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 + clippy_lints/src/misc.rs | 118 +------------------------- clippy_lints/src/toplevel_ref_arg.rs | 120 +++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 116 deletions(-) create mode 100644 clippy_lints/src/toplevel_ref_arg.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b9..e610e655f1582 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -503,7 +503,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::min_ident_chars::MIN_IDENT_CHARS_INFO, crate::minmax::MIN_MAX_INFO, crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, - crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, @@ -706,6 +705,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, + crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a89cf3fdc1eee..7590bec9e1e99 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,6 +360,7 @@ mod temporary_assignment; mod tests_outside_test_module; mod to_digit_is_some; mod to_string_trait_impl; +mod toplevel_ref_arg; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -831,5 +832,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 09ee6f7037c64..19e9910dfe9dd 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,57 +1,11 @@ -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, - last_path_segment, -}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, last_path_segment}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, -}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::def_id::LocalDefId; - -use crate::ref_patterns::REF_PATTERNS; - -declare_clippy_lint! { - /// ### What it does - /// Checks for function arguments and let bindings denoted as - /// `ref`. - /// - /// ### Why is this bad? - /// The `ref` declaration makes the function take an owned - /// value, but turns the argument into a reference (which means that the value - /// is destroyed when exiting the function). This adds not much value: either - /// take a reference type, or take an owned value and create references in the - /// body. - /// - /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The - /// type of `x` is more obvious with the former. - /// - /// ### Known problems - /// If the argument is dereferenced within the function, - /// removing the `ref` will lead to errors. This can be fixed by removing the - /// dereferences, e.g., changing `*x` to `x` within the function. - /// - /// ### Example - /// ```no_run - /// fn foo(ref _x: u8) {} - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn foo(_x: &u8) {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TOPLEVEL_REF_ARG, - style, - "an entire binding declared as `ref`, in a function argument or a `let` statement" -} declare_clippy_lint! { /// ### What it does @@ -140,79 +94,13 @@ declare_clippy_lint! { } declare_lint_pass!(LintPass => [ - TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - k: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, - _: Span, - _: LocalDefId, - ) { - if !matches!(k, FnKind::Closure) { - for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind - && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) - && !arg.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint_hir( - cx, - TOPLEVEL_REF_ARG, - arg.hir_id, - arg.pat.span, - "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ - Consider using a reference type instead", - ); - } - } - } - } - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind - && let Some(init) = local.init - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) - && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), - app, - ); - }, - ); - } if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Binary(binop, a, b) = &expr.kind && matches!(binop.node, BinOpKind::And | BinOpKind::Or) diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs new file mode 100644 index 0000000000000..04da5efce5da2 --- /dev/null +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -0,0 +1,120 @@ +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_lint_allowed, iter_input_pats}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingMode, Body, ByRef, FnDecl, Mutability, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +use crate::ref_patterns::REF_PATTERNS; + +declare_clippy_lint! { + /// ### What it does + /// Checks for function arguments and let bindings denoted as + /// `ref`. + /// + /// ### Why is this bad? + /// The `ref` declaration makes the function take an owned + /// value, but turns the argument into a reference (which means that the value + /// is destroyed when exiting the function). This adds not much value: either + /// take a reference type, or take an owned value and create references in the + /// body. + /// + /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The + /// type of `x` is more obvious with the former. + /// + /// ### Known problems + /// If the argument is dereferenced within the function, + /// removing the `ref` will lead to errors. This can be fixed by removing the + /// dereferences, e.g., changing `*x` to `x` within the function. + /// + /// ### Example + /// ```no_run + /// fn foo(ref _x: u8) {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo(_x: &u8) {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub TOPLEVEL_REF_ARG, + style, + "an entire binding declared as `ref`, in a function argument or a `let` statement" +} + +declare_lint_pass!(ToplevelRefArg => [TOPLEVEL_REF_ARG]); + +impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + k: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: LocalDefId, + ) { + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !arg.span.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ + Consider using a reference type instead", + ); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(local) = stmt.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let Some(init) = local.init + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = if mutabl == Mutability::Mut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); + } + } +} From 2c27b586183779d2437ac758055bb21011c6551c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 15:26:50 +0200 Subject: [PATCH 0245/1900] tiny clean-up --- clippy_lints/src/toplevel_ref_arg.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs index 04da5efce5da2..074b79263d377 100644 --- a/clippy_lints/src/toplevel_ref_arg.rs +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -89,10 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) + let (mutopt, initref) = match mutabl { + Mutability::Mut => ("mut ", sugg_init.mut_addr()), + Mutability::Not => ("", sugg_init.addr()), }; let tyopt = if let Some(ty) = local.ty { let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; From 6da8094b1385b1647178a3ab9dab3c359ef1c38d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 7 Sep 2025 21:49:55 +0800 Subject: [PATCH 0246/1900] Improve make::struct_ field_list whitespace Example --- **Before this PR**: ```rust struct Variant{ field: u32 } ``` **After this PR**: ```rust struct Variant { field: u32 } ``` --- .../extract_struct_from_enum_variant.rs | 44 +++++++++---------- .../src/handlers/json_is_not_rust.rs | 24 +++++----- .../crates/syntax/src/ast/make.rs | 7 ++- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index c56d0b3de5d6a..79a4c73c698d1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -478,7 +478,7 @@ macro_rules! foo { }; } -struct TheVariant{ the_field: u8 } +struct TheVariant { the_field: u8 } enum TheEnum { TheVariant(TheVariant), @@ -502,7 +502,7 @@ enum Foo { } "#, r#" -struct Bar{ node: Box } +struct Bar { node: Box } enum Foo { Bar(Bar), @@ -519,7 +519,7 @@ enum Foo { } "#, r#" -struct Bar{ node: Box, a: Arc> } +struct Bar { node: Box, a: Arc> } enum Foo { Bar(Bar), @@ -560,7 +560,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, "enum A { $0One { foo: u32, bar: u32 } }", - r#"struct One{ foo: u32, bar: u32 } + r#"struct One { foo: u32, bar: u32 } enum A { One(One) }"#, ); @@ -571,7 +571,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, "enum A { $0One { foo: u32 } }", - r#"struct One{ foo: u32 } + r#"struct One { foo: u32 } enum A { One(One) }"#, ); @@ -582,7 +582,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, r"enum En { Var { a: T$0 } }", - r#"struct Var{ a: T } + r#"struct Var { a: T } enum En { Var(Var) }"#, ); @@ -599,7 +599,7 @@ enum Enum { Variant{ field: u32$0 } }"#, r#" #[derive(Debug)] #[derive(Clone)] -struct Variant{ field: u32 } +struct Variant { field: u32 } #[derive(Debug)] #[derive(Clone)] @@ -618,7 +618,7 @@ enum Enum { } }"#, r#" -struct Variant{ +struct Variant { field: u32 } @@ -642,7 +642,7 @@ mod indenting { }"#, r#" mod indenting { - struct Variant{ + struct Variant { field: u32 } @@ -668,7 +668,7 @@ enum A { } }"#, r#" -struct One{ +struct One { // leading comment /// doc comment #[an_attr] @@ -700,7 +700,7 @@ enum A { } }"#, r#" -struct One{ +struct One { // comment /// doc #[attr] @@ -747,7 +747,7 @@ enum A { /* comment */ // other /// comment -struct One{ +struct One { a: u32 } @@ -789,7 +789,7 @@ enum A { extract_struct_from_enum_variant, "enum A { $0One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }", r#" -struct One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } +struct One { a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } enum A { One(One) }"#, ); @@ -850,7 +850,7 @@ pub enum A { One(One) }"#, extract_struct_from_enum_variant, "pub(in something) enum A { $0One{ a: u32, b: u32 } }", r#" -pub(in something) struct One{ pub(in something) a: u32, pub(in something) b: u32 } +pub(in something) struct One { pub(in something) a: u32, pub(in something) b: u32 } pub(in something) enum A { One(One) }"#, ); @@ -862,7 +862,7 @@ pub(in something) enum A { One(One) }"#, extract_struct_from_enum_variant, "pub(crate) enum A { $0One{ a: u32, b: u32, c: u32 } }", r#" -pub(crate) struct One{ pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 } +pub(crate) struct One { pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 } pub(crate) enum A { One(One) }"#, ); @@ -933,7 +933,7 @@ fn f() { } "#, r#" -struct V{ i: i32, j: i32 } +struct V { i: i32, j: i32 } enum E { V(V) @@ -1027,7 +1027,7 @@ fn f() { "#, r#" //- /main.rs -struct V{ i: i32, j: i32 } +struct V { i: i32, j: i32 } enum E { V(V) @@ -1057,7 +1057,7 @@ fn foo() { } "#, r#" -struct One{ a: u32, b: u32 } +struct One { a: u32, b: u32 } enum A { One(One) } @@ -1114,7 +1114,7 @@ enum X<'a, 'b, 'x> { } "#, r#" -struct A<'a, 'x>{ a: &'a &'x mut () } +struct A<'a, 'x> { a: &'a &'x mut () } enum X<'a, 'b, 'x> { A(A<'a, 'x>), @@ -1136,7 +1136,7 @@ enum X<'b, T, V, const C: usize> { } "#, r#" -struct A<'b, T, const C: usize>{ a: T, b: X<'b>, c: [u8; C] } +struct A<'b, T, const C: usize> { a: T, b: X<'b>, c: [u8; C] } enum X<'b, T, V, const C: usize> { A(A<'b, T, C>), @@ -1158,7 +1158,7 @@ enum X<'a, 'b> { } "#, r#" -struct C{ c: () } +struct C { c: () } enum X<'a, 'b> { A { a: &'a () }, @@ -1180,7 +1180,7 @@ enum En { } "#, r#" -struct A{ a: T } +struct A { a: T } enum En { A(A), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 742d614bc5673..a300997723b4d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -233,7 +233,7 @@ mod tests { } #[derive(Serialize)] - struct Root1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + struct Root1 { bar: f64, bay: i64, baz: (), r#box: bool, foo: String } "#, ); @@ -252,9 +252,9 @@ mod tests { } "#, r#" - struct Value1{ } - struct Bar1{ kind: String, value: Value1 } - struct Root1{ bar: Bar1, foo: String } + struct Value1 { } + struct Bar1 { kind: String, value: Value1 } + struct Root1 { bar: Bar1, foo: String } "#, ); @@ -284,12 +284,12 @@ mod tests { } "#, r#" - struct Address1{ house: i64, street: String } - struct User1{ address: Address1, email: String } - struct AnotherUser1{ user: User1 } - struct Address2{ house: i64, street: String } - struct User2{ address: Address2, email: String } - struct Root1{ another_user: AnotherUser1, user: User2 } + struct Address1 { house: i64, street: String } + struct User1 { address: Address1, email: String } + struct AnotherUser1 { user: User1 } + struct Address2 { house: i64, street: String } + struct User2 { address: Address2, email: String } + struct Root1 { another_user: AnotherUser1, user: User2 } "#, ); @@ -326,9 +326,9 @@ mod tests { use serde::Deserialize; #[derive(Serialize, Deserialize)] - struct OfObject1{ x: i64, y: i64 } + struct OfObject1 { x: i64, y: i64 } #[derive(Serialize, Deserialize)] - struct Root1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } + struct Root1 { empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } "#, ); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 9897fd0941570..051c5835571bc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1244,14 +1244,17 @@ pub fn struct_( generic_param_list: Option, field_list: ast::FieldList, ) -> ast::Struct { - let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; + let (semicolon, ws) = + if matches!(field_list, ast::FieldList::TupleFieldList(_)) { (";", "") } else { ("", " ") }; let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string()); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",)) + ast_from_text(&format!( + "{visibility}struct {strukt_name}{type_params}{ws}{field_list}{semicolon}" + )) } pub fn enum_( From a2d66db9badf35d4ad31323b0f5e6132d56f9391 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 6 Sep 2025 17:47:54 +0000 Subject: [PATCH 0247/1900] remove unsused div_rem method from bignum --- library/core/src/num/bignum.rs | 37 --------------------------- library/coretests/tests/num/bignum.rs | 19 -------------- 2 files changed, 56 deletions(-) diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index e33f58197bba5..f21fe0b4438fb 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -335,43 +335,6 @@ macro_rules! define_bignum { } (self, borrow) } - - /// Divide self by another bignum, overwriting `q` with the quotient and `r` with the - /// remainder. - pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) { - // Stupid slow base-2 long division taken from - // https://en.wikipedia.org/wiki/Division_algorithm - // FIXME use a greater base ($ty) for the long division. - assert!(!d.is_zero()); - let digitbits = <$ty>::BITS as usize; - for digit in &mut q.base[..] { - *digit = 0; - } - for digit in &mut r.base[..] { - *digit = 0; - } - r.size = d.size; - q.size = 1; - let mut q_is_zero = true; - let end = self.bit_length(); - for i in (0..end).rev() { - r.mul_pow2(1); - r.base[0] |= self.get_bit(i) as $ty; - if &*r >= d { - r.sub(d); - // Set bit `i` of q to 1. - let digit_idx = i / digitbits; - let bit_idx = i % digitbits; - if q_is_zero { - q.size = digit_idx + 1; - q_is_zero = false; - } - q.base[digit_idx] |= 1 << bit_idx; - } - } - debug_assert!(q.base[q.size..].iter().all(|&d| d == 0)); - debug_assert!(r.base[r.size..].iter().all(|&d| d == 0)); - } } impl crate::cmp::PartialEq for $name { diff --git a/library/coretests/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs index f213fd5366cbf..6dfa496e018bf 100644 --- a/library/coretests/tests/num/bignum.rs +++ b/library/coretests/tests/num/bignum.rs @@ -167,25 +167,6 @@ fn test_div_rem_small() { ); } -#[test] -fn test_div_rem() { - fn div_rem(n: u64, d: u64) -> (Big, Big) { - let mut q = Big::from_small(42); - let mut r = Big::from_small(42); - Big::from_u64(n).div_rem(&Big::from_u64(d), &mut q, &mut r); - (q, r) - } - assert_eq!(div_rem(1, 1), (Big::from_small(1), Big::from_small(0))); - assert_eq!(div_rem(4, 3), (Big::from_small(1), Big::from_small(1))); - assert_eq!(div_rem(1, 7), (Big::from_small(0), Big::from_small(1))); - assert_eq!(div_rem(45, 9), (Big::from_small(5), Big::from_small(0))); - assert_eq!(div_rem(103, 9), (Big::from_small(11), Big::from_small(4))); - assert_eq!(div_rem(123456, 77), (Big::from_u64(1603), Big::from_small(25))); - assert_eq!(div_rem(0xffff, 1), (Big::from_u64(0xffff), Big::from_small(0))); - assert_eq!(div_rem(0xeeee, 0xffff), (Big::from_small(0), Big::from_u64(0xeeee))); - assert_eq!(div_rem(2_000_000, 2), (Big::from_u64(1_000_000), Big::from_u64(0))); -} - #[test] fn test_is_zero() { assert!(Big::from_small(0).is_zero()); From 61af5da8dfc9f5fd6e2d36f8316f7bf5884f5388 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 0248/1900] Implement more features for GenMC mode - Support for atomic fences. - Support for atomic read-modify-write (RMW). - Add tests using RMW and fences. - Add options: - to disable weak memory effects in GenMC mode. - to print GenMC execution graphs. - to print GenMC output message. - Fix GenMC full rebuild issue and run configure step when commit changes. - Do cleanup. Co-authored-by: Ralf Jung --- src/tools/miri/doc/genmc.md | 7 +- src/tools/miri/genmc-sys/build.rs | 37 +++- .../genmc-sys/cpp/include/MiriInterface.hpp | 29 +++ .../cpp/src/MiriInterface/EventHandling.cpp | 66 ++++++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 13 +- src/tools/miri/genmc-sys/src/lib.rs | 77 ++++++- src/tools/miri/src/bin/miri.rs | 23 +-- src/tools/miri/src/concurrency/data_race.rs | 12 +- .../miri/src/concurrency/genmc/config.rs | 60 +++++- src/tools/miri/src/concurrency/genmc/dummy.rs | 8 +- .../miri/src/concurrency/genmc/helper.rs | 54 ++++- src/tools/miri/src/concurrency/genmc/mod.rs | 191 ++++++++++++------ src/tools/miri/src/concurrency/genmc/run.rs | 13 +- .../miri/tests/genmc/fail/loom/buggy_inc.rs | 66 ++++++ .../tests/genmc/fail/loom/buggy_inc.stderr | 13 ++ .../fail/simple/2w2w_weak.relaxed4.stderr | 8 +- .../fail/simple/2w2w_weak.release4.stderr | 8 +- .../miri/tests/genmc/fail/simple/2w2w_weak.rs | 7 +- .../fail/simple/2w2w_weak.sc3_rel1.stderr | 8 +- .../miri/tests/genmc/pass/atomics/rmw_ops.rs | 91 +++++++++ .../tests/genmc/pass/atomics/rmw_ops.stderr | 2 + .../tests/genmc/pass/litmus/2w2w_2sc_scf.rs | 33 +++ .../genmc/pass/litmus/2w2w_2sc_scf.stderr | 2 + .../miri/tests/genmc/pass/litmus/2w2w_4rel.rs | 4 + .../genmc/pass/litmus/2w2w_4rel.sc.stderr | 2 + ...2w2w_4rel.stderr => 2w2w_4rel.weak.stderr} | 0 .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 2 +- .../miri/tests/genmc/pass/litmus/IRIWish.rs | 66 ++++++ .../tests/genmc/pass/litmus/IRIWish.stderr | 30 +++ .../miri/tests/genmc/pass/litmus/LB_incMPs.rs | 41 ++++ .../tests/genmc/pass/litmus/LB_incMPs.stderr | 2 + .../tests/genmc/pass/litmus/MPU_rels_acq.rs | 42 ++++ .../genmc/pass/litmus/MPU_rels_acq.stderr | 2 + .../miri/tests/genmc/pass/litmus/MP_incMPs.rs | 36 ++++ .../tests/genmc/pass/litmus/MP_incMPs.stderr | 2 + .../tests/genmc/pass/litmus/MP_rels_acqf.rs | 40 ++++ .../genmc/pass/litmus/MP_rels_acqf.stderr | 2 + .../tests/genmc/pass/litmus/SB_2sc_scf.rs | 32 +++ .../tests/genmc/pass/litmus/SB_2sc_scf.stderr | 2 + .../miri/tests/genmc/pass/litmus/Z6_U.rs | 65 ++++++ .../tests/genmc/pass/litmus/Z6_U.sc.stderr | 20 ++ .../tests/genmc/pass/litmus/Z6_U.weak.stderr | 24 +++ .../miri/tests/genmc/pass/litmus/Z6_acq.rs | 38 ++++ .../tests/genmc/pass/litmus/Z6_acq.stderr | 2 + .../miri/tests/genmc/pass/litmus/atomicpo.rs | 32 +++ .../tests/genmc/pass/litmus/atomicpo.stderr | 2 + .../tests/genmc/pass/litmus/cumul-release.rs | 58 ++++++ .../genmc/pass/litmus/cumul-release.stderr | 2 + .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 1 - .../miri/tests/genmc/pass/litmus/inc2w.rs | 45 +++++ .../miri/tests/genmc/pass/litmus/inc2w.stderr | 2 + .../genmc/pass/litmus/inc_inc_RR_W_RR.rs | 63 ++++++ .../genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 2 + .../miri/tests/genmc/pass/litmus/riwi.rs | 31 +++ .../miri/tests/genmc/pass/litmus/riwi.stderr | 2 + .../tests/genmc/pass/litmus/viktor-relseq.rs | 36 ++++ .../genmc/pass/litmus/viktor-relseq.stderr | 2 + src/tools/miri/tests/utils/genmc.rs | 7 + 58 files changed, 1431 insertions(+), 136 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs create mode 100644 src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr rename src/tools/miri/tests/genmc/pass/litmus/{2w2w_4rel.stderr => 2w2w_4rel.weak.stderr} (100%) create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc2w.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/riwi.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/riwi.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index fdbf2defe42dc..8af697be34a61 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -24,6 +24,9 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Supported Parameters - `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-print-exec-graphs={none,explored,blocked,all}`: Make GenMC print the execution graph of the program after every explored, every blocked, or after every execution (default: None). +- `-Zmiri-genmc-print-exec-graphs`: Shorthand for suffix `=explored`. +- `-Zmiri-genmc-print-genmc-output`: Print the output that GenMC provides. NOTE: this output is quite verbose and the events in the printed execution graph are hard to map back to the Rust code location they originate from. - `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`. - `quiet`: Disable logging. - `error`: Print errors. @@ -34,7 +37,9 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. - +#### Regular Miri parameters useful for GenMC mode + +- `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled). diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 8eb818a6b265b..8d437c20a0926 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -30,13 +30,23 @@ mod downloading { /// The GenMC commit we depend on. It must be available on the specified GenMC repository. pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; - pub(crate) fn download_genmc() -> PathBuf { + /// Ensure that a local GenMC repo is present and set to the correct commit. + /// Return the path of the GenMC repo and whether the checked out commit was changed. + pub(crate) fn download_genmc() -> (PathBuf, bool) { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid."); match Repository::open(&genmc_download_path) { Ok(repo) => { assert_repo_unmodified(&repo); + if let Ok(head) = repo.head() + && let Ok(head_commit) = head.peel_to_commit() + && head_commit.id() == commit_oid + { + // Fast path: The expected commit is already checked out. + return (genmc_download_path, false); + } + // Check if the local repository already contains the commit we need, download it otherwise. let commit = update_local_repo(&repo, commit_oid); checkout_commit(&repo, &commit); } @@ -51,7 +61,7 @@ mod downloading { } }; - genmc_download_path + (genmc_download_path, true) } fn get_remote(repo: &Repository) -> Remote<'_> { @@ -71,7 +81,8 @@ mod downloading { // Update remote URL. println!( - "cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'" + "cargo::warning=GenMC repository remote URL has changed from '{}' to '{GENMC_GITHUB_URL}'", + remote_url.unwrap_or_default() ); repo.remote_set_url("https://github.com/origin%22,%20GENMC_GITHUB_URL) .expect("cannot rename url of remote 'origin'"); @@ -175,7 +186,7 @@ fn link_to_llvm(config_file: &Path) -> (String, String) { } /// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs -fn compile_cpp_dependencies(genmc_path: &Path) { +fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { // Give each step a separate build directory to prevent interference. let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap()); let genmc_build_dir = out_dir.join("genmc"); @@ -184,15 +195,13 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // Part 1: // Compile the GenMC library using cmake. - let cmakelists_path = genmc_path.join("CMakeLists.txt"); - // FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed. // Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release) let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); - let mut config = cmake::Config::new(cmakelists_path); + let mut config = cmake::Config::new(genmc_path); config - .always_configure(false) // We don't need to reconfigure on subsequent compilation runs. + .always_configure(always_configure) // We force running the configure step when the GenMC commit changed. .out_dir(genmc_build_dir) .profile(GENMC_CMAKE_PROFILE) .define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); @@ -251,7 +260,8 @@ fn compile_cpp_dependencies(genmc_path: &Path) { fn main() { // Select which path to use for the GenMC repo: - let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") { + let (genmc_path, always_configure) = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") + { let genmc_src_path = PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); assert!( @@ -261,13 +271,18 @@ fn main() { ); // Rebuild files in the given path change. println!("cargo::rerun-if-changed={}", genmc_src_path.display()); - genmc_src_path + // We disable `always_configure` when working with a local repository, + // since it increases compile times when working on `genmc-sys`. + (genmc_src_path, false) } else { + // Download GenMC if required and ensure that the correct commit is checked out. + // If anything changed in the downloaded repository (e.g., the commit), + // we set `always_configure` to ensure there are no weird configs from previous builds. downloading::download_genmc() }; // Build all required components: - compile_cpp_dependencies(&genmc_path); + compile_cpp_dependencies(&genmc_path, always_configure); // Only rebuild if anything changes: // Note that we don't add the downloaded GenMC repo, since that should never be modified diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 4484f0b73caec..b076937584326 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -33,6 +33,7 @@ struct GenmcScalar; struct SchedulingResult; struct LoadResult; struct StoreResult; +struct ReadModifyWriteResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -90,6 +91,15 @@ struct MiriGenmcShim : private GenMCDriver { MemOrdering ord, GenmcScalar old_val ); + [[nodiscard]] ReadModifyWriteResult handle_read_modify_write( + ThreadId thread_id, + uint64_t address, + uint64_t size, + RMWBinOp rmw_op, + MemOrdering ordering, + GenmcScalar rhs_value, + GenmcScalar old_val + ); [[nodiscard]] StoreResult handle_store( ThreadId thread_id, uint64_t address, @@ -99,6 +109,8 @@ struct MiriGenmcShim : private GenMCDriver { MemOrdering ord ); + void handle_fence(ThreadId thread_id, MemOrdering ord); + /**** Memory (de)allocation ****/ auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; void handle_free(ThreadId thread_id, uint64_t address); @@ -271,4 +283,21 @@ inline StoreResult from_error(std::unique_ptr error) { } } // namespace StoreResultExt +namespace ReadModifyWriteResultExt { +inline ReadModifyWriteResult +ok(SVal old_value, SVal new_value, bool is_coherence_order_maximal_write) { + return ReadModifyWriteResult { /* error: */ std::unique_ptr(nullptr), + /* old_value: */ GenmcScalarExt::from_sval(old_value), + /* new_value: */ GenmcScalarExt::from_sval(new_value), + is_coherence_order_maximal_write }; +} + +inline ReadModifyWriteResult from_error(std::unique_ptr error) { + return ReadModifyWriteResult { /* error: */ std::move(error), + /* old_value: */ GenmcScalarExt::uninit(), + /* new_value: */ GenmcScalarExt::uninit(), + /* is_coherence_order_maximal_write: */ false }; +} +} // namespace ReadModifyWriteResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index 7d8f0b16b49ab..cd28e0d148f58 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -89,6 +89,72 @@ ); } +void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleFence(pos, ord, EventDeps()); +} + +[[nodiscard]] auto MiriGenmcShim::handle_read_modify_write( + ThreadId thread_id, + uint64_t address, + uint64_t size, + RMWBinOp rmw_op, + MemOrdering ordering, + GenmcScalar rhs_value, + GenmcScalar old_val +) -> ReadModifyWriteResult { + // NOTE: Both the store and load events should get the same `ordering`, it should not be split + // into a load and a store component. This means we can have for example `AcqRel` loads and + // stores, but this is intended for RMW operations. + + // Somewhat confusingly, the GenMC term for RMW read/write labels is + // `FaiRead` and `FaiWrite`. + const auto load_ret = handle_load_reset_if_none( + thread_id, + ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + rmw_op, + GenmcScalarExt::to_sval(rhs_value), + EventDeps() + ); + if (const auto* err = std::get_if(&load_ret)) + return ReadModifyWriteResultExt::from_error(format_error(*err)); + + const auto* ret_val = std::get_if(&load_ret); + if (nullptr == ret_val) { + ERROR("Unimplemented: read-modify-write returned unexpected result."); + } + const auto read_old_val = *ret_val; + const auto new_value = + executeRMWBinOp(read_old_val, GenmcScalarExt::to_sval(rhs_value), size, rmw_op); + + const auto storePos = inc_pos(thread_id); + const auto store_ret = GenMCDriver::handleStore( + storePos, + ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + new_value + ); + if (const auto* err = std::get_if(&store_ret)) + return ReadModifyWriteResultExt::from_error(format_error(*err)); + + const auto* store_ret_val = std::get_if(&store_ret); + ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result."); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return ReadModifyWriteResultExt::ok( + /* old_value: */ read_old_val, + new_value, + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + ); +} + /**** Memory (de)allocation ****/ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index 7194ed02da432..5a53fee059216 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -63,9 +63,12 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel auto conf = std::make_shared(); // Set whether GenMC should print execution graphs after every explored/blocked execution. - // FIXME(genmc): pass these settings from Miri. - conf->printExecGraphs = false; - conf->printBlockedExecs = false; + conf->printExecGraphs = + (params.print_execution_graphs == ExecutiongraphPrinting::Explored || + params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked); + conf->printBlockedExecs = + (params.print_execution_graphs == ExecutiongraphPrinting::Blocked || + params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked); // `1024` is the default value that GenMC uses. // If any thread has at least this many events, a warning/tip will be printed. @@ -79,8 +82,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri. conf->warnOnGraphSize = 1024 * 1024; - // We only support the RC11 memory model for Rust. - conf->model = ModelType::RC11; + // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is disabled. + conf->model = params.disable_weak_memory_emulation ? ModelType::SC : ModelType::RC11; // This prints the seed that GenMC picks for randomized scheduling during estimation mode. conf->printRandomScheduleSeed = params.print_random_schedule_seed; diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 406c90809fcd5..1de4c4eb5e8e9 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -53,7 +53,13 @@ impl GenmcScalar { impl Default for GenmcParams { fn default() -> Self { - Self { print_random_schedule_seed: false, do_symmetry_reduction: false } + Self { + print_random_schedule_seed: false, + do_symmetry_reduction: false, + // GenMC graphs can be quite large since Miri produces a lot of (non-atomic) events. + print_execution_graphs: ExecutiongraphPrinting::None, + disable_weak_memory_emulation: false, + } } } @@ -91,7 +97,10 @@ mod ffi { struct GenmcParams { pub print_random_schedule_seed: bool, pub do_symmetry_reduction: bool, - // FIXME(GenMC): Add remaining parameters. + pub print_execution_graphs: ExecutiongraphPrinting, + /// Enabling this will set the memory model used by GenMC to "Sequential Consistency" (SC). + /// This will disable any weak memory effects, which reduces the number of program executions that will be explored. + pub disable_weak_memory_emulation: bool, } /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). @@ -120,6 +129,19 @@ mod ffi { Debug3ReadsFrom, } + #[derive(Debug)] + /// Setting to control which execution graphs GenMC prints after every execution. + enum ExecutiongraphPrinting { + /// Print no graphs. + None, + /// Print graphs of all fully explored executions. + Explored, + /// Print graphs of all blocked executions. + Blocked, + /// Print graphs of all executions. + ExploredAndBlocked, + } + /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. #[derive(Debug, Clone, Copy)] @@ -163,7 +185,21 @@ mod ffi { is_coherence_order_maximal_write: bool, } - /**** Types shared between Miri/Rust and GenMC/C++ through cxx_bridge: ****/ + #[must_use] + #[derive(Debug)] + struct ReadModifyWriteResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the RMW operation as the left operand. + old_value: GenmcScalar, + /// The value that was produced by the RMW operation. + new_value: GenmcScalar, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + + /**** These are GenMC types that we have to copy-paste here since cxx does not support + "importing" externally defined C++ types. ****/ #[derive(Debug)] /// Corresponds to GenMC's type with the same name. @@ -188,6 +224,21 @@ mod ffi { SequentiallyConsistent = 6, } + #[derive(Debug)] + enum RMWBinOp { + Xchg = 0, + Add = 1, + Sub = 2, + And = 3, + Nand = 4, + Or = 5, + Xor = 6, + Max = 7, + Min = 8, + UMax = 9, + UMin = 10, + } + // # Safety // // This block is unsafe to allow defining safe methods inside. @@ -202,9 +253,12 @@ mod ffi { /**** Types shared between Miri/Rust and Miri/C++: ****/ type MiriGenmcShim; - /**** Types shared between Miri/Rust and GenMC/C++: ****/ + /**** Types shared between Miri/Rust and GenMC/C++: + (This tells cxx that the enums defined above are already defined on the C++ side; + it will emit assertions to ensure that the two definitions agree.) ****/ type ActionKind; type MemOrdering; + type RMWBinOp; /// Set the log level for GenMC. /// @@ -249,6 +303,16 @@ mod ffi { memory_ordering: MemOrdering, old_value: GenmcScalar, ) -> LoadResult; + fn handle_read_modify_write( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + rmw_op: RMWBinOp, + ordering: MemOrdering, + rhs_value: GenmcScalar, + old_value: GenmcScalar, + ) -> ReadModifyWriteResult; fn handle_store( self: Pin<&mut MiriGenmcShim>, thread_id: i32, @@ -258,6 +322,11 @@ mod ffi { old_value: GenmcScalar, memory_ordering: MemOrdering, ) -> StoreResult; + fn handle_fence( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + memory_ordering: MemOrdering, + ); /**** Memory (de)allocation ****/ fn handle_malloc( diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ff05b4b14c79a..731fe283a2195 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -746,24 +746,11 @@ fn main() { many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); // Validate settings for data race detection and GenMC mode. - if miri_config.genmc_config.is_some() { - if !miri_config.data_race_detector { - fatal_error!("Cannot disable data race detection in GenMC mode"); - } else if !miri_config.weak_memory_emulation { - fatal_error!("Cannot disable weak memory emulation in GenMC mode"); - } else if !miri_config.native_lib.is_empty() { - fatal_error!("native-lib not supported in GenMC mode."); - } - if miri_config.borrow_tracker.is_some() { - eprintln!( - "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." - ); - miri_config.borrow_tracker = None; - } - // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway - // would be a NOP in GenMC mode. - miri_config.fixed_scheduling = true; - } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { + if let Err(err) = GenmcConfig::validate_genmc_mode_settings(&mut miri_config) { + fatal_error!("Invalid settings: {err}"); + } + + if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" ); diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index a7822d0d6fa31..30061a78594aa 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -793,9 +793,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this, place.ptr().addr(), place.layout.size, + atomic_op, place.layout.backend_repr.is_signed(), ord, - atomic_op, rhs.to_scalar(), old.to_scalar(), )?; @@ -901,7 +901,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { can_fail_spuriously, old.to_scalar(), )?; - // The store might be the latest store in coherence order (determined by GenMC). // If it is, we need to update the value in Miri's memory: if let Some(new_value) = new_value { @@ -961,6 +960,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// The closure will only be invoked if data race handling is on. fn release_clock(&self, callback: impl FnOnce(&VClock) -> R) -> Option { let this = self.eval_context_ref(); + // FIXME: make this a proper error instead of ICEing the interpreter. + assert!( + this.machine.data_race.as_genmc_ref().is_none(), + "this operation performs synchronization that is not supported in GenMC mode" + ); Some( this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback), ) @@ -973,7 +977,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { match &this.machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(_genmc_ctx) => - throw_unsup_format!("acquire_clock is not (yet) supported in GenMC mode."), + throw_unsup_format!( + "this operation performs synchronization that is not supported in GenMC mode" + ), GlobalDataRaceHandler::Vclocks(data_race) => data_race.acquire_clock(clock, &this.machine.threads), } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index 66116d5cf23c7..34933d423f064 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,4 +1,7 @@ -use genmc_sys::{GenmcParams, LogLevel}; +use genmc_sys::LogLevel; + +use super::GenmcParams; +use crate::{IsolatedOp, MiriConfig, RejectOpWith}; /// Configuration for GenMC mode. /// The `params` field is shared with the C++ side. @@ -7,6 +10,9 @@ use genmc_sys::{GenmcParams, LogLevel}; pub struct GenmcConfig { /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, + /// Print the output message that GenMC generates when an error occurs. + /// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced. + pub(super) print_genmc_output: bool, /// The log level for GenMC. pub(super) log_level: LogLevel, } @@ -36,9 +42,61 @@ impl GenmcConfig { }; if let Some(log_level) = trimmed_arg.strip_prefix("log=") { genmc_config.log_level = log_level.parse()?; + } else if let Some(trimmed_arg) = trimmed_arg.strip_prefix("print-exec-graphs") { + use genmc_sys::ExecutiongraphPrinting; + genmc_config.params.print_execution_graphs = match trimmed_arg { + "=none" => ExecutiongraphPrinting::None, + // Make GenMC print explored executions. + "" | "=explored" => ExecutiongraphPrinting::Explored, + // Make GenMC print blocked executions. + "=blocked" => ExecutiongraphPrinting::Blocked, + // Make GenMC print all executions. + "=all" => ExecutiongraphPrinting::ExploredAndBlocked, + _ => + return Err(format!( + "Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'" + )), + } + } else if trimmed_arg == "print-genmc-output" { + genmc_config.print_genmc_output = true; } else { return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); } Ok(()) } + + /// Validate settings for GenMC mode (NOP if GenMC mode disabled). + /// + /// Unsupported configurations return an error. + /// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user. + pub fn validate_genmc_mode_settings(miri_config: &mut MiriConfig) -> Result<(), &'static str> { + let Some(genmc_config) = miri_config.genmc_config.as_mut() else { + return Ok(()); + }; + + // Check for disallowed configurations. + if !miri_config.data_race_detector { + return Err("Cannot disable data race detection in GenMC mode"); + } else if !miri_config.native_lib.is_empty() { + return Err("native-lib not supported in GenMC mode."); + } else if miri_config.isolated_op != IsolatedOp::Reject(RejectOpWith::Abort) { + return Err("Cannot disable isolation in GenMC mode"); + } + + // Adjust settings where needed. + if !miri_config.weak_memory_emulation { + genmc_config.params.disable_weak_memory_emulation = true; + } + if miri_config.borrow_tracker.is_some() { + eprintln!( + "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." + ); + miri_config.borrow_tracker = None; + } + // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway + // would be a NOP in GenMC mode. + miri_config.fixed_scheduling = true; + + Ok(()) + } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 7c420961ec54a..92b34b83ee0fc 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -94,9 +94,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, + _atomic_op: AtomicRmwOp, _is_signed: bool, _ordering: AtomicRwOrd, - _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { @@ -242,4 +242,10 @@ impl GenmcConfig { Err(format!("GenMC is not supported on this target")) } } + + pub fn validate_genmc_mode_settings( + _miri_config: &mut crate::MiriConfig, + ) -> Result<(), &'static str> { + Ok(()) + } } diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index b70ca8faadfed..2a84ca2036676 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,11 +1,16 @@ -use genmc_sys::MemOrdering; +use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::mir; use rustc_middle::ty::ScalarInt; use tracing::debug; use super::GenmcScalar; -use crate::{AtomicReadOrd, AtomicWriteOrd, MiriInterpCx, Scalar, throw_unsup_format}; +use crate::intrinsics::AtomicRmwOp; +use crate::{ + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MiriInterpCx, Scalar, + throw_unsup_format, +}; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; @@ -95,3 +100,48 @@ impl AtomicWriteOrd { } } } + +impl AtomicFenceOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicFenceOrd::Acquire => MemOrdering::Acquire, + AtomicFenceOrd::Release => MemOrdering::Release, + AtomicFenceOrd::AcqRel => MemOrdering::AcquireRelease, + AtomicFenceOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicRwOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicRwOrd::Relaxed => MemOrdering::Relaxed, + AtomicRwOrd::Acquire => MemOrdering::Acquire, + AtomicRwOrd::Release => MemOrdering::Release, + AtomicRwOrd::AcqRel => MemOrdering::AcquireRelease, + AtomicRwOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +/// Convert an atomic binary operation to its GenMC counterpart. +pub(super) fn to_genmc_rmw_op(atomic_op: AtomicRmwOp, is_signed: bool) -> RMWBinOp { + match (atomic_op, is_signed) { + (AtomicRmwOp::Min, true) => RMWBinOp::Min, + (AtomicRmwOp::Max, true) => RMWBinOp::Max, + (AtomicRmwOp::Min, false) => RMWBinOp::UMin, + (AtomicRmwOp::Max, false) => RMWBinOp::UMax, + (AtomicRmwOp::MirOp { op, neg }, _is_signed) => + match (op, neg) { + (mir::BinOp::Add, false) => RMWBinOp::Add, + (mir::BinOp::Sub, false) => RMWBinOp::Sub, + (mir::BinOp::BitXor, false) => RMWBinOp::Xor, + (mir::BinOp::BitAnd, false) => RMWBinOp::And, + (mir::BinOp::BitAnd, true) => RMWBinOp::Nand, + (mir::BinOp::BitOr, false) => RMWBinOp::Or, + _ => { + panic!("unsupported atomic operation: bin_op: {op:?}, negate: {neg}"); + } + }, + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 26ce0b9c43ac7..c1da86c673364 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, UniquePtr, + GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, RMWBinOp, UniquePtr, create_genmc_driver_handle, }; use rustc_abi::{Align, Size}; @@ -12,7 +12,9 @@ use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; -use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; +use self::helper::{ + MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar, to_genmc_rmw_op, +}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; @@ -29,6 +31,8 @@ mod run; pub(crate) mod scheduling; mod thread_id_map; +pub use genmc_sys::GenmcParams; + pub use self::config::GenmcConfig; pub use self::run::run_genmc_mode; @@ -126,22 +130,19 @@ impl GenmcCtx { /// Get the number of blocked executions encountered by GenMC. pub fn get_blocked_execution_count(&self) -> u64 { - let mc = self.handle.borrow(); - mc.as_ref().unwrap().get_blocked_execution_count() + self.handle.borrow().get_blocked_execution_count() } /// Get the number of explored executions encountered by GenMC. pub fn get_explored_execution_count(&self) -> u64 { - let mc = self.handle.borrow(); - mc.as_ref().unwrap().get_explored_execution_count() + self.handle.borrow().get_explored_execution_count() } /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. pub fn try_get_error(&self) -> Option { - let mc = self.handle.borrow(); - mc.as_ref() - .unwrap() + self.handle + .borrow() .get_error_string() .as_ref() .map(|error| error.to_string_lossy().to_string()) @@ -150,9 +151,8 @@ impl GenmcCtx { /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. pub fn get_result_message(&self) -> String { - let mc = self.handle.borrow(); - mc.as_ref() - .unwrap() + self.handle + .borrow() .get_result_message() .as_ref() .map(|error| error.to_string_lossy().to_string()) @@ -163,8 +163,7 @@ impl GenmcCtx { /// /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. pub fn is_exploration_done(&self) -> bool { - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().is_exploration_done() + self.handle.borrow_mut().pin_mut().is_exploration_done() } /// Select whether data race free actions should be allowed. This function should be used carefully! @@ -196,8 +195,7 @@ impl GenmcCtx { // Reset per-execution state. self.exec_state.reset(); // Inform GenMC about the new execution. - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_execution_start(); + self.handle.borrow_mut().pin_mut().handle_execution_start(); } /// Inform GenMC that the program's execution has ended. @@ -214,13 +212,11 @@ impl GenmcCtx { /// /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. fn handle_execution_end(&self) -> Option { - let mut mc = self.handle.borrow_mut(); - let result = mc.as_mut().unwrap().handle_execution_end(); + let result = self.handle.borrow_mut().pin_mut().handle_execution_end(); result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; // GenMC currently does not return an error value immediately in all cases. // We manually query for any errors here to ensure we don't miss any. - drop(mc); // `try_get_error` needs access to the `RefCell`. self.try_get_error() } @@ -282,11 +278,17 @@ impl GenmcCtx { /// Inform GenMC about an atomic fence. pub(crate) fn atomic_fence<'tcx>( &self, - _machine: &MiriMachine<'tcx>, - _ordering: AtomicFenceOrd, + machine: &MiriMachine<'tcx>, + ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); - throw_unsup_format!("FIXME(genmc): Add support for atomic fences.") + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + + self.handle.borrow_mut().pin_mut().handle_fence(genmc_tid, ordering.to_genmc()); + interp_ok(()) } /// Inform GenMC about an atomic read-modify-write operation. @@ -296,20 +298,24 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_rmw_op<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _is_signed: bool, - _ordering: AtomicRwOrd, - _atomic_op: AtomicRmwOp, - _rhs_scalar: Scalar, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + atomic_op: AtomicRmwOp, + is_signed: bool, + ordering: AtomicRwOrd, + rhs_scalar: Scalar, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic read-modify-write operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") + self.handle_atomic_rmw_op( + ecx, + address, + size, + ordering, + to_genmc_rmw_op(atomic_op, is_signed), + scalar_to_genmc_scalar(ecx, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, old_value)?, + ) } /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. @@ -317,18 +323,22 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_exchange<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _rhs_scalar: Scalar, - _ordering: AtomicRwOrd, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + rhs_scalar: Scalar, + ordering: AtomicRwOrd, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic swap operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic swap.") + self.handle_atomic_rmw_op( + ecx, + address, + size, + ordering, + /* genmc_rmw_op */ RMWBinOp::Xchg, + scalar_to_genmc_scalar(ecx, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, old_value)?, + ) } /// Inform GenMC about an atomic compare-exchange operation. @@ -493,9 +503,11 @@ impl GenmcCtx { // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let chosen_address = pinned_mc.handle_malloc(genmc_tid, genmc_size, alignment.bytes()); + let chosen_address = self.handle.borrow_mut().pin_mut().handle_malloc( + genmc_tid, + genmc_size, + alignment.bytes(), + ); // Non-global addresses should not be in the global address space or null. assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); @@ -530,9 +542,7 @@ impl GenmcCtx { let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - pinned_mc.handle_free(genmc_tid, address.bytes()); + self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()); interp_ok(()) } @@ -554,9 +564,7 @@ impl GenmcCtx { let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_new_tid = thread_infos.add_thread(new_thread_id); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_create(genmc_new_tid, genmc_parent_tid); - + self.handle.borrow_mut().pin_mut().handle_thread_create(genmc_new_tid, genmc_parent_tid); interp_ok(()) } @@ -571,8 +579,7 @@ impl GenmcCtx { let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_join(genmc_curr_tid, genmc_child_tid); + self.handle.borrow_mut().pin_mut().handle_thread_join(genmc_curr_tid, genmc_child_tid); interp_ok(()) } @@ -585,9 +592,8 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); - let mut mc = self.handle.borrow_mut(); // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 - mc.as_mut().unwrap().handle_thread_finish(genmc_tid, /* ret_val */ 0); + self.handle.borrow_mut().pin_mut().handle_thread_finish(genmc_tid, /* ret_val */ 0); } /// Handle a call to `libc::exit` or the exit of the main thread. @@ -618,8 +624,7 @@ impl GenmcCtx { let thread_infos = self.exec_state.thread_id_manager.borrow(); let genmc_tid = thread_infos.get_genmc_tid(thread); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_kill(genmc_tid); + self.handle.borrow_mut().pin_mut().handle_thread_kill(genmc_tid); } else { assert_eq!(thread, ThreadId::MAIN_THREAD); } @@ -669,9 +674,7 @@ impl GenmcCtx { addr = address.bytes() ); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let load_result = pinned_mc.handle_load( + let load_result = self.handle.borrow_mut().pin_mut().handle_load( genmc_tid, address.bytes(), size.bytes(), @@ -722,9 +725,7 @@ impl GenmcCtx { addr = address.bytes() ); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let store_result = pinned_mc.handle_store( + let store_result = self.handle.borrow_mut().pin_mut().handle_store( genmc_tid, address.bytes(), size.bytes(), @@ -741,6 +742,62 @@ impl GenmcCtx { interp_ok(store_result.is_coherence_order_maximal_write) } + /// Inform GenMC about an atomic read-modify-write operation. + /// This includes atomic swap (also often called "exchange"), but does *not* + /// include compare-exchange (see `RMWBinOp` for full list of operations). + /// Returns the previous value at that memory location, and optionally the value that should be written back to Miri's memory. + fn handle_atomic_rmw_op<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + ordering: AtomicRwOrd, + genmc_rmw_op: RMWBinOp, + genmc_rhs_scalar: GenmcScalar, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic read-modify-write operation with data race checking disabled." + ); + assert_ne!(0, size.bytes()); + assert!( + size.bytes() <= MAX_ACCESS_SIZE, + "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)", + MAX_ACCESS_SIZE, + size.bytes() + ); + + let curr_thread_id = ecx.machine.threads.active_thread(); + let genmc_tid = self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread_id); + debug!( + "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", + ); + let rmw_result = self.handle.borrow_mut().pin_mut().handle_read_modify_write( + genmc_tid, + address.bytes(), + size.bytes(), + genmc_rmw_op, + ordering.to_genmc(), + genmc_rhs_scalar, + genmc_old_value, + ); + + if let Some(error) = rmw_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; + + let new_value_scalar = if rmw_result.is_coherence_order_maximal_write { + Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) + } else { + None + }; + interp_ok((old_value_scalar, new_value_scalar)) + } + /**** Blocking functionality ****/ /// Handle a user thread getting blocked. diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 62553297a2024..24d13e6375b11 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -18,6 +18,8 @@ pub fn run_genmc_mode<'tcx>( eval_entry: impl Fn(Rc) -> Option, tcx: TyCtxt<'tcx>, ) -> Option { + let genmc_config = config.genmc_config.as_ref().unwrap(); + // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. @@ -32,8 +34,15 @@ pub fn run_genmc_mode<'tcx>( genmc_ctx.prepare_next_execution(); // Execute the program until completion to get the return value, or return if an error happens: - // FIXME(genmc): add an option to allow the user to see the GenMC output message when the verification is done. - let return_code = eval_entry(genmc_ctx.clone())?; + let Some(return_code) = eval_entry(genmc_ctx.clone()) else { + // If requested, print the output GenMC produced: + if genmc_config.print_genmc_output { + eprintln!("== raw GenMC output ========================="); + eprintln!("{}", genmc_ctx.get_result_message()); + eprintln!("== end of raw GenMC output =================="); + } + return None; + }; // We inform GenMC that the execution is complete. If there was an error, we print it. if let Some(error) = genmc_ctx.handle_execution_end() { diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs new file mode 100644 index 0000000000000..508eae756f320 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs @@ -0,0 +1,66 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche + +// This is the test `checks_fail` from loom/test/smoke.rs adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/smoke.rs + +// This test checks that an incorrect implementation of an incrementing counter is detected. +// The counter behaves wrong if two threads try to increment at the same time (increments can be lost). + +#![no_main] + +#[cfg(not(any(non_genmc_std, genmc_std)))] +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +struct BuggyInc { + num: AtomicUsize, +} + +impl BuggyInc { + const fn new() -> BuggyInc { + BuggyInc { num: AtomicUsize::new(0) } + } + + fn inc(&self) { + // The bug is here: + // Another thread can increment `self.num` between the next two lines, + // which is then overridden by this thread. + let curr = self.num.load(Acquire); + self.num.store(curr + 1, Release); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + static BUGGY_INC: BuggyInc = BuggyInc::new(); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + BUGGY_INC.num.store(0, Relaxed); + + let ids = [ + spawn_pthread_closure(|| { + BUGGY_INC.inc(); + }), + spawn_pthread_closure(|| { + BUGGY_INC.inc(); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // We check that we can detect the incorrect counter implementation: + if 2 != BUGGY_INC.num.load(Relaxed) { + std::process::abort(); //~ ERROR: abnormal termination + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr new file mode 100644 index 0000000000000..5a8948ff9b4b8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/buggy_inc.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index e2b114df652f8..cbfb6ec833eb0 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index e2b114df652f8..cbfb6ec833eb0 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs index dea2b5f952e7e..baf3584966ec8 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs @@ -7,7 +7,7 @@ // When using any combination of orderings except using all 4 `SeqCst`, the memory model allows the program to result in (X, Y) == (1, 1). // The "pass" variants only check that we get the expected number of executions (3 for all SC, 4 otherwise), // and a valid outcome every execution, but do not check that we get all allowed results. -// This "fail" variant ensures we can explore the execution resulting in (1, 1), an incorrect unsafe assumption that the result (1, 1) is impossible. +// This "fail" variant ensures we can explore the execution resulting in (1, 1), with an incorrect assumption that the result (1, 1) is impossible. // // Miri without GenMC is unable to produce this program execution and thus detect the incorrect assumption, even with `-Zmiri-many-seeds`. // @@ -61,11 +61,10 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { // Join so we can read the final values. join_pthreads(ids); - // We mark the result (1, 1) as unreachable, which is incorrect. + // We incorrectly assume that the result (1, 1) as unreachable. let result = (X.load(Relaxed), Y.load(Relaxed)); if result == (1, 1) { - // FIXME(genmc): Use `std::process::abort()` once backtraces for that are improved (https://github.com/rust-lang/rust/pull/146118) - std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code + std::process::abort(); //~ ERROR: abnormal termination } 0 diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index e2b114df652f8..cbfb6ec833eb0 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs new file mode 100644 index 0000000000000..f48466e8ce10c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs @@ -0,0 +1,91 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// This test check for correct handling of atomic read-modify-write operations for all integer sizes. +// Atomic max and min should return the previous value, and store the result in the atomic. +// Atomic addition and subtraction should have wrapping semantics. +// `and`, `nand`, `or`, `xor` should behave like their non-atomic counterparts. + +// FIXME(genmc): add 128 bit atomics for platforms that support it, once GenMC gets 128 bit atomic support + +#![no_main] + +use std::sync::atomic::*; + +const ORD: Ordering = Ordering::SeqCst; + +fn assert_eq(x: T, y: T) { + if x != y { + std::process::abort(); + } +} + +macro_rules! test_rmw_edge_cases { + ($int:ty, $atomic:ty) => {{ + let x = <$atomic>::new(123); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + x.store(123, ORD); + + // MAX, ADD + assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value + assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value + assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping + assert_eq(<$int>::MAX.wrapping_add(10), x.load(ORD)); + + // MIN, SUB + x.store(42, ORD); + assert_eq(42, x.fetch_min(<$int>::MAX, ORD)); // `min` keeps existing value + assert_eq(42, x.fetch_min(<$int>::MIN, ORD)); // `min` stores the new value + assert_eq(<$int>::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping + assert_eq(<$int>::MIN.wrapping_sub(10), x.load(ORD)); + + // Small enough pattern to work for all integer sizes. + let pattern = 0b01010101; + + // AND + x.store(!0, ORD); + assert_eq(!0, x.fetch_and(pattern, ORD)); + assert_eq(!0 & pattern, x.load(ORD)); + + // NAND + x.store(!0, ORD); + assert_eq(!0, x.fetch_nand(pattern, ORD)); + assert_eq(!(!0 & pattern), x.load(ORD)); + + // OR + x.store(!0, ORD); + assert_eq(!0, x.fetch_or(pattern, ORD)); + assert_eq(!0 | pattern, x.load(ORD)); + + // XOR + x.store(!0, ORD); + assert_eq(!0, x.fetch_xor(pattern, ORD)); + assert_eq(!0 ^ pattern, x.load(ORD)); + + // SWAP + x.store(!0, ORD); + assert_eq(!0, x.swap(pattern, ORD)); + assert_eq(pattern, x.load(ORD)); + + // Check correct behavior of atomic min/max combined with overflowing add/sub. + x.store(10, ORD); + assert_eq(10, x.fetch_add(<$int>::MAX, ORD)); // definitely overflows, so new value of x is smaller than 10 + assert_eq(<$int>::MAX.wrapping_add(10), x.fetch_max(10, ORD)); // new value of x should be 10 + // assert_eq(10, x.load(ORD)); // FIXME(genmc,#4572): enable this check once GenMC correctly handles min/max truncation. + }}; +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + test_rmw_edge_cases!(u8, AtomicU8); + test_rmw_edge_cases!(u16, AtomicU16); + test_rmw_edge_cases!(u32, AtomicU32); + test_rmw_edge_cases!(u64, AtomicU64); + test_rmw_edge_cases!(usize, AtomicUsize); + test_rmw_edge_cases!(i8, AtomicI8); + test_rmw_edge_cases!(i16, AtomicI16); + test_rmw_edge_cases!(i32, AtomicI32); + test_rmw_edge_cases!(i64, AtomicI64); + test_rmw_edge_cases!(isize, AtomicIsize); + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr new file mode 100644 index 0000000000000..3b22247ee44cb --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs new file mode 100644 index 0000000000000..3b3fca02285d6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs @@ -0,0 +1,33 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W+2sc+scf". +// It tests correct handling of SeqCst fences combined with relaxed accesses. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }); + spawn_pthread_closure(|| { + Y.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + X.store(2, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs index bbe9995dea0b2..f47f5a11c5c96 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs @@ -1,9 +1,13 @@ +//@revisions: weak sc //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation // Translated from GenMC's test "2+2W". // // The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. // Check "2w2w_weak.rs" for a more detailed description. +// +// The `sc` variant of this test checks that without weak memory emulation, only 3 instead of 4 executions are explored (like the `2w2w_4sc.rs` test). #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr rename to src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs index 79e7c7feb7aac..1ec62ff21342d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -1,6 +1,6 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -// Translated from GenMC's "IRIW-acq-sc" test. +// Translated from GenMC's "litmus/IRIW-acq-sc" test. #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs new file mode 100644 index 0000000000000..163556dcc8975 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -0,0 +1,66 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/IRIWish" test. +// This test prints the values read by the different threads to check that we get all the values we expect. + +// NOTE: the order of the lines in the output may change with changes to GenMC. +// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing. + +// NOTE: GenMC supports instruction caching and does not need replay completed threads. +// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut results = [1234; 5]; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + let r1 = X.load(Relaxed); + Y.store(r1, Release); + results[0] = r1; + }), + spawn_pthread_closure(|| { + results[1] = X.load(Relaxed); + std::sync::atomic::fence(AcqRel); + results[2] = Y.load(Relaxed); + }), + spawn_pthread_closure(|| { + results[3] = Y.load(Relaxed); + std::sync::atomic::fence(AcqRel); + results[4] = X.load(Relaxed); + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + if writeln!(MiriStderr, "{results:?}").is_err() { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr new file mode 100644 index 0000000000000..4fc77b63f3803 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr @@ -0,0 +1,30 @@ +[1, 1, 1, 1, 1] +[1, 1, 1, 0, 1] +[1, 1, 1, 0, 0] +[1, 1, 0, 1, 1] +[1, 1, 0, 0, 1] +[1, 1, 0, 0, 0] +[1, 0, 1, 1, 1] +[1, 0, 1, 0, 1] +[1, 0, 1, 0, 0] +[1, 0, 0, 1, 1] +[1, 0, 0, 0, 1] +[1, 0, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 28 diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs new file mode 100644 index 0000000000000..e43d92fc6c55a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/LB+incMPs" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static W: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.load(Acquire); + Z.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + Z.fetch_add(1, AcqRel); + Y.store(1, Release); + }); + spawn_pthread_closure(|| { + Y.load(Acquire); + W.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + W.fetch_add(1, AcqRel); + X.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr new file mode 100644 index 0000000000000..c879e95a17085 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 15 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs new file mode 100644 index 0000000000000..b36c8a288f426 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MPU+rels+acq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU+rels+acq/mpu+rels+acq.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + if Y.load(Acquire) > 1 { + X.store(2, Relaxed); + } + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr new file mode 100644 index 0000000000000..c5e34b00642aa --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 13 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs new file mode 100644 index 0000000000000..a08b7de27d13c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs @@ -0,0 +1,36 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP+incMP" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + Y.load(Acquire); + Z.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + Z.fetch_add(1, AcqRel); + X.load(Acquire); + }); + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr new file mode 100644 index 0000000000000..2be9a6c7fbded --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs new file mode 100644 index 0000000000000..cf9f5f2dbfa34 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP+rels+acqf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MP+rels+acqf/mp+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + if Y.load(Relaxed) != 0 { + std::sync::atomic::fence(Acquire); + let _x = X.load(Relaxed); + } + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs new file mode 100644 index 0000000000000..ffc44de1bc7cf --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/SB+2sc+scf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.load(SeqCst); + }); + spawn_pthread_closure(|| { + Y.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs new file mode 100644 index 0000000000000..ac5c3724254d6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -0,0 +1,65 @@ +//@revisions: weak sc +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation + +// Translated from GenMC's "litmus/Z6.U" test. +// +// The `sc` variant of this test checks that we get fewer executions when weak memory emulation is disabled. + +// NOTE: the order of the lines in the output may change with changes to GenMC. +// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing. + +// NOTE: GenMC supports instruction caching and does not need replay completed threads. +// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + Y.fetch_add(1, SeqCst); + a = Y.load(Relaxed); + }), + spawn_pthread_closure(|| { + Y.store(3, SeqCst); + b = X.load(SeqCst); + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + if writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) + .is_err() + { + std::process::abort(); + } + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr new file mode 100644 index 0000000000000..8dd2fbe1b9757 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr @@ -0,0 +1,20 @@ +a=2, b=1, X=1, Y=3 +a=4, b=1, X=1, Y=4 +a=3, b=1, X=1, Y=3 +a=2, b=1, X=1, Y=2 +a=2, b=0, X=1, Y=2 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=4, b=1, X=1, Y=1 +a=4, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=3, b=1, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=3, b=1, X=1, Y=1 +a=3, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 18 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr new file mode 100644 index 0000000000000..65622575de21d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr @@ -0,0 +1,24 @@ +a=2, b=1, X=1, Y=3 +a=4, b=1, X=1, Y=4 +a=4, b=0, X=1, Y=4 +a=3, b=1, X=1, Y=3 +a=2, b=1, X=1, Y=2 +a=2, b=0, X=1, Y=2 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=4, b=1, X=1, Y=1 +a=4, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=0, X=1, Y=3 +a=3, b=1, X=1, Y=3 +a=3, b=0, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=3, b=1, X=1, Y=1 +a=3, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=0, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 22 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs new file mode 100644 index 0000000000000..b00f3a59ce672 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs @@ -0,0 +1,38 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/Z6+acq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.load(Acquire); + Z.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Z.store(2, Relaxed); + std::sync::atomic::fence(SeqCst); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr new file mode 100644 index 0000000000000..2be9a6c7fbded --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs new file mode 100644 index 0000000000000..75be89893dab8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/atomicpo". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + std::sync::atomic::fence(AcqRel); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.swap(1, Relaxed); + X.swap(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr new file mode 100644 index 0000000000000..0667962f99c88 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs new file mode 100644 index 0000000000000..2387976a8ca61 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs @@ -0,0 +1,58 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/cumul-release". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + a = Y.load(Relaxed); + Z.store(a, Relaxed); + }), + spawn_pthread_closure(|| { + b = Z.load(Relaxed); + std::sync::atomic::fence(AcqRel); + c = X.load(Relaxed); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!( + (a, b, c), + (0, 0, 0) | (0, 0, 1) | (1, 0, 0) | (1, 0, 1) | (1, 1, 0) | (1, 1, 1) + ) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr new file mode 100644 index 0000000000000..00394048ec5c6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 8 diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs index 9a08d517b7c60..796ffbf97f96b 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -38,7 +38,6 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { result[3] = X.load(Relaxed); }), ]; - // Join so we can read the final values. join_pthreads(ids); diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs new file mode 100644 index 0000000000000..fd8574c4f7c78 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/inc2w". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(4, Release); + }), + spawn_pthread_closure(|| { + X.fetch_add(2, Relaxed); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let x = X.load(Relaxed); + if !matches!(x, 4..=7) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr new file mode 100644 index 0000000000000..be75e68fde77d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs new file mode 100644 index 0000000000000..40ca486318598 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs @@ -0,0 +1,63 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +static mut A: u64 = 0; +static mut B: u64 = 0; +static mut C: u64 = 0; +static mut D: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4, thread_5]; + let ids = unsafe { spawn_pthreads_no_params(thread_order) }; + unsafe { join_pthreads(ids) }; + + if unsafe { A == 42 && B == 2 && C == 1 && D == 42 } { + std::process::abort(); + } + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + unsafe { + A = X.load(Relaxed); + B = X.load(Relaxed); + } + std::ptr::null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.store(42, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { + unsafe { + C = X.load(Relaxed); + D = X.load(Relaxed); + } + std::ptr::null_mut() +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr new file mode 100644 index 0000000000000..b5f8cd15b683f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 600 diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.rs b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs new file mode 100644 index 0000000000000..49564c8e4fe0a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs @@ -0,0 +1,31 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/riwi" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + Y.load(Relaxed); + X.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + Y.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr new file mode 100644 index 0000000000000..115b1986ce557 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs new file mode 100644 index 0000000000000..a193dcf0683ea --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs @@ -0,0 +1,36 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/viktor-relseq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static LOCK: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Acquire); + LOCK.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Relaxed); + LOCK.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Release); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr new file mode 100644 index 0000000000000..d63ac5199d5d4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 180 diff --git a/src/tools/miri/tests/utils/genmc.rs b/src/tools/miri/tests/utils/genmc.rs index fb6334bd06086..29b3181992b11 100644 --- a/src/tools/miri/tests/utils/genmc.rs +++ b/src/tools/miri/tests/utils/genmc.rs @@ -51,6 +51,13 @@ pub unsafe fn join_pthread(thread_id: pthread_t) { } } +/// Spawn `N` threads using `pthread_create` without any arguments, abort the process on any errors. +pub unsafe fn spawn_pthreads_no_params( + functions: [extern "C" fn(*mut c_void) -> *mut c_void; N], +) -> [pthread_t; N] { + functions.map(|func| spawn_pthread(func, std::ptr::null_mut())) +} + // Join the `N` given pthreads, abort the process on any errors. pub unsafe fn join_pthreads(thread_ids: [pthread_t; N]) { let _ = thread_ids.map(|id| join_pthread(id)); From 1806a8244e96e3cb2be8dc93d95e350f81505083 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 8 Sep 2025 06:20:07 +0200 Subject: [PATCH 0249/1900] remove confusing parts of sentence I can guess what this meant, but decided to just keep it simple --- src/doc/rustc-dev-guide/src/profiling/with_perf.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/with_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_perf.md index 0d4f23bcd9ad3..e452dde5226d4 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with_perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with_perf.md @@ -4,8 +4,7 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or ## Initial steps -- Get a clean checkout of rust-lang/master, or whatever it is you want - to profile. +- Get a clean checkout of rust-lang/rust - Set the following settings in your `bootstrap.toml`: - `rust.debuginfo-level = 1` - enables line debuginfo - `rust.jemalloc = false` - lets you do memory use profiling with valgrind From 755db7fdac7217dc320cd2649e213df63e6411dd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 8 Sep 2025 06:48:10 +0200 Subject: [PATCH 0250/1900] sembr --- .../bootstrapping/writing-tools-in-bootstrap.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md index c3660e24b1526..f5f4dfadce27f 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md @@ -4,16 +4,22 @@ There are three types of tools you can write in bootstrap: - **`Mode::ToolBootstrap`** Use this for tools that don’t need anything from the in-tree compiler and can run with the stage0 `rustc`. - The output is placed in the "bootstrap-tools" directory. This mode is for general-purpose tools built - entirely with the stage0 compiler, including target libraries and only works for stage 0. + The output is placed in the "bootstrap-tools" directory. + This mode is for general-purpose tools built entirely with the stage0 compiler, + including target libraries and only works for stage 0. - **`Mode::ToolStd`** - Use this for tools that rely on the locally built std. The output goes into the "stageN-tools" directory. + Use this for tools that rely on the locally built std. + The output goes into the "stageN-tools" directory. This mode is rarely used, mainly for `compiletest` which requires `libtest`. - **`Mode::ToolRustcPrivate`** - Use this for tools that use the `rustc_private` mechanism, and thus depend on the locally built `rustc` and its rlib artifacts. This is more complex than the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" directory. When you choose `Mode::ToolRustcPrivate`, `ToolBuild` implementation takes care of this automatically. If you need to use the builder’s compiler for something specific, you can get it from `ToolBuildResult`, which is - returned by the tool's [`Step`]. + Use this for tools that use the `rustc_private` mechanism, + and thus depend on the locally built `rustc` and its rlib artifacts. + This is more complex than the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" directory. + When you choose `Mode::ToolRustcPrivate`, `ToolBuild` implementation takes care of this automatically. + If you need to use the builder’s compiler for something specific, + you can get it from `ToolBuildResult`, which is returned by the tool's [`Step`]. Regardless of the tool type you must return `ToolBuildResult` from the tool’s [`Step`] implementation and use `ToolBuild` inside it. From cb0e26ff4c40bc7e8e05d62f12ca685928a5f130 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 8 Sep 2025 06:50:14 +0200 Subject: [PATCH 0251/1900] fix markup --- .../src/building/bootstrapping/writing-tools-in-bootstrap.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md index f5f4dfadce27f..8bda2257536a4 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md @@ -3,17 +3,20 @@ There are three types of tools you can write in bootstrap: - **`Mode::ToolBootstrap`** + Use this for tools that don’t need anything from the in-tree compiler and can run with the stage0 `rustc`. The output is placed in the "bootstrap-tools" directory. This mode is for general-purpose tools built entirely with the stage0 compiler, including target libraries and only works for stage 0. - **`Mode::ToolStd`** + Use this for tools that rely on the locally built std. The output goes into the "stageN-tools" directory. This mode is rarely used, mainly for `compiletest` which requires `libtest`. - **`Mode::ToolRustcPrivate`** + Use this for tools that use the `rustc_private` mechanism, and thus depend on the locally built `rustc` and its rlib artifacts. This is more complex than the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" directory. From 94da52007bf7583a833911c92cb58f9e90544542 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 8 Sep 2025 06:54:04 +0200 Subject: [PATCH 0252/1900] readability --- .../src/building/bootstrapping/writing-tools-in-bootstrap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md index 8bda2257536a4..8250a6f3b51df 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md @@ -7,7 +7,7 @@ There are three types of tools you can write in bootstrap: Use this for tools that don’t need anything from the in-tree compiler and can run with the stage0 `rustc`. The output is placed in the "bootstrap-tools" directory. This mode is for general-purpose tools built entirely with the stage0 compiler, - including target libraries and only works for stage 0. + including target libraries, and it only works for stage 0. - **`Mode::ToolStd`** From 344b4ac427999c382f575308af3569b2148485a9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 07:55:10 +0200 Subject: [PATCH 0253/1900] print proper error when using unsupported synchronization primitive with GenMC --- src/tools/miri/src/alloc_addresses/mod.rs | 2 + src/tools/miri/src/concurrency/data_race.rs | 22 +++--- src/tools/miri/src/concurrency/init_once.rs | 10 +-- src/tools/miri/src/concurrency/sync.rs | 68 ++++++------------- .../miri/src/shims/unix/linux_like/epoll.rs | 2 +- .../miri/src/shims/unix/linux_like/eventfd.rs | 2 +- src/tools/miri/src/shims/unix/macos/sync.rs | 4 +- src/tools/miri/src/shims/unix/sync.rs | 16 ++--- .../miri/src/shims/unix/unnamed_socket.rs | 2 +- 9 files changed, 52 insertions(+), 76 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 5d94a1f4138a6..f011ee717850d 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -498,6 +498,8 @@ impl<'tcx> MiriMachine<'tcx> { // Also remember this address for future reuse. let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { + // We already excluded GenMC above. We cannot use `self.release_clock` as + // `self.alloc_addresses` is borrowed. if let Some(data_race) = self.data_race.as_vclocks_ref() { data_race.release_clock(&self.threads, |clock| clock.clone()) } else { diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 30061a78594aa..de60e80b9f7eb 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -958,16 +958,20 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// with this program point. /// /// The closure will only be invoked if data race handling is on. - fn release_clock(&self, callback: impl FnOnce(&VClock) -> R) -> Option { + fn release_clock( + &self, + callback: impl FnOnce(&VClock) -> R, + ) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); - // FIXME: make this a proper error instead of ICEing the interpreter. - assert!( - this.machine.data_race.as_genmc_ref().is_none(), - "this operation performs synchronization that is not supported in GenMC mode" - ); - Some( - this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback), - ) + interp_ok(match &this.machine.data_race { + GlobalDataRaceHandler::None => None, + GlobalDataRaceHandler::Genmc(_genmc_ctx) => + throw_unsup_format!( + "this operation performs synchronization that is not supported in GenMC mode" + ), + GlobalDataRaceHandler::Vclocks(data_race) => + Some(data_race.release_clock(&this.machine.threads, callback)), + }) } /// Acquire the given clock into the current thread, establishing synchronization with diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 3fb3d890419f7..daea20b3779b2 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -96,10 +96,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init_once.status = InitOnceStatus::Complete; // Each complete happens-before the end of the wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race - .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); - } + this.release_clock(|clock| init_once.clock.clone_from(clock))?; // Wake up everyone. // need to take the queue to avoid having `this` be borrowed multiple times @@ -125,10 +122,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init_once.status = InitOnceStatus::Uninitialized; // Each complete happens-before the end of the wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race - .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); - } + this.release_clock(|clock| init_once.clock.clone_from(clock))?; // Wake up one waiting thread, so they can go ahead and try to init this. if let Some(waiter) = init_once.waiters.pop_front() { diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 179094db0febd..15d486c27e36a 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -204,7 +204,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest))); } else { // We can have it right now! - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; // Don't forget to write the return value. this.write_scalar(retval, &dest)?; } @@ -338,7 +338,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Lock by setting the mutex owner and increasing the lock count. - fn mutex_lock(&mut self, mutex_ref: &MutexRef) { + fn mutex_lock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); let mut mutex = mutex_ref.0.borrow_mut(); @@ -352,9 +352,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex.owner = Some(thread); } mutex.lock_count = mutex.lock_count.strict_add(1); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&mutex.clock, &this.machine.threads); - } + this.acquire_clock(&mutex.clock)?; + interp_ok(()) } /// Try unlocking by decreasing the lock count and returning the old lock @@ -377,11 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The mutex is completely unlocked. Try transferring ownership // to another thread. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| { - mutex.clock.clone_from(clock) - }); - } + this.release_clock(|clock| mutex.clock.clone_from(clock))?; let thread_id = mutex.queue.pop_front(); // We need to drop our mutex borrow before unblock_thread // because it will be borrowed again in the unblock callback. @@ -425,7 +420,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(unblock, UnblockKind::Ready); assert!(mutex_ref.owner().is_none()); - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; if let Some((retval, dest)) = retval_dest { this.write_scalar(retval, &dest)?; @@ -439,7 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Read-lock the lock by adding the `reader` the list of threads that own /// this lock. - fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) { + fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread); @@ -447,9 +442,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert!(!rwlock.is_write_locked(), "the lock is write locked"); let count = rwlock.readers.entry(thread).or_insert(0); *count = count.strict_add(1); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); - } + this.acquire_clock(&rwlock.clock_unlocked)?; + interp_ok(()) } /// Try read-unlock the lock for the current threads and potentially give the lock to a new owner. @@ -472,12 +466,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock } - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - // Add this to the shared-release clock of all concurrent readers. - data_race.release_clock(&this.machine.threads, |clock| { - rwlock.clock_current_readers.join(clock) - }); - } + // Add this to the shared-release clock of all concurrent readers. + this.release_clock(|clock| rwlock.clock_current_readers.join(clock))?; // The thread was a reader. If the lock is not held any more, give it to a writer. if rwlock.is_locked().not() { @@ -521,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); - this.rwlock_reader_lock(&rwlock_ref); + this.rwlock_reader_lock(&rwlock_ref)?; this.write_scalar(retval, &dest)?; interp_ok(()) } @@ -531,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Lock by setting the writer that owns the lock. #[inline] - fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) { + fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); trace!("rwlock_writer_lock: now held by {:?}", thread); @@ -539,9 +529,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut rwlock = rwlock_ref.0.borrow_mut(); assert!(!rwlock.is_locked(), "the rwlock is already locked"); rwlock.writer = Some(thread); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); - } + this.acquire_clock(&rwlock.clock_unlocked)?; + interp_ok(()) } /// Try to unlock an rwlock held by the current thread. @@ -559,11 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { rwlock.writer = None; trace!("rwlock_writer_unlock: unlocked by {:?}", thread); // Record release clock for next lock holder. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| { - rwlock.clock_unlocked.clone_from(clock) - }); - } + this.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?; // The thread was a writer. // @@ -613,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); - this.rwlock_writer_lock(&rwlock_ref); + this.rwlock_writer_lock(&rwlock_ref)?; this.write_scalar(retval, &dest)?; interp_ok(()) } @@ -663,12 +648,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match unblock { UnblockKind::Ready => { // The condvar was signaled. Make sure we get the clock for that. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock( + this.acquire_clock( &condvar_ref.0.borrow().clock, - &this.machine.threads, - ); - } + )?; // Try to acquire the mutex. // The timeout only applies to the first wait (until the signal), not for mutex acquisition. this.condvar_reacquire_mutex(mutex_ref, retval_succ, dest) @@ -695,9 +677,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut condvar = condvar_ref.0.borrow_mut(); // Each condvar signal happens-before the end of the condvar wake - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| condvar.clock.clone_from(clock)); - } + this.release_clock(|clock| condvar.clock.clone_from(clock))?; let Some(waiter) = condvar.waiters.pop_front() else { return interp_ok(false); }; @@ -736,9 +716,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { UnblockKind::Ready => { let futex = futex_ref.0.borrow(); // Acquire the clock of the futex. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&futex.clock, &this.machine.threads); - } + this.acquire_clock(&futex.clock)?; }, UnblockKind::TimedOut => { // Remove the waiter from the futex. @@ -766,9 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut futex = futex_ref.0.borrow_mut(); // Each futex-wake happens-before the end of the futex wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock)); - } + this.release_clock(|clock| futex.clock.clone_from(clock))?; // Remove `count` of the threads in the queue that match any of the bits in the bitset. // We collect all of them before unblocking because the unblock callback may access the diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index aac880feda40e..3133c149293db 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -608,7 +608,7 @@ fn check_and_update_one_event_interest<'tcx>( // If we are tracking data races, remember the current clock so we can sync with it later. ecx.release_clock(|clock| { event_instance.clock.clone_from(clock); - }); + })?; // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); interp_ok(true) diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 54cc571884dc8..5d4f207d365e1 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -199,7 +199,7 @@ fn eventfd_write<'tcx>( // Future `read` calls will synchronize with this write, so update the FD clock. ecx.release_clock(|clock| { eventfd.clock.borrow_mut().join(clock); - }); + })?; // Store new counter value. eventfd.counter.set(new_count); diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 05616dd5a4287..c4ddff7805ed2 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -296,7 +296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_enqueue_and_block(mutex_ref, None); } else { - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; } interp_ok(()) @@ -321,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // reentrancy. this.write_scalar(Scalar::from_bool(false), dest)?; } else { - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; this.write_scalar(Scalar::from_bool(true), dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 5ad4fd501a607..cefd8b81ac180 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -498,14 +498,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 }; this.write_scalar(Scalar::from_i32(ret), dest)?; @@ -525,14 +525,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck => this.eval_libc_i32("EBUSY"), MutexKind::Recursive => { - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 })) } @@ -600,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), ); } else { - this.rwlock_reader_lock(&rwlock.rwlock_ref); + this.rwlock_reader_lock(&rwlock.rwlock_ref)?; this.write_null(dest)?; } @@ -615,7 +615,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if rwlock.rwlock_ref.is_write_locked() { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { - this.rwlock_reader_lock(&rwlock.rwlock_ref); + this.rwlock_reader_lock(&rwlock.rwlock_ref)?; interp_ok(Scalar::from_i32(0)) } } @@ -648,7 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), ); } else { - this.rwlock_writer_lock(&rwlock.rwlock_ref); + this.rwlock_writer_lock(&rwlock.rwlock_ref)?; this.write_null(dest)?; } @@ -663,7 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if rwlock.rwlock_ref.is_locked() { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { - this.rwlock_writer_lock(&rwlock.rwlock_ref); + this.rwlock_writer_lock(&rwlock.rwlock_ref)?; interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 0585f3ba7471d..81703d6e176bf 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -259,7 +259,7 @@ fn anonsocket_write<'tcx>( // Remember this clock so `read` can synchronize with us. ecx.release_clock(|clock| { writebuf.clock.join(clock); - }); + })?; // Do full write / partial write based on the space available. let write_size = len.min(available_space); let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap(); From a5575f8f051c2379bae4a83a3ee1bd91a269343a Mon Sep 17 00:00:00 2001 From: Ali Nazzal Date: Mon, 8 Sep 2025 11:19:28 +0300 Subject: [PATCH 0254/1900] Fix broken Microsoft URL missing slash --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index b07d3533f59bc..36610f28854b5 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -149,7 +149,7 @@ On Windows, the Powershell commands may give you an error that looks like this: ``` PS C:\Users\vboxuser\rust> ./x ./x : File C:\Users\vboxuser\rust\x.ps1 cannot be loaded because running scripts is disabled on this system. For more -information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170. +information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170. At line:1 char:1 + ./x + ~~~ From deca4895ca0412ea1a646310c0026d66b1230b9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 11:57:19 +0200 Subject: [PATCH 0255/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to a09fbe2c8372643a27a8082236120f95ed4e6bba. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index a51d86c3d6f15..5b3139b25092b 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7 +a09fbe2c8372643a27a8082236120f95ed4e6bba From 24ce839fb953d1172a19479edcb2f75877cf153e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 12:00:36 +0200 Subject: [PATCH 0256/1900] clippy --- src/tools/miri/src/math.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 7d2f1c083687c..254495637da42 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -308,7 +308,7 @@ where Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) } - _ => return None, + _ => None, } } From 94ab2b9f15b3c5ffe139d17fdae98cc2adff883d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 11:57:49 +0200 Subject: [PATCH 0257/1900] move math intrinsics to their own file --- src/tools/miri/src/intrinsics/math.rs | 311 ++++++++++++++++++++++++++ src/tools/miri/src/intrinsics/mod.rs | 291 +----------------------- 2 files changed, 315 insertions(+), 287 deletions(-) create mode 100644 src/tools/miri/src/intrinsics/math.rs diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs new file mode 100644 index 0000000000000..21d4a92e7d288 --- /dev/null +++ b/src/tools/miri/src/intrinsics/math.rs @@ -0,0 +1,311 @@ +use rand::Rng; +use rustc_apfloat::{self, Float, Round}; +use rustc_middle::mir; +use rustc_middle::ty::{self, FloatTy}; + +use self::helpers::{ToHost, ToSoft}; +use super::check_intrinsic_arg_count; +use crate::*; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_math_intrinsic( + &mut self, + intrinsic_name: &str, + _generic_args: ty::GenericArgsRef<'tcx>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + + match intrinsic_name { + // Operations we can do with soft-floats. + "sqrtf32" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + "sqrtf64" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + "fmaf32" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let res = a.mul_add(b, c).value; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "fmaf64" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let res = a.mul_add(b, c).value; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + + "fmuladdf32" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "fmuladdf64" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_intrinsic_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> { + let ty::Float(fty) = x.layout.ty.kind() else { + bug!("float_finite: non-float input type {}", x.layout.ty) + }; + interp_ok(match fty { + FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), + FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), + FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), + FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(), + }) + }; + match (float_finite(&a)?, float_finite(&b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + let res = this.binary_op(op, &a, &b)?; + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `binary_op` already called `generate_nan` if needed.) + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // Apply a relative error of 4ULP to simulate non-deterministic precision loss + // due to optimizations. + let res = math::apply_random_float_error_to_imm(this, res, 4)?; + this.write_immediate(*res, dest)?; + } + + "float_to_int_unchecked" => { + let [val] = check_intrinsic_arg_count(args)?; + let val = this.read_immediate(val)?; + + let res = this + .float_to_int_checked(&val, dest.layout, Round::TowardZero)? + .ok_or_else(|| { + err_ub_format!( + "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })?; + + this.write_immediate(*res, dest)?; + } + + // Operations that need host floats. + #[rustfmt::skip] + | "sinf32" + | "cosf32" + | "expf32" + | "exp2f32" + | "logf32" + | "log10f32" + | "log2f32" + => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf32" => host.sin(), + "cosf32" => host.cos(), + "expf32" => host.exp(), + "exp2f32" => host.exp2(), + "logf32" => host.ln(), + "log10f32" => host.log10(), + "log2f32" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, + res, + 4, + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(intrinsic_name, res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + #[rustfmt::skip] + | "sinf64" + | "cosf64" + | "expf64" + | "exp2f64" + | "logf64" + | "log10f64" + | "log2f64" + => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf64" => host.sin(), + "cosf64" => host.cos(), + "expf64" => host.exp(), + "exp2f64" => host.exp2(), + "logf64" => host.ln(), + "log10f64" => host.log10(), + "log2f64" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, + res, + 4, + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(intrinsic_name, res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + "powf32" => { + let [f1, f2] = check_intrinsic_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; + + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + "powf64" => { + let [f1, f2] = check_intrinsic_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + + "powif32" => { + let [f, i] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + let i = this.read_scalar(i)?.to_i32()?; + + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + "powif64" => { + let [f, i] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + let i = this.read_scalar(i)?.to_i32()?; + + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + _ => return interp_ok(EmulateItemResult::NotSupported), + } + + interp_ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 8d323ec169df3..a80b939d84ea9 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -1,6 +1,7 @@ #![warn(clippy::arithmetic_side_effects)] mod atomic; +mod math; mod simd; pub use self::atomic::AtomicRmwOp; @@ -8,13 +9,11 @@ pub use self::atomic::AtomicRmwOp; #[rustfmt::skip] // prevent `use` reordering use rand::Rng; use rustc_abi::Size; -use rustc_apfloat::{self, Float, Round}; -use rustc_middle::mir; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; -use self::helpers::{ToHost, ToSoft}; +use self::math::EvalContextExt as _; use self::simd::EvalContextExt as _; use crate::*; @@ -179,288 +178,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(branch), dest)?; } - "sqrtf32" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf64" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "sinf32" - | "cosf32" - | "expf32" - | "exp2f32" - | "logf32" - | "log10f32" - | "log2f32" - => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - - let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf32" => host.sin(), - "cosf32" => host.cos(), - "expf32" => host.exp(), - "exp2f32" => host.exp2(), - "logf32" => host.ln(), - "log10f32" => host.log10(), - "log2f32" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp( - this, - res, - 4, - ); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(intrinsic_name, res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "sinf64" - | "cosf64" - | "expf64" - | "exp2f64" - | "logf64" - | "log10f64" - | "log2f64" - => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - - let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf64" => host.sin(), - "cosf64" => host.cos(), - "expf64" => host.exp(), - "exp2f64" => host.exp2(), - "logf64" => host.ln(), - "log10f64" => host.log10(), - "log2f64" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp( - this, - res, - 4, - ); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(intrinsic_name, res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - "fmaf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmaf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - - "fmuladdf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmuladdf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - - "powf32" => { - let [f1, f2] = check_intrinsic_arg_count(args)?; - let f1 = this.read_scalar(f1)?.to_f32()?; - let f2 = this.read_scalar(f2)?.to_f32()?; - - let res = - math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - "powf64" => { - let [f1, f2] = check_intrinsic_arg_count(args)?; - let f1 = this.read_scalar(f1)?.to_f64()?; - let f2 = this.read_scalar(f2)?.to_f64()?; - - let res = - math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - - "powif32" => { - let [f, i] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - let i = this.read_scalar(i)?.to_i32()?; - - let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "powif64" => { - let [f, i] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let i = this.read_scalar(i)?.to_i32()?; - - let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_intrinsic_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> { - let ty::Float(fty) = x.layout.ty.kind() else { - bug!("float_finite: non-float input type {}", x.layout.ty) - }; - interp_ok(match fty { - FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), - FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), - FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), - FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(), - }) - }; - match (float_finite(&a)?, float_finite(&b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - let res = this.binary_op(op, &a, &b)?; - // This cannot be a NaN so we also don't have to apply any non-determinism. - // (Also, `binary_op` already called `generate_nan` if needed.) - if !float_finite(&res)? { - throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); - } - // Apply a relative error of 4ULP to simulate non-deterministic precision loss - // due to optimizations. - let res = math::apply_random_float_error_to_imm(this, res, 4)?; - this.write_immediate(*res, dest)?; - } - - "float_to_int_unchecked" => { - let [val] = check_intrinsic_arg_count(args)?; - let val = this.read_immediate(val)?; - - let res = this - .float_to_int_checked(&val, dest.layout, Round::TowardZero)? - .ok_or_else(|| { - err_ub_format!( - "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })?; - - this.write_immediate(*res, dest)?; - } - // Other "breakpoint" => { let [] = check_intrinsic_arg_count(args)?; @@ -472,7 +189,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Make these a NOP, so we get the better Miri-native error messages. } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return this.emulate_math_intrinsic(intrinsic_name, generic_args, args, dest), } interp_ok(EmulateItemResult::NeedsReturn) From e00051299aa7a355307baeeac0bd593c71e8019e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 12:11:04 +0200 Subject: [PATCH 0258/1900] extract core operation name instead of listing all function name variants --- src/tools/miri/src/math.rs | 43 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 254495637da42..f89abbaba4805 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -210,61 +210,70 @@ where let pi_over_2 = (pi / two).value; let pi_over_4 = (pi_over_2 / two).value; - Some(match (intrinsic_name, args) { + // Remove `f32`/`f64` suffix, if any. + let name = intrinsic_name + .strip_suffix("f32") + .or_else(|| intrinsic_name.strip_suffix("f64")) + .unwrap_or(intrinsic_name); + // Also strip trailing `f` (indicates "float"), with an exception for "erf" to avoid + // removing that `f`. + let name = if name == "erf" { name } else { name.strip_suffix("f").unwrap_or(name) }; + Some(match (name, args) { // cos(±0) and cosh(±0)= 1 - ("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one, + ("cos" | "cosh", [input]) if input.is_zero() => one, // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, + ("exp" | "exp2", [input]) if input.is_zero() => one, // tanh(±INF) = ±1 - ("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input), + ("tanh", [input]) if input.is_infinite() => one.copy_sign(*input), // atan(±INF) = ±π/2 - ("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), + ("atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), // erf(±INF) = ±1 - ("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input), + ("erf", [input]) if input.is_infinite() => one.copy_sign(*input), // erfc(-INF) = 2 - ("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value, + ("erfc", [input]) if input.is_neg_infinity() => (one + one).value, // hypot(x, ±0) = abs(x), if x is not a NaN. - ("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => + // `_hypot` is the Windows name for this. + ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => x.abs(), // atan2(±0,−0) = ±π. // atan2(±0, y) = ±π for y < 0. // Must check for non NaN because `y.is_negative()` also applies to NaN. - ("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => + ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => pi.copy_sign(*x), // atan2(±x,−∞) = ±π for finite x > 0. - ("atan2f" | "atan2", [x, y]) + ("atan2", [x, y]) if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => pi.copy_sign(*x), // atan2(x, ±0) = −π/2 for x < 0. // atan2(x, ±0) = π/2 for x > 0. - ("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), + ("atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), //atan2(±∞, −∞) = ±3π/4 - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => + ("atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => (pi_over_4 * three).value.copy_sign(*x), //atan2(±∞, +∞) = ±π/4 - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => + ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => pi_over_4.copy_sign(*x), // atan2(±∞, y) returns ±π/2 for finite y. - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => + ("atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => pi_over_2.copy_sign(*x), // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + ("pow", [base, exp]) if *base == -one && exp.is_infinite() => one, // 1^y = 1 for any y, even a NaN - ("powf32" | "powf64", [base, exp]) if *base == one => { + ("pow", [base, exp]) if *base == one => { let rng = this.machine.rng.get_mut(); // SNaN exponents get special treatment: they might return 1, or a NaN. let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random(); @@ -273,7 +282,7 @@ where } // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + ("pow", [base, exp]) if exp.is_zero() => { let rng = this.machine.rng.get_mut(); // SNaN bases get special treatment: they might return 1, or a NaN. let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random(); From 84e1950065525589f2d22860aa64c36f171fc782 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 12:20:43 +0200 Subject: [PATCH 0259/1900] move math foreign_items into their own file --- src/tools/miri/src/math.rs | 12 +- src/tools/miri/src/shims/foreign_items.rs | 238 ++------------------- src/tools/miri/src/shims/math.rs | 247 ++++++++++++++++++++++ src/tools/miri/src/shims/mod.rs | 1 + 4 files changed, 266 insertions(+), 232 deletions(-) create mode 100644 src/tools/miri/src/shims/math.rs diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index f89abbaba4805..401e6dd7aab84 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -239,18 +239,15 @@ where // hypot(x, ±0) = abs(x), if x is not a NaN. // `_hypot` is the Windows name for this. - ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => - x.abs(), + ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => x.abs(), // atan2(±0,−0) = ±π. // atan2(±0, y) = ±π for y < 0. // Must check for non NaN because `y.is_negative()` also applies to NaN. - ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => - pi.copy_sign(*x), + ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => pi.copy_sign(*x), // atan2(±x,−∞) = ±π for finite x > 0. - ("atan2", [x, y]) - if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => + ("atan2", [x, y]) if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => pi.copy_sign(*x), // atan2(x, ±0) = −π/2 for x < 0. @@ -262,8 +259,7 @@ where (pi_over_4 * three).value.copy_sign(*x), //atan2(±∞, +∞) = ±π/4 - ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => - pi_over_4.copy_sign(*x), + ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => pi_over_4.copy_sign(*x), // atan2(±∞, y) returns ±π/2 for finite y. ("atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 10e28178177b2..eca8cccf5efc4 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -3,7 +3,6 @@ use std::io::Write; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; -use rustc_apfloat::Float; use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; @@ -15,7 +14,6 @@ use rustc_middle::{mir, ty}; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; use crate::helpers::EvalContextExt as _; @@ -818,225 +816,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } - // math functions (note that there are also intrinsics for some other functions) - #[rustfmt::skip] - | "cbrtf" - | "coshf" - | "sinhf" - | "tanf" - | "tanhf" - | "acosf" - | "asinf" - | "atanf" - | "log1pf" - | "expm1f" - | "tgammaf" - | "erff" - | "erfcf" - => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f = this.read_scalar(f)?.to_f32()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrtf" => f_host.cbrt(), - "coshf" => f_host.cosh(), - "sinhf" => f_host.sinh(), - "tanf" => f_host.tan(), - "tanhf" => f_host.tanh(), - "acosf" => f_host.acos(), - "asinf" => f_host.asin(), - "atanf" => f_host.atan(), - "log1pf" => f_host.ln_1p(), - "expm1f" => f_host.exp_m1(), - "tgammaf" => f_host.gamma(), - "erff" => f_host.erf(), - "erfcf" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_hypotf" - | "hypotf" - | "atan2f" - | "fdimf" - => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f1 = this.read_scalar(f1)?.to_f32()?; - let f2 = this.read_scalar(f2)?.to_f32()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]) - .unwrap_or_else(|| { - let res = match link_name.as_str() { - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "cbrt" - | "cosh" - | "sinh" - | "tan" - | "tanh" - | "acos" - | "asin" - | "atan" - | "log1p" - | "expm1" - | "tgamma" - | "erf" - | "erfc" - => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f = this.read_scalar(f)?.to_f64()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrt" => f_host.cbrt(), - "cosh" => f_host.cosh(), - "sinh" => f_host.sinh(), - "tan" => f_host.tan(), - "tanh" => f_host.tanh(), - "acos" => f_host.acos(), - "asin" => f_host.asin(), - "atan" => f_host.atan(), - "log1p" => f_host.ln_1p(), - "expm1" => f_host.exp_m1(), - "tgamma" => f_host.gamma(), - "erf" => f_host.erf(), - "erfc" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_hypot" - | "hypot" - | "atan2" - | "fdim" - => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f1 = this.read_scalar(f1)?.to_f64()?; - let f2 = this.read_scalar(f2)?.to_f64()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { - let res = match link_name.as_str() { - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_ldexp" - | "ldexp" - | "scalbn" - => { - let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. - let x = this.read_scalar(x)?.to_f64()?; - let exp = this.read_scalar(exp)?.to_i32()?; - - let res = x.scalbn(exp); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - "lgammaf_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let x = this.read_scalar(x)?.to_f32()?; - let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; - - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let (res, sign) = x.to_host().ln_gamma(); - this.write_int(sign, &signp)?; - - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - let res = math::clamp_float_value(link_name.as_str(), res); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - "lgamma_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let x = this.read_scalar(x)?.to_f64()?; - let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; - - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let (res, sign) = x.to_host().ln_gamma(); - this.write_int(sign, &signp)?; - - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - let res = math::clamp_float_value(link_name.as_str(), res); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - // LLVM intrinsics "llvm.prefetch" => { let [p, rw, loc, ty] = @@ -1118,8 +897,18 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Platform-specific shims - _ => + // Fallback to shims in submodules. + _ => { + // Math shims + #[expect(irrefutable_let_patterns)] + if let res = shims::math::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + )? && !matches!(res, EmulateItemResult::NotSupported) + { + return interp_ok(res); + } + + // Platform-specific shims return match this.tcx.sess.target.os.as_ref() { _ if this.target_os_is_unix() => shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner( @@ -1134,7 +923,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this, link_name, abi, args, dest, ), _ => interp_ok(EmulateItemResult::NotSupported), - }, + }; + } }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs new file mode 100644 index 0000000000000..576e76494bcb7 --- /dev/null +++ b/src/tools/miri/src/shims/math.rs @@ -0,0 +1,247 @@ +use rustc_abi::CanonAbi; +use rustc_apfloat::Float; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; + +use self::helpers::{ToHost, ToSoft}; +use crate::*; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + + // math functions (note that there are also intrinsics for some other functions) + match link_name.as_str() { + // math functions (note that there are also intrinsics for some other functions) + #[rustfmt::skip] + | "cbrtf" + | "coshf" + | "sinhf" + | "tanf" + | "tanhf" + | "acosf" + | "asinf" + | "atanf" + | "log1pf" + | "expm1f" + | "tgammaf" + | "erff" + | "erfcf" + => { + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f = this.read_scalar(f)?.to_f32()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrtf" => f_host.cbrt(), + "coshf" => f_host.cosh(), + "sinhf" => f_host.sinh(), + "tanf" => f_host.tan(), + "tanhf" => f_host.tanh(), + "acosf" => f_host.acos(), + "asinf" => f_host.asin(), + "atanf" => f_host.atan(), + "log1pf" => f_host.ln_1p(), + "expm1f" => f_host.exp_m1(), + "tgammaf" => f_host.gamma(), + "erff" => f_host.erf(), + "erfcf" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_hypotf" + | "hypotf" + | "atan2f" + | "fdimf" + => { + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]) + .unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "cbrt" + | "cosh" + | "sinh" + | "tan" + | "tanh" + | "acos" + | "asin" + | "atan" + | "log1p" + | "expm1" + | "tgamma" + | "erf" + | "erfc" + => { + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f = this.read_scalar(f)?.to_f64()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrt" => f_host.cbrt(), + "cosh" => f_host.cosh(), + "sinh" => f_host.sinh(), + "tan" => f_host.tan(), + "tanh" => f_host.tanh(), + "acos" => f_host.acos(), + "asin" => f_host.asin(), + "atan" => f_host.atan(), + "log1p" => f_host.ln_1p(), + "expm1" => f_host.exp_m1(), + "tgamma" => f_host.gamma(), + "erf" => f_host.erf(), + "erfc" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_hypot" + | "hypot" + | "atan2" + | "fdim" + => { + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_ldexp" + | "ldexp" + | "scalbn" + => { + let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. + let x = this.read_scalar(x)?.to_f64()?; + let exp = this.read_scalar(exp)?.to_i32()?; + + let res = x.scalbn(exp); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + "lgammaf_r" => { + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let x = this.read_scalar(x)?.to_f32()?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; + + // Using host floats (but it's fine, these operations do not have guaranteed precision). + let (res, sign) = x.to_host().ln_gamma(); + this.write_int(sign, &signp)?; + + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + "lgamma_r" => { + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let x = this.read_scalar(x)?.to_f64()?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; + + // Using host floats (but it's fine, these operations do not have guaranteed precision). + let (res, sign) = x.to_host().ln_gamma(); + this.write_int(sign, &signp)?; + + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + + _ => return interp_ok(EmulateItemResult::NotSupported), + } + + interp_ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 7f594d4fdd6bc..7f7bc3b1cf720 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -4,6 +4,7 @@ mod aarch64; mod alloc; mod backtrace; mod files; +mod math; #[cfg(all(unix, feature = "native-lib"))] mod native_lib; mod unix; From a21866a563836da1c47c1088f0e6085514c9595a Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 8 Sep 2025 15:56:56 +0100 Subject: [PATCH 0260/1900] Remove support for register_attr This was removed in rustc in 2022: https://github.com/rust-lang/rust/pull/101123 Closes #20525. --- .../crates/hir-def/src/nameres.rs | 9 ----- .../hir-def/src/nameres/attr_resolution.rs | 9 +---- .../crates/hir-def/src/nameres/collector.rs | 6 --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 38 ++++--------------- .../crates/hir/src/source_analyzer.rs | 3 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 6 +-- .../src/handlers/macro_error.rs | 5 +-- 7 files changed, 14 insertions(+), 62 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index bf6fc15b7d199..7d5e627964eb1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -192,8 +192,6 @@ struct DefMapCrateData { exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, - /// Custom attributes registered with `#![register_attr]`. - registered_attrs: Vec, /// Custom tool modules registered with `#![register_tool]`. registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. @@ -212,7 +210,6 @@ impl DefMapCrateData { Self { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), - registered_attrs: Vec::new(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, @@ -227,7 +224,6 @@ impl DefMapCrateData { let Self { exported_derives, fn_proc_macro_mapping, - registered_attrs, registered_tools, unstable_features, rustc_coherence_is_core: _, @@ -238,7 +234,6 @@ impl DefMapCrateData { } = self; exported_derives.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); - registered_attrs.shrink_to_fit(); registered_tools.shrink_to_fit(); unstable_features.shrink_to_fit(); } @@ -529,10 +524,6 @@ impl DefMap { &self.data.registered_tools } - pub fn registered_attrs(&self) -> &[Symbol] { - &self.data.registered_attrs - } - pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { self.data.unstable_features.contains(feature) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index e7e96804ae737..2f56d608fcbf4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -90,13 +90,8 @@ impl DefMap { return true; } - if segments.len() == 1 { - if find_builtin_attr_idx(name).is_some() { - return true; - } - if self.data.registered_attrs.iter().any(pred) { - return true; - } + if segments.len() == 1 && find_builtin_attr_idx(name).is_some() { + return true; } } false diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 0f84728dcb4a8..a2ce538356515 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -298,12 +298,6 @@ impl<'db> DefCollector<'db> { ); crate_data.unstable_features.extend(features); } - () if *attr_name == sym::register_attr => { - if let Some(ident) = attr.single_ident_value() { - crate_data.registered_attrs.push(ident.sym.clone()); - cov_mark::hit!(register_attr); - } - } () if *attr_name == sym::register_tool => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_tools.push(ident.sym.clone()); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6610375451dd0..6eb8a8bf60fd1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4060,49 +4060,25 @@ impl DeriveHelper { } } -// FIXME: Wrong name? This is could also be a registered attribute #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BuiltinAttr { - krate: Option, idx: u32, } impl BuiltinAttr { - // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs? - pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { - if let builtin @ Some(_) = Self::builtin(name) { - return builtin; - } - let idx = crate_def_map(db, krate.id) - .registered_attrs() - .iter() - .position(|it| it.as_str() == name)? as u32; - Some(BuiltinAttr { krate: Some(krate.id), idx }) - } - fn builtin(name: &str) -> Option { hir_expand::inert_attr_macro::find_builtin_attr_idx(&Symbol::intern(name)) - .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 }) + .map(|idx| BuiltinAttr { idx: idx as u32 }) } - pub fn name(&self, db: &dyn HirDatabase) -> Name { - match self.krate { - Some(krate) => Name::new_symbol_root( - crate_def_map(db, krate).registered_attrs()[self.idx as usize].clone(), - ), - None => Name::new_symbol_root(Symbol::intern( - hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, - )), - } + pub fn name(&self) -> Name { + Name::new_symbol_root(Symbol::intern( + hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, + )) } - pub fn template(&self, _: &dyn HirDatabase) -> Option { - match self.krate { - Some(_) => None, - None => { - Some(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].template) - } - } + pub fn template(&self) -> Option { + Some(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].template) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index dbc2539a9b91c..539b25387aef0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1062,8 +1062,7 @@ impl<'db> SourceAnalyzer<'db> { // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces if let Some(name_ref) = path.as_single_name_ref() { - let builtin = - BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); + let builtin = BuiltinAttr::builtin(&name_ref.text()); if builtin.is_some() { return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 61a21ccf2f775..44ff9d6006d66 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -259,8 +259,8 @@ impl Definition { Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db), Definition::BuiltinAttr(it) => { - let name = it.name(db); - let AttributeTemplate { word, list, name_value_str } = it.template(db)?; + let name = it.name(); + let AttributeTemplate { word, list, name_value_str } = it.template()?; let mut docs = "Valid forms are:".to_owned(); if word { format_to!(docs, "\n - #\\[{}]", name.display(db, display_target.edition)); @@ -348,7 +348,7 @@ impl Definition { Definition::Label(it) => it.name(db).display(db, display_target.edition).to_string(), Definition::ExternCrateDecl(it) => it.display(db, display_target).to_string(), Definition::BuiltinAttr(it) => { - format!("#[{}]", it.name(db).display(db, display_target.edition)) + format!("#[{}]", it.name().display(db, display_target.edition)) } Definition::ToolModule(it) => { it.name(db).display(db, display_target.edition).to_string() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index c39e00e178f81..6a1ecae651501 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -144,16 +144,13 @@ macro_rules! concat { () => {} } } #[test] - fn register_attr_and_tool() { - cov_mark::check!(register_attr); + fn register_tool() { cov_mark::check!(register_tool); check_diagnostics( r#" #![register_tool(tool)] -#![register_attr(attr)] #[tool::path] -#[attr] struct S; "#, ); From bb5b153b7bccab4347d1a4deead6dcc9ca400a34 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 11:12:57 -0400 Subject: [PATCH 0261/1900] feat: support navigation on primitives --- .../rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../crates/ide/src/goto_definition.rs | 27 +++++++++++++++++-- .../crates/ide/src/hover/render.rs | 15 +---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 61a21ccf2f775..43d4611b18ac1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -363,7 +363,7 @@ impl Definition { } } -fn find_std_module( +pub fn find_std_module( famous_defs: &FamousDefs<'_, '_>, name: &str, edition: Edition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f469..002f5b3fe8f44 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -6,12 +6,13 @@ use crate::{ navigation_target::{self, ToNav}, }; use hir::{ - AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, + AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, PathResolution, + Semantics, sym, }; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, - defs::{Definition, IdentClass}, + defs::{Definition, IdentClass, find_std_module}, famous_defs::FamousDefs, helpers::pick_best_token, }; @@ -90,6 +91,9 @@ pub(crate) fn goto_definition( if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { return Some(navs); } + if let Some(navs) = find_definition_for_builtin_types(sema, &token.value, edition) { + return Some(navs); + } let parent = token.value.parent()?; @@ -204,6 +208,25 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema.db, def)) } +// If the token is a builtin type search the definition from the rustdoc module shims. +fn find_definition_for_builtin_types( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, + edition: Edition, +) -> Option> { + let path = original_token.parent_ancestors().find_map(ast::Path::cast)?; + let res = sema.resolve_path(&path)?; + let PathResolution::Def(ModuleDef::BuiltinType(builtin)) = res else { + return None; + }; + + let fd = FamousDefs(sema, sema.scope(path.syntax())?.krate()); + let primitive_mod = format!("prim_{}", builtin.name().display(fd.0.db, edition)); + let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; + + Some(def_to_nav(sema.db, doc_owner.into())) +} + fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 290ee80984edc..2adafbb7af6f7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, - defs::Definition, + defs::{Definition, find_std_module}, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, @@ -1160,19 +1160,6 @@ fn markup( } } -fn find_std_module( - famous_defs: &FamousDefs<'_, '_>, - name: &str, - edition: Edition, -) -> Option { - let db = famous_defs.0.db; - let std_crate = famous_defs.std()?; - let std_root_module = std_crate.root_module(); - std_root_module.children(db).find(|module| { - module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) - }) -} - fn render_memory_layout( config: Option, layout: impl FnOnce() -> Result, From 224428d8fd5f4d2ad28b833d6e4502d4d7952f9b Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 8 Sep 2025 16:45:58 +0100 Subject: [PATCH 0262/1900] Clarify intro in README and manual The first sentence a new user should see should ideally answer the questions: * What is rust-analyzer? * Why might I want to use it? The vast majority of users will be interested in using rust-analyzer inside their favourite editor. We should clarify that rust-analyzer is an LSP implementation and that it supports all the classic IDE features. Whilst it's also true that rust-analyzer is modular and organised into libraries, the first impression should (I think) focus on an overview and the primary use case. --- src/tools/rust-analyzer/README.md | 17 +++++++++++++-- .../rust-analyzer/docs/book/src/README.md | 21 ++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 4360dea4a113c..cb3a41eec5a62 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -4,8 +4,21 @@ alt="rust-analyzer logo">

-rust-analyzer is a modular compiler frontend for the Rust language. -It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust. +rust-analyzer is a language server that provides IDE functionality for +writing Rust programs. You can use it with any editor that supports +the [Language Server +Protocol](https://microsoft.github.io/language-server-protocol/) (VS +Code, Vim, Emacs, Zed, etc). + +rust-analyzer features include go-to-definition, find-all-references, +refactorings and code completion. rust-analyzer also supports +integrated formatting (with rustfmt) and integrated diagnostics (with +rustc and clippy). + +Internally, rust-analyzer is structured as a set of libraries for +analyzing Rust code. See +[Architecture](https://rust-analyzer.github.io/book/contributing/architecture.html) +in the manual. ## Quick Start diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md index 71f34e0346646..060bcf0cea399 100644 --- a/src/tools/rust-analyzer/docs/book/src/README.md +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -1,13 +1,20 @@ # rust-analyzer -At its core, rust-analyzer is a **library** for semantic analysis of -Rust code as it changes over time. This manual focuses on a specific -usage of the library -- running it as part of a server that implements +rust-analyzer is a language server that provides IDE functionality for +writing Rust programs. You can use it with any editor that supports the [Language Server -Protocol](https://microsoft.github.io/language-server-protocol/) (LSP). -The LSP allows various code editors, like VS Code, Emacs or Vim, to -implement semantic features like completion or goto definition by -talking to an external language server process. +Protocol](https://microsoft.github.io/language-server-protocol/) (VS +Code, Vim, Emacs, Zed, etc). + +rust-analyzer features include go-to-definition, find-all-references, +refactorings and code completion. rust-analyzer also supports +integrated formatting (with rustfmt) and integrated diagnostics (with +rustc and clippy). + +Internally, rust-analyzer is structured as a set of libraries for +analyzing Rust code. See +[Architecture](https://rust-analyzer.github.io/book/contributing/architecture.html) +for more details. To improve this document, send a pull request: [https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md) From a6a53ace86ad7ff23b6d84379391099862f3bd1a Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 14:10:25 -0400 Subject: [PATCH 0263/1900] impl TryToNav for BuiltinType instead --- .../crates/ide-db/src/famous_defs.rs | 4 +++ .../crates/ide/src/goto_definition.rs | 27 ++-------------- .../crates/ide/src/navigation_target.rs | 31 ++++++++++++++++--- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 8e687385086fc..8eea2b81bab6d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -210,6 +210,10 @@ impl FamousDefs<'_, '_> { fn find_lang_crate(&self, origin: LangCrateOrigin) -> Option { let krate = self.1; let db = self.0.db; + if krate.origin(db) == CrateOrigin::Lang(origin) { + return Some(krate); + } + let res = krate .dependencies(db) .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 002f5b3fe8f44..f768d4b68f469 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -6,13 +6,12 @@ use crate::{ navigation_target::{self, ToNav}, }; use hir::{ - AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, PathResolution, - Semantics, sym, + AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, }; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, - defs::{Definition, IdentClass, find_std_module}, + defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, }; @@ -91,9 +90,6 @@ pub(crate) fn goto_definition( if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { return Some(navs); } - if let Some(navs) = find_definition_for_builtin_types(sema, &token.value, edition) { - return Some(navs); - } let parent = token.value.parent()?; @@ -208,25 +204,6 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema.db, def)) } -// If the token is a builtin type search the definition from the rustdoc module shims. -fn find_definition_for_builtin_types( - sema: &Semantics<'_, RootDatabase>, - original_token: &SyntaxToken, - edition: Edition, -) -> Option> { - let path = original_token.parent_ancestors().find_map(ast::Path::cast)?; - let res = sema.resolve_path(&path)?; - let PathResolution::Def(ModuleDef::BuiltinType(builtin)) = res else { - return None; - }; - - let fd = FamousDefs(sema, sema.scope(path.syntax())?.krate()); - let primitive_mod = format!("prim_{}", builtin.name().display(fd.0.db, edition)); - let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; - - Some(def_to_nav(sema.db, doc_owner.into())) -} - fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 1b6a4b726e831..641bde5e2b82a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -5,14 +5,15 @@ use std::fmt; use arrayvec::ArrayVec; use either::Either; use hir::{ - AssocItem, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, InFile, - LocalSource, ModuleSource, db::ExpandDatabase, symbols::FileSymbol, + AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, + InFile, LocalSource, ModuleSource, Semantics, db::ExpandDatabase, symbols::FileSymbol, }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, - base_db::salsa, - defs::Definition, + base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb, salsa}, + defs::{Definition, find_std_module}, documentation::{Documentation, HasDocs}, + famous_defs::FamousDefs, }; use span::Edition; use stdx::never; @@ -262,8 +263,8 @@ impl TryToNav for Definition { Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => it.try_to_nav(db), Definition::InlineAsmOperand(it) => it.try_to_nav(db), + Definition::BuiltinType(it) => it.try_to_nav(db), Definition::BuiltinLifetime(_) - | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) @@ -746,6 +747,26 @@ impl TryToNav for hir::InlineAsmOperand { } } +impl TryToNav for hir::BuiltinType { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let sema = Semantics::new(db); + + let krate = db + .all_crates() + .iter() + .copied() + .find(|&krate| matches!(krate.data(db).origin, CrateOrigin::Lang(LangCrateOrigin::Std))) + .map(Crate::from)?; + let edition = krate.edition(db); + + let fd = FamousDefs(&sema, krate); + let primitive_mod = format!("prim_{}", self.name().display(fd.0.db, edition)); + let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; + + Some(doc_owner.to_nav(db)) + } +} + #[derive(Debug)] pub struct UpmappingResult { /// The macro call site. From d80db5b814f594f2e1ecea2e375990287ba4d4df Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Mon, 8 Sep 2025 20:20:03 +0200 Subject: [PATCH 0264/1900] tests/codegen-llvm: Make rust-abi-arch-specific-adjustment portable This test currently only runs on RISC-V and loongarch hosts, but assumes that the host target is the -gnu target. By using minicore, we can run this test on all host targets, regardless of architecture, as long as the LLVM components are built. This also fixes this test on musl hosts of these architectures. Signed-off-by: Jens Reidel --- tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs index 561f081c700ea..ffff4b359947d 100644 --- a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs +++ b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs @@ -1,15 +1,19 @@ +//@ add-core-stubs //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@ revisions: riscv64 loongarch64 -//@[riscv64] only-riscv64 //@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu //@[riscv64] needs-llvm-components: riscv -//@[loongarch64] only-loongarch64 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64] needs-llvm-components: loongarch #![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; #[no_mangle] // riscv64: define noundef i8 @arg_attr_u8(i8 noundef zeroext %x) From fc4450b1f1dedd1328bc9dabad55afc3fda872b7 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 14:33:25 -0400 Subject: [PATCH 0265/1900] add test --- .../crates/ide/src/goto_definition.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f469..d50d7f602fcb8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -3944,6 +3944,20 @@ fn main() { _ => {} } } +"#, + ); + } + + #[test] + fn goto_builtin_type() { + check( + r#" +//- /main.rs crate:main deps:std +const _: &str$0 = ""; } + +//- /libstd.rs crate:std +mod prim_str {} +// ^^^^^^^^ "#, ); } From 36c2c0a8bab6fb5c588ee6980fd8c1be659567c2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 2 Aug 2025 10:50:04 +0200 Subject: [PATCH 0266/1900] fix(semicolon_inside_block): don't lint if block is in parens --- clippy_lints/src/semicolon_block.rs | 14 ++++++++++++++ tests/ui/semicolon_inside_block.fixed | 17 ++++++++++++++++- tests/ui/semicolon_inside_block.rs | 17 ++++++++++++++++- tests/ui/semicolon_inside_block.stderr | 8 ++++---- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 1dea8f17c34be..371d62a068492 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,6 +83,19 @@ impl SemicolonBlock { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); + // If the block is surrounded by parens (`({ 0 });`), the author probably knows what + // they're doing and why, so don't get in their way. + // + // This has the additional benefit of stopping the block being parsed as a function call: + // ``` + // fn foo() { + // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call + // (5); + // } + if remove_span.check_source_text(cx, |src| src.contains(')')) { + return; + } + if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) { return; } diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7308e78aae263..468f0a5b1e472 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 467bf4d779f2a..101374af26471 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index 23433f4e7ef90..2046dd1c36be7 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -1,5 +1,5 @@ error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:38:5 + --> tests/ui/semicolon_inside_block.rs:39:5 | LL | { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:40:5 + --> tests/ui/semicolon_inside_block.rs:41:5 | LL | unsafe { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + unsafe { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:49:5 + --> tests/ui/semicolon_inside_block.rs:50:5 | LL | / { LL | | @@ -41,7 +41,7 @@ LL ~ } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:63:5 + --> tests/ui/semicolon_inside_block.rs:64:5 | LL | { m!(()) }; | ^^^^^^^^^^^ From 2ffa8510a7f29af51b976d98584d7330eadbeee7 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 15:59:46 -0400 Subject: [PATCH 0267/1900] make TryToNav take Semantics instead of RootDatabase --- .../crates/ide/src/call_hierarchy.rs | 10 +- .../crates/ide/src/doc_links/tests.rs | 2 +- .../crates/ide/src/goto_declaration.rs | 10 +- .../crates/ide/src/goto_definition.rs | 20 +- .../crates/ide/src/goto_implementation.rs | 6 +- .../crates/ide/src/goto_type_definition.rs | 2 +- .../crates/ide/src/highlight_related.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 34 +-- .../crates/ide/src/hover/render.rs | 12 +- .../crates/ide/src/inlay_hints.rs | 6 +- .../crates/ide/src/inlay_hints/bounds.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/ide/src/navigation_target.rs | 196 ++++++++++++------ .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 15 +- .../crates/ide/src/static_index.rs | 4 +- .../crates/ide/src/test_explorer.rs | 2 +- 17 files changed, 203 insertions(+), 124 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 7a0405939d10c..f42cead3501d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -67,7 +67,7 @@ pub(crate) fn incoming_calls( let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?; // We should return def before check if it is a test, so that we // will not continue to search for outer fn in nested fns - def.try_to_nav(sema.db).map(|nav| (def, nav)) + def.try_to_nav(sema).map(|nav| (def, nav)) }); if let Some((def, nav)) = def_nav { @@ -122,10 +122,10 @@ pub(crate) fn outgoing_calls( if exclude_tests && it.is_test(db) { return None; } - it.try_to_nav(db) + it.try_to_nav(&sema) } - hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), - hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), + hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(&sema), + hir::CallableKind::TupleStruct(it) => it.try_to_nav(&sema), _ => None, } .zip(Some(sema.original_range(expr.syntax()))) @@ -136,7 +136,7 @@ pub(crate) fn outgoing_calls( return None; } function - .try_to_nav(db) + .try_to_nav(&sema) .zip(Some(sema.original_range(expr.name_ref()?.syntax()))) } }?; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index b004c07576748..11518d1fbf306 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -68,7 +68,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) .unwrap_or_else(|| panic!("Failed to resolve {link}")) }); - def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) + def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link)) }) .map(|(nav_target, link)| { let range = diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 267e8ff7128be..686dbe2412933 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -38,14 +38,14 @@ pub(crate) fn goto_declaration( ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { NameRefClass::Definition(it, _) => Some(it), NameRefClass::FieldShorthand { field_ref, .. } => - return field_ref.try_to_nav(db), + return field_ref.try_to_nav(&sema), NameRefClass::ExternCrateShorthand { decl, .. } => - return decl.try_to_nav(db), + return decl.try_to_nav(&sema), }, ast::Name(name) => match NameClass::classify(&sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it), NameClass::PatFieldShorthand { field_ref, .. } => - return field_ref.try_to_nav(db), + return field_ref.try_to_nav(&sema), }, _ => None } @@ -57,14 +57,14 @@ pub(crate) fn goto_declaration( Definition::Const(c) => c.as_assoc_item(db), Definition::TypeAlias(ta) => ta.as_assoc_item(db), Definition::Function(f) => f.as_assoc_item(db), - Definition::ExternCrateDecl(it) => return it.try_to_nav(db), + Definition::ExternCrateDecl(it) => return it.try_to_nav(&sema), _ => None, }?; let trait_ = assoc.implemented_trait(db)?; let name = Some(assoc.name(db)?); let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; - item.try_to_nav(db) + item.try_to_nav(&sema) }) .flatten() .collect(); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index d50d7f602fcb8..2dcb13d9e7aa1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -62,7 +62,7 @@ pub(crate) fn goto_definition( })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { - let nav = def.try_to_nav(db)?; + let nav = def.try_to_nav(sema)?; Some(RangeInfo::new(link_range, nav.collect())) }); } @@ -73,7 +73,7 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new( range, match resolution { - Some(res) => def_to_nav(db, Definition::from(res)), + Some(res) => def_to_nav(sema, Definition::from(res)), None => vec![], }, )); @@ -121,7 +121,7 @@ pub(crate) fn goto_definition( .collect(); } try_filter_trait_item_definition(sema, &def) - .unwrap_or_else(|| def_to_nav(sema.db, def)) + .unwrap_or_else(|| def_to_nav(sema, def)) }) .collect(), ) @@ -160,7 +160,7 @@ fn find_definition_for_known_blanket_dual_impls( t_f, [return_type.type_arguments().next()?], ) - .map(|f| def_to_nav(sema.db, f.into())); + .map(|f| def_to_nav(sema, f.into())); } hir::AssocItemContainer::Impl(_) => return None, }; @@ -201,7 +201,7 @@ fn find_definition_for_known_blanket_dual_impls( // succeed let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; let def = Definition::from(f); - Some(def_to_nav(sema.db, def)) + Some(def_to_nav(sema, def)) } fn try_lookup_include_path( @@ -246,7 +246,7 @@ fn try_lookup_macro_def_in_macro_use( for mod_def in krate.root_module().declarations(sema.db) { if let ModuleDef::Macro(mac) = mod_def && mac.name(sema.db).as_str() == token.text() - && let Some(nav) = mac.try_to_nav(sema.db) + && let Some(nav) = mac.try_to_nav(sema) { return Some(nav.call_site); } @@ -278,7 +278,7 @@ fn try_filter_trait_item_definition( .items(db) .iter() .filter(|itm| discriminant(*itm) == discriminant_value) - .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) + .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(sema)).flatten()) .map(|it| it.collect()) } } @@ -347,7 +347,7 @@ fn nav_for_exit_points( match_ast! { match node { ast::Fn(fn_) => { - let mut nav = sema.to_def(&fn_)?.try_to_nav(db)?; + let mut nav = sema.to_def(&fn_)?.try_to_nav(sema)?; // For async token, we navigate to itself, which triggers // VSCode to find the references let focus_token = if matches!(token_kind, T![async]) { @@ -564,8 +564,8 @@ fn nav_for_break_points( Some(navs) } -fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { - def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default() +fn def_to_nav(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Vec { + def.try_to_nav(sema).map(|it| it.collect()).unwrap_or_default() } fn expr_to_nav( diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 4f172621908c5..875403c4e32a4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -86,7 +86,7 @@ pub(crate) fn goto_implementation( fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type<'_>) -> Vec { Impl::all_for_type(sema.db, ty) .into_iter() - .filter_map(|imp| imp.try_to_nav(sema.db)) + .filter_map(|imp| imp.try_to_nav(sema)) .flatten() .collect() } @@ -97,7 +97,7 @@ fn impls_for_trait( ) -> Vec { Impl::all_for_trait(sema.db, trait_) .into_iter() - .filter_map(|imp| imp.try_to_nav(sema.db)) + .filter_map(|imp| imp.try_to_nav(sema)) .flatten() .collect() } @@ -114,7 +114,7 @@ fn impls_for_trait_item( let itm_name = itm.name(sema.db)?; (itm_name == fun_name).then_some(*itm) })?; - item.try_to_nav(sema.db) + item.try_to_nav(sema) }) .flatten() .collect() diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index b80e81d39c6df..ffd144a827e34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -30,7 +30,7 @@ pub(crate) fn goto_type_definition( let mut res = Vec::new(); let mut push = |def: Definition| { - if let Some(navs) = def.try_to_nav(db) { + if let Some(navs) = def.try_to_nav(&sema) { for nav in navs { if !res.contains(&nav) { res.push(nav); diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index af1557f5a9a16..04ce5a7567f3c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -277,7 +277,7 @@ fn highlight_references( Definition::Module(module) => { NavigationTarget::from_module_to_decl(sema.db, module) } - def => match def.try_to_nav(sema.db) { + def => match def.try_to_nav(sema) { Some(it) => it, None => continue, }, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 44c98a43f6944..3f52303d74e48 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -85,10 +85,11 @@ pub enum HoverAction { impl HoverAction { fn goto_type_from_targets( - db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, targets: Vec, edition: Edition, ) -> Option { + let db = sema.db; let targets = targets .into_iter() .filter_map(|it| { @@ -99,7 +100,7 @@ impl HoverAction { it.name(db).map(|name| name.display(db, edition).to_string()), edition, ), - nav: it.try_to_nav(db)?.call_site(), + nav: it.try_to_nav(sema)?.call_site(), }) }) .collect::>(); @@ -467,10 +468,10 @@ pub(crate) fn hover_for_definition( HoverResult { markup: render::process_markup(sema.db, def, &markup, range_map, config), actions: [ - show_fn_references_action(sema.db, def), - show_implementations_action(sema.db, def), + show_fn_references_action(sema, def), + show_implementations_action(sema, def), runnable_action(sema, def, file_id), - goto_type_action_for_def(sema.db, def, ¬able_traits, subst_types, edition), + goto_type_action_for_def(sema, def, ¬able_traits, subst_types, edition), ] .into_iter() .flatten() @@ -505,7 +506,10 @@ fn notable_traits<'db>( .collect::>() } -fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option { +fn show_implementations_action( + sema: &Semantics<'_, RootDatabase>, + def: Definition, +) -> Option { fn to_action(nav_target: NavigationTarget) -> HoverAction { HoverAction::Implementation(FilePosition { file_id: nav_target.file_id, @@ -515,19 +519,22 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option { - return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action); + return it.try_to_nav(sema).map(UpmappingResult::call_site).map(to_action); } Definition::Adt(it) => Some(it), - Definition::SelfType(it) => it.self_ty(db).as_adt(), + Definition::SelfType(it) => it.self_ty(sema.db).as_adt(), _ => None, }?; - adt.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) + adt.try_to_nav(sema).map(UpmappingResult::call_site).map(to_action) } -fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option { +fn show_fn_references_action( + sema: &Semantics<'_, RootDatabase>, + def: Definition, +) -> Option { match def { Definition::Function(it) => { - it.try_to_nav(db).map(UpmappingResult::call_site).map(|nav_target| { + it.try_to_nav(sema).map(UpmappingResult::call_site).map(|nav_target| { HoverAction::Reference(FilePosition { file_id: nav_target.file_id, offset: nav_target.focus_or_full_range().start(), @@ -560,12 +567,13 @@ fn runnable_action( } fn goto_type_action_for_def( - db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, def: Definition, notable_traits: &[(hir::Trait, Vec<(Option>, hir::Name)>)], subst_types: Option)>>, edition: Edition, ) -> Option { + let db = sema.db; let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { if !targets.contains(&item) { @@ -612,7 +620,7 @@ fn goto_type_action_for_def( } } - HoverAction::goto_type_from_targets(db, targets, edition) + HoverAction::goto_type_from_targets(sema, targets, edition) } fn walk_and_push_ty( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 2adafbb7af6f7..451260640f844 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -128,7 +128,7 @@ pub(super) fn try_expr( }; walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def); walk_and_push_ty(sema.db, &body_ty, &mut push_new_def); - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } @@ -210,7 +210,7 @@ pub(super) fn deref_expr( ) .into() }; - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } @@ -323,7 +323,7 @@ pub(super) fn struct_rest_pat( Markup::fenced_block(&s) }; - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } res @@ -1030,7 +1030,7 @@ fn type_info( }; desc.into() }; - if let Some(actions) = HoverAction::goto_type_from_targets(db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } Some(res) @@ -1098,7 +1098,7 @@ fn closure_ty( format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); let mut res = HoverResult::default(); - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } res.markup = markup.into(); @@ -1302,7 +1302,7 @@ fn keyword_hints( KeywordHint { description, keyword_mod, - actions: HoverAction::goto_type_from_targets(sema.db, targets, edition) + actions: HoverAction::goto_type_from_targets(sema, targets, edition) .into_iter() .collect(), } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index cc44ae8fc8b9e..507af41d84461 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -672,7 +672,7 @@ impl fmt::Debug for InlayHintLabelPart { #[derive(Debug)] struct InlayHintLabelBuilder<'a> { - db: &'a RootDatabase, + sema: &'a Semantics<'a, RootDatabase>, result: InlayHintLabel, last_part: String, resolve: bool, @@ -694,7 +694,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { LazyProperty::Lazy } else { LazyProperty::Computed({ - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let Some(location) = ModuleDef::from(def).try_to_nav(self.sema) else { return }; let location = location.call_site(); FileRange { file_id: location.file_id, range: location.focus_or_full_range() } }) @@ -782,7 +782,7 @@ fn label_of_ty( } let mut label_builder = InlayHintLabelBuilder { - db: sema.db, + sema, last_part: String::new(), location: None, result: InlayHintLabel::default(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index f0003dae3f36f..4abd67b91f5ec 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -44,7 +44,7 @@ pub(super) fn hints( text: "Sized".to_owned(), linked_location: sized_trait.and_then(|it| { config.lazy_location_opt(|| { - it.try_to_nav(sema.db).map(|it| { + it.try_to_nav(sema).map(|it| { let n = it.call_site(); FileRange { file_id: n.file_id, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index cddf5f04f2442..3a6b3247d65c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -483,7 +483,7 @@ impl Analysis { salsa::attach(&self.db, || { symbols .into_iter() - .filter_map(|s| s.try_to_nav(&self.db)) + .filter_map(|s| s.try_to_nav(&Semantics::new(&self.db))) .take(limit) .map(UpmappingResult::call_site) .collect::>() diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 641bde5e2b82a..46ff16f972625 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -83,14 +83,20 @@ pub(crate) trait ToNav { } pub trait TryToNav { - fn try_to_nav(&self, db: &RootDatabase) -> Option>; + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option>; } impl TryToNav for Either { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - Either::Left(it) => it.try_to_nav(db), - Either::Right(it) => it.try_to_nav(db), + Either::Left(it) => it.try_to_nav(sema), + Either::Right(it) => it.try_to_nav(sema), } } } @@ -185,7 +191,11 @@ impl NavigationTarget { } impl TryToNav for FileSymbol { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let edition = self.def.module(db).map(|it| it.krate().edition(db)).unwrap_or(Edition::CURRENT); let display_target = self.def.krate(db).to_display_target(db); @@ -244,49 +254,55 @@ impl TryToNav for FileSymbol { } impl TryToNav for Definition { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - Definition::Local(it) => Some(it.to_nav(db)), - Definition::Label(it) => it.try_to_nav(db), - Definition::Module(it) => Some(it.to_nav(db)), - Definition::Crate(it) => Some(it.to_nav(db)), - Definition::Macro(it) => it.try_to_nav(db), - Definition::Field(it) => it.try_to_nav(db), - Definition::SelfType(it) => it.try_to_nav(db), - Definition::GenericParam(it) => it.try_to_nav(db), - Definition::Function(it) => it.try_to_nav(db), - Definition::Adt(it) => it.try_to_nav(db), - Definition::Variant(it) => it.try_to_nav(db), - Definition::Const(it) => it.try_to_nav(db), - Definition::Static(it) => it.try_to_nav(db), - Definition::Trait(it) => it.try_to_nav(db), - Definition::TypeAlias(it) => it.try_to_nav(db), - Definition::ExternCrateDecl(it) => it.try_to_nav(db), - Definition::InlineAsmOperand(it) => it.try_to_nav(db), - Definition::BuiltinType(it) => it.try_to_nav(db), + Definition::Local(it) => Some(it.to_nav(sema.db)), + Definition::Label(it) => it.try_to_nav(sema), + Definition::Module(it) => Some(it.to_nav(sema.db)), + Definition::Crate(it) => Some(it.to_nav(sema.db)), + Definition::Macro(it) => it.try_to_nav(sema), + Definition::Field(it) => it.try_to_nav(sema), + Definition::SelfType(it) => it.try_to_nav(sema), + Definition::GenericParam(it) => it.try_to_nav(sema), + Definition::Function(it) => it.try_to_nav(sema), + Definition::Adt(it) => it.try_to_nav(sema), + Definition::Variant(it) => it.try_to_nav(sema), + Definition::Const(it) => it.try_to_nav(sema), + Definition::Static(it) => it.try_to_nav(sema), + Definition::Trait(it) => it.try_to_nav(sema), + Definition::TypeAlias(it) => it.try_to_nav(sema), + Definition::ExternCrateDecl(it) => it.try_to_nav(sema), + Definition::InlineAsmOperand(it) => it.try_to_nav(sema), + Definition::BuiltinType(it) => it.try_to_nav(sema), Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration - Definition::DeriveHelper(it) => it.derive().try_to_nav(db), + Definition::DeriveHelper(it) => it.derive().try_to_nav(sema), } } } impl TryToNav for hir::ModuleDef { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::ModuleDef::Module(it) => Some(it.to_nav(db)), - hir::ModuleDef::Function(it) => it.try_to_nav(db), - hir::ModuleDef::Adt(it) => it.try_to_nav(db), - hir::ModuleDef::Variant(it) => it.try_to_nav(db), - hir::ModuleDef::Const(it) => it.try_to_nav(db), - hir::ModuleDef::Static(it) => it.try_to_nav(db), - hir::ModuleDef::Trait(it) => it.try_to_nav(db), - hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), - hir::ModuleDef::Macro(it) => it.try_to_nav(db), + hir::ModuleDef::Module(it) => Some(it.to_nav(sema.db)), + hir::ModuleDef::Function(it) => it.try_to_nav(sema), + hir::ModuleDef::Adt(it) => it.try_to_nav(sema), + hir::ModuleDef::Variant(it) => it.try_to_nav(sema), + hir::ModuleDef::Const(it) => it.try_to_nav(sema), + hir::ModuleDef::Static(it) => it.try_to_nav(sema), + hir::ModuleDef::Trait(it) => it.try_to_nav(sema), + hir::ModuleDef::TypeAlias(it) => it.try_to_nav(sema), + hir::ModuleDef::Macro(it) => it.try_to_nav(sema), hir::ModuleDef::BuiltinType(_) => None, } } @@ -369,7 +385,11 @@ where D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay + HasCrate, D::Ast: ast::HasName, { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; Some( NavigationTarget::from_named( @@ -423,7 +443,11 @@ impl ToNav for hir::Crate { } impl TryToNav for hir::Impl { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; let derive_path = self.as_builtin_derive_path(db); @@ -447,7 +471,11 @@ impl TryToNav for hir::Impl { } impl TryToNav for hir::ExternCrateDecl { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let InFile { file_id, value } = src; let focus = value @@ -479,7 +507,11 @@ impl TryToNav for hir::ExternCrateDecl { } impl TryToNav for hir::Field { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let krate = self.parent_def(db).module(db).krate(); @@ -512,7 +544,11 @@ impl TryToNav for hir::Field { } impl TryToNav for hir::Macro { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let name_owner: &dyn ast::HasName = match &src.value { Either::Left(it) => it, @@ -533,31 +569,40 @@ impl TryToNav for hir::Macro { } impl TryToNav for hir::Adt { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::Adt::Struct(it) => it.try_to_nav(db), - hir::Adt::Union(it) => it.try_to_nav(db), - hir::Adt::Enum(it) => it.try_to_nav(db), + hir::Adt::Struct(it) => it.try_to_nav(sema), + hir::Adt::Union(it) => it.try_to_nav(sema), + hir::Adt::Enum(it) => it.try_to_nav(sema), } } } impl TryToNav for hir::AssocItem { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - AssocItem::Function(it) => it.try_to_nav(db), - AssocItem::Const(it) => it.try_to_nav(db), - AssocItem::TypeAlias(it) => it.try_to_nav(db), + AssocItem::Function(it) => it.try_to_nav(sema), + AssocItem::Const(it) => it.try_to_nav(sema), + AssocItem::TypeAlias(it) => it.try_to_nav(sema), } } } impl TryToNav for hir::GenericParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::GenericParam::TypeParam(it) => it.try_to_nav(db), - hir::GenericParam::ConstParam(it) => it.try_to_nav(db), - hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db), + hir::GenericParam::TypeParam(it) => it.try_to_nav(sema), + hir::GenericParam::ConstParam(it) => it.try_to_nav(sema), + hir::GenericParam::LifetimeParam(it) => it.try_to_nav(sema), } } } @@ -606,7 +651,11 @@ impl ToNav for hir::Local { } impl TryToNav for hir::Label { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; // Labels can't be keywords, so no escaping needed. let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr(); @@ -628,7 +677,11 @@ impl TryToNav for hir::Label { } impl TryToNav for hir::TypeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.merge().source(db)?; let edition = self.module(db).krate().edition(db); let name = self.name(db).display_no_db(edition).to_smolstr(); @@ -665,13 +718,20 @@ impl TryToNav for hir::TypeParam { } impl TryToNav for hir::TypeOrConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { - self.split(db).try_to_nav(db) + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + self.split(sema.db).try_to_nav(sema) } } impl TryToNav for hir::LifetimeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; // Lifetimes cannot be keywords, so not escaping needed. let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr(); @@ -693,7 +753,11 @@ impl TryToNav for hir::LifetimeParam { } impl TryToNav for hir::ConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.merge().source(db)?; let edition = self.module(db).krate().edition(db); let name = self.name(db).display_no_db(edition).to_smolstr(); @@ -723,7 +787,11 @@ impl TryToNav for hir::ConstParam { } impl TryToNav for hir::InlineAsmOperand { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = &self.source(db)?; let file_id = *file_id; Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( @@ -748,9 +816,11 @@ impl TryToNav for hir::InlineAsmOperand { } impl TryToNav for hir::BuiltinType { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { - let sema = Semantics::new(db); - + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let krate = db .all_crates() .iter() @@ -759,7 +829,7 @@ impl TryToNav for hir::BuiltinType { .map(Crate::from)?; let edition = krate.edition(db); - let fd = FamousDefs(&sema, krate); + let fd = FamousDefs(sema, krate); let primitive_mod = format!("prim_{}", self.name().display(fd.0.db, edition)); let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index dc509b3858389..0189939eac310 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -138,7 +138,7 @@ pub(crate) fn find_all_refs( Definition::Module(module) => { Some(NavigationTarget::from_module_to_decl(sema.db, module)) } - def => def.try_to_nav(sema.db), + def => def.try_to_nav(sema), } .map(|nav| { let (nav, extra_ref) = match nav.def_site { diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6d33ddcde0479..6ad9c7884db93 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -158,15 +158,15 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { Definition::SelfType(impl_) => runnable_impl(&sema, &impl_), _ => None, }; - add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def)); + add_opt(runnable.or_else(|| module_def_doctest(&sema, def)), Some(def)); if let Definition::SelfType(impl_) = def { impl_.items(db).into_iter().for_each(|assoc| { let runnable = match assoc { hir::AssocItem::Function(it) => { - runnable_fn(&sema, it).or_else(|| module_def_doctest(sema.db, it.into())) + runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) } - hir::AssocItem::Const(it) => module_def_doctest(sema.db, it.into()), - hir::AssocItem::TypeAlias(it) => module_def_doctest(sema.db, it.into()), + hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), + hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), }; add_opt(runnable, Some(assoc.into())) }); @@ -410,7 +410,7 @@ pub(crate) fn runnable_impl( return None; } let cfg = attrs.cfg(); - let nav = def.try_to_nav(sema.db)?.call_site(); + let nav = def.try_to_nav(sema)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); @@ -486,7 +486,8 @@ fn runnable_mod_outline_definition( }) } -fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { +fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Option { + let db = sema.db; let attrs = match def { Definition::Module(it) => it.attrs(db), Definition::Function(it) => it.attrs(db), @@ -540,7 +541,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut nav = match def { Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def), - def => def.try_to_nav(db)?, + def => def.try_to_nav(sema)?, } .call_site(); nav.focus_range = None; diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 8c3275df1baab..8214b4d1de22f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -243,7 +243,7 @@ impl StaticIndex<'_> { edition, display_target, )), - definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map( + definition: def.try_to_nav(&sema).map(UpmappingResult::call_site).map( |it| FileRange { file_id: it.file_id, range: it.focus_or_full_range() }, ), references: vec![], @@ -261,7 +261,7 @@ impl StaticIndex<'_> { let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, - is_definition: match def.try_to_nav(self.db).map(UpmappingResult::call_site) { + is_definition: match def.try_to_nav(&sema).map(UpmappingResult::call_site) { Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index 06cbd50e946ac..bd60ffe559120 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -94,7 +94,7 @@ fn discover_tests_in_module( if !f.is_test(db) { continue; } - let nav = f.try_to_nav(db).map(|r| r.call_site); + let nav = f.try_to_nav(&sema).map(|r| r.call_site); let fn_name = f.name(db).as_str().to_owned(); r.push(TestItem { id: format!("{prefix_id}::{fn_name}"), From 17943cc47a42472fed79d92cdabeff55f021d6e3 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Mon, 8 Sep 2025 13:29:06 -0500 Subject: [PATCH 0268/1900] Skip flycheck for workspace if it is already being checked --- .../src/handlers/notification.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index e193ff77743d1..fb743e73c74a4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -303,11 +303,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let world = state.snapshot(); let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); - let mut updated = false; + let mut workspace_check_triggered = false; let task = move || -> std::result::Result<(), Cancelled> { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); if invocation_strategy_once { - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - world.flycheck[0].restart_workspace(saved_file); + world.flycheck[0].restart_workspace(saved_file.clone()); } let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { @@ -330,6 +330,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { tracing::debug!(?target, "flycheck target"); // we have a specific non-library target, attempt to only check that target, nothing // else will be affected + let mut package_workspace_idx = None; if let Some((target, root, package)) = target { // trigger a package check if we have a non-library target as that can't affect // anything else in the workspace OR if we're not allowed to check the workspace as @@ -345,6 +346,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { _ => false, }); if let Some(idx) = workspace { + package_workspace_idx = Some(idx); world.flycheck[idx].restart_for_package(package, target); } } @@ -381,8 +383,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { tracing::debug!(?crate_root_paths, "flycheck crate roots"); // Find all workspaces that have at least one target containing the saved file - let workspace_ids = - world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind { + let workspace_ids = world + .workspaces + .iter() + .enumerate() + .filter(|&(idx, _)| match package_workspace_idx { + Some(pkg_idx) => idx != pkg_idx, + None => true, + }) + .filter(|&(_, ws)| match &ws.kind { project_model::ProjectWorkspaceKind::Cargo { cargo, .. } | project_model::ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), @@ -399,20 +408,18 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, }); - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { for (id, _) in workspace_ids.clone() { if id == flycheck.id() { - updated = true; + workspace_check_triggered = true; flycheck.restart_workspace(saved_file.clone()); continue 'flychecks; } } } // No specific flycheck was triggered, so let's trigger all of them. - if !updated { + if !workspace_check_triggered && package_workspace_idx.is_none() { for flycheck in world.flycheck.iter() { flycheck.restart_workspace(saved_file.clone()); } From 20226db7ff086e7ddcb9a46bcd9764e9d36bc301 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 31 Jul 2025 23:27:02 -0400 Subject: [PATCH 0269/1900] change end to last --- clippy_utils/src/sym.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f96..8033b74a8d25c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -194,7 +194,6 @@ generate! { itertools, join, kw, - last, lazy_static, lint_vec, ln, From e8206d0b88cf48d692c059a913d7124375448934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 9 Sep 2025 08:37:02 +0200 Subject: [PATCH 0270/1900] Update documentation about how to build the RA book --- src/tools/rust-analyzer/docs/book/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md index 0a3161f3af38d..11f7e8f98ca56 100644 --- a/src/tools/rust-analyzer/docs/book/README.md +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -8,6 +8,7 @@ To run the documentation site locally: ```shell cargo install mdbook +cargo install mdbook-toc cargo xtask codegen cd docs/book mdbook serve From 400e687598320fbd9690af5acc4af091b18f577d Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Mon, 8 Sep 2025 15:55:16 +0200 Subject: [PATCH 0271/1900] Bump unicode_data to version 17.0.0 --- library/core/src/unicode/unicode_data.rs | 750 ++++++++++++----------- 1 file changed, 383 insertions(+), 367 deletions(-) diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 2f53de183f629..3c38b44224f87 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,15 +1,15 @@ //! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! -// Alphabetic : 1723 bytes, 142707 codepoints in 755 ranges (U+0000AA - U+0323B0) using skiplist -// Case_Ignorable : 1043 bytes, 2744 codepoints in 447 ranges (U+0000A8 - U+0E01F0) using skiplist -// Cased : 403 bytes, 4526 codepoints in 157 ranges (U+0000AA - U+01F18A) using skiplist -// Grapheme_Extend : 887 bytes, 2193 codepoints in 375 ranges (U+000300 - U+0E01F0) using skiplist -// Lowercase : 933 bytes, 2543 codepoints in 674 ranges (U+0000AA - U+01E944) using bitset -// N : 455 bytes, 1901 codepoints in 143 ranges (U+0000B2 - U+01FBFA) using skiplist -// Uppercase : 797 bytes, 1952 codepoints in 655 ranges (U+0000C0 - U+01F18A) using bitset +// Alphabetic : 1723 bytes, 147369 codepoints in 759 ranges (U+0000AA - U+03347A) using skiplist +// Case_Ignorable : 1063 bytes, 2789 codepoints in 459 ranges (U+0000A8 - U+0E01F0) using skiplist +// Cased : 401 bytes, 4580 codepoints in 156 ranges (U+0000AA - U+01F18A) using skiplist +// Grapheme_Extend : 899 bytes, 2232 codepoints in 383 ranges (U+000300 - U+0E01F0) using skiplist +// Lowercase : 943 bytes, 2569 codepoints in 676 ranges (U+0000AA - U+01E944) using bitset +// N : 463 bytes, 1914 codepoints in 145 ranges (U+0000B2 - U+01FBFA) using skiplist +// Uppercase : 799 bytes, 1980 codepoints in 659 ranges (U+0000C0 - U+01F18A) using bitset // White_Space : 256 bytes, 19 codepoints in 8 ranges (U+000085 - U+003001) using cascading -// to_lower : 11484 bytes -// to_upper : 13432 bytes -// Total : 31413 bytes +// to_lower : 11708 bytes +// to_upper : 13656 bytes +// Total : 31911 bytes #[inline(always)] const fn bitset_search< @@ -140,53 +140,52 @@ unsafe fn skip_search( offset_idx % 2 == 1 } -pub const UNICODE_VERSION: (u8, u8, u8) = (16, 0, 0); +pub const UNICODE_VERSION: (u8, u8, u8) = (17, 0, 0); #[rustfmt::skip] pub mod alphabetic { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 53] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 51] = [ ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(12, 4681), ShortOffsetRunHeader::new(414, 5741), ShortOffsetRunHeader::new(452, 7958), ShortOffsetRunHeader::new(552, 9398), ShortOffsetRunHeader::new(623, 11264), ShortOffsetRunHeader::new(625, 12293), ShortOffsetRunHeader::new(663, 13312), ShortOffsetRunHeader::new(687, 19904), ShortOffsetRunHeader::new(688, 42125), ShortOffsetRunHeader::new(690, 42509), ShortOffsetRunHeader::new(694, 55204), - ShortOffsetRunHeader::new(784, 63744), ShortOffsetRunHeader::new(789, 64110), - ShortOffsetRunHeader::new(790, 64830), ShortOffsetRunHeader::new(812, 66176), - ShortOffsetRunHeader::new(853, 67383), ShortOffsetRunHeader::new(900, 73440), + ShortOffsetRunHeader::new(778, 63744), ShortOffsetRunHeader::new(783, 64110), + ShortOffsetRunHeader::new(784, 64830), ShortOffsetRunHeader::new(806, 66176), + ShortOffsetRunHeader::new(847, 67383), ShortOffsetRunHeader::new(894, 73440), ShortOffsetRunHeader::new(1217, 74650), ShortOffsetRunHeader::new(1228, 77712), ShortOffsetRunHeader::new(1233, 78896), ShortOffsetRunHeader::new(1236, 82939), ShortOffsetRunHeader::new(1240, 83527), ShortOffsetRunHeader::new(1242, 90368), ShortOffsetRunHeader::new(1243, 92160), ShortOffsetRunHeader::new(1245, 92729), - ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 100344), - ShortOffsetRunHeader::new(1278, 101590), ShortOffsetRunHeader::new(1280, 110576), - ShortOffsetRunHeader::new(1283, 110883), ShortOffsetRunHeader::new(1290, 111356), - ShortOffsetRunHeader::new(1300, 113664), ShortOffsetRunHeader::new(1301, 119808), - ShortOffsetRunHeader::new(1311, 120486), ShortOffsetRunHeader::new(1348, 122624), - ShortOffsetRunHeader::new(1371, 123536), ShortOffsetRunHeader::new(1395, 124112), - ShortOffsetRunHeader::new(1399, 124896), ShortOffsetRunHeader::new(1405, 126464), - ShortOffsetRunHeader::new(1421, 127280), ShortOffsetRunHeader::new(1487, 131072), - ShortOffsetRunHeader::new(1493, 173792), ShortOffsetRunHeader::new(1494, 177978), - ShortOffsetRunHeader::new(1496, 183970), ShortOffsetRunHeader::new(1500, 191457), - ShortOffsetRunHeader::new(1502, 192094), ShortOffsetRunHeader::new(1504, 194560), - ShortOffsetRunHeader::new(1505, 195102), ShortOffsetRunHeader::new(1506, 196608), - ShortOffsetRunHeader::new(1507, 201547), ShortOffsetRunHeader::new(1508, 205744), - ShortOffsetRunHeader::new(1510, 1319856), + ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 101590), + ShortOffsetRunHeader::new(1282, 110576), ShortOffsetRunHeader::new(1287, 110883), + ShortOffsetRunHeader::new(1294, 111356), ShortOffsetRunHeader::new(1304, 113664), + ShortOffsetRunHeader::new(1305, 119808), ShortOffsetRunHeader::new(1315, 120486), + ShortOffsetRunHeader::new(1352, 122624), ShortOffsetRunHeader::new(1375, 123536), + ShortOffsetRunHeader::new(1399, 124112), ShortOffsetRunHeader::new(1403, 126464), + ShortOffsetRunHeader::new(1431, 127280), ShortOffsetRunHeader::new(1497, 131072), + ShortOffsetRunHeader::new(1503, 173792), ShortOffsetRunHeader::new(1504, 178206), + ShortOffsetRunHeader::new(1506, 183982), ShortOffsetRunHeader::new(1508, 191457), + ShortOffsetRunHeader::new(1510, 192094), ShortOffsetRunHeader::new(1512, 194560), + ShortOffsetRunHeader::new(1513, 195102), ShortOffsetRunHeader::new(1514, 196608), + ShortOffsetRunHeader::new(1515, 201547), ShortOffsetRunHeader::new(1516, 210042), + ShortOffsetRunHeader::new(1518, 1324154), ]; - static OFFSETS: [u8; 1511] = [ + static OFFSETS: [u8; 1519] = [ 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, 18, 1, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, 39, 14, 1, 1, 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, 10, 3, 2, 1, - 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, 8, 1, 8, 42, + 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 7, 7, 1, 8, 42, 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, 3, 4, 3, 8, 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, - 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 2, - 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, 4, 13, 3, + 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 1, + 2, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 5, 3, 1, 4, 13, 3, 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, 24, 1, 9, 1, 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, 1, 1, 1, 19, 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, 55, 1, 1, 2, 5, @@ -201,37 +200,37 @@ pub mod alphabetic { 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, 16, 23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, 5, 4, 86, 6, 3, 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, 10, 2, 20, 47, - 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, 12, 68, 1, 1, - 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, - 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, - 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, - 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, - 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, - 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, - 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, - 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 70, 56, - 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, - 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 3, - 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, - 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, - 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, - 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, - 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, - 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, - 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, 33, 31, 9, 1, - 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, - 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, 17, - 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, 4, - 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, 8, 0, 41, - 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, 7, 10, 4, - 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, - 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, - 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, 10, 7, 16, - 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, - 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, - 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, - 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, 0, 0, 0, 0, - 5, 0, 0, + 5, 8, 3, 113, 39, 9, 2, 103, 2, 82, 20, 21, 1, 33, 24, 52, 12, 68, 1, 1, 44, 6, 3, 1, 1, 3, + 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, 18, 23, 3, 69, 1, + 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, 6, 123, 21, 0, 12, + 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, 2, 1, 108, 33, 0, + 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, 2, 6, 2, 3, 35, + 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, 13, 30, 5, 43, + 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, 15, 1, 7, 1, 2, + 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, 69, 6, 2, 1, 1, + 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 6, 26, 38, 56, 6, 2, 64, + 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, 13, 18, + 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 6, 50, 3, 3, + 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, 51, 17, 4, + 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, 1, 1, 4, + 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, 3, 1, 6, + 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, 66, 1, 3, + 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, 2, 1, 71, + 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, 4, 93, 8, + 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 103, 8, 88, 33, 31, 9, 1, 45, + 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, 6, + 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 23, 44, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, + 17, 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, + 4, 31, 21, 5, 19, 0, 45, 211, 64, 32, 25, 2, 25, 44, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 7, + 9, 0, 41, 32, 97, 115, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, + 13, 3, 9, 7, 10, 4, 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, + 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, + 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, + 1, 112, 45, 10, 7, 16, 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 207, 31, 1, 22, 8, 2, 224, 7, + 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, + 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, + 32, 0, 2, 0, 2, 0, 15, 0, 0, 0, 0, 0, 5, 0, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -259,28 +258,27 @@ pub mod alphabetic { pub mod case_ignorable { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 37] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 36] = [ ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(11, 4957), ShortOffsetRunHeader::new(263, 5906), ShortOffsetRunHeader::new(265, 8125), - ShortOffsetRunHeader::new(375, 11388), ShortOffsetRunHeader::new(409, 12293), - ShortOffsetRunHeader::new(421, 40981), ShortOffsetRunHeader::new(433, 42232), - ShortOffsetRunHeader::new(435, 42508), ShortOffsetRunHeader::new(437, 64286), - ShortOffsetRunHeader::new(533, 65024), ShortOffsetRunHeader::new(537, 66045), - ShortOffsetRunHeader::new(567, 67456), ShortOffsetRunHeader::new(573, 68097), - ShortOffsetRunHeader::new(579, 68900), ShortOffsetRunHeader::new(591, 69291), - ShortOffsetRunHeader::new(599, 71727), ShortOffsetRunHeader::new(723, 71995), - ShortOffsetRunHeader::new(727, 72752), ShortOffsetRunHeader::new(755, 73459), - ShortOffsetRunHeader::new(785, 78896), ShortOffsetRunHeader::new(797, 90398), - ShortOffsetRunHeader::new(801, 92912), ShortOffsetRunHeader::new(805, 93504), - ShortOffsetRunHeader::new(811, 94031), ShortOffsetRunHeader::new(815, 110576), - ShortOffsetRunHeader::new(823, 113821), ShortOffsetRunHeader::new(829, 118528), - ShortOffsetRunHeader::new(833, 119143), ShortOffsetRunHeader::new(837, 121344), - ShortOffsetRunHeader::new(847, 122880), ShortOffsetRunHeader::new(859, 123566), - ShortOffsetRunHeader::new(875, 124139), ShortOffsetRunHeader::new(879, 125136), - ShortOffsetRunHeader::new(883, 127995), ShortOffsetRunHeader::new(887, 917505), - ShortOffsetRunHeader::new(889, 2032112), + ShortOffsetRunHeader::new(377, 11388), ShortOffsetRunHeader::new(411, 12293), + ShortOffsetRunHeader::new(423, 40981), ShortOffsetRunHeader::new(435, 42232), + ShortOffsetRunHeader::new(437, 42508), ShortOffsetRunHeader::new(439, 64286), + ShortOffsetRunHeader::new(535, 65024), ShortOffsetRunHeader::new(539, 66045), + ShortOffsetRunHeader::new(569, 67456), ShortOffsetRunHeader::new(575, 68097), + ShortOffsetRunHeader::new(581, 68900), ShortOffsetRunHeader::new(593, 69291), + ShortOffsetRunHeader::new(601, 71727), ShortOffsetRunHeader::new(727, 71995), + ShortOffsetRunHeader::new(731, 73459), ShortOffsetRunHeader::new(797, 78896), + ShortOffsetRunHeader::new(809, 90398), ShortOffsetRunHeader::new(813, 92912), + ShortOffsetRunHeader::new(817, 93504), ShortOffsetRunHeader::new(823, 94031), + ShortOffsetRunHeader::new(827, 110576), ShortOffsetRunHeader::new(837, 113821), + ShortOffsetRunHeader::new(843, 118528), ShortOffsetRunHeader::new(847, 119143), + ShortOffsetRunHeader::new(851, 121344), ShortOffsetRunHeader::new(861, 122880), + ShortOffsetRunHeader::new(873, 123566), ShortOffsetRunHeader::new(889, 124139), + ShortOffsetRunHeader::new(893, 125136), ShortOffsetRunHeader::new(907, 127995), + ShortOffsetRunHeader::new(911, 917505), ShortOffsetRunHeader::new(913, 2032112), ]; - static OFFSETS: [u8; 895] = [ + static OFFSETS: [u8; 919] = [ 168, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, 1, 1, 251, 7, 207, 1, 5, 1, 49, 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, 1, 10, 21, 16, 1, 101, 8, 1, 10, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, @@ -292,28 +290,29 @@ pub mod case_ignorable { 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, - 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, - 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, - 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, - 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, - 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, - 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, - 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, - 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, - 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, - 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, - 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, - 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, - 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, - 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, - 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, - 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, - 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, - 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, - 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, - 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, - 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, - 0, 5, 0, 1, 30, 96, 128, 240, 0, + 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 46, 2, 12, 20, 4, 48, 1, 1, 5, 1, 1, 5, 1, + 40, 9, 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, + 1, 7, 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, + 5, 8, 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, + 15, 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, + 1, 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 102, 4, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, + 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, + 12, 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, + 9, 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, + 1, 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, + 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 24, 1, 52, 6, 70, 11, + 49, 4, 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, + 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, + 2, 3, 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, + 1, 4, 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, + 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, + 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 198, 1, 1, 3, 1, 1, 201, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, + 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 65, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, + 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, + 2, 13, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, + 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, + 0, 1, 61, 4, 0, 5, 254, 2, 243, 1, 2, 1, 7, 2, 5, 1, 9, 1, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, + 128, 240, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -346,24 +345,24 @@ pub mod cased { ShortOffsetRunHeader::new(61, 7296), ShortOffsetRunHeader::new(65, 7958), ShortOffsetRunHeader::new(74, 9398), ShortOffsetRunHeader::new(149, 11264), ShortOffsetRunHeader::new(151, 42560), ShortOffsetRunHeader::new(163, 43824), - ShortOffsetRunHeader::new(183, 64256), ShortOffsetRunHeader::new(189, 65313), - ShortOffsetRunHeader::new(193, 66560), ShortOffsetRunHeader::new(197, 67456), - ShortOffsetRunHeader::new(219, 68736), ShortOffsetRunHeader::new(227, 71840), - ShortOffsetRunHeader::new(235, 93760), ShortOffsetRunHeader::new(237, 119808), - ShortOffsetRunHeader::new(239, 120486), ShortOffsetRunHeader::new(276, 122624), - ShortOffsetRunHeader::new(299, 122928), ShortOffsetRunHeader::new(305, 125184), - ShortOffsetRunHeader::new(307, 127280), ShortOffsetRunHeader::new(309, 1241482), + ShortOffsetRunHeader::new(177, 64256), ShortOffsetRunHeader::new(183, 65313), + ShortOffsetRunHeader::new(187, 66560), ShortOffsetRunHeader::new(191, 67456), + ShortOffsetRunHeader::new(213, 68736), ShortOffsetRunHeader::new(221, 71840), + ShortOffsetRunHeader::new(229, 93760), ShortOffsetRunHeader::new(231, 119808), + ShortOffsetRunHeader::new(237, 120486), ShortOffsetRunHeader::new(274, 122624), + ShortOffsetRunHeader::new(297, 122928), ShortOffsetRunHeader::new(303, 125184), + ShortOffsetRunHeader::new(305, 127280), ShortOffsetRunHeader::new(307, 1241482), ]; - static OFFSETS: [u8; 315] = [ - 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, 96, 1, 42, 4, + static OFFSETS: [u8; 313] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 2, 35, 7, 2, 30, 5, 96, 1, 42, 4, 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, 41, 0, 38, 1, 1, 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, 2, 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 6, 4, 1, 2, 4, 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 0, 46, 18, 30, 132, - 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, - 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, - 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, + 102, 3, 4, 1, 77, 20, 6, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, 26, 0, 80, 96, + 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, 1, 42, 1, 9, 0, + 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 32, 25, 2, 25, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 10, 1, 20, 6, 6, 0, 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, @@ -394,26 +393,26 @@ pub mod cased { pub mod grapheme_extend { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 34] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 33] = [ ShortOffsetRunHeader::new(0, 768), ShortOffsetRunHeader::new(1, 1155), ShortOffsetRunHeader::new(3, 1425), ShortOffsetRunHeader::new(5, 4957), ShortOffsetRunHeader::new(249, 5906), ShortOffsetRunHeader::new(251, 8204), - ShortOffsetRunHeader::new(345, 11503), ShortOffsetRunHeader::new(349, 12330), - ShortOffsetRunHeader::new(355, 42607), ShortOffsetRunHeader::new(359, 43010), - ShortOffsetRunHeader::new(367, 64286), ShortOffsetRunHeader::new(433, 65024), - ShortOffsetRunHeader::new(435, 65438), ShortOffsetRunHeader::new(439, 66045), - ShortOffsetRunHeader::new(441, 68097), ShortOffsetRunHeader::new(447, 68900), - ShortOffsetRunHeader::new(459, 69291), ShortOffsetRunHeader::new(463, 71727), - ShortOffsetRunHeader::new(599, 72752), ShortOffsetRunHeader::new(631, 73459), - ShortOffsetRunHeader::new(661, 78912), ShortOffsetRunHeader::new(671, 90398), - ShortOffsetRunHeader::new(675, 92912), ShortOffsetRunHeader::new(679, 94031), - ShortOffsetRunHeader::new(683, 113821), ShortOffsetRunHeader::new(691, 118528), - ShortOffsetRunHeader::new(693, 119141), ShortOffsetRunHeader::new(697, 121344), - ShortOffsetRunHeader::new(709, 122880), ShortOffsetRunHeader::new(721, 123566), - ShortOffsetRunHeader::new(735, 124140), ShortOffsetRunHeader::new(739, 125136), - ShortOffsetRunHeader::new(743, 917536), ShortOffsetRunHeader::new(747, 2032112), + ShortOffsetRunHeader::new(347, 11503), ShortOffsetRunHeader::new(351, 12330), + ShortOffsetRunHeader::new(357, 42607), ShortOffsetRunHeader::new(361, 43010), + ShortOffsetRunHeader::new(369, 64286), ShortOffsetRunHeader::new(435, 65024), + ShortOffsetRunHeader::new(437, 65438), ShortOffsetRunHeader::new(441, 66045), + ShortOffsetRunHeader::new(443, 68097), ShortOffsetRunHeader::new(449, 68900), + ShortOffsetRunHeader::new(461, 69291), ShortOffsetRunHeader::new(465, 71727), + ShortOffsetRunHeader::new(601, 73459), ShortOffsetRunHeader::new(669, 78912), + ShortOffsetRunHeader::new(679, 90398), ShortOffsetRunHeader::new(683, 92912), + ShortOffsetRunHeader::new(687, 94031), ShortOffsetRunHeader::new(691, 113821), + ShortOffsetRunHeader::new(699, 118528), ShortOffsetRunHeader::new(701, 119141), + ShortOffsetRunHeader::new(705, 121344), ShortOffsetRunHeader::new(717, 122880), + ShortOffsetRunHeader::new(729, 123566), ShortOffsetRunHeader::new(743, 124140), + ShortOffsetRunHeader::new(747, 125136), ShortOffsetRunHeader::new(759, 917536), + ShortOffsetRunHeader::new(763, 2032112), ]; - static OFFSETS: [u8; 751] = [ + static OFFSETS: [u8; 767] = [ 0, 112, 0, 7, 0, 45, 1, 1, 1, 2, 1, 2, 1, 1, 72, 11, 48, 21, 16, 1, 101, 7, 2, 6, 2, 2, 1, 4, 35, 1, 30, 27, 91, 11, 58, 9, 9, 1, 24, 4, 1, 9, 1, 3, 1, 5, 43, 3, 59, 9, 42, 24, 1, 32, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 29, 1, 58, 1, 1, 1, 2, 4, 8, 1, 9, 1, 10, 2, 26, 1, 2, @@ -424,23 +423,24 @@ pub mod grapheme_extend { 12, 8, 98, 1, 2, 9, 11, 7, 73, 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 0, 3, 0, 4, 28, 3, 29, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 9, 1, 45, 3, 1, 1, 117, 2, 34, 1, 118, 3, 4, 2, 9, - 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 48, 31, 49, 4, 48, 10, 4, - 3, 38, 9, 12, 2, 32, 4, 2, 6, 56, 1, 1, 2, 3, 1, 1, 5, 56, 8, 2, 2, 152, 3, 1, 13, 1, 7, 4, - 1, 6, 1, 3, 2, 198, 64, 0, 1, 195, 33, 0, 3, 141, 1, 96, 32, 0, 6, 105, 2, 0, 4, 1, 10, 32, - 2, 80, 2, 0, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 1, 1, 44, 3, - 48, 1, 2, 4, 2, 2, 2, 1, 36, 1, 67, 6, 2, 2, 2, 2, 12, 1, 8, 1, 47, 1, 51, 1, 1, 3, 2, 2, 5, - 2, 1, 1, 42, 2, 8, 1, 238, 1, 2, 1, 4, 1, 0, 1, 0, 16, 16, 16, 0, 2, 0, 1, 226, 1, 149, 5, - 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 65, 5, 0, 2, 79, 4, 70, 11, 49, 4, 123, 1, 54, - 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 7, 1, 61, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 1, 1, 8, - 4, 2, 1, 95, 3, 2, 4, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 1, 1, 1, 1, 12, 1, 9, 1, 14, - 7, 3, 5, 67, 1, 2, 6, 1, 1, 2, 1, 1, 3, 4, 3, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 81, 1, - 2, 6, 1, 1, 2, 1, 1, 2, 1, 2, 235, 1, 2, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, - 1, 2, 8, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 245, 1, 10, 4, 4, 1, 144, 4, 2, 2, 4, 1, 32, - 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, - 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 3, 23, 1, 0, 1, 6, - 15, 0, 12, 3, 3, 0, 5, 59, 7, 0, 1, 63, 4, 81, 1, 11, 2, 0, 2, 0, 46, 2, 23, 0, 5, 3, 6, 8, - 8, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, - 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 254, 2, 0, 7, 109, 7, 0, 96, 128, 240, 0, + 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 48, 46, 2, 12, 20, 4, 48, + 10, 4, 3, 38, 9, 12, 2, 32, 4, 2, 6, 56, 1, 1, 2, 3, 1, 1, 5, 56, 8, 2, 2, 152, 3, 1, 13, 1, + 7, 4, 1, 6, 1, 3, 2, 198, 64, 0, 1, 195, 33, 0, 3, 141, 1, 96, 32, 0, 6, 105, 2, 0, 4, 1, + 10, 32, 2, 80, 2, 0, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 1, 1, + 44, 3, 48, 1, 2, 4, 2, 2, 2, 1, 36, 1, 67, 6, 2, 2, 2, 2, 12, 1, 8, 1, 47, 1, 51, 1, 1, 3, + 2, 2, 5, 2, 1, 1, 42, 2, 8, 1, 238, 1, 2, 1, 4, 1, 0, 1, 0, 16, 16, 16, 0, 2, 0, 1, 226, 1, + 149, 5, 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 65, 5, 0, 2, 77, 6, 70, 11, 49, 4, 123, + 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 7, 1, 61, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, + 1, 1, 8, 4, 2, 1, 95, 3, 2, 4, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 1, 1, 1, 1, 12, 1, 9, + 1, 14, 7, 3, 5, 67, 1, 2, 6, 1, 1, 2, 1, 1, 3, 4, 3, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, + 81, 1, 2, 6, 1, 1, 2, 1, 1, 2, 1, 2, 235, 1, 2, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, + 106, 1, 1, 1, 2, 8, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 245, 1, 10, 4, 4, 1, 144, 4, 2, 2, + 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 198, 1, 1, 3, 1, 1, 201, 7, 1, 6, + 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, + 2, 52, 5, 5, 3, 23, 1, 0, 1, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 0, 1, 63, 4, 81, 1, 11, 2, 0, + 2, 0, 46, 2, 23, 0, 5, 3, 6, 8, 8, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, + 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 254, 2, 243, 1, 2, 1, + 7, 2, 5, 1, 0, 7, 109, 7, 0, 96, 128, 240, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -475,27 +475,27 @@ pub mod lowercase { ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 20] = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 56, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 43, 0, 52, 48, 50, 33], - [0, 0, 0, 0, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 3, 0, 16, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27], - [0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 34, 17, 23, 53, 54, 49, 47, 7, 35, 42, 0, 28, 12, 31], - [0, 0, 46, 0, 56, 56, 56, 0, 22, 22, 69, 22, 36, 25, 24, 37], - [0, 5, 70, 0, 29, 15, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [10, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], - [16, 26, 22, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 51, 2, 21, 68, 8, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [65, 41, 55, 11, 66, 63, 18, 13, 1, 64, 76, 20, 73, 74, 4, 45], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 57, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 19, 62, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 44, 0, 53, 49, 51, 34], + [0, 0, 0, 0, 9, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 3, 0, 16, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28], + [0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 35, 17, 24, 54, 55, 50, 48, 7, 36, 43, 0, 29, 12, 32], + [0, 0, 47, 0, 57, 57, 57, 0, 23, 23, 71, 23, 37, 26, 25, 38], + [0, 5, 72, 0, 30, 15, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [10, 61, 0, 6, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 33, 0], + [16, 27, 23, 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 52, 2, 22, 70, 8, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [67, 42, 56, 11, 68, 65, 18, 13, 1, 66, 78, 21, 75, 76, 4, 46], ]; - static BITSET_CANONICAL: [u64; 56] = [ + static BITSET_CANONICAL: [u64; 57] = [ 0b0000000000000000000000000000000000000000000000000000000000000000, 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1010101010101010101010101010101010101010101010101010100000000010, @@ -515,6 +515,7 @@ pub mod lowercase { 0b1111111111111111000000000000000000000000000000000000000000000000, 0b1111111101111111111111111111111110000000000000000000000000000000, 0b1111110000000000000000000000000011111111111111111111111111000000, + 0b1111100000000000000000000000000000000000000000000000000000000000, 0b1111011111111111111111111111111111111111111111110000000000000000, 0b1111000000000000000000000000001111110111111111111111111111111100, 0b1010101010101010101010101010101010101010101010101101010101010100, @@ -529,9 +530,9 @@ pub mod lowercase { 0b0001101111111011111111111111101111111111100000000000000000000000, 0b0001100100101111101010101010101010101010111000110111111111111111, 0b0000011111111101111111111111111111111111111111111111111110111001, - 0b0000011101011100000000000000000000001010101010100010010100001010, + 0b0000011101011110000000000000000000001010101010101010010100001010, 0b0000010000100000000001000000000000000000000000000000000000000000, - 0b0000000111111111111111111111111111111111111011111111111111111111, + 0b0000000111111111111111111111111111111111110011111111111111111111, 0b0000000011111111000000001111111100000000001111110000000011111111, 0b0000000011011100000000001111111100000000110011110000000011011100, 0b0000000000001000010100000001101010101010101010101010101010101010, @@ -553,10 +554,10 @@ pub mod lowercase { 0b1110011001010001001011010010101001001110001001000011000100101001, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET_MAPPING: [(u8, u8); 21] = [ - (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 161), (1, 146), (1, 144), (1, 140), - (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), - (4, 6), (5, 187), (6, 78), + static BITSET_MAPPING: [(u8, u8); 22] = [ + (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 168), (1, 161), (1, 146), (1, 144), + (1, 140), (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), + (4, 12), (4, 6), (5, 187), (6, 78), ]; pub const fn lookup(c: char) -> bool { @@ -576,7 +577,7 @@ pub mod lowercase { pub mod n { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 42] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 43] = [ ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(7, 2406), ShortOffsetRunHeader::new(13, 4160), ShortOffsetRunHeader::new(47, 4969), ShortOffsetRunHeader::new(51, 5870), ShortOffsetRunHeader::new(53, 6470), @@ -590,16 +591,17 @@ pub mod n { ShortOffsetRunHeader::new(181, 69216), ShortOffsetRunHeader::new(187, 70736), ShortOffsetRunHeader::new(207, 71248), ShortOffsetRunHeader::new(211, 71904), ShortOffsetRunHeader::new(219, 72688), ShortOffsetRunHeader::new(223, 73552), - ShortOffsetRunHeader::new(231, 74752), ShortOffsetRunHeader::new(235, 90416), - ShortOffsetRunHeader::new(237, 92768), ShortOffsetRunHeader::new(239, 93552), - ShortOffsetRunHeader::new(247, 93824), ShortOffsetRunHeader::new(249, 118000), - ShortOffsetRunHeader::new(251, 119488), ShortOffsetRunHeader::new(253, 120782), - ShortOffsetRunHeader::new(259, 123200), ShortOffsetRunHeader::new(261, 123632), - ShortOffsetRunHeader::new(263, 124144), ShortOffsetRunHeader::new(265, 125127), - ShortOffsetRunHeader::new(269, 126065), ShortOffsetRunHeader::new(273, 127232), - ShortOffsetRunHeader::new(283, 130032), ShortOffsetRunHeader::new(285, 1244154), + ShortOffsetRunHeader::new(233, 74752), ShortOffsetRunHeader::new(237, 90416), + ShortOffsetRunHeader::new(239, 92768), ShortOffsetRunHeader::new(241, 93552), + ShortOffsetRunHeader::new(249, 93824), ShortOffsetRunHeader::new(251, 94196), + ShortOffsetRunHeader::new(253, 118000), ShortOffsetRunHeader::new(255, 119488), + ShortOffsetRunHeader::new(257, 120782), ShortOffsetRunHeader::new(263, 123200), + ShortOffsetRunHeader::new(265, 123632), ShortOffsetRunHeader::new(267, 124144), + ShortOffsetRunHeader::new(269, 125127), ShortOffsetRunHeader::new(273, 126065), + ShortOffsetRunHeader::new(277, 127232), ShortOffsetRunHeader::new(287, 130032), + ShortOffsetRunHeader::new(289, 1244154), ]; - static OFFSETS: [u8; 287] = [ + static OFFSETS: [u8; 291] = [ 178, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, 10, 118, 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, 70, 20, 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, 182, 10, @@ -609,9 +611,9 @@ pub mod n { 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, 52, 2, 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, 7, 134, 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, 76, 12, - 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, 86, 10, - 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, 10, 247, - 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, + 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 54, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, + 86, 10, 134, 10, 1, 7, 0, 10, 0, 23, 0, 3, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, + 10, 0, 10, 247, 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -647,28 +649,28 @@ pub mod uppercase { static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 0], [44, 44, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 62, 17, 43, 29, 24, 23], + [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 66, 17, 43, 29, 24, 23], [44, 44, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], [44, 44, 44, 44, 9, 8, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 37, 28, 66, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 37, 28, 67, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 61, 60, 44, 20, 14, 16, 4], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 63, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 65, 64, 44, 20, 14, 16, 4], [44, 44, 44, 44, 50, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 53, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 54, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [51, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [52, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 19, 3, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [52, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 3, 65, 64, 63, 67], + [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 2, 62, 61, 60, 68], ]; static BITSET_CANONICAL: [u64; 44] = [ 0b0000000000111111111111111111111111111111111111111111111111111111, 0b1111111111111111111111110000000000000000000000000011111111111111, - 0b0101010101010101010101010101010101010101010101010101010000000001, 0b0000011111111111111111111111110000000000000000000000000000000001, - 0b0000000000100000000000000000000000010101010000010001101011110101, + 0b0101010101010101010101010101010101010101010101010101010000000001, + 0b0000000000100000000000000000000000010101010101010101101011110101, 0b1111111111111111111111111111111100000000000000000000000000000000, 0b1111111111111111111111110000000000000000000000000000001111111111, 0b1111111111111111111100000000000000000000000000011111110001011111, @@ -709,10 +711,10 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET_MAPPING: [(u8, u8); 24] = [ + static BITSET_MAPPING: [(u8, u8); 25] = [ (0, 182), (0, 74), (0, 166), (0, 162), (0, 159), (0, 150), (0, 148), (0, 142), (0, 134), - (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 164), (2, 146), (2, 20), - (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), + (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 146), (2, 140), (2, 134), + (2, 130), (3, 164), (3, 146), (3, 20), (4, 178), (4, 171), ]; pub const fn lookup(c: char) -> bool { @@ -792,7 +794,7 @@ pub mod conversions { } } - static LOWERCASE_TABLE: &[(char, u32); 1434] = &[ + static LOWERCASE_TABLE: &[(char, u32); 1462] = &[ ('\u{c0}', 224), ('\u{c1}', 225), ('\u{c2}', 226), ('\u{c3}', 227), ('\u{c4}', 228), ('\u{c5}', 229), ('\u{c6}', 230), ('\u{c7}', 231), ('\u{c8}', 232), ('\u{c9}', 233), ('\u{ca}', 234), ('\u{cb}', 235), ('\u{cc}', 236), ('\u{cd}', 237), ('\u{ce}', 238), @@ -1060,77 +1062,84 @@ pub mod conversions { ('\u{a7b4}', 42933), ('\u{a7b6}', 42935), ('\u{a7b8}', 42937), ('\u{a7ba}', 42939), ('\u{a7bc}', 42941), ('\u{a7be}', 42943), ('\u{a7c0}', 42945), ('\u{a7c2}', 42947), ('\u{a7c4}', 42900), ('\u{a7c5}', 642), ('\u{a7c6}', 7566), ('\u{a7c7}', 42952), - ('\u{a7c9}', 42954), ('\u{a7cb}', 612), ('\u{a7cc}', 42957), ('\u{a7d0}', 42961), - ('\u{a7d6}', 42967), ('\u{a7d8}', 42969), ('\u{a7da}', 42971), ('\u{a7dc}', 411), - ('\u{a7f5}', 42998), ('\u{ff21}', 65345), ('\u{ff22}', 65346), ('\u{ff23}', 65347), - ('\u{ff24}', 65348), ('\u{ff25}', 65349), ('\u{ff26}', 65350), ('\u{ff27}', 65351), - ('\u{ff28}', 65352), ('\u{ff29}', 65353), ('\u{ff2a}', 65354), ('\u{ff2b}', 65355), - ('\u{ff2c}', 65356), ('\u{ff2d}', 65357), ('\u{ff2e}', 65358), ('\u{ff2f}', 65359), - ('\u{ff30}', 65360), ('\u{ff31}', 65361), ('\u{ff32}', 65362), ('\u{ff33}', 65363), - ('\u{ff34}', 65364), ('\u{ff35}', 65365), ('\u{ff36}', 65366), ('\u{ff37}', 65367), - ('\u{ff38}', 65368), ('\u{ff39}', 65369), ('\u{ff3a}', 65370), ('\u{10400}', 66600), - ('\u{10401}', 66601), ('\u{10402}', 66602), ('\u{10403}', 66603), ('\u{10404}', 66604), - ('\u{10405}', 66605), ('\u{10406}', 66606), ('\u{10407}', 66607), ('\u{10408}', 66608), - ('\u{10409}', 66609), ('\u{1040a}', 66610), ('\u{1040b}', 66611), ('\u{1040c}', 66612), - ('\u{1040d}', 66613), ('\u{1040e}', 66614), ('\u{1040f}', 66615), ('\u{10410}', 66616), - ('\u{10411}', 66617), ('\u{10412}', 66618), ('\u{10413}', 66619), ('\u{10414}', 66620), - ('\u{10415}', 66621), ('\u{10416}', 66622), ('\u{10417}', 66623), ('\u{10418}', 66624), - ('\u{10419}', 66625), ('\u{1041a}', 66626), ('\u{1041b}', 66627), ('\u{1041c}', 66628), - ('\u{1041d}', 66629), ('\u{1041e}', 66630), ('\u{1041f}', 66631), ('\u{10420}', 66632), - ('\u{10421}', 66633), ('\u{10422}', 66634), ('\u{10423}', 66635), ('\u{10424}', 66636), - ('\u{10425}', 66637), ('\u{10426}', 66638), ('\u{10427}', 66639), ('\u{104b0}', 66776), - ('\u{104b1}', 66777), ('\u{104b2}', 66778), ('\u{104b3}', 66779), ('\u{104b4}', 66780), - ('\u{104b5}', 66781), ('\u{104b6}', 66782), ('\u{104b7}', 66783), ('\u{104b8}', 66784), - ('\u{104b9}', 66785), ('\u{104ba}', 66786), ('\u{104bb}', 66787), ('\u{104bc}', 66788), - ('\u{104bd}', 66789), ('\u{104be}', 66790), ('\u{104bf}', 66791), ('\u{104c0}', 66792), - ('\u{104c1}', 66793), ('\u{104c2}', 66794), ('\u{104c3}', 66795), ('\u{104c4}', 66796), - ('\u{104c5}', 66797), ('\u{104c6}', 66798), ('\u{104c7}', 66799), ('\u{104c8}', 66800), - ('\u{104c9}', 66801), ('\u{104ca}', 66802), ('\u{104cb}', 66803), ('\u{104cc}', 66804), - ('\u{104cd}', 66805), ('\u{104ce}', 66806), ('\u{104cf}', 66807), ('\u{104d0}', 66808), - ('\u{104d1}', 66809), ('\u{104d2}', 66810), ('\u{104d3}', 66811), ('\u{10570}', 66967), - ('\u{10571}', 66968), ('\u{10572}', 66969), ('\u{10573}', 66970), ('\u{10574}', 66971), - ('\u{10575}', 66972), ('\u{10576}', 66973), ('\u{10577}', 66974), ('\u{10578}', 66975), - ('\u{10579}', 66976), ('\u{1057a}', 66977), ('\u{1057c}', 66979), ('\u{1057d}', 66980), - ('\u{1057e}', 66981), ('\u{1057f}', 66982), ('\u{10580}', 66983), ('\u{10581}', 66984), - ('\u{10582}', 66985), ('\u{10583}', 66986), ('\u{10584}', 66987), ('\u{10585}', 66988), - ('\u{10586}', 66989), ('\u{10587}', 66990), ('\u{10588}', 66991), ('\u{10589}', 66992), - ('\u{1058a}', 66993), ('\u{1058c}', 66995), ('\u{1058d}', 66996), ('\u{1058e}', 66997), - ('\u{1058f}', 66998), ('\u{10590}', 66999), ('\u{10591}', 67000), ('\u{10592}', 67001), - ('\u{10594}', 67003), ('\u{10595}', 67004), ('\u{10c80}', 68800), ('\u{10c81}', 68801), - ('\u{10c82}', 68802), ('\u{10c83}', 68803), ('\u{10c84}', 68804), ('\u{10c85}', 68805), - ('\u{10c86}', 68806), ('\u{10c87}', 68807), ('\u{10c88}', 68808), ('\u{10c89}', 68809), - ('\u{10c8a}', 68810), ('\u{10c8b}', 68811), ('\u{10c8c}', 68812), ('\u{10c8d}', 68813), - ('\u{10c8e}', 68814), ('\u{10c8f}', 68815), ('\u{10c90}', 68816), ('\u{10c91}', 68817), - ('\u{10c92}', 68818), ('\u{10c93}', 68819), ('\u{10c94}', 68820), ('\u{10c95}', 68821), - ('\u{10c96}', 68822), ('\u{10c97}', 68823), ('\u{10c98}', 68824), ('\u{10c99}', 68825), - ('\u{10c9a}', 68826), ('\u{10c9b}', 68827), ('\u{10c9c}', 68828), ('\u{10c9d}', 68829), - ('\u{10c9e}', 68830), ('\u{10c9f}', 68831), ('\u{10ca0}', 68832), ('\u{10ca1}', 68833), - ('\u{10ca2}', 68834), ('\u{10ca3}', 68835), ('\u{10ca4}', 68836), ('\u{10ca5}', 68837), - ('\u{10ca6}', 68838), ('\u{10ca7}', 68839), ('\u{10ca8}', 68840), ('\u{10ca9}', 68841), - ('\u{10caa}', 68842), ('\u{10cab}', 68843), ('\u{10cac}', 68844), ('\u{10cad}', 68845), - ('\u{10cae}', 68846), ('\u{10caf}', 68847), ('\u{10cb0}', 68848), ('\u{10cb1}', 68849), - ('\u{10cb2}', 68850), ('\u{10d50}', 68976), ('\u{10d51}', 68977), ('\u{10d52}', 68978), - ('\u{10d53}', 68979), ('\u{10d54}', 68980), ('\u{10d55}', 68981), ('\u{10d56}', 68982), - ('\u{10d57}', 68983), ('\u{10d58}', 68984), ('\u{10d59}', 68985), ('\u{10d5a}', 68986), - ('\u{10d5b}', 68987), ('\u{10d5c}', 68988), ('\u{10d5d}', 68989), ('\u{10d5e}', 68990), - ('\u{10d5f}', 68991), ('\u{10d60}', 68992), ('\u{10d61}', 68993), ('\u{10d62}', 68994), - ('\u{10d63}', 68995), ('\u{10d64}', 68996), ('\u{10d65}', 68997), ('\u{118a0}', 71872), - ('\u{118a1}', 71873), ('\u{118a2}', 71874), ('\u{118a3}', 71875), ('\u{118a4}', 71876), - ('\u{118a5}', 71877), ('\u{118a6}', 71878), ('\u{118a7}', 71879), ('\u{118a8}', 71880), - ('\u{118a9}', 71881), ('\u{118aa}', 71882), ('\u{118ab}', 71883), ('\u{118ac}', 71884), - ('\u{118ad}', 71885), ('\u{118ae}', 71886), ('\u{118af}', 71887), ('\u{118b0}', 71888), - ('\u{118b1}', 71889), ('\u{118b2}', 71890), ('\u{118b3}', 71891), ('\u{118b4}', 71892), - ('\u{118b5}', 71893), ('\u{118b6}', 71894), ('\u{118b7}', 71895), ('\u{118b8}', 71896), - ('\u{118b9}', 71897), ('\u{118ba}', 71898), ('\u{118bb}', 71899), ('\u{118bc}', 71900), - ('\u{118bd}', 71901), ('\u{118be}', 71902), ('\u{118bf}', 71903), ('\u{16e40}', 93792), - ('\u{16e41}', 93793), ('\u{16e42}', 93794), ('\u{16e43}', 93795), ('\u{16e44}', 93796), - ('\u{16e45}', 93797), ('\u{16e46}', 93798), ('\u{16e47}', 93799), ('\u{16e48}', 93800), - ('\u{16e49}', 93801), ('\u{16e4a}', 93802), ('\u{16e4b}', 93803), ('\u{16e4c}', 93804), - ('\u{16e4d}', 93805), ('\u{16e4e}', 93806), ('\u{16e4f}', 93807), ('\u{16e50}', 93808), - ('\u{16e51}', 93809), ('\u{16e52}', 93810), ('\u{16e53}', 93811), ('\u{16e54}', 93812), - ('\u{16e55}', 93813), ('\u{16e56}', 93814), ('\u{16e57}', 93815), ('\u{16e58}', 93816), - ('\u{16e59}', 93817), ('\u{16e5a}', 93818), ('\u{16e5b}', 93819), ('\u{16e5c}', 93820), - ('\u{16e5d}', 93821), ('\u{16e5e}', 93822), ('\u{16e5f}', 93823), ('\u{1e900}', 125218), + ('\u{a7c9}', 42954), ('\u{a7cb}', 612), ('\u{a7cc}', 42957), ('\u{a7ce}', 42959), + ('\u{a7d0}', 42961), ('\u{a7d2}', 42963), ('\u{a7d4}', 42965), ('\u{a7d6}', 42967), + ('\u{a7d8}', 42969), ('\u{a7da}', 42971), ('\u{a7dc}', 411), ('\u{a7f5}', 42998), + ('\u{ff21}', 65345), ('\u{ff22}', 65346), ('\u{ff23}', 65347), ('\u{ff24}', 65348), + ('\u{ff25}', 65349), ('\u{ff26}', 65350), ('\u{ff27}', 65351), ('\u{ff28}', 65352), + ('\u{ff29}', 65353), ('\u{ff2a}', 65354), ('\u{ff2b}', 65355), ('\u{ff2c}', 65356), + ('\u{ff2d}', 65357), ('\u{ff2e}', 65358), ('\u{ff2f}', 65359), ('\u{ff30}', 65360), + ('\u{ff31}', 65361), ('\u{ff32}', 65362), ('\u{ff33}', 65363), ('\u{ff34}', 65364), + ('\u{ff35}', 65365), ('\u{ff36}', 65366), ('\u{ff37}', 65367), ('\u{ff38}', 65368), + ('\u{ff39}', 65369), ('\u{ff3a}', 65370), ('\u{10400}', 66600), ('\u{10401}', 66601), + ('\u{10402}', 66602), ('\u{10403}', 66603), ('\u{10404}', 66604), ('\u{10405}', 66605), + ('\u{10406}', 66606), ('\u{10407}', 66607), ('\u{10408}', 66608), ('\u{10409}', 66609), + ('\u{1040a}', 66610), ('\u{1040b}', 66611), ('\u{1040c}', 66612), ('\u{1040d}', 66613), + ('\u{1040e}', 66614), ('\u{1040f}', 66615), ('\u{10410}', 66616), ('\u{10411}', 66617), + ('\u{10412}', 66618), ('\u{10413}', 66619), ('\u{10414}', 66620), ('\u{10415}', 66621), + ('\u{10416}', 66622), ('\u{10417}', 66623), ('\u{10418}', 66624), ('\u{10419}', 66625), + ('\u{1041a}', 66626), ('\u{1041b}', 66627), ('\u{1041c}', 66628), ('\u{1041d}', 66629), + ('\u{1041e}', 66630), ('\u{1041f}', 66631), ('\u{10420}', 66632), ('\u{10421}', 66633), + ('\u{10422}', 66634), ('\u{10423}', 66635), ('\u{10424}', 66636), ('\u{10425}', 66637), + ('\u{10426}', 66638), ('\u{10427}', 66639), ('\u{104b0}', 66776), ('\u{104b1}', 66777), + ('\u{104b2}', 66778), ('\u{104b3}', 66779), ('\u{104b4}', 66780), ('\u{104b5}', 66781), + ('\u{104b6}', 66782), ('\u{104b7}', 66783), ('\u{104b8}', 66784), ('\u{104b9}', 66785), + ('\u{104ba}', 66786), ('\u{104bb}', 66787), ('\u{104bc}', 66788), ('\u{104bd}', 66789), + ('\u{104be}', 66790), ('\u{104bf}', 66791), ('\u{104c0}', 66792), ('\u{104c1}', 66793), + ('\u{104c2}', 66794), ('\u{104c3}', 66795), ('\u{104c4}', 66796), ('\u{104c5}', 66797), + ('\u{104c6}', 66798), ('\u{104c7}', 66799), ('\u{104c8}', 66800), ('\u{104c9}', 66801), + ('\u{104ca}', 66802), ('\u{104cb}', 66803), ('\u{104cc}', 66804), ('\u{104cd}', 66805), + ('\u{104ce}', 66806), ('\u{104cf}', 66807), ('\u{104d0}', 66808), ('\u{104d1}', 66809), + ('\u{104d2}', 66810), ('\u{104d3}', 66811), ('\u{10570}', 66967), ('\u{10571}', 66968), + ('\u{10572}', 66969), ('\u{10573}', 66970), ('\u{10574}', 66971), ('\u{10575}', 66972), + ('\u{10576}', 66973), ('\u{10577}', 66974), ('\u{10578}', 66975), ('\u{10579}', 66976), + ('\u{1057a}', 66977), ('\u{1057c}', 66979), ('\u{1057d}', 66980), ('\u{1057e}', 66981), + ('\u{1057f}', 66982), ('\u{10580}', 66983), ('\u{10581}', 66984), ('\u{10582}', 66985), + ('\u{10583}', 66986), ('\u{10584}', 66987), ('\u{10585}', 66988), ('\u{10586}', 66989), + ('\u{10587}', 66990), ('\u{10588}', 66991), ('\u{10589}', 66992), ('\u{1058a}', 66993), + ('\u{1058c}', 66995), ('\u{1058d}', 66996), ('\u{1058e}', 66997), ('\u{1058f}', 66998), + ('\u{10590}', 66999), ('\u{10591}', 67000), ('\u{10592}', 67001), ('\u{10594}', 67003), + ('\u{10595}', 67004), ('\u{10c80}', 68800), ('\u{10c81}', 68801), ('\u{10c82}', 68802), + ('\u{10c83}', 68803), ('\u{10c84}', 68804), ('\u{10c85}', 68805), ('\u{10c86}', 68806), + ('\u{10c87}', 68807), ('\u{10c88}', 68808), ('\u{10c89}', 68809), ('\u{10c8a}', 68810), + ('\u{10c8b}', 68811), ('\u{10c8c}', 68812), ('\u{10c8d}', 68813), ('\u{10c8e}', 68814), + ('\u{10c8f}', 68815), ('\u{10c90}', 68816), ('\u{10c91}', 68817), ('\u{10c92}', 68818), + ('\u{10c93}', 68819), ('\u{10c94}', 68820), ('\u{10c95}', 68821), ('\u{10c96}', 68822), + ('\u{10c97}', 68823), ('\u{10c98}', 68824), ('\u{10c99}', 68825), ('\u{10c9a}', 68826), + ('\u{10c9b}', 68827), ('\u{10c9c}', 68828), ('\u{10c9d}', 68829), ('\u{10c9e}', 68830), + ('\u{10c9f}', 68831), ('\u{10ca0}', 68832), ('\u{10ca1}', 68833), ('\u{10ca2}', 68834), + ('\u{10ca3}', 68835), ('\u{10ca4}', 68836), ('\u{10ca5}', 68837), ('\u{10ca6}', 68838), + ('\u{10ca7}', 68839), ('\u{10ca8}', 68840), ('\u{10ca9}', 68841), ('\u{10caa}', 68842), + ('\u{10cab}', 68843), ('\u{10cac}', 68844), ('\u{10cad}', 68845), ('\u{10cae}', 68846), + ('\u{10caf}', 68847), ('\u{10cb0}', 68848), ('\u{10cb1}', 68849), ('\u{10cb2}', 68850), + ('\u{10d50}', 68976), ('\u{10d51}', 68977), ('\u{10d52}', 68978), ('\u{10d53}', 68979), + ('\u{10d54}', 68980), ('\u{10d55}', 68981), ('\u{10d56}', 68982), ('\u{10d57}', 68983), + ('\u{10d58}', 68984), ('\u{10d59}', 68985), ('\u{10d5a}', 68986), ('\u{10d5b}', 68987), + ('\u{10d5c}', 68988), ('\u{10d5d}', 68989), ('\u{10d5e}', 68990), ('\u{10d5f}', 68991), + ('\u{10d60}', 68992), ('\u{10d61}', 68993), ('\u{10d62}', 68994), ('\u{10d63}', 68995), + ('\u{10d64}', 68996), ('\u{10d65}', 68997), ('\u{118a0}', 71872), ('\u{118a1}', 71873), + ('\u{118a2}', 71874), ('\u{118a3}', 71875), ('\u{118a4}', 71876), ('\u{118a5}', 71877), + ('\u{118a6}', 71878), ('\u{118a7}', 71879), ('\u{118a8}', 71880), ('\u{118a9}', 71881), + ('\u{118aa}', 71882), ('\u{118ab}', 71883), ('\u{118ac}', 71884), ('\u{118ad}', 71885), + ('\u{118ae}', 71886), ('\u{118af}', 71887), ('\u{118b0}', 71888), ('\u{118b1}', 71889), + ('\u{118b2}', 71890), ('\u{118b3}', 71891), ('\u{118b4}', 71892), ('\u{118b5}', 71893), + ('\u{118b6}', 71894), ('\u{118b7}', 71895), ('\u{118b8}', 71896), ('\u{118b9}', 71897), + ('\u{118ba}', 71898), ('\u{118bb}', 71899), ('\u{118bc}', 71900), ('\u{118bd}', 71901), + ('\u{118be}', 71902), ('\u{118bf}', 71903), ('\u{16e40}', 93792), ('\u{16e41}', 93793), + ('\u{16e42}', 93794), ('\u{16e43}', 93795), ('\u{16e44}', 93796), ('\u{16e45}', 93797), + ('\u{16e46}', 93798), ('\u{16e47}', 93799), ('\u{16e48}', 93800), ('\u{16e49}', 93801), + ('\u{16e4a}', 93802), ('\u{16e4b}', 93803), ('\u{16e4c}', 93804), ('\u{16e4d}', 93805), + ('\u{16e4e}', 93806), ('\u{16e4f}', 93807), ('\u{16e50}', 93808), ('\u{16e51}', 93809), + ('\u{16e52}', 93810), ('\u{16e53}', 93811), ('\u{16e54}', 93812), ('\u{16e55}', 93813), + ('\u{16e56}', 93814), ('\u{16e57}', 93815), ('\u{16e58}', 93816), ('\u{16e59}', 93817), + ('\u{16e5a}', 93818), ('\u{16e5b}', 93819), ('\u{16e5c}', 93820), ('\u{16e5d}', 93821), + ('\u{16e5e}', 93822), ('\u{16e5f}', 93823), ('\u{16ea0}', 93883), ('\u{16ea1}', 93884), + ('\u{16ea2}', 93885), ('\u{16ea3}', 93886), ('\u{16ea4}', 93887), ('\u{16ea5}', 93888), + ('\u{16ea6}', 93889), ('\u{16ea7}', 93890), ('\u{16ea8}', 93891), ('\u{16ea9}', 93892), + ('\u{16eaa}', 93893), ('\u{16eab}', 93894), ('\u{16eac}', 93895), ('\u{16ead}', 93896), + ('\u{16eae}', 93897), ('\u{16eaf}', 93898), ('\u{16eb0}', 93899), ('\u{16eb1}', 93900), + ('\u{16eb2}', 93901), ('\u{16eb3}', 93902), ('\u{16eb4}', 93903), ('\u{16eb5}', 93904), + ('\u{16eb6}', 93905), ('\u{16eb7}', 93906), ('\u{16eb8}', 93907), ('\u{1e900}', 125218), ('\u{1e901}', 125219), ('\u{1e902}', 125220), ('\u{1e903}', 125221), ('\u{1e904}', 125222), ('\u{1e905}', 125223), ('\u{1e906}', 125224), ('\u{1e907}', 125225), ('\u{1e908}', 125226), ('\u{1e909}', 125227), ('\u{1e90a}', 125228), ('\u{1e90b}', 125229), ('\u{1e90c}', 125230), @@ -1146,7 +1155,7 @@ pub mod conversions { ['i', '\u{307}', '\u{0}'], ]; - static UPPERCASE_TABLE: &[(char, u32); 1526] = &[ + static UPPERCASE_TABLE: &[(char, u32); 1554] = &[ ('\u{b5}', 924), ('\u{df}', 4194304), ('\u{e0}', 192), ('\u{e1}', 193), ('\u{e2}', 194), ('\u{e3}', 195), ('\u{e4}', 196), ('\u{e5}', 197), ('\u{e6}', 198), ('\u{e7}', 199), ('\u{e8}', 200), ('\u{e9}', 201), ('\u{ea}', 202), ('\u{eb}', 203), ('\u{ec}', 204), @@ -1415,100 +1424,107 @@ pub mod conversions { ('\u{a7a7}', 42918), ('\u{a7a9}', 42920), ('\u{a7b5}', 42932), ('\u{a7b7}', 42934), ('\u{a7b9}', 42936), ('\u{a7bb}', 42938), ('\u{a7bd}', 42940), ('\u{a7bf}', 42942), ('\u{a7c1}', 42944), ('\u{a7c3}', 42946), ('\u{a7c8}', 42951), ('\u{a7ca}', 42953), - ('\u{a7cd}', 42956), ('\u{a7d1}', 42960), ('\u{a7d7}', 42966), ('\u{a7d9}', 42968), - ('\u{a7db}', 42970), ('\u{a7f6}', 42997), ('\u{ab53}', 42931), ('\u{ab70}', 5024), - ('\u{ab71}', 5025), ('\u{ab72}', 5026), ('\u{ab73}', 5027), ('\u{ab74}', 5028), - ('\u{ab75}', 5029), ('\u{ab76}', 5030), ('\u{ab77}', 5031), ('\u{ab78}', 5032), - ('\u{ab79}', 5033), ('\u{ab7a}', 5034), ('\u{ab7b}', 5035), ('\u{ab7c}', 5036), - ('\u{ab7d}', 5037), ('\u{ab7e}', 5038), ('\u{ab7f}', 5039), ('\u{ab80}', 5040), - ('\u{ab81}', 5041), ('\u{ab82}', 5042), ('\u{ab83}', 5043), ('\u{ab84}', 5044), - ('\u{ab85}', 5045), ('\u{ab86}', 5046), ('\u{ab87}', 5047), ('\u{ab88}', 5048), - ('\u{ab89}', 5049), ('\u{ab8a}', 5050), ('\u{ab8b}', 5051), ('\u{ab8c}', 5052), - ('\u{ab8d}', 5053), ('\u{ab8e}', 5054), ('\u{ab8f}', 5055), ('\u{ab90}', 5056), - ('\u{ab91}', 5057), ('\u{ab92}', 5058), ('\u{ab93}', 5059), ('\u{ab94}', 5060), - ('\u{ab95}', 5061), ('\u{ab96}', 5062), ('\u{ab97}', 5063), ('\u{ab98}', 5064), - ('\u{ab99}', 5065), ('\u{ab9a}', 5066), ('\u{ab9b}', 5067), ('\u{ab9c}', 5068), - ('\u{ab9d}', 5069), ('\u{ab9e}', 5070), ('\u{ab9f}', 5071), ('\u{aba0}', 5072), - ('\u{aba1}', 5073), ('\u{aba2}', 5074), ('\u{aba3}', 5075), ('\u{aba4}', 5076), - ('\u{aba5}', 5077), ('\u{aba6}', 5078), ('\u{aba7}', 5079), ('\u{aba8}', 5080), - ('\u{aba9}', 5081), ('\u{abaa}', 5082), ('\u{abab}', 5083), ('\u{abac}', 5084), - ('\u{abad}', 5085), ('\u{abae}', 5086), ('\u{abaf}', 5087), ('\u{abb0}', 5088), - ('\u{abb1}', 5089), ('\u{abb2}', 5090), ('\u{abb3}', 5091), ('\u{abb4}', 5092), - ('\u{abb5}', 5093), ('\u{abb6}', 5094), ('\u{abb7}', 5095), ('\u{abb8}', 5096), - ('\u{abb9}', 5097), ('\u{abba}', 5098), ('\u{abbb}', 5099), ('\u{abbc}', 5100), - ('\u{abbd}', 5101), ('\u{abbe}', 5102), ('\u{abbf}', 5103), ('\u{fb00}', 4194394), - ('\u{fb01}', 4194395), ('\u{fb02}', 4194396), ('\u{fb03}', 4194397), ('\u{fb04}', 4194398), - ('\u{fb05}', 4194399), ('\u{fb06}', 4194400), ('\u{fb13}', 4194401), ('\u{fb14}', 4194402), - ('\u{fb15}', 4194403), ('\u{fb16}', 4194404), ('\u{fb17}', 4194405), ('\u{ff41}', 65313), - ('\u{ff42}', 65314), ('\u{ff43}', 65315), ('\u{ff44}', 65316), ('\u{ff45}', 65317), - ('\u{ff46}', 65318), ('\u{ff47}', 65319), ('\u{ff48}', 65320), ('\u{ff49}', 65321), - ('\u{ff4a}', 65322), ('\u{ff4b}', 65323), ('\u{ff4c}', 65324), ('\u{ff4d}', 65325), - ('\u{ff4e}', 65326), ('\u{ff4f}', 65327), ('\u{ff50}', 65328), ('\u{ff51}', 65329), - ('\u{ff52}', 65330), ('\u{ff53}', 65331), ('\u{ff54}', 65332), ('\u{ff55}', 65333), - ('\u{ff56}', 65334), ('\u{ff57}', 65335), ('\u{ff58}', 65336), ('\u{ff59}', 65337), - ('\u{ff5a}', 65338), ('\u{10428}', 66560), ('\u{10429}', 66561), ('\u{1042a}', 66562), - ('\u{1042b}', 66563), ('\u{1042c}', 66564), ('\u{1042d}', 66565), ('\u{1042e}', 66566), - ('\u{1042f}', 66567), ('\u{10430}', 66568), ('\u{10431}', 66569), ('\u{10432}', 66570), - ('\u{10433}', 66571), ('\u{10434}', 66572), ('\u{10435}', 66573), ('\u{10436}', 66574), - ('\u{10437}', 66575), ('\u{10438}', 66576), ('\u{10439}', 66577), ('\u{1043a}', 66578), - ('\u{1043b}', 66579), ('\u{1043c}', 66580), ('\u{1043d}', 66581), ('\u{1043e}', 66582), - ('\u{1043f}', 66583), ('\u{10440}', 66584), ('\u{10441}', 66585), ('\u{10442}', 66586), - ('\u{10443}', 66587), ('\u{10444}', 66588), ('\u{10445}', 66589), ('\u{10446}', 66590), - ('\u{10447}', 66591), ('\u{10448}', 66592), ('\u{10449}', 66593), ('\u{1044a}', 66594), - ('\u{1044b}', 66595), ('\u{1044c}', 66596), ('\u{1044d}', 66597), ('\u{1044e}', 66598), - ('\u{1044f}', 66599), ('\u{104d8}', 66736), ('\u{104d9}', 66737), ('\u{104da}', 66738), - ('\u{104db}', 66739), ('\u{104dc}', 66740), ('\u{104dd}', 66741), ('\u{104de}', 66742), - ('\u{104df}', 66743), ('\u{104e0}', 66744), ('\u{104e1}', 66745), ('\u{104e2}', 66746), - ('\u{104e3}', 66747), ('\u{104e4}', 66748), ('\u{104e5}', 66749), ('\u{104e6}', 66750), - ('\u{104e7}', 66751), ('\u{104e8}', 66752), ('\u{104e9}', 66753), ('\u{104ea}', 66754), - ('\u{104eb}', 66755), ('\u{104ec}', 66756), ('\u{104ed}', 66757), ('\u{104ee}', 66758), - ('\u{104ef}', 66759), ('\u{104f0}', 66760), ('\u{104f1}', 66761), ('\u{104f2}', 66762), - ('\u{104f3}', 66763), ('\u{104f4}', 66764), ('\u{104f5}', 66765), ('\u{104f6}', 66766), - ('\u{104f7}', 66767), ('\u{104f8}', 66768), ('\u{104f9}', 66769), ('\u{104fa}', 66770), - ('\u{104fb}', 66771), ('\u{10597}', 66928), ('\u{10598}', 66929), ('\u{10599}', 66930), - ('\u{1059a}', 66931), ('\u{1059b}', 66932), ('\u{1059c}', 66933), ('\u{1059d}', 66934), - ('\u{1059e}', 66935), ('\u{1059f}', 66936), ('\u{105a0}', 66937), ('\u{105a1}', 66938), - ('\u{105a3}', 66940), ('\u{105a4}', 66941), ('\u{105a5}', 66942), ('\u{105a6}', 66943), - ('\u{105a7}', 66944), ('\u{105a8}', 66945), ('\u{105a9}', 66946), ('\u{105aa}', 66947), - ('\u{105ab}', 66948), ('\u{105ac}', 66949), ('\u{105ad}', 66950), ('\u{105ae}', 66951), - ('\u{105af}', 66952), ('\u{105b0}', 66953), ('\u{105b1}', 66954), ('\u{105b3}', 66956), - ('\u{105b4}', 66957), ('\u{105b5}', 66958), ('\u{105b6}', 66959), ('\u{105b7}', 66960), - ('\u{105b8}', 66961), ('\u{105b9}', 66962), ('\u{105bb}', 66964), ('\u{105bc}', 66965), - ('\u{10cc0}', 68736), ('\u{10cc1}', 68737), ('\u{10cc2}', 68738), ('\u{10cc3}', 68739), - ('\u{10cc4}', 68740), ('\u{10cc5}', 68741), ('\u{10cc6}', 68742), ('\u{10cc7}', 68743), - ('\u{10cc8}', 68744), ('\u{10cc9}', 68745), ('\u{10cca}', 68746), ('\u{10ccb}', 68747), - ('\u{10ccc}', 68748), ('\u{10ccd}', 68749), ('\u{10cce}', 68750), ('\u{10ccf}', 68751), - ('\u{10cd0}', 68752), ('\u{10cd1}', 68753), ('\u{10cd2}', 68754), ('\u{10cd3}', 68755), - ('\u{10cd4}', 68756), ('\u{10cd5}', 68757), ('\u{10cd6}', 68758), ('\u{10cd7}', 68759), - ('\u{10cd8}', 68760), ('\u{10cd9}', 68761), ('\u{10cda}', 68762), ('\u{10cdb}', 68763), - ('\u{10cdc}', 68764), ('\u{10cdd}', 68765), ('\u{10cde}', 68766), ('\u{10cdf}', 68767), - ('\u{10ce0}', 68768), ('\u{10ce1}', 68769), ('\u{10ce2}', 68770), ('\u{10ce3}', 68771), - ('\u{10ce4}', 68772), ('\u{10ce5}', 68773), ('\u{10ce6}', 68774), ('\u{10ce7}', 68775), - ('\u{10ce8}', 68776), ('\u{10ce9}', 68777), ('\u{10cea}', 68778), ('\u{10ceb}', 68779), - ('\u{10cec}', 68780), ('\u{10ced}', 68781), ('\u{10cee}', 68782), ('\u{10cef}', 68783), - ('\u{10cf0}', 68784), ('\u{10cf1}', 68785), ('\u{10cf2}', 68786), ('\u{10d70}', 68944), - ('\u{10d71}', 68945), ('\u{10d72}', 68946), ('\u{10d73}', 68947), ('\u{10d74}', 68948), - ('\u{10d75}', 68949), ('\u{10d76}', 68950), ('\u{10d77}', 68951), ('\u{10d78}', 68952), - ('\u{10d79}', 68953), ('\u{10d7a}', 68954), ('\u{10d7b}', 68955), ('\u{10d7c}', 68956), - ('\u{10d7d}', 68957), ('\u{10d7e}', 68958), ('\u{10d7f}', 68959), ('\u{10d80}', 68960), - ('\u{10d81}', 68961), ('\u{10d82}', 68962), ('\u{10d83}', 68963), ('\u{10d84}', 68964), - ('\u{10d85}', 68965), ('\u{118c0}', 71840), ('\u{118c1}', 71841), ('\u{118c2}', 71842), - ('\u{118c3}', 71843), ('\u{118c4}', 71844), ('\u{118c5}', 71845), ('\u{118c6}', 71846), - ('\u{118c7}', 71847), ('\u{118c8}', 71848), ('\u{118c9}', 71849), ('\u{118ca}', 71850), - ('\u{118cb}', 71851), ('\u{118cc}', 71852), ('\u{118cd}', 71853), ('\u{118ce}', 71854), - ('\u{118cf}', 71855), ('\u{118d0}', 71856), ('\u{118d1}', 71857), ('\u{118d2}', 71858), - ('\u{118d3}', 71859), ('\u{118d4}', 71860), ('\u{118d5}', 71861), ('\u{118d6}', 71862), - ('\u{118d7}', 71863), ('\u{118d8}', 71864), ('\u{118d9}', 71865), ('\u{118da}', 71866), - ('\u{118db}', 71867), ('\u{118dc}', 71868), ('\u{118dd}', 71869), ('\u{118de}', 71870), - ('\u{118df}', 71871), ('\u{16e60}', 93760), ('\u{16e61}', 93761), ('\u{16e62}', 93762), - ('\u{16e63}', 93763), ('\u{16e64}', 93764), ('\u{16e65}', 93765), ('\u{16e66}', 93766), - ('\u{16e67}', 93767), ('\u{16e68}', 93768), ('\u{16e69}', 93769), ('\u{16e6a}', 93770), - ('\u{16e6b}', 93771), ('\u{16e6c}', 93772), ('\u{16e6d}', 93773), ('\u{16e6e}', 93774), - ('\u{16e6f}', 93775), ('\u{16e70}', 93776), ('\u{16e71}', 93777), ('\u{16e72}', 93778), - ('\u{16e73}', 93779), ('\u{16e74}', 93780), ('\u{16e75}', 93781), ('\u{16e76}', 93782), - ('\u{16e77}', 93783), ('\u{16e78}', 93784), ('\u{16e79}', 93785), ('\u{16e7a}', 93786), - ('\u{16e7b}', 93787), ('\u{16e7c}', 93788), ('\u{16e7d}', 93789), ('\u{16e7e}', 93790), - ('\u{16e7f}', 93791), ('\u{1e922}', 125184), ('\u{1e923}', 125185), ('\u{1e924}', 125186), + ('\u{a7cd}', 42956), ('\u{a7cf}', 42958), ('\u{a7d1}', 42960), ('\u{a7d3}', 42962), + ('\u{a7d5}', 42964), ('\u{a7d7}', 42966), ('\u{a7d9}', 42968), ('\u{a7db}', 42970), + ('\u{a7f6}', 42997), ('\u{ab53}', 42931), ('\u{ab70}', 5024), ('\u{ab71}', 5025), + ('\u{ab72}', 5026), ('\u{ab73}', 5027), ('\u{ab74}', 5028), ('\u{ab75}', 5029), + ('\u{ab76}', 5030), ('\u{ab77}', 5031), ('\u{ab78}', 5032), ('\u{ab79}', 5033), + ('\u{ab7a}', 5034), ('\u{ab7b}', 5035), ('\u{ab7c}', 5036), ('\u{ab7d}', 5037), + ('\u{ab7e}', 5038), ('\u{ab7f}', 5039), ('\u{ab80}', 5040), ('\u{ab81}', 5041), + ('\u{ab82}', 5042), ('\u{ab83}', 5043), ('\u{ab84}', 5044), ('\u{ab85}', 5045), + ('\u{ab86}', 5046), ('\u{ab87}', 5047), ('\u{ab88}', 5048), ('\u{ab89}', 5049), + ('\u{ab8a}', 5050), ('\u{ab8b}', 5051), ('\u{ab8c}', 5052), ('\u{ab8d}', 5053), + ('\u{ab8e}', 5054), ('\u{ab8f}', 5055), ('\u{ab90}', 5056), ('\u{ab91}', 5057), + ('\u{ab92}', 5058), ('\u{ab93}', 5059), ('\u{ab94}', 5060), ('\u{ab95}', 5061), + ('\u{ab96}', 5062), ('\u{ab97}', 5063), ('\u{ab98}', 5064), ('\u{ab99}', 5065), + ('\u{ab9a}', 5066), ('\u{ab9b}', 5067), ('\u{ab9c}', 5068), ('\u{ab9d}', 5069), + ('\u{ab9e}', 5070), ('\u{ab9f}', 5071), ('\u{aba0}', 5072), ('\u{aba1}', 5073), + ('\u{aba2}', 5074), ('\u{aba3}', 5075), ('\u{aba4}', 5076), ('\u{aba5}', 5077), + ('\u{aba6}', 5078), ('\u{aba7}', 5079), ('\u{aba8}', 5080), ('\u{aba9}', 5081), + ('\u{abaa}', 5082), ('\u{abab}', 5083), ('\u{abac}', 5084), ('\u{abad}', 5085), + ('\u{abae}', 5086), ('\u{abaf}', 5087), ('\u{abb0}', 5088), ('\u{abb1}', 5089), + ('\u{abb2}', 5090), ('\u{abb3}', 5091), ('\u{abb4}', 5092), ('\u{abb5}', 5093), + ('\u{abb6}', 5094), ('\u{abb7}', 5095), ('\u{abb8}', 5096), ('\u{abb9}', 5097), + ('\u{abba}', 5098), ('\u{abbb}', 5099), ('\u{abbc}', 5100), ('\u{abbd}', 5101), + ('\u{abbe}', 5102), ('\u{abbf}', 5103), ('\u{fb00}', 4194394), ('\u{fb01}', 4194395), + ('\u{fb02}', 4194396), ('\u{fb03}', 4194397), ('\u{fb04}', 4194398), ('\u{fb05}', 4194399), + ('\u{fb06}', 4194400), ('\u{fb13}', 4194401), ('\u{fb14}', 4194402), ('\u{fb15}', 4194403), + ('\u{fb16}', 4194404), ('\u{fb17}', 4194405), ('\u{ff41}', 65313), ('\u{ff42}', 65314), + ('\u{ff43}', 65315), ('\u{ff44}', 65316), ('\u{ff45}', 65317), ('\u{ff46}', 65318), + ('\u{ff47}', 65319), ('\u{ff48}', 65320), ('\u{ff49}', 65321), ('\u{ff4a}', 65322), + ('\u{ff4b}', 65323), ('\u{ff4c}', 65324), ('\u{ff4d}', 65325), ('\u{ff4e}', 65326), + ('\u{ff4f}', 65327), ('\u{ff50}', 65328), ('\u{ff51}', 65329), ('\u{ff52}', 65330), + ('\u{ff53}', 65331), ('\u{ff54}', 65332), ('\u{ff55}', 65333), ('\u{ff56}', 65334), + ('\u{ff57}', 65335), ('\u{ff58}', 65336), ('\u{ff59}', 65337), ('\u{ff5a}', 65338), + ('\u{10428}', 66560), ('\u{10429}', 66561), ('\u{1042a}', 66562), ('\u{1042b}', 66563), + ('\u{1042c}', 66564), ('\u{1042d}', 66565), ('\u{1042e}', 66566), ('\u{1042f}', 66567), + ('\u{10430}', 66568), ('\u{10431}', 66569), ('\u{10432}', 66570), ('\u{10433}', 66571), + ('\u{10434}', 66572), ('\u{10435}', 66573), ('\u{10436}', 66574), ('\u{10437}', 66575), + ('\u{10438}', 66576), ('\u{10439}', 66577), ('\u{1043a}', 66578), ('\u{1043b}', 66579), + ('\u{1043c}', 66580), ('\u{1043d}', 66581), ('\u{1043e}', 66582), ('\u{1043f}', 66583), + ('\u{10440}', 66584), ('\u{10441}', 66585), ('\u{10442}', 66586), ('\u{10443}', 66587), + ('\u{10444}', 66588), ('\u{10445}', 66589), ('\u{10446}', 66590), ('\u{10447}', 66591), + ('\u{10448}', 66592), ('\u{10449}', 66593), ('\u{1044a}', 66594), ('\u{1044b}', 66595), + ('\u{1044c}', 66596), ('\u{1044d}', 66597), ('\u{1044e}', 66598), ('\u{1044f}', 66599), + ('\u{104d8}', 66736), ('\u{104d9}', 66737), ('\u{104da}', 66738), ('\u{104db}', 66739), + ('\u{104dc}', 66740), ('\u{104dd}', 66741), ('\u{104de}', 66742), ('\u{104df}', 66743), + ('\u{104e0}', 66744), ('\u{104e1}', 66745), ('\u{104e2}', 66746), ('\u{104e3}', 66747), + ('\u{104e4}', 66748), ('\u{104e5}', 66749), ('\u{104e6}', 66750), ('\u{104e7}', 66751), + ('\u{104e8}', 66752), ('\u{104e9}', 66753), ('\u{104ea}', 66754), ('\u{104eb}', 66755), + ('\u{104ec}', 66756), ('\u{104ed}', 66757), ('\u{104ee}', 66758), ('\u{104ef}', 66759), + ('\u{104f0}', 66760), ('\u{104f1}', 66761), ('\u{104f2}', 66762), ('\u{104f3}', 66763), + ('\u{104f4}', 66764), ('\u{104f5}', 66765), ('\u{104f6}', 66766), ('\u{104f7}', 66767), + ('\u{104f8}', 66768), ('\u{104f9}', 66769), ('\u{104fa}', 66770), ('\u{104fb}', 66771), + ('\u{10597}', 66928), ('\u{10598}', 66929), ('\u{10599}', 66930), ('\u{1059a}', 66931), + ('\u{1059b}', 66932), ('\u{1059c}', 66933), ('\u{1059d}', 66934), ('\u{1059e}', 66935), + ('\u{1059f}', 66936), ('\u{105a0}', 66937), ('\u{105a1}', 66938), ('\u{105a3}', 66940), + ('\u{105a4}', 66941), ('\u{105a5}', 66942), ('\u{105a6}', 66943), ('\u{105a7}', 66944), + ('\u{105a8}', 66945), ('\u{105a9}', 66946), ('\u{105aa}', 66947), ('\u{105ab}', 66948), + ('\u{105ac}', 66949), ('\u{105ad}', 66950), ('\u{105ae}', 66951), ('\u{105af}', 66952), + ('\u{105b0}', 66953), ('\u{105b1}', 66954), ('\u{105b3}', 66956), ('\u{105b4}', 66957), + ('\u{105b5}', 66958), ('\u{105b6}', 66959), ('\u{105b7}', 66960), ('\u{105b8}', 66961), + ('\u{105b9}', 66962), ('\u{105bb}', 66964), ('\u{105bc}', 66965), ('\u{10cc0}', 68736), + ('\u{10cc1}', 68737), ('\u{10cc2}', 68738), ('\u{10cc3}', 68739), ('\u{10cc4}', 68740), + ('\u{10cc5}', 68741), ('\u{10cc6}', 68742), ('\u{10cc7}', 68743), ('\u{10cc8}', 68744), + ('\u{10cc9}', 68745), ('\u{10cca}', 68746), ('\u{10ccb}', 68747), ('\u{10ccc}', 68748), + ('\u{10ccd}', 68749), ('\u{10cce}', 68750), ('\u{10ccf}', 68751), ('\u{10cd0}', 68752), + ('\u{10cd1}', 68753), ('\u{10cd2}', 68754), ('\u{10cd3}', 68755), ('\u{10cd4}', 68756), + ('\u{10cd5}', 68757), ('\u{10cd6}', 68758), ('\u{10cd7}', 68759), ('\u{10cd8}', 68760), + ('\u{10cd9}', 68761), ('\u{10cda}', 68762), ('\u{10cdb}', 68763), ('\u{10cdc}', 68764), + ('\u{10cdd}', 68765), ('\u{10cde}', 68766), ('\u{10cdf}', 68767), ('\u{10ce0}', 68768), + ('\u{10ce1}', 68769), ('\u{10ce2}', 68770), ('\u{10ce3}', 68771), ('\u{10ce4}', 68772), + ('\u{10ce5}', 68773), ('\u{10ce6}', 68774), ('\u{10ce7}', 68775), ('\u{10ce8}', 68776), + ('\u{10ce9}', 68777), ('\u{10cea}', 68778), ('\u{10ceb}', 68779), ('\u{10cec}', 68780), + ('\u{10ced}', 68781), ('\u{10cee}', 68782), ('\u{10cef}', 68783), ('\u{10cf0}', 68784), + ('\u{10cf1}', 68785), ('\u{10cf2}', 68786), ('\u{10d70}', 68944), ('\u{10d71}', 68945), + ('\u{10d72}', 68946), ('\u{10d73}', 68947), ('\u{10d74}', 68948), ('\u{10d75}', 68949), + ('\u{10d76}', 68950), ('\u{10d77}', 68951), ('\u{10d78}', 68952), ('\u{10d79}', 68953), + ('\u{10d7a}', 68954), ('\u{10d7b}', 68955), ('\u{10d7c}', 68956), ('\u{10d7d}', 68957), + ('\u{10d7e}', 68958), ('\u{10d7f}', 68959), ('\u{10d80}', 68960), ('\u{10d81}', 68961), + ('\u{10d82}', 68962), ('\u{10d83}', 68963), ('\u{10d84}', 68964), ('\u{10d85}', 68965), + ('\u{118c0}', 71840), ('\u{118c1}', 71841), ('\u{118c2}', 71842), ('\u{118c3}', 71843), + ('\u{118c4}', 71844), ('\u{118c5}', 71845), ('\u{118c6}', 71846), ('\u{118c7}', 71847), + ('\u{118c8}', 71848), ('\u{118c9}', 71849), ('\u{118ca}', 71850), ('\u{118cb}', 71851), + ('\u{118cc}', 71852), ('\u{118cd}', 71853), ('\u{118ce}', 71854), ('\u{118cf}', 71855), + ('\u{118d0}', 71856), ('\u{118d1}', 71857), ('\u{118d2}', 71858), ('\u{118d3}', 71859), + ('\u{118d4}', 71860), ('\u{118d5}', 71861), ('\u{118d6}', 71862), ('\u{118d7}', 71863), + ('\u{118d8}', 71864), ('\u{118d9}', 71865), ('\u{118da}', 71866), ('\u{118db}', 71867), + ('\u{118dc}', 71868), ('\u{118dd}', 71869), ('\u{118de}', 71870), ('\u{118df}', 71871), + ('\u{16e60}', 93760), ('\u{16e61}', 93761), ('\u{16e62}', 93762), ('\u{16e63}', 93763), + ('\u{16e64}', 93764), ('\u{16e65}', 93765), ('\u{16e66}', 93766), ('\u{16e67}', 93767), + ('\u{16e68}', 93768), ('\u{16e69}', 93769), ('\u{16e6a}', 93770), ('\u{16e6b}', 93771), + ('\u{16e6c}', 93772), ('\u{16e6d}', 93773), ('\u{16e6e}', 93774), ('\u{16e6f}', 93775), + ('\u{16e70}', 93776), ('\u{16e71}', 93777), ('\u{16e72}', 93778), ('\u{16e73}', 93779), + ('\u{16e74}', 93780), ('\u{16e75}', 93781), ('\u{16e76}', 93782), ('\u{16e77}', 93783), + ('\u{16e78}', 93784), ('\u{16e79}', 93785), ('\u{16e7a}', 93786), ('\u{16e7b}', 93787), + ('\u{16e7c}', 93788), ('\u{16e7d}', 93789), ('\u{16e7e}', 93790), ('\u{16e7f}', 93791), + ('\u{16ebb}', 93856), ('\u{16ebc}', 93857), ('\u{16ebd}', 93858), ('\u{16ebe}', 93859), + ('\u{16ebf}', 93860), ('\u{16ec0}', 93861), ('\u{16ec1}', 93862), ('\u{16ec2}', 93863), + ('\u{16ec3}', 93864), ('\u{16ec4}', 93865), ('\u{16ec5}', 93866), ('\u{16ec6}', 93867), + ('\u{16ec7}', 93868), ('\u{16ec8}', 93869), ('\u{16ec9}', 93870), ('\u{16eca}', 93871), + ('\u{16ecb}', 93872), ('\u{16ecc}', 93873), ('\u{16ecd}', 93874), ('\u{16ece}', 93875), + ('\u{16ecf}', 93876), ('\u{16ed0}', 93877), ('\u{16ed1}', 93878), ('\u{16ed2}', 93879), + ('\u{16ed3}', 93880), ('\u{1e922}', 125184), ('\u{1e923}', 125185), ('\u{1e924}', 125186), ('\u{1e925}', 125187), ('\u{1e926}', 125188), ('\u{1e927}', 125189), ('\u{1e928}', 125190), ('\u{1e929}', 125191), ('\u{1e92a}', 125192), ('\u{1e92b}', 125193), ('\u{1e92c}', 125194), ('\u{1e92d}', 125195), ('\u{1e92e}', 125196), ('\u{1e92f}', 125197), ('\u{1e930}', 125198), From a1a1ed25f2749de58c24a618460cb3d4279b1d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 9 Sep 2025 09:12:09 +0200 Subject: [PATCH 0272/1900] Add a FAQ entry about RA and Cargo interaction --- src/tools/rust-analyzer/docs/book/src/faq.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md index c87203309011b..8c143ab949357 100644 --- a/src/tools/rust-analyzer/docs/book/src/faq.md +++ b/src/tools/rust-analyzer/docs/book/src/faq.md @@ -5,3 +5,12 @@ rust-analyzer fails to resolve `None`, and thinks you are binding to a variable named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing it: `rustup component remove rust-src` then `rustup component install rust-src`. + +### Rust Analyzer and Cargo compete over the build lock + +Rust Analyzer invokes Cargo in the background, and it can thus block manually executed +`cargo` commands from making progress (or vice-versa). In some cases, this can also cause +unnecessary recompilations caused by cache thrashing. To avoid this, you can configure +Rust Analyzer to use a [different target directory](./configuration.md#cargo.targetDir). +This will allow both the IDE and Cargo to make progress independently, at the cost of +increased disk space usage caused by the duplicated artifact directories. From 2079d1126bcfb5ed125009f3ccfc67ebd7995d56 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 7 Sep 2025 15:05:11 +0800 Subject: [PATCH 0273/1900] Fix LifetimeParam::lifetime_bounds invalid implement Lifetime node example: ``` LIFETIME_PARAM@15..21 LIFETIME@15..17 LIFETIME_IDENT@15..17 "'a" COLON@17..18 ":" WHITESPACE@18..19 " " TYPE_BOUND_LIST@19..21 TYPE_BOUND@19..21 LIFETIME@19..21 LIFETIME_IDENT@19..21 "'b" ``` --- .../rust-analyzer/crates/syntax/src/ast/node_ext.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 42b0f5cf2da1c..af741d100f680 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -12,8 +12,8 @@ use rowan::{GreenNodeData, GreenTokenData}; use crate::{ NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, T, TokenText, ast::{ - self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, SyntaxNode, - support, + self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, + HasTypeBounds, SyntaxNode, support, }, ted, }; @@ -912,11 +912,10 @@ impl ast::Visibility { impl ast::LifetimeParam { pub fn lifetime_bounds(&self) -> impl Iterator { - self.syntax() - .children_with_tokens() - .filter_map(|it| it.into_token()) - .skip_while(|x| x.kind() != T![:]) - .filter(|it| it.kind() == T![lifetime_ident]) + self.type_bound_list() + .into_iter() + .flat_map(|it| it.bounds()) + .filter_map(|it| it.lifetime()?.lifetime_ident_token()) } } From a69ac553992de87fd5192621a09a21a58887b164 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 10:38:20 +0300 Subject: [PATCH 0274/1900] Resolve paths to snapshot test libraries absolutely That is, resolve them globally, not from the test's location. This *should* result in more robust resolution; for example, previously the code failed to detect the presence of snapshot testing if the snapshot library was renamed in the dependency or was an indirect dependency. --- .../crates/ide/src/hover/tests.rs | 71 +++++++++++++++++++ .../rust-analyzer/crates/ide/src/runnables.rs | 37 ++++------ 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 6f1bbad6ba169..58d8a7edbe042 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10569,6 +10569,77 @@ macro_rules! str { ); } +#[test] +fn test_runnables_with_snapshot_tests_indirect_dep() { + check_actions( + r#" +//- /lib.rs crate:foo deps:utils +use utils::expect_test::expect; + +#[test] +fn test$0() { + let actual = "new25"; + expect!["new25"].assert_eq(&actual); +} + +//- /expect-test/lib.rs crate:expect_test +struct Expect; + +impl Expect { + fn assert_eq(&self, actual: &str) {} +} + +#[macro_export] +macro_rules! expect { + ($e:expr) => Expect; // dummy +} + +//- /utils/lib.rs crate:utils deps:expect_test +pub use expect_test; + "#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 44, + }, + ), + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 33..121, + focus_range: 44..48, + name: "test", + kind: Function, + }, + kind: Test { + test_id: Path( + "test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + update_test: UpdateTest { + expect_test: true, + insta: false, + snapbox: false, + }, + }, + ), + ] + "#]], + ); +} + #[test] fn drop_glue() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6ad9c7884db93..ec13ba0fde340 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -4,8 +4,8 @@ use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ - AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics, - Symbol, db::HirDatabase, sym, + AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, + sym, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ @@ -352,8 +352,7 @@ pub(crate) fn runnable_fn( .call_site(); let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db); - let update_test = - UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); let cfg = def.attrs(sema.db).cfg(); Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test }) @@ -388,7 +387,7 @@ pub(crate) fn runnable_mod( file_id: module_source.file_id.original_file(sema.db), range: module_syntax.text_range(), }; - let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -428,8 +427,7 @@ pub(crate) fn runnable_impl( let impl_source = sema.source(*def)?; let impl_syntax = impl_source.syntax(); let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db); - let update_test = - UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -475,7 +473,7 @@ fn runnable_mod_outline_definition( file_id: mod_source.file_id.original_file(sema.db), range: mod_syntax.text_range(), }; - let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -641,7 +639,7 @@ pub struct UpdateTest { pub snapbox: bool, } -static SNAPSHOT_TEST_MACROS: OnceLock>> = OnceLock::new(); +static SNAPSHOT_TEST_MACROS: OnceLock>> = OnceLock::new(); impl UpdateTest { const EXPECT_CRATE: &str = "expect_test"; @@ -665,22 +663,17 @@ impl UpdateTest { const SNAPBOX_CRATE: &str = "snapbox"; const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"]; - fn find_snapshot_macro( - sema: &Semantics<'_, RootDatabase>, - scope: &SyntaxNode, - file_range: hir::FileRange, - ) -> Self { + fn find_snapshot_macro(sema: &Semantics<'_, RootDatabase>, file_range: hir::FileRange) -> Self { fn init<'a>( krate_name: &'a str, paths: &[&str], - map: &mut FxHashMap<&'a str, Vec>, + map: &mut FxHashMap<&'a str, Vec<[Symbol; 2]>>, ) { let mut res = Vec::with_capacity(paths.len()); - let krate = Name::new_symbol_root(Symbol::intern(krate_name)); + let krate = Symbol::intern(krate_name); for path in paths { - let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))]; - let mod_path = ModPath::from_segments(PathKind::Abs, segments); - res.push(mod_path); + let segments = [krate.clone(), Symbol::intern(path)]; + res.push(segments); } map.insert(krate_name, res); } @@ -694,11 +687,9 @@ impl UpdateTest { }); let search_scope = SearchScope::file_range(file_range); - let find_macro = |paths: &[ModPath]| { + let find_macro = |paths: &[[Symbol; 2]]| { for path in paths { - let Some(items) = sema.resolve_mod_path(scope, path) else { - continue; - }; + let items = hir::resolve_absolute_path(sema.db, path.iter().cloned()); for item in items { if let hir::ItemInNs::Macros(makro) = item && Definition::Macro(makro) From 74a8f7451e75a789a4f656682465b4a2f6b559d2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 13:31:05 +0300 Subject: [PATCH 0275/1900] Expand target info to include the architecture And make it easier to expand it more in the future, if needed. --- .../rust-analyzer/crates/base-db/src/input.rs | 4 +- .../rust-analyzer/crates/base-db/src/lib.rs | 7 ++- .../crates/base-db/src/target.rs | 50 +++++++++++++++++++ .../hir-def/src/nameres/tests/incremental.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 3 +- .../crates/hir-ty/src/layout/target.rs | 8 +-- .../crates/hir-ty/src/layout/tests.rs | 11 ++-- .../crates/hir-ty/src/mir/eval.rs | 3 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/project-model/src/lib.rs | 2 +- .../crates/project-model/src/tests.rs | 7 ++- .../{target_data_layout.rs => target_data.rs} | 41 +++++++++++++-- .../crates/project-model/src/workspace.rs | 38 +++++++------- .../cargo_hello_world_project_model.txt | 10 ++-- ...project_model_with_selective_overrides.txt | 10 ++-- ..._project_model_with_wildcard_overrides.txt | 10 ++-- .../output/rust_project_cfg_groups.txt | 8 +-- ...rust_project_hello_world_project_model.txt | 4 +- .../rust-analyzer/src/cli/rustc_tests.rs | 7 ++- .../rust-analyzer/tests/slow-tests/support.rs | 2 + .../crates/test-fixture/src/lib.rs | 18 +++++-- .../crates/test-utils/src/fixture.rs | 19 ++++++- 22 files changed, 188 insertions(+), 78 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/base-db/src/target.rs rename src/tools/rust-analyzer/crates/project-model/src/toolchain_info/{target_data_layout.rs => target_data.rs} (72%) diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 0bf4fbdfbd691..cac74778a26b0 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -295,8 +295,6 @@ impl CrateDisplayName { } } -pub type TargetLayoutLoadResult = Result, Arc>; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ReleaseChannel { Stable, @@ -929,7 +927,7 @@ mod tests { use super::{CrateGraphBuilder, CrateName, CrateOrigin, Edition::Edition2018, Env, FileId}; fn empty_ws_data() -> Arc { - Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }) + Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None }) } #[test] diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 14544acc11bdf..0e411bcfae60e 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -6,6 +6,7 @@ pub use salsa_macros; // FIXME: Rename this crate, base db is non descriptive mod change; mod input; +pub mod target; use std::{ cell::RefCell, @@ -20,8 +21,7 @@ pub use crate::{ BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap, DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError, - ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult, - UniqueCrateData, + ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, UniqueCrateData, }, }; use dashmap::{DashMap, mapref::entry::Entry}; @@ -359,8 +359,7 @@ impl Nonce { /// Crate related data shared by the whole workspace. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct CrateWorkspaceData { - // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query - pub data_layout: TargetLayoutLoadResult, + pub target: Result, /// Toolchain version used to compile the crate. pub toolchain: Option, } diff --git a/src/tools/rust-analyzer/crates/base-db/src/target.rs b/src/tools/rust-analyzer/crates/base-db/src/target.rs new file mode 100644 index 0000000000000..19d3407bf3c80 --- /dev/null +++ b/src/tools/rust-analyzer/crates/base-db/src/target.rs @@ -0,0 +1,50 @@ +//! Information about the target. + +use std::fmt; + +use triomphe::Arc; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Arch { + // Only what we need is present here. + Wasm32, + Wasm64, + Other, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct TargetData { + pub data_layout: Box, + pub arch: Arch, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct TargetLoadError(Arc); + +impl fmt::Debug for TargetLoadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for TargetLoadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl std::error::Error for TargetLoadError {} + +impl From for TargetLoadError { + fn from(value: String) -> Self { + Self(value.into()) + } +} + +impl From<&str> for TargetLoadError { + fn from(value: &str) -> Self { + Self(value.into()) + } +} + +pub type TargetLoadResult = Result; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 338851b715bf0..6afa04bc412aa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -84,7 +84,7 @@ pub const BAZ: u32 = 0; ) .unwrap(), ), - Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }), + Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None }), ) }; let a = add_crate("a", 0); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 0b7213d785c91..0aec2b9dec7bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -2,6 +2,7 @@ //! type inference-related queries. use base_db::Crate; +use base_db::target::TargetLoadError; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -108,7 +109,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result, Arc>; + fn target_data_layout(&self, krate: Crate) -> Result, TargetLoadError>; #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 82d0ed4f19470..8a7d93d50c05b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -1,6 +1,6 @@ //! Target dependent parameters needed for layouts -use base_db::Crate; +use base_db::{Crate, target::TargetLoadError}; use hir_def::layout::TargetDataLayout; use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors}; use triomphe::Arc; @@ -10,9 +10,9 @@ use crate::db::HirDatabase; pub fn target_data_layout_query( db: &dyn HirDatabase, krate: Crate, -) -> Result, Arc> { - match &krate.workspace_data(db).data_layout { - Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it, AddressSpace::ZERO) { +) -> Result, TargetLoadError> { + match &krate.workspace_data(db).target { + Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) { Ok(it) => Ok(Arc::new(it)), Err(e) => { Err(match e { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 90de7e5ca633d..523ddad94666b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,3 +1,4 @@ +use base_db::target::TargetData; use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; @@ -18,8 +19,8 @@ use crate::{ mod closure; -fn current_machine_data_layout() -> String { - project_model::toolchain_info::target_data_layout::get( +fn current_machine_target_data() -> TargetData { + project_model::toolchain_info::target_data::get( QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), @@ -32,7 +33,8 @@ fn eval_goal( minicore: &str, ) -> Result, LayoutError> { let _tracing = setup_tracing(); - let target_data_layout = current_machine_data_layout(); + let target_data = current_machine_target_data(); + let target_data_layout = target_data.data_layout; let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}", ); @@ -104,7 +106,8 @@ fn eval_expr( minicore: &str, ) -> Result, LayoutError> { let _tracing = setup_tracing(); - let target_data_layout = current_machine_data_layout(); + let target_data = current_machine_target_data(); + let target_data_layout = target_data.data_layout; let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}", ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index c60ace85be124..94d7c2f5ec5ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -3,6 +3,7 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; use base_db::Crate; +use base_db::target::TargetLoadError; use chalk_ir::{Mutability, cast::Cast}; use either::Either; use hir_def::{ @@ -337,7 +338,7 @@ impl Address { pub enum MirEvalError { ConstEvalError(String, Box), LayoutError(LayoutError, Ty), - TargetDataLayoutNotAvailable(Arc), + TargetDataLayoutNotAvailable(TargetLoadError), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. UndefinedBehavior(String), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 3a6b3247d65c6..50cbef581c7d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -263,7 +263,7 @@ impl Analysis { false, proc_macro_cwd, Arc::new(CrateWorkspaceData { - data_layout: Err("fixture has no layout".into()), + target: Err("fixture has no layout".into()), toolchain: None, }), ); diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index d39781b15066d..e36b904881513 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -18,7 +18,7 @@ pub mod project_json; pub mod toolchain_info { pub mod rustc_cfg; - pub mod target_data_layout; + pub mod target_data; pub mod target_tuple; pub mod version; diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index ed72520f40d4d..987d381fac638 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -9,7 +9,6 @@ use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use span::FileId; -use triomphe::Arc; use crate::{ CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, @@ -47,7 +46,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace { sysroot: Sysroot::empty(), rustc_cfg: Vec::new(), toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), + target: Err("target_data_layout not loaded".into()), extra_includes: Vec::new(), set_test: true, } @@ -62,7 +61,7 @@ fn load_rust_project(file: &str) -> (CrateGraphBuilder, ProcMacroPaths) { sysroot, rustc_cfg: Vec::new(), toolchain: None, - target_layout: Err(Arc::from("test has no data layout")), + target: Err("test has no target data".into()), cfg_overrides: Default::default(), extra_includes: Vec::new(), set_test: true, @@ -265,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() { rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), + target: Err("target_data_layout not loaded".into()), extra_includes: Vec::new(), set_test: true, }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs similarity index 72% rename from src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs rename to src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs index f1d99cb8b002c..b815c0b79718e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs @@ -1,23 +1,54 @@ //! Runs `rustc --print target-spec-json` to get the target_data_layout. use anyhow::Context; +use base_db::target; use rustc_hash::FxHashMap; +use serde_derive::Deserialize; use toolchain::Tool; use crate::{Sysroot, toolchain_info::QueryConfig, utf8_stdout}; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Arch { + Wasm32, + Wasm64, + #[serde(other)] + Other, +} + +impl From for target::Arch { + fn from(value: Arch) -> Self { + match value { + Arch::Wasm32 => target::Arch::Wasm32, + Arch::Wasm64 => target::Arch::Wasm64, + Arch::Other => target::Arch::Other, + } + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct TargetSpec { + pub data_layout: String, + pub arch: Arch, +} + /// Uses `rustc --print target-spec-json`. pub fn get( config: QueryConfig<'_>, target: Option<&str>, extra_env: &FxHashMap>, -) -> anyhow::Result { +) -> anyhow::Result { const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; let process = |output: String| { - (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() - .ok_or_else(|| { - anyhow::format_err!("could not parse target-spec-json from command output") - }) + let target_spec = serde_json::from_str::(&output).map_err(|_| { + anyhow::format_err!("could not parse target-spec-json from command output") + })?; + Ok(target::TargetData { + arch: target_spec.arch.into(), + data_layout: target_spec.data_layout.into_boxed_str(), + }) }; let (sysroot, current_dir) = match config { QueryConfig::Cargo(sysroot, cargo_toml, _) => { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 00032bcf1d2e4..e0d2105c8df89 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -8,7 +8,7 @@ use anyhow::Context; use base_db::{ CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError, - ProcMacroPaths, TargetLayoutLoadResult, + ProcMacroPaths, target::TargetLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use intern::{Symbol, sym}; @@ -30,7 +30,7 @@ use crate::{ env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, sysroot::RustLibSrcWorkspace, - toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version}, + toolchain_info::{QueryConfig, rustc_cfg, target_data, target_tuple, version}, utf8_stdout, }; use tracing::{debug, error, info}; @@ -63,7 +63,7 @@ pub struct ProjectWorkspace { /// The toolchain version used by this workspace. pub toolchain: Option, /// The target data layout queried for workspace. - pub target_layout: TargetLayoutLoadResult, + pub target: TargetLoadResult, /// A set of cfg overrides for this workspace. pub cfg_overrides: CfgOverrides, /// Additional includes to add for the VFS. @@ -115,7 +115,7 @@ impl fmt::Debug for ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout, + target: target_layout, cfg_overrides, extra_includes, set_test, @@ -309,8 +309,8 @@ impl ProjectWorkspace { let rustc_cfg = s.spawn(|| { rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env) }); - let data_layout = s.spawn(|| { - target_data_layout::get( + let target_data = s.spawn(|| { + target_data::get( toolchain_config, targets.first().map(Deref::deref), extra_env, @@ -392,7 +392,7 @@ impl ProjectWorkspace { s.spawn(move || cargo_config_env(cargo_toml, &config_file)); thread::Result::Ok(( rustc_cfg.join()?, - data_layout.join()?, + target_data.join()?, rustc_dir.join()?, loaded_sysroot.join()?, cargo_metadata.join()?, @@ -442,7 +442,7 @@ impl ProjectWorkspace { rustc_cfg, cfg_overrides: cfg_overrides.clone(), toolchain, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: data_layout.map_err(|it| it.to_string().into()), extra_includes: extra_includes.clone(), set_test: *set_test, }) @@ -480,11 +480,7 @@ impl ProjectWorkspace { rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let data_layout = s.spawn(|| { - target_data_layout::get( - query_config, - targets.first().map(Deref::deref), - &config.extra_env, - ) + target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let loaded_sysroot = s.spawn(|| { if let Some(sysroot_project) = sysroot_project { @@ -513,7 +509,7 @@ impl ProjectWorkspace { thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?)) }); - let (rustc_cfg, target_layout, loaded_sysroot) = match join { + let (rustc_cfg, target_data, loaded_sysroot) = match join { Ok(it) => it, Err(e) => std::panic::resume_unwind(e), }; @@ -527,7 +523,7 @@ impl ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout: target_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: config.cfg_overrides.clone(), extra_includes: config.extra_includes.clone(), set_test: config.set_test, @@ -551,7 +547,7 @@ impl ProjectWorkspace { let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) .unwrap_or_default(); let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); - let data_layout = target_data_layout::get(query_config, None, &config.extra_env); + let target_data = target_data::get(query_config, None, &config.extra_env); let target_dir = config .target_dir .clone() @@ -610,7 +606,7 @@ impl ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: config.cfg_overrides.clone(), extra_includes: config.extra_includes.clone(), set_test: config.set_test, @@ -942,7 +938,7 @@ impl ProjectWorkspace { let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self; let crate_ws_data = Arc::new(CrateWorkspaceData { toolchain: self.toolchain.clone(), - data_layout: self.target_layout.clone(), + target: self.target.clone(), }); let (crate_graph, proc_macros) = match kind { ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph( @@ -1000,13 +996,15 @@ impl ProjectWorkspace { } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { - let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self; + let Self { + kind, sysroot, rustc_cfg, toolchain, target: target_layout, cfg_overrides, .. + } = self; let Self { kind: o_kind, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg, toolchain: o_toolchain, - target_layout: o_target_layout, + target: o_target_layout, cfg_overrides: o_cfg_overrides, .. } = other; diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 3722e2c721686..4f6ce4dc95374 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -70,7 +70,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -155,7 +155,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -240,7 +240,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -325,7 +325,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -406,7 +406,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 3722e2c721686..4f6ce4dc95374 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -70,7 +70,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -155,7 +155,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -240,7 +240,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -325,7 +325,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -406,7 +406,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 7b156ea63a58f..6862918e09ae6 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -69,7 +69,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -153,7 +153,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -237,7 +237,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -321,7 +321,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -402,7 +402,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt index 98fe598eb3a32..28ad3236ae813 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt @@ -43,8 +43,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, @@ -93,8 +93,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index 0dc373b5b47ed..dabb3aa674414 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -40,8 +40,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 36ae98b321b84..609ebf2b514f0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -11,7 +11,7 @@ use ide_db::base_db; use itertools::Either; use paths::Utf8PathBuf; use profile::StopWatch; -use project_model::toolchain_info::{QueryConfig, target_data_layout}; +use project_model::toolchain_info::{QueryConfig, target_data}; use project_model::{ CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, RustSourceWorkspaceConfig, Sysroot, @@ -19,7 +19,6 @@ use project_model::{ use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; use rustc_hash::FxHashMap; -use triomphe::Arc; use vfs::{AbsPathBuf, FileId}; use walkdir::WalkDir; @@ -87,7 +86,7 @@ impl Tester { sysroot.set_workspace(loaded_sysroot); } - let data_layout = target_data_layout::get( + let target_data = target_data::get( QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), None, &cargo_config.extra_env, @@ -101,7 +100,7 @@ impl Tester { sysroot, rustc_cfg: vec![], toolchain: None, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: Default::default(), extra_includes: vec![], set_test: true, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 2bebb0c1b9700..3464a9644b19d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -97,6 +97,7 @@ impl Project<'_> { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse(self.fixture); assert!(proc_macro_names.is_empty()); assert!(mini_core.is_none()); @@ -177,6 +178,7 @@ impl Project<'_> { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse(self.fixture); assert!(proc_macro_names.is_empty()); assert!(mini_core.is_none()); diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 57fca70547bf9..7574d12c0cfd0 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -1,6 +1,7 @@ //! A set of high-level utility fixture methods to use in tests. use std::{any::TypeId, mem, str::FromStr, sync}; +use base_db::target::TargetData; use base_db::{ Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase, @@ -136,8 +137,11 @@ impl ChangeFixture { proc_macro_names, toolchain, target_data_layout, + target_arch, } = FixtureWithProjectMeta::parse(ra_fixture); - let target_data_layout = Ok(target_data_layout.into()); + let target_data_layout = target_data_layout.into(); + let target_arch = parse_target_arch(&target_arch); + let target = Ok(TargetData { arch: target_arch, data_layout: target_data_layout }); let toolchain = Some({ let channel = toolchain.as_deref().unwrap_or("stable"); Version::parse(&format!("1.76.0-{channel}")).unwrap() @@ -163,8 +167,7 @@ impl ChangeFixture { let mut file_position = None; - let crate_ws_data = - Arc::new(CrateWorkspaceData { data_layout: target_data_layout, toolchain }); + let crate_ws_data = Arc::new(CrateWorkspaceData { target, toolchain }); // FIXME: This is less than ideal let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())); @@ -395,6 +398,15 @@ impl ChangeFixture { } } +fn parse_target_arch(arch: &str) -> base_db::target::Arch { + use base_db::target::Arch::*; + match arch { + "wasm32" => Wasm32, + "wasm64" => Wasm64, + _ => Other, + } +} + fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> { Box::new([ ( diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index e830c6a7cf688..c024089a016f9 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -149,6 +149,8 @@ pub struct FixtureWithProjectMeta { /// You probably don't want to manually specify this. See LLVM manual for the /// syntax, if you must: pub target_data_layout: String, + /// Specifies the target architecture. + pub target_arch: String, } impl FixtureWithProjectMeta { @@ -178,6 +180,7 @@ impl FixtureWithProjectMeta { let mut toolchain = None; let mut target_data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_owned(); + let mut target_arch = "x86_64".to_owned(); let mut mini_core = None; let mut res: Vec = Vec::new(); let mut proc_macro_names = vec![]; @@ -194,6 +197,12 @@ impl FixtureWithProjectMeta { fixture = remain; } + if let Some(meta) = fixture.strip_prefix("//- target_arch:") { + let (meta, remain) = meta.split_once('\n').unwrap(); + meta.trim().clone_into(&mut target_arch); + fixture = remain; + } + if let Some(meta) = fixture.strip_prefix("//- proc_macros:") { let (meta, remain) = meta.split_once('\n').unwrap(); proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect(); @@ -232,7 +241,14 @@ impl FixtureWithProjectMeta { } } - Self { fixture: res, mini_core, proc_macro_names, toolchain, target_data_layout } + Self { + fixture: res, + mini_core, + proc_macro_names, + toolchain, + target_data_layout, + target_arch, + } } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -511,6 +527,7 @@ fn parse_fixture_gets_full_meta() { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse( r#" //- toolchain: nightly From d6638e9bd2b455cf0d3a0df04afc2bab8f134fc9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 13:49:11 +0300 Subject: [PATCH 0276/1900] Make `#[target_feature]` safe always on WASM Even when the feature isn't enabled, as it's not UB to invoke an undefined feature in WASM (just a trap). --- .../hir-ty/src/diagnostics/unsafe_check.rs | 24 +++++++++++++++---- .../rust-analyzer/crates/hir-ty/src/lib.rs | 5 +++- .../rust-analyzer/crates/hir-ty/src/utils.rs | 12 ++++++++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 23 ++++++++++++++---- .../src/handlers/missing_unsafe.rs | 16 +++++++++++++ 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index d6d669258cc1f..64c4cdeaddf31 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -15,8 +15,9 @@ use hir_def::{ use span::Edition; use crate::{ - InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase, - utils::is_fn_unsafe_to_call, + InferenceResult, Interner, TargetFeatures, TyExt, TyKind, + db::HirDatabase, + utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target}, }; #[derive(Debug, Default)] @@ -144,6 +145,9 @@ struct UnsafeVisitor<'db> { def_target_features: TargetFeatures, // FIXME: This needs to be the edition of the span of each call. edition: Edition, + /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when + /// the target feature is not enabled. This flag encodes that. + target_feature_is_safe: bool, } impl<'db> UnsafeVisitor<'db> { @@ -159,7 +163,12 @@ impl<'db> UnsafeVisitor<'db> { DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), _ => TargetFeatures::default(), }; - let edition = resolver.module().krate().data(db).edition; + let krate = resolver.module().krate(); + let edition = krate.data(db).edition; + let target_feature_is_safe = match &krate.workspace_data(db).target { + Ok(target) => target_feature_is_safe_in_target(target), + Err(_) => false, + }; Self { db, infer, @@ -172,6 +181,7 @@ impl<'db> UnsafeVisitor<'db> { callback: unsafe_expr_cb, def_target_features, edition, + target_feature_is_safe, } } @@ -184,7 +194,13 @@ impl<'db> UnsafeVisitor<'db> { } fn check_call(&mut self, node: ExprId, func: FunctionId) { - let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition); + let unsafety = is_fn_unsafe_to_call( + self.db, + func, + &self.def_target_features, + self.edition, + self.target_feature_is_safe, + ); match unsafety { crate::utils::Unsafety::Safe => {} crate::utils::Unsafety::Unsafe => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c16bbb7b99221..ee352619487c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -129,7 +129,10 @@ pub use mapping::{ pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; pub use traits::TraitEnvironment; -pub use utils::{Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use utils::{ + Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call, + target_feature_is_safe_in_target, +}; pub use variance::Variance; pub use chalk_ir::{ diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 881b1c1a9db26..07679d2a119d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -3,7 +3,10 @@ use std::{cell::LazyCell, iter}; -use base_db::Crate; +use base_db::{ + Crate, + target::{self, TargetData}, +}; use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder}; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, @@ -275,18 +278,23 @@ pub enum Unsafety { DeprecatedSafe2024, } +pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool { + matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64) +} + pub fn is_fn_unsafe_to_call( db: &dyn HirDatabase, func: FunctionId, caller_target_features: &TargetFeatures, call_edition: Edition, + target_feature_is_safe: bool, ) -> Unsafety { let data = db.function_signature(func); if data.is_unsafe() { return Unsafety::Unsafe; } - if data.has_target_feature() { + if data.has_target_feature() && !target_feature_is_safe { // RFC 2396 . let callee_target_features = TargetFeatures::from_attrs_no_implications(&db.attrs(func.into())); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6eb8a8bf60fd1..3f78a04df1d29 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2542,11 +2542,26 @@ impl Function { caller: Option, call_edition: Edition, ) -> bool { - let target_features = caller - .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()))) - .unwrap_or_default(); + let (target_features, target_feature_is_safe_in_target) = caller + .map(|caller| { + let target_features = + hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())); + let target_feature_is_safe_in_target = + match &caller.krate(db).id.workspace_data(db).target { + Ok(target) => hir_ty::target_feature_is_safe_in_target(target), + Err(_) => false, + }; + (target_features, target_feature_is_safe_in_target) + }) + .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false)); matches!( - hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition), + hir_ty::is_fn_unsafe_to_call( + db, + self.id, + &target_features, + call_edition, + target_feature_is_safe_in_target + ), hir_ty::Unsafety::Unsafe ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 17caf63018256..4bb64747f5bb7 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -998,4 +998,20 @@ extern "C" fn naked() { "#, ); } + + #[test] + fn target_feature_safe_on_wasm() { + check_diagnostics( + r#" +//- target_arch: wasm32 + +#[target_feature(enable = "simd128")] +fn requires_target_feature() {} + +fn main() { + requires_target_feature(); +} + "#, + ); + } } From 0df12d7b0a4c8f9e05319cc761858841351d7db4 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 21 Aug 2025 16:50:54 +0100 Subject: [PATCH 0277/1900] erase_regions to erase_and_anonymize_regions --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 2 +- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 2 +- clippy_utils/src/ty/mod.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9aa2f3cf0a5b7..9645a26a68ae1 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -364,7 +364,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) - && let arg_ty = cx.tcx.erase_regions(adjusted_ty) + && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let args = typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 010652e1cb902..6ce7f0b1f0bc9 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -140,7 +140,7 @@ fn is_ref_iterable<'tcx>( let res_ty = cx .tcx - .erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); + .erase_and_anonymize_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a42763172f565..809a6728e1280 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize), + FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 3842c4eb60e82..6aeb22d41a7d7 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect, ); triggered = true; - } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context { + } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 26323af312288..3e6aae475ecce 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -12,8 +12,8 @@ pub(super) fn check<'tcx>( from_ty_orig: Ty<'tcx>, to_ty_orig: Ty<'tcx>, ) -> bool { - let mut from_ty = cx.tcx.erase_regions(from_ty_orig); - let mut to_ty = cx.tcx.erase_regions(to_ty_orig); + let mut from_ty = cx.tcx.erase_and_anonymize_regions(from_ty_orig); + let mut to_ty = cx.tcx.erase_and_anonymize_regions(to_ty_orig); while from_ty != to_ty { let reduced_tys = reduce_refs(cx, from_ty, to_ty); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 70ae982a4458b..e45f884cfcbcf 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -98,7 +98,7 @@ fn into_iter_bound<'tcx>( if tr.def_id() == into_iter_did { into_iter_span = Some(*span); } else { - let tr = cx.tcx.erase_regions(tr); + let tr = cx.tcx.erase_and_anonymize_regions(tr); if tr.has_escaping_bound_vars() { return None; } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad12..9f77a1c4d9b62 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -285,7 +285,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let _ = tcx.hir_body_owner_kind(callee_id); } - let ty = tcx.erase_regions(ty); + let ty = tcx.erase_and_anonymize_regions(ty); if ty.has_escaping_bound_vars() { return false; } From 83a99aadcff60482552fc81e0dc2992403c5652e Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Tue, 9 Sep 2025 15:01:47 +0200 Subject: [PATCH 0278/1900] Bump unicode printable to version 17.0.0 --- library/core/src/unicode/printable.rs | 160 +++++++++++++------------- 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/library/core/src/unicode/printable.rs b/library/core/src/unicode/printable.rs index d8fb50e4ed296..68e1c8ae31c06 100644 --- a/library/core/src/unicode/printable.rs +++ b/library/core/src/unicode/printable.rs @@ -54,13 +54,10 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x2a6e0 <= x && x < 0x2a700 { return false; } - if 0x2b73a <= x && x < 0x2b740 { - return false; - } if 0x2b81e <= x && x < 0x2b820 { return false; } - if 0x2cea2 <= x && x < 0x2ceb0 { + if 0x2ceae <= x && x < 0x2ceb0 { return false; } if 0x2ebe1 <= x && x < 0x2ebf0 { @@ -75,7 +72,7 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x3134b <= x && x < 0x31350 { return false; } - if 0x323b0 <= x && x < 0xe0100 { + if 0x3347a <= x && x < 0xe0100 { return false; } if 0xe01f0 <= x && x < 0x110000 { @@ -96,7 +93,7 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x09, 17), (0x0a, 28), (0x0b, 25), - (0x0c, 26), + (0x0c, 25), (0x0d, 16), (0x0e, 12), (0x0f, 4), @@ -107,24 +104,22 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x17, 4), (0x18, 1), (0x19, 3), - (0x1a, 7), + (0x1a, 9), (0x1b, 1), (0x1c, 2), (0x1f, 22), (0x20, 3), - (0x2b, 3), + (0x2b, 2), (0x2d, 11), (0x2e, 1), (0x30, 4), (0x31, 2), (0x32, 1), - (0xa7, 4), (0xa9, 2), (0xaa, 4), (0xab, 8), (0xfa, 2), (0xfb, 5), - (0xfd, 2), (0xfe, 3), (0xff, 9), ]; @@ -143,30 +138,29 @@ const SINGLETONS0L: &[u8] = &[ 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x3a, 0x3b, 0x45, 0x49, - 0x57, 0x5b, 0x5c, 0x5e, 0x5f, 0x64, 0x65, 0x8d, - 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, - 0xe4, 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, - 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, - 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, - 0xbe, 0xbf, 0xc5, 0xc7, 0xcf, 0xda, 0xdb, 0x48, - 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, - 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, - 0xb1, 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, - 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, - 0xff, 0x80, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x1f, - 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, - 0xaf, 0x4d, 0xbb, 0xbc, 0x16, 0x17, 0x1e, 0x1f, - 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, - 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, - 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, + 0x57, 0x5b, 0x5e, 0x5f, 0x64, 0x65, 0x8d, 0x91, + 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, + 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, + 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, + 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, + 0xbf, 0xc5, 0xc7, 0xcf, 0xda, 0xdb, 0x48, 0x98, + 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, + 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, + 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, + 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, + 0x80, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, + 0xde, 0xdf, 0x4d, 0xbb, 0xbc, 0x16, 0x17, 0x1e, + 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, + 0x5e, 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, + 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x00, 0x40, 0x97, 0x98, - 0x30, 0x8f, 0x1f, 0xce, 0xcf, 0xd2, 0xd4, 0xce, - 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, - 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, - 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0x53, 0x67, - 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, - 0xfe, 0xff, + 0x30, 0x8f, 0x1f, 0xce, 0xff, 0x4e, 0x4f, 0x5a, + 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, + 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, + 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, + 0xd9, 0xe7, 0xfe, 0xff, ]; #[rustfmt::skip] const SINGLETONS1U: &[(u8, u8)] = &[ @@ -195,6 +189,7 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0x24, 1), (0x6a, 4), (0x6b, 2), + (0x6e, 2), (0xaf, 3), (0xb1, 2), (0xbc, 2), @@ -207,12 +202,13 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0xda, 1), (0xe0, 5), (0xe1, 2), + (0xe6, 1), (0xe7, 4), (0xe8, 2), (0xee, 32), (0xf0, 4), (0xf8, 2), - (0xfa, 4), + (0xfa, 5), (0xfb, 1), ]; #[rustfmt::skip] @@ -231,18 +227,19 @@ const SINGLETONS1L: &[u8] = &[ 0x39, 0x3a, 0xa8, 0xa9, 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x11, 0x6f, 0x5f, 0xbf, 0xee, - 0xef, 0x5a, 0x62, 0xf4, 0xfc, 0xff, 0x53, 0x54, - 0x9a, 0x9b, 0x2e, 0x2f, 0x27, 0x28, 0x55, 0x9d, - 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, - 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, - 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, 0xa0, - 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xe7, - 0xec, 0xef, 0xff, 0xc5, 0xc6, 0x04, 0x20, 0x23, - 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, - 0x4c, 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, - 0x5e, 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, - 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, - 0xd0, 0xae, 0xaf, 0x6e, 0x6f, 0xdd, 0xde, 0x93, + 0xef, 0x5a, 0x62, 0xb9, 0xba, 0xf4, 0xfc, 0xff, + 0x53, 0x54, 0x9a, 0x9b, 0x2e, 0x2f, 0x27, 0x28, + 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, + 0xad, 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, + 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, + 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, + 0x3f, 0xdf, 0xe7, 0xec, 0xef, 0xff, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, + 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, 0x55, 0x56, + 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, + 0xaf, 0xb0, 0xc0, 0xd0, 0xae, 0xaf, 0x6e, 0x6f, + 0xc7, 0xdd, 0xde, 0x93, ]; #[rustfmt::skip] const NORMAL0: &[u8] = &[ @@ -254,7 +251,7 @@ const NORMAL0: &[u8] = &[ 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x05, - 0x1f, 0x08, + 0x20, 0x07, 0x81, 0x1c, 0x03, 0x19, 0x08, 0x01, 0x04, @@ -282,8 +279,8 @@ const NORMAL0: &[u8] = &[ 0x4e, 0x07, 0x1b, 0x07, 0x57, 0x07, - 0x02, 0x06, - 0x17, 0x0c, + 0x02, 0x05, + 0x18, 0x0c, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, @@ -319,7 +316,7 @@ const NORMAL0: &[u8] = &[ 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, - 0x2f, 0x31, + 0x4c, 0x14, 0x80, 0xf4, 0x08, 0x3c, 0x03, 0x0f, 0x03, @@ -330,7 +327,7 @@ const NORMAL0: &[u8] = &[ 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, - 0x21, 0x0f, + 0x22, 0x0e, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x9a, 0x16, @@ -349,8 +346,8 @@ const NORMAL0: &[u8] = &[ 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, - 0x80, 0xdd, 0x15, - 0x3b, 0x03, + 0x80, 0xdd, 0x14, + 0x3c, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, @@ -370,9 +367,7 @@ const NORMAL0: &[u8] = &[ 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, - 0x80, 0xa6, 0x10, - 0x81, 0xf5, 0x07, - 0x01, 0x20, + 0x82, 0xb3, 0x20, 0x2a, 0x06, 0x4c, 0x04, 0x80, 0x8d, 0x04, @@ -414,7 +409,7 @@ const NORMAL1: &[u8] = &[ 0x16, 0x05, 0x21, 0x03, 0x1b, 0x05, - 0x01, 0x40, + 0x1b, 0x26, 0x38, 0x04, 0x4b, 0x05, 0x2f, 0x04, @@ -437,8 +432,9 @@ const NORMAL1: &[u8] = &[ 0x1d, 0x08, 0x02, 0x80, 0xd0, 0x52, 0x10, - 0x03, 0x37, - 0x2c, 0x08, + 0x06, 0x08, + 0x09, 0x21, + 0x2e, 0x08, 0x2a, 0x16, 0x1a, 0x26, 0x1c, 0x14, @@ -481,7 +477,8 @@ const NORMAL1: &[u8] = &[ 0x48, 0x08, 0x53, 0x0d, 0x49, 0x07, - 0x0a, 0x80, 0xb6, + 0x0a, 0x56, + 0x08, 0x58, 0x22, 0x0e, 0x0a, 0x06, 0x46, 0x0a, @@ -491,7 +488,9 @@ const NORMAL1: &[u8] = &[ 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, - 0x0a, 0x81, 0x36, + 0x0a, 0x06, + 0x2c, 0x04, + 0x0a, 0x80, 0xf6, 0x19, 0x07, 0x3b, 0x03, 0x1d, 0x55, @@ -514,15 +513,16 @@ const NORMAL1: &[u8] = &[ 0x28, 0x05, 0x13, 0x81, 0xb0, 0x3a, 0x80, 0xc6, - 0x5b, 0x65, + 0x5b, 0x05, + 0x34, 0x2c, 0x4b, 0x04, 0x39, 0x07, 0x11, 0x40, 0x05, 0x0b, - 0x02, 0x0e, - 0x97, 0xf8, 0x08, - 0x84, 0xd6, 0x29, - 0x0a, 0xa2, 0xe7, + 0x07, 0x09, + 0x9c, 0xd6, 0x29, + 0x20, 0x61, + 0x73, 0xa1, 0xfd, 0x81, 0x33, 0x0f, 0x01, 0x1d, 0x06, 0x0e, @@ -532,8 +532,10 @@ const NORMAL1: &[u8] = &[ 0x0d, 0x03, 0x09, 0x07, 0x10, 0x8f, 0x60, - 0x80, 0xfa, 0x06, - 0x81, 0xb4, 0x4c, + 0x80, 0xfd, 0x03, + 0x81, 0xb4, 0x06, + 0x17, 0x0f, + 0x11, 0x0f, 0x47, 0x09, 0x74, 0x3c, 0x80, 0xf6, 0x0a, @@ -560,7 +562,9 @@ const NORMAL1: &[u8] = &[ 0x01, 0x81, 0xd0, 0x2a, 0x80, 0xd6, 0x2b, 0x04, - 0x01, 0x81, 0xe0, + 0x01, 0x80, 0xc0, + 0x36, 0x08, + 0x02, 0x80, 0xe0, 0x80, 0xf7, 0x29, 0x4c, 0x04, 0x0a, 0x04, @@ -581,11 +585,10 @@ const NORMAL1: &[u8] = &[ 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, - 0x83, 0xd8, 0x04, + 0x83, 0xd9, 0x03, 0x11, 0x03, 0x0d, 0x03, - 0x77, 0x04, - 0x5f, 0x06, + 0x80, 0xda, 0x06, 0x0c, 0x04, 0x01, 0x0f, 0x0c, 0x04, @@ -593,12 +596,13 @@ const NORMAL1: &[u8] = &[ 0x0a, 0x06, 0x28, 0x08, 0x2c, 0x04, - 0x02, 0x3e, - 0x81, 0x54, 0x0c, + 0x02, 0x0e, + 0x09, 0x27, + 0x81, 0x58, 0x08, 0x1d, 0x03, - 0x0a, 0x05, - 0x38, 0x07, - 0x1c, 0x06, - 0x09, 0x07, - 0x80, 0xfa, 0x84, 0x06, + 0x0b, 0x03, + 0x3b, 0x04, + 0x1e, 0x04, + 0x0a, 0x07, + 0x80, 0xfb, 0x84, 0x05, ]; From 0fad52401e39eba86e8246834a5317b9c8d77652 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 22:54:10 +0000 Subject: [PATCH 0279/1900] WIP switch inference table to next-solver --- .../crates/hir-ty/src/consteval_nextsolver.rs | 6 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../crates/hir-ty/src/infer/coerce.rs | 64 +- .../crates/hir-ty/src/infer/expr.rs | 32 +- .../crates/hir-ty/src/infer/path.rs | 12 +- .../crates/hir-ty/src/infer/unify.rs | 589 ++++++----- .../rust-analyzer/crates/hir-ty/src/lib.rs | 16 +- .../crates/hir-ty/src/method_resolution.rs | 201 ++-- .../infer/canonical/canonicalizer.rs | 785 ++++++++++++++ .../src/next_solver/infer/canonical/mod.rs | 1 + .../hir-ty/src/next_solver/infer/mod.rs | 54 +- .../src/next_solver/infer/snapshot/mod.rs | 4 +- .../crates/hir-ty/src/next_solver/mapping.rs | 965 +++++++++++++++++- .../crates/hir-ty/src/next_solver/solver.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 67 +- .../rust-analyzer/crates/hir/src/attrs.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 12 +- 17 files changed, 2292 insertions(+), 526 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 4f95c9a13acdb..4700335931549 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -27,7 +27,7 @@ use crate::{ next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, SolverDefId, Ty, ValueConst, - mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, }, }; @@ -145,7 +145,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_usize(db, ec) } @@ -168,7 +168,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_isize(db, &ec) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 9ef046afdb3d3..493652b764015 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -568,7 +568,7 @@ impl InferenceContext<'_> { let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); let snapshot = self.table.snapshot(); - if !self.table.unify(&expected_sig.substitution, &supplied_sig.expected_sig.substitution) { + if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>(&expected_sig.substitution.0, &supplied_sig.expected_sig.substitution.0) { self.table.rollback_to(snapshot); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 631a91bac481b..b47834f0c749c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -7,22 +7,20 @@ use std::iter; -use chalk_ir::{BoundVar, Goal, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{hir::ExprId, lang_item::LangItem}; +use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; +use hir_def::{ + hir::ExprId, + lang_item::LangItem, +}; +use rustc_type_ir::solve::Certainty; use stdx::always; use triomphe::Arc; use crate::{ - Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, - autoderef::{Autoderef, AutoderefKind}, - db::HirDatabase, - infer::{ + autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, - }, - traits::NextTraitSolveResult, - utils::ClosureSubst, + }, utils::ClosureSubst, Canonical, FnAbi, FnPointer, FnSig, Goal, InEnvironment, Interner, Lifetime, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt }; use super::unify::InferenceTable; @@ -42,7 +40,7 @@ fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { fn success( adj: Vec, target: Ty, - goals: Vec>>, + goals: Vec>, ) -> CoerceResult { Ok(InferOk { goals, value: (adj, target) }) } @@ -304,7 +302,7 @@ impl InferenceTable<'_> { fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult { if from_ty.is_never() { if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, true); + self.set_diverging(*tv, TyVariableKind::General, true); } if coerce_never == CoerceNever::Yes { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound @@ -707,41 +705,15 @@ impl InferenceTable<'_> { b.push(coerce_from).push(to_ty.clone()).build() }; - let goal: InEnvironment = - InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner)); - - let canonicalized = self.canonicalize_with_free_vars(goal); - - // FIXME: rustc's coerce_unsized is more specialized -- it only tries to - // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the - // rest for later. Also, there's some logic about sized type variables. - // Need to find out in what cases this is necessary - let solution = self.db.trait_solve( - krate, - self.trait_env.block, - canonicalized.value.clone().cast(Interner), - ); - - match solution { - // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique` - // was. Chalk's unique guidance at least guarantees that the real solution - // is some "subset" of the solutions matching the guidance, but the - // substs for `Certainty::No` don't have that same guarantee (I think). - NextTraitSolveResult::Certain(v) => { - canonicalized.apply_solution( - self, - Canonical { - binders: v.binders, - // FIXME handle constraints - value: v.value.subst, - }, - ); - } - // ...so, should think about how to get some actually get some guidance here - NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => { - return Err(TypeError); + let goal: Goal = coerce_unsized_tref.cast(Interner); + + self.commit_if_ok(|table| { + match table.solve_obligation(goal) { + Ok(Certainty::Yes) => Ok(()), + Ok(Certainty::Maybe(_)) => Ok(()), + Err(_) => Err(TypeError), } - } + })?; let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 261c02386822d..e39f3ab75f16f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -23,28 +23,9 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, - DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - autoderef::{Autoderef, builtin_deref, deref_by_trait}, - consteval, - generics::generics, - infer::{ - BreakableKind, - coerce::{CoerceMany, CoerceNever, CoercionCause}, - find_continuable, - pat::contains_explicit_ref_binding, - }, - lang_items::lang_items_for_bin_op, - lower::{ - LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability, - path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, - }, - mapping::{ToChalk, from_chalk}, - method_resolution::{self, VisibleFromModule}, - primitive::{self, UintTy}, - static_lifetime, to_chalk_trait_id, - traits::FnTrait, + autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, generics::generics, infer::{ + coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind + }, lang_items::lang_items_for_bin_op, lower::{lower_to_chalk_mutability, path::{substs_from_args_and_bindings, GenericArgsLowerer, TypeLikeConst}, ParamLoweringMode}, mapping::{from_chalk, ToChalk}, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind }; use super::{ @@ -826,7 +807,7 @@ impl InferenceContext<'_> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = self.canonicalize(base_ty.clone()); + let canonicalized = ChalkToNextSolver::from_nextsolver(self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)), self.table.interner); let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, self.table.trait_env.clone(), @@ -932,6 +913,7 @@ impl InferenceContext<'_> { } None => { let expected_ty = expected.to_option(&mut self.table); + tracing::debug!(?expected_ty); let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, Some(TyKind::Scalar(Scalar::Char)) => { @@ -1678,7 +1660,7 @@ impl InferenceContext<'_> { None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1824,7 +1806,7 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index bc8648ecdd943..9f3cd2b8fb006 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -10,15 +10,7 @@ use hir_expand::name::Name; use stdx::never; use crate::{ - InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, - TyKind, ValueTyDefId, - builder::ParamKind, - consteval, error_lifetime, - generics::generics, - infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, - lower::LifetimeElisionKind, - method_resolution::{self, VisibleFromModule}, - to_chalk_trait_id, + builder::ParamKind, consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, to_chalk_trait_id, InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId }; use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; @@ -322,7 +314,7 @@ impl InferenceContext<'_> { return Some(result); } - let canonical_ty = self.canonicalize(ty.clone()); + let canonical_ty = self.canonicalize(ty.clone().to_nextsolver(self.table.interner)); let mut not_visible = None; let res = method_resolution::iterate_method_candidates( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index bb4782bd41942..3745d2c98fd9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,35 +3,27 @@ use std::{fmt, mem}; use chalk_ir::{ - CanonicalVarKind, FloatTy, IntTy, TyVariableKind, UniverseIndex, cast::Cast, - fold::TypeFoldable, interner::HasInterner, zip::Zip, + cast::Cast, fold::TypeFoldable, interner::HasInterner, CanonicalVarKind, FloatTy, IntTy, TyVariableKind, }; -use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; -use ena::unify::UnifyKey; use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_next_trait_solver::solve::HasChanged; +use rustc_type_ir::{inherent::Span, relate::{solver_relating::RelateExt, Relate}, solve::{Certainty, NoSolution}, FloatVid, IntVid, TyVid}; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, - OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, - consteval::unknown_const, - db::HirDatabase, - fold_generic_args, fold_tys_and_consts, to_chalk_trait_id, - traits::{FnTrait, NextTraitSolveResult}, + consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{infer::{canonical::canonicalizer::OriginalQueryValues, snapshot::CombinedSnapshot, DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, InferenceVarExt}, DbInterner, ParamEnvAnd, SolverDefIds}, to_chalk_trait_id, traits::{next_trait_solve, next_trait_solve_canonical, next_trait_solve_in_ctxt, FnTrait, NextTraitSolveResult}, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause }; -impl InferenceContext<'_> { - pub(super) fn canonicalize(&mut self, t: T) -> Canonical +impl<'db> InferenceContext<'db> { + pub(super) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { self.table.canonicalize(t) } @@ -42,11 +34,11 @@ impl InferenceContext<'_> { ) -> SmallVec<[WhereClause; 4]> { self.table.resolve_obligations_as_possible(); - let root = self.table.var_unification_table.inference_var_root(self_ty); + let root = InferenceVar::from_vid(self.table.infer_ctxt.root_var(self_ty.to_vid())); let pending_obligations = mem::take(&mut self.table.pending_obligations); let obligations = pending_obligations .iter() - .filter_map(|obligation| match obligation.value.value.goal.data(Interner) { + .filter_map(|obligation| match obligation.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(clause)) => { let ty = match clause { WhereClause::AliasEq(AliasEq { @@ -59,18 +51,9 @@ impl InferenceContext<'_> { WhereClause::TypeOutlives(to) => to.ty.clone(), _ => return None, }; - - let uncanonical = - chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner); - if matches!( - self.resolve_ty_shallow(&uncanonical).kind(Interner), - TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, - ) { - Some(chalk_ir::Substitute::apply( - &obligation.free_vars, - clause.clone(), - Interner, - )) + let ty = self.resolve_ty_shallow(&ty); + if matches!(ty.kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root) { + Some(clause.clone()) } else { None } @@ -229,32 +212,31 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; #[derive(Clone)] pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, + pub(crate) interner: DbInterner<'a>, pub(crate) trait_env: Arc, pub(crate) tait_coercion_table: Option>, - var_unification_table: ChalkInferenceTable, - type_variable_table: SmallVec<[TypeVariableFlags; 16]>, - pending_obligations: Vec>>, - /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on - /// temporary allocations. - resolve_obligations_buffer: Vec>>, + infer_ctxt: InferCtxt<'a>, + diverging_tys: FxHashSet, + pending_obligations: Vec>, } pub(crate) struct InferenceTableSnapshot { - var_table_snapshot: chalk_solve::infer::InferenceSnapshot, - type_variable_table: SmallVec<[TypeVariableFlags; 16]>, - pending_obligations: Vec>>, + ctxt_snapshot: CombinedSnapshot, + diverging_tys: FxHashSet, + pending_obligations: Vec>, } impl<'a> InferenceTable<'a> { pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc) -> Self { + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); InferenceTable { db, + interner, trait_env, tait_coercion_table: None, - var_unification_table: ChalkInferenceTable::new(), - type_variable_table: SmallVec::new(), + infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []) }), + diverging_tys: FxHashSet::default(), pending_obligations: Vec::new(), - resolve_obligations_buffer: Vec::new(), } } @@ -265,29 +247,43 @@ impl<'a> InferenceTable<'a> { /// marked as diverging if necessary, so that resolving them gives the right /// result. pub(super) fn propagate_diverging_flag(&mut self) { - for i in 0..self.type_variable_table.len() { - if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) { - continue; + let mut new_tys = FxHashSet::default(); + for ty in self.diverging_tys.iter() { + match ty.kind(Interner) { + TyKind::InferenceVar(var, kind) => { + match kind { + TyVariableKind::General => { + let root = InferenceVar::from(self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + TyVariableKind::Integer => { + let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from_usize(var.index() as usize)).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + TyVariableKind::Float => { + let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from_usize(var.index() as usize)).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + } + } + _ => {} } - let v = InferenceVar::from(i as u32); - let root = self.var_unification_table.inference_var_root(v); - self.modify_type_variable_flag(root, |f| { - *f |= TypeVariableFlags::DIVERGING; - }); } + self.diverging_tys.extend(new_tys.into_iter()); } - pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.modify_type_variable_flag(iv, |f| { - f.set(TypeVariableFlags::DIVERGING, diverging); - }); + pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind, diverging: bool) { + self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner)); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - let is_diverging = self - .type_variable_table - .get(iv.index() as usize) - .is_some_and(|data| data.contains(TypeVariableFlags::DIVERGING)); + let is_diverging = self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); if is_diverging { return TyKind::Never.intern(Interner); } @@ -299,30 +295,27 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize_with_free_vars(&mut self, t: T) -> Canonicalized + pub(crate) fn canonicalize_with_free_vars(&mut self, t: ParamEnvAnd<'a, T>) -> (rustc_type_ir::Canonical, ParamEnvAnd<'a, T>>, OriginalQueryValues<'a>) where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables self.resolve_obligations_as_possible(); - let result = self.var_unification_table.canonicalize(Interner, t); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } + + let mut orig_values = OriginalQueryValues::default(); + let result = self.infer_ctxt.canonicalize_query(t, &mut orig_values); + (result.canonical, orig_values) } - pub(crate) fn canonicalize(&mut self, t: T) -> Canonical + pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables self.resolve_obligations_as_possible(); - self.var_unification_table.canonicalize(Interner, t).quantified + self.infer_ctxt.canonicalize_response(t) } /// Recurses through the given type, normalizing associated types mentioned @@ -348,6 +341,7 @@ impl<'a> InferenceTable<'a> { self.resolve_ty_shallow(&ty) } TyKind::AssociatedType(id, subst) => { + return Either::Left(self.resolve_ty_shallow(&ty)); if ty.data(Interner).flags.intersects( chalk_ir::TypeFlags::HAS_TY_INFER | chalk_ir::TypeFlags::HAS_CT_INFER, @@ -370,21 +364,24 @@ impl<'a> InferenceTable<'a> { )), ); let in_env = InEnvironment::new(&self.trait_env.env, goal); + let goal = in_env.to_nextsolver(self.interner); + let goal = ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; - let canonicalized = { - let result = - self.var_unification_table.canonicalize(Interner, in_env); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } + let (canonical_goal, _orig_values) = { + let mut orig_values = OriginalQueryValues::default(); + let result = self.infer_ctxt.canonicalize_query(goal, &mut orig_values); + (result.canonical, orig_values) + }; + let canonical_goal = rustc_type_ir::Canonical { + max_universe: canonical_goal.max_universe, + variables: canonical_goal.variables, + value: crate::next_solver::Goal { param_env: canonical_goal.value.param_env, predicate: canonical_goal.value.value }, }; - let solution = self.db.trait_solve( + let solution = next_trait_solve_canonical( + self.db, self.trait_env.krate, self.trait_env.block, - canonicalized.value.clone(), + canonical_goal.clone(), ); if let NextTraitSolveResult::Certain(canonical_subst) = solution { // This is not great :) But let's just assert this for now and come back to it later. @@ -512,38 +509,27 @@ impl<'a> InferenceTable<'a> { var } - fn modify_type_variable_flag(&mut self, var: InferenceVar, cb: F) - where - F: FnOnce(&mut TypeVariableFlags), - { - let idx = var.index() as usize; - if self.type_variable_table.len() <= idx { - self.extend_type_variable_table(idx); - } - if let Some(f) = self.type_variable_table.get_mut(idx) { - cb(f); - } - } - fn extend_type_variable_table(&mut self, to_index: usize) { - let count = to_index - self.type_variable_table.len() + 1; - self.type_variable_table.extend(std::iter::repeat_n(TypeVariableFlags::default(), count)); - } - fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); - // Chalk might have created some type variables for its own purposes that we don't know about... - self.extend_type_variable_table(var.index() as usize); - assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); - let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap(); + let var = match kind { + TyVariableKind::General => { + let var = self.infer_ctxt.next_ty_vid(); + InferenceVar::from(var.as_u32()) + } + TyVariableKind::Integer => { + let var = self.infer_ctxt.next_int_vid(); + InferenceVar::from(var.as_u32()) + } + TyVariableKind::Float => { + let var = self.infer_ctxt.next_float_vid(); + InferenceVar::from(var.as_u32()) + } + }; + + let ty = var.to_ty(Interner, kind); if diverging { - *flags |= TypeVariableFlags::DIVERGING; - } - if matches!(kind, TyVariableKind::Integer) { - *flags |= TypeVariableFlags::INTEGER; - } else if matches!(kind, TyVariableKind::Float) { - *flags |= TypeVariableFlags::FLOAT; + self.diverging_tys.insert(ty.clone()); } - var.to_ty_with_kind(Interner, kind) + ty } pub(crate) fn new_type_var(&mut self) -> Ty { @@ -563,12 +549,14 @@ impl<'a> InferenceTable<'a> { } pub(crate) fn new_const_var(&mut self, ty: Ty) -> Const { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + let var = self.infer_ctxt.next_const_vid(); + let var = InferenceVar::from(var.as_u32()); var.to_const(Interner, ty) } pub(crate) fn new_lifetime_var(&mut self) -> Lifetime { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + let var = self.infer_ctxt.next_region_vid(); + let var = InferenceVar::from(var.as_u32()); var.to_lifetime(Interner) } @@ -580,16 +568,16 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) + self.resolve_with_fallback_inner(t, &fallback) } pub(crate) fn fresh_subst(&mut self, binders: &[CanonicalVarKind]) -> Substitution { Substitution::from_iter( Interner, - binders.iter().map(|kind| { - let param_infer_var = - kind.map_ref(|&ui| self.var_unification_table.new_variable(ui)); - param_infer_var.to_generic_arg(Interner) + binders.iter().map(|kind| match &kind.kind { + chalk_ir::VariableKind::Ty(ty_variable_kind) => self.new_var(*ty_variable_kind, false).cast(Interner), + chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner), + chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner), }), ) } @@ -601,16 +589,23 @@ impl<'a> InferenceTable<'a> { let subst = self.fresh_subst(canonical.binders.as_slice(Interner)); subst.apply(canonical.value, Interner) } + + pub(crate) fn instantiate_canonical_ns(&mut self, canonical: rustc_type_ir::Canonical, T>) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + self.infer_ctxt.instantiate_canonical(&canonical).0 + } fn resolve_with_fallback_inner( &mut self, - var_stack: &mut Vec, t: T, fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, ) -> T where T: HasInterner + TypeFoldable, { + let mut var_stack = &mut vec![]; t.fold_with( &mut resolve::Resolver { table: self, var_stack, fallback }, DebruijnIndex::INNERMOST, @@ -639,29 +634,26 @@ impl<'a> InferenceTable<'a> { let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner); let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner); - let scalar_vars: Vec<_> = self - .type_variable_table - .iter() - .enumerate() - .filter_map(|(index, flags)| { - let kind = if flags.contains(TypeVariableFlags::INTEGER) { - TyVariableKind::Integer - } else if flags.contains(TypeVariableFlags::FLOAT) { - TyVariableKind::Float - } else { - return None; + let int_vars = self.infer_ctxt.inner.borrow_mut().int_unification_table().len(); + for v in 0..int_vars { + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); + let maybe_resolved = self.resolve_ty_shallow(&var); + if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { + // I don't think we can ever unify these vars with float vars, but keep this here for now + let fallback = match kind { + TyVariableKind::Integer => &int_fallback, + TyVariableKind::Float => &float_fallback, + TyVariableKind::General => unreachable!(), }; - - // FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them - // without directly constructing them from `index`? - let var = InferenceVar::from(index as u32).to_ty(Interner, kind); - Some(var) - }) - .collect(); - - for var in scalar_vars { + self.unify(&var, fallback); + } + } + let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len(); + for v in 0..float_vars { + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); let maybe_resolved = self.resolve_ty_shallow(&var); if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { + // I don't think we can ever unify these vars with float vars, but keep this here for now let fallback = match kind { TyVariableKind::Integer => &int_fallback, TyVariableKind::Float => &float_fallback, @@ -673,7 +665,7 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -683,58 +675,65 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify_deeply, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, }; result.goals.iter().all(|goal| { - let canonicalized = self.canonicalize_with_free_vars(goal.clone()); - self.try_resolve_obligation(&canonicalized).certain() + let goal = goal.to_nextsolver(self.interner); + match next_trait_solve_in_ctxt(&self.infer_ctxt, goal) { + Ok((_, Certainty::Yes)) => true, + _ => false, + } }) } /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify>( + pub(crate) fn try_unify, U: Relate>>( &mut self, t1: &T, t2: &T, ) -> InferResult<()> { - match self.var_unification_table.relate( - Interner, - &self.db, - &self.trait_env.env, - chalk_ir::Variance::Invariant, - t1, - t2, - ) { - Ok(result) => Ok(InferOk { goals: result.goals, value: () }), - Err(chalk_ir::NoSolution) => Err(TypeError), + let param_env = self.trait_env.env.to_nextsolver(self.interner); + let lhs = t1.to_nextsolver(self.interner); + let rhs = t2.to_nextsolver(self.interner); + let variance = rustc_type_ir::Variance::Invariant; + let span = crate::next_solver::Span::dummy(); + match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { + Ok(res) => { + let goals = res.into_iter().map(|g| ChalkToNextSolver::from_nextsolver(g, self.interner)).collect(); + Ok(InferOk { goals, value: () }) + } + Err(_) => { + Err(TypeError) + } } } /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. + #[tracing::instrument(skip(self))] pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { return ty.clone(); } self.resolve_obligations_as_possible(); - self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) + ChalkToNextSolver::from_nextsolver(self.infer_ctxt.resolve_vars_if_possible(ty.to_nextsolver(self.interner)), self.interner) } pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { - let var_table_snapshot = self.var_unification_table.snapshot(); - let type_variable_table = self.type_variable_table.clone(); + let ctxt_snapshot = self.infer_ctxt.start_snapshot(); + let diverging_tys = self.diverging_tys.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table } + InferenceTableSnapshot {ctxt_snapshot, pending_obligations, diverging_tys } } #[tracing::instrument(skip_all)] pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { - self.var_unification_table.rollback_to(snapshot.var_table_snapshot); - self.type_variable_table = snapshot.type_variable_table; + self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); + self.diverging_tys = snapshot.diverging_tys; self.pending_obligations = snapshot.pending_obligations; } @@ -746,15 +745,39 @@ impl<'a> InferenceTable<'a> { result } + pub(crate) fn commit_if_ok(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> Result) -> Result { + let snapshot = self.snapshot(); + let result = f(self); + match result { + Ok(_) => {} + Err(_) => { + self.rollback_to(snapshot); + } + } + result + } + /// Checks an obligation without registering it. Useful mostly to check /// whether a trait *might* be implemented before deciding to 'lock in' the /// choice (during e.g. method resolution or deref). #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { let in_env = InEnvironment::new(&self.trait_env.env, goal); - let canonicalized = self.canonicalize(in_env); + let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner)); - self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized) + next_trait_solve_canonical(self.db, self.trait_env.krate, self.trait_env.block, canonicalized) + } + + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result { + let goal = InEnvironment::new(&self.trait_env.env, goal); + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { + return Ok(Certainty::Yes); + }; + + let goal = goal.to_nextsolver(self.interner); + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); + result.map(|m| m.1) } pub(crate) fn register_obligation(&mut self, goal: Goal) { @@ -762,8 +785,8 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(in_env) } - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: InEnvironment) { + // If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy) + fn unify_opaque_instead_of_solve(&mut self, goal: InEnvironment) -> Option> { match goal.goal.data(Interner) { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), @@ -779,7 +802,7 @@ impl<'a> InferenceTable<'a> { .intern(Interner), ty, ) { - return; + return None; } } _ => {} @@ -788,20 +811,20 @@ impl<'a> InferenceTable<'a> { } _ => {} } - let canonicalized = { - let result = self.var_unification_table.canonicalize(Interner, goal); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } - }; - tracing::debug!(?canonicalized); - let solution = self.try_resolve_obligation(&canonicalized); - tracing::debug!(?solution); - if solution.uncertain() { - self.pending_obligations.push(canonicalized); + Some(goal) + } + + #[tracing::instrument(level = "debug", skip(self))] + fn register_obligation_in_env(&mut self, goal: InEnvironment) { + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { return }; + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + tracing::debug!(?result); + match result { + Ok((_, Certainty::Yes)) => {} + Err(rustc_type_ir::solve::NoSolution) => {} + Ok((_, Certainty::Maybe(_))) => { + self.pending_obligations.push(goal); + } } } @@ -812,28 +835,35 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_obligations_as_possible(&mut self) { let _span = tracing::info_span!("resolve_obligations_as_possible").entered(); let mut changed = true; - let mut obligations = mem::take(&mut self.resolve_obligations_buffer); while mem::take(&mut changed) { - mem::swap(&mut self.pending_obligations, &mut obligations); + let mut obligations = mem::take(&mut self.pending_obligations); - for canonicalized in obligations.drain(..) { - tracing::debug!(obligation = ?canonicalized); - if !self.check_changed(&canonicalized) { - tracing::debug!("not changed"); - self.pending_obligations.push(canonicalized); + for goal in obligations.drain(..) { + tracing::debug!(obligation = ?goal); + + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { + changed = true; continue; + }; + + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + let (has_changed, certainty) = match result { + Ok(result) => result, + Err(_) => { + continue; + } + }; + + if matches!(has_changed, HasChanged::Yes) { + changed = true; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.pending_obligations.push(goal), } - changed = true; - let uncanonical = chalk_ir::Substitute::apply( - &canonicalized.free_vars, - canonicalized.value.value, - Interner, - ); - self.register_obligation_in_env(uncanonical); } } - self.resolve_obligations_buffer = obligations; - self.resolve_obligations_buffer.clear(); } pub(crate) fn fudge_inference>( @@ -904,32 +934,13 @@ impl<'a> InferenceTable<'a> { .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) } - /// This checks whether any of the free variables in the `canonicalized` - /// have changed (either been unified with another variable, or with a - /// value). If this is not the case, we don't need to try to solve the goal - /// again -- it'll give the same result as last time. - fn check_changed(&mut self, canonicalized: &Canonicalized>) -> bool { - canonicalized.free_vars.iter().any(|var| { - let iv = match var.data(Interner) { - GenericArgData::Ty(ty) => ty.inference_var(Interner), - GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - GenericArgData::Const(c) => c.inference_var(Interner), - } - .expect("free var is not inference var"); - if self.var_unification_table.probe_var(iv).is_some() { - return true; - } - let root = self.var_unification_table.inference_var_root(iv); - iv != root - }) - } - #[tracing::instrument(level = "debug", skip(self))] fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized>, ) -> NextTraitSolveResult { - let solution = self.db.trait_solve( + let solution = next_trait_solve( + self.db, self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), @@ -1014,33 +1025,15 @@ impl<'a> InferenceTable<'a> { .fill_with_unknown() .build(); - let trait_env = self.trait_env.env.clone(); - let obligation = InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = self.canonicalize(obligation.clone()); - if !self - .db - .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .no_solution() - { - self.register_obligation(obligation.goal); + let goal: Goal = trait_ref.clone().cast(Interner); + if !self.try_obligation(goal.clone()).no_solution() { + self.register_obligation(goal); let return_ty = self.normalize_projection_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - let obligation: chalk_ir::InEnvironment> = - InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = self.canonicalize(obligation.clone()); - if !self - .db - .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .no_solution() - { + let goal = trait_ref.clone().cast(Interner); + if !self.try_obligation(goal).no_solution() { return Some((fn_x, arg_tys, return_ty)); } } @@ -1165,20 +1158,26 @@ impl<'a> InferenceTable<'a> { impl fmt::Debug for InferenceTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish() + f.debug_struct("InferenceTable").finish() } } mod resolve { use super::InferenceTable; use crate::{ - ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, - InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind, + next_solver::mapping::ChalkToNextSolver, ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind }; use chalk_ir::{ cast::Cast, fold::{TypeFoldable, TypeFolder}, }; + use rustc_type_ir::{FloatVid, IntVid, TyVid}; + + #[derive(Copy, Clone, PartialEq, Eq)] + pub(super) enum VarKind { + Ty(TyVariableKind), + Const, + } #[derive(chalk_derive::FallibleTypeFolder)] #[has_interner(Interner)] @@ -1188,7 +1187,7 @@ mod resolve { F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, > { pub(super) table: &'a mut InferenceTable<'b>, - pub(super) var_stack: &'a mut Vec, + pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>, pub(super) fallback: F, } impl TypeFolder for Resolver<'_, '_, F> @@ -1209,25 +1208,79 @@ mod resolve { kind: TyVariableKind, outer_binder: DebruijnIndex, ) -> Ty { - let var = self.table.var_unification_table.inference_var_root(var); - if self.var_stack.contains(&var) { - // recursive type - let default = self.table.fallback_value(var, kind).cast(Interner); - return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) - .assert_ty_ref(Interner) - .clone(); - } - if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { - // known_ty may contain other variables that are known by now - self.var_stack.push(var); - let result = known_ty.fold_with(self, outer_binder); - self.var_stack.pop(); - result.assert_ty_ref(Interner).clone() - } else { - let default = self.table.fallback_value(var, kind).cast(Interner); - (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) - .assert_ty_ref(Interner) - .clone() + match kind { + TyVariableKind::General => { + let vid = self.table.infer_ctxt.root_var(TyVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } + TyVariableKind::Integer => { + let vid = self.table.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } + TyVariableKind::Float => { + let vid = self.table.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } } } @@ -1237,25 +1290,27 @@ mod resolve { var: InferenceVar, outer_binder: DebruijnIndex, ) -> Const { - let var = self.table.var_unification_table.inference_var_root(var); + let vid = self.table.infer_ctxt.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); + let var = InferenceVar::from(vid.as_u32()); let default = ConstData { ty: ty.clone(), value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), } .intern(Interner) .cast(Interner); - if self.var_stack.contains(&var) { + if self.var_stack.contains(&(var, VarKind::Const)) { // recursive return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) .assert_const_ref(Interner) .clone(); } - if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { + if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) { + let known_const: Const = ChalkToNextSolver::from_nextsolver(known_const, self.table.interner); // known_ty may contain other variables that are known by now - self.var_stack.push(var); - let result = known_ty.fold_with(self, outer_binder); + self.var_stack.push((var, VarKind::Const)); + let result = known_const.fold_with(self, outer_binder); self.var_stack.pop(); - result.assert_const_ref(Interner).clone() + result } else { (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) .assert_const_ref(Interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c16bbb7b99221..323ea951a9141 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -957,23 +957,13 @@ pub fn callable_sig_from_fn_trait( ) .build(); - let block = trait_env.block; - let trait_env = trait_env.env.clone(); - let obligation = - InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; - let canonical = table.canonicalize(obligation.clone()); - if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { - table.register_obligation(obligation.goal); + if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { + table.register_obligation(trait_ref.clone().cast(Interner)); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - let obligation: chalk_ir::InEnvironment> = InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = table.canonicalize(obligation.clone()); - if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { + if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { let ret_ty = table.resolve_completely(return_ty); let args_ty = table.resolve_completely(args_ty); let params = args_ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index f0be352fdd151..ee223059d1e56 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -16,25 +16,13 @@ use hir_def::{ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{IntoKind, SliceLike}; +use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, - autoderef::{self, AutoderefKind}, - db::HirDatabase, - error_lifetime, from_chalk_trait_id, from_foreign_def_id, - infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, - lang_items::is_box, - next_solver::SolverDefId, - primitive::{FloatTy, IntTy, UintTy}, - to_chalk_trait_id, - traits::NextTraitSolveResult, - utils::all_super_traits, + autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, lang_items::is_box, next_solver::{mapping::ChalkToNextSolver, SolverDefId}, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, traits::{next_trait_solve_canonical}, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause }; /// This is used as a key for indexing impls. @@ -533,9 +521,9 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option, +pub(crate) fn lookup_method<'db>( + db: &'db dyn HirDatabase, + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -697,9 +685,9 @@ impl ReceiverAdjustments { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? -pub(crate) fn iterate_method_candidates( - ty: &Canonical, - db: &dyn HirDatabase, +pub(crate) fn iterate_method_candidates<'db, T>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1046,9 +1034,9 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { is_not_orphan } -pub fn iterate_path_candidates( - ty: &Canonical, - db: &dyn HirDatabase, +pub fn iterate_path_candidates<'db>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1068,9 +1056,9 @@ pub fn iterate_path_candidates( ) } -pub fn iterate_method_candidates_dyn( - ty: &Canonical, - db: &dyn HirDatabase, +pub fn iterate_method_candidates_dyn<'db>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1108,7 +1096,7 @@ pub fn iterate_method_candidates_dyn( // types*. let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical(ty.clone()); + let ty = table.instantiate_canonical_ns(ty.clone()); let deref_chain = autoderef_method_receiver(&mut table, ty); deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { @@ -1139,20 +1127,22 @@ pub fn iterate_method_candidates_dyn( } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_with_autoref( - table: &mut InferenceTable<'_>, - receiver_ty: Canonical, +fn iterate_method_candidates_with_autoref<'db>( + table: &mut InferenceTable<'db>, + receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, first_adjustment: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { + if matches!(receiver_ty.value.kind(), rustc_type_ir::TyKind::Bound(..)) { // don't try to resolve methods on unknown types return ControlFlow::Continue(()); } + let interner = table.interner; + let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { iterate_method_candidates_by_receiver( table, @@ -1166,7 +1156,11 @@ fn iterate_method_candidates_with_autoref( }; let mut maybe_reborrowed = first_adjustment.clone(); - if let Some((_, _, m)) = receiver_ty.value.as_reference() { + if let rustc_type_ir::TyKind::Ref(_, _, m) = receiver_ty.value.kind() { + let m = match m { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; // Prefer reborrow of references to move maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m)); maybe_reborrowed.autoderefs += 1; @@ -1174,10 +1168,10 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; - let refed = Canonical { - value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone()) - .intern(Interner), - binders: receiver_ty.binders.clone(), + let refed = crate::next_solver::Canonical { + max_universe: receiver_ty.max_universe, + variables: receiver_ty.variables, + value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Not), }; iterate_method_candidates_by_receiver( @@ -1185,10 +1179,10 @@ fn iterate_method_candidates_with_autoref( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)), )?; - let ref_muted = Canonical { - value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone()) - .intern(Interner), - binders: receiver_ty.binders.clone(), + let ref_muted = crate::next_solver::Canonical { + max_universe: receiver_ty.max_universe, + variables: receiver_ty.variables, + value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Mut), }; iterate_method_candidates_by_receiver( @@ -1196,10 +1190,11 @@ fn iterate_method_candidates_with_autoref( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)), )?; - if let Some((ty, Mutability::Mut)) = receiver_ty.value.as_raw_ptr() { - let const_ptr_ty = Canonical { - value: TyKind::Raw(Mutability::Not, ty.clone()).intern(Interner), - binders: receiver_ty.binders, + if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = receiver_ty.value.kind() { + let const_ptr_ty = rustc_type_ir::Canonical { + max_universe: rustc_type_ir::UniverseIndex::ZERO, + value: crate::next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), + variables: receiver_ty.variables, }; iterate_method_candidates_by_receiver( const_ptr_ty, @@ -1250,16 +1245,17 @@ where } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_by_receiver( - table: &mut InferenceTable<'_>, - receiver_ty: Canonical, +fn iterate_method_candidates_by_receiver<'db>( + table: &mut InferenceTable<'db>, + receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, receiver_adjustments: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - let receiver_ty = table.instantiate_canonical(receiver_ty); + let receiver_ty = table.instantiate_canonical_ns(receiver_ty); + let receiver_ty: crate::Ty = ChalkToNextSolver::from_nextsolver(receiver_ty, table.interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1307,9 +1303,9 @@ fn iterate_method_candidates_by_receiver( } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_for_self_ty( - self_ty: &Canonical, - db: &dyn HirDatabase, +fn iterate_method_candidates_for_self_ty<'db>( + self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1317,7 +1313,7 @@ fn iterate_method_candidates_for_self_ty( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); - let self_ty = table.instantiate_canonical(self_ty.clone()); + let self_ty = ChalkToNextSolver::from_nextsolver(table.instantiate_canonical_ns(self_ty.clone()), table.interner); iterate_inherent_methods( &self_ty, &mut table, @@ -1354,7 +1350,7 @@ fn iterate_trait_method_candidates( ) -> ControlFlow<()> { let db = table.db; - let canonical_self_ty = table.canonicalize(self_ty.clone()); + let canonical_self_ty = ChalkToNextSolver::from_nextsolver(table.canonicalize(self_ty.clone().to_nextsolver(table.interner)), table.interner); let TraitEnvironment { krate, block, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { @@ -1583,13 +1579,14 @@ pub(crate) fn resolve_indexing_op( ) -> Option { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let deref_chain = autoderef_method_receiver(&mut table, ty); + let interner = table.interner; + let deref_chain = autoderef_method_receiver(&mut table, ty.to_nextsolver(interner)); for (ty, adj) in deref_chain { - let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); - if !db - .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) - .no_solution() - { + //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty); + let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ChalkToNextSolver::from_nextsolver(ty, interner)); + let goal: chalk_ir::Canonical>> = goal.cast(Interner); + let goal = goal.to_nextsolver(interner); + if !next_trait_solve_canonical(db, table.trait_env.krate, table.trait_env.block, goal).no_solution() { return Some(adj); } } @@ -1773,26 +1770,11 @@ fn is_valid_impl_fn_candidate( }); for goal in goals.clone() { - let in_env = InEnvironment::new(&table.trait_env.env, goal); - let canonicalized = table.canonicalize_with_free_vars(in_env); - let solution = table.db.trait_solve( - table.trait_env.krate, - table.trait_env.block, - canonicalized.value.clone(), - ); - - match solution { - NextTraitSolveResult::Certain(canonical_subst) => { - canonicalized.apply_solution( - table, - Canonical { - binders: canonical_subst.binders, - value: canonical_subst.value.subst, - }, - ); + match table.solve_obligation(goal) { + Ok(_) => {} + Err(_) => { + return IsValidCandidate::No; } - NextTraitSolveResult::Uncertain(..) => {} - NextTraitSolveResult::NoSolution => return IsValidCandidate::No, } } @@ -1857,25 +1839,66 @@ fn generic_implements_goal( Canonical { binders, value } } -fn autoderef_method_receiver( - table: &mut InferenceTable<'_>, - ty: Ty, -) -> Vec<(Canonical, ReceiverAdjustments)> { - let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); +/* +/// This creates Substs for a trait with the given Self type and type variables +/// for all other parameters, to query the trait solver with it. +#[tracing::instrument(skip_all)] +fn generic_implements_goal_ns<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + env: &TraitEnvironment, + trait_: TraitId, + self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, +) -> crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { + let variables = self_ty.variables; + let trait_ref = TyBuilder::trait_ref(db, trait_) + .push(ChalkToNextSolver::from_nextsolver(self_ty.value, interner)) + .fill_with_bound_vars(DebruijnIndex::INNERMOST, variables.len()) + .build(); + + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let args = infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); + + rustc_type_ir::TraitRef::new(interner, SolverDefId::TraitId(trait_)).with_self_ty(interner, self_ty.value); + + + let kinds = + binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { + GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), + GenericArgData::Lifetime(_) => VariableKind::Lifetime, + GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()), + }; + WithKind::new(vk, UniverseIndex::ROOT) + })); + let binders = CanonicalVarKinds::from_iter(Interner, kinds); + + let obligation = trait_ref.cast(Interner); + let value = InEnvironment::new(&env.env, obligation); + crate::next_solver::Canonical { max_universe, value, variables } +} +*/ + +fn autoderef_method_receiver<'db>( + table: &mut InferenceTable<'db>, + ty: crate::next_solver::Ty<'db>, +) -> Vec<(crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { + let interner = table.interner; + let mut deref_chain = Vec::new(); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ChalkToNextSolver::from_nextsolver(ty, interner), false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty), + autoderef.table.canonicalize(ty.to_nextsolver(interner)), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) - if let Some((TyKind::Array(parameters, _), binders, adj)) = - deref_chain.last().map(|(ty, adj)| (ty.value.kind(Interner), ty.binders.clone(), adj)) + if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) = + deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables.clone(), d.0.max_universe, d.1.clone())) { - let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); + let unsized_ty = crate::next_solver::Ty::new_slice(interner, parameters); deref_chain.push(( - Canonical { value: unsized_ty, binders }, + crate::next_solver::Canonical { max_universe, value: unsized_ty, variables, }, ReceiverAdjustments { unsize_array: true, ..adj.clone() }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs new file mode 100644 index 0000000000000..0820006e5dfb0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -0,0 +1,785 @@ + +use rustc_hash::FxHashMap; +use rustc_index::Idx; +use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; +use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; +use rustc_type_ir::{BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex}; +use smallvec::SmallVec; +use tracing::debug; + +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::{Binder, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, TyKind}; + +/// When we canonicalize a value to form a query, we wind up replacing +/// various parts of it with canonical variables. This struct stores +/// those replaced bits to remember for when we process the query +/// result. +#[derive(Clone, Debug)] +pub struct OriginalQueryValues<'db> { + /// Map from the universes that appear in the query to the universes in the + /// caller context. For all queries except `evaluate_goal` (used by Chalk), + /// we only ever put ROOT values into the query, so this map is very + /// simple. + pub universe_map: SmallVec<[UniverseIndex; 4]>, + + /// This is equivalent to `CanonicalVarValues`, but using a + /// `SmallVec` yields a significant performance win. + pub var_values: SmallVec<[GenericArg<'db>; 8]>, +} + +impl<'db> Default for OriginalQueryValues<'db> { + fn default() -> Self { + let mut universe_map = SmallVec::default(); + universe_map.push(UniverseIndex::ROOT); + + Self { universe_map, var_values: SmallVec::default() } + } +} + +impl<'db> InferCtxt<'db> { + /// Canonicalizes a query value `V`. When we canonicalize a query, + /// we not only canonicalize unbound inference variables, but we + /// *also* replace all free regions whatsoever. So for example a + /// query like `T: Trait<'static>` would be canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query + pub fn canonicalize_query( + &self, + value: ParamEnvAnd<'db, V>, + query_state: &mut OriginalQueryValues<'db>, + ) -> CanonicalQueryInput, ParamEnvAnd<'db, V>> + where + V: TypeFoldable>, + { + let (param_env, value) = value.into_parts(); + // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the + // `param_env` because they are treated differently by trait selection. + let canonical_param_env = Canonicalizer::canonicalize( + param_env, + None, + self.interner, + &CanonicalizeFreeRegionsOtherThanStatic, + query_state, + ); + + let canonical = Canonicalizer::canonicalize_with_base( + canonical_param_env, + value, + Some(self), + self.interner, + &CanonicalizeAllFreeRegions, + query_state, + ) + .unchecked_map(|(param_env, value)| ParamEnvAnd { param_env, value }); + CanonicalQueryInput { canonical, typing_mode: self.typing_mode() } + } + + /// Canonicalizes a query *response* `V`. When we canonicalize a + /// query response, we only canonicalize unbound inference + /// variables, and we leave other free regions alone. So, + /// continuing with the example from `canonicalize_query`, if + /// there was an input query `T: Trait<'static>`, it would have + /// been canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. But if we found that there + /// exists only one possible impl of `Trait`, and it looks like + /// ```ignore (illustrative) + /// impl Trait<'static> for T { .. } + /// ``` + /// then we would prepare a query result R that (among other + /// things) includes a mapping to `'?0 := 'static`. When + /// canonicalizing this query result R, we would leave this + /// reference to `'static` alone. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result + pub fn canonicalize_response(&self, value: V) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.interner, + &CanonicalizeQueryResponse, + &mut query_state, + ) + } + + pub fn canonicalize_user_type_annotation(&self, value: V) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.interner, + &CanonicalizeUserTypeAnnotation, + &mut query_state, + ) + } +} + +/// Controls how we canonicalize "free regions" that are not inference +/// variables. This depends on what we are canonicalizing *for* -- +/// e.g., if we are canonicalizing to create a query, we want to +/// replace those with inference variables, since we want to make a +/// maximally general query. But if we are canonicalizing a *query +/// response*, then we don't typically replace free regions, as they +/// must have been introduced from other parts of the system. +trait CanonicalizeMode { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db>; + + fn any(&self) -> bool; + + // Do we preserve universe of variables. + fn preserve_universes(&self) -> bool; +} + +struct CanonicalizeQueryResponse; + +impl CanonicalizeMode for CanonicalizeQueryResponse { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + mut r: Region<'db>, + ) -> Region<'db> { + let infcx = canonicalizer.infcx.unwrap(); + + if let RegionKind::ReVar(vid) = r.kind() { + r = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(canonicalizer.tcx, vid); + debug!( + "canonical: region var found with vid {vid:?}, \ + opportunistically resolved to {r:?}", + ); + }; + + match r.kind() { + RegionKind::ReLateParam(_) | RegionKind::ReErased | RegionKind::ReStatic | RegionKind::ReEarlyParam(..) | RegionKind::ReError(..) => r, + + RegionKind::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( + CanonicalVarKind::PlaceholderRegion(placeholder), + r, + ), + + RegionKind::ReVar(vid) => { + let universe = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .probe_value(vid) + .unwrap_err(); + canonicalizer.canonical_var_for_region( + CanonicalVarKind::Region(universe), + r, + ) + } + + _ => { + // Other than `'static` or `'empty`, the query + // response should be executing in a fully + // canonicalized environment, so there shouldn't be + // any other region names it can come up. + // + // rust-lang/rust#57464: `impl Trait` can leak local + // scopes (in manner violating typeck). Therefore, use + // `delayed_bug` to allow type error over an ICE. + panic!("unexpected region in query response: `{r:?}`"); + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + true + } +} + +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + match r.kind() { + RegionKind::ReEarlyParam(_) + | RegionKind::ReLateParam(_) + | RegionKind::ReErased + | RegionKind::ReStatic + | RegionKind::ReError(_) => r, + RegionKind::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), + RegionKind::RePlaceholder(..) | RegionKind::ReBound(..) => { + // We only expect region names that the user can type. + panic!("unexpected region in query response: `{:?}`", r) + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegions; + +impl CanonicalizeMode for CanonicalizeAllFreeRegions { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + canonicalizer.canonical_var_for_region_in_root_universe(r) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeFreeRegionsOtherThanStatic; + +impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) } + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct Canonicalizer<'cx, 'db> { + /// Set to `None` to disable the resolution of inference variables. + infcx: Option<&'cx InferCtxt<'db>>, + tcx: DbInterner<'db>, + variables: SmallVec<[CanonicalVarKind<'db>; 8]>, + query_state: &'cx mut OriginalQueryValues<'db>, + // Note that indices is only used once `var_values` is big enough to be + // heap-allocated. + indices: FxHashMap, BoundVar>, + canonicalize_mode: &'cx dyn CanonicalizeMode, + needs_canonical_flags: TypeFlags, + + binder_index: DebruijnIndex, +} + +impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.tcx + } + + fn fold_binder(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + T: TypeFoldable>, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + RegionKind::ReBound(index, ..) => { + if index >= self.binder_index { + panic!("escaping late-bound region during canonicalization"); + } else { + r + } + } + + RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) + | RegionKind::ReLateParam(_) + | RegionKind::RePlaceholder(..) + | RegionKind::ReVar(_) + | RegionKind::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), + } + } + + fn fold_ty(&mut self, mut t: Ty<'db>) -> Ty<'db> { + match t.kind() { + TyKind::Infer(TyVar(mut vid)) => { + // We need to canonicalize the *root* of our ty var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.unwrap().root_var(vid); + if root_vid != vid { + t = Ty::new_var(self.tcx, root_vid); + vid = root_vid; + } + + debug!("canonical: type var found with vid {:?}", vid); + match self.infcx.unwrap().probe_ty_var(vid) { + // `t` could be a float / int variable; canonicalize that instead. + Ok(t) => { + debug!("(resolved to {:?})", t); + self.fold_ty(t) + } + + // `TyVar(vid)` is unresolved, track its universe index in the canonicalized + // result. + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), + t, + ) + } + } + } + + TyKind::Infer(IntVar(vid)) => { + let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::Int), + t, + ) + } + } + TyKind::Infer(FloatVar(vid)) => { + let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::Float), + t, + ) + } + } + + TyKind::Infer(InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => { + panic!("encountered a fresh type during canonicalization") + } + + TyKind::Placeholder(mut placeholder) => { + if !self.canonicalize_mode.preserve_universes() { + placeholder.universe = UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarKind::PlaceholderTy(placeholder), + t, + ) + } + + TyKind::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + panic!("escaping bound type during canonicalization") + } else { + t + } + } + + TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Bool + | TyKind::Char + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) + | TyKind::Adt(..) + | TyKind::Str + | TyKind::Error(_) + | TyKind::Array(..) + | TyKind::Slice(..) + | TyKind::RawPtr(..) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::Dynamic(..) + | TyKind::UnsafeBinder(_) + | TyKind::Never + | TyKind::Tuple(..) + | TyKind::Alias(..) + | TyKind::Foreign(..) + | TyKind::Pat(..) + | TyKind::Param(..) => { + if t.flags().intersects(self.needs_canonical_flags) { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_const(&mut self, mut ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Infer(InferConst::Var(mut vid)) => { + // We need to canonicalize the *root* of our const var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.unwrap().root_const_var(vid); + if root_vid != vid { + ct = Const::new_var(self.tcx, root_vid); + vid = root_vid; + } + + debug!("canonical: const var found with vid {:?}", vid); + match self.infcx.unwrap().probe_const_var(vid) { + Ok(c) => { + debug!("(resolved to {:?})", c); + return self.fold_const(c); + } + + // `ConstVar(vid)` is unresolved, track its universe index in the + // canonicalized result + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = UniverseIndex::ROOT; + } + return self.canonicalize_const_var( + CanonicalVarKind::Const(ui), + ct, + ); + } + } + } + ConstKind::Infer(InferConst::Fresh(_)) => { + panic!("encountered a fresh const during canonicalization") + } + ConstKind::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + panic!("escaping bound const during canonicalization") + } else { + return ct; + } + } + ConstKind::Placeholder(placeholder) => { + return self.canonicalize_const_var( + CanonicalVarKind::PlaceholderConst(placeholder), + ct, + ); + } + _ => {} + } + + if ct.flags().intersects(self.needs_canonical_flags) { + ct.super_fold_with(self) + } else { + ct + } + } +} + +impl<'cx, 'db> Canonicalizer<'cx, 'db> { + /// The main `canonicalize` method, shared impl of + /// `canonicalize_query` and `canonicalize_response`. + fn canonicalize( + value: V, + infcx: Option<&InferCtxt<'db>>, + tcx: DbInterner<'db>, + canonicalize_region_mode: &dyn CanonicalizeMode, + query_state: &mut OriginalQueryValues<'db>, + ) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let base = Canonical { + max_universe: UniverseIndex::ROOT, + variables: CanonicalVars::new_from_iter(tcx, []), + value: (), + }; + Canonicalizer::canonicalize_with_base( + base, + value, + infcx, + tcx, + canonicalize_region_mode, + query_state, + ) + .unchecked_map(|((), val)| val) + } + + fn canonicalize_with_base( + base: Canonical<'db, U>, + value: V, + infcx: Option<&InferCtxt<'db>>, + tcx: DbInterner<'db>, + canonicalize_region_mode: &dyn CanonicalizeMode, + query_state: &mut OriginalQueryValues<'db>, + ) -> Canonical<'db, (U, V)> + where + V: TypeFoldable>, + { + let needs_canonical_flags = if canonicalize_region_mode.any() { + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS + } else { + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER + }; + + // Fast path: nothing that needs to be canonicalized. + if !value.has_type_flags(needs_canonical_flags) { + return base.unchecked_map(|b| (b, value)); + } + + let mut canonicalizer = Canonicalizer { + infcx, + tcx, + canonicalize_mode: canonicalize_region_mode, + needs_canonical_flags, + variables: SmallVec::from_slice(base.variables.as_slice()), + query_state, + indices: FxHashMap::default(), + binder_index: DebruijnIndex::ZERO, + }; + if canonicalizer.query_state.var_values.spilled() { + canonicalizer.indices = canonicalizer + .query_state + .var_values + .iter() + .enumerate() + .map(|(i, &kind)| (kind, BoundVar::from(i))) + .collect(); + } + let out_value = value.fold_with(&mut canonicalizer); + + // Once we have canonicalized `out_value`, it should not + // contain anything that ties it to this inference context + // anymore. + debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); + + let canonical_variables = CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); + + let max_universe = canonical_variables + .iter() + .map(|cvar| cvar.universe()) + .max() + .unwrap_or(UniverseIndex::ROOT); + + Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + } + + /// Creates a canonical variable replacing `kind` from the input, + /// or returns an existing variable if `kind` has already been + /// seen. `kind` is expected to be an unbound variable (or + /// potentially a free region). + fn canonical_var(&mut self, info: CanonicalVarKind<'db>, kind: GenericArg<'db>) -> BoundVar { + let Canonicalizer { variables, query_state, indices, .. } = self; + + let var_values = &mut query_state.var_values; + + let universe = info.universe(); + if universe != UniverseIndex::ROOT { + assert!(self.canonicalize_mode.preserve_universes()); + + // Insert universe into the universe map. To preserve the order of the + // universes in the value being canonicalized, we don't update the + // universe in `info` until we have finished canonicalizing. + match query_state.universe_map.binary_search(&universe) { + Err(idx) => query_state.universe_map.insert(idx, universe), + Ok(_) => {} + } + } + + // This code is hot. `variables` and `var_values` are usually small + // (fewer than 8 elements ~95% of the time). They are SmallVec's to + // avoid allocations in those cases. We also don't use `indices` to + // determine if a kind has been seen before until the limit of 8 has + // been exceeded, to also avoid allocations for `indices`. + if !var_values.spilled() { + // `var_values` is stack-allocated. `indices` isn't used yet. Do a + // direct linear search of `var_values`. + if let Some(idx) = var_values.iter().position(|&k| k == kind) { + // `kind` is already present in `var_values`. + BoundVar::new(idx) + } else { + // `kind` isn't present in `var_values`. Append it. Likewise + // for `info` and `variables`. + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + + // If `var_values` has become big enough to be heap-allocated, + // fill up `indices` to facilitate subsequent lookups. + if var_values.spilled() { + assert!(indices.is_empty()); + *indices = var_values + .iter() + .enumerate() + .map(|(i, &kind)| (kind, BoundVar::new(i))) + .collect(); + } + // The cv is the index of the appended element. + BoundVar::new(var_values.len() - 1) + } + } else { + // `var_values` is large. Do a hashmap search via `indices`. + *indices.entry(kind).or_insert_with(|| { + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + BoundVar::new(variables.len() - 1) + }) + } + } + + /// Replaces the universe indexes used in `var_values` with their index in + /// `query_state.universe_map`. This minimizes the maximum universe used in + /// the canonicalized value. + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'db>; 8]> { + if self.query_state.universe_map.len() == 1 { + return self.variables; + } + + let reverse_universe_map: FxHashMap = self + .query_state + .universe_map + .iter() + .enumerate() + .map(|(idx, universe)| (*universe, UniverseIndex::from_usize(idx))) + .collect(); + + self.variables + .iter() + .map(|v| match *v { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return *v; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => { + CanonicalVarKind::Region(reverse_universe_map[&u]) + } + CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + }) + .collect() + } + + /// Shorthand helper that creates a canonical region variable for + /// `r` (always in the root universe). The reason that we always + /// put these variables into the root universe is because this + /// method is used during **query construction:** in that case, we + /// are taking all the regions and just putting them into the most + /// generic context we can. This may generate solutions that don't + /// fit (e.g., that equate some region variable with a placeholder + /// it can't name) on the caller side, but that's ok, the caller + /// can figure that out. In the meantime, it maximizes our + /// caching. + /// + /// (This works because unification never fails -- and hence trait + /// selection is never affected -- due to a universe mismatch.) + fn canonical_var_for_region_in_root_universe( + &mut self, + r: Region<'db>, + ) -> Region<'db> { + self.canonical_var_for_region( + CanonicalVarKind::Region(UniverseIndex::ROOT), + r, + ) + } + + /// Creates a canonical variable (with the given `info`) + /// representing the region `r`; return a region referencing it. + fn canonical_var_for_region( + &mut self, + info: CanonicalVarKind<'db>, + r: Region<'db>, + ) -> Region<'db> { + let var = self.canonical_var(info, r.into()); + let br = BoundRegion { var, kind: BoundRegionKind::Anon }; + Region::new_bound(self.cx(), self.binder_index, br) + } + + /// Given a type variable `ty_var` of the given kind, first check + /// if `ty_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `ty_var`. + fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> { + debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); + let var = self.canonical_var(info, ty_var.into()); + Ty::new_bound(self.tcx, self.binder_index, BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }) + } + + /// Given a type variable `const_var` of the given kind, first check + /// if `const_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `const_var`. + fn canonicalize_const_var( + &mut self, + info: CanonicalVarKind<'db>, + const_var: Const<'db>, + ) -> Const<'db> { + debug_assert!( + !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) + ); + let var = self.canonical_var(info, const_var.into()); + Const::new_bound(self.tcx, self.binder_index, var) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 4457e1340d584..ec41111ed8153 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -42,6 +42,7 @@ use rustc_type_ir::{ }, }; +pub mod canonicalizer; pub mod instantiate; impl<'db> InferCtxt<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 585719144ef42..894c91e0f49db 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -190,12 +190,13 @@ impl<'db> InferCtxtInner<'db> { } #[inline] - fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + pub(crate) fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + tracing::debug!(?self.int_unification_storage); self.int_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { + pub(crate) fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { self.float_unification_storage.with_log(&mut self.undo_log) } @@ -213,6 +214,7 @@ impl<'db> InferCtxtInner<'db> { } } +#[derive(Clone)] pub struct InferCtxt<'db> { pub interner: DbInterner<'db>, @@ -500,6 +502,10 @@ impl<'db> InferCtxt<'db> { self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) } + pub fn next_ty_vid(&self) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + } + pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); Ty::new_var(self.interner, vid) @@ -519,6 +525,15 @@ impl<'db> InferCtxt<'db> { self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) } + pub fn next_const_vid(&self) -> ConstVid { + self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin: ConstVariableOrigin { param_def_id: None }, universe: self.universe() }) + .vid + } + pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { let vid = self .inner @@ -546,12 +561,20 @@ impl<'db> InferCtxt<'db> { Ty::new_int_var(self.interner, next_int_var_id) } + pub fn next_int_vid(&self) -> IntVid { + self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown) + } + pub fn next_float_var(&self) -> Ty<'db> { let next_float_var_id = self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); Ty::new_float_var(self.interner, next_float_var_id) } + pub fn next_float_vid(&self) -> FloatVid { + self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown) + } + /// Creates a fresh region variable with the next available index. /// The variable will be created in the maximum universe created /// thus far, allowing it to name any region created thus far. @@ -559,6 +582,10 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(self.universe()) } + pub fn next_region_vid(&self) -> RegionVid { + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe()) + } + /// Creates a fresh region variable with the next available index /// in the given universe; typically, you can use /// `next_region_var` and just use the maximal universe. @@ -782,6 +809,18 @@ impl<'db> InferCtxt<'db> { } } + pub fn resolve_int_var(&self, vid: IntVid) -> Option> { + let mut inner = self.inner.borrow_mut(); + let value = inner.int_unification_table().probe_value(vid); + match value { + IntVarValue::IntType(ty) => Some(Ty::new_int(self.interner, ty)), + IntVarValue::UintType(ty) => Some(Ty::new_uint(self.interner, ty)), + IntVarValue::Unknown => { + None + } + } + } + /// Resolves a float var to a rigid int type, if it was constrained to one, /// or else the root float var in the unification table. pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { @@ -795,6 +834,17 @@ impl<'db> InferCtxt<'db> { } } + pub fn resolve_float_var(&self, vid: FloatVid) -> Option> { + let mut inner = self.inner.borrow_mut(); + let value = inner.float_unification_table().probe_value(vid); + match value { + FloatVarValue::Known(ty) => Some(Ty::new_float(self.interner, ty)), + FloatVarValue::Unknown => { + None + } + } + } + /// Where possible, replaces type/const variables in /// `value` with their final value. Note that region variables /// are unaffected. If a type/const variable has not been unified, it diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index eb426205575ad..8c7dfb25e07f9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -46,7 +46,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::>::num_open_snapshots(&self.inner.borrow_mut().undo_log) } - fn start_snapshot(&self) -> CombinedSnapshot { + pub(crate) fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); let mut inner = self.inner.borrow_mut(); @@ -59,7 +59,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - fn rollback_to(&self, snapshot: CombinedSnapshot) { + pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; self.universe.set(universe); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index dc54ee7d58785..79c402ff29c3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -2,8 +2,8 @@ use base_db::Crate; use chalk_ir::{ - CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind, - WellFormed, fold::Shift, interner::HasInterner, + CanonicalVarKind, CanonicalVarKinds, FnPointer, InferenceVar, Substitution, TyVariableKind, + WellFormed, cast::Cast, fold::Shift, interner::HasInterner, }; use hir_def::{ CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, @@ -24,12 +24,12 @@ use salsa::{Id, plumbing::AsId}; use crate::next_solver::BoundConst; use crate::{ - ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, + ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ HirDatabase, InternedClosureId, InternedCoroutineId, InternedLifetimeParamId, InternedOpaqueTyId, InternedTypeOrConstParamId, }, - from_assoc_type_id, from_chalk_trait_id, + from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, mapping::ToChalk, next_solver::{ Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, @@ -141,6 +141,7 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db pub trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; + fn from_nextsolver(out: Out, interner: DbInterner<'db>) -> Self; } impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { @@ -427,6 +428,343 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }, ) } + + fn from_nextsolver(out: Ty<'db>, interner: DbInterner<'db>) -> Self { + use crate::{Scalar, TyKind}; + use chalk_ir::{FloatTy, IntTy, UintTy}; + match out.kind() { + rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), + rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { + TyKind::Scalar(Scalar::Int(IntTy::I8)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { + TyKind::Scalar(Scalar::Int(IntTy::I16)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { + TyKind::Scalar(Scalar::Int(IntTy::I32)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { + TyKind::Scalar(Scalar::Int(IntTy::I64)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { + TyKind::Scalar(Scalar::Int(IntTy::I128)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { + TyKind::Scalar(Scalar::Int(IntTy::Isize)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { + TyKind::Scalar(Scalar::Uint(UintTy::U8)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { + TyKind::Scalar(Scalar::Uint(UintTy::U16)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { + TyKind::Scalar(Scalar::Uint(UintTy::U32)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { + TyKind::Scalar(Scalar::Uint(UintTy::U64)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { + TyKind::Scalar(Scalar::Uint(UintTy::U128)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { + TyKind::Scalar(Scalar::Uint(UintTy::Usize)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { + TyKind::Scalar(Scalar::Float(FloatTy::F16)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { + TyKind::Scalar(Scalar::Float(FloatTy::F32)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { + TyKind::Scalar(Scalar::Float(FloatTy::F64)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { + TyKind::Scalar(Scalar::Float(FloatTy::F128)) + } + rustc_type_ir::TyKind::Str => TyKind::Str, + rustc_type_ir::TyKind::Error(_) => TyKind::Error, + rustc_type_ir::TyKind::Never => TyKind::Never, + + rustc_type_ir::TyKind::Adt(def, args) => { + let adt_id = def.inner().id; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Adt(chalk_ir::AdtId(adt_id), subst) + } + + rustc_type_ir::TyKind::Infer(infer_ty) => { + let (var, kind) = match infer_ty { + rustc_type_ir::InferTy::TyVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::General) + } + rustc_type_ir::InferTy::IntVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) + } + rustc_type_ir::InferTy::FloatVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Float) + } + _ => todo!(), + }; + TyKind::InferenceVar(var, kind) + } + + rustc_type_ir::TyKind::Ref(r, ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let r = ChalkToNextSolver::from_nextsolver(r, interner); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Ref(mutability, r, ty) + } + + rustc_type_ir::TyKind::Tuple(tys) => { + let size = tys.len(); + let subst = Substitution::from_iter( + Interner, + tys.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver( + ty, interner, + )) + .intern(Interner) + }), + ); + TyKind::Tuple(size, subst) + } + + rustc_type_ir::TyKind::Array(ty, const_) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); + TyKind::Array(ty, const_) + } + + rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { + rustc_type_ir::AliasTyKind::Projection => { + let assoc_ty_id = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let associated_ty_id = to_assoc_type_id(assoc_ty_id); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + TyKind::AssociatedType(associated_ty_id, substitution) + } + rustc_type_ir::AliasTyKind::Opaque => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: opaque_ty_id.into(), + substitution, + })) + } + rustc_type_ir::AliasTyKind::Inherent => todo!(), + rustc_type_ir::AliasTyKind::Free => todo!(), + }, + + rustc_type_ir::TyKind::Placeholder(placeholder) => { + let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; + let placeholder_index = + chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; + TyKind::Placeholder(placeholder_index) + } + + rustc_type_ir::TyKind::Bound(debruijn_index, ty) => { + TyKind::BoundVar(chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }) + } + + rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { + let num_binders = bound_sig.bound_vars().len(); + let sig = chalk_ir::FnSig { + abi: fn_header.abi, + safety: match fn_header.safety { + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: fn_header.c_variadic, + }; + let args = GenericArgs::new_from_iter( + interner, + bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), + ); + let substitution = ChalkToNextSolver::from_nextsolver(args, interner); + let substitution = chalk_ir::FnSubst(substitution); + let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; + TyKind::Function(fnptr) + } + + rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { + assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + let self_ty = Ty::new_bound( + interner, + DebruijnIndex::from_u32(1), + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, + ); + let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( + Interner, + preds.iter().map(|p| { + let binders = chalk_ir::VariableKinds::from_iter( + Interner, + p.bound_vars().iter().map(|b| match b { + BoundVarKind::Ty(kind) => { + chalk_ir::VariableKind::Ty(TyVariableKind::General) + } + BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => chalk_ir::VariableKind::Const( + crate::TyKind::Error.intern(Interner), + ), + }), + ); + let where_clause = match p.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = TraitRef::new( + interner, + trait_ref.def_id, + [self_ty.clone().into()] + .into_iter() + .chain(trait_ref.args.iter()), + ); + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { + let trait_id = match trait_ { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = chalk_ir::Substitution::empty(Interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::Projection( + existential_projection, + ) => { + let projection = ProjectionPredicate { + projection_term: AliasTerm::new( + interner, + existential_projection.def_id, + [self_ty.clone().into()] + .iter() + .chain(existential_projection.args.clone().iter()), + ), + term: existential_projection.term.clone(), + }; + let associated_ty_id = match projection.projection_term.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + projection.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match projection.term { + Term::Ty(ty) => ty, + _ => unreachable!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::WhereClause::AliasEq(alias_eq) + } + }; + chalk_ir::Binders::new(binders, where_clause) + }), + ); + let binders = chalk_ir::VariableKinds::from1( + Interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let bounds = chalk_ir::Binders::new(binders, bounds); + let dyn_ty = chalk_ir::DynTy { + bounds, + lifetime: ChalkToNextSolver::from_nextsolver(region, interner), + }; + TyKind::Dyn(dyn_ty) + } + + rustc_type_ir::TyKind::Slice(ty) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Slice(ty) + } + + rustc_type_ir::TyKind::Foreign(def_id) => { + let id = match def_id { + SolverDefId::TypeAliasId(id) => to_foreign_def_id(id), + _ => unreachable!(), + }; + TyKind::Foreign(id) + } + rustc_type_ir::TyKind::Pat(_, _) => todo!(), + rustc_type_ir::TyKind::RawPtr(ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Raw(mutability, ty) + } + rustc_type_ir::TyKind::FnDef(def_id, args) => { + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + match def_id { + SolverDefId::FunctionId(id) => { + TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Enum(e)) => TyKind::FnDef( + CallableDefId::EnumVariantId(e).to_chalk(interner.db()), + subst, + ), + SolverDefId::Ctor(Ctor::Struct(s)) => { + TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) + } + _ => unreachable!("Unexpected def id {:?}", def_id), + } + } + + rustc_type_ir::TyKind::Closure(def_id, args) => { + let id = match def_id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Closure(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), + rustc_type_ir::TyKind::Coroutine(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Coroutine(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::CoroutineWitness(id.into(), subst) + } + + rustc_type_ir::TyKind::Param(_) => todo!(), + rustc_type_ir::TyKind::UnsafeBinder(_) => todo!(), + } + .intern(Interner) + } } impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { @@ -461,6 +799,40 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { }, ) } + + fn from_nextsolver(out: Region<'db>, interner: DbInterner<'db>) -> Self { + match out.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => todo!(), + rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.as_usize(), + )), + ), + rustc_type_ir::RegionKind::ReLateParam(_) => todo!(), + rustc_type_ir::RegionKind::ReStatic => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + } + rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), + ), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + idx: placeholder.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, + }), + ), + rustc_type_ir::RegionKind::ReErased => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) + } + rustc_type_ir::RegionKind::ReError(_) => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + } + } + } } impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { @@ -505,6 +877,62 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { }, ) } + + fn from_nextsolver(out: Const<'db>, interner: DbInterner<'db>) -> Self { + let value: chalk_ir::ConstValue = match out.kind() { + rustc_type_ir::ConstKind::Param(_) => unimplemented!(), + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { + chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) + } + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + panic!("Vars should not be freshened.") + } + rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + var.index(), + )) + } + rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, + idx: placeholder_const.bound.as_usize(), + }) + } + rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { + let id = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::UnevaluatedConst(id, subst), + }) + } + rustc_type_ir::ConstKind::Value(value_const) => { + let bytes = value_const.value.inner(); + let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + // SAFETY: will never use this without a db + interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { + std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) + }), + }); + return chalk_ir::ConstData { + ty: ChalkToNextSolver::from_nextsolver(value_const.ty, interner), + value, + } + .intern(Interner); + } + rustc_type_ir::ConstKind::Error(_) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) + } + rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), + }; + chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> @@ -518,6 +946,13 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> ), } } + + fn from_nextsolver( + out: rustc_type_ir::FnSigTys>, + interner: DbInterner<'db>, + ) -> Self { + todo!() + } } impl< @@ -536,6 +971,13 @@ impl< binders.to_nextsolver(interner), ) } + + fn from_nextsolver( + out: rustc_type_ir::Binder, U>, + interner: DbInterner<'db>, + ) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { @@ -545,6 +987,10 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { @@ -555,6 +1001,10 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind BoundVarKind::Const, } } + + fn from_nextsolver(out: BoundVarKind, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { @@ -565,6 +1015,10 @@ impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg const_.to_nextsolver(interner).into(), } } + + fn from_nextsolver(out: GenericArg<'db>, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { @@ -573,6 +1027,31 @@ impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution GenericArg<'db> { arg.to_nextsolver(interner) }), ) } + + fn from_nextsolver(out: GenericArgs<'db>, interner: DbInterner<'db>) -> Self { + let mut substs = Vec::with_capacity(out.len()); + for arg in out.iter() { + match arg.clone().kind() { + rustc_type_ir::GenericArgKind::Type(ty) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Lifetime(region) => { + let lifetime = ChalkToNextSolver::from_nextsolver(region, interner); + substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Const(const_) => { + substs.push( + chalk_ir::GenericArgData::Const(ChalkToNextSolver::from_nextsolver( + const_, interner, + )) + .intern(Interner), + ); + } + } + } + Substitution::from_iter(Interner, substs) + } } impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution { @@ -588,18 +1067,30 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution }), ) } + + fn from_nextsolver(out: Tys<'db>, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) } + + fn from_nextsolver(out: rustc_type_ir::DebruijnIndex, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) } + + fn from_nextsolver(out: rustc_type_ir::UniverseIndex, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> @@ -618,6 +1109,10 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> } } } + + fn from_nextsolver(out: rustc_type_ir::InferTy, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { @@ -627,6 +1122,10 @@ impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutabil chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, } } + + fn from_nextsolver(out: rustc_ast_ir::Mutability, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { @@ -638,16 +1137,65 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, } } + + fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { + todo!() + } } -impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate<'db>>>> - for chalk_ir::Canonical>> +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { + match self { + chalk_ir::Variance::Covariant => rustc_type_ir::Variance::Covariant, + chalk_ir::Variance::Invariant => rustc_type_ir::Variance::Invariant, + chalk_ir::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, + } + } + + fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { + todo!() + } +} + +impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> VariancesOf { + VariancesOf::new_from_iter( + interner, + self.as_slice(Interner).iter().map(|v| v.to_nextsolver(interner)), + ) + } + + fn from_nextsolver(out: VariancesOf, interner: DbInterner<'db>) -> Self { + todo!() + } +} + +impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> + for chalk_ir::InEnvironment> { - fn to_nextsolver( - &self, + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Goal, Predicate<'db>> { + Goal::new( + interner, + self.environment.to_nextsolver(interner), + self.goal.to_nextsolver(interner), + ) + } + + fn from_nextsolver( + out: Goal, Predicate<'db>>, interner: DbInterner<'db>, - ) -> Canonical<'db, Goal, Predicate<'db>>> { - let param_env = self.value.environment.to_nextsolver(interner); + ) -> Self { + chalk_ir::InEnvironment { + environment: ChalkToNextSolver::from_nextsolver(out.param_env, interner), + goal: ChalkToNextSolver::from_nextsolver(out.predicate, interner), + } + } +} + +impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> + ChalkToNextSolver<'db, Canonical<'db, U>> for chalk_ir::Canonical +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Canonical<'db, U> { let variables = CanonicalVars::new_from_iter( interner, self.binders.iter(Interner).map(|k| match &k.kind { @@ -672,10 +1220,49 @@ impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate< ); Canonical { max_universe: UniverseIndex::ROOT, - value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)), + value: self.value.to_nextsolver(interner), variables, } } + + fn from_nextsolver(out: Canonical<'db, U>, interner: DbInterner<'db>) -> Self { + let binders = chalk_ir::CanonicalVarKinds::from_iter( + Interner, + out.variables.iter().map(|v| match v { + rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(ui), + ) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::root(), + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::root(), + ) + } + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => todo!(), + rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => todo!(), + rustc_type_ir::CanonicalVarKind::Const(ui) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => todo!(), + }), + ); + let value = ChalkToNextSolver::from_nextsolver(out.value, interner); + chalk_ir::Canonical { binders, value } + } } impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { @@ -693,7 +1280,16 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { } chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), - chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::EqGoal(eq_goal) => { + let pred_kind = PredicateKind::AliasRelate( + Term::Ty(eq_goal.a.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + Term::Ty(eq_goal.b.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + rustc_type_ir::AliasRelationDirection::Equate, + ); + let pred_kind = + Binder::bind_with_vars(pred_kind, BoundVarKinds::new_from_iter(interner, [])); + Predicate::new(interner, pred_kind) + } chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { let subtype_predicate = SubtypePredicate { a: subtype_goal.a.to_nextsolver(interner), @@ -718,6 +1314,124 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), } } + + fn from_nextsolver(out: Predicate<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::Goal::new( + Interner, + match out.kind().skip_binder() { + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( + trait_pred, + )) => { + let trait_ref = + ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( + proj_predicate, + )) => { + let associated_ty_id = match proj_predicate.def_id() { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + proj_predicate.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match proj_predicate.term.kind() { + rustc_type_ir::TermKind::Ty(ty) => ty, + rustc_type_ir::TermKind::Const(_) => todo!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( + outlives, + )) => { + let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let where_clause = + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + lifetime, + ty, + }); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(outlives), + ) => { + let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let where_clause = + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a, + b, + }); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(_) => todo!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), + rustc_type_ir::PredicateKind::Ambiguous => todo!(), + rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), + rustc_type_ir::PredicateKind::AliasRelate( + alias_term, + ty_term, + alias_relation_direction, + ) => { + let ty = match ty_term { + Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), + Term::Const(..) => todo!(), + }; + match alias_term { + Term::Ty(alias_ty) => match alias_ty.kind() { + rustc_type_ir::TyKind::Alias(kind, alias) => match kind { + rustc_type_ir::AliasTyKind::Projection => { + let associated_ty_id = match alias.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => todo!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(alias.args, interner); + let proj_ty = + chalk_ir::ProjectionTy { associated_ty_id, substitution }; + let alias = chalk_ir::AliasTy::Projection(proj_ty); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(alias_eq), + )) + } + _ => todo!(), + }, + rustc_type_ir::TyKind::Infer(var) => { + assert!(matches!( + alias_relation_direction, + rustc_type_ir::AliasRelationDirection::Equate + )); + let a: chalk_ir::Ty<_> = + ChalkToNextSolver::from_nextsolver(alias_ty, interner); + let eq_goal = chalk_ir::EqGoal { + a: chalk_ir::GenericArgData::Ty(a).intern(Interner), + b: chalk_ir::GenericArgData::Ty(ty).intern(Interner), + }; + chalk_ir::GoalData::EqGoal(eq_goal) + } + _ => todo!("Unexpected alias term {:?}", alias_term), + }, + Term::Const(..) => todo!(), + } + } + }, + ) + } } impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { @@ -730,12 +1444,32 @@ impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment, interner: DbInterner<'db>) -> Self { + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + out.clauses.iter().map(|c| -> chalk_ir::ProgramClause { + ChalkToNextSolver::from_nextsolver(c, interner) + }), + ); + chalk_ir::Environment { clauses } + } } impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) } + + fn from_nextsolver(out: Clause<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(chalk_ir::Binders::empty( + Interner, + ChalkToNextSolver::from_nextsolver(out.0.kind().skip_binder(), interner), + )), + ) + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> @@ -746,6 +1480,14 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> assert!(self.constraints.is_empty(Interner)); self.consequence.to_nextsolver(interner) } + fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::ProgramClauseImplication { + consequence: ChalkToNextSolver::from_nextsolver(out, interner), + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + } + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal { @@ -864,6 +1606,98 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal panic!("Should not be constructed."), } } + + fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { + let domain_goal = match out { + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(trait_pred)) => { + let trait_ref = ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( + proj_predicate, + )) => { + let associated_ty_id = match proj_predicate.def_id() { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + proj_predicate.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match proj_predicate.term.kind() { + rustc_type_ir::TermKind::Ty(ty) => ty, + rustc_type_ir::TermKind::Const(_) => todo!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( + outlives, + )) => { + let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let where_clause = + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { lifetime, ty }); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::RegionOutlives( + outlives, + )) => { + let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let where_clause = + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a, b }); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(_) => todo!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), + rustc_type_ir::PredicateKind::Ambiguous => todo!(), + rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), + rustc_type_ir::PredicateKind::AliasRelate( + alias_term, + ty_term, + alias_relation_direction, + ) => { + let alias = match alias_term { + Term::Ty(alias_ty) => match alias_ty.kind() { + rustc_type_ir::TyKind::Alias(kind, alias) => match kind { + rustc_type_ir::AliasTyKind::Projection => { + let associated_ty_id = match alias.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => todo!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(alias.args, interner); + let proj_ty = + chalk_ir::ProjectionTy { associated_ty_id, substitution }; + chalk_ir::AliasTy::Projection(proj_ty) + } + _ => todo!(), + }, + _ => todo!(), + }, + Term::Const(..) => todo!(), + }; + let ty = match ty_term { + Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), + Term::Const(..) => todo!(), + }; + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::AliasEq(alias_eq)) + } + }; + domain_goal + } } impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { @@ -871,6 +1705,15 @@ impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef let args = self.substitution.to_nextsolver(interner); TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } + + fn from_nextsolver(out: TraitRef<'db>, interner: DbInterner<'db>) -> Self { + let trait_id = match out.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver(out.args, interner); + chalk_ir::TraitRef { trait_id, substitution } + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause { @@ -911,6 +1754,10 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause, interner: DbInterner<'db>) -> Self { + todo!() + } } pub fn convert_canonical_args_for_result<'db>( @@ -956,7 +1803,10 @@ pub fn convert_canonical_args_for_result<'db>( binders, value: chalk_ir::ConstrainedSubst { constraints: chalk_ir::Constraints::empty(Interner), - subst: convert_args_for_result(interner, &value), + subst: ChalkToNextSolver::from_nextsolver( + GenericArgs::new_from_iter(interner, value), + interner, + ), }, } } @@ -1047,7 +1897,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Adt(def, args) => { let adt_id = def.inner().id; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Adt(chalk_ir::AdtId(adt_id), subst) } @@ -1062,11 +1912,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::InferTy::FloatVar(var) => { (InferenceVar::from(var.as_u32()), TyVariableKind::Float) } - rustc_type_ir::InferTy::FreshFloatTy(..) - | rustc_type_ir::InferTy::FreshIntTy(..) - | rustc_type_ir::InferTy::FreshTy(..) => { - panic!("Freshening shouldn't happen.") - } + _ => todo!(), }; TyKind::InferenceVar(var, kind) } @@ -1086,7 +1932,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let subst = Substitution::from_iter( Interner, tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) + chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver(ty, interner)) .intern(Interner) }), ); @@ -1094,8 +1940,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = convert_ty_for_result(interner, ty); - let const_ = convert_const_for_result(interner, const_); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); TyKind::Array(ty, const_) } @@ -1106,7 +1952,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) _ => unreachable!(), }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); TyKind::AssociatedType(associated_ty_id, substitution) } rustc_type_ir::AliasTyKind::Opaque => { @@ -1114,14 +1960,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: opaque_ty_id.into(), substitution, })) } - rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), - rustc_type_ir::AliasTyKind::Free => unimplemented!(), + rustc_type_ir::AliasTyKind::Inherent => todo!(), + rustc_type_ir::AliasTyKind::Free => todo!(), }, // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. @@ -1156,7 +2002,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) interner, bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), ); - let substitution = convert_args_for_result(interner, args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(args, interner); let substitution = chalk_ir::FnSubst(substitution); let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; TyKind::Function(fnptr) @@ -1199,11 +2045,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let trait_ref = TraitRef::new( interner, trait_ref.def_id, - [self_ty.into()].into_iter().chain(trait_ref.args.iter()), + [self_ty.clone().into()].into_iter().chain(trait_ref.args.iter()), ); let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = - convert_args_for_result(interner, trait_ref.args.as_slice()); + ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } @@ -1221,19 +2067,19 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) projection_term: AliasTerm::new( interner, existential_projection.def_id, - [self_ty.into()] + [self_ty.clone().into()] .iter() - .chain(existential_projection.args.iter()), + .chain(existential_projection.args.clone().iter()), ), - term: existential_projection.term, + term: existential_projection.term.clone(), }; let associated_ty_id = match projection.projection_term.def_id { SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = convert_args_for_result( + let substitution = ChalkToNextSolver::from_nextsolver( + projection.projection_term.args, interner, - projection.projection_term.args.as_slice(), ); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, @@ -1243,7 +2089,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) Term::Ty(ty) => ty, _ => unreachable!(), }; - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); let alias_eq = chalk_ir::AliasEq { alias, ty }; chalk_ir::WhereClause::AliasEq(alias_eq) } @@ -1262,7 +2108,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Slice(ty) => { - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); TyKind::Slice(ty) } @@ -1273,24 +2119,29 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; TyKind::Foreign(to_foreign_def_id(def_id)) } - rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), + rustc_type_ir::TyKind::Pat(_, _) => todo!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), - SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), - _ => unreachable!(), - }; - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::FnDef(id.to_chalk(interner.db()), subst) + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + match def_id { + SolverDefId::FunctionId(id) => { + TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Enum(e)) => { + TyKind::FnDef(CallableDefId::EnumVariantId(e).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Struct(s)) => { + TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) + } + _ => unreachable!("Unexpected def id {:?}", def_id), + } } rustc_type_ir::TyKind::Closure(def_id, args) => { @@ -1298,16 +2149,16 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedClosureId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Closure(id.into(), subst) } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), + rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { let id = match def_id { SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Coroutine(id.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { @@ -1315,7 +2166,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::CoroutineWitness(id.into(), subst) } @@ -1419,3 +2270,17 @@ pub fn convert_region_for_result<'db>( }; chalk_ir::Lifetime::new(Interner, lifetime) } + +pub trait InferenceVarExt { + fn to_vid(self) -> rustc_type_ir::TyVid; + fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar; +} + +impl InferenceVarExt for InferenceVar { + fn to_vid(self) -> rustc_type_ir::TyVid { + rustc_type_ir::TyVid::from_u32(self.index()) + } + fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar { + InferenceVar::from(vid.as_u32()) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 1d0ee0e488dcf..b9b2950a94a3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -14,7 +14,7 @@ use crate::{ db::HirDatabase, next_solver::{ ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, - mapping::{ChalkToNextSolver, convert_args_for_result}, + mapping::ChalkToNextSolver, util::sizedness_fast_path, }, }; @@ -200,7 +200,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), _ => unreachable!(), }; - let subst = convert_args_for_result(self.interner, uv.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(uv.args, self.interner); let ec = self.cx().db.const_eval(c, subst, None).ok()?; Some(ec.to_nextsolver(self.interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 51cf5be1372ca..0c58e031c3a0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,6 +1,7 @@ //! Trait solving using Chalk. use core::fmt; +use std::hash::Hash; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; use chalk_solve::rust_ir; @@ -20,17 +21,9 @@ use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, - db::HirDatabase, - infer::unify::InferenceTable, - next_solver::{ - DbInterner, GenericArg, SolverContext, Span, - infer::{DbInternerInferExt, InferCtxt}, - mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, - util::mini_canonicalize, - }, - utils::UnevaluatedConstEvaluatorFolder, + db::HirDatabase, infer::unify::InferenceTable, next_solver::{ + infer::{DbInternerInferExt, InferCtxt}, mapping::{convert_canonical_args_for_result, ChalkToNextSolver}, util::mini_canonicalize, DbInterner, GenericArg, Predicate, SolverContext, Span + }, utils::UnevaluatedConstEvaluatorFolder, AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -231,7 +224,6 @@ impl NextTraitSolveResult { } } -/// Solve a trait goal using Chalk. pub fn next_trait_solve( db: &dyn HirDatabase, krate: Crate, @@ -290,6 +282,57 @@ pub fn next_trait_solve( } } +pub fn next_trait_solve_canonical<'db>( + db: &'db dyn HirDatabase, + krate: Crate, + block: Option, + goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, +) -> NextTraitSolveResult { + // FIXME: should use analysis_in_body, but that needs GenericDefId::Block + let context = SolverContext( + DbInterner::new_with(db, Some(krate), block) + .infer_ctxt() + .build(TypingMode::non_body_analysis()), + ); + + tracing::info!(?goal); + + let (goal, var_values) = + context.instantiate_canonical(&goal); + tracing::info!(?var_values); + + let res = context.evaluate_root_goal( + goal.clone(), + Span::dummy(), + None + ); + + let vars = + var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); + let canonical_var_values = mini_canonicalize(context, vars); + + let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + match res { + Err(_) => NextTraitSolveResult::NoSolution, + Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( + convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args) + ), + Ok((_, Certainty::Maybe(_), args)) => { + let subst = convert_canonical_args_for_result( + DbInterner::new_with(db, Some(krate), block), + args, + ); + NextTraitSolveResult::Uncertain(chalk_ir::Canonical { + binders: subst.binders, + value: subst.value.subst, + }) + } + } +} + /// Solve a trait goal using Chalk. pub fn next_trait_solve_in_ctxt<'db, 'a>( infer_ctxt: &'a InferCtxt<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 928753ef0edeb..79a154a5d8e6e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -14,7 +14,7 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::Name, }; -use hir_ty::{db::HirDatabase, method_resolution}; +use hir_ty::{db::HirDatabase, method_resolution, next_solver::{mapping::ChalkToNextSolver, DbInterner}}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -271,7 +271,7 @@ fn resolve_impl_trait_item<'db>( // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) _ = method_resolution::iterate_path_candidates( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with(db, Some(environment.krate), environment.block)), db, environment, &traits_in_scope, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6eb8a8bf60fd1..52ab808d22841 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5611,7 +5611,11 @@ impl<'db> Type<'db> { .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); _ = method_resolution::iterate_method_candidates_dyn( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, traits_in_scope, @@ -5698,7 +5702,11 @@ impl<'db> Type<'db> { .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); _ = method_resolution::iterate_path_candidates( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, traits_in_scope, From 7e7ce10e5b0474e6833c22062c4973f447ca6463 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Wed, 10 Sep 2025 00:10:05 +0800 Subject: [PATCH 0280/1900] fix: `useless_attribute` FP on `deprecated_in_future` --- clippy_lints/src/attrs/useless_attribute.rs | 1 + clippy_utils/src/sym.rs | 1 + tests/ui/useless_attribute.fixed | 10 ++++++++++ tests/ui/useless_attribute.rs | 10 ++++++++++ 4 files changed, 22 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index b9b5cedb5aa76..1cebc18edc90a 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -30,6 +30,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { sym::ambiguous_glob_reexports | sym::dead_code | sym::deprecated + | sym::deprecated_in_future | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f96..e2570a151584f 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -125,6 +125,7 @@ generate! { cycle, cyclomatic_complexity, de, + deprecated_in_future, diagnostics, disallowed_types, drain, diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index be4fb55ddfb86..15070dd9c2ca6 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 5a1bcf97a5b46..3f530de7fd8e3 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} From 69140ec44c2f57497ae2375a6f75f0bfe76b8003 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 29 Aug 2025 21:47:10 +0900 Subject: [PATCH 0281/1900] Fix failing tests and fill-in missing details --- .../crates/hir-ty/src/autoderef.rs | 14 +- .../crates/hir-ty/src/chalk_ext.rs | 41 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 28 +- .../crates/hir-ty/src/infer/closure.rs | 12 +- .../crates/hir-ty/src/infer/coerce.rs | 67 +- .../crates/hir-ty/src/infer/expr.rs | 57 +- .../crates/hir-ty/src/infer/pat.rs | 12 +- .../crates/hir-ty/src/infer/path.rs | 12 +- .../crates/hir-ty/src/infer/unify.rs | 472 +++++---- .../rust-analyzer/crates/hir-ty/src/lib.rs | 11 +- .../crates/hir-ty/src/method_resolution.rs | 223 ++-- .../rust-analyzer/crates/hir-ty/src/mir.rs | 3 +- .../crates/hir-ty/src/mir/monomorphization.rs | 26 +- .../crates/hir-ty/src/next_solver.rs | 4 +- .../infer/canonical/canonicalizer.rs | 136 ++- .../hir-ty/src/next_solver/infer/mod.rs | 25 +- .../crates/hir-ty/src/next_solver/mapping.rs | 974 ++++-------------- .../crates/hir-ty/src/next_solver/solver.rs | 6 +- .../crates/hir-ty/src/tests/coercion.rs | 14 +- .../crates/hir-ty/src/tests/incremental.rs | 6 +- .../crates/hir-ty/src/tests/macros.rs | 4 +- .../hir-ty/src/tests/method_resolution.rs | 10 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/opaque_types.rs | 4 +- .../crates/hir-ty/src/tests/patterns.rs | 26 +- .../crates/hir-ty/src/tests/regression.rs | 12 +- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 4 +- .../crates/hir-ty/src/tests/traits.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 43 +- .../rust-analyzer/crates/hir/src/attrs.rs | 12 +- .../ide-completion/src/context/tests.rs | 3 +- .../src/handlers/type_mismatch.rs | 3 +- .../rust-analyzer/crates/ide-ssr/src/tests.rs | 15 +- .../crates/ide/src/doc_links/tests.rs | 3 +- 36 files changed, 914 insertions(+), 1428 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 4ea0156e1248a..82696a5c94daf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -13,7 +13,7 @@ use triomphe::Arc; use crate::{ Canonical, Goal, Interner, ProjectionTyExt, TraitEnvironment, Ty, TyBuilder, TyKind, - db::HirDatabase, infer::unify::InferenceTable, + db::HirDatabase, infer::unify::InferenceTable, next_solver::mapping::ChalkToNextSolver, }; const AUTODEREF_RECURSION_LIMIT: usize = 20; @@ -98,7 +98,7 @@ impl<'table, 'db> Autoderef<'table, 'db> { explicit: bool, use_receiver_trait: bool, ) -> Self { - let ty = table.resolve_ty_shallow(&ty); + let ty = table.structurally_resolve_type(&ty); Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } @@ -114,7 +114,7 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { explicit: bool, use_receiver_trait: bool, ) -> Self { - let ty = table.resolve_ty_shallow(&ty); + let ty = table.structurally_resolve_type(&ty); Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -160,7 +160,7 @@ pub(crate) fn autoderef_step( use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { - Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) + Some((AutoderefKind::Builtin, table.structurally_resolve_type(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } @@ -187,7 +187,7 @@ pub(crate) fn deref_by_trait( use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); - if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { + if table.structurally_resolve_type(&ty).inference_var(Interner).is_some() { // don't try to deref unknown variables return None; } @@ -229,8 +229,8 @@ pub(crate) fn deref_by_trait( return None; } - table.register_obligation(implements_goal); + table.register_obligation(implements_goal.to_nextsolver(table.interner)); let result = table.normalize_projection_ty(projection); - Some(table.resolve_ty_shallow(&result)) + Some(table.structurally_resolve_type(&result)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 33ae099c690ea..9f0ea14a8063d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -245,26 +245,30 @@ impl TyExt for Ty { } fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { + let handle_async_block_type_impl_trait = |def: DefWithBodyId| { + let krate = def.module(db).krate(); + if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) { + // This is only used by type walking. + // Parameters will be walked outside, and projection predicate is not used. + // So just provide the Future trait. + let impl_bound = Binders::empty( + Interner, + WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(future_trait), + substitution: Substitution::empty(Interner), + }), + ); + Some(vec![impl_bound]) + } else { + None + } + }; + match self.kind(Interner) { TyKind::OpaqueType(opaque_ty_id, subst) => { match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) { ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => { - let krate = def.module(db).krate(); - if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) { - // This is only used by type walking. - // Parameters will be walked outside, and projection predicate is not used. - // So just provide the Future trait. - let impl_bound = Binders::empty( - Interner, - WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(future_trait), - substitution: Substitution::empty(Interner), - }), - ); - Some(vec![impl_bound]) - } else { - None - } + handle_async_block_type_impl_trait(def) } ImplTraitId::ReturnTypeImplTrait(func, idx) => { db.return_type_impl_traits(func).map(|it| { @@ -299,8 +303,9 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } - // It always has an parameter for Future::Output type. - ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), + ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + return handle_async_block_type_impl_trait(def); + } }; predicates.map(|it| it.into_value_and_skipped_binders().0) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 4700335931549..6e07d3afe5524 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -27,7 +27,7 @@ use crate::{ next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, SolverDefId, Ty, ValueConst, - mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_binder_to_early_binder}, }, }; @@ -145,7 +145,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + let subst = unevaluated_const.args.to_chalk(interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_usize(db, ec) } @@ -168,7 +168,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + let subst = unevaluated_const.args.to_chalk(interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_isize(db, &ec) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 71b33a0e9035f..265d1f8541cc8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -55,10 +55,9 @@ use stdx::{always, never}; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - ImplTraitIdx, InEnvironment, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, - ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, - TyBuilder, TyExt, + AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, + IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, + PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, db::HirDatabase, fold_tys, generics::Generics, @@ -70,6 +69,7 @@ use crate::{ }, lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic}, mir::MirSpan, + next_solver::{self, mapping::ChalkToNextSolver}, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::UnevaluatedConstEvaluatorFolder, @@ -182,13 +182,13 @@ impl BindingMode { } #[derive(Debug)] -pub(crate) struct InferOk { +pub(crate) struct InferOk<'db, T> { value: T, - goals: Vec>, + goals: Vec>>, } -impl InferOk { - fn map(self, f: impl FnOnce(T) -> U) -> InferOk { +impl<'db, T> InferOk<'db, T> { + fn map(self, f: impl FnOnce(T) -> U) -> InferOk<'db, U> { InferOk { value: f(self.value), goals: self.goals } } } @@ -203,7 +203,7 @@ pub enum InferenceTyDiagnosticSource { #[derive(Debug)] pub(crate) struct TypeError; -pub(crate) type InferResult = Result, TypeError>; +pub(crate) type InferResult<'db, T> = Result, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { @@ -832,6 +832,7 @@ impl<'db> InferenceContext<'db> { coercion_casts, diagnostics: _, } = &mut result; + table.resolve_obligations_as_possible(); table.fallback_if_possible(); // Comment from rustc: @@ -1480,7 +1481,8 @@ impl<'db> InferenceContext<'db> { } fn push_obligation(&mut self, o: DomainGoal) { - self.table.register_obligation(o.cast(Interner)); + let goal: crate::Goal = o.cast(Interner); + self.table.register_obligation(goal.to_nextsolver(self.table.interner)); } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { @@ -1746,7 +1748,7 @@ impl<'db> InferenceContext<'db> { ty = self.table.insert_type_vars(ty); ty = self.table.normalize_associated_types_in(ty); - ty = self.table.resolve_ty_shallow(&ty); + ty = self.table.structurally_resolve_type(&ty); if ty.is_unknown() { return (self.err_ty(), None); } @@ -1817,7 +1819,7 @@ impl<'db> InferenceContext<'db> { let ty = match ty.kind(Interner) { TyKind::Alias(AliasTy::Projection(proj_ty)) => { let ty = self.table.normalize_projection_ty(proj_ty.clone()); - self.table.resolve_ty_shallow(&ty) + self.table.structurally_resolve_type(&ty) } _ => ty, }; @@ -2047,7 +2049,7 @@ impl Expectation { fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'_>) -> Expectation { match self { Expectation::HasType(ety) => { - let ety = table.resolve_ty_shallow(ety); + let ety = table.structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 493652b764015..5a69ad68b58e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -39,6 +39,7 @@ use crate::{ infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, + next_solver::mapping::ChalkToNextSolver, to_assoc_type_id, traits::FnTrait, utils::{self, elaborate_clause_supertraits}, @@ -437,10 +438,10 @@ impl InferenceContext<'_> { associated_ty_id: to_assoc_type_id(future_output), substitution: Substitution::from1(Interner, ret_param_future.clone()), }); - self.table.register_obligation( + let goal: crate::Goal = crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } - .cast(Interner), - ); + .cast(Interner); + self.table.register_obligation(goal.to_nextsolver(self.table.interner)); Some(FnSubst(Substitution::from_iter( Interner, @@ -568,7 +569,10 @@ impl InferenceContext<'_> { let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); let snapshot = self.table.snapshot(); - if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>(&expected_sig.substitution.0, &supplied_sig.expected_sig.substitution.0) { + if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>( + &expected_sig.substitution.0, + &supplied_sig.expected_sig.substitution.0, + ) { self.table.rollback_to(snapshot); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index b47834f0c749c..df516662bfd37 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -8,24 +8,27 @@ use std::iter; use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{ - hir::ExprId, - lang_item::LangItem, -}; +use hir_def::{hir::ExprId, lang_item::LangItem}; use rustc_type_ir::solve::Certainty; use stdx::always; use triomphe::Arc; use crate::{ - autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ + Canonical, FnAbi, FnPointer, FnSig, Goal, Interner, Lifetime, Substitution, TraitEnvironment, + Ty, TyBuilder, TyExt, + autoderef::{Autoderef, AutoderefKind}, + db::HirDatabase, + infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, - }, utils::ClosureSubst, Canonical, FnAbi, FnPointer, FnSig, Goal, InEnvironment, Interner, Lifetime, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt + }, + next_solver, + utils::ClosureSubst, }; use super::unify::InferenceTable; -pub(crate) type CoerceResult = Result, Ty)>, TypeError>; +pub(crate) type CoerceResult<'db> = Result, Ty)>, TypeError>; /// Do not require any adjustments, i.e. coerce `x -> x`. fn identity(_: Ty) -> Vec { @@ -37,11 +40,11 @@ fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { } /// This always returns `Ok(...)`. -fn success( +fn success<'db>( adj: Vec, target: Ty, - goals: Vec>, -) -> CoerceResult { + goals: Vec>>, +) -> CoerceResult<'db> { Ok(InferOk { goals, value: (adj, target) }) } @@ -107,9 +110,9 @@ impl CoerceMany { /// coerce both to function pointers; /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. - pub(super) fn coerce( + pub(super) fn coerce<'db>( &mut self, - ctx: &mut InferenceContext<'_>, + ctx: &mut InferenceContext<'db>, expr: Option, expr_ty: &Ty, cause: CoercionCause, @@ -276,7 +279,7 @@ impl InferenceContext<'_> { } } -impl InferenceTable<'_> { +impl<'db> InferenceTable<'db> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(crate) fn coerce( @@ -285,8 +288,8 @@ impl InferenceTable<'_> { to_ty: &Ty, coerce_never: CoerceNever, ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); + let from_ty = self.structurally_resolve_type(from_ty); + let to_ty = self.structurally_resolve_type(to_ty); match self.coerce_inner(from_ty, &to_ty, coerce_never) { Ok(InferOk { value: (adjustments, ty), goals }) => { self.register_infer_ok(InferOk { value: (), goals }); @@ -299,10 +302,15 @@ impl InferenceTable<'_> { } } - fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult { + fn coerce_inner( + &mut self, + from_ty: Ty, + to_ty: &Ty, + coerce_never: CoerceNever, + ) -> CoerceResult<'db> { if from_ty.is_never() { if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, TyVariableKind::General, true); + self.set_diverging(*tv, TyVariableKind::General); } if coerce_never == CoerceNever::Yes { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound @@ -370,7 +378,7 @@ impl InferenceTable<'_> { } /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult + fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult<'db> where F: FnOnce(Ty) -> Vec, { @@ -378,7 +386,7 @@ impl InferenceTable<'_> { .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals)) } - fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult { + fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult<'db> { let (is_ref, from_mt, from_inner) = match from_ty.kind(Interner) { TyKind::Ref(mt, _, ty) => (true, mt, ty), TyKind::Raw(mt, ty) => (false, mt, ty), @@ -420,7 +428,7 @@ impl InferenceTable<'_> { to_ty: &Ty, to_mt: Mutability, to_lt: &Lifetime, - ) -> CoerceResult { + ) -> CoerceResult<'db> { let (_from_lt, from_mt) = match from_ty.kind(Interner) { TyKind::Ref(mt, lt, _) => { coerce_mutabilities(*mt, to_mt)?; @@ -524,7 +532,7 @@ impl InferenceTable<'_> { } /// Attempts to coerce from the type of a Rust function item into a function pointer. - fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult { + fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult<'db> { match to_ty.kind(Interner) { TyKind::Function(_) => { let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); @@ -566,7 +574,7 @@ impl InferenceTable<'_> { from_ty: Ty, from_f: &FnPointer, to_ty: &Ty, - ) -> CoerceResult { + ) -> CoerceResult<'db> { self.coerce_from_safe_fn( from_ty, from_f, @@ -583,7 +591,7 @@ impl InferenceTable<'_> { to_ty: &Ty, to_unsafe: F, normal: G, - ) -> CoerceResult + ) -> CoerceResult<'db> where F: FnOnce(Ty) -> Vec, G: FnOnce(Ty) -> Vec, @@ -606,7 +614,7 @@ impl InferenceTable<'_> { from_ty: Ty, from_substs: &Substitution, to_ty: &Ty, - ) -> CoerceResult { + ) -> CoerceResult<'db> { match to_ty.kind(Interner) { // if from_substs is non-capturing (FIXME) TyKind::Function(fn_ty) => { @@ -631,7 +639,7 @@ impl InferenceTable<'_> { /// Coerce a type using `from_ty: CoerceUnsized` /// /// See: - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult { + fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult<'db> { // These 'if' statements require some explanation. // The `CoerceUnsized` trait is special - it is only // possible to write `impl CoerceUnsized for A` where @@ -707,12 +715,9 @@ impl InferenceTable<'_> { let goal: Goal = coerce_unsized_tref.cast(Interner); - self.commit_if_ok(|table| { - match table.solve_obligation(goal) { - Ok(Certainty::Yes) => Ok(()), - Ok(Certainty::Maybe(_)) => Ok(()), - Err(_) => Err(TypeError), - } + self.commit_if_ok(|table| match table.solve_obligation(goal) { + Ok(Certainty::Yes) => Ok(()), + _ => Err(TypeError), })?; let unsize = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e39f3ab75f16f..bfeb5bae851a5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -23,9 +23,29 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, generics::generics, infer::{ - coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind - }, lang_items::lang_items_for_bin_op, lower::{lower_to_chalk_mutability, path::{substs_from_args_and_bindings, GenericArgsLowerer, TypeLikeConst}, ParamLoweringMode}, mapping::{from_chalk, ToChalk}, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, + DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, + Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + autoderef::{Autoderef, builtin_deref, deref_by_trait}, + consteval, + generics::generics, + infer::{ + BreakableKind, + coerce::{CoerceMany, CoerceNever, CoercionCause}, + find_continuable, + pat::contains_explicit_ref_binding, + }, + lang_items::lang_items_for_bin_op, + lower::{ + ParamLoweringMode, lower_to_chalk_mutability, + path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, + }, + mapping::{ToChalk, from_chalk}, + method_resolution::{self, VisibleFromModule}, + next_solver::mapping::ChalkToNextSolver, + primitive::{self, UintTy}, + static_lifetime, to_chalk_trait_id, + traits::FnTrait, }; use super::{ @@ -383,7 +403,7 @@ impl InferenceContext<'_> { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - let input_ty = self.resolve_ty_shallow(&input_ty); + let input_ty = self.table.structurally_resolve_type(&input_ty); self.infer_top_pat(arm.pat, &input_ty, None); } @@ -633,7 +653,7 @@ impl InferenceContext<'_> { &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - let inner_ty = self.resolve_ty_shallow(&inner_ty); + let inner_ty = self.table.structurally_resolve_type(&inner_ty); // FIXME: Note down method resolution her match op { UnaryOp::Deref => { @@ -651,7 +671,7 @@ impl InferenceContext<'_> { ); } if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { - self.resolve_ty_shallow(derefed) + self.table.structurally_resolve_type(derefed) } else { deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) @@ -807,10 +827,10 @@ impl InferenceContext<'_> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = ChalkToNextSolver::from_nextsolver(self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)), self.table.interner); + let canonicalized = + self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)); let receiver_adjustments = method_resolution::resolve_indexing_op( - self.db, - self.table.trait_env.clone(), + &mut self.table, canonicalized, index_trait, ); @@ -983,7 +1003,7 @@ impl InferenceContext<'_> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.resolve_ty_shallow(&ty); + let ty = this.table.structurally_resolve_type(&ty); match ty.kind(Interner) { TyKind::FnDef(def, parameters) => { let fnptr_ty = TyKind::Function( @@ -1405,9 +1425,10 @@ impl InferenceContext<'_> { // use knowledge of built-in binary ops, which can sometimes help inference let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op); self.unify(&builtin_ret, &ret_ty); + builtin_ret + } else { + ret_ty } - - ret_ty } fn infer_block( @@ -1660,7 +1681,8 @@ impl InferenceContext<'_> { None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = + self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1806,7 +1828,8 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = + self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, @@ -2216,7 +2239,7 @@ impl InferenceContext<'_> { } fn register_obligations_for_call(&mut self, callable_ty: &Ty) { - let callable_ty = self.resolve_ty_shallow(callable_ty); + let callable_ty = self.table.structurally_resolve_type(callable_ty); if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) { let def: CallableDefId = from_chalk(self.db, *fn_def); let generic_predicates = @@ -2305,9 +2328,9 @@ impl InferenceContext<'_> { /// Dereferences a single level of immutable referencing. fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty { - let ty = self.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); match ty.kind(Interner) { - TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner), + TyKind::Ref(Mutability::Not, _, inner) => self.table.structurally_resolve_type(inner), _ => ty, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 707bec0fce4ce..16489e3068f35 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -190,7 +190,7 @@ impl InferenceContext<'_> { subs: &[PatId], decl: Option, ) -> Ty { - let expected = self.resolve_ty_shallow(expected); + let expected = self.table.structurally_resolve_type(expected); let expectations = match expected.as_tuple() { Some(parameters) => parameters.as_slice(Interner), _ => &[], @@ -238,7 +238,7 @@ impl InferenceContext<'_> { mut default_bm: BindingMode, decl: Option, ) -> Ty { - let mut expected = self.resolve_ty_shallow(expected); + let mut expected = self.table.structurally_resolve_type(expected); if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { cov_mark::hit!(match_ergonomics_ref); @@ -251,7 +251,7 @@ impl InferenceContext<'_> { let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { pat_adjustments.push(expected.clone()); - expected = self.resolve_ty_shallow(inner); + expected = self.table.structurally_resolve_type(inner); default_bm = match default_bm { BindingMode::Move => BindingMode::Ref(mutability), BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), @@ -494,7 +494,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, decl: Option, ) -> Ty { - let expected = self.resolve_ty_shallow(expected); + let expected = self.table.structurally_resolve_type(expected); // If `expected` is an infer ty, we try to equate it to an array if the given pattern // allows it. See issue #16609 @@ -506,7 +506,7 @@ impl InferenceContext<'_> { self.unify(&expected, &resolved_array_ty); } - let expected = self.resolve_ty_shallow(&expected); + let expected = self.table.structurally_resolve_type(&expected); let elem_ty = match expected.kind(Interner) { TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), _ => self.err_ty(), @@ -542,7 +542,7 @@ impl InferenceContext<'_> { if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] && let Some((inner, ..)) = expected.as_reference() { - let inner = self.resolve_ty_shallow(inner); + let inner = self.table.structurally_resolve_type(inner); if matches!(inner.kind(Interner), TyKind::Slice(_)) { let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); let slice_ty = TyKind::Slice(elem_ty).intern(Interner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 9f3cd2b8fb006..1f62f9c4aa113 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -10,7 +10,15 @@ use hir_expand::name::Name; use stdx::never; use crate::{ - builder::ParamKind, consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, to_chalk_trait_id, InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId + InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, + TyBuilder, TyExt, TyKind, ValueTyDefId, + builder::ParamKind, + consteval, error_lifetime, + generics::generics, + infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, + method_resolution::{self, VisibleFromModule}, + next_solver::mapping::ChalkToNextSolver, + to_chalk_trait_id, }; use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; @@ -384,7 +392,7 @@ impl InferenceContext<'_> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Substitution)> { - let ty = self.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 3745d2c98fd9f..ec4b7ee85dc67 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,7 +3,8 @@ use std::{fmt, mem}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, CanonicalVarKind, FloatTy, IntTy, TyVariableKind, + CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable, + interner::HasInterner, }; use either::Either; use hir_def::{AdtId, lang_item::LangItem}; @@ -11,13 +12,36 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_next_trait_solver::solve::HasChanged; -use rustc_type_ir::{inherent::Span, relate::{solver_relating::RelateExt, Relate}, solve::{Certainty, NoSolution}, FloatVid, IntVid, TyVid}; +use rustc_type_ir::{ + AliasRelationDirection, FloatVid, IntVid, TyVid, + inherent::{Span, Term as _}, + relate::{Relate, solver_relating::RelateExt}, + solve::{Certainty, NoSolution}, +}; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{infer::{canonical::canonicalizer::OriginalQueryValues, snapshot::CombinedSnapshot, DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, InferenceVarExt}, DbInterner, ParamEnvAnd, SolverDefIds}, to_chalk_trait_id, traits::{next_trait_solve, next_trait_solve_canonical, next_trait_solve_in_ctxt, FnTrait, NextTraitSolveResult}, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause + AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, + GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, + OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, + TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, + db::HirDatabase, + fold_generic_args, fold_tys_and_consts, + next_solver::{ + self, Binder, DbInterner, ParamEnvAnd, Predicate, PredicateKind, SolverDefIds, Term, + infer::{ + DbInternerInferExt, InferCtxt, canonical::canonicalizer::OriginalQueryValues, + snapshot::CombinedSnapshot, + }, + mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, + }, + to_chalk_trait_id, + traits::{ + FnTrait, NextTraitSolveResult, next_trait_solve_canonical_in_ctxt, next_trait_solve_in_ctxt, + }, }; impl<'db> InferenceContext<'db> { @@ -38,7 +62,7 @@ impl<'db> InferenceContext<'db> { let pending_obligations = mem::take(&mut self.table.pending_obligations); let obligations = pending_obligations .iter() - .filter_map(|obligation| match obligation.goal.data(Interner) { + .filter_map(|obligation| match obligation.to_chalk(self.table.interner).goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(clause)) => { let ty = match clause { WhereClause::AliasEq(AliasEq { @@ -67,51 +91,6 @@ impl<'db> InferenceContext<'db> { } } -#[derive(Debug, Clone)] -pub(crate) struct Canonicalized -where - T: HasInterner, -{ - pub(crate) value: Canonical, - free_vars: Vec, -} - -impl> Canonicalized { - pub(crate) fn apply_solution( - &self, - ctx: &mut InferenceTable<'_>, - solution: Canonical, - ) { - // the solution may contain new variables, which we need to convert to new inference vars - let new_vars = Substitution::from_iter( - Interner, - solution.binders.iter(Interner).map(|k| match &k.kind { - VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner), - VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner), - VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner), - // Chalk can sometimes return new lifetime variables. We just replace them by errors - // for now. - VariableKind::Lifetime => ctx.new_lifetime_var().cast(Interner), - VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner), - }), - ); - for (i, v) in solution.value.iter(Interner).enumerate() { - let var = &self.free_vars[i]; - if let Some(ty) = v.ty(Interner) { - // eagerly replace projections in the type; we may be getting types - // e.g. from where clauses where this hasn't happened yet - let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); - tracing::debug!("unifying {:?} {:?}", var, ty); - ctx.unify(var.assert_ty_ref(Interner), &ty); - } else { - let v = new_vars.apply(v.clone(), Interner); - tracing::debug!("try_unifying {:?} {:?}", var, v); - let _ = ctx.try_unify(var, &v); - } - } - } -} - /// Check if types unify. /// /// Note that we consider placeholder types to unify with everything. @@ -207,23 +186,21 @@ bitflags::bitflags! { } } -type ChalkInferenceTable = chalk_solve::infer::InferenceTable; - #[derive(Clone)] pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) interner: DbInterner<'a>, pub(crate) trait_env: Arc, pub(crate) tait_coercion_table: Option>, - infer_ctxt: InferCtxt<'a>, + pub(crate) infer_ctxt: InferCtxt<'a>, diverging_tys: FxHashSet, - pending_obligations: Vec>, + pending_obligations: Vec>>, } -pub(crate) struct InferenceTableSnapshot { +pub(crate) struct InferenceTableSnapshot<'a> { ctxt_snapshot: CombinedSnapshot, diverging_tys: FxHashSet, - pending_obligations: Vec>, + pending_obligations: Vec>>, } impl<'a> InferenceTable<'a> { @@ -234,7 +211,9 @@ impl<'a> InferenceTable<'a> { interner, trait_env, tait_coercion_table: None, - infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []) }), + infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { + defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), + }), diverging_tys: FxHashSet::default(), pending_obligations: Vec::new(), } @@ -250,40 +229,55 @@ impl<'a> InferenceTable<'a> { let mut new_tys = FxHashSet::default(); for ty in self.diverging_tys.iter() { match ty.kind(Interner) { - TyKind::InferenceVar(var, kind) => { - match kind { - TyVariableKind::General => { - let root = InferenceVar::from(self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } + TyKind::InferenceVar(var, kind) => match kind { + TyVariableKind::General => { + let root = InferenceVar::from( + self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); } - TyVariableKind::Integer => { - let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from_usize(var.index() as usize)).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } + } + TyVariableKind::Integer => { + let root = InferenceVar::from( + self.infer_ctxt + .inner + .borrow_mut() + .int_unification_table() + .find(IntVid::from_usize(var.index() as usize)) + .as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); } - TyVariableKind::Float => { - let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from_usize(var.index() as usize)).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } + } + TyVariableKind::Float => { + let root = InferenceVar::from( + self.infer_ctxt + .inner + .borrow_mut() + .float_unification_table() + .find(FloatVid::from_usize(var.index() as usize)) + .as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); } } - } + }, _ => {} } } - self.diverging_tys.extend(new_tys.into_iter()); + self.diverging_tys.extend(new_tys); } - pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind, diverging: bool) { + pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind) { self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner)); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - let is_diverging = self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); + let is_diverging = + self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); if is_diverging { return TyKind::Never.intern(Interner); } @@ -295,19 +289,6 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize_with_free_vars(&mut self, t: ParamEnvAnd<'a, T>) -> (rustc_type_ir::Canonical, ParamEnvAnd<'a, T>>, OriginalQueryValues<'a>) - where - T: rustc_type_ir::TypeFoldable>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.resolve_obligations_as_possible(); - - let mut orig_values = OriginalQueryValues::default(); - let result = self.infer_ctxt.canonicalize_query(t, &mut orig_values); - (result.canonical, orig_values) - } - pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where T: rustc_type_ir::TypeFoldable>, @@ -341,7 +322,7 @@ impl<'a> InferenceTable<'a> { self.resolve_ty_shallow(&ty) } TyKind::AssociatedType(id, subst) => { - return Either::Left(self.resolve_ty_shallow(&ty)); + // return Either::Left(self.resolve_ty_shallow(&ty)); if ty.data(Interner).flags.intersects( chalk_ir::TypeFlags::HAS_TY_INFER | chalk_ir::TypeFlags::HAS_CT_INFER, @@ -365,51 +346,44 @@ impl<'a> InferenceTable<'a> { ); let in_env = InEnvironment::new(&self.trait_env.env, goal); let goal = in_env.to_nextsolver(self.interner); - let goal = ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; + let goal = + ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; - let (canonical_goal, _orig_values) = { + let (canonical_goal, orig_values) = { let mut orig_values = OriginalQueryValues::default(); - let result = self.infer_ctxt.canonicalize_query(goal, &mut orig_values); + let result = + self.infer_ctxt.canonicalize_query(goal, &mut orig_values); (result.canonical, orig_values) }; let canonical_goal = rustc_type_ir::Canonical { max_universe: canonical_goal.max_universe, variables: canonical_goal.variables, - value: crate::next_solver::Goal { param_env: canonical_goal.value.param_env, predicate: canonical_goal.value.value }, + value: crate::next_solver::Goal { + param_env: canonical_goal.value.param_env, + predicate: canonical_goal.value.value, + }, }; - let solution = next_trait_solve_canonical( - self.db, - self.trait_env.krate, - self.trait_env.block, - canonical_goal.clone(), + let solution = next_trait_solve_canonical_in_ctxt( + &self.infer_ctxt, + canonical_goal, ); if let NextTraitSolveResult::Certain(canonical_subst) = solution { - // This is not great :) But let's just assert this for now and come back to it later. - if canonical_subst.value.subst.len(Interner) != 1 { + let subst = self.instantiate_canonical(canonical_subst).subst; + if subst.len(Interner) != orig_values.var_values.len() { ty } else { - let normalized = canonical_subst.value.subst.as_slice(Interner) - [0] - .assert_ty_ref(Interner); - match normalized.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - if id == &proj_ty.associated_ty_id - && subst == &proj_ty.substitution - { - ty - } else { - normalized.clone() - } - } - TyKind::AssociatedType(new_id, new_subst) => { - if new_id == id && new_subst == subst { - ty + let target_ty = var.to_nextsolver(self.interner); + subst + .iter(Interner) + .zip(orig_values.var_values.iter()) + .find_map(|(new, orig)| { + if orig.ty() == Some(target_ty) { + Some(new.assert_ty_ref(Interner).clone()) } else { - normalized.clone() + None } - } - _ => normalized.clone(), - } + }) + .unwrap_or(ty) } } else { ty @@ -504,8 +478,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { let var = self.new_type_var(); let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; - let obligation = alias_eq.cast(Interner); - self.register_obligation(obligation); + let obligation: Goal = alias_eq.cast(Interner); + self.register_obligation(obligation.to_nextsolver(self.interner)); var } @@ -575,7 +549,9 @@ impl<'a> InferenceTable<'a> { Substitution::from_iter( Interner, binders.iter().map(|kind| match &kind.kind { - chalk_ir::VariableKind::Ty(ty_variable_kind) => self.new_var(*ty_variable_kind, false).cast(Interner), + chalk_ir::VariableKind::Ty(ty_variable_kind) => { + self.new_var(*ty_variable_kind, false).cast(Interner) + } chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner), chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner), }), @@ -589,8 +565,11 @@ impl<'a> InferenceTable<'a> { let subst = self.fresh_subst(canonical.binders.as_slice(Interner)); subst.apply(canonical.value, Interner) } - - pub(crate) fn instantiate_canonical_ns(&mut self, canonical: rustc_type_ir::Canonical, T>) -> T + + pub(crate) fn instantiate_canonical_ns( + &mut self, + canonical: rustc_type_ir::Canonical, T>, + ) -> T where T: rustc_type_ir::TypeFoldable>, { @@ -605,7 +584,7 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - let mut var_stack = &mut vec![]; + let var_stack = &mut vec![]; t.fold_with( &mut resolve::Resolver { table: self, var_stack, fallback }, DebruijnIndex::INNERMOST, @@ -618,6 +597,7 @@ impl<'a> InferenceTable<'a> { { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); + // let t = self.resolve_opaque_tys_in(t); // Resolve again, because maybe normalization inserted infer vars. self.resolve_with_fallback(t, &|_, _, d, _| d) } @@ -650,7 +630,7 @@ impl<'a> InferenceTable<'a> { } let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len(); for v in 0..float_vars { - let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Float); let maybe_resolved = self.resolve_ty_shallow(&var); if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { // I don't think we can ever unify these vars with float vars, but keep this here for now @@ -665,7 +645,11 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify, U: Relate>>( + &mut self, + ty1: &T, + ty2: &T, + ) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -675,17 +659,17 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify_deeply, U: Relate>>( + &mut self, + ty1: &T, + ty2: &T, + ) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, }; - result.goals.iter().all(|goal| { - let goal = goal.to_nextsolver(self.interner); - match next_trait_solve_in_ctxt(&self.infer_ctxt, goal) { - Ok((_, Certainty::Yes)) => true, - _ => false, - } + result.goals.into_iter().all(|goal| { + matches!(next_trait_solve_in_ctxt(&self.infer_ctxt, goal), Ok((_, Certainty::Yes))) }) } @@ -695,20 +679,15 @@ impl<'a> InferenceTable<'a> { &mut self, t1: &T, t2: &T, - ) -> InferResult<()> { + ) -> InferResult<'a, ()> { let param_env = self.trait_env.env.to_nextsolver(self.interner); let lhs = t1.to_nextsolver(self.interner); let rhs = t2.to_nextsolver(self.interner); let variance = rustc_type_ir::Variance::Invariant; let span = crate::next_solver::Span::dummy(); match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { - Ok(res) => { - let goals = res.into_iter().map(|g| ChalkToNextSolver::from_nextsolver(g, self.interner)).collect(); - Ok(InferOk { goals, value: () }) - } - Err(_) => { - Err(TypeError) - } + Ok(goals) => Ok(InferOk { goals, value: () }), + Err(_) => Err(TypeError), } } @@ -719,19 +698,75 @@ impl<'a> InferenceTable<'a> { if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { return ty.clone(); } + self.infer_ctxt + .resolve_vars_if_possible(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) + } + + pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + use rustc_type_ir::TypeVisitableExt; + + if !t.has_non_region_infer() { + return t; + } + + let t = self.infer_ctxt.resolve_vars_if_possible(t); + + if !t.has_non_region_infer() { + return t; + } + + self.resolve_obligations_as_possible(); + self.infer_ctxt.resolve_vars_if_possible(t) + } + + pub(crate) fn structurally_resolve_type(&mut self, ty: &Ty) -> Ty { + if let TyKind::Alias(..) = ty.kind(Interner) { + self.structurally_normalize_ty(ty) + } else { + self.resolve_vars_with_obligations(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) + } + } + + fn structurally_normalize_ty(&mut self, ty: &Ty) -> Ty { + self.structurally_normalize_term(ty.to_nextsolver(self.interner).into()) + .expect_ty() + .to_chalk(self.interner) + } + + fn structurally_normalize_term(&mut self, term: Term<'a>) -> Term<'a> { + if term.to_alias_term().is_none() { + return term; + } + + let new_infer = self.infer_ctxt.next_term_var_of_kind(term); + + self.register_obligation(Predicate::new( + self.interner, + Binder::dummy(PredicateKind::AliasRelate( + term, + new_infer, + AliasRelationDirection::Equate, + )), + )); self.resolve_obligations_as_possible(); - ChalkToNextSolver::from_nextsolver(self.infer_ctxt.resolve_vars_if_possible(ty.to_nextsolver(self.interner)), self.interner) + let res = self.infer_ctxt.resolve_vars_if_possible(new_infer); + if res == new_infer { term } else { res } } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { + pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'a> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); let diverging_tys = self.diverging_tys.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot {ctxt_snapshot, pending_obligations, diverging_tys } + InferenceTableSnapshot { ctxt_snapshot, pending_obligations, diverging_tys } } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { + pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'a>) { self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); self.diverging_tys = snapshot.diverging_tys; self.pending_obligations = snapshot.pending_obligations; @@ -745,7 +780,10 @@ impl<'a> InferenceTable<'a> { result } - pub(crate) fn commit_if_ok(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> Result) -> Result { + pub(crate) fn commit_if_ok( + &mut self, + f: impl FnOnce(&mut InferenceTable<'_>) -> Result, + ) -> Result { let snapshot = self.snapshot(); let result = f(self); match result { @@ -765,59 +803,31 @@ impl<'a> InferenceTable<'a> { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner)); - next_trait_solve_canonical(self.db, self.trait_env.krate, self.trait_env.block, canonicalized) + next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) } - + #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result { let goal = InEnvironment::new(&self.trait_env.env, goal); - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { - return Ok(Certainty::Yes); - }; - let goal = goal.to_nextsolver(self.interner); let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); result.map(|m| m.1) } - pub(crate) fn register_obligation(&mut self, goal: Goal) { - let in_env = InEnvironment::new(&self.trait_env.env, goal); - self.register_obligation_in_env(in_env) - } - - // If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy) - fn unify_opaque_instead_of_solve(&mut self, goal: InEnvironment) -> Option> { - match goal.goal.data(Interner) { - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), - )) => { - if ty.inference_var(Interner).is_some() { - match alias { - chalk_ir::AliasTy::Opaque(opaque) => { - if self.unify( - &chalk_ir::TyKind::OpaqueType( - opaque.opaque_ty_id, - opaque.substitution.clone(), - ) - .intern(Interner), - ty, - ) { - return None; - } - } - _ => {} - } - } - } - _ => {} - } - Some(goal) + pub(crate) fn register_obligation(&mut self, predicate: Predicate<'a>) { + let goal = next_solver::Goal { + param_env: self.trait_env.env.to_nextsolver(self.interner), + predicate, + }; + self.register_obligation_in_env(goal) } #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { return }; - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + fn register_obligation_in_env( + &mut self, + goal: next_solver::Goal<'a, next_solver::Predicate<'a>>, + ) { + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); tracing::debug!(?result); match result { Ok((_, Certainty::Yes)) => {} @@ -828,7 +838,7 @@ impl<'a> InferenceTable<'a> { } } - pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) { + pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'a, T>) { infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); } @@ -841,12 +851,7 @@ impl<'a> InferenceTable<'a> { for goal in obligations.drain(..) { tracing::debug!(obligation = ?goal); - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { - changed = true; - continue; - }; - - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); let (has_changed, certainty) = match result { Ok(result) => result, Err(_) => { @@ -934,40 +939,6 @@ impl<'a> InferenceTable<'a> { .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) } - #[tracing::instrument(level = "debug", skip(self))] - fn try_resolve_obligation( - &mut self, - canonicalized: &Canonicalized>, - ) -> NextTraitSolveResult { - let solution = next_trait_solve( - self.db, - self.trait_env.krate, - self.trait_env.block, - canonicalized.value.clone(), - ); - - tracing::debug!(?solution, ?canonicalized); - match &solution { - NextTraitSolveResult::Certain(v) => { - canonicalized.apply_solution( - self, - Canonical { - binders: v.binders.clone(), - // FIXME handle constraints - value: v.value.subst.clone(), - }, - ); - } - // ...so, should think about how to get some actually get some guidance here - NextTraitSolveResult::Uncertain(v) => { - canonicalized.apply_solution(self, v.clone()); - } - NextTraitSolveResult::NoSolution => {} - } - - solution - } - pub(crate) fn callable_sig( &mut self, ty: &Ty, @@ -1027,7 +998,7 @@ impl<'a> InferenceTable<'a> { let goal: Goal = trait_ref.clone().cast(Interner); if !self.try_obligation(goal.clone()).no_solution() { - self.register_obligation(goal); + self.register_obligation(goal.to_nextsolver(self.interner)); let return_ty = self.normalize_projection_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; @@ -1067,7 +1038,7 @@ impl<'a> InferenceTable<'a> { match ty.kind(Interner) { TyKind::Error => self.new_type_var(), TyKind::InferenceVar(..) => { - let ty_resolved = self.resolve_ty_shallow(&ty); + let ty_resolved = self.structurally_resolve_type(&ty); if ty_resolved.is_unknown() { self.new_type_var() } else { ty } } _ => ty, @@ -1165,7 +1136,9 @@ impl fmt::Debug for InferenceTable<'_> { mod resolve { use super::InferenceTable; use crate::{ - next_solver::mapping::ChalkToNextSolver, ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind + ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, + InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind, + next_solver::mapping::NextSolverToChalk, }; use chalk_ir::{ cast::Cast, @@ -1220,7 +1193,7 @@ mod resolve { .clone(); } if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1234,7 +1207,13 @@ mod resolve { } } TyVariableKind::Integer => { - let vid = self.table.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from(var.index())); + let vid = self + .table + .infer_ctxt + .inner + .borrow_mut() + .int_unification_table() + .find(IntVid::from(var.index())); let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type @@ -1244,7 +1223,7 @@ mod resolve { .clone(); } if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1258,7 +1237,13 @@ mod resolve { } } TyVariableKind::Float => { - let vid = self.table.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from(var.index())); + let vid = self + .table + .infer_ctxt + .inner + .borrow_mut() + .float_unification_table() + .find(FloatVid::from(var.index())); let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type @@ -1268,7 +1253,7 @@ mod resolve { .clone(); } if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1290,7 +1275,10 @@ mod resolve { var: InferenceVar, outer_binder: DebruijnIndex, ) -> Const { - let vid = self.table.infer_ctxt.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); + let vid = self + .table + .infer_ctxt + .root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); let var = InferenceVar::from(vid.as_u32()); let default = ConstData { ty: ty.clone(), @@ -1305,7 +1293,7 @@ mod resolve { .clone(); } if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) { - let known_const: Const = ChalkToNextSolver::from_nextsolver(known_const, self.table.interner); + let known_const: Const = known_const.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Const)); let result = known_const.fold_with(self, outer_binder); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 323ea951a9141..659bc5ade3c79 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -101,7 +101,10 @@ use crate::{ display::{DisplayTarget, HirDisplay}, generics::Generics, infer::unify::InferenceTable, - next_solver::{DbInterner, mapping::convert_ty_for_result}, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_ty_for_result}, + }, }; pub use autoderef::autoderef; @@ -957,8 +960,10 @@ pub fn callable_sig_from_fn_trait( ) .build(); - if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { - table.register_obligation(trait_ref.clone().cast(Interner)); + let goal: Goal = trait_ref.clone().cast(Interner); + let pred = goal.to_nextsolver(table.interner); + if !table.try_obligation(goal).no_solution() { + table.register_obligation(pred); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index ee223059d1e56..fa80567b1ecc0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -22,7 +22,25 @@ use stdx::never; use triomphe::Arc; use crate::{ - autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, lang_items::is_box, next_solver::{mapping::ChalkToNextSolver, SolverDefId}, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, traits::{next_trait_solve_canonical}, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause + AdtId, AliasTy, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, + GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, + VariableKind, WhereClause, + autoderef::{self, AutoderefKind}, + db::HirDatabase, + from_chalk_trait_id, from_foreign_def_id, + infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, + lang_items::is_box, + next_solver::{ + self, SolverDefId, + fulfill::FulfillmentCtxt, + infer::DefineOpaqueTypes, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, + primitive::{FloatTy, IntTy, UintTy}, + to_chalk_trait_id, + traits::next_trait_solve_canonical_in_ctxt, + utils::all_super_traits, }; /// This is used as a key for indexing impls. @@ -89,6 +107,7 @@ impl TyFingerprint { } TyKind::AssociatedType(_, _) | TyKind::OpaqueType(_, _) + | TyKind::Alias(AliasTy::Opaque(_)) | TyKind::FnDef(_, _) | TyKind::Closure(_, _) | TyKind::Coroutine(..) @@ -106,7 +125,7 @@ impl TyFingerprint { } /// Creates a TyFingerprint for looking up a trait impl. - pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option { + pub fn for_trait_impl_ns<'db>(ty: &next_solver::Ty<'db>) -> Option { use rustc_type_ir::TyKind; let fp = match (*ty).kind() { TyKind::Str => TyFingerprint::Str, @@ -523,7 +542,7 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option( db: &'db dyn HirDatabase, - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -605,7 +624,7 @@ pub struct ReceiverAdjustments { impl ReceiverAdjustments { pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = table.resolve_ty_shallow(&ty); + let mut ty = table.structurally_resolve_type(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { match autoderef::autoderef_step(table, ty.clone(), true, false) { @@ -686,7 +705,7 @@ impl ReceiverAdjustments { // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? pub(crate) fn iterate_method_candidates<'db, T>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -887,7 +906,7 @@ fn find_matching_impl( if table.try_obligation(goal.clone()).no_solution() { return None; } - table.register_obligation(goal); + table.register_obligation(goal.to_nextsolver(table.interner)); } Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) @@ -1035,7 +1054,7 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { } pub fn iterate_path_candidates<'db>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1057,7 +1076,7 @@ pub fn iterate_path_candidates<'db>( } pub fn iterate_method_candidates_dyn<'db>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1096,7 +1115,7 @@ pub fn iterate_method_candidates_dyn<'db>( // types*. let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical_ns(ty.clone()); + let ty = table.instantiate_canonical_ns(*ty); let deref_chain = autoderef_method_receiver(&mut table, ty); deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { @@ -1129,18 +1148,13 @@ pub fn iterate_method_candidates_dyn<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_with_autoref<'db>( table: &mut InferenceTable<'db>, - receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, first_adjustment: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - if matches!(receiver_ty.value.kind(), rustc_type_ir::TyKind::Bound(..)) { - // don't try to resolve methods on unknown types - return ControlFlow::Continue(()); - } - let interner = table.interner; let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { @@ -1166,12 +1180,17 @@ fn iterate_method_candidates_with_autoref<'db>( maybe_reborrowed.autoderefs += 1; } - iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; + iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; - let refed = crate::next_solver::Canonical { + let refed = next_solver::Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Not), + value: next_solver::Ty::new_ref( + interner, + next_solver::Region::error(interner), + receiver_ty.value, + rustc_ast_ir::Mutability::Not, + ), }; iterate_method_candidates_by_receiver( @@ -1179,10 +1198,15 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)), )?; - let ref_muted = crate::next_solver::Canonical { + let ref_muted = next_solver::Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Mut), + value: next_solver::Ty::new_ref( + interner, + next_solver::Region::error(interner), + receiver_ty.value, + rustc_ast_ir::Mutability::Mut, + ), }; iterate_method_candidates_by_receiver( @@ -1190,10 +1214,12 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)), )?; - if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = receiver_ty.value.kind() { + if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = + receiver_ty.value.kind() + { let const_ptr_ty = rustc_type_ir::Canonical { max_universe: rustc_type_ir::UniverseIndex::ZERO, - value: crate::next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), + value: next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), variables: receiver_ty.variables, }; iterate_method_candidates_by_receiver( @@ -1247,7 +1273,7 @@ where #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver<'db>( table: &mut InferenceTable<'db>, - receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, receiver_adjustments: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1255,7 +1281,7 @@ fn iterate_method_candidates_by_receiver<'db>( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let receiver_ty = table.instantiate_canonical_ns(receiver_ty); - let receiver_ty: crate::Ty = ChalkToNextSolver::from_nextsolver(receiver_ty, table.interner); + let receiver_ty: crate::Ty = receiver_ty.to_chalk(table.interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1270,6 +1296,7 @@ fn iterate_method_candidates_by_receiver<'db>( Some(&receiver_ty), Some(receiver_adjustments.clone()), visible_from_module, + LookupMode::MethodCall, &mut |adjustments, item, is_visible| { callback.on_inherent_method(adjustments, item, is_visible) }, @@ -1293,6 +1320,7 @@ fn iterate_method_candidates_by_receiver<'db>( name, Some(&receiver_ty), Some(receiver_adjustments.clone()), + LookupMode::MethodCall, &mut |adjustments, item, is_visible| { callback.on_trait_method(adjustments, item, is_visible) }, @@ -1304,7 +1332,7 @@ fn iterate_method_candidates_by_receiver<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_for_self_ty<'db>( - self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + self_ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1313,7 +1341,7 @@ fn iterate_method_candidates_for_self_ty<'db>( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); - let self_ty = ChalkToNextSolver::from_nextsolver(table.instantiate_canonical_ns(self_ty.clone()), table.interner); + let self_ty = table.instantiate_canonical_ns(*self_ty).to_chalk(table.interner); iterate_inherent_methods( &self_ty, &mut table, @@ -1321,6 +1349,7 @@ fn iterate_method_candidates_for_self_ty<'db>( None, None, visible_from_module, + LookupMode::Path, &mut |adjustments, item, is_visible| { callback.on_inherent_method(adjustments, item, is_visible) }, @@ -1332,6 +1361,7 @@ fn iterate_method_candidates_for_self_ty<'db>( name, None, None, + LookupMode::Path, &mut |adjustments, item, is_visible| { callback.on_trait_method(adjustments, item, is_visible) }, @@ -1346,12 +1376,13 @@ fn iterate_trait_method_candidates( name: Option<&Name>, receiver_ty: Option<&Ty>, receiver_adjustments: Option, + mode: LookupMode, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; - let canonical_self_ty = ChalkToNextSolver::from_nextsolver(table.canonicalize(self_ty.clone().to_nextsolver(table.interner)), table.interner); - let TraitEnvironment { krate, block, .. } = *table.trait_env; + let canonical_self_ty = table.canonicalize(self_ty.clone().to_nextsolver(table.interner)); + let TraitEnvironment { krate, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { let data = db.trait_signature(t); @@ -1391,15 +1422,22 @@ fn iterate_trait_method_candidates( for &(_, item) in t.trait_items(db).items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - let visible = - match is_valid_trait_method_candidate(table, t, name, receiver_ty, item, self_ty) { - IsValidCandidate::Yes => true, - IsValidCandidate::NotVisible => false, - IsValidCandidate::No => continue, - }; + let visible = match is_valid_trait_method_candidate( + table, + t, + name, + receiver_ty, + item, + self_ty, + mode, + ) { + IsValidCandidate::Yes => true, + IsValidCandidate::NotVisible => false, + IsValidCandidate::No => continue, + }; if !known_implemented { - let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); - if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() { + let goal = generic_implements_goal_ns(table, t, canonical_self_ty); + if next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() { continue 'traits; } } @@ -1418,6 +1456,7 @@ fn iterate_inherent_methods( receiver_ty: Option<&Ty>, receiver_adjustments: Option, visible_from_module: VisibleFromModule, + mode: LookupMode, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; @@ -1441,6 +1480,7 @@ fn iterate_inherent_methods( receiver_adjustments.clone(), callback, traits, + mode, )?; } TyKind::Dyn(_) => { @@ -1454,6 +1494,7 @@ fn iterate_inherent_methods( receiver_adjustments.clone(), callback, traits.into_iter(), + mode, )?; } } @@ -1512,6 +1553,7 @@ fn iterate_inherent_methods( receiver_adjustments: Option, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, traits: impl Iterator, + mode: LookupMode, ) -> ControlFlow<()> { let db = table.db; for t in traits { @@ -1525,6 +1567,7 @@ fn iterate_inherent_methods( receiver_ty, item, self_ty, + mode, ) { IsValidCandidate::Yes => true, IsValidCandidate::NotVisible => false, @@ -1571,22 +1614,17 @@ fn iterate_inherent_methods( } /// Returns the receiver type for the index trait call. -pub(crate) fn resolve_indexing_op( - db: &dyn HirDatabase, - env: Arc, - ty: Canonical, +pub(crate) fn resolve_indexing_op<'db>( + table: &mut InferenceTable<'db>, + ty: next_solver::Canonical<'db, next_solver::Ty<'db>>, index_trait: TraitId, ) -> Option { - let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical(ty); - let interner = table.interner; - let deref_chain = autoderef_method_receiver(&mut table, ty.to_nextsolver(interner)); + let ty = table.instantiate_canonical_ns(ty); + let deref_chain = autoderef_method_receiver(table, ty); for (ty, adj) in deref_chain { //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty); - let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ChalkToNextSolver::from_nextsolver(ty, interner)); - let goal: chalk_ir::Canonical>> = goal.cast(Interner); - let goal = goal.to_nextsolver(interner); - if !next_trait_solve_canonical(db, table.trait_env.krate, table.trait_env.block, goal).no_solution() { + let goal = generic_implements_goal_ns(table, index_trait, ty); + if !next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() { return Some(adj); } } @@ -1666,6 +1704,7 @@ fn is_valid_trait_method_candidate( receiver_ty: Option<&Ty>, item: AssocItemId, self_ty: &Ty, + mode: LookupMode, ) -> IsValidCandidate { let db = table.db; match item { @@ -1693,6 +1732,35 @@ fn is_valid_trait_method_candidate( let expected_receiver = sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + // FIXME: Clean up this mess with some context struct like rustc's `ProbeContext` + let variance = match mode { + LookupMode::MethodCall => rustc_type_ir::Variance::Covariant, + LookupMode::Path => rustc_type_ir::Variance::Invariant, + }; + let res = table + .infer_ctxt + .at( + &next_solver::infer::traits::ObligationCause::dummy(), + table.trait_env.env.to_nextsolver(table.interner), + ) + .relate( + DefineOpaqueTypes::No, + expected_receiver.to_nextsolver(table.interner), + variance, + receiver_ty.to_nextsolver(table.interner), + ); + let Ok(infer_ok) = res else { + return IsValidCandidate::No; + }; + + if !infer_ok.obligations.is_empty() { + let mut ctxt = FulfillmentCtxt::new(&table.infer_ctxt); + for pred in infer_ok.into_obligations() { + ctxt.register_predicate_obligation(&table.infer_ctxt, pred); + } + check_that!(ctxt.select_all_or_error(&table.infer_ctxt).is_empty()); + } + check_that!(table.unify(receiver_ty, &expected_receiver)); } @@ -1839,53 +1907,36 @@ fn generic_implements_goal( Canonical { binders, value } } -/* /// This creates Substs for a trait with the given Self type and type variables /// for all other parameters, to query the trait solver with it. #[tracing::instrument(skip_all)] fn generic_implements_goal_ns<'db>( - db: &'db dyn HirDatabase, - interner: DbInterner<'db>, - env: &TraitEnvironment, + table: &mut InferenceTable<'db>, trait_: TraitId, - self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, -) -> crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { - let variables = self_ty.variables; - let trait_ref = TyBuilder::trait_ref(db, trait_) - .push(ChalkToNextSolver::from_nextsolver(self_ty.value, interner)) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, variables.len()) - .build(); - - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let args = infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); - - rustc_type_ir::TraitRef::new(interner, SolverDefId::TraitId(trait_)).with_self_ty(interner, self_ty.value); - - - let kinds = - binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { - let vk = match it.data(Interner) { - GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), - GenericArgData::Lifetime(_) => VariableKind::Lifetime, - GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()), - }; - WithKind::new(vk, UniverseIndex::ROOT) - })); - let binders = CanonicalVarKinds::from_iter(Interner, kinds); + self_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, +) -> next_solver::Canonical<'db, next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { + let args = table.infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); + let self_ty = table.instantiate_canonical_ns(self_ty); + let trait_ref = + rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args) + .with_replaced_self_ty(table.infer_ctxt.interner, self_ty); + let goal = next_solver::Goal::new( + table.infer_ctxt.interner, + table.trait_env.env.to_nextsolver(table.infer_ctxt.interner), + trait_ref, + ); - let obligation = trait_ref.cast(Interner); - let value = InEnvironment::new(&env.env, obligation); - crate::next_solver::Canonical { max_universe, value, variables } + table.canonicalize(goal) } -*/ fn autoderef_method_receiver<'db>( table: &mut InferenceTable<'db>, - ty: crate::next_solver::Ty<'db>, -) -> Vec<(crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { + ty: next_solver::Ty<'db>, +) -> Vec<(next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { let interner = table.interner; let mut deref_chain = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ChalkToNextSolver::from_nextsolver(ty, interner), false, true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, ty.to_chalk(interner), false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty.to_nextsolver(interner)), @@ -1894,11 +1945,11 @@ fn autoderef_method_receiver<'db>( } // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) = - deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables.clone(), d.0.max_universe, d.1.clone())) + deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables, d.0.max_universe, d.1.clone())) { - let unsized_ty = crate::next_solver::Ty::new_slice(interner, parameters); + let unsized_ty = next_solver::Ty::new_slice(interner, parameters); deref_chain.push(( - crate::next_solver::Canonical { max_universe, value: unsized_ty, variables, }, + next_solver::Canonical { max_universe, value: unsized_ty, variables }, ReceiverAdjustments { unsize_array: true, ..adj.clone() }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 8c48a16537a2e..6465099dffd7f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -34,8 +34,7 @@ pub use eval::{ }; pub use lower::{MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_body_query}; pub use monomorphization::{ - monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, - monomorphized_mir_body_query, + monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; use rustc_hash::FxHashMap; use smallvec::{SmallVec, smallvec}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index b0b922316914b..555b87850924c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -38,7 +38,6 @@ struct Filler<'a> { trait_env: Arc, subst: &'a Substitution, generics: Option, - owner: DefWithBodyId, } impl FallibleTypeFolder for Filler<'_> { type Error = MirLowerError; @@ -66,7 +65,11 @@ impl FallibleTypeFolder for Filler<'_> { })) .intern(Interner)) } - TyKind::OpaqueType(id, subst) => { + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: id, + substitution: subst, + })) + | TyKind::OpaqueType(id, subst) => { let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; match impl_trait_id { @@ -74,7 +77,6 @@ impl FallibleTypeFolder for Filler<'_> { let infer = self.db.infer(func.into()); let filler = &mut Filler { db: self.db, - owner: self.owner, trait_env: self.trait_env.clone(), subst: &subst, generics: Some(generics(self.db, func.into())), @@ -306,7 +308,7 @@ pub fn monomorphized_mir_body_query( trait_env: Arc, ) -> Result, MirLowerError> { let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let filler = &mut Filler { db, subst: &subst, trait_env, generics }; let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; @@ -330,23 +332,9 @@ pub fn monomorphized_mir_body_for_closure_query( ) -> Result, MirLowerError> { let InternedClosure(owner, _) = db.lookup_intern_closure(closure); let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let filler = &mut Filler { db, subst: &subst, trait_env, generics }; let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; Ok(Arc::new(body)) } - -// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query. -pub fn monomorphize_mir_body_bad( - db: &dyn HirDatabase, - mut body: MirBody, - subst: Substitution, - trait_env: Arc, -) -> Result { - let owner = body.owner; - let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; - filler.fill_body(&mut body)?; - Ok(body) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 21762aa66a858..0225deebe4f31 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -5,11 +5,10 @@ pub mod abi; mod consts; mod def_id; pub mod fold; -mod fulfill; +pub mod fulfill; mod generic_arg; pub mod generics; pub mod infer; -//mod infer_new; pub mod interner; mod ir_print; pub mod mapping; @@ -24,7 +23,6 @@ pub mod util; pub use consts::*; pub use def_id::*; pub use generic_arg::*; -//pub use infer_new::*; pub use interner::*; pub use opaques::*; pub use predicate::*; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index 0820006e5dfb0..5d11525cd199f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -1,14 +1,28 @@ +//! This module contains code to canonicalize values into a `Canonical<'db, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use rustc_hash::FxHashMap; use rustc_index::Idx; -use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; -use rustc_type_ir::{BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex}; +use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; +use rustc_type_ir::{ + BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, + RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + UniverseIndex, +}; use smallvec::SmallVec; use tracing::debug; use crate::next_solver::infer::InferCtxt; -use crate::next_solver::{Binder, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, TyKind}; +use crate::next_solver::{ + Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, + CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, + TyKind, +}; /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores @@ -65,7 +79,7 @@ impl<'db> InferCtxt<'db> { // `param_env` because they are treated differently by trait selection. let canonical_param_env = Canonicalizer::canonicalize( param_env, - None, + self, self.interner, &CanonicalizeFreeRegionsOtherThanStatic, query_state, @@ -74,7 +88,7 @@ impl<'db> InferCtxt<'db> { let canonical = Canonicalizer::canonicalize_with_base( canonical_param_env, value, - Some(self), + self, self.interner, &CanonicalizeAllFreeRegions, query_state, @@ -115,7 +129,7 @@ impl<'db> InferCtxt<'db> { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( value, - Some(self), + self, self.interner, &CanonicalizeQueryResponse, &mut query_state, @@ -129,7 +143,7 @@ impl<'db> InferCtxt<'db> { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( value, - Some(self), + self, self.interner, &CanonicalizeUserTypeAnnotation, &mut query_state, @@ -165,7 +179,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { canonicalizer: &mut Canonicalizer<'_, 'db>, mut r: Region<'db>, ) -> Region<'db> { - let infcx = canonicalizer.infcx.unwrap(); + let infcx = canonicalizer.infcx; if let RegionKind::ReVar(vid) = r.kind() { r = infcx @@ -180,12 +194,14 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { }; match r.kind() { - RegionKind::ReLateParam(_) | RegionKind::ReErased | RegionKind::ReStatic | RegionKind::ReEarlyParam(..) | RegionKind::ReError(..) => r, + RegionKind::ReLateParam(_) + | RegionKind::ReErased + | RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(..) => r, - RegionKind::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( - CanonicalVarKind::PlaceholderRegion(placeholder), - r, - ), + RegionKind::RePlaceholder(placeholder) => canonicalizer + .canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r), RegionKind::ReVar(vid) => { let universe = infcx @@ -194,10 +210,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { .unwrap_region_constraints() .probe_value(vid) .unwrap_err(); - canonicalizer.canonical_var_for_region( - CanonicalVarKind::Region(universe), - r, - ) + canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r) } _ => { @@ -240,7 +253,7 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { RegionKind::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), RegionKind::RePlaceholder(..) | RegionKind::ReBound(..) => { // We only expect region names that the user can type. - panic!("unexpected region in query response: `{:?}`", r) + panic!("unexpected region in query response: `{r:?}`") } } } @@ -296,7 +309,7 @@ impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { struct Canonicalizer<'cx, 'db> { /// Set to `None` to disable the resolution of inference variables. - infcx: Option<&'cx InferCtxt<'db>>, + infcx: &'cx InferCtxt<'db>, tcx: DbInterner<'db>, variables: SmallVec<[CanonicalVarKind<'db>; 8]>, query_state: &'cx mut OriginalQueryValues<'db>, @@ -350,14 +363,14 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // We need to canonicalize the *root* of our ty var. // This is so that our canonical response correctly reflects // any equated inference vars correctly! - let root_vid = self.infcx.unwrap().root_var(vid); + let root_vid = self.infcx.root_var(vid); if root_vid != vid { t = Ty::new_var(self.tcx, root_vid); vid = root_vid; } debug!("canonical: type var found with vid {:?}", vid); - match self.infcx.unwrap().probe_ty_var(vid) { + match self.infcx.probe_ty_var(vid) { // `t` could be a float / int variable; canonicalize that instead. Ok(t) => { debug!("(resolved to {:?})", t); @@ -380,29 +393,25 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { } TyKind::Infer(IntVar(vid)) => { - let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid); + let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::Int), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) } } TyKind::Infer(FloatVar(vid)) => { - let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid); + let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::Float), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) } } - TyKind::Infer(InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => { + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { panic!("encountered a fresh type during canonicalization") } @@ -410,10 +419,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { if !self.canonicalize_mode.preserve_universes() { placeholder.universe = UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarKind::PlaceholderTy(placeholder), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t) } TyKind::Bound(debruijn, _) => { @@ -465,14 +471,14 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // We need to canonicalize the *root* of our const var. // This is so that our canonical response correctly reflects // any equated inference vars correctly! - let root_vid = self.infcx.unwrap().root_const_var(vid); + let root_vid = self.infcx.root_const_var(vid); if root_vid != vid { ct = Const::new_var(self.tcx, root_vid); vid = root_vid; } debug!("canonical: const var found with vid {:?}", vid); - match self.infcx.unwrap().probe_const_var(vid) { + match self.infcx.probe_const_var(vid) { Ok(c) => { debug!("(resolved to {:?})", c); return self.fold_const(c); @@ -485,10 +491,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // FIXME: perf problem described in #55921. ui = UniverseIndex::ROOT; } - return self.canonicalize_const_var( - CanonicalVarKind::Const(ui), - ct, - ); + return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct); } } } @@ -503,10 +506,8 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { } } ConstKind::Placeholder(placeholder) => { - return self.canonicalize_const_var( - CanonicalVarKind::PlaceholderConst(placeholder), - ct, - ); + return self + .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct); } _ => {} } @@ -524,7 +525,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// `canonicalize_query` and `canonicalize_response`. fn canonicalize( value: V, - infcx: Option<&InferCtxt<'db>>, + infcx: &InferCtxt<'db>, tcx: DbInterner<'db>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'db>, @@ -551,7 +552,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { fn canonicalize_with_base( base: Canonical<'db, U>, value: V, - infcx: Option<&InferCtxt<'db>>, + infcx: &InferCtxt<'db>, tcx: DbInterner<'db>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'db>, @@ -596,7 +597,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { // anymore. debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); - let canonical_variables = CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); + let canonical_variables = + CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -690,15 +692,11 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { self.variables .iter() .map(|v| match *v { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { - return *v; - } + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => *v, CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) } - CanonicalVarKind::Region(u) => { - CanonicalVarKind::Region(reverse_universe_map[&u]) - } + CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { CanonicalVarKind::PlaceholderTy(Placeholder { @@ -735,14 +733,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// /// (This works because unification never fails -- and hence trait /// selection is never affected -- due to a universe mismatch.) - fn canonical_var_for_region_in_root_universe( - &mut self, - r: Region<'db>, - ) -> Region<'db> { - self.canonical_var_for_region( - CanonicalVarKind::Region(UniverseIndex::ROOT), - r, - ) + fn canonical_var_for_region_in_root_universe(&mut self, r: Region<'db>) -> Region<'db> { + self.canonical_var_for_region(CanonicalVarKind::Region(UniverseIndex::ROOT), r) } /// Creates a canonical variable (with the given `info`) @@ -762,9 +754,13 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// *that*. Otherwise, create a new canonical variable for /// `ty_var`. fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> { - debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); + debug_assert_eq!(ty_var, self.infcx.shallow_resolve(ty_var)); let var = self.canonical_var(info, ty_var.into()); - Ty::new_bound(self.tcx, self.binder_index, BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }) + Ty::new_bound( + self.tcx, + self.binder_index, + BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }, + ) } /// Given a type variable `const_var` of the given kind, first check @@ -776,10 +772,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { info: CanonicalVarKind<'db>, const_var: Const<'db>, ) -> Const<'db> { - debug_assert!( - !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) - ); + debug_assert_eq!(const_var, self.infcx.shallow_resolve_const(const_var)); let var = self.canonical_var(info, const_var.into()); - Const::new_bound(self.tcx, self.binder_index, var) + Const::new_bound(self.tcx, self.binder_index, BoundConst { var }) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 894c91e0f49db..2630f2a8cc4e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -503,7 +503,10 @@ impl<'db> InferCtxt<'db> { } pub fn next_ty_vid(&self) -> TyVid { - self.inner.borrow_mut().type_variables().new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + self.inner + .borrow_mut() + .type_variables() + .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) } pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { @@ -526,11 +529,13 @@ impl<'db> InferCtxt<'db> { } pub fn next_const_vid(&self) -> ConstVid { - self - .inner + self.inner .borrow_mut() .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin: ConstVariableOrigin { param_def_id: None }, universe: self.universe() }) + .new_key(ConstVariableValue::Unknown { + origin: ConstVariableOrigin { param_def_id: None }, + universe: self.universe(), + }) .vid } @@ -566,9 +571,7 @@ impl<'db> InferCtxt<'db> { } pub fn next_float_var(&self) -> Ty<'db> { - let next_float_var_id = - self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); - Ty::new_float_var(self.interner, next_float_var_id) + Ty::new_float_var(self.interner, self.next_float_vid()) } pub fn next_float_vid(&self) -> FloatVid { @@ -815,9 +818,7 @@ impl<'db> InferCtxt<'db> { match value { IntVarValue::IntType(ty) => Some(Ty::new_int(self.interner, ty)), IntVarValue::UintType(ty) => Some(Ty::new_uint(self.interner, ty)), - IntVarValue::Unknown => { - None - } + IntVarValue::Unknown => None, } } @@ -839,9 +840,7 @@ impl<'db> InferCtxt<'db> { let value = inner.float_unification_table().probe_value(vid); match value { FloatVarValue::Known(ty) => Some(Ty::new_float(self.interner, ty)), - FloatVarValue::Unknown => { - None - } + FloatVarValue::Unknown => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 79c402ff29c3d..f66b8dace30a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -141,7 +141,10 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db pub trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; - fn from_nextsolver(out: Out, interner: DbInterner<'db>) -> Self; +} + +pub trait NextSolverToChalk<'db, Out> { + fn to_chalk(self, interner: DbInterner<'db>) -> Out; } impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { @@ -428,342 +431,11 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }, ) } +} - fn from_nextsolver(out: Ty<'db>, interner: DbInterner<'db>) -> Self { - use crate::{Scalar, TyKind}; - use chalk_ir::{FloatTy, IntTy, UintTy}; - match out.kind() { - rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), - rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { - TyKind::Scalar(Scalar::Int(IntTy::I8)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { - TyKind::Scalar(Scalar::Int(IntTy::I16)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { - TyKind::Scalar(Scalar::Int(IntTy::I32)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { - TyKind::Scalar(Scalar::Int(IntTy::I64)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { - TyKind::Scalar(Scalar::Int(IntTy::I128)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { - TyKind::Scalar(Scalar::Int(IntTy::Isize)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { - TyKind::Scalar(Scalar::Uint(UintTy::U8)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { - TyKind::Scalar(Scalar::Uint(UintTy::U16)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { - TyKind::Scalar(Scalar::Uint(UintTy::U32)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { - TyKind::Scalar(Scalar::Uint(UintTy::U64)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { - TyKind::Scalar(Scalar::Uint(UintTy::U128)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { - TyKind::Scalar(Scalar::Uint(UintTy::Usize)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { - TyKind::Scalar(Scalar::Float(FloatTy::F16)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { - TyKind::Scalar(Scalar::Float(FloatTy::F32)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { - TyKind::Scalar(Scalar::Float(FloatTy::F64)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { - TyKind::Scalar(Scalar::Float(FloatTy::F128)) - } - rustc_type_ir::TyKind::Str => TyKind::Str, - rustc_type_ir::TyKind::Error(_) => TyKind::Error, - rustc_type_ir::TyKind::Never => TyKind::Never, - - rustc_type_ir::TyKind::Adt(def, args) => { - let adt_id = def.inner().id; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Adt(chalk_ir::AdtId(adt_id), subst) - } - - rustc_type_ir::TyKind::Infer(infer_ty) => { - let (var, kind) = match infer_ty { - rustc_type_ir::InferTy::TyVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::General) - } - rustc_type_ir::InferTy::IntVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) - } - rustc_type_ir::InferTy::FloatVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Float) - } - _ => todo!(), - }; - TyKind::InferenceVar(var, kind) - } - - rustc_type_ir::TyKind::Ref(r, ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let r = ChalkToNextSolver::from_nextsolver(r, interner); - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Ref(mutability, r, ty) - } - - rustc_type_ir::TyKind::Tuple(tys) => { - let size = tys.len(); - let subst = Substitution::from_iter( - Interner, - tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver( - ty, interner, - )) - .intern(Interner) - }), - ); - TyKind::Tuple(size, subst) - } - - rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); - TyKind::Array(ty, const_) - } - - rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { - rustc_type_ir::AliasTyKind::Projection => { - let assoc_ty_id = match alias_ty.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); - TyKind::AssociatedType(associated_ty_id, substitution) - } - rustc_type_ir::AliasTyKind::Opaque => { - let opaque_ty_id = match alias_ty.def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); - TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { - opaque_ty_id: opaque_ty_id.into(), - substitution, - })) - } - rustc_type_ir::AliasTyKind::Inherent => todo!(), - rustc_type_ir::AliasTyKind::Free => todo!(), - }, - - rustc_type_ir::TyKind::Placeholder(placeholder) => { - let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; - let placeholder_index = - chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; - TyKind::Placeholder(placeholder_index) - } - - rustc_type_ir::TyKind::Bound(debruijn_index, ty) => { - TyKind::BoundVar(chalk_ir::BoundVar { - debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - index: ty.var.as_usize(), - }) - } - - rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { - let num_binders = bound_sig.bound_vars().len(); - let sig = chalk_ir::FnSig { - abi: fn_header.abi, - safety: match fn_header.safety { - crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, - crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - }, - variadic: fn_header.c_variadic, - }; - let args = GenericArgs::new_from_iter( - interner, - bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), - ); - let substitution = ChalkToNextSolver::from_nextsolver(args, interner); - let substitution = chalk_ir::FnSubst(substitution); - let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; - TyKind::Function(fnptr) - } - - rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { - assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); - let self_ty = Ty::new_bound( - interner, - DebruijnIndex::from_u32(1), - BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, - ); - let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( - Interner, - preds.iter().map(|p| { - let binders = chalk_ir::VariableKinds::from_iter( - Interner, - p.bound_vars().iter().map(|b| match b { - BoundVarKind::Ty(kind) => { - chalk_ir::VariableKind::Ty(TyVariableKind::General) - } - BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, - BoundVarKind::Const => chalk_ir::VariableKind::Const( - crate::TyKind::Error.intern(Interner), - ), - }), - ); - let where_clause = match p.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = TraitRef::new( - interner, - trait_ref.def_id, - [self_ty.clone().into()] - .into_iter() - .chain(trait_ref.args.iter()), - ); - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { - let trait_id = match trait_ { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = chalk_ir::Substitution::empty(Interner); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::Projection( - existential_projection, - ) => { - let projection = ProjectionPredicate { - projection_term: AliasTerm::new( - interner, - existential_projection.def_id, - [self_ty.clone().into()] - .iter() - .chain(existential_projection.args.clone().iter()), - ), - term: existential_projection.term.clone(), - }; - let associated_ty_id = match projection.projection_term.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver( - projection.projection_term.args, - interner, - ); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match projection.term { - Term::Ty(ty) => ty, - _ => unreachable!(), - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::WhereClause::AliasEq(alias_eq) - } - }; - chalk_ir::Binders::new(binders, where_clause) - }), - ); - let binders = chalk_ir::VariableKinds::from1( - Interner, - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), - ); - let bounds = chalk_ir::Binders::new(binders, bounds); - let dyn_ty = chalk_ir::DynTy { - bounds, - lifetime: ChalkToNextSolver::from_nextsolver(region, interner), - }; - TyKind::Dyn(dyn_ty) - } - - rustc_type_ir::TyKind::Slice(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Slice(ty) - } - - rustc_type_ir::TyKind::Foreign(def_id) => { - let id = match def_id { - SolverDefId::TypeAliasId(id) => to_foreign_def_id(id), - _ => unreachable!(), - }; - TyKind::Foreign(id) - } - rustc_type_ir::TyKind::Pat(_, _) => todo!(), - rustc_type_ir::TyKind::RawPtr(ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Raw(mutability, ty) - } - rustc_type_ir::TyKind::FnDef(def_id, args) => { - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - match def_id { - SolverDefId::FunctionId(id) => { - TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Enum(e)) => TyKind::FnDef( - CallableDefId::EnumVariantId(e).to_chalk(interner.db()), - subst, - ), - SolverDefId::Ctor(Ctor::Struct(s)) => { - TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) - } - _ => unreachable!("Unexpected def id {:?}", def_id), - } - } - - rustc_type_ir::TyKind::Closure(def_id, args) => { - let id = match def_id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Closure(id.into(), subst) - } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), - rustc_type_ir::TyKind::Coroutine(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Coroutine(id.into(), subst) - } - rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::CoroutineWitness(id.into(), subst) - } - - rustc_type_ir::TyKind::Param(_) => todo!(), - rustc_type_ir::TyKind::UnsafeBinder(_) => todo!(), - } - .intern(Interner) +impl<'db> NextSolverToChalk<'db, chalk_ir::Ty> for Ty<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Ty { + convert_ty_for_result(interner, self) } } @@ -799,39 +471,11 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { }, ) } +} - fn from_nextsolver(out: Region<'db>, interner: DbInterner<'db>) -> Self { - match out.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(early) => todo!(), - rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.var.as_usize(), - )), - ), - rustc_type_ir::RegionKind::ReLateParam(_) => todo!(), - rustc_type_ir::RegionKind::ReStatic => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) - } - rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), - ), - rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { - idx: placeholder.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, - }), - ), - rustc_type_ir::RegionKind::ReErased => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) - } - rustc_type_ir::RegionKind::ReError(_) => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) - } - } +impl<'db> NextSolverToChalk<'db, chalk_ir::Lifetime> for Region<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Lifetime { + convert_region_for_result(interner, self) } } @@ -877,61 +521,11 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { }, ) } +} - fn from_nextsolver(out: Const<'db>, interner: DbInterner<'db>) -> Self { - let value: chalk_ir::ConstValue = match out.kind() { - rustc_type_ir::ConstKind::Param(_) => unimplemented!(), - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { - chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) - } - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { - panic!("Vars should not be freshened.") - } - rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { - chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - var.index(), - )) - } - rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { - chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, - idx: placeholder_const.bound.as_usize(), - }) - } - rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { - let id = match unevaluated_const.def { - SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), - SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::UnevaluatedConst(id, subst), - }) - } - rustc_type_ir::ConstKind::Value(value_const) => { - let bytes = value_const.value.inner(); - let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - // SAFETY: will never use this without a db - interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { - std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) - }), - }); - return chalk_ir::ConstData { - ty: ChalkToNextSolver::from_nextsolver(value_const.ty, interner), - value, - } - .intern(Interner); - } - rustc_type_ir::ConstKind::Error(_) => { - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Unknown, - }) - } - rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), - }; - chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) +impl<'db> NextSolverToChalk<'db, chalk_ir::Const> for Const<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Const { + convert_const_for_result(interner, self) } } @@ -946,13 +540,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> ), } } - - fn from_nextsolver( - out: rustc_type_ir::FnSigTys>, - interner: DbInterner<'db>, - ) -> Self { - todo!() - } } impl< @@ -971,13 +558,6 @@ impl< binders.to_nextsolver(interner), ) } - - fn from_nextsolver( - out: rustc_type_ir::Binder, U>, - interner: DbInterner<'db>, - ) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { @@ -987,10 +567,6 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { @@ -1001,10 +577,6 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind BoundVarKind::Const, } } - - fn from_nextsolver(out: BoundVarKind, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { @@ -1015,11 +587,8 @@ impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg const_.to_nextsolver(interner).into(), } } - - fn from_nextsolver(out: GenericArg<'db>, interner: DbInterner<'db>) -> Self { - todo!() - } } + impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { GenericArgs::new_from_iter( @@ -1027,30 +596,11 @@ impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution GenericArg<'db> { arg.to_nextsolver(interner) }), ) } +} - fn from_nextsolver(out: GenericArgs<'db>, interner: DbInterner<'db>) -> Self { - let mut substs = Vec::with_capacity(out.len()); - for arg in out.iter() { - match arg.clone().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Lifetime(region) => { - let lifetime = ChalkToNextSolver::from_nextsolver(region, interner); - substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Const(const_) => { - substs.push( - chalk_ir::GenericArgData::Const(ChalkToNextSolver::from_nextsolver( - const_, interner, - )) - .intern(Interner), - ); - } - } - } - Substitution::from_iter(Interner, substs) +impl<'db> NextSolverToChalk<'db, chalk_ir::Substitution> for GenericArgs<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Substitution { + convert_args_for_result(interner, self.as_slice()) } } @@ -1067,29 +617,29 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution }), ) } - - fn from_nextsolver(out: Tys<'db>, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) } +} - fn from_nextsolver(out: rustc_type_ir::DebruijnIndex, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db> NextSolverToChalk<'db, chalk_ir::DebruijnIndex> for rustc_type_ir::DebruijnIndex { + fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::DebruijnIndex { + chalk_ir::DebruijnIndex::new(self.index() as u32) } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) } +} - fn from_nextsolver(out: rustc_type_ir::UniverseIndex, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db> NextSolverToChalk<'db, chalk_ir::UniverseIndex> for rustc_type_ir::UniverseIndex { + fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::UniverseIndex { + chalk_ir::UniverseIndex { counter: self.index() } } } @@ -1109,10 +659,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> } } } - - fn from_nextsolver(out: rustc_type_ir::InferTy, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { @@ -1122,10 +668,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutabil chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, } } - - fn from_nextsolver(out: rustc_ast_ir::Mutability, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { @@ -1137,10 +679,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, } } - - fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance { @@ -1151,10 +689,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance chalk_ir::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, } } - - fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances { @@ -1164,10 +698,6 @@ impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances self.as_slice(Interner).iter().map(|v| v.to_nextsolver(interner)), ) } - - fn from_nextsolver(out: VariancesOf, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> @@ -1180,14 +710,18 @@ impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> self.goal.to_nextsolver(interner), ) } +} - fn from_nextsolver( - out: Goal, Predicate<'db>>, +impl<'db> NextSolverToChalk<'db, chalk_ir::InEnvironment>> + for Goal, Predicate<'db>> +{ + fn to_chalk( + self, interner: DbInterner<'db>, - ) -> Self { + ) -> chalk_ir::InEnvironment> { chalk_ir::InEnvironment { - environment: ChalkToNextSolver::from_nextsolver(out.param_env, interner), - goal: ChalkToNextSolver::from_nextsolver(out.predicate, interner), + environment: self.param_env.to_chalk(interner), + goal: self.predicate.to_chalk(interner), } } } @@ -1224,11 +758,15 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> variables, } } +} - fn from_nextsolver(out: Canonical<'db, U>, interner: DbInterner<'db>) -> Self { +impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> + NextSolverToChalk<'db, chalk_ir::Canonical> for Canonical<'db, T> +{ + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Canonical { let binders = chalk_ir::CanonicalVarKinds::from_iter( Interner, - out.variables.iter().map(|v| match v { + self.variables.iter().map(|v| match v { rustc_type_ir::CanonicalVarKind::Ty( rustc_type_ir::CanonicalTyVarKind::General(ui), ) => chalk_ir::CanonicalVarKind::new( @@ -1247,20 +785,20 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> chalk_ir::UniverseIndex::root(), ) } - rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => todo!(), rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Lifetime, chalk_ir::UniverseIndex { counter: ui.as_usize() }, ), - rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => todo!(), rustc_type_ir::CanonicalVarKind::Const(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)), chalk_ir::UniverseIndex { counter: ui.as_usize() }, ), - rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => todo!(), + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), }), ); - let value = ChalkToNextSolver::from_nextsolver(out.value, interner); + let value = self.value.to_chalk(interner); chalk_ir::Canonical { binders, value } } } @@ -1281,9 +819,16 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), chalk_ir::GoalData::EqGoal(eq_goal) => { + let arg_to_term = |g: &chalk_ir::GenericArg| match g.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), + chalk_ir::GenericArgData::Const(const_) => { + Term::Const(const_.to_nextsolver(interner)) + } + chalk_ir::GenericArgData::Lifetime(lifetime) => unreachable!(), + }; let pred_kind = PredicateKind::AliasRelate( - Term::Ty(eq_goal.a.assert_ty_ref(Interner).clone().to_nextsolver(interner)), - Term::Ty(eq_goal.b.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + arg_to_term(&eq_goal.a), + arg_to_term(&eq_goal.b), rustc_type_ir::AliasRelationDirection::Equate, ); let pred_kind = @@ -1314,123 +859,11 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), } } +} - fn from_nextsolver(out: Predicate<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::Goal::new( - Interner, - match out.kind().skip_binder() { - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( - trait_pred, - )) => { - let trait_ref = - ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); - let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( - proj_predicate, - )) => { - let associated_ty_id = match proj_predicate.def_id() { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver( - proj_predicate.projection_term.args, - interner, - ); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match proj_predicate.term.kind() { - rustc_type_ir::TermKind::Ty(ty) => ty, - rustc_type_ir::TermKind::Const(_) => todo!(), - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( - outlives, - )) => { - let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let where_clause = - chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { - lifetime, - ty, - }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::RegionOutlives(outlives), - ) => { - let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let where_clause = - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a, - b, - }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(_) => todo!(), - rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), - rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), - rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), - rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), - rustc_type_ir::PredicateKind::Ambiguous => todo!(), - rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), - rustc_type_ir::PredicateKind::AliasRelate( - alias_term, - ty_term, - alias_relation_direction, - ) => { - let ty = match ty_term { - Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), - Term::Const(..) => todo!(), - }; - match alias_term { - Term::Ty(alias_ty) => match alias_ty.kind() { - rustc_type_ir::TyKind::Alias(kind, alias) => match kind { - rustc_type_ir::AliasTyKind::Projection => { - let associated_ty_id = match alias.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => todo!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(alias.args, interner); - let proj_ty = - chalk_ir::ProjectionTy { associated_ty_id, substitution }; - let alias = chalk_ir::AliasTy::Projection(proj_ty); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(alias_eq), - )) - } - _ => todo!(), - }, - rustc_type_ir::TyKind::Infer(var) => { - assert!(matches!( - alias_relation_direction, - rustc_type_ir::AliasRelationDirection::Equate - )); - let a: chalk_ir::Ty<_> = - ChalkToNextSolver::from_nextsolver(alias_ty, interner); - let eq_goal = chalk_ir::EqGoal { - a: chalk_ir::GenericArgData::Ty(a).intern(Interner), - b: chalk_ir::GenericArgData::Ty(ty).intern(Interner), - }; - chalk_ir::GoalData::EqGoal(eq_goal) - } - _ => todo!("Unexpected alias term {:?}", alias_term), - }, - Term::Const(..) => todo!(), - } - } - }, - ) +impl<'db> NextSolverToChalk<'db, chalk_ir::Goal> for Predicate<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Goal { + chalk_ir::Goal::new(Interner, self.kind().skip_binder().to_chalk(interner)) } } @@ -1444,12 +877,14 @@ impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment, interner: DbInterner<'db>) -> Self { +impl<'db> NextSolverToChalk<'db, chalk_ir::Environment> for ParamEnv<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Environment { let clauses = chalk_ir::ProgramClauses::from_iter( Interner, - out.clauses.iter().map(|c| -> chalk_ir::ProgramClause { - ChalkToNextSolver::from_nextsolver(c, interner) + self.clauses.iter().filter_map(|c| -> Option> { + c.to_chalk(interner) }), ); chalk_ir::Environment { clauses } @@ -1460,15 +895,19 @@ impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause) -> Clause<'db> { Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) } +} - fn from_nextsolver(out: Clause<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::ProgramClause::new( +impl<'db> NextSolverToChalk<'db, Option>> for Clause<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> Option> { + let value: chalk_ir::ProgramClauseImplication = + as NextSolverToChalk< + 'db, + Option>, + >>::to_chalk(self.0.kind().skip_binder(), interner)?; + Some(chalk_ir::ProgramClause::new( Interner, - chalk_ir::ProgramClauseData(chalk_ir::Binders::empty( - Interner, - ChalkToNextSolver::from_nextsolver(out.0.kind().skip_binder(), interner), - )), - ) + chalk_ir::ProgramClauseData(chalk_ir::Binders::empty(Interner, value)), + )) } } @@ -1480,13 +919,25 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> assert!(self.constraints.is_empty(Interner)); self.consequence.to_nextsolver(interner) } - fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::ProgramClauseImplication { - consequence: ChalkToNextSolver::from_nextsolver(out, interner), +} + +impl<'db> NextSolverToChalk<'db, Option>> + for PredicateKind<'db> +{ + fn to_chalk( + self, + interner: DbInterner<'db>, + ) -> Option> { + let chalk_ir::GoalData::DomainGoal(consequence) = self.to_chalk(interner) else { + return None; + }; + + Some(chalk_ir::ProgramClauseImplication { + consequence, conditions: chalk_ir::Goals::empty(Interner), constraints: chalk_ir::Constraints::empty(Interner), priority: chalk_ir::ClausePriority::High, - } + }) } } @@ -1606,13 +1057,15 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal panic!("Should not be constructed."), } } +} - fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { - let domain_goal = match out { +impl<'db> NextSolverToChalk<'db, chalk_ir::GoalData> for PredicateKind<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::GoalData { + match self { rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(trait_pred)) => { - let trait_ref = ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let trait_ref = trait_pred.trait_ref.to_chalk(interner); let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( proj_predicate, @@ -1621,82 +1074,67 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver( - proj_predicate.projection_term.args, - interner, - ); + let substitution = proj_predicate.projection_term.args.to_chalk(interner); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, substitution, }); let ty = match proj_predicate.term.kind() { rustc_type_ir::TermKind::Ty(ty) => ty, - rustc_type_ir::TermKind::Const(_) => todo!(), + rustc_type_ir::TermKind::Const(_) => unimplemented!(), }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = ty.to_chalk(interner); let alias_eq = chalk_ir::AliasEq { alias, ty }; let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( outlives, )) => { - let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let lifetime = outlives.1.to_chalk(interner); + let ty = outlives.0.to_chalk(interner); let where_clause = chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { lifetime, ty }); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::RegionOutlives( outlives, )) => { - let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let a = outlives.0.to_chalk(interner); + let b = outlives.1.to_chalk(interner); let where_clause = chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a, b }); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } - rustc_type_ir::PredicateKind::Clause(_) => todo!(), - rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), - rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), - rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), - rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), - rustc_type_ir::PredicateKind::Ambiguous => todo!(), - rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), rustc_type_ir::PredicateKind::AliasRelate( alias_term, - ty_term, + target_term, alias_relation_direction, ) => { - let alias = match alias_term { - Term::Ty(alias_ty) => match alias_ty.kind() { - rustc_type_ir::TyKind::Alias(kind, alias) => match kind { - rustc_type_ir::AliasTyKind::Projection => { - let associated_ty_id = match alias.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => todo!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(alias.args, interner); - let proj_ty = - chalk_ir::ProjectionTy { associated_ty_id, substitution }; - chalk_ir::AliasTy::Projection(proj_ty) - } - _ => todo!(), - }, - _ => todo!(), - }, - Term::Const(..) => todo!(), - }; - let ty = match ty_term { - Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), - Term::Const(..) => todo!(), + let term_to_generic_arg = |term: Term<'db>| match term { + Term::Ty(ty) => chalk_ir::GenericArg::new( + Interner, + chalk_ir::GenericArgData::Ty(ty.to_chalk(interner)), + ), + Term::Const(const_) => chalk_ir::GenericArg::new( + Interner, + chalk_ir::GenericArgData::Const(const_.to_chalk(interner)), + ), }; - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::AliasEq(alias_eq)) + + chalk_ir::GoalData::EqGoal(chalk_ir::EqGoal { + a: term_to_generic_arg(alias_term), + b: term_to_generic_arg(target_term), + }) } - }; - domain_goal + rustc_type_ir::PredicateKind::Clause(_) => unimplemented!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => unimplemented!(), + rustc_type_ir::PredicateKind::Subtype(_) => unimplemented!(), + rustc_type_ir::PredicateKind::Coerce(_) => unimplemented!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => unimplemented!(), + rustc_type_ir::PredicateKind::Ambiguous => unimplemented!(), + rustc_type_ir::PredicateKind::NormalizesTo(_) => unimplemented!(), + } } } @@ -1705,13 +1143,12 @@ impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef let args = self.substitution.to_nextsolver(interner); TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } +} - fn from_nextsolver(out: TraitRef<'db>, interner: DbInterner<'db>) -> Self { - let trait_id = match out.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver(out.args, interner); +impl<'db> NextSolverToChalk<'db, chalk_ir::TraitRef> for TraitRef<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::TraitRef { + let trait_id = to_chalk_trait_id(self.def_id.0); + let substitution = self.args.to_chalk(interner); chalk_ir::TraitRef { trait_id, substitution } } } @@ -1754,9 +1191,17 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db, I> NextSolverToChalk<'db, chalk_ir::ConstrainedSubst> for I +where + I: IntoIterator>, +{ + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::ConstrainedSubst { + chalk_ir::ConstrainedSubst { + constraints: chalk_ir::Constraints::empty(Interner), + subst: GenericArgs::new_from_iter(interner, self).to_chalk(interner), + } } } @@ -1764,51 +1209,7 @@ pub fn convert_canonical_args_for_result<'db>( interner: DbInterner<'db>, args: Canonical<'db, Vec>>, ) -> chalk_ir::Canonical> { - let Canonical { value, variables, max_universe } = args; - let binders = CanonicalVarKinds::from_iter( - Interner, - variables.iter().map(|v| match v { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::General), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Integer), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Float), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new( - chalk_ir::VariableKind::Lifetime, - chalk_ir::UniverseIndex::ROOT, - ), - rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new( - chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)), - chalk_ir::UniverseIndex::ROOT, - ), - rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), - }), - ); - chalk_ir::Canonical { - binders, - value: chalk_ir::ConstrainedSubst { - constraints: chalk_ir::Constraints::empty(Interner), - subst: ChalkToNextSolver::from_nextsolver( - GenericArgs::new_from_iter(interner, value), - interner, - ), - }, - } + args.to_chalk(interner) } pub fn convert_args_for_result<'db>( @@ -1897,7 +1298,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Adt(def, args) => { let adt_id = def.inner().id; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Adt(chalk_ir::AdtId(adt_id), subst) } @@ -1912,7 +1313,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::InferTy::FloatVar(var) => { (InferenceVar::from(var.as_u32()), TyVariableKind::Float) } - _ => todo!(), + rustc_type_ir::InferTy::FreshFloatTy(..) + | rustc_type_ir::InferTy::FreshIntTy(..) + | rustc_type_ir::InferTy::FreshTy(..) => { + panic!("Freshening shouldn't happen.") + } }; TyKind::InferenceVar(var, kind) } @@ -1932,7 +1337,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let subst = Substitution::from_iter( Interner, tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver(ty, interner)) + chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) .intern(Interner) }), ); @@ -1940,8 +1345,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); + let ty = convert_ty_for_result(interner, ty); + let const_ = convert_const_for_result(interner, const_); TyKind::Array(ty, const_) } @@ -1952,7 +1357,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) _ => unreachable!(), }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); TyKind::AssociatedType(associated_ty_id, substitution) } rustc_type_ir::AliasTyKind::Opaque => { @@ -1960,14 +1365,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: opaque_ty_id.into(), substitution, })) } - rustc_type_ir::AliasTyKind::Inherent => todo!(), - rustc_type_ir::AliasTyKind::Free => todo!(), + rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), + rustc_type_ir::AliasTyKind::Free => unimplemented!(), }, // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. @@ -2002,7 +1407,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) interner, bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), ); - let substitution = ChalkToNextSolver::from_nextsolver(args, interner); + let substitution = convert_args_for_result(interner, args.as_slice()); let substitution = chalk_ir::FnSubst(substitution); let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; TyKind::Function(fnptr) @@ -2045,11 +1450,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let trait_ref = TraitRef::new( interner, trait_ref.def_id, - [self_ty.clone().into()].into_iter().chain(trait_ref.args.iter()), + [self_ty.into()].into_iter().chain(trait_ref.args.iter()), ); let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = - ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); + convert_args_for_result(interner, trait_ref.args.as_slice()); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } @@ -2067,19 +1472,19 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) projection_term: AliasTerm::new( interner, existential_projection.def_id, - [self_ty.clone().into()] + [self_ty.into()] .iter() - .chain(existential_projection.args.clone().iter()), + .chain(existential_projection.args.iter()), ), - term: existential_projection.term.clone(), + term: existential_projection.term, }; let associated_ty_id = match projection.projection_term.def_id { SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver( - projection.projection_term.args, + let substitution = convert_args_for_result( interner, + projection.projection_term.args.as_slice(), ); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, @@ -2089,7 +1494,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) Term::Ty(ty) => ty, _ => unreachable!(), }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); let alias_eq = chalk_ir::AliasEq { alias, ty }; chalk_ir::WhereClause::AliasEq(alias_eq) } @@ -2108,7 +1513,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Slice(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); TyKind::Slice(ty) } @@ -2119,29 +1524,24 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; TyKind::Foreign(to_foreign_def_id(def_id)) } - rustc_type_ir::TyKind::Pat(_, _) => todo!(), + rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - match def_id { - SolverDefId::FunctionId(id) => { - TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Enum(e)) => { - TyKind::FnDef(CallableDefId::EnumVariantId(e).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Struct(s)) => { - TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) - } - _ => unreachable!("Unexpected def id {:?}", def_id), - } + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), + SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::FnDef(id.to_chalk(interner.db()), subst) } rustc_type_ir::TyKind::Closure(def_id, args) => { @@ -2149,16 +1549,16 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedClosureId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Closure(id.into(), subst) } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), + rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { let id = match def_id { SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Coroutine(id.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { @@ -2166,7 +1566,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::CoroutineWitness(id.into(), subst) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index b9b2950a94a3b..385149d78432e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,12 +9,12 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ TraitRefExt, db::HirDatabase, next_solver::{ - ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, - mapping::ChalkToNextSolver, + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, mapping::ChalkToNextSolver, util::sizedness_fast_path, }, }; @@ -200,7 +200,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(uv.args, self.interner); + let subst = uv.args.to_chalk(self.interner); let ec = self.cx().db.const_eval(c, subst, None).ok()?; Some(ec.to_nextsolver(self.interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 3b7d4d2184a59..7ec5231a7341a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -49,7 +49,7 @@ fn let_stmt_coerce() { //- minicore: coerce_unsized fn test() { let x: &[isize] = &[1]; - // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) let x: *const [isize] = &[1]; // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize) } @@ -96,7 +96,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test() { let x = if true { foo(&[1]) - // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) } else { &[1] }; @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; @@ -881,7 +881,7 @@ fn adjust_index() { fn test() { let x = [1, 2, 3]; x[2] = 6; - // ^ adjustments: Borrow(Ref('?8, Mut)) + // ^ adjustments: Borrow(Ref('?0, Mut)) } ", ); @@ -906,11 +906,11 @@ impl core::ops::IndexMut for StructMut { } fn test() { Struct[0]; - // ^^^^^^ adjustments: Borrow(Ref('?2, Not)) + // ^^^^^^ adjustments: Borrow(Ref('?0, Not)) StructMut[0]; - // ^^^^^^^^^ adjustments: Borrow(Ref('?5, Not)) + // ^^^^^^^^^ adjustments: Borrow(Ref('?1, Not)) &mut StructMut[0]; - // ^^^^^^^^^ adjustments: Borrow(Ref('?8, Mut)) + // ^^^^^^^^^ adjustments: Borrow(Ref('?2, Mut)) }", ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index df9061d23bf5d..d79639b93727b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -589,6 +589,7 @@ fn main() { "attrs_shim", "attrs_shim", "return_type_impl_traits_shim", + "generic_predicates_ns_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -605,8 +606,6 @@ fn main() { "impl_signature_shim", "impl_signature_with_source_map_shim", "callable_item_signature_shim", - "adt_variance_shim", - "variances_of_shim", "trait_impls_in_deps_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", @@ -615,7 +614,6 @@ fn main() { "impl_trait_with_diagnostics_ns_shim", "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", - "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", "lang_item", @@ -691,6 +689,7 @@ fn main() { "attrs_shim", "attrs_shim", "return_type_impl_traits_shim", + "generic_predicates_ns_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", @@ -706,7 +705,6 @@ fn main() { "impl_trait_with_diagnostics_ns_shim", "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", - "generic_predicates_ns_shim", "generic_predicates_shim", ] "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5d088e40cdeda..6490554b22b7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -202,7 +202,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<{unknown}> + 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -296,7 +296,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<{unknown}> + 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index f09b3ef6bfd0f..d50daf0f0d5be 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1876,9 +1876,9 @@ impl Foo { } fn test() { Foo.foo(); - //^^^ adjustments: Borrow(Ref('?1, Not)) + //^^^ adjustments: Borrow(Ref('?0, Not)) (&Foo).foo(); - // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)) } "#, ); @@ -1892,7 +1892,7 @@ fn receiver_adjustment_unsize_array() { fn test() { let a = [1, 2, 3]; a.len(); -} //^ adjustments: Borrow(Ref('?7, Not)), Pointer(Unsize) +} //^ adjustments: Borrow(Ref('?0, Not)), Pointer(Unsize) "#, ); } @@ -2105,7 +2105,7 @@ impl Foo { } fn test() { Box::new(Foo).foo(); - //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not)) + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?0, Not)) } "#, ); @@ -2123,7 +2123,7 @@ impl Foo { use core::mem::ManuallyDrop; fn test() { ManuallyDrop::new(Foo).foo(); - //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not)) + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?0, Not)) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 6a9135622deb6..baca7f2318e22 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -362,12 +362,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! - 151..172 'for a ...eak; }': {unknown} - 151..172 'for a ...eak; }': &'? mut {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': &'? mut <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +379,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! - 237..250 'for a in b {}': {unknown} - 237..250 'for a in b {}': &'? mut {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': &'? mut <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +395,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! - 315..337 'for a ...urn; }': {unknown} - 315..337 'for a ...urn; }': &'? mut {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': &'? mut <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 11d1a58e5367e..256ca7defb78a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -134,6 +134,9 @@ static ALIAS: AliasTy = { "#, ); + // FIXME(next-solver): This should emit type mismatch error but leaving it for now + // as we should fully migrate into next-solver without chalk-ir and TAIT should be + // reworked on r-a to handle `#[define_opaque(T)]` check_infer_with_mismatches( r#" trait Trait {} @@ -155,7 +158,6 @@ static ALIAS: i32 = { 191..193 '_a': impl Trait + ?Sized 205..211 'Struct': Struct 217..218 '5': i32 - 205..211: expected impl Trait + ?Sized, got Struct "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 4949d4016bf15..60a2641e1a20e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -48,12 +48,12 @@ fn infer_pattern() { 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': ! - 101..151 'for (e... }': {unknown} - 101..151 'for (e... }': &'? mut {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': &'? mut <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -719,28 +719,28 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'static str), i32, impl FnOnce(&'? (i32, &'static str)) -> i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 82..91 '&(1, "a")': &'? (i32, &'static str) 83..91 '(1, "a")': (i32, &'static str) 84..85 '1': i32 87..90 '"a"': &'static str - 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 - 94..101 '&(x, y)': &'? (i32, &'? str) - 95..101 '(x, y)': (i32, &'? str) + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'static str) + 95..101 '(x, y)': (i32, &'static str) 96..97 'x': i32 - 99..100 'y': &'? str + 99..100 'y': &'static str 103..104 'x': i32 - 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 + 142..145 'foo': fn foo<&'? (i32, &'static str), &'? i32, impl FnOnce(&'? (i32, &'static str)) -> &'? i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> &'? i32) -> &'? i32 142..168 'foo(&(...y)| x)': &'? i32 146..155 '&(1, "a")': &'? (i32, &'static str) 147..155 '(1, "a")': (i32, &'static str) 148..149 '1': i32 151..154 '"a"': &'static str - 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 - 158..164 '(x, y)': (i32, &'? str) + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'static str) 159..160 'x': &'? i32 - 162..163 'y': &'? &'? str + 162..163 'y': &'? &'static str 166..167 'x': &'? i32 "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 6a3f2286215f4..eacc4603ea1af 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -268,12 +268,12 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! - 32..320 'for co... }': {unknown} - 32..320 'for co... }': &'? mut {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': &'? mut <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () @@ -628,7 +628,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': () + 488..522 '{ ... }': as BoxedDsl>::Output 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -1248,7 +1248,7 @@ fn test() { 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<{unknown}> + 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index e4ee52f45eb75..97473bbabba04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -20,7 +20,7 @@ impl<'a> IntoIterator for &'a Grid { "#, expect![[r#" 150..154 'self': &'a Grid - 174..181 '{ }': impl Iterator + 174..181 '{ }': impl Iterator "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index a995a45f6e752..e7357ed5aa78b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -1954,6 +1954,8 @@ fn closure_return_inferred() { ); } +// FIXME(next-solver): `&'? str` in 231..262 seems suspicious. +// Should revisit this once we fully migrated into next-solver without chalk-ir. #[test] fn coroutine_types_inferred() { check_infer( @@ -1998,7 +2000,7 @@ fn test() { 225..360 'match ... }': () 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState + 231..262 'Pin::n...usize)': CoroutineState 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index a311e579744df..7a946f7ec7ced 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1,6 +1,8 @@ use cov_mark::check; use expect_test::expect; +use crate::tests::infer_with_mismatches; + use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; #[test] @@ -2460,7 +2462,7 @@ use core::ops::Index; type Key = ::Key; -pub trait UnificationStoreBase: Index> { +pub trait UnificationStoreBase: Index> { type Key; fn len(&self) -> usize; @@ -3634,8 +3636,7 @@ fn minimized() { #[test] fn no_builtin_binop_expectation_for_general_ty_var() { - // FIXME: Ideally type mismatch should be reported on `take_u32(42 - p)`. - check_types( + infer_with_mismatches( r#" //- minicore: add use core::ops::Add; @@ -3659,6 +3660,7 @@ fn minimized() { take_u32(42 + p); } "#, + true, ); } @@ -4188,6 +4190,8 @@ fn g(p:

::Pointer) { ); } +// FIXME(next-solver): Was `&'a T` but now getting error lifetime. +// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn gats_with_impl_trait() { // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot @@ -4211,21 +4215,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'a T + //^ &'? T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } "#, ); @@ -4255,8 +4259,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': {unknown} - 170..192 'v.get:...eref()': &'? {unknown} + 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc + 170..192 'v.get:...eref()': {unknown} "#]], ); } @@ -4931,6 +4935,8 @@ fn main() { ); } +// FIXME(next-solver): Was `>::Error` but now getting error lifetime. +// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn new_solver_crash_1() { check_infer( @@ -4947,7 +4953,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': >::Error "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 0c58e031c3a0f..a6377243ed984 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -21,9 +21,17 @@ use stdx::never; use triomphe::Arc; use crate::{ - db::HirDatabase, infer::unify::InferenceTable, next_solver::{ - infer::{DbInternerInferExt, InferCtxt}, mapping::{convert_canonical_args_for_result, ChalkToNextSolver}, util::mini_canonicalize, DbInterner, GenericArg, Predicate, SolverContext, Span - }, utils::UnevaluatedConstEvaluatorFolder, AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + DbInterner, GenericArg, Predicate, SolverContext, Span, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + util::mini_canonicalize, + }, + utils::UnevaluatedConstEvaluatorFolder, }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -282,30 +290,18 @@ pub fn next_trait_solve( } } -pub fn next_trait_solve_canonical<'db>( - db: &'db dyn HirDatabase, - krate: Crate, - block: Option, +pub fn next_trait_solve_canonical_in_ctxt<'db>( + infer_ctxt: &InferCtxt<'db>, goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, ) -> NextTraitSolveResult { - // FIXME: should use analysis_in_body, but that needs GenericDefId::Block - let context = SolverContext( - DbInterner::new_with(db, Some(krate), block) - .infer_ctxt() - .build(TypingMode::non_body_analysis()), - ); + let context = SolverContext(infer_ctxt.clone()); tracing::info!(?goal); - let (goal, var_values) = - context.instantiate_canonical(&goal); + let (goal, var_values) = context.instantiate_canonical(&goal); tracing::info!(?var_values); - let res = context.evaluate_root_goal( - goal.clone(), - Span::dummy(), - None - ); + let res = context.evaluate_root_goal(goal, Span::dummy(), None); let vars = var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); @@ -318,13 +314,10 @@ pub fn next_trait_solve_canonical<'db>( match res { Err(_) => NextTraitSolveResult::NoSolution, Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args) + convert_canonical_args_for_result(infer_ctxt.interner, args), ), Ok((_, Certainty::Maybe(_), args)) => { - let subst = convert_canonical_args_for_result( - DbInterner::new_with(db, Some(krate), block), - args, - ); + let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); NextTraitSolveResult::Uncertain(chalk_ir::Canonical { binders: subst.binders, value: subst.value.subst, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 79a154a5d8e6e..c230bbad0bc45 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -14,7 +14,11 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::Name, }; -use hir_ty::{db::HirDatabase, method_resolution, next_solver::{mapping::ChalkToNextSolver, DbInterner}}; +use hir_ty::{ + db::HirDatabase, + method_resolution, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, +}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -271,7 +275,11 @@ fn resolve_impl_trait_item<'db>( // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) _ = method_resolution::iterate_path_candidates( - &canonical.to_nextsolver(DbInterner::new_with(db, Some(environment.krate), environment.block)), + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, &traits_in_scope, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 6121c61ea3f49..445afa75f3f4f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -10,7 +10,8 @@ use crate::{ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (db, pos) = position(ra_fixture); let config = TEST_CONFIG; - let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap(); + let (completion_context, _analysis) = + salsa::attach(&db, || CompletionContext::new(&db, pos, &config).unwrap()); let ty = completion_context .expected_type diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index ac54ac0950f39..8613581292f75 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -526,8 +526,7 @@ fn main() { fn run(_t: Rate<5>) { } fn main() { - run(f()) // FIXME: remove this error - //^^^ error: expected Rate<5>, found Rate<_> + run(f()) } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 46b633b8a3250..875b4d9b06cec 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -2,7 +2,10 @@ use expect_test::{Expect, expect}; use hir::{FilePosition, FileRange}; use ide_db::{ EditionedFileId, FxHashSet, - base_db::{SourceDatabase, salsa::Durability}, + base_db::{ + SourceDatabase, + salsa::{self, Durability}, + }, }; use test_utils::RangeOrOffset; use triomphe::Arc; @@ -116,7 +119,7 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { let rule: SsrRule = rule.parse().unwrap(); match_finder.add_rule(rule).unwrap(); } - let edits = match_finder.edits(); + let edits = salsa::attach(&db, || match_finder.edits()); if edits.is_empty() { panic!("No edits were made"); } @@ -155,8 +158,12 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { ) .unwrap(); match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); - let matched_strings: Vec = - match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); + let matched_strings: Vec = salsa::attach(&db, || match_finder.matches()) + .flattened() + .matches + .iter() + .map(|m| m.matched_text()) + .collect(); if matched_strings != expected && !expected.is_empty() { print_match_debug_info(&match_finder, position.file_id, expected[0]); } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 11518d1fbf306..72436307d2cee 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -47,7 +47,8 @@ fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect let (analysis, position) = fixture::position(ra_fixture); let sema = &Semantics::new(&analysis.db); let (cursor_def, docs, range) = def_under_cursor(sema, &position); - let res = rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range)); + let res = + salsa::attach(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range))); expect.assert_eq(&res) } From c9ed8a1ced9601bfd96a63be4fa0cdc4c6cb41a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Sep 2025 19:25:22 +0200 Subject: [PATCH 0282/1900] Strip frontmatter in fewer places --- clippy_lints/src/doc/needless_doctest_main.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 74283d7ba863b..43bb972355500 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; @@ -49,13 +50,14 @@ pub fn check( let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, code) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(Diag::cancel); + return (false, test_attr_spans); + }, + }; let mut relevant_main_found = false; let mut eligible = true; From ce2f518b5393c48370f67725a4e15573b49f2c7d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 21:57:19 +0300 Subject: [PATCH 0283/1900] Upgrade rustc crates and handle changes to canonicalization They have to do with diagnostics, we could probably not support them but we will also someday want good diagnostics. The code is mostly copied from rustc. --- src/tools/rust-analyzer/Cargo.lock | 45 +++---- src/tools/rust-analyzer/Cargo.toml | 16 +-- .../infer/canonical/canonicalizer.rs | 41 +++--- .../src/next_solver/infer/canonical/mod.rs | 59 ++++----- .../hir-ty/src/next_solver/infer/context.rs | 8 ++ .../hir-ty/src/next_solver/infer/mod.rs | 8 ++ .../next_solver/infer/snapshot/undo_log.rs | 4 +- .../src/next_solver/infer/type_variable.rs | 117 ++++++++++++++++++ .../hir-ty/src/next_solver/infer/unify_key.rs | 3 + .../crates/hir-ty/src/next_solver/mapping.rs | 42 +++---- .../crates/hir-ty/src/next_solver/solver.rs | 12 +- .../crates/hir-ty/src/next_solver/util.rs | 17 ++- 12 files changed, 256 insertions(+), 116 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 344e6d101fe35..b70b89ea543d8 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6789d94fb3e6e30d62f55e99a321ba63484a8bb3b4ead338687c9ddc282d28" +checksum = "8da95e732b424802b1f043ab4007c78a0fc515ab249587abbea4634bf5fdce9a" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaab80bda0f05e9842e3afb7779b0bad0a4b54e0f7ba6deb5705dcf86482811d" +checksum = "3838d9d7a3a5cdc511cfb6ad78740ce532f75a2366d3fc3b9853ea1b5c872779" [[package]] name = "ra-ap-rustc_hashes" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bd405e538102b5f699241794b2eefee39d5414c0e4bc72435e91430c51f905" +checksum = "bdc8995d268d3bb4ece910f575ea5a063d6003e193ec155d15703b65882d53fb" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521621e271aa03b8433dad5981838278d6cfd7d2d8c9f4eb6d427f1d671f90fc" +checksum = "ed0ccdf6e5627c6c3e54e571e52ce0bc8b94d5f0b94b7460269ca68a4706be69" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245e30f2e1fef258913cc548b36f575549c8af31cbc4649929d21deda96ceeb7" +checksum = "bd28f42362b5c9fb9b8766c3189df02a402b13363600c6885e11027889f03ee6" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82681f924500e888c860e60ed99e9bf702a219a69374f59116c4261525a2157" +checksum = "f1c31a82f091b910a27ee53a86a9af28a2df10c3484e2f1bbfe70633aa84dee9" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ce51f2431fbdc7fabd2d957522b6e27f41f68ec2af74b52a6f4116352ce1a" +checksum = "f8cac6c2b5a8924209d4ca682cbc507252c58a664911e0ef463c112882ba6f72" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc85ef3fdb6c084bde84857d8948dc66b752129dc8417a8614ce490e99a143f" +checksum = "a085a1cf902dcca8abbc537faaef154bbccbbb51850f779ce5484ae3782b5d8f" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd81eccf33d9528905d4e5abaa254b3129a6405d6c5f123fed9b73a3d217f35" +checksum = "8ba32e3985367bc34856b41c7604133649d4a367eb5d7bdf50623025731459d8" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,10 +1958,11 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cb0da02853698d9c89e1d1c01657b9969752befd56365e8899d4310e52b373" +checksum = "9c9911d72f75d85d21fe88374d7bcec94f2200feffb7234108a24cc3da7c3591" dependencies = [ + "arrayvec", "bitflags 2.9.1", "derive-where", "ena", @@ -1977,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc93adeb52c483ede13bee6680466458218243ab479c04fb71bb53925a6e0ff" +checksum = "22f539b87991683ce17cc52e62600fdf2b4a8af43952db30387edc1a576d3b43" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index f325027ee5871..c5ffad544a6b3 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.126", default-features = false } -ra-ap-rustc_parse_format = { version = "0.126", default-features = false } -ra-ap-rustc_index = { version = "0.126", default-features = false } -ra-ap-rustc_abi = { version = "0.126", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.126", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.126", default-features = false } -ra-ap-rustc_type_ir = { version = "0.126", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.126", default-features = false } +ra-ap-rustc_lexer = { version = "0.128", default-features = false } +ra-ap-rustc_parse_format = { version = "0.128", default-features = false } +ra-ap-rustc_index = { version = "0.128", default-features = false } +ra-ap-rustc_abi = { version = "0.128", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.128", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.128", default-features = false } +ra-ap-rustc_type_ir = { version = "0.128", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.128", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index 5d11525cd199f..ffb9c076fa061 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -10,9 +10,8 @@ use rustc_index::Idx; use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; use rustc_type_ir::{ - BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, - RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, - UniverseIndex, + BoundVar, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind, TyVid, TypeFlags, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, }; use smallvec::SmallVec; use tracing::debug; @@ -316,6 +315,13 @@ struct Canonicalizer<'cx, 'db> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, + /// Maps each `sub_unification_table_root_var` to the index of the first + /// variable which used it. + /// + /// This means in case two type variables have the same sub relations root, + /// we set the `sub_root` of the second variable to the position of the first. + /// Otherwise the `sub_root` of each type variable is just its own position. + sub_root_lookup_table: FxHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, @@ -384,10 +390,9 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // FIXME: perf problem described in #55921. ui = UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - t, - ) + + let sub_root = self.get_or_insert_sub_root(vid); + self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t) } } } @@ -395,17 +400,17 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(IntVar(vid)) => { let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - self.fold_ty(nt) + return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) + self.canonicalize_ty_var(CanonicalVarKind::Int, t) } } TyKind::Infer(FloatVar(vid)) => { let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - self.fold_ty(nt) + return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) + self.canonicalize_ty_var(CanonicalVarKind::Float, t) } } @@ -579,6 +584,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { variables: SmallVec::from_slice(base.variables.as_slice()), query_state, indices: FxHashMap::default(), + sub_root_lookup_table: Default::default(), binder_index: DebruijnIndex::ZERO, }; if canonicalizer.query_state.var_values.spilled() { @@ -673,6 +679,13 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { } } + fn get_or_insert_sub_root(&mut self, vid: TyVid) -> BoundVar { + let root_vid = self.infcx.sub_unification_table_root_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + BoundVar::from(idx) + } + /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. @@ -692,9 +705,9 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { self.variables .iter() .map(|v| match *v { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => *v, - CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + CanonicalVarKind::Int | CanonicalVarKind::Float => *v, + CanonicalVarKind::Ty { ui, sub_root } => { + CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root } } CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index ec41111ed8153..8db4320acc9c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -32,9 +32,10 @@ use crate::next_solver::{ }; use instantiate::CanonicalExt; use rustc_index::IndexVec; +use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy, - TypeFoldable, UniverseIndex, Upcast, Variance, + AliasRelationDirection, AliasTyKind, CanonicalVarKind, InferTy, TypeFoldable, UniverseIndex, + Upcast, Variance, inherent::{SliceLike, Ty as _}, relate::{ Relate, TypeRelation, VarianceDiagInfo, @@ -78,27 +79,15 @@ impl<'db> InferCtxt<'db> { .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) .collect(); - let canonical_inference_vars = - self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]); - let result = canonical.instantiate(self.interner, &canonical_inference_vars); - (result, canonical_inference_vars) - } - - /// Given the "infos" about the canonical variables from some - /// canonical, creates fresh variables with the same - /// characteristics (see `instantiate_canonical_var` for - /// details). You can then use `instantiate` to instantiate the - /// canonical variable with these inference variables. - fn instantiate_canonical_vars( - &self, - variables: CanonicalVars<'db>, - universe_map: impl Fn(UniverseIndex) -> UniverseIndex, - ) -> CanonicalVarValues<'db> { - CanonicalVarValues { - var_values: self.interner.mk_args_from_iter( - variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)), - ), - } + let var_values = CanonicalVarValues::instantiate( + self.interner, + canonical.variables, + |var_values, info| { + self.instantiate_canonical_var(info, &var_values, |ui| universes[ui]) + }, + ); + let result = canonical.instantiate(self.interner, &var_values); + (result, var_values) } /// Given the "info" about a canonical variable, creates a fresh @@ -112,21 +101,27 @@ impl<'db> InferCtxt<'db> { pub fn instantiate_canonical_var( &self, cv_info: CanonicalVarKind>, + previous_var_values: &[GenericArg<'db>], universe_map: impl Fn(UniverseIndex) -> UniverseIndex, ) -> GenericArg<'db> { match cv_info { - CanonicalVarKind::Ty(ty_kind) => { - let ty = match ty_kind { - CanonicalTyVarKind::General(ui) => { - self.next_ty_var_in_universe(universe_map(ui)) + CanonicalVarKind::Ty { ui, sub_root } => { + let vid = self.next_ty_var_id_in_universe(universe_map(ui)); + // If this inference variable is related to an earlier variable + // via subtyping, we need to add that info to the inference context. + if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { + if let TyKind::Infer(InferTy::TyVar(sub_root)) = prev.expect_ty().kind() { + self.sub_unify_ty_vids_raw(vid, sub_root); + } else { + unreachable!() } + } + Ty::new_var(self.interner, vid).into() + } - CanonicalTyVarKind::Int => self.next_int_var(), + CanonicalVarKind::Int => self.next_int_var().into(), - CanonicalTyVarKind::Float => self.next_float_var(), - }; - ty.into() - } + CanonicalVarKind::Float => self.next_float_var().into(), CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { let universe_mapped = universe_map(universe); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 93fd6eeab34e4..45ce7e6f6cc75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -313,4 +313,12 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn reset_opaque_types(&self) { let _ = self.take_opaque_types(); } + + fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + self.sub_unification_table_root_var(var) + } + + fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.sub_unify_ty_vids_raw(a, b); + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 2630f2a8cc4e5..ce6c941287325 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1019,6 +1019,14 @@ impl<'db> InferCtxt<'db> { } } } + + fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var) + } + + fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.inner.borrow_mut().type_variables().sub_unify(a, b); + } } /// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs index 0fa6421f517de..28ae56f4ee70a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -25,7 +25,7 @@ pub struct Snapshot { pub(crate) enum UndoLog<'db> { DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'db>, Option>), - TypeVariables(sv::UndoLog>>), + TypeVariables(type_variable::UndoLog<'db>), ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), @@ -51,6 +51,8 @@ impl_from! { RegionConstraintCollector(region_constraints::UndoLog<'db>), TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), + TypeVariables(type_variable::UndoLog<'db>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index 5217308af473c..b640039af625b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -17,12 +17,48 @@ use crate::next_solver::SolverDefId; use crate::next_solver::Ty; use crate::next_solver::infer::InferCtxtUndoLogs; +/// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'db> From>>> for UndoLog<'db> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'db> From>> for UndoLog<'db> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + impl<'db> Rollback>>> for TypeVariableStorage<'db> { fn reverse(&mut self, undo: sv::UndoLog>>) { self.eq_relations.reverse(undo) } } +impl<'tcx> Rollback>> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: sv::UndoLog>) { + self.sub_unification_table.reverse(undo) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_unification_table.reverse(undo), + } + } +} + #[derive(Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { /// The origins of each type variable. @@ -31,6 +67,25 @@ pub(crate) struct TypeVariableStorage<'db> { /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. eq_relations: ut::UnificationTableStorage>, + /// Only used by `-Znext-solver` and for diagnostics. Tracks whether + /// type variables are related via subtyping at all, ignoring which of + /// the two is the subtype. + /// + /// When reporting ambiguity errors, we sometimes want to + /// treat all inference vars which are subtypes of each + /// others as if they are equal. For this case we compute + /// the transitive closure of our subtype obligations here. + /// + /// E.g. when encountering ambiguity errors, we want to suggest + /// specifying some method argument or to add a type annotation + /// to a local variable. Because subtyping cannot change the + /// shape of a type, it's fine if the cause of the ambiguity error + /// is only related to the suggested variable via subtyping. + /// + /// Even for something like `let x = returns_arg(); x.method();` the + /// type of `x` is only a supertype of the argument of `returns_arg`. We + /// still want to suggest specifying the type of the argument. + sub_unification_table: ut::UnificationTableStorage, } pub(crate) struct TypeVariableTable<'a, 'db> { @@ -112,6 +167,17 @@ impl<'db> TypeVariableTable<'_, 'db> { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); + self.sub_unification_table().union(a, b); + } + + /// Records that `a` and `b` are related via subtyping. We don't track + /// which of the two is the subtype. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn sub_unify(&mut self, a: TyVid, b: TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.sub_unification_table().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -141,6 +207,10 @@ impl<'db> TypeVariableTable<'_, 'db> { /// for improving error messages. pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + + let sub_key = self.sub_unification_table().new_key(()); + debug_assert_eq!(eq_key.vid, sub_key.vid); + let index = self.storage.values.push(TypeVariableData { origin }); debug_assert_eq!(eq_key.vid, index); @@ -163,6 +233,18 @@ impl<'db> TypeVariableTable<'_, 'db> { self.eq_relations().find(vid).vid } + /// Returns the "root" variable of `vid` in the `sub_unification_table` + /// equivalence table. All type variables that have been are related via + /// equality or subtyping will yield the same root variable (per the + /// union-find algorithm), so `sub_unification_table_root_var(a) + /// == sub_unification_table_root_var(b)` implies that: + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` + pub(crate) fn sub_unification_table_root_var(&mut self, vid: TyVid) -> TyVid { + self.sub_unification_table().find(vid).vid + } + /// Retrieves the type to which `vid` has been instantiated, if /// any. pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { @@ -180,6 +262,11 @@ impl<'db> TypeVariableTable<'_, 'db> { self.storage.eq_relations.with_log(self.undo_log) } + #[inline] + fn sub_unification_table(&mut self) -> super::UnificationTable<'_, 'db, TyVidSubKey> { + self.storage.sub_unification_table.with_log(self.undo_log) + } + /// Returns indices of all variables that are not yet /// instantiated. pub(crate) fn unresolved_variables(&mut self) -> Vec { @@ -228,6 +315,36 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> { fn tag() -> &'static str { "TyVidEqKey" } + fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { + if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidSubKey { + vid: TyVid, +} + +impl From for TyVidSubKey { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: TyVid) -> Self { + TyVidSubKey { vid } + } +} + +impl ut::UnifyKey for TyVidSubKey { + type Value = (); + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> TyVidSubKey { + TyVidSubKey { vid: TyVid::from_u32(i) } + } + fn tag() -> &'static str { + "TyVidSubKey" + } } impl<'db> ut::UnifyValue for TypeVariableValue<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs index b9afb45ba89da..dc913b262a7c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -139,6 +139,9 @@ impl<'db> UnifyKey for ConstVidKey<'db> { fn tag() -> &'static str { "ConstVidKey" } + fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { + if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } + } } impl<'db> UnifyValue for ConstVariableValue<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index f66b8dace30a6..11bc6e6abe94b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -734,15 +734,13 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> interner, self.binders.iter(Interner).map(|k| match &k.kind { chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { - TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT), - ), - TyVariableKind::Integer => { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) - } - TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::Float, - ), + // FIXME(next-solver): the info is incorrect, but we have no way to store the information in Chalk. + TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty { + ui: UniverseIndex::ROOT, + sub_root: BoundVar::from_u32(0), + }, + TyVariableKind::Integer => rustc_type_ir::CanonicalVarKind::Int, + TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Float, }, chalk_ir::VariableKind::Lifetime => { rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) @@ -767,24 +765,20 @@ impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> let binders = chalk_ir::CanonicalVarKinds::from_iter( Interner, self.variables.iter().map(|v| match v { - rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(ui), - ) => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::General), - chalk_ir::UniverseIndex { counter: ui.as_usize() }, - ), - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + rustc_type_ir::CanonicalVarKind::Ty { ui, sub_root: _ } => { chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Integer), - chalk_ir::UniverseIndex::root(), - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { - chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Float), - chalk_ir::UniverseIndex::root(), + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, ) } + rustc_type_ir::CanonicalVarKind::Int => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::root(), + ), + rustc_type_ir::CanonicalVarKind::Float => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::root(), + ), rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Lifetime, chalk_ir::UniverseIndex { counter: ui.as_usize() }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 385149d78432e..dc5073305c8ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,6 +9,7 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; +use crate::next_solver::CanonicalVarKind; use crate::next_solver::mapping::NextSolverToChalk; use crate::{ TraitRefExt, @@ -117,13 +118,14 @@ impl<'db> SolverDelegate for SolverContext<'db> { canonical.instantiate(self.cx(), &values) } - fn instantiate_canonical_var_with_infer( + fn instantiate_canonical_var( &self, - cv_info: rustc_type_ir::CanonicalVarKind, - _span: ::Span, + kind: CanonicalVarKind<'db>, + span: ::Span, + var_values: &[GenericArg<'db>], universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, - ) -> ::GenericArg { - self.0.instantiate_canonical_var(cv_info, universe_map) + ) -> GenericArg<'db> { + self.0.instantiate_canonical_var(kind, var_values, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 50b96a160ed10..97d3ea72c93cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -532,17 +532,14 @@ pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( max_universe: UniverseIndex::from_u32(1), variables: CanonicalVars::new_from_iter( context.cx(), - vars.iter().map(|(k, v)| match (*k).kind() { + vars.iter().enumerate().map(|(idx, (k, v))| match (*k).kind() { GenericArgKind::Type(ty) => match ty.kind() { - TyKind::Int(..) | TyKind::Uint(..) => { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) - } - TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::Float, - ), - _ => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO), - ), + TyKind::Int(..) | TyKind::Uint(..) => rustc_type_ir::CanonicalVarKind::Int, + TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Float, + _ => rustc_type_ir::CanonicalVarKind::Ty { + ui: UniverseIndex::ZERO, + sub_root: BoundVar::from_usize(idx), + }, }, GenericArgKind::Lifetime(_) => { rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) From 8407376006546c484bcd72d2743f0e4e1f66ac94 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 22:32:07 +0300 Subject: [PATCH 0284/1900] Adopt even more custom types in the new solver A lot of simplification and fun. --- .../crates/hir-def/src/lang_item.rs | 13 ++ .../crates/hir-ty/src/display.rs | 43 ++--- .../rust-analyzer/crates/hir-ty/src/layout.rs | 12 +- .../crates/hir-ty/src/method_resolution.rs | 5 +- .../crates/hir-ty/src/mir/eval.rs | 61 +++---- .../crates/hir-ty/src/next_solver/def_id.rs | 63 ++++++- .../infer/canonical/canonicalizer.rs | 4 +- .../src/next_solver/infer/canonical/mod.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 158 +++++++++--------- .../crates/hir-ty/src/next_solver/mapping.rs | 46 ++--- .../crates/hir-ty/src/next_solver/solver.rs | 12 +- .../crates/hir-ty/src/next_solver/ty.rs | 21 ++- 12 files changed, 220 insertions(+), 222 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 600f206770016..df0705bf90cbc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -84,6 +84,15 @@ impl LangItemTarget { _ => None, } } + + pub fn as_adt(self) -> Option { + match self { + LangItemTarget::Union(it) => Some(it.into()), + LangItemTarget::EnumId(it) => Some(it.into()), + LangItemTarget::Struct(it) => Some(it.into()), + _ => None, + } + } } /// Salsa query. This will look for lang items in a specific crate. @@ -289,6 +298,10 @@ impl LangItem { lang_item(db, start_crate, self).and_then(|t| t.as_trait()) } + pub fn resolve_adt(self, db: &dyn DefDatabase, start_crate: Crate) -> Option { + lang_item(db, start_crate, self).and_then(|t| t.as_adt()) + } + pub fn resolve_enum(self, db: &dyn DefDatabase, start_crate: Crate) -> Option { lang_item(db, start_crate, self).and_then(|t| t.as_enum()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index fcb79e9ffb579..ea8bdf8bcbae4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -59,7 +59,7 @@ use crate::{ lt_from_placeholder_idx, mir::pad16, next_solver::{ - BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, + BoundExistentialPredicate, DbInterner, GenericArgs, SolverDefId, mapping::{ ChalkToNextSolver, convert_args_for_result, convert_const_for_result, convert_region_for_result, convert_ty_for_result, @@ -911,14 +911,13 @@ fn render_const_scalar_inner( f.write_str("&")?; render_const_scalar_ns(f, bytes, memory_map, t) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { - SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id().0 { + hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; Ok(()) } - SolverDefId::AdtId(_) => f.write_str(""), - _ => unreachable!(), + _ => f.write_str(""), }, _ => { let addr = usize::from_le_bytes(match b.try_into() { @@ -966,10 +965,7 @@ fn render_const_scalar_inner( f.write_str(")") } TyKind::Adt(def, args) => { - let def = match def.def_id() { - SolverDefId::AdtId(def) => def, - _ => unreachable!(), - }; + let def = def.def_id().0; let Ok(layout) = f.db.layout_of_adt(def, args, trait_env.clone()) else { return f.write_str(""); }; @@ -1300,12 +1296,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { sig.hir_fmt(f)?; } TyKind::FnDef(def, args) => { - let def = match def { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), - SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), - _ => unreachable!(), - }; + let def = def.0; let sig = db .callable_item_signature(def) .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); @@ -1406,10 +1397,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } TyKind::Adt(def, parameters) => { - let def_id = match def.def_id() { - SolverDefId::AdtId(id) => id, - _ => unreachable!(), - }; + let def_id = def.def_id().0; f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { @@ -1448,7 +1436,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { hir_fmt_generics( f, convert_args_for_result(interner, parameters.as_slice()).as_slice(Interner), - def.def_id().try_into().ok(), + Some(def.def_id().0.into()), None, )?; } @@ -1466,13 +1454,9 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { projection_ty.hir_fmt(f)?; } - TyKind::Foreign(type_alias) => { - let alias = match type_alias { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let type_alias = db.type_alias_signature(alias); - f.start_location_link(alias.into()); + TyKind::Foreign(alias) => { + let type_alias = db.type_alias_signature(alias.0); + f.start_location_link(alias.0.into()); write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; f.end_location_link(); } @@ -1549,10 +1533,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } TyKind::Closure(id, substs) => { - let id = match id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; + let id = id.0; let substs = convert_args_for_result(interner, substs.as_slice()); if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index f8abb3b7f6fef..2020a8b34b4f9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -25,7 +25,7 @@ use crate::{ consteval_nextsolver::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, + DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, mapping::{ChalkToNextSolver, convert_args_for_result}, project::solve_normalize::deeply_normalize, @@ -323,14 +323,10 @@ pub fn layout_of_ty_query<'db>( ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::Closure(c, args) => { - let id = match c { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - let def = db.lookup_intern_closure(id); + TyKind::Closure(id, args) => { + let def = db.lookup_intern_closure(id.0); let infer = db.infer(def.0); - let (captures, _) = infer.closure_info(&id.into()); + let (captures, _) = infer.closure_info(&id.0.into()); let fields = captures .iter() .map(|it| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index fa80567b1ecc0..3f966694d504f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -161,10 +161,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, - TyKind::Foreign(def) => { - let SolverDefId::TypeAliasId(def) = def else { unreachable!() }; - TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) - } + TyKind::Foreign(def) => TyFingerprint::ForeignType(crate::to_foreign_def_id(def.0)), TyKind::Dynamic(bounds, _, _) => { let trait_ref = bounds .as_slice() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index c60ace85be124..ca7467d3855fa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -42,7 +42,7 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - Ctor, DbInterner, SolverDefId, + DbInterner, mapping::{ChalkToNextSolver, convert_args_for_result, convert_ty_for_result}, }, static_lifetime, @@ -2366,8 +2366,8 @@ impl<'db> Evaluator<'db> { let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, args) => match id.def_id() { - SolverDefId::AdtId(AdtId::StructId(s)) => { + TyKind::Adt(id, args) => match id.def_id().0 { + AdtId::StructId(s) => { for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let ty = ty.instantiate(interner, args); @@ -2380,8 +2380,8 @@ impl<'db> Evaluator<'db> { )?; } } - SolverDefId::AdtId(AdtId::UnionId(_)) => (), - SolverDefId::AdtId(AdtId::EnumId(e)) => { + AdtId::UnionId(_) => (), + AdtId::EnumId(e) => { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, @@ -2402,7 +2402,6 @@ impl<'db> Evaluator<'db> { } } } - _ => unreachable!(), }, TyKind::Tuple(tys) => { for (id, ty) in tys.iter().enumerate() { @@ -2472,38 +2471,24 @@ impl<'db> Evaluator<'db> { let interner = DbInterner::new_with(self.db, None, None); use rustc_type_ir::TyKind; match next_ty.kind() { - TyKind::FnDef(def, generic_args) => { - let def = match def { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), - SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), - _ => unreachable!(), - }; - self.exec_fn_def( - def, - &convert_args_for_result(interner, generic_args.as_slice()), - destination, - args, - locals, - target_bb, - span, - ) - } - TyKind::Closure(id, generic_args) => { - let id = match id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - self.exec_closure( - id.into(), - bytes.slice(0..0), - &convert_args_for_result(interner, generic_args.as_slice()), - destination, - args, - locals, - span, - ) - } + TyKind::FnDef(def, generic_args) => self.exec_fn_def( + def.0, + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + target_bb, + span, + ), + TyKind::Closure(id, generic_args) => self.exec_closure( + id.0.into(), + bytes.slice(0..0), + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + span, + ), _ => Err(MirEvalError::InternalError("function pointer to non function".into())), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 8bbc6e3370399..a9c572d3f34ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -1,8 +1,8 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, - TraitId, TypeAliasId, UnionId, + AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, + StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -42,7 +42,8 @@ impl_from!( TypeAliasId, InternedClosureId, InternedCoroutineId, - InternedOpaqueTyId + InternedOpaqueTyId, + Ctor for SolverDefId ); @@ -145,3 +146,59 @@ macro_rules! declare_id_wrapper { } declare_id_wrapper!(TraitIdWrapper, TraitId); +declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId); +declare_id_wrapper!(ClosureIdWrapper, InternedClosureId); +declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId); +declare_id_wrapper!(AdtIdWrapper, AdtId); +declare_id_wrapper!(ImplIdWrapper, ImplId); + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct CallableIdWrapper(pub CallableDefId); + +impl std::fmt::Debug for CallableIdWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } +} +impl From for CallableDefId { + #[inline] + fn from(value: CallableIdWrapper) -> CallableDefId { + value.0 + } +} +impl From for CallableIdWrapper { + #[inline] + fn from(value: CallableDefId) -> CallableIdWrapper { + Self(value) + } +} +impl From for SolverDefId { + #[inline] + fn from(value: CallableIdWrapper) -> SolverDefId { + match value.0 { + CallableDefId::FunctionId(it) => it.into(), + CallableDefId::StructId(it) => Ctor::Struct(it).into(), + CallableDefId::EnumVariantId(it) => Ctor::Enum(it).into(), + } + } +} +impl TryFrom for CallableIdWrapper { + type Error = (); + #[inline] + fn try_from(value: SolverDefId) -> Result { + match value { + SolverDefId::FunctionId(it) => Ok(Self(it.into())), + SolverDefId::Ctor(Ctor::Struct(it)) => Ok(Self(it.into())), + SolverDefId::Ctor(Ctor::Enum(it)) => Ok(Self(it.into())), + _ => Err(()), + } + } +} +impl<'db> inherent::DefId> for CallableIdWrapper { + fn as_local(self) -> Option { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index ffb9c076fa061..beaac11a2de41 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -400,7 +400,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(IntVar(vid)) => { let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { self.canonicalize_ty_var(CanonicalVarKind::Int, t) } @@ -408,7 +408,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(FloatVar(vid)) => { let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { self.canonicalize_ty_var(CanonicalVarKind::Float, t) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 8db4320acc9c0..d0669f5c3bcc5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -82,9 +82,7 @@ impl<'db> InferCtxt<'db> { let var_values = CanonicalVarValues::instantiate( self.interner, canonical.variables, - |var_values, info| { - self.instantiate_canonical_var(info, &var_values, |ui| universes[ui]) - }, + |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]), ); let result = canonical.instantiate(self.interner, &var_values); (result, var_values) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 5709beaefc292..0f512fdaf8687 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -19,7 +19,7 @@ use rustc_type_ir::error::TypeError; use rustc_type_ir::inherent::{ AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, }; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, @@ -47,8 +47,9 @@ use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ - BoundConst, CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, - SolverContext, SolverDefIds, TraitIdWrapper, + AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, + CoroutineIdWrapper, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, RegionAssumptions, + SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -609,8 +610,8 @@ impl AdtDef { } impl<'db> inherent::AdtDef> for AdtDef { - fn def_id(self) -> as rustc_type_ir::Interner>::DefId { - SolverDefId::AdtId(self.inner().id) + fn def_id(self) -> AdtIdWrapper { + self.inner().id.into() } fn is_struct(self) -> bool { @@ -889,6 +890,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type LocalDefId = SolverDefId; type LocalDefIds = SolverDefIds; type TraitId = TraitIdWrapper; + type ForeignId = TypeAliasIdWrapper; + type FunctionId = CallableIdWrapper; + type ClosureId = ClosureIdWrapper; + type CoroutineClosureId = CoroutineIdWrapper; + type CoroutineId = CoroutineIdWrapper; + type AdtId = AdtIdWrapper; + type ImplId = ImplIdWrapper; type Span = Span; type GenericArgs = GenericArgs<'db>; @@ -1103,12 +1111,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().ty_ns(def_id) } - fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef { - let def_id = match adt_def_id { - SolverDefId::AdtId(adt_id) => adt_id, - _ => panic!("Invalid DefId passed to adt_def"), - }; - AdtDef::new(def_id, self) + fn adt_def(self, def_id: Self::AdtId) -> Self::AdtDef { + AdtDef::new(def_id.0, self) } fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { @@ -1219,24 +1223,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn fn_sig( self, - def_id: Self::DefId, + def_id: Self::FunctionId, ) -> EarlyBinder>> { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(ctor) => match ctor { - super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id), - super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id), - }, - def => unreachable!("{:?}", def), - }; - self.db().callable_item_signature_ns(id) + self.db().callable_item_signature_ns(def_id.0) } - fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability { + fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { unimplemented!() } - fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId { + fn coroutine_for_closure(self, def_id: Self::CoroutineId) -> Self::CoroutineId { unimplemented!() } @@ -1361,13 +1357,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn impl_super_outlives( self, - impl_def_id: Self::DefId, + impl_id: Self::ImplId, ) -> EarlyBinder> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => unreachable!(), - }; - let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait"); + let trait_ref = self.db().impl_trait_ns(impl_id.0).expect("expected an impl of trait"); trait_ref.map_bound(|trait_ref| { let clause: Clause<'_> = trait_ref.upcast(self); Clauses::new_from_iter( @@ -1392,7 +1384,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { EarlyBinder::bind([unimplemented!()]) } - fn has_target_features(self, def_id: Self::DefId) -> bool { + fn has_target_features(self, def_id: Self::FunctionId) -> bool { false } @@ -1407,8 +1399,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverLangItem::DynMetadata => LangItem::DynMetadata, SolverLangItem::FutureOutput => LangItem::FutureOutput, SolverLangItem::Metadata => LangItem::Metadata, - SolverLangItem::Option => LangItem::Option, - SolverLangItem::Poll => LangItem::Poll, }; let target = hir_def::lang_item::lang_item( self.db(), @@ -1468,21 +1458,30 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .into() } - #[allow(clippy::match_like_matches_macro)] - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { - use SolverLangItem::*; + fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> AdtIdWrapper { + let lang_item = match lang_item { + SolverAdtLangItem::Option => LangItem::Option, + SolverAdtLangItem::Poll => LangItem::Poll, + }; + lang_item + .resolve_adt(self.db(), self.krate.expect("Must have self.krate")) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")) + .into() + } - // FIXME: derive PartialEq on SolverLangItem + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { self.as_lang_item(def_id) .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } - #[allow(clippy::match_like_matches_macro)] fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool { - use SolverTraitLangItem::*; + self.as_trait_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) + } + fn is_adt_lang_item(self, def_id: Self::AdtId, lang_item: SolverAdtLangItem) -> bool { // FIXME: derive PartialEq on SolverTraitLangItem - self.as_trait_lang_item(def_id) + self.as_adt_lang_item(def_id) .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } @@ -1505,9 +1504,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { DynMetadata, CoroutineReturn, CoroutineYield, - Poll, FutureOutput, - Option, AsyncFnOnceOutput, CallRefFuture, CallOnceFuture, @@ -1556,6 +1553,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ) } + fn as_adt_lang_item(self, def_id: Self::AdtId) -> Option { + let def_id: AttrDefId = def_id.0.into(); + let lang_item = self.db().lang_attr(def_id)?; + as_lang_item!( + SolverAdtLangItem, lang_item; + + ignore = {} + + Option, + Poll, + ) + } + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { let trait_ = match def_id { SolverDefId::TraitId(id) => id, @@ -1568,7 +1578,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, trait_: Self::TraitId, self_ty: Self::Ty, - mut f: impl FnMut(Self::DefId), + mut f: impl FnMut(Self::ImplId), ) { let trait_ = trait_.0; let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); @@ -1595,7 +1605,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { continue; } - f(SolverDefId::ImplId(i)); + f(i.into()); } ControlFlow::Continue(()) }, @@ -1618,7 +1628,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { continue; } - f(SolverDefId::ImplId(i)); + f(i.into()); } } ControlFlow::Continue(()) @@ -1632,7 +1642,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { true } - fn impl_is_default(self, impl_def_id: Self::DefId) -> bool { + fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool { // FIXME false } @@ -1640,26 +1650,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { #[tracing::instrument(skip(self), ret)] fn impl_trait_ref( self, - impl_def_id: Self::DefId, + impl_id: Self::ImplId, ) -> EarlyBinder> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => panic!("Unexpected SolverDefId in impl_trait_ref"), - }; - let db = self.db(); - - db.impl_trait_ns(impl_id) + db.impl_trait_ns(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach trait solving .expect("invalid impl passed to trait solver") } - fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => unreachable!(), - }; - let impl_data = self.db().impl_signature(impl_id); + fn impl_polarity(self, impl_id: Self::ImplId) -> rustc_type_ir::ImplPolarity { + let impl_data = self.db().impl_signature(impl_id.0); if impl_data.flags.contains(ImplFlags::NEGATIVE) { ImplPolarity::Negative } else { @@ -1701,33 +1701,29 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { panic!("Bug encountered in next-trait-solver.") } - fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool { + fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_async(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } - fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_async_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } - fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams { - let id = match adt_def_id { - SolverDefId::AdtId(id) => id, - _ => unreachable!(), - }; - let def = AdtDef::new(id, self); - let num_params = self.generics_of(adt_def_id).count(); + fn unsizing_params_for_adt(self, id: Self::AdtId) -> Self::UnsizingParams { + let def = AdtDef::new(id.0, self); + let num_params = self.generics_of(id.into()).count(); let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() { GenericArgKind::Type(ty) => match ty.kind() { @@ -1835,15 +1831,15 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { EarlyBinder::bind([]) } - fn fn_is_const(self, def_id: Self::DefId) -> bool { - let id = match def_id { - SolverDefId::FunctionId(id) => id, - _ => unreachable!(), + fn fn_is_const(self, id: Self::FunctionId) -> bool { + let id = match id.0 { + CallableDefId::FunctionId(id) => id, + _ => return false, }; self.db().function_signature(id).flags.contains(FnFlags::CONST) } - fn impl_is_const(self, def_id: Self::DefId) -> bool { + fn impl_is_const(self, def_id: Self::ImplId) -> bool { false } @@ -1877,7 +1873,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn coroutine_hidden_types( self, - def_id: Self::DefId, + def_id: Self::CoroutineId, ) -> EarlyBinder>> { // FIXME(next-solver) @@ -1896,11 +1892,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().trait_signature(trait_.0).flags.contains(TraitFlags::UNSAFE) } - fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { + fn impl_self_is_guaranteed_unsized(self, def_id: Self::ImplId) -> bool { false } - fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool { false } @@ -2020,6 +2016,12 @@ macro_rules! TrivialTypeTraversalImpls { TrivialTypeTraversalImpls! { SolverDefId, TraitIdWrapper, + TypeAliasIdWrapper, + CallableIdWrapper, + ClosureIdWrapper, + CoroutineIdWrapper, + AdtIdWrapper, + ImplIdWrapper, Pattern<'db>, Safety, FnAbi, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 11bc6e6abe94b..ce56282a00b24 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -243,12 +243,10 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { } chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); - let id: SolverDefId = match def_id { - CallableDefId::FunctionId(id) => id.into(), - CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), - CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)), - }; - rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner)) + rustc_type_ir::TyKind::FnDef( + def_id.into(), + substitution.to_nextsolver(interner), + ) } chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, @@ -271,7 +269,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ) } chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( - SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), + crate::from_foreign_def_id(*foreign_def_id).into(), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), chalk_ir::TyKind::Dyn(dyn_ty) => { @@ -1511,13 +1509,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Slice(ty) } - rustc_type_ir::TyKind::Foreign(foreign) => { - let def_id = match foreign { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - TyKind::Foreign(to_foreign_def_id(def_id)) - } + rustc_type_ir::TyKind::Foreign(foreign) => TyKind::Foreign(to_foreign_def_id(foreign.0)), rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { @@ -1528,40 +1520,22 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), - SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::FnDef(id.to_chalk(interner.db()), subst) + TyKind::FnDef(def_id.0.to_chalk(interner.db()), subst) } rustc_type_ir::TyKind::Closure(def_id, args) => { - let id = match def_id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Closure(id.into(), subst) + TyKind::Closure(def_id.0.into(), subst) } rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Coroutine(id.into(), subst) + TyKind::Coroutine(def_id.0.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::CoroutineWitness(id.into(), subst) + TyKind::CoroutineWitness(def_id.0.into(), subst) } rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index dc5073305c8ca..c7591c0c77966 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,8 +9,8 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; -use crate::next_solver::CanonicalVarKind; use crate::next_solver::mapping::NextSolverToChalk; +use crate::next_solver::{CanonicalVarKind, ImplIdWrapper}; use crate::{ TraitRefExt, db::HirDatabase, @@ -148,12 +148,8 @@ impl<'db> SolverDelegate for SolverContext<'db> { &self, goal_trait_ref: rustc_type_ir::TraitRef, trait_assoc_def_id: ::DefId, - impl_def_id: ::DefId, + impl_id: ImplIdWrapper, ) -> Result::DefId>, ErrorGuaranteed> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => panic!("Unexpected SolverDefId"), - }; let trait_assoc_id = match trait_assoc_def_id { SolverDefId::TypeAliasId(id) => id, _ => panic!("Unexpected SolverDefId"), @@ -162,7 +158,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { .0 .interner .db() - .impl_trait(impl_id) + .impl_trait(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach solver .expect("invalid impl passed to next-solver") .into_value_and_skipped_binders() @@ -170,7 +166,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { let trait_ = trait_ref.hir_trait_id(); let trait_data = trait_.trait_items(self.0.interner.db()); let id = - impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { + impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { match item { (_, AssocItemId::TypeAliasId(type_alias)) => { let name = &self.0.interner.db().type_alias_signature(*type_alias).name; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 4794e2d604509..e1c29160ad1ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -20,7 +20,9 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; -use crate::next_solver::GenericArg; +use crate::next_solver::{ + CallableIdWrapper, ClosureIdWrapper, CoroutineIdWrapper, GenericArg, TypeAliasIdWrapper, +}; use crate::{ db::HirDatabase, interner::InternedWrapperNoDebug, @@ -599,10 +601,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { Ty::new(interner, TyKind::Adt(adt_def, args)) } - fn new_foreign( - interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, - ) -> Self { + fn new_foreign(interner: DbInterner<'db>, def_id: TypeAliasIdWrapper) -> Self { Ty::new(interner, TyKind::Foreign(def_id)) } @@ -617,7 +616,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::Coroutine(def_id, args)) @@ -625,7 +624,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_closure( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) @@ -633,7 +632,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_closure( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: ClosureIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::Closure(def_id, args)) @@ -641,7 +640,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_witness( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) @@ -649,7 +648,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_witness_for_coroutine( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, coroutine_args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { // HACK: Coroutine witness types are lifetime erased, so they @@ -714,7 +713,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_fn_def( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CallableIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::FnDef(def_id, args)) From 1acb4c2c1621578ea787afb698ed8718cbdf3d4f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 03:35:22 +0300 Subject: [PATCH 0285/1900] An associated type is not a projection! More correctly, a `TyKind::AssociatedType` is not the same as `TyKind::Projection`. We used to map next-solver `TyKind::Alias` to Chalk's `TyKind::AssociatedType`. This is very incorrect, as `AssociatedType` is assumed to be fully normalized, and caused a lot of type mismatches. Unfortunately fixing this causes a lot of stack overflows, because the next solver doesn't have something akin to `AssociatedType` so we normalize again and again. The reason is that is the lazy-normalization nature of the next solver, which means we need to stop normalizing everything. This will be fixed in the next commit. --- .../crates/hir-ty/src/next_solver/mapping.rs | 5 +++- .../hir-ty/src/tests/regression/new_solver.rs | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index f66b8dace30a6..57a56b3f0c662 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1358,7 +1358,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); - TyKind::AssociatedType(associated_ty_id, substitution) + TyKind::Alias(crate::AliasTy::Projection(crate::ProjectionTy { + associated_ty_id, + substitution, + })) } rustc_type_ir::AliasTyKind::Opaque => { let opaque_ty_id = match alias_ty.def_id { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 97473bbabba04..471108d964f6e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -71,3 +71,30 @@ fn main() { }"#, ); } + +#[test] +fn projection_is_not_associated_type() { + check_no_mismatches( + r#" +//- minicore: fn +trait Iterator { + type Item; + + fn partition(self, f: F) + where + F: FnMut(&Self::Item) -> bool, + { + } +} + +struct Iter; +impl Iterator for Iter { + type Item = i32; +} + +fn main() { + Iter.partition(|n| true); +} + "#, + ); +} From 4a4b77f1c75e48944507fd69e75db36a1658f800 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 03:32:06 +0300 Subject: [PATCH 0286/1900] Properly handle normalization Previously normalization was broken, which caused a lot of fake errors. This fix most type mismatches of the new solver, and it also reverts many test regressions. The downside is that now `chalk_ir::TyKind::AssociatedType`/`chalk_ir::TyKind::Alias` cannot be trusted anymore with their roles, namely: `AssociatedType` is always fully normalized and `Alias` only if it can possibly be normalized further. That seems okay as the new solver does not have this distinction at all (due to it being a lazy normalizer), so this will only hold for the migration time. This does mean we have to change some APIs, notably `hir::Type::walk()` and `TyFingerprint`, to treat `Alias` the same as `AssociatedType`. Another small thing this commit does is to isolate processing of user-written types (currently involving replacing error types and normalizing, but in the future validation will also be needed) to separate functions. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 50 +++- .../crates/hir-ty/src/infer/closure.rs | 10 +- .../crates/hir-ty/src/infer/expr.rs | 8 +- .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir-ty/src/infer/path.rs | 17 +- .../crates/hir-ty/src/infer/unify.rs | 244 +++++++++--------- .../crates/hir-ty/src/method_resolution.rs | 21 +- .../crates/hir-ty/src/tests/macros.rs | 4 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/patterns.rs | 8 +- .../crates/hir-ty/src/tests/regression.rs | 12 +- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 2 +- .../crates/hir-ty/src/tests/traits.rs | 18 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 9 +- 15 files changed, 235 insertions(+), 198 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 265d1f8541cc8..3d91a2558f0b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -54,6 +54,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::{always, never}; use triomphe::Arc; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, @@ -922,13 +924,15 @@ impl<'db> InferenceContext<'db> { }); diagnostics.shrink_to_fit(); for (_, subst) in method_resolutions.values_mut() { - *subst = table.resolve_completely(subst.clone()); + *subst = + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } method_resolutions.shrink_to_fit(); for (_, subst) in assoc_resolutions.values_mut() { - *subst = table.resolve_completely(subst.clone()); + *subst = + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } @@ -946,7 +950,12 @@ impl<'db> InferenceContext<'db> { result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() - .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) + .map(|(idx, subst)| { + ( + TupleId(idx as u32), + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst), + ) + }) .inspect(|(_, subst)| { *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); @@ -1015,14 +1024,12 @@ impl<'db> InferenceContext<'db> { if let Some(self_param) = self.body.self_param && let Some(ty) = param_tys.next() { - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.write_binding_ty(self_param, ty); } let mut tait_candidates = FxHashSet::default(); for (ty, pat) in param_tys.zip(&*self.body.params) { - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.infer_top_pat(*pat, &ty, None); if ty @@ -1073,7 +1080,7 @@ impl<'db> InferenceContext<'db> { None => self.result.standard_types.unit.clone(), }; - self.return_ty = self.normalize_associated_types_in(return_ty); + self.return_ty = self.process_user_written_ty(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); // Functions might be defining usage sites of TAITs. @@ -1415,8 +1422,7 @@ impl<'db> InferenceContext<'db> { ) -> Ty { let ty = self .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); - let ty = self.insert_type_vars(ty); - self.normalize_associated_types_in(ty) + self.process_user_written_ty(ty) } fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty { @@ -1562,15 +1568,35 @@ impl<'db> InferenceContext<'db> { ty } + /// Whenever you lower a user-written type, you should call this. + fn process_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + { + self.table.process_user_written_ty(ty) + } + + /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, + /// while `process_user_written_ty()` should (but doesn't currently). + fn process_remote_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + { + self.table.process_remote_user_written_ty(ty) + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - fn normalize_associated_types_in(&mut self, ty: T) -> T + fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.table.normalize_associated_types_in(ty) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 5a69ad68b58e4..fd7e5a6a0e121 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -53,7 +53,7 @@ pub(super) struct ClosureSignature { pub(super) expected_sig: FnPointer, } -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(super) fn infer_closure( &mut self, body: &ExprId, @@ -71,9 +71,13 @@ impl InferenceContext<'_> { None => (None, None), }; - let ClosureSignature { expected_sig: bound_sig, ret_ty: body_ret_ty } = + let ClosureSignature { expected_sig: mut bound_sig, ret_ty: body_ret_ty } = self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig); - let bound_sig = self.normalize_associated_types_in(bound_sig); + bound_sig.substitution.0 = self + .normalize_associated_types_in::<_, crate::next_solver::GenericArgs<'db>>( + bound_sig.substitution.0, + ); + let bound_sig = bound_sig; let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner); let (id, ty, resume_yield_tys) = match closure_kind { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index bfeb5bae851a5..0a58ea11bb871 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1419,7 +1419,7 @@ impl InferenceContext<'_> { None => self.err_ty(), }; - let ret_ty = self.normalize_associated_types_in(ret_ty); + let ret_ty = self.process_remote_user_written_ty(ret_ty); if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) { // use knowledge of built-in binary ops, which can sometimes help inference @@ -1630,8 +1630,7 @@ impl InferenceContext<'_> { Some(match res { Some((field_id, ty)) => { let adjustments = auto_deref_adjust_steps(&autoderef); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); (ty, field_id, adjustments, true) } @@ -1641,8 +1640,7 @@ impl InferenceContext<'_> { let ty = self.db.field_types(field_id.parent)[field_id.local_id] .clone() .substitute(Interner, &subst); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); (ty, Either::Left(field_id), adjustments, false) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 16489e3068f35..6781bc84d1c11 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -88,7 +88,7 @@ impl InferenceContext<'_> { Some(substs) => f.substitute(Interner, substs), None => f.substitute(Interner, &Substitution::empty(Interner)), }; - self.normalize_associated_types_in(expected_ty) + self.process_remote_user_written_ty(expected_ty) } None => self.err_ty(), } @@ -152,7 +152,7 @@ impl InferenceContext<'_> { Some(substs) => f.substitute(Interner, substs), None => f.substitute(Interner, &Substitution::empty(Interner)), }; - self.normalize_associated_types_in(expected_ty) + self.process_remote_user_written_ty(expected_ty) } None => { self.push_diagnostic(InferenceDiagnostic::NoSuchField { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 1f62f9c4aa113..80f7324e58b2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -23,7 +23,7 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { @@ -31,13 +31,13 @@ impl InferenceContext<'_> { } ValuePathResolution::NonGeneric(ty) => return Some(ty), }; - let substs = self.insert_type_vars(substs); - let substs = self.normalize_associated_types_in(substs); + let substs = + self.process_remote_user_written_ty::<_, crate::next_solver::GenericArgs<'db>>(substs); self.add_required_obligations_for_value_path(generic_def, &substs); let ty = self.db.value_ty(value_def)?.substitute(Interner, &substs); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); Some(ty) } @@ -173,14 +173,12 @@ impl InferenceContext<'_> { let last = path.segments().last()?; let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); + let ty = self.table.process_user_written_ty(ty); path_ctx.ignore_last_segment(); let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true); drop_ctx(ctx, no_diagnostics); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); + let ty = self.table.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { let hygiene = self.body.expr_or_pat_path_hygiene(id); @@ -223,8 +221,7 @@ impl InferenceContext<'_> { return None; } - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last_segment.name, id) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index ec4b7ee85dc67..19b83d3c212df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -12,12 +12,14 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_next_trait_solver::solve::HasChanged; +use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ AliasRelationDirection, FloatVid, IntVid, TyVid, inherent::{Span, Term as _}, relate::{Relate, solver_relating::RelateExt}, solve::{Certainty, NoSolution}, }; +use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt}; use smallvec::SmallVec; use triomphe::Arc; @@ -31,11 +33,8 @@ use crate::{ db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{ - self, Binder, DbInterner, ParamEnvAnd, Predicate, PredicateKind, SolverDefIds, Term, - infer::{ - DbInternerInferExt, InferCtxt, canonical::canonicalizer::OriginalQueryValues, - snapshot::CombinedSnapshot, - }, + self, Binder, DbInterner, Predicate, PredicateKind, SolverDefIds, Term, + infer::{DbInternerInferExt, InferCtxt, snapshot::CombinedSnapshot}, mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, }, to_chalk_trait_id, @@ -305,116 +304,21 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - #[tracing::instrument(skip(self), ret)] - pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T + pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, { - fold_tys_and_consts( - ty, - |e, _| match e { - Either::Left(ty) => { - let ty = self.resolve_ty_shallow(&ty); - tracing::debug!(?ty); - Either::Left(match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - let ty = self.normalize_projection_ty(proj_ty.clone()); - self.resolve_ty_shallow(&ty) - } - TyKind::AssociatedType(id, subst) => { - // return Either::Left(self.resolve_ty_shallow(&ty)); - if ty.data(Interner).flags.intersects( - chalk_ir::TypeFlags::HAS_TY_INFER - | chalk_ir::TypeFlags::HAS_CT_INFER, - ) { - return Either::Left(ty); - } - let var = self.new_type_var(); - let proj_ty = chalk_ir::ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone(), - }; - let normalize = chalk_ir::Normalize { - alias: AliasTy::Projection(proj_ty), - ty: var.clone(), - }; - let goal = chalk_ir::Goal::new( - Interner, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize( - normalize, - )), - ); - let in_env = InEnvironment::new(&self.trait_env.env, goal); - let goal = in_env.to_nextsolver(self.interner); - let goal = - ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; - - let (canonical_goal, orig_values) = { - let mut orig_values = OriginalQueryValues::default(); - let result = - self.infer_ctxt.canonicalize_query(goal, &mut orig_values); - (result.canonical, orig_values) - }; - let canonical_goal = rustc_type_ir::Canonical { - max_universe: canonical_goal.max_universe, - variables: canonical_goal.variables, - value: crate::next_solver::Goal { - param_env: canonical_goal.value.param_env, - predicate: canonical_goal.value.value, - }, - }; - let solution = next_trait_solve_canonical_in_ctxt( - &self.infer_ctxt, - canonical_goal, - ); - if let NextTraitSolveResult::Certain(canonical_subst) = solution { - let subst = self.instantiate_canonical(canonical_subst).subst; - if subst.len(Interner) != orig_values.var_values.len() { - ty - } else { - let target_ty = var.to_nextsolver(self.interner); - subst - .iter(Interner) - .zip(orig_values.var_values.iter()) - .find_map(|(new, orig)| { - if orig.ty() == Some(target_ty) { - Some(new.assert_ty_ref(Interner).clone()) - } else { - None - } - }) - .unwrap_or(ty) - } - } else { - ty - } - } - _ => ty, - }) - } - Either::Right(c) => Either::Right(match &c.data(Interner).value { - chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { - crate::ConstScalar::UnevaluatedConst(c_id, subst) => { - // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable - // and registering an obligation. But it needs chalk support, so we handle the most basic - // case (a non associated const without generic parameters) manually. - if subst.len(Interner) == 0 { - if let Ok(eval) = self.db.const_eval(*c_id, subst.clone(), None) { - eval - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } - _ => c, - }, - _ => c, - }), - }, - DebruijnIndex::INNERMOST, - ) + self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) + } + + pub(crate) fn normalize_associated_types_in_ns(&mut self, ty: T) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + let ty = self.resolve_vars_with_obligations(ty); + ty.fold_with(&mut Normalizer { table: self }) } /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow @@ -476,11 +380,27 @@ impl<'a> InferenceTable<'a> { } pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { - let var = self.new_type_var(); - let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; - let obligation: Goal = alias_eq.cast(Interner); - self.register_obligation(obligation.to_nextsolver(self.interner)); - var + let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(proj_ty)) + .intern(Interner) + .to_nextsolver(self.interner); + self.normalize_alias_ty(ty).to_chalk(self.interner) + } + + pub(crate) fn normalize_alias_ty( + &mut self, + alias: crate::next_solver::Ty<'a>, + ) -> crate::next_solver::Ty<'a> { + let infer_term = self.infer_ctxt.next_ty_var(); + let obligation = crate::next_solver::Predicate::new( + self.interner, + crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( + alias.into(), + infer_term.into(), + rustc_type_ir::AliasRelationDirection::Equate, + )), + ); + self.register_obligation(obligation); + self.resolve_vars_with_obligations(infer_term) } fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { @@ -591,9 +511,10 @@ impl<'a> InferenceTable<'a> { ) } - pub(crate) fn resolve_completely(&mut self, t: T) -> T + pub(crate) fn resolve_completely(&mut self, t: T) -> T where - T: HasInterner + TypeFoldable, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); @@ -1045,6 +966,30 @@ impl<'a> InferenceTable<'a> { } } + /// Whenever you lower a user-written type, you should call this. + pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + { + self.process_remote_user_written_ty(ty) + // FIXME: Register a well-formed obligation. + } + + /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, + /// while `process_user_written_ty()` should (but doesn't currently). + pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + { + let ty = self.insert_type_vars(ty); + // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: + // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs + // to normalize before inspecting the `TyKind`. + self.normalize_associated_types_in(ty) + } + /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { let data = c.data(Interner); @@ -1319,3 +1264,62 @@ mod resolve { } } } + +/// This expects its input to be resolved. +struct Normalizer<'a, 'b> { + table: &'a mut InferenceTable<'b>, +} + +impl<'db> Normalizer<'_, 'db> { + fn normalize_alias_term( + &mut self, + alias_term: crate::next_solver::Term<'db>, + ) -> crate::next_solver::Term<'db> { + let infer_term = self.table.infer_ctxt.next_term_var_of_kind(alias_term); + let obligation = crate::next_solver::Predicate::new( + self.table.interner, + crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( + alias_term, + infer_term, + rustc_type_ir::AliasRelationDirection::Equate, + )), + ); + self.table.register_obligation(obligation); + let term = self.table.resolve_vars_with_obligations(infer_term); + // Now normalize the result, because maybe it contains more aliases. + match term { + Term::Ty(term) => term.super_fold_with(self).into(), + Term::Const(term) => term.super_fold_with(self).into(), + } + } +} + +impl<'db> rustc_type_ir::TypeFolder> for Normalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.table.interner + } + + fn fold_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { + if !ty.has_aliases() { + return ty; + } + + let crate::next_solver::TyKind::Alias(..) = ty.kind() else { + return ty.super_fold_with(self); + }; + // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). + self.normalize_alias_term(ty.into()).expect_type() + } + + fn fold_const(&mut self, ct: crate::next_solver::Const<'db>) -> crate::next_solver::Const<'db> { + if !ct.has_aliases() { + return ct; + } + + let crate::next_solver::ConstKind::Unevaluated(..) = ct.kind() else { + return ct.super_fold_with(self); + }; + // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). + self.normalize_alias_term(ct.into()).expect_const() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index fa80567b1ecc0..a234312173a23 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -22,10 +22,9 @@ use stdx::never; use triomphe::Arc; use crate::{ - AdtId, AliasTy, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, - GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, - VariableKind, WhereClause, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, + Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, @@ -106,8 +105,12 @@ impl TyFingerprint { } } TyKind::AssociatedType(_, _) + // FIXME(next-solver): Putting `Alias` here is *probably* incorrect, AFAIK it should return `None`. But this breaks + // flyimport, which uses an incorrect but fast method resolution algorithm. Therefore we put it here, + // because this function is only called by flyimport, and anyway we should get rid of `TyFingerprint` + // and switch to `rustc_type_ir`'s `SimplifiedType`. + | TyKind::Alias(_) | TyKind::OpaqueType(_, _) - | TyKind::Alias(AliasTy::Opaque(_)) | TyKind::FnDef(_, _) | TyKind::Closure(_, _) | TyKind::Coroutine(..) @@ -115,8 +118,7 @@ impl TyFingerprint { TyKind::Function(fn_ptr) => { TyFingerprint::Function(fn_ptr.substitution.0.len(Interner) as u32) } - TyKind::Alias(_) - | TyKind::Placeholder(_) + TyKind::Placeholder(_) | TyKind::BoundVar(_) | TyKind::InferenceVar(_, _) | TyKind::Error => return None, @@ -908,7 +910,10 @@ fn find_matching_impl( } table.register_obligation(goal.to_nextsolver(table.interner)); } - Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) + Some(( + impl_.impl_items(db), + table.resolve_completely::<_, crate::next_solver::GenericArgs<'_>>(impl_substs), + )) }) }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 6490554b22b7d..5d088e40cdeda 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -202,7 +202,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> + 100..119 'for _ ...!() {}': Option<{unknown}> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -296,7 +296,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> + 114..133 'for _ ...!() {}': Option<{unknown}> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index baca7f2318e22..6a9135622deb6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -362,12 +362,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': ! - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': &'? mut <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': &'? mut {unknown} 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': Option<{unknown}> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +379,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': ! - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': &'? mut <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': &'? mut {unknown} 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': Option<{unknown}> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +395,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': ! - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': &'? mut <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': &'? mut {unknown} 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': Option<{unknown}> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 60a2641e1a20e..02cb03706919b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -48,12 +48,12 @@ fn infer_pattern() { 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} 101..151 'for (e... }': ! - 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': &'? mut <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': &'? mut {unknown} 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': Option<({unknown}, {unknown})> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index eacc4603ea1af..6a3f2286215f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -268,12 +268,12 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} 32..320 'for co... }': ! - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': &'? mut <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} + 32..320 'for co... }': &'? mut {unknown} 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': Option<{unknown}> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () @@ -628,7 +628,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': as BoxedDsl>::Output + 488..522 '{ ... }': () 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -1248,7 +1248,7 @@ fn test() { 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> + 16..66 'for _ ... }': Option<{unknown}> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 471108d964f6e..6d6c56696a30d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -20,7 +20,7 @@ impl<'a> IntoIterator for &'a Grid { "#, expect![[r#" 150..154 'self': &'a Grid - 174..181 '{ }': impl Iterator + 174..181 '{ }': impl Iterator "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index e7357ed5aa78b..60ad0f49c6ab0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2000,7 +2000,7 @@ fn test() { 225..360 'match ... }': () 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState + 231..262 'Pin::n...usize)': CoroutineState 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 7a946f7ec7ced..22332fdc2b8aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4190,8 +4190,6 @@ fn g(p:

", - prefix = prefix, - attr = code_attr.0 - ) - .unwrap(); -} - -// When an attribute is rendered inside a tag, it is formatted using -// a div to produce a newline after it. -fn render_attributes_in_code( - w: &mut impl fmt::Write, - it: &clean::Item, - prefix: &str, - cx: &Context<'_>, -) { - for attr in it.attributes(cx.tcx(), cx.cache()) { - render_code_attribute(prefix, CodeAttribute(attr), w); - } -} - -/// used for type aliases to only render their `repr` attribute. -fn render_repr_attributes_in_code( - w: &mut impl fmt::Write, - cx: &Context<'_>, - def_id: DefId, - item_type: ItemType, -) { - if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - render_code_attribute("", CodeAttribute(repr), w); - } -} - #[derive(Copy, Clone)] enum AssocItemLink<'a> { Anchor(Option<&'a str>), @@ -2959,3 +2923,144 @@ fn render_call_locations( w.write_str("") } + +struct CodeAttribute(String); + +fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { + write!( + w, + "
{prefix}{attr}
", + prefix = prefix, + attr = code_attr.0 + ) + .unwrap(); +} + +// When an attribute is rendered inside a tag, it is formatted using +// a div to produce a newline after it. +fn render_attributes_in_code( + w: &mut impl fmt::Write, + item: &clean::Item, + prefix: &str, + cx: &Context<'_>, +) { + for attr in attributes(item, cx.tcx(), cx.cache()) { + render_code_attribute(prefix, CodeAttribute(attr), w); + } +} + +/// used for type aliases to only render their `repr` attribute. +fn render_repr_attributes_in_code( + w: &mut impl fmt::Write, + cx: &Context<'_>, + def_id: DefId, + item_type: ItemType, +) { + if let Some(repr) = repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + render_code_attribute("", CodeAttribute(repr), w); + } +} + +/// Get a list of attributes excluding `#[repr]` to display. +fn attributes_without_repr(item: &clean::Item) -> Vec { + item.attrs + .other_attrs + .iter() + .filter_map(|attr| match attr { + hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => { + Some(format!("#[unsafe(link_section = \"{name}\")]")) + } + hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => { + Some("#[unsafe(no_mangle)]".to_string()) + } + hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => { + Some(format!("#[unsafe(export_name = \"{name}\")]")) + } + hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => { + Some("#[non_exhaustive]".to_string()) + } + _ => None, + }) + .collect() +} + +/// Get a list of attributes to display on this item. +fn attributes(item: &clean::Item, tcx: TyCtxt<'_>, cache: &Cache) -> Vec { + let mut attrs = attributes_without_repr(item); + + if let Some(repr_attr) = repr(item, tcx, cache) { + attrs.push(repr_attr); + } + attrs +} + +/// Returns a stringified `#[repr(...)]` attribute. +fn repr(item: &clean::Item, tcx: TyCtxt<'_>, cache: &Cache) -> Option { + repr_attributes(tcx, cache, item.def_id()?, item.type_()) +} + +/// Return a string representing the `#[repr]` attribute if present. +pub(crate) fn repr_attributes( + tcx: TyCtxt<'_>, + cache: &Cache, + def_id: DefId, + item_type: ItemType, +) -> Option { + use rustc_abi::IntegerType; + + if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + if repr.transparent() { + // Render `repr(transparent)` iff the non-1-ZST field is public or at least one + // field is public in case all fields are 1-ZST fields. + let render_transparent = cache.document_private + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } +} From 16c720645cac29e71cc897374bb462fab42a8682 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 14 Sep 2025 14:37:10 +1000 Subject: [PATCH 0379/1900] Note some previous attempts to change the Default impl for `[T; 0]` --- library/core/src/array/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index d14419a23a1d8..d713e575b58d3 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -472,6 +472,11 @@ impl SpecArrayClone for T { // The Default impls cannot be done with const generics because `[T; 0]` doesn't // require Default to be implemented, and having different impl blocks for // different numbers isn't supported yet. +// +// Trying to improve the `[T; 0]` situation has proven to be difficult. +// Please see these issues for more context on past attempts and crater runs: +// - https://github.com/rust-lang/rust/issues/61415 +// - https://github.com/rust-lang/rust/pull/145457 macro_rules! array_impl_default { {$n:expr, $t:ident $($ts:ident)*} => { From 3a784d9d1f292707000cdfce9072c876480471e4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 14 Sep 2025 04:52:56 +0000 Subject: [PATCH 0380/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to a015919e54c60b1b2bec7a98dec478cfc4a48f4e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e5f0be9ac9551..8315eb30f9431 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4ba1cf9ade4c8e2fa10676a50ee34594eb161837 +a015919e54c60b1b2bec7a98dec478cfc4a48f4e From 8df078a3f0071b311be2449407ae523f89ca6a33 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sat, 13 Sep 2025 21:59:39 +0000 Subject: [PATCH 0381/1900] RISC-V: Improvements of inline assembly uses This commit performs various improvements (better register allocation, less register clobbering on the worst case and better readability) of RISC-V inline assembly use cases. Note that it does not change the `p` module (which defines the "P" extension draft instructions but very likely to change). 1. Use `lateout` as possible. Unlike `out(reg)` and `in(reg)` pair, `lateout(reg)` and `in(reg)` can share the same register because they state that the late-output register is written after all the reads are performed. It can improve register allocation. 2. Add `preserves_flags` option as possible. While RISC-V doesn't have _regular_ condition codes, RISC-V inline assembly in the Rust language assumes that some registers (mainly vector state registers) may be overwritten by default. By adding `preserves_flags` to the intrinsics corresponding instructions without overwriting them, it can minimize register clobbering on the worst case. 3. Use trailing semicolon. As `asm!` declares an action and it doesn't return a value by itself, it would be better to have trailing semicolon to denote that an `asm!` call is effectively a statement. 4. Make most of `asm!` calls multi-lined. `rustfmt` makes some simple (yet long) `asm!` calls multi-lined but it does not perform formatting of complex `asm!` calls with inputs and/or outputs. To keep consistency, it makes most of the `asm!` calls multi-lined. --- .../crates/core_arch/src/riscv64/mod.rs | 21 +- .../crates/core_arch/src/riscv_shared/mod.rs | 245 ++++++++++++++---- 2 files changed, 215 insertions(+), 51 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/riscv64/mod.rs b/library/stdarch/crates/core_arch/src/riscv64/mod.rs index 0e860f6f2ad2f..a7efc0c7f58a1 100644 --- a/library/stdarch/crates/core_arch/src/riscv64/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv64/mod.rs @@ -20,7 +20,12 @@ pub use zk::*; #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_wu(src: *const u32) -> u32 { let value: u32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x681", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x681", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -38,7 +43,12 @@ pub unsafe fn hlv_wu(src: *const u32) -> u32 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_d(src: *const i64) -> i64 { let value: i64; - asm!(".insn i 0x73, 0x4, {}, {}, 0x6C0", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x6C0", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -53,5 +63,10 @@ pub unsafe fn hlv_d(src: *const i64) -> i64 { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_d(dst: *mut i64, src: i64) { - asm!(".insn r 0x73, 0x4, 0x37, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x37, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } diff --git a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs index 3ce24324de2e7..1bd147a64808c 100644 --- a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs @@ -44,7 +44,12 @@ use crate::arch::asm; #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn pause() { - unsafe { asm!(".insn i 0x0F, 0, x0, x0, 0x010", options(nomem, nostack)) } + unsafe { + asm!( + ".insn i 0x0F, 0, x0, x0, 0x010", + options(nomem, nostack, preserves_flags) + ); + } } /// Generates the `NOP` instruction @@ -54,7 +59,9 @@ pub fn pause() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn nop() { - unsafe { asm!("nop", options(nomem, nostack)) } + unsafe { + asm!("nop", options(nomem, nostack, preserves_flags)); + } } /// Generates the `WFI` instruction @@ -65,7 +72,7 @@ pub fn nop() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn wfi() { - asm!("wfi", options(nomem, nostack)) + asm!("wfi", options(nomem, nostack, preserves_flags)); } /// Generates the `FENCE.I` instruction @@ -78,7 +85,7 @@ pub unsafe fn wfi() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn fence_i() { - asm!("fence.i", options(nostack)) + asm!("fence.i", options(nostack, preserves_flags)); } /// Supervisor memory management fence for given virtual address and address space @@ -92,7 +99,7 @@ pub unsafe fn fence_i() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma(vaddr: usize, asid: usize) { - asm!("sfence.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + asm!("sfence.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); } /// Supervisor memory management fence for given virtual address @@ -104,7 +111,7 @@ pub unsafe fn sfence_vma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_vaddr(vaddr: usize) { - asm!("sfence.vma {}, x0", in(reg) vaddr, options(nostack)) + asm!("sfence.vma {}, x0", in(reg) vaddr, options(nostack, preserves_flags)); } /// Supervisor memory management fence for given address space @@ -118,7 +125,7 @@ pub unsafe fn sfence_vma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_asid(asid: usize) { - asm!("sfence.vma x0, {}", in(reg) asid, options(nostack)) + asm!("sfence.vma x0, {}", in(reg) asid, options(nostack, preserves_flags)); } /// Supervisor memory management fence for all address spaces and virtual addresses @@ -129,7 +136,7 @@ pub unsafe fn sfence_vma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_all() { - asm!("sfence.vma", options(nostack)) + asm!("sfence.vma", options(nostack, preserves_flags)); } /// Invalidate supervisor translation cache for given virtual address and address space @@ -139,8 +146,13 @@ pub unsafe fn sfence_vma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma(vaddr: usize, asid: usize) { - // asm!("sinval.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) - asm!(".insn r 0x73, 0, 0x0B, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("sinval.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x0B, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for given virtual address @@ -150,7 +162,11 @@ pub unsafe fn sinval_vma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x0B, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for given address space @@ -160,7 +176,11 @@ pub unsafe fn sinval_vma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x0B, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for all address spaces and virtual addresses @@ -170,7 +190,10 @@ pub unsafe fn sinval_vma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_all() { - asm!(".insn r 0x73, 0, 0x0B, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Generates the `SFENCE.W.INVAL` instruction @@ -180,8 +203,11 @@ pub unsafe fn sinval_vma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_w_inval() { - // asm!("sfence.w.inval", options(nostack)) - asm!(".insn i 0x73, 0, x0, x0, 0x180", options(nostack)) + // asm!("sfence.w.inval", options(nostack, preserves_flags)); + asm!( + ".insn i 0x73, 0, x0, x0, 0x180", + options(nostack, preserves_flags) + ); } /// Generates the `SFENCE.INVAL.IR` instruction @@ -191,8 +217,11 @@ pub unsafe fn sfence_w_inval() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_inval_ir() { - // asm!("sfence.inval.ir", options(nostack)) - asm!(".insn i 0x73, 0, x0, x0, 0x181", options(nostack)) + // asm!("sfence.inval.ir", options(nostack, preserves_flags)); + asm!( + ".insn i 0x73, 0, x0, x0, 0x181", + options(nostack, preserves_flags) + ); } /// Loads virtual machine memory by signed byte integer @@ -207,7 +236,12 @@ pub unsafe fn sfence_inval_ir() { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_b(src: *const i8) -> i8 { let value: i8; - asm!(".insn i 0x73, 0x4, {}, {}, 0x600", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x600", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -223,7 +257,12 @@ pub unsafe fn hlv_b(src: *const i8) -> i8 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_bu(src: *const u8) -> u8 { let value: u8; - asm!(".insn i 0x73, 0x4, {}, {}, 0x601", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x601", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -239,7 +278,12 @@ pub unsafe fn hlv_bu(src: *const u8) -> u8 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_h(src: *const i16) -> i16 { let value: i16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x640", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x640", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -255,7 +299,12 @@ pub unsafe fn hlv_h(src: *const i16) -> i16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_hu(src: *const u16) -> u16 { let value: u16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x641", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x641", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -271,7 +320,12 @@ pub unsafe fn hlv_hu(src: *const u16) -> u16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlvx_hu(src: *const u16) -> u16 { let insn: u16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x643", out(reg) insn, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x643", + lateout(reg) insn, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); insn } @@ -287,7 +341,12 @@ pub unsafe fn hlvx_hu(src: *const u16) -> u16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_w(src: *const i32) -> i32 { let value: i32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x680", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x680", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -303,7 +362,12 @@ pub unsafe fn hlv_w(src: *const i32) -> i32 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlvx_wu(src: *const u32) -> u32 { let insn: u32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x683", out(reg) insn, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x683", + lateout(reg) insn, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); insn } @@ -318,7 +382,12 @@ pub unsafe fn hlvx_wu(src: *const u32) -> u32 { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_b(dst: *mut i8, src: i8) { - asm!(".insn r 0x73, 0x4, 0x31, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x31, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Stores virtual machine memory by half integer @@ -332,7 +401,12 @@ pub unsafe fn hsv_b(dst: *mut i8, src: i8) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_h(dst: *mut i16, src: i16) { - asm!(".insn r 0x73, 0x4, 0x33, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x33, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Stores virtual machine memory by word integer @@ -346,7 +420,12 @@ pub unsafe fn hsv_h(dst: *mut i16, src: i16) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_w(dst: *mut i32, src: i32) { - asm!(".insn r 0x73, 0x4, 0x35, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x35, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest virtual address and guest address space @@ -360,8 +439,13 @@ pub unsafe fn hsv_w(dst: *mut i32, src: i32) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma(vaddr: usize, asid: usize) { - // asm!("hfence.vvma {}, {}", in(reg) vaddr, in(reg) asid) - asm!(".insn r 0x73, 0, 0x11, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("hfence.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x11, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest virtual address @@ -375,7 +459,11 @@ pub unsafe fn hfence_vvma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x11, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest address space @@ -389,7 +477,11 @@ pub unsafe fn hfence_vvma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x11, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for all guest address spaces and guest virtual addresses @@ -403,7 +495,10 @@ pub unsafe fn hfence_vvma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_all() { - asm!(".insn r 0x73, 0, 0x11, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for guest physical address and virtual machine @@ -416,8 +511,13 @@ pub unsafe fn hfence_vvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma(gaddr: usize, vmid: usize) { - // asm!("hfence.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) - asm!(".insn r 0x73, 0, 0x31, x0, {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) + // asm!("hfence.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x31, x0, {}, {}", + in(reg) gaddr, + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for guest physical address @@ -429,7 +529,11 @@ pub unsafe fn hfence_gvma(gaddr: usize, vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_gaddr(gaddr: usize) { - asm!(".insn r 0x73, 0, 0x31, x0, {}, x0", in(reg) gaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, {}, x0", + in(reg) gaddr, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given virtual machine @@ -441,7 +545,11 @@ pub unsafe fn hfence_gvma_gaddr(gaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_vmid(vmid: usize) { - asm!(".insn r 0x73, 0, 0x31, x0, x0, {}", in(reg) vmid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, x0, {}", + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for all virtual machines and guest physical addresses @@ -453,7 +561,10 @@ pub unsafe fn hfence_gvma_vmid(vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_all() { - asm!(".insn r 0x73, 0, 0x31, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest virtual address and guest address space @@ -465,8 +576,13 @@ pub unsafe fn hfence_gvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma(vaddr: usize, asid: usize) { - // asm!("hinval.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) - asm!(".insn r 0x73, 0, 0x13, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("hinval.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x13, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest virtual address @@ -478,7 +594,11 @@ pub unsafe fn hinval_vvma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x13, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest address space @@ -490,7 +610,11 @@ pub unsafe fn hinval_vvma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x13, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for all guest address spaces and guest virtual addresses @@ -502,7 +626,10 @@ pub unsafe fn hinval_vvma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_all() { - asm!(".insn r 0x73, 0, 0x13, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for guest physical address and virtual machine @@ -515,8 +642,13 @@ pub unsafe fn hinval_vvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma(gaddr: usize, vmid: usize) { - // asm!("hinval.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) - asm!(".insn r 0x73, 0, 0x33, x0, {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) + // asm!("hinval.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x33, x0, {}, {}", + in(reg) gaddr, + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for guest physical address @@ -528,7 +660,11 @@ pub unsafe fn hinval_gvma(gaddr: usize, vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_gaddr(gaddr: usize) { - asm!(".insn r 0x73, 0, 0x33, x0, {}, x0", in(reg) gaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, {}, x0", + in(reg) gaddr, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given virtual machine @@ -540,7 +676,11 @@ pub unsafe fn hinval_gvma_gaddr(gaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_vmid(vmid: usize) { - asm!(".insn r 0x73, 0, 0x33, x0, x0, {}", in(reg) vmid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, x0, {}", + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for all virtual machines and guest physical addresses @@ -552,7 +692,10 @@ pub unsafe fn hinval_gvma_vmid(vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_all() { - asm!(".insn r 0x73, 0, 0x33, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Reads the floating-point rounding mode register `frm` @@ -574,6 +717,12 @@ pub unsafe fn hinval_gvma_all() { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn frrm() -> u32 { let value: u32; - unsafe { asm!("frrm {}", out(reg) value, options(nomem, nostack)) }; + unsafe { + asm!( + "frrm {}", + out(reg) value, + options(nomem, nostack, preserves_flags) + ); + } value } From f56eb060479e6dcb386f7a67fa9f13ae3c31e6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 10 Sep 2025 11:01:34 -0700 Subject: [PATCH 0382/1900] merge crate-level into ALLOWED_TARGETS --- .../src/attributes/crate_level.rs | 40 ++++--------------- .../rustc_attr_parsing/src/attributes/mod.rs | 18 +-------- .../src/attributes/prelude.rs | 2 +- .../src/attributes/traits.rs | 5 +-- compiler/rustc_attr_parsing/src/context.rs | 4 +- compiler/rustc_attr_parsing/src/interface.rs | 1 - .../rustc_attr_parsing/src/target_checking.rs | 15 +++++-- 7 files changed, 23 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 0b2c05482bfaa..4611de4445997 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -43,11 +43,7 @@ impl SingleAttributeParser for CrateNameParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(n) = args else { @@ -76,11 +72,7 @@ impl SingleAttributeParser for RecursionLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -103,11 +95,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -130,11 +118,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -157,11 +141,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -182,11 +162,8 @@ pub(crate) struct NoCoreParser; impl NoArgsAttributeParser for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; - const TYPE: AttributeType = AttributeType::CrateLevel; } pub(crate) struct NoStdParser; @@ -194,9 +171,6 @@ pub(crate) struct NoStdParser; impl NoArgsAttributeParser for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; - const TYPE: AttributeType = AttributeType::CrateLevel; } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 043bc925eac9f..4ed13d239b9d6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,15 +12,11 @@ //! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! -//! By default, attributes are allowed anywhere. When adding an attribute that should only be used -//! at the crate root, consider setting the `TYPE` in the parser trait to -//! [`AttributeType::CrateLevel`](rustc_feature::AttributeType::CrateLevel). -//! //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. use std::marker::PhantomData; -use rustc_feature::{AttributeTemplate, AttributeType, template}; +use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::AttributeKind; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; @@ -89,11 +85,8 @@ pub(crate) trait AttributeParser: Default + 'static { /// /// If an attribute has this symbol, the `accept` function will be called on it. const ATTRIBUTES: AcceptMapping; - const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; - /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. /// @@ -135,8 +128,6 @@ pub(crate) trait SingleAttributeParser: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option; } @@ -183,8 +174,6 @@ impl, S: Stage> AttributeParser for Single )]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; - const TYPE: AttributeType = T::TYPE; - fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { Some(self.1?.0) } @@ -269,7 +258,6 @@ pub(crate) trait NoArgsAttributeParser: 'static { const PATH: &[Symbol]; const ON_DUPLICATE: OnDuplicate; const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; @@ -289,7 +277,6 @@ impl, S: Stage> SingleAttributeParser for Without const ON_DUPLICATE: OnDuplicate = T::ON_DUPLICATE; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); - const TYPE: AttributeType = T::TYPE; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { if let Err(span) = args.no_args() { @@ -323,8 +310,6 @@ pub(crate) trait CombineAttributeParser: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, @@ -360,7 +345,6 @@ impl, S: Stage> AttributeParser for Combine) -> Option { if let Some(first_span) = self.first_span { diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 8f040ffb9d41c..980366b5c372f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -1,6 +1,6 @@ // data structures #[doc(hidden)] -pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template}; +pub(super) use rustc_feature::{AttributeTemplate, template}; #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; #[doc(hidden)] diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index fbd9a480fbb6e..c756bce96e28f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -1,7 +1,5 @@ use std::mem; -use rustc_feature::AttributeType; - use super::prelude::*; use crate::attributes::{ AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, @@ -155,8 +153,7 @@ pub(crate) struct CoherenceIsCoreParser; impl NoArgsAttributeParser for CoherenceIsCoreParser { const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - const TYPE: AttributeType = AttributeType::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b3ab1d3edd6d2..d7998048be5af 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -6,7 +6,7 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; -use rustc_feature::{AttributeTemplate, AttributeType}; +use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; @@ -83,7 +83,6 @@ pub(super) struct GroupTypeInnerAccept { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn, pub(super) allowed_targets: AllowedTargets, - pub(super) attribute_type: AttributeType, } type AcceptFn = @@ -133,7 +132,6 @@ macro_rules! attribute_parsers { }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE, }); } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0fe3c209421fd..8f2de4af14e00 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -272,7 +272,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { (accept.accept_fn)(&mut cx, args); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { - Self::check_type(accept.attribute_type, target, &mut cx); Self::check_target(&accept.allowed_targets, target, &mut cx); } } diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index edc496b460cf8..c52253699b51c 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; -use rustc_feature::{AttributeType, Features}; +use rustc_feature::Features; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{MethodKind, Target}; @@ -14,6 +14,11 @@ use crate::session_diagnostics::InvalidTarget; pub(crate) enum AllowedTargets { AllowList(&'static [Policy]), AllowListWarnRest(&'static [Policy]), + /// Special, and not the same as `AllowList(&[Allow(Target::Crate)])`. + /// For crate-level attributes we emit a specific set of lints to warn + /// people about accidentally not using them on the crate. + /// Only use this for attributes that are *exclusively* valid at the crate level. + CrateLevel, } pub(crate) enum AllowedResult { @@ -43,6 +48,7 @@ impl AllowedTargets { AllowedResult::Warn } } + AllowedTargets::CrateLevel => AllowedResult::Allowed, } } @@ -50,6 +56,7 @@ impl AllowedTargets { match self { AllowedTargets::AllowList(list) => list, AllowedTargets::AllowListWarnRest(list) => list, + AllowedTargets::CrateLevel => ALL_TARGETS, } .iter() .filter_map(|target| match target { @@ -74,6 +81,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { + Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx); + match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { @@ -109,7 +118,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } pub(crate) fn check_type( - attribute_type: AttributeType, + crate_level: bool, target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { @@ -119,7 +128,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - if attribute_type != AttributeType::CrateLevel { + if !crate_level { return; } From d8cc5757ba92085c4fe19e86c4293bb619b2385f Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Tue, 9 Sep 2025 22:27:37 -0700 Subject: [PATCH 0383/1900] std_detect Darwin AArch64: add new-style detection for FEAT_CRC32 Now that this feature has a standard identifier, Darwin has started exposing it accordingly, in addition to the existing less-standard way. Check both, and enable the `crc` feature if either identifier for it is present to ensure backwards and forwards compatibility. --- library/std_detect/src/detect/os/darwin/aarch64.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std_detect/src/detect/os/darwin/aarch64.rs b/library/std_detect/src/detect/os/darwin/aarch64.rs index f5409361d93b9..89521c1c6afb7 100644 --- a/library/std_detect/src/detect/os/darwin/aarch64.rs +++ b/library/std_detect/src/detect/os/darwin/aarch64.rs @@ -37,12 +37,13 @@ pub(crate) fn detect_features() -> cache::Initializer { // Armv8.0 features not using the standard identifiers let fp = _sysctlbyname(c"hw.optional.floatingpoint"); let asimd = _sysctlbyname(c"hw.optional.AdvSIMD"); - let crc = _sysctlbyname(c"hw.optional.armv8_crc32"); + let crc_old = _sysctlbyname(c"hw.optional.armv8_crc32"); // Armv8 and Armv9 features using the standard identifiers let aes = _sysctlbyname(c"hw.optional.arm.FEAT_AES"); let bf16 = _sysctlbyname(c"hw.optional.arm.FEAT_BF16"); let bti = _sysctlbyname(c"hw.optional.arm.FEAT_BTI"); + let crc = _sysctlbyname(c"hw.optional.arm.FEAT_CRC32"); let cssc = _sysctlbyname(c"hw.optional.arm.FEAT_CSSC"); let dit = _sysctlbyname(c"hw.optional.arm.FEAT_DIT"); let dpb = _sysctlbyname(c"hw.optional.arm.FEAT_DPB"); @@ -111,7 +112,7 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::asimd, asimd); enable_feature(Feature::bf16, bf16); enable_feature(Feature::bti, bti); - enable_feature(Feature::crc, crc); + enable_feature(Feature::crc, crc_old || crc); enable_feature(Feature::cssc, cssc); enable_feature(Feature::dit, dit); enable_feature(Feature::dotprod, dotprod); From edc87e301b7eee22331b147dcfac2f855fdfb03a Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Sat, 13 Sep 2025 23:18:16 -0700 Subject: [PATCH 0384/1900] std_detect Darwin AArch64: re-alphabetize --- library/std_detect/src/detect/os/darwin/aarch64.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std_detect/src/detect/os/darwin/aarch64.rs b/library/std_detect/src/detect/os/darwin/aarch64.rs index 89521c1c6afb7..7d9229592aaaf 100644 --- a/library/std_detect/src/detect/os/darwin/aarch64.rs +++ b/library/std_detect/src/detect/os/darwin/aarch64.rs @@ -46,16 +46,16 @@ pub(crate) fn detect_features() -> cache::Initializer { let crc = _sysctlbyname(c"hw.optional.arm.FEAT_CRC32"); let cssc = _sysctlbyname(c"hw.optional.arm.FEAT_CSSC"); let dit = _sysctlbyname(c"hw.optional.arm.FEAT_DIT"); + let dotprod = _sysctlbyname(c"hw.optional.arm.FEAT_DotProd"); let dpb = _sysctlbyname(c"hw.optional.arm.FEAT_DPB"); let dpb2 = _sysctlbyname(c"hw.optional.arm.FEAT_DPB2"); - let dotprod = _sysctlbyname(c"hw.optional.arm.FEAT_DotProd"); let ecv = _sysctlbyname(c"hw.optional.arm.FEAT_ECV"); let fcma = _sysctlbyname(c"hw.optional.arm.FEAT_FCMA"); let fhm = _sysctlbyname(c"hw.optional.arm.FEAT_FHM"); - let fp16 = _sysctlbyname(c"hw.optional.arm.FEAT_FP16"); - let frintts = _sysctlbyname(c"hw.optional.arm.FEAT_FRINTTS"); let flagm = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM"); let flagm2 = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM2"); + let fp16 = _sysctlbyname(c"hw.optional.arm.FEAT_FP16"); + let frintts = _sysctlbyname(c"hw.optional.arm.FEAT_FRINTTS"); let hbc = _sysctlbyname(c"hw.optional.arm.FEAT_HBC"); let i8mm = _sysctlbyname(c"hw.optional.arm.FEAT_I8MM"); let jsconv = _sysctlbyname(c"hw.optional.arm.FEAT_JSCVT"); From d49f6a789ffe4b498fca1f6e173ea5ab727477a6 Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Sat, 13 Sep 2025 23:24:16 -0700 Subject: [PATCH 0385/1900] std_detect Darwin AArch64: synchronize features Brings the list of checkable features up to date with the initial release of macOS 26 "Tahoe". --- library/std_detect/src/detect/os/darwin/aarch64.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/std_detect/src/detect/os/darwin/aarch64.rs b/library/std_detect/src/detect/os/darwin/aarch64.rs index 7d9229592aaaf..8c9fd9647b8a2 100644 --- a/library/std_detect/src/detect/os/darwin/aarch64.rs +++ b/library/std_detect/src/detect/os/darwin/aarch64.rs @@ -63,6 +63,8 @@ pub(crate) fn detect_features() -> cache::Initializer { let rcpc2 = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC2"); let lse = _sysctlbyname(c"hw.optional.arm.FEAT_LSE"); let lse2 = _sysctlbyname(c"hw.optional.arm.FEAT_LSE2"); + let mte = _sysctlbyname(c"hw.optional.arm.FEAT_MTE"); + let mte2 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE2"); let pauth = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth"); let pmull = _sysctlbyname(c"hw.optional.arm.FEAT_PMULL"); let rdm = _sysctlbyname(c"hw.optional.arm.FEAT_RDM"); @@ -73,6 +75,7 @@ pub(crate) fn detect_features() -> cache::Initializer { let sha512 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA512"); let sme = _sysctlbyname(c"hw.optional.arm.FEAT_SME"); let sme2 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2"); + let sme2p1 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2p1"); let sme_f64f64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F64F64"); let sme_i16i64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_I16I64"); let ssbs = _sysctlbyname(c"hw.optional.arm.FEAT_SSBS"); @@ -88,6 +91,12 @@ pub(crate) fn detect_features() -> cache::Initializer { let ebf16 = _sysctlbyname(c"hw.optional.arm.FEAT_EBF16"); let fpac = _sysctlbyname(c"hw.optional.arm.FEAT_FPAC"); let fpaccombine = _sysctlbyname(c"hw.optional.arm.FEAT_FPACCOMBINE"); + let mte_async = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_ASYNC"); + let mte_canonical_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_CANONICAL_TAGS"); + let mte_no_address_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_NO_ADDRESS_TAGS"); + let mte_store_only = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_STORE_ONLY"); + let mte3 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE3"); + let mte4 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE4"); let pacimp = _sysctlbyname(c"hw.optional.arm.FEAT_PACIMP"); let pauth2 = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth2"); let rpres = _sysctlbyname(c"hw.optional.arm.FEAT_RPRES"); @@ -131,6 +140,7 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::jsconv, jsconv); enable_feature(Feature::lse, lse); enable_feature(Feature::lse2, lse2); + enable_feature(Feature::mte, mte && mte2); enable_feature(Feature::paca, pauth); enable_feature(Feature::pacg, pauth); enable_feature(Feature::pmull, aes && pmull); @@ -142,6 +152,7 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::sha3, sha512 && sha3 && asimd); enable_feature(Feature::sme, sme); enable_feature(Feature::sme2, sme2); + enable_feature(Feature::sme2p1, sme2p1); enable_feature(Feature::sme_f64f64, sme_f64f64); enable_feature(Feature::sme_i16i64, sme_i16i64); enable_feature(Feature::ssbs, ssbs); From 074bff027e09e35b929a09ba834c68546e4a1194 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 14 Sep 2025 08:56:40 +0200 Subject: [PATCH 0386/1900] Remove unnecessary `#![allow]` in test --- tests/ui/never_loop_fixable.fixed | 2 +- tests/ui/never_loop_fixable.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed index 00c2af93a28ff..5bc9ff1bb4df0 100644 --- a/tests/ui/never_loop_fixable.fixed +++ b/tests/ui/never_loop_fixable.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] +#![allow(clippy::iter_next_slice, clippy::needless_return)] fn no_break_or_continue_loop() { if let Some(i) = [1, 2, 3].iter().next() { diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs index de85599f094d6..9782bc107e9a6 100644 --- a/tests/ui/never_loop_fixable.rs +++ b/tests/ui/never_loop_fixable.rs @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] +#![allow(clippy::iter_next_slice, clippy::needless_return)] fn no_break_or_continue_loop() { for i in [1, 2, 3].iter() { From b92330b7b87ba86abccd6b4f4298652a9cbed606 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 30 Aug 2025 18:08:50 +0200 Subject: [PATCH 0387/1900] fix `nonstandard_macro_braces`: suggest trailing semicolon when needed --- clippy_lints/src/nonstandard_macro_braces.rs | 91 +++++++++++++------ .../conf_nonstandard_macro_braces.fixed | 8 ++ .../conf_nonstandard_macro_braces.rs | 8 ++ .../conf_nonstandard_macro_braces.stderr | 8 +- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 83f7d9319697b..3a8a4dd0c7137 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -16,8 +16,8 @@ declare_clippy_lint! { /// Checks that common macros are used with consistent bracing. /// /// ### Why is this bad? - /// This is mostly a consistency lint although using () or [] - /// doesn't give you a semicolon in item position, which can be unexpected. + /// Having non-conventional braces on well-stablished macros can be confusing + /// when debugging, and they bring incosistencies with the rest of the ecosystem. /// /// ### Example /// ```no_run @@ -33,8 +33,12 @@ declare_clippy_lint! { "check consistent use of braces in macro" } -/// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo = (Span, (char, char), SourceText); +struct MacroInfo { + callsite_span: Span, + callsite_snippet: SourceText, + old_open_brace: char, + braces: (char, char), +} pub struct MacroBraces { macro_braces: FxHashMap, @@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, item.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + old_open_brace, + }) = is_offending_macro(cx, stmt.span, self) + { + // if we turn `macro!{}` into `macro!()`/`macro![]`, we'll no longer get the implicit + // trailing semicolon, see #9913 + // NOTE: `stmt.kind != StmtKind::MacCall` because `EarlyLintPass` happens after macro expansion + let add_semi = matches!(stmt.kind, ast::StmtKind::Expr(..)) && old_open_brace == '{'; + emit_help(cx, &callsite_snippet, braces, callsite_span, add_semi); + self.done.insert(callsite_span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, expr.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + braces, + callsite_snippet, + .. + }) = is_offending_macro(cx, ty.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } } @@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace .last() .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; - let span_call_site = span.ctxt().outer_expn_data().call_site; + let callsite_span = span.ctxt().outer_expn_data().call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = span_call_site.get_source_text(cx) + && let Some(snip) = callsite_span.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - && snip.starts_with(&format!("{name}!")) + && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) + && let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next() + && old_open_brace != braces.0 && unnested_or_local() - // make formatting consistent - && let c = snip.replace(' ', "") - && !c.starts_with(&format!("{name}!{}", braces.0)) - && !mac_braces.done.contains(&span_call_site) + && !mac_braces.done.contains(&callsite_span) { - Some((span_call_site, braces, snip)) + Some(MacroInfo { + callsite_span, + callsite_snippet: snip, + old_open_brace, + braces, + }) } else { None } } -fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span) { +fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) { + let semi = if add_semi { ";" } else { "" }; if let Some((macro_name, macro_args_str)) = snip.split_once('!') { let mut macro_args = macro_args_str.trim().to_string(); // now remove the wrong braces - macro_args.remove(0); macro_args.pop(); + macro_args.remove(0); span_lint_and_sugg( cx, NONSTANDARD_MACRO_BRACES, span, format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", - format!("{macro_name}!{open}{macro_args}{close}"), + format!("{macro_name}!{open}{macro_args}{close}{semi}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed index 8da607ec65842..419e62f92f469 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println!("hello world"); + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index e35844a209fa5..b0bbced4ea3c2 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println! {"hello world"} + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index fda6addc7aa35..87325f05c9bcd 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -54,5 +54,11 @@ error: use of irregular braces for `eprint!` macro LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` -error: aborting due to 8 previous errors +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5 + | +LL | println! {"hello world"} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");` + +error: aborting due to 9 previous errors From 215c936aa0abd6a11daa8c4313f391163f15d6b8 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 7 Sep 2025 18:39:36 +0100 Subject: [PATCH 0388/1900] Promote armv7a-none-eabihf to Tier 2 This is the target for 32-bit Cortex-A bare-metal, when using the FPU. The target is well tested by the Embedded Devices Working Group, and the soft-float target (armv7a-none-eabi) is already Tier 2. --- compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs | 2 +- src/doc/rustc/src/platform-support.md | 2 +- src/doc/rustc/src/platform-support/armv7a-none-eabi.md | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs index df3a76599a753..217a79d4571e9 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs @@ -30,7 +30,7 @@ pub(crate) fn target() -> Target { llvm_target: "armv7a-none-eabihf".into(), metadata: TargetMetadata { description: Some("Bare Armv7-A, hardfloat".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(false), }, diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e5e46f7263751..a0a228cad2a0f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -165,6 +165,7 @@ target | std | notes `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A +[`armv7a-none-eabihf`](platform-support/armv7a-none-eabi.md) | * | | Bare Armv7-A, hardfloat [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87] @@ -300,7 +301,6 @@ target | std | host | notes [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat -[`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat [`armv7a-vex-v5`](platform-support/armv7a-vex-v5.md) | ? | | Armv7-A Cortex-A9 VEX V5 Brain, VEXos [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS [`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 3dadda86a5f54..4eeee6ae2c907 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -1,7 +1,6 @@ # `armv7a-none-eabi` and `armv7a-none-eabihf` -* **Tier: 2** for `armv7a-none-eabi` -* **Tier: 3** for `armv7a-none-eabihf` +* **Tier: 2** * **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv7-A architecture family, supporting From 89a5bb618050813e681fadea739a71fb6e629f24 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 7 Sep 2025 18:37:38 +0100 Subject: [PATCH 0389/1900] Raise armv8r-none-eabihf to Tier 2 This is the target for supporting Arm Cortex-R52 bare-metal systems, which are common in safety-critical systems. --- src/doc/rustc/src/platform-support.md | 2 +- src/doc/rustc/src/platform-support/armv8r-none-eabihf.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e5e46f7263751..6fb1c78d3f31e 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -167,6 +167,7 @@ target | std | notes [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat +[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] @@ -304,7 +305,6 @@ target | std | host | notes [`armv7a-vex-v5`](platform-support/armv7a-vex-v5.md) | ? | | Armv7-A Cortex-A9 VEX V5 Brain, VEXos [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS [`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS -[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat [`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX [`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat [`avr-none`](platform-support/avr-none.md) | * | | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...` diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index 0d5a36c3ee2d6..47186fdb2cdb8 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -1,6 +1,6 @@ # `armv8r-none-eabihf` -* **Tier: 3** +* **Tier: 2** * **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv8-R architecture family, supporting From bfddf29524878bb4ca9b43ab1912b440f3023c43 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 13 Sep 2025 20:16:32 +0100 Subject: [PATCH 0390/1900] Update target spec to say Tier 2. --- compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs index 8cf1ff9575168..d6e3a03bbba8b 100644 --- a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { llvm_target: "armv8r-none-eabihf".into(), metadata: TargetMetadata { description: Some("Bare Armv8-R, hardfloat".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(false), }, From 430d3532662fe309f3893ac2680775c1a5db0c41 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 7 Sep 2025 18:36:15 +0100 Subject: [PATCH 0391/1900] Drop armebv7r-none-eabi* to Tier 3 These targets are not widely used, and are difficult to test because qemu-system-arm cannot emulate them. --- compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs | 2 +- .../rustc_target/src/spec/targets/armebv7r_none_eabihf.rs | 2 +- src/doc/rustc/src/platform-support.md | 4 ++-- src/doc/rustc/src/platform-support/armebv7r-none-eabi.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs index d227d63c4a346..0ae7cd7a3773d 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { llvm_target: "armebv7r-none-eabi".into(), metadata: TargetMetadata { description: Some("Bare Armv7-R, Big Endian".into()), - tier: Some(2), + tier: Some(3), host_tools: Some(false), std: Some(false), }, diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs index c373afb914e3a..71ffd8baed57b 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { llvm_target: "armebv7r-none-eabihf".into(), metadata: TargetMetadata { description: Some("Bare Armv7-R, Big Endian, hardfloat".into()), - tier: Some(2), + tier: Some(3), host_tools: Some(false), std: Some(false), }, diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e5e46f7263751..3f8e753334e79 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -156,8 +156,6 @@ target | std | notes `arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 `arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC -[`armebv7r-none-eabi`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian -[`armebv7r-none-eabihf`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23) `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android @@ -283,6 +281,8 @@ target | std | host | notes [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-tvos`](platform-support/arm64e-apple-tvos.md) | ✓ | | ARM64e Apple tvOS [`armeb-unknown-linux-gnueabi`](platform-support/armeb-unknown-linux-gnueabi.md) | ✓ | ? | Arm BE8 the default Arm big-endian architecture since [Armv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). +[`armebv7r-none-eabi`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian +[`armebv7r-none-eabihf`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat [`armv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Bare Armv4T `armv4t-unknown-linux-gnueabi` | ? | | Armv4T Linux [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare Armv5TE diff --git a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md index 3e90319c373e4..d5c676ea9a4c0 100644 --- a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md @@ -1,6 +1,6 @@ # `armebv7r-none-eabi` and `armebv7r-none-eabihf` -* **Tier: 2** +* **Tier: 3** * **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv7-R architecture family running in Big From 619a6964c757e9e94253fa6618fb03519ad2b55e Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 22:41:43 +0300 Subject: [PATCH 0392/1900] clean up issue-2284 (core marker trait name shadowing) --- tests/ui/issues/issue-2284.rs | 13 ------------ .../core-marker-name-shadowing-issue-2284.rs | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/issues/issue-2284.rs create mode 100644 tests/ui/traits/core-marker-name-shadowing-issue-2284.rs diff --git a/tests/ui/issues/issue-2284.rs b/tests/ui/issues/issue-2284.rs deleted file mode 100644 index 358331ecd9a4c..0000000000000 --- a/tests/ui/issues/issue-2284.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -trait Send { - fn f(&self); -} - -fn f(t: T) { - t.f(); -} - -pub fn main() { -} diff --git a/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs b/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs new file mode 100644 index 0000000000000..e5d083ac8c3fb --- /dev/null +++ b/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs @@ -0,0 +1,21 @@ +//@ run-pass +#![allow(dead_code)] + +//! Tests that user-defined trait is prioritized in compile time over +//! the core::marker trait with the same name, allowing shadowing core traits. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/2284 +//! Original fix pull request: https://github.com/rust-lang/rust/pull/3792 + + +trait Send { + fn f(&self); +} + +fn f(t: T) { + t.f(); +} + +pub fn main() { +} From 05a5c7d0a104d3c3efbccb0aaf01b51495ad3080 Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 23:43:20 +0300 Subject: [PATCH 0393/1900] clean up issue-19479 (assoc type from another trait) --- .../assoc-type-via-another-trait-issue-19479.rs} | 6 ++++++ 1 file changed, 6 insertions(+) rename tests/ui/{issues/issue-19479.rs => traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs} (50%) diff --git a/tests/ui/issues/issue-19479.rs b/tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs similarity index 50% rename from tests/ui/issues/issue-19479.rs rename to tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs index ed586b7655028..f17a89bcb5f42 100644 --- a/tests/ui/issues/issue-19479.rs +++ b/tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs @@ -1,5 +1,11 @@ //@ check-pass +//! Tests that it's possible to define an associated type in a trait +//! using an associated type from type parameter bound trait in a blanket implementation. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/19479 + trait Base { fn dummy(&self) { } } From 8ee3a08b871f8b24075f67eda06330f7005cd435 Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 23:56:55 +0300 Subject: [PATCH 0394/1900] clean up issue-18088 (operator from supertrait) --- tests/ui/issues/issue-18088.rs | 8 -------- .../inheritance/supertrait-operator-issue-18088.rs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) delete mode 100644 tests/ui/issues/issue-18088.rs create mode 100644 tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs diff --git a/tests/ui/issues/issue-18088.rs b/tests/ui/issues/issue-18088.rs deleted file mode 100644 index ba198884c639f..0000000000000 --- a/tests/ui/issues/issue-18088.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ check-pass - -pub trait Indexable: std::ops::Index { - fn index2(&self, i: usize) -> &T { - &self[i] - } -} -fn main() {} diff --git a/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs b/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs new file mode 100644 index 0000000000000..8bd45cd54a445 --- /dev/null +++ b/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs @@ -0,0 +1,13 @@ +//@ check-pass + +//! Tests that operators from supertrait are available directly on `self` for an inheritor trait. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/18088 + +pub trait Indexable: std::ops::Index { + fn index2(&self, i: usize) -> &T { + &self[i] + } +} +fn main() {} From 22aecd3001038d0ac00ecd06985e2b0abc57e6dc Mon Sep 17 00:00:00 2001 From: omskscream Date: Sun, 14 Sep 2025 01:18:18 +0300 Subject: [PATCH 0395/1900] clean up issue-21950 (dyn trait cast without assoc type at the cast) --- tests/ui/issues/issue-21950.rs | 12 ------------ ...-as-dyn-trait-wo-assoc-type-issue-21950.rs | 19 +++++++++++++++++++ ...yn-trait-wo-assoc-type-issue-21950.stderr} | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/issues/issue-21950.rs create mode 100644 tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs rename tests/ui/{issues/issue-21950.stderr => traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr} (85%) diff --git a/tests/ui/issues/issue-21950.rs b/tests/ui/issues/issue-21950.rs deleted file mode 100644 index 7a85ac91bca0f..0000000000000 --- a/tests/ui/issues/issue-21950.rs +++ /dev/null @@ -1,12 +0,0 @@ -trait Add { - type Output; -} - -impl Add for i32 { - type Output = i32; -} - -fn main() { - let x = &10 as &dyn Add; - //~^ ERROR E0191 -} diff --git a/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs new file mode 100644 index 0000000000000..3c3815054502f --- /dev/null +++ b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs @@ -0,0 +1,19 @@ +//! Tests that compiler yields error E0191 when value with existing trait implementation +//! is cast as same `dyn` trait without specifying associated type at the cast. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/21950 + +trait Add { + type Output; +} + +impl Add for i32 { + type Output = i32; +} + +fn main() { + let x = &10 as &dyn Add; //OK + let x = &10 as &dyn Add; + //~^ ERROR E0191 +} diff --git a/tests/ui/issues/issue-21950.stderr b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr similarity index 85% rename from tests/ui/issues/issue-21950.stderr rename to tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr index 24230cfe17f3d..5f4974e6f237d 100644 --- a/tests/ui/issues/issue-21950.stderr +++ b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr @@ -1,5 +1,5 @@ error[E0191]: the value of the associated type `Output` in `Add` must be specified - --> $DIR/issue-21950.rs:10:25 + --> $DIR/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs:17:25 | LL | type Output; | ----------- `Output` defined here From 25881bfc3400005b047c8eb553fb7bb2fab25950 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 12 Sep 2025 18:56:52 +0200 Subject: [PATCH 0396/1900] fix(elidable_lifetime_names): avoid overlapping spans in suggestions --- clippy_lints/src/lifetimes.rs | 97 ++++++++++++---- tests/ui/crashes/ice-15666.fixed | 6 + tests/ui/crashes/ice-15666.rs | 6 + tests/ui/crashes/ice-15666.stderr | 16 +++ tests/ui/elidable_lifetime_names.fixed | 82 +++++++++++++ tests/ui/elidable_lifetime_names.rs | 82 +++++++++++++ tests/ui/elidable_lifetime_names.stderr | 146 +++++++++++++++++++++++- 7 files changed, 412 insertions(+), 23 deletions(-) create mode 100644 tests/ui/crashes/ice-15666.fixed create mode 100644 tests/ui/crashes/ice-15666.rs create mode 100644 tests/ui/crashes/ice-15666.stderr diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index aac5d08fd37c8..d8b186b6787d1 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -856,36 +856,89 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - let mut suggestions = if elidable_lts.len() == explicit_params.len() { + if !elidable_lts + .iter() + .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) + { + return None; + } + + let mut suggestions = if elidable_lts.is_empty() { + vec![] + } else if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) + match &explicit_params[..] { + // no params, nothing to elide + [] => unreachable!("handled by `elidable_lts.is_empty()`"), + [param] => { + if elidable_lts.contains(¶m.def_id) { + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; - - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) + unreachable!("handled by `elidable_lts.is_empty()`") + } + }, + [_, _, ..] => { + // Given a list like `<'a, 'b, 'c, 'd, ..>`, + // + // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should + // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: + // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> + // ^^ ^^ ^^^^^^^^ + // + // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go + // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the + // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave + // the list valid: + // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^^^ ^^^^^^^^ + // + // In case there is no such starting cluster, we only need to do the second part of the algorithm: + // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ + + // Split off the starting cluster + // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): + // ``` + // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { + // // there were no lifetime param that couldn't be elided + // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // }; + // match split { /* .. */ } + // ``` + let Some(split_pos) = explicit_params + .iter() + .position(|param| !elidable_lts.contains(¶m.def_id)) + else { + // there were no lifetime param that couldn't be elided + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") }; - - Some((span, String::new())) - }) - .collect::>>()? + let split = explicit_params + .split_at_checked(split_pos) + .expect("got `split_pos` from `position` on the same Vec"); + + match split { + ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), + ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), + (cluster, rest @ [rest_first, ..]) => { + // the span for the cluster + (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) + // the span for the remaining lifetimes (calculations independent of the cluster) + .chain( + rest.array_windows() + .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) + .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), + ) + .map(|sp| (sp, String::new())) + .collect() + }, + } + }, + } }; suggestions.extend(usages.iter().map(|&usage| { diff --git a/tests/ui/crashes/ice-15666.fixed b/tests/ui/crashes/ice-15666.fixed new file mode 100644 index 0000000000000..53b618765c0f0 --- /dev/null +++ b/tests/ui/crashes/ice-15666.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.rs b/tests/ui/crashes/ice-15666.rs new file mode 100644 index 0000000000000..1414b3d2035e4 --- /dev/null +++ b/tests/ui/crashes/ice-15666.rs @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.stderr b/tests/ui/crashes/ice-15666.stderr new file mode 100644 index 0000000000000..b417c09b5c65a --- /dev/null +++ b/tests/ui/crashes/ice-15666.stderr @@ -0,0 +1,16 @@ +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/crashes/ice-15666.rs:5:11 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | + = note: `-D clippy::elidable-lifetime-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::elidable_lifetime_names)]` +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/elidable_lifetime_names.fixed b/tests/ui/elidable_lifetime_names.fixed index abeee5c4cef34..a6c4cb7a36a8d 100644 --- a/tests/ui/elidable_lifetime_names.fixed +++ b/tests/ui/elidable_lifetime_names.fixed @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl T for S1<'_> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S2<'a, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S2<'_, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl T for S2<'_, '_> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S3<'a, '_, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl T for S3<'_, '_, '_> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.rs b/tests/ui/elidable_lifetime_names.rs index fae3577a8e960..e08056b2fb56b 100644 --- a/tests/ui/elidable_lifetime_names.rs +++ b/tests/ui/elidable_lifetime_names.rs @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl<'a> T for S1<'a> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> T for S2<'a, 'b> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.stderr b/tests/ui/elidable_lifetime_names.stderr index a60dfc697564e..03fe383b8f67f 100644 --- a/tests/ui/elidable_lifetime_names.stderr +++ b/tests/ui/elidable_lifetime_names.stderr @@ -158,5 +158,149 @@ LL | o: &'t str, LL ~ ) -> Content<'t, '_> { | -error: aborting due to 12 previous errors +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/elidable_lifetime_names.rs:202:15 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:226:10 + | +LL | impl<'a> T for S1<'a> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a> T for S1<'a> {} +LL + impl T for S1<'_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:234:14 + | +LL | impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TA<'a> for S2<'a, 'b> {} +LL + impl<'a> TA<'a> for S2<'a, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:238:10 + | +LL | impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TB<'b> for S2<'a, 'b> {} +LL + impl<'b> TB<'b> for S2<'_, 'b> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:242:10 + | +LL | impl<'a, 'b> T for S2<'a, 'b> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> T for S2<'a, 'b> {} +LL + impl T for S2<'_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'c + --> tests/ui/elidable_lifetime_names.rs:250:18 + | +LL | impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:254:14 + | +LL | impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:258:14 + | +LL | impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} +LL + impl<'a> TA<'a> for S3<'a, '_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:262:10 + | +LL | impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'c + --> tests/ui/elidable_lifetime_names.rs:266:10 + | +LL | impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} +LL + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:270:10 + | +LL | impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} +LL + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:274:10 + | +LL | impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} +LL + impl T for S3<'_, '_, '_> {} + | + +error: aborting due to 24 previous errors From 05156f4e8b1c0e0ef6eedcf99cfedf142c8f56fb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 21:59:03 +0200 Subject: [PATCH 0397/1900] clean-up `single_match` a bit --- clippy_lints/src/matches/single_match.rs | 27 +++++++++++++----------- tests/ui/single_match.fixed | 5 +++-- tests/ui/single_match.rs | 5 +++-- tests/ui/single_match.stderr | 20 +++++++++--------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b700703..0470b5443d189 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -24,17 +24,16 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - if let Some(ff) = span.get_source_range(cx) - && let Some(text) = ff.as_str() - { - text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") - } else { - false - } + span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } -#[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { +pub(crate) fn check<'tcx>( + cx: &LateContext<'tcx>, + ex: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, + contains_comments: bool, +) { if let [arm1, arm2] = arms && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() @@ -226,13 +225,13 @@ enum PatState<'a> { Wild, /// A std enum we know won't be extended. Tracks the states of each variant separately. /// - /// This is not used for `Option` since it uses the current pattern to track it's state. + /// This is not used for `Option` since it uses the current pattern to track its state. StdEnum(&'a mut [PatState<'a>]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// /// For non-std enums there's no need to track the state of sub-patterns as the state of just - /// this pattern on it's own is enough for linting. Consider two cases: + /// this pattern on its own is enough for linting. Consider two cases: /// * This enum has no wild match. This case alone is enough to determine we can lint. /// * This enum has a wild match and therefore all sub-patterns also have a wild match. /// @@ -380,7 +379,11 @@ impl<'a> PatState<'a> { self.add_pat(cx, pat) }, PatKind::Tuple([sub_pat], pos) - if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + // `pat` looks like `(sub_pat)`, without a `..` -- has only one sub-pattern + if pos.as_opt_usize().is_none() + // `pat` looks like `(sub_pat, ..)` or `(.., sub_pat)`, but its type is a unary tuple, + // so it still only has one sub-pattern + || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => { self.add_pat(cx, sub_pat) }, diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index db5107600ee6d..03982b069e675 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -218,7 +218,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { if let Some(v) = bar { unsafe { let r = &v as *const i32; println!("{}", *r); @@ -330,6 +330,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { println!(); //~^^^^ single_match @@ -367,7 +368,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index a367b94c4ca6b..e28128e35adab 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -269,7 +269,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { match bar { Some(v) => unsafe { let r = &v as *const i32; @@ -397,6 +397,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { match DATA { DATA => println!(), @@ -462,7 +463,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 1a4edc45c928d..ba8bc5af5a608 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -225,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:401:5 + --> tests/ui/single_match.rs:402:5 | LL | / match DATA { LL | | DATA => println!(), @@ -234,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:407:5 + --> tests/ui/single_match.rs:408:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -243,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:414:5 + --> tests/ui/single_match.rs:415:5 | LL | / match i { LL | | i => { @@ -263,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:423:5 + --> tests/ui/single_match.rs:424:5 | LL | / match i { LL | | i => {}, @@ -272,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:429:5 + --> tests/ui/single_match.rs:430:5 | LL | / match i { LL | | i => (), @@ -281,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:435:5 + --> tests/ui/single_match.rs:436:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -290,7 +290,7 @@ LL | | } | |_____^ help: try: `println!();` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:443:5 + --> tests/ui/single_match.rs:444:5 | LL | / match x.pop() { LL | | // bla @@ -302,7 +302,7 @@ LL | | } = note: you might want to preserve the comments from inside the `match` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:452:5 + --> tests/ui/single_match.rs:453:5 | LL | / match x.pop() { LL | | // bla @@ -322,7 +322,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:478:5 + --> tests/ui/single_match.rs:479:5 | LL | / match mac!(some) { LL | | Some(u) => println!("{u}"), @@ -331,7 +331,7 @@ LL | | } | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:486:5 + --> tests/ui/single_match.rs:487:5 | LL | / match mac!(str) { LL | | "foo" => println!("eq"), From 7791178805c1bccd2696c123ba5e5e4c896cf7d6 Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Sun, 14 Sep 2025 12:11:58 +0200 Subject: [PATCH 0398/1900] fix 404 link --- src/doc/rustc/src/target-tier-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index 28d3dc32a63ee..f6b78eed24fc4 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -701,4 +701,4 @@ RFC process, with approval by the compiler and infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. -[MCP]: https://forge.rust-lang.org/compiler/mcp.html +[MCP]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#how-do-i-submit-an-mcp From 2e816736efaebf1f4666efac1817bcccd78a3e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 26 Aug 2025 14:24:22 +0200 Subject: [PATCH 0399/1900] Move more early buffered lints to dyn lint diagnostics (1/N) --- compiler/rustc_builtin_macros/messages.ftl | 11 ++++ compiler/rustc_builtin_macros/src/asm.rs | 5 +- compiler/rustc_builtin_macros/src/errors.rs | 22 ++++++- .../rustc_builtin_macros/src/source_util.rs | 3 +- .../rustc_builtin_macros/src/test_harness.rs | 3 +- compiler/rustc_builtin_macros/src/util.rs | 3 +- compiler/rustc_expand/messages.ftl | 14 ++++- compiler/rustc_expand/src/config.rs | 3 +- compiler/rustc_expand/src/errors.rs | 42 ++++++++++++- compiler/rustc_expand/src/mbe/macro_check.rs | 45 +++++++++----- compiler/rustc_expand/src/mbe/transcribe.rs | 6 +- compiler/rustc_lint/messages.ftl | 31 ---------- compiler/rustc_lint/src/early/diagnostics.rs | 41 ------------ compiler/rustc_lint/src/lints.rs | 62 +------------------ compiler/rustc_lint_defs/src/lib.rs | 18 +----- compiler/rustc_resolve/messages.ftl | 6 ++ compiler/rustc_resolve/src/diagnostics.rs | 2 +- compiler/rustc_resolve/src/errors.rs | 18 +++++- compiler/rustc_resolve/src/ident.rs | 3 +- .../issue-61053-duplicate-binder.stderr | 4 +- 20 files changed, 150 insertions(+), 192 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 358c0d3db460f..7c1a5f44e165d 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -64,6 +64,11 @@ builtin_macros_autodiff_ty_activity = {$act} can not be used for this type builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}` builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width} + +builtin_macros_avoid_att_syntax = avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + +builtin_macros_avoid_intel_syntax = avoid using `.intel_syntax`, Intel syntax is the default + builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s .label = not applicable here .label2 = not a `struct`, `enum` or `union` @@ -138,6 +143,8 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value +builtin_macros_duplicate_macro_attribute = duplicated attribute + 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_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time @@ -231,6 +238,8 @@ builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struc builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field +builtin_macros_incomplete_include = include macro expected single expression in source + builtin_macros_multiple_default_attrs = multiple `#[default]` attributes .note = only one `#[default]` attribute is needed .label = `#[default]` used here @@ -294,3 +303,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` + +builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 86b8e1ff8dbb4..ae62b5ea2a097 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,4 +1,3 @@ -use lint::BuiltinLintDiag; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -352,7 +351,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingIntelSyntax, + errors::AvoidIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -360,7 +359,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingAttSyntax, + errors::AvoidAttSyntax, ); } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 54e8f7503377f..0993fdc5be453 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -3,9 +3,29 @@ use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_intel_syntax)] +pub(crate) struct AvoidIntelSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_att_syntax)] +pub(crate) struct AvoidAttSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_incomplete_include)] +pub(crate) struct IncompleteInclude; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_unnameable_test_items)] +pub(crate) struct UnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_duplicate_macro_attribute)] +pub(crate) struct DuplicateMacroAttribute; + #[derive(Diagnostic)] #[diag(builtin_macros_requires_cfg_pattern)] pub(crate) struct RequiresCfgPattern { diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 11b868f81a976..16adaab15c525 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -12,7 +12,6 @@ use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, }; use rustc_expand::module::DirOwnership; -use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::lexer::StripTokens; use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; @@ -159,7 +158,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, p.token.span, self.node_id, - BuiltinLintDiag::IncompleteInclude, + errors::IncompleteInclude, ); } Some(expr) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index a9d91f77560a7..51089e5a1d3f1 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -11,7 +11,6 @@ use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -165,7 +164,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - BuiltinLintDiag::UnnameableTestItems, + errors::UnnameableTestItems, ); } } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 3a4585d5be9d1..e26f31dce67bd 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -5,7 +5,6 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; @@ -49,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - BuiltinLintDiag::DuplicateMacroAttribute, + errors::DuplicateMacroAttribute, ); } } diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 61ba716d082b0..fc50ac6e3bfeb 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -3,6 +3,8 @@ expand_attributes_on_expressions_experimental = .help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//` .help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !` +expand_cfg_attr_no_attributes = `#[cfg_attr]` does not expand to any attributes + expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] @@ -89,6 +91,13 @@ expand_malformed_feature_attribute = malformed `feature` attribute input .expected = expected just one word +expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth + .label = expected repetition + +expand_metavariable_wrong_operator = meta-variable repeats with different Kleene operator + .binder_label = expected repetition + .occurrence_label = conflicting repetition + expand_meta_var_dif_seq_matchers = {$msg} expand_missing_fragment_specifier = missing fragment specifier @@ -176,11 +185,10 @@ expand_resolve_relative_path = expand_trace_macro = trace_macro +expand_unknown_macro_variable = unknown macro variable `{$name}` + expand_unsupported_key_value = key-value macro attributes are not supported -expand_var_still_repeating = - variable `{$ident}` is still repeating at this depth - expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 15419ab7423f4..2925e337071ca 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -21,7 +21,6 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -315,7 +314,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, cfg_attr.span, ast::CRATE_NODE_ID, - BuiltinLintDiag::CfgAttrNoAttributes, + crate::errors::CfgAttrNoAttributes, ); } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 6eb5cd65846eb..a939dadaf2fcd 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -3,9 +3,13 @@ use std::borrow::Cow; use rustc_ast::ast; use rustc_errors::codes::*; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(expand_cfg_attr_no_attributes)] +pub(crate) struct CfgAttrNoAttributes; + #[derive(Diagnostic)] #[diag(expand_expr_repeat_no_syntax_vars)] pub(crate) struct NoSyntaxVarsExprRepeat { @@ -28,13 +32,30 @@ pub(crate) struct CountRepetitionMisplaced { } #[derive(Diagnostic)] -#[diag(expand_var_still_repeating)] -pub(crate) struct VarStillRepeating { +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MacroVarStillRepeating { #[primary_span] pub span: Span, pub ident: MacroRulesNormalizedIdent, } +#[derive(LintDiagnostic)] +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MetaVarStillRepeatingLint { + #[label] + pub label: Span, + pub ident: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(expand_metavariable_wrong_operator)] +pub(crate) struct MetaVariableWrongOperator { + #[label(expand_binder_label)] + pub binder: Span, + #[label(expand_occurrence_label)] + pub occurrence: Span, +} + #[derive(Diagnostic)] #[diag(expand_meta_var_dif_seq_matchers)] pub(crate) struct MetaVarsDifSeqMatchers { @@ -43,6 +64,12 @@ pub(crate) struct MetaVarsDifSeqMatchers { pub msg: String, } +#[derive(LintDiagnostic)] +#[diag(expand_unknown_macro_variable)] +pub(crate) struct UnknownMacroVariable { + pub name: MacroRulesNormalizedIdent, +} + #[derive(Diagnostic)] #[diag(expand_resolve_relative_path)] pub(crate) struct ResolveRelativePath { @@ -345,6 +372,15 @@ pub(crate) struct DuplicateMatcherBinding { pub prev: Span, } +#[derive(LintDiagnostic)] +#[diag(expand_duplicate_matcher_binding)] +pub(crate) struct DuplicateMatcherBindingLint { + #[label] + pub span: Span, + #[label(expand_label2)] + pub prev: Span, +} + #[derive(Diagnostic)] #[diag(expand_missing_fragment_specifier)] #[note] diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index faeae1f494e65..ebd6e887f7d28 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -108,8 +108,7 @@ use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::MultiSpan; -use rustc_lint_defs::BuiltinLintDiag; +use rustc_errors::DecorateDiagCompat; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; @@ -245,9 +244,12 @@ fn check_binders( // There are 3 possibilities: if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. - let mut span = MultiSpan::from_span(span); - span.push_span_label(prev_info.span, "previous declaration"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding); + buffer_lint( + psess, + span, + node_id, + errors::DuplicateMatcherBindingLint { span, prev: prev_info.span }, + ); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. binders.insert(name, BinderInfo { span, ops: ops.into() }); @@ -579,7 +581,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name)); + buffer_lint(psess, span, node_id, errors::UnknownMacroVariable { name }); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -604,29 +606,42 @@ fn ops_is_prefix( psess: &ParseSess, node_id: NodeId, span: Span, - name: MacroRulesNormalizedIdent, + ident: MacroRulesNormalizedIdent, binder_ops: &[KleeneToken], occurrence_ops: &[KleeneToken], ) { for (i, binder) in binder_ops.iter().enumerate() { if i >= occurrence_ops.len() { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name)); + buffer_lint( + psess, + span, + node_id, + errors::MetaVarStillRepeatingLint { label: binder.span, ident }, + ); return; } let occurrence = &occurrence_ops[i]; if occurrence.op != binder.op { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - span.push_span_label(occurrence.span, "conflicting repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator); + buffer_lint( + psess, + span, + node_id, + errors::MetaVariableWrongOperator { + binder: binder.span, + occurrence: occurrence.span, + }, + ); return; } } } -fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) { +fn buffer_lint( + psess: &ParseSess, + span: Span, + node_id: NodeId, + diag: impl Into, +) { // Macros loaded from other crates have dummy node ids. if node_id != DUMMY_NODE_ID { psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index ed8aa71d59dda..6a3f1f62c91e1 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -17,8 +17,8 @@ use rustc_span::{ use smallvec::{SmallVec, smallvec}; use crate::errors::{ - CountRepetitionMisplaced, MetaVarsDifSeqMatchers, MustRepeatOnce, MveUnrecognizedVar, - NoSyntaxVarsExprRepeat, VarStillRepeating, + CountRepetitionMisplaced, MacroVarStillRepeating, MetaVarsDifSeqMatchers, MustRepeatOnce, + MveUnrecognizedVar, NoSyntaxVarsExprRepeat, }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; @@ -483,7 +483,7 @@ fn transcribe_metavar<'tx>( } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. - return Err(dcx.create_err(VarStillRepeating { span: sp, ident })); + return Err(dcx.create_err(MacroVarStillRepeating { span: sp, ident })); } }; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 417e5a97069c3..5a60a4f48d9db 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -40,12 +40,6 @@ lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` order lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` -lint_avoid_att_syntax = - avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - -lint_avoid_intel_syntax = - avoid using `.intel_syntax`, Intel syntax is the default - lint_bad_attribute_argument = bad attribute argument lint_bad_opt_access = {$msg} @@ -190,9 +184,6 @@ lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait .help = consider implementing the trait by hand, or remove the `packed` attribute -lint_cfg_attr_no_attributes = - `#[cfg_attr]` does not expand to any attributes - lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_closure_returning_async_block = closure returning async block can be made into an async closure @@ -249,11 +240,6 @@ lint_dropping_copy_types = calls to `std::mem::drop` with a value that implement lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` -lint_duplicate_macro_attribute = - duplicated attribute - -lint_duplicate_matcher_binding = duplicate matcher binding - lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum @@ -409,9 +395,6 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive -lint_incomplete_include = - include macro expected single expression in source - lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -476,9 +459,6 @@ lint_legacy_derive_helpers = derive helper attribute is used before it is introd lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead -lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths - .note = the macro is defined here - lint_macro_expr_fragment_specifier_2024_migration = the `expr` fragment specifier will accept more expressions in the 2024 edition .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier @@ -499,10 +479,6 @@ lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .map_label = after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items .suggestion = you might have meant to use `Iterator::for_each` -lint_metavariable_still_repeating = variable `{$name}` is still repeating at this depth - -lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator - lint_mismatched_lifetime_syntaxes_eliding_while_named = eliding a lifetime that's named elsewhere is confusing @@ -719,9 +695,6 @@ lint_pattern_in_foreign = patterns aren't allowed in foreign function declaratio lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cannot be re-exported .suggestion = consider making the `extern crate` item publicly accessible -lint_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope - .label = names from parent modules are not accessible without an explicit import - lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale @@ -935,13 +908,9 @@ lint_unknown_lint = *[false] did you mean: `{$replace}` } -lint_unknown_macro_variable = unknown macro variable `{$name}` - lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root -lint_unnameable_test_items = cannot test inner items - lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7300490b838b0..4463586515ec1 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -64,17 +64,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { - span: macro_span, - ns_descr, - ident, - } => lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns_descr, ident } - .decorate_lint(diag), - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } - .decorate_lint(diag) - } - BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { lints::ElidedLifetimesInPaths { subdiag: elided_lifetime_in_path_suggestion( @@ -398,36 +387,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnstableFeature(msg) => { lints::UnstableFeature { msg }.decorate_lint(diag); } - BuiltinLintDiag::AvoidUsingIntelSyntax => { - lints::AvoidIntelSyntax.decorate_lint(diag); - } - BuiltinLintDiag::AvoidUsingAttSyntax => { - lints::AvoidAttSyntax.decorate_lint(diag); - } - BuiltinLintDiag::IncompleteInclude => { - lints::IncompleteInclude.decorate_lint(diag); - } - BuiltinLintDiag::UnnameableTestItems => { - lints::UnnameableTestItems.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMacroAttribute => { - lints::DuplicateMacroAttribute.decorate_lint(diag); - } - BuiltinLintDiag::CfgAttrNoAttributes => { - lints::CfgAttrNoAttributes.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableStillRepeating(name) => { - lints::MetaVariableStillRepeating { name }.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableWrongOperator => { - lints::MetaVariableWrongOperator.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMatcherBinding => { - lints::DuplicateMatcherBinding.decorate_lint(diag); - } - BuiltinLintDiag::UnknownMacroVariable(name) => { - lints::UnknownMacroVariable { name }.decorate_lint(diag); - } BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 56d65ed08f9e3..1c13fadeef968 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::lint::AmbiguityErrorDiag; use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; @@ -2585,50 +2585,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { } } -#[derive(LintDiagnostic)] -#[diag(lint_avoid_intel_syntax)] -pub(crate) struct AvoidIntelSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_avoid_att_syntax)] -pub(crate) struct AvoidAttSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_incomplete_include)] -pub(crate) struct IncompleteInclude; - -#[derive(LintDiagnostic)] -#[diag(lint_unnameable_test_items)] -pub(crate) struct UnnameableTestItems; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_macro_attribute)] -pub(crate) struct DuplicateMacroAttribute; - -#[derive(LintDiagnostic)] -#[diag(lint_cfg_attr_no_attributes)] -pub(crate) struct CfgAttrNoAttributes; - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_still_repeating)] -pub(crate) struct MetaVariableStillRepeating { - pub name: MacroRulesNormalizedIdent, -} - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_wrong_operator)] -pub(crate) struct MetaVariableWrongOperator; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_matcher_binding)] -pub(crate) struct DuplicateMatcherBinding; - -#[derive(LintDiagnostic)] -#[diag(lint_unknown_macro_variable)] -pub(crate) struct UnknownMacroVariable { - pub name: MacroRulesNormalizedIdent, -} - #[derive(LintDiagnostic)] #[diag(lint_unused_crate_dependency)] #[help] @@ -2714,22 +2670,6 @@ pub(crate) struct AbsPathWithModuleSugg { pub replacement: String, } -#[derive(LintDiagnostic)] -#[diag(lint_proc_macro_derive_resolution_fallback)] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Ident, -} - -#[derive(LintDiagnostic)] -#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note] - pub definition: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_hidden_lifetime_parameters)] pub(crate) struct ElidedLifetimesInPaths { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2e84233e5a569..77c17c6c1e39e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -11,7 +11,7 @@ use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -620,12 +620,6 @@ pub enum DeprecatedSinceKind { #[derive(Debug)] pub enum BuiltinLintDiag { AbsPathWithModule(Span), - ProcMacroDeriveResolutionFallback { - span: Span, - ns_descr: &'static str, - ident: Ident, - }, - MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), UnknownCrateTypes { span: Span, @@ -774,16 +768,6 @@ pub enum BuiltinLintDiag { UnusedMacroDefinition(Symbol), MacroRuleNeverUsed(usize, Symbol), UnstableFeature(DiagMessage), - AvoidUsingIntelSyntax, - AvoidUsingAttSyntax, - IncompleteInclude, - UnnameableTestItems, - DuplicateMacroAttribute, - CfgAttrNoAttributes, - MetaVariableStillRepeating(MacroRulesNormalizedIdent), - MetaVariableWrongOperator, - DuplicateMatcherBinding, - UnknownMacroVariable(MacroRulesNormalizedIdent), UnusedCrateDependency { extern_crate: Symbol, local_crate: Symbol, diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 0e566e20a124d..1c0e5ad65b3ab 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -260,6 +260,9 @@ resolve_macro_defined_later = resolve_macro_expanded_extern_crate_cannot_shadow_extern_arguments = macro-expanded `extern crate` items cannot shadow names passed with `--extern` +resolve_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths + .note = the macro is defined here + resolve_macro_expected_found = expected {$expected}, found {$found} `{$macro_path}` .label = not {$article} {$expected} @@ -339,6 +342,9 @@ resolve_param_in_ty_of_const_param = resolve_pattern_doesnt_bind_name = pattern doesn't bind `{$name}` +resolve_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope + .label = names from parent modules are not accessible without an explicit import + resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it .help = you can define integration tests in a directory named `tests` diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 689e713ef46dd..236ab1f09d35f 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -138,7 +138,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, CRATE_NODE_ID, span_use, - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), + errors::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }, ); } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 72be94e58db93..fb77b9ca1d31f 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -3,7 +3,7 @@ use rustc_errors::{ Applicability, Diag, ElidedLifetimeInPathSubdiag, EmissionGuarantee, IntoDiagArg, MultiSpan, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; use crate::late::PatternSource; @@ -566,6 +566,22 @@ pub(crate) struct ProcMacroSameCrate { pub(crate) is_test: bool, } +#[derive(LintDiagnostic)] +#[diag(resolve_proc_macro_derive_resolution_fallback)] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_expanded_macro_exports_accessed_by_absolute_paths)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note] + pub definition: Span, +} + #[derive(Diagnostic)] #[diag(resolve_imported_crate)] pub(crate) struct CrateImported { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index bc06a22757113..35051675fd8dc 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -4,7 +4,6 @@ use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::bug; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; @@ -520,7 +519,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident.span, - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + errors::ProcMacroDeriveResolutionFallback { span: orig_ident.span, ns_descr: ns.descr(), ident, diff --git a/tests/ui/macros/issue-61053-duplicate-binder.stderr b/tests/ui/macros/issue-61053-duplicate-binder.stderr index 7c7cb26b4071e..1ecbc3f86d0de 100644 --- a/tests/ui/macros/issue-61053-duplicate-binder.stderr +++ b/tests/ui/macros/issue-61053-duplicate-binder.stderr @@ -2,9 +2,9 @@ error: duplicate matcher binding --> $DIR/issue-61053-duplicate-binder.rs:7:20 | LL | ($x:tt $x:tt) => { $x }; - | -- ^^ + | -- ^^ duplicate binding | | - | previous declaration + | previous binding | note: the lint level is defined here --> $DIR/issue-61053-duplicate-binder.rs:1:9 From 31c0d96cb604ead17afeb09062d10cc019de9560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 14:11:32 +0200 Subject: [PATCH 0400/1900] Move more early buffered lints to dyn lint diagnostics (2/N) --- compiler/rustc_ast_passes/messages.ftl | 4 +++ .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_passes/src/errors.rs | 7 +++++ compiler/rustc_expand/messages.ftl | 4 +++ compiler/rustc_expand/src/errors.rs | 8 +++++ compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_lint/messages.ftl | 14 --------- compiler/rustc_lint/src/early/diagnostics.rs | 13 -------- compiler/rustc_lint/src/lints.rs | 31 ------------------- compiler/rustc_lint_defs/src/lib.rs | 9 ------ compiler/rustc_resolve/messages.ftl | 7 +++++ compiler/rustc_resolve/src/check_unused.rs | 2 +- compiler/rustc_resolve/src/errors.rs | 16 ++++++++++ compiler/rustc_resolve/src/macros.rs | 2 +- 14 files changed, 50 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8dcf3e3aa3882..e5405a7ad910c 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -216,6 +216,10 @@ ast_passes_match_arm_with_no_body = .suggestion = add a body after the pattern ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe + .suggestion = needs `unsafe` before the extern keyword + +ast_passes_missing_unsafe_on_extern_lint = extern blocks should be unsafe + .suggestion = needs `unsafe` before the extern keyword ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6ef89b553db4..dc221c2fb1abc 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1131,7 +1131,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { MISSING_UNSAFE_ON_EXTERN, item.id, item.span, - BuiltinLintDiag::MissingUnsafeOnExtern { + errors::MissingUnsafeOnExternLint { suggestion: item.span.shrink_to_lo(), }, ); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae805042c549f..e09ca5b81c80c 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -523,6 +523,13 @@ pub(crate) struct MissingUnsafeOnExtern { pub span: Span, } +#[derive(LintDiagnostic)] +#[diag(ast_passes_missing_unsafe_on_extern_lint)] +pub(crate) struct MissingUnsafeOnExternLint { + #[suggestion(code = "unsafe ", applicability = "machine-applicable")] + pub suggestion: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_fieldless_union)] pub(crate) struct FieldlessUnion { diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index fc50ac6e3bfeb..f82ef96e9c75f 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -80,6 +80,10 @@ expand_macro_body_stability = .label = invalid body stability attribute .label2 = body stability attribute affects this macro +expand_macro_call_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for macro invocations + .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion + expand_macro_const_stability = macros cannot have const stability attributes .label = invalid const stability attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index a939dadaf2fcd..06c029482dfe6 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -537,3 +537,11 @@ pub(crate) struct MacroArgsBadDelimSugg { #[suggestion_part(code = ")")] pub close: Span, } + +#[derive(LintDiagnostic)] +#[diag(expand_macro_call_unused_doc_comment)] +#[help] +pub(crate) struct MacroCallUnusedDocComment { + #[label] + pub span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 38e057d2776e4..cda38cd7ae61b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2183,7 +2183,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_DOC_COMMENTS, current_span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedDocComment(attr.span), + crate::errors::MacroCallUnusedDocComment { span: attr.span }, ); } else if rustc_attr_parsing::is_builtin_attr(attr) && !AttributeParser::::is_parsed_attribute(&attr.path()) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5a60a4f48d9db..68bef1772c0e3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -453,9 +453,6 @@ lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a point .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` -lint_legacy_derive_helpers = derive helper attribute is used before it is introduced - .label = the attribute is introduced here - lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -524,9 +521,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_mixed = lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths = use `'_` for type paths -lint_missing_unsafe_on_extern = extern blocks should be unsafe - .suggestion = needs `unsafe` before the extern keyword - lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} @@ -962,14 +956,6 @@ lint_unused_def = unused {$pre}`{$def}`{$post} that must be used lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} -lint_unused_doc_comment = unused doc comment - .label = rustdoc does not generate documentation for macro invocations - .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion - -lint_unused_extern_crate = unused extern crate - .label = unused - .suggestion = remove the unused `extern crate` - lint_unused_import_braces = braces around {$node} is unnecessary lint_unused_imports = {$num_snippets -> diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 4463586515ec1..eb5c89660e1ff 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -135,9 +135,6 @@ pub fn decorate_builtin_lint( stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } .decorate_lint(diag); } - BuiltinLintDiag::UnusedDocComment(attr_span) => { - lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); - } BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; if is_foreign { @@ -147,9 +144,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { - lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); - } BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); } @@ -210,9 +204,6 @@ pub fn decorate_builtin_lint( }; lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); } - BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { - lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); - } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), @@ -242,7 +233,6 @@ pub fn decorate_builtin_lint( .decorate_lint(diag); } BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { - debug!(?deletion_span); lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); } BuiltinLintDiag::NamedArgumentUsedPositionally { @@ -283,9 +273,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); } - BuiltinLintDiag::UnusedExternCrate { span, removal_span } => { - lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag); - } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); let code = if vis_span.is_empty() { "use " } else { " use " }; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1c13fadeef968..c084e8605f782 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2749,14 +2749,6 @@ pub(crate) enum RedundantImportSub { DefinedPrelude(#[primary_span] Span), } -#[derive(LintDiagnostic)] -#[diag(lint_unused_doc_comment)] -#[help] -pub(crate) struct UnusedDocComment { - #[label] - pub span: Span, -} - #[derive(LintDiagnostic)] pub(crate) enum PatternsInFnsWithoutBody { #[diag(lint_pattern_in_foreign)] @@ -2780,13 +2772,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] -#[diag(lint_legacy_derive_helpers)] -pub(crate) struct LegacyDeriveHelpers { - #[label] - pub span: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_or_patterns_back_compat)] pub(crate) struct OrPatternsBackCompat { @@ -2878,13 +2863,6 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { }, } -#[derive(LintDiagnostic)] -#[diag(lint_missing_unsafe_on_extern)] -pub(crate) struct MissingUnsafeOnExtern { - #[suggestion(code = "unsafe ", applicability = "machine-applicable")] - pub suggestion: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_single_use_lifetime)] pub(crate) struct SingleUseLifetime { @@ -2940,15 +2918,6 @@ pub(crate) struct ByteSliceInPackedStructWithDerive { pub ty: String, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_extern_crate)] -pub(crate) struct UnusedExternCrate { - #[label] - pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] - pub removal_span: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_extern_crate_not_idiomatic)] pub(crate) struct ExternCrateNotIdiomatic { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 77c17c6c1e39e..50c3bcf0434bc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -640,7 +640,6 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - UnusedDocComment(Span), UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, @@ -652,7 +651,6 @@ pub enum BuiltinLintDiag { ident: Ident, is_foreign: bool, }, - LegacyDeriveHelpers(Span), OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), /// `'r#` in edition < 2021. @@ -668,9 +666,6 @@ pub enum BuiltinLintDiag { UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), - MissingUnsafeOnExtern { - suggestion: Span, - }, SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, @@ -699,10 +694,6 @@ pub enum BuiltinLintDiag { // FIXME: enum of byte/string ty: String, }, - UnusedExternCrate { - span: Span, - removal_span: Span, - }, ExternCrateNotIdiomatic { vis_span: Span, ident_span: Span, diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 1c0e5ad65b3ab..32833f5e8d9b3 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -231,6 +231,9 @@ resolve_item_was_cfg_out = the item is gated here resolve_label_with_similar_name_reachable = a label with a similar name is reachable +resolve_legacy_derive_helpers = derive helper attribute is used before it is introduced + .label = the attribute is introduced here + resolve_lending_iterator_report_error = associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type .note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type @@ -471,6 +474,10 @@ resolve_unreachable_label_suggestion_use_similarly_named = resolve_unreachable_label_with_similar_name_exists = a label with a similar name exists but is unreachable +resolve_unused_extern_crate = unused extern crate + .label = unused + .suggestion = remove the unused `extern crate` + resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 50a1ad23a5462..a80cd7540afaf 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -169,7 +169,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { UNUSED_EXTERN_CRATES, extern_crate.id, span, - BuiltinLintDiag::UnusedExternCrate { + crate::errors::UnusedExternCrate { span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index fb77b9ca1d31f..6b36906c86532 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1292,3 +1292,19 @@ pub(crate) struct TraitImplMismatch { #[label(resolve_trait_impl_mismatch_label_item)] pub(crate) trait_item_span: Span, } + +#[derive(LintDiagnostic)] +#[diag(resolve_legacy_derive_helpers)] +pub(crate) struct LegacyDeriveHelpers { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_extern_crate)] +pub(crate) struct UnusedExternCrate { + #[label] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + pub removal_span: Span, +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5fa87b4cd9b68..d3e98ef839b0b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -979,7 +979,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LEGACY_DERIVE_HELPERS, node_id, ident.span, - BuiltinLintDiag::LegacyDeriveHelpers(binding.span), + errors::LegacyDeriveHelpers { span: binding.span }, ); } } From 379b181fe698108d61ac05bad328e14567739304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 14:11:41 +0200 Subject: [PATCH 0401/1900] Remove a dead early lint Dropped in favor a hard error in RUST-127907. --- compiler/rustc_lint/messages.ftl | 3 --- compiler/rustc_lint/src/early/diagnostics.rs | 3 --- compiler/rustc_lint/src/lints.rs | 8 -------- compiler/rustc_lint_defs/src/lib.rs | 4 ---- 4 files changed, 18 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 68bef1772c0e3..e348ae2c57458 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -181,9 +181,6 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` -lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait - .help = consider implementing the trait by hand, or remove the `packed` attribute - lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_closure_returning_async_block = closure returning async block can be made into an async closure diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index eb5c89660e1ff..4039888993860 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -270,9 +270,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { - lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); - } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); let code = if vis_span.is_empty() { "use " } else { " use " }; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c084e8605f782..64875ee4e8a1f 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2910,14 +2910,6 @@ pub(crate) struct NamedArgumentUsedPositionally { pub named_arg_name: String, } -#[derive(LintDiagnostic)] -#[diag(lint_byte_slice_in_packed_struct_with_derive)] -#[help] -pub(crate) struct ByteSliceInPackedStructWithDerive { - // FIXME: make this translatable - pub ty: String, -} - #[derive(LintDiagnostic)] #[diag(lint_extern_crate_not_idiomatic)] pub(crate) struct ExternCrateNotIdiomatic { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 50c3bcf0434bc..0b4b5015aca5e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -690,10 +690,6 @@ pub enum BuiltinLintDiag { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, - ByteSliceInPackedStructWithDerive { - // FIXME: enum of byte/string - ty: String, - }, ExternCrateNotIdiomatic { vis_span: Span, ident_span: Span, From 27a180a31a10bad8d5116146459a49a16b4f3866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 15:07:14 +0200 Subject: [PATCH 0402/1900] Move more early buffered lints to dyn lint diagnostics (3/N) --- compiler/rustc_lint/messages.ftl | 11 ---------- compiler/rustc_lint/src/early/diagnostics.rs | 8 -------- compiler/rustc_lint/src/lints.rs | 21 -------------------- compiler/rustc_lint_defs/src/lib.rs | 8 -------- compiler/rustc_resolve/messages.ftl | 11 ++++++++++ compiler/rustc_resolve/src/check_unused.rs | 4 ++-- compiler/rustc_resolve/src/errors.rs | 21 ++++++++++++++++++++ compiler/rustc_resolve/src/imports.rs | 6 +++--- compiler/rustc_resolve/src/late.rs | 4 ++-- 9 files changed, 39 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index e348ae2c57458..c11e59ca05882 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -460,10 +460,6 @@ lint_macro_is_private = macro `{$ident}` is private lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used -lint_macro_use_deprecated = - applying the `#[macro_use]` attribute to an `extern crate` item is deprecated - .help = remove it and import macros at use sites with a `use` item instead - lint_malformed_attribute = malformed lint attribute input lint_map_unit_fn = `Iterator::map` call that discard the iterator's values @@ -726,9 +722,6 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> *[false] this semicolon } -lint_reexport_private_dependency = - {$kind} `{$name}` from private dependency '{$krate}' is re-exported - lint_remove_mut_from_pattern = remove `mut` from the parameter lint_removed_lint = lint `{$name}` has been removed: {$reason} @@ -966,15 +959,11 @@ lint_unused_imports = {$num_snippets -> } .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module -lint_unused_label = unused label - lint_unused_lifetime = lifetime parameter `{$ident}` never used .suggestion = elide the unused lifetime lint_unused_macro_definition = unused macro definition: `{$name}` -lint_unused_macro_use = unused `#[macro_use]` import - lint_unused_op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 4039888993860..e8daf3e46b870 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -308,9 +308,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => { - lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag); - } BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } @@ -350,15 +347,10 @@ pub fn decorate_builtin_lint( }); lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); } - BuiltinLintDiag::MacroUseDeprecated => { - lints::MacroUseDeprecated.decorate_lint(diag); - } - BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } .decorate_lint(diag); } - BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), BuiltinLintDiag::MacroIsPrivate(ident) => { lints::MacroIsPrivate { ident }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 64875ee4e8a1f..7c375a98f9e9d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2535,15 +2535,6 @@ pub(crate) mod unexpected_cfg_value { } } -#[derive(LintDiagnostic)] -#[diag(lint_macro_use_deprecated)] -#[help] -pub(crate) struct MacroUseDeprecated; - -#[derive(LintDiagnostic)] -#[diag(lint_unused_macro_use)] -pub(crate) struct UnusedMacroUse; - #[derive(LintDiagnostic)] #[diag(lint_private_extern_crate_reexport, code = E0365)] pub(crate) struct PrivateExternCrateReexport { @@ -2552,10 +2543,6 @@ pub(crate) struct PrivateExternCrateReexport { pub sugg: Span, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_label)] -pub(crate) struct UnusedLabel; - #[derive(LintDiagnostic)] #[diag(lint_macro_is_private)] pub(crate) struct MacroIsPrivate { @@ -2957,14 +2944,6 @@ pub(crate) struct HiddenGlobReexports { pub namespace: String, } -#[derive(LintDiagnostic)] -#[diag(lint_reexport_private_dependency)] -pub(crate) struct ReexportPrivateDependency { - pub name: String, - pub kind: String, - pub krate: Symbol, -} - #[derive(LintDiagnostic)] #[diag(lint_unnecessary_qualification)] pub(crate) struct UnusedQualifications { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0b4b5015aca5e..239b03edfaf00 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -717,11 +717,6 @@ pub enum BuiltinLintDiag { /// The local binding that shadows the glob reexport. private_item_span: Span, }, - ReexportPrivateDependency { - name: String, - kind: String, - krate: Symbol, - }, UnusedQualifications { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, @@ -744,13 +739,10 @@ pub enum BuiltinLintDiag { span: Span, typo_name: Option, }, - MacroUseDeprecated, - UnusedMacroUse, PrivateExternCrateReexport { source: Ident, extern_crate_span: Span, }, - UnusedLabel, MacroIsPrivate(Ident), UnusedMacroDefinition(Symbol), MacroRuleNeverUsed(usize, Symbol), diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 32833f5e8d9b3..cc8fbc18b7377 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -274,6 +274,10 @@ resolve_macro_extern_deprecated = `#[macro_escape]` is a deprecated synonym for `#[macro_use]` .help = try an outer attribute: `#[macro_use]` +resolve_macro_use_deprecated = + applying the `#[macro_use]` attribute to an `extern crate` item is deprecated + .help = remove it and import macros at use sites with a `use` item instead + resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self` resolve_macro_use_name_already_in_use = @@ -357,6 +361,9 @@ resolve_reexport_of_crate_public = resolve_reexport_of_private = re-export of private `{$ident}` +resolve_reexport_private_dependency = + {$kind} `{$name}` from private dependency '{$krate}' is re-exported + resolve_relative_2018 = relative paths are not supported in visibilities in 2018 edition or later .suggestion = try @@ -478,6 +485,10 @@ resolve_unused_extern_crate = unused extern crate .label = unused .suggestion = remove the unused `extern crate` +resolve_unused_label = unused label + +resolve_unused_macro_use = unused `#[macro_use]` import + resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index a80cd7540afaf..95f979a3fed33 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -406,7 +406,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - BuiltinLintDiag::MacroUseDeprecated, + crate::errors::MacroUseDeprecated, ); } } @@ -427,7 +427,7 @@ impl Resolver<'_, '_> { UNUSED_IMPORTS, import.root_id, import.span, - BuiltinLintDiag::UnusedMacroUse, + crate::errors::UnusedMacroUse, ); } _ => {} diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 6b36906c86532..f0ea97ba8a0c0 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1308,3 +1308,24 @@ pub(crate) struct UnusedExternCrate { #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub removal_span: Span, } + +#[derive(LintDiagnostic)] +#[diag(resolve_reexport_private_dependency)] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_label)] +pub(crate) struct UnusedLabel; + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_macro_use)] +pub(crate) struct UnusedMacroUse; + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_use_deprecated)] +#[help] +pub(crate) struct MacroUseDeprecated; diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d7fc028287ff5..33c2c7436d1b7 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -720,9 +720,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { EXPORTED_PRIVATE_DEPENDENCIES, binding_id, binding.span, - BuiltinLintDiag::ReexportPrivateDependency { - kind: binding.res().descr().to_string(), - name: key.ident.name.to_string(), + crate::errors::ReexportPrivateDependency { + name: key.ident.name, + kind: binding.res().descr(), krate: self.tcx.crate_name(reexported_def_id.krate), }, ); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 521fef2b9d432..4d4acc60ca804 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -29,7 +29,7 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::{DelegationFnSig, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; -use rustc_session::lint::{self, BuiltinLintDiag}; +use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; @@ -5225,7 +5225,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lint::builtin::UNUSED_LABELS, *id, *span, - BuiltinLintDiag::UnusedLabel, + errors::UnusedLabel, ); } } From ec7ad59789138871418362d9592b1e848bab4053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 14 Sep 2025 06:03:22 +0200 Subject: [PATCH 0403/1900] Move more early buffered lints to dyn lint diagnostics (4/N) --- compiler/rustc_expand/messages.ftl | 11 +++++ compiler/rustc_expand/src/errors.rs | 28 +++++++++++++ compiler/rustc_expand/src/expand.rs | 3 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 5 +-- compiler/rustc_interface/messages.ftl | 3 ++ compiler/rustc_interface/src/errors.rs | 17 +++++++- compiler/rustc_interface/src/util.rs | 8 ++-- compiler/rustc_lint/messages.ftl | 14 ------- compiler/rustc_lint/src/early/diagnostics.rs | 19 --------- compiler/rustc_lint/src/lints.rs | 44 -------------------- compiler/rustc_lint_defs/src/lib.rs | 12 ------ 11 files changed, 66 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index f82ef96e9c75f..47c00bff5c950 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -160,6 +160,9 @@ expand_mve_unrecognized_var = expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable +expand_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + .suggestion = use pat_param to preserve semantics + expand_proc_macro_back_compat = using an old version of `{$crate_name}` .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives @@ -189,8 +192,16 @@ expand_resolve_relative_path = expand_trace_macro = trace_macro +expand_trailing_semi_macro = trailing semicolon in macro used in expression position + .note1 = macro invocations at the end of a block are treated as expressions + .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` + expand_unknown_macro_variable = unknown macro variable `{$name}` +expand_unused_builtin_attribute = unused attribute `{$attr_name}` + .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` + .suggestion = remove the attribute + expand_unsupported_key_value = key-value macro attributes are not supported diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 06c029482dfe6..c37c2d88d9cd2 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -545,3 +545,31 @@ pub(crate) struct MacroCallUnusedDocComment { #[label] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag(expand_or_patterns_back_compat)] +pub(crate) struct OrPatternsBackCompat { + #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] + pub span: Span, + pub suggestion: String, +} + +#[derive(LintDiagnostic)] +#[diag(expand_trailing_semi_macro)] +pub(crate) struct TrailingMacro { + #[note(expand_note1)] + #[note(expand_note2)] + pub is_trailing: bool, + pub name: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(expand_unused_builtin_attribute)] +pub(crate) struct UnusedBuiltinAttribute { + #[note] + pub invoc_span: Span, + pub attr_name: Symbol, + pub macro_name: String, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub attr_span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cda38cd7ae61b..4c0e0bbfe26d1 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -25,7 +25,6 @@ use rustc_parse::parser::{ token_descr, }; use rustc_session::Session; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; use rustc_span::hygiene::SyntaxContext; @@ -2196,7 +2195,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedBuiltinAttribute { + crate::errors::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), invoc_span: call.path.span, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8b43f852b2638..946265eba8bdb 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -17,7 +17,6 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; @@ -90,7 +89,7 @@ impl<'a> ParserAnyMacro<'a> { SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, - BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident), + errors::TrailingMacro { is_trailing: is_trailing_mac, name: macro_ident }, ); } parser.bump(); @@ -1425,7 +1424,7 @@ fn check_matcher_core<'tt>( RUST_2021_INCOMPATIBLE_OR_PATTERNS, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::OrPatternsBackCompat(span, suggestion), + errors::OrPatternsBackCompat { span, suggestion }, ); } match is_in_follow(next_token, kind) { diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index d83b18cbc2df6..6f6666f8c76fa 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -30,6 +30,9 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag interface_input_file_would_be_overwritten = the input file "{$path}" would be overwritten by the generated executable +interface_invalid_crate_type_value = invalid `crate_type` value + .suggestion = did you mean + interface_mixed_bin_crate = cannot mix `bin` crate type with others diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index d1082eaf6173c..727e09c7562b5 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,7 +1,7 @@ use std::io; use std::path::Path; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -108,3 +108,18 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(interface_invalid_crate_type_value)] +pub(crate) struct UnknownCrateTypes { + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(interface_suggestion, code = r#""{snippet}""#, applicability = "maybe-incorrect")] +pub(crate) struct UnknownCrateTypesSub { + #[primary_span] + pub span: Span, + pub snippet: Symbol, +} diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 04006f3e446c1..061c764e619a3 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -13,9 +13,8 @@ use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; -use rustc_session::{EarlyDiagCtxt, Session, filesearch}; +use rustc_session::{EarlyDiagCtxt, Session, filesearch, lint}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; @@ -468,7 +467,10 @@ pub(crate) fn check_attr_crate_type( lint::builtin::UNKNOWN_CRATE_TYPES, ast::CRATE_NODE_ID, span, - BuiltinLintDiag::UnknownCrateTypes { span, candidate }, + errors::UnknownCrateTypes { + sugg: candidate + .map(|cand| errors::UnknownCrateTypesSub { span, snippet: cand }), + }, ); } } else { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c11e59ca05882..5b98c9fafaad4 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -407,9 +407,6 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly .note = see the asm section of Rust By Example for more information lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro -lint_invalid_crate_type_value = invalid `crate_type` value - .suggestion = did you mean - # FIXME: we should ordinalize $valid_up_to when we add support for doing so lint_invalid_from_utf8_checked = calls to `{$method}` with an invalid literal always return an error .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes @@ -637,9 +634,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound -lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro - .suggestion = use pat_param to preserve semantics - lint_out_of_scope_macro_calls = cannot find macro `{$path}` in the current scope when looking from {$location} .label = not found from {$location} .help = import `macro_rules` with `use` to make it callable above its definition @@ -783,10 +777,6 @@ lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal lint_too_large_char_cast = value exceeds maximum `char` value .note = maximum valid `char` value is `0x10FFFF` -lint_trailing_semi_macro = trailing semicolon in macro used in expression position - .note1 = macro invocations at the end of a block are treated as expressions - .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` - lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified @@ -917,10 +907,6 @@ lint_untranslatable_diag = diagnostics should be created using translatable mess lint_unused_allocation = unnecessary allocation, use `&` instead lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead -lint_unused_builtin_attribute = unused attribute `{$attr_name}` - .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` - .suggestion = remove the attribute - lint_unused_closure = unused {$pre}{$count -> [one] closure diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index e8daf3e46b870..9029ee0447110 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -76,10 +76,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { - let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); - lints::UnknownCrateTypes { sugg }.decorate_lint(diag); - } BuiltinLintDiag::UnusedImports { remove_whole_use, num_to_remove, @@ -144,9 +140,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { - lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); - } BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { lints::ReservedPrefix { label: label_span, @@ -166,18 +159,6 @@ pub fn decorate_builtin_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } - BuiltinLintDiag::UnusedBuiltinAttribute { - attr_name, - macro_name, - invoc_span, - attr_span, - } => { - lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span } - .decorate_lint(diag); - } - BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); - } BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { lints::BreakWithLabelAndLoop { sub: lints::BreakWithLabelAndLoopSub { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7c375a98f9e9d..b7312484de5cd 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2664,21 +2664,6 @@ pub(crate) struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } -#[derive(LintDiagnostic)] -#[diag(lint_invalid_crate_type_value)] -pub(crate) struct UnknownCrateTypes { - #[subdiagnostic] - pub sugg: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")] -pub(crate) struct UnknownCrateTypesSub { - #[primary_span] - pub span: Span, - pub candidate: Symbol, -} - #[derive(LintDiagnostic)] #[diag(lint_unused_imports)] pub(crate) struct UnusedImports { @@ -2759,14 +2744,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] -#[diag(lint_or_patterns_back_compat)] -pub(crate) struct OrPatternsBackCompat { - #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] - pub span: Span, - pub suggestion: String, -} - #[derive(LintDiagnostic)] #[diag(lint_reserved_prefix)] pub(crate) struct ReservedPrefix { @@ -2787,27 +2764,6 @@ pub(crate) struct RawPrefix { pub suggestion: Span, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_builtin_attribute)] -pub(crate) struct UnusedBuiltinAttribute { - #[note] - pub invoc_span: Span, - pub attr_name: Symbol, - pub macro_name: String, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub attr_span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(lint_trailing_semi_macro)] -pub(crate) struct TrailingMacro { - #[note(lint_note1)] - #[note(lint_note2)] - pub is_trailing: bool, - - pub name: Ident, -} - #[derive(LintDiagnostic)] #[diag(lint_break_with_label_and_loop)] pub(crate) struct BreakWithLabelAndLoop { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 239b03edfaf00..40a818a3c9dc7 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -621,10 +621,6 @@ pub enum DeprecatedSinceKind { pub enum BuiltinLintDiag { AbsPathWithModule(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - UnknownCrateTypes { - span: Span, - candidate: Option, - }, UnusedImports { remove_whole_use: bool, num_to_remove: usize, @@ -640,18 +636,11 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - UnusedBuiltinAttribute { - attr_name: Symbol, - macro_name: String, - invoc_span: Span, - attr_span: Span, - }, PatternsInFnsWithoutBody { span: Span, ident: Ident, is_foreign: bool, }, - OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), /// `'r#` in edition < 2021. RawPrefix(Span), @@ -660,7 +649,6 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, - TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), From 9f850ceb33b2f55084362dddd2396db8469779bd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 12 Sep 2025 01:59:39 +0900 Subject: [PATCH 0404/1900] fix: Infinite loop while elaborting predicates --- .../crates/hir-ty/src/dyn_compatibility.rs | 3 ++ .../hir-ty/src/dyn_compatibility/tests.rs | 3 +- .../crates/hir-ty/src/lower_nextsolver.rs | 3 ++ .../crates/hir-ty/src/next_solver/interner.rs | 23 +++++++++++++- .../hir-ty/src/tests/regression/new_solver.rs | 31 +++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index d4b3751cf5666..b87c998217741 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -138,6 +138,9 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b let interner = DbInterner::new_with(db, Some(krate), None); let predicates = db.generic_predicates_ns(def); + // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to + // rust-analyzer yet + // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490 elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs index 4ffa455084971..04a9ba79921ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -253,7 +253,8 @@ trait Bar { trait Baz : Bar { } "#, - [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + // FIXME: We should also report `SizedSelf` here + [("Bar", vec![]), ("Baz", vec![SelfReferential])], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index c6a8fa81edff5..5c29befe123cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1358,6 +1358,9 @@ where } } + // FIXME: rustc gathers more predicates by recursing through resulting trait predicates. + // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715 + ( GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), create_diagnostics(ctx.diagnostics), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 0f512fdaf8687..7b6e8a1073d78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -5,7 +5,7 @@ use base_db::Crate; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; use hir_def::lang_item::LangItem; use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; -use hir_def::{AdtId, BlockId, TypeAliasId, VariantId}; +use hir_def::{AdtId, BlockId, GenericDefId, TypeAliasId, VariantId}; use hir_def::{AttrDefId, Lookup}; use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; use intern::sym::non_exhaustive; @@ -1334,6 +1334,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .db() .generic_predicates_ns(def_id.0.into()) .iter() + .filter(|p| match p.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(tr) => match tr.self_ty().kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + _ => false, + }, + _ => true, + }) .cloned() .map(|p| (p, Span::dummy())) .collect(); @@ -1345,10 +1352,24 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { + fn is_self_or_assoc(ty: Ty<'_>) -> bool { + match ty.kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias) => { + is_self_or_assoc(alias.self_ty()) + } + _ => false, + } + } + let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) .iter() + .filter(|p| match p.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(tr) => is_self_or_assoc(tr.self_ty()), + _ => true, + }) .cloned() .map(|p| (p, Span::dummy())) .collect(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 4df788638a315..595f285bd930e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -116,3 +116,34 @@ fn main() { "#]], ); } + +#[test] +fn no_infinite_loop_on_super_predicates_elaboration() { + check_infer( + r#" +//- minicore: sized +trait DimMax { + type Output: Dimension; +} + +trait Dimension: DimMax<:: Smaller, Output = Self> { + type Smaller: Dimension; +} + +fn test(t: T) +where + T: DimMax, + U: Dimension, +{ + let t: >::Output = loop {}; +} +"#, + expect![[r#" + 182..183 't': T + 230..280 '{ ... {}; }': () + 240..241 't': >::Output + 270..277 'loop {}': ! + 275..277 '{}': () + "#]], + ) +} From df04be8cf744b500b5972fdc76898a96067d5517 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 13:18:19 +0000 Subject: [PATCH 0405/1900] Comment. --- compiler/rustc_mir_transform/src/gvn.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index f8b3e5c6f8794..fc0a03b1aab52 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -154,19 +154,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + /// This represents a `Value` in the symbolic execution. #[debug_format = "_v{}"] struct VnIndex {} } +/// Marker type to forbid hashing and comparing opaque values. +/// This struct should only be constructed by `ValueSet::insert_unique` to ensure we use that +/// method to create non-unifiable values. It will ICE if used in `ValueSet::insert`. #[derive(Copy, Clone, Debug, Eq)] struct VnOpaque; impl PartialEq for VnOpaque { fn eq(&self, _: &VnOpaque) -> bool { + // ICE if we try to compare unique values unreachable!() } } impl Hash for VnOpaque { fn hash(&self, _: &mut T) { + // ICE if we try to hash unique values unreachable!() } } @@ -191,6 +197,8 @@ enum Value<'tcx> { // `disambiguator` is `None` iff the constant is deterministic. disambiguator: Option, }, + + // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, Vec), @@ -252,6 +260,7 @@ impl<'tcx> ValueSet<'tcx> { } /// Insert a `(Value, Ty)` pair without hashing or deduplication. + /// This always creates a new `VnIndex`. #[inline] fn insert_unique( &mut self, From 8811344f22e0c015aaa45367b6462e3ecdcb22b5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 13:04:53 +0000 Subject: [PATCH 0406/1900] Elaborate comment. --- compiler/rustc_mir_transform/src/dest_prop.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 7e6f39881b8b8..b9ab0a9feee65 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -590,8 +590,12 @@ fn save_as_intervals<'tcx>( twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); debug_assert_eq!(twostep, two_step_loc(loc, Effect::After)); append_at(&mut values, &state, twostep); - // Ensure we have a non-zero live range even for dead stores. This is done by marking - // all the written-to locals as live in the second half of the statement. + // Like terminators, ensure we have a non-zero live range even for dead stores. + // Some rvalues interleave reads and writes, for instance `Rvalue::Aggregate`, see + // https://github.com/rust-lang/rust/issues/146383. By precaution, treat statements + // as behaving so by default. + // We make an exception for simple assignments `_a.stuff = {copy|move} _b.stuff`, + // as marking `_b` live here would prevent unification. let is_simple_assignment = matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); VisitPlacesWith(|place: Place<'tcx>, ctxt| { From f981739fcfdbe3e69d5fd630c4390a33ffc41035 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 4 Sep 2025 22:55:50 +0200 Subject: [PATCH 0407/1900] refactor `ptr_offset_with_cast` --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/methods/mod.rs | 49 +++++- .../src/methods/ptr_offset_with_cast.rs | 82 ++++++++++ clippy_lints/src/ptr_offset_with_cast.rs | 151 ------------------ clippy_utils/src/msrvs.rs | 2 +- tests/ui/ptr_offset_with_cast.fixed | 22 ++- tests/ui/ptr_offset_with_cast.rs | 22 ++- tests/ui/ptr_offset_with_cast.stderr | 41 ++++- 9 files changed, 209 insertions(+), 164 deletions(-) create mode 100644 clippy_lints/src/methods/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/ptr_offset_with_cast.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b9..8d576c2263f05 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -448,6 +448,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::PATH_ENDS_WITH_EXT_INFO, + crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, @@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, - crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0d7931aae765c..d152082851795 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -302,7 +302,6 @@ mod permissions_set_readonly_false; mod pointers_in_nomem_asm_block; mod precedence; mod ptr; -mod ptr_offset_with_cast; mod pub_underscore_fields; mod pub_use; mod question_mark; @@ -592,7 +591,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(unwrap::Unwrap)); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); - store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49ca81dafc280..d06b0ed64bdc2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -91,6 +91,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod path_ends_with_ext; +mod ptr_offset_with_cast; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -1725,6 +1726,43 @@ declare_clippy_lint! { "Check for offset calculations on raw pointers to zero-sized types" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `offset` pointer method with a `usize` casted to an + /// `isize`. + /// + /// ### Why is this bad? + /// If we’re always increasing the pointer address, we can avoid the numeric + /// cast by using the `add` method instead. + /// + /// ### Example + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.offset(offset as isize); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.add(offset); + /// } + /// ``` + #[clippy::version = "1.30.0"] + pub PTR_OFFSET_WITH_CAST, + complexity, + "unneeded pointer offset cast" +} + declare_clippy_lint! { /// ### What it does /// Checks for `FileType::is_file()`. @@ -4665,6 +4703,7 @@ impl_lint_pass!(Methods => [ UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, ZST_OFFSET, + PTR_OFFSET_WITH_CAST, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, @@ -4960,10 +4999,7 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ( - sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, - [_arg], - ) => { + (sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => { zst_offset::check(cx, expr, recv); }, (sym::all, [arg]) => { @@ -5334,6 +5370,11 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, + (sym::offset | sym::wrapping_offset, [arg]) => { + zst_offset::check(cx, expr, recv); + + ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv); + }, (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, diff --git a/clippy_lints/src/methods/ptr_offset_with_cast.rs b/clippy_lints/src/methods/ptr_offset_with_cast.rs new file mode 100644 index 0000000000000..d19d3b8eb89d9 --- /dev/null +++ b/clippy_lints/src/methods/ptr_offset_with_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Symbol; +use std::fmt; + +use super::PTR_OFFSET_WITH_CAST; + +pub(super) fn check( + cx: &LateContext<'_>, + method: Symbol, + expr: &Expr<'_>, + recv: &Expr<'_>, + arg: &Expr<'_>, + msrv: Msrv, +) { + // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions + // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable. + if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) { + return; + } + + let method = match method { + sym::offset => Method::Offset, + sym::wrapping_offset => Method::WrappingOffset, + _ => return, + }; + + if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() { + return; + } + + // Check if the argument to the method call is a cast from usize. + let cast_lhs_expr = match arg.kind { + ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs, + _ => return, + }; + + let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else { + return; + }; + + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); + span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| { + diag.multipart_suggestion( + format!("use `{}` instead", method.suggestion()), + vec![ + (method_name.ident.span, method.suggestion().to_string()), + (arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()), + ], + Applicability::MachineApplicable, + ); + }); +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + #[must_use] + fn suggestion(self) -> &'static str { + match self { + Self::Offset => "add", + Self::WrappingOffset => "wrapping_add", + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs deleted file mode 100644 index d8d813f9846d5..0000000000000 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::sym; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use std::fmt; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `offset` pointer method with a `usize` casted to an - /// `isize`. - /// - /// ### Why is this bad? - /// If we’re always increasing the pointer address, we can avoid the numeric - /// cast by using the `add` method instead. - /// - /// ### Example - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.offset(offset as isize); - /// } - /// ``` - /// - /// Could be written: - /// - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.add(offset); - /// } - /// ``` - #[clippy::version = "1.30.0"] - pub PTR_OFFSET_WITH_CAST, - complexity, - "unneeded pointer offset cast" -} - -declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); - -impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { - return; - }; - - // Check if the argument to the method call is a cast from usize - let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { - return; - }; - - let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); - if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { - span_lint_and_sugg( - cx, - PTR_OFFSET_WITH_CAST, - expr.span, - msg, - "try", - sugg, - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg); - } - } -} - -// If the given expression is a cast from a usize, return the lhs of the cast -fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind - && is_expr_ty_usize(cx, cast_lhs_expr) - { - return Some(cast_lhs_expr); - } - None -} - -// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the -// receiver, the arg of the method call, and the method. -fn expr_as_ptr_offset_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, -) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind - && is_expr_ty_raw_ptr(cx, arg_0) - { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name == sym::wrapping_offset { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } - } - None -} - -// Is the type of the expression a usize? -fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize -} - -// Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_raw_ptr() -} - -fn build_suggestion( - cx: &LateContext<'_>, - method: Method, - receiver_expr: &Expr<'_>, - cast_lhs_expr: &Expr<'_>, -) -> Option { - let receiver = receiver_expr.span.get_source_text(cx)?; - let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?; - Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) -} - -#[derive(Copy, Clone)] -enum Method { - Offset, - WrappingOffset, -} - -impl Method { - #[must_use] - fn suggestion(self) -> &'static str { - match self { - Self::Offset => "add", - Self::WrappingOffset => "wrapping_add", - } - } -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Offset => write!(f, "offset"), - Self::WrappingOffset => write!(f, "wrapping_offset"), - } - } -} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 896d607fbcdd9..21dff28d5650f 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -73,7 +73,7 @@ msrv_aliases! { 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } - 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } + 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 4fe9dcf46c357..42d1abeaa05a6 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).add(offset_usize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_add(offset_usize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index a1fb892733d35..6d06a6af1fa2d 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).offset(offset_usize as isize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_offset(offset_usize as isize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index dcd5e027d1829..022b3286c93b7 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -2,16 +2,51 @@ error: use of `offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:12:17 | LL | let _ = ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_with_cast)]` +help: use `add` instead + | +LL - let _ = ptr.offset(offset_usize as isize); +LL + let _ = ptr.add(offset_usize); + | error: use of `wrapping_offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = ptr.wrapping_offset(offset_usize as isize); +LL + let _ = ptr.wrapping_add(offset_usize); + | + +error: use of `offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:25:17 + | +LL | let _ = (&ptr).offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = (&ptr).offset(offset_usize as isize); +LL + let _ = (&ptr).add(offset_usize); + | + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:27:17 + | +LL | let _ = (&ptr).wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = (&ptr).wrapping_offset(offset_usize as isize); +LL + let _ = (&ptr).wrapping_add(offset_usize); + | -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors From ce859d7713dbca1b2a3dff17ccc4d4a0598bffbf Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sun, 14 Sep 2025 08:06:11 -0700 Subject: [PATCH 0408/1900] Switch `std::vec::PeekMut::pop` from self to this parameter. Since PeekMut implements Deref, it shouldn't have any methods of its own. See also: `std::collections::binary_heap::PeekMut::pop` --- .mailmap | 1 + library/alloc/src/vec/peek_mut.rs | 4 ++-- library/alloctests/tests/vec.rs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index 6e3eed1226e0c..0f7bc5e38bd10 100644 --- a/.mailmap +++ b/.mailmap @@ -609,6 +609,7 @@ Shohei Wada Shotaro Yamada Shotaro Yamada Shyam Sundar B +Sidney Cammeresi Simon Barber-Dueck Simon BD Simon Sapin Simonas Kazlauskas Simonas Kazlauskas diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs index c0dd941ed3933..caeaf2799d733 100644 --- a/library/alloc/src/vec/peek_mut.rs +++ b/library/alloc/src/vec/peek_mut.rs @@ -29,9 +29,9 @@ impl<'a, T> PeekMut<'a, T> { /// Removes the peeked value from the vector and returns it. #[unstable(feature = "vec_peek_mut", issue = "122742")] - pub fn pop(self) -> T { + pub fn pop(this: Self) -> T { // SAFETY: PeekMut is only constructed if the vec is non-empty - unsafe { self.vec.pop().unwrap_unchecked() } + unsafe { this.vec.pop().unwrap_unchecked() } } } diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 00f640cd17ea4..404eb49e1ea9c 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -15,7 +15,7 @@ use std::ops::Bound::*; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; -use std::vec::{Drain, IntoIter}; +use std::vec::{Drain, IntoIter, PeekMut}; use crate::testing::macros::struct_with_counted_drop; @@ -2647,7 +2647,7 @@ fn test_peek_mut() { assert_eq!(*p, 2); *p = 0; assert_eq!(*p, 0); - p.pop(); + PeekMut::pop(p); assert_eq!(vec.len(), 1); } else { unreachable!() From 8af66386660b17ec85a34705f8a475fd0874ac70 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 13 Sep 2025 14:01:24 +0500 Subject: [PATCH 0409/1900] Changelog for Clippy 1.90 --- CHANGELOG.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2a76a818369..4a9be02143722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,152 @@ document. ## Unreleased / Beta / In Rust Nightly -[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) +[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) + +## Rust 1.90 + +Current stable, released 2025-09-18 + +[View all 118 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-06-13T15%3A55%3A04Z..2025-07-25T13%3A24%3A00Z+base%3Amaster) + +Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see +[#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. + +## New Lints + +* Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) +* Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) + +### Moves and Deprecations + +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) + +### Enhancements + +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) + [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) +* [`incompatible_msrv`] now recognizes types exceeding MSRV + [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context + [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) +* [`zero_ptr`] now lints in `const` context as well + [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) +* [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function + [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag + [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) + +### False Positive Fixes + +* [`if_then_some_else_none`] fixed FP when require type coercion + [#15267](https://github.com/rust-lang/rust-clippy/pull/15267) +* [`module_name_repetitions`] fixed FP on exported macros + [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) +* [`unused_async`] fixed FP on function with `todo!` + [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes + on `use` statements + [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) +* [`pattern_type_mismatch`] fixed FP in external macro + [#15306](https://github.com/rust-lang/rust-clippy/pull/15306) +* [`large_enum_variant`] fixed FP by not suggesting `Box` in `no_std` mode + [#15241](https://github.com/rust-lang/rust-clippy/pull/15241) +* [`missing_inline_in_public_items`] fixed FP on functions with `extern` + [#15313](https://github.com/rust-lang/rust-clippy/pull/15313) +* [`needless_range_loop`] fixed FP on array literals + [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) +* [`ptr_as_ptr`] fixed wrong suggestions with turbo fish + [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe + to switch the range type + [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) +* [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument + [#15105](https://github.com/rust-lang/rust-clippy/pull/15105) +* [`unsafe_derive_deserialize`] fixed FP caused by the standard library's `pin!()` macro + [#15137](https://github.com/rust-lang/rust-clippy/pull/15137) +* [`unused_trait_names`] fixed FP in macros + [#14947](https://github.com/rust-lang/rust-clippy/pull/14947) +* [`expect_fun_call`] fixed invalid suggestions + [#15122](https://github.com/rust-lang/rust-clippy/pull/15122) +* [`manual_is_multiple_of`] fixed various false positives + [#15205](https://github.com/rust-lang/rust-clippy/pull/15205) +* [`manual_assert`] fixed wrong suggestions for macros + [#15264](https://github.com/rust-lang/rust-clippy/pull/15264) +* [`expect_used`] fixed false negative when meeting `Option::ok().expect` + [#15253](https://github.com/rust-lang/rust-clippy/pull/15253) +* [`manual_abs_diff`] fixed wrong suggestions behind refs + [#15265](https://github.com/rust-lang/rust-clippy/pull/15265) +* [`approx_constant`] fixed FP with overly precise literals and non trivially formatted numerals + [#15236](https://github.com/rust-lang/rust-clippy/pull/15236) +* [`arithmetic_side_effects`] fixed FP on `NonZeroU*.get() - 1` + [#15238](https://github.com/rust-lang/rust-clippy/pull/15238) +* [`legacy_numeric_constants`] fixed suggestion when call is inside parentheses + [#15191](https://github.com/rust-lang/rust-clippy/pull/15191) +* [`manual_is_variant_and`] fixed inverted suggestions that could lead to code with different semantics + [#15206](https://github.com/rust-lang/rust-clippy/pull/15206) +* [`unnecessary_map_or`] fixed FP by not proposing to replace `map_or` call when types wouldn't be correct + [#15181](https://github.com/rust-lang/rust-clippy/pull/15181) +* [`return_and_then`] fixed FP in case of a partially used expression + [#15115](https://github.com/rust-lang/rust-clippy/pull/15115) +* [`manual_let_else`] fixed FP with binding subpattern with unused name + [#15118](https://github.com/rust-lang/rust-clippy/pull/15118) +* [`suboptimal_flops`] fixed FP with ambiguous float types in mul_add suggestions + [#15133](https://github.com/rust-lang/rust-clippy/pull/15133) +* [`borrow_as_ptr`] fixed FP by not removing cast to trait object pointer + [#15145](https://github.com/rust-lang/rust-clippy/pull/15145) +* [`unnecessary_operation`] fixed FP by not removing casts if they are useful to type the expression + [#15182](https://github.com/rust-lang/rust-clippy/pull/15182) +* [`empty_loop`] fixed FP on intrinsic function declaration + [#15201](https://github.com/rust-lang/rust-clippy/pull/15201) +* [`doc_nested_refdefs`] fixed FP where task lists are reported as refdefs + [#15146](https://github.com/rust-lang/rust-clippy/pull/15146) +* [`std_instead_of_core`] fixed FP when not all items come from the new crate + [#15165](https://github.com/rust-lang/rust-clippy/pull/15165) +* [`swap_with_temporary`] fixed FP leading to different semantics being suggested + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) +* [`cast_possible_truncation`] fixed FP by not giving suggestions inside const context + [#15164](https://github.com/rust-lang/rust-clippy/pull/15164) +* [`missing_panics_doc`] fixed FP by allowing unwrap() and expect()s in const-only contexts + [#15170](https://github.com/rust-lang/rust-clippy/pull/15170) +* [`disallowed_script_idents`] fixed FP on identifiers with `_` + [#15123](https://github.com/rust-lang/rust-clippy/pull/15123) +* [`wildcard_enum_match_arm`] fixed wrong suggestions with raw identifiers + [#15093](https://github.com/rust-lang/rust-clippy/pull/15093) +* [`borrow_deref_ref`] fixed FP by not proposing replacing a reborrow when the reborrow is itself mutably borrowed + [#14967](https://github.com/rust-lang/rust-clippy/pull/14967) +* [`manual_ok_err`] fixed wrong suggestions with references + [#15053](https://github.com/rust-lang/rust-clippy/pull/15053) +* [`identity_op`] fixed FP when encountering `Default::default()` + [#15028](https://github.com/rust-lang/rust-clippy/pull/15028) +* [`exhaustive_structs`] fixed FP on structs with default valued field + [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) +* [`collapsible_else_if`] fixed FP on conditionally compiled stmt + [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing + non-`Sized` trait bounds + [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) +* [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` + [#15082](https://github.com/rust-lang/rust-clippy/pull/15082) + +### ICE Fixes + +* [`single_match`] fixed ICE with deref patterns and string literals + [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) +* [`needless_doctest_main`] fixed panic when doctest is invalid + [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) +* [`zero_sized_map_values`] do not attempt to compute size of a type with escaping lifetimes + [#15434](https://github.com/rust-lang/rust-clippy/pull/15434) + +### Documentation Improvements + +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns + [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) +* [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter + [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) +* [`undocumented_unsafe_blocks`] improved documentation wording + [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 From 572b42346482472736723fe433b417fe32a01800 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 14 Sep 2025 11:33:11 -0600 Subject: [PATCH 0410/1900] On FreeBSD, use readdir instead of readdir_r readdir_r has the same problems on FreeBSD as it does on other platforms: it assumes a fixed NAME_MAX. And readdir has the same thread-safety guarantee as it does on other platforms: it's safe as long as only one thread tries to read from the directory stream at a given time. Furthermore, readdir_r is likely to be removed for FreeBSD 16, so we should stop using it now. --- library/std/src/sys/fs/unix.rs | 120 +++++++++++++++++---------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index dfd6ce56a7682..33a1e7ff5e40e 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -21,29 +21,31 @@ use libc::fstatat as fstatat64; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( + target_os = "aix", target_os = "android", - target_os = "solaris", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", target_os = "illumos", - target_os = "aix", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; #[cfg(not(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", )))] use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] @@ -271,16 +273,17 @@ unsafe impl Send for Dir {} unsafe impl Sync for Dir {} #[cfg(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", - target_os = "aix", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", ))] pub struct DirEntry { dir: Arc, @@ -295,16 +298,17 @@ pub struct DirEntry { // we're not using the immediate `d_name` on these targets. Keeping this as an // `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere. #[cfg(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", - target_os = "aix", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", ))] struct dirent64_min { d_ino: u64, @@ -319,16 +323,17 @@ struct dirent64_min { } #[cfg(not(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", - target_os = "aix", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", )))] pub struct DirEntry { dir: Arc, @@ -698,16 +703,17 @@ impl Iterator for ReadDir { type Item = io::Result; #[cfg(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", + target_os = "hurd", target_os = "illumos", - target_os = "aix", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", ))] fn next(&mut self) -> Option> { use crate::sys::os::{errno, set_errno}; @@ -768,6 +774,9 @@ impl Iterator for ReadDir { // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { + #[cfg(target_os = "freebsd")] + d_ino: (*entry_ptr).d_fileno, + #[cfg(not(target_os = "freebsd"))] d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", @@ -791,16 +800,17 @@ impl Iterator for ReadDir { } #[cfg(not(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", + target_os = "freebsd", target_os = "fuchsia", - target_os = "redox", + target_os = "hurd", target_os = "illumos", - target_os = "aix", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -970,36 +980,32 @@ impl DirEntry { } #[cfg(any( - target_os = "linux", + target_os = "aix", + target_os = "android", target_os = "cygwin", target_os = "emscripten", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "vxworks", target_os = "espidf", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", target_os = "horizon", - target_os = "vita", - target_os = "aix", - target_os = "nto", target_os = "hurd", + target_os = "illumos", + target_os = "l4re", + target_os = "linux", + target_os = "nto", + target_os = "redox", target_os = "rtems", + target_os = "solaris", + target_os = "vita", + target_os = "vxworks", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 } - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "dragonfly" - ))] + #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly"))] pub fn ino(&self) -> u64 { self.entry.d_fileno as u64 } @@ -1014,7 +1020,6 @@ impl DirEntry { #[cfg(any( target_os = "netbsd", target_os = "openbsd", - target_os = "freebsd", target_os = "dragonfly", target_vendor = "apple", ))] @@ -1030,7 +1035,6 @@ impl DirEntry { #[cfg(not(any( target_os = "netbsd", target_os = "openbsd", - target_os = "freebsd", target_os = "dragonfly", target_vendor = "apple", )))] @@ -1040,6 +1044,7 @@ impl DirEntry { #[cfg(not(any( target_os = "android", + target_os = "freebsd", target_os = "linux", target_os = "solaris", target_os = "illumos", @@ -1055,6 +1060,7 @@ impl DirEntry { } #[cfg(any( target_os = "android", + target_os = "freebsd", target_os = "linux", target_os = "solaris", target_os = "illumos", From 5025bfa1466488a879bd83f8a022362970182ce4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 20:34:02 +0300 Subject: [PATCH 0411/1900] Improve `rust-analyzer diagnostics` In my experience the `processing ` messages make it harder to search for the actual diagnostics, so remove them and instead print the filename only if there is a diagnostic. Also allow choosing the minimum severity. --- .../rust-analyzer/src/cli/diagnostics.rs | 46 +++++++++++++------ .../crates/rust-analyzer/src/cli/flags.rs | 24 ++++++++++ 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 7b12cb14009ff..82590c8e707fa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -9,7 +9,7 @@ use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Se use ide_db::{LineIndexDatabase, base_db::SourceDatabase}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; -use crate::cli::flags; +use crate::cli::{flags, progress_report::ProgressReport}; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { @@ -50,23 +50,26 @@ impl flags::Diagnostics { let mut found_error = false; let mut visited_files = FxHashSet::default(); - - let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source_file_id(db).original_file(db); - let source_root = db.file_source_root(file_id.file_id(db)).source_root_id(db); - let source_root = db.source_root(source_root).source_root(db); - !source_root.is_library - }); - + let min_severity = self.severity.unwrap_or(flags::Severity::Weak); + + let work = all_modules(db) + .into_iter() + .filter(|module| { + let file_id = module.definition_source_file_id(db).original_file(db); + let source_root = db.file_source_root(file_id.file_id(db)).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); + !source_root.is_library + }) + .collect::>(); + + let mut bar = ProgressReport::new(work.len()); for module in work { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { + let message = format!("processing {}", _vfs.file_path(file_id.file_id(db))); + bar.set_message(move || message.clone()); let crate_name = module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); - println!( - "processing crate: {crate_name}, module: {}", - _vfs.file_path(file_id.file_id(db)) - ); for diagnostic in analysis .full_diagnostics( &DiagnosticsConfig::test_sample(), @@ -75,6 +78,16 @@ impl flags::Diagnostics { ) .unwrap() { + let severity = match diagnostic.severity { + Severity::Error => flags::Severity::Error, + Severity::Warning => flags::Severity::Warning, + Severity::WeakWarning => flags::Severity::Weak, + Severity::Allow => continue, + }; + if severity < min_severity { + continue; + } + if matches!(diagnostic.severity, Severity::Error) { found_error = true; } @@ -83,12 +96,17 @@ impl flags::Diagnostics { let line_index = db.line_index(range.file_id); let start = line_index.line_col(range.range.start()); let end = line_index.line_col(range.range.end()); - println!("{severity:?} {code:?} from {start:?} to {end:?}: {message}"); + bar.println(format!( + "at crate {crate_name}, file {}: {severity:?} {code:?} from {start:?} to {end:?}: {message}", + _vfs.file_path(file_id.file_id(db)) + )); } visited_files.insert(file_id); } + bar.inc(1); } + bar.finish_and_clear(); println!(); println!("diagnostic scan complete"); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 16f351272b691..75030bedfca3f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -124,6 +124,9 @@ xflags::xflags! { optional --disable-proc-macros /// Run the proc-macro-srv binary at the specified path. optional --proc-macro-srv path: PathBuf + + /// The minimum severity. + optional --severity severity: Severity } /// Report unresolved references @@ -281,6 +284,7 @@ pub struct Diagnostics { pub disable_build_scripts: bool, pub disable_proc_macros: bool, pub proc_macro_srv: Option, + pub severity: Option, } #[derive(Debug)] @@ -376,3 +380,23 @@ impl FromStr for OutputFormat { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Severity { + Weak, + Warning, + Error, +} + +impl FromStr for Severity { + type Err = String; + + fn from_str(s: &str) -> Result { + match &*s.to_ascii_lowercase() { + "weak" => Ok(Self::Weak), + "warning" => Ok(Self::Warning), + "error" => Ok(Self::Error), + _ => Err(format!("unknown severity `{s}`")), + } + } +} From 927c4c03194da284404985747c2cad25fa1f5066 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Sep 2025 17:55:33 -0400 Subject: [PATCH 0412/1900] Fix typo in error message --- compiler/rustc_middle/messages.ftl | 2 +- tests/ui/consts/const_refs_to_static_fail.stderr | 2 +- tests/ui/consts/const_refs_to_static_fail_invalid.stderr | 4 ++-- .../ui/consts/miri_unleashed/const_refers_to_static.stderr | 2 +- .../const_refers_to_static_cross_crate.stderr | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 69adb2fe3919f..279ab9a9d8fbe 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -84,7 +84,7 @@ middle_failed_writing_file = # Note: We only mention patterns here since the error can only occur with references, and those # are forbidden in const generics. middle_invalid_const_in_valtree = constant {$global_const_id} cannot be used as pattern - .note = constants that reference mutable or external memory cannot be used as pattern + .note = constants that reference mutable or external memory cannot be used as patterns middle_layout_cycle = a cycle occurred during layout computation diff --git a/tests/ui/consts/const_refs_to_static_fail.stderr b/tests/ui/consts/const_refs_to_static_fail.stderr index c567b3e0ce1f4..2bb6d2b8fef7c 100644 --- a/tests/ui/consts/const_refs_to_static_fail.stderr +++ b/tests/ui/consts/const_refs_to_static_fail.stderr @@ -16,7 +16,7 @@ error: constant BAD_PATTERN cannot be used as pattern LL | BAD_PATTERN => {}, | ^^^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index f9088c318a621..226a9de285dc3 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -15,7 +15,7 @@ error: constant extern_::C cannot be used as pattern LL | C => {} | ^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant mutable::C cannot be used as pattern --> $DIR/const_refs_to_static_fail_invalid.rs:42:9 @@ -23,7 +23,7 @@ error: constant mutable::C cannot be used as pattern LL | C => {} | ^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 3 previous errors diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr index 6b70a211a72cc..5b8797c511627 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -22,7 +22,7 @@ error: constant REF_INTERIOR_MUT cannot be used as pattern LL | REF_INTERIOR_MUT => {}, | ^^^^^^^^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns warning: skipping const checks | diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr index d753506cc94e3..c2b730375f2c2 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -10,7 +10,7 @@ error: constant SLICE_MUT cannot be used as pattern LL | SLICE_MUT => true, | ^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant U8_MUT cannot be used as pattern --> $DIR/const_refers_to_static_cross_crate.rs:44:9 @@ -18,7 +18,7 @@ error: constant U8_MUT cannot be used as pattern LL | U8_MUT => true, | ^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant U8_MUT2 cannot be used as pattern --> $DIR/const_refers_to_static_cross_crate.rs:53:9 @@ -26,7 +26,7 @@ error: constant U8_MUT2 cannot be used as pattern LL | U8_MUT2 => true, | ^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 4 previous errors From 37d058fa048acd6bf385677f7cdb1e020ceb0769 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 14 Sep 2025 23:03:56 +0100 Subject: [PATCH 0413/1900] tidy: Remove `WorkspaceInfo` constructor --- src/tools/tidy/src/deps.rs | 150 ++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 290f0ea313419..8092a8000cf58 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -63,71 +63,101 @@ pub(crate) struct WorkspaceInfo<'a> { pub(crate) submodules: &'a [&'a str], } -impl<'a> WorkspaceInfo<'a> { - const fn new( - path: &'a str, - exceptions: ExceptionList, - crates_and_deps: Option<(&'a [&str], &'a [&str], ListLocation)>, - submodules: &'a [&str], - ) -> Self { - Self { path, exceptions, crates_and_deps, submodules } - } -} - /// The workspaces to check for licensing and optionally permitted dependencies. // FIXME auto detect all cargo workspaces pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - WorkspaceInfo::new( - ".", - EXCEPTIONS, - Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION)), - &[], - ), - WorkspaceInfo::new( - "library", - EXCEPTIONS_STDLIB, - Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION)), - &[], - ), - // Outside of the alphabetical section because rustfmt formats it using multiple lines. - WorkspaceInfo::new( - "compiler/rustc_codegen_cranelift", - EXCEPTIONS_CRANELIFT, - Some(( - &["rustc_codegen_cranelift"], - PERMITTED_CRANELIFT_DEPENDENCIES, - PERMITTED_CRANELIFT_DEPS_LOCATION, + WorkspaceInfo { + path: ".", + exceptions: EXCEPTIONS, + crates_and_deps: Some(( + &["rustc-main"], + PERMITTED_RUSTC_DEPENDENCIES, + PERMITTED_RUSTC_DEPS_LOCATION, )), - &[], - ), - // tidy-alphabetical-start - WorkspaceInfo::new("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), - WorkspaceInfo::new("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), - WorkspaceInfo::new("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), - //WorkspaceInfo::new("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored - //WorkspaceInfo::new("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored - WorkspaceInfo::new("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), - WorkspaceInfo::new( - "src/tools/rustbook", - EXCEPTIONS_RUSTBOOK, - None, - &["src/doc/book", "src/doc/reference"], - ), - WorkspaceInfo::new( - "src/tools/rustc-perf", - EXCEPTIONS_RUSTC_PERF, - None, - &["src/tools/rustc-perf"], - ), - WorkspaceInfo::new("src/tools/test-float-parse", EXCEPTIONS, None, &[]), - WorkspaceInfo::new( - "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", - EXCEPTIONS_UEFI_QEMU_TEST, - None, - &[], - ), - // tidy-alphabetical-end + submodules: &[], + }, + WorkspaceInfo { + path: "library", + exceptions: EXCEPTIONS_STDLIB, + crates_and_deps: Some(( + &["sysroot"], + PERMITTED_STDLIB_DEPENDENCIES, + PERMITTED_STDLIB_DEPS_LOCATION, + )), + submodules: &[], + }, + { + WorkspaceInfo { + path: "compiler/rustc_codegen_cranelift", + exceptions: EXCEPTIONS_CRANELIFT, + crates_and_deps: Some(( + &["rustc_codegen_cranelift"], + PERMITTED_CRANELIFT_DEPENDENCIES, + PERMITTED_CRANELIFT_DEPS_LOCATION, + )), + submodules: &[], + } + }, + WorkspaceInfo { + path: "compiler/rustc_codegen_gcc", + exceptions: EXCEPTIONS_GCC, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/bootstrap", + exceptions: EXCEPTIONS_BOOTSTRAP, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/tools/cargo", + exceptions: EXCEPTIONS_CARGO, + crates_and_deps: None, + submodules: &["src/tools/cargo"], + }, + // FIXME uncomment once all deps are vendored + // WorkspaceInfo { + // path: "src/tools/miri/test-cargo-miri", + // crates_and_deps: None + // submodules: &[], + // }, + // WorkspaceInfo { + // path: "src/tools/miri/test_dependencies", + // crates_and_deps: None, + // submodules: &[], + // } + WorkspaceInfo { + path: "src/tools/rust-analyzer", + exceptions: EXCEPTIONS_RUST_ANALYZER, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/tools/rustbook", + exceptions: EXCEPTIONS_RUSTBOOK, + crates_and_deps: None, + submodules: &["src/doc/book", "src/doc/reference"], + }, + WorkspaceInfo { + path: "src/tools/rustc-perf", + exceptions: EXCEPTIONS_RUSTC_PERF, + crates_and_deps: None, + submodules: &["src/tools/rustc-perf"], + }, + WorkspaceInfo { + path: "src/tools/test-float-parse", + exceptions: EXCEPTIONS, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", + exceptions: EXCEPTIONS_UEFI_QEMU_TEST, + crates_and_deps: None, + submodules: &[], + }, ]; /// These are exceptions to Rust's permissive licensing policy, and From b92ad97f1d8d458de880daea3826d8db30717a0e Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 14 Sep 2025 18:26:29 -0500 Subject: [PATCH 0414/1900] bootstrap.py: disable incremental build for bootstrap in CI --- src/bootstrap/bootstrap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2ece53eb0cc99..e017cfe6aefbf 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1039,6 +1039,9 @@ def build_bootstrap_cmd(self, env): # See also: . if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] + # if in CI, don't use incremental build when building bootstrap. + if "GITHUB_ACTIONS" in env: + env["CARGO_INCREMENTAL"] = "0" env["CARGO_TARGET_DIR"] = build_dir env["RUSTC"] = self.rustc() env["LD_LIBRARY_PATH"] = ( From 5ebdec5ac2908b0bae42adbe451beeadbe8fa5de Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Mon, 15 Sep 2025 02:16:34 +0000 Subject: [PATCH 0415/1900] rustc_codegen_llvm: Adjust RISC-V inline assembly's clobber list Despite that the `fflags` register (representing floating point exception flags) is stated as a flag register in the reference, it's not in the default clobber list of the RISC-V inline assembly and it would be better to fix it. --- compiler/rustc_codegen_llvm/src/asm.rs | 1 + tests/codegen-llvm/asm/riscv-clobbers.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 38c1d3b53e87c..b79176e909818 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -240,6 +240,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { constraints.extend_from_slice(&[ + "~{fflags}".to_string(), "~{vtype}".to_string(), "~{vl}".to_string(), "~{vxsat}".to_string(), diff --git a/tests/codegen-llvm/asm/riscv-clobbers.rs b/tests/codegen-llvm/asm/riscv-clobbers.rs index e55b6731098e7..0f235ddcdcc85 100644 --- a/tests/codegen-llvm/asm/riscv-clobbers.rs +++ b/tests/codegen-llvm/asm/riscv-clobbers.rs @@ -17,7 +17,7 @@ extern crate minicore; use minicore::*; // CHECK-LABEL: @flags_clobber -// CHECK: call void asm sideeffect "", "~{vtype},~{vl},~{vxsat},~{vxrm}"() +// CHECK: call void asm sideeffect "", "~{fflags},~{vtype},~{vl},~{vxsat},~{vxrm}"() #[no_mangle] pub unsafe fn flags_clobber() { asm!("", options(nostack, nomem)); From 55b8d4e6ffc220db78b928aacc274423631134f7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 07:56:35 +0300 Subject: [PATCH 0416/1900] Don't mark unknown type as implementing every notable trait Because the new solver, will return "yes" for them (which is a good thing). --- .../rust-analyzer/crates/ide/src/hover.rs | 6 +++++ .../crates/ide/src/hover/tests.rs | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 3f52303d74e48..03b9b3677511c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -483,6 +483,12 @@ fn notable_traits<'db>( db: &'db RootDatabase, ty: &hir::Type<'db>, ) -> Vec<(hir::Trait, Vec<(Option>, hir::Name)>)> { + if ty.is_unknown() { + // The trait solver returns "yes" to the question whether the error type + // impls any trait, and we don't want to show it as having any notable trait. + return Vec::new(); + } + db.notable_traits_in_deps(ty.krate(db).into()) .iter() .flat_map(|it| &**it) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 58d8a7edbe042..a88c0c9866e5f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -11097,3 +11097,26 @@ impl Enum<'_, Borrowed> { "#]], ); } + +#[test] +fn unknown_should_not_implement_notable_traits() { + check( + r#" +//- minicore: future, iterator +fn foo() { + let x$0; +} + "#, + expect![[r#" + *x* + + ```rust + let x: {unknown} + ``` + + --- + + no Drop + "#]], + ); +} From 91f32ed1803b7164446f386c5844718e963aeb0c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 08:25:17 +0300 Subject: [PATCH 0417/1900] Add regression tests to some S-blocked-on-new-solver issues That were fixed by the migration. --- .../hir-ty/src/tests/regression/new_solver.rs | 126 ++++++++++++++++++ .../crates/test-utils/src/minicore.rs | 12 +- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 595f285bd930e..f559230fee394 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -2,6 +2,132 @@ use expect_test::expect; use crate::tests::{check_infer, check_no_mismatches}; +#[test] +fn regression_20365() { + check_infer( + r#" +//- minicore: iterator +struct Vec(T); +struct IntoIter(T); +impl IntoIterator for Vec { + type IntoIter = IntoIter; + type Item = T; +} +impl Iterator for IntoIter { + type Item = T; +} + +fn f(a: Vec) { + let iter = a.into_iter(); +} + +pub trait Space: IntoIterator { + type Ty: Space; +} +impl Space for [u8; 1] { + type Ty = Self; +} + "#, + expect![[r#" + 201..202 'a': Vec + 213..246 '{ ...r(); }': () + 223..227 'iter': IntoIter + 230..231 'a': Vec + 230..243 'a.into_iter()': IntoIter + "#]], + ); +} + +#[test] +fn regression_19971() { + check_infer( + r#" +//- minicore: pointee +fn make(_thin: *const (), _meta: core::ptr::DynMetadata) -> *const T +where + T: core::ptr::Pointee> + ?Sized, +{ + loop {} +} +trait Foo { + fn foo(&self) -> i32 { + loop {} + } +} + +fn test() -> i32 { + struct F {} + impl Foo for F {} + let meta = core::ptr::metadata(0 as *const F as *const dyn Foo); + + let f = F {}; + let fat_ptr = make(&f as *const F as *const (), meta); // <-- infers type as `*const {unknown}` + + let fat_ref = unsafe { &*fat_ptr }; // <-- infers type as `&{unknown}` + fat_ref.foo() // cannot 'go to definition' on `foo` +} + + "#, + expect![[r#" + 11..16 '_thin': *const () + 29..34 '_meta': DynMetadata + 155..170 '{ loop {} }': *const T + 161..168 'loop {}': ! + 166..168 '{}': () + 195..199 'self': &'? Self + 208..231 '{ ... }': i32 + 218..225 'loop {}': ! + 223..225 '{}': () + 252..613 '{ ...foo` }': i32 + 300..304 'meta': DynMetadata + 307..326 'core::...tadata': fn metadata(*const (dyn Foo + '?)) -> ::Metadata + 307..359 'core::...n Foo)': DynMetadata + 327..328 '0': usize + 327..340 '0 as *const F': *const F + 327..358 '0 as *...yn Foo': *const (dyn Foo + '?) + 370..371 'f': F + 374..378 'F {}': F + 388..395 'fat_ptr': *const (dyn Foo + '?) + 398..402 'make': fn make(*const (), DynMetadata) -> *const (dyn Foo + '?) + 398..437 'make(&... meta)': *const (dyn Foo + '?) + 403..405 '&f': &'? F + 403..417 '&f as *const F': *const F + 403..430 '&f as ...nst ()': *const () + 404..405 'f': F + 432..436 'meta': DynMetadata + 489..496 'fat_ref': &'? (dyn Foo + '?) + 499..519 'unsafe..._ptr }': &'? (dyn Foo + '?) + 508..517 '&*fat_ptr': &'? (dyn Foo + '?) + 509..517 '*fat_ptr': dyn Foo + '? + 510..517 'fat_ptr': *const (dyn Foo + '?) + 560..567 'fat_ref': &'? (dyn Foo + '?) + 560..573 'fat_ref.foo()': i32 + "#]], + ); +} + +#[test] +fn regression_19752() { + check_no_mismatches( + r#" +//- minicore: sized, copy +trait T1: Sized + Copy { + fn a(self, other: Self) -> Self { + other + } + + fn b(&mut self, other: Self) { + *self = self.a(other); + } +} + +trait T2: Sized { + type T1: T1; +} + "#, + ); +} + #[test] fn opaque_generics() { check_infer( diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 26e0f46705b9e..7c3e7fea1bdef 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -55,7 +55,7 @@ //! panic: fmt //! phantom_data: //! pin: -//! pointee: copy, send, sync, ord, hash, unpin +//! pointee: copy, send, sync, ord, hash, unpin, phantom_data //! range: //! receiver: deref //! result: @@ -504,6 +504,16 @@ pub mod ptr { #[lang = "metadata_type"] type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } + + #[lang = "dyn_metadata"] + pub struct DynMetadata { + _phantom: crate::marker::PhantomData, + } + + pub const fn metadata(ptr: *const T) -> ::Metadata { + loop {} + } + // endregion:pointee // region:non_null #[rustc_layout_scalar_valid_range_start(1)] From f40e71c9de3d5d1075fa9fd94fccdd6126ae1ad4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 08:50:08 +0300 Subject: [PATCH 0418/1900] Add Regression Test For The One And The Only Issue #5514 --- .../hir-ty/src/tests/regression/new_solver.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index f559230fee394..82d670cef2b0e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -128,6 +128,40 @@ trait T2: Sized { ); } +#[test] +fn regression_type_checker_does_not_eagerly_select_predicates_from_where_clauses() { + // This was a very long standing issue (#5514) with a lot of duplicates, that was + // fixed by the switch to the new trait solver, so it deserves a long name and a + // honorable mention. + check_infer( + r#" +//- minicore: from + +struct Foo; +impl Foo { + fn method(self) -> i32 { 0 } +} + +fn f>(u: T) { + let x = u.into(); + x.method(); +} + "#, + expect![[r#" + 38..42 'self': Foo + 51..56 '{ 0 }': i32 + 53..54 '0': i32 + 79..80 'u': T + 85..126 '{ ...d(); }': () + 95..96 'x': Foo + 99..100 'u': T + 99..107 'u.into()': Foo + 113..114 'x': Foo + 113..123 'x.method()': i32 + "#]], + ); +} + #[test] fn opaque_generics() { check_infer( From 96f8dbe1b33691a949b23a005581b66c6f7e4cbf Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 15 Sep 2025 16:36:00 +1000 Subject: [PATCH 0419/1900] compiletest: Enable new-output-capture by default --- src/tools/compiletest/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index f647f96a9bf34..17a60ace80460 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -472,7 +472,7 @@ pub fn parse_config(args: Vec) -> Config { let value = matches .opt_str("new-output-capture") .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok()) - .unwrap_or_else(|| "off".to_owned()); + .unwrap_or_else(|| "on".to_owned()); parse_bool_option(&value) .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given")) }, From 7b0b0bbefa8d0dd3ff3e1698c76db247e8641fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 09:45:19 +0200 Subject: [PATCH 0420/1900] Clarify that backtick escaping doesn't work for `@bors try jobs` --- src/doc/rustc-dev-guide/src/tests/ci.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index a8cc959124ff6..a32edd7240117 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -151,8 +151,9 @@ which can be used in one of two ways: Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using -glob patterns, you might want to wrap them in backticks (`` ` ``) to avoid GitHub rendering -the pattern as Markdown. +glob patterns in the PR description, you can wrap them in backticks (`` ` ``) to avoid GitHub rendering +the pattern as Markdown if it contains e.g. an asterisk. Note that this escaping will not work when using +the `@bors jobs=` parameter. The job pattern needs to match one or more jobs defined in the `auto` or `optional` sections of [`jobs.yml`]: From f6a7da8673de4a87e8e882ff533904274396726b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 10:48:26 +0300 Subject: [PATCH 0421/1900] Add a testing guide --- .../rust-analyzer/docs/book/src/SUMMARY.md | 1 + .../docs/book/src/contributing/testing.md | 106 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/tools/rust-analyzer/docs/book/src/contributing/testing.md diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md index dffdae94a6e86..3fb46d59a4a50 100644 --- a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -23,3 +23,4 @@ - [Setup](contributing/setup.md) - [Style](contributing/style.md) - [Syntax](contributing/syntax.md) + - [Writing Tests](contributing/testing.md) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/testing.md b/src/tools/rust-analyzer/docs/book/src/contributing/testing.md new file mode 100644 index 0000000000000..ccee9b847b6e1 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/contributing/testing.md @@ -0,0 +1,106 @@ +rust-analyzer's testing is based on *snapshot tests*: a test is a piece of input text, usually a Rust code, and some output text. There is then some testing helper that runs the feature on the input text and compares the result to the output text. + +rust-analyzer uses a combination of the crate [`expect-test`](https://docs.rs/expect-test) and a custom testing framework. + +This all may sound too abstract, so let's demonstrate with an example. + +Type inference tests are located at `crates/hir-ty/src/tests`. There are various test helpers you can use. One of the simplest is `check_no_mismatches()`: it is given a piece of Rust code (we'll talk more about Rust code in tests later) and asserts that there are no type mismatches in it, that is, one type was expected but another was found (for example, `let x: () = 1` is a type mismatch). Note that we determine type mismatches via rust-analyzer's own analysis, not via the compiler (this is what we are testing, after all), which means there are often missed mismatches and sometimes bogus ones as well. + +For example, the following test will fail: +```rust +#[test] +fn this_will_fail() { + check_no_mismatches( + r#" +fn main() { + let x: () = 1; +} + "#, + ); +} +``` + +Sometimes we want to check more that there are no type mismatches. For that we use other helpers. For example, often we want to assert that the type of some expression is some specific type. For that we use the `check_types()` function. It takes a Rust code string with custom annotation, that are common in our test suite. The general scheme of annotation is: + + - `$0` marks a position. What to do with it is determined by the testing helper. Commonly it denotes the cursor position in IDE tests (for example, hover). + - `$0...$0` marks a range, commonly a selection in IDE tests. + - `^...^`, commonly seen in a comment (`// ^^^^`), labels the line above. For example, the following will attach the label `hey` to the range of the variable name `cool`: + + ```rust + let cool; + // ^^^^ hey + ``` + +`check_types()` uses labels to assert type: when you attach a label to a range, `check_types()` assert that the type of this range will be what written in the label. + +It's all too abstract without an example: +```rust +#[test] +fn my_test() { + check_types( + r#" +fn main() { + let x = 1; + // ^ i32 +} + "#, + ); +} +``` +Here, we assert that the type of the variable `x` is `i32`. Which is true, of course, so the test will pass. + +Oftentimes it is convenient to assert the types of all of the expressions at once, and that brings us to the last kind of test. It uses `expect-test` to match an output text: +```rust +#[test] +fn my_test() { + check_infer( + r#" +fn main() { + let x = 1; +} + "#, + expect![[r#" + 10..28 '{ ...= 1; }': () + 20..21 'x': i32 + 24..25 '1': i32 + "#]], + ); +} +``` +The text inside the `expect![[]]` is determined by the helper, `check_infer()` in this case. For `check_infer()`, each line is a range in the source code (the range is counted in bytes and the source is trimmed, indentation is stripped), next to it there is the text in that range, or some part of it with `...` if it's too long, and finally comes the type of that range. + +The important feature of `expect-test` is that it allows easy update of the expectation. Say you changed something in the code, maybe fixed a bug, and the output in `expect![[]]` needs to change. Or maybe you are writing it from scratch. Writing it by hand is very tedious and prone to mistakes. But `expect-trait` has a magic. You can set the environment variable `UPDATE_EXPECT=1`, then run the test, and it will update automatically! Some editors (e.g. VSCode) make it even more convenient: on them, on the top of every test that uses `expect-test`, next to the usual `Run | Debug` buttons, rust-analyzer also shows an `Update Expect` button. Clicking it will run that test in updating mode. + +## Rust code in the tests + +The first thing that you probably already noticed is that the Rust code in the tests is syntax highlighted! In fact, it even uses semantic highlighting. rust-analyzer highlights strings "as if" they contain Rust code if they are passed to a parameter marked `#[rust_analyzer::rust_fixture]`, and rust-analyzer test helpers do that (in fact, this was designed for them). + +The syntax highlighting is very important, not just because it's nice to the eye: it's very easy to make mistakes in test code, and debugging that can be very hard. Often the test will just fail, printing an `{unknown}` type, and you'll have no clue what's going wrong. The syntax is the clue; if something isn't highlighted correctly, that probably means there is an error (there is one exception to this, which we'll discuss later). You can even set the semantic highlighting tag `unresolved_reference` to e.g. red, so you will see such things clearly. + +Still, often you won't know what's going wrong. Why you can't fix the test, or worse, you expect it to fail but it doesn't. You can try the code on a real IDE to be sure it works. Later we'll give some tips to fix the test. + +### The fixture + +The Rust code in a test is not, a fact, a single Rust file. It has a mini-language that allows you to express multiple files, multiple crates, different configs, and more. All options are documented in `crates/test-utils/src/fixture.rs`, but here are some of the common ones: + + - `//- minicore: flag1, flag2, ...`. This is by far the most common flag. Tests in rust-analyzer don't have access by default to any other type - not `Option`, not `Iterator`, not even `Sized`. This flag allows you to include parts of the `crates/test-utils/src/minicore.rs` file, which mimics `core`. All possible flags are listed at the top of `minicore` along with the flags they imply, then later you can see by `// region:flag` and `// endregion:flag` what code each flag enables. + - `// /path/to/file.rs crate:crate deps:dep_a,dep_b`. The first component is the filename of the code that follows (until the next file). It is required, but only if you supply this line. Other components in this line are optional. They include `crate:crate_name`, to start a new crate, or `deps:dep_a,dep_b`, to declare dependencies between crates. You can also declare modules as usual in Rust - just name your paths `/foo.rs` or `/foo/mod.rs`, declare `mod foo` and that's it! + +So the following snippet: +```rust +//- minicore: sized, fn +// /lib.rs crate:foo +pub mod bar; +// /bar.rs +pub struct Bar; +// /main.rs crate:main deps:foo +use foo::Bar; +``` +Declares two crates `foo` and `main` where `main` depends on `foo`, with dependency in `Sized` and the `FnX` traits from `core`, and a module of `foo` called `bar`. + +And as promised, here are some tips to make your test work: + + - If you use some type/trait, you must *always* include it in `minicore`. Note - not all types from core/std are available there, you can add new (under flags) if you need. And import them if they are not in the prelude. + - If you use unsized types (`dyn Trait`/slices), you may want to include some or all of the following `minicore` flags: `sized`, `unsize`, `coerce_unsized`, `dispatch_from_dyn`. + - If you use closures, consider including the `fn` minicore flag. Async closures need the `async_fn` flag. + - `sized` is commonly needed, consider adding it if you're stuck. From 8265748da82be9909909930b191ead719cc760eb Mon Sep 17 00:00:00 2001 From: Maksim Bondarenkov Date: Mon, 15 Sep 2025 11:07:07 +0300 Subject: [PATCH 0422/1900] opt-dist: don't set `RUST_LOG=collector=debug` see [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/channel/122651-general/topic/opt-dist.3A.20do.20not.20set.20RUST_LOG.3Dcollector.3Ddebug.20forcefully) --- src/tools/opt-dist/src/training.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 4f9352d11b12c..b3e95e087e3f1 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -39,7 +39,6 @@ fn init_compiler_benchmarks( "--exact-match", crates.join(",").as_str(), ]) - .env("RUST_LOG", "collector=debug") .env("RUSTC", env.rustc_stage_0().as_str()) .env("RUSTC_BOOTSTRAP", "1") .workdir(&env.rustc_perf_dir()); From b919a5f5183afb876b5206b3b23b249183cb313c Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 08:57:22 +0000 Subject: [PATCH 0423/1900] Remove UnsizedConstParamTy trait and make it into an unstable impl --- .../src/deriving/bounds.rs | 39 ------------------- compiler/rustc_builtin_macros/src/lib.rs | 1 - compiler/rustc_hir/src/lang_items.rs | 1 - .../rustc_hir_analysis/src/check/wfcheck.rs | 13 +------ .../src/coherence/builtin.rs | 15 ++----- library/core/src/marker.rs | 33 +++------------- library/core/src/mem/transmutability.rs | 6 +-- library/core/src/tuple.rs | 10 +---- 8 files changed, 14 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index dd8f0e46a0e03..63342880b0941 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -51,43 +51,4 @@ pub(crate) fn expand_deriving_const_param_ty( }; trait_def.expand(cx, mitem, item, push); - - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); -} - -pub(crate) fn expand_deriving_unsized_const_param_ty( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 1bcea95fbb7b0..4541e2cd3b41d 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -129,7 +129,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Clone: clone::expand_deriving_clone, Copy: bounds::expand_deriving_copy, ConstParamTy: bounds::expand_deriving_const_param_ty, - UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty, Debug: debug::expand_deriving_debug, Default: default::expand_deriving_default, Eq: eq::expand_deriving_eq, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 67d2f15d41472..2e099a97b65be 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -370,7 +370,6 @@ language_item_table! { CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); - UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 22a9446fd4cf1..6270fb24ba709 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -819,17 +819,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er let span = tcx.def_span(param.def_id); let def_id = param.def_id.expect_local(); - if tcx.features().unsized_const_params() { - enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { - wfcx.register_bound( - ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), - wfcx.param_env, - ty, - tcx.require_lang_item(LangItem::UnsizedConstParamTy, span), - ); - Ok(()) - }) - } else if tcx.features().adt_const_params() { + if tcx.features().adt_const_params() { enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { wfcx.register_bound( ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), @@ -880,7 +870,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er tcx, tcx.param_env(param.def_id), ty, - LangItem::ConstParamTy, cause, ) { // Can never implement `ConstParamTy`, don't suggest anything. diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 32b175611ceb5..0b9a01d6042f1 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,7 +1,6 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use std::assert_matches::assert_matches; use std::collections::BTreeMap; use rustc_data_structures::fx::FxHashSet; @@ -40,10 +39,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; checker.check(lang_items.const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) - })?; - checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy) + visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; checker @@ -138,12 +134,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } -fn visit_implementation_of_const_param_ty( - checker: &Checker<'_>, - kind: LangItem, -) -> Result<(), ErrorGuaranteed> { - assert_matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); - +fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; let impl_did = checker.impl_def_id; @@ -157,7 +148,7 @@ fn visit_implementation_of_const_param_ty( } let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); - match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) { + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index d03d7a43469a7..1c100312a9a33 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1083,7 +1083,7 @@ pub trait Tuple {} // We name this differently than the derive macro so that the `adt_const_params` can // be used independently of `unsized_const_params` without requiring a full path // to the derive macro every time it is used. This should be renamed on stabilization. -pub trait ConstParamTy_: UnsizedConstParamTy + StructuralPartialEq + Eq {} +pub trait ConstParamTy_: StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] @@ -1093,23 +1093,6 @@ pub macro ConstParamTy($item:item) { /* compiler built-in */ } -#[lang = "unsized_const_param_ty"] -#[unstable(feature = "unsized_const_params", issue = "95174")] -#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] -/// A marker for types which can be used as types of `const` generic parameters. -/// -/// Equivalent to [`ConstParamTy_`] except that this is used by -/// the `unsized_const_params` to allow for fake unstable impls. -pub trait UnsizedConstParamTy: StructuralPartialEq + Eq {} - -/// Derive macro generating an impl of the trait `ConstParamTy`. -#[rustc_builtin_macro] -#[allow_internal_unstable(unsized_const_params)] -#[unstable(feature = "unsized_const_params", issue = "95174")] -pub macro UnsizedConstParamTy($item:item) { - /* compiler built-in */ -} - // FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] @@ -1124,17 +1107,11 @@ marker_impls! { marker_impls! { #[unstable(feature = "unsized_const_params", issue = "95174")] - UnsizedConstParamTy for - usize, u8, u16, u32, u64, u128, - isize, i8, i16, i32, i64, i128, - bool, - char, - (), - {T: UnsizedConstParamTy, const N: usize} [T; N], - + #[unstable_feature_bound(unsized_const_params)] + ConstParamTy_ for str, - {T: UnsizedConstParamTy} [T], - {T: UnsizedConstParamTy + ?Sized} &T, + {T: ConstParamTy_} [T], + {T: ConstParamTy_ + ?Sized} &T, } /// A common trait implemented by all function pointers. diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 782b826448a32..f36cb8cddb837 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,4 +1,4 @@ -use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; +use crate::marker::ConstParamTy_; /// Marks that `Src` is transmutable into `Self`. /// @@ -83,6 +83,7 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// Furthermore, stability does not imply portability. For example, the size of /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] +#[unstable_feature_bound(transmutability)] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl] #[rustc_do_not_implement_via_object] @@ -288,9 +289,8 @@ pub struct Assume { } #[unstable(feature = "transmutability", issue = "99571")] +#[unstable_feature_bound(transmutability)] impl ConstParamTy_ for Assume {} -#[unstable(feature = "transmutability", issue = "99571")] -impl UnsizedConstParamTy for Assume {} impl Assume { /// With this, [`TransmuteFrom`] does not assume you have ensured any safety diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 3892f83107690..c57a8d81ade18 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,7 +1,7 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; +use crate::marker::{ConstParamTy_, StructuralPartialEq}; use crate::ops::ControlFlow::{self, Break, Continue}; // Recursive macro for implementing n-ary tuple functions and operations @@ -47,17 +47,11 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[unstable(feature = "adt_const_params", issue = "95174")] + #[unstable_feature_bound(unsized_const_params)] impl<$($T: ConstParamTy_),+> ConstParamTy_ for ($($T,)+) {} } - maybe_tuple_doc! { - $($T)+ @ - #[unstable(feature = "unsized_const_params", issue = "95174")] - impl<$($T: UnsizedConstParamTy),+> UnsizedConstParamTy for ($($T,)+) - {} - } - maybe_tuple_doc! { $($T)+ @ #[unstable(feature = "structural_match", issue = "31434")] From 1a02cd531d6b5ff154078208daf7816b1657f389 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 08:59:19 +0000 Subject: [PATCH 0424/1900] Add check to make sure ConstParamTy impls of certain types are gated with #[unstable_feature_bound(unsized_const_params)] --- .../rustc_trait_selection/src/traits/misc.rs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 393f458bea273..4c25882daa92f 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,15 +1,14 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. -use std::assert_matches::assert_matches; - use hir::LangItem; use rustc_ast::Mutability; use rustc_hir as hir; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_span::sym; use crate::regions::InferCtxtRegionExt; -use crate::traits::{self, FulfillmentError, ObligationCause}; +use crate::traits::{self, FulfillmentError, Obligation, ObligationCause}; pub enum CopyImplementationError<'tcx> { InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), @@ -98,10 +97,9 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, - lang_item: LangItem, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); + let mut need_unstable_feature_bound = false; let inner_tys: Vec<_> = match *self_type.kind() { // Trivially okay as these types are all: @@ -112,18 +110,14 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( // Handle types gated under `feature(unsized_const_params)` // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references - ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) - if lang_item == LangItem::UnsizedConstParamTy => - { + ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => { + need_unstable_feature_bound = true; vec![inner_ty] } - ty::Str if lang_item == LangItem::UnsizedConstParamTy => { + ty::Str => { + need_unstable_feature_bound = true; vec![Ty::new_slice(tcx, tcx.types.u8)] } - ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => { - return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); - } - ty::Array(inner_ty, _) => vec![inner_ty], // `str` morally acts like a newtype around `[u8]` @@ -137,7 +131,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( adt, args, parent_cause.clone(), - lang_item, + LangItem::ConstParamTy, ) .map_err(ConstParamTyImplementationError::InfrigingFields)?; @@ -153,11 +147,25 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)] + if need_unstable_feature_bound { + ocx.register_obligation(Obligation::new( + tcx, + parent_cause.clone(), + param_env, + ty::ClauseKind::UnstableFeature(sym::unsized_const_params), + )); + + if !ocx.select_all_or_error().is_empty() { + return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); + } + } + ocx.register_bound( parent_cause.clone(), param_env, inner_ty, - tcx.require_lang_item(lang_item, parent_cause.span), + tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span), ); let errors = ocx.select_all_or_error(); From 0bd2ee3a0c260894eb92f5f0f5f8dd56182cda3f Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 09:01:22 +0000 Subject: [PATCH 0425/1900] Fix the testcases to not use UnsizedConstParamTy --- .../auxiliary/unsized_const_param.rs | 2 +- .../adt_const_params/const_param_ty_bad.rs | 2 +- .../const_param_ty_bad.stderr | 42 ++++++------- .../const_param_ty_dyn_compatibility.rs | 5 +- .../const_param_ty_dyn_compatibility.stderr | 19 +----- ...nst_param_ty_generic_bounds_do_not_hold.rs | 2 +- ...param_ty_generic_bounds_do_not_hold.stderr | 24 +++---- .../adt_const_params/const_param_ty_good.rs | 8 +-- .../const_param_ty_impl_bad_field.rs | 4 +- .../const_param_ty_impl_bad_field.stderr | 10 +-- .../const_param_ty_impl_no_structural_eq.rs | 8 +-- ...onst_param_ty_impl_no_structural_eq.stderr | 28 ++++----- .../const_param_ty_impl_union.rs | 4 +- .../const_param_ty_impl_union.stderr | 10 +-- .../nested_bad_const_param_ty.rs | 3 - .../nested_bad_const_param_ty.stderr | 63 +++---------------- .../non_valtreeable_const_arg-2.stderr | 6 +- .../reference_pointee_is_const_param-1.rs | 4 +- .../reference_pointee_is_const_param-1.stderr | 6 +- .../reference_pointee_is_const_param-2.rs | 4 +- .../reference_pointee_is_const_param-2.stderr | 6 +- .../trait_objects_as_a_const_generic.rs | 4 +- .../trait_objects_as_a_const_generic.stderr | 6 +- .../adt_const_params/unsized_field-1.rs | 5 +- .../adt_const_params/unsized_field-1.stderr | 28 +++++++-- .../adt_const_params/unsized_field-2.rs | 14 ----- .../adt_const_params/unsized_field-2.stderr | 27 -------- .../unsizing-wfcheck-issue-126272.rs | 1 - .../unsizing-wfcheck-issue-126272.stderr | 51 +++++++-------- ...const-param-with-additional-obligations.rs | 4 +- .../no_const_param_ty_bound.stderr | 2 +- tests/ui/const-generics/issue-66451.rs | 6 +- .../issues/issue-63322-forbid-dyn.full.stderr | 2 +- ...nst-param-mismatch.adt_const_params.stderr | 22 ++++--- tests/ui/const-generics/slice-const-param.rs | 2 +- ...m-static-reference.adt_const_params.stderr | 12 ++-- .../transmute-const-param-static-reference.rs | 2 +- .../symbol_mangling_v0_str.rs | 4 +- .../feature-gate-unsized-const-params.rs | 10 +-- .../feature-gate-unsized-const-params.stderr | 19 +++--- .../ui/layout/thaw-transmute-invalid-enum.rs | 1 + .../layout/thaw-transmute-invalid-enum.stderr | 19 ++++-- .../const-generics-structural-demangling.rs | 8 +-- 43 files changed, 218 insertions(+), 291 deletions(-) delete mode 100644 tests/ui/const-generics/adt_const_params/unsized_field-2.rs delete mode 100644 tests/ui/const-generics/adt_const_params/unsized_field-2.stderr diff --git a/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs b/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs index e2ba459f8dd33..1bfd7c3465613 100644 --- a/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs +++ b/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs @@ -1,6 +1,6 @@ #![feature(adt_const_params, unsized_const_params)] -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] pub struct Foo([u8]); #[derive(std::marker::ConstParamTy, Eq, PartialEq)] diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs index 35539193a2787..d482e7fad06d2 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(adt_const_params, unsized_const_params)] -fn check(_: impl std::marker::UnsizedConstParamTy) {} +fn check(_: impl std::marker::ConstParamTy_) {} fn main() { check(main); //~ error: `fn() {main}` can't be used as a const parameter type diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr index c05584ef909ac..ff514f5608f19 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr @@ -2,15 +2,15 @@ error[E0277]: `fn() {main}` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:7:11 | LL | check(main); - | ----- ^^^^ the trait `UnsizedConstParamTy` is not implemented for fn item `fn() {main}` + | ----- ^^^^ the trait `ConstParamTy_` is not implemented for fn item `fn() {main}` | | | required by a bound introduced by this call | note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this function | LL | check(main()); @@ -24,12 +24,12 @@ LL | check(|| {}); | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is not implemented for closure `{closure@$DIR/const_param_ty_bad.rs:8:11: 8:13}` + = help: the trait `ConstParamTy_` is not implemented for closure `{closure@$DIR/const_param_ty_bad.rs:8:11: 8:13}` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this closure | LL - check(|| {}); @@ -40,15 +40,15 @@ error[E0277]: `fn()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:9:11 | LL | check(main as fn()); - | ----- ^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `fn()` + | ----- ^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `fn()` | | | required by a bound introduced by this call | note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this function pointer | LL | check(main as fn()()); @@ -58,16 +58,16 @@ error[E0277]: `&mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:10:11 | LL | check(&mut ()); - | ----- ^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `&mut ()` + | ----- ^^^^^^^ the trait `ConstParamTy_` is not implemented for `&mut ()` | | | required by a bound introduced by this call | - = note: `UnsizedConstParamTy` is implemented for `&()`, but not for `&mut ()` + = note: `ConstParamTy_` is implemented for `&()`, but not for `&mut ()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: consider removing the leading `&`-reference | LL - check(&mut ()); @@ -78,31 +78,31 @@ error[E0277]: `*mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:11:11 | LL | check(&mut () as *mut ()); - | ----- ^^^^^^^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `*mut ()` + | ----- ^^^^^^^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `*mut ()` | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is implemented for `()` + = help: the trait `ConstParamTy_` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `*const ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:12:11 | LL | check(&() as *const ()); - | ----- ^^^^^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `*const ()` + | ----- ^^^^^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `*const ()` | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is implemented for `()` + = help: the trait `ConstParamTy_` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 6 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs index 6a553c2e08540..2fcf872c99aaf 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs @@ -1,12 +1,9 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::{ConstParamTy_, UnsizedConstParamTy}; +use std::marker::ConstParamTy_; fn foo(a: &dyn ConstParamTy_) {} //~^ ERROR: the trait `ConstParamTy_` -fn bar(a: &dyn UnsizedConstParamTy) {} -//~^ ERROR: the trait `UnsizedConstParamTy` - fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr index c71ec24dda330..ce695ff66d58c 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr @@ -15,23 +15,6 @@ LL - fn foo(a: &dyn ConstParamTy_) {} LL + fn foo(a: &impl ConstParamTy_) {} | -error[E0038]: the trait `UnsizedConstParamTy` is not dyn compatible - --> $DIR/const_param_ty_dyn_compatibility.rs:9:16 - | -LL | fn bar(a: &dyn UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $SRC_DIR/core/src/cmp.rs:LL:COL - | - = note: the trait is not dyn compatible because it uses `Self` as a type parameter -help: consider using an opaque type instead - | -LL - fn bar(a: &dyn UnsizedConstParamTy) {} -LL + fn bar(a: &impl UnsizedConstParamTy) {} - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs index 7ffdafa33e938..a86d74275de8a 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs @@ -4,7 +4,7 @@ #[derive(PartialEq, Eq)] struct NotParam; -fn check() {} +fn check() {} fn main() { check::<&NotParam>(); //~ error: `NotParam` can't be used as a const parameter type diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr index 158e76630f3a3..ca2aa3adcb7a6 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr @@ -4,17 +4,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<&NotParam>(); | ^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `&NotParam` to implement `UnsizedConstParamTy` + = note: required for `&NotParam` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `NotParam` can't be used as a const parameter type --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:11:13 @@ -22,17 +22,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<[NotParam]>(); | ^^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `[NotParam]` to implement `UnsizedConstParamTy` + = note: required for `[NotParam]` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `NotParam` can't be used as a const parameter type --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:12:13 @@ -40,17 +40,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<[NotParam; 17]>(); | ^^^^^^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `[NotParam; 17]` to implement `UnsizedConstParamTy` + = note: required for `[NotParam; 17]` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs index 98a8eb6ee950d..24bbc5a9a2389 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs @@ -3,7 +3,7 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::{ConstParamTy, ConstParamTy_}; #[derive(PartialEq, Eq)] struct S { @@ -11,15 +11,15 @@ struct S { gen: T, } -impl UnsizedConstParamTy for S {} +impl ConstParamTy_ for S {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] struct D { field: u8, gen: T, } -fn check() {} +fn check() {} fn main() { check::(); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs index 8b3d0546010dc..0614ea97b1add 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs @@ -7,10 +7,10 @@ struct NotParam; #[derive(PartialEq, Eq)] struct CantParam(NotParam); -impl std::marker::UnsizedConstParamTy for CantParam {} +impl std::marker::ConstParamTy_ for CantParam {} //~^ error: the trait `ConstParamTy_` cannot be implemented for this type -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] //~^ error: the trait `ConstParamTy_` cannot be implemented for this type struct CantParamDerive(NotParam); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr index a4e5736d83429..fd1836802c4ae 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr @@ -1,17 +1,17 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/const_param_ty_impl_bad_field.rs:10:43 + --> $DIR/const_param_ty_impl_bad_field.rs:10:37 | LL | struct CantParam(NotParam); | -------- this field does not implement `ConstParamTy_` LL | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_field.rs:13:10 | -LL | #[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct CantParamDerive(NotParam); | -------- this field does not implement `ConstParamTy_` diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index e743b78fd134b..a1c8eccfb095f 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -3,20 +3,20 @@ #[derive(PartialEq, Eq)] struct ImplementsConstParamTy; -impl std::marker::UnsizedConstParamTy for ImplementsConstParamTy {} +impl std::marker::ConstParamTy_ for ImplementsConstParamTy {} struct CantParam(ImplementsConstParamTy); -impl std::marker::UnsizedConstParamTy for CantParam {} +impl std::marker::ConstParamTy_ for CantParam {} //~^ error: the type `CantParam` does not `#[derive(PartialEq)]` //~| ERROR the trait bound `CantParam: Eq` is not satisfied -#[derive(std::marker::UnsizedConstParamTy)] +#[derive(std::marker::ConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(PartialEq)]` //~| ERROR the trait bound `CantParamDerive: Eq` is not satisfied struct CantParamDerive(ImplementsConstParamTy); -fn check() {} +fn check() {} fn main() { check::(); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index d3141381db8cf..c6b791ed9674a 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -1,10 +1,10 @@ error[E0277]: the trait bound `CantParam: Eq` is not satisfied - --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:43 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:37 | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ the trait `Eq` is not implemented for `CantParam` +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ the trait `Eq` is not implemented for `CantParam` | -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL help: consider annotating `CantParam` with `#[derive(Eq)]` | @@ -13,26 +13,26 @@ LL | struct CantParam(ImplementsConstParamTy); | error[E0277]: the type `CantParam` does not `#[derive(PartialEq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:43 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:37 | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ unsatisfied trait bound +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ unsatisfied trait bound | help: the trait `StructuralPartialEq` is not implemented for `CantParam` --> $DIR/const_param_ty_impl_no_structural_eq.rs:8:1 | LL | struct CantParam(ImplementsConstParamTy); | ^^^^^^^^^^^^^^^^ -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL error[E0277]: the trait bound `CantParamDerive: Eq` is not satisfied --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `CantParamDerive` +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `CantParamDerive` | -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL help: consider annotating `CantParamDerive` with `#[derive(Eq)]` | @@ -43,15 +43,15 @@ LL | struct CantParamDerive(ImplementsConstParamTy); error[E0277]: the type `CantParamDerive` does not `#[derive(PartialEq)]` --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound | help: the trait `StructuralPartialEq` is not implemented for `CantParamDerive` --> $DIR/const_param_ty_impl_no_structural_eq.rs:17:1 | LL | struct CantParamDerive(ImplementsConstParamTy); | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs index 236b3bc162aaa..0c9b12805f757 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs @@ -12,10 +12,10 @@ impl PartialEq for Union { } impl Eq for Union {} -impl std::marker::UnsizedConstParamTy for Union {} +impl std::marker::ConstParamTy_ for Union {} //~^ ERROR the trait `ConstParamTy` may not be implemented for this type -#[derive(std::marker::UnsizedConstParamTy)] +#[derive(std::marker::ConstParamTy)] //~^ ERROR this trait cannot be derived for unions union UnionDerive { a: u8, diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr index 837c289c9248f..cc2147b49aea5 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr @@ -1,14 +1,14 @@ error: this trait cannot be derived for unions --> $DIR/const_param_ty_impl_union.rs:18:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: the trait `ConstParamTy` may not be implemented for this type - --> $DIR/const_param_ty_impl_union.rs:15:43 + --> $DIR/const_param_ty_impl_union.rs:15:37 | -LL | impl std::marker::UnsizedConstParamTy for Union {} - | ^^^^^ type is not a structure or enumeration +LL | impl std::marker::ConstParamTy_ for Union {} + | ^^^^^ type is not a structure or enumeration error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs index 34ea143d25462..d42ef90e1b180 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs @@ -5,17 +5,14 @@ use std::marker::ConstParamTy; #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo([*const u8; 1]); #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo2([*mut u8; 1]); #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo3([fn(); 1]); fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr index 6b8d2394a8618..442ec6b96cefa 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr @@ -3,91 +3,46 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo([*const u8; 1]); | -------------- this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:9:12 + --> $DIR/nested_bad_const_param_ty.rs:8:12 | LL | struct Foo([*const u8; 1]); | ^^^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:11:10 + --> $DIR/nested_bad_const_param_ty.rs:10:10 | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo2([*mut u8; 1]); | ------------ this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:14:13 + --> $DIR/nested_bad_const_param_ty.rs:12:13 | LL | struct Foo2([*mut u8; 1]); | ^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:16:10 + --> $DIR/nested_bad_const_param_ty.rs:14:10 | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo3([fn(); 1]); | --------- this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:19:13 + --> $DIR/nested_bad_const_param_ty.rs:16:13 | LL | struct Foo3([fn(); 1]); | ^^^^^^^^^ -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:6:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo([*const u8; 1]); - | -------------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:9:12 - | -LL | struct Foo([*const u8; 1]); - | ^^^^^^^^^^^^^^ - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:11:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo2([*mut u8; 1]); - | ------------ this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:14:13 - | -LL | struct Foo2([*mut u8; 1]); - | ^^^^^^^^^^^^ - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:16:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo3([fn(); 1]); - | --------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:19:13 - | -LL | struct Foo3([fn(); 1]); - | ^^^^^^^^^ - -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr index b13f76eabadbc..72dfda50ea5ca 100644 --- a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr @@ -9,11 +9,13 @@ help: you might be missing a const parameter LL | impl Wrapper<{ bar() }> { | +++++++++++++++++++++++ -error[E0741]: using function pointers as const generic parameters is forbidden +error: using function pointers as const generic parameters is forbidden --> $DIR/non_valtreeable_const_arg-2.rs:8:25 | LL | struct Wrapper; | ^^^^ + | + = note: the only supported types are integers, `bool`, and `char` error[E0599]: the function or associated item `call` exists for struct `Wrapper`, but its trait bounds were not satisfied --> $DIR/non_valtreeable_const_arg-2.rs:17:26 @@ -35,5 +37,5 @@ note: the trait `Fn` must be implemented error: aborting due to 3 previous errors -Some errors have detailed explanations: E0425, E0599, E0741. +Some errors have detailed explanations: E0425, E0599. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs index a1ee1c4cdd585..937acf2e6bb63 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs @@ -1,11 +1,11 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; struct Foo; -impl UnsizedConstParamTy for &'static Foo {} +impl ConstParamTy_ for &'static Foo {} //~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr index 5ca8e6c75167f..b977cf5d44e09 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr @@ -1,8 +1,8 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/reference_pointee_is_const_param-1.rs:8:30 + --> $DIR/reference_pointee_is_const_param-1.rs:8:24 | -LL | impl UnsizedConstParamTy for &'static Foo {} - | ^^^^^^^^^^^^ this field does not implement `ConstParamTy_` +LL | impl ConstParamTy_ for &'static Foo {} + | ^^^^^^^^^^^^ this field does not implement `ConstParamTy_` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs index ac1b522f469e1..605fed26c9c3c 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs @@ -3,12 +3,12 @@ // Regression test for #119299 -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; #[derive(Eq, PartialEq)] struct ConstStrU(*const u8, usize); -impl UnsizedConstParamTy for &'static ConstStrU {} +impl ConstParamTy_ for &'static ConstStrU {} //~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type impl ConstStrU { diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr index 5e5f6cc642d33..0fe92f253a05e 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr @@ -1,8 +1,8 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/reference_pointee_is_const_param-2.rs:11:30 + --> $DIR/reference_pointee_is_const_param-2.rs:11:24 | -LL | impl UnsizedConstParamTy for &'static ConstStrU {} - | ^^^^^^^^^^^^^^^^^^ this field does not implement `ConstParamTy_` +LL | impl ConstParamTy_ for &'static ConstStrU {} + | ^^^^^^^^^^^^^^^^^^ this field does not implement `ConstParamTy_` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs index b0934508399d5..0fe86adb291dd 100644 --- a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs +++ b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs @@ -1,11 +1,11 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; trait Trait {} -impl UnsizedConstParamTy for dyn Trait {} +impl ConstParamTy_ for dyn Trait {} //~^ ERROR: the trait `ConstParamTy` may not be implemented for this type fn foo() {} diff --git a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr index 9933ba6e335b7..67c6314e2975b 100644 --- a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr +++ b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr @@ -1,8 +1,8 @@ error: the trait `ConstParamTy` may not be implemented for this type - --> $DIR/trait_objects_as_a_const_generic.rs:8:30 + --> $DIR/trait_objects_as_a_const_generic.rs:8:24 | -LL | impl UnsizedConstParamTy for dyn Trait {} - | ^^^^^^^^^ type is not a structure or enumeration +LL | impl ConstParamTy_ for dyn Trait {} + | ^^^^^^^^^ type is not a structure or enumeration error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.rs b/tests/ui/const-generics/adt_const_params/unsized_field-1.rs index f6e5bd6e355da..5db031cb900fc 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.rs +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.rs @@ -14,7 +14,10 @@ struct A([u8]); struct B(&'static [u8]); #[derive(ConstParamTy, Eq, PartialEq)] -//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type struct C(unsized_const_param::Foo); +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] +//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type +struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr index 3089b30bd76d1..a5ae5c726da86 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr @@ -6,6 +6,12 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct A([u8]); | ---- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `[u8]` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:10:10 + | +LL | struct A([u8]); + | ^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/unsized_field-1.rs:12:10 @@ -15,15 +21,27 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct B(&'static [u8]); | ------------- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `&'static [u8]` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:14:10 + | +LL | struct B(&'static [u8]); + | ^^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsized_field-1.rs:16:10 + --> $DIR/unsized_field-1.rs:19:10 | -LL | #[derive(ConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | struct C(unsized_const_param::Foo); - | ------------------------ this field does not implement `ConstParamTy_` +LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + | ---------------------------------------------------------- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:21:10 + | +LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-2.rs b/tests/ui/const-generics/adt_const_params/unsized_field-2.rs deleted file mode 100644 index e4a3a481b4e37..0000000000000 --- a/tests/ui/const-generics/adt_const_params/unsized_field-2.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ aux-build:unsized_const_param.rs -#![feature(adt_const_params, unsized_const_params)] -//~^ WARN: the feature `unsized_const_params` is incomplete - -extern crate unsized_const_param; - -#[derive(std::marker::ConstParamTy, Eq, PartialEq)] -//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type -struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] -struct B(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - -fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr deleted file mode 100644 index cef70ca0463bd..0000000000000 --- a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/unsized_field-2.rs:2:30 - | -LL | #![feature(adt_const_params, unsized_const_params)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #95174 for more information - = note: `#[warn(incomplete_features)]` on by default - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsized_field-2.rs:7:10 - | -LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - | ---------------------------------------------------------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `&'static [u8]: ConstParamTy_` - --> $DIR/unsized_field-2.rs:9:10 - | -LL | struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs index 311f507d3c7f4..500e8e22b0e3d 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs @@ -7,7 +7,6 @@ use std::marker::ConstParamTy; #[derive(Debug, PartialEq, Eq, ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` -//~| ERROR the trait `ConstParamTy_` struct Foo { nested: &'static Bar, //~^ ERROR the size for values diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr index 992a27c1c0e90..d4aeb91999c6b 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -1,17 +1,17 @@ error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` @@ -26,34 +26,25 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] ... LL | nested: &'static Bar, | ----------------------------------------- this field does not implement `ConstParamTy_` - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsizing-wfcheck-issue-126272.rs:8:32 | -LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | nested: &'static Bar, - | ----------------------------------------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Eq` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): ConstParamTy_` + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Sized` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Eq` + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): UnsizedConstParamTy` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Sized` + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ----- in this derive macro expansion @@ -64,7 +55,7 @@ LL | nested: &'static Bar, = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` = help: the trait `Debug` is implemented for `Bar` note: required for `Bar<(dyn Debug + 'static)>` to implement `Debug` - --> $DIR/unsizing-wfcheck-issue-126272.rs:20:10 + --> $DIR/unsizing-wfcheck-issue-126272.rs:19:10 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ^^^^^ @@ -75,7 +66,7 @@ LL | struct Bar(T); = note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug` error[E0369]: binary operation `==` cannot be applied to type `&Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | --------- in this derive macro expansion @@ -84,7 +75,7 @@ LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `dyn Debug: Eq` is not satisfied - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion @@ -94,7 +85,7 @@ LL | nested: &'static Bar, | = help: the trait `Eq` is implemented for `Bar` note: required for `Bar` to implement `Eq` - --> $DIR/unsizing-wfcheck-issue-126272.rs:20:28 + --> $DIR/unsizing-wfcheck-issue-126272.rs:19:28 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ^^ unsatisfied trait bound introduced in this `derive` macro @@ -104,7 +95,7 @@ note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion @@ -114,12 +105,12 @@ LL | nested: &'static Bar, | = help: the trait `Sized` is not implemented for `dyn Debug` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` @@ -127,26 +118,26 @@ LL | struct Bar(T); | this could be changed to `T: ?Sized`... error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:26:33 + --> $DIR/unsizing-wfcheck-issue-126272.rs:25:33 | LL | let x: Test<{ Foo { nested: &Bar(4) } }> = Test; | ^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` | | | this could be changed to `T: ?Sized`... -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0204, E0277, E0369. For more information about an error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.rs b/tests/ui/const-generics/const-param-with-additional-obligations.rs index 98097e86c7dfe..5110f95d5bf9a 100644 --- a/tests/ui/const-generics/const-param-with-additional-obligations.rs +++ b/tests/ui/const-generics/const-param-with-additional-obligations.rs @@ -1,14 +1,14 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; #[derive(Eq, PartialEq)] struct Foo(T); trait Other {} -impl UnsizedConstParamTy for Foo where T: Other + UnsizedConstParamTy {} +impl ConstParamTy_ for Foo where T: Other + ConstParamTy_ {} fn foo>() {} //~^ ERROR `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter diff --git a/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr b/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr index 4ea323f4a5c85..1eacc28b2625e 100644 --- a/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr +++ b/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr @@ -4,7 +4,7 @@ error[E0741]: `[T; N]` can't be used as a const parameter type LL | struct UsesType(PhantomData); | ^^^^^^ | - = note: `T` must implement `UnsizedConstParamTy`, but it does not + = note: `T` must implement `ConstParamTy_`, but it does not error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issue-66451.rs b/tests/ui/const-generics/issue-66451.rs index 0b8693e0e675d..9ce7658586ad1 100644 --- a/tests/ui/const-generics/issue-66451.rs +++ b/tests/ui/const-generics/issue-66451.rs @@ -1,15 +1,15 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy; -#[derive(Debug, PartialEq, Eq, UnsizedConstParamTy)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Foo { value: i32, nested: &'static Bar, } -#[derive(Debug, PartialEq, Eq, UnsizedConstParamTy)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Bar(T); struct Test; diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr index 8ea96428deb3b..1301ca92f015c 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -4,7 +4,7 @@ error[E0741]: `&'static (dyn A + 'static)` can't be used as a const parameter ty LL | fn test() { | ^^^^^^^^^^^^^^ | - = note: `(dyn A + 'static)` must implement `UnsizedConstParamTy`, but it does not + = note: `(dyn A + 'static)` must implement `ConstParamTy_`, but it does not error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr b/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr index bcb2bd255dab9..d970b12df6a1d 100644 --- a/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr +++ b/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr @@ -1,14 +1,22 @@ -error[E0741]: `&'static str` can't be used as a const parameter type - --> $DIR/slice-const-param-mismatch.rs:8:29 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/slice-const-param-mismatch.rs:8:20 | LL | struct ConstString; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static str` to implement `ConstParamTy_` -error[E0741]: `&'static [u8]` can't be used as a const parameter type - --> $DIR/slice-const-param-mismatch.rs:11:28 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/slice-const-param-mismatch.rs:11:19 | LL | struct ConstBytes; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static [u8]` to implement `ConstParamTy_` error[E0308]: mismatched types --> $DIR/slice-const-param-mismatch.rs:17:35 @@ -45,5 +53,5 @@ LL | let _: ConstBytes = ConstBytes::; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0741. +Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/slice-const-param.rs b/tests/ui/const-generics/slice-const-param.rs index 1c5088b528355..8b5e934149e42 100644 --- a/tests/ui/const-generics/slice-const-param.rs +++ b/tests/ui/const-generics/slice-const-param.rs @@ -12,7 +12,7 @@ pub fn function_with_bytes() -> &'static [u8] { } // Also check the codepaths for custom DST -#[derive(std::marker::UnsizedConstParamTy, PartialEq, Eq)] +#[derive(std::marker::ConstParamTy, PartialEq, Eq)] struct MyStr(str); fn function_with_my_str() -> &'static MyStr { diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr b/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr index 7a936ced0305a..c2351c707d7bf 100644 --- a/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr +++ b/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr @@ -1,9 +1,13 @@ -error[E0741]: `&'static ()` can't be used as a const parameter type - --> $DIR/transmute-const-param-static-reference.rs:9:23 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/transmute-const-param-static-reference.rs:9:14 | LL | struct Const; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static ()` to implement `ConstParamTy_` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.rs b/tests/ui/const-generics/transmute-const-param-static-reference.rs index 0b47fd31eaf69..bf164bdadb0e0 100644 --- a/tests/ui/const-generics/transmute-const-param-static-reference.rs +++ b/tests/ui/const-generics/transmute-const-param-static-reference.rs @@ -8,7 +8,7 @@ struct Const; //[min]~^ ERROR `&'static ()` is forbidden as the type of a const generic parameter -//[adt_const_params]~^^ ERROR `&'static ()` can't be used as a const parameter type +//[adt_const_params]~^^ ERROR use of unstable library feature `unsized_const_params` fn main() { const A: &'static () = unsafe { std::mem::transmute(10 as *const ()) }; diff --git a/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs b/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs index 359126f125167..429c401af1f55 100644 --- a/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs +++ b/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs @@ -1,13 +1,13 @@ //@ check-pass //@ compile-flags: -Csymbol-mangling-version=v0 #![allow(incomplete_features)] -#![feature(unsized_const_params)] +#![feature(adt_const_params, unsized_const_params)] // Regression test for #116303 #[derive(PartialEq, Eq)] struct MyStr(str); -impl std::marker::UnsizedConstParamTy for MyStr {} +impl std::marker::ConstParamTy_ for MyStr {} fn function_with_my_str() -> &'static MyStr { S diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs index d088d382377cf..56eebbd53c914 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs @@ -1,6 +1,8 @@ -struct Foo; -//~^ ERROR: `[u8]` is forbidden as the type of a const generic parameter -//~| HELP: add `#![feature(adt_const_params)]` to the crate -//~| HELP: add `#![feature(unsized_const_params)]` to the crate +#![feature(adt_const_params)] + +struct Bar(u8); + +struct Foo; +//~^ ERROR: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr index 85ca2f59cb639..d4f7d45ea323f 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr @@ -1,18 +1,15 @@ -error: `[u8]` is forbidden as the type of a const generic parameter - --> $DIR/feature-gate-unsized-const-params.rs:1:21 +error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/feature-gate-unsized-const-params.rs:5:21 | -LL | struct Foo; - | ^^^^ +LL | struct Foo; + | ^^^ | - = note: the only supported types are integers, `bool`, and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types +help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct | -LL + #![feature(adt_const_params)] - | -help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait - | -LL + #![feature(unsized_const_params)] +LL - struct Bar(u8); +LL + #[derive(ConstParamTy, PartialEq, Eq)] | error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/layout/thaw-transmute-invalid-enum.rs b/tests/ui/layout/thaw-transmute-invalid-enum.rs index a7c2e1a86de7b..20fc8d463594b 100644 --- a/tests/ui/layout/thaw-transmute-invalid-enum.rs +++ b/tests/ui/layout/thaw-transmute-invalid-enum.rs @@ -9,6 +9,7 @@ mod assert { where Dst: TransmuteFrom, //~^ ERROR: use of unstable library feature `transmutability` + //~^^ ERROR: use of unstable library feature `transmutability` { } } diff --git a/tests/ui/layout/thaw-transmute-invalid-enum.stderr b/tests/ui/layout/thaw-transmute-invalid-enum.stderr index d12fc4694e0ab..2b89159c26336 100644 --- a/tests/ui/layout/thaw-transmute-invalid-enum.stderr +++ b/tests/ui/layout/thaw-transmute-invalid-enum.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Subset` in this scope - --> $DIR/thaw-transmute-invalid-enum.rs:34:41 + --> $DIR/thaw-transmute-invalid-enum.rs:35:41 | LL | assert::is_transmutable::(); | ^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | fn test() { | ++++++++ error[E0517]: attribute should be applied to a struct or union - --> $DIR/thaw-transmute-invalid-enum.rs:21:11 + --> $DIR/thaw-transmute-invalid-enum.rs:22:11 | LL | #[repr(C, packed(2))] | ^^^^^^^^^ @@ -50,8 +50,19 @@ LL | Dst: TransmuteFrom, = help: add `#![feature(transmutability)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: use of unstable library feature `transmutability` + --> $DIR/thaw-transmute-invalid-enum.rs:10:14 + | +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(transmutability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +note: required by a bound in `TransmuteFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL + error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union - --> $DIR/thaw-transmute-invalid-enum.rs:29:9 + --> $DIR/thaw-transmute-invalid-enum.rs:30:9 | LL | a: Ox00, | ^^^^^^^ @@ -62,7 +73,7 @@ help: wrap the field type in `ManuallyDrop<...>` LL | a: std::mem::ManuallyDrop, | +++++++++++++++++++++++ + -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0412, E0517, E0658, E0740. For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.rs b/tests/ui/symbol-names/const-generics-structural-demangling.rs index 06e3ce51fa6aa..0b4af61f99101 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.rs +++ b/tests/ui/symbol-names/const-generics-structural-demangling.rs @@ -6,7 +6,7 @@ #![feature(adt_const_params, unsized_const_params, decl_macro, rustc_attrs)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy; pub struct RefByte; @@ -42,7 +42,7 @@ pub struct TupleByteBool; //~| ERROR demangling-alt(>) impl TupleByteBool<{ (1, false) }> {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] pub enum MyOption { Some(T), None, @@ -66,7 +66,7 @@ impl OptionUsize<{ MyOption::None }> {} //~| ERROR demangling-alt(::Some(0)}>>) impl OptionUsize<{ MyOption::Some(0) }> {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] pub struct Foo { s: &'static str, ch: char, @@ -83,7 +83,7 @@ impl Foo_<{ Foo { s: "abc", ch: 'x', slice: &[1, 2, 3] } }> {} // NOTE(eddyb) this tests specifically the use of disambiguators in field names, // using macros 2.0 hygiene to create a `struct` with conflicting field names. macro duplicate_field_name_test($x:ident) { - #[derive(PartialEq, Eq, UnsizedConstParamTy)] + #[derive(PartialEq, Eq, ConstParamTy)] pub struct Bar { $x: u8, x: u16, From 1c30399ceae2400e266660a8f98a7504c284fda1 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 09:02:12 +0000 Subject: [PATCH 0426/1900] Add documentation for select_where_possible and select_all_or_error --- compiler/rustc_infer/src/traits/engine.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 9a66bd0574c9d..4a252719694d5 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -72,12 +72,27 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.register_predicate_obligation(infcx, obligation); } } - + /// Go over the list of pending obligations and try to evaluate them. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: leave the obligation in the list to be evaluated later + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Err. #[must_use] fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; + /// Evaluate all pending obligations, return error if they can't be evaluated. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: remove the obligation from the list and return an error + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Ambiguous or Err. #[must_use] fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { let errors = self.select_where_possible(infcx); From 9405e76431374e25077b374ed0cd9c920a1c0f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 02:53:29 -0700 Subject: [PATCH 0427/1900] Detect attempt to use var-args in closure ``` error: unexpected `...` --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 | LL | let mut lol = |...| (); | ^^^ not a valid pattern | = note: C-variadic type `...` is not allowed here ``` --- compiler/rustc_parse/messages.ftl | 1 + compiler/rustc_parse/src/errors.rs | 4 ++- compiler/rustc_parse/src/parser/pat.rs | 27 +++++++++++++------ .../varargs-in-closure-isnt-supported.rs | 11 ++++++++ .../varargs-in-closure-isnt-supported.stderr | 22 +++++++++++++++ 5 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.rs create mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 72cd75f6d8943..6d9521c7d2be0 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -189,6 +189,7 @@ parse_dotdotdot = unexpected token: `...` parse_dotdotdot_rest_pattern = unexpected `...` .label = not a valid pattern .suggestion = for a rest pattern, use `..` instead of `...` + .note = C-variadic type `...` is not allowed here parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 00ca5acd84d51..2b107fe35d592 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2723,7 +2723,9 @@ pub(crate) struct DotDotDotRestPattern { #[label] pub span: Span, #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] - pub suggestion: Span, + pub suggestion: Option, + #[note] + pub var_args: Option<()>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c4d30b3d32832..fda19d62bc774 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -756,7 +756,7 @@ impl<'a> Parser<'a> { self.bump(); // `..` PatKind::Rest } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) { - self.recover_dotdotdot_rest_pat(lo) + self.recover_dotdotdot_rest_pat(lo, expected) } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. } else if self.eat(exp!(Bang)) { @@ -886,16 +886,27 @@ impl<'a> Parser<'a> { /// Recover from a typoed `...` pattern that was encountered /// Ref: Issue #70388 - fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { + fn recover_dotdotdot_rest_pat(&mut self, lo: Span, expected: Option) -> PatKind { // A typoed rest pattern `...`. self.bump(); // `...` - // The user probably mistook `...` for a rest pattern `..`. - self.dcx().emit_err(DotDotDotRestPattern { - span: lo, - suggestion: lo.with_lo(lo.hi() - BytePos(1)), - }); - PatKind::Rest + if let Some(Expected::ParameterName) = expected { + // We have `...` in a closure argument, likely meant to be var-arg, which aren't + // supported in closures (#146489). + PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: None, + var_args: Some(()), + })) + } else { + // The user probably mistook `...` for a rest pattern `..`. + self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: Some(lo.with_lo(lo.hi() - BytePos(1))), + var_args: None, + }); + PatKind::Rest + } } /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs new file mode 100644 index 0000000000000..5dff69194f90e --- /dev/null +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.rs @@ -0,0 +1,11 @@ +// var-args are not supported in closures, ensure we don't misdirect people (#146489) +#![feature(c_variadic)] + +unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { + let mut lol = |...| (); //~ ERROR: unexpected `...` + unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here + //~| ERROR: type annotations needed + //~| NOTE: not a valid pattern +} + +fn main() {} diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr new file mode 100644 index 0000000000000..4f66ff59af127 --- /dev/null +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr @@ -0,0 +1,22 @@ +error: unexpected `...` + --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 + | +LL | let mut lol = |...| (); + | ^^^ not a valid pattern + | + = note: C-variadic type `...` is not allowed here + +error[E0282]: type annotations needed + --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 + | +LL | let mut lol = |...| (); + | ^^^ + | +help: consider giving this closure parameter an explicit type + | +LL | let mut lol = |...: /* Type */| (); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. From 0e290e4228e90720b4b3e263f95250db7046cdd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 03:06:16 -0700 Subject: [PATCH 0428/1900] Silence inference error on `PatKind::Err` --- .../src/error_reporting/infer/need_type_info.rs | 10 ++++++++-- .../closures/varargs-in-closure-isnt-supported.rs | 1 - .../varargs-in-closure-isnt-supported.stderr | 14 +------------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index edab530590b2a..94772be16be20 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir as hir; +use rustc_hir::{self as hir, PatKind}; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; @@ -512,7 +512,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { type_name: ty_to_string(self, ty, def_id), }); } - InferSourceKind::ClosureArg { insert_span, ty } => { + InferSourceKind::ClosureArg { insert_span, ty, .. } => { infer_subdiags.push(SourceKindSubdiag::LetLike { span: insert_span, name: String::new(), @@ -652,6 +652,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), }; *err.long_ty_path() = long_ty_path; + if let InferSourceKind::ClosureArg { kind: PatKind::Err(_), .. } = kind { + // We will have already emitted an error about this pattern. + err.downgrade_to_delayed_bug(); + } err } } @@ -673,6 +677,7 @@ enum InferSourceKind<'tcx> { ClosureArg { insert_span: Span, ty: Ty<'tcx>, + kind: PatKind<'tcx>, }, GenericArg { insert_span: Span, @@ -1197,6 +1202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { kind: InferSourceKind::ClosureArg { insert_span: param.pat.span.shrink_to_hi(), ty: param_ty, + kind: param.pat.kind, }, }) } diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs index 5dff69194f90e..4de78bef14d30 100644 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.rs +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.rs @@ -4,7 +4,6 @@ unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { let mut lol = |...| (); //~ ERROR: unexpected `...` unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here - //~| ERROR: type annotations needed //~| NOTE: not a valid pattern } diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr index 4f66ff59af127..a645741a52771 100644 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr @@ -6,17 +6,5 @@ LL | let mut lol = |...| (); | = note: C-variadic type `...` is not allowed here -error[E0282]: type annotations needed - --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 - | -LL | let mut lol = |...| (); - | ^^^ - | -help: consider giving this closure parameter an explicit type - | -LL | let mut lol = |...: /* Type */| (); - | ++++++++++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. From a84c8deb1f61e020e026e1fa53bc613426da8fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 03:10:27 -0700 Subject: [PATCH 0429/1900] Fix existing test --- tests/ui/c-variadic/no-closure.rs | 3 +-- tests/ui/c-variadic/no-closure.stderr | 18 +++--------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs index c0b77786e8b4a..a5b791fbca800 100644 --- a/tests/ui/c-variadic/no-closure.rs +++ b/tests/ui/c-variadic/no-closure.rs @@ -8,8 +8,7 @@ const F: extern "C" fn(...) = |_: ...| {}; fn foo() { let f = |...| {}; - //~^ ERROR: `..` patterns are not allowed here - //~| ERROR: unexpected `...` + //~^ ERROR: unexpected `...` let f = |_: ...| {}; //~^ ERROR C-variadic type `...` may not be nested inside another type diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index 77bd106bb9519..ad635a29ab486 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -10,26 +10,14 @@ error: unexpected `...` LL | let f = |...| {}; | ^^^ not a valid pattern | -help: for a rest pattern, use `..` instead of `...` - | -LL - let f = |...| {}; -LL + let f = |..| {}; - | + = note: C-variadic type `...` is not allowed here error[E0743]: C-variadic type `...` may not be nested inside another type - --> $DIR/no-closure.rs:14:17 + --> $DIR/no-closure.rs:13:17 | LL | let f = |_: ...| {}; | ^^^ -error: `..` patterns are not allowed here - --> $DIR/no-closure.rs:10:14 - | -LL | let f = |...| {}; - | ^^^ - | - = note: only allowed in tuple, tuple struct, and slice patterns - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0743`. From dcc62e7e58083dabec40464e4b1bcc9788b70ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 12:32:03 +0200 Subject: [PATCH 0430/1900] Only run Cranelift dist test on nightly --- tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs index 198f8d1bc10c1..6d12ab1e4cf6f 100644 --- a/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs +++ b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs @@ -2,6 +2,7 @@ // dist artifacts. //@ only-dist +//@ only-nightly (cranelift is not stable yet) //@ only-x86_64-unknown-linux-gnu //@ compile-flags: -Z codegen-backend=cranelift //@ run-pass From c92dee244dcca49c1eed99a768749e433f17e112 Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Wed, 10 Sep 2025 11:27:36 +0100 Subject: [PATCH 0431/1900] Add relnotes 1.90 --- RELEASES.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 33abe45ce4629..2b65d070d5fd1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,129 @@ +Version 1.90 (2025-09-18) +========================== + +
+ +Language +-------- +- [Split up the `unknown_or_malformed_diagnostic_attributes` lint](https://github.com/rust-lang/rust/pull/140717). This lint has been split up into four finer-grained lints, with `unknown_or_malformed_diagnostic_attributes` now being the lint group that contains these lints: + 1. `unknown_diagnostic_attributes`: unknown to the current compiler + 2. `misplaced_diagnostic_attributes`: placed on the wrong item + 3. `malformed_diagnostic_attributes`: malformed attribute syntax or options + 4. `malformed_diagnostic_format_literals`: malformed format string literal +- [Allow constants whose final value has references to mutable/external memory, but reject such constants as patterns](https://github.com/rust-lang/rust/pull/140942) +- [Allow volatile access to non-Rust memory, including address 0](https://github.com/rust-lang/rust/pull/141260) + + + + +Compiler +-------- +- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525). +- [Tier 3 `musl` targets now link dynamically by default](https://github.com/rust-lang/rust/pull/144410). Affected targets: + - `mips64-unknown-linux-muslabi64` + - `powerpc64-unknown-linux-musl` + - `powerpc-unknown-linux-musl` + - `powerpc-unknown-linux-muslspe` + - `riscv32gc-unknown-linux-musl` + - `s390x-unknown-linux-musl` + - `thumbv7neon-unknown-linux-musleabihf` + + + + +Platform Support +---------------- +- [Demote `x86_64-apple-darwin` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/145252) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- [Stabilize `u*::{checked,overflowing,saturating,wrapping}_sub_signed`](https://github.com/rust-lang/rust/issues/126043) +- [Allow comparisons between `CStr`, `CString`, and `Cow`](https://github.com/rust-lang/rust/pull/137268) +- [Remove some unsized tuple impls since unsized tuples can't be constructed](https://github.com/rust-lang/rust/pull/138340) +- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005) +- [`proc_macro::Ident::new` now supports `$crate`.](https://github.com/rust-lang/rust/pull/141996) +- [Guarantee the pointer returned from `Thread::into_raw` has at least 8 bytes of alignment](https://github.com/rust-lang/rust/pull/143859) + + + + +Stabilized APIs +--------------- + +- [`u{n}::checked_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.checked_sub_signed) +- [`u{n}::overflowing_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.overflowing_sub_signed) +- [`u{n}::saturating_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.saturating_sub_signed) +- [`u{n}::wrapping_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.wrapping_sub_signed) +- [`impl Copy for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Copy-for-IntErrorKind) +- [`impl Hash for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Hash-for-IntErrorKind) +- [`impl PartialEq<&CStr> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3C%26CStr%3E-for-CStr) +- [`impl PartialEq for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCString%3E-for-CStr) +- [`impl PartialEq> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CStr) +- [`impl PartialEq<&CStr> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3C%26CStr%3E-for-CString) +- [`impl PartialEq for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCStr%3E-for-CString) +- [`impl PartialEq> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CString) +- [`impl PartialEq<&CStr> for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3C%26CStr%3E-for-Cow%3C'_,+CStr%3E) +- [`impl PartialEq for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCStr%3E-for-Cow%3C'_,+CStr%3E) +- [`impl PartialEq for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCString%3E-for-Cow%3C'_,+CStr%3E) + + +These previously stable APIs are now stable in const contexts: + +- [`<[T]>::reverse`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.reverse) +- [`f32::floor`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.floor) +- [`f32::ceil`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.ceil) +- [`f32::trunc`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.trunc) +- [`f32::fract`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.fract) +- [`f32::round`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round) +- [`f32::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round_ties_even) +- [`f64::floor`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.floor) +- [`f64::ceil`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.ceil) +- [`f64::trunc`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.trunc) +- [`f64::fract`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.fract) +- [`f64::round`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round) +- [`f64::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round_ties_even) + + + + +Cargo +----- +- [Add `http.proxy-cainfo` config for proxy certs](https://github.com/rust-lang/cargo/pull/15374/) +- [Use `gix` for `cargo package`](https://github.com/rust-lang/cargo/pull/15534/) +- [feat(publish): Stabilize multi-package publishing](https://github.com/rust-lang/cargo/pull/15636/) + + + +Rustdoc +----- +- [Add ways to collapse all impl blocks](https://github.com/rust-lang/rust/pull/141663). Previously the "Summary" button and "-" keyboard shortcut would never collapse `impl` blocks, now they do when shift is held +- [Display unsafe attributes with `unsafe()` wrappers](https://github.com/rust-lang/rust/pull/143662) + + + + +Compatibility Notes +------------------- +- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525). + See also . +- [Make `core::iter::Fuse`'s `Default` impl construct `I::default()` internally as promised in the docs instead of always being empty](https://github.com/rust-lang/rust/pull/140985) +- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005) + This may change program behavior but results in the same behavior as other primitives (e.g., stdout, network sockets). + Programs relying on signals to terminate them should update handling of sockets to handle errors on write by exiting. +- [On Unix `std::env::home_dir` will use the fallback if the `HOME` environment variable is empty](https://github.com/rust-lang/rust/pull/141840) +- We now [reject unsupported `extern "{abi}"`s consistently in all positions](https://github.com/rust-lang/rust/pull/142134). This primarily affects the use of implementing traits on an `extern "{abi}"` function pointer, like `extern "stdcall" fn()`, on a platform that doesn't support that, like aarch64-unknown-linux-gnu. Direct usage of these unsupported ABI strings by declaring or defining functions was already rejected, so this is only a change for consistency. +- [const-eval: error when initializing a static writes to that static](https://github.com/rust-lang/rust/pull/143084) +- [Check that the `proc_macro_derive` macro has correct arguments when applied to the crate root](https://github.com/rust-lang/rust/pull/143607) + + Version 1.89.0 (2025-08-07) ========================== From 53b58b3afdc1d3b069c8b73230abf68878196f75 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Mon, 15 Sep 2025 14:31:43 +0200 Subject: [PATCH 0432/1900] tests/run-make: Update list of statically linked musl targets All of the tier 3 targets in the list now link dynamically by default (except mips64el-unknown-linux-muslabi64, I overlooked that one). Adjust the list of targets expected to link statically accordingly. Signed-off-by: Jens Reidel --- tests/run-make/musl-default-linking/rmake.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/run-make/musl-default-linking/rmake.rs b/tests/run-make/musl-default-linking/rmake.rs index 7bb54e2739c9d..1b30c538b5e30 100644 --- a/tests/run-make/musl-default-linking/rmake.rs +++ b/tests/run-make/musl-default-linking/rmake.rs @@ -4,7 +4,7 @@ use run_make_support::{rustc, serde_json}; // Per https://github.com/rust-lang/compiler-team/issues/422, // we should be trying to move these targets to dynamically link // musl libc by default. -//@ needs-llvm-components: aarch64 arm mips powerpc riscv systemz x86 +//@ needs-llvm-components: aarch64 arm mips powerpc x86 static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "aarch64-unknown-linux-musl", "arm-unknown-linux-musleabi", @@ -14,16 +14,8 @@ static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "armv7-unknown-linux-musleabihf", "i586-unknown-linux-musl", "i686-unknown-linux-musl", - "mips64-unknown-linux-musl", - "mips64-unknown-linux-muslabi64", "mips64el-unknown-linux-muslabi64", - "powerpc-unknown-linux-musl", - "powerpc-unknown-linux-muslspe", - "powerpc64-unknown-linux-musl", "powerpc64le-unknown-linux-musl", - "riscv32gc-unknown-linux-musl", - "s390x-unknown-linux-musl", - "thumbv7neon-unknown-linux-musleabihf", "x86_64-unknown-linux-musl", ]; From 7299e8fc4db506bb5ec3990e22b611c45df2f13d Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 14:15:29 +0000 Subject: [PATCH 0433/1900] Fix feature gate tests --- .../feature-gate-adt_const_params.rs | 6 +++++- .../feature-gate-adt_const_params.stderr | 12 ++++-------- .../feature-gate-unsized-const-params.rs | 10 ++++------ .../feature-gate-unsized-const-params.stderr | 19 +++++++++++-------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.rs b/tests/ui/feature-gates/feature-gate-adt_const_params.rs index 8a3bcf25963f1..7efa529c55791 100644 --- a/tests/ui/feature-gates/feature-gate-adt_const_params.rs +++ b/tests/ui/feature-gates/feature-gate-adt_const_params.rs @@ -1,2 +1,6 @@ -struct Foo; //~ ERROR `&'static str` is forbidden +struct Bar(u8); + +struct Foo; +//~^ ERROR: `Bar` is forbidden as the type of a const generic parameter + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr index 18d514f8cb5f8..7ea91a8f4c2bf 100644 --- a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr +++ b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr @@ -1,18 +1,14 @@ -error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/feature-gate-adt_const_params.rs:1:24 +error: `Bar` is forbidden as the type of a const generic parameter + --> $DIR/feature-gate-adt_const_params.rs:3:21 | -LL | struct Foo; - | ^^^^^^^^^^^^ +LL | struct Foo; + | ^^^ | = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] | -help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait - | -LL + #![feature(unsized_const_params)] - | error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs index 56eebbd53c914..d088d382377cf 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs @@ -1,8 +1,6 @@ -#![feature(adt_const_params)] - -struct Bar(u8); - -struct Foo; -//~^ ERROR: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter +struct Foo; +//~^ ERROR: `[u8]` is forbidden as the type of a const generic parameter +//~| HELP: add `#![feature(adt_const_params)]` to the crate +//~| HELP: add `#![feature(unsized_const_params)]` to the crate fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr index d4f7d45ea323f..85ca2f59cb639 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr @@ -1,15 +1,18 @@ -error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter - --> $DIR/feature-gate-unsized-const-params.rs:5:21 +error: `[u8]` is forbidden as the type of a const generic parameter + --> $DIR/feature-gate-unsized-const-params.rs:1:21 | -LL | struct Foo; - | ^^^ +LL | struct Foo; + | ^^^^ | -help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct + = note: the only supported types are integers, `bool`, and `char` +help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | -LL - struct Bar(u8); -LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL + #![feature(adt_const_params)] + | +help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait + | +LL + #![feature(unsized_const_params)] | error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0741`. From 9cbf1a5502b81399d522a4224cb012df7e1166a6 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sat, 23 Aug 2025 19:20:38 +0200 Subject: [PATCH 0434/1900] Split `FnCtxt::report_args_error` into subfunctions --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 4208 +++++++++-------- 1 file changed, 2282 insertions(+), 1926 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 94b635c41b4b2..22bd7e1d23e15 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use std::{fmt, iter, mem}; use itertools::Itertools; @@ -7,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; @@ -565,358 +566,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error - let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, - _, - ) => { - if let Res::Def(DefKind::Ctor(of, _), _) = - self.typeck_results.borrow().qpath_res(qpath, *hir_id) - { - let name = match of { - CtorOf::Struct => "struct", - CtorOf::Variant => "enum variant", - }; - (call_span, None, *span, name, false) - } else { - (call_span, None, *span, "function", false) - } - } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => { - (call_span, None, *span, "function", false) - } - hir::ExprKind::MethodCall(path_segment, _, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - (*span, Some(path_segment.ident), ident_span, "method", true) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - - // Don't print if it has error types or is just plain `_` - fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> bool { - tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) - } - - let tcx = self.tcx; - - // Get the argument span in the context of the call span so that - // suggestions and labels are (more) correct when an arg is a - // macro invocation. - let normalize_span = |span: Span| -> Span { - let normalized_span = span.find_ancestor_inside_same_ctxt(error_span).unwrap_or(span); - // Sometimes macros mess up the spans, so do not normalize the - // arg span to equal the error span, because that's less useful - // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(error_span) { span } else { normalized_span } - }; - - // Precompute the provided types and spans, since that's all we typically need for below - let provided_arg_tys: IndexVec, Span)> = provided_args - .iter() - .map(|expr| { - let ty = self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| Ty::new_misc_error(tcx)); - (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) - }) - .collect(); - let callee_expr = match &call_expr.peel_blocks().kind { - hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, receiver, ..) => { - if let Some((DefKind::AssocFn, def_id)) = - self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) - && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.is_method() - { - Some(*receiver) - } else { - None - } - } - _ => None, - }; - let callee_ty = callee_expr - .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); - - // Obtain another method on `Self` that have similar name. - let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> { - if let Some(callee_ty) = callee_ty - && let Ok(Some(assoc)) = self.probe_op( - call_name.span, - MethodCall, - Some(call_name), - None, - IsSuggestion(true), - callee_ty.peel_refs(), - callee_expr.unwrap().hir_id, - TraitsInScope, - |mut ctxt| ctxt.probe_for_similar_candidate(), - ) - && assoc.is_method() - { - let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id); - let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args); - - self.instantiate_binder_with_fresh_vars( - call_name.span, - BoundRegionConversionTime::FnCall, - fn_sig, - ); - } - None - }; - - let suggest_confusable = |err: &mut Diag<'_>| { - let Some(call_name) = call_ident else { - return; - }; - let Some(callee_ty) = callee_ty else { - return; - }; - let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); - // Check for other methods in the following order - // - methods marked as `rustc_confusables` with the provided arguments - // - methods with the same argument type/count and short levenshtein distance - // - methods marked as `rustc_confusables` (done) - // - methods with short levenshtein distance - - // Look for commonly confusable method names considering arguments. - if let Some(_name) = self.confusable_method_name( - err, - callee_ty.peel_refs(), - call_name, - Some(input_types.clone()), - ) { - return; - } - // Look for method names with short levenshtein distance, considering arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..] - .iter() - .zip(input_types.iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == input_types.len() - { - let assoc_name = assoc.name(); - err.span_suggestion_verbose( - call_name.span, - format!("you might have meant to use `{}`", assoc_name), - assoc_name, - Applicability::MaybeIncorrect, - ); - return; - } - // Look for commonly confusable method names disregarding arguments. - if let Some(_name) = - self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) - { - return; - } - // Look for similarly named methods with levenshtein distance with the right - // number of arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..].len() == input_types.len() - { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but the arguments don't match", - assoc.name(), - ), - ); - return; - } - // Fallthrough: look for similarly named methods with levenshtein distance. - if let Some((assoc, _)) = similar_assoc(call_name) { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but their argument count \ - doesn't match", - assoc.name(), - ), - ); - return; - } - }; - // A "softer" version of the `demand_compatible`, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { - if provided_idx.as_usize() == expected_idx.as_usize() { - return compatibility_diagonal[provided_idx].clone(); - } - - let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if (formal_input_ty, expected_input_ty).references_error() { - return Compatibility::Incompatible(None); - } - - let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; - - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.may_coerce(arg_ty, coerced_ty); - if !can_coerce { - return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( - ty::error::ExpectedFound::new(coerced_ty, arg_ty), - ))); - } - - // Using probe here, since we don't want this subtyping to affect inference. - let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env) - .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) - .err() - }); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let references_error = (coerced_ty, arg_ty).references_error(); - match (references_error, subtyping_error) { - (false, None) => Compatibility::Compatible, - (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), - } - }; - let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { - let mismatched_ty = if expected_ty == provided_ty { - // If expected == provided, then we must have failed to sup - // the formal type. Avoid printing out "expected Ty, found Ty" - // in that case. - formal_ty - } else { - expected_ty - }; - TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) - }; - - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid - - // First, let's find the errors - let (mut errors, matched_inputs) = - ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) - .find_errors(); + let mut fn_call_diag_ctxt = FnCallDiagCtxt::new( + self, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); // First, check if we just need to wrap some arguments in a tuple. - if let Some((mismatch_idx, terr)) = - compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { - Some((i, *terr)) - } else { - None - } - }) - { - // Is the first bad expected argument a tuple? - // Do we have as many extra provided arguments as the tuple's length? - // If so, we might have just forgotten to wrap some args in a tuple. - if let Some(ty::Tuple(tys)) = - formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) - // If the tuple is unit, we're not actually wrapping any arguments. - && !tys.is_empty() - && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() - { - // Wrap up the N provided arguments starting at this position in a tuple. - let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..]; - let (provided_args_to_tuple, provided_args_after_tuple) = - provided_args_to_tuple.split_at(tys.len()); - let provided_as_tuple = - Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty)); - - let mut satisfied = true; - // Check if the newly wrapped tuple + rest of the arguments are compatible. - for ((_, expected_ty), provided_ty) in std::iter::zip( - formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), - [provided_as_tuple] - .into_iter() - .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), - ) { - if !self.may_coerce(provided_ty, *expected_ty) { - satisfied = false; - break; - } - } + if let Some(err) = fn_call_diag_ctxt.check_wrap_args_in_tuple() { + return err; + } - // If they're compatible, suggest wrapping in an arg, and we're done! - // Take some care with spans, so we don't suggest wrapping a macro's - // innards in parenthesis, for example. - if satisfied - && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple - { - let mut err; - if tys.len() == 1 { - // A tuple wrap suggestion actually occurs within, - // so don't do anything special here. - err = self.err_ctxt().report_and_explain_type_error( - mk_trace( - lo, - formal_and_expected_inputs[mismatch_idx.to_expected_idx()], - provided_arg_tys[mismatch_idx].0, - ), - self.param_env, - terr, - ); - err.span_label( - full_call_span, - format!("arguments to this {call_name} are incorrect"), - ); - } else { - err = self.dcx().struct_span_err( - full_call_span, - format!( - "{call_name} takes {}{} but {} {} supplied", - if c_variadic { "at least " } else { "" }, - potentially_plural_count( - formal_and_expected_inputs.len(), - "argument" - ), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ); - err.code(err_code.to_owned()); - err.multipart_suggestion_verbose( - "wrap these arguments in parentheses to construct a tuple", - vec![ - (lo.shrink_to_lo(), "(".to_string()), - (hi.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - }; - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - None, - Some(mismatch_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - return err.emit(); - } - } + if let Some(fallback_error) = fn_call_diag_ctxt.ensure_has_errors() { + return fallback_error; } // Okay, so here's where it gets complicated in regards to what errors @@ -926,1067 +596,423 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 2) Valid but incorrect arguments // 3) Invalid arguments // - Currently I think this only comes up with `CyclicTy` - // + // We first need to go through, remove those from (3) and emit those // as their own error, particularly since they're error code and // message is special. From what I can tell, we *must* emit these // here (vs somewhere prior to this function) since the arguments // become invalid *because* of how they get used in the function. // It is what it is. - - if errors.is_empty() { - if cfg!(debug_assertions) { - span_bug!(error_span, "expected errors from argument matrix"); - } else { - let mut err = - self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); - suggest_confusable(&mut err); - return err.emit(); - } + if let Some(err) = fn_call_diag_ctxt.filter_out_invalid_arguments() + && fn_call_diag_ctxt.errors.is_empty() + { + // We're done if we found errors, but we already emitted them. + return err; } - let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { - if let ty::Adt(adt, _) = ty.kind() - && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) - && let hir::ExprKind::Struct( - hir::QPath::LangItem(hir::LangItem::RangeFull, _), - [], - _, - ) = expr.kind - { - // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax - // from default field values, which is not supported on tuples. - let explanation = if self.tcx.features().default_field_values() { - "this is only supported on non-tuple struct literals" - } else if self.tcx.sess.is_nightly_build() { - "this is only supported on non-tuple struct literals when \ - `#![feature(default_field_values)]` is enabled" - } else { - "this is not supported" - }; - let msg = format!( - "you might have meant to use `..` to skip providing a value for \ - expected fields, but {explanation}; it is instead interpreted as a \ - `std::ops::RangeFull` literal", - ); - err.span_help(expr.span, msg); - } - }; - - let mut reported = None; - errors.retain(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = - error - else { - return true; - }; - let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let trace = - mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { - let mut err = - self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *e); - suggest_confusable(&mut err); - reported = Some(err.emit()); - return false; - } - true - }); + assert!(!fn_call_diag_ctxt.errors.is_empty()); - // We're done if we found errors, but we already emitted them. - if let Some(reported) = reported - && errors.is_empty() - { - return reported; + // Last special case: if there is only one "Incompatible" error, just emit that + if let Some(err) = fn_call_diag_ctxt.check_single_incompatible() { + return err; } - assert!(!errors.is_empty()); // Okay, now that we've emitted the special errors separately, we // are only left missing/extra/swapped and mismatched arguments, both // can be collated pretty easily if needed. - // Next special case: if there is only one "Incompatible" error, just emit that - if let &[ - Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), - ] = &errors[..] - { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx]; - let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); + // Special case, we found an extra argument is provided, which is very common in practice. + // but there is a obviously better removing suggestion compared to the current one, + // try to find the argument with Error type, if we removed it all the types will become good, + // then we will replace the current suggestion. + fn_call_diag_ctxt.maybe_optimize_extra_arg_suggestion(); - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); + let mut err = fn_call_diag_ctxt.initial_final_diagnostic(); + fn_call_diag_ctxt.suggest_confusable(&mut err); - if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind - && provided_idx.as_usize() == expected_idx.as_usize() - { - self.note_source_of_type_mismatch_constraint( - &mut err, - rcvr, - crate::demand::TypeMismatchSource::Arg { - call_expr, - incompatible_arg: provided_idx.as_usize(), - }, - ); - } + // As we encounter issues, keep track of what we want to provide for the suggestion. - self.suggest_ptr_null_mut( - expected_ty, - provided_ty, - provided_args[provided_idx], - &mut err, - ); + let (mut suggestions, labels, suggestion_text) = + fn_call_diag_ctxt.labels_and_suggestion_text(&mut err); - self.suggest_deref_unwrap_or( - &mut err, - callee_ty, - call_ident, - expected_ty, - provided_ty, - provided_args[provided_idx], - is_method, - ); + fn_call_diag_ctxt.label_generic_mismatches(&mut err); + fn_call_diag_ctxt.append_arguments_changes(&mut suggestions); - // Call out where the function is defined - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - Some(expected_ty), - Some(expected_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); - return err.emit(); + // If we have less than 5 things to say, it would be useful to call out exactly what's wrong + if labels.len() <= 5 { + for (span, label) in labels { + err.span_label(span, label); + } } - // Special case, we found an extra argument is provided, which is very common in practice. - // but there is a obviously better removing suggestion compared to the current one, - // try to find the argument with Error type, if we removed it all the types will become good, - // then we will replace the current suggestion. - if let [Error::Extra(provided_idx)] = &errors[..] { - let remove_idx_is_perfect = |idx: usize| -> bool { - let removed_arg_tys = provided_arg_tys - .iter() - .enumerate() - .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) - .collect::>(); - std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( - |((expected_ty, _), (provided_ty, _))| { - !provided_ty.references_error() - && self.may_coerce(*provided_ty, *expected_ty) - }, - ) - }; + // Call out where the function is defined + fn_call_diag_ctxt.label_fn_like( + &mut err, + fn_def_id, + fn_call_diag_ctxt.callee_ty, + call_expr, + None, + None, + &fn_call_diag_ctxt.matched_inputs, + &fn_call_diag_ctxt.formal_and_expected_inputs, + fn_call_diag_ctxt.call_metadata.is_method, + tuple_arguments, + ); - if !remove_idx_is_perfect(provided_idx.as_usize()) { - if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) { - errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; - } - } + // And add a suggestion block for all of the parameters + if let Some(suggestion_message) = + FnCallDiagCtxt::format_suggestion_text(&mut err, suggestions, suggestion_text) + && !fn_call_diag_ctxt.call_is_in_macro() + { + let (suggestion_span, suggestion_code) = fn_call_diag_ctxt.suggestion_code(); + + err.span_suggestion_verbose( + suggestion_span, + suggestion_message, + suggestion_code, + Applicability::HasPlaceholders, + ); } - let mut err = if formal_and_expected_inputs.len() == provided_args.len() { - struct_span_code_err!( - self.dcx(), - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - self.dcx() - .struct_span_err( - full_call_span, - format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ) - .with_code(err_code.to_owned()) - }; + err.emit() + } - suggest_confusable(&mut err); - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, + fn suggest_ptr_null_mut( + &self, + expected_ty: Ty<'tcx>, + provided_ty: Ty<'tcx>, + arg: &hir::Expr<'tcx>, + err: &mut Diag<'_>, + ) { + if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind() + && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind() + && let hir::ExprKind::Call(callee, _) = arg.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind + && let Res::Def(_, def_id) = path.res + && self.tcx.get_diagnostic_item(sym::ptr_null) == Some(def_id) + { + // The user provided `ptr::null()`, but the function expects + // `ptr::null_mut()`. + err.subdiagnostic(SuggestPtrNullMut { span: arg.span }); } - let mut suggestion_text = SuggestionText::None; + } - let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| { - if ty.is_unit() { - "()".to_string() - } else if ty.is_suggestable(tcx, false) { - 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 = - matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(Some(arg)) = - self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) - && arg.name != kw::SelfLower - { - format!("/* {} */", arg.name) - } else { - "/* value */".to_string() - } - }; + // AST fragment checking + pub(in super::super) fn check_expr_lit( + &self, + lit: &hir::Lit, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; - let mut errors = errors.into_iter().peekable(); - let mut only_extras_so_far = errors - .peek() - .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); - let mut prev_extra_idx = None; - let mut suggestions = vec![]; - while let Some(error) = errors.next() { - only_extras_so_far &= matches!(error, Error::Extra(_)); + match lit.node { + ast::LitKind::Str(..) => Ty::new_static_str(tcx), + ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + Ty::new_array(tcx, tcx.types.u8, v.as_byte_str().len() as u64), + ), + ast::LitKind::Byte(_) => tcx.types.u8, + ast::LitKind::Char(_) => tcx.types.char, + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, t), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, t), + ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Int(_) | ty::Uint(_) => Some(ty), + // These exist to direct casts like `0x61 as char` to use + // the right integer type to cast from, instead of falling back to + // i32 due to no further constraints. + ty::Char => Some(tcx.types.u8), + ty::RawPtr(..) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), + &ty::Pat(base, _) if base.is_integral() => { + let layout = tcx + .layout_of(self.typing_env(self.param_env).as_query_input(ty)) + .ok()?; + assert!(!layout.uninhabited); - match error { - Error::Invalid(provided_idx, expected_idx, compatibility) => { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = compatibility { - let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); - if let Some(e) = error { - self.err_ctxt().note_type_err( - &mut err, - &trace.cause, - None, - Some(self.param_env.and(trace.values)), - e, - true, - None, - ); + match layout.backend_repr { + rustc_abi::BackendRepr::Scalar(scalar) => { + scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty) + } + _ => unreachable!(), } } + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_int_var()) + } + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => Ty::new_float(tcx, t), + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Float(_) => Some(ty), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_float_var()) + } + ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::CStr(_, _) => Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, lit.span)).skip_binder(), + ), + ast::LitKind::Err(guar) => Ty::new_error(tcx, guar), + } + } - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); + pub(crate) fn check_struct_path( + &self, + qpath: &QPath<'tcx>, + hir_id: HirId, + ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { + let path_span = qpath.span(); + let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let variant = match def { + Res::Err => { + let guar = + self.dcx().span_delayed_bug(path_span, "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(guar); + return Err(guar); + } + Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { + Some(adt) => { + Some((adt.variant_of_res(def), adt.did(), Self::user_args_for_adt(ty))) } - Error::Extra(arg_idx) => { - 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}`") - } else { - "".to_string() - }; - let idx = if provided_arg_tys.len() == 1 { - "".to_string() - } else { - format!(" #{}", arg_idx.as_usize() + 1) - }; - labels.push(( - provided_span, - format!("unexpected argument{idx}{provided_ty_name}"), - )); - let mut span = provided_span; - if span.can_be_used_for_suggestions() - && error_span.can_be_used_for_suggestions() - { - if arg_idx.index() > 0 - && let Some((_, prev)) = - provided_arg_tys.get(ProvidedIdx::from_usize(arg_idx.index() - 1)) - { - // Include previous comma - span = prev.shrink_to_hi().to(span); - } - - // Is last argument for deletion in a row starting from the 0-th argument? - // Then delete the next comma, so we are not left with `f(, ...)` - // - // fn f() {} - // - f(0, 1,) - // + f() - let trim_next_comma = match errors.peek() { - Some(Error::Extra(provided_idx)) - if only_extras_so_far - && provided_idx.index() > arg_idx.index() + 1 => - // If the next Error::Extra ("next") doesn't next to current ("current"), - // fn foo(_: (), _: u32) {} - // - foo("current", (), 1u32, "next") - // + foo((), 1u32) - // If the previous error is not a `Error::Extra`, then do not trim the next comma - // - foo((), "current", 42u32, "next") - // + foo((), 42u32) - { - prev_extra_idx.is_none_or(|prev_extra_idx| { - prev_extra_idx + 1 == arg_idx.index() - }) - } - // If no error left, we need to delete the next comma - None if only_extras_so_far => true, - // Not sure if other error type need to be handled as well - _ => false, - }; + _ => bug!("unexpected type: {:?}", ty.normalized), + }, + Res::Def( + DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, + _, + ) + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { + Some(adt) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did(), Self::user_args_for_adt(ty))) + } + _ => None, + }, + _ => bug!("unexpected definition: {:?}", def), + }; - if trim_next_comma { - let next = provided_arg_tys - .get(arg_idx + 1) - .map(|&(_, sp)| sp) - .unwrap_or_else(|| { - // Try to move before `)`. Note that `)` here is not necessarily - // the latin right paren, it could be a Unicode-confusable that - // looks like a `)`, so we must not use `- BytePos(1)` - // manipulations here. - self.tcx().sess.source_map().end_point(call_expr.span) - }); + if let Some((variant, did, ty::UserArgs { args, user_self_ty })) = variant { + debug!("check_struct_path: did={:?} args={:?}", did, args); - // Include next comma - span = span.until(next); - } + // Register type annotation. + self.write_user_type_annotation_from_args(hir_id, did, args, user_self_ty); - suggestions.push((span, String::new())); + // Check bounds on type arguments used in the path. + self.add_required_obligations_for_hir(path_span, did, args, hir_id); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - prev_extra_idx = Some(arg_idx.index()) - } - detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); + Ok((variant, ty.normalized)) + } else { + Err(match *ty.normalized.kind() { + ty::Error(guar) => { + // E0071 might be caused by a spelling error, which will have + // already caused an error message and probably a suggestion + // elsewhere. Refrain from emitting more unhelpful errors here + // (issue #88844). + guar } - Error::Missing(expected_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. + _ => struct_span_code_err!( + self.dcx(), + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.normalized.sort_string(self.tcx) + ) + .with_span_label(path_span, "not a struct") + .emit(), + }) + } + } - let mut missing_idxs = vec![expected_idx]; - while let Some(e) = errors.next_if(|e| { - matches!(e, Error::Missing(next_expected_idx) - if *next_expected_idx == *missing_idxs.last().unwrap() + 1) - }) { - match e { - Error::Missing(expected_idx) => missing_idxs.push(expected_idx), - _ => unreachable!( - "control flow ensures that we should always get an `Error::Missing`" - ), - } - } + fn check_decl_initializer( + &self, + hir_id: HirId, + pat: &'tcx hir::Pat<'tcx>, + init: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed + // for #42640 (default match binding modes). + // + // See #44848. + let ref_bindings = pat.contains_explicit_ref_binding(); - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[expected_idx] => { - let (_, input_ty) = formal_and_expected_inputs[expected_idx]; - let span = if let Some((_, arg_span)) = - provided_arg_tys.get(expected_idx.to_provided_idx()) - { - *arg_span - } else { - args_span - }; - let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{input_ty}`") - } else { - "".to_string() - }; - labels.push(( - span, - format!( - "argument #{}{rendered} is missing", - expected_idx.as_usize() + 1 - ), - )); + let local_ty = self.local_ty(init.span, hir_id); + if let Some(m) = ref_bindings { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the place it is referencing, and not some + // supertype thereof. + let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + if let Err(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { + self.emit_type_mismatch_suggestions( + &mut diag, + init.peel_drop_temps(), + init_ty, + local_ty, + None, + None, + ); + diag.emit(); + } + init_ty + } else { + self.check_expr_coercible_to_type(init, local_ty, None) + } + } - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let span = if let (Some((_, first_span)), Some((_, second_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(second_idx.to_provided_idx()), - ) { - first_span.to(*second_span) - } else { - args_span - }; - let rendered = - if !has_error_or_infer([first_expected_ty, second_expected_ty]) { - format!( - " of type `{first_expected_ty}` and `{second_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("two arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; - let span = if let (Some((_, first_span)), Some((_, third_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(third_idx.to_provided_idx()), - ) { - first_span.to(*third_span) - } else { - args_span - }; - let rendered = if !has_error_or_infer([ - first_expected_ty, - second_expected_ty, - third_expected_ty, - ]) { - format!( - " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("three arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if let (Some((_, first_span)), Some((_, last_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(last_idx.to_provided_idx()), - ) { - first_span.to(*last_span) - } else { - args_span - }; - labels.push((span, "multiple arguments are missing".to_string())); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - } - } - Error::Swap( - first_provided_idx, - second_provided_idx, - first_expected_idx, - second_expected_idx, - ) => { - 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}`") - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{first_expected_ty}`{first_provided_ty_name}"), - )); + pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) -> Ty<'tcx> { + // Determine and write the type which we'll check the pattern against. + let decl_ty = self.local_ty(decl.span, decl.hir_id); - 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}`") - } else { - String::new() - }; - labels.push(( - second_span, - format!("expected `{second_expected_ty}`{second_provided_ty_name}"), - )); + // Type check the initializer. + if let Some(ref init) = decl.init { + let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, init); + self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, init_ty); + } - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - 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}`") - } else { - String::new() - }; - labels.push(( - provided_span, - format!("expected `{expected_ty}`{provided_ty_name}"), - )); - } + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (decl.ty, decl.init) { + (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => { + (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + } // No explicit type; so use the scrutinee. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. + }; - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; - } + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin)); + let pat_ty = self.node_ty(decl.pat.hir_id); + self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); + + if let Some(blk) = decl.origin.try_get_else() { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_expr_block(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); } + self.diverges.set(previous_diverges); } + decl_ty + } - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); + /// Type check a `let` statement. + fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { + GatherLocalsVisitor::gather_from_local(self, local); - // Incorporate the argument changes in the removal suggestion. - // When a type is *missing*, and the rest are additional, we want to suggest these with a - // multipart suggestion, but in order to do so we need to figure out *where* the arg that - // was provided but had the wrong type should go, because when looking at `expected_idx` - // that is the position in the argument list in the definition, while `provided_idx` will - // not be present. So we have to look at what the *last* provided position was, and point - // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's - // probably a better more involved change we can make to make this work. - // For example, if we have - // ``` - // fn foo(i32, &'static str) {} - // foo((), (), ()); - // ``` - // what should be suggested is - // ``` - // foo(/* i32 */, /* &str */); - // ``` - // which includes the replacement of the first two `()` for the correct type, and the - // removal of the last `()`. - let mut prev = -1; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - // We want to point not at the *current* argument expression index, but rather at the - // index position where it *should have been*, which is *after* the previous one. - if let Some(provided_idx) = provided_idx { - prev = provided_idx.index() as i64; - continue; - } - let idx = ProvidedIdx::from_usize((prev + 1) as usize); - if let Some((_, arg_span)) = provided_arg_tys.get(idx) { - prev += 1; - // There is a type that was *not* found anywhere, so it isn't a move, but a - // replacement and we look at what type it should have been. This will allow us - // To suggest a multipart suggestion when encountering `foo(1, "")` where the def - // was `fn foo(())`. - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx))); - } + let ty = self.check_decl(local.into()); + self.write_ty(local.hir_id, ty); + if local.pat.is_never_pattern() { + self.diverges.set(Diverges::Always { + span: local.pat.span, + custom_note: Some("any code following a never pattern is unreachable"), + }); } + } - // If we have less than 5 things to say, it would be useful to call out exactly what's wrong - if labels.len() <= 5 { - for (span, label) in labels { - err.span_label(span, label); - } + fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { + // Don't do all the complex logic below for `DeclItem`. + match stmt.kind { + hir::StmtKind::Item(..) => return, + hir::StmtKind::Let(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} } - // Call out where the function is defined - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - None, - None, - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); + self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - err.multipart_suggestion_verbose( - format!("remove the extra argument{}", if plural { "s" } else { "" }), - suggestions, - Applicability::HasPlaceholders, - ); - None - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text - && !full_call_span.in_external_macro(self.sess().source_map()) - { - let source_map = self.sess().source_map(); - let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) { - // Span of the braces, e.g. `(a, b, c)`. - args_span - } else { - // The arg span of a function call that wasn't even given braces - // like what might happen with delegation reuse. - // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. - full_call_span.shrink_to_hi() - }; + // Hide the outer diverging flags. + let old_diverges = self.diverges.replace(Diverges::Maybe); - // Controls how the arguments should be listed in the suggestion. - enum ArgumentsFormatting { - SingleLine, - Multiline { fallback_indent: String, brace_indent: String }, + match stmt.kind { + hir::StmtKind::Let(l) => { + self.check_decl_local(l); } - let arguments_formatting = { - let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); - if let Some(brace_indent) = source_map.indentation_before(suggestion_span) - && let Some(first_idx) = provided_inputs.by_ref().next() - && let Some(last_idx) = provided_inputs.by_ref().next() - && let (_, first_span) = provided_arg_tys[first_idx] - && let (_, last_span) = provided_arg_tys[last_idx] - && source_map.is_multiline(first_span.to(last_span)) - && let Some(fallback_indent) = source_map.indentation_before(first_span) - { - ArgumentsFormatting::Multiline { fallback_indent, brace_indent } - } else { - ArgumentsFormatting::SingleLine - } - }; - - let mut suggestion = "(".to_owned(); - let mut needs_comma = false; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - if needs_comma { - suggestion += ","; - } - match &arguments_formatting { - ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", - ArgumentsFormatting::SingleLine => {} - ArgumentsFormatting::Multiline { .. } => suggestion += "\n", - } - needs_comma = true; - let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx - && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = source_map.span_to_snippet(provided_span) - { - (Some(provided_span), arg_text) - } else { - // Propose a placeholder of the correct type - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - (None, ty_to_snippet(expected_ty, expected_idx)) - }; - if let ArgumentsFormatting::Multiline { fallback_indent, .. } = - &arguments_formatting - { - let indent = suggestion_span - .and_then(|span| source_map.indentation_before(span)) - .unwrap_or_else(|| fallback_indent.clone()); - suggestion += &indent; - } - suggestion += &suggestion_text; + // Ignore for now. + hir::StmtKind::Item(_) => {} + hir::StmtKind::Expr(ref expr) => { + // Check with expected type of `()`. + self.check_expr_has_type_or_error(expr, self.tcx.types.unit, |err| { + if self.is_next_stmt_expr_continuation(stmt.hir_id) + && let hir::ExprKind::Match(..) | hir::ExprKind::If(..) = expr.kind + { + // We have something like `match () { _ => true } && true`. Suggest + // wrapping in parentheses. We find the statement or expression + // following the `match` (`&& true`) and see if it is something that + // can reasonably be interpreted as a binop following an expression. + err.multipart_suggestion( + "parentheses are required to parse this as an expression", + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + } else if expr.can_have_side_effects() { + self.suggest_semicolon_at_end(expr.span, err); + } + }); } - if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { - suggestion += ",\n"; - suggestion += &brace_indent; + hir::StmtKind::Semi(expr) => { + let ty = self.check_expr(expr); + self.check_place_expr_if_unsized(ty, expr); } - suggestion += ")"; - err.span_suggestion_verbose( - suggestion_span, - suggestion_text, - suggestion, - Applicability::HasPlaceholders, - ); } - err.emit() + // Combine the diverging and `has_error` flags. + self.diverges.set(self.diverges.get() | old_diverges); } - fn suggest_ptr_null_mut( - &self, - expected_ty: Ty<'tcx>, - provided_ty: Ty<'tcx>, - arg: &hir::Expr<'tcx>, - err: &mut Diag<'_>, - ) { - if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind() - && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind() - && let hir::ExprKind::Call(callee, _) = arg.kind - && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind - && let Res::Def(_, def_id) = path.res - && self.tcx.get_diagnostic_item(sym::ptr_null) == Some(def_id) - { - // The user provided `ptr::null()`, but the function expects - // `ptr::null_mut()`. - err.subdiagnostic(SuggestPtrNullMut { span: arg.span }); + pub(crate) fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { + let unit = self.tcx.types.unit; + let ty = self.check_expr_block(blk, ExpectHasType(unit)); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); } } - // AST fragment checking - pub(in super::super) fn check_expr_lit( + pub(in super::super) fn check_expr_block( &self, - lit: &hir::Lit, + blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let tcx = self.tcx; + // In some cases, blocks have just one exit, but other blocks + // can be targeted by multiple breaks. This can happen both + // with labeled blocks as well as when we desugar + // a `try { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) + }; - match lit.node { - ast::LitKind::Str(..) => Ty::new_static_str(tcx), - ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_static, - Ty::new_array(tcx, tcx.types.u8, v.as_byte_str().len() as u64), - ), - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, t), - ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Int(_) | ty::Uint(_) => Some(ty), - // These exist to direct casts like `0x61 as char` to use - // the right integer type to cast from, instead of falling back to - // i32 due to no further constraints. - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), - &ty::Pat(base, _) if base.is_integral() => { - let layout = tcx - .layout_of(self.typing_env(self.param_env).as_query_input(ty)) - .ok()?; - assert!(!layout.uninhabited); + let prev_diverges = self.diverges.get(); + let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - match layout.backend_repr { - rustc_abi::BackendRepr::Scalar(scalar) => { - scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty) - } - _ => unreachable!(), - } - } - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => Ty::new_float(tcx, t), - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::CStr(_, _) => Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_static, - tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, lit.span)).skip_binder(), - ), - ast::LitKind::Err(guar) => Ty::new_error(tcx, guar), - } - } - - pub(crate) fn check_struct_path( - &self, - qpath: &QPath<'tcx>, - hir_id: HirId, - ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { - let path_span = qpath.span(); - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - let guar = - self.dcx().span_delayed_bug(path_span, "`Res::Err` but no error emitted"); - self.set_tainted_by_errors(guar); - return Err(guar); - } - Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { - Some(adt) => { - Some((adt.variant_of_res(def), adt.did(), Self::user_args_for_adt(ty))) - } - _ => bug!("unexpected type: {:?}", ty.normalized), - }, - Res::Def( - DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, - _, - ) - | Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { - Some(adt) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did(), Self::user_args_for_adt(ty))) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, ty::UserArgs { args, user_self_ty })) = variant { - debug!("check_struct_path: did={:?} args={:?}", did, args); - - // Register type annotation. - self.write_user_type_annotation_from_args(hir_id, did, args, user_self_ty); - - // Check bounds on type arguments used in the path. - self.add_required_obligations_for_hir(path_span, did, args, hir_id); - - Ok((variant, ty.normalized)) - } else { - Err(match *ty.normalized.kind() { - ty::Error(guar) => { - // E0071 might be caused by a spelling error, which will have - // already caused an error message and probably a suggestion - // elsewhere. Refrain from emitting more unhelpful errors here - // (issue #88844). - guar - } - _ => struct_span_code_err!( - self.dcx(), - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.normalized.sort_string(self.tcx) - ) - .with_span_label(path_span, "not a struct") - .emit(), - }) - } - } - - fn check_decl_initializer( - &self, - hir_id: HirId, - pat: &'tcx hir::Pat<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, hir_id); - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - if let Err(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { - self.emit_type_mismatch_suggestions( - &mut diag, - init.peel_drop_temps(), - init_ty, - local_ty, - None, - None, - ); - diag.emit(); - } - init_ty - } else { - self.check_expr_coercible_to_type(init, local_ty, None) - } - } - - pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) -> Ty<'tcx> { - // Determine and write the type which we'll check the pattern against. - let decl_ty = self.local_ty(decl.span, decl.hir_id); - - // Type check the initializer. - if let Some(ref init) = decl.init { - let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, init); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => { - (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) - } // No explicit type; so use the scrutinee. - _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin)); - let pat_ty = self.node_ty(decl.pat.hir_id); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); - - if let Some(blk) = decl.origin.try_get_else() { - let previous_diverges = self.diverges.get(); - let else_ty = self.check_expr_block(blk, NoExpectation); - let cause = self.cause(blk.span, ObligationCauseCode::LetElse); - if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - } - self.diverges.set(previous_diverges); - } - decl_ty - } - - /// Type check a `let` statement. - fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { - GatherLocalsVisitor::gather_from_local(self, local); - - let ty = self.check_decl(local.into()); - self.write_ty(local.hir_id, ty); - if local.pat.is_never_pattern() { - self.diverges.set(Diverges::Always { - span: local.pat.span, - custom_note: Some("any code following a never pattern is unreachable"), - }); - } - } - - fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Let(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - - match stmt.kind { - hir::StmtKind::Let(l) => { - self.check_decl_local(l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(expr, self.tcx.types.unit, |err| { - if self.is_next_stmt_expr_continuation(stmt.hir_id) - && let hir::ExprKind::Match(..) | hir::ExprKind::If(..) = expr.kind - { - // We have something like `match () { _ => true } && true`. Suggest - // wrapping in parentheses. We find the statement or expression - // following the `match` (`&& true`) and see if it is something that - // can reasonably be interpreted as a binop following an expression. - err.multipart_suggestion( - "parentheses are required to parse this as an expression", - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - } else if expr.can_have_side_effects() { - self.suggest_semicolon_at_end(expr.span, err); - } - }); - } - hir::StmtKind::Semi(expr) => { - let ty = self.check_expr(expr); - self.check_place_expr_if_unsized(ty, expr); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - } - - pub(crate) fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.types.unit; - let ty = self.check_expr_block(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - pub(in super::super) fn check_expr_block( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for s in blk.stmts { - self.check_stmt(s); + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for s in blk.stmts { + self.check_stmt(s); } // check the tail expression **without** holding the @@ -2139,700 +1165,2030 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(prev_diverges); } - let ty = ctxt.coerce.unwrap().complete(self); - - self.write_ty(blk.hir_id, ty); + let ty = ctxt.coerce.unwrap().complete(self); + + self.write_ty(blk.hir_id, ty); + + ty + } + + fn parent_item_span(&self, id: HirId) -> Option { + let node = self.tcx.hir_node_by_def_id(self.tcx.hir_get_parent_item(id).def_id); + match node { + Node::Item(&hir::Item { kind: hir::ItemKind::Fn { body: body_id, .. }, .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { + let body = self.tcx.hir_body(body_id); + if let ExprKind::Block(block, _) = &body.value.kind { + return Some(block.span); + } + } + _ => {} + } + None + } + + /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail + /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors + /// when given code like the following: + /// ```text + /// if false { return 0i32; } else { 1u32 } + /// // ^^^^ point at this instead of the whole `if` expression + /// ``` + fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { + let check_in_progress = |elem: &hir::Expr<'_>| { + self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( + |_| match elem.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), + _ => elem.span, + }, + ) + }; + + if let hir::ExprKind::If(_, _, Some(el)) = expr.kind + && let Some(rslt) = check_in_progress(el) + { + return rslt; + } + + if let hir::ExprKind::Match(_, arms, _) = expr.kind { + let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); + if let Some(span) = iter.next() { + if iter.next().is_none() { + return span; + } + } + } + + expr.span + } + + fn overwrite_local_ty_if_err(&self, hir_id: HirId, pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>) { + if let Err(guar) = ty.error_reported() { + struct OverwritePatternsWithError { + pat_hir_ids: Vec, + } + impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError { + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + self.pat_hir_ids.push(p.hir_id); + hir::intravisit::walk_pat(self, p); + } + } + // Override the types everywhere with `err()` to avoid knock on errors. + let err = Ty::new_error(self.tcx, guar); + self.write_ty(hir_id, err); + self.write_ty(pat.hir_id, err); + let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] }; + hir::intravisit::walk_pat(&mut visitor, pat); + // Mark all the subpatterns as `{type error}` as well. This allows errors for specific + // subpatterns to be silenced. + for hir_id in visitor.pat_hir_ids { + self.write_ty(hir_id, err); + } + self.locals.borrow_mut().insert(hir_id, err); + self.locals.borrow_mut().insert(pat.hir_id, err); + } + } + + // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. + // The newly resolved definition is written into `type_dependent_defs`. + fn finish_resolving_struct_path( + &self, + qpath: &QPath<'tcx>, + path_span: Span, + hir_id: HirId, + ) -> (Res, LoweredTy<'tcx>) { + match *qpath { + QPath::Resolved(ref maybe_qself, path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); + let ty = self.lowerer().lower_resolved_ty_path( + self_ty, + path, + hir_id, + PermitVariants::Yes, + ); + (path.res, LoweredTy::from_raw(self, path_span, ty)) + } + QPath::TypeRelative(hir_self_ty, segment) => { + let self_ty = self.lower_ty(hir_self_ty); + + let result = self.lowerer().lower_type_relative_ty_path( + self_ty.raw, + hir_self_ty, + segment, + hir_id, + path_span, + PermitVariants::Yes, + ); + let ty = result + .map(|(ty, _, _)| ty) + .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); + let ty = LoweredTy::from_raw(self, path_span, ty); + let result = result.map(|(_, kind, def_id)| (kind, def_id)); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + + (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) + } + QPath::LangItem(lang_item, span) => { + let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id); + (res, LoweredTy::from_raw(self, path_span, ty)) + } + } + } + + /// Given a vector of fulfillment errors, try to adjust the spans of the + /// errors to more accurately point at the cause of the failure. + /// + /// This applies to calls, methods, and struct expressions. This will also + /// try to deduplicate errors that are due to the same cause but might + /// have been created with different [`ObligationCause`][traits::ObligationCause]s. + pub(super) fn adjust_fulfillment_errors_for_expr_obligation( + &self, + errors: &mut Vec>, + ) { + // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that + // other errors that have the same span and predicate can also get fixed, + // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. + // This is important since if we adjust one span but not the other, then + // we will have "duplicated" the error on the UI side. + let mut remap_cause = FxIndexSet::default(); + let mut not_adjusted = vec![]; + + for error in errors { + let before_span = error.obligation.cause.span; + if self.adjust_fulfillment_error_for_expr_obligation(error) + || before_span != error.obligation.cause.span + { + remap_cause.insert(( + before_span, + error.obligation.predicate, + error.obligation.cause.clone(), + )); + } else { + // If it failed to be adjusted once around, it may be adjusted + // via the "remap cause" mapping the second time... + not_adjusted.push(error); + } + } + + // Adjust any other errors that come from other cause codes, when these + // errors are of the same predicate as one we successfully adjusted, and + // when their spans overlap (suggesting they're due to the same root cause). + // + // This is because due to normalization, we often register duplicate + // obligations with misc obligations that are basically impossible to + // line back up with a useful WhereClauseInExpr. + for error in not_adjusted { + for (span, predicate, cause) in &remap_cause { + if *predicate == error.obligation.predicate + && span.contains(error.obligation.cause.span) + { + error.obligation.cause = cause.clone(); + continue; + } + } + } + } + + fn label_fn_like( + &self, + err: &mut Diag<'_>, + callable_def_id: Option, + callee_ty: Option>, + call_expr: &'tcx hir::Expr<'tcx>, + expected_ty: Option>, + // A specific argument should be labeled, instead of all of them + expected_idx: Option, + matched_inputs: &IndexVec>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, + is_method: bool, + tuple_arguments: TupleArgumentsFlag, + ) { + let Some(mut def_id) = callable_def_id else { + return; + }; + + // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly + // (eg invoking a closure) we want to point at the underlying callable, + // not the method implicitly invoked (eg call_once). + // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) + if tuple_arguments == TupleArguments + && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // Since this is an associated item, it might point at either an impl or a trait item. + // We want it to always point to the trait item. + // If we're pointing at an inherent function, we don't need to do anything, + // so we fetch the parent and verify if it's a trait item. + && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) + && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) + // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" + && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) + && let Some(callee_ty) = callee_ty + { + let callee_ty = callee_ty.peel_refs(); + match *callee_ty.kind() { + ty::Param(param) => { + let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx); + if param.kind.is_synthetic() { + // if it's `impl Fn() -> ..` then just fall down to the def-id based logic + def_id = param.def_id; + } else { + // Otherwise, find the predicate that makes this generic callable, + // and point at that. + let instantiated = self + .tcx + .explicit_predicates_of(self.body_id) + .instantiate_identity(self.tcx); + // FIXME(compiler-errors): This could be problematic if something has two + // fn-like predicates with different args, but callable types really never + // do that, so it's OK. + for (predicate, span) in instantiated { + if let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() + && pred.self_ty().peel_refs() == callee_ty + && self.tcx.is_fn_trait(pred.def_id()) + { + err.span_note(span, "callable defined here"); + return; + } + } + } + } + ty::Alias(ty::Opaque, ty::AliasTy { def_id: new_def_id, .. }) + | ty::Closure(new_def_id, _) + | ty::FnDef(new_def_id, _) => { + def_id = new_def_id; + } + _ => { + // Look for a user-provided impl of a `Fn` trait, and point to it. + let new_def_id = self.probe(|_| { + let trait_ref = ty::TraitRef::new( + self.tcx, + self.tcx.fn_trait_kind_to_def_id(call_kind)?, + [callee_ty, self.next_ty_var(DUMMY_SP)], + ); + let obligation = traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + match SelectionContext::new(self).select(&obligation) { + Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { + Some(impl_source.impl_def_id) + } + _ => None, + } + }); + if let Some(new_def_id) = new_def_id { + def_id = new_def_id; + } else { + return; + } + } + } + } + + if let Some(def_span) = self.tcx.def_ident_span(def_id) + && !def_span.is_dummy() + { + let mut spans: MultiSpan = def_span.into(); + if let Some((params_with_generics, hir_generics)) = + self.get_hir_param_info(def_id, is_method) + { + struct MismatchedParam<'a> { + idx: ExpectedIdx, + generic: GenericIdx, + param: &'a FnParam<'a>, + deps: SmallVec<[ExpectedIdx; 4]>, + } + + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // Gather all mismatched parameters with generics. + let mut mismatched_params = Vec::>::new(); + if let Some(expected_idx) = expected_idx { + let expected_idx = ExpectedIdx::from_usize(expected_idx); + let &(expected_generic, ref expected_param) = + ¶ms_with_generics[expected_idx]; + if let Some(expected_generic) = expected_generic { + mismatched_params.push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }); + } else { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + } else { + mismatched_params.extend( + params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( + |((idx, &(generic, ref param)), matched_idx)| { + if matched_idx.is_some() { + None + } else if let Some(generic) = generic { + Some(MismatchedParam { + idx, + generic, + param, + deps: SmallVec::new(), + }) + } else { + // Still mark mismatched parameters + spans.push_span_label(param.span(), ""); + None + } + }, + ), + ); + } + + if !mismatched_params.is_empty() { + // For each mismatched parameter, create a two-way link to each matched parameter + // of the same type. + let mut dependants = IndexVec::::from_fn_n( + |_| SmallVec::<[u32; 4]>::new(), + params_with_generics.len(), + ); + let mut generic_uses = IndexVec::::from_fn_n( + |_| SmallVec::<[ExpectedIdx; 4]>::new(), + hir_generics.params.len(), + ); + for (idx, param) in mismatched_params.iter_mut().enumerate() { + for ((other_idx, &(other_generic, _)), &other_matched_idx) in + params_with_generics.iter_enumerated().zip(matched_inputs) + { + if other_generic == Some(param.generic) && other_matched_idx.is_some() { + generic_uses[param.generic].extend([param.idx, other_idx]); + dependants[other_idx].push(idx as u32); + param.deps.push(other_idx); + } + } + } + + // Highlight each mismatched type along with a note about which other parameters + // the type depends on (if any). + for param in &mismatched_params { + if let Some(deps_list) = listify(¶m.deps, |&dep| { + params_with_generics[dep].1.display(dep.as_usize()).to_string() + }) { + spans.push_span_label( + param.param.span(), + format!( + "this parameter needs to match the {} type of {deps_list}", + self.resolve_vars_if_possible( + formal_and_expected_inputs[param.deps[0]].1 + ) + .sort_string(self.tcx), + ), + ); + } else { + // Still mark mismatched parameters + spans.push_span_label(param.param.span(), ""); + } + } + // Highlight each parameter being depended on for a generic type. + for ((&(_, param), deps), &(_, expected_ty)) in + params_with_generics.iter().zip(&dependants).zip(formal_and_expected_inputs) + { + if let Some(deps_list) = listify(deps, |&dep| { + let param = &mismatched_params[dep as usize]; + param.param.display(param.idx.as_usize()).to_string() + }) { + spans.push_span_label( + param.span(), + format!( + "{deps_list} need{} to match the {} type of this parameter", + pluralize!((deps.len() != 1) as u32), + self.resolve_vars_if_possible(expected_ty) + .sort_string(self.tcx), + ), + ); + } + } + // Highlight each generic parameter in use. + for (param, uses) in hir_generics.params.iter().zip(&mut generic_uses) { + uses.sort(); + uses.dedup(); + if let Some(param_list) = listify(uses, |&idx| { + params_with_generics[idx].1.display(idx.as_usize()).to_string() + }) { + spans.push_span_label( + param.span, + format!( + "{param_list} {} reference this parameter `{}`", + if uses.len() == 2 { "both" } else { "all" }, + param.name.ident().name, + ), + ); + } + } + } + } + err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); + } else if let Some(hir::Node::Expr(e)) = self.tcx.hir_get_if_local(def_id) + && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind + { + let param = expected_idx + .and_then(|expected_idx| self.tcx.hir_body(*body).params.get(expected_idx)); + let (kind, span) = if let Some(param) = param { + // Try to find earlier invocations of this closure to find if the type mismatch + // is because of inference. If we find one, point at them. + let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] }; + let parent_def_id = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id; + match self.tcx.hir_node_by_def_id(parent_def_id) { + hir::Node::Item(item) => call_finder.visit_item(item), + hir::Node::TraitItem(item) => call_finder.visit_trait_item(item), + hir::Node::ImplItem(item) => call_finder.visit_impl_item(item), + _ => {} + } + let typeck = self.typeck_results.borrow(); + for (rcvr, args) in call_finder.calls { + if rcvr.hir_id.owner == typeck.hir_owner + && let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id) + && let ty::Closure(call_def_id, _) = rcvr_ty.kind() + && def_id == *call_def_id + && let Some(idx) = expected_idx + && let Some(arg) = args.get(idx) + && let Some(arg_ty) = typeck.node_type_opt(arg.hir_id) + && let Some(expected_ty) = expected_ty + && self.can_eq(self.param_env, arg_ty, expected_ty) + { + let mut sp: MultiSpan = vec![arg.span].into(); + sp.push_span_label( + arg.span, + format!("expected because this argument is of type `{arg_ty}`"), + ); + sp.push_span_label(rcvr.span, "in this closure call"); + err.span_note( + sp, + format!( + "expected because the closure was earlier called with an \ + argument of type `{arg_ty}`", + ), + ); + break; + } + } + + ("closure parameter", param.span) + } else { + ("closure", self.tcx.def_span(def_id)) + }; + err.span_note(span, format!("{kind} defined here")); + } else { + err.span_note( + self.tcx.def_span(def_id), + format!("{} defined here", self.tcx.def_descr(def_id)), + ); + } + } + + fn label_generic_mismatches( + &self, + err: &mut Diag<'_>, + callable_def_id: Option, + matched_inputs: &IndexVec>, + provided_arg_tys: &IndexVec, Span)>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, + is_method: bool, + ) { + let Some(def_id) = callable_def_id else { + return; + }; + + if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { + if matched_inputs[idx].is_none() { + continue; + } + + let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx()) + else { + continue; + }; + + let Some(generic_param) = generic_param else { + continue; + }; + + let idxs_matched = params_with_generics + .iter_enumerated() + .filter(|&(other_idx, (other_generic_param, _))| { + if other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[other_idx].is_some() { + return false; + } + other_generic_param == generic_param + }) + .count(); + + if idxs_matched == 0 { + continue; + } + + let expected_display_type = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx].1) + .sort_string(self.tcx); + let label = if idxs_matched == params_with_generics.len() - 1 { + format!( + "expected all arguments to be this {} type because they need to match the type of this parameter", + expected_display_type + ) + } else { + format!( + "expected some other arguments to be {} {} type to match the type of this parameter", + a_or_an(&expected_display_type), + expected_display_type, + ) + }; + + err.span_label(*matched_arg_span, label); + } + } + } + + /// Returns the parameters of a function, with their generic parameters if those are the full + /// type of that parameter. + /// + /// Returns `None` if the body is not a named function (e.g. a closure). + fn get_hir_param_info( + &self, + def_id: DefId, + is_method: bool, + ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> + { + let (sig, generics, body_id, params) = match self.tcx.hir_get_if_local(def_id)? { + hir::Node::TraitItem(&hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(sig, trait_fn), + .. + }) => match trait_fn { + hir::TraitFn::Required(params) => (sig, generics, None, Some(params)), + hir::TraitFn::Provided(body) => (sig, generics, Some(body), None), + }, + hir::Node::ImplItem(&hir::ImplItem { + generics, + kind: hir::ImplItemKind::Fn(sig, body), + .. + }) + | hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn { sig, generics, body, .. }, + .. + }) => (sig, generics, Some(body), None), + hir::Node::ForeignItem(&hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(sig, params, generics), + .. + }) => (sig, generics, None, Some(params)), + _ => return None, + }; + + // Make sure to remove both the receiver and variadic argument. Both are removed + // when matching parameter types. + let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + &hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + generics + .params + .iter() + .position(|param| param.def_id.to_def_id() == res_def_id) + .map(GenericIdx::from_usize) + } else { + None + } + }); + match (body_id, params) { + (Some(_), Some(_)) | (None, None) => unreachable!(), + (Some(body), None) => { + let params = self.tcx.hir_body(body).params; + let params = + params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(( + fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect(), + generics, + )) + } + (None, Some(params)) => { + let params = + params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(( + fn_inputs.zip(params.iter().map(|&ident| FnParam::Ident(ident))).collect(), + generics, + )) + } + } + } +} + +struct FindClosureArg<'tcx> { + tcx: TyCtxt<'tcx>, + calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, +} + +impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { + type NestedFilter = rustc_middle::hir::nested_filter::All; + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } + + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Call(rcvr, args) = ex.kind { + self.calls.push((rcvr, args)); + } + hir::intravisit::walk_expr(self, ex); + } +} + +#[derive(Clone, Copy)] +enum FnParam<'hir> { + Param(&'hir hir::Param<'hir>), + Ident(Option), +} - ty +impl FnParam<'_> { + fn span(&self) -> Span { + match self { + Self::Param(param) => param.span, + Self::Ident(ident) => { + if let Some(ident) = ident { + ident.span + } else { + DUMMY_SP + } + } + } } - fn parent_item_span(&self, id: HirId) -> Option { - let node = self.tcx.hir_node_by_def_id(self.tcx.hir_get_parent_item(id).def_id); - match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn { body: body_id, .. }, .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { - let body = self.tcx.hir_body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); + fn display(&self, idx: usize) -> impl '_ + fmt::Display { + struct D<'a>(FnParam<'a>, usize); + impl fmt::Display for D<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // A "unique" param name is one that (a) exists, and (b) is guaranteed to be unique + // among the parameters, i.e. `_` does not count. + let unique_name = match self.0 { + FnParam::Param(param) + if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind => + { + Some(ident.name) + } + FnParam::Ident(ident) + if let Some(ident) = ident + && ident.name != kw::Underscore => + { + Some(ident.name) + } + _ => None, + }; + if let Some(unique_name) = unique_name { + write!(f, "`{unique_name}`") + } else { + write!(f, "parameter #{}", self.1 + 1) } } - _ => {} } - None + D(*self, idx) } +} - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - let check_in_progress = |elem: &hir::Expr<'_>| { - self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( - |_| match elem.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), - _ => elem.span, - }, - ) - }; +struct FnCallDiagCtxt<'a, 'b, 'tcx> { + arg_matching_ctxt: ArgMatchingCtxt<'a, 'b, 'tcx>, + errors: Vec>, + matched_inputs: IndexVec>, +} - if let hir::ExprKind::If(_, _, Some(el)) = expr.kind - && let Some(rslt) = check_in_progress(el) - { - return rslt; - } +impl<'a, 'b, 'tcx> Deref for FnCallDiagCtxt<'a, 'b, 'tcx> { + type Target = ArgMatchingCtxt<'a, 'b, 'tcx>; - if let hir::ExprKind::Match(_, arms, _) = expr.kind { - let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); - if let Some(span) = iter.next() { - if iter.next().is_none() { - return span; - } - } - } + fn deref(&self) -> &Self::Target { + &self.arg_matching_ctxt + } +} - expr.span +// Controls how the arguments should be listed in the suggestion. +enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, +} + +impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let arg_matching_ctxt = ArgMatchingCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + // The algorithm here is inspired by levenshtein distance and longest common subsequence. + // We'll try to detect 4 different types of mistakes: + // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs + // - An input is missing, which isn't satisfied by *any* of the other arguments + // - Some number of arguments have been provided in the wrong order + // - A type is straight up invalid + let (errors, matched_inputs) = ArgMatrix::new( + arg_matching_ctxt.provided_args.len(), + arg_matching_ctxt.formal_and_expected_inputs.len(), + |provided, expected| arg_matching_ctxt.check_compatible(provided, expected), + ) + .find_errors(); + + FnCallDiagCtxt { arg_matching_ctxt, errors, matched_inputs } } - fn overwrite_local_ty_if_err(&self, hir_id: HirId, pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>) { - if let Err(guar) = ty.error_reported() { - struct OverwritePatternsWithError { - pat_hir_ids: Vec, - } - impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError { - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - self.pat_hir_ids.push(p.hir_id); - hir::intravisit::walk_pat(self, p); + fn check_wrap_args_in_tuple(&self) -> Option { + if let Some((mismatch_idx, terr)) = self.first_incompatible_error() { + // Is the first bad expected argument a tuple? + // Do we have as many extra provided arguments as the tuple's length? + // If so, we might have just forgotten to wrap some args in a tuple. + if let Some(ty::Tuple(tys)) = + self.formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() + && self.provided_arg_tys.len() == self.formal_and_expected_inputs.len() - 1 + tys.len() + { + // Wrap up the N provided arguments starting at this position in a tuple. + let provided_args_to_tuple = &self.provided_arg_tys[mismatch_idx..]; + let (provided_args_to_tuple, provided_args_after_tuple) = + provided_args_to_tuple.split_at(tys.len()); + let provided_as_tuple = Ty::new_tup_from_iter( + self.tcx, + provided_args_to_tuple.iter().map(|&(ty, _)| ty), + ); + + let mut satisfied = true; + // Check if the newly wrapped tuple + rest of the arguments are compatible. + for ((_, expected_ty), provided_ty) in std::iter::zip( + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), + [provided_as_tuple] + .into_iter() + .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), + ) { + if !self.may_coerce(provided_ty, *expected_ty) { + satisfied = false; + break; + } } + + // If they're compatible, suggest wrapping in an arg, and we're done! + // Take some care with spans, so we don't suggest wrapping a macro's + // innards in parenthesis, for example. + if satisfied + && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple + { + let mut err; + if tys.len() == 1 { + // A tuple wrap suggestion actually occurs within, + // so don't do anything special here. + err = self.err_ctxt().report_and_explain_type_error( + self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + lo, + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()], + self.provided_arg_tys[mismatch_idx].0, + ), + self.param_env, + terr, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); + } else { + let call_name = self.call_metadata.call_name; + err = self.dcx().struct_span_err( + self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, + format!( + "{call_name} takes {}{} but {} {} supplied", + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count( + self.formal_and_expected_inputs.len(), + "argument" + ), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ); + err.code(self.err_code.to_owned()); + err.multipart_suggestion_verbose( + "wrap these arguments in parentheses to construct a tuple", + vec![ + (lo.shrink_to_lo(), "(".to_string()), + (hi.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + }; + self.arg_matching_ctxt.args_ctxt.call_ctxt.fn_ctxt.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + None, + Some(mismatch_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.suggest_confusable(&mut err); + Some(err.emit()) + } else { + None + } + } else { + None } - // Override the types everywhere with `err()` to avoid knock on errors. - let err = Ty::new_error(self.tcx, guar); - self.write_ty(hir_id, err); - self.write_ty(pat.hir_id, err); - let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] }; - hir::intravisit::walk_pat(&mut visitor, pat); - // Mark all the subpatterns as `{type error}` as well. This allows errors for specific - // subpatterns to be silenced. - for hir_id in visitor.pat_hir_ids { - self.write_ty(hir_id, err); - } - self.locals.borrow_mut().insert(hir_id, err); - self.locals.borrow_mut().insert(pat.hir_id, err); + } else { + None } } - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'tcx>, - path_span: Span, - hir_id: HirId, - ) -> (Res, LoweredTy<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); - let ty = self.lowerer().lower_resolved_ty_path( - self_ty, - path, - hir_id, - PermitVariants::Yes, - ); - (path.res, LoweredTy::from_raw(self, path_span, ty)) + fn ensure_has_errors(&self) -> Option { + if self.errors.is_empty() { + if cfg!(debug_assertions) { + span_bug!(self.call_metadata.error_span, "expected errors from argument matrix"); + } else { + let mut err = self.dcx().create_err(errors::ArgMismatchIndeterminate { + span: self.call_metadata.error_span, + }); + self.arg_matching_ctxt.suggest_confusable(&mut err); + return Some(err.emit()); } - QPath::TypeRelative(hir_self_ty, segment) => { - let self_ty = self.lower_ty(hir_self_ty); + } - let result = self.lowerer().lower_type_relative_ty_path( - self_ty.raw, - hir_self_ty, - segment, - hir_id, - path_span, - PermitVariants::Yes, - ); - let ty = result - .map(|(ty, _, _)| ty) - .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); - let ty = LoweredTy::from_raw(self, path_span, ty); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); + None + } - // Write back the new resolution. - self.write_resolution(hir_id, result); + fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) + && let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) = + expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + } - (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) - } - QPath::LangItem(lang_item, span) => { - let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id); - (res, LoweredTy::from_raw(self, path_span, ty)) + fn filter_out_invalid_arguments(&mut self) -> Option { + let mut reported = None; + + self.errors.retain(|error| { + let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = + error + else { + return true; + }; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + let trace = self.arg_matching_ctxt.mk_trace( + provided_span, + self.arg_matching_ctxt.formal_and_expected_inputs[*expected_idx], + provided_ty, + ); + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { + let mut err = self.arg_matching_ctxt.err_ctxt().report_and_explain_type_error( + trace, + self.arg_matching_ctxt.param_env, + *e, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + reported = Some(err.emit()); + return false; } - } + true + }); + + reported } - /// Given a vector of fulfillment errors, try to adjust the spans of the - /// errors to more accurately point at the cause of the failure. - /// - /// This applies to calls, methods, and struct expressions. This will also - /// try to deduplicate errors that are due to the same cause but might - /// have been created with different [`ObligationCause`][traits::ObligationCause]s. - pub(super) fn adjust_fulfillment_errors_for_expr_obligation( - &self, - errors: &mut Vec>, - ) { - // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that - // other errors that have the same span and predicate can also get fixed, - // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. - // This is important since if we adjust one span but not the other, then - // we will have "duplicated" the error on the UI side. - let mut remap_cause = FxIndexSet::default(); - let mut not_adjusted = vec![]; + fn check_single_incompatible(&self) -> Option { + if let &[ + Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), + ] = &self.errors[..] + { + let (formal_ty, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_arg_span) = self.provided_arg_tys[provided_idx]; + let trace = self.mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); + self.emit_coerce_suggestions( + &mut err, + self.provided_args[provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); - for error in errors { - let before_span = error.obligation.cause.span; - if self.adjust_fulfillment_error_for_expr_obligation(error) - || before_span != error.obligation.cause.span + self.fn_ctxt.label_generic_mismatches( + &mut err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind + && provided_idx.as_usize() == expected_idx.as_usize() { - remap_cause.insert(( - before_span, - error.obligation.predicate, - error.obligation.cause.clone(), - )); - } else { - // If it failed to be adjusted once around, it may be adjusted - // via the "remap cause" mapping the second time... - not_adjusted.push(error); + self.note_source_of_type_mismatch_constraint( + &mut err, + rcvr, + crate::demand::TypeMismatchSource::Arg { + call_expr: self.call_expr, + incompatible_arg: provided_idx.as_usize(), + }, + ); } + + self.suggest_ptr_null_mut( + expected_ty, + provided_ty, + self.provided_args[provided_idx], + &mut err, + ); + + self.suggest_deref_unwrap_or( + &mut err, + self.callee_ty, + self.call_metadata.call_ident, + expected_ty, + provided_ty, + self.provided_args[provided_idx], + self.call_metadata.is_method, + ); + + // Call out where the function is defined + self.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + Some(expected_ty), + Some(expected_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + self.detect_dotdot(&mut err, provided_ty, self.provided_args[provided_idx]); + return Some(err.emit()); } - // Adjust any other errors that come from other cause codes, when these - // errors are of the same predicate as one we successfully adjusted, and - // when their spans overlap (suggesting they're due to the same root cause). - // - // This is because due to normalization, we often register duplicate - // obligations with misc obligations that are basically impossible to - // line back up with a useful WhereClauseInExpr. - for error in not_adjusted { - for (span, predicate, cause) in &remap_cause { - if *predicate == error.obligation.predicate - && span.contains(error.obligation.cause.span) + None + } + + fn maybe_optimize_extra_arg_suggestion(&mut self) { + if let [Error::Extra(provided_idx)] = &self.errors[..] { + if !self.remove_idx_is_perfect(provided_idx.as_usize()) { + if let Some(i) = (0..self.args_ctxt.call_ctxt.provided_args.len()) + .find(|&i| self.remove_idx_is_perfect(i)) { - error.obligation.cause = cause.clone(); - continue; + self.errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; } } } } - fn label_fn_like( + fn initial_final_diagnostic(&self) -> Diag<'_> { + if self.formal_and_expected_inputs.len() == self.provided_args.len() { + struct_span_code_err!( + self.dcx(), + self.call_metadata.full_call_span, + E0308, + "arguments to this {} are incorrect", + self.call_metadata.call_name, + ) + } else { + self.arg_matching_ctxt + .dcx() + .struct_span_err( + self.call_metadata.full_call_span, + format!( + "this {} takes {}{} but {} {} supplied", + self.call_metadata.call_name, + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ) + .with_code(self.err_code.to_owned()) + } + } + + fn labels_and_suggestion_text( &self, err: &mut Diag<'_>, - callable_def_id: Option, - callee_ty: Option>, - call_expr: &'tcx hir::Expr<'tcx>, - expected_ty: Option>, - // A specific argument should be labeled, instead of all of them - expected_idx: Option, - matched_inputs: &IndexVec>, - formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, - is_method: bool, - tuple_arguments: TupleArgumentsFlag, - ) { - let Some(mut def_id) = callable_def_id else { - return; - }; + ) -> (Vec<(Span, String)>, Vec<(Span, String)>, SuggestionText) { + // Don't print if it has error types or is just plain `_` + fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> bool { + tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) + } - // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly - // (eg invoking a closure) we want to point at the underlying callable, - // not the method implicitly invoked (eg call_once). - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - if tuple_arguments == TupleArguments - && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) - // Since this is an associated item, it might point at either an impl or a trait item. - // We want it to always point to the trait item. - // If we're pointing at an inherent function, we don't need to do anything, - // so we fetch the parent and verify if it's a trait item. - && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) - && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) - // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) - && let Some(callee_ty) = callee_ty - { - let callee_ty = callee_ty.peel_refs(); - match *callee_ty.kind() { - ty::Param(param) => { - let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx); - if param.kind.is_synthetic() { - // if it's `impl Fn() -> ..` then just fall down to the def-id based logic - def_id = param.def_id; - } else { - // Otherwise, find the predicate that makes this generic callable, - // and point at that. - let instantiated = self - .tcx - .explicit_predicates_of(self.body_id) - .instantiate_identity(self.tcx); - // FIXME(compiler-errors): This could be problematic if something has two - // fn-like predicates with different args, but callable types really never - // do that, so it's OK. - for (predicate, span) in instantiated { - if let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() - && pred.self_ty().peel_refs() == callee_ty - && self.tcx.is_fn_trait(pred.def_id()) - { - err.span_note(span, "callable defined here"); - return; - } + let mut labels = Vec::new(); + let mut suggestion_text = SuggestionText::None; + + let mut errors = self.errors.iter().peekable(); + let mut only_extras_so_far = errors + .peek() + .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; + let mut suggestions = vec![]; + while let Some(error) = errors.next() { + only_extras_so_far &= matches!(error, Error::Extra(_)); + + match error { + Error::Invalid(provided_idx, expected_idx, compatibility) => { + let (formal_ty, expected_ty) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.formal_and_expected_inputs + [*expected_idx]; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + if let Compatibility::Incompatible(error) = compatibility { + let trace = self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + provided_span, + (formal_ty, expected_ty), + provided_ty, + ); + if let Some(e) = error { + self.err_ctxt().note_type_err( + err, + &trace.cause, + None, + Some(self.param_env.and(trace.values)), + *e, + true, + None, + ); } } + + self.emit_coerce_suggestions( + err, + self.provided_args[*provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + self.detect_dotdot(err, provided_ty, self.provided_args[*provided_idx]); } - ty::Alias(ty::Opaque, ty::AliasTy { def_id: new_def_id, .. }) - | ty::Closure(new_def_id, _) - | ty::FnDef(new_def_id, _) => { - def_id = new_def_id; - } - _ => { - // Look for a user-provided impl of a `Fn` trait, and point to it. - let new_def_id = self.probe(|_| { - let trait_ref = ty::TraitRef::new( - self.tcx, - self.tcx.fn_trait_kind_to_def_id(call_kind)?, - [callee_ty, self.next_ty_var(DUMMY_SP)], - ); - let obligation = traits::Obligation::new( - self.tcx, - traits::ObligationCause::dummy(), - self.param_env, - trait_ref, - ); - match SelectionContext::new(self).select(&obligation) { - Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { - Some(impl_source.impl_def_id) + Error::Extra(arg_idx) => { + let (provided_ty, provided_span) = self.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}`") + } else { + "".to_string() + }; + let idx = if self.provided_arg_tys.len() == 1 { + "".to_string() + } else { + format!(" #{}", arg_idx.as_usize() + 1) + }; + labels.push(( + provided_span, + format!("unexpected argument{idx}{provided_ty_name}"), + )); + let mut span = provided_span; + if span.can_be_used_for_suggestions() + && self.call_metadata.error_span.can_be_used_for_suggestions() + { + if arg_idx.index() > 0 + && let Some((_, prev)) = self + .provided_arg_tys + .get(ProvidedIdx::from_usize(arg_idx.index() - 1)) + { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + + // Is last argument for deletion in a row starting from the 0-th argument? + // Then delete the next comma, so we are not left with `f(, ...)` + // + // fn f() {} + // - f(0, 1,) + // + f() + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.is_none_or(|prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) } - _ => None, + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { + let next = self + .provided_arg_tys + .get(*arg_idx + 1) + .map(|&(_, sp)| sp) + .unwrap_or_else(|| { + // Try to move before `)`. Note that `)` here is not necessarily + // the latin right paren, it could be a Unicode-confusable that + // looks like a `)`, so we must not use `- BytePos(1)` + // manipulations here. + self.arg_matching_ctxt + .tcx() + .sess + .source_map() + .end_point(self.call_expr.span) + }); + + // Include next comma + span = span.until(next); } - }); - if let Some(new_def_id) = new_def_id { - def_id = new_def_id; - } else { - return; - } - } - } - } - if let Some(def_span) = self.tcx.def_ident_span(def_id) - && !def_span.is_dummy() - { - let mut spans: MultiSpan = def_span.into(); - if let Some((params_with_generics, hir_generics)) = - self.get_hir_param_info(def_id, is_method) - { - struct MismatchedParam<'a> { - idx: ExpectedIdx, - generic: GenericIdx, - param: &'a FnParam<'a>, - deps: SmallVec<[ExpectedIdx; 4]>, - } + suggestions.push((span, String::new())); - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - // Gather all mismatched parameters with generics. - let mut mismatched_params = Vec::>::new(); - if let Some(expected_idx) = expected_idx { - let expected_idx = ExpectedIdx::from_usize(expected_idx); - let &(expected_generic, ref expected_param) = - ¶ms_with_generics[expected_idx]; - if let Some(expected_generic) = expected_generic { - mismatched_params.push(MismatchedParam { - idx: expected_idx, - generic: expected_generic, - param: expected_param, - deps: SmallVec::new(), - }); - } else { - // Still mark the mismatched parameter - spans.push_span_label(expected_param.span(), ""); + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + prev_extra_idx = Some(arg_idx.index()) } - } else { - mismatched_params.extend( - params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( - |((idx, &(generic, ref param)), matched_idx)| { - if matched_idx.is_some() { - None - } else if let Some(generic) = generic { - Some(MismatchedParam { - idx, - generic, - param, - deps: SmallVec::new(), - }) - } else { - // Still mark mismatched parameters - spans.push_span_label(param.span(), ""); - None - } - }, - ), - ); + self.detect_dotdot(err, provided_ty, self.provided_args[*arg_idx]); } + Error::Missing(expected_idx) => { + // If there are multiple missing arguments adjacent to each other, + // then we can provide a single error. - if !mismatched_params.is_empty() { - // For each mismatched parameter, create a two-way link to each matched parameter - // of the same type. - let mut dependants = IndexVec::::from_fn_n( - |_| SmallVec::<[u32; 4]>::new(), - params_with_generics.len(), - ); - let mut generic_uses = IndexVec::::from_fn_n( - |_| SmallVec::<[ExpectedIdx; 4]>::new(), - hir_generics.params.len(), - ); - for (idx, param) in mismatched_params.iter_mut().enumerate() { - for ((other_idx, &(other_generic, _)), &other_matched_idx) in - params_with_generics.iter_enumerated().zip(matched_inputs) - { - if other_generic == Some(param.generic) && other_matched_idx.is_some() { - generic_uses[param.generic].extend([param.idx, other_idx]); - dependants[other_idx].push(idx as u32); - param.deps.push(other_idx); - } + let mut missing_idxs = vec![*expected_idx]; + while let Some(e) = errors.next_if(|e| { + matches!(e, Error::Missing(next_expected_idx) + if *next_expected_idx == *missing_idxs.last().unwrap() + 1) + }) { + match e { + Error::Missing(expected_idx) => missing_idxs.push(*expected_idx), + _ => unreachable!( + "control flow ensures that we should always get an `Error::Missing`" + ), } } - // Highlight each mismatched type along with a note about which other parameters - // the type depends on (if any). - for param in &mismatched_params { - if let Some(deps_list) = listify(¶m.deps, |&dep| { - params_with_generics[dep].1.display(dep.as_usize()).to_string() - }) { - spans.push_span_label( - param.param.span(), + // NOTE: Because we might be re-arranging arguments, might have extra + // arguments, etc. it's hard to *really* know where we should provide + // this error label, so as a heuristic, we point to the provided arg, or + // to the call if the missing inputs pass the provided args. + match &missing_idxs[..] { + &[expected_idx] => { + let (_, input_ty) = self.formal_and_expected_inputs[expected_idx]; + let span = if let Some((_, arg_span)) = + self.provided_arg_tys.get(expected_idx.to_provided_idx()) + { + *arg_span + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([input_ty]) { + format!(" of type `{input_ty}`") + } else { + "".to_string() + }; + labels.push(( + span, format!( - "this parameter needs to match the {} type of {deps_list}", - self.resolve_vars_if_possible( - formal_and_expected_inputs[param.deps[0]].1 - ) - .sort_string(self.tcx), + "argument #{}{rendered} is missing", + expected_idx.as_usize() + 1 ), - ); - } else { - // Still mark mismatched parameters - spans.push_span_label(param.param.span(), ""); + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Provide(false), + SuggestionText::Provide(_) => SuggestionText::Provide(true), + _ => SuggestionText::DidYouMean, + }; } - } - // Highlight each parameter being depended on for a generic type. - for ((&(_, param), deps), &(_, expected_ty)) in - params_with_generics.iter().zip(&dependants).zip(formal_and_expected_inputs) - { - if let Some(deps_list) = listify(deps, |&dep| { - let param = &mismatched_params[dep as usize]; - param.param.display(param.idx.as_usize()).to_string() - }) { - spans.push_span_label( - param.span(), - format!( - "{deps_list} need{} to match the {} type of this parameter", - pluralize!((deps.len() != 1) as u32), - self.resolve_vars_if_possible(expected_ty) - .sort_string(self.tcx), - ), - ); + &[first_idx, second_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let span = if let (Some((_, first_span)), Some((_, second_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(second_idx.to_provided_idx()), + ) { + first_span.to(*second_span) + } else { + self.args_span + }; + let rendered = + if !has_error_or_infer([first_expected_ty, second_expected_ty]) { + format!( + " of type `{first_expected_ty}` and `{second_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("two arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; } - } - // Highlight each generic parameter in use. - for (param, uses) in hir_generics.params.iter().zip(&mut generic_uses) { - uses.sort(); - uses.dedup(); - if let Some(param_list) = listify(uses, |&idx| { - params_with_generics[idx].1.display(idx.as_usize()).to_string() - }) { - spans.push_span_label( - param.span, + &[first_idx, second_idx, third_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let (_, third_expected_ty) = self.formal_and_expected_inputs[third_idx]; + let span = if let (Some((_, first_span)), Some((_, third_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(third_idx.to_provided_idx()), + ) { + first_span.to(*third_span) + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([ + first_expected_ty, + second_expected_ty, + third_expected_ty, + ]) { format!( - "{param_list} {} reference this parameter `{}`", - if uses.len() == 2 { "both" } else { "all" }, - param.name.ident().name, - ), - ); + " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("three arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + missing_idxs => { + let first_idx = *missing_idxs.first().unwrap(); + let last_idx = *missing_idxs.last().unwrap(); + // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. + // It's hard to *really* know where we should provide this error label, so this is a + // decent heuristic + let span = if let (Some((_, first_span)), Some((_, last_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(last_idx.to_provided_idx()), + ) { + first_span.to(*last_span) + } else { + self.args_span + }; + labels.push((span, "multiple arguments are missing".to_string())); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; } } } - } - err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); - } else if let Some(hir::Node::Expr(e)) = self.tcx.hir_get_if_local(def_id) - && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind - { - let param = expected_idx - .and_then(|expected_idx| self.tcx.hir_body(*body).params.get(expected_idx)); - let (kind, span) = if let Some(param) = param { - // Try to find earlier invocations of this closure to find if the type mismatch - // is because of inference. If we find one, point at them. - let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] }; - let parent_def_id = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id; - match self.tcx.hir_node_by_def_id(parent_def_id) { - hir::Node::Item(item) => call_finder.visit_item(item), - hir::Node::TraitItem(item) => call_finder.visit_trait_item(item), - hir::Node::ImplItem(item) => call_finder.visit_impl_item(item), - _ => {} + Error::Swap( + first_provided_idx, + second_provided_idx, + first_expected_idx, + second_expected_idx, + ) => { + let (first_provided_ty, first_span) = + self.provided_arg_tys[*first_provided_idx]; + let (_, first_expected_ty) = + self.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}`") + } else { + String::new() + }; + labels.push(( + first_span, + format!("expected `{first_expected_ty}`{first_provided_ty_name}"), + )); + + let (second_provided_ty, second_span) = + self.provided_arg_tys[*second_provided_idx]; + let (_, second_expected_ty) = + self.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}`") + } else { + String::new() + }; + labels.push(( + second_span, + format!("expected `{second_expected_ty}`{second_provided_ty_name}"), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Swap, + _ => SuggestionText::DidYouMean, + }; } - let typeck = self.typeck_results.borrow(); - for (rcvr, args) in call_finder.calls { - if rcvr.hir_id.owner == typeck.hir_owner - && let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id) - && let ty::Closure(call_def_id, _) = rcvr_ty.kind() - && def_id == *call_def_id - && let Some(idx) = expected_idx - && let Some(arg) = args.get(idx) - && let Some(arg_ty) = typeck.node_type_opt(arg.hir_id) - && let Some(expected_ty) = expected_ty - && self.can_eq(self.param_env, arg_ty, expected_ty) - { - let mut sp: MultiSpan = vec![arg.span].into(); - sp.push_span_label( - arg.span, - format!("expected because this argument is of type `{arg_ty}`"), - ); - sp.push_span_label(rcvr.span, "in this closure call"); - err.span_note( - sp, - format!( - "expected because the closure was earlier called with an \ - argument of type `{arg_ty}`", - ), - ); - break; + Error::Permutation(args) => { + for (dst_arg, dest_input) in args { + let (_, expected_ty) = self.formal_and_expected_inputs[*dst_arg]; + let (provided_ty, provided_span) = self.provided_arg_tys[*dest_input]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + format!(", found `{provided_ty}`") + } else { + String::new() + }; + labels.push(( + provided_span, + format!("expected `{expected_ty}`{provided_ty_name}"), + )); } + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Reorder, + _ => SuggestionText::DidYouMean, + }; } + } + } - ("closure parameter", param.span) - } else { - ("closure", self.tcx.def_span(def_id)) - }; - err.span_note(span, format!("{kind} defined here")); - } else { - err.span_note( - self.tcx.def_span(def_id), - format!("{} defined here", self.tcx.def_descr(def_id)), - ); + (suggestions, labels, suggestion_text) + } + + fn label_generic_mismatches(&self, err: &mut Diag<'b>) { + self.fn_ctxt.label_generic_mismatches( + err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + } + + /// Incorporate the argument changes in the removal suggestion. + /// + /// When a type is *missing*, and the rest are additional, we want to suggest these with a + /// multipart suggestion, but in order to do so we need to figure out *where* the arg that + /// was provided but had the wrong type should go, because when looking at `expected_idx` + /// that is the position in the argument list in the definition, while `provided_idx` will + /// not be present. So we have to look at what the *last* provided position was, and point + /// one after to suggest the replacement. + fn append_arguments_changes(&self, suggestions: &mut Vec<(Span, String)>) { + // FIXME(estebank): This is hacky, and there's + // probably a better more involved change we can make to make this work. + // For example, if we have + // ``` + // fn foo(i32, &'static str) {} + // foo((), (), ()); + // ``` + // what should be suggested is + // ``` + // foo(/* i32 */, /* &str */); + // ``` + // which includes the replacement of the first two `()` for the correct type, and the + // removal of the last `()`. + + let mut prev = -1; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + // We want to point not at the *current* argument expression index, but rather at the + // index position where it *should have been*, which is *after* the previous one. + if let Some(provided_idx) = provided_idx { + prev = provided_idx.index() as i64; + continue; + } + let idx = ProvidedIdx::from_usize((prev + 1) as usize); + if let Some((_, arg_span)) = self.provided_arg_tys.get(idx) { + prev += 1; + // There is a type that was *not* found anywhere, so it isn't a move, but a + // replacement and we look at what type it should have been. This will allow us + // To suggest a multipart suggestion when encountering `foo(1, "")` where the def + // was `fn foo(())`. + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } } } - fn label_generic_mismatches( - &self, + fn format_suggestion_text( err: &mut Diag<'_>, - callable_def_id: Option, - matched_inputs: &IndexVec>, - provided_arg_tys: &IndexVec, Span)>, - formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, - is_method: bool, - ) { - let Some(def_id) = callable_def_id else { - return; + suggestions: Vec<(Span, String)>, + suggestion_text: SuggestionText, + ) -> Option { + let suggestion_text = match suggestion_text { + SuggestionText::None => None, + SuggestionText::Provide(plural) => { + Some(format!("provide the argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Remove(plural) => { + err.multipart_suggestion_verbose( + format!("remove the extra argument{}", if plural { "s" } else { "" }), + suggestions, + Applicability::HasPlaceholders, + ); + None + } + SuggestionText::Swap => Some("swap these arguments".to_string()), + SuggestionText::Reorder => Some("reorder these arguments".to_string()), + SuggestionText::DidYouMean => Some("did you mean".to_string()), }; + suggestion_text + } - if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { - if matched_inputs[idx].is_none() { - continue; - } + fn arguments_formatting(&self, suggestion_span: Span) -> ArgumentsFormatting { + let source_map = self.sess().source_map(); + let mut provided_inputs = self.matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = self.provided_arg_tys[first_idx] + && let (_, last_span) = self.provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + } - let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx()) - else { - continue; - }; + fn suggestion_code(&self) -> (Span, String) { + let source_map = self.sess().source_map(); + let suggestion_span = if let Some(args_span) = + self.call_metadata.error_span.trim_start(self.call_metadata.full_call_span) + { + // Span of the braces, e.g. `(a, b, c)`. + args_span + } else { + // The arg span of a function call that wasn't even given braces + // like what might happen with delegation reuse. + // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. + self.call_metadata.full_call_span.shrink_to_hi() + }; - let Some(generic_param) = generic_param else { - continue; - }; + let arguments_formatting = self.arguments_formatting(suggestion_span); - let idxs_matched = params_with_generics - .iter_enumerated() - .filter(|&(other_idx, (other_generic_param, _))| { - if other_idx == idx { - return false; - } - let Some(other_generic_param) = other_generic_param else { - return false; - }; - if matched_inputs[other_idx].is_some() { - return false; - } - other_generic_param == generic_param - }) - .count(); + let mut suggestion = "(".to_owned(); + let mut needs_comma = false; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + if needs_comma { + suggestion += ","; + } + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", + } + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx + && let (_, provided_span) = self.provided_arg_tys[*provided_idx] + && let Ok(arg_text) = source_map.span_to_snippet(provided_span) + { + (Some(provided_span), arg_text) + } else { + // Propose a placeholder of the correct type + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + (None, self.ty_to_snippet(expected_ty, expected_idx)) + }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = &arguments_formatting { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } + suggestion += &suggestion_text; + } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } + suggestion += ")"; - if idxs_matched == 0 { - continue; - } + (suggestion_span, suggestion) + } +} - let expected_display_type = self - .resolve_vars_if_possible(formal_and_expected_inputs[idx].1) - .sort_string(self.tcx); - let label = if idxs_matched == params_with_generics.len() - 1 { - format!( - "expected all arguments to be this {} type because they need to match the type of this parameter", - expected_display_type - ) - } else { - format!( - "expected some other arguments to be {} {} type to match the type of this parameter", - a_or_an(&expected_display_type), - expected_display_type, - ) - }; +struct ArgMatchingCtxt<'a, 'b, 'tcx> { + args_ctxt: ArgsCtxt<'a, 'b, 'tcx>, + provided_arg_tys: IndexVec, Span)>, +} + +impl<'a, 'b, 'tcx> Deref for ArgMatchingCtxt<'a, 'b, 'tcx> { + type Target = ArgsCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.args_ctxt + } +} + +impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let args_ctxt = ArgsCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + let provided_arg_tys = args_ctxt.provided_arg_tys(); - err.span_label(*matched_arg_span, label); - } + ArgMatchingCtxt { args_ctxt, provided_arg_tys } + } + + fn suggest_confusable(&self, err: &mut Diag<'_>) { + let Some(call_name) = self.call_metadata.call_ident else { + return; + }; + let Some(callee_ty) = self.callee_ty else { + return; + }; + let input_types: Vec> = self.provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); + + // Check for other methods in the following order + // - methods marked as `rustc_confusables` with the provided arguments + // - methods with the same argument type/count and short levenshtein distance + // - methods marked as `rustc_confusables` (done) + // - methods with short levenshtein distance + + // Look for commonly confusable method names considering arguments. + if let Some(_name) = self.confusable_method_name( + err, + callee_ty.peel_refs(), + call_name, + Some(input_types.clone()), + ) { + return; + } + // Look for method names with short levenshtein distance, considering arguments. + if let Some((assoc, fn_sig)) = self.similar_assoc(call_name) + && fn_sig.inputs()[1..] + .iter() + .zip(input_types.iter()) + .all(|(expected, found)| self.may_coerce(*expected, *found)) + && fn_sig.inputs()[1..].len() == input_types.len() + { + let assoc_name = assoc.name(); + err.span_suggestion_verbose( + call_name.span, + format!("you might have meant to use `{}`", assoc_name), + assoc_name, + Applicability::MaybeIncorrect, + ); + return; } } - /// Returns the parameters of a function, with their generic parameters if those are the full - /// type of that parameter. - /// - /// Returns `None` if the body is not a named function (e.g. a closure). - fn get_hir_param_info( + /// A "softer" version of the `demand_compatible`, which checks types without persisting them, + /// and treats error types differently + /// This will allow us to "probe" for other argument orders that would likely have been correct + fn check_compatible( &self, - def_id: DefId, - is_method: bool, - ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> - { - let (sig, generics, body_id, params) = match self.tcx.hir_get_if_local(def_id)? { - hir::Node::TraitItem(&hir::TraitItem { - generics, - kind: hir::TraitItemKind::Fn(sig, trait_fn), - .. - }) => match trait_fn { - hir::TraitFn::Required(params) => (sig, generics, None, Some(params)), - hir::TraitFn::Provided(body) => (sig, generics, Some(body), None), - }, - hir::Node::ImplItem(&hir::ImplItem { - generics, - kind: hir::ImplItemKind::Fn(sig, body), - .. - }) - | hir::Node::Item(&hir::Item { - kind: hir::ItemKind::Fn { sig, generics, body, .. }, - .. - }) => (sig, generics, Some(body), None), - hir::Node::ForeignItem(&hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(sig, params, generics), - .. - }) => (sig, generics, None, Some(params)), - _ => return None, - }; + provided_idx: ProvidedIdx, + expected_idx: ExpectedIdx, + ) -> Compatibility<'tcx> { + if provided_idx.as_usize() == expected_idx.as_usize() { + return self.compatibility_diagonal[provided_idx].clone(); + } - // Make sure to remove both the receiver and variadic argument. Both are removed - // when matching parameter types. - let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| { - if let hir::TyKind::Path(QPath::Resolved( - _, - &hir::Path { res: Res::Def(_, res_def_id), .. }, - )) = param.kind - { - generics - .params - .iter() - .position(|param| param.def_id.to_def_id() == res_def_id) - .map(GenericIdx::from_usize) - } else { - None - } + let (formal_input_ty, expected_input_ty) = self.formal_and_expected_inputs[expected_idx]; + // If either is an error type, we defy the usual convention and consider them to *not* be + // coercible. This prevents our error message heuristic from trying to pass errors into + // every argument. + if (formal_input_ty, expected_input_ty).references_error() { + return Compatibility::Incompatible(None); + } + + let (arg_ty, arg_span) = self.provided_arg_tys[provided_idx]; + + let expectation = Expectation::rvalue_hint(self.fn_ctxt, expected_input_ty); + let coerced_ty = expectation.only_has_type(self.fn_ctxt).unwrap_or(formal_input_ty); + let can_coerce = self.may_coerce(arg_ty, coerced_ty); + if !can_coerce { + return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( + ty::error::ExpectedFound::new(coerced_ty, arg_ty), + ))); + } + + // Using probe here, since we don't want this subtyping to affect inference. + let subtyping_error = self.probe(|_| { + self.at(&self.misc(arg_span), self.param_env) + .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) + .err() }); - match (body_id, params) { - (Some(_), Some(_)) | (None, None) => unreachable!(), - (Some(body), None) => { - let params = self.tcx.hir_body(body).params; - let params = - params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; - debug_assert_eq!(params.len(), fn_inputs.len()); - Some(( - fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect(), - generics, - )) - } - (None, Some(params)) => { - let params = - params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; - debug_assert_eq!(params.len(), fn_inputs.len()); - Some(( - fn_inputs.zip(params.iter().map(|&ident| FnParam::Ident(ident))).collect(), - generics, - )) - } + + // Same as above: if either the coerce type or the checked type is an error type, + // consider them *not* compatible. + let references_error = (coerced_ty, arg_ty).references_error(); + match (references_error, subtyping_error) { + (false, None) => Compatibility::Compatible, + (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), } } + + fn remove_idx_is_perfect(&self, idx: usize) -> bool { + let removed_arg_tys = self + .provided_arg_tys + .iter() + .enumerate() + .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) + .collect::>(); + std::iter::zip(self.formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( + |((expected_ty, _), (provided_ty, _))| { + !provided_ty.references_error() && self.may_coerce(*provided_ty, *expected_ty) + }, + ) + } } -struct FindClosureArg<'tcx> { - tcx: TyCtxt<'tcx>, - calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, +struct ArgsCtxt<'a, 'b, 'tcx> { + call_ctxt: CallCtxt<'a, 'b, 'tcx>, + call_metadata: CallMetadata, + args_span: Span, } -impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { - type NestedFilter = rustc_middle::hir::nested_filter::All; +impl<'a, 'b, 'tcx> Deref for ArgsCtxt<'a, 'b, 'tcx> { + type Target = CallCtxt<'a, 'b, 'tcx>; - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx + fn deref(&self) -> &Self::Target { + &self.call_ctxt } +} - fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(rcvr, args) = ex.kind { - self.calls.push((rcvr, args)); +impl<'a, 'b, 'tcx> ArgsCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let call_ctxt: CallCtxt<'_, '_, '_> = CallCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + let call_metadata = call_ctxt.call_metadata(); + let args_span = call_metadata + .error_span + .trim_start(call_metadata.full_call_span) + .unwrap_or(call_metadata.error_span); + + ArgsCtxt { args_span, call_metadata, call_ctxt } + } + + /// Get the argument span in the context of the call span so that + /// suggestions and labels are (more) correct when an arg is a + /// macro invocation. + fn normalize_span(&self, span: Span) -> Span { + let normalized_span = + span.find_ancestor_inside_same_ctxt(self.call_metadata.error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(self.call_metadata.error_span) { + span + } else { + normalized_span } - hir::intravisit::walk_expr(self, ex); + } + + /// Computes the provided types and spans. + fn provided_arg_tys(&self) -> IndexVec, Span)> { + self.call_ctxt + .provided_args + .iter() + .map(|expr| { + let ty = self + .call_ctxt + .fn_ctxt + .typeck_results + .borrow() + .expr_ty_adjusted_opt(*expr) + .unwrap_or_else(|| Ty::new_misc_error(self.call_ctxt.fn_ctxt.tcx)); + ( + self.call_ctxt.fn_ctxt.resolve_vars_if_possible(ty), + self.normalize_span(expr.span), + ) + }) + .collect() + } + + // Obtain another method on `Self` that have similar name. + fn similar_assoc(&self, call_name: Ident) -> Option<(ty::AssocItem, ty::FnSig<'tcx>)> { + if let Some(callee_ty) = self.call_ctxt.callee_ty + && let Ok(Some(assoc)) = self.call_ctxt.fn_ctxt.probe_op( + call_name.span, + MethodCall, + Some(call_name), + None, + IsSuggestion(true), + callee_ty.peel_refs(), + self.call_ctxt.callee_expr.unwrap().hir_id, + TraitsInScope, + |mut ctxt| ctxt.probe_for_similar_candidate(), + ) + && assoc.is_method() + { + let args = + self.call_ctxt.fn_ctxt.infcx.fresh_args_for_item(call_name.span, assoc.def_id); + let fn_sig = self + .call_ctxt + .fn_ctxt + .tcx + .fn_sig(assoc.def_id) + .instantiate(self.call_ctxt.fn_ctxt.tcx, args); + + self.call_ctxt.fn_ctxt.instantiate_binder_with_fresh_vars( + call_name.span, + BoundRegionConversionTime::FnCall, + fn_sig, + ); + } + None + } + + fn call_is_in_macro(&self) -> bool { + self.call_metadata.full_call_span.in_external_macro(self.sess().source_map()) } } -#[derive(Clone, Copy)] -enum FnParam<'hir> { - Param(&'hir hir::Param<'hir>), - Ident(Option), +struct CallMetadata { + error_span: Span, + call_ident: Option, + full_call_span: Span, + call_name: &'static str, + is_method: bool, } -impl FnParam<'_> { - fn span(&self) -> Span { - match self { - Self::Param(param) => param.span, - Self::Ident(ident) => { - if let Some(ident) = ident { - ident.span +struct CallCtxt<'a, 'b, 'tcx> { + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + callee_expr: Option<&'tcx Expr<'tcx>>, + callee_ty: Option>, +} + +impl<'a, 'b, 'tcx> Deref for CallCtxt<'a, 'b, 'tcx> { + type Target = &'a FnCtxt<'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.fn_ctxt + } +} + +impl<'a, 'b, 'tcx> CallCtxt<'a, 'b, 'tcx> { + fn new( + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> CallCtxt<'a, 'b, 'tcx> { + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, receiver, ..) => { + if let Some((DefKind::AssocFn, def_id)) = + fn_ctxt.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = fn_ctxt.tcx.opt_associated_item(def_id) + && assoc.is_method() + { + Some(*receiver) } else { - DUMMY_SP + None } } + _ => None, + }; + + let callee_ty = callee_expr.and_then(|callee_expr| { + fn_ctxt.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr) + }); + + CallCtxt { + fn_ctxt, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + callee_expr, + callee_ty, } } - fn display(&self, idx: usize) -> impl '_ + fmt::Display { - struct D<'a>(FnParam<'a>, usize); - impl fmt::Display for D<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // A "unique" param name is one that (a) exists, and (b) is guaranteed to be unique - // among the parameters, i.e. `_` does not count. - let unique_name = match self.0 { - FnParam::Param(param) - if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind => - { - Some(ident.name) + fn call_metadata(&self) -> CallMetadata { + match &self.call_expr.kind { + hir::ExprKind::Call( + hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, + _, + ) => { + if let Res::Def(DefKind::Ctor(of, _), _) = + self.typeck_results.borrow().qpath_res(qpath, *hir_id) + { + let name = match of { + CtorOf::Struct => "struct", + CtorOf::Variant => "enum variant", + }; + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: name, + is_method: false, } - FnParam::Ident(ident) - if let Some(ident) = ident - && ident.name != kw::Underscore => - { - Some(ident.name) + } else { + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, } - _ => None, - }; - if let Some(unique_name) = unique_name { - write!(f, "`{unique_name}`") + } + } + hir::ExprKind::Call(hir::Expr { span, .. }, _) => CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, + }, + hir::ExprKind::MethodCall(path_segment, _, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) } else { - write!(f, "parameter #{}", self.1 + 1) + ident_span + }; + CallMetadata { + error_span: *span, + call_ident: Some(path_segment.ident), + full_call_span: ident_span, + call_name: "method", + is_method: true, } } + k => span_bug!(self.call_span, "checking argument types on a non-call: `{:?}`", k), + } + } + + fn mk_trace( + &self, + span: Span, + (formal_ty, expected_ty): (Ty<'tcx>, Ty<'tcx>), + provided_ty: Ty<'tcx>, + ) -> TypeTrace<'tcx> { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) + } + + fn ty_to_snippet(&self, ty: Ty<'tcx>, expected_idx: ExpectedIdx) -> String { + if ty.is_unit() { + "()".to_string() + } else if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else if let Some(fn_def_id) = self.fn_def_id + && self.tcx.def_kind(fn_def_id).is_fn_like() + && let self_implicit = + matches!(self.call_expr.kind, hir::ExprKind::MethodCall(..)) as usize + && let Some(Some(arg)) = + self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) + && arg.name != kw::SelfLower + { + format!("/* {} */", arg.name) + } else { + "/* value */".to_string() } - D(*self, idx) } + + fn first_incompatible_error(&self) -> Option<(ProvidedIdx, TypeError<'tcx>)> { + self.compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { + if let Compatibility::Incompatible(Some(terr)) = c { Some((i, *terr)) } else { None } + }) + } +} + +enum SuggestionText { + None, + Provide(bool), + Remove(bool), + Swap, + Reorder, + DidYouMean, } From 1991779bd4a0a8d8905b6644b06aa317dde353ac Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:31:56 +0000 Subject: [PATCH 0435/1900] Make llvm_enzyme a regular cargo feature This makes it clearer that it is set by the build system rather than by the rustc that compiles the current rustc. It also avoids bootstrap needing to pass --check-cfg llvm_enzyme to rustc. --- compiler/rustc/Cargo.toml | 1 + compiler/rustc_builtin_macros/Cargo.toml | 5 +++++ compiler/rustc_builtin_macros/src/autodiff.rs | 3 ++- compiler/rustc_codegen_llvm/Cargo.toml | 1 + compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 6 ++++-- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 8 ++++---- compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_interface/Cargo.toml | 1 + src/bootstrap/src/core/build_steps/compile.rs | 4 ---- src/bootstrap/src/lib.rs | 6 +++--- 11 files changed, 23 insertions(+), 15 deletions(-) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index e3214d1ab9cec..9ef8fa75062a2 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -30,6 +30,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] check_only = ['rustc_driver_impl/check_only'] jemalloc = ['dep:tikv-jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] +llvm_enzyme = ['rustc_driver_impl/llvm_enzyme'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] # tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index e56b9e641a10b..ce9a3ce3f248e 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -33,3 +33,8 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end + +[features] +# tidy-alphabetical-start +llvm_enzyme = [] +# tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 48d0795af5ee2..f4a923797e2d3 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -209,7 +209,8 @@ mod llvm_enzyme { mut item: Annotatable, mode: DiffMode, ) -> Vec { - if cfg!(not(llvm_enzyme)) { + // FIXME(bjorn3) maybe have the backend directly tell if autodiff is supported? + if cfg!(not(feature = "llvm_enzyme")) { ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); return vec![item]; } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 2d11628250cd2..67bd1e59bb0c2 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -46,5 +46,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start check_only = ["rustc_llvm/check_only"] +llvm_enzyme = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index f571716d9dd9f..78107d95e5a70 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -617,7 +617,7 @@ pub(crate) fn run_pass_manager( crate::builder::gpu_offload::handle_gpu_code(cgcx, &cx); } - if cfg!(llvm_enzyme) && enable_ad && !thin { + if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 423f0da48781a..bda81fbd19e82 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -574,7 +574,8 @@ pub(crate) unsafe fn llvm_optimize( // FIXME(ZuseZ4): In a future update we could figure out how to only optimize individual functions getting // differentiated. - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); @@ -740,7 +741,8 @@ pub(crate) fn optimize( // If we know that we will later run AD, then we disable vectorization and loop unrolling. // Otherwise we pretend AD is already done and run the normal opt pipeline (=PostAD). - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let autodiff_stage = if consider_ad { AutodiffStage::PreAD } else { AutodiffStage::PostAD }; // The embedded bitcode is used to run LTO/ThinLTO. // The bitcode obtained during the `codegen` phase is no longer suitable for performing LTO. diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 56d756e52cce1..695435eb6daa7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -59,10 +59,10 @@ pub(crate) enum LLVMRustVerifierFailureAction { LLVMReturnStatusAction = 2, } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) use self::Enzyme_AD::*; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) mod Enzyme_AD { use std::ffi::{CString, c_char}; @@ -134,10 +134,10 @@ pub(crate) mod Enzyme_AD { } } -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) use self::Fallback_AD::*; -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) mod Fallback_AD { #![allow(unused_variables)] diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index ae1dbd2cf5148..46efa50cff364 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -74,6 +74,7 @@ ctrlc = "3.4.4" # tidy-alphabetical-start check_only = ['rustc_interface/check_only'] llvm = ['rustc_interface/llvm'] +llvm_enzyme = ['rustc_interface/llvm_enzyme'] max_level_info = ['rustc_log/max_level_info'] rustc_randomized_layouts = [ 'rustc_index/rustc_randomized_layouts', diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 473ac5e0cea4a..f0836c47740a2 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -58,4 +58,5 @@ rustc_abi = { path = "../rustc_abi" } # tidy-alphabetical-start check_only = ['rustc_codegen_llvm?/check_only'] llvm = ['dep:rustc_codegen_llvm'] +llvm_enzyme = ['rustc_builtin_macros/llvm_enzyme', 'rustc_codegen_llvm/llvm_enzyme'] # tidy-alphabetical-end diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 1458b0beefa85..14104d7d1d783 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1357,10 +1357,6 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } - if builder.config.llvm_enzyme { - cargo.rustflag("--cfg=llvm_enzyme"); - } - // These conditionals represent a tension between three forces: // - For non-check builds, we need to define some LLVM-related environment // variables, requiring LLVM to have been built. diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index a2aeed20948e5..e953fe2945e48 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -87,9 +87,6 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::Codegen), "bootstrap", None), (Some(Mode::ToolRustcPrivate), "bootstrap", None), (Some(Mode::ToolStd), "bootstrap", None), - (Some(Mode::Rustc), "llvm_enzyme", None), - (Some(Mode::Codegen), "llvm_enzyme", None), - (Some(Mode::ToolRustcPrivate), "llvm_enzyme", None), (Some(Mode::ToolRustcPrivate), "rust_analyzer", None), (Some(Mode::ToolStd), "rust_analyzer", None), // Any library specific cfgs like `target_os`, `target_arch` should be put in @@ -869,6 +866,9 @@ impl Build { if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") { features.push("llvm"); } + if self.config.llvm_enzyme { + features.push("llvm_enzyme"); + } // keep in sync with `bootstrap/compile.rs:rustc_cargo_env` if self.config.rust_randomize_layout && check("rustc_randomized_layouts") { features.push("rustc_randomized_layouts"); From 2d76bb1f23be5da37199e632ad2473f245db4a5f Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 12 Sep 2025 18:30:02 -0400 Subject: [PATCH 0436/1900] replace some `#[const_trait]` with `const trait` --- library/core/src/cmp.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 94536f25b4197..95896ab144187 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -334,9 +334,8 @@ pub macro PartialEq($item:item) { #[doc(alias = "!=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Eq"] -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait Eq: [const] PartialEq + PointeeSized { +pub const trait Eq: [const] PartialEq + PointeeSized { // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a // type implements `Eq` itself. The current deriving infrastructure means doing this assertion // without using a method on this trait is nearly impossible. @@ -966,9 +965,8 @@ impl Clone for Reverse { #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Ord"] -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { +pub const trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { /// This method returns an [`Ordering`] between `self` and `other`. /// /// By convention, `self.cmp(&other)` returns the ordering matching the expression @@ -1352,9 +1350,8 @@ pub macro Ord($item:item) { )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait PartialOrd: PartialEq + PointeeSized { +pub const trait PartialOrd: PartialEq + PointeeSized { /// This method returns an ordering between `self` and `other` values if one exists. /// /// # Examples From 4e286122d9a9dfcc1ae33c3959a3e8fd325f324b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 05:21:30 +0300 Subject: [PATCH 0437/1900] Port a bunch of stuff from rustc and fix a bunch of type mismatches/diagnostics This started from porting coercion, but ended with porting much more. --- .../crates/hir-def/src/signatures.rs | 19 + .../crates/hir-ty/src/autoderef.rs | 373 ++- .../crates/hir-ty/src/builder.rs | 47 +- .../crates/hir-ty/src/chalk_ext.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 8 + .../hir-ty/src/diagnostics/unsafe_check.rs | 5 +- .../crates/hir-ty/src/display.rs | 45 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 194 +- .../crates/hir-ty/src/infer/autoderef.rs | 54 + .../crates/hir-ty/src/infer/cast.rs | 156 +- .../crates/hir-ty/src/infer/closure.rs | 2287 +++++------------ .../hir-ty/src/infer/closure/analysis.rs | 1298 ++++++++++ .../crates/hir-ty/src/infer/coerce.rs | 1992 +++++++++----- .../crates/hir-ty/src/infer/expr.rs | 675 +++-- .../crates/hir-ty/src/infer/pat.rs | 31 +- .../crates/hir-ty/src/infer/unify.rs | 612 +++-- .../rust-analyzer/crates/hir-ty/src/layout.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 6 +- .../crates/hir-ty/src/lower_nextsolver.rs | 106 + .../crates/hir-ty/src/method_resolution.rs | 47 +- .../crates/hir-ty/src/mir/borrowck.rs | 9 +- .../crates/hir-ty/src/mir/eval.rs | 6 +- .../crates/hir-ty/src/mir/eval/shim.rs | 15 +- .../crates/hir-ty/src/mir/eval/tests.rs | 144 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 8 +- .../crates/hir-ty/src/next_solver/consts.rs | 47 +- .../crates/hir-ty/src/next_solver/def_id.rs | 60 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 151 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 1332 ++++++++++ .../hir-ty/src/next_solver/generic_arg.rs | 45 +- .../crates/hir-ty/src/next_solver/infer/at.rs | 32 +- .../hir-ty/src/next_solver/infer/mod.rs | 6 + .../infer/region_constraints/mod.rs | 53 +- .../next_solver/infer/relate/generalize.rs | 2 +- .../src/next_solver/infer/relate/lattice.rs | 269 ++ .../src/next_solver/infer/relate/mod.rs | 1 + .../hir-ty/src/next_solver/infer/select.rs | 334 +++ .../src/next_solver/infer/snapshot/fudge.rs | 263 ++ .../src/next_solver/infer/snapshot/mod.rs | 1 + .../next_solver/infer/snapshot/undo_log.rs | 4 + .../hir-ty/src/next_solver/infer/traits.rs | 108 +- .../src/next_solver/infer/type_variable.rs | 15 +- .../crates/hir-ty/src/next_solver/inspect.rs | 501 ++++ .../crates/hir-ty/src/next_solver/interner.rs | 88 +- .../crates/hir-ty/src/next_solver/mapping.rs | 37 + .../solve_normalize.rs => normalize.rs} | 66 +- .../hir-ty/src/next_solver/obligation_ctxt.rs | 203 ++ .../hir-ty/src/next_solver/predicate.rs | 52 +- .../crates/hir-ty/src/next_solver/project.rs | 3 - .../crates/hir-ty/src/next_solver/solver.rs | 16 +- .../src/next_solver/structural_normalize.rs | 57 + .../crates/hir-ty/src/next_solver/ty.rs | 87 +- .../crates/hir-ty/src/next_solver/util.rs | 6 +- .../crates/hir-ty/src/target_feature.rs | 2 +- .../crates/hir-ty/src/tests/coercion.rs | 42 +- .../crates/hir-ty/src/tests/diagnostics.rs | 1 - .../crates/hir-ty/src/tests/incremental.rs | 8 +- .../crates/hir-ty/src/tests/macros.rs | 34 +- .../hir-ty/src/tests/method_resolution.rs | 63 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/opaque_types.rs | 1 + .../crates/hir-ty/src/tests/patterns.rs | 38 +- .../crates/hir-ty/src/tests/regression.rs | 242 +- .../hir-ty/src/tests/regression/new_solver.rs | 111 + .../crates/hir-ty/src/tests/simple.rs | 98 +- .../crates/hir-ty/src/tests/traits.rs | 357 +-- .../rust-analyzer/crates/hir-ty/src/traits.rs | 5 - .../rust-analyzer/crates/hir-ty/src/utils.rs | 108 +- .../crates/hir-ty/src/variance.rs | 57 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 +- .../src/handlers/convert_closure_to_fn.rs | 7 +- .../src/handlers/extract_function.rs | 2 +- .../ide-completion/src/completions/dot.rs | 4 +- .../ide-completion/src/tests/expression.rs | 3 +- .../src/handlers/typed_hole.rs | 2 +- .../src/handlers/unresolved_method.rs | 24 +- .../crates/ide/src/hover/tests.rs | 14 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 16 +- .../crates/intern/src/symbol/symbols.rs | 1 + .../rust-analyzer/tests/slow-tests/main.rs | 4 +- .../crates/test-utils/src/minicore.rs | 28 +- src/tools/rust-analyzer/xtask/src/tidy.rs | 9 + 84 files changed, 9358 insertions(+), 3944 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs rename src/tools/rust-analyzer/crates/hir-ty/src/next_solver/{project/solve_normalize.rs => normalize.rs} (87%) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index bf72fafeae724..47638610ed734 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -489,6 +489,7 @@ bitflags! { const HAS_TARGET_FEATURE = 1 << 9; const DEPRECATED_SAFE_2024 = 1 << 10; const EXPLICIT_SAFE = 1 << 11; + const RUSTC_INTRINSIC = 1 << 12; } } @@ -522,6 +523,9 @@ impl FunctionSignature { if attrs.by_key(sym::target_feature).exists() { flags.insert(FnFlags::HAS_TARGET_FEATURE); } + if attrs.by_key(sym::rustc_intrinsic).exists() { + flags.insert(FnFlags::RUSTC_INTRINSIC); + } let legacy_const_generics_indices = attrs.rustc_legacy_const_generics(); let source = loc.source(db); @@ -617,6 +621,21 @@ impl FunctionSignature { pub fn has_target_feature(&self) -> bool { self.flags.contains(FnFlags::HAS_TARGET_FEATURE) } + + pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool { + let data = db.function_signature(id); + data.flags.contains(FnFlags::RUSTC_INTRINSIC) + // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used + || match &data.abi { + Some(abi) => *abi == sym::rust_dash_intrinsic, + None => match id.lookup(db).container { + ItemContainerId::ExternBlockId(block) => { + block.abi(db) == Some(sym::rust_dash_intrinsic) + } + _ => false, + }, + } + } } bitflags! { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 82696a5c94daf..fd60ffcf24b0a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -3,27 +3,28 @@ //! reference to a type with the field `bar`. This is an approximation of the //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs). -use std::mem; +use std::fmt; -use chalk_ir::cast::Cast; -use hir_def::lang_item::LangItem; -use hir_expand::name::Name; -use intern::sym; +use hir_def::{TypeAliasId, lang_item::LangItem}; +use rustc_type_ir::inherent::{IntoKind, Ty as _}; +use tracing::debug; use triomphe::Arc; +use crate::next_solver::infer::InferOk; use crate::{ - Canonical, Goal, Interner, ProjectionTyExt, TraitEnvironment, Ty, TyBuilder, TyKind, - db::HirDatabase, infer::unify::InferenceTable, next_solver::mapping::ChalkToNextSolver, + TraitEnvironment, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + Ty, TyKind, + infer::traits::{ObligationCause, PredicateObligations}, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, + }, }; const AUTODEREF_RECURSION_LIMIT: usize = 20; -#[derive(Debug)] -pub(crate) enum AutoderefKind { - Builtin, - Overloaded, -} - /// Returns types that `ty` transitively dereferences to. This function is only meant to be used /// outside `hir-ty`. /// @@ -34,16 +35,17 @@ pub(crate) enum AutoderefKind { pub fn autoderef( db: &dyn HirDatabase, env: Arc, - ty: Canonical, -) -> impl Iterator { + ty: crate::Canonical, +) -> impl Iterator { let mut table = InferenceTable::new(db, env); + let interner = table.interner; let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty.to_nextsolver(interner)); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be // resolved, just replace with fallback type. - let resolved = autoderef.table.resolve_completely(ty); + let resolved = autoderef.table.resolve_completely(ty.to_chalk(interner)); // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. @@ -59,178 +61,267 @@ pub fn autoderef( v.into_iter() } -trait TrackAutoderefSteps { +pub(crate) trait TrackAutoderefSteps<'db>: Default + fmt::Debug { fn len(&self) -> usize; - fn push(&mut self, kind: AutoderefKind, ty: &Ty); + fn push(&mut self, ty: Ty<'db>, kind: AutoderefKind); } -impl TrackAutoderefSteps for usize { +impl<'db> TrackAutoderefSteps<'db> for usize { fn len(&self) -> usize { *self } - fn push(&mut self, _: AutoderefKind, _: &Ty) { + fn push(&mut self, _: Ty<'db>, _: AutoderefKind) { *self += 1; } } -impl TrackAutoderefSteps for Vec<(AutoderefKind, Ty)> { +impl<'db> TrackAutoderefSteps<'db> for Vec<(Ty<'db>, AutoderefKind)> { fn len(&self) -> usize { self.len() } - fn push(&mut self, kind: AutoderefKind, ty: &Ty) { - self.push((kind, ty.clone())); + fn push(&mut self, ty: Ty<'db>, kind: AutoderefKind) { + self.push((ty, kind)); } } -#[derive(Debug)] -pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { - pub(crate) table: &'table mut InferenceTable<'db>, - ty: Ty, - at_start: bool, - steps: T, - explicit: bool, - use_receiver_trait: bool, +#[derive(Copy, Clone, Debug)] +pub(crate) enum AutoderefKind { + /// A true pointer type, such as `&T` and `*mut T`. + Builtin, + /// A type which must dispatch to a `Deref` implementation. + Overloaded, } -impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new( - table: &'table mut InferenceTable<'db>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, - ) -> Self { - let ty = table.structurally_resolve_type(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } - } - - pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { - &self.steps - } +struct AutoderefSnapshot<'db, Steps> { + at_start: bool, + reached_recursion_limit: bool, + steps: Steps, + cur_ty: Ty<'db>, + obligations: PredicateObligations<'db>, } -impl<'table, 'db> Autoderef<'table, 'db, usize> { - pub(crate) fn new_no_tracking( - table: &'table mut InferenceTable<'db>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, - ) -> Self { - let ty = table.structurally_resolve_type(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } - } +#[derive(Clone, Copy)] +struct AutoderefTraits { + trait_target: TypeAliasId, } -#[allow(private_bounds)] -impl Autoderef<'_, '_, T> { - pub(crate) fn step_count(&self) -> usize { - self.steps.len() - } +/// Recursively dereference a type, considering both built-in +/// dereferences (`*`) and the `Deref` trait. +/// Although called `Autoderef` it can be configured to use the +/// `Receiver` trait instead of the `Deref` trait. +pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> { + // Meta infos: + pub(crate) table: &'a mut InferenceTable<'db>, + traits: Option, - pub(crate) fn final_ty(&self) -> Ty { - self.ty.clone() - } + // Current state: + state: AutoderefSnapshot<'db, Steps>, + + // Configurations: + include_raw_pointers: bool, + use_receiver_trait: bool, } -impl Iterator for Autoderef<'_, '_, T> { - type Item = (Ty, usize); +impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> { + type Item = (Ty<'db>, usize); - #[tracing::instrument(skip_all)] fn next(&mut self) -> Option { - if mem::take(&mut self.at_start) { - return Some((self.ty.clone(), 0)); + debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); + if self.state.at_start { + self.state.at_start = false; + debug!("autoderef stage #0 is {:?}", self.state.cur_ty); + return Some((self.state.cur_ty, 0)); } - if self.steps.len() > AUTODEREF_RECURSION_LIMIT { + // If we have reached the recursion limit, error gracefully. + if self.state.steps.len() >= AUTODEREF_RECURSION_LIMIT { + self.state.reached_recursion_limit = true; return None; } - let (kind, new_ty) = - autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; + if self.state.cur_ty.is_ty_var() { + return None; + } + + // Otherwise, deref if type is derefable: + // NOTE: in the case of self.use_receiver_trait = true, you might think it would + // be better to skip this clause and use the Overloaded case only, since &T + // and &mut T implement Receiver. But built-in derefs apply equally to Receiver + // and Deref, and this has benefits for const and the emitted MIR. + let (kind, new_ty) = if let Some(ty) = + self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers) + { + debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty)); + // NOTE: we may still need to normalize the built-in deref in case + // we have some type like `&::Assoc`, since users of + // autoderef expect this type to have been structurally normalized. + if let TyKind::Alias(..) = ty.kind() { + let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?; + self.state.obligations.extend(obligations); + (AutoderefKind::Builtin, normalized_ty) + } else { + (AutoderefKind::Builtin, ty) + } + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + // The overloaded deref check already normalizes the pointee type. + (AutoderefKind::Overloaded, ty) + } else { + return None; + }; - self.steps.push(kind, &self.ty); - self.ty = new_ty; + self.state.steps.push(self.state.cur_ty, kind); + debug!( + "autoderef stage #{:?} is {:?} from {:?}", + self.step_count(), + new_ty, + (self.state.cur_ty, kind) + ); + self.state.cur_ty = new_ty; - Some((self.ty.clone(), self.step_count())) + Some((self.state.cur_ty, self.step_count())) } } -pub(crate) fn autoderef_step( - table: &mut InferenceTable<'_>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, -) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { - Some((AutoderefKind::Builtin, table.structurally_resolve_type(derefed))) - } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) +impl<'a, 'db> Autoderef<'a, 'db> { + pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Self::new_impl(table, base_ty) } } -pub(crate) fn builtin_deref<'ty>( - db: &dyn HirDatabase, - ty: &'ty Ty, - explicit: bool, -) -> Option<&'ty Ty> { - match ty.kind(Interner) { - TyKind::Ref(.., ty) => Some(ty), - TyKind::Raw(.., ty) if explicit => Some(ty), - &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) if crate::lang_items::is_box(db, adt) => { - substs.at(Interner, 0).ty(Interner) - } - _ => None, +impl<'a, 'db> Autoderef<'a, 'db, usize> { + pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Self::new_impl(table, base_ty) } } -pub(crate) fn deref_by_trait( - table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, - ty: Ty, - use_receiver_trait: bool, -) -> Option { - let _p = tracing::info_span!("deref_by_trait").entered(); - if table.structurally_resolve_type(&ty).inference_var(Interner).is_some() { - // don't try to deref unknown variables - return None; +impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { + fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Autoderef { + state: AutoderefSnapshot { + steps: Steps::default(), + cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty), + obligations: PredicateObligations::new(), + at_start: true, + reached_recursion_limit: false, + }, + table, + traits: None, + include_raw_pointers: false, + use_receiver_trait: false, + } } - let trait_id = || { - // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will - // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the - // blanked impl on `Deref`. - #[expect(clippy::overly_complex_bool_expr)] - if use_receiver_trait - && false - && let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) - { - return Some(receiver); + fn autoderef_traits(&mut self) -> Option { + match &mut self.traits { + Some(it) => Some(*it), + None => { + let traits = if self.use_receiver_trait { + AutoderefTraits { + trait_target: LangItem::ReceiverTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate) + .or_else(|| { + LangItem::DerefTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate) + })?, + } + } else { + AutoderefTraits { + trait_target: LangItem::DerefTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + } + }; + Some(*self.traits.insert(traits)) + } } - // Old rustc versions might not have `Receiver` trait. - // Fallback to `Deref` if they don't - LangItem::Deref.resolve_trait(db, table.trait_env.krate) - }; - let trait_id = trait_id()?; - let target = - trait_id.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; - - let projection = { - let b = TyBuilder::subst_for_def(db, trait_id, None); - if b.remaining() != 1 { - // the Target type + Deref trait should only have one generic parameter, - // namely Deref's Self type - return None; - } - let deref_subst = b.push(ty).build(); - TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build() - }; + } + + fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option> { + debug!("overloaded_deref_ty({:?})", ty); + let interner = self.table.interner; + + // , or whatever the equivalent trait is that we've been asked to walk. + let AutoderefTraits { trait_target } = self.autoderef_traits()?; + + let (normalized_ty, obligations) = structurally_normalize_ty( + self.table, + Ty::new_projection(interner, trait_target.into(), [ty]), + )?; + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); - // Check that the type implements Deref at all - let trait_ref = projection.trait_ref(db); - let implements_goal: Goal = trait_ref.cast(Interner); - if table.try_obligation(implements_goal.clone()).no_solution() { + Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty)) + } + + /// Returns the final type we ended up with, which may be an unresolved + /// inference variable. + pub(crate) fn final_ty(&self) -> Ty<'db> { + self.state.cur_ty + } + + pub(crate) fn step_count(&self) -> usize { + self.state.steps.len() + } + + pub(crate) fn take_obligations(&mut self) -> PredicateObligations<'db> { + std::mem::take(&mut self.state.obligations) + } + + pub(crate) fn steps(&self) -> &Steps { + &self.state.steps + } + + #[expect(dead_code)] + pub(crate) fn reached_recursion_limit(&self) -> bool { + self.state.reached_recursion_limit + } + + /// also dereference through raw pointer types + /// e.g., assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub(crate) fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + + /// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as + /// the trait and associated type to iterate, instead of + /// `core::ops::Deref` and `core::ops::Deref::Target` + pub(crate) fn use_receiver_trait(mut self) -> Self { + self.use_receiver_trait = true; + self + } +} + +fn structurally_normalize_ty<'db>( + table: &InferenceTable<'db>, + ty: Ty<'db>, +) -> Option<(Ty<'db>, PredicateObligations<'db>)> { + let mut ocx = ObligationCtxt::new(&table.infer_ctxt); + let Ok(normalized_ty) = + ocx.structurally_normalize_ty(&ObligationCause::misc(), table.param_env, ty) + else { + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. return None; + }; + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + unreachable!(); } - table.register_obligation(implements_goal.to_nextsolver(table.interner)); + Some((normalized_ty, ocx.into_pending_obligations())) +} + +pub(crate) fn overloaded_deref_ty<'db>( + table: &InferenceTable<'db>, + ty: Ty<'db>, +) -> Option>> { + let interner = table.interner; + + let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?; + + let (normalized_ty, obligations) = + structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?; - let result = table.normalize_projection_ty(projection); - Some(table.structurally_resolve_type(&result)) + Some(InferOk { value: normalized_ty, obligations }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 8af8fb73f344e..3755175cf5163 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -1,16 +1,12 @@ //! `TyBuilder`, a helper for building instances of `Ty` and related types. -use std::iter; - use chalk_ir::{ AdtId, DebruijnIndex, Scalar, cast::{Cast, CastTo, Caster}, fold::TypeFoldable, interner::HasInterner, }; -use hir_def::{ - DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType, -}; +use hir_def::{GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType}; use smallvec::SmallVec; use crate::{ @@ -246,47 +242,6 @@ impl TyBuilder<()> { TyBuilder::new((), params, parent_subst) } - /// Creates a `TyBuilder` to build `Substitution` for a coroutine defined in `parent`. - /// - /// A coroutine's substitution consists of: - /// - resume type of coroutine - /// - yield type of coroutine ([`Coroutine::Yield`](std::ops::Coroutine::Yield)) - /// - return type of coroutine ([`Coroutine::Return`](std::ops::Coroutine::Return)) - /// - generic parameters in scope on `parent` - /// - /// in this order. - /// - /// This method prepopulates the builder with placeholder substitution of `parent`, so you - /// should only push exactly 3 `GenericArg`s before building. - pub fn subst_for_coroutine(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> { - let parent_subst = - parent.as_generic_def_id(db).map(|p| generics(db, p).placeholder_subst(db)); - // These represent resume type, yield type, and return type of coroutine. - let params = std::iter::repeat_n(ParamKind::Type, 3).collect(); - TyBuilder::new((), params, parent_subst) - } - - pub fn subst_for_closure( - db: &dyn HirDatabase, - parent: DefWithBodyId, - sig_ty: Ty, - ) -> Substitution { - let sig_ty = sig_ty.cast(Interner); - let self_subst = iter::once(&sig_ty); - let Some(parent) = parent.as_generic_def_id(db) else { - return Substitution::from_iter(Interner, self_subst); - }; - Substitution::from_iter( - Interner, - generics(db, parent) - .placeholder_subst(db) - .iter(Interner) - .chain(self_subst) - .cloned() - .collect::>(), - ) - } - pub fn build(self) -> Substitution { let ((), subst) = self.build_internal(); subst diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 9f0ea14a8063d..1faf9f66dc547 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -210,7 +210,7 @@ impl TyExt for Ty { match self.kind(Interner) { TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)), - TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db), + TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty(db).callable_sig(db), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 0aec2b9dec7bc..448fc4aede037 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -283,6 +283,14 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: TyDefId, ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is + /// a `StructId` or `EnumVariantId` with a record constructor. + #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] + fn value_ty_ns<'db>( + &'db self, + def: ValueTyDefId, + ) -> Option>>; + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] fn type_for_type_alias_with_diagnostics_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 64c4cdeaddf31..3f04b72c2fc68 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -14,6 +14,7 @@ use hir_def::{ }; use span::Edition; +use crate::utils::TargetFeatureIsSafeInTarget; use crate::{ InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase, @@ -147,7 +148,7 @@ struct UnsafeVisitor<'db> { edition: Edition, /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when /// the target feature is not enabled. This flag encodes that. - target_feature_is_safe: bool, + target_feature_is_safe: TargetFeatureIsSafeInTarget, } impl<'db> UnsafeVisitor<'db> { @@ -167,7 +168,7 @@ impl<'db> UnsafeVisitor<'db> { let edition = krate.data(db).edition; let target_feature_is_safe = match &krate.workspace_data(db).target { Ok(target) => target_feature_is_safe_in_target(target), - Err(_) => false, + Err(_) => TargetFeatureIsSafeInTarget::No, }; Self { db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ea8bdf8bcbae4..7418e3bbeb49d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -38,14 +38,16 @@ use rustc_apfloat::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, RegionKind, - inherent::{AdtDef, IntoKind, SliceLike}, + AliasTyKind, CoroutineArgsParts, RegionKind, + inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike}, }; use smallvec::SmallVec; use span::Edition; use stdx::never; use triomphe::Arc; +use crate::next_solver::infer::DbInternerInferExt; +use crate::next_solver::infer::traits::ObligationCause; use crate::{ AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, @@ -789,11 +791,11 @@ fn render_const_scalar_ns( ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); - let ty = crate::next_solver::project::solve_normalize::normalize( - interner, - trait_env.env.to_nextsolver(interner), - ty, - ); + let infcx = interner.infer_ctxt().build(rustc_type_ir::TypingMode::PostAnalysis); + let ty = infcx + .at(&ObligationCause::new(), trait_env.env.to_nextsolver(interner)) + .deeply_normalize(ty) + .unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, trait_env) } @@ -1556,7 +1558,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } _ => (), } - let sig = ClosureSubst(&substs).sig_ty().callable_sig(db); + let sig = ClosureSubst(&substs).sig_ty(db).callable_sig(db); if let Some(sig) = sig { let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); @@ -1696,26 +1698,17 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { DisplaySourceCodeError::Coroutine, )); } - let subst = convert_args_for_result(interner, subst.as_slice()); - let subst = subst.as_slice(Interner); - let a: Option> = subst - .get(subst.len() - 3..) - .and_then(|args| args.iter().map(|arg| arg.ty(Interner)).collect()); + let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = + subst.split_coroutine_args(); + write!(f, "|")?; + resume_ty.hir_fmt(f)?; + write!(f, "|")?; - if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() { - write!(f, "|")?; - resume_ty.hir_fmt(f)?; - write!(f, "|")?; + write!(f, " yields ")?; + yield_ty.hir_fmt(f)?; - write!(f, " yields ")?; - yield_ty.hir_fmt(f)?; - - write!(f, " -> ")?; - ret_ty.hir_fmt(f)?; - } else { - // This *should* be unreachable, but fallback just in case. - write!(f, "{{coroutine}}")?; - } + write!(f, " -> ")?; + return_ty.hir_fmt(f)?; } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, TyKind::Pat(_, _) => write!(f, "{{pat}}")?, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index a7e942d92442c..f5c2f41069ea0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -120,7 +120,7 @@ pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc Arc { @@ -159,7 +164,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc, let mut table = unify::InferenceTable::new(db, trait_env); let ty_with_vars = table.normalize_associated_types_in(ty); - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); table.propagate_diverging_flag(); table.resolve_completely(ty_with_vars) } @@ -183,18 +188,14 @@ impl BindingMode { } } +// FIXME: Remove this `InferOk`, switch all code to the second one, that uses `Obligation` instead of `Goal`. #[derive(Debug)] pub(crate) struct InferOk<'db, T> { + #[allow(dead_code)] value: T, goals: Vec>>, } -impl<'db, T> InferOk<'db, T> { - fn map(self, f: impl FnOnce(T) -> U) -> InferOk<'db, U> { - InferOk { value: f(self.value), goals: self.goals } - } -} - #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -378,6 +379,26 @@ impl Adjustment { } } +/// At least for initial deployment, we want to limit two-phase borrows to +/// only a few specific cases. Right now, those are mostly "things that desugar" +/// into method calls: +/// - using `x.some_method()` syntax, where some_method takes `&mut self`, +/// - using `Foo::some_method(&mut x, ...)` syntax, +/// - binary assignment operators (`+=`, `-=`, `*=`, etc.). +/// +/// Anything else should be rejected until generalized two-phase borrow support +/// is implemented. Right now, dataflow can't handle the general case where there +/// is more than one use of a mutable borrow, and we don't want to accept too much +/// new code via two-phase borrows, so we try to limit where we create two-phase +/// capable mutable borrows. +/// See #49434 for tracking. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum AllowTwoPhase { + // FIXME: We should use this when appropriate. + Yes, + No, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Adjust { /// Go from ! to any type. @@ -393,8 +414,6 @@ pub enum Adjust { /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. -/// -/// Mutability is `None` when we are not sure. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct OverloadedDeref(pub Option); @@ -656,6 +675,7 @@ pub(crate) struct InferenceContext<'db> { /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, + target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, generic_def: GenericDefId, generics: OnceCell, table: unify::InferenceTable<'db>, @@ -673,11 +693,11 @@ pub(crate) struct InferenceContext<'db> { /// If `Some`, this stores coercion information for returned /// expressions. If `None`, this is in a context where return is /// inappropriate, such as a const expression. - return_coercion: Option, + return_coercion: Option>, /// The resume type and the yield type, respectively, of the coroutine being inferred. resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, - breakables: Vec, + breakables: Vec>, /// Whether we are inside the pattern of a destructuring assignment. inside_assignment: bool, @@ -692,21 +712,21 @@ pub(crate) struct InferenceContext<'db> { /// We do that because sometimes we truncate projections (when a closure captures /// both `a.b` and `a.b.c`), and we want to provide accurate spans in this case. current_capture_span_stack: Vec, - current_closure: Option, + current_closure: Option, /// Stores the list of closure ids that need to be analyzed before this closure. See the /// comment on `InferenceContext::sort_closures` - closure_dependencies: FxHashMap>, - deferred_closures: FxHashMap, ExprId)>>, + closure_dependencies: FxHashMap>, + deferred_closures: FxHashMap, ExprId)>>, diagnostics: Diagnostics, } #[derive(Clone, Debug)] -struct BreakableContext { +struct BreakableContext<'db> { /// Whether this context contains at least one break expression. may_break: bool, /// The coercion target of the context. - coerce: Option, + coerce: Option>, /// The optional label of the context. label: Option, kind: BreakableKind, @@ -721,10 +741,10 @@ enum BreakableKind { Border, } -fn find_breakable( - ctxs: &mut [BreakableContext], +fn find_breakable<'a, 'db>( + ctxs: &'a mut [BreakableContext<'db>], label: Option, -) -> Option<&mut BreakableContext> { +) -> Option<&'a mut BreakableContext<'db>> { let mut ctxs = ctxs .iter_mut() .rev() @@ -735,10 +755,10 @@ fn find_breakable( } } -fn find_continuable( - ctxs: &mut [BreakableContext], +fn find_continuable<'a, 'db>( + ctxs: &'a mut [BreakableContext<'db>], label: Option, -) -> Option<&mut BreakableContext> { +) -> Option<&'a mut BreakableContext<'db>> { match label { Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)), None => find_breakable(ctxs, label), @@ -759,6 +779,7 @@ impl<'db> InferenceContext<'db> { ) -> Self { let trait_env = db.trait_environment_for_body(owner); InferenceContext { + target_features: OnceCell::new(), generics: OnceCell::new(), result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env), @@ -794,18 +815,56 @@ impl<'db> InferenceContext<'db> { self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def)) } + #[inline] + fn krate(&self) -> Crate { + self.resolver.krate() + } + + fn target_features<'a>( + db: &dyn HirDatabase, + target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, + owner: DefWithBodyId, + krate: Crate, + ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) { + let (target_features, target_feature_is_safe) = target_features.get_or_init(|| { + let target_features = match owner { + DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())), + _ => TargetFeatures::default(), + }; + let target_feature_is_safe = match &krate.workspace_data(db).target { + Ok(target) => crate::utils::target_feature_is_safe_in_target(target), + Err(_) => TargetFeatureIsSafeInTarget::No, + }; + (target_features, target_feature_is_safe) + }); + (target_features, *target_feature_is_safe) + } + + #[inline] + pub(crate) fn set_tainted_by_errors(&mut self) { + self.result.has_errors = true; + } + // FIXME: This function should be private in module. It is currently only used in the consteval, since we need // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. - pub(crate) fn resolve_all(self) -> InferenceResult { + pub(crate) fn resolve_all(mut self) -> InferenceResult { + self.table.select_obligations_where_possible(); + self.table.fallback_if_possible(); + + // Comment from rustc: + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + let cast_checks = std::mem::take(&mut self.deferred_cast_checks); + for mut cast in cast_checks.into_iter() { + if let Err(diag) = cast.check(&mut self) { + self.diagnostics.push(diag); + } + } + let InferenceContext { - mut table, - mut result, - mut deferred_cast_checks, - tuple_field_accesses_rev, - diagnostics, - .. + mut table, mut result, tuple_field_accesses_rev, diagnostics, .. } = self; let mut diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we @@ -831,31 +890,12 @@ impl<'db> InferenceContext<'db> { closure_info: _, mutated_bindings_in_closure: _, tuple_field_access_types: _, - coercion_casts, + coercion_casts: _, diagnostics: _, } = &mut result; - table.resolve_obligations_as_possible(); - table.fallback_if_possible(); - - // Comment from rustc: - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - let mut apply_adjustments = |expr, adj: Vec<_>| { - expr_adjustments.insert(expr, adj.into_boxed_slice()); - }; - let mut set_coercion_cast = |expr| { - coercion_casts.insert(expr); - }; - for cast in deferred_cast_checks.iter_mut() { - if let Err(diag) = - cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast) - { - diagnostics.push(diag); - } - } // FIXME resolve obligations as well (use Guidance if necessary) - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); // make sure diverging type variables are marked as such table.propagate_diverging_flag(); @@ -1081,7 +1121,8 @@ impl<'db> InferenceContext<'db> { }; self.return_ty = self.process_user_written_ty(return_ty); - self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + self.return_coercion = + Some(CoerceMany::new(self.return_ty.to_nextsolver(self.table.interner))); // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. @@ -1117,8 +1158,12 @@ impl<'db> InferenceContext<'db> { fold_tys( t, |ty, _| { + let ty = self.table.structurally_resolve_type(&ty); let opaque_ty_id = match ty.kind(Interner) { - TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, + TyKind::OpaqueType(opaque_ty_id, _) + | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id, .. })) => { + *opaque_ty_id + } _ => return ty, }; let (impl_traits, idx) = @@ -1214,9 +1259,11 @@ impl<'db> InferenceContext<'db> { ty: &chalk_ir::Ty, outer_binder: DebruijnIndex, ) -> std::ops::ControlFlow { - let ty = self.table.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); - if let TyKind::OpaqueType(id, _) = ty.kind(Interner) + if let TyKind::OpaqueType(id, _) + | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id: id, .. })) = + ty.kind(Interner) && let ImplTraitId::TypeAliasImplTrait(alias_id, _) = self.db.lookup_intern_impl_trait_id((*id).into()) { @@ -1361,6 +1408,13 @@ impl<'db> InferenceContext<'db> { } } + fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[Ty]>) { + if adjustments.is_empty() { + return; + } + self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); + } + fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { self.result.method_resolutions.insert(expr, (func, subst)); } @@ -1587,24 +1641,14 @@ impl<'db> InferenceContext<'db> { self.table.process_remote_user_written_ty(ty) } - /// Recurses through the given type, normalizing associated types mentioned - /// in it by replacing them by type variables and registering obligations to - /// resolve later. This should be done once for every type we get from some - /// type annotation (e.g. from a let type annotation, field type or function - /// call). `make_ty` handles this already, but e.g. for field types we need - /// to do it as well. - fn normalize_associated_types_in(&mut self, ty: T) -> T - where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, - { - self.table.normalize_associated_types_in(ty) - } - fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { self.table.resolve_ty_shallow(ty) } + fn shallow_resolve(&self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { + self.table.shallow_resolve(ty) + } + fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option) -> Ty { self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs new file mode 100644 index 0000000000000..77b1ae6a94a46 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs @@ -0,0 +1,54 @@ +//! Autoderef helpers for inference. + +use std::iter; + +use crate::{ + Adjust, Adjustment, OverloadedDeref, + autoderef::{Autoderef, AutoderefKind}, + infer::unify::InferenceTable, + next_solver::{ + Ty, + infer::{InferOk, traits::PredicateObligations}, + mapping::NextSolverToChalk, + }, +}; + +impl<'db> InferenceTable<'db> { + pub(crate) fn autoderef(&mut self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> { + Autoderef::new(self, base_ty) + } +} + +impl<'db> Autoderef<'_, 'db> { + /// Returns the adjustment steps. + pub(crate) fn adjust_steps(mut self) -> Vec { + let infer_ok = self.adjust_steps_as_infer_ok(); + self.table.register_infer_ok(infer_ok) + } + + pub(crate) fn adjust_steps_as_infer_ok(&mut self) -> InferOk<'db, Vec> { + let steps = self.steps(); + if steps.is_empty() { + return InferOk { obligations: PredicateObligations::new(), value: vec![] }; + } + + let targets = steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.final_ty())); + let steps: Vec<_> = steps + .iter() + .map(|&(_source, kind)| { + if let AutoderefKind::Overloaded = kind { + Some(OverloadedDeref(Some(chalk_ir::Mutability::Not))) + } else { + None + } + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { + kind: Adjust::Deref(autoderef), + target: target.to_chalk(self.table.interner), + }) + .collect(); + + InferOk { obligations: self.take_obligations(), value: steps } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index bc3ee3c4c54d9..4cd6144a14cba 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -4,12 +4,14 @@ use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; use stdx::never; +use crate::infer::coerce::CoerceNever; use crate::{ - Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, QuantifiedWhereClauses, Ty, + TyExt, TyKind, TypeFlags, WhereClause, db::HirDatabase, from_chalk_trait_id, - infer::{coerce::CoerceNever, unify::InferenceTable}, + infer::{AllowTwoPhase, InferenceContext}, + next_solver::mapping::ChalkToNextSolver, }; #[derive(Debug)] @@ -93,23 +95,25 @@ impl CastCheck { Self { expr, source_expr, expr_ty, cast_ty } } - pub(super) fn check( + pub(super) fn check( &mut self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - set_coercion_cast: &mut G, - ) -> Result<(), InferenceDiagnostic> - where - F: FnMut(ExprId, Vec), - G: FnMut(ExprId), - { - self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); - self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + ctx: &mut InferenceContext<'_>, + ) -> Result<(), InferenceDiagnostic> { + self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); + self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); // This should always come first so that we apply the coercion, which impacts infer vars. - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); - set_coercion_cast(self.source_expr); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + self.cast_ty.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + ctx.result.coercion_casts.insert(self.source_expr); return Ok(()); } @@ -118,7 +122,7 @@ impl CastCheck { } if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) - && !table.is_sized(&self.cast_ty) + && !ctx.table.is_sized(&self.cast_ty) { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, @@ -133,30 +137,31 @@ impl CastCheck { return Ok(()); } - self.do_check(table, apply_adjustments) + self.do_check(ctx) .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) } - fn do_check( - &self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec), - { + fn do_check(&self, ctx: &mut InferenceContext<'_>) -> Result<(), CastError> { let (t_from, t_cast) = match ( - CastTy::from_ty(table.db, &self.expr_ty), - CastTy::from_ty(table.db, &self.cast_ty), + CastTy::from_ty(ctx.db, &self.expr_ty), + CastTy::from_ty(ctx.db, &self.cast_ty), ) { (Some(t_from), Some(t_cast)) => (t_from, t_cast), (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); - let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); + let sig = self.expr_ty.callable_sig(ctx.db).expect("FnDef had no sig"); + let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + fn_ptr.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { } else { return Err(CastError::IllegalCast); } @@ -176,11 +181,11 @@ impl CastCheck { }, // array-ptr-cast CastTy::Ptr(t, m) => { - let t = table.eagerly_normalize_and_resolve_shallow_in(t); - if !table.is_sized(&t) { + let t = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); + if !ctx.table.is_sized(&t) { return Err(CastError::IllegalCast); } - self.check_ref_cast(table, inner_ty, *mutbl, &t, m, apply_adjustments) + self.check_ref_cast(ctx, inner_ty, *mutbl, &t, m) } _ => Err(CastError::NonScalar), }; @@ -202,12 +207,10 @@ impl CastCheck { } (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), - (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => { - self.check_ptr_ptr_cast(table, &src, &dst) - } - (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), - (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), - (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, &src, &dst), + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, &src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, &dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, &dst), (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), @@ -215,26 +218,30 @@ impl CastCheck { } } - fn check_ref_cast( + fn check_ref_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, t_expr: &Ty, m_expr: Mutability, t_cast: &Ty, m_cast: Mutability, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec), - { + ) -> Result<(), CastError> { // Mutability order is opposite to rustc. `Mut < Not` if m_expr <= m_cast && let TyKind::Array(ety, _) = t_expr.kind(Interner) { // Coerce to a raw pointer so that we generate RawPtr in MIR. let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + array_ptr_type.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { } else { never!( "could not cast from reference to array to pointer to array ({:?} to {:?})", @@ -245,7 +252,16 @@ impl CastCheck { // This is a less strict condition than rustc's `demand_eqtype`, // but false negative is better than false positive - if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { + if ctx + .coerce( + self.source_expr.into(), + ety.to_nextsolver(ctx.table.interner), + t_cast.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { return Ok(()); } } @@ -255,12 +271,12 @@ impl CastCheck { fn check_ptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, src: &Ty, dst: &Ty, ) -> Result<(), CastError> { - let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; - let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), @@ -285,9 +301,9 @@ impl CastCheck { return Ok(()); } let src_principal = - table.db.trait_signature(from_chalk_trait_id(src_principal)); + ctx.db.trait_signature(from_chalk_trait_id(src_principal)); let dst_principal = - table.db.trait_signature(from_chalk_trait_id(dst_principal)); + ctx.db.trait_signature(from_chalk_trait_id(dst_principal)); if src_principal.flags.contains(TraitFlags::AUTO) && dst_principal.flags.contains(TraitFlags::AUTO) { @@ -306,10 +322,10 @@ impl CastCheck { fn check_ptr_addr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, expr_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -320,10 +336,10 @@ impl CastCheck { fn check_addr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, cast_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -336,10 +352,10 @@ impl CastCheck { fn check_fptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, cast_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -362,10 +378,10 @@ enum PointerKind { Error, } -fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { - let ty = table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); +fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result, ()> { + let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); - if table.is_sized(&ty) { + if ctx.table.is_sized(&ty) { return Ok(Some(PointerKind::Thin)); } @@ -378,11 +394,11 @@ fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result) -> Result { match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { None => Ok(Some(PointerKind::Thin)), - Some(ty) => pointer_kind(ty, table), + Some(ty) => pointer_kind(ty, ctx), } } TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index fd7e5a6a0e121..1d5d8dd13edd4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -1,143 +1,168 @@ //! Inference of closure parameter types based on the closure's expected type. -use std::{cmp, convert::Infallible, mem, ops::ControlFlow}; +pub(crate) mod analysis; + +use std::ops::ControlFlow; +use std::{iter, mem}; -use chalk_ir::{ - BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind, - cast::Cast, - fold::{FallibleTypeFolder, Shift, TypeFoldable}, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; -use either::Either; use hir_def::{ - DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, - expr_store::path::Path, - hir::{ - Array, AsmOperand, BinaryOp, BindingId, CaptureBy, ClosureKind, Expr, ExprId, ExprOrPatId, - Pat, PatId, Statement, UnaryOp, - }, - item_tree::FieldsShape, + TraitId, + hir::{ClosureKind, ExprId, PatId}, lang_item::LangItem, - resolver::ValueNs, type_ref::TypeRefId, }; -use hir_def::{ItemContainerId, Lookup, TraitId}; -use hir_expand::name::Name; -use intern::sym; -use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::{SmallVec, smallvec}; -use stdx::{format_to, never}; -use syntax::utils::is_raw_identifier; +use rustc_type_ir::{ + ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, Interner, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, + inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _}, +}; +use tracing::debug; +use crate::traits::FnTrait; use crate::{ - Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ClosureId, DynTy, DynTyExt, FnAbi, - FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, Substitution, Ty, - TyBuilder, TyExt, WhereClause, - db::{HirDatabase, InternedClosure, InternedCoroutine}, - error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, - generics::Generics, - infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, - make_binders, - mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - next_solver::mapping::ChalkToNextSolver, - to_assoc_type_id, - traits::FnTrait, - utils::{self, elaborate_clause_supertraits}, + FnAbi, + db::{InternedClosure, InternedCoroutine}, + infer::{BreakableKind, Diverges, coerce::CoerceMany}, + next_solver::{ + AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, + abi::Safety, + infer::{ + BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult, + traits::{ObligationCause, PredicateObligations}, + }, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + util::explicit_item_bounds, + }, }; use super::{Expectation, InferenceContext}; #[derive(Debug)] -pub(super) struct ClosureSignature { - pub(super) ret_ty: Ty, - pub(super) expected_sig: FnPointer, +struct ClosureSignatures<'tcx> { + /// The signature users of the closure see. + bound_sig: PolyFnSig<'tcx>, + /// The signature within the function body. + /// This mostly differs in the sense that lifetimes are now early bound and any + /// opaque types from the signature expectation are overridden in case there are + /// explicit hidden types written by the user in the closure signature. + liberated_sig: FnSig<'tcx>, } impl<'db> InferenceContext<'db> { pub(super) fn infer_closure( &mut self, - body: &ExprId, + body: ExprId, args: &[PatId], - ret_type: &Option, + ret_type: Option, arg_types: &[Option], closure_kind: ClosureKind, tgt_expr: ExprId, expected: &Expectation, - ) -> Ty { + ) -> crate::Ty { assert_eq!(args.len(), arg_types.len()); + let interner = self.table.interner; let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { - Some(expected_ty) => self.deduce_closure_signature(&expected_ty, closure_kind), + Some(expected_ty) => { + self.deduce_closure_signature(expected_ty.to_nextsolver(interner), closure_kind) + } None => (None, None), }; - let ClosureSignature { expected_sig: mut bound_sig, ret_ty: body_ret_ty } = - self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig); - bound_sig.substitution.0 = self - .normalize_associated_types_in::<_, crate::next_solver::GenericArgs<'db>>( - bound_sig.substitution.0, - ); - let bound_sig = bound_sig; - let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner); + let ClosureSignatures { bound_sig, liberated_sig } = + self.sig_of_closure(arg_types, ret_type, expected_sig); + let body_ret_ty = bound_sig.output().skip_binder(); + let sig_ty = Ty::new_fn_ptr(interner, bound_sig); + let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); let (id, ty, resume_yield_tys) = match closure_kind { ClosureKind::Coroutine(_) => { - let sig_tys = bound_sig.substitution.0.as_slice(Interner); - // FIXME: report error when there are more than 1 parameter. - let resume_ty = match sig_tys.first() { - // When `sig_tys.len() == 1` the first type is the return type, not the - // first parameter type. - Some(ty) if sig_tys.len() > 1 => ty.assert_ty_ref(Interner).clone(), - _ => self.result.standard_types.unit.clone(), + let yield_ty = self.table.next_ty_var(); + let resume_ty = liberated_sig + .inputs() + .get(0) + .unwrap_or(self.result.standard_types.unit.to_nextsolver(interner)); + + // FIXME: Infer the upvars later. + let parts = CoroutineArgsParts { + parent_args, + kind_ty: Ty::new_unit(interner), + resume_ty, + yield_ty, + return_ty: body_ret_ty, + tupled_upvars_ty: Ty::new_unit(interner), }; - let yield_ty = self.table.new_type_var(); - - let subst = TyBuilder::subst_for_coroutine(self.db, self.owner) - .push(resume_ty.clone()) - .push(yield_ty.clone()) - .push(body_ret_ty.clone()) - .build(); let coroutine_id = self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into(); - let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner); + let coroutine_ty = Ty::new_coroutine( + interner, + coroutine_id, + CoroutineArgs::new(interner, parts).args, + ); - (None, coroutine_ty, Some((resume_ty, yield_ty))) + ( + None, + coroutine_ty, + Some((resume_ty.to_chalk(interner), yield_ty.to_chalk(interner))), + ) } + // FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`. + // But for now we treat it as a closure. ClosureKind::Closure | ClosureKind::Async => { - let closure_id = - self.db.intern_closure(InternedClosure(self.owner, tgt_expr)).into(); - let closure_ty = TyKind::Closure( - closure_id, - TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()), - ) - .intern(Interner); + let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr)); + match expected_kind { + Some(kind) => { + self.result.closure_info.insert( + closure_id.into(), + ( + Vec::new(), + match kind { + rustc_type_ir::ClosureKind::Fn => FnTrait::Fn, + rustc_type_ir::ClosureKind::FnMut => FnTrait::FnMut, + rustc_type_ir::ClosureKind::FnOnce => FnTrait::FnOnce, + }, + ), + ); + } + None => {} + }; + // FIXME: Infer the kind and the upvars later when needed. + let parts = ClosureArgsParts { + parent_args, + closure_kind_ty: Ty::from_closure_kind( + interner, + expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn), + ), + closure_sig_as_fn_ptr_ty: sig_ty, + tupled_upvars_ty: Ty::new_unit(interner), + }; + let closure_ty = Ty::new_closure( + interner, + closure_id.into(), + ClosureArgs::new(interner, parts).args, + ); self.deferred_closures.entry(closure_id).or_default(); self.add_current_closure_dependency(closure_id); (Some(closure_id), closure_ty, None) } }; - // Eagerly try to relate the closure type with the expected - // type, otherwise we often won't have enough information to - // infer the body. - self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected, expected_kind); - // Now go through the argument patterns - for (arg_pat, arg_ty) in args.iter().zip(bound_sig.substitution.0.as_slice(Interner).iter()) - { - self.infer_top_pat(*arg_pat, arg_ty.assert_ty_ref(Interner), None); + for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { + self.infer_top_pat(*arg_pat, &arg_ty.to_chalk(interner), None); } // FIXME: lift these out into a struct let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_closure = mem::replace(&mut self.current_closure, id); - let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.clone()); + let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.to_chalk(interner)); let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty)); let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys); self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_return(*body); + this.infer_return(body); }); self.diverges = prev_diverges; @@ -146,1696 +171,644 @@ impl<'db> InferenceContext<'db> { self.current_closure = prev_closure; self.resume_yield_tys = prev_resume_yield_tys; - self.table.normalize_associated_types_in(ty) + ty.to_chalk(interner) } - // This function handles both closures and coroutines. - pub(super) fn deduce_closure_type_from_expectations( - &mut self, - closure_expr: ExprId, - closure_ty: &Ty, - sig_ty: &Ty, - expectation: &Expectation, - expected_kind: Option, - ) { - let expected_ty = match expectation.to_option(&mut self.table) { - Some(ty) => ty, - None => return, - }; - - match (closure_ty.kind(Interner), expected_kind) { - (TyKind::Closure(closure_id, _), Some(closure_kind)) => { - self.result - .closure_info - .entry(*closure_id) - .or_insert_with(|| (Vec::new(), closure_kind)); - } - _ => {} - } - - // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. - let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, CoerceNever::Yes); - - // Coroutines are not Fn* so return early. - if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) { - return; + fn fn_trait_kind_from_def_id(&self, trait_id: TraitId) -> Option { + let lang_item = self.db.lang_attr(trait_id.into())?; + match lang_item { + LangItem::Fn => Some(rustc_type_ir::ClosureKind::Fn), + LangItem::FnMut => Some(rustc_type_ir::ClosureKind::FnMut), + LangItem::FnOnce => Some(rustc_type_ir::ClosureKind::FnOnce), + _ => None, } + } - // Deduction based on the expected `dyn Fn` is done separately. - if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) - && let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) - { - let expected_sig_ty = TyKind::Function(sig).intern(Interner); - - self.unify(sig_ty, &expected_sig_ty); + fn async_fn_trait_kind_from_def_id( + &self, + trait_id: TraitId, + ) -> Option { + let lang_item = self.db.lang_attr(trait_id.into())?; + match lang_item { + LangItem::AsyncFn => Some(rustc_type_ir::ClosureKind::Fn), + LangItem::AsyncFnMut => Some(rustc_type_ir::ClosureKind::FnMut), + LangItem::AsyncFnOnce => Some(rustc_type_ir::ClosureKind::FnOnce), + _ => None, } } - // Closure kind deductions are mostly from `rustc_hir_typeck/src/closure.rs`. - // Might need to port closure sig deductions too. - pub(super) fn deduce_closure_signature( + /// Given the expected type, figures out what it can about this closure we + /// are about to type check: + fn deduce_closure_signature( &mut self, - expected_ty: &Ty, + expected_ty: Ty<'db>, closure_kind: ClosureKind, - ) -> (Option>, Option) { - match expected_ty.kind(Interner) { - TyKind::Alias(AliasTy::Opaque(OpaqueTy { .. })) | TyKind::OpaqueType(..) => { - let clauses = expected_ty.impl_trait_bounds(self.db).into_iter().flatten().map( - |b: chalk_ir::Binders>| { - b.into_value_and_skipped_binders().0 - }, - ); - self.deduce_closure_kind_from_predicate_clauses(expected_ty, clauses, closure_kind) - } - TyKind::Dyn(dyn_ty) => { - let sig = - dyn_ty.bounds.skip_binders().as_slice(Interner).iter().find_map(|bound| { - if let WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - ty: projected_ty, - }) = bound.skip_binders() - && let Some(sig) = self.deduce_sig_from_projection( - closure_kind, - projection_ty, - projected_ty, - ) - { - return Some(sig); - } - None - }); - - let kind = dyn_ty.principal().and_then(|principal_trait_ref| { - self.fn_trait_kind_from_trait_id(from_chalk_trait_id( - principal_trait_ref.skip_binders().skip_binders().trait_id, - )) + ) -> (Option>, Option) { + match expected_ty.kind() { + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id, args, .. }) => self + .deduce_closure_signature_from_predicates( + expected_ty, + closure_kind, + explicit_item_bounds(self.table.interner, def_id) + .iter_instantiated(self.table.interner, args) + .map(|clause| clause.as_predicate()), + ), + TyKind::Dynamic(object_type, ..) => { + let sig = object_type.projection_bounds().into_iter().find_map(|pb| { + let pb = + pb.with_self_ty(self.table.interner, Ty::new_unit(self.table.interner)); + self.deduce_sig_from_projection(closure_kind, pb) }); - + let kind = object_type + .principal_def_id() + .and_then(|did| self.fn_trait_kind_from_def_id(did.0)); (sig, kind) } - TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => { - let clauses = self.clauses_for_self_ty(*ty); - self.deduce_closure_kind_from_predicate_clauses( - expected_ty, - clauses.into_iter(), + TyKind::Infer(rustc_type_ir::TyVar(vid)) => self + .deduce_closure_signature_from_predicates( + Ty::new_var(self.table.interner, self.table.infer_ctxt.root_var(vid)), closure_kind, - ) - } - TyKind::Function(fn_ptr) => match closure_kind { - ClosureKind::Closure => (Some(fn_ptr.substitution.clone()), Some(FnTrait::Fn)), - ClosureKind::Async | ClosureKind::Coroutine(_) => (None, None), + self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), + ), + TyKind::FnPtr(sig_tys, hdr) => match closure_kind { + ClosureKind::Closure => { + let expected_sig = sig_tys.with(hdr); + (Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn)) + } + ClosureKind::Coroutine(_) | ClosureKind::Async => (None, None), }, _ => (None, None), } } - fn deduce_closure_kind_from_predicate_clauses( + fn deduce_closure_signature_from_predicates( &mut self, - expected_ty: &Ty, - clauses: impl DoubleEndedIterator, + expected_ty: Ty<'db>, closure_kind: ClosureKind, - ) -> (Option>, Option) { + predicates: impl DoubleEndedIterator>, + ) -> (Option>, Option) { let mut expected_sig = None; let mut expected_kind = None; - for clause in elaborate_clause_supertraits(self.db, clauses.rev()) { + for pred in rustc_type_ir::elaborate::elaborate( + self.table.interner, + // Reverse the obligations here, since `elaborate_*` uses a stack, + // and we want to keep inference generally in the same order of + // the registered obligations. + predicates.rev(), + ) + // We only care about self bounds + .filter_only_self() + { + debug!(?pred); + let bound_predicate = pred.kind(); + + // Given a Projection predicate, we can potentially infer + // the complete signature. if expected_sig.is_none() - && let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = - &clause + && let PredicateKind::Clause(ClauseKind::Projection(proj_predicate)) = + bound_predicate.skip_binder() { - let inferred_sig = self.deduce_sig_from_projection(closure_kind, projection, ty); + let inferred_sig = self.deduce_sig_from_projection( + closure_kind, + bound_predicate.rebind(proj_predicate), + ); + // Make sure that we didn't infer a signature that mentions itself. // This can happen when we elaborate certain supertrait bounds that - // mention projections containing the `Self` type. See rust-lang/rust#105401. - struct MentionsTy<'a> { - expected_ty: &'a Ty, + // mention projections containing the `Self` type. See #105401. + struct MentionsTy<'db> { + expected_ty: Ty<'db>, } - impl TypeVisitor for MentionsTy<'_> { - type BreakTy = (); - - fn interner(&self) -> Interner { - Interner - } + impl<'db> TypeVisitor> for MentionsTy<'db> { + type Result = ControlFlow<()>; - fn as_dyn( - &mut self, - ) -> &mut dyn TypeVisitor - { - self - } - - fn visit_ty(&mut self, t: &Ty, db: chalk_ir::DebruijnIndex) -> ControlFlow<()> { + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { if t == self.expected_ty { ControlFlow::Break(()) } else { - t.super_visit_with(self, db) + t.super_visit_with(self) } } } - if inferred_sig - .visit_with(&mut MentionsTy { expected_ty }, chalk_ir::DebruijnIndex::INNERMOST) - .is_continue() - { - expected_sig = inferred_sig; - } - } - let trait_id = match clause { - WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), .. - }) => projection.trait_(self.db), - WhereClause::Implemented(trait_ref) => from_chalk_trait_id(trait_ref.trait_id), - _ => continue, - }; - if let Some(closure_kind) = self.fn_trait_kind_from_trait_id(trait_id) { - // always use the closure kind that is more permissive. - match (expected_kind, closure_kind) { - (None, _) => expected_kind = Some(closure_kind), - (Some(FnTrait::FnMut), FnTrait::Fn) => expected_kind = Some(FnTrait::Fn), - (Some(FnTrait::FnOnce), FnTrait::Fn | FnTrait::FnMut) => { - expected_kind = Some(closure_kind) + // Don't infer a closure signature from a goal that names the closure type as this will + // (almost always) lead to occurs check errors later in type checking. + if let Some(inferred_sig) = inferred_sig { + // In the new solver it is difficult to explicitly normalize the inferred signature as we + // would have to manually handle universes and rewriting bound vars and placeholders back + // and forth. + // + // Instead we take advantage of the fact that we relating an inference variable with an alias + // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: + // - Create some new variable `?sig` + // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` + // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, + // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. + // + // *: In cases where there are ambiguous aliases in the signature that make use of bound vars + // they will wind up present in `?sig` even though they are non-rigid. + // + // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` + // even though the normalized form may not name `expected_ty`. However, this matches the existing + // behaviour of the old solver and would be technically a breaking change to fix. + let generalized_fnptr_sig = self.table.next_ty_var(); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.table.interner, inferred_sig); + // FIXME: Report diagnostics. + _ = self + .table + .infer_ctxt + .at(&ObligationCause::new(), self.table.param_env) + .eq(DefineOpaqueTypes::Yes, inferred_fnptr_sig, generalized_fnptr_sig) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); + + let resolved_sig = + self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); + + if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = Some(resolved_sig.fn_sig(self.table.interner)); } - _ => {} + } else if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; } } - } - - (expected_sig, expected_kind) - } - - fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option { - // Search for a predicate like `<$self as FnX>::Output == Ret` - let fn_traits: SmallVec<[TraitId; 3]> = - utils::fn_traits(self.db, self.owner.module(self.db).krate()).collect(); - - let self_ty = self.result.standard_types.unknown.clone(); - let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]); - for bound in bounds.iter(Interner) { - // NOTE(skip_binders): the extracted types are rebound by the returned `FnPointer` - if let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = - bound.skip_binders() - { - let trait_ = - match from_assoc_type_id(projection.associated_ty_id).lookup(self.db).container - { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - if !fn_traits.contains(&trait_) { - return None; + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur when we elaborate a predicate + // like `F : Fn`. Note that due to subtyping we could encounter + // many viable options, so pick the most restrictive. + let trait_def_id = match bound_predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(data)) => { + Some(data.projection_term.trait_def_id(self.table.interner).0) } + PredicateKind::Clause(ClauseKind::Trait(data)) => Some(data.def_id().0), + _ => None, + }; + + if let Some(trait_def_id) = trait_def_id { + let found_kind = match closure_kind { + ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id), + ClosureKind::Async => self + .async_fn_trait_kind_from_def_id(trait_def_id) + .or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)), + _ => None, + }; - // Skip `Self`, get the type argument. - let arg = projection.substitution.as_slice(Interner).get(1)?; - if let Some(subst) = arg.ty(Interner)?.as_tuple() { - let generic_args = subst.as_slice(Interner); - let mut sig_tys = Vec::with_capacity(generic_args.len() + 1); - for arg in generic_args { - sig_tys.push(arg.ty(Interner)?.clone()); + if let Some(found_kind) = found_kind { + // always use the closure kind that is more permissive. + match (expected_kind, found_kind) { + (None, _) => expected_kind = Some(found_kind), + ( + Some(rustc_type_ir::ClosureKind::FnMut), + rustc_type_ir::ClosureKind::Fn, + ) => expected_kind = Some(rustc_type_ir::ClosureKind::Fn), + ( + Some(rustc_type_ir::ClosureKind::FnOnce), + rustc_type_ir::ClosureKind::Fn | rustc_type_ir::ClosureKind::FnMut, + ) => expected_kind = Some(found_kind), + _ => {} } - sig_tys.push(ty.clone()); - - cov_mark::hit!(dyn_fn_param_informs_call_site_closure_signature); - return Some(FnPointer { - num_binders: bound.len(Interner), - sig: FnSig { - abi: FnAbi::RustCall, - safety: chalk_ir::Safety::Safe, - variadic: false, - }, - substitution: FnSubst(Substitution::from_iter(Interner, sig_tys)), - }); } } } - None + (expected_sig, expected_kind) } + /// Given a projection like "::Result == Y", we can deduce + /// everything we need to know about a closure or coroutine. + /// + /// The `cause_span` should be the span that caused us to + /// have this expected signature, or `None` if we can't readily + /// know that. fn deduce_sig_from_projection( &mut self, closure_kind: ClosureKind, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let container = - from_assoc_type_id(projection_ty.associated_ty_id).lookup(self.db).container; - let trait_ = match container { - hir_def::ItemContainerId::TraitId(trait_) => trait_, - _ => return None, - }; + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let SolverDefId::TypeAliasId(def_id) = projection.item_def_id() else { unreachable!() }; + let lang_item = self.db.lang_attr(def_id.into()); // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits, // for closures and async closures, respectively. - let fn_trait_kind = self.fn_trait_kind_from_trait_id(trait_)?; - if !matches!(closure_kind, ClosureKind::Closure | ClosureKind::Async) { - return None; - } - if fn_trait_kind.is_async() { - // If the expected trait is `AsyncFn(...) -> X`, we don't know what the return type is, - // but we do know it must implement `Future`. - self.extract_async_fn_sig_from_projection(projection_ty, projected_ty) - } else { - self.extract_sig_from_projection(projection_ty, projected_ty) + match closure_kind { + ClosureKind::Closure if lang_item == Some(LangItem::FnOnceOutput) => { + self.extract_sig_from_projection(projection) + } + ClosureKind::Async if lang_item == Some(LangItem::AsyncFnOnceOutput) => { + self.extract_sig_from_projection(projection) + } + // It's possible we've passed the closure to a (somewhat out-of-fashion) + // `F: FnOnce() -> Fut, Fut: Future` style bound. Let's still + // guide inference here, since it's beneficial for the user. + ClosureKind::Async if lang_item == Some(LangItem::FnOnceOutput) => { + self.extract_sig_from_projection_and_future_bound(projection) + } + _ => None, } } + /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args + /// and return type to infer a [`ty::PolyFnSig`] for the closure. fn extract_sig_from_projection( &self, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); - - let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { - return None; - }; + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); - let ret_param_ty = projected_ty; + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); - Some(FnSubst(Substitution::from_iter( - Interner, - input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(ret_param_ty.clone()), - ))), - ))) - } - - fn extract_async_fn_sig_from_projection( - &mut self, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); - - let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { + let TyKind::Tuple(input_tys) = arg_param_ty.kind() else { return None; }; - let ret_param_future_output = projected_ty; - let ret_param_future = self.table.new_type_var(); - let future_output = - LangItem::FutureOutput.resolve_type_alias(self.db, self.resolver.krate())?; - let future_projection = crate::AliasTy::Projection(crate::ProjectionTy { - associated_ty_id: to_assoc_type_id(future_output), - substitution: Substitution::from1(Interner, ret_param_future.clone()), - }); - let goal: crate::Goal = - crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } - .cast(Interner); - self.table.register_obligation(goal.to_nextsolver(self.table.interner)); - - Some(FnSubst(Substitution::from_iter( - Interner, - input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(ret_param_future), - ))), - ))) - } + // Since this is a return parameter type it is safe to unwrap. + let ret_param_ty = projection.skip_binder().term.expect_type(); + debug!(?ret_param_ty); + + let sig = projection.rebind(self.table.interner.mk_fn_sig( + input_tys, + ret_param_ty, + false, + Safety::Safe, + FnAbi::Rust, + )); - fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option { - FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?) + Some(sig) } - fn supplied_sig_of_closure( + /// When an async closure is passed to a function that has a "two-part" `Fn` + /// and `Future` trait bound, like: + /// + /// ```rust + /// use std::future::Future; + /// + /// fn not_exactly_an_async_closure(_f: F) + /// where + /// F: FnOnce(String, u32) -> Fut, + /// Fut: Future, + /// {} + /// ``` + /// + /// The we want to be able to extract the signature to guide inference in the async + /// closure. We will have two projection predicates registered in this case. First, + /// we identify the `FnOnce` bound, and if the output type is + /// an inference variable `?Fut`, we check if that is bounded by a `Future` + /// projection. + /// + /// This function is actually best-effort with the return type; if we don't find a + /// `Future` projection, we still will return arguments that we extracted from the `FnOnce` + /// projection, and the output will be an unconstrained type variable instead. + fn extract_sig_from_projection_and_future_bound( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - ) -> ClosureSignature { - let mut sig_tys = Vec::with_capacity(arg_types.len() + 1); - - // collect explicitly written argument types - for arg_type in arg_types.iter() { - let arg_ty = match arg_type { - // FIXME: I think rustc actually lowers closure params with `LifetimeElisionKind::AnonymousCreateParameter` - // (but the return type with infer). - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - sig_tys.push(arg_ty); - } + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); - // add return type - let ret_ty = match ret_type { - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - if let ClosureKind::Async = closure_kind { - sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); - } else { - sig_tys.push(ret_ty.clone()); - } + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); - let expected_sig = FnPointer { - num_binders: 0, - sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false }, - substitution: FnSubst( - Substitution::from_iter(Interner, sig_tys.iter().cloned()).shifted_in(Interner), - ), + let TyKind::Tuple(input_tys) = arg_param_ty.kind() else { + return None; }; - ClosureSignature { ret_ty, expected_sig } - } + // If the return type is a type variable, look for bounds on it. + // We could theoretically support other kinds of return types here, + // but none of them would be useful, since async closures return + // concrete anonymous future types, and their futures are not coerced + // into any other type within the body of the async closure. + let TyKind::Infer(rustc_type_ir::TyVar(return_vid)) = + projection.skip_binder().term.expect_type().kind() + else { + return None; + }; - /// The return type is the signature of the closure, and the return type - /// *as represented inside the body* (so, for async closures, the `Output` ty) - pub(super) fn sig_of_closure( + // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. + let mut return_ty = None; + for bound in self.table.obligations_for_self_ty(return_vid) { + if let PredicateKind::Clause(ClauseKind::Projection(ret_projection)) = + bound.predicate.kind().skip_binder() + && let ret_projection = bound.predicate.kind().rebind(ret_projection) + && let Some(ret_projection) = ret_projection.no_bound_vars() + && let SolverDefId::TypeAliasId(assoc_type) = ret_projection.def_id() + && self.db.lang_attr(assoc_type.into()) == Some(LangItem::FutureOutput) + { + return_ty = Some(ret_projection.term.expect_type()); + break; + } + } + + // SUBTLE: If we didn't find a `Future` bound for the return + // vid, we still want to attempt to provide inference guidance for the async + // closure's arguments. Instantiate a new vid to plug into the output type. + // + // You may be wondering, what if it's higher-ranked? Well, given that we + // found a type variable for the `FnOnce::Output` projection above, we know + // that the output can't mention any of the vars. + // + // Also note that we use a fresh var here for the signature since the signature + // records the output of the *future*, and `return_vid` above is the type + // variable of the future, not its output. + // + // FIXME: We probably should store this signature inference output in a way + // that does not misuse a `FnSig` type, but that can be done separately. + let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); + + let sig = projection.rebind(self.table.interner.mk_fn_sig( + input_tys, + return_ty, + false, + Safety::Safe, + FnAbi::Rust, + )); + + Some(sig) + } + + fn sig_of_closure( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: Option>, - ) -> ClosureSignature { + decl_inputs: &[Option], + decl_output: Option, + expected_sig: Option>, + ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(body, ret_type, arg_types, closure_kind, e) + self.sig_of_closure_with_expectation(decl_inputs, decl_output, e) } else { - self.sig_of_closure_no_expectation(body, ret_type, arg_types, closure_kind) + self.sig_of_closure_no_expectation(decl_inputs, decl_output) } } + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. fn sig_of_closure_no_expectation( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - ) -> ClosureSignature { - self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind) - } - - fn sig_of_closure_with_expectation( - &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: FnSubst, - ) -> ClosureSignature { - let expected_sig = FnPointer { - num_binders: 0, - sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false }, - substitution: expected_sig, - }; - - // If the expected signature does not match the actual arg types, - // then just return the expected signature - if expected_sig.substitution.0.len(Interner) != arg_types.len() + 1 { - let ret_ty = match ret_type { - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - return ClosureSignature { expected_sig, ret_ty }; - } - - self.merge_supplied_sig_with_expectation( - body, - ret_type, - arg_types, - closure_kind, - expected_sig, - ) - } - - fn merge_supplied_sig_with_expectation( - &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: FnPointer, - ) -> ClosureSignature { - let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); - - let snapshot = self.table.snapshot(); - if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>( - &expected_sig.substitution.0, - &supplied_sig.expected_sig.substitution.0, - ) { - self.table.rollback_to(snapshot); - } - - supplied_sig - } -} - -// The below functions handle capture and closure kind (Fn, FnMut, ..) - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct HirPlace { - pub(crate) local: BindingId, - pub(crate) projections: Vec>, -} - -impl HirPlace { - fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { - let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); - for p in &self.projections { - ty = p.projected_ty( - ty, - ctx.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - ctx.owner.module(ctx.db).krate(), - ); - } - ty - } + decl_inputs: &[Option], + decl_output: Option, + ) -> ClosureSignatures<'db> { + let bound_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); - fn capture_kind_of_truncated_place( - &self, - mut current_capture: CaptureKind, - len: usize, - ) -> CaptureKind { - if let CaptureKind::ByRef(BorrowKind::Mut { - kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, - }) = current_capture - && self.projections[len..].contains(&ProjectionElem::Deref) - { - current_capture = - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); - } - current_capture + self.closure_sigs(bound_sig) } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum CaptureKind { - ByRef(BorrowKind), - ByValue, -} -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CapturedItem { - pub(crate) place: HirPlace, - pub(crate) kind: CaptureKind, - /// The inner vec is the stacks; the outer vec is for each capture reference. + /// Invoked to compute the signature of a closure expression. This + /// combines any user-provided type annotations (e.g., `|x: u32| + /// -> u32 { .. }`) with the expected signature. /// - /// Even though we always report only the last span (i.e. the most inclusive span), - /// we need to keep them all, since when a closure occurs inside a closure, we - /// copy all captures of the inner closure to the outer closure, and then we may - /// truncate them, and we want the correct span to be reported. - span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, - pub(crate) ty: Binders, -} - -impl CapturedItem { - pub fn local(&self) -> BindingId { - self.place.local - } - - /// Returns whether this place has any field (aka. non-deref) projections. - pub fn has_field_projections(&self) -> bool { - self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref)) - } - - pub fn ty(&self, subst: &Substitution) -> Ty { - self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst()) - } - - pub fn kind(&self) -> CaptureKind { - self.kind - } - - pub fn spans(&self) -> SmallVec<[MirSpan; 3]> { - self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect() - } - - /// Converts the place to a name that can be inserted into source code. - pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let mut result = body[self.place.local].name.as_str().to_owned(); - for proj in &self.place.projections { - match proj { - ProjectionElem::Deref => {} - ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.fields(db); - match variant_data.shape { - FieldsShape::Record => { - result.push('_'); - result.push_str(variant_data.fields()[f.local_id].name.as_str()) - } - FieldsShape::Tuple => { - let index = - variant_data.fields().iter().position(|it| it.0 == f.local_id); - if let Some(index) = index { - format_to!(result, "_{index}"); - } - } - FieldsShape::Unit => {} - } - } - ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index), - &ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"), - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } - } - } - if is_raw_identifier(&result, owner.module(db).krate().data(db).edition) { - result.insert_str(0, "r#"); - } - result - } - - pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let krate = owner.krate(db); - let edition = krate.data(db).edition; - let mut result = body[self.place.local].name.display(db, edition).to_string(); - for proj in &self.place.projections { - match proj { - // In source code autoderef kicks in. - ProjectionElem::Deref => {} - ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.fields(db); - match variant_data.shape { - FieldsShape::Record => format_to!( - result, - ".{}", - variant_data.fields()[f.local_id].name.display(db, edition) - ), - FieldsShape::Tuple => format_to!( - result, - ".{}", - variant_data - .fields() - .iter() - .position(|it| it.0 == f.local_id) - .unwrap_or_default() - ), - FieldsShape::Unit => {} - } - } - ProjectionElem::Field(Either::Right(f)) => { - let field = f.index; - format_to!(result, ".{field}"); - } - &ProjectionElem::ClosureField(field) => { - format_to!(result, ".{field}"); - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } - } - } - let final_derefs_count = self - .place - .projections - .iter() - .rev() - .take_while(|proj| matches!(proj, ProjectionElem::Deref)) - .count(); - result.insert_str(0, &"*".repeat(final_derefs_count)); - result - } - - pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let krate = owner.krate(db); - let edition = krate.data(db).edition; - let mut result = body[self.place.local].name.display(db, edition).to_string(); - let mut field_need_paren = false; - for proj in &self.place.projections { - match proj { - ProjectionElem::Deref => { - result = format!("*{result}"); - field_need_paren = true; - } - ProjectionElem::Field(Either::Left(f)) => { - if field_need_paren { - result = format!("({result})"); - } - let variant_data = f.parent.fields(db); - let field = match variant_data.shape { - FieldsShape::Record => { - variant_data.fields()[f.local_id].name.as_str().to_owned() - } - FieldsShape::Tuple => variant_data - .fields() - .iter() - .position(|it| it.0 == f.local_id) - .unwrap_or_default() - .to_string(), - FieldsShape::Unit => "[missing field]".to_owned(), - }; - result = format!("{result}.{field}"); - field_need_paren = false; - } - ProjectionElem::Field(Either::Right(f)) => { - let field = f.index; - if field_need_paren { - result = format!("({result})"); - } - result = format!("{result}.{field}"); - field_need_paren = false; - } - &ProjectionElem::ClosureField(field) => { - if field_need_paren { - result = format!("({result})"); - } - result = format!("{result}.{field}"); - field_need_paren = false; - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } - } - } - result - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct CapturedItemWithoutTy { - pub(crate) place: HirPlace, - pub(crate) kind: CaptureKind, - /// The inner vec is the stacks; the outer vec is for each capture reference. - pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, -} - -impl CapturedItemWithoutTy { - fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { - let ty = self.place.ty(ctx); - let ty = match &self.kind { - CaptureKind::ByValue => ty, - CaptureKind::ByRef(bk) => { - let m = match bk { - BorrowKind::Mut { .. } => Mutability::Mut, - _ => Mutability::Not, - }; - TyKind::Ref(m, error_lifetime(), ty).intern(Interner) - } - }; - return CapturedItem { - place: self.place, - kind: self.kind, - span_stacks: self.span_stacks, - ty: replace_placeholder_with_binder(ctx, ty), - }; - - fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { - struct Filler<'a> { - db: &'a dyn HirDatabase, - generics: &'a Generics, - } - impl FallibleTypeFolder for Filler<'_> { - type Error = (); - - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn try_fold_free_placeholder_const( - &mut self, - ty: chalk_ir::Ty, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) - } - - fn try_fold_free_placeholder_ty( - &mut self, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) - } - } - let filler = &mut Filler { db: ctx.db, generics: ctx.generics() }; - let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); - make_binders(ctx.db, filler.generics, result) - } - } -} - -impl InferenceContext<'_> { - fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { - let r = self.place_of_expr_without_adjust(tgt_expr)?; - let adjustments = - self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default(); - apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments) - } - - /// Pushes the span into `current_capture_span_stack`, *without clearing it first*. - fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option { - if path.type_anchor().is_some() { - return None; - } - let hygiene = self.body.expr_or_pat_path_hygiene(id); - self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| { - match result { - ValueNs::LocalBinding(binding) => { - let mir_span = match id { - ExprOrPatId::ExprId(id) => MirSpan::ExprId(id), - ExprOrPatId::PatId(id) => MirSpan::PatId(id), - }; - self.current_capture_span_stack.push(mir_span); - Some(HirPlace { local: binding, projections: Vec::new() }) - } - _ => None, - } - }) - } + /// The approach is as follows: + /// + /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. + /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. + /// - If we have no expectation `E`, then the signature of the closure is `S`. + /// - Otherwise, the signature of the closure is E. Moreover: + /// - Skolemize the late-bound regions in `E`, yielding `E'`. + /// - Instantiate all the late-bound regions bound in the closure within `S` + /// with fresh (existential) variables, yielding `S'` + /// - Require that `E' = S'` + /// - We could use some kind of subtyping relationship here, + /// I imagine, but equality is easier and works fine for + /// our purposes. + /// + /// The key intuition here is that the user's types must be valid + /// from "the inside" of the closure, but the expectation + /// ultimately drives the overall signature. + /// + /// # Examples + /// + /// ```ignore (illustrative) + /// fn with_closure(_: F) + /// where F: Fn(&u32) -> &u32 { .. } + /// + /// with_closure(|x: &u32| { ... }) + /// ``` + /// + /// Here: + /// - E would be `fn(&u32) -> &u32`. + /// - S would be `fn(&u32) -> ?T` + /// - E' is `&'!0 u32 -> &'!0 u32` + /// - S' is `&'?0 u32 -> ?T` + /// + /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. + /// + /// # Arguments + /// + /// - `expr_def_id`: the `LocalDefId` of the closure expression + /// - `decl`: the HIR declaration of the closure + /// - `body`: the body of the closure + /// - `expected_sig`: the expected signature (if any). Note that + /// this is missing a binder: that is, there may be late-bound + /// regions with depth 1, which are bound then by the closure. + fn sig_of_closure_with_expectation( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + expected_sig: PolyFnSig<'db>, + ) -> ClosureSignatures<'db> { + // Watch out for some surprises and just ignore the + // expectation if things don't see to match up with what we + // expect. + if expected_sig.c_variadic() { + return self.sig_of_closure_no_expectation(decl_inputs, decl_output); + } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 { + return self + .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output); + } + + // Create a `PolyFnSig`. Note the oddity that late bound + // regions appearing free in `expected_sig` are now bound up + // in this binder we are creating. + assert!(!expected_sig.skip_binder().has_vars_bound_above(rustc_type_ir::INNERMOST)); + let bound_sig = expected_sig.map_bound(|sig| { + self.table.interner.mk_fn_sig( + sig.inputs(), + sig.output(), + sig.c_variadic, + Safety::Safe, + FnAbi::RustCall, + ) + }); - /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. - fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { - self.current_capture_span_stack.clear(); - match &self.body[tgt_expr] { - Expr::Path(p) => { - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - let result = self.path_place(p, tgt_expr.into()); - self.resolver.reset_to_guard(resolver_guard); - return result; - } - Expr::Field { expr, name: _ } => { - let mut place = self.place_of_expr(*expr)?; - let field = self.result.field_resolution(tgt_expr)?; - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - place.projections.push(ProjectionElem::Field(field)); - return Some(place); - } - Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - let mut place = self.place_of_expr(*expr)?; - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - place.projections.push(ProjectionElem::Deref); - return Some(place); - } - } - _ => (), - } - None - } + // `deduce_expectations_from_expected_type` introduces + // late-bound lifetimes defined elsewhere, which we now + // anonymize away, so as not to confuse the user. + let bound_sig = self.table.interner.anonymize_bound_vars(bound_sig); - fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) { - self.current_captures.push(CapturedItemWithoutTy { - place, - kind, - span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()], - }); - } + let closure_sigs = self.closure_sigs(bound_sig); - fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) { - // The first span is the identifier, and it must always remain. - truncate_to += 1; - for span_stack in &mut capture.span_stacks { - let mut remained = truncate_to; - let mut actual_truncate_to = 0; - for &span in &*span_stack { - actual_truncate_to += 1; - if !span.is_ref_span(self.body) { - remained -= 1; - if remained == 0 { - break; - } - } - } - if actual_truncate_to < span_stack.len() - && span_stack[actual_truncate_to].is_ref_span(self.body) - { - // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect. - actual_truncate_to += 1; - } - span_stack.truncate(actual_truncate_to); + // Up till this point, we have ignored the annotations that the user + // gave. This function will check that they unify successfully. + // Along the way, it also writes out entries for types that the user + // wrote into our typeck results, which are then later used by the privacy + // check. + match self.merge_supplied_sig_with_expectation(decl_inputs, decl_output, closure_sigs) { + Ok(infer_ok) => self.table.register_infer_ok(infer_ok), + Err(_) => self.sig_of_closure_no_expectation(decl_inputs, decl_output), } } - fn ref_expr(&mut self, expr: ExprId, place: Option) { - if let Some(place) = place { - self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); - } - self.walk_expr(expr); - } + fn sig_of_closure_with_mismatched_number_of_arguments( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + ) -> ClosureSignatures<'db> { + let error_sig = self.error_sig_of_closure(decl_inputs, decl_output); - fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { - if self.is_upvar(&place) { - self.push_capture(place, kind); - } + self.closure_sigs(error_sig) } - fn mutate_path_pat(&mut self, path: &Path, id: PatId) { - if let Some(place) = self.path_place(path, id.into()) { - self.add_capture( - place, - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + /// Enforce the user's types against the expectation. See + /// `sig_of_closure_with_expectation` for details on the overall + /// strategy. + fn merge_supplied_sig_with_expectation( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + mut expected_sigs: ClosureSignatures<'db>, + ) -> InferResult<'db, ClosureSignatures<'db>> { + // Get the signature S that the user gave. + // + // (See comment on `sig_of_closure_with_expectation` for the + // meaning of these letters.) + let supplied_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + + debug!(?supplied_sig); + + // FIXME(#45727): As discussed in [this comment][c1], naively + // forcing equality here actually results in suboptimal error + // messages in some cases. For now, if there would have been + // an obvious error, we fallback to declaring the type of the + // closure to be the one the user gave, which allows other + // error message code to trigger. + // + // However, I think [there is potential to do even better + // here][c2], since in *this* code we have the precise span of + // the type parameter in question in hand when we report the + // error. + // + // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 + // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 + self.table.commit_if_ok(|table| { + let mut all_obligations = PredicateObligations::new(); + let supplied_sig = table.infer_ctxt.instantiate_binder_with_fresh_vars( + BoundRegionConversionTime::FnCall, + supplied_sig, ); - self.current_capture_span_stack.pop(); // Remove the pattern span. - } - } - fn mutate_expr(&mut self, expr: ExprId, place: Option) { - if let Some(place) = place { - self.add_capture( - place, - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + // The liberated version of this signature should be a subtype + // of the liberated form of the expectation. + for (supplied_ty, expected_ty) in + iter::zip(supplied_sig.inputs(), expected_sigs.liberated_sig.inputs()) + { + // Check that E' = S'. + let cause = ObligationCause::new(); + let InferOk { value: (), obligations } = table + .infer_ctxt + .at(&cause, table.param_env) + .eq(DefineOpaqueTypes::Yes, expected_ty, supplied_ty)?; + all_obligations.extend(obligations); + } + + let supplied_output_ty = supplied_sig.output(); + let cause = ObligationCause::new(); + let InferOk { value: (), obligations } = + table.infer_ctxt.at(&cause, table.param_env).eq( + DefineOpaqueTypes::Yes, + expected_sigs.liberated_sig.output(), + supplied_output_ty, + )?; + all_obligations.extend(obligations); + + let inputs = supplied_sig + .inputs() + .into_iter() + .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); + + expected_sigs.liberated_sig = table.interner.mk_fn_sig( + inputs, + supplied_output_ty, + expected_sigs.liberated_sig.c_variadic, + Safety::Safe, + FnAbi::RustCall, ); - } - self.walk_expr(expr); - } - fn consume_expr(&mut self, expr: ExprId) { - if let Some(place) = self.place_of_expr(expr) { - self.consume_place(place); - } - self.walk_expr(expr); + Ok(InferOk { value: expected_sigs, obligations: all_obligations }) + }) } - fn consume_place(&mut self, place: HirPlace) { - if self.is_upvar(&place) { - let ty = place.ty(self); - let kind = if self.is_ty_copy(ty) { - CaptureKind::ByRef(BorrowKind::Shared) - } else { - CaptureKind::ByValue - }; - self.push_capture(place, kind); - } - } + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. + /// + /// Also, record this closure signature for later. + fn supplied_sig_of_closure( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + ) -> PolyFnSig<'db> { + let interner = self.table.interner; - fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { - if let Some((last, rest)) = adjustment.split_last() { - match &last.kind { - Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { - self.walk_expr_with_adjust(tgt_expr, rest) - } - Adjust::Deref(Some(m)) => match m.0 { - Some(m) => { - self.ref_capture_with_adjusts(m, tgt_expr, rest); - } - None => unreachable!(), - }, - Adjust::Borrow(b) => { - self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest); - } + let supplied_return = match decl_output { + Some(output) => { + let output = self.make_body_ty(output); + self.process_user_written_ty(output).to_nextsolver(interner) } - } else { - self.walk_expr_without_adjust(tgt_expr); - } - } - - fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { - let capture_kind = match m { - Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), - Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), + None => self.table.next_ty_var(), }; - if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) - && let Some(place) = - apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest) - { - self.add_capture(place, capture_kind); - } - self.walk_expr_with_adjust(tgt_expr, rest); - } - - fn walk_expr(&mut self, tgt_expr: ExprId) { - if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { - // FIXME: this take is completely unneeded, and just is here to make borrow checker - // happy. Remove it if you can. - let x_taken = mem::take(it); - self.walk_expr_with_adjust(tgt_expr, &x_taken); - *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; - } else { - self.walk_expr_without_adjust(tgt_expr); - } - } - - fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { - match &self.body[tgt_expr] { - Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.walk_expr_without_adjust(*in_expr); - if let Some(out_expr) = out_expr { - self.walk_expr_without_adjust(*out_expr); - } - } - AsmOperand::Out { expr: None, .. } - | AsmOperand::Const(_) - | AsmOperand::Label(_) - | AsmOperand::Sym(_) => (), - }), - Expr::If { condition, then_branch, else_branch } => { - self.consume_expr(*condition); - self.consume_expr(*then_branch); - if let &Some(expr) = else_branch { - self.consume_expr(expr); - } - } - Expr::Async { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Block { statements, tail, .. } => { - for s in statements.iter() { - match s { - Statement::Let { pat, type_ref: _, initializer, else_branch } => { - if let Some(else_branch) = else_branch { - self.consume_expr(*else_branch); - } - if let Some(initializer) = initializer { - if else_branch.is_some() { - self.consume_expr(*initializer); - } else { - self.walk_expr(*initializer); - } - if let Some(place) = self.place_of_expr(*initializer) { - self.consume_with_pat(place, *pat); - } - } - } - Statement::Expr { expr, has_semi: _ } => { - self.consume_expr(*expr); - } - Statement::Item(_) => (), - } - } - if let Some(tail) = tail { - self.consume_expr(*tail); - } - } - Expr::Call { callee, args } => { - self.consume_expr(*callee); - self.consume_exprs(args.iter().copied()); - } - Expr::MethodCall { receiver, args, .. } => { - self.consume_expr(*receiver); - self.consume_exprs(args.iter().copied()); - } - Expr::Match { expr, arms } => { - for arm in arms.iter() { - self.consume_expr(arm.expr); - if let Some(guard) = arm.guard { - self.consume_expr(guard); - } - } - self.walk_expr(*expr); - if let Some(discr_place) = self.place_of_expr(*expr) - && self.is_upvar(&discr_place) - { - let mut capture_mode = None; - for arm in arms.iter() { - self.walk_pat(&mut capture_mode, arm.pat); - } - if let Some(c) = capture_mode { - self.push_capture(discr_place, c); - } - } - } - Expr::Break { expr, label: _ } - | Expr::Return { expr } - | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - self.consume_expr(expr); - } - } - &Expr::Become { expr } => { - self.consume_expr(expr); - } - Expr::RecordLit { fields, spread, .. } => { - if let &Some(expr) = spread { - self.consume_expr(expr); - } - self.consume_exprs(fields.iter().map(|it| it.expr)); - } - Expr::Field { expr, name: _ } => self.select_from_expr(*expr), - Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - self.select_from_expr(*expr); - } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { - let mutability = 'b: { - if let Some(deref_trait) = - self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) - && let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) - { - break 'b deref_fn == f; - } - false - }; - let place = self.place_of_expr(*expr); - if mutability { - self.mutate_expr(*expr, place); - } else { - self.ref_expr(*expr, place); - } - } else { - self.select_from_expr(*expr); - } - } - Expr::Let { pat, expr } => { - self.walk_expr(*expr); - if let Some(place) = self.place_of_expr(*expr) { - self.consume_with_pat(place, *pat); - } - } - Expr::UnaryOp { expr, op: _ } - | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) - | Expr::Await { expr } - | Expr::Loop { body: expr, label: _ } - | Expr::Box { expr } - | Expr::Cast { expr, type_ref: _ } => { - self.consume_expr(*expr); - } - Expr::Ref { expr, rawness: _, mutability } => { - // We need to do this before we push the span so the order will be correct. - let place = self.place_of_expr(*expr); - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - match mutability { - hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place), - hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place), - } - } - Expr::BinaryOp { lhs, rhs, op } => { - let Some(op) = op else { - return; - }; - if matches!(op, BinaryOp::Assignment { .. }) { - let place = self.place_of_expr(*lhs); - self.mutate_expr(*lhs, place); - self.consume_expr(*rhs); - return; - } - self.consume_expr(*lhs); - self.consume_expr(*rhs); - } - Expr::Range { lhs, rhs, range_type: _ } => { - if let &Some(expr) = lhs { - self.consume_expr(expr); - } - if let &Some(expr) = rhs { - self.consume_expr(expr); - } - } - Expr::Index { base, index } => { - self.select_from_expr(*base); - self.consume_expr(*index); - } - Expr::Closure { .. } => { - let ty = self.expr_ty(tgt_expr); - let TyKind::Closure(id, _) = ty.kind(Interner) else { - never!("closure type is always closure"); - return; - }; - let (captures, _) = - self.result.closure_info.get(id).expect( - "We sort closures, so we should always have data for inner closures", - ); - let mut cc = mem::take(&mut self.current_captures); - cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { - CapturedItemWithoutTy { - place: it.place.clone(), - kind: it.kind, - span_stacks: it.span_stacks.clone(), - } - })); - self.current_captures = cc; - } - Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => { - self.consume_exprs(exprs.iter().copied()) - } - &Expr::Assignment { target, value } => { - self.walk_expr(value); - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - match self.place_of_expr(value) { - Some(rhs_place) => { - self.inside_assignment = true; - self.consume_with_pat(rhs_place, target); - self.inside_assignment = false; - } - None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] { - Pat::Path(path) => self.mutate_path_pat(path, pat), - &Pat::Expr(expr) => { - let place = self.place_of_expr(expr); - self.mutate_expr(expr, place); - } - _ => {} - }), - } - self.resolver.reset_to_guard(resolver_guard); - } - - Expr::Missing - | Expr::Continue { .. } - | Expr::Path(_) - | Expr::Literal(_) - | Expr::Const(_) - | Expr::Underscore => (), - } - } - - fn walk_pat(&mut self, result: &mut Option, pat: PatId) { - let mut update_result = |ck: CaptureKind| match result { - Some(r) => { - *r = cmp::max(*r, ck); + // First, convert the types that the user supplied (if any). + let supplied_arguments = decl_inputs.iter().map(|&input| match input { + Some(input) => { + let input = self.make_body_ty(input); + self.process_user_written_ty(input).to_nextsolver(interner) } - None => *result = Some(ck), - }; + None => self.table.next_ty_var(), + }); - self.walk_pat_inner( - pat, - &mut update_result, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - ); + Binder::dummy(interner.mk_fn_sig( + supplied_arguments, + supplied_return, + false, + Safety::Safe, + FnAbi::RustCall, + )) } - fn walk_pat_inner( + /// Converts the types that the user supplied, in case that doing + /// so should yield an error, but returns back a signature where + /// all parameters are of type `ty::Error`. + fn error_sig_of_closure( &mut self, - p: PatId, - update_result: &mut impl FnMut(CaptureKind), - mut for_mut: BorrowKind, - ) { - match &self.body[p] { - Pat::Ref { .. } - | Pat::Box { .. } - | Pat::Missing - | Pat::Wild - | Pat::Tuple { .. } - | Pat::Expr(_) - | Pat::Or(_) => (), - Pat::TupleStruct { .. } | Pat::Record { .. } => { - if let Some(variant) = self.result.variant_resolution_for_pat(p) { - let adt = variant.adt_id(self.db); - let is_multivariant = match adt { - hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1, - _ => false, - }; - if is_multivariant { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } - } - } - Pat::Slice { .. } - | Pat::ConstBlock(_) - | Pat::Path(_) - | Pat::Lit(_) - | Pat::Range { .. } => { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } - Pat::Bind { id, .. } => match self.result.binding_modes[p] { - crate::BindingMode::Move => { - if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } else { - update_result(CaptureKind::ByValue); - } - } - crate::BindingMode::Ref(r) => match r { - Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), - Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), - }, - }, - } - if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { - for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; - } - self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); - } - - fn expr_ty(&self, expr: ExprId) -> Ty { - self.result[expr].clone() - } - - fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { - let mut ty = None; - if let Some(it) = self.result.expr_adjustments.get(&e) - && let Some(it) = it.last() - { - ty = Some(it.target.clone()); - } - ty.unwrap_or_else(|| self.expr_ty(e)) - } - - fn is_upvar(&self, place: &HirPlace) -> bool { - if let Some(c) = self.current_closure { - let InternedClosure(_, root) = self.db.lookup_intern_closure(c.into()); - return self.body.is_binding_upvar(place.local, root); - } - false - } - - fn is_ty_copy(&mut self, ty: Ty) -> bool { - if let TyKind::Closure(id, _) = ty.kind(Interner) { - // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We - // should probably let chalk know which closures are copy, but I don't know how doing it - // without creating query cycles. - return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); - } - self.table.resolve_completely(ty).is_copy(self.db, self.owner) - } - - fn select_from_expr(&mut self, expr: ExprId) { - self.walk_expr(expr); - } - - fn restrict_precision_for_unsafe(&mut self) { - // FIXME: Borrow checker problems without this. - let mut current_captures = std::mem::take(&mut self.current_captures); - for capture in &mut current_captures { - let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); - if ty.as_raw_ptr().is_some() || ty.is_union() { - capture.kind = CaptureKind::ByRef(BorrowKind::Shared); - self.truncate_capture_spans(capture, 0); - capture.place.projections.truncate(0); - continue; - } - for (i, p) in capture.place.projections.iter().enumerate() { - ty = p.projected_ty( - ty, - self.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - self.owner.module(self.db).krate(), - ); - if ty.as_raw_ptr().is_some() || ty.is_union() { - capture.kind = CaptureKind::ByRef(BorrowKind::Shared); - self.truncate_capture_spans(capture, i + 1); - capture.place.projections.truncate(i + 1); - break; - } - } - } - self.current_captures = current_captures; - } + decl_inputs: &[Option], + decl_output: Option, + ) -> PolyFnSig<'db> { + let interner = self.table.interner; + let err_ty = Ty::new_error(interner, ErrorGuaranteed); - fn adjust_for_move_closure(&mut self) { - // FIXME: Borrow checker won't allow without this. - let mut current_captures = std::mem::take(&mut self.current_captures); - for capture in &mut current_captures { - if let Some(first_deref) = - capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref) - { - self.truncate_capture_spans(capture, first_deref); - capture.place.projections.truncate(first_deref); - } - capture.kind = CaptureKind::ByValue; + if let Some(output) = decl_output { + self.make_body_ty(output); } - self.current_captures = current_captures; - } - - fn minimize_captures(&mut self) { - self.current_captures.sort_unstable_by_key(|it| it.place.projections.len()); - let mut hash_map = FxHashMap::::default(); - let result = mem::take(&mut self.current_captures); - for mut item in result { - let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; - let mut it = item.place.projections.iter(); - let prev_index = loop { - if let Some(k) = hash_map.get(&lookup_place) { - break Some(*k); - } - match it.next() { - Some(it) => { - lookup_place.projections.push(it.clone()); - } - None => break None, - } - }; - match prev_index { - Some(p) => { - let prev_projections_len = self.current_captures[p].place.projections.len(); - self.truncate_capture_spans(&mut item, prev_projections_len); - self.current_captures[p].span_stacks.extend(item.span_stacks); - let len = self.current_captures[p].place.projections.len(); - let kind_after_truncate = - item.place.capture_kind_of_truncated_place(item.kind, len); - self.current_captures[p].kind = - cmp::max(kind_after_truncate, self.current_captures[p].kind); - } - None => { - hash_map.insert(item.place.clone(), self.current_captures.len()); - self.current_captures.push(item); - } + let supplied_arguments = decl_inputs.iter().map(|&input| match input { + Some(input) => { + self.make_body_ty(input); + err_ty } - } - } - - fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) { - let adjustments_count = - self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default(); - place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref)); - self.current_capture_span_stack - .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat))); - 'reset_span_stack: { - match &self.body[tgt_pat] { - Pat::Missing | Pat::Wild => (), - Pat::Tuple { args, ellipsis } => { - let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); - let field_count = match self.result[tgt_pat].kind(Interner) { - TyKind::Tuple(_, s) => s.len(Interner), - _ => break 'reset_span_stack, - }; - let fields = 0..field_count; - let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); - for (&arg, i) in it { - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy this, as its unused anyways - index: i as u32, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - Pat::Or(pats) => { - for pat in pats.iter() { - self.consume_with_pat(place.clone(), *pat); - } - } - Pat::Record { args, .. } => { - let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { - break 'reset_span_stack; - }; - match variant { - VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { - self.consume_place(place) - } - VariantId::StructId(s) => { - let vd = s.fields(self.db); - for field_pat in args.iter() { - let arg = field_pat.pat; - let Some(local_id) = vd.field(&field_pat.name) else { - continue; - }; - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Left(FieldId { - parent: variant, - local_id, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - } - } - Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => { - self.consume_place(place) - } - Pat::Path(path) => { - if self.inside_assignment { - self.mutate_path_pat(path, tgt_pat); - } - self.consume_place(place); - } - &Pat::Bind { id, subpat: _ } => { - let mode = self.result.binding_modes[tgt_pat]; - let capture_kind = match mode { - BindingMode::Move => { - self.consume_place(place); - break 'reset_span_stack; - } - BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, - BindingMode::Ref(Mutability::Mut) => { - BorrowKind::Mut { kind: MutBorrowKind::Default } - } - }; - self.current_capture_span_stack.push(MirSpan::BindingId(id)); - self.add_capture(place, CaptureKind::ByRef(capture_kind)); - self.current_capture_span_stack.pop(); - } - Pat::TupleStruct { path: _, args, ellipsis } => { - let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { - break 'reset_span_stack; - }; - match variant { - VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { - self.consume_place(place) - } - VariantId::StructId(s) => { - let vd = s.fields(self.db); - let (al, ar) = - args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); - let fields = vd.fields().iter(); - let it = al - .iter() - .zip(fields.clone()) - .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, _)) in it { - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Left(FieldId { - parent: variant, - local_id: i, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - } - } - Pat::Ref { pat, mutability: _ } => { - self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat)); - place.projections.push(ProjectionElem::Deref); - self.consume_with_pat(place, *pat); - self.current_capture_span_stack.pop(); - } - Pat::Box { .. } => (), // not supported - &Pat::Expr(expr) => { - self.consume_place(place); - let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack); - let old_inside_assignment = mem::replace(&mut self.inside_assignment, false); - let lhs_place = self.place_of_expr(expr); - self.mutate_expr(expr, lhs_place); - self.inside_assignment = old_inside_assignment; - self.current_capture_span_stack = pat_capture_span_stack; - } - } - } - self.current_capture_span_stack - .truncate(self.current_capture_span_stack.len() - adjustments_count); - } - - fn consume_exprs(&mut self, exprs: impl Iterator) { - for expr in exprs { - self.consume_expr(expr); - } - } - - fn closure_kind(&self) -> FnTrait { - let mut r = FnTrait::Fn; - for it in &self.current_captures { - r = cmp::min( - r, - match &it.kind { - CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut, - CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn, - CaptureKind::ByValue => FnTrait::FnOnce, - }, - ) - } - r - } + None => err_ty, + }); - fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { - let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into()); - self.current_closure = Some(closure); - let Expr::Closure { body, capture_by, .. } = &self.body[root] else { - unreachable!("Closure expression id is always closure"); - }; - self.consume_expr(*body); - for item in &self.current_captures { - if matches!( - item.kind, - CaptureKind::ByRef(BorrowKind::Mut { - kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow - }) - ) && !item.place.projections.contains(&ProjectionElem::Deref) - { - // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in - // MIR. I didn't do that due duplicate diagnostics. - self.result.mutated_bindings_in_closure.insert(item.place.local); - } - } - self.restrict_precision_for_unsafe(); - // `closure_kind` should be done before adjust_for_move_closure - // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does. - // rustc also does diagnostics here if the latter is not a subtype of the former. - let closure_kind = self - .result - .closure_info - .get(&closure) - .map_or_else(|| self.closure_kind(), |info| info.1); - match capture_by { - CaptureBy::Value => self.adjust_for_move_closure(), - CaptureBy::Ref => (), - } - self.minimize_captures(); - self.strip_captures_ref_span(); - let result = mem::take(&mut self.current_captures); - let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); - self.result.closure_info.insert(closure, (captures, closure_kind)); - closure_kind - } + let result = Binder::dummy(interner.mk_fn_sig( + supplied_arguments, + err_ty, + false, + Safety::Safe, + FnAbi::RustCall, + )); - fn strip_captures_ref_span(&mut self) { - // FIXME: Borrow checker won't allow without this. - let mut captures = std::mem::take(&mut self.current_captures); - for capture in &mut captures { - if matches!(capture.kind, CaptureKind::ByValue) { - for span_stack in &mut capture.span_stacks { - if span_stack[span_stack.len() - 1].is_ref_span(self.body) { - span_stack.truncate(span_stack.len() - 1); - } - } - } - } - self.current_captures = captures; - } + debug!("supplied_sig_of_closure: result={:?}", result); - pub(crate) fn infer_closures(&mut self) { - let deferred_closures = self.sort_closures(); - for (closure, exprs) in deferred_closures.into_iter().rev() { - self.current_captures = vec![]; - let kind = self.analyze_closure(closure); - - for (derefed_callee, callee_ty, params, expr) in exprs { - if let &Expr::Call { callee, .. } = &self.body[expr] { - let mut adjustments = - self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); - self.write_fn_trait_method_resolution( - kind, - &derefed_callee, - &mut adjustments, - &callee_ty, - ¶ms, - expr, - ); - self.result.expr_adjustments.insert(callee, adjustments.into_boxed_slice()); - } - } - } - } - - /// We want to analyze some closures before others, to have a correct analysis: - /// * We should analyze nested closures before the parent, since the parent should capture some of - /// the things that its children captures. - /// * If a closure calls another closure, we need to analyze the callee, to find out how we should - /// capture it (e.g. by move for FnOnce) - /// - /// These dependencies are collected in the main inference. We do a topological sort in this function. It - /// will consume the `deferred_closures` field and return its content in a sorted vector. - fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { - let mut deferred_closures = mem::take(&mut self.deferred_closures); - let mut dependents_count: FxHashMap = - deferred_closures.keys().map(|it| (*it, 0)).collect(); - for deps in self.closure_dependencies.values() { - for dep in deps { - *dependents_count.entry(*dep).or_default() += 1; - } - } - let mut queue: Vec<_> = - deferred_closures.keys().copied().filter(|it| dependents_count[it] == 0).collect(); - let mut result = vec![]; - while let Some(it) = queue.pop() { - if let Some(d) = deferred_closures.remove(&it) { - result.push((it, d)); - } - for dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { - let cnt = dependents_count.get_mut(dep).unwrap(); - *cnt -= 1; - if *cnt == 0 { - queue.push(*dep); - } - } - } - assert!(deferred_closures.is_empty(), "we should have analyzed all closures"); result } - pub(super) fn add_current_closure_dependency(&mut self, dep: ClosureId) { - if let Some(c) = self.current_closure - && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) - { - self.closure_dependencies.entry(c).or_default().push(dep); - } - - fn dep_creates_cycle( - closure_dependencies: &FxHashMap>, - visited: &mut FxHashSet, - from: ClosureId, - to: ClosureId, - ) -> bool { - if !visited.insert(from) { - return false; - } - - if from == to { - return true; - } - - if let Some(deps) = closure_dependencies.get(&to) { - for dep in deps { - if dep_creates_cycle(closure_dependencies, visited, from, *dep) { - return true; - } - } - } - - false - } - } -} - -/// Call this only when the last span in the stack isn't a split. -fn apply_adjusts_to_place( - current_capture_span_stack: &mut Vec, - mut r: HirPlace, - adjustments: &[Adjustment], -) -> Option { - let span = *current_capture_span_stack.last().expect("empty capture span stack"); - for adj in adjustments { - match &adj.kind { - Adjust::Deref(None) => { - current_capture_span_stack.push(span); - r.projections.push(ProjectionElem::Deref); - } - _ => return None, - } + fn closure_sigs(&self, bound_sig: PolyFnSig<'db>) -> ClosureSignatures<'db> { + let liberated_sig = bound_sig.skip_binder(); + // FIXME: When we lower HRTB we'll need to actually liberate regions here. + ClosureSignatures { bound_sig, liberated_sig } } - Some(r) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs new file mode 100644 index 0000000000000..fd14b9e2de571 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -0,0 +1,1298 @@ +//! Post-inference closure analysis: captures and closure kind. + +use std::{cmp, convert::Infallible, mem}; + +use chalk_ir::{ + BoundVar, DebruijnIndex, Mutability, TyKind, + fold::{FallibleTypeFolder, TypeFoldable}, +}; +use either::Either; +use hir_def::{ + DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, + expr_store::path::Path, + hir::{ + Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, + Statement, UnaryOp, + }, + item_tree::FieldsShape, + lang_item::LangItem, + resolver::ValueNs, +}; +use hir_expand::name::Name; +use intern::sym; +use rustc_hash::{FxHashMap, FxHashSet}; +use smallvec::{SmallVec, smallvec}; +use stdx::{format_to, never}; +use syntax::utils::is_raw_identifier; + +use crate::db::InternedClosureId; +use crate::infer::InferenceContext; +use crate::{ + Adjust, Adjustment, Binders, BindingMode, ClosureId, Interner, Substitution, Ty, TyExt, + db::{HirDatabase, InternedClosure}, + error_lifetime, from_placeholder_idx, + generics::Generics, + make_binders, + mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, + traits::FnTrait, + utils, +}; + +// The below functions handle capture and closure kind (Fn, FnMut, ..) + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct HirPlace { + pub(crate) local: BindingId, + pub(crate) projections: Vec>, +} + +impl HirPlace { + fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { + let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); + for p in &self.projections { + ty = p.projected_ty( + ty, + ctx.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db).krate(), + ); + } + ty + } + + fn capture_kind_of_truncated_place( + &self, + mut current_capture: CaptureKind, + len: usize, + ) -> CaptureKind { + if let CaptureKind::ByRef(BorrowKind::Mut { + kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, + }) = current_capture + && self.projections[len..].contains(&ProjectionElem::Deref) + { + current_capture = + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); + } + current_capture + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CaptureKind { + ByRef(BorrowKind), + ByValue, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CapturedItem { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + /// The inner vec is the stacks; the outer vec is for each capture reference. + /// + /// Even though we always report only the last span (i.e. the most inclusive span), + /// we need to keep them all, since when a closure occurs inside a closure, we + /// copy all captures of the inner closure to the outer closure, and then we may + /// truncate them, and we want the correct span to be reported. + span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, + pub(crate) ty: Binders, +} + +impl CapturedItem { + pub fn local(&self) -> BindingId { + self.place.local + } + + /// Returns whether this place has any field (aka. non-deref) projections. + pub fn has_field_projections(&self) -> bool { + self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref)) + } + + pub fn ty(&self, db: &dyn HirDatabase, subst: &Substitution) -> Ty { + self.ty.clone().substitute(Interner, &utils::ClosureSubst(subst).parent_subst(db)) + } + + pub fn kind(&self) -> CaptureKind { + self.kind + } + + pub fn spans(&self) -> SmallVec<[MirSpan; 3]> { + self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect() + } + + /// Converts the place to a name that can be inserted into source code. + pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let mut result = body[self.place.local].name.as_str().to_owned(); + for proj in &self.place.projections { + match proj { + ProjectionElem::Deref => {} + ProjectionElem::Field(Either::Left(f)) => { + let variant_data = f.parent.fields(db); + match variant_data.shape { + FieldsShape::Record => { + result.push('_'); + result.push_str(variant_data.fields()[f.local_id].name.as_str()) + } + FieldsShape::Tuple => { + let index = + variant_data.fields().iter().position(|it| it.0 == f.local_id); + if let Some(index) = index { + format_to!(result, "_{index}"); + } + } + FieldsShape::Unit => {} + } + } + ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index), + &ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"), + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + if is_raw_identifier(&result, owner.module(db).krate().data(db).edition) { + result.insert_str(0, "r#"); + } + result + } + + pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let krate = owner.krate(db); + let edition = krate.data(db).edition; + let mut result = body[self.place.local].name.display(db, edition).to_string(); + for proj in &self.place.projections { + match proj { + // In source code autoderef kicks in. + ProjectionElem::Deref => {} + ProjectionElem::Field(Either::Left(f)) => { + let variant_data = f.parent.fields(db); + match variant_data.shape { + FieldsShape::Record => format_to!( + result, + ".{}", + variant_data.fields()[f.local_id].name.display(db, edition) + ), + FieldsShape::Tuple => format_to!( + result, + ".{}", + variant_data + .fields() + .iter() + .position(|it| it.0 == f.local_id) + .unwrap_or_default() + ), + FieldsShape::Unit => {} + } + } + ProjectionElem::Field(Either::Right(f)) => { + let field = f.index; + format_to!(result, ".{field}"); + } + &ProjectionElem::ClosureField(field) => { + format_to!(result, ".{field}"); + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + let final_derefs_count = self + .place + .projections + .iter() + .rev() + .take_while(|proj| matches!(proj, ProjectionElem::Deref)) + .count(); + result.insert_str(0, &"*".repeat(final_derefs_count)); + result + } + + pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let krate = owner.krate(db); + let edition = krate.data(db).edition; + let mut result = body[self.place.local].name.display(db, edition).to_string(); + let mut field_need_paren = false; + for proj in &self.place.projections { + match proj { + ProjectionElem::Deref => { + result = format!("*{result}"); + field_need_paren = true; + } + ProjectionElem::Field(Either::Left(f)) => { + if field_need_paren { + result = format!("({result})"); + } + let variant_data = f.parent.fields(db); + let field = match variant_data.shape { + FieldsShape::Record => { + variant_data.fields()[f.local_id].name.as_str().to_owned() + } + FieldsShape::Tuple => variant_data + .fields() + .iter() + .position(|it| it.0 == f.local_id) + .unwrap_or_default() + .to_string(), + FieldsShape::Unit => "[missing field]".to_owned(), + }; + result = format!("{result}.{field}"); + field_need_paren = false; + } + ProjectionElem::Field(Either::Right(f)) => { + let field = f.index; + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + &ProjectionElem::ClosureField(field) => { + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + result + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct CapturedItemWithoutTy { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + /// The inner vec is the stacks; the outer vec is for each capture reference. + pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, +} + +impl CapturedItemWithoutTy { + fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { + let ty = self.place.ty(ctx); + let ty = match &self.kind { + CaptureKind::ByValue => ty, + CaptureKind::ByRef(bk) => { + let m = match bk { + BorrowKind::Mut { .. } => Mutability::Mut, + _ => Mutability::Not, + }; + TyKind::Ref(m, error_lifetime(), ty).intern(Interner) + } + }; + return CapturedItem { + place: self.place, + kind: self.kind, + span_stacks: self.span_stacks, + ty: replace_placeholder_with_binder(ctx, ty), + }; + + fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { + struct Filler<'a> { + db: &'a dyn HirDatabase, + generics: &'a Generics, + } + impl FallibleTypeFolder for Filler<'_> { + type Error = (); + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_free_placeholder_const( + &mut self, + ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx).0; + let Some(idx) = self.generics.type_or_const_param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) + } + + fn try_fold_free_placeholder_ty( + &mut self, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> std::result::Result { + let x = from_placeholder_idx(self.db, idx).0; + let Some(idx) = self.generics.type_or_const_param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) + } + } + let filler = &mut Filler { db: ctx.db, generics: ctx.generics() }; + let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); + make_binders(ctx.db, filler.generics, result) + } + } +} + +impl InferenceContext<'_> { + fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { + let r = self.place_of_expr_without_adjust(tgt_expr)?; + let adjustments = + self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default(); + apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments) + } + + /// Pushes the span into `current_capture_span_stack`, *without clearing it first*. + fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option { + if path.type_anchor().is_some() { + return None; + } + let hygiene = self.body.expr_or_pat_path_hygiene(id); + self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| { + match result { + ValueNs::LocalBinding(binding) => { + let mir_span = match id { + ExprOrPatId::ExprId(id) => MirSpan::ExprId(id), + ExprOrPatId::PatId(id) => MirSpan::PatId(id), + }; + self.current_capture_span_stack.push(mir_span); + Some(HirPlace { local: binding, projections: Vec::new() }) + } + _ => None, + } + }) + } + + /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. + fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { + self.current_capture_span_stack.clear(); + match &self.body[tgt_expr] { + Expr::Path(p) => { + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + let result = self.path_place(p, tgt_expr.into()); + self.resolver.reset_to_guard(resolver_guard); + return result; + } + Expr::Field { expr, name: _ } => { + let mut place = self.place_of_expr(*expr)?; + let field = self.result.field_resolution(tgt_expr)?; + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + place.projections.push(ProjectionElem::Field(field)); + return Some(place); + } + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + let mut place = self.place_of_expr(*expr)?; + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + place.projections.push(ProjectionElem::Deref); + return Some(place); + } + } + _ => (), + } + None + } + + fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) { + self.current_captures.push(CapturedItemWithoutTy { + place, + kind, + span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()], + }); + } + + fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) { + // The first span is the identifier, and it must always remain. + truncate_to += 1; + for span_stack in &mut capture.span_stacks { + let mut remained = truncate_to; + let mut actual_truncate_to = 0; + for &span in &*span_stack { + actual_truncate_to += 1; + if !span.is_ref_span(self.body) { + remained -= 1; + if remained == 0 { + break; + } + } + } + if actual_truncate_to < span_stack.len() + && span_stack[actual_truncate_to].is_ref_span(self.body) + { + // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect. + actual_truncate_to += 1; + } + span_stack.truncate(actual_truncate_to); + } + } + + fn ref_expr(&mut self, expr: ExprId, place: Option) { + if let Some(place) = place { + self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); + } + self.walk_expr(expr); + } + + fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { + if self.is_upvar(&place) { + self.push_capture(place, kind); + } + } + + fn mutate_path_pat(&mut self, path: &Path, id: PatId) { + if let Some(place) = self.path_place(path, id.into()) { + self.add_capture( + place, + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + ); + self.current_capture_span_stack.pop(); // Remove the pattern span. + } + } + + fn mutate_expr(&mut self, expr: ExprId, place: Option) { + if let Some(place) = place { + self.add_capture( + place, + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + ); + } + self.walk_expr(expr); + } + + fn consume_expr(&mut self, expr: ExprId) { + if let Some(place) = self.place_of_expr(expr) { + self.consume_place(place); + } + self.walk_expr(expr); + } + + fn consume_place(&mut self, place: HirPlace) { + if self.is_upvar(&place) { + let ty = place.ty(self); + let kind = if self.is_ty_copy(ty) { + CaptureKind::ByRef(BorrowKind::Shared) + } else { + CaptureKind::ByValue + }; + self.push_capture(place, kind); + } + } + + fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { + if let Some((last, rest)) = adjustment.split_last() { + match &last.kind { + Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { + self.walk_expr_with_adjust(tgt_expr, rest) + } + Adjust::Deref(Some(m)) => match m.0 { + Some(m) => { + self.ref_capture_with_adjusts(m, tgt_expr, rest); + } + None => unreachable!(), + }, + Adjust::Borrow(b) => { + self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest); + } + } + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { + let capture_kind = match m { + Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), + }; + if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) + && let Some(place) = + apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest) + { + self.add_capture(place, capture_kind); + } + self.walk_expr_with_adjust(tgt_expr, rest); + } + + fn walk_expr(&mut self, tgt_expr: ExprId) { + if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { + // FIXME: this take is completely unneeded, and just is here to make borrow checker + // happy. Remove it if you can. + let x_taken = mem::take(it); + self.walk_expr_with_adjust(tgt_expr, &x_taken); + *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { + match &self.body[tgt_expr] { + Expr::OffsetOf(_) => (), + Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr_without_adjust(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr_without_adjust(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), + Expr::If { condition, then_branch, else_branch } => { + self.consume_expr(*condition); + self.consume_expr(*then_branch); + if let &Some(expr) = else_branch { + self.consume_expr(expr); + } + } + Expr::Async { statements, tail, .. } + | Expr::Unsafe { statements, tail, .. } + | Expr::Block { statements, tail, .. } => { + for s in statements.iter() { + match s { + Statement::Let { pat, type_ref: _, initializer, else_branch } => { + if let Some(else_branch) = else_branch { + self.consume_expr(*else_branch); + } + if let Some(initializer) = initializer { + if else_branch.is_some() { + self.consume_expr(*initializer); + } else { + self.walk_expr(*initializer); + } + if let Some(place) = self.place_of_expr(*initializer) { + self.consume_with_pat(place, *pat); + } + } + } + Statement::Expr { expr, has_semi: _ } => { + self.consume_expr(*expr); + } + Statement::Item(_) => (), + } + } + if let Some(tail) = tail { + self.consume_expr(*tail); + } + } + Expr::Call { callee, args } => { + self.consume_expr(*callee); + self.consume_exprs(args.iter().copied()); + } + Expr::MethodCall { receiver, args, .. } => { + self.consume_expr(*receiver); + self.consume_exprs(args.iter().copied()); + } + Expr::Match { expr, arms } => { + for arm in arms.iter() { + self.consume_expr(arm.expr); + if let Some(guard) = arm.guard { + self.consume_expr(guard); + } + } + self.walk_expr(*expr); + if let Some(discr_place) = self.place_of_expr(*expr) + && self.is_upvar(&discr_place) + { + let mut capture_mode = None; + for arm in arms.iter() { + self.walk_pat(&mut capture_mode, arm.pat); + } + if let Some(c) = capture_mode { + self.push_capture(discr_place, c); + } + } + } + Expr::Break { expr, label: _ } + | Expr::Return { expr } + | Expr::Yield { expr } + | Expr::Yeet { expr } => { + if let &Some(expr) = expr { + self.consume_expr(expr); + } + } + &Expr::Become { expr } => { + self.consume_expr(expr); + } + Expr::RecordLit { fields, spread, .. } => { + if let &Some(expr) = spread { + self.consume_expr(expr); + } + self.consume_exprs(fields.iter().map(|it| it.expr)); + } + Expr::Field { expr, name: _ } => self.select_from_expr(*expr), + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + self.select_from_expr(*expr); + } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { + let mutability = 'b: { + if let Some(deref_trait) = + self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) + && let Some(deref_fn) = deref_trait + .trait_items(self.db) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + { + break 'b deref_fn == f; + } + false + }; + let place = self.place_of_expr(*expr); + if mutability { + self.mutate_expr(*expr, place); + } else { + self.ref_expr(*expr, place); + } + } else { + self.select_from_expr(*expr); + } + } + Expr::Let { pat, expr } => { + self.walk_expr(*expr); + if let Some(place) = self.place_of_expr(*expr) { + self.consume_with_pat(place, *pat); + } + } + Expr::UnaryOp { expr, op: _ } + | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) + | Expr::Await { expr } + | Expr::Loop { body: expr, label: _ } + | Expr::Box { expr } + | Expr::Cast { expr, type_ref: _ } => { + self.consume_expr(*expr); + } + Expr::Ref { expr, rawness: _, mutability } => { + // We need to do this before we push the span so the order will be correct. + let place = self.place_of_expr(*expr); + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + match mutability { + hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place), + hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place), + } + } + Expr::BinaryOp { lhs, rhs, op } => { + let Some(op) = op else { + return; + }; + if matches!(op, BinaryOp::Assignment { .. }) { + let place = self.place_of_expr(*lhs); + self.mutate_expr(*lhs, place); + self.consume_expr(*rhs); + return; + } + self.consume_expr(*lhs); + self.consume_expr(*rhs); + } + Expr::Range { lhs, rhs, range_type: _ } => { + if let &Some(expr) = lhs { + self.consume_expr(expr); + } + if let &Some(expr) = rhs { + self.consume_expr(expr); + } + } + Expr::Index { base, index } => { + self.select_from_expr(*base); + self.consume_expr(*index); + } + Expr::Closure { .. } => { + let ty = self.expr_ty(tgt_expr); + let TyKind::Closure(id, _) = ty.kind(Interner) else { + never!("closure type is always closure"); + return; + }; + let (captures, _) = + self.result.closure_info.get(id).expect( + "We sort closures, so we should always have data for inner closures", + ); + let mut cc = mem::take(&mut self.current_captures); + cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { + CapturedItemWithoutTy { + place: it.place.clone(), + kind: it.kind, + span_stacks: it.span_stacks.clone(), + } + })); + self.current_captures = cc; + } + Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => { + self.consume_exprs(exprs.iter().copied()) + } + &Expr::Assignment { target, value } => { + self.walk_expr(value); + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + match self.place_of_expr(value) { + Some(rhs_place) => { + self.inside_assignment = true; + self.consume_with_pat(rhs_place, target); + self.inside_assignment = false; + } + None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] { + Pat::Path(path) => self.mutate_path_pat(path, pat), + &Pat::Expr(expr) => { + let place = self.place_of_expr(expr); + self.mutate_expr(expr, place); + } + _ => {} + }), + } + self.resolver.reset_to_guard(resolver_guard); + } + + Expr::Missing + | Expr::Continue { .. } + | Expr::Path(_) + | Expr::Literal(_) + | Expr::Const(_) + | Expr::Underscore => (), + } + } + + fn walk_pat(&mut self, result: &mut Option, pat: PatId) { + let mut update_result = |ck: CaptureKind| match result { + Some(r) => { + *r = cmp::max(*r, ck); + } + None => *result = Some(ck), + }; + + self.walk_pat_inner( + pat, + &mut update_result, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + ); + } + + fn walk_pat_inner( + &mut self, + p: PatId, + update_result: &mut impl FnMut(CaptureKind), + mut for_mut: BorrowKind, + ) { + match &self.body[p] { + Pat::Ref { .. } + | Pat::Box { .. } + | Pat::Missing + | Pat::Wild + | Pat::Tuple { .. } + | Pat::Expr(_) + | Pat::Or(_) => (), + Pat::TupleStruct { .. } | Pat::Record { .. } => { + if let Some(variant) = self.result.variant_resolution_for_pat(p) { + let adt = variant.adt_id(self.db); + let is_multivariant = match adt { + hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1, + _ => false, + }; + if is_multivariant { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + } + } + Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Path(_) + | Pat::Lit(_) + | Pat::Range { .. } => { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + Pat::Bind { id, .. } => match self.result.binding_modes[p] { + crate::BindingMode::Move => { + if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } else { + update_result(CaptureKind::ByValue); + } + } + crate::BindingMode::Ref(r) => match r { + Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), + Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), + }, + }, + } + if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { + for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; + } + self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); + } + + fn expr_ty(&self, expr: ExprId) -> Ty { + self.result[expr].clone() + } + + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + let mut ty = None; + if let Some(it) = self.result.expr_adjustments.get(&e) + && let Some(it) = it.last() + { + ty = Some(it.target.clone()); + } + ty.unwrap_or_else(|| self.expr_ty(e)) + } + + fn is_upvar(&self, place: &HirPlace) -> bool { + if let Some(c) = self.current_closure { + let InternedClosure(_, root) = self.db.lookup_intern_closure(c); + return self.body.is_binding_upvar(place.local, root); + } + false + } + + fn is_ty_copy(&mut self, ty: Ty) -> bool { + if let TyKind::Closure(id, _) = ty.kind(Interner) { + // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We + // should probably let chalk know which closures are copy, but I don't know how doing it + // without creating query cycles. + return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); + } + self.table.resolve_completely(ty).is_copy(self.db, self.owner) + } + + fn select_from_expr(&mut self, expr: ExprId) { + self.walk_expr(expr); + } + + fn restrict_precision_for_unsafe(&mut self) { + // FIXME: Borrow checker problems without this. + let mut current_captures = std::mem::take(&mut self.current_captures); + for capture in &mut current_captures { + let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + self.truncate_capture_spans(capture, 0); + capture.place.projections.truncate(0); + continue; + } + for (i, p) in capture.place.projections.iter().enumerate() { + ty = p.projected_ty( + ty, + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db).krate(), + ); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + self.truncate_capture_spans(capture, i + 1); + capture.place.projections.truncate(i + 1); + break; + } + } + } + self.current_captures = current_captures; + } + + fn adjust_for_move_closure(&mut self) { + // FIXME: Borrow checker won't allow without this. + let mut current_captures = std::mem::take(&mut self.current_captures); + for capture in &mut current_captures { + if let Some(first_deref) = + capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref) + { + self.truncate_capture_spans(capture, first_deref); + capture.place.projections.truncate(first_deref); + } + capture.kind = CaptureKind::ByValue; + } + self.current_captures = current_captures; + } + + fn minimize_captures(&mut self) { + self.current_captures.sort_unstable_by_key(|it| it.place.projections.len()); + let mut hash_map = FxHashMap::::default(); + let result = mem::take(&mut self.current_captures); + for mut item in result { + let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; + let mut it = item.place.projections.iter(); + let prev_index = loop { + if let Some(k) = hash_map.get(&lookup_place) { + break Some(*k); + } + match it.next() { + Some(it) => { + lookup_place.projections.push(it.clone()); + } + None => break None, + } + }; + match prev_index { + Some(p) => { + let prev_projections_len = self.current_captures[p].place.projections.len(); + self.truncate_capture_spans(&mut item, prev_projections_len); + self.current_captures[p].span_stacks.extend(item.span_stacks); + let len = self.current_captures[p].place.projections.len(); + let kind_after_truncate = + item.place.capture_kind_of_truncated_place(item.kind, len); + self.current_captures[p].kind = + cmp::max(kind_after_truncate, self.current_captures[p].kind); + } + None => { + hash_map.insert(item.place.clone(), self.current_captures.len()); + self.current_captures.push(item); + } + } + } + } + + fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) { + let adjustments_count = + self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default(); + place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref)); + self.current_capture_span_stack + .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat))); + 'reset_span_stack: { + match &self.body[tgt_pat] { + Pat::Missing | Pat::Wild => (), + Pat::Tuple { args, ellipsis } => { + let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); + let field_count = match self.result[tgt_pat].kind(Interner) { + TyKind::Tuple(_, s) => s.len(Interner), + _ => break 'reset_span_stack, + }; + let fields = 0..field_count; + let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); + for (&arg, i) in it { + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy this, as its unused anyways + index: i as u32, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + Pat::Or(pats) => { + for pat in pats.iter() { + self.consume_with_pat(place.clone(), *pat); + } + } + Pat::Record { args, .. } => { + let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { + break 'reset_span_stack; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place) + } + VariantId::StructId(s) => { + let vd = s.fields(self.db); + for field_pat in args.iter() { + let arg = field_pat.pat; + let Some(local_id) = vd.field(&field_pat.name) else { + continue; + }; + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { + parent: variant, + local_id, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + } + } + Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => { + self.consume_place(place) + } + Pat::Path(path) => { + if self.inside_assignment { + self.mutate_path_pat(path, tgt_pat); + } + self.consume_place(place); + } + &Pat::Bind { id, subpat: _ } => { + let mode = self.result.binding_modes[tgt_pat]; + let capture_kind = match mode { + BindingMode::Move => { + self.consume_place(place); + break 'reset_span_stack; + } + BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, + BindingMode::Ref(Mutability::Mut) => { + BorrowKind::Mut { kind: MutBorrowKind::Default } + } + }; + self.current_capture_span_stack.push(MirSpan::BindingId(id)); + self.add_capture(place, CaptureKind::ByRef(capture_kind)); + self.current_capture_span_stack.pop(); + } + Pat::TupleStruct { path: _, args, ellipsis } => { + let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { + break 'reset_span_stack; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place) + } + VariantId::StructId(s) => { + let vd = s.fields(self.db); + let (al, ar) = + args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); + let fields = vd.fields().iter(); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())); + for (&arg, (i, _)) in it { + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { + parent: variant, + local_id: i, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + } + } + Pat::Ref { pat, mutability: _ } => { + self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat)); + place.projections.push(ProjectionElem::Deref); + self.consume_with_pat(place, *pat); + self.current_capture_span_stack.pop(); + } + Pat::Box { .. } => (), // not supported + &Pat::Expr(expr) => { + self.consume_place(place); + let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack); + let old_inside_assignment = mem::replace(&mut self.inside_assignment, false); + let lhs_place = self.place_of_expr(expr); + self.mutate_expr(expr, lhs_place); + self.inside_assignment = old_inside_assignment; + self.current_capture_span_stack = pat_capture_span_stack; + } + } + } + self.current_capture_span_stack + .truncate(self.current_capture_span_stack.len() - adjustments_count); + } + + fn consume_exprs(&mut self, exprs: impl Iterator) { + for expr in exprs { + self.consume_expr(expr); + } + } + + fn closure_kind(&self) -> FnTrait { + let mut r = FnTrait::Fn; + for it in &self.current_captures { + r = cmp::min( + r, + match &it.kind { + CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut, + CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn, + CaptureKind::ByValue => FnTrait::FnOnce, + }, + ) + } + r + } + + fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { + let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into()); + self.current_closure = Some(closure.into()); + let Expr::Closure { body, capture_by, .. } = &self.body[root] else { + unreachable!("Closure expression id is always closure"); + }; + self.consume_expr(*body); + for item in &self.current_captures { + if matches!( + item.kind, + CaptureKind::ByRef(BorrowKind::Mut { + kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow + }) + ) && !item.place.projections.contains(&ProjectionElem::Deref) + { + // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in + // MIR. I didn't do that due duplicate diagnostics. + self.result.mutated_bindings_in_closure.insert(item.place.local); + } + } + self.restrict_precision_for_unsafe(); + // `closure_kind` should be done before adjust_for_move_closure + // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does. + // rustc also does diagnostics here if the latter is not a subtype of the former. + let closure_kind = self + .result + .closure_info + .get(&closure) + .map_or_else(|| self.closure_kind(), |info| info.1); + match capture_by { + CaptureBy::Value => self.adjust_for_move_closure(), + CaptureBy::Ref => (), + } + self.minimize_captures(); + self.strip_captures_ref_span(); + let result = mem::take(&mut self.current_captures); + let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); + self.result.closure_info.insert(closure, (captures, closure_kind)); + closure_kind + } + + fn strip_captures_ref_span(&mut self) { + // FIXME: Borrow checker won't allow without this. + let mut captures = std::mem::take(&mut self.current_captures); + for capture in &mut captures { + if matches!(capture.kind, CaptureKind::ByValue) { + for span_stack in &mut capture.span_stacks { + if span_stack[span_stack.len() - 1].is_ref_span(self.body) { + span_stack.truncate(span_stack.len() - 1); + } + } + } + } + self.current_captures = captures; + } + + pub(crate) fn infer_closures(&mut self) { + let deferred_closures = self.sort_closures(); + for (closure, exprs) in deferred_closures.into_iter().rev() { + self.current_captures = vec![]; + let kind = self.analyze_closure(closure); + + for (derefed_callee, callee_ty, params, expr) in exprs { + if let &Expr::Call { callee, .. } = &self.body[expr] { + let mut adjustments = + self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); + self.write_fn_trait_method_resolution( + kind, + &derefed_callee, + &mut adjustments, + &callee_ty, + ¶ms, + expr, + ); + self.result.expr_adjustments.insert(callee, adjustments.into_boxed_slice()); + } + } + } + } + + /// We want to analyze some closures before others, to have a correct analysis: + /// * We should analyze nested closures before the parent, since the parent should capture some of + /// the things that its children captures. + /// * If a closure calls another closure, we need to analyze the callee, to find out how we should + /// capture it (e.g. by move for FnOnce) + /// + /// These dependencies are collected in the main inference. We do a topological sort in this function. It + /// will consume the `deferred_closures` field and return its content in a sorted vector. + fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { + let mut deferred_closures = mem::take(&mut self.deferred_closures); + let mut dependents_count: FxHashMap = + deferred_closures.keys().map(|it| ((*it).into(), 0)).collect(); + for deps in self.closure_dependencies.values() { + for dep in deps { + *dependents_count.entry((*dep).into()).or_default() += 1; + } + } + let mut queue: Vec<_> = deferred_closures + .keys() + .copied() + .filter(|&it| dependents_count[&it.into()] == 0) + .collect(); + let mut result = vec![]; + while let Some(it) = queue.pop() { + if let Some(d) = deferred_closures.remove(&it) { + result.push((it.into(), d)); + } + for &dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { + let cnt = dependents_count.get_mut(&dep.into()).unwrap(); + *cnt -= 1; + if *cnt == 0 { + queue.push(dep); + } + } + } + assert!(deferred_closures.is_empty(), "we should have analyzed all closures"); + result + } + + pub(crate) fn add_current_closure_dependency(&mut self, dep: InternedClosureId) { + if let Some(c) = self.current_closure + && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) + { + self.closure_dependencies.entry(c).or_default().push(dep); + } + + fn dep_creates_cycle( + closure_dependencies: &FxHashMap>, + visited: &mut FxHashSet, + from: InternedClosureId, + to: InternedClosureId, + ) -> bool { + if !visited.insert(from) { + return false; + } + + if from == to { + return true; + } + + if let Some(deps) = closure_dependencies.get(&to) { + for dep in deps { + if dep_creates_cycle(closure_dependencies, visited, from, *dep) { + return true; + } + } + } + + false + } + } +} + +/// Call this only when the last span in the stack isn't a split. +fn apply_adjusts_to_place( + current_capture_span_stack: &mut Vec, + mut r: HirPlace, + adjustments: &[Adjustment], +) -> Option { + let span = *current_capture_span_stack.last().expect("empty capture span stack"); + for adj in adjustments { + match &adj.kind { + Adjust::Deref(None) => { + current_capture_span_stack.push(span); + r.projections.push(ProjectionElem::Deref); + } + _ => return None, + } + } + Some(r) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index df516662bfd37..88b10e87e531e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1,452 +1,388 @@ -//! Coercion logic. Coercions are certain type conversions that can implicitly -//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions -//! like going from `&Vec` to `&[T]`. +//! # Type Coercion //! -//! See and -//! `rustc_hir_analysis/check/coercion.rs`. - -use std::iter; - -use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{hir::ExprId, lang_item::LangItem}; -use rustc_type_ir::solve::Certainty; -use stdx::always; +//! Under certain circumstances we will coerce from one type to another, +//! for example by auto-borrowing. This occurs in situations where the +//! compiler has a firm 'expected type' that was supplied from the user, +//! and where the actual type is similar to that expected type in purpose +//! but not in representation (so actual subtyping is inappropriate). +//! +//! ## Reborrowing +//! +//! Note that if we are expecting a reference, we will *reborrow* +//! even if the argument provided was already a reference. This is +//! useful for freezing mut things (that is, when the expected type is &T +//! but you have &mut T) and also for avoiding the linearity +//! of mut things (when the expected is &mut T and you have &mut T). See +//! the various `tests/ui/coerce/*.rs` tests for +//! examples of where this is useful. +//! +//! ## Subtle note +//! +//! When inferring the generic arguments of functions, the argument +//! order is relevant, which can lead to the following edge case: +//! +//! ```ignore (illustrative) +//! fn foo(a: T, b: T) { +//! // ... +//! } +//! +//! foo(&7i32, &mut 7i32); +//! // This compiles, as we first infer `T` to be `&i32`, +//! // and then coerce `&mut 7i32` to `&7i32`. +//! +//! foo(&mut 7i32, &7i32); +//! // This does not compile, as we first infer `T` to be `&mut i32` +//! // and are then unable to coerce `&7i32` to `&mut i32`. +//! ``` + +use chalk_ir::cast::Cast; +use hir_def::{ + CallableDefId, + hir::{ExprId, ExprOrPatId}, + lang_item::LangItem, + signatures::FunctionSignature, +}; +use intern::sym; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + TypeAndMut, + error::TypeError, + inherent::{IntoKind, Safety, Ty as _}, +}; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; use triomphe::Arc; use crate::{ - Canonical, FnAbi, FnPointer, FnSig, Goal, Interner, Lifetime, Substitution, TraitEnvironment, - Ty, TyBuilder, TyExt, - autoderef::{Autoderef, AutoderefKind}, - db::HirDatabase, - infer::{ - Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, - TypeError, TypeMismatch, + Adjust, Adjustment, AutoBorrow, Interner, PointerCast, TargetFeatures, TraitEnvironment, + autoderef::Autoderef, + db::{HirDatabase, InternedClosureId}, + infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable}, + next_solver::{ + Binder, CallableIdWrapper, ClauseKind, CoercePredicate, DbInterner, ErrorGuaranteed, + GenericArgs, PolyFnSig, PredicateKind, Region, SolverDefId, TraitRef, Ty, TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + relate::RelateResult, + select::{ImplSource, SelectionError}, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, }, - next_solver, - utils::ClosureSubst, + utils::TargetFeatureIsSafeInTarget, }; -use super::unify::InferenceTable; - -pub(crate) type CoerceResult<'db> = Result, Ty)>, TypeError>; - -/// Do not require any adjustments, i.e. coerce `x -> x`. -fn identity(_: Ty) -> Vec { - vec![] +struct Coerce<'a, 'b, 'db> { + table: &'a mut InferenceTable<'db>, + has_errors: &'a mut bool, + target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget), + use_lub: bool, + /// Determines whether or not allow_two_phase_borrow is set on any + /// autoref adjustments we create while coercing. We don't want to + /// allow deref coercions to create two-phase borrows, at least initially, + /// but we do need two-phase borrows for function argument reborrows. + /// See rust#47489 and rust#48598 + /// See docs on the "AllowTwoPhase" type for a more detailed discussion + allow_two_phase: AllowTwoPhase, + /// Whether we allow `NeverToAny` coercions. This is unsound if we're + /// coercing a place expression without it counting as a read in the MIR. + /// This is a side-effect of HIR not really having a great distinction + /// between places and values. + coerce_never: bool, + cause: ObligationCause, } -fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { - move |target| vec![Adjustment { kind, target }] +type CoerceResult<'db> = InferResult<'db, (Vec, Ty<'db>)>; + +/// Coercing a mutable reference to an immutable works, while +/// coercing `&T` to `&mut T` should be forbidden. +fn coerce_mutbls<'db>(from_mutbl: Mutability, to_mutbl: Mutability) -> RelateResult<'db, ()> { + if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } } /// This always returns `Ok(...)`. fn success<'db>( adj: Vec, - target: Ty, - goals: Vec>>, + target: Ty<'db>, + obligations: PredicateObligations<'db>, ) -> CoerceResult<'db> { - Ok(InferOk { goals, value: (adj, target) }) -} - -pub(super) enum CoercionCause { - // FIXME: Make better use of this. Right now things like return and break without a value - // use it to point to themselves, causing us to report a mismatch on those expressions even - // though technically they themselves are `!` - Expr(ExprId), -} - -#[derive(Clone, Debug)] -pub(super) struct CoerceMany { - expected_ty: Ty, - final_ty: Option, - expressions: Vec, + Ok(InferOk { value: (adj, target), obligations }) } -impl CoerceMany { - pub(super) fn new(expected: Ty) -> Self { - CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] } - } - - /// Returns the "expected type" with which this coercion was - /// constructed. This represents the "downward propagated" type - /// that was given to us at the start of typing whatever construct - /// we are typing (e.g., the match expression). - /// - /// Typically, this is used as the expected type when - /// type-checking each of the alternative expressions whose types - /// we are trying to merge. - pub(super) fn expected_ty(&self) -> Ty { - self.expected_ty.clone() - } - - /// Returns the current "merged type", representing our best-guess - /// at the LUB of the expressions we've seen so far (if any). This - /// isn't *final* until you call `self.complete()`, which will return - /// the merged type. - pub(super) fn merged_ty(&self) -> Ty { - self.final_ty.clone().unwrap_or_else(|| self.expected_ty.clone()) +impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { + #[inline] + fn set_tainted_by_errors(&mut self) { + *self.has_errors = true; } - pub(super) fn complete(self, ctx: &mut InferenceContext<'_>) -> Ty { - if let Some(final_ty) = self.final_ty { - final_ty - } else { - ctx.result.standard_types.never.clone() - } + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.table.interner } - pub(super) fn coerce_forced_unit( - &mut self, - ctx: &mut InferenceContext<'_>, - cause: CoercionCause, - ) { - self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause) + #[inline] + fn infer_ctxt(&self) -> &InferCtxt<'db> { + &self.table.infer_ctxt } - /// Merge two types from different branches, with possible coercion. - /// - /// Mostly this means trying to coerce one to the other, but - /// - if we have two function types for different functions or closures, we need to - /// coerce both to function pointers; - /// - if we were concerned with lifetime subtyping, we'd need to look for a - /// least upper bound. - pub(super) fn coerce<'db>( + pub(crate) fn commit_if_ok( &mut self, - ctx: &mut InferenceContext<'db>, - expr: Option, - expr_ty: &Ty, - cause: CoercionCause, - ) { - let expr_ty = ctx.resolve_ty_shallow(expr_ty); - self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty); - - // Special case: two function types. Try to coerce both to - // pointers to have a chance at getting a match. See - // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) { - (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) - if x == y && ctx.table.unify(&self.merged_ty(), &expr_ty) => - { - None - } - (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None, - (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => { - // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, - // we should be coercing the closure to a fn pointer of the safety of the FnDef - cov_mark::hit!(coerce_fn_reification); - let sig = - self.merged_ty().callable_sig(ctx.db).expect("FnDef without callable sig"); - Some(sig) - } - _ => None, - }; - if let Some(sig) = sig { - let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, CoerceNever::Yes); - let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, CoerceNever::Yes); - if let (Ok(result1), Ok(result2)) = (result1, result2) { - ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals }); - for &e in &self.expressions { - ctx.write_expr_adj(e, result1.value.0.clone().into_boxed_slice()); - } - ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals }); - if let Some(expr) = expr { - ctx.write_expr_adj(expr, result2.value.0.into_boxed_slice()); - self.expressions.push(expr); - } - return self.final_ty = Some(target_ty); + f: impl FnOnce(&mut Self) -> Result, + ) -> Result { + let snapshot = self.table.snapshot(); + let result = f(self); + match result { + Ok(_) => {} + Err(_) => { + self.table.rollback_to(snapshot); } } + result + } - // It might not seem like it, but order is important here: If the expected - // type is a type variable and the new one is `!`, trying it the other - // way around first would mean we make the type variable `!`, instead of - // just marking it as possibly diverging. - // - // - [Comment from rustc](https://github.com/rust-lang/rust/blob/5ff18d0eaefd1bd9ab8ec33dab2404a44e7631ed/compiler/rustc_hir_typeck/src/coercion.rs#L1334-L1335) - // First try to coerce the new expression to the type of the previous ones, - // but only if the new expression has no coercion already applied to it. - if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) - && let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) - { - self.final_ty = Some(res); - if let Some(expr) = expr { - self.expressions.push(expr); - } - return; - } + fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> { + debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); + self.commit_if_ok(|this| { + let at = this.infer_ctxt().at(&this.cause, this.table.param_env); - if let Ok((adjustments, res)) = - ctx.coerce_inner(&self.merged_ty(), &expr_ty, CoerceNever::Yes) - { - self.final_ty = Some(res); - for &e in &self.expressions { - ctx.write_expr_adj(e, adjustments.clone().into_boxed_slice()); - } - } else { - match cause { - CoercionCause::Expr(id) => { - ctx.result.type_mismatches.insert( - id.into(), - TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() }, - ); + let res = if this.use_lub { + at.lub(b, a) + } else { + at.sup(DefineOpaqueTypes::Yes, b, a) + .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations }) + }; + + // In the new solver, lazy norm may allow us to shallowly equate + // more types, but we emit possibly impossible-to-satisfy obligations. + // Filter these cases out to make sure our coercion is more accurate. + match res { + Ok(InferOk { value, obligations }) => { + let mut ocx = ObligationCtxt::new(this.infer_ctxt()); + ocx.register_obligations(obligations); + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } } + res => res, } - cov_mark::hit!(coerce_merge_fail_fallback); - } - if let Some(expr) = expr { - self.expressions.push(expr); - } + }) } -} - -pub fn could_coerce( - db: &dyn HirDatabase, - env: Arc, - tys: &Canonical<(Ty, Ty)>, -) -> bool { - coerce(db, env, tys).is_ok() -} - -pub(crate) fn coerce( - db: &dyn HirDatabase, - env: Arc, - tys: &Canonical<(Ty, Ty)>, -) -> Result<(Vec, Ty), TypeError> { - let mut table = InferenceTable::new(db, env); - let vars = table.fresh_subst(tys.binders.as_slice(Interner)); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); - let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, CoerceNever::Yes)?; - // default any type vars that weren't unified back to their original bound vars - // (kind of hacky) - let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), - } == Some(iv)) - }; - let fallback = |iv, kind, default, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), - chalk_ir::VariableKind::Lifetime => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)), - chalk_ir::VariableKind::Const(ty) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)), - }; - // FIXME also map the types in the adjustments - Ok((adjustments, table.resolve_with_fallback(ty, &fallback))) -} -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum CoerceNever { - Yes, - No, -} - -impl InferenceContext<'_> { - /// Unify two types, but may coerce the first one to the second one - /// using "implicit coercion rules" if needed. - pub(super) fn coerce( - &mut self, - expr: Option, - from_ty: &Ty, - to_ty: &Ty, - // [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89) - // Whether we allow `NeverToAny` coercions. This is unsound if we're - // coercing a place expression without it counting as a read in the MIR. - // This is a side-effect of HIR not really having a great distinction - // between places and values. - coerce_never: CoerceNever, - ) -> Result { - let (adjustments, ty) = self.coerce_inner(from_ty, to_ty, coerce_never)?; - if let Some(expr) = expr { - self.write_expr_adj(expr, adjustments.into_boxed_slice()); - } - Ok(ty) + /// Unify two types (using sub or lub). + fn unify(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + self.unify_raw(a, b) + .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) } - fn coerce_inner( + /// Unify two types (using sub or lub) and produce a specific coercion. + fn unify_and( &mut self, - from_ty: &Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); - self.table.coerce(&from_ty, &to_ty, coerce_never) + a: Ty<'db>, + b: Ty<'db>, + adjustments: impl IntoIterator, + final_adjustment: Adjust, + ) -> CoerceResult<'db> { + self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { + success( + adjustments + .into_iter() + .chain(std::iter::once(Adjustment { + target: ty.to_chalk(self.interner()), + kind: final_adjustment, + })) + .collect(), + ty, + obligations, + ) + }) } -} -impl<'db> InferenceTable<'db> { - /// Unify two types, but may coerce the first one to the second one - /// using "implicit coercion rules" if needed. - pub(crate) fn coerce( - &mut self, - from_ty: &Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.structurally_resolve_type(from_ty); - let to_ty = self.structurally_resolve_type(to_ty); - match self.coerce_inner(from_ty, &to_ty, coerce_never) { - Ok(InferOk { value: (adjustments, ty), goals }) => { - self.register_infer_ok(InferOk { value: (), goals }); - Ok((adjustments, ty)) - } - Err(e) => { - // FIXME deal with error - Err(e) + #[instrument(skip(self))] + fn coerce(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + // First, remove any resolved type variables (at the top level, at least): + let a = self.table.shallow_resolve(a); + let b = self.table.shallow_resolve(b); + debug!("Coerce.tys({:?} => {:?})", a, b); + + // Coercing from `!` to any type is allowed: + if a.is_never() { + // If we're coercing into an inference var, mark it as possibly diverging. + // FIXME: rustc does this differently. + if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() { + self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General); } - } - } - fn coerce_inner( - &mut self, - from_ty: Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> CoerceResult<'db> { - if from_ty.is_never() { - if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, TyVariableKind::General); - } - if coerce_never == CoerceNever::Yes { - // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound - // type variable, we want `?T` to fallback to `!` if not - // otherwise constrained. An example where this arises: - // - // let _: Option = Some({ return; }); - // - // here, we would coerce from `!` to `?T`. - return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); + if self.coerce_never { + return success( + vec![Adjustment { + kind: Adjust::NeverToAny, + target: b.to_chalk(self.interner()), + }], + b, + PredicateObligations::new(), + ); } else { - return self.unify_and(&from_ty, to_ty, identity); + // Otherwise the only coercion we can do is unification. + return self.unify(a, b); } } // If we are coercing into a TAIT, coerce into its proxy inference var, instead. - let mut to_ty = to_ty; - let _to; - if let Some(tait_table) = &self.tait_coercion_table - && let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) - && !matches!(from_ty.kind(Interner), TyKind::InferenceVar(..) | TyKind::OpaqueType(..)) - && let Some(ty) = tait_table.get(opaque_ty_id) + // FIXME(next-solver): This should not be here. This is not how rustc does thing, and it also not allows us + // to normalize opaques defined in our scopes. Instead, we should properly register + // `TypingMode::Analysis::defining_opaque_types_and_generators`, and rely on the solver to reveal + // them for us (we'll also need some global-like registry for the values, something we cannot + // really implement, therefore we can really support only RPITs and ITIAT or the new `#[define_opaque]` + // TAIT, not the old global TAIT). + let mut b = b; + if let Some(tait_table) = &self.table.tait_coercion_table + && let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind() + && let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id + && !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _)) + && let Some(ty) = tait_table.get(&opaque_ty_id.into()) { - _to = ty.clone(); - to_ty = &_to; + b = ty.to_nextsolver(self.interner()); + b = self.table.shallow_resolve(b); + } + let b = b; + + // Coercing *from* an unresolved inference variable means that + // we have no information about the source type. This will always + // ultimately fall back to some form of subtyping. + if a.is_infer() { + return self.coerce_from_inference_variable(a, b); } // Consider coercing the subtype to a DST - if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { - return Ok(ret); + // + // NOTE: this is wrapped in a `commit_if_ok` because it creates + // a "spurious" type variable, and we don't want to have that + // type variable in memory if the coercion fails. + let unsize = self.commit_if_ok(|this| this.coerce_unsized(a, b)); + match unsize { + Ok(_) => { + debug!("coerce: unsize successful"); + return unsize; + } + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } } - // Examine the supertype and consider auto-borrowing. - match to_ty.kind(Interner) { - TyKind::Raw(mt, _) => return self.coerce_ptr(from_ty, to_ty, *mt), - TyKind::Ref(mt, lt, _) => return self.coerce_ref(from_ty, to_ty, *mt, lt), + // Examine the supertype and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, a `dyn*` coercion, + // or pin-ergonomics. + match b.kind() { + TyKind::RawPtr(_, b_mutbl) => { + return self.coerce_raw_ptr(a, b, b_mutbl); + } + TyKind::Ref(r_b, _, mutbl_b) => { + return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); + } _ => {} } - match from_ty.kind(Interner) { + match a.kind() { TyKind::FnDef(..) => { // Function items are coercible to any closure // type; function pointers are not (that would // require double indirection). // Additionally, we permit coercion of function // items to drop the unsafe qualifier. - self.coerce_from_fn_item(from_ty, to_ty) + self.coerce_from_fn_item(a, b) } - TyKind::Function(from_fn_ptr) => { + TyKind::FnPtr(a_sig_tys, a_hdr) => { // We permit coercion of fn pointers to drop the // unsafe qualifier. - self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty) + self.coerce_from_fn_pointer(a_sig_tys.with(a_hdr), b) } - TyKind::Closure(_, from_substs) => { + TyKind::Closure(closure_def_id_a, args_a) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty) + self.coerce_closure_to_fn(a, closure_def_id_a.0, args_a, b) } _ => { // Otherwise, just use unification rules. - self.unify_and(&from_ty, to_ty, identity) + self.unify(a, b) } } } - /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult<'db> - where - F: FnOnce(Ty) -> Vec, - { - self.try_unify(t1, t2) - .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals)) - } - - fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult<'db> { - let (is_ref, from_mt, from_inner) = match from_ty.kind(Interner) { - TyKind::Ref(mt, _, ty) => (true, mt, ty), - TyKind::Raw(mt, ty) => (false, mt, ty), - _ => return self.unify_and(&from_ty, to_ty, identity), - }; - - coerce_mutabilities(*from_mt, to_mt)?; - - // Check that the types which they point at are compatible. - let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(Interner); + /// Coercing *from* an inference variable. In this case, we have no information + /// about the source type, so we can't really do a true coercion and we always + /// fall back to subtyping (`unify_and`). + fn coerce_from_inference_variable(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); + debug_assert!(a.is_infer() && self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + if b.is_infer() { + // Two unresolved type variables: create a `Coerce` predicate. + let target_ty = if self.use_lub { self.table.next_ty_var() } else { b }; + + let mut obligations = PredicateObligations::with_capacity(2); + for &source_ty in &[a, b] { + if source_ty != target_ty { + obligations.push(Obligation::new( + self.interner(), + self.cause.clone(), + self.table.param_env, + Binder::dummy(PredicateKind::Coerce(CoercePredicate { + a: source_ty, + b: target_ty, + })), + )); + } + } - // Although references and raw ptrs have the same - // representation, we still register an Adjust::DerefRef so that - // regionck knows that the region for `a` must be valid here. - if is_ref { - self.unify_and(&from_raw, to_ty, |target| { - vec![ - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)), target }, - ] - }) - } else if *from_mt != to_mt { - self.unify_and( - &from_raw, - to_ty, - simple(Adjust::Pointer(PointerCast::MutToConstPointer)), - ) + debug!( + "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", + target_ty, obligations + ); + success(vec![], target_ty, obligations) } else { - self.unify_and(&from_raw, to_ty, identity) + // One unresolved type variable: just apply subtyping, we may be able + // to do something useful. + self.unify(a, b) } } /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. - fn coerce_ref( + fn coerce_borrowed_pointer( &mut self, - from_ty: Ty, - to_ty: &Ty, - to_mt: Mutability, - to_lt: &Lifetime, + a: Ty<'db>, + b: Ty<'db>, + r_b: Region<'db>, + mutbl_b: Mutability, ) -> CoerceResult<'db> { - let (_from_lt, from_mt) = match from_ty.kind(Interner) { - TyKind::Ref(mt, lt, _) => { - coerce_mutabilities(*mt, to_mt)?; - (lt.clone(), *mt) // clone is probably not good? + debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + // If we have a parameter of type `&M T_a` and the value + // provided is `expr`, we will be adding an implicit borrow, + // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, + // to type check, we will construct the type that `&M*expr` would + // yield. + + let (r_a, mt_a) = match a.kind() { + TyKind::Ref(r_a, ty, mutbl) => { + let mt_a = TypeAndMut::> { ty, mutbl }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; + (r_a, mt_a) } - _ => return self.unify_and(&from_ty, to_ty, identity), + _ => return self.unify(a, b), }; - // NOTE: this code is mostly copied and adapted from rustc, and - // currently more complicated than necessary, carrying errors around - // etc.. This complication will become necessary when we actually track - // details of coercion errors though, so I think it's useful to leave - // the structure like it is. - - let snapshot = self.snapshot(); - - let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; + let mut r_borrow_var = None; + let mut autoderef = Autoderef::new(self.table, a); let mut found = None; while let Some((referent_ty, autoderefs)) = autoderef.next() { @@ -456,7 +392,7 @@ impl<'db> InferenceTable<'db> { continue; } - // At this point, we have deref'd `a` to `referent_ty`. So + // At this point, we have deref'd `a` to `referent_ty`. So // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. // In the autoderef loop for `&'a mut Vec`, we would get // three callbacks: @@ -478,11 +414,85 @@ impl<'db> InferenceTable<'db> { // compare those. Note that this means we use the target // mutability [1], since it may be that we are coercing // from `&mut T` to `&U`. - let lt = to_lt; // FIXME: Involve rustc LUB and SUB flag checks - let derefd_from_ty = TyKind::Ref(to_mt, lt.clone(), referent_ty).intern(Interner); - match autoderef.table.try_unify(&derefd_from_ty, to_ty) { - Ok(result) => { - found = Some(result.map(|()| derefd_from_ty)); + // + // One fine point concerns the region that we use. We + // choose the region such that the region of the final + // type that results from `unify` will be the region we + // want for the autoref: + // + // - if in sub mode, that means we want to use `'b` (the + // region from the target reference) for both + // pointers [2]. This is because sub mode (somewhat + // arbitrarily) returns the subtype region. In the case + // where we are coercing to a target type, we know we + // want to use that target type region (`'b`) because -- + // for the program to type-check -- it must be the + // smaller of the two. + // - One fine point. It may be surprising that we can + // use `'b` without relating `'a` and `'b`. The reason + // that this is ok is that what we produce is + // effectively a `&'b *x` expression (if you could + // annotate the region of a borrow), and regionck has + // code that adds edges from the region of a borrow + // (`'b`, here) into the regions in the borrowed + // expression (`*x`, here). (Search for "link".) + // - if in lub mode, things can get fairly complicated. The + // easiest thing is just to make a fresh + // region variable [4], which effectively means we defer + // the decision to region inference (and regionck, which will add + // some more edges to this variable). However, this can wind up + // creating a crippling number of variables in some cases -- + // e.g., #32278 -- so we optimize one particular case [3]. + // Let me try to explain with some examples: + // - The "running example" above represents the simple case, + // where we have one `&` reference at the outer level and + // ownership all the rest of the way down. In this case, + // we want `LUB('a, 'b)` as the resulting region. + // - However, if there are nested borrows, that region is + // too strong. Consider a coercion from `&'a &'x Rc` to + // `&'b T`. In this case, `'a` is actually irrelevant. + // The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)` + // we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`). + // (The errors actually show up in borrowck, typically, because + // this extra edge causes the region `'a` to be inferred to something + // too big, which then results in borrowck errors.) + // - We could track the innermost shared reference, but there is already + // code in regionck that has the job of creating links between + // the region of a borrow and the regions in the thing being + // borrowed (here, `'a` and `'x`), and it knows how to handle + // all the various cases. So instead we just make a region variable + // and let regionck figure it out. + let r = if !self.use_lub { + r_b // [2] above + } else if autoderefs == 1 { + r_a // [3] above + } else { + if r_borrow_var.is_none() { + // create var lazily, at most once + let r = autoderef.table.next_region_var(); + r_borrow_var = Some(r); // [4] above + } + r_borrow_var.unwrap() + }; + let derefd_ty_a = Ty::new_ref( + autoderef.table.interner, + r, + referent_ty, + mutbl_b, // [1] above + ); + // We need to construct a new `Coerce` because of lifetimes. + let mut coerce = Coerce { + table: autoderef.table, + has_errors: self.has_errors, + target_features: self.target_features, + use_lub: self.use_lub, + allow_two_phase: self.allow_two_phase, + coerce_never: self.coerce_never, + cause: self.cause.clone(), + }; + match coerce.unify_raw(derefd_ty_a, b) { + Ok(ok) => { + found = Some(ok); break; } Err(err) => { @@ -498,18 +508,24 @@ impl<'db> InferenceTable<'db> { // (e.g., in example above, the failure from relating `Vec` // to the target type), since that should be the least // confusing. - let InferOk { value: ty, goals } = match found { - Some(d) => d, - None => { - self.rollback_to(snapshot); - let err = first_error.expect("coerce_borrowed_pointer had no error"); - return Err(err); + let Some(InferOk { value: ty, mut obligations }) = found else { + if let Some(first_error) = first_error { + debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error); + return Err(first_error); + } else { + // This may happen in the new trait solver since autoderef requires + // the pointee to be structurally normalizable, or else it'll just bail. + // So when we have a type like `&`, then we get no + // autoderef steps (even though there should be at least one). That means + // we get no type mismatches, since the loop above just exits early. + return Err(TypeError::Mismatch); } }; - if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 { + + if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { // As a special case, if we would produce `&'a *x`, that's // a total no-op. We end up with the type `&'a T` just as - // we started with. In that case, just skip it + // we started with. In that case, just skip it // altogether. This is just an optimization. // // Note that for `&mut`, we DO want to reborrow -- @@ -518,259 +534,1091 @@ impl<'db> InferenceTable<'db> { // `self.x` both have `&mut `type would be a move of // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. - always!(to_mt == Mutability::Not); // can only coerce &T -> &U - return success(vec![], ty, goals); + assert!(mutbl_b.is_not()); // can only coerce &T -> &U + return success(vec![], ty, obligations); } - let mut adjustments = auto_deref_adjust_steps(&autoderef); + let InferOk { value: mut adjustments, obligations: o } = + autoderef.adjust_steps_as_infer_ok(); + obligations.extend(o); + + // Now apply the autoref. We have to extract the region out of + // the final ref type we got. + let TyKind::Ref(region, _, _) = ty.kind() else { + panic!("expected a ref type, got {:?}", ty); + }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(to_lt.clone(), to_mt)), - target: ty.clone(), + kind: Adjust::Borrow(AutoBorrow::Ref( + region.to_chalk(self.interner()), + mutbl_b.to_chalk(self.interner()), + )), + target: ty.to_chalk(self.interner()), }); - success(adjustments, ty, goals) + debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); + + success(adjustments, ty, obligations) } - /// Attempts to coerce from the type of a Rust function item into a function pointer. - fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult<'db> { - match to_ty.kind(Interner) { - TyKind::Function(_) => { - let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); - - // FIXME check ABI: Intrinsics are not coercible to function pointers - // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396) - - // FIXME rustc normalizes assoc types in the sig here, not sure if necessary - - let from_sig = from_sig.to_fn_ptr(); - let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(Interner); - let ok = self.coerce_from_safe_fn( - from_fn_pointer.clone(), - &from_sig, - to_ty, - |unsafe_ty| { - vec![ - Adjustment { - kind: Adjust::Pointer(PointerCast::ReifyFnPointer), - target: from_fn_pointer, - }, - Adjustment { - kind: Adjust::Pointer(PointerCast::UnsafeFnPointer), - target: unsafe_ty, - }, - ] + /// Performs [unsized coercion] by emulating a fulfillment loop on a + /// `CoerceUnsized` goal until all `CoerceUnsized` and `Unsize` goals + /// are successfully selected. + /// + /// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions) + #[instrument(skip(self), level = "debug")] + fn coerce_unsized(&mut self, source: Ty<'db>, target: Ty<'db>) -> CoerceResult<'db> { + debug!(?source, ?target); + debug_assert!(self.table.shallow_resolve(source) == source); + debug_assert!(self.table.shallow_resolve(target) == target); + + // We don't apply any coercions incase either the source or target + // aren't sufficiently well known but tend to instead just equate + // them both. + if source.is_infer() { + debug!("coerce_unsized: source is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + if target.is_infer() { + debug!("coerce_unsized: target is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + + // This is an optimization because coercion is one of the most common + // operations that we do in typeck, since it happens at every assignment + // and call arg (among other positions). + // + // These targets are known to never be RHS in `LHS: CoerceUnsized`. + // That's because these are built-in types for which a core-provided impl + // doesn't exist, and for which a user-written impl is invalid. + // + // This is technically incomplete when users write impossible bounds like + // `where T: CoerceUnsized`, for example, but that trait is unstable + // and coercion is allowed to be incomplete. The only case where this matters + // is impossible bounds. + // + // Note that some of these types implement `LHS: Unsize`, but they + // do not implement *`CoerceUnsized`* which is the root obligation of the + // check below. + match target.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(rustc_type_ir::IntVar(_) | rustc_type_ir::FloatVar(_)) + | TyKind::Str + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::FnDef(_, _) + | TyKind::FnPtr(_, _) + | TyKind::Dynamic(_, _, _) + | TyKind::Closure(_, _) + | TyKind::CoroutineClosure(_, _) + | TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::Never + | TyKind::Tuple(_) => return Err(TypeError::Mismatch), + _ => {} + } + // Additionally, we ignore `&str -> &str` coercions, which happen very + // commonly since strings are one of the most used argument types in Rust, + // we do coercions when type checking call expressions. + if let TyKind::Ref(_, source_pointee, Mutability::Not) = source.kind() + && source_pointee.is_str() + && let TyKind::Ref(_, target_pointee, Mutability::Not) = target.kind() + && target_pointee.is_str() + { + return Err(TypeError::Mismatch); + } + + let traits = ( + LangItem::Unsize.resolve_trait(self.table.db, self.table.trait_env.krate), + LangItem::CoerceUnsized.resolve_trait(self.table.db, self.table.trait_env.krate), + ); + let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { + debug!("missing Unsize or CoerceUnsized traits"); + return Err(TypeError::Mismatch); + }; + + // Note, we want to avoid unnecessary unsizing. We don't want to coerce to + // a DST unless we have to. This currently comes out in the wash since + // we can't unify [T] with U. But to properly support DST, we need to allow + // that, at which point we will need extra checks on the target here. + + // Handle reborrows before selecting `Source: CoerceUnsized`. + let reborrow = match (source.kind(), target.kind()) { + (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => { + coerce_mutbls(mutbl_a, mutbl_b)?; + + let r_borrow = self.table.next_region_var(); + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + let mutbl = mutbl_b.to_chalk(self.interner()); + + Some(( + Adjustment { + kind: Adjust::Deref(None), + target: ty_a.to_chalk(self.interner()), + }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref( + r_borrow.to_chalk(self.interner()), + mutbl, + )), + target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b) + .to_chalk(self.interner()), }, - simple(Adjust::Pointer(PointerCast::ReifyFnPointer)), - )?; + )) + } + (TyKind::Ref(_, ty_a, mt_a), TyKind::RawPtr(_, mt_b)) => { + coerce_mutbls(mt_a, mt_b)?; - Ok(ok) + Some(( + Adjustment { + kind: Adjust::Deref(None), + target: ty_a.to_chalk(self.interner()), + }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b.to_chalk(self.interner()))), + target: Ty::new_ptr(self.interner(), ty_a, mt_b).to_chalk(self.interner()), + }, + )) + } + _ => None, + }; + let coerce_source = + reborrow.as_ref().map_or(source, |(_, r)| r.target.to_nextsolver(self.interner())); + + // Setup either a subtyping or a LUB relationship between + // the `CoerceUnsized` target type and the expected type. + // We only have the latter, so we use an inference variable + // for the former and let type inference do the rest. + let coerce_target = self.table.next_ty_var(); + + let mut coercion = self.unify_and( + coerce_target, + target, + reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), + Adjust::Pointer(PointerCast::Unsize), + )?; + + // Create an obligation for `Source: CoerceUnsized`. + let cause = self.cause.clone(); + + // Use a FIFO queue for this custom fulfillment procedure. + // + // A Vec (or SmallVec) is not a natural choice for a queue. However, + // this code path is hot, and this queue usually has a max length of 1 + // and almost never more than 3. By using a SmallVec we avoid an + // allocation, at the (very small) cost of (occasionally) having to + // shift subsequent elements down when removing the front element. + let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new( + self.interner(), + cause, + self.table.param_env, + TraitRef::new( + self.interner(), + coerce_unsized_did.into(), + [coerce_source, coerce_target] + ) + )]; + // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid + // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where + // inference might unify those two inner type variables later. + let traits = [coerce_unsized_did, unsize_did]; + while !queue.is_empty() { + let obligation = queue.remove(0); + let trait_pred = match obligation.predicate.kind().no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred))) + if traits.contains(&trait_pred.def_id().0) => + { + self.infer_ctxt().resolve_vars_if_possible(trait_pred) + } + // Eagerly process alias-relate obligations in new trait solver, + // since these can be emitted in the process of solving trait goals, + // but we need to constrain vars before processing goals mentioning + // them. + Some(PredicateKind::AliasRelate(..)) => { + let mut ocx = ObligationCtxt::new(self.infer_ctxt()); + ocx.register_obligation(obligation); + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); + } + coercion.obligations.extend(ocx.into_pending_obligations()); + continue; + } + _ => { + coercion.obligations.push(obligation); + continue; + } + }; + debug!("coerce_unsized resolve step: {:?}", trait_pred); + match self.infer_ctxt().select(&obligation.with(self.interner(), trait_pred)) { + // Uncertain or unimplemented. + Ok(None) => { + if trait_pred.def_id().0 == unsize_did { + let self_ty = trait_pred.self_ty(); + let unsize_ty = trait_pred.trait_ref.args.inner()[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); + match (self_ty.kind(), unsize_ty.kind()) { + (TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..)) + if self.table.type_var_is_sized(v) => + { + debug!("coerce_unsized: have sized infer {:?}", v); + coercion.obligations.push(obligation); + // `$0: Unsize` where we know that `$0: Sized`, try going + // for unsizing. + } + _ => { + // Some other case for `$0: Unsize`. Note that we + // hit this case even if `Something` is a sized type, so just + // don't do the coercion. + debug!("coerce_unsized: ambiguous unsize"); + return Err(TypeError::Mismatch); + } + } + } else { + debug!("coerce_unsized: early return - ambiguous"); + if !coerce_source.references_non_lt_error() + && !coerce_target.references_non_lt_error() + { + // rustc always early-returns here, even when the types contains errors. However not bailing + // improves error recovery, and while we don't implement generic consts properly, it also helps + // correct code. + return Err(TypeError::Mismatch); + } + } + } + Err(SelectionError::Unimplemented) => { + debug!("coerce_unsized: early return - can't prove obligation"); + return Err(TypeError::Mismatch); + } + + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // written in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.set_tainted_by_errors(); + } + Err(_err) => { + // FIXME: Report an error: + // let guar = self.err_ctxt().report_selection_error( + // obligation.clone(), + // &obligation, + // &err, + // ); + self.set_tainted_by_errors(); + // Treat this like an obligation and follow through + // with the unsizing - the lack of a coercion should + // be silent, as it causes a type mismatch later. + } + + Ok(Some(ImplSource::UserDefined(impl_source))) => { + queue.extend(impl_source.nested); + } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), } - _ => self.unify_and(&from_ty, to_ty, identity), } + + Ok(coercion) } - fn coerce_from_fn_pointer( + fn coerce_from_safe_fn( &mut self, - from_ty: Ty, - from_f: &FnPointer, - to_ty: &Ty, + fn_ty_a: PolyFnSig<'db>, + b: Ty<'db>, + adjustment: Option, ) -> CoerceResult<'db> { - self.coerce_from_safe_fn( - from_ty, - from_f, - to_ty, - simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)), - identity, - ) + debug_assert!(self.table.shallow_resolve(b) == b); + + self.commit_if_ok(|this| { + if let TyKind::FnPtr(_, hdr_b) = b.kind() + && fn_ty_a.safety().is_safe() + && !hdr_b.safety.is_safe() + { + let unsafe_a = Ty::safe_to_unsafe_fn_ty(this.interner(), fn_ty_a); + this.unify_and( + unsafe_a, + b, + adjustment.map(|kind| Adjustment { + kind, + target: Ty::new_fn_ptr(this.interner(), fn_ty_a).to_chalk(this.interner()), + }), + Adjust::Pointer(PointerCast::UnsafeFnPointer), + ) + } else { + let a = Ty::new_fn_ptr(this.interner(), fn_ty_a); + match adjustment { + Some(adjust) => this.unify_and(a, b, [], adjust), + None => this.unify(a, b), + } + } + }) } - fn coerce_from_safe_fn( - &mut self, - from_ty: Ty, - from_fn_ptr: &FnPointer, - to_ty: &Ty, - to_unsafe: F, - normal: G, - ) -> CoerceResult<'db> - where - F: FnOnce(Ty) -> Vec, - G: FnOnce(Ty) -> Vec, - { - if let TyKind::Function(to_fn_ptr) = to_ty.kind(Interner) - && let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = - (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) - { - let from_unsafe = - TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(Interner); - return self.unify_and(&from_unsafe, to_ty, to_unsafe); + fn coerce_from_fn_pointer(&mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); + debug_assert!(self.table.shallow_resolve(b) == b); + + self.coerce_from_safe_fn(fn_ty_a, b, None) + } + + fn coerce_from_fn_item(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + match b.kind() { + TyKind::FnPtr(_, b_hdr) => { + let a_sig = a.fn_sig(self.interner()); + if let TyKind::FnDef(def_id, _) = a.kind() { + // Intrinsics are not coercible to function pointers + if let CallableDefId::FunctionId(def_id) = def_id.0 { + if FunctionSignature::is_intrinsic(self.table.db, def_id) { + return Err(TypeError::IntrinsicCast); + } + + let attrs = self.table.db.attrs(def_id.into()); + if attrs.by_key(sym::rustc_force_inline).exists() { + return Err(TypeError::ForceInlineCast); + } + + if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() { + let fn_target_features = + TargetFeatures::from_attrs_no_implications(&attrs); + // Allow the coercion if the current function has all the features that would be + // needed to call the coercee safely. + let (target_features, target_feature_is_safe) = + (self.target_features)(); + if target_feature_is_safe == TargetFeatureIsSafeInTarget::No + && !target_features.enabled.is_superset(&fn_target_features.enabled) + { + return Err(TypeError::TargetFeatureCast( + CallableIdWrapper(def_id.into()).into(), + )); + } + } + } + } + + self.coerce_from_safe_fn( + a_sig, + b, + Some(Adjust::Pointer(PointerCast::ReifyFnPointer)), + ) + } + _ => self.unify(a, b), } - self.unify_and(&from_ty, to_ty, normal) } - /// Attempts to coerce from the type of a non-capturing closure into a - /// function pointer. + /// Attempts to coerce from the type of a non-capturing closure + /// into a function pointer. fn coerce_closure_to_fn( &mut self, - from_ty: Ty, - from_substs: &Substitution, - to_ty: &Ty, + a: Ty<'db>, + _closure_def_id_a: InternedClosureId, + args_a: GenericArgs<'db>, + b: Ty<'db>, ) -> CoerceResult<'db> { - match to_ty.kind(Interner) { - // if from_substs is non-capturing (FIXME) - TyKind::Function(fn_ty) => { + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + match b.kind() { + // FIXME: We need to have an `upvars_mentioned()` query: + // At this point we haven't done capture analysis, which means + // that the ClosureArgs just contains an inference variable instead + // of tuple of captured types. + // + // All we care here is if any variable is being captured and not the exact paths, + // so we check `upvars_mentioned` for root variables being captured. + TyKind::FnPtr(_, hdr) => + // if self + // .db + // .upvars_mentioned(closure_def_id_a.expect_local()) + // .is_none_or(|u| u.is_empty()) => + { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to // `fn(arg0,arg1,...) -> _` // or // `unsafe fn(arg0,arg1,...) -> _` - let safety = fn_ty.sig.safety; - let pointer_ty = coerce_closure_fn_ty(from_substs, safety); + let safety = hdr.safety; + let closure_sig = args_a.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = hdr.safety; + sig + }); + let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig); + debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); self.unify_and( - &pointer_ty, - to_ty, - simple(Adjust::Pointer(PointerCast::ClosureFnPointer(safety))), + pointer_ty, + b, + [], + Adjust::Pointer(PointerCast::ClosureFnPointer( + safety.to_chalk(self.interner()), + )), ) } - _ => self.unify_and(&from_ty, to_ty, identity), + _ => self.unify(a, b), } } - /// Coerce a type using `from_ty: CoerceUnsized` - /// - /// See: - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult<'db> { - // These 'if' statements require some explanation. - // The `CoerceUnsized` trait is special - it is only - // possible to write `impl CoerceUnsized for A` where - // A and B have 'matching' fields. This rules out the following - // two types of blanket impls: - // - // `impl CoerceUnsized for SomeType` - // `impl CoerceUnsized for T` - // - // Both of these trigger a special `CoerceUnsized`-related error (E0376) - // - // We can take advantage of this fact to avoid performing unnecessary work. - // If either `source` or `target` is a type variable, then any applicable impl - // would need to be generic over the self-type (`impl CoerceUnsized for T`) - // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for - // SomeType`). - // - // However, these are exactly the kinds of impls which are forbidden by - // the compiler! Therefore, we can be sure that coercion will always fail - // when either the source or target type is a type variable. This allows us - // to skip performing any trait selection, and immediately bail out. - if from_ty.is_ty_var() { - return Err(TypeError); + fn coerce_raw_ptr(&mut self, a: Ty<'db>, b: Ty<'db>, mutbl_b: Mutability) -> CoerceResult<'db> { + debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + let (is_ref, mt_a) = match a.kind() { + TyKind::Ref(_, ty, mutbl) => (true, TypeAndMut::> { ty, mutbl }), + TyKind::RawPtr(ty, mutbl) => (false, TypeAndMut { ty, mutbl }), + _ => return self.unify(a, b), + }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; + + // Check that the types which they point at are compatible. + let a_raw = Ty::new_ptr(self.interner(), mt_a.ty, mutbl_b); + // Although references and raw ptrs have the same + // representation, we still register an Adjust::DerefRef so that + // regionck knows that the region for `a` must be valid here. + if is_ref { + self.unify_and( + a_raw, + b, + [Adjustment { + kind: Adjust::Deref(None), + target: mt_a.ty.to_chalk(self.interner()), + }], + Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b.to_chalk(self.interner()))), + ) + } else if mt_a.mutbl != mutbl_b { + self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCast::MutToConstPointer)) + } else { + self.unify(a_raw, b) } - if to_ty.is_ty_var() { - return Err(TypeError); + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CoerceNever { + No, + Yes, +} + +impl<'db> InferenceContext<'db> { + /// Attempt to coerce an expression to a type, and return the + /// adjusted type of the expression, if successful. + /// Adjustments are only recorded if the coercion succeeded. + /// The expressions *must not* have any preexisting adjustments. + pub(crate) fn coerce( + &mut self, + expr: ExprOrPatId, + expr_ty: Ty<'db>, + mut target: Ty<'db>, + allow_two_phase: AllowTwoPhase, + coerce_never: CoerceNever, + ) -> RelateResult<'db, Ty<'db>> { + let source = self.table.try_structurally_resolve_type(expr_ty); + target = self.table.try_structurally_resolve_type(target); + debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); + + let cause = ObligationCause::new(); + let krate = self.krate(); + let mut coerce = Coerce { + table: &mut self.table, + has_errors: &mut self.result.has_errors, + cause, + allow_two_phase, + coerce_never: matches!(coerce_never, CoerceNever::Yes), + use_lub: false, + target_features: &mut || { + Self::target_features(self.db, &self.target_features, self.owner, krate) + }, + }; + let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; + + let (adjustments, _) = self.table.register_infer_ok(ok); + match expr { + ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()), + ExprOrPatId::PatId(pat) => self + .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()), } + Ok(target) + } - // Handle reborrows before trying to solve `Source: CoerceUnsized`. - let reborrow = match (from_ty.kind(Interner), to_ty.kind(Interner)) { - (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => { - coerce_mutabilities(*from_mt, to_mt)?; + /// Given some expressions, their known unified type and another expression, + /// tries to unify the types, potentially inserting coercions on any of the + /// provided expressions and returns their LUB (aka "common supertype"). + /// + /// This is really an internal helper. From outside the coercion + /// module, you should instantiate a `CoerceMany` instance. + fn try_find_coercion_lub( + &mut self, + exprs: &[ExprId], + prev_ty: Ty<'db>, + new: ExprId, + new_ty: Ty<'db>, + ) -> RelateResult<'db, Ty<'db>> { + let prev_ty = self.table.try_structurally_resolve_type(prev_ty); + let new_ty = self.table.try_structurally_resolve_type(new_ty); + debug!( + "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", + prev_ty, + new_ty, + exprs.len() + ); + + // The following check fixes #88097, where the compiler erroneously + // attempted to coerce a closure type to itself via a function pointer. + if prev_ty == new_ty { + return Ok(prev_ty); + } - let lt = self.new_lifetime_var(); - Some(( - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), to_mt)), - target: TyKind::Ref(to_mt, lt, from_inner.clone()).intern(Interner), - }, - )) + let is_force_inline = |ty: Ty<'db>| { + if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() { + self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists() + } else { + false } - (TyKind::Ref(from_mt, _, from_inner), &TyKind::Raw(to_mt, _)) => { - coerce_mutabilities(*from_mt, to_mt)?; + }; + if is_force_inline(prev_ty) || is_force_inline(new_ty) { + return Err(TypeError::ForceInlineCast); + } - Some(( - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)), - target: TyKind::Raw(to_mt, from_inner.clone()).intern(Interner), - }, - )) + // Special-case that coercion alone cannot handle: + // Function items or non-capturing closures of differing IDs or GenericArgs. + let (a_sig, b_sig) = { + let is_capturing_closure = |_ty: Ty<'db>| { + // FIXME: + // if let TyKind::Closure(closure_def_id, _args) = ty.kind() { + // self.db.upvars_mentioned(closure_def_id.expect_local()).is_some() + // } else { + // false + // } + false + }; + if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { + (None, None) + } else { + match (prev_ty.kind(), new_ty.kind()) { + (TyKind::FnDef(..), TyKind::FnDef(..)) => { + // Don't reify if the function types have a LUB, i.e., they + // are the same function and their parameters have a LUB. + match self.table.commit_if_ok(|table| { + // We need to eagerly handle nested obligations due to lazy norm. + let mut ocx = ObligationCtxt::new(&table.infer_ctxt); + let value = + ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } + }) { + // We have a LUB of prev_ty and new_ty, just return it. + Ok(ok) => return Ok(self.table.register_infer_ok(ok)), + Err(_) => ( + Some(prev_ty.fn_sig(self.table.interner)), + Some(new_ty.fn_sig(self.table.interner)), + ), + } + } + (TyKind::Closure(_, args), TyKind::FnDef(..)) => { + let b_sig = new_ty.fn_sig(self.table.interner); + let a_sig = args.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = b_sig.safety(); + sig + }); + (Some(a_sig), Some(b_sig)) + } + (TyKind::FnDef(..), TyKind::Closure(_, args)) => { + let a_sig = prev_ty.fn_sig(self.table.interner); + let b_sig = args.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = a_sig.safety(); + sig + }); + (Some(a_sig), Some(b_sig)) + } + (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => { + (Some(args_a.closure_sig_untupled()), Some(args_b.closure_sig_untupled())) + } + _ => (None, None), + } } - _ => None, }; - let coerce_from = - reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone()); + if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + // The signature must match. + let sig = self + .table + .infer_ctxt + .at(&ObligationCause::new(), self.table.param_env) + .lub(a_sig, b_sig) + .map(|ok| self.table.register_infer_ok(ok))?; + + // Reify both sides and return the reified fn pointer type. + let fn_ptr = Ty::new_fn_ptr(self.table.interner, sig); + let prev_adjustment = match prev_ty.kind() { + TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( + a_sig.safety().to_chalk(self.table.interner), + )), + TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => panic!("should not try to coerce a {prev_ty:?} to a fn pointer"), + }; + let next_adjustment = match new_ty.kind() { + TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( + b_sig.safety().to_chalk(self.table.interner), + )), + TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => panic!("should not try to coerce a {new_ty:?} to a fn pointer"), + }; + for &expr in exprs { + self.write_expr_adj( + expr, + Box::new([Adjustment { + kind: prev_adjustment.clone(), + target: fn_ptr.to_chalk(self.table.interner), + }]), + ); + } + self.write_expr_adj( + new, + Box::new([Adjustment { + kind: next_adjustment, + target: fn_ptr.to_chalk(self.table.interner), + }]), + ); + return Ok(fn_ptr); + } - let krate = self.trait_env.krate; - let coerce_unsized_trait = match LangItem::CoerceUnsized.resolve_trait(self.db, krate) { - Some(trait_) => trait_, - _ => return Err(TypeError), + // Configure a Coerce instance to compute the LUB. + // We don't allow two-phase borrows on any autorefs this creates since we + // probably aren't processing function arguments here and even if we were, + // they're going to get autorefed again anyway and we can apply 2-phase borrows + // at that time. + // + // NOTE: we set `coerce_never` to `true` here because coercion LUBs only + // operate on values and not places, so a never coercion is valid. + let krate = self.krate(); + let mut coerce = Coerce { + table: &mut self.table, + has_errors: &mut self.result.has_errors, + cause: ObligationCause::new(), + allow_two_phase: AllowTwoPhase::No, + coerce_never: true, + use_lub: true, + target_features: &mut || { + Self::target_features(self.db, &self.target_features, self.owner, krate) + }, }; - let coerce_unsized_tref = { - let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); - if b.remaining() != 2 { - // The CoerceUnsized trait should have two generic params: Self and T. - return Err(TypeError); + // First try to coerce the new expression to the type of the previous ones, + // but only if the new expression has no coercion already applied to it. + let mut first_error = None; + if !self.result.expr_adjustments.contains_key(&new) { + let result = coerce.commit_if_ok(|coerce| coerce.coerce(new_ty, prev_ty)); + match result { + Ok(ok) => { + let (adjustments, target) = self.table.register_infer_ok(ok); + self.write_expr_adj(new, adjustments.into_boxed_slice()); + debug!( + "coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})", + new_ty, prev_ty, target + ); + return Ok(target); + } + Err(e) => first_error = Some(e), } - b.push(coerce_from).push(to_ty.clone()).build() - }; + } + + match coerce.commit_if_ok(|coerce| coerce.coerce(prev_ty, new_ty)) { + Err(_) => { + // Avoid giving strange errors on failed attempts. + if let Some(e) = first_error { + Err(e) + } else { + Err(self + .table + .commit_if_ok(|table| { + table + .infer_ctxt + .at(&ObligationCause::new(), table.param_env) + .lub(prev_ty, new_ty) + }) + .unwrap_err()) + } + } + Ok(ok) => { + let (adjustments, target) = self.table.register_infer_ok(ok); + for &expr in exprs { + self.write_expr_adj(expr, adjustments.as_slice().into()); + } + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", + prev_ty, new_ty, target + ); + Ok(target) + } + } + } +} - let goal: Goal = coerce_unsized_tref.cast(Interner); +/// CoerceMany encapsulates the pattern you should use when you have +/// many expressions that are all getting coerced to a common +/// type. This arises, for example, when you have a match (the result +/// of each arm is coerced to a common type). It also arises in less +/// obvious places, such as when you have many `break foo` expressions +/// that target the same loop, or the various `return` expressions in +/// a function. +/// +/// The basic protocol is as follows: +/// +/// - Instantiate the `CoerceMany` with an initial `expected_ty`. +/// This will also serve as the "starting LUB". The expectation is +/// that this type is something which all of the expressions *must* +/// be coercible to. Use a fresh type variable if needed. +/// - For each expression whose result is to be coerced, invoke `coerce()` with. +/// - In some cases we wish to coerce "non-expressions" whose types are implicitly +/// unit. This happens for example if you have a `break` with no expression, +/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`. +/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this +/// from you so that you don't have to worry your pretty head about it. +/// But if an error is reported, the final type will be `err`. +/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// - When all done, invoke `complete()`. This will return the LUB of +/// all your expressions. +/// - WARNING: I don't believe this final type is guaranteed to be +/// related to your initial `expected_ty` in any particular way, +/// although it will typically be a subtype, so you should check it. +/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// +/// Example: +/// +/// ```ignore (illustrative) +/// let mut coerce = CoerceMany::new(expected_ty); +/// for expr in exprs { +/// let expr_ty = fcx.check_expr_with_expectation(expr, expected); +/// coerce.coerce(fcx, &cause, expr, expr_ty); +/// } +/// let final_ty = coerce.complete(fcx); +/// ``` +#[derive(Debug, Clone)] +pub(crate) struct CoerceMany<'db, 'exprs> { + expected_ty: Ty<'db>, + final_ty: Option>, + expressions: Expressions<'exprs>, + pushed: usize, +} - self.commit_if_ok(|table| match table.solve_obligation(goal) { - Ok(Certainty::Yes) => Ok(()), - _ => Err(TypeError), - })?; +/// The type of a `CoerceMany` that is storing up the expressions into +/// a buffer. We use this for things like `break`. +pub(crate) type DynamicCoerceMany<'db> = CoerceMany<'db, 'db>; - let unsize = - Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; - let adjustments = match reborrow { - None => vec![unsize], - Some((deref, autoref)) => vec![deref, autoref, unsize], - }; - success(adjustments, to_ty.clone(), vec![]) - } +#[derive(Debug, Clone)] +enum Expressions<'exprs> { + Dynamic(SmallVec<[ExprId; 4]>), + UpFront(&'exprs [ExprId]), } -fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { - let closure_sig = ClosureSubst(closure_substs).sig_ty().clone(); - match closure_sig.kind(Interner) { - TyKind::Function(fn_ty) => TyKind::Function(FnPointer { - num_binders: fn_ty.num_binders, - sig: FnSig { safety, abi: FnAbi::Rust, variadic: fn_ty.sig.variadic }, - substitution: fn_ty.substitution.clone(), - }) - .intern(Interner), - _ => TyKind::Error.intern(Interner), +impl<'db, 'exprs> CoerceMany<'db, 'exprs> { + /// The usual case; collect the set of expressions dynamically. + /// If the full set of coercion sites is known before hand, + /// consider `with_coercion_sites()` instead to avoid allocation. + pub(crate) fn new(expected_ty: Ty<'db>) -> Self { + Self::make(expected_ty, Expressions::Dynamic(SmallVec::new())) } -} -fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { - FnPointer { - num_binders: fn_ty.num_binders, - sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig }, - substitution: fn_ty.substitution, + /// As an optimization, you can create a `CoerceMany` with a + /// preexisting slice of expressions. In this case, you are + /// expected to pass each element in the slice to `coerce(...)` in + /// order. This is used with arrays in particular to avoid + /// needlessly cloning the slice. + pub(crate) fn with_coercion_sites( + expected_ty: Ty<'db>, + coercion_sites: &'exprs [ExprId], + ) -> Self { + Self::make(expected_ty, Expressions::UpFront(coercion_sites)) + } + + fn make(expected_ty: Ty<'db>, expressions: Expressions<'exprs>) -> Self { + CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 } + } + + /// Returns the "expected type" with which this coercion was + /// constructed. This represents the "downward propagated" type + /// that was given to us at the start of typing whatever construct + /// we are typing (e.g., the match expression). + /// + /// Typically, this is used as the expected type when + /// type-checking each of the alternative expressions whose types + /// we are trying to merge. + pub(crate) fn expected_ty(&self) -> Ty<'db> { + self.expected_ty + } + + /// Returns the current "merged type", representing our best-guess + /// at the LUB of the expressions we've seen so far (if any). This + /// isn't *final* until you call `self.complete()`, which will return + /// the merged type. + pub(crate) fn merged_ty(&self) -> Ty<'db> { + self.final_ty.unwrap_or(self.expected_ty) } -} -fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> { - match (from, to) { - (Mutability::Mut, Mutability::Mut | Mutability::Not) - | (Mutability::Not, Mutability::Not) => Ok(()), - (Mutability::Not, Mutability::Mut) => Err(TypeError), + /// Indicates that the value generated by `expression`, which is + /// of type `expression_ty`, is one of the possibilities that we + /// could coerce from. This will record `expression`, and later + /// calls to `coerce` may come back and add adjustments and things + /// if necessary. + pub(crate) fn coerce( + &mut self, + icx: &mut InferenceContext<'db>, + cause: &ObligationCause, + expression: ExprId, + expression_ty: Ty<'db>, + ) { + self.coerce_inner(icx, cause, expression, expression_ty, false, false) + } + + /// Indicates that one of the inputs is a "forced unit". This + /// occurs in a case like `if foo { ... };`, where the missing else + /// generates a "forced unit". Another example is a `loop { break; + /// }`, where the `break` has no argument expression. We treat + /// these cases slightly differently for error-reporting + /// purposes. Note that these tend to correspond to cases where + /// the `()` expression is implicit in the source, and hence we do + /// not take an expression argument. + /// + /// The `augment_error` gives you a chance to extend the error + /// message, in case any results (e.g., we use this to suggest + /// removing a `;`). + pub(crate) fn coerce_forced_unit( + &mut self, + icx: &mut InferenceContext<'db>, + expr: ExprId, + cause: &ObligationCause, + label_unit_as_expected: bool, + ) { + self.coerce_inner( + icx, + cause, + expr, + icx.result.standard_types.unit.to_nextsolver(icx.table.interner), + true, + label_unit_as_expected, + ) + } + + /// The inner coercion "engine". If `expression` is `None`, this + /// is a forced-unit case, and hence `expression_ty` must be + /// `Nil`. + pub(crate) fn coerce_inner( + &mut self, + icx: &mut InferenceContext<'db>, + cause: &ObligationCause, + expression: ExprId, + mut expression_ty: Ty<'db>, + force_unit: bool, + label_expression_as_expected: bool, + ) { + // Incorporate whatever type inference information we have + // until now; in principle we might also want to process + // pending obligations, but doing so should only improve + // compatibility (hopefully that is true) by helping us + // uncover never types better. + if expression_ty.is_ty_var() { + expression_ty = icx.shallow_resolve(expression_ty); + } + + let (expected, found) = if label_expression_as_expected { + // In the case where this is a "forced unit", like + // `break`, we want to call the `()` "expected" + // since it is implied by the syntax. + // (Note: not all force-units work this way.)" + (expression_ty, self.merged_ty()) + } else { + // Otherwise, the "expected" type for error + // reporting is the current unification type, + // which is basically the LUB of the expressions + // we've seen so far (combined with the expected + // type) + (self.merged_ty(), expression_ty) + }; + + // Handle the actual type unification etc. + let result = if !force_unit { + if self.pushed == 0 { + // Special-case the first expression we are coercing. + // To be honest, I'm not entirely sure why we do this. + // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why + icx.coerce( + expression.into(), + expression_ty, + self.expected_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + } else { + match self.expressions { + Expressions::Dynamic(ref exprs) => icx.try_find_coercion_lub( + exprs, + self.merged_ty(), + expression, + expression_ty, + ), + Expressions::UpFront(coercion_sites) => icx.try_find_coercion_lub( + &coercion_sites[0..self.pushed], + self.merged_ty(), + expression, + expression_ty, + ), + } + } + } else { + // this is a hack for cases where we default to `()` because + // the expression etc has been omitted from the source. An + // example is an `if let` without an else: + // + // if let Some(x) = ... { } + // + // we wind up with a second match arm that is like `_ => + // ()`. That is the case we are considering here. We take + // a different path to get the right "expected, found" + // message and so forth (and because we know that + // `expression_ty` will be unit). + // + // Another example is `break` with no argument expression. + assert!(expression_ty.is_unit(), "if let hack without unit type"); + icx.table + .infer_ctxt + .at(cause, icx.table.param_env) + .eq( + // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs + DefineOpaqueTypes::Yes, + expected, + found, + ) + .map(|infer_ok| { + icx.table.register_infer_ok(infer_ok); + expression_ty + }) + }; + + debug!(?result); + match result { + Ok(v) => { + self.final_ty = Some(v); + match self.expressions { + Expressions::Dynamic(ref mut buffer) => buffer.push(expression), + Expressions::UpFront(coercion_sites) => { + // if the user gave us an array to validate, check that we got + // the next expression in the list, as expected + assert_eq!(coercion_sites[self.pushed], expression); + } + } + } + Err(_coercion_error) => { + // Mark that we've failed to coerce the types here to suppress + // any superfluous errors we might encounter while trying to + // emit or provide suggestions on how to fix the initial error. + icx.set_tainted_by_errors(); + + self.final_ty = Some(Ty::new_error(icx.table.interner, ErrorGuaranteed)); + + icx.result.type_mismatches.insert( + expression.into(), + if label_expression_as_expected { + TypeMismatch { + expected: found.to_chalk(icx.table.interner), + actual: expected.to_chalk(icx.table.interner), + } + } else { + TypeMismatch { + expected: expected.to_chalk(icx.table.interner), + actual: found.to_chalk(icx.table.interner), + } + }, + ); + } + } + + self.pushed += 1; + } + + pub(crate) fn complete(self, icx: &mut InferenceContext<'db>) -> Ty<'db> { + if let Some(final_ty) = self.final_ty { + final_ty + } else { + // If we only had inputs that were of type `!` (or no + // inputs at all), then the final type is `!`. + assert_eq!(self.pushed, 0); + icx.result.standard_types.never.to_nextsolver(icx.table.interner) + } } } -pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec { - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); - steps - .iter() - .map(|(kind, _source)| match kind { - // We do not know what kind of deref we require at this point yet - AutoderefKind::Overloaded => Some(OverloadedDeref(None)), - AutoderefKind::Builtin => None, - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect() +pub fn could_coerce( + db: &dyn HirDatabase, + env: Arc, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, +) -> bool { + coerce(db, env, tys).is_ok() +} + +fn coerce<'db>( + db: &'db dyn HirDatabase, + env: Arc, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, +) -> Result<(Vec, crate::Ty), TypeError>> { + let mut table = InferenceTable::new(db, env); + let vars = table.fresh_subst(tys.binders.as_slice(Interner)); + let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); + let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); + + let cause = ObligationCause::new(); + // FIXME: Target features. + let target_features = TargetFeatures::default(); + let mut coerce = Coerce { + table: &mut table, + has_errors: &mut false, + cause, + allow_two_phase: AllowTwoPhase::No, + coerce_never: true, + use_lub: false, + target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No), + }; + let InferOk { value: (adjustments, ty), obligations } = coerce.coerce( + ty1_with_vars.to_nextsolver(coerce.table.interner), + ty2_with_vars.to_nextsolver(coerce.table.interner), + )?; + table.register_predicates(obligations); + + // default any type vars that weren't unified back to their original bound vars + // (kind of hacky) + let find_var = |iv| { + vars.iter(Interner).position(|v| match v.interned() { + chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), + chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + } == Some(iv)) + }; + let fallback = |iv, kind, default, binder| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) + .map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), + chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { + crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner) + }), + chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { + crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner) + }), + }; + // FIXME also map the types in the adjustments + Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0a58ea11bb871..c5a51dfc4cf92 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1,12 +1,10 @@ //! Type inference for expressions. -use std::{ - iter::{repeat, repeat_with}, - mem, -}; +use std::{iter::repeat_with, mem}; use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast}; use either::Either; +use hir_def::hir::ClosureKind; use hir_def::{ BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, expr_store::path::{GenericArg, GenericArgs, Path}, @@ -19,19 +17,23 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use stdx::always; use syntax::ast::RangeOp; +use tracing::debug; +use crate::autoderef::overloaded_deref_ty; +use crate::next_solver::ErrorGuaranteed; +use crate::next_solver::infer::DefineOpaqueTypes; +use crate::next_solver::obligation_ctxt::ObligationCtxt; use crate::{ Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, - Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - autoderef::{Autoderef, builtin_deref, deref_by_trait}, - consteval, + Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval, generics::generics, infer::{ - BreakableKind, - coerce::{CoerceMany, CoerceNever, CoercionCause}, + AllowTwoPhase, BreakableKind, + coerce::{CoerceMany, CoerceNever}, find_continuable, pat::contains_explicit_ref_binding, }, @@ -42,7 +44,10 @@ use crate::{ }, mapping::{ToChalk, from_chalk}, method_resolution::{self, VisibleFromModule}, - next_solver::mapping::ChalkToNextSolver, + next_solver::{ + infer::traits::ObligationCause, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, @@ -50,7 +55,7 @@ use crate::{ use super::{ BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, - cast::CastCheck, coerce::auto_deref_adjust_steps, find_breakable, + cast::CastCheck, find_breakable, }; #[derive(Clone, Copy, PartialEq, Eq)] @@ -59,7 +64,7 @@ pub(crate) enum ExprIsRead { No, } -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(crate) fn infer_expr( &mut self, tgt_expr: ExprId, @@ -98,8 +103,14 @@ impl InferenceContext<'_> { } else { CoerceNever::No }; - match self.coerce(Some(expr), &ty, &target, coerce_never) { - Ok(res) => res, + match self.coerce( + expr.into(), + ty.to_nextsolver(self.table.interner), + target.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + coerce_never, + ) { + Ok(res) => res.to_chalk(self.table.interner), Err(_) => { self.result.type_mismatches.insert( expr.into(), @@ -260,8 +271,15 @@ impl InferenceContext<'_> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(Some(expr), &ty, &target, CoerceNever::Yes) - .expect("never-to-any coercion should always succeed") + self.coerce( + expr.into(), + ty.to_nextsolver(self.table.interner), + target.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .expect("never-to-any coercion should always succeed") + .to_chalk(self.table.interner) } else { ty } @@ -304,27 +322,41 @@ impl InferenceContext<'_> { let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes); let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); - coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); + let mut coercion_sites = [then_branch, tgt_expr]; + if let Some(else_branch) = else_branch { + coercion_sites[1] = else_branch; + } + let mut coerce = CoerceMany::with_coercion_sites( + expected + .coercion_target_type(&mut self.table) + .to_nextsolver(self.table.interner), + &coercion_sites, + ); + coerce.coerce( + self, + &ObligationCause::new(), + then_branch, + then_ty.to_nextsolver(self.table.interner), + ); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); coerce.coerce( self, - Some(else_branch), - &else_ty, - CoercionCause::Expr(else_branch), + &ObligationCause::new(), + else_branch, + else_ty.to_nextsolver(self.table.interner), ); self.diverges = condition_diverges | then_diverges & else_diverges; } None => { - coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr)); + coerce.coerce_forced_unit(self, tgt_expr, &ObligationCause::new(), true); self.diverges = condition_diverges; } } - coerce.complete(self) + coerce.complete(self).to_chalk(self.table.interner) } &Expr::Let { pat, expr } => { let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) { @@ -377,7 +409,15 @@ impl InferenceContext<'_> { } } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self - .infer_closure(body, args, ret_type, arg_types, *closure_kind, tgt_expr, expected), + .infer_closure( + *body, + args, + *ret_type, + arg_types, + *closure_kind, + tgt_expr, + expected, + ), Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected), Expr::MethodCall { receiver, args, method_name, generic_args } => self .infer_method_call( @@ -416,7 +456,7 @@ impl InferenceContext<'_> { } _ => self.table.new_type_var(), }; - let mut coerce = CoerceMany::new(result_ty); + let mut coerce = CoerceMany::new(result_ty.to_nextsolver(self.table.interner)); for arm in arms.iter() { if let Some(guard_expr) = arm.guard { @@ -431,12 +471,17 @@ impl InferenceContext<'_> { let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes); all_arms_diverge &= self.diverges; - coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); + coerce.coerce( + self, + &ObligationCause::new(), + arm.expr, + arm_ty.to_nextsolver(self.table.interner), + ); } self.diverges = matchee_diverges | all_arms_diverge; - coerce.complete(self) + coerce.complete(self).to_chalk(self.table.interner) } } Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr), @@ -454,7 +499,7 @@ impl InferenceContext<'_> { let val_ty = if let Some(expr) = expr { let opt_coerce_to = match find_breakable(&mut self.breakables, label) { Some(ctxt) => match &ctxt.coerce { - Some(coerce) => coerce.expected_ty(), + Some(coerce) => coerce.expected_ty().to_chalk(self.table.interner), None => { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, @@ -478,11 +523,12 @@ impl InferenceContext<'_> { match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { - let cause = match expr { - Some(expr) => CoercionCause::Expr(expr), - None => CoercionCause::Expr(tgt_expr), - }; - coerce.coerce(self, expr, &val_ty, cause); + coerce.coerce( + self, + &ObligationCause::new(), + expr.unwrap_or(tgt_expr), + val_ty.to_nextsolver(self.table.interner), + ); // Avoiding borrowck let ctxt = find_breakable(&mut self.breakables, label) @@ -514,7 +560,13 @@ impl InferenceContext<'_> { ); } else { let unit = self.result.standard_types.unit.clone(); - let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes); + let _ = self.coerce( + tgt_expr.into(), + unit.to_nextsolver(self.table.interner), + yield_ty.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } resume_ty } else { @@ -670,11 +722,23 @@ impl InferenceContext<'_> { Substitution::empty(Interner), ); } - if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { - self.table.structurally_resolve_type(derefed) + if let Some(derefed) = + inner_ty.to_nextsolver(self.table.interner).builtin_deref(self.db, true) + { + self.table + .structurally_resolve_type(&derefed.to_chalk(self.table.interner)) } else { - deref_by_trait(&mut self.table, inner_ty, false) - .unwrap_or_else(|| self.err_ty()) + let infer_ok = overloaded_deref_ty( + &self.table, + inner_ty.to_nextsolver(self.table.interner), + ); + match infer_ok { + Some(infer_ok) => self + .table + .register_infer_ok(infer_ok) + .to_chalk(self.table.interner), + None => self.err_ty(), + } } } UnaryOp::Neg => { @@ -1010,11 +1074,23 @@ impl InferenceContext<'_> { CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(), ) .intern(Interner); - _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); + _ = this.coerce( + expr.into(), + ty.to_nextsolver(this.table.interner), + fnptr_ty.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } TyKind::Ref(mutbl, _, base_ty) => { let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); - _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); + _ = this.coerce( + expr.into(), + ty.to_nextsolver(this.table.interner), + ptr_ty.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } _ => {} } @@ -1092,15 +1168,23 @@ impl InferenceContext<'_> { let ret_ty = self.table.new_type_var(); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty.clone())); + let prev_ret_coercion = self + .return_coercion + .replace(CoerceMany::new(ret_ty.to_nextsolver(self.table.interner))); // FIXME: We should handle async blocks like we handle closures let expected = &Expectation::has_type(ret_ty); let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) { - Ok(res) => res, + match this.coerce( + tgt_expr.into(), + ty.to_nextsolver(this.table.interner), + target.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(res) => res.to_chalk(this.table.interner), Err(_) => { this.result.type_mismatches.insert( tgt_expr.into(), @@ -1209,13 +1293,21 @@ impl InferenceContext<'_> { (elem_ty, consteval::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::new(elem_ty); + let mut coerce = CoerceMany::with_coercion_sites( + elem_ty.to_nextsolver(self.table.interner), + elements, + ); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); + coerce.coerce( + self, + &ObligationCause::new(), + expr, + cur_elem_ty.to_nextsolver(self.table.interner), + ); } ( - coerce.complete(self), + coerce.complete(self).to_chalk(self.table.interner), consteval::usize_const(self.db, Some(elements.len() as u128), krate), ) } @@ -1254,11 +1346,17 @@ impl InferenceContext<'_> { .return_coercion .as_mut() .expect("infer_return called outside function body") - .expected_ty(); + .expected_ty() + .to_chalk(self.table.interner); let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); + coerce_many.coerce( + self, + &ObligationCause::new(), + expr, + return_expr_ty.to_nextsolver(self.table.interner), + ); self.return_coercion = Some(coerce_many); } @@ -1269,7 +1367,7 @@ impl InferenceContext<'_> { self.infer_return(expr); } else { let mut coerce = self.return_coercion.take().unwrap(); - coerce.coerce_forced_unit(self, CoercionCause::Expr(ret)); + coerce.coerce_forced_unit(self, ret, &ObligationCause::new(), true); self.return_coercion = Some(coerce); } } @@ -1286,7 +1384,7 @@ impl InferenceContext<'_> { fn infer_expr_become(&mut self, expr: ExprId) -> Ty { match &self.return_coercion { Some(return_coercion) => { - let ret_ty = return_coercion.expected_ty(); + let ret_ty = return_coercion.expected_ty().to_chalk(self.table.interner); let call_expr_ty = self.infer_expr_inner( expr, @@ -1540,9 +1638,10 @@ impl InferenceContext<'_> { }; if this .coerce( - Some(expr), - &this.result.standard_types.unit.clone(), - &t, + expr.into(), + this.result.standard_types.unit.to_nextsolver(this.table.interner), + t.to_nextsolver(this.table.interner), + AllowTwoPhase::No, coerce_never, ) .is_err() @@ -1563,6 +1662,7 @@ impl InferenceContext<'_> { }); self.resolver.reset_to_guard(g); if let Some(prev_env) = prev_env { + self.table.param_env = prev_env.env.to_nextsolver(self.table.interner); self.table.trait_env = prev_env; } @@ -1574,50 +1674,49 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); + let interner = self.table.interner; + let mut autoderef = self.table.autoderef(receiver_ty.to_nextsolver(self.table.interner)); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { - let (field_id, parameters) = match derefed_ty.kind(Interner) { - TyKind::Tuple(_, substs) => { + let (field_id, parameters) = match derefed_ty.kind() { + crate::next_solver::TyKind::Tuple(substs) => { return name.as_tuple_index().and_then(|idx| { - substs - .as_slice(Interner) - .get(idx) - .map(|a| a.assert_ty_ref(Interner)) - .cloned() - .map(|ty| { - ( - Either::Right(TupleFieldId { - tuple: TupleId( - self.tuple_field_accesses_rev - .insert_full(substs.clone()) - .0 - as u32, - ), - index: idx as u32, - }), - ty, - ) - }) + substs.as_slice().get(idx).copied().map(|ty| { + ( + Either::Right(TupleFieldId { + tuple: TupleId( + self.tuple_field_accesses_rev + .insert_full(substs.to_chalk(interner)) + .0 as u32, + ), + index: idx as u32, + }), + ty.to_chalk(interner), + ) + }) }); } - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref parameters) => { - let local_id = s.fields(self.db).field(name)?; - let field = FieldId { parent: s.into(), local_id }; - (field, parameters.clone()) - } - &TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), ref parameters) => { - let local_id = u.fields(self.db).field(name)?; - let field = FieldId { parent: u.into(), local_id }; - (field, parameters.clone()) - } + crate::next_solver::TyKind::Adt(adt, parameters) => match adt.def_id().0 { + hir_def::AdtId::StructId(s) => { + let local_id = s.fields(self.db).field(name)?; + let field = FieldId { parent: s.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::UnionId(u) => { + let local_id = u.fields(self.db).field(name)?; + let field = FieldId { parent: u.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::EnumId(_) => return None, + }, _ => return None, }; + let parameters: crate::Substitution = parameters.to_chalk(interner); let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] .is_visible_from(self.db, self.resolver.module()); if !is_visible { if private_field.is_none() { - private_field = Some((field_id, parameters)); + private_field = Some((field_id, parameters.clone())); } return None; } @@ -1629,14 +1728,14 @@ impl InferenceContext<'_> { Some(match res { Some((field_id, ty)) => { - let adjustments = auto_deref_adjust_steps(&autoderef); + let adjustments = autoderef.adjust_steps(); let ty = self.process_remote_user_written_ty(ty); (ty, field_id, adjustments, true) } None => { let (field_id, subst) = private_field?; - let adjustments = auto_deref_adjust_steps(&autoderef); + let adjustments = autoderef.adjust_steps(); let ty = self.db.field_types(field_id.parent)[field_id.local_id] .clone() .substitute(Interner, &subst); @@ -1725,11 +1824,13 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); + let interner = self.table.interner; + let mut derefs = self.table.autoderef(callee_ty.to_nextsolver(interner)); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); }; + let callee_deref_ty = callee_deref_ty.to_chalk(interner); if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { break (Some(res), callee_deref_ty); } @@ -1740,28 +1841,30 @@ impl InferenceContext<'_> { derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let mut adjustments = auto_deref_adjust_steps(&derefs); - if let TyKind::Closure(c, _) = - self.table.resolve_completely(callee_ty.clone()).kind(Interner) - { - self.add_current_closure_dependency(*c); - self.deferred_closures.entry(*c).or_default().push(( - derefed_callee.clone(), - callee_ty.clone(), - params.clone(), - tgt_expr, - )); - } + let params_chalk = + params.iter().map(|param| param.to_chalk(interner)).collect::>(); + let mut adjustments = derefs.adjust_steps(); if let Some(fn_x) = func { self.write_fn_trait_method_resolution( fn_x, &derefed_callee, &mut adjustments, &callee_ty, - ¶ms, + ¶ms_chalk, tgt_expr, ); } + if let &TyKind::Closure(c, _) = + self.table.resolve_completely(callee_ty.clone()).kind(Interner) + { + self.add_current_closure_dependency(c.into()); + self.deferred_closures.entry(c.into()).or_default().push(( + derefed_callee.clone(), + callee_ty.clone(), + params_chalk, + tgt_expr, + )); + } self.write_expr_adj(callee, adjustments.into_boxed_slice()); (params, ret_ty) } @@ -1770,7 +1873,7 @@ impl InferenceContext<'_> { call_expr: tgt_expr, found: callee_ty.clone(), }); - (Vec::new(), self.err_ty()) + (Vec::new(), crate::next_solver::Ty::new_error(interner, ErrorGuaranteed)) } }; let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args); @@ -1791,29 +1894,24 @@ impl InferenceContext<'_> { tgt_expr: ExprId, args: &[ExprId], callee_ty: Ty, - param_tys: &[Ty], - ret_ty: Ty, + param_tys: &[crate::next_solver::Ty<'db>], + ret_ty: crate::next_solver::Ty<'db>, indices_to_skip: &[u32], is_varargs: bool, expected: &Expectation, ) -> Ty { self.register_obligations_for_call(&callee_ty); - let expected_inputs = self.expected_inputs_for_expected_output( - expected, - ret_ty.clone(), - param_tys.to_owned(), - ); - self.check_call_arguments( tgt_expr, - args, - &expected_inputs, param_tys, + ret_ty, + expected, + args, indices_to_skip, is_varargs, ); - self.normalize_associated_types_in(ret_ty) + self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(self.table.interner) } fn infer_method_call( @@ -1826,6 +1924,21 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); + let receiver_ty = self.table.structurally_resolve_type(&receiver_ty); + + if matches!( + receiver_ty.kind(Interner), + TyKind::Error | TyKind::InferenceVar(_, TyVariableKind::General) + ) { + // Don't probe on error type, or on a fully unresolved infer var. + // FIXME: Emit an error if we're probing on an infer var (type annotations needed). + for &arg in args { + // Make sure we infer and record the arguments. + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } + return receiver_ty; + } + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); @@ -1918,14 +2031,21 @@ impl InferenceContext<'_> { tgt_expr, args, callee_ty, - sig.params().get(strip_first as usize..).unwrap_or(&[]), - sig.ret().clone(), + &sig.params() + .get(strip_first as usize..) + .unwrap_or(&[]) + .iter() + .map(|param| param.to_nextsolver(self.table.interner)) + .collect::>(), + sig.ret().to_nextsolver(self.table.interner), &[], true, expected, ), None => { - self.check_call_arguments(tgt_expr, args, &[], &[], &[], true); + for &arg in args.iter() { + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } self.err_ty() } } @@ -1944,151 +2064,252 @@ impl InferenceContext<'_> { ) -> Ty { let method_ty = method_ty.substitute(Interner, &substs); self.register_obligations_for_call(&method_ty); + let interner = self.table.interner; let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = match method_ty.callable_sig(self.db) { Some(sig) => ( if !sig.params().is_empty() { - (sig.params()[0].clone(), sig.params()[1..].to_vec()) + ( + sig.params()[0].to_nextsolver(interner), + sig.params()[1..] + .iter() + .map(|param| param.to_nextsolver(interner)) + .collect(), + ) } else { - (self.err_ty(), Vec::new()) + (crate::next_solver::Ty::new_error(interner, ErrorGuaranteed), Vec::new()) }, - sig.ret().clone(), + sig.ret().to_nextsolver(interner), sig.is_varargs, ), None => { - let formal_receiver_ty = self.table.new_type_var(); - let ret_ty = self.table.new_type_var(); + let formal_receiver_ty = self.table.next_ty_var(); + let ret_ty = self.table.next_ty_var(); ((formal_receiver_ty, Vec::new()), ret_ty, true) } }; - self.unify(&formal_receiver_ty, &receiver_ty); - - let expected_inputs = - self.expected_inputs_for_expected_output(expected, ret_ty.clone(), param_tys.clone()); + self.table.unify_ns(formal_receiver_ty, receiver_ty.to_nextsolver(interner)); - self.check_call_arguments(tgt_expr, args, &expected_inputs, ¶m_tys, &[], is_varargs); - self.normalize_associated_types_in(ret_ty) + self.check_call_arguments(tgt_expr, ¶m_tys, ret_ty, expected, args, &[], is_varargs); + self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(interner) } - fn expected_inputs_for_expected_output( + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(in super::super) fn check_call_arguments( &mut self, - expected_output: &Expectation, - output: Ty, - inputs: Vec, - ) -> Vec { - if let Some(expected_ty) = expected_output.only_has_type(&mut self.table) { - self.table.fudge_inference(|table| { - if table.try_unify(&expected_ty, &output).is_ok() { - table.resolve_with_fallback(inputs, &|var, kind, _, _| match kind { - chalk_ir::VariableKind::Ty(tk) => var.to_ty(Interner, tk).cast(Interner), - chalk_ir::VariableKind::Lifetime => { - var.to_lifetime(Interner).cast(Interner) - } - chalk_ir::VariableKind::Const(ty) => { - var.to_const(Interner, ty).cast(Interner) + call_expr: ExprId, + // Types (as defined in the *signature* of the target function) + formal_input_tys: &[crate::next_solver::Ty<'db>], + formal_output: crate::next_solver::Ty<'db>, + // Expected output from the parent expression or statement + expectation: &Expectation, + // The expressions for each provided argument + provided_args: &[ExprId], + skip_indices: &[u32], + // Whether the function is variadic, for example when imported from C + c_variadic: bool, + ) { + let interner = self.table.interner; + + // First, let's unify the formal method signature with the expectation eagerly. + // We use this to guide coercion inference; it's output is "fudged" which means + // any remaining type variables are assigned to new, unrelated variables. This + // is because the inference guidance here is only speculative. + let formal_output = self.table.resolve_vars_with_obligations(formal_output); + let expected_input_tys: Option> = expectation + .only_has_type(&mut self.table) + .and_then(|expected_output| { + self.table + .infer_ctxt + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(&self.table.infer_ctxt); + + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = ObligationCause::new(); + ocx.sup( + &origin, + self.table.param_env, + expected_output.to_nextsolver(interner), + formal_output, + )?; + if !ocx.select_where_possible().is_empty() { + return Err(crate::next_solver::TypeError::Mismatch); } + + // Record all the argument types, with the args + // produced from the above subtyping unification. + Ok(Some( + formal_input_tys + .iter() + .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty)) + .collect(), + )) }) - } else { - Vec::new() - } + .ok() }) + .unwrap_or_default(); + + // If there are no external expectations at the call site, just use the types from the function defn + let expected_input_tys = if let Some(expected_input_tys) = &expected_input_tys { + assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + expected_input_tys } else { - Vec::new() - } - } + formal_input_tys + }; - fn check_call_arguments( - &mut self, - expr: ExprId, - args: &[ExprId], - expected_inputs: &[Ty], - param_tys: &[Ty], - skip_indices: &[u32], - ignore_arg_param_mismatch: bool, - ) { - let arg_count_mismatch = - !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len(); - if arg_count_mismatch { + let minimum_input_count = expected_input_tys.len(); + let provided_arg_count = provided_args.len() - skip_indices.len(); + + // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path + // if the wrong number of arguments were supplied, we CAN'T be satisfied, + // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function + // otherwise, they need to be identical, because rust doesn't currently support variadic functions + let args_count_matches = if c_variadic { + provided_arg_count >= minimum_input_count + } else { + provided_arg_count == minimum_input_count + }; + + if !args_count_matches { self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount { - call_expr: expr, - expected: param_tys.len() + skip_indices.len(), - found: args.len(), + call_expr, + expected: expected_input_tys.len() + skip_indices.len(), + found: provided_args.len(), }); + } + + // We introduce a helper function to demand that a given argument satisfy a given input + // This is more complicated than just checking type equality, as arguments could be coerced + // This version writes those types back so further type checking uses the narrowed types + let demand_compatible = |this: &mut InferenceContext<'db>, idx| { + let formal_input_ty: crate::next_solver::Ty<'db> = formal_input_tys[idx]; + let expected_input_ty: crate::next_solver::Ty<'db> = expected_input_tys[idx]; + let provided_arg = provided_args[idx]; + + debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); + + // We're on the happy path here, so we'll do a more involved check and write back types + // To check compatibility, we'll do 3 things: + // 1. Unify the provided argument with the expected type + let expectation = Expectation::rvalue_hint(this, expected_input_ty.to_chalk(interner)); + + let checked_ty = this + .infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes) + .to_nextsolver(interner); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerced_ty = expectation + .only_has_type(&mut this.table) + .map(|it| it.to_nextsolver(interner)) + .unwrap_or(formal_input_ty); + + // Cause selection errors caused by resolving a single argument to point at the + // argument and not the call. This lets us customize the span pointed to in the + // fulfillment error to be more accurate. + let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); + + let coerce_never = if this + .expr_guaranteed_to_constitute_read_for_never(provided_arg, ExprIsRead::Yes) + { + CoerceNever::Yes + } else { + CoerceNever::No + }; + let coerce_error = this + .coerce( + provided_arg.into(), + checked_ty, + coerced_ty, + AllowTwoPhase::Yes, + coerce_never, + ) + .err(); + if coerce_error.is_some() { + return Err((coerce_error, coerced_ty, checked_ty)); + } + + // 3. Check if the formal type is actually equal to the checked one + // and register any such obligations for future type checks. + let formal_ty_error = this + .table + .infer_ctxt + .at(&ObligationCause::new(), this.table.param_env) + .eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty); + + // If neither check failed, the types are compatible + match formal_ty_error { + Ok(crate::next_solver::infer::InferOk { obligations, value: () }) => { + this.table.register_predicates(obligations); + Ok(()) + } + Err(err) => Err((Some(err), coerced_ty, checked_ty)), + } }; - // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 -- + // Check the arguments. // We do this in a pretty awful way: first we type-check any arguments // that are not closures, then we type-check the closures. This is so // that we have more information about the types of arguments when we // type-check the functions. This isn't really the right way to do this. for check_closures in [false, true] { - let mut skip_indices = skip_indices.iter().copied().fuse().peekable(); - let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty())); - let expected_iter = expected_inputs - .iter() - .cloned() - .chain(param_iter.clone().skip(expected_inputs.len())); - for (idx, ((&arg, param_ty), expected_ty)) in - args.iter().zip(param_iter).zip(expected_iter).enumerate() - { - let is_closure = matches!(&self.body[arg], Expr::Closure { .. }); - if is_closure != check_closures { + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.table.select_obligations_where_possible(); + } + + let mut skip_indices = skip_indices.iter().copied(); + // Check each argument, to satisfy the input it was provided for + // Visually, we're traveling down the diagonal of the compatibility matrix + for (idx, arg) in provided_args.iter().enumerate() { + if skip_indices.clone().next() == Some(idx as u32) { + skip_indices.next(); continue; } - while skip_indices.peek().is_some_and(|&i| i < idx as u32) { - skip_indices.next(); + // For this check, we do *not* want to treat async coroutine closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See . + let is_closure = if let Expr::Closure { closure_kind, .. } = self.body[*arg] { + !matches!(closure_kind, ClosureKind::Coroutine(_)) + } else { + false + }; + if is_closure != check_closures { + continue; } - if skip_indices.peek().copied() == Some(idx as u32) { + + if idx >= minimum_input_count { + // Make sure we've checked this expr at least once. + self.infer_expr_no_expect(*arg, ExprIsRead::Yes); continue; } - // the difference between param_ty and expected here is that - // expected is the parameter when the expected *return* type is - // taken into account. So in `let _: &[i32] = identity(&[1, 2])` - // the expected type is already `&[i32]`, whereas param_ty is - // still an unbound type variable. We don't always want to force - // the parameter to coerce to the expected type (for example in - // `coerce_unsize_expected_type_4`). - let param_ty = self.normalize_associated_types_in(param_ty); - let expected_ty = self.normalize_associated_types_in(expected_ty); - let expected = Expectation::rvalue_hint(self, expected_ty); - // infer with the expected type we have... - let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes); - - // then coerce to either the expected type or just the formal parameter type - let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) { - // if we are coercing to the expectation, unify with the - // formal parameter type to connect everything - self.unify(&ty, ¶m_ty); - ty - } else { - param_ty - }; - // The function signature may contain some unknown types, so we need to insert - // type vars here to avoid type mismatch false positive. - let coercion_target = self.insert_type_vars(coercion_target); - - // Any expression that produces a value of type `!` must have diverged, - // unless it's a place expression that isn't being read from, in which case - // diverging would be unsound since we may never actually read the `!`. - // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - let coerce_never = - if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) { - CoerceNever::Yes - } else { - CoerceNever::No - }; - if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err() - && !arg_count_mismatch + if let Err((_error, expected, found)) = demand_compatible(self, idx) + && args_count_matches { + // Don't report type mismatches if there is a mismatch in args count. self.result.type_mismatches.insert( - arg.into(), - TypeMismatch { expected: coercion_target, actual: ty.clone() }, + (*arg).into(), + TypeMismatch { + expected: expected.to_chalk(interner), + actual: found.to_chalk(interner), + }, ); } } } + + if !args_count_matches {} } fn substs_for_method_call( @@ -2448,10 +2669,22 @@ impl InferenceContext<'_> { cb: impl FnOnce(&mut Self) -> T, ) -> (Option, T) { self.breakables.push({ - BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } + BreakableContext { + kind, + may_break: false, + coerce: ty.map(|ty| CoerceMany::new(ty.to_nextsolver(self.table.interner))), + label, + } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + ( + if ctx.may_break { + ctx.coerce.map(|ctx| ctx.complete(self).to_chalk(self.table.interner)) + } else { + None + }, + res, + ) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 6781bc84d1c11..6e11fa942bdfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -10,6 +10,8 @@ use hir_def::{ use hir_expand::name::Name; use stdx::TupleExt; +use crate::infer::AllowTwoPhase; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind, @@ -303,16 +305,15 @@ impl InferenceContext<'_> { Pat::Path(path) => { let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone()); - match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) { - Ok((adjustments, coerced_ty)) => { - if !adjustments.is_empty() { - self.result - .pat_adjustments - .entry(pat) - .or_default() - .extend(adjustments.into_iter().map(|adjust| adjust.target)); - } - self.write_pat_ty(pat, coerced_ty); + match self.coerce( + pat.into(), + expected.to_nextsolver(self.table.interner), + ty_inserted_vars.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(coerced_ty) => { + self.write_pat_ty(pat, coerced_ty.to_chalk(self.table.interner)); return self.pat_ty_after_adjustment(pat); } Err(_) => { @@ -387,8 +388,14 @@ impl InferenceContext<'_> { ); // We are returning early to avoid the unifiability check below. let lhs_ty = self.insert_type_vars_shallow(result); - let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) { - Ok(ty) => ty, + let ty = match self.coerce( + pat.into(), + expected.to_nextsolver(self.table.interner), + lhs_ty.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(ty) => ty.to_chalk(self.table.interner), Err(_) => { self.result.type_mismatches.insert( pat.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 19b83d3c212df..77eaf83eec73d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{fmt, mem}; +use std::fmt; use chalk_ir::{ CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable, @@ -11,31 +11,37 @@ use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_next_trait_solver::solve::HasChanged; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::Ty as _; use rustc_type_ir::{ - AliasRelationDirection, FloatVid, IntVid, TyVid, - inherent::{Span, Term as _}, + FloatVid, IntVid, TyVid, TypeVisitableExt, + inherent::{IntoKind, Span, Term as _}, relate::{Relate, solver_relating::RelateExt}, - solve::{Certainty, NoSolution}, + solve::{Certainty, GoalSource, NoSolution}, }; -use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt}; use smallvec::SmallVec; use triomphe::Arc; -use super::{InferOk, InferResult, InferenceContext, TypeError}; +use super::{InferResult, InferenceContext, TypeError}; +use crate::next_solver::ErrorGuaranteed; use crate::{ - AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, - OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData, + Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, + ProjectionTy, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + VariableKind, WhereClause, consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, + next_solver::infer::InferOk, next_solver::{ - self, Binder, DbInterner, Predicate, PredicateKind, SolverDefIds, Term, - infer::{DbInternerInferExt, InferCtxt, snapshot::CombinedSnapshot}, - mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, + self, ClauseKind, DbInterner, ParamEnv, Predicate, PredicateKind, SolverDefIds, Term, + fulfill::FulfillmentCtxt, + infer::{ + DbInternerInferExt, InferCtxt, + snapshot::CombinedSnapshot, + traits::{Obligation, ObligationCause}, + }, + inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, + mapping::{ChalkToNextSolver, NextSolverToChalk}, }, to_chalk_trait_id, traits::{ @@ -50,43 +56,64 @@ impl<'db> InferenceContext<'db> { { self.table.canonicalize(t) } +} - pub(super) fn clauses_for_self_ty( - &mut self, - self_ty: InferenceVar, - ) -> SmallVec<[WhereClause; 4]> { - self.table.resolve_obligations_as_possible(); - - let root = InferenceVar::from_vid(self.table.infer_ctxt.root_var(self_ty.to_vid())); - let pending_obligations = mem::take(&mut self.table.pending_obligations); - let obligations = pending_obligations - .iter() - .filter_map(|obligation| match obligation.to_chalk(self.table.interner).goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(clause)) => { - let ty = match clause { - WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), - .. - }) => projection.self_type_parameter(self.db), - WhereClause::Implemented(trait_ref) => { - trait_ref.self_type_parameter(Interner) - } - WhereClause::TypeOutlives(to) => to.ty.clone(), - _ => return None, - }; - let ty = self.resolve_ty_shallow(&ty); - if matches!(ty.kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root) { - Some(clause.clone()) - } else { - None - } - } - _ => None, - }) - .collect(); - self.table.pending_obligations = pending_obligations; +struct NestedObligationsForSelfTy<'a, 'db> { + ctx: &'a InferenceTable<'db>, + self_ty: TyVid, + root_cause: &'a ObligationCause, + obligations_for_self_ty: &'a mut SmallVec<[Obligation<'db, Predicate<'db>>; 4]>, +} - obligations +impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { + type Result = (); + + fn config(&self) -> InspectConfig { + // Using an intentionally low depth to minimize the chance of future + // breaking changes in case we adapt the approach later on. This also + // avoids any hangs for exponentially growing proof trees. + InspectConfig { max_depth: 5 } + } + + fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'db>) { + // No need to walk into goal subtrees that certainly hold, since they + // wouldn't then be stalled on an infer var. + if inspect_goal.result() == Ok(Certainty::Yes) { + return; + } + + let db = self.ctx.interner; + let goal = inspect_goal.goal(); + if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) + // We do not push the instantiated forms of goals as it would cause any + // aliases referencing bound vars to go from having escaping bound vars to + // being able to be normalized to an inference variable. + // + // This is mostly just a hack as arbitrary nested goals could still contain + // such aliases while having a different `GoalSource`. Closure signature inference + // however can't really handle *every* higher ranked `Fn` goal also being present + // in the form of `?c: Fn<(>::Assoc)`. + // + // This also just better matches the behaviour of the old solver where we do not + // encounter instantiated forms of goals, only nested goals that referred to bound + // vars from instantiated goals. + && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) + { + self.obligations_for_self_ty.push(Obligation::new( + db, + self.root_cause.clone(), + goal.param_env, + goal.predicate, + )); + } + + // If there's a unique way to prove a given goal, recurse into + // that candidate. This means that for `impl Trait for () {}` + // and a `(): Trait` goal we recurse into the impl and look at + // the nested `?0: FnOnce(u32)` goal. + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } } } @@ -119,7 +146,7 @@ pub fn could_unify_deeply( let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); table.propagate_diverging_flag(); let ty1_with_vars = table.resolve_completely(ty1_with_vars); let ty2_with_vars = table.resolve_completely(ty2_with_vars); @@ -186,35 +213,117 @@ bitflags::bitflags! { } #[derive(Clone)] -pub(crate) struct InferenceTable<'a> { - pub(crate) db: &'a dyn HirDatabase, - pub(crate) interner: DbInterner<'a>, +pub(crate) struct InferenceTable<'db> { + pub(crate) db: &'db dyn HirDatabase, + pub(crate) interner: DbInterner<'db>, pub(crate) trait_env: Arc, + pub(crate) param_env: ParamEnv<'db>, pub(crate) tait_coercion_table: Option>, - pub(crate) infer_ctxt: InferCtxt<'a>, + pub(crate) infer_ctxt: InferCtxt<'db>, diverging_tys: FxHashSet, - pending_obligations: Vec>>, + pub(super) fulfillment_cx: FulfillmentCtxt<'db>, } -pub(crate) struct InferenceTableSnapshot<'a> { +pub(crate) struct InferenceTableSnapshot<'db> { ctxt_snapshot: CombinedSnapshot, + obligations: FulfillmentCtxt<'db>, diverging_tys: FxHashSet, - pending_obligations: Vec>>, } -impl<'a> InferenceTable<'a> { - pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc) -> Self { +impl<'db> InferenceTable<'db> { + pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc) -> Self { let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { + defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), + }); InferenceTable { db, interner, + param_env: trait_env.env.to_nextsolver(interner), trait_env, tait_coercion_table: None, - infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { - defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), - }), + fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), + infer_ctxt, diverging_tys: FxHashSet::default(), - pending_obligations: Vec::new(), + } + } + + pub(crate) fn type_var_is_sized(&self, self_ty: TyVid) -> bool { + let Some(sized_did) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else { + return true; + }; + self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { + match obligation.predicate.kind().skip_binder() { + crate::next_solver::PredicateKind::Clause( + crate::next_solver::ClauseKind::Trait(data), + ) => data.def_id().0 == sized_did, + _ => false, + } + }) + } + + pub(super) fn obligations_for_self_ty( + &self, + self_ty: TyVid, + ) -> SmallVec<[Obligation<'db, Predicate<'db>>; 4]> { + let obligations = self.fulfillment_cx.pending_obligations(); + let mut obligations_for_self_ty = SmallVec::new(); + for obligation in obligations { + let mut visitor = NestedObligationsForSelfTy { + ctx: self, + self_ty, + obligations_for_self_ty: &mut obligations_for_self_ty, + root_cause: &obligation.cause, + }; + + let goal = obligation.as_goal(); + self.infer_ctxt.visit_proof_tree(goal, &mut visitor); + } + + obligations_for_self_ty.retain_mut(|obligation| { + obligation.predicate = self.infer_ctxt.resolve_vars_if_possible(obligation.predicate); + !obligation.predicate.has_placeholders() + }); + obligations_for_self_ty + } + + fn predicate_has_self_ty(&self, predicate: Predicate<'db>, expected_vid: TyVid) -> bool { + match predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(data)) => { + self.type_matches_expected_vid(expected_vid, data.self_ty()) + } + PredicateKind::Clause(ClauseKind::Projection(data)) => { + self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) + | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) + | PredicateKind::Clause(ClauseKind::WellFormed(..)) + | PredicateKind::DynCompatible(..) + | PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) + | PredicateKind::ConstEquate(..) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::Ambiguous => false, + } + } + + fn type_matches_expected_vid( + &self, + expected_vid: TyVid, + ty: crate::next_solver::Ty<'db>, + ) -> bool { + let ty = self.shallow_resolve(ty); + + match ty.kind() { + crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { + self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) + } + _ => false, } } @@ -288,13 +397,13 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> + pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables - self.resolve_obligations_as_possible(); + self.select_obligations_where_possible(); self.infer_ctxt.canonicalize_response(t) } @@ -306,19 +415,24 @@ impl<'a> InferenceTable<'a> { /// to do it as well. pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner)) .to_chalk(self.interner) } + // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing. + // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed. pub(crate) fn normalize_associated_types_in_ns(&mut self, ty: T) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable> + Clone, { let ty = self.resolve_vars_with_obligations(ty); - ty.fold_with(&mut Normalizer { table: self }) + self.infer_ctxt + .at(&ObligationCause::new(), self.param_env) + .deeply_normalize(ty.clone()) + .unwrap_or(ty) } /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow @@ -388,8 +502,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn normalize_alias_ty( &mut self, - alias: crate::next_solver::Ty<'a>, - ) -> crate::next_solver::Ty<'a> { + alias: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { let infer_term = self.infer_ctxt.next_ty_var(); let obligation = crate::next_solver::Predicate::new( self.interner, @@ -430,6 +544,10 @@ impl<'a> InferenceTable<'a> { self.new_var(TyVariableKind::General, false) } + pub(crate) fn next_ty_var(&mut self) -> crate::next_solver::Ty<'db> { + self.infer_ctxt.next_ty_var() + } + pub(crate) fn new_integer_var(&mut self) -> Ty { self.new_var(TyVariableKind::Integer, false) } @@ -454,6 +572,10 @@ impl<'a> InferenceTable<'a> { var.to_lifetime(Interner) } + pub(crate) fn next_region_var(&mut self) -> crate::next_solver::Region<'db> { + self.infer_ctxt.next_region_var() + } + pub(crate) fn resolve_with_fallback( &mut self, t: T, @@ -488,10 +610,10 @@ impl<'a> InferenceTable<'a> { pub(crate) fn instantiate_canonical_ns( &mut self, - canonical: rustc_type_ir::Canonical, T>, + canonical: rustc_type_ir::Canonical, T>, ) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { self.infer_ctxt.instantiate_canonical(&canonical).0 } @@ -513,8 +635,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_completely(&mut self, t: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); @@ -566,7 +688,7 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify, U: Relate>>( + pub(crate) fn unify, U: Relate>>( &mut self, ty1: &T, ty2: &T, @@ -575,12 +697,20 @@ impl<'a> InferenceTable<'a> { Ok(r) => r, Err(_) => return false, }; - self.register_infer_ok(result); + self.register_obligations(result.goals); + true + } + + pub(crate) fn unify_ns>>(&mut self, lhs: T, rhs: T) -> bool { + let Ok(infer_ok) = self.try_unify_ns(lhs, rhs) else { + return false; + }; + self.register_obligations(infer_ok.goals); true } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply, U: Relate>>( + pub(crate) fn unify_deeply, U: Relate>>( &mut self, ty1: &T, ty2: &T, @@ -596,18 +726,27 @@ impl<'a> InferenceTable<'a> { /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify, U: Relate>>( + pub(crate) fn try_unify, U: Relate>>( &mut self, t1: &T, t2: &T, - ) -> InferResult<'a, ()> { - let param_env = self.trait_env.env.to_nextsolver(self.interner); + ) -> InferResult<'db, ()> { let lhs = t1.to_nextsolver(self.interner); let rhs = t2.to_nextsolver(self.interner); + self.try_unify_ns(lhs, rhs) + } + + /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the + /// caller needs to deal with them. + pub(crate) fn try_unify_ns>>( + &mut self, + lhs: T, + rhs: T, + ) -> InferResult<'db, ()> { let variance = rustc_type_ir::Variance::Invariant; let span = crate::next_solver::Span::dummy(); - match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { - Ok(goals) => Ok(InferOk { goals, value: () }), + match self.infer_ctxt.relate(self.param_env, lhs, variance, rhs, span) { + Ok(goals) => Ok(crate::infer::InferOk { goals, value: () }), Err(_) => Err(TypeError), } } @@ -616,17 +755,19 @@ impl<'a> InferenceTable<'a> { /// otherwise, return ty. #[tracing::instrument(skip(self))] pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { - if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { - return ty.clone(); - } - self.infer_ctxt - .resolve_vars_if_possible(ty.to_nextsolver(self.interner)) - .to_chalk(self.interner) + self.shallow_resolve(ty.to_nextsolver(self.interner)).to_chalk(self.interner) + } + + pub(crate) fn shallow_resolve( + &self, + ty: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { + self.infer_ctxt.shallow_resolve(ty) } pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { use rustc_type_ir::TypeVisitableExt; @@ -640,7 +781,7 @@ impl<'a> InferenceTable<'a> { return t; } - self.resolve_obligations_as_possible(); + self.select_obligations_where_possible(); self.infer_ctxt.resolve_vars_if_possible(t) } @@ -659,42 +800,58 @@ impl<'a> InferenceTable<'a> { .to_chalk(self.interner) } - fn structurally_normalize_term(&mut self, term: Term<'a>) -> Term<'a> { - if term.to_alias_term().is_none() { - return term; - } - - let new_infer = self.infer_ctxt.next_term_var_of_kind(term); + fn structurally_normalize_term(&mut self, term: Term<'db>) -> Term<'db> { + self.infer_ctxt + .at(&ObligationCause::new(), self.param_env) + .structurally_normalize_term(term, &mut self.fulfillment_cx) + .unwrap_or(term) + } - self.register_obligation(Predicate::new( - self.interner, - Binder::dummy(PredicateKind::AliasRelate( - term, - new_infer, - AliasRelationDirection::Equate, - )), - )); - self.resolve_obligations_as_possible(); - let res = self.infer_ctxt.resolve_vars_if_possible(new_infer); - if res == new_infer { term } else { res } + /// Try to resolve `ty` to a structural type, normalizing aliases. + /// + /// In case there is still ambiguity, the returned type may be an inference + /// variable. This is different from `structurally_resolve_type` which errors + /// in this case. + pub(crate) fn try_structurally_resolve_type( + &mut self, + ty: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { + if let crate::next_solver::TyKind::Alias(..) = ty.kind() { + // We need to use a separate variable here as otherwise the temporary for + // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting + // in a reentrant borrow, causing an ICE. + let result = self + .infer_ctxt + .at(&ObligationCause::misc(), self.param_env) + .structurally_normalize_ty(ty, &mut self.fulfillment_cx); + match result { + Ok(normalized_ty) => normalized_ty, + Err(_errors) => crate::next_solver::Ty::new_error(self.interner, ErrorGuaranteed), + } + } else { + self.resolve_vars_with_obligations(ty) + } } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'a> { + pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); let diverging_tys = self.diverging_tys.clone(); - let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { ctxt_snapshot, pending_obligations, diverging_tys } + let obligations = self.fulfillment_cx.clone(); + InferenceTableSnapshot { ctxt_snapshot, diverging_tys, obligations } } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'a>) { + pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); self.diverging_tys = snapshot.diverging_tys; - self.pending_obligations = snapshot.pending_obligations; + self.fulfillment_cx = snapshot.obligations; } #[tracing::instrument(skip_all)] - pub(crate) fn run_in_snapshot(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> T) -> T { + pub(crate) fn run_in_snapshot( + &mut self, + f: impl FnOnce(&mut InferenceTable<'db>) -> T, + ) -> T { let snapshot = self.snapshot(); let result = f(self); self.rollback_to(snapshot); @@ -703,7 +860,7 @@ impl<'a> InferenceTable<'a> { pub(crate) fn commit_if_ok( &mut self, - f: impl FnOnce(&mut InferenceTable<'_>) -> Result, + f: impl FnOnce(&mut InferenceTable<'db>) -> Result, ) -> Result { let snapshot = self.snapshot(); let result = f(self); @@ -735,7 +892,7 @@ impl<'a> InferenceTable<'a> { result.map(|m| m.1) } - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'a>) { + pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { let goal = next_solver::Goal { param_env: self.trait_env.env.to_nextsolver(self.interner), predicate, @@ -746,7 +903,7 @@ impl<'a> InferenceTable<'a> { #[tracing::instrument(level = "debug", skip(self))] fn register_obligation_in_env( &mut self, - goal: next_solver::Goal<'a, next_solver::Predicate<'a>>, + goal: next_solver::Goal<'db, next_solver::Predicate<'db>>, ) { let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); tracing::debug!(?result); @@ -754,119 +911,68 @@ impl<'a> InferenceTable<'a> { Ok((_, Certainty::Yes)) => {} Err(rustc_type_ir::solve::NoSolution) => {} Ok((_, Certainty::Maybe(_))) => { - self.pending_obligations.push(goal); + self.fulfillment_cx.register_predicate_obligation( + &self.infer_ctxt, + Obligation::new( + self.interner, + ObligationCause::new(), + goal.param_env, + goal.predicate, + ), + ); } } } - pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'a, T>) { - infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); + pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'db, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.register_predicates(obligations); + value } - pub(crate) fn resolve_obligations_as_possible(&mut self) { - let _span = tracing::info_span!("resolve_obligations_as_possible").entered(); - let mut changed = true; - while mem::take(&mut changed) { - let mut obligations = mem::take(&mut self.pending_obligations); - - for goal in obligations.drain(..) { - tracing::debug!(obligation = ?goal); - - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - let (has_changed, certainty) = match result { - Ok(result) => result, - Err(_) => { - continue; - } - }; - - if matches!(has_changed, HasChanged::Yes) { - changed = true; - } + pub(crate) fn register_obligations( + &mut self, + obligations: Vec>>, + ) { + obligations.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); + } - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => self.pending_obligations.push(goal), - } - } - } + pub(crate) fn select_obligations_where_possible(&mut self) { + self.fulfillment_cx.select_where_possible(&self.infer_ctxt); } - pub(crate) fn fudge_inference>( + pub(super) fn register_predicate( &mut self, - f: impl FnOnce(&mut Self) -> T, - ) -> T { - use chalk_ir::fold::TypeFolder; - - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - struct VarFudger<'a, 'b> { - table: &'a mut InferenceTable<'b>, - highest_known_var: InferenceVar, + obligation: crate::next_solver::infer::traits::PredicateObligation<'db>, + ) { + if obligation.has_escaping_bound_vars() { + panic!("escaping bound vars in predicate {:?}", obligation); } - impl TypeFolder for VarFudger<'_, '_> { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn fold_inference_ty( - &mut self, - var: chalk_ir::InferenceVar, - kind: TyVariableKind, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Ty { - if var < self.highest_known_var { - var.to_ty(Interner, kind) - } else { - self.table.new_type_var() - } - } - - fn fold_inference_lifetime( - &mut self, - var: chalk_ir::InferenceVar, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Lifetime { - if var < self.highest_known_var { - var.to_lifetime(Interner) - } else { - self.table.new_lifetime_var() - } - } - fn fold_inference_const( - &mut self, - ty: chalk_ir::Ty, - var: chalk_ir::InferenceVar, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Const { - if var < self.highest_known_var { - var.to_const(Interner, ty) - } else { - self.table.new_const_var(ty) - } - } - } + self.fulfillment_cx.register_predicate_obligation(&self.infer_ctxt, obligation); + } - let snapshot = self.snapshot(); - let highest_known_var = self.new_type_var().inference_var(Interner).expect("inference_var"); - let result = f(self); - self.rollback_to(snapshot); - result - .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) + pub(super) fn register_predicates(&mut self, obligations: I) + where + I: IntoIterator>, + { + obligations.into_iter().for_each(|obligation| { + self.register_predicate(obligation); + }); } pub(crate) fn callable_sig( &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option, Vec, Ty)> { + ) -> Option<(Option, Vec>, crate::next_solver::Ty<'db>)> + { match ty.callable_sig(self.db) { - Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), + Some(sig) => Some(( + None, + sig.params().iter().map(|param| param.to_nextsolver(self.interner)).collect(), + sig.ret().to_nextsolver(self.interner), + )), None => { let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?; Some((Some(f), args_ty, return_ty)) @@ -878,7 +984,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(FnTrait, Vec, Ty)> { + ) -> Option<(FnTrait, Vec>, crate::next_solver::Ty<'db>)> { for (fn_trait_name, output_assoc_name, subtraits) in [ (FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]), (FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]), @@ -898,7 +1004,7 @@ impl<'a> InferenceTable<'a> { ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; - arg_tys.push(arg.clone()); + arg_tys.push(arg.to_nextsolver(self.interner)); arg.cast(Interner) }) .build(); @@ -920,7 +1026,8 @@ impl<'a> InferenceTable<'a> { let goal: Goal = trait_ref.clone().cast(Interner); if !self.try_obligation(goal.clone()).no_solution() { self.register_obligation(goal.to_nextsolver(self.interner)); - let return_ty = self.normalize_projection_ty(projection); + let return_ty = + self.normalize_projection_ty(projection).to_nextsolver(self.interner); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); @@ -969,8 +1076,8 @@ impl<'a> InferenceTable<'a> { /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.process_remote_user_written_ty(ty) // FIXME: Register a well-formed obligation. @@ -980,13 +1087,14 @@ impl<'a> InferenceTable<'a> { /// while `process_user_written_ty()` should (but doesn't currently). pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { let ty = self.insert_type_vars(ty); // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. + // FIXME(next-solver): We should not deeply normalize here, only shallowly. self.normalize_associated_types_in(ty) } @@ -1074,7 +1182,10 @@ impl<'a> InferenceTable<'a> { impl fmt::Debug for InferenceTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InferenceTable").finish() + f.debug_struct("InferenceTable") + .field("name", &self.infer_ctxt.inner.borrow().type_variable_storage) + .field("fulfillment_cx", &self.fulfillment_cx) + .finish() } } @@ -1091,7 +1202,7 @@ mod resolve { }; use rustc_type_ir::{FloatVid, IntVid, TyVid}; - #[derive(Copy, Clone, PartialEq, Eq)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(super) enum VarKind { Ty(TyVariableKind), Const, @@ -1264,62 +1375,3 @@ mod resolve { } } } - -/// This expects its input to be resolved. -struct Normalizer<'a, 'b> { - table: &'a mut InferenceTable<'b>, -} - -impl<'db> Normalizer<'_, 'db> { - fn normalize_alias_term( - &mut self, - alias_term: crate::next_solver::Term<'db>, - ) -> crate::next_solver::Term<'db> { - let infer_term = self.table.infer_ctxt.next_term_var_of_kind(alias_term); - let obligation = crate::next_solver::Predicate::new( - self.table.interner, - crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( - alias_term, - infer_term, - rustc_type_ir::AliasRelationDirection::Equate, - )), - ); - self.table.register_obligation(obligation); - let term = self.table.resolve_vars_with_obligations(infer_term); - // Now normalize the result, because maybe it contains more aliases. - match term { - Term::Ty(term) => term.super_fold_with(self).into(), - Term::Const(term) => term.super_fold_with(self).into(), - } - } -} - -impl<'db> rustc_type_ir::TypeFolder> for Normalizer<'_, 'db> { - fn cx(&self) -> DbInterner<'db> { - self.table.interner - } - - fn fold_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { - if !ty.has_aliases() { - return ty; - } - - let crate::next_solver::TyKind::Alias(..) = ty.kind() else { - return ty.super_fold_with(self); - }; - // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). - self.normalize_alias_term(ty.into()).expect_type() - } - - fn fold_const(&mut self, ct: crate::next_solver::Const<'db>) -> crate::next_solver::Const<'db> { - if !ct.has_aliases() { - return ct; - } - - let crate::next_solver::ConstKind::Unevaluated(..) = ct.kind() else { - return ct.super_fold_with(self); - }; - // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). - self.normalize_alias_term(ct.into()).expect_const() - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 2020a8b34b4f9..f21673c732e40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -28,7 +28,6 @@ use crate::{ DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, mapping::{ChalkToNextSolver, convert_args_for_result}, - project::solve_normalize::deeply_normalize, }, }; @@ -172,7 +171,7 @@ pub fn layout_of_ty_query<'db>( let cx = LayoutCx::new(dl); let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); - let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); + let ty = infer_ctxt.at(&cause, ParamEnv::empty()).deeply_normalize(ty).unwrap_or(ty); let result = match ty.kind() { TyKind::Adt(def, args) => { match def.inner().id { @@ -335,8 +334,8 @@ pub fn layout_of_ty_query<'db>( .clone() .substitute( Interner, - ClosureSubst(&convert_args_for_result(interner, args.inner())) - .parent_subst(), + &ClosureSubst(&convert_args_for_result(interner, args.inner())) + .parent_subst(db), ) .to_nextsolver(interner); db.layout_of_ty(ty, trait_env.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 02fdbf0ebf0df..3d3cbeaf4b2d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -115,7 +115,7 @@ pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, - closure::{CaptureKind, CapturedItem}, + closure::analysis::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, }; pub use interner::Interner; @@ -133,8 +133,8 @@ pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; pub use traits::TraitEnvironment; pub use utils::{ - Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call, - target_feature_is_safe_in_target, + TargetFeatureIsSafeInTarget, Unsafety, all_super_traits, direct_super_traits, + is_fn_unsafe_to_call, target_feature_is_safe_in_target, }; pub use variance::Variance; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 5c29befe123cf..2292e5c99413e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -17,6 +17,7 @@ use std::{ use base_db::Crate; use either::Either; +use hir_def::item_tree::FieldsShape; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId, @@ -34,6 +35,7 @@ use hir_def::{ TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; +use hir_def::{ConstId, StaticId}; use hir_expand::name::Name; use intern::sym; use la_arena::{Arena, ArenaMap, Idx}; @@ -53,6 +55,7 @@ use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; +use crate::ValueTyDefId; use crate::{ FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, @@ -206,6 +209,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } + pub(crate) fn set_lifetime_elision(&mut self, lifetime_elision: LifetimeElisionKind<'db>) { + self.lifetime_elision = lifetime_elision; + } + pub(crate) fn with_debruijn( &mut self, debruijn: DebruijnIndex, @@ -958,6 +965,105 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind } } +/// Build the declared type of a function. This should not need to look at the +/// function body. +fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::FunctionId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + )) +} + +/// Build the declared type of a const. +fn type_for_const<'db>(db: &'db dyn HirDatabase, def: ConstId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let data = db.const_signature(def); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the declared type of a static. +fn type_for_static<'db>(db: &'db dyn HirDatabase, def: StaticId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let module = resolver.module(); + let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block()); + let data = db.static_signature(def); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the type of a tuple struct constructor. +fn type_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::StructId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + ))) + } + } +} + +/// Build the type of a tuple enum variant constructor. +fn type_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::EnumVariantId(def).into(), + GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), + ))) + } + } +} + +pub(crate) fn value_ty_query<'db>( + db: &'db dyn HirDatabase, + def: ValueTyDefId, +) -> Option>> { + match def { + ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), + ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), + ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), + ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), + ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), + ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), + } +} + pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( db: &'db dyn HirDatabase, t: TypeAliasId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index ac85bf7950723..a4427517a10b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -24,7 +24,7 @@ use triomphe::Arc; use crate::{ AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, @@ -622,18 +622,23 @@ pub struct ReceiverAdjustments { } impl ReceiverAdjustments { - pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = table.structurally_resolve_type(&ty); + pub(crate) fn apply( + &self, + table: &mut InferenceTable<'_>, + mut ty: Ty, + ) -> (Ty, Vec) { let mut adjust = Vec::new(); + let mut autoderef = table.autoderef(ty.to_nextsolver(table.interner)); + autoderef.next(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true, false) { + match autoderef.next() { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); break; } - Some((kind, new_ty)) => { - ty = new_ty.clone(); + Some((new_ty, _)) => { + ty = new_ty.to_chalk(autoderef.table.interner); let mutbl = match self.autoref { Some(AutorefOrPtrAdjustment::Autoref(m)) => Some(m), Some(AutorefOrPtrAdjustment::ToConstPtr) => Some(Mutability::Not), @@ -641,11 +646,11 @@ impl ReceiverAdjustments { None => None, }; adjust.push(Adjustment { - kind: Adjust::Deref(match kind { + kind: Adjust::Deref(match autoderef.steps().last().unwrap().1 { AutoderefKind::Overloaded => Some(OverloadedDeref(mutbl)), AutoderefKind::Builtin => None, }), - target: new_ty, + target: ty.clone(), }); } } @@ -1282,17 +1287,20 @@ fn iterate_method_candidates_by_receiver<'db>( name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { + let interner = table.interner; let receiver_ty = table.instantiate_canonical_ns(receiver_ty); - let receiver_ty: crate::Ty = receiver_ty.to_chalk(table.interner); + let receiver_ty: crate::Ty = receiver_ty.to_chalk(interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); + autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( - &self_ty, + &self_ty.to_chalk(interner), autoderef.table, name, Some(&receiver_ty), @@ -1308,15 +1316,18 @@ fn iterate_method_candidates_by_receiver<'db>( })?; table.run_in_snapshot(|table| { let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); + autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { - if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { + if matches!(self_ty.kind(), crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(_))) + { // don't try to resolve methods on unknown types return ControlFlow::Continue(()); } iterate_trait_method_candidates( - &self_ty, + &self_ty.to_chalk(interner), autoderef.table, traits_in_scope, name, @@ -1760,7 +1771,8 @@ fn is_valid_trait_method_candidate( for pred in infer_ok.into_obligations() { ctxt.register_predicate_obligation(&table.infer_ctxt, pred); } - check_that!(ctxt.select_all_or_error(&table.infer_ctxt).is_empty()); + // FIXME: Are we doing this correctly? Probably better to follow rustc more closely. + check_that!(ctxt.select_where_possible(&table.infer_ctxt).is_empty()); } check_that!(table.unify(receiver_ty, &expected_receiver)); @@ -1937,11 +1949,10 @@ fn autoderef_method_receiver<'db>( ) -> Vec<(next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { let interner = table.interner; let mut deref_chain = Vec::new(); - let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, ty.to_chalk(interner), false, true); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty).use_receiver_trait(); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty.to_nextsolver(interner)), + autoderef.table.canonicalize(ty), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 52df851c30d13..2c09fb9a89e78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -113,8 +113,13 @@ fn make_fetch_closure_field( let InternedClosure(def, _) = db.lookup_intern_closure(c.into()); let infer = db.infer(def); let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); - captures.get(f).expect("broken closure field").ty.clone().substitute(Interner, parent_subst) + let parent_subst = ClosureSubst(subst).parent_subst(db); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, &parent_subst) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index cddd1fb7be526..6f950b8022c98 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -712,13 +712,13 @@ impl<'db> Evaluator<'db> { let InternedClosure(def, _) = self.db.lookup_intern_closure(c.into()); let infer = self.db.infer(def); let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); + let parent_subst = ClosureSubst(subst).parent_subst(self.db); captures .get(f) .expect("broken closure field") .ty .clone() - .substitute(Interner, parent_subst) + .substitute(Interner, &parent_subst) }, self.crate_id, ); @@ -2772,7 +2772,7 @@ impl<'db> Evaluator<'db> { TyKind::Closure(closure, subst) => self.exec_closure( *closure, func_data, - &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), + &ClosureSubst(subst).parent_subst(self.db), destination, &args[1..], locals, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index e27d334d2a991..f67778b0f12f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -4,6 +4,7 @@ use std::cmp::{self, Ordering}; use chalk_ir::TyKind; +use hir_def::signatures::FunctionSignature; use hir_def::{ CrateRootModuleId, builtin_type::{BuiltinInt, BuiltinUint}, @@ -63,17 +64,7 @@ impl Evaluator<'_> { let function_data = self.db.function_signature(def); let attrs = self.db.attrs(def.into()); - let is_intrinsic = attrs.by_key(sym::rustc_intrinsic).exists() - // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used - || (match &function_data.abi { - Some(abi) => *abi == sym::rust_dash_intrinsic, - None => match def.lookup(self.db).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - block.abi(self.db) == Some(sym::rust_dash_intrinsic) - } - _ => false, - }, - }); + let is_intrinsic = FunctionSignature::is_intrinsic(self.db, def); if is_intrinsic { return self.exec_intrinsic( @@ -194,7 +185,7 @@ impl Evaluator<'_> { let infer = self.db.infer(closure_owner); let (captures, _) = infer.closure_info(id); let layout = self.layout(self_ty.to_nextsolver(interner))?; - let ty_iter = captures.iter().map(|c| c.ty(subst)); + let ty_iter = captures.iter().map(|c| c.ty(self.db, subst)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } TyKind::Tuple(_, subst) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 5a56d99fbaa29..2a6e3a147a7d7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -12,34 +12,37 @@ use crate::{ use super::{MirEvalError, interpret_mir}; fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError> { - let module_id = db.module_for_file(file_id.file_id(db)); - let def_map = module_id.def_map(db); - let scope = &def_map[module_id.local_id].scope; - let func_id = scope - .declarations() - .find_map(|x| match x { - hir_def::ModuleDefId::FunctionId(x) => { - if db.function_signature(x).name.display(db, Edition::CURRENT).to_string() == "main" - { - Some(x) - } else { - None + salsa::attach(db, || { + let module_id = db.module_for_file(file_id.file_id(db)); + let def_map = module_id.def_map(db); + let scope = &def_map[module_id.local_id].scope; + let func_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + if db.function_signature(x).name.display(db, Edition::CURRENT).to_string() + == "main" + { + Some(x) + } else { + None + } } - } - _ => None, - }) - .expect("no main function found"); - let body = db - .monomorphized_mir_body( - func_id.into(), - Substitution::empty(Interner), - db.trait_environment(func_id.into()), - ) - .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; - - let (result, output) = salsa::attach(db, || interpret_mir(db, body, false, None))?; - result?; - Ok((output.stdout().into_owned(), output.stderr().into_owned())) + _ => None, + }) + .expect("no main function found"); + let body = db + .monomorphized_mir_body( + func_id.into(), + Substitution::empty(Interner), + db.trait_environment(func_id.into()), + ) + .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; + + let (result, output) = interpret_mir(db, body, false, None)?; + result?; + Ok((output.stdout().into_owned(), output.stderr().into_owned())) + }) } fn check_pass(#[rust_analyzer::rust_fixture] ra_fixture: &str) { @@ -53,43 +56,60 @@ fn check_pass_and_stdio( ) { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let x = eval_main(&db, file_id); - match x { - Err(e) => { - let mut err = String::new(); - let line_index = |size: TextSize| { - let mut size = u32::from(size) as usize; - let lines = ra_fixture.lines().enumerate(); - for (i, l) in lines { - if let Some(x) = size.checked_sub(l.len()) { - size = x; - } else { - return (i, size); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let x = eval_main(&db, file_id); + match x { + Err(e) => { + let mut err = String::new(); + let line_index = |size: TextSize| { + let mut size = u32::from(size) as usize; + let lines = ra_fixture.lines().enumerate(); + for (i, l) in lines { + if let Some(x) = size.checked_sub(l.len()) { + size = x; + } else { + return (i, size); + } } - } - (usize::MAX, size) - }; - let span_formatter = |file, range: TextRange| { - format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end())) - }; - let krate = db.module_for_file(file_id.file_id(&db)).krate(); - e.pretty_print(&mut err, &db, span_formatter, DisplayTarget::from_crate(&db, krate)) + (usize::MAX, size) + }; + let span_formatter = |file, range: TextRange| { + format!( + "{:?} {:?}..{:?}", + file, + line_index(range.start()), + line_index(range.end()) + ) + }; + let krate = db.module_for_file(file_id.file_id(&db)).krate(); + e.pretty_print( + &mut err, + &db, + span_formatter, + DisplayTarget::from_crate(&db, krate), + ) .unwrap(); - panic!("Error in interpreting: {err}"); - } - Ok((stdout, stderr)) => { - assert_eq!(stdout, expected_stdout); - assert_eq!(stderr, expected_stderr); + panic!("Error in interpreting: {err}"); + } + Ok((stdout, stderr)) => { + assert_eq!(stdout, expected_stdout); + assert_eq!(stderr, expected_stderr); + } } - } + }) } fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let e = eval_main(&db, file_id).unwrap_err(); - assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), expected_panic); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert_eq!( + e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), + expected_panic + ); + }) } fn check_error_with( @@ -97,9 +117,11 @@ fn check_error_with( expect_err: impl FnOnce(MirEvalError) -> bool, ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let e = eval_main(&db, file_id).unwrap_err(); - assert!(expect_err(e)); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert!(expect_err(e)); + }) } #[test] @@ -492,7 +514,7 @@ fn main() { fn from_fn() { check_pass( r#" -//- minicore: fn, iterator +//- minicore: fn, iterator, sized struct FromFn(F); impl Option> Iterator for FromFn { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 45a1131e33399..50e416a66a64b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2065,7 +2065,7 @@ pub fn mir_body_for_closure_query( }, }); ctx.result.param_locals.push(closure_local); - let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { + let Some(sig) = ClosureSubst(substs).sig_ty(db).callable_sig(db) else { implementation_error!("closure has not callable sig"); }; let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 0225deebe4f31..34d8b649c981b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -9,14 +9,17 @@ pub mod fulfill; mod generic_arg; pub mod generics; pub mod infer; +pub(crate) mod inspect; pub mod interner; mod ir_print; pub mod mapping; +mod normalize; +pub mod obligation_ctxt; mod opaques; pub mod predicate; -pub(crate) mod project; mod region; mod solver; +mod structural_normalize; mod ty; pub mod util; @@ -37,8 +40,11 @@ pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues = rustc_type_ir::CanonicalVarKind>; pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput, V>; pub type AliasTy<'db> = rustc_type_ir::AliasTy>; +pub type FnSig<'db> = rustc_type_ir::FnSig>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; +pub type TypeError<'db> = rustc_type_ir::error::TypeError>; +pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult>; pub type FxIndexMap = indexmap::IndexMap>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 23789b06e828b..0b3582051bc07 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -4,16 +4,19 @@ use std::hash::Hash; use hir_def::{ConstParamId, TypeOrConstParamId}; use intern::{Interned, Symbol}; -use rustc_ast_ir::try_visit; -use rustc_ast_ir::visit::VisitorResult; +use rustc_ast_ir::{try_visit, visit::VisitorResult}; use rustc_type_ir::{ BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, WithCachedTypeInfo, - inherent::{IntoKind, PlaceholderLike}, + TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, + inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike}, relate::Relate, }; -use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug}; +use crate::{ + ConstScalar, MemoryMap, + interner::InternedWrapperNoDebug, + next_solver::{ClauseKind, ParamEnv}, +}; use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; @@ -96,6 +99,40 @@ impl std::fmt::Debug for ParamConst { } } +impl ParamConst { + pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option>` instead of `Ty<'db>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} + /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index a9c572d3f34ee..1ae59beca2728 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -17,7 +17,7 @@ pub enum Ctor { Enum(EnumVariantId), } -#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] pub enum SolverDefId { AdtId(AdtId), ConstId(ConstId), @@ -32,6 +32,64 @@ pub enum SolverDefId { Ctor(Ctor), } +impl std::fmt::Debug for SolverDefId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let interner = DbInterner::conjure(); + let db = interner.db; + match *self { + SolverDefId::AdtId(AdtId::StructId(id)) => { + f.debug_tuple("AdtId").field(&db.struct_signature(id).name.as_str()).finish() + } + SolverDefId::AdtId(AdtId::EnumId(id)) => { + f.debug_tuple("AdtId").field(&db.enum_signature(id).name.as_str()).finish() + } + SolverDefId::AdtId(AdtId::UnionId(id)) => { + f.debug_tuple("AdtId").field(&db.union_signature(id).name.as_str()).finish() + } + SolverDefId::ConstId(id) => f + .debug_tuple("ConstId") + .field(&db.const_signature(id).name.as_ref().map_or("_", |name| name.as_str())) + .finish(), + SolverDefId::FunctionId(id) => { + f.debug_tuple("FunctionId").field(&db.function_signature(id).name.as_str()).finish() + } + SolverDefId::ImplId(id) => f.debug_tuple("ImplId").field(&id).finish(), + SolverDefId::StaticId(id) => { + f.debug_tuple("StaticId").field(&db.static_signature(id).name.as_str()).finish() + } + SolverDefId::TraitId(id) => { + f.debug_tuple("TraitId").field(&db.trait_signature(id).name.as_str()).finish() + } + SolverDefId::TypeAliasId(id) => f + .debug_tuple("TypeAliasId") + .field(&db.type_alias_signature(id).name.as_str()) + .finish(), + SolverDefId::InternedClosureId(id) => { + f.debug_tuple("InternedClosureId").field(&id).finish() + } + SolverDefId::InternedCoroutineId(id) => { + f.debug_tuple("InternedCoroutineId").field(&id).finish() + } + SolverDefId::InternedOpaqueTyId(id) => { + f.debug_tuple("InternedOpaqueTyId").field(&id).finish() + } + SolverDefId::Ctor(Ctor::Struct(id)) => { + f.debug_tuple("Ctor").field(&db.struct_signature(id).name.as_str()).finish() + } + SolverDefId::Ctor(Ctor::Enum(id)) => { + let parent_enum = id.loc(db).parent; + f.debug_tuple("Ctor") + .field(&format_args!( + "\"{}::{}\"", + db.enum_signature(parent_enum).name.as_str(), + parent_enum.enum_variants(db).variant_name_by_id(id).unwrap().as_str() + )) + .finish() + } + } + } +} + impl_from!( AdtId(StructId, EnumId, UnionId), ConstId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 4258f4c7ac68b..a8183ab422792 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -1,21 +1,28 @@ //! Fulfill loop for next-solver. -use std::marker::PhantomData; -use std::mem; -use std::ops::ControlFlow; -use std::vec::ExtractIf; - -use rustc_next_trait_solver::delegate::SolverDelegate; -use rustc_next_trait_solver::solve::{ - GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt, +mod errors; + +use std::{marker::PhantomData, mem, ops::ControlFlow, vec::ExtractIf}; + +use rustc_hash::FxHashSet; +use rustc_next_trait_solver::{ + delegate::SolverDelegate, + solve::{GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt}, +}; +use rustc_type_ir::{ + Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + inherent::{IntoKind, Span as _}, + solve::{Certainty, NoSolution}, }; -use rustc_type_ir::Interner; -use rustc_type_ir::inherent::Span as _; -use rustc_type_ir::solve::{Certainty, NoSolution}; -use crate::next_solver::infer::InferCtxt; -use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations}; -use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode}; +use crate::next_solver::{ + DbInterner, SolverContext, SolverDefId, Span, Ty, TyKind, TypingMode, + infer::{ + InferCtxt, + traits::{PredicateObligation, PredicateObligations}, + }, + inspect::ProofTreeVisitor, +}; type PendingObligations<'db> = Vec<(PredicateObligation<'db>, Option>>)>; @@ -31,6 +38,7 @@ type PendingObligations<'db> = /// /// It is also likely that we want to use slightly different datastructures /// here as this will have to deal with far more root goals than `evaluate_all`. +#[derive(Debug, Clone)] pub struct FulfillmentCtxt<'db> { obligations: ObligationStorage<'db>, @@ -41,7 +49,7 @@ pub struct FulfillmentCtxt<'db> { usable_in_snapshot: usize, } -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] struct ObligationStorage<'db> { /// Obligations which resulted in an overflow in fulfillment itself. /// @@ -123,10 +131,21 @@ impl<'db> FulfillmentCtxt<'db> { infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + // FIXME: See the comment in `select_where_possible()`. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } + pub(crate) fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'db>, + obligations: impl IntoIterator>, + ) { + // FIXME: See the comment in `select_where_possible()`. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); + } + pub(crate) fn collect_remaining_errors( &mut self, infcx: &InferCtxt<'db>, @@ -143,7 +162,11 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> Vec> { - assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + // FIXME(next-solver): We should bring this assertion back. Currently it panics because + // there are places which use `InferenceTable` and open a snapshot and register obligations + // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able + // to not put the obligations queue in `InferenceTable`'s snapshots. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); loop { let mut any_changed = false; @@ -216,9 +239,94 @@ impl<'db> FulfillmentCtxt<'db> { self.obligations.has_pending_obligations() } - fn pending_obligations(&self) -> PredicateObligations<'db> { + pub(crate) fn pending_obligations(&self) -> PredicateObligations<'db> { self.obligations.clone_pending() } + + pub(crate) fn drain_stalled_obligations_for_coroutines( + &mut self, + infcx: &InferCtxt<'db>, + ) -> PredicateObligations<'db> { + let stalled_coroutines = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + let stalled_coroutines = stalled_coroutines.inner(); + + if stalled_coroutines.is_empty() { + return Default::default(); + } + + self.obligations + .drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_coroutines, + cache: Default::default(), + }, + ) + .is_break() + }) + }) + .into_iter() + .map(|(o, _)| o) + .collect() + } +} + +/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root. +/// +/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to +/// be complete. However, this will lead to ambiguity errors, so we want to make it +/// accurate. +/// +/// This function can be also return false positives, which will lead to poor diagnostics +/// so we want to keep this visitor *precise* too. +pub struct StalledOnCoroutines<'a, 'db> { + pub stalled_coroutines: &'a [SolverDefId], + pub cache: FxHashSet>, +} + +impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> { + type Result = ControlFlow<()>; + + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result { + inspect_goal.goal().predicate.visit_with(self)?; + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Continue(()) + } + } +} + +impl<'db> TypeVisitor> for StalledOnCoroutines<'_, 'db> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if !self.cache.insert(ty) { + return ControlFlow::Continue(()); + } + + if let TyKind::Coroutine(def_id, _) = ty.kind() + && self.stalled_coroutines.contains(&def_id.into()) + { + ControlFlow::Break(()) + } else if ty.has_coroutines() { + ty.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } } #[derive(Debug)] @@ -227,3 +335,10 @@ pub enum NextSolverError<'db> { Ambiguity(PredicateObligation<'db>), Overflow(PredicateObligation<'db>), } + +impl NextSolverError<'_> { + #[inline] + pub fn is_true_error(&self) -> bool { + matches!(self, NextSolverError::TrueError(_)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs new file mode 100644 index 0000000000000..6cd9e55acf0e7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -0,0 +1,1332 @@ +//! Trait solving error diagnosis and reporting. +//! +//! This code isn't used by rust-analyzer (it should, but then it'll probably be better to re-port it from rustc). +//! It's only there because without it, debugging trait solver errors is a nightmare. + +use std::{fmt::Debug, ops::ControlFlow}; + +use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity, + error::ExpectedFound, + inherent::{IntoKind, PlaceholderConst, SliceLike, Span as _}, + lang_items::SolverTraitLangItem, + solve::{CandidateSource, Certainty, GoalSource, MaybeCause, NoSolution}, +}; +use tracing::{instrument, trace}; + +use crate::next_solver::{ + AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind, + SolverContext, SolverDefId, Span, Term, TraitPredicate, Ty, TyKind, TypeError, + fulfill::NextSolverError, + infer::{ + InferCtxt, + select::SelectionError, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + inspect::{self, ProofTreeVisitor}, + normalize::deeply_normalize_for_diagnostics, +}; + +#[derive(Debug)] +pub struct FulfillmentError<'db> { + pub obligation: PredicateObligation<'db>, + pub code: FulfillmentErrorCode<'db>, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'db>, +} + +impl<'db> FulfillmentError<'db> { + pub fn new( + obligation: PredicateObligation<'db>, + code: FulfillmentErrorCode<'db>, + root_obligation: PredicateObligation<'db>, + ) -> FulfillmentError<'db> { + FulfillmentError { obligation, code, root_obligation } + } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } +} + +#[derive(Debug, Clone)] +pub enum FulfillmentErrorCode<'db> { + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. + Cycle(PredicateObligations<'db>), + Select(SelectionError<'db>), + Project(MismatchedProjectionTypes<'db>), + Subtype(ExpectedFound>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, TypeError<'db>), + Ambiguity { + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option, + }, +} + +#[derive(Debug, Clone)] +pub struct MismatchedProjectionTypes<'db> { + pub err: TypeError<'db>, +} + +pub(super) fn fulfillment_error_for_no_solution<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ConstKind::Unevaluated(uv) => { + infcx.interner.type_of(uv.def).instantiate(infcx.interner, uv.args) + } + ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env), + ConstKind::Value(cv) => cv.ty, + kind => panic!( + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + PredicateKind::ConstEquate(..) => { + panic!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +pub(super) fn fulfillment_error_for_stalled<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + root_obligation.as_goal(), + Span::dummy(), + None, + ) { + Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { + (FulfillmentErrorCode::Ambiguity { overflow: None }, true) + } + Ok(GoalEvaluation { + certainty: + Certainty::Maybe(MaybeCause::Overflow { + suggest_increasing_limit, + keep_constraints: _, + }), + .. + }) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { + panic!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + Err(_) => { + panic!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +pub(super) fn fulfillment_error_for_overflow<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn find_best_leaf_obligation<'db>( + infcx: &InferCtxt<'db>, + obligation: &PredicateObligation<'db>, + consider_ambiguities: bool, +) -> PredicateObligation<'db> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + let obligation = infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.as_goal(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + }) + .unwrap_or(obligation); + deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) +} + +struct BestObligation<'db> { + obligation: PredicateObligation<'db>, + consider_ambiguities: bool, +} + +impl<'db> BestObligation<'db> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'db>, + and_then: impl FnOnce(&mut Self) -> >::Result, + ) -> >::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'db>, + ) -> Vec> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // We always handle rigid alias candidates separately as we may not add them for + // aliases whose trait bound doesn't hold. + candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals().iter().any(|nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::InstantiateHigherRanked + | GoalSource::AliasWellFormed + ) && nested_goal.result().is_err() + }) + }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'db>, + term: Term<'db>, + ) -> ControlFlow> { + let infcx = candidate.goal().infcx(); + let param_env = candidate.goal().goal().param_env; + + for obligation in wf::unnormalized_obligations(infcx, param_env, term).into_iter().flatten() + { + let nested_goal = candidate + .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal()); + // Skip nested goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => continue, + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + ControlFlow::Break(self.obligation.clone()) + } + + /// If a normalization of an associated item or a trait goal fails without trying any + /// candidates it's likely that normalizing its self type failed. We manually detect + /// such cases here. + fn detect_error_in_self_ty_normalization( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + self_ty: Ty<'db>, + ) -> ControlFlow> { + assert!(!self.consider_ambiguities); + let interner = goal.infcx().interner; + if let TyKind::Alias(..) = self_ty.kind() { + let infer_term = goal.infcx().next_ty_var(); + let pred = PredicateKind::AliasRelate( + self_ty.into(), + infer_term.into(), + AliasRelationDirection::Equate, + ); + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + pred, + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// When a higher-ranked projection goal fails, check that the corresponding + /// higher-ranked trait goal holds or not. This is because the process of + /// instantiating and then re-canonicalizing the binder of the projection goal + /// forces us to be unable to see that the leak check failed in the nested + /// `NormalizesTo` goal, so we don't fall back to the rigid projection check + /// that should catch when a projection goal fails due to an unsatisfied trait + /// goal. + fn detect_trait_error_in_higher_ranked_projection( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() + && !projection_clause.bound_vars().is_empty() + { + let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner)); + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// It is likely that `NormalizesTo` failed without any applicable candidates + /// because the alias is not well-formed. + /// + /// As we only enter `RigidAlias` candidates if the trait bound of the associated type + /// holds, we discard these candidates in `non_trivial_candidates` and always manually + /// check this here. + fn detect_non_well_formed_assoc_item( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + alias: AliasTerm<'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + alias.trait_ref(interner), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, alias.trait_ref(interner)), + goal.depth() + 1, + this, + ) + }) + } + + /// If we have no candidates, then it's likely that there is a + /// non-well-formed alias in the goal. + fn detect_error_from_empty_candidates( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let pred_kind = goal.goal().predicate.kind(); + + match pred_kind.no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => { + self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; + } + Some(PredicateKind::NormalizesTo(pred)) => { + if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst = + pred.alias.kind(interner) + { + self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; + self.detect_non_well_formed_assoc_item(goal, pred.alias)?; + } + } + Some(_) | None => {} + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { + type Result = ControlFlow>; + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result { + let interner = goal.infcx().interner; + // Skip goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => return ControlFlow::Continue(()), + } + + let pred = goal.goal().predicate; + + let candidates = self.non_trivial_candidates(goal); + let candidate = match candidates.as_slice() { + [candidate] => candidate, + [] => return self.detect_error_from_empty_candidates(goal), + _ => return ControlFlow::Break(self.obligation.clone()), + }; + + // // Don't walk into impls that have `do_not_recommend`. + // if let inspect::ProbeKind::TraitCandidate { + // source: CandidateSource::Impl(impl_def_id), + // result: _, + // } = candidate.kind() + // && interner.do_not_recommend_impl(impl_def_id) + // { + // trace!("#[do_not_recommend] -> exit"); + // return ControlFlow::Break(self.obligation.clone()); + // } + + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let child_mode = match pred.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + ChildMode::Trait(pred.kind().rebind(trait_pred)) + } + PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => { + ChildMode::Host(pred.kind().rebind(host_pred)) + } + PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(interner), + AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst + ) => + { + ChildMode::Trait(pred.kind().rebind(TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(interner), + polarity: PredicatePolarity::Positive, + })) + } + PredicateKind::Clause(ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written + // instead of `impl Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && interner + .is_trait_lang_item(poly_trait_pred.def_id(), SolverTraitLangItem::FnPtrTrait) + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let nested_pred = nested_goal.goal().predicate; + + let make_obligation = || Obligation { + cause: ObligationCause::dummy(), + param_env: nested_goal.goal().param_env, + predicate: nested_pred, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + ( + ChildMode::Trait(_) | ChildMode::Host(_), + GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + ) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(); + impl_where_bound_count += 1; + } + // Skip over a higher-ranked predicate. + (_, GoalSource::InstantiateHigherRanked) => { + obligation = self.obligation.clone(); + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(); + } + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(lhs)), + goal.depth() + 1, + self, + )?; + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(rhs)), + goal.depth() + 1, + self, + )?; + } + + self.detect_trait_error_in_higher_ranked_projection(goal)?; + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'db> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(PolyTraitPredicate<'db>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(Binder<'db, HostEffectPredicate>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +impl<'db> NextSolverError<'db> { + pub fn to_debuggable_error(&self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> { + match self { + NextSolverError::TrueError(obligation) => { + fulfillment_error_for_no_solution(infcx, obligation.clone()) + } + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation.clone()) + } + NextSolverError::Overflow(obligation) => { + fulfillment_error_for_overflow(infcx, obligation.clone()) + } + } + } +} + +mod wf { + use std::iter; + + use hir_def::ItemContainerId; + use rustc_type_ir::inherent::{ + AdtDef, BoundExistentialPredicates, GenericArg, GenericArgs as _, IntoKind, SliceLike, + Term as _, Ty as _, + }; + use rustc_type_ir::lang_items::SolverTraitLangItem; + use rustc_type_ir::{ + Interner, PredicatePolarity, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, + }; + use tracing::{debug, instrument, trace}; + + use crate::next_solver::infer::InferCtxt; + use crate::next_solver::infer::traits::{ + Obligation, ObligationCause, PredicateObligation, PredicateObligations, + }; + use crate::next_solver::{ + AliasTerm, Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, + GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitPredicate, + TraitRef, Ty, TyKind, + }; + + /// Compute the predicates that are required for a type to be well-formed. + /// + /// This is only intended to be used in the new solver, since it does not + /// take into account recursion depth or proper error-reporting spans. + pub fn unnormalized_obligations<'db>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + term: Term<'db>, + ) -> Option> { + debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); + + // However, if `arg` IS an unresolved inference variable, returns `None`, + // because we are not able to make any progress at all. This is to prevent + // cycles where we say "?0 is WF if ?0 is WF". + if term.is_infer() { + return None; + } + + let mut wf = + WfPredicates { infcx, param_env, out: PredicateObligations::new(), recursion_depth: 0 }; + wf.add_wf_preds_for_term(term); + Some(wf.out) + } + + struct WfPredicates<'a, 'db> { + infcx: &'a InferCtxt<'db>, + param_env: ParamEnv<'db>, + out: PredicateObligations<'db>, + recursion_depth: usize, + } + + /// Controls whether we "elaborate" supertraits and so forth on the WF + /// predicates. This is a kind of hack to address #43784. The + /// underlying problem in that issue was a trait structure like: + /// + /// ```ignore (illustrative) + /// trait Foo: Copy { } + /// trait Bar: Foo { } + /// impl Foo for T { } + /// impl Bar for T { } + /// ``` + /// + /// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but + /// we decide that this is true because `T: Bar` is in the + /// where-clauses (and we can elaborate that to include `T: + /// Copy`). This wouldn't be a problem, except that when we check the + /// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` + /// impl. And so nowhere did we check that `T: Copy` holds! + /// + /// To resolve this, we elaborate the WF requirements that must be + /// proven when checking impls. This means that (e.g.) the `impl Bar + /// for T` will be forced to prove not only that `T: Foo` but also `T: + /// Copy` (which it won't be able to do, because there is no `Copy` + /// impl for `T`). + #[derive(Debug, PartialEq, Eq, Copy, Clone)] + enum Elaborate { + All, + None, + } + + impl<'a, 'db> WfPredicates<'a, 'db> { + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. + fn add_wf_preds_for_trait_pred( + &mut self, + trait_pred: TraitPredicate<'db>, + elaborate: Elaborate, + ) { + let tcx = self.interner(); + let trait_ref = trait_pred.trait_ref; + + // Negative trait predicates don't require supertraits to hold, just + // that their args are WF. + if trait_pred.polarity == PredicatePolarity::Negative { + self.add_wf_preds_for_negative_trait_pred(trait_ref); + return; + } + + // if the trait predicate is not const, the wf obligations should not be const as well. + let obligations = self.nominal_obligations(trait_ref.def_id.0.into(), trait_ref.args); + + debug!("compute_trait_pred obligations {:?}", obligations); + let param_env = self.param_env; + let depth = self.recursion_depth; + + let extend = |PredicateObligation { predicate, mut cause, .. }| { + Obligation::with_depth(tcx, cause, depth, param_env, predicate) + }; + + if let Elaborate::All = elaborate { + let implied_obligations = rustc_type_ir::elaborate::elaborate(tcx, obligations); + let implied_obligations = implied_obligations.map(extend); + self.out.extend(implied_obligations); + } else { + self.out.extend(obligations); + } + + self.out.extend( + trait_ref + .args + .iter() + .enumerate() + .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) + .filter(|(_, term)| !term.has_escaping_bound_vars()) + .map(|(i, term)| { + let mut cause = ObligationCause::misc(); + // The first arg is the self ty - use the correct span for it. + Obligation::with_depth( + tcx, + cause, + depth, + param_env, + ClauseKind::WellFormed(term), + ) + }), + ); + } + + // Compute the obligations that are required for `trait_ref` to be WF, + // given that it is a *negative* trait predicate. + fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: TraitRef<'db>) { + for arg in trait_ref.args { + if let Some(term) = arg.as_term() { + self.add_wf_preds_for_term(term); + } + } + } + + /// Pushes the obligations required for an alias (except inherent) to be WF + /// into `self.out`. + fn add_wf_preds_for_alias_term(&mut self, data: AliasTerm<'db>) { + // A projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its args are wf + // + // (*) The predicates of an associated type include the predicates of + // the trait that it's contained in. For example, given + // + // trait A: Clone { + // type X where T: Copy; + // } + // + // The predicates of `<() as A>::X` are: + // [ + // `(): Sized` + // `(): Clone` + // `(): A` + // `i32: Sized` + // `i32: Clone` + // `i32: Copy` + // ] + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); + + self.add_wf_preds_for_projection_args(data.args); + } + + fn add_wf_preds_for_projection_args(&mut self, args: GenericArgs<'db>) { + let tcx = self.interner(); + let cause = ObligationCause::new(); + let param_env = self.param_env; + let depth = self.recursion_depth; + + self.out.extend( + args.iter() + .filter_map(|arg| arg.as_term()) + .filter(|term| !term.has_escaping_bound_vars()) + .map(|term| { + Obligation::with_depth( + tcx, + cause.clone(), + depth, + param_env, + ClauseKind::WellFormed(term), + ) + }), + ); + } + + fn require_sized(&mut self, subty: Ty<'db>) { + if !subty.has_escaping_bound_vars() { + let cause = ObligationCause::new(); + let trait_ref = TraitRef::new( + self.interner(), + self.interner().require_trait_lang_item(SolverTraitLangItem::Sized), + [subty], + ); + self.out.push(Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(trait_ref), + )); + } + } + + /// Pushes all the predicates needed to validate that `term` is WF into `out`. + #[instrument(level = "debug", skip(self))] + fn add_wf_preds_for_term(&mut self, term: Term<'db>) { + term.visit_with(self); + debug!(?self.out); + } + + #[instrument(level = "debug", skip(self))] + fn nominal_obligations( + &mut self, + def_id: SolverDefId, + args: GenericArgs<'db>, + ) -> PredicateObligations<'db> { + // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker + // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining + // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make + // the compiler do a bunch of work needlessly. + if let SolverDefId::TraitId(def_id) = def_id + && self.interner().is_trait_lang_item(def_id.into(), SolverTraitLangItem::Sized) + { + return Default::default(); + } + + self.interner() + .predicates_of(def_id) + .iter_instantiated(self.interner(), args) + .map(|pred| { + let cause = ObligationCause::new(); + Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + pred, + ) + }) + .filter(|pred| !pred.has_escaping_bound_vars()) + .collect() + } + + fn add_wf_preds_for_dyn_ty( + &mut self, + ty: Ty<'db>, + data: &[Binder<'db, ExistentialPredicate<'db>>], + region: Region<'db>, + ) { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. + // + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { + let implicit_bounds = object_region_bounds(self.interner(), data); + + let explicit_bound = region; + + self.out.reserve(implicit_bounds.len()); + for implicit_bound in implicit_bounds { + let cause = ObligationCause::new(); + let outlives = Binder::dummy(rustc_type_ir::OutlivesPredicate( + explicit_bound, + implicit_bound, + )); + self.out.push(Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + outlives, + )); + } + + // We don't add any wf predicates corresponding to the trait ref's generic arguments + // which allows code like this to compile: + // ```rust + // trait Trait {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` + } + } + } + + impl<'a, 'db> TypeVisitor> for WfPredicates<'a, 'db> { + type Result = (); + + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { + debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + + let tcx = self.interner(); + + match t.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) + | TyKind::Error(_) + | TyKind::Str + | TyKind::CoroutineWitness(..) + | TyKind::Never + | TyKind::Param(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + // Can only infer to `TyKind::Int(_) | TyKind::Uint(_)`. + TyKind::Infer(rustc_type_ir::IntVar(_)) => {} + + // Can only infer to `TyKind::Float(_)`. + TyKind::Infer(rustc_type_ir::FloatVar(_)) => {} + + TyKind::Slice(subty) => { + self.require_sized(subty); + } + + TyKind::Array(subty, len) => { + self.require_sized(subty); + // Note that the len being WF is implicitly checked while visiting. + // Here we just check that it's of type usize. + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstArgHasType( + len, + Ty::new_unit(self.interner()), + ))), + )); + } + + TyKind::Pat(base_ty, pat) => { + self.require_sized(base_ty); + } + + TyKind::Tuple(tys) => { + if let Some((_last, rest)) = tys.split_last() { + for &elem in rest { + self.require_sized(elem); + } + } + } + + TyKind::RawPtr(_, _) => { + // Simple cases that are WF if their type args are WF. + } + + TyKind::Alias( + rustc_type_ir::Projection | rustc_type_ir::Opaque | rustc_type_ir::Free, + data, + ) => { + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); + } + TyKind::Alias(rustc_type_ir::Inherent, data) => { + return; + } + + TyKind::Adt(def, args) => { + // WfNominalType + let obligations = self.nominal_obligations(def.def_id().0.into(), args); + self.out.extend(obligations); + } + + TyKind::FnDef(did, args) => { + // HACK: Check the return type of function definitions for + // well-formedness to mostly fix #84533. This is still not + // perfect and there may be ways to abuse the fact that we + // ignore requirements with escaping bound vars. That's a + // more general issue however. + let fn_sig = tcx.fn_sig(did).instantiate(tcx, args); + fn_sig.output().skip_binder().visit_with(self); + + let did = match did.0 { + hir_def::CallableDefId::FunctionId(id) => id.into(), + hir_def::CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), + hir_def::CallableDefId::EnumVariantId(id) => { + SolverDefId::Ctor(Ctor::Enum(id)) + } + }; + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + TyKind::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives( + rustc_type_ir::OutlivesPredicate(rty, r), + ))), + )); + } + } + + TyKind::Coroutine(did, args, ..) => { + // Walk ALL the types in the coroutine: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // coroutines don't take arguments. + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + } + + TyKind::Closure(did, args) => { + // Note that we cannot skip the generic types + // types. Normally, within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + let upvars = args.as_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + TyKind::CoroutineClosure(did, args) => { + // See the above comments. The same apply to coroutine-closures. + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + let upvars = args.as_coroutine_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + TyKind::FnPtr(..) => { + // Let the visitor iterate into the argument/return + // types appearing in the fn signature. + } + TyKind::UnsafeBinder(ty) => {} + + TyKind::Dynamic(data, r, _) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.add_wf_preds_for_dyn_ty(t, data.as_slice(), r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + if let Some(principal) = data.principal_def_id() { + self.out.push(Obligation::with_depth( + tcx, + ObligationCause::new(), + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::DynCompatible(principal)), + )); + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, we've at least simplified things (e.g., we went + // from `Vec?0>: WF` to `?0: WF`), so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + // See also the comment on `fn obligations`, describing cycle + // prevention, which happens before this can be reached. + TyKind::Infer(_) => { + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(t.into()))), + )); + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + let tcx = self.interner(); + + match c.kind() { + ConstKind::Unevaluated(uv) => { + if !c.has_escaping_bound_vars() { + let predicate = + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + + if let SolverDefId::ConstId(uv_def) = uv.def + && let ItemContainerId::ImplId(impl_) = + uv_def.loc(self.interner().db).container + && self.interner().db.impl_signature(impl_).target_trait.is_none() + { + return; // Subtree is handled by above function + } else { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + } + } + } + ConstKind::Infer(_) => { + let cause = ObligationCause::new(); + + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(c.into()))), + )); + } + ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + + ConstKind::Error(_) + | ConstKind::Param(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, _p: Predicate<'db>) -> Self::Result { + panic!("predicate should not be checked for well-formedness"); + } + } + + /// Given an object type like `SomeTrait + Send`, computes the lifetime + /// bounds that must hold on the elided self type. These are derived + /// from the declarations of `SomeTrait`, `Send`, and friends -- if + /// they declare `trait SomeTrait : 'static`, for example, then + /// `'static` would appear in the list. + /// + /// N.B., in some cases, particularly around higher-ranked bounds, + /// this function returns a kind of conservative approximation. + /// That is, all regions returned by this function are definitely + /// required, but there may be other region bounds that are not + /// returned, as well as requirements like `for<'a> T: 'a`. + /// + /// Requires that trait definitions have been processed so that we can + /// elaborate predicates and walk supertraits. + pub fn object_region_bounds<'db>( + interner: DbInterner<'db>, + existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>], + ) -> Vec> { + let erased_self_ty = Ty::new_unit(interner); + + let predicates = existential_predicates + .iter() + .map(|predicate| predicate.with_self_ty(interner, erased_self_ty)); + + rustc_type_ir::elaborate::elaborate(interner, predicates) + .filter_map(|pred| { + debug!(?pred); + match pred.kind().skip_binder() { + ClauseKind::TypeOutlives(rustc_type_ir::OutlivesPredicate(ref t, ref r)) => { + // Search for a bound of the form `erased_self_ty + // : 'a`, but be wary of something like `for<'a> + // erased_self_ty : 'a` (we interpret a + // higher-ranked bound like that as 'static, + // though at present the code in `fulfill.rs` + // considers such bounds to be unsatisfiable, so + // it's kind of a moot point since you could never + // construct such an object, but this seems + // correct even if that code changes). + if t == &erased_self_ty && !r.has_escaping_bound_vars() { + Some(*r) + } else { + None + } + } + ClauseKind::Trait(_) + | ClauseKind::HostEffect(..) + | ClauseKind::RegionOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::UnstableFeature(_) + | ClauseKind::ConstEvaluatable(_) => None, + } + }) + .collect() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 4e124d07d2b38..097bb85cbd491 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -6,12 +6,15 @@ use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, Variance, - inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, + inherent::{ + GenericArg as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _, + }, relate::{Relate, VarianceDiagInfo}, }; use smallvec::SmallVec; use crate::db::HirDatabase; +use crate::next_solver::{Binder, PolyFnSig}; use super::{ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, @@ -240,6 +243,34 @@ impl<'db> GenericArgs<'db> { args.push(kind); } } + + pub fn closure_sig_untupled(self) -> PolyFnSig<'db> { + let TyKind::FnPtr(inputs_and_output, hdr) = + self.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.kind() + else { + unreachable!("not a function pointer") + }; + inputs_and_output.with(hdr) + } + + /// A "sensible" `.split_closure_args()`, where the arguments are not in a tuple. + pub fn split_closure_args_untupled(self) -> rustc_type_ir::ClosureArgsParts> { + // FIXME: should use `ClosureSubst` when possible + match self.inner().as_slice() { + [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { + let interner = DbInterner::conjure(); + rustc_type_ir::ClosureArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(), + closure_kind_ty: closure_kind_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + } + } + _ => { + unreachable!("unexpected closure sig"); + } + } + } } impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { @@ -329,7 +360,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts> { // FIXME: should use `ClosureSubst` when possible match self.inner().as_slice() { - [parent_args @ .., sig_ty] => { + [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { let interner = DbInterner::conjure(); // This is stupid, but the next solver expects the first input to actually be a tuple let sig_ty = match sig_ty.expect_ty().kind() { @@ -354,8 +385,8 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< rustc_type_ir::ClosureArgsParts { parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), closure_sig_as_fn_ptr_ty: sig_ty, - closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)), - tupled_upvars_ty: Ty::new_unit(interner), + closure_kind_ty: closure_kind_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), } } _ => { @@ -392,14 +423,14 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts> { let interner = DbInterner::conjure(); match self.inner().as_slice() { - [parent_args @ .., resume_ty, yield_ty, return_ty] => { + [parent_args @ .., kind_ty, resume_ty, yield_ty, return_ty, tupled_upvars_ty] => { rustc_type_ir::CoroutineArgsParts { parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), - kind_ty: Ty::new_unit(interner), + kind_ty: kind_ty.expect_ty(), resume_ty: resume_ty.expect_ty(), yield_ty: yield_ty.expect_ty(), return_ty: return_ty.expect_ty(), - tupled_upvars_ty: Ty::new_unit(interner), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), } } _ => panic!("GenericArgs were likely not for a Coroutine."), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs index d64c7ed626eb2..8dfffe0d365e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -26,7 +26,7 @@ //! things. (That system should probably be refactored.) use rustc_type_ir::{ - FnSig, GenericArgKind, TypingMode, Variance, + FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance, error::ExpectedFound, inherent::{IntoKind, Span as _}, relate::{Relate, TypeRelation, solver_relating::RelateExt}, @@ -36,6 +36,8 @@ use crate::next_solver::{ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, TraitRef, Ty, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::relate::lattice::{LatticeOp, LatticeOpKind}, }; use super::{ @@ -210,6 +212,34 @@ impl<'a, 'db> At<'a, 'db> { } } + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. This errors in case normalization fails or is ambiguous. + pub fn deeply_normalize(self, value: T) -> Result>> + where + T: TypeFoldable>, + { + crate::next_solver::normalize::deeply_normalize(self, value) + } + + /// Computes the least-upper-bound, or mutual supertype, of two + /// values. The order of the arguments doesn't matter, but since + /// this can result in an error (e.g., if asked to compute LUB of + /// u32 and i32), it is meaningful to call one of them the + /// "expected type". + pub fn lub(self, expected: T, actual: T) -> InferResult<'db, T> + where + T: ToTrace<'db>, + { + let mut op = LatticeOp::new( + self.infcx, + ToTrace::to_trace(self.cause, expected, actual), + self.param_env, + LatticeOpKind::Lub, + ); + let value = op.relate(expected, actual)?; + Ok(InferOk { value, obligations: op.into_obligations() }) + } + fn goals_to_obligations(&self, goals: Vec>>) -> InferOk<'db, ()> { InferOk { value: (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index ce6c941287325..8e922abacb206 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::fmt; +use std::ops::Range; use std::sync::Arc; pub use BoundRegionConversionTime::*; @@ -55,6 +56,7 @@ mod opaque_types; pub mod region_constraints; pub mod relate; pub mod resolve; +pub(crate) mod select; pub(crate) mod snapshot; pub(crate) mod traits; mod type_variable; @@ -81,6 +83,10 @@ pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< ut::InPlace, &'a mut InferCtxtUndoLogs<'db>>, >; +fn iter_idx_range + Into>(range: Range) -> impl Iterator { + (range.start.into()..range.end.into()).map(Into::into) +} + /// This type contains all the things within `InferCtxt` that sit within a /// `RefCell` and are involved with taking/rolling back snapshots. Snapshot /// operations are hot enough that we want only one call to `borrow_mut` per diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index 50549694c3f23..7f15a467b3e87 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -22,7 +22,7 @@ use crate::next_solver::{ AliasTy, Binder, DbInterner, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, }; -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. pub(super) var_infos: IndexVec, @@ -239,7 +239,7 @@ pub struct VerifyIfEq<'db> { pub bound: Region<'db>, } -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct TwoRegions<'db> { a: Region<'db>, b: Region<'db>, @@ -458,6 +458,44 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } + pub(super) fn lub_regions( + &mut self, + db: DbInterner<'db>, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); + #[expect(clippy::if_same_then_else)] + if a.is_static() || b.is_static() { + a // nothing lives longer than static + } else if a == b { + a // LUB(a,a) = a + } else { + self.combine_vars(db, Lub, a, b) + } + } + + pub(super) fn glb_regions( + &mut self, + db: DbInterner<'db>, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); + #[expect(clippy::if_same_then_else)] + if a.is_static() { + b // static lives longer than everything else + } else if b.is_static() { + a // static lives longer than everything else + } else if a == b { + a // GLB(a,a) = a + } else { + self.combine_vars(db, Glb, a, b) + } + } + /// Resolves a region var to its value in the unification table, if it exists. /// Otherwise, it is resolved to the root `ReVar` in the table. pub fn opportunistic_resolve_var( @@ -531,6 +569,17 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } + pub fn vars_since_snapshot(&self, value_count: usize) -> Range { + RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()) + } + + /// See `InferCtxt::region_constraints_added_in_snapshot`. + pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot) -> bool { + self.undo_log + .region_constraints_in_snapshot(mark) + .any(|elt| matches!(elt, AddConstraint(_))) + } + #[inline] fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> { ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index de336c69b3180..7e2735db3b77a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -350,7 +350,7 @@ impl<'db> Generalizer<'_, 'db> { // with inference variables can cause incorrect ambiguity. // // cc trait-system-refactor-initiative#110 - if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { + if !alias.has_escaping_bound_vars() && !self.in_alias { return Ok(self.next_ty_var_for_alias()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs new file mode 100644 index 0000000000000..c7f771ffe37f7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -0,0 +1,269 @@ +//! # Lattice variables +//! +//! Generic code for operating on [lattices] of inference variables +//! that are characterized by an upper- and lower-bound. +//! +//! The code is defined quite generically so that it can be +//! applied both to type variables, which represent types being inferred, +//! and fn variables, which represent function types being inferred. +//! (It may eventually be applied to their types as well.) +//! In some cases, the functions are also generic with respect to the +//! operation on the lattice (GLB vs LUB). +//! +//! ## Note +//! +//! Although all the functions are generic, for simplicity, comments in the source code +//! generally refer to type variables and the LUB operation. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) + +use rustc_type_ir::{ + AliasRelationDirection, TypeVisitableExt, Upcast, Variance, + inherent::{IntoKind, Span as _}, + relate::{ + Relate, StructurallyRelateAliases, TypeRelation, VarianceDiagInfo, + combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}, + }, +}; + +use crate::next_solver::{ + AliasTy, Binder, Const, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Region, Span, Ty, + TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + relate::RelateResult, + traits::{Obligation, PredicateObligations}, + }, +}; + +#[derive(Clone, Copy)] +pub(crate) enum LatticeOpKind { + Glb, + Lub, +} + +impl LatticeOpKind { + fn invert(self) -> Self { + match self { + LatticeOpKind::Glb => LatticeOpKind::Lub, + LatticeOpKind::Lub => LatticeOpKind::Glb, + } + } +} + +/// A greatest lower bound" (common subtype) or least upper bound (common supertype). +pub(crate) struct LatticeOp<'infcx, 'db> { + infcx: &'infcx InferCtxt<'db>, + // Immutable fields + trace: TypeTrace<'db>, + param_env: ParamEnv<'db>, + // Mutable fields + kind: LatticeOpKind, + obligations: PredicateObligations<'db>, +} + +impl<'infcx, 'db> LatticeOp<'infcx, 'db> { + pub(crate) fn new( + infcx: &'infcx InferCtxt<'db>, + trace: TypeTrace<'db>, + param_env: ParamEnv<'db>, + kind: LatticeOpKind, + ) -> LatticeOp<'infcx, 'db> { + LatticeOp { infcx, trace, param_env, kind, obligations: PredicateObligations::new() } + } + + pub(crate) fn into_obligations(self) -> PredicateObligations<'db> { + self.obligations + } +} + +impl<'db> TypeRelation> for LatticeOp<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn relate_with_variance>>( + &mut self, + variance: Variance, + _info: VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'db, T> { + match variance { + Variance::Invariant => { + self.obligations.extend( + self.infcx + .at(&self.trace.cause, self.param_env) + .eq_trace(DefineOpaqueTypes::Yes, self.trace.clone(), a, b)? + .into_obligations(), + ); + Ok(a) + } + Variance::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + Variance::Bivariant => Ok(a), + Variance::Contravariant => { + self.kind = self.kind.invert(); + let res = self.relate(a, b); + self.kind = self.kind.invert(); + res + } + } + } + + /// Relates two types using a given lattice. + fn tys(&mut self, a: Ty<'db>, b: Ty<'db>) -> RelateResult<'db, Ty<'db>> { + if a == b { + return Ok(a); + } + + let infcx = self.infcx; + + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (TyKind::Infer(rustc_type_ir::TyVar(..)), _) => { + let v = infcx.next_ty_var(); + self.relate_bound(v, b, a)?; + Ok(v) + } + (_, TyKind::Infer(rustc_type_ir::TyVar(..))) => { + let v = infcx.next_ty_var(); + self.relate_bound(v, a, b)?; + Ok(v) + } + + ( + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: a_def_id, .. }), + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b), + + _ => super_combine_tys(infcx, self, a, b), + } + } + + fn regions(&mut self, a: Region<'db>, b: Region<'db>) -> RelateResult<'db, Region<'db>> { + let mut inner = self.infcx.inner.borrow_mut(); + let mut constraints = inner.unwrap_region_constraints(); + Ok(match self.kind { + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), a, b), + + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), a, b), + }) + } + + fn consts(&mut self, a: Const<'db>, b: Const<'db>) -> RelateResult<'db, Const<'db>> { + super_combine_consts(self.infcx, self, a, b) + } + + fn binders( + &mut self, + a: Binder<'db, T>, + b: Binder<'db, T>, + ) -> RelateResult<'db, Binder<'db, T>> + where + T: Relate>, + { + // GLB/LUB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB/LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance(Variance::Invariant, VarianceDiagInfo::default(), a, b)?; + Ok(a) + } else { + Ok(Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'infcx, 'db> LatticeOp<'infcx, 'db> { + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. + fn relate_bound(&mut self, v: Ty<'db>, a: Ty<'db>, b: Ty<'db>) -> RelateResult<'db, ()> { + let at = self.infcx.at(&self.trace.cause, self.param_env); + match self.kind { + LatticeOpKind::Glb => { + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, a)?.into_obligations()); + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, b)?.into_obligations()); + } + LatticeOpKind::Lub => { + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, a, v)?.into_obligations()); + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, b, v)?.into_obligations()); + } + } + Ok(()) + } +} + +impl<'db> PredicateEmittingRelation> for LatticeOp<'_, 'db> { + fn span(&self) -> Span { + Span::dummy() + } + + fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { + StructurallyRelateAliases::No + } + + fn param_env(&self) -> ParamEnv<'db> { + self.param_env + } + + fn register_predicates( + &mut self, + preds: impl IntoIterator, Predicate<'db>>>, + ) { + self.obligations.extend(preds.into_iter().map(|pred| { + Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred) + })) + } + + fn register_goals(&mut self, goals: impl IntoIterator>>) { + self.obligations.extend(goals.into_iter().map(|goal| { + Obligation::new( + self.infcx.interner, + self.trace.cause.clone(), + goal.param_env, + goal.predicate, + ) + })) + } + + fn register_alias_relate_predicate(&mut self, a: Ty<'db>, b: Ty<'db>) { + self.register_predicates([Binder::dummy(PredicateKind::AliasRelate( + a.into(), + b.into(), + // FIXME(deferred_projection_equality): This isn't right, I think? + AliasRelationDirection::Equate, + ))]); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs index 836ae39dc5253..0cc1cf756a9c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs @@ -9,5 +9,6 @@ use crate::next_solver::DbInterner; mod generalize; mod higher_ranked; +pub(crate) mod lattice; pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult, T>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs new file mode 100644 index 0000000000000..d656d94f4f91e --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -0,0 +1,334 @@ +use std::ops::ControlFlow; + +use hir_def::{ImplId, TraitId}; +use rustc_type_ir::{ + Interner, + solve::{BuiltinImplSource, CandidateSource, Certainty, inspect::ProbeKind}, +}; + +use crate::{ + db::InternedOpaqueTyId, + next_solver::{ + Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause, PredicateObligation, TraitObligation}, + }, + inspect::{InspectCandidate, InspectGoal, ProofTreeVisitor}, + }, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SelectionError<'db> { + /// The trait is not implemented. + Unimplemented, + /// After a closure impl has selected, its "outputs" were evaluated + /// (which for closures includes the "input" type params) and they + /// didn't resolve. See `confirm_poly_trait_refs` for more. + SignatureMismatch(Box>), + /// The trait pointed by `DefId` is dyn-incompatible. + TraitDynIncompatible(TraitId), + /// A given constant couldn't be evaluated. + NotConstEvaluatable(NotConstEvaluatable), + /// Exceeded the recursion depth during type projection. + Overflow(OverflowError), + /// Computing an opaque type's hidden type caused an error (e.g. a cycle error). + /// We can thus not know whether the hidden type implements an auto trait, so + /// we should not presume anything about it. + OpaqueTypeAutoTraitLeakageUnknown(InternedOpaqueTyId), + /// Error for a `ConstArgHasType` goal + ConstArgHasWrongType { ct: Const<'db>, ct_ty: Ty<'db>, expected_ty: Ty<'db> }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum NotConstEvaluatable { + Error(ErrorGuaranteed), + MentionsInfer, + MentionsParam, +} + +/// Indicates that trait evaluation caused overflow and in which pass. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum OverflowError { + Error(ErrorGuaranteed), + Canonical, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SignatureMismatchData<'db> { + pub found_trait_ref: TraitRef<'db>, + pub expected_trait_ref: TraitRef<'db>, + pub terr: TypeError<'db>, +} + +/// When performing resolution, it is typically the case that there +/// can be one of three outcomes: +/// +/// - `Ok(Some(r))`: success occurred with result `r` +/// - `Ok(None)`: could not definitely determine anything, usually due +/// to inconclusive type inference. +/// - `Err(e)`: error `e` occurred +pub type SelectionResult<'db, T> = Result, SelectionError<'db>>; + +/// Given the successful resolution of an obligation, the `ImplSource` +/// indicates where the impl comes from. +/// +/// For example, the obligation may be satisfied by a specific impl (case A), +/// or it may be relative to some bound that is in scope (case B). +/// +/// ```ignore (illustrative) +/// impl Clone for Option { ... } // Impl_1 +/// impl Clone for Box { ... } // Impl_2 +/// impl Clone for i32 { ... } // Impl_3 +/// +/// fn foo(concrete: Option>, param: T, mixed: Option) { +/// // Case A: ImplSource points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, ImplSource will carry resolutions for those as well: +/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) +/// +/// // Case B: ImplSource must be provided by caller. This applies when +/// // type is a type parameter. +/// param.clone(); // ImplSource::Param +/// +/// // Case C: A mix of cases A and B. +/// mixed.clone(); // ImplSource(Impl_1, [ImplSource::Param]) +/// } +/// ``` +/// +/// ### The type parameter `N` +/// +/// See explanation on `ImplSourceUserDefinedData`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ImplSource<'db, N> { + /// ImplSource identifying a particular impl. + UserDefined(ImplSourceUserDefinedData<'db, N>), + + /// Successful resolution to an obligation provided by the caller + /// for some type parameter. The `Vec` represents the + /// obligations incurred from normalizing the where-clause (if + /// any). + Param(Vec), + + /// Successful resolution for a builtin impl. + Builtin(BuiltinImplSource, Vec), +} + +impl<'db, N> ImplSource<'db, N> { + pub fn nested_obligations(self) -> Vec { + match self { + ImplSource::UserDefined(i) => i.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, + } + } + + 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, + } + } + + pub fn map(self, f: F) -> ImplSource<'db, M> + where + F: FnMut(N) -> M, + { + match self { + ImplSource::UserDefined(i) => ImplSource::UserDefined(ImplSourceUserDefinedData { + impl_def_id: i.impl_def_id, + args: i.args, + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSource::Param(n) => ImplSource::Param(n.into_iter().map(f).collect()), + ImplSource::Builtin(source, n) => { + ImplSource::Builtin(source, n.into_iter().map(f).collect()) + } + } + } +} + +/// Identifies a particular impl in the source, along with a set of +/// generic parameters from the impl's type/lifetime parameters. The +/// `nested` vector corresponds to the nested obligations attached to +/// the impl's type parameters. +/// +/// The type parameter `N` indicates the type used for "nested +/// obligations" that are required by the impl. During type-check, this +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an +/// impl, and nested obligations are satisfied later. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ImplSourceUserDefinedData<'db, N> { + pub impl_def_id: ImplId, + pub args: GenericArgs<'db>, + pub nested: Vec, +} + +pub type Selection<'db> = ImplSource<'db, PredicateObligation<'db>>; + +impl<'db> InferCtxt<'db> { + pub(crate) fn select( + &self, + obligation: &TraitObligation<'db>, + ) -> SelectionResult<'db, Selection<'db>> { + self.visit_proof_tree( + Goal::new(self.interner, obligation.param_env, obligation.predicate), + &mut Select {}, + ) + .break_value() + .unwrap() + } +} + +struct Select {} + +impl<'db> ProofTreeVisitor<'db> for Select { + type Result = ControlFlow>>; + + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { + let mut candidates = goal.candidates(); + candidates.retain(|cand| cand.result().is_ok()); + + // No candidates -- not implemented. + if candidates.is_empty() { + return ControlFlow::Break(Err(SelectionError::Unimplemented)); + } + + // One candidate, no need to winnow. + if candidates.len() == 1 { + return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))); + } + + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, and only on the good path. + if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + return ControlFlow::Break(Ok(None)); + } + + // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`. + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()) + .filter(|&j| i != j) + .any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return ControlFlow::Break(Ok(None)); + } + } + } + + ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))) + } +} + +/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)` +/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity, +/// and should always just gracefully fail here. We shouldn't rely on this incompleteness. +fn candidate_should_be_dropped_in_favor_of<'db>( + victim: &InspectCandidate<'_, 'db>, + other: &InspectCandidate<'_, 'db>, +) -> bool { + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, and only on the good path. + if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + return false; + } + + let ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind() else { + return false; + }; + let ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind() else { + return false; + }; + + match (victim_source, other_source) { + (_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => { + panic!("should not have assembled a CoherenceUnknowable candidate") + } + + // In the old trait solver, we arbitrarily choose lower vtable candidates + // over higher ones. + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)), + ) => a >= b, + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)), + ) => a >= b, + // Prefer dyn candidates over non-dyn candidates. This is necessary to + // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. + ( + CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound, + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + ) => true, + + // Prefer specializing candidates over specialized candidates. + (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { + victim.goal().infcx().interner.impl_specializes(other_def_id, victim_def_id) + } + + _ => false, + } +} + +fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> { + if let Certainty::Maybe(..) = cand.shallow_certainty() { + return None; + } + + let nested = match cand.result().expect("expected positive result") { + Certainty::Yes => Vec::new(), + Certainty::Maybe(_) => cand + .instantiate_nested_goals() + .into_iter() + .map(|nested| { + Obligation::new( + nested.infcx().interner, + ObligationCause::dummy(), + nested.goal().param_env, + nested.goal().predicate, + ) + }) + .collect(), + }; + + Some(match cand.kind() { + ProbeKind::TraitCandidate { source, result: _ } => match source { + CandidateSource::Impl(impl_def_id) => { + // FIXME: Remove this in favor of storing this in the tree + // For impl candidates, we do the rematch manually to compute the args. + ImplSource::UserDefined(ImplSourceUserDefinedData { + impl_def_id: impl_def_id.0, + args: cand.instantiate_impl_args(), + nested, + }) + } + CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested), + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested), + CandidateSource::CoherenceUnknowable => { + panic!("didn't expect to select an unknowable candidate") + } + }, + ProbeKind::NormalizedSelfTyAssembly + | ProbeKind::UnsizeAssembly + | ProbeKind::ProjectionCompatibility + | ProbeKind::OpaqueTypeStorageLookup { result: _ } + | ProbeKind::Root { result: _ } + | ProbeKind::ShadowedEnvProbing + | ProbeKind::RigidAlias { result: _ } => { + panic!("didn't expect to assemble trait candidate from {:#?}", cand.kind()) + } + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs new file mode 100644 index 0000000000000..74353574e3298 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs @@ -0,0 +1,263 @@ +use std::ops::Range; + +use ena::{ + snapshot_vec as sv, + unify::{self as ut, UnifyKey}, +}; +use rustc_type_ir::{ + ConstVid, FloatVid, IntVid, RegionKind, RegionVid, TyVid, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, +}; + +use crate::next_solver::{ + Const, ConstKind, DbInterner, Region, Ty, TyKind, + infer::{ + InferCtxt, UnificationTable, iter_idx_range, + snapshot::VariableLengths, + type_variable::TypeVariableOrigin, + unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}, + }, +}; + +fn vars_since_snapshot<'db, T>( + table: &UnificationTable<'_, 'db, T>, + snapshot_var_len: usize, +) -> Range +where + T: UnifyKey, + super::UndoLog<'db>: From>>, +{ + T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) +} + +fn const_vars_since_snapshot<'db>( + table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>, + snapshot_var_len: usize, +) -> (Range, Vec) { + let range = vars_since_snapshot(table, snapshot_var_len); + let range = range.start.vid..range.end.vid; + + ( + range.clone(), + iter_idx_range(range) + .map(|index| match table.probe_value(index) { + ConstVariableValue::Known { value: _ } => { + ConstVariableOrigin { param_def_id: None } + } + ConstVariableValue::Unknown { origin, universe: _ } => origin, + }) + .collect(), + ) +} + +impl<'db> InferCtxt<'db> { + /// This rather funky routine is used while processing expected + /// types. What happens here is that we want to propagate a + /// coercion through the return type of a fn to its + /// argument. Consider the type of `Option::Some`, which is + /// basically `for fn(T) -> Option`. So if we have an + /// expression `Some(&[1, 2, 3])`, and that has the expected type + /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]` + /// with the expectation of `&[u32]`. This will cause us to coerce + /// from `&[u32; 3]` to `&[u32]` and make the users life more + /// pleasant. + /// + /// The way we do this is using `fudge_inference_if_ok`. What the + /// routine actually does is to start a snapshot and execute the + /// closure `f`. In our example above, what this closure will do + /// is to unify the expectation (`Option<&[u32]>`) with the actual + /// return type (`Option`, where `?T` represents the variable + /// instantiated for `T`). This will cause `?T` to be unified + /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The + /// input type (`?T`) is then returned by `f()`. + /// + /// At this point, `fudge_inference_if_ok` will normalize all type + /// variables, converting `?T` to `&?a [u32]` and end the + /// snapshot. The problem is that we can't just return this type + /// out, because it references the region variable `?a`, and that + /// region variable was popped when we popped the snapshot. + /// + /// So what we do is to keep a list (`region_vars`, in the code below) + /// of region variables created during the snapshot (here, `?a`). We + /// fold the return value and replace any such regions with a *new* + /// region variable (e.g., `?b`) and return the result (`&?b [u32]`). + /// This can then be used as the expectation for the fn argument. + /// + /// The important point here is that, for soundness purposes, the + /// regions in question are not particularly important. We will + /// use the expected types to guide coercions, but we will still + /// type-check the resulting types from those coercions against + /// the actual types (`?T`, `Option`) -- and remember that + /// after the snapshot is popped, the variable `?T` is no longer + /// unified. + pub fn fudge_inference_if_ok(&self, f: F) -> Result + where + F: FnOnce() -> Result, + T: TypeFoldable>, + { + let variable_lengths = self.variable_lengths(); + let (snapshot_vars, value) = self.probe(|_| { + let value = f()?; + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + let snapshot_vars = SnapshotVarData::new(self, variable_lengths); + Ok((snapshot_vars, self.resolve_vars_if_possible(value))) + })?; + + // At this point, we need to replace any of the now-popped + // type/region variables that appear in `value` with a fresh + // variable of the appropriate kind. We can't do this during + // the probe because they would just get popped then too. =) + Ok(self.fudge_inference(snapshot_vars, value)) + } + + fn fudge_inference>>( + &self, + snapshot_vars: SnapshotVarData, + value: T, + ) -> T { + // Micro-optimization: if no variables have been created, then + // `value` can't refer to any of them. =) So we can just return it. + if snapshot_vars.is_empty() { + value + } else { + value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars }) + } + } +} + +struct SnapshotVarData { + region_vars: Range, + type_vars: (Range, Vec), + int_vars: Range, + float_vars: Range, + const_vars: (Range, Vec), +} + +impl SnapshotVarData { + fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData { + let mut inner = infcx.inner.borrow_mut(); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(vars_pre_snapshot.region_constraints_len); + let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len); + let int_vars = + vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len); + let float_vars = + vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len); + + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + vars_pre_snapshot.const_var_len, + ); + SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } + } + + fn is_empty(&self) -> bool { + let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self; + region_vars.is_empty() + && type_vars.0.is_empty() + && int_vars.is_empty() + && float_vars.is_empty() + && const_vars.0.is_empty() + } +} + +struct InferenceFudger<'a, 'db> { + infcx: &'a InferCtxt<'db>, + snapshot_vars: SnapshotVarData, +} + +impl<'a, 'db> TypeFolder> for InferenceFudger<'a, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Infer(infer_ty) = ty.kind() { + match infer_ty { + rustc_type_ir::TyVar(vid) => { + if self.snapshot_vars.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); + let origin = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var_with_origin(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } + } + rustc_type_ir::IntVar(vid) => { + if self.snapshot_vars.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } + } + rustc_type_ir::FloatVar(vid) => { + if self.snapshot_vars.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + rustc_type_ir::FreshTy(_) + | rustc_type_ir::FreshIntTy(_) + | rustc_type_ir::FreshFloatTy(_) => { + unreachable!("unexpected fresh infcx var") + } + } + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if let RegionKind::ReVar(vid) = r.kind() { + if self.snapshot_vars.region_vars.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.start.index(); + self.infcx.next_region_var() + } else { + r + } + } else { + r + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if let ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + rustc_type_ir::InferConst::Var(vid) => { + if self.snapshot_vars.const_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); + let origin = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var_with_origin(origin) + } else { + ct + } + } + rustc_type_ir::InferConst::Fresh(_) => { + unreachable!("unexpected fresh infcx var") + } + } + } else if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 8c7dfb25e07f9..7b9ca96c51406 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -7,6 +7,7 @@ use tracing::{debug, instrument}; use super::InferCtxt; use super::region_constraints::RegionSnapshot; +mod fudge; pub(crate) mod undo_log; use undo_log::{Snapshot, UndoLog}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs index 28ae56f4ee70a..05a1013b3fbd5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -178,6 +178,10 @@ impl<'db> InferCtxtUndoLogs<'db> { }) } + pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot) -> bool { + self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..))) + } + fn assert_open_snapshot(&self, snapshot: &Snapshot) { // Failures here may indicate a failure to follow a stack discipline. assert!(self.logs.len() >= snapshot.undo_len); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index f1df806ab3187..68aa12d7bb0cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -7,14 +7,16 @@ use std::{ hash::{Hash, Hasher}, }; +use rustc_type_ir::elaborate::Elaboratable; use rustc_type_ir::{ PredicatePolarity, Upcast, solve::{Certainty, NoSolution}, }; +use rustc_type_ir::{TypeFoldable, TypeVisitable}; use crate::next_solver::{ - Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate, - Ty, + Binder, Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, Span, + TraitPredicate, Ty, }; use super::InferCtxt; @@ -29,24 +31,29 @@ use super::InferCtxt; /// only live for a short period of time. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ObligationCause { - /// The ID of the fn body that triggered this obligation. This is - /// used for region obligations to determine the precise - /// environment in which the region obligation should be evaluated - /// (in particular, closures can add new assumptions). See the - /// field `region_obligations` of the `FulfillmentContext` for more - /// information. - pub body_id: Option, + // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we + // don't report trait solving diagnostics, so this is irrelevant. + _private: (), } impl ObligationCause { + #[expect( + clippy::new_without_default, + reason = "`new` is temporary, eventually we will provide span etc. here" + )] #[inline] - pub fn new(body_id: SolverDefId) -> ObligationCause { - ObligationCause { body_id: Some(body_id) } + pub fn new() -> ObligationCause { + ObligationCause { _private: () } } - #[inline(always)] + #[inline] pub fn dummy() -> ObligationCause { - ObligationCause { body_id: None } + ObligationCause::new() + } + + #[inline] + pub fn misc() -> ObligationCause { + ObligationCause::new() } } @@ -75,6 +82,72 @@ pub struct Obligation<'db, T> { pub recursion_depth: usize, } +/// For [`Obligation`], a sub-obligation is combined with the current obligation's +/// param-env and cause code. +impl<'db> Elaboratable> for PredicateObligation<'db> { + fn predicate(&self) -> Predicate<'db> { + self.predicate + } + + fn child(&self, clause: Clause<'db>) -> Self { + Obligation { + cause: self.cause.clone(), + param_env: self.param_env, + recursion_depth: 0, + predicate: clause.as_predicate(), + } + } + + fn child_with_derived_cause( + &self, + clause: Clause<'db>, + span: Span, + parent_trait_pred: PolyTraitPredicate<'db>, + index: usize, + ) -> Self { + let cause = ObligationCause::new(); + Obligation { + cause, + param_env: self.param_env, + recursion_depth: 0, + predicate: clause.as_predicate(), + } + } +} + +impl<'db, T: TypeVisitable>> TypeVisitable> for Obligation<'db, T> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + rustc_ast_ir::try_visit!(self.param_env.visit_with(visitor)); + self.predicate.visit_with(visitor) + } +} + +impl<'db, T: TypeFoldable>> TypeFoldable> for Obligation<'db, T> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(Obligation { + cause: self.cause.clone(), + param_env: self.param_env.try_fold_with(folder)?, + predicate: self.predicate.try_fold_with(folder)?, + recursion_depth: self.recursion_depth, + }) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + Obligation { + cause: self.cause.clone(), + param_env: self.param_env.fold_with(folder), + predicate: self.predicate.fold_with(folder), + recursion_depth: self.recursion_depth, + } + } +} + impl<'db, T: Copy> Obligation<'db, T> { pub fn as_goal(&self) -> Goal<'db, T> { Goal { param_env: self.param_env, predicate: self.predicate } @@ -156,15 +229,6 @@ impl<'db, O> Obligation<'db, O> { Obligation { cause, param_env, recursion_depth, predicate } } - pub fn misc( - tcx: DbInterner<'db>, - body_id: SolverDefId, - param_env: ParamEnv<'db>, - trait_ref: impl Upcast, O>, - ) -> Obligation<'db, O> { - Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref) - } - pub fn with

::Metadata @@ -491,6 +490,13 @@ define!( /// This allows bypassing normal validation to generate strange casts. fn CastPtrToPtr(operand: T) -> U ); +define!( + "mir_cast_unsize", + /// Emits a `CastKind::PointerCoercion(Unsize)` cast. + /// + /// This allows bypassing normal validation to generate strange casts. + fn CastUnsize(operand: T) -> U +); define!( "mir_make_place", #[doc(hidden)] diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea25589..40fd2cdeb86fd 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -126,7 +126,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), diff --git a/tests/mir-opt/building/custom/arrays.arrays.built.after.mir b/tests/mir-opt/building/custom/arrays.arrays.built.after.mir index 30d11e31e4d4e..f9f24c8eabe4d 100644 --- a/tests/mir-opt/building/custom/arrays.arrays.built.after.mir +++ b/tests/mir-opt/building/custom/arrays.arrays.built.after.mir @@ -3,12 +3,16 @@ fn arrays() -> usize { let mut _0: usize; let mut _1: [i32; C]; - let mut _2: usize; + let mut _2: *const [i32; C]; + let mut _3: *const [i32]; + let mut _4: usize; bb0: { _1 = [const 5_i32; C]; - _2 = Len(_1); - _0 = copy _2; + _2 = &raw const _1; + _3 = copy _2 as *const [i32] (PointerCoercion(Unsize, AsCast)); + _4 = PtrMetadata(copy _3); + _0 = copy _4; return; } } diff --git a/tests/mir-opt/building/custom/arrays.rs b/tests/mir-opt/building/custom/arrays.rs index 4bd6f93e11340..e9cdded4a0e68 100644 --- a/tests/mir-opt/building/custom/arrays.rs +++ b/tests/mir-opt/building/custom/arrays.rs @@ -10,7 +10,9 @@ fn arrays() -> usize { mir! { { let x = [5_i32; C]; - let c = Len(x); + let y = &raw const x; + let z = CastUnsize::<_, *const [i32]>(y); + let c = PtrMetadata(z); RET = c; Return() } diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff new file mode 100644 index 0000000000000..fd9ef54fe775f --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff new file mode 100644 index 0000000000000..1fec08c256201 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff new file mode 100644 index 0000000000000..fd9ef54fe775f --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff new file mode 100644 index 0000000000000..1fec08c256201 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs new file mode 100644 index 0000000000000..e0e68f9fde54a --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs @@ -0,0 +1,34 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ test-mir-pass: DataflowConstProp +//@ compile-flags: -Zmir-enable-passes=+InstSimplify-after-simplifycfg +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR slice_len.main.DataflowConstProp.diff + +// CHECK-LABEL: fn main( +fn main() { + // CHECK: debug local => [[local:_.*]]; + // CHECK: debug constant => [[constant:_.*]]; + + // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = Lt( + // CHECK-NOT: assert(move _ + // CHECK: {{_.*}} = const 3_usize; + // CHECK: {{_.*}} = const true; + // CHECK: assert(const true, + + // CHECK: [[local]] = copy (*{{_.*}})[1 of 2]; + let local = (&[1u32, 2, 3] as &[u32])[1]; + + // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = Lt( + // CHECK-NOT: assert(move _ + const SLICE: &[u32] = &[1, 2, 3]; + // CHECK: {{_.*}} = const 3_usize; + // CHECK: {{_.*}} = const true; + // CHECK: assert(const true, + + // CHECK-NOT: [[constant]] = {{copy|move}} (*{{_.*}})[_ + // CHECK: [[constant]] = copy (*{{_.*}})[1 of 2]; + let constant = SLICE[1]; +} diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff index 0d0477fe7729f..59b65a52f4ee6 100644 --- a/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff @@ -12,8 +12,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); _3 = &(*_1); _2 = move _3 as &[i32] (PointerCoercion(Unsize, Implicit)); @@ -23,8 +22,7 @@ - _0 = PtrMetadata(move _4); + _0 = const 42_usize; StorageDead(_4); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff index 0d0477fe7729f..59b65a52f4ee6 100644 --- a/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff @@ -12,8 +12,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); _3 = &(*_1); _2 = move _3 as &[i32] (PointerCoercion(Unsize, Implicit)); @@ -23,8 +22,7 @@ - _0 = PtrMetadata(move _4); + _0 = const 42_usize; StorageDead(_4); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff new file mode 100644 index 0000000000000..feba0fe7c4a21 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff @@ -0,0 +1,71 @@ +- // MIR for `norm2` before InstSimplify-after-simplifycfg ++ // MIR for `norm2` after InstSimplify-after-simplifycfg + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const 2_usize); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _3) -> [success: bb1, unwind unreachable]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const 2_usize); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _6) -> [success: bb2, unwind unreachable]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff new file mode 100644 index 0000000000000..0ccf6a825c49a --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff @@ -0,0 +1,71 @@ +- // MIR for `norm2` before InstSimplify-after-simplifycfg ++ // MIR for `norm2` after InstSimplify-after-simplifycfg + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const 2_usize); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _3) -> [success: bb1, unwind continue]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const 2_usize); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _6) -> [success: bb2, unwind continue]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs new file mode 100644 index 0000000000000..91f43f75689de --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -0,0 +1,15 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ test-mir-pass: InstSimplify-after-simplifycfg + +// EMIT_MIR combine_array_len.norm2.InstSimplify-after-simplifycfg.diff +fn norm2(x: [f32; 2]) -> f32 { + // CHECK-LABEL: fn norm2( + // CHECK-NOT: Len( + let a = x[0]; + let b = x[1]; + a * a + b * b +} + +fn main() { + assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); +} diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index 08dee3697e072..5cf36b9aebf2f 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -7,18 +7,16 @@ let _2: &[T]; let _3: &[T; 3]; let _4: [T; 3]; - let mut _5: usize; - let mut _6: bool; - let mut _10: !; + let mut _8: !; scope 1 { debug v => _2; + let _5: &T; + let _6: &T; let _7: &T; - let _8: &T; - let _9: &T; scope 2 { - debug v1 => _7; - debug v2 => _8; - debug v3 => _9; + debug v1 => _5; + debug v2 => _6; + debug v3 => _7; } } @@ -27,25 +25,23 @@ _4 = [copy _1, copy _1, copy _1]; _3 = &_4; _2 = copy _3 as &[T] (PointerCoercion(Unsize, Implicit)); - nop; - nop; goto -> bb2; } bb1: { - _10 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; + _8 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; } bb2: { + StorageLive(_5); + _5 = &(*_2)[0 of 3]; + StorageLive(_6); + _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[0 of 3]; - StorageLive(_8); - _8 = &(*_2)[1 of 3]; - StorageLive(_9); - _9 = &(*_2)[2 of 3]; - StorageDead(_9); - StorageDead(_8); + _7 = &(*_2)[2 of 3]; StorageDead(_7); + StorageDead(_6); + StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index aa44a2ad532be..0598a3aa3f19a 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -7,18 +7,16 @@ let _2: &[T]; let _3: &[T; 3]; let _4: [T; 3]; - let mut _5: usize; - let mut _6: bool; - let mut _10: !; + let mut _8: !; scope 1 { debug v => _2; + let _5: &T; + let _6: &T; let _7: &T; - let _8: &T; - let _9: &T; scope 2 { - debug v1 => _7; - debug v2 => _8; - debug v3 => _9; + debug v1 => _5; + debug v2 => _6; + debug v3 => _7; } } @@ -27,25 +25,23 @@ _4 = [copy _1, copy _1, copy _1]; _3 = &_4; _2 = copy _3 as &[T] (PointerCoercion(Unsize, Implicit)); - nop; - nop; goto -> bb2; } bb1: { - _10 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; + _8 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; } bb2: { + StorageLive(_5); + _5 = &(*_2)[0 of 3]; + StorageLive(_6); + _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[0 of 3]; - StorageLive(_8); - _8 = &(*_2)[1 of 3]; - StorageLive(_9); - _9 = &(*_2)[2 of 3]; - StorageDead(_9); - StorageDead(_8); + _7 = &(*_2)[2 of 3]; StorageDead(_7); + StorageDead(_6); + StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff index 180a7db029794..714a12804e692 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff @@ -18,8 +18,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -41,8 +40,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff index 180a7db029794..714a12804e692 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff @@ -18,8 +18,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -41,8 +40,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff index 49964f8b49e26..b236b22f067d8 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff @@ -17,8 +17,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -38,8 +37,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff index 49964f8b49e26..b236b22f067d8 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff @@ -17,8 +17,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -38,8 +37,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff index 3da795b61f948..375b6096d88d8 100644 --- a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff @@ -17,14 +17,15 @@ let mut _15: std::ops::RangeFull; let mut _16: usize; let mut _17: usize; - let mut _18: bool; - let _23: &&mut u8; - let _24: &mut u8; - let mut _25: debuginfo::T; + let mut _18: usize; + let mut _19: bool; + let _24: &&mut u8; + let _25: &mut u8; + let mut _26: debuginfo::T; scope 1 { debug ref_mut_u8 => _1; let _3: &u8; - let mut _28: &debuginfo::T; + let mut _29: &debuginfo::T; scope 2 { debug field => _3; let _5: &u8; @@ -32,22 +33,22 @@ - debug reborrow => _5; + debug reborrow => _1; let _9: &i32; - let _22: &&&mut u8; - let mut _27: &std::option::Option; + let _23: &&&mut u8; + let mut _28: &std::option::Option; scope 4 { debug variant_field => _9; } scope 5 { - debug constant_index => _19; - debug subslice => _20; - debug constant_index_from_end => _21; - let _19: &i32; - let _20: &[i32]; - let _21: &i32; - let mut _26: &[i32; 10]; + debug constant_index => _20; + debug subslice => _21; + debug constant_index_from_end => _22; + let _20: &i32; + let _21: &[i32]; + let _22: &i32; + let mut _27: &[i32; 10]; } scope 6 { - debug multiple_borrow => _22; + debug multiple_borrow => _23; } } } @@ -59,8 +60,8 @@ _2 = const 5_u8; _1 = &mut _2; StorageLive(_3); - _28 = const debuginfo::promoted[2]; - _3 = &((*_28).0: u8); + _29 = const debuginfo::promoted[2]; + _3 = &((*_29).0: u8); - StorageLive(_5); - _5 = &(*_1); - StorageLive(_6); @@ -76,8 +77,8 @@ bb2: { StorageLive(_9); - _27 = const debuginfo::promoted[1]; - _9 = &(((*_27) as Some).0: i32); + _28 = const debuginfo::promoted[1]; + _9 = &(((*_28) as Some).0: i32); - _6 = const (); StorageDead(_9); goto -> bb4; @@ -92,11 +93,11 @@ StorageDead(_7); - StorageDead(_6); - StorageLive(_10); -- StorageLive(_11); + StorageLive(_11); - StorageLive(_12); StorageLive(_13); - _26 = const debuginfo::promoted[0]; - _13 = &(*_26); + _27 = const debuginfo::promoted[0]; + _13 = &(*_27); StorageLive(_15); _15 = RangeFull; - _12 = <[i32; 10] as Index>::index(move _13, move _15) -> [return: bb5, unwind continue]; @@ -106,28 +107,28 @@ bb5: { StorageDead(_15); StorageDead(_13); -- _11 = &(*_12); -- _16 = Len((*_11)); -+ _16 = Len((*_12)); - _17 = const 3_usize; - _18 = Ge(move _16, move _17); - switchInt(move _18) -> [0: bb7, otherwise: bb6]; + _11 = &(*_12); + _17 = PtrMetadata(copy _11); + _16 = move _17; + _18 = const 3_usize; + _19 = Ge(move _16, move _18); + switchInt(move _19) -> [0: bb7, otherwise: bb6]; } bb6: { - StorageLive(_19); -- _19 = &(*_11)[1 of 3]; -+ _19 = &(*_12)[1 of 3]; StorageLive(_20); -- _20 = &(*_11)[2:-1]; -+ _20 = &(*_12)[2:-1]; +- _20 = &(*_11)[1 of 3]; ++ _20 = &(*_12)[1 of 3]; StorageLive(_21); -- _21 = &(*_11)[-1 of 3]; +- _21 = &(*_11)[2:-1]; ++ _21 = &(*_12)[2:-1]; + StorageLive(_22); +- _22 = &(*_11)[-1 of 3]; - _10 = const (); -+ _21 = &(*_12)[-1 of 3]; ++ _22 = &(*_12)[-1 of 3]; + StorageDead(_22); StorageDead(_21); StorageDead(_20); - StorageDead(_19); goto -> bb8; } @@ -138,21 +139,21 @@ bb8: { - StorageDead(_12); -- StorageDead(_11); + StorageDead(_11); - StorageDead(_10); - StorageLive(_22); StorageLive(_23); StorageLive(_24); StorageLive(_25); - _25 = T(const 6_u8); - _24 = &mut (_25.0: u8); + StorageLive(_26); + _26 = T(const 6_u8); + _25 = &mut (_26.0: u8); + _24 = &_25; _23 = &_24; - _22 = &_23; _0 = const (); + StorageDead(_26); StorageDead(_25); StorageDead(_24); StorageDead(_23); - StorageDead(_22); - StorageDead(_5); StorageDead(_3); StorageDead(_2); From f018b46558c2c4049cf26bd88d562eefa69ff336 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 01:45:06 +0000 Subject: [PATCH 0490/1900] Update docs. --- library/core/src/intrinsics/mir.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index c4bd10e606aab..a800ef1cb9375 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -233,7 +233,8 @@ //! //! - Operands implicitly convert to `Use` rvalues. //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue. -//! - [`Discriminant`], [`Len`], and [`CopyForDeref`] have associated functions. +//! - [`CopyForDeref`], [`CastTransmute`], [`CastPtrToPtr`], [`CastUnsize`], and [`Discriminant`] +//! have associated functions. //! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc. //! - The binary operation `Offset` can be created via [`Offset`]. //! - Checked binary operations are represented by wrapping the associated binop in [`Checked`]. From 4516fee8cbcbc6e85e4da34614aaa579315563c7 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 22:29:04 +0000 Subject: [PATCH 0491/1900] Remove Rvalue::Len. --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea25589..40fd2cdeb86fd 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -126,7 +126,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), From de73af9ec47552dcade86e4ebc4edb8a68fddd2e Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 22:44:35 +0000 Subject: [PATCH 0492/1900] Add test. --- ...onst_array_len.built.after.panic-abort.mir | 155 ++++++++++++++++++ ...nst_array_len.built.after.panic-unwind.mir | 155 ++++++++++++++++++ tests/mir-opt/building/match/array_len.rs | 31 ++++ ..._len.slice_len.built.after.panic-abort.mir | 115 +++++++++++++ ...len.slice_len.built.after.panic-unwind.mir | 115 +++++++++++++ ...implify-after-simplifycfg.panic-abort.diff | 71 ++++++++ ...mplify-after-simplifycfg.panic-unwind.diff | 71 ++++++++ .../mir-opt/instsimplify/combine_array_len.rs | 12 +- 8 files changed, 724 insertions(+), 1 deletion(-) create mode 100644 tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir create mode 100644 tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir create mode 100644 tests/mir-opt/building/match/array_len.rs create mode 100644 tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir create mode 100644 tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir create mode 100644 tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff diff --git a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir new file mode 100644 index 0000000000000..8d16f074b1e34 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir @@ -0,0 +1,155 @@ +// MIR for `const_array_len` after built + +fn const_array_len(_1: [T; 5]) -> () { + debug x => _1; + let mut _0: (); + let _6: (); + let mut _7: T; + let _8: (); + let mut _9: T; + let _10: (); + let mut _11: [T; 2]; + let _12: (); + let mut _13: T; + scope 1 { + debug a => _2; + debug b => _3; + debug rest => _4; + debug e => _5; + let _2: T; + let _3: T; + let _4: [T; 2]; + let _5: T; + } + + bb0: { + PlaceMention(_1); + falseEdge -> [real: bb2, imaginary: bb1]; + } + + bb1: { + goto -> bb7; + } + + bb2: { + StorageLive(_2); + _2 = move _1[0 of 5]; + StorageLive(_3); + _3 = move _1[1 of 5]; + StorageLive(_4); + _4 = move _1[2..4]; + StorageLive(_5); + _5 = move _1[4 of 5]; + StorageLive(_6); + StorageLive(_7); + _7 = move _2; + _6 = opaque::(move _7) -> [return: bb3, unwind: bb17]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = move _3; + _8 = opaque::(move _9) -> [return: bb4, unwind: bb16]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = move _4; + _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = move _5; + _12 = opaque::(move _13) -> [return: bb6, unwind: bb14]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb19]; + } + + bb7: { + _0 = const (); + goto -> bb12; + } + + bb8: { + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb20]; + } + + bb9: { + StorageDead(_4); + drop(_3) -> [return: bb10, unwind: bb21]; + } + + bb10: { + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb22]; + } + + bb11: { + StorageDead(_2); + goto -> bb12; + } + + bb12: { + drop(_1) -> [return: bb13, unwind: bb23]; + } + + bb13: { + return; + } + + bb14 (cleanup): { + drop(_13) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_9) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_7) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { + drop(_5) -> [return: bb19, unwind terminate(cleanup)]; + } + + bb19 (cleanup): { + drop(_4) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_3) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_2) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_1) -> [return: bb23, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir new file mode 100644 index 0000000000000..8d16f074b1e34 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir @@ -0,0 +1,155 @@ +// MIR for `const_array_len` after built + +fn const_array_len(_1: [T; 5]) -> () { + debug x => _1; + let mut _0: (); + let _6: (); + let mut _7: T; + let _8: (); + let mut _9: T; + let _10: (); + let mut _11: [T; 2]; + let _12: (); + let mut _13: T; + scope 1 { + debug a => _2; + debug b => _3; + debug rest => _4; + debug e => _5; + let _2: T; + let _3: T; + let _4: [T; 2]; + let _5: T; + } + + bb0: { + PlaceMention(_1); + falseEdge -> [real: bb2, imaginary: bb1]; + } + + bb1: { + goto -> bb7; + } + + bb2: { + StorageLive(_2); + _2 = move _1[0 of 5]; + StorageLive(_3); + _3 = move _1[1 of 5]; + StorageLive(_4); + _4 = move _1[2..4]; + StorageLive(_5); + _5 = move _1[4 of 5]; + StorageLive(_6); + StorageLive(_7); + _7 = move _2; + _6 = opaque::(move _7) -> [return: bb3, unwind: bb17]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = move _3; + _8 = opaque::(move _9) -> [return: bb4, unwind: bb16]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = move _4; + _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = move _5; + _12 = opaque::(move _13) -> [return: bb6, unwind: bb14]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb19]; + } + + bb7: { + _0 = const (); + goto -> bb12; + } + + bb8: { + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb20]; + } + + bb9: { + StorageDead(_4); + drop(_3) -> [return: bb10, unwind: bb21]; + } + + bb10: { + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb22]; + } + + bb11: { + StorageDead(_2); + goto -> bb12; + } + + bb12: { + drop(_1) -> [return: bb13, unwind: bb23]; + } + + bb13: { + return; + } + + bb14 (cleanup): { + drop(_13) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_9) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_7) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { + drop(_5) -> [return: bb19, unwind terminate(cleanup)]; + } + + bb19 (cleanup): { + drop(_4) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_3) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_2) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_1) -> [return: bb23, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.rs b/tests/mir-opt/building/match/array_len.rs new file mode 100644 index 0000000000000..0d889ada9b602 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.rs @@ -0,0 +1,31 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Zmir-opt-level=0 + +fn opaque(x: T) {} + +// EMIT_MIR array_len.const_array_len.built.after.mir +fn const_array_len(x: [T; 5]) { + // CHECK-LABEL: fn const_array_len( + // CHECK-NOT: Len + // CHECK-NOT: PtrMetadata + // CHECK: = const 5_usize; + if let [a, b, rest @ .., e] = x { + opaque(a); + opaque(b); + opaque(rest); + opaque(e); + } +} + +// EMIT_MIR array_len.slice_len.built.after.mir +fn slice_len(x: &[T]) { + // CHECK-LABEL: fn slice_len( + // CHECK-NOT: Len + // CHECK: = PtrMetadata(copy _1); + if let [a, b, rest @ .., e] = x { + opaque(a); + opaque(b); + opaque(rest); + opaque(e); + } +} diff --git a/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir new file mode 100644 index 0000000000000..d73f5a1b6f716 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir @@ -0,0 +1,115 @@ +// MIR for `slice_len` after built + +fn slice_len(_1: &[T]) -> () { + debug x => _1; + let mut _0: (); + let mut _2: usize; + let mut _3: usize; + let mut _4: usize; + let mut _5: bool; + let _10: (); + let mut _11: &T; + let _12: (); + let mut _13: &T; + let _14: (); + let mut _15: &[T]; + let _16: (); + let mut _17: &T; + scope 1 { + debug a => _6; + debug b => _7; + debug rest => _8; + debug e => _9; + let _6: &T; + let _7: &T; + let _8: &[T]; + let _9: &T; + } + + bb0: { + PlaceMention(_1); + _3 = PtrMetadata(copy _1); + _2 = move _3; + _4 = const 3_usize; + _5 = Ge(move _2, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb9; + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb1]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_6); + _6 = &(*_1)[0 of 3]; + StorageLive(_7); + _7 = &(*_1)[1 of 3]; + StorageLive(_8); + _8 = &(*_1)[2:-1]; + StorageLive(_9); + _9 = &(*_1)[-1 of 3]; + StorageLive(_10); + StorageLive(_11); + _11 = copy _6; + _10 = opaque::<&T>(move _11) -> [return: bb5, unwind: bb11]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _7; + _12 = opaque::<&T>(move _13) -> [return: bb6, unwind: bb11]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + StorageLive(_14); + StorageLive(_15); + _15 = copy _8; + _14 = opaque::<&[T]>(move _15) -> [return: bb7, unwind: bb11]; + } + + bb7: { + StorageDead(_15); + StorageDead(_14); + StorageLive(_16); + StorageLive(_17); + _17 = copy _9; + _16 = opaque::<&T>(move _17) -> [return: bb8, unwind: bb11]; + } + + bb8: { + StorageDead(_17); + StorageDead(_16); + _0 = const (); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + goto -> bb10; + } + + bb9: { + _0 = const (); + goto -> bb10; + } + + bb10: { + return; + } + + bb11 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir new file mode 100644 index 0000000000000..d73f5a1b6f716 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir @@ -0,0 +1,115 @@ +// MIR for `slice_len` after built + +fn slice_len(_1: &[T]) -> () { + debug x => _1; + let mut _0: (); + let mut _2: usize; + let mut _3: usize; + let mut _4: usize; + let mut _5: bool; + let _10: (); + let mut _11: &T; + let _12: (); + let mut _13: &T; + let _14: (); + let mut _15: &[T]; + let _16: (); + let mut _17: &T; + scope 1 { + debug a => _6; + debug b => _7; + debug rest => _8; + debug e => _9; + let _6: &T; + let _7: &T; + let _8: &[T]; + let _9: &T; + } + + bb0: { + PlaceMention(_1); + _3 = PtrMetadata(copy _1); + _2 = move _3; + _4 = const 3_usize; + _5 = Ge(move _2, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb9; + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb1]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_6); + _6 = &(*_1)[0 of 3]; + StorageLive(_7); + _7 = &(*_1)[1 of 3]; + StorageLive(_8); + _8 = &(*_1)[2:-1]; + StorageLive(_9); + _9 = &(*_1)[-1 of 3]; + StorageLive(_10); + StorageLive(_11); + _11 = copy _6; + _10 = opaque::<&T>(move _11) -> [return: bb5, unwind: bb11]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _7; + _12 = opaque::<&T>(move _13) -> [return: bb6, unwind: bb11]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + StorageLive(_14); + StorageLive(_15); + _15 = copy _8; + _14 = opaque::<&[T]>(move _15) -> [return: bb7, unwind: bb11]; + } + + bb7: { + StorageDead(_15); + StorageDead(_14); + StorageLive(_16); + StorageLive(_17); + _17 = copy _9; + _16 = opaque::<&T>(move _17) -> [return: bb8, unwind: bb11]; + } + + bb8: { + StorageDead(_17); + StorageDead(_16); + _0 = const (); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + goto -> bb10; + } + + bb9: { + _0 = const (); + goto -> bb10; + } + + bb10: { + return; + } + + bb11 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff new file mode 100644 index 0000000000000..e9cb2c5817720 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff @@ -0,0 +1,71 @@ +- // MIR for `normN` before InstSimplify-after-simplifycfg ++ // MIR for `normN` after InstSimplify-after-simplifycfg + + fn normN(_1: [f32; N]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const N); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const N, copy _3) -> [success: bb1, unwind unreachable]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const N); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const N, copy _6) -> [success: bb2, unwind unreachable]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff new file mode 100644 index 0000000000000..0ddc70f2003e8 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff @@ -0,0 +1,71 @@ +- // MIR for `normN` before InstSimplify-after-simplifycfg ++ // MIR for `normN` after InstSimplify-after-simplifycfg + + fn normN(_1: [f32; N]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const N); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const N, copy _3) -> [success: bb1, unwind continue]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const N); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const N, copy _6) -> [success: bb2, unwind continue]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs index 91f43f75689de..1c4d42d3bbe82 100644 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -4,7 +4,16 @@ // EMIT_MIR combine_array_len.norm2.InstSimplify-after-simplifycfg.diff fn norm2(x: [f32; 2]) -> f32 { // CHECK-LABEL: fn norm2( - // CHECK-NOT: Len( + // CHECK-NOT: PtrMetadata( + let a = x[0]; + let b = x[1]; + a * a + b * b +} + +// EMIT_MIR combine_array_len.normN.InstSimplify-after-simplifycfg.diff +fn normN(x: [f32; N]) -> f32 { + // CHECK-LABEL: fn normN( + // CHECK-NOT: PtrMetadata( let a = x[0]; let b = x[1]; a * a + b * b @@ -12,4 +21,5 @@ fn norm2(x: [f32; 2]) -> f32 { fn main() { assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); + assert_eq!(normN([3.0, 4.0]), 5.0 * 5.0); } From 40d879a47f940a8a6219ded487ce1298ecafe271 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 19:08:16 +0000 Subject: [PATCH 0493/1900] Add test. --- compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 2 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 12 +- ....main-{closure#0}.StateTransform.after.mir | 208 ++++++++++++++++++ ....main-{closure#1}.StateTransform.after.mir | 208 ++++++++++++++++++ tests/mir-opt/building/coroutine.rs | 26 +++ ...ny.main-{closure#0}.coroutine_resume.0.mir | 6 +- 7 files changed, 453 insertions(+), 11 deletions(-) create mode 100644 tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir create mode 100644 tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir create mode 100644 tests/mir-opt/building/coroutine.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index da2245b12d2c2..28142382b130b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -471,7 +471,7 @@ impl<'tcx> Body<'tcx> { /// Returns an iterator over all function arguments. #[inline] - pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { + pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator + use<> { (1..self.arg_count + 1).map(Local::new) } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 466b9c7a3c23a..a509c40c89cd6 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -17,7 +17,7 @@ use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty}; rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] - #[debug_format = "_{}"] + #[debug_format = "_s{}"] pub struct CoroutineSavedLocal {} } diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 9bff257e06392..945d6299e4373 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `b::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + _s0: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -18,7 +18,7 @@ }, ignore_for_traits: false, }, - _1: CoroutineSavedTy { + _s1: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -40,12 +40,12 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], - Suspend1 (4): [_1], + Suspend0 (3): [_s0], + Suspend1 (4): [_s1], }, storage_conflicts: BitMatrix(2x2) { - (_0, _0), - (_1, _1), + (_s0, _s0), + (_s1, _s1), }, } */ diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir new file mode 100644 index 0000000000000..349cfa89838de --- /dev/null +++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir @@ -0,0 +1,208 @@ +// MIR for `main::{closure#0}` after StateTransform +/* coroutine_layout = CoroutineLayout { + field_tys: { + _s0: CoroutineSavedTy { + ty: std::string::String, + source_info: SourceInfo { + span: $DIR/coroutine.rs:17:32: 17:35 (#0), + scope: scope[0], + }, + ignore_for_traits: false, + }, + }, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + }, + storage_conflicts: BitMatrix(1x1) { + (_s0, _s0), + }, +} */ + +fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44})) as variant#4).0: std::string::String); + let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; + let mut _16: (); + let _17: std::string::String; + let mut _18: u32; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _26: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + + bb0: { + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _18 = discriminant((*_19)); + switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + } + + bb1: { + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + (((*_20) as variant#4).0: std::string::String) = move _2; + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _6 = &(((*_21) as variant#4).0: std::string::String); + _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_6); + StorageLive(_7); + _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + } + + bb3: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb4; + } + + bb4: { + StorageDead(_5); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); + StorageDead(_3); + StorageDead(_4); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_22)) = 3; + return; + } + + bb5: { + goto -> bb6; + } + + bb6: { + StorageDead(_4); + drop(_3) -> [return: bb7, unwind unreachable]; + } + + bb7: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _13 = &(((*_23) as variant#4).0: std::string::String); + _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + } + + bb8: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + } + + bb9: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_10); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); + StorageDead(_8); + StorageDead(_9); + StorageDead(_11); + StorageDead(_15); + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_24)) = 4; + return; + } + + bb11: { + goto -> bb12; + } + + bb12: { + StorageDead(_9); + drop(_8) -> [return: bb13, unwind unreachable]; + } + + bb13: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); + _16 = const (); + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + } + + bb14: { + goto -> bb16; + } + + bb15: { + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); + _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_26)) = 1; + return; + } + + bb16: { + goto -> bb15; + } + + bb17: { + StorageLive(_3); + StorageLive(_4); + _3 = move _2; + goto -> bb5; + } + + bb18: { + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); + StorageLive(_15); + _8 = move _2; + goto -> bb11; + } + + bb19: { + assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + } + + bb20: { + unreachable; + } +} + +ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second +} + +ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first +} diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir new file mode 100644 index 0000000000000..dd8fb41896361 --- /dev/null +++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir @@ -0,0 +1,208 @@ +// MIR for `main::{closure#1}` after StateTransform +/* coroutine_layout = CoroutineLayout { + field_tys: { + _s0: CoroutineSavedTy { + ty: std::string::String, + source_info: SourceInfo { + span: $DIR/coroutine.rs:22:54: 22:57 (#0), + scope: scope[0], + }, + ignore_for_traits: false, + }, + }, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + }, + storage_conflicts: BitMatrix(1x1) { + (_s0, _s0), + }, +} */ + +fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66})) as variant#4).0: std::string::String); + let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; + let mut _16: (); + let _17: std::string::String; + let mut _18: u32; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _26: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + + bb0: { + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _18 = discriminant((*_19)); + switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + } + + bb1: { + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + (((*_20) as variant#4).0: std::string::String) = move _2; + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _6 = &(((*_21) as variant#4).0: std::string::String); + _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_6); + StorageLive(_7); + _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + } + + bb3: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb4; + } + + bb4: { + StorageDead(_5); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); + StorageDead(_3); + StorageDead(_4); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_22)) = 3; + return; + } + + bb5: { + goto -> bb6; + } + + bb6: { + StorageDead(_4); + drop(_3) -> [return: bb7, unwind unreachable]; + } + + bb7: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _13 = &(((*_23) as variant#4).0: std::string::String); + _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + } + + bb8: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + } + + bb9: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_10); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); + StorageDead(_8); + StorageDead(_9); + StorageDead(_11); + StorageDead(_15); + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_24)) = 4; + return; + } + + bb11: { + goto -> bb12; + } + + bb12: { + StorageDead(_9); + drop(_8) -> [return: bb13, unwind unreachable]; + } + + bb13: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); + _16 = const (); + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + } + + bb14: { + goto -> bb16; + } + + bb15: { + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); + _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_26)) = 1; + return; + } + + bb16: { + goto -> bb15; + } + + bb17: { + StorageLive(_3); + StorageLive(_4); + _3 = move _2; + goto -> bb5; + } + + bb18: { + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); + StorageLive(_15); + _8 = move _2; + goto -> bb11; + } + + bb19: { + assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + } + + bb20: { + unreachable; + } +} + +ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second +} + +ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first +} diff --git a/tests/mir-opt/building/coroutine.rs b/tests/mir-opt/building/coroutine.rs new file mode 100644 index 0000000000000..4b3310f7dd8bf --- /dev/null +++ b/tests/mir-opt/building/coroutine.rs @@ -0,0 +1,26 @@ +// skip-filecheck +//@ edition:2024 +//@ compile-flags: -Zmir-opt-level=0 -C panic=abort + +#![feature(stmt_expr_attributes)] +#![feature(closure_track_caller)] +#![feature(coroutine_trait)] +#![feature(coroutines)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; +use std::panic::Location; + +// EMIT_MIR coroutine.main-{closure#0}.StateTransform.after.mir +// EMIT_MIR coroutine.main-{closure#1}.StateTransform.after.mir +fn main() { + let simple = #[coroutine] |arg: String| { + yield ("first", arg.clone(), Location::caller()); + yield ("second", arg.clone(), Location::caller()); + }; + + let track_caller = #[track_caller] #[coroutine] |arg: String| { + yield ("first", arg.clone(), Location::caller()); + yield ("second", arg.clone(), Location::caller()); + }; +} diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index f8b3f68d21e63..83159b5055acc 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `main::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + _s0: CoroutineSavedTy { ty: HasDrop, source_info: SourceInfo { span: $DIR/coroutine_tiny.rs:22:13: 22:15 (#0), @@ -14,10 +14,10 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], + Suspend0 (3): [_s0], }, storage_conflicts: BitMatrix(1x1) { - (_0, _0), + (_s0, _s0), }, } */ From fe3a784ef23603180efef1501d80b265663fdec2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 15:15:49 +0000 Subject: [PATCH 0494/1900] Do not renumber resume local. --- compiler/rustc_mir_transform/src/coroutine.rs | 47 +++---- ...#0}.coroutine_drop_async.0.panic-abort.mir | 9 +- ...0}.coroutine_drop_async.0.panic-unwind.mir | 9 +- ...await.a-{closure#0}.coroutine_resume.0.mir | 10 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 18 ++- ....main-{closure#0}.StateTransform.after.mir | 59 +++++---- ....main-{closure#1}.StateTransform.after.mir | 59 +++++---- tests/mir-opt/building/coroutine.rs | 9 +- ...losure#0}.coroutine_drop.0.panic-abort.mir | 7 +- ...osure#0}.coroutine_drop.0.panic-unwind.mir | 7 +- ...ny.main-{closure#0}.coroutine_resume.0.mir | 10 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 114 ++++++++-------- ....run2-{closure#0}.Inline.panic-unwind.diff | 122 +++++++++--------- 13 files changed, 228 insertions(+), 252 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4603c695dedd5..08316aaed3b42 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1340,14 +1340,13 @@ fn create_cases<'tcx>( } } - if operation == Operation::Resume { + if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() { // Move the resume argument to the destination place of the `Yield` terminator - let resume_arg = CTX_ARG; statements.push(Statement::new( source_info, StatementKind::Assign(Box::new(( point.resume_arg, - Rvalue::Use(Operand::Move(resume_arg.into())), + Rvalue::Use(Operand::Move(CTX_ARG.into())), ))), )); } @@ -1439,7 +1438,10 @@ fn check_field_tys_sized<'tcx>( } impl<'tcx> crate::MirPass<'tcx> for StateTransform { + #[instrument(level = "debug", skip(self, tcx, body), ret)] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!(def_id = ?body.source.def_id()); + let Some(old_yield_ty) = body.yield_ty() else { // This only applies to coroutines return; @@ -1518,31 +1520,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { cleanup_async_drops(body); } - // We also replace the resume argument and insert an `Assign`. - // This is needed because the resume argument `_2` might be live across a `yield`, in which - // case there is no `Assign` to it that the transform can turn into a store to the coroutine - // state. After the yield the slot in the coroutine state would then be uninitialized. - let resume_local = CTX_ARG; - let resume_ty = body.local_decls[resume_local].ty; - let old_resume_local = replace_local(resume_local, resume_ty, body, tcx); - - // When first entering the coroutine, move the resume argument into its old local - // (which is now a generator interior). - let source_info = SourceInfo::outermost(body.span); - let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements; - stmts.insert( - 0, - Statement::new( - source_info, - StatementKind::Assign(Box::new(( - old_resume_local.into(), - Rvalue::Use(Operand::Move(resume_local.into())), - ))), - ), - ); - let always_live_locals = always_storage_live_locals(body); - let movable = coroutine_kind.movability() == hir::Movability::Movable; let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); @@ -1583,6 +1561,21 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { }; transform.visit_body(body); + // MIR parameters are not explicitly assigned-to when entering the MIR body. + // If we want to save their values inside the coroutine state, we need to do so explicitly. + let source_info = SourceInfo::outermost(body.span); + let args_iter = body.args_iter(); + body.basic_blocks.as_mut()[START_BLOCK].statements.splice( + 0..0, + args_iter.filter_map(|local| { + let (ty, variant_index, idx) = transform.remap[local]?; + let lhs = transform.make_field(variant_index, idx, ty); + let rhs = Rvalue::Use(Operand::Move(local.into())); + let assign = StatementKind::Assign(Box::new((lhs, rhs))); + Some(Statement::new(source_info, assign)) + }), + ); + // Update our MIR struct to reflect the changes we've made body.arg_count = 2; // self, resume arg body.spread_arg = None; diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir index 347e4119cd0e0..050aac7c3ff05 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir @@ -1,7 +1,7 @@ // MIR for `a::{closure#0}` 0 coroutine_drop_async fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _19; + debug _task_context => _2; debug x => ((*(_1.0: &mut {async fn body of a()})).0: T); let mut _0: std::task::Poll<()>; let _3: T; @@ -20,15 +20,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _16: &mut impl std::future::Future; let mut _17: std::pin::Pin<&mut impl std::future::Future>; let mut _18: isize; - let mut _19: &mut std::task::Context<'_>; - let mut _20: u32; + let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); } bb0: { - _20 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _20) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14]; + _19 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _19) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14]; } bb1: { diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir index b1cf5373f9191..796e95ff3d825 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir @@ -1,7 +1,7 @@ // MIR for `a::{closure#0}` 0 coroutine_drop_async fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _19; + debug _task_context => _2; debug x => ((*(_1.0: &mut {async fn body of a()})).0: T); let mut _0: std::task::Poll<()>; let _3: T; @@ -20,15 +20,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _16: &mut impl std::future::Future; let mut _17: std::pin::Pin<&mut impl std::future::Future>; let mut _18: isize; - let mut _19: &mut std::task::Context<'_>; - let mut _20: u32; + let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); } bb0: { - _20 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _20) -> [0: bb12, 2: bb18, 3: bb16, 4: bb17, otherwise: bb19]; + _19 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _19) -> [0: bb12, 2: bb18, 3: bb16, 4: bb17, otherwise: bb19]; } bb1: { diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir index 7480324b17791..2e2876cb3fcf2 100644 --- a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir @@ -10,19 +10,17 @@ } */ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _4; + debug _task_context => _2; let mut _0: std::task::Poll<()>; let mut _3: (); - let mut _4: &mut std::task::Context<'_>; - let mut _5: u32; + let mut _4: u32; bb0: { - _5 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _5) -> [0: bb1, 1: bb4, otherwise: bb5]; + _4 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _4) -> [0: bb1, 1: bb4, otherwise: bb5]; } bb1: { - _4 = move _2; _3 = const (); goto -> bb3; } diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 945d6299e4373..20fc4112ef288 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -50,7 +50,7 @@ } */ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _38; + debug _task_context => _2; let mut _0: std::task::Poll<()>; let _3: (); let mut _4: {async fn body of a()}; @@ -85,8 +85,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> let mut _35: &mut std::task::Context<'_>; let mut _36: (); let mut _37: (); - let mut _38: &mut std::task::Context<'_>; - let mut _39: u32; + let mut _38: u32; scope 1 { debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); let _17: (); @@ -103,12 +102,11 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> } bb0: { - _39 = discriminant((*(_1.0: &mut {async fn body of b()}))); - switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8]; + _38 = discriminant((*(_1.0: &mut {async fn body of b()}))); + switchInt(move _38) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8]; } bb1: { - _38 = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); @@ -143,7 +141,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_13); StorageLive(_14); StorageLive(_15); - _15 = copy _38; + _15 = copy _2; _14 = move _15; goto -> bb6; } @@ -198,7 +196,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb11: { StorageDead(_20); - _38 = move _19; + _2 = move _19; StorageDead(_19); _7 = const (); goto -> bb4; @@ -245,7 +243,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_29); StorageLive(_30); StorageLive(_31); - _31 = copy _38; + _31 = copy _2; _30 = move _31; goto -> bb18; } @@ -295,7 +293,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb22: { StorageDead(_36); - _38 = move _35; + _2 = move _35; StorageDead(_35); _7 = const (); goto -> bb16; diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir index 349cfa89838de..d8fdb446135b3 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:17:32: 17:35 (#0), + span: $DIR/coroutine.rs:18:6: 18:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,8 +22,8 @@ }, } */ -fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44})) as variant#4).0: std::string::String); +fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18})) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); @@ -39,32 +39,31 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ let mut _14: &std::panic::Location<'_>; let _15: &std::panic::Location<'_>; let mut _16: (); - let _17: std::string::String; - let mut _18: u32; - let mut _19: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _20: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _21: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _22: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _23: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _24: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _25: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _26: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _17: u32; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; bb0: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _18 = discriminant((*_19)); - switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _17 = discriminant((*_18)); + switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - (((*_20) as variant#4).0: std::string::String) = move _2; + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _6 = &(((*_21) as variant#4).0: std::string::String); + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -85,8 +84,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_22)) = 3; + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_21)) = 3; return; } @@ -109,8 +108,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _13 = &(((*_23) as variant#4).0: std::string::String); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -136,8 +135,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ StorageDead(_9); StorageDead(_11); StorageDead(_15); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_24)) = 4; + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_23)) = 4; return; } @@ -155,8 +154,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ StorageDead(_11); StorageDead(_8); _16 = const (); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } bb14: { @@ -165,8 +164,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_26)) = 1; + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir index dd8fb41896361..dd17afad656b8 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:22:54: 22:57 (#0), + span: $DIR/coroutine.rs:25:6: 25:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,8 +22,8 @@ }, } */ -fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66})) as variant#4).0: std::string::String); +fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18})) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); @@ -39,32 +39,31 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ let mut _14: &std::panic::Location<'_>; let _15: &std::panic::Location<'_>; let mut _16: (); - let _17: std::string::String; - let mut _18: u32; - let mut _19: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _20: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _21: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _22: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _23: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _24: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _25: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _26: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _17: u32; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; bb0: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _18 = discriminant((*_19)); - switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _17 = discriminant((*_18)); + switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - (((*_20) as variant#4).0: std::string::String) = move _2; + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _6 = &(((*_21) as variant#4).0: std::string::String); + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -85,8 +84,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_22)) = 3; + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_21)) = 3; return; } @@ -109,8 +108,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _13 = &(((*_23) as variant#4).0: std::string::String); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -136,8 +135,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ StorageDead(_9); StorageDead(_11); StorageDead(_15); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_24)) = 4; + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_23)) = 4; return; } @@ -155,8 +154,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ StorageDead(_11); StorageDead(_8); _16 = const (); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } bb14: { @@ -165,8 +164,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_26)) = 1; + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/building/coroutine.rs b/tests/mir-opt/building/coroutine.rs index 4b3310f7dd8bf..6d50c4d90b1a4 100644 --- a/tests/mir-opt/building/coroutine.rs +++ b/tests/mir-opt/building/coroutine.rs @@ -8,18 +8,21 @@ #![feature(coroutines)] use std::ops::{Coroutine, CoroutineState}; -use std::pin::Pin; use std::panic::Location; +use std::pin::Pin; // EMIT_MIR coroutine.main-{closure#0}.StateTransform.after.mir // EMIT_MIR coroutine.main-{closure#1}.StateTransform.after.mir fn main() { - let simple = #[coroutine] |arg: String| { + let simple = #[coroutine] + |arg: String| { yield ("first", arg.clone(), Location::caller()); yield ("second", arg.clone(), Location::caller()); }; - let track_caller = #[track_caller] #[coroutine] |arg: String| { + let track_caller = #[track_caller] + #[coroutine] + |arg: String| { yield ("first", arg.clone(), Location::caller()); yield ("second", arg.clone(), Location::caller()); }; diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir index 7e033916fd348..33fbca7f77ed1 100644 --- a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir +++ b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir @@ -7,15 +7,14 @@ fn main::{closure#0}(_1: *mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 let _4: (); let mut _5: (); let mut _6: (); - let mut _7: (); - let mut _8: u32; + let mut _7: u32; scope 1 { debug _s => (((*_1) as variant#3).0: std::string::String); } bb0: { - _8 = discriminant((*_1)); - switchInt(move _8) -> [0: bb5, 3: bb8, otherwise: bb9]; + _7 = discriminant((*_1)); + switchInt(move _7) -> [0: bb5, 3: bb8, otherwise: bb9]; } bb1: { diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir index 613ef2909b5e4..69e7219af9ff8 100644 --- a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir +++ b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir @@ -7,15 +7,14 @@ fn main::{closure#0}(_1: *mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 let _4: (); let mut _5: (); let mut _6: (); - let mut _7: (); - let mut _8: u32; + let mut _7: u32; scope 1 { debug _s => (((*_1) as variant#3).0: std::string::String); } bb0: { - _8 = discriminant((*_1)); - switchInt(move _8) -> [0: bb7, 3: bb10, otherwise: bb11]; + _7 = discriminant((*_1)); + switchInt(move _7) -> [0: bb7, 3: bb10, otherwise: bb11]; } bb1: { diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index 83159b5055acc..9905cc3e00f99 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -22,7 +22,7 @@ } */ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}>, _2: u8) -> CoroutineState<(), ()> { - debug _x => _10; + debug _x => _2; let mut _0: std::ops::CoroutineState<(), ()>; let _3: HasDrop; let mut _4: !; @@ -31,19 +31,17 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13} let mut _7: (); let _8: (); let mut _9: (); - let _10: u8; - let mut _11: u32; + let mut _10: u32; scope 1 { debug _d => (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop); } bb0: { - _11 = discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}))); - switchInt(move _11) -> [0: bb1, 3: bb5, otherwise: bb6]; + _10 = discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}))); + switchInt(move _10) -> [0: bb1, 3: bb5, otherwise: bb6]; } bb1: { - _10 = move _2; nop; (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop) = HasDrop; StorageLive(_4); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 22e6ea722ddaf..2ae86e2eb8bbd 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -39,8 +39,8 @@ + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); + let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; ++ let mut _31: u32; ++ let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; @@ -48,7 +48,6 @@ + let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _38: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -58,14 +57,14 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _42: (); -+ let mut _43: std::option::Option<()>; -+ let mut _44: &mut std::option::Option<()>; -+ let mut _45: &mut std::future::Ready<()>; -+ let mut _46: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _41: (); ++ let mut _42: std::option::Option<()>; ++ let mut _43: &mut std::option::Option<()>; ++ let mut _44: &mut std::future::Ready<()>; ++ let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _47: &mut &mut std::future::Ready<()>; ++ let mut _46: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -75,22 +74,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _48: std::option::Option<()>; ++ let mut _47: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _49: isize; -+ let mut _50: !; ++ let mut _48: isize; ++ let mut _49: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _41: std::option::Option<()>; ++ let mut _40: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -145,10 +144,9 @@ + StorageLive(_37); + StorageLive(_38); + StorageLive(_39); -+ StorageLive(_40); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; ++ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = discriminant((*_32)); ++ switchInt(move _31) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; } - bb3: { @@ -158,7 +156,6 @@ + } + + bb2: { -+ StorageDead(_40); + StorageDead(_39); + StorageDead(_38); + StorageDead(_37); @@ -186,23 +183,22 @@ } + bb3: { -+ _31 = move _9; ++ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_41); -+ _41 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _41); -+ StorageDead(_41); ++ StorageLive(_40); ++ _40 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _40); ++ StorageDead(_40); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb4; + } + @@ -214,39 +210,39 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _9; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_44); + StorageLive(_45); -+ StorageLive(_46); -+ StorageLive(_50); ++ StorageLive(_49); ++ StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ StorageLive(_44); -+ _46 = &mut _19; ++ _45 = &mut _19; ++ StorageLive(_46); ++ _46 = &mut (_19.0: &mut std::future::Ready<()>); ++ _44 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_46); ++ _43 = &mut ((*_44).0: std::option::Option<()>); + StorageLive(_47); -+ _47 = &mut (_19.0: &mut std::future::Ready<()>); -+ _45 = copy (_19.0: &mut std::future::Ready<()>); ++ _47 = Option::<()>::None; ++ _42 = copy ((*_44).0: std::option::Option<()>); ++ ((*_44).0: std::option::Option<()>) = copy _47; + StorageDead(_47); -+ _44 = &mut ((*_45).0: std::option::Option<()>); ++ StorageDead(_43); + StorageLive(_48); -+ _48 = Option::<()>::None; -+ _43 = copy ((*_45).0: std::option::Option<()>); -+ ((*_45).0: std::option::Option<()>) = copy _48; -+ StorageDead(_48); -+ StorageDead(_44); -+ StorageLive(_49); -+ _49 = discriminant(_43); -+ switchInt(move _49) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _48 = discriminant(_42); ++ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -266,8 +262,8 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_37)) = 3; + goto -> bb2; + } + @@ -281,14 +277,14 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; + } + + bb8: { + _7 = Poll::<()>::Ready(move _30); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 1; + goto -> bb2; + } + @@ -298,7 +294,7 @@ + StorageLive(_29); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _9 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb4; @@ -309,18 +305,18 @@ + } + + bb11: { -+ _50 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { -+ _42 = move ((_43 as Some).0: ()); -+ StorageDead(_49); -+ StorageDead(_43); -+ _18 = Poll::<()>::Ready(move _42); ++ _41 = move ((_42 as Some).0: ()); ++ StorageDead(_48); + StorageDead(_42); -+ StorageDead(_50); -+ StorageDead(_46); ++ _18 = Poll::<()>::Ready(move _41); ++ StorageDead(_41); ++ StorageDead(_49); + StorageDead(_45); ++ StorageDead(_44); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 8b027e988b8e8..d7ae931aaae58 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -39,8 +39,8 @@ + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); + let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; ++ let mut _31: u32; ++ let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; @@ -50,7 +50,6 @@ + let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _42: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -60,14 +59,14 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _44: (); -+ let mut _45: std::option::Option<()>; -+ let mut _46: &mut std::option::Option<()>; -+ let mut _47: &mut std::future::Ready<()>; -+ let mut _48: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _43: (); ++ let mut _44: std::option::Option<()>; ++ let mut _45: &mut std::option::Option<()>; ++ let mut _46: &mut std::future::Ready<()>; ++ let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _49: &mut &mut std::future::Ready<()>; ++ let mut _48: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -77,22 +76,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _50: std::option::Option<()>; ++ let mut _49: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _51: isize; -+ let mut _52: !; ++ let mut _50: isize; ++ let mut _51: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _43: std::option::Option<()>; ++ let mut _42: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -149,10 +148,9 @@ + StorageLive(_39); + StorageLive(_40); + StorageLive(_41); -+ StorageLive(_42); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; ++ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = discriminant((*_32)); ++ switchInt(move _31) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; } - bb3: { @@ -170,7 +168,6 @@ + } + + bb4: { -+ StorageDead(_42); + StorageDead(_41); + StorageDead(_40); + StorageDead(_39); @@ -203,23 +200,22 @@ - StorageDead(_2); - return; + bb5: { -+ _31 = move _9; ++ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_43); -+ _43 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _43); -+ StorageDead(_43); ++ StorageLive(_42); ++ _42 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _42); ++ StorageDead(_42); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb6; } @@ -231,39 +227,39 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _9; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_46); + StorageLive(_47); -+ StorageLive(_48); -+ StorageLive(_52); ++ StorageLive(_51); ++ StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ StorageLive(_46); -+ _48 = &mut _19; ++ _47 = &mut _19; ++ StorageLive(_48); ++ _48 = &mut (_19.0: &mut std::future::Ready<()>); ++ _46 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_48); ++ _45 = &mut ((*_46).0: std::option::Option<()>); + StorageLive(_49); -+ _49 = &mut (_19.0: &mut std::future::Ready<()>); -+ _47 = copy (_19.0: &mut std::future::Ready<()>); ++ _49 = Option::<()>::None; ++ _44 = copy ((*_46).0: std::option::Option<()>); ++ ((*_46).0: std::option::Option<()>) = copy _49; + StorageDead(_49); -+ _46 = &mut ((*_47).0: std::option::Option<()>); ++ StorageDead(_45); + StorageLive(_50); -+ _50 = Option::<()>::None; -+ _45 = copy ((*_47).0: std::option::Option<()>); -+ ((*_47).0: std::option::Option<()>) = copy _50; -+ StorageDead(_50); -+ StorageDead(_46); -+ StorageLive(_51); -+ _51 = discriminant(_45); -+ switchInt(move _51) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _50 = discriminant(_44); ++ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -285,8 +281,8 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_37)) = 3; + goto -> bb4; + } + @@ -300,14 +296,14 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; + } + + bb10: { + _7 = Poll::<()>::Ready(move _30); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 1; + goto -> bb4; + } + @@ -319,13 +315,13 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; ++ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { -+ _42 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_42)) = 2; ++ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_41)) = 2; + goto -> bb2; + } + @@ -335,7 +331,7 @@ + StorageLive(_29); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _9 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb6; @@ -350,18 +346,18 @@ + } + + bb16: { -+ _52 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { -+ _44 = move ((_45 as Some).0: ()); -+ StorageDead(_51); -+ StorageDead(_45); -+ _18 = Poll::<()>::Ready(move _44); ++ _43 = move ((_44 as Some).0: ()); ++ StorageDead(_50); + StorageDead(_44); -+ StorageDead(_52); -+ StorageDead(_48); ++ _18 = Poll::<()>::Ready(move _43); ++ StorageDead(_43); ++ StorageDead(_51); + StorageDead(_47); ++ StorageDead(_46); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); From eddd755f284093b0d3a8ce027f68653d5f5773d1 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 22:58:54 +0000 Subject: [PATCH 0495/1900] Bless ui. --- .../ui/rustc_public-ir-print/async-closure.rs | 2 +- .../async-closure.stdout | 44 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/tests/ui/rustc_public-ir-print/async-closure.rs b/tests/ui/rustc_public-ir-print/async-closure.rs index 80f96e09cfc78..bd8c7e888a37c 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.rs +++ b/tests/ui/rustc_public-ir-print/async-closure.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Z unpretty=stable-mir --crate-type lib -C panic=abort -Zmir-opt-level=0 //@ check-pass -//@ only-x86_64 +//@ only-64bit //@ edition: 2024 //@ needs-unwind unwind edges are different with panic=abort diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout index 73e9b8fc097ab..5113dc5048b93 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.stdout +++ b/tests/ui/rustc_public-ir-print/async-closure.stdout @@ -40,30 +40,28 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo let _3: i32; let mut _4: &i32; let mut _5: (); - let mut _6: &mut Context<'_>; - let mut _7: u32; + let mut _6: u32; + let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - debug _task_context => _6; + debug _task_context => _2; debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _7 = discriminant((*_8)); - switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; + _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _6 = discriminant((*_7)); + switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { - _6 = move _2; StorageLive(_3); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_9).0: &i32)); + _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _4 = CopyForDeref(((*_8).0: &i32)); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10)) = 1; + _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + discriminant((*_9)) = 1; return; } bb2: { @@ -78,30 +76,28 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c let _3: i32; let mut _4: &i32; let mut _5: (); - let mut _6: &mut Context<'_>; - let mut _7: u32; + let mut _6: u32; + let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - debug _task_context => _6; + debug _task_context => _2; debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _7 = discriminant((*_8)); - switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; + _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _6 = discriminant((*_7)); + switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { - _6 = move _2; StorageLive(_3); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_9).0: &i32)); + _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _4 = CopyForDeref(((*_8).0: &i32)); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10)) = 1; + _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + discriminant((*_9)) = 1; return; } bb2: { From 541989611144cdf5f2b5d7340b4f579ec004c799 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 17:32:37 +1000 Subject: [PATCH 0496/1900] Use `LLVMDIBuilderCreateSubroutineType` --- .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 14 +++++++++++--- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 13 ++++++++----- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 7 ------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc3a84b6a151a..db821b3dee716 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -318,7 +318,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); - let fn_di_node = create_subroutine_type(cx, create_DIArray(DIB(cx), &signature_di_nodes[..])); + let fn_di_node = create_subroutine_type(cx, &signature_di_nodes[..]); // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); @@ -346,9 +346,17 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( pub(super) fn create_subroutine_type<'ll>( cx: &CodegenCx<'ll, '_>, - signature: &'ll DICompositeType, + signature: &[Option<&'ll llvm::Metadata>], ) -> &'ll DICompositeType { - unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), signature) } + unsafe { + llvm::LLVMDIBuilderCreateSubroutineType( + DIB(cx), + None, // ("File" is ignored and has no effect) + signature.as_ptr(), + signature.len() as c_uint, + DIFlags::FlagZero, // (default value) + ) + } } /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 79334f7f9fe84..126082aa3aa68 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -349,7 +349,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let file_metadata = file_metadata(self, &loc.file); let function_type_metadata = - create_subroutine_type(self, get_function_signature(self, fn_abi)); + create_subroutine_type(self, &get_function_signature(self, fn_abi)); let mut name = String::with_capacity(64); type_names::push_item_name(tcx, def_id, false, &mut name); @@ -441,9 +441,9 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - ) -> &'ll DIArray { + ) -> Vec> { if cx.sess().opts.debuginfo != DebugInfo::Full { - return create_DIArray(DIB(cx), &[]); + return vec![]; } let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); @@ -484,7 +484,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty)))); } - create_DIArray(DIB(cx), &signature[..]) + signature } fn get_template_parameters<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f08..af0ec6a33b0d2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1870,6 +1870,14 @@ unsafe extern "C" { Scope: &'ll Metadata, InlinedAt: Option<&'ll Metadata>, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateSubroutineType<'ll>( + Builder: &DIBuilder<'ll>, + File: Option<&'ll Metadata>, // (ignored and has no effect) + ParameterTypes: *const Option<&'ll Metadata>, + NumParameterTypes: c_uint, + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2172,11 +2180,6 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( - Builder: &DIBuilder<'a>, - ParameterTypes: &'a DIArray, - ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 361a5f765510f..a1c3cce99202d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1026,13 +1026,6 @@ LLVMRustDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, CSInfo, oSource)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder, - LLVMMetadataRef ParameterTypes) { - return wrap(unwrap(Builder)->createSubroutineType( - DITypeRefArray(unwrap(ParameterTypes)))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 2552deb9cd84b3bebaec98902d9d42bee2315822 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:21:46 +1000 Subject: [PATCH 0497/1900] Use `LLVMDIBuilderCreateUnionType` --- .../src/debuginfo/metadata/type_map.rs | 13 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 33 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 13 -------- 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 18a783a348a45..af72d4ea3de0a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; +use libc::c_uint; use rustc_abi::{Align, Size, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -191,6 +192,7 @@ pub(super) fn stub<'ll, 'tcx>( containing_scope: Option<&'ll DIScope>, flags: DIFlags, ) -> StubInfo<'ll, 'tcx> { + let no_elements: &[Option<&llvm::Metadata>] = &[]; let empty_array = create_DIArray(DIB(cx), &[]); let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); @@ -227,19 +229,20 @@ pub(super) fn stub<'ll, 'tcx>( } } Stub::Union => unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( + llvm::LLVMDIBuilderCreateUnionType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, size.bits(), align.bits() as u32, flags, - Some(empty_array), - 0, - unique_type_id_str.as_c_char_ptr(), + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) }, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index af0ec6a33b0d2..58aebe4c391ec 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1878,6 +1878,23 @@ unsafe extern "C" { NumParameterTypes: c_uint, Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateUnionType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2391,22 +2408,6 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - Elements: Option<&'a DIArray>, - RunTimeLang: c_uint, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a1c3cce99202d..2cbfcf79c83d2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1268,19 +1268,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( /* RunTimeLang */ 0, "", IsScoped)); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId, - size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createUnionType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), DINodeArray(unwrapDI(Elements)), RunTimeLang, - StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef Ty) { From bef8f646a632215c362ee45d88d2390e47e65d4c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:37:23 +1000 Subject: [PATCH 0498/1900] Use `LLVMDIBuilderCreateArrayType` --- .../src/debuginfo/metadata.rs | 14 ++++++-------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 +++++++++-------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index db821b3dee716..fcbfe6ca2242f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -32,9 +32,7 @@ use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId}; use super::CodegenUnitDebugContext; use super::namespace::mangled_name_of_instance; use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name}; -use super::utils::{ - DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, -}; +use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; @@ -119,17 +117,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( .try_to_target_usize(cx.tcx) .expect("expected monomorphic const in codegen") as c_longlong; - let subrange = - unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; + let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; + let subscripts = &[subrange]; - let subscripts = create_DIArray(DIB(cx), &[subrange]); let di_node = unsafe { - llvm::LLVMRustDIBuilderCreateArrayType( + llvm::LLVMDIBuilderCreateArrayType( DIB(cx), size.bits(), align.bits() as u32, element_type_di_node, - subscripts, + subscripts.as_ptr(), + subscripts.len() as c_uint, ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 58aebe4c391ec..e2426980c6ad0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1895,6 +1895,15 @@ unsafe extern "C" { UniqueId: *const c_uchar, // See "PTR_LEN_STR". UniqueIdLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateArrayType<'ll>( + Builder: &DIBuilder<'ll>, + Size: u64, + Align: u32, + Ty: &'ll Metadata, + Subscripts: *const &'ll Metadata, + NumSubscripts: c_uint, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2355,14 +2364,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( - Builder: &DIBuilder<'a>, - Size: u64, - AlignInBits: u32, - Ty: &'a DIType, - Subscripts: &'a DIArray, - ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( Builder: &DIBuilder<'a>, Lo: i64, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2cbfcf79c83d2..d5be7aeb236ef 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1211,15 +1211,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size, - uint32_t AlignInBits, LLVMMetadataRef Ty, - LLVMMetadataRef Subscripts) { - return wrap(unwrap(Builder)->createArrayType( - Size, AlignInBits, unwrapDI(Ty), - DINodeArray(unwrapDI(Subscripts)))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, int64_t Count) { From 3e9048d9a47bb9db2b95adda85cd544a30bfcb8a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:46:45 +1000 Subject: [PATCH 0499/1900] Use `LLVMDIBuilderCreateBasicType` --- .../src/debuginfo/metadata.rs | 5 +++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 +++++++++-------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 -------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index fcbfe6ca2242f..4c210b46f7247 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -840,12 +840,13 @@ fn create_basic_type<'ll, 'tcx>( encoding: u32, ) -> &'ll DIBasicType { unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( + llvm::LLVMDIBuilderCreateBasicType( DIB(cx), - name.as_c_char_ptr(), + name.as_ptr(), name.len(), size.bits(), encoding, + DIFlags::FlagZero, ) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e2426980c6ad0..63fdc079bae4f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1904,6 +1904,15 @@ unsafe extern "C" { Subscripts: *const &'ll Metadata, NumSubscripts: c_uint, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateBasicType<'ll>( + Builder: &DIBuilder<'ll>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + SizeInBits: u64, + Encoding: c_uint, // (`LLVMDWARFTypeEncoding`) + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2239,14 +2248,6 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( - Builder: &DIBuilder<'a>, - Name: *const c_char, - NameLen: size_t, - SizeInBits: u64, - Encoding: c_uint, - ) -> &'a DIBasicType; - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( Builder: &DIBuilder<'a>, Type: &'a DIBasicType, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index d5be7aeb236ef..80d3db3b99d97 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1064,14 +1064,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name, - size_t NameLen, uint64_t SizeInBits, - unsigned Encoding) { - return wrap(unwrap(Builder)->createBasicType(StringRef(Name, NameLen), - SizeInBits, Encoding)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, From bae6fde27057c4b8fe216eb39a4ba6867aeeb8fb Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 20:09:34 +1000 Subject: [PATCH 0500/1900] Use `LLVMDIBuilderCreatePointerType` --- .../src/debuginfo/metadata.rs | 68 ++++++++++--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 20 +++--- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 4c210b46f7247..0903ddab28549 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -173,17 +173,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( "ptr_type={ptr_type}, pointee_type={pointee_type}", ); - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - pointer_size.bits(), - pointer_align.abi.bits() as u32, - 0, // Ignore DWARF address space. - ptr_type_debuginfo_name.as_c_char_ptr(), - ptr_type_debuginfo_name.len(), - ) - }; + let di_node = create_pointer_type( + cx, + pointee_type_di_node, + pointer_size, + pointer_align.abi, + &ptr_type_debuginfo_name, + ); DINodeCreationResult { di_node, already_stored_in_typemap: false } } @@ -231,17 +227,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( // The data pointer type is a regular, thin pointer, regardless of whether this // is a slice or a trait object. - let data_ptr_type_di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - addr_field.size.bits(), - addr_field.align.abi.bits() as u32, - 0, // Ignore DWARF address space. - std::ptr::null(), - 0, - ) - }; + let data_ptr_type_di_node = create_pointer_type( + cx, + pointee_type_di_node, + addr_field.size, + addr_field.align.abi, + "", + ); smallvec![ build_field_di_node( @@ -327,17 +319,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( } _ => unreachable!(), }; - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - fn_di_node, - size.bits(), - align.bits() as u32, - 0, // Ignore DWARF address space. - name.as_c_char_ptr(), - name.len(), - ) - }; + let di_node = create_pointer_type(cx, fn_di_node, size, align, &name); DINodeCreationResult::new(di_node, false) } @@ -357,6 +339,26 @@ pub(super) fn create_subroutine_type<'ll>( } } +fn create_pointer_type<'ll>( + cx: &CodegenCx<'ll, '_>, + pointee_ty: &'ll llvm::Metadata, + size: Size, + align: Align, + name: &str, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreatePointerType( + DIB(cx), + pointee_ty, + size.bits(), + align.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr(), + name.len(), + ) + } +} + /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs /// we with the correct type name (e.g. "dyn SomeTrait + Sync"). fn build_dyn_type_di_node<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 63fdc079bae4f..bd26590e7d36d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1913,6 +1913,16 @@ unsafe extern "C" { Encoding: c_uint, // (`LLVMDWARFTypeEncoding`) Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreatePointerType<'ll>( + Builder: &DIBuilder<'ll>, + PointeeTy: &'ll Metadata, + SizeInBits: u64, + AlignInBits: u32, + AddressSpace: c_uint, // (optional DWARF address space; default is 0) + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2258,16 +2268,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( - Builder: &DIBuilder<'a>, - PointeeTy: &'a DIType, - SizeInBits: u64, - AlignInBits: u32, - AddressSpace: c_uint, - Name: *const c_char, - NameLen: size_t, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIDescriptor>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 80d3db3b99d97..e6be7e2961b41 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1074,15 +1074,6 @@ LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, LineNo, unwrapDIPtr(Scope))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( - LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits, - uint32_t AlignInBits, unsigned AddressSpace, const char *Name, - size_t NameLen) { - return wrap(unwrap(Builder)->createPointerType( - unwrapDI(PointeeTy), SizeInBits, AlignInBits, AddressSpace, - StringRef(Name, NameLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From af88d14cac15dd47452ce670e65c1ed56dae9586 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 20:29:56 +1000 Subject: [PATCH 0501/1900] Use `LLVMDIBuilderCreateStructType` --- .../src/debuginfo/metadata/type_map.rs | 14 +++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 43 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 14 ------ 3 files changed, 29 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index af72d4ea3de0a..37200fdc41afa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt}; use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::utils::{DIB, create_DIArray, debug_context}; use crate::llvm::debuginfo::{DIFlags, DIScope, DIType}; use crate::llvm::{self}; @@ -193,7 +193,6 @@ pub(super) fn stub<'ll, 'tcx>( flags: DIFlags, ) -> StubInfo<'ll, 'tcx> { let no_elements: &[Option<&llvm::Metadata>] = &[]; - let empty_array = create_DIArray(DIB(cx), &[]); let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); let (file_metadata, line_number) = if let Some(def_location) = def_location { @@ -209,10 +208,10 @@ pub(super) fn stub<'ll, 'tcx>( _ => None, }; unsafe { - llvm::LLVMRustDIBuilderCreateStructType( + llvm::LLVMDIBuilderCreateStructType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, @@ -220,10 +219,11 @@ pub(super) fn stub<'ll, 'tcx>( align.bits() as u32, flags, None, - empty_array, - 0, + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) vtable_holder, - unique_type_id_str.as_c_char_ptr(), + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bd26590e7d36d..f88eaebc70061 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -24,9 +24,9 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ - DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, - DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, + DIArray, DIBasicType, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, + DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, + DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -1923,6 +1923,25 @@ unsafe extern "C" { Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStructType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + DerivedFrom: Option<&'ll Metadata>, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + VTableHolder: Option<&'ll Metadata>, + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2268,24 +2287,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIDescriptor>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - DerivedFrom: Option<&'a DIType>, - Elements: &'a DIArray, - RunTimeLang: c_uint, - VTableHolder: Option<&'a DIType>, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e6be7e2961b41..b4ca641674f08 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1074,20 +1074,6 @@ LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, LineNo, unwrapDIPtr(Scope))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang, - LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createStructType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), unwrapDI(DerivedFrom), - DINodeArray(unwrapDI(Elements)), RunTimeLang, - unwrapDI(VTableHolder), StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From 26f3337d4eda0ba22b615744fda0185d0ee344b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 04:16:47 +0200 Subject: [PATCH 0502/1900] Remove `DynKind` --- .../src/diagnostics/explain_borrow.rs | 4 +-- compiler/rustc_borrowck/src/lib.rs | 4 +-- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++--- .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- .../rustc_codegen_cranelift/src/unsize.rs | 4 +-- .../src/value_and_place.rs | 3 +-- compiler/rustc_codegen_ssa/src/base.rs | 6 ++--- compiler/rustc_codegen_ssa/src/meth.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../rustc_const_eval/src/interpret/call.rs | 6 ++--- .../rustc_const_eval/src/interpret/cast.rs | 4 +-- .../src/interpret/eval_context.rs | 2 +- .../src/interpret/intrinsics.rs | 2 +- .../rustc_const_eval/src/interpret/stack.rs | 2 +- .../rustc_const_eval/src/interpret/traits.rs | 2 +- .../src/interpret/validity.rs | 2 +- .../rustc_const_eval/src/interpret/visitor.rs | 2 +- .../rustc_const_eval/src/util/type_name.rs | 2 +- .../rustc_hir_analysis/src/collect/dump.rs | 2 +- .../src/hir_ty_lowering/dyn_compatibility.rs | 5 ++-- .../src/hir_ty_lowering/errors.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 7 +---- .../src/variance/constraints.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 8 ++---- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- .../src/deref_into_dyn_supertrait.rs | 4 +-- compiler/rustc_lint/src/types.rs | 2 +- compiler/rustc_lint/src/unused.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/layout.rs | 6 ++--- compiler/rustc_middle/src/ty/print/pretty.rs | 6 ++--- .../src/ty/significant_drop_order.rs | 2 +- .../rustc_middle/src/ty/structural_impls.rs | 14 +++++----- compiler/rustc_middle/src/ty/sty.rs | 14 +++++----- .../src/move_paths/builder.rs | 4 +-- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- .../src/canonicalizer.rs | 2 +- .../src/solve/assembly/mod.rs | 2 +- .../src/solve/assembly/structural_traits.rs | 6 ++--- .../src/solve/normalizes_to/mod.rs | 4 +-- .../src/solve/trait_goals.rs | 12 ++++----- compiler/rustc_passes/src/check_export.rs | 2 +- compiler/rustc_pattern_analysis/src/rustc.rs | 2 +- compiler/rustc_public/src/ty.rs | 11 +++----- .../src/unstable/convert/internal.rs | 19 ++----------- .../src/unstable/convert/stable/ty.rs | 27 +++++-------------- compiler/rustc_public/src/visitor.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 6 ++--- .../cfi/typeid/itanium_cxx_abi/transform.rs | 8 +++--- compiler/rustc_symbol_mangling/src/export.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 6 ++--- .../nice_region_error/static_impl_trait.rs | 2 +- .../error_reporting/infer/note_and_explain.rs | 12 +++------ .../src/error_reporting/infer/region.rs | 2 +- .../src/error_reporting/traits/mod.rs | 2 +- .../traits/on_unimplemented.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 6 ++--- .../src/traits/select/candidate_assembly.rs | 8 +++--- .../src/traits/select/confirmation.rs | 14 +++++----- .../src/traits/vtable.rs | 6 ++--- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 2 +- compiler/rustc_ty_utils/src/ty.rs | 4 +-- compiler/rustc_type_ir/src/flags.rs | 2 +- compiler/rustc_type_ir/src/inherent.rs | 9 ++----- compiler/rustc_type_ir/src/lib.rs | 1 - compiler/rustc_type_ir/src/outlives.rs | 2 +- compiler/rustc_type_ir/src/relate.rs | 15 ++++------- compiler/rustc_type_ir/src/ty_kind.rs | 22 +++------------ compiler/rustc_type_ir/src/walk.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 4 +-- 74 files changed, 142 insertions(+), 237 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index fda96dde8269d..7ca07bb9b4348 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -438,7 +438,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let elaborated_args = std::iter::zip(*args, &generics.own_params).map(|(arg, param)| { - if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) { + if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) { let default = tcx.object_lifetime_default(param.def_id); let re_static = tcx.lifetimes.re_static; @@ -464,7 +464,7 @@ impl<'tcx> BorrowExplanation<'tcx> { has_dyn = true; - Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into() + Ty::new_dynamic(tcx, obj, implied_region).into() } else { arg } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5d2dda8b0e7cc..5b20c5536c390 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1904,7 +1904,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -1950,7 +1950,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9e7..8e6150fe9da2c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1487,9 +1487,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { unsize_to: None, }, ); - } else if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = + } else if let ty::Dynamic(src_tty, _src_lt) = *self.struct_tail(src.ty, location).kind() - && let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = + && let ty::Dynamic(dst_tty, dst_lt) = *self.struct_tail(dst.ty, location).kind() && src_tty.principal().is_some() && dst_tty.principal().is_some() @@ -1511,7 +1511,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME: Once we disallow casting `*const dyn Trait + 'short` // to `*const dyn Trait + 'long`, then this can just be `src_lt`. dst_lt, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -1519,7 +1518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { &dst_tty.without_auto_traits().collect::>(), ), dst_lt, - ty::Dyn, ); debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7d0731c77bdc4..29ee46194de19 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -715,7 +715,7 @@ pub(crate) fn codegen_drop<'tcx>( fx.bcx.ins().jump(ret_block, &[]); } else { match ty.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists ( *mut T, Vtable ) diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2aee0b2e97424..643c7feb89a26 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -30,9 +30,7 @@ pub(crate) fn unsized_info<'tcx>( fx.pointer_type, len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64, ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 9d73f200afe2b..4519fa1a270e4 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -909,8 +909,7 @@ pub(crate) fn assert_assignable<'tcx>( ); // fn(&T) -> for<'l> fn(&'l T) is allowed } - (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { - // FIXME(dyn-star): Do the right thing with DynKinds + (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { for (from, to) in from_traits.iter().zip(to_traits) { let from = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), from); let to = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), to); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 45b028aa8eff2..68a2f43ec67bc 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -168,9 +168,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize( len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"), ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); @@ -208,7 +206,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( old_info } } - (_, ty::Dynamic(data, _, _)) => meth::get_vtable( + (_, ty::Dynamic(data, _)) => meth::get_vtable( cx, source, data.principal() diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 34ad35a729b98..2fa466b500179 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -78,7 +78,7 @@ fn dyn_trait_in_self<'tcx>( ) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.kind() - && let ty::Dynamic(data, _, _) = ty.kind() + && let ty::Dynamic(data, _) = ty.kind() { // FIXME(arbitrary_self_types): This is likely broken for receivers which // have a "non-self" trait objects as a generic argument. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6492ef73956b0..1b218a0d33956 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (maybe_null, drop_fn, fn_abi, drop_instance) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists ( *mut T, Vtable ) diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4cb88d44e1b11..23e4a2921ea6e 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -658,7 +658,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = self.read_immediate(&receiver)?; break self.ref_to_mplace(&val)?; } - ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values + ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values _ => { // Not there yet, search for the only non-ZST field. // (The rules for `DispatchFromDyn` ensure there's exactly one such field.) @@ -675,7 +675,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // (For that reason we also cannot use `unpack_dyn_trait`.) let receiver_tail = self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env); - let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { + let ty::Dynamic(receiver_trait, _) = receiver_tail.kind() else { span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) }; assert!(receiver_place.layout.is_unsized()); @@ -822,7 +822,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call` // since we can just get a place of the underlying type and use `mplace_to_ref`. let place = match place.layout.ty.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // Dropping a trait object. Need to find actual drop fn. self.unpack_dyn_trait(&place, data)? } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index e3afeda5b7c97..0075740e03178 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); self.write_immediate(val, dest) } - (ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => { + (ty::Dynamic(data_a, _), ty::Dynamic(data_b, _)) => { let val = self.read_immediate(src)?; // MIR building generates odd NOP casts, prevent them from causing unexpected trouble. // See . @@ -436,7 +436,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } - (_, &ty::Dynamic(data, _, ty::Dyn)) => { + (_, &ty::Dynamic(data, _)) => { // Initial cast from sized to dyn trait let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 91ed71ac3e54c..0e4a98f0941ac 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -469,7 +469,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } interp_ok(Some((full_size, full_align))) } - ty::Dynamic(expected_trait, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5e3d0a15d8bc1..418dd658121db 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -181,7 +181,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 7cabfd961212f..1c1c59da9d886 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -509,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 870f9a396ae2b..d982ed9616742 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -109,7 +109,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { assert!( - matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _)), "`unpack_dyn_trait` only makes sense on `dyn*` types" ); let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 02e3d90f4af70..9adc3fa463180 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -449,7 +449,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env); match tail.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer for the right trait. try_validation!( diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 82c50fac6c0ee..b5de10c7dcd11 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -90,7 +90,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // 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(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // 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 diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 5bcf96abd8cb5..db651811551f3 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -41,7 +41,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::FnPtr(..) | ty::Never | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::UnsafeBinder(_) => self.pretty_print_type(ty), // Placeholders (all printed as `_` to uniformize them). diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index c3f965d845693..44cc2dec1cb52 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -152,7 +152,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { ); continue; }; - let ty::Dynamic(data, _, _) = *ty.kind() else { + let ty::Dynamic(data, _) = *ty.kind() else { tcx.dcx() .span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type"); continue; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 76bb59e3f0901..c823a4a0430f4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -6,7 +6,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ - self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_span::{ErrorGuaranteed, Span}; @@ -28,7 +28,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, - representation: DynKind, ) -> Ty<'tcx> { let tcx = self.tcx(); let dummy_self = tcx.types.trait_object_dummy_self; @@ -431,7 +430,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; debug!(?region_bound); - Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) + Ty::new_dynamic(tcx, existential_predicates, region_bound) } /// Check that elaborating the principal of a trait ref doesn't lead to projections diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 0cf9cb7193f72..165051744641f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -859,7 +859,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c5e079fe89ac4..b64129dc54e67 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -23,7 +23,6 @@ mod lint; use std::assert_matches::assert_matches; use std::slice; -use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2428,7 +2427,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - let repr = tagged_ptr.tag(); if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing @@ -2436,10 +2434,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // keyword like `impl` Ty::new_error(tcx, guar) } else { - let repr = match repr { - TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, - }; - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime) } } // If we encounter a fully qualified path with RTN generics, then it must have diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 960ec7f66ab15..be841675821c1 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -281,7 +281,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, data.def_id, data.args, variance); } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 27540fd1a4382..40b21c45bc564 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), + ty::Dynamic(tty, _) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -250,9 +250,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - Err(check.report_cast_to_unsized_type(fcx)) - } + ty::Dynamic(_, _) | ty::Slice(..) => Err(check.report_cast_to_unsized_type(fcx)), _ => Ok(check), } } @@ -900,7 +898,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &src_tty.without_auto_traits().collect::>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -908,7 +905,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &dst_tty.without_auto_traits().collect::>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index b99f811db1a14..ced2cf2b57b14 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -558,7 +558,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c8f6c06b720dc..a39ac0fcb6e3e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1008,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index e1c51ff829907..703f757abd50c 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -66,11 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && tcx.is_lang_item(did, LangItem::Deref) // the self type is `dyn t_principal` && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() - && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() + && let ty::Dynamic(data, _) = self_ty.kind() && let Some(self_principal) = data.principal() // `::Target` is `dyn target_principal` && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target) - && let ty::Dynamic(data, _, ty::Dyn) = target.kind() + && let ty::Dynamic(data, _) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty)) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 88527fa2e6e78..eaec0c9857d28 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -320,7 +320,7 @@ fn lint_wide_pointer<'tcx>( }; (!ty.is_sized(cx.tcx, cx.typing_env())) - .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))) + .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _)))) }; // the left and right operands can have references, remove any explicit references diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 22d89d2461273..95c5bea6be898 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -312,7 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { }) .map(|inner| MustUsePath::Opaque(Box::new(inner))) } - ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { + ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 79700d485c440..7d3e2c9965dad 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -574,7 +574,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa43..9524057eebc6c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -812,7 +812,7 @@ where | ty::CoroutineWitness(..) | ty::Foreign(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, ty::Dyn) => { + | ty::Dynamic(_, _) => { bug!("TyAndLayout::field({:?}): not applicable", this) } @@ -878,7 +878,7 @@ where // `std::mem::uninitialized::<&dyn Trait>()`, for example. if let ty::Adt(def, args) = metadata.kind() && tcx.is_lang_item(def.did(), LangItem::DynMetadata) - && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind() + && let ty::Dynamic(data, _) = args.type_at(0).kind() { mk_dyn_vtable(data.principal()) } else { @@ -887,7 +887,7 @@ where } else { match tcx.struct_tail_for_codegen(pointee, cx.typing_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), + ty::Dynamic(data, _) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), } }; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index fc821ffdaa68b..1b7ef8de8454a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -784,14 +784,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, }, ty::Adt(def, args) => self.print_def_path(def.did(), args)?, - ty::Dynamic(data, r, repr) => { + ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); if print_r { write!(self, "(")?; } - match repr { - ty::Dyn => write!(self, "dyn ")?, - } + write!(self, "dyn ")?; data.print(self)?; if print_r { write!(self, " + ")?; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index 5ada9ecc80cf3..f1aa7076d98ac 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -132,7 +132,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Ref(_, _, _) | ty::FnPtr(_, _) | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 89ef46b1ae571..11d109b463d90 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -390,11 +390,9 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), - ty::Dynamic(trait_ty, region, representation) => ty::Dynamic( - trait_ty.try_fold_with(folder)?, - region.try_fold_with(folder)?, - representation, - ), + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) + } ty::Tuple(ts) => ty::Tuple(ts.try_fold_with(folder)?), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.try_fold_with(folder)?), ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.try_fold_with(folder)?, hdr), @@ -437,8 +435,8 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), ty::Adt(tid, args) => ty::Adt(tid, args.fold_with(folder)), - ty::Dynamic(trait_ty, region, representation) => { - ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder), representation) + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) } ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.fold_with(folder)), @@ -481,7 +479,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { } ty::Slice(typ) => typ.visit_with(visitor), ty::Adt(_, args) => args.visit_with(visitor), - ty::Dynamic(trait_ty, reg, _) => { + ty::Dynamic(trait_ty, reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 755fc68d86f34..2bea797799958 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -17,7 +17,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::walk::TypeWalker; -use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; +use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, TypeVisitableExt, elaborate}; use tracing::instrument; use ty::util::IntTypeExt; @@ -734,7 +734,6 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, obj: &'tcx List>, reg: ty::Region<'tcx>, - repr: DynKind, ) -> Ty<'tcx> { if cfg!(debug_assertions) { let projection_count = obj @@ -767,7 +766,7 @@ impl<'tcx> Ty<'tcx> { but it has {projection_count}" ); } - Ty::new(tcx, Dynamic(obj, reg, repr)) + Ty::new(tcx, Dynamic(obj, reg)) } #[inline] @@ -980,9 +979,8 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { interner: TyCtxt<'tcx>, preds: &'tcx List>, region: ty::Region<'tcx>, - kind: ty::DynKind, ) -> Self { - Ty::new_dynamic(interner, preds, region, kind) + Ty::new_dynamic(interner, preds, region) } fn new_coroutine( @@ -1356,7 +1354,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_trait(self) -> bool { - matches!(self.kind(), Dynamic(_, _, ty::Dyn)) + matches!(self.kind(), Dynamic(_, _)) } #[inline] @@ -1671,7 +1669,7 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) => Ok(tcx.types.usize), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, DUMMY_SP); Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()])) } @@ -1853,7 +1851,7 @@ impl<'tcx> Ty<'tcx> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => match sizedness { SizedTraitKind::Sized => false, SizedTraitKind::MetaSized => true, }, diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 48718cad597a8..18f41c8b97f64 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -152,7 +152,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) @@ -196,7 +196,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4603c695dedd5..d3cc23818c7e1 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1891,7 +1891,7 @@ fn check_must_not_suspend_ty<'tcx>( } has_emitted } - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { let mut has_emitted = false; for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index a8f2b4e8db662..4b4ec4956eb88 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -390,7 +390,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31eb..7de7870bbb12c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -86,7 +86,7 @@ where ) -> Result, NoSolution> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let cx = ecx.cx(); - let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { + let ty::Dynamic(bounds, _) = goal.predicate.self_ty().kind() else { panic!("expected object type in `probe_and_consider_object_bound_candidate`"); }; match structural_traits::predicates_for_object_candidate( diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index f6eab286ba70b..c40739d12e680 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -383,7 +383,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 54b92ebac1ded..653c59c5d4241 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -665,7 +665,7 @@ where ty::Str | ty::Slice(_) => Ty::new_usize(cx), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) @@ -916,7 +916,7 @@ where | ty::Adt(_, _) | ty::Str | ty::Slice(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) | ty::Error(_) => self_ty.discriminant_ty(ecx.cx()), diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index a69e867289cee..3974114e9b43c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -817,15 +817,13 @@ where } // 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, - ), + (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => ecx + .consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), // `T` -> `dyn Trait` unsizing. - (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( + (_, ty::Dynamic(b_region, b_data)) => result_to_single( ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), ), diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index 6eded3a9eb9ac..fee920221e1d1 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -295,7 +295,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::Ref(_, _, _) | ty::Param(_) | ty::Closure(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::Foreign(_) | ty::Str diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index c9bf4fe4449b0..d9f8085083ebd 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -407,7 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index bcc77ff849da5..0afb94c18d7b9 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -333,7 +333,7 @@ impl TyKind { #[inline] pub fn is_trait(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _))) } #[inline] @@ -472,7 +472,7 @@ impl TyKind { } pub fn trait_principal(&self) -> Option> { - if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _)) = self { if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = predicates.first() { @@ -562,7 +562,7 @@ pub enum RigidTy { Closure(ClosureDef, GenericArgs), Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), - Dynamic(Vec>, Region, DynKind), + Dynamic(Vec>, Region), Never, Tuple(Vec), CoroutineWitness(CoroutineWitnessDef, GenericArgs), @@ -1206,11 +1206,6 @@ pub enum BoundRegionKind { BrEnv, } -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum DynKind { - Dyn, -} - #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ExistentialPredicate { Trait(ExistentialTraitRef), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 66f767a98f5bd..dc9abd88614d3 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -14,7 +14,7 @@ use crate::mir::alloc::AllocId; use crate::mir::mono::{Instance, MonoItem, StaticDef}; use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use crate::ty::{ - Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, + Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, @@ -188,10 +188,9 @@ impl RustcInternal for RigidTy { def.0.internal(tables, tcx), args.internal(tables, tcx), ), - RigidTy::Dynamic(predicate, region, dyn_kind) => rustc_ty::TyKind::Dynamic( + RigidTy::Dynamic(predicate, region) => rustc_ty::TyKind::Dynamic( tcx.mk_poly_existential_predicates(&predicate.internal(tables, tcx)), region.internal(tables, tcx), - dyn_kind.internal(tables, tcx), ), RigidTy::Tuple(tys) => { rustc_ty::TyKind::Tuple(tcx.mk_type_list(&tys.internal(tables, tcx))) @@ -460,20 +459,6 @@ impl RustcInternal for BoundVariableKind { } } -impl RustcInternal for DynKind { - type T<'tcx> = rustc_ty::DynKind; - - fn internal<'tcx>( - &self, - _tables: &mut Tables<'_, BridgeTys>, - _tcx: impl InternalCx<'tcx>, - ) -> Self::T<'tcx> { - match self { - DynKind::Dyn => rustc_ty::DynKind::Dyn, - } - } -} - impl RustcInternal for ExistentialPredicate { type T<'tcx> = rustc_ty::ExistentialPredicate<'tcx>; diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 5131611eb02b3..7f14f878d3737 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -48,16 +48,6 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { } } -impl<'tcx> Stable<'tcx> for ty::DynKind { - type T = crate::ty::DynKind; - - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - match self { - ty::Dyn => crate::ty::DynKind::Dyn, - } - } -} - impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> { type T = crate::ty::ExistentialPredicate; @@ -439,16 +429,13 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { } // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), - ty::Dynamic(existential_predicates, region, dyn_kind) => { - TyKind::RigidTy(RigidTy::Dynamic( - existential_predicates - .iter() - .map(|existential_predicate| existential_predicate.stable(tables, cx)) - .collect(), - region.stable(tables, cx), - dyn_kind.stable(tables, cx), - )) - } + ty::Dynamic(existential_predicates, region) => TyKind::RigidTy(RigidTy::Dynamic( + existential_predicates + .iter() + .map(|existential_predicate| existential_predicate.stable(tables, cx)) + .collect(), + region.stable(tables, cx), + )), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( tables.closure_def(*def_id), generic_args.stable(tables, cx), diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index 87f1cc6ae69d8..acc3334769613 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -171,7 +171,7 @@ impl Visitable for RigidTy { | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), RigidTy::FnPtr(sig) => sig.visit(visitor), - RigidTy::Dynamic(pred, r, _) => { + RigidTy::Dynamic(pred, r) => { pred.visit(visitor)?; r.visit(visitor) } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index f4a14b36ce5cf..ec7a4a81a71aa 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -626,12 +626,10 @@ pub(crate) fn encode_ty<'tcx>( } // Trait types - ty::Dynamic(predicates, region, kind) => { + ty::Dynamic(predicates, region) => { // u3dynIE, where is , as // vendor extended type. - let mut s = String::from(match kind { - ty::Dyn => "u3dynI", - }); + let mut s = String::from("u3dynI"); s.push_str(&encode_predicates(tcx, predicates, dict, options)); s.push_str(&encode_region(*region, dict)); s.push('E'); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 577a16a0d2573..82a2a64f23078 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -268,7 +268,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc let preds = tcx.mk_poly_existential_predicates_from_iter( iter::once(principal_pred).chain(assoc_preds.into_iter()), ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased) } /// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI @@ -334,7 +334,7 @@ pub(crate) fn transform_instance<'tcx>( ty::List::empty(), )); let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased); instance.args = tcx.mk_args_trait(self_ty, List::empty()); } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual @@ -347,7 +347,7 @@ pub(crate) fn transform_instance<'tcx>( // drop_in_place won't have a defining trait, skip the upcast None => instance.args.type_at(0), }; - let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else { bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); }; let self_ty = if preds.principal().is_some() { @@ -355,7 +355,7 @@ pub(crate) fn transform_instance<'tcx>( tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + Ty::new_dynamic(tcx, filtered_preds, *lifetime) } else { // If there's no principal type, re-encode it as a unit, since we don't know anything // about it. This technically discards the knowledge that it was a type that was made diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 76ac82cf95a12..3896e06a627b7 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -110,7 +110,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 1605b4958ba3d..9fa7e2f100393 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -577,10 +577,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // FIXME(unsafe_binder): ty::UnsafeBinder(..) => todo!(), - ty::Dynamic(predicates, r, kind) => { - self.push(match kind { - ty::Dyn => "D", - }); + ty::Dynamic(predicates, r) => { + self.push("D"); self.print_dyn_existential(predicates)?; r.print(self)?; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3edc365c886ea..5b8315841765b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -399,7 +399,7 @@ pub struct TraitObjectVisitor(pub FxIndexSet); impl<'tcx> TypeVisitor> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - ty::Dynamic(preds, re, _) if re.is_static() => { + ty::Dynamic(preds, re) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index f997842a607f6..b0b858aa270c1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -292,7 +292,7 @@ impl Trait for X { ); } } - (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) + (ty::Dynamic(t, _), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx .explicit_item_self_bounds(alias.def_id) @@ -314,9 +314,7 @@ impl Trait for X { values.found, values.expected, )); } - (ty::Dynamic(t, _, ty::DynKind::Dyn), _) - if let Some(def_id) = t.principal_def_id() => - { + (ty::Dynamic(t, _), _) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.found, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -335,9 +333,7 @@ impl Trait for X { )); } } - (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) - if let Some(def_id) = t.principal_def_id() => - { + (_, ty::Dynamic(t, _)) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.expected, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -489,7 +485,7 @@ impl Trait for X { && let Some(then) = blk.expr && def.is_box() && let boxed_ty = args.type_at(0) - && let ty::Dynamic(t, _, _) = boxed_ty.kind() + && let ty::Dynamic(t, _) = boxed_ty.kind() && let Some(def_id) = t.principal_def_id() && let mut impl_def_ids = vec![] && let _ = diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 518d4fe17e8f4..9a8ccea3aca87 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -430,7 +430,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut alt_span = None; if let Some(ty) = ty && sub.is_static() - && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind() + && let ty::Dynamic(preds, _) = ty.kind() && let Some(def_id) = preds.principal_def_id() { for (clause, span) in diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index f794ff632c592..b3d1b8e3888a1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -554,7 +554,7 @@ fn attempt_dyn_to_enum_suggestion( // defaults to assuming that things are *not* sized, whereas we want to // fall back to assuming that things may be sized. match impl_type.kind() { - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => { return None; } _ => {} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index bb5c6469f34b4..00c123981e1c0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -237,7 +237,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } - if let ty::Dynamic(traits, _, _) = self_ty.kind() { + if let ty::Dynamic(traits, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { self_types.push(self.tcx.def_path_str(trait_ref.def_id)); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index f2f840581cf02..37e622102e70f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1131,7 +1131,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }, ) } - ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { + ty::Dynamic(data, _) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) // for existential projection, args are shifted over by 1 @@ -1520,7 +1520,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; }; - let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { + let ty::Dynamic(predicates, _) = object_ty.kind() else { return; }; let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty); @@ -1883,7 +1883,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { return false; }; - let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { + let ty::Dynamic(_, _) = trait_pred.self_ty().skip_binder().kind() else { return false; }; 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 62795c8a3a686..60f1fcb26c004 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -669,7 +669,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These may potentially implement `FnPtr` ty::Placeholder(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Infer(_) | ty::Param(..) @@ -991,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => { + (&ty::Dynamic(a_data, a_region), &ty::Dynamic(b_data, b_region)) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -1054,7 +1054,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(_, _, ty::Dyn)) => { + (_, &ty::Dynamic(_, _)) => { candidates.vec.push(BuiltinUnsizeCandidate); } @@ -1327,7 +1327,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Pat(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 488094b15ac60..7ad65a1df8e9b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1023,10 +1023,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + let ty::Dynamic(a_data, a_region) = *a_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + let ty::Dynamic(b_data, b_region) = *b_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; @@ -1062,10 +1062,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "confirm_builtin_unsize_candidate"); Ok(match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). - (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b)) - if dyn_a == dyn_b => - { + // `dyn Trait + Kx + 'a` -> `dyn Trait + Ky + 'b` (auto traits and lifetime subtyping). + (&ty::Dynamic(data_a, r_a), &ty::Dynamic(data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let existential_predicates = if data_b.principal().is_some() { @@ -1098,7 +1096,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ) }; - let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a); + let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -1122,7 +1120,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `dyn Trait` - (_, &ty::Dynamic(data, r, ty::Dyn)) => { + (_, &ty::Dynamic(data, r)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); if let Some(did) = object_dids.find(|did| !tcx.is_dyn_compatible(*did)) { return Err(SelectionError::TraitDynIncompatible(did)); diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 7e8a41457d411..584c8e2a27c82 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -317,7 +317,7 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe "vtable trait ref should be normalized" ); - let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { + let ty::Dynamic(source, _) = *key.self_ty().kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( @@ -384,13 +384,13 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target_data, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _) = *target.kind() else { bug!(); }; let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source_data, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _) = *source.kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index adce9850b594c..45cbb56b1c2ef 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -915,7 +915,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // We recurse into the binder below. } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // WfObject // // Here, we defer WF checking due to higher-ranked diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2adc..643e3db8f83fe 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -476,7 +476,7 @@ fn layout_of_uncached<'tcx>( } // Odd unit types. - ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + ty::FnDef(..) | ty::Dynamic(_, _) | ty::Foreign(..) => { let sized = matches!(ty.kind(), ty::FnDef(..)); tcx.mk_layout(LayoutData::unit(cx, sized)) } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index a5987757dc34d..18a9a7c22d9dd 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -40,7 +40,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::CoroutineWitness(..) | ty::Never => None, - ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(..) | ty::Dynamic(_, _) => match sizedness { // Never `Sized` SizedTraitKind::Sized => Some(ty), // Always `MetaSized` @@ -366,7 +366,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) ); match tail.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 23b7f55fbbe53..24704c5bb5358 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -288,7 +288,7 @@ impl FlagComputation { self.add_alias_ty(data); } - ty::Dynamic(obj, r, _) => { + ty::Dynamic(obj, r) => { for predicate in obj.iter() { self.bound_computation(predicate, |computation, predicate| match predicate { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index ecfc05a6e20e7..b5b552dbaec09 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -76,12 +76,7 @@ pub trait Ty>: fn new_foreign(interner: I, def_id: I::ForeignId) -> Self; - fn new_dynamic( - interner: I, - preds: I::BoundExistentialPredicates, - region: I::Region, - kind: ty::DynKind, - ) -> Self; + fn new_dynamic(interner: I, preds: I::BoundExistentialPredicates, region: I::Region) -> Self; fn new_coroutine(interner: I, def_id: I::CoroutineId, args: I::GenericArgs) -> Self; @@ -167,7 +162,7 @@ pub trait Ty>: fn is_guaranteed_unsized_raw(self) -> bool { match self.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 50b588029aed9..61e0b67b1639d 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -57,7 +57,6 @@ mod upcast; mod visit; pub use AliasTyKind::*; -pub use DynKind::*; pub use InferTy::*; pub use RegionKind::*; pub use TyKind::*; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index b09a378d34143..c7dccea6adc12 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -203,7 +203,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Ref(_, _, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) => { ty.super_visit_with(self); } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 690a5f65e0848..09add529286d9 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -412,16 +412,11 @@ pub fn structurally_relate_tys>( (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), - (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr)) - if a_repr == b_repr => - { - Ok(Ty::new_dynamic( - cx, - relation.relate(a_obj, b_obj)?, - relation.relate(a_region, b_region)?, - a_repr, - )) - } + (ty::Dynamic(a_obj, a_region), ty::Dynamic(b_obj, b_region)) => Ok(Ty::new_dynamic( + cx, + relation.relate(a_obj, b_obj)?, + relation.relate(a_region, b_region)?, + )), (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => { // All Coroutine types with the same id represent diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 225d85f79c3d1..dda59283677cd 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -19,20 +19,6 @@ use crate::{self as ty, DebruijnIndex, FloatTy, IntTy, Interner, UintTy}; mod closure; -/// Specifies how a trait object is represented. -/// -/// This used to have a variant `DynStar`, but that variant has been removed, -/// and it's likely this whole enum will be removed soon. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr( - feature = "nightly", - derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) -)] -pub enum DynKind { - /// An unsized `dyn Trait` object - Dyn, -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr( feature = "nightly", @@ -165,7 +151,7 @@ pub enum TyKind { UnsafeBinder(UnsafeBinderInner), /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. - Dynamic(I::BoundExistentialPredicates, I::Region, DynKind), + Dynamic(I::BoundExistentialPredicates, I::Region), /// The anonymous type of a closure. Used to represent the type of `|a| a`. /// @@ -314,7 +300,7 @@ impl TyKind { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -367,9 +353,7 @@ impl fmt::Debug for TyKind { FnPtr(sig_tys, hdr) => write!(f, "{:?}", sig_tys.with(*hdr)), // FIXME(unsafe_binder): print this like `unsafe<'a> T<'a>`. UnsafeBinder(binder) => write!(f, "{:?}", binder), - Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"), - }, + Dynamic(p, r) => write!(f, "dyn {p:?} + {r:?}"), Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&s).finish(), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 9912fad1756ea..6d51817a7bf44 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -109,7 +109,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::Alias(_, data) => { stack.extend(data.args.iter().rev()); } - ty::Dynamic(obj, lt, _) => { + ty::Dynamic(obj, lt) => { stack.push(lt.into()); stack.extend( obj.iter() diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5ccacafea0158..0afb969d5c84f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2093,7 +2093,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ); Type::Path { path } } - ty::Dynamic(obj, reg, _) => { + ty::Dynamic(obj, reg) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea25589..ac87e21b82f15 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -86,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in *preds { match pred.skip_binder() { ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 9f77a1c4d9b62..3e41bce1dc4ce 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -349,7 +349,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } false }, - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. }) @@ -673,7 +673,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), - ty::Dynamic(bounds, _, _) => { + ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { Some(bound) From 4d931e643af306eeabc417957975271879b68c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 04:16:47 +0200 Subject: [PATCH 0503/1900] Remove `DynKind` --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea25589..ac87e21b82f15 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -86,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in *preds { match pred.skip_binder() { ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 9f77a1c4d9b62..3e41bce1dc4ce 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -349,7 +349,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } false }, - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. }) @@ -673,7 +673,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), - ty::Dynamic(bounds, _, _) => { + ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { Some(bound) From 34062b124f00eb7c35da2e3981cfb5b45dd26039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 05:34:19 +0200 Subject: [PATCH 0504/1900] Merge modules dyn_compatibility and lint into dyn_trait --- .../{dyn_compatibility.rs => dyn_trait.rs} | 553 +++++++++++++++++- .../src/hir_ty_lowering/lint.rs | 533 ----------------- .../src/hir_ty_lowering/mod.rs | 14 +- 3 files changed, 550 insertions(+), 550 deletions(-) rename compiler/rustc_hir_analysis/src/hir_ty_lowering/{dyn_compatibility.rs => dyn_trait.rs} (51%) delete mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs similarity index 51% rename from compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs rename to compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index c823a4a0430f4..c248cd7fec2e5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -1,16 +1,22 @@ +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::struct_span_code_err; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; +use rustc_hir::def_id::DefId; +use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::traits; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -28,10 +34,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, + syntax: TraitObjectSyntax, ) -> Ty<'tcx> { let tcx = self.tcx(); let dummy_self = tcx.types.trait_object_dummy_self; + match syntax { + TraitObjectSyntax::Dyn => {} + TraitObjectSyntax::None => { + match self.prohibit_or_lint_bare_trait_object_ty(span, hir_id, hir_bounds) { + // Don't continue with type analysis if the `dyn` keyword is missing. + // It generates confusing errors, especially if the user meant to use + // another keyword like `impl`. + Some(guar) => return Ty::new_error(tcx, guar), + None => {} + } + } + } + let mut user_written_bounds = Vec::new(); let mut potential_assoc_types = Vec::new(); for poly_trait_ref in hir_bounds.iter() { @@ -46,10 +66,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let ast_bounds: Vec<_> = - hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); - - self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span); + self.add_default_traits( + &mut user_written_bounds, + dummy_self, + &hir_bounds + .iter() + .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + .collect::>(), + None, + span, + ); let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); @@ -482,6 +508,521 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); } + + /// Prohibit or lint against *bare* trait object types depending on the edition. + /// + /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. + /// In edition 2021 and onward we emit a hard error for them. + fn prohibit_or_lint_bare_trait_object_ty( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + ) -> Option { + let tcx = self.tcx(); + let [poly_trait_ref, ..] = hir_bounds else { return None }; + + let in_path = match tcx.parent_hir_node(hir_id) { + hir::Node::Ty(hir::Ty { + kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) if qself.hir_id == hir_id => true, + _ => false, + }; + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(span) + .ok() + .is_some_and(|s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = vec![( + span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]; + + if is_global || needs_bracket { + sugg.push(( + span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if span.edition().at_least_rust_2021() { + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + span, + E0782, + "{}", + "expected a type, found a trait" + ); + if span.can_be_used_for_suggestions() + && poly_trait_ref.trait_ref.trait_def_id().is_some() + && !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag) + && !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag) + { + self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag); + } + // Check if the impl trait that we are considering is an impl of a local trait. + self.maybe_suggest_blanket_trait_impl(span, hir_id, &mut diag); + self.maybe_suggest_assoc_ty_bound(hir_id, &mut diag); + self.maybe_suggest_typoed_method( + hir_id, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name + // Add the suggestion to this error + if let Some(mut sugg) = + self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) + && let Suggestions::Enabled(ref mut s1) = diag.suggestions + && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + { + s1.append(s2); + sugg.cancel(); + } + Some(diag.emit()) + } else { + tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { + lint.primary_message("trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion_verbose( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); + }); + None + } + } + + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. + fn maybe_suggest_add_generic_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + let parent_hir_id = tcx.parent_hir_id(hir_id); + let parent_item = tcx.hir_get_parent_item(hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, generics, variant), + .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics + } + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); + } + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); + true + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_suggest_blanket_trait_impl( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_, G>, + ) { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), + .. + }) = tcx.hir_node_by_def_id(parent_id) + && hir_id == impl_self_ty.hir_id + { + let Some(of_trait) = of_trait else { + diag.span_suggestion_verbose( + impl_self_ty.span.shrink_to_hi(), + "you might have intended to implement this trait for a given type", + format!(" for /* Type */"), + Applicability::HasPlaceholders, + ); + return; + }; + if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait.trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { + return; + }; + + let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(span) else { + return; + }; + let sugg = self.add_generic_param_suggestion(generics, span, &impl_trait_name); + diag.multipart_suggestion( + format!( + "alternatively use a blanket implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`" + ), + sugg, + Applicability::MaybeIncorrect, + ); + } + } + + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. + fn maybe_suggest_dyn_trait( + &self, + hir_id: hir::HirId, + sugg: Vec<(Span, String)>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} + + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, _, variant), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } + } + _ => return false, + } + + // FIXME: Only emit this suggestion if the trait is dyn-compatible. + diag.multipart_suggestion_verbose( + "you can add the `dyn` keyword if you want a trait object", + sugg, + Applicability::MachineApplicable, + ); + true + } + + fn add_generic_param_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Vec<(Span, String)> { + // check if the trait has generics, to make a correct suggestion + 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}")) + } else { + (generics.span, format!("<{param_name}: {impl_trait_name}>")) + }; + vec![(self_ty_span, param_name), add_generic_sugg] + } + + /// Make sure that we are in the condition to suggest `impl Trait`. + fn maybe_suggest_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` + // and suggest `Trait0`. + // Functions are found in three different contexts. + // 1. Independent functions + // 2. Functions inside trait blocks + // 3. Functions inside impl blocks + let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, generics, .. }, .. + }) => (sig, generics), + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + _ => return false, + }; + let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + let impl_sugg = vec![(span.shrink_to_lo(), "impl ".to_string())]; + // Check if trait object is safe for suggesting dynamic dispatch. + let is_dyn_compatible = hir_bounds.iter().all(|bound| match bound.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), + _ => false, + }); + + let borrowed = matches!( + tcx.parent_hir_node(hir_id), + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) + ); + + // Suggestions for function return type. + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.peel_refs().hir_id == hir_id + { + let pre = if !is_dyn_compatible { + format!("`{trait_name}` is dyn-incompatible, ") + } else { + String::new() + }; + let msg = format!( + "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ + single underlying type", + ); + + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + + // Suggest `Box` for return type + if is_dyn_compatible { + // If the return type is `&Trait`, we don't want + // the ampersand to be displayed in the `Box` + // suggestion. + let suggestion = if borrowed { + vec![(ty.span, format!("Box"))] + } else { + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ] + }; + + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + suggestion, + Applicability::MachineApplicable, + ); + } + return true; + } + + // Suggestions for function parameters. + for ty in sig.decl.inputs { + if ty.peel_refs().hir_id != hir_id { + continue; + } + let sugg = self.add_generic_param_suggestion(generics, span, &trait_name); + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "you can also use an opaque type, but users won't be able to specify the type \ + parameter when calling the `fn`, having to rely exclusively on type inference", + impl_sugg, + Applicability::MachineApplicable, + ); + if !is_dyn_compatible { + diag.note(format!( + "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" + )); + } else { + // No ampersand in suggestion if it's borrowed already + let (dyn_str, paren_dyn_str) = + if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; + + let sugg = if let [_, _, ..] = hir_bounds { + // There is more than one trait bound, we need surrounding parentheses. + vec![ + (span.shrink_to_lo(), paren_dyn_str.to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(span.shrink_to_lo(), dyn_str.to_string())] + }; + diag.multipart_suggestion_verbose( + format!( + "alternatively, use a trait object to accept any type that implements \ + `{trait_name}`, accessing its methods at runtime using dynamic dispatch", + ), + sugg, + Applicability::MachineApplicable, + ); + } + return true; + } + false + } + + fn maybe_suggest_assoc_ty_bound(&self, hir_id: hir::HirId, diag: &mut Diag<'_>) { + let mut parents = self.tcx().hir_parent_iter(hir_id); + + if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() + && let Some(obj_ty) = constraint.ty() + && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() + { + if let Some((_, hir::Node::Ty(ty))) = parents.next() + && let hir::TyKind::TraitObject(..) = ty.kind + { + // Assoc ty bounds aren't permitted inside trait object types. + return; + } + + if trait_ref + .path + .segments + .iter() + .find_map(|seg| { + seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) + }) + .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) + { + // Only consider angle-bracketed args (where we have a `=` to replace with `:`). + return; + } + + let lo = if constraint.gen_args.span_ext.is_dummy() { + constraint.ident.span + } else { + constraint.gen_args.span_ext + }; + let hi = obj_ty.span; + + if !lo.eq_ctxt(hi) { + return; + } + + diag.span_suggestion_verbose( + lo.between(hi), + "you might have meant to write a bound here", + ": ", + Applicability::MaybeIncorrect, + ); + } + } + + fn maybe_suggest_typoed_method( + &self, + hir_id: hir::HirId, + trait_def_id: Option, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(hir_id) + else { + return; + }; + if path_ty.hir_id != hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.namespace() == hir::def::Namespace::ValueNS) + .map(|cand| cand.name()) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } } fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs deleted file mode 100644 index 56998b5b53cc4..0000000000000 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ /dev/null @@ -1,533 +0,0 @@ -use rustc_ast::TraitObjectSyntax; -use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_lint_defs::Applicability; -use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; -use rustc_span::Span; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; - -use super::HirTyLowerer; - -impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Prohibit or lint against *bare* trait object types depending on the edition. - /// - /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. - /// In edition 2021 and onward we emit a hard error for them. - pub(super) fn prohibit_or_lint_bare_trait_object_ty( - &self, - self_ty: &hir::Ty<'_>, - ) -> Option { - let tcx = self.tcx(); - - let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = - self_ty.kind - && let TraitObjectSyntax::None = tagged_ptr.tag() - { - poly_trait_ref - } else { - return None; - }; - - let in_path = match tcx.parent_hir_node(self_ty.hir_id) { - hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::PatExpr(hir::PatExpr { - kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) if qself.hir_id == self_ty.hir_id => true, - _ => false, - }; - let needs_bracket = in_path - && !tcx - .sess - .source_map() - .span_to_prev_source(self_ty.span) - .ok() - .is_some_and(|s| s.trim_end().ends_with('<')); - - let is_global = poly_trait_ref.trait_ref.path.is_global(); - - let mut sugg = vec![( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - )]; - - if is_global || needs_bracket { - sugg.push(( - self_ty.span.shrink_to_hi(), - format!( - "{}{}", - if is_global { ")" } else { "" }, - if needs_bracket { ">" } else { "" }, - ), - )); - } - - if self_ty.span.edition().at_least_rust_2021() { - let mut diag = rustc_errors::struct_span_code_err!( - self.dcx(), - self_ty.span, - E0782, - "{}", - "expected a type, found a trait" - ); - if self_ty.span.can_be_used_for_suggestions() - && poly_trait_ref.trait_ref.trait_def_id().is_some() - && !self.maybe_suggest_impl_trait(self_ty, &mut diag) - && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) - { - self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); - } - // Check if the impl trait that we are considering is an impl of a local trait. - self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); - self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - self.maybe_suggest_typoed_method( - self_ty, - poly_trait_ref.trait_ref.trait_def_id(), - &mut diag, - ); - // In case there is an associated type with the same name - // Add the suggestion to this error - if let Some(mut sugg) = - self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) - && let Suggestions::Enabled(ref mut s1) = diag.suggestions - && let Suggestions::Enabled(ref mut s2) = sugg.suggestions - { - s1.append(s2); - sugg.cancel(); - } - Some(diag.emit()) - } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if self_ty.span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(self_ty, lint); - }); - None - } - } - - /// For a struct or enum with an invalid bare trait object field, suggest turning - /// it into a generic type bound. - fn maybe_suggest_add_generic_impl_trait( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); - let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - - let generics = match tcx.hir_node_by_def_id(parent_item) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics, variant), - .. - }) => { - if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { - return false; - } - generics - } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { - if !def - .variants - .iter() - .flat_map(|variant| variant.data.fields().iter()) - .any(|field| field.hir_id == parent_hir_id) - { - return false; - } - generics - } - _ => return false, - }; - - let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - - let param = "TUV" - .chars() - .map(|c| c.to_string()) - .chain((0..).map(|i| format!("P{i}"))) - .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) - .expect("we definitely can find at least one param name to generate"); - let mut sugg = vec![(self_ty.span, param.to_string())]; - if let Some(insertion_span) = generics.span_for_param_suggestion() { - sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); - } else { - sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); - } - diag.multipart_suggestion_verbose( - "you might be missing a type parameter", - sugg, - Applicability::MachineApplicable, - ); - true - } - /// Make sure that we are in the condition to suggest the blanket implementation. - fn maybe_suggest_blanket_trait_impl( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_, G>, - ) { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), - .. - }) = tcx.hir_node_by_def_id(parent_id) - && self_ty.hir_id == impl_self_ty.hir_id - { - let Some(of_trait) = of_trait else { - diag.span_suggestion_verbose( - impl_self_ty.span.shrink_to_hi(), - "you might have intended to implement this trait for a given type", - format!(" for /* Type */"), - Applicability::HasPlaceholders, - ); - return; - }; - if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { - return; - } - let of_trait_span = of_trait.trait_ref.path.span; - // make sure that we are not calling unwrap to abort during the compilation - let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { - return; - }; - - let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) - else { - return; - }; - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - diag.multipart_suggestion( - format!( - "alternatively use a blanket implementation to implement `{of_trait_name}` for \ - all types that also implement `{impl_trait_name}`" - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - } - - /// Try our best to approximate when adding `dyn` would be helpful for a bare - /// trait object. - /// - /// Right now, this is if the type is either directly nested in another ty, - /// or if it's in the tail field within a struct. This approximates what the - /// user would've gotten on edition 2015, except for the case where we have - /// an *obvious* knock-on `Sized` error. - fn maybe_suggest_dyn_trait( - &self, - self_ty: &hir::Ty<'_>, - sugg: Vec<(Span, String)>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - // Look at the direct HIR parent, since we care about the relationship between - // the type and the thing that directly encloses it. - match tcx.parent_hir_node(self_ty.hir_id) { - // These are all generally ok. Namely, when a trait object is nested - // into another expression or ty, it's either very certain that they - // missed the ty (e.g. `&Trait`) or it's not really possible to tell - // what their intention is, so let's not give confusing suggestions and - // just mention `dyn`. The user can make up their mind what to do here. - hir::Node::Ty(_) - | hir::Node::Expr(_) - | hir::Node::PatExpr(_) - | hir::Node::PathSegment(_) - | hir::Node::AssocItemConstraint(_) - | hir::Node::TraitRef(_) - | hir::Node::Item(_) - | hir::Node::WherePredicate(_) => {} - - hir::Node::Field(field) => { - // Enums can't have unsized fields, fields can only have an unsized tail field. - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, _, variant), .. - }) = tcx.parent_hir_node(field.hir_id) - && variant - .fields() - .last() - .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) - { - // Ok - } else { - return false; - } - } - _ => return false, - } - - // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose( - "you can add the `dyn` keyword if you want a trait object", - sugg, - Applicability::MachineApplicable, - ); - true - } - - fn add_generic_param_suggestion( - &self, - generics: &hir::Generics<'_>, - self_ty_span: Span, - impl_trait_name: &str, - ) -> Vec<(Span, String)> { - // check if the trait has generics, to make a correct suggestion - 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}")) - } else { - (generics.span, format!("<{param_name}: {impl_trait_name}>")) - }; - vec![(self_ty_span, param_name), add_generic_sugg] - } - - /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` - // and suggest `Trait0`. - // Functions are found in three different contexts. - // 1. Independent functions - // 2. Functions inside trait blocks - // 3. Functions inside impl blocks - let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { sig, generics, .. }, .. - }) => (sig, generics), - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - _ => return false, - }; - let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - // Check if trait object is safe for suggesting dynamic dispatch. - let is_dyn_compatible = match self_ty.kind { - hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), - _ => false, - }) - } - _ => false, - }; - - let borrowed = matches!( - tcx.parent_hir_node(self_ty.hir_id), - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) - ); - - // Suggestions for function return type. - if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.peel_refs().hir_id == self_ty.hir_id - { - let pre = if !is_dyn_compatible { - format!("`{trait_name}` is dyn-incompatible, ") - } else { - String::new() - }; - let msg = format!( - "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ - single underlying type", - ); - - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); - - // Suggest `Box` for return type - if is_dyn_compatible { - // If the return type is `&Trait`, we don't want - // the ampersand to be displayed in the `Box` - // suggestion. - let suggestion = if borrowed { - vec![(ty.span, format!("Box"))] - } else { - vec![ - (ty.span.shrink_to_lo(), "Box".to_string()), - ] - }; - - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - suggestion, - Applicability::MachineApplicable, - ); - } - return true; - } - - // Suggestions for function parameters. - for ty in sig.decl.inputs { - if ty.peel_refs().hir_id != self_ty.hir_id { - continue; - } - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "you can also use an opaque type, but users won't be able to specify the type \ - parameter when calling the `fn`, having to rely exclusively on type inference", - impl_sugg, - Applicability::MachineApplicable, - ); - if !is_dyn_compatible { - diag.note(format!( - "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" - )); - } else { - // No ampersand in suggestion if it's borrowed already - let (dyn_str, paren_dyn_str) = - if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { - // There is more than one trait bound, we need surrounding parentheses. - vec![ - (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), - (self_ty.span.shrink_to_hi(), ")".to_string()), - ] - } else { - vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] - }; - diag.multipart_suggestion_verbose( - format!( - "alternatively, use a trait object to accept any type that implements \ - `{trait_name}`, accessing its methods at runtime using dynamic dispatch", - ), - sugg, - Applicability::MachineApplicable, - ); - } - return true; - } - false - } - - fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { - let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id); - - if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() - && let Some(obj_ty) = constraint.ty() - && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() - { - if let Some((_, hir::Node::Ty(ty))) = parents.next() - && let hir::TyKind::TraitObject(..) = ty.kind - { - // Assoc ty bounds aren't permitted inside trait object types. - return; - } - - if trait_ref - .path - .segments - .iter() - .find_map(|seg| { - seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) - }) - .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) - { - // Only consider angle-bracketed args (where we have a `=` to replace with `:`). - return; - } - - let lo = if constraint.gen_args.span_ext.is_dummy() { - constraint.ident.span - } else { - constraint.gen_args.span_ext - }; - let hi = obj_ty.span; - - if !lo.eq_ctxt(hi) { - return; - } - - diag.span_suggestion_verbose( - lo.between(hi), - "you might have meant to write a bound here", - ": ", - Applicability::MaybeIncorrect, - ); - } - } - - fn maybe_suggest_typoed_method( - &self, - self_ty: &hir::Ty<'_>, - trait_def_id: Option, - diag: &mut Diag<'_>, - ) { - let tcx = self.tcx(); - let Some(trait_def_id) = trait_def_id else { - return; - }; - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), - .. - }) = tcx.parent_hir_node(self_ty.hir_id) - else { - return; - }; - if path_ty.hir_id != self_ty.hir_id { - return; - } - let names: Vec<_> = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|assoc| assoc.namespace() == Namespace::ValueNS) - .map(|cand| cand.name()) - .collect(); - if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { - diag.span_suggestion_verbose( - segment.ident.span, - format!( - "you may have misspelled this associated item, causing `{}` \ - to be interpreted as a type rather than a trait", - tcx.item_name(trait_def_id), - ), - typo, - Applicability::MaybeIncorrect, - ); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index b64129dc54e67..9b198d044542f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,10 +15,9 @@ mod bounds; mod cmse; -mod dyn_compatibility; +mod dyn_trait; pub mod errors; pub mod generics; -mod lint; use std::assert_matches::assert_matches; use std::slice; @@ -2427,15 +2426,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - - if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { - // Don't continue with type analysis if the `dyn` keyword is missing - // It generates confusing errors, especially if the user meant to use another - // keyword like `impl` - Ty::new_error(tcx, guar) - } else { - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime) - } + let syntax = tagged_ptr.tag(); + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, syntax) } // If we encounter a fully qualified path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore From 06e881042d3f58fdb60fde58de5bf112b2184b11 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 17 Sep 2025 04:52:58 +0000 Subject: [PATCH 0505/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 3f1552a273e43e15f6ed240d00e1efdd6a53e65e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f27677cd50388..0eb16a943d6ab 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9d82de19dfae60e55c291f5f28e28cfc2c1b9630 +3f1552a273e43e15f6ed240d00e1efdd6a53e65e From 603d64c3bb61e5e39bf624cc152fe1b844e70220 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 17 Sep 2025 14:59:02 +0800 Subject: [PATCH 0506/1900] Fix complete type in nested pattern Example --- ```rust struct Foo { num: u32 } struct Bar(Foo); fn foo(Bar($0)) {} ``` **Before this PR**: ```rust struct Foo { num: u32 } struct Bar(Foo); fn foo(Bar(Foo { num$1 }: Foo$0)) {} ``` **After this PR**: ```rust struct Foo { num: u32 } struct Bar(Foo); fn foo(Bar(Foo { num$1 }$0)) {} ``` --- .../ide-completion/src/render/pattern.rs | 1 + .../ide-completion/src/tests/pattern.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 60ec1128233e2..312d3bd426f90 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -163,6 +163,7 @@ fn render_pat( PatternContext { param_ctx: Some(ParamContext { kind: ParamKind::Function(_), .. }), has_type_ascription: false, + parent_pat: None, .. } ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 9ec27252fd741..6eb0b818d6970 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -398,6 +398,25 @@ fn foo($0) {} ) } +#[test] +fn completes_in_fn_param_in_nested_pattern() { + check( + r#" +struct Foo { num: u32 } +struct Bar(Foo); +fn foo(Bar($0)) {} +"#, + expect![[r#" + st Bar + st Foo + bn Bar(…) Bar($1)$0 + bn Foo {…} Foo { num$1 }$0 + kw mut + kw ref + "#]], + ) +} + #[test] fn completes_in_closure_param() { check( From ee496f203828c4aad321500cbe756de8f22895d8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 17 Sep 2025 11:20:57 +0200 Subject: [PATCH 0507/1900] a valid state is achieved by passing the test suite --- src/doc/rustc-dev-guide/src/tests/ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index d9fc2324d8bf6..1488103f469d1 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -1,7 +1,7 @@ # Testing with CI The primary goal of our CI system is to ensure that the `master` branch of -`rust-lang/rust` is always in a valid state and passes our test suite. +`rust-lang/rust` is always in a valid state by passing our test suite. From a high-level point of view, when you open a pull request at `rust-lang/rust`, the following will happen: From 11107679ee19cc54d4f6cf66501fd62f123e9eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 14 Jul 2025 17:05:51 +0000 Subject: [PATCH 0508/1900] Add test for autodiff abi handling --- tests/codegen-llvm/autodiff/abi_handling.rs | 210 ++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 tests/codegen-llvm/autodiff/abi_handling.rs diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs new file mode 100644 index 0000000000000..454ec698b917c --- /dev/null +++ b/tests/codegen-llvm/autodiff/abi_handling.rs @@ -0,0 +1,210 @@ +//@ revisions: debug release + +//@[debug] compile-flags: -Zautodiff=Enable -C opt-level=0 -Clto=fat +//@[release] compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme + +// This test checks that Rust types are lowered to LLVM-IR types in a way +// we expect and Enzyme can handle. We explicitly check release mode to +// ensure that LLVM's O3 pipeline doesn't rewrite function signatures +// into forms that Enzyme can't process correctly. + +#![feature(autodiff)] + +use std::autodiff::{autodiff_forward, autodiff_reverse}; + +#[derive(Copy, Clone)] +struct Input { + x: f32, + y: f32, +} + +#[derive(Copy, Clone)] +struct Wrapper { + z: f32, +} + +#[derive(Copy, Clone)] +struct NestedInput { + x: f32, + y: Wrapper, +} + +fn square(x: f32) -> f32 { + x * x +} + +// CHECK-LABEL: ; abi_handling::df1 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0) +// release-NEXT: define internal fastcc float +// release-SAME: (float %x.0.val, float %x.4.val) + +// CHECK-LABEL: ; abi_handling::f1 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.val, float %x.4.val) +#[autodiff_forward(df1, Dual, Dual)] +#[inline(never)] +fn f1(x: &[f32; 2]) -> f32 { + x[0] + x[1] +} + +// CHECK-LABEL: ; abi_handling::df2 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr %f, float %x, float %dret) +// release-NEXT: define internal fastcc float +// release-SAME: (float noundef %x) + +// CHECK-LABEL: ; abi_handling::f2 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr %f, float %x) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %x) +#[autodiff_reverse(df2, Const, Active, Active)] +#[inline(never)] +fn f2(f: fn(f32) -> f32, x: f32) -> f32 { + f(x) +} + +// CHECK-LABEL: ; abi_handling::df3 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float %x.0.val) + +// CHECK-LABEL: ; abi_handling::f3 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x, ptr align 4 %y) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.val) +#[autodiff_forward(df3, Dual, Dual, Dual)] +#[inline(never)] +fn f3<'a>(x: &'a f32, y: &'a f32) -> f32 { + *x * *y +} + +// CHECK-LABEL: ; abi_handling::df4 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %x.0, float %x.1, float %bx_0.0, float %bx_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float noundef %x.0, float noundef %x.1) + +// CHECK-LABEL: ; abi_handling::f4 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %x.0, float %x.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %x.0, float noundef %x.1) +#[autodiff_forward(df4, Dual, Dual)] +#[inline(never)] +fn f4(x: (f32, f32)) -> f32 { + x.0 * x.1 +} + +// CHECK-LABEL: ; abi_handling::df5 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %i.0, float %i.1, float %bi_0.0, float %bi_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float noundef %i.0, float noundef %i.1) + +// CHECK-LABEL: ; abi_handling::f5 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %i.0, float %i.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %i.0, float noundef %i.1) +#[autodiff_forward(df5, Dual, Dual)] +#[inline(never)] +fn f5(i: Input) -> f32 { + i.x + i.y +} + +// CHECK-LABEL: ; abi_handling::df6 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %i.0, float %i.1, float %bi_0.0, float %bi_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: float noundef %i.0, float noundef %i.1 +// release-SAME: float noundef %bi_0.0, float noundef %bi_0.1 + +// CHECK-LABEL: ; abi_handling::f6 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %i.0, float %i.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %i.0, float noundef %i.1) +#[autodiff_forward(df6, Dual, Dual)] +#[inline(never)] +fn f6(i: NestedInput) -> f32 { + i.x + i.y.z * i.y.z +} + +// CHECK-LABEL: ; abi_handling::df7 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1, ptr align 4 %bx_0.0, ptr align 4 %bx_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float %x.0.0.val, float %x.1.0.val) + +// CHECK-LABEL: ; abi_handling::f7 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.0.val, float %x.1.0.val) +#[autodiff_forward(df7, Dual, Dual)] +#[inline(never)] +fn f7(x: (&f32, &f32)) -> f32 { + x.0 * x.1 +} + +fn main() { + let x = std::hint::black_box(2.0); + let y = std::hint::black_box(3.0); + let z = std::hint::black_box(4.0); + static Y: f32 = std::hint::black_box(3.2); + + let in_f1 = [x, y]; + dbg!(f1(&in_f1)); + let res_f1 = df1(&in_f1, &[1.0, 0.0]); + dbg!(res_f1); + + dbg!(f2(square, x)); + let res_f2 = df2(square, x, 1.0); + dbg!(res_f2); + + dbg!(f3(&x, &Y)); + let res_f3 = df3(&x, &Y, &1.0, &0.0); + dbg!(res_f3); + + let in_f4 = (x, y); + dbg!(f4(in_f4)); + let res_f4 = df4(in_f4, (1.0, 0.0)); + dbg!(res_f4); + + let in_f5 = Input { x, y }; + dbg!(f5(in_f5)); + let res_f5 = df5(in_f5, Input { x: 1.0, y: 0.0 }); + dbg!(res_f5); + + let in_f6 = NestedInput { x, y: Wrapper { z: y } }; + dbg!(f6(in_f6)); + let res_f6 = df6(in_f6, NestedInput { x, y: Wrapper { z } }); + dbg!(res_f6); + + let in_f7 = (&x, &y); + dbg!(f7(in_f7)); + let res_f7 = df7(in_f7, (&1.0, &0.0)); + dbg!(res_f7); +} From 466bec90292a1b04a50a5889ab13fddd53c925ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 14 Jul 2025 17:07:01 +0000 Subject: [PATCH 0509/1900] Adjust autodiff actitivies for ScalarPair --- .../rustc_codegen_llvm/src/builder/autodiff.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index e2df3265f6f7d..a1cc4fdbdf67e 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -80,6 +80,23 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( continue; } } + + let pci = PseudoCanonicalInput { typing_env: TypingEnv::fully_monomorphized(), value: *ty }; + + let layout = match tcx.layout_of(pci) { + Ok(layout) => layout.layout, + Err(_) => { + bug!("failed to compute layout for type {:?}", ty); + } + }; + + match layout.backend_repr() { + rustc_abi::BackendRepr::ScalarPair(_, _) => { + new_activities.push(da[i].clone()); + new_positions.push(i + 1); + } + _ => {} + } } // now add the extra activities coming from slices // Reverse order to not invalidate the indices From 8dbd1b014ac43cc9c950e3b7e112b62d0963ec17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Tue, 15 Jul 2025 09:56:51 +0000 Subject: [PATCH 0510/1900] doc and move single branch match to an if let --- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index a1cc4fdbdf67e..a19a0d867ace8 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -90,12 +90,12 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; - match layout.backend_repr() { - rustc_abi::BackendRepr::ScalarPair(_, _) => { - new_activities.push(da[i].clone()); - new_positions.push(i + 1); - } - _ => {} + // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. + // Otherwise, the number of activities won't match the number of LLVM arguments and + // this will lead to errors when verifying the Enzyme call. + if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { + new_activities.push(da[i].clone()); + new_positions.push(i + 1); } } // now add the extra activities coming from slices From ef90d22ddad5f2f43689e17fde5f048bcd83fd5a Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 17 Sep 2025 20:04:57 +0800 Subject: [PATCH 0511/1900] update enzyme submodule --- src/tools/enzyme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/enzyme b/src/tools/enzyme index 58af4e9e6c047..09f4820b78e2d 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 58af4e9e6c047534ba059b12af17cecb8a2e9f9e +Subproject commit 09f4820b78e2d71b85a3278bbb41dc3a012e84dd From 0bf85d35ec9ed9cbffd274def09027c5fe9a8f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sun, 3 Aug 2025 11:12:34 +0000 Subject: [PATCH 0512/1900] Support ZST args --- .../rustc_codegen_llvm/src/builder/autodiff.rs | 11 ++++++++++- tests/ui/autodiff/zst.rs | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/ui/autodiff/zst.rs diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index a19a0d867ace8..78deffa3a7a6e 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -29,6 +29,7 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( let mut new_activities = vec![]; let mut new_positions = vec![]; + let mut del_activities = 0; for (i, ty) in sig.inputs().iter().enumerate() { if let Some(inner_ty) = ty.builtin_deref(true) { if inner_ty.is_slice() { @@ -90,12 +91,20 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; + // For ZST, just ignore and don't add its activity, as this arg won't be present + // in the LLVM passed to Enzyme. + // FIXME(Sa4dUs): Enforce ZST corresponding diff activity be `Const` + if layout.is_zst() { + del_activities += 1; + da.remove(i); + } + // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. // Otherwise, the number of activities won't match the number of LLVM arguments and // this will lead to errors when verifying the Enzyme call. if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { new_activities.push(da[i].clone()); - new_positions.push(i + 1); + new_positions.push(i + 1 - del_activities); } } // now add the extra activities coming from slices diff --git a/tests/ui/autodiff/zst.rs b/tests/ui/autodiff/zst.rs new file mode 100644 index 0000000000000..7b9b5f5f20bdc --- /dev/null +++ b/tests/ui/autodiff/zst.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +//@ build-pass + +// Check that differentiating functions with ZST args does not break + +#![feature(autodiff)] + +#[core::autodiff::autodiff_forward(fd_inner, Const, Dual)] +fn f(_zst: (), _x: &mut f64) {} + +fn fd(x: &mut f64, xd: &mut f64) { + fd_inner((), x, xd); +} + +fn main() {} From 923d1be6b6caec99a5eb46b3fb236f45f9e18233 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:06:38 +1000 Subject: [PATCH 0513/1900] Use `LLVMDIBuilderCreateMemberType` --- .../src/debuginfo/metadata.rs | 4 +-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 28 +++++++++---------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 11 -------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0903ddab28549..f2000311ae476 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1034,10 +1034,10 @@ fn create_member_type<'ll, 'tcx>( type_di_node: &'ll DIType, ) -> &'ll DIType { unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( + llvm::LLVMDIBuilderCreateMemberType( DIB(cx), owner, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 71d8b7d25fe72..2549296d12c69 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1943,6 +1943,20 @@ unsafe extern "C" { UniqueId: *const c_uchar, // See "PTR_LEN_STR". UniqueIdLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + SizeInBits: u64, + AlignInBits: u32, + OffsetInBits: u64, + Flags: DIFlags, + Ty: &'ll Metadata, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2288,20 +2302,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - SizeInBits: u64, - AlignInBits: u32, - OffsetInBits: u64, - Flags: DIFlags, - Ty: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b4ca641674f08..af2469eeb9347 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1088,17 +1088,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( StringRef(UniqueId, UniqueIdLen))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags, - LLVMMetadataRef Ty) { - return wrap(unwrap(Builder)->createMemberType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, - fromRust(Flags), unwrapDI(Ty))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, From bb21dbeac79da68586d584ed2c7f8e87286fd8a8 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:52:44 +1000 Subject: [PATCH 0514/1900] Use `LLVMDIBuilderCreateStaticMemberType` --- .../src/debuginfo/metadata/enums/cpp_like.rs | 65 +++++++++++++------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 26 ++++---- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 --- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index a5c808957410e..28945d6a2d7d4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; use smallvec::smallvec; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::dwarf_const::DW_TAG_const_type; use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; @@ -378,20 +378,17 @@ fn build_single_variant_union_fields<'ll, 'tcx>( variant_struct_type_wrapper_di_node, None, ), - unsafe { - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), - enum_type_di_node, - TAG_FIELD_NAME.as_c_char_ptr(), - TAG_FIELD_NAME.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - variant_names_type_di_node, - visibility_flags, - Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), - tag_base_type_align.bits() as u32, - ) - } + create_static_member_type( + cx, + enum_type_di_node, + TAG_FIELD_NAME, + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + variant_names_type_di_node, + visibility_flags, + Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), + tag_base_type_align, + ), ] } @@ -576,21 +573,20 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( // directly inspected via the debugger visualizer - which compares it to the `tag` value // (whose type is not modified at all) it shouldn't cause any real problems. let (t_di, align) = if name == ASSOC_CONST_DISCR_NAME { - (type_di_node_, align.bits() as u32) + (type_di_node_, align) } else { let ty_u64 = Ty::new_uint(cx.tcx, ty::UintTy::U64); - (type_di_node(cx, ty_u64), Align::EIGHT.bits() as u32) + (type_di_node(cx, ty_u64), Align::EIGHT) }; // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member let field_type = llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), + create_static_member_type( + cx, wrapper_struct_type_di_node, - name.as_c_char_ptr(), - name.len(), + name, unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, field_type, @@ -975,3 +971,30 @@ fn variant_struct_wrapper_type_name(variant_index: VariantIdx) -> Cow<'static, s .map(|&s| Cow::from(s)) .unwrap_or_else(|| format!("Variant{}", variant_index.as_usize()).into()) } + +fn create_static_member_type<'ll>( + cx: &CodegenCx<'ll, '_>, + scope: &'ll llvm::Metadata, + name: &str, + file: &'ll llvm::Metadata, + line_number: c_uint, + ty: &'ll llvm::Metadata, + flags: DIFlags, + value: Option<&'ll llvm::Value>, + align: Align, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreateStaticMemberType( + DIB(cx), + scope, + name.as_ptr(), + name.len(), + file, + line_number, + ty, + flags, + value, + align.bits() as c_uint, + ) + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2549296d12c69..a157a9b8c2610 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1957,6 +1957,19 @@ unsafe extern "C" { Flags: DIFlags, Ty: &'ll Metadata, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStaticMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + Type: &'ll Metadata, + Flags: DIFlags, + ConstantVal: Option<&'ll Value>, + AlignInBits: u32, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2317,19 +2330,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - Flags: DIFlags, - val: Option<&'a Value>, - AlignInBits: u32, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( Builder: &DIBuilder<'a>, Tag: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index af2469eeb9347..1b2303f5e7451 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1103,16 +1103,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - LLVMDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) { - return wrap(unwrap(Builder)->createStaticMemberType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, unwrapDI(Ty), fromRust(Flags), - unwrap(val), llvm::dwarf::DW_TAG_member, AlignInBits)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Type) { From 002771ab5c3a329038843d578d5f83c0289a959d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:36:59 +1000 Subject: [PATCH 0515/1900] Use `LLVMDIBuilderCreateQualifiedType` --- .../src/debuginfo/metadata/enums/cpp_like.rs | 8 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 14 ++++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 7 ------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 28945d6a2d7d4..4ecc3086e1bdf 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -567,7 +567,8 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( let build_assoc_const = |name: &str, type_di_node_: &'ll DIType, value: u64, - align: Align| unsafe { + align: Align| + -> &'ll llvm::Metadata { // FIXME: Currently we force all DISCR_* values to be u64's as LLDB seems to have // problems inspecting other value types. Since DISCR_* is typically only going to be // directly inspected via the debugger visualizer - which compares it to the `tag` value @@ -580,8 +581,9 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( }; // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member - let field_type = - llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); + let field_type = unsafe { + llvm::LLVMDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di) + }; create_static_member_type( cx, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a157a9b8c2610..7c8a3261bb825 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1970,6 +1970,14 @@ unsafe extern "C" { ConstantVal: Option<&'ll Value>, AlignInBits: u32, ) -> &'ll Metadata; + + /// Creates a "qualified type" in the C/C++ sense, by adding modifiers + /// like `const` or `volatile`. + pub(crate) fn LLVMDIBuilderCreateQualifiedType<'ll>( + Builder: &DIBuilder<'ll>, + Tag: c_uint, // (DWARF tag, e.g. `DW_TAG_const_type`) + Type: &'ll Metadata, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2330,12 +2338,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Type: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 1b2303f5e7451..eff1b75620597 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1103,13 +1103,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, - LLVMMetadataRef Type) { - return wrap( - unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 6b51f7c07666b1b1446201477ad887fdfa26e08a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:36:59 +1000 Subject: [PATCH 0516/1900] Use `LLVMDIBuilderCreateTypedef` --- .../src/debuginfo/metadata.rs | 9 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 23 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 -------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f2000311ae476..aa8b8bd152dcc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -821,14 +821,15 @@ fn build_basic_type_di_node<'ll, 'tcx>( }; let typedef_di_node = unsafe { - llvm::LLVMRustDIBuilderCreateTypedef( + llvm::LLVMDIBuilderCreateTypedef( DIB(cx), ty_di_node, - typedef_name.as_c_char_ptr(), + typedef_name.as_ptr(), typedef_name.len(), unknown_file_metadata(cx), - 0, - None, + 0, // (no line number) + None, // (no scope) + 0u32, // (no alignment specified) ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7c8a3261bb825..1124ebc3d44b0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -24,7 +24,7 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ - DIArray, DIBasicType, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, + DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; @@ -1978,6 +1978,17 @@ unsafe extern "C" { Tag: c_uint, // (DWARF tag, e.g. `DW_TAG_const_type`) Type: &'ll Metadata, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateTypedef<'ll>( + Builder: &DIBuilder<'ll>, + Type: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Scope: Option<&'ll Metadata>, + AlignInBits: u32, // (optional; default is 0) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2313,16 +2324,6 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( - Builder: &DIBuilder<'a>, - Type: &'a DIBasicType, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Scope: Option<&'a DIScope>, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index eff1b75620597..64151962321fd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1064,16 +1064,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Scope) { - return wrap(unwrap(Builder)->createTypedef( - unwrap(Type), StringRef(Name, NameLen), unwrap(File), - LineNo, unwrapDIPtr(Scope))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From ea8baccbb15fd58afcde959eed7dc73741dd626d Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 0517/1900] rustc_codegen_llvm: Name major version of LLVM It makes LLVM version comparison clearer. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 8461c8b03d5a4..393361a1afe2a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -228,6 +228,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::with_dependencies( "sse4.2", @@ -260,7 +261,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version - ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, + ("loongarch32" | "loongarch64", "32s") if major < 21 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, From d9f67cbb8be4500ba73bb30177c05153cbc9424a Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 0518/1900] rustc_codegen_llvm: Simplify `arch` conversion This commit simplifies construction of `arch` from `sess.target.arch`. It also preserves a reference to `sess.target.arch` as `raw_arch` to make this function future proof. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 393361a1afe2a..6849cd241644a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -217,16 +217,13 @@ impl<'a> IntoIterator for LLVMFeature<'a> { /// Rust can also be build with an external precompiled version of LLVM which might lead to failures /// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option> { - let arch = if sess.target.arch == "x86_64" { - "x86" - } else if sess.target.arch == "arm64ec" { - "aarch64" - } else if sess.target.arch == "sparc64" { - "sparc" - } else if sess.target.arch == "powerpc64" { - "powerpc" - } else { - &*sess.target.arch + let raw_arch = &*sess.target.arch; + let arch = match raw_arch { + "x86_64" => "x86", + "arm64ec" => "aarch64", + "sparc64" => "sparc", + "powerpc64" => "powerpc", + _ => raw_arch, }; let (major, _, _) = get_version(); match (arch, s) { From a1a3cd043815a8b6e5faf53f33002d8dec1b2f30 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 0519/1900] rustc_codegen_llvm: Reorder conversion cases For maintainability, this commit reorders target feature conversion cases by the architecture. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6849cd241644a..45c5c9aa5514d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -227,15 +227,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::with_dependencies( - "sse4.2", - smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], - )), - ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), - ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), - ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), - ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), - ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), ("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")), ("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")), ("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")), @@ -259,13 +250,22 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version ("loongarch32" | "loongarch64", "32s") if major < 21 => None, + ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), + ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), + ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( + "sse4.2", + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], + )), + ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), + ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), + ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), + ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), + ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( From 6f813e887af44d74e9bcfdb207ecab407037a939 Mon Sep 17 00:00:00 2001 From: Reuben Cruise Date: Mon, 1 Sep 2025 16:53:50 +0100 Subject: [PATCH 0520/1900] Adds AArch64 GCS support - Adds option to rustc config to enable GCS - Passes `guarded-control-stack` flag to llvm if enabled --- compiler/rustc_codegen_llvm/src/attributes.rs | 5 ++++- compiler/rustc_codegen_llvm/src/context.rs | 9 ++++++++- compiler/rustc_interface/src/tests.rs | 3 ++- compiler/rustc_session/src/config.rs | 1 + compiler/rustc_session/src/options.rs | 3 ++- .../src/compiler-flags/branch-protection.md | 1 + 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 573c51a95398b..dcf6b9454978c 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -407,13 +407,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); // For non-naked functions, set branch protection attributes on aarch64. - if let Some(BranchProtection { bti, pac_ret }) = + if let Some(BranchProtection { bti, pac_ret, gcs }) = cx.sess().opts.unstable_opts.branch_protection { assert!(cx.sess().target.arch == "aarch64"); if bti { to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); } + if gcs { + to_add.push(llvm::CreateAttrString(cx.llcx, "guarded-control-stack")); + } if let Some(PacRet { leaf, pc, key }) = pac_ret { if pc { to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr")); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 4a8ea11a3a834..057f525c76f03 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -370,7 +370,8 @@ pub(crate) unsafe fn create_module<'ll>( ); } - if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { + if let Some(BranchProtection { bti, pac_ret, gcs }) = sess.opts.unstable_opts.branch_protection + { if sess.target.arch == "aarch64" { llvm::add_module_flag_u32( llmod, @@ -403,6 +404,12 @@ pub(crate) unsafe fn create_module<'ll>( "sign-return-address-with-bkey", u32::from(pac_opts.key == PAuthKey::B), ); + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Min, + "guarded-control-stack", + gcs.into(), + ); } else { bug!( "branch-protection used on non-AArch64 target; \ diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 7730bddc0f12d..800f5efee41bf 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -772,7 +772,8 @@ fn test_unstable_options_tracking_hash() { branch_protection, Some(BranchProtection { bti: true, - pac_ret: Some(PacRet { leaf: true, pc: true, key: PAuthKey::B }) + pac_ret: Some(PacRet { leaf: true, pc: true, key: PAuthKey::B }), + gcs: true, }) ); tracked!(codegen_backend, Some("abc".to_string())); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c9765..8d9f424f1fe37 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1615,6 +1615,7 @@ pub struct PacRet { pub struct BranchProtection { pub bti: bool, pub pac_ret: Option, + pub gcs: bool, } pub(crate) const fn default_lib_output() -> CrateType { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 69facde693689..7956a06f6a985 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -866,7 +866,7 @@ mod desc { pub(crate) const parse_polonius: &str = "either no value or `legacy` (the default), or `next`"; pub(crate) const parse_stack_protector: &str = "one of (`none` (default), `basic`, `strong`, or `all`)"; - pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `pac-ret`, followed by a combination of `pc`, `b-key`, or `leaf`"; + pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)"; pub(crate) const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; pub(crate) const parse_remap_path_scope: &str = @@ -1903,6 +1903,7 @@ pub mod parse { Some(pac) => pac.pc = true, _ => return false, }, + "gcs" => slot.gcs = true, _ => return false, }; } diff --git a/src/doc/unstable-book/src/compiler-flags/branch-protection.md b/src/doc/unstable-book/src/compiler-flags/branch-protection.md index f0cc44a07f30d..c15567dcac2ee 100644 --- a/src/doc/unstable-book/src/compiler-flags/branch-protection.md +++ b/src/doc/unstable-book/src/compiler-flags/branch-protection.md @@ -13,6 +13,7 @@ It takes some combination of the following values, separated by a `,`. - `leaf` - Enable pointer authentication for all functions, including leaf functions. - `b-key` - Sign return addresses with key B, instead of the default key A. - `bti` - Enable branch target identification. +- `gcs` - Enable guarded control stack support. `leaf`, `b-key` and `pc` are only valid if `pac-ret` was previously specified. For example, `-Z branch-protection=bti,pac-ret,leaf` is valid, but From 2972ae5e539162778f83529599040d5bbc4adca4 Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:39:43 -0400 Subject: [PATCH 0521/1900] rename_lint: unchecked_duration_subtraction to unchecked_time_subtraction --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 2 +- clippy_config/src/conf.rs | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/instant_subtraction.rs | 6 +- tests/ui/manual_instant_elapsed.fixed | 2 +- tests/ui/manual_instant_elapsed.rs | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 154 +++++++++--------- ...fixed => unchecked_time_subtraction.fixed} | 10 +- ...ction.rs => unchecked_time_subtraction.rs} | 10 +- ...derr => unchecked_time_subtraction.stderr} | 12 +- 14 files changed, 111 insertions(+), 98 deletions(-) rename tests/ui/{unchecked_duration_subtraction.fixed => unchecked_time_subtraction.fixed} (64%) rename tests/ui/{unchecked_duration_subtraction.rs => unchecked_time_subtraction.rs} (58%) rename tests/ui/{unchecked_duration_subtraction.stderr => unchecked_time_subtraction.stderr} (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be02143722..2144d68ae1527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6702,6 +6702,7 @@ Released 2018-09-13 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unbuffered_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#unbuffered_bytes [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction +[`unchecked_time_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction [`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1d..5502e6ff5e833 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -894,7 +894,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) -* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [`unchecked_time_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad82..ae994815f63a5 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -790,7 +790,7 @@ define_Conf! { transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, - unchecked_duration_subtraction, + unchecked_time_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, unnested_or_patterns, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1b19a8851edd4..7734f2266308f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -227,7 +227,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, + crate::instant_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 88aebc3e6a168..80b74f50223a4 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -184,6 +184,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::transmute_int_to_float", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + #[clippy::version = "1.90.0"] + ("clippy::unchecked_duration_subtraction", "clippy::unchecked_time_subtraction"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 13117f60abd52..5fe26e3e3e0c3 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); /// ``` #[clippy::version = "1.67.0"] - pub UNCHECKED_DURATION_SUBTRACTION, + pub UNCHECKED_TIME_SUBTRACTION, pedantic, "finds unchecked subtraction of a 'Duration' from an 'Instant'" } @@ -74,7 +74,7 @@ impl InstantSubtraction { } } -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); impl LateLintPass<'_> for InstantSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -141,7 +141,7 @@ fn print_unchecked_duration_subtraction_sugg( span_lint_and_sugg( cx, - UNCHECKED_DURATION_SUBTRACTION, + UNCHECKED_TIME_SUBTRACTION, expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 187802bb76c9e..a04c601e08c10 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 61e14e5a3d9d3..7c67f6acf85de 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index ff81c64260274..fdd851414746b 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_time_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b5d5d07e639a0..591c8ca53ac22 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_duration_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 2487dfc8eba44..b54fec8c57949 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,436 +8,442 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` + --> tests/ui/rename.rs:135:9 + | +LL | #![warn(clippy::unchecked_duration_subtraction)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` + error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:135:9 + --> tests/ui/rename.rs:137:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:136:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 73 previous errors +error: aborting due to 74 previous errors diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed similarity index 64% rename from tests/ui/unchecked_duration_subtraction.fixed rename to tests/ui/unchecked_time_subtraction.fixed index bddffe44ac4d0..0850226eeea74 100644 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::unchecked_duration_subtraction)] +#![warn(clippy::unchecked_time_subtraction)] use std::time::{Duration, Instant}; @@ -7,14 +7,14 @@ fn main() { let second = Duration::from_secs(3); let _ = _first.checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now().checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs similarity index 58% rename from tests/ui/unchecked_duration_subtraction.rs rename to tests/ui/unchecked_time_subtraction.rs index bb0f712396424..a2dbfd1c1fadf 100644 --- a/tests/ui/unchecked_duration_subtraction.rs +++ b/tests/ui/unchecked_time_subtraction.rs @@ -1,4 +1,4 @@ -#![warn(clippy::unchecked_duration_subtraction)] +#![warn(clippy::unchecked_time_subtraction)] use std::time::{Duration, Instant}; @@ -7,14 +7,14 @@ fn main() { let second = Duration::from_secs(3); let _ = _first - second; - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now() - Duration::from_secs(5); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = _first - Duration::from_secs(5); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now() - second; - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr similarity index 75% rename from tests/ui/unchecked_duration_subtraction.stderr rename to tests/ui/unchecked_time_subtraction.stderr index be291c320e689..da0d51dbaef16 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -1,26 +1,26 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:9:13 + --> tests/ui/unchecked_time_subtraction.rs:9:13 | LL | let _ = _first - second; | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` | - = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]` + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:12:13 + --> tests/ui/unchecked_time_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:15:13 + --> tests/ui/unchecked_time_subtraction.rs:15:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:18:13 + --> tests/ui/unchecked_time_subtraction.rs:18:13 | LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` From b0566b949b1e68319ed9852e637c5a77aae45186 Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:41:55 -0400 Subject: [PATCH 0522/1900] rename module: instant_substraction to time_substraction --- clippy_lints/src/declared_lints.rs | 4 ++-- clippy_lints/src/lib.rs | 4 ++-- .../src/{instant_subtraction.rs => time_subtraction.rs} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename clippy_lints/src/{instant_subtraction.rs => time_subtraction.rs} (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7734f2266308f..6a2833e5f86eb 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -226,8 +226,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::instant_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..1ab6eef5e57d2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -176,7 +176,6 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; -mod instant_subtraction; mod int_plus_one; mod integer_division_remainder_used; mod invalid_upcast_comparisons; @@ -357,6 +356,7 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; +mod time_subtraction; mod to_digit_is_some; mod to_string_trait_impl; mod toplevel_ref_arg; @@ -718,7 +718,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/time_subtraction.rs similarity index 100% rename from clippy_lints/src/instant_subtraction.rs rename to clippy_lints/src/time_subtraction.rs From f296de4665cec2f1200496a0432667a150322f9b Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:47:24 -0400 Subject: [PATCH 0523/1900] Implement Duration - Duration Renames InstantSubtraction to UncheckedTimeSubtraction --- clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/time_subtraction.rs | 111 ++++++++++++++---- tests/ui/unchecked_time_subtraction.fixed | 17 +++ tests/ui/unchecked_time_subtraction.rs | 17 +++ tests/ui/unchecked_time_subtraction.stderr | 26 +++- .../unchecked_time_subtraction_unfixable.rs | 22 ++++ ...nchecked_time_subtraction_unfixable.stderr | 29 +++++ 8 files changed, 201 insertions(+), 27 deletions(-) create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.rs create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6a2833e5f86eb..e1b6ee41967a3 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -226,8 +226,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, @@ -703,6 +701,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1ab6eef5e57d2..65c9a947b578d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -718,7 +718,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(time_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index 5fe26e3e3e0c3..fde8c3d9a9a76 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,12 +1,12 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Lints subtraction between an `Instant` and a `Duration`. + /// Lints subtraction between an `Instant` and a `Duration`, or between two `Duration` values. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to @@ -51,32 +51,38 @@ declare_clippy_lint! { /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now() - Duration::from_secs(5); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1 - dur2; /// ``` /// /// Use instead: /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1.checked_sub(dur2); /// ``` #[clippy::version = "1.67.0"] pub UNCHECKED_TIME_SUBTRACTION, pedantic, - "finds unchecked subtraction of a 'Duration' from an 'Instant'" + "finds unchecked subtraction involving 'Duration' or 'Instant'" } -pub struct InstantSubtraction { +pub struct UncheckedTimeSubtraction { msrv: Msrv, } -impl InstantSubtraction { +impl UncheckedTimeSubtraction { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv } } } -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); +impl_lint_pass!(UncheckedTimeSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); -impl LateLintPass<'_> for InstantSubtraction { +impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -85,21 +91,54 @@ impl LateLintPass<'_> for InstantSubtraction { lhs, rhs, ) = expr.kind - && let typeck = cx.typeck_results() - && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) { + let typeck = cx.typeck_results(); + let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) - && let Some(sugg) = Sugg::hir_opt(cx, rhs) - { - print_manual_instant_elapsed_sugg(cx, expr, sugg); - } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + // Instant::now() - instant + if is_instant_now_call(cx, lhs) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } + // instant - duration + else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (instant - dur1) - dur2, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + ); + } else { + // instant - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + // For chained subtraction like (dur1 - dur2) - dur3, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction between 'Duration' values", + ); + } else { + // duration - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } } } } @@ -115,6 +154,25 @@ fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { } } +/// Returns true if this subtraction is part of a chain like `(a - b) - c` +fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { + if let ExprKind::Binary(op, inner_lhs, inner_rhs) = &lhs.kind + && matches!(op.node, BinOpKind::Sub) + { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(inner_lhs); + let right_ty = typeck.expr_ty(inner_rhs); + is_time_type(cx, left_ty) && is_time_type(cx, right_ty) + } else { + false + } +} + +/// Returns true if the type is Duration or Instant +fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) +} + fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { span_lint_and_sugg( cx, @@ -133,19 +191,26 @@ fn print_unchecked_duration_subtraction_sugg( right_expr: &Expr<'_>, expr: &Expr<'_>, ) { - let mut applicability = Applicability::MachineApplicable; + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(left_expr); - let ctxt = expr.span.ctxt(); - let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; - let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; + let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + "unchecked subtraction of a 'Duration' from an 'Instant'" + } else { + "unchecked subtraction between 'Duration' values" + }; + + let mut applicability = Applicability::MachineApplicable; + let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); + let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); span_lint_and_sugg( cx, UNCHECKED_TIME_SUBTRACTION, expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", + lint_msg, "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap()"), + format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), applicability, ); } diff --git a/tests/ui/unchecked_time_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed index 0850226eeea74..2f923fef4c25d 100644 --- a/tests/ui/unchecked_time_subtraction.fixed +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -17,4 +17,21 @@ fn main() { let _ = Instant::now().checked_sub(second).unwrap(); //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1.checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = second.checked_sub(dur1).unwrap(); + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = (2 * dur1).checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_time_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs index a2dbfd1c1fadf..cf727f62aafa7 100644 --- a/tests/ui/unchecked_time_subtraction.rs +++ b/tests/ui/unchecked_time_subtraction.rs @@ -17,4 +17,21 @@ fn main() { let _ = Instant::now() - second; //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1 - dur2; + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10) - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = second - dur1; + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = 2 * dur1 - dur2; + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr index da0d51dbaef16..7a39712269cf7 100644 --- a/tests/ui/unchecked_time_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -25,5 +25,29 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` -error: aborting due to 4 previous errors +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:25:13 + | +LL | let _ = dur1 - dur2; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:28:13 + | +LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:31:13 + | +LL | let _ = second - dur1; + | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:35:13 + | +LL | let _ = 2 * dur1 - dur2; + | ^^^^^^^^^^^^^^^ help: try: `(2 * dur1).checked_sub(dur2).unwrap()` + +error: aborting due to 8 previous errors diff --git a/tests/ui/unchecked_time_subtraction_unfixable.rs b/tests/ui/unchecked_time_subtraction_unfixable.rs new file mode 100644 index 0000000000000..4b6a5ca156209 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unchecked_time_subtraction)] +//@no-rustfix + +use std::time::{Duration, Instant}; + +fn main() { + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + let dur3 = Duration::from_secs(1); + + // Chained Duration subtraction - should lint without suggestion due to complexity + let _ = dur1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction + + // Chained Instant - Duration subtraction - should lint without suggestion due to complexity + let instant1 = Instant::now(); + + let _ = instant1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr new file mode 100644 index 0000000000000..c25c112b06ce0 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -0,0 +1,29 @@ +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^ help: try: `instant1.checked_sub(dur2).unwrap()` + +error: aborting due to 4 previous errors + From 6e101637fa9485e71e77217ab32cbfb0f013e6e5 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Wed, 17 Sep 2025 15:46:23 +0200 Subject: [PATCH 0524/1900] Add the `rust-analyzer.semanticHighlighting.comments.enable` configuration value --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 14 ++++++ .../crates/ide/src/syntax_highlighting.rs | 4 ++ .../ide/src/syntax_highlighting/html.rs | 42 +++++++++------- .../ide/src/syntax_highlighting/inject.rs | 2 + .../highlight_comments_disabled.html | 48 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 35 +++++++++++++- .../crates/rust-analyzer/src/config.rs | 8 ++++ .../docs/book/src/configuration_generated.md | 11 +++++ .../rust-analyzer/editors/code/package.json | 10 ++++ 9 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index cddf5f04f2442..481c2312176b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -703,6 +703,20 @@ impl Analysis { }) } + /// Computes syntax highlighting for the given file. + pub fn highlight_as_html_with_config( + &self, + config: HighlightConfig, + file_id: FileId, + rainbow: bool, + ) -> Cancellable { + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight_as_html_with_config(&self.db, config, file_id, rainbow) + }) + } + /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable { // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f98770805a451..4e43387f8d9da 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -35,6 +35,7 @@ use crate::{ }; pub(crate) use html::highlight_as_html; +pub(crate) use html::highlight_as_html_with_config; #[derive(Debug, Clone, Copy)] pub struct HlRange { @@ -47,6 +48,8 @@ pub struct HlRange { pub struct HighlightConfig { /// Whether to highlight strings pub strings: bool, + /// Whether to highlight comments + pub comments: bool, /// Whether to highlight punctuation pub punctuation: bool, /// Whether to specialize punctuation highlights @@ -588,6 +591,7 @@ fn descend_token( fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool { match &mut highlight.tag { HlTag::StringLiteral if !config.strings => return false, + HlTag::Comment if !config.comments => return false, // If punctuation is disabled, make the macro bang part of the macro call again. tag @ HlTag::Punctuation(HlPunct::MacroBang) => { if !config.macro_bang { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 9fd807f031f1f..358ac9b4ef352 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -10,7 +10,12 @@ use crate::{ syntax_highlighting::{HighlightConfig, highlight}, }; -pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { +pub(crate) fn highlight_as_html_with_config( + db: &RootDatabase, + config: HighlightConfig, + file_id: FileId, + rainbow: bool, +) -> String { let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) @@ -27,21 +32,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo ) } - let hl_ranges = highlight( - db, - HighlightConfig { - strings: true, - punctuation: true, - specialize_punctuation: true, - specialize_operator: true, - operator: true, - inject_doc_comment: true, - macro_bang: true, - syntactic_name_ref_highlighting: false, - }, - file_id.file_id(db), - None, - ); + let hl_ranges = highlight(db, config, file_id.file_id(db), None); let text = file.to_string(); let mut buf = String::new(); buf.push_str(STYLE); @@ -66,6 +57,25 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo buf } +pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { + highlight_as_html_with_config( + db, + HighlightConfig { + strings: true, + comments: true, + punctuation: true, + specialize_punctuation: true, + specialize_operator: true, + operator: true, + inject_doc_comment: true, + macro_bang: true, + syntactic_name_ref_highlighting: false, + }, + file_id, + rainbow, + ) +} + //FIXME: like, real html escaping fn html_escape(text: &str) -> String { text.replace('<', "<").replace('>', ">") diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index abe7be8c68881..4bb7308024144 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -80,6 +80,7 @@ pub(super) fn ra_fixture( .highlight( HighlightConfig { syntactic_name_ref_highlighting: false, + comments: true, punctuation: true, operator: true, strings: true, @@ -250,6 +251,7 @@ pub(super) fn doc_comment( db, HighlightConfig { syntactic_name_ref_highlighting: true, + comments: true, punctuation: true, operator: true, strings: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html new file mode 100644 index 0000000000000..4607448bebaaa --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html @@ -0,0 +1,48 @@ + + +

// This is a regular comment
+/// This is a doc comment
+fn main() {
+    // Another comment
+    println!("Hello, world!");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index dd359326c61d6..8198701d68432 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -9,6 +9,7 @@ use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture}; const HL_CONFIG: HighlightConfig = HighlightConfig { strings: true, + comments: true, punctuation: true, specialize_punctuation: true, specialize_operator: true, @@ -1220,16 +1221,25 @@ fn foo(x: &fn(&dyn Trait)) {} /// Highlights the code given by the `ra_fixture` argument, renders the /// result as HTML, and compares it with the HTML file given as `snapshot`. /// Note that the `snapshot` file is overwritten by the rendered HTML. -fn check_highlighting( +fn check_highlighting_with_config( #[rust_analyzer::rust_fixture] ra_fixture: &str, + config: HighlightConfig, expect: ExpectFile, rainbow: bool, ) { let (analysis, file_id) = fixture::file(ra_fixture.trim()); - let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); + let actual_html = &analysis.highlight_as_html_with_config(config, file_id, rainbow).unwrap(); expect.assert_eq(actual_html) } +fn check_highlighting( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect: ExpectFile, + rainbow: bool, +) { + check_highlighting_with_config(ra_fixture, HL_CONFIG, expect, rainbow) +} + #[test] fn benchmark_syntax_highlighting_long_struct() { if skip_slow_tests() { @@ -1435,3 +1445,24 @@ fn main() { false, ); } + +#[test] +fn test_comment_highlighting_disabled() { + // Test that comments are not highlighted when disabled + check_highlighting_with_config( + r#" +// This is a regular comment +/// This is a doc comment +fn main() { + // Another comment + println!("Hello, world!"); +} +"#, + HighlightConfig { + comments: false, // Disable comment highlighting + ..HL_CONFIG + }, + expect_file!["./test_data/highlight_comments_disabled.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2252185a3aa3..8fe0a5d2eb662 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -382,6 +382,13 @@ config_data! { /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, + /// Use semantic tokens for comments. + /// + /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. + /// By disabling semantic tokens for comments, other grammars can be used to highlight + /// their contents. + semanticHighlighting_comments_enable: bool = true, + /// Inject additional highlighting into doc comments. /// /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra @@ -1968,6 +1975,7 @@ impl Config { pub fn highlighting_config(&self) -> HighlightConfig { HighlightConfig { strings: self.semanticHighlighting_strings_enable().to_owned(), + comments: self.semanticHighlighting_comments_enable().to_owned(), punctuation: self.semanticHighlighting_punctuation_enable().to_owned(), specialize_punctuation: self .semanticHighlighting_punctuation_specialization_enable() diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 9a51212462db5..50dacd88f4072 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1378,6 +1378,17 @@ Enables the use of rustfmt's unstable range formatting command for the available on a nightly build. +## rust-analyzer.semanticHighlighting.comments.enable {#semanticHighlighting.comments.enable} + +Default: `true` + +Use semantic tokens for comments. + +In some editors (e.g. vscode) semantic tokens override other highlighting grammars. +By disabling semantic tokens for comments, other grammars can be used to highlight +their contents. + + ## rust-analyzer.semanticHighlighting.doc.comment.inject.enable {#semanticHighlighting.doc.comment.inject.enable} Default: `true` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2b2e25e11c88a..1d27a12053552 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2850,6 +2850,16 @@ } } }, + { + "title": "Semantic Highlighting", + "properties": { + "rust-analyzer.semanticHighlighting.comments.enable": { + "markdownDescription": "Use semantic tokens for comments.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for comments, other grammars can be used to highlight\ntheir contents.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Semantic Highlighting", "properties": { From e04567c363e1f0417bf8bf24830c2bc536020582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Wed, 17 Sep 2025 13:58:17 +0000 Subject: [PATCH 0525/1900] Check ZST via `PassMode` --- .../src/builder/autodiff.rs | 20 ++++++++++++++++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 ++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 78deffa3a7a6e..566877f4a1ec5 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,8 +3,9 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; use rustc_middle::{bug, ty}; +use rustc_target::callconv::PassMode; use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; @@ -16,9 +17,12 @@ use crate::value::Value; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, - fn_ty: Ty<'tcx>, + instance: Instance<'tcx>, + typing_env: TypingEnv<'tcx>, da: &mut Vec, ) { + let fn_ty = instance.ty(tcx, typing_env); + if !matches!(fn_ty.kind(), ty::FnDef(..)) { bug!("expected fn def for autodiff, got {:?}", fn_ty); } @@ -27,6 +31,13 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( // All we do is decide how to handle the arguments. let sig = fn_ty.fn_sig(tcx).skip_binder(); + // FIXME(Sa4dUs): pass proper varargs once we have support for differentiating variadic functions + let Ok(fn_abi) = + tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty()))) + else { + bug!("failed to get fn_abi of instance with empty varargs"); + }; + let mut new_activities = vec![]; let mut new_positions = vec![]; let mut del_activities = 0; @@ -91,10 +102,13 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; + let pass_mode = &fn_abi.args[i].mode; + // For ZST, just ignore and don't add its activity, as this arg won't be present // in the LLVM passed to Enzyme. + // Some targets pass ZST indirectly in the C ABI, in that case, handle it as a normal arg // FIXME(Sa4dUs): Enforce ZST corresponding diff activity be `Const` - if layout.is_zst() { + if *pass_mode == PassMode::Ignore { del_activities += 1; da.remove(i); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 06c3d8ed6bc2d..9e6e760649120 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1198,7 +1198,8 @@ fn codegen_autodiff<'ll, 'tcx>( adjust_activity_to_abi( tcx, - fn_source.ty(tcx, TypingEnv::fully_monomorphized()), + fn_source, + TypingEnv::fully_monomorphized(), &mut diff_attrs.input_activity, ); From 28b0e4e15e0ffd0c5f747f94dadb7a243566449d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 17 Sep 2025 22:57:25 +0800 Subject: [PATCH 0526/1900] Fix applicable on variant field for change_visibility Enum variant fields do not allow visibility Example --- ```rust enum Foo { Variant($0String), } ``` **Before this PR**: ```rust enum Foo { Variant(pub(crate) String), } ``` **After this PR**: Assist not applicable --- .../ide-assists/src/handlers/change_visibility.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs index 9b9f0c4522ed2..7119d5b9c23eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs @@ -65,11 +65,13 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { if field.visibility().is_some() { return None; } + check_is_not_variant(&field)?; (vis_offset(field.syntax()), field_name.syntax().text_range()) } else if let Some(field) = ctx.find_node_at_offset::() { if field.visibility().is_some() { return None; } + check_is_not_variant(&field)?; (vis_offset(field.syntax()), field.syntax().text_range()) } else { return None; @@ -134,6 +136,11 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { None } +fn check_is_not_variant(field: &impl AstNode) -> Option<()> { + let kind = field.syntax().parent()?.parent()?.kind(); + (kind != SyntaxKind::VARIANT).then_some(()) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -239,6 +246,13 @@ mod tests { ); } + #[test] + fn not_applicable_for_enum_variant_fields() { + check_assist_not_applicable(change_visibility, r"pub enum Foo { Foo1($0i32) }"); + + check_assist_not_applicable(change_visibility, r"pub enum Foo { Foo1 { $0n: i32 } }"); + } + #[test] fn change_visibility_target() { check_assist_target(change_visibility, "$0fn foo() {}", "fn"); From 301092e249f3773fd41c8af7170acd88a2681c89 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 17:36:17 +0200 Subject: [PATCH 0527/1900] Remove automatic feature freeze comment The feature freeze period is over. --- .github/workflows/feature_freeze.yml | 45 --------------------- book/src/README.md | 4 -- book/src/SUMMARY.md | 1 - book/src/development/adding_lints.md | 3 -- book/src/development/feature_freeze.md | 55 -------------------------- 5 files changed, 108 deletions(-) delete mode 100644 .github/workflows/feature_freeze.yml delete mode 100644 book/src/development/feature_freeze.md diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml deleted file mode 100644 index 5b139e767007b..0000000000000 --- a/.github/workflows/feature_freeze.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Feature freeze check - -on: - pull_request_target: - types: - - opened - branches: - - master - paths: - - 'clippy_lints/src/declared_lints.rs' - -jobs: - auto-comment: - runs-on: ubuntu-latest - - permissions: - pull-requests: write - - # Do not in any case add code that runs anything coming from the content - # of the pull request, as malicious code would be able to access the private - # GitHub token. - steps: - - name: Add freeze warning comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(echo "**Seems that you are trying to add a new lint!**\n\ - \n\ - We are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and [focusing on bugfixes](https://github.com/rust-lang/rust-clippy/issues/15086).\n\ - \n\ - Thanks a lot for your contribution, and sorry for the inconvenience.\n\ - \n\ - With ❤ from the Clippy team.\n\ - \n\ - @rustbot note Feature-freeze\n\ - @rustbot blocked\n\ - @rustbot label +A-lint" - ) - curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "Content-Type: application/vnd.github.raw+json" \ - -X POST \ - --data "{\"body\":\"${COMMENT}\"}" \ - "https://api.github.com/repos/$%7BGITHUB_REPOSITORY%7D/issues/$%7BPR_NUMBER%7D/comments" diff --git a/book/src/README.md b/book/src/README.md index db73b49ecc24e..5d2c3972b060a 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,9 +1,5 @@ # Clippy -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md) - ----- - [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index b66c3481e4930..39fe7358ed87a 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,7 +13,6 @@ - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index a42a298374465..2b89e94cf8f4f 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -1,8 +1,5 @@ # Adding a new lint -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md) - - You are probably here because you want to add a new lint to Clippy. If this is the first time you're contributing to Clippy, this document guides you through creating an example lint from scratch. diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md deleted file mode 100644 index 260cb136cc075..0000000000000 --- a/book/src/development/feature_freeze.md +++ /dev/null @@ -1,55 +0,0 @@ -# IMPORTANT: FEATURE FREEZE - -This is a temporary notice. - -From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed -except already open ones. Every feature-adding PR opened in between those dates will be moved into a -milestone to be reviewed separately at another time. - -We do this because of the long backlog of bugs that need to be addressed -in order to continue being the state-of-the-art linter that Clippy has become known for being. - -## For contributors - -If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open -bugs of all levels of difficulty that you can address instead! - -We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible -use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a -refinement period. - -If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends, -adding additional load into our reviewing schedules. - -## I want to help, what can I do - -Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period! -If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step! - -To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that -issue with `@rustbot claim`. - -As a general metric and always taking into account your skill and knowledge level, you can use this guide: - -- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level -debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that -improves a lot developer workflows! - -- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. -Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs - -- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error -when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar -easy-to-happen occurrences. - -- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" -identifying the root of a false positive and making an exception for those cases. - -Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a -trench coat. - -[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 -[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug -[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 -[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive -[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 From e84b5ca1dd40d09b261b0a495c6cbd6a5d8cb194 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 18:51:42 +0200 Subject: [PATCH 0528/1900] Do not look for `Cargo.toml` inside `target` This test, which checks that we do not define new profiles directly in Clippy's multiple `Cargo.toml` files, must not look inside `target` as `lintcheck` might place some third-party sources there. Of course those third-party sources are allowed to define profiles in their `Cargo.toml`. --- tests/no-profile-in-cargo-toml.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs index 2ad9bfb75dee8..1f8c4fae9b31f 100644 --- a/tests/no-profile-in-cargo-toml.rs +++ b/tests/no-profile-in-cargo-toml.rs @@ -17,6 +17,9 @@ fn no_profile_in_cargo_toml() { // keep it fast and simple. for entry in WalkDir::new(".") .into_iter() + // Do not recurse into `target` as lintcheck might put some sources (and their + // `Cargo.toml`) there. + .filter_entry(|e| e.file_name() != "target") .filter_map(Result::ok) .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) { From b2b43b25a845ff624d4b4c0efb604ed3b684a3bd Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 17 Sep 2025 12:59:07 -0400 Subject: [PATCH 0529/1900] Add space after brace in `Box<[T]>::new_uninit_slice` example --- library/alloc/src/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 98c9f6b51ab86..7d2dd12c6eaea 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -640,7 +640,7 @@ impl Box<[T]> { /// values[0].write(1); /// values[1].write(2); /// values[2].write(3); - /// let values = unsafe {values.assume_init() }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` From 205189c8c767b3910a630f79b156417b82b3558b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 11:19:51 +0200 Subject: [PATCH 0530/1900] port `#[rustc_coherence_is_core]` to the new attribute parsing infrastructure --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/attributes/traits.rs | 8 -------- compiler/rustc_attr_parsing/src/context.rs | 12 ++++++------ compiler/rustc_hir/src/attrs/data_structures.rs | 6 +++--- compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 4611de4445997..0a340cd5e9330 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -174,3 +174,12 @@ impl NoArgsAttributeParser for NoStdParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; } + +pub(crate) struct RustcCoherenceIsCoreParser; + +impl NoArgsAttributeParser for RustcCoherenceIsCoreParser { + const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index c756bce96e28f..ced3bcad2293a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -149,14 +149,6 @@ impl NoArgsAttributeParser for AllowIncoherentImplParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; } -pub(crate) struct CoherenceIsCoreParser; -impl NoArgsAttributeParser for CoherenceIsCoreParser { - const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; -} - pub(crate) struct FundamentalParser; impl NoArgsAttributeParser for FundamentalParser { const PATH: &[Symbol] = &[sym::fundamental]; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 58b1329248412..ee5b7322b0247 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,7 +26,7 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, - RecursionLimitParser, TypeLengthLimitParser, + RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; @@ -61,10 +61,10 @@ use crate::attributes::stability::{ }; use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ - AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser, - DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, - ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, - TypeConstParser, UnsafeSpecializationMarkerParser, + AllowIncoherentImplParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser, + DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, + PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, + UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; @@ -206,7 +206,6 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, Single>, Single>, Single>, @@ -234,6 +233,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 04b144abd03df..0784675b177a0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -444,9 +444,6 @@ pub enum AttributeKind { span: Span, }, - /// Represents `#[rustc_coherence_is_core]`. - CoherenceIsCore, - /// Represents `#[rustc_coinductive]`. Coinductive(Span), @@ -639,6 +636,9 @@ pub enum AttributeKind { /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_coherence_is_core]` + RustcCoherenceIsCore(Span), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index cb4feeb05f1a0..563e7a58c6d5e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -26,7 +26,6 @@ impl AttributeKind { AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, BodyStability { .. } => No, - CoherenceIsCore => No, Coinductive(..) => No, Cold(..) => No, Confusables { .. } => Yes, @@ -84,6 +83,7 @@ impl AttributeKind { RecursionLimit { .. } => No, Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcCoherenceIsCore(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 4370816d38e5f..430cd329408f5 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -370,7 +370,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn hir_rustc_coherence_is_core(self) -> bool { - find_attr!(self.hir_krate_attrs(), AttributeKind::CoherenceIsCore) + find_attr!(self.hir_krate_attrs(), AttributeKind::RustcCoherenceIsCore(..)) } pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 60f575cb84415..4d5a8447695be 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -246,7 +246,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Repr { .. } | AttributeKind::Cold(..) | AttributeKind::ExportName { .. } - | AttributeKind::CoherenceIsCore | AttributeKind::Fundamental | AttributeKind::Optimize(..) | AttributeKind::LinkSection { .. } @@ -278,6 +277,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::NoStd { .. } | AttributeKind::ObjcClass { .. } | AttributeKind::ObjcSelector { .. } + | AttributeKind::RustcCoherenceIsCore(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 1d8971cf4e5f954c7fc9a2611dcf359a832f4c7a Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 16 Sep 2025 10:34:04 +0000 Subject: [PATCH 0531/1900] Cleanup `FnDecl::inner_full_print` --- src/librustdoc/display.rs | 6 +- src/librustdoc/html/format.rs | 107 +++++++++++++++++++--------------- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/librustdoc/display.rs b/src/librustdoc/display.rs index aa0fad265208d..db868c5c9a8f3 100644 --- a/src/librustdoc/display.rs +++ b/src/librustdoc/display.rs @@ -10,7 +10,7 @@ pub(crate) trait Joined: IntoIterator { /// /// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt) /// was already called (`joined`'s API doesn't allow it be called more than once). - fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result; + fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result; } impl Joined for I @@ -18,12 +18,12 @@ where I: IntoIterator, T: Display, { - fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result { + fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result { let mut iter = self.into_iter(); let Some(first) = iter.next() else { return Ok(()) }; first.fmt(f)?; for item in iter { - f.write_str(sep)?; + sep.fmt(f)?; item.fmt(f)?; } Ok(()) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 493fdc6fb1b33..8c75f301841f3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1264,6 +1264,7 @@ impl std::fmt::Write for WriteCounter { } // Implements Display by emitting the given number of spaces. +#[derive(Clone, Copy)] struct Indent(usize); impl Display for Indent { @@ -1275,6 +1276,37 @@ impl Display for Indent { } } +impl clean::Parameter { + fn print(&self, cx: &Context<'_>) -> impl fmt::Display { + fmt::from_fn(move |f| { + if let Some(self_ty) = self.to_receiver() { + match self_ty { + clean::SelfTy => f.write_str("self"), + clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { + f.write_str(if f.alternate() { "&" } else { "&" })?; + if let Some(lt) = lifetime { + write!(f, "{lt} ", lt = lt.print())?; + } + write!(f, "{mutability}self", mutability = mutability.print_with_space()) + } + _ => { + f.write_str("self: ")?; + self_ty.print(cx).fmt(f) + } + } + } else { + if self.is_const { + write!(f, "const ")?; + } + if let Some(name) = self.name { + write!(f, "{name}: ")?; + } + self.type_.print(cx).fmt(f) + } + }) + } +} + impl clean::FnDecl { pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { @@ -1333,63 +1365,42 @@ impl clean::FnDecl { f: &mut fmt::Formatter<'_>, cx: &Context<'_>, ) -> fmt::Result { - let amp = if f.alternate() { "&" } else { "&" }; + f.write_char('(')?; - write!(f, "(")?; - if let Some(n) = line_wrapping_indent - && !self.inputs.is_empty() - { - write!(f, "\n{}", Indent(n + 4))?; - } + if !self.inputs.is_empty() { + let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4)); - let last_input_index = self.inputs.len().checked_sub(1); - for (i, param) in self.inputs.iter().enumerate() { - if let Some(selfty) = param.to_receiver() { - match selfty { - clean::SelfTy => { - write!(f, "self")?; - } - clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { - write!(f, "{amp}")?; - if let Some(lt) = lifetime { - write!(f, "{lt} ", lt = lt.print())?; - } - write!(f, "{mutability}self", mutability = mutability.print_with_space())?; - } - _ => { - write!(f, "self: ")?; - selfty.print(cx).fmt(f)?; - } - } - } else { - if param.is_const { - write!(f, "const ")?; - } - if let Some(name) = param.name { - write!(f, "{name}: ")?; + if let Some(indent) = line_wrapping_indent { + write!(f, "\n{indent}")?; + } + + let sep = fmt::from_fn(|f| { + if let Some(indent) = line_wrapping_indent { + write!(f, ",\n{indent}") + } else { + f.write_str(", ") } - param.type_.print(cx).fmt(f)?; + }); + + self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?; + + if line_wrapping_indent.is_some() { + writeln!(f, ",")? } - match (line_wrapping_indent, last_input_index) { - (_, None) => (), - (None, Some(last_i)) if i != last_i => write!(f, ", ")?, - (None, Some(_)) => (), - (Some(n), Some(last_i)) if i != last_i => write!(f, ",\n{}", Indent(n + 4))?, - (Some(_), Some(_)) => writeln!(f, ",")?, + + if self.c_variadic { + match line_wrapping_indent { + None => write!(f, ", ...")?, + Some(indent) => writeln!(f, "{indent}...")?, + }; } } - if self.c_variadic { - match line_wrapping_indent { - None => write!(f, ", ...")?, - Some(n) => writeln!(f, "{}...", Indent(n + 4))?, - }; + if let Some(n) = line_wrapping_indent { + write!(f, "{}", Indent(n))? } - match line_wrapping_indent { - None => write!(f, ")")?, - Some(n) => write!(f, "{})", Indent(n))?, - }; + f.write_char(')')?; self.print_output(cx).fmt(f) } From c1f782919bac1071a514eafb87e2463be25fc99e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 19:23:28 +0200 Subject: [PATCH 0532/1900] `match_as_ref`: do not lint if other arm is not `None => None` --- clippy_lints/src/manual_option_as_slice.rs | 6 ++--- clippy_utils/src/lib.rs | 16 +++++++----- tests/ui/match_as_ref.fixed | 30 ++++++++++++++++++++++ tests/ui/match_as_ref.rs | 30 ++++++++++++++++++++++ 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 922db174e3d49..b036e78cdedc5 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -60,8 +60,8 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } match expr.kind { ExprKind::Match(scrutinee, [arm1, arm2], _) => { - if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) - || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) { check_as_ref(cx, scrutinee, span, self.msrv); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5223cd872a680..3e48397fbedbe 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -329,13 +329,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } -// Checks if arm has the form `None => None` -pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, +/// Checks if the `pat` is `None`. +pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) - ) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) +} + +/// Checks if `arm` has the form `None => None`. +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + is_none_pattern(cx, arm.pat) + && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) } /// Checks if the given `QPath` belongs to a type alias. diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 8c07076af4a49..a39f0c9299bd5 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -41,3 +41,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 3a5b1227331e7..049928167901a 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -53,3 +53,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} From 9cdc09b604f2a3bdab334911bac99d23ed556c23 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 16 Sep 2025 09:48:08 +0200 Subject: [PATCH 0533/1900] readdir for freebsd --- .../src/shims/unix/freebsd/foreign_items.rs | 6 +- src/tools/miri/src/shims/unix/fs.rs | 60 ++++++++++++------- .../src/shims/unix/linux/foreign_items.rs | 2 +- .../src/shims/unix/solarish/foreign_items.rs | 2 +- src/tools/miri/tests/pass/shims/fs.rs | 3 +- 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 9e247053fbcdb..413df85ee3aae 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -159,7 +159,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } - + "readdir" | "readdir@FBSD_1.0" => { + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let result = this.readdir64("dirent", dirp)?; + this.write_scalar(result, dest)?; + } // Miscellaneous "__error" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index f9bcacf64c412..22bec9bd83941 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -900,14 +900,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn linux_solarish_readdir64( - &mut self, - dirent_type: &str, - dirp_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + fn readdir64(&mut self, dirent_type: &str, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos" | "freebsd") { panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os); } @@ -926,6 +922,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let entry = match open_dir.read_dir.next() { Some(Ok(dir_entry)) => { + // If the host is a Unix system, fill in the inode number with its real value. + // If not, use 0 as a fallback value. + #[cfg(unix)] + let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry); + #[cfg(not(unix))] + let ino = 0u64; + // Write the directory entry into a newly allocated buffer. // The name is written with write_bytes, while the rest of the // dirent64 (or dirent) struct is written using write_int_fields. @@ -947,6 +950,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // pub d_reclen: c_ushort, // pub d_name: [c_char; 3], // } + // + // On FreeBSD: + // pub struct dirent{ + // pub d_fileno: uint32_t, + // pub d_reclen: uint16_t, + // pub d_type: uint8_t, + // pub d_namlen: uint8_t, + // pub d_name: [c_char; 256] + // } let mut name = dir_entry.file_name(); // not a Path as there are no separators! name.push("\0"); // Add a NUL terminator @@ -965,31 +977,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MiriMemoryKind::Runtime.into(), AllocInit::Uninit, )?; - let entry: Pointer = entry.into(); + let entry = this.ptr_to_mplace(entry.into(), dirent_layout); - // If the host is a Unix system, fill in the inode number with its real value. - // If not, use 0 as a fallback value. - #[cfg(unix)] - let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry); - #[cfg(not(unix))] - let ino = 0u64; - - let file_type = this.file_type_to_d_type(dir_entry.file_type())?; + // Write common fields + let ino_name = + if this.tcx.sess.target.os == "freebsd" { "d_fileno" } else { "d_ino" }; this.write_int_fields_named( - &[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())], - &this.ptr_to_mplace(entry, dirent_layout), + &[(ino_name, ino.into()), ("d_reclen", size.into())], + &entry, )?; - if let Some(d_type) = this - .try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")? - { + // Write "optional" fields. + if let Some(d_off) = this.try_project_field_named(&entry, "d_off")? { + this.write_null(&d_off)?; + } + + if let Some(d_namlen) = this.try_project_field_named(&entry, "d_namlen")? { + this.write_int(name_len.strict_sub(1), &d_namlen)?; + } + + let file_type = this.file_type_to_d_type(dir_entry.file_type())?; + if let Some(d_type) = this.try_project_field_named(&entry, "d_type")? { this.write_int(file_type, &d_type)?; } - let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this); + // The name is not a normal field, we already computed the offset above. + let name_ptr = entry.ptr().wrapping_offset(Size::from_bytes(d_name_offset), this); this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?; - Some(entry) + Some(entry.ptr()) } None => { // end of stream: return NULL diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index e7e0c3b6ecd96..79052698f4bad 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "readdir64" => { let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let result = this.linux_solarish_readdir64("dirent64", dirp)?; + let result = this.readdir64("dirent64", dirp)?; this.write_scalar(result, dest)?; } "sync_file_range" => { diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index d7033a65fe22b..31269bf00c948 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "readdir" => { let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let result = this.linux_solarish_readdir64("dirent", dirp)?; + let result = this.readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 0748007b3c058..022dcc5dcbafa 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -28,8 +28,7 @@ fn main() { test_from_raw_os_error(); test_file_clone(); // Windows file handling is very incomplete. - // FIXME: read_dir broken on FreeBSD (https://github.com/rust-lang/miri/issues/4587) - if cfg!(not(windows)) && !cfg!(target_os = "freebsd") { + if cfg!(not(windows)) { test_file_set_len(); test_file_sync(); test_rename(); From 987f9603f9907bdcea9911517f216554f3c5cd4d Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 17 Sep 2025 14:10:13 -0400 Subject: [PATCH 0534/1900] Sort safe intrinsic list --- .../rustc_hir_analysis/src/check/intrinsic.rs | 120 +++++++++--------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index aa2d27ab80945..5fd04427496d4 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -64,83 +64,87 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. - sym::abort + + // tidy-alphabetical-start + | sym::abort + | sym::add_with_overflow + | sym::aggregate_raw_ptr + | sym::align_of | sym::assert_inhabited - | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::assert_zero_valid + | sym::autodiff + | sym::bitreverse + | sym::black_box | sym::box_new | sym::breakpoint - | sym::size_of - | sym::align_of - | sym::needs_drop + | sym::bswap | sym::caller_location - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow | sym::carrying_mul_add - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::saturating_add - | sym::saturating_sub - | sym::rotate_left - | sym::rotate_right - | sym::ctpop + | sym::cold_path + | sym::const_eval_select + | sym::contract_check_ensures + | sym::contract_check_requires + | sym::contract_checks | sym::ctlz + | sym::ctpop | sym::cttz - | sym::bswap - | sym::bitreverse - | sym::three_way_compare | sym::discriminant_value - | sym::type_id - | sym::type_id_eq - | sym::select_unpredictable - | sym::cold_path - | sym::ptr_guaranteed_cmp - | sym::minnumf16 - | sym::minnumf32 - | sym::minnumf64 - | sym::minnumf128 - | sym::minimumf16 - | sym::minimumf32 - | sym::minimumf64 - | sym::minimumf128 - | sym::maxnumf16 - | sym::maxnumf32 - | sym::maxnumf64 - | sym::maxnumf128 + | sym::fadd_algebraic + | sym::fdiv_algebraic + | sym::fmul_algebraic + | sym::forget + | sym::frem_algebraic + | sym::fsub_algebraic + | sym::is_val_statically_known | sym::maximumf16 | sym::maximumf32 | sym::maximumf64 | sym::maximumf128 - | sym::rustc_peek - | sym::type_name - | sym::forget - | sym::black_box - | sym::variant_count - | sym::is_val_statically_known + | sym::maxnumf16 + | sym::maxnumf32 + | sym::maxnumf64 + | sym::maxnumf128 + | sym::minimumf16 + | sym::minimumf32 + | sym::minimumf64 + | sym::minimumf128 + | sym::minnumf16 + | sym::minnumf32 + | sym::minnumf64 + | sym::minnumf128 + | sym::mul_with_overflow + | sym::needs_drop + | sym::prefetch_read_data + | sym::prefetch_read_instruction + | sym::prefetch_write_data + | sym::prefetch_write_instruction + | sym::ptr_guaranteed_cmp | sym::ptr_mask - | sym::aggregate_raw_ptr | sym::ptr_metadata - | sym::ub_checks - | sym::contract_checks - | sym::contract_check_requires - | sym::contract_check_ensures - | sym::fadd_algebraic - | sym::fsub_algebraic - | sym::fmul_algebraic - | sym::fdiv_algebraic - | sym::frem_algebraic + | sym::rotate_left + | sym::rotate_right | sym::round_ties_even_f16 | sym::round_ties_even_f32 | sym::round_ties_even_f64 | sym::round_ties_even_f128 - | sym::autodiff - | sym::prefetch_read_data - | sym::prefetch_write_data - | sym::prefetch_read_instruction - | sym::prefetch_write_instruction - | sym::const_eval_select => hir::Safety::Safe, + | sym::rustc_peek + | sym::saturating_add + | sym::saturating_sub + | sym::select_unpredictable + | sym::size_of + | sym::sub_with_overflow + | sym::three_way_compare + | sym::type_id + | sym::type_id_eq + | sym::type_name + | sym::ub_checks + | sym::variant_count + | sym::wrapping_add + | sym::wrapping_mul + | sym::wrapping_sub + // tidy-alphabetical-end + => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; From b17213a87b66d94b0d7f7202cd0f56e74bcad857 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 17 Sep 2025 22:04:28 +0200 Subject: [PATCH 0535/1900] about-this-guide.md: improve a bit --- src/doc/rustc-dev-guide/src/about-this-guide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index f1a406a1c29be..4f5733ae0821a 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -48,9 +48,9 @@ In addition, many of the ideas discussed throughout this guide are idealized des that are not fully realized yet. All this makes keeping this guide completely up to date on everything very hard! -The Guide itself is of course open-source as well, -and the sources can be found at the [GitHub repository]. -If you find any mistakes in the guide, please file an issue about it. +The guide itself is of course open source as well, +and the sources are hosted on [a GitHub repository]. +If you find any mistakes in the guide, please file an issue. Even better, open a PR with a correction! If you do contribute to the guide, @@ -105,7 +105,7 @@ You might also find the following sites useful: [cheatsheet]: https://bors.rust-lang.org/ [Miri]: https://github.com/rust-lang/miri [@bors]: https://github.com/bors -[GitHub repository]: https://github.com/rust-lang/rustc-dev-guide/ +[a GitHub repository]: https://github.com/rust-lang/rustc-dev-guide/ [rustc API docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle [Forge]: https://forge.rust-lang.org/ [compiler-team]: https://github.com/rust-lang/compiler-team/ From 80db273b6445cdbbac943c047ca4a10ee5c7ea45 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 17 Sep 2025 16:28:46 -0400 Subject: [PATCH 0536/1900] Remove jujutsu directory from search tools. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 36a4cdc1c3528..666c4ceac4dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ helper.txt # mdbook generated output /book/book + +# Remove jujutsu directory from search tools +.jj From 912785d966395d36cd2cebe5d0959316fdd28cef Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 18:07:35 +0000 Subject: [PATCH 0537/1900] Lint overlapping assignments in MIR. --- compiler/rustc_middle/src/mir/visit.rs | 17 +++++++ compiler/rustc_mir_transform/src/dest_prop.rs | 18 +------- compiler/rustc_mir_transform/src/lint.rs | 44 ++++++++++++++----- tests/ui/mir/lint/assignment-overlap.rs | 2 +- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 07a15b3cd18a7..81df239dee42d 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1475,3 +1475,20 @@ impl PlaceContext { } } } + +/// Small utility to visit places and locals without manually implementing a full visitor. +pub struct VisitPlacesWith(pub F); + +impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith +where + F: FnMut(Place<'tcx>, PlaceContext), +{ + fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { + (self.0)(local.into(), ctxt); + } + + fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { + (self.0)(*place, ctxt); + self.visit_projection(place.as_ref(), ctxt, location); + } +} diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index abd1cd4e35ac0..74c22ff10c198 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -141,7 +141,7 @@ use rustc_data_structures::union_find::UnionFind; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{IndexVec, newtype_index}; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; @@ -503,22 +503,6 @@ impl TwoStepIndex { } } -struct VisitPlacesWith(F); - -impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith -where - F: FnMut(Place<'tcx>, PlaceContext), -{ - fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { - (self.0)(local.into(), ctxt); - } - - fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { - (self.0)(*place, ctxt); - self.visit_projection(place.as_ref(), ctxt, location); - } -} - /// Add points depending on the result of the given dataflow analysis. fn save_as_intervals<'tcx>( elements: &DenseLocationMap, diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index f472c7cb493d0..2ab49645dc44f 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::DenseBitSet; -use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals}; @@ -79,15 +79,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match &statement.kind { StatementKind::Assign(box (dest, rvalue)) => { - 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", - ); - } + let forbid_aliasing = match rvalue { + Rvalue::Use(..) + | Rvalue::CopyForDeref(..) + | Rvalue::Repeat(..) + | Rvalue::Aggregate(..) + | Rvalue::Cast(..) + | Rvalue::ShallowInitBox(..) + | Rvalue::WrapUnsafeBinder(..) => true, + Rvalue::ThreadLocalRef(..) + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::BinaryOp(..) + | Rvalue::Ref(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) => false, + }; + // The sides of an assignment must not alias. + if forbid_aliasing { + VisitPlacesWith(|src: Place<'tcx>, _| { + if *dest == src + || (dest.local == src.local + && !dest.is_indirect() + && !src.is_indirect()) + { + self.fail( + location, + format!( + "encountered `{statement:?}` statement with overlapping memory" + ), + ); + } + }) + .visit_rvalue(rvalue, location); } } StatementKind::StorageLive(local) => { diff --git a/tests/ui/mir/lint/assignment-overlap.rs b/tests/ui/mir/lint/assignment-overlap.rs index bbc140904671e..5d1213a77585f 100644 --- a/tests/ui/mir/lint/assignment-overlap.rs +++ b/tests/ui/mir/lint/assignment-overlap.rs @@ -13,7 +13,7 @@ pub fn main() { let a: [u8; 1024]; { a = a; //~ ERROR broken MIR - //~^ ERROR encountered `Assign` statement with overlapping memory + //~^ ERROR encountered `_1 = copy _1` statement with overlapping memory Return() } } From 8d7ec96c00994e3e4da652201792ae2040c979c1 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 12 Sep 2025 18:35:21 -0400 Subject: [PATCH 0538/1900] add `[const] PartialEq` bound to `PartialOrd` This change is included for discussion purposes. The PartialOrd bound on PartialEq is not strictly necessary. It is, rather, logical: anything which is orderable should by definition have equality. Is the same true for constness? Should every type which is const orderable also have const equality? --- library/core/src/cmp.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 95896ab144187..7f369d19c3d12 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1351,7 +1351,9 @@ pub macro Ord($item:item) { #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub const trait PartialOrd: PartialEq + PointeeSized { +pub const trait PartialOrd: + [const] PartialEq + PointeeSized +{ /// This method returns an ordering between `self` and `other` values if one exists. /// /// # Examples From f90075862346c78b4c0c373e48fb8929168d277e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Sep 2025 14:47:21 -0700 Subject: [PATCH 0539/1900] std: Fix WASI implementation of `remove_dir_all` This commit is a change to the WASI-specific implementation of the `std::fs::remove_dir_all` function. Specifically it changes how directory entries are read of a directory-being-deleted to specifically buffer them all into a `Vec` before actually proceeding to delete anything. This is necessary to fix an interaction with how the WASIp1 `fd_readdir` API works to have everything work out in the face of mutations while reading a directory. The basic problem is that `fd_readdir`, the WASIp1 API for reading directories, is not a stateful read of a directory but instead a "seekable" read of a directory. Its `cookie` argument enables seeking anywhere within the directory at any time to read further entries. Native host implementations do not have this ability, however, which means that this seeking property must be achieved by re-reading the directory. The problem with this is that WASIp1 has under-specified semantics around what should happen if a directory is mutated between two calls to `fd_readdir`. In essence there's not really any possible implementation in hosts except to read the entire directory and support seeking through the already-read list. This implementation is not possible in the WASIp1-to-WASIp2 adapter that is primarily used to create components for the `wasm32-wasip2` target where it has constrained memory requirements and can't buffer up arbitrarily sized directories. The WASIp1 API definitions are effectively "dead" now at the standards level meaning that `fd_readdir` won't be changing nor will a replacement be coming. For the `wasm32-wasip2` target this will get fixed once filesystem APIs are updated to use WASIp2 directly instead of WASIp1, making this buffering unnecessary. In essence while this is a hack it's sort of the least invasive thing that works everywhere for now. I don't think this is viable to fix in hosts so guests compiled to wasm are going to have to work around it by not relying on any guarantees about what happens to a directory if it's mutated between reads. --- library/std/src/sys/fs/wasi.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index b65d86de12a3d..0b65b9cb389df 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -848,7 +848,14 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // Iterate over all the entries in this directory, and travel recursively if // necessary - for entry in ReadDir::new(fd, dummy_root) { + // + // Note that all directory entries for this directory are read first before + // any removal is done. This works around the fact that the WASIp1 API for + // reading directories is not well-designed for handling mutations between + // invocations of reading a directory. By reading all the entries at once + // this ensures that, at least without concurrent modifications, it should + // be possible to delete everything. + for entry in ReadDir::new(fd, dummy_root).collect::>() { let entry = entry?; let path = crate::str::from_utf8(&entry.name).map_err(|_| { io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") From 9ecca51bbe151c4ce0e374c46d1a0bfd1ec63d49 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 16 Sep 2025 17:08:14 -0500 Subject: [PATCH 0540/1900] Remove ImplSubject --- compiler/rustc_infer/src/infer/at.rs | 19 +------------------ compiler/rustc_middle/src/hir/mod.rs | 9 +-------- compiler/rustc_middle/src/ty/mod.rs | 6 ------ compiler/rustc_middle/src/ty/relate.rs | 24 ------------------------ src/librustdoc/html/render/mod.rs | 10 +++++----- 5 files changed, 7 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ad19cdef4e752..70e3d7dc9fef0 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -28,7 +28,7 @@ use relate::lattice::{LatticeOp, LatticeOpKind}; use rustc_middle::bug; use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate; -use rustc_middle::ty::{Const, ImplSubject, TypingMode}; +use rustc_middle::ty::{Const, TypingMode}; use super::*; use crate::infer::relate::type_relating::TypeRelating; @@ -304,23 +304,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } -impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> { - fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { - match (a, b) { - (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { - ToTrace::to_trace(cause, trait_ref_a, trait_ref_b) - } - (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { - ToTrace::to_trace(cause, ty_a, ty_b) - } - (ImplSubject::Trait(_), ImplSubject::Inherent(_)) - | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { - bug!("can not trace TraitRef and Ty"); - } - } - } -} - impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { TypeTrace { diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 67bc89692ff79..9e3162785f4b5 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -18,7 +18,7 @@ use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; -use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; +use crate::ty::TyCtxt; /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. @@ -154,13 +154,6 @@ impl<'tcx> TyCtxt<'tcx> { LocalModDefId::new_unchecked(id) } - pub fn impl_subject(self, def_id: DefId) -> EarlyBinder<'tcx, ImplSubject<'tcx>> { - match self.impl_trait_ref(def_id) { - Some(t) => t.map_bound(ImplSubject::Trait), - None => self.type_of(def_id).map_bound(ImplSubject::Inherent), - } - } - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). pub fn is_foreign_item(self, def_id: impl Into) -> bool { self.opt_parent(def_id.into()) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d4c001f625e11..0ffef393a33bd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -254,12 +254,6 @@ pub struct ImplTraitHeader<'tcx> { pub constness: hir::Constness, } -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] -pub enum ImplSubject<'tcx> { - Trait(TraitRef<'tcx>), - Inherent(Ty<'tcx>), -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] #[derive(TypeFoldable, TypeVisitable)] pub enum Asyncness { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index dc1d60f3d43c1..2f96970af4788 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -7,30 +7,6 @@ use crate::ty::{self as ty, Ty, TyCtxt}; pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult, T>; -impl<'tcx> Relate> for ty::ImplSubject<'tcx> { - #[inline] - fn relate>>( - relation: &mut R, - a: ty::ImplSubject<'tcx>, - b: ty::ImplSubject<'tcx>, - ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> { - match (a, b) { - (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => { - let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; - Ok(ty::ImplSubject::Trait(trait_ref)) - } - (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => { - let ty = Ty::relate(relation, ty_a, ty_b)?; - Ok(ty::ImplSubject::Inherent(ty)) - } - (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_)) - | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => { - bug!("can not relate TraitRef and Ty"); - } - } - } -} - impl<'tcx> Relate> for Ty<'tcx> { #[inline] fn relate>>( diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b4ef47d1e2694..6d684449b6d2e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2454,11 +2454,11 @@ fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String { (ty, Some(ty::TraitRef::new(tcx, trait_, [ty]))) } ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => { - match tcx.impl_subject(impl_id).skip_binder() { - ty::ImplSubject::Trait(trait_ref) => { - (trait_ref.args[0].expect_ty(), Some(trait_ref)) - } - ty::ImplSubject::Inherent(ty) => (ty, None), + if let Some(trait_ref) = tcx.impl_trait_ref(impl_id) { + let trait_ref = trait_ref.skip_binder(); + (trait_ref.self_ty(), Some(trait_ref)) + } else { + (tcx.type_of(impl_id).skip_binder(), None) } } }; From 26cf3eee16d982ca66da4d103513f08da82f7ca5 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 8 May 2025 02:53:00 -0400 Subject: [PATCH 0541/1900] Rework `module_inception` * Don't check for repetition if the previous module crosses a body boundary. * Don't take a snippet of the entire item. * Check each item's visibility once. * Use `is_from_proc_macro` before linting --- clippy_lints/src/item_name_repetitions.rs | 180 ++++++++++++++-------- tests/ui/module_inception.rs | 9 ++ 2 files changed, 121 insertions(+), 68 deletions(-) diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 945bb84708f85..76f5fdfaa8dcf 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,11 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; -use clippy_utils::is_bool; -use clippy_utils::macros::span_is_local; -use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -158,7 +156,8 @@ declare_clippy_lint! { } pub struct ItemNameRepetitions { - modules: Vec<(Symbol, String, OwnerId)>, + /// The module path the lint pass is in. + modules: Vec, enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, @@ -167,6 +166,17 @@ pub struct ItemNameRepetitions { allowed_prefixes: FxHashSet, } +struct ModInfo { + name: Symbol, + name_camel: String, + /// Does this module have the `pub` visibility modifier. + is_public: bool, + /// How many bodies are between this module and the current lint pass position. + /// + /// Only the most recently seen module is updated when entering/exiting a body. + in_body_count: u32, +} + impl ItemNameRepetitions { pub fn new(conf: &'static Conf) -> Self { Self { @@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_> } impl LateLintPass<'_> for ItemNameRepetitions { - fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { - let Some(_ident) = item.kind.ident() else { return }; - - let last = self.modules.pop(); - assert!(last.is_some()); + fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + if matches!(item.kind, ItemKind::Mod(..)) { + let prev = self.modules.pop(); + debug_assert!(prev.is_some()); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let Some(ident) = item.kind.ident() else { return }; - - let item_name = ident.name.as_str(); - let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) - && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules - // constants don't have surrounding modules - && !mod_camel.is_empty() - { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + fn check_body(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count += 1; + } + } - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + fn check_body_post(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count -= 1; + } + } - let both_are_public = - cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + let ident = match item.kind { + ItemKind::Mod(ident, _) => { + if let [.., prev] = &*self.modules + && prev.name == ident.name + && prev.in_body_count == 0 + && (!self.allow_private_module_inception || prev.is_public) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + ident + }, - if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name is the same as its containing module's name", - ); - } + ItemKind::Enum(ident, _, def) => { + if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { + self.check_variants(cx, item, &def); + } + ident + }, + ItemKind::Struct(ident, _, data) => { + if let VariantData::Struct { fields, .. } = data + && !ident.span.in_external_macro(cx.tcx.sess.source_map()) + { + self.check_fields(cx, item, fields); + } + ident + }, - let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _)); - if both_are_public && item_camel.len() > mod_camel.len() && !is_macro { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); + ItemKind::Const(ident, ..) + | ItemKind::ExternCrate(_, ident) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) + | ItemKind::TraitAlias(ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Use(_, UseKind::Single(ident)) => ident, + + ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) | ItemKind::Use(..) => return, + }; - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); + let item_name = ident.name.as_str(); + let item_camel = to_camel_case(item_name); - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( + if let [.., prev] = &*self.modules + && prev.is_public + && prev.in_body_count == 0 + && !item.span.from_expansion() + && !matches!(item.kind, ItemKind::Macro(..)) + && cx.tcx.visibility(item.owner_id).is_public() + { + if !self.allow_exact_repetitions && item_camel == prev.name_camel { + if !is_from_proc_macro(cx, item) { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + } else if item_camel.len() > prev.name_camel.len() { + if let Some(s) = item_camel.strip_prefix(&prev.name_camel) + && let Some(c) = s.chars().next() + && (c == '_' || c.is_uppercase() || c.is_numeric()) + { + if !is_from_proc_macro(cx, item) { + span_lint( cx, MODULE_NAME_REPETITIONS, ident.span, "item name starts with its containing module's name", - ), - _ => (), + ); } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + } else if let Some(s) = item_camel.strip_suffix(&prev.name_camel) + && !self.is_allowed_prefix(s) + && !is_from_proc_macro(cx, item) { span_lint( cx, @@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } - if span_is_local(item.span) { - match item.kind { - ItemKind::Enum(_, _, def) => { - self.check_variants(cx, item, &def); - }, - ItemKind::Struct(_, _, VariantData::Struct { fields, .. }) => { - self.check_fields(cx, item, fields); - }, - _ => (), - } + if matches!(item.kind, ItemKind::Mod(..)) { + self.modules.push(ModInfo { + name: ident.name, + name_camel: item_camel, + is_public: cx.tcx.visibility(item.owner_id).is_public(), + in_body_count: 0, + }); } - self.modules.push((ident.name, item_camel, item.owner_id)); } } diff --git a/tests/ui/module_inception.rs b/tests/ui/module_inception.rs index 15b7fb8777700..5735dd5867d55 100644 --- a/tests/ui/module_inception.rs +++ b/tests/ui/module_inception.rs @@ -38,4 +38,13 @@ mod bar { mod bar {} } +mod with_inner_impl { + struct S; + impl S { + fn f() { + mod with_inner_impl {} + } + } +} + fn main() {} From b216cf34b1d4dfce1a44f99f7c58e64724439a8e Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 18 Sep 2025 00:46:27 +0000 Subject: [PATCH 0542/1900] Avoid invalidating from MirPatch::apply. --- compiler/rustc_mir_transform/src/patch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index c781d1a5324b7..f38881baf0f29 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -244,7 +244,7 @@ impl<'tcx> MirPatch<'tcx> { self.new_blocks.len(), body.basic_blocks.len() ); - let bbs = if self.term_patch_map.is_empty() && self.new_blocks.is_empty() { + let bbs = if self.term_patch_map.iter().all(Option::is_none) && self.new_blocks.is_empty() { body.basic_blocks.as_mut_preserves_cfg() } else { body.basic_blocks.as_mut() @@ -273,8 +273,8 @@ impl<'tcx> MirPatch<'tcx> { } debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta); loc.statement_index += delta; - let source_info = Self::source_info_for_index(&body[loc.block], loc); - body[loc.block] + let source_info = Self::source_info_for_index(&bbs[loc.block], loc); + bbs[loc.block] .statements .insert(loc.statement_index, Statement::new(source_info, stmt)); delta += 1; From 15ce2093d53e3274e79d55858baa82ccc9c09b02 Mon Sep 17 00:00:00 2001 From: Daniel Gulotta Date: Wed, 17 Sep 2025 20:39:20 -0700 Subject: [PATCH 0543/1900] test cases for option_if_let_else --- tests/ui/option_if_let_else.fixed | 10 ++++++ tests/ui/option_if_let_else.rs | 16 ++++++++++ tests/ui/option_if_let_else.stderr | 50 ++++++++++++++++++++---------- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 0f86de5646cdb..6ce067f5c2462 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -125,6 +125,16 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7aabd778f87e7..096d3aabf28db 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -152,6 +152,22 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + match r { + //~^ option_if_let_else + Ok(s) => s.to_owned(), + Err(_) => Vec::new(), + } +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + if let Ok(s) = r { s.to_owned() } + //~^ option_if_let_else + else { Vec::new() } +} + fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 2e2fe6f20492b..21a80ae038d8c 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -188,14 +188,32 @@ LL + true LL + }) | +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:157:5 + | +LL | / match r { +LL | | +LL | | Ok(s) => s.to_owned(), +LL | | Err(_) => Vec::new(), +LL | | } + | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:166:5 + | +LL | / if let Ok(s) = r { s.to_owned() } +LL | | +LL | | else { Vec::new() } + | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:13 + --> tests/ui/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:168:13 + --> tests/ui/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -217,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:197:13 + --> tests/ui/option_if_let_else.rs:213:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:202:13 + --> tests/ui/option_if_let_else.rs:218:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -245,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:242:13 + --> tests/ui/option_if_let_else.rs:258:13 | LL | let _ = match s { | _____________^ @@ -256,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:247:13 + --> tests/ui/option_if_let_else.rs:263:13 | LL | let _ = match Some(10) { | _____________^ @@ -267,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:254:13 + --> tests/ui/option_if_let_else.rs:270:13 | LL | let _ = match res { | _____________^ @@ -278,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:259:13 + --> tests/ui/option_if_let_else.rs:275:13 | LL | let _ = match res { | _____________^ @@ -289,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:264:13 + --> tests/ui/option_if_let_else.rs:280:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:282:17 + --> tests/ui/option_if_let_else.rs:298:17 | LL | let _ = match initial { | _________________^ @@ -306,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:290:17 + --> tests/ui/option_if_let_else.rs:306:17 | LL | let _ = match initial { | _________________^ @@ -317,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:314:24 + --> tests/ui/option_if_let_else.rs:330:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -329,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:321:19 + --> tests/ui/option_if_let_else.rs:337:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:372:22 + --> tests/ui/option_if_let_else.rs:388:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:378:13 + --> tests/ui/option_if_let_else.rs:394:13 | LL | let _ = match res { | _____________^ @@ -351,5 +369,5 @@ LL | | Err(_) => String::new(), LL | | }; | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors From 8b0a25486040b91ede0120ff5a797517e0973895 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 13:46:46 +1000 Subject: [PATCH 0544/1900] Move target machine command-line quoting from C++ to Rust --- .../src/back/command_line_args.rs | 37 +++++++++++++++++++ .../src/back/command_line_args/tests.rs | 25 +++++++++++++ compiler/rustc_codegen_llvm/src/back/mod.rs | 1 + .../src/back/owned_target_machine.rs | 14 +++---- compiler/rustc_codegen_llvm/src/back/write.rs | 30 ++++++--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 ++- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 28 ++++---------- 7 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/back/command_line_args.rs create mode 100644 compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs new file mode 100644 index 0000000000000..b14713969b34c --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +mod tests; + +/// Joins command-line arguments into a single space-separated string, quoting +/// and escaping individual arguments as necessary. +/// +/// The result is intended to be informational, for embedding in debug metadata, +/// and might not be properly quoted/escaped for actual command-line use. +pub(crate) fn quote_command_line_args(args: &[String]) -> String { + // Start with a decent-sized buffer, since rustc invocations tend to be long. + let mut buf = String::with_capacity(128); + + for arg in args { + if !buf.is_empty() { + buf.push(' '); + } + + print_arg_quoted(&mut buf, arg); + } + + buf +} + +/// Equivalent to LLVM's `sys::printArg` with quoting always enabled +/// (see llvm/lib/Support/Program.cpp). +fn print_arg_quoted(buf: &mut String, arg: &str) { + buf.reserve(arg.len() + 2); + + buf.push('"'); + for ch in arg.chars() { + if matches!(ch, '"' | '\\' | '$') { + buf.push('\\'); + } + buf.push(ch); + } + buf.push('"'); +} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs new file mode 100644 index 0000000000000..69641fed3bc92 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs @@ -0,0 +1,25 @@ +#[test] +fn quote_command_line_args() { + use super::quote_command_line_args; + + struct Case<'a> { + args: &'a [&'a str], + expected: &'a str, + } + + let cases = &[ + Case { args: &[], expected: "" }, + Case { args: &["--hello", "world"], expected: r#""--hello" "world""# }, + Case { args: &["--hello world"], expected: r#""--hello world""# }, + Case { + args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"], + expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#, + }, + ]; + + for &Case { args, expected } in cases { + let args = args.iter().copied().map(str::to_owned).collect::>(); + let actual = quote_command_line_args(&args); + assert_eq!(actual, expected, "args {args:?}"); + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab89a..fe3883e8c73e6 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod archive; +mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 6d8178320febd..d5228f0e0dee1 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -39,13 +38,10 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, - args_cstr_buff: &[u8], + argv0: &str, + command_line_args: &str, use_wasm_eh: bool, ) -> Result> { - // The argument list is passed as the concatenation of one or more C strings. - // This implies that there must be a last byte, and it must be 0. - assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( @@ -70,8 +66,10 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - args_cstr_buff.as_ptr(), - args_cstr_buff.len(), + argv0.as_ptr(), + argv0.len(), + command_line_args.as_ptr(), + command_line_args.len(), use_wasm_eh, ) }; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bda81fbd19e82..c4881f0aafc80 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -31,6 +31,7 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -249,23 +250,15 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // copy the exe path, followed by path all into one buffer - // null terminating them so we can use them as null terminated strings - let args_cstr_buff = { - let mut args_cstr_buff: Vec = Vec::new(); - let exe_path = std::env::current_exe().unwrap_or_default(); - let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default(); - - args_cstr_buff.extend_from_slice(exe_path_str.as_bytes()); - args_cstr_buff.push(0); - - for arg in sess.expanded_args.iter() { - args_cstr_buff.extend_from_slice(arg.as_bytes()); - args_cstr_buff.push(0); - } - - args_cstr_buff - }; + // Command-line information to be included in the target machine. + // This seems to only be used for embedding in PDB debuginfo files. + // FIXME(Zalathar): Maybe skip this for non-PDB targets? + let argv0 = std::env::current_exe() + .unwrap_or_default() + .into_os_string() + .into_string() + .unwrap_or_default(); + let command_line_args = quote_command_line_args(&sess.expanded_args); let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { @@ -323,7 +316,8 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &args_cstr_buff, + &argv0, + &command_line_args, use_wasm_eh, ) }) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 71d8b7d25fe72..c1b5649a58291 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2488,8 +2488,10 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". - ArgsCstrBuffLen: usize, + Argv0: *const c_uchar, // See "PTR_LEN_STR". + Argv0Len: size_t, + CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR". + CommandLineArgsLen: size_t, UseWasmEH: bool, ) -> *mut TargetMachine; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index ab5d5c03e817a..7518b40799bc1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -271,8 +271,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, - const char *DebugInfoCompression, bool UseEmulatedTls, - const char *ArgsCstrBuff, size_t ArgsCstrBuffLen, bool UseWasmEH) { + const char *DebugInfoCompression, bool UseEmulatedTls, const char *Argv0, + size_t Argv0Len, const char *CommandLineArgs, size_t CommandLineArgsLen, + bool UseWasmEH) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -343,25 +344,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; - if (ArgsCstrBuff != nullptr) { - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - auto Arg0 = std::string(ArgsCstrBuff); - buffer_offset = Arg0.size() + 1; - - std::string CommandlineArgs; - raw_string_ostream OS(CommandlineArgs); - ListSeparator LS(" "); - for (StringRef Arg : split(StringRef(ArgsCstrBuff + buffer_offset, - ArgsCstrBuffLen - buffer_offset), - '\0')) { - OS << LS; - sys::printArg(OS, Arg, /*Quote=*/true); - } - OS.flush(); - Options.MCOptions.Argv0 = Arg0; - Options.MCOptions.CommandlineArgs = CommandlineArgs; - } + if (Argv0 != nullptr) + Options.MCOptions.Argv0 = {Argv0, Argv0Len}; + if (CommandLineArgs != nullptr) + Options.MCOptions.CommandlineArgs = {CommandLineArgs, CommandLineArgsLen}; #if LLVM_VERSION_GE(21, 0) TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, From b3e056f19882e1d60a2a67ad9485546890108b44 Mon Sep 17 00:00:00 2001 From: Zihan Date: Mon, 11 Aug 2025 11:06:28 -0400 Subject: [PATCH 0545/1900] `module_style`: refactor and don't lint mod tagged with path attr refactored with `check_item` and `check_item_post`. this refactoring also fixed a few bugs related to mod path with shared segment name caused by preivous approach. changelog: [`self_named_module_files`]: don't lint mod tagged with path attribute changelog: [`mod_module_files`]: don't lint mod tagged with path attribute Signed-off-by: Zihan --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/module_style.rs | 155 +++++++++--------- .../duplicated_mod_names_14697/Cargo.stderr | 11 ++ .../duplicated_mod_names_14697/Cargo.toml | 11 ++ .../duplicated_mod_names_14697/src/foo.rs | 1 + .../duplicated_mod_names_14697/src/foo/bar.rs | 1 + .../duplicated_mod_names_14697/src/lib.rs | 4 + .../src/other/foo/mod.rs | 1 + .../src/other/mod.rs | 1 + .../module_style/fail_mod/Cargo.stderr | 16 +- .../Cargo.toml | 10 ++ .../foo/bar/Cargo.toml | 5 + .../foo/bar/src/foo.rs | 1 + .../foo/bar/src/lib.rs | 1 + .../src/lib.rs | 1 + .../with_path_attr_mod/Cargo.toml | 10 ++ .../with_path_attr_mod/src/bar/mod.rs | 1 + .../with_path_attr_mod/src/lib.rs | 4 + .../with_path_attr_no_mod/Cargo.toml | 10 ++ .../with_path_attr_no_mod/src/bar.rs | 1 + .../with_path_attr_no_mod/src/foo.rs | 2 + .../with_path_attr_no_mod/src/lib.rs | 3 + 22 files changed, 167 insertions(+), 85 deletions(-) create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..2b8f09d33405c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -671,7 +671,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_early_pass(move || Box::new(module_style::ModStyle::default())); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 98614baffcea6..f132b90ac4f2e 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_ast::ast::{self, Inline, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{FileName, SourceFile, Span, SyntaxContext}; -use std::ffi::OsStr; -use std::path::{Component, Path}; +use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; declare_clippy_lint! { /// ### What it does @@ -60,107 +59,97 @@ declare_clippy_lint! { /// mod.rs /// lib.rs /// ``` - #[clippy::version = "1.57.0"] pub SELF_NAMED_MODULE_FILES, restriction, "checks that module layout is consistent" } -pub struct ModStyle; - impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); +pub struct ModState { + contains_external: bool, + has_path_attr: bool, + mod_file: Arc, +} + +#[derive(Default)] +pub struct ModStyle { + working_dir: Option, + module_stack: Vec, +} + impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf); + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, mod_spans, ..)) = &item.kind { + let has_path_attr = item.attrs.iter().any(|attr| attr.has_name(sym::path)); + if !has_path_attr && let Some(current) = self.module_stack.last_mut() { + current.contains_external = true; + } + let mod_file = cx.sess().source_map().lookup_source_file(mod_spans.inner_span.lo()); + self.module_stack.push(ModState { + contains_external: false, + has_path_attr, + mod_file, + }); + } + } - let files = cx.sess().source_map().files(); - - let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { + fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow + { return; - }; - - // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives - // `[path, to]` but not foo - let mut folder_segments = FxIndexSet::default(); - // `mod_folders` is all the unique folder names that contain a mod.rs file - let mut mod_folders = FxHashSet::default(); - // `file_map` maps file names to the full path including the file name - // `{ foo => path/to/foo.rs, .. } - let mut file_map = FxHashMap::default(); - for file in files.iter() { - if let FileName::Real(name) = &file.name - && let Some(lp) = name.local_path() - && file.cnum == LOCAL_CRATE - { - // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887) - // Only check files in the current crate. - // Fix false positive that crate dependency in workspace sub directory - // is checked unintentionally. - let path = if lp.is_relative() { - lp - } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { - relative - } else { - continue; - }; - - if let Some(stem) = path.file_stem() { - file_map.insert(stem, (file, path)); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - } } - for folder in &folder_segments { - if !mod_folders.contains(folder) - && let Some((file, path)) = file_map.get(folder) - { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, ..)) = &item.kind + && let Some(current) = self.module_stack.pop() + && !current.has_path_attr + { + let Some(path) = self + .working_dir + .as_ref() + .and_then(|src| try_trim_file_path_prefix(¤t.mod_file, src)) + else { + return; + }; + if current.contains_external { + check_self_named_module(cx, path, ¤t.mod_file); } + check_mod_module(cx, path, ¤t.mod_file); } } } -/// For each `path` we add each folder component to `folder_segments` and if the file name -/// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files<'a>( - path: &'a Path, - folder_segments: &mut FxIndexSet<&'a OsStr>, - mod_folders: &mut FxHashSet<&'a OsStr>, -) { - let mut comp = path.components().rev().peekable(); - let _: Option<_> = comp.next(); - if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); +fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if !path.ends_with("mod.rs") { + let mut mod_folder = path.with_extension(""); + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + mod_folder.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), mod_folder.display())); + }, + ); } - let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); - folder_segments.extend(folders); } -/// Checks every path for the presence of `mod.rs` files and emits the lint if found. /// We should not emit a lint for test modules in the presence of `mod.rs`. /// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) /// for code-sharing between tests. -fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { +fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { span_lint_and_then( cx, @@ -177,3 +166,17 @@ fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &Source ); } } + +fn try_trim_file_path_prefix<'a>(file: &'a SourceFile, prefix: &'a Path) -> Option<&'a Path> { + if let FileName::Real(name) = &file.name + && let Some(mut path) = name.local_path() + && file.cnum == LOCAL_CRATE + { + if !path.is_relative() { + path = path.strip_prefix(prefix).ok()?; + } + Some(path) + } else { + None + } +} diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr new file mode 100644 index 0000000000000..c7490c5da027b --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are required, found `src/foo.rs` + --> src/foo.rs:1:1 + | +1 | pub mod bar; + | ^ + | + = help: move `src/foo.rs` to `src/foo/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` + +error: could not compile `duplicated-mod-names-14697` (lib) due to 1 previous error diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml new file mode 100644 index 0000000000000..569082f2f6599 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml @@ -0,0 +1,11 @@ +# Should trigger when multiple mods with the same name exist and not all of them follow self-named convention. +# See issue #14697. +[package] +name = "duplicated-mod-names-14697" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs new file mode 100644 index 0000000000000..46f285ca47d69 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs @@ -0,0 +1 @@ +pub mod bar; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs new file mode 100644 index 0000000000000..a85dae5748165 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; +pub mod other; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index 902330e178530..f134943e69bfc 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `src/bad/inner.rs` - --> src/bad/inner.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` + --> src/bad/inner/stuff.rs:1:1 | -1 | pub mod stuff; +1 | pub mod most; | ^ | - = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` + = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` -error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` - --> src/bad/inner/stuff.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner.rs` + --> src/bad/inner.rs:1:1 | -1 | pub mod most; +1 | pub mod stuff; | ^ | - = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` + = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` error: could not compile `fail-mod` (bin "fail-mod") due to 2 previous errors diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml new file mode 100644 index 0000000000000..5c2fabd2283d3 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml @@ -0,0 +1,10 @@ +# Should not produce FP when irrelavant path segment shares the same name with module. +# See issue #10271 and #11916. +[package] +name = "segment-with-mod-name-10271-11916" +version = "0.1.0" +edition = "2024" +publish = false + +[workspace] +members = ["foo/bar"] \ No newline at end of file diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml new file mode 100644 index 0000000000000..1f68c0dccac54 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "bar" +version = "0.1.0" +edition = "2024" +publish = false diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml new file mode 100644 index 0000000000000..d867377545e15 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs new file mode 100644 index 0000000000000..a5c2109ece7db --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::mod_module_files)] + +#[path = "bar/mod.rs"] +pub mod foo; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml new file mode 100644 index 0000000000000..ddf2ac394cddc --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-no-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs new file mode 100644 index 0000000000000..3b12aefa3d5fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs @@ -0,0 +1,2 @@ +#[path = "bar.rs"] +mod bar; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs new file mode 100644 index 0000000000000..bf2a5d0193350 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs @@ -0,0 +1,3 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; From 6e74905be29b861bcfd2780a8c81495deffec6c3 Mon Sep 17 00:00:00 2001 From: Haidong Zhang Date: Wed, 3 Sep 2025 17:40:45 +0800 Subject: [PATCH 0546/1900] Set lto="fat" automatically when compiling with RUSTFLAGS="-Zautodiff=Enable". --- compiler/rustc_codegen_ssa/messages.ftl | 2 -- compiler/rustc_codegen_ssa/src/errors.rs | 4 ---- compiler/rustc_session/src/config.rs | 5 +++++ compiler/rustc_session/src/session.rs | 7 +++++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 1dd65d38a2be7..91c3806df4c34 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} -codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto - codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index fb5a82051405d..d5c30c5c7a6b0 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -37,10 +37,6 @@ pub(crate) struct CguNotRecorded<'a> { pub cgu_name: &'a str, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_autodiff_without_lto)] -pub struct AutodiffWithoutLto; - #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c9765..795cb2b2cfeba 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1509,6 +1509,11 @@ impl Options { pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) } + + #[inline] + pub fn autodiff_enabled(&self) -> bool { + self.unstable_opts.autodiff.contains(&AutoDiff::Enable) + } } impl UnstableOptions { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3525c7c1d1a99..d0dd2cdac0c48 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -600,6 +600,13 @@ impl Session { /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { + // Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types. + // fat-lto is the easiest solution to this requirement, but quite expensive. + // FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto. + if self.opts.autodiff_enabled() { + return config::Lto::Fat; + } + // If our target has codegen requirements ignore the command line if self.target.requires_lto { return config::Lto::Fat; From 2c1f1f0e9bb706d9adbb71e0bc33923c4478eb85 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 8 Sep 2025 10:17:14 +0200 Subject: [PATCH 0547/1900] Add GenMC estimation mode. Improve error handling and output printing. --- src/tools/miri/doc/genmc.md | 3 + .../genmc-sys/cpp/include/MiriInterface.hpp | 8 +- .../cpp/src/MiriInterface/Exploration.cpp | 14 ++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 23 ++- src/tools/miri/genmc-sys/src/lib.rs | 55 +++++- src/tools/miri/src/bin/miri.rs | 9 +- .../miri/src/concurrency/genmc/config.rs | 17 ++ src/tools/miri/src/concurrency/genmc/dummy.rs | 12 -- src/tools/miri/src/concurrency/genmc/mod.rs | 61 ++++--- src/tools/miri/src/concurrency/genmc/run.rs | 156 ++++++++++++++---- .../genmc/fail/data_race/mpu2_rels_rlx.stderr | 2 + .../data_race/weak_orderings.rel_rlx.stderr | 2 + .../data_race/weak_orderings.rlx_acq.stderr | 2 + .../data_race/weak_orderings.rlx_rlx.stderr | 2 + .../tests/genmc/fail/loom/buggy_inc.stderr | 2 + .../fail/loom/store_buffering.genmc.stderr | 2 + .../fail/simple/2w2w_weak.relaxed4.stderr | 2 + .../fail/simple/2w2w_weak.release4.stderr | 2 + .../fail/simple/2w2w_weak.sc3_rel1.stderr | 2 + .../cas_failure_ord_racy_key_init.stderr | 4 +- .../genmc/pass/atomics/cas_simple.stderr | 4 +- .../tests/genmc/pass/atomics/rmw_ops.stderr | 4 +- .../miri/tests/genmc/pass/litmus/2cowr.stderr | 4 +- .../genmc/pass/litmus/2w2w_2sc_scf.stderr | 4 +- .../pass/litmus/2w2w_3sc_1rel.release1.stderr | 4 +- .../pass/litmus/2w2w_3sc_1rel.release2.stderr | 4 +- .../genmc/pass/litmus/2w2w_4rel.sc.stderr | 4 +- .../genmc/pass/litmus/2w2w_4rel.weak.stderr | 4 +- .../tests/genmc/pass/litmus/2w2w_4sc.stderr | 4 +- .../genmc/pass/litmus/IRIW-acq-sc.stderr | 4 +- .../tests/genmc/pass/litmus/IRIWish.stderr | 4 +- .../miri/tests/genmc/pass/litmus/LB.stderr | 4 +- .../tests/genmc/pass/litmus/LB_incMPs.stderr | 4 +- .../miri/tests/genmc/pass/litmus/MP.stderr | 4 +- .../genmc/pass/litmus/MPU2_rels_acqf.stderr | 4 +- .../genmc/pass/litmus/MPU_rels_acq.stderr | 4 +- .../tests/genmc/pass/litmus/MP_incMPs.stderr | 4 +- .../genmc/pass/litmus/MP_rels_acqf.stderr | 4 +- .../miri/tests/genmc/pass/litmus/SB.stderr | 4 +- .../tests/genmc/pass/litmus/SB_2sc_scf.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_U.sc.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_U.weak.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_acq.stderr | 4 +- .../tests/genmc/pass/litmus/atomicpo.stderr | 4 +- .../tests/genmc/pass/litmus/casdep.stderr | 4 +- .../miri/tests/genmc/pass/litmus/ccr.stderr | 4 +- .../miri/tests/genmc/pass/litmus/cii.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr0.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr1.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr2.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corw.stderr | 4 +- .../miri/tests/genmc/pass/litmus/cowr.stderr | 4 +- .../genmc/pass/litmus/cumul-release.stderr | 4 +- .../tests/genmc/pass/litmus/default.stderr | 4 +- .../genmc/pass/litmus/detour.join.stderr | 4 +- .../genmc/pass/litmus/detour.no_join.stderr | 4 +- .../genmc/pass/litmus/fr_w_w_w_reads.stderr | 4 +- .../miri/tests/genmc/pass/litmus/inc2w.stderr | 4 +- .../genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 4 +- .../miri/tests/genmc/pass/litmus/riwi.stderr | 4 +- .../tests/genmc/pass/litmus/viktor-relseq.rs | 4 +- .../genmc/pass/litmus/viktor-relseq.stderr | 6 +- src/tools/miri/tests/ui.rs | 2 + 64 files changed, 386 insertions(+), 170 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 8af697be34a61..44e11dcbec44c 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -24,6 +24,8 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Supported Parameters - `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-estimate`: This enables estimation of the concurrent execution space and verification time, before running the full verification. This should help users detect when their program is too complex to fully verify in a reasonable time. This will explore enough executions to make a good estimation, but at least 10 and at most `estimation-max` executions. +- `-Zmiri-genmc-estimation-max={MAX_ITERATIONS}`: Set the maximum number of executions that will be explored during estimation (default: 1000). - `-Zmiri-genmc-print-exec-graphs={none,explored,blocked,all}`: Make GenMC print the execution graph of the program after every explored, every blocked, or after every execution (default: None). - `-Zmiri-genmc-print-exec-graphs`: Shorthand for suffix `=explored`. - `-Zmiri-genmc-print-genmc-output`: Print the output that GenMC provides. NOTE: this output is quite verbose and the events in the printed execution graph are hard to map back to the Rust code location they originate from. @@ -36,6 +38,7 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug1`: Print revisits considered by GenMC. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. +- `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification. #### Regular Miri parameters useful for GenMC mode diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 662eb0e173ca8..444c9375319af 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -31,6 +31,7 @@ enum class LogLevel : std::uint8_t; struct GenmcScalar; struct SchedulingResult; +struct EstimationResult; struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; @@ -66,7 +67,7 @@ struct MiriGenmcShim : private GenMCDriver { /// `logLevel`, causing a data race. The safest way to use these functions is to call /// `set_log_level_raw` once, and only then start creating handles. There should not be any /// other (safe) way to create a `MiriGenmcShim`. - /* unsafe */ static auto create_handle(const GenmcParams& params) + /* unsafe */ static auto create_handle(const GenmcParams& params, bool estimation_mode) -> std::unique_ptr; virtual ~MiriGenmcShim() {} @@ -183,6 +184,11 @@ struct MiriGenmcShim : private GenMCDriver { return nullptr; } + /**** Printing and estimation mode functionality. ****/ + + /// Get the results of a run in estimation mode. + auto get_estimation_results() const -> EstimationResult; + private: /** Increment the event index in the given thread by 1 and return the new event. */ [[nodiscard]] inline auto inc_pos(ThreadId tid) -> Event { diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp index c51b59e865170..5e7188f17e0d2 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -10,7 +10,9 @@ #include "Support/Verbosity.hpp" // C++ headers: +#include #include +#include auto MiriGenmcShim::schedule_next( const int curr_thread_id, @@ -40,3 +42,15 @@ auto MiriGenmcShim::handle_execution_end() -> std::unique_ptr { GenMCDriver::handleExecutionEnd(); return {}; } + +/**** Estimation mode result ****/ + +auto MiriGenmcShim::get_estimation_results() const -> EstimationResult { + const auto& res = getResult(); + return EstimationResult { + .mean = static_cast(res.estimationMean), + .sd = static_cast(std::sqrt(res.estimationVariance)), + .explored_execs = static_cast(res.explored), + .blocked_execs = static_cast(res.exploredBlocked), + }; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index a17a83aa06e13..af13f0d07746e 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -58,7 +58,7 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel logLevel = to_genmc_verbosity_level(log_level); } -/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params) +/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params, bool estimation_mode) -> std::unique_ptr { auto conf = std::make_shared(); @@ -82,7 +82,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri. conf->warnOnGraphSize = 1024 * 1024; - // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is disabled. + // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is + // disabled. conf->model = params.disable_weak_memory_emulation ? ModelType::SC : ModelType::RC11; // This prints the seed that GenMC picks for randomized scheduling during estimation mode. @@ -119,12 +120,20 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel ); conf->symmetryReduction = params.do_symmetry_reduction; - // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). - conf->schedulePolicy = SchedulePolicy::WF; - + // Set the scheduling policy. GenMC uses `WFR` for estimation mode. + // For normal verification, `WF` has the best performance and is the GenMC default. + // Other scheduling policies are used by GenMC for testing and for modes currently + // unsupported with Miri such as bounding, which uses LTR. + conf->schedulePolicy = estimation_mode ? SchedulePolicy::WFR : SchedulePolicy::WF; + + // Set the min and max number of executions tested in estimation mode. + conf->estimationMin = 10; // default taken from GenMC + conf->estimationMax = params.estimation_max; + // Deviation threshold % under which estimation is deemed good enough. + conf->sdThreshold = 10; // default taken from GenMC // Set the mode used for this driver, either estimation or verification. - // FIXME(genmc): implement estimation mode. - const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode {}); + const auto mode = estimation_mode ? GenMCDriver::Mode(GenMCDriver::EstimationMode {}) + : GenMCDriver::Mode(GenMCDriver::VerificationMode {}); // Running Miri-GenMC without race detection is not supported. // Disabling this option also changes the behavior of the replay scheduler to only schedule diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 31bc2606adc01..733b3d780b187 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -27,6 +27,7 @@ static GENMC_LOG_LEVEL: OnceLock = OnceLock::new(); pub fn create_genmc_driver_handle( params: &GenmcParams, genmc_log_level: LogLevel, + do_estimation: bool, ) -> UniquePtr { // SAFETY: Only setting the GenMC log level once is guaranteed by the `OnceLock`. // No other place calls `set_log_level_raw`, so the `logLevel` value in GenMC will not change once we initialize it once. @@ -40,7 +41,7 @@ pub fn create_genmc_driver_handle( }), "Attempt to change the GenMC log level after it was already set" ); - unsafe { MiriGenmcShim::create_handle(params) } + unsafe { MiriGenmcShim::create_handle(params, do_estimation) } } impl GenmcScalar { @@ -54,6 +55,7 @@ impl GenmcScalar { impl Default for GenmcParams { fn default() -> Self { Self { + estimation_max: 1000, // default taken from GenMC print_random_schedule_seed: false, do_symmetry_reduction: false, // GenMC graphs can be quite large since Miri produces a lot of (non-atomic) events. @@ -70,6 +72,20 @@ impl Default for LogLevel { } } +impl FromStr for SchedulePolicy { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(match s { + "wf" => SchedulePolicy::WF, + "wfr" => SchedulePolicy::WFR, + "arbitrary" | "random" => SchedulePolicy::Arbitrary, + "ltr" => SchedulePolicy::LTR, + _ => return Err("invalid scheduling policy"), + }) + } +} + impl FromStr for LogLevel { type Err = &'static str; @@ -92,9 +108,12 @@ mod ffi { /**** Types shared between Miri/Rust and Miri/C++ through cxx_bridge: ****/ /// Parameters that will be given to GenMC for setting up the model checker. - /// (The fields of this struct are visible to both Rust and C++) + /// The fields of this struct are visible to both Rust and C++. + /// Note that this struct is #[repr(C)], so the order of fields matters. #[derive(Clone, Debug)] struct GenmcParams { + /// Maximum number of executions explored in estimation mode. + pub estimation_max: u32, pub print_random_schedule_seed: bool, pub do_symmetry_reduction: bool, pub print_execution_graphs: ExecutiongraphPrinting, @@ -165,6 +184,19 @@ mod ffi { next_thread: i32, } + #[must_use] + #[derive(Debug)] + struct EstimationResult { + /// Expected number of total executions. + mean: f64, + /// Standard deviation of the total executions estimate. + sd: f64, + /// Number of explored executions during the estimation. + explored_execs: u64, + /// Number of encounteded blocked executions during the estimation. + blocked_execs: u64, + } + #[must_use] #[derive(Debug)] struct LoadResult { @@ -214,6 +246,14 @@ mod ffi { /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ + #[derive(Clone, Copy, Debug)] + enum SchedulePolicy { + LTR, + WF, + WFR, + Arbitrary, + } + #[derive(Debug)] /// Corresponds to GenMC's type with the same name. /// Should only be modified if changed by GenMC. @@ -272,6 +312,7 @@ mod ffi { type ActionKind; type MemOrdering; type RMWBinOp; + type SchedulePolicy; /// Set the log level for GenMC. /// @@ -295,7 +336,10 @@ mod ffi { /// start creating handles. /// There should not be any other (safe) way to create a `MiriGenmcShim`. #[Self = "MiriGenmcShim"] - unsafe fn create_handle(params: &GenmcParams) -> UniquePtr; + unsafe fn create_handle( + params: &GenmcParams, + estimation_mode: bool, + ) -> UniquePtr; /// Get the bit mask that GenMC expects for global memory allocations. fn get_global_alloc_static_mask() -> u64; @@ -403,5 +447,10 @@ mod ffi { fn get_result_message(self: &MiriGenmcShim) -> UniquePtr; /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. fn get_error_string(self: &MiriGenmcShim) -> UniquePtr; + + /**** Printing functionality. ****/ + + /// Get the results of a run in estimation mode. + fn get_estimation_results(self: &MiriGenmcShim) -> EstimationResult; } } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 731fe283a2195..8b15a7863476e 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -186,18 +186,17 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(_genmc_config) = &config.genmc_config { + // Run in GenMC mode if enabled. + if config.genmc_config.is_some() { + // This is the entry point used in GenMC mode. + // This closure will be called multiple times to explore the concurrent execution space of the program. let eval_entry_once = |genmc_ctx: Rc| { miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) }; - - // FIXME(genmc): add estimation mode here. - let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| { tcx.dcx().abort_if_errors(); rustc_driver::EXIT_FAILURE }); - exit(return_code); }; diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index 34933d423f064..c7cfa6012b8dc 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -10,11 +10,15 @@ use crate::{IsolatedOp, MiriConfig, RejectOpWith}; pub struct GenmcConfig { /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, + pub(super) do_estimation: bool, /// Print the output message that GenMC generates when an error occurs. /// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced. pub(super) print_genmc_output: bool, /// The log level for GenMC. pub(super) log_level: LogLevel, + /// Enable more verbose output, such as number of executions estimate + /// and time to completion of verification step. + pub(super) verbose_output: bool, } impl GenmcConfig { @@ -57,8 +61,21 @@ impl GenmcConfig { "Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'" )), } + } else if trimmed_arg == "estimate" { + // FIXME(genmc): should this be on by default (like for GenMC)? + // Enable estimating the execution space and require time before running the actual verification. + genmc_config.do_estimation = true; + } else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") { + // Set the maximum number of executions to explore during estimation. + genmc_config.params.estimation_max = estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0).ok_or_else(|| { + format!( + "'-Zmiri-genmc-estimation-max=...' expects a positive integer argument, but got '{estimation_max_str}'" + ) + })?; } else if trimmed_arg == "print-genmc-output" { genmc_config.print_genmc_output = true; + } else if trimmed_arg == "verbose" { + genmc_config.verbose_output = true; } else { return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 92b34b83ee0fc..c28984cef35ad 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -39,18 +39,6 @@ mod run { impl GenmcCtx { // We don't provide the `new` function in the dummy module. - pub fn get_blocked_execution_count(&self) -> usize { - unreachable!() - } - - pub fn get_explored_execution_count(&self) -> usize { - unreachable!() - } - - pub fn is_exploration_done(&self) -> bool { - unreachable!() - } - /**** Memory access handling ****/ pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 7270c66810608..0086d3f2bf0bc 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,8 +2,8 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, RMWBinOp, UniquePtr, - create_genmc_driver_handle, + EstimationResult, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, + RMWBinOp, UniquePtr, create_genmc_driver_handle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -16,6 +16,7 @@ use self::helper::{ MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, }; +use self::run::GenmcMode; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; @@ -37,6 +38,16 @@ pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; pub use self::run::run_genmc_mode; +#[derive(Debug)] +pub enum ExecutionEndResult { + /// An error occurred at the end of the execution. + Error(String), + /// No errors occurred, and there are more executions to explore. + Continue, + /// No errors occurred and we are finished. + Stop, +} + #[derive(Clone, Copy, Debug)] pub enum ExitType { MainThreadFinish, @@ -128,26 +139,33 @@ pub struct GenmcCtx { /// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - fn new(miri_config: &MiriConfig, global_state: Arc) -> Self { + fn new(miri_config: &MiriConfig, global_state: Arc, mode: GenmcMode) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); - let handle = - RefCell::new(create_genmc_driver_handle(&genmc_config.params, genmc_config.log_level)); + let handle = RefCell::new(create_genmc_driver_handle( + &genmc_config.params, + genmc_config.log_level, + /* do_estimation: */ mode == GenmcMode::Estimation, + )); Self { handle, exec_state: Default::default(), global_state } } + fn get_estimation_results(&self) -> EstimationResult { + self.handle.borrow().get_estimation_results() + } + /// Get the number of blocked executions encountered by GenMC. - pub fn get_blocked_execution_count(&self) -> u64 { + fn get_blocked_execution_count(&self) -> u64 { self.handle.borrow().get_blocked_execution_count() } /// Get the number of explored executions encountered by GenMC. - pub fn get_explored_execution_count(&self) -> u64 { + fn get_explored_execution_count(&self) -> u64 { self.handle.borrow().get_explored_execution_count() } /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. - pub fn try_get_error(&self) -> Option { + fn try_get_error(&self) -> Option { self.handle .borrow() .get_error_string() @@ -157,7 +175,7 @@ impl GenmcCtx { /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. - pub fn get_result_message(&self) -> String { + fn get_result_message(&self) -> String { self.handle .borrow() .get_result_message() @@ -166,13 +184,6 @@ impl GenmcCtx { .expect("there should always be a message") } - /// This function determines if we should continue exploring executions or if we are done. - /// - /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. - pub fn is_exploration_done(&self) -> bool { - self.handle.borrow_mut().pin_mut().is_exploration_done() - } - /// Select whether data race free actions should be allowed. This function should be used carefully! /// /// If `true` is passed, allow for data races to happen without triggering an error, until this function is called again with argument `false`. @@ -218,13 +229,25 @@ impl GenmcCtx { /// This function will also check for those, and return their error description. /// /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. - fn handle_execution_end(&self) -> Option { + fn handle_execution_end(&self) -> ExecutionEndResult { let result = self.handle.borrow_mut().pin_mut().handle_execution_end(); - result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; + if let Some(error) = result.as_ref() { + return ExecutionEndResult::Error(error.to_string_lossy().to_string()); + } + + // GenMC decides if there is more to explore: + let exploration_done = self.handle.borrow_mut().pin_mut().is_exploration_done(); // GenMC currently does not return an error value immediately in all cases. + // Both `handle_execution_end` and `is_exploration_done` can produce such errors. // We manually query for any errors here to ensure we don't miss any. - self.try_get_error() + if let Some(error) = self.try_get_error() { + ExecutionEndResult::Error(error) + } else if exploration_done { + ExecutionEndResult::Stop + } else { + ExecutionEndResult::Continue + } } /**** Memory access handling ****/ diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 7e4ed816a76a1..bc2f5f7b79ec4 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -1,32 +1,65 @@ use std::rc::Rc; use std::sync::Arc; +use std::time::Instant; +use genmc_sys::EstimationResult; use rustc_middle::ty::TyCtxt; use super::GlobalState; +use crate::concurrency::genmc::ExecutionEndResult; use crate::rustc_const_eval::interpret::PointerArithmetic; -use crate::{GenmcCtx, MiriConfig}; +use crate::{GenmcConfig, GenmcCtx, MiriConfig}; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) enum GenmcMode { + Estimation, + Verification, +} + +impl GenmcMode { + /// Return whether warnings on unsupported features should be printed in this mode. + fn print_unsupported_warnings(self) -> bool { + self == GenmcMode::Verification + } +} /// Do a complete run of the program in GenMC mode. /// This will call `eval_entry` multiple times, until either: /// - An error is detected (indicated by a `None` return value) /// - All possible executions are explored. /// -/// FIXME(genmc): add estimation mode setting. +/// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program. pub fn run_genmc_mode<'tcx>( config: &MiriConfig, eval_entry: impl Fn(Rc) -> Option, tcx: TyCtxt<'tcx>, ) -> Option { let genmc_config = config.genmc_config.as_ref().unwrap(); + // Run in Estimation mode if requested. + if genmc_config.do_estimation { + eprintln!("Estimating GenMC verification time..."); + run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Estimation)?; + } + // Run in Verification mode. + eprintln!("Running GenMC Verification..."); + run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Verification) +} + +fn run_genmc_mode_impl<'tcx>( + config: &MiriConfig, + eval_entry: &impl Fn(Rc) -> Option, + tcx: TyCtxt<'tcx>, + mode: GenmcMode, +) -> Option { + let time_start = Instant::now(); + let genmc_config = config.genmc_config.as_ref().unwrap(); // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - // FIXME(genmc): disable warnings in estimation mode once it is added. let global_state = - Arc::new(GlobalState::new(tcx.target_usize_max(), /* print_warnings */ true)); - let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); + Arc::new(GlobalState::new(tcx.target_usize_max(), mode.print_unsupported_warnings())); + let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state, mode)); // `rep` is used to report the progress, Miri will panic on wrap-around. for rep in 0u64.. { @@ -37,46 +70,97 @@ pub fn run_genmc_mode<'tcx>( // Execute the program until completion to get the return value, or return if an error happens: let Some(return_code) = eval_entry(genmc_ctx.clone()) else { - // If requested, print the output GenMC produced: - if genmc_config.print_genmc_output { - eprintln!("== raw GenMC output ========================="); - eprintln!("{}", genmc_ctx.get_result_message()); - eprintln!("== end of raw GenMC output =================="); - } + genmc_ctx.print_genmc_output(genmc_config); return None; }; - // We inform GenMC that the execution is complete. If there was an error, we print it. - if let Some(error) = genmc_ctx.handle_execution_end() { - // This can be reached for errors that affect the entire execution, not just a specific event. - // For instance, linearizability checking and liveness checking report their errors this way. - // Neither are supported by Miri-GenMC at the moment though. However, GenMC also - // treats races on deallocation as global errors, so this code path is still reachable. - // Since we don't have any span information for the error at this point, - // we just print GenMC's error message. - eprintln!("(GenMC) Error detected: {error}"); - eprintln!(); - eprintln!("{}", genmc_ctx.get_result_message()); - return None; + // We inform GenMC that the execution is complete. + // If there was an error, we print it. + match genmc_ctx.handle_execution_end() { + ExecutionEndResult::Continue => continue, + ExecutionEndResult::Stop => { + let elapsed_time_sec = Instant::now().duration_since(time_start).as_secs_f64(); + // Print the output for the current mode. + if mode == GenmcMode::Estimation { + genmc_ctx.print_estimation_output(genmc_config, elapsed_time_sec); + } else { + genmc_ctx.print_verification_output(genmc_config, elapsed_time_sec); + } + // Return the return code of the last execution. + return Some(return_code); + } + ExecutionEndResult::Error(error) => { + // This can be reached for errors that affect the entire execution, not just a specific event. + // For instance, linearizability checking and liveness checking report their errors this way. + // Neither are supported by Miri-GenMC at the moment though. However, GenMC also + // treats races on deallocation as global errors, so this code path is still reachable. + // Since we don't have any span information for the error at this point, + // we just print GenMC's error string, and the full GenMC output if requested. + eprintln!("(GenMC) Error detected: {error}"); + genmc_ctx.print_genmc_output(genmc_config); + return None; + } } + } + unreachable!() +} - // Check if we've explored enough executions: - if !genmc_ctx.is_exploration_done() { - continue; +impl GenmcCtx { + /// Print the full output message produced by GenMC if requested, or a hint on how to enable it. + /// + /// This message can be very verbose and is likely not useful for the average user. + /// This function should be called *after* Miri has printed all of its output. + fn print_genmc_output(&self, genmc_config: &GenmcConfig) { + if genmc_config.print_genmc_output { + eprintln!("GenMC error report:"); + eprintln!("{}", self.get_result_message()); + } else { + eprintln!( + "(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.)" + ); } + } - eprintln!("(GenMC) Verification complete. No errors were detected."); - - let explored_execution_count = genmc_ctx.get_explored_execution_count(); - let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); + /// Given the time taken for the estimation mode run, print the expected time range for verification. + /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked. + fn print_estimation_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) { + let EstimationResult { mean, sd, explored_execs, blocked_execs } = + self.get_estimation_results(); + #[allow(clippy::as_conversions)] + let time_per_exec_sec = elapsed_time_sec / (explored_execs as f64 + blocked_execs as f64); + let estimated_mean_sec = time_per_exec_sec * mean; + let estimated_sd_sec = time_per_exec_sec * sd; - eprintln!("Number of complete executions explored: {explored_execution_count}"); - if blocked_execution_count > 0 { - eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + if genmc_config.verbose_output { + eprintln!("Finished estimation in {elapsed_time_sec:.2?}s"); + if blocked_execs != 0 { + eprintln!(" Explored executions: {explored_execs}"); + eprintln!(" Blocked executions: {blocked_execs}"); + } + eprintln!("Expected number of executions: {mean:.0} ± {sd:.0}"); + } + // The estimation can be out-of-bounds of an `f64`. + if !(mean.is_finite() && mean >= 0.0 && sd.is_finite() && sd >= 0.0) { + eprintln!("WARNING: Estimation gave weird results, there may have been an overflow."); } + eprintln!("Expected verification time: {estimated_mean_sec:.2}s ± {estimated_sd_sec:.2}s"); + } - // Return the return code of the last execution. - return Some(return_code); + /// Given the time taken for the verification mode run, print the expected time range for verification. + /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked. + fn print_verification_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) { + let explored_execution_count = self.get_explored_execution_count(); + let blocked_execution_count = self.get_blocked_execution_count(); + eprintln!( + "Verification complete with {} executions. No errors found.", + explored_execution_count + blocked_execution_count + ); + if genmc_config.verbose_output { + if blocked_execution_count > 0 { + eprintln!("Number of complete executions explored: {explored_execution_count}"); + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + } + eprintln!("Verification took {elapsed_time_sec:.2?}s."); + } } - unreachable!() } diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr index 946d9a2124b88..00de74009d9b8 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr index 121ded2a181ca..501957a90d3ac 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr index 121ded2a181ca..501957a90d3ac 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr index 121ded2a181ca..501957a90d3ac 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr index 5a8948ff9b4b8..9c3ba1d4c5924 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr index a6c3ed7055e75..db92d0573fad2 100644 --- a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index cbfb6ec833eb0..773f86a975967 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index cbfb6ec833eb0..773f86a975967 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index cbfb6ec833eb0..773f86a975967 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr index d97b8b3d92f48..31438f9352fe6 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Release' was upgraded to 'AcqRel' to match failure ordering 'Acquire'. Miri with GenMC might miss bugs related to this memory access. --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC | @@ -18,5 +19,4 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/p LL | f(); | ^^^ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr index 3b22247ee44cb..7867be2dbe8ed 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr index 3b22247ee44cb..7867be2dbe8ed 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr index c760b44605112..d6ec73a8c6672 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 16 +Running GenMC Verification... +Verification complete with 16 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr index 4fc77b63f3803..7ea2dd5085136 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... [1, 1, 1, 1, 1] [1, 1, 1, 0, 1] [1, 1, 1, 0, 0] @@ -26,5 +27,4 @@ [0, 0, 0, 0, 0] [0, 0, 0, 0, 1] [0, 0, 0, 0, 0] -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 28 +Verification complete with 28 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr index c879e95a17085..e414cd5e064d9 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 15 +Running GenMC Verification... +Verification complete with 15 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr index 4551c00b0575d..29b59ce3bc1a3 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... X=1, Y=2, a=Err(1), b=Ok(1), c=2 X=1, Y=2, a=Err(1), b=Ok(1), c=1 X=1, Y=2, a=Err(1), b=Ok(1), c=0 @@ -34,5 +35,4 @@ X=1, Y=1, a=Err(0), b=Err(0), c=0 X=1, Y=1, a=Err(0), b=Err(0), c=1 X=1, Y=1, a=Err(0), b=Err(0), c=0 X=1, Y=1, a=Err(0), b=Err(0), c=0 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 +Verification complete with 36 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr index c5e34b00642aa..423ee6dc39996 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 13 +Running GenMC Verification... +Verification complete with 13 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr index 2be9a6c7fbded..f527b61202327 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 +Running GenMC Verification... +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr index 8dd2fbe1b9757..c8fbb8951a386 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... a=2, b=1, X=1, Y=3 a=4, b=1, X=1, Y=4 a=3, b=1, X=1, Y=3 @@ -16,5 +17,4 @@ a=3, b=0, X=1, Y=1 a=1, b=1, X=1, Y=3 a=1, b=1, X=1, Y=1 a=1, b=0, X=1, Y=1 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 18 +Verification complete with 18 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr index 65622575de21d..72c59d33f77cf 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... a=2, b=1, X=1, Y=3 a=4, b=1, X=1, Y=4 a=4, b=0, X=1, Y=4 @@ -20,5 +21,4 @@ a=1, b=1, X=1, Y=3 a=1, b=0, X=1, Y=3 a=1, b=1, X=1, Y=1 a=1, b=0, X=1, Y=1 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 22 +Verification complete with 22 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr index 2be9a6c7fbded..f527b61202327 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 +Running GenMC Verification... +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr index 01701dfe6918a..bde951866d013 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Running GenMC Verification... +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr index 01701dfe6918a..bde951866d013 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Running GenMC Verification... +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr index be75e68fde77d..b9bdf2245aed6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr index be75e68fde77d..b9bdf2245aed6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr index 0667962f99c88..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr index f6d07e9c77b2b..384425fc43c67 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 +Running GenMC Verification... +Verification complete with 36 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr index 78a90b63feab6..07fd2d4de180c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 +Running GenMC Verification... +Verification complete with 72 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr index 00394048ec5c6..a031dfc8977a5 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 8 +Running GenMC Verification... +Verification complete with 8 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.stderr b/src/tools/miri/tests/genmc/pass/litmus/default.stderr index e0313930282ea..87d38d250411a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/default.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/default.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 12 +Running GenMC Verification... +Verification complete with 12 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr index 15017249dc3a7..5006f11fefb1c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 9 +Running GenMC Verification... +Verification complete with 9 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr index 15017249dc3a7..5006f11fefb1c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 9 +Running GenMC Verification... +Verification complete with 9 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr index 3b6ba238f5342..c09b50e282f1e 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 210 +Running GenMC Verification... +Verification complete with 210 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr index be75e68fde77d..b9bdf2245aed6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr index b5f8cd15b683f..770fb7ef88046 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 600 +Running GenMC Verification... +Verification complete with 600 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr index 115b1986ce557..485142e945a72 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs index a193dcf0683ea..3256c9f421193 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs @@ -1,6 +1,8 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-estimate // Translated from GenMC's "litmus/viktor-relseq" test. +// +// This test also checks that we can run the GenMC estimation mode. #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr index d63ac5199d5d4..c53d5c569b9ca 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr @@ -1,2 +1,4 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 180 +Estimating GenMC verification time... +Expected verification time: [MEAN] ± [SD] +Running GenMC Verification... +Verification complete with 180 executions. No errors found. diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index b7286d9a3673a..efaaf9fc84170 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -277,6 +277,8 @@ regexes! { r"\bsys/([a-z_]+)/[a-z]+\b" => "sys/$1/PLATFORM", // erase paths into the crate registry r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/.../$1", + // remove time print from GenMC estimation mode output. + "\nExpected verification time: .* ± .*" => "\nExpected verification time: [MEAN] ± [SD]", } enum Dependencies { From 4919d5560120e8dfbf4f93924a2c3dc5d4ed446a Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 18 Sep 2025 10:50:51 +0200 Subject: [PATCH 0548/1900] fix location for nested bodies in promoteds --- compiler/rustc_borrowck/src/type_check/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9e7..e05c5b224858b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -505,6 +505,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); + let mut deferred_closure_requirements = Default::default(); // Don't try to add borrow_region facts for the promoted MIR as they refer // to the wrong locations. @@ -512,6 +513,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { mem::swap(this.polonius_facts, polonius_facts); mem::swap(&mut this.constraints.outlives_constraints, &mut constraints); mem::swap(&mut this.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.deferred_closure_requirements, &mut deferred_closure_requirements); }; swap_constraints(self); @@ -536,6 +538,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } self.constraints.outlives_constraints.push(constraint) } + + // If there are nested bodies in promoteds, we also need to update their + // location to something in the actually body, not the promoted. + // + // We don't update the constraint categories of the resulting constraints + // as returns in nested bodies are a proper return, even if that nested body + // is in a promoted. + for (closure_def_id, args, _locations) in deferred_closure_requirements { + self.deferred_closure_requirements.push((closure_def_id, args, locations)); + } + // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region // at the location provided as an argument to this method From 72fb9b28a62e56d266e34764ef7330ca09266f48 Mon Sep 17 00:00:00 2001 From: lukaslueg Date: Thu, 18 Sep 2025 11:51:29 +0200 Subject: [PATCH 0549/1900] Remove feature-freeze from gh templates --- .github/ISSUE_TEMPLATE/new_lint.yml | 4 +--- .github/PULL_REQUEST_TEMPLATE.md | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index a8202f6378fd3..6ad16aead601d 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -1,7 +1,5 @@ name: New lint suggestion -description: | - Suggest a new Clippy lint (currently not accepting new lints) - Check out the Clippy book for more information about the feature freeze. +description: Suggest a new Clippy lint. labels: ["A-lint"] body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 83bfd8e9c6865..9e49f60892d26 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,10 +32,6 @@ order to get feedback. Delete this line and everything above before opening your PR. -Note that we are currently not taking in new PRs that add new lints. We are in a -feature freeze. Check out the book for more information. If you open a -feature-adding pull request, its review will be delayed. - --- *Please write a short comment explaining your change (or "none" for internal only changes)* From 68473ad11b058ed6a533eec889e28d1695fd2f8f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 11:54:58 +0200 Subject: [PATCH 0550/1900] Check that `.unwrap_or(LITERAL)` doesn't trigger `unwrap_or_default` --- tests/ui/or_fun_call.fixed | 12 ++++++ tests/ui/or_fun_call.rs | 12 ++++++ tests/ui/or_fun_call.stderr | 76 ++++++++++++++++++------------------- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 0a8525a12f5e0..ac771c34ef49f 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -77,6 +77,18 @@ fn or_fun_call() { with_default_type.unwrap_or_default(); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index b4f9b950a7fe0..75762fb020ac0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -77,6 +77,18 @@ fn or_fun_call() { with_default_type.unwrap_or(u64::default()); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 3e4df772668d7..0f0cd3329b669 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:81:18 + --> tests/ui/or_fun_call.rs:93:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:85:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:89:14 + --> tests/ui/or_fun_call.rs:101:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:21 + --> tests/ui/or_fun_call.rs:105:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:97:19 + --> tests/ui/or_fun_call.rs:109:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:101:23 + --> tests/ui/or_fun_call.rs:113:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:117:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:25 + --> tests/ui/or_fun_call.rs:121:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:113:21 + --> tests/ui/or_fun_call.rs:125:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:118:17 + --> tests/ui/or_fun_call.rs:130:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:123:21 + --> tests/ui/or_fun_call.rs:135:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:126:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:151:35 + --> tests/ui/or_fun_call.rs:163:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:194:18 + --> tests/ui/or_fun_call.rs:206:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:202:14 + --> tests/ui/or_fun_call.rs:214:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:205:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:281:25 + --> tests/ui/or_fun_call.rs:293:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:283:25 + --> tests/ui/or_fun_call.rs:295:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:286:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:317:18 + --> tests/ui/or_fun_call.rs:329:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:321:28 + --> tests/ui/or_fun_call.rs:333:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:325:27 + --> tests/ui/or_fun_call.rs:337:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:22 + --> tests/ui/or_fun_call.rs:341:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:333:23 + --> tests/ui/or_fun_call.rs:345:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:337:25 + --> tests/ui/or_fun_call.rs:349:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:383:17 + --> tests/ui/or_fun_call.rs:395:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:388:17 + --> tests/ui/or_fun_call.rs:400:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:393:17 + --> tests/ui/or_fun_call.rs:405:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:411:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:416:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:411:21 + --> tests/ui/or_fun_call.rs:423:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:426:19 + --> tests/ui/or_fun_call.rs:438:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:428:19 + --> tests/ui/or_fun_call.rs:440:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:431:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:442:15 + --> tests/ui/or_fun_call.rs:454:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:452:15 + --> tests/ui/or_fun_call.rs:464:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:462:15 + --> tests/ui/or_fun_call.rs:474:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From ad21dff50ab20a4a8c306eddffc779dfa9b0ee47 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 11:54:58 +0200 Subject: [PATCH 0551/1900] Do not replace `.unwrap_or(vec![])` by `.unwrap_or_default()` `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. --- clippy_lints/src/methods/or_fun_call.rs | 7 ++ tests/ui/or_fun_call.fixed | 4 ++ tests/ui/or_fun_call.rs | 10 ++- tests/ui/or_fun_call.stderr | 88 ++++++++++++------------- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04f0e3c0479e0..71b2f251eded6 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; +use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -97,6 +98,12 @@ pub(super) fn check<'tcx>( return false; } + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + // needs to target Default::default in particular or be *::new and have a Default impl // available if (is_new(fun) && output_type_implements_default(fun)) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index ac771c34ef49f..7a0be97017ebc 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -89,6 +89,10 @@ fn or_fun_call() { with_default_literal.unwrap_or(""); // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 75762fb020ac0..724af606de9cf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -89,6 +89,10 @@ fn or_fun_call() { with_default_literal.unwrap_or(""); // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call @@ -98,7 +102,7 @@ fn or_fun_call() { //~^ unwrap_or_default let with_vec = Some(vec![1]); - with_vec.unwrap_or(vec![]); + with_vec.unwrap_or(Vec::new()); //~^ unwrap_or_default let without_default = Some(Foo); @@ -110,7 +114,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut map_vec = HashMap::>::new(); - map_vec.entry(42).or_insert(vec![]); + map_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let mut btree = BTreeMap::::new(); @@ -118,7 +122,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut btree_vec = BTreeMap::>::new(); - btree_vec.entry(42).or_insert(vec![]); + btree_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let stringy = Some(String::new()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0f0cd3329b669..40b25f91154dd 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:14 + --> tests/ui/or_fun_call.rs:105:14 | -LL | with_vec.unwrap_or(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +LL | with_vec.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:23 + --> tests/ui/or_fun_call.rs:117:23 | -LL | map_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | map_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:25 + --> tests/ui/or_fun_call.rs:125:25 | -LL | btree_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | btree_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:125:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:130:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:135:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:163:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:206:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:214:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:217:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:293:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:295:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:298:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:345:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:395:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:400:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:405:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:411:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:416:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:423:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:438:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:440:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:443:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:454:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:464:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:474:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From f4e19c68786211f3c3cf2593442629599678800a Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 11 Sep 2025 13:08:36 +0200 Subject: [PATCH 0552/1900] support calls on opaque types :< --- compiler/rustc_hir_analysis/src/autoderef.rs | 15 ++- compiler/rustc_hir_typeck/src/callee.rs | 26 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 37 +++--- compiler/rustc_hir_typeck/src/method/mod.rs | 29 ++++- compiler/rustc_hir_typeck/src/op.rs | 15 ++- compiler/rustc_hir_typeck/src/place_op.rs | 28 ++++- compiler/rustc_infer/src/infer/context.rs | 3 + compiler/rustc_infer/src/infer/mod.rs | 54 ++++++++ .../src/solve/assembly/mod.rs | 40 ++++-- .../src/solve/eval_ctxt/canonical.rs | 25 ++-- .../src/solve/eval_ctxt/mod.rs | 76 +++++++----- .../rustc_next_trait_solver/src/solve/mod.rs | 35 +++--- .../src/solve/search_graph.rs | 2 +- .../src/solve/fulfill.rs | 4 +- .../src/solve/fulfill/derive_errors.rs | 22 ++-- .../src/solve/inspect/analyse.rs | 2 +- .../rustc_trait_selection/src/solve/select.rs | 8 +- .../src/traits/coherence.rs | 2 +- .../src/traits/query/evaluate_obligation.rs | 17 +++ compiler/rustc_type_ir/src/infer_ctxt.rs | 3 +- compiler/rustc_type_ir/src/solve/mod.rs | 80 +++++++++++- .../ambiguous-ops.current.stderr | 62 ++++++++++ .../non-defining-uses/ambiguous-ops.rs | 117 ++++++++++++++++++ .../function-call-on-infer.rs | 73 +++++++++++ .../impl-deref-function-call.rs | 56 +++++++++ .../shex_compat-regression-test.rs | 19 +++ .../return-block-type-inference-15965.stderr | 6 +- ...oxed-closures-failed-recursive-fn-2.stderr | 2 +- 28 files changed, 734 insertions(+), 124 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e8237471e1b68..88bd3339e4e18 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { return None; } - if self.state.cur_ty.is_ty_var() { + // We want to support method and function calls for `impl Deref`. + // + // To do so we don't eagerly bail if the current type is the hidden type of an + // opaque type and instead return `None` in `fn overloaded_deref_ty` if the + // opaque does not have a `Deref` item-bound. + if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind() + && !self.infcx.has_opaques_with_sub_unified_hidden_type(vid) + { return None; } @@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.param_env, ty::Binder::dummy(trait_ref), ); - if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) { + // We detect whether the self type implements `Deref` before trying to + // structurally normalize. We use `predicate_may_hold_opaque_types_jank` + // to support not-yet-defined opaque types. It will succeed for `impl Deref` + // but fail for `impl OtherTrait`. + if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); return None; } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index c6a4d78dcc830..f59fcab46661f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -25,6 +25,7 @@ use tracing::{debug, instrument}; use super::method::MethodCallee; use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use crate::method::TreatNotYetDefinedOpaques; use crate::{errors, fluent_generated}; /// Checks that it is legal to call methods of the trait corresponding @@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.check_expr(callee_expr), }; - let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty); + let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty); let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut result = None; @@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { - let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); + let adjusted_ty = + self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty()); // If the callee is a function pointer or a closure, then we're all set. match *adjusted_ty.kind() { @@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + ty::Infer(ty::TyVar(vid)) => { + // If we end up with an inference variable which is not the hidden type of + // an opaque, emit an error. + if !self.has_opaques_with_sub_unified_hidden_type(vid) { + self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty); + return None; + } + } + ty::Error(_) => { return None; } @@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) }); + // We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty` + // is `Box` we choose `FnOnce` instead of `Fn`. + // + // We try all the different call traits in order and choose the first + // one which may apply. So if we treat opaques as inference variables + // `Box: Fn` is considered ambiguous and chosen. if let Some(ok) = self.lookup_method_for_operator( self.misc(call_expr.span), method_name, trait_def_id, adjusted_ty, opt_input_type, + TreatNotYetDefinedOpaques::AsRigid, ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else { + let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else { bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") }; // For initial two-phase borrow // deployment, conservatively omit // overloaded function call ops. - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No); autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7370124e800da..3444523974af2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.try_structurally_resolve_type(sp, ty); - if !ty.is_ty_var() { - ty - } else { - let e = self.tainted_by_errors().unwrap_or_else(|| { - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - sp, - ty.into(), - TypeAnnotationNeeded::E0282, - true, - ) - .emit() - }); - let err = Ty::new_error(self.tcx, e); - self.demand_suptype(sp, err, ty); - err - } + if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) } + } + + #[cold] + pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let guar = self.tainted_by_errors().unwrap_or_else(|| { + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) + .emit() + }); + let err = Ty::new_error(self.tcx, guar); + self.demand_suptype(sp, err, ty); + err } pub(crate) fn structurally_resolve_const( diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 652644ad78cce..04f112e4a39cc 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )?; Ok(pick) } +} + +/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// +/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while +/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary +/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// types as rigid to support `impl Deref` and +/// `Box`. +/// +/// We only want to treat opaque types as rigid if we need to eagerly choose +/// between multiple candidates. We otherwise treat them as ordinary inference +/// variable to avoid rejecting otherwise correct code. +#[derive(Debug)] +pub(super) enum TreatNotYetDefinedOpaques { + AsInfer, + AsRigid, +} +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs @@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_def_id: DefId, self_ty: Ty<'tcx>, opt_rhs_ty: Option>, + treat_opaques: TreatNotYetDefinedOpaques, ) -> Option>> { // Construct a trait-reference `self_ty : Trait` let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind { @@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { + let matches_trait = match treat_opaques { + TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation), + TreatNotYetDefinedOpaques::AsRigid => { + self.predicate_may_hold_opaque_types_jank(&obligation) + } + }; + + if !matches_trait { debug!("--> Cannot match obligation"); // Cannot be matched, no such method resolution is possible. return None; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 11defc3aa0339..054435379434c 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::FnCtxt; use super::method::MethodCallee; use crate::Expectation; +use crate::method::TreatNotYetDefinedOpaques; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` @@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let method = - self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); + // We don't consider any other candidates if this lookup fails + // so we can freely treat opaque types as inference variables here + // to allow more code to compile. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + let method = self.lookup_method_for_operator( + cause.clone(), + opname, + trait_did, + lhs_ty, + opt_rhs_ty, + treat_opaques, + ); match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1125e98408045..a48db2cc855c0 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -12,7 +12,7 @@ use rustc_span::{Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -use crate::method::MethodCallee; +use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; use crate::{FnCtxt, PlaceOp}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty) + // FIXME(trait-system-refactor-initiative#231): we may want to treat + // opaque types as rigid here to support `impl Deref>`. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + imm_op, + imm_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } fn try_mutable_overloaded_place_op( @@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty) + // We have to replace the operator with the mutable variant for the + // program to compile, so we don't really have a choice here and want + // to just try using `DerefMut` even if its not in the item bounds + // of the opaque. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + mut_op, + mut_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 14cc590720ac5..5ffa7304efaff 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { .map(|(k, h)| (k, h.ty)) .collect() } + fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec> { + self.opaques_with_sub_unified_hidden_type(ty) + } fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9d3886aff1c1a..c9fc124d3bf8d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } + pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool { + if !self.next_trait_solver() { + return false; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return true; + } + } + + false + }) + } + + /// Searches for an opaque type key whose hidden type is related to `ty_vid`. + /// + /// This only checks for a subtype relation, it does not require equality. + pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec> { + // Avoid accidentally allowing more code to compile with the old solver. + if !self.next_trait_solver() { + return vec![]; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(ty::AliasTy::new_from_args( + self.tcx, + key.def_id.into(), + key.args, + )); + } + } + + None + }) + .collect() + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31eb..62dea547890b4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, - MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints, + MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult, + has_no_inference_or_external_constraints, }; enum AliasBoundKind { @@ -474,7 +475,7 @@ where // // cc trait-system-refactor-initiative#105 let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); - let certainty = Certainty::Maybe(cause); + let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood }; self.probe_trait_candidate(source) .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty)) } @@ -974,11 +975,21 @@ where candidates: &mut Vec>, ) { let self_ty = goal.predicate.self_ty(); - // If the self type is sub unified with any opaque type, we - // also look at blanket impls for it. - let mut assemble_blanket_impls = false; - for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { - assemble_blanket_impls = true; + // We only use this hack during HIR typeck. + let opaque_types = match self.typing_mode() { + TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), + TypingMode::Coherence + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => vec![], + }; + + if opaque_types.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + return; + } + + for &alias_ty in &opaque_types { debug!("self ty is sub unified with {alias_ty:?}"); struct ReplaceOpaque { @@ -1028,10 +1039,11 @@ where } } - // We also need to consider blanket impls for not-yet-defined opaque types. + // If the self type is sub unified with any opaque type, we also look at blanket + // impls for it. // // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. - if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + if assemble_from.should_assemble_impl_candidates() { let cx = self.cx(); cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { // For every `default impl`, there's always a non-default `impl` @@ -1062,7 +1074,15 @@ where } if candidates.is_empty() { - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy, + }; + candidates + .extend(self.probe_trait_candidate(source).enter(|this| { + this.evaluate_added_goals_and_make_canonical_response(certainty) + })); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 169832ca5fbde..889588afe61ff 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -15,6 +15,7 @@ use rustc_index::IndexVec; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, @@ -141,8 +142,10 @@ where } }; - if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) = - certainty + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty { // If we have overflow, it's probable that we're substituting a type // into itself infinitely and any partial substitutions in the query @@ -155,7 +158,7 @@ where // // Changing this to retain some constraints in the future // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause)); + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); } let external_constraints = @@ -199,10 +202,13 @@ where .count(); if num_non_region_vars > self.cx().recursion_limit() { debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - })); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); } } } @@ -216,13 +222,14 @@ where /// ambiguity but return constrained variables to guide inference. pub(in crate::solve) fn make_ambiguous_response_no_constraints( &self, - maybe_cause: MaybeCause, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, ) -> CanonicalResponse { response_no_constraints_raw( self.cx(), self.max_input_universe, self.variables, - Certainty::Maybe(maybe_cause), + Certainty::Maybe { cause, opaque_types_jank }, ) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3e3a5246f3d76..5df7c92d88145 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -151,6 +152,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate { stalled_on: Option>, ) -> Result, NoSolution>; + /// Checks whether evaluating `goal` may hold while treating not-yet-defined + /// opaque types as being kind of rigid. + /// + /// See the comment on [OpaqueTypesJank] for more details. + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool; + /// Check whether evaluating `goal` with a depth of `root_depth` may /// succeed. This only returns `false` if the goal is guaranteed to /// not hold. In case evaluation overflows and fails with ambiguity this @@ -193,6 +203,24 @@ where }) } + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool { + self.probe(|| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, None) + }) + .is_ok_and(|r| match r.certainty { + Certainty::Yes => true, + Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank { + OpaqueTypesJank::AllGood => true, + OpaqueTypesJank::ErrorIfRigidSelfTy => false, + }, + }) + }) + } + fn root_goal_may_hold_with_depth( &self, root_depth: usize, @@ -407,8 +435,12 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = - stalled_on + if let Some(GoalStalledOn { + num_opaques, + ref stalled_vars, + ref sub_roots, + stalled_certainty, + }) = stalled_on && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) && !sub_roots .iter() @@ -419,7 +451,7 @@ where NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_cause), + certainty: stalled_certainty, has_changed: HasChanged::No, stalled_on, }, @@ -468,7 +500,7 @@ where let stalled_on = match certainty { Certainty::Yes => None, - Certainty::Maybe(stalled_cause) => match has_changed { + Certainty::Maybe { .. } => match has_changed { // FIXME: We could recompute a *new* set of stalled variables by walking // through the orig values, resolving, and computing the root vars of anything // that is not resolved. Only when *these* have changed is it meaningful @@ -518,7 +550,7 @@ where .len(), stalled_vars, sub_roots, - stalled_cause, + stalled_certainty: certainty, }) } }, @@ -634,7 +666,7 @@ where if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, None)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -710,7 +742,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, with_resolved_vars, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -724,7 +756,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -1184,28 +1216,12 @@ where pub(crate) fn opaques_with_sub_unified_hidden_type( &self, self_ty: I::Ty, - ) -> impl Iterator> + use<'a, D, I> { - let delegate = self.delegate; - delegate - .clone_opaque_types_lookup_table() - .into_iter() - .chain(delegate.clone_duplicate_opaque_types()) - .filter_map(move |(key, hidden_ty)| { - if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { - if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { - if delegate.sub_unification_table_root_var(self_vid) - == delegate.sub_unification_table_root_var(hidden_vid) - { - return Some(ty::AliasTy::new_from_args( - delegate.cx(), - key.def_id.into(), - key.args, - )); - } - } - } - None - }) + ) -> Vec> { + if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() { + self.delegate.opaques_with_sub_unified_hidden_type(vid) + } else { + vec![] + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index cd27c9c26c1fd..fb900b592d13b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -158,9 +158,10 @@ where if self.may_use_unstable_feature(param_env, symbol) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Ambiguity, - )) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }) } } @@ -278,18 +279,21 @@ where fn bail_with_ambiguity(&mut self, candidates: &[Candidate]) -> CanonicalResponse { debug_assert!(candidates.len() > 1); - let maybe_cause = - candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| { - // Pull down the certainty of `Certainty::Yes` to ambiguity when combining + let (cause, opaque_types_jank) = candidates.iter().fold( + (MaybeCause::Ambiguity, OpaqueTypesJank::AllGood), + |(c, jank), candidates| { + // We pull down the certainty of `Certainty::Yes` to ambiguity when combining // these responses, b/c we're combining more than one response and this we // don't know which one applies. - let candidate = match candidates.result.value.certainty { - Certainty::Yes => MaybeCause::Ambiguity, - Certainty::Maybe(candidate) => candidate, - }; - maybe_cause.or(candidate) - }); - self.make_ambiguous_response_no_constraints(maybe_cause) + match candidates.result.value.certainty { + Certainty::Yes => (c, jank), + Certainty::Maybe { cause, opaque_types_jank } => { + (c.or(cause), jank.or(opaque_types_jank)) + } + } + }, + ); + self.make_ambiguous_response_no_constraints(cause, opaque_types_jank) } /// If we fail to merge responses we flounder and return overflow or ambiguity. @@ -427,6 +431,7 @@ pub struct GoalStalledOn { pub num_opaques: usize, pub stalled_vars: Vec, pub sub_roots: Vec, - /// The cause that will be returned on subsequent evaluations if this goal remains stalled. - pub stalled_cause: MaybeCause, + /// The certainty that will be returned on subsequent evaluations if this + /// goal remains stalled. + pub stalled_certainty: Certainty, } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index f0342e0523ff4..289325d70550c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -93,7 +93,7 @@ where fn is_ambiguous_result(result: QueryResult) -> bool { result.is_ok_and(|response| { has_no_inference_or_external_constraints(response) - && matches!(response.value.certainty, Certainty::Maybe(_)) + && matches!(response.value.certainty, Certainty::Maybe { .. }) }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 575e0472e0e38..bff4f6ce3fc6b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -204,7 +204,7 @@ where // // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -258,7 +258,7 @@ where infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } } - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index e31d1052d16c3..eef0ddcbf5965 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -95,15 +95,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( root_obligation.cause.span, None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -266,7 +268,8 @@ impl<'tcx> BestObligation<'tcx> { ); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -407,7 +410,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let tcx = goal.infcx().tcx; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 342d7121fc373..086a7a44786d6 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert_matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) ); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index fb1adc2fd2ac3..8d01c880f8c59 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -62,7 +62,7 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -95,7 +95,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -143,13 +143,13 @@ fn to_selection<'tcx>( span: Span, cand: inspect::InspectCandidate<'_, 'tcx>, ) -> Option> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => thin_vec![], - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals(span) .into_iter() .map(|nested| { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 39afd77a8b682..8e8c7dd7c9d48 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -682,7 +682,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // was irrelevant. match goal.result() { Ok(Certainty::Yes) | Err(NoSolution) => return, - Ok(Certainty::Maybe(_)) => {} + Ok(Certainty::Maybe { .. }) => {} } // For bound predicates we simply call `infcx.enter_forall` 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 9e1a2a3e7d286..ae731505abfa5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,8 +1,11 @@ +use rustc_infer::traits::solve::Goal; use rustc_macros::extension; use rustc_middle::span_bug; +use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use crate::infer::InferCtxt; use crate::infer::canonical::OriginalQueryValues; +use crate::solve::SolverDelegate; use crate::traits::{ EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext, }; @@ -15,6 +18,20 @@ impl<'tcx> InferCtxt<'tcx> { self.evaluate_obligation_no_overflow(obligation).may_apply() } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if self.next_trait_solver() { + <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new( + self.tcx, + obligation.param_env, + obligation.predicate, + )) + } else { + self.predicate_may_hold(obligation) + } + } + /// Evaluates whether the predicate can be satisfied in the given /// `ParamEnv`, and returns `false` if not certain. However, this is /// not entirely accurate if inference variables are involved. diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 56962b4597b88..f743b84bce689 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -6,7 +6,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; -use crate::{self as ty, Interner}; +use crate::{self as ty, Interner, TyVid}; /// The current typing mode of an inference context. We unfortunately have some /// slightly different typing rules depending on the current context. See the @@ -271,6 +271,7 @@ pub trait InferCtxtLike: Sized { &self, prev_entries: Self::OpaqueTypeStorageEntries, ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + fn opaques_with_sub_unified_hidden_type(&self, ty: TyVid) -> Vec>; fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b48a8f46ebef7..1a1606d82685c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -269,11 +269,70 @@ impl NestedNormalizationGoals { #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, - Maybe(MaybeCause), + Maybe { cause: MaybeCause, opaque_types_jank: OpaqueTypesJank }, +} + +/// Supporting not-yet-defined opaque types in HIR typeck is somewhat +/// challenging. Ideally we'd normalize them to a new inference variable +/// and just defer type inference which relies on the opaque until we've +/// constrained the hidden type. +/// +/// This doesn't work for method and function calls as we need to guide type +/// inference for the function arguments. We treat not-yet-defined opaque types +/// as if they were rigid instead in these places. +/// +/// When we encounter a `?hidden_type_of_opaque: Trait` goal, we use the +/// item bounds and blanket impls to guide inference by constraining other type +/// variables, see `EvalCtxt::try_assemble_bounds_via_registered_opaques`. We +/// always keep the certainty as `Maybe` so that we properly prove these goals +/// once the hidden type has been constrained. +/// +/// If we fail to prove the trait goal via item bounds or blanket impls, the +/// goal would have errored if the opaque type were rigid. In this case, we +/// set `OpaqueTypesJank::ErrorIfRigidSelfTy` in the [Certainty]. +/// +/// Places in HIR typeck where we want to treat not-yet-defined opaque types as if +/// they were kind of rigid then use `fn root_goal_may_hold_opaque_types_jank` which +/// returns `false` if the goal doesn't hold or if `OpaqueTypesJank::ErrorIfRigidSelfTy` +/// is set (i.e. proving it required relies on some `?hidden_ty: NotInItemBounds` goal). +/// +/// This is subtly different from actually treating not-yet-defined opaque types as +/// rigid, e.g. it allows constraining opaque types if they are not the self-type of +/// a goal. It is good enough for now and only matters for very rare type inference +/// edge cases. We can improve this later on if necessary. +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum OpaqueTypesJank { + AllGood, + ErrorIfRigidSelfTy, +} +impl OpaqueTypesJank { + fn and(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::AllGood, OpaqueTypesJank::AllGood) => OpaqueTypesJank::AllGood, + (OpaqueTypesJank::ErrorIfRigidSelfTy, _) | (_, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + } + } + + pub fn or(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::ErrorIfRigidSelfTy, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + (OpaqueTypesJank::AllGood, _) | (_, OpaqueTypesJank::AllGood) => { + OpaqueTypesJank::AllGood + } + } + } } impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const AMBIGUOUS: Certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }; /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -290,14 +349,23 @@ impl Certainty { pub fn and(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)), + (Certainty::Yes, Certainty::Maybe { .. }) => other, + (Certainty::Maybe { .. }, Certainty::Yes) => self, + ( + Certainty::Maybe { cause: a_cause, opaque_types_jank: a_jank }, + Certainty::Maybe { cause: b_cause, opaque_types_jank: b_jank }, + ) => Certainty::Maybe { + cause: a_cause.and(b_cause), + opaque_types_jank: a_jank.and(b_jank), + }, } } pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }) + Certainty::Maybe { + cause: MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }, + opaque_types_jank: OpaqueTypesJank::AllGood, + } } } diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr new file mode 100644 index 0000000000000..c54c1bba028cb --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr @@ -0,0 +1,62 @@ +error[E0369]: cannot add `{integer}` to `impl Sized` + --> $DIR/ambiguous-ops.rs:17:15 + | +LL | add() + 1 + | ----- ^ - {integer} + | | + | impl Sized + +error[E0368]: binary assignment operation `*=` cannot be applied to type `impl Sized` + --> $DIR/ambiguous-ops.rs:31:9 + | +LL | temp *= 2; + | ----^^^^^ + | | + | cannot use `*=` on type `impl Sized` + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:57:22 + | +LL | let _rarw = &*explicit_deref(); + | ^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:69:9 + | +LL | *explicit_deref_mut() = 1; + | ^^^^^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:94:18 + | +LL | let _y = explicit_index()[0]; + | ^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:106:9 + | +LL | explicit_index_mut()[0] = 1; + | ^^^^^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0368, E0369, E0614. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs new file mode 100644 index 0000000000000..0aa5715339dc2 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs @@ -0,0 +1,117 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// Make sure we support non-call operations for opaque types even if +// its not part of its item bounds. + +use std::ops::{Deref, DerefMut, Index, IndexMut}; + +fn mk() -> T { + todo!() +} + +fn add() -> impl Sized { + let unconstrained = if false { + add() + 1 + //[current]~^ ERROR cannot add `{integer}` to `impl Sized + } else { + let with_infer = mk(); + let _ = with_infer + 1; + with_infer + }; + let _: u32 = unconstrained; + 1u32 +} + +fn mul_assign() -> impl Sized { + if false { + let mut temp = mul_assign(); + temp *= 2; + //[current]~^ ERROR binary assignment operation `*=` cannot be applied to type `impl Sized` + } + + let mut with_infer = mk(); + with_infer *= 2; + let _: u32 = with_infer; + + 1u32 +} + +struct DerefWrapper(T); +impl Deref for DerefWrapper { + type Target = T::Target; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} +impl DerefMut for DerefWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + +fn explicit_deref() -> DerefWrapper { + if false { + let _rarw = &*explicit_deref(); + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(mk()); + let _rarw = &*with_infer; + with_infer + } else { + DerefWrapper(&1u32) + } +} +fn explicit_deref_mut() -> DerefWrapper { + if false { + *explicit_deref_mut() = 1; + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(Default::default()); + *with_infer = 1; + with_infer + } else { + DerefWrapper(Box::new(1u32)) + } +} + +struct IndexWrapper(T); +impl, U> Index for IndexWrapper { + type Output = T::Output; + fn index(&self, index: U) -> &Self::Output { + &self.0[index] + } +} +impl, U> IndexMut for IndexWrapper { + fn index_mut(&mut self, index: U) -> &mut Self::Output { + &mut self.0[index] + } +} +fn explicit_index() -> IndexWrapper { + if false { + let _y = explicit_index()[0]; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let with_infer = IndexWrapper(Default::default()); + let _y = with_infer[0]; + with_infer + } else { + IndexWrapper([1u32]) + } +} +fn explicit_index_mut() -> IndexWrapper { + if false { + explicit_index_mut()[0] = 1; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let mut with_infer = IndexWrapper(Default::default()); + with_infer[0] = 1; + with_infer + } else { + IndexWrapper([1u32]) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs new file mode 100644 index 0000000000000..9b9156ee4c716 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs @@ -0,0 +1,73 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. Make sure calling +// opaque types works. + +fn fn_trait() -> impl Fn() { + if false { + let f = fn_trait(); + f(); + } + + || () +} + +fn fn_trait_ref() -> impl Fn() { + if false { + let f = &fn_trait(); + f(); + } + || () +} + +fn fn_mut() -> impl FnMut() -> usize { + if false { + let mut f = fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + +fn fn_mut_ref() -> impl FnMut() -> usize { + if false { + let mut f = &mut fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + + +fn fn_once() -> impl FnOnce() { + if false { + let mut f = fn_once(); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn fn_once_ref() -> impl FnOnce() { + if false { + let mut f = Box::new(fn_once_ref()); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs new file mode 100644 index 0000000000000..5ff0dae55cc35 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs @@ -0,0 +1,56 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. We want to +// be able to step through `impl Deref` in its defining scope. +use std::ops::{Deref, DerefMut}; +fn impl_deref_fn() -> impl Deref usize)> { + if false { + let func = impl_deref_fn(); + func(|s| s.len()); + } + + &((|_| ()) as fn(_)) +} + +fn impl_deref_impl_fn() -> impl Deref { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + &|| () +} + +fn impl_deref_impl_deref_impl_fn() -> impl Deref> { + if false { + let func = impl_deref_impl_deref_impl_fn(); + func(); + } + + &&|| () +} + + +fn impl_deref_mut_impl_fn() -> impl DerefMut { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + Box::new(|| ()) +} + + +fn impl_deref_mut_impl_fn_mut() -> impl DerefMut { + if false { + let mut func = impl_deref_mut_impl_fn_mut(); + func(); + } + + let mut state = 0; + Box::new(move || state += 1) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs new file mode 100644 index 0000000000000..aa1b51d6906e4 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. + +struct ShExCompactPrinter; + +struct TripleExpr; + +impl ShExCompactPrinter { + fn pp_triple_expr(&self) -> impl Fn(&TripleExpr, &ShExCompactPrinter) + '_ { + move |te, printer| { + printer.pp_triple_expr()(te, printer); + } + } +} +fn main() {} diff --git a/tests/ui/inference/return-block-type-inference-15965.stderr b/tests/ui/inference/return-block-type-inference-15965.stderr index fc4f2defe7ffa..bfee904192204 100644 --- a/tests/ui/inference/return-block-type-inference-15965.stderr +++ b/tests/ui/inference/return-block-type-inference-15965.stderr @@ -1,10 +1,8 @@ error[E0282]: type annotations needed --> $DIR/return-block-type-inference-15965.rs:5:9 | -LL | / { return () } -LL | | -LL | | () - | |______^ cannot infer type +LL | { return () } + | ^^^^^^^^^^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 058dbb1e220f6..739182e120b4f 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -5,7 +5,7 @@ LL | let mut closure0 = None; | ^^^^^^^^^^^^ ... LL | return c(); - | --- type must be known at this point + | - type must be known at this point | help: consider giving `closure0` an explicit type, where the placeholders `_` are specified | From 9913c47da2b616fee57f308071d6adc39bff4568 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 15 Sep 2025 14:39:01 +0200 Subject: [PATCH 0553/1900] add tests, silence type annotations needed errors for opaques --- compiler/rustc_hir_typeck/src/opaque_types.rs | 34 ++++++----- ...ncorrect-choice-diagnostics.current.stderr | 53 +++++++++++++++++ ...r-incorrect-choice-diagnostics.next.stderr | 57 +++++++++++++++++++ .../call-expr-incorrect-choice-diagnostics.rs | 52 +++++++++++++++++ .../deref-constrains-self-ty.current.stderr | 16 ++++++ .../deref-constrains-self-ty.rs | 28 +++++++++ ...uble-wrap-with-defining-use.current.stderr | 4 +- .../double-wrap-with-defining-use.rs | 1 - .../ice-issue-146191.current.stderr | 2 +- .../ice-issue-146191.next.stderr | 10 +--- .../non-defining-uses/ice-issue-146191.rs | 1 - .../two_tait_defining_each_other2.next.stderr | 8 +-- .../two_tait_defining_each_other2.rs | 3 +- 13 files changed, 231 insertions(+), 38 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 97feac3d0099a..5cefa506b5a06 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -117,21 +117,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) } UsageKind::UnconstrainedHiddenType(hidden_type) => { - let infer_var = hidden_type - .ty - .walk() - .filter_map(ty::GenericArg::as_term) - .find(|term| term.is_infer()) - .unwrap_or_else(|| hidden_type.ty.into()); - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - hidden_type.span, - infer_var, - TypeAnnotationNeeded::E0282, - false, - ) - .emit() + if let Some(guar) = self.tainted_by_errors() { + guar + } else { + let infer_var = hidden_type + .ty + .walk() + .filter_map(ty::GenericArg::as_term) + .find(|term| term.is_infer()) + .unwrap_or_else(|| hidden_type.ty.into()); + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + hidden_type.span, + infer_var, + TypeAnnotationNeeded::E0282, + false, + ) + .emit() + } } UsageKind::HasDefiningUse => continue, }; diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr new file mode 100644 index 0000000000000..e213dab5d9618 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr @@ -0,0 +1,53 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `impl FnOnce()`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Deref` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `impl Deref` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr new file mode 100644 index 0000000000000..5678349cad32d --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr @@ -0,0 +1,57 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `{closure@$DIR/call-expr-incorrect-choice-diagnostics.rs:19:5: 19:12}`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ +help: consider cloning the value if the performance cost is acceptable + | +LL | var.clone()(); + | ++++++++ + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs new file mode 100644 index 0000000000000..1d73985f78a12 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs @@ -0,0 +1,52 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// Testing the errors in case we've made a wrong choice when +// calling an opaque. + +use std::ops::Deref; + +fn item_bound_is_too_weak() -> impl FnOnce() { + if false { + let mut var = item_bound_is_too_weak(); + var(); + var(); + //~^ ERROR use of moved value: `var` + } + + let mut state = String::new(); + move || state.push('a') +} + +fn opaque_type_no_impl_fn() -> impl Sized { + if false { + opaque_type_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + 1 +} + +fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + if false { + opaque_type_no_impl_fn_incorrect()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + || () +} + +fn opaque_type_deref_no_impl_fn() -> impl Deref { + if false { + opaque_type_deref_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Deref` + //[next]~^^ ERROR expected function, found `_` + } + + &1 +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr new file mode 100644 index 0000000000000..bbe90e5873d04 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `len` found for struct `Wrapper` in the current scope + --> $DIR/deref-constrains-self-ty.rs:22:32 + | +LL | struct Wrapper(T); + | ----------------- method `len` not found for this struct +... +LL | let _ = Wrapper(foo()).len(); + | ^^^ method not found in `Wrapper` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `len`, perhaps you need to implement it: + candidate #1: `ExactSizeIterator` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs new file mode 100644 index 0000000000000..d143878bc7466 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// A test which shows that autoderef can constrain opaque types even +// though it's supposed to treat not-yet-defined opaque types as +// mostly rigid. I don't think this should necessarily compile :shrug: +use std::ops::Deref; + +struct Wrapper(T); + +impl Deref for Wrapper> { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo() -> impl Sized { + if false { + let _ = Wrapper(foo()).len(); + //[current]~^ ERROR no method named `len` found for struct `Wrapper` in the current scope + } + + std::iter::once(1).collect() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr index 30424ec58f913..bac5b3e0cf441 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic type parameter, found `impl Foo` - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | fn a(x: T) -> impl Foo { | - this generic parameter must be used with a generic type parameter @@ -7,7 +7,7 @@ LL | if true { x } else { a(a(x)) } | ^^^^^^^ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | if true { x } else { a(a(x)) } | ^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs index 734b1920772f7..39b327eff18ed 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs @@ -1,7 +1,6 @@ // Regression test for ICE from issue #140545 // The error message is confusing and wrong, but that's a different problem (#139350) -//@ edition:2018 //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr index ccbe2d3593c66..5dc66f454652a 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr @@ -5,7 +5,7 @@ LL | fn create_complex_future() -> impl Future { | ^^^^^^^^^^^^^^^^ the trait `ReturnsSend` is not implemented for `()` | help: this trait has no implementations, consider adding one - --> $DIR/ice-issue-146191.rs:14:1 + --> $DIR/ice-issue-146191.rs:13:1 | LL | trait ReturnsSend {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr index e8b551c65fc0c..4a88359ca9679 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr @@ -4,14 +4,6 @@ error[E0282]: type annotations needed LL | async { create_complex_future().await } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/ice-issue-146191.rs:8:5 - | -LL | async { create_complex_future().await } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs index 356f7d01eb9b7..84f139da4e32a 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs @@ -8,7 +8,6 @@ fn create_complex_future() -> impl Future { async { create_complex_future().await } //[current]~^ ERROR recursion in an async block requires //[next]~^^ ERROR type annotations needed - //[next]~| ERROR type annotations needed } trait ReturnsSend {} diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr index 785e5fdeb6433..9b18a9715f21f 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -4,12 +4,6 @@ error[E0282]: type annotations needed LL | fn muh(x: A) -> B { | ^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/two_tait_defining_each_other2.rs:14:5 - | -LL | x // B's hidden type is A (opaquely) - | ^ cannot infer type - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index 99262f4bc4b38..ec2963249f9da 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -12,8 +12,7 @@ trait Foo {} fn muh(x: A) -> B { //[next]~^ ERROR: type annotations needed x // B's hidden type is A (opaquely) - //[next]~^ ERROR: type annotations needed - //[current]~^^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar; From 92646739fe52bc4c40756db71f78fe83b666847e Mon Sep 17 00:00:00 2001 From: aklaiber Date: Tue, 16 Sep 2025 06:36:22 +0200 Subject: [PATCH 0554/1900] Add regression test for issue 91831 --- .../ex3-both-anon-regions-one-is-struct-5.rs | 13 ++++++++++++ ...3-both-anon-regions-one-is-struct-5.stderr | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs create mode 100644 tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs new file mode 100644 index 0000000000000..16039f177b4de --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs @@ -0,0 +1,13 @@ +// Regression test for #91831 + +struct Foo<'a>(&'a i32); + +impl<'a> Foo<'a> { + fn modify(&'a mut self) {} +} + +fn bar(foo: &mut Foo) { + foo.modify(); //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr new file mode 100644 index 0000000000000..f02b65230b6eb --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/ex3-both-anon-regions-one-is-struct-5.rs:10:5 + | +LL | fn bar(foo: &mut Foo) { + | --- - let's call the lifetime of this reference `'1` + | | + | has type `&mut Foo<'2>` +LL | foo.modify(); + | ^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of a mutable reference to `Foo<'_>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance +help: consider introducing a named lifetime parameter + | +LL | fn bar<'a>(foo: &'a mut Foo<'a>) { + | ++++ ++ ++++ + +error: aborting due to 1 previous error + From 3b2bbcd87ebdf9c1a62b1169667481cafe6d8d5d Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 18 Sep 2025 10:57:13 +0200 Subject: [PATCH 0555/1900] internal constraints are better than placeholder outlives --- .../rustc_borrowck/src/region_infer/mod.rs | 7 ++-- .../hr-do-not-blame-outlives-static-ice.rs | 17 ++++++++++ ...hr-do-not-blame-outlives-static-ice.stderr | 34 +++++++++++++++++++ .../do-not-blame-outlives-static-ice.rs | 12 +++++++ .../do-not-blame-outlives-static-ice.stderr | 17 ++++++++++ 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.rs create mode 100644 tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.stderr create mode 100644 tests/ui/higher-ranked/do-not-blame-outlives-static-ice.rs create mode 100644 tests/ui/higher-ranked/do-not-blame-outlives-static-ice.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f57456949bb5e..bd017b213b645 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1726,9 +1726,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // `BoringNoLocation` constraints can point to user-written code, but are less // specific, and are not used for relations that would make sense to blame. ConstraintCategory::BoringNoLocation => 6, - // Do not blame internal constraints. - ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 7, - ConstraintCategory::Internal => 8, + // Do not blame internal constraints if we can avoid it. Never blame + // the `'region: 'static` constraints introduced by placeholder outlives. + ConstraintCategory::Internal => 7, + ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 8, }; debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); diff --git a/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.rs b/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.rs new file mode 100644 index 0000000000000..e5c1f47b9e02b --- /dev/null +++ b/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Zdeduplicate-diagnostics=yes + +// Regression test for #146467. +#![feature(inherent_associated_types)] +//~^ WARN the feature `inherent_associated_types` is incomplete + +struct Foo(T); + +impl<'a> Foo { + //~^ ERROR the lifetime parameter `'a` is not constrained by the impl trait + type Assoc = &'a (); +} + +fn foo(_: for<'a> fn(Foo::Assoc)) {} +//~^ ERROR mismatched types +//~| ERROR higher-ranked subtype error +fn main() {} diff --git a/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.stderr b/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.stderr new file mode 100644 index 0000000000000..4c0726d4ddca9 --- /dev/null +++ b/tests/ui/associated-inherent-types/hr-do-not-blame-outlives-static-ice.stderr @@ -0,0 +1,34 @@ +warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/hr-do-not-blame-outlives-static-ice.rs:4:12 + | +LL | #![feature(inherent_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/hr-do-not-blame-outlives-static-ice.rs:9:6 + | +LL | impl<'a> Foo { + | ^^ unconstrained lifetime parameter + +error[E0308]: mismatched types + --> $DIR/hr-do-not-blame-outlives-static-ice.rs:14:11 + | +LL | fn foo(_: for<'a> fn(Foo::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo fn(&'a ())>` + found struct `Foo fn(&'a ())>` + +error: higher-ranked subtype error + --> $DIR/hr-do-not-blame-outlives-static-ice.rs:14:1 + | +LL | fn foo(_: for<'a> fn(Foo::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0308. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.rs b/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.rs new file mode 100644 index 0000000000000..dfdb816652c44 --- /dev/null +++ b/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zdeduplicate-diagnostics=yes + +// Regression test for #146467. +trait Trait { type Assoc; } + +impl Trait for fn(&()) { type Assoc = (); } + +fn f(_: for<'a> fn(::Assoc)) {} +//~^ ERROR implementation of `Trait` is not general enough +//~| ERROR higher-ranked subtype error + +fn main() {} diff --git a/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.stderr b/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.stderr new file mode 100644 index 0000000000000..c75a063e45fa0 --- /dev/null +++ b/tests/ui/higher-ranked/do-not-blame-outlives-static-ice.stderr @@ -0,0 +1,17 @@ +error: implementation of `Trait` is not general enough + --> $DIR/do-not-blame-outlives-static-ice.rs:8:9 + | +LL | fn f(_: for<'a> fn(::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `for<'a> fn(&'a ())` must implement `Trait`, for any lifetime `'0`... + = note: ...but `Trait` is actually implemented for the type `for<'a> fn(&'a ())` + +error: higher-ranked subtype error + --> $DIR/do-not-blame-outlives-static-ice.rs:8:1 + | +LL | fn f(_: for<'a> fn(::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 1a56d279b8dee896dd46552806686fac513336a7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 18 Sep 2025 20:00:31 +0800 Subject: [PATCH 0556/1900] Fix applicable after l_curly for replace_is_method_with_if_let_method --- .../replace_is_method_with_if_let_method.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 5ef8ba46b9e51..f507cae1bb0de 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -31,6 +31,9 @@ pub(crate) fn replace_is_method_with_if_let_method( ast::Expr::MethodCallExpr(call) => call, _ => return None, }; + if ctx.offset() > if_expr.then_branch()?.stmt_list()?.l_curly_token()?.text_range().end() { + return None; + } let name_ref = call_expr.name_ref()?; match name_ref.text().as_str() { @@ -188,6 +191,21 @@ fn main() { let x = Ok(1); if x.is_e$0rr() {} } +"#, + ); + } + + #[test] + fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() { + check_assist_not_applicable( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_some() { + ()$0 + } +} "#, ); } From 2ed53732932716647b72f5f3e32a06da8634d6e2 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 18 Sep 2025 13:11:37 +0200 Subject: [PATCH 0557/1900] Clean up universe evaluation during type test evaluation The logic was, as the removed comments suggest, hackish and meant to implement previous logic that was factored out. The new logic does exactly what the comments say, and is much less surprising. --- .../rustc_borrowck/src/handle_placeholders.rs | 10 +++------- .../rustc_borrowck/src/region_infer/mod.rs | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 94379cdebf730..6be90994015f3 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -166,13 +166,9 @@ impl RegionTracker { } } - /// Determine if the tracked universes of the two SCCs are compatible. - pub(crate) fn universe_compatible_with(&self, other: Self) -> bool { - // HACK: We first check whether we can name the highest existential universe - // of `other`. This only exists to avoid errors in case that scc already - // depends on a placeholder it cannot name itself. - self.max_nameable_universe().can_name(other.max_nameable_universe()) - || other.reachable_placeholders.can_be_named_by(self.max_nameable_universe()) + /// Determine if we can name all the placeholders in `other`. + pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { + other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0) } /// If this SCC reaches a placeholder it can't name, return it. diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f57456949bb5e..5f4bfd9df48e5 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -571,11 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Returns `true` if all the elements in the value of `scc_b` are nameable + /// Returns `true` if all the placeholders in the value of `scc_b` are nameable /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. - fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b]) + fn can_name_all_placeholders( + &self, + scc_a: ConstraintSccIndex, + scc_b: ConstraintSccIndex, + ) -> bool { + self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) } /// Once regions have been propagated, this method is used to see @@ -964,16 +968,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } + let fr_static = self.universal_regions().fr_static; + // If we are checking that `'sup: 'sub`, and `'sub` contains // some placeholder that `'sup` cannot name, then this is only // true if `'sup` outlives static. - if !self.universe_compatible(sub_region_scc, sup_region_scc) { + // + // Avoid infinite recursion if `sub_region` is already `'static` + if sub_region != fr_static + && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc) + { debug!( "sub universe `{sub_region_scc:?}` is not nameable \ by super `{sup_region_scc:?}`, promoting to static", ); - return self.eval_outlives(sup_region, self.universal_regions().fr_static); + return self.eval_outlives(sup_region, fr_static); } // Both the `sub_region` and `sup_region` consist of the union From 389907a17e5c1eecd9fe41a53ca145f42dcd1488 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 15 Sep 2025 09:42:14 -0400 Subject: [PATCH 0558/1900] Enforce E0719 only for trait aliases --- .../src/error_codes/E0719.md | 14 +- .../src/hir_ty_lowering/bounds.rs | 27 +- .../src/hir_ty_lowering/dyn_trait.rs | 6 +- .../src/hir_ty_lowering/mod.rs | 3 +- .../duplicate-bound-err.rs | 114 +++ .../duplicate-bound-err.stderr | 268 +++++++ .../associated-type-bounds/duplicate-bound.rs | 240 ++++++ tests/ui/associated-type-bounds/duplicate.rs | 278 ------- .../associated-type-bounds/duplicate.stderr | 751 ------------------ ...sociated-types-overridden-binding-2.stderr | 2 +- ...associated-types-overridden-binding.stderr | 2 +- tests/ui/error-codes/E0719.rs | 5 - tests/ui/error-codes/E0719.stderr | 34 +- 13 files changed, 650 insertions(+), 1094 deletions(-) create mode 100644 tests/ui/associated-type-bounds/duplicate-bound-err.rs create mode 100644 tests/ui/associated-type-bounds/duplicate-bound-err.stderr create mode 100644 tests/ui/associated-type-bounds/duplicate-bound.rs delete mode 100644 tests/ui/associated-type-bounds/duplicate.rs delete mode 100644 tests/ui/associated-type-bounds/duplicate.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md index cd981db1058a6..17cbd2de49efd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0719.md +++ b/compiler/rustc_error_codes/src/error_codes/E0719.md @@ -1,4 +1,4 @@ -An associated type value was specified more than once. +An associated item value was specified more than once in a trait object. Erroneous code example: @@ -7,21 +7,15 @@ trait FooTrait {} trait BarTrait {} // error: associated type `Item` in trait `Iterator` is specified twice -struct Foo> { f: T } +type Foo = dyn Iterator; ``` -`Item` in trait `Iterator` cannot be specified multiple times for struct `Foo`. -To fix this, create a new trait that is a combination of the desired traits and -specify the associated type with the new trait. +To fix this, remove the duplicate specifier: Corrected example: ``` -trait FooTrait {} -trait BarTrait {} -trait FooBarTrait: FooTrait + BarTrait {} - -struct Foo> { f: T } // ok! +type Foo = dyn Iterator; // ok! ``` For more information about associated types, see [the book][bk-at]. For more diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 99dc8e6e52217..a8d75ba223ab3 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -362,6 +362,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { param_ty, bounds, predicate_filter, + false, ); } hir::GenericBound::Outlives(lifetime) => { @@ -402,7 +403,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref: ty::PolyTraitRef<'tcx>, constraint: &hir::AssocItemConstraint<'tcx>, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, - duplicates: &mut FxIndexMap, + duplicates: Option<&mut FxIndexMap>, path_span: Span, predicate_filter: PredicateFilter, ) -> Result<(), ErrorGuaranteed> { @@ -458,17 +459,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) .expect("failed to find associated item"); - duplicates - .entry(assoc_item.def_id) - .and_modify(|prev_span| { - self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { - span: constraint.span, - prev_span: *prev_span, - item_name: constraint.ident, - def_path: tcx.def_path_str(assoc_item.container_id(tcx)), - }); - }) - .or_insert(constraint.span); + if let Some(duplicates) = duplicates { + duplicates + .entry(assoc_item.def_id) + .and_modify(|prev_span| { + self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { + span: constraint.span, + prev_span: *prev_span, + item_name: constraint.ident, + def_path: tcx.def_path_str(assoc_item.container_id(tcx)), + }); + }) + .or_insert(constraint.span); + } let projection_term = if let ty::AssocTag::Fn = assoc_tag { let bound_vars = tcx.late_bound_vars(constraint.hir_id); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index c248cd7fec2e5..a4179776572d6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -60,6 +60,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { dummy_self, &mut user_written_bounds, PredicateFilter::SelfOnly, + true, ); if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct { potential_assoc_types.extend(invalid_args); @@ -157,10 +158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx() .struct_span_err( span, - format!( - "conflicting associated type bounds for `{item}` when \ - expanding trait alias" - ), + format!("conflicting associated type bounds for `{item}`"), ) .with_span_label( old_proj_span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9b198d044542f..0ff1fabd7b303 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -752,6 +752,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, predicate_filter: PredicateFilter, + for_dyn: bool, ) -> GenericArgCountResult { let tcx = self.tcx(); @@ -927,7 +928,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { poly_trait_ref, constraint, bounds, - &mut dup_constraints, + for_dyn.then_some(&mut dup_constraints), constraint.span, predicate_filter, ); diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.rs b/tests/ui/associated-type-bounds/duplicate-bound-err.rs new file mode 100644 index 0000000000000..50db5de7ca768 --- /dev/null +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.rs @@ -0,0 +1,114 @@ +//@ edition: 2024 + +#![feature(associated_const_equality, type_alias_impl_trait, return_type_notation)] +#![allow(refining_impl_trait_internal)] + +use std::iter; + +fn frpit1() -> impl Iterator { + iter::empty() + //~^ ERROR type annotations needed +} +fn frpit2() -> impl Iterator { + iter::empty() + //~^ ERROR type annotations needed +} +fn frpit3() -> impl Iterator { + iter::empty() + //~^ ERROR type annotations needed +} + +type ETAI1> = impl Copy; +//~^ ERROR unconstrained opaque type +type ETAI2> = impl Copy; +//~^ ERROR unconstrained opaque type +type ETAI3> = impl Copy; +//~^ ERROR unconstrained opaque type + +type ETAI4 = impl Iterator; +//~^ ERROR unconstrained opaque type +type ETAI5 = impl Iterator; +//~^ ERROR unconstrained opaque type +type ETAI6 = impl Iterator; +//~^ ERROR unconstrained opaque type + +fn mismatch() -> impl Iterator { + //~^ ERROR [E0277] + iter::empty::<*const ()>() +} + +fn mismatch_2() -> impl Iterator { + //~^ ERROR [E0277] + iter::empty::() +} + +trait Trait { + type Gat; + + const ASSOC: i32; + + fn foo() -> impl Sized; +} + +impl Trait for () { + type Gat = (); + + const ASSOC: i32 = 3; + + fn foo() {} +} + +impl Trait for u32 { + type Gat = (); + + const ASSOC: i32 = 4; + + fn foo() -> u32 { + 42 + } +} + +fn uncallable(_: impl Iterator) {} + +fn uncallable_const(_: impl Trait) {} + +fn uncallable_rtn(_: impl Trait, foo(..): Trait>) {} + +type MustFail = dyn Iterator; +//~^ ERROR [E0719] +//~| ERROR conflicting associated type bounds + +trait Trait2 { + const ASSOC: u32; +} + +type MustFail2 = dyn Trait2; +//~^ ERROR [E0719] +//~| ERROR conflicting associated type bounds + +type MustFail3 = dyn Iterator; +//~^ ERROR [E0719] + +type MustFail4 = dyn Trait2; +//~^ ERROR [E0719] + +trait Trait3 { + fn foo() -> impl Iterator; +} + +impl Trait3 for () { + fn foo() -> impl Iterator { + //~^ ERROR[E0271] + //~| ERROR[E0271] + [2u32].into_iter() + } +} + +fn main() { + uncallable(iter::empty::()); //~ ERROR [E0271] + uncallable(iter::empty::()); //~ ERROR [E0271] + uncallable_const(()); //~ ERROR [E0271] + uncallable_const(4u32); //~ ERROR [E0271] + uncallable_rtn(()); //~ ERROR [E0271] + uncallable_rtn(17u32); //~ ERROR [E0271] +} diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr new file mode 100644 index 0000000000000..6c1dc03676c79 --- /dev/null +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -0,0 +1,268 @@ +error[E0282]: type annotations needed + --> $DIR/duplicate-bound-err.rs:9:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0282]: type annotations needed + --> $DIR/duplicate-bound-err.rs:13:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0282]: type annotations needed + --> $DIR/duplicate-bound-err.rs:17:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:21:51 + | +LL | type ETAI1> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI1` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:23:51 + | +LL | type ETAI2> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI2` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:25:57 + | +LL | type ETAI3> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI3` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:28:14 + | +LL | type ETAI4 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI4` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:30:14 + | +LL | type ETAI5 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI5` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate-bound-err.rs:32:14 + | +LL | type ETAI6 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI6` must be used in combination with a concrete type within the same crate + +error[E0277]: `*const ()` cannot be sent between threads safely + --> $DIR/duplicate-bound-err.rs:35:18 + | +LL | fn mismatch() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const ()` cannot be sent between threads safely +LL | +LL | iter::empty::<*const ()>() + | -------------------------- return type was inferred to be `std::iter::Empty<*const ()>` here + | + = help: the trait `Send` is not implemented for `*const ()` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/duplicate-bound-err.rs:40:20 + | +LL | fn mismatch_2() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` +LL | +LL | iter::empty::() + | ----------------------- return type was inferred to be `std::iter::Empty` here + +error[E0271]: expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/duplicate-bound-err.rs:100:17 + | +LL | fn foo() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` +... +LL | [2u32].into_iter() + | ------------------ return type was inferred to be `std::array::IntoIter` here + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate-bound-err.rs:77:42 + | +LL | type MustFail = dyn Iterator; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error: conflicting associated type bounds for `Item` + --> $DIR/duplicate-bound-err.rs:77:17 + | +LL | type MustFail = dyn Iterator; + | ^^^^^^^^^^^^^----------^^----------^ + | | | + | | `Item` is specified to be `u32` here + | `Item` is specified to be `i32` here + +error[E0719]: the value of the associated type `ASSOC` in trait `Trait2` is already specified + --> $DIR/duplicate-bound-err.rs:85:43 + | +LL | type MustFail2 = dyn Trait2; + | ------------ ^^^^^^^^^^^^ re-bound here + | | + | `ASSOC` bound here first + +error: conflicting associated type bounds for `ASSOC` + --> $DIR/duplicate-bound-err.rs:85:18 + | +LL | type MustFail2 = dyn Trait2; + | ^^^^^^^^^^^------------^^------------^ + | | | + | | `ASSOC` is specified to be `4` here + | `ASSOC` is specified to be `3` here + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate-bound-err.rs:89:43 + | +LL | type MustFail3 = dyn Iterator; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `ASSOC` in trait `Trait2` is already specified + --> $DIR/duplicate-bound-err.rs:92:43 + | +LL | type MustFail4 = dyn Trait2; + | ------------ ^^^^^^^^^^^^ re-bound here + | | + | `ASSOC` bound here first + +error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/duplicate-bound-err.rs:100:17 + | +LL | fn foo() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` + | +note: required by a bound in `Trait3::foo::{anon_assoc#0}` + --> $DIR/duplicate-bound-err.rs:96:31 + | +LL | fn foo() -> impl Iterator; + | ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}` + +error[E0271]: expected `Empty` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/duplicate-bound-err.rs:108:16 + | +LL | uncallable(iter::empty::()); + | ---------- ^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` + | | + | required by a bound introduced by this call + | +note: required by a bound in `uncallable` + --> $DIR/duplicate-bound-err.rs:71:32 + | +LL | fn uncallable(_: impl Iterator) {} + | ^^^^^^^^^^ required by this bound in `uncallable` + +error[E0271]: expected `Empty` to be an iterator that yields `u32`, but it yields `i32` + --> $DIR/duplicate-bound-err.rs:109:16 + | +LL | uncallable(iter::empty::()); + | ---------- ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `i32` + | | + | required by a bound introduced by this call + | +note: required by a bound in `uncallable` + --> $DIR/duplicate-bound-err.rs:71:44 + | +LL | fn uncallable(_: impl Iterator) {} + | ^^^^^^^^^^ required by this bound in `uncallable` + +error[E0271]: type mismatch resolving `<() as Trait>::ASSOC == 4` + --> $DIR/duplicate-bound-err.rs:110:22 + | +LL | uncallable_const(()); + | ---------------- ^^ expected `4`, found `3` + | | + | required by a bound introduced by this call + | + = note: expected constant `4` + found constant `3` +note: required by a bound in `uncallable_const` + --> $DIR/duplicate-bound-err.rs:73:46 + | +LL | fn uncallable_const(_: impl Trait) {} + | ^^^^^^^^^ required by this bound in `uncallable_const` + +error[E0271]: type mismatch resolving `::ASSOC == 3` + --> $DIR/duplicate-bound-err.rs:111:22 + | +LL | uncallable_const(4u32); + | ---------------- ^^^^ expected `3`, found `4` + | | + | required by a bound introduced by this call + | + = note: expected constant `3` + found constant `4` +note: required by a bound in `uncallable_const` + --> $DIR/duplicate-bound-err.rs:73:35 + | +LL | fn uncallable_const(_: impl Trait) {} + | ^^^^^^^^^ required by this bound in `uncallable_const` + +error[E0271]: type mismatch resolving `<() as Trait>::ASSOC == 4` + --> $DIR/duplicate-bound-err.rs:112:20 + | +LL | uncallable_rtn(()); + | -------------- ^^ expected `4`, found `3` + | | + | required by a bound introduced by this call + | + = note: expected constant `4` + found constant `3` +note: required by a bound in `uncallable_rtn` + --> $DIR/duplicate-bound-err.rs:75:75 + | +LL | fn uncallable_rtn(_: impl Trait, foo(..): Trait>) {} + | ^^^^^^^^^ required by this bound in `uncallable_rtn` + +error[E0271]: type mismatch resolving `::ASSOC == 3` + --> $DIR/duplicate-bound-err.rs:113:20 + | +LL | uncallable_rtn(17u32); + | -------------- ^^^^^ expected `3`, found `4` + | | + | required by a bound introduced by this call + | + = note: expected constant `3` + found constant `4` +note: required by a bound in `uncallable_rtn` + --> $DIR/duplicate-bound-err.rs:75:48 + | +LL | fn uncallable_rtn(_: impl Trait, foo(..): Trait>) {} + | ^^^^^^^^^ required by this bound in `uncallable_rtn` + +error: aborting due to 25 previous errors + +Some errors have detailed explanations: E0271, E0277, E0282, E0719. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-type-bounds/duplicate-bound.rs b/tests/ui/associated-type-bounds/duplicate-bound.rs new file mode 100644 index 0000000000000..97b2b3905a55e --- /dev/null +++ b/tests/ui/associated-type-bounds/duplicate-bound.rs @@ -0,0 +1,240 @@ +//@ edition: 2024 +//@ run-pass + +#![feature(associated_const_equality, return_type_notation)] +#![allow(dead_code, refining_impl_trait_internal, type_alias_bounds)] + +use std::iter; +use std::mem::ManuallyDrop; + +struct SI1> { + f: T, +} +struct SI2> { + f: T, +} +struct SI3> { + f: T, +} +struct SW1 +where + T: Iterator, +{ + f: T, +} +struct SW2 +where + T: Iterator, +{ + f: T, +} +struct SW3 +where + T: Iterator, +{ + f: T, +} + +enum EI1> { + V(T), +} +enum EI2> { + V(T), +} +enum EI3> { + V(T), +} +enum EW1 +where + T: Iterator, +{ + V(T), +} +enum EW2 +where + T: Iterator, +{ + V(T), +} +enum EW3 +where + T: Iterator, +{ + V(T), +} + +union UI1> { + f: ManuallyDrop, +} +union UI2> { + f: ManuallyDrop, +} +union UI3> { + f: ManuallyDrop, +} +union UW1 +where + T: Iterator, +{ + f: ManuallyDrop, +} +union UW2 +where + T: Iterator, +{ + f: ManuallyDrop, +} +union UW3 +where + T: Iterator, +{ + f: ManuallyDrop, +} + +fn fi1>() {} +fn fi2>() {} +fn fi3>() {} +fn fw1() +where + T: Iterator, +{ +} +fn fw2() +where + T: Iterator, +{ +} +fn fw3() +where + T: Iterator, +{ +} + +fn frpit1() -> impl Iterator { + iter::empty::() +} +fn frpit2() -> impl Iterator { + iter::empty::() +} +fn frpit3() -> impl Iterator { + iter::empty::() +} +fn fapit1(_: impl Iterator) {} +fn fapit2(_: impl Iterator) {} +fn fapit3(_: impl Iterator) {} + +type TAI1> = T; +type TAI2> = T; +type TAI3> = T; +type TAW1 +where + T: Iterator, += T; +type TAW2 +where + T: Iterator, += T; +type TAW3 +where + T: Iterator, += T; + +trait TRI1> {} +trait TRI2> {} +trait TRI3> {} +trait TRS1: Iterator {} +trait TRS2: Iterator {} +trait TRS3: Iterator {} +trait TRW1 +where + T: Iterator, +{ +} +trait TRW2 +where + T: Iterator, +{ +} +trait TRW3 +where + T: Iterator, +{ +} +trait TRSW1 +where + Self: Iterator, +{ +} +trait TRSW2 +where + Self: Iterator, +{ +} +trait TRSW3 +where + Self: Iterator, +{ +} +trait TRA1 { + type A: Iterator; +} +trait TRA2 { + type A: Iterator; +} +trait TRA3 { + type A: Iterator; +} + +trait Trait { + type Gat; + + const ASSOC: i32; + + fn foo() -> impl Sized; +} + +impl Trait for () { + type Gat = (); + + const ASSOC: i32 = 3; + + fn foo() {} +} + +trait Subtrait: Trait = u32, Gat = u64> {} + +fn f = (), Gat = ()>>() { + let _: T::Gat = (); + let _: T::Gat = (); +} + +fn g = (), Gat = &'static str>>() { + let _: T::Gat = (); + let _: T::Gat = ""; +} + +fn uncallable(_: impl Iterator) {} + +fn callable(_: impl Iterator) {} + +fn uncallable_const(_: impl Trait) {} + +fn callable_const(_: impl Trait) {} + +fn uncallable_rtn(_: impl Trait, foo(..): Trait>) {} + +fn callable_rtn(_: impl Trait) {} + +trait Trait2 { + const ASSOC: u32; +} + +trait Trait3 { + fn foo() -> impl Iterator; +} + +fn main() { + callable(iter::empty::()); + callable_const(()); + callable_rtn(()); +} diff --git a/tests/ui/associated-type-bounds/duplicate.rs b/tests/ui/associated-type-bounds/duplicate.rs deleted file mode 100644 index e9d94787e9829..0000000000000 --- a/tests/ui/associated-type-bounds/duplicate.rs +++ /dev/null @@ -1,278 +0,0 @@ -#![feature(type_alias_impl_trait)] - -use std::iter; -use std::mem::ManuallyDrop; - -struct SI1> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: T, -} -struct SI2> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: T, -} -struct SI3> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: T, -} -struct SW1 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: T, -} -struct SW2 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: T, -} -struct SW3 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: T, -} - -enum EI1> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - V(T), -} -enum EI2> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - V(T), -} -enum EI3> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - V(T), -} -enum EW1 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - V(T), -} -enum EW2 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - V(T), -} -enum EW3 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - V(T), -} - -union UI1> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: ManuallyDrop, -} -union UI2> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: ManuallyDrop, -} -union UI3> { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - f: ManuallyDrop, -} -union UW1 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: ManuallyDrop, -} -union UW2 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: ManuallyDrop, -} -union UW3 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ - f: ManuallyDrop, -} - -fn FI1>() {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -fn FI2>() {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -fn FI3>() {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -fn FW1() -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -fn FW2() -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -fn FW3() -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} - -fn FRPIT1() -> impl Iterator { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - iter::empty() - //~^ ERROR type annotations needed -} -fn FRPIT2() -> impl Iterator { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - iter::empty() - //~^ ERROR type annotations needed -} -fn FRPIT3() -> impl Iterator { - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - iter::empty() - //~^ ERROR type annotations needed -} -fn FAPIT1(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -fn FAPIT2(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -fn FAPIT3(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - -type TAI1> = T; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -type TAI2> = T; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -type TAI3> = T; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -type TAW1 -where - T: Iterator, -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -= T; -type TAW2 -where - T: Iterator, -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -= T; -type TAW3 -where - T: Iterator, -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -= T; - -type ETAI1> = impl Copy; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type -type ETAI2> = impl Copy; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type -type ETAI3> = impl Copy; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type -type ETAI4 = impl Iterator; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type -type ETAI5 = impl Iterator; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type -type ETAI6 = impl Iterator; -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR unconstrained opaque type - -trait TRI1> {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRI2> {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRI3> {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRS1: Iterator {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRS2: Iterator {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRS3: Iterator {} -//~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -trait TRW1 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRW2 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRW3 -where - T: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRSW1 -where - Self: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRSW2 -where - Self: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRSW3 -where - Self: Iterator, - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -{ -} -trait TRA1 { - type A: Iterator; - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -} -trait TRA2 { - type A: Iterator; - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -} -trait TRA3 { - type A: Iterator; - //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] - //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] -} - -fn main() {} diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr deleted file mode 100644 index 68fbb345f6f93..0000000000000 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ /dev/null @@ -1,751 +0,0 @@ -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:6:36 - | -LL | struct SI1> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:10:36 - | -LL | struct SI2> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:14:39 - | -LL | struct SI3> { - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:20:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:27:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:34:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:40:34 - | -LL | enum EI1> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:44:34 - | -LL | enum EI2> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:48:37 - | -LL | enum EI3> { - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:54:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:61:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:68:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:74:35 - | -LL | union UI1> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:78:35 - | -LL | union UI2> { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:82:38 - | -LL | union UI3> { - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:88:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:95:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:102:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:108:32 - | -LL | fn FI1>() {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:110:32 - | -LL | fn FI2>() {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:112:35 - | -LL | fn FI3>() {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:116:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:122:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:128:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:133:42 - | -LL | fn FRPIT1() -> impl Iterator { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:139:42 - | -LL | fn FRPIT2() -> impl Iterator { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:145:45 - | -LL | fn FRPIT3() -> impl Iterator { - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:151:40 - | -LL | fn FAPIT1(_: impl Iterator) {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:153:40 - | -LL | fn FAPIT2(_: impl Iterator) {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:155:43 - | -LL | fn FAPIT3(_: impl Iterator) {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:158:35 - | -LL | type TAI1> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:160:35 - | -LL | type TAI2> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:162:38 - | -LL | type TAI3> = T; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:166:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:171:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:176:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:180:36 - | -LL | type ETAI1> = impl Copy; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:183:36 - | -LL | type ETAI2> = impl Copy; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:186:39 - | -LL | type ETAI3> = impl Copy; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:202:36 - | -LL | trait TRI1> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:204:36 - | -LL | trait TRI2> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:206:39 - | -LL | trait TRI3> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:208:34 - | -LL | trait TRS1: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:208:34 - | -LL | trait TRS1: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:208:34 - | -LL | trait TRS1: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:212:34 - | -LL | trait TRS2: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:212:34 - | -LL | trait TRS2: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:212:34 - | -LL | trait TRS2: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:216:37 - | -LL | trait TRS3: Iterator {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:216:37 - | -LL | trait TRS3: Iterator {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:216:37 - | -LL | trait TRS3: Iterator {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:222:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:228:29 - | -LL | T: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:234:32 - | -LL | T: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:240:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:240:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:240:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:248:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:248:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:248:32 - | -LL | Self: Iterator, - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:256:35 - | -LL | Self: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:256:35 - | -LL | Self: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:256:35 - | -LL | Self: Iterator, - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:263:34 - | -LL | type A: Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:263:34 - | -LL | type A: Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:268:34 - | -LL | type A: Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:268:34 - | -LL | type A: Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:273:37 - | -LL | type A: Iterator; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:273:37 - | -LL | type A: Iterator; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:133:42 - | -LL | fn FRPIT1() -> impl Iterator { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:136:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:139:42 - | -LL | fn FRPIT2() -> impl Iterator { - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:142:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:145:45 - | -LL | fn FRPIT3() -> impl Iterator { - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:148:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - -error: unconstrained opaque type - --> $DIR/duplicate.rs:180:51 - | -LL | type ETAI1> = impl Copy; - | ^^^^^^^^^ - | - = note: `ETAI1` must be used in combination with a concrete type within the same crate - -error: unconstrained opaque type - --> $DIR/duplicate.rs:183:51 - | -LL | type ETAI2> = impl Copy; - | ^^^^^^^^^ - | - = note: `ETAI2` must be used in combination with a concrete type within the same crate - -error: unconstrained opaque type - --> $DIR/duplicate.rs:186:57 - | -LL | type ETAI3> = impl Copy; - | ^^^^^^^^^ - | - = note: `ETAI3` must be used in combination with a concrete type within the same crate - -error: unconstrained opaque type - --> $DIR/duplicate.rs:189:14 - | -LL | type ETAI4 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `ETAI4` must be used in combination with a concrete type within the same crate - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:189:40 - | -LL | type ETAI4 = impl Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:189:40 - | -LL | type ETAI4 = impl Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: unconstrained opaque type - --> $DIR/duplicate.rs:193:14 - | -LL | type ETAI5 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `ETAI5` must be used in combination with a concrete type within the same crate - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:193:40 - | -LL | type ETAI5 = impl Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:193:40 - | -LL | type ETAI5 = impl Iterator; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: unconstrained opaque type - --> $DIR/duplicate.rs:197:14 - | -LL | type ETAI6 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `ETAI6` must be used in combination with a concrete type within the same crate - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:197:43 - | -LL | type ETAI6 = impl Iterator; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:197:43 - | -LL | type ETAI6 = impl Iterator; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 87 previous errors - -Some errors have detailed explanations: E0282, E0719. -For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr index 71a4a2610aac4..e96a2446b6ce0 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -1,4 +1,4 @@ -error: conflicting associated type bounds for `Item` when expanding trait alias +error: conflicting associated type bounds for `Item` --> $DIR/associated-types-overridden-binding-2.rs:6:13 | LL | trait I32Iterator = Iterator; diff --git a/tests/ui/associated-types/associated-types-overridden-binding.stderr b/tests/ui/associated-types/associated-types-overridden-binding.stderr index 3b20015dfcab3..08ab9b63ee9fd 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding.stderr @@ -22,7 +22,7 @@ note: required by a bound in `I32Iterator` LL | trait I32Iterator = Iterator; | ^^^^^^^^^^ required by this bound in `I32Iterator` -error: conflicting associated type bounds for `Item` when expanding trait alias +error: conflicting associated type bounds for `Item` --> $DIR/associated-types-overridden-binding.rs:10:13 | LL | trait I32Iterator = Iterator; diff --git a/tests/ui/error-codes/E0719.rs b/tests/ui/error-codes/E0719.rs index 0ea6d19000bd3..d7b4b876d1b78 100644 --- a/tests/ui/error-codes/E0719.rs +++ b/tests/ui/error-codes/E0719.rs @@ -1,8 +1,3 @@ -trait Foo: Iterator {} -//~^ ERROR is already specified -//~| ERROR is already specified -//~| ERROR is already specified - type Unit = (); fn test() -> Box> { diff --git a/tests/ui/error-codes/E0719.stderr b/tests/ui/error-codes/E0719.stderr index 7e8329db1f48f..f48175689249e 100644 --- a/tests/ui/error-codes/E0719.stderr +++ b/tests/ui/error-codes/E0719.stderr @@ -1,33 +1,5 @@ error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:1:33 - | -LL | trait Foo: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:1:33 - | -LL | trait Foo: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:1:33 - | -LL | trait Foo: Iterator {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:8:42 + --> $DIR/E0719.rs:3:42 | LL | fn test() -> Box> { | --------- ^^^^^^^^^^^ re-bound here @@ -35,13 +7,13 @@ LL | fn test() -> Box> { | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/E0719.rs:14:38 + --> $DIR/E0719.rs:9:38 | LL | let _: &dyn Iterator; | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0719`. From 495d7ee587dc1b8d99fd9f0bce2f72b0072e3aca Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 18 Sep 2025 09:41:23 -0400 Subject: [PATCH 0559/1900] Include patch in release notes This should fix triagebot publication of the GitHub release on merge. --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2b65d070d5fd1..aa5f37fffebff 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,5 @@ -Version 1.90 (2025-09-18) -========================== +Version 1.90.0 (2025-09-18) +=========================== From 7e270ab27e80c06275c80fa1041f5ff9af3ffcd4 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 18 Sep 2025 22:22:29 +0900 Subject: [PATCH 0560/1900] chore: Update rustc deps --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/next_solver/interner.rs | 14 ++++++ 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b70b89ea543d8..9475391acdb7f 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da95e732b424802b1f043ab4007c78a0fc515ab249587abbea4634bf5fdce9a" +checksum = "aa338fe027a8915009ca4a5a1cb7dde5fb4bc4170a928cb9462fda9d2ec52cec" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3838d9d7a3a5cdc511cfb6ad78740ce532f75a2366d3fc3b9853ea1b5c872779" +checksum = "a8468ef77e5359b3a51e327406f29ca2283a4feef93d3ba04f6740b274636922" [[package]] name = "ra-ap-rustc_hashes" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc8995d268d3bb4ece910f575ea5a063d6003e193ec155d15703b65882d53fb" +checksum = "300bc3264ccc1e7a5b3f065023a02e612774206d8ad685b3b05c2e4e317f8daa" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0ccdf6e5627c6c3e54e571e52ce0bc8b94d5f0b94b7460269ca68a4706be69" +checksum = "5eaa4a3ff61302e45c17ee72e067a39179081c19a12aa03192975a095f5d4e4b" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd28f42362b5c9fb9b8766c3189df02a402b13363600c6885e11027889f03ee6" +checksum = "0f7af0d51ee6bd5280be8e2eb7e9ac5cd9fc87af7a99f50cdb1316a8779c15ab" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c31a82f091b910a27ee53a86a9af28a2df10c3484e2f1bbfe70633aa84dee9" +checksum = "ee4e7df9bf702c855de7bea5e3c14b96f0728d4712edb663b0f4b183622341fc" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cac6c2b5a8924209d4ca682cbc507252c58a664911e0ef463c112882ba6f72" +checksum = "15768080a276088a4a6af1e08a4ca622c57d5b4845ce5329dbbd71a2e025eecb" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a085a1cf902dcca8abbc537faaef154bbccbbb51850f779ce5484ae3782b5d8f" +checksum = "e6f0c54b200c47768eaf142b1c829da9be1a3331a5defa4ac60bad4996f474e9" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba32e3985367bc34856b41c7604133649d4a367eb5d7bdf50623025731459d8" +checksum = "a078fbbefda17d8d5d2c9d6b5a1f9ee1e23fae5f057e74784f6b95c189b0b048" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9911d72f75d85d21fe88374d7bcec94f2200feffb7234108a24cc3da7c3591" +checksum = "644e980122cdb7f2d7e175f33224dc6df414e8cf3e5dfbba9047e63336d9737a" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f539b87991683ce17cc52e62600fdf2b4a8af43952db30387edc1a576d3b43" +checksum = "827d242d444cea86d9a64b5ce99db1462c9d43c7c6226d44ec2bc621b7c253c0" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c5ffad544a6b3..94ec1a07438f4 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.128", default-features = false } -ra-ap-rustc_parse_format = { version = "0.128", default-features = false } -ra-ap-rustc_index = { version = "0.128", default-features = false } -ra-ap-rustc_abi = { version = "0.128", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.128", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.128", default-features = false } -ra-ap-rustc_type_ir = { version = "0.128", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.128", default-features = false } +ra-ap-rustc_lexer = { version = "0.129", default-features = false } +ra-ap-rustc_parse_format = { version = "0.129", default-features = false } +ra-ap-rustc_index = { version = "0.129", default-features = false } +ra-ap-rustc_abi = { version = "0.129", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.129", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.129", default-features = false } +ra-ap-rustc_type_ir = { version = "0.129", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.129", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 062f6aebf160d..9cf56bef9578a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1674,6 +1674,20 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } + fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, mut f: impl FnMut(Self::ImplId)) { + let Some(krate) = self.krate else { return }; + + for impls in self.db.trait_impls_in_deps(krate).iter() { + for impl_id in impls.for_trait(trait_def_id.0) { + let impl_data = self.db.impl_signature(impl_id); + let self_ty_ref = &impl_data.store[impl_data.self_ty]; + if matches!(self_ty_ref, hir_def::type_ref::TypeRef::TypeParam(_)) { + f(impl_id.into()); + } + } + } + } + fn has_item_definition(self, def_id: Self::DefId) -> bool { // FIXME(next-solver): should check if the associated item has a value. true From d6890e33e5d5f0e7b5a48b2692047fc824bf3fca Mon Sep 17 00:00:00 2001 From: bendn Date: Thu, 18 Sep 2025 20:20:59 +0700 Subject: [PATCH 0561/1900] extend while_let_loop to loop { let else } --- clippy_lints/src/loops/mod.rs | 1 + clippy_lints/src/loops/while_let_loop.rs | 30 +++++++++++++++++------- tests/ui/infinite_loops.rs | 2 +- tests/ui/while_let_loop.rs | 13 ++++++++++ tests/ui/while_let_loop.stderr | 29 +++++++++++++++-------- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 01c36b8cb12fc..7d14aa87d820a 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -861,6 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) + // (also matches on `let {} else break`) if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 845edb9cae158..d4285db0abfcd 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -10,19 +10,19 @@ use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { - let (init, let_info) = match (loop_block.stmts, loop_block.expr) { + let (init, let_info, els) = match (loop_block.stmts, loop_block.expr) { ([stmt, ..], _) => match stmt.kind { StmtKind::Let(LetStmt { init: Some(e), - els: None, + els, pat, ty, .. - }) => (*e, Some((*pat, *ty))), - StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None), + }) => (*e, Some((*pat, *ty)), *els), + StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None, None), _ => return, }, - ([], Some(e)) => (e, None), + ([], Some(e)) => (e, None, None), _ => return, }; let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; @@ -38,14 +38,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo if_let.let_expr, has_trailing_exprs, let_info, - if_let.if_then, + Some(if_let.if_then), ); + } else if els.and_then(|x| x.expr).is_some_and(is_simple_break_expr) + && let Some((pat, _)) = let_info + { + could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() && is_simple_break_expr(arm2.body) { - could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body); + could_be_while_let( + cx, + expr, + arm1.pat, + scrutinee, + has_trailing_exprs, + let_info, + Some(arm1.body), + ); } } @@ -70,7 +82,7 @@ fn could_be_while_let<'tcx>( let_expr: &'tcx Expr<'_>, has_trailing_exprs: bool, let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>, - inner_expr: &Expr<'_>, + inner_expr: Option<&Expr<'_>>, ) { if has_trailing_exprs && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr)) @@ -85,7 +97,7 @@ fn could_be_while_let<'tcx>( // 1) it was ugly with big bodies; // 2) it was not indented properly; // 3) it wasn’t very smart (see #675). - let inner_content = if let Some((pat, ty)) = let_info + let inner_content = if let Some(((pat, ty), inner_expr)) = let_info.zip(inner_expr) // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but // keep them if the type has been explicitly specified. && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index 7d01a7fb61fc1..0bde31aca0304 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -1,7 +1,7 @@ //@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs -#![allow(clippy::never_loop)] +#![allow(clippy::never_loop, clippy::while_let_loop)] #![warn(clippy::infinite_loop)] extern crate proc_macros; diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index 95062c9f46c7d..f28c504742fd7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -22,6 +22,19 @@ fn main() { break; } + loop { + //~^ while_let_loop + let Some(_x) = y else { break }; + } + + loop { + // no error, else branch does something other than break + let Some(_x) = y else { + let _z = 1; + break; + }; + } + loop { //~^ while_let_loop diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index ed42628a53e7f..b9aee6eb42ecf 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -17,6 +17,15 @@ error: this loop could be written as a `while let` loop | LL | / loop { LL | | +LL | | let Some(_x) = y else { break }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | LL | | LL | | match y { ... | @@ -25,7 +34,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:34:5 + --> tests/ui/while_let_loop.rs:47:5 | LL | / loop { LL | | @@ -37,7 +46,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:45:5 + --> tests/ui/while_let_loop.rs:58:5 | LL | / loop { LL | | @@ -48,7 +57,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:77:5 + --> tests/ui/while_let_loop.rs:90:5 | LL | / loop { LL | | @@ -68,7 +77,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:167:9 + --> tests/ui/while_let_loop.rs:180:9 | LL | / loop { LL | | @@ -88,7 +97,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:182:5 + --> tests/ui/while_let_loop.rs:195:5 | LL | / loop { LL | | @@ -107,7 +116,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:194:5 + --> tests/ui/while_let_loop.rs:207:5 | LL | / loop { LL | | @@ -126,7 +135,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:206:5 + --> tests/ui/while_let_loop.rs:219:5 | LL | / loop { LL | | @@ -137,7 +146,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = Some(3) { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:218:5 + --> tests/ui/while_let_loop.rs:231:5 | LL | / loop { LL | | @@ -156,7 +165,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:230:5 + --> tests/ui/while_let_loop.rs:243:5 | LL | / loop { LL | | @@ -177,5 +186,5 @@ LL + .. LL + } | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 427774af0719cdf2214825cfa0a634e550839169 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Thu, 18 Sep 2025 07:46:17 -0700 Subject: [PATCH 0562/1900] Changelog: fix heading level in 1.90 section --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be02143722..fb7cbbed6542d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Current stable, released 2025-09-18 Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see [#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. -## New Lints +### New Lints * Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) * Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) From 2c7350269c38ae3284702597c9fd016453d445cf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 16:59:20 +0200 Subject: [PATCH 0563/1900] Bump nightly version -> 2025-09-18 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index e01f563c49e73..2c66fdc73f539 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-04 +nightly-2025-09-18 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ec2f24a0a6d84..9c102de448200 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-04" +channel = "nightly-2025-09-18" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 6b14443a028b9338ddf0c57962f2c6cea1170f5c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 16:59:44 +0200 Subject: [PATCH 0564/1900] Bump Clippy version -> 0.1.92 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b3618932ded7d..e06383499893a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 6ad2cf0d0b104..f8c748290e418 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 70184ee2ca596..51e59ae205076 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index bdf7431f29f29..d58b47bf6deb3 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index ec0e59e705495..4de7b5fb5924d 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" From 47bcee4ee26cc03aa19df2331559686d6a719bd1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 17:21:44 +0200 Subject: [PATCH 0565/1900] Merge commit '20ce69b9a63bcd2756cd906fe0964d1e901e042a' into clippy-subtree-update --- .github/ISSUE_TEMPLATE/new_lint.yml | 4 +- .github/PULL_REQUEST_TEMPLATE.md | 4 - .github/workflows/feature_freeze.yml | 45 -- .gitignore | 3 + CHANGELOG.md | 148 ++++- Cargo.toml | 2 +- book/src/README.md | 4 - book/src/SUMMARY.md | 1 - book/src/development/adding_lints.md | 3 - book/src/development/feature_freeze.md | 55 -- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/attrs/useless_attribute.rs | 1 + clippy_lints/src/casts/as_underscore.rs | 8 +- clippy_lints/src/casts/cast_possible_wrap.rs | 30 +- clippy_lints/src/casts/cast_sign_loss.rs | 29 +- clippy_lints/src/casts/mod.rs | 4 +- clippy_lints/src/casts/utils.rs | 15 + clippy_lints/src/declared_lints.rs | 5 +- clippy_lints/src/dereference.rs | 7 +- clippy_lints/src/derive.rs | 527 ------------------ .../src/derive/derive_ord_xor_partial_ord.rs | 50 ++ .../derive/derive_partial_eq_without_eq.rs | 87 +++ .../src/derive/derived_hash_with_manual_eq.rs | 49 ++ .../src/derive/expl_impl_clone_on_copy.rs | 65 +++ clippy_lints/src/derive/mod.rs | 215 +++++++ .../src/derive/unsafe_derive_deserialize.rs | 93 ++++ clippy_lints/src/disallowed_macros.rs | 4 +- clippy_lints/src/eta_reduction.rs | 27 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 4 +- clippy_lints/src/functions/ref_option.rs | 48 +- clippy_lints/src/future_not_send.rs | 109 ++-- .../src/invalid_upcast_comparisons.rs | 13 +- clippy_lints/src/len_zero.rs | 6 +- clippy_lints/src/let_with_type_underscore.rs | 16 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/lifetimes.rs | 109 +++- clippy_lints/src/loops/explicit_iter_loop.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 59 +- clippy_lints/src/manual_abs_diff.rs | 6 +- clippy_lints/src/manual_option_as_slice.rs | 6 +- .../src/manual_slice_size_calculation.rs | 3 +- clippy_lints/src/matches/manual_ok_err.rs | 16 +- clippy_lints/src/matches/manual_utils.rs | 9 +- clippy_lints/src/matches/needless_match.rs | 6 +- .../matches/rest_pat_in_fully_bound_struct.rs | 10 +- clippy_lints/src/matches/single_match.rs | 8 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/inefficient_to_string.rs | 4 +- .../src/methods/iter_overeager_cloned.rs | 5 +- clippy_lints/src/methods/mod.rs | 75 ++- clippy_lints/src/methods/mut_mutex_lock.rs | 5 +- clippy_lints/src/methods/or_fun_call.rs | 7 + .../src/methods/ptr_offset_with_cast.rs | 82 +++ .../src/methods/unnecessary_to_owned.rs | 13 +- clippy_lints/src/methods/useless_asref.rs | 6 +- clippy_lints/src/min_ident_chars.rs | 6 +- clippy_lints/src/misc.rs | 118 +--- clippy_lints/src/missing_doc.rs | 2 +- .../src/multiple_unsafe_ops_per_block.rs | 8 + clippy_lints/src/only_used_in_recursion.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 151 ----- clippy_lints/src/question_mark.rs | 21 +- clippy_lints/src/read_zero_byte_vec.rs | 27 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 8 +- clippy_lints/src/returns.rs | 513 ----------------- clippy_lints/src/returns/let_and_return.rs | 86 +++ clippy_lints/src/returns/mod.rs | 140 +++++ clippy_lints/src/returns/needless_return.rs | 269 +++++++++ .../needless_return_with_question_mark.rs | 60 ++ clippy_lints/src/semicolon_block.rs | 14 + clippy_lints/src/size_of_ref.rs | 5 +- clippy_lints/src/toplevel_ref_arg.rs | 119 ++++ .../src/transmute/transmute_ptr_to_ref.rs | 44 +- .../src/transmute/transmute_ref_to_ref.rs | 4 +- clippy_lints/src/uninit_vec.rs | 11 +- clippy_lints/src/unused_peekable.rs | 6 +- clippy_lints/src/unwrap.rs | 1 + clippy_lints/src/use_self.rs | 81 +-- clippy_lints/src/useless_conversion.rs | 14 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/check_proc_macro.rs | 146 ++++- clippy_utils/src/lib.rs | 29 +- clippy_utils/src/msrvs.rs | 4 +- clippy_utils/src/source.rs | 5 + clippy_utils/src/sym.rs | 1 + clippy_utils/src/ty/mod.rs | 88 ++- declare_clippy_lint/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/no-profile-in-cargo-toml.rs | 3 + .../ref_option/all/clippy.toml | 0 .../ref_option/private/clippy.toml | 0 tests/ui-toml/ref_option/ref_option.all.fixed | 114 ++++ .../ref_option/ref_option.all.stderr | 68 +-- .../ref_option/ref_option.private.fixed | 114 ++++ .../ref_option/ref_option.private.stderr | 44 +- tests/ui-toml/ref_option/ref_option.rs | 114 ++++ .../ref_option/ref_option_traits.all.stderr | 8 +- .../ref_option_traits.private.stderr | 4 +- tests/ui-toml/ref_option/ref_option_traits.rs | 71 +++ tests/ui/as_underscore_unfixable.rs | 14 + tests/ui/as_underscore_unfixable.stderr | 20 + tests/ui/cast.rs | 13 + tests/ui/cast.stderr | 96 ++-- .../ui/checked_unwrap/simple_conditionals.rs | 22 + .../checked_unwrap/simple_conditionals.stderr | 36 +- tests/ui/crashes/ice-15657.rs | 11 + tests/ui/crashes/ice-15666.fixed | 6 + tests/ui/crashes/ice-15666.rs | 6 + tests/ui/crashes/ice-15666.stderr | 16 + tests/ui/elidable_lifetime_names.fixed | 82 +++ tests/ui/elidable_lifetime_names.rs | 82 +++ tests/ui/elidable_lifetime_names.stderr | 146 ++++- tests/ui/eta.fixed | 8 + tests/ui/eta.rs | 8 + tests/ui/invalid_upcast_comparisons.rs | 12 + tests/ui/invalid_upcast_comparisons.stderr | 8 +- tests/ui/iter_overeager_cloned.fixed | 12 +- tests/ui/iter_overeager_cloned.rs | 12 +- tests/ui/iter_overeager_cloned.stderr | 4 +- tests/ui/match_as_ref.fixed | 30 + tests/ui/match_as_ref.rs | 30 + tests/ui/multiple_unsafe_ops_per_block.rs | 119 ++-- tests/ui/multiple_unsafe_ops_per_block.stderr | 143 +++-- tests/ui/needless_return.fixed | 7 + tests/ui/needless_return.rs | 7 + tests/ui/never_loop.rs | 24 + tests/ui/never_loop.stderr | 97 +++- tests/ui/option_if_let_else.fixed | 10 + tests/ui/option_if_let_else.rs | 16 + tests/ui/option_if_let_else.stderr | 50 +- tests/ui/or_fun_call.fixed | 16 + tests/ui/or_fun_call.rs | 22 +- tests/ui/or_fun_call.stderr | 88 +-- tests/ui/ptr_offset_with_cast.fixed | 22 +- tests/ui/ptr_offset_with_cast.rs | 22 +- tests/ui/ptr_offset_with_cast.stderr | 41 +- tests/ui/question_mark.fixed | 14 +- tests/ui/question_mark.rs | 26 +- tests/ui/question_mark.stderr | 92 +-- tests/ui/read_zero_byte_vec.rs | 27 + tests/ui/read_zero_byte_vec.stderr | 65 ++- tests/ui/ref_option/ref_option.all.fixed | 79 --- tests/ui/ref_option/ref_option.private.fixed | 79 --- tests/ui/ref_option/ref_option.rs | 79 --- tests/ui/ref_option/ref_option_traits.rs | 40 -- .../ui/rest_pat_in_fully_bound_structs.fixed | 61 ++ .../ui/rest_pat_in_fully_bound_structs.stderr | 18 +- tests/ui/semicolon_inside_block.fixed | 17 +- tests/ui/semicolon_inside_block.rs | 17 +- tests/ui/semicolon_inside_block.stderr | 8 +- tests/ui/track-diagnostics-clippy.stderr | 2 +- tests/ui/transmute_ptr_to_ref.fixed | 39 +- tests/ui/transmute_ptr_to_ref.rs | 39 +- tests/ui/transmute_ptr_to_ref.stderr | 54 +- tests/ui/use_self.fixed | 18 +- tests/ui/use_self.rs | 16 +- tests/ui/use_self.stderr | 8 +- tests/ui/useless_attribute.fixed | 10 + tests/ui/useless_attribute.rs | 10 + 163 files changed, 4375 insertions(+), 2474 deletions(-) delete mode 100644 .github/workflows/feature_freeze.yml delete mode 100644 book/src/development/feature_freeze.md delete mode 100644 clippy_lints/src/derive.rs create mode 100644 clippy_lints/src/derive/derive_ord_xor_partial_ord.rs create mode 100644 clippy_lints/src/derive/derive_partial_eq_without_eq.rs create mode 100644 clippy_lints/src/derive/derived_hash_with_manual_eq.rs create mode 100644 clippy_lints/src/derive/expl_impl_clone_on_copy.rs create mode 100644 clippy_lints/src/derive/mod.rs create mode 100644 clippy_lints/src/derive/unsafe_derive_deserialize.rs create mode 100644 clippy_lints/src/methods/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/returns.rs create mode 100644 clippy_lints/src/returns/let_and_return.rs create mode 100644 clippy_lints/src/returns/mod.rs create mode 100644 clippy_lints/src/returns/needless_return.rs create mode 100644 clippy_lints/src/returns/needless_return_with_question_mark.rs create mode 100644 clippy_lints/src/toplevel_ref_arg.rs rename tests/{ui => ui-toml}/ref_option/all/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/private/clippy.toml (100%) create mode 100644 tests/ui-toml/ref_option/ref_option.all.fixed rename tests/{ui => ui-toml}/ref_option/ref_option.all.stderr (62%) create mode 100644 tests/ui-toml/ref_option/ref_option.private.fixed rename tests/{ui => ui-toml}/ref_option/ref_option.private.stderr (63%) create mode 100644 tests/ui-toml/ref_option/ref_option.rs rename tests/{ui => ui-toml}/ref_option/ref_option_traits.all.stderr (85%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.private.stderr (86%) create mode 100644 tests/ui-toml/ref_option/ref_option_traits.rs create mode 100644 tests/ui/as_underscore_unfixable.rs create mode 100644 tests/ui/as_underscore_unfixable.stderr create mode 100644 tests/ui/crashes/ice-15657.rs create mode 100644 tests/ui/crashes/ice-15666.fixed create mode 100644 tests/ui/crashes/ice-15666.rs create mode 100644 tests/ui/crashes/ice-15666.stderr delete mode 100644 tests/ui/ref_option/ref_option.all.fixed delete mode 100644 tests/ui/ref_option/ref_option.private.fixed delete mode 100644 tests/ui/ref_option/ref_option.rs delete mode 100644 tests/ui/ref_option/ref_option_traits.rs create mode 100644 tests/ui/rest_pat_in_fully_bound_structs.fixed diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index a8202f6378fd3..6ad16aead601d 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -1,7 +1,5 @@ name: New lint suggestion -description: | - Suggest a new Clippy lint (currently not accepting new lints) - Check out the Clippy book for more information about the feature freeze. +description: Suggest a new Clippy lint. labels: ["A-lint"] body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 83bfd8e9c6865..9e49f60892d26 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,10 +32,6 @@ order to get feedback. Delete this line and everything above before opening your PR. -Note that we are currently not taking in new PRs that add new lints. We are in a -feature freeze. Check out the book for more information. If you open a -feature-adding pull request, its review will be delayed. - --- *Please write a short comment explaining your change (or "none" for internal only changes)* diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml deleted file mode 100644 index 5b139e767007b..0000000000000 --- a/.github/workflows/feature_freeze.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Feature freeze check - -on: - pull_request_target: - types: - - opened - branches: - - master - paths: - - 'clippy_lints/src/declared_lints.rs' - -jobs: - auto-comment: - runs-on: ubuntu-latest - - permissions: - pull-requests: write - - # Do not in any case add code that runs anything coming from the content - # of the pull request, as malicious code would be able to access the private - # GitHub token. - steps: - - name: Add freeze warning comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(echo "**Seems that you are trying to add a new lint!**\n\ - \n\ - We are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and [focusing on bugfixes](https://github.com/rust-lang/rust-clippy/issues/15086).\n\ - \n\ - Thanks a lot for your contribution, and sorry for the inconvenience.\n\ - \n\ - With ❤ from the Clippy team.\n\ - \n\ - @rustbot note Feature-freeze\n\ - @rustbot blocked\n\ - @rustbot label +A-lint" - ) - curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "Content-Type: application/vnd.github.raw+json" \ - -X POST \ - --data "{\"body\":\"${COMMENT}\"}" \ - "https://api.github.com/repos/$%7BGITHUB_REPOSITORY%7D/issues/$%7BPR_NUMBER%7D/comments" diff --git a/.gitignore b/.gitignore index 36a4cdc1c3528..666c4ceac4dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ helper.txt # mdbook generated output /book/book + +# Remove jujutsu directory from search tools +.jj diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2a76a818369..3f26b9470e826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,152 @@ document. ## Unreleased / Beta / In Rust Nightly -[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) +[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) + +## Rust 1.90 + +Current stable, released 2025-09-18 + +[View all 118 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-06-13T15%3A55%3A04Z..2025-07-25T13%3A24%3A00Z+base%3Amaster) + +Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see +[#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. + +## New Lints + +* Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) +* Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) + +### Moves and Deprecations + +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) + +### Enhancements + +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) + [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) +* [`incompatible_msrv`] now recognizes types exceeding MSRV + [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context + [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) +* [`zero_ptr`] now lints in `const` context as well + [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) +* [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function + [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag + [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) + +### False Positive Fixes + +* [`if_then_some_else_none`] fixed FP when require type coercion + [#15267](https://github.com/rust-lang/rust-clippy/pull/15267) +* [`module_name_repetitions`] fixed FP on exported macros + [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) +* [`unused_async`] fixed FP on function with `todo!` + [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes + on `use` statements + [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) +* [`pattern_type_mismatch`] fixed FP in external macro + [#15306](https://github.com/rust-lang/rust-clippy/pull/15306) +* [`large_enum_variant`] fixed FP by not suggesting `Box` in `no_std` mode + [#15241](https://github.com/rust-lang/rust-clippy/pull/15241) +* [`missing_inline_in_public_items`] fixed FP on functions with `extern` + [#15313](https://github.com/rust-lang/rust-clippy/pull/15313) +* [`needless_range_loop`] fixed FP on array literals + [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) +* [`ptr_as_ptr`] fixed wrong suggestions with turbo fish + [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe + to switch the range type + [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) +* [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument + [#15105](https://github.com/rust-lang/rust-clippy/pull/15105) +* [`unsafe_derive_deserialize`] fixed FP caused by the standard library's `pin!()` macro + [#15137](https://github.com/rust-lang/rust-clippy/pull/15137) +* [`unused_trait_names`] fixed FP in macros + [#14947](https://github.com/rust-lang/rust-clippy/pull/14947) +* [`expect_fun_call`] fixed invalid suggestions + [#15122](https://github.com/rust-lang/rust-clippy/pull/15122) +* [`manual_is_multiple_of`] fixed various false positives + [#15205](https://github.com/rust-lang/rust-clippy/pull/15205) +* [`manual_assert`] fixed wrong suggestions for macros + [#15264](https://github.com/rust-lang/rust-clippy/pull/15264) +* [`expect_used`] fixed false negative when meeting `Option::ok().expect` + [#15253](https://github.com/rust-lang/rust-clippy/pull/15253) +* [`manual_abs_diff`] fixed wrong suggestions behind refs + [#15265](https://github.com/rust-lang/rust-clippy/pull/15265) +* [`approx_constant`] fixed FP with overly precise literals and non trivially formatted numerals + [#15236](https://github.com/rust-lang/rust-clippy/pull/15236) +* [`arithmetic_side_effects`] fixed FP on `NonZeroU*.get() - 1` + [#15238](https://github.com/rust-lang/rust-clippy/pull/15238) +* [`legacy_numeric_constants`] fixed suggestion when call is inside parentheses + [#15191](https://github.com/rust-lang/rust-clippy/pull/15191) +* [`manual_is_variant_and`] fixed inverted suggestions that could lead to code with different semantics + [#15206](https://github.com/rust-lang/rust-clippy/pull/15206) +* [`unnecessary_map_or`] fixed FP by not proposing to replace `map_or` call when types wouldn't be correct + [#15181](https://github.com/rust-lang/rust-clippy/pull/15181) +* [`return_and_then`] fixed FP in case of a partially used expression + [#15115](https://github.com/rust-lang/rust-clippy/pull/15115) +* [`manual_let_else`] fixed FP with binding subpattern with unused name + [#15118](https://github.com/rust-lang/rust-clippy/pull/15118) +* [`suboptimal_flops`] fixed FP with ambiguous float types in mul_add suggestions + [#15133](https://github.com/rust-lang/rust-clippy/pull/15133) +* [`borrow_as_ptr`] fixed FP by not removing cast to trait object pointer + [#15145](https://github.com/rust-lang/rust-clippy/pull/15145) +* [`unnecessary_operation`] fixed FP by not removing casts if they are useful to type the expression + [#15182](https://github.com/rust-lang/rust-clippy/pull/15182) +* [`empty_loop`] fixed FP on intrinsic function declaration + [#15201](https://github.com/rust-lang/rust-clippy/pull/15201) +* [`doc_nested_refdefs`] fixed FP where task lists are reported as refdefs + [#15146](https://github.com/rust-lang/rust-clippy/pull/15146) +* [`std_instead_of_core`] fixed FP when not all items come from the new crate + [#15165](https://github.com/rust-lang/rust-clippy/pull/15165) +* [`swap_with_temporary`] fixed FP leading to different semantics being suggested + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) +* [`cast_possible_truncation`] fixed FP by not giving suggestions inside const context + [#15164](https://github.com/rust-lang/rust-clippy/pull/15164) +* [`missing_panics_doc`] fixed FP by allowing unwrap() and expect()s in const-only contexts + [#15170](https://github.com/rust-lang/rust-clippy/pull/15170) +* [`disallowed_script_idents`] fixed FP on identifiers with `_` + [#15123](https://github.com/rust-lang/rust-clippy/pull/15123) +* [`wildcard_enum_match_arm`] fixed wrong suggestions with raw identifiers + [#15093](https://github.com/rust-lang/rust-clippy/pull/15093) +* [`borrow_deref_ref`] fixed FP by not proposing replacing a reborrow when the reborrow is itself mutably borrowed + [#14967](https://github.com/rust-lang/rust-clippy/pull/14967) +* [`manual_ok_err`] fixed wrong suggestions with references + [#15053](https://github.com/rust-lang/rust-clippy/pull/15053) +* [`identity_op`] fixed FP when encountering `Default::default()` + [#15028](https://github.com/rust-lang/rust-clippy/pull/15028) +* [`exhaustive_structs`] fixed FP on structs with default valued field + [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) +* [`collapsible_else_if`] fixed FP on conditionally compiled stmt + [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing + non-`Sized` trait bounds + [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) +* [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` + [#15082](https://github.com/rust-lang/rust-clippy/pull/15082) + +### ICE Fixes + +* [`single_match`] fixed ICE with deref patterns and string literals + [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) +* [`needless_doctest_main`] fixed panic when doctest is invalid + [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) +* [`zero_sized_map_values`] do not attempt to compute size of a type with escaping lifetimes + [#15434](https://github.com/rust-lang/rust-clippy/pull/15434) + +### Documentation Improvements + +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns + [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) +* [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter + [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) +* [`undocumented_unsafe_blocks`] improved documentation wording + [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -6408,6 +6553,7 @@ Released 2018-09-13 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards +[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/Cargo.toml b/Cargo.toml index b3618932ded7d..e06383499893a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/book/src/README.md b/book/src/README.md index db73b49ecc24e..5d2c3972b060a 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,9 +1,5 @@ # Clippy -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md) - ----- - [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index b66c3481e4930..39fe7358ed87a 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,7 +13,6 @@ - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index a42a298374465..2b89e94cf8f4f 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -1,8 +1,5 @@ # Adding a new lint -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md) - - You are probably here because you want to add a new lint to Clippy. If this is the first time you're contributing to Clippy, this document guides you through creating an example lint from scratch. diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md deleted file mode 100644 index 260cb136cc075..0000000000000 --- a/book/src/development/feature_freeze.md +++ /dev/null @@ -1,55 +0,0 @@ -# IMPORTANT: FEATURE FREEZE - -This is a temporary notice. - -From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed -except already open ones. Every feature-adding PR opened in between those dates will be moved into a -milestone to be reviewed separately at another time. - -We do this because of the long backlog of bugs that need to be addressed -in order to continue being the state-of-the-art linter that Clippy has become known for being. - -## For contributors - -If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open -bugs of all levels of difficulty that you can address instead! - -We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible -use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a -refinement period. - -If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends, -adding additional load into our reviewing schedules. - -## I want to help, what can I do - -Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period! -If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step! - -To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that -issue with `@rustbot claim`. - -As a general metric and always taking into account your skill and knowledge level, you can use this guide: - -- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level -debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that -improves a lot developer workflows! - -- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. -Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs - -- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error -when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar -easy-to-happen occurrences. - -- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" -identifying the root of a false positive and making an exception for those cases. - -Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a -trench coat. - -[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 -[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug -[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 -[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive -[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 6ad2cf0d0b104..f8c748290e418 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 70184ee2ca596..51e59ae205076 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index b9b5cedb5aa76..1cebc18edc90a 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -30,6 +30,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { sym::ambiguous_glob_reexports | sym::dead_code | sym::deprecated + | sym::deprecated_in_future | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs index 3ac486dd63fbb..a73e48e5fd5d1 100644 --- a/clippy_lints/src/casts/as_underscore.rs +++ b/clippy_lints/src/casts/as_underscore.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Expr, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::IsSuggestable; use super::AS_UNDERSCORE; @@ -10,15 +10,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tc if matches!(ty.kind, TyKind::Infer(())) { span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| { let ty_resolved = cx.typeck_results().expr_ty(expr); - if let ty::Error(_) = ty_resolved.kind() { - diag.help("consider giving the type explicitly"); - } else { + if ty_resolved.is_suggestable(cx.tcx, true) { diag.span_suggestion( ty.span, "consider giving the type explicitly", ty_resolved, Applicability::MachineApplicable, ); + } else { + diag.help("consider giving the type explicitly"); } }); } diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda933..9eaa6e4cf26e4 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,4 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -16,7 +19,14 @@ enum EmitState { LintOnPtrSize(u64), } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_op: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: Msrv, +) { let (Some(from_nbits), Some(to_nbits)) = ( utils::int_ty_to_nbits(cx.tcx, cast_from), utils::int_ty_to_nbits(cx.tcx, cast_to), @@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); } + + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, use `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a70bd88619195..f870d27b796e8 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -2,15 +2,18 @@ use std::convert::Infallible; use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext, sym}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::Symbol; -use super::CAST_SIGN_LOSS; +use super::{CAST_SIGN_LOSS, utils}; /// A list of methods that can never return a negative value. /// Includes methods that panic rather than returning a negative value. @@ -42,13 +45,33 @@ pub(super) fn check<'cx>( cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>, + msrv: Msrv, ) { if should_lint(cx, cast_op, cast_from, cast_to) { - span_lint( + span_lint_and_then( cx, CAST_SIGN_LOSS, expr.span, format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + |diag| { + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, use `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } + }, ); } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d2e62ee56e43b..47cc1da0a6e9f 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -890,9 +890,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { - cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index d846d78b9ee78..707fc2a8eed35 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -60,3 +60,18 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 { neg_bits.max(pos_bits).into() } } + +pub(super) enum CastTo { + Signed, + Unsigned, +} +/// Returns `Some` if the type cast is between 2 integral types that differ +/// only in signedness, otherwise `None`. The value of `Some` is which +/// signedness is casted to. +pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { + match (cast_from.kind(), cast_to.kind()) { + (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), + (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), + _ => None, + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b9..2a4bedc184552 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -448,10 +448,12 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::PATH_ENDS_WITH_EXT_INFO, + crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REDUNDANT_AS_STR_INFO, + crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, @@ -503,7 +505,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::min_ident_chars::MIN_IDENT_CHARS_INFO, crate::minmax::MIN_MAX_INFO, crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, - crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, @@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, - crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, @@ -706,6 +706,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, + crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9645a26a68ae1..a70105db19490 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, - peel_middle_ty_refs, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -942,7 +941,7 @@ fn report<'tcx>( let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_middle_ty_refs(ty); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. @@ -1045,7 +1044,7 @@ fn report<'tcx>( if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() && dst.is_slice() { - let (src, n_src_refs) = peel_middle_ty_refs(ty); + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); if n_src_refs >= 2 && src.is_array() { return; } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs deleted file mode 100644 index c53a957f6a8b7..0000000000000 --- a/clippy_lints/src/derive.rs +++ /dev/null @@ -1,527 +0,0 @@ -use std::ops::ControlFlow; - -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, -}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; - -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialEq` implementations for types with a derived `Hash` - /// implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `HashMap`) so it’s probably a bad idea to use a - /// default-generated `Hash` implementation with an explicitly defined - /// `PartialEq`. In particular, the following must hold for any type: - /// - /// ```text - /// k1 == k2 ⇒ hash(k1) == hash(k2) - /// ``` - /// - /// ### Example - /// ```ignore - /// #[derive(Hash)] - /// struct Foo; - /// - /// impl PartialEq for Foo { - /// ... - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DERIVED_HASH_WITH_MANUAL_EQ, - correctness, - "deriving `Hash` but implementing `PartialEq` explicitly" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` - /// or `PartialOrd` implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `sort`) so it’s probably a bad idea to use a - /// default-generated `Ord` implementation with an explicitly defined - /// `PartialOrd`. In particular, the following must hold for any type - /// implementing `Ord`: - /// - /// ```text - /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() - /// ``` - /// - /// ### Example - /// ```rust,ignore - /// #[derive(Ord, PartialEq, Eq)] - /// struct Foo; - /// - /// impl PartialOrd for Foo { - /// ... - /// } - /// ``` - /// Use instead: - /// ```rust,ignore - /// #[derive(PartialEq, Eq)] - /// struct Foo; - /// - /// impl PartialOrd for Foo { - /// fn partial_cmp(&self, other: &Foo) -> Option { - /// Some(self.cmp(other)) - /// } - /// } - /// - /// impl Ord for Foo { - /// ... - /// } - /// ``` - /// or, if you don't need a custom ordering: - /// ```rust,ignore - /// #[derive(Ord, PartialOrd, PartialEq, Eq)] - /// struct Foo; - /// ``` - #[clippy::version = "1.47.0"] - pub DERIVE_ORD_XOR_PARTIAL_ORD, - correctness, - "deriving `Ord` but implementing `PartialOrd` explicitly" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for explicit `Clone` implementations for `Copy` - /// types. - /// - /// ### Why is this bad? - /// To avoid surprising behavior, these traits should - /// agree and the behavior of `Copy` cannot be overridden. In almost all - /// situations a `Copy` type should have a `Clone` implementation that does - /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` - /// gets you. - /// - /// ### Example - /// ```rust,ignore - /// #[derive(Copy)] - /// struct Foo; - /// - /// impl Clone for Foo { - /// // .. - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub EXPL_IMPL_CLONE_ON_COPY, - pedantic, - "implementing `Clone` explicitly on `Copy` types" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for deriving `serde::Deserialize` on a type that - /// has methods using `unsafe`. - /// - /// ### Why is this bad? - /// Deriving `serde::Deserialize` will create a constructor - /// that may violate invariants held by another constructor. - /// - /// ### Example - /// ```rust,ignore - /// use serde::Deserialize; - /// - /// #[derive(Deserialize)] - /// pub struct Foo { - /// // .. - /// } - /// - /// impl Foo { - /// pub fn new() -> Self { - /// // setup here .. - /// } - /// - /// pub unsafe fn parts() -> (&str, &str) { - /// // assumes invariants hold - /// } - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub UNSAFE_DERIVE_DESERIALIZE, - pedantic, - "deriving `serde::Deserialize` on a type that has methods using `unsafe`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for types that derive `PartialEq` and could implement `Eq`. - /// - /// ### Why is this bad? - /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, - /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used - /// in APIs that require `Eq` types. It also allows structs containing `T` to derive - /// `Eq` themselves. - /// - /// ### Example - /// ```no_run - /// #[derive(PartialEq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[derive(PartialEq, Eq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - #[clippy::version = "1.63.0"] - pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - nursery, - "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" -} - -declare_lint_pass!(Derive => [ - EXPL_IMPL_CLONE_ON_COPY, - DERIVED_HASH_WITH_MANUAL_EQ, - DERIVE_ORD_XOR_PARTIAL_ORD, - UNSAFE_DERIVE_DESERIALIZE, - DERIVE_PARTIAL_EQ_WITHOUT_EQ -]); - -impl<'tcx> LateLintPass<'tcx> for Derive { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - of_trait: Some(of_trait), - .. - }) = item.kind - { - let trait_ref = &of_trait.trait_ref; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - - check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); - - if is_automatically_derived { - check_unsafe_derive_deserialize(cx, item, trait_ref, ty); - check_partial_eq_without_eq(cx, item.span, trait_ref, ty); - } else { - check_copy_clone(cx, item, trait_ref, ty); - } - } - } -} - -/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. -fn check_hash_peq<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - hash_is_automatically_derived: bool, -) { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Hash, def_id) - { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); - } - }, - ); - } - }); - } -} - -/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_partial_ord<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - ord_is_automatically_derived: bool, -) { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() - && let Some(def_id) = &trait_ref.trait_def_id() - && *def_id == ord_trait_def_id - { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; - - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); - } - }); - } - }); - } -} - -/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - let clone_id = match cx.tcx.lang_items().clone_trait() { - Some(id) if trait_ref.trait_def_id() == Some(id) => id, - _ => return, - }; - let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { - return; - }; - let (ty_adt, ty_subs) = match *ty.kind() { - // Unions can't derive clone. - ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), - _ => return, - }; - // If the current self type doesn't implement Copy (due to generic constraints), search to see if - // there's a Copy impl for any instance of the adt. - if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) - if ty_adt.did() == adt.did()) - }); - if !has_copy_impl { - return; - } - } else { - return; - } - } - // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for - // this impl. - if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { - return; - } - // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. - // https://github.com/rust-lang/rust-clippy/issues/10188 - if ty_adt.repr().packed() - && ty_subs - .iter() - .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) - { - return; - } - // The presence of `unsafe` fields prevents deriving `Clone` automatically - if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { - return; - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); -} - -/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -fn check_unsafe_derive_deserialize<'tcx>( - cx: &LateContext<'tcx>, - item: &Item<'_>, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { - fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx }; - walk_item(&mut visitor, item).is_break() - } - - if let Some(trait_def_id) = trait_ref.trait_def_id() - && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) - && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) - && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) - && cx - .tcx - .inherent_impls(def.did()) - .iter() - .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)) - { - span_lint_hir_and_then( - cx, - UNSAFE_DERIVE_DESERIALIZE, - adt_hir_id, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - |diag| { - diag.help( - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", - ); - }, - ); - } -} - -struct UnsafeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = nested_filter::All; - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - _: Span, - id: LocalDefId, - ) -> Self::Result { - if let Some(header) = kind.header() - && header.is_unsafe() - { - ControlFlow::Break(()) - } else { - walk_fn(self, kind, decl, body_id, id) - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind - && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - && block - .span - .source_callee() - .and_then(|expr| expr.macro_def_id) - .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) - { - return ControlFlow::Break(()); - } - - walk_expr(self, expr) - } - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - -/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if let ty::Adt(adt, args) = ty.kind() - && cx.tcx.visibility(adt.did()).is_public() - && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) - && !has_non_exhaustive_attr(cx.tcx, *adt) - && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() - // If all of our fields implement `Eq`, we can implement `Eq` too - && adt - .all_fields() - .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) - { - span_lint_hir_and_then( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - |diag| { - diag.span_suggestion( - span.ctxt().outer_expn_data().call_site, - "consider deriving `Eq` as well", - "PartialEq, Eq", - Applicability::MachineApplicable, - ); - }, - ); - } -} - -fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { - tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() -} - -/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. -fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { - // Initial map from generic index to param def. - // Vec<(param_def, needs_eq)> - let mut params = tcx - .generics_of(did) - .own_params - .iter() - .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) - .collect::>(); - - let ty_predicates = tcx.predicates_of(did).predicates; - for (p, _) in ty_predicates { - if let ClauseKind::Trait(p) = p.kind().skip_binder() - && p.trait_ref.def_id == eq_trait_id - && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() - { - // Flag types which already have an `Eq` bound. - params[self_ty.index as usize].1 = false; - } - } - - let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( - params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - ClauseKind::Trait(TraitPredicate { - trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), - polarity: ty::PredicatePolarity::Positive, - }) - .upcast(tcx) - }), - ))); - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env, - } -} diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs new file mode 100644 index 0000000000000..cbbcb2f7a3bab --- /dev/null +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVE_ORD_XOR_PARTIAL_ORD; + +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); + } +} diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs new file mode 100644 index 0000000000000..ed7881c461ffc --- /dev/null +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::has_non_exhaustive_attr; +use clippy_utils::ty::implements_trait_with_env; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; +use rustc_span::{Span, sym}; + +use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) + && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) + && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let Some(local_def_id) = adt.did().as_local() + // If all of our fields implement `Eq`, we can implement `Eq` too + && adt + .all_fields() + .map(|f| f.ty(cx.tcx, args)) + .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) + { + span_lint_hir_and_then( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + cx.tcx.local_def_id_to_hir_id(local_def_id), + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + |diag| { + diag.span_suggestion( + span.ctxt().outer_expn_data().call_site, + "consider deriving `Eq` as well", + "PartialEq, Eq", + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { + tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() +} + +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. +fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .own_params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let ClauseKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + ClauseKind::Trait(TraitPredicate { + trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), + polarity: ty::PredicatePolarity::Positive, + }) + .upcast(tcx) + }), + ))); + ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env, + } +} diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs new file mode 100644 index 0000000000000..6f36a58025a2b --- /dev/null +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVED_HASH_WITH_MANUAL_EQ; + +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + hash_is_automatically_derived: bool, +) { + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); + } + }, + ); + } + }); + } +} diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs new file mode 100644 index 0000000000000..6b97b4bd6b4d3 --- /dev/null +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::{self as hir, Item}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Ty}; + +use super::EXPL_IMPL_CLONE_ON_COPY; + +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { + return; + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + if ty_adt.did() == adt.did()) + }); + if !has_copy_impl { + return; + } + } else { + return; + } + } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); +} diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs new file mode 100644 index 0000000000000..1d63394ce37d4 --- /dev/null +++ b/clippy_lints/src/derive/mod.rs @@ -0,0 +1,215 @@ +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +mod derive_ord_xor_partial_ord; +mod derive_partial_eq_without_eq; +mod derived_hash_with_manual_eq; +mod expl_impl_clone_on_copy; +mod unsafe_derive_deserialize; + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialEq` implementations for types with a derived `Hash` + /// implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `HashMap`) so it’s probably a bad idea to use a + /// default-generated `Hash` implementation with an explicitly defined + /// `PartialEq`. In particular, the following must hold for any type: + /// + /// ```text + /// k1 == k2 ⇒ hash(k1) == hash(k2) + /// ``` + /// + /// ### Example + /// ```ignore + /// #[derive(Hash)] + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// ... + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DERIVED_HASH_WITH_MANUAL_EQ, + correctness, + "deriving `Hash` but implementing `PartialEq` explicitly" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` + /// or `PartialOrd` implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `sort`) so it’s probably a bad idea to use a + /// default-generated `Ord` implementation with an explicitly defined + /// `PartialOrd`. In particular, the following must hold for any type + /// implementing `Ord`: + /// + /// ```text + /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() + /// ``` + /// + /// ### Example + /// ```rust,ignore + /// #[derive(Ord, PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// ... + /// } + /// ``` + /// Use instead: + /// ```rust,ignore + /// #[derive(PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// fn partial_cmp(&self, other: &Foo) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for Foo { + /// ... + /// } + /// ``` + /// or, if you don't need a custom ordering: + /// ```rust,ignore + /// #[derive(Ord, PartialOrd, PartialEq, Eq)] + /// struct Foo; + /// ``` + #[clippy::version = "1.47.0"] + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "deriving `Ord` but implementing `PartialOrd` explicitly" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for explicit `Clone` implementations for `Copy` + /// types. + /// + /// ### Why is this bad? + /// To avoid surprising behavior, these traits should + /// agree and the behavior of `Copy` cannot be overridden. In almost all + /// situations a `Copy` type should have a `Clone` implementation that does + /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` + /// gets you. + /// + /// ### Example + /// ```rust,ignore + /// #[derive(Copy)] + /// struct Foo; + /// + /// impl Clone for Foo { + /// // .. + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub EXPL_IMPL_CLONE_ON_COPY, + pedantic, + "implementing `Clone` explicitly on `Copy` types" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for deriving `serde::Deserialize` on a type that + /// has methods using `unsafe`. + /// + /// ### Why is this bad? + /// Deriving `serde::Deserialize` will create a constructor + /// that may violate invariants held by another constructor. + /// + /// ### Example + /// ```rust,ignore + /// use serde::Deserialize; + /// + /// #[derive(Deserialize)] + /// pub struct Foo { + /// // .. + /// } + /// + /// impl Foo { + /// pub fn new() -> Self { + /// // setup here .. + /// } + /// + /// pub unsafe fn parts() -> (&str, &str) { + /// // assumes invariants hold + /// } + /// } + /// ``` + #[clippy::version = "1.45.0"] + pub UNSAFE_DERIVE_DESERIALIZE, + pedantic, + "deriving `serde::Deserialize` on a type that has methods using `unsafe`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```no_run + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + nursery, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVED_HASH_WITH_MANUAL_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE, + DERIVE_PARTIAL_EQ_WITHOUT_EQ +]); + +impl<'tcx> LateLintPass<'tcx> for Derive { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl(Impl { + of_trait: Some(of_trait), + .. + }) = item.kind + { + let trait_ref = &of_trait.trait_ref; + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); + + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + + if is_automatically_derived { + unsafe_derive_deserialize::check(cx, item, trait_ref, ty); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + } else { + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + } + } + } +} diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs new file mode 100644 index 0000000000000..c391e7b62289f --- /dev/null +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -0,0 +1,93 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::{is_lint_allowed, paths}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, sym}; + +use super::UNSAFE_DERIVE_DESERIALIZE; + +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() + } + + if let Some(trait_def_id) = trait_ref.trait_def_id() + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) + .iter() + .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_hir_and_then( + cx, + UNSAFE_DERIVE_DESERIALIZE, + adt_hir_id, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + |diag| { + diag.help( + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); + }, + ); + } +} + +struct UnsafeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, +} + +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + type NestedFilter = nested_filter::All; + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { + if let Some(header) = kind.header() + && header.is_unsafe() + { + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && block + .span + .source_callee() + .and_then(|expr| expr.macro_def_id) + .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) + { + return ControlFlow::Break(()); + } + + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 49cd2671dc003..1c9c971730f6e 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,4 +1,3 @@ - use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; @@ -8,7 +7,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, + TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2da1c2bad1174..752f39b4e6dc1 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -197,6 +197,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // in a type which is `'static`. // For now ignore all callee types which reference a type parameter. && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + // Rule out `AsyncFn*`s, because while they can be called as `|x| f(x)`, + // they can't be passed directly into a place expecting an `Fn*` (#13892) + && let Ok((closure_kind, _)) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { span_lint_hir_and_then( cx, @@ -213,19 +225,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match closure_kind { // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + ClosureKind::FnMut => snippet = format!("&mut {snippet}"), + ClosureKind::Fn if !callee_ty_raw.is_ref() => { snippet = format!("&{snippet}"); }, _ => (), diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 906bbd006d463..72f8795304055 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -4,7 +4,7 @@ use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; @@ -51,7 +51,7 @@ fn check_raw_ptr<'tcx>( let typeck = cx.tcx.typeck_body(body.id()); let _: Option = for_each_expr(cx, body.value, |e| { match e.kind { - hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { check_arg(cx, &raw_ptrs, arg); } diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 106202d00d403..5dc1b7269b761 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -1,23 +1,20 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_from_proc_macro, is_trait_impl_item}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{FnDecl, HirId}; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_hir::{self as hir, FnDecl, HirId}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::{self, Mutability, Ty}; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; -fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { - if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() - && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen_args) = opt_ty.kind() - && let [gen_arg] = opt_gen_args.as_slice() - && let GenericArgKind::Type(gen_ty) = gen_arg.kind() +fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { + if !param.span.in_external_macro(cx.sess().source_map()) + && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind @@ -27,6 +24,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a args: [hir::GenericArg::Type(opt_ty)], .. }) = last.args + && !is_from_proc_macro(cx, param) { let lifetime = snippet(cx, lifetime.ident.span, ".."); fixes.push(( @@ -67,21 +65,24 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty #[allow(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, - kind: FnKind<'_>, + kind: FnKind<'a>, decl: &FnDecl<'a>, span: Span, hir_id: HirId, def_id: LocalDefId, - body: &hir::Body<'_>, + body: &hir::Body<'a>, avoid_breaking_exported_api: bool, ) { if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } + if span.in_external_macro(cx.sess().source_map()) { + return; + } if let FnKind::Closure = kind { // Compute the span of the closure parameters + return type if set - let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output { if decl.inputs.is_empty() { out_ty.span } else { @@ -100,9 +101,18 @@ pub(crate) fn check_fn<'a>( }; let sig = args.as_closure().sig().skip_binder(); - check_fn_sig(cx, decl, span, sig); + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + + check_fn_sig(cx, decl, inputs_output_span, sig); } else if !is_trait_impl_item(cx, hir_id) { let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + check_fn_sig(cx, decl, span, sig); } } @@ -112,8 +122,10 @@ pub(super) fn check_trait_item<'a>( trait_item: &hir::TraitItem<'a>, avoid_breaking_exported_api: bool, ) { - if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + if !trait_item.span.in_external_macro(cx.sess().source_map()) + && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + && !is_from_proc_macro(cx, trait_item) { let def_id = trait_item.owner_id.def_id; let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 3ccfa51ab70b7..596047977a9b1 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -78,66 +78,65 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) + && let preds = cx.tcx.explicit_item_self_bounds(def_id) + // If is a Future + && preds + .iter_instantiated_copied(cx.tcx, args) + .filter_map(|(p, _)| p.as_trait_clause()) + .any(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) { - let preds = cx.tcx.explicit_item_self_bounds(def_id); - let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { - p.as_trait_clause() - .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) - }); - if is_future { - let span = decl.output.span(); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, fn_def_id); - ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); + let span = decl.output.span(); + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, fn_def_id); + ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); + let send_errors = ocx.select_all_or_error(); - // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top - // level". - // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, - // which is always unconditionally `!Send` for any possible type `T`. - // - // We also allow associated type projections if the self type is either itself a projection or a - // type parameter. - // This is to prevent emitting warnings for e.g. holding a `::Output` across await - // points, where `Fut` is a type parameter. + // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top + // level". + // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, + // which is always unconditionally `!Send` for any possible type `T`. + // + // We also allow associated type projections if the self type is either itself a projection or a + // type parameter. + // This is to prevent emitting warnings for e.g. holding a `::Output` across await + // points, where `Fut` is a type parameter. - let is_send = send_errors.iter().all(|err| { - err.obligation - .predicate - .as_trait_clause() - .map(Binder::skip_binder) - .is_some_and(|pred| { - pred.def_id() == send_trait - && pred.self_ty().has_param() - && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) - }) - }); + let is_send = send_errors.iter().all(|err| { + err.obligation + .predicate + .as_trait_clause() + .map(Binder::skip_binder) + .is_some_and(|pred| { + pred.def_id() == send_trait + && pred.self_ty().has_param() + && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) + }) + }); - if !is_send { - span_lint_and_then( - cx, - FUTURE_NOT_SEND, - span, - "future cannot be sent between threads safely", - |db| { - for FulfillmentError { obligation, .. } in send_errors { - infcx - .err_ctxt() - .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = - obligation.predicate.kind().skip_binder() - { - db.note(format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + if !is_send { + span_lint_and_then( + cx, + FUTURE_NOT_SEND, + span, + "future cannot be sent between threads safely", + |db| { + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { + db.note(format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }, - ); - } + } + }, + ); } } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index b0ecc5d52ddb8..1666e8e5ae324 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -1,3 +1,4 @@ +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -9,7 +10,7 @@ use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; declare_clippy_lint! { /// ### What it does @@ -69,13 +70,21 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); span_lint( cx, INVALID_UPCAST_COMPARISONS, span, format!( "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), + cast_val_snip, if always { "true" } else { "false" }, ), ); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f44a5fdf715e5..28a0fbc051158 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -371,9 +371,9 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => Some(LenOutput::Option(adt.did())), - Some(sym::Result) => Some(LenOutput::Result(adt.did())), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())), _ => None, }, _ => None, diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5b0f95ffc377f..24a4c321bdabd 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; -use rustc_hir::{LetStmt, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -26,14 +26,14 @@ declare_clippy_lint! { } declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); -impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { - if let Some(ty) = local.ty // Ensure that it has a type defined - && let TyKind::Infer(()) = &ty.kind // that type is '_' +impl EarlyLintPass for UnderscoreTyped { + fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) { + if let Some(ty) = &local.ty // Ensure that it has a type defined + && let TyKind::Infer = ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && let sm = cx.tcx.sess.source_map() + && let sm = cx.sess().source_map() && !local.span.in_external_macro(sm) - && !is_from_proc_macro(cx, ty) + && !is_from_proc_macro(cx, &**ty) { let span_to_remove = sm .span_extend_to_prev_char_before(ty.span, ':', true) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a89cf3fdc1eee..c56fa257b0689 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -302,7 +302,6 @@ mod permissions_set_readonly_false; mod pointers_in_nomem_asm_block; mod precedence; mod ptr; -mod ptr_offset_with_cast; mod pub_underscore_fields; mod pub_use; mod question_mark; @@ -360,6 +359,7 @@ mod temporary_assignment; mod tests_outside_test_module; mod to_digit_is_some; mod to_string_trait_impl; +mod toplevel_ref_arg; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -592,7 +592,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(unwrap::Unwrap)); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); - store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); @@ -744,7 +743,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); - store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); @@ -831,5 +830,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 149ae5e710c1f..d8b186b6787d1 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>( impl_: &'tcx Impl<'_>, map: &FxIndexMap>, ) { - let single_usages = map + let (elidable_lts, usages): (Vec<_>, Vec<_>) = map .iter() .filter_map(|(def_id, usages)| { if let [ @@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>( None } }) - .collect::>(); + .unzip(); - if single_usages.is_empty() { + if elidable_lts.is_empty() { return; } - let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); - report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } @@ -795,9 +793,7 @@ fn report_elidable_lifetimes( // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); + .format(", "); let elidable_usages: Vec = usages .iter() @@ -860,36 +856,89 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - let mut suggestions = if elidable_lts.len() == explicit_params.len() { + if !elidable_lts + .iter() + .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) + { + return None; + } + + let mut suggestions = if elidable_lts.is_empty() { + vec![] + } else if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) + match &explicit_params[..] { + // no params, nothing to elide + [] => unreachable!("handled by `elidable_lts.is_empty()`"), + [param] => { + if elidable_lts.contains(¶m.def_id) { + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; - - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) + unreachable!("handled by `elidable_lts.is_empty()`") + } + }, + [_, _, ..] => { + // Given a list like `<'a, 'b, 'c, 'd, ..>`, + // + // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should + // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: + // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> + // ^^ ^^ ^^^^^^^^ + // + // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go + // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the + // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave + // the list valid: + // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^^^ ^^^^^^^^ + // + // In case there is no such starting cluster, we only need to do the second part of the algorithm: + // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ + + // Split off the starting cluster + // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): + // ``` + // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { + // // there were no lifetime param that couldn't be elided + // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // }; + // match split { /* .. */ } + // ``` + let Some(split_pos) = explicit_params + .iter() + .position(|param| !elidable_lts.contains(¶m.def_id)) + else { + // there were no lifetime param that couldn't be elided + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") }; - - Some((span, String::new())) - }) - .collect::>>()? + let split = explicit_params + .split_at_checked(split_pos) + .expect("got `split_pos` from `position` on the same Vec"); + + match split { + ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), + ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), + (cluster, rest @ [rest_first, ..]) => { + // the span for the cluster + (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) + // the span for the remaining lifetimes (calculations independent of the cluster) + .chain( + rest.array_windows() + .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) + .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), + ) + .map(|sp| (sp, String::new())) + .collect() + }, + } + }, + } }; suggestions.extend(usages.iter().map(|&usage| { diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 6ce7f0b1f0bc9..af475c40586fe 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -138,9 +138,9 @@ fn is_ref_iterable<'tcx>( return Some((AdjustKind::None, self_ty)); } - let res_ty = cx - .tcx - .erase_and_anonymize_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); + let res_ty = cx.tcx.erase_and_anonymize_regions( + EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)), + ); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 2ccff76809767..544c3c34d029c 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -22,7 +22,10 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::Diverging { ref break_spans } => { + NeverLoopResult::Diverging { + ref break_spans, + ref never_spans, + } => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -34,12 +37,16 @@ pub(super) fn check<'tcx>( { // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not // appropriate. - let app = if !contains_any_break_or_continue(block) && label.is_none() { + let mut app = if !contains_any_break_or_continue(block) && label.is_none() { Applicability::MachineApplicable } else { Applicability::Unspecified }; + if !never_spans.is_empty() { + app = Applicability::HasPlaceholders; + } + let mut suggestions = vec![( for_span.with_hi(iterator.span.hi()), for_to_if_let_sugg(cx, iterator, pat), @@ -51,6 +58,13 @@ pub(super) fn check<'tcx>( suggestions, app, ); + + for span in never_spans { + diag.span_help( + *span, + "this code is unreachable. Consider moving the reachable parts out", + ); + } } }); }, @@ -77,13 +91,16 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool { /// The first two bits of information are in this enum, and the last part is in the /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by /// scope. -#[derive(Clone)] +#[derive(Clone, Debug)] enum NeverLoopResult { /// A continue may occur for the main loop. MayContinueMainLoop, /// We have not encountered any main loop continue, /// but we are diverging (subsequent control flow is not reachable) - Diverging { break_spans: Vec }, + Diverging { + break_spans: Vec, + never_spans: Vec, + }, /// We have not encountered any main loop continue, /// and subsequent control flow is (possibly) reachable Normal, @@ -128,14 +145,18 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult ( NeverLoopResult::Diverging { break_spans: mut break_spans1, + never_spans: mut never_spans1, }, NeverLoopResult::Diverging { break_spans: mut break_spans2, + never_spans: mut never_spans2, }, ) => { break_spans1.append(&mut break_spans2); + never_spans1.append(&mut never_spans2); NeverLoopResult::Diverging { break_spans: break_spans1, + never_spans: never_spans1, } }, } @@ -207,6 +228,8 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { } return vec![stmt.span]; + } else if let Node::Block(_) = cx.tcx.parent_hir_node(expr.hir_id) { + return vec![expr.span]; } vec![] @@ -270,10 +293,13 @@ fn never_loop_expr<'tcx>( ExprKind::Match(e, arms, _) => { let e = never_loop_expr(cx, e, local_labels, main_loop_id); combine_seq(e, || { - arms.iter() - .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| { - combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) - }) + arms.iter().fold( + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + }, + |a, b| combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)), + ) }) }, ExprKind::Block(b, _) => { @@ -296,6 +322,7 @@ fn never_loop_expr<'tcx>( } else { NeverLoopResult::Diverging { break_spans: all_spans_after_expr(cx, expr), + never_spans: vec![], } } }, @@ -306,7 +333,10 @@ fn never_loop_expr<'tcx>( combine_seq(first, || { // checks if break targets a block instead of a loop mark_block_as_reachable(expr, local_labels); - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }) }, ExprKind::Break(dest, e) => { @@ -322,11 +352,15 @@ fn never_loop_expr<'tcx>( } else { all_spans_after_expr(cx, expr) }, + never_spans: vec![], } }) }, ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }), ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { @@ -356,7 +390,10 @@ fn never_loop_expr<'tcx>( }; let result = combine_seq(result, || { if cx.typeck_results().expr_ty(expr).is_never() { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: all_spans_after_expr(cx, expr), + } } else { NeverLoopResult::Normal } diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 288f27db8ca2c..5814b6815a1ea 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -4,8 +4,8 @@ use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, peel_blocks, peel_middle_ty_refs, span_contains_comment}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,7 +107,7 @@ impl ManualAbsDiff { |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); - let (b_ty, b_n_refs) = peel_middle_ty_refs(cx.typeck_results().expr_ty(b)); + let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); (a_ty == b_ty && (is_int(a_ty) || is_duration(a_ty))).then_some((a_ty, b_n_refs)) } diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 922db174e3d49..b036e78cdedc5 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -60,8 +60,8 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } match expr.kind { ExprKind::Match(scrutinee, [arm1, arm2], _) => { - if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) - || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) { check_as_ref(cx, scrutinee, span, self.msrv); } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 0c09a47c96518..de12fa29d02cd 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -102,7 +103,7 @@ fn simplify_half<'tcx>( && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind && method_path.ident.name == sym::len && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty) + && let (receiver_ty, refs_count, _) = peel_and_count_ty_refs(receiver_ty) && let ty::Slice(ty1) = receiver_ty.kind() // expr2 is `size_of::()`? && let ExprKind::Call(func, []) = expr2.kind diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index edbb556fd9766..a8490d6aa7d82 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; @@ -135,15 +135,11 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); - let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty); - let prefix = if n_ref > 0 { - if mutability == Mutability::Mut { - ".as_mut()" - } else { - ".as_ref()" - } - } else { - "" + let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty); + let prefix = match mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", }; let sugg = format!("{scrut}{prefix}.{method}()"); diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index dbae71bbb1b06..d4bfdb7e440d8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -30,8 +30,9 @@ pub(super) fn check_with<'tcx, F>( where F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); + let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { @@ -191,7 +192,7 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { Some(func) }, diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index b04db03f8d2e7..3a2097c3df261 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,7 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> // Compare match_expr ty with local in `let local = match match_expr {..}` Node::LetStmt(local) => { let results = cx.typeck_results(); - return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> .instantiate_identity() .output() .skip_binder(); - return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); + return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index ae09c2e87d6b7..1130d82ab78f9 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion() - && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind + && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(dotdot)) = pat.kind && let Some(def_id) = path.res.opt_def_id() && let ty = cx.tcx.type_of(def_id).instantiate_identity() && let ty::Adt(def, _) = ty.kind() @@ -15,14 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", |diag| { - diag.help("consider removing `..` from this binding"); + diag.span_suggestion_verbose( + dotdot, + "consider removing `..` from this binding", + "", + rustc_errors::Applicability::MachineApplicable, + ); }, ); } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b700703..83939d325794e 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -2,10 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; -use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::{Applicability, Diag}; @@ -133,7 +131,7 @@ fn report_single_pattern( let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Expr(_) = pat.kind - && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) + && let (ty, ty_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && (ty.is_integral() diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 8a976d1b4dc00..0ba84919395cc 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_middle_ty_refs(input_type) + && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4ed7de81ea3d0..47195fdd65f5f 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub fn check( && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) && let self_ty = args.type_at(0) - && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 && specializes_tostring(cx, deref_self_ty) { diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f851ebe91f37f..d43dc23a86b23 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::{Symbol, sym}; -use super::ITER_OVEREAGER_CLONED; -use crate::redundant_clone::REDUNDANT_CLONE; +use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; #[derive(Clone, Copy)] pub(super) enum Op<'a> { @@ -96,7 +95,7 @@ pub(super) fn check<'tcx>( } let (lint, msg, trailing_clone) = match op { - Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""), + Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""), Op::LaterCloned | Op::FixClosure(_, _) => ( ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49ca81dafc280..8679689c8ad4d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -91,6 +91,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod path_ends_with_ext; +mod ptr_offset_with_cast; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -1725,6 +1726,43 @@ declare_clippy_lint! { "Check for offset calculations on raw pointers to zero-sized types" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `offset` pointer method with a `usize` casted to an + /// `isize`. + /// + /// ### Why is this bad? + /// If we’re always increasing the pointer address, we can avoid the numeric + /// cast by using the `add` method instead. + /// + /// ### Example + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.offset(offset as isize); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.add(offset); + /// } + /// ``` + #[clippy::version = "1.30.0"] + pub PTR_OFFSET_WITH_CAST, + complexity, + "unneeded pointer offset cast" +} + declare_clippy_lint! { /// ### What it does /// Checks for `FileType::is_file()`. @@ -4576,6 +4614,31 @@ declare_clippy_lint! { "hardcoded localhost IP address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Iterator::cloned` where the original value could be used + /// instead. + /// + /// ### Why is this bad? + /// It is not always possible for the compiler to eliminate useless allocations and + /// deallocations generated by redundant `clone()`s. + /// + /// ### Example + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().cloned().map(|x| x.len()); + /// ``` + /// Use instead: + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().map(|x| x.len()); + /// ``` + #[clippy::version = "1.90.0"] + pub REDUNDANT_ITER_CLONED, + perf, + "detects redundant calls to `Iterator::cloned`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4665,6 +4728,7 @@ impl_lint_pass!(Methods => [ UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, ZST_OFFSET, + PTR_OFFSET_WITH_CAST, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, @@ -4755,6 +4819,7 @@ impl_lint_pass!(Methods => [ IO_OTHER_ERROR, SWAP_WITH_TEMPORARY, IP_CONSTANT, + REDUNDANT_ITER_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4960,10 +5025,7 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ( - sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, - [_arg], - ) => { + (sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => { zst_offset::check(cx, expr, recv); }, (sym::all, [arg]) => { @@ -5334,6 +5396,11 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, + (sym::offset | sym::wrapping_offset, [arg]) => { + zst_offset::check(cx, expr, recv); + + ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv); + }, (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 4235af882b0c7..9d2c5e6232d65 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -10,8 +10,7 @@ use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) - && ref_depth >= 1 + && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04f0e3c0479e0..71b2f251eded6 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; +use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -97,6 +98,12 @@ pub(super) fn check<'tcx>( return false; } + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + // needs to target Default::default in particular or be *::new and have a Default impl // available if (is_new(fun) && output_type_implements_default(fun)) diff --git a/clippy_lints/src/methods/ptr_offset_with_cast.rs b/clippy_lints/src/methods/ptr_offset_with_cast.rs new file mode 100644 index 0000000000000..d19d3b8eb89d9 --- /dev/null +++ b/clippy_lints/src/methods/ptr_offset_with_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Symbol; +use std::fmt; + +use super::PTR_OFFSET_WITH_CAST; + +pub(super) fn check( + cx: &LateContext<'_>, + method: Symbol, + expr: &Expr<'_>, + recv: &Expr<'_>, + arg: &Expr<'_>, + msrv: Msrv, +) { + // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions + // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable. + if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) { + return; + } + + let method = match method { + sym::offset => Method::Offset, + sym::wrapping_offset => Method::WrappingOffset, + _ => return, + }; + + if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() { + return; + } + + // Check if the argument to the method call is a cast from usize. + let cast_lhs_expr = match arg.kind { + ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs, + _ => return, + }; + + let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else { + return; + }; + + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); + span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| { + diag.multipart_suggestion( + format!("use `{}` instead", method.suggestion()), + vec![ + (method_name.ident.span, method.suggestion().to_string()), + (arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()), + ], + Applicability::MachineApplicable, + ); + }); +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + #[must_use] + fn suggestion(self) -> &'static str { + match self { + Self::Offset => "add", + Self::WrappingOffset => "wrapping_add", + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c1f4904af7c48..640931a828998 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,11 +3,12 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, +}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, sym, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -119,8 +120,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) + && let (target_ty, n_target_refs, _) = peel_and_count_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs, _) = peel_and_count_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -385,7 +386,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_middle_ty_refs(*input) + && let (input, n_refs, _) = peel_and_count_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait() diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 38fad239f6799..e56f4b80d0172 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; @@ -50,8 +50,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + let (base_res_ty, res_depth, _) = peel_and_count_ty_refs(res_ty); + let (base_rcv_ty, rcv_depth, _) = peel_and_count_ty_refs(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { if let Some(parent) = get_parent_expr(cx, expr) { // allow the `as_ref` or `as_mut` if it is followed by another method call diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 6258e408217fc..1ceee836732ae 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -256,7 +256,11 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo } fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { - if let ImplItemImplKind::Trait { trait_item_def_id: Ok(trait_item_def_id), .. } = impl_item.impl_kind { + if let ImplItemImplKind::Trait { + trait_item_def_id: Ok(trait_item_def_id), + .. + } = impl_item.impl_kind + { let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); let ImplItemKind::Fn(_, body_id) = impl_item.kind else { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 09ee6f7037c64..19e9910dfe9dd 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,57 +1,11 @@ -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, - last_path_segment, -}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, last_path_segment}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, -}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::def_id::LocalDefId; - -use crate::ref_patterns::REF_PATTERNS; - -declare_clippy_lint! { - /// ### What it does - /// Checks for function arguments and let bindings denoted as - /// `ref`. - /// - /// ### Why is this bad? - /// The `ref` declaration makes the function take an owned - /// value, but turns the argument into a reference (which means that the value - /// is destroyed when exiting the function). This adds not much value: either - /// take a reference type, or take an owned value and create references in the - /// body. - /// - /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The - /// type of `x` is more obvious with the former. - /// - /// ### Known problems - /// If the argument is dereferenced within the function, - /// removing the `ref` will lead to errors. This can be fixed by removing the - /// dereferences, e.g., changing `*x` to `x` within the function. - /// - /// ### Example - /// ```no_run - /// fn foo(ref _x: u8) {} - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn foo(_x: &u8) {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TOPLEVEL_REF_ARG, - style, - "an entire binding declared as `ref`, in a function argument or a `let` statement" -} declare_clippy_lint! { /// ### What it does @@ -140,79 +94,13 @@ declare_clippy_lint! { } declare_lint_pass!(LintPass => [ - TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - k: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, - _: Span, - _: LocalDefId, - ) { - if !matches!(k, FnKind::Closure) { - for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind - && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) - && !arg.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint_hir( - cx, - TOPLEVEL_REF_ARG, - arg.hir_id, - arg.pat.span, - "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ - Consider using a reference type instead", - ); - } - } - } - } - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind - && let Some(init) = local.init - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) - && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), - app, - ); - }, - ); - } if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Binary(binop, a, b) = &expr.kind && matches!(binop.node, BinOpKind::And | BinOpKind::Or) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 39b5964bd87b1..1c62caa1c8274 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -250,7 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { AssocContainer::Trait | AssocContainer::TraitImpl(_) => { note_prev_span_then_ret!(self.prev_span, impl_item.span); }, - AssocContainer::InherentImpl => {} + AssocContainer::InherentImpl => {}, } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index c6c27e22b90e5..bc5e72270f4e3 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,3 +1,4 @@ +use clippy_utils::desugar_await; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; @@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>( ) { for_each_expr(cx, node, |expr| { match expr.kind { + // The `await` itself will desugar to two unsafe calls, but we should ignore those. + // Instead, check the expression that is `await`ed + _ if let Some(e) = desugar_await(expr) => { + collect_unsafe_exprs(cx, e, unsafe_ops); + return Continue(Descend::No); + }, + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), ExprKind::Field(e, _) => { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index c4cad592e3675..a21c361356e83 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize), + FnKind::ImplTraitFn( + std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize + ), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index e3fc8d8fea7d1..8f5ee390f7222 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::same_type_and_consts; +use clippy_utils::ty::same_type_modulo_regions; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { let input_ty = tck.expr_ty(input).peel_refs(); let output_ty = tck.expr_ty(output).peel_refs(); - !same_type_and_consts(input_ty, output_ty) + !same_type_modulo_regions(input_ty, output_ty) } fn check_op<'tcx>( diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs deleted file mode 100644 index d8d813f9846d5..0000000000000 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::sym; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use std::fmt; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `offset` pointer method with a `usize` casted to an - /// `isize`. - /// - /// ### Why is this bad? - /// If we’re always increasing the pointer address, we can avoid the numeric - /// cast by using the `add` method instead. - /// - /// ### Example - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.offset(offset as isize); - /// } - /// ``` - /// - /// Could be written: - /// - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.add(offset); - /// } - /// ``` - #[clippy::version = "1.30.0"] - pub PTR_OFFSET_WITH_CAST, - complexity, - "unneeded pointer offset cast" -} - -declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); - -impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { - return; - }; - - // Check if the argument to the method call is a cast from usize - let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { - return; - }; - - let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); - if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { - span_lint_and_sugg( - cx, - PTR_OFFSET_WITH_CAST, - expr.span, - msg, - "try", - sugg, - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg); - } - } -} - -// If the given expression is a cast from a usize, return the lhs of the cast -fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind - && is_expr_ty_usize(cx, cast_lhs_expr) - { - return Some(cast_lhs_expr); - } - None -} - -// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the -// receiver, the arg of the method call, and the method. -fn expr_as_ptr_offset_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, -) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind - && is_expr_ty_raw_ptr(cx, arg_0) - { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name == sym::wrapping_offset { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } - } - None -} - -// Is the type of the expression a usize? -fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize -} - -// Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_raw_ptr() -} - -fn build_suggestion( - cx: &LateContext<'_>, - method: Method, - receiver_expr: &Expr<'_>, - cast_lhs_expr: &Expr<'_>, -) -> Option { - let receiver = receiver_expr.span.get_source_text(cx)?; - let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?; - Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) -} - -#[derive(Copy, Clone)] -enum Method { - Offset, - WrappingOffset, -} - -impl Method { - #[must_use] - fn suggestion(self) -> &'static str { - match self { - Self::Offset => "add", - Self::WrappingOffset => "wrapping_add", - } - } -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Offset => write!(f, "offset"), - Self::WrappingOffset => write!(f, "wrapping_offset"), - } - } -} diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index de12a25b03dff..4aa100a50e053 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,9 +8,9 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, + is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -393,8 +393,8 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) - // check `...` is `val` from binding - && path_to_local_id(ret_expr, ok_val) + // check if `...` is `val` from binding or `val.into()` + && is_local_or_local_into(cx, ret_expr, ok_val) { true } else { @@ -417,6 +417,17 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A } } +/// Check if `expr` is `val` or `val.into()` +fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> bool { + let is_into_call = fn_def_id_with_node_args(cx, expr) + .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) + .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); + match expr.kind { + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), + _ => path_to_local_id(expr, val), + } +} + fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index acd840401c6bb..b8d4e7c4651d2 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::source::snippet; +use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{get_enclosing_block, sym}; use rustc_errors::Applicability; @@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", - format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")), + format!("{ident}.resize({len}, 0);\n{}", " ".repeat(indent)), applicability, ); }, @@ -100,14 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", format!( - "{}.resize({}, 0); {}", - ident, + "{ident}.resize({}, 0);\n{}", snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") + " ".repeat(indent) ), applicability, ); @@ -130,6 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } +fn first_stmt_containing_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx hir::Stmt<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let hir::Node::Stmt(stmt) = node { + Some(stmt) + } else { + None + } + }) +} + struct ReadVecVisitor<'tcx> { local_id: HirId, read_zero_expr: Option<&'tcx Expr<'tcx>>, diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1d58cdd26d88d..de6766cbe94aa 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -263,7 +263,7 @@ fn is_call_with_ref_arg<'tcx>( && args.len() == 1 && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() - && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx)) + && let (inner_ty, 1, _) = peel_and_count_ty_refs(args[0].node.ty(mir, cx.tcx)) && !is_copy(cx, inner_ty) { Some((def_id, *local, inner_ty, destination.as_local()?)) diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 324a05cdcc0c8..a358eff2ce554 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs deleted file mode 100644 index e0c93153a77a8..0000000000000 --- a/clippy_lints/src/returns.rs +++ /dev/null @@ -1,513 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; -use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, - leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, sym, -}; -use core::ops::ControlFlow; -use rustc_ast::MetaItemInner; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, - StmtKind, -}; -use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span}; -use std::borrow::Cow; -use std::fmt::Display; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// ### Why is this bad? - /// It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// ### Known problems - /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead - /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). - /// This could become relevant if the code is later changed to use the code that would have been - /// bound without first assigning it to a let-binding. - /// - /// ### Example - /// ```no_run - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ```no_run - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements at the end of a block. - /// - /// ### Why is this bad? - /// Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// ### Example - /// ```no_run - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```no_run - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NEEDLESS_RETURN, - // This lint requires some special handling in `check_final_expr` for `#[expect]`. - // This handling needs to be updated if the group gets changed. This should also - // be caught by tests. - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements on `Err` paired with the `?` operator. - /// - /// ### Why is this bad? - /// The `return` is unnecessary. - /// - /// Returns may be used to add attributes to the return expression. Return - /// statements with attributes are therefore be accepted by this lint. - /// - /// ### Example - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// simplify to - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// if paired with `try_err`, use instead: - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...); - /// } - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.73.0"] - pub NEEDLESS_RETURN_WITH_QUESTION_MARK, - style, - "using a return statement like `return Err(expr)?;` where removing it would suffice" -} - -#[derive(PartialEq, Eq)] -enum RetReplacement<'tcx> { - Empty, - Block, - Unit, - NeedsPar(Cow<'tcx, str>, Applicability), - Expr(Cow<'tcx, str>, Applicability), -} - -impl RetReplacement<'_> { - fn sugg_help(&self) -> &'static str { - match self { - Self::Empty | Self::Expr(..) => "remove `return`", - Self::Block => "replace `return` with an empty block", - Self::Unit => "replace `return` with a unit value", - Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", - } - } - - fn applicability(&self) -> Applicability { - match self { - Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, - _ => Applicability::MachineApplicable, - } - } -} - -impl Display for RetReplacement<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Empty => write!(f, ""), - Self::Block => write!(f, "{{}}"), - Self::Unit => write!(f, "()"), - Self::NeedsPar(inner, _) => write!(f, "({inner})"), - Self::Expr(inner, _) => write!(f, "{inner}"), - } - } -} - -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); - -/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This -/// is the case when the enclosing block expression is coerced to some other type, which only works -/// because of the never-ness of `return` expressions -fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { - cx.tcx - .hir_parent_iter(stmt_hir_id) - .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) - .is_some_and(|e| { - cx.typeck_results() - .expr_adjustments(e) - .iter() - .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) - }) -} - -impl<'tcx> LateLintPass<'tcx> for Return { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !stmt.span.in_external_macro(cx.sess().source_map()) - && let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Ret(Some(ret)) = expr.kind - // return Err(...)? desugars to a match - // over a Err(...).branch() - // which breaks down to a branch call, with the callee being - // the constructor of the Err variant - && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind - && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind - && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) - - // Ensure this is not the final stmt, otherwise removing it would cause a compile error - && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) - && let ItemKind::Fn { body, .. } = item.kind - && let block = cx.tcx.hir_body(body).value - && let ExprKind::Block(block, _) = block.kind - && !is_inside_let_else(cx.tcx, expr) - && let [.., final_stmt] = block.stmts - && final_stmt.hir_id != stmt.hir_id - && !is_from_proc_macro(cx, expr) - && !stmt_needs_never_type(cx, stmt.hir_id) - { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN_WITH_QUESTION_MARK, - expr.span.until(ret.span), - "unneeded `return` statement with `?` operator", - "remove it", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.iter().last() - && let StmtKind::Let(local) = &stmt.kind - && local.ty.is_none() - && cx.tcx.hir_attrs(local.hir_id).is_empty() - && let Some(initexpr) = &local.init - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) - && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) - && !initexpr.span.in_external_macro(cx.sess().source_map()) - && !retexpr.span.in_external_macro(cx.sess().source_map()) - && !local.span.from_expansion() - && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) - { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(src) = initexpr.span.get_source_text(cx) { - let sugg = if binary_expr_needs_parentheses(initexpr) { - if has_enclosing_paren(&src) { - src.to_owned() - } else { - format!("({src})") - } - } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if has_enclosing_paren(&src) { - format!("{src} as _") - } else { - format!("({src}) as _") - } - } else { - src.to_owned() - }; - err.multipart_suggestion( - "return the expression directly", - vec![(local.span, String::new()), (retexpr.span, sugg)], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - sp: Span, - _: LocalDefId, - ) { - if sp.from_expansion() { - return; - } - - match kind { - FnKind::Closure => { - // when returning without value in closure, replace this `return` - // with an empty block to prevent invalid suggestion (see #6501) - let replacement = if let ExprKind::Ret(None) = &body.value.kind { - RetReplacement::Block - } else { - RetReplacement::Empty - }; - check_final_expr(cx, body.value, vec![], replacement, None); - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, sp, vec![]); - }, - } - } -} - -// if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { - if let ExprKind::Block(block, _) = expr_kind { - if let Some(block_expr) = block.expr { - check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(expr) => { - check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); - }, - StmtKind::Semi(semi_expr) => { - // Remove ending semicolons and any whitespace ' ' in between. - // Without `return`, the suggestion might not compile if the semicolon is retained - if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { - let semi_span_to_remove = - span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); - semi_spans.push(semi_span_to_remove); - } - check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); - }, - _ => (), - } - } - } -} - -fn check_final_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an - * needless return */ - replacement: RetReplacement<'tcx>, - match_ty_opt: Option>, -) { - let peeled_drop_expr = expr.peel_drop_temps(); - match &peeled_drop_expr.kind { - // simple return is always "bad" - ExprKind::Ret(inner) => { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - let replacement = if let Some(inner_expr) = inner { - // if desugar of `do yeet`, don't lint - if let ExprKind::Call(path_expr, [_]) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind - { - return; - } - - let mut applicability = Applicability::MachineApplicable; - let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); - if binary_expr_needs_parentheses(inner_expr) { - RetReplacement::NeedsPar(snippet, applicability) - } else { - RetReplacement::Expr(snippet, applicability) - } - } else { - match match_ty_opt { - Some(match_ty) => { - match match_ty.kind() { - // If the code got till here with - // tuple not getting detected before it, - // then we are sure it's going to be Unit - // type - ty::Tuple(_) => RetReplacement::Unit, - // We don't want to anything in this case - // cause we can't predict what the user would - // want here - _ => return, - } - }, - None => replacement, - } - }; - - if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { - return; - } - - if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { - return; - } - - // Returns may be used to turn an expression into a statement in rustc's AST. - // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) - // `#[expect(clippy::needless_return)]` needs to be handled separately to - // actually fulfill the expectation (clippy::#12998) - match cx.tcx.hir_attrs(expr.hir_id) { - [] => {}, - [attr] => { - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings - ) - { - // This is an expectation of the `needless_return` lint - } else { - return; - } - }, - _ => return, - } - - emit_return_lint( - cx, - peeled_drop_expr.span, - ret_span, - semi_spans, - &replacement, - expr.hir_id, - ); - }, - ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); - if let Some(else_clause) = else_clause_opt { - // The `RetReplacement` won't be used there as `else_clause` will be either a block or - // a `if` expression. - check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); - } - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, MatchSource::Normal) => { - let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); - for arm in *arms { - check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); - } - }, - // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), - } -} - -fn emit_return_lint( - cx: &LateContext<'_>, - lint_span: Span, - ret_span: Span, - semi_spans: Vec, - replacement: &RetReplacement<'_>, - at: HirId, -) { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - at, - lint_span, - "unneeded `return` statement", - |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); - - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }, - ); -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { - if let Some(def_id) = fn_def_id(cx, e) - && cx - .tcx - .fn_sig(def_id) - .instantiate_identity() - .skip_binder() - .output() - .walk() - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -// Go backwards while encountering whitespace and extend the given Span to that point. -fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { - if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [b' ', b'\t', b'\n']; - if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { - let len = prev_source.len() - non_ws_pos - 1; - return sp.with_lo(sp.lo() - BytePos::from_usize(len)); - } - } - - sp -} diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs new file mode 100644 index 0000000000000..e2002fb36e5a6 --- /dev/null +++ b/clippy_lints/src/returns/let_and_return.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::has_enclosing_paren; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, PatKind, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::GenericArgKind; +use rustc_span::edition::Edition; + +use super::LET_AND_RETURN; + +pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.last() + && let StmtKind::Let(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir_attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) + && !initexpr.span.in_external_macro(cx.sess().source_map()) + && !retexpr.span.in_external_macro(cx.sess().source_map()) + && !local.span.from_expansion() + && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(src) = initexpr.span.get_source_text(cx) { + let sugg = if binary_expr_needs_parentheses(initexpr) { + if has_enclosing_paren(&src) { + src.to_owned() + } else { + format!("({src})") + } + } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if has_enclosing_paren(&src) { + format!("{src} as _") + } else { + format!("({src}) as _") + } + } else { + src.to_owned() + }; + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, sugg)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } +} +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_expr(cx, expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_binder() + .output() + .walk() + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/clippy_lints/src/returns/mod.rs b/clippy_lints/src/returns/mod.rs new file mode 100644 index 0000000000000..47c6332b9b81d --- /dev/null +++ b/clippy_lints/src/returns/mod.rs @@ -0,0 +1,140 @@ +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Block, Body, FnDecl, Stmt}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +mod let_and_return; +mod needless_return; +mod needless_return_with_question_mark; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// ### Why is this bad? + /// It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// ### Known problems + /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead + /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). + /// This could become relevant if the code is later changed to use the code that would have been + /// bound without first assigning it to a let-binding. + /// + /// ### Example + /// ```no_run + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ```no_run + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements at the end of a block. + /// + /// ### Why is this bad? + /// Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// ### Example + /// ```no_run + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```no_run + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_RETURN, + // This lint requires some special handling in `check_final_expr` for `#[expect]`. + // This handling needs to be updated if the group gets changed. This should also + // be caught by tests. + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements on `Err` paired with the `?` operator. + /// + /// ### Why is this bad? + /// The `return` is unnecessary. + /// + /// Returns may be used to add attributes to the return expression. Return + /// statements with attributes are therefore be accepted by this lint. + /// + /// ### Example + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// simplify to + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// if paired with `try_err`, use instead: + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...); + /// } + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, + style, + "using a return statement like `return Err(expr)?;` where removing it would suffice" +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + needless_return_with_question_mark::check_stmt(cx, stmt); + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + let_and_return::check_block(cx, block); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + sp: Span, + _: LocalDefId, + ) { + needless_return::check_fn(cx, kind, body, sp); + } +} diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs new file mode 100644 index 0000000000000..04739fc1b22ad --- /dev/null +++ b/clippy_lints/src/returns/needless_return.rs @@ -0,0 +1,269 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{ + binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, + span_contains_cfg, span_find_starting_semi, sym, +}; +use rustc_ast::MetaItemInner; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind}; +use rustc_lint::{LateContext, Level, LintContext}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{BytePos, Pos, Span}; +use std::borrow::Cow; +use std::fmt::Display; + +use super::NEEDLESS_RETURN; + +#[derive(PartialEq, Eq)] +enum RetReplacement<'tcx> { + Empty, + Block, + Unit, + NeedsPar(Cow<'tcx, str>, Applicability), + Expr(Cow<'tcx, str>, Applicability), +} + +impl RetReplacement<'_> { + fn sugg_help(&self) -> &'static str { + match self { + Self::Empty | Self::Expr(..) => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", + } + } + + fn applicability(&self) -> Applicability { + match self { + Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, + _ => Applicability::MachineApplicable, + } + } +} + +impl Display for RetReplacement<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), + Self::NeedsPar(inner, _) => write!(f, "({inner})"), + Self::Expr(inner, _) => write!(f, "{inner}"), + } + } +} + +pub(super) fn check_fn<'tcx>(cx: &LateContext<'tcx>, kind: FnKind<'tcx>, body: &'tcx Body<'tcx>, sp: Span) { + if sp.from_expansion() { + return; + } + + match kind { + FnKind::Closure => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, body.value, vec![], replacement, None); + }, + FnKind::ItemFn(..) | FnKind::Method(..) => { + check_block_return(cx, &body.value.kind, sp, vec![]); + }, + } +} + +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); + } else if let Some(stmt) = block.stmts.last() { + if span_contains_cfg( + cx, + Span::between( + stmt.span, + cx.sess().source_map().end_point(block.span), // the closing brace of the block + ), + ) { + return; + } + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); + }, + StmtKind::Semi(semi_expr) => { + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); + } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); + }, + _ => (), + } + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ + replacement: RetReplacement<'tcx>, + match_ty_opt: Option>, +) { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { + // simple return is always "bad" + ExprKind::Ret(inner) => { + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + let replacement = if let Some(inner_expr) = inner { + // if desugar of `do yeet`, don't lint + if let ExprKind::Call(path_expr, [_]) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind + { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); + if binary_expr_needs_parentheses(inner_expr) { + RetReplacement::NeedsPar(snippet, applicability) + } else { + RetReplacement::Expr(snippet, applicability) + } + } else { + match match_ty_opt { + Some(match_ty) => { + match match_ty.kind() { + // If the code got till here with + // tuple not getting detected before it, + // then we are sure it's going to be Unit + // type + ty::Tuple(_) => RetReplacement::Unit, + // We don't want to anything in this case + // cause we can't predict what the user would + // want here + _ => return, + } + }, + None => replacement, + } + }; + + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; + } + + if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // `#[expect(clippy::needless_return)]` needs to be handled separately to + // actually fulfill the expectation (clippy::#12998) + match cx.tcx.hir_attrs(expr.hir_id) { + [] => {}, + [attr] => { + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let metas = attr.meta_item_list() + && let Some(lst) = metas + && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && matches!( + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings + ) + { + // This is an expectation of the `needless_return` lint + } else { + return; + } + }, + _ => return, + } + + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); + }, + ExprKind::If(_, then, else_clause_opt) => { + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); + if let Some(else_clause) = else_clause_opt { + // The `RetReplacement` won't be used there as `else_clause` will be either a block or + // a `if` expression. + check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); + } + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, arms, MatchSource::Normal) => { + let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); + for arm in *arms { + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); + } + }, + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), + } +} + +fn emit_return_lint( + cx: &LateContext<'_>, + lint_span: Span, + ret_span: Span, + semi_spans: Vec, + replacement: &RetReplacement<'_>, + at: HirId, +) { + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + lint_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); +} + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs new file mode 100644 index 0000000000000..c05038cd1e50e --- /dev/null +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; + +use super::NEEDLESS_RETURN_WITH_QUESTION_MARK; + +pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !stmt.span.in_external_macro(cx.sess().source_map()) + && let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Ret(Some(ret)) = expr.kind + // return Err(...)? desugars to a match + // over a Err(...).branch() + // which breaks down to a branch call, with the callee being + // the constructor of the Err variant + && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind + && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind + && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + + // Ensure this is not the final stmt, otherwise removing it would cause a compile error + && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) + && let ItemKind::Fn { body, .. } = item.kind + && let block = cx.tcx.hir_body(body).value + && let ExprKind::Block(block, _) = block.kind + && !is_inside_let_else(cx.tcx, expr) + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id != stmt.hir_id + && !is_from_proc_macro(cx, expr) + && !stmt_needs_never_type(cx, stmt.hir_id) + { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN_WITH_QUESTION_MARK, + expr.span.until(ret.span), + "unneeded `return` statement with `?` operator", + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + } +} + +/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. +/// This is the case when the enclosing block expression is coerced to some other type, +/// which only works because of the never-ness of `return` expressions +fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { + cx.tcx + .hir_parent_iter(stmt_hir_id) + .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) + .is_some_and(|e| { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) + }) +} diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 1dea8f17c34be..371d62a068492 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,6 +83,19 @@ impl SemicolonBlock { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); + // If the block is surrounded by parens (`({ 0 });`), the author probably knows what + // they're doing and why, so don't get in their way. + // + // This has the additional benefit of stopping the block being parsed as a function call: + // ``` + // fn foo() { + // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call + // (5); + // } + if remove_span.check_source_text(cx, |src| src.contains(')')) { + return; + } + if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) { return; } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 60d923bcd77e7..606e852aae9e3 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, peel_middle_ty_refs}; +use clippy_utils::path_def_id; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -59,7 +60,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_middle_ty_refs(arg_ty).1 > 1 + && peel_and_count_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs new file mode 100644 index 0000000000000..074b79263d377 --- /dev/null +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -0,0 +1,119 @@ +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_lint_allowed, iter_input_pats}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingMode, Body, ByRef, FnDecl, Mutability, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +use crate::ref_patterns::REF_PATTERNS; + +declare_clippy_lint! { + /// ### What it does + /// Checks for function arguments and let bindings denoted as + /// `ref`. + /// + /// ### Why is this bad? + /// The `ref` declaration makes the function take an owned + /// value, but turns the argument into a reference (which means that the value + /// is destroyed when exiting the function). This adds not much value: either + /// take a reference type, or take an owned value and create references in the + /// body. + /// + /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The + /// type of `x` is more obvious with the former. + /// + /// ### Known problems + /// If the argument is dereferenced within the function, + /// removing the `ref` will lead to errors. This can be fixed by removing the + /// dereferences, e.g., changing `*x` to `x` within the function. + /// + /// ### Example + /// ```no_run + /// fn foo(ref _x: u8) {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo(_x: &u8) {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub TOPLEVEL_REF_ARG, + style, + "an entire binding declared as `ref`, in a function argument or a `let` statement" +} + +declare_lint_pass!(ToplevelRefArg => [TOPLEVEL_REF_ARG]); + +impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + k: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: LocalDefId, + ) { + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !arg.span.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ + Consider using a reference type instead", + ); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(local) = stmt.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let Some(init) = local.init + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = match mutabl { + Mutability::Mut => ("mut ", sugg_init.mut_addr()), + Mutability::Not => ("", sugg_init.addr()), + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); + } + } +} diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index e58212fae15cf..e67ab6a73d269 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -28,16 +28,27 @@ pub(super) fn check<'tcx>( format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); - let (deref, cast) = if *mutbl == Mutability::Mut { - ("&mut *", "*mut") - } else { - ("&*", "*const") + let (deref, cast) = match mutbl { + Mutability::Mut => ("&mut *", "*mut"), + Mutability::Not => ("&*", "*const"), }; let mut app = Applicability::MachineApplicable; let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if msrv.meets(cx, msrvs::POINTER_CAST) { + if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + if from_ptr_ty.has_erased_regions() { + // We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe + // to do because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + } + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -45,15 +56,22 @@ pub(super) fn check<'tcx>( sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if *from_ptr_ty == *to_ref_ty { - if from_ptr_ty.has_erased_regions() { - if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) - } else { - sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) - .to_string() - } - } else { + if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // 1. We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + // 2. We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe to do + // because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) + } else { + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))).to_string() } } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 6aeb22d41a7d7..70c2a73ce6ef2 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,9 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect, ); triggered = true; - } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { + } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) + && !const_context + { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index cee4a53f03cbe..51116b5eba9e1 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; @@ -95,16 +95,13 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( + span_lint_and_help( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, + None, + "initialize the buffer or wrap the content in `MaybeUninit`", ); } } else { diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 1f5351e32aacf..5224b62e9fc71 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && let Some(init) = local.init && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -211,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { true diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 490da4f1e037a..34dfe5b6546fa 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -292,6 +292,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // Shouldn't lint when `expr` is in macro. if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { + walk_expr(self, expr); return; } // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 8252e6d48694b..9d5be922f43f8 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,20 +2,21 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty}; +use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, + self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let types_to_skip = generics .params .iter() - .filter_map(|param| match param { - GenericParam { - kind: - GenericParamKind::Const { - ty: Ty { hir_id, .. }, .. - }, - .. - } => Some(*hir_id), + .filter_map(|param| match param.kind { + GenericParamKind::Const { ty, .. } => Some(ty.hir_id), _ => None, }) - .chain(std::iter::once(self_ty.hir_id)) + .chain([self_ty.hir_id]) .collect(); StackItem::Check { impl_id: item.owner_id.def_id, @@ -209,11 +204,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && !types_to_skip.contains(&hir_ty.hir_id) && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() - && same_type_and_consts(ty, impl_ty) + && same_type_modulo_regions(ty, impl_ty) // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that - // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && same_lifetimes(ty, impl_ty) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -226,18 +221,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { - } else { - return; - } - match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), - ExprKind::Call(fun, _) => { - if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - check_path(cx, path); - } - }, - ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), - _ => (), + match expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), + ExprKind::Call(fun, _) => { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { + check_path(cx, path); + } + }, + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), + _ => (), + } } } @@ -307,36 +300,20 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { } } -/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns -/// `false`. +/// Checks whether types `a` and `b` have the same lifetime parameters. /// /// This function does not check that types `a` and `b` are the same types. fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; - match (&a.kind(), &b.kind()) { - (&Adt(_, args_a), &Adt(_, args_b)) => { - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - // TODO: Handle inferred lifetimes - (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), - _ => true, - }) + match (a.kind(), b.kind()) { + (Adt(_, args_a), Adt(_, args_b)) => { + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) }, _ => a == b, } } - -/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. -fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { - use rustc_middle::ty::{Adt, GenericArgKind}; - match ty.kind() { - &Adt(_, args) => !args - .iter() - // TODO: Handle inferred lifetimes - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), - _ => true, - } -} diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index e45f884cfcbcf..1b137017ecb46 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() - && same_type_and_consts(from_ty, to_ty) + && same_type_modulo_regions(from_ty, to_ty) { span_lint_and_then( cx, @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + if same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut app).0; span_lint_and_sugg( @@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // If the types are identical then .into_iter() can be removed, unless the type // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. - if same_type_and_consts(a, b) && !is_copy(cx, b) { + if same_type_modulo_regions(a, b) && !is_copy(cx, b) { // Below we check if the parent method call meets the following conditions: // 1. First parameter is `&mut self` (requires mutable reference) // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) @@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { span_lint_and_help( cx, @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( @@ -407,7 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } else if name == sym::from_fn && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index bdf7431f29f29..d58b47bf6deb3 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index e01f563c49e73..2c66fdc73f539 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-04 +nightly-2025-09-18 ``` diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 1a25c90d735e4..948a7203402d5 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -13,20 +13,24 @@ //! if the span is not from a `macro_rules` based macro. use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::AttrStyle; -use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; +use rustc_ast::ast::{ + AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy, +}; use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, - TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, + ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, + QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, + YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; /// The search pattern to look for. Used by `span_matches_pat` #[derive(Clone)] @@ -289,7 +293,7 @@ fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { && !vis_span.is_empty() { start_pat = Pat::Str("pub"); - }; + } (start_pat, end_pat) } @@ -321,14 +325,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI }; match tcx.hir_node(hir_id) { Node::Item(Item { vis_span, .. }) - | Node::ImplItem(ImplItem { impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, .. }) => { + | Node::ImplItem(ImplItem { + impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, + .. + }) => { if !vis_span.is_empty() { - start_pat = Pat::Str("pub") + start_pat = Pat::Str("pub"); } }, Node::ImplItem(_) | Node::TraitItem(_) => {}, _ => start_pat = Pat::Str(""), - }; + } (start_pat, end_pat) } @@ -403,6 +410,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, @@ -411,6 +419,127 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } } +fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { + use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind}; + + match &ty.kind { + TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => { + (Pat::Str("&"), ast_ty_search_pat(ty).1) + }, + TyKind::FnPtr(fn_ptr) => ( + if let Safety::Unsafe(_) = fn_ptr.safety { + Pat::Str("unsafe") + } else if let Extern::Explicit(strlit, _) = fn_ptr.ext + && strlit.symbol == sym::rust + { + Pat::MultiStr(&["fn", "extern"]) + } else { + Pat::Str("extern") + }, + match &fn_ptr.decl.output { + FnRetTy::Default(_) => { + if let [.., param] = &*fn_ptr.decl.inputs { + ast_ty_search_pat(¶m.ty).1 + } else { + Pat::Str("(") + } + }, + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + ), + TyKind::Never => (Pat::Str("!"), Pat::Str("!")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` + TyKind::Tup(tup) => match &**tup { + [] => (Pat::Str(")"), Pat::Str("(")), + [ty] => ast_ty_search_pat(ty), + [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + }, + TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::Path(qself_path, path) => { + let start = if qself_path.is_some() { + Pat::Str("<") + } else if let Some(first) = path.segments.first() { + ident_search_pat(first.ident).0 + } else { + // this shouldn't be possible, but sure + Pat::Str("") + }; + let end = if let Some(last) = path.segments.last() { + match last.args.as_deref() { + // last `>` in `std::foo::Bar` + Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"), + Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output { + FnRetTy::Default(_) => { + if let Some(last) = par_args.inputs.last() { + // `B` in `(A, B)` -- `)` gets stripped + ast_ty_search_pat(last).1 + } else { + // `(` in `()` -- `)` gets stripped + Pat::Str("(") + } + }, + // `C` in `(A, B) -> C` + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + // last `..` in `(..)` -- `)` gets stripped + Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."), + // `bar` in `std::foo::bar` + None => ident_search_pat(last.ident).1, + } + } else { + // this shouldn't be possible, but sure + #[allow( + clippy::collapsible_else_if, + reason = "we want to keep these cases together, since they are both impossible" + )] + if qself_path.is_some() { + // last `>` in `` + Pat::Str(">") + } else { + Pat::Str("") + } + }; + (start, end) + }, + TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::Paren(ty) => ast_ty_search_pat(ty), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1), + TyKind::TraitObject(_, trait_obj_syntax) => { + if let TraitObjectSyntax::Dyn = trait_obj_syntax { + (Pat::Str("dyn"), Pat::Str("")) + } else { + // NOTE: `TraitObject` is incomplete. It will always return true then. + (Pat::Str(""), Pat::Str("")) + } + }, + TyKind::MacCall(mac_call) => { + let start = if let Some(first) = mac_call.path.segments.first() { + ident_search_pat(first.ident).0 + } else { + Pat::Str("") + }; + (start, Pat::Str("")) + }, + + // implicit, so has no contents to match against + TyKind::ImplicitSelf + + // experimental + |TyKind::Pat(..) + + // unused + | TyKind::CVarArgs + | TyKind::Typeof(_) + + // placeholder + | TyKind::Dummy + | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")), + } +} + fn ident_search_pat(ident: Ident) -> (Pat, Pat) { (Pat::Sym(ident.name), Pat::Sym(ident.name)) } @@ -445,6 +574,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self)); impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 120ab2e717db8..feadc0ecf659a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -329,13 +329,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } -// Checks if arm has the form `None => None` -pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, +/// Checks if the `pat` is `None`. +pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) - ) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) +} + +/// Checks if `arm` has the form `None => None`. +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + is_none_pattern(cx, arm.pat) + && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) } /// Checks if the given `QPath` belongs to a type alias. @@ -2374,15 +2378,12 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) } } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { - let mut count = 0; - while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() { - ty = *dest_ty; - count += 1; +/// Returns the base type for HIR references and pointers. +pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty), + _ => ty, } - (ty, count) } /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 896d607fbcdd9..6e07ed9ffcc4d 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } @@ -73,7 +73,7 @@ msrv_aliases! { 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } - 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } + 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index e675291b6f3a7..638d329031231 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -215,6 +215,11 @@ impl fmt::Display for SourceText { self.as_str().fmt(f) } } +impl fmt::Debug for SourceText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} fn get_source_range(sm: &SourceMap, sp: Range) -> Option { let start = sm.lookup_byte_offset(sp.start); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 8033b74a8d25c..7530d3bc7157d 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -125,6 +125,7 @@ generate! { cycle, cyclomatic_complexity, de, + deprecated_in_future, diagnostics, disallowed_types, drain, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 3e41bce1dc4ce..e4bc3b7682942 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; +use rustc_hir::{Expr, FnDecl, LangItem, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -43,13 +43,8 @@ pub use type_certainty::expr_type_is_certain; /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`]. pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { cx.maybe_typeck_results() - .and_then(|results| { - if results.hir_owner == hir_ty.hir_id.owner { - results.node_type_opt(hir_ty.hir_id) - } else { - None - } - }) + .filter(|results| results.hir_owner == hir_ty.hir_id.owner) + .and_then(|results| results.node_type_opt(hir_ty.hir_id)) .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty)) } @@ -475,63 +470,50 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } -/// Peels off all references on the type. Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } - } - f(ty, 0, Mutability::Mut) -} - -/// Returns `true` if the given type is an `unsafe` function. -pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { +/// Returns `true` if `ty` denotes an `unsafe fn`. +pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() } -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), - _ => ty, - } -} - -/// Returns the base type for references and raw pointers, and count reference -/// depth. -pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(*ty, depth + 1), - _ => (ty, depth), - } +/// Peels off all references on the type. Returns the underlying type, the number of references +/// removed, and, if there were any such references, whether the pointer is ultimately mutable or +/// not. +pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option) { + let mut count = 0; + let mut mutbl = None; + while let ty::Ref(_, dest_ty, m) = ty.kind() { + ty = *dest_ty; + count += 1; + mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m))); } - inner(ty, 0) + (ty, count, mutbl) } -/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, -/// otherwise returns `false` -pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores +/// lifetimes. +/// +/// For example, the function would return `true` for +/// - `u32` and `u32` +/// - `[u8; N]` and `[u8; M]`, if `N=M` +/// - `Option` and `Option`, if `same_type_modulo_regions(T, U)` holds +/// - `&'a str` and `&'b str` +/// +/// and `false` for: +/// - `Result` and `Result` +pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { - same_type_and_consts(type_a, type_b) - }, - _ => true, - }) + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_modulo_regions(type_a, type_b) + }, + _ => true, + }) }, _ => a == b, } diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index ec0e59e705495..4de7b5fb5924d 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ec2f24a0a6d84..9c102de448200 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-04" +channel = "nightly-2025-09-18" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs index 2ad9bfb75dee8..1f8c4fae9b31f 100644 --- a/tests/no-profile-in-cargo-toml.rs +++ b/tests/no-profile-in-cargo-toml.rs @@ -17,6 +17,9 @@ fn no_profile_in_cargo_toml() { // keep it fast and simple. for entry in WalkDir::new(".") .into_iter() + // Do not recurse into `target` as lintcheck might put some sources (and their + // `Cargo.toml`) there. + .filter_entry(|e| e.file_name() != "target") .filter_map(Result::ok) .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) { diff --git a/tests/ui/ref_option/all/clippy.toml b/tests/ui-toml/ref_option/all/clippy.toml similarity index 100% rename from tests/ui/ref_option/all/clippy.toml rename to tests/ui-toml/ref_option/all/clippy.toml diff --git a/tests/ui/ref_option/private/clippy.toml b/tests/ui-toml/ref_option/private/clippy.toml similarity index 100% rename from tests/ui/ref_option/private/clippy.toml rename to tests/ui-toml/ref_option/private/clippy.toml diff --git a/tests/ui-toml/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed new file mode 100644 index 0000000000000..f8f097e9a75e3 --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +//~^ ref_option +fn opt_gen(a: Option<&T>) {} +//~^ ref_option +fn opt_string(a: std::option::Option<&String>) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> Option<&'static u8> { + //~^ ref_option + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~^ ref_option +fn ret_box<'a>() -> Option<&'a Box> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: Option<&String>) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: Option<&()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> Option<&String> { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> Option<&String> { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr similarity index 62% rename from tests/ui/ref_option/ref_option.all.stderr rename to tests/ui-toml/ref_option/ref_option.all.stderr index bd43c28336eb3..45ce105e03084 100644 --- a/tests/ui/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:30:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:32:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,39 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:35:5 - | -LL | fn pub_trait_opt(&self, a: &Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:37:5 - | -LL | fn pub_trait_ret(&self) -> &Option>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:51:5 + --> tests/ui-toml/ref_option/ref_option.rs:38:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -134,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:53:5 + --> tests/ui-toml/ref_option/ref_option.rs:40:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -146,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -154,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -165,5 +133,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui-toml/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed new file mode 100644 index 0000000000000..4dd14a822067b --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +//~^ ref_option +fn opt_gen(a: Option<&T>) {} +//~^ ref_option +fn opt_string(a: std::option::Option<&String>) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> Option<&'static u8> { + //~^ ref_option + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~^ ref_option +fn ret_box<'a>() -> Option<&'a Box> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: &Option, b: &Option>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> &Option { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> Option<&String> { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr similarity index 63% rename from tests/ui/ref_option/ref_option.private.stderr rename to tests/ui-toml/ref_option/ref_option.private.stderr index 88c65e429d870..a63efd60a0368 100644 --- a/tests/ui/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,23 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -98,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -109,5 +93,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui-toml/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs new file mode 100644 index 0000000000000..8397c2213d172 --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: &Option) {} +//~^ ref_option +fn opt_gen(a: &Option) {} +//~^ ref_option +fn opt_string(a: &std::option::Option) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> &'a Option { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> &'static Option { + //~^ ref_option + panic!() +} +fn mult_string(a: &Option, b: &Option>) {} +//~^ ref_option +fn ret_box<'a>() -> &'a Option> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: &Option, b: &Option>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> &Option { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> &Option { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr similarity index 85% rename from tests/ui/ref_option/ref_option_traits.all.stderr rename to tests/ui-toml/ref_option/ref_option_traits.all.stderr index 886bf2b034989..602e148be6012 100644 --- a/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:10:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:12:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr similarity index 86% rename from tests/ui/ref_option/ref_option_traits.private.stderr rename to tests/ui-toml/ref_option/ref_option_traits.private.stderr index cfab7fa5734c3..20bea400edfe6 100644 --- a/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs new file mode 100644 index 0000000000000..e1477d7f8463d --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -0,0 +1,71 @@ +//@no-rustfix: fixes are only done to traits, not the impls +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![warn(clippy::ref_option)] + +pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + //~[all]^ ref_option + fn pub_trait_ret(&self) -> &Option>; + //~[all]^ ref_option +} + +trait PrivateTrait { + fn trait_opt(&self, a: &Option); + //~^ ref_option + fn trait_ret(&self) -> &Option; + //~^ ref_option +} + +pub struct PubStruct; + +impl PubTrait for PubStruct { + fn pub_trait_opt(&self, a: &Option>) {} + fn pub_trait_ret(&self) -> &Option> { + panic!() + } +} + +struct PrivateStruct; + +impl PrivateTrait for PrivateStruct { + fn trait_opt(&self, a: &Option) {} + fn trait_ret(&self) -> &Option { + panic!() + } +} + +pub mod external { + proc_macros::external!( + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +fn main() {} diff --git a/tests/ui/as_underscore_unfixable.rs b/tests/ui/as_underscore_unfixable.rs new file mode 100644 index 0000000000000..854feca7174fb --- /dev/null +++ b/tests/ui/as_underscore_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix + +#![warn(clippy::as_underscore)] + +fn main() { + // From issue #15282 + let f = async || (); + let _: Box _> = Box::new(f) as _; + //~^ as_underscore + + let barr = || (|| ()); + let _: Box _> = Box::new(barr) as _; + //~^ as_underscore +} diff --git a/tests/ui/as_underscore_unfixable.stderr b/tests/ui/as_underscore_unfixable.stderr new file mode 100644 index 0000000000000..7385bea5c6501 --- /dev/null +++ b/tests/ui/as_underscore_unfixable.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:8:37 + | +LL | let _: Box _> = Box::new(f) as _; + | ^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + = note: `-D clippy::as-underscore` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::as_underscore)]` + +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:12:33 + | +LL | let _: Box _> = Box::new(barr) as _; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + +error: aborting due to 2 previous errors + diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 525be8216500a..fab02bf7b24e4 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -569,3 +569,16 @@ fn issue12721() { (255 % 999999u64) as u8; //~^ cast_possible_truncation } + +mod issue14150 { + #[clippy::msrv = "1.87"] + fn msrv_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } + #[clippy::msrv = "1.86"] + fn msrv_doesnt_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 1cb30d956679a..8c48855123f93 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation @@ -752,5 +752,17 @@ LL - (255 % 999999u64) as u8; LL + u8::try_from(255 % 999999u64); | -error: aborting due to 92 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:576:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` + +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:581:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ + +error: aborting due to 94 previous errors diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 785b2473c0530..bba264080b406 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -273,6 +273,28 @@ const ISSUE14763: fn(Option) = |x| { } }; +fn issue12295() { + let option = Some(()); + + if option.is_some() { + println!("{:?}", option.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", option.unwrap()); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + println!("{:?}", result.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", result.unwrap()); + //~^ panicking_unwrap + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 939b509d85c91..2007a85954137 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -322,6 +322,40 @@ LL | if x.is_some() { LL | _ = x.unwrap(); | ^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = option` +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = result` +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -332,5 +366,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default -error: aborting due to 36 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/crashes/ice-15657.rs b/tests/ui/crashes/ice-15657.rs new file mode 100644 index 0000000000000..c6f6506cd8d03 --- /dev/null +++ b/tests/ui/crashes/ice-15657.rs @@ -0,0 +1,11 @@ +//@check-pass +#![warn(clippy::len_zero)] + +pub struct S1; +pub struct S2; + +impl S1 { + pub fn len(&self) -> S2 { + S2 + } +} diff --git a/tests/ui/crashes/ice-15666.fixed b/tests/ui/crashes/ice-15666.fixed new file mode 100644 index 0000000000000..53b618765c0f0 --- /dev/null +++ b/tests/ui/crashes/ice-15666.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.rs b/tests/ui/crashes/ice-15666.rs new file mode 100644 index 0000000000000..1414b3d2035e4 --- /dev/null +++ b/tests/ui/crashes/ice-15666.rs @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.stderr b/tests/ui/crashes/ice-15666.stderr new file mode 100644 index 0000000000000..b417c09b5c65a --- /dev/null +++ b/tests/ui/crashes/ice-15666.stderr @@ -0,0 +1,16 @@ +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/crashes/ice-15666.rs:5:11 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | + = note: `-D clippy::elidable-lifetime-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::elidable_lifetime_names)]` +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/elidable_lifetime_names.fixed b/tests/ui/elidable_lifetime_names.fixed index abeee5c4cef34..a6c4cb7a36a8d 100644 --- a/tests/ui/elidable_lifetime_names.fixed +++ b/tests/ui/elidable_lifetime_names.fixed @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl T for S1<'_> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S2<'a, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S2<'_, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl T for S2<'_, '_> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S3<'a, '_, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl T for S3<'_, '_, '_> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.rs b/tests/ui/elidable_lifetime_names.rs index fae3577a8e960..e08056b2fb56b 100644 --- a/tests/ui/elidable_lifetime_names.rs +++ b/tests/ui/elidable_lifetime_names.rs @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl<'a> T for S1<'a> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> T for S2<'a, 'b> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.stderr b/tests/ui/elidable_lifetime_names.stderr index a60dfc697564e..03fe383b8f67f 100644 --- a/tests/ui/elidable_lifetime_names.stderr +++ b/tests/ui/elidable_lifetime_names.stderr @@ -158,5 +158,149 @@ LL | o: &'t str, LL ~ ) -> Content<'t, '_> { | -error: aborting due to 12 previous errors +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/elidable_lifetime_names.rs:202:15 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:226:10 + | +LL | impl<'a> T for S1<'a> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a> T for S1<'a> {} +LL + impl T for S1<'_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:234:14 + | +LL | impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TA<'a> for S2<'a, 'b> {} +LL + impl<'a> TA<'a> for S2<'a, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:238:10 + | +LL | impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TB<'b> for S2<'a, 'b> {} +LL + impl<'b> TB<'b> for S2<'_, 'b> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:242:10 + | +LL | impl<'a, 'b> T for S2<'a, 'b> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> T for S2<'a, 'b> {} +LL + impl T for S2<'_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'c + --> tests/ui/elidable_lifetime_names.rs:250:18 + | +LL | impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:254:14 + | +LL | impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:258:14 + | +LL | impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} +LL + impl<'a> TA<'a> for S3<'a, '_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:262:10 + | +LL | impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'c + --> tests/ui/elidable_lifetime_names.rs:266:10 + | +LL | impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} +LL + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:270:10 + | +LL | impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} +LL + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:274:10 + | +LL | impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} +LL + impl T for S3<'_, '_, '_> {} + | + +error: aborting due to 24 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 6944a979c05e6..107318e5323d0 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5bcc1cb26fd78..b85e8e75153a1 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/invalid_upcast_comparisons.rs b/tests/ui/invalid_upcast_comparisons.rs index 4f3194869f430..88ecca65e1509 100644 --- a/tests/ui/invalid_upcast_comparisons.rs +++ b/tests/ui/invalid_upcast_comparisons.rs @@ -133,3 +133,15 @@ fn main() { -5 == (u32 as i32); } + +fn issue15662() { + macro_rules! add_one { + ($x:expr) => { + $x + 1 + }; + } + + let x: u8 = 1; + (add_one!(x) as u32) > 300; + //~^ invalid_upcast_comparisons +} diff --git a/tests/ui/invalid_upcast_comparisons.stderr b/tests/ui/invalid_upcast_comparisons.stderr index ef36f18eabc9a..cc042a7c4b01e 100644 --- a/tests/ui/invalid_upcast_comparisons.stderr +++ b/tests/ui/invalid_upcast_comparisons.stderr @@ -163,5 +163,11 @@ error: because of the numeric bounds on `u8` prior to casting, this expression i LL | -5 >= (u8 as i32); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: because of the numeric bounds on `add_one!(x)` prior to casting, this expression is always false + --> tests/ui/invalid_upcast_comparisons.rs:145:5 + | +LL | (add_one!(x) as u32) > 300; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index b0e548f179093..4171f19469a4d 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().take(2).cloned().collect(); //~^ iter_overeager_cloned @@ -77,19 +77,19 @@ fn main() { } let _ = vec.iter().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index cedf62a6b4730..fe6aba24dd3e2 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().cloned().take(2).collect(); //~^ iter_overeager_cloned @@ -78,19 +78,19 @@ fn main() { } let _ = vec.iter().cloned().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 1616dec95b792..f234d19e4aaaa 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -25,8 +25,8 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | | | help: try: `.count()` | - = note: `-D clippy::redundant-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` + = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:21:21 diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 8c07076af4a49..a39f0c9299bd5 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -41,3 +41,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 3a5b1227331e7..049928167901a 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -53,3 +53,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 016fd89a7b7ac..132673d5164ab 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,10 +1,10 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![allow(unused)] -#![allow(deref_nullptr)] -#![allow(clippy::unnecessary_operation)] -#![allow(dropping_copy_types)] -#![allow(clippy::assign_op_pattern)] +#![expect( + dropping_copy_types, + clippy::unnecessary_operation, + clippy::unnecessary_literal_unwrap +)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -105,17 +105,17 @@ fn correct3() { } } -// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) - -unsafe fn read_char_bad(ptr: *const u8) -> char { - unsafe { char::from_u32_unchecked(*ptr.cast::()) } - //~^ multiple_unsafe_ops_per_block -} +fn issue10064() { + unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } + //~^ multiple_unsafe_ops_per_block + } -// no lint -unsafe fn read_char_good(ptr: *const u8) -> char { - let int_value = unsafe { *ptr.cast::() }; - unsafe { core::char::from_u32_unchecked(int_value) } + // no lint + unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } + } } // no lint @@ -126,42 +126,87 @@ fn issue10259() { }); } -fn _fn_ptr(x: unsafe fn()) { - unsafe { - //~^ multiple_unsafe_ops_per_block - x(); - x(); +fn issue10367() { + fn fn_ptr(x: unsafe fn()) { + unsafe { + //~^ multiple_unsafe_ops_per_block + x(); + x(); + } } -} -fn _assoc_const() { - trait X { - const X: unsafe fn(); + fn assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + //~^ multiple_unsafe_ops_per_block + T::X(); + T::X(); + } + } } - fn _f() { + + fn field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); unsafe { //~^ multiple_unsafe_ops_per_block - T::X(); - T::X(); + x.0(); + x.0(); } } } -fn _field_fn_ptr(x: unsafe fn()) { - struct X(unsafe fn()); - let x = X(x); +// await expands to an unsafe block with several operations, but this is fine. +async fn issue11312() { + async fn helper() {} + + helper().await; +} + +async fn issue13879() { + async fn foo() {} + + // no lint: nothing unsafe beyond the `await` which we ignore + unsafe { + foo().await; + } + + // no lint: only one unsafe call beyond the `await` + unsafe { + not_very_safe(); + foo().await; + } + + // lint: two unsafe calls beyond the `await` unsafe { //~^ multiple_unsafe_ops_per_block - x.0(); - x.0(); + not_very_safe(); + STATIC += 1; + foo().await; } -} -// await expands to an unsafe block with several operations, but this is fine.: #11312 -async fn await_desugaring_silent() { - async fn helper() {} + async unsafe fn foo_unchecked() {} - helper().await; + // no lint: only one unsafe call in the `await`ed expr + unsafe { + foo_unchecked().await; + } + + // lint: one unsafe call in the `await`ed expr, and one outside + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + foo_unchecked().await; + } + + // lint: two unsafe calls in the `await`ed expr + unsafe { + //~^ multiple_unsafe_ops_per_block + Some(foo_unchecked()).unwrap_unchecked().await; + } } fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 3130cecc25250..922a464c6b6ef 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -113,84 +113,147 @@ LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9 | -LL | / unsafe { +LL | / unsafe { LL | | -LL | | x(); -LL | | x(); -LL | | } - | |_____^ +LL | | x(); +LL | | x(); +LL | | } + | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13 | -LL | x(); - | ^^^ +LL | x(); + | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13 | -LL | x(); - | ^^^ +LL | x(); + | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13 + | +LL | / unsafe { +LL | | +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_____________^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17 + | +LL | T::X(); + | ^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17 + | +LL | T::X(); + | ^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9 | LL | / unsafe { LL | | -LL | | T::X(); -LL | | T::X(); +LL | | x.0(); +LL | | x.0(); LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13 | -LL | T::X(); - | ^^^^^^ +LL | x.0(); + | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13 | -LL | T::X(); - | ^^^^^^ +LL | x.0(); + | ^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5 | LL | / unsafe { LL | | -LL | | x.0(); -LL | | x.0(); +LL | | not_very_safe(); +LL | | STATIC += 1; +LL | | foo().await; LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:155:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | foo_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9 + | +LL | foo_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5 + | +LL | / unsafe { +LL | | +LL | | Some(foo_unchecked()).unwrap_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe method call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9 | -LL | x.0(); - | ^^^^^ +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14 | -LL | x.0(); - | ^^^^^ +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d571b97f51946..f5f8bb21e815b 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -517,3 +517,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2e4348ea338c0..495516c1c2e5d 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -526,3 +526,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 48d4b8ad15196..01db64a446c0f 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -498,3 +498,27 @@ fn issue15059() { () } } + +fn issue15350() { + 'bar: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + break 'bar; + } + } + + 'foo: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + loop { + //~^ never_loop + println!("This will still run"); + break 'foo; + } + } + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 54b463266a3a5..4fda06cff4ab9 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -193,6 +193,19 @@ LL | | return; LL | | } | |_____^ | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:440:9 + | +LL | return; + | ^^^^^^^ help: if you need the first element of the iterator, try writing | LL - 'outer: for v in 0..10 { @@ -297,5 +310,87 @@ LL ~ LL ~ | -error: aborting due to 24 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:503:5 + | +LL | / 'bar: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'bar: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:512:5 + | +LL | / 'foo: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'foo: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:517:13 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'foo; +LL | | } + | |_____________^ + +error: aborting due to 29 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 0f86de5646cdb..6ce067f5c2462 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -125,6 +125,16 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7aabd778f87e7..096d3aabf28db 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -152,6 +152,22 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + match r { + //~^ option_if_let_else + Ok(s) => s.to_owned(), + Err(_) => Vec::new(), + } +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + if let Ok(s) = r { s.to_owned() } + //~^ option_if_let_else + else { Vec::new() } +} + fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 2e2fe6f20492b..21a80ae038d8c 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -188,14 +188,32 @@ LL + true LL + }) | +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:157:5 + | +LL | / match r { +LL | | +LL | | Ok(s) => s.to_owned(), +LL | | Err(_) => Vec::new(), +LL | | } + | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:166:5 + | +LL | / if let Ok(s) = r { s.to_owned() } +LL | | +LL | | else { Vec::new() } + | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:13 + --> tests/ui/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:168:13 + --> tests/ui/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -217,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:197:13 + --> tests/ui/option_if_let_else.rs:213:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:202:13 + --> tests/ui/option_if_let_else.rs:218:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -245,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:242:13 + --> tests/ui/option_if_let_else.rs:258:13 | LL | let _ = match s { | _____________^ @@ -256,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:247:13 + --> tests/ui/option_if_let_else.rs:263:13 | LL | let _ = match Some(10) { | _____________^ @@ -267,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:254:13 + --> tests/ui/option_if_let_else.rs:270:13 | LL | let _ = match res { | _____________^ @@ -278,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:259:13 + --> tests/ui/option_if_let_else.rs:275:13 | LL | let _ = match res { | _____________^ @@ -289,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:264:13 + --> tests/ui/option_if_let_else.rs:280:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:282:17 + --> tests/ui/option_if_let_else.rs:298:17 | LL | let _ = match initial { | _________________^ @@ -306,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:290:17 + --> tests/ui/option_if_let_else.rs:306:17 | LL | let _ = match initial { | _________________^ @@ -317,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:314:24 + --> tests/ui/option_if_let_else.rs:330:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -329,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:321:19 + --> tests/ui/option_if_let_else.rs:337:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:372:22 + --> tests/ui/option_if_let_else.rs:388:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:378:13 + --> tests/ui/option_if_let_else.rs:394:13 | LL | let _ = match res { | _____________^ @@ -351,5 +369,5 @@ LL | | Err(_) => String::new(), LL | | }; | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 0a8525a12f5e0..7a0be97017ebc 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -77,6 +77,22 @@ fn or_fun_call() { with_default_type.unwrap_or_default(); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index b4f9b950a7fe0..724af606de9cf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -77,6 +77,22 @@ fn or_fun_call() { with_default_type.unwrap_or(u64::default()); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call @@ -86,7 +102,7 @@ fn or_fun_call() { //~^ unwrap_or_default let with_vec = Some(vec![1]); - with_vec.unwrap_or(vec![]); + with_vec.unwrap_or(Vec::new()); //~^ unwrap_or_default let without_default = Some(Foo); @@ -98,7 +114,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut map_vec = HashMap::>::new(); - map_vec.entry(42).or_insert(vec![]); + map_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let mut btree = BTreeMap::::new(); @@ -106,7 +122,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut btree_vec = BTreeMap::>::new(); - btree_vec.entry(42).or_insert(vec![]); + btree_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let stringy = Some(String::new()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 3e4df772668d7..40b25f91154dd 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:81:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:85:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:89:14 + --> tests/ui/or_fun_call.rs:105:14 | -LL | with_vec.unwrap_or(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +LL | with_vec.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:97:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:101:23 + --> tests/ui/or_fun_call.rs:117:23 | -LL | map_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | map_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:25 + --> tests/ui/or_fun_call.rs:125:25 | -LL | btree_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | btree_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:113:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:118:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:123:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:126:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:151:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:194:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:202:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:205:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:281:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:283:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:286:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:317:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:321:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:325:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:333:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:337:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:383:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:388:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:393:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:411:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:426:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:428:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:431:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:442:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:452:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:462:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 4fe9dcf46c357..42d1abeaa05a6 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).add(offset_usize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_add(offset_usize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index a1fb892733d35..6d06a6af1fa2d 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).offset(offset_usize as isize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_offset(offset_usize as isize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index dcd5e027d1829..022b3286c93b7 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -2,16 +2,51 @@ error: use of `offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:12:17 | LL | let _ = ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_with_cast)]` +help: use `add` instead + | +LL - let _ = ptr.offset(offset_usize as isize); +LL + let _ = ptr.add(offset_usize); + | error: use of `wrapping_offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = ptr.wrapping_offset(offset_usize as isize); +LL + let _ = ptr.wrapping_add(offset_usize); + | + +error: use of `offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:25:17 + | +LL | let _ = (&ptr).offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = (&ptr).offset(offset_usize as isize); +LL + let _ = (&ptr).add(offset_usize); + | + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:27:17 + | +LL | let _ = (&ptr).wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = (&ptr).wrapping_offset(offset_usize as isize); +LL + let _ = (&ptr).wrapping_add(offset_usize); + | -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8d6f5fbadca56..ac81b324c2049 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -465,3 +463,15 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + some_result?; + + some_result?; + + some_result?; + + Ok(0) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index f13eee29c113c..b5866dac6b8f6 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -561,3 +559,27 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(err.into()), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(Into::into(err)), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(<&str as Into>::into(err)), + }; + + Ok(0) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d8ce4420aeeb6..1ecd936292e55 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:9:5 + --> tests/ui/question_mark.rs:7:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:55:9 + --> tests/ui/question_mark.rs:53:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:60:9 + --> tests/ui/question_mark.rs:58:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:65:17 + --> tests/ui/question_mark.rs:63:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:72:17 + --> tests/ui/question_mark.rs:70:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:90:9 + --> tests/ui/question_mark.rs:88:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:99:9 + --> tests/ui/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:108:9 + --> tests/ui/question_mark.rs:106:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:116:26 + --> tests/ui/question_mark.rs:114:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:127:17 + --> tests/ui/question_mark.rs:125:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:149:5 + --> tests/ui/question_mark.rs:147:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:154:16 + --> tests/ui/question_mark.rs:152:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:165:5 + --> tests/ui/question_mark.rs:163:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:171:5 + --> tests/ui/question_mark.rs:169:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:198:13 + --> tests/ui/question_mark.rs:196:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:201:5 + --> tests/ui/question_mark.rs:199:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:206:16 + --> tests/ui/question_mark.rs:204:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:212:5 + --> tests/ui/question_mark.rs:210:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:304:5 + --> tests/ui/question_mark.rs:302:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:312:5 + --> tests/ui/question_mark.rs:310:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:395:13 + --> tests/ui/question_mark.rs:393:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:456:5 + --> tests/ui/question_mark.rs:454:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:471:5 + --> tests/ui/question_mark.rs:469:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:481:5 + --> tests/ui/question_mark.rs:479:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:492:5 + --> tests/ui/question_mark.rs:490:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:496:5 + --> tests/ui/question_mark.rs:494:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:500:5 + --> tests/ui/question_mark.rs:498:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:522:5 + --> tests/ui/question_mark.rs:520:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:526:15 + --> tests/ui/question_mark.rs:524:15 | LL | let val = match arg { | _______________^ @@ -276,12 +276,42 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:536:5 + --> tests/ui/question_mark.rs:534:5 | LL | / let Some(a) = *a else { LL | | return None; LL | | }; | |______^ help: replace it with: `let a = (*a)?;` -error: aborting due to 30 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:566:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err.into()), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:572:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(Into::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:578:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(<&str as Into>::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: aborting due to 33 previous errors diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 938d61b68607c..720276cb55488 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -120,3 +120,30 @@ fn allow_works(mut f: F) { } fn main() {} + +fn issue15575() -> usize { + use std::io::Read; + use std::net::TcpListener; + + let listener = TcpListener::bind("127.0.0.1:9010").unwrap(); + let mut stream_and_addr = listener.accept().unwrap(); + let mut buf = Vec::with_capacity(32); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + //~^ read_zero_byte_vec + + use std::fs::File; + let mut f = File::open("foo.txt").unwrap(); + let mut data = Vec::with_capacity(100); + f.read(&mut data).unwrap() + //~^ read_zero_byte_vec +} diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 8f255bc87ab82..8dd74592e4c15 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -2,16 +2,27 @@ error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:22:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` +help: try + | +LL ~ data.resize(20, 0); +LL ~ f.read_exact(&mut data).unwrap(); + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:28:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data2.resize(cap, 0); +LL ~ f.read_exact(&mut data2)?; + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:33:5 @@ -67,5 +78,53 @@ error: reading zero byte data to `Vec` LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:131:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(32, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:136:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:141:32 + | +LL | let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:147:5 + | +LL | f.read(&mut data).unwrap() + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data.resize(100, 0); +LL ~ f.read(&mut data).unwrap() + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/ref_option/ref_option.all.fixed b/tests/ui/ref_option/ref_option.all.fixed deleted file mode 100644 index 4159e916f1995..0000000000000 --- a/tests/ui/ref_option/ref_option.all.fixed +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: Option<&u8>) {} -//~^ ref_option -fn opt_gen(a: Option<&T>) {} -//~^ ref_option -fn opt_string(a: std::option::Option<&String>) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { - //~^ ref_option - panic!() -} -fn ret_string_static() -> Option<&'static u8> { - //~^ ref_option - panic!() -} -fn mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~^ ref_option -fn ret_box<'a>() -> Option<&'a Box> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: Option<&String>) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: Option<&Vec>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> Option<&Vec>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: Option<&()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> Option<&String> { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: Option<&()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> Option<&String> { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option.private.fixed b/tests/ui/ref_option/ref_option.private.fixed deleted file mode 100644 index 3b158befb926e..0000000000000 --- a/tests/ui/ref_option/ref_option.private.fixed +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: Option<&u8>) {} -//~^ ref_option -fn opt_gen(a: Option<&T>) {} -//~^ ref_option -fn opt_string(a: std::option::Option<&String>) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { - //~^ ref_option - panic!() -} -fn ret_string_static() -> Option<&'static u8> { - //~^ ref_option - panic!() -} -fn mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~^ ref_option -fn ret_box<'a>() -> Option<&'a Box> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: &Option) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: &Option, b: &Option>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: &Option<()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> &Option { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: Option<&()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> Option<&String> { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option.rs b/tests/ui/ref_option/ref_option.rs deleted file mode 100644 index 35cd94174f8bc..0000000000000 --- a/tests/ui/ref_option/ref_option.rs +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: &Option) {} -//~^ ref_option -fn opt_gen(a: &Option) {} -//~^ ref_option -fn opt_string(a: &std::option::Option) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> &'a Option { - //~^ ref_option - panic!() -} -fn ret_string_static() -> &'static Option { - //~^ ref_option - panic!() -} -fn mult_string(a: &Option, b: &Option>) {} -//~^ ref_option -fn ret_box<'a>() -> &'a Option> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: &Option) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: &Option, b: &Option>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: &Option<()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> &Option { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: &Option<()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> &Option { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option_traits.rs b/tests/ui/ref_option/ref_option_traits.rs deleted file mode 100644 index 4c773e84f8da8..0000000000000 --- a/tests/ui/ref_option/ref_option_traits.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@no-rustfix: fixes are only done to traits, not the impls -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![warn(clippy::ref_option)] - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubTrait for PubStruct { - fn pub_trait_opt(&self, a: &Option>) {} - fn pub_trait_ret(&self) -> &Option> { - panic!() - } -} - -struct PrivateStruct; - -impl PrivateTrait for PrivateStruct { - fn trait_opt(&self, a: &Option) {} - fn trait_ret(&self) -> &Option { - panic!() - } -} - -fn main() {} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.fixed b/tests/ui/rest_pat_in_fully_bound_structs.fixed new file mode 100644 index 0000000000000..ac200b3b19b74 --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.fixed @@ -0,0 +1,61 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] +#![allow(clippy::struct_field_names)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } +} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.stderr b/tests/ui/rest_pat_in_fully_bound_structs.stderr index d048933ddb7bc..8a2da302b9ef2 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.stderr +++ b/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -4,9 +4,13 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]` +help: consider removing `..` from this binding + | +LL - A { a: 5, b: 42, c: "", .. } => {}, // Lint +LL + A { a: 5, b: 42, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:25:9 @@ -14,7 +18,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:32:9 @@ -22,7 +30,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: aborting due to 3 previous errors diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7308e78aae263..468f0a5b1e472 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 467bf4d779f2a..101374af26471 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index 23433f4e7ef90..2046dd1c36be7 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -1,5 +1,5 @@ error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:38:5 + --> tests/ui/semicolon_inside_block.rs:39:5 | LL | { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:40:5 + --> tests/ui/semicolon_inside_block.rs:41:5 | LL | unsafe { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + unsafe { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:49:5 + --> tests/ui/semicolon_inside_block.rs:50:5 | LL | / { LL | | @@ -41,7 +41,7 @@ LL ~ } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:63:5 + --> tests/ui/semicolon_inside_block.rs:64:5 | LL | { m!(()) }; | ^^^^^^^^^^^ diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr index d5533877b4515..3ceb501463b77 100644 --- a/tests/ui/track-diagnostics-clippy.stderr +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns/let_and_return.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 61e3ac2fe88e3..c130575df9607 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = &*p; //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => &*x.cast::<&u32>(), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + let _: &[u32] = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = &*(ptr as *const [i32]); + //~^ transmute_ptr_to_ref + let _: &[i32] = &*ptr; + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + let _: &[&str] = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index 48e2f527b554c..f79d54234a2c9 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = std::mem::transmute(p); //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => std::mem::transmute(x), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = core::mem::transmute::<_, &[u32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[u32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = core::mem::transmute::<_, &[i32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[i32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&str] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 7685c345c8619..3f404d295fef0 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -43,13 +43,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, u8>`) --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, &u8>`) --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; @@ -133,5 +133,53 @@ error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32 LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` -error: aborting due to 22 previous errors +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:113:17 + | +LL | let _ = core::mem::transmute::<_, &[u32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:115:25 + | +LL | let _: &[u32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:119:17 + | +LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:121:27 + | +LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:125:17 + | +LL | let _ = core::mem::transmute::<_, &[i32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:127:25 + | +LL | let _: &[i32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:131:17 + | +LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:133:26 + | +LL | let _: &[&str] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: aborting due to 30 previous errors diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index cccb6bffabb71..075e31d202b06 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -530,8 +530,8 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { - Self::new() - //~^ use_self + S2::new() + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 09288677aa715..6fbba0bbc550b 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -531,7 +531,7 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { S2::new() - //~^ use_self + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 781327696ac19..5f65c53ea25c3 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -169,12 +169,6 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> tests/ui/use_self.rs:533:13 - | -LL | S2::new() - | ^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> tests/ui/use_self.rs:571:17 | @@ -259,5 +253,5 @@ error: unnecessary structure name repetition LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 43 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index be4fb55ddfb86..15070dd9c2ca6 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 5a1bcf97a5b46..3f530de7fd8e3 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} From 5d1619b3ec4b843715c795789b7173e48066db5a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 17:21:54 +0200 Subject: [PATCH 0566/1900] Update Cargo.lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d39cfefea0c77..4d1c6d4c46b23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,7 +568,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clippy" -version = "0.1.91" +version = "0.1.92" dependencies = [ "anstream", "askama", @@ -595,7 +595,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" dependencies = [ "clippy_utils", "itertools", @@ -618,7 +618,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -649,7 +649,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" dependencies = [ "arrayvec", "itertools", @@ -1051,7 +1051,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" [[package]] name = "derive-where" From c6b4f6f33f6e78d575a477a3398af44e47b18630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 18 Sep 2025 19:03:35 +0300 Subject: [PATCH 0567/1900] Bump rustc crates again --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/display.rs | 6 +-- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/lower_nextsolver.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 2 +- .../crates/hir-ty/src/next_solver/mapping.rs | 6 +-- .../crates/hir-ty/src/next_solver/ty.rs | 23 ++++------ .../crates/hir-ty/src/next_solver/util.rs | 2 +- 11 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 9475391acdb7f..be21cfd9c24fe 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa338fe027a8915009ca4a5a1cb7dde5fb4bc4170a928cb9462fda9d2ec52cec" +checksum = "dd3df655461690c1554bc0e355f19495eef6cd729dc29f01bd07cb7cd2a0990a" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8468ef77e5359b3a51e327406f29ca2283a4feef93d3ba04f6740b274636922" +checksum = "10f55c57676d67dba036171d23f7941a9c3b884181c5e93d3bd5fa599f8f129b" [[package]] name = "ra-ap-rustc_hashes" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300bc3264ccc1e7a5b3f065023a02e612774206d8ad685b3b05c2e4e317f8daa" +checksum = "feec9ffe50d93b8f17770b49d4cbf46280e074a105c7c9325c8d2171cf395b76" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eaa4a3ff61302e45c17ee72e067a39179081c19a12aa03192975a095f5d4e4b" +checksum = "a73bab902bcdeceac4a9630ed732d19f6b7189cbe8e828a94156a780bd8e1196" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7af0d51ee6bd5280be8e2eb7e9ac5cd9fc87af7a99f50cdb1316a8779c15ab" +checksum = "88c7906b654b9c4188aee3fde82a7449bac6e156a5d86ad923bae05bd5040690" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e7df9bf702c855de7bea5e3c14b96f0728d4712edb663b0f4b183622341fc" +checksum = "acec3893f60dca2a37f9e838c08f3f3c8e012ce36194c2b84da96781e794e359" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15768080a276088a4a6af1e08a4ca622c57d5b4845ce5329dbbd71a2e025eecb" +checksum = "c4626e22dc21062bdac1a8340b497365e1983a69cc271d7d052a62348b88bd7f" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f0c54b200c47768eaf142b1c829da9be1a3331a5defa4ac60bad4996f474e9" +checksum = "47a95720d31edf45a8ccde190c14954425af0d53c9b6d4702fdd905b3951a2e1" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a078fbbefda17d8d5d2c9d6b5a1f9ee1e23fae5f057e74784f6b95c189b0b048" +checksum = "0c1ebf6272bf11f0ba3b909e3405f547de32ea19d745b8fef11f1fb643b72fcd" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644e980122cdb7f2d7e175f33224dc6df414e8cf3e5dfbba9047e63336d9737a" +checksum = "a9aa8f1344940e097514403c53bb5a1e4f5c3986da888461bc651373d195090b" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827d242d444cea86d9a64b5ce99db1462c9d43c7c6226d44ec2bc621b7c253c0" +checksum = "30c65f1a5da2f3c39d72a56687694568abb203b3737a2321090e112da04958b3" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 94ec1a07438f4..e81c4088122bf 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.129", default-features = false } -ra-ap-rustc_parse_format = { version = "0.129", default-features = false } -ra-ap-rustc_index = { version = "0.129", default-features = false } -ra-ap-rustc_abi = { version = "0.129", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.129", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.129", default-features = false } -ra-ap-rustc_type_ir = { version = "0.129", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.129", default-features = false } +ra-ap-rustc_lexer = { version = "0.130", default-features = false } +ra-ap-rustc_parse_format = { version = "0.130", default-features = false } +ra-ap-rustc_index = { version = "0.130", default-features = false } +ra-ap-rustc_abi = { version = "0.130", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.130", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.130", default-features = false } +ra-ap-rustc_type_ir = { version = "0.130", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.130", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0a514f389b471..519e4b59237f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -897,7 +897,7 @@ fn render_const_scalar_inner( } f.write_str("]") } - TyKind::Dynamic(_, _, _) => { + TyKind::Dynamic(_, _) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); let Ok(t) = memory_map.vtable_ty(ty_id) else { @@ -1064,7 +1064,7 @@ fn render_const_scalar_inner( | TyKind::Bound(_, _) | TyKind::Infer(_) => f.write_str(""), // The below arms are unreachable, since we handled them in ref case. - TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _, _) => f.write_str(""), + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => f.write_str(""), } } @@ -1213,7 +1213,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { }) }; let (preds_to_print, has_impl_fn_pred) = match t.kind() { - TyKind::Dynamic(bounds, region, _) => { + TyKind::Dynamic(bounds, region) => { let render_lifetime = f.render_region(region); ( bounds.len() + render_lifetime as usize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 88b10e87e531e..7930d8b0ed68f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -611,7 +611,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { | TyKind::Slice(_) | TyKind::FnDef(_, _) | TyKind::FnPtr(_, _) - | TyKind::Dynamic(_, _, _) + | TyKind::Dynamic(_, _) | TyKind::Closure(_, _) | TyKind::CoroutineClosure(_, _) | TyKind::Coroutine(_, _) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 2292e5c99413e..b8e0599dba289 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -790,7 +790,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }, None => Region::new_static(self.interner), }; - Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn) + Ty::new_dynamic(self.interner, bounds, region) } else { // FIXME: report error // (additional non-auto traits, associated type rebound, or no resolved trait) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index a4427517a10b8..7fa3d31fe5fdc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -164,7 +164,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, TyKind::Foreign(def) => TyFingerprint::ForeignType(crate::to_foreign_def_id(def.0)), - TyKind::Dynamic(bounds, _, _) => { + TyKind::Dynamic(bounds, _) => { let trait_ref = bounds .as_slice() .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 6f950b8022c98..3e658cb93ed8a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2446,7 +2446,7 @@ impl<'db> Evaluator<'db> { | TyKind::Foreign(_) | TyKind::Error(_) | TyKind::Placeholder(_) - | TyKind::Dynamic(_, _, _) + | TyKind::Dynamic(_, _) | TyKind::Alias(_, _) | TyKind::Bound(_, _) | TyKind::Infer(_) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs index 6cd9e55acf0e7..b49fac18c5dd3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -1146,7 +1146,7 @@ mod wf { } TyKind::UnsafeBinder(ty) => {} - TyKind::Dynamic(data, r, _) => { + TyKind::Dynamic(data, r) => { // WfObject // // Here, we defer WF checking due to higher-ranked diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index d8a86ea083e08..b24b996b0927c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -370,8 +370,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }), ); let region = dyn_ty.lifetime.to_nextsolver(interner); - let kind = rustc_type_ir::DynKind::Dyn; - rustc_type_ir::TyKind::Dynamic(bounds, region, kind) + rustc_type_ir::TyKind::Dynamic(bounds, region) } chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { chalk_ir::AliasTy::Projection(projection_ty) => { @@ -1445,8 +1444,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Function(fnptr) } - rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { - assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + rustc_type_ir::TyKind::Dynamic(preds, region) => { let self_ty = Ty::new_bound( interner, DebruijnIndex::from_u32(1), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index e6c444d0d6bed..70139e8666948 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -178,7 +178,7 @@ impl<'db> Ty<'db> { | TyKind::Never | TyKind::Error(_) => true, - TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness { + TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _) => match sizedness { SizedTraitKind::Sized => false, SizedTraitKind::MetaSized => true, }, @@ -421,7 +421,7 @@ impl<'db> TypeSuperVisitable> for Ty<'db> { } TyKind::Slice(typ) => typ.visit_with(visitor), TyKind::Adt(_, args) => args.visit_with(visitor), - TyKind::Dynamic(ref trait_ty, ref reg, _) => { + TyKind::Dynamic(ref trait_ty, ref reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) } @@ -486,11 +486,9 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { } TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?), TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?), - TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( - trait_ty.try_fold_with(folder)?, - region.try_fold_with(folder)?, - representation, - ), + TyKind::Dynamic(trait_ty, region) => { + TyKind::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) + } TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?), TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?), TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr), @@ -537,11 +535,9 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)), TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)), TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)), - TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( - trait_ty.fold_with(folder), - region.fold_with(folder), - representation, - ), + TyKind::Dynamic(trait_ty, region) => { + TyKind::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) + } TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)), TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)), TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr), @@ -676,9 +672,8 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { interner: DbInterner<'db>, preds: as rustc_type_ir::Interner>::BoundExistentialPredicates, region: as rustc_type_ir::Interner>::Region, - kind: rustc_type_ir::DynKind, ) -> Self { - Ty::new(interner, TyKind::Dynamic(preds, region, kind)) + Ty::new(interner, TyKind::Dynamic(preds, region)) } fn new_coroutine( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index a50f20a565ee2..a7f9817f9c08e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -461,7 +461,7 @@ pub fn sizedness_constraint_for_ty<'db>( | CoroutineWitness(..) | Never => None, // these are never sized - Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness { + Str | Slice(..) | Dynamic(_, _) => match sizedness { // Never `Sized` SizedTraitKind::Sized => Some(ty), // Always `MetaSized` From 7f55f5761cd44b8ad49efb976ae650602fc2d42a Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 12 Sep 2025 20:29:23 +0000 Subject: [PATCH 0568/1900] Allow windows resource compiler to be overridden It is now required to provide a resource compiler on windows when compiling rust. This allows toolchain builders to explicitly provide a path to an alternative, such as llvm-rc, instead of the one that's provided by the Windows SDK. --- bootstrap.example.toml | 3 +++ compiler/rustc_windows_rc/src/lib.rs | 7 +++++-- src/bootstrap/src/core/builder/cargo.rs | 5 +++++ src/bootstrap/src/core/config/config.rs | 3 +++ src/bootstrap/src/core/config/toml/build.rs | 1 + src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 51529751dd58f..0cd571134ef15 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -325,6 +325,9 @@ # Defaults to the Python interpreter used to execute x.py. #build.python = "python" +# The path to (or name of) the resource compiler executable to use on Windows. +#build.windows-rc = "rc.exe" + # The path to the REUSE executable to use. Note that REUSE is not required in # most cases, as our tooling relies on a cached (and shrunk) copy of the # REUSE output present in the git repository and in our source tarballs. diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs index caa5e5ef27656..5e95557501ea2 100644 --- a/compiler/rustc_windows_rc/src/lib.rs +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -35,8 +35,11 @@ pub fn compile_windows_resource_file( resources_dir.push("resources"); fs::create_dir_all(&resources_dir).unwrap(); - let resource_compiler = - find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") { + path.into() + } else { + find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe") + }; let rc_path = resources_dir.join(file_stem.with_extension("rc")); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 6121bf7d9cd62..ee2bb710674c0 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1227,6 +1227,11 @@ impl Builder<'_> { rustflags.arg("-Zehcont-guard"); } + // Optionally override the rc.exe when compiling rustc on Windows. + if let Some(windows_rc) = &self.config.windows_rc { + cargo.env("RUSTC_WINDOWS_RC", windows_rc); + } + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs // This replaces spaces with tabs because RUSTDOCFLAGS does not // support arguments with regular spaces. Hopefully someday Cargo will diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index efb7ad9169971..a97399b3d4ae9 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -273,6 +273,7 @@ pub struct Config { pub gdb: Option, pub lldb: Option, pub python: Option, + pub windows_rc: Option, pub reuse: Option, pub cargo_native_static: bool, pub configure_args: Vec, @@ -450,6 +451,7 @@ impl Config { nodejs: build_nodejs, npm: build_npm, python: build_python, + windows_rc: build_windows_rc, reuse: build_reuse, locked_deps: build_locked_deps, vendor: build_vendor, @@ -1342,6 +1344,7 @@ impl Config { .unwrap_or(rust_debug == Some(true)), vendor, verbose_tests, + windows_rc: build_windows_rc.map(PathBuf::from), // tidy-alphabetical-end } } diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 25c19f1070a39..a9d4d3961c9b7 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -37,6 +37,7 @@ define_config! { nodejs: Option = "nodejs", npm: Option = "npm", python: Option = "python", + windows_rc: Option = "windows-rc", reuse: Option = "reuse", locked_deps: Option = "locked-deps", vendor: Option = "vendor", diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 2c48cebd2df05..6b187578c31ec 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -551,4 +551,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "There is now a bootstrap option called `rust.parallel-frontend-threads`, which can be used to set the number of threads for the compiler frontend used during compilation of Rust code.", }, + ChangeInfo { + change_id: 146663, + severity: ChangeSeverity::Info, + summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.", + }, ]; From 63a55083aa03e94dc380af31d51e3ffd944cb2b4 Mon Sep 17 00:00:00 2001 From: pommicket Date: Mon, 12 May 2025 10:32:21 -0400 Subject: [PATCH 0569/1900] Add clippy::self_only_used_in_recursion lint and use it instead of clippy::only_used_in_recursion when the parameter in question is self. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/only_used_in_recursion.rs | 153 ++++++++++++++++----- tests/ui/only_used_in_recursion.rs | 3 +- tests/ui/only_used_in_recursion.stderr | 70 +++++----- 5 files changed, 162 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be02143722..f4e4cbfe37509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6597,6 +6597,7 @@ Released 2018-09-13 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files +[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1b19a8851edd4..c67026e6f266d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -575,6 +575,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO, crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a42763172f565..bd2fb9d1aa72a 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -24,6 +24,33 @@ declare_clippy_lint! { /// the calculations have no side-effects (function calls or mutating dereference) /// and the assigned variables are also only in recursion, it is useless. /// + /// ### Example + /// ```no_run + /// fn f(a: usize, b: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1, b + 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1, 1)); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(a: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1)); + /// # } + /// ``` + /// /// ### Known problems /// Too many code paths in the linting code are currently untested and prone to produce false /// positives or are prone to have performance implications. @@ -51,39 +78,90 @@ declare_clippy_lint! { /// - struct pattern binding /// /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.61.0"] + pub ONLY_USED_IN_RECURSION, + complexity, + "arguments that is only used in recursion can be removed" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `self` receiver that is only used in recursion with no side-effects. + /// + /// ### Why is this bad? + /// + /// It may be possible to remove the `self` argument, allowing the function to be + /// used without an object of type `Self`. /// /// ### Example /// ```no_run - /// fn f(a: usize, b: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1, b + 1) + /// struct Foo; + /// impl Foo { + /// fn f(&self, n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * self.f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1, 1)); + /// # print!("{}", Foo.f(10)); /// # } /// ``` /// Use instead: /// ```no_run - /// fn f(a: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1) + /// struct Foo; + /// impl Foo { + /// fn f(n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * Self::f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1)); + /// # print!("{}", Foo::f(10)); /// # } /// ``` - #[clippy::version = "1.61.0"] - pub ONLY_USED_IN_RECURSION, - complexity, - "arguments that is only used in recursion can be removed" + /// + /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// + /// In some cases, this would not catch all useless arguments. + /// + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn foo(&self, a: usize) -> usize { + /// let f = |x| x; + /// + /// if a == 0 { + /// 1 + /// } else { + /// f(self).foo(a) + /// } + /// } + /// } + /// ``` + /// + /// For example, here `self` is only used in recursion, but the lint would not catch it. + /// + /// List of some examples that can not be caught: + /// - binary operation of non-primitive types + /// - closure usage + /// - some `break` relative operations + /// - struct pattern binding + /// + /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.92.0"] + pub SELF_ONLY_USED_IN_RECURSION, + pedantic, + "self receiver only used to recursively call method can be removed" } -impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION, SELF_ONLY_USED_IN_RECURSION]); #[derive(Clone, Copy)] enum FnKind { @@ -355,26 +433,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { self.params.flag_for_linting(); for param in &self.params.params { if param.apply_lint.get() { - span_lint_and_then( - cx, - ONLY_USED_IN_RECURSION, - param.ident.span, - "parameter is only used in recursion", - |diag| { - if param.ident.name != kw::SelfLower { + if param.ident.name == kw::SelfLower { + span_lint_and_then( + cx, + SELF_ONLY_USED_IN_RECURSION, + param.ident.span, + "`self` is only used in recursion", + |diag| { + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "`self` used here", + ); + }, + ); + } else { + span_lint_and_then( + cx, + ONLY_USED_IN_RECURSION, + param.ident.span, + "parameter is only used in recursion", + |diag| { diag.span_suggestion( param.ident.span, "if this is intentional, prefix it with an underscore", format!("_{}", param.ident.name), Applicability::MaybeIncorrect, ); - } - diag.span_note( - param.uses.iter().map(|x| x.span).collect::>(), - "parameter used here", - ); - }, - ); + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "parameter used here", + ); + }, + ); + } } } self.params.clear(); diff --git a/tests/ui/only_used_in_recursion.rs b/tests/ui/only_used_in_recursion.rs index 7d6075ba9ea47..d58635969238b 100644 --- a/tests/ui/only_used_in_recursion.rs +++ b/tests/ui/only_used_in_recursion.rs @@ -1,4 +1,5 @@ #![warn(clippy::only_used_in_recursion)] +#![warn(clippy::self_only_used_in_recursion)] //@no-rustfix fn _simple(x: u32) -> u32 { x @@ -74,7 +75,7 @@ impl A { } fn _method_self(&self, flag: usize, a: usize) -> usize { - //~^ only_used_in_recursion + //~^ self_only_used_in_recursion //~| only_used_in_recursion if flag == 0 { 0 } else { self._method_self(flag - 1, a) } diff --git a/tests/ui/only_used_in_recursion.stderr b/tests/ui/only_used_in_recursion.stderr index ca08319e11200..5d1e3e9c50fd9 100644 --- a/tests/ui/only_used_in_recursion.stderr +++ b/tests/ui/only_used_in_recursion.stderr @@ -1,11 +1,11 @@ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:11:27 + --> tests/ui/only_used_in_recursion.rs:12:27 | LL | fn _one_unused(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:14:53 + --> tests/ui/only_used_in_recursion.rs:15:53 | LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } | ^ @@ -13,181 +13,183 @@ LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:27 + --> tests/ui/only_used_in_recursion.rs:18:27 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:53 + --> tests/ui/only_used_in_recursion.rs:22:53 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:35 + --> tests/ui/only_used_in_recursion.rs:18:35 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:56 + --> tests/ui/only_used_in_recursion.rs:22:56 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:24:26 + --> tests/ui/only_used_in_recursion.rs:25:26 | LL | fn _with_calc(flag: u32, a: i64) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:30:32 + --> tests/ui/only_used_in_recursion.rs:31:32 | LL | _with_calc(flag - 1, (-a + 10) * 5) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:33 + --> tests/ui/only_used_in_recursion.rs:40:33 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:38 + --> tests/ui/only_used_in_recursion.rs:47:38 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:41 + --> tests/ui/only_used_in_recursion.rs:40:41 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:45 + --> tests/ui/only_used_in_recursion.rs:47:45 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:35 + --> tests/ui/only_used_in_recursion.rs:51:35 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:39 + --> tests/ui/only_used_in_recursion.rs:58:39 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:43 + --> tests/ui/only_used_in_recursion.rs:51:43 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:43 + --> tests/ui/only_used_in_recursion.rs:58:43 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:61:30 + --> tests/ui/only_used_in_recursion.rs:62:30 | LL | fn _not_primitive(flag: u32, b: String) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:64:56 + --> tests/ui/only_used_in_recursion.rs:65:56 | LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:70:29 + --> tests/ui/only_used_in_recursion.rs:71:29 | LL | fn _method(flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:73:59 + --> tests/ui/only_used_in_recursion.rs:74:59 | LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) } | ^ -error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:22 +error: `self` is only used in recursion + --> tests/ui/only_used_in_recursion.rs:77:22 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^^^^ | -note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:35 +note: `self` used here + --> tests/ui/only_used_in_recursion.rs:81:35 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^^^^ + = note: `-D clippy::self-only-used-in-recursion` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:41 + --> tests/ui/only_used_in_recursion.rs:77:41 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:63 + --> tests/ui/only_used_in_recursion.rs:81:63 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:90:26 + --> tests/ui/only_used_in_recursion.rs:91:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:93:58 + --> tests/ui/only_used_in_recursion.rs:94:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:96:38 + --> tests/ui/only_used_in_recursion.rs:97:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:99:62 + --> tests/ui/only_used_in_recursion.rs:100:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:124:26 + --> tests/ui/only_used_in_recursion.rs:125:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:127:58 + --> tests/ui/only_used_in_recursion.rs:128:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:130:38 + --> tests/ui/only_used_in_recursion.rs:131:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:133:62 + --> tests/ui/only_used_in_recursion.rs:134:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ From 60f0407ba313fbea163ee4fe21a8a71ba633ecc6 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 03:04:14 +0900 Subject: [PATCH 0570/1900] fix: Fix `indexmap` with `in-rust-tree` --- src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index deee8dd1ff06e..073a02908deeb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -45,9 +45,4 @@ pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; pub type TypeError<'db> = rustc_type_ir::error::TypeError>; pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult>; - -#[cfg(feature = "in-rust-tree")] -use rustc_data_structure::sorted_map::index_map as indexmap; - -pub type FxIndexMap = - indexmap::IndexMap>; +pub type FxIndexMap = rustc_type_ir::data_structures::IndexMap; From 3ba8b8e0e5284ad3c33eb6a1490a6adcea4c105d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 03:26:03 +0900 Subject: [PATCH 0571/1900] minor: Yet another rustc crates bump --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 4 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 22 ++++++---- .../hir-ty/src/next_solver/infer/context.rs | 9 ++++ .../hir-ty/src/next_solver/infer/select.rs | 8 ++-- .../crates/hir-ty/src/next_solver/inspect.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 4 +- 9 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index be21cfd9c24fe..97c4f06dd5960 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd3df655461690c1554bc0e355f19495eef6cd729dc29f01bd07cb7cd2a0990a" +checksum = "016c05852e89655395fbf7d5e729e31cd58b2690480b3a8468eb2b38c91f3756" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f55c57676d67dba036171d23f7941a9c3b884181c5e93d3bd5fa599f8f129b" +checksum = "c5bff48bd0a26f17a4e2e8610bc393296c3d002e221f5c6c4d2875e6a64a3b4b" [[package]] name = "ra-ap-rustc_hashes" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feec9ffe50d93b8f17770b49d4cbf46280e074a105c7c9325c8d2171cf395b76" +checksum = "5d15e7571f6f31f6112fd2fcbc3450a6ef477cc6bfe51643a2b110436a7455f0" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a73bab902bcdeceac4a9630ed732d19f6b7189cbe8e828a94156a780bd8e1196" +checksum = "60a30f0a15682f1194e5812cc3544266f3494ca8fb3c5033e8b9c08f4f8b8c6d" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c7906b654b9c4188aee3fde82a7449bac6e156a5d86ad923bae05bd5040690" +checksum = "e2f82c9176b964591e1657a9f0fae2850525542d39075c49c2459afc291f4804" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acec3893f60dca2a37f9e838c08f3f3c8e012ce36194c2b84da96781e794e359" +checksum = "585c71ff7da5ca1e8a0c65d5e7cec7ab2a7e9e45b6859543bd2d0be21bee631c" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4626e22dc21062bdac1a8340b497365e1983a69cc271d7d052a62348b88bd7f" +checksum = "839f521a6cd97c71b2b6681604ace38a9c6737f7a223d072fa8f909879f9dc4b" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a95720d31edf45a8ccde190c14954425af0d53c9b6d4702fdd905b3951a2e1" +checksum = "83e7872a4fa0620937b60fc6270aa8c31864cb324357553e2be5b2bdc25fa89a" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1ebf6272bf11f0ba3b909e3405f547de32ea19d745b8fef11f1fb643b72fcd" +checksum = "4ecd7f9b960c8cf1e9d02a25297f52a520134132164cfedd3e69fee4f294d41c" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aa8f1344940e097514403c53bb5a1e4f5c3986da888461bc651373d195090b" +checksum = "9494498f8f7c57a5b4ea4e35a6c554cca8d5323adb534d13dc6cd117a7e00e4d" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c65f1a5da2f3c39d72a56687694568abb203b3737a2321090e112da04958b3" +checksum = "28ed893760b86529080af59438c28dfa9ca976b821cfd0a3b0eb9591932847e6" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e81c4088122bf..513af11a87aad 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.130", default-features = false } -ra-ap-rustc_parse_format = { version = "0.130", default-features = false } -ra-ap-rustc_index = { version = "0.130", default-features = false } -ra-ap-rustc_abi = { version = "0.130", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.130", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.130", default-features = false } -ra-ap-rustc_type_ir = { version = "0.130", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.130", default-features = false } +ra-ap-rustc_lexer = { version = "0.131", default-features = false } +ra-ap-rustc_parse_format = { version = "0.131", default-features = false } +ra-ap-rustc_index = { version = "0.131", default-features = false } +ra-ap-rustc_abi = { version = "0.131", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.131", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.131", default-features = false } +ra-ap-rustc_type_ir = { version = "0.131", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.131", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 77eaf83eec73d..1687857ae1ac2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -910,7 +910,7 @@ impl<'db> InferenceTable<'db> { match result { Ok((_, Certainty::Yes)) => {} Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe(_))) => { + Ok((_, Certainty::Maybe { .. })) => { self.fulfillment_cx.register_predicate_obligation( &self.infer_ctxt, Obligation::new( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index a8183ab422792..34dff37972e7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -182,7 +182,7 @@ impl<'db> FulfillmentCtxt<'db> { if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -211,7 +211,7 @@ impl<'db> FulfillmentCtxt<'db> { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs index b49fac18c5dd3..ab4a229fbc05f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -153,15 +153,17 @@ pub(super) fn fulfillment_error_for_stalled<'db>( Span::dummy(), None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -314,7 +316,8 @@ impl<'db> BestObligation<'db> { .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal()); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -456,7 +459,8 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { let interner = goal.infcx().interner; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 45ce7e6f6cc75..5aa5ad14af551 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -321,4 +321,13 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { self.sub_unify_ty_vids_raw(a, b); } + + fn opaques_with_sub_unified_hidden_type( + &self, + _ty: TyVid, + ) -> Vec> { + // FIXME: I guess we are okay without this for now since currently r-a lacks of + // detailed checks over opaque types. Might need to implement this in future. + vec![] + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index d656d94f4f91e..4f111fa662668 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -208,7 +208,7 @@ impl<'db> ProofTreeVisitor<'db> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -241,7 +241,7 @@ fn candidate_should_be_dropped_in_favor_of<'db>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -284,13 +284,13 @@ fn candidate_should_be_dropped_in_favor_of<'db>( } fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => Vec::new(), - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals() .into_iter() .map(|nested| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index cab3c69118f64..f9d0aa99cf4f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -350,7 +350,7 @@ impl<'a, 'db> InspectGoal<'a, 'db> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert!(matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) )); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index d73d7da4b9b98..8b6836c0e90bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -277,7 +277,7 @@ pub fn next_trait_solve( Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), ), - Ok((_, Certainty::Maybe(_), args)) => { + Ok((_, Certainty::Maybe { .. }, args)) => { let subst = convert_canonical_args_for_result( DbInterner::new_with(db, Some(krate), block), args, @@ -316,7 +316,7 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( convert_canonical_args_for_result(infer_ctxt.interner, args), ), - Ok((_, Certainty::Maybe(_), args)) => { + Ok((_, Certainty::Maybe { .. }, args)) => { let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); NextTraitSolveResult::Uncertain(chalk_ir::Canonical { binders: subst.binders, From 00bfe9ce6ed3d21b39a40309974dda5c19dc6fb0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 20:41:06 +0200 Subject: [PATCH 0572/1900] tweak genmc error report note --- src/tools/miri/src/concurrency/genmc/run.rs | 10 +++++----- .../tests/genmc/fail/data_race/mpu2_rels_rlx.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rel_rlx.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rlx_acq.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rlx_rlx.stderr | 3 ++- src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr | 3 ++- .../tests/genmc/fail/loom/store_buffering.genmc.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.release4.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr | 3 ++- 10 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index bc2f5f7b79ec4..33c5b6b0a005f 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -70,7 +70,7 @@ fn run_genmc_mode_impl<'tcx>( // Execute the program until completion to get the return value, or return if an error happens: let Some(return_code) = eval_entry(genmc_ctx.clone()) else { - genmc_ctx.print_genmc_output(genmc_config); + genmc_ctx.print_genmc_output(genmc_config, tcx); return None; }; @@ -97,7 +97,7 @@ fn run_genmc_mode_impl<'tcx>( // Since we don't have any span information for the error at this point, // we just print GenMC's error string, and the full GenMC output if requested. eprintln!("(GenMC) Error detected: {error}"); - genmc_ctx.print_genmc_output(genmc_config); + genmc_ctx.print_genmc_output(genmc_config, tcx); return None; } } @@ -110,13 +110,13 @@ impl GenmcCtx { /// /// This message can be very verbose and is likely not useful for the average user. /// This function should be called *after* Miri has printed all of its output. - fn print_genmc_output(&self, genmc_config: &GenmcConfig) { + fn print_genmc_output(&self, genmc_config: &GenmcConfig, tcx: TyCtxt<'_>) { if genmc_config.print_genmc_output { eprintln!("GenMC error report:"); eprintln!("{}", self.get_result_message()); } else { - eprintln!( - "(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.)" + tcx.dcx().note( + "add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report" ); } } diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr index 00de74009d9b8..1ffb55f22e6e0 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr index 501957a90d3ac..b0037c211c69d 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr index 501957a90d3ac..b0037c211c69d 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr index 501957a90d3ac..b0037c211c69d 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr index 9c3ba1d4c5924..290cdf90a0847 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr index db92d0573fad2..7742790e33307 100644 --- a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index 773f86a975967..80ac7206644c6 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index 773f86a975967..80ac7206644c6 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index 773f86a975967..80ac7206644c6 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error From 01cd04f78d86cdc1a60441fd9d6d08aa2fe05a2f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 04:20:24 +0900 Subject: [PATCH 0573/1900] fix: Fix one more thing in `in-rust-tree` --- .../rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index f9d0aa99cf4f3..128135d8debe8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -1,4 +1,4 @@ -pub use ra_ap_rustc_next_trait_solver::solve::inspect::*; +pub use rustc_next_trait_solver::solve::inspect::*; use rustc_ast_ir::try_visit; use rustc_next_trait_solver::{ From eb7abeb2614fb64f4d23afaa83df7380159b72c2 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 16 Feb 2025 10:14:53 +0000 Subject: [PATCH 0574/1900] Specialize `Iterator::eq[_by]` for `TrustedLen` iterators --- library/core/src/iter/traits/iterator.rs | 52 ++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 7fb162a653fb9..695f8d1e195e9 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -4,6 +4,7 @@ use super::super::{ Product, Rev, Scan, Skip, SkipWhile, StepBy, Sum, Take, TakeWhile, TrustedRandomAccessNoCoerce, Zip, try_process, }; +use super::TrustedLen; use crate::array; use crate::cmp::{self, Ordering}; use crate::num::NonZero; @@ -3816,10 +3817,7 @@ pub trait Iterator { } } - match iter_compare(self, other.into_iter(), compare(eq)) { - ControlFlow::Continue(ord) => ord == Ordering::Equal, - ControlFlow::Break(()) => false, - } + SpecIterEq::spec_iter_eq(self, other.into_iter(), compare(eq)) } /// Determines if the elements of this [`Iterator`] are not equal to those of @@ -4038,6 +4036,42 @@ pub trait Iterator { } } +trait SpecIterEq: Iterator { + fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>; +} + +impl SpecIterEq for A { + #[inline] + default fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>, + { + iter_eq(self, b, f) + } +} + +impl SpecIterEq for A { + #[inline] + fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>, + { + // we *can't* short-circuit if: + match (self.size_hint(), b.size_hint()) { + // ... both iterators have the same length + ((_, Some(a)), (_, Some(b))) if a == b => {} + // ... or both of them are longer than `usize::MAX` (i.e. have an unknown length). + ((_, None), (_, None)) => {} + // otherwise, we can ascertain that they are unequal without actually comparing items + _ => return false, + } + + iter_eq(self, b, f) + } +} + /// Compares two iterators element-wise using the given function. /// /// If `ControlFlow::Continue(())` is returned from the function, the comparison moves on to the next @@ -4078,6 +4112,16 @@ where } } +#[inline] +fn iter_eq(a: A, b: B, f: F) -> bool +where + A: Iterator, + B: Iterator, + F: FnMut(A::Item, B::Item) -> ControlFlow<()>, +{ + iter_compare(a, b, f).continue_value().is_some_and(|ord| ord == Ordering::Equal) +} + /// Implements `Iterator` for mutable references to iterators, such as those produced by [`Iterator::by_ref`]. /// /// This implementation passes all method calls on to the original iterator. From f19b560fa96ca8dd4352db6372f662fa32942a9f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 22:02:46 +0200 Subject: [PATCH 0575/1900] implement sqrt for f16 and f128 --- src/tools/miri/src/intrinsics/math.rs | 16 ++++++++++ src/tools/miri/tests/pass/float.rs | 44 ++++++++++++++++++--------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index 21d4a92e7d288..ca81454a98c08 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -20,6 +20,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match intrinsic_name { // Operations we can do with soft-floats. + "sqrtf16" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f16()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } "sqrtf32" => { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -36,6 +44,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "sqrtf128" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f128()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } "fmaf32" => { let [a, b, c] = check_intrinsic_arg_count(args)?; diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 9f1b3f612b2d2..3ce5ea8356bd5 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -281,6 +281,35 @@ fn basic() { assert_eq!(34.2f64.abs(), 34.2f64); assert_eq!((-1.0f128).abs(), 1.0f128); assert_eq!(34.2f128.abs(), 34.2f128); + + assert_eq!(64_f16.sqrt(), 8_f16); + assert_eq!(64_f32.sqrt(), 8_f32); + assert_eq!(64_f64.sqrt(), 8_f64); + assert_eq!(64_f128.sqrt(), 8_f128); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); + assert_eq!(0.0_f16.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f128.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f16).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f128).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert!((-5.0_f16).sqrt().is_nan()); + assert!((-5.0_f32).sqrt().is_nan()); + assert!((-5.0_f64).sqrt().is_nan()); + assert!((-5.0_f128).sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!(f16::NAN.sqrt().is_nan()); + assert!(f32::NAN.sqrt().is_nan()); + assert!(f64::NAN.sqrt().is_nan()); + assert!(f128::NAN.sqrt().is_nan()); } /// Test casts from floats to ints and back @@ -1012,21 +1041,6 @@ pub fn libm() { unsafe { ldexp(a, b) } } - assert_eq!(64_f32.sqrt(), 8_f32); - assert_eq!(64_f64.sqrt(), 8_f64); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); - assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); - assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); - assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); - assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); - assert!((-5.0_f32).sqrt().is_nan()); - assert!((-5.0_f64).sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!(f32::NAN.sqrt().is_nan()); - assert!(f64::NAN.sqrt().is_nan()); - assert_approx_eq!(25f32.powi(-2), 0.0016f32); assert_approx_eq!(23.2f64.powi(2), 538.24f64); From 77f2d865549e8f2e1ad656c8202de9bfa6d2e354 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 22:13:15 +0200 Subject: [PATCH 0576/1900] share sqrt implemention across float types --- src/tools/miri/src/intrinsics/math.rs | 52 ++++++++++----------------- src/tools/miri/src/math.rs | 12 +++---- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index ca81454a98c08..b9c99f2859468 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -1,5 +1,5 @@ use rand::Rng; -use rustc_apfloat::{self, Float, Round}; +use rustc_apfloat::{self, Float, FloatConvert, Round}; use rustc_middle::mir; use rustc_middle::ty::{self, FloatTy}; @@ -7,6 +7,20 @@ use self::helpers::{ToHost, ToSoft}; use super::check_intrinsic_arg_count; use crate::*; +fn sqrt<'tcx, F: Float + FloatConvert + Into>( + this: &mut MiriInterpCx<'tcx>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx> { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?; + let f: F = f.to_float()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest) +} + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_math_intrinsic( @@ -20,38 +34,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match intrinsic_name { // Operations we can do with soft-floats. - "sqrtf16" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f16()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf32" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf64" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf128" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f128()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } + "sqrtf16" => sqrt::(this, args, dest)?, + "sqrtf32" => sqrt::(this, args, dest)?, + "sqrtf64" => sqrt::(this, args, dest)?, + "sqrtf128" => sqrt::(this, args, dest)?, "fmaf32" => { let [a, b, c] = check_intrinsic_arg_count(args)?; diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 401e6dd7aab84..50472ed3638e9 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -2,7 +2,7 @@ use std::ops::Neg; use std::{f32, f64}; use rand::Rng as _; -use rustc_apfloat::Float as _; +use rustc_apfloat::Float; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, Semantics, SingleS}; use rustc_middle::ty::{self, FloatTy, ScalarInt}; @@ -317,19 +317,19 @@ where } } -pub(crate) fn sqrt(x: IeeeFloat) -> IeeeFloat { +pub(crate) fn sqrt(x: F) -> F { match x.category() { // preserve zero sign rustc_apfloat::Category::Zero => x, // propagate NaN rustc_apfloat::Category::NaN => x, // sqrt of negative number is NaN - _ if x.is_negative() => IeeeFloat::NAN, + _ if x.is_negative() => F::NAN, // sqrt(∞) = ∞ - rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY, + rustc_apfloat::Category::Infinity => F::INFINITY, rustc_apfloat::Category::Normal => { // Floating point precision, excluding the integer bit - let prec = i32::try_from(S::PRECISION).unwrap() - 1; + let prec = i32::try_from(F::PRECISION).unwrap() - 1; // x = 2^(exp - prec) * mant // where mant is an integer with prec+1 bits @@ -394,7 +394,7 @@ pub(crate) fn sqrt(x: IeeeFloat) -> IeeeFl res = (res + 1) >> 1; // Build resulting value with res as mantissa and exp/2 as exponent - IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec) + F::from_u128(res).value.scalbn(exp / 2 - prec) } } } From 45e5c765c660463179128c5deaf6f56ff24f3578 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:17:39 -0700 Subject: [PATCH 0577/1900] fix tidy spellchecking on windows --- src/tools/tidy/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 2a9c3963d2d86..f7920e3205ab2 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -237,7 +237,7 @@ pub fn ensure_version_or_cargo_install( if !cargo_exit_code.success() { return Err(io::Error::other("cargo install failed")); } - let bin_path = tool_bin_dir.join(bin_name); + let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); assert!( matches!(bin_path.try_exists(), Ok(true)), "cargo install did not produce the expected binary" From 7af6e590a4d1f66770b49698bd01d88cca98f18f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 23:33:38 +0200 Subject: [PATCH 0578/1900] update lockfile --- Cargo.lock | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d39cfefea0c77..910a03eaffca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,7 +674,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.1", ] [[package]] @@ -937,23 +937,24 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa144b12f11741f0dab5b4182896afad46faa0598b6a061f7b9d17a21837ba7" +checksum = "2f81de88da10862f22b5b3a60f18f6f42bbe7cb8faa24845dd7b1e4e22190e77" dependencies = [ "cc", + "cxx-build", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", - "foldhash", + "foldhash 0.2.0", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d3cbb84fb003242941c231b45ca9417e786e66e94baa39584bd99df3a270b6" +checksum = "5edd58bf75c3fdfc80d79806403af626570662f7b6cc782a7fabe156166bd6d6" dependencies = [ "cc", "codespan-reporting", @@ -966,9 +967,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa36b7b249d43f67a3f54bd65788e35e7afe64bbc671396387a48b3e8aaea94" +checksum = "fd46bf2b541a4e0c2d5abba76607379ee05d68e714868e3cb406dc8d591ce2d2" dependencies = [ "clap", "codespan-reporting", @@ -980,15 +981,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77707c70f6563edc5429618ca34a07241b75ebab35bd01d46697c75d58f8ddfe" +checksum = "2c79b68f6a3a8f809d39b38ae8af61305a6113819b19b262643b9c21353b92d9" [[package]] name = "cxxbridge-macro" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede6c0fb7e318f0a11799b86ee29dcf17b9be2960bd379a6c38e1a96a6010fff" +checksum = "862b7fdb048ff9ef0779a0d0a03affd09746c4c875543746b640756be9cff2af" dependencies = [ "indexmap", "proc-macro2", @@ -1388,6 +1389,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1567,7 +1574,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "serde", ] @@ -2160,9 +2167,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" dependencies = [ "cc", ] From a08e6499e6a215021684681448ed0f3ce60c827a Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Sep 2025 15:18:42 +0200 Subject: [PATCH 0579/1900] move `mod canonical` out of `eval_ctxt` --- .../src/{ => canonical}/canonicalizer.rs | 0 .../canonical.rs => canonical/mod.rs} | 226 ++---------------- compiler/rustc_next_trait_solver/src/lib.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 202 +++++++++++++++- .../src/solve/inspect/build.rs | 2 +- .../src/solve/inspect/mod.rs | 2 - .../rustc_next_trait_solver/src/solve/mod.rs | 19 -- .../src/solve/search_graph.rs | 3 +- .../src/solve/inspect/analyse.rs | 4 +- 9 files changed, 229 insertions(+), 231 deletions(-) rename compiler/rustc_next_trait_solver/src/{ => canonical}/canonicalizer.rs (100%) rename compiler/rustc_next_trait_solver/src/{solve/eval_ctxt/canonical.rs => canonical/mod.rs} (58%) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs similarity index 100% rename from compiler/rustc_next_trait_solver/src/canonicalizer.rs rename to compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs similarity index 58% rename from compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs rename to compiler/rustc_next_trait_solver/src/canonical/mod.rs index 889588afe61ff..35881acf0c753 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -11,27 +11,25 @@ use std::iter; +use canonicalizer::Canonicalizer; use rustc_index::IndexVec; -use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, }; -use tracing::{debug, instrument, trace}; +use tracing::instrument; -use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::eager_resolve_vars; -use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, - MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, - QueryResult, Response, inspect, response_no_constraints_raw, + NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, }; +pub mod canonicalizer; + trait ResponseT { fn var_values(&self) -> CanonicalVarValues; } @@ -78,199 +76,6 @@ where (orig_values, query_input) } - /// To return the constraints of a canonical query to the caller, we canonicalize: - /// - /// - `var_values`: a map from bound variables in the canonical goal to - /// the values inferred while solving the instantiated goal. - /// - `external_constraints`: additional constraints which aren't expressible - /// using simple unification of inference variables. - /// - /// This takes the `shallow_certainty` which represents whether we're confident - /// that the final result of the current goal only depends on the nested goals. - /// - /// In case this is `Certainty::Maybe`, there may still be additional nested goals - /// or inference constraints required for this candidate to be hold. The candidate - /// always requires all already added constraints and nested goals. - #[instrument(level = "trace", skip(self), ret)] - pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( - &mut self, - shallow_certainty: Certainty, - ) -> QueryResult { - self.inspect.make_canonical_response(shallow_certainty); - - let goals_certainty = self.try_evaluate_added_goals()?; - assert_eq!( - self.tainted, - Ok(()), - "EvalCtxt is tainted -- nested goals may have been dropped in a \ - previous call to `try_evaluate_added_goals!`" - ); - - // We only check for leaks from universes which were entered inside - // of the query. - self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { - trace!("failed the leak check"); - NoSolution - })?; - - let (certainty, normalization_nested_goals) = - match (self.current_goal_kind, shallow_certainty) { - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. We only do so if we do not - // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly - // uplifting its nested goals. This is the case if the `shallow_certainty` is - // `Certainty::Yes`. - (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { - let goals = std::mem::take(&mut self.nested_goals); - // As we return all ambiguous nested goals, we can ignore the certainty - // returned by `self.try_evaluate_added_goals()`. - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } - ( - Certainty::Yes, - NestedNormalizationGoals( - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), - ), - ) - } - _ => { - let certainty = shallow_certainty.and(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; - - if let Certainty::Maybe { - cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, - opaque_types_jank, - } = certainty - { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); - } - - let external_constraints = - self.compute_external_query_constraints(certainty, normalization_nested_goals); - let (var_values, mut external_constraints) = - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); - - // Remove any trivial or duplicated region constraints once we've resolved regions - let mut unique = HashSet::default(); - external_constraints.region_constraints.retain(|outlives| { - outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) - }); - - let canonical = Canonicalizer::canonicalize_response( - self.delegate, - self.max_input_universe, - &mut Default::default(), - Response { - var_values, - certainty, - external_constraints: self.cx().mk_external_constraints(external_constraints), - }, - ); - - // HACK: We bail with overflow if the response would have too many non-region - // inference variables. This tends to only happen if we encounter a lot of - // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents hangs caused by an exponential blowup, - // see tests/ui/traits/next-solver/coherence-alias-hang.rs. - match self.current_goal_kind { - // We don't do so for `NormalizesTo` goals as we erased the expected term and - // bailing with overflow here would prevent us from detecting a type-mismatch, - // causing a coherence error in diesel, see #131969. We still bail with overflow - // when later returning from the parent AliasRelate goal. - CurrentGoalKind::NormalizesTo => {} - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let num_non_region_vars = canonical - .variables - .iter() - .filter(|c| !c.is_region() && c.is_existential()) - .count(); - if num_non_region_vars > self.cx().recursion_limit() { - debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints( - MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - }, - OpaqueTypesJank::AllGood, - )); - } - } - } - - Ok(canonical) - } - - /// Constructs a totally unconstrained, ambiguous response to a goal. - /// - /// Take care when using this, since often it's useful to respond with - /// ambiguity but return constrained variables to guide inference. - pub(in crate::solve) fn make_ambiguous_response_no_constraints( - &self, - cause: MaybeCause, - opaque_types_jank: OpaqueTypesJank, - ) -> CanonicalResponse { - response_no_constraints_raw( - self.cx(), - self.max_input_universe, - self.variables, - Certainty::Maybe { cause, opaque_types_jank }, - ) - } - - /// 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 = "trace", skip(self), ret)] - fn compute_external_query_constraints( - &self, - certainty: Certainty, - normalization_nested_goals: NestedNormalizationGoals, - ) -> ExternalConstraintsData { - // We only return region constraints once the certainty is `Yes`. This - // is necessary as we may drop nested goals on ambiguity, which may result - // in unconstrained inference variables in the region constraints. It also - // prevents us from emitting duplicate region constraints, avoiding some - // unnecessary work. This slightly weakens the leak check in case it uses - // region constraints from an ambiguous nested goal. This is tested in both - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. - let region_constraints = if certainty == Certainty::Yes { - self.delegate.make_deduplicated_outlives_constraints() - } else { - Default::default() - }; - - // We only return *newly defined* opaque types from canonical queries. - // - // Constraints for any existing opaque types are already tracked by changes - // to the `var_values`. - let opaque_types = self - .delegate - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); - - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } - } - /// After calling a canonical query, we apply the constraints returned /// by the query using this function. /// @@ -469,7 +274,7 @@ where /// evaluating a goal. The `var_values` not only include the bound variables /// of the query input, but also contain all unconstrained inference vars /// created while evaluating this goal. -pub(in crate::solve) fn make_canonical_state( +pub fn make_canonical_state( delegate: &D, var_values: &[I::GenericArg], max_input_universe: ty::UniverseIndex, @@ -515,3 +320,22 @@ where EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } + +pub fn response_no_constraints_raw( + cx: I, + max_universe: ty::UniverseIndex, + variables: I::CanonicalVarKinds, + certainty: Certainty, +) -> CanonicalResponse { + ty::Canonical { + max_universe, + variables, + value: Response { + var_values: ty::CanonicalVarValues::make_identity(cx, variables), + // FIXME: maybe we should store the "no response" version in cx, like + // we do for cx.types and stuff. + external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), + certainty, + }, + } +} diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index d3965e14c68a8..5fa29b7d9f813 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -10,7 +10,7 @@ #![allow(rustc::usage_of_type_ir_traits)] // tidy-alphabetical-end -pub mod canonicalizer; +pub mod canonical; pub mod coherence; pub mod delegate; pub mod placeholder; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 5df7c92d88145..8f9b68dbac4f4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,6 +17,8 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; +use crate::canonical::canonicalizer::Canonicalizer; +use crate::canonical::response_no_constraints_raw; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -24,12 +26,11 @@ use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, - GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, - inspect, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT, + Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause, + NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, inspect, }; -pub(super) mod canonical; mod probe; /// The kind of goal we're currently proving. @@ -1223,6 +1224,199 @@ where vec![] } } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressible + /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainty::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. + #[instrument(level = "trace", skip(self), ret)] + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + shallow_certainty: Certainty, + ) -> QueryResult { + self.inspect.make_canonical_response(shallow_certainty); + + let goals_certainty = self.try_evaluate_added_goals()?; + assert_eq!( + self.tainted, + Ok(()), + "EvalCtxt is tainted -- nested goals may have been dropped in a \ + previous call to `try_evaluate_added_goals!`" + ); + + // We only check for leaks from universes which were entered inside + // of the query. + self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { + trace!("failed the leak check"); + NoSolution + })?; + + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) + } + _ => { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; + + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty + { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); + } + + let external_constraints = + self.compute_external_query_constraints(certainty, normalization_nested_goals); + let (var_values, mut external_constraints) = + eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + + // Remove any trivial or duplicated region constraints once we've resolved regions + let mut unique = HashSet::default(); + external_constraints.region_constraints.retain(|outlives| { + outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) + }); + + let canonical = Canonicalizer::canonicalize_response( + self.delegate, + self.max_input_universe, + &mut Default::default(), + Response { + var_values, + certainty, + external_constraints: self.cx().mk_external_constraints(external_constraints), + }, + ); + + // HACK: We bail with overflow if the response would have too many non-region + // inference variables. This tends to only happen if we encounter a lot of + // ambiguous alias types which get replaced with fresh inference variables + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + match self.current_goal_kind { + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let num_non_region_vars = canonical + .variables + .iter() + .filter(|c| !c.is_region() && c.is_existential()) + .count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); + } + } + } + + Ok(canonical) + } + + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, + ) -> CanonicalResponse { + response_no_constraints_raw( + self.cx(), + self.max_input_universe, + self.variables, + Certainty::Maybe { cause, opaque_types_jank }, + ) + } + + /// 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 = "trace", skip(self), ret)] + fn compute_external_query_constraints( + &self, + certainty: Certainty, + normalization_nested_goals: NestedNormalizationGoals, + ) -> ExternalConstraintsData { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + self.delegate.make_deduplicated_outlives_constraints() + } else { + Default::default() + }; + + // We only return *newly defined* opaque types from canonical queries. + // + // Constraints for any existing opaque types are already tracked by changes + // to the `var_values`. + let opaque_types = self + .delegate + .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 2675ed0d0da65..4369148baf91d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -10,8 +10,8 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner}; +use crate::canonical; use crate::delegate::SolverDelegate; -use crate::solve::eval_ctxt::canonical; use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect}; /// We need to know whether to build a prove tree while evaluating. We diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs index 0d8c00601269b..65f32f1947fef 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs @@ -2,5 +2,3 @@ pub use rustc_type_ir::solve::inspect::*; mod build; pub(in crate::solve) use build::*; - -pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index fb900b592d13b..afb86aaf8ab21 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -380,25 +380,6 @@ where } } -fn response_no_constraints_raw( - cx: I, - max_universe: ty::UniverseIndex, - variables: I::CanonicalVarKinds, - certainty: Certainty, -) -> CanonicalResponse { - ty::Canonical { - max_universe, - variables, - value: Response { - var_values: ty::CanonicalVarValues::make_identity(cx, variables), - // FIXME: maybe we should store the "no response" version in cx, like - // we do for cx.types and stuff. - external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), - certainty, - }, - } -} - /// The result of evaluating a goal. pub struct GoalEvaluation { /// The goal we've evaluated. This is the input goal, but potentially with its diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 289325d70550c..aa9dfc9a9a265 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -6,6 +6,7 @@ use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, TypingMode}; +use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; use crate::solve::{ EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints, inspect, @@ -127,7 +128,7 @@ fn response_no_constraints( input: CanonicalInput, certainty: Certainty, ) -> QueryResult { - Ok(super::response_no_constraints_raw( + Ok(response_no_constraints_raw( cx, input.canonical.max_universe, input.canonical.variables, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 086a7a44786d6..c010add0fc50f 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -18,9 +18,9 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::canonical::instantiate_canonical_state; use rustc_next_trait_solver::resolve::eager_resolve_vars; -use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; -use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; From b83c0f0e94592d1a7eae1124e4e951f331ccf6c8 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Sep 2025 15:30:25 +0200 Subject: [PATCH 0580/1900] canonical: yeet `EvalCtxt`, mk `Canonicalizer` private --- .../src/canonical/canonicalizer.rs | 7 +- .../src/canonical/mod.rs | 419 +++++++++--------- .../src/solve/eval_ctxt/mod.rs | 18 +- 3 files changed, 233 insertions(+), 211 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 4b4ec4956eb88..b25671d676b9c 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -57,7 +57,7 @@ enum CanonicalizeMode { }, } -pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { +pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { delegate: &'a D, // Immutable field. @@ -83,7 +83,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { - pub fn canonicalize_response>( + pub(super) fn canonicalize_response>( delegate: &'a D, max_input_universe: ty::UniverseIndex, variables: &'a mut Vec, @@ -112,7 +112,6 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); Canonical { max_universe, variables, value } } - fn canonicalize_param_env( delegate: &'a D, variables: &'a mut Vec, @@ -195,7 +194,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { /// /// We want to keep the option of canonicalizing `'static` to an existential /// variable in the future by changing the way we detect global where-bounds. - pub fn canonicalize_input>( + pub(super) fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, input: QueryInput, diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 35881acf0c753..e3520e238ed37 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -24,7 +24,7 @@ use tracing::instrument; use crate::delegate::SolverDelegate; use crate::resolve::eager_resolve_vars; use crate::solve::{ - CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, }; @@ -46,226 +46,248 @@ impl ResponseT for inspect::State { } } -impl EvalCtxt<'_, D> +/// Canonicalizes the goal remembering the original values +/// for each bound variable. +/// +/// This expects `goal` and `opaque_types` to be eager resolved. +pub(super) fn canonicalize_goal( + delegate: &D, + goal: Goal, + opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, +) -> (Vec, CanonicalInput) where D: SolverDelegate, I: Interner, { - /// Canonicalizes the goal remembering the original values - /// for each bound variable. - /// - /// This expects `goal` and `opaque_types` to be eager resolved. - pub(super) fn canonicalize_goal( - delegate: &D, - goal: Goal, - opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, - ) -> (Vec, CanonicalInput) { - let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize_input( - delegate, - &mut orig_values, - QueryInput { - goal, - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }, - ); - let query_input = - ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; - (orig_values, query_input) - } + let mut orig_values = Default::default(); + let canonical = Canonicalizer::canonicalize_input( + delegate, + &mut orig_values, + QueryInput { + goal, + predefined_opaques_in_body: delegate + .cx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), + }, + ); + let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; + (orig_values, query_input) +} - /// After calling a canonical query, we apply the constraints returned - /// by the query using this function. - /// - /// This happens in three steps: - /// - we instantiate the bound variables of the query response - /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query, returning - /// the `normalization_nested_goals` - pub(super) fn instantiate_and_apply_query_response( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - response: CanonicalResponse, - span: I::Span, - ) -> (NestedNormalizationGoals, Certainty) { - let instantiation = Self::compute_query_response_instantiation_values( - delegate, - &original_values, - &response, - span, - ); +pub(super) fn canonicalize_response( + delegate: &D, + max_input_universe: ty::UniverseIndex, + value: T, +) -> ty::Canonical +where + D: SolverDelegate, + I: Interner, + T: TypeFoldable, +{ + let mut orig_values = Default::default(); + let canonical = + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value); + canonical +} - let Response { var_values, external_constraints, certainty } = - delegate.instantiate_canonical(response, instantiation); +/// After calling a canonical query, we apply the constraints returned +/// by the query using this function. +/// +/// This happens in three steps: +/// - we instantiate the bound variables of the query response +/// - we unify the `var_values` of the response with the `original_values` +/// - we apply the `external_constraints` returned by the query, returning +/// the `normalization_nested_goals` +pub(super) fn instantiate_and_apply_query_response( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + response: CanonicalResponse, + span: I::Span, +) -> (NestedNormalizationGoals, Certainty) +where + D: SolverDelegate, + I: Interner, +{ + let instantiation = + compute_query_response_instantiation_values(delegate, &original_values, &response, span); - Self::unify_query_var_values(delegate, param_env, &original_values, var_values, span); + let Response { var_values, external_constraints, certainty } = + delegate.instantiate_canonical(response, instantiation); - let ExternalConstraintsData { - region_constraints, - opaque_types, - normalization_nested_goals, - } = &*external_constraints; + unify_query_var_values(delegate, param_env, &original_values, var_values, span); - Self::register_region_constraints(delegate, region_constraints, span); - Self::register_new_opaque_types(delegate, opaque_types, span); + let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = + &*external_constraints; - (normalization_nested_goals.clone(), certainty) - } + register_region_constraints(delegate, region_constraints, span); + register_new_opaque_types(delegate, opaque_types, span); - /// This returns the canonical variable values to instantiate the bound variables of - /// the canonical response. This depends on the `original_values` for the - /// bound variables. - fn compute_query_response_instantiation_values>( - delegate: &D, - original_values: &[I::GenericArg], - response: &Canonical, - span: I::Span, - ) -> CanonicalVarValues { - // FIXME: Longterm canonical queries should deal with all placeholders - // created inside of the query directly instead of returning them to the - // caller. - let prev_universe = delegate.universe(); - let universes_created_in_query = response.max_universe.index(); - for _ in 0..universes_created_in_query { - delegate.create_next_universe(); - } + (normalization_nested_goals.clone(), certainty) +} + +/// This returns the canonical variable values to instantiate the bound variables of +/// the canonical response. This depends on the `original_values` for the +/// bound variables. +fn compute_query_response_instantiation_values( + delegate: &D, + original_values: &[I::GenericArg], + response: &Canonical, + span: I::Span, +) -> CanonicalVarValues +where + D: SolverDelegate, + I: Interner, + T: ResponseT, +{ + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = delegate.universe(); + let universes_created_in_query = response.max_universe.index(); + for _ in 0..universes_created_in_query { + delegate.create_next_universe(); + } - let var_values = response.value.var_values(); - assert_eq!(original_values.len(), var_values.len()); + let var_values = response.value.var_values(); + assert_eq!(original_values.len(), var_values.len()); - // If the query did not make progress with constraining inference variables, - // we would normally create a new inference variables for bound existential variables - // only then unify this new inference variable with the inference variable from - // the input. - // - // We therefore instantiate the existential variable in the canonical response with the - // inference variable of the input right away, which is more performant. - let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); - for (original_value, result_value) in - iter::zip(original_values, var_values.var_values.iter()) - { - match result_value.kind() { - ty::GenericArgKind::Type(t) => { - // We disable the instantiation guess for inference variables - // and only use it for placeholders. We need to handle the - // `sub_root` of type inference variables which would make this - // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = t.kind() - && !matches!( - response.variables.get(b.var().as_usize()).unwrap(), - CanonicalVarKind::Ty { .. } - ) - { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[b.var()] = Some(*original_value); - } + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) { + match result_value.kind() { + ty::GenericArgKind::Type(t) => { + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = t.kind() + && !matches!( + response.variables.get(b.var().as_usize()).unwrap(), + CanonicalVarKind::Ty { .. } + ) + { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var()] = Some(*original_value); } - ty::GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = r.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.var()] = Some(*original_value); - } + } + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = r.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var()] = Some(*original_value); } - ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[bv.var()] = Some(*original_value); - } + } + ty::GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[bv.var()] = Some(*original_value); } } } - CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { - if kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { - prev_universe + idx.index() - }) - } else if kind.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { - v - } else { - delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) - } + } + CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { + if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if kind.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { + v } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[kind.expect_placeholder_index()] + delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) } - }) - } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[kind.expect_placeholder_index()] + } + }) +} - /// Unify the `original_values` with the `var_values` returned by the canonical query.. - /// - /// This assumes that this unification will always succeed. This is the case when - /// applying a query response right away. However, calling a canonical query, doing any - /// other kind of trait solving, and only then instantiating the result of the query - /// can cause the instantiation to fail. This is not supported and we ICE in this case. - /// - /// We always structurally instantiate aliases. Relating aliases needs to be different - /// depending on whether the alias is *rigid* or not. We're only really able to tell - /// whether an alias is rigid by using the trait solver. When instantiating a response - /// from the solver we assume that the solver correctly handled aliases and therefore - /// always relate them structurally here. - #[instrument(level = "trace", skip(delegate))] - fn unify_query_var_values( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - var_values: CanonicalVarValues, - span: I::Span, - ) { - assert_eq!(original_values.len(), var_values.len()); +/// Unify the `original_values` with the `var_values` returned by the canonical query.. +/// +/// This assumes that this unification will always succeed. This is the case when +/// applying a query response right away. However, calling a canonical query, doing any +/// other kind of trait solving, and only then instantiating the result of the query +/// can cause the instantiation to fail. This is not supported and we ICE in this case. +/// +/// We always structurally instantiate aliases. Relating aliases needs to be different +/// depending on whether the alias is *rigid* or not. We're only really able to tell +/// whether an alias is rigid by using the trait solver. When instantiating a response +/// from the solver we assume that the solver correctly handled aliases and therefore +/// always relate them structurally here. +#[instrument(level = "trace", skip(delegate))] +fn unify_query_var_values( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + var_values: CanonicalVarValues, + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + assert_eq!(original_values.len(), var_values.len()); - for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); - assert!(goals.is_empty()); - } + for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { + let goals = + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); + assert!(goals.is_empty()); } +} - fn register_region_constraints( - delegate: &D, - outlives: &[ty::OutlivesPredicate], - span: I::Span, - ) { - for &ty::OutlivesPredicate(lhs, rhs) in outlives { - match lhs.kind() { - ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), - ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), - ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), - } +fn register_region_constraints( + delegate: &D, + outlives: &[ty::OutlivesPredicate], + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + for &ty::OutlivesPredicate(lhs, rhs) in outlives { + match lhs.kind() { + ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), + ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), + ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), } } +} - fn register_new_opaque_types( - delegate: &D, - opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], - span: I::Span, - ) { - for &(key, ty) in opaque_types { - let prev = delegate.register_hidden_type_in_storage(key, ty, span); - // We eagerly resolve inference variables when computing the query response. - // This can cause previously distinct opaque type keys to now be structurally equal. - // - // To handle this, we store any duplicate entries in a separate list to check them - // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden - // types here. However, doing so is difficult as it may result in nested goals and - // any errors may make it harder to track the control flow for diagnostics. - if let Some(prev) = prev { - delegate.add_duplicate_opaque_type(key, prev, span); - } +fn register_new_opaque_types( + delegate: &D, + opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + for &(key, ty) in opaque_types { + let prev = delegate.register_hidden_type_in_storage(key, ty, span); + // We eagerly resolve inference variables when computing the query response. + // This can cause previously distinct opaque type keys to now be structurally equal. + // + // To handle this, we store any duplicate entries in a separate list to check them + // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden + // types here. However, doing so is difficult as it may result in nested goals and + // any errors may make it harder to track the control flow for diagnostics. + if let Some(prev) = prev { + delegate.add_duplicate_opaque_type(key, prev, span); } } } @@ -274,7 +296,7 @@ where /// evaluating a goal. The `var_values` not only include the bound variables /// of the query input, but also contain all unconstrained inference vars /// created while evaluating this goal. -pub fn make_canonical_state( +pub fn make_canonical_state( delegate: &D, var_values: &[I::GenericArg], max_input_universe: ty::UniverseIndex, @@ -293,7 +315,7 @@ where // FIXME: needs to be pub to be accessed by downstream // `rustc_trait_selection::solve::inspect::analyse`. -pub fn instantiate_canonical_state>( +pub fn instantiate_canonical_state( delegate: &D, span: I::Span, param_env: I::ParamEnv, @@ -303,6 +325,7 @@ pub fn instantiate_canonical_state>( where D: SolverDelegate, I: Interner, + T: TypeFoldable, { // In case any fresh inference variables have been created between `state` // and the previous instantiation, extend `orig_values` for it. @@ -313,11 +336,11 @@ where ); let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); + compute_query_response_instantiation_values(delegate, orig_values, &state, span); let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); + unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8f9b68dbac4f4..bb86357a85f8a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,8 +17,10 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; -use crate::canonical::canonicalizer::Canonicalizer; -use crate::canonical::response_no_constraints_raw; +use crate::canonical::{ + canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, + response_no_constraints_raw, +}; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -465,8 +467,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = - Self::canonicalize_goal(self.delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -481,7 +482,7 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = Self::instantiate_and_apply_query_response( + let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response( self.delegate, goal.param_env, &orig_values, @@ -1319,10 +1320,9 @@ where outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) }); - let canonical = Canonicalizer::canonicalize_response( + let canonical = canonicalize_response( self.delegate, self.max_input_universe, - &mut Default::default(), Response { var_values, certainty, @@ -1557,7 +1557,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = EvalCtxt::canonicalize_goal(delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, opaque_types); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); @@ -1574,7 +1574,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, Ok(response) => response, }; - let (normalization_nested_goals, _certainty) = EvalCtxt::instantiate_and_apply_query_response( + let (normalization_nested_goals, _certainty) = instantiate_and_apply_query_response( delegate, goal.param_env, &proof_tree.orig_values, From 47d6936c7d37898a2de5549dcba8b50b25be21dd Mon Sep 17 00:00:00 2001 From: Oblarg Date: Thu, 18 Sep 2025 18:15:19 -0400 Subject: [PATCH 0581/1900] fix negative const generic integer literals --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 23 +++++++++++++++++++ .../crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 79f78c545e595..4d5172fd4f24f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -299,6 +299,29 @@ impl<'a> TyLoweringContext<'a> { const_type, self.resolver.krate(), ), + hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { + if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { + // Only handle negation for signed integers and floats + match literal { + hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { + if let Some(negated_literal) = literal.clone().negate() { + intern_const_ref( + self.db, + &negated_literal.into(), + const_type, + self.resolver.krate(), + ) + } else { + unknown_const(const_type) + } + } + // For unsigned integers, chars, bools, etc., negation is not meaningful + _ => unknown_const(const_type), + } + } else { + unknown_const(const_type) + } + } _ => unknown_const(const_type), } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 45aec3877475e..1ea11a215f83d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -4738,7 +4738,7 @@ fn main() { *value* ```rust - let value: Const<_> + let value: Const<-1> ``` --- From 934ee043feb6601f9dfb5be173a190a921e6d9a9 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sun, 14 Sep 2025 11:14:50 -0700 Subject: [PATCH 0582/1900] Plumb Allocator generic into `std::vec::PeekMut` --- library/alloc/src/vec/mod.rs | 54 +++++++++++++++---------------- library/alloc/src/vec/peek_mut.rs | 22 ++++++++----- library/alloctests/tests/vec.rs | 19 +++++------ 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 2e40227a058af..1b80b43abc357 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -760,33 +760,6 @@ impl Vec { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } - /// Returns a mutable reference to the last item in the vector, or - /// `None` if it is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(vec_peek_mut)] - /// let mut vec = Vec::new(); - /// assert!(vec.peek_mut().is_none()); - /// - /// vec.push(1); - /// vec.push(5); - /// vec.push(2); - /// assert_eq!(vec.last(), Some(&2)); - /// if let Some(mut val) = vec.peek_mut() { - /// *val = 0; - /// } - /// assert_eq!(vec.last(), Some(&0)); - /// ``` - #[inline] - #[unstable(feature = "vec_peek_mut", issue = "122742")] - pub fn peek_mut(&mut self) -> Option> { - PeekMut::new(self) - } - /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. /// /// Returns the raw pointer to the underlying data, the length of @@ -2747,6 +2720,33 @@ impl Vec { if predicate(last) { self.pop() } else { None } } + /// Returns a mutable reference to the last item in the vector, or + /// `None` if it is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(vec_peek_mut)] + /// let mut vec = Vec::new(); + /// assert!(vec.peek_mut().is_none()); + /// + /// vec.push(1); + /// vec.push(5); + /// vec.push(2); + /// assert_eq!(vec.last(), Some(&2)); + /// if let Some(mut val) = vec.peek_mut() { + /// *val = 0; + /// } + /// assert_eq!(vec.last(), Some(&0)); + /// ``` + #[inline] + #[unstable(feature = "vec_peek_mut", issue = "122742")] + pub fn peek_mut(&mut self) -> Option> { + PeekMut::new(self) + } + /// Moves all the elements of `other` into `self`, leaving `other` empty. /// /// # Panics diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs index caeaf2799d733..979bcaa1111d5 100644 --- a/library/alloc/src/vec/peek_mut.rs +++ b/library/alloc/src/vec/peek_mut.rs @@ -1,6 +1,7 @@ use core::ops::{Deref, DerefMut}; use super::Vec; +use crate::alloc::{Allocator, Global}; use crate::fmt; /// Structure wrapping a mutable reference to the last item in a @@ -11,19 +12,23 @@ use crate::fmt; /// /// [`peek_mut`]: Vec::peek_mut #[unstable(feature = "vec_peek_mut", issue = "122742")] -pub struct PeekMut<'a, T> { - vec: &'a mut Vec, +pub struct PeekMut< + 'a, + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + vec: &'a mut Vec, } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl fmt::Debug for PeekMut<'_, T> { +impl fmt::Debug for PeekMut<'_, T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PeekMut").field(self.deref()).finish() } } -impl<'a, T> PeekMut<'a, T> { - pub(crate) fn new(vec: &'a mut Vec) -> Option { +impl<'a, T, A: Allocator> PeekMut<'a, T, A> { + pub(super) fn new(vec: &'a mut Vec) -> Option { if vec.is_empty() { None } else { Some(Self { vec }) } } @@ -36,17 +41,18 @@ impl<'a, T> PeekMut<'a, T> { } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl<'a, T> Deref for PeekMut<'a, T> { +impl<'a, T, A: Allocator> Deref for PeekMut<'a, T, A> { type Target = T; fn deref(&self) -> &Self::Target { + let idx = self.vec.len() - 1; // SAFETY: PeekMut is only constructed if the vec is non-empty - unsafe { self.vec.get_unchecked(self.vec.len() - 1) } + unsafe { self.vec.get_unchecked(idx) } } } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl<'a, T> DerefMut for PeekMut<'a, T> { +impl<'a, T, A: Allocator> DerefMut for PeekMut<'a, T, A> { fn deref_mut(&mut self) -> &mut Self::Target { let idx = self.vec.len() - 1; // SAFETY: PeekMut is only constructed if the vec is non-empty diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 404eb49e1ea9c..33a34daccbfd2 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2643,15 +2643,16 @@ fn test_peek_mut() { assert!(vec.peek_mut().is_none()); vec.push(1); vec.push(2); - if let Some(mut p) = vec.peek_mut() { - assert_eq!(*p, 2); - *p = 0; - assert_eq!(*p, 0); - PeekMut::pop(p); - assert_eq!(vec.len(), 1); - } else { - unreachable!() - } + let mut p = vec.peek_mut().unwrap(); + assert_eq!(*p, 2); + *p = 0; + assert_eq!(*p, 0); + drop(p); + assert_eq!(vec, vec![1, 0]); + let p = vec.peek_mut().unwrap(); + let p = PeekMut::pop(p); + assert_eq!(p, 0); + assert_eq!(vec, vec![1]); } /// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments From 7ce81d145359aa230f8fc2d7de12650091508329 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:12:20 -0700 Subject: [PATCH 0583/1900] deref related cleanups in ref_prop --- compiler/rustc_mir_transform/src/ref_prop.rs | 33 ++++++++------------ 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 6f61215cee2d6..b9d6e74ecae89 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -195,10 +195,10 @@ fn compute_replacement<'tcx>( // including DEF. This violates the DEF dominates USE condition, and so is impossible. let is_constant_place = |place: Place<'_>| { // We only allow `Deref` as the first projection, to avoid surprises. - if place.projection.first() == Some(&PlaceElem::Deref) { + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() { // `place == (*some_local).xxx`, it is constant only if `some_local` is constant. // We approximate constness using SSAness. - ssa.is_ssa(place.local) && place.projection[1..].iter().all(PlaceElem::is_stable_offset) + ssa.is_ssa(place.local) && rest.iter().all(PlaceElem::is_stable_offset) } else { storage_live.has_single_storage(place.local) && place.projection[..].iter().all(PlaceElem::is_stable_offset) @@ -206,7 +206,7 @@ fn compute_replacement<'tcx>( }; let mut can_perform_opt = |target: Place<'tcx>, loc: Location| { - if target.projection.first() == Some(&PlaceElem::Deref) { + if target.is_indirect_first_projection() { // We are creating a reborrow. As `place.local` is a reference, removing the storage // statements should not make it much harder for LLVM to optimize. storage_to_remove.insert(target.local); @@ -266,7 +266,7 @@ fn compute_replacement<'tcx>( Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let mut place = *place; // Try to see through `place` in order to collapse reborrow chains. - if place.projection.first() == Some(&PlaceElem::Deref) + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() && let Value::Pointer(target, inner_needs_unique) = targets[place.local] // Only see through immutable reference and pointers, as we do not know yet if // mutable references are fully replaced. @@ -274,7 +274,7 @@ fn compute_replacement<'tcx>( // Only collapse chain if the pointee is definitely live. && can_perform_opt(target, location) { - place = target.project_deeper(&place.projection[1..], tcx); + place = target.project_deeper(rest, tcx); } assert_ne!(place.local, local); if is_constant_place(place) { @@ -323,7 +323,7 @@ fn compute_replacement<'tcx>( return; } - if place.projection.first() != Some(&PlaceElem::Deref) { + if !place.is_indirect_first_projection() { // This is not a dereference, nothing to do. return; } @@ -392,20 +392,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place: - // - if it's a reborrow, see through it; - // - if it's a direct borrow, increase `debuginfo.references`. + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() && let Value::Pointer(target, _) = self.targets[place.local] - && target.projection.iter().all(|p| p.can_use_in_debuginfo()) + && let &[PlaceElem::Deref] = &target.projection[..] { - if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() { - *place = Place::from(target.local).project_deeper(rest, self.tcx); - self.any_replacement = true; - } else { - break; - } + *place = Place::from(target.local); + self.any_replacement = true; } // Simplify eventual projections left inside `debuginfo`. @@ -414,9 +409,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { - if place.projection.first() != Some(&PlaceElem::Deref) { - return; - } + let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; let Value::Pointer(target, _) = self.targets[place.local] else { return }; @@ -432,7 +425,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { return; } - *place = target.project_deeper(&place.projection[1..], self.tcx); + *place = target.project_deeper(rest, self.tcx); self.any_replacement = true; } } From e1258e79d6cb709b26ded97d32de6c55f355e2aa Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 20:17:32 +0000 Subject: [PATCH 0584/1900] autodiff: Add basic TypeTree with NoTT flag Signed-off-by: Karan Janthe --- .../rustc_ast/src/expand/autodiff_attrs.rs | 17 +++- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 + compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_middle/src/error.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 80 +++++++++++++++++++ compiler/rustc_session/src/config.rs | 2 + compiler/rustc_session/src/options.rs | 3 +- tests/codegen-llvm/autodiff/typetree.rs | 33 ++++++++ .../autodiff/type-trees/nott-flag/nott.check | 3 + .../autodiff/type-trees/nott-flag/rmake.rs | 38 +++++++++ .../autodiff/type-trees/nott-flag/test.rs | 15 ++++ .../type-trees/nott-flag/with_tt.check | 3 + tests/ui/autodiff/flag_nott.rs | 19 +++++ 13 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/autodiff/typetree.rs create mode 100644 tests/run-make/autodiff/type-trees/nott-flag/nott.check create mode 100644 tests/run-make/autodiff/type-trees/nott-flag/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/nott-flag/test.rs create mode 100644 tests/run-make/autodiff/type-trees/nott-flag/with_tt.check create mode 100644 tests/ui/autodiff/flag_nott.rs diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index 33451f9974835..90f15753e99c9 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -6,6 +6,7 @@ use std::fmt::{self, Display, Formatter}; use std::str::FromStr; +use crate::expand::typetree::TypeTree; use crate::expand::{Decodable, Encodable, HashStable_Generic}; use crate::{Ty, TyKind}; @@ -84,6 +85,8 @@ pub struct AutoDiffItem { /// The name of the function being generated pub target: String, pub attrs: AutoDiffAttrs, + pub inputs: Vec, + pub output: TypeTree, } #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] @@ -275,14 +278,22 @@ impl AutoDiffAttrs { !matches!(self.mode, DiffMode::Error | DiffMode::Source) } - pub fn into_item(self, source: String, target: String) -> AutoDiffItem { - AutoDiffItem { source, target, attrs: self } + pub fn into_item( + self, + source: String, + target: String, + inputs: Vec, + output: TypeTree, + ) -> AutoDiffItem { + AutoDiffItem { source, target, inputs, output, attrs: self } } } impl fmt::Display for AutoDiffItem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Differentiating {} -> {}", self.source, self.target)?; - write!(f, " with attributes: {:?}", self.attrs) + write!(f, " with attributes: {:?}", self.attrs)?; + write!(f, " with inputs: {:?}", self.inputs)?; + write!(f, " with output: {:?}", self.output) } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 78107d95e5a70..5ac3a87c158e6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -563,6 +563,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) { config::AutoDiff::Enable => {} // We handle this below config::AutoDiff::NoPostopt => {} + // Disables TypeTree generation + config::AutoDiff::NoTT => {} } } // This helps with handling enums for now. diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 7730bddc0f12d..837acdadd579d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -766,6 +766,7 @@ fn test_unstable_options_tracking_hash() { tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); tracked!(autodiff, vec![AutoDiff::Enable]); + tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); tracked!( diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index e3e1393b5f9ae..ef014af7beb14 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -37,7 +37,6 @@ pub(crate) struct OpaqueHiddenTypeMismatch<'tcx> { pub sub: TypeMismatchReason, } -// FIXME(autodiff): I should get used somewhere #[derive(Diagnostic)] #[diag(middle_unsupported_union)] pub struct UnsupportedUnion { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0ffef393a33bd..df42c40031752 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -25,6 +25,7 @@ pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx}; +use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; @@ -2216,3 +2217,82 @@ pub struct DestructuredConst<'tcx> { pub variant: Option, pub fields: &'tcx [ty::Const<'tcx>], } + +/// Generate TypeTree information for autodiff. +/// This function creates TypeTree metadata that describes the memory layout +/// of function parameters and return types for Enzyme autodiff. +pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { + // Check if TypeTrees are disabled via NoTT flag + if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Check if this is actually a function type + if !fn_ty.is_fn() { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Get the function signature + let fn_sig = fn_ty.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + // Create TypeTrees for each input parameter + let mut args = vec![]; + for ty in sig.inputs().iter() { + let type_tree = typetree_from_ty(tcx, *ty); + args.push(type_tree); + } + + // Create TypeTree for return type + let ret = typetree_from_ty(tcx, sig.output()); + + FncTree { args, ret } +} + +/// Generate TypeTree for a specific type. +/// This function analyzes a Rust type and creates appropriate TypeTree metadata. +fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + // Handle basic scalar types + if ty.is_scalar() { + let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { + (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) + } else if ty.is_floating_point() { + match ty { + x if x == tcx.types.f32 => (Kind::Float, 4), + x if x == tcx.types.f64 => (Kind::Double, 8), + _ => return TypeTree::new(), // Unknown float type + } + } else { + // TODO(KMJ-007): Handle other scalar types if needed + return TypeTree::new(); + }; + + return TypeTree(vec![Type { + offset: -1, + size, + kind, + child: TypeTree::new() + }]); + } + + // Handle references and pointers + if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { + let inner_ty = if let Some(inner) = ty.builtin_deref(true) { + inner + } else { + // TODO(KMJ-007): Handle complex pointer types + return TypeTree::new(); + }; + + let child = typetree_from_ty(tcx, inner_ty); + return TypeTree(vec![Type { + offset: -1, + size: 8, // TODO(KMJ-007): Get actual pointer size from target + kind: Kind::Pointer, + child, + }]); + } + + // TODO(KMJ-007): Handle arrays, slices, structs, and other complex types + TypeTree::new() +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c9765..3a035fcf9e896 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -257,6 +257,8 @@ pub enum AutoDiff { LooseTypes, /// Runs Enzyme's aggressive inlining Inline, + /// Disable Type Tree + NoTT, } /// Settings for `-Z instrument-xray` flag. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 69facde693689..ee53dd39bc887 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -792,7 +792,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`"; pub(crate) const parse_offload: &str = "a comma separated list of settings: `Enable`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; @@ -1479,6 +1479,7 @@ pub mod parse { "PrintPasses" => AutoDiff::PrintPasses, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, + "NoTT" => AutoDiff::NoTT, _ => { // FIXME(ZuseZ4): print an error saying which value is not recognized return false; diff --git a/tests/codegen-llvm/autodiff/typetree.rs b/tests/codegen-llvm/autodiff/typetree.rs new file mode 100644 index 0000000000000..3ad38d581b9a1 --- /dev/null +++ b/tests/codegen-llvm/autodiff/typetree.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme + +// Test that basic autodiff still works with our TypeTree infrastructure +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_simple, Duplicated, Active)] +#[no_mangle] +#[inline(never)] +fn simple(x: &f64) -> f64 { + 2.0 * x +} + +// CHECK-LABEL: @simple +// CHECK: fmul double + +// The derivative function should be generated normally +// CHECK-LABEL: diffesimple +// CHECK: fadd fast double + +fn main() { + let x = std::hint::black_box(3.0); + let output = simple(&x); + assert_eq!(6.0, output); + + let mut df_dx = 0.0; + let output_ = d_simple(&x, &mut df_dx, 1.0); + assert_eq!(output, output_); + assert_eq!(2.0, df_dx); +} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/nott.check b/tests/run-make/autodiff/type-trees/nott-flag/nott.check new file mode 100644 index 0000000000000..56ef2f0bdf380 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/nott-flag/nott.check @@ -0,0 +1,3 @@ +// TODO(KMJ-007): Update this test when TypeTree integration is complete +// CHECK: square - {[-1]:Float@double} |{[-1]:Pointer}:{} +// CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs new file mode 100644 index 0000000000000..536164192dc06 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -0,0 +1,38 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Test with NoTT flag - should not generate TypeTree metadata + let output_nott = rustc() + .input("test.rs") + .arg("-Zautodiff=Enable,NoTT,PrintTAFn=square") + .arg("-Zautodiff=NoPostopt") + .opt_level("3") + .arg("-Clto=fat") + .arg("-g") + .run(); + + // Write output for NoTT case + rfs::write("nott.stdout", output_nott.stdout_utf8()); + + // Test without NoTT flag - should generate TypeTree metadata + let output_with_tt = rustc() + .input("test.rs") + .arg("-Zautodiff=Enable,PrintTAFn=square") + .arg("-Zautodiff=NoPostopt") + .opt_level("3") + .arg("-Clto=fat") + .arg("-g") + .run(); + + // Write output for TypeTree case + rfs::write("with_tt.stdout", output_with_tt.stdout_utf8()); + + // Verify NoTT output has minimal TypeTree info + llvm_filecheck().patterns("nott.check").stdin_buf(rfs::read("nott.stdout")).run(); + + // Verify normal output will have TypeTree info (once implemented) + llvm_filecheck().patterns("with_tt.check").stdin_buf(rfs::read("with_tt.stdout")).run(); +} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/test.rs b/tests/run-make/autodiff/type-trees/nott-flag/test.rs new file mode 100644 index 0000000000000..5c634eea035d9 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/nott-flag/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_square, Duplicated, Active)] +#[no_mangle] +fn square(x: &f64) -> f64 { + x * x +} + +fn main() { + let x = 2.0; + let mut dx = 0.0; + let _result = d_square(&x, &mut dx, 1.0); +} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check new file mode 100644 index 0000000000000..56ef2f0bdf380 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -0,0 +1,3 @@ +// TODO(KMJ-007): Update this test when TypeTree integration is complete +// CHECK: square - {[-1]:Float@double} |{[-1]:Pointer}:{} +// CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double} \ No newline at end of file diff --git a/tests/ui/autodiff/flag_nott.rs b/tests/ui/autodiff/flag_nott.rs new file mode 100644 index 0000000000000..7a97d892cd881 --- /dev/null +++ b/tests/ui/autodiff/flag_nott.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Zautodiff=Enable,NoTT +//@ needs-enzyme +//@ check-pass + +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +// Test that NoTT flag is accepted and doesn't cause compilation errors +#[autodiff_reverse(d_square, Duplicated, Active)] +fn square(x: &f64) -> f64 { + x * x +} + +fn main() { + let x = 2.0; + let mut dx = 0.0; + let result = d_square(&x, &mut dx, 1.0); +} \ No newline at end of file From 375e14ef491ac7bfa701e269b2815625abf2fca6 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 21:56:56 +0000 Subject: [PATCH 0585/1900] Add TypeTree metadata attachment for autodiff - Add F128 support to TypeTree Kind enum - Implement TypeTree FFI bindings and conversion functions - Add typetree.rs module for metadata attachment to LLVM functions - Integrate TypeTree generation with autodiff intrinsic pipeline - Support scalar types: f32, f64, integers, f16, f128 - Attach enzyme_type attributes as LLVM string metadata for Enzyme Signed-off-by: Karan Janthe --- compiler/rustc_ast/src/expand/typetree.rs | 1 + .../src/builder/autodiff.rs | 6 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 + compiler/rustc_codegen_llvm/src/lib.rs | 1 + .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 182 +++++++++++++++++- compiler/rustc_codegen_llvm/src/typetree.rs | 144 ++++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 19 +- 7 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/typetree.rs diff --git a/compiler/rustc_ast/src/expand/typetree.rs b/compiler/rustc_ast/src/expand/typetree.rs index 9a2dd2e85e0d6..e7b4f3aff413a 100644 --- a/compiler/rustc_ast/src/expand/typetree.rs +++ b/compiler/rustc_ast/src/expand/typetree.rs @@ -31,6 +31,7 @@ pub enum Kind { Half, Float, Double, + F128, Unknown, } diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index b66e3dfdeec29..c3485f563916f 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -1,6 +1,7 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; +use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; @@ -294,6 +295,7 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args: &[&'ll Value], attrs: AutoDiffAttrs, dest: PlaceRef<'tcx, &'ll Value>, + fnc_tree: FncTree, ) { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { @@ -370,6 +372,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args, ); + if !fnc_tree.args.is_empty() || !fnc_tree.ret.0.is_empty() { + crate::typetree::add_tt(cx.llmod, cx.llcx, fn_to_diff, fnc_tree); + } + let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); builder.store_to_place(call, dest.val); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e7f4a3570488e..3254b31865197 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1213,6 +1213,9 @@ fn codegen_autodiff<'ll, 'tcx>( &mut diff_attrs.input_activity, ); + let fnc_tree = + rustc_middle::ty::fnc_typetrees(tcx, fn_source.ty(tcx, TypingEnv::fully_monomorphized())); + // Build body generate_enzyme_call( bx, @@ -1223,6 +1226,7 @@ fn codegen_autodiff<'ll, 'tcx>( &val_arr, diff_attrs.clone(), result, + fnc_tree, ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6fb23d0984335..a5ac789285d47 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -67,6 +67,7 @@ mod llvm_util; mod mono_item; mod type_; mod type_of; +mod typetree; mod va_arg; mod value; diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 695435eb6daa7..12b6ffa3c0b5f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -3,9 +3,35 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; -use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; +use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value}; use crate::llvm::{Bool, Builder}; +// TypeTree types +pub(crate) type CTypeTreeRef = *mut EnzymeTypeTree; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(crate) struct EnzymeTypeTree { + _unused: [u8; 0], +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub(crate) enum CConcreteType { + DT_Anything = 0, + DT_Integer = 1, + DT_Pointer = 2, + DT_Half = 3, + DT_Float = 4, + DT_Double = 5, + DT_Unknown = 6, +} + +pub(crate) struct TypeTree { + pub(crate) inner: CTypeTreeRef, +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -68,10 +94,33 @@ pub(crate) mod Enzyme_AD { use libc::c_void; + use super::{CConcreteType, CTypeTreeRef, Context}; + unsafe extern "C" { pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); pub(crate) fn EnzymeSetCLString(arg1: *mut ::std::os::raw::c_void, arg2: *const c_char); } + + // TypeTree functions + unsafe extern "C" { + pub(crate) fn EnzymeNewTypeTree() -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef; + pub(crate) fn EnzymeFreeTypeTree(CTT: CTypeTreeRef); + pub(crate) fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool; + pub(crate) fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64); + pub(crate) fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef); + pub(crate) fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ); + pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; + pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); + } + unsafe extern "C" { static mut EnzymePrintPerf: c_void; static mut EnzymePrintActivity: c_void; @@ -141,6 +190,57 @@ pub(crate) use self::Fallback_AD::*; pub(crate) mod Fallback_AD { #![allow(unused_variables)] + use libc::c_char; + + use super::{CConcreteType, CTypeTreeRef, Context}; + + // TypeTree function fallbacks + pub(crate) unsafe fn EnzymeNewTypeTree() -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeFreeTypeTree(CTT: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToStringFree(arg1: *const c_char) { + unimplemented!() + } + pub(crate) fn set_inline(val: bool) { unimplemented!() } @@ -169,3 +269,83 @@ pub(crate) mod Fallback_AD { unimplemented!() } } + +impl TypeTree { + pub(crate) fn new() -> TypeTree { + let inner = unsafe { EnzymeNewTypeTree() }; + TypeTree { inner } + } + + pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree { + let inner = unsafe { EnzymeNewTypeTreeCT(t, ctx) }; + TypeTree { inner } + } + + pub(crate) fn merge(self, other: Self) -> Self { + unsafe { + EnzymeMergeTypeTree(self.inner, other.inner); + } + drop(other); + self + } + + #[must_use] + pub(crate) fn shift( + self, + layout: &str, + offset: isize, + max_size: isize, + add_offset: usize, + ) -> Self { + let layout = std::ffi::CString::new(layout).unwrap(); + + unsafe { + EnzymeTypeTreeShiftIndiciesEq( + self.inner, + layout.as_ptr(), + offset as i64, + max_size as i64, + add_offset as u64, + ); + } + + self + } +} + +impl Clone for TypeTree { + fn clone(&self) -> Self { + let inner = unsafe { EnzymeNewTypeTreeTR(self.inner) }; + TypeTree { inner } + } +} + +impl std::fmt::Display for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ptr = unsafe { EnzymeTypeTreeToString(self.inner) }; + let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; + match cstr.to_str() { + Ok(x) => write!(f, "{}", x)?, + Err(err) => write!(f, "could not parse: {}", err)?, + } + + // delete C string pointer + unsafe { + EnzymeTypeTreeToStringFree(ptr); + } + + Ok(()) + } +} + +impl std::fmt::Debug for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ::fmt(self, f) + } +} + +impl Drop for TypeTree { + fn drop(&mut self) { + unsafe { EnzymeFreeTypeTree(self.inner) } + } +} diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs new file mode 100644 index 0000000000000..434316464e626 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -0,0 +1,144 @@ +use std::ffi::{CString, c_char, c_uint}; + +use rustc_ast::expand::typetree::{FncTree, TypeTree as RustTypeTree}; + +use crate::attributes; +use crate::llvm::{self, Value}; + +/// Converts a Rust TypeTree to Enzyme's internal TypeTree format +/// +/// This function takes a Rust-side TypeTree (from rustc_ast::expand::typetree) +/// and converts it to Enzyme's internal C++ TypeTree representation that +/// Enzyme can understand during differentiation analysis. +#[cfg(llvm_enzyme)] +fn to_enzyme_typetree( + rust_typetree: RustTypeTree, + data_layout: &str, + llcx: &llvm::Context, +) -> llvm::TypeTree { + // Start with an empty TypeTree + let mut enzyme_tt = llvm::TypeTree::new(); + + // Convert each Type in the Rust TypeTree to Enzyme format + for rust_type in rust_typetree.0 { + let concrete_type = match rust_type.kind { + rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything, + rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer, + rustc_ast::expand::typetree::Kind::Pointer => llvm::CConcreteType::DT_Pointer, + rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half, + rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float, + rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double, + rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_Unknown, + rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, + }; + + // Create a TypeTree for this specific type + let type_tt = llvm::TypeTree::from_type(concrete_type, llcx); + + // Apply offset if specified + let type_tt = if rust_type.offset == -1 { + type_tt // -1 means everywhere/no specific offset + } else { + // Apply specific offset positioning + type_tt.shift(data_layout, rust_type.offset, rust_type.size as isize, 0) + }; + + // Merge this type into the main TypeTree + enzyme_tt = enzyme_tt.merge(type_tt); + } + + enzyme_tt +} + +#[cfg(not(llvm_enzyme))] +fn to_enzyme_typetree( + _rust_typetree: RustTypeTree, + _data_layout: &str, + _llcx: &llvm::Context, +) -> ! { + unimplemented!("TypeTree conversion not available without llvm_enzyme support") +} + +// Attaches TypeTree information to LLVM function as enzyme_type attributes. +#[cfg(llvm_enzyme)] +pub(crate) fn add_tt<'ll>( + llmod: &'ll llvm::Module, + llcx: &'ll llvm::Context, + fn_def: &'ll Value, + tt: FncTree, +) { + let inputs = tt.args; + let ret_tt: RustTypeTree = tt.ret; + + // Get LLVM data layout string for TypeTree conversion + let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; + let llvm_data_layout = + std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) + .expect("got a non-UTF8 data-layout from LLVM"); + + // Attribute name that Enzyme recognizes for TypeTree information + let attr_name = "enzyme_type"; + let c_attr_name = CString::new(attr_name).unwrap(); + + // Attach TypeTree attributes to each input parameter + // Enzyme uses these to understand parameter memory layouts during differentiation + for (i, input) in inputs.iter().enumerate() { + unsafe { + // Convert Rust TypeTree to Enzyme's internal format + let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx); + + // Serialize TypeTree to string format that Enzyme can parse + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + // Create LLVM string attribute with TypeTree information + let attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + // Attach attribute to the specific function parameter + // Note: ArgumentPlace uses 0-based indexing, but LLVM uses 1-based for arguments + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); + + // Free the C string to prevent memory leaks + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } + } + + // Attach TypeTree attribute to the return type + // Enzyme needs this to understand how to handle return value derivatives + unsafe { + let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx); + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + let ret_attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + // Attach to function return type + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); + + // Free the C string + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } +} + +// Fallback implementation when Enzyme is not available +#[cfg(not(llvm_enzyme))] +pub(crate) fn add_tt<'ll>( + _llmod: &'ll llvm::Module, + _llcx: &'ll llvm::Context, + _fn_def: &'ll Value, + _tt: FncTree, +) { + unimplemented!() +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index df42c40031752..e7130757f1db6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2251,36 +2251,29 @@ pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { /// Generate TypeTree for a specific type. /// This function analyzes a Rust type and creates appropriate TypeTree metadata. -fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { - // Handle basic scalar types +pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if ty.is_scalar() { let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) } else if ty.is_floating_point() { match ty { + x if x == tcx.types.f16 => (Kind::Half, 2), x if x == tcx.types.f32 => (Kind::Float, 4), x if x == tcx.types.f64 => (Kind::Double, 8), - _ => return TypeTree::new(), // Unknown float type + x if x == tcx.types.f128 => (Kind::F128, 16), + _ => return TypeTree::new(), } } else { - // TODO(KMJ-007): Handle other scalar types if needed return TypeTree::new(); }; - - return TypeTree(vec![Type { - offset: -1, - size, - kind, - child: TypeTree::new() - }]); + + return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); } - // Handle references and pointers if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { let inner_ty = if let Some(inner) = ty.builtin_deref(true) { inner } else { - // TODO(KMJ-007): Handle complex pointer types return TypeTree::new(); }; From beba6b9b8692d0db14c56292d4fb793fcd8ceb1e Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 21:57:37 +0000 Subject: [PATCH 0586/1900] Update TypeTree tests to verify metadata attachment - Fix nott-flag test to emit LLVM IR and check enzyme_type attributes - Replace TODO comments with actual TypeTree metadata verification - Test that NoTT flag properly disables TypeTree generation - Test that TypeTree enabled generates proper enzyme_type attributes Signed-off-by: Karan Janthe --- .../autodiff/type-trees/nott-flag/nott.check | 8 ++-- .../autodiff/type-trees/nott-flag/rmake.rs | 42 +++++++++---------- .../type-trees/nott-flag/with_tt.check | 7 ++-- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/tests/run-make/autodiff/type-trees/nott-flag/nott.check b/tests/run-make/autodiff/type-trees/nott-flag/nott.check index 56ef2f0bdf380..8d23e2ee31957 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/nott.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/nott.check @@ -1,3 +1,5 @@ -// TODO(KMJ-007): Update this test when TypeTree integration is complete -// CHECK: square - {[-1]:Float@double} |{[-1]:Pointer}:{} -// CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double} \ No newline at end of file +// Check that enzyme_type attributes are NOT present when NoTT flag is used +// This verifies the NoTT flag correctly disables TypeTree metadata + +CHECK: define{{.*}}@square +CHECK-NOT: "enzyme_type" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index 536164192dc06..bab863ca9ff29 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -5,34 +5,32 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Test with NoTT flag - should not generate TypeTree metadata - let output_nott = rustc() + rustc() .input("test.rs") - .arg("-Zautodiff=Enable,NoTT,PrintTAFn=square") - .arg("-Zautodiff=NoPostopt") - .opt_level("3") - .arg("-Clto=fat") - .arg("-g") + .arg("-Zautodiff=Enable,NoTT") + .emit("llvm-ir") + .arg("-o") + .arg("nott.ll") .run(); - // Write output for NoTT case - rfs::write("nott.stdout", output_nott.stdout_utf8()); - // Test without NoTT flag - should generate TypeTree metadata - let output_with_tt = rustc() + rustc() .input("test.rs") - .arg("-Zautodiff=Enable,PrintTAFn=square") - .arg("-Zautodiff=NoPostopt") - .opt_level("3") - .arg("-Clto=fat") - .arg("-g") + .arg("-Zautodiff=Enable") + .emit("llvm-ir") + .arg("-o") + .arg("with_tt.ll") .run(); - // Write output for TypeTree case - rfs::write("with_tt.stdout", output_with_tt.stdout_utf8()); - - // Verify NoTT output has minimal TypeTree info - llvm_filecheck().patterns("nott.check").stdin_buf(rfs::read("nott.stdout")).run(); + // Verify NoTT version does NOT have enzyme_type attributes + llvm_filecheck() + .patterns("nott.check") + .stdin_buf(rfs::read("nott.ll")) + .run(); - // Verify normal output will have TypeTree info (once implemented) - llvm_filecheck().patterns("with_tt.check").stdin_buf(rfs::read("with_tt.stdout")).run(); + // Verify TypeTree version DOES have enzyme_type attributes + llvm_filecheck() + .patterns("with_tt.check") + .stdin_buf(rfs::read("with_tt.ll")) + .run(); } \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check index 56ef2f0bdf380..53eafc10a65da 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -1,3 +1,4 @@ -// TODO(KMJ-007): Update this test when TypeTree integration is complete -// CHECK: square - {[-1]:Float@double} |{[-1]:Pointer}:{} -// CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double} \ No newline at end of file +// Check that enzyme_type attributes are present when TypeTree is enabled +// This verifies our TypeTree metadata attachment is working + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file From 5d3ebc3804299503387b7ea1427f1619d410c2b2 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 21:57:54 +0000 Subject: [PATCH 0587/1900] Add TypeTree tests for scalar types - Add specific tests for f32, f64, i32, f16, f128 TypeTree generation - Verify correct enzyme_type metadata for each scalar type - Ensure TypeTree metadata matches expected Enzyme format Signed-off-by: Karan Janthe --- .../scalar-types/f128-typetree/f128.check | 4 ++++ .../scalar-types/f128-typetree/rmake.rs | 12 ++++++++++++ .../type-trees/scalar-types/f128-typetree/test.rs | 15 +++++++++++++++ .../scalar-types/f16-typetree/f16.check | 4 ++++ .../type-trees/scalar-types/f16-typetree/rmake.rs | 12 ++++++++++++ .../type-trees/scalar-types/f16-typetree/test.rs | 15 +++++++++++++++ .../scalar-types/f32-typetree/f32.check | 4 ++++ .../type-trees/scalar-types/f32-typetree/rmake.rs | 12 ++++++++++++ .../type-trees/scalar-types/f32-typetree/test.rs | 15 +++++++++++++++ .../scalar-types/f64-typetree/f64.check | 4 ++++ .../type-trees/scalar-types/f64-typetree/rmake.rs | 12 ++++++++++++ .../type-trees/scalar-types/f64-typetree/test.rs | 15 +++++++++++++++ .../scalar-types/i32-typetree/i32.check | 4 ++++ .../type-trees/scalar-types/i32-typetree/rmake.rs | 12 ++++++++++++ .../type-trees/scalar-types/i32-typetree/test.rs | 14 ++++++++++++++ 15 files changed, 154 insertions(+) create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/test.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/test.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/test.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/test.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check new file mode 100644 index 0000000000000..31cef113420db --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check @@ -0,0 +1,4 @@ +; Check that f128 TypeTree metadata is correctly generated +; f128 maps to Unknown in our current implementation since CConcreteType doesn't have DT_F128 + +CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f128{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs new file mode 100644 index 0000000000000..44320ecdd5714 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs @@ -0,0 +1,12 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Compile with TypeTree enabled and emit LLVM IR + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + + // Check that f128 TypeTree metadata is correctly generated + llvm_filecheck().patterns("f128.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/test.rs new file mode 100644 index 0000000000000..5c71baa3e6999 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff, f128)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_f128(x: &f128) -> f128 { + *x * *x +} + +fn main() { + let x = 2.0_f128; + let mut dx = 0.0_f128; + let _result = d_test(&x, &mut dx, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check new file mode 100644 index 0000000000000..97a3964a569b1 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check @@ -0,0 +1,4 @@ +; Check that f16 TypeTree metadata is correctly generated +; Should show Half for f16 values and Pointer for references + +CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f16{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs new file mode 100644 index 0000000000000..0aebdbf55209b --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs @@ -0,0 +1,12 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Compile with TypeTree enabled and emit LLVM IR + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + + // Check that f16 TypeTree metadata is correctly generated + llvm_filecheck().patterns("f16.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/test.rs new file mode 100644 index 0000000000000..6b68e8252f4c0 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff, f16)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_f16(x: &f16) -> f16 { + *x * *x +} + +fn main() { + let x = 2.0_f16; + let mut dx = 0.0_f16; + let _result = d_test(&x, &mut dx, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check new file mode 100644 index 0000000000000..b32d5086d07d1 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check @@ -0,0 +1,4 @@ +; Check that f32 TypeTree metadata is correctly generated +; Should show Float@float for f32 values and Pointer for references + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs new file mode 100644 index 0000000000000..ee3ab753bf505 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs @@ -0,0 +1,12 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Compile with TypeTree enabled and emit LLVM IR + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + + // Check that f32 TypeTree metadata is correctly generated + llvm_filecheck().patterns("f32.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/test.rs new file mode 100644 index 0000000000000..56c118399ee4b --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_f32(x: &f32) -> f32 { + x * x +} + +fn main() { + let x = 2.0_f32; + let mut dx = 0.0_f32; + let _result = d_test(&x, &mut dx, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check new file mode 100644 index 0000000000000..0e9d9c03a0f86 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check @@ -0,0 +1,4 @@ +; Check that f64 TypeTree metadata is correctly generated +; Should show Float@double for f64 values and Pointer for references + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs new file mode 100644 index 0000000000000..5fac9b23bc80f --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs @@ -0,0 +1,12 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Compile with TypeTree enabled and emit LLVM IR + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + + // Check that f64 TypeTree metadata is correctly generated + llvm_filecheck().patterns("f64.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/test.rs new file mode 100644 index 0000000000000..235360b76b23d --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_f64(x: &f64) -> f64 { + x * x +} + +fn main() { + let x = 2.0_f64; + let mut dx = 0.0_f64; + let _result = d_test(&x, &mut dx, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check new file mode 100644 index 0000000000000..aa8e5223e8ed5 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check @@ -0,0 +1,4 @@ +; Check that i32 TypeTree metadata is correctly generated +; Should show Integer for i32 values (integers are typically Const in autodiff) + +CHECK: define{{.*}}"enzyme_type"="{[]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[]:Integer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs new file mode 100644 index 0000000000000..a40fd55d88adf --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs @@ -0,0 +1,12 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // Compile with TypeTree enabled and emit LLVM IR + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + + // Check that i32 TypeTree metadata is correctly generated + llvm_filecheck().patterns("i32.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs new file mode 100644 index 0000000000000..530c137f62b9c --- /dev/null +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs @@ -0,0 +1,14 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Const, Active)] +#[no_mangle] +fn test_i32(x: i32) -> i32 { + x * x +} + +fn main() { + let x = 5_i32; + let _result = d_test(x, 1); +} From 664e83b3e76b51fb5192e74a64eef3bc5bbd4e32 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 23:10:48 +0000 Subject: [PATCH 0588/1900] added typetree support for memcpy --- compiler/rustc_codegen_gcc/src/builder.rs | 1 + .../rustc_codegen_gcc/src/intrinsic/mod.rs | 1 + compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/builder.rs | 15 ++++++- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 + compiler/rustc_codegen_llvm/src/typetree.rs | 20 ++++------ compiler/rustc_codegen_llvm/src/va_arg.rs | 1 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + .../rustc_codegen_ssa/src/mir/intrinsic.rs | 2 +- .../rustc_codegen_ssa/src/mir/statement.rs | 2 +- .../rustc_codegen_ssa/src/traits/builder.rs | 3 +- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 4 +- tests/codegen-llvm/autodiff/typetree.rs | 2 +- .../memcpy-typetree/memcpy-ir.check | 8 ++++ .../type-trees/memcpy-typetree/memcpy.check | 13 +++++++ .../type-trees/memcpy-typetree/memcpy.rs | 36 +++++++++++++++++ .../type-trees/memcpy-typetree/rmake.rs | 39 +++++++++++++++++++ .../autodiff/type-trees/nott-flag/rmake.rs | 14 ++----- .../autodiff/type-trees/nott-flag/test.rs | 2 +- tests/ui/autodiff/flag_nott.rs | 2 +- 21 files changed, 135 insertions(+), 34 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f7a7a3f8c7e35..5657620879ca1 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1383,6 +1383,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _src_align: Align, size: RValue<'gcc>, flags: MemFlags, + _tt: Option, // Autodiff TypeTrees are LLVM-only, ignored in GCC backend ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_size_t(), false); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 84fa56cf90308..3b897a1983503 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -771,6 +771,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(self.layout.size.bytes()), MemFlags::empty(), + None, ); bx.lifetime_end(scratch, scratch_size); diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 11be704116790..61fadad606613 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -246,6 +246,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); bx.lifetime_end(llscratch, scratch_size); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0f17cc9063a87..83a9cf620f109 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2,6 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; +use rustc_ast::expand::typetree::FncTree; pub(crate) mod autodiff; pub(crate) mod gpu_offload; @@ -1107,11 +1108,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align: Align, size: &'ll Value, flags: MemFlags, + tt: Option, ) { 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); - unsafe { + let memcpy = unsafe { llvm::LLVMRustBuildMemCpy( self.llbuilder, dst, @@ -1120,7 +1122,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align.bytes() as c_uint, size, is_volatile, - ); + ) + }; + + // TypeTree metadata for memcpy is especially important: when Enzyme encounters + // a memcpy during autodiff, it needs to know the structure of the data being + // copied to properly track derivatives. For example, copying an array of floats + // vs. copying a struct with mixed types requires different derivative handling. + // The TypeTree tells Enzyme exactly what memory layout to expect. + if let Some(tt) = tt { + crate::typetree::add_tt(self.cx().llmod, self.cx().llcx, memcpy, tt); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 12b6ffa3c0b5f..9dd84ad9a4dc3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -25,6 +25,7 @@ pub(crate) enum CConcreteType { DT_Half = 3, DT_Float = 4, DT_Double = 5, + // FIXME(KMJ-007): handle f128 using long double here(https://github.com/EnzymeAD/Enzyme/issues/1600) DT_Unknown = 6, } diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 434316464e626..8e6272a186b88 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -1,8 +1,11 @@ -use std::ffi::{CString, c_char, c_uint}; - -use rustc_ast::expand::typetree::{FncTree, TypeTree as RustTypeTree}; +use rustc_ast::expand::typetree::FncTree; +#[cfg(llvm_enzyme)] +use { + crate::attributes, + rustc_ast::expand::typetree::TypeTree as RustTypeTree, + std::ffi::{CString, c_char, c_uint}, +}; -use crate::attributes; use crate::llvm::{self, Value}; /// Converts a Rust TypeTree to Enzyme's internal TypeTree format @@ -50,15 +53,6 @@ fn to_enzyme_typetree( enzyme_tt } -#[cfg(not(llvm_enzyme))] -fn to_enzyme_typetree( - _rust_typetree: RustTypeTree, - _data_layout: &str, - _llcx: &llvm::Context, -) -> ! { - unimplemented!("TypeTree conversion not available without llvm_enzyme support") -} - // Attaches TypeTree information to LLVM function as enzyme_type attributes. #[cfg(llvm_enzyme)] pub(crate) fn add_tt<'ll>( diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index ab08125217ff5..d48c7cf874a0a 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -738,6 +738,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( src_align, bx.const_u32(layout.layout.size().bytes() as u32), MemFlags::empty(), + None, ); tmp } else { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1b218a0d33956..b2dc4fe32b0fe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1626,6 +1626,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); // ...and then load it with the ABI type. llval = load_cast(bx, cast, llscratch, scratch_align); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 3c667b8e88203..befa00c6861ed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -30,7 +30,7 @@ fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if allow_overlap { bx.memmove(dst, align, src, align, size, flags); } else { - bx.memcpy(dst, align, src, align, size, flags); + bx.memcpy(dst, align, src, align, size, flags, None); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index f164e0f912373..0a50d7f18dbef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let align = pointee_layout.align; let dst = dst_val.immediate(); let src = src_val.immediate(); - bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty()); + bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 4a5694e97fa20..60296e36e0c1a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -451,6 +451,7 @@ pub trait BuilderMethods<'a, 'tcx>: src_align: Align, size: Self::Value, flags: MemFlags, + tt: Option, ); fn memmove( &mut self, @@ -507,7 +508,7 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst.with_type(layout), flags); } else if !layout.is_zst() { let bytes = self.const_usize(layout.size.bytes()); - self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags); + self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None); } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 837acdadd579d..0dc5b5af3aca0 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -765,7 +765,6 @@ fn test_unstable_options_tracking_hash() { tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); - tracked!(autodiff, vec![AutoDiff::Enable]); tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e7130757f1db6..741b5d7fd4e06 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2280,12 +2280,12 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let child = typetree_from_ty(tcx, inner_ty); return TypeTree(vec![Type { offset: -1, - size: 8, // TODO(KMJ-007): Get actual pointer size from target + size: tcx.data_layout.pointer_size().bytes_usize(), kind: Kind::Pointer, child, }]); } - // TODO(KMJ-007): Handle arrays, slices, structs, and other complex types + // FIXME(KMJ-007): Handle arrays, slices, structs, and other complex types TypeTree::new() } diff --git a/tests/codegen-llvm/autodiff/typetree.rs b/tests/codegen-llvm/autodiff/typetree.rs index 3ad38d581b9a1..1cb0c2fb68be3 100644 --- a/tests/codegen-llvm/autodiff/typetree.rs +++ b/tests/codegen-llvm/autodiff/typetree.rs @@ -30,4 +30,4 @@ fn main() { let output_ = d_simple(&x, &mut df_dx, 1.0); assert_eq!(output, output_); assert_eq!(2.0, df_dx); -} \ No newline at end of file +} diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check new file mode 100644 index 0000000000000..3a59f06dda1e4 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check @@ -0,0 +1,8 @@ +; Check that enzyme_type attributes are present in the LLVM IR function definition +; This verifies our TypeTree system correctly attaches metadata for Enzyme + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[]:Pointer}" + +; Check that llvm.memcpy exists (either call or declare) +CHECK: {{(call|declare).*}}@llvm.memcpy + diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check new file mode 100644 index 0000000000000..ae70830297a78 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check @@ -0,0 +1,13 @@ +CHECK: force_memcpy + +CHECK: @llvm.memcpy.p0.p0.i64 + +CHECK: test_memcpy - {[-1]:Float@double} |{[-1]:Pointer}:{} + +CHECK-DAG: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double, [-1,24]:Float@double} + +CHECK-DAG: load double{{.*}}: {[-1]:Float@double} + +CHECK-DAG: fmul double{{.*}}: {[-1]:Float@double} + +CHECK-DAG: fadd double{{.*}}: {[-1]:Float@double} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs new file mode 100644 index 0000000000000..3c1029190c88a --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs @@ -0,0 +1,36 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; +use std::ptr; + +#[inline(never)] +fn force_memcpy(src: *const f64, dst: *mut f64, count: usize) { + unsafe { + ptr::copy_nonoverlapping(src, dst, count); + } +} + +#[autodiff_reverse(d_test_memcpy, Duplicated, Active)] +#[no_mangle] +fn test_memcpy(input: &[f64; 128]) -> f64 { + let mut local_data = [0.0f64; 128]; + + // Use a separate function to prevent inlining and optimization + force_memcpy(input.as_ptr(), local_data.as_mut_ptr(), 128); + + // Sum only first few elements to keep the computation simple + local_data[0] * local_data[0] + + local_data[1] * local_data[1] + + local_data[2] * local_data[2] + + local_data[3] * local_data[3] +} + +fn main() { + let input = [1.0; 128]; + let mut d_input = [0.0; 128]; + let result = test_memcpy(&input); + let result_d = d_test_memcpy(&input, &mut d_input, 1.0); + + assert_eq!(result, result_d); + println!("Memcpy test passed: result = {}", result); +} diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs new file mode 100644 index 0000000000000..b4c650330fe94 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs @@ -0,0 +1,39 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // First, compile to LLVM IR to check for enzyme_type attributes + let _ir_output = rustc() + .input("memcpy.rs") + .arg("-Zautodiff=Enable") + .arg("-Zautodiff=NoPostopt") + .opt_level("0") + .arg("--emit=llvm-ir") + .arg("-o") + .arg("main.ll") + .run(); + + // Then compile with TypeTree analysis output for the existing checks + let output = rustc() + .input("memcpy.rs") + .arg("-Zautodiff=Enable,PrintTAFn=test_memcpy") + .arg("-Zautodiff=NoPostopt") + .opt_level("3") + .arg("-Clto=fat") + .arg("-g") + .run(); + + let stdout = output.stdout_utf8(); + let stderr = output.stderr_utf8(); + let ir_content = rfs::read_to_string("main.ll"); + + rfs::write("memcpy.stdout", &stdout); + rfs::write("memcpy.stderr", &stderr); + rfs::write("main.ir", &ir_content); + + llvm_filecheck().patterns("memcpy.check").stdin_buf(stdout).run(); + + llvm_filecheck().patterns("memcpy-ir.check").stdin_buf(ir_content).run(); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index bab863ca9ff29..de540b990cabd 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -23,14 +23,8 @@ fn main() { .run(); // Verify NoTT version does NOT have enzyme_type attributes - llvm_filecheck() - .patterns("nott.check") - .stdin_buf(rfs::read("nott.ll")) - .run(); - + llvm_filecheck().patterns("nott.check").stdin_buf(rfs::read("nott.ll")).run(); + // Verify TypeTree version DOES have enzyme_type attributes - llvm_filecheck() - .patterns("with_tt.check") - .stdin_buf(rfs::read("with_tt.ll")) - .run(); -} \ No newline at end of file + llvm_filecheck().patterns("with_tt.check").stdin_buf(rfs::read("with_tt.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/test.rs b/tests/run-make/autodiff/type-trees/nott-flag/test.rs index 5c634eea035d9..de3549c37c679 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/test.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/test.rs @@ -12,4 +12,4 @@ fn main() { let x = 2.0; let mut dx = 0.0; let _result = d_square(&x, &mut dx, 1.0); -} \ No newline at end of file +} diff --git a/tests/ui/autodiff/flag_nott.rs b/tests/ui/autodiff/flag_nott.rs index 7a97d892cd881..faa9949fe8167 100644 --- a/tests/ui/autodiff/flag_nott.rs +++ b/tests/ui/autodiff/flag_nott.rs @@ -16,4 +16,4 @@ fn main() { let x = 2.0; let mut dx = 0.0; let result = d_square(&x, &mut dx, 1.0); -} \ No newline at end of file +} From b8bb2e8bfec298aecec54f67e8ea3deda471eafa Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Wed, 27 Aug 2025 05:58:42 +0000 Subject: [PATCH 0589/1900] typo: allow EnzymeTypeTreeShiftIndiciesEq --- typos.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/typos.toml b/typos.toml index b0ff48f8fa28b..c7d5b0000b9d7 100644 --- a/typos.toml +++ b/typos.toml @@ -53,6 +53,7 @@ ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC = "ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC" ERROR_MCA_OCCURED = "ERROR_MCA_OCCURED" ERRNO_ACCES = "ERRNO_ACCES" tolen = "tolen" +EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq" [default] extend-ignore-words-re = [ From d89ee858ab80f2c922c117c95b20264be8b84e3c Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Wed, 27 Aug 2025 18:19:55 +0000 Subject: [PATCH 0590/1900] enzyme submodule updated --- src/llvm-project | 2 +- src/tools/cargo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm-project b/src/llvm-project index 333793696b0a0..2a22c8014390b 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 333793696b0a08002acac6af983adc7a5233fc56 +Subproject commit 2a22c8014390bdf387d8ed26005fee3187fb98bd diff --git a/src/tools/cargo b/src/tools/cargo index 966f94733bbc9..a4bd03c92d3b9 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 +Subproject commit a4bd03c92d3b977909953d06cc45e263bd7ca7d8 From 54f9376660707d4ca9fce51fd423658f75128ac4 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Wed, 27 Aug 2025 18:23:54 +0000 Subject: [PATCH 0591/1900] autodiff: f128 support added for typetree --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 2 +- compiler/rustc_codegen_llvm/src/typetree.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 9dd84ad9a4dc3..1596dc4837978 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -25,8 +25,8 @@ pub(crate) enum CConcreteType { DT_Half = 3, DT_Float = 4, DT_Double = 5, - // FIXME(KMJ-007): handle f128 using long double here(https://github.com/EnzymeAD/Enzyme/issues/1600) DT_Unknown = 6, + DT_FP128 = 9, } pub(crate) struct TypeTree { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 8e6272a186b88..8c0d255bba84e 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -31,7 +31,7 @@ fn to_enzyme_typetree( rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half, rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float, rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double, - rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_Unknown, + rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_FP128, rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, }; From 31541feb6f7e46c23141bdeb3e35ecd305bf8762 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Mon, 1 Sep 2025 05:05:49 +0000 Subject: [PATCH 0592/1900] autodiff: add TypeTree support for arrays --- compiler/rustc_middle/src/ty/mod.rs | 42 ++++++++++++++++++- .../type-trees/array-typetree/array.check | 4 ++ .../type-trees/array-typetree/rmake.rs | 9 ++++ .../type-trees/array-typetree/test.rs | 15 +++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/autodiff/type-trees/array-typetree/array.check create mode 100644 tests/run-make/autodiff/type-trees/array-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/array-typetree/test.rs diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 741b5d7fd4e06..02a4e4e2b1592 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2286,6 +2286,46 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { }]); } - // FIXME(KMJ-007): Handle arrays, slices, structs, and other complex types + if ty.is_array() { + if let ty::Array(element_ty, len_const) = ty.kind() { + let len = len_const.try_to_target_usize(tcx).unwrap_or(0); + if len == 0 { + return TypeTree::new(); + } + + let element_tree = typetree_from_ty(tcx, *element_ty); + + let element_layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*element_ty)) + .ok() + .map(|layout| layout.size.bytes_usize()) + .unwrap_or(0); + + if element_layout == 0 { + return TypeTree::new(); + } + + let mut types = Vec::new(); + for i in 0..len { + let base_offset = (i as usize * element_layout) as isize; + + for elem_type in &element_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + base_offset + } else { + base_offset + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + } + + return TypeTree(types); + } + } + TypeTree::new() } diff --git a/tests/run-make/autodiff/type-trees/array-typetree/array.check b/tests/run-make/autodiff/type-trees/array-typetree/array.check new file mode 100644 index 0000000000000..7513458b8ab81 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/array-typetree/array.check @@ -0,0 +1,4 @@ +; Check that array TypeTree metadata is correctly generated +; Should show Float@double at each array element offset (0, 8, 16, 24, 32 bytes) + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_array{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs new file mode 100644 index 0000000000000..20b6a06690625 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("array.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/array-typetree/test.rs b/tests/run-make/autodiff/type-trees/array-typetree/test.rs new file mode 100644 index 0000000000000..f54ebf5a4c7b5 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/array-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_array(arr: &[f64; 5]) -> f64 { + arr[0] + arr[1] + arr[2] + arr[3] + arr[4] +} + +fn main() { + let arr = [1.0, 2.0, 3.0, 4.0, 5.0]; + let mut d_arr = [0.0; 5]; + let _result = d_test(&arr, &mut d_arr, 1.0); +} From be3617b04031c7d4f227ce54c4ce6ceeae00981c Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Mon, 1 Sep 2025 06:17:34 +0000 Subject: [PATCH 0593/1900] autodiff: slice support in typetree --- compiler/rustc_middle/src/ty/mod.rs | 7 +++++++ .../autodiff/type-trees/slice-typetree/rmake.rs | 9 +++++++++ .../type-trees/slice-typetree/slice.check | 4 ++++ .../autodiff/type-trees/slice-typetree/test.rs | 16 ++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/slice-typetree/slice.check create mode 100644 tests/run-make/autodiff/type-trees/slice-typetree/test.rs diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 02a4e4e2b1592..82a41f403f8b4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2327,5 +2327,12 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { } } + if ty.is_slice() { + if let ty::Slice(element_ty) = ty.kind() { + let element_tree = typetree_from_ty(tcx, *element_ty); + return element_tree; + } + } + TypeTree::new() } diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs new file mode 100644 index 0000000000000..b81fb50bf1a75 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("slice.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/slice.check b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check new file mode 100644 index 0000000000000..8fc9e9c4f1b17 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check @@ -0,0 +1,4 @@ +; Check that slice TypeTree metadata is correctly generated +; Should show Float@double for slice elements + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_slice{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/test.rs b/tests/run-make/autodiff/type-trees/slice-typetree/test.rs new file mode 100644 index 0000000000000..7117fa3844f59 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/slice-typetree/test.rs @@ -0,0 +1,16 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_slice(slice: &[f64]) -> f64 { + slice.iter().sum() +} + +fn main() { + let arr = [1.0, 2.0, 3.0, 4.0, 5.0]; + let slice = &arr[..]; + let mut d_slice = [0.0; 5]; + let _result = d_test(slice, &mut d_slice[..], 1.0); +} From 7c5fbfbdbbb389462e0ffb936ba9b16cffbce6ed Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Mon, 1 Sep 2025 16:14:31 +0000 Subject: [PATCH 0594/1900] autodiff: tuple support in typetree --- compiler/rustc_middle/src/ty/mod.rs | 36 +++++++++++++++++++ .../type-trees/tuple-typetree/rmake.rs | 9 +++++ .../type-trees/tuple-typetree/test.rs | 15 ++++++++ .../type-trees/tuple-typetree/tuple.check | 4 +++ 4 files changed, 64 insertions(+) create mode 100644 tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/tuple-typetree/test.rs create mode 100644 tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 82a41f403f8b4..c0a091551c9eb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2334,5 +2334,41 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { } } + if let ty::Tuple(tuple_types) = ty.kind() { + if tuple_types.is_empty() { + return TypeTree::new(); + } + + let mut types = Vec::new(); + let mut current_offset = 0; + + for tuple_ty in tuple_types.iter() { + let element_tree = typetree_from_ty(tcx, tuple_ty); + + let element_layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) + .ok() + .map(|layout| layout.size.bytes_usize()) + .unwrap_or(0); + + for elem_type in &element_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + current_offset as isize + } else { + current_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + current_offset += element_layout; + } + + return TypeTree(types); + } + TypeTree::new() } diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs new file mode 100644 index 0000000000000..76913828901c6 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("tuple.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/test.rs b/tests/run-make/autodiff/type-trees/tuple-typetree/test.rs new file mode 100644 index 0000000000000..32187b587a383 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/test.rs @@ -0,0 +1,15 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_tuple(tuple: &(f64, f64, f64)) -> f64 { + tuple.0 + tuple.1 * 2.0 + tuple.2 * 3.0 +} + +fn main() { + let tuple = (1.0, 2.0, 3.0); + let mut d_tuple = (0.0, 0.0, 0.0); + let _result = d_test(&tuple, &mut d_tuple, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check new file mode 100644 index 0000000000000..50aa25e96aedd --- /dev/null +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check @@ -0,0 +1,4 @@ +; Check that tuple TypeTree metadata is correctly generated +; Should show Float@double at offsets 0, 8, 16 for (f64, f64, f64) + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_tuple{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file From 574f0b97d6f30cd6cedb165fde13cdec176611b8 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Mon, 1 Sep 2025 16:28:14 +0000 Subject: [PATCH 0595/1900] autodiff: struct support in typetree --- compiler/rustc_middle/src/ty/mod.rs | 32 +++++++++++++++++++ .../type-trees/struct-typetree/rmake.rs | 9 ++++++ .../type-trees/struct-typetree/struct.check | 4 +++ .../type-trees/struct-typetree/test.rs | 22 +++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/struct-typetree/struct.check create mode 100644 tests/run-make/autodiff/type-trees/struct-typetree/test.rs diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c0a091551c9eb..703d417f96db9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2370,5 +2370,37 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { return TypeTree(types); } + if let ty::Adt(adt_def, args) = ty.kind() { + if adt_def.is_struct() { + let struct_layout = + tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); + if let Ok(layout) = struct_layout { + let mut types = Vec::new(); + + for (field_idx, field_def) in adt_def.all_fields().enumerate() { + let field_ty = field_def.ty(tcx, args); + let field_tree = typetree_from_ty(tcx, field_ty); + + let field_offset = layout.fields.offset(field_idx).bytes_usize(); + + for elem_type in &field_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + field_offset as isize + } else { + field_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + } + + return TypeTree(types); + } + } + } + TypeTree::new() } diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs new file mode 100644 index 0000000000000..0af1b65ee181b --- /dev/null +++ b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("struct.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/struct.check b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check new file mode 100644 index 0000000000000..2f763f18c1cee --- /dev/null +++ b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check @@ -0,0 +1,4 @@ +; Check that struct TypeTree metadata is correctly generated +; Should show Float@double at offsets 0, 8, 16 for Point struct fields + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_struct{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/test.rs b/tests/run-make/autodiff/type-trees/struct-typetree/test.rs new file mode 100644 index 0000000000000..cbe7b10e40974 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/struct-typetree/test.rs @@ -0,0 +1,22 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[repr(C)] +struct Point { + x: f64, + y: f64, + z: f64, +} + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +fn test_struct(point: &Point) -> f64 { + point.x + point.y * 2.0 + point.z * 3.0 +} + +fn main() { + let point = Point { x: 1.0, y: 2.0, z: 3.0 }; + let mut d_point = Point { x: 0.0, y: 0.0, z: 0.0 }; + let _result = d_test(&point, &mut d_point, 1.0); +} From 4f3f0f48e7b1e61818b2bcbe4451f89bb4f47049 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Thu, 4 Sep 2025 11:17:34 +0000 Subject: [PATCH 0596/1900] autodiff: fixed test to be more precise for type tree checking --- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 23 ++++++ compiler/rustc_codegen_llvm/src/typetree.rs | 70 ++++++++----------- compiler/rustc_middle/src/ty/mod.rs | 36 +++------- .../type-trees/array-typetree/array.check | 2 +- .../memcpy-typetree/memcpy-ir.check | 2 +- .../mixed-struct-typetree/mixed.check | 2 + .../type-trees/mixed-struct-typetree/rmake.rs | 16 +++++ .../type-trees/mixed-struct-typetree/test.rs | 23 ++++++ .../type-trees/nott-flag/with_tt.check | 2 +- .../scalar-types/f128-typetree/f128.check | 4 +- .../scalar-types/f16-typetree/f16.check | 4 +- .../scalar-types/f32-typetree/f32.check | 2 +- .../scalar-types/f64-typetree/f64.check | 2 +- .../scalar-types/i32-typetree/i32.check | 4 +- .../scalar-types/i32-typetree/test.rs | 7 +- .../type-trees/slice-typetree/slice.check | 2 +- .../type-trees/struct-typetree/struct.check | 2 +- .../type-trees/tuple-typetree/tuple.check | 2 +- .../type-trees/type-analysis/vec/vec.check | 2 +- 19 files changed, 120 insertions(+), 87 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 1596dc4837978..e63043b21227f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -118,6 +118,13 @@ pub(crate) mod Enzyme_AD { max_size: i64, add_offset: u64, ); + pub(crate) fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); } @@ -234,6 +241,16 @@ pub(crate) mod Fallback_AD { unimplemented!() } + pub(crate) unsafe fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ) { + unimplemented!() + } + pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { unimplemented!() } @@ -312,6 +329,12 @@ impl TypeTree { self } + + pub(crate) fn insert(&mut self, indices: &[i64], ct: CConcreteType, ctx: &Context) { + unsafe { + EnzymeTypeTreeInsertEq(self.inner, indices.as_ptr(), indices.len(), ct, ctx); + } + } } impl Clone for TypeTree { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 8c0d255bba84e..ae6a2da62b5dd 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -8,22 +8,24 @@ use { use crate::llvm::{self, Value}; -/// Converts a Rust TypeTree to Enzyme's internal TypeTree format -/// -/// This function takes a Rust-side TypeTree (from rustc_ast::expand::typetree) -/// and converts it to Enzyme's internal C++ TypeTree representation that -/// Enzyme can understand during differentiation analysis. #[cfg(llvm_enzyme)] fn to_enzyme_typetree( rust_typetree: RustTypeTree, - data_layout: &str, + _data_layout: &str, llcx: &llvm::Context, ) -> llvm::TypeTree { - // Start with an empty TypeTree let mut enzyme_tt = llvm::TypeTree::new(); - - // Convert each Type in the Rust TypeTree to Enzyme format - for rust_type in rust_typetree.0 { + process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); + enzyme_tt +} +#[cfg(llvm_enzyme)] +fn process_typetree_recursive( + enzyme_tt: &mut llvm::TypeTree, + rust_typetree: &RustTypeTree, + parent_indices: &[i64], + llcx: &llvm::Context, +) { + for rust_type in &rust_typetree.0 { let concrete_type = match rust_type.kind { rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything, rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer, @@ -35,25 +37,27 @@ fn to_enzyme_typetree( rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, }; - // Create a TypeTree for this specific type - let type_tt = llvm::TypeTree::from_type(concrete_type, llcx); - - // Apply offset if specified - let type_tt = if rust_type.offset == -1 { - type_tt // -1 means everywhere/no specific offset + let mut indices = parent_indices.to_vec(); + if !parent_indices.is_empty() { + if rust_type.offset == -1 { + indices.push(-1); + } else { + indices.push(rust_type.offset as i64); + } + } else if rust_type.offset == -1 { + indices.push(-1); } else { - // Apply specific offset positioning - type_tt.shift(data_layout, rust_type.offset, rust_type.size as isize, 0) - }; + indices.push(rust_type.offset as i64); + } - // Merge this type into the main TypeTree - enzyme_tt = enzyme_tt.merge(type_tt); - } + enzyme_tt.insert(&indices, concrete_type, llcx); - enzyme_tt + if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer && !rust_type.child.0.is_empty() { + process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx); + } + } } -// Attaches TypeTree information to LLVM function as enzyme_type attributes. #[cfg(llvm_enzyme)] pub(crate) fn add_tt<'ll>( llmod: &'ll llvm::Module, @@ -64,28 +68,20 @@ pub(crate) fn add_tt<'ll>( let inputs = tt.args; let ret_tt: RustTypeTree = tt.ret; - // Get LLVM data layout string for TypeTree conversion let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; let llvm_data_layout = std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) .expect("got a non-UTF8 data-layout from LLVM"); - // Attribute name that Enzyme recognizes for TypeTree information let attr_name = "enzyme_type"; let c_attr_name = CString::new(attr_name).unwrap(); - // Attach TypeTree attributes to each input parameter - // Enzyme uses these to understand parameter memory layouts during differentiation for (i, input) in inputs.iter().enumerate() { unsafe { - // Convert Rust TypeTree to Enzyme's internal format let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx); - - // Serialize TypeTree to string format that Enzyme can parse let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); let c_str = std::ffi::CStr::from_ptr(c_str); - // Create LLVM string attribute with TypeTree information let attr = llvm::LLVMCreateStringAttribute( llcx, c_attr_name.as_ptr(), @@ -94,17 +90,11 @@ pub(crate) fn add_tt<'ll>( c_str.to_bytes().len() as c_uint, ); - // Attach attribute to the specific function parameter - // Note: ArgumentPlace uses 0-based indexing, but LLVM uses 1-based for arguments attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); - - // Free the C string to prevent memory leaks llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); } } - // Attach TypeTree attribute to the return type - // Enzyme needs this to understand how to handle return value derivatives unsafe { let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx); let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); @@ -118,15 +108,11 @@ pub(crate) fn add_tt<'ll>( c_str.to_bytes().len() as c_uint, ); - // Attach to function return type attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); - - // Free the C string llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); } } -// Fallback implementation when Enzyme is not available #[cfg(not(llvm_enzyme))] pub(crate) fn add_tt<'ll>( _llmod: &'ll llvm::Module, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 703d417f96db9..581f20e8492d9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2261,10 +2261,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { x if x == tcx.types.f32 => (Kind::Float, 4), x if x == tcx.types.f64 => (Kind::Double, 8), x if x == tcx.types.f128 => (Kind::F128, 16), - _ => return TypeTree::new(), + _ => (Kind::Integer, 0), } } else { - return TypeTree::new(); + (Kind::Integer, 0) }; return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); @@ -2295,32 +2295,14 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let element_tree = typetree_from_ty(tcx, *element_ty); - let element_layout = tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*element_ty)) - .ok() - .map(|layout| layout.size.bytes_usize()) - .unwrap_or(0); - - if element_layout == 0 { - return TypeTree::new(); - } - let mut types = Vec::new(); - for i in 0..len { - let base_offset = (i as usize * element_layout) as isize; - - for elem_type in &element_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - base_offset - } else { - base_offset + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } + for elem_type in &element_tree.0 { + types.push(Type { + offset: -1, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); } return TypeTree(types); diff --git a/tests/run-make/autodiff/type-trees/array-typetree/array.check b/tests/run-make/autodiff/type-trees/array-typetree/array.check index 7513458b8ab81..0d38bdec17e84 100644 --- a/tests/run-make/autodiff/type-trees/array-typetree/array.check +++ b/tests/run-make/autodiff/type-trees/array-typetree/array.check @@ -1,4 +1,4 @@ ; Check that array TypeTree metadata is correctly generated ; Should show Float@double at each array element offset (0, 8, 16, 24, 32 bytes) -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_array{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_array{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check index 3a59f06dda1e4..0e6351ac4d39d 100644 --- a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check @@ -1,7 +1,7 @@ ; Check that enzyme_type attributes are present in the LLVM IR function definition ; This verifies our TypeTree system correctly attaches metadata for Enzyme -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[]:Pointer}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" ; Check that llvm.memcpy exists (either call or declare) CHECK: {{(call|declare).*}}@llvm.memcpy diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check new file mode 100644 index 0000000000000..584f584084358 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check @@ -0,0 +1,2 @@ +; Check that mixed struct with large array generates correct detailed type tree +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_mixed_struct{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer, [-1,8]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs new file mode 100644 index 0000000000000..1c19963bc3612 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs @@ -0,0 +1,16 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc() + .input("test.rs") + .arg("-Zautodiff=Enable") + .arg("-Zautodiff=NoPostopt") + .opt_level("0") + .emit("llvm-ir") + .run(); + + llvm_filecheck().patterns("mixed.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs new file mode 100644 index 0000000000000..7a734980e617c --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs @@ -0,0 +1,23 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[repr(C)] +struct Container { + header: i64, + data: [f32; 1000], +} + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +#[inline(never)] +fn test_mixed_struct(container: &Container) -> f32 { + container.data[0] + container.data[999] +} + +fn main() { + let container = Container { header: 42, data: [1.0; 1000] }; + let mut d_container = Container { header: 0, data: [0.0; 1000] }; + let result = d_test(&container, &mut d_container, 1.0); + std::hint::black_box(result); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check index 53eafc10a65da..3c02003c88287 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -1,4 +1,4 @@ // Check that enzyme_type attributes are present when TypeTree is enabled // This verifies our TypeTree metadata attachment is working -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check index 31cef113420db..733e46aa45a57 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check @@ -1,4 +1,4 @@ ; Check that f128 TypeTree metadata is correctly generated -; f128 maps to Unknown in our current implementation since CConcreteType doesn't have DT_F128 +; Should show Float@fp128 for f128 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f128{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@fp128}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check index 97a3964a569b1..9caca26b5cf51 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check @@ -1,4 +1,4 @@ ; Check that f16 TypeTree metadata is correctly generated -; Should show Half for f16 values and Pointer for references +; Should show Float@half for f16 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f16{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@half}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check index b32d5086d07d1..ec12ba6b2348d 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check @@ -1,4 +1,4 @@ ; Check that f32 TypeTree metadata is correctly generated ; Should show Float@float for f32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check index 0e9d9c03a0f86..f1af270824d57 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check @@ -1,4 +1,4 @@ ; Check that f64 TypeTree metadata is correctly generated ; Should show Float@double for f64 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check index aa8e5223e8ed5..fd9a94be8100b 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check @@ -1,4 +1,4 @@ ; Check that i32 TypeTree metadata is correctly generated -; Should show Integer for i32 values (integers are typically Const in autodiff) +; Should show Integer for i32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[]:Integer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Integer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs index 530c137f62b9c..249803c5d9f7a 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs @@ -2,13 +2,14 @@ use std::autodiff::autodiff_reverse; -#[autodiff_reverse(d_test, Const, Active)] +#[autodiff_reverse(d_test, Duplicated, Active)] #[no_mangle] -fn test_i32(x: i32) -> i32 { +fn test_i32(x: &i32) -> i32 { x * x } fn main() { let x = 5_i32; - let _result = d_test(x, 1); + let mut dx = 0_i32; + let _result = d_test(&x, &mut dx, 1); } diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/slice.check b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check index 8fc9e9c4f1b17..6543b61611538 100644 --- a/tests/run-make/autodiff/type-trees/slice-typetree/slice.check +++ b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check @@ -1,4 +1,4 @@ ; Check that slice TypeTree metadata is correctly generated ; Should show Float@double for slice elements -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_slice{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_slice{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/struct.check b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check index 2f763f18c1cee..54956317e1e95 100644 --- a/tests/run-make/autodiff/type-trees/struct-typetree/struct.check +++ b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check @@ -1,4 +1,4 @@ ; Check that struct TypeTree metadata is correctly generated ; Should show Float@double at offsets 0, 8, 16 for Point struct fields -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_struct{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_struct{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check index 50aa25e96aedd..47647e78cc35c 100644 --- a/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check @@ -1,4 +1,4 @@ ; Check that tuple TypeTree metadata is correctly generated ; Should show Float@double at offsets 0, 8, 16 for (f64, f64, f64) -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_tuple{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_tuple{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check b/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check index dcf9508b69d6f..cdb70eb83fc19 100644 --- a/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check +++ b/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check @@ -1,7 +1,7 @@ // CHECK: callee - {[-1]:Float@float} |{[-1]:Pointer}:{} // CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer} // CHECK-DAG: %{{[0-9]+}} = getelementptr inbounds nuw i8, ptr %{{[0-9]+}}, i64 8, !dbg !{{[0-9]+}}: {[-1]:Pointer} -// CHECK-DAG: %{{[0-9]+}} = load ptr, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !nonnull !102, !noundef !{{[0-9]+}}: {} +// CHECK-DAG: %{{[0-9]+}} = load ptr, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !nonnull !{{[0-9]+}}, !noundef !{{[0-9]+}}: {} // CHECK-DAG: %{{[0-9]+}} = getelementptr inbounds nuw i8, ptr %{{[0-9]+}}, i64 16, !dbg !{{[0-9]+}}: {[-1]:Pointer} // CHECK-DAG: %{{[0-9]+}} = load i64, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !noundef !{{[0-9]+}}: {} // CHECK-DAG: %{{[0-9]+}} = icmp eq i64 %{{[0-9]+}}, 0, !dbg !{{[0-9]+}}: {[-1]:Integer} From 4520926bb527bd43edbf0de84c2b0c6a9c5fc5ce Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Thu, 11 Sep 2025 07:30:35 +0000 Subject: [PATCH 0597/1900] autodiff: recurion added for typetree --- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 + compiler/rustc_codegen_llvm/src/typetree.rs | 10 +- compiler/rustc_middle/src/ty/mod.rs | 76 +++++++++++-- .../type-trees/nott-flag/with_tt.check | 2 +- .../recursion-typetree/recursion.check | 3 + .../type-trees/recursion-typetree/rmake.rs | 9 ++ .../type-trees/recursion-typetree/test.rs | 100 ++++++++++++++++++ .../scalar-types/f128-typetree/f128.check | 2 +- .../scalar-types/f16-typetree/f16.check | 2 +- .../scalar-types/f32-typetree/f32.check | 2 +- .../scalar-types/f64-typetree/f64.check | 2 +- .../scalar-types/i32-typetree/i32.check | 2 +- 12 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/test.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index e63043b21227f..b604f5139c8c1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -127,6 +127,7 @@ pub(crate) mod Enzyme_AD { ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); + pub(crate) fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; } unsafe extern "C" { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index ae6a2da62b5dd..1a54884f6c5a4 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -39,11 +39,7 @@ fn process_typetree_recursive( let mut indices = parent_indices.to_vec(); if !parent_indices.is_empty() { - if rust_type.offset == -1 { - indices.push(-1); - } else { - indices.push(rust_type.offset as i64); - } + indices.push(rust_type.offset as i64); } else if rust_type.offset == -1 { indices.push(-1); } else { @@ -52,7 +48,9 @@ fn process_typetree_recursive( enzyme_tt.insert(&indices, concrete_type, llcx); - if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer && !rust_type.child.0.is_empty() { + if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer + && !rust_type.child.0.is_empty() + { process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx); } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 581f20e8492d9..7ca2355947ad4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2252,6 +2252,61 @@ pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { /// Generate TypeTree for a specific type. /// This function analyzes a Rust type and creates appropriate TypeTree metadata. pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + let mut visited = Vec::new(); + typetree_from_ty_inner(tcx, ty, 0, &mut visited) +} + +/// Internal recursive function for TypeTree generation with cycle detection and depth limiting. +fn typetree_from_ty_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, +) -> TypeTree { + #[cfg(llvm_enzyme)] + { + unsafe extern "C" { + fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; + } + let max_depth = unsafe { EnzymeGetMaxTypeDepth() } as usize; + if depth > max_depth { + return TypeTree::new(); + } + } + + #[cfg(not(llvm_enzyme))] + if depth > 6 { + return TypeTree::new(); + } + + if visited.contains(&ty) { + return TypeTree::new(); + } + + visited.push(ty); + let result = typetree_from_ty_impl(tcx, ty, depth, visited); + visited.pop(); + result +} + +/// Implementation of TypeTree generation logic. +fn typetree_from_ty_impl<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, +) -> TypeTree { + typetree_from_ty_impl_inner(tcx, ty, depth, visited, false) +} + +/// Internal implementation with context about whether this is for a reference target. +fn typetree_from_ty_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, + is_reference_target: bool, +) -> TypeTree { if ty.is_scalar() { let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) @@ -2267,7 +2322,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { (Kind::Integer, 0) }; - return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); + // Use offset 0 for scalars that are direct targets of references (like &f64) + // Use offset -1 for scalars used directly (like function return types) + let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; + return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); } if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { @@ -2277,7 +2335,7 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { return TypeTree::new(); }; - let child = typetree_from_ty(tcx, inner_ty); + let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); return TypeTree(vec![Type { offset: -1, size: tcx.data_layout.pointer_size().bytes_usize(), @@ -2292,9 +2350,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if len == 0 { return TypeTree::new(); } - - let element_tree = typetree_from_ty(tcx, *element_ty); - + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); let mut types = Vec::new(); for elem_type in &element_tree.0 { types.push(Type { @@ -2311,7 +2368,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if ty.is_slice() { if let ty::Slice(element_ty) = ty.kind() { - let element_tree = typetree_from_ty(tcx, *element_ty); + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); return element_tree; } } @@ -2325,7 +2383,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let mut current_offset = 0; for tuple_ty in tuple_types.iter() { - let element_tree = typetree_from_ty(tcx, tuple_ty); + let element_tree = + typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); let element_layout = tcx .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) @@ -2361,7 +2420,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { for (field_idx, field_def) in adt_def.all_fields().enumerate() { let field_ty = field_def.ty(tcx, args); - let field_tree = typetree_from_ty(tcx, field_ty); + let field_tree = + typetree_from_ty_impl_inner(tcx, field_ty, depth + 1, visited, false); let field_offset = layout.fields.offset(field_idx).bytes_usize(); diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check index 3c02003c88287..0b4c91191798b 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -1,4 +1,4 @@ // Check that enzyme_type attributes are present when TypeTree is enabled // This verifies our TypeTree metadata attachment is working -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check b/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check new file mode 100644 index 0000000000000..1960e7b816c5f --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check @@ -0,0 +1,3 @@ +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_deep{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_graph{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer, [-1,8]:Integer, [-1,16]:Integer, [-1,24]:Float@double}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_node{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs new file mode 100644 index 0000000000000..78718f3a21595 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("recursion.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs new file mode 100644 index 0000000000000..9d40bec1bf1d1 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs @@ -0,0 +1,100 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +// Self-referential struct to test recursion detection +#[derive(Clone)] +struct Node { + value: f64, + next: Option>, +} + +// Mutually recursive structs to test cycle detection +#[derive(Clone)] +struct GraphNodeA { + value: f64, + connections: Vec, +} + +#[derive(Clone)] +struct GraphNodeB { + weight: f64, + target: Option>, +} + +#[autodiff_reverse(d_test_node, Duplicated, Active)] +#[no_mangle] +fn test_node(node: &Node) -> f64 { + node.value * 2.0 +} + +#[autodiff_reverse(d_test_graph, Duplicated, Active)] +#[no_mangle] +fn test_graph(a: &GraphNodeA) -> f64 { + a.value * 3.0 +} + +// Simple depth test - deeply nested but not circular +#[derive(Clone)] +struct Level1 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level2 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level3 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level4 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level5 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level6 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level7 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level8 { + val: f64, +} + +#[autodiff_reverse(d_test_deep, Duplicated, Active)] +#[no_mangle] +fn test_deep(deep: &Level1) -> f64 { + deep.val * 4.0 +} + +fn main() { + let node = Node { value: 1.0, next: None }; + + let graph = GraphNodeA { value: 2.0, connections: vec![] }; + + let deep = Level1 { val: 5.0, next: None }; + + let mut d_node = Node { value: 0.0, next: None }; + + let mut d_graph = GraphNodeA { value: 0.0, connections: vec![] }; + + let mut d_deep = Level1 { val: 0.0, next: None }; + + let _result1 = d_test_node(&node, &mut d_node, 1.0); + let _result2 = d_test_graph(&graph, &mut d_graph, 1.0); + let _result3 = d_test_deep(&deep, &mut d_deep, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check index 733e46aa45a57..23db64eea52aa 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check @@ -1,4 +1,4 @@ ; Check that f128 TypeTree metadata is correctly generated ; Should show Float@fp128 for f128 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@fp128}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@fp128}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check index 9caca26b5cf51..9adff68d36f34 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check @@ -1,4 +1,4 @@ ; Check that f16 TypeTree metadata is correctly generated ; Should show Float@half for f16 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@half}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@half}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check index ec12ba6b2348d..176630f57e8f7 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check @@ -1,4 +1,4 @@ ; Check that f32 TypeTree metadata is correctly generated ; Should show Float@float for f32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@float}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check index f1af270824d57..929cd379694ac 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check @@ -1,4 +1,4 @@ ; Check that f64 TypeTree metadata is correctly generated ; Should show Float@double for f64 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check index fd9a94be8100b..dee4aa5bbb6b2 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check @@ -1,4 +1,4 @@ ; Check that i32 TypeTree metadata is correctly generated ; Should show Integer for i32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Integer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer}" \ No newline at end of file From b1a9f231fea0459356f751ad6c1704b55f541118 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 14:41:18 +1000 Subject: [PATCH 0598/1900] Use `LLVMDIBuilderGetOrCreateSubrange` --- .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++++++-------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index aa8b8bd152dcc..c3712e5b74477 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -117,7 +117,7 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( .try_to_target_usize(cx.tcx) .expect("expected monomorphic const in codegen") as c_longlong; - let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; + let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; let subscripts = &[subrange]; let di_node = unsafe { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1124ebc3d44b0..d125c1c9c297b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -25,7 +25,7 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, - DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, + DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -890,7 +890,6 @@ pub(crate) mod debuginfo { pub(crate) type DIVariable = DIDescriptor; pub(crate) type DIGlobalVariableExpression = DIDescriptor; pub(crate) type DIArray = DIDescriptor; - pub(crate) type DISubrange = DIDescriptor; pub(crate) type DIEnumerator = DIDescriptor; pub(crate) type DITemplateTypeParameter = DIDescriptor; @@ -1989,6 +1988,12 @@ unsafe extern "C" { Scope: Option<&'ll Metadata>, AlignInBits: u32, // (optional; default is 0) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateSubrange<'ll>( + Builder: &DIBuilder<'ll>, + LowerBound: i64, + Count: i64, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2370,12 +2375,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( - Builder: &DIBuilder<'a>, - Lo: i64, - Count: i64, - ) -> &'a DISubrange; - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( Builder: &DIBuilder<'a>, Ptr: *const Option<&'a DIDescriptor>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321fd..de79c3e6861c6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,12 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, - int64_t Count) { - return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, LLVMMetadataRef *Ptr, unsigned Count) { From a6d261712ee546fc1fc1b7b7c0ee0782aa827b8b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 14:44:54 +1000 Subject: [PATCH 0599/1900] Use `LLVMDIBuilderGetOrCreateArray` --- compiler/rustc_codegen_llvm/src/debuginfo/utils.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 ++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index cc1d504b43017..7e1e49310f61c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -28,7 +28,7 @@ pub(crate) fn create_DIArray<'ll>( builder: &DIBuilder<'ll>, arr: &[Option<&'ll DIDescriptor>], ) -> &'ll DIArray { - unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) } + unsafe { llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len()) } } #[inline] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d125c1c9c297b..290dbda77f6fb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1994,6 +1994,12 @@ unsafe extern "C" { LowerBound: i64, Count: i64, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateArray<'ll>( + Builder: &DIBuilder<'ll>, + Data: *const Option<&'ll Metadata>, + NumElements: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2375,12 +2381,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( - Builder: &DIBuilder<'a>, - Ptr: *const Option<&'a DIDescriptor>, - Count: c_uint, - ) -> &'a DIArray; - pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( Builder: &DIBuilder<'a>, Val: &'a Value, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index de79c3e6861c6..b78786921760e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,15 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, - LLVMMetadataRef *Ptr, unsigned Count) { - Metadata **DataValue = unwrap(Ptr); - return wrap(unwrap(Builder) - ->getOrCreateArray(ArrayRef(DataValue, Count)) - .get()); -} - extern "C" void LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, uint64_t *AddrOps, From 6870e24fdbd9dc1770101457ebdcf9d5837f0b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 19 Sep 2025 08:08:23 +0300 Subject: [PATCH 0600/1900] Set WithCachedTypeInfo::stable_hash when in-tree --- src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs | 2 ++ .../rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs | 2 ++ src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 0b3582051bc07..7ebefa76ed029 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -36,6 +36,8 @@ impl<'db> Const<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Const::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 99b1354b6335f..86545415009a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -227,6 +227,8 @@ impl<'db> Predicate<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 70139e8666948..c7a747ade3e76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -60,6 +60,8 @@ impl<'db> Ty<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) } From 5a043cef02229eb6eb777277745cd7e12b350311 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 19 Sep 2025 13:35:34 +0800 Subject: [PATCH 0601/1900] Fix extract_variable on LetExpr Example --- ```rust fn main() { if $0let$0 Some(x) = Some(2+2) {} } ``` **Before this PR**: ```rust fn main() { let $0var_name = let Some(x) = Some(2+2); if var_name {} } ``` **After this PR**: ```rust fn main() { let $0var_name = Some(2+2); if let Some(x) = var_name {} } ``` --- .../src/handlers/extract_variable.rs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index bd88e8b09ced0..da596262962c5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -285,7 +285,7 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { /// In general that's true for any expression, but in some cases that would produce invalid code. fn valid_target_expr(node: SyntaxNode) -> Option { match node.kind() { - SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR => None, + SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::BLOCK_EXPR => { @@ -1403,6 +1403,25 @@ fn main() { ); } + #[test] + fn extract_var_let_expr() { + check_assist_by_label( + extract_variable, + r#" +fn main() { + if $0let$0 Some(x) = Some(2+2) {} +} +"#, + r#" +fn main() { + let $0var_name = Some(2+2); + if let Some(x) = var_name {} +} +"#, + "Extract into variable", + ); + } + #[test] fn extract_var_for_cast() { check_assist_by_label( @@ -1738,6 +1757,14 @@ fn main() { check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); } + #[test] + fn extract_var_for_let_expr_not_applicable() { + check_assist_not_applicable( + extract_variable, + "fn main() { if $0let Some(x) = Some(2+2) {} }", + ); + } + #[test] fn extract_var_unit_expr_not_applicable() { check_assist_not_applicable( From f69da330493233c9f5485919cc7c421c35a83bbc Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 19 Sep 2025 13:39:45 +0800 Subject: [PATCH 0602/1900] Add `#[track_caller]` for check_assist_by_label --- src/tools/rust-analyzer/crates/ide-assists/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 3e422ae1b8f6c..c0637a7470f3e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -180,6 +180,7 @@ pub(crate) fn check_assist_import_one( // There is no way to choose what assist within a group you want to test against, // so this is here to allow you choose. +#[track_caller] pub(crate) fn check_assist_by_label( assist: Handler, #[rust_analyzer::rust_fixture] ra_fixture_before: &str, From 3ba5f19182bf7144c54cbbd0b7af3d4fe76b5317 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Fri, 12 Sep 2025 06:11:18 +0000 Subject: [PATCH 0603/1900] autodiff: typetree recursive depth query from enzyme with fallback Signed-off-by: Karan Janthe --- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 - compiler/rustc_codegen_llvm/src/typetree.rs | 10 ++++----- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 12 +++++++++++ compiler/rustc_middle/src/ty/mod.rs | 21 +++++++------------ src/llvm-project | 2 +- src/tools/cargo | 2 +- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index b604f5139c8c1..e63043b21227f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -127,7 +127,6 @@ pub(crate) mod Enzyme_AD { ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); - pub(crate) fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; } unsafe extern "C" { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 1a54884f6c5a4..7e2635037008e 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -1,5 +1,5 @@ use rustc_ast::expand::typetree::FncTree; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] use { crate::attributes, rustc_ast::expand::typetree::TypeTree as RustTypeTree, @@ -8,7 +8,7 @@ use { use crate::llvm::{self, Value}; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] fn to_enzyme_typetree( rust_typetree: RustTypeTree, _data_layout: &str, @@ -18,7 +18,7 @@ fn to_enzyme_typetree( process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); enzyme_tt } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] fn process_typetree_recursive( enzyme_tt: &mut llvm::TypeTree, rust_typetree: &RustTypeTree, @@ -56,7 +56,7 @@ fn process_typetree_recursive( } } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) fn add_tt<'ll>( llmod: &'ll llvm::Module, llcx: &'ll llvm::Context, @@ -111,7 +111,7 @@ pub(crate) fn add_tt<'ll>( } } -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) fn add_tt<'ll>( _llmod: &'ll llvm::Module, _llcx: &'ll llvm::Context, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321fd..c1a924a87e414 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1847,3 +1847,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { MD.NoHWAddress = true; GV.setSanitizerMetadata(MD); } + +#ifdef ENZYME +extern "C" { +extern llvm::cl::opt EnzymeMaxTypeDepth; +} + +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return EnzymeMaxTypeDepth; } +#else +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { + return 6; // Default fallback depth +} +#endif diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7ca2355947ad4..ce4de6b95e0bb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -63,7 +63,7 @@ pub use rustc_type_ir::solve::SizedTraitKind; pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; pub use vtable::*; use {rustc_ast as ast, rustc_hir as hir}; @@ -2256,6 +2256,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { typetree_from_ty_inner(tcx, ty, 0, &mut visited) } +/// Maximum recursion depth for TypeTree generation to prevent stack overflow +/// from pathological deeply nested types. Combined with cycle detection. +const MAX_TYPETREE_DEPTH: usize = 6; + /// Internal recursive function for TypeTree generation with cycle detection and depth limiting. fn typetree_from_ty_inner<'tcx>( tcx: TyCtxt<'tcx>, @@ -2263,19 +2267,8 @@ fn typetree_from_ty_inner<'tcx>( depth: usize, visited: &mut Vec>, ) -> TypeTree { - #[cfg(llvm_enzyme)] - { - unsafe extern "C" { - fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; - } - let max_depth = unsafe { EnzymeGetMaxTypeDepth() } as usize; - if depth > max_depth { - return TypeTree::new(); - } - } - - #[cfg(not(llvm_enzyme))] - if depth > 6 { + if depth >= MAX_TYPETREE_DEPTH { + trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); return TypeTree::new(); } diff --git a/src/llvm-project b/src/llvm-project index 2a22c8014390b..333793696b0a0 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 2a22c8014390bdf387d8ed26005fee3187fb98bd +Subproject commit 333793696b0a08002acac6af983adc7a5233fc56 diff --git a/src/tools/cargo b/src/tools/cargo index a4bd03c92d3b9..966f94733bbc9 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a4bd03c92d3b977909953d06cc45e263bd7ca7d8 +Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 From 1db74d4845ed18b63e0c683f7a13714c3653a268 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 09:02:03 +0200 Subject: [PATCH 0604/1900] fix miri bootstrap build --- src/tools/miri/src/clock.rs | 16 +++++++++++++++- src/tools/miri/src/lib.rs | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 47608f873a481..dbbe741a0714d 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -46,7 +46,21 @@ impl Instant { InstantKind::Virtual { nanoseconds: earlier }, ) => { let duration = nanoseconds.saturating_sub(earlier); - Duration::from_nanos_u128(duration) + cfg_select! { + bootstrap => { + // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, + // so we have to implement this ourselves. + // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). + // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. + let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); + // It is impossible for nanosecond to overflow because u32::MAX > 1e9. + let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); + Duration::new(seconds, nanosecond) + } + _ => { + Duration::from_nanos_u128(duration) + } + } } _ => panic!("all `Instant` must be of the same kind"), } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index dbc2a99593d2c..7f5f25b9f66b2 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -18,7 +18,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] -#![feature(duration_from_nanos_u128)] +#![cfg_attr(not(bootstrap), feature(duration_from_nanos_u128))] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, From 9daa026cadca6de0d932ddb6f6ddbfa5411a25e3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 16:56:28 +1000 Subject: [PATCH 0605/1900] Use `LLVMDIBuilder(CreateExpression|InsertDeclareRecordAtEnd)` --- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 15 ++++++----- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 27 ++++++++++++------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 12 --------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 126082aa3aa68..dc36ed17875e7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -191,18 +191,21 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { addr_ops.push((fragment.end - fragment.start).bits() as u64); } + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; unsafe { // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. - llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(self.cx()), + llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( + di_builder, variable_alloca, dbg_var, - addr_ops.as_ptr(), - addr_ops.len() as c_uint, + addr_expr, dbg_loc, self.llbb(), - ); - } + ) + }; } fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 290dbda77f6fb..11de4ed965303 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -806,6 +806,8 @@ unsafe extern "C" { pub(crate) type Metadata; pub(crate) type BasicBlock; pub(crate) type Comdat; + /// `&'ll DbgRecord` represents `LLVMDbgRecordRef`. + pub(crate) type DbgRecord; } #[repr(C)] pub(crate) struct Builder<'a>(InvariantOpaque<'a>); @@ -2000,6 +2002,21 @@ unsafe extern "C" { Data: *const Option<&'ll Metadata>, NumElements: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateExpression<'ll>( + Builder: &DIBuilder<'ll>, + Addr: *const u64, + Length: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderInsertDeclareRecordAtEnd<'ll>( + Builder: &DIBuilder<'ll>, + Storage: &'ll Value, + VarInfo: &'ll Metadata, + Expr: &'ll Metadata, + DebugLoc: &'ll Metadata, + Block: &'ll BasicBlock, + ) -> &'ll DbgRecord; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2381,16 +2398,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( - Builder: &DIBuilder<'a>, - Val: &'a Value, - VarInfo: &'a DIVariable, - AddrOps: *const u64, - AddrOpsCount: c_uint, - DL: &'a DILocation, - InsertAtEnd: &'a BasicBlock, - ); - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b78786921760e..fc79cb9d473e8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,18 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" void -LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, - LLVMMetadataRef VarInfo, uint64_t *AddrOps, - unsigned AddrOpsCount, LLVMMetadataRef DL, - LLVMBasicBlockRef InsertAtEnd) { - unwrap(Builder)->insertDeclare( - unwrap(V), unwrap(VarInfo), - unwrap(Builder)->createExpression( - llvm::ArrayRef(AddrOps, AddrOpsCount)), - DebugLoc(cast(unwrap(DL))), unwrap(InsertAtEnd)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], From 4a909bc6b6d39f3596e12ebd77f1dc02f039af3d Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 19 Sep 2025 13:24:16 +0500 Subject: [PATCH 0606/1900] Fix versions lints from 1.90.0 --- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/operators/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index eca3bc390d778..f8ae770b3a4db 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -315,7 +315,7 @@ declare_clippy_lint! { /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/) /// pub fn do_something() {} /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.90.0"] pub DOC_BROKEN_LINK, pedantic, "broken document link" diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index bdbbb3475cd5f..aaea4ff11fc37 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -854,7 +854,7 @@ declare_clippy_lint! { /// println!("{a} is divisible by {b}"); /// } /// ``` - #[clippy::version = "1.89.0"] + #[clippy::version = "1.90.0"] pub MANUAL_IS_MULTIPLE_OF, complexity, "manual implementation of `.is_multiple_of()`" From 7506352af077b316c12be32e0f9782a0cfd73b1d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 10:41:56 +0200 Subject: [PATCH 0607/1900] fix clippy warning --- src/tools/miri/src/concurrency/sync.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 15d486c27e36a..e4e7fb1d725fe 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -381,8 +381,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We need to drop our mutex borrow before unblock_thread // because it will be borrowed again in the unblock callback. drop(mutex); - if thread_id.is_some() { - this.unblock_thread(thread_id.unwrap(), BlockReason::Mutex)?; + if let Some(thread_id) = thread_id { + this.unblock_thread(thread_id, BlockReason::Mutex)?; } } Some(old_lock_count) From be01d87ba2c3ae88ed8a5f73c7440b02dbf9d80b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 10:52:53 +0200 Subject: [PATCH 0608/1900] Allow running remote-test-server on Apple simulators --- src/tools/remote-test-server/src/main.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 67a7ad6f3b454..5ec5e6e28982d 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -53,6 +53,10 @@ impl Config { batch: false, bind: if cfg!(target_os = "android") || cfg!(windows) { ([0, 0, 0, 0], 12345).into() + } else if cfg!(target_env = "sim") { + // iOS/tvOS/watchOS/visionOS simulators share network device + // with the host machine. + ([127, 0, 0, 1], 12345).into() } else { ([10, 0, 2, 15], 12345).into() }, @@ -262,10 +266,17 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf cmd.args(args); cmd.envs(env); - // On windows, libraries are just searched in the executable directory, - // system directories, PWD, and PATH, in that order. PATH is the only one - // we can change for this. - let library_path = if cfg!(windows) { "PATH" } else { "LD_LIBRARY_PATH" }; + let library_path = if cfg!(windows) { + // On windows, libraries are just searched in the executable directory, + // system directories, PWD, and PATH, in that order. PATH is the only + // one we can change for this. + "PATH" + } else if cfg!(target_vendor = "apple") { + // On Apple platforms, the environment variable is named differently. + "DYLD_LIBRARY_PATH" + } else { + "LD_LIBRARY_PATH" + }; // Support libraries were uploaded to `work` earlier, so make sure that's // in `LD_LIBRARY_PATH`. Also include our own current dir which may have From 09d3120a99fee8d839e502bdb54f8117c0623c5d Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 14 Sep 2025 15:30:44 +0200 Subject: [PATCH 0609/1900] std: simplify host lookup --- library/std/src/net/socket_addr.rs | 41 +++++++------ library/std/src/sys/net/connection/sgx.rs | 29 ++-------- .../std/src/sys/net/connection/socket/mod.rs | 58 +++++-------------- .../src/sys/net/connection/socket/tests.rs | 2 +- .../std/src/sys/net/connection/uefi/mod.rs | 22 +------ .../std/src/sys/net/connection/unsupported.rs | 22 +------ library/std/src/sys/net/connection/wasip1.rs | 22 +------ .../std/src/sys/net/connection/xous/dns.rs | 45 +------------- .../std/src/sys/net/connection/xous/mod.rs | 2 +- tests/ui/inference/issue-72616.rs | 1 - tests/ui/inference/issue-72616.stderr | 19 +----- 11 files changed, 51 insertions(+), 212 deletions(-) diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 41e623e79ce27..5b56dd3f74472 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,7 +6,6 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::LookupHost; use crate::{io, iter, option, slice, vec}; /// A trait for objects which can be converted or resolved to one or more @@ -188,15 +187,9 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -fn resolve_socket_addr(lh: LookupHost) -> io::Result> { - let p = lh.port(); - let v: Vec<_> = lh - .map(|mut a| { - a.set_port(p); - a - }) - .collect(); - Ok(v.into_iter()) +fn lookup_host(host: &str, port: u16) -> io::Result> { + let addrs = crate::sys::net::lookup_host(host, port)?; + Ok(Vec::from_iter(addrs).into_iter()) } #[stable(feature = "rust1", since = "1.0.0")] @@ -205,17 +198,14 @@ impl ToSocketAddrs for (&str, u16) { fn to_socket_addrs(&self) -> io::Result> { let (host, port) = *self; - // try to parse the host as a regular IP address first - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV4::new(addr, port); - return Ok(vec![SocketAddr::V4(addr)].into_iter()); - } - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV6::new(addr, port, 0, 0); - return Ok(vec![SocketAddr::V6(addr)].into_iter()); + // Try to parse the host as a regular IP address first + if let Ok(addr) = host.parse::() { + let addr = SocketAddr::new(addr, port); + return Ok(vec![addr].into_iter()); } - resolve_socket_addr((host, port).try_into()?) + // Otherwise, make the system look it up. + lookup_host(host, port) } } @@ -232,12 +222,21 @@ impl ToSocketAddrs for (String, u16) { impl ToSocketAddrs for str { type Iter = vec::IntoIter; fn to_socket_addrs(&self) -> io::Result> { - // try to parse as a regular SocketAddr first + // Try to parse as a regular SocketAddr first if let Ok(addr) = self.parse() { return Ok(vec![addr].into_iter()); } - resolve_socket_addr(self.try_into()?) + // Otherwise, split the string by ':' and convert the second part to u16... + let Some((host, port_str)) = self.rsplit_once(':') else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); + }; + let Ok(port) = port_str.parse::() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); + }; + + // ... and make the system look up the host. + lookup_host(host, port) } } diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 9b54571997d0b..8c9c17d3f1714 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -499,16 +499,6 @@ impl fmt::Display for NonIpSockAddr { pub struct LookupHost(!); -impl LookupHost { - fn new(host: String) -> io::Result { - Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) - } - - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -516,18 +506,9 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - LookupHost::new(v.to_owned()) - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - LookupHost::new(format!("{host}:{port}")) - } +pub fn lookup_host(host: &str, port: u16) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Uncategorized, + NonIpSockAddr { host: format!("{host}:{port}") }, + )) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 564f2e3a01f3b..1dd06e97bbabd 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -258,7 +258,7 @@ fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { } //////////////////////////////////////////////////////////////////////////////// -// get_host_addresses +// lookup_host //////////////////////////////////////////////////////////////////////////////// pub struct LookupHost { @@ -267,12 +267,6 @@ pub struct LookupHost { port: u16, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -281,7 +275,10 @@ impl Iterator for LookupHost { let cur = self.cur.as_ref()?; self.cur = cur.ai_next; match socket_addr_from_c(cur.ai_addr.cast(), cur.ai_addrlen as usize) { - Ok(addr) => return Some(addr), + Ok(mut addr) => { + addr.set_port(self.port); + return Some(addr); + } Err(_) => continue, } } @@ -298,42 +295,17 @@ impl Drop for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), - } - }; +pub fn lookup_host(host: &str, port: u16) -> io::Result { + init(); + run_with_cstr(host.as_bytes(), &|c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - init(); - - run_with_cstr(host.as_bytes(), &|c_host| { - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } - }) - } + }) } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index fc236b8027b67..049355afca7ac 100644 --- a/library/std/src/sys/net/connection/socket/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -4,7 +4,7 @@ use crate::collections::HashMap; #[test] fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); - let lh = match LookupHost::try_from(("localhost", 0)) { + let lh = match lookup_host("localhost", 0) { Ok(lh) => lh, Err(e) => panic!("couldn't resolve `localhost`: {e}"), }; diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 0036804287352..004f6d413a1f3 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -333,12 +333,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -346,18 +340,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fbc86343272fc..fb18e8dec557c 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -304,12 +304,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -317,18 +311,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index cdfa25c8a4407..048dafdcd7f7c 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -477,12 +477,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -490,18 +484,6 @@ impl Iterator for LookupHost { } } -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index bb29d211fad56..b139376f59768 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -1,15 +1,8 @@ -use core::convert::{TryFrom, TryInto}; - use crate::io; use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{DnsLendMut, dns_server}; -pub struct DnsError { - #[allow(dead_code)] - pub code: u8, -} - #[repr(C, align(4096))] struct LookupHostQuery([u8; 4096]); @@ -20,12 +13,6 @@ pub struct LookupHost { count: usize, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -72,7 +59,7 @@ impl Iterator for LookupHost { } } -pub fn lookup(query: &str, port: u16) -> Result { +pub fn lookup_host(query: &str, port: u16) -> io::Result { let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; // Copy the query into the message that gets sent to the DNS server @@ -89,7 +76,7 @@ pub fn lookup(query: &str, port: u16) -> Result { ) .unwrap(); if result.data.0[0] != 0 { - return Err(DnsError { code: result.data.0[1] }); + return Err(io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")); } assert_eq!(result.offset, 0); result.count = result.data.0[1] as usize; @@ -98,31 +85,3 @@ pub fn lookup(query: &str, port: u16) -> Result { result.offset = 2; Ok(result) } - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl TryFrom<(&str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")) - } -} diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index e44a375b9e3c5..0f77be5c3fac8 100644 --- a/library/std/src/sys/net/connection/xous/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -45,4 +45,4 @@ pub struct GetAddress { raw: [u8; 4096], } -pub use dns::LookupHost; +pub use dns::lookup_host; diff --git a/tests/ui/inference/issue-72616.rs b/tests/ui/inference/issue-72616.rs index 71e095cc6fc02..ba18575a57156 100644 --- a/tests/ui/inference/issue-72616.rs +++ b/tests/ui/inference/issue-72616.rs @@ -21,7 +21,6 @@ pub fn main() { { if String::from("a") == "a".try_into().unwrap() {} //~^ ERROR type annotations needed - //~| ERROR type annotations needed } { let _: String = match "_".try_into() { diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index a271639996fd2..6b47d56881134 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -16,23 +16,6 @@ LL - if String::from("a") == "a".try_into().unwrap() {} LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} | -error[E0283]: type annotations needed - --> $DIR/issue-72616.rs:22:37 - | -LL | if String::from("a") == "a".try_into().unwrap() {} - | ^^^^^^^^ - | - = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - - impl TryFrom for T - where U: Into; - = note: required for `&str` to implement `TryInto<_>` -help: try using a fully qualified path to specify the expected types - | -LL - if String::from("a") == "a".try_into().unwrap() {} -LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0283`. From e39e5a0d157a56680cdc10a77551af225b797787 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 16:14:35 +1000 Subject: [PATCH 0610/1900] Use `LLVMDIBuilderCreate(Auto|Parameter)Variable` --- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 58 ++++++++++--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 43 +++++++++----- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 18 ------ 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index dc36ed17875e7..66b963436afb2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -52,15 +52,6 @@ mod utils; use self::create_scope_map::compute_mir_scopes; pub(crate) use self::metadata::build_global_var_di_node; -// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were -// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp` -// to decide which C++ API to call. Instead, we should just have two separate -// FFI functions and choose the correct one on the Rust side. -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; - /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, @@ -633,28 +624,39 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let type_metadata = spanned_type_di_node(self, variable_type, span); - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable => (0, DW_TAG_auto_variable), - }; let align = self.align_of(variable_type); let name = variable_name.as_str(); - unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(self), - dwarf_tag, - scope_metadata, - name.as_c_char_ptr(), - name.len(), - file_metadata, - loc.line, - type_metadata, - true, - DIFlags::FlagZero, - argument_index, - align.bits() as u32, - ) + + match variable_kind { + ArgumentVariable(arg_index) => unsafe { + llvm::LLVMDIBuilderCreateParameterVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + arg_index as c_uint, + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + ) + }, + LocalVariable => unsafe { + llvm::LLVMDIBuilderCreateAutoVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + align.bits() as u32, + ) + }, } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 11de4ed965303..b0388594f27db 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -26,7 +26,7 @@ use super::RustString; use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, - DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, + DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -2017,6 +2017,32 @@ unsafe extern "C" { DebugLoc: &'ll Metadata, Block: &'ll BasicBlock, ) -> &'ll DbgRecord; + + pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + AlignInBits: u32, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateParameterVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ArgNo: c_uint, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2383,21 +2409,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - AlwaysPreserve: bool, - Flags: DIFlags, - ArgNo: c_uint, - AlignInBits: u32, - ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index fc79cb9d473e8..c7fa26f18e14a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1124,24 +1124,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( return wrap(VarExpr); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( - LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo, - uint32_t AlignInBits) { - if (Tag == 0x100) { // DW_TAG_auto_variable - return wrap(unwrap(Builder)->createAutoVariable( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, - fromRust(Flags), AlignInBits)); - } else { - return wrap(unwrap(Builder)->createParameterVariable( - unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, - unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, - fromRust(Flags))); - } -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], From 272d336f0f89610bc18bc276b5f89dc163c69d24 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 17:25:16 +1000 Subject: [PATCH 0611/1900] Remove some unnecessary `as u64` casts In each of these casts, the LHS is already `u64`. --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 66b963436afb2..af64e4ebed0f7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -165,21 +165,21 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { if direct_offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(direct_offset.bytes()); } for &offset in indirect_offsets { addr_ops.push(DW_OP_deref); if offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(offset.bytes() as u64); + addr_ops.push(offset.bytes()); } } if let Some(fragment) = fragment { // `DW_OP_LLVM_fragment` takes as arguments the fragment's // offset and size, both of them in bits. addr_ops.push(DW_OP_LLVM_fragment); - addr_ops.push(fragment.start.bits() as u64); - addr_ops.push((fragment.end - fragment.start).bits() as u64); + addr_ops.push(fragment.start.bits()); + addr_ops.push((fragment.end - fragment.start).bits()); } let di_builder = DIB(self.cx()); From b30c1b53f3fbd79b94f40b1d000067e1c49ea1d2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 13:55:02 +0200 Subject: [PATCH 0612/1900] Document how to test with iOS/tvOS/watchOS/visionOS simulator --- src/doc/rustc-dev-guide/src/tests/running.md | 31 +++++++++++++++++-- .../rustc/src/platform-support/apple-ios.md | 22 ++++++++++--- .../rustc/src/platform-support/apple-tvos.md | 13 ++------ .../src/platform-support/apple-visionos.md | 13 ++------ .../src/platform-support/apple-watchos.md | 13 ++------ 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 317b65f98cd3c..482f3c42578ae 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -339,9 +339,34 @@ results. The Docker image is set up to launch `remote-test-server` and the build tools use `remote-test-client` to communicate with the server to coordinate running tests (see [src/bootstrap/src/core/build_steps/test.rs]). -> **TODO** -> -> - Is there any support for using an iOS emulator? +To run on the iOS/tvOS/watchOS/visionOS simulator, we can similarly treat it as +a "remote" machine. A curious detail here is that the network is shared between +the simulator instance and the host macOS, so we can use the local loopback +address `127.0.0.1`. Something like the following should work: + +```sh +# Build the test server for the iOS simulator: +./x build src/tools/remote-test-server --target aarch64-apple-ios-sim + +# If you already have a simulator instance open, copy the device UUID from: +xcrun simctl list devices booted +UDID=01234567-89AB-CDEF-0123-456789ABCDEF + +# Alternatively, create and boot a new simulator instance: +xcrun simctl list runtimes +xcrun simctl list devicetypes +UDID=$(xcrun simctl create $CHOSEN_DEVICE_TYPE $CHOSEN_RUNTIME) +xcrun simctl boot $UDID +# See https://nshipster.com/simctl/ for details. + +# Spawn the runner on port 12345: +xcrun simctl spawn $UDID ./build/host/stage2-tools/aarch64-apple-ios-sim/release/remote-test-server -v --bind 127.0.0.1:12345 + +# In a new terminal, run tests via the runner: +export TEST_DEVICE_ADDR="127.0.0.1:12345" +./x test --host='' --target aarch64-apple-ios-sim --skip tests/debuginfo +# FIXME(madsmtm): Allow debuginfo tests to work (maybe needs `.dSYM` folder to be copied to the target?). +``` [armhf-gnu]: https://github.com/rust-lang/rust/tree/master/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile [QEMU]: https://www.qemu.org/ diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index 586afa652262b..3ac1470475468 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -66,6 +66,11 @@ Rust programs can be built for these targets by specifying `--target`, if $ rustc --target aarch64-apple-ios your-code.rs ``` +Or if using Cargo and `-Zbuild-std`: +```console +$ cargo +nightly build -Zbuild-std --target armv7s-apple-ios +``` + The simulator variants can be differentiated from the variants running on-device with the `target_env = "sim"` cfg (or `target_abi = "sim"` before Rust CURRENT_RUSTC_VERSION). @@ -73,7 +78,7 @@ Rust CURRENT_RUSTC_VERSION). ```rust if cfg!(all(target_vendor = "apple", target_env = "sim")) { // Do something on the iOS/tvOS/visionOS/watchOS Simulator. -} { +} else { // Everything else, like Windows and non-Simulator iOS. } ``` @@ -82,8 +87,15 @@ This is similar to the `TARGET_OS_SIMULATOR` define in C code. ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. +Running and testing your code naturally requires either an actual device +running iOS, or the equivalent Xcode simulator environment. There exists +several tools in the ecosystem for running a Cargo project on one of these. +One of these tools is [`cargo-dinghy`]. [madsmtm/objc2#459] contains a more +exhaustive list. + +See also [testing on emulators in the `rustc-dev-guide`][test-sim] for +instructions on running the standard library's test suite. -It hopefully will be possible to improve this in the future. +[`cargo-dinghy`]: https://github.com/sonos/dinghy +[madsmtm/objc2#459]: https://github.com/madsmtm/objc2/issues/459 +[test-sim]: https://rustc-dev-guide.rust-lang.org/tests/running.html#testing-on-emulators diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 193d64666121e..a952d8e230d44 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -65,17 +65,8 @@ Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-tvos your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index ed96912da7a48..2ac069248ee91 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -46,20 +46,11 @@ be fixed in [#124560](https://github.com/rust-lang/rust/pull/124560). ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-visionos-sim your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). ## Cross-compilation toolchains and C code diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 6ac09d0d1e538..c1a009614250a 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -50,17 +50,8 @@ Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-watchos-sim your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). From 37be93497e5318fee712dabb70631f9676f07701 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 09:17:20 +0200 Subject: [PATCH 0613/1900] Fix test suite in iOS/tvOS/watchOS/visionOS simulator --- library/std/src/os/unix/process.rs | 2 + library/std/src/process.rs | 16 +-- library/std/src/process/tests.rs | 102 ++++++++++++++---- .../std/src/sys/process/unix/unix/tests.rs | 1 + library/std/tests/process_spawning.rs | 1 + tests/ui/backtrace/apple-no-dsymutil.rs | 1 + tests/ui/backtrace/dylib-dep.rs | 4 + tests/ui/backtrace/line-tables-only.rs | 4 + tests/ui/command/command-current-dir.rs | 2 + tests/ui/command/command-exec.rs | 2 + tests/ui/command/command-pre-exec.rs | 2 + tests/ui/command/command-uid-gid.rs | 2 + .../ui/compiletest-self-test/test-aux-bin.rs | 1 + ...xporting-impl-from-root-causes-ice-2472.rs | 5 +- tests/ui/issues/issue-45731.rs | 4 + tests/ui/process/core-run-destroy.rs | 4 + tests/ui/process/env-funky-keys.rs | 19 +++- tests/ui/process/fds-are-cloexec.rs | 11 +- tests/ui/process/println-with-broken-pipe.rs | 4 + tests/ui/process/process-envs.rs | 4 + tests/ui/process/process-panic-after-fork.rs | 2 + tests/ui/process/process-remove-from-env.rs | 4 + tests/ui/process/process-sigpipe.rs | 4 + tests/ui/process/process-spawn-failure.rs | 4 + tests/ui/runtime/backtrace-debuginfo.rs | 4 + .../runtime/on-broken-pipe/child-processes.rs | 1 + tests/ui/runtime/on-broken-pipe/inherit.rs | 1 + 27 files changed, 179 insertions(+), 32 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 09429af06e386..5b7b5a8ea803d 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -406,8 +406,10 @@ pub trait ChildExt: Sealed { /// use libc::SIGTERM; /// /// fn main() -> io::Result<()> { + /// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) { /// let child = Command::new("cat").stdin(Stdio::piped()).spawn()?; /// child.send_signal(SIGTERM)?; + /// # } /// Ok(()) /// } /// ``` diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 48265de90c4d6..0883e56342c8f 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -532,6 +532,7 @@ impl fmt::Debug for ChildStderr { /// to be changed (for example, by adding arguments) prior to spawning: /// /// ``` +/// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) { /// use std::process::Command; /// /// let output = if cfg!(target_os = "windows") { @@ -548,6 +549,7 @@ impl fmt::Debug for ChildStderr { /// }; /// /// let hello = output.stdout; +/// # } /// ``` /// /// `Command` can be reused to spawn multiple processes. The builder methods @@ -1348,7 +1350,7 @@ impl Output { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(all(unix, not(target_os = "android")))] { + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; /// assert!(Command::new("false").output().unwrap().exit_ok().is_err()); /// # } @@ -1695,7 +1697,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// # test().unwrap(); /// # } /// ``` @@ -1724,7 +1726,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// # test().unwrap(); /// # } /// ``` @@ -1800,7 +1802,7 @@ impl ExitStatus { /// /// ``` /// #![feature(exit_status_error)] - /// # if cfg!(unix) { + /// # if cfg!(all(unix, not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::process::Command; /// /// let status = Command::new("ls") @@ -1907,7 +1909,7 @@ impl crate::sealed::Sealed for ExitStatusError {} /// /// ``` /// #![feature(exit_status_error)] -/// # if cfg!(all(unix, not(target_os = "android"))) { +/// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::process::{Command, ExitStatusError}; /// /// fn run(cmd: &str) -> Result<(), ExitStatusError> { @@ -1950,7 +1952,7 @@ impl ExitStatusError { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(all(unix, not(target_os = "android")))] { + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; /// /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); @@ -1975,7 +1977,7 @@ impl ExitStatusError { /// ``` /// #![feature(exit_status_error)] /// - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::num::NonZero; /// use std::process::Command; /// diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 5879914ca206a..12c5130defe5a 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -5,7 +5,15 @@ use crate::mem::MaybeUninit; use crate::str; fn known_command() -> Command { - if cfg!(windows) { Command::new("help") } else { Command::new("echo") } + if cfg!(windows) { + Command::new("help") + } else if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) { + // iOS/tvOS/watchOS/visionOS have a very limited set of commandline + // binaries available. + Command::new("log") + } else { + Command::new("echo") + } } #[cfg(target_os = "android")] @@ -19,7 +27,10 @@ fn shell_cmd() -> Command { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn smoke() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 0"]).spawn() @@ -41,7 +52,10 @@ fn smoke_failure() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn exit_reported_right() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn() @@ -56,7 +70,10 @@ fn exit_reported_right() { #[test] #[cfg(unix)] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn signal_reported_right() { use crate::os::unix::process::ExitStatusExt; @@ -80,7 +97,10 @@ pub fn run_output(mut cmd: Command) -> String { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn stdout_works() { if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); @@ -94,7 +114,11 @@ fn stdout_works() { } #[test] -#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] +#[cfg_attr(windows, ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn set_current_dir_works() { // On many Unix platforms this will use the posix_spawn path. let mut cmd = shell_cmd(); @@ -116,7 +140,11 @@ fn set_current_dir_works() { } #[test] -#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] +#[cfg_attr(windows, ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn stdin_works() { let mut p = shell_cmd() .arg("-c") @@ -134,7 +162,10 @@ fn stdin_works() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn child_stdout_read_buf() { let mut cmd = if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); @@ -165,7 +196,10 @@ fn child_stdout_read_buf() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_status() { let mut status = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() @@ -191,7 +225,10 @@ fn test_process_output_fail_to_start() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_output_output() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() @@ -206,7 +243,10 @@ fn test_process_output_output() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_output_error() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() @@ -221,7 +261,10 @@ fn test_process_output_error() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_finish_once() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() @@ -232,7 +275,10 @@ fn test_finish_once() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_finish_twice() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() @@ -244,7 +290,10 @@ fn test_finish_twice() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_wait_with_output_once() { let prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() @@ -279,7 +328,10 @@ pub fn env_cmd() -> Command { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_override_env() { use crate::env; @@ -302,7 +354,10 @@ fn test_override_env() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_add_to_env() { let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); let output = String::from_utf8_lossy(&result.stdout).to_string(); @@ -314,7 +369,10 @@ fn test_add_to_env() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_capture_env_at_spawn() { use crate::env; @@ -378,7 +436,10 @@ fn test_interior_nul_in_current_dir_is_error() { // Regression tests for #30862. #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no `env` cmd available" +)] fn test_interior_nul_in_env_key_is_error() { match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), @@ -387,7 +448,10 @@ fn test_interior_nul_in_env_key_is_error() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no `env` cmd available" +)] fn test_interior_nul_in_env_value_is_error() { match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), diff --git a/library/std/src/sys/process/unix/unix/tests.rs b/library/std/src/sys/process/unix/unix/tests.rs index f4d6ac6b4e340..663ba61f966c9 100644 --- a/library/std/src/sys/process/unix/unix/tests.rs +++ b/library/std/src/sys/process/unix/unix/tests.rs @@ -51,6 +51,7 @@ fn exitstatus_display_tests() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] fn test_command_fork_no_unwind() { let got = catch_unwind(|| { let mut c = Command::new("echo"); diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 43b45cb2d2b5c..93f73ccad3ea4 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -7,6 +7,7 @@ mod common; #[test] // Process spawning not supported by Miri, Emscripten and wasi #[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] +#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/tests/ui/backtrace/apple-no-dsymutil.rs b/tests/ui/backtrace/apple-no-dsymutil.rs index e5aeced25ca35..00c8349d1293e 100644 --- a/tests/ui/backtrace/apple-no-dsymutil.rs +++ b/tests/ui/backtrace/apple-no-dsymutil.rs @@ -3,6 +3,7 @@ //@ compile-flags:-Cstrip=none //@ compile-flags:-g -Csplit-debuginfo=unpacked //@ only-apple +//@ ignore-remote needs the compiler-produced `.o` file to be copied to the device use std::process::Command; use std::str; diff --git a/tests/ui/backtrace/dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs index 05fdb9afef80d..cf420ec8d0697 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -7,6 +7,10 @@ //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized //@ ignore-musl musl doesn't support dynamic libraries (at least when the original test was written). +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device //@ needs-unwind //@ ignore-backends: gcc //@ compile-flags: -g -Copt-level=0 -Cstrip=none -Cforce-frame-pointers=yes diff --git a/tests/ui/backtrace/line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs index 6624c71e184b0..5863cc1d17d46 100644 --- a/tests/ui/backtrace/line-tables-only.rs +++ b/tests/ui/backtrace/line-tables-only.rs @@ -11,6 +11,10 @@ //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized //@ ignore-emscripten Requires custom symbolization code +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device //@ needs-unwind //@ aux-build: line-tables-only-helper.rs diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs index e264cbe4d7045..a6b51df5f176c 100644 --- a/tests/ui/command/command-current-dir.rs +++ b/tests/ui/command/command-current-dir.rs @@ -2,6 +2,8 @@ //@ no-prefer-dynamic We move the binary around, so do not depend dynamically on libstd //@ needs-subprocess //@ ignore-fuchsia Needs directory creation privilege +//@ ignore-tvos `Command::current_dir` requires fork, which is prohibited +//@ ignore-watchos `Command::current_dir` requires fork, which is prohibited use std::env; use std::fs; diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs index 77336377e8899..870f8b047b9a2 100644 --- a/tests/ui/command/command-exec.rs +++ b/tests/ui/command/command-exec.rs @@ -3,6 +3,8 @@ //@ only-unix (this is a unix-specific test) //@ needs-subprocess //@ ignore-fuchsia no execvp syscall provided +//@ ignore-tvos execvp is prohibited +//@ ignore-watchos execvp is prohibited use std::env; use std::os::unix::process::CommandExt; diff --git a/tests/ui/command/command-pre-exec.rs b/tests/ui/command/command-pre-exec.rs index 7299f357bd078..a62ab0b5ed6a7 100644 --- a/tests/ui/command/command-pre-exec.rs +++ b/tests/ui/command/command-pre-exec.rs @@ -2,6 +2,8 @@ //@ only-unix (this is a unix-specific test) //@ needs-subprocess //@ ignore-fuchsia no execvp syscall +//@ ignore-tvos execvp is prohibited +//@ ignore-watchos execvp is prohibited #![feature(rustc_private)] diff --git a/tests/ui/command/command-uid-gid.rs b/tests/ui/command/command-uid-gid.rs index f54a0f50708ba..ef0653eb2cb4e 100644 --- a/tests/ui/command/command-uid-gid.rs +++ b/tests/ui/command/command-uid-gid.rs @@ -1,6 +1,8 @@ //@ run-pass //@ ignore-android //@ ignore-fuchsia no '/bin/sh', '/bin/ls' +//@ ignore-tvos `Command::uid/gid` requires fork, which is prohibited +//@ ignore-watchos `Command::uid/gid` requires fork, which is prohibited //@ needs-subprocess #![feature(rustc_private)] diff --git a/tests/ui/compiletest-self-test/test-aux-bin.rs b/tests/ui/compiletest-self-test/test-aux-bin.rs index c1c28e12b3b5a..9ac17e6e14619 100644 --- a/tests/ui/compiletest-self-test/test-aux-bin.rs +++ b/tests/ui/compiletest-self-test/test-aux-bin.rs @@ -1,4 +1,5 @@ //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ aux-bin: print-it-works.rs //@ run-pass diff --git a/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs b/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs index 86d637b579dc6..c6cfae1e5452c 100644 --- a/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs +++ b/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs @@ -1,6 +1,9 @@ //@ run-pass //@ aux-build:exporting-impl-from-root-causes-ice-2472-b.rs - +//@ ignore-ios FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-tvos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-watchos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-visionos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? extern crate exporting_impl_from_root_causes_ice_2472_b as lib; diff --git a/tests/ui/issues/issue-45731.rs b/tests/ui/issues/issue-45731.rs index 49335362dd0f7..db11d1dbef1c7 100644 --- a/tests/ui/issues/issue-45731.rs +++ b/tests/ui/issues/issue-45731.rs @@ -1,6 +1,10 @@ //@ run-pass #![allow(unused_variables)] //@ compile-flags:--test -g +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device #[cfg(target_vendor = "apple")] #[test] diff --git a/tests/ui/process/core-run-destroy.rs b/tests/ui/process/core-run-destroy.rs index f4be54da8fe67..f381997ef7931 100644 --- a/tests/ui/process/core-run-destroy.rs +++ b/tests/ui/process/core-run-destroy.rs @@ -8,6 +8,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'cat' and 'sleep' //@ ignore-fuchsia no 'cat' +//@ ignore-ios no 'cat' and 'sleep' +//@ ignore-tvos no 'cat' and 'sleep' +//@ ignore-watchos no 'cat' and 'sleep' +//@ ignore-visionos no 'cat' and 'sleep' // N.B., these tests kill child processes. Valgrind sees these children as leaking // memory, which makes for some *confusing* logs. That's why these are here diff --git a/tests/ui/process/env-funky-keys.rs b/tests/ui/process/env-funky-keys.rs index a4a71c94020cd..193659bea293e 100644 --- a/tests/ui/process/env-funky-keys.rs +++ b/tests/ui/process/env-funky-keys.rs @@ -1,8 +1,7 @@ //@ run-pass //@ edition: 2021 -// Ignore this test on Android, because it segfaults there. -//@ ignore-android +//@ ignore-android segfaults //@ ignore-windows //@ ignore-wasm32 no execve //@ ignore-sgx no execve @@ -24,6 +23,9 @@ use std::ptr; fn main() { if env::args_os().count() == 2 { for (key, value) in env::vars_os() { + if key == "DYLD_ROOT_PATH" { + continue; + } panic!("found env value {:?} {:?}", key, value); } return; @@ -35,7 +37,18 @@ fn main() { .as_bytes()).unwrap(); let filename: *const c_char = current_exe.as_ptr(); let argv: &[*const c_char] = &[filename, filename, ptr::null()]; - let envp: &[*const c_char] = &[c"FOOBAR".as_ptr(), ptr::null()]; + + let root; + let envp: &[*const c_char] = if cfg!(all(target_vendor = "apple", target_env = "sim")) { + // Workaround: iOS/tvOS/watchOS/visionOS simulators need the root path + // from the current process. + root = format!("DYLD_ROOT_PATH={}\0", std::env::var("DYLD_ROOT_PATH").unwrap()); + &[c"FOOBAR".as_ptr(), root.as_ptr().cast(), ptr::null()] + } else { + // Try to set an environment variable without a value. + &[c"FOOBAR".as_ptr(), ptr::null()] + }; + unsafe { execve(filename, &argv[0], &envp[0]); } diff --git a/tests/ui/process/fds-are-cloexec.rs b/tests/ui/process/fds-are-cloexec.rs index f6678379dd675..0fae7c2b5024d 100644 --- a/tests/ui/process/fds-are-cloexec.rs +++ b/tests/ui/process/fds-are-cloexec.rs @@ -74,8 +74,15 @@ fn child(args: &[String]) { let fd: libc::c_int = arg.parse().unwrap(); unsafe { assert_eq!(libc::read(fd, b.as_mut_ptr() as *mut _, 2), -1); - assert_eq!(io::Error::last_os_error().raw_os_error(), - Some(libc::EBADF)); + let raw = io::Error::last_os_error().raw_os_error(); + if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) { + // Workaround: iOS/tvOS/watchOS/visionOS seems to treat `tcp6` + // as a directory? + if raw == Some(libc::EISDIR) { + continue; + } + } + assert_eq!(raw, Some(libc::EBADF)); } } } diff --git a/tests/ui/process/println-with-broken-pipe.rs b/tests/ui/process/println-with-broken-pipe.rs index 58b83a2dd9a2f..e87e207737027 100644 --- a/tests/ui/process/println-with-broken-pipe.rs +++ b/tests/ui/process/println-with-broken-pipe.rs @@ -5,6 +5,10 @@ //@ ignore-fuchsia //@ ignore-horizon //@ ignore-android +//@ ignore-ios no 'head' +//@ ignore-tvos no 'head' +//@ ignore-watchos no 'head' +//@ ignore-visionos no 'head' //@ ignore-backends: gcc //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" //@ compile-flags: -Zon-broken-pipe=error diff --git a/tests/ui/process/process-envs.rs b/tests/ui/process/process-envs.rs index 98052f1d3a5d7..cbe16704a8e19 100644 --- a/tests/ui/process/process-envs.rs +++ b/tests/ui/process/process-envs.rs @@ -2,6 +2,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' +//@ ignore-ios no 'env' +//@ ignore-tvos no 'env' +//@ ignore-watchos no 'env' +//@ ignore-visionos no 'env' use std::process::Command; use std::env; diff --git a/tests/ui/process/process-panic-after-fork.rs b/tests/ui/process/process-panic-after-fork.rs index 6e0267e0a54f5..653ff6ce314d9 100644 --- a/tests/ui/process/process-panic-after-fork.rs +++ b/tests/ui/process/process-panic-after-fork.rs @@ -3,6 +3,8 @@ //@ only-unix //@ needs-subprocess //@ ignore-fuchsia no fork +//@ ignore-tvos fork is prohibited +//@ ignore-watchos fork is prohibited #![feature(rustc_private)] #![feature(never_type)] diff --git a/tests/ui/process/process-remove-from-env.rs b/tests/ui/process/process-remove-from-env.rs index c1a2b2daf5b63..68c3909b15ae0 100644 --- a/tests/ui/process/process-remove-from-env.rs +++ b/tests/ui/process/process-remove-from-env.rs @@ -2,6 +2,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' +//@ ignore-ios no 'env' +//@ ignore-tvos no 'env' +//@ ignore-watchos no 'env' +//@ ignore-visionos no 'env' use std::process::Command; use std::env; diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs index 3ecf271599d23..574d79ee1ddaf 100644 --- a/tests/ui/process/process-sigpipe.rs +++ b/tests/ui/process/process-sigpipe.rs @@ -15,6 +15,10 @@ //@ ignore-vxworks no 'sh' //@ ignore-fuchsia no 'sh' +//@ ignore-ios no 'sh' +//@ ignore-tvos no 'sh' +//@ ignore-watchos no 'sh' +//@ ignore-visionos no 'sh' //@ needs-threads //@ only-unix SIGPIPE is a unix feature diff --git a/tests/ui/process/process-spawn-failure.rs b/tests/ui/process/process-spawn-failure.rs index 0950b044c97ca..ac2c34bc7836d 100644 --- a/tests/ui/process/process-spawn-failure.rs +++ b/tests/ui/process/process-spawn-failure.rs @@ -9,6 +9,10 @@ //@ ignore-vxworks no 'ps' //@ ignore-fuchsia no 'ps' //@ ignore-nto no 'ps' +//@ ignore-ios no 'ps' +//@ ignore-tvos no 'ps' +//@ ignore-watchos no 'ps' +//@ ignore-visionos no 'ps' #![feature(rustc_private)] diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index 5fb9943d6c3c4..5e91f22aec029 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -11,6 +11,10 @@ //@ compile-flags:-Cstrip=none //@ needs-subprocess //@ ignore-fuchsia Backtrace not symbolized, trace different line alignment +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device // FIXME(#117097): backtrace (possibly unwinding mechanism) seems to be different on at least // `i686-mingw` (32-bit windows-gnu)? cc #128911. diff --git a/tests/ui/runtime/on-broken-pipe/child-processes.rs b/tests/ui/runtime/on-broken-pipe/child-processes.rs index c0c8ad4e2f5e6..b7022e1b09d90 100644 --- a/tests/ui/runtime/on-broken-pipe/child-processes.rs +++ b/tests/ui/runtime/on-broken-pipe/child-processes.rs @@ -1,5 +1,6 @@ //@ revisions: default error kill inherit //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ only-unix because SIGPIPE is a unix thing //@ ignore-backends: gcc //@ run-pass diff --git a/tests/ui/runtime/on-broken-pipe/inherit.rs b/tests/ui/runtime/on-broken-pipe/inherit.rs index f3c8140eaaef1..e99c7c7a0fe4f 100644 --- a/tests/ui/runtime/on-broken-pipe/inherit.rs +++ b/tests/ui/runtime/on-broken-pipe/inherit.rs @@ -1,4 +1,5 @@ //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ only-unix because SIGPIPE is a unix thing //@ aux-bin: assert-inherit-sig_dfl.rs //@ aux-bin: assert-inherit-sig_ign.rs From 024ea0274d0ac4c938712653e2f8e2e7fec619f2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 19 Sep 2025 19:55:02 +0800 Subject: [PATCH 0614/1900] Fix `else` completion before else keyword Example --- ```rust fn foo() { let x = if true { 1 } el$0 else { 2 }; } ``` **Before this PR**: ```text else~ k [LS] else if~ k [LS] ``` **After this PR**: ```text else if~ k [LS] ``` --- .../ide-completion/src/completions/expr.rs | 3 +- .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 4 +- .../ide-completion/src/tests/expression.rs | 128 +++++++++++++++++- 4 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a7df0ab3863b1..080875e016329 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -59,6 +59,7 @@ pub(crate) fn complete_expr_path( in_block_expr, in_breakable, after_if_expr, + before_else_kw, in_condition, incomplete_let, after_incomplete_let, @@ -386,7 +387,7 @@ pub(crate) fn complete_expr_path( add_keyword("let", "let $1 = $0;"); } - if after_if_expr || after_incomplete_let { + if !before_else_kw && (after_if_expr || after_incomplete_let) { add_keyword("else", "else {\n $0\n}"); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 007475688d209..9deaaf6631270 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -144,6 +144,7 @@ pub(crate) struct PathExprCtx<'db> { pub(crate) in_block_expr: bool, pub(crate) in_breakable: BreakableKind, pub(crate) after_if_expr: bool, + pub(crate) before_else_kw: bool, /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index b33a547dee972..7b78a9700ec56 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1282,11 +1282,12 @@ fn classify_name_ref<'db>( let after_incomplete_let = after_incomplete_let(it.clone()).is_some(); let incomplete_expr_stmt = it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none()); + let before_else_kw = before_else_kw(it); let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()) - || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it); + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw; let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); @@ -1302,6 +1303,7 @@ fn classify_name_ref<'db>( in_block_expr, in_breakable: in_loop_body, after_if_expr, + before_else_kw, in_condition, ref_expr_parent, after_amp, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 79137104b568d..db9a7b2efe178 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1673,7 +1673,133 @@ fn foo() { let x = if foo {} $0 else {}; } kw async kw const kw crate:: - kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 else if true {}; } +"#, + expect![[r#" + fn foo fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0 else if true {} else {}; } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 else if true {} else {}; } +"#, + expect![[r#" + fn foo fn() + bt u32 u32 + kw async + kw const + kw crate:: kw else if kw enum kw extern From bf5fe6e44c2cce2877b25686dd1c0bb3c5d64d80 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 22:35:46 +0900 Subject: [PATCH 0615/1900] minor: Get rid of unused deps `chalk-solve` and `chalk-recursive` --- src/tools/rust-analyzer/Cargo.lock | 88 ++++--------------- src/tools/rust-analyzer/Cargo.toml | 4 - .../rust-analyzer/crates/hir-ty/Cargo.toml | 2 - .../rust-analyzer/crates/hir-ty/src/traits.rs | 10 --- 4 files changed, 16 insertions(+), 88 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 97c4f06dd5960..097bb81f66765 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -279,35 +279,6 @@ dependencies = [ "chalk-derive", ] -[[package]] -name = "chalk-recursive" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4" -dependencies = [ - "chalk-derive", - "chalk-ir", - "chalk-solve", - "rustc-hash 1.1.0", - "tracing", -] - -[[package]] -name = "chalk-solve" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927" -dependencies = [ - "chalk-derive", - "chalk-ir", - "ena", - "indexmap", - "itertools 0.12.1", - "petgraph", - "rustc-hash 1.1.0", - "tracing", -] - [[package]] name = "clap" version = "4.5.42" @@ -574,12 +545,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.1.2" @@ -706,7 +671,7 @@ dependencies = [ "hir-ty", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "smallvec", @@ -739,7 +704,7 @@ dependencies = [ "hir-expand", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "mbe", "query-group-macro", @@ -773,7 +738,7 @@ dependencies = [ "either", "expect-test", "intern", - "itertools 0.14.0", + "itertools", "mbe", "parser", "query-group-macro", @@ -799,8 +764,6 @@ dependencies = [ "bitflags 2.9.1", "chalk-derive", "chalk-ir", - "chalk-recursive", - "chalk-solve", "cov-mark", "either", "ena", @@ -809,7 +772,7 @@ dependencies = [ "hir-expand", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "oorandom", "project-model", @@ -949,7 +912,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools 0.14.0", + "itertools", "nohash-hasher", "oorandom", "profile", @@ -977,7 +940,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "smallvec", "stdx", "syntax", @@ -995,7 +958,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "smallvec", "stdx", "syntax", @@ -1018,7 +981,7 @@ dependencies = [ "fst", "hir", "indexmap", - "itertools 0.14.0", + "itertools", "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr", "nohash-hasher", @@ -1049,7 +1012,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "paths", "serde_json", "stdx", @@ -1067,7 +1030,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "parser", "syntax", "test-fixture", @@ -1146,15 +1109,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -1283,7 +1237,7 @@ dependencies = [ "hir-expand", "ide-db", "intern", - "itertools 0.14.0", + "itertools", "proc-macro-api", "project-model", "span", @@ -1640,16 +1594,6 @@ dependencies = [ "libc", ] -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1784,7 +1728,7 @@ dependencies = [ "cfg", "expect-test", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "rustc-hash 2.1.1", @@ -2062,7 +2006,7 @@ dependencies = [ "ide-ssr", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "load-cargo", "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", @@ -2400,7 +2344,7 @@ dependencies = [ "backtrace", "crossbeam-channel", "crossbeam-utils", - "itertools 0.14.0", + "itertools", "jod-thread", "libc", "miow", @@ -2436,7 +2380,7 @@ version = "0.0.0" dependencies = [ "either", "expect-test", - "itertools 0.14.0", + "itertools", "parser", "rayon", "rowan", @@ -3285,7 +3229,7 @@ dependencies = [ "edition", "either", "flate2", - "itertools 0.14.0", + "itertools", "proc-macro2", "quote", "stdx", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 513af11a87aad..cfbd8a56b76fd 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -37,9 +37,7 @@ debug = 2 [patch.'crates-io'] # rowan = { path = "../rowan" } -# chalk-solve = { path = "../chalk/chalk-solve" } # chalk-ir = { path = "../chalk/chalk-ir" } -# chalk-recursive = { path = "../chalk/chalk-recursive" } # chalk-derive = { path = "../chalk/chalk-derive" } # line-index = { path = "lib/line-index" } # la-arena = { path = "lib/la-arena" } @@ -111,9 +109,7 @@ arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-solve = { version = "0.104.0", default-features = false } chalk-ir = "0.104.0" -chalk-recursive = { version = "0.104.0", default-features = false } chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 64182110e1862..138d02e5a6105 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -24,9 +24,7 @@ oorandom = "11.1.5" tracing.workspace = true rustc-hash.workspace = true scoped-tls = "1.0.1" -chalk-solve.workspace = true chalk-ir.workspace = true -chalk-recursive.workspace = true chalk-derive.workspace = true la-arena.workspace = true triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 8b6836c0e90bf..8095d702be489 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -4,7 +4,6 @@ use core::fmt; use std::hash::Hash; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; -use chalk_solve::rust_ir; use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; @@ -405,15 +404,6 @@ impl FnTrait { } } - pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind { - // Chalk doesn't support async fn traits. - match self { - FnTrait::AsyncFnOnce | FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce, - FnTrait::AsyncFnMut | FnTrait::FnMut => rust_ir::ClosureKind::FnMut, - FnTrait::AsyncFn | FnTrait::Fn => rust_ir::ClosureKind::Fn, - } - } - pub fn method_name(self) -> Name { match self { FnTrait::FnOnce => Name::new_symbol_root(sym::call_once), From 9ba9869b335b56823753a6ce6614f8dffee4fcf3 Mon Sep 17 00:00:00 2001 From: Oblarg Date: Fri, 19 Sep 2025 10:03:14 -0400 Subject: [PATCH 0616/1900] address review feedback --- .../crates/hir-ty/src/lower_nextsolver.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index b8e0599dba289..0076446a958b0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -285,6 +285,29 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { const_type, self.resolver.krate(), ), + hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { + if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { + // Only handle negation for signed integers and floats + match literal { + hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { + if let Some(negated_literal) = literal.clone().negate() { + intern_const_ref( + self.db, + &negated_literal.into(), + const_type, + self.resolver.krate(), + ) + } else { + unknown_const(const_type) + } + } + // For unsigned integers, chars, bools, etc., negation is not meaningful + _ => unknown_const(const_type), + } + } else { + unknown_const(const_type) + } + } _ => unknown_const(const_type), } } From 58814bce6cfb8faf62979c7d043347e0526d7e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 19 Sep 2025 16:21:34 +0200 Subject: [PATCH 0617/1900] Allow running `x ` from a different directory --- src/bootstrap/src/core/builder/mod.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 75c8ee365284e..8226b4325b6b8 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -590,18 +590,30 @@ impl StepDescription { // Attempt to resolve paths to be relative to the builder source directory. let mut paths: Vec = paths .iter() - .map(|p| { + .map(|original_path| { + let mut path = original_path.clone(); + + // Someone could run `x ` from a different repository than the source + // directory. + // In that case, we should not try to resolve the paths relative to the working + // directory, but rather relative to the source directory. + // So we forcefully "relocate" the path to the source directory here. + if !path.is_absolute() { + path = builder.src.join(path); + } + // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` - if !p.exists() { - return p.clone(); + if !path.exists() { + // Use the original path here + return original_path.clone(); } // Make the path absolute, strip the prefix, and convert to a PathBuf. - match std::path::absolute(p) { + match std::path::absolute(&path) { Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), Err(e) => { eprintln!("ERROR: {e:?}"); - panic!("Due to the above error, failed to resolve path: {p:?}"); + panic!("Due to the above error, failed to resolve path: {path:?}"); } } }) From 4320de6424626cb7cd798973e22aef15dae3ba58 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 14:22:26 +0000 Subject: [PATCH 0618/1900] fixes for numerous clippy warnings --- compiler/rustc_hir_typeck/src/pat.rs | 72 +++++++++++++--------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f735c0a416099..46accb76a184a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -47,7 +47,7 @@ You can read more about trait objects in the Trait Objects section of the Refere https://doc.rust-lang.org/reference/types.html#trait-objects"; fn is_number(text: &str) -> bool { - text.chars().all(|c: char| c.is_digit(10)) + text.chars().all(|c: char| c.is_ascii_digit()) } /// Information about the expected type at the top level of type checking a pattern. @@ -262,8 +262,9 @@ enum InheritedRefMatchRule { /// pattern matches a given type: /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; /// - If the underlying type is a reference, a reference pattern matches if it can eat either one - /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the - /// underlying type is `&mut` or the inherited reference is `&mut`. + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// /// If `false`, a reference pattern is only matched against the underlying type. /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and /// `ref_pat_eat_one_layer_2024_structural` feature gates. @@ -1069,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if !self.tcx.features().mut_ref() { feature_err( - &self.tcx.sess, + self.tcx.sess, sym::mut_ref, pat.span.until(ident.span), "binding cannot be both mutable and by-reference", @@ -1487,31 +1488,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_def_id: Option, ident: Ident, ) -> bool { - 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), - .. - })) => match self.tcx.hir_node(body_id.hir_id) { - hir::Node::Expr(expr) => { - if hir::is_range_literal(expr) { - let span = self.tcx.hir_span(body_id.hir_id); - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { - e.span_suggestion_verbose( - ident.span, - "you may want to move the range into the match block", - snip, - Applicability::MachineApplicable, - ); - return true; - } - } - } - _ => (), - }, - _ => (), - }, - _ => (), + if let Some(def_id) = opt_def_id + && let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, _, _, body_id), + .. + })) = self.tcx.hir_get_if_local(def_id) + && let hir::Node::Expr(expr) = self.tcx.hir_node(body_id.hir_id) + && hir::is_range_literal(expr) + { + let span = self.tcx.hir_span(body_id.hir_id); + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { + e.span_suggestion_verbose( + ident.span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable, + ); + return true; + } } false } @@ -1529,7 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = self.tcx.hir_res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); - if let [hir::PathSegment { ident, .. }] = &*segments { + if let [hir::PathSegment { ident, .. }] = segments { e.span_label( pat_span, format!( @@ -1557,17 +1551,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (None, None), }; - let is_range = match type_def_id.and_then(|id| self.tcx.as_lang_item(id)) { + let is_range = matches!( + type_def_id.and_then(|id| self.tcx.as_lang_item(id)), Some( LangItem::Range - | LangItem::RangeFrom - | LangItem::RangeTo - | LangItem::RangeFull - | LangItem::RangeInclusiveStruct - | LangItem::RangeToInclusive, - ) => true, - _ => false, - }; + | LangItem::RangeFrom + | LangItem::RangeTo + | LangItem::RangeFull + | LangItem::RangeInclusiveStruct + | LangItem::RangeToInclusive, + ) + ); if is_range { if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ From e500dd820bc703b5b727e691e89a8f879522a90f Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 14:46:12 +0000 Subject: [PATCH 0619/1900] fixes for numerous clippy warnings --- .../rustc_hir_typeck/src/method/suggest.rs | 42 ++++++++----------- compiler/rustc_hir_typeck/src/op.rs | 8 ++-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a39ac0fcb6e3e..024b9ee08c222 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2194,7 +2194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_associated_call_syntax( &self, err: &mut Diag<'_>, - static_candidates: &Vec, + static_candidates: &[CandidateSource], rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, @@ -2422,7 +2422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) + eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(func, ..) => func.span.contains(span), @@ -2484,7 +2484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { simplify_type(tcx, ty, TreatParams::InstantiateWithInfer) .and_then(|simp| { tcx.incoherent_impls(simp) - .into_iter() + .iter() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() @@ -2617,7 +2617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let ControlFlow::Break(Some(expr)) = - (LetVisitor { ident_name: seg1.ident.name }).visit_body(&body) + (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( @@ -2960,14 +2960,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>() .into(); for pred in &local_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - local_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + local_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if local_spans.primary_span().is_some() { @@ -3006,14 +3003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>() .into(); for pred in &foreign_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - foreign_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + foreign_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if foreign_spans.primary_span().is_some() { @@ -3595,7 +3589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // would take care of them. && !skippable.contains(&Some(pick.item.container_id(self.tcx))) // Do not suggest pinning when the method is directly on `Pin`. - && pick.item.impl_container(self.tcx).map_or(true, |did| { + && pick.item.impl_container(self.tcx).is_none_or(|did| { match self.tcx.type_of(did).skip_binder().kind() { ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(), _ => true, @@ -3653,7 +3647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![ ( rcvr.span.shrink_to_lo(), - format!("let mut pinned = std::pin::pin!("), + "let mut pinned = std::pin::pin!(".to_string(), ), ( rcvr.span.shrink_to_hi(), @@ -4128,7 +4122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let trait_span = self.tcx.def_span(trait_def_id); let mut multi_span: MultiSpan = trait_span.into(); - multi_span.push_span_label(trait_span, format!("this is the trait that is needed")); + multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string()); let descr = self.tcx.associated_item(item_def_id).descr(); let rcvr_ty = rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string()); @@ -4146,7 +4140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } multi_span.push_span_label( self.tcx.def_span(def_id), - format!("this is the trait that was imported"), + "this is the trait that was imported".to_string(), ); } err.span_note(multi_span, msg); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 054435379434c..a8e8582c51c49 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), + is_compatible_after_call, ) { // Cool } @@ -1170,8 +1170,8 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { } } -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be +/// Returns `true` if this is a built-in arithmetic operation (e.g., +/// u32 + u32, i16x4 == i16x4) and false if these types would have to be /// overloaded to be legal. There are two reasons that we distinguish /// builtin operations from overloaded ones (vs trying to drive /// everything uniformly through the trait system and intrinsics or @@ -1191,7 +1191,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match category.into() { + match category { BinOpCategory::Shortcircuit => true, BinOpCategory::Shift => { lhs.references_error() From 04f6b8f99242eb61756fe294dd9d46536c1c8571 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 16:48:05 +0200 Subject: [PATCH 0620/1900] fix ./x readdir logic when CDPATH is set --- x | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x b/x index 551cfe6efbf33..4fce0be219e7c 100755 --- a/x +++ b/x @@ -15,7 +15,8 @@ realpath() { if [ -L "$path" ]; then readlink -f "$path" elif [ -d "$path" ]; then - (cd -P "$path" && pwd) + # "cd" is not always silent (e.g. when CDPATH is set), so discard its output. + (cd -P "$path" >/dev/null && pwd) else echo "$(realpath "$(dirname "$path")")/$(basename "$path")" fi From fb7e89cc7268f7b02d39e2c61ab4bd2df458a586 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 9 Sep 2025 23:30:24 +0800 Subject: [PATCH 0621/1900] fix: `question_mark` FP on variables used after --- clippy_lints/src/question_mark.rs | 10 +++++++++- tests/ui/question_mark.fixed | 25 ++++++++++++++++++++++++ tests/ui/question_mark.rs | 32 +++++++++++++++++++++++++++++++ tests/ui/question_mark.stderr | 22 ++++++++++++++++++++- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4aa100a50e053..d3a5a5dddfbeb 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -6,7 +6,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, @@ -483,6 +484,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .filter(|e| *e) .is_none() { + if !is_copy(cx, caller_ty) + && let Some(hir_id) = path_to_local(let_expr) + && local_used_after_expr(cx, hir_id, expr) + { + return; + } + let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index ac81b324c2049..2c5ee02450389 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -475,3 +475,28 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = result?; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + result?; + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index b5866dac6b8f6..b9ff9d1565b2f 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -583,3 +583,35 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = match result { + //~^ question_mark + Ok(v) => v, + Err(e) => return Err(e), + }; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + //~^ question_mark + return Err(reason); + } + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1ecd936292e55..9b2896328e665 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -313,5 +313,25 @@ LL | | Err(err) => return Err(<&str as Into>::into(err)), LL | | }; | |_____^ help: try instead: `some_result?` -error: aborting due to 33 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:596:17 + | +LL | let x = match result { + | _________________^ +LL | | +LL | | Ok(v) => v, +LL | | Err(e) => return Err(e), +LL | | }; + | |_________^ help: try instead: `result?` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:609:9 + | +LL | / if let Err(reason) = result { +LL | | +LL | | return Err(reason); +LL | | } + | |_________^ help: replace it with: `result?;` + +error: aborting due to 35 previous errors From 3ab89abac41443b125f2440b8f525f56536d8ffc Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 19 Sep 2025 23:06:44 +0800 Subject: [PATCH 0622/1900] mbe: Fix feature gate for `macro_derive` --- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- tests/ui/feature-gates/feature-gate-macro-derive.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 946265eba8bdb..1d147a0385c68 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -702,7 +702,7 @@ pub fn compile_declarative_macro( kinds |= MacroKinds::DERIVE; let derive_keyword_span = p.prev_token.span; if !features.macro_derive() { - feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable") .emit(); } if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { diff --git a/tests/ui/feature-gates/feature-gate-macro-derive.stderr b/tests/ui/feature-gates/feature-gate-macro-derive.stderr index b7ca6717bd51f..177518edafb38 100644 --- a/tests/ui/feature-gates/feature-gate-macro-derive.stderr +++ b/tests/ui/feature-gates/feature-gate-macro-derive.stderr @@ -4,8 +4,8 @@ error[E0658]: `macro_rules!` derives are unstable LL | macro_rules! MyDerive { derive() {} => {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #83527 for more information - = help: add `#![feature(macro_attr)]` to the crate attributes to enable + = note: see issue #143549 for more information + = help: add `#![feature(macro_derive)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error From 3ba0c221aea356b73376784de50cf604ead1b441 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 18 Sep 2025 03:21:01 +0900 Subject: [PATCH 0623/1900] fix: Make flycheck clearing dependency-aware --- .../project-model/src/build_dependencies.rs | 11 +- .../project-model/src/cargo_workspace.rs | 57 ++++++++- .../crates/rust-analyzer/src/diagnostics.rs | 23 ++++ .../crates/rust-analyzer/src/flycheck.rs | 110 ++++++++++++++---- .../crates/rust-analyzer/src/global_state.rs | 23 ++++ .../src/handlers/notification.rs | 10 +- .../crates/rust-analyzer/src/main_loop.rs | 19 +-- .../crates/rust-analyzer/src/target_spec.rs | 3 + 8 files changed, 213 insertions(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 203173c11be40..5eda5af3ace0b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -9,7 +9,7 @@ use std::{cell::RefCell, io, mem, process::Command}; use base_db::Env; -use cargo_metadata::{Message, camino::Utf8Path}; +use cargo_metadata::{Message, PackageId, camino::Utf8Path}; use cfg::CfgAtom; use itertools::Itertools; use la_arena::ArenaMap; @@ -18,6 +18,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize as _; use stdx::never; use toolchain::Tool; +use triomphe::Arc; use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, @@ -284,7 +285,7 @@ impl WorkspaceBuildScripts { // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. - let mut by_id: FxHashMap = FxHashMap::default(); + let mut by_id: FxHashMap, Package> = FxHashMap::default(); for package in workspace.packages() { outputs.insert(package, BuildScriptOutput::default()); by_id.insert(workspace[package].id.clone(), package); @@ -323,7 +324,7 @@ impl WorkspaceBuildScripts { // ideally this would be something like: // with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)), // but owned trait objects aren't a thing - mut with_output_for: impl FnMut(&str, &mut dyn FnMut(&str, &mut BuildScriptOutput)), + mut with_output_for: impl FnMut(&PackageId, &mut dyn FnMut(&str, &mut BuildScriptOutput)), progress: &dyn Fn(String), ) -> io::Result> { let errors = RefCell::new(String::new()); @@ -346,7 +347,7 @@ impl WorkspaceBuildScripts { match message { Message::BuildScriptExecuted(mut message) => { - with_output_for(&message.package_id.repr, &mut |name, data| { + with_output_for(&message.package_id, &mut |name, data| { progress(format!("build script {name} run")); let cfgs = { let mut acc = Vec::new(); @@ -377,7 +378,7 @@ impl WorkspaceBuildScripts { }); } Message::CompilerArtifact(message) => { - with_output_for(&message.package_id.repr, &mut |name, data| { + with_output_for(&message.package_id, &mut |name, data| { progress(format!("proc-macro {name} built")); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index e613fd590c70b..adc0cc5094166 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -5,7 +5,7 @@ use std::str::from_utf8; use anyhow::Context; use base_db::Env; -use cargo_metadata::{CargoOpt, MetadataCommand}; +use cargo_metadata::{CargoOpt, MetadataCommand, PackageId}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -14,6 +14,7 @@ use serde_json::from_value; use span::Edition; use stdx::process::spawn_with_streaming_output; use toolchain::Tool; +use triomphe::Arc; use crate::cargo_config_file::make_lockfile_copy; use crate::{CfgOverrides, InvocationStrategy}; @@ -155,8 +156,8 @@ pub struct PackageData { pub features: FxHashMap>, /// List of features enabled on this package pub active_features: Vec, - /// String representation of package id - pub id: String, + /// Package id + pub id: Arc, /// Authors as given in the `Cargo.toml` pub authors: Vec, /// Description as given in the `Cargo.toml` @@ -173,6 +174,10 @@ pub struct PackageData { pub rust_version: Option, /// The contents of [package.metadata.rust-analyzer] pub metadata: RustAnalyzerPackageMetaData, + /// If this package is a member of the workspace, store all direct and transitive + /// dependencies as long as they are workspace members, to track dependency relationships + /// between members. + pub all_member_deps: Option>, } #[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)] @@ -334,6 +339,8 @@ impl CargoWorkspace { let mut is_virtual_workspace = true; let mut requires_rustc_private = false; + let mut members = FxHashSet::default(); + meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); for meta_pkg in meta.packages { let cargo_metadata::Package { @@ -356,6 +363,7 @@ impl CargoWorkspace { rust_version, .. } = meta_pkg; + let id = Arc::new(id); let meta = from_value::(metadata).unwrap_or_default(); let edition = match edition { cargo_metadata::Edition::E2015 => Edition::Edition2015, @@ -375,7 +383,7 @@ impl CargoWorkspace { let manifest = ManifestPath::try_from(AbsPathBuf::assert(manifest_path)).unwrap(); is_virtual_workspace &= manifest != ws_manifest_path; let pkg = packages.alloc(PackageData { - id: id.repr.clone(), + id: id.clone(), name: name.to_string(), version, manifest: manifest.clone(), @@ -395,7 +403,11 @@ impl CargoWorkspace { features: features.into_iter().collect(), active_features: Vec::new(), metadata: meta.rust_analyzer.unwrap_or_default(), + all_member_deps: None, }); + if is_member { + members.insert(pkg); + } let pkg_data = &mut packages[pkg]; requires_rustc_private |= pkg_data.metadata.rustc_private; pkg_by_id.insert(id, pkg); @@ -440,6 +452,43 @@ impl CargoWorkspace { .extend(node.features.into_iter().map(|it| it.to_string())); } + fn saturate_all_member_deps( + packages: &mut Arena, + to_visit: Package, + visited: &mut FxHashSet, + members: &FxHashSet, + ) { + let pkg_data = &mut packages[to_visit]; + + if !visited.insert(to_visit) { + return; + } + + let deps: Vec<_> = pkg_data + .dependencies + .iter() + .filter_map(|dep| { + let pkg = dep.pkg; + if members.contains(&pkg) { Some(pkg) } else { None } + }) + .collect(); + + let mut all_member_deps = FxHashSet::from_iter(deps.iter().copied()); + for dep in deps { + saturate_all_member_deps(packages, dep, visited, members); + if let Some(transitives) = &packages[dep].all_member_deps { + all_member_deps.extend(transitives); + } + } + + packages[to_visit].all_member_deps = Some(all_member_deps); + } + + let mut visited = FxHashSet::default(); + for member in members.iter() { + saturate_all_member_deps(&mut packages, *member, &mut visited, &members); + } + CargoWorkspace { packages, targets, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index ee50237c405e6..4bfad98b39976 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -120,6 +120,29 @@ impl DiagnosticCollection { } } + pub(crate) fn clear_check_older_than_for_package( + &mut self, + flycheck_id: usize, + package_id: Arc, + generation: DiagnosticsGeneration, + ) { + let Some(check) = self.check.get_mut(flycheck_id) else { + return; + }; + let package_id = Some(package_id); + let Some((_, checks)) = check + .per_package + .extract_if(|k, v| *k == package_id && v.generation < generation) + .next() + else { + return; + }; + self.changes.extend(checks.per_file.into_keys()); + if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { + fixes.remove(&package_id); + } + } + pub(crate) fn clear_native_for(&mut self, file_id: FileId) { self.native_syntax.remove(&file_id); self.native_semantic.remove(&file_id); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 315c45d5b6392..cded34be14a28 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -180,17 +180,27 @@ impl FlycheckHandle { pub(crate) fn restart_workspace(&self, saved_file: Option) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender - .send(StateChange::Restart { generation, package: None, saved_file, target: None }) + .send(StateChange::Restart { + generation, + scope: FlycheckScope::Workspace, + saved_file, + target: None, + }) .unwrap(); } /// Schedule a re-start of the cargo check worker to do a package wide check. - pub(crate) fn restart_for_package(&self, package: String, target: Option) { + pub(crate) fn restart_for_package( + &self, + package: Arc, + target: Option, + workspace_deps: Option>>, + ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender .send(StateChange::Restart { generation, - package: Some(package), + scope: FlycheckScope::Package { package, workspace_deps }, saved_file: None, target, }) @@ -213,8 +223,13 @@ impl FlycheckHandle { #[derive(Debug)] pub(crate) enum ClearDiagnosticsKind { - All, - OlderThan(DiagnosticsGeneration), + All(ClearScope), + OlderThan(DiagnosticsGeneration, ClearScope), +} + +#[derive(Debug)] +pub(crate) enum ClearScope { + Workspace, Package(Arc), } @@ -275,10 +290,15 @@ pub(crate) enum Progress { DidFailToRestart(String), } +enum FlycheckScope { + Workspace, + Package { package: Arc, workspace_deps: Option>> }, +} + enum StateChange { Restart { generation: DiagnosticsGeneration, - package: Option, + scope: FlycheckScope, saved_file: Option, target: Option, }, @@ -298,6 +318,7 @@ struct FlycheckActor { /// or the project root of the project. root: Arc, sysroot_root: Option, + scope: FlycheckScope, /// CargoHandle exists to wrap around the communication needed to be able to /// run `cargo check` without blocking. Currently the Rust standard library /// doesn't provide a way to read sub-process output without blocking, so we @@ -343,6 +364,7 @@ impl FlycheckActor { config, sysroot_root, root: Arc::new(workspace_root), + scope: FlycheckScope::Workspace, manifest_path, command_handle: None, command_receiver: None, @@ -376,7 +398,7 @@ impl FlycheckActor { } Event::RequestStateChange(StateChange::Restart { generation, - package, + scope, saved_file, target, }) => { @@ -389,11 +411,11 @@ impl FlycheckActor { } } + let command = self.check_command(&scope, saved_file.as_deref(), target); + self.scope = scope; self.generation = generation; - let Some(command) = - self.check_command(package.as_deref(), saved_file.as_deref(), target) - else { + let Some(command) = command else { continue; }; @@ -435,19 +457,55 @@ impl FlycheckActor { tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. // Clear everything for good measure - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::All, - }); + match &self.scope { + FlycheckScope::Workspace => { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), + }); + } + FlycheckScope::Package { package, workspace_deps } => { + for pkg in + std::iter::once(package).chain(workspace_deps.iter().flatten()) + { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Package( + pkg.clone(), + )), + }); + } + } + } } else if res.is_ok() { // We clear diagnostics for packages on // `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where // cargo may not report an artifact to our runner at all. To handle such // cases, clear stale diagnostics when flycheck completes successfully. - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::OlderThan(self.generation), - }); + match &self.scope { + FlycheckScope::Workspace => { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::OlderThan( + self.generation, + ClearScope::Workspace, + ), + }); + } + FlycheckScope::Package { package, workspace_deps } => { + for pkg in + std::iter::once(package).chain(workspace_deps.iter().flatten()) + { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::OlderThan( + self.generation, + ClearScope::Package(pkg.clone()), + ), + }); + } + } + } } self.clear_diagnostics_state(); @@ -475,7 +533,7 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::Package(package_id), + kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)), }); } } @@ -498,7 +556,9 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::Package(package_id.clone()), + kind: ClearDiagnosticsKind::All(ClearScope::Package( + package_id.clone(), + )), }); } } else if self.diagnostics_received @@ -507,7 +567,7 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::All, + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), }); } self.send(FlycheckMessage::AddDiagnostic { @@ -548,7 +608,7 @@ impl FlycheckActor { /// return None. fn check_command( &self, - package: Option<&str>, + scope: &FlycheckScope, saved_file: Option<&AbsPath>, target: Option, ) -> Option { @@ -564,9 +624,9 @@ impl FlycheckActor { } cmd.arg(command); - match package { - Some(pkg) => cmd.arg("-p").arg(pkg), - None => cmd.arg("--workspace"), + match scope { + FlycheckScope::Workspace => cmd.arg("--workspace"), + FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr), }; if let Some(tgt) = target { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 89d6fb8edc2e6..ce6644f725ca6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -9,6 +9,7 @@ use std::{ time::{Duration, Instant}, }; +use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; @@ -784,6 +785,7 @@ impl GlobalStateSnapshot { cargo_toml: package_data.manifest.clone(), crate_id, package: cargo.package_flag(package_data), + package_id: package_data.id.clone(), target: target_data.name.clone(), target_kind: target_data.kind, required_features: target_data.required_features.clone(), @@ -812,6 +814,27 @@ impl GlobalStateSnapshot { None } + pub(crate) fn all_workspace_dependencies_for_package( + &self, + package: &Arc, + ) -> Option>> { + for workspace in self.workspaces.iter() { + match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + + return cargo[package] + .all_member_deps + .as_ref() + .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); + } + _ => {} + } + } + None + } + pub(crate) fn file_exists(&self, file_id: FileId) -> bool { self.vfs.read().0.exists(file_id) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 68c91a653940b..87be09dcbd275 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -331,7 +331,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), + TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id), _ => return None, }; @@ -368,7 +368,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { _ => false, }); if let Some(idx) = package_workspace_idx { - world.flycheck[idx].restart_for_package(package, target); + let workspace_deps = + world.all_workspace_dependencies_for_package(&package); + world.flycheck[idx].restart_for_package( + package, + target, + workspace_deps, + ); } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index c6762f318326a..3e80e8b7bdfb5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -20,7 +20,7 @@ use crate::{ config::Config, diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics}, discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, - flycheck::{self, ClearDiagnosticsKind, FlycheckMessage}, + flycheck::{self, ClearDiagnosticsKind, ClearScope, FlycheckMessage}, global_state::{ FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, file_id_to_url, url_to_file_id, @@ -1042,17 +1042,22 @@ impl GlobalState { }; } } - FlycheckMessage::ClearDiagnostics { id, kind: ClearDiagnosticsKind::All } => { - self.diagnostics.clear_check(id) - } FlycheckMessage::ClearDiagnostics { id, - kind: ClearDiagnosticsKind::OlderThan(generation), - } => self.diagnostics.clear_check_older_than(id, generation), + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), + } => self.diagnostics.clear_check(id), FlycheckMessage::ClearDiagnostics { id, - kind: ClearDiagnosticsKind::Package(package_id), + kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)), } => self.diagnostics.clear_check_for_package(id, package_id), + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Workspace), + } => self.diagnostics.clear_check_older_than(id, generation), + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)), + } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation), FlycheckMessage::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => (Progress::Begin, None), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 7132e09146ebc..e532d155536c1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -2,12 +2,14 @@ use std::mem; +use cargo_metadata::PackageId; use cfg::{CfgAtom, CfgExpr}; use hir::sym; use ide::{Cancellable, Crate, FileId, RunnableKind, TestId}; use project_model::project_json::Runnable; use project_model::{CargoFeatures, ManifestPath, TargetKind}; use rustc_hash::FxHashSet; +use triomphe::Arc; use vfs::AbsPathBuf; use crate::global_state::GlobalStateSnapshot; @@ -52,6 +54,7 @@ pub(crate) struct CargoTargetSpec { pub(crate) workspace_root: AbsPathBuf, pub(crate) cargo_toml: ManifestPath, pub(crate) package: String, + pub(crate) package_id: Arc, pub(crate) target: String, pub(crate) target_kind: TargetKind, pub(crate) crate_id: Crate, From 7eea65f8e0544d3e51ce383513c0108e9d02e874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Fri, 19 Sep 2025 10:57:03 +0200 Subject: [PATCH 0624/1900] Stop linking rs{begin,end} on x86_64-*-windows-gnu Until now, x86_64-pc-windows-gnu linked `rsbegin.o` and `rsend.o` just like i686-pc-windows-gnu, even though they were no-ops for it. This was likely done for the simplicity back when it was introduced. Today the things are different and these startup/end objects harm other features, like `build-std`. Given the demotion of i686-pc-windows-gnu from tier 1, there is no point in hurting x86_64-pc-windows-gnu, which remains a tier 1. The files are still shipped in case downstream crates expect them, as in case of the unmaintained `xargo`. --- .../rustc_target/src/spec/base/windows_gnu.rs | 3 --- compiler/rustc_target/src/spec/crt_objects.rs | 17 ++++++++++++++--- .../src/spec/targets/i686_pc_windows_gnu.rs | 8 +++++++- src/bootstrap/src/core/build_steps/compile.rs | 2 ++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index 4ba1102198847..2867428e42f7a 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -93,10 +93,7 @@ pub(crate) fn opts() -> TargetOptions { binary_format: BinaryFormat::Coff, allows_weak_linkage: false, pre_link_args, - pre_link_objects: crt_objects::pre_mingw(), - post_link_objects: crt_objects::post_mingw(), pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), - post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), link_self_contained: LinkSelfContainedDefault::InferredForMingw, late_link_args, late_link_args_dynamic, diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index e3b6430a46371..2d84e78f25572 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -85,6 +85,17 @@ pub(super) fn post_musl_self_contained() -> CrtObjects { } pub(super) fn pre_mingw_self_contained() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt2.o"]), + (LinkOutputKind::DynamicPicExe, &["crt2.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt2.o"]), + (LinkOutputKind::StaticPicExe, &["crt2.o"]), + (LinkOutputKind::DynamicDylib, &["dllcrt2.o"]), + (LinkOutputKind::StaticDylib, &["dllcrt2.o"]), + ]) +} + +pub(super) fn pre_i686_mingw_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), @@ -95,15 +106,15 @@ pub(super) fn pre_mingw_self_contained() -> CrtObjects { ]) } -pub(super) fn post_mingw_self_contained() -> CrtObjects { +pub(super) fn post_i686_mingw_self_contained() -> CrtObjects { all("rsend.o") } -pub(super) fn pre_mingw() -> CrtObjects { +pub(super) fn pre_i686_mingw() -> CrtObjects { all("rsbegin.o") } -pub(super) fn post_mingw() -> CrtObjects { +pub(super) fn post_i686_mingw() -> CrtObjects { all("rsend.o") } diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index e775c8fc524c4..a0d403bd05e69 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -1,4 +1,6 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, base}; +use crate::spec::{ + Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, base, crt_objects, +}; pub(crate) fn target() -> Target { let mut base = base::windows_gnu::opts(); @@ -15,6 +17,10 @@ pub(crate) fn target() -> Target { &["-m", "i386pe", "--large-address-aware"], ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); + base.pre_link_objects = crt_objects::pre_i686_mingw(); + base.post_link_objects = crt_objects::post_i686_mingw(); + base.pre_link_objects_self_contained = crt_objects::pre_i686_mingw_self_contained(); + base.post_link_objects_self_contained = crt_objects::post_i686_mingw_self_contained(); Target { llvm_target: "i686-pc-windows-gnu".into(), diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 14104d7d1d783..6daf82d4e6834 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -895,6 +895,8 @@ impl Step for StartupObjects { fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { let for_compiler = self.compiler; let target = self.target; + // Even though no longer necessary on x86_64, they are kept for now to + // avoid potential issues in downstream crates. if !target.is_windows_gnu() { return vec![]; } From e2de670558111dc6ce46d764aed6cb3dc4222794 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 17:21:55 +0000 Subject: [PATCH 0625/1900] btree: safety comments for init and new --- library/alloc/src/collections/btree/node.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 37f784a322cad..b233e1740b7b7 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -67,6 +67,10 @@ struct LeafNode { impl LeafNode { /// Initializes a new `LeafNode` in-place. + /// + /// # Safety + /// + /// The caller must ensure that `this` points to a (possibly uninitialized) `LeafNode` unsafe fn init(this: *mut Self) { // As a general policy, we leave fields uninitialized if they can be, as this should // be both slightly faster and easier to track in Valgrind. @@ -79,9 +83,11 @@ impl LeafNode { /// Creates a new boxed `LeafNode`. fn new(alloc: A) -> Box { + let mut leaf = Box::new_uninit_in(alloc); unsafe { - let mut leaf = Box::new_uninit_in(alloc); + // SAFETY: `leaf` points to a `LeafNode` LeafNode::init(leaf.as_mut_ptr()); + // SAFETY: `leaf` was just initialized leaf.assume_init() } } From e32b975e840e99748b947884505b74ebe4bd23b9 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Thu, 18 Sep 2025 14:23:43 -0400 Subject: [PATCH 0626/1900] tests: relax expectations after llvm change 902ddda120a5 LLVM 22 is able to drop assumes that seem to not help further optimizations, which actually seems to dramatically _help_ further optimizations in some of our small test cases. --- .../issues/issue-122600-ptr-discriminant-update.rs | 7 +++++-- tests/codegen-llvm/vec_pop_push_noop.rs | 5 ++++- tests/codegen-llvm/vecdeque_pop_push.rs | 5 ++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs b/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs index 853a1ff36b109..631468d8c217a 100644 --- a/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs +++ b/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs @@ -1,4 +1,7 @@ //@ compile-flags: -Copt-level=3 +//@ revisions: new old +//@ [old] ignore-llvm-version: 22 - 99 +//@ [new] min-llvm-version: 22 #![crate_type = "lib"] @@ -22,8 +25,8 @@ pub unsafe fn update(s: *mut State) { // CHECK-NOT: memcpy // CHECK-NOT: 75{{3|4}} - // CHECK: %[[TAG:.+]] = load i8, ptr %s, align 1 - // CHECK-NEXT: trunc nuw i8 %[[TAG]] to i1 + // old: %[[TAG:.+]] = load i8, ptr %s, align 1 + // old-NEXT: trunc nuw i8 %[[TAG]] to i1 // CHECK-NOT: load // CHECK-NOT: store diff --git a/tests/codegen-llvm/vec_pop_push_noop.rs b/tests/codegen-llvm/vec_pop_push_noop.rs index 3e375219fe018..fadc6e99dc7a6 100644 --- a/tests/codegen-llvm/vec_pop_push_noop.rs +++ b/tests/codegen-llvm/vec_pop_push_noop.rs @@ -1,4 +1,7 @@ //@ compile-flags: -Copt-level=3 +//@ revisions: new old +//@ [old] ignore-llvm-version: 22 - 99 +//@ [new] min-llvm-version: 22 #![crate_type = "lib"] @@ -7,7 +10,7 @@ pub fn noop(v: &mut Vec) { // CHECK-NOT: grow_one // CHECK-NOT: call - // CHECK: tail call void @llvm.assume + // old: tail call void @llvm.assume // CHECK-NOT: grow_one // CHECK-NOT: call // CHECK: {{ret|[}]}} diff --git a/tests/codegen-llvm/vecdeque_pop_push.rs b/tests/codegen-llvm/vecdeque_pop_push.rs index 5afa1b2248b0e..7886db3fedeba 100644 --- a/tests/codegen-llvm/vecdeque_pop_push.rs +++ b/tests/codegen-llvm/vecdeque_pop_push.rs @@ -1,4 +1,7 @@ //@ compile-flags: -Copt-level=3 +//@ revisions: new old +//@ [old] ignore-llvm-version: 22 - 99 +//@ [new] min-llvm-version: 22 #![crate_type = "lib"] @@ -8,7 +11,7 @@ use std::collections::VecDeque; // CHECK-LABEL: @noop_back( pub fn noop_back(v: &mut VecDeque) { // CHECK-NOT: grow - // CHECK: tail call void @llvm.assume + // old: tail call void @llvm.assume // CHECK-NOT: grow // CHECK: ret if let Some(x) = v.pop_back() { From 696add0d477a65d6a79d97c6b8e0f4ab9268bb89 Mon Sep 17 00:00:00 2001 From: sysrex <769991+sysrex@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:04:48 +0100 Subject: [PATCH 0627/1900] chore: fixes #146756 --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aadc7c48ea839..5b52c3dfff61d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,8 +31,8 @@ bootstrapping, the compiler architecture, source code representation, and more. ## [Getting help](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) -There are many ways you can get help when you're stuck. Rust has many platforms for this: -[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on +There are many ways you can get help when you're stuck. Rust has two platforms for this: +[internals] and [rust-zulip]. It is recommended to ask for help on the [rust-zulip], but any of these platforms are great ways to seek help and even find a mentor! You can learn more about asking questions and getting help in the [Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide]. From 4b755141bc297444401397b09995bb2baafb3fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 19 Sep 2025 16:00:05 +0200 Subject: [PATCH 0628/1900] Simplify default value of `download-ci-llvm` --- src/bootstrap/src/core/config/config.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index efb7ad9169971..3dd1b0160a974 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2134,15 +2134,7 @@ pub fn parse_download_ci_llvm<'a>( asserts: bool, ) -> bool { let dwn_ctx = dwn_ctx.as_ref(); - - // We don't ever want to use `true` on CI, as we should not - // download upstream artifacts if there are any local modifications. - let default = if dwn_ctx.is_running_on_ci { - StringOrBool::String("if-unchanged".to_string()) - } else { - StringOrBool::Bool(true) - }; - let download_ci_llvm = download_ci_llvm.unwrap_or(default); + let download_ci_llvm = download_ci_llvm.unwrap_or(StringOrBool::Bool(true)); let if_unchanged = || { if rust_info.is_from_tarball() { @@ -2174,8 +2166,9 @@ pub fn parse_download_ci_llvm<'a>( ); } - if b && dwn_ctx.is_running_on_ci { - // On CI, we must always rebuild LLVM if there were any modifications to it + #[cfg(not(test))] + if b && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() { + // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it panic!( "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." ); From 82eed00d3979b74315f66201c6bf4d3f00d38c25 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Tue, 16 Sep 2025 01:48:24 -0600 Subject: [PATCH 0629/1900] chore(compiletest): Use newest anstyle-svg version --- Cargo.lock | 6 +++--- src/tools/compiletest/Cargo.toml | 2 +- tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg | 4 ++-- tests/ui/codemap_tests/huge_multispan_highlight.unicode.svg | 4 ++-- tests/ui/diagnostic-flags/colored-session-opt-error.svg | 4 ++-- tests/ui/error-emitter/E0308-clarification.svg | 2 +- tests/ui/error-emitter/highlighting.svg | 2 +- tests/ui/error-emitter/highlighting.windows.svg | 2 +- tests/ui/error-emitter/multiline-multipart-suggestion.svg | 2 +- .../multiline-multipart-suggestion.windows.svg | 2 +- tests/ui/error-emitter/multiline-removal-suggestion.svg | 4 ++-- tests/ui/error-emitter/unicode-output.svg | 2 +- ...ht-difference-between-expected-trait-and-found-trait.svg | 4 ++-- tests/ui/suggestions/incorrect-variant-literal.svg | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d39cfefea0c77..71343a48be771 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "anstyle-svg" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc03a770ef506fe1396c0e476120ac0e6523cf14b74218dd5f18cd6833326fa9" +checksum = "26b9ec8c976eada1b0f9747a3d7cc4eae3bef10613e443746e7487f26c872fde" dependencies = [ "anstyle", "anstyle-lossy", @@ -674,7 +674,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.1", ] [[package]] diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index cdada5a223062..6597c3c70f691 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -12,7 +12,7 @@ path = "src/bin/main.rs" [dependencies] # tidy-alphabetical-start -anstyle-svg = "0.1.3" +anstyle-svg = "0.1.11" build_helper = { path = "../../build_helper" } camino = "1" colored = "2" diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg b/tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg index 1cedbf75e4bf6..7ffbc64b074bf 100644 --- a/tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg +++ b/tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg @@ -1,7 +1,7 @@ - + u.field; &u.field; - &raw const u.field; + &raw const u.field; // this should be safe! let Union { field: _ }; // but not these From fd7f8b2b25d195d47682bbddb9de38b01ae9f6da Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 20 Sep 2025 17:58:32 +0200 Subject: [PATCH 0661/1900] [GCC backend] Ignore failing ui abi tests --- tests/ui/abi/abi-sysv64-arg-passing.rs | 1 + tests/ui/abi/abi-sysv64-register-usage.rs | 1 + tests/ui/abi/issue-28676.rs | 2 ++ tests/ui/abi/large-byval-align.rs | 1 + tests/ui/abi/numbers-arithmetic/float-struct.rs | 1 + tests/ui/abi/stack-probes-lto.rs | 1 + tests/ui/abi/stack-probes.rs | 1 + tests/ui/abi/stack-protector.rs | 1 + tests/ui/abi/struct-enums/struct-return.rs | 2 ++ tests/ui/abi/variadic-ffi.rs | 2 ++ tests/ui/abi/x86stdcall.rs | 1 + 11 files changed, 14 insertions(+) diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index c18752418a1d6..362a1862f9d4c 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -28,6 +28,7 @@ //@ ignore-arm //@ ignore-aarch64 //@ ignore-windows +//@ ignore-backends: gcc // Windows is ignored because bootstrap doesn't yet know to compile rust_test_helpers with // the sysv64 ABI on Windows. diff --git a/tests/ui/abi/abi-sysv64-register-usage.rs b/tests/ui/abi/abi-sysv64-register-usage.rs index d2fb2ae53ac73..cf1620db6f7be 100644 --- a/tests/ui/abi/abi-sysv64-register-usage.rs +++ b/tests/ui/abi/abi-sysv64-register-usage.rs @@ -6,6 +6,7 @@ //@ ignore-arm //@ ignore-aarch64 //@ needs-asm-support +//@ ignore-backends: gcc #[cfg(target_arch = "x86_64")] pub extern "sysv64" fn all_the_registers( diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs index 8640f5aad21c9..2abb4ce52b3b3 100644 --- a/tests/ui/abi/issue-28676.rs +++ b/tests/ui/abi/issue-28676.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![allow(dead_code)] #![allow(improper_ctypes)] diff --git a/tests/ui/abi/large-byval-align.rs b/tests/ui/abi/large-byval-align.rs index c1de841178fcd..69418e1cbc7b8 100644 --- a/tests/ui/abi/large-byval-align.rs +++ b/tests/ui/abi/large-byval-align.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Copt-level=0 //@ only-x86_64 //@ build-pass +//@ ignore-backends: gcc #[repr(align(536870912))] pub struct A(i64); diff --git a/tests/ui/abi/numbers-arithmetic/float-struct.rs b/tests/ui/abi/numbers-arithmetic/float-struct.rs index a958dc272722f..dc4c536a14e2d 100644 --- a/tests/ui/abi/numbers-arithmetic/float-struct.rs +++ b/tests/ui/abi/numbers-arithmetic/float-struct.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc use std::fmt::Debug; use std::hint::black_box; diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index c6e5bea5f4225..6203be90590d3 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -13,5 +13,6 @@ //@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-backends: gcc include!("stack-probes.rs"); diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index f0fbd80d2e734..4d0301411e0fd 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -10,6 +10,7 @@ //@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-backends: gcc use std::env; use std::mem::MaybeUninit; diff --git a/tests/ui/abi/stack-protector.rs b/tests/ui/abi/stack-protector.rs index 29332861977b7..928b49c0eea50 100644 --- a/tests/ui/abi/stack-protector.rs +++ b/tests/ui/abi/stack-protector.rs @@ -4,6 +4,7 @@ //@ [ssp] compile-flags: -Z stack-protector=all //@ compile-flags: -C opt-level=2 //@ compile-flags: -g +//@ ignore-backends: gcc use std::env; use std::process::{Command, ExitStatus}; diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs index 5b39c0de45467..5fe80d5f670b2 100644 --- a/tests/ui/abi/struct-enums/struct-return.rs +++ b/tests/ui/abi/struct-enums/struct-return.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![allow(dead_code)] #[repr(C)] diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index 6cfae0f2a320f..dfdbff33264be 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![feature(c_variadic)] use std::ffi::VaList; diff --git a/tests/ui/abi/x86stdcall.rs b/tests/ui/abi/x86stdcall.rs index c1bd35b80d266..77866c30f0e49 100644 --- a/tests/ui/abi/x86stdcall.rs +++ b/tests/ui/abi/x86stdcall.rs @@ -1,5 +1,6 @@ //@ run-pass //@ only-windows +//@ ignore-backends: gcc // GetLastError doesn't seem to work with stack switching #[cfg(windows)] From 5c0cb3af59e03b4248ae58ca4e65f14307320195 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 20 Sep 2025 18:27:33 -0400 Subject: [PATCH 0662/1900] Fix old typo in lang_start_internal comment --- library/std/src/rt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b3f3b301e3db6..8d95cc1fb5747 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -161,7 +161,7 @@ fn lang_start_internal( // mechanism itself. // // There are a couple of instances where unwinding can begin. First is inside of the - // `rt::init`, `rt::cleanup` and similar functions controlled by bstd. In those instances a + // `rt::init`, `rt::cleanup` and similar functions controlled by std. In those instances a // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. From 1dcba116f57014454282f8cf0997682e3769003d Mon Sep 17 00:00:00 2001 From: "Igor S. Gerasimov" Date: Sun, 21 Sep 2025 04:19:10 +0200 Subject: [PATCH 0663/1900] Update list of good combinations (inc. beta + nightly) --- src/doc/rustc/src/linker-plugin-lto.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md index ab95aa2e5a193..32e712a48d76f 100644 --- a/src/doc/rustc/src/linker-plugin-lto.md +++ b/src/doc/rustc/src/linker-plugin-lto.md @@ -144,7 +144,7 @@ def minor_version(version): INSTALL_TOOLCHAIN = ["rustup", "toolchain", "install", "--profile", "minimal"] subprocess.run(INSTALL_TOOLCHAIN + ["nightly"]) -LOWER_BOUND = 73 +LOWER_BOUND = 87 NIGHTLY_VERSION = minor_version(subprocess.run( ["rustc", "+nightly", "--version"], capture_output=True, @@ -201,6 +201,9 @@ The following table shows known good combinations of toolchain versions. | 1.65 - 1.69 | 15 | | 1.70 - 1.72 | 16 | | 1.73 - 1.77 | 17 | -| 1.78 | 18 | +| 1.78 - 1.81 | 18 | +| 1.82 - 1.86 | 19 | +| 1.87 - 1.90 | 20 | +| 1.91 - 1.92 | 21 | Note that the compatibility policy for this feature might change in the future. From 1525bf3e8dcd1e800ec5602030bf01e39dcd6137 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 21 Sep 2025 11:00:00 +0800 Subject: [PATCH 0664/1900] Fix not applicable on trailing comma for remove_dbg `remove_dbg` not applicable for whitespaces after trailing comma Example --- ```rust fn foo() { dbg!( bar(), ); } ``` **Before this PR**: Assist not applicable **After this PR**: ```rust fn foo() { bar(); } ``` --- .../ide-assists/src/handlers/remove_dbg.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 414f6746d4404..08779a3ed1f77 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -83,7 +83,9 @@ fn compute_dbg_replacement( let input_expressions = input_expressions .into_iter() .filter_map(|(is_sep, group)| (!is_sep).then_some(group)) - .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT)) + .map(|tokens| tokens.collect::>()) + .filter(|tokens| !tokens.iter().all(|it| it.kind().is_trivia())) + .map(|tokens| syntax::hacks::parse_expr_from_str(&tokens.iter().join(""), Edition::CURRENT)) .collect::>>()?; let parent = macro_expr.syntax().parent()?; @@ -268,6 +270,8 @@ fn foo() { dbg!('x'); dbg!(&n); dbg!(n); + dbg!(n,); + dbg!(n, ); // needless comment dbg!("foo");$0 } @@ -281,6 +285,17 @@ fn foo() { ); } + #[test] + fn test_remove_trailing_comma_dbg() { + check("$0dbg!(1 + 1,)", "1 + 1"); + check("$0dbg!(1 + 1, )", "1 + 1"); + check("$0dbg!(1 + 1,\n)", "1 + 1"); + check("$0dbg!(1 + 1, 2 + 3)", "(1 + 1, 2 + 3)"); + check("$0dbg!(1 + 1, 2 + 3 )", "(1 + 1, 2 + 3)"); + check("$0dbg!(1 + 1, 2 + 3, )", "(1 + 1, 2 + 3)"); + check("$0dbg!(1 + 1, 2 + 3 ,)", "(1 + 1, 2 + 3)"); + } + #[test] fn test_remove_dbg_not_applicable() { check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}"); From 3934fc9eb29061168327640ba7b9ca8f79ba01e2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 02:33:24 +0000 Subject: [PATCH 0665/1900] Consider errors in MIR as impossible predicates. --- .../src/impossible_predicates.rs | 33 ++++++----- tests/crashes/122904-2.rs | 17 ------ tests/crashes/139556.rs | 13 ----- tests/ui/consts/promoted_const_call2.rs | 1 - tests/ui/consts/promoted_const_call2.stderr | 17 ++---- .../122630.rs => ui/coroutine/moved-twice.rs} | 11 +++- tests/ui/coroutine/moved-twice.stderr | 24 ++++++++ .../recursive-drop-elaboration-2.rs | 19 ++++++ .../recursive-drop-elaboration-2.stderr | 30 ++++++++++ .../recursive-drop-elaboration.rs | 24 ++++++++ .../recursive-drop-elaboration.stderr | 58 +++++++++++++++++++ .../type-error-drop-elaboration.rs} | 3 +- .../type-error-drop-elaboration.stderr | 12 ++++ 13 files changed, 201 insertions(+), 61 deletions(-) delete mode 100644 tests/crashes/122904-2.rs delete mode 100644 tests/crashes/139556.rs rename tests/{crashes/122630.rs => ui/coroutine/moved-twice.rs} (62%) create mode 100644 tests/ui/coroutine/moved-twice.stderr create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr rename tests/{crashes/125185.rs => ui/type-alias-impl-trait/type-error-drop-elaboration.rs} (78%) create mode 100644 tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index b03518de00a9c..883ee32bdec91 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -28,6 +28,7 @@ use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt}; +use rustc_span::def_id::DefId; use rustc_trait_selection::traits; use tracing::trace; @@ -35,23 +36,29 @@ use crate::pass_manager::MirPass; pub(crate) struct ImpossiblePredicates; +fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let predicates = tcx.predicates_of(def_id).instantiate_identity(tcx); + tracing::trace!(?predicates); + let predicates = predicates.predicates.into_iter().filter(|p| { + !p.has_type_flags( + // Only consider global clauses to simplify. + TypeFlags::HAS_FREE_LOCAL_NAMES + // Clauses that refer to unevaluated constants as they cause cycles. + | TypeFlags::HAS_CT_PROJECTION, + ) + }); + let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); + tracing::trace!(?predicates); + predicates.references_error() || traits::impossible_predicates(tcx, predicates) +} + impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tracing::trace!(def_id = ?body.source.def_id()); - let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx); - tracing::trace!(?predicates); - let predicates = predicates.predicates.into_iter().filter(|p| { - !p.has_type_flags( - // Only consider global clauses to simplify. - TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, - ) - }); - let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); - tracing::trace!(?predicates); - if predicates.references_error() || traits::impossible_predicates(tcx, predicates) { + let impossible = body.tainted_by_errors.is_some() + || has_impossible_predicates(tcx, body.source.def_id()); + if impossible { trace!("found unsatisfiable predicates"); // Clear the body to only contain a single `unreachable` statement. let bbs = body.basic_blocks.as_mut(); diff --git a/tests/crashes/122904-2.rs b/tests/crashes/122904-2.rs deleted file mode 100644 index db66b8625db0e..0000000000000 --- a/tests/crashes/122904-2.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #122904 -trait T {} - -type Alias<'a> = impl T; - -struct S; -impl<'a> T for &'a S {} - -#[define_opaque(Alias)] -fn with_positive(fun: impl Fn(Alias<'_>)) { - with_positive(|&n| ()); -} - -#[define_opaque(Alias)] -fn main(Alias<'_>) { - with_positive(|&a| ()); -} diff --git a/tests/crashes/139556.rs b/tests/crashes/139556.rs deleted file mode 100644 index 60dc8d7c3afcc..0000000000000 --- a/tests/crashes/139556.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #139556 - -trait T {} - -type Alias<'a> = impl T; - -struct S; -impl<'a> T for &'a S {} - -#[define_opaque(Alias)] -fn with_positive(fun: impl Fn(Alias<'_>)) { - with_positive(|&n| ()); -} diff --git a/tests/ui/consts/promoted_const_call2.rs b/tests/ui/consts/promoted_const_call2.rs index f332cd18cea37..62391f098e5d1 100644 --- a/tests/ui/consts/promoted_const_call2.rs +++ b/tests/ui/consts/promoted_const_call2.rs @@ -4,7 +4,6 @@ pub const C: () = { let _: &'static _ = &id(&String::new()); //~^ ERROR: temporary value dropped while borrowed //~| ERROR: temporary value dropped while borrowed - //~| ERROR: destructor of `String` cannot be evaluated at compile-time }; fn main() { diff --git a/tests/ui/consts/promoted_const_call2.stderr b/tests/ui/consts/promoted_const_call2.stderr index bdb43385d2032..e62458d1a6ae4 100644 --- a/tests/ui/consts/promoted_const_call2.stderr +++ b/tests/ui/consts/promoted_const_call2.stderr @@ -18,16 +18,8 @@ LL | let _: &'static _ = &id(&String::new()); | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` -error[E0493]: destructor of `String` cannot be evaluated at compile-time - --> $DIR/promoted_const_call2.rs:4:30 - | -LL | let _: &'static _ = &id(&String::new()); - | ^^^^^^^^^^^^^ - value is dropped here - | | - | the destructor for this type cannot be evaluated in constants - error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call2.rs:11:26 + --> $DIR/promoted_const_call2.rs:10:26 | LL | let _: &'static _ = &id(&String::new()); | ---------- ^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use @@ -38,7 +30,7 @@ LL | } | - temporary value is freed at the end of this statement error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call2.rs:11:30 + --> $DIR/promoted_const_call2.rs:10:30 | LL | let _: &'static _ = &id(&String::new()); | ---------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -46,7 +38,6 @@ LL | let _: &'static _ = &id(&String::new()); | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0493, E0716. -For more information about an error, try `rustc --explain E0493`. +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/crashes/122630.rs b/tests/ui/coroutine/moved-twice.rs similarity index 62% rename from tests/crashes/122630.rs rename to tests/ui/coroutine/moved-twice.rs index e66624431c510..72b83e274c929 100644 --- a/tests/crashes/122630.rs +++ b/tests/ui/coroutine/moved-twice.rs @@ -1,22 +1,27 @@ -//@ known-bug: #122630 +//! Regression test for #122630 //@ compile-flags: -Zvalidate-mir +#![feature(coroutines, coroutine_trait, yield_expr)] + use std::ops::Coroutine; const FOO_SIZE: usize = 1024; struct Foo([u8; FOO_SIZE]); impl Drop for Foo { - fn move_before_yield_with_noop() -> impl Coroutine {} + fn drop(&mut self) {} } fn overlap_move_points() -> impl Coroutine { - static || { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); yield; let second = first; yield; let second = first; + //~^ ERROR: use of moved value: `first` [E0382] yield; } } + +fn main() {} diff --git a/tests/ui/coroutine/moved-twice.stderr b/tests/ui/coroutine/moved-twice.stderr new file mode 100644 index 0000000000000..2b21f6c59f0bc --- /dev/null +++ b/tests/ui/coroutine/moved-twice.stderr @@ -0,0 +1,24 @@ +error[E0382]: use of moved value: `first` + --> $DIR/moved-twice.rs:21:22 + | +LL | let first = Foo([0; FOO_SIZE]); + | ----- move occurs because `first` has type `Foo`, which does not implement the `Copy` trait +LL | yield; +LL | let second = first; + | ----- value moved here +LL | yield; +LL | let second = first; + | ^^^^^ value used here after move + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/moved-twice.rs:9:1 + | +LL | struct Foo([u8; FOO_SIZE]); + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | let second = first; + | ----- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs new file mode 100644 index 0000000000000..5541c5267f39a --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs @@ -0,0 +1,19 @@ +//! Regression test for ICE #139556 + +#![feature(type_alias_impl_trait)] + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { +//~^ WARN: function cannot return without recursing + with_positive(|&n| ()); + //~^ ERROR: cannot move out of a shared reference +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr new file mode 100644 index 0000000000000..e1fdd222ee147 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr @@ -0,0 +1,30 @@ +warning: function cannot return without recursing + --> $DIR/recursive-drop-elaboration-2.rs:13:1 + | +LL | fn with_positive(fun: impl Fn(Alias<'_>)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | with_positive(|&n| ()); + | ---------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration-2.rs:15:20 + | +LL | with_positive(|&n| ()); + | ^- + | | + | data moved here + | move occurs because `n` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs new file mode 100644 index 0000000000000..dd28732ebb281 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs @@ -0,0 +1,24 @@ +//! Regression test for #122904. + +#![feature(type_alias_impl_trait)] + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { +//~^ WARN: function cannot return without recursing + with_positive(|&n| ()); + //~^ ERROR: cannot move out of a shared reference +} + +#[define_opaque(Alias)] +fn main(_: Alias<'_>) { +//~^ ERROR: `main` function has wrong type [E0580] + with_positive(|&a| ()); + //~^ ERROR: cannot move out of a shared reference +} diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr new file mode 100644 index 0000000000000..8b5dc950afdde --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr @@ -0,0 +1,58 @@ +warning: function cannot return without recursing + --> $DIR/recursive-drop-elaboration.rs:13:1 + | +LL | fn with_positive(fun: impl Fn(Alias<'_>)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | with_positive(|&n| ()); + | ---------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration.rs:15:20 + | +LL | with_positive(|&n| ()); + | ^- + | | + | data moved here + | move occurs because `n` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration.rs:22:20 + | +LL | with_positive(|&a| ()); + | ^- + | | + | data moved here + | move occurs because `a` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&a| ()); +LL + with_positive(|a| ()); + | + +error[E0580]: `main` function has wrong type + --> $DIR/recursive-drop-elaboration.rs:20:1 + | +LL | type Alias<'a> = impl T; + | ------ the found opaque type +... +LL | fn main(_: Alias<'_>) { + | ^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters + | + = note: expected signature `fn()` + found signature `for<'a> fn(Alias<'a>)` + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0507, E0580. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/crashes/125185.rs b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs similarity index 78% rename from tests/crashes/125185.rs rename to tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs index e77666ca73d36..c0fb9007865a5 100644 --- a/tests/crashes/125185.rs +++ b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs @@ -1,4 +1,4 @@ -//@ known-bug: rust-lang/rust#125185 +//! Regression test for #125185 //@ compile-flags: -Zvalidate-mir #![feature(type_alias_impl_trait)] @@ -10,6 +10,7 @@ struct A; #[define_opaque(Foo)] const fn foo() -> Foo { value() + //~^ ERROR: cannot find function `value` in this scope } const VALUE: Foo = foo(); diff --git a/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr new file mode 100644 index 0000000000000..1cb33eabd902e --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr @@ -0,0 +1,12 @@ +error[E0425]: cannot find function `value` in this scope + --> $DIR/type-error-drop-elaboration.rs:12:5 + | +LL | value() + | ^^^^^ help: a constant with a similar name exists: `VALUE` +... +LL | const VALUE: Foo = foo(); + | ------------------------- similarly named constant `VALUE` defined here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. From b441b801fa72dac0886b98dc68b02993dc07649e Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Sun, 21 Sep 2025 11:43:36 +0800 Subject: [PATCH 0666/1900] fix a crash in rustdoc merge finalize without input file --- src/librustdoc/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9871066b9eb51..904972f52d018 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -835,8 +835,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { config::InputMode::NoInputMergeFinalize => { return wrap_return( dcx, - run_merge_finalize(render_options) - .map_err(|e| format!("could not write merged cross-crate info: {e}")), + rustc_span::create_session_globals_then(options.edition, &[], None, || { + run_merge_finalize(render_options) + .map_err(|e| format!("could not write merged cross-crate info: {e}")) + }), ); } }; From 7971b2a76b8b87ef72ace70c577dca56651e20c5 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 29 Apr 2025 07:55:37 -0700 Subject: [PATCH 0667/1900] Document MaybeUninit bit validity Co-authored-by: Ralf Jung Edited-by: TC --- library/core/src/mem/maybe_uninit.rs | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c160360cfacf9..47a34821e9018 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -252,6 +252,54 @@ use crate::{fmt, intrinsics, ptr, slice}; /// std::process::exit(*code); // UB! Accessing uninitialized memory. /// } /// ``` +/// +/// # Validity +/// +/// A `MaybeUninit` has no validity requirement – any sequence of +/// [bytes][reference-byte] of the appropriate length, initialized or +/// uninitialized, are a valid representation of `MaybeUninit`. +/// +/// However, "round-tripping" via `MaybeUninit` does not always result in the +/// original value. `MaybeUninit` can have padding, and the contents of that +/// padding are not preserved. Concretely, given distinct `T` and `U` where +/// `size_of::() == size_of::()`, the following code is not guaranteed to +/// be sound: +/// +/// ```rust,no_run +/// # use core::mem::{MaybeUninit, transmute}; +/// # struct T; struct U; +/// fn identity(t: T) -> T { +/// unsafe { +/// let u: MaybeUninit = transmute(t); +/// transmute(u) +/// } +/// } +/// ``` +/// +/// If the representation of `t` contains initialized bytes at byte offsets +/// where `U` contains padding bytes, these may not be preserved in +/// `MaybeUninit`. Transmuting `u` back to `T` (i.e., `transmute(u)` above) +/// may thus be undefined behavior or yield a value different from `t` due to +/// those bytes being lost. This is an active area of discussion, and this code +/// may become sound in the future. +/// +/// However, so long as no such byte offsets exist, then the preceding +/// `identity` example *is* sound. In particular, since `[u8; N]` has no padding +/// bytes, transmuting `t` to `MaybeUninit<[u8; size_of::]>` and back will +/// always produce the original value `t` again. This is true even if `t` +/// contains [provenance]: the resulting value will have the same provenance as +/// the original `t`. +/// +/// Note a potential footgun: if `t` contains a reference, then there may be +/// implicit reborrows of the reference any time it is copied, which may alter +/// its provenance. In that case, the value returned by `identity` may not be +/// exactly the same as its argument. However, even in this case, it remains +/// true that `identity` behaves the same as a function that just returns `t` +/// immediately (i.e., `fn identity(t: T) -> T { t }`). +/// +/// [provenance]: crate::ptr#provenance +/// +/// [reference-byte]: ../../reference/memory-model.html#bytes #[stable(feature = "maybe_uninit", since = "1.36.0")] // Lang item so we can wrap other types in it. This is useful for coroutines. #[lang = "maybe_uninit"] From f4b876867d609404be8a78220c0d5117303bb0f8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 21 Sep 2025 13:48:22 +0900 Subject: [PATCH 0668/1900] Support ctr and lr as clobber-only registers in PowerPC inline assembly --- compiler/rustc_codegen_gcc/src/asm.rs | 16 +- compiler/rustc_codegen_llvm/src/asm.rs | 14 +- compiler/rustc_span/src/symbol.rs | 2 + compiler/rustc_target/src/asm/mod.rs | 5 +- compiler/rustc_target/src/asm/powerpc.rs | 12 +- .../asm-experimental-arch.md | 6 +- tests/codegen-llvm/asm/powerpc-clobbers.rs | 8 +- tests/ui/asm/powerpc/bad-reg.aix64.stderr | 162 ++++++++++++---- tests/ui/asm/powerpc/bad-reg.powerpc.stderr | 176 +++++++++++++----- tests/ui/asm/powerpc/bad-reg.powerpc64.stderr | 168 ++++++++++++----- .../ui/asm/powerpc/bad-reg.powerpc64le.stderr | 162 ++++++++++++---- tests/ui/asm/powerpc/bad-reg.rs | 30 ++- 12 files changed, 572 insertions(+), 189 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 17e2e028b16fa..a14881c502cf3 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -698,8 +698,12 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -777,8 +781,12 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { cx.type_vector(cx.type_i32(), 4) } - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index b79176e909818..197ca7e0ba663 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -662,7 +662,12 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", PowerPC(PowerPCInlineAsmRegClass::freg) => "f", PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -830,7 +835,12 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4fef65f46b1fd..a6ae58f87dcc8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -794,6 +794,7 @@ symbols! { ctlz, ctlz_nonzero, ctpop, + ctr, cttz, cttz_nonzero, custom_attribute, @@ -1333,6 +1334,7 @@ symbols! { loongarch_target_feature, loop_break_value, loop_match, + lr, lt, m68k_target_feature, macro_at_most_once_rep, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e06f881e4b1c7..0601613567ead 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1260,11 +1260,12 @@ impl InlineAsmClobberAbi { v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - // cr0-cr1, cr5-cr7, xer + // cr0-cr1, cr5-cr7, ctr, lr, xer cr0, cr1, cr5, cr6, cr7, + ctr, + lr, xer, - // lr and ctr are reserved } }, InlineAsmClobberAbi::S390x => clobbered_regs! { diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index f3934afa6d94f..2348a0fd202e1 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -13,6 +13,8 @@ def_reg_class! { freg, vreg, cr, + ctr, + lr, xer, } } @@ -56,7 +58,7 @@ impl PowerPCInlineAsmRegClass { altivec: VecI8(16), VecI16(8), VecI32(4), VecF32(4); vsx: F32, F64, VecI64(2), VecF64(2); }, - Self::cr | Self::xer => &[], + Self::cr | Self::ctr | Self::lr | Self::xer => &[], } } } @@ -195,6 +197,8 @@ def_regs! { cr5: cr = ["cr5"], cr6: cr = ["cr6"], cr7: cr = ["cr7"], + ctr: ctr = ["ctr"], + lr: lr = ["lr"], xer: xer = ["xer"], #error = ["r1", "1", "sp"] => "the stack pointer cannot be used as an operand for inline asm", @@ -206,10 +210,6 @@ def_regs! { "r30 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["r31", "31", "fp"] => "the frame pointer cannot be used as an operand for inline asm", - #error = ["lr"] => - "the link register cannot be used as an operand for inline asm", - #error = ["ctr"] => - "the counter register cannot be used as an operand for inline asm", #error = ["vrsave"] => "the vrsave register cannot be used as an operand for inline asm", } @@ -247,6 +247,8 @@ impl PowerPCInlineAsmReg { (v24, "24"), (v25, "25"), (v26, "26"), (v27, "27"), (v28, "28"), (v29, "29"), (v30, "30"), (v31, "31"); (cr, "cr"); (cr0, "0"), (cr1, "1"), (cr2, "2"), (cr3, "3"), (cr4, "4"), (cr5, "5"), (cr6, "6"), (cr7, "7"); + (ctr, "ctr"); + (lr, "lr"); (xer, "xer"); } } diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index d9566c9f55c5c..9434868dc08e6 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -36,6 +36,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | PowerPC | `freg` | `f[0-31]` | `f` | | PowerPC | `vreg` | `v[0-31]` | `v` | | PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers | +| PowerPC | `ctr` | `ctr` | Only clobbers | +| PowerPC | `lr` | `lr` | Only clobbers | | PowerPC | `xer` | `xer` | Only clobbers | | wasm32 | `local` | None\* | `r` | | BPF | `reg` | `r[0-10]` | `r` | @@ -78,6 +80,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | PowerPC | `vreg` | `altivec` | `i8x16`, `i16x8`, `i32x4`, `f32x4` | | PowerPC | `vreg` | `vsx` | `f32`, `f64`, `i64x2`, `f64x2` | | PowerPC | `cr` | N/A | Only clobbers | +| PowerPC | `ctr` | N/A | Only clobbers | +| PowerPC | `lr` | N/A | Only clobbers | | PowerPC | `xer` | N/A | Only clobbers | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | BPF | `reg` | None | `i8` `i16` `i32` `i64` | @@ -150,8 +154,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | | PowerPC | `r2`, `r13` | These are system reserved registers. | -| PowerPC | `lr` | The link register cannot be used as an input or output. | -| PowerPC | `ctr` | The counter register cannot be used as an input or output. | | PowerPC | `vrsave` | The vrsave register cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | |MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | diff --git a/tests/codegen-llvm/asm/powerpc-clobbers.rs b/tests/codegen-llvm/asm/powerpc-clobbers.rs index f7fc7eea5d506..10d7ae4dba40b 100644 --- a/tests/codegen-llvm/asm/powerpc-clobbers.rs +++ b/tests/codegen-llvm/asm/powerpc-clobbers.rs @@ -58,10 +58,10 @@ pub unsafe fn v0_clobber() { // Output format depends on the availability of altivec. // CHECK-LABEL: @clobber_abi -// powerpc: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{xer}"() -// powerpc64: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{xer}"() -// powerpc64le: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{xer}"() -// aix64: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{xer}"() +// powerpc: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{ctr},~{lr},~{xer}"() +// powerpc64: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{ctr},~{lr},~{xer}"() +// powerpc64le: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{ctr},~{lr},~{xer}"() +// aix64: asm sideeffect "", "={r0},={r3},={r4},={r5},={r6},={r7},={r8},={r9},={r10},={r11},={r12},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},={f8},={f9},={f10},={f11},={f12},={f13},={v0},={v1},={v2},={v3},={v4},={v5},={v6},={v7},={v8},={v9},={v10},={v11},={v12},={v13},={v14},={v15},={v16},={v17},={v18},={v19},~{cr0},~{cr1},~{cr5},~{cr6},~{cr7},~{ctr},~{lr},~{xer}"() #[no_mangle] pub unsafe fn clobber_abi() { asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); diff --git a/tests/ui/asm/powerpc/bad-reg.aix64.stderr b/tests/ui/asm/powerpc/bad-reg.aix64.stderr index 124013f89afa9..82faba8d167fa 100644 --- a/tests/ui/asm/powerpc/bad-reg.aix64.stderr +++ b/tests/ui/asm/powerpc/bad-reg.aix64.stderr @@ -28,74 +28,110 @@ error: invalid register `fp`: the frame pointer cannot be used as an operand for LL | asm!("", out("fp") _); | ^^^^^^^^^^^ -error: invalid register `lr`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:48:18 - | -LL | asm!("", out("lr") _); - | ^^^^^^^^^^^ - -error: invalid register `ctr`: the counter register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:50:18 - | -LL | asm!("", out("ctr") _); - | ^^^^^^^^^^^^ - error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:99:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:106:26 + --> $DIR/bad-reg.rs:102:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:105:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:109:18 + | +LL | asm!("", in("ctr") x); + | ^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:112:18 + | +LL | asm!("", out("ctr") x); + | ^^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:115:26 + | +LL | asm!("/* {} */", in(ctr) x); + | ^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:118:26 + | +LL | asm!("/* {} */", out(ctr) _); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:122:18 + | +LL | asm!("", in("lr") x); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:125:18 + | +LL | asm!("", out("lr") x); + | ^^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:128:26 + | +LL | asm!("/* {} */", in(lr) x); + | ^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:131:26 + | +LL | asm!("/* {} */", out(lr) _); + | ^^^^^^^^^ + error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:113:18 + --> $DIR/bad-reg.rs:135:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:116:18 + --> $DIR/bad-reg.rs:138:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:119:26 + --> $DIR/bad-reg.rs:141:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:122:26 + --> $DIR/bad-reg.rs:144:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:126:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -103,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:128:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -111,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:130:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -119,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:132:31 + --> $DIR/bad-reg.rs:154:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -127,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:134:31 + --> $DIR/bad-reg.rs:156:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -135,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:136:31 + --> $DIR/bad-reg.rs:158:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -143,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:138:31 + --> $DIR/bad-reg.rs:160:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -151,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:140:31 + --> $DIR/bad-reg.rs:162:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -165,7 +201,7 @@ LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -173,7 +209,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:70:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -181,7 +217,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:78:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -189,7 +225,7 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:27 + --> $DIR/bad-reg.rs:96:27 | LL | asm!("", in("cr") x); | ^ @@ -197,7 +233,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:103:28 + --> $DIR/bad-reg.rs:99:28 | LL | asm!("", out("cr") x); | ^ @@ -205,7 +241,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:106:33 + --> $DIR/bad-reg.rs:102:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -213,7 +249,55 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:113:28 + --> $DIR/bad-reg.rs:109:28 + | +LL | asm!("", in("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:112:29 + | +LL | asm!("", out("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:115:34 + | +LL | asm!("/* {} */", in(ctr) x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:122:27 + | +LL | asm!("", in("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:125:28 + | +LL | asm!("", out("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:128:33 + | +LL | asm!("/* {} */", in(lr) x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:135:28 | LL | asm!("", in("xer") x); | ^ @@ -221,7 +305,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:116:29 + --> $DIR/bad-reg.rs:138:29 | LL | asm!("", out("xer") x); | ^ @@ -229,12 +313,12 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:119:34 + --> $DIR/bad-reg.rs:141:34 | LL | asm!("/* {} */", in(xer) x); | ^ | = note: register class `xer` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 46 previous errors diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc.stderr index b11c946f80da5..fac70ea77cb3a 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc.stderr @@ -28,74 +28,110 @@ error: invalid register `fp`: the frame pointer cannot be used as an operand for LL | asm!("", out("fp") _); | ^^^^^^^^^^^ -error: invalid register `lr`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:48:18 - | -LL | asm!("", out("lr") _); - | ^^^^^^^^^^^ - -error: invalid register `ctr`: the counter register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:50:18 - | -LL | asm!("", out("ctr") _); - | ^^^^^^^^^^^^ - error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:99:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:106:26 + --> $DIR/bad-reg.rs:102:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:105:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:109:18 + | +LL | asm!("", in("ctr") x); + | ^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:112:18 + | +LL | asm!("", out("ctr") x); + | ^^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:115:26 + | +LL | asm!("/* {} */", in(ctr) x); + | ^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:118:26 + | +LL | asm!("/* {} */", out(ctr) _); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:122:18 + | +LL | asm!("", in("lr") x); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:125:18 + | +LL | asm!("", out("lr") x); + | ^^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:128:26 + | +LL | asm!("/* {} */", in(lr) x); + | ^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:131:26 + | +LL | asm!("/* {} */", out(lr) _); + | ^^^^^^^^^ + error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:113:18 + --> $DIR/bad-reg.rs:135:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:116:18 + --> $DIR/bad-reg.rs:138:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:119:26 + --> $DIR/bad-reg.rs:141:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:122:26 + --> $DIR/bad-reg.rs:144:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:126:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -103,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:128:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -111,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:130:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -119,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:132:31 + --> $DIR/bad-reg.rs:154:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -127,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:134:31 + --> $DIR/bad-reg.rs:156:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -135,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:136:31 + --> $DIR/bad-reg.rs:158:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -143,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:138:31 + --> $DIR/bad-reg.rs:160:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -151,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:140:31 + --> $DIR/bad-reg.rs:162:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -165,67 +201,67 @@ LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:57:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", in("v0") v32x4); // requires altivec | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:59:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("v0") v32x4); // requires altivec | ^^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:61:18 + --> $DIR/bad-reg.rs:57:18 | LL | asm!("", in("v0") v64x2); // requires vsx | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("v0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:67:18 + --> $DIR/bad-reg.rs:63:18 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:73:26 + --> $DIR/bad-reg.rs:69:26 | LL | asm!("/* {} */", in(vreg) v32x4); // requires altivec | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:75:26 + --> $DIR/bad-reg.rs:71:26 | LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:78:26 + --> $DIR/bad-reg.rs:74:26 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:81:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", out(vreg) _); // requires altivec | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:27 + --> $DIR/bad-reg.rs:96:27 | LL | asm!("", in("cr") x); | ^ @@ -233,7 +269,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:103:28 + --> $DIR/bad-reg.rs:99:28 | LL | asm!("", out("cr") x); | ^ @@ -241,7 +277,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:106:33 + --> $DIR/bad-reg.rs:102:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -249,7 +285,55 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:113:28 + --> $DIR/bad-reg.rs:109:28 + | +LL | asm!("", in("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:112:29 + | +LL | asm!("", out("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:115:34 + | +LL | asm!("/* {} */", in(ctr) x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:122:27 + | +LL | asm!("", in("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:125:28 + | +LL | asm!("", out("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:128:33 + | +LL | asm!("/* {} */", in(lr) x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:135:28 | LL | asm!("", in("xer") x); | ^ @@ -257,7 +341,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:116:29 + --> $DIR/bad-reg.rs:138:29 | LL | asm!("", out("xer") x); | ^ @@ -265,12 +349,12 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:119:34 + --> $DIR/bad-reg.rs:141:34 | LL | asm!("/* {} */", in(xer) x); | ^ | = note: register class `xer` supports these types: -error: aborting due to 41 previous errors +error: aborting due to 53 previous errors diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr index a93b2b018df0e..42a59448f425e 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr @@ -28,74 +28,110 @@ error: invalid register `fp`: the frame pointer cannot be used as an operand for LL | asm!("", out("fp") _); | ^^^^^^^^^^^ -error: invalid register `lr`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:48:18 - | -LL | asm!("", out("lr") _); - | ^^^^^^^^^^^ - -error: invalid register `ctr`: the counter register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:50:18 - | -LL | asm!("", out("ctr") _); - | ^^^^^^^^^^^^ - error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:99:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:106:26 + --> $DIR/bad-reg.rs:102:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:105:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:109:18 + | +LL | asm!("", in("ctr") x); + | ^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:112:18 + | +LL | asm!("", out("ctr") x); + | ^^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:115:26 + | +LL | asm!("/* {} */", in(ctr) x); + | ^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:118:26 + | +LL | asm!("/* {} */", out(ctr) _); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:122:18 + | +LL | asm!("", in("lr") x); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:125:18 + | +LL | asm!("", out("lr") x); + | ^^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:128:26 + | +LL | asm!("/* {} */", in(lr) x); + | ^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:131:26 + | +LL | asm!("/* {} */", out(lr) _); + | ^^^^^^^^^ + error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:113:18 + --> $DIR/bad-reg.rs:135:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:116:18 + --> $DIR/bad-reg.rs:138:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:119:26 + --> $DIR/bad-reg.rs:141:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:122:26 + --> $DIR/bad-reg.rs:144:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:126:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -103,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:128:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -111,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:130:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -119,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:132:31 + --> $DIR/bad-reg.rs:154:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -127,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:134:31 + --> $DIR/bad-reg.rs:156:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -135,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:136:31 + --> $DIR/bad-reg.rs:158:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -143,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:138:31 + --> $DIR/bad-reg.rs:160:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -151,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:140:31 + --> $DIR/bad-reg.rs:162:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -165,7 +201,7 @@ LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:61:27 + --> $DIR/bad-reg.rs:57:27 | LL | asm!("", in("v0") v64x2); // requires vsx | ^^^^^ @@ -173,7 +209,7 @@ LL | asm!("", in("v0") v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:64:28 + --> $DIR/bad-reg.rs:60:28 | LL | asm!("", out("v0") v64x2); // requires vsx | ^^^^^ @@ -181,7 +217,7 @@ LL | asm!("", out("v0") v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -189,7 +225,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:70:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -197,7 +233,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:75:35 + --> $DIR/bad-reg.rs:71:35 | LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx | ^^^^^ @@ -205,7 +241,7 @@ LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:78:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -213,7 +249,7 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:27 + --> $DIR/bad-reg.rs:96:27 | LL | asm!("", in("cr") x); | ^ @@ -221,7 +257,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:103:28 + --> $DIR/bad-reg.rs:99:28 | LL | asm!("", out("cr") x); | ^ @@ -229,7 +265,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:106:33 + --> $DIR/bad-reg.rs:102:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -237,7 +273,55 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:113:28 + --> $DIR/bad-reg.rs:109:28 + | +LL | asm!("", in("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:112:29 + | +LL | asm!("", out("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:115:34 + | +LL | asm!("/* {} */", in(ctr) x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:122:27 + | +LL | asm!("", in("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:125:28 + | +LL | asm!("", out("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:128:33 + | +LL | asm!("/* {} */", in(lr) x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:135:28 | LL | asm!("", in("xer") x); | ^ @@ -245,7 +329,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:116:29 + --> $DIR/bad-reg.rs:138:29 | LL | asm!("", out("xer") x); | ^ @@ -253,12 +337,12 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:119:34 + --> $DIR/bad-reg.rs:141:34 | LL | asm!("/* {} */", in(xer) x); | ^ | = note: register class `xer` supports these types: -error: aborting due to 37 previous errors +error: aborting due to 49 previous errors diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr index 124013f89afa9..82faba8d167fa 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr @@ -28,74 +28,110 @@ error: invalid register `fp`: the frame pointer cannot be used as an operand for LL | asm!("", out("fp") _); | ^^^^^^^^^^^ -error: invalid register `lr`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:48:18 - | -LL | asm!("", out("lr") _); - | ^^^^^^^^^^^ - -error: invalid register `ctr`: the counter register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:50:18 - | -LL | asm!("", out("ctr") _); - | ^^^^^^^^^^^^ - error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:100:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:99:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:106:26 + --> $DIR/bad-reg.rs:102:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:105:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:109:18 + | +LL | asm!("", in("ctr") x); + | ^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:112:18 + | +LL | asm!("", out("ctr") x); + | ^^^^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:115:26 + | +LL | asm!("/* {} */", in(ctr) x); + | ^^^^^^^^^ + +error: register class `ctr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:118:26 + | +LL | asm!("/* {} */", out(ctr) _); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:122:18 + | +LL | asm!("", in("lr") x); + | ^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:125:18 + | +LL | asm!("", out("lr") x); + | ^^^^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:128:26 + | +LL | asm!("/* {} */", in(lr) x); + | ^^^^^^^^ + +error: register class `lr` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:131:26 + | +LL | asm!("/* {} */", out(lr) _); + | ^^^^^^^^^ + error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:113:18 + --> $DIR/bad-reg.rs:135:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:116:18 + --> $DIR/bad-reg.rs:138:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:119:26 + --> $DIR/bad-reg.rs:141:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:122:26 + --> $DIR/bad-reg.rs:144:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:126:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -103,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:128:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -111,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:130:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -119,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:132:31 + --> $DIR/bad-reg.rs:154:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -127,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:134:31 + --> $DIR/bad-reg.rs:156:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -135,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:136:31 + --> $DIR/bad-reg.rs:158:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -143,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:138:31 + --> $DIR/bad-reg.rs:160:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -151,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:140:31 + --> $DIR/bad-reg.rs:162:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -165,7 +201,7 @@ LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -173,7 +209,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:70:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -181,7 +217,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:78:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -189,7 +225,7 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:100:27 + --> $DIR/bad-reg.rs:96:27 | LL | asm!("", in("cr") x); | ^ @@ -197,7 +233,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:103:28 + --> $DIR/bad-reg.rs:99:28 | LL | asm!("", out("cr") x); | ^ @@ -205,7 +241,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:106:33 + --> $DIR/bad-reg.rs:102:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -213,7 +249,55 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:113:28 + --> $DIR/bad-reg.rs:109:28 + | +LL | asm!("", in("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:112:29 + | +LL | asm!("", out("ctr") x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:115:34 + | +LL | asm!("/* {} */", in(ctr) x); + | ^ + | + = note: register class `ctr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:122:27 + | +LL | asm!("", in("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:125:28 + | +LL | asm!("", out("lr") x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:128:33 + | +LL | asm!("/* {} */", in(lr) x); + | ^ + | + = note: register class `lr` supports these types: + +error: type `i32` cannot be used with this register class + --> $DIR/bad-reg.rs:135:28 | LL | asm!("", in("xer") x); | ^ @@ -221,7 +305,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:116:29 + --> $DIR/bad-reg.rs:138:29 | LL | asm!("", out("xer") x); | ^ @@ -229,12 +313,12 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:119:34 + --> $DIR/bad-reg.rs:141:34 | LL | asm!("/* {} */", in(xer) x); | ^ | = note: register class `xer` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 46 previous errors diff --git a/tests/ui/asm/powerpc/bad-reg.rs b/tests/ui/asm/powerpc/bad-reg.rs index 5598f8379603d..21ea451934ed4 100644 --- a/tests/ui/asm/powerpc/bad-reg.rs +++ b/tests/ui/asm/powerpc/bad-reg.rs @@ -45,10 +45,6 @@ fn f() { //~^ ERROR invalid register `r30`: r30 is used internally by LLVM and cannot be used as an operand for inline asm asm!("", out("fp") _); //~^ ERROR invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - asm!("", out("lr") _); - //~^ ERROR invalid register `lr`: the link register cannot be used as an operand for inline asm - asm!("", out("ctr") _); - //~^ ERROR invalid register `ctr`: the counter register cannot be used as an operand for inline asm asm!("", out("vrsave") _); //~^ ERROR invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm @@ -108,6 +104,32 @@ fn f() { //~| ERROR type `i32` cannot be used with this register class asm!("/* {} */", out(cr) _); //~^ ERROR can only be used as a clobber + // ctr + asm!("", out("ctr") _); // ok + asm!("", in("ctr") x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("", out("ctr") x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("/* {} */", in(ctr) x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("/* {} */", out(ctr) _); + //~^ ERROR can only be used as a clobber + // lr + asm!("", out("lr") _); // ok + asm!("", in("lr") x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("", out("lr") x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("/* {} */", in(lr) x); + //~^ ERROR can only be used as a clobber + //~| ERROR type `i32` cannot be used with this register class + asm!("/* {} */", out(lr) _); + //~^ ERROR can only be used as a clobber // xer asm!("", out("xer") _); // ok asm!("", in("xer") x); From 77f44f2f0760867aef315fb8429fa1c495622fb7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 11:05:22 +0200 Subject: [PATCH 0669/1900] `default_constructed_unit_structs`: use `check_source_text` --- clippy_lints/src/default_constructed_unit_structs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index f8a9037fc8047..641f8ae03b72a 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -75,7 +75,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + if expr.span.check_source_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } From f76664ca83b816c08370e806b61f5230fd980d18 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 08:57:36 +0200 Subject: [PATCH 0670/1900] clean-up `match_same_arms` a bit --- clippy_lints/src/matches/match_same_arms.rs | 54 ++++++++------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ae277da089fd2..818e504245549 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -18,11 +18,7 @@ use super::MATCH_SAME_ARMS; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(arm.body); - h.finish() - }; + let hash = |&(_, arm): &(_, &Arm<'_>)| hash_expr(cx, arm.body); let arena = DroplessArena::default(); let normalized_pats: Vec<_> = arms @@ -35,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[i + 1..] - .iter() - .enumerate() + (normalized_pats[i + 1..].iter().enumerate()) .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j)) .unwrap_or(normalized_pats.len()) }) @@ -48,16 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[..i] - .iter() - .enumerate() - .rev() - .zip(forwards_blocking_idxs[..i].iter().copied().rev()) - .skip_while(|&(_, forward_block)| forward_block > i) - .find_map(|((j, other), forward_block)| { - (forward_block == i || pat.has_overlapping_values(other)).then_some(j) - }) - .unwrap_or(0) + iter::zip( + normalized_pats[..i].iter().enumerate().rev(), + forwards_blocking_idxs[..i].iter().copied().rev(), + ) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then_some(j) + }) + .unwrap_or(0) }) .collect(); @@ -158,12 +151,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|(_, arm)| arm.pat.span.get_source_text(cx)) .collect::>>() { - let mut suggs = src + let suggs = src .iter() .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .chain([(dest.pat.span, pat_snippets.iter().join(" | "))]) .collect_vec(); - suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); diag.multipart_suggestion_verbose( "otherwise merge the patterns into a single arm", suggs, @@ -396,10 +389,7 @@ impl<'a> NormalizedPat<'a> { if lpath != rpath { return false; } - lpats - .iter() - .zip(rpats.iter()) - .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + iter::zip(lpats, rpats).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) }, (Self::Path(x), Self::Path(y)) => x == y, (Self::LitStr(x), Self::LitStr(y)) => x == y, @@ -409,7 +399,7 @@ impl<'a> NormalizedPat<'a> { (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { - lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) + lpats.len() == rpats.len() && iter::zip(lpats, rpats).all(|(x, y)| x.has_overlapping_values(y)) }, (Self::Slice(pats, None), Self::Slice(front, Some(back))) | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { @@ -418,16 +408,12 @@ impl<'a> NormalizedPat<'a> { if pats.len() < front.len() + back.len() { return false; } - pats[..front.len()] - .iter() - .zip(front.iter()) - .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + iter::zip(&pats[..front.len()], front) + .chain(iter::zip(&pats[pats.len() - back.len()..], back)) .all(|(x, y)| x.has_overlapping_values(y)) }, - (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront - .iter() - .zip(rfront.iter()) - .chain(lback.iter().rev().zip(rback.iter().rev())) + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => iter::zip(lfront, rfront) + .chain(iter::zip(lback.iter().rev(), rback.iter().rev())) .all(|(x, y)| x.has_overlapping_values(y)), // Enums can mix unit variants with tuple/struct variants. These can never overlap. From b6c4b3068ba7398c3e82b54675dcade446f276bf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 22:51:17 +0200 Subject: [PATCH 0671/1900] move `copies.rs` to `ifs/mod.rs` all the lints work with `if`-expressions --- clippy_lints/src/declared_lints.rs | 8 ++++---- clippy_lints/src/{copies.rs => ifs/mod.rs} | 0 clippy_lints/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename clippy_lints/src/{copies.rs => ifs/mod.rs} (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5563b8094f01b..a468cc19004cf 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -84,10 +84,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::collapsible_if::COLLAPSIBLE_IF_INFO, crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, - crate::copies::BRANCHES_SHARING_CODE_INFO, - crate::copies::IFS_SAME_COND_INFO, - crate::copies::IF_SAME_THEN_ELSE_INFO, - crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::copy_iterator::COPY_ITERATOR_INFO, crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, crate::create_dir::CREATE_DIR_INFO, @@ -204,6 +200,10 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::ifs::BRANCHES_SHARING_CODE_INFO, + crate::ifs::IFS_SAME_COND_INFO, + crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, crate::implicit_hasher::IMPLICIT_HASHER_INFO, diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/ifs/mod.rs similarity index 100% rename from clippy_lints/src/copies.rs rename to clippy_lints/src/ifs/mod.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..1cae377f3fbd3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -100,7 +100,6 @@ mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; mod comparison_chain; -mod copies; mod copy_iterator; mod crate_in_macro_def; mod create_dir; @@ -158,6 +157,7 @@ mod future_not_send; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; +mod ifs; mod ignored_unit_patterns; mod impl_hash_with_borrow_str_and_bytes; mod implicit_hasher; @@ -549,7 +549,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); From 95f5a00d8628fba223c802251af3c42547927c5a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 11:19:14 +0200 Subject: [PATCH 0672/1900] extract each lint into its own module --- clippy_lints/src/ifs/branches_sharing_code.rs | 407 ++++++++++++++ clippy_lints/src/ifs/if_same_then_else.rs | 29 + clippy_lints/src/ifs/ifs_same_cond.rs | 46 ++ clippy_lints/src/ifs/mod.rs | 508 +----------------- .../src/ifs/same_functions_in_if_cond.rs | 31 ++ 5 files changed, 525 insertions(+), 496 deletions(-) create mode 100644 clippy_lints/src/ifs/branches_sharing_code.rs create mode 100644 clippy_lints/src/ifs/if_same_then_else.rs create mode 100644 clippy_lints/src/ifs/ifs_same_cond.rs create mode 100644 clippy_lints/src/ifs/same_functions_in_if_cond.rs diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs new file mode 100644 index 0000000000000..eb1025f71498e --- /dev/null +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -0,0 +1,407 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::for_each_expr_without_closures; +use clippy_utils::{ + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, + path_to_local, +}; +use core::iter; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; +use rustc_lint::LateContext; +use rustc_span::hygiene::walk_chain; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; + +use super::BRANCHES_SHARING_CODE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + conds: &[&'tcx Expr<'_>], + blocks: &[&'tcx Block<'_>], + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + let &[first_block, ref blocks @ ..] = blocks else { + return; + }; + let &[.., last_block] = blocks else { + return; + }; + + let res = scan_block_for_eq(cx, conds, first_block, blocks); + let sm = cx.tcx.sess.source_map(); + let start_suggestion = res.start_span(first_block, sm).map(|span| { + let first_line_span = first_line_of_span(cx, expr.span); + let replace_span = first_line_span.with_hi(span.hi()); + let cond_span = first_line_span.until(first_block.span); + let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); + let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(&suggestion, true, cond_indent); + (replace_span, suggestion) + }); + let end_suggestion = res.end_span(last_block, sm).map(|span| { + let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); + let indent = indent_of(cx, expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(&suggestion, true, indent); + + let span = span.with_hi(last_block.span.hi()); + // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) + let span = span + .map_range(cx, |_, src, range| { + (range.start > 4 && src.get(range.start - 4..range.start)? == " ") + .then_some(range.start - 4..range.end) + }) + .map_or(span, |range| range.with_ctxt(span.ctxt())); + (span, suggestion.clone()) + }); + + let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { + (&Some((span, _)), &Some((end_span, _))) => ( + span, + "all if blocks contain the same code at both the start and the end", + Some(end_span), + ), + (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), + (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), + (None, None) => return, + }; + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { + if let Some(span) = end_span { + diag.span_note(span, "this code is shared at the end"); + } + if let Some((span, sugg)) = start_suggestion { + diag.span_suggestion( + span, + "consider moving these statements before the if", + sugg, + Applicability::Unspecified, + ); + } + if let Some((span, sugg)) = end_suggestion { + diag.span_suggestion( + span, + "consider moving these statements after the if", + sugg, + Applicability::Unspecified, + ); + if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() { + diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); + } + } + if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { + diag.warn("some moved values might need to be renamed to avoid wrong references"); + } + }); +} + +struct BlockEq { + /// The end of the range of equal stmts at the start. + start_end_eq: usize, + /// The start of the range of equal stmts at the end. + end_begin_eq: Option, + /// The name and id of every local which can be moved at the beginning and the end. + moved_locals: Vec<(HirId, Symbol)>, +} +impl BlockEq { + fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match &b.stmts[..self.start_end_eq] { + [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + [s] => Some(sm.stmt_span(s.span, b.span)), + [] => None, + } + } + + fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { + ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([s], None) => Some(sm.stmt_span(s.span, b.span)), + ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), + ([], None) => None, + } + } +} + +/// If the statement is a local, checks if the bound names match the expected list of names. +fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { + if let StmtKind::Let(l) = s.kind { + let mut i = 0usize; + let mut res = true; + l.pat.each_binding_or_first(&mut |_, _, _, name| { + if names.get(i).is_some_and(|&(_, n)| n == name.name) { + i += 1; + } else { + res = false; + } + }); + res && i == names.len() + } else { + false + } +} + +/// Checks if the statement modifies or moves any of the given locals. +fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { + for_each_expr_without_closures(s, |e| { + if let Some(id) = path_to_local(e) + && locals.contains(&id) + && !capture_local_usage(cx, e).is_imm_ref() + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +/// Checks if the given statement should be considered equal to the statement in the same +/// position for each block. +fn eq_stmts( + stmt: &Stmt<'_>, + blocks: &[&Block<'_>], + get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, + eq: &mut HirEqInterExpr<'_, '_, '_>, + moved_bindings: &mut Vec<(HirId, Symbol)>, +) -> bool { + (if let StmtKind::Let(l) = stmt.kind { + let old_count = moved_bindings.len(); + l.pat.each_binding_or_first(&mut |_, id, _, name| { + moved_bindings.push((id, name.name)); + }); + let new_bindings = &moved_bindings[old_count..]; + blocks + .iter() + .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings))) + } else { + true + }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) +} + +#[expect(clippy::too_many_lines)] +fn scan_block_for_eq<'tcx>( + cx: &LateContext<'tcx>, + conds: &[&'tcx Expr<'_>], + block: &'tcx Block<'_>, + blocks: &[&'tcx Block<'_>], +) -> BlockEq { + let mut eq = SpanlessEq::new(cx); + let mut eq = eq.inter_expr(); + let mut moved_locals = Vec::new(); + + let mut cond_locals = HirIdSet::default(); + for &cond in conds { + let _: Option = for_each_expr_without_closures(cond, |e| { + if let Some(id) = path_to_local(e) { + cond_locals.insert(id); + } + ControlFlow::Continue(()) + }); + } + + let mut local_needs_ordered_drop = false; + let start_end_eq = block + .stmts + .iter() + .enumerate() + .find(|&(i, stmt)| { + if let StmtKind::Let(l) = stmt.kind + && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id)) + { + local_needs_ordered_drop = true; + return true; + } + modifies_any_local(cx, stmt, &cond_locals) + || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals) + }) + .map_or(block.stmts.len(), |(i, stmt)| { + adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev()) + }); + + if local_needs_ordered_drop { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + + // Walk backwards through the final expression/statements so long as their hashes are equal. Note + // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block + // to match those in other blocks. e.g. If each block ends with the following the hash value will be + // the same even though each `x` binding will have a different `HirId`: + // let x = foo(); + // x + 50 + let expr_hash_eq = if let Some(e) = block.expr { + let hash = hash_expr(cx, e); + blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash)) + } else { + blocks.iter().all(|b| b.expr.is_none()) + }; + if !expr_hash_eq { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + let end_search_start = block.stmts[start_end_eq..] + .iter() + .rev() + .enumerate() + .find(|&(offset, stmt)| { + let hash = hash_stmt(cx, stmt); + blocks.iter().any(|b| { + b.stmts + // the bounds check will catch the underflow + .get(b.stmts.len().wrapping_sub(offset + 1)) + .is_none_or(|s| hash != hash_stmt(cx, s)) + }) + }) + .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| { + adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter())) + }); + + let moved_locals_at_start = moved_locals.len(); + let mut i = end_search_start; + let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] + .iter() + .zip(iter::repeat_with(move || { + let x = i; + i -= 1; + x + })) + .fold(end_search_start, |init, (stmt, offset)| { + if eq_stmts( + stmt, + blocks, + |b| b.stmts.get(b.stmts.len() - offset), + &mut eq, + &mut moved_locals, + ) { + init + } else { + // Clear out all locals seen at the end so far. None of them can be moved. + let stmts = &blocks[0].stmts; + for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { + if let StmtKind::Let(l) = stmt.kind { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + // FIXME(rust/#120456) - is `swap_remove` correct? + eq.locals.swap_remove(&id); + }); + } + } + moved_locals.truncate(moved_locals_at_start); + offset - 1 + } + }); + if let Some(e) = block.expr { + for block in blocks { + if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { + moved_locals.truncate(moved_locals_at_start); + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + } + } + + BlockEq { + start_end_eq, + end_begin_eq: Some(end_begin_eq), + moved_locals, + } +} + +/// Adjusts the index for which the statements begin to differ to the closest macro callsite. +/// This avoids giving suggestions that requires splitting a macro call in half, when only a +/// part of the macro expansion is equal. +/// +/// For example, for the following macro: +/// ```rust,ignore +/// macro_rules! foo { +/// ($x:expr) => { +/// let y = 42; +/// $x; +/// }; +/// } +/// ``` +/// If the macro is called like this: +/// ```rust,ignore +/// if false { +/// let z = 42; +/// foo!(println!("Hello")); +/// } else { +/// let z = 42; +/// foo!(println!("World")); +/// } +/// ``` +/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the +/// suggestion. +fn adjust_by_closest_callsite<'tcx>( + i: usize, + stmt: &'tcx Stmt<'tcx>, + mut iter: impl Iterator)>, +) -> usize { + let Some((_, first)) = iter.next() else { + return 0; + }; + + // If it is already at the boundary of a macro call, then just return. + if first.span.source_callsite() != stmt.span.source_callsite() { + return i; + } + + iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite()) + .map_or(0, |(i, _)| i + 1) +} + +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { + get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| { + let ignore_span = block.span.shrink_to_lo().to(if_expr.span); + + symbols + .iter() + .filter(|&&(_, name)| !name.as_str().starts_with('_')) + .any(|&(_, name)| { + let mut walker = ContainsName { name, cx }; + + // Scan block + let mut res = block + .stmts + .iter() + .filter(|stmt| !ignore_span.overlaps(stmt.span)) + .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + + if let Some(expr) = block.expr + && res.is_continue() + { + res = intravisit::walk_expr(&mut walker, expr); + } + + res.is_break() + }) + }) +} + +fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + if let Node::LetStmt(LetStmt { init: Some(e), .. }) + | Node::Expr(Expr { + kind: ExprKind::Assign(_, e, _), + .. + }) = parent + { + return e.hir_id == expr.hir_id; + } + + false +} diff --git a/clippy_lints/src/ifs/if_same_then_else.rs b/clippy_lints/src/ifs/if_same_then_else.rs new file mode 100644 index 0000000000000..69402ec890768 --- /dev/null +++ b/clippy_lints/src/ifs/if_same_then_else.rs @@ -0,0 +1,29 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::has_let_expr; +use rustc_hir::{Block, Expr}; +use rustc_lint::LateContext; + +use super::IF_SAME_THEN_ELSE; + +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs new file mode 100644 index 0000000000000..ca76fc2587db9 --- /dev/null +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use super::IFS_SAME_COND; + +fn method_caller_is_mutable<'tcx>( + cx: &LateContext<'tcx>, + caller_expr: &Expr<'_>, + interior_mut: &mut InteriorMut<'tcx>, +) -> bool { + let caller_ty = cx.typeck_results().expr_ty(caller_expr); + + interior_mut.is_interior_mut_ty(cx, caller_ty) + || caller_ty.is_mutable_ptr() + // `find_binding_init` will return the binding iff its not mutable + || path_to_local(caller_expr) + .and_then(|hid| find_binding_init(cx, hid)) + .is_none() +} + +/// Implementation of `IFS_SAME_COND`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { + for group in search_same( + conds, + |e| hash_expr(cx, e), + |lhs, rhs| { + // Ignore eq_expr side effects iff one of the expression kind is a method call + // and the caller is not a mutable, including inner mutable type. + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { + if method_caller_is_mutable(cx, caller, interior_mut) { + false + } else { + SpanlessEq::new(cx).eq_expr(lhs, rhs) + } + } else { + eq_expr_value(cx, lhs, rhs) + } + }, + ) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); + } +} diff --git a/clippy_lints/src/ifs/mod.rs b/clippy_lints/src/ifs/mod.rs index 4fdb497950f8d..739f2fc917293 100644 --- a/clippy_lints/src/ifs/mod.rs +++ b/clippy_lints/src/ifs/mod.rs @@ -1,24 +1,15 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; -use clippy_utils::higher::has_let_expr; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; -use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{ - ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, - get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, - search_same, -}; -use core::iter; -use core::ops::ControlFlow; -use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{if_sequence, is_else_clause, is_lint_allowed}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::hygiene::walk_chain; -use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; + +mod branches_sharing_code; +mod if_same_then_else; +mod ifs_same_cond; +mod same_functions_in_if_cond; declare_clippy_lint! { /// ### What it does @@ -179,488 +170,13 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &mut self.interior_mut); - lint_same_fns_in_if_cond(cx, &conds); + ifs_same_cond::check(cx, &conds, &mut self.interior_mut); + same_functions_in_if_cond::check(cx, &conds); let all_same = - !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && if_same_then_else::check(cx, &conds, &blocks); if !all_same && conds.len() != blocks.len() { - lint_branches_sharing_code(cx, &conds, &blocks, expr); - } - } - } -} - -fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { - let mut eq = SpanlessEq::new(cx); - blocks - .array_windows::<2>() - .enumerate() - .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - lhs.span, - "this `if` has identical blocks", - Some(rhs.span), - "same as this", - ); - all_eq - } else { - false - } - }) -} - -fn lint_branches_sharing_code<'tcx>( - cx: &LateContext<'tcx>, - conds: &[&'tcx Expr<'_>], - blocks: &[&'tcx Block<'_>], - expr: &'tcx Expr<'_>, -) { - // We only lint ifs with multiple blocks - let &[first_block, ref blocks @ ..] = blocks else { - return; - }; - let &[.., last_block] = blocks else { - return; - }; - - let res = scan_block_for_eq(cx, conds, first_block, blocks); - let sm = cx.tcx.sess.source_map(); - let start_suggestion = res.start_span(first_block, sm).map(|span| { - let first_line_span = first_line_of_span(cx, expr.span); - let replace_span = first_line_span.with_hi(span.hi()); - let cond_span = first_line_span.until(first_block.span); - let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); - let cond_indent = indent_of(cx, cond_span); - let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); - let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; - let suggestion = reindent_multiline(&suggestion, true, cond_indent); - (replace_span, suggestion) - }); - let end_suggestion = res.end_span(last_block, sm).map(|span| { - let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); - let indent = indent_of(cx, expr.span.shrink_to_hi()); - let suggestion = "}\n".to_string() + &moved_snipped; - let suggestion = reindent_multiline(&suggestion, true, indent); - - let span = span.with_hi(last_block.span.hi()); - // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) - let span = span - .map_range(cx, |_, src, range| { - (range.start > 4 && src.get(range.start - 4..range.start)? == " ") - .then_some(range.start - 4..range.end) - }) - .map_or(span, |range| range.with_ctxt(span.ctxt())); - (span, suggestion.clone()) - }); - - let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { - (&Some((span, _)), &Some((end_span, _))) => ( - span, - "all if blocks contain the same code at both the start and the end", - Some(end_span), - ), - (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), - (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), - (None, None) => return, - }; - span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { - if let Some(span) = end_span { - diag.span_note(span, "this code is shared at the end"); - } - if let Some((span, sugg)) = start_suggestion { - diag.span_suggestion( - span, - "consider moving these statements before the if", - sugg, - Applicability::Unspecified, - ); - } - if let Some((span, sugg)) = end_suggestion { - diag.span_suggestion( - span, - "consider moving these statements after the if", - sugg, - Applicability::Unspecified, - ); - if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() { - diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); + branches_sharing_code::check(cx, &conds, &blocks, expr); } } - if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { - diag.warn("some moved values might need to be renamed to avoid wrong references"); - } - }); -} - -struct BlockEq { - /// The end of the range of equal stmts at the start. - start_end_eq: usize, - /// The start of the range of equal stmts at the end. - end_begin_eq: Option, - /// The name and id of every local which can be moved at the beginning and the end. - moved_locals: Vec<(HirId, Symbol)>, -} -impl BlockEq { - fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { - match &b.stmts[..self.start_end_eq] { - [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - [s] => Some(sm.stmt_span(s.span, b.span)), - [] => None, - } } - - fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { - match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { - ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - ([s], None) => Some(sm.stmt_span(s.span, b.span)), - ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), - ([], None) => None, - } - } -} - -/// If the statement is a local, checks if the bound names match the expected list of names. -fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { - if let StmtKind::Let(l) = s.kind { - let mut i = 0usize; - let mut res = true; - l.pat.each_binding_or_first(&mut |_, _, _, name| { - if names.get(i).is_some_and(|&(_, n)| n == name.name) { - i += 1; - } else { - res = false; - } - }); - res && i == names.len() - } else { - false - } -} - -/// Checks if the statement modifies or moves any of the given locals. -fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { - for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) - && locals.contains(&id) - && !capture_local_usage(cx, e).is_imm_ref() - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -/// Checks if the given statement should be considered equal to the statement in the same position -/// for each block. -fn eq_stmts( - stmt: &Stmt<'_>, - blocks: &[&Block<'_>], - get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, - eq: &mut HirEqInterExpr<'_, '_, '_>, - moved_bindings: &mut Vec<(HirId, Symbol)>, -) -> bool { - (if let StmtKind::Let(l) = stmt.kind { - let old_count = moved_bindings.len(); - l.pat.each_binding_or_first(&mut |_, id, _, name| { - moved_bindings.push((id, name.name)); - }); - let new_bindings = &moved_bindings[old_count..]; - blocks - .iter() - .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings))) - } else { - true - }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) -} - -#[expect(clippy::too_many_lines)] -fn scan_block_for_eq<'tcx>( - cx: &LateContext<'tcx>, - conds: &[&'tcx Expr<'_>], - block: &'tcx Block<'_>, - blocks: &[&'tcx Block<'_>], -) -> BlockEq { - let mut eq = SpanlessEq::new(cx); - let mut eq = eq.inter_expr(); - let mut moved_locals = Vec::new(); - - let mut cond_locals = HirIdSet::default(); - for &cond in conds { - let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { - cond_locals.insert(id); - } - ControlFlow::Continue(()) - }); - } - - let mut local_needs_ordered_drop = false; - let start_end_eq = block - .stmts - .iter() - .enumerate() - .find(|&(i, stmt)| { - if let StmtKind::Let(l) = stmt.kind - && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id)) - { - local_needs_ordered_drop = true; - return true; - } - modifies_any_local(cx, stmt, &cond_locals) - || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals) - }) - .map_or(block.stmts.len(), |(i, stmt)| { - adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev()) - }); - - if local_needs_ordered_drop { - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - - // Walk backwards through the final expression/statements so long as their hashes are equal. Note - // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block - // to match those in other blocks. e.g. If each block ends with the following the hash value will be - // the same even though each `x` binding will have a different `HirId`: - // let x = foo(); - // x + 50 - let expr_hash_eq = if let Some(e) = block.expr { - let hash = hash_expr(cx, e); - blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash)) - } else { - blocks.iter().all(|b| b.expr.is_none()) - }; - if !expr_hash_eq { - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - let end_search_start = block.stmts[start_end_eq..] - .iter() - .rev() - .enumerate() - .find(|&(offset, stmt)| { - let hash = hash_stmt(cx, stmt); - blocks.iter().any(|b| { - b.stmts - // the bounds check will catch the underflow - .get(b.stmts.len().wrapping_sub(offset + 1)) - .is_none_or(|s| hash != hash_stmt(cx, s)) - }) - }) - .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| { - adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter())) - }); - - let moved_locals_at_start = moved_locals.len(); - let mut i = end_search_start; - let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] - .iter() - .zip(iter::repeat_with(move || { - let x = i; - i -= 1; - x - })) - .fold(end_search_start, |init, (stmt, offset)| { - if eq_stmts( - stmt, - blocks, - |b| b.stmts.get(b.stmts.len() - offset), - &mut eq, - &mut moved_locals, - ) { - init - } else { - // Clear out all locals seen at the end so far. None of them can be moved. - let stmts = &blocks[0].stmts; - for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { - if let StmtKind::Let(l) = stmt.kind { - l.pat.each_binding_or_first(&mut |_, id, _, _| { - // FIXME(rust/#120456) - is `swap_remove` correct? - eq.locals.swap_remove(&id); - }); - } - } - moved_locals.truncate(moved_locals_at_start); - offset - 1 - } - }); - if let Some(e) = block.expr { - for block in blocks { - if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { - moved_locals.truncate(moved_locals_at_start); - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - } - } - - BlockEq { - start_end_eq, - end_begin_eq: Some(end_begin_eq), - moved_locals, - } -} - -/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This -/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the -/// macro expansion is equal. -/// -/// For example, for the following macro: -/// ```rust,ignore -/// macro_rules! foo { -/// ($x:expr) => { -/// let y = 42; -/// $x; -/// }; -/// } -/// ``` -/// If the macro is called like this: -/// ```rust,ignore -/// if false { -/// let z = 42; -/// foo!(println!("Hello")); -/// } else { -/// let z = 42; -/// foo!(println!("World")); -/// } -/// ``` -/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the -/// suggestion. -fn adjust_by_closest_callsite<'tcx>( - i: usize, - stmt: &'tcx Stmt<'tcx>, - mut iter: impl Iterator)>, -) -> usize { - let Some((_, first)) = iter.next() else { - return 0; - }; - - // If it is already at the boundary of a macro call, then just return. - if first.span.source_callsite() != stmt.span.source_callsite() { - return i; - } - - iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite()) - .map_or(0, |(i, _)| i + 1) -} - -fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { - get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| { - let ignore_span = block.span.shrink_to_lo().to(if_expr.span); - - symbols - .iter() - .filter(|&&(_, name)| !name.as_str().starts_with('_')) - .any(|&(_, name)| { - let mut walker = ContainsName { name, cx }; - - // Scan block - let mut res = block - .stmts - .iter() - .filter(|stmt| !ignore_span.overlaps(stmt.span)) - .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); - - if let Some(expr) = block.expr - && res.is_continue() - { - res = intravisit::walk_expr(&mut walker, expr); - } - - res.is_break() - }) - }) -} - -fn method_caller_is_mutable<'tcx>( - cx: &LateContext<'tcx>, - caller_expr: &Expr<'_>, - interior_mut: &mut InteriorMut<'tcx>, -) -> bool { - let caller_ty = cx.typeck_results().expr_ty(caller_expr); - - interior_mut.is_interior_mut_ty(cx, caller_ty) - || caller_ty.is_mutable_ptr() - // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) - .and_then(|hid| find_binding_init(cx, hid)) - .is_none() -} - -/// Implementation of `IFS_SAME_COND`. -fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for group in search_same( - conds, - |e| hash_expr(cx, e), - |lhs, rhs| { - // Ignore eq_expr side effects iff one of the expression kind is a method call - // and the caller is not a mutable, including inner mutable type. - if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_mutable(cx, caller, interior_mut) { - false - } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) - } - } else { - eq_expr_value(cx, lhs, rhs) - } - }, - ) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); - } -} - -/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. -fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { - // Do not lint if any expr originates from a macro - if lhs.span.from_expansion() || rhs.span.from_expansion() { - return false; - } - // Do not spawn warning if `IFS_SAME_COND` already produced it. - if eq_expr_value(cx, lhs, rhs) { - return false; - } - SpanlessEq::new(cx).eq_expr(lhs, rhs) - }; - - for group in search_same(conds, |e| hash_expr(cx, e), eq) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint( - cx, - SAME_FUNCTIONS_IN_IF_CONDITION, - spans, - "these `if` branches have the same function call", - ); - } -} - -fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let parent = cx.tcx.parent_hir_node(expr.hir_id); - if let Node::LetStmt(LetStmt { init: Some(e), .. }) - | Node::Expr(Expr { - kind: ExprKind::Assign(_, e, _), - .. - }) = parent - { - return e.hir_id == expr.hir_id; - } - - false } diff --git a/clippy_lints/src/ifs/same_functions_in_if_cond.rs b/clippy_lints/src/ifs/same_functions_in_if_cond.rs new file mode 100644 index 0000000000000..1f6bf04e22e7d --- /dev/null +++ b/clippy_lints/src/ifs/same_functions_in_if_cond.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same}; +use rustc_hir::Expr; +use rustc_lint::LateContext; + +use super::SAME_FUNCTIONS_IN_IF_CONDITION; + +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if lhs.span.from_expansion() || rhs.span.from_expansion() { + return false; + } + // Do not spawn warning if `IFS_SAME_COND` already produced it. + if eq_expr_value(cx, lhs, rhs) { + return false; + } + SpanlessEq::new(cx).eq_expr(lhs, rhs) + }; + + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( + cx, + SAME_FUNCTIONS_IN_IF_CONDITION, + spans, + "these `if` branches have the same function call", + ); + } +} From 039fcdf09ac85df20ddb0771aa518ab864c0c226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Bj=C3=B8rnager=20Jensen?= Date: Sun, 21 Sep 2025 11:45:27 +0200 Subject: [PATCH 0673/1900] Stabilise '<[_]>::{rotate_left, rotate_right}'; --- library/core/src/slice/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dfbb3628350a8..6adeb5f18663f 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3629,7 +3629,7 @@ impl [T] { /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_slice_rotate", issue = "143812")] + #[rustc_const_stable(feature = "const_slice_rotate", since = "CURRENT_RUSTC_VERSION")] pub const fn rotate_left(&mut self, mid: usize) { assert!(mid <= self.len()); let k = self.len() - mid; @@ -3675,7 +3675,7 @@ impl [T] { /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_slice_rotate", issue = "143812")] + #[rustc_const_stable(feature = "const_slice_rotate", since = "CURRENT_RUSTC_VERSION")] pub const fn rotate_right(&mut self, k: usize) { assert!(k <= self.len()); let mid = self.len() - k; From b17fb70d0437b90fc9ada0ac2d924cce06eb8828 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Sep 2025 20:50:44 +1000 Subject: [PATCH 0674/1900] Add self-profile events for target-machine creation These code paths are surprisingly hot in the `large-workspace` benchmark; it would be handy to see some detailed timings and execution counts. --- compiler/rustc_codegen_llvm/src/back/write.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c4881f0aafc80..1950b8288d149 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -204,6 +204,9 @@ pub(crate) fn target_machine_factory( optlvl: config::OptLevel, target_features: &[String], ) -> TargetMachineFactoryFn { + // Self-profile timer for creating a _factory_. + let _prof_timer = sess.prof.generic_activity("target_machine_factory"); + let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); @@ -259,6 +262,9 @@ pub(crate) fn target_machine_factory( .into_string() .unwrap_or_default(); let command_line_args = quote_command_line_args(&sess.expanded_args); + // Self-profile counter for the number of bytes produced by command-line quoting. + // Values are summed, so the summary result is cumulative across all TM factories. + sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64); let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { @@ -281,7 +287,11 @@ pub(crate) fn target_machine_factory( let use_wasm_eh = wants_wasm_eh(sess); + let prof = SelfProfilerRef::clone(&sess.prof); Arc::new(move |config: TargetMachineFactoryConfig| { + // Self-profile timer for invoking a factory to create a target machine. + let _prof_timer = prof.generic_activity("target_machine_factory_inner"); + let path_to_cstring_helper = |path: Option| -> CString { let path = path.unwrap_or_default(); let path = path_mapping From 2dfcd0948e0dc1608a04e35d2f1812a21cf45b7a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Sun, 21 Sep 2025 12:05:09 +0000 Subject: [PATCH 0675/1900] btree InternalNode::new safety comments --- library/alloc/src/collections/btree/node.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index b233e1740b7b7..2b8103c8b7786 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -117,10 +117,11 @@ impl InternalNode { /// initialized and valid edge. This function does not set up /// such an edge. unsafe fn new(alloc: A) -> Box { + let mut node = Box::::new_uninit_in(alloc); unsafe { - let mut node = Box::::new_uninit_in(alloc); - // We only need to initialize the data; the edges are MaybeUninit. + // SAFETY: argument points to the `node.data` `LeafNode` LeafNode::init(&raw mut (*node.as_mut_ptr()).data); + // SAFETY: `node.data` was just initialized and `node.edges` is MaybeUninit. node.assume_init() } } From b0010dd5aeb3b9837fdf92c121ebfdd1a07bce6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:16:25 +0200 Subject: [PATCH 0676/1900] Add diagnostics context and migrate the `style` check to it --- src/tools/tidy/src/diagnostics.rs | 102 +++++++++++++++++++++++ src/tools/tidy/src/lib.rs | 8 +- src/tools/tidy/src/main.rs | 129 +++++++++++++++--------------- src/tools/tidy/src/style.rs | 37 ++++----- 4 files changed, 187 insertions(+), 89 deletions(-) create mode 100644 src/tools/tidy/src/diagnostics.rs diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs new file mode 100644 index 0000000000000..a410938400eeb --- /dev/null +++ b/src/tools/tidy/src/diagnostics.rs @@ -0,0 +1,102 @@ +use std::collections::HashSet; +use std::fmt::Display; +use std::sync::{Arc, Mutex}; + +use crate::tidy_error; + +/// Collects diagnostics from all tidy steps, and contains shared information +/// that determines how should message and logs be presented. +/// +/// Since checks are executed in parallel, the context is internally synchronized, to avoid +/// all checks to lock it explicitly. +#[derive(Clone)] +pub struct DiagCtx(Arc>); + +impl DiagCtx { + pub fn new(verbose: bool) -> Self { + Self(Arc::new(Mutex::new(DiagCtxInner { + running_checks: Default::default(), + finished_checks: Default::default(), + verbose, + }))) + } + + pub fn start_check(&self, name: T) -> RunningCheck { + let name = name.to_string(); + + let mut ctx = self.0.lock().unwrap(); + ctx.start_check(&name); + RunningCheck { name, bad: false, ctx: self.0.clone() } + } + + pub fn into_conclusion(self) -> bool { + let ctx = self.0.lock().unwrap(); + assert!(ctx.running_checks.is_empty(), "Some checks are still running"); + ctx.finished_checks.iter().any(|c| c.bad) + } +} + +struct DiagCtxInner { + running_checks: HashSet, + finished_checks: HashSet, + verbose: bool, +} + +impl DiagCtxInner { + fn start_check(&mut self, name: &str) { + if self.has_check(name) { + panic!("Starting a check named {name} for the second time"); + } + self.running_checks.insert(name.to_string()); + } + + fn finish_check(&mut self, check: FinishedCheck) { + assert!( + self.running_checks.remove(&check.name), + "Finishing check {} that was not started", + check.name + ); + self.finished_checks.insert(check); + } + + fn has_check(&self, name: &str) -> bool { + self.running_checks + .iter() + .chain(self.finished_checks.iter().map(|c| &c.name)) + .any(|c| c == name) + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +struct FinishedCheck { + name: String, + bad: bool, +} + +/// Represents a single tidy check, identified by its `name`, running. +pub struct RunningCheck { + name: String, + bad: bool, + ctx: Arc>, +} + +impl RunningCheck { + /// Immediately output an error and mark the check as failed. + pub fn error(&mut self, t: T) { + self.mark_as_bad(); + tidy_error(&t.to_string()).expect("failed to output error"); + } + + fn mark_as_bad(&mut self) { + self.bad = true; + } +} + +impl Drop for RunningCheck { + fn drop(&mut self) { + self.ctx + .lock() + .unwrap() + .finish_check(FinishedCheck { name: self.name.clone(), bad: self.bad }) + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index f7920e3205ab2..953d699199066 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -50,13 +50,6 @@ macro_rules! tidy_error { }); } -macro_rules! tidy_error_ext { - ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({ - $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error"); - *$bad = true; - }); -} - fn tidy_error(args: &str) -> std::io::Result<()> { use std::io::Write; @@ -250,6 +243,7 @@ pub mod alphabetical; pub mod bins; pub mod debug_artifacts; pub mod deps; +pub mod diagnostics; pub mod edition; pub mod error_codes; pub mod extdeps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index f9e82341b7aa5..708daffe657ab 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -9,9 +9,11 @@ use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; +use tidy::diagnostics::DiagCtx; use tidy::*; fn main() { @@ -54,6 +56,8 @@ fn main() { let ci_info = CiInfo::new(&mut bad); let bad = std::sync::Arc::new(AtomicBool::new(bad)); + let mut diag_ctx = DiagCtx::new(verbose); + let drain_handles = |handles: &mut VecDeque>| { // poll all threads for completion before awaiting the oldest one for i in (0..handles.len()).rev() { @@ -87,76 +91,73 @@ fn main() { (@ $p:ident, name=$name:expr $(, $args:expr)* ) => { drain_handles(&mut handles); + let diag_ctx = diag_ctx.clone(); let handle = thread::Builder::new().name($name).spawn_scoped(s, || { - let mut flag = false; - $p::check($($args, )* &mut flag); - if (flag) { - bad.store(true, Ordering::Relaxed); - } + $p::check($($args, )* diag_ctx); }).unwrap(); handles.push_back(handle); } } - check!(target_specific_tests, &tests_path); + // check!(target_specific_tests, &tests_path); // Checks that are done on the cargo workspace. - check!(deps, &root_path, &cargo, bless); - check!(extdeps, &root_path); + // check!(deps, &root_path, &cargo, bless); + // check!(extdeps, &root_path); // Checks over tests. - check!(tests_placement, &root_path); - check!(tests_revision_unpaired_stdout_stderr, &tests_path); - check!(debug_artifacts, &tests_path); - check!(ui_tests, &root_path, bless); - check!(mir_opt_tests, &tests_path, bless); - check!(rustdoc_gui_tests, &tests_path); - check!(rustdoc_css_themes, &librustdoc_path); - check!(rustdoc_templates, &librustdoc_path); - check!(rustdoc_json, &src_path, &ci_info); - check!(known_bug, &crashes_path); - check!(unknown_revision, &tests_path); + // check!(tests_placement, &root_path); + // check!(tests_revision_unpaired_stdout_stderr, &tests_path); + // check!(debug_artifacts, &tests_path); + // check!(ui_tests, &root_path, bless); + // check!(mir_opt_tests, &tests_path, bless); + // check!(rustdoc_gui_tests, &tests_path); + // check!(rustdoc_css_themes, &librustdoc_path); + // check!(rustdoc_templates, &librustdoc_path); + // check!(rustdoc_json, &src_path, &ci_info); + // check!(known_bug, &crashes_path); + // check!(unknown_revision, &tests_path); // Checks that only make sense for the compiler. - check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); - check!(fluent_alphabetical, &compiler_path, bless); - check!(fluent_period, &compiler_path); - check!(fluent_lowercase, &compiler_path); - check!(target_policy, &root_path); - check!(gcc_submodule, &root_path, &compiler_path); + // check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); + // check!(fluent_alphabetical, &compiler_path, bless); + // check!(fluent_period, &compiler_path); + // check!(fluent_lowercase, &compiler_path); + // check!(target_policy, &root_path); + // check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. - check!(pal, &library_path); + // check!(pal, &library_path); // Checks that need to be done for both the compiler and std libraries. - check!(unit_tests, &src_path, false); - check!(unit_tests, &compiler_path, false); - check!(unit_tests, &library_path, true); - - if bins::check_filesystem_support(&[&root_path], &output_directory) { - check!(bins, &root_path); - } + // check!(unit_tests, &src_path, false); + // check!(unit_tests, &compiler_path, false); + // check!(unit_tests, &library_path, true); + // + // if bins::check_filesystem_support(&[&root_path], &output_directory) { + // check!(bins, &root_path); + // } check!(style, &src_path); check!(style, &tests_path); check!(style, &compiler_path); check!(style, &library_path); - check!(edition, &src_path); - check!(edition, &compiler_path); - check!(edition, &library_path); - - check!(alphabetical, &root_manifest); - check!(alphabetical, &src_path); - check!(alphabetical, &tests_path); - check!(alphabetical, &compiler_path); - check!(alphabetical, &library_path); - - check!(x_version, &root_path, &cargo); - - check!(triagebot, &root_path); - - check!(filenames, &root_path); + // check!(edition, &src_path); + // check!(edition, &compiler_path); + // check!(edition, &library_path); + // + // check!(alphabetical, &root_manifest); + // check!(alphabetical, &src_path); + // check!(alphabetical, &tests_path); + // check!(alphabetical, &compiler_path); + // check!(alphabetical, &library_path); + // + // check!(x_version, &root_path, &cargo); + // + // check!(triagebot, &root_path); + // + // check!(filenames, &root_path); let collected = { drain_handles(&mut handles); @@ -175,24 +176,24 @@ fn main() { } r }; - check!(unstable_book, &src_path, collected); - - check!( - extra_checks, - &root_path, - &output_directory, - &ci_info, - &librustdoc_path, - &tools_path, - &npm, - &cargo, - bless, - extra_checks, - pos_args - ); + // check!(unstable_book, &src_path, collected); + // + // check!( + // extra_checks, + // &root_path, + // &output_directory, + // &ci_info, + // &librustdoc_path, + // &tools_path, + // &npm, + // &cargo, + // bless, + // extra_checks, + // pos_args + // ); }); - if bad.load(Ordering::Relaxed) { + if diag_ctx.into_conclusion() { eprintln!("some tidy checks failed"); process::exit(1); } diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index fca097c091b75..7d6ebed397fb4 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -24,6 +24,7 @@ use std::sync::LazyLock; use regex::RegexSetBuilder; use rustc_hash::FxHashMap; +use crate::diagnostics::DiagCtx; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -338,7 +339,9 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool { true } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(format!("style {}", path.display())); + fn skip(path: &Path, is_dir: bool) -> bool { if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) { // vim or emacs temporary file @@ -391,7 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) { }); if contents.is_empty() { - tidy_error!(bad, "{}: empty file", file.display()); + check.error(format!("{}: empty file", file.display())); } let extension = file.extension().unwrap().to_string_lossy(); @@ -467,7 +470,7 @@ pub fn check(path: &Path, bad: &mut bool) { } let mut err = |msg: &str| { - tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg); + check.error(format!("{}:{}: {msg}", file.display(), i + 1)); }; if trimmed.contains("dbg!") @@ -611,7 +614,7 @@ pub fn check(path: &Path, bad: &mut bool) { && backtick_count % 2 == 1 { let mut err = |msg: &str| { - tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); + check.error(format!("{}:{start_line}: {msg}", file.display())); }; let block_len = (i + 1) - start_line; if block_len == 1 { @@ -632,12 +635,12 @@ pub fn check(path: &Path, bad: &mut bool) { } if leading_new_lines { let mut err = |_| { - tidy_error!(bad, "{}: leading newline", file.display()); + check.error(format!("{}: leading newline", file.display())); }; suppressible_tidy_err!(err, skip_leading_newlines, "missing leading newline"); } let mut err = |msg: &str| { - tidy_error!(bad, "{}: {}", file.display(), msg); + check.error(format!("{}: {}", file.display(), msg)); }; match trailing_new_lines { 0 => suppressible_tidy_err!(err, skip_trailing_newlines, "missing trailing newline"), @@ -650,38 +653,36 @@ pub fn check(path: &Path, bad: &mut bool) { }; if lines > LINES { let mut err = |_| { - tidy_error!( - bad, - "{}: too many lines ({}) (add `// \ + check.error(format!( + "{}: too many lines ({lines}) (add `// \ ignore-tidy-filelength` to the file to suppress this error)", file.display(), - lines - ); + )); }; suppressible_tidy_err!(err, skip_file_length, ""); } if let Directive::Ignore(false) = skip_cr { - tidy_error!(bad, "{}: ignoring CR characters unnecessarily", file.display()); + check.error(format!("{}: ignoring CR characters unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_tab { - tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display()); + check.error(format!("{}: ignoring tab characters unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_end_whitespace { - tidy_error!(bad, "{}: ignoring trailing whitespace unnecessarily", file.display()); + check.error(format!("{}: ignoring trailing whitespace unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_trailing_newlines { - tidy_error!(bad, "{}: ignoring trailing newlines unnecessarily", file.display()); + check.error(format!("{}: ignoring trailing newlines unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_leading_newlines { - tidy_error!(bad, "{}: ignoring leading newlines unnecessarily", file.display()); + check.error(format!("{}: ignoring leading newlines unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_copyright { - tidy_error!(bad, "{}: ignoring copyright unnecessarily", file.display()); + check.error(format!("{}: ignoring copyright unnecessarily", file.display())); } // We deliberately do not warn about these being unnecessary, // that would just lead to annoying churn. let _unused = skip_line_length; let _unused = skip_file_length; - }) + }); } From c36faff900bc03c5c4ee5772dcac4f3630fa2dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:24:46 +0200 Subject: [PATCH 0677/1900] Add `CheckId`, migrate the `alphabetical` check to diagnostics --- src/tools/tidy/src/alphabetical.rs | 31 +++++++-------- src/tools/tidy/src/diagnostics.rs | 63 ++++++++++++++++++++---------- src/tools/tidy/src/main.rs | 10 ++--- src/tools/tidy/src/style.rs | 4 +- 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs index 9ddce72510693..f93d3b5611309 100644 --- a/src/tools/tidy/src/alphabetical.rs +++ b/src/tools/tidy/src/alphabetical.rs @@ -24,6 +24,7 @@ use std::fmt::Display; use std::iter::Peekable; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -43,8 +44,7 @@ const END_MARKER: &str = "tidy-alphabetical-end"; fn check_section<'a>( file: impl Display, lines: impl Iterator, - err: &mut dyn FnMut(&str) -> std::io::Result<()>, - bad: &mut bool, + check: &mut RunningCheck, ) { let mut prev_line = String::new(); let mut first_indent = None; @@ -56,12 +56,10 @@ fn check_section<'a>( } if line.contains(START_MARKER) { - tidy_error_ext!( - err, - bad, + check.error(format!( "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`", idx + 1 - ); + )); return; } @@ -104,45 +102,44 @@ fn check_section<'a>( let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' '); if version_sort(trimmed_line, prev_line_trimmed_lowercase).is_lt() { - tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1); + check.error(format!("{file}:{}: line not in alphabetical order", idx + 1)); } prev_line = line; } - tidy_error_ext!(err, bad, "{file}: reached end of file expecting `{END_MARKER}`") + check.error(format!("{file}: reached end of file expecting `{END_MARKER}`")); } fn check_lines<'a>( file: &impl Display, mut lines: impl Iterator, - err: &mut dyn FnMut(&str) -> std::io::Result<()>, - bad: &mut bool, + check: &mut RunningCheck, ) { while let Some((idx, line)) = lines.next() { if line.contains(END_MARKER) { - tidy_error_ext!( - err, - bad, + check.error(format!( "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`", idx + 1 - ) + )); } if line.contains(START_MARKER) { - check_section(file, &mut lines, err, bad); + check_section(file, &mut lines, check); } } } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("alphabetical").path(path)); + let skip = |path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs"); walk(path, skip, &mut |entry, contents| { let file = &entry.path().display(); let lines = contents.lines().enumerate(); - check_lines(file, lines, &mut crate::tidy_error, bad) + check_lines(file, lines, &mut check) }); } diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index a410938400eeb..ae20f92715c06 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::fmt::Display; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::tidy_error; @@ -21,12 +22,12 @@ impl DiagCtx { }))) } - pub fn start_check(&self, name: T) -> RunningCheck { - let name = name.to_string(); + pub fn start_check>(&self, id: Id) -> RunningCheck { + let id = id.into(); let mut ctx = self.0.lock().unwrap(); - ctx.start_check(&name); - RunningCheck { name, bad: false, ctx: self.0.clone() } + ctx.start_check(id.clone()); + RunningCheck { id, bad: false, ctx: self.0.clone() } } pub fn into_conclusion(self) -> bool { @@ -37,45 +38,68 @@ impl DiagCtx { } struct DiagCtxInner { - running_checks: HashSet, + running_checks: HashSet, finished_checks: HashSet, verbose: bool, } impl DiagCtxInner { - fn start_check(&mut self, name: &str) { - if self.has_check(name) { - panic!("Starting a check named {name} for the second time"); + fn start_check(&mut self, id: CheckId) { + if self.has_check_id(&id) { + panic!("Starting a check named `{id:?}` for the second time"); } - self.running_checks.insert(name.to_string()); + self.running_checks.insert(id); } fn finish_check(&mut self, check: FinishedCheck) { assert!( - self.running_checks.remove(&check.name), - "Finishing check {} that was not started", - check.name + self.running_checks.remove(&check.id), + "Finishing check `{:?}` that was not started", + check.id ); self.finished_checks.insert(check); } - fn has_check(&self, name: &str) -> bool { + fn has_check_id(&self, id: &CheckId) -> bool { self.running_checks .iter() - .chain(self.finished_checks.iter().map(|c| &c.name)) - .any(|c| c == name) + .chain(self.finished_checks.iter().map(|c| &c.id)) + .any(|c| c == id) + } +} + +/// Identifies a single step +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct CheckId { + name: String, + path: Option, +} + +impl CheckId { + pub fn new(name: &'static str) -> Self { + Self { name: name.to_string(), path: None } + } + + pub fn path(self, path: &Path) -> Self { + Self { path: Some(path.to_path_buf()), ..self } + } +} + +impl From<&'static str> for CheckId { + fn from(name: &'static str) -> Self { + Self::new(name) } } #[derive(PartialEq, Eq, Hash, Debug)] struct FinishedCheck { - name: String, + id: CheckId, bad: bool, } /// Represents a single tidy check, identified by its `name`, running. pub struct RunningCheck { - name: String, + id: CheckId, bad: bool, ctx: Arc>, } @@ -94,9 +118,6 @@ impl RunningCheck { impl Drop for RunningCheck { fn drop(&mut self) { - self.ctx - .lock() - .unwrap() - .finish_check(FinishedCheck { name: self.name.clone(), bad: self.bad }) + self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad }) } } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 708daffe657ab..7d42102bbb9d0 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -147,11 +147,11 @@ fn main() { // check!(edition, &compiler_path); // check!(edition, &library_path); // - // check!(alphabetical, &root_manifest); - // check!(alphabetical, &src_path); - // check!(alphabetical, &tests_path); - // check!(alphabetical, &compiler_path); - // check!(alphabetical, &library_path); + check!(alphabetical, &root_manifest); + check!(alphabetical, &src_path); + check!(alphabetical, &tests_path); + check!(alphabetical, &compiler_path); + check!(alphabetical, &library_path); // // check!(x_version, &root_path, &cargo); // diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 7d6ebed397fb4..d17278edc849a 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -24,7 +24,7 @@ use std::sync::LazyLock; use regex::RegexSetBuilder; use rustc_hash::FxHashMap; -use crate::diagnostics::DiagCtx; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -340,7 +340,7 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool { } pub fn check(path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(format!("style {}", path.display())); + let mut check = diag_ctx.start_check(CheckId::new("style").path(path)); fn skip(path: &Path, is_dir: bool) -> bool { if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) { From 352fa3960a00046ff81bd2e0528c51371e5c6c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:26:29 +0200 Subject: [PATCH 0678/1900] Migrate the remaining tidy checks to diagnostics --- src/tools/tidy/src/bins.rs | 14 +- src/tools/tidy/src/debug_artifacts.rs | 15 +- src/tools/tidy/src/deps.rs | 115 +++++++-------- src/tools/tidy/src/diagnostics.rs | 44 +++++- src/tools/tidy/src/edition.rs | 9 +- src/tools/tidy/src/error_codes.rs | 124 +++++++--------- src/tools/tidy/src/extdeps.rs | 9 +- src/tools/tidy/src/extra_checks/mod.rs | 7 +- src/tools/tidy/src/features.rs | 132 ++++++++---------- src/tools/tidy/src/filenames.rs | 20 +-- src/tools/tidy/src/fluent_alphabetical.rs | 36 +++-- src/tools/tidy/src/fluent_lowercase.rs | 13 +- src/tools/tidy/src/fluent_period.rs | 13 +- src/tools/tidy/src/fluent_used.rs | 7 +- src/tools/tidy/src/gcc_submodule.rs | 13 +- src/tools/tidy/src/known_bug.rs | 9 +- src/tools/tidy/src/lib.rs | 43 ++---- src/tools/tidy/src/main.rs | 128 ++++++++--------- src/tools/tidy/src/mir_opt_tests.rs | 23 +-- src/tools/tidy/src/pal.rs | 11 +- src/tools/tidy/src/rustdoc_css_themes.rs | 37 ++--- src/tools/tidy/src/rustdoc_gui_tests.rs | 11 +- src/tools/tidy/src/rustdoc_json.rs | 42 +++--- src/tools/tidy/src/rustdoc_templates.rs | 15 +- src/tools/tidy/src/target_policy.rs | 7 +- src/tools/tidy/src/target_specific_tests.rs | 23 ++- src/tools/tidy/src/tests_placement.rs | 17 ++- .../tests_revision_unpaired_stdout_stderr.rs | 16 ++- src/tools/tidy/src/triagebot.rs | 38 +++-- src/tools/tidy/src/ui_tests.rs | 53 ++++--- src/tools/tidy/src/unit_tests.rs | 14 +- src/tools/tidy/src/unknown_revision.rs | 17 +-- src/tools/tidy/src/unstable_book.rs | 40 +++--- src/tools/tidy/src/x_version.rs | 17 ++- 34 files changed, 560 insertions(+), 572 deletions(-) diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index a18f549844b86..10b6186997167 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -12,11 +12,13 @@ pub use os_impl::*; mod os_impl { use std::path::Path; + use crate::diagnostics::DiagCtx; + pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool { return false; } - pub fn check(_path: &Path, _bad: &mut bool) {} + pub fn check(_path: &Path, _diag_ctx: DiagCtx) {} } #[cfg(unix)] @@ -36,6 +38,8 @@ mod os_impl { use FilesystemSupport::*; + use crate::diagnostics::DiagCtx; + fn is_executable(path: &Path) -> std::io::Result { Ok(path.metadata()?.mode() & 0o111 != 0) } @@ -106,14 +110,16 @@ mod os_impl { } #[cfg(unix)] - pub fn check(path: &Path, bad: &mut bool) { + pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("bins"); + use std::ffi::OsStr; const ALLOWED: &[&str] = &["configure", "x"]; for p in RI_EXCLUSION_LIST { if !path.join(Path::new(p)).exists() { - tidy_error!(bad, "rust-installer test bins missed: {p}"); + check.error(format!("rust-installer test bins missed: {p}")); } } @@ -153,7 +159,7 @@ mod os_impl { }); let path_bytes = rel_path.as_os_str().as_bytes(); if output.status.success() && output.stdout.starts_with(path_bytes) { - tidy_error!(bad, "binary checked into source: {}", file.display()); + check.error(format!("binary checked into source: {}", file.display())); } } }, diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs index 645534cc82738..19effaede817b 100644 --- a/src/tools/tidy/src/debug_artifacts.rs +++ b/src/tools/tidy/src/debug_artifacts.rs @@ -2,24 +2,25 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, filter_not_rust, walk}; const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test"; -pub fn check(test_dir: &Path, bad: &mut bool) { +pub fn check(test_dir: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir)); + walk( test_dir, |path, _is_dir| filter_dirs(path) || filter_not_rust(path), &mut |entry, contents| { for (i, line) in contents.lines().enumerate() { if line.contains("borrowck_graphviz_postflow") { - tidy_error!( - bad, - "{}:{}: {}", + check.error(format!( + "{}:{}: {GRAPHVIZ_POSTFLOW_MSG}", entry.path().display(), - i + 1, - GRAPHVIZ_POSTFLOW_MSG - ); + i + 1 + )); } } }, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 568ec0c119896..a9d07c7531579 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -9,6 +9,8 @@ use build_helper::ci::CiEnv; use cargo_metadata::semver::Version; use cargo_metadata::{Metadata, Package, PackageId}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] mod proc_macro_deps; @@ -661,10 +663,12 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ /// /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path /// to the cargo executable. -pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { +pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("deps").path(root)); + let mut checked_runtime_licenses = false; - check_proc_macro_dep_list(root, cargo, bless, bad); + check_proc_macro_dep_list(root, cargo, bless, &mut check); for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { if has_missing_submodule(root, submodules) { @@ -672,7 +676,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { } if !root.join(path).join("Cargo.lock").exists() { - tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); + check.error(format!("the `{path}` workspace doesn't have a Cargo.lock")); continue; } @@ -683,16 +687,23 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { .other_options(vec!["--locked".to_owned()]); let metadata = t!(cmd.exec()); - check_license_exceptions(&metadata, path, exceptions, bad); + check_license_exceptions(&metadata, path, exceptions, &mut check); if let Some((crates, permitted_deps, location)) = crates_and_deps { let descr = crates.get(0).unwrap_or(&path); - check_permitted_dependencies(&metadata, descr, permitted_deps, crates, location, bad); + check_permitted_dependencies( + &metadata, + descr, + permitted_deps, + crates, + location, + &mut check, + ); } if path == "library" { - check_runtime_license_exceptions(&metadata, bad); - check_runtime_no_duplicate_dependencies(&metadata, bad); - check_runtime_no_proc_macros(&metadata, bad); + check_runtime_license_exceptions(&metadata, &mut check); + check_runtime_no_duplicate_dependencies(&metadata, &mut check); + check_runtime_no_proc_macros(&metadata, &mut check); checked_runtime_licenses = true; } } @@ -703,7 +714,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { } /// Ensure the list of proc-macro crate transitive dependencies is up to date -fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { +fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) { let mut cmd = cargo_metadata::MetadataCommand::new(); cmd.cargo_path(cargo) .manifest_path(root.join("Cargo.toml")) @@ -750,22 +761,22 @@ pub static CRATES: &[&str] = &[ ) .unwrap(); } else { - let old_bad = *bad; + let mut error_found = false; for missing in proc_macro_deps.difference(&expected) { - tidy_error!( - bad, + error_found = true; + check.error(format!( "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`", - ); + )); } for extra in expected.difference(&proc_macro_deps) { - tidy_error!( - bad, + error_found = true; + check.error(format!( "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency", - ); + )); } - if *bad != old_bad { - eprintln!("Run `./x.py test tidy --bless` to regenerate the list"); + if error_found { + check.message("Run `./x.py test tidy --bless` to regenerate the list"); } } } @@ -787,7 +798,7 @@ pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool { /// /// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole /// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx. -fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) { for pkg in &metadata.packages { if pkg.source.is_none() { // No need to check local packages. @@ -796,7 +807,8 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { let license = match &pkg.license { Some(license) => license, None => { - tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); + check + .error(format!("dependency `{}` does not define a license expression", pkg.id)); continue; } }; @@ -809,7 +821,7 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { continue; } - tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id); + check.error(format!("invalid license `{}` in `{}`", license, pkg.id)); } } } @@ -821,37 +833,32 @@ fn check_license_exceptions( metadata: &Metadata, workspace: &str, exceptions: &[(&str, &str)], - bad: &mut bool, + check: &mut RunningCheck, ) { // Validate the EXCEPTIONS list hasn't changed. for (name, license) in exceptions { // Check that the package actually exists. if !metadata.packages.iter().any(|p| *p.name == *name) { - tidy_error!( - bad, - "could not find exception package `{}` in workspace `{workspace}`\n\ + check.error(format!( + "could not find exception package `{name}` in workspace `{workspace}`\n\ Remove from EXCEPTIONS list if it is no longer used.", - name - ); + )); } // Check that the license hasn't changed. for pkg in metadata.packages.iter().filter(|p| *p.name == *name) { match &pkg.license { None => { - tidy_error!( - bad, + check.error(format!( "dependency exception `{}` in workspace `{workspace}` does not declare a license expression", pkg.id - ); + )); } Some(pkg_license) => { if pkg_license.as_str() != *license { - println!( - "dependency exception `{name}` license in workspace `{workspace}` has changed" - ); - println!(" previously `{license}` now `{pkg_license}`"); - println!(" update EXCEPTIONS for the new license"); - *bad = true; + check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed + previously `{license}` now `{pkg_license}` + update EXCEPTIONS for the new license +"#)); } } } @@ -872,26 +879,23 @@ fn check_license_exceptions( let license = match &pkg.license { Some(license) => license, None => { - tidy_error!( - bad, + check.error(format!( "dependency `{}` in workspace `{workspace}` does not define a license expression", pkg.id - ); + )); continue; } }; if !LICENSES.contains(&license.as_str()) { - tidy_error!( - bad, + check.error(format!( "invalid license `{}` for package `{}` in workspace `{workspace}`", - license, - pkg.id - ); + license, pkg.id + )); } } } -fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) { let mut seen_pkgs = HashSet::new(); for pkg in &metadata.packages { if pkg.source.is_none() { @@ -902,25 +906,23 @@ fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool) // depends on two version of (one for the `wasm32-wasip1` target and // another for the `wasm32-wasip2` target). if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) { - tidy_error!( - bad, + check.error(format!( "duplicate package `{}` is not allowed for the standard library", pkg.name - ); + )); } } } -fn check_runtime_no_proc_macros(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) { for pkg in &metadata.packages { if pkg.targets.iter().any(|target| target.is_proc_macro()) { - tidy_error!( - bad, + check.error(format!( "proc macro `{}` is not allowed as standard library dependency.\n\ Using proc macros in the standard library would break cross-compilation \ as proc-macros don't get shipped for the host tuple.", pkg.name - ); + )); } } } @@ -935,7 +937,7 @@ fn check_permitted_dependencies( permitted_dependencies: &[&'static str], restricted_dependency_crates: &[&'static str], permitted_location: ListLocation, - bad: &mut bool, + check: &mut RunningCheck, ) { let mut has_permitted_dep_error = false; let mut deps = HashSet::new(); @@ -957,11 +959,10 @@ fn check_permitted_dependencies( } } if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) { - tidy_error!( - bad, + check.error(format!( "could not find allowed package `{permitted}`\n\ Remove from PERMITTED_DEPENDENCIES list if it is no longer used.", - ); + )); has_permitted_dep_error = true; } } @@ -988,7 +989,7 @@ fn check_permitted_dependencies( false }; if !is_eq { - tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); + check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id)); has_permitted_dep_error = true; } } diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index ae20f92715c06..d7889f925ea91 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use crate::tidy_error; +use termcolor::WriteColor; /// Collects diagnostics from all tidy steps, and contains shared information /// that determines how should message and logs be presented. @@ -111,6 +111,33 @@ impl RunningCheck { tidy_error(&t.to_string()).expect("failed to output error"); } + /// Immediately output a warning. + pub fn warning(&mut self, t: T) { + eprintln!("WARNING: {t}"); + } + + /// Output an informational message + pub fn message(&mut self, t: T) { + eprintln!("{t}"); + } + + /// Output a message only if verbose output is enabled. + pub fn verbose_msg(&mut self, t: T) { + if self.is_verbose_enabled() { + self.message(t); + } + } + + /// Has an error already occured for this check? + pub fn is_bad(&self) -> bool { + self.bad + } + + /// Is verbose output enabled? + pub fn is_verbose_enabled(&self) -> bool { + self.ctx.lock().unwrap().verbose + } + fn mark_as_bad(&mut self) { self.bad = true; } @@ -121,3 +148,18 @@ impl Drop for RunningCheck { self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad }) } } + +fn tidy_error(args: &str) -> std::io::Result<()> { + use std::io::Write; + + use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; + + let mut stderr = StandardStream::stdout(ColorChoice::Auto); + stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + + write!(&mut stderr, "tidy error")?; + stderr.set_color(&ColorSpec::new())?; + + writeln!(&mut stderr, ": {args}")?; + Ok(()) +} diff --git a/src/tools/tidy/src/edition.rs b/src/tools/tidy/src/edition.rs index 08f6a3909f806..448e0b0e0a8c6 100644 --- a/src/tools/tidy/src/edition.rs +++ b/src/tools/tidy/src/edition.rs @@ -2,9 +2,11 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("edition").path(path)); walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| { let file = entry.path(); let filename = file.file_name().unwrap(); @@ -23,11 +25,10 @@ pub fn check(path: &Path, bad: &mut bool) { // Check that all packages use the 2021 edition. Virtual workspaces don't allow setting an // edition, so these shouldn't be checked. if is_package && !is_current_edition { - tidy_error!( - bad, + check.error(format!( "{} doesn't have `edition = \"2021\"` or `edition = \"2024\"` on a separate line", file.display() - ); + )); } }); } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 65aa89fe80169..83fbefa43d975 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -22,6 +22,7 @@ use std::path::Path; use regex::Regex; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk, walk_many}; const ERROR_CODES_PATH: &str = "compiler/rustc_error_codes/src/lib.rs"; @@ -35,71 +36,50 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E07 const IGNORE_UI_TEST_CHECK: &[&str] = &["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"]; -macro_rules! verbose_print { - ($verbose:expr, $($fmt:tt)*) => { - if $verbose { - println!("{}", format_args!($($fmt)*)); - } - }; -} - -pub fn check( - root_path: &Path, - search_paths: &[&Path], - verbose: bool, - ci_info: &crate::CiInfo, - bad: &mut bool, -) { - let mut errors = Vec::new(); +pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("error_codes"); // Check that no error code explanation was removed. - check_removed_error_code_explanation(ci_info, bad); + check_removed_error_code_explanation(ci_info, &mut check); // Stage 1: create list - let error_codes = extract_error_codes(root_path, &mut errors); - if verbose { - println!("Found {} error codes", error_codes.len()); - println!("Highest error code: `{}`", error_codes.iter().max().unwrap()); - } + let error_codes = extract_error_codes(root_path, &mut check); + check.verbose_msg(format!("Found {} error codes", error_codes.len())); + check.verbose_msg(format!("Highest error code: `{}`", error_codes.iter().max().unwrap())); // Stage 2: check list has docs - let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut errors, verbose); + let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut check); // Stage 3: check list has UI tests - check_error_codes_tests(root_path, &error_codes, &mut errors, verbose, &no_longer_emitted); + check_error_codes_tests(root_path, &error_codes, &mut check, &no_longer_emitted); // Stage 4: check list is emitted by compiler - check_error_codes_used(search_paths, &error_codes, &mut errors, &no_longer_emitted, verbose); - - // Print any errors. - for error in errors { - tidy_error!(bad, "{}", error); - } + check_error_codes_used(search_paths, &error_codes, &mut check, &no_longer_emitted); } -fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, bad: &mut bool) { +fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, check: &mut RunningCheck) { let Some(base_commit) = &ci_info.base_commit else { - eprintln!("Skipping error code explanation removal check"); + check.verbose_msg("Skipping error code explanation removal check"); return; }; let Some(diff) = crate::git_diff(base_commit, "--name-status") else { - *bad = true; - eprintln!("removed error code explanation tidy check: Failed to run git diff"); + check.error(format!("removed error code explanation: Failed to run git diff")); return; }; if diff.lines().any(|line| { line.starts_with('D') && line.contains("compiler/rustc_error_codes/src/error_codes/") }) { - *bad = true; - eprintln!("tidy check error: Error code explanations should never be removed!"); - eprintln!("Take a look at E0001 to see how to handle it."); + check.error(format!( + r#"Error code explanations should never be removed! +Take a look at E0001 to see how to handle it."# + )); return; } - println!("No error code explanation was removed!"); + check.verbose_msg("No error code explanation was removed!"); } /// Stage 1: Parses a list of error codes from `error_codes.rs`. -fn extract_error_codes(root_path: &Path, errors: &mut Vec) -> Vec { +fn extract_error_codes(root_path: &Path, check: &mut RunningCheck) -> Vec { let path = root_path.join(Path::new(ERROR_CODES_PATH)); let file = fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}")); @@ -117,7 +97,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec) -> Vec) -> Vec) -> Vec) -> Vec) -> Vec, - verbose: bool, + check: &mut RunningCheck, ) -> Vec { let docs_path = root_path.join(Path::new(ERROR_DOCS_PATH)); @@ -184,7 +164,7 @@ fn check_error_codes_docs( // Error if the file isn't markdown. if path.extension() != Some(OsStr::new("md")) { - errors.push(format!( + check.error(format!( "Found unexpected non-markdown file in error code docs directory: {}", path.display() )); @@ -196,7 +176,7 @@ fn check_error_codes_docs( let err_code = filename.unwrap().0; // `unwrap` is ok because we know the filename is in the correct format. if error_codes.iter().all(|e| e != err_code) { - errors.push(format!( + check.error(format!( "Found valid file `{}` in error code docs directory without corresponding \ entry in `rustc_error_codes/src/lib.rs`", path.display() @@ -208,11 +188,10 @@ fn check_error_codes_docs( check_explanation_has_doctest(contents, err_code); if emit_ignore_warning { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{err_code}` uses the ignore header. This should not be used, add the error code to the \ `IGNORE_DOCTEST_CHECK` constant instead." - ); + )); } if no_longer_emitted { @@ -220,11 +199,10 @@ fn check_error_codes_docs( } if !found_code_example { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{err_code}` doesn't have a code example, all error codes are expected to have one \ (even if untested)." - ); + )); return; } @@ -232,12 +210,12 @@ fn check_error_codes_docs( // Check that the explanation has a doctest, and if it shouldn't, that it doesn't if !found_proper_doctest && !test_ignored { - errors.push(format!( + check.error(format!( "`{}` doesn't use its own error code in compile_fail example", path.display(), )); } else if found_proper_doctest && test_ignored { - errors.push(format!( + check.error(format!( "`{}` has a compile_fail doctest with its own error code, it shouldn't \ be listed in `IGNORE_DOCTEST_CHECK`", path.display(), @@ -289,8 +267,7 @@ fn check_explanation_has_doctest(explanation: &str, err_code: &str) -> (bool, bo fn check_error_codes_tests( root_path: &Path, error_codes: &[String], - errors: &mut Vec, - verbose: bool, + check: &mut RunningCheck, no_longer_emitted: &[String], ) { let tests_path = root_path.join(Path::new(ERROR_TESTS_PATH)); @@ -299,15 +276,14 @@ fn check_error_codes_tests( let test_path = tests_path.join(format!("{code}.stderr")); if !test_path.exists() && !IGNORE_UI_TEST_CHECK.contains(&code.as_str()) { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` needs to have at least one UI test in the `tests/error-codes/` directory`!" - ); + )); continue; } if IGNORE_UI_TEST_CHECK.contains(&code.as_str()) { if test_path.exists() { - errors.push(format!( + check.error(format!( "Error code `{code}` has a UI test in `tests/ui/error-codes/{code}.rs`, it shouldn't be listed in `EXEMPTED_FROM_TEST`!" )); } @@ -317,11 +293,10 @@ fn check_error_codes_tests( let file = match fs::read_to_string(&test_path) { Ok(file) => file, Err(err) => { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Failed to read UI test file (`{}`) for `{code}` but the file exists. The test is assumed to work:\n{err}", test_path.display() - ); + )); continue; } }; @@ -343,10 +318,9 @@ fn check_error_codes_tests( } if !found_code { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` has a UI test file, but doesn't contain its own error code!" - ); + )); } } } @@ -355,9 +329,8 @@ fn check_error_codes_tests( fn check_error_codes_used( search_paths: &[&Path], error_codes: &[String], - errors: &mut Vec, + check: &mut RunningCheck, no_longer_emitted: &[String], - verbose: bool, ) { // Search for error codes in the form `E0123`. let regex = Regex::new(r#"\bE\d{4}\b"#).unwrap(); @@ -384,7 +357,7 @@ fn check_error_codes_used( if !error_codes.contains(&error_code) { // This error code isn't properly defined, we must error. - errors.push(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.")); + check.error(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.")); continue; } @@ -397,7 +370,7 @@ fn check_error_codes_used( for code in error_codes { if !found_codes.contains(code) && !no_longer_emitted.contains(code) { - errors.push(format!( + check.error(format!( "Error code `{code}` exists, but is not emitted by the compiler!\n\ Please mark the code as no longer emitted by adding the following note to the top of the `EXXXX.md` file:\n\ `#### Note: this error code is no longer emitted by the compiler`\n\ @@ -406,10 +379,9 @@ fn check_error_codes_used( } if found_codes.contains(code) && no_longer_emitted.contains(code) { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` is used when it's marked as \"no longer emitted\"" - ); + )); } } } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 2b212cfa67a4b..21612980007d8 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,6 +4,7 @@ use std::fs; use std::path::Path; use crate::deps::WorkspaceInfo; +use crate::diagnostics::{CheckId, DiagCtx}; /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ @@ -14,7 +15,9 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. -pub fn check(root: &Path, bad: &mut bool) { +pub fn check(root: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("extdeps").path(root)); + for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { continue; @@ -25,7 +28,7 @@ pub fn check(root: &Path, bad: &mut bool) { let lockfile = root.join(path).join("Cargo.lock"); if !lockfile.exists() { - tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); + check.error(format!("the `{path}` workspace doesn't have a Cargo.lock")); continue; } @@ -44,7 +47,7 @@ pub fn check(root: &Path, bad: &mut bool) { // Ensure source is allowed. if !ALLOWED_SOURCES.contains(&source) { - tidy_error!(bad, "invalid source: {}", source); + check.error(format!("invalid source: {}", source)); } } } diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index a6b50b5ca4701..9f2f2da2e86d7 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -24,6 +24,7 @@ use std::str::FromStr; use std::{fmt, fs, io}; use crate::CiInfo; +use crate::diagnostics::DiagCtx; mod rustdoc_js; @@ -54,8 +55,10 @@ pub fn check( bless: bool, extra_checks: Option<&str>, pos_args: &[String], - bad: &mut bool, + diag_ctx: DiagCtx, ) { + let mut check = diag_ctx.start_check("extra_checks"); + if let Err(e) = check_impl( root_path, outdir, @@ -68,7 +71,7 @@ pub fn check( extra_checks, pos_args, ) { - tidy_error!(bad, "{e}"); + check.error(e); } } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 6618ba24be609..0a0ba217c6338 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -16,6 +16,7 @@ use std::num::NonZeroU32; use std::path::{Path, PathBuf}; use std::{fmt, fs}; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many}; #[cfg(test)] @@ -91,13 +92,14 @@ pub fn check( tests_path: &Path, compiler_path: &Path, lib_path: &Path, - bad: &mut bool, - verbose: bool, + diag_ctx: DiagCtx, ) -> CollectedFeatures { - let mut features = collect_lang_features(compiler_path, bad); + let mut check = diag_ctx.start_check("features"); + + let mut features = collect_lang_features(compiler_path, &mut check); assert!(!features.is_empty()); - let lib_features = get_and_check_lib_features(lib_path, bad, &features); + let lib_features = get_and_check_lib_features(lib_path, &mut check, &features); assert!(!lib_features.is_empty()); walk_many( @@ -121,7 +123,7 @@ pub fn check( for (i, line) in contents.lines().enumerate() { let mut err = |msg: &str| { - tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg); + check.error(format!("{}:{}: {}", file.display(), i + 1, msg)); }; let gate_test_str = "gate-test-"; @@ -175,7 +177,7 @@ pub fn check( } if !gate_untested.is_empty() { - tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len()); + check.error(format!("Found {} features without a gate test.", gate_untested.len())); } let (version, channel) = get_version_and_channel(src_path); @@ -189,39 +191,32 @@ pub fn check( let file = feature.file.display(); let line = feature.line; if since > version && since != Version::CurrentPlaceholder { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}" - ); + )); } if channel == "nightly" && since == version { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}", version::VERSION_PLACEHOLDER - ); + )); } if channel != "nightly" && since == Version::CurrentPlaceholder { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel", - ); + )); } } - if *bad { - return CollectedFeatures { lib: lib_features, lang: features }; - } - - if verbose { + if !check.is_bad() && check.is_verbose_enabled() { let mut lines = Vec::new(); lines.extend(format_features(&features, "lang")); lines.extend(format_features(&lib_features, "lib")); - lines.sort(); - for line in lines { - println!("* {line}"); - } + + check.verbose_msg( + lines.into_iter().map(|l| format!("* {l}")).collect::>().join("\n"), + ); } CollectedFeatures { lib: lib_features, lang: features } @@ -275,15 +270,20 @@ fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Op None } -pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features { +pub fn collect_lang_features(base_compiler_path: &Path, check: &mut RunningCheck) -> Features { let mut features = Features::new(); - collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad); - collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad); - collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", bad); + collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", check); + collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", check); + collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", check); features } -fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) { +fn collect_lang_features_in( + features: &mut Features, + base: &Path, + file: &str, + check: &mut RunningCheck, +) { let path = base.join("rustc_feature").join("src").join(file); let contents = t!(fs::read_to_string(&path)); @@ -315,13 +315,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba if line.starts_with(FEATURE_GROUP_START_PREFIX) { if in_feature_group { - tidy_error!( - bad, - "{}:{}: \ + check.error(format!( + "{}:{line_number}: \ new feature group is started without ending the previous one", - path.display(), - line_number, - ); + path.display() + )); } in_feature_group = true; @@ -353,14 +351,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let since = match since_str.parse() { Ok(since) => Some(since), Err(err) => { - tidy_error!( - bad, - "{}:{}: failed to parse since: {} ({:?})", - path.display(), - line_number, - since_str, - err, - ); + check.error(format!( + "{}:{line_number}: failed to parse since: {since_str} ({err:?})", + path.display() + )); None } }; @@ -371,13 +365,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let correct_index = match prev_names.binary_search(&name) { Ok(_) => { // This only occurs when the feature name has already been declared. - tidy_error!( - bad, - "{}:{}: duplicate feature {}", - path.display(), - line_number, - name, - ); + check.error(format!( + "{}:{line_number}: duplicate feature {name}", + path.display() + )); // skip any additional checks for this line continue; } @@ -398,14 +389,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba ) }; - tidy_error!( - bad, - "{}:{}: feature {} is not sorted by feature name (should be {})", + check.error(format!( + "{}:{line_number}: feature {name} is not sorted by feature name (should be {correct_placement})", path.display(), - line_number, - name, - correct_placement, - ); + )); } prev_names.push(name); } @@ -413,13 +400,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let issue_str = parts.next().unwrap().trim(); let tracking_issue = if issue_str.starts_with("None") { if level == Status::Unstable && !next_feature_omits_tracking_issue { - tidy_error!( - bad, - "{}:{}: no tracking issue for feature {}", + check.error(format!( + "{}:{line_number}: no tracking issue for feature {name}", path.display(), - line_number, - name, - ); + )); } None } else { @@ -428,13 +412,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba }; match features.entry(name.to_owned()) { Entry::Occupied(e) => { - tidy_error!( - bad, - "{}:{} feature {name} already specified with status '{}'", + check.error(format!( + "{}:{line_number} feature {name} already specified with status '{}'", path.display(), - line_number, e.get().level, - ); + )); } Entry::Vacant(e) => { e.insert(Feature { @@ -458,7 +440,7 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba fn get_and_check_lib_features( base_src_path: &Path, - bad: &mut bool, + check: &mut RunningCheck, lang_features: &Features, ) -> Features { let mut lib_features = Features::new(); @@ -469,16 +451,12 @@ fn get_and_check_lib_features( && f.tracking_issue != s.tracking_issue && f.level != Status::Accepted { - tidy_error!( - bad, - "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"", + check.error(format!( + "{}:{line}: feature gate {name} has inconsistent `issue`: \"{}\" mismatches the {display} `issue` of \"{}\"", file.display(), - line, - name, f.tracking_issue_display(), - display, s.tracking_issue_display(), - ); + )); } }; check_features(&f, lang_features, "corresponding lang feature"); @@ -486,7 +464,7 @@ fn get_and_check_lib_features( lib_features.insert(name.to_owned(), f); } Err(msg) => { - tidy_error!(bad, "{}:{}: {}", file.display(), line, msg); + check.error(format!("{}:{line}: {msg}", file.display())); } }); lib_features diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs index 53115f4eaa41b..d52a82297388f 100644 --- a/src/tools/tidy/src/filenames.rs +++ b/src/tools/tidy/src/filenames.rs @@ -10,7 +10,10 @@ use std::path::Path; use std::process::Command; -pub fn check(root_path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("filenames").path(root_path)); let stat_output = Command::new("git") .arg("-C") .arg(root_path) @@ -20,20 +23,17 @@ pub fn check(root_path: &Path, bad: &mut bool) { .stdout; for filename in stat_output.split(|&b| b == 0) { match str::from_utf8(filename) { - Err(_) => tidy_error!( - bad, + Err(_) => check.error(format!( r#"non-UTF8 file names are not supported: "{}""#, String::from_utf8_lossy(filename), - ), - Ok(name) if name.chars().any(|c| c.is_control()) => tidy_error!( - bad, + )), + Ok(name) if name.chars().any(|c| c.is_control()) => check.error(format!( r#"control characters are not supported in file names: "{}""#, String::from_utf8_lossy(filename), - ), - Ok(name) if name.contains(':') => tidy_error!( - bad, + )), + Ok(name) if name.contains(':') => check.error(format!( r#"":" is not supported in file names because of Windows compatibility: "{name}""#, - ), + )), _ => (), } } diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 48d14a37514bd..02b914c21aebf 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -7,6 +7,7 @@ use std::path::Path; use regex::Regex; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; fn message() -> &'static Regex { @@ -20,19 +21,17 @@ fn is_fluent(path: &Path) -> bool { fn check_alphabetic( filename: &str, fluent: &str, - bad: &mut bool, + check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) { let mut matches = message().captures_iter(fluent).peekable(); while let Some(m) = matches.next() { let name = m.get(1).unwrap(); if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - tidy_error!( - bad, - "{filename}: message `{}` is already defined in {}", + check.error(format!( + "{filename}: message `{}` is already defined in {defined_filename}", name.as_str(), - defined_filename, - ); + )); } all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); @@ -40,13 +39,12 @@ fn check_alphabetic( if let Some(next) = matches.peek() { let next = next.get(1).unwrap(); if name.as_str() > next.as_str() { - tidy_error!( - bad, + check.error(format!( "{filename}: message `{}` appears before `{}`, but is alphabetically later than it run `./x.py test tidy --bless` to sort the file correctly", name.as_str(), next.as_str() - ); + )); } } else { break; @@ -57,7 +55,7 @@ run `./x.py test tidy --bless` to sort the file correctly", fn sort_messages( filename: &str, fluent: &str, - bad: &mut bool, + check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) -> String { let mut chunks = vec![]; @@ -65,12 +63,10 @@ fn sort_messages( for line in fluent.lines() { if let Some(name) = message().find(line) { if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - tidy_error!( - bad, - "{filename}: message `{}` is already defined in {}", + check.error(format!( + "{filename}: message `{}` is already defined in {defined_filename}", name.as_str(), - defined_filename, - ); + )); } all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); @@ -88,7 +84,9 @@ fn sort_messages( out } -pub fn check(path: &Path, bless: bool, bad: &mut bool) { +pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_alphabetical").path(path)); + let mut all_defined_msgs = HashMap::new(); walk( path, @@ -98,7 +96,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) { let sorted = sort_messages( ent.path().to_str().unwrap(), contents, - bad, + &mut check, &mut all_defined_msgs, ); if sorted != contents { @@ -110,12 +108,12 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) { check_alphabetic( ent.path().to_str().unwrap(), contents, - bad, + &mut check, &mut all_defined_msgs, ); } }, ); - crate::fluent_used::check(path, all_defined_msgs, bad); + crate::fluent_used::check(path, all_defined_msgs, diag_ctx); } diff --git a/src/tools/tidy/src/fluent_lowercase.rs b/src/tools/tidy/src/fluent_lowercase.rs index 13f0319909e17..1d80fda8f3b86 100644 --- a/src/tools/tidy/src/fluent_lowercase.rs +++ b/src/tools/tidy/src/fluent_lowercase.rs @@ -4,6 +4,7 @@ use std::path::Path; use fluent_syntax::ast::{Entry, Message, PatternElement}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; #[rustfmt::skip] @@ -34,7 +35,7 @@ fn is_allowed_capitalized_word(msg: &str) -> bool { }) } -fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) { +fn check_lowercase(filename: &str, contents: &str, check: &mut RunningCheck) { let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents); for entry in &parse.body { @@ -45,20 +46,20 @@ fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) { && value.chars().next().is_some_and(char::is_uppercase) && !is_allowed_capitalized_word(value) { - tidy_error!( - bad, + check.error(format!( "{filename}: message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`" - ); + )); } } } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_lowercase").path(path)); walk( path, |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), &mut |ent, contents| { - check_lowercase(ent.path().to_str().unwrap(), contents, bad); + check_lowercase(ent.path().to_str().unwrap(), contents, &mut check); }, ); } diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs index 836b5699289f2..c7c760b8d54fd 100644 --- a/src/tools/tidy/src/fluent_period.rs +++ b/src/tools/tidy/src/fluent_period.rs @@ -4,6 +4,7 @@ use std::path::Path; use fluent_syntax::ast::{Entry, PatternElement}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; fn filter_fluent(path: &Path) -> bool { @@ -20,7 +21,7 @@ const ALLOWLIST: &[&str] = &[ "incremental_corrupt_file", ]; -fn check_period(filename: &str, contents: &str, bad: &mut bool) { +fn check_period(filename: &str, contents: &str, check: &mut RunningCheck) { if filename.contains("codegen") { // FIXME: Too many codegen messages have periods right now... return; @@ -40,7 +41,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) { if value.ends_with(".") && !value.ends_with("...") { let ll = find_line(contents, value); let name = m.id.name; - tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period"); + check.error(format!("{filename}:{ll}: message `{name}` ends in a period")); } } @@ -56,7 +57,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) { { let ll = find_line(contents, value); let name = attr.id.name; - tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period"); + check.error(format!("{filename}:{ll}: attr `{name}` ends in a period")); } } } @@ -74,12 +75,14 @@ fn find_line(haystack: &str, needle: &str) -> usize { 1 } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_period").path(path)); + walk( path, |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), &mut |ent, contents| { - check_period(ent.path().to_str().unwrap(), contents, bad); + check_period(ent.path().to_str().unwrap(), contents, &mut check); }, ); } diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs index 909bf482ddfce..2047089631b38 100644 --- a/src/tools/tidy/src/fluent_used.rs +++ b/src/tools/tidy/src/fluent_used.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; fn filter_used_messages( @@ -27,13 +28,15 @@ fn filter_used_messages( } } -pub fn check(path: &Path, mut all_defined_msgs: HashMap, bad: &mut bool) { +pub fn check(path: &Path, mut all_defined_msgs: HashMap, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_used").path(path)); + let mut msgs_appear_only_once = HashMap::new(); walk(path, |path, _| filter_dirs(path), &mut |_, contents| { filter_used_messages(contents, &mut all_defined_msgs, &mut msgs_appear_only_once); }); for (name, filename) in msgs_appear_only_once { - tidy_error!(bad, "{filename}: message `{}` is not used", name,); + check.error(format!("{filename}: message `{name}` is not used")); } } diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs index 217eaf1758c48..3a6e3247de606 100644 --- a/src/tools/tidy/src/gcc_submodule.rs +++ b/src/tools/tidy/src/gcc_submodule.rs @@ -4,7 +4,11 @@ use std::path::Path; use std::process::Command; -pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { +use crate::diagnostics::DiagCtx; + +pub fn check(root_path: &Path, compiler_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("gcc_submodule"); + let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version"); let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path) .unwrap_or_else(|_| { @@ -26,7 +30,7 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { // Git is not available or we are in a tarball if !git_output.status.success() { - eprintln!("Cannot figure out the SHA of the GCC submodule"); + check.message("Cannot figure out the SHA of the GCC submodule"); return; } @@ -43,12 +47,11 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { // The SHA can start with + if the submodule is modified or - if it is not checked out. let gcc_submodule_sha = git_output.trim_start_matches(['+', '-']); if gcc_submodule_sha != cg_gcc_version { - *bad = true; - eprintln!( + check.error(format!( r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`). Make sure to set the src/gcc submodule to commit {cg_gcc_version}. The GCC codegen backend commit is configured at {}."#, cg_gcc_version_path.display(), - ); + )); } } diff --git a/src/tools/tidy/src/known_bug.rs b/src/tools/tidy/src/known_bug.rs index e1921715ab922..d3b75e0cf5b8c 100644 --- a/src/tools/tidy/src/known_bug.rs +++ b/src/tools/tidy/src/known_bug.rs @@ -2,9 +2,11 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::*; -pub fn check(filepath: &Path, bad: &mut bool) { +pub fn check(filepath: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("known_bug").path(filepath)); walk(filepath, |path, _is_dir| filter_not_rust(path), &mut |entry, contents| { let file: &Path = entry.path(); @@ -19,11 +21,10 @@ pub fn check(filepath: &Path, bad: &mut bool) { [.., "tests", "crashes", "auxiliary", _aux_file_rs] ) && !contents.lines().any(|line| line.starts_with("//@ known-bug: ")) { - tidy_error!( - bad, + check.error(format!( "{} crash/ice test does not have a \"//@ known-bug: \" directive", file.display() - ); + )); } }); } diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 953d699199066..0bfee93796be1 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -11,7 +11,8 @@ use std::{env, io}; use build_helper::ci::CiEnv; use build_helper::git::{GitConfig, get_closest_upstream_commit}; use build_helper::stage0_parser::{Stage0Config, parse_stage0_file}; -use termcolor::WriteColor; + +use crate::diagnostics::{DiagCtx, RunningCheck}; macro_rules! static_regex { ($re:literal) => {{ @@ -43,28 +44,6 @@ macro_rules! t { }; } -macro_rules! tidy_error { - ($bad:expr, $($fmt:tt)*) => ({ - $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error"); - *$bad = true; - }); -} - -fn tidy_error(args: &str) -> std::io::Result<()> { - use std::io::Write; - - use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; - - let mut stderr = StandardStream::stdout(ColorChoice::Auto); - stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - - write!(&mut stderr, "tidy error")?; - stderr.set_color(&ColorSpec::new())?; - - writeln!(&mut stderr, ": {args}")?; - Ok(()) -} - pub struct CiInfo { pub git_merge_commit_email: String, pub nightly_branch: String, @@ -73,7 +52,9 @@ pub struct CiInfo { } impl CiInfo { - pub fn new(bad: &mut bool) -> Self { + pub fn new(diag_ctx: DiagCtx) -> Self { + let mut check = diag_ctx.start_check("CI history"); + let stage0 = parse_stage0_file(); let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config; @@ -86,11 +67,14 @@ impl CiInfo { let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) { Ok(Some(commit)) => Some(commit), Ok(None) => { - info.error_if_in_ci("no base commit found", bad); + info.error_if_in_ci("no base commit found", &mut check); None } Err(error) => { - info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad); + info.error_if_in_ci( + &format!("failed to retrieve base commit: {error}"), + &mut check, + ); None } }; @@ -105,12 +89,11 @@ impl CiInfo { } } - pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) { + pub fn error_if_in_ci(&self, msg: &str, check: &mut RunningCheck) { if self.ci_env.is_running_in_ci() { - *bad = true; - eprintln!("tidy check error: {msg}"); + check.error(msg); } else { - eprintln!("tidy check warning: {msg}. Some checks will be skipped."); + check.warning(format!("{msg}. Some checks will be skipped.")); } } } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 7d42102bbb9d0..73ff183868a27 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -8,8 +8,6 @@ use std::collections::VecDeque; use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; @@ -52,11 +50,8 @@ fn main() { let extra_checks = cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); - let mut bad = false; - let ci_info = CiInfo::new(&mut bad); - let bad = std::sync::Arc::new(AtomicBool::new(bad)); - - let mut diag_ctx = DiagCtx::new(verbose); + let diag_ctx = DiagCtx::new(verbose); + let ci_info = CiInfo::new(diag_ctx.clone()); let drain_handles = |handles: &mut VecDeque>| { // poll all threads for completion before awaiting the oldest one @@ -99,98 +94,85 @@ fn main() { } } - // check!(target_specific_tests, &tests_path); + check!(target_specific_tests, &tests_path); // Checks that are done on the cargo workspace. - // check!(deps, &root_path, &cargo, bless); - // check!(extdeps, &root_path); + check!(deps, &root_path, &cargo, bless); + check!(extdeps, &root_path); // Checks over tests. - // check!(tests_placement, &root_path); - // check!(tests_revision_unpaired_stdout_stderr, &tests_path); - // check!(debug_artifacts, &tests_path); - // check!(ui_tests, &root_path, bless); - // check!(mir_opt_tests, &tests_path, bless); - // check!(rustdoc_gui_tests, &tests_path); - // check!(rustdoc_css_themes, &librustdoc_path); - // check!(rustdoc_templates, &librustdoc_path); - // check!(rustdoc_json, &src_path, &ci_info); - // check!(known_bug, &crashes_path); - // check!(unknown_revision, &tests_path); + check!(tests_placement, &root_path); + check!(tests_revision_unpaired_stdout_stderr, &tests_path); + check!(debug_artifacts, &tests_path); + check!(ui_tests, &root_path, bless); + check!(mir_opt_tests, &tests_path, bless); + check!(rustdoc_gui_tests, &tests_path); + check!(rustdoc_css_themes, &librustdoc_path); + check!(rustdoc_templates, &librustdoc_path); + check!(rustdoc_json, &src_path, &ci_info); + check!(known_bug, &crashes_path); + check!(unknown_revision, &tests_path); // Checks that only make sense for the compiler. - // check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); - // check!(fluent_alphabetical, &compiler_path, bless); - // check!(fluent_period, &compiler_path); - // check!(fluent_lowercase, &compiler_path); - // check!(target_policy, &root_path); - // check!(gcc_submodule, &root_path, &compiler_path); + check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info); + check!(fluent_alphabetical, &compiler_path, bless); + check!(fluent_period, &compiler_path); + check!(fluent_lowercase, &compiler_path); + check!(target_policy, &root_path); + check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. - // check!(pal, &library_path); + check!(pal, &library_path); // Checks that need to be done for both the compiler and std libraries. - // check!(unit_tests, &src_path, false); - // check!(unit_tests, &compiler_path, false); - // check!(unit_tests, &library_path, true); - // - // if bins::check_filesystem_support(&[&root_path], &output_directory) { - // check!(bins, &root_path); - // } + check!(unit_tests, &src_path, false); + check!(unit_tests, &compiler_path, false); + check!(unit_tests, &library_path, true); + + if bins::check_filesystem_support(&[&root_path], &output_directory) { + check!(bins, &root_path); + } check!(style, &src_path); check!(style, &tests_path); check!(style, &compiler_path); check!(style, &library_path); - // check!(edition, &src_path); - // check!(edition, &compiler_path); - // check!(edition, &library_path); - // + check!(edition, &src_path); + check!(edition, &compiler_path); + check!(edition, &library_path); + check!(alphabetical, &root_manifest); check!(alphabetical, &src_path); check!(alphabetical, &tests_path); check!(alphabetical, &compiler_path); check!(alphabetical, &library_path); - // - // check!(x_version, &root_path, &cargo); - // - // check!(triagebot, &root_path); - // - // check!(filenames, &root_path); + + check!(x_version, &root_path, &cargo); + + check!(triagebot, &root_path); + check!(filenames, &root_path); let collected = { drain_handles(&mut handles); - let mut flag = false; - let r = features::check( - &src_path, - &tests_path, - &compiler_path, - &library_path, - &mut flag, - verbose, - ); - if flag { - bad.store(true, Ordering::Relaxed); - } - r + features::check(&src_path, &tests_path, &compiler_path, &library_path, diag_ctx.clone()) }; - // check!(unstable_book, &src_path, collected); - // - // check!( - // extra_checks, - // &root_path, - // &output_directory, - // &ci_info, - // &librustdoc_path, - // &tools_path, - // &npm, - // &cargo, - // bless, - // extra_checks, - // pos_args - // ); + check!(unstable_book, &src_path, collected); + + check!( + extra_checks, + &root_path, + &output_directory, + &ci_info, + &librustdoc_path, + &tools_path, + &npm, + &cargo, + bless, + extra_checks, + pos_args + ); }); if diag_ctx.into_conclusion() { diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs index 6119eb58383e3..0f9fab51d096f 100644 --- a/src/tools/tidy/src/mir_opt_tests.rs +++ b/src/tools/tidy/src/mir_opt_tests.rs @@ -5,9 +5,10 @@ use std::path::{Path, PathBuf}; use miropt_test_tools::PanicStrategy; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::walk_no_read; -fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) { +fn check_unused_files(path: &Path, bless: bool, check: &mut RunningCheck) { let mut rs_files = Vec::::new(); let mut output_files = HashSet::::new(); @@ -37,18 +38,17 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) { for extra in output_files { if !bless { - tidy_error!( - bad, + check.error(format!( "the following output file is not associated with any mir-opt test, you can remove it: {}", extra.display() - ); + )); } else { let _ = std::fs::remove_file(extra); } } } -fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { +fn check_dash_files(path: &Path, bless: bool, check: &mut RunningCheck) { for file in walkdir::WalkDir::new(path.join("mir-opt")) .into_iter() .filter_map(Result::ok) @@ -60,11 +60,10 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { && name.contains('-') { if !bless { - tidy_error!( - bad, + check.error(format!( "mir-opt test files should not have dashes in them: {}", path.display() - ); + )); } else { let new_name = name.replace('-', "_"); let mut new_path = path.to_owned(); @@ -75,7 +74,9 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { } } -pub fn check(path: &Path, bless: bool, bad: &mut bool) { - check_unused_files(path, bless, bad); - check_dash_files(path, bless, bad); +pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("mir_opt_tests").path(path)); + + check_unused_files(path, bless, &mut check); + check_dash_files(path, bless, &mut check); } diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 991ad55809cc3..cefad7d9596a6 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -32,6 +32,7 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; // Paths that may contain platform-specific code. @@ -67,7 +68,9 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/std/src/io/error.rs", // Repr unpacked needed for UEFI ]; -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("pal").path(path)); + // Sanity check that the complex parsing here works. let mut saw_target_arch = false; let mut saw_cfg_bang = false; @@ -88,7 +91,7 @@ pub fn check(path: &Path, bad: &mut bool) { return; } - check_cfgs(contents, file, bad, &mut saw_target_arch, &mut saw_cfg_bang); + check_cfgs(contents, file, &mut check, &mut saw_target_arch, &mut saw_cfg_bang); }); assert!(saw_target_arch); @@ -98,7 +101,7 @@ pub fn check(path: &Path, bad: &mut bool) { fn check_cfgs( contents: &str, file: &Path, - bad: &mut bool, + check: &mut RunningCheck, saw_target_arch: &mut bool, saw_cfg_bang: &mut bool, ) { @@ -115,7 +118,7 @@ fn check_cfgs( Ok(_) => unreachable!(), Err(i) => i + 1, }; - tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg); + check.error(format!("{}:{line}: platform-specific cfg: {cfg}", file.display())); }; for (idx, cfg) in cfgs { diff --git a/src/tools/tidy/src/rustdoc_css_themes.rs b/src/tools/tidy/src/rustdoc_css_themes.rs index af36f9ba58e0d..8d4af7a3bd564 100644 --- a/src/tools/tidy/src/rustdoc_css_themes.rs +++ b/src/tools/tidy/src/rustdoc_css_themes.rs @@ -3,7 +3,11 @@ use std::path::Path; -pub fn check(librustdoc_path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + +pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_css_themes").path(librustdoc_path)); + let rustdoc_css = "html/static/css/rustdoc.css"; let noscript_css = "html/static/css/noscript.css"; let rustdoc_css_contents = std::fs::read_to_string(librustdoc_path.join(rustdoc_css)) @@ -14,13 +18,13 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { "light", rustdoc_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())), noscript_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())), - bad, + &mut check, ); compare_themes_from_files( "dark", rustdoc_css_contents.lines().enumerate(), noscript_css_contents.lines().enumerate(), - bad, + &mut check, ); } @@ -28,7 +32,7 @@ fn compare_themes_from_files<'a>( name: &str, mut rustdoc_css_lines: impl Iterator, mut noscript_css_lines: impl Iterator, - bad: &mut bool, + check: &mut RunningCheck, ) { let begin_theme_pat = format!("/* Begin theme: {name}"); let mut found_theme = None; @@ -38,10 +42,9 @@ fn compare_themes_from_files<'a>( continue; } if let Some(found_theme) = found_theme { - tidy_error!( - bad, + check.error(format!( "rustdoc.css contains two {name} themes on lines {rustdoc_css_line_number} and {found_theme}", - ); + )); return; } found_theme = Some(rustdoc_css_line_number); @@ -50,14 +53,13 @@ fn compare_themes_from_files<'a>( continue; } if let Some(found_theme_noscript) = found_theme_noscript { - tidy_error!( - bad, + check.error(format!( "noscript.css contains two {name} themes on lines {noscript_css_line_number} and {found_theme_noscript}", - ); + )); return; } found_theme_noscript = Some(noscript_css_line_number); - compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, bad); + compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, check); } } } @@ -66,7 +68,7 @@ fn compare_themes<'a>( name: &str, rustdoc_css_lines: impl Iterator, noscript_css_lines: impl Iterator, - bad: &mut bool, + check: &mut RunningCheck, ) { let end_theme_pat = format!("/* End theme: {name}"); for ( @@ -90,12 +92,11 @@ fn compare_themes<'a>( break; } if rustdoc_css_line != noscript_css_line { - tidy_error!( - bad, - "noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same", - ); - eprintln!("- {noscript_css_line}"); - eprintln!("+ {rustdoc_css_line}"); + check.error(format!( + r#"noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same +- {noscript_css_line} ++ {rustdoc_css_line}"#, + )); return; } } diff --git a/src/tools/tidy/src/rustdoc_gui_tests.rs b/src/tools/tidy/src/rustdoc_gui_tests.rs index 3b995f219d269..8ec300c42ce9f 100644 --- a/src/tools/tidy/src/rustdoc_gui_tests.rs +++ b/src/tools/tidy/src/rustdoc_gui_tests.rs @@ -2,18 +2,21 @@ use std::path::Path; -pub fn check(path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_gui_tests").path(path)); + crate::walk::walk( &path.join("rustdoc-gui"), |p, is_dir| !is_dir && p.extension().is_none_or(|e| e != "goml"), &mut |entry, content| { for line in content.lines() { if !line.starts_with("// ") { - tidy_error!( - bad, + check.error(format!( "{}: rustdoc-gui tests must start with a small description", entry.path().display(), - ); + )); return; } else if line.starts_with("// ") { let parts = line[2..].trim(); diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index 722e1ebd0cad2..7a53c08737f6c 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -4,19 +4,22 @@ use std::path::Path; use std::str::FromStr; +use crate::diagnostics::{CheckId, DiagCtx}; + const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types"; -pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { - println!("Checking tidy rustdoc_json..."); +pub fn check(src_path: &Path, ci_info: &crate::CiInfo, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_json").path(src_path)); + let Some(base_commit) = &ci_info.base_commit else { - eprintln!("No base commit, skipping rustdoc_json check"); + check.verbose_msg("No base commit, skipping rustdoc_json check"); return; }; // First we check that `src/rustdoc-json-types` was modified. if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) { // `rustdoc-json-types` was not modified so nothing more to check here. - println!("`rustdoc-json-types` was not modified."); + check.verbose_msg("`rustdoc-json-types` was not modified."); return; } // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated. @@ -45,34 +48,29 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { } } if format_version_updated != latest_feature_comment_updated { - *bad = true; - if latest_feature_comment_updated { - eprintln!( - "error in `rustdoc_json` tidy check: `Latest feature` comment was updated \ - whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`" - ); + let msg = if latest_feature_comment_updated { + format!( + "`Latest feature` comment was updated whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`" + ) } else { - eprintln!( - "error in `rustdoc_json` tidy check: `Latest feature` comment was not \ - updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`" - ); - } + format!( + "`Latest feature` comment was not updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`" + ) + }; + check.error(msg); } match (new_version, old_version) { (Some(new_version), Some(old_version)) if new_version != old_version + 1 => { - *bad = true; - eprintln!( - "error in `rustdoc_json` tidy check: invalid `FORMAT_VERSION` increase in \ - `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`", + check.error(format!( + "invalid `FORMAT_VERSION` increase in `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`", old_version + 1, - ); + )); } _ => {} } } None => { - *bad = true; - eprintln!("error: failed to run `git diff` in rustdoc_json check"); + check.error("failed to run `git diff` in rustdoc_json check"); } } } diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs index 597290a6a9a8d..4e5b9988d5391 100644 --- a/src/tools/tidy/src/rustdoc_templates.rs +++ b/src/tools/tidy/src/rustdoc_templates.rs @@ -6,12 +6,15 @@ use std::path::Path; use ignore::DirEntry; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::walk; // Array containing `("beginning of tag", "end of tag")`. const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")]; -pub fn check(librustdoc_path: &Path, bad: &mut bool) { +pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_templates").path(librustdoc_path)); + walk( &librustdoc_path.join("html/templates"), |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("html")), @@ -46,12 +49,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { }) { // It seems like ending this line with a jinja tag is not needed after all. - tidy_error!( - bad, + check.error(format!( "`{}` at line {}: unneeded `{{# #}}` tag at the end of the line", path.path().display(), pos + 1, - ); + )); } continue; } @@ -67,12 +69,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { }) { None => { // No it's not, let's error. - tidy_error!( - bad, + check.error(format!( "`{}` at line {}: missing `{{# #}}` at the end of the line", path.path().display(), pos + 1, - ); + )); } Some(end_tag) => { // We skip the tag. diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs index 550932dbfdc38..cfcfcaf2435b3 100644 --- a/src/tools/tidy/src/target_policy.rs +++ b/src/tools/tidy/src/target_policy.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::path::Path; +use crate::diagnostics::DiagCtx; use crate::walk::{filter_not_rust, walk}; const TARGET_DEFINITIONS_PATH: &str = "compiler/rustc_target/src/spec/targets/"; @@ -23,7 +24,9 @@ const EXCEPTIONS: &[&str] = &[ "xtensa_esp32s3_espidf", ]; -pub fn check(root_path: &Path, bad: &mut bool) { +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("target_policy"); + let mut targets_to_find = HashSet::new(); let definitions_path = root_path.join(TARGET_DEFINITIONS_PATH); @@ -55,7 +58,7 @@ pub fn check(root_path: &Path, bad: &mut bool) { for target in targets_to_find { if !EXCEPTIONS.contains(&target.as_str()) { - tidy_error!(bad, "{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}") + check.error(format!("{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}")); } } } diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs index b2d5f259eb2db..c1db3874ad547 100644 --- a/src/tools/tidy/src/target_specific_tests.rs +++ b/src/tools/tidy/src/target_specific_tests.rs @@ -4,6 +4,7 @@ use std::collections::BTreeMap; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::iter_header::{HeaderLine, iter_header}; use crate::walk::filter_not_rust; @@ -16,7 +17,9 @@ struct RevisionInfo<'a> { llvm_components: Option>, } -pub fn check(tests_path: &Path, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("target-specific-tests").path(tests_path)); + crate::walk::walk(tests_path, |path, _is_dir| filter_not_rust(path), &mut |entry, content| { if content.contains("// ignore-tidy-target-specific-tests") { return; @@ -44,8 +47,7 @@ pub fn check(tests_path: &Path, bad: &mut bool) { } else if let Some((arch, _)) = v.split_once("-") { info.target_arch.replace(Some(arch)); } else { - eprintln!("{file}: seems to have a malformed --target value"); - *bad = true; + check.error(format!("{file}: seems to have a malformed --target value")); } } }); @@ -62,25 +64,22 @@ pub fn check(tests_path: &Path, bad: &mut bool) { (Some(target_arch), None) => { let llvm_component = target_arch.map_or_else(|| "".to_string(), arch_to_llvm_component); - eprintln!( + check.error(format!( "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" - ); - *bad = true; + )); } (None, Some(_)) => { - eprintln!( + check.error(format!( "{file}: revision {rev} should not specify `{LLVM_COMPONENTS_HEADER}` as it doesn't need `--target`" - ); - *bad = true; + )); } (Some(target_arch), Some(llvm_components)) => { if let Some(target_arch) = target_arch { let llvm_component = arch_to_llvm_component(target_arch); if !llvm_components.contains(&llvm_component.as_str()) { - eprintln!( + check.error(format!( "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" - ); - *bad = true; + )); } } } diff --git a/src/tools/tidy/src/tests_placement.rs b/src/tools/tidy/src/tests_placement.rs index 9d0057df8bcd8..e978e1c453c1e 100644 --- a/src/tools/tidy/src/tests_placement.rs +++ b/src/tools/tidy/src/tests_placement.rs @@ -1,15 +1,18 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; + const FORBIDDEN_PATH: &str = "src/test"; const ALLOWED_PATH: &str = "tests"; -pub fn check(root_path: impl AsRef, bad: &mut bool) { - if root_path.as_ref().join(FORBIDDEN_PATH).exists() { - tidy_error!( - bad, +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("tests-placement").path(root_path)); + + if root_path.join(FORBIDDEN_PATH).exists() { + check.error(format!( "Tests have been moved, please move them from {} to {}", - root_path.as_ref().join(FORBIDDEN_PATH).display(), - root_path.as_ref().join(ALLOWED_PATH).display() - ) + root_path.join(FORBIDDEN_PATH).display(), + root_path.join(ALLOWED_PATH).display() + )); } } diff --git a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs index 02412b6f190e8..1738088a3a0c0 100644 --- a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs +++ b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs @@ -4,6 +4,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsStr; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::iter_header::*; use crate::walk::*; @@ -21,7 +22,10 @@ const IGNORES: &[&str] = &[ const EXTENSIONS: &[&str] = &["stdout", "stderr"]; const SPECIAL_TEST: &str = "tests/ui/command/need-crate-arg-ignore-tidy.x.rs"; -pub fn check(tests_path: impl AsRef, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx + .start_check(CheckId::new("tests_revision_unpaired_stdout_stderr").path(tests_path)); + // Recurse over subdirectories under `tests/` walk_dir(tests_path.as_ref(), filter, &mut |entry| { // We are inspecting a folder. Collect the paths to interesting files `.rs`, `.stderr`, @@ -122,12 +126,11 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { [] | [_] => return, [_, _] if !expected_revisions.is_empty() => { // Found unrevisioned output files for a revisioned test. - tidy_error!( - bad, + check.error(format!( "found unrevisioned output file `{}` for a revisioned test `{}`", sibling.display(), test_path.display(), - ); + )); } [_, _] => return, [_, found_revision, .., extension] => { @@ -138,13 +141,12 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { { // Found some unexpected revision-esque component that is not a known // compare-mode or expected revision. - tidy_error!( - bad, + check.error(format!( "found output file `{}` for unexpected revision `{}` of test `{}`", sibling.display(), found_revision, test_path.display() - ); + )); } } } diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 6f25ed616fac7..9eccef29f2e2a 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -4,7 +4,10 @@ use std::path::Path; use toml::Value; -pub fn check(path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("triagebot").path(path)); let triagebot_path = path.join("triagebot.toml"); // This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce @@ -30,17 +33,14 @@ pub fn check(path: &Path, bad: &mut bool) { let full_path = path.join(clean_path); if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [mentions.*] contains path '{}' which doesn't exist", - clean_path - ); + check.error(format!( + "triagebot.toml [mentions.*] contains path '{clean_path}' which doesn't exist" + )); } } } else { - tidy_error!( - bad, - "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo." + check.error( + "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo.", ); } @@ -55,16 +55,13 @@ pub fn check(path: &Path, bad: &mut bool) { let full_path = path.join(clean_path); if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [assign.owners] contains path '{}' which doesn't exist", - clean_path - ); + check.error(format!( + "triagebot.toml [assign.owners] contains path '{clean_path}' which doesn't exist" + )); } } } else { - tidy_error!( - bad, + check.error( "triagebot.toml missing [assign.owners] section, this wrong for rust-lang/rust repo." ); } @@ -86,12 +83,9 @@ pub fn check(path: &Path, bad: &mut bool) { // Handle both file and directory paths if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [autolabel.{}] contains trigger_files path '{}' which doesn't exist", - label, - file_str - ); + check.error(format!( + "triagebot.toml [autolabel.{label}] contains trigger_files path '{file_str}' which doesn't exist", + )); } } } diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 5bf966b658c63..12eca47c171dc 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -7,13 +7,16 @@ use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + const ISSUES_TXT_HEADER: &str = r#"============================================================ ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️ ============================================================ "#; -pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { +pub fn check(root_path: &Path, bless: bool, diag_ctx: DiagCtx) { let path = &root_path.join("tests"); + let mut check = diag_ctx.start_check(CheckId::new("ui_tests").path(path)); // the list of files in ui tests that are allowed to start with `issue-XXXX` // BTreeSet because we would like a stable ordering so --bless works @@ -33,16 +36,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { .collect(); if !is_sorted && !bless { - tidy_error!( - bad, + check.error( "`src/tools/tidy/src/issues.txt` is not in order, mostly because you modified it manually, please only update it with command `x test tidy --bless`" ); } - deny_new_top_level_ui_tests(bad, &path.join("ui")); + deny_new_top_level_ui_tests(&mut check, &path.join("ui")); - let remaining_issue_names = recursively_check_ui_tests(bad, path, &allowed_issue_names); + let remaining_issue_names = recursively_check_ui_tests(&mut check, path, &allowed_issue_names); // if there are any file names remaining, they were moved on the fs. // our data must remain up to date, so it must be removed from issues.txt @@ -64,16 +66,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { for file_name in remaining_issue_names { let mut p = PathBuf::from(path); p.push(file_name); - tidy_error!( - bad, + check.error(format!( "file `{}` no longer exists and should be removed from the exclusions in `src/tools/tidy/src/issues.txt`", p.display() - ); + )); } } } -fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) { +fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) { // See where we propose banning adding // new ui tests *directly* under `tests/ui/`. For more context, see: // @@ -93,16 +94,15 @@ fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) { }) .filter(|e| !e.file_type().is_dir()); for entry in top_level_ui_tests { - tidy_error!( - bad, + check.error(format!( "ui tests should be added under meaningful subdirectories: `{}`", entry.path().display() - ) + )); } } fn recursively_check_ui_tests<'issues>( - bad: &mut bool, + check: &mut RunningCheck, path: &Path, allowed_issue_names: &'issues BTreeSet<&'issues str>, ) -> BTreeSet<&'issues str> { @@ -113,19 +113,19 @@ fn recursively_check_ui_tests<'issues>( crate::walk::walk_no_read(&paths, |_, _| false, &mut |entry| { let file_path = entry.path(); if let Some(ext) = file_path.extension().and_then(OsStr::to_str) { - check_unexpected_extension(bad, file_path, ext); + check_unexpected_extension(check, file_path, ext); // NB: We do not use file_stem() as some file names have multiple `.`s and we // must strip all of them. let testname = file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; if ext == "stderr" || ext == "stdout" || ext == "fixed" { - check_stray_output_snapshot(bad, file_path, testname); - check_empty_output_snapshot(bad, file_path); + check_stray_output_snapshot(check, file_path, testname); + check_empty_output_snapshot(check, file_path); } deny_new_nondescriptive_test_names( - bad, + check, path, &mut remaining_issue_names, file_path, @@ -137,7 +137,7 @@ fn recursively_check_ui_tests<'issues>( remaining_issue_names } -fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) { +fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &str) { const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files "stderr", // expected stderr file, corresponds to a rs file @@ -178,11 +178,11 @@ fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) { if !(EXPECTED_TEST_FILE_EXTENSIONS.contains(&ext) || EXTENSION_EXCEPTION_PATHS.iter().any(|path| file_path.ends_with(path))) { - tidy_error!(bad, "file {} has unexpected extension {}", file_path.display(), ext); + check.error(format!("file {} has unexpected extension {}", file_path.display(), ext)); } } -fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str) { +fn check_stray_output_snapshot(check: &mut RunningCheck, file_path: &Path, testname: &str) { // Test output filenames have one of the formats: // ``` // $testname.stderr @@ -197,20 +197,20 @@ fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str) if !file_path.with_file_name(testname).with_extension("rs").exists() && !testname.contains("ignore-tidy") { - tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path); + check.error(format!("Stray file with UI testing output: {:?}", file_path)); } } -fn check_empty_output_snapshot(bad: &mut bool, file_path: &Path) { +fn check_empty_output_snapshot(check: &mut RunningCheck, file_path: &Path) { if let Ok(metadata) = fs::metadata(file_path) && metadata.len() == 0 { - tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path); + check.error(format!("Empty file with UI testing output: {:?}", file_path)); } } fn deny_new_nondescriptive_test_names( - bad: &mut bool, + check: &mut RunningCheck, path: &Path, remaining_issue_names: &mut BTreeSet<&str>, file_path: &Path, @@ -231,11 +231,10 @@ fn deny_new_nondescriptive_test_names( if !remaining_issue_names.remove(stripped_path.as_str()) && !stripped_path.starts_with("ui/issues/") { - tidy_error!( - bad, + check.error(format!( "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`", issue_n = &test_name[1], - ); + )); } } } diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 7396310ed37c9..cab445ac63a1b 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -11,9 +11,12 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; -pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { +pub fn check(root_path: &Path, stdlib: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("unit_tests").path(root_path)); + let skip = move |path: &Path, is_dir| { let file_name = path.file_name().unwrap_or_default(); @@ -92,14 +95,11 @@ pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { .to_owned() }; let name = if is_test() { "test" } else { "bench" }; - tidy_error!( - bad, - "`{}:{}` contains `#[{}]`; {}", + check.error(format!( + "`{}:{}` contains `#[{name}]`; {explanation}", path.display(), i + 1, - name, - explanation, - ); + )); return; } } diff --git a/src/tools/tidy/src/unknown_revision.rs b/src/tools/tidy/src/unknown_revision.rs index 0ba05c80a791b..776d45e25de0d 100644 --- a/src/tools/tidy/src/unknown_revision.rs +++ b/src/tools/tidy/src/unknown_revision.rs @@ -12,12 +12,14 @@ use std::sync::OnceLock; use ignore::DirEntry; use regex::Regex; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::iter_header::{HeaderLine, iter_header}; use crate::walk::{filter_dirs, filter_not_rust, walk}; -pub fn check(tests_path: impl AsRef, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("unknown_revision").path(tests_path)); walk( - tests_path.as_ref(), + tests_path, |path, is_dir| { filter_dirs(path) || filter_not_rust(path) || { // Auxiliary source files for incremental tests can refer to revisions @@ -25,11 +27,11 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { is_dir && path.file_name().is_some_and(|name| name == "auxiliary") } }, - &mut |entry, contents| visit_test_file(entry, contents, bad), + &mut |entry, contents| visit_test_file(entry, contents, &mut check), ); } -fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { +fn visit_test_file(entry: &DirEntry, contents: &str, check: &mut RunningCheck) { let mut revisions = HashSet::new(); let mut unused_revision_names = HashSet::new(); @@ -68,10 +70,9 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { // Fail if any revision names appear in both places, since that's probably a mistake. for rev in revisions.intersection(&unused_revision_names).copied().collect::>() { - tidy_error!( - bad, + check.error(format!( "revision name [{rev}] appears in both `revisions` and `unused-revision-names` in {path}" - ); + )); } // Compute the set of revisions that were mentioned but not declared, @@ -84,7 +85,7 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { bad_revisions.sort(); for (line_number, rev) in bad_revisions { - tidy_error!(bad, "unknown revision [{rev}] at {path}:{line_number}"); + check.error(format!("unknown revision [{rev}] at {path}:{line_number}")); } } diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index 0ed954d48deb1..bab294abee02d 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::features::{CollectedFeatures, Features, Status}; pub const PATH_STR: &str = "doc/unstable-book"; @@ -75,19 +76,18 @@ fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) - } /// Would switching underscores for dashes work? -fn maybe_suggest_dashes(names: &BTreeSet, feature_name: &str, bad: &mut bool) { +fn maybe_suggest_dashes(names: &BTreeSet, feature_name: &str, check: &mut RunningCheck) { let with_dashes = feature_name.replace('_', "-"); if names.contains(&with_dashes) { - tidy_error!( - bad, - "the file `{}.md` contains underscores; use dashes instead: `{}.md`", - feature_name, - with_dashes, - ); + check.error(format!( + "the file `{feature_name}.md` contains underscores; use dashes instead: `{with_dashes}.md`", + )); } } -pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) { +pub fn check(path: &Path, features: CollectedFeatures, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("unstable_book"); + let lang_features = features.lang; let lib_features = features .lib @@ -108,26 +108,22 @@ pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) { // Check for Unstable Book sections that don't have a corresponding unstable feature for feature_name in &unstable_book_lib_features_section_file_names - &unstable_lib_feature_names { - tidy_error!( - bad, - "The Unstable Book has a 'library feature' section '{}' which doesn't \ - correspond to an unstable library feature", - feature_name - ); - maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, bad); + check.error(format!( + "The Unstable Book has a 'library feature' section '{feature_name}' which doesn't \ + correspond to an unstable library feature" + )); + maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, &mut check); } // Check for Unstable Book sections that don't have a corresponding unstable feature. for feature_name in &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names { - tidy_error!( - bad, - "The Unstable Book has a 'language feature' section '{}' which doesn't \ - correspond to an unstable language feature", - feature_name - ); - maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, bad); + check.error(format!( + "The Unstable Book has a 'language feature' section '{feature_name}' which doesn't \ + correspond to an unstable language feature" + )); + maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, &mut check); } // List unstable features that don't have Unstable Book sections. diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs index 9f7f43c4000b1..b3e322d9403f4 100644 --- a/src/tools/tidy/src/x_version.rs +++ b/src/tools/tidy/src/x_version.rs @@ -3,12 +3,18 @@ use std::process::{Command, Stdio}; use semver::Version; -pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(root: &Path, cargo: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("x_version").path(root)); let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn(); let child = match cargo_list { Ok(child) => child, - Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e), + Err(e) => { + check.error(format!("failed to run `cargo`: {e}")); + return; + } }; let cargo_list = child.wait_with_output().unwrap(); @@ -47,13 +53,10 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { ) } } else { - tidy_error!( - bad, - "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`" - ) + check.error("Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`") } } else { - tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status) + check.error(format!("failed to check version of `x`: {}", cargo_list.status)) } } From 4c208f5c64c3a86e52f5ebff2792ed1e3d8ca5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 15:24:56 +0200 Subject: [PATCH 0679/1900] Implement output of colored messages with optional check context --- src/tools/features-status-dump/src/main.rs | 3 +- src/tools/tidy/src/alphabetical/tests.rs | 27 +++-- src/tools/tidy/src/deps.rs | 4 +- src/tools/tidy/src/diagnostics.rs | 132 ++++++++++++++++----- src/tools/tidy/src/extdeps.rs | 4 +- src/tools/tidy/src/filenames.rs | 4 +- src/tools/tidy/src/main.rs | 22 +++- src/tools/tidy/src/tests_placement.rs | 4 +- src/tools/tidy/src/triagebot.rs | 4 +- src/tools/unstable-book-gen/src/main.rs | 3 +- 10 files changed, 152 insertions(+), 55 deletions(-) diff --git a/src/tools/features-status-dump/src/main.rs b/src/tools/features-status-dump/src/main.rs index 1ce98d1506d1c..a4f88362ab816 100644 --- a/src/tools/features-status-dump/src/main.rs +++ b/src/tools/features-status-dump/src/main.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use clap::Parser; +use tidy::diagnostics::RunningCheck; use tidy::features::{Feature, collect_lang_features, collect_lib_features}; #[derive(Debug, Parser)] @@ -29,7 +30,7 @@ struct FeaturesStatus { fn main() -> Result<()> { let Cli { compiler_path, library_path, output_path } = Cli::parse(); - let lang_features_status = collect_lang_features(&compiler_path, &mut false); + let lang_features_status = collect_lang_features(&compiler_path, &mut RunningCheck::new_noop()); let lib_features_status = collect_lib_features(&library_path) .into_iter() .filter(|&(ref name, _)| !lang_features_status.contains_key(name)) diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs index 4d05bc33cedc3..b181ab8f744f9 100644 --- a/src/tools/tidy/src/alphabetical/tests.rs +++ b/src/tools/tidy/src/alphabetical/tests.rs @@ -1,19 +1,22 @@ -use std::io::Write; -use std::str::from_utf8; +use std::path::Path; -use super::*; +use crate::alphabetical::check_lines; +use crate::diagnostics::DiagCtx; #[track_caller] fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) { - let mut actual_msg = Vec::new(); - let mut actual_bad = false; - let mut err = |args: &_| { - write!(&mut actual_msg, "{args}")?; - Ok(()) - }; - check_lines(&name, lines.lines().enumerate(), &mut err, &mut actual_bad); - assert_eq!(expected_msg, from_utf8(&actual_msg).unwrap()); - assert_eq!(expected_bad, actual_bad); + let diag_ctx = DiagCtx::new(Path::new("/"), false); + let mut check = diag_ctx.start_check("alphabetical-test"); + check_lines(&name, lines.lines().enumerate(), &mut check); + + assert_eq!(expected_bad, check.is_bad()); + let errors = check.get_errors(); + if expected_bad { + assert_eq!(errors.len(), 1); + assert_eq!(expected_msg, errors[0]); + } else { + assert!(errors.is_empty()); + } } #[track_caller] diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index a9d07c7531579..e275d3042cbba 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -9,7 +9,7 @@ use build_helper::ci::CiEnv; use cargo_metadata::semver::Version; use cargo_metadata::{Metadata, Package, PackageId}; -use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; +use crate::diagnostics::{DiagCtx, RunningCheck}; #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] mod proc_macro_deps; @@ -664,7 +664,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path /// to the cargo executable. pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("deps").path(root)); + let mut check = diag_ctx.start_check("deps"); let mut checked_runtime_licenses = false; diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index d7889f925ea91..6e95f97d0104f 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -1,9 +1,9 @@ use std::collections::HashSet; -use std::fmt::Display; +use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use termcolor::WriteColor; +use termcolor::{Color, WriteColor}; /// Collects diagnostics from all tidy steps, and contains shared information /// that determines how should message and logs be presented. @@ -14,26 +14,40 @@ use termcolor::WriteColor; pub struct DiagCtx(Arc>); impl DiagCtx { - pub fn new(verbose: bool) -> Self { + pub fn new(root_path: &Path, verbose: bool) -> Self { Self(Arc::new(Mutex::new(DiagCtxInner { running_checks: Default::default(), finished_checks: Default::default(), + root_path: root_path.to_path_buf(), verbose, }))) } pub fn start_check>(&self, id: Id) -> RunningCheck { - let id = id.into(); + let mut id = id.into(); let mut ctx = self.0.lock().unwrap(); + + // Shorten path for shorter diagnostics + id.path = match id.path { + Some(path) => Some(path.strip_prefix(&ctx.root_path).unwrap_or(&path).to_path_buf()), + None => None, + }; + ctx.start_check(id.clone()); - RunningCheck { id, bad: false, ctx: self.0.clone() } + RunningCheck { + id, + bad: false, + ctx: self.0.clone(), + #[cfg(test)] + errors: vec![], + } } - pub fn into_conclusion(self) -> bool { - let ctx = self.0.lock().unwrap(); + pub fn into_failed_checks(self) -> Vec { + let ctx = Arc::into_inner(self.0).unwrap().into_inner().unwrap(); assert!(ctx.running_checks.is_empty(), "Some checks are still running"); - ctx.finished_checks.iter().any(|c| c.bad) + ctx.finished_checks.into_iter().filter(|c| c.bad).collect() } } @@ -41,6 +55,7 @@ struct DiagCtxInner { running_checks: HashSet, finished_checks: HashSet, verbose: bool, + root_path: PathBuf, } impl DiagCtxInner { @@ -48,6 +63,7 @@ impl DiagCtxInner { if self.has_check_id(&id) { panic!("Starting a check named `{id:?}` for the second time"); } + self.running_checks.insert(id); } @@ -57,6 +73,13 @@ impl DiagCtxInner { "Finishing check `{:?}` that was not started", check.id ); + + if check.bad { + output_message("FAIL", Some(&check.id), Some(COLOR_ERROR)); + } else if self.verbose { + output_message("OK", Some(&check.id), Some(COLOR_SUCCESS)); + } + self.finished_checks.insert(check); } @@ -71,8 +94,8 @@ impl DiagCtxInner { /// Identifies a single step #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct CheckId { - name: String, - path: Option, + pub name: String, + pub path: Option, } impl CheckId { @@ -91,40 +114,70 @@ impl From<&'static str> for CheckId { } } +impl Display for CheckId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name)?; + if let Some(path) = &self.path { + write!(f, " ({})", path.display())?; + } + Ok(()) + } +} + #[derive(PartialEq, Eq, Hash, Debug)] -struct FinishedCheck { +pub struct FinishedCheck { id: CheckId, bad: bool, } +impl FinishedCheck { + pub fn id(&self) -> &CheckId { + &self.id + } +} + /// Represents a single tidy check, identified by its `name`, running. pub struct RunningCheck { id: CheckId, bad: bool, ctx: Arc>, + #[cfg(test)] + errors: Vec, } impl RunningCheck { + /// Creates a new instance of a running check without going through the diag + /// context. + /// Useful if you want to run some functions from tidy without configuring + /// diagnostics. + pub fn new_noop() -> Self { + let ctx = DiagCtx::new(Path::new(""), false); + ctx.start_check("noop") + } + /// Immediately output an error and mark the check as failed. - pub fn error(&mut self, t: T) { + pub fn error(&mut self, msg: T) { self.mark_as_bad(); - tidy_error(&t.to_string()).expect("failed to output error"); + let msg = msg.to_string(); + output_message(&msg, Some(&self.id), Some(COLOR_ERROR)); + #[cfg(test)] + self.errors.push(msg); } /// Immediately output a warning. - pub fn warning(&mut self, t: T) { - eprintln!("WARNING: {t}"); + pub fn warning(&mut self, msg: T) { + output_message(&msg.to_string(), Some(&self.id), Some(COLOR_WARNING)); } /// Output an informational message - pub fn message(&mut self, t: T) { - eprintln!("{t}"); + pub fn message(&mut self, msg: T) { + output_message(&msg.to_string(), Some(&self.id), None); } /// Output a message only if verbose output is enabled. - pub fn verbose_msg(&mut self, t: T) { + pub fn verbose_msg(&mut self, msg: T) { if self.is_verbose_enabled() { - self.message(t); + self.message(msg); } } @@ -138,6 +191,11 @@ impl RunningCheck { self.ctx.lock().unwrap().verbose } + #[cfg(test)] + pub fn get_errors(&self) -> Vec { + self.errors.clone() + } + fn mark_as_bad(&mut self) { self.bad = true; } @@ -149,17 +207,37 @@ impl Drop for RunningCheck { } } -fn tidy_error(args: &str) -> std::io::Result<()> { +pub const COLOR_SUCCESS: Color = Color::Green; +pub const COLOR_ERROR: Color = Color::Red; +pub const COLOR_WARNING: Color = Color::Yellow; + +/// Output a message to stderr. +/// The message can be optionally scoped to a certain check, and it can also have a certain color. +pub fn output_message(msg: &str, id: Option<&CheckId>, color: Option) { use std::io::Write; - use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; + use termcolor::{ColorChoice, ColorSpec, StandardStream}; - let mut stderr = StandardStream::stdout(ColorChoice::Auto); - stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + let mut stderr = StandardStream::stderr(ColorChoice::Auto); + if let Some(color) = &color { + stderr.set_color(ColorSpec::new().set_fg(Some(*color))).unwrap(); + } - write!(&mut stderr, "tidy error")?; - stderr.set_color(&ColorSpec::new())?; + match id { + Some(id) => { + write!(&mut stderr, "tidy [{}", id.name).unwrap(); + if let Some(path) = &id.path { + write!(&mut stderr, " ({})", path.display()).unwrap(); + } + write!(&mut stderr, "]").unwrap(); + } + None => { + write!(&mut stderr, "tidy").unwrap(); + } + } + if color.is_some() { + stderr.set_color(&ColorSpec::new()).unwrap(); + } - writeln!(&mut stderr, ": {args}")?; - Ok(()) + writeln!(&mut stderr, ": {msg}").unwrap(); } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 21612980007d8..f75de13b45ceb 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::Path; use crate::deps::WorkspaceInfo; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ @@ -16,7 +16,7 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("extdeps").path(root)); + let mut check = diag_ctx.start_check("extdeps"); for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs index d52a82297388f..835cbefbf6910 100644 --- a/src/tools/tidy/src/filenames.rs +++ b/src/tools/tidy/src/filenames.rs @@ -10,10 +10,10 @@ use std::path::Path; use std::process::Command; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; pub fn check(root_path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("filenames").path(root_path)); + let mut check = diag_ctx.start_check("filenames"); let stat_output = Command::new("git") .arg("-C") .arg(root_path) diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 73ff183868a27..93bc16111992a 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; -use tidy::diagnostics::DiagCtx; +use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message}; use tidy::*; fn main() { @@ -50,7 +50,7 @@ fn main() { let extra_checks = cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); - let diag_ctx = DiagCtx::new(verbose); + let diag_ctx = DiagCtx::new(&root_path, verbose); let ci_info = CiInfo::new(diag_ctx.clone()); let drain_handles = |handles: &mut VecDeque>| { @@ -175,8 +175,22 @@ fn main() { ); }); - if diag_ctx.into_conclusion() { - eprintln!("some tidy checks failed"); + let failed_checks = diag_ctx.into_failed_checks(); + if !failed_checks.is_empty() { + let mut failed: Vec = + failed_checks.into_iter().map(|c| c.id().to_string()).collect(); + failed.sort(); + output_message( + &format!( + "The following check{} failed: {}", + if failed.len() > 1 { "s" } else { "" }, + failed.join(", ") + ), + None, + Some(COLOR_ERROR), + ); process::exit(1); + } else { + output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS)); } } diff --git a/src/tools/tidy/src/tests_placement.rs b/src/tools/tidy/src/tests_placement.rs index e978e1c453c1e..8ba8cf552bd7f 100644 --- a/src/tools/tidy/src/tests_placement.rs +++ b/src/tools/tidy/src/tests_placement.rs @@ -1,12 +1,12 @@ use std::path::Path; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; const FORBIDDEN_PATH: &str = "src/test"; const ALLOWED_PATH: &str = "tests"; pub fn check(root_path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("tests-placement").path(root_path)); + let mut check = diag_ctx.start_check("tests_placement"); if root_path.join(FORBIDDEN_PATH).exists() { check.error(format!( diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 9eccef29f2e2a..41d61dcd14118 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -4,10 +4,10 @@ use std::path::Path; use toml::Value; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; pub fn check(path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("triagebot").path(path)); + let mut check = diag_ctx.start_check("triagebot"); let triagebot_path = path.join("triagebot.toml"); // This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index a7c6173d88c48..16550f83003dc 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -5,6 +5,7 @@ use std::env; use std::fs::{self, write}; use std::path::Path; +use tidy::diagnostics::RunningCheck; use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features}; use tidy::t; use tidy::unstable_book::{ @@ -122,7 +123,7 @@ fn main() { let src_path = Path::new(&src_path_str); let dest_path = Path::new(&dest_path_str); - let lang_features = collect_lang_features(compiler_path, &mut false); + let lang_features = collect_lang_features(compiler_path, &mut RunningCheck::new_noop()); let lib_features = collect_lib_features(library_path) .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) From 6abcadc235c24b0541e7bb3b6f6444d397d32f35 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:20:56 -0400 Subject: [PATCH 0680/1900] Port #[macro_export] to the new attribute parsing infrastructure Co-authored-by: Anne Stijns --- .../src/attributes/macro_attrs.rs | 63 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_attr_parsing/src/lints.rs | 12 ++++ compiler/rustc_expand/src/base.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir/src/lints.rs | 35 +++++++++-- compiler/rustc_lint/src/non_local_def.rs | 10 ++- compiler/rustc_lint_defs/src/builtin.rs | 7 ++- compiler/rustc_passes/messages.ftl | 7 --- compiler/rustc_passes/src/check_attr.rs | 60 ++++++------------ compiler/rustc_passes/src/errors.rs | 9 --- src/librustdoc/html/render/search_index.rs | 23 ++++--- src/librustdoc/json/conversions.rs | 9 +-- src/librustdoc/passes/strip_hidden.rs | 5 +- src/librustdoc/visit_ast.rs | 10 +-- .../src/macro_metavars_in_unsafe.rs | 7 ++- 17 files changed, 177 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 180130c7be4fc..849141c34f7d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,3 +1,4 @@ +use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; @@ -133,3 +134,65 @@ impl NoArgsAttributeParser for AllowInternalUnsafeParser { ]); const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); } + +pub(crate) struct MacroExportParser; + +impl SingleAttributeParser for MacroExportParser { + const PATH: &[Symbol] = &[sym::macro_export]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::MacroDef), + Error(Target::WherePredicate), + Error(Target::Crate), + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let suggestions = || { + >::TEMPLATE + .suggestions(AttrStyle::Inner, "macro_export") + }; + let local_inner_macros = match args { + ArgParser::NoArgs => false, + ArgParser::List(list) => { + let Some(l) = list.single() else { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + }; + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::local_inner_macros) => true, + _ => { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + } + } + } + ArgParser::NameValue(_) => { + let span = cx.attr_span; + let suggestions = suggestions(); + cx.emit_err(IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + return None; + } + }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ee5b7322b0247..4c32bb87a242c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -40,7 +40,7 @@ use crate::attributes::lint_helpers::{ }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, + AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser, }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; @@ -183,6 +183,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index b1a971eec3245..ab8ba0daf1f10 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -31,6 +31,18 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi }, ); } + AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, *id, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 88f88f30a8c69..3956125bace02 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -942,9 +942,9 @@ impl SyntaxExtension { .unwrap_or_default(); let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); - let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) - .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); + let local_inner_macros = + *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) + .unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 0784675b177a0..8ab43ff2582c0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -551,6 +551,9 @@ pub enum AttributeKind { /// Represents `#[macro_escape]`. MacroEscape(Span), + /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). + MacroExport { span: Span, local_inner_macros: bool }, + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 563e7a58c6d5e..8e44340507478 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -56,6 +56,7 @@ impl AttributeKind { Linkage(..) => No, LoopMatch(..) => No, MacroEscape(..) => No, + MacroExport { .. } => Yes, MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 0b24052b453e4..b7a0a6a0c197f 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,9 +31,34 @@ pub struct AttributeLint { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { this: Span, other: Span, warning: bool }, - IllFormedAttributeInput { suggestions: Vec }, - EmptyAttribute { first_span: Span }, - InvalidTarget { name: AttrPath, target: Target, applied: Vec, only: &'static str }, - InvalidStyle { name: AttrPath, is_used_as_inner: bool, target: Target, target_span: Span }, + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, + IllFormedAttributeInput { + suggestions: Vec, + }, + EmptyAttribute { + first_span: Span, + }, + + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see + InvalidMacroExportArguments { + suggestions: Vec, + }, + InvalidTarget { + name: AttrPath, + target: Target, + applied: Vec, + only: &'static str, + }, + InvalidStyle { + name: AttrPath, + is_used_as_inner: bool, + target: Target, + target_span: Span, + }, } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index dca22b986ffaa..b10be22dc389d 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,11 +1,12 @@ use rustc_errors::MultiSpan; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, Span, kw, sym}; +use rustc_span::{ExpnKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -241,7 +242,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ) } ItemKind::Macro(_, _macro, _kinds) - if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => + if find_attr!( + cx.tcx.get_all_attrs(item.owner_id.def_id), + AttributeKind::MacroExport { .. } + ) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b79075ac09b9c..3d0974d5d2809 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4191,8 +4191,13 @@ declare_lint! { /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. /// pub INVALID_MACRO_EXPORT_ARGUMENTS, - Warn, + Deny, "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #57571 ", + report_in_deps: true, + }; } declare_lint! { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 75537caa8946a..6cd68380e46f1 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -349,10 +349,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument - -passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments - passes_lang_item_fn = {$name -> [panic_impl] `#[panic_handler]` *[other] `{$name}` lang item @@ -392,9 +388,6 @@ passes_loop_match_attr = `#[loop_match]` should be applied to a loop .label = not a loop -passes_macro_export = - `#[macro_export]` only has an effect on macro definitions - passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4d5a8447695be..94c9e71ce7705 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -38,8 +38,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -217,7 +217,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, Attribute::Parsed(AttributeKind::Link(_, attr_span)) => { self.check_link(hir_id, *attr_span, span, target) - } + }, + Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { + self.check_macro_export(hir_id, *span, target) + }, Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -331,7 +334,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1850,45 +1852,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { if target != Target::MacroDef { + return; + } + + // special case when `#[macro_export]` is applied to a macro 2.0 + let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); + let is_decl_macro = !macro_definition.macro_rules; + + if is_decl_macro { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::MacroExport::Normal, + attr_span, + errors::MacroExport::OnDeclMacro, ); - } else if let Some(meta_item_list) = attr.meta_item_list() - && !meta_item_list.is_empty() - { - if meta_item_list.len() > 1 { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - attr.span(), - errors::MacroExport::TooManyItems, - ); - } else if !meta_item_list[0].has_name(sym::local_inner_macros) { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - meta_item_list[0].span(), - errors::MacroExport::InvalidArgument, - ); - } - } else { - // special case when `#[macro_export]` is applied to a macro 2.0 - let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); - let is_decl_macro = !macro_definition.macro_rules; - - if is_decl_macro { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::MacroExport::OnDeclMacro, - ); - } } } @@ -2253,7 +2232,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { + if macro_def.macro_rules + && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2384,7 +2365,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // which were unsuccessfully resolved due to cannot determine // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ - sym::macro_export, sym::rustc_main, sym::derive, sym::test, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2da4b6f52cf2c..cd8935f6b2f07 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -530,18 +530,9 @@ pub(crate) struct RustcForceInlineCoro { #[derive(LintDiagnostic)] pub(crate) enum MacroExport { - #[diag(passes_macro_export)] - Normal, - #[diag(passes_macro_export_on_decl_macro)] #[note] OnDeclMacro, - - #[diag(passes_invalid_macro_export_arguments)] - InvalidArgument, - - #[diag(passes_invalid_macro_export_arguments_too_many_items)] - TooManyItems, } #[derive(Subdiagnostic)] diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 2984f3ab50eea..3ffce61f7c613 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -6,6 +6,8 @@ use std::path::Path; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -1458,16 +1460,17 @@ pub(crate) fn build_index( if fqp.last() != Some(&item.name) { return None; } - let path = - if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { - // `#[macro_export]` always exports to the crate root. - vec![tcx.crate_name(defid.krate)] - } else { - if fqp.len() < 2 { - return None; - } - fqp[..fqp.len() - 1].to_vec() - }; + let path = if item.ty == ItemType::Macro + && find_attr!(tcx.get_all_attrs(defid), AttributeKind::MacroExport { .. }) + { + // `#[macro_export]` always exports to the crate root. + vec![tcx.crate_name(defid.krate)] + } else { + if fqp.len() < 2 { + return None; + } + fqp[..fqp.len() - 1].to_vec() + }; if path == item.module_path { return None; } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 6fe94f9d291b4..779e26c7b0f7b 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -912,12 +912,8 @@ fn maybe_from_hir_attr( hir::Attribute::Parsed(kind) => kind, hir::Attribute::Unparsed(_) => { - return Some(if attr.has_name(sym::macro_export) { - Attribute::MacroExport - // FIXME: We should handle `#[doc(hidden)]`. - } else { - other_attr(tcx, attr) - }); + // FIXME: We should handle `#[doc(hidden)]`. + return Some(other_attr(tcx, attr)); } }; @@ -925,6 +921,7 @@ fn maybe_from_hir_attr( AK::Deprecation { .. } => return None, // Handled separately into Item::deprecation. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), + AK::MacroExport { .. } => Attribute::MacroExport, AK::MustUse { reason, span: _ } => { Attribute::MustUse { reason: reason.map(|s| s.to_string()) } } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 3388ae46f056c..525d05b6a9860 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -2,9 +2,10 @@ use std::mem; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; use tracing::debug; use crate::clean::utils::inherits_doc_hidden; @@ -114,7 +115,7 @@ impl DocFolder for Stripper<'_, '_> { // If the macro has the `#[macro_export]` attribute, it means it's accessible at the // crate level so it should be handled differently. clean::MacroItem(..) => { - i.attrs.other_attrs.iter().any(|attr| attr.has_name(sym::macro_export)) + find_attr!(&i.attrs.other_attrs, AttributeKind::MacroExport { .. }) } _ => false, }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index b2e4b59437504..cd28322f5901c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,10 +5,11 @@ use std::mem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{CRATE_HIR_ID, Node}; +use rustc_hir::{CRATE_HIR_ID, Node, find_attr}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -166,7 +167,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if !child.reexport_chain.is_empty() && let Res::Def(DefKind::Macro(_), def_id) = child.res && let Some(local_def_id) = def_id.as_local() - && self.cx.tcx.has_attr(def_id, sym::macro_export) + && find_attr!(self.cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) && inserted.insert(def_id) { let item = self.cx.tcx.hir_expect_item(local_def_id); @@ -406,7 +407,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { || match item.kind { hir::ItemKind::Impl(..) => true, hir::ItemKind::Macro(_, _, _) => { - self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) + find_attr!(self.cx.tcx.get_all_attrs(item.owner_id.def_id), AttributeKind::MacroExport{..}) } _ => false, } @@ -524,7 +525,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let def_id = item.owner_id.to_def_id(); let is_macro_2_0 = !macro_def.macro_rules; - let nonexported = !tcx.has_attr(def_id, sym::macro_export); + let nonexported = + !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }); if is_macro_2_0 || nonexported || self.inlining { self.add_to_current_mod(item, renamed, import_id); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 9071c9c95f9d7..c5acaf0999332 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,10 +5,12 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; @@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + ( cx.effective_visibilities.is_exported(def_id) || + find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) && !cx.tcx.is_doc_hidden(def_id) } From f7fa83ec626b91c360067e78eae94d9199cf43dc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:21:11 -0400 Subject: [PATCH 0681/1900] Changes to uitests for macro_export port Co-authored-by: Anne Stijns --- ...invalid_macro_export_argument.allow.stderr | 40 ++ .../invalid_macro_export_argument.deny.stderr | 93 ++++- .../invalid_macro_export_argument.rs | 12 +- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 22 +- ...sue-43106-gating-of-builtin-attrs-error.rs | 3 +- ...43106-gating-of-builtin-attrs-error.stderr | 79 ++-- .../issue-43106-gating-of-builtin-attrs.rs | 30 +- ...issue-43106-gating-of-builtin-attrs.stderr | 389 +++++++++--------- .../lint/unused/unused-attr-duplicate.stderr | 24 +- 10 files changed, 417 insertions(+), 277 deletions(-) create mode 100644 tests/ui/attributes/invalid_macro_export_argument.allow.stderr diff --git a/tests/ui/attributes/invalid_macro_export_argument.allow.stderr b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr new file mode 100644 index 0000000000000..162b315b072a4 --- /dev/null +++ b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr @@ -0,0 +1,40 @@ +Future incompatibility report: Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + diff --git a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr index 9d44bd162c7b7..ad225ae187b11 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr +++ b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr @@ -1,26 +1,103 @@ -error: `#[macro_export]` can only take 1 or 0 arguments +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` --> $DIR/invalid_macro_export_argument.rs:7:1 | LL | #[macro_export(hello, world)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 note: the lint level is defined here --> $DIR/invalid_macro_export_argument.rs:4:24 | LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:13:16 +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 | LL | #[macro_export(not_local_inner_macros)] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:33:16 +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 | LL | #[macro_export("blah")] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error: aborting due to 4 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/invalid_macro_export_argument.rs b/tests/ui/attributes/invalid_macro_export_argument.rs index c5fe39d062a4e..05a40913434e8 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.rs +++ b/tests/ui/attributes/invalid_macro_export_argument.rs @@ -5,13 +5,15 @@ #![cfg_attr(allow, allow(invalid_macro_export_arguments))] #[macro_export(hello, world)] -//[deny]~^ ERROR `#[macro_export]` can only take 1 or 0 arguments +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! a { () => () } #[macro_export(not_local_inner_macros)] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! b { () => () } @@ -20,18 +22,22 @@ macro_rules! b { macro_rules! c { () => () } + #[macro_export(local_inner_macros)] macro_rules! d { () => () } #[macro_export()] +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! e { () => () } #[macro_export("blah")] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! f { () => () } diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 932aaf7a9e2fa..e30479b03b11f 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -211,7 +211,7 @@ extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] #[macro_export = 18] -//~^ ERROR malformed `macro_export` attribute input +//~^ ERROR valid forms for the attribute are #[allow_internal_unsafe = 1] //~^ ERROR malformed //~| ERROR allow_internal_unsafe side-steps the unsafe_code lint diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index b85864b401e37..246029ecf80fe 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -178,22 +178,6 @@ LL | #[no_link()] | = note: for more information, visit -error: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:213:1 - | -LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL - #[macro_export = 18] -LL + #[macro_export(local_inner_macros)] - | -LL - #[macro_export = 18] -LL + #[macro_export] - | - error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/malformed-attrs.rs:98:1 | @@ -725,6 +709,12 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and ` LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/malformed-attrs.rs:213:1 + | +LL | #[macro_export = 18] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0565]: malformed `allow_internal_unsafe` attribute input --> $DIR/malformed-attrs.rs:215:1 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index f391cf92fb639..a1a78df8d5375 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -8,7 +8,7 @@ #![macro_export] -//~^ ERROR: `macro_export` attribute cannot be used at crate level +//~^ ERROR: `#[macro_export]` attribute cannot be used on crates #![rustc_main] //~^ ERROR: `rustc_main` attribute cannot be used at crate level //~| ERROR: use of an internal attribute [E0658] @@ -32,7 +32,6 @@ mod inline { //~^ NOTE the inner attribute doesn't annotate this module //~| NOTE the inner attribute doesn't annotate this module - //~| NOTE the inner attribute doesn't annotate this module mod inner { #![inline] } //~^ ERROR attribute cannot be used on diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 3b010c3e3127f..4f4edeef420fe 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -8,6 +8,14 @@ LL | #![rustc_main] = note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_main]` attribute is used internally to specify test entry point function +error: `#[macro_export]` attribute cannot be used on crates + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:10:1 + | +LL | #![macro_export] + | ^^^^^^^^^^^^^^^^ + | + = help: `#[macro_export]` can only be applied to macro defs + error: `#[path]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 | @@ -49,7 +57,7 @@ LL | #[inline] = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:37:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:36:17 | LL | mod inner { #![inline] } | ^^^^^^^^^^ @@ -57,7 +65,7 @@ LL | mod inner { #![inline] } = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:45:5 | LL | #[inline] struct S; | ^^^^^^^^^ @@ -65,7 +73,7 @@ LL | #[inline] struct S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:49:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:48:5 | LL | #[inline] type T = S; | ^^^^^^^^^ @@ -73,7 +81,7 @@ LL | #[inline] type T = S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:52:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:51:5 | LL | #[inline] impl S { } | ^^^^^^^^^ @@ -81,7 +89,7 @@ LL | #[inline] impl S { } = help: `#[inline]` can only be applied to functions error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:82:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:81:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +97,7 @@ LL | #[export_name = "2200"] = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:85:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:84:17 | LL | mod inner { #![export_name="2200"] } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +105,7 @@ LL | mod inner { #![export_name="2200"] } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:90:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +113,7 @@ LL | #[export_name = "2200"] struct S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:93:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:92:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +121,7 @@ LL | #[export_name = "2200"] type T = S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:95:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +129,7 @@ LL | #[export_name = "2200"] impl S { } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on required trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:99:9 | LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +137,7 @@ LL | #[export_name = "2200"] fn foo(); = help: `#[export_name]` can be applied to statics, functions, inherent methods, provided trait methods, and trait methods in impl blocks error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:56:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:55:1 | LL | #[no_link] | ^^^^^^^^^^ @@ -143,7 +151,7 @@ LL | | } | |_- not an `extern crate` item error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:107:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:8 | LL | #[repr(C)] | ^ @@ -156,7 +164,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:130:8 | LL | #[repr(Rust)] | ^^^^ @@ -174,21 +182,6 @@ error: attribute should be applied to an `extern crate` item LL | #![no_link] | ^^^^^^^^^^^ not an `extern crate` item -error: `macro_export` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:10:1 - | -LL | #![macro_export] - | ^^^^^^^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![macro_export] -LL + #[macro_export] - | - error: `rustc_main` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 | @@ -220,85 +213,85 @@ LL + #[repr()] | error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:61:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:60:17 | LL | mod inner { #![no_link] } | ------------^^^^^^^^^^^-- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:65:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:64:5 | LL | #[no_link] fn f() { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:69:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:5 | LL | #[no_link] struct S; | ^^^^^^^^^^ --------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:73:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5 | LL | #[no_link]type T = S; | ^^^^^^^^^^----------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:77:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5 | LL | #[no_link] impl S { } | ^^^^^^^^^^ ---------- not an `extern crate` item error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:111:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:110:25 | LL | mod inner { #![repr(C)] } | --------------------^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:115:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:114:12 | LL | #[repr(C)] fn f() { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:121:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:12 | LL | #[repr(C)] type T = S; | ^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:125:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:124:12 | LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:135:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:134:25 | LL | mod inner { #![repr(Rust)] } | --------------------^^^^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:139:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:138:12 | LL | #[repr(Rust)] fn f() { } | ^^^^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:145:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:144:12 | LL | #[repr(Rust)] type T = S; | ^^^^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:149:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:148:12 | LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:39:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -313,7 +306,7 @@ Some errors have detailed explanations: E0517, E0658. For more information about an error, try `rustc --explain E0517`. Future incompatibility report: Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:39:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 546aa4052d333..aa5aab41e7258 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -214,22 +214,40 @@ mod macro_use { } #[macro_export] -//~^ WARN `#[macro_export]` only has an effect on macro definitions +//~^ WARN `#[macro_export]` attribute cannot be used on modules [unused_attributes] +//~| WARN previously accepted +//~| HELP can only be applied to +//~| HELP remove the attribute mod macro_export { mod inner { #![macro_export] } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on modules + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] fn f() { } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on function + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] struct S; - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on structs + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] type T = S; - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on type aliases + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] impl S { } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on inherent impl blocks + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute } // At time of unit test authorship, if compiling without `--test` then diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 3c835be5cffab..5e2029c45165a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1,5 +1,5 @@ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:511:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:529:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:508:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:526:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ @@ -186,31 +186,24 @@ warning: unknown lint: `x5100` LL | #[deny(x5100)] impl S { } | ^^^^^ -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:1 +warning: crate-level attribute should be an inner attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:1 | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ +LL | #[reexport_test_harness_main = "2900"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/issue-43106-gating-of-builtin-attrs.rs:37:9 | LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:483:1 - | -LL | #[reexport_test_harness_main = "2900"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | help: add a `!` | LL | #![reexport_test_harness_main = "2900"] | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 | LL | #[link(name = "x")] | ^^^^^^^^^^^^^^^^^^^ @@ -226,7 +219,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:771:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -237,7 +230,7 @@ LL | #![windows_subsystem = "windows"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:821:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -248,7 +241,7 @@ LL | #![crate_type = "0800"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:845:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:863:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ @@ -259,7 +252,7 @@ LL | #![feature(x0600)] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:888:1 | LL | #[no_main] | ^^^^^^^^^^ @@ -270,7 +263,7 @@ LL | #![no_main] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:894:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:912:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ @@ -296,44 +289,14 @@ LL | #![feature(rust1)] | = note: `#[warn(stable_features)]` on by default -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:219:17 - | -LL | mod inner { #![macro_export] } - | ^^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:5 - | -LL | #[macro_export] fn f() { } - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:225:5 - | -LL | #[macro_export] struct S; - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:5 - | -LL | #[macro_export] type T = S; - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:231:5 - | -LL | #[macro_export] impl S { } - | ^^^^^^^^^^^^^^^ - warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:487:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:490:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:508:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,7 +307,7 @@ LL | #![reexport_test_harness_main = "2900"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:512:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -355,7 +318,7 @@ LL | #![reexport_test_harness_main = "2900"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:498:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:516:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,7 +329,7 @@ LL | #![reexport_test_harness_main = "2900"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:502:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:520:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -377,7 +340,7 @@ LL | #![reexport_test_harness_main = "2900"] impl S { } | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:701:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:719:17 | LL | mod inner { #![link(name = "x")] } | ------------^^^^^^^^^^^^^^^^^^^^-- not an `extern` block @@ -385,7 +348,7 @@ LL | mod inner { #![link(name = "x")] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[link(name = "x")] fn f() { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -393,7 +356,7 @@ LL | #[link(name = "x")] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:711:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 | LL | #[link(name = "x")] struct S; | ^^^^^^^^^^^^^^^^^^^ --------- not an `extern` block @@ -401,7 +364,7 @@ LL | #[link(name = "x")] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:5 | LL | #[link(name = "x")] type T = S; | ^^^^^^^^^^^^^^^^^^^ ----------- not an `extern` block @@ -409,7 +372,7 @@ LL | #[link(name = "x")] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:5 | LL | #[link(name = "x")] impl S { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -417,7 +380,7 @@ LL | #[link(name = "x")] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:5 | LL | #[link(name = "x")] extern "Rust" {} | ^^^^^^^^^^^^^^^^^^^ @@ -425,13 +388,13 @@ LL | #[link(name = "x")] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -442,7 +405,7 @@ LL | #![windows_subsystem = "windows"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:782:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:800:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -453,7 +416,7 @@ LL | #![windows_subsystem = "windows"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -464,7 +427,7 @@ LL | #![windows_subsystem = "windows"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:790:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -475,13 +438,13 @@ LL | #![windows_subsystem = "windows"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:825:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:828:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -492,7 +455,7 @@ LL | #![crate_type = "0800"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:832:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:850:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -503,7 +466,7 @@ LL | #![crate_type = "0800"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:836:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:854:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +477,7 @@ LL | #![crate_type = "0800"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:840:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:858:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -525,13 +488,13 @@ LL | #![crate_type = "0800"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:849:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ @@ -542,7 +505,7 @@ LL | #![feature(x0600)] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ @@ -553,7 +516,7 @@ LL | #![feature(x0600)] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:878:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ @@ -564,7 +527,7 @@ LL | #![feature(x0600)] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:864:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:882:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ @@ -575,13 +538,13 @@ LL | #![feature(x0600)] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:877:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:895:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ @@ -592,7 +555,7 @@ LL | #![no_main] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:881:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:899:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ @@ -603,7 +566,7 @@ LL | #![no_main] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:885:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:903:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ @@ -614,7 +577,7 @@ LL | #![no_main] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:889:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:907:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ @@ -625,13 +588,13 @@ LL | #![no_main] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:898:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:901:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:919:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ @@ -642,7 +605,7 @@ LL | #![no_builtins] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:905:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:923:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ @@ -653,7 +616,7 @@ LL | #![no_builtins] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:909:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:927:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ @@ -664,7 +627,7 @@ LL | #![no_builtins] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:913:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:931:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ @@ -710,8 +673,62 @@ LL | #[macro_use] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[macro_use]` can be applied to modules, extern crates, and crates +warning: `#[macro_export]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:1 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:17 + | +LL | mod inner { #![macro_export] } + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on functions + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:5 + | +LL | #[macro_export] fn f() { } + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on structs + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:234:5 + | +LL | #[macro_export] struct S; + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on type aliases + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:5 + | +LL | #[macro_export] type T = S; + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on inherent impl blocks + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 + | +LL | #[macro_export] impl S { } + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + warning: `#[path]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:271:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ @@ -720,7 +737,7 @@ LL | #[path = "3800"] fn f() { } = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:277:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:295:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ @@ -729,7 +746,7 @@ LL | #[path = "3800"] struct S; = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:283:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:301:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ @@ -738,7 +755,7 @@ LL | #[path = "3800"] type T = S; = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:307:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ @@ -747,7 +764,7 @@ LL | #[path = "3800"] impl S { } = help: `#[path]` can only be applied to modules warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:296:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -756,7 +773,7 @@ LL | #[automatically_derived] = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:302:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -765,7 +782,7 @@ LL | mod inner { #![automatically_derived] } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:308:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -774,7 +791,7 @@ LL | #[automatically_derived] fn f() { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -783,7 +800,7 @@ LL | #[automatically_derived] struct S; = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -792,7 +809,7 @@ LL | #[automatically_derived] type T = S; = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on traits - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:344:5 | LL | #[automatically_derived] trait W { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -801,7 +818,7 @@ LL | #[automatically_derived] trait W { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 | LL | #[automatically_derived] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -810,7 +827,7 @@ LL | #[automatically_derived] impl S { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[no_mangle]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:341:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:359:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ @@ -819,7 +836,7 @@ LL | #[no_mangle] = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:347:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:365:17 | LL | mod inner { #![no_mangle] } | ^^^^^^^^^^^^^ @@ -828,7 +845,7 @@ LL | mod inner { #![no_mangle] } = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:355:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:5 | LL | #[no_mangle] struct S; | ^^^^^^^^^^^^ @@ -837,7 +854,7 @@ LL | #[no_mangle] struct S; = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:361:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:5 | LL | #[no_mangle] type T = S; | ^^^^^^^^^^^^ @@ -846,7 +863,7 @@ LL | #[no_mangle] type T = S; = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:385:5 | LL | #[no_mangle] impl S { } | ^^^^^^^^^^^^ @@ -855,7 +872,7 @@ LL | #[no_mangle] impl S { } = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on required trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:9 | LL | #[no_mangle] fn foo(); | ^^^^^^^^^^^^ @@ -864,7 +881,7 @@ LL | #[no_mangle] fn foo(); = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks warning: `#[no_mangle]` attribute cannot be used on provided trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:9 | LL | #[no_mangle] fn bar() {} | ^^^^^^^^^^^^ @@ -873,7 +890,7 @@ LL | #[no_mangle] fn bar() {} = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks warning: `#[should_panic]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:388:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:406:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ @@ -882,7 +899,7 @@ LL | #[should_panic] = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:394:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:412:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ @@ -891,7 +908,7 @@ LL | mod inner { #![should_panic] } = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:402:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ @@ -900,7 +917,7 @@ LL | #[should_panic] struct S; = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ @@ -909,7 +926,7 @@ LL | #[should_panic] type T = S; = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ @@ -918,7 +935,7 @@ LL | #[should_panic] impl S { } = help: `#[should_panic]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:421:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:439:1 | LL | #[ignore] | ^^^^^^^^^ @@ -927,7 +944,7 @@ LL | #[ignore] = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:445:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ @@ -936,7 +953,7 @@ LL | mod inner { #![ignore] } = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:435:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:5 | LL | #[ignore] struct S; | ^^^^^^^^^ @@ -945,7 +962,7 @@ LL | #[ignore] struct S; = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:441:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ @@ -954,7 +971,7 @@ LL | #[ignore] type T = S; = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:447:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ @@ -963,7 +980,7 @@ LL | #[ignore] impl S { } = help: `#[ignore]` can only be applied to functions warning: `#[no_implicit_prelude]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -972,7 +989,7 @@ LL | #[no_implicit_prelude] fn f() { } = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:464:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -981,7 +998,7 @@ LL | #[no_implicit_prelude] struct S; = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:470:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -990,7 +1007,7 @@ LL | #[no_implicit_prelude] type T = S; = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -999,7 +1016,7 @@ LL | #[no_implicit_prelude] impl S { } = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[macro_escape]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:533:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ @@ -1008,7 +1025,7 @@ LL | #[macro_escape] fn f() { } = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:521:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:539:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ @@ -1017,7 +1034,7 @@ LL | #[macro_escape] struct S; = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:527:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:545:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ @@ -1026,7 +1043,7 @@ LL | #[macro_escape] type T = S; = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:533:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ @@ -1035,13 +1052,13 @@ LL | #[macro_escape] impl S { } = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:558:1 | LL | #[no_std] | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:542:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:560:1 | LL | / mod no_std { LL | | @@ -1051,61 +1068,61 @@ LL | | } | |_^ warning: the `#![no_std]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:562:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:15 | LL | #[no_std] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:5 | LL | #[no_std] struct S; | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:15 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:573:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:573:15 | LL | #[no_std] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:577:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:577:15 | LL | #[no_std] impl S { } | ^^^^^^^^^^ warning: `#[cold]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:581:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:599:1 | LL | #[cold] | ^^^^^^^ @@ -1114,7 +1131,7 @@ LL | #[cold] = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:588:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:606:17 | LL | mod inner { #![cold] } | ^^^^^^^^ @@ -1123,7 +1140,7 @@ LL | mod inner { #![cold] } = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:596:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:5 | LL | #[cold] struct S; | ^^^^^^^ @@ -1132,7 +1149,7 @@ LL | #[cold] struct S; = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:602:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:5 | LL | #[cold] type T = S; | ^^^^^^^ @@ -1141,7 +1158,7 @@ LL | #[cold] type T = S; = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:608:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:626:5 | LL | #[cold] impl S { } | ^^^^^^^ @@ -1150,7 +1167,7 @@ LL | #[cold] impl S { } = help: `#[cold]` can only be applied to functions warning: `#[link_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:615:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:1 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -1159,7 +1176,7 @@ LL | #[link_name = "1900"] = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on foreign modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:621:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:639:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -1168,7 +1185,7 @@ LL | #[link_name = "1900"] = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:628:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:17 | LL | mod inner { #![link_name="1900"] } | ^^^^^^^^^^^^^^^^^^^^ @@ -1177,7 +1194,7 @@ LL | mod inner { #![link_name="1900"] } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:634:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 | LL | #[link_name = "1900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^ @@ -1186,7 +1203,7 @@ LL | #[link_name = "1900"] fn f() { } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:640:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:5 | LL | #[link_name = "1900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^ @@ -1195,7 +1212,7 @@ LL | #[link_name = "1900"] struct S; = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:664:5 | LL | #[link_name = "1900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^ @@ -1204,7 +1221,7 @@ LL | #[link_name = "1900"] type T = S; = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5 | LL | #[link_name = "1900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^ @@ -1213,7 +1230,7 @@ LL | #[link_name = "1900"] impl S { } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_section]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:677:1 | LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1222,7 +1239,7 @@ LL | #[link_section = "1800"] = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:665:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17 | LL | mod inner { #![link_section="1800"] } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -1231,7 +1248,7 @@ LL | mod inner { #![link_section="1800"] } = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 | LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1240,7 +1257,7 @@ LL | #[link_section = "1800"] struct S; = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:697:5 | LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1249,7 +1266,7 @@ LL | #[link_section = "1800"] type T = S; = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1258,7 +1275,7 @@ LL | #[link_section = "1800"] impl S { } = help: `#[link_section]` can be applied to statics and functions warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -1267,7 +1284,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ @@ -1276,7 +1293,7 @@ LL | mod inner { #![must_use] } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ @@ -1285,7 +1302,7 @@ LL | #[must_use] type T = S; = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ @@ -1294,13 +1311,13 @@ LL | #[must_use] impl S { } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:799:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:1 | LL | / mod crate_name { LL | | @@ -1310,67 +1327,67 @@ LL | | } | |_^ warning: the `#![crate_name]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:28 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:28 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:28 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:816:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:816:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:28 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 | LL | / mod recursion_limit { LL | | @@ -1380,67 +1397,67 @@ LL | | } | |_^ warning: the `#![recursion_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:31 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:31 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:31 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:31 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 | LL | / mod type_length_limit { LL | | @@ -1450,55 +1467,55 @@ LL | | } | |_^ warning: the `#![type_length_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:33 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:33 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:33 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:33 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^ diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 3a3b450f3c55f..fa2c9e59a4184 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -53,18 +53,6 @@ note: attribute also specified here LL | #![no_builtins] | ^^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:44:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:43:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:41:1 | @@ -77,6 +65,18 @@ note: attribute also specified here LL | #[macro_use] | ^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:44:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:43:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:51:1 | From 7d80c15fa049a808e3b7cd7ff88447f9a42c0ceb Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:20:56 -0400 Subject: [PATCH 0682/1900] Port #[macro_export] to the new attribute parsing infrastructure Co-authored-by: Anne Stijns --- clippy_lints/src/macro_metavars_in_unsafe.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 9071c9c95f9d7..c5acaf0999332 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,10 +5,12 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; @@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + ( cx.effective_visibilities.is_exported(def_id) || + find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) && !cx.tcx.is_doc_hidden(def_id) } From d84505b2ca9d5c79c3700eb2098f8d0dc9aabb2e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 22:30:21 +0200 Subject: [PATCH 0683/1900] Post lint on the type HIR node Out of the 5 lints in the `derive` directory, only two of them (`unsafe_derive_deserialize` and `derive_partial_eq_without_eq`) posted the lint on the ADT node. This fixes the situation for the three other lints: - `derive_hash_with_manual_eq` - `derive_ord_xor_partial_ord` - `expl_impl_clone_on_copy` This allows `#[expect]` to be used on the ADT to silence the lint. Also, this makes `expl_impl_clone_on_copy` properly use an "help" message instead of a "note" one when suggesting a fix. --- .../src/derive/derive_ord_xor_partial_ord.rs | 7 ++++--- .../derive/derive_partial_eq_without_eq.rs | 13 ++++++++---- .../src/derive/derived_hash_with_manual_eq.rs | 10 ++++++---- .../src/derive/expl_impl_clone_on_copy.rs | 20 +++++++++++++------ clippy_lints/src/derive/mod.rs | 16 ++++++++++----- .../src/derive/unsafe_derive_deserialize.rs | 12 +++++++---- tests/ui/derive.rs | 13 ++++++++++++ tests/ui/derive.stderr | 10 +++++----- tests/ui/derive_ord_xor_partial_ord.rs | 15 ++++++++++++++ tests/ui/derived_hash_with_manual_eq.rs | 16 +++++++++++++++ 10 files changed, 101 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index cbbcb2f7a3bab..274c699ff9d24 100644 --- a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -12,6 +12,7 @@ pub(super) fn check<'tcx>( span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, ord_is_automatically_derived: bool, ) { if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) @@ -38,7 +39,7 @@ pub(super) fn check<'tcx>( "you are deriving `Ord` but have implemented `PartialOrd` explicitly" }; - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs index ed7881c461ffc..fbace0bd73acf 100644 --- a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::has_non_exhaustive_attr; use clippy_utils::ty::implements_trait_with_env; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; use rustc_span::{Span, sym}; @@ -11,7 +11,13 @@ use rustc_span::{Span, sym}; use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { if let ty::Adt(adt, args) = ty.kind() && cx.tcx.visibility(adt.did()).is_public() && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) @@ -20,7 +26,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() @@ -30,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T span_lint_hir_and_then( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), + adt_hir_id, span.ctxt().outer_expn_data().call_site, "you are deriving `PartialEq` and can implement `Eq`", |diag| { diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs index 6f36a58025a2b..afc02ce32d48e 100644 --- a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{HirId, TraitRef}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -10,8 +10,9 @@ use super::DERIVED_HASH_WITH_MANUAL_EQ; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, hash_is_automatically_derived: bool, ) { if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() @@ -31,9 +32,10 @@ pub(super) fn check<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( + span_lint_hir_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, + adt_hir_id, span, "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index 6b97b4bd6b4d3..dfb723b86eb93 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,13 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{implements_trait, is_copy}; -use rustc_hir::{self as hir, Item}; +use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use super::EXPL_IMPL_CLONE_ON_COPY; /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -54,12 +60,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h return; } - span_lint_and_note( + span_lint_hir_and_then( cx, EXPL_IMPL_CLONE_ON_COPY, + adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", + |diag| { + diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); + }, ); } diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 1d63394ce37d4..06efc2709faa6 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,3 +1,5 @@ +use clippy_utils::path_res; +use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -194,21 +196,25 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), + self_ty, .. }) = item.kind + && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Some(local_def_id) = def_id.as_local() { + let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); let trait_ref = &of_trait.trait_ref; let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); - derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); if is_automatically_derived { - unsafe_derive_deserialize::check(cx, item, trait_ref, ty); - derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty, adt_hir_id); } else { - expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id); } } } diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs index c391e7b62289f..38f3251fd3891 100644 --- a/clippy_lints/src/derive/unsafe_derive_deserialize.rs +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_lint_allowed, paths}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -13,7 +13,13 @@ use rustc_span::{Span, sym}; use super::UNSAFE_DERIVE_DESERIALIZE; /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { let mut visitor = UnsafeVisitor { cx }; walk_item(&mut visitor, item).is_break() @@ -22,8 +28,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if let Some(trait_def_id) = trait_ref.trait_def_id() && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) && cx .tcx diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 2c5fa0be9755c..305f73c92cd5c 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -130,3 +130,16 @@ fn issue14558() { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::expl_impl_clone_on_copy)] + #[derive(Copy)] + struct S; + + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index ff2c24ff48ee7..48e55fc7469e4 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,7 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:15:1 | LL | / impl Clone for Qux { @@ -33,7 +33,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:41:1 | LL | / impl<'a> Clone for Lt<'a> { @@ -55,7 +55,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:54:1 | LL | / impl Clone for BigArray { @@ -77,7 +77,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:67:1 | LL | / impl Clone for FnPtr { @@ -99,7 +99,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:89:1 | LL | / impl Clone for Generic2 { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 3ef4ee9463dca..b4bb24b0d2fe7 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -76,3 +76,18 @@ mod use_ord { } fn main() {} + +mod issue15708 { + use std::cmp::{Ord, Ordering}; + + // Check that the lint posts on the type definition node + #[expect(clippy::derive_ord_xor_partial_ord)] + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} diff --git a/tests/ui/derived_hash_with_manual_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs index 88b574add3f27..9f5c85d7fbc38 100644 --- a/tests/ui/derived_hash_with_manual_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -41,3 +41,19 @@ impl std::hash::Hash for Bah { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::derived_hash_with_manual_eq)] + #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)] + pub struct Span { + start: usize, + end: usize, + } + + impl PartialEq for Span { + fn eq(&self, other: &Self) -> bool { + self.start.cmp(&other.start).then(self.end.cmp(&other.end)).is_eq() + } + } +} From 1bc19932e6f0a5ce098af6f3d59e28a61abbc631 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 15 Sep 2025 19:08:55 +0530 Subject: [PATCH 0684/1900] make cargo test work for bootstrap self test --- src/bootstrap/src/core/config/config.rs | 24 ++++----- src/bootstrap/src/core/config/tests.rs | 62 ++---------------------- src/bootstrap/src/utils/helpers/tests.rs | 4 +- 3 files changed, 17 insertions(+), 73 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2d5a1fd5332..19fcaeaaa24de 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -630,19 +630,9 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); - let out = flags_build_dir.or(build_build_dir.map(PathBuf::from)).unwrap_or_else(|| { - if cfg!(test) { - // Use the build directory of the original x.py invocation, so that we can set `initial_rustc` properly. - Path::new( - &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"), - ) - .parent() - .unwrap() - .to_path_buf() - } else { - PathBuf::from("build") - } - }); + let out = flags_build_dir + .or(build_build_dir.map(PathBuf::from)) + .unwrap_or_else(|| PathBuf::from("build")); // NOTE: Bootstrap spawns various commands with different working directories. // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. @@ -693,8 +683,14 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { + let out = if cfg!(test) { Path::new("../../build").to_path_buf() } else { out.clone() }; + download_beta_toolchain(&dwn_ctx, &out); - out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + + out.join(if cfg!(test) { get_host_target() } else { host_target }) + .join("stage0") + .join("bin") + .join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index e93525fbd09ce..46d05fbc06864 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -39,7 +39,11 @@ fn prepare_test_specific_dir() -> PathBuf { // Replace "::" with "_" to make it safe for directory names on Windows systems let test_path = current.name().unwrap().replace("::", "_"); - let testdir = parse("").tempdir().join(test_path); + let testdir = crate::utils::tests::TestCtx::new() + .config("check") + .create_config() + .tempdir() + .join(test_path); // clean up any old test files let _ = fs::remove_dir_all(&testdir); @@ -64,62 +68,6 @@ fn download_ci_llvm() { } } -// FIXME(onur-ozkan): extend scope of the test -// refs: -// - https://github.com/rust-lang/rust/issues/109120 -// - https://github.com/rust-lang/rust/pull/109162#issuecomment-1496782487 -#[test] -fn detect_src_and_out() { - fn test(cfg: Config, build_dir: Option<&str>) { - // This will bring absolute form of `src/bootstrap` path - let current_dir = std::env::current_dir().unwrap(); - - // get `src` by moving into project root path - let expected_src = current_dir.ancestors().nth(2).unwrap(); - assert_eq!(&cfg.src, expected_src); - - // Sanity check for `src` - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - let expected_src = manifest_dir.ancestors().nth(2).unwrap(); - assert_eq!(&cfg.src, expected_src); - - // test if build-dir was manually given in bootstrap.toml - if let Some(custom_build_dir) = build_dir { - assert_eq!(&cfg.out, Path::new(custom_build_dir)); - } - // test the native bootstrap way - else { - // This should bring output path of bootstrap in absolute form - let cargo_target_dir = env::var_os("CARGO_TARGET_DIR").expect( - "CARGO_TARGET_DIR must been provided for the test environment from bootstrap", - ); - - // Move to `build` from `build/bootstrap` - let expected_out = Path::new(&cargo_target_dir).parent().unwrap(); - assert_eq!(&cfg.out, expected_out); - - let args: Vec = env::args().collect(); - - // Another test for `out` as a sanity check - // - // This will bring something similar to: - // `{build-dir}/bootstrap/debug/deps/bootstrap-c7ee91d5661e2804` - // `{build-dir}` can be anywhere, not just in the rust project directory. - let dep = Path::new(args.first().unwrap()); - let expected_out = dep.ancestors().nth(5).unwrap(); - - assert_eq!(&cfg.out, expected_out); - } - } - - test(parse(""), None); - - { - let build_dir = if cfg!(windows) { "C:\\tmp" } else { "/tmp" }; - test(parse(&format!("build.build-dir = '{build_dir}'")), Some(build_dir)); - } -} - #[test] fn clap_verify() { Flags::command().debug_assert(); diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 9030ca2820a8b..e586fe77b4f62 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -6,6 +6,7 @@ use crate::utils::helpers::{ check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, symlink_dir, }; +use crate::utils::tests::TestCtx; use crate::{Config, Flags}; #[test] @@ -80,8 +81,7 @@ fn test_symlink_dir() { #[test] fn test_set_file_times_sanity_check() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + let config = TestCtx::new().config("check").create_config(); let tempfile = config.tempdir().join(".tmp-file"); { From a12969e0d10387ad17755879e98cf5097a937473 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 15 Sep 2025 19:20:05 +0530 Subject: [PATCH 0685/1900] walk up the ancestors --- src/bootstrap/src/core/config/config.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 19fcaeaaa24de..003cf3f2f9c3e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -683,14 +683,15 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { - let out = if cfg!(test) { Path::new("../../build").to_path_buf() } else { out.clone() }; - + let out = if cfg!(test) { + std::env::current_dir().unwrap().ancestors().nth(2).unwrap().join("build") + } else { + out.clone() + }; download_beta_toolchain(&dwn_ctx, &out); + let target = if cfg!(test) { get_host_target() } else { host_target }; - out.join(if cfg!(test) { get_host_target() } else { host_target }) - .join("stage0") - .join("bin") - .join(exe("rustc", host_target)) + out.join(target).join("stage0").join("bin").join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( From 6c79f547f9ecf72ff8b9748dc0001d92217dff71 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:16:29 +0530 Subject: [PATCH 0686/1900] add an API in ConfigBuilder to point to config file for toml parsing --- src/bootstrap/src/utils/tests/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 3332187e2a853..ff84bfdb4cde3 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -98,6 +98,14 @@ impl ConfigBuilder { self } + pub fn config_toml(mut self, config_toml: &str) -> Self { + let toml_path = self.directory.join("bootstrap.toml"); + std::fs::write(&toml_path, config_toml).unwrap(); + self.args.push("--config".to_string()); + self.args.push(toml_path.display().to_string()); + self + } + pub fn create_config(mut self) -> Config { // Run in dry-check, otherwise the test would be too slow self.args.push("--dry-run".to_string()); From d488d33fd6f7d4832cab12d0dece896bf1b975c0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:17:15 +0530 Subject: [PATCH 0687/1900] let verify method run in test settings --- src/bootstrap/src/core/download.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 2f3c80559c0ef..ce3f49a35b9e6 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -857,7 +857,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) - println!("verifying {}", path.display()); }); - if exec_ctx.dry_run() { + if exec_ctx.dry_run() && !cfg!(test) { return false; } From ce4604e34e6f29740b41306806c6bd7947e103a4 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:18:35 +0530 Subject: [PATCH 0688/1900] remove using default toml config for test in get_toml, we handle it via using the temp directory created via the testCtx --- src/bootstrap/src/core/config/toml/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index 7af22432ef8ca..f6dc5b67e1010 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,10 +152,6 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { - #[cfg(test)] - return Ok(TomlConfig::default()); - - #[cfg(not(test))] Self::get_toml_inner(file) } From 24ed1a0455ced62f48402f6740effbf818f45269 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:18:58 +0530 Subject: [PATCH 0689/1900] allow symlinking during test --- src/bootstrap/src/utils/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index e802c0214ddcb..0561f343a8c19 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -160,7 +160,7 @@ impl Drop for TimeIt { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { - if config.dry_run() { + if config.dry_run() && !cfg!(test) { return Ok(()); } let _ = fs::remove_dir_all(link); From 05131bd5f11812390018dddc29fb9a36c4657ed2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:19:29 +0530 Subject: [PATCH 0690/1900] move most of the test to new testCtx --- src/bootstrap/src/core/builder/tests.rs | 31 +-- src/bootstrap/src/core/config/tests.rs | 305 +++++++++++------------ src/bootstrap/src/utils/helpers/tests.rs | 7 +- 3 files changed, 152 insertions(+), 191 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 229adf714598b..a3cb4660ffc51 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -218,18 +218,16 @@ fn prepare_rustc_checkout(ctx: &mut GitCtx) { /// Parses a Config directory from `path`, with the given value of `download_rustc`. fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) -> Config { - Config::parse_inner( - Flags::parse(&[ - "build".to_owned(), - "--dry-run".to_owned(), - "--ci".to_owned(), - if ci { "true" } else { "false" }.to_owned(), - format!("--set=rust.download-rustc='{download_rustc}'"), - "--src".to_owned(), - path.to_str().unwrap().to_owned(), - ]), - |&_| Ok(Default::default()), - ) + TestCtx::new() + .config("build") + .args(&[ + "--ci", + if ci { "true" } else { "false" }, + format!("--set=rust.download-rustc='{download_rustc}'").as_str(), + "--src", + path.to_str().unwrap(), + ]) + .create_config_without_ci_llvm_override() } mod dist { @@ -359,14 +357,7 @@ fn test_test_coverage() { #[test] fn test_prebuilt_llvm_config_path_resolution() { fn configure(config: &str) -> Config { - Config::parse_inner( - Flags::parse(&[ - "build".to_string(), - "--dry-run".to_string(), - "--config=/does/not/exist".to_string(), - ]), - |&_| toml::from_str(&config), - ) + TestCtx::new().config("build").config_toml(config).create_config() } // Removes Windows disk prefix if present diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 46d05fbc06864..68859c6545e10 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -14,17 +14,15 @@ use super::toml::change_id::ChangeIdWrapper; use super::{Config, RUSTC_IF_UNCHANGED_ALLOWED_PATHS}; use crate::ChangeId; use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; -use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; +use crate::core::build_steps::{llvm, test}; use crate::core::config::toml::TomlConfig; use crate::core::config::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection}; +use crate::utils::tests::TestCtx; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { - Config::parse_inner( - Flags::parse(&["check".to_string(), "--config=/does/not/exist".to_string()]), - |&_| toml::from_str(&config), - ) + TestCtx::new().config("check").config_toml(config).create_config() } fn get_toml(file: &Path) -> Result { @@ -54,10 +52,14 @@ fn prepare_test_specific_dir() -> PathBuf { #[test] fn download_ci_llvm() { - let config = parse("llvm.download-ci-llvm = false"); + let config = TestCtx::new().config("check").create_config(); assert!(!config.llvm_from_ci); - let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); + // this doesn't make sense, as we are overriding it later. + let if_unchanged_config = TestCtx::new() + .config("check") + .config_toml("llvm.download-ci-llvm = \"if-unchanged\"") + .create_config(); if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); @@ -75,54 +77,51 @@ fn clap_verify() { #[test] fn override_toml() { - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=change-id=1".to_owned(), - "--set=rust.lto=fat".to_owned(), - "--set=rust.deny-warnings=false".to_owned(), - "--set=build.optimized-compiler-builtins=true".to_owned(), - "--set=build.gdb=\"bar\"".to_owned(), - "--set=build.tools=[\"cargo\"]".to_owned(), - "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), - "--set=target.x86_64-unknown-linux-gnu.runner=bar".to_owned(), - "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), - "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), - "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), - "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), - ]), - |&_| { - toml::from_str( - r#" -change-id = 0 -[rust] -lto = "off" -deny-warnings = true -download-rustc=false - -[build] -gdb = "foo" -tools = [] - -[llvm] -download-ci-llvm = false -build-config = {} - -[target.aarch64-unknown-linux-gnu] -sanitizers = true -rpath = true -runner = "aarch64-runner" - -[target.x86_64-unknown-linux-gnu] -sanitizers = true -rpath = true -runner = "x86_64-runner" - - "#, - ) - }, - ); + let config_toml: &str = r#" + change-id = 0 + + [rust] + lto = "off" + deny-warnings = true + download-rustc = false + + [build] + gdb = "foo" + tools = [] + + [llvm] + download-ci-llvm = false + build-config = {} + + [target.aarch64-unknown-linux-gnu] + sanitizers = true + rpath = true + runner = "aarch64-runner" + + [target.x86_64-unknown-linux-gnu] + sanitizers = true + rpath = true + runner = "x86_64-runner" + "#; + + let args = [ + "--set=change-id=1", + "--set=rust.lto=fat", + "--set=rust.deny-warnings=false", + "--set=build.optimized-compiler-builtins=true", + "--set=build.gdb=\"bar\"", + "--set=build.tools=[\"cargo\"]", + "--set=llvm.build-config={\"foo\" = \"bar\"}", + "--set=target.x86_64-unknown-linux-gnu.runner=bar", + "--set=target.x86_64-unknown-linux-gnu.rpath=false", + "--set=target.aarch64-unknown-linux-gnu.sanitizers=false", + "--set=target.aarch64-apple-darwin.runner=apple", + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false", + ]; + + let config = + TestCtx::new().config("check").config_toml(config_toml).args(&args).create_config(); + assert_eq!(config.change_id, Some(ChangeId::Id(1)), "setting top-level value"); assert_eq!( config.rust_lto, @@ -181,15 +180,14 @@ runner = "x86_64-runner" #[test] #[should_panic] fn override_toml_duplicate() { - Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_string(), - "--set=change-id=1".to_owned(), - "--set=change-id=2".to_owned(), - ]), - |&_| toml::from_str("change-id = 0"), - ); + TestCtx::new() + .config("check") + .config_toml("change-id = 0") + .arg("--set") + .arg("change-id=1") + .arg("--set") + .arg("change-id=2") + .create_config(); } #[test] @@ -225,12 +223,12 @@ fn rust_optimize() { #[test] #[should_panic] fn invalid_rust_optimize() { - parse("rust.optimize = \"a\""); + TestCtx::new().config("check").config_toml("rust.optimize = \"a\"").create_config(); } #[test] fn verify_file_integrity() { - let config = parse(""); + let config = TestCtx::new().config("check").create_config(); let tempfile = config.tempdir().join(".tmp-test-file"); File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); @@ -272,22 +270,23 @@ fn parse_change_id_with_unknown_field() { #[test] fn order_of_clippy_rules() { - let args = vec![ - "clippy".to_string(), - "--fix".to_string(), - "--allow-dirty".to_string(), - "--allow-staged".to_string(), - "-Aclippy:all".to_string(), - "-Wclippy::style".to_string(), - "-Aclippy::foo1".to_string(), - "-Aclippy::foo2".to_string(), + let args = [ + "clippy", + "--fix", + "--allow-dirty", + "--allow-staged", + "-Aclippy:all", + "-Wclippy::style", + "-Aclippy::foo1", + "-Aclippy::foo2", ]; - let config = Config::parse(Flags::parse(&args)); + let config = TestCtx::new().config(&args[0]).args(&args[1..]).create_config(); let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { let cfg = LintConfig { allow, deny, warn, forbid }; - get_clippy_rules_in_order(&args, &cfg) + let args_vec: Vec = args.iter().map(|s| s.to_string()).collect(); + get_clippy_rules_in_order(&args_vec, &cfg) } _ => panic!("invalid subcommand"), }; @@ -304,14 +303,14 @@ fn order_of_clippy_rules() { #[test] fn clippy_rule_separate_prefix() { - let args = - vec!["clippy".to_string(), "-A clippy:all".to_string(), "-W clippy::style".to_string()]; - let config = Config::parse(Flags::parse(&args)); + let args = ["clippy", "-A clippy:all", "-W clippy::style"]; + let config = TestCtx::new().config(&args[0]).args(&args[1..]).create_config(); let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { let cfg = LintConfig { allow, deny, warn, forbid }; - get_clippy_rules_in_order(&args, &cfg) + let args_vec: Vec = args.iter().map(|s| s.to_string()).collect(); + get_clippy_rules_in_order(&args_vec, &cfg) } _ => panic!("invalid subcommand"), }; @@ -331,7 +330,10 @@ fn verbose_tests_default_value() { #[test] fn parse_rust_std_features() { - let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]"); + let config = TestCtx::new() + .config("check") + .config_toml("rust.std-features = [\"panic-unwind\", \"backtrace\"]") + .create_config(); let expected_features: BTreeSet = ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); assert_eq!(config.rust_std_features, expected_features); @@ -339,7 +341,8 @@ fn parse_rust_std_features() { #[test] fn parse_rust_std_features_empty() { - let config = parse("rust.std-features = []"); + let config = + TestCtx::new().config("check").config_toml("rust.std-features = []").create_config(); let expected_features: BTreeSet = BTreeSet::new(); assert_eq!(config.rust_std_features, expected_features); } @@ -347,70 +350,58 @@ fn parse_rust_std_features_empty() { #[test] #[should_panic] fn parse_rust_std_features_invalid() { - parse("rust.std-features = \"backtrace\""); + TestCtx::new().config("check").config_toml("rust.std-features = \"backtrace\"").create_config(); } #[test] fn parse_jobs() { - assert_eq!(parse("build.jobs = 1").jobs, Some(1)); + assert_eq!( + TestCtx::new().config("check").config_toml("build.jobs = 1").create_config().jobs, + Some(1) + ); } #[test] fn jobs_precedence() { // `--jobs` should take precedence over using `--set build.jobs`. - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--jobs=67890".to_owned(), - "--set=build.jobs=12345".to_owned(), - ]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new() + .config("check") + .args(&["--jobs=67890", "--set=build.jobs=12345"]) + .create_config(); assert_eq!(config.jobs, Some(67890)); // `--set build.jobs` should take precedence over `bootstrap.toml`. - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=build.jobs=12345".to_owned(), - ]), - |&_| { - toml::from_str( - r#" - [build] - jobs = 67890 - "#, - ) - }, - ); + let config = TestCtx::new() + .config("check") + .args(&["--set=build.jobs=12345"]) + .config_toml( + r#" + [build] + jobs = 67890 + "#, + ) + .create_config(); + assert_eq!(config.jobs, Some(12345)); // `--jobs` > `--set build.jobs` > `bootstrap.toml` - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--jobs=123".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=build.jobs=456".to_owned(), - ]), - |&_| { - toml::from_str( - r#" - [build] - jobs = 789 - "#, - ) - }, - ); + let config = TestCtx::new() + .config("check") + .args(&["--jobs=123", "--set=build.jobs=456"]) + .config_toml( + r#" + [build] + jobs = 789 + "#, + ) + .create_config(); assert_eq!(config.jobs, Some(123)); } #[test] fn check_rustc_if_unchanged_paths() { - let config = parse(""); + let config = TestCtx::new().config("check").create_config(); let normalised_allowed_paths: Vec<_> = RUSTC_IF_UNCHANGED_ALLOWED_PATHS .iter() .map(|t| { @@ -425,59 +416,42 @@ fn check_rustc_if_unchanged_paths() { #[test] fn test_explicit_stage() { - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]), - |&_| { - toml::from_str( - r#" + let config = TestCtx::new() + .config("check") + .config_toml( + r#" [build] test-stage = 1 "#, - ) - }, - ); + ) + .create_config(); assert!(!config.explicit_stage_from_cli); assert!(config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--stage=2".to_owned(), - "--config=/does/not/exist".to_owned(), - ]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new().config("check").stage(2).create_config(); assert!(config.explicit_stage_from_cli); assert!(!config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--stage=2".to_owned(), - "--config=/does/not/exist".to_owned(), - ]), - |&_| { - toml::from_str( - r#" + let config = TestCtx::new() + .config("check") + .stage(2) + .config_toml( + r#" [build] test-stage = 1 "#, - ) - }, - ); + ) + .create_config(); assert!(config.explicit_stage_from_cli); assert!(config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new().config("check").create_config(); assert!(!config.explicit_stage_from_cli); assert!(!config.explicit_stage_from_config); @@ -487,7 +461,10 @@ fn test_explicit_stage() { #[test] fn test_exclude() { let exclude_path = "compiler"; - let config = parse(&format!("build.exclude=[\"{}\"]", exclude_path)); + let config = TestCtx::new() + .config("check") + .config_toml(&format!("build.exclude=[\"{}\"]", exclude_path)) + .create_config(); let first_excluded = config .skip @@ -501,17 +478,13 @@ fn test_exclude() { #[test] fn test_ci_flag() { - let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=false".into()]), |&_| { - toml::from_str("") - }); + let config = TestCtx::new().config("check").arg("--ci").arg("false").create_config(); assert!(!config.is_running_on_ci); - let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=true".into()]), |&_| { - toml::from_str("") - }); + let config = TestCtx::new().config("check").arg("--ci").arg("true").create_config(); assert!(config.is_running_on_ci); - let config = Config::parse_inner(Flags::parse(&["check".into()]), |&_| toml::from_str("")); + let config = TestCtx::new().config("check").create_config(); assert_eq!(config.is_running_on_ci, CiEnv::is_ci()); } diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index e586fe77b4f62..ec99743d1ed88 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -60,8 +60,7 @@ fn test_check_cfg_arg() { #[test] fn test_symlink_dir() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + let config = TestCtx::new().config("check").create_config(); let tempdir = config.tempdir().join(".tmp-dir"); let link_path = config.tempdir().join(".tmp-link"); @@ -102,9 +101,7 @@ fn test_set_file_times_sanity_check() { #[test] fn test_submodule_path_of() { - let config = Config::parse_inner(Flags::parse(&["build".into(), "--dry-run".into()]), |&_| { - Ok(Default::default()) - }); + let config = TestCtx::new().config("build").create_config(); let build = crate::Build::new(config.clone()); let builder = crate::core::builder::Builder::new(&build); From a29474d3ff06660071ebb5f071a7cdce0ee07646 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:20:14 +0530 Subject: [PATCH 0691/1900] this is dicy, whether we have a method to explicitly enable_llvm_override --- src/bootstrap/src/utils/tests/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index ff84bfdb4cde3..d47a224d7af51 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -125,4 +125,19 @@ impl ConfigBuilder { Config::parse(Flags::parse(&self.args)) } + + pub fn create_config_without_ci_llvm_override(mut self) -> Config { + // Run in dry-check, otherwise the test would be too slow + self.args.push("--dry-run".to_string()); + + // Ignore submodules + self.args.push("--set".to_string()); + self.args.push("build.submodules=false".to_string()); + + // Do not mess with the local rustc checkout build directory + self.args.push("--build-dir".to_string()); + self.args.push(self.directory.join("build").display().to_string()); + + Config::parse(Flags::parse(&self.args)) + } } From 0c68c82957e86c15d4a543c0d32f9e101ce74ba9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 18:16:47 +0530 Subject: [PATCH 0692/1900] rename config_toml to with_default_toml_config --- src/bootstrap/src/core/builder/tests.rs | 2 +- src/bootstrap/src/core/config/tests.rs | 47 ++++++++++++++++--------- src/bootstrap/src/utils/tests/mod.rs | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index a3cb4660ffc51..7033098b79bff 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -357,7 +357,7 @@ fn test_test_coverage() { #[test] fn test_prebuilt_llvm_config_path_resolution() { fn configure(config: &str) -> Config { - TestCtx::new().config("build").config_toml(config).create_config() + TestCtx::new().config("build").with_default_toml_config(config).create_config() } // Removes Windows disk prefix if present diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 68859c6545e10..30b4c6cfc4317 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -22,7 +22,7 @@ use crate::utils::tests::TestCtx; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { - TestCtx::new().config("check").config_toml(config).create_config() + TestCtx::new().config("check").with_default_toml_config(config).create_config() } fn get_toml(file: &Path) -> Result { @@ -58,7 +58,7 @@ fn download_ci_llvm() { // this doesn't make sense, as we are overriding it later. let if_unchanged_config = TestCtx::new() .config("check") - .config_toml("llvm.download-ci-llvm = \"if-unchanged\"") + .with_default_toml_config("llvm.download-ci-llvm = \"if-unchanged\"") .create_config(); if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); @@ -119,8 +119,11 @@ fn override_toml() { "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false", ]; - let config = - TestCtx::new().config("check").config_toml(config_toml).args(&args).create_config(); + let config = TestCtx::new() + .config("check") + .with_default_toml_config(config_toml) + .args(&args) + .create_config(); assert_eq!(config.change_id, Some(ChangeId::Id(1)), "setting top-level value"); assert_eq!( @@ -182,7 +185,7 @@ fn override_toml() { fn override_toml_duplicate() { TestCtx::new() .config("check") - .config_toml("change-id = 0") + .with_default_toml_config("change-id = 0") .arg("--set") .arg("change-id=1") .arg("--set") @@ -223,7 +226,10 @@ fn rust_optimize() { #[test] #[should_panic] fn invalid_rust_optimize() { - TestCtx::new().config("check").config_toml("rust.optimize = \"a\"").create_config(); + TestCtx::new() + .config("check") + .with_default_toml_config("rust.optimize = \"a\"") + .create_config(); } #[test] @@ -332,7 +338,7 @@ fn verbose_tests_default_value() { fn parse_rust_std_features() { let config = TestCtx::new() .config("check") - .config_toml("rust.std-features = [\"panic-unwind\", \"backtrace\"]") + .with_default_toml_config("rust.std-features = [\"panic-unwind\", \"backtrace\"]") .create_config(); let expected_features: BTreeSet = ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); @@ -341,8 +347,10 @@ fn parse_rust_std_features() { #[test] fn parse_rust_std_features_empty() { - let config = - TestCtx::new().config("check").config_toml("rust.std-features = []").create_config(); + let config = TestCtx::new() + .config("check") + .with_default_toml_config("rust.std-features = []") + .create_config(); let expected_features: BTreeSet = BTreeSet::new(); assert_eq!(config.rust_std_features, expected_features); } @@ -350,13 +358,20 @@ fn parse_rust_std_features_empty() { #[test] #[should_panic] fn parse_rust_std_features_invalid() { - TestCtx::new().config("check").config_toml("rust.std-features = \"backtrace\"").create_config(); + TestCtx::new() + .config("check") + .with_default_toml_config("rust.std-features = \"backtrace\"") + .create_config(); } #[test] fn parse_jobs() { assert_eq!( - TestCtx::new().config("check").config_toml("build.jobs = 1").create_config().jobs, + TestCtx::new() + .config("check") + .with_default_toml_config("build.jobs = 1") + .create_config() + .jobs, Some(1) ); } @@ -375,7 +390,7 @@ fn jobs_precedence() { let config = TestCtx::new() .config("check") .args(&["--set=build.jobs=12345"]) - .config_toml( + .with_default_toml_config( r#" [build] jobs = 67890 @@ -389,7 +404,7 @@ fn jobs_precedence() { let config = TestCtx::new() .config("check") .args(&["--jobs=123", "--set=build.jobs=456"]) - .config_toml( + .with_default_toml_config( r#" [build] jobs = 789 @@ -418,7 +433,7 @@ fn check_rustc_if_unchanged_paths() { fn test_explicit_stage() { let config = TestCtx::new() .config("check") - .config_toml( + .with_default_toml_config( r#" [build] test-stage = 1 @@ -439,7 +454,7 @@ fn test_explicit_stage() { let config = TestCtx::new() .config("check") .stage(2) - .config_toml( + .with_default_toml_config( r#" [build] test-stage = 1 @@ -463,7 +478,7 @@ fn test_exclude() { let exclude_path = "compiler"; let config = TestCtx::new() .config("check") - .config_toml(&format!("build.exclude=[\"{}\"]", exclude_path)) + .with_default_toml_config(&format!("build.exclude=[\"{}\"]", exclude_path)) .create_config(); let first_excluded = config diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index d47a224d7af51..80df15939b057 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -98,7 +98,7 @@ impl ConfigBuilder { self } - pub fn config_toml(mut self, config_toml: &str) -> Self { + pub fn with_default_toml_config(mut self, config_toml: &str) -> Self { let toml_path = self.directory.join("bootstrap.toml"); std::fs::write(&toml_path, config_toml).unwrap(); self.args.push("--config".to_string()); From 9189bf79d43dd162858bc58d64c6c99da7ca0add Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 18:32:20 +0530 Subject: [PATCH 0693/1900] remove create_config_without_ci_llvm_override duplication --- src/bootstrap/src/core/builder/tests.rs | 3 +- src/bootstrap/src/utils/tests/mod.rs | 37 ++++++++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 7033098b79bff..0bb22eccd1936 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -227,7 +227,8 @@ fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) - "--src", path.to_str().unwrap(), ]) - .create_config_without_ci_llvm_override() + .no_override_download_ci_llvm() + .create_config() } mod dist { diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 80df15939b057..0ccc8e1ebb8f4 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -48,11 +48,16 @@ impl TestCtx { pub struct ConfigBuilder { args: Vec, directory: PathBuf, + override_download_ci_llvm: bool, } impl ConfigBuilder { fn from_args(args: &[&str], directory: PathBuf) -> Self { - Self { args: args.iter().copied().map(String::from).collect(), directory } + Self { + args: args.iter().copied().map(String::from).collect(), + directory, + override_download_ci_llvm: true, + } } pub fn path(mut self, path: &str) -> Self { @@ -106,27 +111,12 @@ impl ConfigBuilder { self } - pub fn create_config(mut self) -> Config { - // Run in dry-check, otherwise the test would be too slow - self.args.push("--dry-run".to_string()); - - // Ignore submodules - self.args.push("--set".to_string()); - self.args.push("build.submodules=false".to_string()); - - // Override any external LLVM set and inhibit CI LLVM; pretend that we're always building - // in-tree LLVM from sources. - self.args.push("--set".to_string()); - self.args.push("llvm.download-ci-llvm=false".to_string()); - - // Do not mess with the local rustc checkout build directory - self.args.push("--build-dir".to_string()); - self.args.push(self.directory.join("build").display().to_string()); - - Config::parse(Flags::parse(&self.args)) + pub fn no_override_download_ci_llvm(mut self) -> Self { + self.override_download_ci_llvm = false; + self } - pub fn create_config_without_ci_llvm_override(mut self) -> Config { + pub fn create_config(mut self) -> Config { // Run in dry-check, otherwise the test would be too slow self.args.push("--dry-run".to_string()); @@ -134,6 +124,13 @@ impl ConfigBuilder { self.args.push("--set".to_string()); self.args.push("build.submodules=false".to_string()); + if self.override_download_ci_llvm { + // Override any external LLVM set and inhibit CI LLVM; pretend that we're always building + // in-tree LLVM from sources. + self.args.push("--set".to_string()); + self.args.push("llvm.download-ci-llvm=false".to_string()); + } + // Do not mess with the local rustc checkout build directory self.args.push("--build-dir".to_string()); self.args.push(self.directory.join("build").display().to_string()); From 671aabd4eb1a56a09b989abc021bee32750ee59b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 19:34:49 +0530 Subject: [PATCH 0694/1900] add dry_run flag in config builder and remove runtime test hacks --- src/bootstrap/src/core/config/tests.rs | 2 +- src/bootstrap/src/core/download.rs | 2 +- src/bootstrap/src/utils/helpers.rs | 2 +- src/bootstrap/src/utils/helpers/tests.rs | 2 +- src/bootstrap/src/utils/tests/mod.rs | 14 +++++++++++--- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 30b4c6cfc4317..a7429e07d858c 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -234,7 +234,7 @@ fn invalid_rust_optimize() { #[test] fn verify_file_integrity() { - let config = TestCtx::new().config("check").create_config(); + let config = TestCtx::new().config("check").no_dry_run().create_config(); let tempfile = config.tempdir().join(".tmp-test-file"); File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index ce3f49a35b9e6..2f3c80559c0ef 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -857,7 +857,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) - println!("verifying {}", path.display()); }); - if exec_ctx.dry_run() && !cfg!(test) { + if exec_ctx.dry_run() { return false; } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 0561f343a8c19..e802c0214ddcb 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -160,7 +160,7 @@ impl Drop for TimeIt { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { - if config.dry_run() && !cfg!(test) { + if config.dry_run() { return Ok(()); } let _ = fs::remove_dir_all(link); diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index ec99743d1ed88..676fe6cbd5fe5 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -60,7 +60,7 @@ fn test_check_cfg_arg() { #[test] fn test_symlink_dir() { - let config = TestCtx::new().config("check").create_config(); + let config = TestCtx::new().config("check").no_dry_run().create_config(); let tempdir = config.tempdir().join(".tmp-dir"); let link_path = config.tempdir().join(".tmp-link"); diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 0ccc8e1ebb8f4..dc905969dcac1 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -49,6 +49,7 @@ pub struct ConfigBuilder { args: Vec, directory: PathBuf, override_download_ci_llvm: bool, + dry_run: bool, } impl ConfigBuilder { @@ -57,6 +58,7 @@ impl ConfigBuilder { args: args.iter().copied().map(String::from).collect(), directory, override_download_ci_llvm: true, + dry_run: true, } } @@ -116,10 +118,16 @@ impl ConfigBuilder { self } - pub fn create_config(mut self) -> Config { - // Run in dry-check, otherwise the test would be too slow - self.args.push("--dry-run".to_string()); + pub fn no_dry_run(mut self) -> Self { + self.dry_run = false; + self + } + pub fn create_config(mut self) -> Config { + if self.dry_run { + // Run in dry-check, otherwise the test would be too slow + self.args.push("--dry-run".to_string()); + } // Ignore submodules self.args.push("--set".to_string()); self.args.push("build.submodules=false".to_string()); From afe380dd58170b55b100a659f05017b1149d0ec9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 19:43:43 +0530 Subject: [PATCH 0695/1900] initialize out with CARGO_TARGET_DIR and then go for manifest and then for current --- src/bootstrap/src/core/config/config.rs | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 003cf3f2f9c3e..1f3b90d324fb1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -630,9 +630,13 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); - let out = flags_build_dir - .or(build_build_dir.map(PathBuf::from)) - .unwrap_or_else(|| PathBuf::from("build")); + let out = if cfg!(test) { + test_build_dir() + } else { + flags_build_dir + .or_else(|| build_build_dir.map(PathBuf::from)) + .unwrap_or_else(|| PathBuf::from("build")) + }; // NOTE: Bootstrap spawns various commands with different working directories. // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. @@ -683,11 +687,6 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { - let out = if cfg!(test) { - std::env::current_dir().unwrap().ancestors().nth(2).unwrap().join("build") - } else { - out.clone() - }; download_beta_toolchain(&dwn_ctx, &out); let target = if cfg!(test) { get_host_target() } else { host_target }; @@ -2481,3 +2480,15 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { }) .collect() } + +fn test_build_dir() -> PathBuf { + env::var_os("CARGO_TARGET_DIR") + .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) + .unwrap_or_else(|| { + let base = option_env!("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .unwrap_or_else(|| std::env::current_dir().expect("failed to get current dir")); + + base.ancestors().nth(2).unwrap_or_else(|| Path::new(".")).join("build") + }) +} From a48cd767f634ec7461b8367e5d301a2985cfce7d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 20:03:15 +0530 Subject: [PATCH 0696/1900] add explicit config assignment when running the test, as the src is assigned to CARGO_MANIFEST_DIR, so the config actually use the /bootstrap.toml and the /tmp/bootstrap.toml --- src/bootstrap/src/utils/tests/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index dc905969dcac1..764b89086cf2f 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -50,6 +50,7 @@ pub struct ConfigBuilder { directory: PathBuf, override_download_ci_llvm: bool, dry_run: bool, + explicit_config: bool, } impl ConfigBuilder { @@ -59,6 +60,7 @@ impl ConfigBuilder { directory, override_download_ci_llvm: true, dry_run: true, + explicit_config: true, } } @@ -108,6 +110,7 @@ impl ConfigBuilder { pub fn with_default_toml_config(mut self, config_toml: &str) -> Self { let toml_path = self.directory.join("bootstrap.toml"); std::fs::write(&toml_path, config_toml).unwrap(); + self.explicit_config = false; self.args.push("--config".to_string()); self.args.push(toml_path.display().to_string()); self @@ -139,6 +142,12 @@ impl ConfigBuilder { self.args.push("llvm.download-ci-llvm=false".to_string()); } + // always use the bootstrap toml created in the + // temporary directory and not from the + if self.explicit_config { + self = self.with_default_toml_config(""); + } + // Do not mess with the local rustc checkout build directory self.args.push("--build-dir".to_string()); self.args.push(self.directory.join("build").display().to_string()); From 33e262e8cc5ec07e0d3312be2d2c2ae3404f5fbd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 20:52:26 +0530 Subject: [PATCH 0697/1900] remove prepare_test_specific_dir and update tests accordingly --- src/bootstrap/src/core/config/tests.rs | 154 +++++++++++-------------- 1 file changed, 65 insertions(+), 89 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index a7429e07d858c..f277e3704f277 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -30,26 +30,6 @@ fn get_toml(file: &Path) -> Result { toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) } -/// Helps with debugging by using consistent test-specific directories instead of -/// random temporary directories. -fn prepare_test_specific_dir() -> PathBuf { - let current = std::thread::current(); - // Replace "::" with "_" to make it safe for directory names on Windows systems - let test_path = current.name().unwrap().replace("::", "_"); - - let testdir = crate::utils::tests::TestCtx::new() - .config("check") - .create_config() - .tempdir() - .join(test_path); - - // clean up any old test files - let _ = fs::remove_dir_all(&testdir); - let _ = fs::create_dir_all(&testdir); - - testdir -} - #[test] fn download_ci_llvm() { let config = TestCtx::new().config("check").create_config(); @@ -505,16 +485,8 @@ fn test_ci_flag() { #[test] fn test_precedence_of_includes() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - - [llvm] - link-jobs = 2 - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -535,10 +507,17 @@ fn test_precedence_of_includes() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + let config = test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + + [llvm] + link-jobs = 2 + "#, + ) + .create_config(); assert_eq!(config.change_id.unwrap(), ChangeId::Id(543)); assert_eq!(config.llvm_link_jobs.unwrap(), 2); @@ -548,36 +527,29 @@ fn test_precedence_of_includes() { #[test] #[should_panic(expected = "Cyclic inclusion detected")] fn test_cyclic_include_direct() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" - include = ["./config.toml"] + include = ["./bootstrap.toml"] "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + "#, + ) + .create_config(); } #[test] #[should_panic(expected = "Cyclic inclusion detected")] fn test_cyclic_include_indirect() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -597,43 +569,37 @@ fn test_cyclic_include_indirect() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + "#, + ) + .create_config(); } #[test] fn test_include_absolute_paths() { - let testdir = prepare_test_specific_dir(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); File::create(&extension).unwrap().write_all(&[]).unwrap(); - let root_config = testdir.join("config.toml"); let extension_absolute_path = extension.canonicalize().unwrap().to_str().unwrap().replace('\\', r"\\"); let root_config_content = format!(r#"include = ["{}"]"#, extension_absolute_path); - File::create(&root_config).unwrap().write_all(root_config_content.as_bytes()).unwrap(); - - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx.config("check").with_default_toml_config(&root_config_content).create_config(); } #[test] fn test_include_relative_paths() { - let testdir = prepare_test_specific_dir(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let _ = fs::create_dir_all(&testdir.join("subdir/another_subdir")); - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./subdir/extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - let extension = testdir.join("subdir/extension.toml"); let extension_content = br#" include = ["../extension2.toml"] @@ -655,22 +621,20 @@ fn test_include_relative_paths() { let extension = testdir.join("extension4.toml"); File::create(extension).unwrap().write_all(&[]).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./subdir/extension.toml"] + "#, + ) + .create_config(); } #[test] fn test_include_precedence_over_profile() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - profile = "dist" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -679,10 +643,22 @@ fn test_include_precedence_over_profile() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + profile = "dist" + include = ["./extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let config = test_ctx + .config("check") + .with_default_toml_config( + r#" + profile = "dist" + include = ["./extension.toml"] + "#, + ) + .create_config(); // "dist" profile would normally set the channel to "auto-detect", but includes should // override profile settings, so we expect this to be "dev" here. From d887c570a0688bed2c5c8995f35cfdad5980cc54 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sun, 21 Sep 2025 09:30:18 -0700 Subject: [PATCH 0698/1900] Correct a misspelling of RUSTC_LOG --- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 5e5b81fc65b22..a7cdab73e7973 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -109,7 +109,7 @@ Miri, use `MIRI_LOG` instead. You get the idea :) See the [`tracing`] crate's docs, and specifically the docs for [`debug!`] to see the full syntax you can use. (Note: unlike the compiler, the [`tracing`] -crate and its examples use the `RUST_LOG` environment variable. rustc, rustdoc, +crate and its examples use the `RUSTC_LOG` environment variable. rustc, rustdoc, and other tools set custom environment variables.) **Note that unless you use a very strict filter, the logger will emit a lot of From e99e226748d3438d40a11ff556820375a762c8e1 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 21 Sep 2025 23:20:20 +0800 Subject: [PATCH 0699/1900] Switch `dummy_bang` from `LegacyBang` to `Bang` --- compiler/rustc_expand/src/base.rs | 23 ++++++++++------------- compiler/rustc_expand/src/expand.rs | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 88f88f30a8c69..7db1be24f2418 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -324,16 +324,16 @@ pub trait BangProcMacro { impl BangProcMacro for F where - F: Fn(TokenStream) -> TokenStream, + F: Fn(&mut ExtCtxt<'_>, Span, TokenStream) -> Result, { fn expand<'cx>( &self, - _ecx: &'cx mut ExtCtxt<'_>, - _span: Span, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, ts: TokenStream, ) -> Result { // FIXME setup implicit context in TLS before calling self. - Ok(self(ts)) + self(ecx, span, ts) } } @@ -999,17 +999,14 @@ impl SyntaxExtension { /// A dummy bang macro `foo!()`. pub fn dummy_bang(edition: Edition) -> SyntaxExtension { - fn expander<'cx>( - cx: &'cx mut ExtCtxt<'_>, + fn expand( + ecx: &mut ExtCtxt<'_>, span: Span, - _: TokenStream, - ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(DummyResult::any( - span, - cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"), - )) + _ts: TokenStream, + ) -> Result { + Err(ecx.dcx().span_delayed_bug(span, "expanded a dummy bang macro")) } - SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Arc::new(expander)), edition) + SyntaxExtension::default(SyntaxExtensionKind::Bang(Arc::new(expand)), edition) } /// A dummy derive macro `#[derive(Foo)]`. diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 4c0e0bbfe26d1..172bc3d1d9f86 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -971,7 +971,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }, - SyntaxExtensionKind::LegacyBang(..) => { + SyntaxExtensionKind::Bang(..) => { let msg = "expanded a dummy glob delegation"; let guar = self.cx.dcx().span_delayed_bug(span, msg); return ExpandResult::Ready(fragment_kind.dummy(span, guar)); From a1646bf90c12c168a898361d2ef34e73311eccde Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 21 Sep 2025 23:28:10 +0800 Subject: [PATCH 0700/1900] mbe: Switch dummy extension used for errors from `LegacyBang` to `Bang` --- compiler/rustc_expand/src/mbe/macro_rules.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1d147a0385c68..9ed4c56efb3ea 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -33,8 +33,8 @@ use super::diagnostics::{FailedMacro, failed_to_match_macro}; use super::macro_parser::{NamedMatches, NamedParseResult}; use super::{SequenceRepetition, diagnostics}; use crate::base::{ - AttrProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult, - SyntaxExtension, SyntaxExtensionKind, TTMacroExpander, + AttrProcMacro, BangProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, + MacroExpanderResult, SyntaxExtension, SyntaxExtensionKind, TTMacroExpander, }; use crate::errors; use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment}; @@ -267,16 +267,16 @@ impl AttrProcMacro for MacroRulesMacroExpander { } } -struct DummyExpander(ErrorGuaranteed); +struct DummyBang(ErrorGuaranteed); -impl TTMacroExpander for DummyExpander { +impl BangProcMacro for DummyBang { fn expand<'cx>( &self, _: &'cx mut ExtCtxt<'_>, - span: Span, + _: Span, _: TokenStream, - ) -> ExpandResult, ()> { - ExpandResult::Ready(DummyResult::any(span, self.0)) + ) -> Result { + Err(self.0) } } @@ -664,7 +664,7 @@ pub fn compile_declarative_macro( SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local) }; let dummy_syn_ext = - |guar| (mk_syn_ext(SyntaxExtensionKind::LegacyBang(Arc::new(DummyExpander(guar)))), 0); + |guar| (mk_syn_ext(SyntaxExtensionKind::Bang(Arc::new(DummyBang(guar)))), 0); let macro_rules = macro_def.macro_rules; let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) }; From 888679013d1f424adef06267f3630069b4cabd40 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 7 Sep 2025 12:31:35 -0400 Subject: [PATCH 0701/1900] Add panic=immediate-abort --- .../rustc_builtin_macros/src/test_harness.rs | 10 +-- compiler/rustc_codegen_gcc/src/base.rs | 4 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 3 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 2 +- compiler/rustc_codegen_ssa/src/back/link.rs | 10 +-- compiler/rustc_interface/messages.ftl | 2 +- compiler/rustc_interface/src/passes.rs | 3 +- compiler/rustc_metadata/messages.ftl | 7 ++ compiler/rustc_metadata/src/creader.rs | 4 + .../rustc_metadata/src/dependency_format.rs | 47 ++++++++--- compiler/rustc_metadata/src/errors.rs | 10 +++ .../rustc_middle/src/middle/lang_items.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 8 +- compiler/rustc_mir_transform/src/coroutine.rs | 3 +- .../src/ffi_unwind_calls.rs | 9 +- .../src/remove_noop_landing_pads.rs | 3 +- compiler/rustc_session/src/config.rs | 9 +- compiler/rustc_session/src/config/cfg.rs | 7 +- compiler/rustc_session/src/options.rs | 4 +- compiler/rustc_session/src/session.rs | 10 ++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 6 ++ library/alloc/Cargo.toml | 2 - library/alloc/src/alloc.rs | 4 +- library/alloc/src/raw_vec/mod.rs | 2 +- library/alloc/src/vec/mod.rs | 8 +- library/core/Cargo.toml | 2 +- library/core/src/cell.rs | 4 +- library/core/src/num/mod.rs | 4 +- library/core/src/option.rs | 8 +- library/core/src/panicking.rs | 83 ++++++++++--------- library/core/src/result.rs | 4 +- library/core/src/slice/index.rs | 4 +- library/core/src/slice/mod.rs | 4 +- .../core/src/slice/sort/shared/smallsort.rs | 4 +- library/core/src/str/mod.rs | 4 +- library/std/Cargo.toml | 5 -- library/std/src/panicking.rs | 26 +++--- library/std/src/rt.rs | 4 +- library/std/src/thread/local.rs | 2 +- library/sysroot/Cargo.toml | 1 - src/doc/rustc/src/codegen-options/index.md | 2 + src/tools/compiletest/src/directives.rs | 19 +++++ .../src/directives/directive_names.rs | 1 + src/tools/compiletest/src/runtest.rs | 22 +++-- .../panic-immediate-abort-codegen/Cargo.toml | 12 +++ .../panic-immediate-abort-codegen/lib.rs | 65 +++++++++++++++ .../panic-immediate-abort-codegen/rmake.rs | 41 +++++++++ .../hello/Cargo.toml | 4 + .../hello/src/main.rs | 1 + .../panic-immediate-abort-works/rmake.rs | 28 +++++++ .../report-in-external-macros.cargo.stderr | 2 +- .../report-in-external-macros.rustc.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 2 +- .../ui/panic-runtime/auxiliary/needs-abort.rs | 2 + .../auxiliary/needs-immediate-abort.rs | 7 ++ .../auxiliary/needs-unwind-immediate-abort.rs | 18 ++++ tests/ui/panic-runtime/bad-panic-flag1.rs | 2 +- tests/ui/panic-runtime/bad-panic-flag1.stderr | 2 +- tests/ui/panic-runtime/bad-panic-flag2.rs | 2 +- tests/ui/panic-runtime/bad-panic-flag2.stderr | 2 +- .../immediate-abort-default-sysroot.rs | 15 ++++ .../immediate-abort-default-sysroot.stderr | 4 + .../need-abort-got-immediate-abort.rs | 21 +++++ .../need-abort-got-immediate-abort.stderr | 4 + .../need-immediate-abort-got-abort.rs | 20 +++++ .../need-immediate-abort-got-abort.stderr | 4 + .../need-immediate-abort-got-unwind.rs | 20 +++++ .../need-immediate-abort-got-unwind.stderr | 4 + .../need-unwind-got-immediate-abort.rs | 21 +++++ .../need-unwind-got-immediate-abort.stderr | 4 + tests/ui/proc-macro/panic-abort.rs | 2 +- tests/ui/proc-macro/panic-abort.stderr | 2 +- 74 files changed, 541 insertions(+), 157 deletions(-) create mode 100644 tests/run-make-cargo/panic-immediate-abort-codegen/Cargo.toml create mode 100644 tests/run-make-cargo/panic-immediate-abort-codegen/lib.rs create mode 100644 tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs create mode 100644 tests/run-make-cargo/panic-immediate-abort-works/hello/Cargo.toml create mode 100644 tests/run-make-cargo/panic-immediate-abort-works/hello/src/main.rs create mode 100644 tests/run-make-cargo/panic-immediate-abort-works/rmake.rs create mode 100644 tests/ui/panic-runtime/auxiliary/needs-immediate-abort.rs create mode 100644 tests/ui/panic-runtime/auxiliary/needs-unwind-immediate-abort.rs create mode 100644 tests/ui/panic-runtime/immediate-abort-default-sysroot.rs create mode 100644 tests/ui/panic-runtime/immediate-abort-default-sysroot.stderr create mode 100644 tests/ui/panic-runtime/need-abort-got-immediate-abort.rs create mode 100644 tests/ui/panic-runtime/need-abort-got-immediate-abort.stderr create mode 100644 tests/ui/panic-runtime/need-immediate-abort-got-abort.rs create mode 100644 tests/ui/panic-runtime/need-immediate-abort-got-abort.stderr create mode 100644 tests/ui/panic-runtime/need-immediate-abort-got-unwind.rs create mode 100644 tests/ui/panic-runtime/need-immediate-abort-got-unwind.stderr create mode 100644 tests/ui/panic-runtime/need-unwind-got-immediate-abort.rs create mode 100644 tests/ui/panic-runtime/need-unwind-got-immediate-abort.stderr diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 51089e5a1d3f1..82c59d5a3a20b 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -63,8 +63,8 @@ pub fn inject( if sess.is_test_crate() { let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) { - (PanicStrategy::Abort, true) => PanicStrategy::Abort, - (PanicStrategy::Abort, false) => { + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, true) => panic_strategy, + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, false) => { if panic_strategy == platform_panic_strategy { // Silently allow compiling with panic=abort on these platforms, // but with old behavior (abort if a test fails). @@ -287,10 +287,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let ecx = &cx.ext_cx; let test_ident = Ident::new(sym::test, sp); - let runner_name = match cx.panic_strategy { - PanicStrategy::Unwind => "test_main_static", - PanicStrategy::Abort => "test_main_static_abort", - }; + let runner_name = + if cx.panic_strategy.unwinds() { "test_main_static" } else { "test_main_static_abort" }; // test::test_main_static(...) let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index e9d72e457a086..e8672f49580b6 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -15,9 +15,9 @@ use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; use rustc_span::Symbol; +use rustc_target::spec::RelocModel; #[cfg(feature = "master")] use rustc_target::spec::SymbolVisibility; -use rustc_target::spec::{PanicStrategy, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -101,7 +101,7 @@ pub fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let context = new_context(tcx); - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if tcx.sess.panic_strategy().unwinds() { context.add_command_line_option("-fexceptions"); context.add_driver_option("-fexceptions"); } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 84fa56cf90308..a915f5d64188f 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -29,7 +29,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; -use rustc_target::spec::PanicStrategy; #[cfg(feature = "master")] use crate::abi::FnAbiGccExt; @@ -1334,7 +1333,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( _catch_func: RValue<'gcc>, dest: PlaceRef<'tcx, RValue<'gcc>>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e7f4a3570488e..50398a32142eb 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,6 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -674,7 +673,7 @@ fn catch_unwind_intrinsic<'ll, 'tcx>( catch_func: &'ll Value, dest: PlaceRef<'tcx, &'ll Value>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 45c5c9aa5514d..3b920168e06dc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -106,7 +106,7 @@ unsafe fn configure_llvm(sess: &Session) { if sess.target.os == "emscripten" && !sess.opts.unstable_opts.emscripten_wasm_eh - && sess.panic_strategy() == PanicStrategy::Unwind + && sess.panic_strategy().unwinds() { add("-enable-emscripten-cxx-exceptions", false); } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 48b01ea2df197..327f001e1c85a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -47,8 +47,8 @@ use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, - LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, - SanitizerSet, SplitDebuginfo, + LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet, + SplitDebuginfo, }; use tracing::{debug, info, warn}; @@ -2512,10 +2512,10 @@ fn add_order_independent_options( if sess.target.os == "emscripten" { cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh { "-fwasm-exceptions" - } else if sess.panic_strategy() == PanicStrategy::Abort { - "-sDISABLE_EXCEPTION_CATCHING=1" - } else { + } else if sess.panic_strategy().unwinds() { "-sDISABLE_EXCEPTION_CATCHING=0" + } else { + "-sDISABLE_EXCEPTION_CATCHING=1" }); } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 6f6666f8c76fa..1f5c5e74d97ae 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -47,7 +47,7 @@ interface_out_dir_error = failed to find or create the directory specified by `--out-dir` interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic + building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6cefe8875306a..761a5c8091822 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -42,7 +42,6 @@ use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::{solve, traits}; use tracing::{info, instrument}; @@ -282,7 +281,7 @@ fn configure_and_expand( feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit(); } - if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { + if is_proc_macro_crate && !sess.panic_strategy().unwinds() { sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index e104be2c46637..e624bfc5b8bda 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -98,6 +98,13 @@ metadata_full_metadata_not_found = metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait +metadata_incompatible_with_immediate_abort = + the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` + +metadata_incompatible_with_immediate_abort_core = + the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + .help = consider building the standard library from source with `cargo build -Zbuild-std` + metadata_incompatible_panic_in_drop_strategy = the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 9e23da88f5e1e..7650acbd292d2 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -1027,6 +1027,10 @@ impl CStore { let name = match desired_strategy { PanicStrategy::Unwind => sym::panic_unwind, PanicStrategy::Abort => sym::panic_abort, + PanicStrategy::ImmediateAbort => { + // Immediate-aborting panics don't use a runtime. + return; + } }; info!("panic runtime not found -- loading {}", name); diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index fb9c2e23b715b..8054a48d37af1 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -61,11 +61,13 @@ use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; use rustc_span::sym; +use rustc_target::spec::PanicStrategy; use tracing::info; use crate::creader::CStore; use crate::errors::{ - BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired, + BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, + IncompatibleWithImmediateAbort, IncompatibleWithImmediateAbortCore, LibRequired, NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired, TwoPanicRuntimes, }; @@ -402,15 +404,43 @@ fn activate_injected_dep( /// there's only going to be one panic runtime in the output. fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { let sess = &tcx.sess; + let list: Vec<_> = list + .iter_enumerated() + .filter_map( + |(cnum, linkage)| if *linkage == Linkage::NotLinked { None } else { Some(cnum) }, + ) + .collect(); if list.is_empty() { return; } - let mut panic_runtime = None; - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; + let desired_strategy = sess.panic_strategy(); + + // If we are panic=immediate-abort, make sure everything in the dependency tree has also been + // compiled with immediate-abort. + if list + .iter() + .any(|cnum| tcx.required_panic_strategy(*cnum) == Some(PanicStrategy::ImmediateAbort)) + { + let mut invalid_crates = Vec::new(); + for cnum in list.iter().copied() { + if tcx.required_panic_strategy(cnum) != Some(PanicStrategy::ImmediateAbort) { + invalid_crates.push(cnum); + // If core is incompatible, it's very likely that we'd emit an error for every + // sysroot crate, so instead of doing that emit a single fatal error that suggests + // using build-std. + if tcx.crate_name(cnum) == sym::core { + sess.dcx().emit_fatal(IncompatibleWithImmediateAbortCore); + } + } } + for cnum in invalid_crates { + sess.dcx() + .emit_err(IncompatibleWithImmediateAbort { crate_name: tcx.crate_name(cnum) }); + } + } + let mut panic_runtime = None; + for cnum in list.iter().copied() { if tcx.is_panic_runtime(cnum) { if let Some((prev, _)) = panic_runtime { let prev_name = tcx.crate_name(prev); @@ -430,8 +460,6 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // only one, but we perform validation here that all the panic strategy // compilation modes for the whole DAG are valid. if let Some((runtime_cnum, found_strategy)) = panic_runtime { - let desired_strategy = sess.panic_strategy(); - // First up, validate that our selected panic runtime is indeed exactly // our same strategy. if found_strategy != desired_strategy { @@ -445,10 +473,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // strategy. If the dep isn't linked, we ignore it, and if our strategy // is abort then it's compatible with everything. Otherwise all crates' // panic strategy must match our own. - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; - } + for cnum in list.iter().copied() { if cnum == runtime_cnum || tcx.is_compiler_builtins(cnum) { continue; } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index e5a4fd48353fa..abfd078f7462d 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -75,6 +75,16 @@ pub struct RequiredPanicStrategy { pub desired_strategy: PanicStrategy, } +#[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort)] +pub struct IncompatibleWithImmediateAbort { + pub crate_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort_core)] +pub struct IncompatibleWithImmediateAbortCore; + #[derive(Diagnostic)] #[diag(metadata_incompatible_panic_in_drop_strategy)] pub struct IncompatiblePanicInDropStrategy { diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 93264f02cc259..a0e4c288c4a85 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -98,5 +98,6 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo } PanicStrategy::Unwind => true, + PanicStrategy::ImmediateAbort => false, } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index c477e65f5d6b3..47b45c58b9f8c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -16,7 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, PanicStrategy, Target, X86Abi}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; @@ -1198,7 +1198,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) // // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"` // function defined in Rust is also required to abort. - if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) { + if !tcx.sess.panic_strategy().unwinds() && !tcx.is_foreign_item(did) { return false; } @@ -1206,7 +1206,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) // // This is not part of `codegen_fn_attrs` as it can differ between crates // and therefore cannot be computed in core. - if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort + if !tcx.sess.opts.unstable_opts.panic_in_drop.unwinds() && tcx.is_lang_item(did, LangItem::DropInPlace) { return false; @@ -1245,7 +1245,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | RiscvInterruptS | RustInvalid | Unadjusted => false, - Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, + Rust | RustCall | RustCold => tcx.sess.panic_strategy().unwinds(), } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c1cd2788348a6..c5cd06f170c47 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -86,7 +86,6 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::source_map::dummy_spanned; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; @@ -1149,7 +1148,7 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, typing_env: ty::Typing fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // Nothing can unwind when landing pads are off. - if tcx.sess.panic_strategy() == PanicStrategy::Abort { + if !tcx.sess.panic_strategy().unwinds() { return false; } diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index abbff1c48dd9a..7c66783548ea4 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -101,12 +101,15 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { } fn required_panic_strategy(tcx: TyCtxt<'_>, _: LocalCrate) -> Option { + let local_strategy = tcx.sess.panic_strategy(); + if tcx.is_panic_runtime(LOCAL_CRATE) { - return Some(tcx.sess.panic_strategy()); + return Some(local_strategy); } - if tcx.sess.panic_strategy() == PanicStrategy::Abort { - return Some(PanicStrategy::Abort); + match local_strategy { + PanicStrategy::Abort | PanicStrategy::ImmediateAbort => return Some(local_strategy), + _ => {} } for def_id in tcx.hir_body_owners() { diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 5b6d7ffb51102..b53c1f6d20203 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,7 +1,6 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::patch::MirPatch; @@ -13,7 +12,7 @@ pub(super) struct RemoveNoopLandingPads; impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.panic_strategy() != PanicStrategy::Abort + sess.panic_strategy().unwinds() } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 795cb2b2cfeba..81ada79dd4384 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -29,7 +29,8 @@ use rustc_span::{ SourceFileHashAlgorithm, Symbol, sym, }; use rustc_target::spec::{ - FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, + FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, + Target, TargetTuple, }; use tracing::debug; @@ -2799,6 +2800,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } + if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) { + early_dcx.early_fatal( + "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler", + ) + } + let crate_name = matches.opt_str("crate-name"); let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); // Parse any `-l` flags, which link to native libraries. diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 7e970461ab731..fc36fe9d5d76e 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -125,7 +125,9 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { None | Some(_), ) => disallow(cfg, "-Z sanitizer=cfi"), (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), - (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), + (sym::panic, Some(sym::abort | sym::unwind | sym::immediate_abort)) => { + disallow(cfg, "-C panic") + } (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), (sym::unix, None) | (sym::windows, None) @@ -204,6 +206,9 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { } ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); + if sess.panic_strategy() == PanicStrategy::ImmediateAbort { + ins_sym!(sym::panic, PanicStrategy::Abort.desc_symbol()); + } // JUSTIFICATION: before wrapper fn is available #[allow(rustc::bad_opt_access)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 69facde693689..3b9d81177861d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -802,7 +802,7 @@ mod desc { pub(crate) const parse_threads: &str = parse_number; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; - pub(crate) const parse_panic_strategy: &str = "either `unwind` or `abort`"; + pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; @@ -1165,6 +1165,7 @@ pub mod parse { match v { Some("unwind") => *slot = Some(PanicStrategy::Unwind), Some("abort") => *slot = Some(PanicStrategy::Abort), + Some("immediate-abort") => *slot = Some(PanicStrategy::ImmediateAbort), _ => return false, } true @@ -1174,6 +1175,7 @@ pub mod parse { match v { Some("unwind") => *slot = PanicStrategy::Unwind, Some("abort") => *slot = PanicStrategy::Abort, + Some("immediate-abort") => *slot = PanicStrategy::ImmediateAbort, _ => return false, } true diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index d0dd2cdac0c48..25b46241c52df 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -777,9 +777,11 @@ impl Session { // Otherwise, we can defer to the `-C force-unwind-tables=` // value, if it is provided, or disable them, if not. self.target.requires_uwtable - || self.opts.cg.force_unwind_tables.unwrap_or( - self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, - ) + || self + .opts + .cg + .force_unwind_tables + .unwrap_or(self.panic_strategy().unwinds() || self.target.default_uwtable) } /// Returns the number of query threads that should be used for this @@ -1229,7 +1231,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // KCFI requires panic=abort - if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort { + if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy().unwinds() { sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4fef65f46b1fd..efeb4371e4ca6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1195,6 +1195,7 @@ symbols! { if_let_rescope, if_while_or_patterns, ignore, + immediate_abort, impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_assoc_type, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f705af52bd868..2299db94535d7 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -834,6 +834,7 @@ crate::target_spec_enum! { pub enum PanicStrategy { Unwind = "unwind", Abort = "abort", + ImmediateAbort = "immediate-abort", } parse_error_type = "panic strategy"; @@ -852,8 +853,13 @@ impl PanicStrategy { match *self { PanicStrategy::Unwind => sym::unwind, PanicStrategy::Abort => sym::abort, + PanicStrategy::ImmediateAbort => sym::immediate_abort, } } + + pub fn unwinds(self) -> bool { + matches!(self, PanicStrategy::Unwind) + } } crate::target_spec_enum! { diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 9ba7c5bd28a11..fb1f8c86dbfd5 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -22,8 +22,6 @@ compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] -# Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort"] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size"] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 76630a746dd26..304b473f14df7 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -408,12 +408,12 @@ pub const fn handle_alloc_error(layout: Layout) -> ! { } } - #[cfg(not(feature = "panic_immediate_abort"))] + #[cfg(not(panic = "immediate_abort"))] { core::intrinsics::const_eval_select((layout,), ct_error, rt_error) } - #[cfg(feature = "panic_immediate_abort")] + #[cfg(panic = "immediate_abort")] ct_error(layout) } diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index b0027e964e467..4853b84a01a32 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -23,7 +23,7 @@ mod tests; // ensure that the code generation related to these panics is minimal as there's // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] fn capacity_overflow() -> ! { panic!("capacity overflow"); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 10c7ee4f6c89d..1a9041290ef17 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2020,7 +2020,7 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { #[cold] - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2102,7 +2102,7 @@ impl Vec { #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"] pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T { #[cold] - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2166,7 +2166,7 @@ impl Vec { #[rustc_confusables("delete", "take")] pub fn remove(&mut self, index: usize) -> T { #[cold] - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2955,7 +2955,7 @@ impl Vec { A: Clone, { #[cold] - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(at: usize, len: usize) -> ! { diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 3e34e03a61e91..d094172b07659 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -16,7 +16,7 @@ test = false bench = false [features] -# Make panics and failed asserts immediately abort without formatting any message +# Issue a compile error that says to use -Cpanic=immediate-abort panic_immediate_abort = [] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = [] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 9b53b75ebee80..d7097e3b6fd4e 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -778,7 +778,7 @@ impl Display for BorrowMutError { } // This ensures the panicking code is outlined from `borrow_mut` for `RefCell`. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[cold] const fn panic_already_borrowed(err: BorrowMutError) -> ! { @@ -790,7 +790,7 @@ const fn panic_already_borrowed(err: BorrowMutError) -> ! { } // This ensures the panicking code is outlined from `borrow` for `RefCell`. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[cold] const fn panic_already_mutably_borrowed(err: BorrowError) -> ! { diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 54d5a63633c22..3c4bfe28aa326 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,8 +1387,8 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) radix <= 16 && digits.len() <= size_of::() * 2 - is_signed_ty as usize } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(panic = "immediate_abort", inline)] #[cold] #[track_caller] const fn from_ascii_radix_panic(radix: u32) -> ! { diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 886d581b0a6b6..10eec2cdfb6cf 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2161,8 +2161,8 @@ impl Option> { } } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(panic = "immediate_abort", inline)] #[cold] #[track_caller] const fn unwrap_failed() -> ! { @@ -2170,8 +2170,8 @@ const fn unwrap_failed() -> ! { } // This is a separate function to reduce the code size of .expect() itself. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(panic = "immediate_abort", inline)] #[cold] #[track_caller] const fn expect_failed(msg: &str) -> ! { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 804a12ee477bb..da37d72960a61 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -33,7 +33,10 @@ use crate::intrinsics::const_eval_select; use crate::panic::{Location, PanicInfo}; #[cfg(feature = "panic_immediate_abort")] -const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C panic=abort"); +compile_error!( + "panic_immediate_abort is now a real panic strategy! \ + Enable it with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" +); // First we define the two main entry points that all panics go through. // In the end both are just convenience wrappers around `panic_impl`. @@ -44,16 +47,16 @@ const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C /// site as much as possible (so that `panic!()` has as low an impact /// on (e.g.) the inlining of other functions as possible), by moving /// the actual formatting into this shared place. -// If panic_immediate_abort, inline the abort call, +// If panic=immediate-abort, inline the abort call, // otherwise avoid inlining because of it is cold path. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } @@ -78,8 +81,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { /// Like `panic_fmt`, but for non-unwinding panics. /// /// Has to be a separate function so that it can carry the `rustc_nounwind` attribute. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] // This attribute has the key side-effect that if the panic handler ignores `can_unwind` // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, @@ -94,7 +97,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // We don't unwind anyway at compile-time so we can call the regular `panic_fmt`. panic_fmt(fmt) } else #[track_caller] { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } @@ -123,10 +126,10 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // above. /// The underlying implementation of core's `panic!` macro when no formatting is used. -// Never inline unless panic_immediate_abort to avoid code +// Never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = "panic"] // used by lints and miri for panics @@ -158,10 +161,10 @@ macro_rules! panic_const { $( /// This is a panic called with a message that's a result of a MIR-produced Assert. // - // never inline unless panic_immediate_abort to avoid code + // never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] - #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] + #[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = stringify!($lang)] @@ -216,8 +219,8 @@ pub mod panic_const { /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable @@ -226,8 +229,8 @@ pub const fn panic_nounwind(expr: &'static str) -> ! { } /// Like `panic_nounwind`, but also inhibits showing a backtrace. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[rustc_nounwind] pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true); @@ -259,25 +262,25 @@ pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access fn panic_bounds_check(index: usize, len: usize) -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } panic!("index out of bounds: the len is {len} but the index is {index}") } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref #[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } @@ -289,13 +292,13 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[lang = "panic_null_pointer_dereference"] // needed by codegen for panic on null pointer deref #[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind fn panic_null_pointer_dereference() -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } @@ -305,13 +308,13 @@ fn panic_null_pointer_dereference() -> ! { ) } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[lang = "panic_invalid_enum_construction"] // needed by codegen for panic on invalid enum construction. #[rustc_nounwind] // `CheckEnums` MIR pass requires this function to never unwind fn panic_invalid_enum_construction(source: u128) -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { super::intrinsics::abort() } @@ -328,8 +331,8 @@ fn panic_invalid_enum_construction(source: u128) -> ! { /// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[lang = "panic_cannot_unwind"] // needed by codegen for panic in nounwind function #[rustc_nounwind] fn panic_cannot_unwind() -> ! { @@ -344,8 +347,8 @@ fn panic_cannot_unwind() -> ! { /// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[lang = "panic_in_cleanup"] // needed by codegen for panic in nounwind function #[rustc_nounwind] fn panic_in_cleanup() -> ! { @@ -377,8 +380,8 @@ pub enum AssertKind { } /// Internal function for `assert_eq!` and `assert_ne!` macros -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[doc(hidden)] pub fn assert_failed( @@ -395,8 +398,8 @@ where } /// Internal function for `assert_match!` -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[doc(hidden)] pub fn assert_matches_failed( @@ -415,8 +418,8 @@ pub fn assert_matches_failed( } /// Non-generic version of the above functions, to avoid code bloat. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] fn assert_failed_inner( kind: AssertKind, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 5c1f64bfe14af..742f1c19112b7 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1847,7 +1847,7 @@ impl Result, E> { } // This is a separate function to reduce the code size of the methods -#[cfg(not(feature = "panic_immediate_abort"))] +#[cfg(not(panic = "immediate_abort"))] #[inline(never)] #[cold] #[track_caller] @@ -1859,7 +1859,7 @@ fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! { // that gets immediately thrown away, since vtables don't get cleaned up // by dead code elimination if a trait object is constructed even if it goes // unused -#[cfg(feature = "panic_immediate_abort")] +#[cfg(panic = "immediate_abort")] #[inline] #[cold] #[track_caller] diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index a8147d745f3ab..c1a55c1f62a20 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -31,8 +31,8 @@ where } } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! { if start > len { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dfbb3628350a8..e8b2712e147c5 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3858,8 +3858,8 @@ impl [T] { { // The panic code path was put into a cold function to not bloat the // call site. - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] - #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] + #[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { const_panic!( diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 400daba16c1b8..17858bf9f0d01 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -840,8 +840,8 @@ unsafe fn bidirectional_merge bool>( } } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate_abort", inline)] fn panic_on_ord_violation() -> ! { // This is indicative of a logic bug in the user-provided comparison function or Ord // implementation. They are expected to implement a total order as explained in the Ord diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 04fdaa8143eff..910ea2948ee12 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -64,12 +64,12 @@ pub use validations::{next_code_point, utf8_char_width}; #[cold] #[track_caller] #[rustc_allow_const_fn_unstable(const_eval_select)] -#[cfg(not(feature = "panic_immediate_abort"))] +#[cfg(not(panic = "immediate_abort"))] const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { crate::intrinsics::const_eval_select((s, begin, end), slice_error_fail_ct, slice_error_fail_rt) } -#[cfg(feature = "panic_immediate_abort")] +#[cfg(panic = "immediate_abort")] const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { slice_error_fail_ct(s, begin, end) } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index d28a7f0b46022..958cafb8f3d43 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -106,11 +106,6 @@ compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] -# Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = [ - "core/panic_immediate_abort", - "alloc/panic_immediate_abort", -] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8b7282c51d123..5d52bc366bd73 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -331,7 +331,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { #[cfg(not(test))] #[doc(hidden)] -#[cfg(feature = "panic_immediate_abort")] +#[cfg(panic = "immediate_abort")] #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { /// A reason for forcing an immediate abort on panic. @@ -371,7 +371,7 @@ pub mod panic_count { #[cfg(not(test))] #[doc(hidden)] -#[cfg(not(feature = "panic_immediate_abort"))] +#[cfg(not(panic = "immediate_abort"))] #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { use crate::cell::Cell; @@ -499,13 +499,13 @@ pub mod panic_count { pub use realstd::rt::panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. -#[cfg(feature = "panic_immediate_abort")] +#[cfg(panic = "immediate_abort")] pub unsafe fn catch_unwind R>(f: F) -> Result> { Ok(f()) } /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. -#[cfg(not(feature = "panic_immediate_abort"))] +#[cfg(not(panic = "immediate_abort"))] pub unsafe fn catch_unwind R>(f: F) -> Result> { union Data { f: ManuallyDrop, @@ -720,14 +720,14 @@ pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] #[cfg_attr(not(any(test, doctest)), lang = "begin_panic")] // lang item for CTFE panic support -// never inline unless panic_immediate_abort to avoid code +// never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate_abort", inline)] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval pub const fn begin_panic(msg: M) -> ! { - if cfg!(feature = "panic_immediate_abort") { + if cfg!(panic = "immediate_abort") { intrinsics::abort() } @@ -861,7 +861,7 @@ fn panic_with_hook( /// This is the entry point for `resume_unwind`. /// It just forwards the payload to the panic runtime. -#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(panic = "immediate_abort", inline)] pub fn resume_unwind(payload: Box) -> ! { panic_count::increase(false); @@ -890,16 +890,14 @@ pub fn resume_unwind(payload: Box) -> ! { /// on which to slap yer breakpoints. #[inline(never)] #[cfg_attr(not(test), rustc_std_internal_symbol)] -#[cfg(not(feature = "panic_immediate_abort"))] +#[cfg(not(panic = "immediate_abort"))] fn rust_panic(msg: &mut dyn PanicPayload) -> ! { let code = unsafe { __rust_start_panic(msg) }; rtabort!("failed to initiate panic, error {code}") } #[cfg_attr(not(test), rustc_std_internal_symbol)] -#[cfg(feature = "panic_immediate_abort")] +#[cfg(panic = "immediate_abort")] fn rust_panic(_: &mut dyn PanicPayload) -> ! { - unsafe { - crate::intrinsics::abort(); - } + crate::intrinsics::abort(); } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 8d95cc1fb5747..d98f2e692cd50 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -39,11 +39,11 @@ fn __rust_abort() { // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { - #[cfg(not(feature = "panic_immediate_abort"))] + #[cfg(not(panic = "immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } - #[cfg(feature = "panic_immediate_abort")] + #[cfg(panic = "immediate_abort")] { let _ = format_args!($($t)*); } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 797feeb2bbb5f..b49de8d1b6084 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -230,7 +230,7 @@ impl fmt::Display for AccessError { impl Error for AccessError {} // This ensures the panicking code is outlined from `with` for `LocalKey`. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate_abort"), inline(never))] #[track_caller] #[cold] fn panic_access_error(err: AccessError) -> ! { diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 7b4aeed94e980..ee4aec61872e7 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -31,7 +31,6 @@ llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] optimize_for_size = ["std/optimize_for_size"] panic-unwind = ["std/panic-unwind"] -panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 445b10188e3f8..0e340de4daa27 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -471,11 +471,13 @@ If not specified, overflow checks are enabled if This option lets you control what happens when the code panics. * `abort`: terminate the process upon panic +* `immediate-abort`: terminate the process upon panic, and do not call any panic hooks * `unwind`: unwind the stack upon panic If not specified, the default depends on the target. If any crate in the crate graph uses `abort`, the final binary (`bin`, `dylib`, `cdylib`, `staticlib`) must also use `abort`. +If any crate in the crate graph uses `immediate-abort`, every crate in the graph must use `immediate-abort`. If `std` is used as a `dylib` with `unwind`, the final binary must also use `unwind`. ## passes diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 1277fd225eb66..e84a22787668d 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -201,6 +201,8 @@ pub struct TestProps { /// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios /// that don't otherwise want/need `-Z build-std`. pub add_core_stubs: bool, + /// Add these flags to the build of `minicore`. + pub core_stubs_compile_flags: Vec, /// Whether line annotatins are required for the given error kind. pub dont_require_annotations: HashSet, /// Whether pretty printers should be disabled in gdb. @@ -253,6 +255,7 @@ mod directives { pub const FILECHECK_FLAGS: &'static str = "filecheck-flags"; pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg"; pub const ADD_CORE_STUBS: &'static str = "add-core-stubs"; + pub const CORE_STUBS_COMPILE_FLAGS: &'static str = "core-stubs-compile-flags"; // This isn't a real directive, just one that is probably mistyped often pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags"; pub const DISABLE_GDB_PRETTY_PRINTERS: &'static str = "disable-gdb-pretty-printers"; @@ -311,6 +314,7 @@ impl TestProps { no_auto_check_cfg: false, has_enzyme: false, add_core_stubs: false, + core_stubs_compile_flags: vec![], dont_require_annotations: Default::default(), disable_gdb_pretty_printers: false, compare_output_by_lines: false, @@ -653,6 +657,21 @@ impl TestProps { self.update_add_core_stubs(ln, config); + if let Some(flags) = config.parse_name_value_directive( + ln, + directives::CORE_STUBS_COMPILE_FLAGS, + testfile, + line_number, + ) { + let flags = split_flags(&flags); + for flag in &flags { + if flag == "--edition" || flag.starts_with("--edition=") { + panic!("you must use `//@ edition` to configure the edition"); + } + } + self.core_stubs_compile_flags.extend(flags); + } + if let Some(err_kind) = config.parse_name_value_directive( ln, DONT_REQUIRE_ANNOTATIONS, diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 0ef84fb459493..4fef899256739 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -19,6 +19,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "check-test-line-numbers-match", "compare-output-by-lines", "compile-flags", + "core-stubs-compile-flags", "disable-gdb-pretty-printers", "doc-flags", "dont-check-compiler-stderr", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 89fb8eb4357b7..29578acb40400 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1322,6 +1322,7 @@ impl<'test> TestCx<'test> { rustc.args(&["--crate-type", "rlib"]); rustc.arg("-Cpanic=abort"); + rustc.args(self.props.core_stubs_compile_flags.clone()); let res = self.compose_and_run(rustc, self.config.compile_lib_path.as_path(), None, None); if !res.status.success() { @@ -1432,6 +1433,12 @@ impl<'test> TestCx<'test> { aux_rustc.arg("-L").arg(&aux_dir); + if aux_props.add_core_stubs { + let minicore_path = self.build_minicore(); + aux_rustc.arg("--extern"); + aux_rustc.arg(&format!("minicore={}", minicore_path)); + } + let auxres = aux_cx.compose_and_run( aux_rustc, aux_cx.config.compile_lib_path.as_path(), @@ -1858,14 +1865,13 @@ impl<'test> TestCx<'test> { } } - rustc.args(&self.props.compile_flags); - // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with - // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`, - // however, by moving this last we should override previous `-Cpanic`s and - // `-Cforce-unwind-tables`s. Note that checking here is very fragile, because we'd have to - // account for all possible compile flag splittings (they have some... intricacies and are - // not yet normalized). + // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`. + // + // We could apply these last and override any provided flags. That would ensure that the + // build works, but some tests want to exercise that mixing panic modes in specific ways is + // rejected. So we enable aborting panics and unwind tables before adding flags, just to + // change the default. // // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. if self.props.add_core_stubs { @@ -1873,6 +1879,8 @@ impl<'test> TestCx<'test> { rustc.arg("-Cforce-unwind-tables=yes"); } + rustc.args(&self.props.compile_flags); + rustc } diff --git a/tests/run-make-cargo/panic-immediate-abort-codegen/Cargo.toml b/tests/run-make-cargo/panic-immediate-abort-codegen/Cargo.toml new file mode 100644 index 0000000000000..3c61c12a84ede --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-codegen/Cargo.toml @@ -0,0 +1,12 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "panic_scenarios" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" + +[profile.release] +rustflags = ["-Zmerge-functions=disabled", "-Zcodegen-source-order", "--emit=llvm-ir"] diff --git a/tests/run-make-cargo/panic-immediate-abort-codegen/lib.rs b/tests/run-make-cargo/panic-immediate-abort-codegen/lib.rs new file mode 100644 index 0000000000000..1e20da93ba805 --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-codegen/lib.rs @@ -0,0 +1,65 @@ +#![no_std] + +#[unsafe(no_mangle)] +pub fn panic_noarg() { + // CHECK-LABEL: @panic_noarg( + // CHECK-NEXT: start: + // CHECK-NEXT: tail call void @llvm.trap() + panic!(); +} + +#[unsafe(no_mangle)] +pub fn panic_str() { + // CHECK-LABEL: @panic_str( + // CHECK-NEXT: start: + // CHECK-NEXT: tail call void @llvm.trap() + panic!("ouch"); +} + +#[unsafe(no_mangle)] +pub fn bounds_check(x: &[u8], idx: usize) -> &u8 { + // CHECK-LABEL: @bounds_check( + // CHECK-NEXT: start: + // CHECK-NEXT: icmp ult + // CHECK-NEXT: br i1 + // CHECK: bb1: + // CHECK-NEXT: getelementptr inbounds nuw i8 + // CHECK-NEXT: ret ptr + // CHECK: panic: + // CHECK-NEXT: tail call void @llvm.trap() + &x[idx] +} + +#[unsafe(no_mangle)] +pub fn str_bounds_check(x: &str, idx: usize) -> &str { + // CHECK-LABEL: @str_bounds_check( + // CHECK-NOT: call + // CHECK: tail call void @llvm.trap() + // CHECK-NOT: call + &x[idx..] +} + +#[unsafe(no_mangle)] +pub fn unsigned_integer_div(x: u16, y: u16) -> u16 { + // CHECK-LABEL: @unsigned_integer_div( + // CHECK-NEXT: start: + // CHECK-NEXT: icmp eq i16 + // CHECK-NEXT: br i1 + // CHECK: bb1: + // CHECK-NEXT: udiv i16 + // CHECK-NEXT: ret i16 + // CHECK: panic: + // CHECK-NEXT: tail call void @llvm.trap() + x / y +} + +#[unsafe(no_mangle)] +pub fn refcell_already_borrowed() { + // CHECK-LABEL: @refcell_already_borrowed( + // CHECK-NOT: call + // CHECK: tail call void @llvm.trap() + // CHECK-NOT: call + let r = core::cell::RefCell::new(0u8); + let _guard = r.borrow_mut(); + r.borrow_mut(); +} diff --git a/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs b/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs new file mode 100644 index 0000000000000..e91d1db83d1d1 --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs @@ -0,0 +1,41 @@ +#![deny(warnings)] + +use run_make_support::{cargo, llvm_filecheck, path, rfs, target}; + +fn main() { + let target_dir = path("target"); + + cargo() + .args(&[ + "build", + "--release", + "--lib", + "--manifest-path", + "Cargo.toml", + "-Zbuild-std=core", + "--target", + &target(), + ]) + .env("RUSTFLAGS", "-Zunstable-options -Cpanic=immediate-abort") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + // Visual Studio 2022 requires that the LIB env var be set so it can + // find the Windows SDK. + .env("LIB", std::env::var("LIB").unwrap_or_default()) + .run(); + + let out_dir = target_dir.join(target()).join("release").join("deps"); + let ir_file = rfs::read_dir(out_dir) + .find_map(|e| { + let path = e.unwrap().path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if file_name.starts_with("panic_scenarios") && file_name.ends_with(".ll") { + Some(path) + } else { + None + } + }) + .unwrap(); + + llvm_filecheck().patterns("lib.rs").input_file(ir_file).run(); +} diff --git a/tests/run-make-cargo/panic-immediate-abort-works/hello/Cargo.toml b/tests/run-make-cargo/panic-immediate-abort-works/hello/Cargo.toml new file mode 100644 index 0000000000000..1e278d557c082 --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-works/hello/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "hello" +version = "0.1.0" +edition = "2024" diff --git a/tests/run-make-cargo/panic-immediate-abort-works/hello/src/main.rs b/tests/run-make-cargo/panic-immediate-abort-works/hello/src/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-works/hello/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs new file mode 100644 index 0000000000000..a3c5877a6a19d --- /dev/null +++ b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs @@ -0,0 +1,28 @@ +//@ needs-target-std + +#![deny(warnings)] + +use run_make_support::{cargo, path, target}; + +fn main() { + let target_dir = path("target"); + + cargo() + .current_dir("hello") + .args(&[ + "build", + "--release", + "--manifest-path", + "Cargo.toml", + "-Zbuild-std", + "--target", + &target(), + ]) + .env("RUSTFLAGS", "-Zunstable-options -Cpanic=immediate-abort") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + // Visual Studio 2022 requires that the LIB env var be set so it can + // find the Windows SDK. + .env("LIB", std::env::var("LIB").unwrap_or_default()) + .run(); +} diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index 989a01f224412..b99ce169b8900 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -18,7 +18,7 @@ warning: unexpected `cfg` condition value: `UNEXPECTED_VALUE` LL | cfg_macro::my_lib_macro_value!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort` and `unwind` + = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_value` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro_value` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 95d10e014f33b..8e1760bd969a7 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -18,7 +18,7 @@ warning: unexpected `cfg` condition value: `UNEXPECTED_VALUE` LL | cfg_macro::my_lib_macro_value!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort` and `unwind` + = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_value` crate for guidance on how handle this unexpected cfg = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 6490fc63fd766..c34c20b172d50 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -80,7 +80,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | panic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort` and `unwind` + = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/panic-runtime/auxiliary/needs-abort.rs b/tests/ui/panic-runtime/auxiliary/needs-abort.rs index 21f862e4b431c..cba4907dbb633 100644 --- a/tests/ui/panic-runtime/auxiliary/needs-abort.rs +++ b/tests/ui/panic-runtime/auxiliary/needs-abort.rs @@ -1,5 +1,7 @@ //@ compile-flags:-C panic=abort //@ no-prefer-dynamic +#![feature(no_core)] #![crate_type = "rlib"] #![no_std] +#![no_core] diff --git a/tests/ui/panic-runtime/auxiliary/needs-immediate-abort.rs b/tests/ui/panic-runtime/auxiliary/needs-immediate-abort.rs new file mode 100644 index 0000000000000..4a41d16faa080 --- /dev/null +++ b/tests/ui/panic-runtime/auxiliary/needs-immediate-abort.rs @@ -0,0 +1,7 @@ +//@ compile-flags:-C panic=immediate-abort -Zunstable-options +//@ no-prefer-dynamic + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_std] +#![no_core] diff --git a/tests/ui/panic-runtime/auxiliary/needs-unwind-immediate-abort.rs b/tests/ui/panic-runtime/auxiliary/needs-unwind-immediate-abort.rs new file mode 100644 index 0000000000000..295876fec5279 --- /dev/null +++ b/tests/ui/panic-runtime/auxiliary/needs-unwind-immediate-abort.rs @@ -0,0 +1,18 @@ +//@ compile-flags:-C panic=unwind +//@ no-prefer-dynamic +//@ add-core-stubs + +#![crate_type = "rlib"] +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; + +extern "C-unwind" fn foo() {} + +#[inline] +fn bar() { + let ptr: extern "C-unwind" fn() = foo; + ptr(); +} diff --git a/tests/ui/panic-runtime/bad-panic-flag1.rs b/tests/ui/panic-runtime/bad-panic-flag1.rs index 117935847cbd7..575e30f785cf5 100644 --- a/tests/ui/panic-runtime/bad-panic-flag1.rs +++ b/tests/ui/panic-runtime/bad-panic-flag1.rs @@ -2,4 +2,4 @@ fn main() {} -//~? ERROR incorrect value `foo` for codegen option `panic` - either `unwind` or `abort` was expected +//~? ERROR incorrect value `foo` for codegen option `panic` - either `unwind`, `abort`, or `immediate-abort` was expected diff --git a/tests/ui/panic-runtime/bad-panic-flag1.stderr b/tests/ui/panic-runtime/bad-panic-flag1.stderr index 013373c6f9313..c30598bba538e 100644 --- a/tests/ui/panic-runtime/bad-panic-flag1.stderr +++ b/tests/ui/panic-runtime/bad-panic-flag1.stderr @@ -1,2 +1,2 @@ -error: incorrect value `foo` for codegen option `panic` - either `unwind` or `abort` was expected +error: incorrect value `foo` for codegen option `panic` - either `unwind`, `abort`, or `immediate-abort` was expected diff --git a/tests/ui/panic-runtime/bad-panic-flag2.rs b/tests/ui/panic-runtime/bad-panic-flag2.rs index b5d0442a03326..4e34da217d7d2 100644 --- a/tests/ui/panic-runtime/bad-panic-flag2.rs +++ b/tests/ui/panic-runtime/bad-panic-flag2.rs @@ -2,4 +2,4 @@ fn main() {} -//~? ERROR codegen option `panic` requires either `unwind` or `abort` +//~? ERROR codegen option `panic` requires either `unwind`, `abort`, or `immediate-abort` diff --git a/tests/ui/panic-runtime/bad-panic-flag2.stderr b/tests/ui/panic-runtime/bad-panic-flag2.stderr index 6ab94ea704d30..c8d12749c5d96 100644 --- a/tests/ui/panic-runtime/bad-panic-flag2.stderr +++ b/tests/ui/panic-runtime/bad-panic-flag2.stderr @@ -1,2 +1,2 @@ -error: codegen option `panic` requires either `unwind` or `abort` (C panic=) +error: codegen option `panic` requires either `unwind`, `abort`, or `immediate-abort` (C panic=) diff --git a/tests/ui/panic-runtime/immediate-abort-default-sysroot.rs b/tests/ui/panic-runtime/immediate-abort-default-sysroot.rs new file mode 100644 index 0000000000000..94dc7c5671ead --- /dev/null +++ b/tests/ui/panic-runtime/immediate-abort-default-sysroot.rs @@ -0,0 +1,15 @@ +//@ build-fail +//@ aux-build:needs-unwind.rs +//@ compile-flags:-C panic=immediate-abort -Zunstable-options +//@ no-prefer-dynamic + +extern crate needs_unwind; + +// immediate-abort does not require any panic runtime, so trying to build a binary crate with +// panic=immediate-abort and the precompiled sysroot will fail to link, because no panic runtime +// provides the panic entrypoints used by sysroot crates. +// This test ensures that we get a clean compile error instead of a linker error. + +fn main() {} + +//~? ERROR the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` diff --git a/tests/ui/panic-runtime/immediate-abort-default-sysroot.stderr b/tests/ui/panic-runtime/immediate-abort-default-sysroot.stderr new file mode 100644 index 0000000000000..bd6bdd8b6673a --- /dev/null +++ b/tests/ui/panic-runtime/immediate-abort-default-sysroot.stderr @@ -0,0 +1,4 @@ +error: the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + +error: aborting due to 1 previous error + diff --git a/tests/ui/panic-runtime/need-abort-got-immediate-abort.rs b/tests/ui/panic-runtime/need-abort-got-immediate-abort.rs new file mode 100644 index 0000000000000..78977c60be985 --- /dev/null +++ b/tests/ui/panic-runtime/need-abort-got-immediate-abort.rs @@ -0,0 +1,21 @@ +//@ build-fail +//@ aux-build:needs-abort.rs +//@ compile-flags:-Cpanic=immediate-abort -Zunstable-options +//@ no-prefer-dynamic +//@ add-core-stubs +//@ core-stubs-compile-flags: -Cpanic=immediate-abort -Zunstable-options + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +extern crate minicore; +extern crate needs_abort; + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + 0 +} + +//~? ERROR the crate `needs_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` diff --git a/tests/ui/panic-runtime/need-abort-got-immediate-abort.stderr b/tests/ui/panic-runtime/need-abort-got-immediate-abort.stderr new file mode 100644 index 0000000000000..65a26b676b92e --- /dev/null +++ b/tests/ui/panic-runtime/need-abort-got-immediate-abort.stderr @@ -0,0 +1,4 @@ +error: the crate `needs_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` + +error: aborting due to 1 previous error + diff --git a/tests/ui/panic-runtime/need-immediate-abort-got-abort.rs b/tests/ui/panic-runtime/need-immediate-abort-got-abort.rs new file mode 100644 index 0000000000000..1c5f597a3f992 --- /dev/null +++ b/tests/ui/panic-runtime/need-immediate-abort-got-abort.rs @@ -0,0 +1,20 @@ +//@ build-fail +//@ aux-build:needs-immediate-abort.rs +//@ compile-flags:-C panic=abort +//@ no-prefer-dynamic +//@ add-core-stubs +//@ core-stubs-compile-flags: -Zunstable-options -Cpanic=immediate-abort + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +extern crate minicore; +extern crate needs_immediate_abort; + +extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 { + 0 +} + +//~? ERROR the crate `need_immediate_abort_got_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` diff --git a/tests/ui/panic-runtime/need-immediate-abort-got-abort.stderr b/tests/ui/panic-runtime/need-immediate-abort-got-abort.stderr new file mode 100644 index 0000000000000..8dcf120cb9f46 --- /dev/null +++ b/tests/ui/panic-runtime/need-immediate-abort-got-abort.stderr @@ -0,0 +1,4 @@ +error: the crate `need_immediate_abort_got_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` + +error: aborting due to 1 previous error + diff --git a/tests/ui/panic-runtime/need-immediate-abort-got-unwind.rs b/tests/ui/panic-runtime/need-immediate-abort-got-unwind.rs new file mode 100644 index 0000000000000..24d521230d49a --- /dev/null +++ b/tests/ui/panic-runtime/need-immediate-abort-got-unwind.rs @@ -0,0 +1,20 @@ +//@ build-fail +//@ needs-unwind +//@ aux-build:needs-immediate-abort.rs +//@ no-prefer-dynamic +//@ add-core-stubs +//@ core-stubs-compile-flags: -Zunstable-options -Cpanic=immediate-abort + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +extern crate minicore; +extern crate needs_immediate_abort; + +extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 { + 0 +} + +//~? ERROR the crate `need_immediate_abort_got_unwind` was compiled with a panic strategy which is incompatible with `immediate-abort` diff --git a/tests/ui/panic-runtime/need-immediate-abort-got-unwind.stderr b/tests/ui/panic-runtime/need-immediate-abort-got-unwind.stderr new file mode 100644 index 0000000000000..740fc80a77df8 --- /dev/null +++ b/tests/ui/panic-runtime/need-immediate-abort-got-unwind.stderr @@ -0,0 +1,4 @@ +error: the crate `need_immediate_abort_got_unwind` was compiled with a panic strategy which is incompatible with `immediate-abort` + +error: aborting due to 1 previous error + diff --git a/tests/ui/panic-runtime/need-unwind-got-immediate-abort.rs b/tests/ui/panic-runtime/need-unwind-got-immediate-abort.rs new file mode 100644 index 0000000000000..5aec028a46cf7 --- /dev/null +++ b/tests/ui/panic-runtime/need-unwind-got-immediate-abort.rs @@ -0,0 +1,21 @@ +//@ build-fail +//@ aux-build:needs-unwind-immediate-abort.rs +//@ compile-flags:-C panic=immediate-abort -Zunstable-options +//@ no-prefer-dynamic +//@ add-core-stubs +//@ core-stubs-compile-flags: -Zunstable-options -Cpanic=immediate-abort + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +extern crate minicore; +extern crate needs_unwind_immediate_abort; + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + 0 +} + +//~? ERROR the crate `needs_unwind_immediate_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` diff --git a/tests/ui/panic-runtime/need-unwind-got-immediate-abort.stderr b/tests/ui/panic-runtime/need-unwind-got-immediate-abort.stderr new file mode 100644 index 0000000000000..8b3747d644f13 --- /dev/null +++ b/tests/ui/panic-runtime/need-unwind-got-immediate-abort.stderr @@ -0,0 +1,4 @@ +error: the crate `needs_unwind_immediate_abort` was compiled with a panic strategy which is incompatible with `immediate-abort` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/panic-abort.rs b/tests/ui/proc-macro/panic-abort.rs index 58e1d00643309..adedba4ebca61 100644 --- a/tests/ui/proc-macro/panic-abort.rs +++ b/tests/ui/proc-macro/panic-abort.rs @@ -2,4 +2,4 @@ //@ force-host //@ check-pass -//~? WARN building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic +//~? WARN building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic diff --git a/tests/ui/proc-macro/panic-abort.stderr b/tests/ui/proc-macro/panic-abort.stderr index a6e18614f8f06..3dd75768bc4ab 100644 --- a/tests/ui/proc-macro/panic-abort.stderr +++ b/tests/ui/proc-macro/panic-abort.stderr @@ -1,4 +1,4 @@ -warning: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic +warning: building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic warning: 1 warning emitted From df58fd8cf7710f7516c541769a141f0235978dab Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Sep 2025 10:48:18 -0400 Subject: [PATCH 0702/1900] Change the cfg to a dash --- compiler/rustc_span/src/symbol.rs | 2 +- library/alloc/src/alloc.rs | 4 +- library/alloc/src/raw_vec/mod.rs | 2 +- library/alloc/src/vec/mod.rs | 8 +-- library/core/src/cell.rs | 4 +- library/core/src/num/mod.rs | 4 +- library/core/src/option.rs | 8 +-- library/core/src/panicking.rs | 72 +++++++++---------- library/core/src/result.rs | 4 +- library/core/src/slice/index.rs | 4 +- library/core/src/slice/mod.rs | 4 +- .../core/src/slice/sort/shared/smallsort.rs | 4 +- library/core/src/str/mod.rs | 4 +- library/std/src/panicking.rs | 20 +++--- library/std/src/rt.rs | 4 +- library/std/src/thread/local.rs | 2 +- .../report-in-external-macros.cargo.stderr | 2 +- .../report-in-external-macros.rustc.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 2 +- 19 files changed, 78 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index efeb4371e4ca6..a2840828b19b6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1195,7 +1195,7 @@ symbols! { if_let_rescope, if_while_or_patterns, ignore, - immediate_abort, + immediate_abort: "immediate-abort", impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_assoc_type, diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 304b473f14df7..65c8206e9d462 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -408,12 +408,12 @@ pub const fn handle_alloc_error(layout: Layout) -> ! { } } - #[cfg(not(panic = "immediate_abort"))] + #[cfg(not(panic = "immediate-abort"))] { core::intrinsics::const_eval_select((layout,), ct_error, rt_error) } - #[cfg(panic = "immediate_abort")] + #[cfg(panic = "immediate-abort")] ct_error(layout) } diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 4853b84a01a32..b7c153b825dfd 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -23,7 +23,7 @@ mod tests; // ensure that the code generation related to these panics is minimal as there's // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] fn capacity_overflow() -> ! { panic!("capacity overflow"); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1a9041290ef17..ebdb86f98a857 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2020,7 +2020,7 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { #[cold] - #[cfg_attr(not(panic = "immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2102,7 +2102,7 @@ impl Vec { #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"] pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T { #[cold] - #[cfg_attr(not(panic = "immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2166,7 +2166,7 @@ impl Vec { #[rustc_confusables("delete", "take")] pub fn remove(&mut self, index: usize) -> T { #[cold] - #[cfg_attr(not(panic = "immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { @@ -2955,7 +2955,7 @@ impl Vec { A: Clone, { #[cold] - #[cfg_attr(not(panic = "immediate_abort"), inline(never))] + #[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[optimize(size)] fn assert_failed(at: usize, len: usize) -> ! { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index d7097e3b6fd4e..7d4a66640b106 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -778,7 +778,7 @@ impl Display for BorrowMutError { } // This ensures the panicking code is outlined from `borrow_mut` for `RefCell`. -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[cold] const fn panic_already_borrowed(err: BorrowMutError) -> ! { @@ -790,7 +790,7 @@ const fn panic_already_borrowed(err: BorrowMutError) -> ! { } // This ensures the panicking code is outlined from `borrow` for `RefCell`. -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[cold] const fn panic_already_mutably_borrowed(err: BorrowError) -> ! { diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3c4bfe28aa326..c75ee11d15efe 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,8 +1387,8 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) radix <= 16 && digits.len() <= size_of::() * 2 - is_signed_ty as usize } -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] +#[cfg_attr(panic = "immediate-abort", inline)] #[cold] #[track_caller] const fn from_ascii_radix_panic(radix: u32) -> ! { diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 10eec2cdfb6cf..430ee3470ac3f 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2161,8 +2161,8 @@ impl Option> { } } -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] +#[cfg_attr(panic = "immediate-abort", inline)] #[cold] #[track_caller] const fn unwrap_failed() -> ! { @@ -2170,8 +2170,8 @@ const fn unwrap_failed() -> ! { } // This is a separate function to reduce the code size of .expect() itself. -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] +#[cfg_attr(panic = "immediate-abort", inline)] #[cold] #[track_caller] const fn expect_failed(msg: &str) -> ! { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index da37d72960a61..3f30038dbc03e 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -49,14 +49,14 @@ compile_error!( /// the actual formatting into this shared place. // If panic=immediate-abort, inline the abort call, // otherwise avoid inlining because of it is cold path. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } @@ -81,8 +81,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { /// Like `panic_fmt`, but for non-unwinding panics. /// /// Has to be a separate function so that it can carry the `rustc_nounwind` attribute. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] // This attribute has the key side-effect that if the panic handler ignores `can_unwind` // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, @@ -97,7 +97,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // We don't unwind anyway at compile-time so we can call the regular `panic_fmt`. panic_fmt(fmt) } else #[track_caller] { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } @@ -128,8 +128,8 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo /// The underlying implementation of core's `panic!` macro when no formatting is used. // Never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = "panic"] // used by lints and miri for panics @@ -163,8 +163,8 @@ macro_rules! panic_const { // // never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible - #[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] - #[cfg_attr(panic = "immediate_abort", inline)] + #[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] + #[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = stringify!($lang)] @@ -219,8 +219,8 @@ pub mod panic_const { /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable @@ -229,8 +229,8 @@ pub const fn panic_nounwind(expr: &'static str) -> ! { } /// Like `panic_nounwind`, but also inhibits showing a backtrace. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[rustc_nounwind] pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true); @@ -262,25 +262,25 @@ pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access fn panic_bounds_check(index: usize, len: usize) -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } panic!("index out of bounds: the len is {len} but the index is {index}") } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref #[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } @@ -292,13 +292,13 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[lang = "panic_null_pointer_dereference"] // needed by codegen for panic on null pointer deref #[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind fn panic_null_pointer_dereference() -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } @@ -308,13 +308,13 @@ fn panic_null_pointer_dereference() -> ! { ) } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[lang = "panic_invalid_enum_construction"] // needed by codegen for panic on invalid enum construction. #[rustc_nounwind] // `CheckEnums` MIR pass requires this function to never unwind fn panic_invalid_enum_construction(source: u128) -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { super::intrinsics::abort() } @@ -331,8 +331,8 @@ fn panic_invalid_enum_construction(source: u128) -> ! { /// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[lang = "panic_cannot_unwind"] // needed by codegen for panic in nounwind function #[rustc_nounwind] fn panic_cannot_unwind() -> ! { @@ -347,8 +347,8 @@ fn panic_cannot_unwind() -> ! { /// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[lang = "panic_in_cleanup"] // needed by codegen for panic in nounwind function #[rustc_nounwind] fn panic_in_cleanup() -> ! { @@ -380,8 +380,8 @@ pub enum AssertKind { } /// Internal function for `assert_eq!` and `assert_ne!` macros -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[doc(hidden)] pub fn assert_failed( @@ -398,8 +398,8 @@ where } /// Internal function for `assert_match!` -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[doc(hidden)] pub fn assert_matches_failed( @@ -418,8 +418,8 @@ pub fn assert_matches_failed( } /// Non-generic version of the above functions, to avoid code bloat. -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] fn assert_failed_inner( kind: AssertKind, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 742f1c19112b7..c69762a728598 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1847,7 +1847,7 @@ impl Result, E> { } // This is a separate function to reduce the code size of the methods -#[cfg(not(panic = "immediate_abort"))] +#[cfg(not(panic = "immediate-abort"))] #[inline(never)] #[cold] #[track_caller] @@ -1859,7 +1859,7 @@ fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! { // that gets immediately thrown away, since vtables don't get cleaned up // by dead code elimination if a trait object is constructed even if it goes // unused -#[cfg(panic = "immediate_abort")] +#[cfg(panic = "immediate-abort")] #[inline] #[cold] #[track_caller] diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index c1a55c1f62a20..d3de81555c49d 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -31,8 +31,8 @@ where } } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! { if start > len { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index e8b2712e147c5..f7f5ee819b2e4 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3858,8 +3858,8 @@ impl [T] { { // The panic code path was put into a cold function to not bloat the // call site. - #[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] - #[cfg_attr(panic = "immediate_abort", inline)] + #[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] + #[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { const_panic!( diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 17858bf9f0d01..e555fce440872 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -840,8 +840,8 @@ unsafe fn bidirectional_merge bool>( } } -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold)] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] +#[cfg_attr(panic = "immediate-abort", inline)] fn panic_on_ord_violation() -> ! { // This is indicative of a logic bug in the user-provided comparison function or Ord // implementation. They are expected to implement a total order as explained in the Ord diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 910ea2948ee12..2e473d348b0b2 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -64,12 +64,12 @@ pub use validations::{next_code_point, utf8_char_width}; #[cold] #[track_caller] #[rustc_allow_const_fn_unstable(const_eval_select)] -#[cfg(not(panic = "immediate_abort"))] +#[cfg(not(panic = "immediate-abort"))] const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { crate::intrinsics::const_eval_select((s, begin, end), slice_error_fail_ct, slice_error_fail_rt) } -#[cfg(panic = "immediate_abort")] +#[cfg(panic = "immediate-abort")] const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { slice_error_fail_ct(s, begin, end) } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5d52bc366bd73..b7be869c4eb48 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -331,7 +331,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { #[cfg(not(test))] #[doc(hidden)] -#[cfg(panic = "immediate_abort")] +#[cfg(panic = "immediate-abort")] #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { /// A reason for forcing an immediate abort on panic. @@ -371,7 +371,7 @@ pub mod panic_count { #[cfg(not(test))] #[doc(hidden)] -#[cfg(not(panic = "immediate_abort"))] +#[cfg(not(panic = "immediate-abort"))] #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { use crate::cell::Cell; @@ -499,13 +499,13 @@ pub mod panic_count { pub use realstd::rt::panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. -#[cfg(panic = "immediate_abort")] +#[cfg(panic = "immediate-abort")] pub unsafe fn catch_unwind R>(f: F) -> Result> { Ok(f()) } /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. -#[cfg(not(panic = "immediate_abort"))] +#[cfg(not(panic = "immediate-abort"))] pub unsafe fn catch_unwind R>(f: F) -> Result> { union Data { f: ManuallyDrop, @@ -722,12 +722,12 @@ pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // lang item for CTFE panic support // never inline unless panic=immediate-abort to avoid code // bloat at the call sites as much as possible -#[cfg_attr(not(panic = "immediate_abort"), inline(never), cold, optimize(size))] -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))] +#[cfg_attr(panic = "immediate-abort", inline)] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval pub const fn begin_panic(msg: M) -> ! { - if cfg!(panic = "immediate_abort") { + if cfg!(panic = "immediate-abort") { intrinsics::abort() } @@ -861,7 +861,7 @@ fn panic_with_hook( /// This is the entry point for `resume_unwind`. /// It just forwards the payload to the panic runtime. -#[cfg_attr(panic = "immediate_abort", inline)] +#[cfg_attr(panic = "immediate-abort", inline)] pub fn resume_unwind(payload: Box) -> ! { panic_count::increase(false); @@ -890,14 +890,14 @@ pub fn resume_unwind(payload: Box) -> ! { /// on which to slap yer breakpoints. #[inline(never)] #[cfg_attr(not(test), rustc_std_internal_symbol)] -#[cfg(not(panic = "immediate_abort"))] +#[cfg(not(panic = "immediate-abort"))] fn rust_panic(msg: &mut dyn PanicPayload) -> ! { let code = unsafe { __rust_start_panic(msg) }; rtabort!("failed to initiate panic, error {code}") } #[cfg_attr(not(test), rustc_std_internal_symbol)] -#[cfg(panic = "immediate_abort")] +#[cfg(panic = "immediate-abort")] fn rust_panic(_: &mut dyn PanicPayload) -> ! { crate::intrinsics::abort(); } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index d98f2e692cd50..2717b7b469cee 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -39,11 +39,11 @@ fn __rust_abort() { // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { - #[cfg(not(panic = "immediate_abort"))] + #[cfg(not(panic = "immediate-abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } - #[cfg(panic = "immediate_abort")] + #[cfg(panic = "immediate-abort")] { let _ = format_args!($($t)*); } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index b49de8d1b6084..0a6f2e5d5088b 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -230,7 +230,7 @@ impl fmt::Display for AccessError { impl Error for AccessError {} // This ensures the panicking code is outlined from `with` for `LocalKey`. -#[cfg_attr(not(panic = "immediate_abort"), inline(never))] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] #[track_caller] #[cold] fn panic_access_error(err: AccessError) -> ! { diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index b99ce169b8900..4b5fc91c7eb91 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -18,7 +18,7 @@ warning: unexpected `cfg` condition value: `UNEXPECTED_VALUE` LL | cfg_macro::my_lib_macro_value!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` + = note: expected values for `panic` are: `abort`, `immediate-abort`, and `unwind` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_value` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro_value` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 8e1760bd969a7..0d99d061d28df 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -18,7 +18,7 @@ warning: unexpected `cfg` condition value: `UNEXPECTED_VALUE` LL | cfg_macro::my_lib_macro_value!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` + = note: expected values for `panic` are: `abort`, `immediate-abort`, and `unwind` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_value` crate for guidance on how handle this unexpected cfg = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index c34c20b172d50..43dd53939cb03 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -80,7 +80,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | panic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort`, `immediate_abort`, and `unwind` + = note: expected values for `panic` are: `abort`, `immediate-abort`, and `unwind` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From aef02fb864349a249207cbd14b51b9fe5f5691ca Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 21 Sep 2025 13:04:59 -0400 Subject: [PATCH 0703/1900] Explain tests and setting cfgs --- compiler/rustc_session/src/config/cfg.rs | 4 ++++ tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs | 5 +++++ tests/run-make-cargo/panic-immediate-abort-works/rmake.rs | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index fc36fe9d5d76e..f3d91ce4a5dd8 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -205,6 +205,10 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_none!(sym::overflow_checks); } + // We insert a cfg for the name of session's panic strategy. + // Since the ImmediateAbort strategy is new, it also sets cfg(panic="abort"), so that code + // which is trying to detect whether unwinding is enabled by checking for cfg(panic="abort") + // does not need to be updated. ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); if sess.panic_strategy() == PanicStrategy::ImmediateAbort { ins_sym!(sym::panic, PanicStrategy::Abort.desc_symbol()); diff --git a/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs b/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs index e91d1db83d1d1..d7a7a8bfd8c3b 100644 --- a/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs +++ b/tests/run-make-cargo/panic-immediate-abort-codegen/rmake.rs @@ -1,3 +1,8 @@ +// This is a codegen test which checks that when code is compiled with panic=immediate-abort, +// we get a `tail call void @llvm.trap()` in user code instead of a call into the standard +// library's panic formatting code (such as panic_fmt) or one of the numerous panic outlining shims +// (such as slice_index_fail). + #![deny(warnings)] use run_make_support::{cargo, llvm_filecheck, path, rfs, target}; diff --git a/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs index a3c5877a6a19d..bb15bd41e79c4 100644 --- a/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs +++ b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs @@ -1,3 +1,9 @@ +// This test ensures we are able to compile and link a simple binary with panic=immediate-abort. +// The test panic-immediate-abort-codegen checks that panic strategy produces the desired codegen, +// but is based on compiling a library crate (which is the norm for codegen tests because it is +// cleaner and more portable). So this test ensures that we didn't mix up a cfg or a compiler +// implementation detail in a way that makes panic=immediate-abort encounter errors at link time. + //@ needs-target-std #![deny(warnings)] From 87a00f67ba4c8148e6aa2e1768025b9d51a0fa8b Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 16 Sep 2025 15:48:43 +0200 Subject: [PATCH 0704/1900] std: merge definitions of `StdioPipes` All platforms define this structure the same way, so we can just put it in the `process` module directly. --- library/std/src/process.rs | 13 +++++++++++-- library/std/src/sys/process/mod.rs | 2 +- library/std/src/sys/process/uefi.rs | 9 +-------- library/std/src/sys/process/unix/common.rs | 9 +-------- library/std/src/sys/process/unix/fuchsia.rs | 1 + library/std/src/sys/process/unix/mod.rs | 2 +- library/std/src/sys/process/unix/unix.rs | 1 + library/std/src/sys/process/unix/unsupported.rs | 1 + library/std/src/sys/process/unix/vxworks.rs | 1 + library/std/src/sys/process/unsupported.rs | 9 +-------- library/std/src/sys/process/windows.rs | 7 +------ 11 files changed, 21 insertions(+), 34 deletions(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 0883e56342c8f..5c0ac526a36c9 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -268,8 +268,8 @@ impl AsInner for Child { } } -impl FromInner<(imp::Process, imp::StdioPipes)> for Child { - fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { +impl FromInner<(imp::Process, StdioPipes)> for Child { + fn from_inner((handle, io): (imp::Process, StdioPipes)) -> Child { Child { handle, stdin: io.stdin.map(ChildStdin::from_inner), @@ -296,6 +296,15 @@ impl fmt::Debug for Child { } } +/// The pipes connected to a spawned process. +/// +/// Used to pass pipe handles between this module and [`imp`]. +pub(crate) struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + /// A handle to a child process's standard input (stdin). /// /// This struct is used in the [`stdin`] field on [`Child`]. diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 9ef5496e57a05..a1ed0cd2cdd2d 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -24,7 +24,7 @@ mod env; pub use env::CommandEnvs; pub use imp::{ - Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, StdioPipes, + Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, }; #[cfg(any( diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 4864c58698817..11c8b682bb9bc 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -6,6 +6,7 @@ pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pal::helpers; use crate::sys::pal::os::error_string; @@ -27,14 +28,6 @@ pub struct Command { env: CommandEnv, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - #[derive(Copy, Clone, Debug)] pub enum Stdio { Inherit, diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index ea45b08e90a34..1d5909e99bacc 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -9,6 +9,7 @@ use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fd::FileDesc; use crate::sys::fs::File; #[cfg(not(target_os = "fuchsia"))] @@ -104,14 +105,6 @@ pub struct Command { setsid: bool, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - // passed to do_exec() with configuration of what the child stdio should look // like #[cfg_attr(target_os = "vita", allow(dead_code))] diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs index d71be510b6afe..3fae5ec1468b1 100644 --- a/library/std/src/sys/process/unix/fuchsia.rs +++ b/library/std/src/sys/process/unix/fuchsia.rs @@ -2,6 +2,7 @@ use libc::{c_int, size_t}; use super::common::*; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::pal::fuchsia::*; use crate::{fmt, io, mem, ptr}; diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index b4cf060fba9bf..cda1bf74f1cad 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -23,5 +23,5 @@ cfg_select! { pub use imp::{ExitStatus, ExitStatusError, Process}; -pub use self::common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; +pub use self::common::{Command, CommandArgs, ExitCode, Stdio}; pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 11d48878727b0..7d944f2f7eef1 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -13,6 +13,7 @@ use libc::{gid_t, uid_t}; use super::common::*; use crate::io::{self, Error, ErrorKind}; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::cvt; #[cfg(target_os = "linux")] use crate::sys::pal::linux::pidfd::PidFd; diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs index 87403cd50f822..9bda394f24659 100644 --- a/library/std/src/sys/process/unix/unsupported.rs +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -3,6 +3,7 @@ use libc::{c_int, pid_t}; use super::common::*; use crate::io; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::pal::unsupported::*; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index b9298f5fa44c1..346ca6d74c9bd 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -4,6 +4,7 @@ use libc::{self, RTP_ID, c_char, c_int}; use super::common::*; use crate::io::{self, ErrorKind}; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::{cvt, thread}; use crate::{fmt, sys}; diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 469922c78aca2..636465b68e541 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -3,6 +3,7 @@ pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; @@ -23,14 +24,6 @@ pub struct Command { stderr: Option, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - #[derive(Debug)] pub enum Stdio { Inherit, diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index f9e15b824757d..1f2001bdc2040 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -15,6 +15,7 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; use crate::os::windows::process::ProcThreadAttributeList; use crate::path::{Path, PathBuf}; +use crate::process::StdioPipes; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS}; @@ -169,12 +170,6 @@ pub enum Stdio { Handle(Handle), } -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - impl Command { pub fn new(program: &OsStr) -> Command { Command { From 5f0a68eb220c4aa780e0206ee6c6273413d8d5fd Mon Sep 17 00:00:00 2001 From: The 8472 Date: Fri, 15 Aug 2025 01:20:37 +0200 Subject: [PATCH 0705/1900] regression test for https://github.com/rust-lang/rust/issues/117763 --- .../issues/cows-dont-have-branches-117763.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs diff --git a/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs new file mode 100644 index 0000000000000..b97729fa14657 --- /dev/null +++ b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Copt-level=3 +//@ needs-deterministic-layouts + +// Currently Vec and &[T] have layouts that start with (pointer, len) +// which makes the conversion branchless. +// A nice-to-have property, not guaranteed. +#![crate_type = "cdylib"] + +// CHECK-LABEL: @branchless_cow_slices +#[no_mangle] +pub fn branchless_cow_slices<'a>(cow: &'a std::borrow::Cow<'a, [u8]>) -> &'a [u8] { + // CHECK-NOT: br + // CHECK-NOT: select + // CHECK-NOT: icmp + // CHECK: ret { ptr, {{i32|i64}} } + &*cow +} From 97b2292b065f825d482899149001a74b6a5450c4 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 17 Sep 2025 10:26:52 -0400 Subject: [PATCH 0706/1900] Allow shared access to `Exclusive` when `T: Sync` --- library/core/src/sync/exclusive.rs | 114 ++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 10 deletions(-) diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs index cf086bf4f5080..f181c5514f256 100644 --- a/library/core/src/sync/exclusive.rs +++ b/library/core/src/sync/exclusive.rs @@ -1,28 +1,32 @@ //! Defines [`Exclusive`]. +use core::cmp::Ordering; use core::fmt; use core::future::Future; -use core::marker::Tuple; +use core::hash::{Hash, Hasher}; +use core::marker::{StructuralPartialEq, Tuple}; use core::ops::{Coroutine, CoroutineState}; use core::pin::Pin; use core::task::{Context, Poll}; -/// `Exclusive` provides only _mutable_ access, also referred to as _exclusive_ -/// access to the underlying value. It provides no _immutable_, or _shared_ -/// access to the underlying value. +/// `Exclusive` provides _mutable_ access, also referred to as _exclusive_ +/// access to the underlying value. However, it only permits _immutable_, or _shared_ +/// access to the underlying value when that value is [`Sync`]. /// /// While this may seem not very useful, it allows `Exclusive` to _unconditionally_ -/// implement [`Sync`]. Indeed, the safety requirements of `Sync` state that for `Exclusive` +/// implement `Sync`. Indeed, the safety requirements of `Sync` state that for `Exclusive` /// to be `Sync`, it must be sound to _share_ across threads, that is, it must be sound -/// for `&Exclusive` to cross thread boundaries. By design, a `&Exclusive` has no API -/// whatsoever, making it useless, thus harmless, thus memory safe. +/// for `&Exclusive` to cross thread boundaries. By design, a `&Exclusive` for non-`Sync` T +/// has no API whatsoever, making it useless, thus harmless, thus memory safe. /// /// Certain constructs like [`Future`]s can only be used with _exclusive_ access, /// and are often `Send` but not `Sync`, so `Exclusive` can be used as hint to the /// Rust compiler that something is `Sync` in practice. /// /// ## Examples -/// Using a non-`Sync` future prevents the wrapping struct from being `Sync` +/// +/// Using a non-`Sync` future prevents the wrapping struct from being `Sync`: +/// /// ```compile_fail /// use core::cell::Cell; /// @@ -43,7 +47,8 @@ use core::task::{Context, Poll}; /// ``` /// /// `Exclusive` ensures the struct is `Sync` without stripping the future of its -/// functionality. +/// functionality: +/// /// ``` /// #![feature(exclusive_wrapper)] /// use core::cell::Cell; @@ -66,6 +71,7 @@ use core::task::{Context, Poll}; /// ``` /// /// ## Parallels with a mutex +/// /// In some sense, `Exclusive` can be thought of as a _compile-time_ version of /// a mutex, as the borrow-checker guarantees that only one `&mut` can exist /// for any value. This is a parallel with the fact that @@ -75,7 +81,7 @@ use core::task::{Context, Poll}; #[doc(alias = "SyncWrapper")] #[doc(alias = "SyncCell")] #[doc(alias = "Unique")] -// `Exclusive` can't have `PartialOrd`, `Clone`, etc. impls as they would +// `Exclusive` can't have derived `PartialOrd`, `Clone`, etc. impls as they would // use `&` access to the inner value, violating the `Sync` impl's safety // requirements. #[derive(Default)] @@ -195,6 +201,17 @@ where } } +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Fn for Exclusive +where + F: Sync + Fn, + Args: Tuple, +{ + extern "rust-call" fn call(&self, args: Args) -> Self::Output { + self.as_ref().call(args) + } +} + #[unstable(feature = "exclusive_wrapper", issue = "98407")] impl Future for Exclusive where @@ -221,3 +238,80 @@ where G::resume(self.get_pin_mut(), arg) } } + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl AsRef for Exclusive +where + T: Sync + ?Sized, +{ + #[inline] + fn as_ref(&self) -> &T { + &self.inner + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Clone for Exclusive +where + T: Sync + Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self { inner: self.inner.clone() } + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Copy for Exclusive where T: Sync + Copy {} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl PartialEq> for Exclusive +where + T: Sync + PartialEq + ?Sized, + U: Sync + ?Sized, +{ + #[inline] + fn eq(&self, other: &Exclusive) -> bool { + self.inner == other.inner + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl StructuralPartialEq for Exclusive where T: Sync + StructuralPartialEq + ?Sized {} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Eq for Exclusive where T: Sync + Eq + ?Sized {} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Hash for Exclusive +where + T: Sync + Hash + ?Sized, +{ + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(&self.inner, state) + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl PartialOrd> for Exclusive +where + T: Sync + PartialOrd + ?Sized, + U: Sync + ?Sized, +{ + #[inline] + fn partial_cmp(&self, other: &Exclusive) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "98407")] +impl Ord for Exclusive +where + T: Sync + Ord + ?Sized, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.inner.cmp(&other.inner) + } +} From b3c24356884d9cb333ff71e49e41b648a7dec1e2 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Sun, 21 Sep 2025 21:13:03 +0200 Subject: [PATCH 0707/1900] Make mips64el-unknown-linux-muslabi64 link dynamically I missed this target when I changed all the other tier 3 targets. Only realized that this one was still statically linked when I looked at the list of targets in the test later. Signed-off-by: Jens Reidel --- .../src/spec/targets/mips64el_unknown_linux_muslabi64.rs | 2 -- tests/run-make/musl-default-linking/rmake.rs | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index d42e097b0fd8b..38c3c7dfaa1bb 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -5,8 +5,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips64r2".into(); base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); - // FIXME(compiler-team#422): musl targets should be dynamically linked by default. - base.crt_static_default = true; Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), diff --git a/tests/run-make/musl-default-linking/rmake.rs b/tests/run-make/musl-default-linking/rmake.rs index 1b30c538b5e30..e9d09e359c686 100644 --- a/tests/run-make/musl-default-linking/rmake.rs +++ b/tests/run-make/musl-default-linking/rmake.rs @@ -4,7 +4,7 @@ use run_make_support::{rustc, serde_json}; // Per https://github.com/rust-lang/compiler-team/issues/422, // we should be trying to move these targets to dynamically link // musl libc by default. -//@ needs-llvm-components: aarch64 arm mips powerpc x86 +//@ needs-llvm-components: aarch64 arm powerpc x86 static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "aarch64-unknown-linux-musl", "arm-unknown-linux-musleabi", @@ -14,7 +14,6 @@ static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "armv7-unknown-linux-musleabihf", "i586-unknown-linux-musl", "i686-unknown-linux-musl", - "mips64el-unknown-linux-muslabi64", "powerpc64le-unknown-linux-musl", "x86_64-unknown-linux-musl", ]; From 3565b0699d6830dc31732afa96272bcbd1f83606 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 19 Sep 2025 20:39:47 +0200 Subject: [PATCH 0708/1900] emit attribute for readonly non-pure inline assembly --- compiler/rustc_codegen_llvm/src/asm.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 5 ++ tests/codegen-llvm/asm/readonly-not-pure.rs | 48 +++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/asm/readonly-not-pure.rs diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index b79176e909818..838eb0db0403d 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -340,8 +340,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { attrs.push(llvm::AttributeKind::WillReturn.create_attr(self.cx.llcx)); } else if options.contains(InlineAsmOptions::NOMEM) { attrs.push(llvm::MemoryEffects::InaccessibleMemOnly.create_attr(self.cx.llcx)); - } else { - // LLVM doesn't have an attribute to represent ReadOnly + SideEffect + } else if options.contains(InlineAsmOptions::READONLY) { + attrs.push(llvm::MemoryEffects::ReadOnlyNotPure.create_attr(self.cx.llcx)); } attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs }); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9a86e4373d8f4..fd972f371df49 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -710,6 +710,7 @@ pub(crate) enum MemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, } /// LLVMOpcode diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321fd..414274f24fb50 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -553,6 +553,7 @@ enum class LLVMRustMemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, }; extern "C" LLVMAttributeRef @@ -568,6 +569,10 @@ LLVMRustCreateMemoryEffectsAttr(LLVMContextRef C, case LLVMRustMemoryEffects::InaccessibleMemOnly: return wrap(Attribute::getWithMemoryEffects( *unwrap(C), MemoryEffects::inaccessibleMemOnly())); + case LLVMRustMemoryEffects::ReadOnlyNotPure: + return wrap(Attribute::getWithMemoryEffects( + *unwrap(C), + MemoryEffects::readOnly() | MemoryEffects::inaccessibleMemOnly())); default: report_fatal_error("bad MemoryEffects."); } diff --git a/tests/codegen-llvm/asm/readonly-not-pure.rs b/tests/codegen-llvm/asm/readonly-not-pure.rs new file mode 100644 index 0000000000000..a3c0e276c7f5e --- /dev/null +++ b/tests/codegen-llvm/asm/readonly-not-pure.rs @@ -0,0 +1,48 @@ +//@ add-core-stubs +//@ compile-flags: -Copt-level=3 --target x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 + +#![crate_type = "rlib"] +#![feature(no_core)] +#![no_core] + +// Test that when an inline assembly block specifies `readonly` but not `pure`, a detailed +// `MemoryEffects` is provided to LLVM: this assembly block is not allowed to perform writes, +// but it may have side-effects. + +extern crate minicore; +use minicore::*; + +pub static mut VAR: i32 = 0; + +// CHECK-LABEL: @no_options +// CHECK: call i32 asm +#[no_mangle] +pub unsafe fn no_options() -> i32 { + VAR = 1; + let _ignored: i32; + asm!("mov {0}, 1", out(reg) _ignored); + VAR +} + +// CHECK-LABEL: @readonly_pure +// CHECK-NOT: call i32 asm +#[no_mangle] +pub unsafe fn readonly_pure() -> i32 { + VAR = 1; + let _ignored: i32; + asm!("mov {0}, 1", out(reg) _ignored, options(pure, readonly)); + VAR +} + +// CHECK-LABEL: @readonly_not_pure +// CHECK: call i32 asm {{.*}} #[[ATTR:[0-9]+]] +#[no_mangle] +pub unsafe fn readonly_not_pure() -> i32 { + VAR = 1; + let _ignored: i32; + asm!("mov {0}, 1", out(reg) _ignored, options(readonly)); + VAR +} + +// CHECK: attributes #[[ATTR]] = { nounwind memory(read, inaccessiblemem: readwrite) } From 27cad5a61602e0435967d2c5c34f0d94178ab1be Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 13 Aug 2025 20:30:34 +0200 Subject: [PATCH 0709/1900] fix `unnecessary_semicolon`: FN on `#[feature(stmt_expr_attributes)]` --- clippy_lints/src/unnecessary_semicolon.rs | 3 ++- ...ary_semicolon_feature_stmt_expr_attributes.fixed | 13 +++++++++++++ ...essary_semicolon_feature_stmt_expr_attributes.rs | 13 +++++++++++++ ...ry_semicolon_feature_stmt_expr_attributes.stderr | 11 +++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index 76e24b6bf8058..e1e450a52fdf0 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr).is_unit() // if a stmt has attrs, then turning it into an expr will break the code, since attrs aren't allowed on exprs - && cx.tcx.hir_attrs(stmt.hir_id).is_empty() + // -- unless the corresponding feature is enabled + && (cx.tcx.hir_attrs(stmt.hir_id).is_empty() || cx.tcx.features().stmt_expr_attributes()) { if let Some(block_is_unit) = self.is_last_in_block(stmt) { if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed new file mode 100644 index 0000000000000..b90ae1365bbf2 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + } + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs new file mode 100644 index 0000000000000..606c901c20d35 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + }; + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr new file mode 100644 index 0000000000000..3e98a92ef299c --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr @@ -0,0 +1,11 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs:11:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: aborting due to 1 previous error + From 0138bbd4958f2247770105ff63e9129e779da569 Mon Sep 17 00:00:00 2001 From: "U. Lasiotus" Date: Sun, 21 Sep 2025 08:54:25 -0700 Subject: [PATCH 0710/1900] Add x86_64-unknown-motor (Motor OS) tier 3 target Add the initial no-std Motor OS compiler target. Motor OS has been developed for several years in the open: https://github.com/moturus/motor-os. It has a more or less full implementation of Rust std library, as well as tokio/mio ports. Build instructions can be found here: https://github.com/moturus/motor-os/blob/main/docs/build.md. Signed-off-by: U. Lasiotus --- compiler/rustc_target/src/spec/base/mod.rs | 1 + compiler/rustc_target/src/spec/base/motor.rs | 34 ++++++++++++++ compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/targets/x86_64_unknown_motor.rs | 38 ++++++++++++++++ src/bootstrap/src/core/sanity.rs | 5 +++ src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/motor.md | 45 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 ++ tests/ui/check-cfg/cfg-crate-features.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 4 +- 10 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_target/src/spec/base/motor.rs create mode 100644 compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs create mode 100644 src/doc/rustc/src/platform-support/motor.md diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index be15da7329d79..6ab8597a4ecb0 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -21,6 +21,7 @@ pub(crate) mod linux_uclibc; pub(crate) mod linux_wasm; pub(crate) mod lynxos178; pub(crate) mod managarm_mlibc; +pub(crate) mod motor; pub(crate) mod msvc; pub(crate) mod netbsd; pub(crate) mod nto_qnx; diff --git a/compiler/rustc_target/src/spec/base/motor.rs b/compiler/rustc_target/src/spec/base/motor.rs new file mode 100644 index 0000000000000..18485b2cef2fc --- /dev/null +++ b/compiler/rustc_target/src/spec/base/motor.rs @@ -0,0 +1,34 @@ +use crate::spec::{ + Cc, FramePointer, LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions, +}; + +pub(crate) fn opts() -> TargetOptions { + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "-e", + "motor_start", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-u", + "__rust_abort", + ], + ); + TargetOptions { + os: "motor".into(), + executables: true, + // TLS is false below because if true, the compiler assumes + // we handle TLS at the ELF loading level, which we don't. + // We use "OS level" TLS (see thread/local.rs in stdlib). + has_thread_local: false, + frame_pointer: FramePointer::NonLeaf, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + main_needs_argc_argv: true, + panic_strategy: PanicStrategy::Abort, + pre_link_args, + stack_probes: StackProbeType::Inline, + supports_stack_protector: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f705af52bd868..c40358af59388 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1642,6 +1642,7 @@ supported_targets! { ("aarch64-unknown-hermit", aarch64_unknown_hermit), ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unknown-motor", x86_64_unknown_motor), ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs new file mode 100644 index 0000000000000..0fd43357a7664 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + CodeModel, LinkSelfContainedDefault, LldFlavor, RelocModel, RelroLevel, Target, base, +}; + +pub(crate) fn target() -> Target { + let mut base = base::motor::opts(); + base.cpu = "x86-64".into(); + base.max_atomic_width = Some(64); + base.code_model = Some(CodeModel::Small); + + // We want fully static relocatable binaries. It was surprisingly + // difficult to make it happen reliably, especially various + // linker-related options below. Mostly trial and error. + base.position_independent_executables = true; + base.relro_level = RelroLevel::Full; + base.static_position_independent_executables = true; + base.relocation_model = RelocModel::Pic; + base.lld_flavor_json = LldFlavor::Ld; + base.link_self_contained = LinkSelfContainedDefault::True; + base.dynamic_linking = false; + base.crt_static_default = true; + base.crt_static_respected = true; + + Target { + llvm_target: "x86_64-unknown-none-elf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Motor OS".into()), + tier: Some(3), + host_tools: None, + std: None, + }, + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 68202500d9791..91d80c96e4268 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -38,6 +38,7 @@ const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "aarch64_be-unknown-hermit", "aarch64_be-unknown-none-softfloat", + "x86_64-unknown-motor", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -239,6 +240,10 @@ than building it. continue; } + if target.contains("motor") { + continue; + } + // skip check for cross-targets if skip_target_sanity && target != &build.host_target { continue; diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e5e46f7263751..99c8e365f5cf5 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -431,6 +431,7 @@ target | std | host | notes `x86_64-unknown-l4re-uclibc` | ? | | [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`x86_64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | x86_64 Managarm +[`x86_64-unknown-motor`[(platform-support/motor.md) | ? | | x86_64 Motor OS [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD [`x86_64-unknown-trusty`](platform-support/trusty.md) | ✓ | | `x86_64-uwp-windows-gnu` | ✓ | | diff --git a/src/doc/rustc/src/platform-support/motor.md b/src/doc/rustc/src/platform-support/motor.md new file mode 100644 index 0000000000000..e7aa7b23f3a54 --- /dev/null +++ b/src/doc/rustc/src/platform-support/motor.md @@ -0,0 +1,45 @@ +# `x86_64-unknown-motor` + +**Tier: 3** + +[Motor OS](https://github.com/moturus/motor-os) is a new operating system +for virtualized environments. + +## Target maintainers + +[@lasiotus](https://github.com/lasiotus) + +## Requirements + +This target is cross-compiled. There are no special requirements for the host. + +Motor OS uses the ELF file format. + +## Building the target + +The target can be built by enabling it for a `rustc` build, for example: + +```toml +[build] +build-stage = 2 +target = ["x86_64-unknown-motor"] +``` + +## Building Rust programs + +Rust standard library is fully supported/implemented, but is not yet part of +the official Rust repo, so an out-of-tree building process should be +followed, as described in the +[build doc](https://github.com/moturus/motor-os/blob/main/docs/build.md). + +## Testing + +Cross-compiled Rust binaries and test artifacts can be executed in Motor OS VMs, +as described in e.g. +[Hello Motor OS](https://github.com/moturus/motor-os/blob/main/docs/recipes/hello-motor-os.md) +example. + +## Cross-compilation toolchains and C code + +C code can be compiled as part of Rust cargo projects. However, there is +no libc support. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index b5c116cdfef0d..ebea9fe40f518 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -658,6 +658,9 @@ //@ revisions: x86_64_unknown_managarm_mlibc //@ [x86_64_unknown_managarm_mlibc] compile-flags: --target x86_64-unknown-managarm-mlibc //@ [x86_64_unknown_managarm_mlibc] needs-llvm-components: x86 +//@ revisions: x86_64_unknown_motor +//@ [x86_64_unknown_motor] compile-flags: --target x86_64-unknown-motor +//@ [x86_64_unknown_motor] needs-llvm-components: x86 //@ revisions: x86_64_unknown_netbsd //@ [x86_64_unknown_netbsd] compile-flags: --target x86_64-unknown-netbsd //@ [x86_64_unknown_netbsd] needs-llvm-components: x86 diff --git a/tests/ui/check-cfg/cfg-crate-features.stderr b/tests/ui/check-cfg/cfg-crate-features.stderr index 6b2e628e12ea1..39fee52a909b6 100644 --- a/tests/ui/check-cfg/cfg-crate-features.stderr +++ b/tests/ui/check-cfg/cfg-crate-features.stderr @@ -24,7 +24,7 @@ warning: unexpected `cfg` condition value: `does_not_exist` LL | #![cfg(not(target(os = "does_not_exist")))] | ^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, and `tvos` and 11 more + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, and `trusty` and 12 more = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 6490fc63fd766..df2357696a377 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -274,7 +274,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 28 warnings emitted From c54a953402aaa132c363b578f59cd30e39cb0abb Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 21 Sep 2025 11:18:30 +0300 Subject: [PATCH 0711/1900] Early return in `visibility_print_with_space` --- src/librustdoc/html/format.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8c75f301841f3..950d3a08827d2 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1422,10 +1422,13 @@ pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) f.write_str("#[doc(hidden)] ")?; } - match item.visibility(cx.tcx()) { - None => {} - Some(ty::Visibility::Public) => f.write_str("pub ")?, - Some(ty::Visibility::Restricted(vis_did)) => { + let Some(vis) = item.visibility(cx.tcx()) else { + return Ok(()); + }; + + match vis { + ty::Visibility::Public => f.write_str("pub ")?, + ty::Visibility::Restricted(vis_did) => { // FIXME(camelid): This may not work correctly if `item_did` is a module. // However, rustdoc currently never displays a module's // visibility, so it shouldn't matter. From cdf96614cf19643a0337e8b012d3e72f1bfa3cd2 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 21 Sep 2025 10:55:50 +0300 Subject: [PATCH 0712/1900] Re-use some existing util fns --- src/librustdoc/html/format.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 950d3a08827d2..fa55de4239a2a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -965,10 +965,7 @@ fn fmt_type( write!(f, "]") } clean::RawPointer(m, t) => { - let m = match m { - hir::Mutability::Mut => "mut", - hir::Mutability::Not => "const", - }; + let m = m.ptr_str(); if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() { let ty = t.print(cx); @@ -1406,12 +1403,13 @@ impl clean::FnDecl { } fn print_output(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| match &self.output { - clean::Tuple(tys) if tys.is_empty() => Ok(()), - ty if f.alternate() => { - write!(f, " -> {:#}", ty.print(cx)) + fmt::from_fn(move |f| { + if self.output.is_unit() { + return Ok(()); } - ty => write!(f, " -> {}", ty.print(cx)), + + f.write_str(if f.alternate() { " -> " } else { " -> " })?; + self.output.print(cx).fmt(f) }) } } From f1d6e000b500bd23a9f75716a161bc015ab49273 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 21 Sep 2025 17:26:39 -0400 Subject: [PATCH 0713/1900] Bless UI tests --- tests/ui/explicit-tail-calls/callee_is_weird.stderr | 2 +- tests/ui/impl-trait/where-allowed.stderr | 2 ++ tests/ui/traits/next-solver/well-formed-in-relate.stderr | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/ui/explicit-tail-calls/callee_is_weird.stderr b/tests/ui/explicit-tail-calls/callee_is_weird.stderr index a4e5a38ce3320..9a5da28b559e3 100644 --- a/tests/ui/explicit-tail-calls/callee_is_weird.stderr +++ b/tests/ui/explicit-tail-calls/callee_is_weird.stderr @@ -12,7 +12,7 @@ error: tail calls can only be performed with function definitions or pointers LL | become (&mut &std::sync::Exclusive::new(f))() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: callee has type `Exclusive` + = note: callee has type `&Exclusive` error: tail calls can only be performed with function definitions or pointers --> $DIR/callee_is_weird.rs:22:12 diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 08caff326c470..4d8f23bf7ca64 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -387,6 +387,8 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani where A: Tuple, F: Fn, F: ?Sized; - impl Fn for Box where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; + - impl Fn for Exclusive + where F: Sync, F: Fn, Args: Tuple; error[E0118]: no nominal type found for inherent implementation --> $DIR/where-allowed.rs:240:1 diff --git a/tests/ui/traits/next-solver/well-formed-in-relate.stderr b/tests/ui/traits/next-solver/well-formed-in-relate.stderr index 5294a072d3123..d79e465b3e387 100644 --- a/tests/ui/traits/next-solver/well-formed-in-relate.stderr +++ b/tests/ui/traits/next-solver/well-formed-in-relate.stderr @@ -12,6 +12,8 @@ LL | x = unconstrained_map(); where A: Tuple, F: Fn, F: ?Sized; - impl Fn for Box where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; + - impl Fn for Exclusive + where F: Sync, F: Fn, Args: Tuple; note: required by a bound in `unconstrained_map` --> $DIR/well-formed-in-relate.rs:21:25 | From 0ed31beba681fab11b8137b81c981c57cab06308 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 23:27:21 +0200 Subject: [PATCH 0714/1900] rename `mut_reference` to `unnecessary_mut_passed` The lint was probably renamed at some point, but the files weren't. This made it annoying to search for the lint. --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...reference.rs => unnecessary_mut_passed.rs} | 0 ...nce.fixed => unnecessary_mut_passed.fixed} | 0 ...reference.rs => unnecessary_mut_passed.rs} | 0 ...e.stderr => unnecessary_mut_passed.stderr} | 24 +++++++++---------- 6 files changed, 15 insertions(+), 15 deletions(-) rename clippy_lints/src/{mut_reference.rs => unnecessary_mut_passed.rs} (100%) rename tests/ui/{mut_reference.fixed => unnecessary_mut_passed.fixed} (100%) rename tests/ui/{mut_reference.rs => unnecessary_mut_passed.rs} (100%) rename tests/ui/{mut_reference.stderr => unnecessary_mut_passed.stderr} (80%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5563b8094f01b..89178290e1a2f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -535,7 +535,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, - crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, @@ -752,6 +751,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, + crate::unnecessary_mut_passed::UNNECESSARY_MUT_PASSED_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..1d6c836691547 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -253,7 +253,6 @@ mod multiple_bound_locations; mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; -mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; @@ -375,6 +374,7 @@ mod unit_types; mod unnecessary_box_returns; mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; +mod unnecessary_mut_passed; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_semicolon; @@ -483,7 +483,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); - store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/unnecessary_mut_passed.rs similarity index 100% rename from clippy_lints/src/mut_reference.rs rename to clippy_lints/src/unnecessary_mut_passed.rs diff --git a/tests/ui/mut_reference.fixed b/tests/ui/unnecessary_mut_passed.fixed similarity index 100% rename from tests/ui/mut_reference.fixed rename to tests/ui/unnecessary_mut_passed.fixed diff --git a/tests/ui/mut_reference.rs b/tests/ui/unnecessary_mut_passed.rs similarity index 100% rename from tests/ui/mut_reference.rs rename to tests/ui/unnecessary_mut_passed.rs diff --git a/tests/ui/mut_reference.stderr b/tests/ui/unnecessary_mut_passed.stderr similarity index 80% rename from tests/ui/mut_reference.stderr rename to tests/ui/unnecessary_mut_passed.stderr index 5ecfaa37416ba..c69a637bf4088 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -1,5 +1,5 @@ error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:56:15 + --> tests/ui/unnecessary_mut_passed.rs:56:15 | LL | takes_ref(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` @@ -8,67 +8,67 @@ LL | takes_ref(&mut 42); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:58:19 + --> tests/ui/unnecessary_mut_passed.rs:58:19 | LL | takes_ref_ref(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:60:22 + --> tests/ui/unnecessary_mut_passed.rs:60:22 | LL | takes_ref_refmut(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:62:21 + --> tests/ui/unnecessary_mut_passed.rs:62:21 | LL | takes_raw_const(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:66:12 + --> tests/ui/unnecessary_mut_passed.rs:66:12 | LL | as_ptr(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:69:12 + --> tests/ui/unnecessary_mut_passed.rs:69:12 | LL | as_ptr(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:72:12 + --> tests/ui/unnecessary_mut_passed.rs:72:12 | LL | as_ptr(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:75:12 + --> tests/ui/unnecessary_mut_passed.rs:75:12 | LL | as_ptr(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:80:25 + --> tests/ui/unnecessary_mut_passed.rs:80:25 | LL | my_struct.takes_ref(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:82:29 + --> tests/ui/unnecessary_mut_passed.rs:82:29 | LL | my_struct.takes_ref_ref(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:84:32 + --> tests/ui/unnecessary_mut_passed.rs:84:32 | LL | my_struct.takes_ref_refmut(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:86:31 + --> tests/ui/unnecessary_mut_passed.rs:86:31 | LL | my_struct.takes_raw_const(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` From 20671600a2873139a61c39128a359225c8580d3c Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 21 Sep 2025 11:13:37 +0300 Subject: [PATCH 0715/1900] Introduce "wrapper" helpers to rustdoc --- src/librustdoc/clean/cfg.rs | 78 ++++----- src/librustdoc/display.rs | 86 ++++++++- src/librustdoc/html/format.rs | 321 ++++++++++++++-------------------- src/librustdoc/lib.rs | 1 + 4 files changed, 248 insertions(+), 238 deletions(-) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index e204e1788baa8..8feca1367fc9e 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -3,16 +3,16 @@ // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619), // switch to use those structures instead. -use std::fmt::{self, Write}; -use std::{mem, ops}; +use std::{fmt, mem, ops}; +use itertools::Either; use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_data_structures::fx::FxHashSet; use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; -use crate::display::Joined as _; +use crate::display::{Joined as _, MaybeDisplay, Wrapped}; use crate::html::escape::Escape; #[cfg(test)] @@ -376,27 +376,20 @@ impl Format { Format::LongPlain => false, } } + + fn escape(self, s: &str) -> impl fmt::Display { + if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) } + } } /// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used. struct Display<'a>(&'a Cfg, Format); -fn write_with_opt_paren( - fmt: &mut fmt::Formatter<'_>, - has_paren: bool, - obj: T, -) -> fmt::Result { - if has_paren { - fmt.write_char('(')?; - } - obj.fmt(fmt)?; - if has_paren { - fmt.write_char(')')?; +impl Display<'_> { + fn code_wrappers(&self) -> Wrapped<&'static str> { + if self.1.is_html() { Wrapped::with("", "") } else { Wrapped::with("`", "`") } } - Ok(()) -} -impl Display<'_> { fn display_sub_cfgs( &self, fmt: &mut fmt::Formatter<'_>, @@ -427,20 +420,17 @@ impl Display<'_> { sub_cfgs .iter() .map(|sub_cfg| { - fmt::from_fn(move |fmt| { - if let Cfg::Cfg(_, Some(feat)) = sub_cfg - && short_longhand - { - if self.1.is_html() { - write!(fmt, "{feat}")?; - } else { - write!(fmt, "`{feat}`")?; - } - } else { - write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; - } - Ok(()) - }) + if let Cfg::Cfg(_, Some(feat)) = sub_cfg + && short_longhand + { + Either::Left(self.code_wrappers().wrap(feat)) + } else { + Either::Right( + Wrapped::with_parens() + .when(!sub_cfg.is_all()) + .wrap(Display(sub_cfg, self.1)), + ) + } }) .joined(separator, f) }) @@ -461,9 +451,9 @@ impl fmt::Display for Display<'_> { sub_cfgs .iter() .map(|sub_cfg| { - fmt::from_fn(|fmt| { - write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1)) - }) + Wrapped::with_parens() + .when(!sub_cfg.is_all()) + .wrap(Display(sub_cfg, self.1)) }) .joined(separator, fmt) } @@ -568,21 +558,13 @@ impl fmt::Display for Display<'_> { }; if !human_readable.is_empty() { fmt.write_str(human_readable) - } else if let Some(v) = value { - if self.1.is_html() { - write!( - fmt, - r#"{}="{}""#, - Escape(name.as_str()), - Escape(v.as_str()) - ) - } else { - write!(fmt, r#"`{name}="{v}"`"#) - } - } else if self.1.is_html() { - write!(fmt, "{}", Escape(name.as_str())) } else { - write!(fmt, "`{name}`") + let value = value + .map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str())))) + .maybe_display(); + self.code_wrappers() + .wrap(format_args!("{}{value}", self.1.escape(name.as_str()))) + .fmt(fmt) } } } diff --git a/src/librustdoc/display.rs b/src/librustdoc/display.rs index db868c5c9a8f3..d62ea4c368804 100644 --- a/src/librustdoc/display.rs +++ b/src/librustdoc/display.rs @@ -1,6 +1,6 @@ //! Various utilities for working with [`fmt::Display`] implementations. -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Display, Formatter, FormattingOptions}; pub(crate) trait Joined: IntoIterator { /// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`. @@ -45,3 +45,87 @@ impl MaybeDisplay for Option { }) } } + +#[derive(Clone, Copy)] +pub(crate) struct Wrapped { + prefix: T, + suffix: T, +} + +pub(crate) enum AngleBracket { + Open, + Close, +} + +impl Display for AngleBracket { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match (self, f.alternate()) { + (Self::Open, true) => "<", + (Self::Open, false) => "<", + (Self::Close, true) => ">", + (Self::Close, false) => ">", + }) + } +} + +impl Wrapped { + pub(crate) fn with_angle_brackets() -> Self { + Self { prefix: AngleBracket::Open, suffix: AngleBracket::Close } + } +} + +impl Wrapped { + pub(crate) fn with_parens() -> Self { + Self { prefix: '(', suffix: ')' } + } + + pub(crate) fn with_square_brackets() -> Self { + Self { prefix: '[', suffix: ']' } + } +} + +impl Wrapped { + pub(crate) fn with(prefix: T, suffix: T) -> Self { + Self { prefix, suffix } + } + + pub(crate) fn when(self, if_: bool) -> Wrapped { + Wrapped { + prefix: if_.then_some(self.prefix).maybe_display(), + suffix: if_.then_some(self.suffix).maybe_display(), + } + } + + pub(crate) fn wrap_fn( + self, + content: impl Fn(&mut Formatter<'_>) -> fmt::Result, + ) -> impl Display { + fmt::from_fn(move |f| { + self.prefix.fmt(f)?; + content(f)?; + self.suffix.fmt(f) + }) + } + + pub(crate) fn wrap(self, content: C) -> impl Display { + self.wrap_fn(move |f| content.fmt(f)) + } +} + +#[derive(Clone, Copy)] +pub(crate) struct WithOpts { + opts: FormattingOptions, +} + +impl WithOpts { + pub(crate) fn from(f: &Formatter<'_>) -> Self { + Self { opts: f.options() } + } + + pub(crate) fn display(self, t: impl Display) -> impl Display { + fmt::from_fn(move |f| { + let mut f = f.with_options(self.opts); + t.fmt(&mut f) + }) + } +} diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index fa55de4239a2a..ecaff4cdf43af 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -30,7 +30,7 @@ use super::url_parts_builder::UrlPartsBuilder; use crate::clean::types::ExternalLocation; use crate::clean::utils::find_nearest_parent_module; use crate::clean::{self, ExternalCrate, PrimitiveType}; -use crate::display::{Joined as _, MaybeDisplay as _}; +use crate::display::{Joined as _, MaybeDisplay as _, WithOpts, Wrapped}; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyText}; @@ -105,20 +105,16 @@ impl clean::GenericParamDef { impl clean::Generics { pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); - if real_params.peek().is_none() { - return Ok(()); - } - - let real_params = - fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f)); - if f.alternate() { - write!(f, "<{real_params:#}>") - } else { - write!(f, "<{real_params}>") - } - }) + let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); + if real_params.peek().is_none() { + None + } else { + Some( + Wrapped::with_angle_brackets() + .wrap_fn(move |f| real_params.clone().map(|g| g.print(cx)).joined(", ", f)), + ) + } + .maybe_display() } } @@ -151,11 +147,8 @@ fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> Ok(()) } clean::WherePredicate::EqPredicate { lhs, rhs } => { - if f.alternate() { - write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) - } else { - write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) - } + let opts = WithOpts::from(f); + write!(f, "{} == {}", opts.display(lhs.print(cx)), opts.display(rhs.print(cx))) } } }) @@ -279,13 +272,10 @@ impl clean::GenericBound { ty.print(cx).fmt(f) } clean::GenericBound::Use(args) => { - if f.alternate() { - f.write_str("use<")?; - } else { - f.write_str("use<")?; - } - args.iter().map(|arg| arg.name()).joined(", ", f)?; - if f.alternate() { f.write_str(">") } else { f.write_str(">") } + f.write_str("use")?; + Wrapped::with_angle_brackets() + .wrap_fn(|f| args.iter().map(|arg| arg.name()).joined(", ", f)) + .fmt(f) } }) } @@ -297,40 +287,29 @@ impl clean::GenericArgs { match self { clean::GenericArgs::AngleBracketed { args, constraints } => { if !args.is_empty() || !constraints.is_empty() { - if f.alternate() { - f.write_str("<")?; - } else { - f.write_str("<")?; - } - - [Either::Left(args), Either::Right(constraints)] - .into_iter() - .flat_map(Either::factor_into_iter) - .map(|either| { - either.map_either( - |arg| arg.print(cx), - |constraint| constraint.print(cx), - ) + Wrapped::with_angle_brackets() + .wrap_fn(|f| { + [Either::Left(args), Either::Right(constraints)] + .into_iter() + .flat_map(Either::factor_into_iter) + .map(|either| { + either.map_either( + |arg| arg.print(cx), + |constraint| constraint.print(cx), + ) + }) + .joined(", ", f) }) - .joined(", ", f)?; - - if f.alternate() { - f.write_str(">")?; - } else { - f.write_str(">")?; - } + .fmt(f)?; } } clean::GenericArgs::Parenthesized { inputs, output } => { - f.write_str("(")?; - inputs.iter().map(|ty| ty.print(cx)).joined(", ", f)?; - f.write_str(")")?; + Wrapped::with_parens() + .wrap_fn(|f| inputs.iter().map(|ty| ty.print(cx)).joined(", ", f)) + .fmt(f)?; if let Some(ref ty) = *output { - if f.alternate() { - write!(f, " -> {:#}", ty.print(cx))?; - } else { - write!(f, " -> {}", ty.print(cx))?; - } + f.write_str(if f.alternate() { " -> " } else { " -> " })?; + ty.print(cx).fmt(f)?; } } clean::GenericArgs::ReturnTypeNotation => { @@ -834,9 +813,10 @@ fn print_higher_ranked_params_with_space( fmt::from_fn(move |f| { if !params.is_empty() { f.write_str(keyword)?; - f.write_str(if f.alternate() { "<" } else { "<" })?; - params.iter().map(|lt| lt.print(cx)).joined(", ", f)?; - f.write_str(if f.alternate() { "> " } else { "> " })?; + Wrapped::with_angle_brackets() + .wrap_fn(|f| params.iter().map(|lt| lt.print(cx)).joined(", ", f)) + .fmt(f)?; + f.write_char(' ')?; } Ok(()) }) @@ -923,26 +903,23 @@ fn fmt_type( f, PrimitiveType::Tuple, format_args!( - "({})", - fmt::from_fn(|f| generic_names.iter().joined(", ", f)) + "{}", + Wrapped::with_parens() + .wrap_fn(|f| generic_names.iter().joined(", ", f)) ), cx, ) } else { - f.write_str("(")?; - many.iter().map(|item| item.print(cx)).joined(", ", f)?; - f.write_str(")") + Wrapped::with_parens() + .wrap_fn(|f| many.iter().map(|item| item.print(cx)).joined(", ", f)) + .fmt(f) } } }, clean::Slice(box clean::Generic(name)) => { primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) } - clean::Slice(t) => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - write!(f, "]") - } + clean::Slice(t) => Wrapped::with_square_brackets().wrap(t.print(cx)).fmt(f), clean::Type::Pat(t, pat) => { fmt::Display::fmt(&t.print(cx), f)?; write!(f, " is {pat}") @@ -953,37 +930,27 @@ fn fmt_type( format_args!("[{name}; {n}]", n = Escape(n)), cx, ), - clean::Array(t, n) => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - if f.alternate() { - write!(f, "; {n}")?; - } else { - write!(f, "; ")?; - primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)?; - } - write!(f, "]") - } + clean::Array(t, n) => Wrapped::with_square_brackets() + .wrap(fmt::from_fn(|f| { + t.print(cx).fmt(f)?; + f.write_str("; ")?; + if f.alternate() { + f.write_str(n) + } else { + primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx) + } + })) + .fmt(f), clean::RawPointer(m, t) => { let m = m.ptr_str(); if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() { - let ty = t.print(cx); - if f.alternate() { - primitive_link( - f, - clean::PrimitiveType::RawPointer, - format_args!("*{m} {ty:#}"), - cx, - ) - } else { - primitive_link( - f, - clean::PrimitiveType::RawPointer, - format_args!("*{m} {ty}"), - cx, - ) - } + primitive_link( + f, + clean::PrimitiveType::RawPointer, + format_args!("*{m} {ty}", ty = WithOpts::from(f).display(t.print(cx))), + cx, + ) } else { primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?; t.print(cx).fmt(f) @@ -1017,14 +984,10 @@ fn fmt_type( clean::ImplTrait(ref bounds) if bounds.len() > 1 => true, _ => false, }; - if needs_parens { - f.write_str("(")?; - } - fmt_type(ty, f, use_absolute, cx)?; - if needs_parens { - f.write_str(")")?; - } - Ok(()) + Wrapped::with_parens() + .when(needs_parens) + .wrap_fn(|f| fmt_type(ty, f, use_absolute, cx)) + .fmt(f) } clean::ImplTrait(bounds) => { f.write_str("impl ")?; @@ -1054,23 +1017,21 @@ impl clean::QPathData { // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), // we need to surround them with angle brackets in some cases (e.g. `::P`). - if f.alternate() { - if let Some(trait_) = trait_ - && should_fully_qualify - { - write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? - } else { - write!(f, "{:#}::", self_type.print(cx))? - } + if let Some(trait_) = trait_ + && should_fully_qualify + { + let opts = WithOpts::from(f); + Wrapped::with_angle_brackets() + .wrap(format_args!( + "{} as {}", + opts.display(self_type.print(cx)), + opts.display(trait_.print(cx)) + )) + .fmt(f)? } else { - if let Some(trait_) = trait_ - && should_fully_qualify - { - write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? - } else { - write!(f, "{}::", self_type.print(cx))? - } - }; + self_type.print(cx).fmt(f)?; + } + f.write_str("::")?; // It's pretty unsightly to look at `::C` in output, and // we've got hyperlinking on our side, so try to avoid longer // notation as much as possible by making `C` a hyperlink to trait @@ -1129,7 +1090,7 @@ impl clean::Impl { if let Some(ref ty) = self.trait_ { if self.is_negative_trait_impl() { - write!(f, "!")?; + f.write_char('!')?; } if self.kind.is_fake_variadic() && let Some(generics) = ty.generics() @@ -1137,18 +1098,17 @@ impl clean::Impl { { let last = ty.last(); if f.alternate() { - write!(f, "{last}<")?; - self.print_type(inner_type, f, use_absolute, cx)?; - write!(f, ">")?; + write!(f, "{last}")?; } else { - write!(f, "{}<", print_anchor(ty.def_id(), last, cx))?; - self.print_type(inner_type, f, use_absolute, cx)?; - write!(f, ">")?; - } + write!(f, "{}", print_anchor(ty.def_id(), last, cx))?; + }; + Wrapped::with_angle_brackets() + .wrap_fn(|f| self.print_type(inner_type, f, use_absolute, cx)) + .fmt(f)?; } else { ty.print(cx).fmt(f)?; } - write!(f, " for ")?; + f.write_str(" for ")?; } if let Some(ty) = self.kind.as_blanket_ty() { @@ -1215,18 +1175,10 @@ impl clean::Impl { && let Ok(ty) = generics.exactly_one() && self.kind.is_fake_variadic() { - let wrapper = print_anchor(path.def_id(), path.last(), cx); - if f.alternate() { - write!(f, "{wrapper:#}<")?; - } else { - write!(f, "{wrapper}<")?; - } - self.print_type(ty, f, use_absolute, cx)?; - if f.alternate() { - write!(f, ">")?; - } else { - write!(f, ">")?; - } + print_anchor(path.def_id(), path.last(), cx).fmt(f)?; + Wrapped::with_angle_brackets() + .wrap_fn(|f| self.print_type(ty, f, use_absolute, cx)) + .fmt(f)?; } else { fmt_type(type_, f, use_absolute, cx)?; } @@ -1308,23 +1260,13 @@ impl clean::FnDecl { pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; - if f.alternate() { - write!( - f, - "({params:#}{ellipsis}){arrow:#}", - params = print_params(&self.inputs, cx), - ellipsis = ellipsis, - arrow = self.print_output(cx) - ) - } else { - write!( - f, - "({params}{ellipsis}){arrow}", - params = print_params(&self.inputs, cx), - ellipsis = ellipsis, - arrow = self.print_output(cx) - ) - } + Wrapped::with_parens() + .wrap_fn(|f| { + print_params(&self.inputs, cx).fmt(f)?; + f.write_str(ellipsis) + }) + .fmt(f)?; + self.print_output(cx).fmt(f) }) } @@ -1343,8 +1285,7 @@ impl clean::FnDecl { fmt::from_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); - write!(&mut counter, "{:#}", fmt::from_fn(|f| { self.inner_full_print(None, f, cx) })) - .unwrap(); + write!(&mut counter, "{:#}", fmt::from_fn(|f| { self.inner_full_print(None, f, cx) }))?; // If the text form was over 80 characters wide, we will line-wrap our output. let line_wrapping_indent = if header_len + counter.0 > 80 { Some(indent) } else { None }; @@ -1362,42 +1303,44 @@ impl clean::FnDecl { f: &mut fmt::Formatter<'_>, cx: &Context<'_>, ) -> fmt::Result { - f.write_char('(')?; - - if !self.inputs.is_empty() { - let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4)); + Wrapped::with_parens() + .wrap_fn(|f| { + if !self.inputs.is_empty() { + let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4)); - if let Some(indent) = line_wrapping_indent { - write!(f, "\n{indent}")?; - } + if let Some(indent) = line_wrapping_indent { + write!(f, "\n{indent}")?; + } - let sep = fmt::from_fn(|f| { - if let Some(indent) = line_wrapping_indent { - write!(f, ",\n{indent}") - } else { - f.write_str(", ") - } - }); + let sep = fmt::from_fn(|f| { + if let Some(indent) = line_wrapping_indent { + write!(f, ",\n{indent}") + } else { + f.write_str(", ") + } + }); - self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?; + self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?; - if line_wrapping_indent.is_some() { - writeln!(f, ",")? - } + if line_wrapping_indent.is_some() { + writeln!(f, ",")? + } - if self.c_variadic { - match line_wrapping_indent { - None => write!(f, ", ...")?, - Some(indent) => writeln!(f, "{indent}...")?, - }; - } - } + if self.c_variadic { + match line_wrapping_indent { + None => write!(f, ", ...")?, + Some(indent) => writeln!(f, "{indent}...")?, + }; + } + } - if let Some(n) = line_wrapping_indent { - write!(f, "{}", Indent(n))? - } + if let Some(n) = line_wrapping_indent { + write!(f, "{}", Indent(n))? + } - f.write_char(')')?; + Ok(()) + }) + .fmt(f)?; self.print_output(cx).fmt(f) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9871066b9eb51..0ff1c09068936 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -10,6 +10,7 @@ #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(file_buffered)] +#![feature(formatting_options)] #![feature(if_let_guard)] #![feature(iter_advance_by)] #![feature(iter_intersperse)] From 055e05a338af00751ffccc992feeda227b8436b1 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 17 Sep 2025 14:07:23 -0400 Subject: [PATCH 0716/1900] Mark float intrinsics with no preconditions as safe --- .../rustc_hir_analysis/src/check/intrinsic.rs | 64 ++++++++ .../libm/src/math/support/float_traits.rs | 2 +- library/core/src/intrinsics/mod.rs | 138 +++++++++--------- library/core/src/num/f128.rs | 21 +-- library/core/src/num/f16.rs | 21 +-- library/core/src/num/f32.rs | 21 +-- library/core/src/num/f64.rs | 21 +-- library/std/src/num/f128.rs | 16 +- library/std/src/num/f16.rs | 16 +- library/std/src/num/f32.rs | 16 +- library/std/src/num/f64.rs | 16 +- .../core_arch/src/aarch64/neon/generated.rs | 12 +- .../crates/core_arch/src/wasm32/mod.rs | 16 +- .../spec/neon/aarch64.spec.yml | 12 +- src/tools/miri/tests/pass/float.rs | 4 +- .../intrinsics/fmuladd_nondeterministic.rs | 4 +- .../codegen-llvm/intrinsic-no-unnamed-attr.rs | 4 +- tests/ui/intrinsics/intrinsic-fmuladd.rs | 4 +- tests/ui/intrinsics/reify-intrinsic.stderr | 2 +- 19 files changed, 222 insertions(+), 188 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 5fd04427496d4..6faa67f6a904a 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -81,22 +81,62 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::bswap | sym::caller_location | sym::carrying_mul_add + | sym::ceilf16 + | sym::ceilf32 + | sym::ceilf64 + | sym::ceilf128 | sym::cold_path | sym::const_eval_select | sym::contract_check_ensures | sym::contract_check_requires | sym::contract_checks + | sym::cosf16 + | sym::cosf32 + | sym::cosf64 + | sym::cosf128 | sym::ctlz | sym::ctpop | sym::cttz | sym::discriminant_value + | sym::exp2f16 + | sym::exp2f32 + | sym::exp2f64 + | sym::exp2f128 + | sym::expf16 + | sym::expf32 + | sym::expf64 + | sym::expf128 | sym::fadd_algebraic | sym::fdiv_algebraic + | sym::floorf16 + | sym::floorf32 + | sym::floorf64 + | sym::floorf128 + | sym::fmaf16 + | sym::fmaf32 + | sym::fmaf64 + | sym::fmaf128 | sym::fmul_algebraic + | sym::fmuladdf16 + | sym::fmuladdf32 + | sym::fmuladdf64 + | sym::fmuladdf128 | sym::forget | sym::frem_algebraic | sym::fsub_algebraic | sym::is_val_statically_known + | sym::log2f16 + | sym::log2f32 + | sym::log2f64 + | sym::log2f128 + | sym::log10f16 + | sym::log10f32 + | sym::log10f64 + | sym::log10f128 + | sym::logf16 + | sym::logf32 + | sym::logf64 + | sym::logf128 | sym::maximumf16 | sym::maximumf32 | sym::maximumf64 @@ -115,6 +155,14 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::minnumf128 | sym::mul_with_overflow | sym::needs_drop + | sym::powf16 + | sym::powf32 + | sym::powf64 + | sym::powf128 + | sym::powif16 + | sym::powif32 + | sym::powif64 + | sym::powif128 | sym::prefetch_read_data | sym::prefetch_read_instruction | sym::prefetch_write_data @@ -128,13 +176,29 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::round_ties_even_f32 | sym::round_ties_even_f64 | sym::round_ties_even_f128 + | sym::roundf16 + | sym::roundf32 + | sym::roundf64 + | sym::roundf128 | sym::rustc_peek | sym::saturating_add | sym::saturating_sub | sym::select_unpredictable + | sym::sinf16 + | sym::sinf32 + | sym::sinf64 + | sym::sinf128 | sym::size_of + | sym::sqrtf16 + | sym::sqrtf32 + | sym::sqrtf64 + | sym::sqrtf128 | sym::sub_with_overflow | sym::three_way_compare + | sym::truncf16 + | sym::truncf32 + | sym::truncf64 + | sym::truncf128 | sym::type_id | sym::type_id_eq | sym::type_name diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs index fb790e696159f..b5ee6413d5539 100644 --- a/library/compiler-builtins/libm/src/math/support/float_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs @@ -289,7 +289,7 @@ macro_rules! float_impl { cfg_if! { // fma is not yet available in `core` if #[cfg(intrinsics_enabled)] { - unsafe{ core::intrinsics::$fma_intrinsic(self, y, z) } + core::intrinsics::$fma_intrinsic(self, y, z) } else { super::super::$fma_fn(self, y, z) } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index bffffbc29c1eb..a174ced5a2a63 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1022,28 +1022,28 @@ pub unsafe fn unaligned_volatile_store(dst: *mut T, val: T); /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf16(x: f16) -> f16; +pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is /// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf32(x: f32) -> f32; +pub fn sqrtf32(x: f32) -> f32; /// Returns the square root of an `f64` /// /// The stabilized version of this intrinsic is /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf64(x: f64) -> f64; +pub fn sqrtf64(x: f64) -> f64; /// Returns the square root of an `f128` /// /// The stabilized version of this intrinsic is /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf128(x: f128) -> f128; +pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1051,28 +1051,28 @@ pub unsafe fn sqrtf128(x: f128) -> f128; /// [`f16::powi`](../../std/primitive.f16.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif16(a: f16, x: i32) -> f16; +pub fn powif16(a: f16, x: i32) -> f16; /// Raises an `f32` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f32::powi`](../../std/primitive.f32.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif32(a: f32, x: i32) -> f32; +pub fn powif32(a: f32, x: i32) -> f32; /// Raises an `f64` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f64::powi`](../../std/primitive.f64.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif64(a: f64, x: i32) -> f64; +pub fn powif64(a: f64, x: i32) -> f64; /// Raises an `f128` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f128::powi`](../../std/primitive.f128.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif128(a: f128, x: i32) -> f128; +pub fn powif128(a: f128, x: i32) -> f128; /// Returns the sine of an `f16`. /// @@ -1080,28 +1080,28 @@ pub unsafe fn powif128(a: f128, x: i32) -> f128; /// [`f16::sin`](../../std/primitive.f16.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf16(x: f16) -> f16; +pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::sin`](../../std/primitive.f32.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf32(x: f32) -> f32; +pub fn sinf32(x: f32) -> f32; /// Returns the sine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf64(x: f64) -> f64; +pub fn sinf64(x: f64) -> f64; /// Returns the sine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::sin`](../../std/primitive.f128.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf128(x: f128) -> f128; +pub fn sinf128(x: f128) -> f128; /// Returns the cosine of an `f16`. /// @@ -1109,28 +1109,28 @@ pub unsafe fn sinf128(x: f128) -> f128; /// [`f16::cos`](../../std/primitive.f16.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf16(x: f16) -> f16; +pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::cos`](../../std/primitive.f32.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf32(x: f32) -> f32; +pub fn cosf32(x: f32) -> f32; /// Returns the cosine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf64(x: f64) -> f64; +pub fn cosf64(x: f64) -> f64; /// Returns the cosine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::cos`](../../std/primitive.f128.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf128(x: f128) -> f128; +pub fn cosf128(x: f128) -> f128; /// Raises an `f16` to an `f16` power. /// @@ -1138,28 +1138,28 @@ pub unsafe fn cosf128(x: f128) -> f128; /// [`f16::powf`](../../std/primitive.f16.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf16(a: f16, x: f16) -> f16; +pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is /// [`f32::powf`](../../std/primitive.f32.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf32(a: f32, x: f32) -> f32; +pub fn powf32(a: f32, x: f32) -> f32; /// Raises an `f64` to an `f64` power. /// /// The stabilized version of this intrinsic is /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf64(a: f64, x: f64) -> f64; +pub fn powf64(a: f64, x: f64) -> f64; /// Raises an `f128` to an `f128` power. /// /// The stabilized version of this intrinsic is /// [`f128::powf`](../../std/primitive.f128.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf128(a: f128, x: f128) -> f128; +pub fn powf128(a: f128, x: f128) -> f128; /// Returns the exponential of an `f16`. /// @@ -1167,28 +1167,28 @@ pub unsafe fn powf128(a: f128, x: f128) -> f128; /// [`f16::exp`](../../std/primitive.f16.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf16(x: f16) -> f16; +pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp`](../../std/primitive.f32.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf32(x: f32) -> f32; +pub fn expf32(x: f32) -> f32; /// Returns the exponential of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf64(x: f64) -> f64; +pub fn expf64(x: f64) -> f64; /// Returns the exponential of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp`](../../std/primitive.f128.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf128(x: f128) -> f128; +pub fn expf128(x: f128) -> f128; /// Returns 2 raised to the power of an `f16`. /// @@ -1196,28 +1196,28 @@ pub unsafe fn expf128(x: f128) -> f128; /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f16(x: f16) -> f16; +pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f32(x: f32) -> f32; +pub fn exp2f32(x: f32) -> f32; /// Returns 2 raised to the power of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f64(x: f64) -> f64; +pub fn exp2f64(x: f64) -> f64; /// Returns 2 raised to the power of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f128(x: f128) -> f128; +pub fn exp2f128(x: f128) -> f128; /// Returns the natural logarithm of an `f16`. /// @@ -1225,28 +1225,28 @@ pub unsafe fn exp2f128(x: f128) -> f128; /// [`f16::ln`](../../std/primitive.f16.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf16(x: f16) -> f16; +pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ln`](../../std/primitive.f32.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf32(x: f32) -> f32; +pub fn logf32(x: f32) -> f32; /// Returns the natural logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf64(x: f64) -> f64; +pub fn logf64(x: f64) -> f64; /// Returns the natural logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ln`](../../std/primitive.f128.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf128(x: f128) -> f128; +pub fn logf128(x: f128) -> f128; /// Returns the base 10 logarithm of an `f16`. /// @@ -1254,28 +1254,28 @@ pub unsafe fn logf128(x: f128) -> f128; /// [`f16::log10`](../../std/primitive.f16.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f16(x: f16) -> f16; +pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log10`](../../std/primitive.f32.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f32(x: f32) -> f32; +pub fn log10f32(x: f32) -> f32; /// Returns the base 10 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f64(x: f64) -> f64; +pub fn log10f64(x: f64) -> f64; /// Returns the base 10 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log10`](../../std/primitive.f128.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f128(x: f128) -> f128; +pub fn log10f128(x: f128) -> f128; /// Returns the base 2 logarithm of an `f16`. /// @@ -1283,28 +1283,28 @@ pub unsafe fn log10f128(x: f128) -> f128; /// [`f16::log2`](../../std/primitive.f16.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f16(x: f16) -> f16; +pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log2`](../../std/primitive.f32.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f32(x: f32) -> f32; +pub fn log2f32(x: f32) -> f32; /// Returns the base 2 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f64(x: f64) -> f64; +pub fn log2f64(x: f64) -> f64; /// Returns the base 2 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log2`](../../std/primitive.f128.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f128(x: f128) -> f128; +pub fn log2f128(x: f128) -> f128; /// Returns `a * b + c` for `f16` values. /// @@ -1312,28 +1312,28 @@ pub unsafe fn log2f128(x: f128) -> f128; /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf16(a: f16, b: f16, c: f16) -> f16; +pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf32(a: f32, b: f32, c: f32) -> f32; +pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values. /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf64(a: f64, b: f64, c: f64) -> f64; +pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values. /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf128(a: f128, b: f128, c: f128) -> f128; +pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// Returns `a * b + c` for `f16` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the @@ -1347,7 +1347,7 @@ pub unsafe fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; +pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1360,7 +1360,7 @@ pub unsafe fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; +pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1373,7 +1373,7 @@ pub unsafe fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; +pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1386,7 +1386,7 @@ pub unsafe fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; +pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; /// Returns the largest integer less than or equal to an `f16`. /// @@ -1395,7 +1395,7 @@ pub unsafe fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn floorf16(x: f16) -> f16; +pub const fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1403,7 +1403,7 @@ pub const unsafe fn floorf16(x: f16) -> f16; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn floorf32(x: f32) -> f32; +pub const fn floorf32(x: f32) -> f32; /// Returns the largest integer less than or equal to an `f64`. /// /// The stabilized version of this intrinsic is @@ -1411,7 +1411,7 @@ pub const unsafe fn floorf32(x: f32) -> f32; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn floorf64(x: f64) -> f64; +pub const fn floorf64(x: f64) -> f64; /// Returns the largest integer less than or equal to an `f128`. /// /// The stabilized version of this intrinsic is @@ -1419,7 +1419,7 @@ pub const unsafe fn floorf64(x: f64) -> f64; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn floorf128(x: f128) -> f128; +pub const fn floorf128(x: f128) -> f128; /// Returns the smallest integer greater than or equal to an `f16`. /// @@ -1428,7 +1428,7 @@ pub const unsafe fn floorf128(x: f128) -> f128; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn ceilf16(x: f16) -> f16; +pub const fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1436,7 +1436,7 @@ pub const unsafe fn ceilf16(x: f16) -> f16; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn ceilf32(x: f32) -> f32; +pub const fn ceilf32(x: f32) -> f32; /// Returns the smallest integer greater than or equal to an `f64`. /// /// The stabilized version of this intrinsic is @@ -1444,7 +1444,7 @@ pub const unsafe fn ceilf32(x: f32) -> f32; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn ceilf64(x: f64) -> f64; +pub const fn ceilf64(x: f64) -> f64; /// Returns the smallest integer greater than or equal to an `f128`. /// /// The stabilized version of this intrinsic is @@ -1452,7 +1452,7 @@ pub const unsafe fn ceilf64(x: f64) -> f64; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn ceilf128(x: f128) -> f128; +pub const fn ceilf128(x: f128) -> f128; /// Returns the integer part of an `f16`. /// @@ -1461,7 +1461,7 @@ pub const unsafe fn ceilf128(x: f128) -> f128; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn truncf16(x: f16) -> f16; +pub const fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1469,7 +1469,7 @@ pub const unsafe fn truncf16(x: f16) -> f16; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn truncf32(x: f32) -> f32; +pub const fn truncf32(x: f32) -> f32; /// Returns the integer part of an `f64`. /// /// The stabilized version of this intrinsic is @@ -1477,7 +1477,7 @@ pub const unsafe fn truncf32(x: f32) -> f32; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn truncf64(x: f64) -> f64; +pub const fn truncf64(x: f64) -> f64; /// Returns the integer part of an `f128`. /// /// The stabilized version of this intrinsic is @@ -1485,7 +1485,7 @@ pub const unsafe fn truncf64(x: f64) -> f64; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn truncf128(x: f128) -> f128; +pub const fn truncf128(x: f128) -> f128; /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number with an even /// least significant digit. @@ -1534,7 +1534,7 @@ pub const fn round_ties_even_f128(x: f128) -> f128; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn roundf16(x: f16) -> f16; +pub const fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1542,7 +1542,7 @@ pub const unsafe fn roundf16(x: f16) -> f16; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn roundf32(x: f32) -> f32; +pub const fn roundf32(x: f32) -> f32; /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1550,7 +1550,7 @@ pub const unsafe fn roundf32(x: f32) -> f32; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn roundf64(x: f64) -> f64; +pub const fn roundf64(x: f64) -> f64; /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1558,10 +1558,10 @@ pub const unsafe fn roundf64(x: f64) -> f64; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn roundf128(x: f128) -> f128; +pub const fn roundf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. -/// May assume inputs are finite. +/// Requires that inputs and output of the operation are finite, causing UB otherwise. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] @@ -1569,7 +1569,7 @@ pub const unsafe fn roundf128(x: f128) -> f128; pub unsafe fn fadd_fast(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. -/// May assume inputs are finite. +/// Requires that inputs and output of the operation are finite, causing UB otherwise. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] @@ -1577,7 +1577,7 @@ pub unsafe fn fadd_fast(a: T, b: T) -> T; pub unsafe fn fsub_fast(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. -/// May assume inputs are finite. +/// Requires that inputs and output of the operation are finite, causing UB otherwise. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] @@ -1585,7 +1585,7 @@ pub unsafe fn fsub_fast(a: T, b: T) -> T; pub unsafe fn fmul_fast(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. -/// May assume inputs are finite. +/// Requires that inputs and output of the operation are finite, causing UB otherwise. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] @@ -1593,7 +1593,7 @@ pub unsafe fn fmul_fast(a: T, b: T) -> T; pub unsafe fn fdiv_fast(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. -/// May assume inputs are finite. +/// Requires that inputs and output of the operation are finite, causing UB otherwise. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 66c892aadd083..6088fb6fa1786 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1459,8 +1459,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn floor(self) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf128(self) } + intrinsics::floorf128(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -1488,8 +1487,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn ceil(self) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf128(self) } + intrinsics::ceilf128(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -1523,8 +1521,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn round(self) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf128(self) } + intrinsics::roundf128(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -1587,8 +1584,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn trunc(self) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf128(self) } + intrinsics::truncf128(self) } /// Returns the fractional part of `self`. @@ -1664,8 +1660,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f128, b: f128) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf128(self, a, b) } + intrinsics::fmaf128(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -1780,8 +1775,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn powi(self, n: i32) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif128(self, n) } + intrinsics::powif128(self, n) } /// Returns the square root of a number. @@ -1816,7 +1810,6 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sqrt(self) -> f128 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf128(self) } + intrinsics::sqrtf128(self) } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 81220065e72a6..3f66c5973d4ac 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1434,8 +1434,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn floor(self) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf16(self) } + intrinsics::floorf16(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -1463,8 +1462,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn ceil(self) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf16(self) } + intrinsics::ceilf16(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -1498,8 +1496,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn round(self) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf16(self) } + intrinsics::roundf16(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -1562,8 +1559,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn trunc(self) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf16(self) } + intrinsics::truncf16(self) } /// Returns the fractional part of `self`. @@ -1639,8 +1635,7 @@ impl f16 { #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f16, b: f16) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf16(self, a, b) } + intrinsics::fmaf16(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -1755,8 +1750,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn powi(self, n: i32) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif16(self, n) } + intrinsics::powif16(self, n) } /// Returns the square root of a number. @@ -1791,8 +1785,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sqrt(self) -> f16 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf16(self) } + intrinsics::sqrtf16(self) } /// Returns the cube root of a number. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index cefcf1d1fe2fc..ebce89e5b3da5 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1603,8 +1603,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn floor(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf32(x) } + intrinsics::floorf32(x) } /// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. @@ -1632,8 +1631,7 @@ pub mod math { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] pub const fn ceil(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf32(x) } + intrinsics::ceilf32(x) } /// Experimental version of `round` in `core`. See [`f32::round`] for details. @@ -1666,8 +1664,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn round(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf32(x) } + intrinsics::roundf32(x) } /// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for @@ -1729,8 +1726,7 @@ pub mod math { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] pub const fn trunc(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf32(x) } + intrinsics::truncf32(x) } /// Experimental version of `fract` in `core`. See [`f32::fract`] for details. @@ -1804,8 +1800,7 @@ pub mod math { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf32(x, y, z) } + intrinsics::fmaf32(x, y, z) } /// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. @@ -1896,8 +1891,7 @@ pub mod math { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] pub fn powi(x: f32, n: i32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif32(x, n) } + intrinsics::powif32(x, n) } /// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. @@ -1927,8 +1921,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sqrt(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf32(x) } + intrinsics::sqrtf32(x) } /// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 9dd1141e70331..91e3949fc3900 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1601,8 +1601,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn floor(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf64(x) } + intrinsics::floorf64(x) } /// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. @@ -1630,8 +1629,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn ceil(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf64(x) } + intrinsics::ceilf64(x) } /// Experimental version of `round` in `core`. See [`f64::round`] for details. @@ -1664,8 +1662,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn round(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf64(x) } + intrinsics::roundf64(x) } /// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for @@ -1727,8 +1724,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn trunc(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf64(x) } + intrinsics::truncf64(x) } /// Experimental version of `fract` in `core`. See [`f64::fract`] for details. @@ -1802,8 +1798,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf64(x, a, b) } + intrinsics::fmaf64(x, a, b) } /// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. @@ -1894,8 +1889,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn powi(x: f64, n: i32) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif64(x, n) } + intrinsics::powif64(x, n) } /// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. @@ -1925,8 +1919,7 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sqrt(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf64(x) } + intrinsics::sqrtf64(x) } /// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index b83692390b6bc..5d206c4b7da5f 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -44,7 +44,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn powf(self, n: f128) -> f128 { - unsafe { intrinsics::powf128(self, n) } + intrinsics::powf128(self, n) } /// Returns `e^(self)`, (the exponential function). @@ -76,7 +76,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp(self) -> f128 { - unsafe { intrinsics::expf128(self) } + intrinsics::expf128(self) } /// Returns `2^(self)`. @@ -106,7 +106,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp2(self) -> f128 { - unsafe { intrinsics::exp2f128(self) } + intrinsics::exp2f128(self) } /// Returns the natural logarithm of the number. @@ -151,7 +151,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln(self) -> f128 { - unsafe { intrinsics::logf128(self) } + intrinsics::logf128(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -241,7 +241,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn log2(self) -> f128 { - unsafe { intrinsics::log2f128(self) } + intrinsics::log2f128(self) } /// Returns the base 10 logarithm of the number. @@ -284,7 +284,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn log10(self) -> f128 { - unsafe { intrinsics::log10f128(self) } + intrinsics::log10f128(self) } /// Returns the cube root of a number. @@ -385,7 +385,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sin(self) -> f128 { - unsafe { intrinsics::sinf128(self) } + intrinsics::sinf128(self) } /// Computes the cosine of a number (in radians). @@ -414,7 +414,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn cos(self) -> f128 { - unsafe { intrinsics::cosf128(self) } + intrinsics::cosf128(self) } /// Computes the tangent of a number (in radians). diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 5599528717cbe..2565ef0f9f2cd 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -44,7 +44,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn powf(self, n: f16) -> f16 { - unsafe { intrinsics::powf16(self, n) } + intrinsics::powf16(self, n) } /// Returns `e^(self)`, (the exponential function). @@ -76,7 +76,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp(self) -> f16 { - unsafe { intrinsics::expf16(self) } + intrinsics::expf16(self) } /// Returns `2^(self)`. @@ -106,7 +106,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp2(self) -> f16 { - unsafe { intrinsics::exp2f16(self) } + intrinsics::exp2f16(self) } /// Returns the natural logarithm of the number. @@ -151,7 +151,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln(self) -> f16 { - unsafe { intrinsics::logf16(self) } + intrinsics::logf16(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -241,7 +241,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn log2(self) -> f16 { - unsafe { intrinsics::log2f16(self) } + intrinsics::log2f16(self) } /// Returns the base 10 logarithm of the number. @@ -284,7 +284,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn log10(self) -> f16 { - unsafe { intrinsics::log10f16(self) } + intrinsics::log10f16(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the @@ -350,7 +350,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sin(self) -> f16 { - unsafe { intrinsics::sinf16(self) } + intrinsics::sinf16(self) } /// Computes the cosine of a number (in radians). @@ -379,7 +379,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn cos(self) -> f16 { - unsafe { intrinsics::cosf16(self) } + intrinsics::cosf16(self) } /// Computes the tangent of a number (in radians). diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 0247080a8d6be..ac1d889cc3731 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -338,7 +338,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powf(self, n: f32) -> f32 { - unsafe { intrinsics::powf32(self, n) } + intrinsics::powf32(self, n) } /// Returns the square root of a number. @@ -395,7 +395,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp(self) -> f32 { - unsafe { intrinsics::expf32(self) } + intrinsics::expf32(self) } /// Returns `2^(self)`. @@ -420,7 +420,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp2(self) -> f32 { - unsafe { intrinsics::exp2f32(self) } + intrinsics::exp2f32(self) } /// Returns the natural logarithm of the number. @@ -455,7 +455,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f32 { - unsafe { intrinsics::logf32(self) } + intrinsics::logf32(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -525,7 +525,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - unsafe { intrinsics::log2f32(self) } + intrinsics::log2f32(self) } /// Returns the base 10 logarithm of the number. @@ -558,7 +558,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f32 { - unsafe { intrinsics::log10f32(self) } + intrinsics::log10f32(self) } /// The positive difference of two numbers. @@ -683,7 +683,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sin(self) -> f32 { - unsafe { intrinsics::sinf32(self) } + intrinsics::sinf32(self) } /// Computes the cosine of a number (in radians). @@ -707,7 +707,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cos(self) -> f32 { - unsafe { intrinsics::cosf32(self) } + intrinsics::cosf32(self) } /// Computes the tangent of a number (in radians). diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 1cfd3909d967e..55c8593a0c0b1 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -338,7 +338,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powf(self, n: f64) -> f64 { - unsafe { intrinsics::powf64(self, n) } + intrinsics::powf64(self, n) } /// Returns the square root of a number. @@ -395,7 +395,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp(self) -> f64 { - unsafe { intrinsics::expf64(self) } + intrinsics::expf64(self) } /// Returns `2^(self)`. @@ -420,7 +420,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp2(self) -> f64 { - unsafe { intrinsics::exp2f64(self) } + intrinsics::exp2f64(self) } /// Returns the natural logarithm of the number. @@ -455,7 +455,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f64 { - unsafe { intrinsics::logf64(self) } + intrinsics::logf64(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -525,7 +525,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - unsafe { intrinsics::log2f64(self) } + intrinsics::log2f64(self) } /// Returns the base 10 logarithm of the number. @@ -558,7 +558,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f64 { - unsafe { intrinsics::log10f64(self) } + intrinsics::log10f64(self) } /// The positive difference of two numbers. @@ -683,7 +683,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sin(self) -> f64 { - unsafe { intrinsics::sinf64(self) } + intrinsics::sinf64(self) } /// Computes the cosine of a number (in radians). @@ -707,7 +707,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cos(self) -> f64 { - unsafe { intrinsics::cosf64(self) } + intrinsics::cosf64(self) } /// Computes the tangent of a number (in radians). diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index bc4c438038dc5..40612660d4db4 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -10183,7 +10183,7 @@ pub fn vfmad_lane_f64(a: f64, b: f64, c: float64x1_t) -> f64 { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] pub fn vfmah_f16(a: f16, b: f16, c: f16) -> f16 { - unsafe { fmaf16(b, c, a) } + fmaf16(b, c, a) } #[doc = "Floating-point fused multiply-add to accumulator"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vfmah_lane_f16)"] @@ -23045,7 +23045,7 @@ pub fn vrndaq_f64(a: float64x2_t) -> float64x2_t { #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg_attr(test, assert_instr(frinta))] pub fn vrndah_f16(a: f16) -> f16 { - unsafe { roundf16(a) } + roundf16(a) } #[doc = "Floating-point round to integral, to nearest with ties to away"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrndh_f16)"] @@ -23054,7 +23054,7 @@ pub fn vrndah_f16(a: f16) -> f16 { #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg_attr(test, assert_instr(frintz))] pub fn vrndh_f16(a: f16) -> f16 { - unsafe { truncf16(a) } + truncf16(a) } #[doc = "Floating-point round to integral, using current rounding mode"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrndi_f16)"] @@ -23229,7 +23229,7 @@ pub fn vrndmq_f64(a: float64x2_t) -> float64x2_t { #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndmh_f16(a: f16) -> f16 { - unsafe { floorf16(a) } + floorf16(a) } #[doc = "Floating-point round to integral, to nearest with ties to even"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrndn_f64)"] @@ -23356,7 +23356,7 @@ pub fn vrndpq_f64(a: float64x2_t) -> float64x2_t { #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndph_f16(a: f16) -> f16 { - unsafe { ceilf16(a) } + ceilf16(a) } #[doc = "Floating-point round to integral exact, using current rounding mode"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrndx_f16)"] @@ -24846,7 +24846,7 @@ pub fn vsqrtq_f64(a: float64x2_t) -> float64x2_t { #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg_attr(test, assert_instr(fsqrt))] pub fn vsqrth_f16(a: f16) -> f16 { - unsafe { sqrtf16(a) } + sqrtf16(a) } #[doc = "Shift Right and Insert (immediate)"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vsri_n_s8)"] diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 60049c73295c1..01bf0a71658b8 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -43,7 +43,7 @@ pub fn unreachable() -> ! { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f32_ceil(a: f32) -> f32 { - unsafe { crate::intrinsics::ceilf32(a) } + crate::intrinsics::ceilf32(a) } /// Generates the [`f32.floor`] instruction, returning the largest integer less than or equal to `a`. @@ -57,7 +57,7 @@ pub fn f32_ceil(a: f32) -> f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f32_floor(a: f32) -> f32 { - unsafe { crate::intrinsics::floorf32(a) } + crate::intrinsics::floorf32(a) } /// Generates the [`f32.trunc`] instruction, roundinging to the nearest integer towards zero. @@ -71,7 +71,7 @@ pub fn f32_floor(a: f32) -> f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f32_trunc(a: f32) -> f32 { - unsafe { crate::intrinsics::truncf32(a) } + crate::intrinsics::truncf32(a) } /// Generates the [`f32.nearest`] instruction, roundinging to the nearest integer. Rounds half-way @@ -100,7 +100,7 @@ pub fn f32_nearest(a: f32) -> f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f32_sqrt(a: f32) -> f32 { - unsafe { crate::intrinsics::sqrtf32(a) } + crate::intrinsics::sqrtf32(a) } /// Generates the [`f64.ceil`] instruction, returning the smallest integer greater than or equal to `a`. @@ -114,7 +114,7 @@ pub fn f32_sqrt(a: f32) -> f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f64_ceil(a: f64) -> f64 { - unsafe { crate::intrinsics::ceilf64(a) } + crate::intrinsics::ceilf64(a) } /// Generates the [`f64.floor`] instruction, returning the largest integer less than or equal to `a`. @@ -128,7 +128,7 @@ pub fn f64_ceil(a: f64) -> f64 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f64_floor(a: f64) -> f64 { - unsafe { crate::intrinsics::floorf64(a) } + crate::intrinsics::floorf64(a) } /// Generates the [`f64.trunc`] instruction, roundinging to the nearest integer towards zero. @@ -142,7 +142,7 @@ pub fn f64_floor(a: f64) -> f64 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f64_trunc(a: f64) -> f64 { - unsafe { crate::intrinsics::truncf64(a) } + crate::intrinsics::truncf64(a) } /// Generates the [`f64.nearest`] instruction, roundinging to the nearest integer. Rounds half-way @@ -171,7 +171,7 @@ pub fn f64_nearest(a: f64) -> f64 { #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "wasm_numeric_instr", issue = "133908")] pub fn f64_sqrt(a: f64) -> f64 { - unsafe { crate::intrinsics::sqrtf64(a) } + crate::intrinsics::sqrtf64(a) } unsafe extern "C-unwind" { diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index a31613e6b1aef..ece435870ae2f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -3054,7 +3054,7 @@ intrinsics: types: - [f16, 'h_'] compose: - - FnCall: [roundf16, [a], [], true] + - FnCall: [roundf16, [a], []] - name: "vrndn{neon_type.no}" doc: "Floating-point round to integral, to nearest with ties to even" @@ -3151,7 +3151,7 @@ intrinsics: types: - [f16, 'h_'] compose: - - FnCall: [floorf16, [a], [], true] + - FnCall: [floorf16, [a], []] @@ -3198,7 +3198,7 @@ intrinsics: types: - [f16, 'h_'] compose: - - FnCall: [ceilf16, [a], [], true] + - FnCall: [ceilf16, [a], []] - name: "vrnd{neon_type.no}" doc: "Floating-point round to integral, toward zero" @@ -3243,7 +3243,7 @@ intrinsics: types: - [f16, 'h_'] compose: - - FnCall: [truncf16, [a], [], true] + - FnCall: [truncf16, [a], []] - name: "vrndi{neon_type.no}" @@ -8398,7 +8398,7 @@ intrinsics: types: - [f16, 'h_'] compose: - - FnCall: [sqrtf16, [a], [], true] + - FnCall: [sqrtf16, [a], []] - name: "vrsqrts{type[0]}" doc: "Floating-point reciprocal square root step" @@ -10346,7 +10346,7 @@ intrinsics: types: - ["f16", "h_f16"] compose: - - FnCall: [fmaf16, [b, c, a], [], true] + - FnCall: [fmaf16, [b, c, a], []] - name: "vfmah_lane{type[2]}" diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 9f1b3f612b2d2..323019df42504 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1401,12 +1401,12 @@ fn test_fmuladd() { #[inline(never)] pub fn test_operations_f32(a: f32, b: f32, c: f32) { - assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c); + assert_approx_eq!(fmuladdf32(a, b, c), a * b + c); } #[inline(never)] pub fn test_operations_f64(a: f64, b: f64, c: f64) { - assert_approx_eq!(unsafe { fmuladdf64(a, b, c) }, a * b + c); + assert_approx_eq!(fmuladdf64(a, b, c), a * b + c); } test_operations_f32(0.1, 0.2, 0.3); diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs index b688405c4b184..4d3e91c4cba76 100644 --- a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs +++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs @@ -28,7 +28,7 @@ fn main() { let c = std::hint::black_box(-a * b); // It is unspecified whether the following operation is fused or not. The // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused. - let x = unsafe { fmuladdf64(a, b, c) }; + let x = fmuladdf64(a, b, c); x == 0.0 }), "`fmuladdf64` failed to be evaluated as both fused and unfused" @@ -41,7 +41,7 @@ fn main() { let c = std::hint::black_box(-a * b); // It is unspecified whether the following operation is fused or not. The // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused. - let x = unsafe { fmuladdf32(a, b, c) }; + let x = fmuladdf32(a, b, c); x == 0.0 }), "`fmuladdf32` failed to be evaluated as both fused and unfused" diff --git a/tests/codegen-llvm/intrinsic-no-unnamed-attr.rs b/tests/codegen-llvm/intrinsic-no-unnamed-attr.rs index 4bec579831dc4..255f20e6ff640 100644 --- a/tests/codegen-llvm/intrinsic-no-unnamed-attr.rs +++ b/tests/codegen-llvm/intrinsic-no-unnamed-attr.rs @@ -7,7 +7,5 @@ use std::intrinsics::sqrtf32; // CHECK: @llvm.sqrt.f32(float) #{{[0-9]*}} fn main() { - unsafe { - sqrtf32(0.0f32); - } + sqrtf32(0.0f32); } diff --git a/tests/ui/intrinsics/intrinsic-fmuladd.rs b/tests/ui/intrinsics/intrinsic-fmuladd.rs index d03297884f791..ab4285590cb95 100644 --- a/tests/ui/intrinsics/intrinsic-fmuladd.rs +++ b/tests/ui/intrinsics/intrinsic-fmuladd.rs @@ -11,7 +11,7 @@ macro_rules! assert_approx_eq { } fn main() { - unsafe { + { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; @@ -25,7 +25,7 @@ fn main() { assert_eq!(fmuladdf32(8.9, inf, 3.2), inf); assert_eq!(fmuladdf32(-3.2, 2.4, neg_inf), neg_inf); } - unsafe { + { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr index aea6f838e0d44..1307a85c8b6ca 100644 --- a/tests/ui/intrinsics/reify-intrinsic.stderr +++ b/tests/ui/intrinsics/reify-intrinsic.stderr @@ -22,7 +22,7 @@ LL | std::intrinsics::floorf32, | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | = note: expected fn pointer `unsafe fn(_) -> _` - found fn item `unsafe fn(_) -> _ {floorf32}` + found fn item `fn(_) -> _ {floorf32}` error: aborting due to 3 previous errors From ab7cfc7c307d50659a0165dad910565809f5d40f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 22 Sep 2025 02:14:50 +0300 Subject: [PATCH 0717/1900] Clarify `rust-analyzer.inlayHints.maxLength` is not a hard guarantee --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 ++ .../rust-analyzer/docs/book/src/configuration_generated.md | 2 ++ src/tools/rust-analyzer/editors/code/package.json | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 6b489d5114381..a88d228fcb60c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -267,6 +267,8 @@ config_data! { inlayHints_lifetimeElisionHints_useParameterNames: bool = false, /// Maximum length for inlay hints. Set to null to have an unlimited length. + /// + /// **Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit. inlayHints_maxLength: Option = Some(25), /// Show function parameter name inlay hints at the call site. diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 50dacd88f4072..e78f1b4ba3582 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1046,6 +1046,8 @@ Default: `25` Maximum length for inlay hints. Set to null to have an unlimited length. +**Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit. + ## rust-analyzer.inlayHints.parameterHints.enable {#inlayHints.parameterHints.enable} diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 1d27a12053552..745e0da4efef0 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2355,7 +2355,7 @@ "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.maxLength": { - "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.", + "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.\n\n**Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit.", "default": 25, "type": [ "null", From 8f1a1f8533cb270625a057a2bb7f50ef3f8ec1b5 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 05:45:23 +0200 Subject: [PATCH 0718/1900] Update tests/rustdoc/private-mod-override-re-export.rs Co-authored-by: lolbinarycat --- tests/rustdoc/private-mod-override-re-export.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rustdoc/private-mod-override-re-export.rs b/tests/rustdoc/private-mod-override-re-export.rs index 47b07958709bb..8deafe2422256 100644 --- a/tests/rustdoc/private-mod-override-re-export.rs +++ b/tests/rustdoc/private-mod-override-re-export.rs @@ -11,4 +11,4 @@ mod m1 { pub use m1::*; use crate::m1::m2; -//@ !has foo/index.html '//a[@href="https://github.com/m2/index.html"]' 'm2' +//@ count foo/index.html '//a[@class="mod"]' 0 From 52e6998a1837884daad9c9a0f8bf78631557fd2a Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 11:47:12 +0800 Subject: [PATCH 0719/1900] Move test file --- .../private-mod-override-reexport.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/rustdoc/{private-mod-override-re-export.rs => reexport/private-mod-override-reexport.rs} (100%) diff --git a/tests/rustdoc/private-mod-override-re-export.rs b/tests/rustdoc/reexport/private-mod-override-reexport.rs similarity index 100% rename from tests/rustdoc/private-mod-override-re-export.rs rename to tests/rustdoc/reexport/private-mod-override-reexport.rs From 5658b39397d2667ed8c51ef6b5c52f562af4415e Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 22 Sep 2025 04:13:50 +0000 Subject: [PATCH 0720/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 9f32ccf35fb877270bc44a86a126440f04d676d0. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index df2bac877b66f..0dc9ce843e9c9 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -2f3f27bf79ec147fec9d2e7980605307a74067f4 +9f32ccf35fb877270bc44a86a126440f04d676d0 From 9acc63a48c8206cfe2c2d272600d538983308657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 17 Sep 2025 22:29:49 -0700 Subject: [PATCH 0721/1900] port `#[debugger_visualizer]` to the new attribute system --- .../src/attributes/debugger.rs | 60 ++++++++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_codegen_llvm/src/debuginfo/gdb.rs | 2 +- compiler/rustc_codegen_ssa/src/base.rs | 4 +- .../rustc_hir/src/attrs/data_structures.rs | 19 ++- .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../src/middle/debugger_visualizer.rs | 8 +- compiler/rustc_passes/messages.ftl | 9 -- compiler/rustc_passes/src/check_attr.rs | 16 +-- .../rustc_passes/src/debugger_visualizer.rs | 113 +++++++++--------- compiler/rustc_passes/src/errors.rs | 17 --- tests/ui/attributes/malformed-attrs.rs | 3 +- tests/ui/attributes/malformed-attrs.stderr | 53 ++++---- .../invalid-debugger-visualizer-option.rs | 2 +- .../invalid-debugger-visualizer-option.stderr | 22 ++-- .../invalid-debugger-visualizer-target.rs | 3 +- .../invalid-debugger-visualizer-target.stderr | 4 +- 18 files changed, 185 insertions(+), 154 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/debugger.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs new file mode 100644 index 0000000000000..56ff10be42640 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -0,0 +1,60 @@ +use rustc_hir::attrs::{DebugVisualizer, DebuggerVisualizerType}; + +use super::prelude::*; + +pub(crate) struct DebuggerViualizerParser; + +impl CombineAttributeParser for DebuggerViualizerParser { + const PATH: &[Symbol] = &[sym::debugger_visualizer]; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!( + List: &[r#"natvis_file = "...", gdb_script_file = "...""#], + "https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute" + ); + + type Item = DebugVisualizer; + const CONVERT: ConvertFn = |v, _| AttributeKind::DebuggerVisualizer(v); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { + let Some(l) = args.list() else { + cx.expected_list(args.span().unwrap_or(cx.attr_span)); + return None; + }; + let Some(single) = l.single() else { + cx.expected_single_argument(l.span); + return None; + }; + let Some(mi) = single.meta_item() else { + cx.expected_name_value(single.span(), None); + return None; + }; + let path = mi.path().word_sym(); + let visualizer_type = match path { + Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, + Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, + _ => { + cx.expected_specific_argument( + mi.path().span(), + &[sym::natvis_file, sym::gdb_script_file], + ); + return None; + } + }; + + let Some(path) = mi.args().name_value() else { + cx.expected_name_value(single.span(), path); + return None; + }; + + let Some(path) = path.value_as_str() else { + cx.expected_string_literal(path.value_span, Some(path.value_as_lit())); + return None; + }; + + Some(DebugVisualizer { span: mi.span(), visualizer_type, path }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 4ed13d239b9d6..8dbf4c0ef32ef 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -36,6 +36,7 @@ pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod crate_level; +pub(crate) mod debugger; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4c32bb87a242c..1f1f9d6d50e56 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -28,6 +28,7 @@ use crate::attributes::crate_level::{ CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser, }; +use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -163,6 +164,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 7a6dc008c7b9e..3081badb821fa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -2,9 +2,9 @@ use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; -use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType; use rustc_session::config::{CrateType, DebugInfo}; use crate::builder::Builder; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 68a2f43ec67bc..422b06350e1fc 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -11,12 +11,12 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::OptimizeAttr; +use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8ab43ff2582c0..5a9e644f9702c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -363,6 +363,20 @@ pub struct LinkEntry { pub import_name_type: Option<(PeImportNameType, Span)>, } +#[derive(HashStable_Generic, PrintAttribute)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum DebuggerVisualizerType { + Natvis, + GdbPrettyPrinter, +} + +#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] +pub struct DebugVisualizer { + pub span: Span, + pub visualizer_type: DebuggerVisualizerType, + pub path: Symbol, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -485,7 +499,10 @@ pub enum AttributeKind { /// Represents `#[custom_mir]`. CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), - ///Represents `#[rustc_deny_explicit_impl]`. + /// Represents `#[debugger_visualizer]`. + DebuggerVisualizer(ThinVec), + + /// Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 8e44340507478..7112daa327e2f 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -37,6 +37,7 @@ impl AttributeKind { Coverage(..) => No, CrateName { .. } => No, CustomMir(_, _, _) => Yes, + DebuggerVisualizer(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index 5a811321f58b9..a7f0095dcdfb8 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,15 +1,9 @@ use std::path::PathBuf; use std::sync::Arc; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_macros::{Decodable, Encodable, HashStable}; -#[derive(HashStable)] -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] -pub enum DebuggerVisualizerType { - Natvis, - GdbPrettyPrinter, -} - /// A single debugger visualizer file. #[derive(HashStable)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6cd68380e46f1..870e0a90b5421 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -93,15 +93,6 @@ passes_dead_codes = } } never {$participle} -passes_debug_visualizer_invalid = - invalid argument - .note_1 = expected: `natvis_file = "..."` - .note_2 = OR - .note_3 = expected: `gdb_script_file = "..."` - -passes_debug_visualizer_placement = - attribute should be applied to a module - passes_debug_visualizer_unreadable = couldn't read {$file}: {$error} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 94c9e71ce7705..9f40e713e5084 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -281,6 +281,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ObjcClass { .. } | AttributeKind::ObjcSelector { .. } | AttributeKind::RustcCoherenceIsCore(..) + | AttributeKind::DebuggerVisualizer(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -301,7 +302,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut doc_aliases, ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } @@ -1782,20 +1782,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. - fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) { - // Here we only check that the #[debugger_visualizer] attribute is attached - // to nothing other than a module. All other checks are done in the - // `debugger_visualizer` query where they need to be done for decoding - // anyway. - match target { - Target::Mod => {} - _ => { - self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() }); - } - } - } - /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7a7a8175e5569..7211f3cf85b31 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -1,67 +1,60 @@ //! Detecting usage of the `#[debugger_visualizer]` attribute. -use rustc_ast::Attribute; +use rustc_ast::ast::NodeId; +use rustc_ast::{HasNodeId, ItemKind, ast}; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::resolve_path; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, DebugVisualizer}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::sym; +use rustc_span::{DUMMY_SP, Span, sym}; -use crate::errors::{DebugVisualizerInvalid, DebugVisualizerUnreadable}; +use crate::errors::DebugVisualizerUnreadable; impl DebuggerVisualizerCollector<'_> { - fn check_for_debugger_visualizer(&mut self, attr: &Attribute) { - if attr.has_name(sym::debugger_visualizer) { - let Some(hints) = attr.meta_item_list() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; + fn check_for_debugger_visualizer( + &mut self, + attrs: &[ast::Attribute], + span: Span, + node_id: NodeId, + ) { + if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) = + AttributeParser::parse_limited( + &self.sess, + attrs, + sym::debugger_visualizer, + span, + node_id, + None, + ) + { + for DebugVisualizer { span, visualizer_type, path } in visualizers { + let file = match resolve_path(&self.sess, path.as_str(), span) { + Ok(file) => file, + Err(err) => { + err.emit(); + return; + } + }; - let [hint] = hints.as_slice() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let Some(meta_item) = hint.meta_item() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str()) - { - (Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value), - (Some(sym::gdb_script_file), Some(value)) => { - (DebuggerVisualizerType::GdbPrettyPrinter, value) - } - (_, _) => { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span }); - return; - } - }; - - let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) { - Ok(file) => file, - Err(err) => { - err.emit(); - return; - } - }; - - match self.sess.source_map().load_binary_file(&file) { - Ok((source, _)) => { - self.visualizers.push(DebuggerVisualizerFile::new( - source, - visualizer_type, - file, - )); - } - Err(error) => { - self.sess.dcx().emit_err(DebugVisualizerUnreadable { - span: meta_item.span, - file: &file, - error, - }); + match self.sess.source_map().load_binary_file(&file) { + Ok((source, _)) => { + self.visualizers.push(DebuggerVisualizerFile::new( + source, + visualizer_type, + file, + )); + } + Err(error) => { + self.sess.dcx().emit_err(DebugVisualizerUnreadable { + span, + file: &file, + error, + }); + } } } } @@ -74,9 +67,15 @@ struct DebuggerVisualizerCollector<'a> { } impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> { - fn visit_attribute(&mut self, attr: &'ast Attribute) { - self.check_for_debugger_visualizer(attr); - rustc_ast::visit::walk_attribute(self, attr); + fn visit_item(&mut self, item: &'ast rustc_ast::Item) -> Self::Result { + if let ItemKind::Mod(..) = item.kind { + self.check_for_debugger_visualizer(&item.attrs, item.span, item.node_id()); + } + rustc_ast::visit::walk_item(self, item); + } + fn visit_crate(&mut self, krate: &'ast ast::Crate) -> Self::Result { + self.check_for_debugger_visualizer(&krate.attrs, DUMMY_SP, krate.id); + rustc_ast::visit::walk_crate(self, krate); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index cd8935f6b2f07..cfd6b9e6dffaf 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -475,23 +475,6 @@ pub(crate) struct MacroOnlyAttribute { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_debug_visualizer_placement)] -pub(crate) struct DebugVisualizerPlacement { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_debug_visualizer_invalid)] -#[note(passes_note_1)] -#[note(passes_note_2)] -#[note(passes_note_3)] -pub(crate) struct DebugVisualizerInvalid { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_debug_visualizer_unreadable)] pub(crate) struct DebugVisualizerUnreadable<'a> { diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index e30479b03b11f..820484aa015d2 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -185,8 +185,7 @@ extern "C" { #[forbid] //~^ ERROR malformed #[debugger_visualizer] -//~^ ERROR invalid argument -//~| ERROR malformed `debugger_visualizer` attribute input +//~^ ERROR malformed `debugger_visualizer` attribute input #[automatically_derived = 18] //~^ ERROR malformed mod yooo { diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 246029ecf80fe..70ab3fb13c49e 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -22,7 +22,7 @@ LL | #[cfg_attr(condition, attribute, other_attribute, ...)] | ++++++++++++++++++++++++++++++++++++++++++++ error[E0463]: can't find crate for `wloop` - --> $DIR/malformed-attrs.rs:210:1 + --> $DIR/malformed-attrs.rs:209:1 | LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate @@ -156,22 +156,14 @@ LL | #[forbid(lint1, lint2, ...)] LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ -error: malformed `debugger_visualizer` attribute input - --> $DIR/malformed-attrs.rs:187:1 - | -LL | #[debugger_visualizer] - | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` - | - = note: for more information, visit - error: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:202:1 + --> $DIR/malformed-attrs.rs:201:1 | LL | #[thread_local()] | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[thread_local]` error: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:206:1 + --> $DIR/malformed-attrs.rs:205:1 | LL | #[no_link()] | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]` @@ -197,7 +189,7 @@ LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint - --> $DIR/malformed-attrs.rs:215:1 + --> $DIR/malformed-attrs.rs:214:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,16 +218,6 @@ LL | #[doc] = note: for more information, see issue #57571 = note: for more information, visit -error: invalid argument - --> $DIR/malformed-attrs.rs:187:1 - | -LL | #[debugger_visualizer] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected: `natvis_file = "..."` - = note: OR - = note: expected: `gdb_script_file = "..."` - error[E0539]: malformed `export_name` attribute input --> $DIR/malformed-attrs.rs:29:1 | @@ -685,8 +667,19 @@ LL | #[linkage = "external"] | ++++++++++++ = and 5 other candidates +error[E0539]: malformed `debugger_visualizer` attribute input + --> $DIR/malformed-attrs.rs:187:1 + | +LL | #[debugger_visualizer] + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` + | + = note: for more information, visit + error[E0565]: malformed `automatically_derived` attribute input - --> $DIR/malformed-attrs.rs:190:1 + --> $DIR/malformed-attrs.rs:189:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -695,7 +688,7 @@ LL | #[automatically_derived = 18] | help: must be of the form: `#[automatically_derived]` error[E0565]: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[non_exhaustive = 1] | ^^^^^^^^^^^^^^^^^---^ @@ -704,19 +697,19 @@ LL | #[non_exhaustive = 1] | help: must be of the form: `#[non_exhaustive]` error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` - --> $DIR/malformed-attrs.rs:208:1 + --> $DIR/malformed-attrs.rs:207:1 | LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` - --> $DIR/malformed-attrs.rs:213:1 + --> $DIR/malformed-attrs.rs:212:1 | LL | #[macro_export = 18] | ^^^^^^^^^^^^^^^^^^^^ error[E0565]: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:215:1 + --> $DIR/malformed-attrs.rs:214:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^---^ @@ -800,7 +793,7 @@ LL | #[ignore()] = note: for more information, see issue #57571 error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:222:1 + --> $DIR/malformed-attrs.rs:221:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ @@ -819,7 +812,7 @@ LL | #[coroutine = 63] || {} = note: expected unit type `()` found coroutine `{coroutine@$DIR/malformed-attrs.rs:110:23: 110:25}` -error: aborting due to 77 previous errors; 3 warnings emitted +error: aborting due to 76 previous errors; 3 warnings emitted Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. For more information about an error, try `rustc --explain E0308`. @@ -871,7 +864,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:222:1 + --> $DIR/malformed-attrs.rs:221:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ diff --git a/tests/ui/invalid/invalid-debugger-visualizer-option.rs b/tests/ui/invalid/invalid-debugger-visualizer-option.rs index 0f1cf15a6879a..166962866dce4 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-option.rs +++ b/tests/ui/invalid/invalid-debugger-visualizer-option.rs @@ -1,6 +1,6 @@ //@ normalize-stderr: "foo.random:.*\(" -> "foo.random: $$FILE_NOT_FOUND_MSG (" //@ normalize-stderr: "os error \d+" -> "os error $$FILE_NOT_FOUND_CODE" -#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument +#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR malformed `debugger_visualizer` attribute input #![debugger_visualizer(natvis_file = "../foo.random")] //~ ERROR fn main() {} diff --git a/tests/ui/invalid/invalid-debugger-visualizer-option.stderr b/tests/ui/invalid/invalid-debugger-visualizer-option.stderr index 6fbb4d641e6f6..e877e39d8f119 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-option.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-option.stderr @@ -1,18 +1,20 @@ -error: invalid argument - --> $DIR/invalid-debugger-visualizer-option.rs:4:24 - | -LL | #![debugger_visualizer(random_file = "../foo.random")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected: `natvis_file = "..."` - = note: OR - = note: expected: `gdb_script_file = "..."` - error: couldn't read $DIR/../foo.random: $FILE_NOT_FOUND_MSG (os error $FILE_NOT_FOUND_CODE) --> $DIR/invalid-debugger-visualizer-option.rs:5:24 | LL | #![debugger_visualizer(natvis_file = "../foo.random")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0539]: malformed `debugger_visualizer` attribute input + --> $DIR/invalid-debugger-visualizer-option.rs:4:1 + | +LL | #![debugger_visualizer(random_file = "../foo.random")] + | ^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^^^^^^^ + | | | + | | valid arguments are `natvis_file` or `gdb_script_file` + | help: must be of the form: `#![debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` + | + = note: for more information, visit + error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.rs b/tests/ui/invalid/invalid-debugger-visualizer-target.rs index 1efb9555c242a..48b041532140a 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.rs +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.rs @@ -1,2 +1,3 @@ -#[debugger_visualizer(natvis_file = "./foo.natvis.xml")] //~ ERROR attribute should be applied to a module +#[debugger_visualizer(natvis_file = "./foo.natvis.xml")] +//~^ ERROR `#[debugger_visualizer]` attribute cannot be used on functions fn main() {} diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr index 1df3453253372..629af94c5ef22 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr @@ -1,8 +1,10 @@ -error: attribute should be applied to a module +error: `#[debugger_visualizer]` attribute cannot be used on functions --> $DIR/invalid-debugger-visualizer-target.rs:1:1 | LL | #[debugger_visualizer(natvis_file = "./foo.natvis.xml")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[debugger_visualizer]` can be applied to modules and crates error: aborting due to 1 previous error From e1ae7b783781b93ec6a112ce586f75ac8db8c0d4 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 22 Sep 2025 07:22:37 +0200 Subject: [PATCH 0722/1900] tests: Check error when struct from wrong crate version is used for impl --- tests/run-make/duplicate-dependency/foo-v1.rs | 1 + tests/run-make/duplicate-dependency/foo-v2.rs | 1 + tests/run-make/duplicate-dependency/main.rs | 15 +++++++ .../run-make/duplicate-dependency/main.stderr | 18 ++++++++ .../duplicate-dependency/re-export-foo.rs | 3 ++ tests/run-make/duplicate-dependency/rmake.rs | 43 +++++++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 tests/run-make/duplicate-dependency/foo-v1.rs create mode 100644 tests/run-make/duplicate-dependency/foo-v2.rs create mode 100644 tests/run-make/duplicate-dependency/main.rs create mode 100644 tests/run-make/duplicate-dependency/main.stderr create mode 100644 tests/run-make/duplicate-dependency/re-export-foo.rs create mode 100644 tests/run-make/duplicate-dependency/rmake.rs diff --git a/tests/run-make/duplicate-dependency/foo-v1.rs b/tests/run-make/duplicate-dependency/foo-v1.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/run-make/duplicate-dependency/foo-v1.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/run-make/duplicate-dependency/foo-v2.rs b/tests/run-make/duplicate-dependency/foo-v2.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/run-make/duplicate-dependency/foo-v2.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/run-make/duplicate-dependency/main.rs b/tests/run-make/duplicate-dependency/main.rs new file mode 100644 index 0000000000000..b22d9581c9a40 --- /dev/null +++ b/tests/run-make/duplicate-dependency/main.rs @@ -0,0 +1,15 @@ +struct Bar; + +impl From for foo::Foo { + fn from(_: Bar) -> Self { + foo::Foo + } +} + +fn main() { + // The user might wrongly expect this to work since From for Foo + // implies Into for Bar. What the user missed is that different + // versions of Foo exist in the dependency graph, and the impl is for the + // wrong version. + re_export_foo::into_foo(Bar); +} diff --git a/tests/run-make/duplicate-dependency/main.stderr b/tests/run-make/duplicate-dependency/main.stderr new file mode 100644 index 0000000000000..2c912f872fe35 --- /dev/null +++ b/tests/run-make/duplicate-dependency/main.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `re_export_foo::foo::Foo: From` is not satisfied + --> main.rs:14:29 + | +LL | re_export_foo::into_foo(Bar); + | ----------------------- ^^^ the trait `From` is not implemented for `re_export_foo::foo::Foo` + | | + | required by a bound introduced by this call + | + = note: required for `Bar` to implement `Into` +note: required by a bound in `into_foo` + --> $DIR/re-export-foo.rs:3:25 + | +LL | pub fn into_foo(_: impl Into) {} + | ^^^^^^^^^^^^^^ required by this bound in `into_foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/run-make/duplicate-dependency/re-export-foo.rs b/tests/run-make/duplicate-dependency/re-export-foo.rs new file mode 100644 index 0000000000000..b346cfcee9b1f --- /dev/null +++ b/tests/run-make/duplicate-dependency/re-export-foo.rs @@ -0,0 +1,3 @@ +pub use foo; + +pub fn into_foo(_: impl Into) {} diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs new file mode 100644 index 0000000000000..13ab4caaba56f --- /dev/null +++ b/tests/run-make/duplicate-dependency/rmake.rs @@ -0,0 +1,43 @@ +use run_make_support::{Rustc, cwd, diff, rust_lib_name, rustc}; + +fn rustc_with_common_args() -> Rustc { + let mut rustc = rustc(); + rustc.remap_path_prefix(cwd(), "$DIR"); + rustc.edition("2018"); // Don't require `extern crate` + rustc +} + +fn main() { + rustc_with_common_args() + .input("foo-v1.rs") + .crate_type("rlib") + .crate_name("foo") + .extra_filename("-v1") + .metadata("-v1") + .run(); + + rustc_with_common_args() + .input("foo-v2.rs") + .crate_type("rlib") + .crate_name("foo") + .extra_filename("-v2") + .metadata("-v2") + .run(); + + rustc_with_common_args() + .input("re-export-foo.rs") + .crate_type("rlib") + .extern_("foo", rust_lib_name("foo-v2")) + .run(); + + let stderr = rustc_with_common_args() + .input("main.rs") + .extern_("foo", rust_lib_name("foo-v1")) + .extern_("re_export_foo", rust_lib_name("re_export_foo")) + .library_search_path(cwd()) + .ui_testing() + .run_fail() + .stderr_utf8(); + + diff().expected_file("main.stderr").actual_text("(rustc)", &stderr).run(); +} From dc805bf49844d7bde1013f817c4368c1c3c485aa Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 22 Sep 2025 14:18:48 +0800 Subject: [PATCH 0723/1900] Fix apply in internal if for pull_assignment_up Example --- ```rust fn foo() { let mut a = 1; if true { a = 2; } else if true { $0a = 3; } else { a = 4; } } ``` **Before this PR**: ```rust fn foo() { let mut a = 1; if true { a = 2; } else a = if true { 3 } else { 4 }; } ``` **After this PR**: ```rust fn foo() { let mut a = 1; a = if true { 2 } else if true { 3 } else { 4 }; } ``` --- .../src/handlers/pull_assignment_up.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 21debf6745a67..00902fafe8278 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -53,6 +53,10 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> }; let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::() { + let if_expr = std::iter::successors(Some(if_expr), |it| { + it.syntax().parent().and_then(ast::IfExpr::cast) + }) + .last()?; collector.collect_if(&if_expr)?; if_expr.into() } else if let Some(match_expr) = ctx.find_node_at_offset::() { @@ -237,6 +241,37 @@ fn foo() { ); } + #[test] + fn test_pull_assignment_up_inner_if() { + check_assist( + pull_assignment_up, + r#" +fn foo() { + let mut a = 1; + + if true { + a = 2; + } else if true { + $0a = 3; + } else { + a = 4; + } +}"#, + r#" +fn foo() { + let mut a = 1; + + a = if true { + 2 + } else if true { + 3 + } else { + 4 + }; +}"#, + ); + } + #[test] fn test_pull_assignment_up_match() { check_assist( From c5e668274b56a0c83745336352fd9df9fe68e50a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 22 Sep 2025 14:45:31 +0800 Subject: [PATCH 0724/1900] Fix applicable on underscore for bind_unused_param Fixes: - applicable on underscore prefix parameter - using binding mode as an expression Examples --- ```rust fn foo($0_x: i32, y: i32) {} ``` **Before this PR**: ```rust fn foo(_x: i32, y: i32) { let _ = _x; } ``` **After this PR**: Assist not applicable --- ```rust fn foo(ref $0y: i32) {} ``` **Before this PR**: ```rust fn foo(ref y: i32) { let _ = ref y; } ``` **After this PR**: ```rust fn foo(ref y: i32) { let _ = y; } ``` --- .../src/handlers/bind_unused_param.rs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs index 00c7d25b257b2..1b24f7fe7ffba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -2,7 +2,7 @@ use crate::assist_context::{AssistContext, Assists}; use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition}; use syntax::{ AstNode, - ast::{self, edit_in_place::Indent}, + ast::{self, HasName, edit_in_place::Indent}, }; // Assist: bind_unused_param @@ -22,6 +22,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let param: ast::Param = ctx.find_node_at_offset()?; let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; + let name = ident_pat.name().filter(|n| !n.text().starts_with('_'))?; let param_def = { let local = ctx.sema.to_def(&ident_pat)?; @@ -39,14 +40,14 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O acc.add( AssistId::quick_fix("bind_unused_param"), - format!("Bind as `let _ = {ident_pat};`"), + format!("Bind as `let _ = {name};`"), param.syntax().text_range(), |builder| { let line_index = ctx.db().line_index(ctx.vfs_file_id()); let indent = func.indent_level(); let text_indent = indent + 1; - let mut text = format!("\n{text_indent}let _ = {ident_pat};"); + let mut text = format!("\n{text_indent}let _ = {name};"); let left_line = line_index.line_col(l_curly_range.end()).line; let right_line = line_index.line_col(r_curly_range.start()).line; @@ -83,6 +84,22 @@ fn foo(y: i32) { ); } + #[test] + fn bind_unused_ref_ident_pat() { + cov_mark::check!(single_line); + check_assist( + bind_unused_param, + r#" +fn foo(ref $0y: i32) {} +"#, + r#" +fn foo(ref y: i32) { + let _ = y; +} +"#, + ); + } + #[test] fn bind_unused_empty_block_with_newline() { check_assist( @@ -149,6 +166,16 @@ impl Trait for () { bind_unused_param, r#" fn foo(x: i32, $0y: i32) { y; } +"#, + ); + } + + #[test] + fn keep_underscore_used() { + check_assist_not_applicable( + bind_unused_param, + r#" +fn foo($0_x: i32, y: i32) {} "#, ); } From 4193716963035cccc26dfde9dbf6a78a133dfb60 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 22 Sep 2025 10:26:03 +0300 Subject: [PATCH 0725/1900] Another regression test for next solver fixed bug --- .../hir-ty/src/tests/regression/new_solver.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index ead79a8f5b90b..c7711f31bf262 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -418,3 +418,35 @@ fn foo() { "#]], ); } + +#[test] +fn regression_19637() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized +pub trait Any {} + +impl Any for T {} + +pub trait Trait: Any { + type F; +} + +pub struct TT {} + +impl Trait for TT { + type F = f32; +} + +pub fn coercion(x: &mut dyn Any) -> &mut dyn Any { + x +} + +fn main() { + let mut t = TT {}; + let tt = &mut t as &mut dyn Trait; + let st = coercion(tt); +} + "#, + ); +} From 7d0012914ea223ba1f6b7dae1ecd31be95e0051a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 09:28:38 +0200 Subject: [PATCH 0726/1900] assert_unsafe_precondition: fix some incorrect check_language_ub --- library/core/src/ascii/ascii_char.rs | 2 +- library/core/src/num/int_macros.rs | 6 +++--- library/core/src/num/uint_macros.rs | 4 ++-- library/core/src/slice/index.rs | 2 +- library/core/src/ub_checks.rs | 5 +++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 178af2c0e3b46..d77fafed2039b 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -515,7 +515,7 @@ impl AsciiChar { #[track_caller] pub const unsafe fn digit_unchecked(d: u8) -> Self { assert_unsafe_precondition!( - check_language_ub, + check_library_ub, "`ascii::Char::digit_unchecked` input cannot exceed 9.", (d: u8 = d) => d < 10 ); diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 64a3dd3e8bc54..0d80c40fb2363 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1460,8 +1460,8 @@ macro_rules! int_impl { #[inline] pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( - check_language_ub, - concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out non-zero bits"), + check_library_ub, + concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out bits that would change the value of the first bit"), ( zeros: u32 = self.leading_zeros(), ones: u32 = self.leading_ones(), @@ -1638,7 +1638,7 @@ macro_rules! int_impl { #[inline] pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( - check_language_ub, + check_library_ub, concat!(stringify!($SelfT), "::unchecked_exact_shr cannot shift out non-zero bits"), ( zeros: u32 = self.trailing_zeros(), diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index bf72ec8319704..d68c7be9865b1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1865,7 +1865,7 @@ macro_rules! uint_impl { #[inline] pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( - check_language_ub, + check_library_ub, concat!(stringify!($SelfT), "::exact_shl_unchecked cannot shift out non-zero bits"), ( zeros: u32 = self.leading_zeros(), @@ -2037,7 +2037,7 @@ macro_rules! uint_impl { #[inline] pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( - check_language_ub, + check_library_ub, concat!(stringify!($SelfT), "::exact_shr_unchecked cannot shift out non-zero bits"), ( zeros: u32 = self.trailing_zeros(), diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index a8147d745f3ab..d4c466201edf5 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -233,7 +233,7 @@ unsafe impl const SliceIndex<[T]> for usize { #[track_caller] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { assert_unsafe_precondition!( - check_language_ub, + check_language_ub, // okay because of the `assume` below "slice::get_unchecked requires that the index is within the slice", (this: usize = self, len: usize = slice.len()) => this < len ); diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index b809294cfcee7..514ff93c9820e 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -21,8 +21,9 @@ use crate::intrinsics::{self, const_eval_select}; /// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice /// diagnostic, but our ability to detect UB is unchanged. /// But if `check_language_ub` is used when the check is actually for library UB, the check is -/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the -/// library UB, the backtrace Miri reports may be far removed from original cause. +/// omitted in const-eval/Miri and thus UB might occur undetected. Even if we eventually execute +/// language UB which relies on the library UB, the backtrace Miri reports may be far removed from +/// original cause. /// /// These checks are behind a condition which is evaluated at codegen time, not expansion time like /// [`debug_assert`]. This means that a standard library built with optimizations and debug From e9b2c4f395673399b0445733c7e8d3955e42ff0c Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Thu, 15 May 2025 15:46:53 +0300 Subject: [PATCH 0727/1900] avoid violating `slice::from_raw_parts` safety contract in `Vec::extract_if` The implementation of the `Vec::extract_if` iterator violates the safety contract adverized by `slice::from_raw_parts` by always constructing a mutable slice for the entire length of the vector even though that span of memory can contain holes from items already drained. The safety contract of `slice::from_raw_parts` requires that all elements must be properly initialized. As an example we can look at the following code: ```rust let mut v = vec![Box::new(0u64), Box::new(1u64)]; for item in v.extract_if(.., |x| **x == 0) { drop(item); } ``` In the second iteration a `&mut [Box]` slice of length 2 will be constructed. The first slot of the slice contains the bitpattern of an already deallocated box, which is invalid. This fixes the issue by only creating references to valid items and using pointer manipulation for the rest. I have also taken the liberty to remove the big `unsafe` blocks in place of targetted ones with a SAFETY comment. The approach closely mirrors the implementation of `Vec::retain_mut`. Signed-off-by: Petros Angelatos --- library/alloc/src/vec/extract_if.rs | 64 ++++++++++++++++++----------- src/tools/miri/tests/pass/vec.rs | 10 +++++ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index a456d3d9e602d..cb9e14f554d41 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -64,27 +64,37 @@ where type Item = T; fn next(&mut self) -> Option { - unsafe { - while self.idx < self.end { - let i = self.idx; - let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); - let drained = (self.pred)(&mut v[i]); - // Update the index *after* the predicate is called. If the index - // is updated prior and the predicate panics, the element at this - // index would be leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(ptr::read(&v[i])); - } else if self.del > 0 { - let del = self.del; - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - ptr::copy_nonoverlapping(src, dst, 1); + while self.idx < self.end { + let i = self.idx; + // SAFETY: + // We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from + // the validity of `Self`. Therefore `i` points to an element within `vec`. + // + // Additionally, the i-th element is valid because each element is visited at most once + // and it is the first time we access vec[i]. + // + // Note: we can't use `vec.get_unchecked_mut(i)` here since the precondition for that + // function is that i < vec.len(), but we've set vec's length to zero. + let cur = unsafe { &mut *self.vec.as_mut_ptr().add(i) }; + let drained = (self.pred)(cur); + // Update the index *after* the predicate is called. If the index + // is updated prior and the predicate panics, the element at this + // index would be leaked. + self.idx += 1; + if drained { + self.del += 1; + // SAFETY: We never touch this element again after returning it. + return Some(unsafe { ptr::read(cur) }); + } else if self.del > 0 { + // SAFETY: `self.del` > 0, so the hole slot must not overlap with current element. + // We use copy for move, and never touch this element again. + unsafe { + let hole_slot = self.vec.as_mut_ptr().add(i - self.del); + ptr::copy_nonoverlapping(cur, hole_slot, 1); } } - None } + None } fn size_hint(&self) -> (usize, Option) { @@ -95,14 +105,18 @@ where #[stable(feature = "extract_if", since = "1.87.0")] impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { - unsafe { - if self.idx < self.old_len && self.del > 0 { - let ptr = self.vec.as_mut_ptr(); - let src = ptr.add(self.idx); - let dst = src.sub(self.del); - let tail_len = self.old_len - self.idx; - src.copy_to(dst, tail_len); + if self.del > 0 { + // SAFETY: Trailing unchecked items must be valid since we never touch them. + unsafe { + ptr::copy( + self.vec.as_ptr().add(self.idx), + self.vec.as_mut_ptr().add(self.idx - self.del), + self.old_len - self.idx, + ); } + } + // SAFETY: After filling holes, all items are in contiguous memory. + unsafe { self.vec.set_len(self.old_len - self.del); } } diff --git a/src/tools/miri/tests/pass/vec.rs b/src/tools/miri/tests/pass/vec.rs index 3e526813bb457..8b1b1e143b16d 100644 --- a/src/tools/miri/tests/pass/vec.rs +++ b/src/tools/miri/tests/pass/vec.rs @@ -169,6 +169,15 @@ fn miri_issue_2759() { input.replace_range(0..0, "0"); } +/// This was skirting the edge of UB, let's make sure it remains on the sound side. +/// Context: . +fn extract_if() { + let mut v = vec![Box::new(0u64), Box::new(1u64)]; + for item in v.extract_if(.., |x| **x == 0) { + drop(item); + } +} + fn main() { assert_eq!(vec_reallocate().len(), 5); @@ -199,4 +208,5 @@ fn main() { swap_remove(); reverse(); miri_issue_2759(); + extract_if(); } From 8d7e637dc947e11fab4294ec032cfc1e252e421d Mon Sep 17 00:00:00 2001 From: sysrex <769991+sysrex@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:15:50 +0100 Subject: [PATCH 0728/1900] chore: remove dead links --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b52c3dfff61d..2b5699dcd098d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,5 +47,4 @@ refer to [this section][contributing-bug-reports] and [open an issue][issue temp [contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports [issue template]: https://github.com/rust-lang/rust/issues/new/choose [internals]: https://internals.rust-lang.org -[rust-discord]: http://discord.gg/rust-lang [rust-zulip]: https://rust-lang.zulipchat.com From e37f0fed052701f38c9b61541f565b834106ad32 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 17:32:50 +0800 Subject: [PATCH 0729/1900] Add regression test --- .../rustdoc-merge-no-input-finalize/rmake.rs | 22 +++++++++++++++++++ .../rustdoc-merge-no-input-finalize/sierra.rs | 1 + 2 files changed, 23 insertions(+) create mode 100644 tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs create mode 100644 tests/run-make/rustdoc-merge-no-input-finalize/sierra.rs diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs new file mode 100644 index 0000000000000..6df9e95829d8d --- /dev/null +++ b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs @@ -0,0 +1,22 @@ +// Running --merge=finalize without an input crate root should not trigger ICE. +// Issue: https://github.com/rust-lang/rust/issues/146646 + +//@ needs-target-std + +use run_make_support::rustdoc; + +fn main() { + rustdoc() + .input("sierra.rs") + .arg("-Zunstable-options") + .arg("--parts-out-dir=parts") + .arg("--merge=none") + .run(); + + rustdoc() + .arg("-Zunstable-options") + .arg("--include-parts-dir=parts") + .arg("--merge=finalize") + .out_dir("out") + .run(); +} diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/sierra.rs b/tests/run-make/rustdoc-merge-no-input-finalize/sierra.rs new file mode 100644 index 0000000000000..f8fc48341ed6b --- /dev/null +++ b/tests/run-make/rustdoc-merge-no-input-finalize/sierra.rs @@ -0,0 +1 @@ +pub struct Sierra; From 1c316023d65be6ae88ff938d272669bc286fd5b3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 12:24:22 +0200 Subject: [PATCH 0730/1900] =?UTF-8?q?TB:=20rename=20Active=20=E2=86=92=20U?= =?UTF-8?q?nique=20to=20match=20paper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tree_borrows/diagnostics.rs | 4 +- .../src/borrow_tracker/tree_borrows/perms.rs | 102 +++++++++--------- .../src/borrow_tracker/tree_borrows/tree.rs | 18 ++-- .../borrow_tracker/tree_borrows/tree/tests.rs | 4 +- .../fail/async-shared-mutable.tree.stderr | 2 +- .../box_noalias_violation.tree.stderr | 4 +- .../both_borrows/illegal_write6.tree.stderr | 4 +- .../arg_inplace_locals_alias.tree.stderr | 4 +- .../arg_inplace_locals_alias_ret.tree.stderr | 4 +- .../arg_inplace_mutate.tree.stderr | 4 +- .../arg_inplace_observe_during.tree.stderr | 4 +- .../return_pointer_aliasing_read.tree.stderr | 4 +- .../return_pointer_aliasing_write.tree.stderr | 4 +- ...inter_aliasing_write_tail_call.tree.stderr | 4 +- .../tree_borrows/alternate-read-write.stderr | 2 +- .../tree_borrows/fnentry_invalidation.stderr | 2 +- .../parent_read_freezes_raw_mut.stderr | 2 +- .../fail/tree_borrows/pass_invalid_mut.stderr | 2 +- .../tree_borrows/reservedim_spurious_write.rs | 3 +- .../tree_borrows/return_invalid_mut.stderr | 2 +- .../subtree_traversal_skipping_diagnostics.rs | 2 +- .../fail/tree_borrows/unique.default.stderr | 32 ------ .../pass/tree_borrows/cell-inside-box.rs | 2 +- .../pass/tree_borrows/reborrow-is-read.rs | 4 +- .../miri/tests/pass/tree_borrows/reserved.rs | 4 +- 25 files changed, 95 insertions(+), 128 deletions(-) delete mode 100644 src/tools/miri/tests/fail/tree_borrows/unique.default.stderr diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 7b4c533cfaed7..a7977cd3366f9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -244,8 +244,8 @@ pub(super) enum TransitionError { ChildAccessForbidden(Permission), /// A protector was triggered due to an invalid transition that loses /// too much permissions. - /// For example, if a protected tag goes from `Active` to `Disabled` due - /// to a foreign write this will produce a `ProtectedDisabled(Active)`. + /// For example, if a protected tag goes from `Unique` to `Disabled` due + /// to a foreign write this will produce a `ProtectedDisabled(Unique)`. /// This kind of error can only occur on foreign accesses. ProtectedDisabled(Permission), /// Cannot deallocate because some tag in the allocation is strongly protected. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 390435e58d173..e21775c9f239f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -14,7 +14,7 @@ enum PermissionPriv { Cell, /// represents: a local mutable reference that has not yet been written to; /// allows: child reads, foreign reads; - /// affected by: child writes (becomes Active), + /// affected by: child writes (becomes Unique), /// rejects: foreign writes (Disabled). /// /// `ReservedFrz` is mostly for types that are `Freeze` (no interior mutability). @@ -31,17 +31,17 @@ enum PermissionPriv { /// This is so that the behavior of `Reserved` adheres to the rules of `noalias`: /// - foreign-read then child-write is UB due to `conflicted`, /// - child-write then foreign-read is UB since child-write will activate and then - /// foreign-read disables a protected `Active`, which is UB. + /// foreign-read disables a protected `Unique`, which is UB. ReservedFrz { conflicted: bool }, /// Alternative version of `ReservedFrz` made for types with interior mutability. /// allows: child reads, foreign reads, foreign writes (extra); - /// affected by: child writes (becomes Active); + /// affected by: child writes (becomes Unique); /// rejects: nothing. ReservedIM, /// represents: a unique pointer; /// allows: child reads, child writes; /// rejects: foreign reads (Frozen), foreign writes (Disabled). - Active, + Unique, /// represents: a shared pointer; /// allows: all read accesses; /// rejects child writes (UB), foreign writes (Disabled). @@ -56,7 +56,7 @@ use super::foreign_access_skipping::IdempotentForeignAccess; impl PartialOrd for PermissionPriv { /// PermissionPriv is ordered by the reflexive transitive closure of - /// `Reserved(conflicted=false) < Reserved(conflicted=true) < Active < Frozen < Disabled`. + /// `Reserved(conflicted=false) < Reserved(conflicted=true) < Unique < Frozen < Disabled`. /// `Reserved` that have incompatible `ty_is_freeze` are incomparable to each other. /// This ordering matches the reachability by transitions, as asserted by the exhaustive test /// `permissionpriv_partialord_is_reachability`. @@ -76,8 +76,8 @@ impl PartialOrd for PermissionPriv { (_, Disabled) => Less, (Frozen, _) => Greater, (_, Frozen) => Less, - (Active, _) => Greater, - (_, Active) => Less, + (Unique, _) => Greater, + (_, Unique) => Less, (ReservedIM, ReservedIM) => Equal, (ReservedFrz { conflicted: c1 }, ReservedFrz { conflicted: c2 }) => { // `bool` is ordered such that `false <= true`, so this works as intended. @@ -115,8 +115,8 @@ impl PermissionPriv { // Famously, ReservedIM survives foreign writes. It is never protected. ReservedIM if prot => unreachable!("Protected ReservedIM should not exist!"), ReservedIM => IdempotentForeignAccess::Write, - // Active changes on any foreign access (becomes Frozen/Disabled). - Active => IdempotentForeignAccess::None, + // Unique changes on any foreign access (becomes Frozen/Disabled). + Unique => IdempotentForeignAccess::None, // Frozen survives foreign reads, but not writes. Frozen => IdempotentForeignAccess::Read, // Disabled survives foreign reads and writes. It survives them @@ -139,12 +139,12 @@ mod transition { Disabled => return None, // The inner data `ty_is_freeze` of `Reserved` is always irrelevant for Read // accesses, since the data is not being mutated. Hence the `{ .. }`. - readable @ (Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen) => readable, + readable @ (Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen) => readable, }) } /// A non-child node was read-accessed: keep `Reserved` but mark it as `conflicted` if it - /// is protected; invalidate `Active`. + /// is protected; invalidate `Unique`. fn foreign_read(state: PermissionPriv, protected: bool) -> Option { Some(match state { // Cell ignores foreign reads. @@ -167,10 +167,10 @@ mod transition { assert!(!protected); res } - Active => + Unique => if protected { // We wrote, someone else reads -- that's bad. - // (Since Active is always initialized, this move-to-protected will mean insta-UB.) + // (Since Unique is always initialized, this move-to-protected will mean insta-UB.) Disabled } else { // We don't want to disable here to allow read-read reordering: it is crucial @@ -180,7 +180,7 @@ mod transition { }) } - /// A child node was write-accessed: `Reserved` must become `Active` to obtain + /// A child node was write-accessed: `Reserved` must become `Unique` to obtain /// write permissions, `Frozen` and `Disabled` cannot obtain such permissions and produce UB. fn child_write(state: PermissionPriv, protected: bool) -> Option { Some(match state { @@ -192,7 +192,7 @@ mod transition { ReservedFrz { conflicted: true } if protected => return None, // A write always activates the 2-phase borrow, even with interior // mutability - ReservedFrz { .. } | ReservedIM | Active => Active, + ReservedFrz { .. } | ReservedIM | Unique => Unique, Frozen | Disabled => return None, }) } @@ -266,8 +266,8 @@ impl Permission { /// Default initial permission of the root of a new tree at inbounds positions. /// Must *only* be used for the root, this is not in general an "initial" permission! - pub fn new_active() -> Self { - Self { inner: Active } + pub fn new_unique() -> Self { + Self { inner: Unique } } /// Default initial permission of a reborrowed mutable reference that is either @@ -309,7 +309,7 @@ impl Permission { // Do not do perform access if it is a `Cell`, as this // can cause data races when using thread-safe data types. Cell => None, - Active => Some(AccessKind::Write), + Unique => Some(AccessKind::Write), _ => Some(AccessKind::Read), } } @@ -344,7 +344,7 @@ impl Permission { (_, Cell) => false, // ReservedIM can be replaced by anything besides Cell. // ReservedIM allows all transitions, but unlike Cell, a local write - // to ReservedIM transitions to Active, while it is a no-op for Cell. + // to ReservedIM transitions to Unique, while it is a no-op for Cell. (ReservedIM, _) => true, (_, ReservedIM) => false, // Reserved (as parent, where conflictedness does not matter) @@ -352,12 +352,12 @@ impl Permission { // since ReservedIM and Cell alone would survive foreign writes (ReservedFrz { .. }, _) => true, (_, ReservedFrz { .. }) => false, - // Active can not be replaced by something surviving + // Unique can not be replaced by something surviving // foreign reads and then remaining writable (i.e., Reserved*). // Replacing a state by itself is always okay, even if the child state is protected. - // Active can be replaced by Frozen, since it is not protected. - (Active, Active | Frozen | Disabled) => true, - (_, Active) => false, + // Unique can be replaced by Frozen, since it is not protected. + (Unique, Unique | Frozen | Disabled) => true, + (_, Unique) => false, // Frozen can only be replaced by Disabled (and itself). (Frozen, Frozen | Disabled) => true, (_, Frozen) => false, @@ -410,7 +410,7 @@ pub mod diagnostics { ReservedFrz { conflicted: false } => "Reserved", ReservedFrz { conflicted: true } => "Reserved (conflicted)", ReservedIM => "Reserved (interior mutable)", - Active => "Active", + Unique => "Unique", Frozen => "Frozen", Disabled => "Disabled", } @@ -441,7 +441,7 @@ pub mod diagnostics { ReservedFrz { conflicted: false } => "Res ", ReservedFrz { conflicted: true } => "ResC", ReservedIM => "ReIM", - Active => "Act ", + Unique => "Act ", Frozen => "Frz ", Disabled => "Dis ", } @@ -455,7 +455,7 @@ pub mod diagnostics { assert!(self.is_possible()); assert!(!self.is_noop()); match (self.from, self.to) { - (_, Active) => "the first write to a 2-phase borrowed mutable reference", + (_, Unique) => "the first write to a 2-phase borrowed mutable reference", (_, Frozen) => "a loss of write permissions", (ReservedFrz { conflicted: false }, ReservedFrz { conflicted: true }) => "a temporary loss of write permissions until function exit", @@ -472,8 +472,8 @@ pub mod diagnostics { /// /// Irrelevant events: /// - modifications of write permissions when the error is related to read permissions - /// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Active`, - /// `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Active -> Frozen`) + /// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Unique`, + /// `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Unique -> Frozen`) /// - all transitions for attempts to deallocate strongly protected tags /// /// # Panics @@ -481,10 +481,10 @@ pub mod diagnostics { /// This function assumes that its arguments apply to the same location /// and that they were obtained during a normal execution. It will panic otherwise. /// - all transitions involved in `self` and `err` should be increasing - /// (Reserved < Active < Frozen < Disabled); + /// (Reserved < Unique < Frozen < Disabled); /// - between `self` and `err` the permission should also be increasing, /// so all permissions inside `err` should be greater than `self.1`; - /// - `Active`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error + /// - `Unique`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error /// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)` /// of either of them; /// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected @@ -500,11 +500,11 @@ pub mod diagnostics { TransitionError::ChildAccessForbidden(insufficient) => { // Show where the permission was gained then lost, // but ignore unrelated permissions. - // This eliminates transitions like `Active -> Frozen` + // This eliminates transitions like `Unique -> Frozen` // when the error is a failed `Read`. match (self.to, insufficient.inner) { (Frozen, Frozen) => true, - (Active, Frozen) => true, + (Unique, Frozen) => true, (Disabled, Disabled) => true, ( ReservedFrz { conflicted: true, .. }, @@ -512,14 +512,14 @@ pub mod diagnostics { ) => true, // A pointer being `Disabled` is a strictly stronger source of // errors than it being `Frozen`. If we try to access a `Disabled`, - // then where it became `Frozen` (or `Active` or `Reserved`) is the least + // then where it became `Frozen` (or `Unique` or `Reserved`) is the least // of our concerns for now. - (ReservedFrz { conflicted: true } | Active | Frozen, Disabled) => false, + (ReservedFrz { conflicted: true } | Unique | Frozen, Disabled) => false, (ReservedFrz { conflicted: true }, Frozen) => false, - // `Active`, `Reserved`, and `Cell` have all permissions, so a - // `ChildAccessForbidden(Reserved | Active)` can never exist. - (_, Active) | (_, ReservedFrz { conflicted: false }) | (_, Cell) => + // `Unique`, `Reserved`, and `Cell` have all permissions, so a + // `ChildAccessForbidden(Reserved | Unique)` can never exist. + (_, Unique) | (_, ReservedFrz { conflicted: false }) | (_, Cell) => unreachable!("this permission cannot cause an error"), // No transition has `Reserved { conflicted: false }` or `ReservedIM` // as its `.to` unless it's a noop. `Cell` cannot be in its `.to` @@ -527,11 +527,11 @@ pub mod diagnostics { (ReservedFrz { conflicted: false } | ReservedIM | Cell, _) => unreachable!("self is a noop transition"), // All transitions produced in normal executions (using `apply_access`) - // change permissions in the order `Reserved -> Active -> Frozen -> Disabled`. + // change permissions in the order `Reserved -> Unique -> Frozen -> Disabled`. // We assume that the error was triggered on the same location that // the transition `self` applies to, so permissions found must be increasing // in the order `self.from < self.to <= insufficient.inner` - (Active | Frozen | Disabled, ReservedFrz { .. } | ReservedIM) + (Unique | Frozen | Disabled, ReservedFrz { .. } | ReservedIM) | (Disabled, Frozen) | (ReservedFrz { .. }, ReservedIM) => unreachable!("permissions between self and err must be increasing"), @@ -540,29 +540,29 @@ pub mod diagnostics { TransitionError::ProtectedDisabled(before_disabled) => { // Show how we got to the starting point of the forbidden transition, // but ignore what came before. - // This eliminates transitions like `Reserved -> Active` + // This eliminates transitions like `Reserved -> Unique` // when the error is a `Frozen -> Disabled`. match (self.to, before_disabled.inner) { // We absolutely want to know where it was activated/frozen/marked // conflicted. - (Active, Active) => true, + (Unique, Unique) => true, (Frozen, Frozen) => true, ( ReservedFrz { conflicted: true, .. }, ReservedFrz { conflicted: true, .. }, ) => true, // If the error is a transition `Frozen -> Disabled`, then we don't really - // care whether before that was `Reserved -> Active -> Frozen` or + // care whether before that was `Reserved -> Unique -> Frozen` or // `Frozen` directly. // The error will only show either // - created as Reserved { conflicted: false }, // then Reserved { .. } -> Disabled is forbidden // - created as Reserved { conflicted: false }, - // then Active -> Disabled is forbidden + // then Unique -> Disabled is forbidden // A potential `Reserved { conflicted: false } // -> Reserved { conflicted: true }` is inexistant or irrelevant, - // and so is the `Reserved { conflicted: false } -> Active` - (Active, Frozen) => false, + // and so is the `Reserved { conflicted: false } -> Unique` + (Unique, Frozen) => false, (ReservedFrz { conflicted: true }, _) => false, (_, Disabled) => @@ -575,12 +575,12 @@ pub mod diagnostics { (ReservedFrz { conflicted: false } | ReservedIM | Cell, _) => unreachable!("self is a noop transition"), - // Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`, + // Permissions only evolve in the order `Reserved -> Unique -> Frozen -> Disabled`, // so permissions found must be increasing in the order // `self.from < self.to <= forbidden.from < forbidden.to`. - (Disabled, Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen) - | (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Active) - | (Active, Cell | ReservedFrz { .. } | ReservedIM) => + (Disabled, Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen) + | (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Unique) + | (Unique, Cell | ReservedFrz { .. } | ReservedIM) => unreachable!("permissions between self and err must be increasing"), } } @@ -617,7 +617,7 @@ mod propagation_optimization_checks { impl Exhaustive for PermissionPriv { fn exhaustive() -> Box> { Box::new( - vec![Active, Frozen, Disabled, ReservedIM, Cell] + vec![Unique, Frozen, Disabled, ReservedIM, Cell] .into_iter() .chain(::exhaustive().map(|conflicted| ReservedFrz { conflicted })), ) @@ -730,7 +730,7 @@ mod propagation_optimization_checks { #[test] // Check that all transitions are consistent with the order on PermissionPriv, - // i.e. Reserved -> Active -> Frozen -> Disabled + // i.e. Reserved -> Unique -> Frozen -> Disabled fn permissionpriv_partialord_is_reachability() { let reach = { let mut reach = rustc_data_structures::fx::FxHashSet::default(); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 22bd63bd6b6f4..9e7d272ee4b00 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -57,7 +57,7 @@ pub(super) struct LocationState { impl LocationState { /// Constructs a new initial state. It has neither been accessed, nor been subjected /// to any foreign access yet. - /// The permission is not allowed to be `Active`. + /// The permission is not allowed to be `Unique`. /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs` pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self { assert!(permission.is_initial() || permission.is_disabled()); @@ -80,7 +80,7 @@ impl LocationState { /// Check if the state can exist as the initial permission of a pointer. /// /// Do not confuse with `is_accessed`, the two are almost orthogonal - /// as apart from `Active` which is not initial and must be accessed, + /// as apart from `Unique` which is not initial and must be accessed, /// any other permission can have an arbitrary combination of being /// initial/accessed. /// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally @@ -170,7 +170,7 @@ impl LocationState { } if self.permission.is_frozen() && access_kind == AccessKind::Read { // A foreign read to a `Frozen` tag will have almost no observable effect. - // It's a theorem that `Frozen` nodes have no active children, so all children + // It's a theorem that `Frozen` nodes have no `Unique` children, so all children // already survive foreign reads. Foreign reads in general have almost no // effect, the only further thing they could do is make protected `Reserved` // nodes become conflicted, i.e. make them reject child writes for the further @@ -265,7 +265,7 @@ pub(super) struct Node { pub children: SmallVec<[UniIndex; 4]>, /// Either `Reserved`, `Frozen`, or `Disabled`, it is the permission this tag will /// lazily be initialized to on the first access. - /// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by + /// It is only ever `Disabled` for a tree root, since the root is initialized to `Unique` by /// its own separate mechanism. default_initial_perm: Permission, /// The default initial (strongest) idempotent foreign access. @@ -598,14 +598,14 @@ impl Tree { }; let rperms = { let mut perms = UniValMap::default(); - // We manually set it to `Active` on all in-bounds positions. - // We also ensure that it is accessed, so that no `Active` but + // We manually set it to `Unique` on all in-bounds positions. + // We also ensure that it is accessed, so that no `Unique` but // not yet accessed nodes exist. Essentially, we pretend there - // was a write that initialized these to `Active`. + // was a write that initialized these to `Unique`. perms.insert( root_idx, LocationState::new_accessed( - Permission::new_active(), + Permission::new_unique(), IdempotentForeignAccess::None, ), ); @@ -790,7 +790,7 @@ impl<'tcx> Tree { /// - the access will be applied only to accessed locations of the allocation, /// - it will not be visible to children, /// - it will be recorded as a `FnExit` diagnostic access - /// - and it will be a read except if the location is `Active`, i.e. has been written to, + /// - and it will be a read except if the location is `Unique`, i.e. has been written to, /// in which case it will be a write. /// /// `LocationState::perform_access` will take care of raising transition diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index d9b3696e4f805..83232615616e6 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -420,7 +420,7 @@ mod spurious_read { /// `(LocStateProt, LocStateProt)` where the two states are not guaranteed /// to be updated at the same time. /// Some `LocStateProtPair` may be unreachable through normal means - /// such as `x: Active, y: Active` in the case of mutually foreign pointers. + /// such as `x: Unique, y: Unique` in the case of mutually foreign pointers. struct LocStateProtPair { xy_rel: RelPosXY, x: LocStateProt, @@ -709,7 +709,7 @@ mod spurious_read { let mut err = 0; for pat in Pattern::exhaustive() { let Ok(initial_source) = pat.initial_state() else { - // Failed to retag `x` in the source (e.g. `y` was protected Active) + // Failed to retag `x` in the source (e.g. `y` was protected Unique) continue; }; // `x` must stay protected, but the function protecting `y` might return here diff --git a/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr index 449a29088a01d..dc8b4f6665a03 100644 --- a/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr +++ b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr @@ -16,7 +16,7 @@ LL | | Poll::<()>::Pending LL | | }) LL | | .await | |______________^ -help: the accessed tag later transitioned to Active due to a child write access at offsets [OFFSET] +help: the accessed tag later transitioned to Unique due to a child write access at offsets [OFFSET] --> tests/fail/async-shared-mutable.rs:LL:CC | LL | *x = 1; diff --git a/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr b/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr index 3c8ec7f7d3e4a..6a1f7761a4108 100644 --- a/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr @@ -7,7 +7,7 @@ LL | *y = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: this foreign read access would cause the protected tag (currently Active) to become Disabled + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC @@ -19,7 +19,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | unsafe fn test(mut x: Box, y: *const i32) -> i32 { | ^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC | LL | *x = 5; diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr index 6780e52c3baf3..1547a6ca73a04 100644 --- a/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { *y = 2 }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: this foreign write access would cause the protected tag (currently Active) to become Disabled + = help: this foreign write access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/both_borrows/illegal_write6.rs:LL:CC @@ -19,7 +19,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | fn foo(a: &mut u32, y: *mut u32) -> u32 { | ^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/both_borrows/illegal_write6.rs:LL:CC | LL | *a = 1; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr index 2266a9c39f91d..2d9ce2aa1fb62 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr @@ -7,7 +7,7 @@ LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(a = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign read access would cause the protected tag (currently Active) to become Disabled + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC @@ -19,7 +19,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | y.0 = 0; | ^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC | LL | y.0 = 0; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr index b7f514de0af98..42e391b5daf0e 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr @@ -7,7 +7,7 @@ LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this reborrow (acting as a foreign read access) would cause the protected tag (currently Active) to become Disabled + = help: this reborrow (acting as a foreign read access) would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC @@ -19,7 +19,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | x | ^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC | LL | x diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr index 1995528e9f9df..74706d6b9f6bc 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { ptr.write(S(0)) }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign write access would cause the protected tag (currently Active) to become Disabled + = help: this foreign write access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC @@ -24,7 +24,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | unsafe { ptr.write(S(0)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC | LL | unsafe { ptr.write(S(0)) }; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr index e506a61c6bb3b..c8c0e5c37efed 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { ptr.read() }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign read access would cause the protected tag (currently Active) to become Disabled + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC @@ -24,7 +24,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | x.0 = 0; | ^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC | LL | x.0 = 0; diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr index b1aa2ba28860c..b43e19c3905c0 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { ptr.read() }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign read access would cause the protected tag (currently Active) to become Disabled + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC @@ -24,7 +24,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | unsafe { ptr.read() }; | ^^^^^^^^^^^^^^^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC | LL | unsafe { ptr.read() }; diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr index 0cf449ea3ec2f..deefb24b7850d 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { ptr.write(0) }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign write access would cause the protected tag (currently Active) to become Disabled + = help: this foreign write access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC @@ -24,7 +24,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | unsafe { ptr.write(0) }; | ^^^^^^^^^^^^^^^^^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC | LL | unsafe { ptr.write(0) }; diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr index a006c6feae43c..76ccf39744d9b 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr @@ -7,7 +7,7 @@ LL | unsafe { ptr.write(0) }; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: this foreign write access would cause the protected tag (currently Active) to become Disabled + = help: this foreign write access would cause the protected tag (currently Unique) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC @@ -24,7 +24,7 @@ help: the protected tag was created here, in the initial state Reserved | LL | unsafe { ptr.write(0) }; | ^^^^^^^^^^^^^^^^^^^^^^^ -help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the protected tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC | LL | unsafe { ptr.write(0) }; diff --git a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr index 9e955a6d5b196..aff482abfa0c7 100644 --- a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Reserved | LL | let y = unsafe { &mut *(x as *mut u8) }; | ^^^^^^^^^^^^^^^^^^^^ -help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x1] +help: the accessed tag later transitioned to Unique due to a child write access at offsets [0x0..0x1] --> tests/fail/tree_borrows/alternate-read-write.rs:LL:CC | LL | *y += 1; // Success diff --git a/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr b/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr index 7886029dccfc2..bfd6854514e57 100644 --- a/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Reserved | LL | let z = &mut x as *mut i32; | ^^^^^^ -help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the accessed tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/tree_borrows/fnentry_invalidation.rs:LL:CC | LL | *z = 1; diff --git a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr index 2edbbd80569b2..7a713abcbc455 100644 --- a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Reserved | LL | let mref = &mut root; | ^^^^^^^^^ -help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x1] +help: the accessed tag later transitioned to Unique due to a child write access at offsets [0x0..0x1] --> tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs:LL:CC | LL | *ptr = 0; // Write diff --git a/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr index c00c67173b7d8..9a70d248aa0c3 100644 --- a/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr @@ -18,7 +18,7 @@ help: the conflicting tag was created here, in the initial state Reserved | LL | let xref = unsafe { &mut *xraw }; | ^^^^^^^^^^ -help: the conflicting tag later transitioned to Active due to a child write access at offsets [0x0..0x4] +help: the conflicting tag later transitioned to Unique due to a child write access at offsets [0x0..0x4] --> tests/fail/tree_borrows/pass_invalid_mut.rs:LL:CC | LL | *xref = 18; // activate xref diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs index 024a14600b1ba..3e5d83911ee63 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -60,8 +60,7 @@ fn main() { fn inner(x: &mut u8, b: IdxBarrier) { *x = 42; // activate immediately synchronized!(b, "[lazy] retag y (&mut, protect, IM)"); - // A spurious write should be valid here because `x` is - // `Active` and protected. + // A spurious write should be valid here because `x` is `Unique` and protected. if cfg!(with) { synchronized!(b, "spurious write x (executed)"); *x = 64; diff --git a/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr index ba8ab472872f7..4b6308847bb8f 100644 --- a/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr @@ -18,7 +18,7 @@ help: the conflicting tag was created here, in the initial state Reserved | LL | let ret = unsafe { &mut (*xraw).1 }; | ^^^^^^^^^^^^^^ -help: the conflicting tag later transitioned to Active due to a child write access at offsets [0x4..0x8] +help: the conflicting tag later transitioned to Unique due to a child write access at offsets [0x4..0x8] --> tests/fail/tree_borrows/return_invalid_mut.rs:LL:CC | LL | *ret = *ret; // activate diff --git a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs index 6514334b09df6..94a3bb805442c 100644 --- a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs +++ b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs @@ -5,7 +5,7 @@ // When this method is called, the tree will be a single line and look like this, // with other_ptr being the root at the top -// other_ptr = root : Active +// other_ptr = root : Unique // intermediary : Frozen // an intermediary node // m : Reserved fn write_to_mut(m: &mut u8, other_ptr: *const u8) { diff --git a/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr b/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr deleted file mode 100644 index c7d72f70f40cf..0000000000000 --- a/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden - --> tests/fail/tree_borrows/unique.rs:LL:CC - | -LL | *uniq.as_ptr() = 3; - | ^^^^^^^^^^^^^^^^^^ write access through at ALLOC[0x0] is forbidden - | - = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids this child write access -help: the accessed tag was created here, in the initial state Reserved - --> tests/fail/tree_borrows/unique.rs:LL:CC - | -LL | let refmut = &mut data; - | ^^^^^^^^^ -help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x1] - --> tests/fail/tree_borrows/unique.rs:LL:CC - | -LL | *uniq.as_ptr() = 1; // activation - | ^^^^^^^^^^^^^^^^^^ - = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference -help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] - --> tests/fail/tree_borrows/unique.rs:LL:CC - | -LL | let _definitely_parent = data; // definitely Frozen by now - | ^^^^ - = help: this transition corresponds to a loss of write permissions - = note: BACKTRACE (of the first span): - = note: inside `main` at tests/fail/tree_borrows/unique.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs index adf2f4e845b5b..4a868455c8497 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs @@ -20,7 +20,7 @@ pub fn main() { name!(ptr2); // We perform a write through `x`. - // Because `ptr1` is ReservedIM, a child write will make it transition to Active. + // Because `ptr1` is ReservedIM, a child write will make it transition to Unique. // Because `ptr2` is ReservedIM, a foreign write doesn't have any effect on it. let x = (*ptr1).get(); *x = 1; diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs index 4fbccef2367d5..edd649b91edd3 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs @@ -6,7 +6,7 @@ mod utils; // To check that a reborrow is counted as a Read access, we use a reborrow -// with no additional Read to Freeze an Active pointer. +// with no additional Read to Freeze an Unique pointer. fn main() { unsafe { @@ -15,7 +15,7 @@ fn main() { let alloc_id = alloc_id!(parent); let x = &mut *parent; name!(x); - *x = 0; // x is now Active + *x = 0; // x is now Unique print_state!(alloc_id); let y = &mut *parent; name!(y); diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs index c57cd7fcf0abe..83e1d9c70ed50 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs @@ -68,7 +68,7 @@ unsafe fn cell_unprotected_read() { } // Foreign Write on an interior mutable pointer is a noop. -// Also y must become Active. +// Also y must become Unique. unsafe fn cell_unprotected_write() { print("[interior mut] Foreign Write: Re* -> Re*"); let base = &mut UnsafeCell::new(0u64); @@ -97,7 +97,7 @@ unsafe fn int_protected_read() { } // Foreign Read on a Reserved is a noop. -// Also y must become Active. +// Also y must become Unique. unsafe fn int_unprotected_read() { print("[] Foreign Read: Res -> Res"); let base = &mut 0u8; From bbe05dcbdf4e08ab24e179cbbaaf50c96fa0634e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 09:54:24 +0200 Subject: [PATCH 0731/1900] Tree::new_child: remove SIFA precondition and sync terminology --- .../tree_borrows/diagnostics.rs | 2 +- .../src/borrow_tracker/tree_borrows/mod.rs | 28 ++------ .../src/borrow_tracker/tree_borrows/tree.rs | 68 +++++++------------ .../borrow_tracker/tree_borrows/tree/tests.rs | 6 +- 4 files changed, 34 insertions(+), 70 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a7977cd3366f9..00f921b0f8afb 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -504,7 +504,7 @@ impl DisplayFmt { if let Some(perm) = perm { format!( "{ac}{st}", - ac = if perm.is_accessed() { self.accessed.yes } else { self.accessed.no }, + ac = if perm.accessed() { self.accessed.yes } else { self.accessed.no }, st = perm.permission().short_name(), ) } else { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index bed65440dc9aa..6e5b5c807aa22 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -294,24 +294,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); } - let span = this.machine.current_span(); - - // When adding a new node, the SIFA of its parents needs to be updated, potentially across - // the entire memory range. For the parts that are being accessed below, the access itself - // trivially takes care of that. However, we have to do some more work to also deal with the - // parts that are not being accessed. Specifically what we do is that we call - // `update_last_accessed_after_retag` on the SIFA of the permission set for the part of - // memory outside `perm_map` -- so that part is definitely taken care of. The remaining - // concern is the part of memory that is in the range of `perms_map`, but not accessed - // below. There we have two cases: - // * If the type is `!Freeze`, then the non-accessed part uses `nonfreeze_perm`, so the - // `nonfreeze_perm` initialized parts are also fine. We enforce the `freeze_perm` parts to - // be accessed via the assert below, and thus everything is taken care of. - // * If the type is `Freeze`, then `freeze_perm` is used everywhere (both inside and outside - // the initial range), and we update everything to have the `freeze_perm`'s SIFA, so there - // are no issues. (And this assert below is not actually needed in this case). - assert!(new_perm.freeze_access); - let protected = new_perm.protector.is_some(); let precise_interior_mut = this .machine @@ -337,7 +319,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { LocationState::new_non_accessed(perm, sifa) } }; - let perms_map = if !precise_interior_mut { + let inside_perms = if !precise_interior_mut { // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`. let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); let state = loc_state(ty_is_freeze); @@ -364,8 +346,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let alloc_extra = this.get_alloc_extra(alloc_id)?; let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - for (perm_range, perm) in perms_map.iter_all() { - if perm.is_accessed() { + for (perm_range, perm) in inside_perms.iter_all() { + if perm.accessed() { // Some reborrows incur a read access to the parent. // Adjust range to be relative to allocation start (rather than to `place`). let range_in_alloc = AllocRange { @@ -401,10 +383,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { base_offset, orig_tag, new_tag, - perms_map, + inside_perms, new_perm.outside_perm, protected, - span, + this.machine.current_span(), )?; drop(tree_borrows); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 9e7d272ee4b00..be2a07eee1503 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -11,7 +11,7 @@ //! - idempotency properties asserted in `perms.rs` (for optimizations) use std::ops::Range; -use std::{fmt, mem}; +use std::{cmp, fmt, mem}; use rustc_abi::Size; use rustc_data_structures::fx::FxHashSet; @@ -73,23 +73,10 @@ impl LocationState { /// Check if the location has been accessed, i.e. if it has /// ever been accessed through a child pointer. - pub fn is_accessed(&self) -> bool { + pub fn accessed(&self) -> bool { self.accessed } - /// Check if the state can exist as the initial permission of a pointer. - /// - /// Do not confuse with `is_accessed`, the two are almost orthogonal - /// as apart from `Unique` which is not initial and must be accessed, - /// any other permission can have an arbitrary combination of being - /// initial/accessed. - /// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally - /// passes and can be uncommented, remove this `#[allow(dead_code)]`. - #[cfg_attr(not(test), allow(dead_code))] - pub fn is_initial(&self) -> bool { - self.permission.is_initial() - } - pub fn permission(&self) -> Permission { self.permission } @@ -618,30 +605,26 @@ impl Tree { impl<'tcx> Tree { /// Insert a new tag in the tree. /// - /// `initial_perms` defines the initial permissions for the part of memory - /// that is already considered "initialized" immediately. The ranges in this - /// map are relative to `base_offset`. - /// `default_perm` defines the initial permission for the rest of the allocation. - /// - /// For all non-accessed locations in the RangeMap (those that haven't had an - /// implicit read), their SIFA must be weaker than or as weak as the SIFA of - /// `default_perm`. + /// `inside_perm` defines the initial permissions for a block of memory starting at + /// `base_offset`. These may nor may not be already marked as "accessed". + /// `outside_perm` defines the initial permission for the rest of the allocation. + /// These are definitely not "accessed". pub(super) fn new_child( &mut self, base_offset: Size, parent_tag: BorTag, new_tag: BorTag, - initial_perms: DedupRangeMap, - default_perm: Permission, + inside_perms: DedupRangeMap, + outside_perm: Permission, protected: bool, span: Span, ) -> InterpResult<'tcx> { let idx = self.tag_mapping.insert(new_tag); let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); - assert!(default_perm.is_initial()); + assert!(outside_perm.is_initial()); let default_strongest_idempotent = - default_perm.strongest_idempotent_foreign_access(protected); + outside_perm.strongest_idempotent_foreign_access(protected); // Create the node self.nodes.insert( idx, @@ -649,36 +632,35 @@ impl<'tcx> Tree { tag: new_tag, parent: Some(parent_idx), children: SmallVec::default(), - default_initial_perm: default_perm, + default_initial_perm: outside_perm, default_initial_idempotent_foreign_access: default_strongest_idempotent, - debug_info: NodeDebugInfo::new(new_tag, default_perm, span), + debug_info: NodeDebugInfo::new(new_tag, outside_perm, span), }, ); // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); + // We need to know the biggest SIFA for `update_last_accessed_after_retag` below. + let mut max_sifa = default_strongest_idempotent; for (Range { start, end }, &perm) in - initial_perms.iter(Size::from_bytes(0), initial_perms.size()) + inside_perms.iter(Size::from_bytes(0), inside_perms.size()) { - assert!(perm.is_initial()); + assert!(perm.permission.is_initial()); + max_sifa = cmp::max(max_sifa, perm.idempotent_foreign_access); for (_perms_range, perms) in self .rperms .iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start)) { - assert!( - default_strongest_idempotent - >= perm.permission.strongest_idempotent_foreign_access(protected) - ); perms.insert(idx, perm); } } - // Inserting the new perms might have broken the SIFA invariant (see `foreign_access_skipping.rs`). - // We now weaken the recorded SIFA for our parents, until the invariant is restored. - // We could weaken them all to `LocalAccess`, but it is more efficient to compute the SIFA - // for the new permission statically, and use that. - // See the comment in `tb_reborrow` for why it is correct to use the SIFA of `default_uninit_perm`. - self.update_last_accessed_after_retag(parent_idx, default_strongest_idempotent); + // Inserting the new perms might have broken the SIFA invariant (see + // `foreign_access_skipping.rs`). We now weaken the recorded SIFA for our parents, until the + // invariant is restored. We could weaken them all to `LocalAccess`, but it is more + // efficient to compute the SIFA for the new permission statically, and use that. For this + // we need the *maximum* SIFA (`Write` needs more fixup than `None`). + self.update_last_accessed_after_retag(parent_idx, max_sifa); interp_ok(()) } @@ -755,9 +737,9 @@ impl<'tcx> Tree { == Some(&ProtectorKind::StrongProtector) // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. - && !perm.permission().is_cell() + && !perm.permission.is_cell() // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. - && perm.is_accessed() + && perm.accessed { Err(TransitionError::ProtectedDealloc) } else { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 83232615616e6..189e48eca724a 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -106,7 +106,7 @@ fn tree_compacting_is_sound() { as_foreign_or_child(rel), kind, parent.permission(), - as_lazy_or_accessed(child.is_accessed()), + as_lazy_or_accessed(child.accessed()), child.permission(), as_protected(child_protected), np.permission(), @@ -122,7 +122,7 @@ fn tree_compacting_is_sound() { as_foreign_or_child(rel), kind, parent.permission(), - as_lazy_or_accessed(child.is_accessed()), + as_lazy_or_accessed(child.accessed()), child.permission(), as_protected(child_protected), nc.permission() @@ -375,7 +375,7 @@ mod spurious_read { impl LocStateProt { fn is_initial(&self) -> bool { - self.state.is_initial() + self.state.permission().is_initial() } fn perform_access(&self, kind: AccessKind, rel: AccessRelatedness) -> Result { From ba2537b6ffdbd9979874ca029e5bd86c6b925b37 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 18:09:10 +0800 Subject: [PATCH 0732/1900] add exit code check --- .../rustdoc-merge-no-input-finalize/rmake.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs index 6df9e95829d8d..4ead8c2e56c56 100644 --- a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs +++ b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs @@ -3,20 +3,26 @@ //@ needs-target-std -use run_make_support::rustdoc; +use run_make_support::{path, rustdoc}; fn main() { + let out_dir = path("out"); + let merged_dir = path("merged"); + let parts_out_dir = path("parts"); rustdoc() .input("sierra.rs") + .out_dir(&out_dir) .arg("-Zunstable-options") - .arg("--parts-out-dir=parts") + .arg(format!("--parts-out-dir={}", parts_out_dir.display())) .arg("--merge=none") .run(); - rustdoc() + let output = rustdoc() .arg("-Zunstable-options") - .arg("--include-parts-dir=parts") + .out_dir(&out_dir) + .arg(format!("--include-parts-dir={}", parts_out_dir.display())) .arg("--merge=finalize") - .out_dir("out") .run(); + + output.assert_exit_code(0); } From 06819d95c0697c6a2f8f1498a22260f47c210bd7 Mon Sep 17 00:00:00 2001 From: Reuben Cruise Date: Tue, 2 Sep 2025 16:13:51 +0100 Subject: [PATCH 0733/1900] Extends branch protection tests to include GCS --- tests/assembly-llvm/aarch64-pointer-auth.rs | 6 +++++- tests/codegen-llvm/branch-protection.rs | 6 +++++- .../pointer-auth-link-with-c-lto-clang/rmake.rs | 8 +++++--- tests/run-make/pointer-auth-link-with-c/rmake.rs | 10 ++++++---- .../branch-protection-missing-pac-ret.BADFLAGS.stderr | 2 +- ...branch-protection-missing-pac-ret.BADFLAGSPC.stderr | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/tests/assembly-llvm/aarch64-pointer-auth.rs b/tests/assembly-llvm/aarch64-pointer-auth.rs index 56a26df469f35..e1ca6d775813d 100644 --- a/tests/assembly-llvm/aarch64-pointer-auth.rs +++ b/tests/assembly-llvm/aarch64-pointer-auth.rs @@ -1,10 +1,13 @@ // Test that PAC instructions are emitted when branch-protection is specified. //@ add-core-stubs -//@ revisions: PACRET PAUTHLR_NOP PAUTHLR +//@ revisions: GCS PACRET PAUTHLR_NOP PAUTHLR //@ assembly-output: emit-asm //@ needs-llvm-components: aarch64 //@ compile-flags: --target aarch64-unknown-linux-gnu +//@ [GCS] min-llvm-version: 21 +//@ [GCS] ignore-apple (XCode version needs updating) +//@ [GCS] compile-flags: -Z branch-protection=gcs //@ [PACRET] compile-flags: -Z branch-protection=pac-ret,leaf //@ [PAUTHLR_NOP] compile-flags: -Z branch-protection=pac-ret,pc,leaf //@ [PAUTHLR] compile-flags: -C target-feature=+pauth-lr -Z branch-protection=pac-ret,pc,leaf @@ -17,6 +20,7 @@ extern crate minicore; use minicore::*; +// GCS: .aeabi_attribute 2, 1 // Tag_Feature_GCS // PACRET: hint #25 // PACRET: hint #29 // PAUTHLR_NOP: hint #25 diff --git a/tests/codegen-llvm/branch-protection.rs b/tests/codegen-llvm/branch-protection.rs index d67e494cc0d65..f92259c941cef 100644 --- a/tests/codegen-llvm/branch-protection.rs +++ b/tests/codegen-llvm/branch-protection.rs @@ -1,9 +1,10 @@ // Test that the correct module flags are emitted with different branch protection flags. //@ add-core-stubs -//@ revisions: BTI PACRET LEAF BKEY PAUTHLR PAUTHLR_BKEY PAUTHLR_LEAF PAUTHLR_BTI NONE +//@ revisions: BTI GCS PACRET LEAF BKEY PAUTHLR PAUTHLR_BKEY PAUTHLR_LEAF PAUTHLR_BTI NONE //@ needs-llvm-components: aarch64 //@ [BTI] compile-flags: -Z branch-protection=bti +//@ [GCS] compile-flags: -Z branch-protection=gcs //@ [PACRET] compile-flags: -Z branch-protection=pac-ret //@ [LEAF] compile-flags: -Z branch-protection=pac-ret,leaf //@ [BKEY] compile-flags: -Z branch-protection=pac-ret,b-key @@ -32,6 +33,9 @@ pub fn test() {} // BTI: !"sign-return-address-all", i32 0 // BTI: !"sign-return-address-with-bkey", i32 0 +// GCS: attributes [[ATTR]] = {{.*}} "guarded-control-stack" +// GCS: !"guarded-control-stack", i32 1 + // PACRET: attributes [[ATTR]] = {{.*}} "sign-return-address"="non-leaf" // PACRET-SAME: "sign-return-address-key"="a_key" // PACRET: !"branch-target-enforcement", i32 0 diff --git a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs index 0a2186b095389..2ac5fdee063c2 100644 --- a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs @@ -1,12 +1,14 @@ // `-Z branch protection` is an unstable compiler feature which adds pointer-authentication // code (PAC), a useful hashing measure for verifying that pointers have not been modified. // This test checks that compilation and execution is successful when this feature is activated, -// with some of its possible extra arguments (bti, pac-ret, leaf) when doing LTO. +// with some of its possible extra arguments (bti, gcs, pac-ret, leaf) when doing LTO. // See https://github.com/rust-lang/rust/pull/88354 //@ needs-force-clang-based-tests //@ only-aarch64 // Reason: branch protection is not supported on other architectures +//@ ignore-apple +// Reason: XCode needs updating to support gcs //@ ignore-cross-compile // Reason: the compiled binary is executed @@ -19,7 +21,7 @@ fn main() { clang() .arg("-v") .lto("thin") - .arg("-mbranch-protection=bti+pac-ret+b-key+leaf") + .arg("-mbranch-protection=bti+gcs+pac-ret+b-key+leaf") .arg("-c") .out_exe("test.o") .input("test.c") @@ -30,7 +32,7 @@ fn main() { .opt_level("2") .linker(&env_var("CLANG")) .link_arg("-fuse-ld=lld") - .arg("-Zbranch-protection=bti,pac-ret,leaf") + .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") .input("test.rs") .output("test.bin") .run(); diff --git a/tests/run-make/pointer-auth-link-with-c/rmake.rs b/tests/run-make/pointer-auth-link-with-c/rmake.rs index a4d7454e5755a..1ddcb79d64ff4 100644 --- a/tests/run-make/pointer-auth-link-with-c/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c/rmake.rs @@ -1,11 +1,13 @@ // `-Z branch protection` is an unstable compiler feature which adds pointer-authentication // code (PAC), a useful hashing measure for verifying that pointers have not been modified. // This test checks that compilation and execution is successful when this feature is activated, -// with some of its possible extra arguments (bti, pac-ret, pc, leaf, b-key). +// with some of its possible extra arguments (bti, gcs, pac-ret, pc, leaf, b-key). // See https://github.com/rust-lang/rust/pull/88354 //@ only-aarch64 // Reason: branch protection is not supported on other architectures +//@ ignore-apple +// Reason: XCode needs updating to support gcs //@ ignore-cross-compile // Reason: the compiled binary is executed @@ -13,17 +15,17 @@ use run_make_support::{build_native_static_lib, cc, is_windows_msvc, llvm_ar, ru fn main() { build_native_static_lib("test"); - rustc().arg("-Zbranch-protection=bti,pac-ret,leaf").input("test.rs").run(); + rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); run("test"); cc().arg("-v") .arg("-c") .out_exe("test") .input("test.c") - .arg("-mbranch-protection=bti+pac-ret+leaf") + .arg("-mbranch-protection=bti+gcs+pac-ret+leaf") .run(); let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); - rustc().arg("-Zbranch-protection=bti,pac-ret,leaf").input("test.rs").run(); + rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); run("test"); // FIXME: +pc was only recently added to LLVM diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr index dae08119dbc68..277111a41f29c 100644 --- a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr +++ b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr @@ -1,2 +1,2 @@ -error: incorrect value `leaf` for unstable option `branch-protection` - a `,` separated combination of `bti`, `pac-ret`, followed by a combination of `pc`, `b-key`, or `leaf` was expected +error: incorrect value `leaf` for unstable option `branch-protection` - a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set) was expected diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr index 13f79e94674b2..e1ade01d2fe76 100644 --- a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr +++ b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr @@ -1,2 +1,2 @@ -error: incorrect value `pc` for unstable option `branch-protection` - a `,` separated combination of `bti`, `pac-ret`, followed by a combination of `pc`, `b-key`, or `leaf` was expected +error: incorrect value `pc` for unstable option `branch-protection` - a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set) was expected From f51fb9178e8d3648a908136d132bbfd1811481fd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 21 Sep 2025 23:39:59 +0200 Subject: [PATCH 0734/1900] kcfi: only reify trait methods when dyn-compatible --- compiler/rustc_middle/src/ty/instance.rs | 4 +- .../sanitizer/kcfi/fn-ptr-reify-shim.rs | 74 +++++++++++++++++++ .../sanitizer/kcfi/naked-function.rs | 7 +- tests/ui/sanitizer/kcfi-c-variadic.rs | 20 +++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs create mode 100644 tests/ui/sanitizer/kcfi-c-variadic.rs diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 34ead91b4f6de..c27d47fcc0d8d 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -618,11 +618,11 @@ impl<'tcx> Instance<'tcx> { // be directly reified because it's closure-like. The reify can handle the // unresolved instance. resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } - // Reify `Trait::method` implementations - // FIXME(maurer) only reify it if it is a vtable-safe function + // Reify `Trait::method` implementations if the trait is dyn-compatible. } else if let Some(assoc) = tcx.opt_associated_item(def_id) && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = assoc.container + && tcx.is_dyn_compatible(assoc.container_id(tcx)) { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. diff --git a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs new file mode 100644 index 0000000000000..604b4c8c2f8a5 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs @@ -0,0 +1,74 @@ +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +// A `ReifyShim` should only be created when the trait is dyn-compatible. + +extern crate minicore; +use minicore::*; + +trait DynCompatible { + fn dyn_name(&self) -> &'static str; + + fn dyn_name_default(&self) -> &'static str { + let _ = self; + "dyn_default" + } +} + +// Not dyn-compatible because the `Self: Sized` bound is missing. +trait NotDynCompatible { + fn not_dyn_name() -> &'static str; + + fn not_dyn_name_default() -> &'static str { + "not_dyn_default" + } +} + +struct S; + +impl DynCompatible for S { + fn dyn_name(&self) -> &'static str { + "dyn_compatible" + } +} + +impl NotDynCompatible for S { + fn not_dyn_name() -> &'static str { + "not_dyn_compatible" + } +} + +#[no_mangle] +pub fn main() { + let s = S; + + // `DynCompatible` is indeed dyn-compatible. + let _: &dyn DynCompatible = &s; + + // CHECK: call ::dyn_name{{.*}}reify.shim.fnptr + let dyn_name = S::dyn_name as fn(&S) -> &str; + let _unused = dyn_name(&s); + + // CHECK: call fn_ptr_reify_shim::DynCompatible::dyn_name_default{{.*}}reify.shim.fnptr + let dyn_name_default = S::dyn_name_default as fn(&S) -> &str; + let _unused = dyn_name_default(&s); + + // Check using $ (end-of-line) that these calls do not contain `reify.shim.fnptr`. + + // CHECK: call ::not_dyn_name{{$}} + let not_dyn_name = S::not_dyn_name as fn() -> &'static str; + let _unused = not_dyn_name(); + + // CHECK: call fn_ptr_reify_shim::NotDynCompatible::not_dyn_name_default{{$}} + let not_dyn_name_default = S::not_dyn_name_default as fn() -> &'static str; + let _unused = not_dyn_name_default(); +} diff --git a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs index 2c8cdc919b85d..31f59ee01decb 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs @@ -15,8 +15,9 @@ use minicore::*; struct Thing; trait MyTrait { + // NOTE: this test assumes that this trait is dyn-compatible. #[unsafe(naked)] - extern "C" fn my_naked_function() { + extern "C" fn my_naked_function(&self) { // the real function is defined // CHECK: .globl // CHECK-SAME: my_naked_function @@ -34,13 +35,13 @@ impl MyTrait for Thing {} #[unsafe(no_mangle)] pub fn main() { // Trick the compiler into generating an indirect call. - const F: extern "C" fn() = Thing::my_naked_function; + const F: extern "C" fn(&Thing) = Thing::my_naked_function; // main calls the shim function // CHECK: call void // CHECK-SAME: my_naked_function // CHECK-SAME: reify.shim.fnptr - (F)(); + (F)(&Thing); } // CHECK: declare !kcfi_type diff --git a/tests/ui/sanitizer/kcfi-c-variadic.rs b/tests/ui/sanitizer/kcfi-c-variadic.rs new file mode 100644 index 0000000000000..45d00a4524ebf --- /dev/null +++ b/tests/ui/sanitizer/kcfi-c-variadic.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-kcfi +//@ no-prefer-dynamic +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer +//@ ignore-backends: gcc +//@ run-pass + +#![feature(c_variadic)] + +trait Trait { + unsafe extern "C" fn foo(x: i32, y: i32, mut ap: ...) -> i32 { + x + y + ap.arg::() + ap.arg::() + } +} + +impl Trait for i32 {} + +fn main() { + let f = i32::foo as unsafe extern "C" fn(i32, i32, ...) -> i32; + assert_eq!(unsafe { f(1, 2, 3, 4) }, 1 + 2 + 3 + 4); +} From 870a98c7b3bec5e0ec3432a4900d2bb6f0e1cc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 22 Sep 2025 14:14:48 +0200 Subject: [PATCH 0735/1900] Fix modification check of `rustdoc-json-types` --- src/tools/tidy/src/rustdoc_json.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index 7a53c08737f6c..ade774616c71b 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -17,11 +17,13 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, diag_ctx: DiagCtx) { }; // First we check that `src/rustdoc-json-types` was modified. - if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) { + if !crate::files_modified(ci_info, |p| p.starts_with(RUSTDOC_JSON_TYPES)) { // `rustdoc-json-types` was not modified so nothing more to check here. - check.verbose_msg("`rustdoc-json-types` was not modified."); return; } + + check.message("`rustdoc-json-types` modified, checking format version"); + // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated. match crate::git_diff(base_commit, src_path.join("rustdoc-json-types")) { Some(output) => { From 6adbb3a18972c1e4c24ac8a2766a0a5fc699f900 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:06:07 +0530 Subject: [PATCH 0736/1900] remove explicit target assignment in config during rustc initialization --- src/bootstrap/src/core/builder/tests.rs | 19 +++++++------------ src/bootstrap/src/core/config/config.rs | 4 +--- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 0bb22eccd1936..f838149977d01 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -22,13 +22,7 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { } fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config { - TestCtx::new() - .config(cmd[0]) - .args(&cmd[1..]) - .hosts(host) - .targets(target) - .args(&["--build", TEST_TRIPLE_1]) - .create_config() + TestCtx::new().config(cmd[0]).args(&cmd[1..]).hosts(host).targets(target).create_config() } fn first(v: Vec<(A, B)>) -> Vec { @@ -236,6 +230,7 @@ mod dist { use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build}; use crate::Flags; + use crate::core::builder::tests::host_target; use crate::core::builder::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -244,11 +239,11 @@ mod dist { #[test] fn llvm_out_behaviour() { - let mut config = configure(&[TEST_TRIPLE_1], &[TEST_TRIPLE_2]); + let mut config = configure(&[], &[TEST_TRIPLE_2]); config.llvm_from_ci = true; let build = Build::new(config.clone()); - let target = TargetSelection::from_user(TEST_TRIPLE_1); + let target = TargetSelection::from_user(&host_target()); assert!(build.llvm_out(target).ends_with("ci-llvm")); let target = TargetSelection::from_user(TEST_TRIPLE_2); assert!(build.llvm_out(target).ends_with("llvm")); @@ -313,14 +308,14 @@ mod sysroot_target_dirs { /// cg_gcc tests instead. #[test] fn test_test_compiler() { - let config = configure_with_args(&["test", "compiler"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let config = configure_with_args(&["test", "compiler"], &[], &[TEST_TRIPLE_1]); let cache = run_build(&config.paths.clone(), config); let compiler = cache.contains::(); let cranelift = cache.contains::(); let gcc = cache.contains::(); - assert_eq!((compiler, cranelift, gcc), (true, false, false)); + assert_eq!((compiler, cranelift, gcc), (false, false, false)); } #[test] @@ -346,7 +341,7 @@ fn test_test_coverage() { // Print each test case so that if one fails, the most recently printed // case is the one that failed. println!("Testing case: {cmd:?}"); - let config = configure_with_args(cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let config = configure_with_args(cmd, &[], &[TEST_TRIPLE_1]); let mut cache = run_build(&config.paths.clone(), config); let modes = diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1f3b90d324fb1..957de5367ef6a 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -688,9 +688,7 @@ impl Config { let initial_rustc = build_rustc.unwrap_or_else(|| { download_beta_toolchain(&dwn_ctx, &out); - let target = if cfg!(test) { get_host_target() } else { host_target }; - - out.join(target).join("stage0").join("bin").join(exe("rustc", host_target)) + out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( From 42ebba214b3c570761dc99f5fc1517ad092778ac Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 20:36:18 +0800 Subject: [PATCH 0737/1900] address review comments --- tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs index 4ead8c2e56c56..0b1e1948d5fcc 100644 --- a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs +++ b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs @@ -16,6 +16,7 @@ fn main() { .arg(format!("--parts-out-dir={}", parts_out_dir.display())) .arg("--merge=none") .run(); + assert!(parts_out_dir.join("crate-info").exists()); let output = rustdoc() .arg("-Zunstable-options") @@ -23,6 +24,5 @@ fn main() { .arg(format!("--include-parts-dir={}", parts_out_dir.display())) .arg("--merge=finalize") .run(); - - output.assert_exit_code(0); + output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); } From a25896bc2785b234c8b093a25b7f7df34798aea8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 14:34:34 +0200 Subject: [PATCH 0738/1900] share check_all_outcomes impl, and increase max iteration counts --- .../miri/tests/pass/0weak_memory/weak.rs | 33 ++--------------- src/tools/miri/tests/pass/float_nan.rs | 37 +++---------------- src/tools/miri/tests/utils/mod.rs | 35 ++++++++++++++++++ 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index c752fc114ba57..611733d0dac52 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -13,6 +13,10 @@ use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicUsize, fence}; use std::thread::spawn; +#[path = "../../utils/mod.rs"] +mod utils; +use utils::check_all_outcomes; + #[allow(dead_code)] #[derive(Copy, Clone)] struct EvilSend(pub T); @@ -33,35 +37,6 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize { val } -/// Check that the function produces the intended set of outcomes. -#[track_caller] -fn check_all_outcomes( - expected: impl IntoIterator, - generate: impl Fn() -> T, -) { - use std::collections::HashSet; - - let expected: HashSet = HashSet::from_iter(expected); - let mut seen = HashSet::new(); - // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 16; - for i in 0..tries { - let val = generate(); - assert!(expected.contains(&val), "got an unexpected value: {val:?}"); - seen.insert(val); - if i > tries / 2 && expected.len() == seen.len() { - // We saw everything and we did quite a few tries, let's avoid wasting time. - return; - } - } - // Let's see if we saw them all. - for val in expected { - if !seen.contains(&val) { - panic!("did not get value that should be possible: {val:?}"); - } - } -} - fn relaxed() { check_all_outcomes([0, 1, 2], || { let x = static_atomic(0); diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index 902816307403a..c07ffdf9740c4 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -5,6 +5,10 @@ use std::fmt; use std::hint::black_box; +#[path = "../utils/mod.rs"] +mod utils; +use utils::check_all_outcomes; + fn ldexp(a: f64, b: i32) -> f64 { extern "C" { fn ldexp(x: f64, n: i32) -> f64; @@ -26,35 +30,6 @@ enum NaNKind { } use NaNKind::*; -/// Check that the function produces the intended set of outcomes. -#[track_caller] -fn check_all_outcomes( - expected: impl IntoIterator, - generate: impl Fn() -> T, -) { - use std::collections::HashSet; - - let expected: HashSet = HashSet::from_iter(expected); - let mut seen = HashSet::new(); - // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 12; - for i in 0..tries { - let val = generate(); - assert!(expected.contains(&val), "got an unexpected value: {val}"); - seen.insert(val); - if i > tries / 2 && expected.len() == seen.len() { - // We saw everything and we did quite a few tries, let's avoid wasting time. - return; - } - } - // Let's see if we saw them all. - for val in expected { - if !seen.contains(&val) { - panic!("did not get value that should be possible: {val}"); - } - } -} - // -- f32 support #[repr(C)] #[derive(Copy, Clone, Eq, PartialEq, Hash)] @@ -81,7 +56,7 @@ const F32_EXP: u32 = 8; // 8 bits of exponent const F32_MANTISSA: u32 = F32_SIGN_BIT - F32_EXP; const F32_NAN_PAYLOAD: u32 = F32_MANTISSA - 1; -impl fmt::Display for F32 { +impl fmt::Debug for F32 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Alaways show raw bits. write!(f, "0x{:08x} ", self.0)?; @@ -154,7 +129,7 @@ const F64_EXP: u32 = 11; // 11 bits of exponent const F64_MANTISSA: u32 = F64_SIGN_BIT - F64_EXP; const F64_NAN_PAYLOAD: u32 = F64_MANTISSA - 1; -impl fmt::Display for F64 { +impl fmt::Debug for F64 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Alaways show raw bits. write!(f, "0x{:08x} ", self.0)?; diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index 138ada4e20d7a..459fea404ea3c 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -16,3 +16,38 @@ pub fn run_provenance_gc() { // SAFETY: No preconditions. The GC is fine to run at any time. unsafe { miri_run_provenance_gc() } } + +/// Check that the function produces the intended set of outcomes. +#[track_caller] +pub fn check_all_outcomes( + expected: impl IntoIterator, + generate: impl Fn() -> T, +) { + use std::collections::HashSet; + + let expected: HashSet = HashSet::from_iter(expected); + let mut seen = HashSet::new(); + // Let's give it N times as many tries as we are expecting values. + let min_tries = std::cmp::max(20, expected.len() * 4); + let max_tries = expected.len() * 50; + for i in 0..max_tries { + let val = generate(); + assert!(expected.contains(&val), "got an unexpected value: {val:?}"); + seen.insert(val); + if i >= min_tries && expected.len() == seen.len() { + // We saw everything and we did enough tries, let's avoid wasting time. + return; + } + } + // Let's see if we saw them all. + if expected.len() == seen.len() { + return; + } + // Find the missing one. + for val in expected { + if !seen.contains(&val) { + panic!("did not get value that should be possible: {val:?}"); + } + } + unreachable!() +} From 37de09fed92a2814d93faff6e789c34d2a18718e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 14:49:13 +0200 Subject: [PATCH 0739/1900] share the check_nondet helper as well --- src/tools/miri/tests/pass/float.rs | 194 ++++++++---------- .../intrinsics/fmuladd_nondeterministic.rs | 103 ++++------ src/tools/miri/tests/utils/mod.rs | 15 ++ 3 files changed, 138 insertions(+), 174 deletions(-) diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 3ce5ea8356bd5..8570addeedd20 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -8,12 +8,16 @@ #![allow(internal_features)] #![allow(unnecessary_transmutes)] +#[path = "../utils/mod.rs"] +mod utils; use std::any::type_name; use std::cmp::min; use std::fmt::{Debug, Display, LowerHex}; use std::hint::black_box; use std::{f32, f64}; +use utils::check_nondet; + /// Compare the two floats, allowing for $ulp many ULPs of error. /// /// ULP means "Units in the Last Place" or "Units of Least Precision". @@ -1429,29 +1433,14 @@ fn test_fmuladd() { /// `min` and `max` on equal arguments are non-deterministic. fn test_min_max_nondet() { - /// Ensure that if we call the closure often enough, we see both `true` and `false.` - #[track_caller] - fn ensure_both(f: impl Fn() -> bool) { - let rounds = 32; - let first = f(); - for _ in 1..rounds { - if f() != first { - // We saw two different values! - return; - } - } - // We saw the same thing N times. - panic!("expected non-determinism, got {rounds} times the same result: {first:?}"); - } - - ensure_both(|| f16::min(0.0, -0.0).is_sign_positive()); - ensure_both(|| f16::max(0.0, -0.0).is_sign_positive()); - ensure_both(|| f32::min(0.0, -0.0).is_sign_positive()); - ensure_both(|| f32::max(0.0, -0.0).is_sign_positive()); - ensure_both(|| f64::min(0.0, -0.0).is_sign_positive()); - ensure_both(|| f64::max(0.0, -0.0).is_sign_positive()); - ensure_both(|| f128::min(0.0, -0.0).is_sign_positive()); - ensure_both(|| f128::max(0.0, -0.0).is_sign_positive()); + check_nondet(|| f16::min(0.0, -0.0).is_sign_positive()); + check_nondet(|| f16::max(0.0, -0.0).is_sign_positive()); + check_nondet(|| f32::min(0.0, -0.0).is_sign_positive()); + check_nondet(|| f32::max(0.0, -0.0).is_sign_positive()); + check_nondet(|| f64::min(0.0, -0.0).is_sign_positive()); + check_nondet(|| f64::max(0.0, -0.0).is_sign_positive()); + check_nondet(|| f128::min(0.0, -0.0).is_sign_positive()); + check_nondet(|| f128::max(0.0, -0.0).is_sign_positive()); } fn test_non_determinism() { @@ -1461,35 +1450,20 @@ fn test_non_determinism() { }; use std::{f32, f64}; - /// Ensure that the operation is non-deterministic - #[track_caller] - fn ensure_nondet(f: impl Fn() -> T) { - let rounds = 16; - let first = f(); - for _ in 1..rounds { - if f() != first { - // We saw two different values! - return; - } - } - // We saw the same thing N times. - panic!("expected non-determinism, got {rounds} times the same result: {first:?}"); - } - macro_rules! test_operations_f { ($a:expr, $b:expr) => { - ensure_nondet(|| fadd_algebraic($a, $b)); - ensure_nondet(|| fsub_algebraic($a, $b)); - ensure_nondet(|| fmul_algebraic($a, $b)); - ensure_nondet(|| fdiv_algebraic($a, $b)); - ensure_nondet(|| frem_algebraic($a, $b)); + check_nondet(|| fadd_algebraic($a, $b)); + check_nondet(|| fsub_algebraic($a, $b)); + check_nondet(|| fmul_algebraic($a, $b)); + check_nondet(|| fdiv_algebraic($a, $b)); + check_nondet(|| frem_algebraic($a, $b)); unsafe { - ensure_nondet(|| fadd_fast($a, $b)); - ensure_nondet(|| fsub_fast($a, $b)); - ensure_nondet(|| fmul_fast($a, $b)); - ensure_nondet(|| fdiv_fast($a, $b)); - ensure_nondet(|| frem_fast($a, $b)); + check_nondet(|| fadd_fast($a, $b)); + check_nondet(|| fsub_fast($a, $b)); + check_nondet(|| fmul_fast($a, $b)); + check_nondet(|| fdiv_fast($a, $b)); + check_nondet(|| frem_fast($a, $b)); } }; } @@ -1499,70 +1473,70 @@ fn test_non_determinism() { } pub fn test_operations_f32(a: f32, b: f32) { test_operations_f!(a, b); - ensure_nondet(|| a.powf(b)); - ensure_nondet(|| a.powi(2)); - ensure_nondet(|| a.log(b)); - ensure_nondet(|| a.exp()); - ensure_nondet(|| 10f32.exp2()); - ensure_nondet(|| f32::consts::E.ln()); - ensure_nondet(|| 10f32.log10()); - ensure_nondet(|| 8f32.log2()); - ensure_nondet(|| 1f32.ln_1p()); - ensure_nondet(|| 27.0f32.cbrt()); - ensure_nondet(|| 3.0f32.hypot(4.0f32)); - ensure_nondet(|| 1f32.sin()); - ensure_nondet(|| 1f32.cos()); + check_nondet(|| a.powf(b)); + check_nondet(|| a.powi(2)); + check_nondet(|| a.log(b)); + check_nondet(|| a.exp()); + check_nondet(|| 10f32.exp2()); + check_nondet(|| f32::consts::E.ln()); + check_nondet(|| 10f32.log10()); + check_nondet(|| 8f32.log2()); + check_nondet(|| 1f32.ln_1p()); + check_nondet(|| 27.0f32.cbrt()); + check_nondet(|| 3.0f32.hypot(4.0f32)); + check_nondet(|| 1f32.sin()); + check_nondet(|| 1f32.cos()); // On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version, // which means the little rounding errors Miri introduces are discarded by the cast down to // `f32`. Just skip the test for them. if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) { - ensure_nondet(|| 1.0f32.tan()); - ensure_nondet(|| 1.0f32.asin()); - ensure_nondet(|| 5.0f32.acos()); - ensure_nondet(|| 1.0f32.atan()); - ensure_nondet(|| 1.0f32.atan2(2.0f32)); - ensure_nondet(|| 1.0f32.sinh()); - ensure_nondet(|| 1.0f32.cosh()); - ensure_nondet(|| 1.0f32.tanh()); + check_nondet(|| 1.0f32.tan()); + check_nondet(|| 1.0f32.asin()); + check_nondet(|| 5.0f32.acos()); + check_nondet(|| 1.0f32.atan()); + check_nondet(|| 1.0f32.atan2(2.0f32)); + check_nondet(|| 1.0f32.sinh()); + check_nondet(|| 1.0f32.cosh()); + check_nondet(|| 1.0f32.tanh()); } - ensure_nondet(|| 1.0f32.asinh()); - ensure_nondet(|| 2.0f32.acosh()); - ensure_nondet(|| 0.5f32.atanh()); - ensure_nondet(|| 5.0f32.gamma()); - ensure_nondet(|| 5.0f32.ln_gamma()); - ensure_nondet(|| 5.0f32.erf()); - ensure_nondet(|| 5.0f32.erfc()); + check_nondet(|| 1.0f32.asinh()); + check_nondet(|| 2.0f32.acosh()); + check_nondet(|| 0.5f32.atanh()); + check_nondet(|| 5.0f32.gamma()); + check_nondet(|| 5.0f32.ln_gamma()); + check_nondet(|| 5.0f32.erf()); + check_nondet(|| 5.0f32.erfc()); } pub fn test_operations_f64(a: f64, b: f64) { test_operations_f!(a, b); - ensure_nondet(|| a.powf(b)); - ensure_nondet(|| a.powi(2)); - ensure_nondet(|| a.log(b)); - ensure_nondet(|| a.exp()); - ensure_nondet(|| 50f64.exp2()); - ensure_nondet(|| 3f64.ln()); - ensure_nondet(|| f64::consts::E.log10()); - ensure_nondet(|| f64::consts::E.log2()); - ensure_nondet(|| 1f64.ln_1p()); - ensure_nondet(|| 27.0f64.cbrt()); - ensure_nondet(|| 3.0f64.hypot(4.0f64)); - ensure_nondet(|| 1f64.sin()); - ensure_nondet(|| 1f64.cos()); - ensure_nondet(|| 1.0f64.tan()); - ensure_nondet(|| 1.0f64.asin()); - ensure_nondet(|| 5.0f64.acos()); - ensure_nondet(|| 1.0f64.atan()); - ensure_nondet(|| 1.0f64.atan2(2.0f64)); - ensure_nondet(|| 1.0f64.sinh()); - ensure_nondet(|| 1.0f64.cosh()); - ensure_nondet(|| 1.0f64.tanh()); - ensure_nondet(|| 1.0f64.asinh()); - ensure_nondet(|| 3.0f64.acosh()); - ensure_nondet(|| 0.5f64.atanh()); - ensure_nondet(|| 5.0f64.gamma()); - ensure_nondet(|| 5.0f64.ln_gamma()); - ensure_nondet(|| 5.0f64.erf()); - ensure_nondet(|| 5.0f64.erfc()); + check_nondet(|| a.powf(b)); + check_nondet(|| a.powi(2)); + check_nondet(|| a.log(b)); + check_nondet(|| a.exp()); + check_nondet(|| 50f64.exp2()); + check_nondet(|| 3f64.ln()); + check_nondet(|| f64::consts::E.log10()); + check_nondet(|| f64::consts::E.log2()); + check_nondet(|| 1f64.ln_1p()); + check_nondet(|| 27.0f64.cbrt()); + check_nondet(|| 3.0f64.hypot(4.0f64)); + check_nondet(|| 1f64.sin()); + check_nondet(|| 1f64.cos()); + check_nondet(|| 1.0f64.tan()); + check_nondet(|| 1.0f64.asin()); + check_nondet(|| 5.0f64.acos()); + check_nondet(|| 1.0f64.atan()); + check_nondet(|| 1.0f64.atan2(2.0f64)); + check_nondet(|| 1.0f64.sinh()); + check_nondet(|| 1.0f64.cosh()); + check_nondet(|| 1.0f64.tanh()); + check_nondet(|| 1.0f64.asinh()); + check_nondet(|| 3.0f64.acosh()); + check_nondet(|| 0.5f64.atanh()); + check_nondet(|| 5.0f64.gamma()); + check_nondet(|| 5.0f64.ln_gamma()); + check_nondet(|| 5.0f64.erf()); + check_nondet(|| 5.0f64.erfc()); } pub fn test_operations_f128(a: f128, b: f128) { test_operations_f!(a, b); @@ -1574,15 +1548,15 @@ fn test_non_determinism() { test_operations_f128(25., 18.); // SNaN^0 = (1 | NaN) - ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); - ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); + check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); + check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); // 1^SNaN = (1 | NaN) - ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); - ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); + check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); + check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); // same as powf (keep it consistent): // x^SNaN = (1 | NaN) - ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); - ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); + check_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); + check_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); } diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs index b688405c4b184..401b2911f809a 100644 --- a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs +++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs @@ -3,73 +3,48 @@ use std::intrinsics::simd::simd_relaxed_fma; use std::intrinsics::{fmuladdf32, fmuladdf64}; use std::simd::prelude::*; -fn ensure_both_happen(f: impl Fn() -> bool) -> bool { - let mut saw_true = false; - let mut saw_false = false; - for _ in 0..50 { - let b = f(); - if b { - saw_true = true; - } else { - saw_false = true; - } - if saw_true && saw_false { - return true; - } - } - false -} +#[path = "../../utils/mod.rs"] +mod utils; +use utils::check_nondet; fn main() { - assert!( - ensure_both_happen(|| { - let a = std::hint::black_box(0.1_f64); - let b = std::hint::black_box(0.2); - let c = std::hint::black_box(-a * b); - // It is unspecified whether the following operation is fused or not. The - // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused. - let x = unsafe { fmuladdf64(a, b, c) }; - x == 0.0 - }), - "`fmuladdf64` failed to be evaluated as both fused and unfused" - ); + check_nondet(|| { + let a = std::hint::black_box(0.1_f64); + let b = std::hint::black_box(0.2); + let c = std::hint::black_box(-a * b); + // It is unspecified whether the following operation is fused or not. The + // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused. + let x = unsafe { fmuladdf64(a, b, c) }; + x == 0.0 + }); - assert!( - ensure_both_happen(|| { - let a = std::hint::black_box(0.1_f32); - let b = std::hint::black_box(0.2); - let c = std::hint::black_box(-a * b); - // It is unspecified whether the following operation is fused or not. The - // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused. - let x = unsafe { fmuladdf32(a, b, c) }; - x == 0.0 - }), - "`fmuladdf32` failed to be evaluated as both fused and unfused" - ); + check_nondet(|| { + let a = std::hint::black_box(0.1_f32); + let b = std::hint::black_box(0.2); + let c = std::hint::black_box(-a * b); + // It is unspecified whether the following operation is fused or not. The + // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused. + let x = unsafe { fmuladdf32(a, b, c) }; + x == 0.0 + }); - assert!( - ensure_both_happen(|| { - let a = f32x4::splat(std::hint::black_box(0.1)); - let b = f32x4::splat(std::hint::black_box(0.2)); - let c = std::hint::black_box(-a * b); - let x = unsafe { simd_relaxed_fma(a, b, c) }; - // Whether we fuse or not is a per-element decision, so sometimes these should be - // the same and sometimes not. - x[0] == x[1] - }), - "`simd_relaxed_fma` failed to be evaluated as both fused and unfused" - ); + check_nondet(|| { + let a = f32x4::splat(std::hint::black_box(0.1)); + let b = f32x4::splat(std::hint::black_box(0.2)); + let c = std::hint::black_box(-a * b); + let x = unsafe { simd_relaxed_fma(a, b, c) }; + // Whether we fuse or not is a per-element decision, so sometimes these should be + // the same and sometimes not. + x[0] == x[1] + }); - assert!( - ensure_both_happen(|| { - let a = f64x4::splat(std::hint::black_box(0.1)); - let b = f64x4::splat(std::hint::black_box(0.2)); - let c = std::hint::black_box(-a * b); - let x = unsafe { simd_relaxed_fma(a, b, c) }; - // Whether we fuse or not is a per-element decision, so sometimes these should be - // the same and sometimes not. - x[0] == x[1] - }), - "`simd_relaxed_fma` failed to be evaluated as both fused and unfused" - ); + check_nondet(|| { + let a = f64x4::splat(std::hint::black_box(0.1)); + let b = f64x4::splat(std::hint::black_box(0.2)); + let c = std::hint::black_box(-a * b); + let x = unsafe { simd_relaxed_fma(a, b, c) }; + // Whether we fuse or not is a per-element decision, so sometimes these should be + // the same and sometimes not. + x[0] == x[1] + }); } diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index 459fea404ea3c..37f9996216343 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -51,3 +51,18 @@ pub fn check_all_outcomes( } unreachable!() } + +/// Check that the operation is non-deterministic +#[track_caller] +pub fn check_nondet(f: impl Fn() -> T) { + let rounds = 50; + let first = f(); + for _ in 1..rounds { + if f() != first { + // We saw two different values! + return; + } + } + // We saw the same thing N times. + panic!("expected non-determinism, got {rounds} times the same result: {first:?}"); +} From 823337a4ade5e559d485904b685e8f2cef4a3a2c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 22 Sep 2025 22:15:25 +0900 Subject: [PATCH 0740/1900] Remove unused #![feature(get_mut_unchecked)] in Rc and Arc examples --- library/alloc/src/rc.rs | 9 --------- library/alloc/src/sync.rs | 9 --------- 2 files changed, 18 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index fcb466778a3fb..aed3357afbf4b 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -480,8 +480,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::rc::Rc; /// /// let mut five = Rc::::new_uninit(); @@ -572,7 +570,6 @@ impl Rc { /// /// ``` /// #![feature(allocator_api)] - /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; /// @@ -1014,8 +1011,6 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::rc::Rc; /// /// let mut values = Rc::<[u32]>::new_uninit_slice(3); @@ -1181,8 +1176,6 @@ impl Rc, A> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::rc::Rc; /// /// let mut five = Rc::::new_uninit(); @@ -1218,8 +1211,6 @@ impl Rc<[mem::MaybeUninit], A> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::rc::Rc; /// /// let mut values = Rc::<[u32]>::new_uninit_slice(3); diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 32396cccb8fca..a466b74944c5d 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -480,8 +480,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::sync::Arc; /// /// let mut five = Arc::::new_uninit(); @@ -586,7 +584,6 @@ impl Arc { /// /// ``` /// #![feature(allocator_api)] - /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; /// @@ -1156,8 +1153,6 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::sync::Arc; /// /// let mut values = Arc::<[u32]>::new_uninit_slice(3); @@ -1326,8 +1321,6 @@ impl Arc, A> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::sync::Arc; /// /// let mut five = Arc::::new_uninit(); @@ -1364,8 +1357,6 @@ impl Arc<[mem::MaybeUninit], A> { /// # Examples /// /// ``` - /// #![feature(get_mut_unchecked)] - /// /// use std::sync::Arc; /// /// let mut values = Arc::<[u32]>::new_uninit_slice(3); From 8a0e3808c04c16732c4651884c2a28063f4eec43 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:46:18 +0530 Subject: [PATCH 0741/1900] add check for toml file --- src/bootstrap/src/core/config/tests.rs | 13 +++---------- src/bootstrap/src/core/config/toml/mod.rs | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index f277e3704f277..3390e9586a4c7 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -307,10 +307,11 @@ fn clippy_rule_separate_prefix() { #[test] fn verbose_tests_default_value() { - let config = Config::parse(Flags::parse(&["build".into(), "compiler".into()])); + let config = TestCtx::new().config("build").args(&["compiler".into()]).create_config(); assert_eq!(config.verbose_tests, false); - let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()])); + let config = + TestCtx::new().config("build").args(&["compiler".into(), "-v".into()]).create_config(); assert_eq!(config.verbose_tests, true); } @@ -643,18 +644,10 @@ fn test_include_precedence_over_profile() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - profile = "dist" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - let config = test_ctx .config("check") .with_default_toml_config( r#" - profile = "dist" include = ["./extension.toml"] "#, ) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index f6dc5b67e1010..5f86d2e728184 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,6 +152,12 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { + #[cfg(test)] + { + let tmp = std::env::temp_dir(); + assert!(file.starts_with(&tmp), "Expected path in temp dir {:?}, got {:?}", tmp, file); + } + Self::get_toml_inner(file) } From 83b784fda12a3422bfa7ce14c1bbd9864df70e5e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:54:23 +0530 Subject: [PATCH 0742/1900] add comment explaining the test_build_dir --- src/bootstrap/src/core/config/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 957de5367ef6a..280ae088f3f3d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2479,6 +2479,13 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { .collect() } +/// Resolve the build directory used for tests. +/// +/// - When tests are run through bootstrap (`x.py test`), the build system +/// sets `CARGO_TARGET_DIR`, so we can trust and use it here. +/// - When tests are run directly with cargo test, `CARGO_TARGET_DIR` will +/// not be set. In that case we fall back to resolving relative to +/// `CARGO_MANIFEST_DIR`, by walking two parents up and appending `build`. fn test_build_dir() -> PathBuf { env::var_os("CARGO_TARGET_DIR") .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) From a45d58bcb55f3639f07721ca3032d5bec1b0443f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 22 Sep 2025 16:53:20 +0300 Subject: [PATCH 0743/1900] Fix lifetime elision handling for `Fn`-style trait bounds Two fixes were needed: 1. Previously, we enabled elision for the generic args of `Fn` itself, instead of for generic args of paths within it. 2. In lowering in the new solver the `Output` parameter did not have elision set correctly, I don't know why. In the old lowering it was done correctly. --- .../crates/hir-ty/src/lower/path.rs | 12 ++-- .../hir-ty/src/lower_nextsolver/path.rs | 56 ++++++++++++------- .../src/handlers/missing_lifetime.rs | 12 ++++ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index b0132e4dcbc46..bc03298e3bbb0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -603,7 +603,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { explicit_self_ty: Option, lowering_assoc_type_generics: bool, ) -> Substitution { - let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + let old_lifetime_elision = self.ctx.lifetime_elision.clone(); if let Some(args) = self.current_or_prev_segment.args_and_bindings && args.parenthesized != GenericArgsParentheses::No @@ -633,19 +633,21 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. - lifetime_elision = + self.ctx.lifetime_elision = LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; } - self.substs_from_args_and_bindings( + let result = self.substs_from_args_and_bindings( self.current_or_prev_segment.args_and_bindings, def, infer_args, explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, - lifetime_elision, - ) + self.ctx.lifetime_elision.clone(), + ); + self.ctx.lifetime_elision = old_lifetime_elision; + result } pub(super) fn substs_from_args_and_bindings( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index 7d6734303c48b..46dc66a8c8bae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -616,7 +616,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty: Option>, lowering_assoc_type_generics: bool, ) -> crate::next_solver::GenericArgs<'db> { - let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + let old_lifetime_elision = self.ctx.lifetime_elision.clone(); if let Some(args) = self.current_or_prev_segment.args_and_bindings && args.parenthesized != GenericArgsParentheses::No @@ -646,19 +646,21 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. - lifetime_elision = + self.ctx.lifetime_elision = LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; } - self.substs_from_args_and_bindings( + let result = self.substs_from_args_and_bindings( self.current_or_prev_segment.args_and_bindings, def, infer_args, explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, - lifetime_elision, - ) + self.ctx.lifetime_elision.clone(), + ); + self.ctx.lifetime_elision = old_lifetime_elision; + result } pub(super) fn substs_from_args_and_bindings( @@ -915,22 +917,36 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = binding.type_ref { - match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { - (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), - (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { - let ty = self.ctx.lower_ty(type_ref); - let pred = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Projection(ProjectionPredicate { - projection_term, - term: ty.into(), - }), - )), - )); - predicates.push(pred); + let lifetime_elision = + if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar { + // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). + LifetimeElisionKind::for_fn_ret(self.ctx.interner) + } else { + self.ctx.lifetime_elision.clone() + }; + self.with_lifetime_elision(lifetime_elision, |this| { + match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + ( + _, + ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque, + ) => { + let ty = this.ctx.lower_ty(type_ref); + let pred = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection( + ProjectionPredicate { + projection_term, + term: ty.into(), + }, + ), + )), + )); + predicates.push(pred); + } } - } + }) } for bound in binding.bounds.iter() { predicates.extend(self.ctx.lower_type_bound( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index 76b30745a04d1..b07f9e68f6341 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -88,4 +88,16 @@ fn bar() {} "#, ); } + + #[test] + fn fn_traits() { + check_diagnostics( + r#" +//- minicore: fn +struct WithLifetime<'a>(&'a ()); + +fn foo WithLifetime>() {} + "#, + ); + } } From a23d4e16d82953c493c75acc568a2c75a368828c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 22 Sep 2025 15:56:27 +0200 Subject: [PATCH 0744/1900] flip1995 is back from vacation --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 7b19f8658c088..b2fb50918f583 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,7 +60,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB%20users_on_vacation%20=%20["matthiaskrgr", "Manishearth", - "flip1995", ] [assign.owners] From 5dde557fc49ff0268f9890786ef050802b309a75 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Sat, 6 Sep 2025 12:57:18 -0400 Subject: [PATCH 0745/1900] constify {float}::total_cmp() --- library/core/src/num/f128.rs | 3 +- library/core/src/num/f16.rs | 3 +- library/core/src/num/f32.rs | 3 +- library/core/src/num/f64.rs | 3 +- library/coretests/tests/floats/mod.rs | 171 +++++++++++++------------- 5 files changed, 93 insertions(+), 90 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 66c892aadd083..c5323c9d0421a 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1196,7 +1196,8 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + pub const fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i128; let mut right = other.to_bits() as i128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 81220065e72a6..77b73b6348924 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1175,7 +1175,8 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + pub const fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i16; let mut right = other.to_bits() as i16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index cefcf1d1fe2fc..da0ba7fe1db56 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1353,9 +1353,10 @@ impl f32 { /// } /// ``` #[stable(feature = "total_cmp", since = "1.62.0")] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[must_use] #[inline] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + pub const fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i32; let mut right = other.to_bits() as i32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 9dd1141e70331..c93056e310635 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1351,9 +1351,10 @@ impl f64 { /// } /// ``` #[stable(feature = "total_cmp", since = "1.62.0")] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[must_use] #[inline] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + pub const fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i64; let mut right = other.to_bits() as i64; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 31515561c637b..d2b5722309445 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1246,104 +1246,103 @@ float_test! { float_test! { name: total_cmp, attrs: { - const: #[cfg(false)], f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, test { use core::cmp::Ordering; - fn quiet_bit_mask() -> ::Int { + const fn quiet_bit_mask() -> ::Int { 1 << (Float::MANTISSA_DIGITS - 2) } - fn q_nan() -> Float { + const fn q_nan() -> Float { Float::from_bits(Float::NAN.to_bits() | quiet_bit_mask()) } - assert_eq!(Ordering::Equal, Float::total_cmp(&-q_nan(), &-q_nan())); - assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::INFINITY, &-Float::INFINITY)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MAX, &-Float::MAX)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-2.5, &-2.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-1.0, &-1.0)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-1.5, &-1.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-0.5, &-0.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::TINY, &-Float::TINY)); - assert_eq!(Ordering::Equal, Float::total_cmp(&-0.0, &-0.0)); - assert_eq!(Ordering::Equal, Float::total_cmp(&0.0, &0.0)); - assert_eq!(Ordering::Equal, Float::total_cmp(&Float::TINY, &Float::TINY)); - assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MIN_POSITIVE, &Float::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, Float::total_cmp(&0.5, &0.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&1.0, &1.0)); - assert_eq!(Ordering::Equal, Float::total_cmp(&1.5, &1.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&2.5, &2.5)); - assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MAX, &Float::MAX)); - assert_eq!(Ordering::Equal, Float::total_cmp(&Float::INFINITY, &Float::INFINITY)); - assert_eq!(Ordering::Equal, Float::total_cmp(&q_nan(), &q_nan())); - - assert_eq!(Ordering::Less, Float::total_cmp(&-Float::INFINITY, &-Float::MAX)); - assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MAX, &-2.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-2.5, &-1.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-1.5, &-1.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-1.0, &-0.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-0.5, &-Float::MIN_POSITIVE)); - assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::TINY)); - assert_eq!(Ordering::Less, Float::total_cmp(&-Float::TINY, &-0.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-0.0, &0.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&0.0, &Float::TINY)); - assert_eq!(Ordering::Less, Float::total_cmp(&Float::TINY, &Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Less, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MIN_POSITIVE)); - assert_eq!(Ordering::Less, Float::total_cmp(&Float::MIN_POSITIVE, &0.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&0.5, &1.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&1.0, &1.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&1.5, &2.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&2.5, &Float::MAX)); - assert_eq!(Ordering::Less, Float::total_cmp(&Float::MAX, &Float::INFINITY)); - - assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MAX, &-Float::INFINITY)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-2.5, &-Float::MAX)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-1.5, &-2.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-1.0, &-1.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-0.5, &-1.0)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MIN_POSITIVE, &-0.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::TINY, &-Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Greater, Float::total_cmp(&-0.0, &-Float::TINY)); - assert_eq!(Ordering::Greater, Float::total_cmp(&0.0, &-0.0)); - assert_eq!(Ordering::Greater, Float::total_cmp(&Float::TINY, &0.0)); - assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::TINY)); - assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MIN_POSITIVE, &Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Greater, Float::total_cmp(&0.5, &Float::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, Float::total_cmp(&1.0, &0.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&1.5, &1.0)); - assert_eq!(Ordering::Greater, Float::total_cmp(&2.5, &1.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MAX, &2.5)); - assert_eq!(Ordering::Greater, Float::total_cmp(&Float::INFINITY, &Float::MAX)); - - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::INFINITY)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MAX)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-2.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-1.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-1.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-0.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MIN_POSITIVE)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::TINY)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-0.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &0.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::TINY)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MAX_SUBNORMAL)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MIN_POSITIVE)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &0.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &1.0)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &1.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &2.5)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MAX)); - assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::INFINITY)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-q_nan()), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-Float::INFINITY, &-Float::INFINITY), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-Float::MAX, &-Float::MAX), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-2.5, &-2.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-1.0, &-1.0), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-1.5, &-1.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-0.5, &-0.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MIN_POSITIVE), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MAX_SUBNORMAL), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-Float::TINY, &-Float::TINY), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&-0.0, &-0.0), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&0.0, &0.0), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&Float::TINY, &Float::TINY), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MAX_SUBNORMAL), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&Float::MIN_POSITIVE, &Float::MIN_POSITIVE), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&0.5, &0.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&1.0, &1.0), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&1.5, &1.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&2.5, &2.5), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&Float::MAX, &Float::MAX), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&Float::INFINITY, &Float::INFINITY), Ordering::Equal)); + assert!(matches!(Float::total_cmp(&q_nan(), &q_nan()), Ordering::Equal)); + + assert!(matches!(Float::total_cmp(&-Float::INFINITY, &-Float::MAX), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-Float::MAX, &-2.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-2.5, &-1.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-1.5, &-1.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-1.0, &-0.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-0.5, &-Float::MIN_POSITIVE), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MAX_SUBNORMAL), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::TINY), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-Float::TINY, &-0.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-0.0, &0.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&0.0, &Float::TINY), Ordering::Less)); + assert!(matches!(Float::total_cmp(&Float::TINY, &Float::MAX_SUBNORMAL), Ordering::Less)); + assert!(matches!(Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MIN_POSITIVE), Ordering::Less)); + assert!(matches!(Float::total_cmp(&Float::MIN_POSITIVE, &0.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&0.5, &1.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&1.0, &1.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&1.5, &2.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&2.5, &Float::MAX), Ordering::Less)); + assert!(matches!(Float::total_cmp(&Float::MAX, &Float::INFINITY), Ordering::Less)); + + assert!(matches!(Float::total_cmp(&-Float::MAX, &-Float::INFINITY), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-2.5, &-Float::MAX), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-1.5, &-2.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-1.0, &-1.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-0.5, &-1.0), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-Float::MIN_POSITIVE, &-0.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MIN_POSITIVE), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-Float::TINY, &-Float::MAX_SUBNORMAL), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&-0.0, &-Float::TINY), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&0.0, &-0.0), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&Float::TINY, &0.0), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::TINY), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&Float::MIN_POSITIVE, &Float::MAX_SUBNORMAL), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&0.5, &Float::MIN_POSITIVE), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&1.0, &0.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&1.5, &1.0), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&2.5, &1.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&Float::MAX, &2.5), Ordering::Greater)); + assert!(matches!(Float::total_cmp(&Float::INFINITY, &Float::MAX), Ordering::Greater)); + + assert!(matches!(Float::total_cmp(&-q_nan(), &-Float::INFINITY), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-Float::MAX), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-2.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-1.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-1.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-0.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-Float::MIN_POSITIVE), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-Float::MAX_SUBNORMAL), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-Float::TINY), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &-0.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &0.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &Float::TINY), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &Float::MAX_SUBNORMAL), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &Float::MIN_POSITIVE), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &0.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &1.0), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &1.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &2.5), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &Float::MAX), Ordering::Less)); + assert!(matches!(Float::total_cmp(&-q_nan(), &Float::INFINITY), Ordering::Less)); } } From 62b2bd5809c18b79798b730058f494d9a2fb85c4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 22 Sep 2025 16:26:27 +0200 Subject: [PATCH 0746/1900] reduce overlong lines --- .../bootstrapping/writing-tools-in-bootstrap.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md index 8250a6f3b51df..8ac2e6bfe287c 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md @@ -19,11 +19,16 @@ There are three types of tools you can write in bootstrap: Use this for tools that use the `rustc_private` mechanism, and thus depend on the locally built `rustc` and its rlib artifacts. - This is more complex than the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" directory. - When you choose `Mode::ToolRustcPrivate`, `ToolBuild` implementation takes care of this automatically. + This is more complex than the other modes, + because the tool must be built with the same compiler used for `rustc`, + and placed in the "stageN-tools" directory. + When you choose `Mode::ToolRustcPrivate`, + `ToolBuild` implementation takes care of this automatically. If you need to use the builder’s compiler for something specific, you can get it from `ToolBuildResult`, which is returned by the tool's [`Step`]. -Regardless of the tool type you must return `ToolBuildResult` from the tool’s [`Step`] implementation and use `ToolBuild` inside it. +Regardless of the tool type, +you must return `ToolBuildResult` from the tool’s [`Step`] implementation, +and use `ToolBuild` inside it. [`Step`]: https://doc.rust-lang.org/nightly/nightly-rustc/bootstrap/core/builder/trait.Step.html From 1e2594f94f6ba665d5fc290986f8181137c94a17 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 22 Sep 2025 16:32:50 +0200 Subject: [PATCH 0747/1900] various improvements resulting from reading Testing with CI --- src/doc/rustc-dev-guide/src/tests/ci.md | 106 ++++++++++---------- src/doc/rustc-dev-guide/src/tests/crater.md | 24 ++--- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index d9fc2324d8bf6..43afb8536cb80 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -7,12 +7,12 @@ From a high-level point of view, when you open a pull request at `rust-lang/rust`, the following will happen: - A small [subset](#pull-request-builds) of tests and checks are run after each - push to the PR. This should help catching common errors. + push to the PR. This should help catch common errors. - When the PR is approved, the [bors] bot enqueues the PR into a [merge queue]. - Once the PR gets to the front of the queue, bors will create a merge commit and run the [full test suite](#auto-builds) on it. The merge commit either contains only one specific PR or it can be a ["rollup"](#rollups) which - combines multiple PRs together, to save CI costs. + combines multiple PRs together, to reduce CI costs and merge delays. - Once the whole test suite finishes, two things can happen. Either CI fails with an error that needs to be addressed by the developer, or CI succeeds and the merge commit is then pushed to the `master` branch. @@ -38,12 +38,12 @@ input, which contains a declarative configuration of all our CI jobs. > orchestrating the scripts that drive the process. In essence, all CI jobs run `./x test`, `./x dist` or some other command with -different configurations, across various operating systems, targets and +different configurations, across various operating systems, targets, and platforms. There are two broad categories of jobs that are executed, `dist` and non-`dist` jobs. - Dist jobs build a full release of the compiler for a specific platform, - including all the tools we ship through rustup; Those builds are then uploaded + including all the tools we ship through rustup. Those builds are then uploaded to the `rust-lang-ci2` S3 bucket and are available to be locally installed with the [rustup-toolchain-install-master] tool. The same builds are also used for actual releases: our release process basically consists of copying those @@ -70,7 +70,7 @@ these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `pr-check-1`, `pr-che and `tidy` jobs, all running on Linux. These execute a relatively short (~40 minutes) and lightweight test suite that should catch common issues. More specifically, they run a set of lints, they try to perform a cross-compile check -build to Windows mingw (without producing any artifacts) and they test the +build to Windows mingw (without producing any artifacts), and they test the compiler using a *system* version of LLVM. Unfortunately, it would take too many resources to run the full test suite for each commit on every PR. @@ -95,17 +95,16 @@ jobs that exercise various tests across operating systems and targets. The full test suite is quite slow; it can take several hours until all the `auto` CI jobs finish. -Most platforms only run the build steps, some run a restricted set of tests, +Most platforms only run the build steps, some run a restricted set of tests; only a subset run the full suite of tests (see Rust's [platform tiers]). Auto jobs are defined in the `auto` section of [`jobs.yml`]. They are executed -on the `auto` branch under the `rust-lang/rust` repository and -their results can be seen [here](https://github.com/rust-lang/rust/actions), -although usually you will be notified of the result by a comment made by bors on -the corresponding PR. +on the `auto` branch under the `rust-lang/rust` repository, +and the final result will be reported via a comment made by bors on the corresponding PR. +The live results can be seen on [the GitHub Actions workflows page]. At any given time, at most a single `auto` build is being executed. Find out -more [here](#merging-prs-serially-with-bors). +more in [Merging PRs serially with bors](#merging-prs-serially-with-bors). [platform tiers]: https://forge.rust-lang.org/release/platform-support.html#rust-platform-support @@ -125,7 +124,7 @@ There are several use-cases for try builds: when you start a try build). To create a try build and schedule it for a performance benchmark, you can use the `@bors try @rust-timer queue` command combination. -- Check the impact of the PR across the Rust ecosystem, using a [crater] run. +- Check the impact of the PR across the Rust ecosystem, using a [Crater](crater.md) run. Again, a working compiler build is needed for this, which can be produced by the [dist-x86_64-linux] CI job. - Run a specific CI job (e.g. Windows tests) on a PR, to quickly test if it @@ -134,11 +133,11 @@ There are several use-cases for try builds: By default, if you send a comment with `@bors try`, the jobs defined in the `try` section of [`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build will not execute any tests, and it will allow compilation warnings. It is useful when you want to -get an optimized toolchain as fast as possible, for a crater run or performance benchmarks, +get an optimized toolchain as fast as possible, for a Crater run or performance benchmarks, even if it might not be working fully correctly. If you want to do a full build for the default try job, specify its job name in a job pattern (explained below). -If you want to run custom CI job(s) in a try build and make sure that they pass all tests and do +If you want to run custom CI jobs in a try build and make sure that they pass all tests and do not produce any compilation warnings, you can select CI jobs to be executed by specifying a *job pattern*, which can be used in one of two ways: - You can add a set of `try-job: ` directives to the PR description (described below) and then @@ -151,7 +150,7 @@ which can be used in one of two ways: Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using -glob patterns in the PR description, you can (but do not have to) wrap them in backticks (`` ` ``) to avoid GitHub rendering +glob patterns in the PR description, you can optionally wrap them in backticks (`` ` ``) to avoid GitHub rendering the pattern as Markdown if it contains e.g. an asterisk. Note that this escaping will not work when using the `@bors jobs=` parameter. @@ -190,18 +189,17 @@ of [`jobs.yml`]: > that are exercised this way. Try builds are executed on the `try` branch under the `rust-lang/rust` repository and -their results can be seen [here](https://github.com/rust-lang/rust/actions), +their results can be seen on [the GitHub Actions workflows page], although usually you will be notified of the result by a comment made by bors on the corresponding PR. Multiple try builds can execute concurrently across different PRs, but there can be at most a single try build running on a single PR at any given time. -Note that try builds are handled using the new [bors][new-bors] implementation. +Note that try builds are handled using the [new bors] implementation. [rustc-perf]: https://github.com/rust-lang/rustc-perf -[crater]: https://github.com/rust-lang/crater -[new-bors]: https://github.com/rust-lang/bors +[new bors]: https://github.com/rust-lang/bors ### Modifying CI jobs @@ -211,8 +209,7 @@ If you want to modify what gets executed on our CI, you can simply modify the You can also modify what gets executed temporarily, for example to test a particular platform or configuration that is challenging to test locally (for example, if a Windows build fails, but you don't have access to a Windows -machine). Don't hesitate to use CI resources in such situations to try out a -fix! +machine). Don't hesitate to use CI resources in such situations. You can perform an arbitrary CI job in two ways: - Use the [try build](#try-builds) functionality, and specify the CI jobs that @@ -255,8 +252,8 @@ purposes. Although you are welcome to use CI, just be conscious that this is a shared -resource with limited concurrency. Try not to enable too many jobs at once (one -or two should be sufficient in most cases). +resource with limited concurrency. Try not to enable too many jobs at once; +one or two should be sufficient in most cases. ## Merging PRs serially with bors @@ -280,12 +277,12 @@ by listening for either Commit Statuses or Check Runs. Since the merge commit is based on the latest `master` and only one can be tested at the same time, when the results are green, `master` is fast-forwarded to that merge commit. -Unfortunately testing a single PR at the time, combined with our long CI (~2 -hours for a full run), means we can’t merge too many PRs in a single day, and a -single failure greatly impacts our throughput for the day. The maximum number of +Unfortunately, testing a single PR at a time, combined with our long CI (~2 +hours for a full run), means we can’t merge a lot of PRs in a single day, and a +single failure greatly impacts our throughput. The maximum number of PRs we can merge in a day is around ~10. -The large CI run times and requirement for a large builder pool is largely due +The long CI run times, and requirement for a large builder pool, is largely due to the fact that full release artifacts are built in the `dist-` builders. This is worth it because these release artifacts: @@ -298,12 +295,11 @@ is worth it because these release artifacts: Some PRs don’t need the full test suite to be executed: trivial changes like typo fixes or README improvements *shouldn’t* break the build, and testing every -single one of them for 2+ hours is a big waste of time. To solve this, we +single one of them for 2+ hours would be wasteful. To solve this, we regularly create a "rollup", a PR where we merge several pending trivial PRs so they can be tested together. Rollups are created manually by a team member using the "create a rollup" button on the [merge queue]. The team member uses their -judgment to decide if a PR is risky or not, and are the best tool we have at the -moment to keep the queue in a manageable state. +judgment to decide if a PR is risky or not. ## Docker @@ -316,18 +312,22 @@ platform’s custom [Docker container]. This has a lot of advantages for us: - We can use ancient build environments to ensure maximum binary compatibility, for example [using older CentOS releases][dist-x86_64-linux] on our Linux builders. -- We can avoid reinstalling tools (like QEMU or the Android emulator) every time +- We can avoid reinstalling tools (like QEMU or the Android emulator) every time, thanks to Docker image caching. -- Users can run the same tests in the same environment locally by just running - `cargo run --manifest-path src/ci/citool/Cargo.toml run-local `, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and +- Users can run the same tests in the same environment locally by just running this command: + + cargo run --manifest-path src/ci/citool/Cargo.toml run-local + + This is helpful for debugging failures. + Note that there are only Linux Docker images available locally due to licensing and other restrictions. -The docker images prefixed with `dist-` are used for building artifacts while +The Docker images prefixed with `dist-` are used for building artifacts while those without that prefix run tests and checks. We also run tests for less common architectures (mainly Tier 2 and Tier 3 -platforms) in CI. Since those platforms are not x86 we either run everything -inside QEMU or just cross-compile if we don’t want to run the tests for that +platforms) in CI. Since those platforms are not x86, we either run everything +inside QEMU, or we just cross-compile if we don’t want to run the tests for that platform. These builders are running on a special pool of builders set up and maintained @@ -364,41 +364,41 @@ invalidated if one of the following changes: [ghcr.io]: https://github.com/rust-lang/rust/pkgs/container/rust-ci [Docker registry caching]: https://docs.docker.com/build/cache/backends/registry/ -### LLVM caching with sccache +### LLVM caching with Sccache -We build some C/C++ stuff in various CI jobs, and we rely on [sccache] to cache +We build some C/C++ stuff in various CI jobs, and we rely on [Sccache] to cache the intermediate LLVM artifacts. Sccache is a distributed ccache developed by Mozilla, which can use an object storage bucket as the storage backend. -With sccache there's no need to calculate the hash key ourselves. Sccache +With Sccache there's no need to calculate the hash key ourselves. Sccache invalidates the cache automatically when it detects changes to relevant inputs, such as the source code, the version of the compiler, and important environment variables. -So we just pass the sccache wrapper on top of cargo and sccache does the rest. +So we just pass the Sccache wrapper on top of Cargo and Sccache does the rest. -We store the persistent artifacts on the S3 bucket `rust-lang-ci-sccache2`. So -when the CI runs, if sccache sees that LLVM is being compiled with the same C/C++ -compiler and the LLVM source code is the same, sccache retrieves the individual +We store the persistent artifacts on the S3 bucket, `rust-lang-ci-sccache2`. So +when the CI runs, if Sccache sees that LLVM is being compiled with the same C/C++ +compiler and the LLVM source code is the same, Sccache retrieves the individual compiled translation units from S3. [sccache]: https://github.com/mozilla/sccache ## Custom tooling around CI -During the years we developed some custom tooling to improve our CI experience. +During the years, we developed some custom tooling to improve our CI experience. ### Rust Log Analyzer to show the error message in PRs The build logs for `rust-lang/rust` are huge, and it’s not practical to find -what caused the build to fail by looking at the logs. To improve the developers’ -experience we developed a bot called [Rust Log Analyzer][rla] (RLA) that -receives the build logs on failure and extracts the error message automatically, -posting it on the PR. +what caused the build to fail by looking at the logs. +We therefore developed a bot called [Rust Log Analyzer][rla] (RLA) that +receives the build logs on failure, and extracts the error message automatically, +posting it on the PR thread. The bot is not hardcoded to look for error strings, but was trained with a bunch of build failures to recognize which lines are common between builds and which are not. While the generated snippets can be weird sometimes, the bot is pretty -good at identifying the relevant lines even if it’s an error we've never seen +good at identifying the relevant lines, even if it’s an error we've never seen before. [rla]: https://github.com/rust-lang/rust-log-analyzer @@ -430,11 +430,11 @@ More information is available in the [toolstate documentation]. ## Public CI dashboard -To monitor the Rust CI, you can have a look at the [public dashboard] maintained by the infra-team. +To monitor the Rust CI, you can have a look at the [public dashboard] maintained by the infra team. These are some useful panels from the dashboard: -- Pipeline duration: check how long the auto builds takes to run. +- Pipeline duration: check how long the auto builds take to run. - Top slowest jobs: check which jobs are taking the longest to run. - Change in median job duration: check what jobs are slowest than before. Useful to detect regressions. @@ -457,8 +457,7 @@ this: 2. Choose the job you are interested in on the left-hand side. 3. Click on the gear icon and choose "View raw logs" 4. Search for the string "Configure the build" -5. All of the build settings are listed below that starting with the - `configure:` prefix. +5. All of the build settings are listed on the line with the text, `build.configure-args` [GitHub Actions]: https://github.com/rust-lang/rust/actions [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml @@ -468,3 +467,4 @@ this: [homu]: https://github.com/rust-lang/homu [merge queue]: https://bors.rust-lang.org/queue/rust [dist-x86_64-linux]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +[the GitHub Actions workflows page]: https://github.com/rust-lang/rust/actions diff --git a/src/doc/rustc-dev-guide/src/tests/crater.md b/src/doc/rustc-dev-guide/src/tests/crater.md index 9d4ac87daf36a..96bb5a4f2ae67 100644 --- a/src/doc/rustc-dev-guide/src/tests/crater.md +++ b/src/doc/rustc-dev-guide/src/tests/crater.md @@ -8,30 +8,30 @@ stable compiler versions. ## When to run Crater -You should request a crater run if your PR makes large changes to the compiler +You should request a Crater run if your PR makes large changes to the compiler or could cause breakage. If you are unsure, feel free to ask your PR's reviewer. ## Requesting Crater Runs -The rust team maintains a few machines that can be used for running crater runs -on the changes introduced by a PR. If your PR needs a crater run, leave a +The Rust team maintains a few machines that can be used for Crater runs +on the changes introduced by a PR. If your PR needs a Crater run, leave a comment for the triage team in the PR thread. Please inform the team whether you -require a "check-only" crater run, a "build only" crater run, or a -"build-and-test" crater run. The difference is primarily in time; the -conservative (if you're not sure) option is to go for the build-and-test run. If +require a "check-only" Crater run, a "build only" Crater run, or a +"build-and-test" Crater run. The difference is primarily in time; +if you're not sure, go for the build-and-test run. If making changes that will only have an effect at compile-time (e.g., implementing -a new trait) then you only need a check run. +a new trait), then you only need a check run. Your PR will be enqueued by the triage team and the results will be posted when -they are ready. Check runs will take around ~3-4 days, with the other two taking +they are ready. Check runs will take around ~3-4 days, and the other two taking 5-6 days on average. -While crater is really useful, it is also important to be aware of a few +While Crater is really useful, it is also important to be aware of a few caveats: - Not all code is on crates.io! There is a lot of code in repos on GitHub and elsewhere. Also, companies may not wish to publish their code. Thus, a - successful crater run is not a magically green light that there will be no + successful Crater run does not mean there will be no breakage; you still need to be careful. - Crater only runs Linux builds on x86_64. Thus, other architectures and @@ -41,5 +41,5 @@ caveats: the crate doesn't compile any more (e.g. used old nightly features), has broken or flaky tests, requires network access, or other reasons. -- Before crater can be run, `@bors try` needs to succeed in building artifacts. - This means that if your code doesn't compile, you cannot run crater. +- Before Crater can be run, `@bors try` needs to succeed in building artifacts. + This means that if your code doesn't compile, you cannot run Crater. From 75e3eb82682752563a2687ffb3f135e68ea2b802 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 22 Sep 2025 00:42:44 +0900 Subject: [PATCH 0748/1900] internal: Migrate more predicate things to next-solver --- .../crates/hir-ty/src/infer/unify.rs | 110 +++++++----------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 47 +++++--- .../crates/hir-ty/src/lower_nextsolver.rs | 12 ++ .../crates/hir-ty/src/method_resolution.rs | 100 ++++++---------- .../rust-analyzer/crates/hir-ty/src/traits.rs | 6 +- 5 files changed, 122 insertions(+), 153 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 1687857ae1ac2..cee95d3880df9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -11,39 +11,35 @@ use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::Ty as _; use rustc_type_ir::{ - FloatVid, IntVid, TyVid, TypeVisitableExt, - inherent::{IntoKind, Span, Term as _}, + FloatVid, IntVid, TyVid, TypeVisitableExt, UpcastFrom, + inherent::{IntoKind, Span, Term as _, Ty as _}, relate::{Relate, solver_relating::RelateExt}, - solve::{Certainty, GoalSource, NoSolution}, + solve::{Certainty, GoalSource}, }; use smallvec::SmallVec; use triomphe::Arc; use super::{InferResult, InferenceContext, TypeError}; -use crate::next_solver::ErrorGuaranteed; use crate::{ AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData, - Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, - ProjectionTy, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - VariableKind, WhereClause, + InferenceVar, Interner, Lifetime, OpaqueTyId, ProjectionTy, Scalar, Substitution, + TraitEnvironment, Ty, TyExt, TyKind, VariableKind, consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, - next_solver::infer::InferOk, next_solver::{ - self, ClauseKind, DbInterner, ParamEnv, Predicate, PredicateKind, SolverDefIds, Term, + self, ClauseKind, DbInterner, ErrorGuaranteed, ParamEnv, Predicate, PredicateKind, + SolverDefIds, Term, TraitRef, fulfill::FulfillmentCtxt, infer::{ - DbInternerInferExt, InferCtxt, + DbInternerInferExt, InferCtxt, InferOk, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause}, }, inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, - to_chalk_trait_id, traits::{ FnTrait, NextTraitSolveResult, next_trait_solve_canonical_in_ctxt, next_trait_solve_in_ctxt, }, @@ -877,26 +873,15 @@ impl<'db> InferenceTable<'db> { /// whether a trait *might* be implemented before deciding to 'lock in' the /// choice (during e.g. method resolution or deref). #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { - let in_env = InEnvironment::new(&self.trait_env.env, goal); - let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner)); + pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { + let goal = next_solver::Goal { param_env: self.param_env, predicate }; + let canonicalized = self.canonicalize(goal); next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) } - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result { - let goal = InEnvironment::new(&self.trait_env.env, goal); - let goal = goal.to_nextsolver(self.interner); - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - result.map(|m| m.1) - } - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = next_solver::Goal { - param_env: self.trait_env.env.to_nextsolver(self.interner), - predicate, - }; + let goal = next_solver::Goal { param_env: self.param_env, predicate }; self.register_obligation_in_env(goal) } @@ -984,7 +969,7 @@ impl<'db> InferenceTable<'db> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(FnTrait, Vec>, crate::next_solver::Ty<'db>)> { + ) -> Option<(FnTrait, Vec>, next_solver::Ty<'db>)> { for (fn_trait_name, output_assoc_name, subtraits) in [ (FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]), (FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]), @@ -997,42 +982,34 @@ impl<'db> InferenceTable<'db> { trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?; let mut arg_tys = Vec::with_capacity(num_args); - let arg_ty = TyBuilder::tuple(num_args) - .fill(|it| { - let arg = match it { - ParamKind::Type => self.new_type_var(), - ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), - ParamKind::Const(_) => unreachable!("Tuple with const parameter"), - }; - arg_tys.push(arg.to_nextsolver(self.interner)); - arg.cast(Interner) + let arg_ty = next_solver::Ty::new_tup_from_iter( + self.interner, + std::iter::repeat_with(|| { + let ty = self.next_ty_var(); + arg_tys.push(ty); + ty }) - .build(); - - let b = TyBuilder::trait_ref(self.db, fn_trait); - if b.remaining() != 2 { - return None; - } - let mut trait_ref = b.push(ty.clone()).push(arg_ty).build(); - - let projection = TyBuilder::assoc_type_projection( - self.db, - output_assoc_type, - Some(trait_ref.substitution.clone()), - ) - .fill_with_unknown() - .build(); - - let goal: Goal = trait_ref.clone().cast(Interner); - if !self.try_obligation(goal.clone()).no_solution() { - self.register_obligation(goal.to_nextsolver(self.interner)); - let return_ty = - self.normalize_projection_ty(projection).to_nextsolver(self.interner); + .take(num_args), + ); + let args = [ty.to_nextsolver(self.interner), arg_ty]; + let trait_ref = crate::next_solver::TraitRef::new(self.interner, fn_trait.into(), args); + + let projection = crate::next_solver::Ty::new_alias( + self.interner, + rustc_type_ir::AliasTyKind::Projection, + crate::next_solver::AliasTy::new(self.interner, output_assoc_type.into(), args), + ); + + let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner); + if !self.try_obligation(pred).no_solution() { + self.register_obligation(pred); + let return_ty = self.normalize_alias_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; - trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - let goal = trait_ref.clone().cast(Interner); - if !self.try_obligation(goal).no_solution() { + let trait_ref = + crate::next_solver::TraitRef::new(self.interner, fn_x_trait.into(), args); + let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner); + if !self.try_obligation(pred).no_solution() { return Some((fn_x, arg_tys, return_ty)); } } @@ -1171,12 +1148,11 @@ impl<'db> InferenceTable<'db> { let Some(sized) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else { return false; }; - let sized_pred = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(sized), - substitution: Substitution::from1(Interner, ty), - }); - let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); - self.try_obligation(goal).certain() + let sized_pred = Predicate::upcast_from( + TraitRef::new(self.interner, sized.into(), [ty.to_nextsolver(self.interner)]), + self.interner, + ); + self.try_obligation(sized_pred).certain() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 451622ef7472b..2accf48843442 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -93,7 +93,10 @@ use intern::{Symbol, sym}; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::SliceLike; +use rustc_type_ir::{ + UpcastFrom, + inherent::{SliceLike, Ty as _}, +}; use syntax::ast::{ConstArg, make}; use traits::FnTrait; use triomphe::Arc; @@ -106,7 +109,7 @@ use crate::{ infer::unify::InferenceTable, next_solver::{ DbInterner, - mapping::{ChalkToNextSolver, convert_ty_for_result}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result}, }, }; @@ -957,26 +960,32 @@ pub fn callable_sig_from_fn_trait( // Register two obligations: // - Self: FnOnce // - >::Output == ?ret_ty - let args_ty = table.new_type_var(); - let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); - let projection = TyBuilder::assoc_type_projection( - db, - output_assoc_type, - Some(trait_ref.substitution.clone()), - ) - .build(); - - let goal: Goal = trait_ref.clone().cast(Interner); - let pred = goal.to_nextsolver(table.interner); - if !table.try_obligation(goal).no_solution() { + let args_ty = table.next_ty_var(); + let args = [self_ty.to_nextsolver(table.interner), args_ty]; + let trait_ref = crate::next_solver::TraitRef::new(table.interner, fn_once_trait.into(), args); + let projection = crate::next_solver::Ty::new_alias( + table.interner, + rustc_type_ir::AliasTyKind::Projection, + crate::next_solver::AliasTy::new(table.interner, output_assoc_type.into(), args), + ); + + let pred = crate::next_solver::Predicate::upcast_from(trait_ref, table.interner); + if !table.try_obligation(pred).no_solution() { table.register_obligation(pred); - let return_ty = table.normalize_projection_ty(projection); + let return_ty = table.normalize_alias_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; - trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { - let ret_ty = table.resolve_completely(return_ty); - let args_ty = table.resolve_completely(args_ty); + let trait_ref = + crate::next_solver::TraitRef::new(table.interner, fn_x_trait.into(), args); + if !table + .try_obligation(crate::next_solver::Predicate::upcast_from( + trait_ref, + table.interner, + )) + .no_solution() + { + let ret_ty = table.resolve_completely(return_ty.to_chalk(table.interner)); + let args_ty = table.resolve_completely(args_ty.to_chalk(table.interner)); let params = args_ty .as_tuple()? .iter(Interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 0076446a958b0..6ecf8874c46d2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1355,6 +1355,18 @@ pub(crate) fn generic_predicates_for_param_cycle_result( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates<'db>(Option]>>); +impl<'db> GenericPredicates<'db> { + pub fn instantiate( + &self, + interner: DbInterner<'db>, + args: GenericArgs<'db>, + ) -> Option>> { + self.0 + .as_ref() + .map(|it| EarlyBinder::bind(it.iter().copied()).iter_instantiated(interner, args)) + } +} + impl<'db> ops::Deref for GenericPredicates<'db> { type Target = [Clause<'db>]; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 7fa3d31fe5fdc..5cd4879a54046 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -32,9 +32,12 @@ use crate::{ lang_items::is_box, next_solver::{ self, SolverDefId, - fulfill::FulfillmentCtxt, - infer::DefineOpaqueTypes, + infer::{ + DefineOpaqueTypes, + traits::{ObligationCause, PredicateObligation}, + }, mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, }, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, @@ -907,10 +910,11 @@ fn find_matching_impl( .into_iter() .map(|b| -> Goal { b.cast(Interner) }); for goal in wcs { - if table.try_obligation(goal.clone()).no_solution() { + let goal = goal.to_nextsolver(table.interner); + if table.try_obligation(goal).no_solution() { return None; } - table.register_obligation(goal.to_nextsolver(table.interner)); + table.register_obligation(goal); } Some(( impl_.impl_items(db), @@ -1395,7 +1399,7 @@ fn iterate_trait_method_candidates( let db = table.db; let canonical_self_ty = table.canonicalize(self_ty.clone().to_nextsolver(table.interner)); - let TraitEnvironment { krate, .. } = *table.trait_env; + let krate = table.trait_env.krate; 'traits: for &t in traits_in_scope { let data = db.trait_signature(t); @@ -1635,7 +1639,6 @@ pub(crate) fn resolve_indexing_op<'db>( let ty = table.instantiate_canonical_ns(ty); let deref_chain = autoderef_method_receiver(table, ty); for (ty, adj) in deref_chain { - //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty); let goal = generic_implements_goal_ns(table, index_trait, ty); if !next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() { return Some(adj); @@ -1752,10 +1755,7 @@ fn is_valid_trait_method_candidate( }; let res = table .infer_ctxt - .at( - &next_solver::infer::traits::ObligationCause::dummy(), - table.trait_env.env.to_nextsolver(table.interner), - ) + .at(&next_solver::infer::traits::ObligationCause::dummy(), table.param_env) .relate( DefineOpaqueTypes::No, expected_receiver.to_nextsolver(table.interner), @@ -1767,12 +1767,10 @@ fn is_valid_trait_method_candidate( }; if !infer_ok.obligations.is_empty() { - let mut ctxt = FulfillmentCtxt::new(&table.infer_ctxt); - for pred in infer_ok.into_obligations() { - ctxt.register_predicate_obligation(&table.infer_ctxt, pred); - } + let mut ctxt = ObligationCtxt::new(&table.infer_ctxt); + ctxt.register_obligations(infer_ok.into_obligations()); // FIXME: Are we doing this correctly? Probably better to follow rustc more closely. - check_that!(ctxt.select_where_possible(&table.infer_ctxt).is_empty()); + check_that!(ctxt.select_where_possible().is_empty()); } check_that!(table.unify(receiver_ty, &expected_receiver)); @@ -1815,9 +1813,11 @@ fn is_valid_impl_fn_candidate( } table.run_in_snapshot(|table| { let _p = tracing::info_span!("subst_for_def").entered(); - let impl_subst = - TyBuilder::subst_for_def(db, impl_id, None).fill_with_inference_vars(table).build(); - let expect_self_ty = db.impl_self_ty(impl_id).substitute(Interner, &impl_subst); + let impl_subst = table.infer_ctxt.fresh_args_for_item(impl_id.into()); + let expect_self_ty = db + .impl_self_ty_ns(impl_id) + .instantiate(table.interner, &impl_subst) + .to_chalk(table.interner); check_that!(table.unify(&expect_self_ty, self_ty)); @@ -1825,9 +1825,8 @@ fn is_valid_impl_fn_candidate( let _p = tracing::info_span!("check_receiver_ty").entered(); check_that!(data.has_self_param()); - let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) - .fill_with_inference_vars(table) - .build(); + let fn_subst: crate::Substitution = + table.infer_ctxt.fresh_args_for_item(fn_id.into()).to_chalk(table.interner); let sig = db.callable_item_signature(fn_id.into()); let expected_receiver = @@ -1838,46 +1837,23 @@ fn is_valid_impl_fn_candidate( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. - let predicates = db.generic_predicates(impl_id.into()); - let goals = predicates.iter().map(|p| { - let (p, b) = p - .clone() - .substitute(Interner, &impl_subst) - // Skipping the inner binders is ok, as we don't handle quantified where - // clauses yet. - .into_value_and_skipped_binders(); - stdx::always!(b.len(Interner) == 0); - - p.cast::(Interner) - }); - - for goal in goals.clone() { - match table.solve_obligation(goal) { - Ok(_) => {} - Err(_) => { - return IsValidCandidate::No; - } - } - } + let predicates = db.generic_predicates_ns(impl_id.into()); + let Some(predicates) = predicates.instantiate(table.interner, impl_subst) else { + return IsValidCandidate::Yes; + }; - for goal in goals { - if table.try_obligation(goal).no_solution() { - return IsValidCandidate::No; - } - } + let mut ctxt = ObligationCtxt::new(&table.infer_ctxt); - IsValidCandidate::Yes - }) -} + ctxt.register_obligations(predicates.into_iter().map(|p| { + PredicateObligation::new(table.interner, ObligationCause::new(), table.param_env, p.0) + })); -pub fn implements_trait( - ty: &Canonical, - db: &dyn HirDatabase, - env: &TraitEnvironment, - trait_: TraitId, -) -> bool { - let goal = generic_implements_goal(db, env, trait_, ty); - !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution() + if ctxt.select_where_possible().is_empty() { + IsValidCandidate::Yes + } else { + IsValidCandidate::No + } + }) } pub fn implements_trait_unique( @@ -1891,7 +1867,7 @@ pub fn implements_trait_unique( } /// This creates Substs for a trait with the given Self type and type variables -/// for all other parameters, to query Chalk with it. +/// for all other parameters, to query next solver with it. #[tracing::instrument(skip_all)] fn generic_implements_goal( db: &dyn HirDatabase, @@ -1934,11 +1910,7 @@ fn generic_implements_goal_ns<'db>( let trait_ref = rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args) .with_replaced_self_ty(table.infer_ctxt.interner, self_ty); - let goal = next_solver::Goal::new( - table.infer_ctxt.interner, - table.trait_env.env.to_nextsolver(table.infer_ctxt.interner), - trait_ref, - ); + let goal = next_solver::Goal::new(table.infer_ctxt.interner, table.param_env, trait_ref); table.canonicalize(goal) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 8095d702be489..3ce78e83c1294 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,4 +1,4 @@ -//! Trait solving using Chalk. +//! Trait solving using next trait solver. use core::fmt; use std::hash::Hash; @@ -128,7 +128,7 @@ fn identity_subst( chalk_ir::Canonical { binders, value: identity_subst } } -/// Solve a trait goal using Chalk. +/// Solve a trait goal using next trait solver. pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: Crate, @@ -325,7 +325,7 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( } } -/// Solve a trait goal using Chalk. +/// Solve a trait goal using next trait solver. pub fn next_trait_solve_in_ctxt<'db, 'a>( infer_ctxt: &'a InferCtxt<'db>, goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, From bd98e73fe0da34a9c57cf87f3ac248a2e653f970 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 17:45:51 +0200 Subject: [PATCH 0749/1900] Update tests/rustdoc/reexport/private-mod-override-reexport.rs --- tests/rustdoc/reexport/private-mod-override-reexport.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rustdoc/reexport/private-mod-override-reexport.rs b/tests/rustdoc/reexport/private-mod-override-reexport.rs index 8deafe2422256..849acc5fdaecd 100644 --- a/tests/rustdoc/reexport/private-mod-override-reexport.rs +++ b/tests/rustdoc/reexport/private-mod-override-reexport.rs @@ -1,6 +1,5 @@ // https://github.com/rust-lang/rust/issues/60926 #![crate_name = "foo"] -#![crate_type = "lib"] mod m1 { pub mod m2 { From 293e0a3aa0dee0cacb4e7aad80f1007e5f06cf39 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 17:36:20 +0200 Subject: [PATCH 0750/1900] fix SIFA logic --- .../tree_borrows/foreign_access_skipping.rs | 2 +- .../src/borrow_tracker/tree_borrows/tree.rs | 33 ++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs index 928b3e6baef44..90df05d36d965 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs @@ -24,7 +24,7 @@ use super::tree::AccessRelatedness; /// "manually" reset the parent's SIFA to be at least as strong as the new child's. This is accomplished with the `ensure_no_stronger_than` method. /// /// Note that we derive Ord and PartialOrd, so the order in which variants are listed below matters: -/// None < Read < Write. Do not change that order. See the `test_order` test. +/// None < Read < Write (weaker to stronger). Do not change that order. See the `test_order` test. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub enum IdempotentForeignAccess { #[default] diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index be2a07eee1503..c4345c63289f1 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -640,13 +640,18 @@ impl<'tcx> Tree { // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); - // We need to know the biggest SIFA for `update_last_accessed_after_retag` below. - let mut max_sifa = default_strongest_idempotent; + // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`. + let mut min_sifa = default_strongest_idempotent; for (Range { start, end }, &perm) in inside_perms.iter(Size::from_bytes(0), inside_perms.size()) { assert!(perm.permission.is_initial()); - max_sifa = cmp::max(max_sifa, perm.idempotent_foreign_access); + assert_eq!( + perm.idempotent_foreign_access, + perm.permission.strongest_idempotent_foreign_access(protected) + ); + + min_sifa = cmp::min(min_sifa, perm.idempotent_foreign_access); for (_perms_range, perms) in self .rperms .iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start)) @@ -656,22 +661,28 @@ impl<'tcx> Tree { } // Inserting the new perms might have broken the SIFA invariant (see - // `foreign_access_skipping.rs`). We now weaken the recorded SIFA for our parents, until the - // invariant is restored. We could weaken them all to `LocalAccess`, but it is more - // efficient to compute the SIFA for the new permission statically, and use that. For this - // we need the *maximum* SIFA (`Write` needs more fixup than `None`). - self.update_last_accessed_after_retag(parent_idx, max_sifa); + // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent. + // We now weaken the recorded SIFA for our parents, until the invariant is restored. We + // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new + // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs + // more fixup than `Write`). + self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa); interp_ok(()) } - /// Restores the SIFA "children are stronger" invariant after a retag. - /// See `foreign_access_skipping` and `new_child`. - fn update_last_accessed_after_retag( + /// Restores the SIFA "children are stronger"/"parents are weaker" invariant after a retag: + /// reduce the SIFA of `current` and its parents to be no stronger than `strongest_allowed`. + /// See `foreign_access_skipping.rs` and [`Tree::new_child`]. + fn update_idempotent_foreign_access_after_retag( &mut self, mut current: UniIndex, strongest_allowed: IdempotentForeignAccess, ) { + if strongest_allowed == IdempotentForeignAccess::Write { + // Nothing is stronger than `Write`. + return; + } // We walk the tree upwards, until the invariant is restored loop { let current_node = self.nodes.get_mut(current).unwrap(); From 2dc1354cd0ed52bedc4f7c823e6fa6af581a3109 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Fri, 5 Sep 2025 19:58:01 +0200 Subject: [PATCH 0751/1900] tests/run-make/crate-loading: Rename source files for clarity To make the code easier to understand. --- .../{multiple-dep-versions-3.rs => dep-2-reexport.rs} | 0 .../{multiple-dep-versions-1.rs => dependency-1.rs} | 0 .../{multiple-dep-versions-2.rs => dependency-2.rs} | 0 tests/run-make/crate-loading/rmake.rs | 9 +++------ 4 files changed, 3 insertions(+), 6 deletions(-) rename tests/run-make/crate-loading/{multiple-dep-versions-3.rs => dep-2-reexport.rs} (100%) rename tests/run-make/crate-loading/{multiple-dep-versions-1.rs => dependency-1.rs} (100%) rename tests/run-make/crate-loading/{multiple-dep-versions-2.rs => dependency-2.rs} (100%) diff --git a/tests/run-make/crate-loading/multiple-dep-versions-3.rs b/tests/run-make/crate-loading/dep-2-reexport.rs similarity index 100% rename from tests/run-make/crate-loading/multiple-dep-versions-3.rs rename to tests/run-make/crate-loading/dep-2-reexport.rs diff --git a/tests/run-make/crate-loading/multiple-dep-versions-1.rs b/tests/run-make/crate-loading/dependency-1.rs similarity index 100% rename from tests/run-make/crate-loading/multiple-dep-versions-1.rs rename to tests/run-make/crate-loading/dependency-1.rs diff --git a/tests/run-make/crate-loading/multiple-dep-versions-2.rs b/tests/run-make/crate-loading/dependency-2.rs similarity index 100% rename from tests/run-make/crate-loading/multiple-dep-versions-2.rs rename to tests/run-make/crate-loading/dependency-2.rs diff --git a/tests/run-make/crate-loading/rmake.rs b/tests/run-make/crate-loading/rmake.rs index 6ad456e3e3e57..8f2577861239d 100644 --- a/tests/run-make/crate-loading/rmake.rs +++ b/tests/run-make/crate-loading/rmake.rs @@ -6,12 +6,9 @@ use run_make_support::{diff, rust_lib_name, rustc}; fn main() { - rustc().input("multiple-dep-versions-1.rs").run(); - rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run(); - rustc() - .input("multiple-dep-versions-3.rs") - .extern_("dependency", rust_lib_name("dependency2")) - .run(); + rustc().input("dependency-1.rs").run(); + rustc().input("dependency-2.rs").extra_filename("2").metadata("2").run(); + rustc().input("dep-2-reexport.rs").extern_("dependency", rust_lib_name("dependency2")).run(); let out = rustc() .input("multiple-dep-versions.rs") From 5e7d34636447a6ae155f4140e7e4cc1ab2b33944 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:01:13 +0200 Subject: [PATCH 0752/1900] Update books --- src/doc/book | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/book b/src/doc/book index 3e9dc46aa563c..33f1af40cc44d 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 3e9dc46aa563ca0c53ec826c41b05f10c5915925 +Subproject commit 33f1af40cc44dde7e3e892f7a508e6f427d2cbc6 diff --git a/src/doc/reference b/src/doc/reference index b3ce60628c6f5..cc7247d8dfaef 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit b3ce60628c6f55ab8ff3dba9f3d20203df1c0dee +Subproject commit cc7247d8dfaef4c39000bb12c55c32ba5b5ba976 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index dd26bc8e726dc..2c9b490d70e53 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit dd26bc8e726dc2e73534c8972d4dccd1bed7495f +Subproject commit 2c9b490d70e535cf166bf17feba59e594579843f From 88f954453f794e7eaa16a4cb78b63e0777e12c20 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 22 Sep 2025 20:45:37 +0200 Subject: [PATCH 0753/1900] compiler-team.md: remove obsolete membership level There used to be compiler team and compiler-contributor team, and those have since been collapsed into 1 team. --- src/doc/rustc-dev-guide/src/compiler-team.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 896d9e6f6d94c..8c0481f6ea8f5 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -134,12 +134,3 @@ Getting on the reviewer rotation is much appreciated as it lowers the review burden for all of us! However, if you don't have time to give people timely feedback on their PRs, it may be better that you don't get on the list. - -### Full team membership - -Full team membership is typically extended once someone made many -contributions to the Rust compiler over time, ideally (but not -necessarily) to multiple areas. Sometimes this might be implementing a -new feature, but it is also important — perhaps more important! — to -have time and willingness to help out with general upkeep such as -bugfixes, tracking regressions, and other less glamorous work. From 24144ff499852b0fab63cf0c93d4c8d8b2fad8ef Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 22 Sep 2025 21:02:37 +0200 Subject: [PATCH 0754/1900] make rust-analyzer settings use dedicated directory This avoids rust-analyzer having to wait for a build lock due to ./x running other commands (and the other way around). --- src/bootstrap/src/core/build_steps/setup.rs | 4 ++++ src/etc/rust_analyzer_eglot.el | 8 +++++-- src/etc/rust_analyzer_helix.toml | 14 ++++++------- src/etc/rust_analyzer_settings.json | 8 +++++-- src/etc/rust_analyzer_zed.json | 23 +++++++++++++++++---- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 9f9af1d9abe34..4b0080f1c80a4 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -587,6 +587,7 @@ Select which editor you would like to set up [default: None]: "; "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce", "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1", + "e260553b71e4773c30a63c4b23b42b279fc73e72f95b775c47b7b7c511c51595", ], EditorKind::Helix => &[ "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", @@ -594,6 +595,7 @@ Select which editor you would like to set up [default: None]: "; "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5", "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5", "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1", + "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6", ], EditorKind::Vim | EditorKind::VsCode => &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", @@ -610,6 +612,7 @@ Select which editor you would like to set up [default: None]: "; "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12", "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054", + "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47", ], EditorKind::Zed => &[ "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", @@ -617,6 +620,7 @@ Select which editor you would like to set up [default: None]: "; "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6", "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382", + "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f", ], } } diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index 3151cb1a6e755..e5abf67235a5d 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -6,6 +6,8 @@ :overrideCommand ["python3" "x.py" "check" + "--build-dir" + "build-rust-analyzer" "--json-output"]) :linkedProjects ["Cargo.toml" "compiler/rustc_codegen_cranelift/Cargo.toml" @@ -13,9 +15,9 @@ "library/Cargo.toml" "src/bootstrap/Cargo.toml" "src/tools/rust-analyzer/Cargo.toml"] - :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" + :rustfmt ( :overrideCommand ["build-rust-analyzer/host/rustfmt/bin/rustfmt" "--edition=2024"]) - :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + :procMacro ( :server "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv" :enable t) :cargo ( :buildScripts ( :enable t :invocationLocation "root" @@ -23,6 +25,8 @@ :overrideCommand ["python3" "x.py" "check" + "--build-dir" + "build-rust-analyzer" "--json-output" "--compile-time-deps"])] :sysrootSrc "./library" diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml index 8c1782a1abce3..e2de2a374cbed 100644 --- a/src/etc/rust_analyzer_helix.toml +++ b/src/etc/rust_analyzer_helix.toml @@ -1,10 +1,10 @@ # This config uses a separate build directory for rust-analyzer, # so that r-a's checks don't block user `x` commands and vice-verse. -# R-a's build directory is located in `build/rust-analyzer`. +# R-a's build directory is located in `build-rust-analyzer`. # # To build rustfmt and proc macro server for r-a run the following command: # ``` -# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build/rust-analyzer +# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build-rust-analyzer # ``` [language-server.rust-analyzer.config] @@ -26,17 +26,17 @@ overrideCommand = [ "check", "--json-output", "--build-dir", - "build/rust-analyzer", + "build-rust-analyzer", ] [language-server.rust-analyzer.config.rustfmt] overrideCommand = [ - "build/rust-analyzer/host/rustfmt/bin/rustfmt", + "build-rust-analyzer/host/rustfmt/bin/rustfmt", "--edition=2024" ] [language-server.rust-analyzer.config.procMacro] -server = "build/rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv" +server = "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv" enable = true [language-server.rust-analyzer.config.rustc] @@ -58,6 +58,6 @@ overrideCommand = [ "check", "--json-output", "--build-dir", - "build/rust-analyzer", - "--compile-time-deps" + "build-rust-analyzer", + "--compile-time-deps", ] diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index b31169857c5e1..f89e8a2df1878 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -5,6 +5,8 @@ "python3", "x.py", "check", + "--build-dir", + "build-rust-analyzer", "--json-output" ], "rust-analyzer.linkedProjects": [ @@ -16,10 +18,10 @@ "src/tools/rust-analyzer/Cargo.toml" ], "rust-analyzer.rustfmt.overrideCommand": [ - "${workspaceFolder}/build/host/rustfmt/bin/rustfmt", + "${workspaceFolder}/build-rust-analyzer/host/rustfmt/bin/rustfmt", "--edition=2024" ], - "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv", + "rust-analyzer.procMacro.server": "${workspaceFolder}/build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv", "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.buildScripts.enable": true, "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", @@ -27,6 +29,8 @@ "python3", "x.py", "check", + "--build-dir", + "build-rust-analyzer", "--json-output", "--compile-time-deps" ], diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json index 7eace92500e83..d98a082a9b8ad 100644 --- a/src/etc/rust_analyzer_zed.json +++ b/src/etc/rust_analyzer_zed.json @@ -7,7 +7,15 @@ "enable": true, "invocationLocation": "root", "invocationStrategy": "once", - "overrideCommand": ["python3", "x.py", "check", "--json-output", "--compile-time-deps"] + "overrideCommand": [ + "python3", + "x.py", + "check", + "--build-dir", + "build-rust-analyzer", + "--compile-time-deps", + "--json-output" + ] }, "extraEnv": { "RUSTC_BOOTSTRAP": "1" @@ -17,7 +25,14 @@ "check": { "invocationLocation": "root", "invocationStrategy": "once", - "overrideCommand": ["python3", "x.py", "check", "--json-output"] + "overrideCommand": [ + "python3", + "x.py", + "check", + "--json-output", + "--build-dir", + "build-rust-analyzer" + ] }, "linkedProjects": [ "Cargo.toml", @@ -29,14 +44,14 @@ ], "procMacro": { "enable": true, - "server": "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + "server": "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv" }, "rustc": { "source": "./Cargo.toml" }, "rustfmt": { "overrideCommand": [ - "build/host/rustfmt/bin/rustfmt", + "build-rust-analyzer/host/rustfmt/bin/rustfmt", "--edition=2024" ] }, From 2d18c886f5754258109fad2e1e0c3bcca0cc9fa5 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 22 Sep 2025 15:10:41 -0400 Subject: [PATCH 0755/1900] Fix a crash/mislex when more than one frontmatter closing possibility is considered --- compiler/rustc_lexer/src/lib.rs | 4 +++- tests/ui/frontmatter/unclosed-6.rs | 12 ++++++++++++ tests/ui/frontmatter/unclosed-6.stderr | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/ui/frontmatter/unclosed-6.rs create mode 100644 tests/ui/frontmatter/unclosed-6.stderr diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index c29ab569f4795..f6790f7ed1e96 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -599,14 +599,16 @@ impl Cursor<'_> { if potential_closing.is_none() { // a less fortunate recovery if all else fails which finds any dashes preceded by whitespace // on a standalone line. Might be wrong. + let mut base_index = 0; while let Some(closing) = rest.find("---") { let preceding_chars_start = rest[..closing].rfind("\n").map_or(0, |i| i + 1); if rest[preceding_chars_start..closing].chars().all(is_horizontal_whitespace) { // candidate found - potential_closing = Some(closing); + potential_closing = Some(closing + base_index); break; } else { rest = &rest[closing + 3..]; + base_index += closing + 3; } } } diff --git a/tests/ui/frontmatter/unclosed-6.rs b/tests/ui/frontmatter/unclosed-6.rs new file mode 100644 index 0000000000000..ea8d4702f6307 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-6.rs @@ -0,0 +1,12 @@ +--- +//~^ ERROR unclosed frontmatter +🦀--- + --- + +// This test checks the location of the --- recovered by the parser is not +// incorrectly tracked during the less fortunate recovery case and multiple +// candidates are found, as seen with #146847 + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/unclosed-6.stderr b/tests/ui/frontmatter/unclosed-6.stderr new file mode 100644 index 0000000000000..01a13e87268c7 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-6.stderr @@ -0,0 +1,19 @@ +error: unclosed frontmatter + --> $DIR/unclosed-6.rs:1:1 + | +LL | / --- +LL | | +LL | | 🦀--- +LL | | --- +... | +LL | | + | |_^ + | +note: frontmatter opening here was not closed + --> $DIR/unclosed-6.rs:1:1 + | +LL | --- + | ^^^ + +error: aborting due to 1 previous error + From 819f8b05b90adcf976d9b76e00fe3a9542781bf5 Mon Sep 17 00:00:00 2001 From: Peter Lyons Kehl Date: Mon, 22 Sep 2025 11:40:53 -0700 Subject: [PATCH 0756/1900] Mutex/RwLock/ReentrantLock::data_ptr to be const fn --- library/std/src/sync/nonpoison/mutex.rs | 2 +- library/std/src/sync/nonpoison/rwlock.rs | 2 +- library/std/src/sync/poison/mutex.rs | 2 +- library/std/src/sync/poison/rwlock.rs | 2 +- library/std/src/sync/reentrant_lock.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index 07430ce3a1393..eeecf5d710767 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -373,7 +373,7 @@ impl Mutex { /// or written through after the mutex is dropped. #[unstable(feature = "mutex_data_ptr", issue = "140368")] // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn data_ptr(&self) -> *mut T { + pub const fn data_ptr(&self) -> *mut T { self.data.get() } } diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index eb0aef99cc1e7..b2f26edc083e9 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -495,7 +495,7 @@ impl RwLock { /// or written through after the lock is dropped. #[unstable(feature = "rwlock_data_ptr", issue = "140368")] // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn data_ptr(&self) -> *mut T { + pub const fn data_ptr(&self) -> *mut T { self.data.get() } } diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 7e9d920d92f85..6fdb4f6799ee5 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -668,7 +668,7 @@ impl Mutex { /// are properly synchronized to avoid data races, and that it is not read /// or written through after the mutex is dropped. #[unstable(feature = "mutex_data_ptr", issue = "140368")] - pub fn data_ptr(&self) -> *mut T { + pub const fn data_ptr(&self) -> *mut T { self.data.get() } } diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 0a463f3f9c7e3..e3a72c73bf4ed 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -667,7 +667,7 @@ impl RwLock { /// are properly synchronized to avoid data races, and that it is not read /// or written through after the lock is dropped. #[unstable(feature = "rwlock_data_ptr", issue = "140368")] - pub fn data_ptr(&self) -> *mut T { + pub const fn data_ptr(&self) -> *mut T { self.data.get() } } diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 4140718560c65..f560b616dd922 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -355,7 +355,7 @@ impl ReentrantLock { /// properly synchronized to avoid data races, and that it is not read /// through after the lock is dropped. #[unstable(feature = "reentrant_lock_data_ptr", issue = "140368")] - pub fn data_ptr(&self) -> *const T { + pub const fn data_ptr(&self) -> *const T { &raw const self.data } From 82c4018619b8ec48807151ce1164c9649e998b4d Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 22 Sep 2025 11:20:52 -0500 Subject: [PATCH 0757/1900] fix ICE in rustdoc::invalid_html_tags --- src/librustdoc/passes/lint/html_tags.rs | 3 +- .../lints/invalid-html-tags-ice-146890.rs | 25 ++++++++++++ .../lints/invalid-html-tags-ice-146890.stderr | 38 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs create mode 100644 tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.stderr diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index da09117b1bba7..136ff258048bf 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -364,6 +364,7 @@ impl TagParser { } else { if !self.tag_name.is_empty() { self.in_attrs = true; + // range of the entire tag within dox let mut r = Range { start: range.start + start_pos, end: range.start + pos }; if c == '>' { // In case we have a tag without attribute, we can consider the span to @@ -381,7 +382,7 @@ impl TagParser { for (new_pos, c) in text[pos..].char_indices() { if !c.is_whitespace() { if c == '>' { - r.end = range.start + new_pos + 1; + r.end = range.start + pos + new_pos + 1; found = true; } else if c == '<' { self.handle_lt_in_tag(range.clone(), pos + new_pos, f); diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs new file mode 100644 index 0000000000000..d7efc201e7e39 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs @@ -0,0 +1,25 @@ +// this test ensures that bad HTML with multiline tags doesn't cause an ICE +// regression test for https://github.com/rust-lang/rust/issues/146890 +#[deny(rustdoc::invalid_html_tags)] + +/// +/// +/// +///
key +/// +/// value +/// +///
+pub fn foo() {} diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.stderr b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.stderr new file mode 100644 index 0000000000000..64a82b3a952aa --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.stderr @@ -0,0 +1,38 @@ +error: unopened HTML tag `TD` + --> $DIR/invalid-html-tags-ice-146890.rs:12:5 + | +LL | /// + | |_____^ + | +note: the lint level is defined here + --> $DIR/invalid-html-tags-ice-146890.rs:3:8 + | +LL | #[deny(rustdoc::invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unopened HTML tag `TD` + --> $DIR/invalid-html-tags-ice-146890.rs:18:5 + | +LL | /// + | |_____^ + +error: unclosed HTML tag `TH` + --> $DIR/invalid-html-tags-ice-146890.rs:9:5 + | +LL | /// $DIR/invalid-html-tags-ice-146890.rs:15:5 + | +LL | /// Date: Mon, 22 Sep 2025 22:02:24 +0200 Subject: [PATCH 0758/1900] add regression test for issue 146537 --- .../suggestions/non_copy_move_out_of_tuple.rs | 8 ++++++ .../non_copy_move_out_of_tuple.stderr | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/ui/suggestions/non_copy_move_out_of_tuple.rs create mode 100644 tests/ui/suggestions/non_copy_move_out_of_tuple.stderr diff --git a/tests/ui/suggestions/non_copy_move_out_of_tuple.rs b/tests/ui/suggestions/non_copy_move_out_of_tuple.rs new file mode 100644 index 0000000000000..baf15dba33a96 --- /dev/null +++ b/tests/ui/suggestions/non_copy_move_out_of_tuple.rs @@ -0,0 +1,8 @@ +// Regression test for #146537. + +struct NonCopy; +fn main() { + let tuple = &(NonCopy,); + let b: NonCopy; + (b,) = *tuple; //~ ERROR: cannot move out of `tuple.0` which is behind a shared reference [E0507] +} diff --git a/tests/ui/suggestions/non_copy_move_out_of_tuple.stderr b/tests/ui/suggestions/non_copy_move_out_of_tuple.stderr new file mode 100644 index 0000000000000..62f24324fcc17 --- /dev/null +++ b/tests/ui/suggestions/non_copy_move_out_of_tuple.stderr @@ -0,0 +1,26 @@ +error[E0507]: cannot move out of `tuple.0` which is behind a shared reference + --> $DIR/non_copy_move_out_of_tuple.rs:7:12 + | +LL | (b,) = *tuple; + | - ^^^^^^ + | | + | data moved here + | move occurs because the place has type `NonCopy`, which does not implement the `Copy` trait + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/non_copy_move_out_of_tuple.rs:3:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | (b,) = *tuple; + | - you could clone this value +help: consider removing the dereference here + | +LL - (b,) = *tuple; +LL + (b,) = tuple; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. From 389a502ade65e82d8a0c4d323118121c1c09c4f8 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 19 Sep 2025 17:16:05 -0700 Subject: [PATCH 0759/1900] Fix a dangling reference in `rustc_thread_pool` --- compiler/rustc_thread_pool/src/latch.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_thread_pool/src/latch.rs b/compiler/rustc_thread_pool/src/latch.rs index 18d654d9f7828..58dabaf35c005 100644 --- a/compiler/rustc_thread_pool/src/latch.rs +++ b/compiler/rustc_thread_pool/src/latch.rs @@ -388,13 +388,17 @@ impl Latch for CountLatch { #[inline] unsafe fn set(this: *const Self) { if unsafe { (*this).counter.fetch_sub(1, Ordering::SeqCst) == 1 } { - // NOTE: Once we call `set` on the internal `latch`, + // SAFETY: Once we call `set` on the internal `latch`, // the target may proceed and invalidate `this`! match unsafe { &(*this).kind } { CountLatchKind::Stealing { latch, registry, worker_index } => { let registry = Arc::clone(registry); + let worker_index = *worker_index; + // SAFETY: We don't use any references from `this` after this call. if unsafe { CoreLatch::set(latch) } { - registry.notify_worker_latch_is_set(*worker_index); + // We **must not** access any part of `this` anymore, which + // is why we read and shadowed these fields beforehand. + registry.notify_worker_latch_is_set(worker_index); } } CountLatchKind::Blocking { latch } => unsafe { LockLatch::set(latch) }, From 2ba71fba652720807a2c8cd5c35c230943ce06e9 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 15:59:28 +0000 Subject: [PATCH 0760/1900] Use ns versions of with_diagnostics queries --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f8dacf0fb863d..f231348d24a4b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -680,7 +680,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(s.id.into()).1, + db.field_types_with_diagnostics_ns(s.id.into()).1, source_map, ); } @@ -692,7 +692,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(u.id.into()).1, + db.field_types_with_diagnostics_ns(u.id.into()).1, source_map, ); } @@ -722,7 +722,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(v.into()).1, + db.field_types_with_diagnostics_ns(v.into()).1, source_map, ); expr_store_diagnostics(db, acc, source_map); @@ -738,7 +738,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).1, + db.type_for_type_alias_with_diagnostics_ns(type_alias.id).1, &source_map, ); acc.extend(def.diagnostics(db, style_lints)); @@ -913,13 +913,13 @@ impl Module { push_ty_diagnostics( db, acc, - db.impl_self_ty_with_diagnostics(impl_def.id).1, + db.impl_self_ty_with_diagnostics_ns(impl_def.id).1, &source_map, ); push_ty_diagnostics( db, acc, - db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1), + db.impl_trait_with_diagnostics_ns(impl_def.id).and_then(|it| it.1), &source_map, ); @@ -3646,7 +3646,7 @@ impl AssocItem { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).1, + db.type_for_type_alias_with_diagnostics_ns(type_alias.id).1, &db.type_alias_signature_with_source_map(type_alias.id).1, ); for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { @@ -3782,7 +3782,7 @@ impl GenericDef { push_ty_diagnostics( db, acc, - db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked( + db.const_param_ty_with_diagnostics_ns(ConstParamId::from_unchecked( TypeOrConstParamId { parent: def, local_id: param_id }, )) .1, From 9e8f6e82b1b76055edb33b1d8de1263344c50cc0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 17 Aug 2025 16:02:17 +0000 Subject: [PATCH 0761/1900] Make Field::ty return TypeNs --- .../crates/hir-ty/src/next_solver/mapping.rs | 2 +- .../rust-analyzer/crates/hir/src/display.rs | 8 ++++- src/tools/rust-analyzer/crates/hir/src/lib.rs | 33 ++++++++++--------- .../src/handlers/add_missing_match_arms.rs | 2 +- .../src/handlers/expand_rest_pattern.rs | 7 ++-- .../ide-completion/src/completions/record.rs | 12 +++++-- .../crates/ide/src/goto_type_definition.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 4 +-- .../crates/ide/src/hover/render.rs | 8 ++--- .../crates/ide/src/signature_help.rs | 2 +- .../crates/ide/src/view_memory_layout.rs | 2 +- 11 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index b24b996b0927c..5dcb1e62329d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1266,7 +1266,7 @@ pub fn convert_args_for_result<'db>( Substitution::from_iter(Interner, substs) } -pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { +pub fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { use crate::{Scalar, TyKind}; use chalk_ir::{FloatTy, IntTy, UintTy}; match ty.kind() { diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 833a9ef03065d..2bf9bb85e50dd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -24,7 +24,7 @@ use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, TyBuilder, - Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -437,6 +437,12 @@ impl HirDisplay for Type<'_> { } } +impl HirDisplay for TypeNs<'_> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + self.ty.hir_fmt(f) + } +} + impl HirDisplay for ExternCrateDecl { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f231348d24a4b..1cec01da6008e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -86,7 +86,9 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - ClauseKind, DbInterner, GenericArgs, infer::InferCtxt, mapping::ChalkToNextSolver, + ClauseKind, DbInterner, GenericArgs, + infer::InferCtxt, + mapping::{ChalkToNextSolver, convert_ty_for_result}, }, primitive::UintTy, traits::FnTrait, @@ -1346,19 +1348,12 @@ impl Field { u32::from(self.id.into_raw()) as usize } - /// Returns the type as in the signature of the struct (i.e., with - /// placeholder types for type parameters). Only use this in the context of - /// the field definition. - pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { + /// Returns the type as in the signature of the struct. Only use this in the + /// context of the field definition. + pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { let var_id = self.parent.into(); - let generic_def_id: GenericDefId = match self.parent { - VariantDef::Struct(it) => it.id.into(), - VariantDef::Union(it) => it.id.into(), - VariantDef::Variant(it) => it.id.lookup(db).parent.into(), - }; - let substs = TyBuilder::placeholder_subst(db, generic_def_id); - let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); - Type::new(db, var_id, ty) + let ty = db.field_types_ns(var_id)[self.id].skip_binder(); + TypeNs::new(db, var_id, ty) } // FIXME: Find better API to also handle const generics @@ -1388,9 +1383,8 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - let interner = DbInterner::new_with(db, None, None); db.layout_of_ty( - self.ty(db).ty.to_nextsolver(interner), + self.ty(db).ty, db.trait_environment(match hir_def::VariantId::from(self.parent) { hir_def::VariantId::EnumVariantId(id) => { GenericDefId::AdtId(id.lookup(db).parent.into()) @@ -5969,6 +5963,11 @@ impl<'db> TypeNs<'db> { TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() } } + pub fn to_type(&self, db: &'db dyn HirDatabase) -> Type<'db> { + let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block); + Type { env: self.env.clone(), ty: convert_ty_for_result(interner, self.ty), _pd: self._pd } + } + // FIXME: Find better API that also handles const generics pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { let args = GenericArgs::new_from_iter( @@ -5992,6 +5991,10 @@ impl<'db> TypeNs<'db> { let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) } + + pub fn is_bool(&self) -> bool { + matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool) + } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 4d3212c515f28..3910921fbe03a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -521,7 +521,7 @@ fn build_pat( hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::default(); let pats = fields.into_iter().map(|f| { - let name = name_generator.for_type(&f.ty(db), db, edition); + let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition); match name { Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(), None => make.wildcard_pat().into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index c80b78fd97056..1c0d330fc3793 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -145,8 +145,11 @@ fn expand_tuple_struct_rest_pattern( make.ident_pat( false, false, - match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition()) - { + match name_gen.for_type( + &f.ty(ctx.sema.db).to_type(ctx.sema.db), + ctx.sema.db, + ctx.edition(), + ) { Some(name) => make.name(&name), None => make.name(&format!("_{}", f.index())), }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 36f38a70db638..2f5abd18934fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -28,7 +28,11 @@ pub(crate) fn complete_record_pattern_fields( record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), + false => un + .fields(ctx.db) + .into_iter() + .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) + .collect(), true => return, } } @@ -56,7 +60,11 @@ pub(crate) fn complete_record_expr_fields( record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), + false => un + .fields(ctx.db) + .into_iter() + .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) + .collect(), true => return, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ffd144a827e34..ae208fe1b5615 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -88,7 +88,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db), + ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 03b9b3677511c..c4fb6d1a5b4b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -440,7 +440,7 @@ pub(crate) fn hover_for_definition( Definition::Local(it) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db)), + Definition::Field(field) => Some(field.ty(db).to_type(db)), Definition::TupleField(it) => Some(it.ty(db)), Definition::Function(it) => Some(it.ty(db)), Definition::Adt(it) => Some(it.ty(db)), @@ -602,7 +602,7 @@ fn goto_type_action_for_def( let ty = match def { Definition::Local(it) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db)), + Definition::Field(field) => Some(field.ty(db).to_type(db)), Definition::TupleField(field) => Some(field.ty(db)), Definition::Const(it) => Some(it.ty(db)), Definition::Static(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 65375ed8f78c0..c5d695ccec3a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -692,14 +692,14 @@ pub(super) fn definition( } let drop_info = match def { Definition::Field(field) => { - DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None } } Definition::Adt(Adt::Struct(strukt)) => { let struct_drop_glue = strukt.ty_placeholders(db).drop_glue(db); let mut fields_drop_glue = strukt .fields(db) .iter() - .map(|field| field.ty(db).drop_glue(db)) + .map(|field| field.ty(db).to_type(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); let has_dtor = match (fields_drop_glue, struct_drop_glue) { @@ -727,7 +727,7 @@ pub(super) fn definition( variant .fields(db) .iter() - .map(|field| field.ty(db).drop_glue(db)) + .map(|field| field.ty(db).to_type(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None) }) @@ -742,7 +742,7 @@ pub(super) fn definition( let fields_drop_glue = variant .fields(db) .iter() - .map(|field| field.ty(db).drop_glue(db)) + .map(|field| field.ty(db).to_type(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); DropInfo { drop_glue: fields_drop_glue, has_dtor: None } diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index f45d096ac1904..e74d997e97c52 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -526,7 +526,7 @@ fn signature_help_for_tuple_struct_pat( pat.syntax(), token, pat.fields(), - fields.into_iter().map(|it| it.ty(db)), + fields.into_iter().map(|it| it.ty(db).to_type(db)), display_target, )) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 950f3f6c64706..5457d57b3976e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -99,7 +99,7 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => it.ty(db), + Definition::Field(it) => salsa::attach(db, || it.ty(db).to_type(db)), Definition::Const(it) => it.ty(db), Definition::Static(it) => it.ty(db), _ => return None, From 263b2a5c277a26c3d686bbbe1c4a194ad601f341 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Thu, 11 Sep 2025 04:47:39 -0400 Subject: [PATCH 0762/1900] Add 'db to TraitEnvironment --- .../crates/hir-ty/src/autoderef.rs | 8 ++-- .../crates/hir-ty/src/consteval.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 21 +++++---- .../crates/hir-ty/src/diagnostics/expr.rs | 8 ++-- .../diagnostics/match_check/pat_analysis.rs | 4 +- .../crates/hir-ty/src/display.rs | 10 ++-- .../rust-analyzer/crates/hir-ty/src/drop.rs | 12 +++-- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/infer/coerce.rs | 8 ++-- .../crates/hir-ty/src/infer/unify.rs | 10 ++-- .../crates/hir-ty/src/inhabitedness.rs | 6 +-- .../rust-analyzer/crates/hir-ty/src/layout.rs | 6 +-- .../crates/hir-ty/src/layout/adt.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 6 +-- .../rust-analyzer/crates/hir-ty/src/lower.rs | 8 ++-- .../crates/hir-ty/src/method_resolution.rs | 46 +++++++++---------- .../crates/hir-ty/src/mir/eval.rs | 14 +++--- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/mir/monomorphization.rs | 20 ++++---- .../rust-analyzer/crates/hir-ty/src/traits.rs | 20 +++++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 10 ++-- .../crates/hir/src/source_analyzer.rs | 2 +- 22 files changed, 122 insertions(+), 109 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index fd60ffcf24b0a..4c79c800fcf3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -32,11 +32,11 @@ const AUTODEREF_RECURSION_LIMIT: usize = 20; /// - the yielded types don't contain inference variables (but may contain `TyKind::Error`). /// - a type won't be yielded more than once; in other words, the returned iterator will stop if it /// detects a cycle in the deref chain. -pub fn autoderef( - db: &dyn HirDatabase, - env: Arc, +pub fn autoderef<'db>( + db: &'db dyn HirDatabase, + env: Arc>, ty: crate::Canonical, -) -> impl Iterator { +) -> impl Iterator + use<> { let mut table = InferenceTable::new(db, env); let interner = table.interner; let ty = table.instantiate_canonical(ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 0f2cc17f563dd..e2a8d1cedc284 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -229,7 +229,7 @@ pub(crate) fn const_eval_cycle_result( _: &dyn HirDatabase, _: GeneralConstId, _: Substitution, - _: Option>, + _: Option>>, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } @@ -252,7 +252,7 @@ pub(crate) fn const_eval_query( db: &dyn HirDatabase, def: GeneralConstId, subst: Substitution, - trait_env: Option>, + trait_env: Option>>, ) -> Result { let body = match def { GeneralConstId::ConstId(c) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 448fc4aede037..24e617135160d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -49,7 +49,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &self, def: DefWithBodyId, subst: Substitution, - env: Arc, + env: Arc>, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] @@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &self, def: InternedClosureId, subst: Substitution, - env: Arc, + env: Arc>, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::borrowck_query)] @@ -70,7 +70,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &self, def: GeneralConstId, subst: Substitution, - trait_env: Option>, + trait_env: Option>>, ) -> Result; #[salsa::invoke(crate::consteval::const_eval_static_query)] @@ -84,7 +84,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, - env: Arc, + env: Arc>, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution); @@ -97,7 +97,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &'db self, def: AdtId, args: crate::next_solver::GenericArgs<'db>, - trait_env: Arc, + trait_env: Arc>, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] @@ -105,7 +105,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn layout_of_ty<'db>( &'db self, ty: crate::next_solver::Ty<'db>, - env: Arc, + env: Arc>, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] @@ -184,10 +184,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] - fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; + fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId) + -> Arc>; #[salsa::invoke(crate::lower::trait_environment_query)] - fn trait_environment(&self, def: GenericDefId) -> Arc; + fn trait_environment<'db>(&'db self, def: GenericDefId) -> Arc>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)] @@ -258,7 +259,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn normalize_projection( &self, projection: crate::ProjectionTy, - env: Arc, + env: Arc>, ) -> Ty; #[salsa::invoke(crate::traits::trait_solve_query)] @@ -272,7 +273,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::drop::has_drop_glue)] #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] - fn has_drop_glue(&self, ty: Ty, env: Arc) -> DropGlue; + fn has_drop_glue(&self, ty: Ty, env: Arc>) -> DropGlue; // next trait solver diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 403ea05a4f53c..d05814e0e7e40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -81,17 +81,17 @@ impl BodyValidationDiagnostic { } } -struct ExprValidator { +struct ExprValidator<'db> { owner: DefWithBodyId, body: Arc, infer: Arc, - env: Arc, + env: Arc>, diagnostics: Vec, validate_lints: bool, } -impl ExprValidator { - fn validate_body(&mut self, db: &dyn HirDatabase) { +impl<'db> ExprValidator<'db> { + fn validate_body(&mut self, db: &'db dyn HirDatabase) { let mut filter_map_next_checker = None; // we'll pass &mut self while iterating over body.exprs, so they need to be disjoint let body = Arc::clone(&self.body); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 56fd12e1f2b47..eb20d3c51ff41 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -70,7 +70,7 @@ pub(crate) struct MatchCheckCtx<'db> { body: DefWithBodyId, pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, - env: Arc, + env: Arc>, } impl<'db> MatchCheckCtx<'db> { @@ -78,7 +78,7 @@ impl<'db> MatchCheckCtx<'db> { module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, ) -> Self { let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 519e4b59237f4..02f3eb39a4e0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -799,12 +799,12 @@ fn render_const_scalar_ns( render_const_scalar_inner(f, b, memory_map, ty, trait_env) } -fn render_const_scalar_inner( +fn render_const_scalar_inner<'db>( f: &mut HirFormatter<'_>, b: &[u8], memory_map: &MemoryMap<'_>, - ty: crate::next_solver::Ty<'_>, - trait_env: Arc, + ty: crate::next_solver::Ty<'db>, + trait_env: Arc>, ) -> Result<(), HirDisplayError> { use rustc_type_ir::TyKind; match ty.kind() { @@ -1068,11 +1068,11 @@ fn render_const_scalar_inner( } } -fn render_variant_after_name( +fn render_variant_after_name<'db>( data: &VariantFields, f: &mut HirFormatter<'_>, field_types: &ArenaMap>, - trait_env: Arc, + trait_env: Arc>, layout: &Layout, args: GenericArgs<'_>, b: &[u8], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index f5c2f41069ea0..88a7d15d8ea7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -43,7 +43,11 @@ pub enum DropGlue { HasDropGlue, } -pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc) -> DropGlue { +pub(crate) fn has_drop_glue( + db: &dyn HirDatabase, + ty: Ty, + env: Arc>, +) -> DropGlue { match ty.kind(Interner) { TyKind::Adt(adt, subst) => { if has_destructor(db, adt.0) { @@ -165,7 +169,7 @@ pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc, + env: Arc>, projection: ProjectionTy, ty: Ty, ) -> DropGlue { @@ -178,7 +182,7 @@ fn projection_has_drop_glue( } } -fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc) -> bool { +fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc>) -> bool { let Some(copy_trait) = LangItem::Copy.resolve_trait(db, env.krate) else { return false; }; @@ -193,7 +197,7 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc) -> bool { pub(crate) fn has_drop_glue_cycle_result( _db: &dyn HirDatabase, _ty: Ty, - _env: Arc, + _env: Arc>, ) -> DropGlue { DropGlue::None } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 017119781a709..3ece62ec35b63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -152,7 +152,7 @@ pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc, ty: Ty) -> Ty { +pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc>, ty: Ty) -> Ty { // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only // works for the type case, so we check array unconditionally. Remove the array part // when the bug in chalk becomes fixed. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 7930d8b0ed68f..42948ccb93063 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1564,9 +1564,9 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { } } -pub fn could_coerce( - db: &dyn HirDatabase, - env: Arc, +pub fn could_coerce<'db>( + db: &'db dyn HirDatabase, + env: Arc>, tys: &crate::Canonical<(crate::Ty, crate::Ty)>, ) -> bool { coerce(db, env, tys).is_ok() @@ -1574,7 +1574,7 @@ pub fn could_coerce( fn coerce<'db>( db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, tys: &crate::Canonical<(crate::Ty, crate::Ty)>, ) -> Result<(Vec, crate::Ty), TypeError>> { let mut table = InferenceTable::new(db, env); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index cee95d3880df9..1204fb06d2fe2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -121,7 +121,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { /// unresolved goal `T = U`. pub fn could_unify( db: &dyn HirDatabase, - env: Arc, + env: Arc>, tys: &Canonical<(Ty, Ty)>, ) -> bool { unify(db, env, tys).is_some() @@ -133,7 +133,7 @@ pub fn could_unify( /// them. For example `Option` and `Option` do not unify as we cannot show that `T = U` pub fn could_unify_deeply( db: &dyn HirDatabase, - env: Arc, + env: Arc>, tys: &Canonical<(Ty, Ty)>, ) -> bool { let mut table = InferenceTable::new(db, env); @@ -151,7 +151,7 @@ pub fn could_unify_deeply( pub(crate) fn unify( db: &dyn HirDatabase, - env: Arc, + env: Arc>, tys: &Canonical<(Ty, Ty)>, ) -> Option { let mut table = InferenceTable::new(db, env); @@ -212,7 +212,7 @@ bitflags::bitflags! { pub(crate) struct InferenceTable<'db> { pub(crate) db: &'db dyn HirDatabase, pub(crate) interner: DbInterner<'db>, - pub(crate) trait_env: Arc, + pub(crate) trait_env: Arc>, pub(crate) param_env: ParamEnv<'db>, pub(crate) tait_coercion_table: Option>, pub(crate) infer_ctxt: InferCtxt<'db>, @@ -227,7 +227,7 @@ pub(crate) struct InferenceTableSnapshot<'db> { } impl<'db> InferenceTable<'db> { - pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc) -> Self { + pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc>) -> Self { let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index b16b6a1178460..bdebe41b29989 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -20,7 +20,7 @@ pub(crate) fn is_ty_uninhabited_from( db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId, - env: Arc, + env: Arc>, ) -> bool { let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered(); let mut uninhabited_from = @@ -36,7 +36,7 @@ pub(crate) fn is_enum_variant_uninhabited_from( variant: EnumVariantId, subst: &Substitution, target_mod: ModuleId, - env: Arc, + env: Arc>, ) -> bool { let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered(); @@ -52,7 +52,7 @@ struct UninhabitedFrom<'a> { // guard for preventing stack overflow in non trivial non terminating types max_depth: usize, db: &'a dyn HirDatabase, - env: Arc, + env: Arc>, } const CONTINUE_OPAQUELY_INHABITED: ControlFlow = Continue(()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index f21673c732e40..4071b9a1d5e9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -132,7 +132,7 @@ fn layout_of_simd_ty<'db>( id: StructId, repr_packed: bool, args: &GenericArgs<'db>, - env: Arc, + env: Arc>, dl: &TargetDataLayout, ) -> Result, LayoutError> { // Supported SIMD vectors are homogeneous ADTs with exactly one array field: @@ -160,7 +160,7 @@ fn layout_of_simd_ty<'db>( pub fn layout_of_ty_query<'db>( db: &'db dyn HirDatabase, ty: Ty<'db>, - trait_env: Arc, + trait_env: Arc>, ) -> Result, LayoutError> { let krate = trait_env.krate; let interner = DbInterner::new_with(db, Some(krate), trait_env.block); @@ -371,7 +371,7 @@ pub fn layout_of_ty_query<'db>( pub(crate) fn layout_of_ty_cycle_result<'db>( _: &dyn HirDatabase, _: Ty<'db>, - _: Arc, + _: Arc>, ) -> Result, LayoutError> { Err(LayoutError::RecursiveTypeWithoutIndirection) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 9a746ca888589..a8f04bf8c132e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -23,7 +23,7 @@ pub fn layout_of_adt_query<'db>( db: &'db dyn HirDatabase, def: AdtId, args: GenericArgs<'db>, - trait_env: Arc, + trait_env: Arc>, ) -> Result, LayoutError> { let krate = trait_env.krate; let Ok(target) = db.target_data_layout(krate) else { @@ -99,7 +99,7 @@ pub(crate) fn layout_of_adt_cycle_result<'db>( _: &'db dyn HirDatabase, _def: AdtId, _args: GenericArgs<'db>, - _trait_env: Arc, + _trait_env: Arc>, ) -> Result, LayoutError> { Err(LayoutError::RecursiveTypeWithoutIndirection) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2accf48843442..05c5a7a28b9a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -940,10 +940,10 @@ where Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) } } -pub fn callable_sig_from_fn_trait( +pub fn callable_sig_from_fn_trait<'db>( self_ty: &Ty, - trait_env: Arc, - db: &dyn HirDatabase, + trait_env: Arc>, + db: &'db dyn HirDatabase, ) -> Option<(FnTrait, CallableSig)> { let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 4d5172fd4f24f..eeec9d0454d3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1089,7 +1089,7 @@ pub(crate) fn generic_predicates_for_param_cycle_result( pub(crate) fn trait_environment_for_body_query( db: &dyn HirDatabase, def: DefWithBodyId, -) -> Arc { +) -> Arc> { let Some(def) = def.as_generic_def_id(db) else { let krate = def.module(db).krate(); return TraitEnvironment::empty(krate); @@ -1097,10 +1097,10 @@ pub(crate) fn trait_environment_for_body_query( db.trait_environment(def) } -pub(crate) fn trait_environment_query( - db: &dyn HirDatabase, +pub(crate) fn trait_environment_query<'db>( + db: &'db dyn HirDatabase, def: GenericDefId, -) -> Arc { +) -> Arc> { let generics = generics(db, def); if generics.has_no_predicates() && generics.is_empty() { return TraitEnvironment::empty(def.krate(db)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 5cd4879a54046..db74ca258732c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -545,7 +545,7 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option( db: &'db dyn HirDatabase, ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, - env: Arc, + env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: &Name, @@ -714,7 +714,7 @@ impl ReceiverAdjustments { pub(crate) fn iterate_method_candidates<'db, T>( ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -742,9 +742,9 @@ pub(crate) fn iterate_method_candidates<'db, T>( slot } -pub fn lookup_impl_const( - db: &dyn HirDatabase, - env: Arc, +pub fn lookup_impl_const<'db>( + db: &'db dyn HirDatabase, + env: Arc>, const_id: ConstId, subs: Substitution, ) -> (ConstId, Substitution) { @@ -770,9 +770,9 @@ pub fn lookup_impl_const( /// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should /// call the method using the vtable. -pub fn is_dyn_method( - db: &dyn HirDatabase, - _env: Arc, +pub fn is_dyn_method<'db>( + db: &'db dyn HirDatabase, + _env: Arc>, func: FunctionId, fn_subst: Substitution, ) -> Option { @@ -812,9 +812,9 @@ pub fn is_dyn_method( /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. -pub(crate) fn lookup_impl_method_query( - db: &dyn HirDatabase, - env: Arc, +pub(crate) fn lookup_impl_method_query<'db>( + db: &'db dyn HirDatabase, + env: Arc>, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution) { @@ -845,10 +845,10 @@ pub(crate) fn lookup_impl_method_query( ) } -fn lookup_impl_assoc_item_for_trait_ref( +fn lookup_impl_assoc_item_for_trait_ref<'db>( trait_ref: TraitRef, - db: &dyn HirDatabase, - env: Arc, + db: &'db dyn HirDatabase, + env: Arc>, name: &Name, ) -> Option<(AssocItemId, Substitution)> { let hir_trait_id = trait_ref.hir_trait_id(); @@ -1067,7 +1067,7 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { pub fn iterate_path_candidates<'db>( ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -1089,7 +1089,7 @@ pub fn iterate_path_candidates<'db>( pub fn iterate_method_candidates_dyn<'db>( ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -1351,7 +1351,7 @@ fn iterate_method_candidates_by_receiver<'db>( fn iterate_method_candidates_for_self_ty<'db>( self_ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, - env: Arc, + env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -1856,10 +1856,10 @@ fn is_valid_impl_fn_candidate( }) } -pub fn implements_trait_unique( +pub fn implements_trait_unique<'db>( ty: &Canonical, - db: &dyn HirDatabase, - env: &TraitEnvironment, + db: &'db dyn HirDatabase, + env: &TraitEnvironment<'db>, trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); @@ -1869,9 +1869,9 @@ pub fn implements_trait_unique( /// This creates Substs for a trait with the given Self type and type variables /// for all other parameters, to query next solver with it. #[tracing::instrument(skip_all)] -fn generic_implements_goal( - db: &dyn HirDatabase, - env: &TraitEnvironment, +fn generic_implements_goal<'db>( + db: &'db dyn HirDatabase, + env: &TraitEnvironment<'db>, trait_: TraitId, self_ty: &Canonical, ) -> Canonical> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 3e658cb93ed8a..3d7ad8d3b0693 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -165,7 +165,7 @@ enum MirOrDynIndex { pub struct Evaluator<'a> { db: &'a dyn HirDatabase, - trait_env: Arc, + trait_env: Arc>, target_data_layout: Arc, stack: Vec, heap: Vec, @@ -582,8 +582,8 @@ impl MirOutput { } } -pub fn interpret_mir( - db: &dyn HirDatabase, +pub fn interpret_mir<'db>( + db: &'db dyn HirDatabase, body: Arc, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which @@ -591,7 +591,7 @@ pub fn interpret_mir( // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, - trait_env: Option>, + trait_env: Option>>, ) -> Result<(Result, MirOutput)> { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; @@ -632,11 +632,11 @@ const EXECUTION_LIMIT: usize = 10_000_000; impl<'db> Evaluator<'db> { pub fn new( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, - trait_env: Option>, - ) -> Result> { + trait_env: Option>>, + ) -> Result> { let crate_id = owner.module(db).krate(); let target_data_layout = match db.target_data_layout(crate_id) { Ok(target_data_layout) => target_data_layout, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 50e416a66a64b..0416f4315d4dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -82,7 +82,7 @@ struct MirLowerCtx<'db> { infer: &'db InferenceResult, resolver: Resolver<'db>, drop_scopes: Vec, - env: Arc, + env: Arc>, } // FIXME: Make this smaller, its stored in database queries diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 555b87850924c..f293f38c76980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -35,7 +35,7 @@ macro_rules! not_supported { struct Filler<'a> { db: &'a dyn HirDatabase, - trait_env: Arc, + trait_env: Arc>, subst: &'a Substitution, generics: Option, } @@ -301,11 +301,11 @@ impl Filler<'_> { } } -pub fn monomorphized_mir_body_query( - db: &dyn HirDatabase, +pub fn monomorphized_mir_body_query<'db>( + db: &'db dyn HirDatabase, owner: DefWithBodyId, subst: Substitution, - trait_env: Arc, + trait_env: Arc>, ) -> Result, MirLowerError> { let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); let filler = &mut Filler { db, subst: &subst, trait_env, generics }; @@ -315,20 +315,20 @@ pub fn monomorphized_mir_body_query( Ok(Arc::new(body)) } -pub(crate) fn monomorphized_mir_body_cycle_result( - _db: &dyn HirDatabase, +pub(crate) fn monomorphized_mir_body_cycle_result<'db>( + _db: &'db dyn HirDatabase, _: DefWithBodyId, _: Substitution, - _: Arc, + _: Arc>, ) -> Result, MirLowerError> { Err(MirLowerError::Loop) } -pub fn monomorphized_mir_body_for_closure_query( - db: &dyn HirDatabase, +pub fn monomorphized_mir_body_for_closure_query<'db>( + db: &'db dyn HirDatabase, closure: InternedClosureId, subst: Substitution, - trait_env: Arc, + trait_env: Arc>, ) -> Result, MirLowerError> { let InternedClosure(owner, _) = db.lookup_intern_closure(closure); let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 3ce78e83c1294..d6a86c13978b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -39,21 +39,23 @@ use crate::{ /// ``` /// we assume that `T: Default`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TraitEnvironment { +pub struct TraitEnvironment<'db> { pub krate: Crate, pub block: Option, // FIXME make this a BTreeMap traits_from_clauses: Box<[(Ty, TraitId)]>, pub env: chalk_ir::Environment, + _db: std::marker::PhantomData<&'db ()>, } -impl TraitEnvironment { +impl<'db> TraitEnvironment<'db> { pub fn empty(krate: Crate) -> Arc { Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: Box::default(), env: chalk_ir::Environment::new(Interner), + _db: std::marker::PhantomData, }) } @@ -63,7 +65,13 @@ impl TraitEnvironment { traits_from_clauses: Box<[(Ty, TraitId)]>, env: chalk_ir::Environment, ) -> Arc { - Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) + Arc::new(TraitEnvironment { + krate, + block, + traits_from_clauses, + env, + _db: std::marker::PhantomData, + }) } // pub fn with_block(self: &mut Arc, block: BlockId) { @@ -78,10 +86,10 @@ impl TraitEnvironment { } } -pub(crate) fn normalize_projection_query( - db: &dyn HirDatabase, +pub(crate) fn normalize_projection_query<'db>( + db: &'db dyn HirDatabase, projection: ProjectionTy, - env: Arc, + env: Arc>, ) -> Ty { if projection.substitution.iter(Interner).any(|arg| { arg.ty(Interner) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1cec01da6008e..eaae15835d0b5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3808,12 +3808,12 @@ impl GenericDef { pub struct GenericSubstitution<'db> { def: GenericDefId, subst: Substitution, - env: Arc, + env: Arc>, _pd: PhantomCovariantLifetime<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: Substitution, env: Arc) -> Self { + fn new(def: GenericDefId, subst: Substitution, env: Arc>) -> Self { Self { def, subst, env, _pd: PhantomCovariantLifetime::new() } } @@ -4567,7 +4567,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - env: Arc, + env: Arc>, trait_ref: hir_ty::next_solver::TraitRef<'db>, _pd: PhantomCovariantLifetime<'db>, } @@ -4790,7 +4790,7 @@ impl CaptureUsageSource { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct Type<'db> { - env: Arc, + env: Arc>, ty: Ty, _pd: PhantomCovariantLifetime<'db>, } @@ -5945,7 +5945,7 @@ impl<'db> Type<'db> { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TypeNs<'db> { - env: Arc, + env: Arc>, ty: hir_ty::next_solver::Ty<'db>, _pd: PhantomCovariantLifetime<'db>, } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 539b25387aef0..a19183fa1302a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -219,7 +219,7 @@ impl<'db> SourceAnalyzer<'db> { }) } - fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc { + fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc> { self.body_().map(|(def, ..)| def).map_or_else( || TraitEnvironment::empty(self.resolver.krate()), |def| db.trait_environment_for_body(def), From 06078dfa9782115e83fb2a02bec9253e927aeee3 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:38:31 -0400 Subject: [PATCH 0763/1900] Use ParamEnv in TraitEnvironment --- .../crates/hir-ty/src/autoderef.rs | 2 +- .../crates/hir-ty/src/chalk_ext.rs | 14 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 4 +- .../crates/hir-ty/src/display.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 7 +- .../crates/hir-ty/src/infer/closure.rs | 6 +- .../crates/hir-ty/src/infer/coerce.rs | 20 +-- .../crates/hir-ty/src/infer/expr.rs | 5 +- .../crates/hir-ty/src/infer/unify.rs | 18 ++- .../rust-analyzer/crates/hir-ty/src/lower.rs | 111 +-------------- .../crates/hir-ty/src/lower_nextsolver.rs | 130 +++++++++++++++++- .../crates/hir-ty/src/method_resolution.rs | 21 ++- .../crates/hir-ty/src/tests/regression.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 18 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +- .../crates/ide-completion/src/tests.rs | 8 +- .../crates/ide/src/view_memory_layout.rs | 4 +- 17 files changed, 216 insertions(+), 170 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 4c79c800fcf3b..21a86d3e43727 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -298,7 +298,7 @@ fn structurally_normalize_ty<'db>( ) -> Option<(Ty<'db>, PredicateObligations<'db>)> { let mut ocx = ObligationCtxt::new(&table.infer_ctxt); let Ok(normalized_ty) = - ocx.structurally_normalize_ty(&ObligationCause::misc(), table.param_env, ty) + ocx.structurally_normalize_ty(&ObligationCause::misc(), table.trait_env.env, ty) else { // We shouldn't have errors here in the old solver, except for // evaluate/fulfill mismatches, but that's not a reason for an ICE. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 1faf9f66dc547..6956a0a123283 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -15,8 +15,13 @@ use crate::{ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, - WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, + WhereClause, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, + generics::generics, + next_solver::{DbInterner, mapping::NextSolverToChalk}, + to_chalk_trait_id, + utils::ClosureSubst, }; pub trait TyExt { @@ -372,7 +377,10 @@ impl TyExt for Ty { let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build(); let env = db.trait_environment_for_body(owner); let goal = Canonical { - value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), + value: InEnvironment::new( + &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)), + trait_ref.cast(Interner), + ), binders: CanonicalVarKinds::empty(Interner), }; !db.trait_solve(crate_id, None, goal).no_solution() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 24e617135160d..44f48069ab240 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -182,12 +182,12 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::trait_environment_for_body_query)] + #[salsa::invoke(crate::lower_nextsolver::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId) -> Arc>; - #[salsa::invoke(crate::lower::trait_environment_query)] + #[salsa::invoke(crate::lower_nextsolver::trait_environment_query)] fn trait_environment<'db>(&'db self, def: GenericDefId) -> Arc>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 02f3eb39a4e0f..dc42304e1cab2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -792,10 +792,7 @@ fn render_const_scalar_ns( let trait_env = TraitEnvironment::empty(f.krate()); let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); let infcx = interner.infer_ctxt().build(rustc_type_ir::TypingMode::PostAnalysis); - let ty = infcx - .at(&ObligationCause::new(), trait_env.env.to_nextsolver(interner)) - .deeply_normalize(ty) - .unwrap_or(ty); + let ty = infcx.at(&ObligationCause::new(), trait_env.env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, trait_env) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 88a7d15d8ea7d..413f70532a548 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -7,6 +7,8 @@ use hir_def::signatures::StructFlags; use stdx::never; use triomphe::Arc; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment, Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase, @@ -188,7 +190,10 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc>) -> bool }; let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build(); let goal = Canonical { - value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), + value: InEnvironment::new( + &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)), + trait_ref.cast(Interner), + ), binders: CanonicalVarKinds::empty(Interner), }; db.trait_solve(env.krate, env.block, goal).certain() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 1d5d8dd13edd4..4a57b2f37510e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -318,7 +318,7 @@ impl<'db> InferenceContext<'db> { _ = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(), self.table.trait_env.env) .eq(DefineOpaqueTypes::Yes, inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); @@ -703,7 +703,7 @@ impl<'db> InferenceContext<'db> { let cause = ObligationCause::new(); let InferOk { value: (), obligations } = table .infer_ctxt - .at(&cause, table.param_env) + .at(&cause, table.trait_env.env) .eq(DefineOpaqueTypes::Yes, expected_ty, supplied_ty)?; all_obligations.extend(obligations); } @@ -711,7 +711,7 @@ impl<'db> InferenceContext<'db> { let supplied_output_ty = supplied_sig.output(); let cause = ObligationCause::new(); let InferOk { value: (), obligations } = - table.infer_ctxt.at(&cause, table.param_env).eq( + table.infer_ctxt.at(&cause, table.trait_env.env).eq( DefineOpaqueTypes::Yes, expected_sigs.liberated_sig.output(), supplied_output_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 42948ccb93063..219b519e46bb8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -144,7 +144,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> { debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); self.commit_if_ok(|this| { - let at = this.infer_ctxt().at(&this.cause, this.table.param_env); + let at = this.infer_ctxt().at(&this.cause, this.table.trait_env.env); let res = if this.use_lub { at.lub(b, a) @@ -330,7 +330,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { obligations.push(Obligation::new( self.interner(), self.cause.clone(), - self.table.param_env, + self.table.trait_env.env, Binder::dummy(PredicateKind::Coerce(CoercePredicate { a: source_ty, b: target_ty, @@ -718,7 +718,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new( self.interner(), cause, - self.table.param_env, + self.table.trait_env.env, TraitRef::new( self.interner(), coerce_unsized_did.into(), @@ -1114,8 +1114,12 @@ impl<'db> InferenceContext<'db> { match self.table.commit_if_ok(|table| { // We need to eagerly handle nested obligations due to lazy norm. let mut ocx = ObligationCtxt::new(&table.infer_ctxt); - let value = - ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; + let value = ocx.lub( + &ObligationCause::new(), + table.trait_env.env, + prev_ty, + new_ty, + )?; if ocx.select_where_possible().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { @@ -1158,7 +1162,7 @@ impl<'db> InferenceContext<'db> { let sig = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(), self.table.trait_env.env) .lub(a_sig, b_sig) .map(|ok| self.table.register_infer_ok(ok))?; @@ -1248,7 +1252,7 @@ impl<'db> InferenceContext<'db> { .commit_if_ok(|table| { table .infer_ctxt - .at(&ObligationCause::new(), table.param_env) + .at(&ObligationCause::new(), table.trait_env.env) .lub(prev_ty, new_ty) }) .unwrap_err()) @@ -1498,7 +1502,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { assert!(expression_ty.is_unit(), "if let hack without unit type"); icx.table .infer_ctxt - .at(cause, icx.table.param_env) + .at(cause, icx.table.trait_env.env) .eq( // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs DefineOpaqueTypes::Yes, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index c5a51dfc4cf92..b4a332f1da849 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1662,7 +1662,6 @@ impl<'db> InferenceContext<'db> { }); self.resolver.reset_to_guard(g); if let Some(prev_env) = prev_env { - self.table.param_env = prev_env.env.to_nextsolver(self.table.interner); self.table.trait_env = prev_env; } @@ -2132,7 +2131,7 @@ impl<'db> InferenceContext<'db> { let origin = ObligationCause::new(); ocx.sup( &origin, - self.table.param_env, + self.table.trait_env.env, expected_output.to_nextsolver(interner), formal_output, )?; @@ -2239,7 +2238,7 @@ impl<'db> InferenceContext<'db> { let formal_ty_error = this .table .infer_ctxt - .at(&ObligationCause::new(), this.table.param_env) + .at(&ObligationCause::new(), this.table.trait_env.env) .eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty); // If neither check failed, the types are compatible diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 1204fb06d2fe2..6df9cbaa29394 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -29,8 +29,8 @@ use crate::{ db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{ - self, ClauseKind, DbInterner, ErrorGuaranteed, ParamEnv, Predicate, PredicateKind, - SolverDefIds, Term, TraitRef, + self, ClauseKind, DbInterner, ErrorGuaranteed, Predicate, PredicateKind, SolverDefIds, + Term, TraitRef, fulfill::FulfillmentCtxt, infer::{ DbInternerInferExt, InferCtxt, InferOk, @@ -213,7 +213,6 @@ pub(crate) struct InferenceTable<'db> { pub(crate) db: &'db dyn HirDatabase, pub(crate) interner: DbInterner<'db>, pub(crate) trait_env: Arc>, - pub(crate) param_env: ParamEnv<'db>, pub(crate) tait_coercion_table: Option>, pub(crate) infer_ctxt: InferCtxt<'db>, diverging_tys: FxHashSet, @@ -235,7 +234,6 @@ impl<'db> InferenceTable<'db> { InferenceTable { db, interner, - param_env: trait_env.env.to_nextsolver(interner), trait_env, tait_coercion_table: None, fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), @@ -426,7 +424,7 @@ impl<'db> InferenceTable<'db> { { let ty = self.resolve_vars_with_obligations(ty); self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) + .at(&ObligationCause::new(), self.trait_env.env) .deeply_normalize(ty.clone()) .unwrap_or(ty) } @@ -741,7 +739,7 @@ impl<'db> InferenceTable<'db> { ) -> InferResult<'db, ()> { let variance = rustc_type_ir::Variance::Invariant; let span = crate::next_solver::Span::dummy(); - match self.infer_ctxt.relate(self.param_env, lhs, variance, rhs, span) { + match self.infer_ctxt.relate(self.trait_env.env, lhs, variance, rhs, span) { Ok(goals) => Ok(crate::infer::InferOk { goals, value: () }), Err(_) => Err(TypeError), } @@ -798,7 +796,7 @@ impl<'db> InferenceTable<'db> { fn structurally_normalize_term(&mut self, term: Term<'db>) -> Term<'db> { self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) + .at(&ObligationCause::new(), self.trait_env.env) .structurally_normalize_term(term, &mut self.fulfillment_cx) .unwrap_or(term) } @@ -818,7 +816,7 @@ impl<'db> InferenceTable<'db> { // in a reentrant borrow, causing an ICE. let result = self .infer_ctxt - .at(&ObligationCause::misc(), self.param_env) + .at(&ObligationCause::misc(), self.trait_env.env) .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, @@ -874,14 +872,14 @@ impl<'db> InferenceTable<'db> { /// choice (during e.g. method resolution or deref). #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { - let goal = next_solver::Goal { param_env: self.param_env, predicate }; + let goal = next_solver::Goal { param_env: self.trait_env.env, predicate }; let canonicalized = self.canonicalize(goal); next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) } pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = next_solver::Goal { param_env: self.param_env, predicate }; + let goal = next_solver::Goal { param_env: self.trait_env.env, predicate }; self.register_obligation_in_env(goal) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index eeec9d0454d3e..20f421dbbcd90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -24,9 +24,9 @@ use chalk_ir::{ use either::Either; use hir_def::{ - AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, - FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId, - Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, + AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, + GenericDefId, GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, + StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, @@ -46,11 +46,10 @@ use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DomainGoal, DynTy, FnAbi, - FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, - LifetimeData, LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, - all_super_traits, + AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, + FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, + LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, + TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, db::HirDatabase, error_lifetime, @@ -1086,102 +1085,6 @@ pub(crate) fn generic_predicates_for_param_cycle_result( GenericPredicates(None) } -pub(crate) fn trait_environment_for_body_query( - db: &dyn HirDatabase, - def: DefWithBodyId, -) -> Arc> { - let Some(def) = def.as_generic_def_id(db) else { - let krate = def.module(db).krate(); - return TraitEnvironment::empty(krate); - }; - db.trait_environment(def) -} - -pub(crate) fn trait_environment_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, -) -> Arc> { - let generics = generics(db, def); - if generics.has_no_predicates() && generics.is_empty() { - return TraitEnvironment::empty(def.krate(db)); - } - - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(ParamLoweringMode::Placeholder); - let mut traits_in_scope = Vec::new(); - let mut clauses = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - for pred in ctx.lower_where_predicate(pred, false) { - if let WhereClause::Implemented(tr) = pred.skip_binders() { - traits_in_scope - .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); - } - let program_clause: Binders = - pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner)); - clauses.push(program_clause); - } - } - } - - if let Some(trait_id) = def.assoc_trait_container(db) { - // add `Self: Trait` to the environment in trait - // function default implementations (and speculative code - // inside consts or type aliases) - cov_mark::hit!(trait_self_implements_self); - let substs = TyBuilder::placeholder_subst(db, trait_id); - let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; - let pred = WhereClause::Implemented(trait_ref); - clauses.push(Binders::empty( - Interner, - pred.cast::(Interner).into_from_env_goal(Interner), - )); - } - - let subst = generics.placeholder_subst(db); - if !subst.is_empty(Interner) { - let explicitly_unsized_tys = ctx.unsized_types; - if let Some(implicitly_sized_clauses) = - implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) - { - clauses.extend(implicitly_sized_clauses.map(|pred| { - Binders::empty( - Interner, - pred.into_from_env_goal(Interner).cast::(Interner), - ) - })); - }; - } - - let clauses = chalk_ir::ProgramClauses::from_iter( - Interner, - clauses.into_iter().map(|g| { - chalk_ir::ProgramClause::new( - Interner, - chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { - consequence: g, - conditions: chalk_ir::Goals::empty(Interner), - constraints: chalk_ir::Constraints::empty(Interner), - priority: chalk_ir::ClausePriority::High, - })), - ) - }), - ); - let env = chalk_ir::Environment { clauses }; - - TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates(Option]>>); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 6ecf8874c46d2..1ca56feb099a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -19,9 +19,9 @@ use base_db::Crate; use either::Either; use hir_def::item_tree::FieldsShape; use hir_def::{ - AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId, - TypeOrConstParamId, VariantId, + AdtId, AssocItemId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, + GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, + StructId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, expr_store::{ ExpressionStore, path::{GenericArg, Path}, @@ -57,7 +57,7 @@ use triomphe::Arc; use crate::ValueTyDefId; use crate::{ - FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, + FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic}, db::HirDatabase, @@ -66,8 +66,10 @@ use crate::{ next_solver::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, - EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, - TraitPredicate, TraitRef, Ty, Tys, abi::Safety, mapping::ChalkToNextSolver, + EarlyParamRegion, ErrorGuaranteed, GenericArgs, ParamEnv, PolyFnSig, Predicate, Region, + SolverDefId, TraitPredicate, TraitRef, Ty, Tys, + abi::Safety, + mapping::{ChalkToNextSolver, convert_ty_for_result}, }, }; @@ -1375,6 +1377,122 @@ impl<'db> ops::Deref for GenericPredicates<'db> { } } +pub(crate) fn trait_environment_for_body_query( + db: &dyn HirDatabase, + def: DefWithBodyId, +) -> Arc> { + let Some(def) = def.as_generic_def_id(db) else { + let krate = def.module(db).krate(); + return TraitEnvironment::empty(krate); + }; + db.trait_environment(def) +} + +pub(crate) fn trait_environment_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> Arc> { + let generics = generics(db, def); + if generics.has_no_predicates() && generics.is_empty() { + return TraitEnvironment::empty(def.krate(db)); + } + + let interner = DbInterner::new_with(db, Some(def.krate(db)), None); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + let mut traits_in_scope = Vec::new(); + let mut clauses = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + for pred in ctx.lower_where_predicate(pred, false, &generics, PredicateFilter::All) { + if let rustc_type_ir::ClauseKind::Trait(tr) = pred.kind().skip_binder() { + traits_in_scope + .push((convert_ty_for_result(interner, tr.self_ty()), tr.def_id().0)); + } + clauses.push(pred); + } + } + } + + if let Some(trait_id) = def.assoc_trait_container(db) { + // add `Self: Trait` to the environment in trait + // function default implementations (and speculative code + // inside consts or type aliases) + cov_mark::hit!(trait_self_implements_self); + let trait_ref = TraitRef::identity(ctx.interner, trait_id.into()); + let clause = Clause(Predicate::new( + ctx.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( + TraitPredicate { trait_ref, polarity: rustc_type_ir::PredicatePolarity::Positive }, + ))), + )); + clauses.push(clause); + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let (mut generics, mut def_id) = + (crate::next_solver::generics::generics(db, def.into()), def); + loop { + let self_idx = trait_self_param_idx(db, def_id); + for (idx, p) in generics.own_params.iter().enumerate() { + if let Some(self_idx) = self_idx + && p.index() as usize == self_idx + { + continue; + } + let GenericParamId::TypeParamId(param_id) = p.id else { + continue; + }; + let idx = idx as u32 + generics.parent_count as u32; + let param_ty = Ty::new_param(ctx.interner, param_id, idx, p.name.clone()); + if explicitly_unsized_tys.contains(¶m_ty) { + continue; + } + let trait_ref = TraitRef::new_from_args( + ctx.interner, + sized_trait.into(), + GenericArgs::new_from_iter(ctx.interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + ctx.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + clauses.push(clause); + } + + if let Some(g) = generics.parent { + generics = crate::next_solver::generics::generics(db, g.into()); + def_id = g; + } else { + break; + } + } + } + + let clauses = rustc_type_ir::elaborate::elaborate(ctx.interner, clauses); + let clauses = Clauses::new_from_iter(ctx.interner, clauses); + let env = ParamEnv { clauses }; + + TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) +} + #[derive(Copy, Clone, Debug)] pub(crate) enum PredicateFilter { SelfTrait, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index db74ca258732c..fd28159b49cb6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -31,7 +31,7 @@ use crate::{ infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, lang_items::is_box, next_solver::{ - self, SolverDefId, + self, DbInterner, SolverDefId, infer::{ DefineOpaqueTypes, traits::{ObligationCause, PredicateObligation}, @@ -1755,7 +1755,10 @@ fn is_valid_trait_method_candidate( }; let res = table .infer_ctxt - .at(&next_solver::infer::traits::ObligationCause::dummy(), table.param_env) + .at( + &next_solver::infer::traits::ObligationCause::dummy(), + table.trait_env.env, + ) .relate( DefineOpaqueTypes::No, expected_receiver.to_nextsolver(table.interner), @@ -1845,7 +1848,12 @@ fn is_valid_impl_fn_candidate( let mut ctxt = ObligationCtxt::new(&table.infer_ctxt); ctxt.register_obligations(predicates.into_iter().map(|p| { - PredicateObligation::new(table.interner, ObligationCause::new(), table.param_env, p.0) + PredicateObligation::new( + table.interner, + ObligationCause::new(), + table.trait_env.env, + p.0, + ) })); if ctxt.select_where_possible().is_empty() { @@ -1893,7 +1901,10 @@ fn generic_implements_goal<'db>( let binders = CanonicalVarKinds::from_iter(Interner, kinds); let obligation = trait_ref.cast(Interner); - let value = InEnvironment::new(&env.env, obligation); + let value = InEnvironment::new( + &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)), + obligation, + ); Canonical { binders, value } } @@ -1910,7 +1921,7 @@ fn generic_implements_goal_ns<'db>( let trait_ref = rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args) .with_replaced_self_ty(table.infer_ctxt.interner, self_ty); - let goal = next_solver::Goal::new(table.infer_ctxt.interner, table.param_env, trait_ref); + let goal = next_solver::Goal::new(table.infer_ctxt.interner, table.trait_env.env, trait_ref); table.canonicalize(goal) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 2ba1e2341b297..a6215ef8fe4fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -632,7 +632,7 @@ fn issue_4053_diesel_where_clauses() { 488..522 '{ ... }': as BoxedDsl>::Output 498..502 'self': SelectStatement 498..508 'self.order': O - 498..515 'self.o...into()': dyn QueryFragment + '? + 498..515 'self.o...into()': dyn QueryFragment + 'static "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index d6a86c13978b3..8ac152341e753 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -25,7 +25,7 @@ use crate::{ db::HirDatabase, infer::unify::InferenceTable, next_solver::{ - DbInterner, GenericArg, Predicate, SolverContext, Span, + DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span, infer::{DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, util::mini_canonicalize, @@ -44,8 +44,7 @@ pub struct TraitEnvironment<'db> { pub block: Option, // FIXME make this a BTreeMap traits_from_clauses: Box<[(Ty, TraitId)]>, - pub env: chalk_ir::Environment, - _db: std::marker::PhantomData<&'db ()>, + pub env: ParamEnv<'db>, } impl<'db> TraitEnvironment<'db> { @@ -54,8 +53,7 @@ impl<'db> TraitEnvironment<'db> { krate, block: None, traits_from_clauses: Box::default(), - env: chalk_ir::Environment::new(Interner), - _db: std::marker::PhantomData, + env: ParamEnv::empty(), }) } @@ -63,15 +61,9 @@ impl<'db> TraitEnvironment<'db> { krate: Crate, block: Option, traits_from_clauses: Box<[(Ty, TraitId)]>, - env: chalk_ir::Environment, + env: ParamEnv<'db>, ) -> Arc { - Arc::new(TraitEnvironment { - krate, - block, - traits_from_clauses, - env, - _db: std::marker::PhantomData, - }) + Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) } // pub fn with_block(self: &mut Arc, block: BlockId) { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index eaae15835d0b5..862067e5916c0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -88,7 +88,7 @@ use hir_ty::{ next_solver::{ ClauseKind, DbInterner, GenericArgs, infer::InferCtxt, - mapping::{ChalkToNextSolver, convert_ty_for_result}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result}, }, primitive::UintTy, traits::FnTrait, @@ -5171,7 +5171,14 @@ impl<'db> Type<'db> { .build(); let goal = Canonical { - value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(Interner)), + value: hir_ty::InEnvironment::new( + &self.env.env.to_chalk(DbInterner::new_with( + db, + Some(self.env.krate), + self.env.block, + )), + trait_ref.cast(Interner), + ), binders: CanonicalVarKinds::empty(Interner), }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 809a26bf5de47..b20b570c2b8df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -26,6 +26,7 @@ mod visibility; use base_db::{SourceDatabase, salsa}; use expect_test::Expect; +use hir::db::HirDatabase; use hir::{PrefixKind, setup_tracing}; use ide_db::{ FilePosition, RootDatabase, SnippetCap, @@ -306,8 +307,11 @@ pub(crate) fn get_all_items( trigger_character: Option, ) -> Vec { let (db, position) = position(code); - let res = salsa::attach(&db, || crate::completions(&db, &config, position, trigger_character)) - .map_or_else(Vec::default, Into::into); + let res = salsa::attach(&db, || { + HirDatabase::zalsa_register_downcaster(&db); + crate::completions(&db, &config, position, trigger_character) + }) + .map_or_else(Vec::default, Into::into); // validate res.iter().for_each(|it| { let sr = it.source_range; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 5457d57b3976e..04537f908fbb8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -100,8 +100,8 @@ pub(crate) fn view_memory_layout( Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), Definition::Field(it) => salsa::attach(db, || it.ty(db).to_type(db)), - Definition::Const(it) => it.ty(db), - Definition::Static(it) => it.ty(db), + Definition::Const(it) => salsa::attach(db, || it.ty(db)), + Definition::Static(it) => salsa::attach(db, || it.ty(db)), _ => return None, }; From 4c1595a93b349fa755d84f8943860589c27b64f0 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 22 Sep 2025 21:13:38 -0400 Subject: [PATCH 0764/1900] Skip the panic-immediate-abort-works test when cross-compiling --- tests/run-make-cargo/panic-immediate-abort-works/rmake.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs index bb15bd41e79c4..3eeef38c962dc 100644 --- a/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs +++ b/tests/run-make-cargo/panic-immediate-abort-works/rmake.rs @@ -4,7 +4,12 @@ // cleaner and more portable). So this test ensures that we didn't mix up a cfg or a compiler // implementation detail in a way that makes panic=immediate-abort encounter errors at link time. +// Ideally this test would be run for most targets, but unfortunately: +// This test is currently written using `fn main() {}` which requires std. +// And since the default linker is only a linker for the host, we can't handle cross-compilation. +// Both of these shortcomings could be addressed at the cost of making the test more complicated. //@ needs-target-std +//@ ignore-cross-compile #![deny(warnings)] From c241ff4089bf4619ec327c6da0955ee71a08c0a2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 23 Sep 2025 11:26:52 +0800 Subject: [PATCH 0765/1900] Add const parameter keyword completion Example --- ```rust fn foo() {} ``` -> ```rust fn foo() {} ``` --- .../crates/ide-completion/src/completions.rs | 4 ++- .../ide-completion/src/tests/special.rs | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index e36e0e5704581..eb2bb31f963e1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -691,6 +691,9 @@ pub(super) fn complete_name( NameKind::RecordField => { field::complete_field_list_record_variant(acc, ctx); } + NameKind::TypeParam => { + acc.add_keyword_snippet(ctx, "const", "const $1: $0"); + } NameKind::ConstParam | NameKind::Enum | NameKind::MacroDef @@ -700,7 +703,6 @@ pub(super) fn complete_name( | NameKind::Static | NameKind::Struct | NameKind::Trait - | NameKind::TypeParam | NameKind::Union | NameKind::Variant => (), } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 84ddff8f617ac..c438ca7880625 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1510,3 +1510,28 @@ fn foo() { "#]], ); } + +#[test] +fn fn_generic_params_const_param_snippet() { + check_edit("const", "fn foo() {}", "fn foo() {}"); + check_edit("const", "fn foo() {}", "fn foo() {}"); + check( + r#" +fn foo() {} +"#, + expect![[r#" + kw crate:: + kw self:: + "#]], + ); + check( + r#" +fn foo() {} +"#, + expect![[r#" + bt u32 u32 + kw crate:: + kw self:: + "#]], + ); +} From aaa82aea053bfbb226916b38c66243a28a296b6e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 21:52:48 +0530 Subject: [PATCH 0766/1900] move config check logic from get_toml to parse_inner --- src/bootstrap/src/core/builder/tests.rs | 4 ++-- src/bootstrap/src/core/config/config.rs | 12 +++++++++++ src/bootstrap/src/core/config/tests.rs | 25 ++++++++--------------- src/bootstrap/src/core/config/toml/mod.rs | 6 ------ 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f838149977d01..4555f0d20912e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -308,14 +308,14 @@ mod sysroot_target_dirs { /// cg_gcc tests instead. #[test] fn test_test_compiler() { - let config = configure_with_args(&["test", "compiler"], &[], &[TEST_TRIPLE_1]); + let config = configure_with_args(&["test", "compiler"], &[&host_target()], &[TEST_TRIPLE_1]); let cache = run_build(&config.paths.clone(), config); let compiler = cache.contains::(); let cranelift = cache.contains::(); let gcc = cache.contains::(); - assert_eq!((compiler, cranelift, gcc), (false, false, false)); + assert_eq!((compiler, cranelift, gcc), (true, false, false)); } #[test] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 280ae088f3f3d..1e207380a0a1b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -424,6 +424,18 @@ impl Config { src = src_; } + #[cfg(test)] + { + if let Some(config_path) = flags_config.as_ref() { + assert!( + !config_path.starts_with(&src), + "Path {config_path:?} should not be inside or equal to src dir {src:?}" + ); + } else { + panic!("During test the config should be explicitly added"); + } + } + // Now load the TOML config, as soon as possible let (mut toml, toml_path) = load_toml_config(&src, flags_config, &get_toml); diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 3390e9586a4c7..4f2df76a15658 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -175,20 +175,14 @@ fn override_toml_duplicate() { #[test] fn profile_user_dist() { - fn get_toml(file: &Path) -> Result { - let contents = if file.ends_with("bootstrap.toml") - || file.ends_with("config.toml") - || env::var_os("RUST_BOOTSTRAP_CONFIG").is_some() - { - "profile = \"user\"".to_owned() - } else { - assert!(file.ends_with("config.dist.toml") || file.ends_with("bootstrap.dist.toml")); - std::fs::read_to_string(file).unwrap() - }; - - toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) - } - Config::parse_inner(Flags::parse(&["check".to_owned()]), get_toml); + TestCtx::new() + .config("check") + .with_default_toml_config( + r#" + profile = "user" + "#, + ) + .create_config(); } #[test] @@ -224,8 +218,6 @@ fn verify_file_integrity() { config .verify(&tempfile, "7e255dd9542648a8779268a0f268b891a198e9828e860ed23f826440e786eae5") ); - - remove_file(tempfile).unwrap(); } #[test] @@ -648,6 +640,7 @@ fn test_include_precedence_over_profile() { .config("check") .with_default_toml_config( r#" + profile = "dist" include = ["./extension.toml"] "#, ) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index 5f86d2e728184..f6dc5b67e1010 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,12 +152,6 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { - #[cfg(test)] - { - let tmp = std::env::temp_dir(); - assert!(file.starts_with(&tmp), "Expected path in temp dir {:?}, got {:?}", tmp, file); - } - Self::get_toml_inner(file) } From 2d495ccaaa874666768b1a9e47e06d7de9e9aceb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 23 Sep 2025 12:14:14 +0800 Subject: [PATCH 0767/1900] Migrate `expand_record_rest_pattern` assist to use `SyntaxEditor` Because `add_field` uses `ted` --- .../src/handlers/expand_rest_pattern.rs | 21 +++++++++---------- .../crates/ide-assists/src/tests/generated.rs | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index c80b78fd97056..44fe7957bca0e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // struct Bar { y: Y, z: Z } // // fn foo(bar: Bar) { -// let Bar { y, z } = bar; +// let Bar { y, z } = bar; // } // ``` fn expand_record_rest_pattern( @@ -53,18 +53,17 @@ fn expand_record_rest_pattern( |builder| { let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); - let new_field_list = make.record_pat_field_list(old_field_list.fields(), None); - for (f, _) in missing_fields.iter() { - let field = make.record_pat_field_shorthand( + let new_fields = old_field_list.fields().chain(missing_fields.iter().map(|(f, _)| { + make.record_pat_field_shorthand( make.ident_pat( false, false, make.name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()), ) .into(), - ); - new_field_list.add_field(field); - } + ) + })); + let new_field_list = make.record_pat_field_list(new_fields, None); editor.replace(old_field_list.syntax(), new_field_list.syntax()); @@ -211,7 +210,7 @@ enum Foo { fn bar(foo: Foo) { match foo { Foo::A(_) => false, - Foo::B{ y, z } => true, + Foo::B{ y, z } => true, }; } "#, @@ -272,7 +271,7 @@ struct Bar { } fn foo(bar: Bar) { - let Bar { y, z } = bar; + let Bar { y, z } = bar; } "#, ); @@ -376,7 +375,7 @@ macro_rules! position { position!(usize); fn macro_call(pos: Pos) { - let Pos { x, y } = pos; + let Pos { x, y } = pos; } "#, ); @@ -420,7 +419,7 @@ enum_gen!(usize); fn macro_call(foo: Foo) { match foo { Foo::A(_) => false, - Foo::B{ x, y } => true, + Foo::B{ x, y } => true, } } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 91348be97eb72..71ffaa23fc640 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1035,7 +1035,7 @@ fn foo(bar: Bar) { struct Bar { y: Y, z: Z } fn foo(bar: Bar) { - let Bar { y, z } = bar; + let Bar { y, z } = bar; } "#####, ) From 9ae7aef06d1146ee30190fb399d548e1db3652dd Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Mon, 22 Sep 2025 15:02:02 +0800 Subject: [PATCH 0768/1900] prevent line number from being copied in chrome --- src/librustdoc/html/static/js/main.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 75febd6f737e6..3ea9de381eca6 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -2227,11 +2227,18 @@ function preLoadCss(cssUrl) { }); }()); -// This section is a bugfix for firefox: when copying text with `user-select: none`, it adds -// extra backline characters. + +// Workaround for browser-specific bugs when copying code snippets. +// +// * In Firefox, copying text that includes elements with `user-select: none` +// inserts extra blank lines. +// - Firefox issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1273836 +// - Rust issue: https://github.com/rust-lang/rust/issues/141464 // -// Rustdoc issue: Workaround for https://github.com/rust-lang/rust/issues/141464 -// Firefox issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1273836 +// * In Chromium-based browsers, `document.getSelection()` includes elements +// with `user-select: none`, causing unwanted line numbers to be copied. +// - Chromium issue: https://issues.chromium.org/issues/446539520 +// - Rust issue: https://github.com/rust-lang/rust/issues/146816 (function() { document.body.addEventListener("copy", event => { let target = nonnull(event.target); @@ -2248,9 +2255,13 @@ function preLoadCss(cssUrl) { if (!isInsideCode) { return; } - const selection = document.getSelection(); - // @ts-expect-error - nonnull(event.clipboardData).setData("text/plain", selection.toString()); + const selection = nonnull(document.getSelection()); + const text = Array.from({ length: selection.rangeCount }, (_, i) => { + const fragment = selection.getRangeAt(i).cloneContents(); + fragment.querySelectorAll("[data-nosnippet]").forEach(el => el.remove()); + return fragment.textContent; + }).join(""); + nonnull(event.clipboardData).setData("text/plain", text); event.preventDefault(); }); }()); From 465e373542b529055cd1302849f79db13a617a98 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 07:55:43 +0200 Subject: [PATCH 0769/1900] add regression test --- tests/ui/methods/overflow-if-subtyping.rs | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/methods/overflow-if-subtyping.rs diff --git a/tests/ui/methods/overflow-if-subtyping.rs b/tests/ui/methods/overflow-if-subtyping.rs new file mode 100644 index 0000000000000..a97f29f1f6dfc --- /dev/null +++ b/tests/ui/methods/overflow-if-subtyping.rs @@ -0,0 +1,30 @@ +//@ check-pass + +// Regression test for #128887. +#![allow(unconditional_recursion)] +trait Mappable { + type Output; +} + +trait Bound {} +// Deleting this impl made it compile on beta +impl Bound for T {} + +trait Generic {} + +// Deleting the `: Mappable` already made it error on stable. +struct IndexWithIter, T>(I, M, T); + +impl IndexWithIter +where + >::Output: Bound, + // Flipping these where bounds causes this to succeed, even when removing + // the where-clause on the struct definition. + M: Mappable, + I: Generic, +{ + fn new(x: I) { + IndexWithIter::<_, _, _>::new(x); + } +} +fn main() {} From 3c8d8da693eb5d63099eef5cf4a73106a3a2ba25 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Sep 2025 09:09:53 +0200 Subject: [PATCH 0770/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to f6092f224d2b1774b31033f12d0bee626943b02f. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 7333f7968f771..388e88fe43eb7 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ec38671075266e9cee0348701da2e133379e7c6c +f6092f224d2b1774b31033f12d0bee626943b02f From c960e9c218e5adac9b61a3cd044d40ebc0cb1ace Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Mon, 22 Sep 2025 23:46:40 -0400 Subject: [PATCH 0771/1900] Remove lower::ty in favor of lower_nextsolver::ty --- .../crates/hir-ty/src/builder.rs | 35 ++++++++------ .../rust-analyzer/crates/hir-ty/src/db.rs | 14 ++---- .../rust-analyzer/crates/hir-ty/src/infer.rs | 46 ++++++++++++++----- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 23 +++++----- .../rust-analyzer/crates/hir-ty/src/lower.rs | 22 --------- .../crates/hir-ty/src/lower/path.rs | 15 ++++-- .../crates/hir-ty/src/method_resolution.rs | 6 ++- .../crates/hir-ty/src/mir/eval/shim.rs | 6 +-- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- .../crates/hir-ty/src/tests/incremental.rs | 2 - .../hir-ty/src/tests/method_resolution.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 23 +++++++--- .../crates/ide/src/view_memory_layout.rs | 4 +- 14 files changed, 112 insertions(+), 92 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 3755175cf5163..4957c69ae1656 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -3,17 +3,20 @@ use chalk_ir::{ AdtId, DebruijnIndex, Scalar, cast::{Cast, CastTo, Caster}, - fold::TypeFoldable, - interner::HasInterner, }; use hir_def::{GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType}; use smallvec::SmallVec; use crate::{ - Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, - Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, consteval::unknown_const_as_generic, - db::HirDatabase, error_lifetime, generics::generics, infer::unify::InferenceTable, primitive, - to_assoc_type_id, to_chalk_trait_id, + BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, + TraitRef, Ty, TyDefId, TyExt, TyKind, + consteval::unknown_const_as_generic, + db::HirDatabase, + error_lifetime, + generics::generics, + infer::unify::InferenceTable, + next_solver::{DbInterner, EarlyBinder, mapping::ChalkToNextSolver}, + primitive, to_assoc_type_id, to_chalk_trait_id, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -345,19 +348,20 @@ impl TyBuilder { } } -impl + TypeFoldable> TyBuilder> { - pub fn build(self) -> T { +impl<'db, T: rustc_type_ir::TypeFoldable>> TyBuilder> { + pub fn build(self, interner: DbInterner<'db>) -> T { let (b, subst) = self.build_internal(); - b.substitute(Interner, &subst) + let args: crate::next_solver::GenericArgs<'db> = subst.to_nextsolver(interner); + b.instantiate(interner, args) } } -impl TyBuilder> { +impl<'db> TyBuilder>> { pub fn def_ty( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: TyDefId, parent_subst: Option, - ) -> TyBuilder> { + ) -> TyBuilder>> { let poly_ty = db.ty(def); let id: GenericDefId = match def { TyDefId::BuiltinType(_) => { @@ -370,7 +374,10 @@ impl TyBuilder> { TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty) } - pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder> { - TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def)) + pub fn impl_self_ty( + db: &'db dyn HirDatabase, + def: hir_def::ImplId, + ) -> TyBuilder>> { + TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty_ns(def)) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 44f48069ab240..41540f328b237 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -114,9 +114,12 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; - #[salsa::invoke(crate::lower::ty_query)] + #[salsa::invoke(crate::lower_nextsolver::ty_query)] #[salsa::transparent] - fn ty(&self, def: TyDefId) -> Binders; + fn ty<'db>( + &'db self, + def: TyDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower::type_for_type_alias_with_diagnostics_cycle_result)] @@ -277,13 +280,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::lower_nextsolver::ty_query)] - #[salsa::transparent] - fn ty_ns<'db>( - &'db self, - def: TyDefId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; - /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 3ece62ec35b63..0a095ea644589 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1708,6 +1708,7 @@ impl<'db> InferenceContext<'db> { LifetimeElisionKind::Infer, ); let mut path_ctx = ctx.at_path(path, node); + let interner = DbInterner::conjure(); let (resolution, unresolved) = if value_ns { let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else { return (self.err_ty(), None); @@ -1717,15 +1718,27 @@ impl<'db> InferenceContext<'db> { ValueNs::EnumVariantId(var) => { let substs = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); - let ty = self.db.ty(var.lookup(self.db).parent.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let args: crate::next_solver::GenericArgs<'_> = + substs.to_nextsolver(interner); + let ty = self + .db + .ty(var.lookup(self.db).parent.into()) + .instantiate(interner, args) + .to_chalk(interner); + let ty = self.insert_type_vars(ty); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { let substs = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); - let ty = self.db.ty(strukt.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let args: crate::next_solver::GenericArgs<'_> = + substs.to_nextsolver(interner); + let ty = self + .db + .ty(strukt.into()) + .instantiate(interner, args) + .to_chalk(interner); + let ty = self.insert_type_vars(ty); return (ty, Some(strukt.into())); } ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), @@ -1746,22 +1759,29 @@ impl<'db> InferenceContext<'db> { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); - let ty = self.db.ty(strukt.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = self.db.ty(strukt.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.insert_type_vars(ty); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { let substs = path_ctx.substs_from_path(u.into(), true, false); drop(ctx); - let ty = self.db.ty(u.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = self.db.ty(u.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.insert_type_vars(ty); forbid_unresolved_segments((ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { let substs = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); - let ty = self.db.ty(var.lookup(self.db).parent.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = self + .db + .ty(var.lookup(self.db).parent.into()) + .instantiate(interner, args) + .to_chalk(interner); + let ty = self.insert_type_vars(ty); forbid_unresolved_segments((ty, Some(var.into())), unresolved) } TypeNs::SelfType(impl_id) => { @@ -1844,8 +1864,10 @@ impl<'db> InferenceContext<'db> { }; let substs = path_ctx.substs_from_path_segment(it.into(), true, None, false); drop(ctx); - let ty = self.db.ty(it.into()); - let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + let interner = DbInterner::conjure(); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = self.db.ty(it.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.insert_type_vars(ty); self.resolve_variant_on_alias(ty, unresolved, mod_path) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6df9cbaa29394..dd7e77ba8c098 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -780,7 +780,7 @@ impl<'db> InferenceTable<'db> { } pub(crate) fn structurally_resolve_type(&mut self, ty: &Ty) -> Ty { - if let TyKind::Alias(..) = ty.kind(Interner) { + if let TyKind::Alias(chalk_ir::AliasTy::Projection(..)) = ty.kind(Interner) { self.structurally_normalize_ty(ty) } else { self.resolve_vars_with_obligations(ty.to_nextsolver(self.interner)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 523ddad94666b..6960e230a6850 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,18 +1,17 @@ use base_db::target::TargetData; -use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; use project_model::{Sysroot, toolchain_info::QueryConfig}; use rustc_hash::FxHashMap; +use rustc_type_ir::inherent::{GenericArgs as _, Ty as _}; use syntax::ToSmolStr; use test_fixture::WithFixture; use triomphe::Arc; use crate::{ - Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, + next_solver::{AdtDef, DbInterner, GenericArgs, mapping::ChalkToNextSolver}, setup_tracing, test_db::TestDB, }; @@ -80,18 +79,18 @@ fn eval_goal( Some(adt_or_type_alias_id) }) .unwrap(); - let goal_ty = match adt_or_type_alias_id { - Either::Left(adt_id) => { - TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner) - } - Either::Right(ty_id) => { - db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner)) - } - }; salsa::attach(&db, || { let interner = DbInterner::new_with(&db, None, None); + let goal_ty = match adt_or_type_alias_id { + Either::Left(adt_id) => crate::next_solver::Ty::new_adt( + interner, + AdtDef::new(adt_id, interner), + GenericArgs::identity_for_item(interner, adt_id.into()), + ), + Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity(), + }; db.layout_of_ty( - goal_ty.to_nextsolver(interner), + goal_ty, db.trait_environment(match adt_or_type_alias_id { Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 20f421dbbcd90..97005e5d466a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1458,16 +1458,6 @@ fn type_for_enum_variant_constructor( } } -#[salsa_macros::tracked(cycle_result = type_for_adt_cycle_result)] -fn type_for_adt_tracked(db: &dyn HirDatabase, adt: AdtId) -> Binders { - type_for_adt(db, adt) -} - -fn type_for_adt_cycle_result(db: &dyn HirDatabase, adt: AdtId) -> Binders { - let generics = generics(db, adt.into()); - make_binders(db, &generics, TyKind::Error.intern(Interner)) -} - fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders { let generics = generics(db, adt.into()); let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); @@ -1547,18 +1537,6 @@ impl ValueTyDefId { } } -/// Build the declared type of an item. This depends on the namespace; e.g. for -/// `struct Foo(usize)`, we have two types: The type of the struct itself, and -/// the constructor function `(usize) -> Foo` which lives in the values -/// namespace. -pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders { - match def { - TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)), - TyDefId::AdtId(it) => type_for_adt_tracked(db, it), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, - } -} - pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option> { match def { ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index bc03298e3bbb0..279bbff7c0d9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -28,6 +28,10 @@ use crate::{ error_lifetime, generics::{Generics, generics}, lower::{LifetimeElisionKind, named_associated_type_shorthand_candidates}, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, utils::associated_type_by_name_including_super_traits, }; @@ -256,7 +260,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id) .fill_with_bound_vars(self.ctx.in_binders, 0) - .build(), + .build(DbInterner::conjure()) + .to_chalk(DbInterner::conjure()), } } TypeNs::AdtSelfType(adt) => { @@ -267,7 +272,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders) } }; - self.ctx.db.ty(adt.into()).substitute(Interner, &substs) + let interner = DbInterner::conjure(); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + self.ctx.db.ty(adt.into()).instantiate(interner, args).to_chalk(interner) } TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), @@ -537,7 +544,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TyDefId::TypeAliasId(it) => it.into(), }; let substs = self.substs_from_path_segment(generic_def, infer_args, None, false); - self.ctx.db.ty(typeable).substitute(Interner, &substs) + let interner = DbInterner::conjure(); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner) } /// Collect generic arguments from a path into a `Substs`. See also diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index fd28159b49cb6..66687490b4a45 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1697,8 +1697,10 @@ fn is_valid_impl_method_candidate( return IsValidCandidate::NotVisible; } let self_ty_matches = table.run_in_snapshot(|table| { - let expected_self_ty = - TyBuilder::impl_self_ty(db, impl_id).fill_with_inference_vars(table).build(); + let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) + .fill_with_inference_vars(table) + .build(DbInterner::conjure()) + .to_chalk(DbInterner::conjure()); table.unify(&expected_self_ty, self_ty) }); if !self_ty_matches { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index f67778b0f12f3..bb0d1f70fbb29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -14,6 +14,7 @@ use hir_expand::name::Name; use intern::{Symbol, sym}; use stdx::never; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ DropGlue, display::DisplayTarget, @@ -1371,9 +1372,8 @@ impl Evaluator<'_> { result = (l as i8).cmp(&(r as i8)); } if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) { - let ty = self.db.ty(e.into()); - let r = self - .compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?; + let ty = self.db.ty(e.into()).skip_binder().to_chalk(interner); + let r = self.compute_discriminant(ty.clone(), &[result as i8 as u8])?; destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?; Ok(()) } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9cf56bef9578a..6c4212e6ecd43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1091,9 +1091,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ItemContainerId::ImplId(it) => it, _ => panic!("assoc ty value should be in impl"), }; - self.db().ty_ns(id.into()) + self.db().ty(id.into()) } - SolverDefId::AdtId(id) => self.db().ty_ns(id.into()), + SolverDefId::AdtId(id) => self.db().ty(id.into()), // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. // // We currently always use the type from HIR typeck which ignores regions. This diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index c0b930e5e1231..0d922de9aeb07 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -511,7 +511,6 @@ impl SomeStruct { "struct_signature_shim", "struct_signature_with_source_map_shim", "attrs_shim", - "type_for_adt_tracked", ] "#]], ); @@ -609,7 +608,6 @@ fn main() { "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", - "type_for_adt_tracked", "impl_trait_with_diagnostics_ns_shim", "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index b14ce35aa99c8..6a566a505579c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -2053,7 +2053,7 @@ impl dyn Error + Send { // ^^^^ expected Box, got Box // FIXME, type mismatch should not occur ::downcast(err).map_err(|_| loop {}) - //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> + //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> } } "#, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 862067e5916c0..a1d9e6d365f83 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1504,7 +1504,7 @@ impl<'db> InstantiatedStruct<'db> { let krate = self.inner.krate(db); let interner = DbInterner::new_with(db, Some(krate.base()), None); - let ty = db.ty_ns(self.inner.id.into()); + let ty = db.ty(self.inner.id.into()); TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) } } @@ -1664,7 +1664,7 @@ impl<'db> InstantiatedEnum<'db> { let krate = self.inner.krate(db); let interner = DbInterner::new_with(db, Some(krate.base()), None); - let ty = db.ty_ns(self.inner.id.into()); + let ty = db.ty(self.inner.id.into()); TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) } } @@ -1851,7 +1851,8 @@ impl Adt { ParamKind::Lifetime => error_lifetime().cast(Interner), } }) - .build(); + .build(DbInterner::conjure()) + .to_chalk(DbInterner::conjure()); Type::new(db, id, ty) } @@ -4828,32 +4829,40 @@ impl<'db> Type<'db> { } fn from_def(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { + let interner = DbInterner::new_with(db, None, None); let ty = db.ty(def.into()); let substs = TyBuilder::unknown_subst( db, match def.into() { TyDefId::AdtId(it) => GenericDefId::AdtId(it), TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()), + TyDefId::BuiltinType(_) => { + return Type::new(db, def, ty.skip_binder().to_chalk(interner)); + } }, ); - Type::new(db, def, ty.substitute(Interner, &substs)) + let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner)) } fn from_def_placeholders( db: &'db dyn HirDatabase, def: impl Into + HasResolver, ) -> Self { + let interner = DbInterner::new_with(db, None, None); let ty = db.ty(def.into()); let substs = TyBuilder::placeholder_subst( db, match def.into() { TyDefId::AdtId(it) => GenericDefId::AdtId(it), TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()), + TyDefId::BuiltinType(_) => { + return Type::new(db, def, ty.skip_binder().to_chalk(interner)); + } }, ); - Type::new(db, def, ty.substitute(Interner, &substs)) + let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner)) } fn from_value_def( diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 04537f908fbb8..ddd58a0a3c9fb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -94,8 +94,8 @@ pub(crate) fn view_memory_layout( let def = get_definition(&sema, token)?; let ty = match def { - Definition::Adt(it) => it.ty(db), - Definition::TypeAlias(it) => it.ty(db), + Definition::Adt(it) => salsa::attach(db, || it.ty(db)), + Definition::TypeAlias(it) => salsa::attach(db, || it.ty(db)), Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), From 9e34268e24b753355a96bc9326d8e4e2a2ca916a Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 23 Sep 2025 00:26:28 -0400 Subject: [PATCH 0772/1900] Remove lower::value_ty in favor of lower_nextsolver::value_ty --- .../rust-analyzer/crates/hir-ty/src/db.rs | 15 +-- .../crates/hir-ty/src/infer/expr.rs | 42 +++++--- .../crates/hir-ty/src/infer/path.rs | 13 ++- .../rust-analyzer/crates/hir-ty/src/lower.rs | 99 +------------------ .../crates/hir-ty/src/next_solver/interner.rs | 6 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 +- .../crates/hir/src/source_analyzer.rs | 10 +- .../ide-db/src/syntax_helpers/suggest_name.rs | 2 +- 8 files changed, 61 insertions(+), 134 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 41540f328b237..6eb5f8defaa1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -127,8 +127,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. - #[salsa::invoke(crate::lower::value_ty_query)] - fn value_ty(&self, def: ValueTyDefId) -> Option>; + #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] + fn value_ty<'db>( + &'db self, + def: ValueTyDefId, + ) -> Option>>; #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower::impl_self_ty_with_diagnostics_cycle_result)] @@ -280,14 +283,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is - /// a `StructId` or `EnumVariantId` with a record constructor. - #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] - fn value_ty_ns<'db>( - &'db self, - def: ValueTyDefId, - ) -> Option>>; - #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] fn type_for_type_alias_with_diagnostics_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index b4a332f1da849..ddf632c1c81b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -23,13 +23,13 @@ use syntax::ast::RangeOp; use tracing::debug; use crate::autoderef::overloaded_deref_ty; -use crate::next_solver::ErrorGuaranteed; use crate::next_solver::infer::DefineOpaqueTypes; use crate::next_solver::obligation_ctxt::ObligationCtxt; +use crate::next_solver::{DbInterner, ErrorGuaranteed}; use crate::{ - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, - DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, - Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval, + Adjust, Adjustment, AdtId, AutoBorrow, CallableDefId, CallableSig, DeclContext, DeclOrigin, + IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, + TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval, generics::generics, infer::{ AllowTwoPhase, BreakableKind, @@ -1481,7 +1481,10 @@ impl<'db> InferenceContext<'db> { self.write_method_resolution(tgt_expr, func, subst.clone()); - let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst); + let interner = DbInterner::new_with(self.db, None, None); + let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); + let method_ty = + self.db.value_ty(func.into()).unwrap().instantiate(interner, args).to_chalk(interner); self.register_obligations_for_call(&method_ty); self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes); @@ -1800,11 +1803,17 @@ impl<'db> InferenceContext<'db> { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); self.write_method_resolution(tgt_expr, func, substs.clone()); + let interner = DbInterner::new_with(self.db, None, None); + let args: crate::next_solver::GenericArgs<'_> = + substs.to_nextsolver(interner); self.check_method_call( tgt_expr, &[], - self.db.value_ty(func.into()).unwrap(), - substs, + self.db + .value_ty(func.into()) + .unwrap() + .instantiate(interner, args) + .to_chalk(interner), ty, expected, ) @@ -1963,11 +1972,16 @@ impl<'db> InferenceContext<'db> { let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args); self.write_method_resolution(tgt_expr, func, substs.clone()); + let interner = DbInterner::new_with(self.db, None, None); + let gen_args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); self.check_method_call( tgt_expr, args, - self.db.value_ty(func.into()).expect("we have a function def"), - substs, + self.db + .value_ty(func.into()) + .expect("we have a function def") + .instantiate(interner, gen_args) + .to_chalk(interner), ty, expected, ) @@ -2012,11 +2026,15 @@ impl<'db> InferenceContext<'db> { let recovered = match assoc_func_with_same_name { Some(f) => { let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args); + let interner = DbInterner::new_with(self.db, None, None); + let args: crate::next_solver::GenericArgs<'_> = + substs.to_nextsolver(interner); let f = self .db .value_ty(f.into()) .expect("we have a function def") - .substitute(Interner, &substs); + .instantiate(interner, args) + .to_chalk(interner); let sig = f.callable_sig(self.db).expect("we have a function def"); Some((f, sig, true)) } @@ -2056,12 +2074,10 @@ impl<'db> InferenceContext<'db> { &mut self, tgt_expr: ExprId, args: &[ExprId], - method_ty: Binders, - substs: Substitution, + method_ty: Ty, receiver_ty: Ty, expected: &Expectation, ) -> Ty { - let method_ty = method_ty.substitute(Interner, &substs); self.register_obligations_for_call(&method_ty); let interner = self.table.interner; let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 80f7324e58b2b..3a50b832cf19d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -17,7 +17,10 @@ use crate::{ generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, - next_solver::mapping::ChalkToNextSolver, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, to_chalk_trait_id, }; @@ -36,7 +39,9 @@ impl<'db> InferenceContext<'db> { self.add_required_obligations_for_value_path(generic_def, &substs); - let ty = self.db.value_ty(value_def)?.substitute(Interner, &substs); + let interner = DbInterner::new_with(self.db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = self.db.value_ty(value_def)?.instantiate(interner, args).to_chalk(interner); let ty = self.process_remote_user_written_ty(ty); Some(ty) } @@ -89,9 +94,9 @@ impl<'db> InferenceContext<'db> { let generic_def = value_def.to_generic_def_id(self.db); if let GenericDefId::StaticId(_) = generic_def { + let interner = DbInterner::new_with(self.db, None, None); // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. - let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders(); - stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",); + let ty = self.db.value_ty(value_def)?.skip_binder().to_chalk(interner); return Some(ValuePathResolution::NonGeneric(ty)); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 97005e5d466a2..756769a2174f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -30,7 +30,6 @@ use hir_def::{ builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, - item_tree::FieldsShape, lang_item::LangItem, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, @@ -59,7 +58,7 @@ use crate::{ path::{PathDiagnosticCallback, PathLoweringContext}, }, make_binders, - mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx}, + mapping::{from_chalk_trait_id, lt_to_placeholder_idx}, static_lifetime, to_chalk_trait_id, to_placeholder_idx, utils::all_super_trait_refs, variable_kinds_from_iter, @@ -1352,51 +1351,6 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { make_binders(db, &generics, sig) } -/// Build the declared type of a function. This should not need to look at the -/// function body. -fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders { - let generics = generics(db, def.into()); - let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - make_binders( - db, - &generics, - TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(Interner), - ) -} - -/// Build the declared type of a const. -fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders { - let data = db.const_signature(def); - let generics = generics(db, def.into()); - let resolver = def.resolver(db); - let parent = def.loc(db).container; - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_const(parent), - ) - .with_type_param_mode(ParamLoweringMode::Variable); - - make_binders(db, &generics, ctx.lower_ty(data.type_ref)) -} - -/// Build the declared type of a static. -fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders { - let data = db.static_signature(def); - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::Elided(static_lifetime()), - ); - - Binders::empty(Interner, ctx.lower_ty(data.type_ref)) -} - fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { let field_tys = db.field_types(def.into()); let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); @@ -1407,24 +1361,6 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS ) } -/// Build the type of a tuple struct constructor. -fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option> { - let struct_data = def.fields(db); - match struct_data.shape { - FieldsShape::Record => None, - FieldsShape::Unit => Some(type_for_adt(db, def.into())), - FieldsShape::Tuple => { - let generics = generics(db, AdtId::from(def).into()); - let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - Some(make_binders( - db, - &generics, - TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner), - )) - } - } -} - fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig { let field_tys = db.field_types(def.into()); let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); @@ -1436,28 +1372,6 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) ) } -/// Build the type of a tuple enum variant constructor. -fn type_for_enum_variant_constructor( - db: &dyn HirDatabase, - def: EnumVariantId, -) -> Option> { - let e = def.lookup(db).parent; - match def.fields(db).shape { - FieldsShape::Record => None, - FieldsShape::Unit => Some(type_for_adt(db, e.into())), - FieldsShape::Tuple => { - let generics = generics(db, e.into()); - let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - Some(make_binders( - db, - &generics, - TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs) - .intern(Interner), - )) - } - } -} - fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders { let generics = generics(db, adt.into()); let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); @@ -1537,17 +1451,6 @@ impl ValueTyDefId { } } -pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option> { - match def { - ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), - ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), - ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), - ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), - ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), - ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), - } -} - pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders { db.impl_self_ty_with_diagnostics(impl_id).0 } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 6c4212e6ecd43..3f61b9f02621b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1099,15 +1099,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { // We currently always use the type from HIR typeck which ignores regions. This // should be fine. SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id), - SolverDefId::FunctionId(id) => self.db.value_ty_ns(id.into()).unwrap(), + SolverDefId::FunctionId(id) => self.db.value_ty(id.into()).unwrap(), SolverDefId::Ctor(id) => { let id = match id { Ctor::Struct(id) => id.into(), Ctor::Enum(id) => id.into(), }; - self.db - .value_ty_ns(id) - .expect("`SolverDefId::Ctor` should have a function-like ctor") + self.db.value_ty(id).expect("`SolverDefId::Ctor` should have a function-like ctor") } _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index a1d9e6d365f83..8b440611e6ccd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4872,6 +4872,7 @@ impl<'db> Type<'db> { let Some(ty) = db.value_ty(def.into()) else { return Type::new(db, def, TyKind::Error.intern(Interner)); }; + let interner = DbInterner::new_with(db, None, None); let substs = TyBuilder::unknown_subst( db, match def.into() { @@ -4882,10 +4883,13 @@ impl<'db> Type<'db> { ValueTyDefId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()), + ValueTyDefId::StaticId(_) => { + return Type::new(db, def, ty.skip_binder().to_chalk(interner)); + } }, ); - Type::new(db, def, ty.substitute(Interner, &substs)) + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner)) } pub fn new_slice(ty: Self) -> Self { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index a19183fa1302a..c6b7e84dc20f1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -46,6 +46,10 @@ use hir_ty::{ from_assoc_type_id, lang_items::lang_items_for_bin_op, method_resolution, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, }; use intern::sym; use itertools::Itertools; @@ -372,8 +376,10 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; let (func, substs) = self.infer()?.method_resolution(expr_id)?; - let ty = db.value_ty(func.into())?.substitute(Interner, &substs); - let ty = Type::new_with_resolver(db, &self.resolver, ty); + let interner = DbInterner::new_with(db, None, None); + let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = db.value_ty(func.into())?.instantiate(interner, args); + let ty = Type::new_with_resolver(db, &self.resolver, ty.to_chalk(interner)); let mut res = ty.as_callable(db)?; res.is_bound_method = true; Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 995bf72dca163..2e03665765f38 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -473,7 +473,7 @@ mod tests { frange.range, "selection is not an expression(yet contained in one)" ); - let name = NameGenerator::default().for_variable(&expr, &sema); + let name = salsa::attach(sema.db, || NameGenerator::default().for_variable(&expr, &sema)); assert_eq!(&name, expected); } From bc7986ec791ded521f3f75ab82645b6be0c39508 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Sep 2025 17:09:14 +0200 Subject: [PATCH 0773/1900] Add attributes for #[global_allocator] functions Emit `#[rustc_allocator]` etc. attributes on the functions generated by the `#[global_allocator]` macro, which will emit LLVM attributes like `"alloc-family"`. If the module with the global allocator participates in LTO, this ensures that the attributes typically emitted on the allocator declarations are not lost if the definition is imported. --- .../src/global_allocator.rs | 16 ++++++-- .../global-allocator-attributes.rs | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/codegen-llvm/global-allocator-attributes.rs diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index f14b1920722f4..96bece2a368cd 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -85,7 +85,7 @@ impl AllocFnFactory<'_, '_> { body, define_opaque: None, })); - let item = self.cx.item(self.span, self.attrs(), kind); + let item = self.cx.item(self.span, self.attrs(method), kind); self.cx.stmt_item(self.ty_span, item) } @@ -100,8 +100,18 @@ impl AllocFnFactory<'_, '_> { self.cx.expr_call(self.ty_span, method, args) } - fn attrs(&self) -> AttrVec { - thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] + fn attrs(&self, method: &AllocatorMethod) -> AttrVec { + let alloc_attr = match method.name { + sym::alloc => sym::rustc_allocator, + sym::dealloc => sym::rustc_deallocator, + sym::realloc => sym::rustc_reallocator, + sym::alloc_zeroed => sym::rustc_allocator_zeroed, + _ => unreachable!("Unknown allocator method!"), + }; + thin_vec![ + self.cx.attr_word(sym::rustc_std_internal_symbol, self.span), + self.cx.attr_word(alloc_attr, self.span) + ] } fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec) -> Box { diff --git a/tests/codegen-llvm/global-allocator-attributes.rs b/tests/codegen-llvm/global-allocator-attributes.rs new file mode 100644 index 0000000000000..472ca77207500 --- /dev/null +++ b/tests/codegen-llvm/global-allocator-attributes.rs @@ -0,0 +1,41 @@ +//@ compile-flags: -C opt-level=3 +#![crate_type = "lib"] + +mod foobar { + use std::alloc::{GlobalAlloc, Layout}; + + struct Allocator; + + unsafe impl GlobalAlloc for Allocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // CHECK-LABEL: ; __rustc::__rust_alloc + // CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,uninitialized,aligned") allocsize(0){{.*}} + // CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc(i[[SIZE:[0-9]+]] {{.*}}%size, i[[SIZE]] allocalign{{.*}} %align) + panic!() + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // CHECK-LABEL: ; __rustc::__rust_dealloc + // CHECK-NEXT: ; Function Attrs: {{.*}}allockind("free"){{.*}} + // CHECK-NEXT: define{{.*}} void @{{.*}}__rust_dealloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] {{.*}} %align) + panic!() + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // CHECK-LABEL: ; __rustc::__rust_realloc + // CHECK-NEXT: ; Function Attrs: {{.*}}allockind("realloc,aligned") allocsize(3){{.*}} + // CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_realloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align, i[[SIZE]] {{.*}} %new_size) + panic!() + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // CHECK-LABEL: ; __rustc::__rust_alloc_zeroed + // CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,zeroed,aligned") allocsize(0){{.*}} + // CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc_zeroed(i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align) + panic!() + } + } + + #[global_allocator] + static GLOBAL: Allocator = Allocator; +} From a025cacc600fe1848db29cdaf199ac2d33f83181 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 17 Sep 2025 21:19:53 +0200 Subject: [PATCH 0774/1900] Expose iterators over an inference result's types (This re-introduces a reduced access to a couple of previously public fields on `InferenceResult`) --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 3ece62ec35b63..b5e9aece364c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -632,6 +632,26 @@ impl InferenceResult { pub fn binding_mode(&self, id: PatId) -> Option { self.binding_modes.get(id).copied() } + + // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. + pub fn expression_types(&self) -> impl Iterator { + self.type_of_expr.iter() + } + + // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. + pub fn pattern_types(&self) -> impl Iterator { + self.type_of_pat.iter() + } + + // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. + pub fn binding_types(&self) -> impl Iterator { + self.type_of_binding.iter() + } + + // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. + pub fn return_position_impl_trait_types(&self) -> impl Iterator { + self.type_of_rpit.iter() + } } impl Index for InferenceResult { From ffca2f0a1e5366221c3b81925089e4fa14cd3a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 23 Sep 2025 11:39:40 +0200 Subject: [PATCH 0775/1900] Make it possible to `x install` Cranelift and LLVM bitcode linker --- src/bootstrap/src/core/builder/mod.rs | 2 ++ src/bootstrap/src/core/builder/tests.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 8226b4325b6b8..10587b02c7166 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1221,6 +1221,8 @@ impl<'a> Builder<'a> { install::Miri, install::LlvmTools, install::Src, + install::RustcCodegenCranelift, + install::LlvmBitcodeLinker ), Kind::Run => describe!( run::BuildManifest, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 229adf714598b..87bea431a0588 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2911,6 +2911,9 @@ mod snapshot { [build] rustc 1 -> cargo-miri 2 [dist] rustc 1 -> miri 2 [dist] src <> + [build] rustc 1 -> rustc_codegen_cranelift 2 + [dist] rustc 1 -> rustc_codegen_cranelift 2 + [build] rustc 1 -> LlvmBitcodeLinker 2 "); } From 739e89980f2c5602851c9271fd61f7381007e87a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Jun 2025 11:32:57 +0000 Subject: [PATCH 0776/1900] Add proper name mangling for pattern types --- compiler/rustc_symbol_mangling/src/v0.rs | 14 +++++------ src/doc/rustc/src/symbol-mangling/v0.md | 28 ++++++++++++++++++++++ tests/codegen-llvm/pattern_type_symbols.rs | 4 ++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 9fa7e2f100393..d24924b424a5b 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -262,15 +262,16 @@ impl<'tcx> V0SymbolMangler<'tcx> { fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> { Ok(match *pat { ty::PatternKind::Range { start, end } => { - let consts = [start, end]; - for ct in consts { - Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?; - } + self.push("R"); + self.print_const(start)?; + self.print_const(end)?; } ty::PatternKind::Or(patterns) => { + self.push("O"); for pat in patterns { self.print_pat(pat)?; } + self.push("E"); } }) } @@ -498,12 +499,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { } ty::Pat(ty, pat) => { - // HACK: Represent as tuple until we have something better. - // HACK: constants are used in arrays, even if the types don't match. - self.push("T"); + self.push("W"); ty.print(self)?; self.print_pat(pat)?; - self.push("E"); } ty::Array(ty, len) => { diff --git a/src/doc/rustc/src/symbol-mangling/v0.md b/src/doc/rustc/src/symbol-mangling/v0.md index 109942518fc93..2bcc453a53265 100644 --- a/src/doc/rustc/src/symbol-mangling/v0.md +++ b/src/doc/rustc/src/symbol-mangling/v0.md @@ -710,6 +710,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re [mut-ptr-type]: #mut-ptr-type [fn-type]: #fn-type [dyn-trait-type]: #dyn-trait-type +[pattern-type]: #pattern-type > type → \ >       *[basic-type]* \ @@ -722,6 +723,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re >    | *[mut-ptr-type]* \ >    | *[fn-type]* \ >    | *[dyn-trait-type]* \ +>    | *[pattern-type]* \ >    | *[path]* \ >    | *[backref]* @@ -830,6 +832,23 @@ Remaining primitives are encoded as a crate production, e.g. `C4f128`. [fn-sig]: #fn-sig [abi]: #abi +* `W` — A [pattern-type][pattern-tpye] `u32 is 0..100`. + > pattern-type → `W` *[pattern-kind]* + > + > pattern-kind → \ + >       *[range-pattern-kind]* \ + >    *[or-pattern-kind]* + > + > range-pattern-kind → `R` *[const]* *[const]* + > + > or-pattern-kind → `O` *[pattern-kind]* `E` + + While or patterns can be nested in theory, in practice this does not happen and they are instead flattened. + + Range patterns have a start and end constant that are both included in the range. + The end must be larger than the start (there can be no wraparound). To emulate wraparound, + you need to use an or pattern of the two ranges to the upper limit and from the lower limit. + * `D` — A [trait object][reference-trait-object] `dyn Trait + Send + 'a`. > dyn-trait-type → `D` *[dyn-bounds]* *[lifetime]* @@ -1139,6 +1158,7 @@ The following is a summary of all of the productions of the symbol grammar. >    | *[mut-ptr-type]* \ >    | *[fn-type]* \ >    | *[dyn-trait-type]* \ +>    | *[pattern-type]* \ >    | *[path]* \ >    | *[backref]* > @@ -1152,6 +1172,14 @@ The following is a summary of all of the productions of the symbol grammar. > [mut-ptr-type] → `O` *[type]* \ > [fn-type] → `F` *[fn-sig]* \ > [dyn-trait-type] → `D` *[dyn-bounds]* *[lifetime]* +> [pattern-type] → `W` *[pattern-kind]* +> +> [pattern-kind] → \ +>       *[range-pattern-kind]* \ +>    *[or-pattern-kind]* +> +> [range-pattern-kind] -> `R` *[const]* *[const]* \ +> [or-pattern-kind] -> `O` *[pattern-kind]* `E` \ > > [namespace] → *[lower]* | *[upper]* > diff --git a/tests/codegen-llvm/pattern_type_symbols.rs b/tests/codegen-llvm/pattern_type_symbols.rs index e86a9ef27de1b..a90262ff12d21 100644 --- a/tests/codegen-llvm/pattern_type_symbols.rs +++ b/tests/codegen-llvm/pattern_type_symbols.rs @@ -16,7 +16,7 @@ pub fn bar() { // CHECK: call pattern_type_symbols::foo:: // CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_ foo::(); - // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])> - // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_ + // CHECK: call pattern_type_symbols::foo:: + // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooWmRm0_m3b9ac9ff_EB2_ foo::(); } From 9d4130b1bb81fd53a74cc8c83f5f74682e828129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 23 Sep 2025 14:12:43 +0200 Subject: [PATCH 0777/1900] Use `rustc-josh-sync` merge commit message for pull PR description --- src/tools/miri/.github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 740118bb4a02b..3078cd35b1084 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -231,6 +231,9 @@ jobs: exit ${exitcode} fi + # Store merge commit message + git log -1 --pretty=%B > message.txt + # Format changes ./miri toolchain ./miri fmt --check || (./miri fmt && git commit -am "fmt") @@ -239,7 +242,7 @@ jobs: BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH - gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)." + gh pr create -B master --title 'Automatic Rustup' --body-file message.txt env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} From 526d794d807ca0e54d3411fed4c508b1c6f0f0e5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 01:34:05 +0200 Subject: [PATCH 0778/1900] create let-chain --- clippy_lints/src/non_canonical_impls.rs | 222 +++++++++++------------- 1 file changed, 106 insertions(+), 116 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index ba67dc62abbda..c479f40707e98 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -114,136 +114,126 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { - return; - }; - let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { - return; - }; - if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { - return; - } - let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind else { - return; - }; - let body = cx.tcx.hir_body(impl_item_id); - let ExprKind::Block(block, ..) = body.value.kind else { - return; - }; - if block.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, impl_item) { - return; - } - - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) + && let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind + && let body = cx.tcx.hir_body(impl_item_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + && !is_from_proc_macro(cx, impl_item) { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { + let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); + if trait_name == Some(sym::Clone) + && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) + && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + } else { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + + return; + } + } + + if impl_item.ident.name == sym::clone_from { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), Applicability::MaybeIncorrect, ); + } + } else if trait_name == Some(sym::PartialOrd) + && impl_item.ident.name == sym::partial_cmp + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) + { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { return; } - } - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( + span_lint_and_then( cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, - ); - } - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; - - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { - return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } - - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); + } } } } From 54e28471d707c86476f7519571eea74c0016d3c8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 01:31:18 +0200 Subject: [PATCH 0779/1900] extract helper functions too many lines no more! --- clippy_lints/src/non_canonical_impls.rs | 214 +++++++++++++----------- 1 file changed, 113 insertions(+), 101 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index c479f40707e98..5f2c388ff678f 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,9 +5,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::EarlyBinder; +use rustc_middle::ty::{EarlyBinder, TraitRef}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -112,7 +112,6 @@ declare_clippy_lint! { declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); impl LateLintPass<'_> for NonCanonicalImpls { - #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) @@ -128,114 +127,127 @@ impl LateLintPass<'_> for NonCanonicalImpls { && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); - - return; - } - } - - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, - ); - } + check_clone_on_copy(cx, impl_item, block); } else if trait_name == Some(sym::PartialOrd) && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; + check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + } + } + } +} - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { - return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } +fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &Block<'_>) { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + } else { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + } + } - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; + if impl_item.ident.name == sym::clone_from { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), + Applicability::MaybeIncorrect, + ); + } +} - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; +fn check_partial_ord_on_ord<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &ImplItem<'_>, + item: &Item<'_>, + trait_impl: &TraitRef<'_>, + body: &Body<'_>, + block: &Block<'tcx>, +) { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); - } - } + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; } + + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; + + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; + + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); } /// Return true if `expr_kind` is a `cmp` call. From 34d9a2a084b42533d508cbfe3207b91b2f014eba Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 23:17:32 +0200 Subject: [PATCH 0780/1900] use `match` imo it's a bit more clear than checking for the same thing (`expr.kind`) in multiple branches --- clippy_lints/src/non_canonical_impls.rs | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 5f2c388ff678f..7dd5157340305 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -258,26 +258,27 @@ fn expr_is_cmp<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { let impl_item_did = impl_item.owner_id.def_id; - if let ExprKind::Call( - Expr { - kind: ExprKind::Path(some_path), - hir_id: some_hir_id, - .. - }, - [cmp_expr], - ) = expr.kind - { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) => { + is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) - } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) - } else { - false + }, + ExprKind::MethodCall(_, recv, [], _) => { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + }, + _ => false, } } From 21a5948ca17787961c6dcae8964086ddadb4f1b8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 22:04:25 +0200 Subject: [PATCH 0781/1900] incorporate review feedback --- clippy_lints/src/non_canonical_impls.rs | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 7dd5157340305..e531f797272d2 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -113,16 +113,18 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind + && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) + // NOTE: check this early to avoid expensive checks that come after this one + && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind && let body = cx.tcx.hir_body(impl_item_id) && let ExprKind::Block(block, ..) = body.value.kind && !block.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, impl_item) { - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); if trait_name == Some(sym::Clone) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) @@ -147,17 +149,19 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B && let ExprKind::Path(qpath) = deref.kind && last_path_segment(&qpath).ident.name == kw::SelfLower { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); + // this is the canonical implementation, `fn clone(&self) -> Self { *self }` + return; } + + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); } if impl_item.ident.name == sym::clone_from { From 08020def99d2851af0dabde12cc6d203017fa72c Mon Sep 17 00:00:00 2001 From: Reuben Cruise Date: Wed, 10 Sep 2025 15:40:27 +0100 Subject: [PATCH 0782/1900] Changes some aarch64 CIs g++ install & ubuntu ver. GCS support was added to GCC in version 15, thus the rmake test for this patch requires GCC15 Similarly, the ubuntu version is updated so the newer clang version is available, and/or GCC15 is the default. --- src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile | 2 +- src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile | 2 +- src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile index 71de8f917fa2c..04b46226acfa4 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile index adbb1f03378a6..095624d6fb714 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:25.04 +FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index e6133fce83e28..4b61fd94a6cfa 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ From 220e137089943b3977cdac4755212646bded08dc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 14:34:38 +0200 Subject: [PATCH 0783/1900] split `unnecessary_clone` test into `clone_on_ref_ptr` and `clone_on_copy` --- tests/ui/clone_on_copy.fixed | 65 +++++++++++++---- tests/ui/clone_on_copy.rs | 65 +++++++++++++---- tests/ui/clone_on_copy.stderr | 26 ++++--- tests/ui/clone_on_ref_ptr.fixed | 51 ++++++++++++++ tests/ui/clone_on_ref_ptr.rs | 51 ++++++++++++++ tests/ui/clone_on_ref_ptr.stderr | 41 +++++++++++ tests/ui/unnecessary_clone.rs | 111 ------------------------------ tests/ui/unnecessary_clone.stderr | 62 ----------------- 8 files changed, 261 insertions(+), 211 deletions(-) create mode 100644 tests/ui/clone_on_ref_ptr.fixed create mode 100644 tests/ui/clone_on_ref_ptr.rs create mode 100644 tests/ui/clone_on_ref_ptr.stderr delete mode 100644 tests/ui/unnecessary_clone.rs delete mode 100644 tests/ui/unnecessary_clone.stderr diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 2dd8af1525150..469121bbd740f 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42; //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = *****a; + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = (*opt)?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index a371afb04a78f..b05f1d3aa35e9 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42.clone(); //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'.clone()); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42.clone()); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = opt.clone()?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 92cdd635d20a8..c87d1d488dde5 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:23:5 + --> tests/ui/clone_on_copy.rs:14:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -8,52 +8,58 @@ LL | 42.clone(); = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:28:5 + --> tests/ui/clone_on_copy.rs:19:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:32:5 + --> tests/ui/clone_on_copy.rs:23:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `u32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:36:5 + --> tests/ui/clone_on_copy.rs:27:5 | LL | x.clone().rotate_left(1); | ^^^^^^^^^ help: try removing the `clone` call: `x` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:51:5 + --> tests/ui/clone_on_copy.rs:42:5 | LL | m!(42).clone(); | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` error: using `clone` on type `[u32; 2]` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:62:5 + --> tests/ui/clone_on_copy.rs:53:5 | LL | x.clone()[0]; | ^^^^^^^^^ help: try dereferencing it: `(*x)` +error: using `clone` on type `E` which implements the `Copy` trait + --> tests/ui/clone_on_copy.rs:92:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + error: using `clone` on type `char` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:73:14 + --> tests/ui/clone_on_copy.rs:107:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:78:14 + --> tests/ui/clone_on_copy.rs:114:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:83:17 + --> tests/ui/clone_on_copy.rs:120:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? | ^^^^^^^^^^^ help: try dereferencing it: `(*opt)` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed new file mode 100644 index 0000000000000..8ef4b36566363 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Weak::::clone(&rc_weak); + //~^ clone_on_ref_ptr + std::sync::Arc::::clone(&arc); + //~^ clone_on_ref_ptr + std::sync::Weak::::clone(&arc_weak); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = std::sync::Arc::::clone(&x); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(std::rc::Rc::::clone(&try_opt!(Some(rc)))) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs new file mode 100644 index 0000000000000..fbd787099aee3 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.rs @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + rc.clone(); + //~^ clone_on_ref_ptr + rc_weak.clone(); + //~^ clone_on_ref_ptr + arc.clone(); + //~^ clone_on_ref_ptr + arc_weak.clone(); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = x.clone(); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr new file mode 100644 index 0000000000000..b15f0e803a352 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -0,0 +1,41 @@ +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:9:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:11:5 + | +LL | rc_weak.clone(); + | ^^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:13:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:15:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:30:33 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:48:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs deleted file mode 100644 index 7335b6f9f0390..0000000000000 --- a/tests/ui/unnecessary_clone.rs +++ /dev/null @@ -1,111 +0,0 @@ -// does not test any rustfixable lints -#![warn(clippy::clone_on_ref_ptr)] -#![allow(unused)] -#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] -//@no-rustfix -use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; - -trait SomeTrait {} -struct SomeImpl; -impl SomeTrait for SomeImpl {} - -fn main() {} - -fn clone_on_ref_ptr() { - let rc = Rc::new(true); - let arc = Arc::new(true); - - let rcweak = Rc::downgrade(&rc); - let arc_weak = Arc::downgrade(&arc); - - rc.clone(); - //~^ clone_on_ref_ptr - - Rc::clone(&rc); - - arc.clone(); - //~^ clone_on_ref_ptr - - Arc::clone(&arc); - - rcweak.clone(); - //~^ clone_on_ref_ptr - - rc::Weak::clone(&rcweak); - - arc_weak.clone(); - //~^ clone_on_ref_ptr - - sync::Weak::clone(&arc_weak); - - let x = Arc::new(SomeImpl); - let _: Arc = x.clone(); - //~^ clone_on_ref_ptr -} - -fn clone_on_copy_generic(t: T) { - t.clone(); - //~^ clone_on_copy - - Some(t).clone(); - //~^ clone_on_copy -} - -mod many_derefs { - struct A; - struct B; - struct C; - struct D; - #[derive(Copy, Clone)] - struct E; - - macro_rules! impl_deref { - ($src:ident, $dst:ident) => { - impl std::ops::Deref for $src { - type Target = $dst; - fn deref(&self) -> &Self::Target { - &$dst - } - } - }; - } - - impl_deref!(A, B); - impl_deref!(B, C); - impl_deref!(C, D); - impl std::ops::Deref for D { - type Target = &'static E; - fn deref(&self) -> &Self::Target { - &&E - } - } - - fn go1() { - let a = A; - let _: E = a.clone(); - //~^ clone_on_copy - - let _: E = *****a; - } -} - -mod issue2076 { - use std::rc::Rc; - - macro_rules! try_opt { - ($expr: expr) => { - match $expr { - Some(value) => value, - None => return None, - } - }; - } - - fn func() -> Option> { - let rc = Rc::new(42); - Some(try_opt!(Some(rc)).clone()) - //~^ clone_on_ref_ptr - } -} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr deleted file mode 100644 index 17518e123d12e..0000000000000 --- a/tests/ui/unnecessary_clone.stderr +++ /dev/null @@ -1,62 +0,0 @@ -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:23:5 - | -LL | rc.clone(); - | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` - | - = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:28:5 - | -LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:33:5 - | -LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:38:5 - | -LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:44:33 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` - -error: using `clone` on type `T` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:49:5 - | -LL | t.clone(); - | ^^^^^^^^^ help: try removing the `clone` call: `t` - | - = note: `-D clippy::clone-on-copy` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` - -error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:52:5 - | -LL | Some(t).clone(); - | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` - -error: using `clone` on type `E` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:87:20 - | -LL | let _: E = a.clone(); - | ^^^^^^^^^ help: try dereferencing it: `*****a` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:108:14 - | -LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` - -error: aborting due to 9 previous errors - From 1ec098878c5f7a6f5cda2a81093fd34b76f75e9e Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 8 Sep 2025 00:04:33 +0200 Subject: [PATCH 0784/1900] Expand GenMC mode: - Implement support for 'assume' statements. - Improve scheduling and next instruction type determination. - Improve GenMC mode documentation. Co-authored-by: Ralf Jung --- src/tools/miri/doc/genmc.md | 77 ++++++++++- .../genmc-sys/cpp/include/MiriInterface.hpp | 7 + .../cpp/src/MiriInterface/EventHandling.cpp | 6 + src/tools/miri/genmc-sys/src/lib.rs | 12 +- src/tools/miri/src/concurrency/genmc/dummy.rs | 43 +++--- .../miri/src/concurrency/genmc/intercept.rs | 38 ++++++ src/tools/miri/src/concurrency/genmc/mod.rs | 22 ++- .../miri/src/concurrency/genmc/scheduling.rs | 128 +++++++++++++----- src/tools/miri/src/concurrency/mod.rs | 2 +- src/tools/miri/src/concurrency/thread.rs | 17 ++- src/tools/miri/src/shims/foreign_items.rs | 12 ++ .../spinloop_assume.bounded123.stderr | 5 + .../spinloop_assume.bounded321.stderr | 5 + .../spinloop_assume.replaced123.stderr | 5 + .../spinloop_assume.replaced321.stderr | 5 + .../genmc/pass/intercept/spinloop_assume.rs | 98 ++++++++++++++ src/tools/miri/tests/utils/miri_extern.rs | 3 + 17 files changed, 405 insertions(+), 80 deletions(-) create mode 100644 src/tools/miri/src/concurrency/genmc/intercept.rs create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 44e11dcbec44c..3e5859c6f5b27 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -1,15 +1,26 @@ # **(WIP)** Documentation for Miri-GenMC +**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** + + [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. Miri-GenMC integrates that model checker into Miri. -**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** +Miri in GenMC mode takes a program as input like regular Miri, but instead of running it once, the program is executed repeatedly, until all possible executions allowed by the Rust memory model are explored. +This includes all possible thread interleavings and all allowed return values for atomic operations, including cases that are very rare to encounter on actual hardware. +(However, this does not include other sources of non-determinism, such as the absolute addresses of allocations. +It is hence still possible to have latent bugs in a test case even if they passed GenMC.) - +GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate. +Any loops that may run infinitely must be replaced or bounded (see below). + +GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads). +Large programs may not be verifiable in a reasonable amount of time. ## Usage For testing/developing Miri-GenMC: +- install all [dependencies required by GenMC](https://github.com/MPI-SWS/genmc?tab=readme-ov-file#dependencies) - clone the Miri repo. - build Miri-GenMC with `./miri build --features=genmc`. - OR: install Miri-GenMC in the current system with `./miri install --features=genmc` @@ -50,6 +61,68 @@ Note that `cargo miri test` in GenMC mode is currently not supported. +### Eliminating unbounded loops + +#### Limiting the number of explored executions + +The number of explored executions in a concurrent program can increase super-exponentially in the size of the program. +Reducing the number of explored executions is the most impactful way to improve verification times. +Some programs also contain possibly infinite loops, which are not supported by GenMC. + +One way to drastically improve verification performance is by bounding spinloops. +A spinloop may loop a many times before it can finally make progress. +If such a loop doesn't have any visible side effects, meaning it does not matter to the outcome of the program whether the loop ran once or a million time, then the loop can be limited to one iteration. + +The following code gives an example for how to replace a loop that waits for a boolean to be true. +Since there are no side effects, replacing the loop with one iteration is safe. + +```rust +#[cfg(miri)] +unsafe extern "Rust" { + // This is a special function that Miri provides. + // It blocks the thread calling this function if the condition is false. + pub unsafe fn miri_genmc_assume(condition: bool); +} + +/// This functions loads an atomic boolean in a loop until it is true. +/// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. +fn spin_until_true(flag: &AtomicBool) { + while (!flag.load(Relaxed)) { + std::hint::spin_loop(); + } +} + +/// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load. +/// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. +fn spin_until_true_genmc(flag: &AtomicBool) { + unsafe { miri_genmc_assume(flag.load(Relaxed)); } +} +``` + +Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program. +Replacing the loop with one iteration like we did above would mean we miss all those possible executions. + +In such a case, the loop can be limited to a fixed number of iterations instead. +The choice of iteration limit trades off verification time for possibly missing bugs requiring more iterations. + +```rust +/// The loop in this function has a side effect, which is to increment the counter for the number of iterations. +/// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations. +fn count_until_true_genmc(flag: &AtomicBool) -> u64 { + let mut count = 0; + while (!flag.load(Relaxed)) { + // Any execution that takes more than 3 iterations will not be explored. + unsafe { miri_genmc_assume(count < 3); } + count += 1; + std::hint::spin_loop(); + } + count +} +``` + + + + ## Limitations Some or all of these limitations might get removed in the future: diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 444c9375319af..8972341d491df 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -134,6 +134,13 @@ struct MiriGenmcShim : private GenMCDriver { void handle_thread_finish(ThreadId thread_id, uint64_t ret_val); void handle_thread_kill(ThreadId thread_id); + /**** Blocking instructions ****/ + /// Inform GenMC that the thread should be blocked. + /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user + /// supplied assume statements. This can become a parameter once more types of assumes are + /// added. + void handle_assume_block(ThreadId thread_id); + /***** Exploration related functionality *****/ /** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index 05c82641df948..fac7217ea5c8b 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -30,6 +30,12 @@ #include #include +/**** Blocking instructions ****/ + +void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { + GenMCDriver::handleAssume(inc_pos(thread_id), AssumeType::User); +} + /**** Memory access handling ****/ [[nodiscard]] auto MiriGenmcShim::handle_load( diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 733b3d780b187..385afb9eaba00 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -258,9 +258,11 @@ mod ffi { /// Corresponds to GenMC's type with the same name. /// Should only be modified if changed by GenMC. enum ActionKind { - /// Any Mir terminator that's atomic and has load semantics. + /// Any MIR terminator that's atomic and that may have load semantics. + /// This includes functions with atomic properties, such as `pthread_create`. + /// If the exact type of the terminator cannot be determined, load is a safe default `Load`. Load, - /// Anything that's not a `Load`. + /// Anything that's definitely not a `Load`. NonLoad, } @@ -412,6 +414,12 @@ mod ffi { fn handle_thread_finish(self: Pin<&mut MiriGenmcShim>, thread_id: i32, ret_val: u64); fn handle_thread_kill(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + /**** Blocking instructions ****/ + /// Inform GenMC that the thread should be blocked. + /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user supplied assume statements. + /// This can become a parameter once more types of assumes are added. + fn handle_assume_block(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + /***** Exploration related functionality *****/ /// Ask the GenMC scheduler for a new thread to schedule and diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index c28984cef35ad..e260af7c4a16b 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,11 +1,12 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; +pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, - ThreadId, ThreadManager, VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, OpTy, + Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, }; #[derive(Clone, Copy, Debug)] @@ -36,9 +37,27 @@ mod run { } } +mod intercept { + use super::*; + + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} + pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + unreachable!(); + } + } +} + impl GenmcCtx { // We don't provide the `new` function in the dummy module. + pub(crate) fn schedule_thread<'tcx>( + &self, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, Option> { + unreachable!() + } + /**** Memory access handling ****/ pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { @@ -191,26 +210,6 @@ impl GenmcCtx { ) -> InterpResult<'tcx> { unreachable!() } - - /**** Scheduling functionality ****/ - - pub fn schedule_thread<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - unreachable!() - } - - /**** Blocking instructions ****/ - - #[allow(unused)] - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - _machine: &MiriMachine<'tcx>, - _condition: bool, - ) -> InterpResult<'tcx, ()> { - unreachable!() - } } impl VisitProvenance for GenmcCtx { diff --git a/src/tools/miri/src/concurrency/genmc/intercept.rs b/src/tools/miri/src/concurrency/genmc/intercept.rs new file mode 100644 index 0000000000000..4867b1dc21afb --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/intercept.rs @@ -0,0 +1,38 @@ +use tracing::debug; + +use crate::concurrency::thread::EvalContextExt as _; +use crate::{ + BlockReason, InterpResult, MachineCallback, MiriInterpCx, OpTy, UnblockKind, VisitProvenance, + VisitWith, callback, interp_ok, +}; + +// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. + +/// Other functionality not directly related to event handling +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. + /// Returns `true` if the current thread should be blocked in Miri too. + fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let condition_bool = this.read_scalar(condition)?.to_bool()?; + debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); + if condition_bool { + return interp_ok(()); + } + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + genmc_ctx.handle_assume_block(&this.machine)?; + this.block_thread( + BlockReason::Genmc, + None, + callback!( + @capture<'tcx> {} + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + unreachable!("GenMC should never unblock a thread blocked by an `assume`."); + } + ), + ); + interp_ok(()) + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 0086d3f2bf0bc..3e8e8fbfd491a 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -29,6 +29,7 @@ use crate::{ mod config; mod global_allocations; mod helper; +mod intercept; mod run; pub(crate) mod scheduling; mod thread_id_map; @@ -36,6 +37,7 @@ mod thread_id_map; pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; +pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; #[derive(Debug)] @@ -732,17 +734,6 @@ impl GenmcCtx { self.exec_state.exit_status.set(Some(exit_status)); interp_ok(()) } - - /**** Blocking instructions ****/ - - #[allow(unused)] - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - machine: &MiriMachine<'tcx>, - condition: bool, - ) -> InterpResult<'tcx, ()> { - if condition { interp_ok(()) } else { self.handle_user_block(machine) } - } } impl GenmcCtx { @@ -903,8 +894,13 @@ impl GenmcCtx { /// Handle a user thread getting blocked. /// This may happen due to an manual `assume` statement added by a user /// or added by some automated program transformation, e.g., for spinloops. - fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - todo!() + fn handle_assume_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { + let curr_thread = machine.threads.active_thread(); + let genmc_curr_thread = + self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread); + debug!("GenMC: assume statement, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); + self.handle.borrow_mut().pin_mut().handle_assume_block(genmc_curr_thread); + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index b5c23f2d0845f..7fb3d8fa3ef39 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -1,58 +1,112 @@ use genmc_sys::{ActionKind, ExecutionState}; +use rustc_middle::mir::TerminatorKind; +use rustc_middle::ty::{self, Ty}; use super::GenmcCtx; use crate::{ InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop, }; +enum NextInstrKind { + MaybeAtomic(ActionKind), + NonAtomic, +} + +/// Check if a call or tail-call could have atomic load semantics. +fn get_next_instruction_kind<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, +) -> InterpResult<'tcx, NextInstrKind> { + use NextInstrKind::*; + + let thread_manager = &ecx.machine.threads; + + // Determine whether the next instruction in the current thread might be a load. + // This is used for the "writes-first" scheduling in GenMC. + // Scheduling writes before reads can be beneficial for verification performance. + // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. + if !thread_manager.active_thread_ref().is_enabled() { + // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. + // Most to all blocking operations have load semantics, since they wait on something to change in another thread, + // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), + // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). + // `Load` is a safe default for the next instruction type, since we may not know what the next instruction is. + return interp_ok(MaybeAtomic(ActionKind::Load)); + } + // This thread is still enabled. If it executes a terminator next, we consider yielding, + // but in all other cases we just keep running this thread since it never makes sense + // to yield before a non-atomic operation. + let Some(frame) = thread_manager.active_thread_stack().last() else { + return interp_ok(NonAtomic); + }; + let either::Either::Left(loc) = frame.current_loc() else { + // We are unwinding, so the next step is definitely not atomic. + return interp_ok(NonAtomic); + }; + let basic_block = &frame.body().basic_blocks[loc.block]; + if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + // Statements can't be atomic. + return interp_ok(NonAtomic); + } + match &basic_block.terminator().kind { + // All atomics are modeled as function calls to intrinsic functions. + // The one exception is thread joining, but those are also calls. + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => + get_function_kind(ecx, func.ty(&frame.body().local_decls, *ecx.tcx)), + // Non-call terminators are not atomic. + _ => interp_ok(NonAtomic), + } +} + +fn get_function_kind<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + func_ty: Ty<'tcx>, +) -> InterpResult<'tcx, NextInstrKind> { + use NextInstrKind::*; + let callee_def_id = match func_ty.kind() { + ty::FnDef(def_id, _args) => def_id, + _ => return interp_ok(MaybeAtomic(ActionKind::Load)), // we don't know the callee, might be pthread_join + }; + let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { + if ecx.tcx.is_foreign_item(*callee_def_id) { + // Some shims, like pthread_join, must be considered loads. So just consider them all loads, + // these calls are not *that* common. + return interp_ok(MaybeAtomic(ActionKind::Load)); + } + // The next step is a call to a regular Rust function. + return interp_ok(NonAtomic); + }; + let intrinsic_name = intrinsic_def.name.as_str(); + let Some(suffix) = intrinsic_name.strip_prefix("atomic_") else { + return interp_ok(NonAtomic); // Non-atomic intrinsic, so guaranteed not an atomic load + }; + // `atomic_store`, `atomic_fence` and `atomic_singlethreadfence` are not considered loads. + // Any future `atomic_*` intrinsics may have load semantics, so we err on the side of caution and classify them as "maybe loads". + interp_ok(MaybeAtomic(if matches!(suffix, "store" | "fence" | "singlethreadfence") { + ActionKind::NonLoad + } else { + ActionKind::Load + })) +} + impl GenmcCtx { + /// Returns the thread ID of the next thread to schedule, or `None` to continue with the current thread. pub(crate) fn schedule_thread<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - let thread_manager = &ecx.machine.threads; - let active_thread_id = thread_manager.active_thread(); - - // Determine whether the next instruction in the current thread might be a load. - // This is used for the "writes-first" scheduling in GenMC. - // Scheduling writes before reads can be beneficial for verification performance. - // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. - let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().is_enabled() { - // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. - // Most to all blocking operations have load semantics, since they wait on something to change in another thread, - // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), - // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). - ActionKind::Load - } else { - // This thread is still enabled. If it executes a terminator next, we consider yielding, - // but in all other cases we just keep running this thread since it never makes sense - // to yield before a non-atomic operation. - let Some(frame) = thread_manager.active_thread_stack().last() else { - return interp_ok(active_thread_id); - }; - let either::Either::Left(loc) = frame.current_loc() else { - // We are unwinding, so the next step is definitely not atomic. - return interp_ok(active_thread_id); - }; - let basic_block = &frame.body().basic_blocks[loc.block]; - if let Some(_statement) = basic_block.statements.get(loc.statement_index) { - // Statements can't be atomic. - return interp_ok(active_thread_id); - } - - // FIXME(genmc): determine terminator kind. - ActionKind::Load + ) -> InterpResult<'tcx, Option> { + let atomic_kind = match get_next_instruction_kind(ecx)? { + NextInstrKind::MaybeAtomic(atomic_kind) => atomic_kind, + NextInstrKind::NonAtomic => return interp_ok(None), // No need to reschedule on a non-atomic. }; + let active_thread_id = ecx.machine.threads.active_thread(); let thread_infos = self.exec_state.thread_id_manager.borrow(); let genmc_tid = thread_infos.get_genmc_tid(active_thread_id); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let result = pinned_mc.schedule_next(genmc_tid, curr_thread_next_instr_kind); + let result = self.handle.borrow_mut().pin_mut().schedule_next(genmc_tid, atomic_kind); // Depending on the exec_state, we either schedule the given thread, or we are finished with this execution. match result.exec_state { - ExecutionState::Ok => interp_ok(thread_infos.get_miri_tid(result.next_thread)), + ExecutionState::Ok => interp_ok(Some(thread_infos.get_miri_tid(result.next_thread))), ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution), ExecutionState::Finished => { let exit_status = self.exec_state.exit_status.get().expect( diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 9dae858592f1f..b20a17dd6989e 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, run_genmc_mode}; +pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, GenmcEvalContextExt, run_genmc_mode}; pub use self::vector_clock::VClock; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 00c5e337b1e9e..246300f8dda15 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -110,6 +110,9 @@ pub enum BlockReason { Eventfd, /// Blocked on unnamed_socket. UnnamedSocket, + /// Blocked for any reason related to GenMC, such as `assume` statements (GenMC mode only). + /// Will be implicitly unblocked when GenMC schedules this thread again. + Genmc, } /// The state of a thread. @@ -572,6 +575,7 @@ impl<'tcx> ThreadManager<'tcx> { /// See : /// > The handle is valid until closed, even after the thread it represents has been terminated. fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> { + // NOTE: In GenMC mode, we treat detached threads like regular threads that are never joined, so there is no special handling required here. trace!("detaching {:?}", id); let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() { @@ -705,11 +709,18 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // In GenMC mode, we let GenMC do the scheduling. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let next_thread_id = genmc_ctx.schedule_thread(this)?; - + let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { + return interp_ok(SchedulingAction::ExecuteStep); + }; + // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. + if this.machine.threads.threads[next_thread_id].state.is_blocked_on(BlockReason::Genmc) + { + info!("GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now."); + this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + } + // Set the new active thread. let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; - assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); return interp_ok(SchedulingAction::ExecuteStep); } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index eca8cccf5efc4..1d086906e7a59 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -16,6 +16,7 @@ use rustc_target::callconv::FnAbi; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; +use crate::concurrency::GenmcEvalContextExt as _; use crate::helpers::EvalContextExt as _; use crate::*; @@ -485,6 +486,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // GenMC mode: Assume statements block the current thread when their condition is false. + "miri_genmc_assume" => { + let [condition] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + if this.machine.data_race.as_genmc_ref().is_some() { + this.handle_genmc_verifier_assume(condition)?; + } else { + throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode") + } + } + // Aborting the process. "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr new file mode 100644 index 0000000000000..1458300b1110a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 232 executions. No errors found. +Number of complete executions explored: 108 +Number of blocked executions seen: 124 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr new file mode 100644 index 0000000000000..1458300b1110a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 232 executions. No errors found. +Number of complete executions explored: 108 +Number of blocked executions seen: 124 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr new file mode 100644 index 0000000000000..c79a87b33a5f5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 9 executions. No errors found. +Number of complete executions explored: 1 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr new file mode 100644 index 0000000000000..c79a87b33a5f5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 9 executions. No errors found. +Number of complete executions explored: 1 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs new file mode 100644 index 0000000000000..9d7c823b1e485 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs @@ -0,0 +1,98 @@ +//@ revisions: bounded123 bounded321 replaced123 replaced321 +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test uses GenMC assume statements to bound or replace spinloops. +// Three threads pass a value to each other, spinning on an atomic FLAG to wait for the previous thread. +// +// There are two variants, one limits the spinloop to three iterations, and one that completely replaces the spin loop. +// Without this loop bounding, this test *cannot* be verified, since GenMC will have to explore infinitely many executions (one per possible number of loop iterations). + +// FIXME(genmc): GenMC provides the `--unroll=N` option, which limits all loops to at most N iterations (at the LLVM IR level). +// Such an option for Miri would allow a variant of this test without manual bounding, using this automatic loop bounding instead. + +// We use different thread orders to ensure it doesn't just pass by chance (each thread order should give the same result). +// We use verbose output to see the number of explored vs blocked executions. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static mut X: u64 = 0; +static FLAG: AtomicU64 = AtomicU64::new(0); + +/// Unbounded variant of the spinloop. +/// This function causes GenMC to explore infinite executions. +#[allow(unused)] +fn spin_until_unbounded(value: u64) { + while FLAG.load(Acquire) != value { + std::hint::spin_loop(); + } +} + +#[cfg(any(bounded123, bounded321))] +/// We bound the loop to at most 3 iterations. +fn spin_until(value: u64) { + for _ in 0..3 { + if FLAG.load(Acquire) == value { + return; + } + } + unsafe { miri_genmc_assume(false) }; +} + +#[cfg(not(any(bounded123, bounded321)))] +/// For full replacement, we limit it to only 1 load. +fn spin_until(value: u64) { + unsafe { miri_genmc_assume(FLAG.load(Acquire) == value) }; +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + unsafe { X = 0 }; + FLAG.store(0, Relaxed); + + unsafe { + let t0 = || { + X = 42; + FLAG.store(1, Release); + + spin_until(3); + let c = X; + if c != 44 { + std::process::abort(); + } + }; + let t1 = || { + spin_until(1); + let a = X; + X = a + 1; + FLAG.store(2, Release); + }; + let t2 = || { + spin_until(2); + let b = X; + X = b + 1; + FLAG.store(3, Release); + }; + // Reverse the order for the second test variant. + #[cfg(any(bounded321, replaced321))] + let (t0, t1, t2) = (t2, t1, t0); + + spawn_pthread_closure(t0); + spawn_pthread_closure(t1); + spawn_pthread_closure(t2); + + 0 + } +} diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index d6c43b1882195..633f337f7e7cd 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -147,4 +147,7 @@ extern "Rust" { /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). pub fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); + + /// Blocks the current execution if the argument is false + pub fn miri_genmc_assume(condition: bool); } From 58c7505d868515eb55971cf1ad8744c81d5ea7d2 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 00:34:05 +0300 Subject: [PATCH 0785/1900] Make `render_example_with_highlighting` return an `impl fmt::Display` --- src/librustdoc/html/highlight.rs | 116 ++++++++++++++----------------- src/librustdoc/html/markdown.rs | 17 +++-- 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 0e06361024b9f..3ddfb3470f2f1 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -8,6 +8,7 @@ use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display, Write}; +use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; @@ -15,8 +16,9 @@ use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, DUMMY_SP, Span}; -use super::format::{self, write_str}; +use super::format; use crate::clean::PrimitiveType; +use crate::display::Joined as _; use crate::html::escape::EscapeBodyText; use crate::html::macro_expansion::ExpandedCode; use crate::html::render::{Context, LinkFromSrc}; @@ -51,26 +53,26 @@ pub(crate) enum Tooltip { /// Highlights `src` as an inline example, returning the HTML output. pub(crate) fn render_example_with_highlighting( src: &str, - out: &mut String, - tooltip: Tooltip, + tooltip: &Tooltip, playground_button: Option<&str>, extra_classes: &[String], -) { - write_header(out, "rust-example-rendered", None, tooltip, extra_classes); - write_code(out, src, None, None, None); - write_footer(out, playground_button); +) -> impl Display { + fmt::from_fn(move |f| { + write_header("rust-example-rendered", None, tooltip, extra_classes).fmt(f)?; + write_code(f, src, None, None, None); + write_footer(playground_button).fmt(f) + }) } fn write_header( - out: &mut String, class: &str, extra_content: Option<&str>, - tooltip: Tooltip, + tooltip: &Tooltip, extra_classes: &[String], -) { - write_str( - out, - format_args!( +) -> impl Display { + fmt::from_fn(move |f| { + write!( + f, "", playground_button.unwrap_or_default())); +fn write_footer(playground_button: Option<&str>) -> impl Display { + fmt::from_fn(move |f| write!(f, "{}", playground_button.unwrap_or_default())) } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 4addf2c3c9644..46923d1f69826 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -337,15 +337,14 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { // insert newline to clearly separate it from the // previous block so we can shorten the html output - let mut s = String::new(); - s.push('\n'); - - highlight::render_example_with_highlighting( - &text, - &mut s, - tooltip, - playground_button.as_deref(), - &added_classes, + let s = format!( + "\n{}", + highlight::render_example_with_highlighting( + &text, + &tooltip, + playground_button.as_deref(), + &added_classes, + ) ); Some(Event::Html(s.into())) } From aaff372f4b45f88dc3b28fe3c1c8ec22cb3b3411 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 01:04:04 +0300 Subject: [PATCH 0786/1900] Remove `Tooltip::None` variant, use `Option::None` --- src/librustdoc/html/highlight.rs | 23 +++++++++++------------ src/librustdoc/html/markdown.rs | 30 +++++++++++++++++------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 3ddfb3470f2f1..b1257999c070c 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -47,13 +47,12 @@ pub(crate) enum Tooltip { CompileFail, ShouldPanic, Edition(Edition), - None, } /// Highlights `src` as an inline example, returning the HTML output. pub(crate) fn render_example_with_highlighting( src: &str, - tooltip: &Tooltip, + tooltip: Option<&Tooltip>, playground_button: Option<&str>, extra_classes: &[String], ) -> impl Display { @@ -67,23 +66,24 @@ pub(crate) fn render_example_with_highlighting( fn write_header( class: &str, extra_content: Option<&str>, - tooltip: &Tooltip, + tooltip: Option<&Tooltip>, extra_classes: &[String], ) -> impl Display { fmt::from_fn(move |f| { write!( f, "
", - match tooltip { - Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", - Tooltip::CompileFail => " compile_fail", - Tooltip::ShouldPanic => " should_panic", - Tooltip::Edition(_) => " edition", - Tooltip::None => "", - } + tooltip + .map(|tooltip| match tooltip { + Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", + Tooltip::CompileFail => " compile_fail", + Tooltip::ShouldPanic => " should_panic", + Tooltip::Edition(_) => " edition", + }) + .unwrap_or_default() )?; - if *tooltip != Tooltip::None { + if let Some(tooltip) = tooltip { let tooltip = fmt::from_fn(|f| match tooltip { Tooltip::IgnoreAll => f.write_str("This example is not tested"), Tooltip::IgnoreSome(platforms) => { @@ -104,7 +104,6 @@ fn write_header( Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), Tooltip::ShouldPanic => f.write_str("This example panics"), Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), - Tooltip::None => unreachable!(), }); write!(f, "")?; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 46923d1f69826..7065de14c8ea7 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -321,18 +321,22 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { )) }); - let tooltip = if ignore == Ignore::All { - highlight::Tooltip::IgnoreAll - } else if let Ignore::Some(platforms) = ignore { - highlight::Tooltip::IgnoreSome(platforms) - } else if compile_fail { - highlight::Tooltip::CompileFail - } else if should_panic { - highlight::Tooltip::ShouldPanic - } else if explicit_edition { - highlight::Tooltip::Edition(edition) - } else { - highlight::Tooltip::None + let tooltip = { + use highlight::Tooltip::*; + + if ignore == Ignore::All { + Some(IgnoreAll) + } else if let Ignore::Some(platforms) = ignore { + Some(IgnoreSome(platforms)) + } else if compile_fail { + Some(CompileFail) + } else if should_panic { + Some(ShouldPanic) + } else if explicit_edition { + Some(Edition(edition)) + } else { + None + } }; // insert newline to clearly separate it from the @@ -341,7 +345,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { "\n{}", highlight::render_example_with_highlighting( &text, - &tooltip, + tooltip.as_ref(), playground_button.as_deref(), &added_classes, ) From e697f206fe4eba49f8984ef2d436bc0cd19ff8fc Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 01:04:14 +0300 Subject: [PATCH 0787/1900] Remove unused param from `write_header` --- src/librustdoc/html/highlight.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index b1257999c070c..fe2b2fc603120 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -57,18 +57,13 @@ pub(crate) fn render_example_with_highlighting( extra_classes: &[String], ) -> impl Display { fmt::from_fn(move |f| { - write_header("rust-example-rendered", None, tooltip, extra_classes).fmt(f)?; + write_header("rust-example-rendered", tooltip, extra_classes).fmt(f)?; write_code(f, src, None, None, None); write_footer(playground_button).fmt(f) }) } -fn write_header( - class: &str, - extra_content: Option<&str>, - tooltip: Option<&Tooltip>, - extra_classes: &[String], -) -> impl Display { +fn write_header(class: &str, tooltip: Option<&Tooltip>, extra_classes: &[String]) -> impl Display { fmt::from_fn(move |f| { write!( f, @@ -109,10 +104,6 @@ fn write_header( write!(f, "")?; } - if let Some(extra) = extra_content { - f.write_str(extra)?; - } - let classes = fmt::from_fn(|f| { iter::once("rust") .chain(Some(class).filter(|class| !class.is_empty())) From 38a2829c97e07e7a403d59424403561de4f91a3a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 16:58:45 +0200 Subject: [PATCH 0788/1900] doc(trait_checking): use `is_some_and` --- book/src/development/trait_checking.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index 6d01496eebe07..c6f6f6bd2f99e 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -24,12 +24,11 @@ use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]) - }); - if implements_iterator { - // [...] - } + let implements_iterator = (cx.tcx.get_diagnostic_item(sym::Iterator)) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])); + if implements_iterator { + // [...] + } } } From 4fcafc9daad24e5811546896b80e4e18578bf9f3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 08:25:07 +0200 Subject: [PATCH 0789/1900] yeet fastpath --- compiler/rustc_trait_selection/src/infer.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 4c50c44b84182..cd076d1cb692a 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -9,7 +9,7 @@ use rustc_middle::infer::canonical::{ Canonical, CanonicalQueryInput, CanonicalQueryResponse, QueryResponse, }; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; +use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::DUMMY_SP; use tracing::instrument; @@ -31,19 +31,7 @@ impl<'tcx> InferCtxt<'tcx> { fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let ty = self.resolve_vars_if_possible(ty); - - // FIXME(#132279): This should be removed as it causes us to incorrectly - // handle opaques in their defining scope, and stalled coroutines. - if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() { - return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); - } - let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, DUMMY_SP); - - // This can get called from typeck (by euv), and `moves_by_default` - // rightly refuses to work with inference variables, but - // moves_by_default has a cache, which we want to use in other - // cases. traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } From 83532f8544e18dfc2025ab33e7c01fb27865667f Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 17:08:29 +0200 Subject: [PATCH 0790/1900] remove test for no_core --- .../missing-copy-lang-item-issue-19660.rs | 19 ------------------- .../missing-copy-lang-item-issue-19660.stderr | 8 -------- 2 files changed, 27 deletions(-) delete mode 100644 tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs delete mode 100644 tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs deleted file mode 100644 index 35d5d079c6894..0000000000000 --- a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(lang_items, no_core)] -#![no_core] -#![no_main] - -#[lang = "pointee_sized"] -pub trait PointeeSized {} - -#[lang = "meta_sized"] -pub trait MetaSized: PointeeSized {} - -#[lang = "sized"] -trait Sized: MetaSized { } - -struct S; - -#[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { - argc //~ ERROR requires `copy` lang_item -} diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr deleted file mode 100644 index 7b9541f734fa8..0000000000000 --- a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: requires `copy` lang_item - --> $DIR/missing-copy-lang-item-issue-19660.rs:18:5 - | -LL | argc - | ^^^^ - -error: aborting due to 1 previous error - From af1b14bb9b1b3d301c1e8e0a989d814deaf054c9 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 23 Sep 2025 17:13:35 +0200 Subject: [PATCH 0791/1900] std: move WinSock abstractions to `sys::pal` --- .../src/sys/net/connection/socket/windows.rs | 80 +------------------ library/std/src/sys/pal/windows/mod.rs | 1 + library/std/src/sys/pal/windows/winsock.rs | 80 +++++++++++++++++++ 3 files changed, 84 insertions(+), 77 deletions(-) create mode 100644 library/std/src/sys/pal/windows/winsock.rs diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index b71d8b1357b5a..5b6f4cedf1b77 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; -use crate::sync::atomic::Atomic; -use crate::sync::atomic::Ordering::{AcqRel, Relaxed}; use crate::sys::c; +use crate::sys::pal::winsock::last_error; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -112,84 +111,11 @@ pub(super) mod netc { } } +pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init}; + #[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); -static WSA_INITIALIZED: Atomic = Atomic::::new(false); - -/// Checks whether the Windows socket interface has been started already, and -/// if not, starts it. -#[inline] -pub fn init() { - if !WSA_INITIALIZED.load(Relaxed) { - wsa_startup(); - } -} - -#[cold] -fn wsa_startup() { - unsafe { - let mut data: c::WSADATA = mem::zeroed(); - let ret = c::WSAStartup( - 0x202, // version 2.2 - &mut data, - ); - assert_eq!(ret, 0); - if WSA_INITIALIZED.swap(true, AcqRel) { - // If another thread raced with us and called WSAStartup first then call - // WSACleanup so it's as though WSAStartup was only called once. - c::WSACleanup(); - } - } -} - -pub fn cleanup() { - // We don't need to call WSACleanup here because exiting the process will cause - // the OS to clean everything for us, which is faster than doing it manually. - // See #141799. -} - -/// Returns the last error from the Windows socket interface. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) -/// and if so, returns the last error from the Windows socket interface. This -/// function must be called before another call to the socket API is made. -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { Ok(()) } else { Err(last_error()) } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - impl Socket { pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { let family = match *addr { diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3357946b8f71d..85a4d3ca6db27 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -30,6 +30,7 @@ cfg_select! { pub use self::stack_overflow_uwp as stack_overflow; } } +pub mod winsock; /// Map a [`Result`] to [`io::Result`](crate::io::Result). pub trait IoResult { diff --git a/library/std/src/sys/pal/windows/winsock.rs b/library/std/src/sys/pal/windows/winsock.rs new file mode 100644 index 0000000000000..b110a43ef3aa8 --- /dev/null +++ b/library/std/src/sys/pal/windows/winsock.rs @@ -0,0 +1,80 @@ +use super::c; +use crate::ffi::c_int; +use crate::sync::atomic::Atomic; +use crate::sync::atomic::Ordering::{AcqRel, Relaxed}; +use crate::{io, mem}; + +static WSA_STARTED: Atomic = Atomic::::new(false); + +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +#[inline] +pub fn startup() { + if !WSA_STARTED.load(Relaxed) { + wsa_startup(); + } +} + +#[cold] +fn wsa_startup() { + unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup( + 0x202, // version 2.2 + &mut data, + ); + assert_eq!(ret, 0); + if WSA_STARTED.swap(true, AcqRel) { + // If another thread raced with us and called WSAStartup first then call + // WSACleanup so it's as though WSAStartup was only called once. + c::WSACleanup(); + } + } +} + +pub fn cleanup() { + // We don't need to call WSACleanup here because exiting the process will cause + // the OS to clean everything for us, which is faster than doing it manually. + // See #141799. +} + +/// Returns the last error from the Windows socket interface. +pub fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) +/// and if so, returns the last error from the Windows socket interface. This +/// function must be called before another call to the socket API is made. +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { Ok(()) } else { Err(last_error()) } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} From 42cf78f762ecad22de89d914904887afc073278f Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Mon, 22 Sep 2025 15:44:30 -0400 Subject: [PATCH 0792/1900] llvm: update remarks support on LLVM 22 LLVM change dfbd76bda01e removed separate remark support entirely, but it turns out we can just drop the parameter and everything appears to work fine. Fixes 146912 as far as I can tell (the test passes.) @rustbot label llvm-main --- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 414274f24fb50..ef85ce153b88c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1779,9 +1779,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( // Do not delete the file after we gather remarks RemarkFile->keep(); +#if LLVM_VERSION_GE(22, 0) + auto RemarkSerializer = remarks::createRemarkSerializer( + llvm::remarks::Format::YAML, RemarkFile->os()); +#else auto RemarkSerializer = remarks::createRemarkSerializer( llvm::remarks::Format::YAML, remarks::SerializerMode::Separate, RemarkFile->os()); +#endif if (Error E = RemarkSerializer.takeError()) { std::string Error = std::string("Cannot create remark serializer: ") + toString(std::move(E)); From 60b35635e8084be2e4f8b55f170e8665edb8e275 Mon Sep 17 00:00:00 2001 From: ash Date: Tue, 23 Sep 2025 12:37:59 -0600 Subject: [PATCH 0793/1900] revert change removing `has_infer` check. Commit conservatively patches for now, but more development proceeding. Also contains a more concise test --- compiler/rustc_middle/src/ty/util.rs | 11 ++++++++++- tests/ui/type-inference/box_has_sigdrop.rs | 9 +++++++++ tests/ui/type-inference/box_has_sigdrop.stderr | 17 +++++++++++++++++ .../{has_sigdrop.rs => dropper_has_sigdrop.rs} | 0 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/ui/type-inference/box_has_sigdrop.rs create mode 100644 tests/ui/type-inference/box_has_sigdrop.stderr rename tests/ui/type-inference/{has_sigdrop.rs => dropper_has_sigdrop.rs} (100%) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 4f039381e500d..a4422abc6883a 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1368,7 +1368,6 @@ impl<'tcx> Ty<'tcx> { /// 2229 drop reorder migration analysis. #[inline] pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { - assert!(!self.has_non_region_infer()); // Avoid querying in simple cases. match needs_drop_components(tcx, self) { Err(AlwaysRequiresDrop) => true, @@ -1381,6 +1380,16 @@ impl<'tcx> Ty<'tcx> { _ => self, }; + // FIXME + // We should be canonicalizing, or else moving this to a method of inference + // context, or *something* like that, + // but for now just avoid passing inference variables + // to queries that can't cope with them. + // Instead, conservatively return "true" (may change drop order). + if query_ty.has_infer() { + return true; + } + // This doesn't depend on regions, so try to minimize distinct // query keys used. let erased = tcx.normalize_erasing_regions(typing_env, query_ty); diff --git a/tests/ui/type-inference/box_has_sigdrop.rs b/tests/ui/type-inference/box_has_sigdrop.rs new file mode 100644 index 0000000000000..3e801197a78e6 --- /dev/null +++ b/tests/ui/type-inference/box_has_sigdrop.rs @@ -0,0 +1,9 @@ +//@ should-fail +//@ compile-flags: -Wrust-2021-incompatible-closure-captures +// Inference, canonicalization, and significant drops should work nicely together. +// Related issue: #86868 + +fn main() { + let mut state = 0; + Box::new(move || state) +} diff --git a/tests/ui/type-inference/box_has_sigdrop.stderr b/tests/ui/type-inference/box_has_sigdrop.stderr new file mode 100644 index 0000000000000..b61b6322c107f --- /dev/null +++ b/tests/ui/type-inference/box_has_sigdrop.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/box_has_sigdrop.rs:8:5 + | +LL | fn main() { + | - expected `()` because of default return type +LL | let mut state = 0; +LL | Box::new(move || state) + | ^^^^^^^^^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `Box<{closure@box_has_sigdrop.rs:8:14}>` + | + = note: expected unit type `()` + found struct `Box<{closure@$DIR/box_has_sigdrop.rs:8:14: 8:21}>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-inference/has_sigdrop.rs b/tests/ui/type-inference/dropper_has_sigdrop.rs similarity index 100% rename from tests/ui/type-inference/has_sigdrop.rs rename to tests/ui/type-inference/dropper_has_sigdrop.rs From 489f0179e789620c1b94b2451749a4d1ed7b80f4 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 23 Sep 2025 02:51:21 -0400 Subject: [PATCH 0794/1900] Use lower_nextsolver::callable_item_signature instead of lower::callable_item_signature --- .../rust-analyzer/crates/hir-ty/src/db.rs | 17 ++-- .../crates/hir-ty/src/display.rs | 6 +- .../crates/hir-ty/src/dyn_compatibility.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 90 ++----------------- .../crates/hir-ty/src/method_resolution.rs | 15 +++- .../crates/hir-ty/src/mir/lower.rs | 14 ++- .../crates/hir-ty/src/next_solver/interner.rs | 2 +- .../crates/hir-ty/src/next_solver/mapping.rs | 45 ++++++++++ .../hir-ty/src/tests/method_resolution.rs | 2 +- .../crates/hir-ty/src/tests/traits.rs | 14 +-- .../crates/hir-ty/src/variance.rs | 19 ++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 80 ++++++++++++++--- .../ide-completion/src/context/analysis.rs | 13 ++- .../crates/ide/src/inlay_hints/bind_pat.rs | 2 +- .../crates/ide/src/signature_help.rs | 4 +- 16 files changed, 188 insertions(+), 141 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 6eb5f8defaa1b..84c6c0471b5b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -16,8 +16,8 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, + Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, drop::DropGlue, dyn_compatibility::DynCompatibilityViolation, @@ -167,8 +167,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::transparent] fn field_types(&self, var: VariantId) -> Arc>>; - #[salsa::invoke(crate::lower::callable_item_signature_query)] - fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; + #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] + fn callable_item_signature<'db>( + &'db self, + def: CallableDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; #[salsa::invoke(crate::lower::return_type_impl_traits)] fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; @@ -354,12 +357,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ArenaMap>>, >; - #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] - fn callable_item_signature_ns<'db>( - &'db self, - def: CallableDefId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; - #[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)] fn return_type_impl_traits_ns<'db>( &'db self, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index dc42304e1cab2..e11ce51cdb8f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -46,8 +46,8 @@ use span::Edition; use stdx::never; use triomphe::Arc; -use crate::next_solver::infer::DbInternerInferExt; use crate::next_solver::infer::traits::ObligationCause; +use crate::next_solver::{infer::DbInternerInferExt, mapping::NextSolverToChalk}; use crate::{ AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, @@ -1298,7 +1298,9 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { let def = def.0; let sig = db .callable_item_signature(def) - .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); if f.display_kind.is_source_code() { // `FnDef` is anonymous and there's no surface syntax for it. Show it as a diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index b87c998217741..f033c511acd6a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -329,7 +329,7 @@ where cb(MethodViolationCode::AsyncFn)?; } - let sig = db.callable_item_signature_ns(func.into()); + let sig = db.callable_item_signature(func.into()); if sig .skip_binder() .inputs() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 05c5a7a28b9a9..c2acfc4142e89 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -578,8 +578,10 @@ impl CallableSig { pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig { let callable_def = ToChalk::from_chalk(db, def); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); let sig = db.callable_item_signature(callable_def); - sig.substitute(Interner, substs) + sig.instantiate(interner, args).skip_binder().to_chalk(interner) } pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { CallableSig { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 756769a2174f1..598fd38b4b8e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -24,9 +24,9 @@ use chalk_ir::{ use either::Either; use hir_def::{ - AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, - GenericDefId, GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, - StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, + AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, + TypeOrConstParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, @@ -45,10 +45,10 @@ use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, - FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, - LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, - TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, + AliasTy, Binders, BoundVar, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, FnSubst, + ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, LifetimeOutlives, + QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitRef, TraitRefExt, Ty, + TyBuilder, TyKind, WhereClause, all_super_traits, consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, db::HirDatabase, error_lifetime, @@ -824,15 +824,6 @@ impl<'a> TyLoweringContext<'a> { } } -/// Build the signature of a callable item (function, struct or enum variant). -pub(crate) fn callable_item_signature_query(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig { - match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), - CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), - } -} - fn named_associated_type_shorthand_candidates( db: &dyn HirDatabase, // If the type parameter is defined in an impl and we're in a method, there @@ -1312,73 +1303,6 @@ pub(crate) fn generic_defaults_with_diagnostics_cycle_result( (GenericDefaults(None), None) } -fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { - let data = db.function_signature(def); - let resolver = def.resolver(db); - let mut ctx_params = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_fn_params(&data), - ) - .with_type_param_mode(ParamLoweringMode::Variable); - let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); - - let ret = match data.ret_type { - Some(ret_type) => { - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_fn_ret(), - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); - ctx_ret.lower_ty(ret_type) - } - None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner), - }; - let generics = generics(db, def.into()); - let sig = CallableSig::from_params_and_return( - params, - ret, - data.is_varargs(), - if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, - data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - ); - make_binders(db, &generics, sig) -} - -fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { - let field_tys = db.field_types(def.into()); - let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); - let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); - Binders::new( - binders, - CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall), - ) -} - -fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig { - let field_tys = db.field_types(def.into()); - let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); - let parent = def.lookup(db).parent; - let (ret, binders) = type_for_adt(db, parent.into()).into_value_and_skipped_binders(); - Binders::new( - binders, - CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall), - ) -} - -fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders { - let generics = generics(db, adt.into()); - let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let ty = TyKind::Adt(crate::AdtId(adt), subst).intern(Interner); - make_binders(db, &generics, ty) -} - pub(crate) fn type_for_type_alias_with_diagnostics_query( db: &dyn HirDatabase, t: TypeAliasId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 66687490b4a45..93ef64272d14b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1746,9 +1746,13 @@ fn is_valid_trait_method_candidate( .fill_with_inference_vars(table) .build(); + let args: crate::next_solver::GenericArgs<'_> = + fn_subst.to_nextsolver(table.interner); let sig = db.callable_item_signature(fn_id.into()); - let expected_receiver = - sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + let expected_receiver = sig + .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0]) + .instantiate(table.interner, args) + .to_chalk(table.interner); // FIXME: Clean up this mess with some context struct like rustc's `ProbeContext` let variance = match mode { @@ -1833,9 +1837,12 @@ fn is_valid_impl_fn_candidate( let fn_subst: crate::Substitution = table.infer_ctxt.fresh_args_for_item(fn_id.into()).to_chalk(table.interner); + let args: crate::next_solver::GenericArgs<'_> = fn_subst.to_nextsolver(table.interner); let sig = db.callable_item_signature(fn_id.into()); - let expected_receiver = - sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + let expected_receiver = sig + .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0]) + .instantiate(table.interner, args) + .to_chalk(table.interner); check_that!(table.unify(receiver_ty, &expected_receiver)); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 0416f4315d4dd..3e44e8c68ddaa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -43,7 +43,10 @@ use crate::{ Terminator, TerminatorKind, TupleFieldId, Ty, UnOp, VariantId, intern_const_scalar, return_slot, }, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, static_lifetime, traits::FnTrait, utils::ClosureSubst, @@ -2207,8 +2210,13 @@ pub fn lower_to_mir( // otherwise it's an inline const, and has no parameter if let DefWithBodyId::FunctionId(fid) = owner { let substs = TyBuilder::placeholder_subst(db, fid); - let callable_sig = - db.callable_item_signature(fid.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(fid.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let mut params = callable_sig.params().iter(); let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone()))); break 'b ctx.lower_params_and_bindings( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 3f61b9f02621b..1a14ac9a7457b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1225,7 +1225,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::FunctionId, ) -> EarlyBinder>> { - self.db().callable_item_signature_ns(def_id.0) + self.db().callable_item_signature(def_id.0) } fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 5dcb1e62329d0..f3f74f67c04de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -575,6 +575,17 @@ impl< } } +impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> + NextSolverToChalk<'db, chalk_ir::Binders> for rustc_type_ir::Binder, T> +{ + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Binders { + chalk_ir::Binders::new( + self.bound_vars().to_chalk(interner), + self.skip_binder().to_chalk(interner), + ) + } +} + impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds { BoundVarKinds::new_from_iter( @@ -584,6 +595,12 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds NextSolverToChalk<'db, chalk_ir::VariableKinds> for BoundVarKinds { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKinds { + chalk_ir::VariableKinds::from_iter(Interner, self.iter().map(|v| v.to_chalk(interner))) + } +} + impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind { match self { @@ -594,6 +611,18 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind NextSolverToChalk<'db, chalk_ir::VariableKind> for BoundVarKind { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKind { + match self { + BoundVarKind::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + BoundVarKind::Region(_) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => { + chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)) + } + } + } +} + impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> { match self.data(Interner) { @@ -1233,6 +1262,22 @@ where } } +impl<'db> NextSolverToChalk<'db, crate::CallableSig> for rustc_type_ir::FnSig> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::CallableSig { + crate::CallableSig { + abi: self.abi, + is_varargs: self.c_variadic, + safety: match self.safety { + super::abi::Safety::Safe => chalk_ir::Safety::Safe, + super::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + params_and_return: triomphe::Arc::from_iter( + self.inputs_and_output.iter().map(|ty| convert_ty_for_result(interner, ty)), + ), + } + } +} + pub fn convert_canonical_args_for_result<'db>( interner: DbInterner<'db>, args: Canonical<'db, Vec>>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 6a566a505579c..5ada14bc5342e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -2053,7 +2053,7 @@ impl dyn Error + Send { // ^^^^ expected Box, got Box // FIXME, type mismatch should not occur ::downcast(err).map_err(|_| loop {}) - //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> + //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> } } "#, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 41f8d4ed555f2..66faac09cc299 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1487,8 +1487,8 @@ fn test(x: Box>, y: &dyn Trait) { 268..269 'x': Box + '?> 275..276 'y': &'? (dyn Trait + '?) 286..287 'z': Box + '?> - 290..293 'bar': fn bar() -> Box + '?> - 290..295 'bar()': Box + '?> + 290..293 'bar': fn bar() -> Box + 'static> + 290..295 'bar()': Box + 'static> 301..302 'x': Box + '?> 301..308 'x.foo()': u64 314..315 'y': &'? (dyn Trait + '?) @@ -1535,7 +1535,7 @@ fn test(s: S) { 251..252 's': S 267..289 '{ ...z(); }': () 273..274 's': S - 273..280 's.bar()': &'? (dyn Trait + '?) + 273..280 's.bar()': &'? (dyn Trait + 'static) 273..286 's.bar().baz()': (u32, i32) "#]], ); @@ -1568,8 +1568,8 @@ fn test(x: Trait, y: &Trait) -> u64 { 106..107 'x': dyn Trait + '? 113..114 'y': &'? (dyn Trait + '?) 124..125 'z': dyn Trait + '? - 128..131 'bar': fn bar() -> dyn Trait + '? - 128..133 'bar()': dyn Trait + '? + 128..131 'bar': fn bar() -> dyn Trait + 'static + 128..133 'bar()': dyn Trait + 'static 139..140 'x': dyn Trait + '? 139..146 'x.foo()': u64 152..153 'y': &'? (dyn Trait + '?) @@ -1597,7 +1597,7 @@ fn main() { 47..48 '_': &'? (dyn Fn(S) + '?) 58..60 '{}': () 71..105 '{ ...()); }': () - 77..78 'f': fn f(&'? (dyn Fn(S) + '?)) + 77..78 'f': fn f(&'? (dyn Fn(S) + 'static)) 77..102 'f(&|nu...foo())': () 79..101 '&|numb....foo()': &'? impl Fn(S) 80..101 '|numbe....foo()': impl Fn(S) @@ -2952,7 +2952,7 @@ fn test(x: &dyn Foo) { 34..36 '{}': () 46..47 'x': &'? (dyn Foo + '?) 59..74 '{ foo(x); }': () - 65..68 'foo': fn foo(&'? (dyn Foo + '?)) + 65..68 'foo': fn foo(&'? (dyn Foo + 'static)) 65..71 'foo(x)': () 69..70 'x': &'? (dyn Foo + '?) "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 8593dba301b85..a17cf3782701a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -15,6 +15,8 @@ use crate::db::HirDatabase; use crate::generics::{Generics, generics}; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, LifetimeData, Ty, TyKind, @@ -238,14 +240,15 @@ impl Context<'_> { } GenericDefId::FunctionId(f) => { let subst = self.generics.placeholder_subst(self.db); - self.add_constraints_from_sig( - self.db - .callable_item_signature(f.into()) - .substitute(Interner, &subst) - .params_and_return - .iter(), - Variance::Covariant, - ); + let interner = DbInterner::new_with(self.db, None, None); + let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); + let sig = self + .db + .callable_item_signature(f.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); + self.add_constraints_from_sig(sig.params_and_return.iter(), Variance::Covariant); } _ => {} } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8b440611e6ccd..709d165e0b8b0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2287,7 +2287,13 @@ impl Function { pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.resolver(db); let substs = TyBuilder::placeholder_subst(db, self.id); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let ty = TyKind::Function(callable_sig.to_fn_ptr()).intern(Interner); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -2296,8 +2302,14 @@ impl Function { pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.resolver(db); let substs = TyBuilder::placeholder_subst(db, self.id); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); - let ty = callable_sig.ret().clone(); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .output() + .to_chalk(interner); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -2326,8 +2338,14 @@ impl Function { parent_id.map(|id| TyBuilder::subst_for_def(db, id, None).fill(&mut filler).build()); let substs = TyBuilder::subst_for_def(db, self.id, parent_substs).fill(&mut filler).build(); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); - let ty = callable_sig.ret().clone(); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .output() + .to_chalk(interner); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -2337,8 +2355,14 @@ impl Function { } let resolver = self.id.resolver(db); let substs = TyBuilder::placeholder_subst(db, self.id); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); - let ret_ty = callable_sig.ret().clone(); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ret_ty = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .output() + .to_chalk(interner); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 { return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into(); @@ -2358,7 +2382,13 @@ impl Function { pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec> { let environment = db.trait_environment(self.id.into()); let substs = TyBuilder::placeholder_subst(db, self.id); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); callable_sig .params() .iter() @@ -2386,7 +2416,13 @@ impl Function { pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec> { let environment = db.trait_environment(self.id.into()); let substs = TyBuilder::placeholder_subst(db, self.id); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 }; callable_sig .params() @@ -2436,7 +2472,13 @@ impl Function { GenericArg::new(Interner, GenericArgData::Ty(ty)) }) .build(); - let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.id.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 }; callable_sig .params() @@ -2731,8 +2773,13 @@ impl SelfParam { pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { let substs = TyBuilder::placeholder_subst(db, self.func); - let callable_sig = - db.callable_item_signature(self.func.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.func.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let environment = db.trait_environment(self.func.into()); let ty = callable_sig.params()[0].clone(); Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() } @@ -2764,8 +2811,13 @@ impl SelfParam { let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); let substs = TyBuilder::subst_for_def(db, self.func, Some(parent_substs)).fill(&mut filler).build(); - let callable_sig = - db.callable_item_signature(self.func.into()).substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let callable_sig = db + .callable_item_signature(self.func.into()) + .instantiate(interner, args) + .skip_binder() + .to_chalk(interner); let environment = db.trait_environment(self.func.into()); let ty = callable_sig.params()[0].clone(); Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index cdb1ad8b38e71..77a94403abb94 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1,6 +1,7 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; +use base_db::salsa; use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant}; use ide_db::{RootDatabase, active_parameter::ActiveParameter}; use itertools::Either; @@ -85,9 +86,15 @@ pub(super) fn expand_and_analyze<'db>( let original_offset = expansion.original_offset + relative_offset; let token = expansion.original_file.token_at_offset(original_offset).left_biased()?; - analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| { - AnalysisResult { analysis, expected, qualifier_ctx, token, original_offset } - }) + salsa::attach(sema.db, || analyze(sema, expansion, original_token, &token)).map( + |(analysis, expected, qualifier_ctx)| AnalysisResult { + analysis, + expected, + qualifier_ctx, + token, + original_offset, + }, + ) } fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 104740cbbf74a..b7c124139608b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -380,7 +380,7 @@ fn main() { let foo = foo4(); // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + // ^^^ &dyn Fn(&(dyn Fn(f64, f64) -> u32 + 'static), f64) -> u32 let foo = foo6(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo7(); diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index e74d997e97c52..6f55886adca48 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -1097,8 +1097,8 @@ fn foo(mut r: impl WriteHandler<()>) { By default this method stops actor's `Context`. ------ - fn finished(&mut self, ctx: &mut as Actor>::Context) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + fn finished(&mut self, ctx: &mut as Actor>::Context<()>) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "#]], ); } From 8337750474dce91d285accf351c561582c1ede25 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 23 Sep 2025 03:47:30 -0400 Subject: [PATCH 0795/1900] Remove all non-ns diagnostics queries, naming consistenly --- .../rust-analyzer/crates/hir-ty/src/db.rs | 132 ++++++++---------- .../crates/hir-ty/src/dyn_compatibility.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 64 ++------- .../crates/hir-ty/src/lower_nextsolver.rs | 20 ++- .../crates/hir-ty/src/next_solver/interner.rs | 2 +- .../crates/hir-ty/src/tests/incremental.rs | 6 - src/tools/rust-analyzer/crates/hir/src/lib.rs | 18 +-- 7 files changed, 90 insertions(+), 154 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 84c6c0471b5b3..42f7ec6b59fd3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -121,9 +121,12 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: TyDefId, ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; - #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower::type_for_type_alias_with_diagnostics_cycle_result)] - fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders, Diagnostics); + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] + fn type_for_type_alias_with_diagnostics<'db>( + &'db self, + def: TypeAliasId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. @@ -133,35 +136,56 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: ValueTyDefId, ) -> Option>>; - #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower::impl_self_ty_with_diagnostics_cycle_result)] - fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders, Diagnostics); + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] + fn impl_self_ty_with_diagnostics<'db>( + &'db self, + def: ImplId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); #[salsa::invoke(crate::lower::impl_self_ty_query)] #[salsa::transparent] fn impl_self_ty(&self, def: ImplId) -> Binders; // FIXME: Make this a non-interned query. - #[salsa::invoke_interned(crate::lower::const_param_ty_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower::const_param_ty_with_diagnostics_cycle_result)] - fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics); + #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::const_param_ty_with_diagnostics_cycle_result)] + fn const_param_ty_with_diagnostics<'db>( + &'db self, + def: ConstParamId, + ) -> (crate::next_solver::Ty<'db>, Diagnostics); - #[salsa::invoke(crate::lower::const_param_ty_query)] - #[salsa::transparent] + // FIXME: Make this a non-interned query. + #[salsa::invoke_interned(crate::lower::const_param_ty_query)] + #[salsa::cycle(cycle_result = crate::lower::const_param_ty_cycle_result)] fn const_param_ty(&self, def: ConstParamId) -> Ty; - #[salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)] - fn impl_trait_with_diagnostics(&self, def: ImplId) -> Option<(Binders, Diagnostics)>; + #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] + fn impl_trait_with_diagnostics<'db>( + &'db self, + def: ImplId, + ) -> Option<( + crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, + Diagnostics, + )>; #[salsa::invoke(crate::lower::impl_trait_query)] #[salsa::transparent] fn impl_trait(&self, def: ImplId) -> Option>; - #[salsa::invoke(crate::lower::field_types_with_diagnostics_query)] - fn field_types_with_diagnostics( - &self, + #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] + fn field_types_with_diagnostics<'db>( + &'db self, var: VariantId, - ) -> (Arc>>, Diagnostics); + ) -> ( + Arc< + ArenaMap< + LocalFieldId, + crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, + >, + >, + Diagnostics, + ); #[salsa::invoke(crate::lower::field_types_query)] #[salsa::transparent] @@ -191,6 +215,21 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke( + crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query + )] + fn generic_predicates_without_parent_with_diagnostics<'db>( + &'db self, + def: GenericDefId, + ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] + #[salsa::transparent] + fn generic_predicates_without_parent<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + #[salsa::invoke(crate::lower_nextsolver::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId) @@ -286,20 +325,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] - fn type_for_type_alias_with_diagnostics_ns<'db>( - &'db self, - def: TypeAliasId, - ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); - - #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] - fn impl_self_ty_with_diagnostics_ns<'db>( - &'db self, - def: ImplId, - ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); - #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] #[salsa::transparent] fn impl_self_ty_ns<'db>( @@ -307,26 +332,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: ImplId, ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; - // FIXME: Make this a non-interned query. - #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] - fn const_param_ty_with_diagnostics_ns<'db>( - &'db self, - def: ConstParamId, - ) -> (crate::next_solver::Ty<'db>, Diagnostics); - #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] #[salsa::transparent] fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; - #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] - fn impl_trait_with_diagnostics_ns<'db>( - &'db self, - def: ImplId, - ) -> Option<( - crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, - Diagnostics, - )>; - #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] #[salsa::transparent] fn impl_trait_ns<'db>( @@ -334,20 +343,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: ImplId, ) -> Option>>; - #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] - fn field_types_with_diagnostics_ns<'db>( - &'db self, - var: VariantId, - ) -> ( - Arc< - ArenaMap< - LocalFieldId, - crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, - >, - >, - Diagnostics, - ); - #[salsa::invoke(crate::lower_nextsolver::field_types_query)] #[salsa::transparent] fn field_types_ns<'db>( @@ -383,21 +378,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &'db self, def: GenericDefId, ) -> crate::lower_nextsolver::GenericPredicates<'db>; - - #[salsa::invoke( - crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query - )] - fn generic_predicates_without_parent_with_diagnostics_ns<'db>( - &'db self, - def: GenericDefId, - ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); - - #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] - #[salsa::transparent] - fn generic_predicates_without_parent_ns<'db>( - &'db self, - def: GenericDefId, - ) -> crate::lower_nextsolver::GenericPredicates<'db>; } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index f033c511acd6a..b2406a0889583 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -364,7 +364,7 @@ where cb(MethodViolationCode::UndispatchableReceiver)?; } - let predicates = &*db.generic_predicates_without_parent_ns(func.into()); + let predicates = &*db.generic_predicates_without_parent(func.into()); for pred in predicates { let pred = pred.kind().skip_binder(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 598fd38b4b8e9..b55f9cd145e26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -32,7 +32,7 @@ use hir_def::{ hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, lang_item::LangItem, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, - signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, + signatures::{FunctionSignature, TraitFlags}, type_ref::{ ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, @@ -908,7 +908,7 @@ pub(crate) fn field_types_query( db: &dyn HirDatabase, variant_id: VariantId, ) -> Arc>> { - db.field_types_with_diagnostics(variant_id).0 + field_types_with_diagnostics_query(db, variant_id).0 } /// Build the type of all specific fields of a struct or enum variant. @@ -1303,46 +1303,6 @@ pub(crate) fn generic_defaults_with_diagnostics_cycle_result( (GenericDefaults(None), None) } -pub(crate) fn type_for_type_alias_with_diagnostics_query( - db: &dyn HirDatabase, - t: TypeAliasId, -) -> (Binders, Diagnostics) { - let generics = generics(db, t.into()); - let type_alias_data = db.type_alias_signature(t); - let mut diags = None; - let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner) - } else { - let resolver = t.resolver(db); - let alias = db.type_alias_signature(t); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &alias.store, - t.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); - let res = alias - .ty - .map(|type_ref| ctx.lower_ty(type_ref)) - .unwrap_or_else(|| TyKind::Error.intern(Interner)); - diags = create_diagnostics(ctx.diagnostics); - res - }; - - (make_binders(db, &generics, inner), diags) -} - -pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - adt: TypeAliasId, -) -> (Binders, Diagnostics) { - let generics = generics(db, adt.into()); - (make_binders(db, &generics, TyKind::Error.intern(Interner)), None) -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum TyDefId { BuiltinType(BuiltinType), @@ -1376,7 +1336,7 @@ impl ValueTyDefId { } pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders { - db.impl_self_ty_with_diagnostics(impl_id).0 + impl_self_ty_with_diagnostics_query(db, impl_id).0 } pub(crate) fn impl_self_ty_with_diagnostics_query( @@ -1400,16 +1360,8 @@ pub(crate) fn impl_self_ty_with_diagnostics_query( ) } -pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - impl_id: ImplId, -) -> (Binders, Diagnostics) { - let generics = generics(db, impl_id.into()); - (make_binders(db, &generics, TyKind::Error.intern(Interner)), None) -} - pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { - db.const_param_ty_with_diagnostics(def).0 + const_param_ty_with_diagnostics_query(db, def).0 } // returns None if def is a type arg @@ -1437,16 +1389,16 @@ pub(crate) fn const_param_ty_with_diagnostics_query( (ty, create_diagnostics(ctx.diagnostics)) } -pub(crate) fn const_param_ty_with_diagnostics_cycle_result( +pub(crate) fn const_param_ty_cycle_result( _: &dyn HirDatabase, _: crate::db::HirDatabaseData, _: ConstParamId, -) -> (Ty, Diagnostics) { - (TyKind::Error.intern(Interner), None) +) -> Ty { + TyKind::Error.intern(Interner) } pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option> { - db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) + impl_trait_with_diagnostics_query(db, impl_id).map(|it| it.0) } pub(crate) fn impl_trait_with_diagnostics_query( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 1ca56feb099a1..a537d3a101710 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -904,7 +904,7 @@ pub(crate) fn impl_trait_query<'db>( db: &'db dyn HirDatabase, impl_id: ImplId, ) -> Option>> { - db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0) + db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) } pub(crate) fn impl_trait_with_diagnostics_query<'db>( @@ -986,7 +986,7 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind AdtDef::new(it, interner), GenericArgs::identity_for_item(interner, it.into()), )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0, + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, } } @@ -1131,7 +1131,7 @@ pub(crate) fn impl_self_ty_query<'db>( db: &'db dyn HirDatabase, impl_id: ImplId, ) -> EarlyBinder<'db, Ty<'db>> { - db.impl_self_ty_with_diagnostics_ns(impl_id).0 + db.impl_self_ty_with_diagnostics(impl_id).0 } pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( @@ -1162,7 +1162,7 @@ pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( } pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { - db.const_param_ty_with_diagnostics_ns(def).0 + db.const_param_ty_with_diagnostics(def).0 } // returns None if def is a type arg @@ -1191,11 +1191,21 @@ pub(crate) fn const_param_ty_with_diagnostics_query<'db>( (ty, create_diagnostics(ctx.diagnostics)) } +pub(crate) fn const_param_ty_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, + _: crate::db::HirDatabaseData, + def: ConstParamId, +) -> (Ty<'db>, Diagnostics) { + let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + (Ty::new_error(interner, ErrorGuaranteed), None) +} + pub(crate) fn field_types_query<'db>( db: &'db dyn HirDatabase, variant_id: VariantId, ) -> Arc>>> { - db.field_types_with_diagnostics_ns(variant_id).0 + db.field_types_with_diagnostics(variant_id).0 } /// Build the type of all specific fields of a struct or enum variant. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 1a14ac9a7457b..3ff9fde9768b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1320,7 +1320,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); + let predicates = self.db().generic_predicates_without_parent(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0d922de9aeb07..cc5ad2045ed4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -504,10 +504,8 @@ impl SomeStruct { "crate_local_def_map", "trait_impls_in_crate_shim", "attrs_shim", - "impl_trait_with_diagnostics_shim", "impl_signature_shim", "impl_signature_with_source_map_shim", - "impl_self_ty_with_diagnostics_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", "attrs_shim", @@ -608,8 +606,6 @@ fn main() { "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", - "impl_trait_with_diagnostics_ns_shim", - "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", @@ -698,8 +694,6 @@ fn main() { "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", - "impl_trait_with_diagnostics_ns_shim", - "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", "generic_predicates_shim", ] diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 709d165e0b8b0..5592ca285fb01 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -682,7 +682,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics_ns(s.id.into()).1, + db.field_types_with_diagnostics(s.id.into()).1, source_map, ); } @@ -694,7 +694,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics_ns(u.id.into()).1, + db.field_types_with_diagnostics(u.id.into()).1, source_map, ); } @@ -724,7 +724,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics_ns(v.into()).1, + db.field_types_with_diagnostics(v.into()).1, source_map, ); expr_store_diagnostics(db, acc, source_map); @@ -740,7 +740,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics_ns(type_alias.id).1, + db.type_for_type_alias_with_diagnostics(type_alias.id).1, &source_map, ); acc.extend(def.diagnostics(db, style_lints)); @@ -915,13 +915,13 @@ impl Module { push_ty_diagnostics( db, acc, - db.impl_self_ty_with_diagnostics_ns(impl_def.id).1, + db.impl_self_ty_with_diagnostics(impl_def.id).1, &source_map, ); push_ty_diagnostics( db, acc, - db.impl_trait_with_diagnostics_ns(impl_def.id).and_then(|it| it.1), + db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1), &source_map, ); @@ -3693,7 +3693,7 @@ impl AssocItem { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics_ns(type_alias.id).1, + db.type_for_type_alias_with_diagnostics(type_alias.id).1, &db.type_alias_signature_with_source_map(type_alias.id).1, ); for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { @@ -3821,7 +3821,7 @@ impl GenericDef { push_ty_diagnostics( db, acc, - db.generic_predicates_without_parent_with_diagnostics_ns(def).1, + db.generic_predicates_without_parent_with_diagnostics(def).1, &source_map, ); for (param_id, param) in generics.iter_type_or_consts() { @@ -3829,7 +3829,7 @@ impl GenericDef { push_ty_diagnostics( db, acc, - db.const_param_ty_with_diagnostics_ns(ConstParamId::from_unchecked( + db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked( TypeOrConstParamId { parent: def, local_id: param_id }, )) .1, From 94bf6b4da632c9f34c082e0c6464d46cf1f6b2ed Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:54:29 -0400 Subject: [PATCH 0796/1900] Be sure to instantiate and pass up trait refs in named_associated_type_shorthand_candidates --- .../crates/hir-ty/src/lower_nextsolver.rs | 23 +++++++++++-------- .../crates/ide/src/signature_help.rs | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index a537d3a101710..02774a63df422 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1970,7 +1970,8 @@ fn named_associated_type_shorthand_candidates<'db, R>( let mut search = |t: TraitRef<'db>| -> Option { let trait_id = t.def_id.0; let mut checked_traits = FxHashSet::default(); - let mut check_trait = |trait_id: TraitId| { + let mut check_trait = |trait_ref: TraitRef<'db>| { + let trait_id = trait_ref.def_id.0; let name = &db.trait_signature(trait_id).name; tracing::debug!(?trait_id, ?name); if !checked_traits.insert(trait_id) { @@ -1981,37 +1982,39 @@ fn named_associated_type_shorthand_candidates<'db, R>( tracing::debug!(?data.items); for (name, assoc_id) in &data.items { if let &AssocItemId::TypeAliasId(alias) = assoc_id - && let Some(ty) = check_alias(name, t, alias) + && let Some(ty) = check_alias(name, trait_ref, alias) { return Some(ty); } } None }; - let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; - while let Some(trait_def_id) = stack.pop() { - if let Some(alias) = check_trait(trait_def_id) { + let mut stack: SmallVec<[_; 4]> = smallvec![t]; + while let Some(trait_ref) = stack.pop() { + if let Some(alias) = check_trait(trait_ref) { return Some(alias); } for pred in generic_predicates_filtered_by( db, - GenericDefId::TraitId(trait_def_id), + GenericDefId::TraitId(trait_ref.def_id.0), PredicateFilter::SelfTrait, // We are likely in the midst of lowering generic predicates of `def`. // So, if we allow `pred == def` we might fall into an infinite recursion. // Actually, we have already checked for the case `pred == def` above as we started // with a stack including `trait_id` - |pred| pred != def && pred == GenericDefId::TraitId(trait_def_id), + |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0), ) .0 .deref() { tracing::debug!(?pred); - let trait_id = match pred.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + let sup_trait_ref = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.trait_ref, _ => continue, }; - stack.push(trait_id.0); + let sup_trait_ref = + EarlyBinder::bind(sup_trait_ref).instantiate(interner, trait_ref.args); + stack.push(sup_trait_ref); } tracing::debug!(?stack); } diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 6f55886adca48..e74d997e97c52 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -1097,8 +1097,8 @@ fn foo(mut r: impl WriteHandler<()>) { By default this method stops actor's `Context`. ------ - fn finished(&mut self, ctx: &mut as Actor>::Context<()>) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + fn finished(&mut self, ctx: &mut as Actor>::Context) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "#]], ); } From e8a8e061bf0187efbb33d972e31ef2b5d9f6d529 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 22 Sep 2025 21:02:53 -0400 Subject: [PATCH 0797/1900] Make missed precondition-free float intrinsics safe --- .../rustc_hir_analysis/src/check/intrinsic.rs | 8 ++++++++ library/core/src/intrinsics/mod.rs | 16 ++++++++-------- library/core/src/num/f128.rs | 3 +-- library/core/src/num/f16.rs | 3 +-- library/core/src/num/f32.rs | 6 ++---- library/core/src/num/f64.rs | 6 ++---- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 6faa67f6a904a..bc3448be5823e 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -90,6 +90,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::contract_check_ensures | sym::contract_check_requires | sym::contract_checks + | sym::copysignf16 + | sym::copysignf32 + | sym::copysignf64 + | sym::copysignf128 | sym::cosf16 | sym::cosf32 | sym::cosf64 @@ -106,6 +110,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::expf32 | sym::expf64 | sym::expf128 + | sym::fabsf16 + | sym::fabsf32 + | sym::fabsf64 + | sym::fabsf128 | sym::fadd_algebraic | sym::fdiv_algebraic | sym::floorf16 diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a174ced5a2a63..eaf2738d8b7f5 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3170,7 +3170,7 @@ pub const fn maximumf128(x: f128, y: f128) -> f128 { /// [`f16::abs`](../../std/primitive.f16.html#method.abs) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn fabsf16(x: f16) -> f16; +pub const fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// @@ -3179,7 +3179,7 @@ pub const unsafe fn fabsf16(x: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn fabsf32(x: f32) -> f32; +pub const fn fabsf32(x: f32) -> f32; /// Returns the absolute value of an `f64`. /// @@ -3188,7 +3188,7 @@ pub const unsafe fn fabsf32(x: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn fabsf64(x: f64) -> f64; +pub const fn fabsf64(x: f64) -> f64; /// Returns the absolute value of an `f128`. /// @@ -3196,7 +3196,7 @@ pub const unsafe fn fabsf64(x: f64) -> f64; /// [`f128::abs`](../../std/primitive.f128.html#method.abs) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn fabsf128(x: f128) -> f128; +pub const fn fabsf128(x: f128) -> f128; /// Copies the sign from `y` to `x` for `f16` values. /// @@ -3204,7 +3204,7 @@ pub const unsafe fn fabsf128(x: f128) -> f128; /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn copysignf16(x: f16, y: f16) -> f16; +pub const fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// @@ -3213,7 +3213,7 @@ pub const unsafe fn copysignf16(x: f16, y: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn copysignf32(x: f32, y: f32) -> f32; +pub const fn copysignf32(x: f32, y: f32) -> f32; /// Copies the sign from `y` to `x` for `f64` values. /// /// The stabilized version of this intrinsic is @@ -3221,7 +3221,7 @@ pub const unsafe fn copysignf32(x: f32, y: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn copysignf64(x: f64, y: f64) -> f64; +pub const fn copysignf64(x: f64, y: f64) -> f64; /// Copies the sign from `y` to `x` for `f128` values. /// @@ -3229,7 +3229,7 @@ pub const unsafe fn copysignf64(x: f64, y: f64) -> f64; /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn copysignf128(x: f128, y: f128) -> f128; +pub const fn copysignf128(x: f128, y: f128) -> f128; /// Generates the LLVM body for the automatic differentiation of `f` using Enzyme, /// with `df` as the derivative function and `args` as its arguments. diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6088fb6fa1786..54d2d1c5706b7 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1366,8 +1366,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn copysign(self, sign: f128) -> f128 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::copysignf128(self, sign) } + intrinsics::copysignf128(self, sign) } /// Float addition that allows optimizations based on algebraic rules. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 3f66c5973d4ac..f3e49544d259a 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1343,8 +1343,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn copysign(self, sign: f16) -> f16 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::copysignf16(self, sign) } + intrinsics::copysignf16(self, sign) } /// Float addition that allows optimizations based on algebraic rules. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index ebce89e5b3da5..ded70c825af93 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1449,8 +1449,7 @@ impl f32 { #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f32 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::fabsf32(self) } + intrinsics::fabsf32(self) } /// Returns a number that represents the sign of `self`. @@ -1508,8 +1507,7 @@ impl f32 { #[stable(feature = "copysign", since = "1.35.0")] #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] pub const fn copysign(self, sign: f32) -> f32 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::copysignf32(self, sign) } + intrinsics::copysignf32(self, sign) } /// Float addition that allows optimizations based on algebraic rules. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 91e3949fc3900..3abdc7c108ab5 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1447,8 +1447,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f64 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::fabsf64(self) } + intrinsics::fabsf64(self) } /// Returns a number that represents the sign of `self`. @@ -1506,8 +1505,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn copysign(self, sign: f64) -> f64 { - // SAFETY: this is actually a safe intrinsic - unsafe { intrinsics::copysignf64(self, sign) } + intrinsics::copysignf64(self, sign) } /// Float addition that allows optimizations based on algebraic rules. From ace2e274db8165fc5bfeba1faf83e15095723229 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 23:02:59 +0200 Subject: [PATCH 0798/1900] Cleanup: group code for `single_char_add_str` lint in one file A lot of code was duplicated instead of being shared. --- clippy_lints/src/methods/mod.rs | 2 - .../src/methods/single_char_add_str.rs | 80 +++++++++++++++++-- .../src/methods/single_char_insert_string.rs | 67 ---------------- .../src/methods/single_char_push_string.rs | 65 --------------- 4 files changed, 73 insertions(+), 141 deletions(-) delete mode 100644 clippy_lints/src/methods/single_char_insert_string.rs delete mode 100644 clippy_lints/src/methods/single_char_push_string.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4d..3755ec37fb967 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -103,8 +103,6 @@ mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; mod single_char_add_str; -mod single_char_insert_string; -mod single_char_push_string; mod skip_while_next; mod sliced_string_as_bytes; mod stable_sort_primitive; diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ef3d7acdc01ef..1248d1658d77e 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -1,14 +1,80 @@ -use crate::methods::{single_char_insert_string, single_char_push_string}; -use rustc_hir as hir; +use super::SINGLE_CHAR_ADD_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - match cx.tcx.get_diagnostic_name(fn_def_id) { - Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), - Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), - _ => {}, + let mut applicability = Applicability::MachineApplicable; + let (short_name, arg, extra) = match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_insert_str) => ( + "insert", + &args[1], + Some(|applicability| { + format!( + "{}, ", + snippet_with_applicability(cx, args[0].span, "..", applicability) + ) + }), + ), + Some(sym::string_push_str) => ("push", &args[0], None), + _ => return, + }; + + if let Some(extension_string) = str_literal_to_char_literal(cx, arg, &mut applicability, false) { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character string literal"), + format!("consider using `{short_name}` with a character literal"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); + } else if let ExprKind::AddrOf(BorrowKind::Ref, _, inner) = arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = inner.kind + && path_segment.ident.name == sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + let extension_string = match ( + snippet_with_applicability(cx, method_arg.span.source_callsite(), "_", &mut applicability), + is_ref_char(cx, method_arg), + ) { + (snippet, false) => snippet, + (snippet, true) => format!("*{snippet}").into(), + }; + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character converted to string"), + format!("consider using `{short_name}` without `to_string()`"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); } } } + +fn is_ref_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::Ref(_, ty, _) if ty.is_char()) +} + +fn is_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() +} diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs deleted file mode 100644 index 4a1d25deade90..0000000000000 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ /dev/null @@ -1,67 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `insert_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character converted to string", - "consider using `insert` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs deleted file mode 100644 index bc271d593925e..0000000000000 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `push_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{base_string_snippet}.push({extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character converted to string", - "consider using `push` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} From aa537824c40437d11d32c66fe16d4764b072c094 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 24 Sep 2025 00:50:36 +0200 Subject: [PATCH 0799/1900] core: simplify `CStr::default()` Just use a `CStr`-literal... --- library/core/src/ffi/c_str.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index d0b53e3a237f6..09d9b160700ca 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -179,9 +179,7 @@ impl fmt::Debug for CStr { impl Default for &CStr { #[inline] fn default() -> Self { - const SLICE: &[c_char] = &[0]; - // SAFETY: `SLICE` is indeed pointing to a valid nul-terminated string. - unsafe { CStr::from_ptr(SLICE.as_ptr()) } + c"" } } From 95ddfa102af7f841c36ab8b207eb42571716b1cd Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 24 Sep 2025 07:34:22 +0800 Subject: [PATCH 0800/1900] Update LLVM to 21.1.2 --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 333793696b0a0..8c30b9c5098bd 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 333793696b0a08002acac6af983adc7a5233fc56 +Subproject commit 8c30b9c5098bdff1d3d9a2d460ee091cd1171e60 From ce677c7db80a245e1eee5b948dd6b20b4bba0423 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 23 Sep 2025 20:38:38 -0300 Subject: [PATCH 0801/1900] Update compiler/rustc_mir_transform/src/patch.rs Co-authored-by: lcnr --- compiler/rustc_mir_transform/src/patch.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 8f2124405650a..d831ab50b1ac0 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -23,6 +23,8 @@ pub(crate) struct MirPatch<'tcx> { terminate_block: Option<(BasicBlock, UnwindTerminateReason)>, body_span: Span, next_local: usize, + /// The number of blocks at the start of the transformation. New blocks + /// get appended at the end. next_block: usize, } From f5c6c9542ecfab74e654f9b7f1150d486560ae43 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 16 Sep 2025 02:23:24 -0400 Subject: [PATCH 0802/1900] Add an attribute to check the number of lanes in a SIMD vector after monomorphization Unify zero-length and oversized SIMD errors --- .../src/attributes/crate_level.rs | 36 ------------------ .../src/attributes/rustc_internal.rs | 18 +++++++++ .../rustc_attr_parsing/src/attributes/util.rs | 37 ++++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- .../rustc_codegen_cranelift/src/common.rs | 9 ++++- .../rustc_codegen_cranelift/src/global_asm.rs | 5 ++- compiler/rustc_codegen_gcc/src/context.rs | 9 ++++- compiler/rustc_codegen_llvm/src/context.rs | 11 +++++- compiler/rustc_feature/src/builtin_attrs.rs | 6 +++ .../rustc_hir/src/attrs/data_structures.rs | 3 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../src/hir_ty_lowering/cmse.rs | 1 + compiler/rustc_middle/messages.ftl | 6 +++ compiler/rustc_middle/src/error.rs | 6 +++ compiler/rustc_middle/src/ty/layout.rs | 26 +++++++++++++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_transmute/src/layout/tree.rs | 1 + compiler/rustc_ty_utils/src/errors.rs | 13 ------- compiler/rustc_ty_utils/src/layout.rs | 26 +++++++++++-- .../html/templates/type_layout.html | 5 +++ tests/ui/simd/auxiliary/simd-lane-limit.rs | 5 +++ tests/ui/simd/monomorphize-too-long.rs | 4 +- tests/ui/simd/monomorphize-too-long.stderr | 6 ++- tests/ui/simd/monomorphize-zero-length.rs | 4 +- tests/ui/simd/monomorphize-zero-length.stderr | 6 ++- tests/ui/simd/simd-lane-limit-err-npow2.rs | 12 ++++++ .../ui/simd/simd-lane-limit-err-npow2.stderr | 8 ++++ tests/ui/simd/simd-lane-limit-err.rs | 11 ++++++ tests/ui/simd/simd-lane-limit-err.stderr | 8 ++++ tests/ui/simd/simd-lane-limit-ok.rs | 14 +++++++ .../type-generic-monomorphisation-empty.rs | 4 +- ...type-generic-monomorphisation-empty.stderr | 6 ++- ...type-generic-monomorphisation-oversized.rs | 5 +-- ...-generic-monomorphisation-oversized.stderr | 6 ++- 35 files changed, 245 insertions(+), 78 deletions(-) create mode 100644 tests/ui/simd/auxiliary/simd-lane-limit.rs create mode 100644 tests/ui/simd/simd-lane-limit-err-npow2.rs create mode 100644 tests/ui/simd/simd-lane-limit-err-npow2.stderr create mode 100644 tests/ui/simd/simd-lane-limit-err.rs create mode 100644 tests/ui/simd/simd-lane-limit-err.stderr create mode 100644 tests/ui/simd/simd-lane-limit-ok.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 4611de4445997..00edafecd9a88 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,40 +1,4 @@ -use std::num::IntErrorKind; - -use rustc_hir::limit::Limit; - use super::prelude::*; -use crate::session_diagnostics::LimitInvalid; - -impl AcceptContext<'_, '_, S> { - fn parse_limit_int(&self, nv: &NameValueParser) -> Option { - let Some(limit) = nv.value_as_str() else { - self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); - return None; - }; - - let error_str = match limit.as_str().parse() { - Ok(i) => return Some(Limit::new(i)), - Err(e) => match e.kind() { - IntErrorKind::PosOverflow => "`limit` is too large", - IntErrorKind::Empty => "`limit` must be a non-negative integer", - IntErrorKind::InvalidDigit => "not a valid integer", - IntErrorKind::NegOverflow => { - panic!( - "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" - ) - } - IntErrorKind::Zero => { - panic!("zero is a valid `limit` so should have returned Ok() when parsing") - } - kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), - }, - }; - - self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); - - None - } -} pub(crate) struct CrateNameParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index a995549fc7c83..cf2f5c6c79024 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -49,3 +49,21 @@ impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { Some(AttributeKind::RustcObjectLifetimeDefault) } } + +pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; + +impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { + const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 77e8c32e59d73..62b72798e9699 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,11 +1,15 @@ +use std::num::IntErrorKind; + use rustc_ast::LitKind; use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; +use rustc_hir::limit::Limit; use rustc_span::{Symbol, sym}; use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::parser::{ArgParser, NameValueParser}; +use crate::session_diagnostics::LimitInvalid; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are @@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer( }; Some(num.0) } + +impl AcceptContext<'_, '_, S> { + pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option { + let Some(limit) = nv.value_as_str() else { + self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + let error_str = match limit.as_str().parse() { + Ok(i) => return Some(Limit::new(i)), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => "`limit` is too large", + IntErrorKind::Empty => "`limit` must be a non-negative integer", + IntErrorKind::InvalidDigit => "not a valid integer", + IntErrorKind::NegOverflow => { + panic!( + "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" + ) + } + IntErrorKind::Zero => { + panic!("zero is a valid `limit` so should have returned Ok() when parsing") + } + kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + }, + }; + + self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); + + None + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d7998048be5af..72fe269b135a7 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -53,7 +53,7 @@ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, - RustcObjectLifetimeDefaultParser, + RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -195,6 +195,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 2fbe5c02802ab..81b1814605a12 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.0.sess.dcx().span_fatal(span, err.to_string()) } else { self.0 @@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.0.sess.dcx().emit_fatal(Spanned { span, node: err }) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 203b443269fa7..1306c6aa5179c 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.sess.dcx().span_fatal(span, err.to_string()) } else { self.tcx diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 665cf22ddbaee..9815fb07eaae9 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic())) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.tcx.dcx().emit_fatal(respan(span, err)) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a4d..e4528efaa37b1 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -991,7 +991,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::ReferencesError(_) + | LayoutError::InvalidSimd { .. } = err + { self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() }) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -1008,7 +1011,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { match err { - FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => { + FnAbiError::Layout( + LayoutError::SizeOverflow(_) + | LayoutError::Cycle(_) + | LayoutError::InvalidSimd { .. }, + ) => { self.tcx.dcx().emit_fatal(Spanned { span, node: err }); } _ => match fn_abi_request { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 129ab7eccb577..0d890b57de0d6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1203,6 +1203,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ niche optimizations in the standard library", ), + rustc_attr!( + rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \ + for better error messages", + ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ea11a99efbc35..80cb641768471 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -642,6 +642,9 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. + RustcSimdMonomorphizeLaneLimit(Limit), + /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 55521c1585402..968ea668efea1 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -85,6 +85,7 @@ impl AttributeKind { RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e60..76b0ce985f0ca 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -213,6 +213,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError } Unknown(..) | SizeOverflow(..) + | InvalidSimd { .. } | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 279ab9a9d8fbe..82f3df8da3b12 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -98,6 +98,12 @@ middle_layout_references_error = middle_layout_size_overflow = values of the type `{$ty}` are too big for the target architecture +middle_layout_simd_too_many = + the SIMD type `{$ty}` has more elements than the limit {$max_lanes} + +middle_layout_simd_zero_length = + the SIMD type `{$ty}` has zero elements + middle_layout_too_generic = the type `{$ty}` does not have a fixed layout middle_layout_unknown = diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dad402ec69616..e15da827bda9b 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -141,6 +141,12 @@ pub enum LayoutError<'tcx> { #[diag(middle_layout_size_overflow)] Overflow { ty: Ty<'tcx> }, + #[diag(middle_layout_simd_too_many)] + SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 }, + + #[diag(middle_layout_simd_zero_length)] + SimdZeroLength { ty: Ty<'tcx> }, + #[diag(middle_layout_normalization_failure)] NormalizationFailure { ty: Ty<'tcx>, failure_ty: String }, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa43..47e561d9d3ce8 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -217,6 +217,15 @@ impl fmt::Display for ValidityRequirement { } } +#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] +pub enum SimdLayoutError { + /// The vector has 0 lanes. + ZeroLength, + /// The vector has more lanes than supported or permitted by + /// #\[rustc_simd_monomorphize_lane_limit\]. + TooManyLanes(u64), +} + #[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] pub enum LayoutError<'tcx> { /// A type doesn't have a sensible layout. @@ -229,6 +238,8 @@ pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), /// The size of a type exceeds [`TargetDataLayout::obj_size_bound`]. SizeOverflow(Ty<'tcx>), + /// A SIMD vector has invalid layout, such as zero-length or too many lanes. + InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError }, /// The layout can vary due to a generic parameter. /// /// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout @@ -256,6 +267,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(_) => middle_layout_unknown, SizeOverflow(_) => middle_layout_size_overflow, + InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => { + middle_layout_simd_too_many + } + InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length, TooGeneric(_) => middle_layout_too_generic, NormalizationFailure(_, _) => middle_layout_normalization_failure, Cycle(_) => middle_layout_cycle, @@ -270,6 +285,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(ty) => E::Unknown { ty }, SizeOverflow(ty) => E::Overflow { ty }, + InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + E::SimdTooManyLanes { ty, max_lanes } + } + InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty }, TooGeneric(ty) => E::TooGeneric { ty }, NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } @@ -292,6 +311,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}") + } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => { + write!(f, "the SIMD type `{ty}` has zero elements") + } LayoutError::NormalizationFailure(t, e) => write!( f, "unable to determine layout for `{}` because `{}` cannot be normalized", @@ -373,6 +398,7 @@ impl<'tcx> SizeSkeleton<'tcx> { e @ LayoutError::Cycle(_) | e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) + | e @ LayoutError::InvalidSimd { .. } | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), ) => return Err(e), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2562d2e0b83c5..cabf28197413b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -254,6 +254,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MacroEscape( .. ) | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) + | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::ExportStable | AttributeKind::FfiConst(..) | AttributeKind::UnstableFeatureBound(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6d..92a1fad47552b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1937,6 +1937,7 @@ symbols! { rustc_regions, rustc_reservation_impl, rustc_serialize, + rustc_simd_monomorphize_lane_limit, rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d7ea26a2ab1d0..a02e8ecf613f4 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -279,6 +279,7 @@ pub(crate) mod rustc { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) | LayoutError::TooGeneric(..) + | LayoutError::InvalidSimd { .. } | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index 0298e7e0e9511..f92c405242ce9 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -78,19 +78,6 @@ pub(crate) struct UnexpectedFnPtrAssociatedItem { pub span: Span, } -#[derive(Diagnostic)] -#[diag(ty_utils_zero_length_simd_type)] -pub(crate) struct ZeroLengthSimdType<'tcx> { - pub ty: Ty<'tcx>, -} - -#[derive(Diagnostic)] -#[diag(ty_utils_oversized_simd_type)] -pub(crate) struct OversizedSimdType<'tcx> { - pub ty: Ty<'tcx>, - pub max_lanes: u64, -} - #[derive(Diagnostic)] #[diag(ty_utils_non_primitive_simd_type)] pub(crate) struct NonPrimitiveSimdType<'tcx> { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2adc..5c4451ce264f0 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -7,11 +7,13 @@ use rustc_abi::{ VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, + FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -22,7 +24,7 @@ use rustc_span::{Symbol, sym}; use tracing::{debug, instrument}; use {rustc_abi as abi, rustc_hir as hir}; -use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType}; +use crate::errors::NonPrimitiveSimdType; mod invariant; @@ -120,11 +122,11 @@ fn map_error<'tcx>( } LayoutCalculatorError::ZeroLengthSimdType => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } } LayoutCalculatorError::OversizedSimdType { max_lanes } => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } } LayoutCalculatorError::NonPrimitiveSimdType(field) => { // This error isn't caught in typeck, e.g., if @@ -561,6 +563,22 @@ fn layout_of_uncached<'tcx>( let e_ly = cx.layout_of(e_ty)?; + // Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit + if let Some(limit) = find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit + ) { + if !limit.value_within_limit(e_len as usize) { + return Err(map_error( + &cx, + ty, + rustc_abi::LayoutCalculatorError::OversizedSimdType { + max_lanes: limit.0 as u64, + }, + )); + } + } + map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))? } diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index 0034552bdd3b7..49153d58fe98c 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -65,5 +65,10 @@

{# #} Note: Encountered an error during type layout; {#+ #} the type's layout depended on the type's layout itself. {# #}

+ {% when Err(LayoutError::InvalidSimd {..}) %} +

{# #} + Note: Encountered an error during type layout; {#+ #} + the vector type had zero elements or too many elements. {# #} +

{% endmatch %}

{# #} diff --git a/tests/ui/simd/auxiliary/simd-lane-limit.rs b/tests/ui/simd/auxiliary/simd-lane-limit.rs new file mode 100644 index 0000000000000..dde6b880c62ec --- /dev/null +++ b/tests/ui/simd/auxiliary/simd-lane-limit.rs @@ -0,0 +1,5 @@ +#![feature(rustc_attrs, repr_simd)] + +#[repr(simd, packed)] +#[rustc_simd_monomorphize_lane_limit = "8"] +pub struct Simd(pub [T; N]); diff --git a/tests/ui/simd/monomorphize-too-long.rs b/tests/ui/simd/monomorphize-too-long.rs index 4fac987b0b5aa..9c83741519178 100644 --- a/tests/ui/simd/monomorphize-too-long.rs +++ b/tests/ui/simd/monomorphize-too-long.rs @@ -6,7 +6,5 @@ struct Simd([T; N]); fn main() { - let _too_big = Simd([1_u16; 54321]); + let _too_big = Simd([1_u16; 54321]); //~ ERROR the SIMD type `Simd` has more elements than the limit 32768 } - -//~? ERROR monomorphising SIMD type `Simd` of length greater than 32768 diff --git a/tests/ui/simd/monomorphize-too-long.stderr b/tests/ui/simd/monomorphize-too-long.stderr index 978eef307abd1..71bc78ef5c955 100644 --- a/tests/ui/simd/monomorphize-too-long.stderr +++ b/tests/ui/simd/monomorphize-too-long.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd` of length greater than 32768 +error: the SIMD type `Simd` has more elements than the limit 32768 + --> $DIR/monomorphize-too-long.rs:9:9 + | +LL | let _too_big = Simd([1_u16; 54321]); + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/monomorphize-zero-length.rs b/tests/ui/simd/monomorphize-zero-length.rs index d38870c572d97..f956197a61cf0 100644 --- a/tests/ui/simd/monomorphize-zero-length.rs +++ b/tests/ui/simd/monomorphize-zero-length.rs @@ -6,7 +6,5 @@ struct Simd([T; N]); fn main() { - let _empty = Simd([1.0; 0]); + let _empty = Simd([1.0; 0]); //~ ERROR the SIMD type `Simd` has zero elements } - -//~? ERROR monomorphising SIMD type `Simd` of zero length diff --git a/tests/ui/simd/monomorphize-zero-length.stderr b/tests/ui/simd/monomorphize-zero-length.stderr index 738c20fe51a46..66f26d95c9d97 100644 --- a/tests/ui/simd/monomorphize-zero-length.stderr +++ b/tests/ui/simd/monomorphize-zero-length.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd` of zero length +error: the SIMD type `Simd` has zero elements + --> $DIR/monomorphize-zero-length.rs:9:9 + | +LL | let _empty = Simd([1.0; 0]); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/simd-lane-limit-err-npow2.rs b/tests/ui/simd/simd-lane-limit-err-npow2.rs new file mode 100644 index 0000000000000..d5c5c92e953db --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err-npow2.rs @@ -0,0 +1,12 @@ +//@ build-fail +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + // test non-power-of-two, since #[repr(simd, packed)] has unusual layout + let _x: Simd = Simd([0; 24]); + //~^ ERROR the SIMD type `simd::Simd` has more elements than the limit 8 +} diff --git a/tests/ui/simd/simd-lane-limit-err-npow2.stderr b/tests/ui/simd/simd-lane-limit-err-npow2.stderr new file mode 100644 index 0000000000000..fff26c4c1c1b0 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err-npow2.stderr @@ -0,0 +1,8 @@ +error: the SIMD type `simd::Simd` has more elements than the limit 8 + --> $DIR/simd-lane-limit-err-npow2.rs:10:9 + | +LL | let _x: Simd = Simd([0; 24]); + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/simd-lane-limit-err.rs b/tests/ui/simd/simd-lane-limit-err.rs new file mode 100644 index 0000000000000..00390bdbdafac --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err.rs @@ -0,0 +1,11 @@ +//@ build-fail +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + let _x: Simd = Simd([0; 16]); + //~^ ERROR the SIMD type `simd::Simd` has more elements than the limit 8 +} diff --git a/tests/ui/simd/simd-lane-limit-err.stderr b/tests/ui/simd/simd-lane-limit-err.stderr new file mode 100644 index 0000000000000..3f2eaeda2d415 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err.stderr @@ -0,0 +1,8 @@ +error: the SIMD type `simd::Simd` has more elements than the limit 8 + --> $DIR/simd-lane-limit-err.rs:9:9 + | +LL | let _x: Simd = Simd([0; 16]); + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/simd-lane-limit-ok.rs b/tests/ui/simd/simd-lane-limit-ok.rs new file mode 100644 index 0000000000000..52fd3158440ad --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-ok.rs @@ -0,0 +1,14 @@ +//@ build-pass +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + let _x: Simd = Simd([0; 4]); + let _y: Simd = Simd([0; 8]); + + // test non-power-of-two, since #[repr(simd, packed)] has unusual layout + let _z: Simd = Simd([0; 6]); +} diff --git a/tests/ui/simd/type-generic-monomorphisation-empty.rs b/tests/ui/simd/type-generic-monomorphisation-empty.rs index c08dc9fe3df2f..7c43b8914da63 100644 --- a/tests/ui/simd/type-generic-monomorphisation-empty.rs +++ b/tests/ui/simd/type-generic-monomorphisation-empty.rs @@ -6,7 +6,5 @@ struct Simd([f32; N]); fn main() { - let _ = Simd::<0>([]); + let _empty = Simd::<0>([]); //~ ERROR the SIMD type `Simd<0>` has zero elements } - -//~? ERROR monomorphising SIMD type `Simd<0>` of zero length diff --git a/tests/ui/simd/type-generic-monomorphisation-empty.stderr b/tests/ui/simd/type-generic-monomorphisation-empty.stderr index fc294607ae310..450db7e47db03 100644 --- a/tests/ui/simd/type-generic-monomorphisation-empty.stderr +++ b/tests/ui/simd/type-generic-monomorphisation-empty.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd<0>` of zero length +error: the SIMD type `Simd<0>` has zero elements + --> $DIR/type-generic-monomorphisation-empty.rs:9:9 + | +LL | let _empty = Simd::<0>([]); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/type-generic-monomorphisation-oversized.rs b/tests/ui/simd/type-generic-monomorphisation-oversized.rs index efe3480317c9e..73a1f00e8c7f0 100644 --- a/tests/ui/simd/type-generic-monomorphisation-oversized.rs +++ b/tests/ui/simd/type-generic-monomorphisation-oversized.rs @@ -6,7 +6,6 @@ struct Simd([f32; N]); fn main() { - let _ = Simd::<65536>([0.; 65536]); + let _x = Simd::<65536>([0.; 65536]); + //~^ ERROR the SIMD type `Simd<65536>` has more elements than the limit 32768 } - -//~? ERROR monomorphising SIMD type `Simd<65536>` of length greater than 32768 diff --git a/tests/ui/simd/type-generic-monomorphisation-oversized.stderr b/tests/ui/simd/type-generic-monomorphisation-oversized.stderr index 39ff36799cc92..0065049abd68d 100644 --- a/tests/ui/simd/type-generic-monomorphisation-oversized.stderr +++ b/tests/ui/simd/type-generic-monomorphisation-oversized.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd<65536>` of length greater than 32768 +error: the SIMD type `Simd<65536>` has more elements than the limit 32768 + --> $DIR/type-generic-monomorphisation-oversized.rs:9:9 + | +LL | let _x = Simd::<65536>([0.; 65536]); + | ^^ error: aborting due to 1 previous error From 60548ffaa3dafec019e52f39ab7a075d3f11e24e Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 23 Sep 2025 22:14:25 -0400 Subject: [PATCH 0803/1900] Including spans in layout errors for all ADTs --- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc3a84b6a151a..7ecba13d7274c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -469,8 +469,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id), ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id), ty::Adt(def, ..) => match def.adt_kind() { - AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), - AdtKind::Union => build_union_type_di_node(cx, unique_type_id), + AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span), + AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span), AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), @@ -1066,6 +1066,7 @@ fn visibility_di_flags<'ll, 'tcx>( fn build_struct_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let struct_type = unique_type_id.expect_ty(); let ty::Adt(adt_def, _) = struct_type.kind() else { @@ -1073,7 +1074,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( }; assert!(adt_def.is_struct()); let containing_scope = get_namespace_for_item(cx, adt_def.did()); - let struct_type_and_layout = cx.layout_of(struct_type); + let struct_type_and_layout = cx.spanned_layout_of(struct_type, span); let variant_def = adt_def.non_enum_variant(); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(adt_def.did()))) @@ -1266,6 +1267,7 @@ fn build_closure_env_di_node<'ll, 'tcx>( fn build_union_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let union_type = unique_type_id.expect_ty(); let (union_def_id, variant_def) = match union_type.kind() { @@ -1273,7 +1275,7 @@ fn build_union_type_di_node<'ll, 'tcx>( _ => bug!("build_union_type_di_node on a non-ADT"), }; let containing_scope = get_namespace_for_item(cx, union_def_id); - let union_ty_and_layout = cx.layout_of(union_type); + let union_ty_and_layout = cx.spanned_layout_of(union_type, span); let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(union_def_id))) From c9dc0e307ac643518ecf98373f86be5f68a197a0 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 24 Sep 2025 07:01:24 +0200 Subject: [PATCH 0804/1900] temporary-lifetime-extension-tuple-ctor.rs: make usable on all editions Also - add Reference id - fix typo --- .../ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs index bb537f855a4ab..7de786dff3b77 100644 --- a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs +++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs @@ -1,4 +1,4 @@ -//@ edition:2024 +//@ reference: destructors.scope.lifetime-extension.exprs fn temp() -> String { String::from("Hello") @@ -22,7 +22,7 @@ fn main() { let a = &temp(); let b = Some(&temp()); let c = Option::Some::<&String>(&temp()); - use Option::Some as S; + use std::option::Option::Some as S; let d = S(&temp()); let e = X(&temp()); let f = Some(Ok::<_, ()>(std::borrow::Cow::Borrowed(if true { @@ -31,6 +31,6 @@ fn main() { panic!() }))); let some = Some; // Turn the ctor into a regular function. - let g = some(&temp()); //~ERROR temporary value dropped while borrowe + let g = some(&temp()); //~ERROR temporary value dropped while borrowed println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}"); } From 763ef13d3c0ddda792617987b2f13dd40f67c266 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 24 Sep 2025 05:45:12 +0000 Subject: [PATCH 0805/1900] Remove non-ns version of impl_self_ty and impl_trait --- .../crates/hir-ty/src/builder.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 32 +++----- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 +- .../crates/hir-ty/src/infer/path.rs | 10 ++- .../rust-analyzer/crates/hir-ty/src/lower.rs | 77 +++---------------- .../crates/hir-ty/src/lower/path.rs | 9 ++- .../crates/hir-ty/src/lower_nextsolver.rs | 4 +- .../hir-ty/src/lower_nextsolver/path.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 22 ++++-- .../crates/hir-ty/src/mir/eval.rs | 5 +- .../crates/hir-ty/src/next_solver/interner.rs | 8 +- .../crates/hir-ty/src/next_solver/solver.rs | 6 +- .../crates/hir-ty/src/tests/incremental.rs | 2 + .../hir-ty/src/tests/method_resolution.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 19 +++-- .../ide-completion/src/tests/type_pos.rs | 18 ++--- .../ide/src/inlay_hints/closing_brace.rs | 2 +- 17 files changed, 97 insertions(+), 127 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 4957c69ae1656..5511587c71a46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -378,6 +378,6 @@ impl<'db> TyBuilder>> { db: &'db dyn HirDatabase, def: hir_def::ImplId, ) -> TyBuilder>> { - TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty_ns(def)) + TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def)) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 42f7ec6b59fd3..71fb3d44fb707 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -16,8 +16,8 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, - TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, + Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, Ty, + TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, drop::DropGlue, dyn_compatibility::DynCompatibilityViolation, @@ -143,9 +143,12 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: ImplId, ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); - #[salsa::invoke(crate::lower::impl_self_ty_query)] + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] #[salsa::transparent] - fn impl_self_ty(&self, def: ImplId) -> Binders; + fn impl_self_ty<'db>( + &'db self, + def: ImplId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; // FIXME: Make this a non-interned query. #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] @@ -169,9 +172,12 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { Diagnostics, )>; - #[salsa::invoke(crate::lower::impl_trait_query)] + #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] #[salsa::transparent] - fn impl_trait(&self, def: ImplId) -> Option>; + fn impl_trait<'db>( + &'db self, + def: ImplId, + ) -> Option>>; #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] fn field_types_with_diagnostics<'db>( @@ -325,24 +331,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] - #[salsa::transparent] - fn impl_self_ty_ns<'db>( - &'db self, - def: ImplId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; - #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] #[salsa::transparent] fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; - #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] - #[salsa::transparent] - fn impl_trait_ns<'db>( - &'db self, - def: ImplId, - ) -> Option>>; - #[salsa::invoke(crate::lower_nextsolver::field_types_query)] #[salsa::transparent] fn field_types_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0a095ea644589..2c57a5e83d04e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1787,7 +1787,9 @@ impl<'db> InferenceContext<'db> { TypeNs::SelfType(impl_id) => { let generics = crate::generics::generics(self.db, impl_id.into()); let substs = generics.placeholder_subst(self.db); - let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let mut ty = + self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); let Some(remaining_idx) = unresolved else { drop(ctx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 3a50b832cf19d..733f3c278806f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -74,8 +74,11 @@ impl<'db> InferenceContext<'db> { } ValueNs::ImplSelf(impl_id) => { let generics = crate::generics::generics(self.db, impl_id.into()); + let interner = DbInterner::new_with(self.db, None, None); let substs = generics.placeholder_subst(self.db); - let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = + self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { Some(ValuePathResolution::GenericDef( struct_id.into(), @@ -359,10 +362,13 @@ impl<'db> InferenceContext<'db> { }; let substs = match container { ItemContainerId::ImplId(impl_id) => { + let interner = DbInterner::new_with(self.db, None, None); let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None) .fill_with_inference_vars(&mut self.table) .build(); - let impl_self_ty = self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs); + let args: crate::next_solver::GenericArgs<'_> = impl_substs.to_nextsolver(interner); + let impl_self_ty = + self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); self.unify(&impl_self_ty, &ty); impl_substs } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index b55f9cd145e26..0c197b27034bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -25,7 +25,7 @@ use chalk_ir::{ use either::Either; use hir_def::{ AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, + GenericParamId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, @@ -34,8 +34,8 @@ use hir_def::{ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, signatures::{FunctionSignature, TraitFlags}, type_ref::{ - ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, - TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, + ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, TypeBound, TypeRef, + TypeRefId, }, }; use hir_expand::name::Name; @@ -59,6 +59,10 @@ use crate::{ }, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx}, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, static_lifetime, to_chalk_trait_id, to_placeholder_idx, utils::all_super_trait_refs, variable_kinds_from_iter, @@ -565,14 +569,6 @@ impl<'a> TyLoweringContext<'a> { Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx)) } - fn lower_trait_ref( - &mut self, - trait_ref: &HirTraitRef, - explicit_self_ty: Ty, - ) -> Option { - self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) - } - /// When lowering predicates from parents (impl, traits) for children defs (fns, consts, types), `generics` should /// contain the `Generics` for the **child**, while `predicate_owner` should contain the `GenericDefId` of the /// **parent**. This is important so we generate the correct bound var/placeholder. @@ -851,21 +847,21 @@ fn named_associated_type_shorthand_candidates( }) }; + let interner = DbInterner::new_with(db, None, None); match res { TypeNs::SelfType(impl_id) => { - // we're _in_ the impl -- the binders get added back later. Correct, - // but it would be nice to make this more explicit - let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0; + let trait_ref = db.impl_trait(impl_id)?; let impl_id_as_generic_def: GenericDefId = impl_id.into(); if impl_id_as_generic_def != def { let subst = TyBuilder::subst_for_def(db, impl_id, None) .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) .build(); - let trait_ref = subst.apply(trait_ref, Interner); + let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); + let trait_ref = trait_ref.instantiate(interner, args).to_chalk(interner); search(trait_ref) } else { - search(trait_ref) + search(trait_ref.skip_binder().to_chalk(interner)) } } TypeNs::GenericParam(param_id) => { @@ -1335,31 +1331,6 @@ impl ValueTyDefId { } } -pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders { - impl_self_ty_with_diagnostics_query(db, impl_id).0 -} - -pub(crate) fn impl_self_ty_with_diagnostics_query( - db: &dyn HirDatabase, - impl_id: ImplId, -) -> (Binders, Diagnostics) { - let impl_data = db.impl_signature(impl_id); - let resolver = impl_id.resolver(db); - let generics = generics(db, impl_id.into()); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ) - .with_type_param_mode(ParamLoweringMode::Variable); - ( - make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)), - create_diagnostics(ctx.diagnostics), - ) -} - pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { const_param_ty_with_diagnostics_query(db, def).0 } @@ -1397,30 +1368,6 @@ pub(crate) fn const_param_ty_cycle_result( TyKind::Error.intern(Interner) } -pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option> { - impl_trait_with_diagnostics_query(db, impl_id).map(|it| it.0) -} - -pub(crate) fn impl_trait_with_diagnostics_query( - db: &dyn HirDatabase, - impl_id: ImplId, -) -> Option<(Binders, Diagnostics)> { - let impl_data = db.impl_signature(impl_id); - let resolver = impl_id.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ) - .with_type_param_mode(ParamLoweringMode::Variable); - let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); - let target_trait = impl_data.target_trait.as_ref()?; - let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?); - Some((trait_ref, create_diagnostics(ctx.diagnostics))) -} - pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 279bbff7c0d9e..da9dd21183e8f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -255,8 +255,15 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // `def` can be either impl itself or item within, and we need impl itself // now. let generics = generics.parent_or_self(); + let interner = DbInterner::new_with(self.ctx.db, None, None); let subst = generics.placeholder_subst(self.ctx.db); - self.ctx.db.impl_self_ty(impl_id).substitute(Interner, &subst) + let args: crate::next_solver::GenericArgs<'_> = + subst.to_nextsolver(interner); + self.ctx + .db + .impl_self_ty(impl_id) + .instantiate(interner, args) + .to_chalk(interner) } ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id) .fill_with_bound_vars(self.ctx.in_binders, 0) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 02774a63df422..84cd216b81218 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -920,7 +920,7 @@ pub(crate) fn impl_trait_with_diagnostics_query<'db>( impl_id.into(), LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, ); - let self_ty = db.impl_self_ty_ns(impl_id).skip_binder(); + let self_ty = db.impl_self_ty(impl_id).skip_binder(); let target_trait = impl_data.target_trait.as_ref()?; let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); Some((trait_ref, create_diagnostics(ctx.diagnostics))) @@ -2024,7 +2024,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( match res { TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait_ns(impl_id)?; + let trait_ref = db.impl_trait(impl_id)?; // FIXME(next-solver): same method in `lower` checks for impl or not // Is that needed here? diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index 46dc66a8c8bae..0a9f34c9dabd6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -287,7 +287,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } } } - TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(), + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), TypeNs::AdtSelfType(adt) => { let args = crate::next_solver::GenericArgs::identity_for_item( self.ctx.interner, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 93ef64272d14b..61d3091a0c1d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -297,11 +297,12 @@ impl TraitImpls { continue; } let target_trait = match db.impl_trait(impl_id) { - Some(tr) => tr.skip_binders().hir_trait_id(), + Some(tr) => tr.skip_binder().def_id.0, None => continue, }; - let self_ty = db.impl_self_ty(impl_id); - let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); + let interner = DbInterner::new_with(db, None, None); + let self_ty = db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner); + let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty); map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id); } @@ -414,8 +415,8 @@ impl InherentImpls { continue; } - let self_ty = db.impl_self_ty(impl_id); - let self_ty = self_ty.skip_binders(); + let interner = DbInterner::new_with(db, None, None); + let self_ty = &db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner); match is_inherent_impl_coherent(db, def_map, impl_id, self_ty) { true => { @@ -897,10 +898,13 @@ fn find_matching_impl( table.run_in_snapshot(|table| { let impl_substs = TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build(); + let args: crate::next_solver::GenericArgs<'_> = + impl_substs.to_nextsolver(table.interner); let trait_ref = db .impl_trait(impl_) .expect("non-trait method in find_matching_impl") - .substitute(Interner, &impl_substs); + .instantiate(table.interner, args) + .to_chalk(table.interner); if !table.unify(&trait_ref, &actual_trait_ref) { return None; @@ -1018,7 +1022,9 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { let local_crate = impl_.lookup(db).container.krate(); let is_local = |tgt_crate| tgt_crate == local_crate; - let trait_ref = impl_trait.substitute(Interner, &substs); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let trait_ref = impl_trait.instantiate(interner, args).to_chalk(interner); let trait_id = from_chalk_trait_id(trait_ref.trait_id); if is_local(trait_id.module(db).krate()) { // trait to be implemented is local @@ -1824,7 +1830,7 @@ fn is_valid_impl_fn_candidate( let _p = tracing::info_span!("subst_for_def").entered(); let impl_subst = table.infer_ctxt.fresh_args_for_item(impl_id.into()); let expect_self_ty = db - .impl_self_ty_ns(impl_id) + .impl_self_ty(impl_id) .instantiate(table.interner, &impl_subst) .to_chalk(table.interner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 3d7ad8d3b0693..fa63baa5d2ab8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -432,9 +432,12 @@ impl MirEvalError { let self_ = match func.lookup(db).container { ItemContainerId::ImplId(impl_id) => Some({ let generics = crate::generics::generics(db, impl_id.into()); + let interner = DbInterner::new_with(db, None, None); let substs = generics.placeholder_subst(db); + let args: crate::next_solver::GenericArgs<'_> = + substs.to_nextsolver(interner); db.impl_self_ty(impl_id) - .substitute(Interner, &substs) + .instantiate(interner, args) .display(db, display_target) .to_string() }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 3ff9fde9768b2..b72504a19cf0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1394,7 +1394,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, impl_id: Self::ImplId, ) -> EarlyBinder> { - let trait_ref = self.db().impl_trait_ns(impl_id.0).expect("expected an impl of trait"); + let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait"); trait_ref.map_bound(|trait_ref| { let clause: Clause<'_> = trait_ref.upcast(self); Clauses::new_from_iter( @@ -1633,7 +1633,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { |impls| { for i in impls.for_trait(trait_) { use rustc_type_ir::TypeVisitable; - let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + let contains_errors = self.db().impl_trait(i).map_or(false, |b| { b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() }); if contains_errors { @@ -1656,7 +1656,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { for fp in fps { for i in impls.for_trait_and_self_ty(trait_, *fp) { use rustc_type_ir::TypeVisitable; - let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + let contains_errors = self.db().impl_trait(i).map_or(false, |b| { b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() }); if contains_errors { @@ -1702,7 +1702,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { impl_id: Self::ImplId, ) -> EarlyBinder> { let db = self.db(); - db.impl_trait_ns(impl_id.0) + db.impl_trait(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach trait solving .expect("invalid impl passed to trait solver") } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 946e57e6cb741..a161423da4d76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -156,16 +156,16 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::TypeAliasId(id) => id, _ => panic!("Unexpected SolverDefId"), }; - let trait_ref = self + let trait_ = self .0 .interner .db() .impl_trait(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach solver .expect("invalid impl passed to next-solver") - .into_value_and_skipped_binders() + .skip_binder() + .def_id .0; - let trait_ = trait_ref.hir_trait_id(); let trait_data = trait_.trait_items(self.0.interner.db()); let id = impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index cc5ad2045ed4f..8587c13e87f77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -504,8 +504,10 @@ impl SomeStruct { "crate_local_def_map", "trait_impls_in_crate_shim", "attrs_shim", + "impl_trait_with_diagnostics_shim", "impl_signature_shim", "impl_signature_with_source_map_shim", + "impl_self_ty_with_diagnostics_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", "attrs_shim", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 5ada14bc5342e..2f8f666475668 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -2050,7 +2050,7 @@ impl dyn Error + Send { /// Attempts to downcast the box to a concrete type. pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; - // ^^^^ expected Box, got Box + // ^^^^ expected Box, got Box // FIXME, type mismatch should not occur ::downcast(err).map_err(|_| loop {}) //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5592ca285fb01..c8ce46c4732cb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -869,10 +869,13 @@ impl Module { .collect(); if !missing.is_empty() { - let self_ty = db.impl_self_ty(impl_def.id).substitute( - Interner, - &hir_ty::generics::generics(db, impl_def.id.into()).placeholder_subst(db), - ); + let interner = DbInterner::new_with(db, None, None); + let args: crate::next_solver::GenericArgs<'_> = + hir_ty::generics::generics(db, impl_def.id.into()) + .placeholder_subst(db) + .to_nextsolver(interner); + let self_ty = + db.impl_self_ty(impl_def.id).instantiate(interner, args).to_chalk(interner); let self_ty = if let TyKind::Alias(AliasTy::Projection(projection)) = self_ty.kind(Interner) { @@ -4546,21 +4549,23 @@ impl Impl { } pub fn trait_(self, db: &dyn HirDatabase) -> Option { - let trait_ref = db.impl_trait_ns(self.id)?; + let trait_ref = db.impl_trait(self.id)?; let id = trait_ref.skip_binder().def_id; Some(Trait { id: id.0 }) } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { - let trait_ref = db.impl_trait_ns(self.id)?.instantiate_identity(); + let trait_ref = db.impl_trait(self.id)?.instantiate_identity(); let resolver = self.id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); let substs = TyBuilder::placeholder_subst(db, self.id); - let ty = db.impl_self_ty(self.id).substitute(Interner, &substs); + let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); + let ty = db.impl_self_ty(self.id).instantiate(interner, args).to_chalk(interner); Type::new_with_resolver_inner(db, &resolver, ty) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index c7e2d058257e3..125e11e9e3589 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -429,18 +429,18 @@ trait Tr { impl Tr<$0 "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - sp Self dyn Tr<{unknown}> - st Record Record - st S S - st Tuple Tuple - st Unit Unit + sp Self dyn Tr<{unknown}> + 'static + st Record Record + st S S + st Tuple Tuple + st Unit Unit tt Tr tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index e80c9dc9d4732..9d246eda57e04 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -191,7 +191,7 @@ impl Tr for () { //^ impl Tr for () impl dyn Tr { } -//^ impl dyn Tr +//^ impl dyn Tr + 'static static S0: () = 0; static S1: () = {}; From 6e897949a5a1fb3bb70c5166cced9b24af879882 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 24 Sep 2025 14:36:09 +0800 Subject: [PATCH 0806/1900] Fix applicable on if-let-chain for invert_if Example --- ```rust fn f() { i$0f x && let Some(_) = Some(1) { 1 } else { 0 } } ``` **Before this PR**: ```rust fn f() { if !(x && let Some(_) = Some(1)) { 0 } else { 1 } } ``` **After this PR**: Assist not applicable --- .../crates/ide-assists/src/handlers/invert_if.rs | 12 ++++++++++++ .../crates/ide-db/src/syntax_helpers/node_ext.rs | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs index d198870b023e6..7576d2fab976f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs @@ -124,6 +124,18 @@ mod tests { ) } + #[test] + fn invert_if_doesnt_apply_with_if_let_chain() { + check_assist_not_applicable( + invert_if, + "fn f() { i$0f x && let Some(_) = Some(1) { 1 } else { 0 } }", + ); + check_assist_not_applicable( + invert_if, + "fn f() { i$0f let Some(_) = Some(1) && x { 1 } else { 0 } }", + ); + } + #[test] fn invert_if_option_case() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index cefd8fd49676e..e1d140730edce 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -265,10 +265,7 @@ pub fn is_pattern_cond(expr: ast::Expr) -> bool { ast::Expr::BinExpr(expr) if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) => { - expr.lhs() - .map(is_pattern_cond) - .or_else(|| expr.rhs().map(is_pattern_cond)) - .unwrap_or(false) + expr.lhs().map_or(false, is_pattern_cond) || expr.rhs().map_or(false, is_pattern_cond) } ast::Expr::ParenExpr(expr) => expr.expr().is_some_and(is_pattern_cond), ast::Expr::LetExpr(_) => true, From 11627f00c0599c5d033b3e38e3196786537c439b Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sun, 21 Sep 2025 04:28:19 +0000 Subject: [PATCH 0807/1900] Revise `MaybeUninit` validity documentation Let's rewrite this for better clarity. In particular, let's document our language guarantees upfront and in positive form. We'll then list the caveats and the non-guarantees after. --- library/core/src/mem/maybe_uninit.rs | 101 ++++++++++++++++++--------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 47a34821e9018..2b074416141ce 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -255,51 +255,86 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// # Validity /// -/// A `MaybeUninit` has no validity requirement – any sequence of -/// [bytes][reference-byte] of the appropriate length, initialized or -/// uninitialized, are a valid representation of `MaybeUninit`. -/// -/// However, "round-tripping" via `MaybeUninit` does not always result in the -/// original value. `MaybeUninit` can have padding, and the contents of that -/// padding are not preserved. Concretely, given distinct `T` and `U` where -/// `size_of::() == size_of::()`, the following code is not guaranteed to -/// be sound: +/// `MaybeUninit` has no validity requirements –- any sequence of [bytes] of +/// the appropriate length, initialized or uninitialized, are a valid +/// representation. +/// +/// Moving or copying a value of type `MaybeUninit` (i.e., performing a +/// "typed copy") will exactly preserve the contents, including the +/// [provenance], of all non-padding bytes of type `T` in the value's +/// representation. +/// +/// Therefore `MaybeUninit` can be used to perform a round trip of a value from +/// type `T` to type `MaybeUninit` then back to type `T`, while preserving +/// the original value, if two conditions are met. One, type `U` must have the +/// same size as type `T`. Two, for all byte offsets where type `U` has padding, +/// the corresponding bytes in the representation of the value must be +/// uninitialized. +/// +/// For example, due to the fact that the type `[u8; size_of::]` has no +/// padding, the following is sound for any type `T` and will return the +/// original value: /// /// ```rust,no_run /// # use core::mem::{MaybeUninit, transmute}; -/// # struct T; struct U; +/// # struct T; /// fn identity(t: T) -> T { /// unsafe { +/// let u: MaybeUninit<[u8; size_of::()]> = transmute(t); +/// transmute(u) // OK. +/// } +/// } +/// ``` +/// +/// Note: Copying a value that contains references may implicitly reborrow them +/// causing the provenance of the returned value to differ from that of the +/// original. This applies equally to the trivial identity function: +/// +/// ```rust,no_run +/// fn trivial_identity(t: T) -> T { t } +/// ``` +/// +/// Note: Moving or copying a value whose representation has initialized bytes +/// at byte offsets where the type has padding may lose the value of those +/// bytes, so while the original value will be preserved, the original +/// *representation* of that value as bytes may not be. Again, this applies +/// equally to `trivial_identity`. +/// +/// Note: Performing this round trip when type `U` has padding at byte offsets +/// where the representation of the original value has initialized bytes may +/// produce undefined behavior or a different value. For example, the following +/// is unsound since `T` requires all bytes to be initialized: +/// +/// ```rust,no_run +/// # use core::mem::{MaybeUninit, transmute}; +/// #[repr(C)] struct T([u8; 4]); +/// #[repr(C)] struct U(u8, u16); +/// fn unsound_identity(t: T) -> T { +/// unsafe { /// let u: MaybeUninit = transmute(t); -/// transmute(u) +/// transmute(u) // UB. /// } /// } /// ``` /// -/// If the representation of `t` contains initialized bytes at byte offsets -/// where `U` contains padding bytes, these may not be preserved in -/// `MaybeUninit`. Transmuting `u` back to `T` (i.e., `transmute(u)` above) -/// may thus be undefined behavior or yield a value different from `t` due to -/// those bytes being lost. This is an active area of discussion, and this code -/// may become sound in the future. -/// -/// However, so long as no such byte offsets exist, then the preceding -/// `identity` example *is* sound. In particular, since `[u8; N]` has no padding -/// bytes, transmuting `t` to `MaybeUninit<[u8; size_of::]>` and back will -/// always produce the original value `t` again. This is true even if `t` -/// contains [provenance]: the resulting value will have the same provenance as -/// the original `t`. -/// -/// Note a potential footgun: if `t` contains a reference, then there may be -/// implicit reborrows of the reference any time it is copied, which may alter -/// its provenance. In that case, the value returned by `identity` may not be -/// exactly the same as its argument. However, even in this case, it remains -/// true that `identity` behaves the same as a function that just returns `t` -/// immediately (i.e., `fn identity(t: T) -> T { t }`). +/// Conversely, the following is sound since `T` allows uninitialized bytes in +/// the representation of a value, but the round trip may alter the value: /// -/// [provenance]: crate::ptr#provenance +/// ```rust,no_run +/// # use core::mem::{MaybeUninit, transmute}; +/// #[repr(C)] struct T(MaybeUninit<[u8; 4]>); +/// #[repr(C)] struct U(u8, u16); +/// fn non_identity(t: T) -> T { +/// unsafe { +/// // May lose an initialized byte. +/// let u: MaybeUninit = transmute(t); +/// transmute(u) +/// } +/// } +/// ``` /// -/// [reference-byte]: ../../reference/memory-model.html#bytes +/// [bytes]: ../../reference/memory-model.html#bytes +/// [provenance]: crate::ptr#provenance #[stable(feature = "maybe_uninit", since = "1.36.0")] // Lang item so we can wrap other types in it. This is useful for coroutines. #[lang = "maybe_uninit"] From 03fd823dbf39ebbd69f6e20e9d44c0cb893140c3 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 24 Sep 2025 13:26:03 +0530 Subject: [PATCH 0808/1900] library: std: sys: pal: uefi: Add some comments I seemed to have forgotten that since I am using GET_PROTOCOL attribute for the std usecases, I did not need to close the protocols explicitly. So adding these comments as a note to future self not to waste time on the same thing again. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/helpers.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index b50574de937aa..c0d69c3e0029a 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -92,6 +92,9 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( handle: NonNull, mut protocol_guid: Guid, @@ -473,6 +476,7 @@ impl<'a> crate::fmt::Debug for DevicePathNode<'a> { } } +/// Protocols installed by Rust side on a handle. pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, From 8e37f0f120ba8b77889bea3fc607004c89afffb4 Mon Sep 17 00:00:00 2001 From: sysrex <769991+sysrex@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:13:08 +0100 Subject: [PATCH 0809/1900] chore: remove discord references from the std library as well --- library/std/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 33e3bf0c085da..9266a0af2c83b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -94,7 +94,7 @@ //! pull-requests for your suggested changes. //! //! Contributions are appreciated! If you see a part of the docs that can be -//! improved, submit a PR, or chat with us first on [Discord][rust-discord] +//! improved, submit a PR, or chat with us first on [Zulip][rust-zulip] //! #docs. //! //! # A Tour of The Rust Standard Library @@ -212,7 +212,7 @@ //! [multithreading]: thread //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html -//! [rust-discord]: https://discord.gg/rust-lang +//! [rust-zulip]: https://rust-lang.zulipchat.com/ //! [array]: prim@array //! [slice]: prim@slice From 64bfe7c98f126a189218f0615a377fa0d7412c77 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Sep 2025 10:56:32 +0200 Subject: [PATCH 0810/1900] genmc doc edits --- src/tools/miri/doc/genmc.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 3e5859c6f5b27..7da7a3d189487 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -63,18 +63,14 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Eliminating unbounded loops -#### Limiting the number of explored executions +As mentioned above, GenMC requires all loops to be bounded. +Otherwise, it is not possible to exhaustively explore all executions. +Currently, Miri-GenMC has no support for automatically bounding loops, so this needs to be done manually. -The number of explored executions in a concurrent program can increase super-exponentially in the size of the program. -Reducing the number of explored executions is the most impactful way to improve verification times. -Some programs also contain possibly infinite loops, which are not supported by GenMC. +#### Bounding loops without side effects -One way to drastically improve verification performance is by bounding spinloops. -A spinloop may loop a many times before it can finally make progress. -If such a loop doesn't have any visible side effects, meaning it does not matter to the outcome of the program whether the loop ran once or a million time, then the loop can be limited to one iteration. - -The following code gives an example for how to replace a loop that waits for a boolean to be true. -Since there are no side effects, replacing the loop with one iteration is safe. +The easiest case is that of a loop that simply spins until it observes a certain condition, without any side effects. +Such loops can be limited to one iteration, as demonstrated by the following example: ```rust #[cfg(miri)] @@ -84,21 +80,23 @@ unsafe extern "Rust" { pub unsafe fn miri_genmc_assume(condition: bool); } -/// This functions loads an atomic boolean in a loop until it is true. -/// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. +// This functions loads an atomic boolean in a loop until it is true. +// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. fn spin_until_true(flag: &AtomicBool) { - while (!flag.load(Relaxed)) { + while !flag.load(Relaxed) { std::hint::spin_loop(); } } -/// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load. -/// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. +// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load that observes the expected value. +// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. fn spin_until_true_genmc(flag: &AtomicBool) { - unsafe { miri_genmc_assume(flag.load(Relaxed)); } + unsafe { miri_genmc_assume(flag.load(Relaxed)) }; } ``` +#### Bounding loops with side effects + Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program. Replacing the loop with one iteration like we did above would mean we miss all those possible executions. @@ -110,11 +108,11 @@ The choice of iteration limit trades off verification time for possibly missing /// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations. fn count_until_true_genmc(flag: &AtomicBool) -> u64 { let mut count = 0; - while (!flag.load(Relaxed)) { - // Any execution that takes more than 3 iterations will not be explored. - unsafe { miri_genmc_assume(count < 3); } + while !flag.load(Relaxed) { count += 1; std::hint::spin_loop(); + // Any execution that takes more than 3 iterations will not be explored. + unsafe { miri_genmc_assume(count <= 3) }; } count } From 3378997867a9501beb3137f1ae508aadf1b9fc4b Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 12:50:50 +0200 Subject: [PATCH 0811/1900] fix wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémy Rakic --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e05c5b224858b..be44dc955c213 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -540,7 +540,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // If there are nested bodies in promoteds, we also need to update their - // location to something in the actually body, not the promoted. + // location to something in the actual body, not the promoted. // // We don't update the constraint categories of the resulting constraints // as returns in nested bodies are a proper return, even if that nested body From 32d24f9efa81a1c467da21c3ae2d93a6b00a9f5b Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 13:02:27 +0200 Subject: [PATCH 0812/1900] allow bound regions in writeback --- compiler/rustc_hir_typeck/src/writeback.rs | 6 +++-- tests/crashes/117808.rs | 27 ------------------- .../writeback-predicate-bound-region.rs | 14 ++++++++++ 3 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 tests/crashes/117808.rs create mode 100644 tests/ui/traits/next-solver/writeback-predicate-bound-region.rs diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6192420898f6e..d01eeb9a4b691 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -1003,8 +1003,10 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - debug_assert!(!r.is_bound(), "Should not be resolving bound region."); - self.fcx.tcx.lifetimes.re_erased + match r.kind() { + ty::ReBound(..) => r, + _ => self.fcx.tcx.lifetimes.re_erased, + } } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/tests/crashes/117808.rs b/tests/crashes/117808.rs deleted file mode 100644 index 2c727986dd07f..0000000000000 --- a/tests/crashes/117808.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ known-bug: #117808 -//@ edition:2021 -//@ needs-rustc-debug-assertions - -use std::future::Future; - -fn hrc AsyncClosure<'a, (), R>>(f: F) -> F { - f -} - -fn main() { - hrc(|x| async {}); -} - -trait AsyncClosure<'a, I, R> -where - I: 'a, -{ -} - -impl<'a, I, R, Fut, F> AsyncClosure<'a, I, R> for F -where - I: 'a, - F: Fn(&'a I) -> Fut, - Fut: Future + Send + 'a, -{ -} diff --git a/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs b/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs new file mode 100644 index 0000000000000..a7ed5dbcf086d --- /dev/null +++ b/tests/ui/traits/next-solver/writeback-predicate-bound-region.rs @@ -0,0 +1,14 @@ +//@ edition: 2024 +//@ check-pass +//@ compile-flags: -Znext-solver + +// This previously ICE'd during writeback when resolving +// the stalled coroutine predicate due to its bound lifetime. + +trait Trait<'a> {} +impl<'a, T: Send> Trait<'a> for T {} + +fn is_trait Trait<'a>>(_: T) {} +fn main() { + is_trait(async {}) +} From 2886ca496a8adff7424da8b19eeaffa9d392210c Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 13:04:36 +0200 Subject: [PATCH 0813/1900] imrpove type_op failure ICE --- .../src/traits/query/type_op/custom.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 6ce68507d6521..a96cb738b81f0 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 @@ -100,9 +100,9 @@ where } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { Err(guar) } else { - Err(infcx - .dcx() - .delayed_bug(format!("errors selecting obligation during MIR typeck: {errors:?}"))) + Err(infcx.dcx().delayed_bug(format!( + "errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}" + ))) } })?; From 0a41add6293d76b165968d70f95e7edbbe65fe11 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Sep 2025 19:46:30 +0200 Subject: [PATCH 0814/1900] const-eval: improve and actually test the errors when pointers might be outside the range of a scalar --- compiler/rustc_const_eval/messages.ftl | 4 ++-- compiler/rustc_const_eval/src/errors.rs | 7 +++---- .../rustc_const_eval/src/interpret/validity.rs | 5 +---- .../rustc_middle/src/mir/interpret/error.rs | 5 +---- tests/ui/consts/const-eval/ub-nonnull.rs | 4 ++++ tests/ui/consts/const-eval/ub-nonnull.stderr | 17 ++++++++++++++--- tests/ui/consts/const-eval/ub-ref-ptr.rs | 11 ++++++++++- tests/ui/consts/const-eval/ub-ref-ptr.stderr | 17 ++++++++++++++--- 8 files changed, 49 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 700d7c26752b6..97674aea540ca 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -479,11 +479,11 @@ const_eval_validation_never_val = {$front_matter}: encountered a value of the ne const_eval_validation_null_box = {$front_matter}: encountered a null box const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer const_eval_validation_null_ref = {$front_matter}: encountered a null reference -const_eval_validation_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} -const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range} const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 2d412ee5ec26e..29bddd59ffd59 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -668,7 +668,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { MutableRefInConst => const_eval_validation_mutable_ref_in_const, NullFnPtr => const_eval_validation_null_fn_ptr, NeverVal => const_eval_validation_never_val, - NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range, + NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range, PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, OutOfRange { .. } => const_eval_validation_out_of_range, UnsafeCellInImmutable => const_eval_validation_unsafe_cell, @@ -804,9 +804,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | InvalidFnPtr { value } => { err.arg("value", value); } - NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => { - add_range_arg(range, max_value, err) - } + PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, err), OutOfRange { range, max_value, value } => { err.arg("value", value); add_range_arg(range, max_value, err); @@ -826,6 +824,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | MutableRefToImmutable | MutableRefInConst | NullFnPtr + | NonnullPtrMaybeNull | NeverVal | UnsafeCellInImmutable | InvalidMetaSliceTooLarge { .. } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 9adc3fa463180..8648b83b8dcd2 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -819,10 +819,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if start == 1 && end == max_value { // Only null is the niche. So make sure the ptr is NOT null. if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!( - self.path, - NullablePtrOutOfRange { range: valid_range, max_value } - ) + throw_validation_failure!(self.path, NonnullPtrMaybeNull) } else { return interp_ok(()); } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 7c72a8ec243a8..1a9ae4bbceb4f 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -499,10 +499,7 @@ pub enum ValidationErrorKind<'tcx> { MutableRefInConst, NullFnPtr, NeverVal, - NullablePtrOutOfRange { - range: WrappingRange, - max_value: u128, - }, + NonnullPtrMaybeNull, PtrOutOfRange { range: WrappingRange, max_value: u128, diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index 9164684262480..851f3996cd104 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -57,4 +57,8 @@ const NULL_FAT_PTR: NonNull = unsafe { mem::transmute((0_usize, meta)) }; +static S: u32 = 0; // just a static to construct a maybe-null pointer off of +const MAYBE_NULL_PTR: NonNull<()> = unsafe { mem::transmute((&raw const S).wrapping_add(4)) }; +//~^ ERROR invalid value + fn main() {} diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index 91c82efbc5ed1..e4486e3c500b9 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -9,7 +9,7 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC1 which is only 1 byte from the end of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC2 which is only 1 byte from the end of the allocation --> $DIR/ub-nonnull.rs:22:29 | LL | let out_of_bounds_ptr = &ptr[255]; @@ -37,7 +37,7 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC2[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC3[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory --> $DIR/ub-nonnull.rs:36:38 | LL | const UNINIT: NonZero = unsafe { MaybeUninit { uninit: () }.init }; @@ -80,6 +80,17 @@ LL | const NULL_FAT_PTR: NonNull = unsafe { HEX_DUMP } -error: aborting due to 8 previous errors +error[E0080]: constructing invalid value: encountered a maybe-null pointer, but expected something that is definitely non-zero + --> $DIR/ub-nonnull.rs:61:1 + | +LL | const MAYBE_NULL_PTR: NonNull<()> = unsafe { mem::transmute((&raw const S).wrapping_add(4)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = 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. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.rs b/tests/ui/consts/const-eval/ub-ref-ptr.rs index d8e5102fcbe78..5ff9748af0c15 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.rs +++ b/tests/ui/consts/const-eval/ub-ref-ptr.rs @@ -4,7 +4,7 @@ //@ normalize-stderr: "([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ dont-require-annotations: NOTE //@ normalize-stderr: "0x[0-9](\.\.|\])" -> "0x%$1" - +#![feature(rustc_attrs)] #![allow(invalid_value)] use std::mem; @@ -65,5 +65,14 @@ const UNALIGNED_READ: () = unsafe { ptr.read(); //~ ERROR accessing memory }; +// Check the general case of a pointer value not falling into the scalar valid range. +#[rustc_layout_scalar_valid_range_start(1000)] +pub struct High { + pointer: *const (), +} +static S: u32 = 0; // just a static to construct a pointer with unknown absolute address +const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) }; +//~^ ERROR invalid value + fn main() {} diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index c45f66c29259c..9170cde94e2ad 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -103,7 +103,7 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC3[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC4[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory --> $DIR/ub-ref-ptr.rs:49:41 | LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; @@ -124,7 +124,7 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC4[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC5[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory --> $DIR/ub-ref-ptr.rs:54:38 | LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; @@ -162,6 +162,17 @@ error[E0080]: accessing memory based on pointer with alignment 1, but alignment LL | ptr.read(); | ^^^^^^^^^^ evaluation of `UNALIGNED_READ` failed here -error: aborting due to 15 previous errors +error[E0080]: constructing invalid value: encountered a pointer with unknown absolute address, but expected something that is definitely greater or equal to 1000 + --> $DIR/ub-ref-ptr.rs:74:1 + | +LL | const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = 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. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0080`. From 8328c3dada0c888b1c570f97314b3f697d4b2a96 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Sep 2025 12:12:41 +0200 Subject: [PATCH 0815/1900] const validation: better error for maybe-null references --- compiler/rustc_const_eval/messages.ftl | 10 +++- compiler/rustc_const_eval/src/errors.rs | 10 ++-- .../src/interpret/validity.rs | 13 ++++- .../rustc_middle/src/mir/interpret/error.rs | 2 + src/tools/compiletest/src/runtest.rs | 2 +- tests/ui/consts/const-eval/ub-ref-ptr.rs | 12 +++- tests/ui/consts/const-eval/ub-ref-ptr.stderr | 58 +++++++++++++------ tests/ui/consts/const_transmute_type_id7.rs | 16 +++++ .../ui/consts/const_transmute_type_id7.stderr | 14 +++++ 9 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 tests/ui/consts/const_transmute_type_id7.rs create mode 100644 tests/ui/consts/const_transmute_type_id7.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 97674aea540ca..010ffa60c7a53 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -476,9 +476,15 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` -const_eval_validation_null_box = {$front_matter}: encountered a null box +const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null + } box const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer -const_eval_validation_null_ref = {$front_matter}: encountered a null reference +const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null + } reference const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 29bddd59ffd59..d352a6384245f 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -696,8 +696,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box, - NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box, - NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref, + NullPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_null_box, + NullPtr { ptr_kind: PointerKind::Ref(_), .. } => const_eval_validation_null_ref, DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { const_eval_validation_dangling_box_no_provenance } @@ -820,8 +820,10 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); err.arg("expected_dyn_type", expected_dyn_type.to_string()); } - NullPtr { .. } - | MutableRefToImmutable + NullPtr { maybe, .. } => { + err.arg("maybe", maybe); + } + MutableRefToImmutable | MutableRefInConst | NullFnPtr | NonnullPtrMaybeNull diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 8648b83b8dcd2..5f088fe37e80b 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -511,7 +511,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message ), self.path, - Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind }, + Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false }, Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance { ptr_kind, // FIXME this says "null pointer" when null but we need translate @@ -538,8 +538,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ); // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero // that does not imply non-null. - if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { - throw_validation_failure!(self.path, NullPtr { ptr_kind }) + let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx); + if self.ecx.scalar_may_be_null(scalar)? { + let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); + throw_validation_failure!(self.path, NullPtr { ptr_kind, maybe }) } // Do not allow references to uninhabited types. if place.layout.is_uninhabited() { @@ -757,6 +759,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } else { // Otherwise (for standalone Miri), we have to still check it to be non-null. if self.ecx.scalar_may_be_null(scalar)? { + let maybe = + !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); + // This can't be a "maybe-null" pointer since the check for this being + // a fn ptr at all already ensures that the pointer is inbounds. + assert!(!maybe); throw_validation_failure!(self.path, NullFnPtr); } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 1a9ae4bbceb4f..951aac503fee8 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -541,6 +541,8 @@ pub enum ValidationErrorKind<'tcx> { }, NullPtr { ptr_kind: PointerKind, + /// Records whether this pointer is definitely null or just may be null. + maybe: bool, }, DanglingPtrNoProvenance { ptr_kind: PointerKind, diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 89fb8eb4357b7..59bd3111ecf35 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2655,7 +2655,7 @@ impl<'test> TestCx<'test> { // The alloc-id appears in pretty-printed allocations. normalized = static_regex!( - r"╾─*a(lloc)?([0-9]+)(\+0x[0-9]+)?()?( \([0-9]+ ptr bytes\))?─*╼" + r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?()?( \([0-9]+ ptr bytes\))?─*╼" ) .replace_all(&normalized, |caps: &Captures<'_>| { // Renumber the captured index. diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.rs b/tests/ui/consts/const-eval/ub-ref-ptr.rs index 5ff9748af0c15..a5fdde1f9a4e9 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.rs +++ b/tests/ui/consts/const-eval/ub-ref-ptr.rs @@ -27,6 +27,11 @@ const NULL: &u16 = unsafe { mem::transmute(0usize) }; const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; //~^ ERROR invalid value +const MAYBE_NULL_BOX: Box<()> = unsafe { mem::transmute({ +//~^ ERROR maybe-null + let ref_ = &0u8; + (ref_ as *const u8).wrapping_add(10) +}) }; // It is very important that we reject this: We do promote `&(4 * REF_AS_USIZE)`, // but that would fail to compile; so we ended up breaking user code that would @@ -57,7 +62,12 @@ const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; //~^ ERROR invalid value const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; //~^ ERROR invalid value - +const MAYBE_NULL_FN_PTR: fn() = unsafe { mem::transmute({ +//~^ ERROR invalid value + fn fun() {} + let ptr = fun as fn(); + (ptr as *const u8).wrapping_add(10) +}) }; const UNALIGNED_READ: () = unsafe { let x = &[0u8; 4]; diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index 9170cde94e2ad..349a98f11be42 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -42,8 +42,19 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; HEX_DUMP } +error[E0080]: constructing invalid value: encountered a maybe-null box + --> $DIR/ub-ref-ptr.rs:30:1 + | +LL | const MAYBE_NULL_BOX: Box<()> = unsafe { mem::transmute({ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = 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. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:34:1 + --> $DIR/ub-ref-ptr.rs:39:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE` failed here @@ -52,7 +63,7 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:37:39 + --> $DIR/ub-ref-ptr.rs:42:39 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_SLICE` failed here @@ -61,13 +72,13 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:37:38 + --> $DIR/ub-ref-ptr.rs:42:38 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:40:86 + --> $DIR/ub-ref-ptr.rs:45:86 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_BOX_SLICE` failed here @@ -76,13 +87,13 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:40:85 + --> $DIR/ub-ref-ptr.rs:45:85 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance) - --> $DIR/ub-ref-ptr.rs:43:1 + --> $DIR/ub-ref-ptr.rs:48:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -93,7 +104,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance) - --> $DIR/ub-ref-ptr.rs:46:1 + --> $DIR/ub-ref-ptr.rs:51:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -103,8 +114,8 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC4[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-ref-ptr.rs:49:41 +error[E0080]: reading memory at ALLOC6[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-ref-ptr.rs:54:41 | LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_PTR` failed here @@ -114,7 +125,7 @@ LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; } error[E0080]: constructing invalid value: encountered null pointer, but expected a function pointer - --> $DIR/ub-ref-ptr.rs:52:1 + --> $DIR/ub-ref-ptr.rs:57:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -124,8 +135,8 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC5[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-ref-ptr.rs:54:38 +error[E0080]: reading memory at ALLOC7[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-ref-ptr.rs:59:38 | LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_FN_PTR` failed here @@ -135,7 +146,7 @@ LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; } error[E0080]: constructing invalid value: encountered 0xd[noalloc], but expected a function pointer - --> $DIR/ub-ref-ptr.rs:56:1 + --> $DIR/ub-ref-ptr.rs:61:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -145,8 +156,8 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; HEX_DUMP } -error[E0080]: constructing invalid value: encountered ALLOC2, but expected a function pointer - --> $DIR/ub-ref-ptr.rs:58:1 +error[E0080]: constructing invalid value: encountered ALLOC3, but expected a function pointer + --> $DIR/ub-ref-ptr.rs:63:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -156,14 +167,25 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; HEX_DUMP } +error[E0080]: constructing invalid value: encountered ALLOC4+0xa, but expected a function pointer + --> $DIR/ub-ref-ptr.rs:65:1 + | +LL | const MAYBE_NULL_FN_PTR: fn() = unsafe { mem::transmute({ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = 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. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + error[E0080]: accessing memory based on pointer with alignment 1, but alignment 4 is required - --> $DIR/ub-ref-ptr.rs:65:5 + --> $DIR/ub-ref-ptr.rs:75:5 | LL | ptr.read(); | ^^^^^^^^^^ evaluation of `UNALIGNED_READ` failed here error[E0080]: constructing invalid value: encountered a pointer with unknown absolute address, but expected something that is definitely greater or equal to 1000 - --> $DIR/ub-ref-ptr.rs:74:1 + --> $DIR/ub-ref-ptr.rs:84:1 | LL | const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -173,6 +195,6 @@ LL | const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) }; HEX_DUMP } -error: aborting due to 16 previous errors +error: aborting due to 18 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const_transmute_type_id7.rs b/tests/ui/consts/const_transmute_type_id7.rs new file mode 100644 index 0000000000000..73b8187a8007a --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id7.rs @@ -0,0 +1,16 @@ +//! Ensure a decent error message for maybe-null references. +//! (see ) + +// Strip out raw byte dumps to make comparison platform-independent: +//@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" + +#![feature(const_trait_impl, const_cmp)] + +use std::any::TypeId; +use std::mem::transmute; + +const A: [&(); 16 / size_of::<*const ()>()] = unsafe { transmute(TypeId::of::()) }; +//~^ERROR: maybe-null + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id7.stderr b/tests/ui/consts/const_transmute_type_id7.stderr new file mode 100644 index 0000000000000..664975831f402 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id7.stderr @@ -0,0 +1,14 @@ +error[E0080]: constructing invalid value at [0]: encountered a maybe-null reference + --> $DIR/const_transmute_type_id7.rs:13:1 + | +LL | const A: [&(); 16 / size_of::<*const ()>()] = unsafe { transmute(TypeId::of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = 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. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From a875f7779e3c3b9023bff24b6beca08e7be0e0e8 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 24 Sep 2025 14:13:34 +0200 Subject: [PATCH 0816/1900] alloc: simplify `Default` for `Box` and `Rc` --- library/alloc/src/ffi/c_str.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b0c8c4b1ca4a7..3e78d680ea68a 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -970,17 +970,14 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let rc = Rc::<[u8]>::from(*b"\0"); - // `[u8]` has the same layout as `CStr`, and it is `NUL` terminated. - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } + Rc::from(c"") } } #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { - let boxed: Box<[u8]> = Box::from([0]); - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } + Box::from(c"") } } From 0e7cc32633ea7c7978529d3d4478b17474383a18 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 24 Sep 2025 21:28:50 +0900 Subject: [PATCH 0817/1900] Switch next-solver related rustc dependencies of r-a to crates.io ones --- .../rust-analyzer/crates/hir-def/src/lib.rs | 4 --- .../rust-analyzer/crates/hir-ty/src/lib.rs | 31 +++---------------- .../crates/hir-ty/src/next_solver/consts.rs | 2 -- .../hir-ty/src/next_solver/predicate.rs | 2 -- .../crates/hir-ty/src/next_solver/ty.rs | 2 -- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 --- 6 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 301d4cca0666c..e5c213ca937c8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -15,10 +15,6 @@ extern crate rustc_parse_format; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_parse_format as rustc_parse_format; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_abi; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 451622ef7472b..2e59a488e6734 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -3,45 +3,24 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[cfg(feature = "in-rust-tree")] -extern crate rustc_index; +// FIXME: We used to import `rustc_*` deps from `rustc_private` with `feature = "in-rust-tree" but +// temporarily switched to crates.io versions due to hardships that working on them from rustc +// demands corresponding changes on rust-analyzer at the same time. +// For details, see the zulip discussion below: +// https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/relying.20on.20in-tree.20.60rustc_type_ir.60.2F.60rustc_next_trait_solver.60/with/541055689 -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_index as rustc_index; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_abi; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_pattern_analysis; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_ast_ir; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_ast_ir as rustc_ast_ir; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_type_ir; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_type_ir as rustc_type_ir; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_next_trait_solver; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_data_structures as ena; - mod builder; mod chalk_db; mod chalk_ext; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 7ebefa76ed029..0b3582051bc07 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -36,8 +36,6 @@ impl<'db> Const<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, - #[cfg(feature = "in-rust-tree")] - stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Const::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 86545415009a0..99b1354b6335f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -227,8 +227,6 @@ impl<'db> Predicate<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, - #[cfg(feature = "in-rust-tree")] - stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index c7a747ade3e76..70139e8666948 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -60,8 +60,6 @@ impl<'db> Ty<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, - #[cfg(feature = "in-rust-tree")] - stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f8dacf0fb863d..027a386abe8ce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -20,10 +20,6 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] -#[cfg(feature = "in-rust-tree")] -extern crate rustc_type_ir; - -#[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_type_ir as rustc_type_ir; mod attrs; From 7a0adc08786df857e810c9f6a5a0cb6cae32659b Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 15:02:41 +0200 Subject: [PATCH 0818/1900] add test --- .../coroutine/copy-fast-path-query-cycle.rs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/ui/coroutine/copy-fast-path-query-cycle.rs diff --git a/tests/ui/coroutine/copy-fast-path-query-cycle.rs b/tests/ui/coroutine/copy-fast-path-query-cycle.rs new file mode 100644 index 0000000000000..644cba0d47af2 --- /dev/null +++ b/tests/ui/coroutine/copy-fast-path-query-cycle.rs @@ -0,0 +1,40 @@ +//@ edition: 2024 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for #146813. We previously used a pseudo-canonical +// query during HIR typeck which caused a query cycle when looking at the +// witness of a coroutine. + +use std::future::Future; + +trait ConnectMiddleware {} + +trait ConnectHandler: Sized { + fn with(self, _: M) -> impl ConnectHandler + where + M: ConnectMiddleware, + { + LayeredConnectHandler + } +} + +struct LayeredConnectHandler; +impl ConnectHandler for LayeredConnectHandler {} +impl ConnectHandler for F where F: FnOnce() {} + +impl ConnectMiddleware for F +where + F: FnOnce() -> Fut, + Fut: Future + Send, +{ +} + +pub async fn fails() { + { || {} } + .with(async || ()) + .with(async || ()) + .with(async || ()); +} +fn main() {} From d91025316554f9cff948639ee34bf217ef724947 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Sat, 13 Sep 2025 08:51:28 -0400 Subject: [PATCH 0819/1900] constify Default on Nanoseconds --- library/core/src/num/niche_types.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs index 610d9d8cf92e0..9ac0eb72bdcbc 100644 --- a/library/core/src/num/niche_types.rs +++ b/library/core/src/num/niche_types.rs @@ -112,7 +112,8 @@ impl Nanoseconds { pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; } -impl Default for Nanoseconds { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for Nanoseconds { #[inline] fn default() -> Self { Self::ZERO From 4acea466e42758468e6cd1de7c6173eabcd5f1b3 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 24 Sep 2025 13:26:53 +0000 Subject: [PATCH 0820/1900] simplify setup_constraining_predicates, and note it is potentially cubic --- .../src/constrained_generic_params.rs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 366b3943a0589..44d7f3a5e8bc2 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -167,15 +167,20 @@ pub(crate) fn setup_constraining_predicates<'tcx>( // which is `O(nt)` where `t` is the depth of type-parameter constraints, // remembering that `t` should be less than 7 in practice. // + // FIXME(hkBst): the big-O bound above would be accurate for the number + // of calls to `parameters_for`, which itself is some O(complexity of type). + // That would make this potentially cubic instead of merely quadratic... + // ...unless we cache those `parameters_for` calls. + // // Basically, I iterate over all projections and swap every // "ready" projection to the start of the list, such that // all of the projections before `i` are topologically sorted // and constrain all the parameters in `input_parameters`. // - // In the example, `input_parameters` starts by containing `U` - which - // is constrained by the trait-ref - and so on the first pass we + // In the first example, `input_parameters` starts by containing `U`, + // which is constrained by the self type `U`. Then, on the first pass we // observe that `::Item = T` is a "ready" projection that - // constrains `T` and swap it to front. As it is the sole projection, + // constrains `T` and swap it to the front. As it is the sole projection, // no more swaps can take place afterwards, with the result being // * ::Item = T // * T: Debug @@ -193,33 +198,28 @@ pub(crate) fn setup_constraining_predicates<'tcx>( for j in i..predicates.len() { // Note that we don't have to care about binders here, // as the impl trait ref never contains any late-bound regions. - if let ty::ClauseKind::Projection(projection) = predicates[j].0.kind().skip_binder() { - // Special case: watch out for some kind of sneaky attempt - // to project out an associated type defined by this very - // trait. - let unbound_trait_ref = projection.projection_term.trait_ref(tcx); - if Some(unbound_trait_ref) == impl_trait_ref { - continue; - } - - // A projection depends on its input types and determines its output - // type. For example, if we have - // `<::Baz as Iterator>::Output = ::Output` - // Then the projection only applies if `T` is known, but it still - // does not determine `U`. + if let ty::ClauseKind::Projection(projection) = predicates[j].0.kind().skip_binder() && + + // Special case: watch out for some kind of sneaky attempt to + // project out an associated type defined by this very trait. + !impl_trait_ref.is_some_and(|t| t == projection.projection_term.trait_ref(tcx)) && + + // A projection depends on its input types and determines its output + // type. For example, if we have + // `<::Baz as Iterator>::Output = ::Output` + // then the projection only applies if `T` is known, but it still + // does not determine `U`. + { let inputs = parameters_for(tcx, projection.projection_term, true); let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); - if !relies_only_on_inputs { - continue; - } + relies_only_on_inputs + } { input_parameters.extend(parameters_for(tcx, projection.term, false)); - } else { - continue; + + predicates.swap(i, j); + i += 1; + changed = true; } - // fancy control flow to bypass borrow checker - predicates.swap(i, j); - i += 1; - changed = true; } debug!( "setup_constraining_predicates: predicates={:?} \ From 56734495e2fd485a7be1ba77da1bf18a0eca4636 Mon Sep 17 00:00:00 2001 From: BenjaminBrienen Date: Wed, 24 Sep 2025 14:25:10 +0200 Subject: [PATCH 0821/1900] feature: Implement vec_try_remove Vec::try_remove is a non-panicking version of Vec::remove --- library/alloc/src/vec/mod.rs | 32 ++++++++++++++++++++++++++++++-- library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec.rs | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index ebdb86f98a857..694b7b2df08fe 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2173,9 +2173,37 @@ impl Vec { panic!("removal index (is {index}) should be < len (is {len})"); } + match self.try_remove(index) { + Some(elem) => elem, + None => assert_failed(index, self.len()), + } + } + + /// Remove and return the element at position `index` within the vector, + /// shifting all elements after it to the left, or [`None`] if it does not + /// exist. + /// + /// Note: Because this shifts over the remaining elements, it has a + /// worst-case performance of *O*(*n*). If you'd like to remove + /// elements from the beginning of the `Vec`, consider using + /// [`VecDeque::pop_front`] instead. + /// + /// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_try_remove)] + /// let mut v = vec![1, 2, 3]; + /// assert_eq!(v.try_remove(0), Some(1)); + /// assert_eq!(v.try_remove(2), None); + /// ``` + #[unstable(feature = "vec_try_remove", issue = "146954")] + #[rustc_confusables("delete", "take", "remove")] + pub fn try_remove(&mut self, index: usize) -> Option { let len = self.len(); if index >= len { - assert_failed(index, len); + return None; } unsafe { // infallible @@ -2191,7 +2219,7 @@ impl Vec { ptr::copy(ptr.add(1), ptr, len - index - 1); } self.set_len(len - 1); - ret + Some(ret) } } diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 8c3ce156f3c1d..cba9883e148e2 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -41,6 +41,7 @@ #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![feature(vec_peek_mut)] +#![feature(vec_try_remove)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 33a34daccbfd2..ea334ab0f143a 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -630,6 +630,21 @@ fn test_swap_remove_empty() { vec.swap_remove(0); } +#[test] +fn test_try_remove() { + let mut vec = vec![1, 2, 3]; + // We are attempting to remove vec[0] which contains 1 + assert_eq!(vec.try_remove(0), Some(1)); + // Now `vec` looks like: [2, 3] + // We will now try to remove vec[2] which does not exist + // This should return `None` + assert_eq!(vec.try_remove(2), None); + + // We will try the same thing with an empty vector + let mut v: Vec = vec![]; + assert!(v.try_remove(0).is_none()); +} + #[test] fn test_move_items() { let vec = vec![1, 2, 3]; From 30289353e8e04eff33a0e533b6b398c308b24025 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 22 Sep 2025 15:50:22 +0000 Subject: [PATCH 0822/1900] Improve derive suggestion of const param Make the suggestion not to remove the adt and use the name of the adt variant --- .../src/error_reporting/traits/fulfillment_errors.rs | 11 +++++++---- .../forbid-non-structural_match-types.stderr | 2 +- tests/ui/const-generics/issue-80471.stderr | 4 ++-- tests/ui/const-generics/issues/issue-97278.stderr | 4 ++-- .../ui/consts/refs_check_const_eq-issue-88384.stderr | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 149f5e638b1ad..d485eb7266b28 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1305,8 +1305,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { if ty.is_structural_eq_shallow(self.tcx) { diag.span_suggestion( - span, - "add `#[derive(ConstParamTy)]` to the struct", + span.shrink_to_lo(), + format!("add `#[derive(ConstParamTy)]` to the {}", def.descr()), "#[derive(ConstParamTy)]\n", Applicability::MachineApplicable, ); @@ -1314,8 +1314,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME(adt_const_params): We should check there's not already an // overlapping `Eq`/`PartialEq` impl. diag.span_suggestion( - span, - "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + span.shrink_to_lo(), + format!( + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the {}", + def.descr() + ), "#[derive(ConstParamTy, PartialEq, Eq)]\n", Applicability::MachineApplicable, ); diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.stderr b/tests/ui/const-generics/forbid-non-structural_match-types.stderr index 8ef629329f103..94afded9469e8 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/tests/ui/const-generics/forbid-non-structural_match-types.stderr @@ -6,8 +6,8 @@ LL | struct D; | help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct | -LL - struct C; LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL | struct C; | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issue-80471.stderr b/tests/ui/const-generics/issue-80471.stderr index 8cf3d68e5d691..fff2eb53cf126 100644 --- a/tests/ui/const-generics/issue-80471.stderr +++ b/tests/ui/const-generics/issue-80471.stderr @@ -4,10 +4,10 @@ error[E0741]: `Nat` must implement `ConstParamTy` to be used as the type of a co LL | fn foo() {} | ^^^ | -help: add `#[derive(ConstParamTy)]` to the struct +help: add `#[derive(ConstParamTy)]` to the enum | -LL - enum Nat { LL + #[derive(ConstParamTy)] +LL | enum Nat { | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-97278.stderr b/tests/ui/const-generics/issues/issue-97278.stderr index 4894ddb7b8db8..21a5fc94032c0 100644 --- a/tests/ui/const-generics/issues/issue-97278.stderr +++ b/tests/ui/const-generics/issues/issue-97278.stderr @@ -4,10 +4,10 @@ error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a co LL | fn test() {} | ^^^ | -help: add `#[derive(ConstParamTy)]` to the struct +help: add `#[derive(ConstParamTy)]` to the enum | -LL - enum Bar { LL + #[derive(ConstParamTy)] +LL | enum Bar { | error: aborting due to 1 previous error diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr index 65e4dc22c2887..62c5c52764115 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -15,8 +15,8 @@ LL | struct Foo; | help: add `#[derive(ConstParamTy)]` to the struct | -LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings { | error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter @@ -27,8 +27,8 @@ LL | impl Foo { | help: add `#[derive(ConstParamTy)]` to the struct | -LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings { | error: aborting due to 2 previous errors; 1 warning emitted From 5a4e53603631475335bab3e1416f4aca61172094 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 24 Sep 2025 12:55:38 +0800 Subject: [PATCH 0823/1900] Fix infinite recursion in Path::eq with String --- library/std/src/path.rs | 12 ++++++------ library/std/tests/path.rs | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 70ba502d68421..88d8a4f21cac5 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2107,7 +2107,7 @@ impl PartialEq for PathBuf { impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &str) -> bool { - Path::eq(self, other) + self.as_path() == other } } @@ -2115,7 +2115,7 @@ impl cmp::PartialEq for PathBuf { impl cmp::PartialEq for str { #[inline] fn eq(&self, other: &PathBuf) -> bool { - other == self + self == other.as_path() } } @@ -2123,7 +2123,7 @@ impl cmp::PartialEq for str { impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &String) -> bool { - **self == **other + self.as_path() == other.as_str() } } @@ -2131,7 +2131,7 @@ impl cmp::PartialEq for PathBuf { impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &PathBuf) -> bool { - other == self + self.as_str() == other.as_path() } } @@ -3426,7 +3426,7 @@ impl cmp::PartialEq for str { impl cmp::PartialEq for Path { #[inline] fn eq(&self, other: &String) -> bool { - self == &*other + self == other.as_str() } } @@ -3434,7 +3434,7 @@ impl cmp::PartialEq for Path { impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &Path) -> bool { - other == self + self.as_str() == other } } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index fa76c50597b05..837a14b808ff8 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -2528,7 +2528,17 @@ fn normalize_lexically() { } #[test] -/// See issue#146183 -fn compare_path_to_str() { - assert!(&PathBuf::from("x") == "x"); +/// See issue#146183 and issue#146940 +fn compare_path_like_to_str_like() { + let path_buf = PathBuf::from("x"); + let path = Path::new("x"); + let s = String::from("x"); + assert!(path == "x"); + assert!("x" == path); + assert!(path == &s); + assert!(&s == path); + assert!(&path_buf == "x"); + assert!("x" == &path_buf); + assert!(path_buf == s); + assert!(s == path_buf); } From 6dafcaf6c85e235b259f1d2f1da3268bda74ab65 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 24 Sep 2025 09:33:43 -0400 Subject: [PATCH 0824/1900] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 966f94733bbc9..f2932725b045d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 +Subproject commit f2932725b045d361ff5f18ba02b1409dd1f44e71 From f509dff56d82de64c37080731cbada0ac4dde2b7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 21:55:05 +0200 Subject: [PATCH 0825/1900] f16_f128: enable some more tests in Miri --- library/coretests/tests/floats/mod.rs | 32 +++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index d2b5722309445..d8a831af78d3e 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1180,15 +1180,12 @@ float_test! { } } -// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support -// the intrinsics. - float_test! { name: sqrt_domain, attrs: { const: #[cfg(false)], - f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { assert!(Float::NAN.sqrt().is_nan()); @@ -1246,8 +1243,8 @@ float_test! { float_test! { name: total_cmp, attrs: { - f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { use core::cmp::Ordering; @@ -1355,8 +1352,8 @@ float_test! { name: total_cmp_s_nan, attrs: { const: #[cfg(false)], - f16: #[cfg(false)], - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f16: #[cfg(miri)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { use core::cmp::Ordering; @@ -1432,6 +1429,7 @@ float_test! { name: powi, attrs: { const: #[cfg(false)], + // FIXME(f16_f128): `powi` does not work in Miri for these types f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, @@ -1452,8 +1450,8 @@ float_test! { float_test! { name: to_degrees, attrs: { - f16: #[cfg(target_has_reliable_f16)], - f128: #[cfg(target_has_reliable_f128)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { let pi: Float = Float::PI; @@ -1473,8 +1471,8 @@ float_test! { float_test! { name: to_radians, attrs: { - f16: #[cfg(target_has_reliable_f16)], - f128: #[cfg(target_has_reliable_f128)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { let pi: Float = Float::PI; @@ -1494,8 +1492,8 @@ float_test! { float_test! { name: to_algebraic, attrs: { - f16: #[cfg(target_has_reliable_f16)], - f128: #[cfg(target_has_reliable_f128)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { let a: Float = 123.0; @@ -1518,8 +1516,8 @@ float_test! { float_test! { name: to_bits_conv, attrs: { - f16: #[cfg(target_has_reliable_f16)], - f128: #[cfg(target_has_reliable_f128)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { assert_biteq!(flt(1.0), Float::RAW_1); From 431ef038a3b20e680687a98c4ad2507ae27a7b0a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 24 Sep 2025 15:18:31 +0000 Subject: [PATCH 0826/1900] impl Ord for params and use unstable sort --- compiler/rustc_hir_analysis/src/constrained_generic_params.rs | 2 +- .../rustc_hir_analysis/src/impl_wf_check/min_specialization.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 366b3943a0589..2a633810cd701 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeV use rustc_span::Span; use tracing::debug; -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) struct Parameter(pub u32); impl From for Parameter { 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 b38639ed8c624..13c744ab46139 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 @@ -275,7 +275,7 @@ fn check_duplicate_params<'tcx>( span: Span, ) -> Result<(), ErrorGuaranteed> { let mut base_params = cgp::parameters_for(tcx, parent_args, true); - base_params.sort_by_key(|param| param.0); + base_params.sort_unstable(); if let (_, [duplicate, ..]) = base_params.partition_dedup() { let param = impl1_args[duplicate.0 as usize]; return Err(tcx From 4e62715541925e1a62aebb3b8a0f633a674b808d Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 19 Sep 2025 07:07:49 +0000 Subject: [PATCH 0827/1900] Improve the pretty print of UnstableFeature clause --- compiler/rustc_middle/src/ty/print/pretty.rs | 3 +-- .../const-generics/adt_const_params/unsized_field-1.stderr | 6 +++--- .../unstable_impl_coherence.disabled.stderr | 2 +- .../unstable_impl_coherence.enabled.stderr | 2 +- .../unstable_impl_method_selection.stderr | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1b7ef8de8454a..8f7c8170f7adf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3177,8 +3177,7 @@ define_print! { write!(p, "` can be evaluated")?; } ty::ClauseKind::UnstableFeature(symbol) => { - write!(p, "unstable feature: ")?; - write!(p, "`{symbol}`")?; + write!(p, "feature({symbol}) is enabled")?; } } } diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr index a5ae5c726da86..134dbba0d63ab 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr @@ -7,7 +7,7 @@ LL | LL | struct A([u8]); | ---- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `[u8]` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `[u8]` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:10:10 | LL | struct A([u8]); @@ -22,7 +22,7 @@ LL | LL | struct B(&'static [u8]); | ------------- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `&'static [u8]` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `&'static [u8]` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:14:10 | LL | struct B(&'static [u8]); @@ -37,7 +37,7 @@ LL | LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); | ---------------------------------------------------------- this field does not implement `ConstParamTy_` | -note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `unstable feature: `unsized_const_params`` +note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `feature(unsized_const_params) is enabled` --> $DIR/unsized_field-1.rs:21:10 | LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr index c3147558b03f9..afef024e1b9c1 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr @@ -6,7 +6,7 @@ LL | impl aux::Trait for LocalTy {} | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - impl Trait for T - where unstable feature: `foo`; + where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr index c3147558b03f9..afef024e1b9c1 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr @@ -6,7 +6,7 @@ LL | impl aux::Trait for LocalTy {} | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - impl Trait for T - where unstable feature: `foo`; + where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr index c2bb10f043b2e..840af730154d9 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr @@ -7,7 +7,7 @@ LL | vec![].foo(); = note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_method_selection_aux` crate: - impl Trait for Vec; - impl Trait for Vec - where unstable feature: `bar`; + where feature(bar) is enabled; error: aborting due to 1 previous error From 5317b7be529eb1274dd255cbb2fab94e74475e12 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 09:15:38 +0200 Subject: [PATCH 0828/1900] clean-up --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 356 ++++++++++++------------ tests/ui/or_fun_call.fixed | 1 - tests/ui/or_fun_call.rs | 1 - tests/ui/or_fun_call.stderr | 90 +++--- 5 files changed, 223 insertions(+), 227 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3755ec37fb967..be1fcf136e92d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4460,7 +4460,7 @@ declare_clippy_lint! { /// Checks for calls to `Read::bytes` on types which don't implement `BufRead`. /// /// ### Why is this bad? - /// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`. + /// The default implementation calls `read` for each byte, which can be very inefficient for data that's not in memory, such as `File`. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 71b2f251eded6..1433c5c1307b2 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -18,7 +18,6 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -27,184 +26,6 @@ pub(super) fn check<'tcx>( receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], ) { - /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, - /// `or_insert(T::new())` or `or_insert(T::default())`. - /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, - /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: Symbol, - receiver: &hir::Expr<'_>, - fun: &hir::Expr<'_>, - call_expr: Option<&hir::Expr<'_>>, - span: Span, - method_span: Span, - ) -> bool { - if !expr_type_is_certain(cx, receiver) { - return false; - } - - let is_new = |fun: &hir::Expr<'_>| { - if let hir::ExprKind::Path(ref qpath) = fun.kind { - let path = last_path_segment(qpath).ident.name; - matches!(path, sym::new) - } else { - false - } - }; - - let output_type_implements_default = |fun| { - let fun_ty = cx.typeck_results().expr_ty(fun); - if let ty::FnDef(def_id, args) = fun_ty.kind() { - let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); - cx.tcx - .get_diagnostic_item(sym::Default) - .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) - } else { - false - } - }; - - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { - cx.tcx - .inherent_impls(adt_def.did()) - .iter() - .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .find_map(|assoc| { - if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 - { - Some(assoc.def_id) - } else { - None - } - }) - }) else { - return false; - }; - let in_sugg_method_implementation = { - matches!( - suggested_method_def_id.as_local(), - Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id - ) - }; - if in_sugg_method_implementation { - return false; - } - - // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a - // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. - if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { - return false; - } - - // needs to target Default::default in particular or be *::new and have a Default impl - // available - if (is_new(fun) && output_type_implements_default(fun)) - || match call_expr { - Some(call_expr) => is_default_equivalent(cx, call_expr), - None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), - } - { - span_lint_and_sugg( - cx, - UNWRAP_OR_DEFAULT, - method_span.with_hi(span.hi()), - format!("use of `{name}` to construct default value"), - "try", - format!("{sugg}()"), - Applicability::MachineApplicable, - ); - - true - } else { - false - } - } - - /// Checks for `*or(foo())`. - #[expect(clippy::too_many_arguments)] - fn check_or_fn_call<'tcx>( - cx: &LateContext<'tcx>, - name: Symbol, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - // `Some` if fn has second argument - second_arg: Option<&hir::Expr<'_>>, - span: Span, - // None if lambda is required - fun_span: Option, - ) -> bool { - // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ - (sym::BTreeEntry, false, &[sym::or_insert], "with"), - (sym::HashMapEntry, false, &[sym::or_insert], "with"), - ( - sym::Option, - false, - &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], - "else", - ), - (sym::Option, false, &[sym::get_or_insert], "with"), - (sym::Option, true, &[sym::and], "then"), - (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), - (sym::Result, true, &[sym::and], "then"), - ]; - - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) - && switch_to_lazy_eval(cx, arg) - && !contains_return(arg) - && let self_ty = cx.typeck_results().expr_ty(self_expr) - && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES - .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) - { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - format!("function call inside of `{name}`"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - true - } else { - false - } - } - if let [arg] = args { let inner_arg = peel_blocks(arg); for_each_expr(cx, inner_arg, |ex| { @@ -272,6 +93,183 @@ pub(super) fn check<'tcx>( } } +/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, +/// `or_insert(T::new())` or `or_insert(T::default())`. +/// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, +/// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: Symbol, + receiver: &hir::Expr<'_>, + fun: &hir::Expr<'_>, + call_expr: Option<&hir::Expr<'_>>, + span: Span, + method_span: Span, +) -> bool { + if !expr_type_is_certain(cx, receiver) { + return false; + } + + let is_new = |fun: &hir::Expr<'_>| { + if let hir::ExprKind::Path(ref qpath) = fun.kind { + let path = last_path_segment(qpath).ident.name; + matches!(path, sym::new) + } else { + false + } + }; + + let output_type_implements_default = |fun| { + let fun_ty = cx.typeck_results().expr_ty(fun); + if let ty::FnDef(def_id, args) = fun_ty.kind() { + let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); + cx.tcx + .get_diagnostic_item(sym::Default) + .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) + } else { + false + } + }; + + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { + cx.tcx + .inherent_impls(adt_def.did()) + .iter() + .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) + .find_map(|assoc| { + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { + Some(assoc.def_id) + } else { + None + } + }) + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { + return false; + } + + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), + } + { + span_lint_and_sugg( + cx, + UNWRAP_OR_DEFAULT, + method_span.with_hi(span.hi()), + format!("use of `{name}` to construct default value"), + "try", + format!("{sugg}()"), + Applicability::MachineApplicable, + ); + + true + } else { + false + } +} + +/// Checks for `*or(foo())`. +#[expect(clippy::too_many_arguments)] +fn check_or_fn_call<'tcx>( + cx: &LateContext<'tcx>, + name: Symbol, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, + span: Span, + // None if lambda is required + fun_span: Option, +) -> bool { + // (path, fn_has_argument, methods, suffix) + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), + (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), + ]; + + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES + .iter() + .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() + }; + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + format!("function call inside of `{name}`"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); + true + } else { + false + } +} + fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { let body = cx.tcx.hir_body(body); diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7a0be97017ebc..f42e637328a11 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 724af606de9cf..7d2bd32ca0b3b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 40b25f91154dd..551ca8f3b8016 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:53:22 + --> tests/ui/or_fun_call.rs:52:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:57:14 + --> tests/ui/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:61:21 + --> tests/ui/or_fun_call.rs:60:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:65:14 + --> tests/ui/or_fun_call.rs:64:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:69:19 + --> tests/ui/or_fun_call.rs:68:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:73:24 + --> tests/ui/or_fun_call.rs:72:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:77:23 + --> tests/ui/or_fun_call.rs:76:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:96:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:18 + --> tests/ui/or_fun_call.rs:100:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:105:14 + --> tests/ui/or_fun_call.rs:104:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:109:21 + --> tests/ui/or_fun_call.rs:108:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:19 + --> tests/ui/or_fun_call.rs:112:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:23 + --> tests/ui/or_fun_call.rs:116:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:21 + --> tests/ui/or_fun_call.rs:120:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:125:25 + --> tests/ui/or_fun_call.rs:124:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:129:21 + --> tests/ui/or_fun_call.rs:128:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:134:17 + --> tests/ui/or_fun_call.rs:133:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:139:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:142:21 + --> tests/ui/or_fun_call.rs:141:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:167:35 + --> tests/ui/or_fun_call.rs:166:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:210:18 + --> tests/ui/or_fun_call.rs:209:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:218:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:221:14 + --> tests/ui/or_fun_call.rs:220:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:297:25 + --> tests/ui/or_fun_call.rs:296:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:299:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:302:25 + --> tests/ui/or_fun_call.rs:301:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:18 + --> tests/ui/or_fun_call.rs:332:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:28 + --> tests/ui/or_fun_call.rs:336:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:27 + --> tests/ui/or_fun_call.rs:340:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:345:22 + --> tests/ui/or_fun_call.rs:344:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:23 + --> tests/ui/or_fun_call.rs:348:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:352:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:357:25 + --> tests/ui/or_fun_call.rs:356:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:398:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:403:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:17 + --> tests/ui/or_fun_call.rs:408:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:415:17 + --> tests/ui/or_fun_call.rs:414:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:420:17 + --> tests/ui/or_fun_call.rs:419:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:427:21 + --> tests/ui/or_fun_call.rs:426:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:442:19 + --> tests/ui/or_fun_call.rs:441:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:444:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:447:19 + --> tests/ui/or_fun_call.rs:446:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:458:15 + --> tests/ui/or_fun_call.rs:457:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:468:15 + --> tests/ui/or_fun_call.rs:467:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:478:15 + --> tests/ui/or_fun_call.rs:477:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From b2634e31c435aeff149c5660f9329a0578ad1e72 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:10:15 -0500 Subject: [PATCH 0829/1900] std: add support for armv7a-vex-v5 target Co-authored-by: Lewis McClelland --- .../src/spec/targets/armv7a_vex_v5.rs | 2 +- library/Cargo.lock | 10 + library/std/Cargo.toml | 7 +- library/std/build.rs | 1 + library/std/src/env.rs | 2 + library/std/src/sys/alloc/mod.rs | 3 + library/std/src/sys/alloc/vexos.rs | 96 +++ library/std/src/sys/env_consts.rs | 11 + library/std/src/sys/fs/mod.rs | 4 + library/std/src/sys/fs/vexos.rs | 615 ++++++++++++++++++ library/std/src/sys/pal/mod.rs | 4 + library/std/src/sys/pal/vexos/mod.rs | 80 +++ library/std/src/sys/pal/vexos/time.rs | 28 + library/std/src/sys/random/mod.rs | 2 + library/std/src/sys/stdio/mod.rs | 4 + library/std/src/sys/stdio/vexos.rs | 100 +++ library/std/src/sys/thread/mod.rs | 7 + library/std/src/sys/thread/vexos.rs | 17 + library/std/src/sys/thread_local/mod.rs | 2 + .../src/platform-support/armv7a-vex-v5.md | 26 +- src/tools/tidy/src/deps.rs | 1 + 21 files changed, 1012 insertions(+), 10 deletions(-) create mode 100644 library/std/src/sys/alloc/vexos.rs create mode 100644 library/std/src/sys/fs/vexos.rs create mode 100644 library/std/src/sys/pal/vexos/mod.rs create mode 100644 library/std/src/sys/pal/vexos/time.rs create mode 100644 library/std/src/sys/stdio/vexos.rs create mode 100644 library/std/src/sys/thread/vexos.rs diff --git a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs index e78f783997470..06dd26297758f 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs @@ -34,7 +34,7 @@ pub(crate) fn target() -> Target { description: Some("ARMv7-A Cortex-A9 VEX V5 Brain".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/library/Cargo.lock b/library/Cargo.lock index e4b3839847b1a..47fbf5169f491 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -326,6 +326,7 @@ dependencies = [ "rustc-demangle", "std_detect", "unwind", + "vex-sdk", "wasi 0.11.1+wasi-snapshot-preview1", "wasi 0.14.4+wasi-0.2.4", "windows-targets 0.0.0", @@ -379,6 +380,15 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "vex-sdk" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f74fce61d7a7ba1589da9634c6305a72befb7cc9150c1f872d87d8060f32b9" +dependencies = [ + "rustc-std-workspace-core", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index d28a7f0b46022..b4d349d66def9 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -62,7 +62,7 @@ path = "../windows_targets" rand = { version = "0.9.0", default-features = false, features = ["alloc"] } rand_xorshift = "0.4.0" -[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] @@ -89,6 +89,11 @@ wasip2 = { version = '0.14.4', features = [ r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } +[target.'cfg(target_os = "vexos")'.dependencies] +vex-sdk = { version = "0.27.0", features = [ + 'rustc-dep-of-std', +], default-features = false } + [features] backtrace = [ 'addr2line/rustc-dep-of-std', diff --git a/library/std/build.rs b/library/std/build.rs index ef695601a448a..8a5a785060c85 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -52,6 +52,7 @@ fn main() { || target_os == "rtems" || target_os == "nuttx" || target_os == "cygwin" + || target_os == "vexos" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e457cd61c7596..6d716bd854433 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -1098,6 +1098,7 @@ pub mod consts { /// * `"redox"` /// * `"solaris"` /// * `"solid_asp3` + /// * `"vexos"` /// * `"vita"` /// * `"vxworks"` /// * `"xous"` @@ -1148,6 +1149,7 @@ pub mod consts { /// ///
Full list of possible values /// + /// * `"bin"` /// * `"exe"` /// * `"efi"` /// * `"js"` diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 6d4b09494a3f5..2045b2fecc6ac 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -92,6 +92,9 @@ cfg_select! { target_os = "uefi" => { mod uefi; } + target_os = "vexos" => { + mod vexos; + } target_family = "wasm" => { mod wasm; } diff --git a/library/std/src/sys/alloc/vexos.rs b/library/std/src/sys/alloc/vexos.rs new file mode 100644 index 0000000000000..c1fb6896a89ae --- /dev/null +++ b/library/std/src/sys/alloc/vexos.rs @@ -0,0 +1,96 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sync::atomic::{AtomicBool, Ordering}; + +// Symbols for heap section boundaries defined in the target's linkerscript +unsafe extern "C" { + static mut __heap_start: u8; + static mut __heap_end: u8; +} + +static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new_with_allocator(Vexos); + +struct Vexos; + +unsafe impl dlmalloc::Allocator for Vexos { + /// Allocs system resources + fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) { + static INIT: AtomicBool = AtomicBool::new(false); + + if !INIT.swap(true, Ordering::Relaxed) { + // This target has no growable heap, as user memory has a fixed + // size/location and VEXos does not manage allocation for us. + unsafe { + ( + (&raw mut __heap_start).cast::(), + (&raw const __heap_end).offset_from_unsigned(&raw const __heap_start), + 0, + ) + } + } else { + (ptr::null_mut(), 0, 0) + } + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + ptr::null_mut() + } + + fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool { + false + } + + fn free(&self, _ptr: *mut u8, _size: usize) -> bool { + return false; + } + + fn can_release_part(&self, _flags: u32) -> bool { + false + } + + fn allocates_zeros(&self) -> bool { + false + } + + fn page_size(&self) -> usize { + 0x1000 + } +} + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } + } +} diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs index 711ba0a5f8a92..573f540483b1a 100644 --- a/library/std/src/sys/env_consts.rs +++ b/library/std/src/sys/env_consts.rs @@ -323,6 +323,17 @@ pub mod os { pub const EXE_EXTENSION: &str = "efi"; } +#[cfg(target_os = "vexos")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "vexos"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".bin"; + pub const EXE_EXTENSION: &str = "bin"; +} + #[cfg(target_os = "visionos")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0276bf6e64c8b..64f5a6b36d3db 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -35,6 +35,10 @@ cfg_select! { mod uefi; use uefi as imp; } + target_os = "vexos" => { + mod vexos; + use vexos as imp; + } target_os = "wasi" => { mod wasi; use wasi as imp; diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs new file mode 100644 index 0000000000000..f642e7cb074ec --- /dev/null +++ b/library/std/src/sys/fs/vexos.rs @@ -0,0 +1,615 @@ +use crate::ffi::{OsString, c_char}; +use crate::fmt; +use crate::fs::TryLockError; +use crate::hash::Hash; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::time::SystemTime; +use crate::sys::{unsupported, unsupported_err}; + +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_fs; +pub use unsupported_fs::{ + DirBuilder, FileTimes, canonicalize, link, readlink, remove_dir_all, rename, rmdir, symlink, + unlink, +}; + +/// VEXos file descriptor. +/// +/// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos +/// representing an open file on disk. +/// +/// [FatFs file object structure]: https://github.com/Xilinx/embeddedsw/blob/master/lib/sw_services/xilffs/src/include/ff.h?rgh-link-date=2025-09-23T20%3A03%3A43Z#L215 +/// +/// # Safety +/// +/// Since this platform uses a pointer to to an internal filesystem structure with a lifetime +/// associated with it (rather than a UNIX-style file descriptor table), care must be taken to +/// ensure that the pointer held by `FileDesc` is valid for as long as it exists. +#[derive(Debug)] +struct FileDesc(*mut vex_sdk::FIL); + +// SAFETY: VEXos's FDs can be used on a thread other than the one they were created on. +unsafe impl Send for FileDesc {} +// SAFETY: We assume an environment without threads (i.e. no RTOS). +// (If there were threads, it is possible that a mutex would be required.) +unsafe impl Sync for FileDesc {} + +pub struct File { + fd: FileDesc, +} + +#[derive(Clone)] +pub enum FileAttr { + Dir, + File { size: u64 }, +} + +pub struct ReadDir(!); + +pub struct DirEntry { + path: PathBuf, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions {} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + is_dir: bool, +} + +impl FileAttr { + pub fn size(&self) -> u64 { + match self { + Self::File { size } => *size, + Self::Dir => 0, + } + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions {} + } + + pub fn file_type(&self) -> FileType { + FileType { is_dir: matches!(self, FileAttr::Dir) } + } + + pub fn modified(&self) -> io::Result { + unsupported() + } + + pub fn accessed(&self) -> io::Result { + unsupported() + } + + pub fn created(&self) -> io::Result { + unsupported() + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + false + } + + pub fn set_readonly(&mut self, _readonly: bool) { + panic!("Perimissions do not exist") + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.is_dir + } + + pub fn is_file(&self) -> bool { + !self.is_dir + } + + pub fn is_symlink(&self) -> bool { + // No symlinks in VEXos - entries are either files or directories. + false + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.path.clone() + } + + pub fn file_name(&self) -> OsString { + self.path.file_name().unwrap_or_default().into() + } + + pub fn metadata(&self) -> io::Result { + stat(&self.path) + } + + pub fn file_type(&self) -> io::Result { + Ok(self.metadata()?.file_type()) + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + pub fn write(&mut self, write: bool) { + self.write = write; + } + pub fn append(&mut self, append: bool) { + self.append = append; + } + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + pub fn create(&mut self, create: bool) { + self.create = create; + } + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path, &|path| { + // Enforce the invariants of `create_new`/`create`. + // + // Since VEXos doesn't have anything akin to POSIX's `oflags`, we need to enforce + // the requirements that `create_new` can't have an existing file and `!create` + // doesn't create a file ourselves. + if !opts.read && (opts.write || opts.append) && (opts.create_new || !opts.create) { + let status = unsafe { vex_sdk::vexFileStatus(path.as_ptr()) }; + + if opts.create_new && status != 0 { + return Err(io::const_error!(io::ErrorKind::AlreadyExists, "file exists",)); + } else if !opts.create && status == 0 { + return Err(io::const_error!( + io::ErrorKind::NotFound, + "no such file or directory", + )); + } + } + + let file = match opts { + // read + write - unsupported + OpenOptions { read: true, write: true, .. } => { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "opening files with read and write access is unsupported on this target", + )); + } + + // read + OpenOptions { + read: true, + write: false, + append: _, + truncate: false, + create: false, + create_new: false, + } => unsafe { vex_sdk::vexFileOpen(path.as_ptr(), c"".as_ptr()) }, + + // append + OpenOptions { + read: false, + write: _, + append: true, + truncate: false, + create: _, + create_new: _, + } => unsafe { vex_sdk::vexFileOpenWrite(path.as_ptr()) }, + + // write + OpenOptions { + read: false, + write: true, + append: false, + truncate, + create: _, + create_new: _, + } => unsafe { + if *truncate { + vex_sdk::vexFileOpenCreate(path.as_ptr()) + } else { + // Open in append, but jump to the start of the file. + let fd = vex_sdk::vexFileOpenWrite(path.as_ptr()); + vex_sdk::vexFileSeek(fd, 0, 0); + fd + } + }, + + _ => { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid argument")); + } + }; + + if file.is_null() { + Err(io::const_error!(io::ErrorKind::NotFound, "could not open file")) + } else { + Ok(Self { fd: FileDesc(file) }) + } + }) + } + + pub fn file_attr(&self) -> io::Result { + // `vexFileSize` returns -1 upon error, so u64::try_from will fail on error. + if let Ok(size) = u64::try_from(unsafe { + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + vex_sdk::vexFileSize(self.fd.0) + }) { + Ok(FileAttr::File { size }) + } else { + Err(io::const_error!(io::ErrorKind::InvalidData, "failed to get file size")) + } + } + + pub fn fsync(&self) -> io::Result<()> { + self.flush() + } + + pub fn datasync(&self) -> io::Result<()> { + self.flush() + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let len = buf.len() as u32; + let buf_ptr = buf.as_mut_ptr(); + let read = unsafe { + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + vex_sdk::vexFileRead(buf_ptr.cast::(), 1, len, self.fd.0) + }; + + if read < 0 { + Err(io::const_error!(io::ErrorKind::Other, "could not read from file")) + } else { + Ok(read as usize) + } + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|b| self.read(b), cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = buf.len() as u32; + let buf_ptr = buf.as_ptr(); + let written = unsafe { + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + vex_sdk::vexFileWrite(buf_ptr.cast_mut().cast::(), 1, len, self.fd.0) + }; + + if written < 0 { + Err(io::const_error!(io::ErrorKind::Other, "could not write to file")) + } else { + Ok(written as usize) + } + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn flush(&self) -> io::Result<()> { + unsafe { + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + vex_sdk::vexFileSync(self.fd.0); + } + Ok(()) + } + + pub fn tell(&self) -> io::Result { + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + let position = unsafe { vex_sdk::vexFileTell(self.fd.0) }; + + position.try_into().map_err(|_| { + io::const_error!(io::ErrorKind::InvalidData, "failed to get current location in file") + }) + } + + pub fn size(&self) -> Option> { + None + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + const SEEK_SET: i32 = 0; + const SEEK_CUR: i32 = 1; + const SEEK_END: i32 = 2; + + fn try_convert_offset>(offset: T) -> io::Result { + offset.try_into().map_err(|_| { + io::const_error!( + io::ErrorKind::InvalidInput, + "cannot seek to an offset too large to fit in a 32 bit integer", + ) + }) + } + + // SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime. + match pos { + SeekFrom::Start(offset) => unsafe { + map_fresult(vex_sdk::vexFileSeek(self.fd.0, try_convert_offset(offset)?, SEEK_SET))? + }, + SeekFrom::End(offset) => unsafe { + if offset >= 0 { + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset(offset)?, + SEEK_END, + ))? + } else { + // `vexFileSeek` does not support seeking with negative offset, meaning + // we have to calculate the offset from the end of the file ourselves. + + // Seek to the end of the file to get the end position in the open buffer. + map_fresult(vex_sdk::vexFileSeek(self.fd.0, 0, SEEK_END))?; + let end_position = self.tell()?; + + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + // NOTE: Files internally use a 32-bit representation for stream + // position, so `end_position as i64` should never overflow. + try_convert_offset(end_position as i64 + offset)?, + SEEK_SET, + ))? + } + }, + SeekFrom::Current(offset) => unsafe { + if offset >= 0 { + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset(offset)?, + SEEK_CUR, + ))? + } else { + // `vexFileSeek` does not support seeking with negative offset, meaning + // we have to calculate the offset from the stream position ourselves. + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset((self.tell()? as i64) + offset)?, + SEEK_SET, + ))? + } + }, + } + + Ok(self.tell()?) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + unsupported() + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File").field("fd", &self.fd.0).finish() + } +} +impl Drop for File { + fn drop(&mut self) { + unsafe { vex_sdk::vexFileClose(self.fd.0) }; + } +} + +pub fn readdir(_p: &Path) -> io::Result { + // While there *is* a userspace function for reading file directories, + // the necessary implementation cannot currently be done cleanly, as + // VEXos does not expose directory length to user programs. + // + // This means that we would need to create a large fixed-length buffer + // and hope that the folder's contents didn't exceed that buffer's length, + // which obviously isn't behavior we want to rely on in the standard library. + unsupported() +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + +pub fn exists(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| Ok(unsafe { vex_sdk::vexFileStatus(path.as_ptr()) } != 0)) +} + +pub fn stat(p: &Path) -> io::Result { + // `vexFileStatus` returns 3 if the given path is a directory, 1 if the path is a + // file, or 0 if no such path exists. + const FILE_STATUS_DIR: u32 = 3; + + run_path_with_cstr(p, &|c_path| { + let file_type = unsafe { vex_sdk::vexFileStatus(c_path.as_ptr()) }; + + // We can't get the size if its a directory because we cant open it as a file + if file_type == FILE_STATUS_DIR { + Ok(FileAttr::Dir) + } else { + let mut opts = OpenOptions::new(); + opts.read(true); + let file = File::open(p, &opts)?; + file.file_attr() + } + }) +} + +pub fn lstat(p: &Path) -> io::Result { + // Symlinks aren't supported in this filesystem + stat(p) +} + +// Cannot use `copy` from `common` here, since `File::set_permissions` is unsupported on this target. +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::fs::File; + + // NOTE: If `from` is a directory, this call should fail due to vexFileOpen* returning null. + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + + io::copy(&mut reader, &mut writer) +} + +fn map_fresult(fresult: vex_sdk::FRESULT) -> io::Result<()> { + // VEX uses a derivative of FatFs (Xilinx's xilffs library) for filesystem operations. + match fresult { + vex_sdk::FRESULT::FR_OK => Ok(()), + vex_sdk::FRESULT::FR_DISK_ERR => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "internal function reported an unrecoverable hard error", + )), + vex_sdk::FRESULT::FR_INT_ERR => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "internal error in filesystem runtime", + )), + vex_sdk::FRESULT::FR_NOT_READY => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "the storage device could not be prepared to work", + )), + vex_sdk::FRESULT::FR_NO_FILE => Err(io::const_error!( + io::ErrorKind::NotFound, + "could not find the file in the directory" + )), + vex_sdk::FRESULT::FR_NO_PATH => Err(io::const_error!( + io::ErrorKind::NotFound, + "a directory in the path name could not be found", + )), + vex_sdk::FRESULT::FR_INVALID_NAME => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "the given string is invalid as a path name", + )), + vex_sdk::FRESULT::FR_DENIED => Err(io::const_error!( + io::ErrorKind::PermissionDenied, + "the required access for this operation was denied", + )), + vex_sdk::FRESULT::FR_EXIST => Err(io::const_error!( + io::ErrorKind::AlreadyExists, + "an object with the same name already exists in the directory", + )), + vex_sdk::FRESULT::FR_INVALID_OBJECT => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "invalid or null file/directory object", + )), + vex_sdk::FRESULT::FR_WRITE_PROTECTED => Err(io::const_error!( + io::ErrorKind::PermissionDenied, + "a write operation was performed on write-protected media", + )), + vex_sdk::FRESULT::FR_INVALID_DRIVE => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "an invalid drive number was specified in the path name", + )), + vex_sdk::FRESULT::FR_NOT_ENABLED => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "work area for the logical drive has not been registered", + )), + vex_sdk::FRESULT::FR_NO_FILESYSTEM => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "valid FAT volume could not be found on the drive", + )), + vex_sdk::FRESULT::FR_MKFS_ABORTED => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "failed to create filesystem volume" + )), + vex_sdk::FRESULT::FR_TIMEOUT => Err(io::const_error!( + io::ErrorKind::TimedOut, + "the function was canceled due to a timeout of thread-safe control", + )), + vex_sdk::FRESULT::FR_LOCKED => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "the operation to the object was rejected by file sharing control", + )), + vex_sdk::FRESULT::FR_NOT_ENOUGH_CORE => { + Err(io::const_error!(io::ErrorKind::OutOfMemory, "not enough memory for the operation")) + } + vex_sdk::FRESULT::FR_TOO_MANY_OPEN_FILES => Err(io::const_error!( + io::ErrorKind::Uncategorized, + "maximum number of open files has been reached", + )), + vex_sdk::FRESULT::FR_INVALID_PARAMETER => { + Err(io::const_error!(io::ErrorKind::InvalidInput, "a given parameter was invalid")) + } + _ => unreachable!(), // C-style enum + } +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 513121c6d30ee..dd5e83ee570b6 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -45,6 +45,10 @@ cfg_select! { mod trusty; pub use self::trusty::*; } + target_os = "vexos" => { + mod vexos; + pub use self::vexos::*; + } all(target_os = "wasi", target_env = "p2") => { mod wasip2; pub use self::wasip2::*; diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs new file mode 100644 index 0000000000000..61a34b0f68a30 --- /dev/null +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -0,0 +1,80 @@ +#[path = "../unsupported/os.rs"] +pub mod os; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +pub mod time; + +#[expect(dead_code)] +#[path = "../unsupported/common.rs"] +mod unsupported_common; + +pub use unsupported_common::{ + decode_error_kind, init, is_interrupted, unsupported, unsupported_err, +}; + +use crate::arch::global_asm; +use crate::ptr; +use crate::sys::stdio; +use crate::time::{Duration, Instant}; + +global_asm!( + r#" + .section .boot, "ax" + .global _boot + + _boot: + ldr sp, =__stack_top @ Set up the user stack. + b _start @ Jump to the Rust entrypoint. + "# +); + +#[cfg(not(test))] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _start() -> ! { + unsafe extern "C" { + static mut __bss_start: u8; + static mut __bss_end: u8; + + fn main() -> i32; + } + + // Clear the .bss (uninitialized statics) section by filling it with zeroes. + // This is required, since the compiler assumes it will be zeroed on first access. + ptr::write_bytes( + &raw mut __bss_start, + 0, + (&raw mut __bss_end).offset_from_unsigned(&raw mut __bss_start), + ); + + main(); + + cleanup(); + abort_internal() +} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() { + let exit_time = Instant::now(); + const FLUSH_TIMEOUT: Duration = Duration::from_millis(15); + + // Force the serial buffer to flush + while exit_time.elapsed() < FLUSH_TIMEOUT { + vex_sdk::vexTasksRun(); + + // If the buffer has been fully flushed, exit the loop + if vex_sdk::vexSerialWriteFree(stdio::STDIO_CHANNEL) == (stdio::STDOUT_BUF_SIZE as i32) { + break; + } + } +} + +pub fn abort_internal() -> ! { + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } +} diff --git a/library/std/src/sys/pal/vexos/time.rs b/library/std/src/sys/pal/vexos/time.rs new file mode 100644 index 0000000000000..f95d96cd27ac0 --- /dev/null +++ b/library/std/src/sys/pal/vexos/time.rs @@ -0,0 +1,28 @@ +use crate::time::Duration; + +#[expect(dead_code)] +#[path = "../unsupported/time.rs"] +mod unsupported_time; +pub use unsupported_time::{SystemTime, UNIX_EPOCH}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +impl Instant { + pub fn now() -> Instant { + let micros = unsafe { vex_sdk::vexSystemHighResTimeGet() }; + Self(Duration::from_micros(micros)) + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 1e0eec07b5006..3c5a4c82a9f1e 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -101,6 +101,7 @@ cfg_select! { any( all(target_family = "wasm", target_os = "unknown"), target_os = "xous", + target_os = "vexos", ) => { // FIXME: finally remove std support for wasm32-unknown-unknown // FIXME: add random data generation to xous @@ -116,6 +117,7 @@ cfg_select! { all(target_family = "wasm", target_os = "unknown"), all(target_os = "wasi", target_env = "p2"), target_os = "xous", + target_os = "vexos", )))] pub fn hashmap_random_keys() -> (u64, u64) { let mut buf = [0; 16]; diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 7436e4d9de4da..404ac87792696 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -29,6 +29,10 @@ cfg_select! { mod uefi; pub use uefi::*; } + target_os = "vexos" => { + mod vexos; + pub use vexos::*; + } all(target_os = "wasi", target_env = "p1") => { mod wasip1; pub use wasip1::*; diff --git a/library/std/src/sys/stdio/vexos.rs b/library/std/src/sys/stdio/vexos.rs new file mode 100644 index 0000000000000..1f2251c6421d8 --- /dev/null +++ b/library/std/src/sys/stdio/vexos.rs @@ -0,0 +1,100 @@ +use crate::io; + +pub struct Stdin; +pub struct Stdout; +pub type Stderr = Stdout; + +pub const STDIO_CHANNEL: u32 = 1; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + let mut count = 0; + + for out_byte in buf.iter_mut() { + let byte = unsafe { vex_sdk::vexSerialReadChar(STDIO_CHANNEL) }; + if byte < 0 { + break; + } + + *out_byte = byte as u8; + count += 1; + } + + Ok(count) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut written = 0; + + // HACK: VEXos holds an internal ringbuffer for serial writes that is flushed to USB1 + // roughly every millisecond by `vexTasksRun`. For writes larger than 2048 bytes, we + // must block until that buffer is flushed to USB1 before writing the rest of `buf`. + // + // This is fairly nonstandard for a `write` implementation, but it avoids a guaranteed + // recursive panic when using macros such as `print!` to write large amounts of data + // (buf.len() > 2048) to stdout at once. + for chunk in buf.chunks(STDOUT_BUF_SIZE) { + if unsafe { vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize } < chunk.len() { + self.flush().unwrap(); + } + + let count: usize = unsafe { + vex_sdk::vexSerialWriteBuffer(STDIO_CHANNEL, chunk.as_ptr(), chunk.len() as u32) + } + .try_into() + .map_err(|_| { + io::const_error!(io::ErrorKind::Uncategorized, "internal write error occurred") + })?; + + written += count; + + // This is a sanity check to ensure that we don't end up with non-contiguous + // buffer writes. e.g. a chunk gets only partially written, but we continue + // attempting to write the remaining chunks. + // + // In practice, this should never really occur since the previous flush ensures + // enough space in FIFO to write the entire chunk to vexSerialWriteBuffer. + if count != chunk.len() { + break; + } + } + + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + // This may block for up to a millisecond. + unsafe { + while (vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize) != STDOUT_BUF_SIZE { + vex_sdk::vexTasksRun(); + } + } + + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 4096; +pub const STDOUT_BUF_SIZE: usize = 2048; + +pub fn is_ebadf(_err: &io::Error) -> bool { + false +} + +pub fn panic_output() -> Option { + Some(Stdout::new()) +} diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index 6bb7fc1a20e2f..3bd83dd760ac9 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -81,6 +81,13 @@ cfg_select! { ))] pub use unsupported::set_name; } + target_os = "vexos" => { + mod vexos; + pub use vexos::{sleep, yield_now}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE}; + } all(target_os = "wasi", target_env = "p1") => { mod wasip1; pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now}; diff --git a/library/std/src/sys/thread/vexos.rs b/library/std/src/sys/thread/vexos.rs new file mode 100644 index 0000000000000..d917dde4d0bc1 --- /dev/null +++ b/library/std/src/sys/thread/vexos.rs @@ -0,0 +1,17 @@ +use crate::time::{Duration, Instant}; + +pub fn yield_now() { + unsafe { + vex_sdk::vexTasksRun(); + } +} + +pub fn sleep(dur: Duration) { + let start = Instant::now(); + + while start.elapsed() < dur { + unsafe { + vex_sdk::vexTasksRun(); + } + } +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index cff74857c4733..d5c795093cf04 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -29,6 +29,7 @@ cfg_select! { target_os = "uefi", target_os = "zkvm", target_os = "trusty", + target_os = "vexos", ) => { mod no_threads; pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner}; @@ -98,6 +99,7 @@ pub(crate) mod guard { target_os = "uefi", target_os = "zkvm", target_os = "trusty", + target_os = "vexos", ) => { pub(crate) fn enable() { // FIXME: Right now there is no concept of "thread exit" on diff --git a/src/doc/rustc/src/platform-support/armv7a-vex-v5.md b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md index a7da1b16f7e3d..3677f8931dd68 100644 --- a/src/doc/rustc/src/platform-support/armv7a-vex-v5.md +++ b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md @@ -4,7 +4,7 @@ Allows compiling user programs for the [VEX V5 Brain](https://www.vexrobotics.com/276-4810.html), a microcontroller for educational and competitive robotics. -Rust support for this target is not affiliated with VEX Robotics or IFI. +Rust support for this target is not affiliated with VEX Robotics or IFI, and does not link to any official VEX SDK. ## Target maintainers @@ -17,11 +17,24 @@ This target is maintained by members of the [vexide](https://github.com/vexide) ## Requirements -This target is cross-compiled and currently requires `#![no_std]`. Dynamic linking is unsupported. +This target is cross-compiled. Dynamic linking is unsupported. -When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (soft float ABI). +`#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target. -This target generates binaries in the ELF format that may uploaded to the brain with external tools. +`std` has only partial support due platform limitations. Notably: +- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment. +- `std::time` has full support for `Instant`, but no support for `SystemTime`. +- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated. +- `std::fs` has limited support for reading or writing to files. Directory operations, file deletion, and some file opening features are unsupported and will return errors. +- A global allocator implemented on top of `dlmalloc` is provided. +- Modules that do not need to interact with the OS beyond allocation such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc are fully supported. +- Random number generation and hashing is insecure, as there is no reliable source of entropy on this platform. + +In order to support some APIs, users are expected to provide a supporting runtime SDK for `libstd` to link against. This library may be provided either by [`vex-sdk-build`](https://github.com/vexide/vex-sdk/tree/main/packages/vex-sdk-build) (which will download an official SDK from VEX) or through an open-source implementation such as [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable). + +When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (softfp ABI). + +This target generates binaries in the ELF format that may be uploaded to the brain with external tools. ## Building the target @@ -29,10 +42,7 @@ You can build Rust with support for this target by adding it to the `target` lis ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using `build-std` or similar. When the compiler builds a binary, an ELF build artifact will be produced. Additional tools are required for this artifact to be recognizable to VEXos as a user program. diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 568ec0c119896..7c5f823b57c76 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -575,6 +575,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "rustc-literal-escaper", "shlex", "unwinding", + "vex-sdk", "wasi", "windows-sys", "windows-targets", From 7449d63ab7dc12de7e311f1bf56de51051bb68dc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 09:40:45 +0200 Subject: [PATCH 0830/1900] fix(or_fun_call): respect MSRV for `Result::unwrap_or_default` suggestion --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 16 ++++++++++++--- clippy_utils/src/msrvs.rs | 2 +- tests/ui/or_fun_call.fixed | 15 ++++++++++++++ tests/ui/or_fun_call.rs | 15 ++++++++++++++ tests/ui/or_fun_call.stderr | 26 ++++++++++++++++++++++++- 8 files changed, 72 insertions(+), 6 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1d..f8263bb8852ab 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -883,6 +883,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) +* [`or_fun_call`](https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad82..e14e15a022f71 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -779,6 +779,7 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, + or_fun_call, ptr_as_ptr, question_mark, redundant_field_names, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index be1fcf136e92d..c18f20c17e2e2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4854,7 +4854,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); expect_fun_call::check( cx, &self.format_args, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1433c5c1307b2..04e4503e4097f 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -25,6 +26,7 @@ pub(super) fn check<'tcx>( name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], + msrv: Msrv, ) { if let [arg] = args { let inner_arg = peel_blocks(arg); @@ -45,11 +47,11 @@ pub(super) fn check<'tcx>( }; (!inner_fun_has_args && !is_nested_expr - && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span, msrv)) || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) }, hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { - check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span, msrv) }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) @@ -97,6 +99,7 @@ pub(super) fn check<'tcx>( /// `or_insert(T::new())` or `or_insert(T::default())`. /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +#[expect(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: Symbol, @@ -105,7 +108,15 @@ fn check_unwrap_or_default( call_expr: Option<&hir::Expr<'_>>, span: Span, method_span: Span, + msrv: Msrv, ) -> bool { + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + + // Check MSRV, but only for `Result::unwrap_or_default` + if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + return false; + } + if !expr_type_is_certain(cx, receiver) { return false; } @@ -137,7 +148,6 @@ fn check_unwrap_or_default( _ => return false, }; - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4d..3cccce3975449 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -77,7 +77,7 @@ msrv_aliases! { 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } - 1,16,0 { STR_REPEAT } + 1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT } 1,15,0 { MAYBE_BOUND_IN_WHERE } 1,13,0 { QUESTION_MARK_OPERATOR } } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index f42e637328a11..386351aa39f51 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -478,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_else(|_| Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_default(); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7d2bd32ca0b3b..e27f9aa65c37a 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -478,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 551ca8f3b8016..6bce06ab20eb6 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -288,5 +288,29 @@ error: function call inside of `and` LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` -error: aborting due to 45 previous errors +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:483:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:485:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:491:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:493:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: aborting due to 49 previous errors From 9cb82d7ac849adf1bffb2d7e30f114db8d9a9208 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 23:22:31 +0200 Subject: [PATCH 0831/1900] fix(unnecessary_mut_passed): retain parens around the arguments --- clippy_lints/src/unnecessary_mut_passed.rs | 33 +++- tests/ui/unnecessary_mut_passed.fixed | 20 +++ tests/ui/unnecessary_mut_passed.rs | 20 +++ tests/ui/unnecessary_mut_passed.stderr | 193 ++++++++++++++++++--- 4 files changed, 233 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/unnecessary_mut_passed.rs b/clippy_lints/src/unnecessary_mut_passed.rs index ec93ef97cfaf7..eb2d7639e91f7 100644 --- a/clippy_lints/src/unnecessary_mut_passed.rs +++ b/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -87,16 +87,33 @@ fn check_arguments<'tcx>( if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - let mut applicability = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); - span_lint_and_sugg( + let applicability = Applicability::MachineApplicable; + + let span_to_remove = { + let span_until_arg = argument.span.until(arg.span); + if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| { + src + // we don't use `strip_prefix` here, because `argument` might be enclosed in parens, in + // which case `&` is no longer the prefix + .find('&') + // just a sanity check, in case some proc-macro messes up the spans + .filter(|ref_pos| src[*ref_pos..].contains("mut")) + }) && let Ok(lo) = u32::try_from(ref_pos + '&'.len_utf8()) + { + span_until_arg.split_at(lo).1 + } else { + return; + } + }; + + span_lint_and_then( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), - "remove this `mut`", - sugg.to_string(), - applicability, + |diag| { + diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability); + }, ); } } diff --git a/tests/ui/unnecessary_mut_passed.fixed b/tests/ui/unnecessary_mut_passed.fixed index 03d854099e644..63bbadb01dcb2 100644 --- a/tests/ui/unnecessary_mut_passed.fixed +++ b/tests/ui/unnecessary_mut_passed.fixed @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&my_struct).takes_ref(&42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&my_struct).takes_ref((&42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.rs b/tests/ui/unnecessary_mut_passed.rs index 80e3f50692770..b719ca1871b29 100644 --- a/tests/ui/unnecessary_mut_passed.rs +++ b/tests/ui/unnecessary_mut_passed.rs @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&mut my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&mut my_struct).takes_ref(&mut 42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&mut my_struct).takes_ref((&mut 42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&mut 42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.stderr b/tests/ui/unnecessary_mut_passed.stderr index c69a637bf4088..ace11027e3e25 100644 --- a/tests/ui/unnecessary_mut_passed.stderr +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -1,77 +1,220 @@ error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:56:15 + --> tests/ui/unnecessary_mut_passed.rs:57:15 | LL | takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +help: remove this `mut` + | +LL - takes_ref(&mut 42); +LL + takes_ref(&42); + | error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:58:19 + --> tests/ui/unnecessary_mut_passed.rs:59:19 | LL | takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_ref(&mut &42); +LL + takes_ref_ref(&&42); + | error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:60:22 + --> tests/ui/unnecessary_mut_passed.rs:61:22 | LL | takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_refmut(&mut &mut 42); +LL + takes_ref_refmut(&&mut 42); + | error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:62:21 + --> tests/ui/unnecessary_mut_passed.rs:63:21 | LL | takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - takes_raw_const(&mut 42); +LL + takes_raw_const(&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:66:12 + --> tests/ui/unnecessary_mut_passed.rs:67:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:69:12 + --> tests/ui/unnecessary_mut_passed.rs:70:12 | LL | as_ptr(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &42); +LL + as_ptr(&&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:72:12 + --> tests/ui/unnecessary_mut_passed.rs:73:12 | LL | as_ptr(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &mut 42); +LL + as_ptr(&&mut 42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:75:12 + --> tests/ui/unnecessary_mut_passed.rs:76:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:80:25 + --> tests/ui/unnecessary_mut_passed.rs:81:25 | LL | my_struct.takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref(&mut 42); +LL + my_struct.takes_ref(&42); + | error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:82:29 + --> tests/ui/unnecessary_mut_passed.rs:83:29 | LL | my_struct.takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_ref(&mut &42); +LL + my_struct.takes_ref_ref(&&42); + | error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:84:32 + --> tests/ui/unnecessary_mut_passed.rs:85:32 | LL | my_struct.takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_refmut(&mut &mut 42); +LL + my_struct.takes_ref_refmut(&&mut 42); + | error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:86:31 + --> tests/ui/unnecessary_mut_passed.rs:87:31 | LL | my_struct.takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_raw_const(&mut 42); +LL + my_struct.takes_raw_const(&42); + | + +error: the method `takes_nothing` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:175:5 + | +LL | (&mut my_struct).takes_nothing(); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_nothing(); +LL + (&my_struct).takes_nothing(); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:5 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&my_struct).takes_ref(&mut 42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:32 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&mut my_struct).takes_ref(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:5 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&my_struct).takes_ref((&mut 42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:32 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&mut my_struct).takes_ref((&42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:188:25 + | +LL | my_struct.takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref((&mut 42)); +LL + my_struct.takes_ref((&42)); + | -error: aborting due to 12 previous errors +error: aborting due to 18 previous errors From a3840d9592e764f48617bf178c4c00bdb0b8ab84 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 22 Sep 2025 08:59:36 +0300 Subject: [PATCH 0832/1900] Implement fallback properly fallback.rs was ported straight from rustc (minus the lint parts). This fixes the `!` regressions. --- src/tools/rust-analyzer/Cargo.lock | 18 + src/tools/rust-analyzer/Cargo.toml | 1 + .../rust-analyzer/crates/hir-ty/Cargo.toml | 1 + .../crates/hir-ty/src/consteval.rs | 2 +- .../crates/hir-ty/src/consteval/tests.rs | 48 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 95 +++- .../crates/hir-ty/src/infer/coerce.rs | 28 +- .../crates/hir-ty/src/infer/fallback.rs | 439 ++++++++++++++++++ .../crates/hir-ty/src/infer/unify.rs | 332 ++++++------- .../crates/hir-ty/src/next_solver.rs | 2 +- .../crates/hir-ty/src/next_solver/region.rs | 10 +- .../crates/hir-ty/src/next_solver/ty.rs | 9 + .../crates/hir-ty/src/tests/never_type.rs | 10 +- .../crates/hir-ty/src/tests/regression.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + 16 files changed, 770 insertions(+), 230 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 17dea1ba4cdec..9d0f63d2bcecb 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -545,6 +545,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.2" @@ -775,6 +781,7 @@ dependencies = [ "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "oorandom", + "petgraph", "project-model", "query-group-macro", "ra-ap-rustc_abi", @@ -1594,6 +1601,17 @@ dependencies = [ "libc", ] +[[package]] +name = "petgraph" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.4", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.16" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0401367f78647..d3a4e375613eb 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -170,6 +170,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features = triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" xshell = "0.2.7" +petgraph = { version = "0.8.2", default-features = false } # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 138d02e5a6105..4013d19ad08eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -34,6 +34,7 @@ rustc_apfloat = "0.2.3" query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true +petgraph.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index e2a8d1cedc284..b2daed425ef1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -327,7 +327,7 @@ pub(crate) fn eval_to_const( debruijn: DebruijnIndex, ) -> Const { let db = ctx.db; - let infer = ctx.clone().resolve_all(); + let infer = ctx.fixme_resolve_all_clone(); fn has_closure(body: &Body, expr: ExprId) -> bool { if matches!(body[expr], Expr::Closure { .. }) { return true; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 299b73a7d6cc4..1586846bbe58d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -36,12 +36,12 @@ fn check_fail( error: impl FnOnce(ConstEvalError) -> bool, ) { let (db, file_id) = TestDB::with_single_file(ra_fixture); - match eval_goal(&db, file_id) { + salsa::attach(&db, || match eval_goal(&db, file_id) { Ok(_) => panic!("Expected fail, but it succeeded"), Err(e) => { - assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db)) + assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db)) } - } + }) } #[track_caller] @@ -79,36 +79,38 @@ fn check_answer( check: impl FnOnce(&[u8], &MemoryMap<'_>), ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let r = match eval_goal(&db, file_id) { - Ok(t) => t, - Err(e) => { - let err = pretty_print_err(e, db); - panic!("Error in evaluating goal: {err}"); - } - }; - match &r.data(Interner).value { - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(b, mm) => { - check(b, mm); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let r = match eval_goal(&db, file_id) { + Ok(t) => t, + Err(e) => { + let err = pretty_print_err(e, &db); + panic!("Error in evaluating goal: {err}"); } - x => panic!("Expected number but found {x:?}"), - }, - _ => panic!("result of const eval wasn't a concrete const"), - } + }; + match &r.data(Interner).value { + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(b, mm) => { + check(b, mm); + } + x => panic!("Expected number but found {x:?}"), + }, + _ => panic!("result of const eval wasn't a concrete const"), + } + }); } -fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { +fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String { let mut err = String::new(); let span_formatter = |file, range| format!("{file:?} {range:?}"); let display_target = - DisplayTarget::from_crate(&db, *db.all_crates().last().expect("no crate graph present")); + DisplayTarget::from_crate(db, *db.all_crates().last().expect("no crate graph present")); match e { ConstEvalError::MirLowerError(e) => { - e.pretty_print(&mut err, &db, span_formatter, display_target) + e.pretty_print(&mut err, db, span_formatter, display_target) } ConstEvalError::MirEvalError(e) => { - e.pretty_print(&mut err, &db, span_formatter, display_target) + e.pretty_print(&mut err, db, span_formatter, display_target) } } .unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 6e07d3afe5524..155f1336e41d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -222,7 +222,7 @@ pub(crate) fn const_eval_discriminant_variant( // and make this function private. See the fixme comment on `InferenceContext::resolve_all`. pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> { let interner = DbInterner::new_with(ctx.db, None, None); - let infer = ctx.clone().resolve_all(); + let infer = ctx.fixme_resolve_all_clone(); fn has_closure(body: &Body, expr: ExprId) -> bool { if matches!(body[expr], Expr::Closure { .. }) { return true; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index fd10f923987f0..287afb039b457 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -19,6 +19,7 @@ pub(crate) mod closure; mod coerce; pub(crate) mod diagnostics; mod expr; +mod fallback; mod mutability; mod pat; mod path; @@ -53,16 +54,16 @@ use indexmap::IndexSet; use intern::sym; use la_arena::{ArenaMap, Entry}; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::Ty as _; use stdx::{always, never}; use triomphe::Arc; -use crate::db::InternedClosureId; use crate::{ AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TargetFeatures, TraitEnvironment, Ty, TyBuilder, TyExt, - db::HirDatabase, + db::{HirDatabase, InternedClosureId}, fold_tys, generics::Generics, infer::{ @@ -75,6 +76,7 @@ use crate::{ mir::MirSpan, next_solver::{ self, DbInterner, + infer::{DefineOpaqueTypes, traits::ObligationCause}, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, static_lifetime, to_assoc_type_id, @@ -138,6 +140,20 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc for InferenceResult { } } +#[derive(Debug, Clone)] +struct InternedStandardTypesNextSolver<'db> { + unit: crate::next_solver::Ty<'db>, + never: crate::next_solver::Ty<'db>, + i32: crate::next_solver::Ty<'db>, + f64: crate::next_solver::Ty<'db>, +} + +impl<'db> InternedStandardTypesNextSolver<'db> { + fn new(interner: DbInterner<'db>) -> Self { + Self { + unit: crate::next_solver::Ty::new_unit(interner), + never: crate::next_solver::Ty::new(interner, crate::next_solver::TyKind::Never), + i32: crate::next_solver::Ty::new_int(interner, rustc_type_ir::IntTy::I32), + f64: crate::next_solver::Ty::new_float(interner, rustc_type_ir::FloatTy::F64), + } + } +} + /// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] pub(crate) struct InferenceContext<'db> { @@ -718,6 +752,7 @@ pub(crate) struct InferenceContext<'db> { resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, breakables: Vec>, + types: InternedStandardTypesNextSolver<'db>, /// Whether we are inside the pattern of a destructuring assignment. inside_assignment: bool, @@ -798,11 +833,13 @@ impl<'db> InferenceContext<'db> { resolver: Resolver<'db>, ) -> Self { let trait_env = db.trait_environment_for_body(owner); + let table = unify::InferenceTable::new(db, trait_env); InferenceContext { + types: InternedStandardTypesNextSolver::new(table.interner), target_features: OnceCell::new(), generics: OnceCell::new(), result: InferenceResult::default(), - table: unify::InferenceTable::new(db, trait_env), + table, tuple_field_accesses_rev: Default::default(), return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, @@ -865,24 +902,33 @@ impl<'db> InferenceContext<'db> { self.result.has_errors = true; } - // FIXME: This function should be private in module. It is currently only used in the consteval, since we need - // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you - // used this function for another workaround, mention it here. If you really need this function and believe that - // there is no problem in it being `pub(crate)`, remove this comment. - pub(crate) fn resolve_all(mut self) -> InferenceResult { - self.table.select_obligations_where_possible(); - self.table.fallback_if_possible(); + /// Clones `self` and calls `resolve_all()` on it. + // FIXME: Remove this. + pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult { + let mut ctx = self.clone(); + + ctx.type_inference_fallback(); // Comment from rustc: // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - let cast_checks = std::mem::take(&mut self.deferred_cast_checks); + let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks); for mut cast in cast_checks.into_iter() { - if let Err(diag) = cast.check(&mut self) { - self.diagnostics.push(diag); + if let Err(diag) = cast.check(&mut ctx) { + ctx.diagnostics.push(diag); } } + ctx.table.select_obligations_where_possible(); + + ctx.resolve_all() + } + + // FIXME: This function should be private in module. It is currently only used in the consteval, since we need + // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you + // used this function for another workaround, mention it here. If you really need this function and believe that + // there is no problem in it being `pub(crate)`, remove this comment. + pub(crate) fn resolve_all(self) -> InferenceResult { let InferenceContext { mut table, mut result, tuple_field_accesses_rev, diagnostics, .. } = self; @@ -914,11 +960,6 @@ impl<'db> InferenceContext<'db> { diagnostics: _, } = &mut result; - // FIXME resolve obligations as well (use Guidance if necessary) - table.select_obligations_where_possible(); - - // make sure diverging type variables are marked as such - table.propagate_diverging_flag(); for ty in type_of_expr.values_mut() { *ty = table.resolve_completely(ty.clone()); *has_errors = *has_errors || ty.contains_unknown(); @@ -1673,6 +1714,22 @@ impl<'db> InferenceContext<'db> { self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) } + fn demand_eqtype( + &mut self, + expected: crate::next_solver::Ty<'db>, + actual: crate::next_solver::Ty<'db>, + ) { + let result = self + .table + .infer_ctxt + .at(&ObligationCause::new(), self.table.trait_env.env) + .eq(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); + if let Err(_err) = result { + // FIXME: Emit diagnostic. + } + } + fn resolve_associated_type_with_params( &mut self, inner_ty: Ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 219b519e46bb8..62ce00a2e33d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -210,9 +210,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { // Coercing from `!` to any type is allowed: if a.is_never() { // If we're coercing into an inference var, mark it as possibly diverging. - // FIXME: rustc does this differently. - if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() { - self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General); + if b.is_infer() { + self.table.set_diverging(b); } if self.coerce_never { @@ -1613,16 +1612,21 @@ fn coerce<'db>( chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), } == Some(iv)) }; - let fallback = |iv, kind, default, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) - .map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), - chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { - crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner) - }), - chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { - crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner) - }), + let fallback = |iv, kind, binder| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else( + || chalk_ir::TyKind::Error.intern(Interner).cast(Interner), + |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner), + ), + chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else( + || crate::LifetimeData::Error.intern(Interner).cast(Interner), + |i| crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner), + ), + chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else( + || crate::unknown_const(ty.clone()).cast(Interner), + |i| crate::BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner), + ), }; // FIXME also map the types in the adjustments + // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`. Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs new file mode 100644 index 0000000000000..2022447ad4339 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs @@ -0,0 +1,439 @@ +//! Fallback of infer vars to `!` and `i32`/`f64`. + +use intern::sym; +use petgraph::{ + Graph, + visit::{Dfs, Walker}, +}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; +use rustc_type_ir::{ + TyVid, + inherent::{IntoKind, Ty as _}, +}; +use tracing::debug; + +use crate::{ + infer::InferenceContext, + next_solver::{CoercePredicate, PredicateKind, SubtypePredicate, Ty, TyKind}, +}; + +#[derive(Copy, Clone)] +pub(crate) enum DivergingFallbackBehavior { + /// Always fallback to `()` (aka "always spontaneous decay") + ToUnit, + /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. + ContextDependent, + /// Always fallback to `!` (which should be equivalent to never falling back + not making + /// never-to-any coercions unless necessary) + ToNever, +} + +impl<'db> InferenceContext<'db> { + pub(super) fn type_inference_fallback(&mut self) { + debug!( + "type-inference-fallback start obligations: {:#?}", + self.table.fulfillment_cx.pending_obligations() + ); + + // All type checking constraints were added, try to fallback unsolved variables. + self.table.select_obligations_where_possible(); + + debug!( + "type-inference-fallback post selection obligations: {:#?}", + self.table.fulfillment_cx.pending_obligations() + ); + + let fallback_occurred = self.fallback_types(); + + if !fallback_occurred { + return; + } + + // We now see if we can make progress. This might cause us to + // unify inference variables for opaque types, since we may + // have unified some other type variables during the first + // phase of fallback. This means that we only replace + // inference variables with their underlying opaque types as a + // last resort. + // + // In code like this: + // + // ```rust + // type MyType = impl Copy; + // fn produce() -> MyType { true } + // fn bad_produce() -> MyType { panic!() } + // ``` + // + // we want to unify the opaque inference variable in `bad_produce` + // with the diverging fallback for `panic!` (e.g. `()` or `!`). + // This will produce a nice error message about conflicting concrete + // types for `MyType`. + // + // If we had tried to fallback the opaque inference variable to `MyType`, + // we will generate a confusing type-check error that does not explicitly + // refer to opaque types. + self.table.select_obligations_where_possible(); + } + + fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { + if self.krate().data(self.db).edition.at_least_2024() { + return DivergingFallbackBehavior::ToNever; + } + + if self.resolver.def_map().is_unstable_feature_enabled(&sym::never_type_fallback) { + return DivergingFallbackBehavior::ContextDependent; + } + + DivergingFallbackBehavior::ToUnit + } + + fn fallback_types(&mut self) -> bool { + // Check if we have any unresolved variables. If not, no need for fallback. + let unresolved_variables = self.table.infer_ctxt.unresolved_variables(); + + if unresolved_variables.is_empty() { + return false; + } + + let diverging_fallback_behavior = self.diverging_fallback_behavior(); + + let diverging_fallback = + self.calculate_diverging_fallback(&unresolved_variables, diverging_fallback_behavior); + + // We do fallback in two passes, to try to generate + // better error messages. + // The first time, we do *not* replace opaque types. + let mut fallback_occurred = false; + for ty in unresolved_variables { + debug!("unsolved_variable = {:?}", ty); + fallback_occurred |= self.fallback_if_possible(ty, &diverging_fallback); + } + + fallback_occurred + } + + // Tries to apply a fallback to `ty` if it is an unsolved variable. + // + // - Unconstrained ints are replaced with `i32`. + // + // - Unconstrained floats are replaced with `f64`. + // + // - Non-numerics may get replaced with `()` or `!`, depending on + // how they were categorized by `calculate_diverging_fallback` + // (and the setting of `#![feature(never_type_fallback)]`). + // + // Fallback becomes very dubious if we have encountered + // type-checking errors. In that case, fallback to Error. + // + // Sets `FnCtxt::fallback_has_occurred` if fallback is performed + // during this call. + fn fallback_if_possible( + &mut self, + ty: Ty<'db>, + diverging_fallback: &FxHashMap, Ty<'db>>, + ) -> bool { + // Careful: we do NOT shallow-resolve `ty`. We know that `ty` + // is an unsolved variable, and we determine its fallback + // based solely on how it was created, not what other type + // variables it may have been unified with since then. + // + // The reason this matters is that other attempts at fallback + // may (in principle) conflict with this fallback, and we wish + // to generate a type error in that case. (However, this + // actually isn't true right now, because we're only using the + // builtin fallback rules. This would be true if we were using + // user-supplied fallbacks. But it's still useful to write the + // code to detect bugs.) + // + // (Note though that if we have a general type variable `?T` + // that is then unified with an integer type variable `?I` + // that ultimately never gets resolved to a special integral + // type, `?T` is not considered unsolved, but `?I` is. The + // same is true for float variables.) + let fallback = match ty.kind() { + TyKind::Infer(rustc_type_ir::IntVar(_)) => self.types.i32, + TyKind::Infer(rustc_type_ir::FloatVar(_)) => self.types.f64, + _ => match diverging_fallback.get(&ty) { + Some(&fallback_ty) => fallback_ty, + None => return false, + }, + }; + debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); + + self.demand_eqtype(ty, fallback); + true + } + + /// The "diverging fallback" system is rather complicated. This is + /// a result of our need to balance 'do the right thing' with + /// backwards compatibility. + /// + /// "Diverging" type variables are variables created when we + /// coerce a `!` type into an unbound type variable `?X`. If they + /// never wind up being constrained, the "right and natural" thing + /// is that `?X` should "fallback" to `!`. This means that e.g. an + /// expression like `Some(return)` will ultimately wind up with a + /// type like `Option` (presuming it is not assigned or + /// constrained to have some other type). + /// + /// However, the fallback used to be `()` (before the `!` type was + /// added). Moreover, there are cases where the `!` type 'leaks + /// out' from dead code into type variables that affect live + /// code. The most common case is something like this: + /// + /// ```rust + /// # fn foo() -> i32 { 4 } + /// match foo() { + /// 22 => Default::default(), // call this type `?D` + /// _ => return, // return has type `!` + /// } // call the type of this match `?M` + /// ``` + /// + /// Here, coercing the type `!` into `?M` will create a diverging + /// type variable `?X` where `?X <: ?M`. We also have that `?D <: + /// ?M`. If `?M` winds up unconstrained, then `?X` will + /// fallback. If it falls back to `!`, then all the type variables + /// will wind up equal to `!` -- this includes the type `?D` + /// (since `!` doesn't implement `Default`, we wind up a "trait + /// not implemented" error in code like this). But since the + /// original fallback was `()`, this code used to compile with `?D + /// = ()`. This is somewhat surprising, since `Default::default()` + /// on its own would give an error because the types are + /// insufficiently constrained. + /// + /// Our solution to this dilemma is to modify diverging variables + /// so that they can *either* fallback to `!` (the default) or to + /// `()` (the backwards compatibility case). We decide which + /// fallback to use based on whether there is a coercion pattern + /// like this: + /// + /// ```ignore (not-rust) + /// ?Diverging -> ?V + /// ?NonDiverging -> ?V + /// ?V != ?NonDiverging + /// ``` + /// + /// Here `?Diverging` represents some diverging type variable and + /// `?NonDiverging` represents some non-diverging type + /// variable. `?V` can be any type variable (diverging or not), so + /// long as it is not equal to `?NonDiverging`. + /// + /// Intuitively, what we are looking for is a case where a + /// "non-diverging" type variable (like `?M` in our example above) + /// is coerced *into* some variable `?V` that would otherwise + /// fallback to `!`. In that case, we make `?V` fallback to `!`, + /// along with anything that would flow into `?V`. + /// + /// The algorithm we use: + /// * Identify all variables that are coerced *into* by a + /// diverging variable. Do this by iterating over each + /// diverging, unsolved variable and finding all variables + /// reachable from there. Call that set `D`. + /// * Walk over all unsolved, non-diverging variables, and find + /// any variable that has an edge into `D`. + fn calculate_diverging_fallback( + &self, + unresolved_variables: &[Ty<'db>], + behavior: DivergingFallbackBehavior, + ) -> FxHashMap, Ty<'db>> { + debug!("calculate_diverging_fallback({:?})", unresolved_variables); + + // Construct a coercion graph where an edge `A -> B` indicates + // a type variable is that is coerced + let coercion_graph = self.create_coercion_graph(); + + // Extract the unsolved type inference variable vids; note that some + // unsolved variables are integer/float variables and are excluded. + let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid()); + + // Compute the diverging root vids D -- that is, the root vid of + // those type variables that (a) are the target of a coercion from + // a `!` type and (b) have not yet been solved. + // + // These variables are the ones that are targets for fallback to + // either `!` or `()`. + let diverging_roots: FxHashSet = self + .table + .diverging_type_vars + .iter() + .map(|&ty| self.shallow_resolve(ty)) + .filter_map(|ty| ty.ty_vid()) + .map(|vid| self.table.infer_ctxt.root_var(vid)) + .collect(); + debug!( + "calculate_diverging_fallback: diverging_type_vars={:?}", + self.table.diverging_type_vars + ); + debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots); + + // Find all type variables that are reachable from a diverging + // type variable. These will typically default to `!`, unless + // we find later that they are *also* reachable from some + // other type variable outside this set. + let mut roots_reachable_from_diverging = Dfs::empty(&coercion_graph); + let mut diverging_vids = vec![]; + let mut non_diverging_vids = vec![]; + for unsolved_vid in unsolved_vids { + let root_vid = self.table.infer_ctxt.root_var(unsolved_vid); + debug!( + "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", + unsolved_vid, + root_vid, + diverging_roots.contains(&root_vid), + ); + if diverging_roots.contains(&root_vid) { + diverging_vids.push(unsolved_vid); + roots_reachable_from_diverging.move_to(root_vid.as_u32().into()); + + // drain the iterator to visit all nodes reachable from this node + while roots_reachable_from_diverging.next(&coercion_graph).is_some() {} + } else { + non_diverging_vids.push(unsolved_vid); + } + } + + debug!( + "calculate_diverging_fallback: roots_reachable_from_diverging={:?}", + roots_reachable_from_diverging, + ); + + // Find all type variables N0 that are not reachable from a + // diverging variable, and then compute the set reachable from + // N0, which we call N. These are the *non-diverging* type + // variables. (Note that this set consists of "root variables".) + let mut roots_reachable_from_non_diverging = Dfs::empty(&coercion_graph); + for &non_diverging_vid in &non_diverging_vids { + let root_vid = self.table.infer_ctxt.root_var(non_diverging_vid); + if roots_reachable_from_diverging.discovered.contains(root_vid.as_usize()) { + continue; + } + roots_reachable_from_non_diverging.move_to(root_vid.as_u32().into()); + while roots_reachable_from_non_diverging.next(&coercion_graph).is_some() {} + } + debug!( + "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}", + roots_reachable_from_non_diverging, + ); + + debug!("obligations: {:#?}", self.table.fulfillment_cx.pending_obligations()); + + // For each diverging variable, figure out whether it can + // reach a member of N. If so, it falls back to `()`. Else + // `!`. + let mut diverging_fallback = + FxHashMap::with_capacity_and_hasher(diverging_vids.len(), FxBuildHasher); + + for &diverging_vid in &diverging_vids { + let diverging_ty = Ty::new_var(self.table.interner, diverging_vid); + let root_vid = self.table.infer_ctxt.root_var(diverging_vid); + let can_reach_non_diverging = Dfs::new(&coercion_graph, root_vid.as_u32().into()) + .iter(&coercion_graph) + .any(|n| roots_reachable_from_non_diverging.discovered.contains(n.index())); + + let mut fallback_to = |ty| { + diverging_fallback.insert(diverging_ty, ty); + }; + + match behavior { + DivergingFallbackBehavior::ToUnit => { + debug!("fallback to () - legacy: {:?}", diverging_vid); + fallback_to(self.types.unit); + } + DivergingFallbackBehavior::ContextDependent => { + // FIXME: rustc does the following, but given this is only relevant when the unstable + // `never_type_fallback` feature is active, I chose to not port this. + // if found_infer_var_info.self_in_trait && found_infer_var_info.output { + // // This case falls back to () to ensure that the code pattern in + // // tests/ui/never_type/fallback-closure-ret.rs continues to + // // compile when never_type_fallback is enabled. + // // + // // This rule is not readily explainable from first principles, + // // but is rather intended as a patchwork fix to ensure code + // // which compiles before the stabilization of never type + // // fallback continues to work. + // // + // // Typically this pattern is encountered in a function taking a + // // closure as a parameter, where the return type of that closure + // // (checked by `relationship.output`) is expected to implement + // // some trait (checked by `relationship.self_in_trait`). This + // // can come up in non-closure cases too, so we do not limit this + // // rule to specifically `FnOnce`. + // // + // // When the closure's body is something like `panic!()`, the + // // return type would normally be inferred to `!`. However, it + // // needs to fall back to `()` in order to still compile, as the + // // trait is specifically implemented for `()` but not `!`. + // // + // // For details on the requirements for these relationships to be + // // set, see the relationship finding module in + // // compiler/rustc_trait_selection/src/traits/relationships.rs. + // debug!("fallback to () - found trait and projection: {:?}", diverging_vid); + // fallback_to(self.types.unit); + // } + if can_reach_non_diverging { + debug!("fallback to () - reached non-diverging: {:?}", diverging_vid); + fallback_to(self.types.unit); + } else { + debug!("fallback to ! - all diverging: {:?}", diverging_vid); + fallback_to(self.types.never); + } + } + DivergingFallbackBehavior::ToNever => { + debug!( + "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}", + diverging_vid + ); + fallback_to(self.types.never); + } + } + } + + diverging_fallback + } + + /// Returns a graph whose nodes are (unresolved) inference variables and where + /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. + fn create_coercion_graph(&self) -> Graph<(), ()> { + let pending_obligations = self.table.fulfillment_cx.pending_obligations(); + let pending_obligations_len = pending_obligations.len(); + debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations); + let coercion_edges = pending_obligations + .into_iter() + .filter_map(|obligation| { + // The predicates we are looking for look like `Coerce(?A -> ?B)`. + // They will have no bound variables. + obligation.predicate.kind().no_bound_vars() + }) + .filter_map(|atom| { + // We consider both subtyping and coercion to imply 'flow' from + // some position in the code `a` to a different position `b`. + // This is then used to determine which variables interact with + // live code, and as such must fall back to `()` to preserve + // soundness. + // + // In practice currently the two ways that this happens is + // coercion and subtyping. + let (a, b) = match atom { + PredicateKind::Coerce(CoercePredicate { a, b }) => (a, b), + PredicateKind::Subtype(SubtypePredicate { a_is_expected: _, a, b }) => (a, b), + _ => return None, + }; + + let a_vid = self.root_vid(a)?; + let b_vid = self.root_vid(b)?; + Some((a_vid.as_u32(), b_vid.as_u32())) + }); + let num_ty_vars = self.table.infer_ctxt.num_ty_vars(); + let mut graph = Graph::with_capacity(num_ty_vars, pending_obligations_len); + for _ in 0..num_ty_vars { + graph.add_node(()); + } + graph.extend_with_edges(coercion_edges); + graph + } + + /// If `ty` is an unresolved type variable, returns its root vid. + fn root_vid(&self, ty: Ty<'db>) -> Option { + Some(self.table.infer_ctxt.root_var(self.shallow_resolve(ty).ty_vid()?)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index dd7e77ba8c098..108cf5b1a2b8e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,8 +3,7 @@ use std::fmt; use chalk_ir::{ - CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable, - interner::HasInterner, + CanonicalVarKind, TyVariableKind, cast::Cast, fold::TypeFoldable, interner::HasInterner, }; use either::Either; use hir_def::{AdtId, lang_item::LangItem}; @@ -12,7 +11,7 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - FloatVid, IntVid, TyVid, TypeVisitableExt, UpcastFrom, + TyVid, TypeVisitableExt, UpcastFrom, inherent::{IntoKind, Span, Term as _, Ty as _}, relate::{Relate, solver_relating::RelateExt}, solve::{Certainty, GoalSource}, @@ -23,8 +22,8 @@ use triomphe::Arc; use super::{InferResult, InferenceContext, TypeError}; use crate::{ AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData, - InferenceVar, Interner, Lifetime, OpaqueTyId, ProjectionTy, Scalar, Substitution, - TraitEnvironment, Ty, TyExt, TyKind, VariableKind, + InferenceVar, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, TraitEnvironment, Ty, + TyExt, TyKind, VariableKind, consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, @@ -143,7 +142,6 @@ pub fn could_unify_deeply( let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); table.select_obligations_where_possible(); - table.propagate_diverging_flag(); let ty1_with_vars = table.resolve_completely(ty1_with_vars); let ty2_with_vars = table.resolve_completely(ty2_with_vars); table.unify_deeply(&ty1_with_vars, &ty2_with_vars) @@ -170,13 +168,19 @@ pub(crate) fn unify( GenericArgData::Const(c) => c.inference_var(Interner), } == Some(iv)) }; - let fallback = |iv, kind, default, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), - chalk_ir::VariableKind::Lifetime => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)), - chalk_ir::VariableKind::Const(ty) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)), + let fallback = |iv, kind, binder| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else( + || TyKind::Error.intern(Interner).cast(Interner), + |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner), + ), + chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else( + || crate::error_lifetime().cast(Interner), + |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner), + ), + chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else( + || crate::unknown_const(ty.clone()).cast(Interner), + |i| BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner), + ), }; Some(Substitution::from_iter( Interner, @@ -215,14 +219,13 @@ pub(crate) struct InferenceTable<'db> { pub(crate) trait_env: Arc>, pub(crate) tait_coercion_table: Option>, pub(crate) infer_ctxt: InferCtxt<'db>, - diverging_tys: FxHashSet, pub(super) fulfillment_cx: FulfillmentCtxt<'db>, + pub(super) diverging_type_vars: FxHashSet>, } pub(crate) struct InferenceTableSnapshot<'db> { ctxt_snapshot: CombinedSnapshot, obligations: FulfillmentCtxt<'db>, - diverging_tys: FxHashSet, } impl<'db> InferenceTable<'db> { @@ -238,7 +241,7 @@ impl<'db> InferenceTable<'db> { tait_coercion_table: None, fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), infer_ctxt, - diverging_tys: FxHashSet::default(), + diverging_type_vars: FxHashSet::default(), } } @@ -321,74 +324,8 @@ impl<'db> InferenceTable<'db> { } } - /// Chalk doesn't know about the `diverging` flag, so when it unifies two - /// type variables of which one is diverging, the chosen root might not be - /// diverging and we have no way of marking it as such at that time. This - /// function goes through all type variables and make sure their root is - /// marked as diverging if necessary, so that resolving them gives the right - /// result. - pub(super) fn propagate_diverging_flag(&mut self) { - let mut new_tys = FxHashSet::default(); - for ty in self.diverging_tys.iter() { - match ty.kind(Interner) { - TyKind::InferenceVar(var, kind) => match kind { - TyVariableKind::General => { - let root = InferenceVar::from( - self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32(), - ); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } - } - TyVariableKind::Integer => { - let root = InferenceVar::from( - self.infer_ctxt - .inner - .borrow_mut() - .int_unification_table() - .find(IntVid::from_usize(var.index() as usize)) - .as_u32(), - ); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } - } - TyVariableKind::Float => { - let root = InferenceVar::from( - self.infer_ctxt - .inner - .borrow_mut() - .float_unification_table() - .find(FloatVid::from_usize(var.index() as usize)) - .as_u32(), - ); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } - } - }, - _ => {} - } - } - self.diverging_tys.extend(new_tys); - } - - pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind) { - self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner)); - } - - fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - let is_diverging = - self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); - if is_diverging { - return TyKind::Never.intern(Interner); - } - match kind { - TyVariableKind::General => TyKind::Error, - TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), - TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), - } - .intern(Interner) + pub(super) fn set_diverging(&mut self, ty: crate::next_solver::Ty<'db>) { + self.diverging_type_vars.insert(ty); } pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> @@ -529,7 +466,7 @@ impl<'db> InferenceTable<'db> { let ty = var.to_ty(Interner, kind); if diverging { - self.diverging_tys.insert(ty.clone()); + self.diverging_type_vars.insert(ty.to_nextsolver(self.interner)); } ty } @@ -573,7 +510,7 @@ impl<'db> InferenceTable<'db> { pub(crate) fn resolve_with_fallback( &mut self, t: T, - fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, + fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, ) -> T where T: HasInterner + TypeFoldable, @@ -615,7 +552,7 @@ impl<'db> InferenceTable<'db> { fn resolve_with_fallback_inner( &mut self, t: T, - fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, + fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, ) -> T where T: HasInterner + TypeFoldable, @@ -632,53 +569,15 @@ impl<'db> InferenceTable<'db> { T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { - let t = self.resolve_with_fallback(t, &|_, _, d, _| d); - let t = self.normalize_associated_types_in(t); - // let t = self.resolve_opaque_tys_in(t); - // Resolve again, because maybe normalization inserted infer vars. - self.resolve_with_fallback(t, &|_, _, d, _| d) - } + let value = t.to_nextsolver(self.interner); + let value = self.infer_ctxt.resolve_vars_if_possible(value); - /// Apply a fallback to unresolved scalar types. Integer type variables and float type - /// variables are replaced with i32 and f64, respectively. - /// - /// This method is only intended to be called just before returning inference results (i.e. in - /// `InferenceContext::resolve_all()`). - /// - /// FIXME: This method currently doesn't apply fallback to unconstrained general type variables - /// whereas rustc replaces them with `()` or `!`. - pub(super) fn fallback_if_possible(&mut self) { - let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner); - let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner); - - let int_vars = self.infer_ctxt.inner.borrow_mut().int_unification_table().len(); - for v in 0..int_vars { - let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); - let maybe_resolved = self.resolve_ty_shallow(&var); - if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { - // I don't think we can ever unify these vars with float vars, but keep this here for now - let fallback = match kind { - TyVariableKind::Integer => &int_fallback, - TyVariableKind::Float => &float_fallback, - TyVariableKind::General => unreachable!(), - }; - self.unify(&var, fallback); - } - } - let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len(); - for v in 0..float_vars { - let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Float); - let maybe_resolved = self.resolve_ty_shallow(&var); - if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { - // I don't think we can ever unify these vars with float vars, but keep this here for now - let fallback = match kind { - TyVariableKind::Integer => &int_fallback, - TyVariableKind::Float => &float_fallback, - TyVariableKind::General => unreachable!(), - }; - self.unify(&var, fallback); - } - } + let mut goals = vec![]; + let value = value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)); + + // FIXME(next-solver): Handle `goals`. + + value.to_chalk(self.interner) } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. @@ -829,15 +728,13 @@ impl<'db> InferenceTable<'db> { pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); - let diverging_tys = self.diverging_tys.clone(); let obligations = self.fulfillment_cx.clone(); - InferenceTableSnapshot { ctxt_snapshot, diverging_tys, obligations } + InferenceTableSnapshot { ctxt_snapshot, obligations } } #[tracing::instrument(skip_all)] pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); - self.diverging_tys = snapshot.diverging_tys; self.fulfillment_cx = snapshot.obligations; } @@ -1166,14 +1063,10 @@ impl fmt::Debug for InferenceTable<'_> { mod resolve { use super::InferenceTable; use crate::{ - ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, - InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind, - next_solver::mapping::NextSolverToChalk, - }; - use chalk_ir::{ - cast::Cast, - fold::{TypeFoldable, TypeFolder}, + Const, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, + VariableKind, next_solver::mapping::NextSolverToChalk, }; + use chalk_ir::fold::{TypeFoldable, TypeFolder}; use rustc_type_ir::{FloatVid, IntVid, TyVid}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -1187,7 +1080,7 @@ mod resolve { pub(super) struct Resolver< 'a, 'b, - F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, + F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, > { pub(super) table: &'a mut InferenceTable<'b>, pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>, @@ -1195,7 +1088,7 @@ mod resolve { } impl TypeFolder for Resolver<'_, '_, F> where - F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, + F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, { fn as_dyn(&mut self) -> &mut dyn TypeFolder { self @@ -1217,8 +1110,7 @@ mod resolve { let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type - let default = self.table.fallback_value(var, kind).cast(Interner); - return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone(); } @@ -1230,8 +1122,7 @@ mod resolve { self.var_stack.pop(); result } else { - let default = self.table.fallback_value(var, kind).cast(Interner); - (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone() } @@ -1247,8 +1138,7 @@ mod resolve { let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type - let default = self.table.fallback_value(var, kind).cast(Interner); - return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone(); } @@ -1260,8 +1150,7 @@ mod resolve { self.var_stack.pop(); result } else { - let default = self.table.fallback_value(var, kind).cast(Interner); - (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone() } @@ -1277,8 +1166,7 @@ mod resolve { let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type - let default = self.table.fallback_value(var, kind).cast(Interner); - return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone(); } @@ -1290,8 +1178,7 @@ mod resolve { self.var_stack.pop(); result } else { - let default = self.table.fallback_value(var, kind).cast(Interner); - (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + (self.fallback)(var, VariableKind::Ty(kind), outer_binder) .assert_ty_ref(Interner) .clone() } @@ -1310,15 +1197,9 @@ mod resolve { .infer_ctxt .root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); let var = InferenceVar::from(vid.as_u32()); - let default = ConstData { - ty: ty.clone(), - value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), - } - .intern(Interner) - .cast(Interner); if self.var_stack.contains(&(var, VarKind::Const)) { // recursive - return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) + return (self.fallback)(var, VariableKind::Const(ty), outer_binder) .assert_const_ref(Interner) .clone(); } @@ -1330,7 +1211,7 @@ mod resolve { self.var_stack.pop(); result } else { - (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) + (self.fallback)(var, VariableKind::Const(ty), outer_binder) .assert_const_ref(Interner) .clone() } @@ -1349,3 +1230,124 @@ mod resolve { } } } + +mod resolve_completely { + use rustc_type_ir::{ + DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable, + inherent::{Const as _, Ty as _}, + }; + + use crate::next_solver::Region; + use crate::{ + infer::unify::InferenceTable, + next_solver::{ + Const, DbInterner, ErrorGuaranteed, Goal, Predicate, Term, Ty, + infer::traits::ObligationCause, + normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, + }, + }; + + pub(super) struct Resolver<'a, 'db> { + ctx: &'a mut InferenceTable<'db>, + /// Whether we should normalize, disabled when resolving predicates. + should_normalize: bool, + nested_goals: &'a mut Vec>>, + } + + impl<'a, 'db> Resolver<'a, 'db> { + pub(super) fn new( + ctx: &'a mut InferenceTable<'db>, + should_normalize: bool, + nested_goals: &'a mut Vec>>, + ) -> Resolver<'a, 'db> { + Resolver { ctx, nested_goals, should_normalize } + } + + fn handle_term( + &mut self, + value: T, + outer_exclusive_binder: impl FnOnce(T) -> DebruijnIndex, + ) -> T + where + T: Into> + TypeSuperFoldable> + Copy, + { + let value = if self.should_normalize { + let cause = ObligationCause::new(); + let at = self.ctx.infer_ctxt.at(&cause, self.ctx.trait_env.env); + let universes = vec![None; outer_exclusive_binder(value).as_usize()]; + match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + ) { + Ok((value, goals)) => { + self.nested_goals.extend(goals); + value + } + Err(_errors) => { + // FIXME: Report the error. + value + } + } + } else { + value + }; + + value.fold_with(&mut ReplaceInferWithError { interner: self.ctx.interner }) + } + } + + impl<'cx, 'db> TypeFolder> for Resolver<'cx, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.ctx.interner + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { Region::error(self.ctx.interner) } else { r } + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + self.handle_term(ty, |it| it.outer_exclusive_binder()) + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + self.handle_term(ct, |it| it.outer_exclusive_binder()) + } + + fn fold_predicate(&mut self, predicate: Predicate<'db>) -> Predicate<'db> { + assert!( + !self.should_normalize, + "normalizing predicates in writeback is not generally sound" + ); + predicate.super_fold_with(self) + } + } + + struct ReplaceInferWithError<'db> { + interner: DbInterner<'db>, + } + + impl<'db> TypeFolder> for ReplaceInferWithError<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if t.is_infer() { + Ty::new_error(self.interner, ErrorGuaranteed) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if c.is_ct_infer() { + Const::new_error(self.interner, ErrorGuaranteed) + } else { + c.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { Region::error(self.interner) } else { r } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 073a02908deeb..ab167e88af2ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -13,7 +13,7 @@ pub(crate) mod inspect; pub mod interner; mod ir_print; pub mod mapping; -mod normalize; +pub mod normalize; pub mod obligation_ctxt; mod opaques; pub mod predicate; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index d6214d991560a..0bfd2b8003d05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -15,7 +15,7 @@ use super::{ interner::{BoundVarKind, DbInterner, Placeholder}, }; -type RegionKind<'db> = rustc_type_ir::RegionKind>; +pub type RegionKind<'db> = rustc_type_ir::RegionKind>; #[salsa::interned(constructor = new_, debug)] pub struct Region<'db> { @@ -53,6 +53,10 @@ impl<'db> Region<'db> { Region::new(interner, RegionKind::ReVar(v)) } + pub fn new_erased(interner: DbInterner<'db>) -> Region<'db> { + Region::new(interner, RegionKind::ReErased) + } + pub fn is_placeholder(&self) -> bool { matches!(self.inner(), RegionKind::RePlaceholder(..)) } @@ -61,6 +65,10 @@ impl<'db> Region<'db> { matches!(self.inner(), RegionKind::ReStatic) } + pub fn is_var(&self) -> bool { + matches!(self.inner(), RegionKind::ReVar(_)) + } + pub fn error(interner: DbInterner<'db>) -> Self { Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index c7a747ade3e76..16bf082b01a35 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -7,6 +7,7 @@ use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::TyVid; use rustc_type_ir::{ BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, @@ -338,6 +339,14 @@ impl<'db> Ty<'db> { matches!(self.kind(), TyKind::Tuple(tys) if tys.inner().is_empty()) } + #[inline] + pub fn ty_vid(self) -> Option { + match self.kind() { + TyKind::Infer(rustc_type_ir::TyVar(vid)) => Some(vid), + _ => None, + } + } + /// Given a `fn` type, returns an equivalent `unsafe fn` type; /// that is, a `fn` type that is equivalent in every way for being /// unsafe. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index af5290d720356..4d68179a88b82 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -14,8 +14,6 @@ fn test() { ); } -// FIXME(next-solver): The never type fallback implemented in r-a no longer works properly because of -// `Coerce` predicates. We should reimplement fallback like rustc. #[test] fn infer_never2() { check_types( @@ -26,7 +24,7 @@ fn test() { let a = gen(); if false { a } else { loop {} }; a; -} //^ {unknown} +} //^ ! "#, ); } @@ -41,7 +39,7 @@ fn test() { let a = gen(); if false { loop {} } else { a }; a; - //^ {unknown} + //^ ! } "#, ); @@ -56,7 +54,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; -} //^ Option<{unknown}> +} //^ Option "#, ); } @@ -220,7 +218,7 @@ fn test(a: i32) { _ => loop {}, }; i; -} //^ {unknown} +} //^ ! "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index a6215ef8fe4fe..00835aa03130c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1951,7 +1951,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ {unknown} + //^^^^^^^^^^^^^ ! let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 1db4f8ecd6bab..920bdd9568fcf 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -516,4 +516,5 @@ define_symbols! { flags, precision, width, + never_type_fallback, } From 60dd0df6e73e195b9779121bac7e2927ddcf48f2 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 24 Sep 2025 13:52:33 -0400 Subject: [PATCH 0833/1900] Address review comments --- .../rustc_error_codes/src/error_codes/E0719.md | 2 +- .../src/hir_ty_lowering/bounds.rs | 5 +++-- .../src/hir_ty_lowering/dyn_trait.rs | 6 ++++-- .../src/hir_ty_lowering/mod.rs | 18 +++++++++++++++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md index 17cbd2de49efd..6aec38b42a3b1 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0719.md +++ b/compiler/rustc_error_codes/src/error_codes/E0719.md @@ -1,4 +1,4 @@ -An associated item value was specified more than once in a trait object. +An associated item was specified more than once in a trait object. Erroneous code example: diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index a8d75ba223ab3..a59520f16feb9 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -21,7 +21,8 @@ use tracing::{debug, instrument}; use super::errors::GenericsArgsErrExtend; use crate::errors; use crate::hir_ty_lowering::{ - AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason, + AssocItemQSelf, FeedConstTy, HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, + RegionInferReason, }; #[derive(Debug, Default)] @@ -362,7 +363,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { param_ty, bounds, predicate_filter, - false, + OverlappingAsssocItemConstraints::Allowed, ); } hir::GenericBound::Outlives(lifetime) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a4179776572d6..c0b1377308923 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -23,7 +23,9 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::errors::SelfInTypeAlias; -use crate::hir_ty_lowering::{GenericArgCountMismatch, PredicateFilter, RegionInferReason}; +use crate::hir_ty_lowering::{ + GenericArgCountMismatch, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, +}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Lower a trait object type from the HIR to our internal notion of a type. @@ -60,7 +62,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { dummy_self, &mut user_written_bounds, PredicateFilter::SelfOnly, - true, + OverlappingAsssocItemConstraints::Forbidden, ); if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct { potential_assoc_types.extend(invalid_args); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 0ff1fabd7b303..cc5c0d0ad6ce9 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -332,6 +332,15 @@ pub(crate) enum GenericArgPosition { MethodCall, } +/// Whether to allow duplicate associated iten constraints in a trait ref, e.g. +/// `Trait`. This is forbidden in `dyn Trait<...>` +/// but allowed everywhere else. +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum OverlappingAsssocItemConstraints { + Allowed, + Forbidden, +} + /// A marker denoting that the generic arguments that were /// provided did not match the respective generic parameters. #[derive(Clone, Debug)] @@ -752,7 +761,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, predicate_filter: PredicateFilter, - for_dyn: bool, + overlapping_assoc_item_constraints: OverlappingAsssocItemConstraints, ) -> GenericArgCountResult { let tcx = self.tcx(); @@ -909,7 +918,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let mut dup_constraints = FxIndexMap::default(); + let mut dup_constraints = (overlapping_assoc_item_constraints + == OverlappingAsssocItemConstraints::Forbidden) + .then_some(FxIndexMap::default()); + for constraint in trait_segment.args().constraints { // Don't register any associated item constraints for negative bounds, // since we should have emitted an error for them earlier, and they @@ -928,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { poly_trait_ref, constraint, bounds, - for_dyn.then_some(&mut dup_constraints), + dup_constraints.as_mut(), constraint.span, predicate_filter, ); From a86f14072714fb817a6f60fa20be5a4e875d049f Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Tue, 12 Aug 2025 06:39:50 +0800 Subject: [PATCH 0834/1900] do not materialise X in [X; 0] when X is unsizing a const --- .../src/builder/expr/as_rvalue.rs | 21 ++++++++++++- ...no_local_for_coerced_const-issue-143671.rs | 30 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index a4ef6e9273921..d6dc12e5955a5 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -8,6 +8,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::*; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::{CastTy, mir_cast_kind}; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; @@ -656,6 +657,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(rvalue) } + /// Recursively inspect a THIR expression and probe through unsizing + /// operations that can be const-folded today. + fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool { + loop { + match kind { + &ExprKind::PointerCoercion { + cast: PointerCoercion::Unsize, + source: eid, + is_from_as_cast: _, + } + | &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => { + kind = &self.thir[eid].kind + } + _ => return matches!(Category::of(&kind), Some(Category::Constant)), + } + } + } + fn build_zero_repeat( &mut self, mut block: BasicBlock, @@ -666,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let this = self; let value_expr = &this.thir[value]; let elem_ty = value_expr.ty; - if let Some(Category::Constant) = Category::of(&value_expr.kind) { + if this.check_constness(&value_expr.kind) { // Repeating a const does nothing } else { // For a non-const, we may need to generate an appropriate `Drop` diff --git a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs new file mode 100644 index 0000000000000..eb814e9723c1c --- /dev/null +++ b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs @@ -0,0 +1,30 @@ +//@ run-pass + +#![feature(unsize)] +#![feature(coerce_unsized)] + +use std::fmt::Display; +use std::marker::Unsize; +use std::ops::CoerceUnsized; + +#[repr(transparent)] +struct X<'a, T: ?Sized> { + f: &'a T, +} + +impl<'a, T: ?Sized> Drop for X<'a, T> { + fn drop(&mut self) { + panic!() + } +} + +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for X<'a, T> where + &'a T: CoerceUnsized<&'a U> +{ +} + +const Y: X<'static, i32> = X { f: &0 }; + +fn main() { + let _: [X<'static, dyn Display>; 0] = [Y; 0]; +} From 120162e30ffa92308a6a3a76f2328467fbd10ced Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 13 Aug 2025 02:26:04 +0800 Subject: [PATCH 0835/1900] add test fixture for newly allowed const expr Signed-off-by: Ding Xiang Fei Co-authored-by: Theemathas Chirananthavat --- .../coercion/no_local_for_coerced_const-issue-143671.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs index eb814e9723c1c..5924809f34c85 100644 --- a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs +++ b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs @@ -6,6 +6,7 @@ use std::fmt::Display; use std::marker::Unsize; use std::ops::CoerceUnsized; +use std::rc::Weak; #[repr(transparent)] struct X<'a, T: ?Sized> { @@ -27,4 +28,11 @@ const Y: X<'static, i32> = X { f: &0 }; fn main() { let _: [X<'static, dyn Display>; 0] = [Y; 0]; + coercion_on_weak_in_const(); +} + +fn coercion_on_weak_in_const() { + const X: Weak = Weak::new(); + const Y: [Weak; 0] = [X; 0]; + let _ = Y; } From b77de834c031890c048f8164d4b5979d2511c00e Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Thu, 25 Sep 2025 01:53:34 +0800 Subject: [PATCH 0836/1900] mark THIR use as candidate for constness check --- compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs | 5 ++++- .../coercion/no_local_for_coerced_const-issue-143671.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index d6dc12e5955a5..3a5839f2d404d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -661,8 +661,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// operations that can be const-folded today. fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool { loop { + debug!(?kind, "check_constness"); match kind { - &ExprKind::PointerCoercion { + &ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ } + | &ExprKind::Use { source: eid } + | &ExprKind::PointerCoercion { cast: PointerCoercion::Unsize, source: eid, is_from_as_cast: _, diff --git a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs index 5924809f34c85..38479d9070b8d 100644 --- a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs +++ b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs @@ -29,6 +29,7 @@ const Y: X<'static, i32> = X { f: &0 }; fn main() { let _: [X<'static, dyn Display>; 0] = [Y; 0]; coercion_on_weak_in_const(); + coercion_on_weak_as_cast(); } fn coercion_on_weak_in_const() { @@ -36,3 +37,10 @@ fn coercion_on_weak_in_const() { const Y: [Weak; 0] = [X; 0]; let _ = Y; } + +fn coercion_on_weak_as_cast() { + const Y: X<'static, i32> = X { f: &0 }; + // What happens in the following code is that + // a constant is explicitly coerced into + let _a: [X<'static, dyn Display>; 0] = [Y as X<'static, dyn Display>; 0]; +} From 5bec161ce12501695356492129ea8fc952a30f85 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 24 Sep 2025 10:34:52 -0700 Subject: [PATCH 0837/1900] rustdoc-search: stringdex update with more packing Before: 18M build/x86_64-unknown-linux-gnu/doc/search.index/ 57M build/x86_64-unknown-linux-gnu/compiler-doc/search.index/ After: 16M build/x86_64-unknown-linux-gnu/doc/search.index/ 49M build/x86_64-unknown-linux-gnu/compiler-doc/search.index/ --- Cargo.lock | 4 +- src/librustdoc/Cargo.toml | 2 +- src/librustdoc/html/static/js/stringdex.js | 78 ++++++++++++++-------- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2e635b4cfe65..7c2eb559b21e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5233,9 +5233,9 @@ dependencies = [ [[package]] name = "stringdex" -version = "0.0.1-alpha9" +version = "0.0.1-alpha10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7081029913fd7d591c0112182aba8c98ae886b4f12edb208130496cd17dc3c15" +checksum = "0fa846a7d509d1828a4f90962dc09810e161abcada7fc6a921e92c168d0811d7" dependencies = [ "stacker", ] diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index f37a8d85361f2..0d77fe64e30f8 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -21,7 +21,7 @@ rustdoc-json-types = { path = "../rustdoc-json-types" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" smallvec = "1.8.1" -stringdex = { version = "0.0.1-alpha9" } +stringdex = { version = "0.0.1-alpha10" } tempfile = "3" threadpool = "1.8.1" tracing = "0.1" diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index 5bdc81e330efa..6299576d56439 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -1108,22 +1108,39 @@ function loadDatabase(hooks) { const id2 = id1 + ((nodeid[4] << 8) | nodeid[5]); leaves = RoaringBitmap.makeSingleton(id1) .union(RoaringBitmap.makeSingleton(id2)); + } else if (!isWhole && (nodeid[0] & 0xf0) === 0x80) { + const id1 = ((nodeid[0] & 0x0f) << 16) | (nodeid[1] << 8) | nodeid[2]; + const id2 = id1 + ((nodeid[3] << 4) | ((nodeid[4] >> 4) & 0x0f)); + const id3 = id2 + (((nodeid[4] & 0x0f) << 8) | nodeid[5]); + leaves = RoaringBitmap.makeSingleton(id1) + .union(RoaringBitmap.makeSingleton(id2)) + .union(RoaringBitmap.makeSingleton(id3)); } else { leaves = RoaringBitmap.makeSingleton( (nodeid[2] << 24) | (nodeid[3] << 16) | (nodeid[4] << 8) | nodeid[5], ); } - const data = (nodeid[0] & 0x20) !== 0 ? - Uint8Array.of(((nodeid[0] & 0x0f) << 4) | (nodeid[1] >> 4)) : - EMPTY_UINT8; - newPromise = Promise.resolve(new PrefixSearchTree( - EMPTY_SEARCH_TREE_BRANCHES, - EMPTY_SEARCH_TREE_BRANCHES, - data, - isWhole ? leaves : EMPTY_BITMAP, - isWhole ? EMPTY_BITMAP : leaves, - )); + if (isWhole) { + const data = (nodeid[0] & 0x20) !== 0 ? + Uint8Array.of(((nodeid[0] & 0x0f) << 4) | (nodeid[1] >> 4)) : + EMPTY_UINT8; + newPromise = Promise.resolve(new PrefixSearchTree( + EMPTY_SEARCH_TREE_BRANCHES, + EMPTY_SEARCH_TREE_BRANCHES, + data, + leaves, + EMPTY_BITMAP, + )); + } else { + const data = (nodeid[0] & 0xf0) === 0x80 ? 0 : ( + ((nodeid[0] & 0x0f) << 4) | (nodeid[1] >> 4)); + newPromise = Promise.resolve(new SuffixSearchTree( + EMPTY_SEARCH_TREE_BRANCHES, + data, + leaves, + )); + } } else { const hashHex = makeHexFromUint8Array(nodeid); newPromise = new Promise((resolve, reject) => { @@ -2748,6 +2765,7 @@ function loadDatabase(hooks) { // because that's the canonical, hashed version of the data let compression_tag = input[i]; const is_pure_suffixes_only_node = (compression_tag & 0x01) !== 0; + let no_leaves_flag; if (compression_tag > 1) { // compressed node const is_long_compressed = (compression_tag & 0x04) !== 0; @@ -2759,7 +2777,8 @@ function loadDatabase(hooks) { compression_tag |= input[i] << 16; i += 1; } - let dlen = input[i]; + let dlen = input[i] & 0x7F; + no_leaves_flag = input[i] & 0x80; i += 1; if (is_data_compressed) { data = data_history[data_history.length - dlen - 1]; @@ -2786,10 +2805,15 @@ function loadDatabase(hooks) { let whole; let suffix; if (is_pure_suffixes_only_node) { - suffix = input[i] === 0 ? - EMPTY_BITMAP1 : - new RoaringBitmap(input, i); - i += suffix.consumed_len_bytes; + if (no_leaves_flag) { + whole = EMPTY_BITMAP; + suffix = EMPTY_BITMAP; + } else { + suffix = input[i] === 0 ? + EMPTY_BITMAP1 : + new RoaringBitmap(input, i); + i += suffix.consumed_len_bytes; + } tree = new SuffixSearchTree( branches, dlen, @@ -2807,7 +2831,7 @@ function loadDatabase(hooks) { let ci = 0; canonical[ci] = 1; ci += 1; - canonical[ci] = dlen; + canonical[ci] = dlen | no_leaves_flag; ci += 1; canonical[ci] = input[coffset]; // suffix child count ci += 1; @@ -2821,10 +2845,9 @@ function loadDatabase(hooks) { } siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); } else { - if (input[i] === 0xff) { + if (no_leaves_flag) { whole = EMPTY_BITMAP; - suffix = EMPTY_BITMAP1; - i += 1; + suffix = EMPTY_BITMAP; } else { whole = input[i] === 0 ? EMPTY_BITMAP1 : @@ -2856,7 +2879,7 @@ function loadDatabase(hooks) { let ci = 0; canonical[ci] = 0; ci += 1; - canonical[ci] = dlen; + canonical[ci] = dlen | no_leaves_flag; ci += 1; canonical.set(data, ci); ci += data.length; @@ -2880,9 +2903,11 @@ function loadDatabase(hooks) { } hash[2] &= 0x7f; } else { + i += 1; // uncompressed node - const dlen = input [i + 1]; - i += 2; + const dlen = input[i] & 0x7F; + no_leaves_flag = input[i] & 0x80; + i += 1; if (dlen === 0 || is_pure_suffixes_only_node) { data = EMPTY_UINT8; } else { @@ -2897,16 +2922,15 @@ function loadDatabase(hooks) { i += branches_consumed_len_bytes; let whole; let suffix; - if (is_pure_suffixes_only_node) { + if (no_leaves_flag) { + whole = EMPTY_BITMAP; + suffix = EMPTY_BITMAP; + } else if (is_pure_suffixes_only_node) { whole = EMPTY_BITMAP; suffix = input[i] === 0 ? EMPTY_BITMAP1 : new RoaringBitmap(input, i); i += suffix.consumed_len_bytes; - } else if (input[i] === 0xff) { - whole = EMPTY_BITMAP; - suffix = EMPTY_BITMAP; - i += 1; } else { whole = input[i] === 0 ? EMPTY_BITMAP1 : From d63a7ccc7cd9eab72a0ddca84625589d6ba0aec8 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Wed, 24 Sep 2025 23:41:07 +0500 Subject: [PATCH 0838/1900] Update actions/github-script to v8 --- .github/workflows/lintcheck_summary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml index 183258a964379..6768cd65701ac 100644 --- a/.github/workflows/lintcheck_summary.yml +++ b/.github/workflows/lintcheck_summary.yml @@ -35,7 +35,7 @@ jobs: github-token: ${{ github.token }} - name: Format comment - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require("fs"); From a9554b4d5f2666b0bce66dfb8f70a41092c9265f Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 24 Sep 2025 20:02:34 +0100 Subject: [PATCH 0839/1900] Clarify Display for error should not include source --- library/core/src/error.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 92b3c83d1bf34..9ca91ee009ee9 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -16,13 +16,19 @@ use crate::fmt::{self, Debug, Display, Formatter}; /// assert_eq!(err.to_string(), "invalid digit found in string"); /// ``` /// +/// # Error source +/// /// Errors may provide cause information. [`Error::source()`] is generally /// used when errors cross "abstraction boundaries". If one module must report /// an error that is caused by an error from a lower-level module, it can allow -/// accessing that error via [`Error::source()`]. This makes it possible for the +/// accessing that error via `Error::source()`. This makes it possible for the /// high-level module to provide its own errors while also revealing some of the /// implementation for debugging. /// +/// In error types that wrap an underlying error, the underlying error +/// should be either returned by the outer error's `Error::source()`, or rendered +/// by the outer error's `Display` implementation, but not both. +/// /// # Example /// /// Implementing the `Error` trait only requires that `Debug` and `Display` are implemented too. From a00f24116e9bcecc8c73f9f7ec0c399964b37a92 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 18 Sep 2025 15:02:49 -0400 Subject: [PATCH 0840/1900] unstably constify float mul_add methods Co-authored-by: Ralf Jung --- .../src/interpret/intrinsics.rs | 46 +++++++++++++++++++ .../rustc_const_eval/src/interpret/machine.rs | 8 ++++ library/core/src/intrinsics/mod.rs | 16 +++---- library/core/src/num/f128.rs | 3 +- library/core/src/num/f16.rs | 3 +- library/core/src/num/f32.rs | 3 +- library/core/src/num/f64.rs | 3 +- library/coretests/tests/floats/f128.rs | 18 -------- library/coretests/tests/floats/f16.rs | 18 -------- library/coretests/tests/floats/f32.rs | 21 --------- library/coretests/tests/floats/f64.rs | 21 --------- library/coretests/tests/floats/mod.rs | 39 +++++++++++++++- library/coretests/tests/lib.rs | 1 + library/std/src/lib.rs | 1 + library/std/src/num/f32.rs | 3 +- library/std/src/num/f64.rs | 3 +- src/tools/miri/src/intrinsics/math.rs | 41 ----------------- src/tools/miri/src/machine.rs | 5 ++ 18 files changed, 118 insertions(+), 135 deletions(-) delete mode 100644 library/coretests/tests/floats/f32.rs delete mode 100644 library/coretests/tests/floats/f64.rs diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 418dd658121db..785978b4d7111 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -636,6 +636,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest, rustc_apfloat::Round::NearestTiesToEven, )?, + sym::fmaf16 => self.fma_intrinsic::(args, dest)?, + sym::fmaf32 => self.fma_intrinsic::(args, dest)?, + sym::fmaf64 => self.fma_intrinsic::(args, dest)?, + sym::fmaf128 => self.fma_intrinsic::(args, dest)?, + sym::fmuladdf16 => self.float_muladd_intrinsic::(args, dest)?, + sym::fmuladdf32 => self.float_muladd_intrinsic::(args, dest)?, + sym::fmuladdf64 => self.float_muladd_intrinsic::(args, dest)?, + sym::fmuladdf128 => self.float_muladd_intrinsic::(args, dest)?, // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), @@ -1035,4 +1043,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(res, dest)?; interp_ok(()) } + + fn fma_intrinsic( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let c: F = self.read_scalar(&args[2])?.to_float()?; + + let res = a.mul_add(b, c).value; + let res = self.adjust_nan(res, &[a, b, c]); + self.write_scalar(res, dest)?; + interp_ok(()) + } + + fn float_muladd_intrinsic( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let c: F = self.read_scalar(&args[2])?.to_float()?; + + let fuse = M::float_fuse_mul_add(self); + + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = self.adjust_nan(res, &[a, b, c]); + self.write_scalar(res, dest)?; + interp_ok(()) + } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e22629993fba7..1725635e0b479 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -289,6 +289,9 @@ pub trait Machine<'tcx>: Sized { a } + /// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations. + fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool; + /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { @@ -672,6 +675,11 @@ pub macro compile_time_machine(<$tcx: lifetime>) { match fn_val {} } + #[inline(always)] + fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool { + true + } + #[inline(always)] fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { // We can't look at `tcx.sess` here as that can differ across crates, which can lead to diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a174ced5a2a63..98eec7303da6c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1312,28 +1312,28 @@ pub fn log2f128(x: f128) -> f128; /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; +pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; +pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values. /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; +pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values. /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; +pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// Returns `a * b + c` for `f16` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the @@ -1347,7 +1347,7 @@ pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; +pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1360,7 +1360,7 @@ pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; +pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1373,7 +1373,7 @@ pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; +pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1386,7 +1386,7 @@ pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; +pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; /// Returns the largest integer less than or equal to an `f16`. /// diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6088fb6fa1786..7653db0bf7db4 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1659,7 +1659,8 @@ impl f128 { #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f128, b: f128) -> f128 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(self, a: f128, b: f128) -> f128 { intrinsics::fmaf128(self, a, b) } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 3f66c5973d4ac..58b4dfb4bdf84 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1634,7 +1634,8 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f16, b: f16) -> f16 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(self, a: f16, b: f16) -> f16 { intrinsics::fmaf16(self, a, b) } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index ebce89e5b3da5..6ff7d24726b55 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1799,7 +1799,8 @@ pub mod math { #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] - pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(x: f32, y: f32, z: f32) -> f32 { intrinsics::fmaf32(x, y, z) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 91e3949fc3900..f8529a5469bc8 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1797,7 +1797,8 @@ pub mod math { #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(x: f64, a: f64, b: f64) -> f64 { intrinsics::fmaf64(x, a, b) } diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index d31eba863f5fe..62278bf96c3c1 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -20,24 +20,6 @@ const TOL_PRECISE: f128 = 1e-28; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_mul_add() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_biteq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037); - assert_biteq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049); - assert_biteq!(0.0f128.mul_add(8.9, 1.2), 1.2); - assert_biteq!(3.4f128.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_biteq!(inf.mul_add(7.8, 9.0), inf); - assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_biteq!(8.9f128.mul_add(inf, 3.2), inf); - assert_biteq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); -} - #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_max_recip() { diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 302fd0861d75d..7ffafd467a519 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -22,24 +22,6 @@ const TOL_P4: f16 = 10.0; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_mul_add() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_biteq!(12.3f16.mul_add(4.5, 6.7), 62.031); - assert_biteq!((-12.3f16).mul_add(-4.5, -6.7), 48.625); - assert_biteq!(0.0f16.mul_add(8.9, 1.2), 1.2); - assert_biteq!(3.4f16.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_biteq!(inf.mul_add(7.8, 9.0), inf); - assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_biteq!(8.9f16.mul_add(inf, 3.2), inf); - assert_biteq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); -} - #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_max_recip() { diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs deleted file mode 100644 index a1fe8b076501d..0000000000000 --- a/library/coretests/tests/floats/f32.rs +++ /dev/null @@ -1,21 +0,0 @@ -use core::f32; - -use super::assert_biteq; - -// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ -#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] -#[test] -fn test_mul_add() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_biteq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); - assert_biteq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); - assert_biteq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); - assert_biteq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); - assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan()); - assert_biteq!(f32::math::mul_add(inf, 7.8, 9.0), inf); - assert_biteq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); - assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); - assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); -} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs deleted file mode 100644 index 4c5a3d68d1fd6..0000000000000 --- a/library/coretests/tests/floats/f64.rs +++ /dev/null @@ -1,21 +0,0 @@ -use core::f64; - -use super::assert_biteq; - -// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ -#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] -#[test] -fn test_mul_add() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_biteq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004); - assert_biteq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006); - assert_biteq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_biteq!(3.4f64.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_biteq!(inf.mul_add(7.8, 9.0), inf); - assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_biteq!(8.9f64.mul_add(inf, 3.2), inf); - assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); -} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 31515561c637b..9f52adcb6e200 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -34,6 +34,10 @@ trait TestableFloat: Sized { const RAW_12_DOT_5: Self; const RAW_1337: Self; const RAW_MINUS_14_DOT_25: Self; + /// The result of 12.3.mul_add(4.5, 6.7) + const MUL_ADD_RESULT: Self; + /// The result of (-12.3).mul_add(-4.5, -6.7) + const NEG_MUL_ADD_RESULT: Self; } impl TestableFloat for f16 { @@ -58,6 +62,8 @@ impl TestableFloat for f16 { const RAW_12_DOT_5: Self = Self::from_bits(0x4a40); const RAW_1337: Self = Self::from_bits(0x6539); const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20); + const MUL_ADD_RESULT: Self = 62.031; + const NEG_MUL_ADD_RESULT: Self = 48.625; } impl TestableFloat for f32 { @@ -84,6 +90,8 @@ impl TestableFloat for f32 { const RAW_12_DOT_5: Self = Self::from_bits(0x41480000); const RAW_1337: Self = Self::from_bits(0x44a72000); const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000); + const MUL_ADD_RESULT: Self = 62.05; + const NEG_MUL_ADD_RESULT: Self = 48.65; } impl TestableFloat for f64 { @@ -106,6 +114,8 @@ impl TestableFloat for f64 { const RAW_12_DOT_5: Self = Self::from_bits(0x4029000000000000); const RAW_1337: Self = Self::from_bits(0x4094e40000000000); const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000); + const MUL_ADD_RESULT: Self = 62.050000000000004; + const NEG_MUL_ADD_RESULT: Self = 48.650000000000006; } impl TestableFloat for f128 { @@ -128,6 +138,8 @@ impl TestableFloat for f128 { const RAW_12_DOT_5: Self = Self::from_bits(0x40029000000000000000000000000000); const RAW_1337: Self = Self::from_bits(0x40094e40000000000000000000000000); const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000); + const MUL_ADD_RESULT: Self = 62.0500000000000000000000000000000037; + const NEG_MUL_ADD_RESULT: Self = 48.6500000000000000000000000000000049; } /// Determine the tolerance for values of the argument type. @@ -359,8 +371,6 @@ macro_rules! float_test { mod f128; mod f16; -mod f32; -mod f64; float_test! { name: num, @@ -1542,3 +1552,28 @@ float_test! { assert_biteq!(Float::from_bits(masked_nan2), Float::from_bits(masked_nan2)); } } + +float_test! { + name: mul_add, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ + f32: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], + f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_biteq!(flt(12.3).mul_add(4.5, 6.7), Float::MUL_ADD_RESULT); + assert_biteq!((flt(-12.3)).mul_add(-4.5, -6.7), Float::NEG_MUL_ADD_RESULT); + assert_biteq!(flt(0.0).mul_add(8.9, 1.2), 1.2); + assert_biteq!(flt(3.4).mul_add(-0.0, 5.6), 5.6); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(flt(8.9).mul_add(inf, 3.2), inf); + assert_biteq!((flt(-3.2)).mul_add(2.4, neg_inf), neg_inf); + } +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 5c519f3a499d2..a80d7f8b44d7d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -20,6 +20,7 @@ #![feature(const_convert)] #![feature(const_destruct)] #![feature(const_eval_select)] +#![feature(const_mul_add)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 45abd6bca8a5b..233e41aa34535 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -332,6 +332,7 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_convert)] +#![feature(const_mul_add)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(drop_guard)] diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index ac1d889cc3731..e7810e77e76f0 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -217,7 +217,8 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn mul_add(self, a: f32, b: f32) -> f32 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(self, a: f32, b: f32) -> f32 { core::f32::math::mul_add(self, a, b) } diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 55c8593a0c0b1..cbebbfb1be166 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -217,7 +217,8 @@ impl f64 { #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn mul_add(self, a: f64, b: f64) -> f64 { + #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + pub const fn mul_add(self, a: f64, b: f64) -> f64 { core::f64::math::mul_add(self, a, b) } diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index b9c99f2859468..0cc4342f0d55d 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -1,4 +1,3 @@ -use rand::Rng; use rustc_apfloat::{self, Float, FloatConvert, Round}; use rustc_middle::mir; use rustc_middle::ty::{self, FloatTy}; @@ -39,46 +38,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "sqrtf64" => sqrt::(this, args, dest)?, "sqrtf128" => sqrt::(this, args, dest)?, - "fmaf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmaf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - - "fmuladdf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmuladdf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] | "fadd_fast" | "fsub_fast" diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 04c8bee72c038..d307636e782fc 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1293,6 +1293,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx.equal_float_min_max(a, b) } + #[inline(always)] + fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool { + ecx.machine.float_nondet && ecx.machine.rng.get_mut().random() + } + #[inline(always)] fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> { interp_ok(ecx.tcx.sess.ub_checks()) From a22334371cb7d3cf358c025772292d5b8f2b9519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 24 Sep 2025 21:49:12 +0200 Subject: [PATCH 0841/1900] Make install test target independent --- src/bootstrap/src/core/builder/tests.rs | 116 +++++++++++++----------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 87bea431a0588..e595ca5b28e1e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2857,63 +2857,69 @@ mod snapshot { // Using backslashes fails with `--set` "--set", &format!("install.prefix={}", ctx.dir().display()).replace("\\", "/"), "--set", &format!("install.sysconfdir={}", ctx.dir().display()).replace("\\", "/"), - "--set", "build.extended=true" + "--set", "build.extended=true", + // For Cranelift to be disted + "--build", "x86_64-unknown-linux-gnu", + "--host", "x86_64-unknown-linux-gnu" ]) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 0 -> WasmComponentLd 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [build] rustc 1 -> std 1 - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) - [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> WasmComponentLd 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] cargo (book) - [doc] clippy (book) - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> releases 2 - [build] rustc 0 -> RustInstaller 1 - [dist] docs - [dist] rustc 1 -> std 1 - [build] rustdoc 2 - [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 - [build] rustc 0 -> GenerateCopyright 1 - [dist] rustc - [build] rustc 1 -> cargo 2 - [dist] rustc 1 -> cargo 2 - [build] rustc 1 -> rust-analyzer 2 - [dist] rustc 1 -> rust-analyzer 2 - [build] rustc 1 -> rustfmt 2 - [build] rustc 1 -> cargo-fmt 2 - [dist] rustc 1 -> rustfmt 2 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 - [dist] rustc 1 -> clippy 2 - [build] rustc 1 -> miri 2 - [build] rustc 1 -> cargo-miri 2 - [dist] rustc 1 -> miri 2 + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> WasmComponentLd 1 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> WasmComponentLd 2 + [build] rustc 1 -> error-index 2 + [doc] rustc 1 -> error-index 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 + [build] rustc 0 -> RustInstaller 1 + [dist] docs + [dist] rustc 1 -> std 1 + [build] rustdoc 2 + [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 + [build] rustc 0 -> GenerateCopyright 1 + [dist] rustc + [build] rustc 1 -> cargo 2 + [dist] rustc 1 -> cargo 2 + [build] rustc 1 -> rust-analyzer 2 + [dist] rustc 1 -> rust-analyzer 2 + [build] rustc 1 -> rustfmt 2 + [build] rustc 1 -> cargo-fmt 2 + [dist] rustc 1 -> rustfmt 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [dist] rustc 1 -> clippy 2 + [build] rustc 1 -> miri 2 + [build] rustc 1 -> cargo-miri 2 + [dist] rustc 1 -> miri 2 [dist] src <> - [build] rustc 1 -> rustc_codegen_cranelift 2 - [dist] rustc 1 -> rustc_codegen_cranelift 2 - [build] rustc 1 -> LlvmBitcodeLinker 2 + [build] rustc 1 -> rustc_codegen_cranelift 2 + [dist] rustc 1 -> rustc_codegen_cranelift 2 + [build] rustc 1 -> LlvmBitcodeLinker 2 "); } From 92859e98ee1a2101df8322a8581e4d638eb15078 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 24 Sep 2025 20:32:50 +0100 Subject: [PATCH 0842/1900] Repro duration_since regression from issue 146228 --- library/std/src/sys/pal/hermit/time.rs | 11 +++++++++-- library/std/src/sys/pal/unix/time.rs | 27 ++++++++++++-------------- library/std/tests/time.rs | 16 +++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index f76a5f96c8750..bd6fd5a3de428 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -26,15 +26,22 @@ impl Timespec { } fn sub_timespec(&self, other: &Timespec) -> Result { + fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { + debug_assert!(a >= b); + a.wrapping_sub(b).cast_unsigned() + } + if self >= other { + // Logic here is identical to Unix version of `Timestamp::sub_timespec`, + // check comments there why operations do not overflow. Ok(if self.t.tv_nsec >= other.t.tv_nsec { Duration::new( - (self.t.tv_sec - other.t.tv_sec) as u64, + sub_ge_to_unsigned(self.t.tv_sec, other.t.tv_sec), (self.t.tv_nsec - other.t.tv_nsec) as u32, ) } else { Duration::new( - (self.t.tv_sec - 1 - other.t.tv_sec) as u64, + sub_ge_to_unsigned(self.t.tv_sec - 1, other.t.tv_sec), (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, ) }) diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index bd7f74fea6a9c..c207f41cad4b5 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -134,28 +134,25 @@ impl Timespec { } pub fn sub_timespec(&self, other: &Timespec) -> Result { + // When a >= b, the difference fits in u64. + fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { + debug_assert!(a >= b); + a.wrapping_sub(b).cast_unsigned() + } + if self >= other { - // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM - // to optimize it into a branchless form (see also #75545): - // - // 1. `self.tv_sec - other.tv_sec` shows up as a common expression - // in both branches, i.e. the `else` must have its `- 1` - // subtraction after the common one, not interleaved with it - // (it used to be `self.tv_sec - 1 - other.tv_sec`) - // - // 2. the `Duration::new` call (or any other additional complexity) - // is outside of the `if`-`else`, not duplicated in both branches - // - // Ideally this code could be rearranged such that it more - // directly expresses the lower-cost behavior we want from it. let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { ( - (self.tv_sec - other.tv_sec) as u64, + sub_ge_to_unsigned(self.tv_sec, other.tv_sec), self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), ) } else { + // Following sequence of assertions explain why `self.tv_sec - 1` does not underflow. + debug_assert!(self.tv_nsec < other.tv_nsec); + debug_assert!(self.tv_sec > other.tv_sec); + debug_assert!(self.tv_sec > i64::MIN); ( - (self.tv_sec - other.tv_sec - 1) as u64, + sub_ge_to_unsigned(self.tv_sec - 1, other.tv_sec), self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index 40709eae37cfc..be1948af91564 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -227,3 +227,19 @@ fn big_math() { check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); } + +#[test] +#[cfg(unix)] +fn system_time_duration_since_max_range_on_unix() { + // Repro regression https://github.com/rust-lang/rust/issues/146228 + + // Min and max values of `SystemTime` on Unix. + let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0)); + let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999)); + + let delta_a = max.duration_since(min).expect("duration_since overflow"); + let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration(); + + assert_eq!(Duration::MAX, delta_a); + assert_eq!(Duration::MAX, delta_b); +} From 43057698c12e8ceab1f49eff8a3e5a38cc05f7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 24 Sep 2025 21:06:54 +0000 Subject: [PATCH 0843/1900] Tweak handling of "struct like start" where a struct isn't supported This improves the case where someone tries to write a `match` expr where the patterns have type ascription syntax. Makes them less verbose, by giving up on the first encounter in the block, and makes them more accurate by only treating them as a struct literal if successfuly parsed as such. --- .../rustc_parse/src/parser/diagnostics.rs | 40 ++++------ compiler/rustc_parse/src/parser/expr.rs | 61 +++++++++----- .../issues/issue-87086-colon-path-sep.rs | 3 +- .../issues/issue-87086-colon-path-sep.stderr | 20 ++--- tests/ui/parser/type-ascription-in-pattern.rs | 17 ++-- .../parser/type-ascription-in-pattern.stderr | 80 +++++-------------- 6 files changed, 93 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a28af7833c387..a9fbd0fa33d5f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> { if token::Colon != self.token.kind { return first_pat; } - if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..)) - || !self.look_ahead(1, |token| token.is_non_reserved_ident()) - { - let mut snapshot_type = self.create_snapshot_for_diagnostic(); - snapshot_type.bump(); // `:` - match snapshot_type.parse_ty() { - Err(inner_err) => { - inner_err.cancel(); - } - Ok(ty) => { - let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else { - return first_pat; - }; - err.span_label(ty.span, "specifying the type of a pattern isn't supported"); - self.restore_snapshot(snapshot_type); - let span = first_pat.span.to(ty.span); - first_pat = self.mk_pat(span, PatKind::Wild); - err.emit(); - } - } - return first_pat; - } + // The pattern looks like it might be a path with a `::` -> `:` typo: // `match foo { bar:baz => {} }` let colon_span = self.token.span; @@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } else { - first_pat = self.mk_pat(new_span, PatKind::Wild); + first_pat = self.mk_pat( + new_span, + PatKind::Err( + self.dcx() + .span_delayed_bug(colon_span, "recovered bad path pattern"), + ), + ); } self.restore_snapshot(snapshot_pat); } @@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> { err.span_label(ty.span, "specifying the type of a pattern isn't supported"); self.restore_snapshot(snapshot_type); let new_span = first_pat.span.to(ty.span); - first_pat = self.mk_pat(new_span, PatKind::Wild); + first_pat = + self.mk_pat( + new_span, + PatKind::Err(self.dcx().span_delayed_bug( + colon_span, + "recovered bad pattern with type", + )), + ); } } err.emit(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 81a5d48d94e8c..2f4158450d838 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3612,36 +3612,55 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1) } - fn is_certainly_not_a_block(&self) -> bool { - // `{ ident, ` and `{ ident: ` cannot start a block. - self.look_ahead(1, |t| t.is_ident()) - && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon) - } - fn maybe_parse_struct_expr( &mut self, qself: &Option>, path: &ast::Path, ) -> Option>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); - if struct_allowed || self.is_certainly_not_a_block() { - if let Err(err) = self.expect(exp!(OpenBrace)) { - return Some(Err(err)); + let is_ident = self.look_ahead(1, |t| t.is_ident()); + let is_comma = self.look_ahead(2, |t| t == &token::Comma); + let is_colon = self.look_ahead(2, |t| t == &token::Colon); + match (struct_allowed, is_ident, is_comma, is_colon) { + (false, true, true, _) | (false, true, _, true) => { + // We have something like `match foo { bar,` or `match foo { bar:`, which means the + // user might have meant to write a struct literal as part of the `match` + // discriminant. + let snapshot = self.create_snapshot_for_diagnostic(); + if let Err(err) = self.expect(exp!(OpenBrace)) { + return Some(Err(err)); + } + match self.parse_expr_struct(qself.clone(), path.clone(), false) { + Ok(expr) => { + // This is a struct literal, but we don't accept them here. + self.dcx().emit_err(errors::StructLiteralNotAllowedHere { + span: expr.span, + sub: errors::StructLiteralNotAllowedHereSugg { + left: path.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + }); + Some(Ok(expr)) + } + Err(err) => { + // We couldn't parse a valid struct, rollback and let the parser emit an + // error elsewhere. + err.cancel(); + self.restore_snapshot(snapshot); + None + } + } } - let expr = self.parse_expr_struct(qself.clone(), path.clone(), true); - if let (Ok(expr), false) = (&expr, struct_allowed) { - // This is a struct literal, but we don't can't accept them here. - self.dcx().emit_err(errors::StructLiteralNotAllowedHere { - span: expr.span, - sub: errors::StructLiteralNotAllowedHereSugg { - left: path.span.shrink_to_lo(), - right: expr.span.shrink_to_hi(), - }, - }); + (true, _, _, _) => { + // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for + // any kind of recovery. + if let Err(err) = self.expect(exp!(OpenBrace)) { + return Some(Err(err)); + } + Some(self.parse_expr_struct(qself.clone(), path.clone(), true)) } - return Some(expr); + (false, _, _, _) => None, } - None } pub(super) fn parse_struct_fields( diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs index d081c06044f14..e1ea38f2795df 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs @@ -37,10 +37,9 @@ fn g1() { //~| HELP: maybe write a path separator here _ => {} } - if let Foo:Bar = f() { //~ WARN: irrefutable `if let` pattern + if let Foo:Bar = f() { //~^ ERROR: expected one of //~| HELP: maybe write a path separator here - //~| HELP: consider replacing the `if let` with a `let` } } diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index a9bad96f9af76..061586882e0c9 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -64,7 +64,7 @@ LL | if let Foo::Bar = f() { | + error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:49:16 + --> $DIR/issue-87086-colon-path-sep.rs:48:16 | LL | ref qux: Foo::Baz => {} | ^ -------- specifying the type of a pattern isn't supported @@ -77,7 +77,7 @@ LL | ref qux::Foo::Baz => {} | ~~ error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:58:16 + --> $DIR/issue-87086-colon-path-sep.rs:57:16 | LL | mut qux: Foo::Baz => {} | ^ -------- specifying the type of a pattern isn't supported @@ -90,7 +90,7 @@ LL | mut qux::Foo::Baz => {} | ~~ error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:69:12 + --> $DIR/issue-87086-colon-path-sep.rs:68:12 | LL | Foo:Bar::Baz => {} | ^-------- specifying the type of a pattern isn't supported @@ -103,7 +103,7 @@ LL | Foo::Bar::Baz => {} | + error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:75:12 + --> $DIR/issue-87086-colon-path-sep.rs:74:12 | LL | Foo:Bar => {} | ^--- specifying the type of a pattern isn't supported @@ -115,15 +115,5 @@ help: maybe write a path separator here LL | Foo::Bar => {} | + -warning: irrefutable `if let` pattern - --> $DIR/issue-87086-colon-path-sep.rs:40:8 - | -LL | if let Foo:Bar = f() { - | ^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match, so the `if let` is useless - = help: consider replacing the `if let` with a `let` - = note: `#[warn(irrefutable_let_patterns)]` on by default - -error: aborting due to 9 previous errors; 1 warning emitted +error: aborting due to 9 previous errors diff --git a/tests/ui/parser/type-ascription-in-pattern.rs b/tests/ui/parser/type-ascription-in-pattern.rs index 18d7061d69c8d..75059d33db645 100644 --- a/tests/ui/parser/type-ascription-in-pattern.rs +++ b/tests/ui/parser/type-ascription-in-pattern.rs @@ -1,15 +1,16 @@ fn foo(x: bool) -> i32 { - match x { //~ ERROR struct literals are not allowed here - x: i32 => x, //~ ERROR expected - true => 42., //~ ERROR expected identifier - false => 0.333, //~ ERROR expected identifier + match x { + x: i32 => x, //~ ERROR: expected + //~^ ERROR: mismatched types + true => 42., + false => 0.333, } -} //~ ERROR expected one of +} fn main() { match foo(true) { - 42: i32 => (), //~ ERROR expected - _: f64 => (), //~ ERROR expected - x: i32 => (), //~ ERROR expected + 42: i32 => (), //~ ERROR: expected + _: f64 => (), //~ ERROR: expected + x: i32 => (), //~ ERROR: expected } } diff --git a/tests/ui/parser/type-ascription-in-pattern.stderr b/tests/ui/parser/type-ascription-in-pattern.stderr index 135879f208b2b..0919075499368 100644 --- a/tests/ui/parser/type-ascription-in-pattern.stderr +++ b/tests/ui/parser/type-ascription-in-pattern.stderr @@ -1,64 +1,18 @@ -error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>` - --> $DIR/type-ascription-in-pattern.rs:3:16 - | -LL | match x { - | - while parsing this struct -LL | x: i32 => x, - | -^^ expected one of 8 possible tokens - | | - | help: try adding a comma: `,` - -error: expected identifier, found keyword `true` - --> $DIR/type-ascription-in-pattern.rs:4:9 - | -LL | match x { - | - while parsing this struct -LL | x: i32 => x, -LL | true => 42., - | ^^^^ expected identifier, found keyword - -error: expected identifier, found keyword `false` - --> $DIR/type-ascription-in-pattern.rs:5:9 - | -LL | match x { - | - while parsing this struct -... -LL | false => 0.333, - | ^^^^^ expected identifier, found keyword - -error: struct literals are not allowed here - --> $DIR/type-ascription-in-pattern.rs:2:11 - | -LL | match x { - | ___________^ -LL | | x: i32 => x, -LL | | true => 42., -LL | | false => 0.333, -LL | | } - | |_____^ - | -help: surround the struct literal with parentheses +error: expected one of `@` or `|`, found `:` + --> $DIR/type-ascription-in-pattern.rs:3:10 | -LL ~ match (x { LL | x: i32 => x, -LL | true => 42., -LL | false => 0.333, -LL ~ }) + | ^ --- specifying the type of a pattern isn't supported + | | + | expected one of `@` or `|` | - -error: expected one of `.`, `?`, `{`, or an operator, found `}` - --> $DIR/type-ascription-in-pattern.rs:7:1 +help: maybe write a path separator here | -LL | match x { - | ----- while parsing this `match` expression -... -LL | } - | - expected one of `.`, `?`, `{`, or an operator -LL | } - | ^ unexpected token +LL | x::i32 => x, + | ~~ error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:11:11 + --> $DIR/type-ascription-in-pattern.rs:12:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -66,7 +20,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:12:10 + --> $DIR/type-ascription-in-pattern.rs:13:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -74,7 +28,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:13:10 + --> $DIR/type-ascription-in-pattern.rs:14:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -86,5 +40,15 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error: aborting due to 8 previous errors +error[E0308]: mismatched types + --> $DIR/type-ascription-in-pattern.rs:3:19 + | +LL | fn foo(x: bool) -> i32 { + | --- expected `i32` because of return type +LL | match x { +LL | x: i32 => x, + | ^ expected `i32`, found `bool` + +error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0308`. From aa75d340353dd07bf347978360a9566831128e35 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 24 Sep 2025 23:47:05 +0200 Subject: [PATCH 0844/1900] Small string formatting cleanup --- compiler/rustc_interface/src/util.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 26e09c95e7698..76ccd12797e5e 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -386,8 +386,8 @@ fn get_codegen_sysroot( .collect::>() .join("\n* "); let err = format!( - "failed to find a `codegen-backends` folder \ - in the sysroot candidates:\n* {candidates}" + "failed to find a `codegen-backends` folder in the sysroot candidates:\n\ + * {candidates}" ); early_dcx.early_fatal(err); }); @@ -396,10 +396,8 @@ fn get_codegen_sysroot( let d = sysroot.read_dir().unwrap_or_else(|e| { let err = format!( - "failed to load default codegen backend, couldn't \ - read `{}`: {}", + "failed to load default codegen backend, couldn't read `{}`: {e}", sysroot.display(), - e ); early_dcx.early_fatal(err); }); From 852da23a2d06de5a7821b1fdb356fe3a410b2d00 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 24 Sep 2025 15:56:38 -0700 Subject: [PATCH 0845/1900] Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`. Although the examples below this list do imply that there's an impl of `ToSocketAddrs` for `&[SocketAddr]`, it's not actually noted in the list of default implementations. --- library/std/src/net/socket_addr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 5b56dd3f74472..8214ad381f1f7 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -28,6 +28,8 @@ use crate::{io, iter, option, slice, vec}; /// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like /// `:` pair where `` is a [`u16`] value. /// +/// * &[[SocketAddr]]: all [`SocketAddr`] values in the slice will be used. +/// /// This trait allows constructing network objects like [`TcpStream`] or /// [`UdpSocket`] easily with values of various types for the bind/connection /// address. It is needed because sometimes one type is more appropriate than From 16460273e0b980b8b34213eac9426066777a520b Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Wed, 24 Sep 2025 18:47:36 -0500 Subject: [PATCH 0846/1900] fix SCIP panicking due to salsa not attaching --- src/tools/rust-analyzer/crates/ide/src/static_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 8214b4d1de22f..9911b85799b62 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -278,7 +278,7 @@ impl StaticIndex<'_> { for token in tokens { let range = token.text_range(); let node = token.parent().unwrap(); - match get_definitions(&sema, token.clone()) { + match salsa::attach(self.db, || get_definitions(&sema, token.clone())) { Some(it) => { for i in it { add_token(i, range, &node); From fe440ec934995e499360dc05ae485f1ccbd0e694 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Sep 2025 16:53:17 -0700 Subject: [PATCH 0847/1900] llvm: add a destructor to call releaseSerializer --- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index ef85ce153b88c..9953f5e173168 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1685,6 +1685,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( RemarkStreamer(std::move(RemarkStreamer)), LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {} +#if LLVM_VERSION_GE(22, 0) + ~RustDiagnosticHandler() { + if (RemarkStreamer) { + RemarkStreamer->releaseSerializer(); + } + } +#endif + virtual bool handleDiagnostics(const DiagnosticInfo &DI) override { // If this diagnostic is one of the optimization remark kinds, we can // check if it's enabled before emitting it. This can avoid many From 137d4eaf12139af894da6c0b6fe74195d6b7b98c Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 20 Sep 2025 16:54:51 -0700 Subject: [PATCH 0848/1900] BTreeMap: Don't leak allocators when initializing nodes Memory was allocated via `Box::leak` and thence intended to be tracked and deallocated manually, but the allocator was also leaked, not tracked, and never dropped. Now it is dropped immediately. According to my reading of the `Allocator` trait, if a copy of the allocator remains live, then its allocations must remain live. Since the B-tree has a copy of the allocator that will only be dropped after the nodes, it's safe to not store the allocator in each node (which would be a much more intrusive change). --- library/alloc/src/collections/btree/map.rs | 3 +++ library/alloc/src/collections/btree/node.rs | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index cebd25c6039cc..e3b53b2fe291a 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -194,6 +194,9 @@ pub struct BTreeMap< root: Option>, length: usize, /// `ManuallyDrop` to control drop order (needs to be dropped after all the nodes). + // Although some of the accessory types store a copy of the allocator, the nodes do not. + // Because allocations will remain live as long as any copy (like this one) of the allocator + // is live, it's unnecessary to store the allocator in each node. pub(super) alloc: ManuallyDrop, // For dropck; the `Box` avoids making the `Unpin` impl more strict than before _marker: PhantomData>, diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index b233e1740b7b7..77bd90f2c750c 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -224,7 +224,11 @@ impl NodeRef { } fn from_new_leaf(leaf: Box, A>) -> Self { - NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData } + // The allocator must be dropped, not leaked. See also `BTreeMap::alloc`. + let (leaf, _alloc) = Box::into_raw_with_allocator(leaf); + // SAFETY: the node was just allocated. + let node = unsafe { NonNull::new_unchecked(leaf) }; + NodeRef { height: 0, node, _marker: PhantomData } } } @@ -242,7 +246,11 @@ impl NodeRef { height: usize, ) -> Self { debug_assert!(height > 0); - let node = NonNull::from(Box::leak(internal)).cast(); + // The allocator must be dropped, not leaked. See also `BTreeMap::alloc`. + let (internal, _alloc) = Box::into_raw_with_allocator(internal); + // SAFETY: the node was just allocated. + let internal = unsafe { NonNull::new_unchecked(internal) }; + let node = internal.cast(); let mut this = NodeRef { height, node, _marker: PhantomData }; this.borrow_mut().correct_all_childrens_parent_links(); this From 3280c210dd6e5f26595ff4857572f8feba678992 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 25 Sep 2025 04:11:22 +0000 Subject: [PATCH 0849/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to caccb4d0368bd918ef6668af8e13834d07040417. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 02b217f7d80dc..8854fb95997bd 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -21a19c297d4f5a03501d92ca251bd7a17073c08a +caccb4d0368bd918ef6668af8e13834d07040417 From bc37dd4a724fc3e3043a46f488775dbe1bfd9649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 24 Sep 2025 15:48:34 +0200 Subject: [PATCH 0850/1900] Remove an erroneous normalization step in `tests/run-make/linker-warning` --- tests/run-make/linker-warning/rmake.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index 9ea706af50356..b0c40dd171d34 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -61,7 +61,6 @@ fn main() { diff() .expected_file("short-error.txt") .actual_text("(linker error)", out.stderr()) - .normalize(r#"/rustc[^/_-]*/"#, "/rustc/") .normalize("libpanic_abort", "libpanic_unwind") .normalize( regex::escape( From 61792afab930c9726e11bf838999bfa1e5f6caad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 25 Sep 2025 10:19:23 +0300 Subject: [PATCH 0851/1900] Also install rustfmt on stable --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 770652494f4a8..e87ece5b6524c 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -98,9 +98,9 @@ jobs: run: | rustup update --no-self-update stable rustup default stable - rustup component add --toolchain stable rust-src clippy - # We always use a nightly rustfmt, regardless of channel, because we need - # --file-lines. + rustup component add --toolchain stable rust-src clippy rustfmt + # We also install a nightly rustfmt, because we use `--file-lines` in + # a test. rustup toolchain install nightly --profile minimal --component rustfmt # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - name: Install Rust Problem Matcher From 932f3a8ecd0158fcb70a54c8591dec70e43b53d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 25 Sep 2025 09:29:10 +0200 Subject: [PATCH 0852/1900] rustdoc: Fix documentation for `--doctest-build-arg` --- src/doc/rustdoc/src/unstable-features.md | 26 +++++------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 25c929a1dbada..4327a80cd0832 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -739,7 +739,7 @@ This flag can be passed multiple times to nest wrappers. ## Passing arguments to rustc when compiling doctests -You can use the `--doctest-compilation-args` flag if you want to add options when compiling the +You can use the `--doctest-build-arg` flag if you want to add options when compiling the doctest. For example if you have: ```rust,no_run @@ -784,10 +784,10 @@ failures: test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s ``` -But if you can limit the lint level to warning by using `--doctest_compilation_args=--cap-lints=warn`: +But if you can limit the lint level to warning by using `--doctest-build-arg=--cap-lints=warn`: ```console -$ rustdoc --test --doctest_compilation_args=--cap-lints=warn file.rs +$ rustdoc --test --doctest-build-arg=--cap-lints=warn file.rs running 1 test test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok @@ -795,24 +795,8 @@ test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s ``` -The parsing of arguments works as follows: if it encounters a `"` or a `'`, it will continue -until it finds the character unescaped (without a prepending `\`). If not inside a string, a -whitespace character will also split arguments. Example: - -```text -"hello 'a'\" ok" how are 'you today?' -``` - -will be split as follows: - -```text -[ - "hello 'a'\" ok", - "how", - "are", - "you today?", -] -``` +In order to pass multiple arguments to the underlying compiler, +pass `--doctest-build-arg ARG` for each argument `ARG`. ## `--generate-macro-expansion`: Generate macros expansion toggles in source code From 13d512f2d57a4ad1b97cb0f543b3d85c4cbd1962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 25 Sep 2025 10:54:58 +0300 Subject: [PATCH 0853/1900] Install cargo for proc-macro-srv tests --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index e87ece5b6524c..0eb57a605f828 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -56,8 +56,8 @@ jobs: # Install a pinned rustc commit to avoid surprises - name: Install Rust toolchain run: | - RUSTC_VERSION=`cat rust-version` - rustup-toolchain-install-master ${RUSTC_VERSION} -c rust-src -c rustfmt + RUSTC_VERSION=$(cat rust-version) + rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt rustup default ${RUSTC_VERSION} # Emulate a nightly toolchain, because the toolchain installed above does not have "nightly" From d226e7aa93425ba2090f423642341a99ab047838 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 19 Sep 2025 14:53:16 +0200 Subject: [PATCH 0854/1900] Use standard attribute logic for allocator shim Use llfn_attrs_from_instance() to generate the attributes for the allocator shim. This ensures that we generate all the usual attributes (and don't get to find out one-by-one that a certain attribute is important for a certain target). Additionally this will enable emitting the allocator-specific attributes (not included here). This change is quite awkward because the allocator shim uses SimpleCx, while llfn_attrs_from_instance uses CodegenCx. I've switched it to use SimpleCx plus tcx/sess arguments where necessary. If there's a simpler way to do this, I'd love to know about it... --- compiler/rustc_codegen_llvm/src/abi.rs | 8 +- compiler/rustc_codegen_llvm/src/allocator.rs | 25 +-- compiler/rustc_codegen_llvm/src/attributes.rs | 191 ++++++++++-------- compiler/rustc_codegen_llvm/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 10 +- compiler/rustc_codegen_llvm/src/declare.rs | 2 +- 7 files changed, 126 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 11be704116790..861227f7c2a70 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -538,7 +538,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { // If the declaration has an associated instance, compute extra attributes based on that. if let Some(instance) = instance { - llfn_attrs_from_instance(cx, llfn, instance); + llfn_attrs_from_instance( + cx, + cx.tcx, + llfn, + &cx.tcx.codegen_instance_attrs(instance.def), + Some(instance), + ); } } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index df3e49279d976..abd6312039733 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -5,15 +5,16 @@ use rustc_ast::expand::allocator::{ }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; use rustc_symbol_mangling::mangle_internal_symbol; -use smallvec::SmallVec; +use crate::attributes::llfn_attrs_from_instance; use crate::builder::SBuilder; use crate::declare::declare_simple_fn; use crate::llvm::{self, FALSE, TRUE, Type, Value}; -use crate::{SimpleCx, attributes, debuginfo, llvm_util}; +use crate::{SimpleCx, attributes, debuginfo}; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, @@ -149,18 +150,8 @@ fn create_wrapper_function( ty, ); - let mut attrs = SmallVec::<[_; 2]>::new(); - - let target_cpu = llvm_util::target_cpu(tcx.sess); - let target_cpu_attr = llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu); - - let tune_cpu_attr = llvm_util::tune_cpu(tcx.sess) - .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)); - - attrs.push(target_cpu_attr); - attrs.extend(tune_cpu_attr); - - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); + let attrs = CodegenFnAttrs::new(); + llfn_attrs_from_instance(cx, tcx, llfn, &attrs, None); let no_return = if no_return { // -> ! DIFlagNoReturn @@ -171,12 +162,6 @@ fn create_wrapper_function( None }; - if tcx.sess.must_emit_unwind_tables() { - let uwtable = - attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); - } - let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) }; let mut bx = SBuilder::build(&cx, llbb); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index dcf6b9454978c..8070ea0b3e927 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,20 +1,21 @@ //! Set and unset common attributes on LLVM values. -use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; +use rustc_middle::middle::codegen_fn_attrs::{ + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; -use crate::context::CodegenCx; +use crate::context::SimpleCx; use crate::errors::SanitizerMemtagRequiresMte; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects}; use crate::value::Value; -use crate::{attributes, llvm_util}; +use crate::{Session, attributes, llvm_util}; pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { if !attrs.is_empty() { @@ -30,18 +31,19 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[ /// Get LLVM attribute for the provided inline heuristic. pub(crate) fn inline_attr<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, ) -> Option<&'ll Attribute> { // `optnone` requires `noinline` - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, + (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, (inline, _) => inline, }; - if !cx.tcx.sess.opts.unstable_opts.inline_llvm { + if !tcx.sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); } @@ -51,7 +53,7 @@ pub(crate) fn inline_attr<'ll, 'tcx>( Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) } InlineAttr::Never => { - if cx.sess().target.arch != "amdgpu" { + if tcx.sess.target.arch != "amdgpu" { Some(AttributeKind::NoInline.create_attr(cx.llcx)) } else { None @@ -63,12 +65,13 @@ pub(crate) fn inline_attr<'ll, 'tcx>( #[inline] fn patchable_function_entry_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, + cx: &SimpleCx<'ll>, + sess: &Session, attr: Option, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry) + PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) }); let entry = patchable_spec.entry(); let prefix = patchable_spec.prefix(); @@ -91,12 +94,13 @@ fn patchable_function_entry_attrs<'ll>( /// Get LLVM sanitize attributes. #[inline] -pub(crate) fn sanitize_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn sanitize_attrs<'ll, 'tcx>( + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, no_sanitize: SanitizerSet, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; + let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -114,11 +118,11 @@ pub(crate) fn sanitize_attrs<'ll>( } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. - let features = cx.tcx.global_backend_features(()); + let features = tcx.global_backend_features(()); let mte_feature = features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..])); if let None | Some("-mte") = mte_feature { - cx.tcx.dcx().emit_err(SanitizerMemtagRequiresMte); + tcx.dcx().emit_err(SanitizerMemtagRequiresMte); } attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx)); @@ -139,9 +143,12 @@ pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option) llvm::CreateUWTableAttr(llcx, async_unwind) } -pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let mut fp = cx.sess().target.frame_pointer; - let opts = &cx.sess().opts; +pub(crate) fn frame_pointer_type_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> Option<&'ll Attribute> { + let mut fp = sess.target.frame_pointer; + let opts = &sess.opts; // "mcount" function relies on stack pointer. // See . if opts.unstable_opts.instrument_mcount { @@ -156,8 +163,8 @@ pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&' Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value)) } -fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let function_return_attr = match cx.sess().opts.unstable_opts.function_return { +fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + let function_return_attr = match sess.opts.unstable_opts.function_return { FunctionReturn::Keep => return None, FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern, }; @@ -167,17 +174,20 @@ fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> /// Tell LLVM what instrument function to insert. #[inline] -fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> { +fn instrument_function_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - if cx.sess().opts.unstable_opts.instrument_mcount { + if sess.opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. - let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic { + let mcount_name = match &sess.target.llvm_mcount_intrinsic { Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), - None => cx.sess().target.mcount.as_ref(), + None => sess.target.mcount.as_ref(), }; attrs.push(llvm::CreateAttrStringValue( @@ -186,7 +196,7 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr mcount_name, )); } - if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray { + if let Some(options) = &sess.opts.unstable_opts.instrument_xray { // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. // Function prologue and epilogue are instrumented with NOP sleds, // a runtime library later replaces them with detours into tracing code. @@ -217,20 +227,20 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr attrs } -fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - if !cx.sess().opts.unstable_opts.no_jump_tables { +fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + if !sess.opts.unstable_opts.no_jump_tables { return None; } Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true")) } -fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&'ll Attribute> { // Currently stack probes seem somewhat incompatible with the address // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. - if cx - .sess() + if tcx + .sess .opts .unstable_opts .sanitizer @@ -240,22 +250,22 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { } // probestack doesn't play nice either with `-C profile-generate`. - if cx.sess().opts.cg.profile_generate.enabled() { + if tcx.sess.opts.cg.profile_generate.enabled() { return None; } - let attr_value = match cx.sess().target.stack_probes { + let attr_value = match tcx.sess.target.stack_probes { StackProbeType::None => return None, // Request LLVM to generate the probes inline. If the given LLVM version does not support // this, no probe is generated at all (even if the attribute is specified). StackProbeType::Inline => "inline-asm", // Flag our internal `__rust_probestack` function as the stack probe symbol. // This is defined in the `compiler-builtins` crate for each architecture. - StackProbeType::Call => &mangle_internal_symbol(cx.tcx, "__rust_probestack"), + StackProbeType::Call => &mangle_internal_symbol(tcx, "__rust_probestack"), // Pick from the two above based on the LLVM version. StackProbeType::InlineOrCall { min_llvm_version_for_inline } => { if llvm_util::get_version() < min_llvm_version_for_inline { - &mangle_internal_symbol(cx.tcx, "__rust_probestack") + &mangle_internal_symbol(tcx, "__rust_probestack") } else { "inline-asm" } @@ -264,8 +274,8 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value)) } -fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let sspattr = match cx.sess().stack_protector() { +fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + let sspattr = match sess.stack_protector() { StackProtector::None => return None, StackProtector::All => AttributeKind::StackProtectReq, StackProtector::Strong => AttributeKind::StackProtectStrong, @@ -275,33 +285,34 @@ fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { Some(sspattr.create_attr(cx.llcx)) } -fn backchain_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - if cx.sess().target.arch != "s390x" { +fn backchain_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + if sess.target.arch != "s390x" { return None; } - let requested_features = cx.sess().opts.cg.target_feature.split(','); + let requested_features = sess.opts.cg.target_feature.split(','); let found_positive = requested_features.clone().any(|r| r == "+backchain"); if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None } } -pub(crate) fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute { - let target_cpu = llvm_util::target_cpu(cx.tcx.sess); +pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute { + let target_cpu = llvm_util::target_cpu(sess); llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu) } -pub(crate) fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - llvm_util::tune_cpu(cx.tcx.sess) +pub(crate) fn tune_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + llvm_util::tune_cpu(sess) .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)) } /// Get the `target-features` LLVM attribute. -pub(crate) fn target_features_attr<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn target_features_attr<'ll, 'tcx>( + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, function_features: Vec, ) -> Option<&'ll Attribute> { - let global_features = cx.tcx.global_backend_features(()).iter().map(String::as_str); + let global_features = tcx.global_backend_features(()).iter().map(String::as_str); let function_features = function_features.iter().map(String::as_str); let target_features = global_features.chain(function_features).intersperse(",").collect::(); @@ -311,22 +322,22 @@ pub(crate) fn target_features_attr<'ll>( /// Get the `NonLazyBind` LLVM attribute, /// if the codegen options allow skipping the PLT. -pub(crate) fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +pub(crate) fn non_lazy_bind_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> Option<&'ll Attribute> { // Don't generate calls through PLT if it's not necessary - if !cx.sess().needs_plt() { - Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) - } else { - None - } + if !sess.needs_plt() { Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) } else { None } } /// Get the default optimizations attrs for a function. #[inline] pub(crate) fn default_optimisation_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, + cx: &SimpleCx<'ll>, + sess: &Session, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - match cx.sess().opts.optimize { + match sess.opts.optimize { OptLevel::Size => { attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); } @@ -347,17 +358,18 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, llfn: &'ll Value, - instance: ty::Instance<'tcx>, + codegen_fn_attrs: &CodegenFnAttrs, + instance: Option>, ) { - let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); - + let sess = tcx.sess; let mut to_add = SmallVec::<[_; 16]>::new(); match codegen_fn_attrs.optimize { OptimizeAttr::Default => { - to_add.extend(default_optimisation_attrs(cx)); + to_add.extend(default_optimisation_attrs(cx, sess)); } OptimizeAttr::DoNotOptimize => { to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx)); @@ -369,21 +381,21 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - if cx.sess().must_emit_unwind_tables() { - to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind)); + if sess.must_emit_unwind_tables() { + to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind)); } - if cx.sess().opts.unstable_opts.profile_sample_use.is_some() { + if sess.opts.unstable_opts.profile_sample_use.is_some() { to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile")); } // FIXME: none of these functions interact with source level attributes. - to_add.extend(frame_pointer_type_attr(cx)); - to_add.extend(function_return_attr(cx)); - to_add.extend(instrument_function_attr(cx)); - to_add.extend(nojumptables_attr(cx)); - to_add.extend(probestack_attr(cx)); - to_add.extend(stackprotector_attr(cx)); + to_add.extend(frame_pointer_type_attr(cx, sess)); + to_add.extend(function_return_attr(cx, sess)); + to_add.extend(instrument_function_attr(cx, sess)); + to_add.extend(nojumptables_attr(cx, sess)); + to_add.extend(probestack_attr(cx, tcx)); + to_add.extend(stackprotector_attr(cx, sess)); if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) { to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins")); @@ -404,13 +416,13 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // not used. } else { // Do not set sanitizer attributes for naked functions. - to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); + to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize)); // For non-naked functions, set branch protection attributes on aarch64. if let Some(BranchProtection { bti, pac_ret, gcs }) = - cx.sess().opts.unstable_opts.branch_protection + sess.opts.unstable_opts.branch_protection { - assert!(cx.sess().target.arch == "aarch64"); + assert!(sess.target.arch == "aarch64"); if bti { to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); } @@ -438,14 +450,15 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) { to_add.push(create_alloc_family_attr(cx.llcx)); - if let Some(zv) = - cx.tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) + if let Some(instance) = instance + && let Some(zv) = + tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) && let Some(name) = zv.value_str() { to_add.push(llvm::CreateAttrStringValue( cx.llcx, "alloc-variant-zeroed", - &mangle_internal_symbol(cx.tcx, name.as_str()), + &mangle_internal_symbol(tcx, name.as_str()), )); } // apply to argument place instead of function @@ -490,18 +503,22 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } - if let Some(backchain) = backchain_attr(cx) { + if let Some(backchain) = backchain_attr(cx, sess) { to_add.push(backchain); } - to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry)); + to_add.extend(patchable_function_entry_attrs( + cx, + sess, + codegen_fn_attrs.patchable_function_entry, + )); // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated // functions (because Clang annotates functions this way too). - to_add.push(target_cpu_attr(cx)); + to_add.push(target_cpu_attr(cx, sess)); // tune-cpu is only conveyed through the attribute for our purpose. // The target doesn't care; the subtarget reads our attribute. - to_add.extend(tune_cpu_attr(cx)); + to_add.extend(tune_cpu_attr(cx, sess)); let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); @@ -509,7 +526,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // Apply function attributes as per usual if there are no user defined // target features otherwise this will get applied at the callsite. if function_features.is_empty() { - if let Some(inline_attr) = inline_attr(cx, instance) { + if let Some(instance) = instance + && let Some(inline_attr) = inline_attr(cx, tcx, instance) + { to_add.push(inline_attr); } } @@ -517,7 +536,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones - .flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat)) + .flat_map(|feat| llvm_util::to_llvm_features(sess, feat)) // Convert LLVMFeatures & dependencies to +s .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}"))) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { @@ -526,20 +545,22 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( })) .collect::>(); - if cx.tcx.sess.target.is_like_wasm { + if sess.target.is_like_wasm { // If this function is an import from the environment but the wasm // import has a specific module/name, apply them here. - if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { + if let Some(instance) = instance + && let Some(module) = wasm_import_module(tcx, instance.def_id()) + { to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module)); let name = - codegen_fn_attrs.symbol_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); + codegen_fn_attrs.symbol_name.unwrap_or_else(|| tcx.item_name(instance.def_id())); let name = name.as_str(); to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name)); } } - to_add.extend(target_features_attr(cx, function_features)); + to_add.extend(target_features_attr(cx, tcx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 978134cc32b1c..6d12b511e9c81 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -105,7 +105,7 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, SanitizerSet::empty()); + let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerSet::empty()); attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0f17cc9063a87..a4dc4eb532f95 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1433,7 +1433,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // If there is an inline attribute and a target feature that matches // we will add the attribute to the callsite otherwise we'll omit // this and not add the attribute to prevent soundness issues. - && let Some(inlining_rule) = attributes::inline_attr(&self.cx, instance) + && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, instance) && self.cx.tcx.is_target_feature_call_safe( &fn_call_attrs.target_features, &fn_defn_attrs.target_features, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 057f525c76f03..2478042a2e62f 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -876,7 +876,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } else { let fty = self.type_variadic_func(&[], self.type_i32()); let llfn = self.declare_cfn(name, llvm::UnnamedAddr::Global, fty); - let target_cpu = attributes::target_cpu_attr(self); + let target_cpu = attributes::target_cpu_attr(self, self.sess()); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[target_cpu]); llfn } @@ -891,15 +891,15 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn set_frame_pointer_type(&self, llfn: &'ll Value) { - if let Some(attr) = attributes::frame_pointer_type_attr(self) { + if let Some(attr) = attributes::frame_pointer_type_attr(self, self.sess()) { attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[attr]); } } fn apply_target_cpu_attr(&self, llfn: &'ll Value) { let mut attrs = SmallVec::<[_; 2]>::new(); - attrs.push(attributes::target_cpu_attr(self)); - attrs.extend(attributes::tune_cpu_attr(self)); + attrs.push(attributes::target_cpu_attr(self, self.sess())); + attrs.extend(attributes::tune_cpu_attr(self, self.sess())); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); } @@ -918,7 +918,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { attributes::apply_to_llfn( llfn, llvm::AttributePlace::Function, - attributes::target_features_attr(self, vec![]).as_slice(), + attributes::target_features_attr(self, self.tcx, vec![]).as_slice(), ); Some(llfn) } else { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 960a895a2031c..36cdb49883957 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -76,7 +76,7 @@ pub(crate) fn declare_raw_fn<'ll, 'tcx>( attrs.push(llvm::AttributeKind::NoRedZone.create_attr(cx.llcx)); } - attrs.extend(attributes::non_lazy_bind_attr(cx)); + attrs.extend(attributes::non_lazy_bind_attr(cx, cx.tcx.sess)); attributes::apply_to_llfn(llfn, Function, &attrs); From 85018f09f67bd54868fe12a4632bbd637a474853 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 25 Sep 2025 18:10:55 +1000 Subject: [PATCH 0855/1900] Use `LLVMDisposeTargetMachine` --- compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs | 4 +--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 ++- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 ---- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index d5228f0e0dee1..903a882916ee8 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -95,8 +95,6 @@ impl Drop for OwnedTargetMachine { // SAFETY: constructing ensures we have a valid pointer created by // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no // double free or use after free. - unsafe { - llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr()); - } + unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) }; } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index fd972f371df49..38a6a31195438 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1053,6 +1053,8 @@ unsafe extern "C" { SLen: c_uint, ) -> MetadataKindId; + pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull); + // Create modules. pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, @@ -2499,7 +2501,6 @@ unsafe extern "C" { UseWasmEH: bool, ) -> *mut TargetMachine; - pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); pub(crate) fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, M: &'a Module, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 7518b40799bc1..013d68fa3e485 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -359,10 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( return wrap(TM); } -extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { - delete unwrap(TM); -} - // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, From 88e797784e21ecad4dc768109ede33c76293482e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 14 Sep 2025 01:12:34 +0200 Subject: [PATCH 0856/1900] rustdoc: Slightly clean up attr rendering --- src/librustdoc/html/render/mod.rs | 145 ++++++++--------------- src/librustdoc/html/render/print_item.rs | 8 +- 2 files changed, 56 insertions(+), 97 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index cc2744e48c8f6..4b7822cd3a9f9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -53,6 +53,7 @@ use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; @@ -2924,99 +2925,56 @@ fn render_call_locations( w.write_str("") } -struct CodeAttribute(String); - -fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { - write!( - w, - "
{prefix}{attr}
", - prefix = prefix, - attr = code_attr.0 - ) - .unwrap(); -} - -// When an attribute is rendered inside a tag, it is formatted using -// a div to produce a newline after it. fn render_attributes_in_code( w: &mut impl fmt::Write, item: &clean::Item, prefix: &str, cx: &Context<'_>, ) { - for attr in attributes(item, cx.tcx(), cx.cache()) { - render_code_attribute(prefix, CodeAttribute(attr), w); + for attr in &item.attrs.other_attrs { + let hir::Attribute::Parsed(kind) = attr else { continue }; + let attr = match kind { + AttributeKind::LinkSection { name, .. } => { + Cow::Owned(format!("#[unsafe(link_section = \"{name}\")]")) + } + AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"), + AttributeKind::ExportName { name, .. } => { + Cow::Owned(format!("#[unsafe(export_name = \"{name}\")]")) + } + AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), + _ => continue, + }; + render_code_attribute(prefix, attr.as_ref(), w); } -} -/// used for type aliases to only render their `repr` attribute. -fn render_repr_attributes_in_code( - w: &mut impl fmt::Write, - cx: &Context<'_>, - def_id: DefId, - item_type: ItemType, -) { - if let Some(repr) = repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - render_code_attribute("", CodeAttribute(repr), w); + if let Some(def_id) = item.def_id() + && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) + { + render_code_attribute(prefix, &repr, w); } } -/// Get a list of attributes excluding `#[repr]` to display. -fn attributes_without_repr(item: &clean::Item) -> Vec { - item.attrs - .other_attrs - .iter() - .filter_map(|attr| match attr { - hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => { - Some(format!("#[unsafe(link_section = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => { - Some("#[unsafe(no_mangle)]".to_string()) - } - hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => { - Some(format!("#[unsafe(export_name = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => { - Some("#[non_exhaustive]".to_string()) - } - _ => None, - }) - .collect() -} - -/// Get a list of attributes to display on this item. -fn attributes(item: &clean::Item, tcx: TyCtxt<'_>, cache: &Cache) -> Vec { - let mut attrs = attributes_without_repr(item); - - if let Some(repr_attr) = repr(item, tcx, cache) { - attrs.push(repr_attr); +fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) { + if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) { + render_code_attribute("", &repr, w); } - attrs } -/// Returns a stringified `#[repr(...)]` attribute. -fn repr(item: &clean::Item, tcx: TyCtxt<'_>, cache: &Cache) -> Option { - repr_attributes(tcx, cache, item.def_id()?, item.type_()) +fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) { + write!(w, "
{prefix}{attr}
").unwrap(); } -/// Return a string representing the `#[repr]` attribute if present. -pub(crate) fn repr_attributes( - tcx: TyCtxt<'_>, +fn repr_attribute<'tcx>( + tcx: TyCtxt<'tcx>, cache: &Cache, def_id: DefId, - item_type: ItemType, -) -> Option { - use rustc_abi::IntegerType; - - if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { - return None; - } - let adt = tcx.adt_def(def_id); +) -> Option> { + let adt = match tcx.def_kind(def_id) { + DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id), + _ => return None, + }; let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } + if repr.transparent() { // Render `repr(transparent)` iff the non-1-ZST field is public or at least one // field is public in case all fields are 1-ZST fields. @@ -3033,34 +2991,35 @@ pub(crate) fn repr_attributes( |field| field.vis.is_public(), ); - if render_transparent { - out.push("transparent"); - } + // Since the transparent repr can't have any other reprs or + // repr modifiers beside it, we can safely return early here. + return render_transparent.then(|| "#[repr(transparent)]".into()); + } + + let mut result = Vec::>::new(); + + if repr.c() { + result.push("C".into()); } if repr.simd() { - out.push("simd"); + result.push("simd".into()); } - let pack_s; if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); + result.push(format!("packed({})", pack.bytes()).into()); } - let align_s; if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); + result.push(format!("align({})", align.bytes()).into()); } - let int_s; if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + let prefix = if int.is_signed() { 'i' } else { 'u' }; + let int = match int { + rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"), + rustc_abi::IntegerType::Fixed(int, _) => { + format!("{prefix}{}", int.size().bytes() * 8) } }; - out.push(&int_s); + result.push(int.into()); } - if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } + + (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into()) } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index afa438f259690..adfc7481c73ae 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -21,7 +21,7 @@ use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl, - render_repr_attributes_in_code, render_rightside, render_stability_since_raw, + render_repr_attribute_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1555,7 +1555,7 @@ impl<'clean> DisplayEnum<'clean> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2017,7 +2017,7 @@ impl<'a> DisplayStruct<'a> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2371,7 +2371,7 @@ fn render_union( fmt::from_fn(move |mut f| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(f, cx, def_id, ItemType::Union); + render_repr_attribute_in_code(f, cx, def_id); } else { render_attributes_in_code(f, it, "", cx); } From d7d7725b8cbeea8db490c3b033773776ce8794f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 14 Sep 2025 01:32:20 +0200 Subject: [PATCH 0857/1900] rustdoc: Fully escape link section and export name Escape "special characters" (e.g., double quotes `"` and line breaks `\n`). Escape HTML. Lastly, add regression tests and clean up existing tests. --- src/librustdoc/html/render/mod.rs | 4 ++-- tests/rustdoc/attribute-rendering.rs | 8 -------- tests/rustdoc/attributes.rs | 12 +++++++++++ tests/rustdoc/inline_cross/attributes.rs | 17 ++++++++++++++-- .../inline_cross/auxiliary/attributes.rs | 9 +++++++++ .../reexport/auxiliary/reexports-attrs.rs | 14 ------------- tests/rustdoc/reexport/reexport-attrs.rs | 20 ------------------- 7 files changed, 38 insertions(+), 46 deletions(-) delete mode 100644 tests/rustdoc/attribute-rendering.rs delete mode 100644 tests/rustdoc/reexport/auxiliary/reexports-attrs.rs delete mode 100644 tests/rustdoc/reexport/reexport-attrs.rs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 4b7822cd3a9f9..9280701ea3f8b 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2935,11 +2935,11 @@ fn render_attributes_in_code( let hir::Attribute::Parsed(kind) = attr else { continue }; let attr = match kind { AttributeKind::LinkSection { name, .. } => { - Cow::Owned(format!("#[unsafe(link_section = \"{name}\")]")) + Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}")))) } AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"), AttributeKind::ExportName { name, .. } => { - Cow::Owned(format!("#[unsafe(export_name = \"{name}\")]")) + Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}")))) } AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), _ => continue, diff --git a/tests/rustdoc/attribute-rendering.rs b/tests/rustdoc/attribute-rendering.rs deleted file mode 100644 index fb40d0a9887cf..0000000000000 --- a/tests/rustdoc/attribute-rendering.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![crate_name = "foo"] - -//@ has 'foo/fn.f.html' -//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]' -//@ has - //*[@'class="rust item-decl"]' 'pub fn f()' -#[unsafe(export_name = "\ -f")] -pub fn f() {} diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs index 33e4e31bec6e3..429a42a7252cd 100644 --- a/tests/rustdoc/attributes.rs +++ b/tests/rustdoc/attributes.rs @@ -9,6 +9,18 @@ pub extern "C" fn f() {} #[unsafe(export_name = "bar")] pub extern "C" fn g() {} +//@ has foo/fn.escape_special.html '//*[@class="code-attribute"]' \ +// '#[unsafe(export_name = "\n\"\n")]' +#[unsafe(export_name = "\n\" +")] +pub extern "C" fn escape_special() {} + +// issue: +//@ has foo/fn.escape_html.html '//*[@class="code-attribute"]' \ +// '#[unsafe(export_name = "")]' +#[unsafe(export_name = "")] +pub extern "C" fn escape_html() {} + //@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]' #[unsafe(link_section = ".text")] pub extern "C" fn example() {} diff --git a/tests/rustdoc/inline_cross/attributes.rs b/tests/rustdoc/inline_cross/attributes.rs index 4747f8ad67c1f..1657b7bdc8f77 100644 --- a/tests/rustdoc/inline_cross/attributes.rs +++ b/tests/rustdoc/inline_cross/attributes.rs @@ -1,7 +1,20 @@ +// Ensure that we render attributes on inlined cross-crate re-exported items. +// issue: + //@ aux-crate:attributes=attributes.rs //@ edition:2021 #![crate_name = "user"] -//@ has 'user/struct.NonExhaustive.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[non_exhaustive]' +//@ has 'user/fn.no_mangle.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +pub use attributes::no_mangle; + +//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \ +// '#[unsafe(link_section = ".here")]' +pub use attributes::link_section; + +//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \ +// '#[unsafe(export_name = "exonym")]' +pub use attributes::export_name; + +//@ has 'user/struct.NonExhaustive.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]' pub use attributes::NonExhaustive; diff --git a/tests/rustdoc/inline_cross/auxiliary/attributes.rs b/tests/rustdoc/inline_cross/auxiliary/attributes.rs index c6f155d4ba5a9..6068d38558586 100644 --- a/tests/rustdoc/inline_cross/auxiliary/attributes.rs +++ b/tests/rustdoc/inline_cross/auxiliary/attributes.rs @@ -1,2 +1,11 @@ +#[unsafe(no_mangle)] +pub fn no_mangle() {} + +#[unsafe(link_section = ".here")] +pub fn link_section() {} + +#[unsafe(export_name = "exonym")] +pub fn export_name() {} + #[non_exhaustive] pub struct NonExhaustive; diff --git a/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs b/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs deleted file mode 100644 index 96fa8209cde84..0000000000000 --- a/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[unsafe(no_mangle)] -pub fn f0() {} - -#[unsafe(link_section = ".here")] -pub fn f1() {} - -#[unsafe(export_name = "f2export")] -pub fn f2() {} - -#[repr(u8)] -pub enum T0 { V1 } - -#[non_exhaustive] -pub enum T1 {} diff --git a/tests/rustdoc/reexport/reexport-attrs.rs b/tests/rustdoc/reexport/reexport-attrs.rs deleted file mode 100644 index aec0a11c0c6a0..0000000000000 --- a/tests/rustdoc/reexport/reexport-attrs.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ aux-build: reexports-attrs.rs - -#![crate_name = "foo"] - -extern crate reexports_attrs; - -//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' -pub use reexports_attrs::f0; - -//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]' -pub use reexports_attrs::f1; - -//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]' -pub use reexports_attrs::f2; - -//@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]' -pub use reexports_attrs::T0; - -//@ has 'foo/enum.T1.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]' -pub use reexports_attrs::T1; From 85c193a4ed9cf54a70d6d1edaf411b082d15fd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 17 May 2025 14:05:07 +0200 Subject: [PATCH 0858/1900] rustdoc: hide `#[repr(...)]` if it isn't part of the public ABI --- src/doc/rustdoc/src/advanced-features.md | 20 ++- src/librustdoc/html/render/mod.rs | 82 +++++++--- tests/rustdoc-gui/src/test_docs/lib.rs | 8 +- tests/rustdoc/auxiliary/ext-repr.rs | 5 + tests/rustdoc/inline_cross/auxiliary/repr.rs | 42 ----- tests/rustdoc/inline_cross/repr.rs | 40 ----- tests/rustdoc/repr.rs | 154 +++++++++++++++++-- 7 files changed, 229 insertions(+), 122 deletions(-) create mode 100644 tests/rustdoc/auxiliary/ext-repr.rs delete mode 100644 tests/rustdoc/inline_cross/auxiliary/repr.rs delete mode 100644 tests/rustdoc/inline_cross/repr.rs diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md index c02c9aebe7eef..f49edb2ac78b4 100644 --- a/src/doc/rustdoc/src/advanced-features.md +++ b/src/doc/rustdoc/src/advanced-features.md @@ -89,20 +89,30 @@ https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL to automatically go to the first result. -## `#[repr(transparent)]`: Documenting the transparent representation +## `#[repr(...)]`: Documenting the representation of a type + +Generally, rustdoc only displays the representation of a given type if none of its variants are +`#[doc(hidden)]` and if all of its fields are public and not `#[doc(hidden)]` since it's likely +not meant to be considered part of the public ABI otherwise. + +Note that there's no way to overwrite that heuristic and force rustdoc to show the representation +regardless. + +### `#[repr(transparent)]` You can read more about `#[repr(transparent)]` itself in the [Rust Reference][repr-trans-ref] and in the [Rustonomicon][repr-trans-nomicon]. Since this representation is only considered part of the public ABI if the single field with non-trivial -size or alignment is public and if the documentation does not state otherwise, Rustdoc helpfully displays -the attribute if and only if the non-1-ZST field is public or at least one field is public in case all -fields are 1-ZST fields. The term *1-ZST* refers to types that are one-aligned and zero-sized. +size or alignment is public and if the documentation does not state otherwise, rustdoc helpfully displays +the attribute if and only if the non-1-ZST field is public and not `#[doc(hidden)]` or +– in case all fields are 1-ZST fields — at least one field is public and not `#[doc(hidden)]`. +The term *1-ZST* refers to types that are one-aligned and zero-sized. It would seem that one can manually hide the attribute with `#[cfg_attr(not(doc), repr(transparent))]` if one wishes to declare the representation as private even if the non-1-ZST field is public. However, due to [current limitations][cross-crate-cfg-doc], this method is not always guaranteed to work. -Therefore, if you would like to do so, you should always write it down in prose independently of whether +Therefore, if you would like to do so, you should always write that down in prose independently of whether you use `cfg_attr` or not. [repr-trans-ref]: https://doc.rust-lang.org/reference/type-layout.html#the-transparent-representation diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 9280701ea3f8b..143911cb104da 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2964,6 +2964,10 @@ fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) { write!(w, "
{prefix}{attr}
").unwrap(); } +/// Compute the *public* `#[repr]` of the item given by `DefId`. +/// +/// Read more about it here: +/// . fn repr_attribute<'tcx>( tcx: TyCtxt<'tcx>, cache: &Cache, @@ -2975,25 +2979,59 @@ fn repr_attribute<'tcx>( }; let repr = adt.repr(); + let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id); + let is_public_field = |field: &ty::FieldDef| { + (cache.document_private || field.vis.is_public()) && is_visible(field.did) + }; + if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); + // The transparent repr is public iff the non-1-ZST field is public and visible or + // – in case all fields are 1-ZST fields — at least one field is public and visible. + let is_public = 'is_public: { + // `#[repr(transparent)]` can only be applied to structs and single-variant enums. + let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant + + if !is_visible(var.def_id) { + break 'is_public false; + } + + // Side note: There can only ever be one or zero non-1-ZST fields. + let non_1zst_field = var.fields.iter().find(|field| { + let ty = ty::TypingEnv::post_analysis(tcx, field.did) + .as_query_input(tcx.type_of(field.did).instantiate_identity()); + tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst()) + }); + + match non_1zst_field { + Some(field) => is_public_field(field), + None => var.fields.is_empty() || var.fields.iter().any(is_public_field), + } + }; // Since the transparent repr can't have any other reprs or // repr modifiers beside it, we can safely return early here. - return render_transparent.then(|| "#[repr(transparent)]".into()); + return is_public.then(|| "#[repr(transparent)]".into()); + } + + // Fast path which avoids looking through the variants and fields in + // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`. + // FIXME: This check is not very robust / forward compatible! + if !repr.c() + && !repr.simd() + && repr.int.is_none() + && repr.pack.is_none() + && repr.align.is_none() + { + return None; + } + + // The repr is public iff all components are public and visible. + let is_public = adt + .variants() + .iter() + .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field)); + if !is_public { + return None; } let mut result = Vec::>::new(); @@ -3004,12 +3042,6 @@ fn repr_attribute<'tcx>( if repr.simd() { result.push("simd".into()); } - if let Some(pack) = repr.pack { - result.push(format!("packed({})", pack.bytes()).into()); - } - if let Some(align) = repr.align { - result.push(format!("align({})", align.bytes()).into()); - } if let Some(int) = repr.int { let prefix = if int.is_signed() { 'i' } else { 'u' }; let int = match int { @@ -3021,5 +3053,13 @@ fn repr_attribute<'tcx>( result.push(int.into()); } + // Render modifiers last. + if let Some(pack) = repr.pack { + result.push(format!("packed({})", pack.bytes()).into()); + } + if let Some(align) = repr.align { + result.push(format!("align({})", align.bytes()).into()); + } + (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into()) } diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 42f2fbd93b1ee..de7c89a9fa375 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -459,10 +459,10 @@ pub fn safe_fn() {} #[repr(C)] pub struct WithGenerics { - s: S, - t: T, - e: E, - p: P, + pub s: S, + pub t: T, + pub e: E, + pub p: P, } pub struct StructWithPublicUndocumentedFields { diff --git a/tests/rustdoc/auxiliary/ext-repr.rs b/tests/rustdoc/auxiliary/ext-repr.rs new file mode 100644 index 0000000000000..25acaa49449e6 --- /dev/null +++ b/tests/rustdoc/auxiliary/ext-repr.rs @@ -0,0 +1,5 @@ +#[repr(i8)] +pub enum ReprI8 { + Var0, + Var1, +} diff --git a/tests/rustdoc/inline_cross/auxiliary/repr.rs b/tests/rustdoc/inline_cross/auxiliary/repr.rs deleted file mode 100644 index 0211e1a86588f..0000000000000 --- a/tests/rustdoc/inline_cross/auxiliary/repr.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![feature(repr_simd)] - -#[repr(C, align(8))] -pub struct ReprC { - field: u8, -} -#[repr(simd, packed(2))] -pub struct ReprSimd { - field: [u8; 1], -} -#[repr(transparent)] -pub struct ReprTransparent { - pub field: u8, -} -#[repr(isize)] -pub enum ReprIsize { - Bla, -} -#[repr(u8)] -pub enum ReprU8 { - Bla, -} - -#[repr(transparent)] // private -pub struct ReprTransparentPrivField { - field: u32, // non-1-ZST field -} - -#[repr(transparent)] // public -pub struct ReprTransparentPriv1ZstFields { - marker0: Marker, - pub main: u64, // non-1-ZST field - marker1: Marker, -} - -#[repr(transparent)] // private -pub struct ReprTransparentPrivFieldPub1ZstFields { - main: [u16; 0], // non-1-ZST field - pub marker: Marker, -} - -pub struct Marker; // 1-ZST diff --git a/tests/rustdoc/inline_cross/repr.rs b/tests/rustdoc/inline_cross/repr.rs deleted file mode 100644 index d13e560b8d77f..0000000000000 --- a/tests/rustdoc/inline_cross/repr.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Regression test for . -// This test ensures that the re-exported items still have the `#[repr(...)]` attribute. - -//@ aux-build:repr.rs - -#![crate_name = "foo"] - -extern crate repr; - -//@ has 'foo/struct.ReprC.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C, align(8))]' -pub use repr::ReprC; -//@ has 'foo/struct.ReprSimd.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]' -pub use repr::ReprSimd; -//@ has 'foo/struct.ReprTransparent.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparent; -//@ has 'foo/enum.ReprIsize.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]' -pub use repr::ReprIsize; -//@ has 'foo/enum.ReprU8.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u8)]' -pub use repr::ReprU8; - -// Regression test for . -// Check that we show `#[repr(transparent)]` iff the non-1-ZST field is public or at least one -// field is public in case all fields are 1-ZST fields. - -//@ has 'foo/struct.ReprTransparentPrivField.html' -//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPrivField; - -//@ has 'foo/struct.ReprTransparentPriv1ZstFields.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPriv1ZstFields; - -//@ has 'foo/struct.ReprTransparentPrivFieldPub1ZstFields.html' -//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPrivFieldPub1ZstFields; diff --git a/tests/rustdoc/repr.rs b/tests/rustdoc/repr.rs index f4f683b3d81a2..1e8fad6ec0a66 100644 --- a/tests/rustdoc/repr.rs +++ b/tests/rustdoc/repr.rs @@ -1,29 +1,163 @@ -// Regression test for . -// Check that we show `#[repr(transparent)]` iff the non-1-ZST field is public or at least one -// field is public in case all fields are 1-ZST fields. +// Test the rendering of `#[repr]` on ADTs. +#![feature(repr_simd)] // only used for the `ReprSimd` test case + +// Check the "local case" (HIR cleaning) // + +// Don't render the default repr which is `Rust`. +//@ has 'repr/struct.ReprDefault.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]' +pub struct ReprDefault; + +// Don't render the `Rust` repr — even if given explicitly — since it's the default. +//@ has 'repr/struct.ReprRust.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]' +#[repr(Rust)] // omitted +pub struct ReprRust; + +//@ has 'repr/struct.ReprCPubFields.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // public +pub struct ReprCPubFields { + pub a: u32, + pub b: u32, +} + +//@ has 'repr/struct.ReprCPrivField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // private... +pub struct ReprCPrivField { + a: u32, // ...since this is private + pub b: u32, +} + +//@ has 'repr/enum.ReprIsize.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]' +#[repr(isize)] // public +pub enum ReprIsize { + Bla, +} + +//@ has 'repr/enum.ReprU32HiddenVariant.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32)]' +#[repr(u32)] // private... +pub enum ReprU32HiddenVariant { + #[doc(hidden)] + Hidden, // ...since this is hidden + Public, +} + +//@ has 'repr/struct.ReprAlignHiddenField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(align(4))]' +#[repr(align(4))] // private... +pub struct ReprAlignHiddenField { + #[doc(hidden)] + pub hidden: i16, // ...since this field is hidden +} + +//@ has 'repr/struct.ReprSimd.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]' +#[repr(simd, packed(2))] // public +pub struct ReprSimd { + pub field: [u8; 1], +} + +//@ has 'repr/enum.ReprU32Align.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32, align(8))]' +#[repr(u32, align(8))] // public +pub enum ReprU32Align { + Variant(u16), +} + +//@ has 'repr/enum.ReprCHiddenVariantField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // private... +pub enum ReprCHiddenVariantField { + Variant { #[doc(hidden)] field: () }, //...since this field is hidden +} //@ has 'repr/struct.ReprTransparentPrivField.html' //@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // private +#[repr(transparent)] // private... pub struct ReprTransparentPrivField { - field: u32, // non-1-ZST field + field: u32, // ...since the non-1-ZST field is private } //@ has 'repr/struct.ReprTransparentPriv1ZstFields.html' //@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // public +#[repr(transparent)] // public... pub struct ReprTransparentPriv1ZstFields { marker0: Marker, - pub main: u64, // non-1-ZST field + pub main: u64, // ...since the non-1-ZST field is public and visible marker1: Marker, +} // the two private 1-ZST fields don't matter + +//@ has 'repr/struct.ReprTransparentPrivFieldPub1ZstField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private... +pub struct ReprTransparentPrivFieldPub1ZstField { + main: [u16; 0], // ...since the non-1-ZST field is private + pub marker: Marker, // this public 1-ZST field doesn't matter } //@ has 'repr/struct.ReprTransparentPub1ZstField.html' //@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // public +#[repr(transparent)] // public... pub struct ReprTransparentPub1ZstField { - marker0: Marker, - pub marker1: Marker, + marker0: Marker, // ...since we don't have a non-1-ZST field... + pub marker1: Marker, // ...and this field is public and visible +} + +//@ has 'repr/struct.ReprTransparentUnitStruct.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public +pub struct ReprTransparentUnitStruct; + +//@ has 'repr/enum.ReprTransparentEnumUnitVariant.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public +pub enum ReprTransparentEnumUnitVariant { + Variant, +} + +//@ has 'repr/enum.ReprTransparentEnumHiddenUnitVariant.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private +pub enum ReprTransparentEnumHiddenUnitVariant { + #[doc(hidden)] Variant(u32), +} + +//@ has 'repr/enum.ReprTransparentEnumPub1ZstField.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public... +pub enum ReprTransparentEnumPub1ZstField { + Variant { + field: u64, // ...since the non-1-ZST field is public + #[doc(hidden)] + marker: Marker, // this hidden 1-ZST field doesn't matter + }, +} + +//@ has 'repr/enum.ReprTransparentEnumHidden1ZstField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private... +pub enum ReprTransparentEnumHidden1ZstField { + Variant { + #[doc(hidden)] + field: u64, // ...since the non-1-ZST field is public + }, } struct Marker; // 1-ZST + +// Check the "extern case" (middle cleaning) // + +// Internally, HIR and middle cleaning share `#[repr]` rendering. +// Thus we'll only test the very basics in this section. + +//@ aux-build: ext-repr.rs +extern crate ext_repr as ext; + +// Regression test for . +//@ has 'repr/enum.ReprI8.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(i8)]' +pub use ext::ReprI8; From 747019ce4647dd2194c2d9e08d482e4d9c669069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Wed, 24 Sep 2025 07:28:32 +0000 Subject: [PATCH 0859/1900] bootstrap.py: Respect build.jobs while building bootstrap tool On resource-constrained systems, it is vital to respect the value of build.jobs, in order to avoid overwhelming the available memory. --- src/bootstrap/bootstrap.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index effd33d288fa6..8b1775178c915 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -596,6 +596,7 @@ def __init__(self, config_toml="", args=None): self.download_url = ( os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"] ) + self.jobs = self.get_toml("jobs", "build") or "default" self.build = args.build or self.build_triple() @@ -1144,6 +1145,7 @@ def build_bootstrap_cmd(self, env): args = [ self.cargo(), "build", + "--jobs=" + self.jobs, "--manifest-path", os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"), "-Zroot-dir=" + self.rust_root, From d834935b57bcd4604727e8a1fa93e8785979eb5c Mon Sep 17 00:00:00 2001 From: DimitriiTrater Date: Thu, 11 Sep 2025 19:17:05 +0200 Subject: [PATCH 0860/1900] Fix atan2 inaccuracy in documentation --- library/std/src/num/f128.rs | 10 ++++++---- library/std/src/num/f16.rs | 10 ++++++---- library/std/src/num/f32.rs | 10 ++++++---- library/std/src/num/f64.rs | 10 ++++++---- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index b83692390b6bc..5a85c1daaf0ab 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -557,10 +557,12 @@ impl f128 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 5599528717cbe..cc10c41b9e7d7 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -522,10 +522,12 @@ impl f16 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 0247080a8d6be..72e5f4d4c410a 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -826,10 +826,12 @@ impl f32 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 1cfd3909d967e..5f3e793c3a711 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -826,10 +826,12 @@ impl f64 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// From a9637efa275767c270829579f5282e5585043d19 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 25 Sep 2025 19:58:45 +0800 Subject: [PATCH 0861/1900] Fix fixes for unused raw variables Example --- ``` fn main() { let $0r#type = 2; } ``` **Before this PR**: ```rust fn main() { let _r#type = 2; } ``` **After this PR**: ```rust fn main() { let _type = 2; } ``` --- .../src/handlers/unused_variables.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index a4f3ca0a26725..84e63acbc04f0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -6,7 +6,7 @@ use ide_db::{ label::Label, source_change::SourceChange, }; -use syntax::{AstNode, Edition, TextRange}; +use syntax::{AstNode, Edition, TextRange, ToSmolStr}; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -73,7 +73,8 @@ fn fixes( if is_in_marco { return None; } - let name = var_name.display(db, edition); + let name = var_name.display(db, edition).to_smolstr(); + let name = name.strip_prefix("r#").unwrap_or(&name); let new_name = if is_shorthand_field { format!("{name}: _{name}") } else { format!("_{name}") }; Some(vec![Assist { @@ -231,6 +232,19 @@ fn main() { } } } +"#, + ); + + check_fix( + r#" +fn main() { + let $0r#type = 2; +} +"#, + r#" +fn main() { + let _type = 2; +} "#, ); } From c1cd1da668650d0e9e0f143f89a17387d96979c2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 3 Sep 2025 20:07:19 +0800 Subject: [PATCH 0862/1900] Add let-chain support for convert_to_guarded_return - And add early expression `None` in function `Option` return Example --- ```rust fn main() { if$0 let Ok(x) = Err(92) && x < 30 && let Some(y) = Some(8) { foo(x, y); } } ``` -> ```rust fn main() { let Ok(x) = Err(92) else { return }; if x >= 30 { return; } let Some(y) = Some(8) else { return }; foo(x, y); } ``` --- .../src/handlers/convert_to_guarded_return.rs | 217 +++++++++++++++--- 1 file changed, 179 insertions(+), 38 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 2ea032fb62ba1..3dc4737ffc51f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -1,13 +1,11 @@ use std::iter::once; -use ide_db::{ - syntax_helpers::node_ext::{is_pattern_cond, single_let}, - ty_filter::TryEnum, -}; +use hir::Semantics; +use ide_db::{RootDatabase, ty_filter::TryEnum}; use syntax::{ AstNode, SyntaxKind::{FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE}, - T, + SyntaxNode, T, ast::{ self, edit::{AstNodeEdit, IndentLevel}, @@ -73,13 +71,7 @@ fn if_expr_to_guarded_return( return None; } - // Check if there is an IfLet that we can handle. - let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) { - let let_ = single_let(cond)?; - (Some(let_.pat()?), let_.expr()?) - } else { - (None, cond) - }; + let let_chains = flat_let_chain(cond); let then_block = if_expr.then_branch()?; let then_block = then_block.stmt_list()?; @@ -106,11 +98,7 @@ fn if_expr_to_guarded_return( let parent_container = parent_block.syntax().parent()?; - let early_expression: ast::Expr = match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), - FN => make::expr_return(None), - _ => return None, - }; + let early_expression: ast::Expr = early_expression(parent_container, &ctx.sema)?; then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?; @@ -132,32 +120,42 @@ fn if_expr_to_guarded_return( target, |edit| { let if_indent_level = IndentLevel::from_node(if_expr.syntax()); - let replacement = match if_let_pat { - None => { - // If. - let new_expr = { - let then_branch = - make::block_expr(once(make::expr_stmt(early_expression).into()), None); - let cond = invert_boolean_expression_legacy(cond_expr); - make::expr_if(cond, then_branch, None).indent(if_indent_level) - }; - new_expr.syntax().clone() - } - Some(pat) => { + let replacement = let_chains.into_iter().map(|expr| { + if let ast::Expr::LetExpr(let_expr) = &expr + && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr()) + { // If-let. let let_else_stmt = make::let_else_stmt( pat, None, - cond_expr, - ast::make::tail_only_block_expr(early_expression), + expr, + ast::make::tail_only_block_expr(early_expression.clone()), ); let let_else_stmt = let_else_stmt.indent(if_indent_level); let_else_stmt.syntax().clone() + } else { + // If. + let new_expr = { + let then_branch = make::block_expr( + once(make::expr_stmt(early_expression.clone()).into()), + None, + ); + let cond = invert_boolean_expression_legacy(expr); + make::expr_if(cond, then_branch, None).indent(if_indent_level) + }; + new_expr.syntax().clone() } - }; + }); + let newline = &format!("\n{if_indent_level}"); let then_statements = replacement - .children_with_tokens() + .enumerate() + .flat_map(|(i, node)| { + (i != 0) + .then(|| make::tokens::whitespace(newline).into()) + .into_iter() + .chain(node.children_with_tokens()) + }) .chain( then_block_items .syntax() @@ -201,11 +199,7 @@ fn let_stmt_to_guarded_return( let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; let parent_container = parent_block.syntax().parent()?; - match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), - FN => make::expr_return(None), - _ => return None, - } + early_expression(parent_container, &ctx.sema)? }; acc.add( @@ -232,6 +226,44 @@ fn let_stmt_to_guarded_return( ) } +fn early_expression( + parent_container: SyntaxNode, + sema: &Semantics<'_, RootDatabase>, +) -> Option { + if let Some(fn_) = ast::Fn::cast(parent_container.clone()) + && let Some(fn_def) = sema.to_def(&fn_) + && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db)) + { + let none_expr = make::expr_path(make::ext::ident_path("None")); + return Some(make::expr_return(Some(none_expr))); + } + Some(match parent_container.kind() { + WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), + FN => make::expr_return(None), + _ => return None, + }) +} + +fn flat_let_chain(mut expr: ast::Expr) -> Vec { + let mut chains = vec![]; + + while let ast::Expr::BinExpr(bin_expr) = &expr + && bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) + && let (Some(lhs), Some(rhs)) = (bin_expr.lhs(), bin_expr.rhs()) + { + if let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_))) { + chains.push(make::expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last)); + } else { + chains.push(rhs); + } + expr = lhs; + } + + chains.push(expr); + chains.reverse(); + chains +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -268,6 +300,37 @@ fn main() { ); } + #[test] + fn convert_inside_fn_return_option() { + check_assist( + convert_to_guarded_return, + r#" +//- minicore: option +fn ret_option() -> Option<()> { + bar(); + if$0 true { + foo(); + + // comment + bar(); + } +} +"#, + r#" +fn ret_option() -> Option<()> { + bar(); + if false { + return None; + } + foo(); + + // comment + bar(); +} +"#, + ); + } + #[test] fn convert_let_inside_fn() { check_assist( @@ -316,6 +379,58 @@ fn main() { ); } + #[test] + fn convert_if_let_chain_result() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 let Ok(x) = Err(92) + && x < 30 + && let Some(y) = Some(8) + { + foo(x, y); + } +} +"#, + r#" +fn main() { + let Ok(x) = Err(92) else { return }; + if x >= 30 { + return; + } + let Some(y) = Some(8) else { return }; + foo(x, y); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 let Ok(x) = Err(92) + && x < 30 + && y < 20 + && let Some(y) = Some(8) + { + foo(x, y); + } +} +"#, + r#" +fn main() { + let Ok(x) = Err(92) else { return }; + if !(x < 30 && y < 20) { + return; + } + let Some(y) = Some(8) else { return }; + foo(x, y); +} +"#, + ); + } + #[test] fn convert_let_ok_inside_fn() { check_assist( @@ -560,6 +675,32 @@ fn main() { ); } + #[test] + fn convert_let_stmt_inside_fn_return_option() { + check_assist( + convert_to_guarded_return, + r#" +//- minicore: option +fn foo() -> Option { + None +} + +fn ret_option() -> Option { + let x$0 = foo(); +} +"#, + r#" +fn foo() -> Option { + None +} + +fn ret_option() -> Option { + let Some(x) = foo() else { return None }; +} +"#, + ); + } + #[test] fn convert_let_stmt_inside_loop() { check_assist( From 079addf985d1902e8f79620697834c3ea3de84c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 15:07:34 +0200 Subject: [PATCH 0863/1900] Ensure that `--build-dir` is always specified in tests --- src/bootstrap/src/core/config/config.rs | 27 ++++--------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1e207380a0a1b..74a272c740bd3 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -642,12 +642,12 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); + + let out = flags_build_dir.or_else(|| build_build_dir.map(PathBuf::from)); let out = if cfg!(test) { - test_build_dir() + out.expect("--build-dir has to be specified in tests") } else { - flags_build_dir - .or_else(|| build_build_dir.map(PathBuf::from)) - .unwrap_or_else(|| PathBuf::from("build")) + out.unwrap_or_else(|| PathBuf::from("build")) }; // NOTE: Bootstrap spawns various commands with different working directories. @@ -2490,22 +2490,3 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { }) .collect() } - -/// Resolve the build directory used for tests. -/// -/// - When tests are run through bootstrap (`x.py test`), the build system -/// sets `CARGO_TARGET_DIR`, so we can trust and use it here. -/// - When tests are run directly with cargo test, `CARGO_TARGET_DIR` will -/// not be set. In that case we fall back to resolving relative to -/// `CARGO_MANIFEST_DIR`, by walking two parents up and appending `build`. -fn test_build_dir() -> PathBuf { - env::var_os("CARGO_TARGET_DIR") - .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) - .unwrap_or_else(|| { - let base = option_env!("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .unwrap_or_else(|| std::env::current_dir().expect("failed to get current dir")); - - base.ancestors().nth(2).unwrap_or_else(|| Path::new(".")).join("build") - }) -} From 56194654c6b9f2ba5eb077c0e535969f34c12e21 Mon Sep 17 00:00:00 2001 From: Zihan Date: Tue, 23 Sep 2025 17:02:09 -0400 Subject: [PATCH 0864/1900] `filter_next`: check for `filter().next_back()` changelog: [`filter_next`]: suggest replacing `filter().next_back()` with `rfind()` for `DoubleEndedIterator` Signed-off-by: Zihan --- clippy_lints/src/methods/filter_next.rs | 91 +++++++++++++++---------- clippy_lints/src/methods/mod.rs | 12 +++- clippy_utils/src/msrvs.rs | 2 +- tests/ui/auxiliary/option_helpers.rs | 4 ++ tests/ui/methods.rs | 20 ++++++ tests/ui/methods.stderr | 13 +++- tests/ui/methods_fixable.fixed | 14 ++++ tests/ui/methods_fixable.rs | 14 ++++ tests/ui/methods_fixable.stderr | 14 +++- 9 files changed, 143 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 72f83b245a0cb..e1a9a79e20eea 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -10,51 +10,68 @@ use rustc_span::sym; use super::FILTER_NEXT; -/// lint use of `filter().next()` for `Iterators` +#[derive(Copy, Clone)] +pub(super) enum Direction { + Forward, + Backward, +} + +/// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for +/// `DoubleEndedIterator` pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, filter_arg: &'tcx hir::Expr<'_>, + direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator - let recv_impls_iterator = cx + // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a + // DoubleEndedIterator + let (required_trait, next_method, find_method) = match direction { + Direction::Forward => (sym::Iterator, "next", "find"), + Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), + }; + if !cx .tcx - .get_diagnostic_item(sym::Iterator) - .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])); - if recv_impls_iterator { - let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead"; - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - (Applicability::Unspecified, Some((pat.span, ident))) - } else { - (Applicability::MachineApplicable, None) - }; + .get_diagnostic_item(required_trait) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])) + { + return; + } + let msg = format!( + "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ + `.{find_method}(..)` instead", + required_trait.as_str() + ); + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + // add note if not multi-line + span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { + let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) + && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; - diag.span_suggestion( - expr.span, - "try", - format!("{iter_snippet}.find({filter_snippet})"), - applicability, - ); + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.{find_method}({filter_snippet})"), + applicability, + ); - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"), - ); - } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), + ); + } + }); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4d..9b27a6c19e1fb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5369,7 +5369,9 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter, [arg]) => { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Forward); + }, (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), @@ -5379,6 +5381,14 @@ impl Methods { } } }, + (sym::next_back, []) => { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) + && let (sym::filter, [arg]) = (name2, args2) + && self.msrv.meets(cx, msrvs::DOUBLE_ENDED_ITERATOR_RFIND) + { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Backward); + } + }, (sym::nth, [n_arg]) => match method_call(recv) { Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4d..ad3d52193bf6e 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -72,7 +72,7 @@ msrv_aliases! { 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } - 1,27,0 { ITERATOR_TRY_FOLD } + 1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index f9bc9436b0792..796d4dabf04d2 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -25,6 +25,10 @@ impl IteratorFalsePositives { self } + pub fn next_back(self) -> IteratorFalsePositives { + self + } + pub fn find(self) -> Option { Some(self.foo) } diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index f73fe288b0f8c..9595888b99f83 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -135,6 +135,26 @@ fn filter_next() { let _ = foo.filter(42).next(); } +#[rustfmt::skip] +fn filter_next_back() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Multi-line case. + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + fn main() { filter_next(); + filter_next_back(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b226ce7c65dab..45efea4ee01cd 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -24,5 +24,16 @@ LL | | ).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 2 previous errors +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods.rs:143:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed index 49730d8115584..287d8d881ec28 100644 --- a/tests/ui/methods_fixable.fixed +++ b/tests/ui/methods_fixable.fixed @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().find(|&x| *x < 0); //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs index a499b63b6f5bd..11ce1b63560db 100644 --- a/tests/ui/methods_fixable.rs +++ b/tests/ui/methods_fixable.rs @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().filter(|&x| *x < 0).next(); //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 852e7a224a6e3..d26b5e9ac271e 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -7,5 +7,17 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 1 previous error +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:12:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:18:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` + +error: aborting due to 3 previous errors From 1947949f3d7212c181049d96e2665481d3bddc6b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 25 Sep 2025 21:57:10 +0800 Subject: [PATCH 0865/1900] Fix not applicable for if-expr in let-stmt Example --- ```rust fn main() { let _x = loop { if$0 let Ok(x) = Err(92) { foo(x); } }; } ``` **Before**: Assist not applicable **After**: ```rust fn main() { let _x = loop { let Ok(x) = Err(92) else { continue }; foo(x); }; } ``` --- .../src/handlers/convert_to_guarded_return.rs | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 3dc4737ffc51f..aee9ce7878b29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -1,6 +1,7 @@ use std::iter::once; use hir::Semantics; +use either::Either; use ide_db::{RootDatabase, ty_filter::TryEnum}; use syntax::{ AstNode, @@ -42,12 +43,9 @@ use crate::{ // } // ``` pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - if let Some(let_stmt) = ctx.find_node_at_offset() { - let_stmt_to_guarded_return(let_stmt, acc, ctx) - } else if let Some(if_expr) = ctx.find_node_at_offset() { - if_expr_to_guarded_return(if_expr, acc, ctx) - } else { - None + match ctx.find_node_at_offset::>()? { + Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx), + Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx), } } @@ -379,6 +377,30 @@ fn main() { ); } + #[test] + fn convert_if_let_result_inside_let() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + let _x = loop { + if$0 let Ok(x) = Err(92) { + foo(x); + } + }; +} +"#, + r#" +fn main() { + let _x = loop { + let Ok(x) = Err(92) else { continue }; + foo(x); + }; +} +"#, + ); + } + #[test] fn convert_if_let_chain_result() { check_assist( From 11c35cd0bcb6e0c285be031f10d14d64bbf2bd9c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 25 Sep 2025 21:57:45 +0800 Subject: [PATCH 0866/1900] Add applicable in closure for convert_to_guarded_return Example --- ```rust fn main() { let _f = || { bar(); if$0 true { foo(); // comment bar(); } } } ``` -> ```rust fn main() { let _f = || { bar(); if false { return; } foo(); // comment bar(); } } ``` --- .../src/handlers/convert_to_guarded_return.rs | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index aee9ce7878b29..82213ae3217e7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -1,11 +1,11 @@ use std::iter::once; -use hir::Semantics; use either::Either; +use hir::{Semantics, TypeInfo}; use ide_db::{RootDatabase, ty_filter::TryEnum}; use syntax::{ AstNode, - SyntaxKind::{FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE}, + SyntaxKind::{CLOSURE_EXPR, FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE}, SyntaxNode, T, ast::{ self, @@ -228,16 +228,26 @@ fn early_expression( parent_container: SyntaxNode, sema: &Semantics<'_, RootDatabase>, ) -> Option { + let return_none_expr = || { + let none_expr = make::expr_path(make::ext::ident_path("None")); + make::expr_return(Some(none_expr)) + }; if let Some(fn_) = ast::Fn::cast(parent_container.clone()) && let Some(fn_def) = sema.to_def(&fn_) && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db)) { - let none_expr = make::expr_path(make::ext::ident_path("None")); - return Some(make::expr_return(Some(none_expr))); + return Some(return_none_expr()); + } + if let Some(body) = ast::ClosureExpr::cast(parent_container.clone()).and_then(|it| it.body()) + && let Some(ret_ty) = sema.type_of_expr(&body).map(TypeInfo::original) + && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &ret_ty) + { + return Some(return_none_expr()); } + Some(match parent_container.kind() { WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), - FN => make::expr_return(None), + FN | CLOSURE_EXPR => make::expr_return(None), _ => return None, }) } @@ -329,6 +339,40 @@ fn ret_option() -> Option<()> { ); } + #[test] + fn convert_inside_closure() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + let _f = || { + bar(); + if$0 true { + foo(); + + // comment + bar(); + } + } +} +"#, + r#" +fn main() { + let _f = || { + bar(); + if false { + return; + } + foo(); + + // comment + bar(); + } +} +"#, + ); + } + #[test] fn convert_let_inside_fn() { check_assist( From 89a3a445bfb31637549e4f1a652869f631416c73 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 25 Sep 2025 07:18:36 -0700 Subject: [PATCH 0867/1900] mbe: macro_check: Fix function comments referencing non-existent parameters Several functions had comments referencing a non-existent `valid` parameter. Remove those. The `guar` parameter that handles errors is already documented. In the process, remove another duplicate reference to an already-documented parameter (`binders`). --- compiler/rustc_expand/src/mbe/macro_check.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index ebd6e887f7d28..0eae44a05e783 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -210,8 +210,7 @@ pub(super) fn check_meta_variables( guar.map_or(Ok(()), Err) } -/// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and -/// sets `valid` to false in case of errors. +/// Checks `lhs` as part of the LHS of a macro definition. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -306,8 +305,7 @@ fn get_binder_info<'a>( binders.get(&name).or_else(|| macros.find_map(|state| state.binders.get(&name))) } -/// Checks `rhs` as part of the RHS of a macro definition and sets `valid` to false in case of -/// errors. +/// Checks `rhs` as part of the RHS of a macro definition. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -372,7 +370,7 @@ enum NestedMacroState { } /// Checks `tts` as part of the RHS of a macro definition, tries to recognize nested macro -/// definitions, and sets `valid` to false in case of errors. +/// definitions. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -491,8 +489,7 @@ fn check_nested_occurrences( } } -/// Checks the body of nested macro, returns where the check stopped, and sets `valid` to false in -/// case of errors. +/// Checks the body of nested macro, returns where the check stopped. /// /// The token trees are checked as long as they look like a list of (LHS) => {RHS} token trees. This /// check is a best-effort to detect a macro definition. It returns the position in `tts` where we From 9877b26fd14d32064b1f944dd1e79c9c8faccf16 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 16:42:45 +0200 Subject: [PATCH 0868/1900] Correctly display merged doctest compilation time --- src/librustdoc/doctest.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 95bd31729de18..9499258f983a1 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -404,11 +404,15 @@ pub(crate) fn run_tests( std::mem::drop(temp_dir.take()); times.display_times(); }); + } else { + // If the first condition branch exited successfully, `test_main_with_exit_callback` will + // not exit the process. So to prevent displaying the times twice, we put it behind an + // `else` condition. + times.display_times(); } + // We ensure temp dir destructor is called. + std::mem::drop(temp_dir); if nb_errors != 0 { - // We ensure temp dir destructor is called. - std::mem::drop(temp_dir); - times.display_times(); std::process::exit(test::ERROR_EXIT_CODE); } } From 9e3f7487e9e572aee95381688bda52525d79b005 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 25 Sep 2025 07:43:25 -0700 Subject: [PATCH 0869/1900] mbe: Simplify check_redundant_vis_repetition Eliminate a use of `map_or` in favor of a match. Inline some variable definitions that don't add clarity, and that prevent short-circuiting. --- compiler/rustc_expand/src/mbe/macro_rules.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1d147a0385c68..74b7de7e12bc8 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -894,12 +894,12 @@ fn check_redundant_vis_repetition( seq: &SequenceRepetition, span: &DelimSpan, ) { - let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne; - let is_vis = seq.tts.first().map_or(false, |tt| { - matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. }) - }); - - if is_vis && is_zero_or_one { + if seq.kleene.op == KleeneOp::ZeroOrOne + && matches!( + seq.tts.first(), + Some(mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. }) + ) + { err.note("a `vis` fragment can already be empty"); err.multipart_suggestion( "remove the `$(` and `)?`", From 7e58c9110521d960cb97e730dfe700b3ec64c9ab Mon Sep 17 00:00:00 2001 From: helldawg <116204326+helldawg@users.noreply.github.com> Date: Fri, 19 Sep 2025 04:59:14 +0300 Subject: [PATCH 0870/1900] usize/isize range matching error clarification --- .../src/thir/pattern/check_match.rs | 8 +++---- ...ture-gate-precise_pointer_size_matching.rs | 4 ++-- ...-gate-precise_pointer_size_matching.stderr | 4 ++-- .../pointer-sized-int.deny.stderr | 22 +++++++++---------- .../precise_pointer_matching-message.rs | 4 ++-- .../precise_pointer_matching-message.stderr | 4 ++-- ...pes-containing-non-exhaustive-types.stderr | 16 +++++++------- 7 files changed, 31 insertions(+), 31 deletions(-) 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 ae67bb5075e8f..3929a97eed8f2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1275,13 +1275,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( if ty.is_ptr_sized_integral() { if ty.inner() == cx.tcx.types.usize { err.note(format!( - "`{ty}` does not have a fixed maximum value, so half-open ranges are \ - necessary to match exhaustively", + "`{ty}::MAX` is not treated as exhaustive, \ + so half-open ranges are necessary to match exhaustively", )); } else if ty.inner() == cx.tcx.types.isize { err.note(format!( - "`{ty}` does not have fixed minimum and maximum values, so half-open \ - ranges are necessary to match exhaustively", + "`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \ + so half-open ranges are necessary to match exhaustively", )); } } else if ty.inner() == cx.tcx.types.str_ { diff --git a/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.rs b/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.rs index b4dc1fd45564d..e37e405d1aff0 100644 --- a/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.rs +++ b/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.rs @@ -3,7 +3,7 @@ fn main() { //~^ ERROR non-exhaustive patterns: `usize::MAX..` not covered //~| NOTE pattern `usize::MAX..` not covered //~| NOTE the matched value is of type `usize` - //~| NOTE `usize` does not have a fixed maximum value + //~| NOTE `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively 0..=usize::MAX => {} } @@ -11,7 +11,7 @@ fn main() { //~^ ERROR non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered //~| NOTE patterns `..isize::MIN` and `isize::MAX..` not covered //~| NOTE the matched value is of type `isize` - //~| NOTE `isize` does not have fixed minimum and maximum values + //~| NOTE `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively isize::MIN..=isize::MAX => {} } } diff --git a/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr b/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr index c89dcaf727ac3..cfb00d6e74128 100644 --- a/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr +++ b/tests/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr @@ -5,7 +5,7 @@ LL | match 0usize { | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ 0..=usize::MAX => {}, @@ -19,7 +19,7 @@ LL | match 0isize { | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ isize::MIN..=isize::MAX => {}, diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr index 7caee64a33fbc..099d6e862434c 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr @@ -5,7 +5,7 @@ LL | match 0usize { | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ 0..=usize::MAX => {}, @@ -19,7 +19,7 @@ LL | match 0isize { | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ isize::MIN..=isize::MAX => {}, @@ -33,7 +33,7 @@ LL | m!(0usize, 0..=usize::MAX); | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() } @@ -46,7 +46,7 @@ LL | m!(0usize, 0..5 | 5..=usize::MAX); | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() } @@ -59,7 +59,7 @@ LL | m!(0usize, 0..usize::MAX | usize::MAX); | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match $s { $($t)+ => {}, usize::MAX.. => todo!() } @@ -72,7 +72,7 @@ LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize:: | ^^^^^^^^^^^^^^ pattern `(usize::MAX.., _)` not covered | = note: the matched value is of type `(usize, bool)` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match $s { $($t)+ => {}, (usize::MAX.., _) => todo!() } @@ -85,7 +85,7 @@ LL | m!(0isize, isize::MIN..=isize::MAX); | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() } @@ -98,7 +98,7 @@ LL | m!(0isize, isize::MIN..5 | 5..=isize::MAX); | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() } @@ -111,7 +111,7 @@ LL | m!(0isize, isize::MIN..=-1 | 0 | 1..=isize::MAX); | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() } @@ -124,7 +124,7 @@ LL | m!(0isize, isize::MIN..isize::MAX | isize::MAX); | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL | match $s { $($t)+ => {}, ..isize::MIN | isize::MAX.. => todo!() } @@ -137,7 +137,7 @@ LL | (0isize, true), | ^^^^^^^^^^^^^^ patterns `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered | = note: the matched value is of type `(isize, bool)` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL | match $s { $($t)+ => {}, (..isize::MIN, _) | (isize::MAX.., _) => todo!() } diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs index d60f479c0d155..6a0106134b50a 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs @@ -4,7 +4,7 @@ fn main() { //~^ ERROR non-exhaustive patterns: `usize::MAX..` not covered //~| NOTE pattern `usize::MAX..` not covered //~| NOTE the matched value is of type `usize` - //~| NOTE `usize` does not have a fixed maximum value + //~| NOTE `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively 0..=usize::MAX => {} } @@ -12,7 +12,7 @@ fn main() { //~^ ERROR non-exhaustive patterns: `..isize::MIN` and `isize::MAX..` not covered //~| NOTE patterns `..isize::MIN` and `isize::MAX..` not covered //~| NOTE the matched value is of type `isize` - //~| NOTE `isize` does not have fixed minimum and maximum values + //~| NOTE `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively isize::MIN..=isize::MAX => {} } } diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr index 36743aa810296..fefe7f46ead9d 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr @@ -5,7 +5,7 @@ LL | match 0usize { | ^^^^^^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ 0..=usize::MAX => {}, @@ -19,7 +19,7 @@ LL | match 0isize { | ^^^^^^ patterns `..isize::MIN` and `isize::MAX..` not covered | = note: the matched value is of type `isize` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ isize::MIN..=isize::MAX => {}, diff --git a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr index c31411018bc46..9d7b53093df98 100644 --- a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr +++ b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr @@ -5,7 +5,7 @@ LL | match 0 { | ^ pattern `usize::MAX..` not covered | = note: the matched value is of type `usize` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ 1..=usize::MAX => (), @@ -19,7 +19,7 @@ LL | match (0usize, 0usize) { | ^^^^^^^^^^^^^^^^ pattern `(usize::MAX.., _)` not covered | = note: the matched value is of type `(usize, usize)` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ (1..=usize::MAX, 1..=usize::MAX) => (), @@ -33,7 +33,7 @@ LL | match (0isize, 0usize) { | ^^^^^^^^^^^^^^^^ patterns `(..isize::MIN, _)` and `(isize::MAX.., _)` not covered | = note: the matched value is of type `(isize, usize)` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ (isize::MIN..=isize::MAX, 1..=usize::MAX) => (), @@ -70,7 +70,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ None => (), @@ -93,7 +93,7 @@ note: `Option>>` defined here | = note: not covered = note: the matched value is of type `Option>>` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ None => (), @@ -112,7 +112,7 @@ note: `A` defined here LL | struct A { | ^ = note: the matched value is of type `A` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ A { a: 1..=usize::MAX } => (), @@ -131,7 +131,7 @@ note: `B` defined here LL | struct B(T, U); | ^ = note: the matched value is of type `B` - = note: `isize` does not have fixed minimum and maximum values, so half-open ranges are necessary to match exhaustively + = note: `isize::MIN` and `isize::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (), @@ -150,7 +150,7 @@ note: `B` defined here LL | struct B(T, U); | ^ = note: the matched value is of type `B` - = note: `usize` does not have a fixed maximum value, so half-open ranges are necessary to match exhaustively + = note: `usize::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ B(_, 1..=usize::MAX) => (), From 185ae698aabe0f6f7cc5ef7eeed13a556bce5334 Mon Sep 17 00:00:00 2001 From: Tim 'Piepmatz' Hesse Date: Thu, 25 Sep 2025 17:52:24 +0200 Subject: [PATCH 0871/1900] add doc for `NonZero*` const creation --- library/core/src/num/nonzero.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1b7c28bb95aab..d9184e3c9c229 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -548,6 +548,18 @@ macro_rules! nonzero_integer { #[doc = concat!("assert_eq!(align_of::<", stringify!($Ty), ">(), align_of::>());")] /// ``` /// + /// # Compile-time creation + /// + /// Since both [`Option::unwrap()`] and [`Option::expect()`] are `const`, it is possible to + /// define a new + #[doc = concat!("`", stringify!($Ty), "`")] + /// at compile time via: + /// ``` + #[doc = concat!("use std::num::", stringify!($Ty), ";")] + /// + #[doc = concat!("const TEN: ", stringify!($Ty), " = ", stringify!($Ty) , r#"::new(10).expect("ten is non-zero");"#)] + /// ``` + /// /// [null pointer optimization]: crate::option#representation #[$stability] pub type $Ty = NonZero<$Int>; From 191c7ed10f68c6891ba66e342fa58342e16db402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 17:32:17 +0200 Subject: [PATCH 0872/1900] Make `cargo test` work again --- src/bootstrap/src/core/config/config.rs | 38 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 74a272c740bd3..07a6616be0c98 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -414,15 +414,17 @@ impl Config { // Set config values based on flags. let mut exec_ctx = ExecutionContext::new(flags_verbose, flags_cmd.fail_fast()); exec_ctx.set_dry_run(if flags_dry_run { DryRun::UserSelected } else { DryRun::Disabled }); - let mut src = { + + let default_src_dir = { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // Undo `src/bootstrap` manifest_dir.parent().unwrap().parent().unwrap().to_owned() }; - - if let Some(src_) = compute_src_directory(flags_src, &exec_ctx) { - src = src_; - } + let src = if let Some(s) = compute_src_directory(flags_src, &exec_ctx) { + s + } else { + default_src_dir.clone() + }; #[cfg(test)] { @@ -659,6 +661,10 @@ impl Config { out }; + let default_stage0_rustc_path = |dir: &Path| { + dir.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + }; + if cfg!(test) { // When configuring bootstrap for tests, make sure to set the rustc and Cargo to the // same ones used to call the tests (if custom ones are not defined in the toml). If we @@ -667,6 +673,22 @@ impl Config { // Cargo in their bootstrap.toml. build_rustc = build_rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into())); build_cargo = build_cargo.take().or(std::env::var_os("CARGO").map(|p| p.into())); + + // If we are running only `cargo test` (and not `x test bootstrap`), which is useful + // e.g. for debugging bootstrap itself, then we won't have RUSTC and CARGO set to the + // proper paths. + // We thus "guess" that the build directory is located at /build, and try to load + // rustc and cargo from there + let is_test_outside_x = std::env::var("CARGO_TARGET_DIR").is_err(); + if is_test_outside_x && build_rustc.is_none() { + let stage0_rustc = default_stage0_rustc_path(&default_src_dir.join("build")); + assert!( + stage0_rustc.exists(), + "Trying to run cargo test without having a stage0 rustc available in {}", + stage0_rustc.display() + ); + build_rustc = Some(stage0_rustc); + } } if !flags_skip_stage0_validation { @@ -700,7 +722,7 @@ impl Config { let initial_rustc = build_rustc.unwrap_or_else(|| { download_beta_toolchain(&dwn_ctx, &out); - out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + default_stage0_rustc_path(&out) }); let initial_sysroot = t!(PathBuf::from_str( @@ -1540,11 +1562,11 @@ impl Config { println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled."); println!("HELP: Consider rebasing to a newer commit if available."); return None; - }, + } Err(e) => { eprintln!("ERROR: Failed to parse CI rustc bootstrap.toml: {e}"); exit!(2); - }, + } }; let current_config_toml = Self::get_toml(config_path).unwrap(); From 5595f437c7b82b1ca18f77f8834fe9398b682153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 18:12:39 +0200 Subject: [PATCH 0873/1900] Remove `verbose_than` function --- src/bootstrap/src/core/build_steps/compile.rs | 7 +------ src/bootstrap/src/lib.rs | 13 ++++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 14104d7d1d783..b47b4f248a9ac 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1902,12 +1902,7 @@ impl Step for Sysroot { if !path.parent().is_none_or(|p| p.ends_with(&suffix)) { return true; } - if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) { - builder.verbose_than(1, || println!("ignoring {}", path.display())); - false - } else { - true - } + filtered_files.iter().all(|f| f != path.file_name().unwrap()) }); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index e953fe2945e48..db963f9c6f990 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1092,13 +1092,6 @@ impl Build { self.verbosity > level } - /// Runs a function if verbosity is greater than `level`. - fn verbose_than(&self, level: usize, f: impl Fn()) { - if self.is_verbose_than(level) { - f() - } - } - fn info(&self, msg: &str) { match self.config.get_dry_run() { DryRun::SelfCheck => (), @@ -1816,7 +1809,6 @@ impl Build { if self.config.dry_run() { return; } - self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}")); if src == dst { return; } @@ -1933,7 +1925,10 @@ impl Build { return; } let dst = dstdir.join(src.file_name().unwrap()); - self.verbose_than(1, || println!("Install {src:?} to {dst:?}")); + + #[cfg(feature = "tracing")] + let _span = trace_io!("install", ?src, ?dst); + t!(fs::create_dir_all(dstdir)); if !src.exists() { panic!("ERROR: File \"{}\" not found!", src.display()); From 5a6e3cccebd9cac06af815f0480ad58d14182480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 18:14:33 +0200 Subject: [PATCH 0874/1900] Remove `is_verbose_than` function --- src/bootstrap/src/core/builder/cargo.rs | 2 +- src/bootstrap/src/lib.rs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index ee2bb710674c0..9fc4ce669c2a5 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1139,7 +1139,7 @@ impl Builder<'_> { cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); } - if self.is_verbose_than(1) { + if self.verbosity >= 2 { // This provides very useful logs especially when debugging build cache-related stuff. cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index db963f9c6f990..75cc79e6689c8 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1087,11 +1087,6 @@ impl Build { }) } - /// Check if verbosity is greater than the `level` - pub fn is_verbose_than(&self, level: usize) -> bool { - self.verbosity > level - } - fn info(&self, msg: &str) { match self.config.get_dry_run() { DryRun::SelfCheck => (), From 0374df1b50d227f173c07631b390b271efb04ae6 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Fri, 5 Sep 2025 08:34:00 +0200 Subject: [PATCH 0875/1900] Introduce and use CmCell during import resolution. --- .../rustc_resolve/src/build_reduced_graph.rs | 19 +-- compiler/rustc_resolve/src/imports.rs | 26 ++-- compiler/rustc_resolve/src/lib.rs | 128 +++++++++++++++--- compiler/rustc_resolve/src/macros.rs | 2 +- 4 files changed, 135 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index fa3c06059b3da..c10b6ca7e71b5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -5,7 +5,6 @@ //! unexpanded macros in the fragment are visited and registered. //! Imports are also considered items and placed into modules here, but not resolved yet. -use std::cell::Cell; use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; @@ -35,6 +34,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS}; use crate::def_collector::collect_definitions; use crate::imports::{ImportData, ImportKind}; use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; +use crate::ref_mut::CmCell; use crate::{ BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used, @@ -87,7 +87,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - parent.underscore_disambiguator.update(|d| d + 1); + // FIXME(batched): Will be fixed in batched resolution. + parent.underscore_disambiguator.update_unchecked(|d| d + 1); parent.underscore_disambiguator.get() }); if self @@ -477,7 +478,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind, parent_scope: self.parent_scope, module_path, - imported_module: Cell::new(None), + imported_module: CmCell::new(None), span, use_span: item.span, use_span_with_attributes: item.span_with_attributes(), @@ -505,7 +506,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }); } } - ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import), + ImportKind::Glob { .. } => current_module.globs.borrow_mut(self.r).push(import), _ => unreachable!(), } } @@ -668,7 +669,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ast::UseTreeKind::Glob => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: Cell::new(None), id }; + let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -971,7 +972,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id }, root_id: item.id, parent_scope: self.parent_scope, - imported_module: Cell::new(module), + imported_module: CmCell::new(module), has_attributes: !item.attrs.is_empty(), use_span_with_attributes: item.span_with_attributes(), use_span: item.span, @@ -1103,7 +1104,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::MacroUse { warn_private }, root_id: item.id, parent_scope: this.parent_scope, - imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), + imported_module: CmCell::new(Some(ModuleOrUniformRoot::Module(module))), use_span_with_attributes: item.span_with_attributes(), has_attributes: !item.attrs.is_empty(), use_span: item.span, @@ -1196,7 +1197,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { /// directly into its parent scope's module. fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> { let invoc_id = self.visit_invoc(id); - self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id); + self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } @@ -1274,7 +1275,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::MacroExport, root_id: item.id, parent_scope: self.parent_scope, - imported_module: Cell::new(None), + imported_module: CmCell::new(None), has_attributes: false, use_span_with_attributes: span, use_span: span, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d7fc028287ff5..2370bf8093943 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1,6 +1,5 @@ //! A bunch of methods and structures more or less related to resolving imports. -use std::cell::Cell; use std::mem; use rustc_ast::NodeId; @@ -32,6 +31,7 @@ use crate::errors::{ CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates, ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate, }; +use crate::ref_mut::CmCell; use crate::{ AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion, Module, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, @@ -68,7 +68,7 @@ pub(crate) enum ImportKind<'ra> { /// It will directly use `source` when the format is `use prefix::source`. target: Ident, /// Bindings introduced by the import. - bindings: PerNS>>, + bindings: PerNS>>, /// `true` for `...::{self [as target]}` imports, `false` otherwise. type_ns_only: bool, /// Did this import result from a nested import? ie. `use foo::{bar, baz};` @@ -89,7 +89,7 @@ pub(crate) enum ImportKind<'ra> { Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. - max_vis: Cell>, + max_vis: CmCell>, id: NodeId, }, ExternCrate { @@ -182,7 +182,7 @@ pub(crate) struct ImportData<'ra> { /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | /// |`use ::foo` | `ModuleOrUniformRoot::ModuleAndExternPrelude` | a special case in 2015 edition | /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | - pub imported_module: Cell>>, + pub imported_module: CmCell>>, pub vis: Visibility, /// Span of the visibility. @@ -320,7 +320,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && (vis == import_vis || max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx))) { - max_vis.set(Some(vis.expect_local())) + // FIXME(batched): Will be fixed in batched import resolution. + max_vis.set_unchecked(Some(vis.expect_local())) } self.arenas.alloc_name_binding(NameBindingData { @@ -349,7 +350,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - module.underscore_disambiguator.update(|d| d + 1); + // FIXME(batched): Will be fixed in batched resolution. + module.underscore_disambiguator.update_unchecked(|d| d + 1); module.underscore_disambiguator.get() }); self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { @@ -482,7 +484,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - let Ok(glob_importers) = module.glob_importers.try_borrow_mut() else { + let Ok(glob_importers) = module.glob_importers.try_borrow_mut_unchecked() else { return t; }; @@ -862,7 +864,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - import.imported_module.set(Some(module)); + // FIXME(batched): Will be fixed in batched import resolution. + import.imported_module.set_unchecked(Some(module)); let (source, target, bindings, type_ns_only) = match import.kind { ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => { (source, target, bindings, type_ns_only) @@ -937,7 +940,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PendingBinding::Pending } }; - bindings[ns].set(binding); + // FIXME(batched): Will be fixed in batched import resolution. + bindings[ns].set_unchecked(binding); } }); @@ -1508,7 +1512,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Add to module's glob_importers - module.glob_importers.borrow_mut().push(import); + module.glob_importers.borrow_mut_unchecked().push(import); // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. @@ -1550,7 +1554,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // reporting conflicts, and reporting unresolved imports. fn finalize_resolutions_in(&mut self, module: Module<'ra>) { // Since import resolution is finished, globs will not define any more names. - *module.globs.borrow_mut() = Vec::new(); + *module.globs.borrow_mut(self) = Vec::new(); let Some(def_id) = module.opt_def_id() else { return }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0dea6ae332735..245bcdac81e86 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -19,6 +19,7 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(ptr_as_ref_unchecked)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![recursion_limit = "256"] @@ -26,7 +27,7 @@ use std::cell::{Cell, Ref, RefCell}; use std::collections::BTreeSet; -use std::fmt; +use std::fmt::{self}; use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; @@ -95,6 +96,8 @@ pub mod rustdoc; pub use macros::registered_tools_ast; +use crate::ref_mut::{CmCell, CmRefCell}; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } #[derive(Debug)] @@ -592,22 +595,22 @@ struct ModuleData<'ra> { /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'ra>, /// True if this is a module from other crate that needs to be populated on access. - populate_on_access: Cell, + populate_on_access: Cell, // FIXME(parallel): Use an atomic in parallel import resolution /// Used to disambiguate underscore items (`const _: T = ...`) in the module. - underscore_disambiguator: Cell, + underscore_disambiguator: CmCell, /// Macro invocations that can expand into items in this module. - unexpanded_invocations: RefCell>, + unexpanded_invocations: CmRefCell>, /// Whether `#[no_implicit_prelude]` is active. no_implicit_prelude: bool, - glob_importers: RefCell>>, - globs: RefCell>>, + glob_importers: CmRefCell>>, + globs: CmRefCell>>, /// Used to memoize the traits in this module for faster searches through all traits in scope. traits: - RefCell, Option>)]>>>, + CmRefCell, Option>)]>>>, /// Span of the module itself. Used for error reporting. span: Span, @@ -656,12 +659,12 @@ impl<'ra> ModuleData<'ra> { kind, lazy_resolutions: Default::default(), populate_on_access: Cell::new(is_foreign), - underscore_disambiguator: Cell::new(0), + underscore_disambiguator: CmCell::new(0), unexpanded_invocations: Default::default(), no_implicit_prelude, - glob_importers: RefCell::new(Vec::new()), - globs: RefCell::new(Vec::new()), - traits: RefCell::new(None), + glob_importers: CmRefCell::new(Vec::new()), + globs: CmRefCell::new(Vec::new()), + traits: CmRefCell::new(None), span, expansion, self_binding, @@ -696,7 +699,7 @@ impl<'ra> Module<'ra> { /// This modifies `self` in place. The traits will be stored in `self.traits`. fn ensure_traits<'tcx>(self, resolver: &impl AsRef>) { - let mut traits = self.traits.borrow_mut(); + let mut traits = self.traits.borrow_mut(resolver.as_ref()); if traits.is_none() { let mut collected_traits = Vec::new(); self.for_each_child(resolver, |r, name, ns, binding| { @@ -1974,6 +1977,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { if module.populate_on_access.get() { + // FIXME(batched): Will be fixed in batched import resolution. module.populate_on_access.set(false); self.build_reduced_graph_external(module); } @@ -2502,9 +2506,20 @@ pub fn provide(providers: &mut Providers) { providers.registered_tools = macros::registered_tools; } +/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions. +/// +/// `Cm` stands for "conditionally mutable". +/// +/// Prefer constructing it through [`Resolver::cm`] to ensure correctness. +type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; + mod ref_mut { + use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; + use std::fmt; use std::ops::Deref; + use crate::Resolver; + /// A wrapper around a mutable reference that conditionally allows mutable access. pub(crate) struct RefOrMut<'a, T> { p: &'a mut T, @@ -2553,11 +2568,86 @@ mod ref_mut { self.p } } -} -/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions. -/// -/// `Cm` stands for "conditionally mutable". -/// -/// Prefer constructing it through [`Resolver::cm`] to ensure correctness. -type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; + /// A wrapper around a [`Cell`] that only allows mutation based on a condition in the resolver. + #[derive(Default)] + pub(crate) struct CmCell(Cell); + + impl fmt::Debug for CmCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CmCell").field(&self.get()).finish() + } + } + + impl Clone for CmCell { + #[inline] + fn clone(&self) -> CmCell { + CmCell::new(self.get()) + } + } + + impl CmCell { + pub(crate) const fn get(&self) -> T { + self.0.get() + } + + pub(crate) fn update_unchecked(&self, f: impl FnOnce(T) -> T) + where + T: Copy, + { + let old = self.get(); + self.set_unchecked(f(old)); + } + } + + impl CmCell { + pub(crate) const fn new(value: T) -> CmCell { + CmCell(Cell::new(value)) + } + + pub(crate) fn set_unchecked(&self, val: T) { + self.0.set(val); + } + + pub(crate) fn into_inner(self) -> T { + self.0.into_inner() + } + } + + /// A wrapper around a [`RefCell`] that only allows mutable borrows based on a condition in the resolver. + #[derive(Default)] + pub(crate) struct CmRefCell(RefCell); + + impl CmRefCell { + pub(crate) const fn new(value: T) -> CmRefCell { + CmRefCell(RefCell::new(value)) + } + + #[inline] + #[track_caller] + pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { + self.0.borrow_mut() + } + + #[inline] + #[track_caller] + pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> RefMut<'_, T> { + if r.assert_speculative { + panic!("Not allowed to mutably borrow a CmRefCell during speculative resolution"); + } + self.borrow_mut_unchecked() + } + + #[inline] + #[track_caller] + pub(crate) fn try_borrow_mut_unchecked(&self) -> Result, BorrowMutError> { + self.0.try_borrow_mut() + } + + #[inline] + #[track_caller] + pub(crate) fn borrow(&self) -> Ref<'_, T> { + self.0.borrow() + } + } +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5fa87b4cd9b68..3b6ef88c5303a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -189,7 +189,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); - parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); + parent_scope.module.unexpanded_invocations.borrow_mut(self).remove(&expansion); if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) { From 8ea9122c9b9465982424f7254a9cd88147e85642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 18:16:56 +0200 Subject: [PATCH 0876/1900] Rename `verbose` to `do_if_verbose` --- src/bootstrap/src/core/build_steps/compile.rs | 7 ++++--- src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/gcc.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 6 +++--- src/bootstrap/src/core/builder/mod.rs | 4 ++-- src/bootstrap/src/core/config/config.rs | 6 +++--- src/bootstrap/src/core/download.rs | 18 ++++++++++-------- src/bootstrap/src/lib.rs | 10 +++++----- src/bootstrap/src/utils/build_stamp.rs | 2 +- src/bootstrap/src/utils/cc_detect.rs | 10 +++++----- src/bootstrap/src/utils/exec.rs | 6 +++--- src/bootstrap/src/utils/render_tests.rs | 2 +- src/bootstrap/src/utils/tarball.rs | 2 +- 13 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index b47b4f248a9ac..e699922f4dc05 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1832,8 +1832,9 @@ impl Step for Sysroot { let sysroot = sysroot_dir(compiler.stage); trace!(stage = ?compiler.stage, ?sysroot); - builder - .verbose(|| println!("Removing sysroot {} to avoid caching bugs", sysroot.display())); + builder.do_if_verbose(|| { + println!("Removing sysroot {} to avoid caching bugs", sysroot.display()) + }); let _ = fs::remove_dir_all(&sysroot); t!(fs::create_dir_all(&sysroot)); @@ -2591,7 +2592,7 @@ pub fn stream_cargo( cmd.arg(arg); } - builder.verbose(|| println!("running: {cmd:?}")); + builder.do_if_verbose(|| println!("running: {cmd:?}")); let streaming_command = cmd.stream_capture_stdout(&builder.config.exec_ctx); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 99a1062109adc..b79d2cb413db7 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2304,7 +2304,7 @@ fn maybe_install_llvm( let mut cmd = command(host_llvm_config); cmd.cached(); cmd.arg("--libfiles"); - builder.verbose(|| println!("running {cmd:?}")); + builder.do_if_verbose(|| println!("running {cmd:?}")); let files = cmd.run_capture_stdout(builder).stdout(); let build_llvm_out = &builder.llvm_out(builder.config.host_target); let target_llvm_out = &builder.llvm_out(target); diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 717dea37e9e61..17ab8c4e2f479 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -128,7 +128,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option\n". let sysroot = stdout.trim_end(); - builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}")); + builder.do_if_verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}")); PathBuf::from(sysroot) } } @@ -2675,7 +2675,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> return true; } - builder.verbose(|| println!("doc tests for: {}", markdown.display())); + builder.do_if_verbose(|| println!("doc tests for: {}", markdown.display())); let mut cmd = builder.rustdoc_cmd(compiler); builder.add_rust_test_threads(&mut cmd); // allow for unstable options such as new editions diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 8226b4325b6b8..049d2647bec49 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -545,7 +545,7 @@ impl StepDescription { if !builder.config.skip.is_empty() && !matches!(builder.config.get_dry_run(), DryRun::SelfCheck) { - builder.verbose(|| { + builder.do_if_verbose(|| { println!( "{:?} not skipped for {:?} -- not in {:?}", pathset, self.name, builder.config.skip @@ -947,7 +947,7 @@ impl Step for Libdir { // Sysroot`). if !builder.download_rustc() { let sysroot_target_libdir = sysroot.join(self.target).join("lib"); - builder.verbose(|| { + builder.do_if_verbose(|| { eprintln!( "Removing sysroot {} to avoid caching bugs", sysroot_target_libdir.display() diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2d5a1fd5332..fb7c334491b34 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1571,8 +1571,8 @@ impl Config { } /// Runs a function if verbosity is greater than 0 - pub fn verbose(&self, f: impl Fn()) { - self.exec_ctx.verbose(f); + pub fn do_if_verbose(&self, f: impl Fn()) { + self.exec_ctx.do_if_verbose(f); } pub fn any_sanitizers_to_build(&self) -> bool { @@ -2061,7 +2061,7 @@ pub fn download_ci_rustc_commit<'a>( // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let freshness = check_path_modifications_(dwn_ctx, RUSTC_IF_UNCHANGED_ALLOWED_PATHS); - dwn_ctx.exec_ctx.verbose(|| { + dwn_ctx.exec_ctx.do_if_verbose(|| { eprintln!("rustc freshness: {freshness:?}"); }); match freshness { diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 2f3c80559c0ef..37871f0fe1e28 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -106,7 +106,7 @@ enum DownloadSource { /// Functions that are only ever called once, but named for clarity and to avoid thousand-line functions. impl Config { pub(crate) fn download_clippy(&self) -> PathBuf { - self.verbose(|| println!("downloading stage0 clippy artifacts")); + self.do_if_verbose(|| println!("downloading stage0 clippy artifacts")); let date = &self.stage0_metadata.compiler.date; let version = &self.stage0_metadata.compiler.version; @@ -151,7 +151,9 @@ impl Config { } pub(crate) fn download_ci_rustc(&self, commit: &str) { - self.verbose(|| println!("using downloaded stage2 artifacts from CI (commit {commit})")); + self.do_if_verbose(|| { + println!("using downloaded stage2 artifacts from CI (commit {commit})") + }); let version = self.artifact_version_part(commit); // download-rustc doesn't need its own cargo, it can just use beta's. But it does need the @@ -258,7 +260,7 @@ impl Config { let llvm_root = self.ci_llvm_root(); let llvm_freshness = detect_llvm_freshness(self, self.rust_info.is_managed_git_subrepository()); - self.verbose(|| { + self.do_if_verbose(|| { eprintln!("LLVM freshness: {llvm_freshness:?}"); }); let llvm_sha = match llvm_freshness { @@ -557,7 +559,7 @@ pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef(dwn_ctx: impl AsRef>, out: &Path) { let dwn_ctx = dwn_ctx.as_ref(); - dwn_ctx.exec_ctx.verbose(|| { + dwn_ctx.exec_ctx.do_if_verbose(|| { println!("downloading stage0 beta artifacts"); }); @@ -812,7 +814,7 @@ fn download_component<'a>( unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); return; } else { - dwn_ctx.exec_ctx.verbose(|| { + dwn_ctx.exec_ctx.do_if_verbose(|| { println!( "ignoring cached file {} due to failed verification", tarball.display() @@ -853,7 +855,7 @@ download-rustc = false pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool { use sha2::Digest; - exec_ctx.verbose(|| { + exec_ctx.do_if_verbose(|| { println!("verifying {}", path.display()); }); @@ -934,7 +936,7 @@ fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); let dst_path = dst.join(short_path); - exec_ctx.verbose(|| { + exec_ctx.do_if_verbose(|| { println!("extracting {} to {}", original_path.display(), dst.display()); }); @@ -965,7 +967,7 @@ fn download_file<'a>( ) { let dwn_ctx = dwn_ctx.as_ref(); - dwn_ctx.exec_ctx.verbose(|| { + dwn_ctx.exec_ctx.do_if_verbose(|| { println!("download {url}"); }); // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 75cc79e6689c8..4f4d35673d5d1 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -415,7 +415,7 @@ macro_rules! forward { } forward! { - verbose(f: impl Fn()), + do_if_verbose(f: impl Fn()), is_verbose() -> bool, create(path: &Path, s: &str), remove(f: &Path), @@ -601,11 +601,11 @@ impl Build { .unwrap() .trim(); if local_release.split('.').take(2).eq(version.split('.').take(2)) { - build.verbose(|| println!("auto-detected local-rebuild {local_release}")); + build.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}")); build.local_rebuild = true; } - build.verbose(|| println!("finding compilers")); + build.do_if_verbose(|| println!("finding compilers")); utils::cc_detect::fill_compilers(&mut build); // When running `setup`, the profile is about to change, so any requirements we have now may // be different on the next invocation. Don't check for them until the next time x.py is @@ -613,7 +613,7 @@ impl Build { // // Similarly, for `setup` we don't actually need submodules or cargo metadata. if !matches!(build.config.cmd, Subcommand::Setup { .. }) { - build.verbose(|| println!("running sanity check")); + build.do_if_verbose(|| println!("running sanity check")); crate::core::sanity::check(&mut build); // Make sure we update these before gathering metadata so we don't get an error about missing @@ -631,7 +631,7 @@ impl Build { // Now, update all existing submodules. build.update_existing_submodules(); - build.verbose(|| println!("learning about cargo")); + build.do_if_verbose(|| println!("learning about cargo")); crate::core::metadata::build(&mut build); } diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index 4c35388a181aa..5cd68f6d4fe7f 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -112,7 +112,7 @@ pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { let stamp = BuildStamp::new(dir); let mut cleared = false; if mtime(stamp.path()) < mtime(input) { - builder.verbose(|| println!("Dirty - {}", dir.display())); + builder.do_if_verbose(|| println!("Dirty - {}", dir.display())); let _ = fs::remove_dir_all(dir); cleared = true; } else if stamp.path().exists() { diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index d3926df9650ce..0662ae304ac06 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -137,16 +137,16 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { build.cxx.insert(target, compiler); } - build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target))); - build.verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple)); + build.do_if_verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target))); + build.do_if_verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple)); if let Ok(cxx) = build.cxx(target) { let mut cxxflags = build.cc_handled_clags(target, CLang::Cxx); cxxflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx)); - build.verbose(|| println!("CXX_{} = {cxx:?}", target.triple)); - build.verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple)); + build.do_if_verbose(|| println!("CXX_{} = {cxx:?}", target.triple)); + build.do_if_verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple)); } if let Some(ar) = ar { - build.verbose(|| println!("AR_{} = {ar:?}", target.triple)); + build.do_if_verbose(|| println!("AR_{} = {ar:?}", target.triple)); build.ar.insert(target, ar); } diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index e09f3086b777c..f875e6e1af75c 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -630,7 +630,7 @@ impl ExecutionContext { &self.dry_run } - pub fn verbose(&self, f: impl Fn()) { + pub fn do_if_verbose(&self, f: impl Fn()) { if self.is_verbose() { f() } @@ -686,7 +686,7 @@ impl ExecutionContext { if let Some(cached_output) = self.command_cache.get(&fingerprint) { command.mark_as_executed(); - self.verbose(|| println!("Cache hit: {command:?}")); + self.do_if_verbose(|| println!("Cache hit: {command:?}")); self.profiler.record_cache_hit(fingerprint); return DeferredCommand { state: CommandState::Cached(cached_output) }; } @@ -713,7 +713,7 @@ impl ExecutionContext { }; } - self.verbose(|| { + self.do_if_verbose(|| { println!("running: {command:?} (created at {created_at}, executed at {executed_at})") }); diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 90fd57d976d3f..e90a7ef42324a 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -48,7 +48,7 @@ pub(crate) fn try_run_tests( } fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool { - builder.verbose(|| println!("running: {cmd:?}")); + builder.do_if_verbose(|| println!("running: {cmd:?}")); let Some(mut streaming_command) = cmd.stream_capture_stdout(&builder.config.exec_ctx) else { return true; diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 7b77b21293413..079afb7a00548 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -356,7 +356,7 @@ impl<'a> Tarball<'a> { // For `x install` tarball files aren't needed, so we can speed up the process by not producing them. let compression_profile = if self.builder.kind == Kind::Install { - self.builder.verbose(|| { + self.builder.do_if_verbose(|| { println!("Forcing dist.compression-profile = 'no-op' for `x install`.") }); // "no-op" indicates that the rust-installer won't produce compressed tarball sources. From a2d286992409d8077a03e0994a3839abdbfccc90 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Sep 2025 09:56:57 -0700 Subject: [PATCH 0877/1900] Add `clippy::unconditional_recursion` to `./x clippy ci` The clippy lint catches some things that rustc's equivalent builtin lint does not, for example rust-lang/rust#146940: error: function cannot return without recursing --> library/std/src/path.rs:3428:5 | 3428 | / fn eq(&self, other: &String) -> bool { 3429 | | self == &*other 3430 | | } | |_____^ | note: recursive call site --> library/std/src/path.rs:3429:9 | 3429 | self == &*other | ^^^^^^^^^^^^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion = note: requested on the command line with `-D clippy::unconditional-recursion` --- src/bootstrap/src/core/build_steps/clippy.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 2083c675e1fdd..d5b15d7908646 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -564,6 +564,7 @@ impl Step for CI { "clippy::same_item_push".into(), "clippy::single_char_add_str".into(), "clippy::to_string_in_format_args".into(), + "clippy::unconditional_recursion".into(), ], forbid: vec![], }; @@ -591,6 +592,7 @@ impl Step for CI { "clippy::same_item_push".into(), "clippy::single_char_add_str".into(), "clippy::to_string_in_format_args".into(), + "clippy::unconditional_recursion".into(), ], forbid: vec![], }; From f89660e4aa018401ca993f0df190e5f4c4a6799b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 1 Aug 2025 21:55:11 +0300 Subject: [PATCH 0878/1900] resolve: Do not finalize shadowed bindings I.e. do not mark them as used, or non-speculative loaded, or similar. Previously they were sometimes finalized during early resolution, causing issues like https://github.com/rust-lang/rust/pull/144793#issuecomment-3168108005. --- compiler/rustc_macros/src/query.rs | 2 +- compiler/rustc_resolve/src/ident.rs | 11 ++++++----- library/alloctests/tests/lib.rs | 1 - library/std/src/sys/pal/unix/stack_overflow.rs | 2 +- src/tools/clippy/tests/ui/useless_attribute.fixed | 2 +- src/tools/clippy/tests/ui/useless_attribute.rs | 2 +- .../crates/hir-expand/src/builtin/derive_macro.rs | 2 +- .../crates/hir-expand/src/builtin/fn_macro.rs | 2 +- .../crates/hir-expand/src/builtin/quote.rs | 2 -- src/tools/rustfmt/src/config/mod.rs | 1 - tests/ui/resolve/unused-macro-import.rs | 13 +++++++++++++ tests/ui/resolve/unused-macro-import.stderr | 14 ++++++++++++++ 12 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 tests/ui/resolve/unused-macro-import.rs create mode 100644 tests/ui/resolve/unused-macro-import.stderr diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 5821ffa3a302d..5d32950875adb 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -5,7 +5,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ AttrStyle, Attribute, Block, Error, Expr, Ident, Pat, ReturnType, Token, Type, braced, - parenthesized, parse_macro_input, parse_quote, token, + parenthesized, parse_macro_input, token, }; mod kw { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 35051675fd8dc..51489019950ad 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -492,14 +492,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => Err(Determinacy::Determined), }, Scope::Module(module, derive_fallback_lint_id) => { - // FIXME: use `finalize_scope` here. let (adjusted_parent_scope, adjusted_finalize) = if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { - (parent_scope, finalize) + (parent_scope, finalize_scope!()) } else { ( &ParentScope { module, ..*parent_scope }, - finalize.map(|f| Finalize { used: Used::Scope, ..f }), + finalize_scope!().map(|f| Finalize { used: Used::Scope, ..f }), ) }; let binding = this.reborrow().resolve_ident_in_module_unadjusted( @@ -557,8 +556,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => Err(Determinacy::Determined), }, Scope::ExternPreludeItems => { - // FIXME: use `finalize_scope` here. - match this.reborrow().extern_prelude_get_item(ident, finalize.is_some()) { + match this + .reborrow() + .extern_prelude_get_item(ident, finalize_scope!().is_some()) + { Some(binding) => { extern_prelude_item_binding = Some(binding); Ok((binding, Flags::empty())) diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 8c3ce156f3c1d..4947b38ba89e4 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -46,7 +46,6 @@ #![deny(unsafe_op_in_unsafe_fn)] extern crate alloc; -extern crate test; use std::hash::{DefaultHasher, Hash, Hasher}; diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0d2100d66bc09..51463eef5b778 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -72,7 +72,7 @@ mod imp { use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, panic, ptr}; + use crate::{io, mem, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index 15070dd9c2ca6..e0bc23e0788c6 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 3f530de7fd8e3..30a4c354b238a 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 15e68ff95cd57..0fa412ad7fa2c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::{ ExpandError, ExpandResult, MacroCallId, - builtin::quote::{dollar_crate, quote}, + builtin::quote::dollar_crate, db::ExpandDatabase, hygiene::span_with_def_site_ctxt, name::{self, AsName, Name}, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index ec34461376165..6fe63f249cd4a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -19,7 +19,7 @@ use syntax_bridge::syntax_node_to_token_tree; use crate::{ EditionedFileId, ExpandError, ExpandResult, Lookup as _, MacroCallId, - builtin::quote::{WithDelimiter, dollar_crate, quote}, + builtin::quote::{WithDelimiter, dollar_crate}, db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 70c38d4d7c75b..84dd4a24d901f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -229,8 +229,6 @@ mod tests { use span::{Edition, ROOT_ERASED_FILE_AST_ID, SpanAnchor, SyntaxContext}; use syntax::{TextRange, TextSize}; - use super::quote; - const DUMMY: tt::Span = tt::Span { range: TextRange::empty(TextSize::new(0)), anchor: SpanAnchor { diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 6b63108c037eb..525953bf4458b 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -516,7 +516,6 @@ mod test { #[allow(dead_code)] mod mock { use super::super::*; - use crate::config_option_with_style_edition_default; use rustfmt_config_proc_macro::config_type; #[config_type] diff --git a/tests/ui/resolve/unused-macro-import.rs b/tests/ui/resolve/unused-macro-import.rs new file mode 100644 index 0000000000000..e85f7a43993f4 --- /dev/null +++ b/tests/ui/resolve/unused-macro-import.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![warn(unused_imports)] + +#[macro_export] +macro_rules! mac { () => {} } + +fn main() { + // Unused, `mac` as `macro_rules!` is already in scope and has higher priority. + use crate::mac; //~ WARN unused import: `crate::mac` + + mac!(); +} diff --git a/tests/ui/resolve/unused-macro-import.stderr b/tests/ui/resolve/unused-macro-import.stderr new file mode 100644 index 0000000000000..5f9813808a0aa --- /dev/null +++ b/tests/ui/resolve/unused-macro-import.stderr @@ -0,0 +1,14 @@ +warning: unused import: `crate::mac` + --> $DIR/unused-macro-import.rs:10:9 + | +LL | use crate::mac; + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macro-import.rs:3:9 + | +LL | #![warn(unused_imports)] + | ^^^^^^^^^^^^^^ + +warning: 1 warning emitted + From 283aad06e9a9145b963119b2b061db92ada9d680 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 1 Aug 2025 21:55:11 +0300 Subject: [PATCH 0879/1900] resolve: Do not finalize shadowed bindings I.e. do not mark them as used, or non-speculative loaded, or similar. Previously they were sometimes finalized during early resolution, causing issues like https://github.com/rust-lang/rust/pull/144793#issuecomment-3168108005. --- tests/ui/useless_attribute.fixed | 2 +- tests/ui/useless_attribute.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 15070dd9c2ca6..e0bc23e0788c6 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3f530de7fd8e3..30a4c354b238a 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); From bd39ea463f30fd51be0b4587191c67cd53bf6658 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 25 Sep 2025 00:17:03 +0200 Subject: [PATCH 0880/1900] inline `clippy_utils::ptr` into `needless_pass_by_value` ..and make it less generic, thanks to all the callers being known --- clippy_lints/src/needless_pass_by_value.rs | 63 +++++++++++++++++----- clippy_utils/src/lib.rs | 1 - clippy_utils/src/ptr.rs | 52 ------------------ 3 files changed, 51 insertions(+), 65 deletions(-) delete mode 100644 clippy_utils/src/ptr.rs diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 32ded96c1236f..455d1426aa8cb 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use clippy_utils::{is_self, peel_hir_ty_options}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Attribute, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, - PatKind, QPath, TyKind, + Attribute, BindingMode, Body, ExprKind, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, + Node, PatKind, QPath, TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_lint::{LateContext, LateLintPass}; @@ -19,10 +19,13 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; +use std::borrow::Cow; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for functions taking arguments by value, but not @@ -217,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -260,12 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = get_spans( - cx, - Some(body.id()), - idx, - &[(sym::clone, ".to_string()"), (sym::as_str, "")], - ) + && let Some(clone_spans) = + get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diag.span_suggestion( input.span, @@ -340,3 +339,43 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } + +fn get_spans<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Body<'_>, + idx: usize, + replacements: &[(Symbol, &'static str)], +) -> Option)>> { + if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { + extract_clone_suggestions(cx, binding_id, replacements, body) + } else { + Some(vec![]) + } +} + +fn extract_clone_suggestions<'tcx>( + cx: &LateContext<'tcx>, + id: HirId, + replace: &[(Symbol, &'static str)], + body: &'tcx Body<'_>, +) -> Option)>> { + let mut spans = Vec::new(); + for_each_expr_without_closures(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.name == sym::capacity { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.name == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }) + .is_none() + .then_some(spans) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659a..d6586625a1e5e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,7 +63,6 @@ pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; -pub mod ptr; pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs deleted file mode 100644 index 5847e916e340f..0000000000000 --- a/clippy_utils/src/ptr.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::source::snippet; -use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs, sym}; -use core::ops::ControlFlow; -use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; -use std::borrow::Cow; - -pub fn get_spans( - cx: &LateContext<'_>, - opt_body_id: Option, - idx: usize, - replacements: &[(Symbol, &'static str)], -) -> Option)>> { - if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { - if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { - extract_clone_suggestions(cx, binding_id, replacements, body) - } else { - Some(vec![]) - } - } else { - Some(vec![]) - } -} - -fn extract_clone_suggestions<'tcx>( - cx: &LateContext<'tcx>, - id: HirId, - replace: &[(Symbol, &'static str)], - body: &'tcx Body<'_>, -) -> Option)>> { - let mut spans = Vec::new(); - for_each_expr_without_closures(body, |e| { - if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) - { - if seg.ident.name == sym::capacity { - return ControlFlow::Break(()); - } - for &(fn_name, suffix) in replace { - if seg.ident.name == fn_name { - spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }) - .is_none() - .then_some(spans) -} From 9316d4508c42d5e6d91480aec34f66462801ee5d Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:36:58 +0200 Subject: [PATCH 0881/1900] Remove most `#[track_caller]` from allocating Vec methods They cause significant binary size overhead while contributing little value. Also removes them from the wrapping String methods that do not panic. --- .../alloc/src/collections/vec_deque/mod.rs | 27 ------------- .../src/collections/vec_deque/spec_extend.rs | 6 --- .../collections/vec_deque/spec_from_iter.rs | 1 - library/alloc/src/raw_vec/mod.rs | 17 --------- library/alloc/src/string.rs | 13 ------- library/alloc/src/vec/cow.rs | 1 - library/alloc/src/vec/in_place_collect.rs | 2 - library/alloc/src/vec/mod.rs | 38 ------------------- library/alloc/src/vec/spec_extend.rs | 6 --- library/alloc/src/vec/spec_from_elem.rs | 4 -- library/alloc/src/vec/spec_from_iter.rs | 2 - .../alloc/src/vec/spec_from_iter_nested.rs | 2 - library/alloc/src/vec/splice.rs | 2 - tests/ui/hygiene/panic-location.run.stderr | 2 +- 14 files changed, 1 insertion(+), 122 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index d589860524b82..ac619a42d356d 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -103,7 +103,6 @@ pub struct VecDeque< #[stable(feature = "rust1", since = "1.0.0")] impl Clone for VecDeque { - #[track_caller] fn clone(&self) -> Self { let mut deq = Self::with_capacity_in(self.len(), self.allocator().clone()); deq.extend(self.iter().cloned()); @@ -114,7 +113,6 @@ impl Clone for VecDeque { /// /// This method is preferred over simply assigning `source.clone()` to `self`, /// as it avoids reallocation if possible. - #[track_caller] fn clone_from(&mut self, source: &Self) { self.clear(); self.extend(source.iter().cloned()); @@ -577,7 +575,6 @@ impl VecDeque { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] - #[track_caller] pub fn with_capacity(capacity: usize) -> VecDeque { Self::with_capacity_in(capacity, Global) } @@ -633,7 +630,6 @@ impl VecDeque { /// let deque: VecDeque = VecDeque::with_capacity(10); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[track_caller] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque { VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) } } @@ -799,7 +795,6 @@ impl VecDeque { /// /// [`reserve`]: VecDeque::reserve #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn reserve_exact(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); let old_cap = self.capacity(); @@ -830,7 +825,6 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] - #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); let old_cap = self.capacity(); @@ -962,7 +956,6 @@ impl VecDeque { /// assert!(buf.capacity() >= 4); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] - #[track_caller] pub fn shrink_to_fit(&mut self) { self.shrink_to(0); } @@ -988,7 +981,6 @@ impl VecDeque { /// assert!(buf.capacity() >= 4); /// ``` #[stable(feature = "shrink_to", since = "1.56.0")] - #[track_caller] pub fn shrink_to(&mut self, min_capacity: usize) { let target_cap = min_capacity.max(self.len); @@ -1891,7 +1883,6 @@ impl VecDeque { /// assert_eq!(d.front(), Some(&2)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn push_front(&mut self, value: T) { let _ = self.push_front_mut(value); } @@ -1910,7 +1901,6 @@ impl VecDeque { /// assert_eq!(d.front(), Some(&7)); /// ``` #[unstable(feature = "push_mut", issue = "135974")] - #[track_caller] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_front` instead"] pub fn push_front_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -1937,7 +1927,6 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "put", "append")] - #[track_caller] pub fn push_back(&mut self, value: T) { let _ = self.push_back_mut(value); } @@ -1956,7 +1945,6 @@ impl VecDeque { /// assert_eq!(d.back(), Some(&10)); /// ``` #[unstable(feature = "push_mut", issue = "135974")] - #[track_caller] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_back` instead"] pub fn push_back_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2071,7 +2059,6 @@ impl VecDeque { /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c', 'e']); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] - #[track_caller] pub fn insert(&mut self, index: usize, value: T) { let _ = self.insert_mut(index, value); } @@ -2099,7 +2086,6 @@ impl VecDeque { /// assert_eq!(vec_deque, &[1, 12, 2, 3]); /// ``` #[unstable(feature = "push_mut", issue = "135974")] - #[track_caller] #[must_use = "if you don't need a reference to the value, use `VecDeque::insert` instead"] pub fn insert_mut(&mut self, index: usize, value: T) -> &mut T { assert!(index <= self.len(), "index out of bounds"); @@ -2205,7 +2191,6 @@ impl VecDeque { #[inline] #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] - #[track_caller] pub fn split_off(&mut self, at: usize) -> Self where A: Clone, @@ -2272,7 +2257,6 @@ impl VecDeque { /// ``` #[inline] #[stable(feature = "append", since = "1.4.0")] - #[track_caller] pub fn append(&mut self, other: &mut Self) { if T::IS_ZST { self.len = self.len.checked_add(other.len).expect("capacity overflow"); @@ -2395,7 +2379,6 @@ impl VecDeque { // be called in cold paths. // This may panic or abort #[inline(never)] - #[track_caller] fn grow(&mut self) { // Extend or possibly remove this assertion when valid use-cases for growing the // buffer without it being full emerge @@ -2434,7 +2417,6 @@ impl VecDeque { /// assert_eq!(buf, [5, 10, 101, 102, 103]); /// ``` #[stable(feature = "vec_resize_with", since = "1.33.0")] - #[track_caller] pub fn resize_with(&mut self, new_len: usize, generator: impl FnMut() -> T) { let len = self.len; @@ -2981,7 +2963,6 @@ impl VecDeque { /// assert_eq!(buf, [5, 10, 20, 20, 20]); /// ``` #[stable(feature = "deque_extras", since = "1.16.0")] - #[track_caller] pub fn resize(&mut self, new_len: usize, value: T) { if new_len > self.len() { let extra = new_len - self.len(); @@ -3101,7 +3082,6 @@ impl IndexMut for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator for VecDeque { - #[track_caller] fn from_iter>(iter: I) -> VecDeque { SpecFromIter::spec_from_iter(iter.into_iter()) } @@ -3141,19 +3121,16 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut VecDeque { #[stable(feature = "rust1", since = "1.0.0")] impl Extend for VecDeque { - #[track_caller] fn extend>(&mut self, iter: I) { >::spec_extend(self, iter.into_iter()); } #[inline] - #[track_caller] fn extend_one(&mut self, elem: T) { self.push_back(elem); } #[inline] - #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3169,19 +3146,16 @@ impl Extend for VecDeque { #[stable(feature = "extend_ref", since = "1.2.0")] impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque { - #[track_caller] fn extend>(&mut self, iter: I) { self.spec_extend(iter.into_iter()); } #[inline] - #[track_caller] fn extend_one(&mut self, &elem: &'a T) { self.push_back(elem); } #[inline] - #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3279,7 +3253,6 @@ impl From<[T; N]> for VecDeque { /// let deq2: VecDeque<_> = [1, 2, 3, 4].into(); /// assert_eq!(deq1, deq2); /// ``` - #[track_caller] fn from(arr: [T; N]) -> Self { let mut deq = VecDeque::with_capacity(N); let arr = ManuallyDrop::new(arr); diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index 7c7072c4c3a1b..6c2199135e08a 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -8,7 +8,6 @@ use crate::vec; // Specialization trait used for VecDeque::extend pub(super) trait SpecExtend { - #[track_caller] fn spec_extend(&mut self, iter: I); } @@ -16,7 +15,6 @@ impl SpecExtend for VecDeque where I: Iterator, { - #[track_caller] default fn spec_extend(&mut self, mut iter: I) { // This function should be the moral equivalent of: // @@ -47,7 +45,6 @@ impl SpecExtend for VecDeque where I: TrustedLen, { - #[track_caller] default fn spec_extend(&mut self, iter: I) { // This is the case for a TrustedLen iterator. let (low, high) = iter.size_hint(); @@ -81,7 +78,6 @@ where #[cfg(not(test))] impl SpecExtend> for VecDeque { - #[track_caller] fn spec_extend(&mut self, mut iterator: vec::IntoIter) { let slice = iterator.as_slice(); self.reserve(slice.len()); @@ -99,7 +95,6 @@ where I: Iterator, T: Copy, { - #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.spec_extend(iterator.copied()) } @@ -109,7 +104,6 @@ impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for VecDeque where T: Copy, { - #[track_caller] fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { let slice = iterator.as_slice(); self.reserve(slice.len()); diff --git a/library/alloc/src/collections/vec_deque/spec_from_iter.rs b/library/alloc/src/collections/vec_deque/spec_from_iter.rs index c80a30c2103dd..557666ea3b874 100644 --- a/library/alloc/src/collections/vec_deque/spec_from_iter.rs +++ b/library/alloc/src/collections/vec_deque/spec_from_iter.rs @@ -9,7 +9,6 @@ impl SpecFromIter for VecDeque where I: Iterator, { - #[track_caller] default fn spec_from_iter(iterator: I) -> Self { // Since converting is O(1) now, just re-use the `Vec` logic for // anything where we can't do something extra-special for `VecDeque`, diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 1d4a1f592a99a..bc9692f5b6c2f 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -24,7 +24,6 @@ mod tests; // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] #[cfg_attr(not(panic = "immediate-abort"), inline(never))] -#[track_caller] fn capacity_overflow() -> ! { panic!("capacity overflow"); } @@ -123,7 +122,6 @@ impl RawVec { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] - #[track_caller] pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -132,7 +130,6 @@ impl RawVec { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] - #[track_caller] pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), @@ -145,7 +142,6 @@ impl RawVecInner { #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] #[inline] - #[track_caller] fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { Ok(res) => res, @@ -186,7 +182,6 @@ impl RawVec { /// allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), @@ -208,7 +203,6 @@ impl RawVec { /// of allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), @@ -328,7 +322,6 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] pub(crate) fn reserve(&mut self, len: usize, additional: usize) { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout unsafe { self.inner.reserve(len, additional, T::LAYOUT) } @@ -338,7 +331,6 @@ impl RawVec { /// caller to ensure `len == self.capacity()`. #[cfg(not(no_global_oom_handling))] #[inline(never)] - #[track_caller] pub(crate) fn grow_one(&mut self) { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout unsafe { self.inner.grow_one(T::LAYOUT) } @@ -372,7 +364,6 @@ impl RawVec { /// /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] - #[track_caller] pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout unsafe { self.inner.reserve_exact(len, additional, T::LAYOUT) } @@ -399,7 +390,6 @@ impl RawVec { /// /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] - #[track_caller] #[inline] pub(crate) fn shrink_to_fit(&mut self, cap: usize) { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout @@ -425,7 +415,6 @@ impl RawVecInner
{ #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { Ok(this) => { @@ -450,7 +439,6 @@ impl RawVecInner { #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { Ok(res) => res, @@ -553,7 +541,6 @@ impl RawVecInner { /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] unsafe fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { // Callers expect this function to be very cheap when there is already sufficient capacity. // Therefore, we move all the resizing and error-handling logic from grow_amortized and @@ -585,7 +572,6 @@ impl RawVecInner { /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] unsafe fn grow_one(&mut self, elem_layout: Layout) { // SAFETY: Precondition passed to caller if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } { @@ -621,7 +607,6 @@ impl RawVecInner { /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment #[cfg(not(no_global_oom_handling))] - #[track_caller] unsafe fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { // SAFETY: Precondition passed to caller if let Err(err) = unsafe { self.try_reserve_exact(len, additional, elem_layout) } { @@ -659,7 +644,6 @@ impl RawVecInner { /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { if let Err(err) = unsafe { self.shrink(cap, elem_layout) } { handle_error(err); @@ -872,7 +856,6 @@ where #[cfg(not(no_global_oom_handling))] #[cold] #[optimize(size)] -#[track_caller] fn handle_error(e: TryReserveError) -> ! { match e.kind() { CapacityOverflow => capacity_overflow(), diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index e669c4708ad09..ae30cabf5af5b 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1105,7 +1105,6 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("append", "push")] #[rustc_diagnostic_item = "string_push_str"] @@ -1208,7 +1207,6 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { self.vec.reserve(additional) @@ -1260,7 +1258,6 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn reserve_exact(&mut self, additional: usize) { self.vec.reserve_exact(additional) } @@ -1356,7 +1353,6 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { self.vec.shrink_to_fit() @@ -1384,7 +1380,6 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] #[stable(feature = "shrink_to", since = "1.56.0")] pub fn shrink_to(&mut self, min_capacity: usize) { self.vec.shrink_to(min_capacity) @@ -1406,7 +1401,6 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn push(&mut self, ch: char) { let len = self.len(); let ch_len = ch.len_utf8(); @@ -2115,7 +2109,6 @@ impl String { #[stable(feature = "box_str", since = "1.4.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] - #[track_caller] pub fn into_boxed_str(self) -> Box { let slice = self.vec.into_boxed_slice(); unsafe { from_boxed_utf8_unchecked(slice) } @@ -2293,7 +2286,6 @@ impl Error for FromUtf16Error {} #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for String { - #[track_caller] fn clone(&self) -> Self { String { vec: self.vec.clone() } } @@ -2302,7 +2294,6 @@ impl Clone for String { /// /// This method is preferred over simply assigning `source.clone()` to `self`, /// as it avoids reallocation if possible. - #[track_caller] fn clone_from(&mut self, source: &Self) { self.vec.clone_from(&source.vec); } @@ -2477,13 +2468,11 @@ impl<'a> Extend> for String { #[unstable(feature = "ascii_char", issue = "110998")] impl Extend for String { #[inline] - #[track_caller] fn extend>(&mut self, iter: I) { self.vec.extend(iter.into_iter().map(|c| c.to_u8())); } #[inline] - #[track_caller] fn extend_one(&mut self, c: core::ascii::Char) { self.vec.push(c.to_u8()); } @@ -2493,13 +2482,11 @@ impl Extend for String { #[unstable(feature = "ascii_char", issue = "110998")] impl<'a> Extend<&'a core::ascii::Char> for String { #[inline] - #[track_caller] fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } #[inline] - #[track_caller] fn extend_one(&mut self, c: &'a core::ascii::Char) { self.vec.push(c.to_u8()); } diff --git a/library/alloc/src/vec/cow.rs b/library/alloc/src/vec/cow.rs index 4deb35efffc14..c18091705a636 100644 --- a/library/alloc/src/vec/cow.rs +++ b/library/alloc/src/vec/cow.rs @@ -58,7 +58,6 @@ impl<'a, T> FromIterator for Cow<'a, [T]> where T: Clone, { - #[track_caller] fn from_iter>(it: I) -> Cow<'a, [T]> { Cow::Owned(FromIterator::from_iter(it)) } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index b98a118048f2d..8a7c0b92eccf6 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -229,7 +229,6 @@ where I: Iterator + InPlaceCollect, ::Source: AsVecIntoIter, { - #[track_caller] default fn from_iter(iterator: I) -> Self { // Select the implementation in const eval to avoid codegen of the dead branch to improve compile times. let fun: fn(I) -> Vec = const { @@ -247,7 +246,6 @@ where } } -#[track_caller] fn from_iter_in_place(mut iterator: I) -> Vec where I: Iterator + InPlaceCollect, diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index ebdb86f98a857..81b405253a1b4 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -515,7 +515,6 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[rustc_diagnostic_item = "vec_with_capacity"] - #[track_caller] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) } @@ -926,7 +925,6 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - #[track_caller] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } @@ -1335,7 +1333,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] #[rustc_diagnostic_item = "vec_reserve"] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); @@ -1367,7 +1364,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn reserve_exact(&mut self, additional: usize) { self.buf.reserve_exact(self.len, additional); } @@ -1471,7 +1467,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] #[inline] pub fn shrink_to_fit(&mut self) { // The capacity is never less than the length, and there's nothing to do when @@ -1502,7 +1497,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "shrink_to", since = "1.56.0")] - #[track_caller] pub fn shrink_to(&mut self, min_capacity: usize) { if self.capacity() > min_capacity { self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); @@ -1536,7 +1530,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] pub fn into_boxed_slice(mut self) -> Box<[T], A> { unsafe { self.shrink_to_fit(); @@ -2021,7 +2014,6 @@ impl Vec { pub fn swap_remove(&mut self, index: usize) -> T { #[cold] #[cfg_attr(not(panic = "immediate-abort"), inline(never))] - #[track_caller] #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { panic!("swap_remove index (is {index}) should be < len (is {len})"); @@ -2540,7 +2532,6 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push_back", "put", "append")] - #[track_caller] pub fn push(&mut self, value: T) { let _ = self.push_mut(value); } @@ -2617,7 +2608,6 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] #[unstable(feature = "push_mut", issue = "135974")] - #[track_caller] #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"] pub fn push_mut(&mut self, value: T) -> &mut T { // Inform codegen that the length does not change across grow_one(). @@ -2765,7 +2755,6 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "append", since = "1.4.0")] - #[track_caller] pub fn append(&mut self, other: &mut Self) { unsafe { self.append_elements(other.as_slice() as _); @@ -2776,7 +2765,6 @@ impl Vec { /// Appends elements to `self` from other buffer. #[cfg(not(no_global_oom_handling))] #[inline] - #[track_caller] unsafe fn append_elements(&mut self, other: *const [T]) { let count = other.len(); self.reserve(count); @@ -3011,7 +2999,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize_with", since = "1.33.0")] - #[track_caller] pub fn resize_with(&mut self, new_len: usize, f: F) where F: FnMut() -> T, @@ -3276,7 +3263,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize", since = "1.5.0")] - #[track_caller] pub fn resize(&mut self, new_len: usize, value: T) { let len = self.len(); @@ -3307,7 +3293,6 @@ impl Vec { /// [`extend`]: Vec::extend #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_slice", since = "1.6.0")] - #[track_caller] pub fn extend_from_slice(&mut self, other: &[T]) { self.spec_extend(other.iter()) } @@ -3338,7 +3323,6 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_within", since = "1.53.0")] - #[track_caller] pub fn extend_from_within(&mut self, src: R) where R: RangeBounds, @@ -3399,7 +3383,6 @@ impl Vec<[T; N], A> { impl Vec { #[cfg(not(no_global_oom_handling))] - #[track_caller] /// Extend the vector by `n` clones of value. fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); @@ -3460,7 +3443,6 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_from_elem"] -#[track_caller] pub fn from_elem(elem: T, n: usize) -> Vec { ::from_elem(elem, n, Global) } @@ -3468,7 +3450,6 @@ pub fn from_elem(elem: T, n: usize) -> Vec { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] -#[track_caller] pub fn from_elem_in(elem: T, n: usize, alloc: A) -> Vec { ::from_elem(elem, n, alloc) } @@ -3559,7 +3540,6 @@ unsafe impl ops::DerefPure for Vec {} #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { - #[track_caller] fn clone(&self) -> Self { let alloc = self.allocator().clone(); <[T]>::to_vec_in(&**self, alloc) @@ -3587,7 +3567,6 @@ impl Clone for Vec { /// // And no reallocation occurred /// assert_eq!(yp, y.as_ptr()); /// ``` - #[track_caller] fn clone_from(&mut self, source: &Self) { crate::slice::SpecCloneIntoVec::clone_into(source.as_slice(), self); } @@ -3678,7 +3657,6 @@ impl, A: Allocator> IndexMut for Vec { #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator for Vec { #[inline] - #[track_caller] fn from_iter>(iter: I) -> Vec { >::from_iter(iter.into_iter()) } @@ -3747,19 +3725,16 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec { #[stable(feature = "rust1", since = "1.0.0")] impl Extend for Vec { #[inline] - #[track_caller] fn extend>(&mut self, iter: I) { >::spec_extend(self, iter.into_iter()) } #[inline] - #[track_caller] fn extend_one(&mut self, item: T) { self.push(item); } #[inline] - #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -3779,7 +3754,6 @@ impl Vec { // leaf method to which various SpecFrom/SpecExtend implementations delegate when // they have no further optimizations to apply #[cfg(not(no_global_oom_handling))] - #[track_caller] fn extend_desugared>(&mut self, mut iterator: I) { // This is the case for a general iterator. // @@ -3807,7 +3781,6 @@ impl Vec { // specific extend for `TrustedLen` iterators, called both by the specializations // and internal places where resolving specialization makes compilation slower #[cfg(not(no_global_oom_handling))] - #[track_caller] fn extend_trusted(&mut self, iterator: impl iter::TrustedLen) { let (low, high) = iterator.size_hint(); if let Some(additional) = high { @@ -3985,19 +3958,16 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "extend_ref", since = "1.2.0")] impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec { - #[track_caller] fn extend>(&mut self, iter: I) { self.spec_extend(iter.into_iter()) } #[inline] - #[track_caller] fn extend_one(&mut self, &item: &'a T) { self.push(item); } #[inline] - #[track_caller] fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } @@ -4108,7 +4078,6 @@ impl From<&[T]> for Vec { /// ``` /// assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[track_caller] fn from(s: &[T]) -> Vec { s.to_vec() } @@ -4124,7 +4093,6 @@ impl From<&mut [T]> for Vec { /// ``` /// assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[track_caller] fn from(s: &mut [T]) -> Vec { s.to_vec() } @@ -4140,7 +4108,6 @@ impl From<&[T; N]> for Vec { /// ``` /// assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]); /// ``` - #[track_caller] fn from(s: &[T; N]) -> Vec { Self::from(s.as_slice()) } @@ -4156,7 +4123,6 @@ impl From<&mut [T; N]> for Vec { /// ``` /// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); /// ``` - #[track_caller] fn from(s: &mut [T; N]) -> Vec { Self::from(s.as_mut_slice()) } @@ -4172,7 +4138,6 @@ impl From<[T; N]> for Vec { /// ``` /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); /// ``` - #[track_caller] fn from(s: [T; N]) -> Vec { <[T]>::into_vec(Box::new(s)) } @@ -4197,7 +4162,6 @@ where /// let b: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]); /// assert_eq!(Vec::from(o), Vec::from(b)); /// ``` - #[track_caller] fn from(s: Cow<'a, [T]>) -> Vec { s.into_owned() } @@ -4244,7 +4208,6 @@ impl From> for Box<[T], A> { /// /// assert_eq!(Box::from(vec), vec![1, 2, 3].into_boxed_slice()); /// ``` - #[track_caller] fn from(v: Vec) -> Self { v.into_boxed_slice() } @@ -4260,7 +4223,6 @@ impl From<&str> for Vec { /// ``` /// assert_eq!(Vec::from("123"), vec![b'1', b'2', b'3']); /// ``` - #[track_caller] fn from(s: &str) -> Vec { From::from(s.as_bytes()) } diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index b98db669059f9..7085bceef5baa 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -6,7 +6,6 @@ use crate::alloc::Allocator; // Specialization trait used for Vec::extend pub(super) trait SpecExtend { - #[track_caller] fn spec_extend(&mut self, iter: I); } @@ -14,7 +13,6 @@ impl SpecExtend for Vec where I: Iterator, { - #[track_caller] default fn spec_extend(&mut self, iter: I) { self.extend_desugared(iter) } @@ -24,14 +22,12 @@ impl SpecExtend for Vec where I: TrustedLen, { - #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.extend_trusted(iterator) } } impl SpecExtend> for Vec { - #[track_caller] fn spec_extend(&mut self, mut iterator: IntoIter) { unsafe { self.append_elements(iterator.as_slice() as _); @@ -45,7 +41,6 @@ where I: Iterator, T: Clone, { - #[track_caller] default fn spec_extend(&mut self, iterator: I) { self.spec_extend(iterator.cloned()) } @@ -55,7 +50,6 @@ impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec) { let slice = iterator.as_slice(); unsafe { self.append_elements(slice) }; diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index 6c7b4d89f2da7..96d701e15d487 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -10,7 +10,6 @@ pub(super) trait SpecFromElem: Sized { } impl SpecFromElem for T { - #[track_caller] default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { let mut v = Vec::with_capacity_in(n, alloc); v.extend_with(n, elem); @@ -20,7 +19,6 @@ impl SpecFromElem for T { impl SpecFromElem for T { #[inline] - #[track_caller] default fn from_elem(elem: T, n: usize, alloc: A) -> Vec { if elem.is_zero() { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; @@ -33,7 +31,6 @@ impl SpecFromElem for T { impl SpecFromElem for i8 { #[inline] - #[track_caller] fn from_elem(elem: i8, n: usize, alloc: A) -> Vec { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; @@ -49,7 +46,6 @@ impl SpecFromElem for i8 { impl SpecFromElem for u8 { #[inline] - #[track_caller] fn from_elem(elem: u8, n: usize, alloc: A) -> Vec { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index ad7688e1c59f0..e1f0b639bdfd6 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -29,14 +29,12 @@ impl SpecFromIter for Vec where I: Iterator, { - #[track_caller] default fn from_iter(iterator: I) -> Self { SpecFromIterNested::from_iter(iterator) } } impl SpecFromIter> for Vec { - #[track_caller] fn from_iter(iterator: IntoIter) -> Self { // A common case is passing a vector into a function which immediately // re-collects into a vector. We can short circuit this if the IntoIter diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index 22eed238798cf..77f7761d22f95 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -15,7 +15,6 @@ impl SpecFromIterNested for Vec where I: Iterator, { - #[track_caller] default fn from_iter(mut iterator: I) -> Self { // Unroll the first iteration, as the vector is going to be // expanded on this iteration in every case when the iterable is not @@ -48,7 +47,6 @@ impl SpecFromIterNested for Vec where I: TrustedLen, { - #[track_caller] fn from_iter(iterator: I) -> Self { let mut vector = match iterator.size_hint() { (_, Some(upper)) => Vec::with_capacity(upper), diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index ed1a0dda76d29..d571e35828aeb 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -52,7 +52,6 @@ impl ExactSizeIterator for Splice<'_, I, A> {} #[stable(feature = "vec_splice", since = "1.21.0")] impl Drop for Splice<'_, I, A> { - #[track_caller] fn drop(&mut self) { self.drain.by_ref().for_each(drop); // At this point draining is done and the only remaining tasks are splicing @@ -124,7 +123,6 @@ impl Drain<'_, T, A> { } /// Makes room for inserting more elements before the tail. - #[track_caller] unsafe fn move_tail(&mut self, additional: usize) { let vec = unsafe { self.vec.as_mut() }; let len = self.tail_start + self.tail_len; diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index d28ab86418345..bd74e96fd8cc7 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,4 +1,4 @@ -thread 'main' ($TID) panicked at $DIR/panic-location.rs:LL:CC: +thread 'main' ($TID) panicked at library/alloc/src/raw_vec/mod.rs:LL:CC: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace From c98b3b2da46146956b982e4ae1747c3614a461fd Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Thu, 25 Sep 2025 13:57:59 -0400 Subject: [PATCH 0882/1900] const_caller_location: edit FIXME to explain use of `DUMMY_SP` Co-authored-by: Ralf Jung --- compiler/rustc_const_eval/src/util/caller_location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 5249b32eca469..4e7c8310007b8 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -61,7 +61,7 @@ pub(crate) fn const_caller_location_provider( trace!("const_caller_location: {}:{}:{}", file, line, col); let mut ecx = mk_eval_cx_to_read_const_val( tcx, - rustc_span::DUMMY_SP, // FIXME: use a proper span here? + rustc_span::DUMMY_SP, // This interpreter cannot fail, so the span is irrelevant. ty::TypingEnv::fully_monomorphized(), CanAccessMutGlobal::No, ); From b9037f97e38acc698af1260cad5d31ca07ac616a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 25 Sep 2025 13:04:34 -0700 Subject: [PATCH 0883/1900] rustdoc-search: use the same ID for entry and path to same item This decreases the size of the compiler-doc from 57MiB to 56MiB. --- src/librustdoc/html/render/search_index.rs | 32 ++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 3ffce61f7c613..74cf2b1812330 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -241,6 +241,26 @@ impl SerializedSearchIndex { self.alias_pointers.push(alias_pointer); index } + fn add_entry(&mut self, name: Symbol, entry_data: EntryData, desc: String) -> usize { + let fqp = if let Some(module_path_index) = entry_data.module_path { + let mut fqp = self.path_data[module_path_index].as_ref().unwrap().module_path.clone(); + fqp.push(Symbol::intern(&self.names[module_path_index])); + fqp.push(name); + fqp + } else { + vec![name] + }; + if let Some(&other_path) = self.crate_paths_index.get(&(entry_data.ty, fqp)) + && self.entry_data[other_path].is_none() + && self.descs[other_path].is_empty() + { + self.entry_data[other_path] = Some(entry_data); + self.descs[other_path] = desc; + other_path + } else { + self.push(name.as_str().to_string(), None, Some(entry_data), desc, None, None, None) + } + } fn push_path(&mut self, name: String, path_data: PathData) -> usize { self.push(name, Some(path_data), None, String::new(), None, None, None) } @@ -1516,10 +1536,9 @@ pub(crate) fn build_index( .as_ref() .map(|path| serialized_index.get_id_by_module_path(path)); - let new_entry_id = serialized_index.push( - item.name.as_str().to_string(), - None, - Some(EntryData { + let new_entry_id = serialized_index.add_entry( + item.name, + EntryData { ty: item.ty, parent: item.parent_idx, module_path, @@ -1538,11 +1557,8 @@ pub(crate) fn build_index( None }, krate: crate_idx, - }), + }, item.desc.to_string(), - None, // filled in after all the types have been indexed - None, - None, ); // Aliases From e0f22df8b9376c86496eb7facaadd7b27ef6e4a8 Mon Sep 17 00:00:00 2001 From: Oblarg Date: Thu, 25 Sep 2025 12:11:11 -0400 Subject: [PATCH 0884/1900] fix transcriber error in declarative macros for negative integer literal const generic params --- .../crates/mbe/src/expander/transcriber.rs | 14 ++- .../rust-analyzer/crates/mbe/src/tests.rs | 97 +++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 2c046df10f5e5..3e4ab8bdc1d8d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -401,7 +401,19 @@ fn expand_var( let sub = sub.strip_invisible(); let mut span = id; marker(&mut span); - let wrap_in_parens = !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)]) + + // Check if this is a simple negative literal (MINUS + LITERAL) + // that should not be wrapped in parentheses + let is_negative_literal = matches!( + sub.flat_tokens(), + [ + tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '-', .. })), + tt::TokenTree::Leaf(tt::Leaf::Literal(_)) + ] + ); + + let wrap_in_parens = !is_negative_literal + && !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)]) && sub.try_into_subtree().is_none_or(|it| { it.top_subtree().delimiter.kind == tt::DelimiterKind::Invisible }); diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 56034516ef3b2..589e169d30a3d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -476,3 +476,100 @@ fn minus_belongs_to_literal() { --"#]], ); } + +#[test] +fn negative_literals_in_const_generics() { + // Test that negative literals work correctly in declarative macros + // when used as const generic arguments. The issue was that expressions + // like -1 would be wrapped in parentheses, creating invalid syntax + // Foo::<(-1)> instead of the correct Foo::<-1>. + let decl = r#" +($val:expr) => { + struct Foo { + pub value: i16, + } + + impl Foo { + pub fn new(value: i16) -> Self { + Self { value } + } + } + + Foo::<$val>::new($val) +}; +"#; + let check = |args, expect| check(Edition::CURRENT, Edition::CURRENT, decl, args, expect); + + // Test negative integer literal - should produce Foo::<-1>, not Foo::<(-1)> + check( + "-1", + expect![[r#" + SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024 + IDENT struct 0:Root[0000, 0]@22..28#ROOT2024 + IDENT Foo 0:Root[0000, 0]@29..32#ROOT2024 + PUNCH < [alone] 0:Root[0000, 0]@32..33#ROOT2024 + IDENT const 0:Root[0000, 0]@33..38#ROOT2024 + IDENT I 0:Root[0000, 0]@39..40#ROOT2024 + PUNCH : [alone] 0:Root[0000, 0]@40..41#ROOT2024 + IDENT i16 0:Root[0000, 0]@42..45#ROOT2024 + PUNCH > [alone] 0:Root[0000, 0]@45..46#ROOT2024 + SUBTREE {} 0:Root[0000, 0]@47..48#ROOT2024 0:Root[0000, 0]@77..78#ROOT2024 + IDENT pub 0:Root[0000, 0]@57..60#ROOT2024 + IDENT value 0:Root[0000, 0]@61..66#ROOT2024 + PUNCH : [alone] 0:Root[0000, 0]@66..67#ROOT2024 + IDENT i16 0:Root[0000, 0]@68..71#ROOT2024 + PUNCH , [alone] 0:Root[0000, 0]@71..72#ROOT2024 + IDENT impl 0:Root[0000, 0]@84..88#ROOT2024 + PUNCH < [alone] 0:Root[0000, 0]@88..89#ROOT2024 + IDENT const 0:Root[0000, 0]@89..94#ROOT2024 + IDENT I 0:Root[0000, 0]@95..96#ROOT2024 + PUNCH : [alone] 0:Root[0000, 0]@96..97#ROOT2024 + IDENT i16 0:Root[0000, 0]@98..101#ROOT2024 + PUNCH > [alone] 0:Root[0000, 0]@101..102#ROOT2024 + IDENT Foo 0:Root[0000, 0]@103..106#ROOT2024 + PUNCH < [alone] 0:Root[0000, 0]@106..107#ROOT2024 + IDENT I 0:Root[0000, 0]@107..108#ROOT2024 + PUNCH > [alone] 0:Root[0000, 0]@108..109#ROOT2024 + SUBTREE {} 0:Root[0000, 0]@110..111#ROOT2024 0:Root[0000, 0]@194..195#ROOT2024 + IDENT pub 0:Root[0000, 0]@120..123#ROOT2024 + IDENT fn 0:Root[0000, 0]@124..126#ROOT2024 + IDENT new 0:Root[0000, 0]@127..130#ROOT2024 + SUBTREE () 0:Root[0000, 0]@130..131#ROOT2024 0:Root[0000, 0]@141..142#ROOT2024 + IDENT value 0:Root[0000, 0]@131..136#ROOT2024 + PUNCH : [alone] 0:Root[0000, 0]@136..137#ROOT2024 + IDENT i16 0:Root[0000, 0]@138..141#ROOT2024 + PUNCH - [joint] 0:Root[0000, 0]@143..144#ROOT2024 + PUNCH > [alone] 0:Root[0000, 0]@144..145#ROOT2024 + IDENT Self 0:Root[0000, 0]@146..150#ROOT2024 + SUBTREE {} 0:Root[0000, 0]@151..152#ROOT2024 0:Root[0000, 0]@188..189#ROOT2024 + IDENT Self 0:Root[0000, 0]@165..169#ROOT2024 + SUBTREE {} 0:Root[0000, 0]@170..171#ROOT2024 0:Root[0000, 0]@178..179#ROOT2024 + IDENT value 0:Root[0000, 0]@172..177#ROOT2024 + IDENT Foo 0:Root[0000, 0]@201..204#ROOT2024 + PUNCH : [joint] 0:Root[0000, 0]@204..205#ROOT2024 + PUNCH : [joint] 0:Root[0000, 0]@205..206#ROOT2024 + PUNCH < [joint] 0:Root[0000, 0]@206..207#ROOT2024 + PUNCH - [alone] 1:Root[0000, 0]@0..1#ROOT2024 + LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024 + PUNCH > [joint] 0:Root[0000, 0]@211..212#ROOT2024 + PUNCH : [joint] 0:Root[0000, 0]@212..213#ROOT2024 + PUNCH : [alone] 0:Root[0000, 0]@213..214#ROOT2024 + IDENT new 0:Root[0000, 0]@214..217#ROOT2024 + SUBTREE () 0:Root[0000, 0]@217..218#ROOT2024 0:Root[0000, 0]@222..223#ROOT2024 + PUNCH - [alone] 1:Root[0000, 0]@0..1#ROOT2024 + LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024 + + struct Foo{ + pub value:i16, + } + impl Foo{ + pub fn new(value:i16) -> Self { + Self { + value + } + } + + } + Foo::<-1>::new(-1)"#]], + ); +} From 8daad494b11ae5d0e5282be485625687bcf27e37 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 25 Sep 2025 14:13:26 -0700 Subject: [PATCH 0885/1900] rustdoc: put the toolbar on the all item index --- src/librustdoc/html/render/mod.rs | 7 ++++++- src/librustdoc/html/render/write_shared.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6d684449b6d2e..bb494de19d9bd 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -601,7 +601,12 @@ impl AllTypes { } fmt::from_fn(|f| { - f.write_str("

List of all items

")?; + f.write_str( + "
\ +

List of all items

\ + \ +
", + )?; // Note: print_entries does not escape the title, because we know the current set of titles // doesn't require escaping. print_entries(&self.structs, ItemSection::Structs).fmt(f)?; diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index e37a5246a7684..3a1db805d01cf 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -386,8 +386,13 @@ impl CratesIndexPart { let layout = &cx.shared.layout; let style_files = &cx.shared.style_files; const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this - let content = - format!("

List of all crates

    {DELIMITER}
"); + let content = format!( + "
\ +

List of all crates

\ + \ +
\ +
    {DELIMITER}
" + ); let template = layout::render(layout, &page, "", content, style_files); SortedTemplate::from_template(&template, DELIMITER) .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page") From 0ede3fe48c0d373d3673459218e1b9e7eaa667b8 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:34:20 -0500 Subject: [PATCH 0886/1900] std: fix warning in VEXos stdio module --- library/std/src/sys/stdio/vexos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/stdio/vexos.rs b/library/std/src/sys/stdio/vexos.rs index 1f2251c6421d8..9a391feb7a8a1 100644 --- a/library/std/src/sys/stdio/vexos.rs +++ b/library/std/src/sys/stdio/vexos.rs @@ -13,7 +13,7 @@ impl Stdin { } impl io::Read for Stdin { - fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut count = 0; for out_byte in buf.iter_mut() { From 9dce3e8556022599c1323cdad6a0d87d6991c8b5 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 25 Sep 2025 15:16:16 -0700 Subject: [PATCH 0887/1900] rustdoc-search: add descriptive comment to space-saving hack --- src/librustdoc/html/render/search_index.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 74cf2b1812330..7b21c68d2e949 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -241,6 +241,9 @@ impl SerializedSearchIndex { self.alias_pointers.push(alias_pointer); index } + /// Add potential search result to the database and return the row ID. + /// + /// The returned ID can be used to attach more data to the search result. fn add_entry(&mut self, name: Symbol, entry_data: EntryData, desc: String) -> usize { let fqp = if let Some(module_path_index) = entry_data.module_path { let mut fqp = self.path_data[module_path_index].as_ref().unwrap().module_path.clone(); @@ -250,6 +253,11 @@ impl SerializedSearchIndex { } else { vec![name] }; + // If a path with the same name already exists, but no entry does, + // we can fill in the entry without having to allocate a new row ID. + // + // Because paths and entries both share the same index, using the same + // ID saves space by making the tree smaller. if let Some(&other_path) = self.crate_paths_index.get(&(entry_data.ty, fqp)) && self.entry_data[other_path].is_none() && self.descs[other_path].is_empty() From 7a9b6d94d41720a206d0e52e4827bac45d9a7bc3 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Thu, 25 Sep 2025 18:12:33 -0400 Subject: [PATCH 0888/1900] PassWrapper: update for new PGOOptions args in LLVM 22 This changed in upstream change a5569b4bd7f8. @rustbot label llvm-main --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 013d68fa3e485..136b6e9bf50fd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -573,21 +573,37 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + PGOGenPath, "", "", "", PGOOptions::IRInstr, PGOOptions::NoCSAction, +#else PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + PGOUsePath, "", "", "", PGOOptions::IRUse, PGOOptions::NoCSAction, +#else PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOSampleUsePath) { PGOOpt = +#if LLVM_VERSION_GE(22, 0) + PGOOptions(PGOSampleUsePath, "", "", "", PGOOptions::SampleUse, +#else PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, +#endif PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (DebugInfoForProfiling) { PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + "", "", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, +#else "", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } From 51ae86dec97f4db86f624f609c41f6bf5e620301 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 25 Sep 2025 15:23:49 -0700 Subject: [PATCH 0889/1900] rustdoc-search: add test case for all/index.html search --- tests/rustdoc-gui/search-title.goml | 12 ++++++++++++ tests/rustdoc-gui/src/test_docs/lib.rs | 1 + 2 files changed, 13 insertions(+) diff --git a/tests/rustdoc-gui/search-title.goml b/tests/rustdoc-gui/search-title.goml index 83321a05f2bbb..5808ed845a3b1 100644 --- a/tests/rustdoc-gui/search-title.goml +++ b/tests/rustdoc-gui/search-title.goml @@ -20,3 +20,15 @@ assert-document-property: {"title": '"another one" Search - Rust'} press-key: "Escape" assert-document-property: {"title": |title|} + +// check that all.html does it correctly, too. +go-to: "file://" + |DOC_PATH| + "/test_docs/all.html" +assert-document-property: {"title": "List of all items in this crate"} +call-function: ("perform-search", {"query": "verify"}) +assert-document-property: {"title": '"verify" Search - Rust'} + +// check that index.html does it correctly, too. +go-to: "file://" + |DOC_PATH| + "/index.html" +assert-document-property: {"title": "Index of crates"} +call-function: ("perform-search", {"query": "verify"}) +assert-document-property: {"title": '"verify" Search - Rust'} diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 42f2fbd93b1ee..08523d0df2f2e 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -1,3 +1,4 @@ +//@ compile-flags: --enable-index-page -Z unstable-options //! The point of this crate is to be able to have enough different "kinds" of //! documentation generated so we can test each different features. #![doc(html_playground_url="https://play.rust-lang.org/")] From 1a37374973f2d57c7c1c18a8371aa99607adfad8 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 21 Sep 2025 03:46:07 +0000 Subject: [PATCH 0890/1900] JumpThreading: Avoid computing dominators to identify loop headers. --- .../rustc_mir_transform/src/jump_threading.rs | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index f9e642e28ebd7..c69660108d9a3 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -84,7 +84,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { body, arena, map: Map::new(tcx, body, Some(MAX_PLACES)), - loop_headers: loop_headers(body), + maybe_loop_headers: maybe_loop_headers(body), opportunities: Vec::new(), }; @@ -100,7 +100,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { // Verify that we do not thread through a loop header. for to in opportunities.iter() { - assert!(to.chain.iter().all(|&block| !finder.loop_headers.contains(block))); + assert!(to.chain.iter().all(|&block| !finder.maybe_loop_headers.contains(block))); } OpportunitySet::new(body, opportunities).apply(body); } @@ -124,7 +124,7 @@ struct TOFinder<'a, 'tcx> { ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, map: Map<'tcx>, - loop_headers: DenseBitSet, + maybe_loop_headers: DenseBitSet, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec, @@ -190,7 +190,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { #[instrument(level = "trace", skip(self))] fn start_from_switch(&mut self, bb: BasicBlock) { let bbdata = &self.body[bb]; - if bbdata.is_cleanup || self.loop_headers.contains(bb) { + if bbdata.is_cleanup || self.maybe_loop_headers.contains(bb) { return; } let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return }; @@ -235,7 +235,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { depth: usize, ) { // Do not thread through loop headers. - if self.loop_headers.contains(bb) { + if self.maybe_loop_headers.contains(bb) { return; } @@ -833,20 +833,26 @@ enum Update { Decr, } -/// Compute the set of loop headers in the given body. We define a loop header as a block which has -/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. -/// But if the CFG is already irreducible, there is no point in trying much harder. -/// is already irreducible. -fn loop_headers(body: &Body<'_>) -> DenseBitSet { - let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); - let dominators = body.basic_blocks.dominators(); - // Only visit reachable blocks. - for (bb, bbdata) in traversal::preorder(body) { +/// Compute the set of loop headers in the given body. A loop header is usually defined as a block +/// which dominates one of its predecessors. This definition is only correct for reducible CFGs. +/// However, computing dominators is expensive, so we approximate according to the post-order +/// traversal order. A loop header for us is a block which is visited after its predecessor in +/// post-order. This is ok as we mostly need a heuristic. +fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet { + let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); + let mut visited = DenseBitSet::new_empty(body.basic_blocks.len()); + for (bb, bbdata) in traversal::postorder(body) { + let _new = visited.insert(bb); + debug_assert!(_new); + + // Post-order means we visit successors before the block for acyclic CFGs. + // If the successor is not visited yet, consider it a loop header. for succ in bbdata.terminator().successors() { - if dominators.dominates(succ, bb) { - loop_headers.insert(succ); + if !visited.contains(succ) { + maybe_loop_headers.insert(succ); } } } - loop_headers + + maybe_loop_headers } From 5de617e71f7ab7f70f13381033c09f1c6ad55e0f Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 25 Sep 2025 23:57:59 -0400 Subject: [PATCH 0891/1900] Rename ui test items --- .../duplicate-bound-err.rs | 18 ++-- .../duplicate-bound-err.stderr | 24 ++--- .../associated-type-bounds/duplicate-bound.rs | 90 +++++++++---------- 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.rs b/tests/ui/associated-type-bounds/duplicate-bound-err.rs index 50db5de7ca768..01cc05f2545f1 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.rs +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.rs @@ -5,31 +5,31 @@ use std::iter; -fn frpit1() -> impl Iterator { +fn rpit1() -> impl Iterator { iter::empty() //~^ ERROR type annotations needed } -fn frpit2() -> impl Iterator { +fn rpit2() -> impl Iterator { iter::empty() //~^ ERROR type annotations needed } -fn frpit3() -> impl Iterator { +fn rpit3() -> impl Iterator { iter::empty() //~^ ERROR type annotations needed } -type ETAI1> = impl Copy; +type Tait1> = impl Copy; //~^ ERROR unconstrained opaque type -type ETAI2> = impl Copy; +type Tait2> = impl Copy; //~^ ERROR unconstrained opaque type -type ETAI3> = impl Copy; +type Tait3> = impl Copy; //~^ ERROR unconstrained opaque type -type ETAI4 = impl Iterator; +type Tait4 = impl Iterator; //~^ ERROR unconstrained opaque type -type ETAI5 = impl Iterator; +type Tait5 = impl Iterator; //~^ ERROR unconstrained opaque type -type ETAI6 = impl Iterator; +type Tait6 = impl Iterator; //~^ ERROR unconstrained opaque type fn mismatch() -> impl Iterator { diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr index 6c1dc03676c79..1737d0dc5a385 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -34,50 +34,50 @@ LL | iter::empty::() error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:21:51 | -LL | type ETAI1> = impl Copy; +LL | type Tait1> = impl Copy; | ^^^^^^^^^ | - = note: `ETAI1` must be used in combination with a concrete type within the same crate + = note: `Tait1` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:23:51 | -LL | type ETAI2> = impl Copy; +LL | type Tait2> = impl Copy; | ^^^^^^^^^ | - = note: `ETAI2` must be used in combination with a concrete type within the same crate + = note: `Tait2` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:25:57 | -LL | type ETAI3> = impl Copy; +LL | type Tait3> = impl Copy; | ^^^^^^^^^ | - = note: `ETAI3` must be used in combination with a concrete type within the same crate + = note: `Tait3` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:28:14 | -LL | type ETAI4 = impl Iterator; +LL | type Tait4 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `ETAI4` must be used in combination with a concrete type within the same crate + = note: `Tait4` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:30:14 | -LL | type ETAI5 = impl Iterator; +LL | type Tait5 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `ETAI5` must be used in combination with a concrete type within the same crate + = note: `Tait5` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/duplicate-bound-err.rs:32:14 | -LL | type ETAI6 = impl Iterator; +LL | type Tait6 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `ETAI6` must be used in combination with a concrete type within the same crate + = note: `Tait6` must be used in combination with a concrete type within the same crate error[E0277]: `*const ()` cannot be sent between threads safely --> $DIR/duplicate-bound-err.rs:35:18 diff --git a/tests/ui/associated-type-bounds/duplicate-bound.rs b/tests/ui/associated-type-bounds/duplicate-bound.rs index 97b2b3905a55e..696710d76f6df 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound.rs +++ b/tests/ui/associated-type-bounds/duplicate-bound.rs @@ -7,84 +7,84 @@ use std::iter; use std::mem::ManuallyDrop; -struct SI1> { +struct Si1> { f: T, } -struct SI2> { +struct Si2> { f: T, } -struct SI3> { +struct Si3> { f: T, } -struct SW1 +struct Sw1 where T: Iterator, { f: T, } -struct SW2 +struct Sw2 where T: Iterator, { f: T, } -struct SW3 +struct Sw3 where T: Iterator, { f: T, } -enum EI1> { +enum Ei1> { V(T), } -enum EI2> { +enum Ei2> { V(T), } -enum EI3> { +enum Ei3> { V(T), } -enum EW1 +enum Ew1 where T: Iterator, { V(T), } -enum EW2 +enum Ew2 where T: Iterator, { V(T), } -enum EW3 +enum Ew3 where T: Iterator, { V(T), } -union UI1> { +union Ui1> { f: ManuallyDrop, } -union UI2> { +union Ui2> { f: ManuallyDrop, } -union UI3> { +union Ui3> { f: ManuallyDrop, } -union UW1 +union Uw1 where T: Iterator, { f: ManuallyDrop, } -union UW2 +union Uw2 where T: Iterator, { f: ManuallyDrop, } -union UW3 +union Uw3 where T: Iterator, { @@ -110,78 +110,78 @@ where { } -fn frpit1() -> impl Iterator { +fn rpit1() -> impl Iterator { iter::empty::() } -fn frpit2() -> impl Iterator { +fn rpit2() -> impl Iterator { iter::empty::() } -fn frpit3() -> impl Iterator { +fn rpit3() -> impl Iterator { iter::empty::() } -fn fapit1(_: impl Iterator) {} -fn fapit2(_: impl Iterator) {} -fn fapit3(_: impl Iterator) {} +fn apit1(_: impl Iterator) {} +fn apit2(_: impl Iterator) {} +fn apit3(_: impl Iterator) {} -type TAI1> = T; -type TAI2> = T; -type TAI3> = T; -type TAW1 +type Tait1> = T; +type Tait2> = T; +type Tait3> = T; +type Taw1 where T: Iterator, = T; -type TAW2 +type Taw2 where T: Iterator, = T; -type TAW3 +type Taw3 where T: Iterator, = T; -trait TRI1> {} -trait TRI2> {} -trait TRI3> {} -trait TRS1: Iterator {} -trait TRS2: Iterator {} -trait TRS3: Iterator {} -trait TRW1 +trait Tri1> {} +trait Tri2> {} +trait Tri3> {} +trait Trs1: Iterator {} +trait Trs2: Iterator {} +trait Trs3: Iterator {} +trait Trw1 where T: Iterator, { } -trait TRW2 +trait Trw2 where T: Iterator, { } -trait TRW3 +trait Trw3 where T: Iterator, { } -trait TRSW1 +trait Trsw1 where Self: Iterator, { } -trait TRSW2 +trait Trsw2 where Self: Iterator, { } -trait TRSW3 +trait Trsw3 where Self: Iterator, { } -trait TRA1 { +trait Tra1 { type A: Iterator; } -trait TRA2 { +trait Tra2 { type A: Iterator; } -trait TRA3 { +trait Tra3 { type A: Iterator; } From 7dfc6b2e27825ca5e1928233971f3e7996bbe44b Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 26 Sep 2025 04:53:29 +0000 Subject: [PATCH 0892/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to b733736ea2feb7798c99cbb9a769bce74be108df. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 388e88fe43eb7..c06de8db0a513 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f6092f224d2b1774b31033f12d0bee626943b02f +b733736ea2feb7798c99cbb9a769bce74be108df From 68c0e97cc6d084424cd2eb9001d63a0989b03655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Thu, 25 Sep 2025 11:34:08 +0200 Subject: [PATCH 0893/1900] re-order normalizations in run-make linker-warning test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit otherwise a buildroot containing `libpanic_abort` would be mangled before being replaced by the build root placeholder value.. e.g., running `./x.py test --verbose tests/run-make/linker-warning` with rustc checked out in ~/ext/rustc-libpanic_abort will result in (output slightly shortened): ``` running 1 tests test [run-make] tests/run-make/linker-warning ... FAILED failures: ---- [run-make] tests/run-make/linker-warning stdout ---- ------rustc stdout------------------------------ ------rustc stderr------------------------------ ------------------------------------------ error: rmake recipe failed to complete status: exit status: 101 command: cd "/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/test/run-make/linker-warning/rmake_out" && env -u RUSTFLAGS -u __RUSTC_DEBUG_ASSERTIONS_ENABLED -u __STD_DEBUG_ASSERTIONS_ENABLED AR="ar" BUILD_ROOT="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu" CC="cc" CC_DEFAULT_FLAGS="-ffunction-sections -fdata-sections -fPIC -m64" CXX="c++" CXX_DEFAULT_FLAGS="-ffunction-sections -fdata-sections -fPIC -m64" HOST_RUSTC_DYLIB_PATH="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/stage1/lib" LD_LIBRARY_PATH="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/bootstrap-tools/x86_64-unknown-linux-gnu/release/deps:/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/stage0/lib/rustlib/x86_64-unknown-linux-gnu/lib" LD_LIB_PATH_ENVVAR="LD_LIBRARY_PATH" LLVM_BIN_DIR="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/ci-llvm/bin" LLVM_COMPONENTS="<...>" LLVM_FILECHECK="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/ci-llvm/bin/FileCheck" NODE="/usr/bin/node" PYTHON="/usr/bin/python3" RUSTC="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/stage1/bin/rustc" RUSTDOC="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/stage1/bin/rustdoc" SOURCE_ROOT="/home/user/ext/rustc-libpanic_abort" TARGET="x86_64-unknown-linux-gnu" TARGET_EXE_DYLIB_PATH="/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/home/user/ext/rustc-libpanic_abort/build/x86_64-unknown-linux-gnu/test/run-make/linker-warning/rmake" stdout: none --- stderr ------------------------------- thread 'main' panicked at /home/user/ext/rustc-libpanic_abort/tests/run-make/linker-warning/rmake.rs:74:14: test failed: `short-error.txt` is different from `(linker error)` --- short-error.txt +++ (linker error) @@ -1,6 +1,6 @@ error: linking with `./fake-linker` failed: exit status: 1 | - = note: "./fake-linker" "-m64" "/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/build-root/test/run-make/linker-warning/rmake_out/{libfoo,libbar}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/build-root/test/run-make/linker-warning/rmake_out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "main" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "run_make_error" + = note: "./fake-linker" "-m64" "/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/home/user/ext/rustc-libpanic_unwind/build/x86_64-unknown-linux-gnu/test/run-make/linker-warning/rmake_out/{libfoo,libbar}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/user/ext/rustc-libpanic_unwind/build/x86_64-unknown-linux-gnu/test/run-make/linker-warning/rmake_out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "main" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "run_make_error" = note: some arguments are omitted. use `--verbose` to show all linker arguments = note: error: baz [..] ``` without this fix. Signed-off-by: Fabian Grünbichler --- tests/run-make/linker-warning/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index b0c40dd171d34..a31b08d6c6956 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -61,13 +61,13 @@ fn main() { diff() .expected_file("short-error.txt") .actual_text("(linker error)", out.stderr()) - .normalize("libpanic_abort", "libpanic_unwind") .normalize( regex::escape( run_make_support::build_root().canonicalize().unwrap().to_str().unwrap(), ), "/build-root", ) + .normalize("libpanic_abort", "libpanic_unwind") .normalize(r#""[^"]*\/symbols.o""#, "\"/symbols.o\"") .normalize(r#""[^"]*\/raw-dylibs""#, "\"/raw-dylibs\"") .run(); From 38284d10dc45b3bb2c9aa412dce1e5b1e16c639a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 23 Sep 2025 13:18:33 +0800 Subject: [PATCH 0894/1900] Fix expand rest pattern in tuple and slice pattern Assist: expand_tuple_rest_pattern Fills fields by replacing rest pattern in tuple patterns. Example --- ``` fn foo(bar: (char, i32, i32)) { let (ch, ..$0) = bar; } ``` -> ``` fn foo(bar: (char, i32, i32)) { let (ch, _1, _2) = bar; } ``` --- Assist: expand_slice_rest_pattern Fills fields by replacing rest pattern in slice patterns. Example --- ``` fn foo(bar: [i32; 3]) { let [first, ..$0] = bar; } ``` -> ``` fn foo(bar: [i32; 3]) { let [first, _1, _2] = bar; } ``` --- .../src/handlers/expand_rest_pattern.rs | 274 ++++++++++++++++-- .../crates/ide-assists/src/tests/generated.rs | 34 +++ 2 files changed, 289 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 932166adb0a02..b746099e72791 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -113,9 +113,7 @@ fn expand_tuple_struct_rest_pattern( }; let rest_pat = rest_pat.into(); - let mut pats = pat.fields(); - let prefix_count = pats.by_ref().position(|p| p == rest_pat)?; - let suffix_count = pats.count(); + let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?; if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 { cov_mark::hit!(no_missing_fields_tuple_struct); @@ -141,19 +139,13 @@ fn expand_tuple_struct_rest_pattern( pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { - make.ident_pat( - false, - false, - match name_gen.for_type( - &f.ty(ctx.sema.db).to_type(ctx.sema.db), - ctx.sema.db, - ctx.edition(), - ) { - Some(name) => make.name(&name), - None => make.name(&format!("_{}", f.index())), - }, + gen_unnamed_pat( + ctx, + &make, + &mut name_gen, + &f.ty(ctx.db()).to_type(ctx.sema.db), + f.index(), ) - .into() })) .chain(pat.fields().skip(prefix_count + 1)), ); @@ -166,6 +158,134 @@ fn expand_tuple_struct_rest_pattern( ) } +// Assist: expand_tuple_rest_pattern +// +// Fills fields by replacing rest pattern in tuple patterns. +// +// ``` +// fn foo(bar: (char, i32, i32)) { +// let (ch, ..$0) = bar; +// } +// ``` +// -> +// ``` +// fn foo(bar: (char, i32, i32)) { +// let (ch, _1, _2) = bar; +// } +// ``` +fn expand_tuple_rest_pattern( + acc: &mut Assists, + ctx: &AssistContext<'_>, + pat: ast::TuplePat, + rest_pat: ast::RestPat, +) -> Option<()> { + let fields = ctx.sema.type_of_pat(&pat.clone().into())?.original.tuple_fields(ctx.db()); + let len = fields.len(); + + let rest_pat = rest_pat.into(); + let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?; + + if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 { + cov_mark::hit!(no_missing_fields_tuple); + return None; + } + + let old_range = ctx.sema.original_range_opt(pat.syntax())?; + if old_range.file_id != ctx.file_id() { + return None; + } + + acc.add( + AssistId::refactor_rewrite("expand_tuple_rest_pattern"), + "Fill tuple fields", + rest_pat.syntax().text_range(), + |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(rest_pat.syntax()); + + let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); + let new_pat = make.tuple_pat( + pat.fields() + .take(prefix_count) + .chain(fields[prefix_count..len - suffix_count].iter().enumerate().map( + |(index, ty)| { + gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index) + }, + )) + .chain(pat.fields().skip(prefix_count + 1)), + ); + + editor.replace(pat.syntax(), new_pat.syntax()); + + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + +// Assist: expand_slice_rest_pattern +// +// Fills fields by replacing rest pattern in slice patterns. +// +// ``` +// fn foo(bar: [i32; 3]) { +// let [first, ..$0] = bar; +// } +// ``` +// -> +// ``` +// fn foo(bar: [i32; 3]) { +// let [first, _1, _2] = bar; +// } +// ``` +fn expand_slice_rest_pattern( + acc: &mut Assists, + ctx: &AssistContext<'_>, + pat: ast::SlicePat, + rest_pat: ast::RestPat, +) -> Option<()> { + let (ty, len) = ctx.sema.type_of_pat(&pat.clone().into())?.original.as_array(ctx.db())?; + + let rest_pat = rest_pat.into(); + let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.pats())?; + + if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 { + cov_mark::hit!(no_missing_fields_slice); + return None; + } + + let old_range = ctx.sema.original_range_opt(pat.syntax())?; + if old_range.file_id != ctx.file_id() { + return None; + } + + acc.add( + AssistId::refactor_rewrite("expand_slice_rest_pattern"), + "Fill slice fields", + rest_pat.syntax().text_range(), + |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(rest_pat.syntax()); + + let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); + let new_pat = make.slice_pat( + pat.pats() + .take(prefix_count) + .chain( + (prefix_count..len - suffix_count) + .map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)), + ) + .chain(pat.pats().skip(prefix_count + 1)), + ); + + editor.replace(pat.syntax(), new_pat.syntax()); + + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let rest_pat = ctx.find_node_at_offset::()?; let parent = rest_pat.syntax().parent()?; @@ -173,15 +293,40 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> match parent { ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat), ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat), - // FIXME - // ast::TuplePat(it) => (), - // FIXME - // ast::SlicePat(it) => (), + ast::TuplePat(it) => expand_tuple_rest_pattern(acc, ctx, it, rest_pat), + ast::SlicePat(it) => expand_slice_rest_pattern(acc, ctx, it, rest_pat), _ => None, } } } +fn gen_unnamed_pat( + ctx: &AssistContext<'_>, + make: &SyntaxFactory, + name_gen: &mut NameGenerator, + ty: &hir::Type<'_>, + index: usize, +) -> ast::Pat { + make.ident_pat( + false, + false, + match name_gen.for_type(ty, ctx.sema.db, ctx.edition()) { + Some(name) => make.name(&name), + None => make.name(&format!("_{index}")), + }, + ) + .into() +} + +fn calculate_counts( + rest_pat: &ast::Pat, + mut pats: ast::AstChildren, +) -> Option<(usize, usize)> { + let prefix_count = pats.by_ref().position(|p| p == *rest_pat)?; + let suffix_count = pats.count(); + Some((prefix_count, suffix_count)) +} + #[cfg(test)] mod tests { use super::*; @@ -351,6 +496,79 @@ fn foo(bar: Bar) { ) } + #[test] + fn fill_tuple_with_fields() { + check_assist( + expand_rest_pattern, + r#" +fn foo(bar: (char, i32, i32)) { + let (ch, ..$0) = bar; +} +"#, + r#" +fn foo(bar: (char, i32, i32)) { + let (ch, _1, _2) = bar; +} +"#, + ); + check_assist( + expand_rest_pattern, + r#" +fn foo(bar: (char, i32, i32)) { + let (ch, ..$0, end) = bar; +} +"#, + r#" +fn foo(bar: (char, i32, i32)) { + let (ch, _1, end) = bar; +} +"#, + ); + } + + #[test] + fn fill_array_with_fields() { + check_assist( + expand_rest_pattern, + r#" +fn foo(bar: [i32; 4]) { + let [first, ..$0] = bar; +} +"#, + r#" +fn foo(bar: [i32; 4]) { + let [first, _1, _2, _3] = bar; +} +"#, + ); + check_assist( + expand_rest_pattern, + r#" +fn foo(bar: [i32; 4]) { + let [first, second, ..$0] = bar; +} +"#, + r#" +fn foo(bar: [i32; 4]) { + let [first, second, _2, _3] = bar; +} +"#, + ); + check_assist( + expand_rest_pattern, + r#" +fn foo(bar: [i32; 4]) { + let [first, second, ..$0, end] = bar; +} +"#, + r#" +fn foo(bar: [i32; 4]) { + let [first, second, _2, end] = bar; +} +"#, + ); + } + #[test] fn fill_fields_struct_generated_by_macro() { check_assist( @@ -486,6 +704,8 @@ fn bar(foo: Foo) { // This is still possible even though it's meaningless cov_mark::check!(no_missing_fields); cov_mark::check!(no_missing_fields_tuple_struct); + cov_mark::check!(no_missing_fields_tuple); + cov_mark::check!(no_missing_fields_slice); check_assist_not_applicable( expand_rest_pattern, r#" @@ -523,6 +743,22 @@ struct Bar(Y, Z) fn foo(bar: Bar) { let Bar(y, ..$0, z) = bar; } +"#, + ); + check_assist_not_applicable( + expand_rest_pattern, + r#" +fn foo(bar: (i32, i32)) { + let (y, ..$0, z) = bar; +} +"#, + ); + check_assist_not_applicable( + expand_rest_pattern, + r#" +fn foo(bar: [i32; 2]) { + let [y, ..$0, z] = bar; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 71ffaa23fc640..e7f91ff3fbc14 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1041,6 +1041,40 @@ fn foo(bar: Bar) { ) } +#[test] +fn doctest_expand_slice_rest_pattern() { + check_doc_test( + "expand_slice_rest_pattern", + r#####" +fn foo(bar: [i32; 3]) { + let [first, ..$0] = bar; +} +"#####, + r#####" +fn foo(bar: [i32; 3]) { + let [first, _1, _2] = bar; +} +"#####, + ) +} + +#[test] +fn doctest_expand_tuple_rest_pattern() { + check_doc_test( + "expand_tuple_rest_pattern", + r#####" +fn foo(bar: (char, i32, i32)) { + let (ch, ..$0) = bar; +} +"#####, + r#####" +fn foo(bar: (char, i32, i32)) { + let (ch, _1, _2) = bar; +} +"#####, + ) +} + #[test] fn doctest_expand_tuple_struct_rest_pattern() { check_doc_test( From aa5a21450a070fdea66a07d3cab3b69e6735c328 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:54:54 -0700 Subject: [PATCH 0895/1900] ProjectionElem::Subtype -> CastKind::Subtype --- .../src/diagnostics/conflict_errors.rs | 1 - .../rustc_borrowck/src/diagnostics/mod.rs | 7 +++--- .../src/diagnostics/mutability_errors.rs | 1 - compiler/rustc_borrowck/src/lib.rs | 4 ---- .../rustc_borrowck/src/places_conflict.rs | 2 -- compiler/rustc_borrowck/src/prefixes.rs | 3 --- compiler/rustc_borrowck/src/type_check/mod.rs | 9 +++---- compiler/rustc_codegen_cranelift/src/base.rs | 4 ++-- .../src/value_and_place.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 4 ---- compiler/rustc_codegen_ssa/src/mir/operand.rs | 5 ---- compiler/rustc_codegen_ssa/src/mir/place.rs | 1 - compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 8 +++++-- .../src/check_consts/qualifs.rs | 1 - .../rustc_const_eval/src/interpret/cast.rs | 2 +- .../src/interpret/projection.rs | 2 -- compiler/rustc_middle/src/mir/pretty.rs | 4 ---- compiler/rustc_middle/src/mir/statement.rs | 8 ++----- compiler/rustc_middle/src/mir/syntax.rs | 24 +++++++++---------- compiler/rustc_middle/src/mir/visit.rs | 6 ----- .../src/builder/expr/as_place.rs | 3 +-- .../src/move_paths/builder.rs | 5 +--- .../src/add_subtyping_projections.rs | 3 +-- .../src/dataflow_const_prop.rs | 2 +- compiler/rustc_mir_transform/src/gvn.rs | 3 +-- .../src/known_panics_lint.rs | 2 +- .../rustc_mir_transform/src/promote_consts.rs | 1 - compiler/rustc_mir_transform/src/validate.rs | 24 +++++++------------ compiler/rustc_public/src/mir/body.rs | 11 ++------- compiler/rustc_public/src/mir/visit.rs | 2 -- .../src/unstable/convert/internal.rs | 3 --- .../src/unstable/convert/stable/mir.rs | 2 +- 32 files changed, 47 insertions(+), 112 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7e20a5133e07f..a5b16b508388f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3974,7 +3974,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) | ProjectionElem::UnwrapUnsafeBinder(_) => kind, }, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 5642cdf87fded..e13c1c712d8d7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -402,7 +402,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ProjectionElem::Downcast(..) if opt.including_downcast => return None, ProjectionElem::Downcast(..) => (), ProjectionElem::OpaqueCast(..) => (), - ProjectionElem::Subtype(..) => (), ProjectionElem::UnwrapUnsafeBinder(_) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. @@ -484,9 +483,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), - ProjectionElem::Subtype(ty) - | ProjectionElem::OpaqueCast(ty) - | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty), + ProjectionElem::OpaqueCast(ty) | ProjectionElem::UnwrapUnsafeBinder(ty) => { + PlaceTy::from_ty(*ty) + } ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 6d69040c711d9..727cf19cd8bb3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -192,7 +192,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { [ .., ProjectionElem::Index(_) - | ProjectionElem::Subtype(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4c380ddcf7084..cc27b9e5dde61 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1989,10 +1989,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { }, // `OpaqueCast`: only transmutes the type, so no moves there. // `Downcast` : only changes information about a `Place` without moving. - // `Subtype` : only transmutes the type, so no moves. // So it's safe to skip these. ProjectionElem::OpaqueCast(_) - | ProjectionElem::Subtype(_) | ProjectionElem::Downcast(_, _) | ProjectionElem::UnwrapUnsafeBinder(_) => (), } @@ -2218,7 +2216,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | - ProjectionElem::Subtype(_) | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | // assigning to P[i] requires P to be valid. @@ -2610,7 +2607,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } - | ProjectionElem::Subtype(..) | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast(..) | ProjectionElem::UnwrapUnsafeBinder(_) => { diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index cf3e82426e8a0..60676ac6b8644 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -249,7 +249,6 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::ConstantIndex { .. }, _, _) | (ProjectionElem::Subslice { .. }, _, _) | (ProjectionElem::OpaqueCast { .. }, _, _) - | (ProjectionElem::Subtype(_), _, _) | (ProjectionElem::Downcast { .. }, _, _) | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => { // Recursive case. This can still be disjoint on a @@ -510,7 +509,6 @@ fn place_projection_conflict<'tcx>( | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index 83cca38a5c090..9e51264d8edcb 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -77,9 +77,6 @@ impl<'tcx> Iterator for Prefixes<'tcx> { | ProjectionElem::Index(_) => { cursor = cursor_base; } - ProjectionElem::Subtype(..) => { - panic!("Subtype projection is not allowed before borrow check") - } ProjectionElem::Deref => { match self.kind { PrefixSet::Shallow => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 606d3d95d9e65..781fb5ba113ab 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1558,6 +1558,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ), } } + CastKind::Subtype => { + bug!("CastKind::Subtype shouldn't exist in borrowck") + } } } @@ -1882,9 +1885,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) .unwrap(); } - ProjectionElem::Subtype(_) => { - bug!("ProjectionElem::Subtype shouldn't exist in borrowck") - } } } } @@ -2412,9 +2412,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | ProjectionElem::UnwrapUnsafeBinder(_) => { // other field access } - ProjectionElem::Subtype(_) => { - bug!("ProjectionElem::Subtype shouldn't exist in borrowck") - } } } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 41e11e1de6163..a2f844642f85e 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -789,7 +789,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let operand = codegen_operand(fx, operand); crate::unsize::coerce_unsized_into(fx, operand, lval); } - Rvalue::Cast(CastKind::Transmute, ref operand, _to_ty) => { + Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, ref operand, _to_ty) => { let operand = codegen_operand(fx, operand); lval.write_cvalue_transmute(fx, operand); } @@ -996,7 +996,7 @@ pub(crate) fn codegen_place<'tcx>( cplace = cplace.place_deref(fx); } PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"), - PlaceElem::Subtype(ty) | PlaceElem::UnwrapUnsafeBinder(ty) => { + PlaceElem::UnwrapUnsafeBinder(ty) => { cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)); } PlaceElem::Field(field, _ty) => { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 4519fa1a270e4..25bba48a9e3af 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -660,7 +660,7 @@ impl<'tcx> CPlace<'tcx> { } } - /// Used for `ProjectionElem::Subtype`, `ty` has to be monomorphized before + /// Used for `ProjectionElem::UnwrapUnsafeBinder`, `ty` has to be monomorphized before /// passed on. pub(crate) fn place_transmute_type( self, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index c2c023af090a5..45bc545194682 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -150,10 +150,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx> { layout.for_variant(self.fx.cx, vidx) } - mir::PlaceElem::Subtype(subtype_ty) => { - let subtype_ty = self.fx.monomorphize(subtype_ty); - self.fx.cx.layout_of(subtype_ty) - } _ => { self.locals[place_ref.local] = LocalKind::Memory; return; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index d851c3329802c..5f7f87fc69206 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -956,11 +956,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = o.layout.for_variant(bx.cx(), vidx); o = OperandRef { val: o.val, layout } } - mir::PlaceElem::Subtype(subtype_ty) => { - let subtype_ty = self.monomorphize(subtype_ty); - let layout = self.cx.layout_of(subtype_ty); - o = OperandRef { val: o.val, layout } - } _ => return None, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 0090be9fdef06..50f56f913a51e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -347,7 +347,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::ProjectionElem::OpaqueCast(ty) => { bug!("encountered OpaqueCast({ty}) in codegen") } - mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)), mir::ProjectionElem::UnwrapUnsafeBinder(ty) => { cg_base.project_type(bx, self.monomorphize(ty)) } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 2602bf82095c5..69478d078cc05 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -86,7 +86,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => { + mir::Rvalue::Cast( + mir::CastKind::Transmute | mir::CastKind::Subtype, + ref operand, + _ty, + ) => { let src = self.codegen_operand(bx, operand); self.codegen_transmute(bx, src, dest); } @@ -486,7 +490,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("Unsupported cast of {operand:?} to {cast:?}"); }) } - mir::CastKind::Transmute => { + mir::CastKind::Transmute | mir::CastKind::Subtype => { self.codegen_transmute_operand(bx, operand, cast) } }; diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 34d1fdd8c8694..8a6827bca2bd6 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -293,7 +293,6 @@ where ProjectionElem::Index(index) if in_local(index) => return true, ProjectionElem::Deref - | ProjectionElem::Subtype(_) | ProjectionElem::Field(_, _) | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 0075740e03178..b058d4b8ad446 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -133,7 +133,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::Transmute => { + CastKind::Transmute | CastKind::Subtype => { assert!(src.layout.is_sized()); assert!(dest.layout.is_sized()); assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely... diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index d05871bfc773c..2fd1657f6ba3a 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -395,8 +395,6 @@ where span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?, - // We don't want anything happening here, this is here as a dummy. - Subtype(_) => base.transmute(base.layout(), self)?, Field(field, _) => self.project_field(base, field)?, Downcast(_, variant) => self.project_downcast(base, variant)?, Deref => self.deref_pointer(&base.to_op(self)?)?.into(), diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 96148fd5b9269..350d75c2ee772 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1274,7 +1274,6 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> for &elem in projection.iter().rev() { match elem { ProjectionElem::OpaqueCast(_) - | ProjectionElem::Subtype(_) | ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { write!(fmt, "(")?; @@ -1300,9 +1299,6 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::OpaqueCast(ty) => { write!(fmt, " as {ty})")?; } - ProjectionElem::Subtype(ty) => { - write!(fmt, " as subtype {ty})")?; - } ProjectionElem::Downcast(Some(name), _index) => { write!(fmt, " as {name})")?; } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 28294b47e90f2..e009fe05b53e6 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -222,7 +222,6 @@ impl<'tcx> PlaceTy<'tcx> { fty, )), ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)), - ProjectionElem::Subtype(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)), // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general. ProjectionElem::UnwrapUnsafeBinder(ty) => { @@ -244,7 +243,6 @@ impl ProjectionElem { Self::Field(_, _) | Self::Index(_) | Self::OpaqueCast(_) - | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) @@ -259,7 +257,6 @@ impl ProjectionElem { Self::Deref | Self::Index(_) => false, Self::Field(_, _) | Self::OpaqueCast(_) - | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) @@ -286,7 +283,6 @@ impl ProjectionElem { | Self::Field(_, _) => true, Self::ConstantIndex { from_end: true, .. } | Self::Index(_) - | Self::Subtype(_) | Self::OpaqueCast(_) | Self::Subslice { .. } => false, @@ -319,7 +315,6 @@ impl ProjectionElem { ProjectionElem::Subslice { from, to, from_end } } ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)), ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)), ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?), }) @@ -706,7 +701,8 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::PtrToPtr | CastKind::PointerCoercion(_, _) | CastKind::PointerWithExposedProvenance - | CastKind::Transmute, + | CastKind::Transmute + | CastKind::Subtype, _, _, ) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index e6c8512564edc..a823c365394f7 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1275,18 +1275,6 @@ pub enum ProjectionElem { /// A transmute from an unsafe binder to the type that it wraps. This is a projection /// of a place, so it doesn't necessarily constitute a move out of the binder. UnwrapUnsafeBinder(T), - - /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where - /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping - /// explicit during optimizations and codegen. - /// - /// This projection doesn't impact the runtime behavior of the program except for potentially changing - /// some type metadata of the interpreter or codegen backend. - /// - /// This goal is achieved with mir_transform pass `Subtyper`, which runs right after - /// borrowchecker, as we only care about subtyping that can affect trait selection and - /// `TypeId`. - Subtype(T), } /// Alias for projections as they appear in places, where the base is a place @@ -1513,6 +1501,18 @@ pub enum CastKind { /// MIR is well-formed if the input and output types have different sizes, /// but running a transmute between differently-sized types is UB. Transmute, + + /// A `Subtype` cast is applied to any `StatementKind::Assign` where + /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping + /// explicit during optimizations and codegen. + /// + /// This cast doesn't impact the runtime behavior of the program except for potentially changing + /// some type metadata of the interpreter or codegen backend. + /// + /// This goal is achieved with mir_transform pass `Subtyper`, which runs right after + /// borrowchecker, as we only care about subtyping that can affect trait selection and + /// `TypeId`. + Subtype, } /// Represents how a [`CastKind::PointerCoercion`] was constructed. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 81df239dee42d..f392347780051 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1166,11 +1166,6 @@ macro_rules! visit_place_fns { self.visit_ty(&mut new_ty, TyContext::Location(location)); if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None } } - PlaceElem::Subtype(ty) => { - let mut new_ty = ty; - self.visit_ty(&mut new_ty, TyContext::Location(location)); - if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None } - } PlaceElem::UnwrapUnsafeBinder(ty) => { let mut new_ty = ty; self.visit_ty(&mut new_ty, TyContext::Location(location)); @@ -1244,7 +1239,6 @@ macro_rules! visit_place_fns { ) { match elem { ProjectionElem::OpaqueCast(ty) - | ProjectionElem::Subtype(ty) | ProjectionElem::Field(_, ty) | ProjectionElem::UnwrapUnsafeBinder(ty) => { self.visit_ty(ty, TyContext::Location(location)); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 5a6bd2f413c2d..5e7a57d51a9ca 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -103,7 +103,7 @@ fn convert_to_hir_projections_and_truncate_for_capture( } ProjectionElem::UnwrapUnsafeBinder(_) => HirProjectionKind::UnwrapUnsafeBinder, // These do not affect anything, they just make sure we know the right type. - ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue, + ProjectionElem::OpaqueCast(_) => continue, ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { @@ -802,7 +802,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ProjectionElem::Field(..) | ProjectionElem::Downcast(..) | ProjectionElem::OpaqueCast(..) - | ProjectionElem::Subtype(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::UnwrapUnsafeBinder(_) => (), diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 72d4cd72c2bcf..434f106302f5d 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -227,11 +227,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { ProjectionElem::UnwrapUnsafeBinder(_) => {} // `OpaqueCast`:Only transmutes the type, so no moves there. // `Downcast` :Only changes information about a `Place` without moving. - // `Subtype` :Only transmutes the type, so moves. // So it's safe to skip these. - ProjectionElem::OpaqueCast(_) - | ProjectionElem::Subtype(_) - | ProjectionElem::Downcast(_, _) => (), + ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) => (), } let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty; if !(self.filter)(elem_ty) { diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index be4f84d64d03f..a6a60fddf9097 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -40,8 +40,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { .new_temp(rval_ty, self.local_decls[place.as_ref().local].source_info.span); let new_place = Place::from(temp); self.patcher.add_assign(location, new_place, rvalue.clone()); - let subtyped = new_place.project_deeper(&[ProjectionElem::Subtype(place_ty)], self.tcx); - *rvalue = Rvalue::Use(Operand::Move(subtyped)); + *rvalue = Rvalue::Cast(CastKind::Subtype, Operand::Move(new_place), place_ty); } } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 5c984984d3cc3..d250cdea7e805 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -440,7 +440,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { FlatSet::Top => FlatSet::Top, } } - Rvalue::Cast(CastKind::Transmute, operand, _) => { + Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, operand, _) => { match self.eval_operand(operand, state) { FlatSet::Elem(op) => self.wrap_immediate(*op), FlatSet::Bottom => FlatSet::Bottom, diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index ebec3d125003d..eb59ae830ae13 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -656,7 +656,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?; res.into() } - CastKind::Transmute => { + CastKind::Transmute | CastKind::Subtype => { let value = self.evaluated[value].as_ref()?; // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do @@ -788,7 +788,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Subslice { from, to, from_end } } ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()), - ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()), ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()), }; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index aaacc5866a2ae..0686dca9962d6 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -637,7 +637,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; res.into() } - CastKind::Transmute => { + CastKind::Transmute | CastKind::Subtype => { let value = self.eval_operand(value)?; let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index a0b0c8c990f33..48ddf5a1bcabb 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -292,7 +292,6 @@ impl<'tcx> Validator<'_, 'tcx> { match elem { // Recurse directly. ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::Subslice { .. } | ProjectionElem::UnwrapUnsafeBinder(_) => {} diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index c8a9a88dc3fe3..cbabb982df8b8 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -814,22 +814,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - ProjectionElem::Subtype(ty) => { - if !util::sub_types( - self.tcx, - self.typing_env, - ty, - place_ref.ty(&self.body.local_decls, self.tcx).ty, - ) { - self.fail( - location, - format!( - "Failed subtyping {ty} and {}", - place_ref.ty(&self.body.local_decls, self.tcx).ty - ), - ) - } - } ProjectionElem::UnwrapUnsafeBinder(unwrapped_ty) => { let binder_ty = place_ref.ty(&self.body.local_decls, self.tcx); let ty::UnsafeBinder(binder_ty) = *binder_ty.ty.kind() else { @@ -1331,6 +1315,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + CastKind::Subtype => { + if !util::sub_types(self.tcx, self.typing_env, op_ty, *target_type) { + self.fail( + location, + format!("Failed subtyping {op_ty} and {target_type}"), + ) + } + } } } Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 276adacd99e06..7bd06fee721ce 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -836,14 +836,6 @@ pub enum ProjectionElem { /// Like an explicit cast from an opaque type to a concrete type, but without /// requiring an intermediate variable. OpaqueCast(Ty), - - /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where - /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping - /// explicit during optimizations and codegen. - /// - /// This projection doesn't impact the runtime behavior of the program except for potentially changing - /// some type metadata of the interpreter or codegen backend. - Subtype(Ty), } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] @@ -1028,6 +1020,7 @@ pub enum CastKind { PtrToPtr, FnPtrToPtr, Transmute, + Subtype, } #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] @@ -1089,7 +1082,7 @@ impl ProjectionElem { Self::subslice_ty(ty, *from, *to, *from_end) } ProjectionElem::Downcast(_) => Ok(ty), - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + ProjectionElem::OpaqueCast(ty) => Ok(*ty), } } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 04c4d4d2a82d7..7563c9ca00820 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -471,7 +471,6 @@ macro_rules! visit_place_fns { ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} ProjectionElem::Downcast(_idx) => {} ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), - ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), } } }; @@ -512,7 +511,6 @@ macro_rules! visit_place_fns { ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} ProjectionElem::Downcast(_idx) => {} ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), - ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), } } }; diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index dc9abd88614d3..064fb6c6803a5 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -703,9 +703,6 @@ impl RustcInternal for ProjectionElem { ProjectionElem::OpaqueCast(ty) => { rustc_middle::mir::PlaceElem::OpaqueCast(ty.internal(tables, tcx)) } - ProjectionElem::Subtype(ty) => { - rustc_middle::mir::PlaceElem::Subtype(ty.internal(tables, tcx)) - } } } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index b10af6526ead5..62ab91d17baee 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -356,6 +356,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { PtrToPtr => crate::mir::CastKind::PtrToPtr, FnPtrToPtr => crate::mir::CastKind::FnPtrToPtr, Transmute => crate::mir::CastKind::Transmute, + Subtype => crate::mir::CastKind::Subtype, } } } @@ -453,7 +454,6 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { // found at https://github.com/rust-lang/rust/pull/117517#issuecomment-1811683486 Downcast(_, idx) => crate::mir::ProjectionElem::Downcast(idx.stable(tables, cx)), OpaqueCast(ty) => crate::mir::ProjectionElem::OpaqueCast(ty.stable(tables, cx)), - Subtype(ty) => crate::mir::ProjectionElem::Subtype(ty.stable(tables, cx)), UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } From 78ff5ddad940c245ba2ace56c8d4aa33e1bb6338 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 20:40:28 +0300 Subject: [PATCH 0896/1900] Remove usages of `write_str` from `render_assoc_items_inner` --- src/librustdoc/html/format.rs | 4 --- src/librustdoc/html/render/mod.rs | 59 ++++++++++++------------------- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ecaff4cdf43af..856e637a4587b 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -37,10 +37,6 @@ use crate::html::escape::{Escape, EscapeBodyText}; use crate::html::render::Context; use crate::passes::collect_intra_doc_links::UrlFragment; -pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { - s.write_fmt(f).unwrap(); -} - pub(crate) fn print_generic_bounds( bounds: &[clean::GenericBound], cx: &Context<'_>, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 9a14137a6e801..8c9953c469b9f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1472,12 +1472,10 @@ fn render_assoc_items_inner( ) } }; - let mut impls_buf = String::new(); - for i in &non_trait { - write_str( - &mut impls_buf, - format_args!( - "{}", + let impls_buf = fmt::from_fn(|f| { + non_trait + .iter() + .map(|i| { render_impl( cx, i, @@ -1493,9 +1491,11 @@ fn render_assoc_items_inner( toggle_open_by_default: true, }, ) - ), - ); - } + }) + .joined("", f) + }) + .to_string(); + if !impls_buf.is_empty() { write!( w, @@ -1805,27 +1805,19 @@ fn render_impl( document_item_info(cx, it, Some(parent)) .render_into(&mut info_buffer) .unwrap(); - write_str( - &mut doc_buffer, - format_args!("{}", document_full(item, cx, HeadingOffset::H5)), - ); + doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string(); short_documented = false; } else { // In case the item isn't documented, // provide short documentation from the trait. - write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short( - it, - cx, - link, - parent, - rendering_params.show_def_docs, - ) - ), - ); + doc_buffer = document_short( + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ) + .to_string(); } } } else { @@ -1833,21 +1825,14 @@ fn render_impl( .render_into(&mut info_buffer) .unwrap(); if rendering_params.show_def_docs { - write_str( - &mut doc_buffer, - format_args!("{}", document_full(item, cx, HeadingOffset::H5)), - ); + doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string(); short_documented = false; } } } else { - write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short(item, cx, link, parent, rendering_params.show_def_docs) - ), - ); + doc_buffer = + document_short(item, cx, link, parent, rendering_params.show_def_docs) + .to_string(); } } let mut w = if short_documented && trait_.is_some() { From 82fecf0ee3e362bf20b46d7867fe25f9af4e82d8 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 20:40:31 +0300 Subject: [PATCH 0897/1900] Cleanup `notable_traits_decl` --- src/librustdoc/html/render/mod.rs | 119 ++++++++++++++++-------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8c9953c469b9f..878af36e4ff1d 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -77,7 +77,6 @@ use crate::html::escape::Escape; use crate::html::format::{ Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space, - write_str, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -1647,69 +1646,77 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option) -> (String, String) { - let mut out = String::new(); - let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); - for i in impls { - let impl_ = i.inner_impl(); - if impl_.polarity != ty::ImplPolarity::Positive { - continue; - } - - if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) { - // Two different types might have the same did, - // without actually being the same. - continue; - } - if let Some(trait_) = &impl_.trait_ { - let trait_did = trait_.def_id(); - - if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) { - if out.is_empty() { - write_str( - &mut out, - format_args!( - "

Notable traits for {}

\ -
",
-                            impl_.for_.print(cx)
-                        ),
-                    );
+    let out = fmt::from_fn(|f| {
+        let mut notable_impls = impls
+            .iter()
+            .map(|impl_| impl_.inner_impl())
+            .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
+            .filter(|impl_| {
+                // Two different types might have the same did, without actually being the same.
+                ty.is_doc_subtype_of(&impl_.for_, cx.cache())
+            })
+            .filter_map(|impl_| {
+                if let Some(trait_) = &impl_.trait_
+                    && let trait_did = trait_.def_id()
+                    && let Some(trait_) = cx.cache().traits.get(&trait_did)
+                    && trait_.is_notable_trait(cx.tcx())
+                {
+                    Some((impl_, trait_did))
+                } else {
+                    None
                 }
+            })
+            .peekable();
 
-                write_str(
-                    &mut out,
-                    format_args!("
{}
", impl_.print(false, cx)), - ); - for it in &impl_.items { - if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { - let empty_set = FxIndexSet::default(); - let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); - write_str( - &mut out, - format_args!( - "
{};
", - assoc_type( - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, - ) - ), - ); - } - } + let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() { + write!( + f, + "

Notable traits for {}

\ +
",
+                impl_.for_.print(cx)
+            )?;
+            true
+        } else {
+            false
+        };
+
+        for (impl_, trait_did) in notable_impls {
+            write!(f, "
{}
", impl_.print(false, cx))?; + for it in &impl_.items { + let clean::AssocTypeItem(tydef, ..) = &it.kind else { + continue; + }; + + let empty_set = FxIndexSet::default(); + let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + + write!( + f, + "
{};
", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + src_link, + 0, + cx, + ) + )?; } } - } - if out.is_empty() { - out.push_str("
"); - } + + if !has_notable_impl { + f.write_str("
")?; + } + + Ok(()) + }) + .to_string(); (format!("{:#}", ty.print(cx)), out) } From a40032d9e38a9237e61f8b909e5387b388698c15 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 23 Sep 2025 23:17:25 +0300 Subject: [PATCH 0898/1900] Simplify notable traits map serialization --- src/librustdoc/Cargo.toml | 2 +- src/librustdoc/html/render/mod.rs | 23 ++++------------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index f37a8d85361f2..0988d099eb4ff 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -12,7 +12,7 @@ path = "lib.rs" arrayvec = { version = "0.7", default-features = false } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" -indexmap = "2" +indexmap = { version = "2", features = ["serde"] } itertools = "0.12" minifier = { version = "0.3.5", default-features = false } pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 878af36e4ff1d..d3b35ec727c57 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,6 +48,7 @@ use std::path::PathBuf; use std::{fs, str}; use askama::Template; +use indexmap::IndexMap; use itertools::Either; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -60,8 +61,6 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; -use serde::ser::SerializeMap; -use serde::{Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -1722,23 +1721,9 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { } fn notable_traits_json<'a>(tys: impl Iterator, cx: &Context<'_>) -> String { - let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect(); - mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2)); - struct NotableTraitsMap(Vec<(String, String)>); - impl Serialize for NotableTraitsMap { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(self.0.len()))?; - for item in &self.0 { - map.serialize_entry(&item.0, &item.1)?; - } - map.end() - } - } - serde_json::to_string(&NotableTraitsMap(mp)) - .expect("serialize (string, string) -> json object cannot fail") + let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::>(); + mp.sort_unstable_keys(); + serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail") } #[derive(Clone, Copy, Debug)] From 93a96bf29c22d05894ca343b624cd09048aa7bb7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 26 Sep 2025 16:53:45 +0800 Subject: [PATCH 0899/1900] Migrate `replace_arith_op` assist to use `SyntaxEditor` --- .../src/handlers/replace_arith_op.rs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 94ac1c342d302..a3fb851fb0e29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -1,7 +1,7 @@ use ide_db::assists::{AssistId, GroupLabel}; use syntax::{ - AstNode, TextRange, - ast::{self, ArithOp, BinaryOp}, + AstNode, + ast::{self, ArithOp, BinaryOp, syntax_factory::SyntaxFactory}, }; use crate::assist_context::{AssistContext, Assists}; @@ -71,28 +71,31 @@ pub(crate) fn replace_arith_with_wrapping( fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> { let (lhs, op, rhs) = parse_binary_op(ctx)?; + let op_expr = lhs.syntax().parent()?; if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) { return None; } - let start = lhs.syntax().text_range().start(); - let end = rhs.syntax().text_range().end(); - let range = TextRange::new(start, end); - acc.add_group( &GroupLabel("Replace arithmetic...".into()), kind.assist_id(), kind.label(), - range, + op_expr.text_range(), |builder| { + let mut edit = builder.make_editor(rhs.syntax()); + let make = SyntaxFactory::with_mappings(); let method_name = kind.method_name(op); - if lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix) { - builder.replace(range, format!("({lhs}).{method_name}({rhs})")) - } else { - builder.replace(range, format!("{lhs}.{method_name}({rhs})")) - } + let needs_parentheses = + lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix); + let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs }; + let arith_expr = + make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])); + edit.replace(op_expr, arith_expr.syntax()); + + edit.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } From fc703ec5c88e678a4931f7bb8ed86efc141775f7 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 25 Sep 2025 09:30:31 +0000 Subject: [PATCH 0900/1900] fix doc comments to be more standard --- compiler/rustc_mir_build/src/builder/mod.rs | 6 +----- compiler/rustc_privacy/src/lib.rs | 22 ++++----------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index cdb2c5561ce6a..7ca94e655fb20 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -395,12 +395,10 @@ enum NeedsTemporary { Maybe, } -/////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` /// macro (and methods below) makes working with `BlockAnd` much more /// convenient. - #[must_use = "if you don't use one of these results, you're leaving a dangling edge"] struct BlockAnd(BasicBlock, T); @@ -438,9 +436,7 @@ macro_rules! unpack { }}; } -/////////////////////////////////////////////////////////////////////////// -/// the main entry point for building MIR for a function - +/// The main entry point for building MIR for a function. fn construct_fn<'tcx>( tcx: TyCtxt<'tcx>, fn_def: LocalDefId, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1bddbd03cc3d3..c9dbb204f61f7 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -45,7 +45,7 @@ use tracing::debug; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } //////////////////////////////////////////////////////////////////////////////// -/// Generic infrastructure used to implement specific visitors below. +// Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// struct LazyDefPathStr<'tcx> { @@ -309,10 +309,7 @@ fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visib if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } } -//////////////////////////////////////////////////////////////////////////////// /// Visitor used to determine impl visibility and reachability. -//////////////////////////////////////////////////////////////////////////////// - struct FindMin<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> { tcx: TyCtxt<'tcx>, effective_visibilities: &'a EffectiveVisibilities, @@ -387,10 +384,7 @@ impl VisibilityLike for EffectiveVisibility { } } -//////////////////////////////////////////////////////////////////////////////// /// The embargo visitor, used to determine the exports of the AST. -//////////////////////////////////////////////////////////////////////////////// - struct EmbargoVisitor<'tcx> { tcx: TyCtxt<'tcx>, @@ -849,9 +843,7 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> } } -//////////////////////////////////////////////////////////////////////////////// /// Visitor, used for EffectiveVisibilities table checking -//////////////////////////////////////////////////////////////////////////////// pub struct TestReachabilityVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, effective_visibilities: &'a EffectiveVisibilities, @@ -909,13 +901,11 @@ impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> { } } -////////////////////////////////////////////////////////////////////////////////////// /// Name privacy visitor, checks privacy and reports violations. +/// /// Most of name privacy checks are performed during the main resolution phase, /// or later in type checking when field accesses and associated items are resolved. /// This pass performs remaining checks for fields in struct expressions and patterns. -////////////////////////////////////////////////////////////////////////////////////// - struct NamePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, @@ -1120,12 +1110,10 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { } } -//////////////////////////////////////////////////////////////////////////////////////////// /// Type privacy visitor, checks types for privacy and reports violations. +/// /// Both explicitly written types and inferred types of expressions and patterns are checked. /// Checks are performed on "semantic" types regardless of names and their hygiene. -//////////////////////////////////////////////////////////////////////////////////////////// - struct TypePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, module_def_id: LocalModDefId, @@ -1345,13 +1333,11 @@ impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> { } } -/////////////////////////////////////////////////////////////////////////////// /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and /// finds any private components in it. +/// /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types /// and traits in public interfaces. -/////////////////////////////////////////////////////////////////////////////// - struct SearchInterfaceForPrivateItemsVisitor<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, From 7a2c1730336e487eed37e97014b7864ebd81962b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 15:25:27 +0200 Subject: [PATCH 0901/1900] Add new `tyalias` intra-doc link disambiguator --- src/librustdoc/passes/collect_intra_doc_links.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0da42f38251cc..79d74c3c4eb90 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -130,6 +130,7 @@ impl Res { DefKind::Static { .. } => "static", DefKind::Field => "field", DefKind::Variant | DefKind::Ctor(..) => "variant", + DefKind::TyAlias => "tyalias", // Now handle things that don't have a specific disambiguator _ => match kind .ns() @@ -1708,6 +1709,7 @@ impl Disambiguator { "value" => NS(Namespace::ValueNS), "macro" => NS(Namespace::MacroNS), "prim" | "primitive" => Primitive, + "tyalias" | "typealias" => Kind(DefKind::TyAlias), _ => return Err((format!("unknown disambiguator `{prefix}`"), 0..idx)), }; From dba45cde68ba25b7fa790e4e36593b6fd55d6d8a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 15:25:51 +0200 Subject: [PATCH 0902/1900] Add tests for new `tyalias` intra-doc link disambiguator --- .../type-alias-primitive-suggestion.rs | 21 ++++++++++ .../type-alias-primitive-suggestion.stderr | 39 +++++++++++++++++++ .../intra-doc/type-alias-primitive.rs | 14 +++++++ .../rustdoc/intra-doc/type-alias-primitive.rs | 21 ++++++++++ 4 files changed, 95 insertions(+) create mode 100644 tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.rs create mode 100644 tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.stderr create mode 100644 tests/rustdoc-ui/intra-doc/type-alias-primitive.rs create mode 100644 tests/rustdoc/intra-doc/type-alias-primitive.rs diff --git a/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.rs b/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.rs new file mode 100644 index 0000000000000..c4527c626d9c5 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.rs @@ -0,0 +1,21 @@ +// Ensure that no warning is emitted if the disambiguator is used for type alias. +// Regression test for . + +#![deny(rustdoc::broken_intra_doc_links)] + +pub struct Foo; + +#[allow(non_camel_case_types)] +pub type f32 = Foo; + +/// This function returns [`f32`]. +//~^ ERROR: `f32` is both a type alias and a primitive type +//~| HELP: to link to the type alias, prefix with `tyalias@` +//~| HELP: to link to the primitive type, prefix with `prim@` +pub fn my_fn() -> f32 {} + +/// This function returns [type@f32]. +//~^ ERROR: `f32` is both a type alias and a primitive type +//~| HELP: to link to the type alias, prefix with `tyalias@` +//~| HELP: to link to the primitive type, prefix with `prim@` +pub fn my_fn2() -> f32 {} diff --git a/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.stderr b/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.stderr new file mode 100644 index 0000000000000..c99e7d1d10434 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/type-alias-primitive-suggestion.stderr @@ -0,0 +1,39 @@ +error: `f32` is both a type alias and a primitive type + --> $DIR/type-alias-primitive-suggestion.rs:11:29 + | +LL | /// This function returns [`f32`]. + | ^^^ ambiguous link + | +note: the lint level is defined here + --> $DIR/type-alias-primitive-suggestion.rs:4:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the type alias, prefix with `tyalias@` + | +LL | /// This function returns [`tyalias@f32`]. + | ++++++++ +help: to link to the primitive type, prefix with `prim@` + | +LL | /// This function returns [`prim@f32`]. + | +++++ + +error: `f32` is both a type alias and a primitive type + --> $DIR/type-alias-primitive-suggestion.rs:17:28 + | +LL | /// This function returns [type@f32]. + | ^^^^^^^^ ambiguous link + | +help: to link to the type alias, prefix with `tyalias@` + | +LL - /// This function returns [type@f32]. +LL + /// This function returns [tyalias@f32]. + | +help: to link to the primitive type, prefix with `prim@` + | +LL - /// This function returns [type@f32]. +LL + /// This function returns [prim@f32]. + | + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/intra-doc/type-alias-primitive.rs b/tests/rustdoc-ui/intra-doc/type-alias-primitive.rs new file mode 100644 index 0000000000000..62b2c83eeca47 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/type-alias-primitive.rs @@ -0,0 +1,14 @@ +// Ensure that no warning is emitted if the disambiguator is used for type alias. +// Regression test for . + +//@ check-pass + +#![deny(rustdoc::broken_intra_doc_links)] + +pub struct Foo; + +#[allow(non_camel_case_types)] +pub type f32 = Foo; + +/// This function returns [`tyalias@f32`] and not [`prim@f32`]. +pub fn my_fn() -> f32 {} diff --git a/tests/rustdoc/intra-doc/type-alias-primitive.rs b/tests/rustdoc/intra-doc/type-alias-primitive.rs new file mode 100644 index 0000000000000..7504166cbcce2 --- /dev/null +++ b/tests/rustdoc/intra-doc/type-alias-primitive.rs @@ -0,0 +1,21 @@ +// Ensure that no warning is emitted if the disambiguator is used for type alias. +// Regression test for . + +#![crate_name = "foo"] +#![deny(rustdoc::broken_intra_doc_links)] + +pub struct Foo; + +#[allow(non_camel_case_types)] +pub type f32 = Foo; + +/// This function returns [`tyalias@f32`] and not [bla][`prim@f32`]. +//@ has 'foo/fn.my_fn.html' +//@ has - '//a[@href="https://github.com/type.f32.html"]' "f32" +//@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/std/primitive.f32.html"]' "bla" +pub fn my_fn() -> f32 { 0. } + +/// This function returns [`typealias@f32`]. +//@ has 'foo/fn.my_other_fn.html' +//@ has - '//a[@href="https://github.com/type.f32.html"]' "f32" +pub fn my_other_fn() -> f32 { 0. } From 62c457bb02488dc6df95c574f34dd0ce444475dd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 15:27:00 +0200 Subject: [PATCH 0903/1900] Mention `tyalias` in intra-doc link rustdoc book chapter --- .../rustdoc/src/write-documentation/linking-to-items-by-name.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md index 5e7854834028a..b1495b2575e23 100644 --- a/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md +++ b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md @@ -90,7 +90,7 @@ fn Foo() {} These prefixes will be stripped when displayed in the documentation, so `[struct@Foo]` will be rendered as `Foo`. The following prefixes are available: `struct`, `enum`, `trait`, `union`, `mod`, `module`, `const`, `constant`, `fn`, `function`, `field`, `variant`, `method`, `derive`, -`type`, `value`, `macro`, `prim` or `primitive`. +`type`, `value`, `macro`, `tyalias`, `typealias`, `prim` or `primitive`. You can also disambiguate for functions by adding `()` after the function name, or for macros by adding `!` after the macro name. The macro `!` can be followed by `()`, `{}`, From 3d25a36099bd57ba95f685b699bedcba6a9cae2c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 24 Aug 2025 19:26:12 +0800 Subject: [PATCH 0904/1900] Fix .let completion not work for let-chain Example --- ```rust fn main() { let bar = Some(true); if true && bar.$0 } ``` **Before this PR**: Cannot complete `.let` **After this PR**: ```rust fn main() { let bar = Some(true); if true && let Some($0) = bar } ``` --- .../ide-completion/src/completions/postfix.rs | 62 ++++++++++++++++--- .../ide-completion/src/context/analysis.rs | 33 +++------- 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index d355fdbe0739c..51acdb7802b3f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -11,12 +11,12 @@ use ide_db::{ text_edit::TextEdit, ty_filter::TryEnum, }; -use itertools::Either; use stdx::never; use syntax::{ SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, - TextRange, TextSize, + T, TextRange, TextSize, ast::{self, AstNode, AstToken}, + match_ast, }; use crate::{ @@ -113,12 +113,8 @@ pub(crate) fn complete_postfix( if let Some(parent) = dot_receiver_including_refs.syntax().parent() && let Some(second_ancestor) = parent.parent() { - let sec_ancestor_kind = second_ancestor.kind(); - if let Some(expr) = >::cast(second_ancestor) { - is_in_cond = match expr { - Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), - Either::Right(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), - } + if let Some(parent_expr) = ast::Expr::cast(parent) { + is_in_cond = is_in_condition(&parent_expr); } match &try_enum { Some(try_enum) if is_in_cond => match try_enum { @@ -147,7 +143,7 @@ pub(crate) fn complete_postfix( .add_to(acc, ctx.db); } }, - _ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => { + _ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => { postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) @@ -454,6 +450,22 @@ fn add_custom_postfix_completions( None } +pub(crate) fn is_in_condition(it: &ast::Expr) -> bool { + it.syntax() + .parent() + .and_then(|parent| { + Some(match_ast! { match parent { + ast::IfExpr(expr) => expr.condition()? == *it, + ast::WhileExpr(expr) => expr.condition()? == *it, + ast::MatchGuard(guard) => guard.condition()? == *it, + ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&]) + .then(|| is_in_condition(&bin_expr.into()))?, + _ => return None, + } }) + }) + .unwrap_or(false) +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -648,6 +660,38 @@ fn main() { let bar = Some(true); if let Some($0) = bar } +"#, + ); + check_edit( + "let", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if true && bar.$0 +} +"#, + r#" +fn main() { + let bar = Some(true); + if true && let Some($0) = bar +} +"#, + ); + check_edit( + "let", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if true && true && bar.$0 +} +"#, + r#" +fn main() { + let bar = Some(true); + if true && true && let Some($0) = bar +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 77a94403abb94..6a154c61b82ff 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -20,12 +20,15 @@ use syntax::{ match_ast, }; -use crate::context::{ - AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, - DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, - NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, PathKind, - PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, - TypeLocation, +use crate::{ + completions::postfix::is_in_condition, + context::{ + AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, + DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, + NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, + PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + TypeAscriptionTarget, TypeLocation, + }, }; #[derive(Debug)] @@ -1216,24 +1219,6 @@ fn classify_name_ref<'db>( Some(res) }; - fn is_in_condition(it: &ast::Expr) -> bool { - (|| { - let parent = it.syntax().parent()?; - if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { - Some(expr.condition()? == *it) - } else if let Some(expr) = ast::IfExpr::cast(parent.clone()) { - Some(expr.condition()? == *it) - } else if let Some(expr) = ast::BinExpr::cast(parent) - && expr.op_token()?.kind() == T![&&] - { - Some(is_in_condition(&expr.into())) - } else { - None - } - })() - .unwrap_or(false) - } - let make_path_kind_expr = |expr: ast::Expr| { let it = expr.syntax(); let in_block_expr = is_in_block(it); From 4a0c7cc730a5434574c41a6073d9efb92141af1a Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 26 Sep 2025 13:48:22 +0200 Subject: [PATCH 0905/1900] fix cycle head usages tracking --- .../src/solve/search_graph.rs | 22 ++- .../rustc_type_ir/src/search_graph/mod.rs | 141 ++++++++++++------ .../ignore-head-usages-provisional-cache.rs | 55 +++++++ 3 files changed, 166 insertions(+), 52 deletions(-) create mode 100644 tests/ui/traits/next-solver/cycles/ignore-head-usages-provisional-cache.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index aa9dfc9a9a265..4ae2af59a7031 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -74,13 +74,21 @@ where } } - fn is_initial_provisional_result( - cx: Self::Cx, - kind: PathKind, - input: CanonicalInput, - result: QueryResult, - ) -> bool { - Self::initial_provisional_result(cx, kind, input) == result + fn is_initial_provisional_result(result: QueryResult) -> Option { + match result { + Ok(response) => { + if has_no_inference_or_external_constraints(response) { + if response.value.certainty == Certainty::Yes { + return Some(PathKind::Coinductive); + } else if response.value.certainty == Certainty::overflow(false) { + return Some(PathKind::Unknown); + } + } + + None + } + Err(NoSolution) => Some(PathKind::Inductive), + } } fn on_stack_overflow(cx: I, input: CanonicalInput) -> QueryResult { diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8f8f019510fe8..a7902cd021038 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -86,12 +86,7 @@ pub trait Delegate: Sized { kind: PathKind, input: ::Input, ) -> ::Result; - fn is_initial_provisional_result( - cx: Self::Cx, - kind: PathKind, - input: ::Input, - result: ::Result, - ) -> bool; + fn is_initial_provisional_result(result: ::Result) -> Option; fn on_stack_overflow(cx: Self::Cx, input: ::Input) -> ::Result; fn on_fixpoint_overflow( cx: Self::Cx, @@ -215,6 +210,27 @@ impl HeadUsages { let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = self; inductive == 0 && unknown == 0 && coinductive == 0 && forced_ambiguity == 0 } + + fn is_single(self, path_kind: PathKind) -> bool { + match path_kind { + PathKind::Inductive => matches!( + self, + HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 }, + ), + PathKind::Unknown => matches!( + self, + HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 }, + ), + PathKind::Coinductive => matches!( + self, + HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 }, + ), + PathKind::ForcedAmbiguity => matches!( + self, + HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ }, + ), + } + } } #[derive(Debug, Default)] @@ -888,7 +904,29 @@ impl, X: Cx> SearchGraph { !entries.is_empty() }); } +} + +/// We need to rebase provisional cache entries when popping one of their cycle +/// heads from the stack. This may not necessarily mean that we've actually +/// reached a fixpoint for that cycle head, which impacts the way we rebase +/// provisional cache entries. +enum RebaseReason { + NoCycleUsages, + Ambiguity, + Overflow, + /// We've actually reached a fixpoint. + /// + /// This either happens in the first evaluation step for the cycle head. + /// In this case the used provisional result depends on the cycle `PathKind`. + /// We store this path kind to check whether the the provisional cache entry + /// we're rebasing relied on the same cycles. + /// + /// In later iterations cycles always return `stack_entry.provisional_result` + /// so we no longer depend on the `PathKind`. We store `None` in that case. + ReachedFixpoint(Option), +} +impl, X: Cx> SearchGraph { /// A necessary optimization to handle complex solver cycles. A provisional cache entry /// relies on a set of cycle heads and the path towards these heads. When popping a cycle /// head from the stack after we've finished computing it, we can't be sure that the @@ -908,8 +946,9 @@ impl, X: Cx> SearchGraph { /// to me. fn rebase_provisional_cache_entries( &mut self, + cx: X, stack_entry: &StackEntry, - mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -927,6 +966,10 @@ impl, X: Cx> SearchGraph { return true; }; + let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { + return false; + }; + // We're rebasing an entry `e` over a head `p`. This head // has a number of own heads `h` it depends on. // @@ -977,22 +1020,37 @@ impl, X: Cx> SearchGraph { let eph = ep.extend_with_paths(ph); heads.insert(head_index, eph, head.usages); } - } - let Some(head_index) = heads.opt_highest_cycle_head_index() else { - return false; - }; + // The provisional cache entry does depend on the provisional result + // of the popped cycle head. We need to mutate the result of our + // provisional cache entry in case we did not reach a fixpoint. + match rebase_reason { + // If the cycle head does not actually depend on itself, then + // the provisional result used by the provisional cache entry + // is not actually equal to the final provisional result. We + // need to discard the provisional cache entry in this case. + RebaseReason::NoCycleUsages => return false, + RebaseReason::Ambiguity => { + *result = D::propagate_ambiguity(cx, input, *result); + } + RebaseReason::Overflow => *result = D::on_fixpoint_overflow(cx, input), + RebaseReason::ReachedFixpoint(None) => {} + RebaseReason::ReachedFixpoint(Some(path_kind)) => { + if !popped_head.usages.is_single(path_kind) { + return false; + } + } + }; + } // We now care about the path from the next highest cycle head to the // provisional cache entry. *path_from_head = path_from_head.extend(Self::cycle_path_kind( &self.stack, stack_entry.step_kind_from_parent, - head_index, + new_highest_head_index, )); - // Mutate the result of the provisional cache entry in case we did - // not reach a fixpoint. - *result = mutate_result(input, *result); + true }); !entries.is_empty() @@ -1209,33 +1267,19 @@ impl, X: Cx> SearchGraph { /// Whether we've reached a fixpoint when evaluating a cycle head. fn reached_fixpoint( &mut self, - cx: X, stack_entry: &StackEntry, usages: HeadUsages, result: X::Result, - ) -> bool { + ) -> Result, ()> { let provisional_result = stack_entry.provisional_result; - if usages.is_empty() { - true - } else if let Some(provisional_result) = provisional_result { - provisional_result == result + if let Some(provisional_result) = provisional_result { + if provisional_result == result { Ok(None) } else { Err(()) } + } else if let Some(path_kind) = D::is_initial_provisional_result(result) + .filter(|&path_kind| usages.is_single(path_kind)) + { + Ok(Some(path_kind)) } else { - let check = |k| D::is_initial_provisional_result(cx, k, stack_entry.input, result); - match usages { - HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 } => { - check(PathKind::Inductive) - } - HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 } => { - check(PathKind::Unknown) - } - HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 } => { - check(PathKind::Coinductive) - } - HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ } => { - check(PathKind::ForcedAmbiguity) - } - _ => false, - } + Err(()) } } @@ -1280,8 +1324,19 @@ impl, X: Cx> SearchGraph { // is equal to the provisional result of the previous iteration, or because // this was only the head of either coinductive or inductive cycles, and the // final result is equal to the initial response for that case. - if self.reached_fixpoint(cx, &stack_entry, usages, result) { - self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); + if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::ReachedFixpoint(fixpoint), + ); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); + } else if usages.is_empty() { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::NoCycleUsages, + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1298,9 +1353,7 @@ impl, X: Cx> SearchGraph { // we also taint all provisional cache entries which depend on the // current goal. if D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries(&stack_entry, |input, _| { - D::propagate_ambiguity(cx, input, result) - }); + self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1310,9 +1363,7 @@ impl, X: Cx> SearchGraph { if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); let result = D::on_fixpoint_overflow(cx, input); - self.rebase_provisional_cache_entries(&stack_entry, |input, _| { - D::on_fixpoint_overflow(cx, input) - }); + self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } diff --git a/tests/ui/traits/next-solver/cycles/ignore-head-usages-provisional-cache.rs b/tests/ui/traits/next-solver/cycles/ignore-head-usages-provisional-cache.rs new file mode 100644 index 0000000000000..9a33ae536442f --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/ignore-head-usages-provisional-cache.rs @@ -0,0 +1,55 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// A regression test for trait-system-refactor-initiative#232. We've +// previously incorrectly rebased provisional cache entries even if +// the cycle head didn't reach a fixpoint as it did not depend on any +// cycles itself. +// +// Just because the result of a goal does not depend on its own provisional +// result, it does not mean its nested goals don't depend on its result. +struct B; +struct C; +struct D; + +pub trait Trait { + type Output; +} +macro_rules! k { + ($t:ty) => { + <$t as Trait>::Output + }; +} + +trait CallB { + type Output; + type Return; +} + +trait CallC { + type Output; + type Return; +} + +trait CallD { + type Output; +} + +fn foo() +where + X: Trait, + Y: Trait, + D: CallD, + C: CallC<>::Output>, + >::Output>>::Output: Trait, + B: CallB< + >::Output>>::Return, + >::Output>>::Output, + >, + >::Output>>::Return, + >::Output>>::Output, + >>::Output: Trait, +{ +} +fn main() {} From 4e44d58bbc199ff7c17a98b283621a6df327d60f Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 26 Sep 2025 14:51:13 +0200 Subject: [PATCH 0906/1900] rename `search_graph::Delegate` fns --- .../src/solve/search_graph.rs | 4 ++-- compiler/rustc_type_ir/src/search_graph/mod.rs | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 4ae2af59a7031..109c8476ccb16 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -91,11 +91,11 @@ where } } - fn on_stack_overflow(cx: I, input: CanonicalInput) -> QueryResult { + fn stack_overflow_result(cx: I, input: CanonicalInput) -> QueryResult { response_no_constraints(cx, input, Certainty::overflow(true)) } - fn on_fixpoint_overflow(cx: I, input: CanonicalInput) -> QueryResult { + fn fixpoint_overflow_result(cx: I, input: CanonicalInput) -> QueryResult { response_no_constraints(cx, input, Certainty::overflow(false)) } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index a7902cd021038..7aa58d096d5c0 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -87,8 +87,11 @@ pub trait Delegate: Sized { input: ::Input, ) -> ::Result; fn is_initial_provisional_result(result: ::Result) -> Option; - fn on_stack_overflow(cx: Self::Cx, input: ::Input) -> ::Result; - fn on_fixpoint_overflow( + fn stack_overflow_result( + cx: Self::Cx, + input: ::Input, + ) -> ::Result; + fn fixpoint_overflow_result( cx: Self::Cx, input: ::Input, ) -> ::Result; @@ -885,7 +888,7 @@ impl, X: Cx> SearchGraph { } debug!("encountered stack overflow"); - D::on_stack_overflow(cx, input) + D::stack_overflow_result(cx, input) } /// When reevaluating a goal with a changed provisional result, all provisional cache entry @@ -1033,7 +1036,7 @@ impl, X: Cx> SearchGraph { RebaseReason::Ambiguity => { *result = D::propagate_ambiguity(cx, input, *result); } - RebaseReason::Overflow => *result = D::on_fixpoint_overflow(cx, input), + RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { if !popped_head.usages.is_single(path_kind) { @@ -1362,7 +1365,7 @@ impl, X: Cx> SearchGraph { i += 1; if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); - let result = D::on_fixpoint_overflow(cx, input); + let result = D::fixpoint_overflow_result(cx, input); self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } From a535c7be5444fb6584eecc99f53e6cdb710ff70f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Sep 2025 13:59:06 +0200 Subject: [PATCH 0907/1900] Ignore more failing ui tests for GCC backend --- .../array-slice-vec/box-of-array-of-drop-1.rs | 1 + .../array-slice-vec/box-of-array-of-drop-2.rs | 1 + tests/ui/array-slice-vec/nested-vec-3.rs | 1 + tests/ui/array-slice-vec/slice-panic-1.rs | 1 + tests/ui/array-slice-vec/slice-panic-2.rs | 1 + .../asm/global-asm-isnt-really-a-mir-body.rs | 1 + tests/ui/asm/x86_64/goto.rs | 1 + tests/ui/asm/x86_64/goto.stderr | 4 +- tests/ui/asm/x86_64/srcloc.rs | 1 + tests/ui/asm/x86_64/srcloc.stderr | 50 ++++----- tests/ui/attributes/fn-align-dyn.rs | 1 + tests/ui/attributes/main-removed-2/main.rs | 1 + .../backtrace/synchronized-panic-handler.rs | 1 + .../synchronized-panic-handler.run.stderr | 4 +- tests/ui/box/unit/unwind-unique.rs | 1 + tests/ui/c-variadic/inherent-method.rs | 1 + tests/ui/c-variadic/trait-method.rs | 1 + tests/ui/c-variadic/valid.rs | 1 + .../assume-incomplete.rs | 1 + .../codegen/issue-82833-slice-miscompile.rs | 1 + tests/ui/codegen/llvm-args-invalid-flag.rs | 1 + tests/ui/codegen/remark-flag-functionality.rs | 1 + .../codegen/virtual-function-elimination.rs | 1 + tests/ui/coroutine/gen_block_panic.rs | 1 + tests/ui/coroutine/gen_block_panic.stderr | 2 +- tests/ui/delegation/fn-header-variadic.rs | 1 + tests/ui/delegation/fn-header-variadic.stderr | 4 +- tests/ui/drop/drop-trait-enum.rs | 1 + tests/ui/drop/terminate-in-initializer.rs | 1 + .../callee_is_track_caller.rs | 1 + .../callee_is_track_caller.stderr | 2 +- .../callee_is_track_caller_polymorphic.rs | 1 + .../callee_is_track_caller_polymorphic.stderr | 2 +- tests/ui/explicit-tail-calls/drop-order.rs | 1 + tests/ui/explicit-tail-calls/indexer.rs | 1 + tests/ui/explicit-tail-calls/recursion-etc.rs | 1 + tests/ui/extern/extern-types-field-offset.rs | 1 + ...llow-unwind-when-calling-panic-directly.rs | 1 + ...sue-64655-extern-rust-must-allow-unwind.rs | 1 + ...at-args-capture-from-pm-first-arg-macro.rs | 1 + ...rgs-capture-from-pm-first-arg-macro.stderr | 2 +- tests/ui/frontmatter/proc-macro-observer.rs | 1 + .../issue-77523-def-site-async-await.rs | 1 + .../precise-capturing/external-macro.rs | 1 + .../invalid-llvm-passes.rs | 1 + tests/ui/issues/issue-25089.rs | 1 + tests/ui/issues/issue-26655.rs | 1 + tests/ui/issues/issue-29485.rs | 1 + tests/ui/issues/issue-30018-panic.rs | 1 + tests/ui/issues/issue-44056.rs | 1 + .../issues/issue-68696-catch-during-unwind.rs | 1 + .../common-linkage-non-zero-init.rs | 1 + .../raw-dylib/elf/glibc-x86_64.rs | 1 + .../ui/linking/no-gc-encapsulation-symbols.rs | 1 + ...nused-qualification-in-derive-expansion.rs | 1 + tests/ui/lto/debuginfo-lto-alloc.rs | 1 + tests/ui/lto/debuginfo-lto.rs | 1 + tests/ui/lto/dwarf-mixed-versions-lto.rs | 1 + tests/ui/lto/fat-lto.rs | 1 + tests/ui/lto/issue-100772.rs | 1 + tests/ui/lto/lto-duplicate-symbols.rs | 1 + tests/ui/lto/lto-many-codegen-units.rs | 1 + tests/ui/lto/lto-rustc-loads-linker-plugin.rs | 1 + tests/ui/lto/lto-still-runs-thread-dtors.rs | 1 + tests/ui/macros/same-sequence-span.rs | 1 + tests/ui/macros/same-sequence-span.stderr | 12 +- .../ui/numbers-arithmetic/int-abs-overflow.rs | 1 + tests/ui/numbers-arithmetic/issue-8460.rs | 1 + tests/ui/panic-runtime/lto-unwind.rs | 1 + tests/ui/panics/oom-panic-unwind.rs | 1 + .../panics/panic-handler-chain-update-hook.rs | 1 + tests/ui/panics/panic-handler-chain.rs | 1 + tests/ui/panics/panic-handler-flail-wildly.rs | 1 + tests/ui/panics/panic-handler-set-twice.rs | 1 + tests/ui/panics/panic-in-dtor-drops-fields.rs | 1 + tests/ui/panics/panic-recover-propagate.rs | 1 + .../panics/rvalue-cleanup-during-box-panic.rs | 1 + .../panics/unwind-force-no-unwind-tables.rs | 1 + ...971-outer-attr-following-inner-attr-ice.rs | 1 + ...outer-attr-following-inner-attr-ice.stderr | 2 +- .../unicode-control-codepoints-macros.rs | 1 + .../unicode-control-codepoints-macros.stderr | 10 +- .../parser/tuple-index-suffix-proc-macro.rs | 1 + .../tuple-index-suffix-proc-macro.stderr | 8 +- tests/ui/proc-macro/add-impl.rs | 1 + .../ambiguous-builtin-attrs-test.rs | 1 + .../ambiguous-builtin-attrs-test.stderr | 2 +- .../ui/proc-macro/ambiguous-builtin-attrs.rs | 1 + .../proc-macro/ambiguous-builtin-attrs.stderr | 30 ++--- tests/ui/proc-macro/append-impl.rs | 1 + tests/ui/proc-macro/attr-args.rs | 1 + tests/ui/proc-macro/attr-invalid-exprs.rs | 1 + tests/ui/proc-macro/attr-invalid-exprs.stderr | 6 +- tests/ui/proc-macro/attr-on-trait.rs | 1 + tests/ui/proc-macro/bang-macro.rs | 1 + tests/ui/proc-macro/call-site.rs | 1 + tests/ui/proc-macro/count_compound_ops.rs | 1 + tests/ui/proc-macro/derive-bad.rs | 1 + tests/ui/proc-macro/derive-bad.stderr | 6 +- .../ui/proc-macro/derive-helper-shadowing.rs | 1 + .../proc-macro/derive-helper-shadowing.stderr | 18 +-- tests/ui/proc-macro/derive-same-struct.rs | 1 + tests/ui/proc-macro/edition-imports-2018.rs | 1 + tests/ui/proc-macro/env.rs | 1 + tests/ui/proc-macro/expand-expr.rs | 3 +- tests/ui/proc-macro/expand-expr.stderr | 14 +-- tests/ui/proc-macro/expand-to-unstable.rs | 1 + tests/ui/proc-macro/expand-to-unstable.stderr | 2 +- tests/ui/proc-macro/expand-with-a-macro.rs | 1 + .../ui/proc-macro/gen-macro-rules-hygiene.rs | 1 + .../proc-macro/gen-macro-rules-hygiene.stderr | 6 +- tests/ui/proc-macro/gen-macro-rules.rs | 1 + tests/ui/proc-macro/generate-mod.rs | 1 + tests/ui/proc-macro/generate-mod.stderr | 28 ++--- tests/ui/proc-macro/hygiene_example.rs | 1 + tests/ui/proc-macro/is-available.rs | 1 + .../issue-104884-trait-impl-sugg-err.rs | 1 + .../issue-104884-trait-impl-sugg-err.stderr | 18 +-- tests/ui/proc-macro/issue-107113-wrap.rs | 1 + tests/ui/proc-macro/issue-107113-wrap.stderr | 2 +- tests/ui/proc-macro/issue-118809.rs | 1 + tests/ui/proc-macro/issue-118809.stderr | 4 +- tests/ui/proc-macro/issue-38586.rs | 1 + tests/ui/proc-macro/issue-38586.stderr | 2 +- .../issue-59191-replace-root-with-fn.rs | 1 + tests/ui/proc-macro/issue-79148.rs | 1 + tests/ui/proc-macro/issue-79148.stderr | 4 +- tests/ui/proc-macro/issue-83510.rs | 1 + tests/ui/proc-macro/issue-83510.stderr | 8 +- tests/ui/proc-macro/issue-91800.rs | 1 + tests/ui/proc-macro/issue-91800.stderr | 14 +-- tests/ui/proc-macro/lifetimes-rpass.rs | 1 + tests/ui/proc-macro/lints_in_proc_macros.rs | 1 + .../ui/proc-macro/lints_in_proc_macros.stderr | 2 +- tests/ui/proc-macro/load-two.rs | 1 + .../proc-macro/macro-crate-multi-decorator.rs | 1 + .../proc-macro/macro_rules_edition_from_pm.rs | 1 + tests/ui/proc-macro/match-expander.rs | 1 + tests/ui/proc-macro/match-expander.stderr | 2 +- tests/ui/proc-macro/mixed-site-span.rs | 1 + tests/ui/proc-macro/mixed-site-span.stderr | 104 +++++++++--------- tests/ui/proc-macro/modify-ast.rs | 1 + tests/ui/proc-macro/parent-source-spans.rs | 1 + .../ui/proc-macro/parent-source-spans.stderr | 42 +++---- tests/ui/proc-macro/quote/basic.rs | 1 + tests/ui/proc-macro/span-api-tests.rs | 1 + tests/ui/proc-macro/span-from-proc-macro.rs | 1 + .../ui/proc-macro/span-from-proc-macro.stderr | 8 +- tests/ui/proc-macro/weird-hygiene.rs | 1 + tests/ui/proc-macro/weird-hygiene.stderr | 4 +- tests/ui/process/multi-panic.rs | 1 + tests/ui/resolve/prelude-order.rs | 1 + tests/ui/resolve/prelude-order.stderr | 10 +- .../edition-spans.rs | 1 + tests/ui/runtime/backtrace-debuginfo.rs | 1 + tests/ui/runtime/out-of-stack.rs | 1 + .../suggestions-not-always-applicable.fixed | 1 + .../suggestions-not-always-applicable.rs | 1 + .../rust-2021/reserved-prefixes-via-macro.rs | 1 + .../reserved-guarded-strings-via-macro.rs | 1 + .../unsafe-attributes-from-pm.rs | 1 + .../sanitizer/cfi/transparent-has-regions.rs | 1 + .../issue-111184-cfi-coroutine-witness.rs | 1 + tests/ui/sepcomp/sepcomp-lib-lto.rs | 1 + tests/ui/sepcomp/sepcomp-unwind.rs | 1 + .../ui/simd/intrinsic/generic-arithmetic-2.rs | 1 + .../intrinsic/generic-arithmetic-2.stderr | 56 +++++----- tests/ui/simd/intrinsic/generic-elements.rs | 1 + .../ui/simd/intrinsic/generic-elements.stderr | 42 +++---- tests/ui/simd/masked-load-store-build-fail.rs | 1 + .../simd/masked-load-store-build-fail.stderr | 16 +-- .../monomorphize-shuffle-index.generic.stderr | 2 +- tests/ui/simd/monomorphize-shuffle-index.rs | 1 + tests/ui/simd/not-out-of-bounds.rs | 1 + tests/ui/simd/not-out-of-bounds.stderr | 18 +-- .../unit-like-struct-drop-run.rs | 1 + tests/ui/suggestions/issue-61963.rs | 1 + tests/ui/suggestions/issue-61963.stderr | 6 +- tests/ui/suggestions/suggest-ref-macro.rs | 1 + tests/ui/suggestions/suggest-ref-macro.stderr | 12 +- .../test-attrs/test-panic-while-printing.rs | 1 + tests/ui/threads-sendsync/task-stderr.rs | 1 + tests/ui/threads-sendsync/unwind-resource.rs | 1 + tests/ui/traits/clone-unwind-rc-cleanup.rs | 1 + ...alid-sugg-for-derive-macro-issue-136343.rs | 1 + ...-sugg-for-derive-macro-issue-136343.stderr | 2 +- 186 files changed, 440 insertions(+), 297 deletions(-) diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs index c7c05946c4ca9..5f4e381c983a2 100644 --- a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs +++ b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc #![allow(overflowing_literals)] diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs index 98175a26ec0d1..ea37d3e721264 100644 --- a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs +++ b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc #![allow(overflowing_literals)] diff --git a/tests/ui/array-slice-vec/nested-vec-3.rs b/tests/ui/array-slice-vec/nested-vec-3.rs index 51975743742cf..e3c04ed6f6bf0 100644 --- a/tests/ui/array-slice-vec/nested-vec-3.rs +++ b/tests/ui/array-slice-vec/nested-vec-3.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc #![allow(overflowing_literals)] diff --git a/tests/ui/array-slice-vec/slice-panic-1.rs b/tests/ui/array-slice-vec/slice-panic-1.rs index a745dff96afc7..d7c1098fa2bda 100644 --- a/tests/ui/array-slice-vec/slice-panic-1.rs +++ b/tests/ui/array-slice-vec/slice-panic-1.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc // Test that if a slicing expr[..] fails, the correct cleanups happen. diff --git a/tests/ui/array-slice-vec/slice-panic-2.rs b/tests/ui/array-slice-vec/slice-panic-2.rs index 483a4cbe245db..157e716a95d49 100644 --- a/tests/ui/array-slice-vec/slice-panic-2.rs +++ b/tests/ui/array-slice-vec/slice-panic-2.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc // Test that if a slicing expr[..] fails, the correct cleanups happen. diff --git a/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs index aef25d057d4ba..94dab4235e093 100644 --- a/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs +++ b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs @@ -18,6 +18,7 @@ //@ build-pass //@ needs-asm-support +//@ ignore-backends: gcc use std::arch::global_asm; diff --git a/tests/ui/asm/x86_64/goto.rs b/tests/ui/asm/x86_64/goto.rs index 00a8e588f9673..c1dbce0d1c912 100644 --- a/tests/ui/asm/x86_64/goto.rs +++ b/tests/ui/asm/x86_64/goto.rs @@ -1,6 +1,7 @@ //@ only-x86_64 //@ run-pass //@ needs-asm-support +//@ ignore-backends: gcc #![deny(unreachable_code)] #![feature(asm_goto_with_outputs)] diff --git a/tests/ui/asm/x86_64/goto.stderr b/tests/ui/asm/x86_64/goto.stderr index 78b726b3f3d31..f8f09f32f6cbc 100644 --- a/tests/ui/asm/x86_64/goto.stderr +++ b/tests/ui/asm/x86_64/goto.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/goto.rs:143:9 + --> $DIR/goto.rs:144:9 | LL | / asm!( LL | | "jmp {}", @@ -13,7 +13,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^ unreachable statement | note: the lint level is defined here - --> $DIR/goto.rs:133:8 + --> $DIR/goto.rs:134:8 | LL | #[warn(unreachable_code)] | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/x86_64/srcloc.rs b/tests/ui/asm/x86_64/srcloc.rs index 2938bafe5e70c..f4ffa8c5c3b2c 100644 --- a/tests/ui/asm/x86_64/srcloc.rs +++ b/tests/ui/asm/x86_64/srcloc.rs @@ -1,6 +1,7 @@ //@ only-x86_64 //@ build-fail //@ compile-flags: -Ccodegen-units=1 +//@ ignore-backends: gcc use std::arch::asm; diff --git a/tests/ui/asm/x86_64/srcloc.stderr b/tests/ui/asm/x86_64/srcloc.stderr index bb4e855163d77..b2079120ec065 100644 --- a/tests/ui/asm/x86_64/srcloc.stderr +++ b/tests/ui/asm/x86_64/srcloc.stderr @@ -1,5 +1,5 @@ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:11:15 + --> $DIR/srcloc.rs:12:15 | LL | asm!("invalid_instruction"); | ^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:15:13 + --> $DIR/srcloc.rs:16:13 | LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:20:13 + --> $DIR/srcloc.rs:21:13 | LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:26:13 + --> $DIR/srcloc.rs:27:13 | LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:33:13 + --> $DIR/srcloc.rs:34:13 | LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:38:14 + --> $DIR/srcloc.rs:39:14 | LL | asm!(concat!("invalid", "_", "instruction")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ warning: scale factor without index register is ignored - --> $DIR/srcloc.rs:41:15 + --> $DIR/srcloc.rs:42:15 | LL | asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +83,7 @@ LL | movaps %xmm3, (%esi, 2) | ^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:45:14 + --> $DIR/srcloc.rs:46:14 | LL | "invalid_instruction", | ^^^^^^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:51:14 + --> $DIR/srcloc.rs:52:14 | LL | "invalid_instruction", | ^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:58:14 + --> $DIR/srcloc.rs:59:14 | LL | "invalid_instruction", | ^^^^^^^^^^^^^^^^^^^ @@ -119,7 +119,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:65:13 + --> $DIR/srcloc.rs:66:13 | LL | concat!("invalid", "_", "instruction"), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -131,7 +131,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:72:13 + --> $DIR/srcloc.rs:73:13 | LL | concat!("invalid", "_", "instruction"), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | invalid_instruction | ^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:79:14 + --> $DIR/srcloc.rs:80:14 | LL | "invalid_instruction1", | ^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | invalid_instruction1 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:80:14 + --> $DIR/srcloc.rs:81:14 | LL | "invalid_instruction2", | ^^^^^^^^^^^^^^^^^^^^ @@ -167,7 +167,7 @@ LL | invalid_instruction2 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:86:13 + --> $DIR/srcloc.rs:87:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -182,7 +182,7 @@ LL | invalid_instruction1 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:86:13 + --> $DIR/srcloc.rs:87:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -197,7 +197,7 @@ LL | invalid_instruction2 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:95:13 + --> $DIR/srcloc.rs:96:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -212,7 +212,7 @@ LL | invalid_instruction1 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:95:13 + --> $DIR/srcloc.rs:96:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -227,7 +227,7 @@ LL | invalid_instruction2 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:99:13 + --> $DIR/srcloc.rs:100:13 | LL | / concat!( LL | | "invalid", "_", "instruction3", "\n", @@ -242,7 +242,7 @@ LL | invalid_instruction3 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:99:13 + --> $DIR/srcloc.rs:100:13 | LL | / concat!( LL | | "invalid", "_", "instruction3", "\n", @@ -257,7 +257,7 @@ LL | invalid_instruction4 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:110:13 + --> $DIR/srcloc.rs:111:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -272,7 +272,7 @@ LL | invalid_instruction1 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:110:13 + --> $DIR/srcloc.rs:111:13 | LL | / concat!( LL | | "invalid", "_", "instruction1", "\n", @@ -287,7 +287,7 @@ LL | invalid_instruction2 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:114:13 + --> $DIR/srcloc.rs:115:13 | LL | / concat!( LL | | "invalid", "_", "instruction3", "\n", @@ -302,7 +302,7 @@ LL | invalid_instruction3 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:114:13 + --> $DIR/srcloc.rs:115:13 | LL | / concat!( LL | | "invalid", "_", "instruction3", "\n", @@ -317,7 +317,7 @@ LL | invalid_instruction4 | ^^^^^^^^^^^^^^^^^^^^ error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:127:14 + --> $DIR/srcloc.rs:128:14 | LL | "invalid_instruction" | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/fn-align-dyn.rs b/tests/ui/attributes/fn-align-dyn.rs index 3778c75a2caa7..91e2dab65a3cc 100644 --- a/tests/ui/attributes/fn-align-dyn.rs +++ b/tests/ui/attributes/fn-align-dyn.rs @@ -1,5 +1,6 @@ //@ run-pass //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) +//@ ignore-backends: gcc // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity #![feature(rustc_attrs)] diff --git a/tests/ui/attributes/main-removed-2/main.rs b/tests/ui/attributes/main-removed-2/main.rs index 53696d68ced07..21a05dc4b40be 100644 --- a/tests/ui/attributes/main-removed-2/main.rs +++ b/tests/ui/attributes/main-removed-2/main.rs @@ -2,6 +2,7 @@ //@ proc-macro: tokyo.rs //@ compile-flags:--extern tokyo //@ edition:2021 +//@ ignore-backends: gcc use tokyo::main; diff --git a/tests/ui/backtrace/synchronized-panic-handler.rs b/tests/ui/backtrace/synchronized-panic-handler.rs index 29431ae3c458a..ef7cc1faec8de 100644 --- a/tests/ui/backtrace/synchronized-panic-handler.rs +++ b/tests/ui/backtrace/synchronized-panic-handler.rs @@ -4,6 +4,7 @@ //@ exec-env:RUST_BACKTRACE=0 //@ needs-threads //@ needs-unwind +//@ ignore-backends: gcc use std::thread; const PANIC_MESSAGE: &str = "oops oh no woe is me"; diff --git a/tests/ui/backtrace/synchronized-panic-handler.run.stderr b/tests/ui/backtrace/synchronized-panic-handler.run.stderr index c604d49c193c5..5296a0d39ff30 100644 --- a/tests/ui/backtrace/synchronized-panic-handler.run.stderr +++ b/tests/ui/backtrace/synchronized-panic-handler.run.stderr @@ -1,7 +1,7 @@ -thread '' ($TID) panicked at $DIR/synchronized-panic-handler.rs:11:5: +thread '' ($TID) panicked at $DIR/synchronized-panic-handler.rs:12:5: oops oh no woe is me note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread '' ($TID) panicked at $DIR/synchronized-panic-handler.rs:11:5: +thread '' ($TID) panicked at $DIR/synchronized-panic-handler.rs:12:5: oops oh no woe is me diff --git a/tests/ui/box/unit/unwind-unique.rs b/tests/ui/box/unit/unwind-unique.rs index 1da55c45ee969..ed549f50a740a 100644 --- a/tests/ui/box/unit/unwind-unique.rs +++ b/tests/ui/box/unit/unwind-unique.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc use std::thread; diff --git a/tests/ui/c-variadic/inherent-method.rs b/tests/ui/c-variadic/inherent-method.rs index 537bae7b3f0f4..c5256aaa1fea3 100644 --- a/tests/ui/c-variadic/inherent-method.rs +++ b/tests/ui/c-variadic/inherent-method.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![feature(c_variadic)] #[repr(transparent)] diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs index 97da0706a3a18..876a303f53ba5 100644 --- a/tests/ui/c-variadic/trait-method.rs +++ b/tests/ui/c-variadic/trait-method.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![feature(c_variadic)] #[repr(transparent)] diff --git a/tests/ui/c-variadic/valid.rs b/tests/ui/c-variadic/valid.rs index 5a0b32026dc7a..8b42eb4932906 100644 --- a/tests/ui/c-variadic/valid.rs +++ b/tests/ui/c-variadic/valid.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![feature(c_variadic)] // In rust (and C23 and above) `...` can be the only argument. diff --git a/tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs b/tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs index cafb7389e29fa..2ca004d9a906d 100644 --- a/tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs +++ b/tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs @@ -2,6 +2,7 @@ //@ proc-macro: ver-cfg-rel.rs //@ revisions: assume no_assume //@ [assume]compile-flags: -Z assume-incomplete-release +//@ ignore-backends: gcc #![feature(cfg_version)] diff --git a/tests/ui/codegen/issue-82833-slice-miscompile.rs b/tests/ui/codegen/issue-82833-slice-miscompile.rs index 32eac923a6361..e0cb871662967 100644 --- a/tests/ui/codegen/issue-82833-slice-miscompile.rs +++ b/tests/ui/codegen/issue-82833-slice-miscompile.rs @@ -1,5 +1,6 @@ //@ run-pass //@ compile-flags: -Ccodegen-units=1 -Cllvm-args=--inline-threshold=0 -Clink-dead-code -Copt-level=0 -Cdebuginfo=2 +//@ ignore-backends: gcc // Make sure LLVM does not miscompile this. diff --git a/tests/ui/codegen/llvm-args-invalid-flag.rs b/tests/ui/codegen/llvm-args-invalid-flag.rs index a8fa55a220a58..f88a7101abda1 100644 --- a/tests/ui/codegen/llvm-args-invalid-flag.rs +++ b/tests/ui/codegen/llvm-args-invalid-flag.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Cllvm-args=-not-a-real-llvm-arg //@ normalize-stderr: "--help" -> "-help" //@ normalize-stderr: "\n(\n|.)*" -> "" +//@ ignore-backends: gcc // I'm seeing "--help" locally, but "-help" in CI, so I'm normalizing it to just "-help". diff --git a/tests/ui/codegen/remark-flag-functionality.rs b/tests/ui/codegen/remark-flag-functionality.rs index 797c55ba830c1..4cfc5f5c8ec19 100644 --- a/tests/ui/codegen/remark-flag-functionality.rs +++ b/tests/ui/codegen/remark-flag-functionality.rs @@ -17,6 +17,7 @@ //@ dont-check-compiler-stderr //@ dont-require-annotations: NOTE +//@ ignore-backends: gcc #[no_mangle] #[inline(never)] diff --git a/tests/ui/codegen/virtual-function-elimination.rs b/tests/ui/codegen/virtual-function-elimination.rs index 3cbeb1293e50a..90fc86f95c5c6 100644 --- a/tests/ui/codegen/virtual-function-elimination.rs +++ b/tests/ui/codegen/virtual-function-elimination.rs @@ -2,6 +2,7 @@ //@ compile-flags: -Zvirtual-function-elimination=true -Clto=true //@ only-x86_64 //@ no-prefer-dynamic +//@ ignore-backends: gcc // issue #123955 pub fn test0() { diff --git a/tests/ui/coroutine/gen_block_panic.rs b/tests/ui/coroutine/gen_block_panic.rs index b6362d5046a3b..5417ed583e8ca 100644 --- a/tests/ui/coroutine/gen_block_panic.rs +++ b/tests/ui/coroutine/gen_block_panic.rs @@ -1,6 +1,7 @@ //@ edition: 2024 //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc #![feature(gen_blocks)] fn main() { diff --git a/tests/ui/coroutine/gen_block_panic.stderr b/tests/ui/coroutine/gen_block_panic.stderr index a43c9e03691a5..d0a146e7baf9a 100644 --- a/tests/ui/coroutine/gen_block_panic.stderr +++ b/tests/ui/coroutine/gen_block_panic.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/gen_block_panic.rs:10:9 + --> $DIR/gen_block_panic.rs:11:9 | LL | panic!("foo"); | ------------- any code following this expression is unreachable diff --git a/tests/ui/delegation/fn-header-variadic.rs b/tests/ui/delegation/fn-header-variadic.rs index 2c83d64d0b3ff..346c49f08e5d3 100644 --- a/tests/ui/delegation/fn-header-variadic.rs +++ b/tests/ui/delegation/fn-header-variadic.rs @@ -1,4 +1,5 @@ //@ aux-crate:fn_header_aux=fn-header-aux.rs +//@ ignore-backends: gcc #![feature(c_variadic)] #![feature(fn_delegation)] diff --git a/tests/ui/delegation/fn-header-variadic.stderr b/tests/ui/delegation/fn-header-variadic.stderr index 688a965fb4d5c..c2d7672939fcc 100644 --- a/tests/ui/delegation/fn-header-variadic.stderr +++ b/tests/ui/delegation/fn-header-variadic.stderr @@ -1,5 +1,5 @@ error: delegation to C-variadic functions is not allowed - --> $DIR/fn-header-variadic.rs:11:17 + --> $DIR/fn-header-variadic.rs:12:17 | LL | pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} | ------------------------------------------------------------- callee defined here @@ -8,7 +8,7 @@ LL | reuse to_reuse::variadic_fn; | ^^^^^^^^^^^ error: delegation to C-variadic functions is not allowed - --> $DIR/fn-header-variadic.rs:13:22 + --> $DIR/fn-header-variadic.rs:14:22 | LL | reuse fn_header_aux::variadic_fn_extern; | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/drop/drop-trait-enum.rs b/tests/ui/drop/drop-trait-enum.rs index 5a88d959ec6fa..efb6b2a912ab1 100644 --- a/tests/ui/drop/drop-trait-enum.rs +++ b/tests/ui/drop/drop-trait-enum.rs @@ -4,6 +4,7 @@ #![allow(unused_variables)] //@ needs-threads //@ needs-unwind +//@ ignore-backends: gcc use std::thread; use std::sync::mpsc::{channel, Sender}; diff --git a/tests/ui/drop/terminate-in-initializer.rs b/tests/ui/drop/terminate-in-initializer.rs index 24ec39fe09638..5dd8fe43c71e3 100644 --- a/tests/ui/drop/terminate-in-initializer.rs +++ b/tests/ui/drop/terminate-in-initializer.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc // Issue #787 // Don't try to clean up uninitialized locals diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller.rs b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs index b85b335844b02..1246a3801fc36 100644 --- a/tests/ui/explicit-tail-calls/callee_is_track_caller.rs +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs @@ -1,5 +1,6 @@ //@ run-pass //@ ignore-pass +//@ ignore-backends: gcc #![expect(incomplete_features)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr b/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr index e1a251d156f76..020a0542a57fd 100644 --- a/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr @@ -1,5 +1,5 @@ warning: tail calling a function marked with `#[track_caller]` has no special effect - --> $DIR/callee_is_track_caller.rs:7:12 + --> $DIR/callee_is_track_caller.rs:8:12 | LL | become b(x); | ^^^^ diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs index 33384de83eb74..51688897b4001 100644 --- a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs @@ -1,5 +1,6 @@ //@ run-pass //@ ignore-pass +//@ ignore-backends: gcc #![expect(incomplete_features)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr index 5a1c40509ad3a..7b4c144acffa5 100644 --- a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr @@ -1,5 +1,5 @@ warning: tail calling a function marked with `#[track_caller]` has no special effect - --> $DIR/callee_is_track_caller_polymorphic.rs:7:12 + --> $DIR/callee_is_track_caller_polymorphic.rs:8:12 | LL | become T::f(); | ^^^^^^ diff --git a/tests/ui/explicit-tail-calls/drop-order.rs b/tests/ui/explicit-tail-calls/drop-order.rs index 58e1afbdf0c51..ff6e2f09f57ce 100644 --- a/tests/ui/explicit-tail-calls/drop-order.rs +++ b/tests/ui/explicit-tail-calls/drop-order.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![expect(incomplete_features)] #![feature(explicit_tail_calls)] use std::cell::RefCell; diff --git a/tests/ui/explicit-tail-calls/indexer.rs b/tests/ui/explicit-tail-calls/indexer.rs index 5644506b2f584..c26d9774ce780 100644 --- a/tests/ui/explicit-tail-calls/indexer.rs +++ b/tests/ui/explicit-tail-calls/indexer.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc // Indexing taken from // https://github.com/phi-go/rfcs/blob/guaranteed-tco/text%2F0000-explicit-tail-calls.md#tail-call-elimination // no other test has utilized the "function table" diff --git a/tests/ui/explicit-tail-calls/recursion-etc.rs b/tests/ui/explicit-tail-calls/recursion-etc.rs index 8c89ceb7869aa..c22401d23799d 100644 --- a/tests/ui/explicit-tail-calls/recursion-etc.rs +++ b/tests/ui/explicit-tail-calls/recursion-etc.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![expect(incomplete_features)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/extern/extern-types-field-offset.rs b/tests/ui/extern/extern-types-field-offset.rs index 035f063cd502c..7a5f36da20917 100644 --- a/tests/ui/extern/extern-types-field-offset.rs +++ b/tests/ui/extern/extern-types-field-offset.rs @@ -2,6 +2,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stderr: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ ignore-backends: gcc #![feature(extern_types)] extern "C" { diff --git a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index 1cd52b70315c1..4d0afa1cdfa64 100644 --- a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -21,6 +21,7 @@ //@[no]compile-flags: -C lto=no //@[thin]compile-flags: -C lto=thin //@[fat]compile-flags: -C lto=fat +//@ ignore-backends: gcc #![feature(panic_internals)] diff --git a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs index a44eb3828d0a9..1fc3409c1f900 100644 --- a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs +++ b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs @@ -48,6 +48,7 @@ //@[fat1]compile-flags: -C opt-level=1 -C lto=fat //@[fat2]compile-flags: -C opt-level=2 -C lto=fat //@[fat3]compile-flags: -C opt-level=3 -C lto=fat +//@ ignore-backends: gcc fn main() { use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs index 26d483e43ae50..794b51c7a8a25 100644 --- a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs +++ b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs @@ -1,4 +1,5 @@ //@ proc-macro: format-string-proc-macro.rs +//@ ignore-backends: gcc extern crate format_string_proc_macro; diff --git a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr index e7ed2a76e6af1..c341c6ee4c513 100644 --- a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr +++ b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr @@ -1,5 +1,5 @@ error: there is no argument named `x` - --> $DIR/format-args-capture-from-pm-first-arg-macro.rs:6:5 + --> $DIR/format-args-capture-from-pm-first-arg-macro.rs:7:5 | LL | format_string_proc_macro::bad_format_args_captures!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/frontmatter/proc-macro-observer.rs b/tests/ui/frontmatter/proc-macro-observer.rs index 6c4c8c572897e..5237a0e982a6f 100644 --- a/tests/ui/frontmatter/proc-macro-observer.rs +++ b/tests/ui/frontmatter/proc-macro-observer.rs @@ -1,6 +1,7 @@ //@ check-pass //@ proc-macro: makro.rs //@ edition: 2021 +//@ ignore-backends: gcc // Check that a proc-macro doesn't try to parse frontmatter and instead treats // it as a regular Rust token sequence. See `auxiliary/makro.rs` for details. diff --git a/tests/ui/hygiene/issue-77523-def-site-async-await.rs b/tests/ui/hygiene/issue-77523-def-site-async-await.rs index ad6bd5e0b78bc..0a279682b71bb 100644 --- a/tests/ui/hygiene/issue-77523-def-site-async-await.rs +++ b/tests/ui/hygiene/issue-77523-def-site-async-await.rs @@ -1,5 +1,6 @@ //@ build-pass //@ aux-build:def-site-async-await.rs +//@ ignore-backends: gcc // Regression test for issue #77523 // Tests that we don't ICE when an unusual combination diff --git a/tests/ui/impl-trait/precise-capturing/external-macro.rs b/tests/ui/impl-trait/precise-capturing/external-macro.rs index 9d4d8a1bb119a..1342ecd58dcf2 100644 --- a/tests/ui/impl-trait/precise-capturing/external-macro.rs +++ b/tests/ui/impl-trait/precise-capturing/external-macro.rs @@ -6,6 +6,7 @@ //@ aux-crate: no_use_macro=no-use-macro.rs //@ edition: 2024 //@ check-pass +//@ ignore-backends: gcc no_use_pm::pm_rpit!{} diff --git a/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs index 832821c9c883f..2ed0014f8b0ef 100644 --- a/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs +++ b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs @@ -1,5 +1,6 @@ //@ build-fail //@ compile-flags: -Cpasses=unknown-pass +//@ ignore-backends: gcc fn main() {} diff --git a/tests/ui/issues/issue-25089.rs b/tests/ui/issues/issue-25089.rs index 929738c3e794d..63fdf64cea946 100644 --- a/tests/ui/issues/issue-25089.rs +++ b/tests/ui/issues/issue-25089.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc use std::thread; diff --git a/tests/ui/issues/issue-26655.rs b/tests/ui/issues/issue-26655.rs index 416472b0b269e..32c4b33a8c967 100644 --- a/tests/ui/issues/issue-26655.rs +++ b/tests/ui/issues/issue-26655.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc // Check that the destructors of simple enums are run on unwinding diff --git a/tests/ui/issues/issue-29485.rs b/tests/ui/issues/issue-29485.rs index a44bcd49c6a9c..8e6436cb11e34 100644 --- a/tests/ui/issues/issue-29485.rs +++ b/tests/ui/issues/issue-29485.rs @@ -3,6 +3,7 @@ //@ aux-build:issue-29485.rs //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc #[feature(recover)] diff --git a/tests/ui/issues/issue-30018-panic.rs b/tests/ui/issues/issue-30018-panic.rs index 591848b6f7b4e..09b832bb59d02 100644 --- a/tests/ui/issues/issue-30018-panic.rs +++ b/tests/ui/issues/issue-30018-panic.rs @@ -6,6 +6,7 @@ //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc struct Foo; diff --git a/tests/ui/issues/issue-44056.rs b/tests/ui/issues/issue-44056.rs index 12e4f018466f0..37d7b00cf7f0e 100644 --- a/tests/ui/issues/issue-44056.rs +++ b/tests/ui/issues/issue-44056.rs @@ -2,5 +2,6 @@ //@ only-x86_64 //@ no-prefer-dynamic //@ compile-flags: -Ctarget-feature=+avx -Clto +//@ ignore-backends: gcc fn main() {} diff --git a/tests/ui/issues/issue-68696-catch-during-unwind.rs b/tests/ui/issues/issue-68696-catch-during-unwind.rs index 80d63b0cde705..655879e186929 100644 --- a/tests/ui/issues/issue-68696-catch-during-unwind.rs +++ b/tests/ui/issues/issue-68696-catch-during-unwind.rs @@ -4,6 +4,7 @@ // entering the catch_unwind. // //@ run-pass +//@ ignore-backends: gcc use std::panic::catch_unwind; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs index e5de08a7a28bb..512616251c2f7 100644 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs +++ b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs @@ -3,6 +3,7 @@ //@ known-bug: #109681 //@ ignore-wasm32 this appears to SIGABRT on wasm, not fail cleanly //@ compile-flags: -Z verify-llvm-ir +//@ ignore-backends: gcc // This test verifies that we continue to hit the LLVM error for common linkage with non-zero // initializers, since it generates invalid LLVM IR. diff --git a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs index 57492ed2d0e1f..62d352facd178 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs @@ -3,6 +3,7 @@ //@ run-pass //@ compile-flags: -Cpanic=abort //@ edition: 2024 +//@ ignore-backends: gcc #![allow(incomplete_features)] #![feature(raw_dylib_elf)] diff --git a/tests/ui/linking/no-gc-encapsulation-symbols.rs b/tests/ui/linking/no-gc-encapsulation-symbols.rs index 36d69969199ce..c60f35b55eb2f 100644 --- a/tests/ui/linking/no-gc-encapsulation-symbols.rs +++ b/tests/ui/linking/no-gc-encapsulation-symbols.rs @@ -5,6 +5,7 @@ // //@ build-pass //@ only-x86_64-unknown-linux-gnu +//@ ignore-backends: gcc unsafe extern "Rust" { // The __start_ section name is magical for the linker, diff --git a/tests/ui/lint/unused-qualification-in-derive-expansion.rs b/tests/ui/lint/unused-qualification-in-derive-expansion.rs index b2067e22c4443..bf095c6449d8f 100644 --- a/tests/ui/lint/unused-qualification-in-derive-expansion.rs +++ b/tests/ui/lint/unused-qualification-in-derive-expansion.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: add-impl.rs +//@ ignore-backends: gcc #![forbid(unused_qualifications)] diff --git a/tests/ui/lto/debuginfo-lto-alloc.rs b/tests/ui/lto/debuginfo-lto-alloc.rs index d6855f8760d52..7c82d978a0753 100644 --- a/tests/ui/lto/debuginfo-lto-alloc.rs +++ b/tests/ui/lto/debuginfo-lto-alloc.rs @@ -12,6 +12,7 @@ //@ compile-flags: --test -C debuginfo=2 -C lto=fat //@ no-prefer-dynamic //@ incremental +//@ ignore-backends: gcc extern crate alloc; diff --git a/tests/ui/lto/debuginfo-lto.rs b/tests/ui/lto/debuginfo-lto.rs index f189a1df05673..6d8b836235cc5 100644 --- a/tests/ui/lto/debuginfo-lto.rs +++ b/tests/ui/lto/debuginfo-lto.rs @@ -7,6 +7,7 @@ //@ aux-build:debuginfo-lto-aux.rs //@ compile-flags: -C lto -g //@ no-prefer-dynamic +//@ ignore-backends: gcc extern crate debuginfo_lto_aux; diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.rs b/tests/ui/lto/dwarf-mixed-versions-lto.rs index 900274eb22f44..8ed3afa5e33ae 100644 --- a/tests/ui/lto/dwarf-mixed-versions-lto.rs +++ b/tests/ui/lto/dwarf-mixed-versions-lto.rs @@ -7,6 +7,7 @@ //@ compile-flags: -C lto -g -Cdwarf-version=5 //@ no-prefer-dynamic //@ build-pass +//@ ignore-backends: gcc extern crate dwarf_mixed_versions_lto_aux; diff --git a/tests/ui/lto/fat-lto.rs b/tests/ui/lto/fat-lto.rs index 73d6801a25acb..fe00d7feb3773 100644 --- a/tests/ui/lto/fat-lto.rs +++ b/tests/ui/lto/fat-lto.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Clto=fat //@ no-prefer-dynamic +//@ ignore-backends: gcc fn main() { println!("hello!"); diff --git a/tests/ui/lto/issue-100772.rs b/tests/ui/lto/issue-100772.rs index 9468e20894ace..e07d44e3be880 100644 --- a/tests/ui/lto/issue-100772.rs +++ b/tests/ui/lto/issue-100772.rs @@ -3,6 +3,7 @@ //@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu +//@ ignore-backends: gcc #![feature(allocator_api)] diff --git a/tests/ui/lto/lto-duplicate-symbols.rs b/tests/ui/lto/lto-duplicate-symbols.rs index a62ab2e22172b..08465eb0fb27a 100644 --- a/tests/ui/lto/lto-duplicate-symbols.rs +++ b/tests/ui/lto/lto-duplicate-symbols.rs @@ -4,6 +4,7 @@ //@ compile-flags: -C lto //@ no-prefer-dynamic //@ normalize-stderr: "lto-duplicate-symbols2\.lto_duplicate_symbols2\.[0-9a-zA-Z]+-cgu" -> "lto-duplicate-symbols2.lto_duplicate_symbols2.HASH-cgu" +//@ ignore-backends: gcc extern crate lto_duplicate_symbols1; extern crate lto_duplicate_symbols2; diff --git a/tests/ui/lto/lto-many-codegen-units.rs b/tests/ui/lto/lto-many-codegen-units.rs index fb6636fb81514..6761510e4274d 100644 --- a/tests/ui/lto/lto-many-codegen-units.rs +++ b/tests/ui/lto/lto-many-codegen-units.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -C lto -C codegen-units=8 //@ no-prefer-dynamic +//@ ignore-backends: gcc fn main() { } diff --git a/tests/ui/lto/lto-rustc-loads-linker-plugin.rs b/tests/ui/lto/lto-rustc-loads-linker-plugin.rs index 18e937cb29a62..2be320f0bffca 100644 --- a/tests/ui/lto/lto-rustc-loads-linker-plugin.rs +++ b/tests/ui/lto/lto-rustc-loads-linker-plugin.rs @@ -2,6 +2,7 @@ //@ aux-build:lto-rustc-loads-linker-plugin.rs //@ run-pass //@ no-prefer-dynamic +//@ ignore-backends: gcc // This test ensures that if a dependency was compiled with // `-Clinker-plugin-lto` then we can compile with `-Clto` and still link against diff --git a/tests/ui/lto/lto-still-runs-thread-dtors.rs b/tests/ui/lto/lto-still-runs-thread-dtors.rs index 900368496ebde..9a97677773cca 100644 --- a/tests/ui/lto/lto-still-runs-thread-dtors.rs +++ b/tests/ui/lto/lto-still-runs-thread-dtors.rs @@ -2,6 +2,7 @@ //@ compile-flags: -C lto //@ no-prefer-dynamic //@ needs-threads +//@ ignore-backends: gcc // FIXME(static_mut_refs): this could use an atomic #![allow(static_mut_refs)] diff --git a/tests/ui/macros/same-sequence-span.rs b/tests/ui/macros/same-sequence-span.rs index dfaf669a769a6..9fae847a4e2fa 100644 --- a/tests/ui/macros/same-sequence-span.rs +++ b/tests/ui/macros/same-sequence-span.rs @@ -1,4 +1,5 @@ //@ proc-macro: proc_macro_sequence.rs +//@ ignore-backends: gcc // Regression test for issue #62831: Check that multiple sequences with the same span in the // left-hand side of a macro definition behave as if they had unique spans, and in particular that diff --git a/tests/ui/macros/same-sequence-span.stderr b/tests/ui/macros/same-sequence-span.stderr index 34df201f5a5c7..1ca89b6b595c8 100644 --- a/tests/ui/macros/same-sequence-span.stderr +++ b/tests/ui/macros/same-sequence-span.stderr @@ -1,5 +1,5 @@ error: `$x:expr` may be followed by `$y:tt`, which is not allowed for `expr` fragments - --> $DIR/same-sequence-span.rs:14:18 + --> $DIR/same-sequence-span.rs:15:18 | LL | (1 $x:expr $($y:tt,)* | ^^^^^ not allowed after `expr` fragments @@ -7,7 +7,7 @@ LL | (1 $x:expr $($y:tt,)* = note: allowed there are: `=>`, `,` or `;` error: `$x:expr` may be followed by `=`, which is not allowed for `expr` fragments - --> $DIR/same-sequence-span.rs:15:18 + --> $DIR/same-sequence-span.rs:16:18 | LL | $(= $z:tt)* | ^ not allowed after `expr` fragments @@ -15,10 +15,10 @@ LL | $(= $z:tt)* = note: allowed there are: `=>`, `,` or `;` error: `$x:expr` may be followed by `$y:tt`, which is not allowed for `expr` fragments - --> $DIR/same-sequence-span.rs:19:1 + --> $DIR/same-sequence-span.rs:20:1 | -LL | | macro_rules! manual_foo { - | |__________________________^not allowed after `expr` fragments +LL | | // `proc_macro_sequence.rs`. + | |_____________________________^not allowed after `expr` fragments ... LL | proc_macro_sequence::make_foo!(); | ^------------------------------- @@ -30,7 +30,7 @@ LL | proc_macro_sequence::make_foo!(); = note: this error originates in the macro `proc_macro_sequence::make_foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `$x:expr` may be followed by `=`, which is not allowed for `expr` fragments - --> $DIR/same-sequence-span.rs:19:1 + --> $DIR/same-sequence-span.rs:20:1 | LL | proc_macro_sequence::make_foo!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not allowed after `expr` fragments diff --git a/tests/ui/numbers-arithmetic/int-abs-overflow.rs b/tests/ui/numbers-arithmetic/int-abs-overflow.rs index 6397f62d06551..fd4a5a6052b83 100644 --- a/tests/ui/numbers-arithmetic/int-abs-overflow.rs +++ b/tests/ui/numbers-arithmetic/int-abs-overflow.rs @@ -2,6 +2,7 @@ //@ compile-flags: -C overflow-checks=on //@ needs-threads //@ needs-unwind +//@ ignore-backends: gcc use std::thread; diff --git a/tests/ui/numbers-arithmetic/issue-8460.rs b/tests/ui/numbers-arithmetic/issue-8460.rs index 87867fdc93e41..52df432669f12 100644 --- a/tests/ui/numbers-arithmetic/issue-8460.rs +++ b/tests/ui/numbers-arithmetic/issue-8460.rs @@ -2,6 +2,7 @@ #![allow(unused_must_use)] //@ needs-threads //@ needs-unwind +//@ ignore-backends: gcc #![feature(rustc_attrs)] use std::thread; diff --git a/tests/ui/panic-runtime/lto-unwind.rs b/tests/ui/panic-runtime/lto-unwind.rs index 93275052f8527..bafc6d5aaa5c5 100644 --- a/tests/ui/panic-runtime/lto-unwind.rs +++ b/tests/ui/panic-runtime/lto-unwind.rs @@ -3,6 +3,7 @@ //@ needs-unwind //@ no-prefer-dynamic //@ needs-subprocess +//@ ignore-backends: gcc use std::process::Command; use std::env; diff --git a/tests/ui/panics/oom-panic-unwind.rs b/tests/ui/panics/oom-panic-unwind.rs index 5974ad91406f3..4f7939ce60b20 100644 --- a/tests/ui/panics/oom-panic-unwind.rs +++ b/tests/ui/panics/oom-panic-unwind.rs @@ -5,6 +5,7 @@ //@ no-prefer-dynamic //@ needs-unwind //@ only-linux +//@ ignore-backends: gcc use std::hint::black_box; use std::mem::forget; diff --git a/tests/ui/panics/panic-handler-chain-update-hook.rs b/tests/ui/panics/panic-handler-chain-update-hook.rs index 662ea9e978f70..2ae79ad236ef2 100644 --- a/tests/ui/panics/panic-handler-chain-update-hook.rs +++ b/tests/ui/panics/panic-handler-chain-update-hook.rs @@ -3,6 +3,7 @@ #![allow(stable_features)] //@ needs-threads +//@ ignore-backends: gcc #![feature(std_panic)] #![feature(panic_update_hook)] diff --git a/tests/ui/panics/panic-handler-chain.rs b/tests/ui/panics/panic-handler-chain.rs index fea71ad9ec4e3..cc591c1d9992d 100644 --- a/tests/ui/panics/panic-handler-chain.rs +++ b/tests/ui/panics/panic-handler-chain.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc #![allow(stable_features)] #![feature(std_panic)] diff --git a/tests/ui/panics/panic-handler-flail-wildly.rs b/tests/ui/panics/panic-handler-flail-wildly.rs index d42dfd68d9cf8..d5f5195d38121 100644 --- a/tests/ui/panics/panic-handler-flail-wildly.rs +++ b/tests/ui/panics/panic-handler-flail-wildly.rs @@ -5,6 +5,7 @@ #![allow(unused_must_use)] //@ needs-threads +//@ ignore-backends: gcc #![feature(std_panic)] diff --git a/tests/ui/panics/panic-handler-set-twice.rs b/tests/ui/panics/panic-handler-set-twice.rs index 5f670d5f49298..ca4ed65f5683d 100644 --- a/tests/ui/panics/panic-handler-set-twice.rs +++ b/tests/ui/panics/panic-handler-set-twice.rs @@ -6,6 +6,7 @@ #![feature(std_panic)] //@ needs-threads +//@ ignore-backends: gcc use std::sync::atomic::{AtomicUsize, Ordering}; use std::panic; diff --git a/tests/ui/panics/panic-in-dtor-drops-fields.rs b/tests/ui/panics/panic-in-dtor-drops-fields.rs index 38eb6d0acfb81..db07923433752 100644 --- a/tests/ui/panics/panic-in-dtor-drops-fields.rs +++ b/tests/ui/panics/panic-in-dtor-drops-fields.rs @@ -4,6 +4,7 @@ #![allow(non_upper_case_globals)] //@ needs-threads +//@ ignore-backends: gcc use std::thread; diff --git a/tests/ui/panics/panic-recover-propagate.rs b/tests/ui/panics/panic-recover-propagate.rs index ef6ae4fd7887d..36ca279bdbd25 100644 --- a/tests/ui/panics/panic-recover-propagate.rs +++ b/tests/ui/panics/panic-recover-propagate.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc use std::sync::atomic::{AtomicUsize, Ordering}; use std::panic; diff --git a/tests/ui/panics/rvalue-cleanup-during-box-panic.rs b/tests/ui/panics/rvalue-cleanup-during-box-panic.rs index 84c5d85d7e096..03571f111aac0 100644 --- a/tests/ui/panics/rvalue-cleanup-during-box-panic.rs +++ b/tests/ui/panics/rvalue-cleanup-during-box-panic.rs @@ -21,6 +21,7 @@ // scenario worth testing. //@ needs-threads +//@ ignore-backends: gcc use std::thread; diff --git a/tests/ui/panics/unwind-force-no-unwind-tables.rs b/tests/ui/panics/unwind-force-no-unwind-tables.rs index 2226e4dd03ebc..715f288fff10e 100644 --- a/tests/ui/panics/unwind-force-no-unwind-tables.rs +++ b/tests/ui/panics/unwind-force-no-unwind-tables.rs @@ -6,6 +6,7 @@ //@ needs-unwind //@ ignore-windows target requires uwtable //@ compile-flags: -C panic=unwind -C force-unwind-tables=n +//@ ignore-backends: gcc use std::panic::{self, AssertUnwindSafe}; diff --git a/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.rs b/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.rs index 461890e63e37b..c82efe79e4d0f 100644 --- a/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.rs +++ b/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-89971-outer-attr-following-inner-attr-ice.rs +//@ ignore-backends: gcc #[macro_use] extern crate issue_89971_outer_attr_following_inner_attr_ice; diff --git a/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.stderr b/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.stderr index 51df17c7cc670..392e7d0321f03 100644 --- a/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.stderr +++ b/tests/ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.stderr @@ -1,5 +1,5 @@ error: an inner attribute is not permitted in this context - --> $DIR/issue-89971-outer-attr-following-inner-attr-ice.rs:11:1 + --> $DIR/issue-89971-outer-attr-following-inner-attr-ice.rs:12:1 | LL | #![deny(missing_docs)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.rs b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs index 775c50779760a..701e7dfa30a39 100644 --- a/tests/ui/parser/macro/unicode-control-codepoints-macros.rs +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs @@ -1,6 +1,7 @@ // Regression test for #140281 //@ edition: 2021 //@ proc-macro: unicode-control.rs +//@ ignore-backends: gcc extern crate unicode_control; use unicode_control::*; diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr index ca813399eac27..22fb1b945c654 100644 --- a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr @@ -1,5 +1,5 @@ error: unicode codepoint changing visible direction of text present in doc comment - --> $DIR/unicode-control-codepoints-macros.rs:20:9 + --> $DIR/unicode-control-codepoints-macros.rs:21:9 | LL | /// �test� RTL in doc in vec | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints @@ -10,7 +10,7 @@ LL | /// �test� RTL in doc in vec = note: `#[deny(text_direction_codepoint_in_literal)]` on by default error: unicode codepoint changing visible direction of text present in doc comment - --> $DIR/unicode-control-codepoints-macros.rs:25:9 + --> $DIR/unicode-control-codepoints-macros.rs:26:9 | LL | / /** LL | | * �test� RTL in doc in macro @@ -22,7 +22,7 @@ LL | | */ = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment - --> $DIR/unicode-control-codepoints-macros.rs:32:9 + --> $DIR/unicode-control-codepoints-macros.rs:33:9 | LL | / /** LL | | * �test� RTL in doc in macro @@ -34,7 +34,7 @@ LL | | */ = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment - --> $DIR/unicode-control-codepoints-macros.rs:40:9 + --> $DIR/unicode-control-codepoints-macros.rs:41:9 | LL | /// �test� RTL in doc in proc macro | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints @@ -44,7 +44,7 @@ LL | /// �test� RTL in doc in proc macro = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment - --> $DIR/unicode-control-codepoints-macros.rs:45:9 + --> $DIR/unicode-control-codepoints-macros.rs:46:9 | LL | /// �test� RTL in doc in proc macro | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.rs b/tests/ui/parser/tuple-index-suffix-proc-macro.rs index 557c67738d30f..2463897381ec2 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.rs +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.rs @@ -3,6 +3,7 @@ //! Like `tuple-index-suffix.rs`, but exercises the proc-macro interaction. //@ proc-macro: tuple-index-suffix-proc-macro-aux.rs +//@ ignore-backends: gcc extern crate tuple_index_suffix_proc_macro_aux; use tuple_index_suffix_proc_macro_aux as aux; diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr index 47d179d355513..a242af5a789f7 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr @@ -1,23 +1,23 @@ error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:17:28 + --> $DIR/tuple-index-suffix-proc-macro.rs:18:28 | LL | aux::bad_tup_indexing!(0usize); | ^^^^^^ invalid suffix `usize` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:19:47 + --> $DIR/tuple-index-suffix-proc-macro.rs:20:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0isize); | ^^^^^^ invalid suffix `isize` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:24:28 + --> $DIR/tuple-index-suffix-proc-macro.rs:25:28 | LL | aux::bad_tup_indexing!(0u8); | ^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:26:47 + --> $DIR/tuple-index-suffix-proc-macro.rs:27:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0u64); | ^^^^ invalid suffix `u64` diff --git a/tests/ui/proc-macro/add-impl.rs b/tests/ui/proc-macro/add-impl.rs index 2299f05c2e7fa..645e9321bba04 100644 --- a/tests/ui/proc-macro/add-impl.rs +++ b/tests/ui/proc-macro/add-impl.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: add-impl.rs +//@ ignore-backends: gcc #[macro_use] extern crate add_impl; diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs-test.rs b/tests/ui/proc-macro/ambiguous-builtin-attrs-test.rs index 8ee2223822a3c..e580e0784b34e 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs-test.rs +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs-test.rs @@ -1,5 +1,6 @@ //@ proc-macro: builtin-attrs.rs //@ compile-flags:--test +//@ ignore-backends: gcc #![feature(decl_macro, test)] diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs-test.stderr b/tests/ui/proc-macro/ambiguous-builtin-attrs-test.stderr index 346cebf639d17..e5de873cf31f8 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs-test.stderr +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs-test.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `NonExistent` in this scope - --> $DIR/ambiguous-builtin-attrs-test.rs:19:5 + --> $DIR/ambiguous-builtin-attrs-test.rs:20:5 | LL | NonExistent; | ^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs.rs b/tests/ui/proc-macro/ambiguous-builtin-attrs.rs index edc7748eff3da..63d3c79055ca2 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs.rs +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs.rs @@ -1,5 +1,6 @@ //@ edition:2018 //@ proc-macro: builtin-attrs.rs +//@ ignore-backends: gcc #![feature(decl_macro)] //~ ERROR `feature` is ambiguous extern crate builtin_attrs; diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr b/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr index 0f4ddc065a742..ff7894a41eab0 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr @@ -1,11 +1,11 @@ error[E0425]: cannot find value `NonExistent` in this scope - --> $DIR/ambiguous-builtin-attrs.rs:34:5 + --> $DIR/ambiguous-builtin-attrs.rs:35:5 | LL | NonExistent; | ^^^^^^^^^^^ not found in this scope error[E0659]: `repr` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:9:3 + --> $DIR/ambiguous-builtin-attrs.rs:10:3 | LL | #[repr(C)] | ^^^^ ambiguous name @@ -13,14 +13,14 @@ LL | #[repr(C)] = note: ambiguous because of a name conflict with a builtin attribute = note: `repr` could refer to a built-in attribute note: `repr` could also refer to the attribute macro imported here - --> $DIR/ambiguous-builtin-attrs.rs:6:5 + --> $DIR/ambiguous-builtin-attrs.rs:7:5 | LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `repr` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:11:19 + --> $DIR/ambiguous-builtin-attrs.rs:12:19 | LL | #[cfg_attr(all(), repr(C))] | ^^^^ ambiguous name @@ -28,14 +28,14 @@ LL | #[cfg_attr(all(), repr(C))] = note: ambiguous because of a name conflict with a builtin attribute = note: `repr` could refer to a built-in attribute note: `repr` could also refer to the attribute macro imported here - --> $DIR/ambiguous-builtin-attrs.rs:6:5 + --> $DIR/ambiguous-builtin-attrs.rs:7:5 | LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `repr` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:20:34 + --> $DIR/ambiguous-builtin-attrs.rs:21:34 | LL | fn non_macro_expanded_location<#[repr(C)] T>() { | ^^^^ ambiguous name @@ -43,14 +43,14 @@ LL | fn non_macro_expanded_location<#[repr(C)] T>() { = note: ambiguous because of a name conflict with a builtin attribute = note: `repr` could refer to a built-in attribute note: `repr` could also refer to the attribute macro imported here - --> $DIR/ambiguous-builtin-attrs.rs:6:5 + --> $DIR/ambiguous-builtin-attrs.rs:7:5 | LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `repr` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:24:11 + --> $DIR/ambiguous-builtin-attrs.rs:25:11 | LL | #[repr(C)] | ^^^^ ambiguous name @@ -58,14 +58,14 @@ LL | #[repr(C)] = note: ambiguous because of a name conflict with a builtin attribute = note: `repr` could refer to a built-in attribute note: `repr` could also refer to the attribute macro imported here - --> $DIR/ambiguous-builtin-attrs.rs:6:5 + --> $DIR/ambiguous-builtin-attrs.rs:7:5 | LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `allow` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:38:3 + --> $DIR/ambiguous-builtin-attrs.rs:39:3 | LL | #[allow(unused)] | ^^^^^ ambiguous name @@ -73,14 +73,14 @@ LL | #[allow(unused)] = note: ambiguous because of a name conflict with a builtin attribute = note: `allow` could refer to a built-in attribute note: `allow` could also refer to the built-in attribute imported here - --> $DIR/ambiguous-builtin-attrs.rs:37:5 + --> $DIR/ambiguous-builtin-attrs.rs:38:5 | LL | use deny as allow; | ^^^^^^^^^^^^^ = help: use `crate::allow` to refer to this built-in attribute unambiguously error[E0659]: `feature` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:3:4 + --> $DIR/ambiguous-builtin-attrs.rs:4:4 | LL | #![feature(decl_macro)] | ^^^^^^^ ambiguous name @@ -88,20 +88,20 @@ LL | #![feature(decl_macro)] = note: ambiguous because of a name conflict with a builtin attribute = note: `feature` could refer to a built-in attribute note: `feature` could also refer to the attribute macro imported here - --> $DIR/ambiguous-builtin-attrs.rs:6:5 + --> $DIR/ambiguous-builtin-attrs.rs:7:5 | LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::feature` to refer to this attribute macro unambiguously error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/ambiguous-builtin-attrs.rs:20:39 + --> $DIR/ambiguous-builtin-attrs.rs:21:39 | LL | fn non_macro_expanded_location<#[repr(C)] T>() { | ^ - not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/ambiguous-builtin-attrs.rs:24:16 + --> $DIR/ambiguous-builtin-attrs.rs:25:16 | LL | #[repr(C)] | ^ diff --git a/tests/ui/proc-macro/append-impl.rs b/tests/ui/proc-macro/append-impl.rs index c0f208460b297..48d21968de0ce 100644 --- a/tests/ui/proc-macro/append-impl.rs +++ b/tests/ui/proc-macro/append-impl.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: append-impl.rs +//@ ignore-backends: gcc #![allow(warnings)] diff --git a/tests/ui/proc-macro/attr-args.rs b/tests/ui/proc-macro/attr-args.rs index 1d3e0f725d250..4109b450a8ab3 100644 --- a/tests/ui/proc-macro/attr-args.rs +++ b/tests/ui/proc-macro/attr-args.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: attr-args.rs +//@ ignore-backends: gcc #![allow(warnings)] diff --git a/tests/ui/proc-macro/attr-invalid-exprs.rs b/tests/ui/proc-macro/attr-invalid-exprs.rs index f476858a32ba1..bdfc0587b3b02 100644 --- a/tests/ui/proc-macro/attr-invalid-exprs.rs +++ b/tests/ui/proc-macro/attr-invalid-exprs.rs @@ -1,6 +1,7 @@ //! Attributes producing expressions in invalid locations //@ proc-macro: attr-stmt-expr.rs +//@ ignore-backends: gcc #![feature(proc_macro_hygiene)] #![feature(stmt_expr_attributes)] diff --git a/tests/ui/proc-macro/attr-invalid-exprs.stderr b/tests/ui/proc-macro/attr-invalid-exprs.stderr index 0d500c871453f..43241e1e6fd5c 100644 --- a/tests/ui/proc-macro/attr-invalid-exprs.stderr +++ b/tests/ui/proc-macro/attr-invalid-exprs.stderr @@ -1,11 +1,11 @@ error: expected expression, found end of macro arguments - --> $DIR/attr-invalid-exprs.rs:12:13 + --> $DIR/attr-invalid-exprs.rs:13:13 | LL | let _ = #[no_output] "Hello, world!"; | ^^^^^^^^^^^^ error: macro expansion ignores `,` and any tokens following - --> $DIR/attr-invalid-exprs.rs:15:13 + --> $DIR/attr-invalid-exprs.rs:16:13 | LL | let _ = #[duplicate] "Hello, world!"; | ^^^^^^^^^^^^ caused by the macro expansion here @@ -17,7 +17,7 @@ LL | let _ = #[duplicate]; "Hello, world!"; | + error: macro expansion ignores `,` and any tokens following - --> $DIR/attr-invalid-exprs.rs:24:9 + --> $DIR/attr-invalid-exprs.rs:25:9 | LL | #[duplicate] | ^^^^^^^^^^^^ caused by the macro expansion here diff --git a/tests/ui/proc-macro/attr-on-trait.rs b/tests/ui/proc-macro/attr-on-trait.rs index e95760a837c05..345653864f841 100644 --- a/tests/ui/proc-macro/attr-on-trait.rs +++ b/tests/ui/proc-macro/attr-on-trait.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: attr-on-trait.rs +//@ ignore-backends: gcc extern crate attr_on_trait; diff --git a/tests/ui/proc-macro/bang-macro.rs b/tests/ui/proc-macro/bang-macro.rs index 2287e34c5dda9..75f40de242ec9 100644 --- a/tests/ui/proc-macro/bang-macro.rs +++ b/tests/ui/proc-macro/bang-macro.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: bang-macro.rs +//@ ignore-backends: gcc extern crate bang_macro; use bang_macro::rewrite; diff --git a/tests/ui/proc-macro/call-site.rs b/tests/ui/proc-macro/call-site.rs index 9c285e1ed117a..5de4061b2a948 100644 --- a/tests/ui/proc-macro/call-site.rs +++ b/tests/ui/proc-macro/call-site.rs @@ -1,5 +1,6 @@ //@ check-pass //@ proc-macro: call-site.rs +//@ ignore-backends: gcc extern crate call_site; diff --git a/tests/ui/proc-macro/count_compound_ops.rs b/tests/ui/proc-macro/count_compound_ops.rs index 20b0b87817e2f..fe90e7bfbe49a 100644 --- a/tests/ui/proc-macro/count_compound_ops.rs +++ b/tests/ui/proc-macro/count_compound_ops.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: count_compound_ops.rs +//@ ignore-backends: gcc extern crate count_compound_ops; use count_compound_ops::count_compound_ops; diff --git a/tests/ui/proc-macro/derive-bad.rs b/tests/ui/proc-macro/derive-bad.rs index 9b237c731dbe4..9b9a2bc33c946 100644 --- a/tests/ui/proc-macro/derive-bad.rs +++ b/tests/ui/proc-macro/derive-bad.rs @@ -1,4 +1,5 @@ //@ proc-macro: derive-bad.rs +//@ ignore-backends: gcc #[macro_use] extern crate derive_bad; diff --git a/tests/ui/proc-macro/derive-bad.stderr b/tests/ui/proc-macro/derive-bad.stderr index 43e97f40ba884..8a252e826efff 100644 --- a/tests/ui/proc-macro/derive-bad.stderr +++ b/tests/ui/proc-macro/derive-bad.stderr @@ -1,5 +1,5 @@ error: expected `:`, found `}` - --> $DIR/derive-bad.rs:6:10 + --> $DIR/derive-bad.rs:7:10 | LL | #[derive(A)] | ^ @@ -10,13 +10,13 @@ LL | #[derive(A)] = note: this error originates in the derive macro `A` (in Nightly builds, run with -Z macro-backtrace for more info) error: proc-macro derive produced unparsable tokens - --> $DIR/derive-bad.rs:6:10 + --> $DIR/derive-bad.rs:7:10 | LL | #[derive(A)] | ^ error[E0428]: the name `A` is defined multiple times - --> $DIR/derive-bad.rs:9:1 + --> $DIR/derive-bad.rs:10:1 | LL | #[derive(A)] | - previous definition of the type `A` here diff --git a/tests/ui/proc-macro/derive-helper-shadowing.rs b/tests/ui/proc-macro/derive-helper-shadowing.rs index ee883be33526c..5ddd914d1026c 100644 --- a/tests/ui/proc-macro/derive-helper-shadowing.rs +++ b/tests/ui/proc-macro/derive-helper-shadowing.rs @@ -1,6 +1,7 @@ //@ edition:2018 //@ proc-macro: test-macros.rs //@ proc-macro: derive-helper-shadowing.rs +//@ ignore-backends: gcc #[macro_use] extern crate test_macros; diff --git a/tests/ui/proc-macro/derive-helper-shadowing.stderr b/tests/ui/proc-macro/derive-helper-shadowing.stderr index 2e4ddd19b7e1f..90b42e8d6e228 100644 --- a/tests/ui/proc-macro/derive-helper-shadowing.stderr +++ b/tests/ui/proc-macro/derive-helper-shadowing.stderr @@ -1,17 +1,17 @@ error: cannot use a derive helper attribute through an import - --> $DIR/derive-helper-shadowing.rs:42:15 + --> $DIR/derive-helper-shadowing.rs:43:15 | LL | #[renamed] | ^^^^^^^ | note: the derive helper attribute imported here - --> $DIR/derive-helper-shadowing.rs:41:17 + --> $DIR/derive-helper-shadowing.rs:42:17 | LL | use empty_helper as renamed; | ^^^^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `empty_helper` in this scope - --> $DIR/derive-helper-shadowing.rs:38:22 + --> $DIR/derive-helper-shadowing.rs:39:22 | LL | #[derive(GenHelperUse)] | ^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL + use empty_helper; | error: cannot find attribute `empty_helper` in this scope - --> $DIR/derive-helper-shadowing.rs:14:11 + --> $DIR/derive-helper-shadowing.rs:15:11 | LL | #[empty_helper] | ^^^^^^^^^^^^ @@ -40,26 +40,26 @@ LL + use crate::empty_helper; | error[E0659]: `empty_helper` is ambiguous - --> $DIR/derive-helper-shadowing.rs:19:3 + --> $DIR/derive-helper-shadowing.rs:20:3 | LL | #[empty_helper] | ^^^^^^^^^^^^ ambiguous name | = note: ambiguous because of a name conflict with a derive helper attribute note: `empty_helper` could refer to the derive helper attribute defined here - --> $DIR/derive-helper-shadowing.rs:22:10 + --> $DIR/derive-helper-shadowing.rs:23:10 | LL | #[derive(Empty)] | ^^^^^ note: `empty_helper` could also refer to the attribute macro imported here - --> $DIR/derive-helper-shadowing.rs:10:5 + --> $DIR/derive-helper-shadowing.rs:11:5 | LL | use test_macros::empty_attr as empty_helper; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use `crate::empty_helper` to refer to this attribute macro unambiguously error: derive helper attribute is used before it is introduced - --> $DIR/derive-helper-shadowing.rs:19:3 + --> $DIR/derive-helper-shadowing.rs:20:3 | LL | #[empty_helper] | ^^^^^^^^^^^^ @@ -76,7 +76,7 @@ error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0659`. Future incompatibility report: Future breakage diagnostic: error: derive helper attribute is used before it is introduced - --> $DIR/derive-helper-shadowing.rs:19:3 + --> $DIR/derive-helper-shadowing.rs:20:3 | LL | #[empty_helper] | ^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/derive-same-struct.rs b/tests/ui/proc-macro/derive-same-struct.rs index f7669ba1480b2..04ab08dc76e5b 100644 --- a/tests/ui/proc-macro/derive-same-struct.rs +++ b/tests/ui/proc-macro/derive-same-struct.rs @@ -3,6 +3,7 @@ #![allow(path_statements)] #![allow(dead_code)] //@ proc-macro: derive-same-struct.rs +//@ ignore-backends: gcc #[macro_use] extern crate derive_same_struct; diff --git a/tests/ui/proc-macro/edition-imports-2018.rs b/tests/ui/proc-macro/edition-imports-2018.rs index a3808d9dce823..af9eed74adb9b 100644 --- a/tests/ui/proc-macro/edition-imports-2018.rs +++ b/tests/ui/proc-macro/edition-imports-2018.rs @@ -1,6 +1,7 @@ //@ check-pass //@ edition:2018 //@ proc-macro: edition-imports-2015.rs +//@ ignore-backends: gcc #[macro_use] extern crate edition_imports_2015; diff --git a/tests/ui/proc-macro/env.rs b/tests/ui/proc-macro/env.rs index 94e3b09e5269b..fc248f8835964 100644 --- a/tests/ui/proc-macro/env.rs +++ b/tests/ui/proc-macro/env.rs @@ -2,6 +2,7 @@ //@ run-pass //@ rustc-env: THE_CONST=1 //@ compile-flags: -Zunstable-options --env-set THE_CONST=12 --env-set ANOTHER=4 +//@ ignore-backends: gcc #![crate_name = "foo"] diff --git a/tests/ui/proc-macro/expand-expr.rs b/tests/ui/proc-macro/expand-expr.rs index 8a4ed9768d53a..1e058abe3bc1b 100644 --- a/tests/ui/proc-macro/expand-expr.rs +++ b/tests/ui/proc-macro/expand-expr.rs @@ -1,4 +1,5 @@ //@ proc-macro: expand-expr.rs +//@ ignore-backends: gcc // no-remap-src-base: check_expand_expr_file!() fails when enabled. #![feature(concat_bytes)] @@ -10,7 +11,7 @@ use expand_expr::{ // Check builtin macros can be expanded. -expand_expr_is!(13u32, line!()); +expand_expr_is!(14u32, line!()); expand_expr_is!(24u32, column!()); expand_expr_is!("Hello, World!", concat!("Hello, ", "World", "!")); diff --git a/tests/ui/proc-macro/expand-expr.stderr b/tests/ui/proc-macro/expand-expr.stderr index 8b1df177cfa6d..fd5f672adf5c0 100644 --- a/tests/ui/proc-macro/expand-expr.stderr +++ b/tests/ui/proc-macro/expand-expr.stderr @@ -1,29 +1,29 @@ error: expected one of `.`, `?`, or an operator, found `;` - --> $DIR/expand-expr.rs:108:27 + --> $DIR/expand-expr.rs:109:27 | LL | expand_expr_fail!("string"; hello); | ^ expected one of `.`, `?`, or an operator error: expected expression, found `$` - --> $DIR/expand-expr.rs:111:19 + --> $DIR/expand-expr.rs:112:19 | LL | expand_expr_fail!($); | ^ expected expression error: expected expression, found `$` - --> $DIR/expand-expr.rs:112:29 + --> $DIR/expand-expr.rs:113:29 | LL | expand_expr_fail!(echo_tts!($)); | ^ expected expression error: expected expression, found `$` - --> $DIR/expand-expr.rs:113:28 + --> $DIR/expand-expr.rs:114:28 | LL | expand_expr_fail!(echo_pm!($)); | ^ expected expression error: macro expansion ignores `hello` and any tokens following - --> $DIR/expand-expr.rs:117:47 + --> $DIR/expand-expr.rs:118:47 | LL | expand_expr_is!("string", echo_tts!("string"; hello)); | --------------------^^^^^- caused by the macro expansion here @@ -35,7 +35,7 @@ LL | expand_expr_is!("string", echo_tts!("string"; hello);); | + error: macro expansion ignores `;` and any tokens following - --> $DIR/expand-expr.rs:118:44 + --> $DIR/expand-expr.rs:119:44 | LL | expand_expr_is!("string", echo_pm!("string"; hello)); | -----------------^------- caused by the macro expansion here @@ -47,7 +47,7 @@ LL | expand_expr_is!("string", echo_pm!("string"; hello);); | + error: recursion limit reached while expanding `recursive_expand!` - --> $DIR/expand-expr.rs:126:16 + --> $DIR/expand-expr.rs:127:16 | LL | const _: u32 = recursive_expand!(); | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/expand-to-unstable.rs b/tests/ui/proc-macro/expand-to-unstable.rs index 8968471ebd872..37bfeab1fe71e 100644 --- a/tests/ui/proc-macro/expand-to-unstable.rs +++ b/tests/ui/proc-macro/expand-to-unstable.rs @@ -1,4 +1,5 @@ //@ proc-macro: derive-unstable.rs +//@ ignore-backends: gcc #![allow(warnings)] diff --git a/tests/ui/proc-macro/expand-to-unstable.stderr b/tests/ui/proc-macro/expand-to-unstable.stderr index 563c7ae8f9561..255f80501ea73 100644 --- a/tests/ui/proc-macro/expand-to-unstable.stderr +++ b/tests/ui/proc-macro/expand-to-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `core_intrinsics`: intrinsics are unlikely to ever be stabilized, instead they should be used through stabilized interfaces in the rest of the standard library - --> $DIR/expand-to-unstable.rs:8:10 + --> $DIR/expand-to-unstable.rs:9:10 | LL | #[derive(Unstable)] | ^^^^^^^^ diff --git a/tests/ui/proc-macro/expand-with-a-macro.rs b/tests/ui/proc-macro/expand-with-a-macro.rs index e5baf3601db0a..aa02cefbec684 100644 --- a/tests/ui/proc-macro/expand-with-a-macro.rs +++ b/tests/ui/proc-macro/expand-with-a-macro.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ proc-macro: expand-with-a-macro.rs +//@ ignore-backends: gcc #![deny(warnings)] diff --git a/tests/ui/proc-macro/gen-macro-rules-hygiene.rs b/tests/ui/proc-macro/gen-macro-rules-hygiene.rs index 3deec94fa3411..fb7c830c2edf4 100644 --- a/tests/ui/proc-macro/gen-macro-rules-hygiene.rs +++ b/tests/ui/proc-macro/gen-macro-rules-hygiene.rs @@ -3,6 +3,7 @@ // `$crate` refers to the crate that defines `macro_rules` and not the outer transparent macro. //@ proc-macro: gen-macro-rules-hygiene.rs +//@ ignore-backends: gcc #[macro_use] extern crate gen_macro_rules_hygiene; diff --git a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr index df7c4f72eb0b6..e904b43aaae06 100644 --- a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr +++ b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr @@ -1,5 +1,5 @@ error[E0426]: use of undeclared label `'label_use` - --> $DIR/gen-macro-rules-hygiene.rs:12:1 + --> $DIR/gen-macro-rules-hygiene.rs:13:1 | LL | gen_macro_rules!(); | ^^^^^^^^^^^^^^^^^^ undeclared label `'label_use` @@ -10,7 +10,7 @@ LL | generated!(); = note: this error originates in the macro `generated` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_use` in this scope - --> $DIR/gen-macro-rules-hygiene.rs:12:1 + --> $DIR/gen-macro-rules-hygiene.rs:13:1 | LL | gen_macro_rules!(); | ^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `local_def` @@ -21,7 +21,7 @@ LL | generated!(); = note: this error originates in the macro `generated` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope - --> $DIR/gen-macro-rules-hygiene.rs:21:9 + --> $DIR/gen-macro-rules-hygiene.rs:22:9 | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` diff --git a/tests/ui/proc-macro/gen-macro-rules.rs b/tests/ui/proc-macro/gen-macro-rules.rs index 121d029e2e346..8ee38b2cc2795 100644 --- a/tests/ui/proc-macro/gen-macro-rules.rs +++ b/tests/ui/proc-macro/gen-macro-rules.rs @@ -2,6 +2,7 @@ //@ check-pass //@ proc-macro: gen-macro-rules.rs +//@ ignore-backends: gcc extern crate gen_macro_rules as repro; diff --git a/tests/ui/proc-macro/generate-mod.rs b/tests/ui/proc-macro/generate-mod.rs index 729bfc1db6674..0a1629e75ec17 100644 --- a/tests/ui/proc-macro/generate-mod.rs +++ b/tests/ui/proc-macro/generate-mod.rs @@ -1,6 +1,7 @@ // Modules generated by transparent proc macros still acts as barriers for names (issue #50504). //@ proc-macro: generate-mod.rs +//@ ignore-backends: gcc extern crate generate_mod; diff --git a/tests/ui/proc-macro/generate-mod.stderr b/tests/ui/proc-macro/generate-mod.stderr index 142ff1abeed6b..03cf8c35188ac 100644 --- a/tests/ui/proc-macro/generate-mod.stderr +++ b/tests/ui/proc-macro/generate-mod.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:9:1 + --> $DIR/generate-mod.rs:10:1 | LL | generate_mod::check!(); | ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -9,7 +9,7 @@ LL | generate_mod::check!(); = note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0412]: cannot find type `Outer` in this scope - --> $DIR/generate-mod.rs:9:1 + --> $DIR/generate-mod.rs:10:1 | LL | generate_mod::check!(); | ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -19,7 +19,7 @@ LL | generate_mod::check!(); = note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0412]: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:12:1 + --> $DIR/generate-mod.rs:13:1 | LL | #[generate_mod::check_attr] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -29,7 +29,7 @@ LL | #[generate_mod::check_attr] = note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0412]: cannot find type `OuterAttr` in this scope - --> $DIR/generate-mod.rs:12:1 + --> $DIR/generate-mod.rs:13:1 | LL | #[generate_mod::check_attr] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -39,7 +39,7 @@ LL | #[generate_mod::check_attr] = note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:16:10 + --> $DIR/generate-mod.rs:17:10 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -50,7 +50,7 @@ LL | #[derive(generate_mod::CheckDerive)] = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope - --> $DIR/generate-mod.rs:16:10 + --> $DIR/generate-mod.rs:17:10 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -60,7 +60,7 @@ LL | #[derive(generate_mod::CheckDerive)] = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:23:14 + --> $DIR/generate-mod.rs:24:14 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -70,7 +70,7 @@ LL | #[derive(generate_mod::CheckDerive)] = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope - --> $DIR/generate-mod.rs:23:14 + --> $DIR/generate-mod.rs:24:14 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -84,7 +84,7 @@ error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0412`. Future incompatibility report: Future breakage diagnostic: error: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:16:10 + --> $DIR/generate-mod.rs:17:10 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -96,7 +96,7 @@ LL | #[derive(generate_mod::CheckDerive)] Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope - --> $DIR/generate-mod.rs:16:10 + --> $DIR/generate-mod.rs:17:10 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -108,7 +108,7 @@ LL | #[derive(generate_mod::CheckDerive)] Future breakage diagnostic: error: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:23:14 + --> $DIR/generate-mod.rs:24:14 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -120,7 +120,7 @@ LL | #[derive(generate_mod::CheckDerive)] Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope - --> $DIR/generate-mod.rs:23:14 + --> $DIR/generate-mod.rs:24:14 | LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -132,7 +132,7 @@ LL | #[derive(generate_mod::CheckDerive)] Future breakage diagnostic: warning: cannot find type `FromOutside` in this scope - --> $DIR/generate-mod.rs:30:10 + --> $DIR/generate-mod.rs:31:10 | LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import @@ -143,7 +143,7 @@ LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed Future breakage diagnostic: warning: cannot find type `OuterDeriveLint` in this scope - --> $DIR/generate-mod.rs:30:10 + --> $DIR/generate-mod.rs:31:10 | LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import diff --git a/tests/ui/proc-macro/hygiene_example.rs b/tests/ui/proc-macro/hygiene_example.rs index 84b5e345608aa..f74f22fb3b0e0 100644 --- a/tests/ui/proc-macro/hygiene_example.rs +++ b/tests/ui/proc-macro/hygiene_example.rs @@ -1,5 +1,6 @@ //@ check-pass //@ aux-build:hygiene_example.rs +//@ ignore-backends: gcc extern crate hygiene_example; use hygiene_example::hello; diff --git a/tests/ui/proc-macro/is-available.rs b/tests/ui/proc-macro/is-available.rs index faee560d7a975..9e9cf5d11b661 100644 --- a/tests/ui/proc-macro/is-available.rs +++ b/tests/ui/proc-macro/is-available.rs @@ -3,6 +3,7 @@ extern crate proc_macro; //@ proc-macro: is-available.rs +//@ ignore-backends: gcc extern crate is_available; fn main() { diff --git a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs index abdd6bf136dcf..d420f2641daf5 100644 --- a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs +++ b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-104884.rs +//@ ignore-backends: gcc use std::collections::BinaryHeap; diff --git a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr index f3ed9e5761d67..b7aed4a8485a8 100644 --- a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr +++ b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr @@ -1,11 +1,11 @@ error[E0277]: can't compare `PriorityQueue` with `PriorityQueue` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:10 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:10 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^^^^ no implementation for `PriorityQueue == PriorityQueue` | help: the trait `PartialEq` is not implemented for `PriorityQueue` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:20:1 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:21:1 | LL | struct PriorityQueue(BinaryHeap>); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,13 +13,13 @@ note: required by a bound in `PartialOrd` --> $SRC_DIR/core/src/cmp.rs:LL:COL error[E0277]: the trait bound `PriorityQueue: Eq` is not satisfied - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:22 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^ unsatisfied trait bound | help: the trait `Eq` is not implemented for `PriorityQueue` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:20:1 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:21:1 | LL | struct PriorityQueue(BinaryHeap>); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,13 +28,13 @@ note: required by a bound in `Ord` = note: this error originates in the derive macro `AddImpl` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `T` with `T` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:22 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^ no implementation for `T < T` and `T > T` | note: required for `PriorityQueue` to implement `PartialOrd` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:10 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:10 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro @@ -42,7 +42,7 @@ note: required by a bound in `Ord` --> $SRC_DIR/core/src/cmp.rs:LL:COL error[E0277]: can't compare `BinaryHeap>` with `_` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:20:25 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:21:25 | LL | #[derive(PartialOrd, AddImpl)] | ---------- in this derive macro expansion @@ -53,7 +53,7 @@ LL | struct PriorityQueue(BinaryHeap>); = help: the trait `PartialOrd<_>` is not implemented for `BinaryHeap>` error[E0599]: no method named `cmp` found for struct `BinaryHeap>` in the current scope - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:22 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^ `BinaryHeap>` is not an iterator @@ -61,7 +61,7 @@ LL | #[derive(PartialOrd, AddImpl)] = note: this error originates in the derive macro `AddImpl` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0609]: no field `height` on type `&PriorityQueue` - --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 + --> $DIR/issue-104884-trait-impl-sugg-err.rs:14:22 | LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^ unknown field diff --git a/tests/ui/proc-macro/issue-107113-wrap.rs b/tests/ui/proc-macro/issue-107113-wrap.rs index 2799e79bb1cb4..a46cf893d90e8 100644 --- a/tests/ui/proc-macro/issue-107113-wrap.rs +++ b/tests/ui/proc-macro/issue-107113-wrap.rs @@ -1,5 +1,6 @@ //@ edition:2021 //@ proc-macro: issue-107113.rs +//@ ignore-backends: gcc #[macro_use] extern crate issue_107113; diff --git a/tests/ui/proc-macro/issue-107113-wrap.stderr b/tests/ui/proc-macro/issue-107113-wrap.stderr index b541051147d53..9b5b0333256e6 100644 --- a/tests/ui/proc-macro/issue-107113-wrap.stderr +++ b/tests/ui/proc-macro/issue-107113-wrap.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-107113-wrap.rs:7:1 + --> $DIR/issue-107113-wrap.rs:8:1 | LL | #[issue_107113::main] | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/issue-118809.rs b/tests/ui/proc-macro/issue-118809.rs index a6a3956981a78..3ceede7e885cf 100644 --- a/tests/ui/proc-macro/issue-118809.rs +++ b/tests/ui/proc-macro/issue-118809.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-118809.rs +//@ ignore-backends: gcc #[macro_use] extern crate issue_118809; diff --git a/tests/ui/proc-macro/issue-118809.stderr b/tests/ui/proc-macro/issue-118809.stderr index 30b09fd4006bf..98329fea11940 100644 --- a/tests/ui/proc-macro/issue-118809.stderr +++ b/tests/ui/proc-macro/issue-118809.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-118809.rs:6:10 + --> $DIR/issue-118809.rs:7:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[derive(Deserialize)] | arguments to this enum variant are incorrect | help: the type constructed contains `u32` due to the type of the argument passed - --> $DIR/issue-118809.rs:6:10 + --> $DIR/issue-118809.rs:7:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ this argument influences the type of `Some` diff --git a/tests/ui/proc-macro/issue-38586.rs b/tests/ui/proc-macro/issue-38586.rs index 88dbb8037bebd..c9623fd383b1a 100644 --- a/tests/ui/proc-macro/issue-38586.rs +++ b/tests/ui/proc-macro/issue-38586.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-38586.rs +//@ ignore-backends: gcc #[macro_use] extern crate issue_38586; diff --git a/tests/ui/proc-macro/issue-38586.stderr b/tests/ui/proc-macro/issue-38586.stderr index 0049155645078..e49d4c83e2734 100644 --- a/tests/ui/proc-macro/issue-38586.stderr +++ b/tests/ui/proc-macro/issue-38586.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `foo` in this scope - --> $DIR/issue-38586.rs:6:10 + --> $DIR/issue-38586.rs:7:10 | LL | #[derive(A)] | ^ not found in this scope diff --git a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs index df236cce6d2a8..988641b2b9c48 100644 --- a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs +++ b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs @@ -4,6 +4,7 @@ //@ edition:2018 //@ proc-macro: issue-59191.rs //@ needs-unwind (affects error output) +//@ ignore-backends: gcc #![feature(custom_inner_attributes)] #![issue_59191::no_main] diff --git a/tests/ui/proc-macro/issue-79148.rs b/tests/ui/proc-macro/issue-79148.rs index b2248759b5f8e..7ce6216c842a6 100644 --- a/tests/ui/proc-macro/issue-79148.rs +++ b/tests/ui/proc-macro/issue-79148.rs @@ -1,5 +1,6 @@ //@ proc-macro: re-export.rs //@ edition:2018 +//@ ignore-backends: gcc extern crate re_export; diff --git a/tests/ui/proc-macro/issue-79148.stderr b/tests/ui/proc-macro/issue-79148.stderr index 8adc4c6e0dbe4..80a5b1a0855f7 100644 --- a/tests/ui/proc-macro/issue-79148.stderr +++ b/tests/ui/proc-macro/issue-79148.stderr @@ -1,11 +1,11 @@ error[E0364]: `Variant` is only public within the crate, and cannot be re-exported outside - --> $DIR/issue-79148.rs:8:1 + --> $DIR/issue-79148.rs:9:1 | LL | cause_ice!(); | ^^^^^^^^^^^^ | note: consider marking `Variant` as `pub` in the imported module - --> $DIR/issue-79148.rs:8:1 + --> $DIR/issue-79148.rs:9:1 | LL | cause_ice!(); | ^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/issue-83510.rs b/tests/ui/proc-macro/issue-83510.rs index 67469511fc367..d49e1867f1d67 100644 --- a/tests/ui/proc-macro/issue-83510.rs +++ b/tests/ui/proc-macro/issue-83510.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-83510.rs +//@ ignore-backends: gcc extern crate issue_83510; diff --git a/tests/ui/proc-macro/issue-83510.stderr b/tests/ui/proc-macro/issue-83510.stderr index e59b77af3dc39..a7c3f5a1d5b67 100644 --- a/tests/ui/proc-macro/issue-83510.stderr +++ b/tests/ui/proc-macro/issue-83510.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Foo` in this scope - --> $DIR/issue-83510.rs:5:1 + --> $DIR/issue-83510.rs:6:1 | LL | issue_83510::dance_like_you_want_to_ice!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -7,7 +7,7 @@ LL | issue_83510::dance_like_you_want_to_ice!(); = note: this error originates in the macro `issue_83510::dance_like_you_want_to_ice` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0404]: expected trait, found struct `Box` - --> $DIR/issue-83510.rs:5:1 + --> $DIR/issue-83510.rs:6:1 | LL | issue_83510::dance_like_you_want_to_ice!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a trait @@ -15,7 +15,7 @@ LL | issue_83510::dance_like_you_want_to_ice!(); = note: this error originates in the macro `issue_83510::dance_like_you_want_to_ice` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0405]: cannot find trait `Baz` in this scope - --> $DIR/issue-83510.rs:5:1 + --> $DIR/issue-83510.rs:6:1 | LL | issue_83510::dance_like_you_want_to_ice!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -23,7 +23,7 @@ LL | issue_83510::dance_like_you_want_to_ice!(); = note: this error originates in the macro `issue_83510::dance_like_you_want_to_ice` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: inherent associated types are unstable - --> $DIR/issue-83510.rs:5:1 + --> $DIR/issue-83510.rs:6:1 | LL | issue_83510::dance_like_you_want_to_ice!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/issue-91800.rs b/tests/ui/proc-macro/issue-91800.rs index 8cecfad32b55b..79cbf8632f009 100644 --- a/tests/ui/proc-macro/issue-91800.rs +++ b/tests/ui/proc-macro/issue-91800.rs @@ -1,4 +1,5 @@ //@ proc-macro: issue-91800-macro.rs +//@ ignore-backends: gcc #[macro_use] extern crate issue_91800_macro; diff --git a/tests/ui/proc-macro/issue-91800.stderr b/tests/ui/proc-macro/issue-91800.stderr index 63ebc0a552e33..be5a8ece38404 100644 --- a/tests/ui/proc-macro/issue-91800.stderr +++ b/tests/ui/proc-macro/issue-91800.stderr @@ -1,5 +1,5 @@ error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-91800.rs:6:10 + --> $DIR/issue-91800.rs:7:10 | LL | #[derive(MyTrait)] | ^^^^^^^ @@ -7,13 +7,13 @@ LL | #[derive(MyTrait)] = note: this error originates in the derive macro `MyTrait` (in Nightly builds, run with -Z macro-backtrace for more info) error: proc-macro derive produced unparsable tokens - --> $DIR/issue-91800.rs:6:10 + --> $DIR/issue-91800.rs:7:10 | LL | #[derive(MyTrait)] | ^^^^^^^ error: - --> $DIR/issue-91800.rs:6:10 + --> $DIR/issue-91800.rs:7:10 | LL | #[derive(MyTrait)] | ^^^^^^^ @@ -21,7 +21,7 @@ LL | #[derive(MyTrait)] = note: this error originates in the derive macro `MyTrait` (in Nightly builds, run with -Z macro-backtrace for more info) error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-91800.rs:10:1 + --> $DIR/issue-91800.rs:11:1 | LL | #[attribute_macro] | ^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | #[attribute_macro] = note: this error originates in the attribute macro `attribute_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: - --> $DIR/issue-91800.rs:10:1 + --> $DIR/issue-91800.rs:11:1 | LL | #[attribute_macro] | ^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | #[attribute_macro] = note: this error originates in the attribute macro `attribute_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-91800.rs:15:1 + --> $DIR/issue-91800.rs:16:1 | LL | fn_macro! {} | ^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | fn_macro! {} = note: this error originates in the macro `fn_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: - --> $DIR/issue-91800.rs:15:1 + --> $DIR/issue-91800.rs:16:1 | LL | fn_macro! {} | ^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/lifetimes-rpass.rs b/tests/ui/proc-macro/lifetimes-rpass.rs index c462b27722f7e..9b794e695cd1c 100644 --- a/tests/ui/proc-macro/lifetimes-rpass.rs +++ b/tests/ui/proc-macro/lifetimes-rpass.rs @@ -2,6 +2,7 @@ #![allow(unused_variables)] //@ proc-macro: lifetimes-rpass.rs +//@ ignore-backends: gcc extern crate lifetimes_rpass as lifetimes; use lifetimes::*; diff --git a/tests/ui/proc-macro/lints_in_proc_macros.rs b/tests/ui/proc-macro/lints_in_proc_macros.rs index 6714b8b6e1d5f..2c22c787982eb 100644 --- a/tests/ui/proc-macro/lints_in_proc_macros.rs +++ b/tests/ui/proc-macro/lints_in_proc_macros.rs @@ -1,4 +1,5 @@ //@ proc-macro: bang_proc_macro2.rs +//@ ignore-backends: gcc extern crate bang_proc_macro2; diff --git a/tests/ui/proc-macro/lints_in_proc_macros.stderr b/tests/ui/proc-macro/lints_in_proc_macros.stderr index 244d218608be7..016b236bda881 100644 --- a/tests/ui/proc-macro/lints_in_proc_macros.stderr +++ b/tests/ui/proc-macro/lints_in_proc_macros.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `foobar2` in this scope - --> $DIR/lints_in_proc_macros.rs:9:5 + --> $DIR/lints_in_proc_macros.rs:10:5 | LL | bang_proc_macro2!(); | ^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `foobar` diff --git a/tests/ui/proc-macro/load-two.rs b/tests/ui/proc-macro/load-two.rs index 608379949e665..197e7845db33b 100644 --- a/tests/ui/proc-macro/load-two.rs +++ b/tests/ui/proc-macro/load-two.rs @@ -4,6 +4,7 @@ #![allow(dead_code)] //@ proc-macro: derive-atob.rs //@ proc-macro: derive-ctod.rs +//@ ignore-backends: gcc #[macro_use] extern crate derive_atob; diff --git a/tests/ui/proc-macro/macro-crate-multi-decorator.rs b/tests/ui/proc-macro/macro-crate-multi-decorator.rs index c4f02e7adfcbc..e247c9526a4c9 100644 --- a/tests/ui/proc-macro/macro-crate-multi-decorator.rs +++ b/tests/ui/proc-macro/macro-crate-multi-decorator.rs @@ -2,6 +2,7 @@ //@ check-pass //@ proc-macro: duplicate.rs +//@ ignore-backends: gcc #[macro_use] extern crate duplicate; diff --git a/tests/ui/proc-macro/macro_rules_edition_from_pm.rs b/tests/ui/proc-macro/macro_rules_edition_from_pm.rs index 8fc7d9097493d..fc3ae3ef2c814 100644 --- a/tests/ui/proc-macro/macro_rules_edition_from_pm.rs +++ b/tests/ui/proc-macro/macro_rules_edition_from_pm.rs @@ -7,6 +7,7 @@ //@[edition2021] edition:2021 //@[edition2024] edition:2024 //@ check-pass +//@ ignore-backends: gcc // This checks how the expr fragment specifier works. macro_rules_edition_pm::make_edition_macro!{} diff --git a/tests/ui/proc-macro/match-expander.rs b/tests/ui/proc-macro/match-expander.rs index 23e5746c540f7..b7245c7e682c1 100644 --- a/tests/ui/proc-macro/match-expander.rs +++ b/tests/ui/proc-macro/match-expander.rs @@ -1,4 +1,5 @@ //@ proc-macro: match-expander.rs +//@ ignore-backends: gcc // Ensure that we don't point at macro invocation when providing inference contexts. #[macro_use] diff --git a/tests/ui/proc-macro/match-expander.stderr b/tests/ui/proc-macro/match-expander.stderr index b77468ec60a5e..d2423336b1d22 100644 --- a/tests/ui/proc-macro/match-expander.stderr +++ b/tests/ui/proc-macro/match-expander.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/match-expander.rs:8:5 + --> $DIR/match-expander.rs:9:5 | LL | match_expander::matcher!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `S`, found `bool` diff --git a/tests/ui/proc-macro/mixed-site-span.rs b/tests/ui/proc-macro/mixed-site-span.rs index 442b440c1211a..98a022632cd59 100644 --- a/tests/ui/proc-macro/mixed-site-span.rs +++ b/tests/ui/proc-macro/mixed-site-span.rs @@ -2,6 +2,7 @@ //@ aux-build: token-site-span.rs //@ proc-macro: mixed-site-span.rs +//@ ignore-backends: gcc extern crate mixed_site_span; extern crate token_site_span; diff --git a/tests/ui/proc-macro/mixed-site-span.stderr b/tests/ui/proc-macro/mixed-site-span.stderr index d62031a853c05..2d2d55fe148d5 100644 --- a/tests/ui/proc-macro/mixed-site-span.stderr +++ b/tests/ui/proc-macro/mixed-site-span.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:47:5 + --> $DIR/mixed-site-span.rs:48:5 | LL | invoke_with_crate!{input proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -7,7 +7,7 @@ LL | invoke_with_crate!{input proc_macro_item} = note: this error originates in the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:48:5 + --> $DIR/mixed-site-span.rs:49:5 | LL | invoke_with_ident!{input proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -15,7 +15,7 @@ LL | invoke_with_ident!{input proc_macro_item} = note: this error originates in the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:49:5 + --> $DIR/mixed-site-span.rs:50:5 | LL | invoke_with_crate!{call proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -23,7 +23,7 @@ LL | invoke_with_crate!{call proc_macro_item} = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:50:5 + --> $DIR/mixed-site-span.rs:51:5 | LL | invoke_with_ident!{call proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -31,7 +31,7 @@ LL | invoke_with_ident!{call proc_macro_item} = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:51:5 + --> $DIR/mixed-site-span.rs:52:5 | LL | invoke_with_ident!{hello call proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -39,7 +39,7 @@ LL | invoke_with_ident!{hello call proc_macro_item} = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate::proc_macro_item` - --> $DIR/mixed-site-span.rs:54:5 + --> $DIR/mixed-site-span.rs:55:5 | LL | invoke_with_ident!{krate input proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^ @@ -50,7 +50,7 @@ LL | invoke_with_ident!{krate input proc_macro_item} = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate::proc_macro_item` - --> $DIR/mixed-site-span.rs:55:5 + --> $DIR/mixed-site-span.rs:56:5 | LL | with_crate!{krate input proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^ @@ -61,7 +61,7 @@ LL | with_crate!{krate input proc_macro_item} = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:56:5 + --> $DIR/mixed-site-span.rs:57:5 | LL | with_crate!{krate call proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ @@ -72,7 +72,7 @@ LL | with_crate!{krate call proc_macro_item} = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:60:28 + --> $DIR/mixed-site-span.rs:61:28 | LL | invoke_with_ident!{$crate input proc_macro_item} | ^^^^^^ --------------- help: a similar name exists in the module: `proc_macro_rules` @@ -85,7 +85,7 @@ LL | test!(); = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:61:21 + --> $DIR/mixed-site-span.rs:62:21 | LL | with_crate!{$crate input proc_macro_item} | ^^^^^^ --------------- help: a similar name exists in the module: `proc_macro_rules` @@ -98,7 +98,7 @@ LL | test!(); = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:62:9 + --> $DIR/mixed-site-span.rs:63:9 | LL | with_crate!{$crate call proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^ @@ -112,7 +112,7 @@ LL | test!(); = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:67:5 + --> $DIR/mixed-site-span.rs:68:5 | LL | test!(); | ^^^^^^^ no `proc_macro_item` in the root @@ -120,7 +120,7 @@ LL | test!(); = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate::TokenItem` - --> $DIR/mixed-site-span.rs:87:5 + --> $DIR/mixed-site-span.rs:88:5 | LL | invoke_with_ident!{krate input TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -133,7 +133,7 @@ LL | quote!(use $krate::$ident as token_site_span::TokenItem as _;) | +++++++++++++++++++++++++++++ error[E0432]: unresolved import `$crate::TokenItem` - --> $DIR/mixed-site-span.rs:88:5 + --> $DIR/mixed-site-span.rs:89:5 | LL | with_crate!{krate input TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -146,7 +146,7 @@ LL | quote!(use $krate::$ident as token_site_span::TokenItem as _;) | +++++++++++++++++++++++++++++ error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:89:5 + --> $DIR/mixed-site-span.rs:90:5 | LL | with_crate!{krate call TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -159,7 +159,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:92:5 + --> $DIR/mixed-site-span.rs:93:5 | LL | invoke_with_crate!{mixed TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -173,7 +173,7 @@ LL + ($s:ident $i:ident) => { token_site_span::TokenItem as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:93:5 + --> $DIR/mixed-site-span.rs:94:5 | LL | invoke_with_ident!{mixed TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -187,7 +187,7 @@ LL + ($s:ident $i:ident) => { token_site_span::TokenItem as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:94:5 + --> $DIR/mixed-site-span.rs:95:5 | LL | invoke_with_ident!{krate mixed TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -201,7 +201,7 @@ LL + ($m:ident $s:ident $i:ident) => { token_site_span::TokenItem as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:95:5 + --> $DIR/mixed-site-span.rs:96:5 | LL | with_crate!{krate mixed TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -214,7 +214,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:99:28 + --> $DIR/mixed-site-span.rs:100:28 | LL | invoke_with_ident!{$crate input TokenItem} | ^^^^^^ no `TokenItem` in the root @@ -230,7 +230,7 @@ LL + invoke_with_ident!{token_site_span::TokenItem as _ input TokenItem} | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:100:21 + --> $DIR/mixed-site-span.rs:101:21 | LL | with_crate!{$crate input TokenItem} | ^^^^^^ no `TokenItem` in the root @@ -246,7 +246,7 @@ LL + with_crate!{token_site_span::TokenItem as _ input TokenItem} | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:101:9 + --> $DIR/mixed-site-span.rs:102:9 | LL | with_crate!{$crate call TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -262,7 +262,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:108:5 + --> $DIR/mixed-site-span.rs:109:5 | LL | test!(); | ^^^^^^^ no `TokenItem` in the root @@ -276,7 +276,7 @@ LL + ($m:ident $s:ident $i:ident) => { token_site_span::TokenItem as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:105:9 + --> $DIR/mixed-site-span.rs:106:9 | LL | with_crate!{$crate mixed TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -292,7 +292,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:129:5 + --> $DIR/mixed-site-span.rs:130:5 | LL | invoke_with_crate!{input ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -306,7 +306,7 @@ LL + ($s:ident $i:ident) => { with_crate!{ItemUse as _ $s $i} }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:130:5 + --> $DIR/mixed-site-span.rs:131:5 | LL | invoke_with_ident!{input ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -320,7 +320,7 @@ LL + ($s:ident $i:ident) => { with_crate!{ItemUse as _ $s $i} }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:133:5 + --> $DIR/mixed-site-span.rs:134:5 | LL | invoke_with_crate!{mixed ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -334,7 +334,7 @@ LL + ($s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:134:5 + --> $DIR/mixed-site-span.rs:135:5 | LL | invoke_with_ident!{mixed ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -348,7 +348,7 @@ LL + ($s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:135:5 + --> $DIR/mixed-site-span.rs:136:5 | LL | invoke_with_ident!{krate mixed ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -362,7 +362,7 @@ LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:136:5 + --> $DIR/mixed-site-span.rs:137:5 | LL | with_crate!{krate mixed ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -375,7 +375,7 @@ LL + ItemUse as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:138:5 + --> $DIR/mixed-site-span.rs:139:5 | LL | invoke_with_crate!{call ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -389,7 +389,7 @@ LL + ($s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:139:5 + --> $DIR/mixed-site-span.rs:140:5 | LL | invoke_with_ident!{call ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -403,7 +403,7 @@ LL + ($s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:140:5 + --> $DIR/mixed-site-span.rs:141:5 | LL | invoke_with_ident!{hello call ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -417,7 +417,7 @@ LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:148:5 + --> $DIR/mixed-site-span.rs:149:5 | LL | test!(); | ^^^^^^^ no `ItemUse` in the root @@ -431,7 +431,7 @@ LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:144:9 + --> $DIR/mixed-site-span.rs:145:9 | LL | with_crate!{$crate mixed ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -447,7 +447,7 @@ LL + ItemUse as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:148:5 + --> $DIR/mixed-site-span.rs:149:5 | LL | test!(); | ^^^^^^^ no `ItemUse` in the root @@ -461,7 +461,7 @@ LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:153:1 + --> $DIR/mixed-site-span.rs:154:1 | LL | use_input_crate!{proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -469,7 +469,7 @@ LL | use_input_crate!{proc_macro_item} = note: this error originates in the macro `use_input_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:154:1 + --> $DIR/mixed-site-span.rs:155:1 | LL | use_input_krate!{proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -477,7 +477,7 @@ LL | use_input_krate!{proc_macro_item} = note: this error originates in the macro `use_input_krate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:157:1 + --> $DIR/mixed-site-span.rs:158:1 | LL | use_call_crate!{proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -485,7 +485,7 @@ LL | use_call_crate!{proc_macro_item} = note: this error originates in the macro `use_call_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:158:1 + --> $DIR/mixed-site-span.rs:159:1 | LL | use_call_krate!{proc_macro_item} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root @@ -493,7 +493,7 @@ LL | use_call_krate!{proc_macro_item} = note: this error originates in the macro `use_call_krate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:163:1 + --> $DIR/mixed-site-span.rs:164:1 | LL | use_mixed_crate!{TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -507,7 +507,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:164:1 + --> $DIR/mixed-site-span.rs:165:1 | LL | use_mixed_krate!{TokenItem} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root @@ -521,7 +521,7 @@ LL + token_site_span::TokenItem as _ | error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:169:1 + --> $DIR/mixed-site-span.rs:170:1 | LL | use_input_crate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -529,7 +529,7 @@ LL | use_input_crate!{ItemUse} = note: this error originates in the macro `use_input_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:170:1 + --> $DIR/mixed-site-span.rs:171:1 | LL | use_input_krate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -537,7 +537,7 @@ LL | use_input_krate!{ItemUse} = note: this error originates in the macro `use_input_krate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:171:1 + --> $DIR/mixed-site-span.rs:172:1 | LL | use_mixed_crate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -545,7 +545,7 @@ LL | use_mixed_crate!{ItemUse} = note: this error originates in the macro `use_mixed_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:172:1 + --> $DIR/mixed-site-span.rs:173:1 | LL | use_mixed_krate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -553,7 +553,7 @@ LL | use_mixed_krate!{ItemUse} = note: this error originates in the macro `use_mixed_krate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:173:1 + --> $DIR/mixed-site-span.rs:174:1 | LL | use_call_crate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -561,7 +561,7 @@ LL | use_call_crate!{ItemUse} = note: this error originates in the macro `use_call_crate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0432]: unresolved import `$crate` - --> $DIR/mixed-site-span.rs:174:1 + --> $DIR/mixed-site-span.rs:175:1 | LL | use_call_krate!{ItemUse} | ^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root @@ -569,7 +569,7 @@ LL | use_call_krate!{ItemUse} = note: this error originates in the macro `use_call_krate` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0426]: use of undeclared label `'label_use` - --> $DIR/mixed-site-span.rs:21:9 + --> $DIR/mixed-site-span.rs:22:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ undeclared label `'label_use` @@ -577,7 +577,7 @@ LL | proc_macro_rules!(); = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0412]: cannot find type `ItemUse` in crate `$crate` - --> $DIR/mixed-site-span.rs:21:9 + --> $DIR/mixed-site-span.rs:22:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ not found in `$crate` @@ -589,7 +589,7 @@ LL + use ItemUse; | error[E0425]: cannot find value `local_use` in this scope - --> $DIR/mixed-site-span.rs:21:9 + --> $DIR/mixed-site-span.rs:22:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `local_def` @@ -597,7 +597,7 @@ LL | proc_macro_rules!(); = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope - --> $DIR/mixed-site-span.rs:26:9 + --> $DIR/mixed-site-span.rs:27:9 | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` diff --git a/tests/ui/proc-macro/modify-ast.rs b/tests/ui/proc-macro/modify-ast.rs index 9e890f3ebaad9..75aea597ed576 100644 --- a/tests/ui/proc-macro/modify-ast.rs +++ b/tests/ui/proc-macro/modify-ast.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: modify-ast.rs +//@ ignore-backends: gcc extern crate modify_ast; diff --git a/tests/ui/proc-macro/parent-source-spans.rs b/tests/ui/proc-macro/parent-source-spans.rs index cc3ac795f7f17..f675f6fb6f7da 100644 --- a/tests/ui/proc-macro/parent-source-spans.rs +++ b/tests/ui/proc-macro/parent-source-spans.rs @@ -1,4 +1,5 @@ //@ proc-macro: parent-source-spans.rs +//@ ignore-backends: gcc #![feature(decl_macro)] diff --git a/tests/ui/proc-macro/parent-source-spans.stderr b/tests/ui/proc-macro/parent-source-spans.stderr index db1eed5e4588e..28a70eea873d1 100644 --- a/tests/ui/proc-macro/parent-source-spans.stderr +++ b/tests/ui/proc-macro/parent-source-spans.stderr @@ -1,5 +1,5 @@ error: first final: "hello" - --> $DIR/parent-source-spans.rs:16:12 + --> $DIR/parent-source-spans.rs:17:12 | LL | three!($a, $b); | ^^ @@ -10,7 +10,7 @@ LL | one!("hello", "world"); = note: this error originates in the macro `two` which comes from the expansion of the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) error: second final: "world" - --> $DIR/parent-source-spans.rs:16:16 + --> $DIR/parent-source-spans.rs:17:16 | LL | three!($a, $b); | ^^ @@ -21,7 +21,7 @@ LL | one!("hello", "world"); = note: this error originates in the macro `two` which comes from the expansion of the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) error: first parent: "hello" - --> $DIR/parent-source-spans.rs:10:5 + --> $DIR/parent-source-spans.rs:11:5 | LL | two!($a, $b); | ^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | one!("hello", "world"); = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) error: second parent: "world" - --> $DIR/parent-source-spans.rs:10:5 + --> $DIR/parent-source-spans.rs:11:5 | LL | two!($a, $b); | ^^^^^^^^^^^^ @@ -43,31 +43,31 @@ LL | one!("hello", "world"); = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) error: first grandparent: "hello" - --> $DIR/parent-source-spans.rs:36:5 + --> $DIR/parent-source-spans.rs:37:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ error: second grandparent: "world" - --> $DIR/parent-source-spans.rs:36:5 + --> $DIR/parent-source-spans.rs:37:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ error: first source: "hello" - --> $DIR/parent-source-spans.rs:36:5 + --> $DIR/parent-source-spans.rs:37:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ error: second source: "world" - --> $DIR/parent-source-spans.rs:36:5 + --> $DIR/parent-source-spans.rs:37:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ error: first final: "yay" - --> $DIR/parent-source-spans.rs:16:12 + --> $DIR/parent-source-spans.rs:17:12 | LL | three!($a, $b); | ^^ @@ -78,7 +78,7 @@ LL | two!("yay", "rust"); = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) error: second final: "rust" - --> $DIR/parent-source-spans.rs:16:16 + --> $DIR/parent-source-spans.rs:17:16 | LL | three!($a, $b); | ^^ @@ -89,55 +89,55 @@ LL | two!("yay", "rust"); = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) error: first parent: "yay" - --> $DIR/parent-source-spans.rs:42:5 + --> $DIR/parent-source-spans.rs:43:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ error: second parent: "rust" - --> $DIR/parent-source-spans.rs:42:5 + --> $DIR/parent-source-spans.rs:43:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ error: first source: "yay" - --> $DIR/parent-source-spans.rs:42:5 + --> $DIR/parent-source-spans.rs:43:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ error: second source: "rust" - --> $DIR/parent-source-spans.rs:42:5 + --> $DIR/parent-source-spans.rs:43:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ error: first final: "hip" - --> $DIR/parent-source-spans.rs:48:12 + --> $DIR/parent-source-spans.rs:49:12 | LL | three!("hip", "hop"); | ^^^^^ error: second final: "hop" - --> $DIR/parent-source-spans.rs:48:19 + --> $DIR/parent-source-spans.rs:49:19 | LL | three!("hip", "hop"); | ^^^^^ error: first source: "hip" - --> $DIR/parent-source-spans.rs:48:12 + --> $DIR/parent-source-spans.rs:49:12 | LL | three!("hip", "hop"); | ^^^^^ error: second source: "hop" - --> $DIR/parent-source-spans.rs:48:19 + --> $DIR/parent-source-spans.rs:49:19 | LL | three!("hip", "hop"); | ^^^^^ error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:29:5 + --> $DIR/parent-source-spans.rs:30:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` @@ -152,7 +152,7 @@ LL | one!("hello", "world"); = note: this error originates in the macro `parent_source_spans` which comes from the expansion of the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:29:5 + --> $DIR/parent-source-spans.rs:30:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` @@ -167,7 +167,7 @@ LL | two!("yay", "rust"); = note: this error originates in the macro `parent_source_spans` which comes from the expansion of the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:29:5 + --> $DIR/parent-source-spans.rs:30:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` diff --git a/tests/ui/proc-macro/quote/basic.rs b/tests/ui/proc-macro/quote/basic.rs index 0336dbb785623..4c6fb2408fb49 100644 --- a/tests/ui/proc-macro/quote/basic.rs +++ b/tests/ui/proc-macro/quote/basic.rs @@ -1,5 +1,6 @@ //@ run-pass //@ proc-macro: basic.rs +//@ ignore-backends: gcc extern crate basic; diff --git a/tests/ui/proc-macro/span-api-tests.rs b/tests/ui/proc-macro/span-api-tests.rs index 792859ed05b16..12832ba116397 100644 --- a/tests/ui/proc-macro/span-api-tests.rs +++ b/tests/ui/proc-macro/span-api-tests.rs @@ -2,6 +2,7 @@ //@ proc-macro: span-api-tests.rs //@ aux-build:span-test-macros.rs //@ compile-flags: -Ztranslate-remapped-path-to-local-path=yes +//@ ignore-backends: gcc #[macro_use] extern crate span_test_macros; diff --git a/tests/ui/proc-macro/span-from-proc-macro.rs b/tests/ui/proc-macro/span-from-proc-macro.rs index 4e12a695a5c09..24a28d53476c1 100644 --- a/tests/ui/proc-macro/span-from-proc-macro.rs +++ b/tests/ui/proc-macro/span-from-proc-macro.rs @@ -1,6 +1,7 @@ //@ proc-macro: custom-quote.rs //@ proc-macro: span-from-proc-macro.rs //@ compile-flags: -Z macro-backtrace +//@ ignore-backends: gcc #[macro_use] extern crate span_from_proc_macro; diff --git a/tests/ui/proc-macro/span-from-proc-macro.stderr b/tests/ui/proc-macro/span-from-proc-macro.stderr index c79ab04eadf4e..945a5620fac48 100644 --- a/tests/ui/proc-macro/span-from-proc-macro.stderr +++ b/tests/ui/proc-macro/span-from-proc-macro.stderr @@ -7,7 +7,7 @@ LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> Tok LL | field: MissingType | ^^^^^^^^^^^ not found in this scope | - ::: $DIR/span-from-proc-macro.rs:8:1 + ::: $DIR/span-from-proc-macro.rs:9:1 | LL | #[error_from_attribute] | ----------------------- in this attribute macro expansion @@ -21,7 +21,7 @@ LL | pub fn error_from_derive(_input: TokenStream) -> TokenStream { LL | Variant(OtherMissingType) | ^^^^^^^^^^^^^^^^ not found in this scope | - ::: $DIR/span-from-proc-macro.rs:11:10 + ::: $DIR/span-from-proc-macro.rs:12:10 | LL | #[derive(ErrorFromDerive)] | --------------- in this derive macro expansion @@ -35,7 +35,7 @@ LL | custom_quote::custom_quote! { LL | my_ident | ^^^^^^^^ not found in this scope | - ::: $DIR/span-from-proc-macro.rs:16:5 + ::: $DIR/span-from-proc-macro.rs:17:5 | LL | other_error_from_bang!(); | ------------------------ in this macro invocation @@ -51,7 +51,7 @@ LL | let bang_error: bool = 25; LL | pub fn error_from_bang(_input: TokenStream) -> TokenStream { | ---------------------------------------------------------- in this expansion of `error_from_bang!` | - ::: $DIR/span-from-proc-macro.rs:15:5 + ::: $DIR/span-from-proc-macro.rs:16:5 | LL | error_from_bang!(); | ------------------ in this macro invocation diff --git a/tests/ui/proc-macro/weird-hygiene.rs b/tests/ui/proc-macro/weird-hygiene.rs index de55484109ae3..8d8427d0e417e 100644 --- a/tests/ui/proc-macro/weird-hygiene.rs +++ b/tests/ui/proc-macro/weird-hygiene.rs @@ -1,4 +1,5 @@ //@ proc-macro: weird-hygiene.rs +//@ ignore-backends: gcc #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] diff --git a/tests/ui/proc-macro/weird-hygiene.stderr b/tests/ui/proc-macro/weird-hygiene.stderr index 256e68e8970e6..0cfac3f89a045 100644 --- a/tests/ui/proc-macro/weird-hygiene.stderr +++ b/tests/ui/proc-macro/weird-hygiene.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `hidden_ident` in this scope - --> $DIR/weird-hygiene.rs:23:43 + --> $DIR/weird-hygiene.rs:24:43 | LL | Value = (stringify!($tokens + hidden_ident), 1).1 | ^^^^^^^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | other!(50); = note: this error originates in the macro `inner` which comes from the expansion of the macro `other` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `hidden_ident` in this scope - --> $DIR/weird-hygiene.rs:34:13 + --> $DIR/weird-hygiene.rs:35:13 | LL | hidden_ident | ^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/process/multi-panic.rs b/tests/ui/process/multi-panic.rs index 1fddffeb770a3..67bbd16fba75e 100644 --- a/tests/ui/process/multi-panic.rs +++ b/tests/ui/process/multi-panic.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-subprocess //@ needs-unwind +//@ ignore-backends: gcc fn check_for_no_backtrace(test: std::process::Output) { assert!(!test.status.success()); diff --git a/tests/ui/resolve/prelude-order.rs b/tests/ui/resolve/prelude-order.rs index a3f194270d483..c6683bdff22a6 100644 --- a/tests/ui/resolve/prelude-order.rs +++ b/tests/ui/resolve/prelude-order.rs @@ -1,5 +1,6 @@ //@ proc-macro:macro_helpers.rs //@ compile-flags: --crate-type=lib +//@ ignore-backends: gcc /* There are 5 preludes and 3 namespaces. Test the order in which they are resolved. * See https://doc.rust-lang.org/nightly/reference/names/preludes.html. diff --git a/tests/ui/resolve/prelude-order.stderr b/tests/ui/resolve/prelude-order.stderr index 1b9cc94285aa5..4dad39fb6d240 100644 --- a/tests/ui/resolve/prelude-order.stderr +++ b/tests/ui/resolve/prelude-order.stderr @@ -1,17 +1,17 @@ error[E0433]: failed to resolve: could not find `inner` in `type_ns` - --> $DIR/prelude-order.rs:61:12 + --> $DIR/prelude-order.rs:62:12 | LL | #[type_ns::inner] | ^^^^^ could not find `inner` in `type_ns` error[E0433]: failed to resolve: could not find `inner` in `usize` - --> $DIR/prelude-order.rs:73:10 + --> $DIR/prelude-order.rs:74:10 | LL | #[usize::inner] | ^^^^^ could not find `inner` in `usize` error[E0573]: expected type, found crate `Option` - --> $DIR/prelude-order.rs:79:12 + --> $DIR/prelude-order.rs:80:12 | LL | fn e2() -> Option { None } | ^^^^^^^^^^^ not a type @@ -22,7 +22,7 @@ LL + use std::option::Option; | error[E0308]: mismatched types - --> $DIR/prelude-order.rs:82:1 + --> $DIR/prelude-order.rs:83:1 | LL | #[test] | ^^^^^^^- help: try adding a return type: `-> &'static str` @@ -32,7 +32,7 @@ LL | #[test] = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types - --> $DIR/prelude-order.rs:86:1 + --> $DIR/prelude-order.rs:87:1 | LL | #[global_allocator] | ^^^^^^^^^^^^^^^^^^^- help: try adding a return type: `-> &'static str` diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs index 414d5518e1fb0..2387dfb2bc575 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs @@ -7,6 +7,7 @@ //@ check-pass //@ proc-macro: count.rs +//@ ignore-backends: gcc extern crate count; const _: () = { diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index 5e91f22aec029..d3b4d057e6d0f 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -19,6 +19,7 @@ // FIXME(#117097): backtrace (possibly unwinding mechanism) seems to be different on at least // `i686-mingw` (32-bit windows-gnu)? cc #128911. //@ ignore-windows-gnu +//@ ignore-backends: gcc use std::env; diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 913d3637c8f2b..3e092728f2929 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -10,6 +10,7 @@ //@ ignore-tvos stack overflow handlers aren't enabled //@ ignore-watchos stack overflow handlers aren't enabled //@ ignore-visionos stack overflow handlers aren't enabled +//@ ignore-backends: gcc #![feature(rustc_private)] diff --git a/tests/ui/rust-2018/suggestions-not-always-applicable.fixed b/tests/ui/rust-2018/suggestions-not-always-applicable.fixed index e3070ba150b6d..3a42434494d84 100644 --- a/tests/ui/rust-2018/suggestions-not-always-applicable.fixed +++ b/tests/ui/rust-2018/suggestions-not-always-applicable.fixed @@ -3,6 +3,7 @@ //@ run-rustfix //@ rustfix-only-machine-applicable //@ check-pass +//@ ignore-backends: gcc #![warn(rust_2018_compatibility)] diff --git a/tests/ui/rust-2018/suggestions-not-always-applicable.rs b/tests/ui/rust-2018/suggestions-not-always-applicable.rs index e3070ba150b6d..3a42434494d84 100644 --- a/tests/ui/rust-2018/suggestions-not-always-applicable.rs +++ b/tests/ui/rust-2018/suggestions-not-always-applicable.rs @@ -3,6 +3,7 @@ //@ run-rustfix //@ rustfix-only-machine-applicable //@ check-pass +//@ ignore-backends: gcc #![warn(rust_2018_compatibility)] diff --git a/tests/ui/rust-2021/reserved-prefixes-via-macro.rs b/tests/ui/rust-2021/reserved-prefixes-via-macro.rs index eec1b859c2033..456649f23ca67 100644 --- a/tests/ui/rust-2021/reserved-prefixes-via-macro.rs +++ b/tests/ui/rust-2021/reserved-prefixes-via-macro.rs @@ -1,6 +1,7 @@ //@ run-pass //@ edition:2021 //@ proc-macro: reserved-prefixes-macro-2018.rs +//@ ignore-backends: gcc extern crate reserved_prefixes_macro_2018 as m2018; diff --git a/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs b/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs index ead2ab40b77ac..ddb32c2671739 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs +++ b/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs @@ -1,6 +1,7 @@ //@ run-pass //@ edition:2024 //@ proc-macro: reserved-guarded-strings-macro-2021.rs +//@ ignore-backends: gcc extern crate reserved_guarded_strings_macro_2021 as m2021; diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-from-pm.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-from-pm.rs index e2c504e708c55..8b7073649b7a2 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-from-pm.rs +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-from-pm.rs @@ -6,6 +6,7 @@ //@[edition2021] edition:2021 //@[edition2024] edition:2024 //@ proc-macro: unsafe-attributes-pm.rs +//@ ignore-backends: gcc unsafe_attributes_pm::missing_unsafe!(); diff --git a/tests/ui/sanitizer/cfi/transparent-has-regions.rs b/tests/ui/sanitizer/cfi/transparent-has-regions.rs index b82850133c100..3e9893df23c92 100644 --- a/tests/ui/sanitizer/cfi/transparent-has-regions.rs +++ b/tests/ui/sanitizer/cfi/transparent-has-regions.rs @@ -3,6 +3,7 @@ //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ build-pass +//@ ignore-backends: gcc pub trait Trait {} diff --git a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs index be81c7bd0cac1..ac2b95b639820 100644 --- a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs +++ b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs @@ -7,6 +7,7 @@ //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ build-pass +//@ ignore-backends: gcc use std::future::Future; diff --git a/tests/ui/sepcomp/sepcomp-lib-lto.rs b/tests/ui/sepcomp/sepcomp-lib-lto.rs index 2166a8fd031ff..f47ea25a4fc58 100644 --- a/tests/ui/sepcomp/sepcomp-lib-lto.rs +++ b/tests/ui/sepcomp/sepcomp-lib-lto.rs @@ -5,6 +5,7 @@ //@ aux-build:sepcomp_lib.rs //@ compile-flags: -C lto -g //@ no-prefer-dynamic +//@ ignore-backends: gcc extern crate sepcomp_lib; use sepcomp_lib::a::one; diff --git a/tests/ui/sepcomp/sepcomp-unwind.rs b/tests/ui/sepcomp/sepcomp-unwind.rs index 95591676b5eed..0038e887c4ee6 100644 --- a/tests/ui/sepcomp/sepcomp-unwind.rs +++ b/tests/ui/sepcomp/sepcomp-unwind.rs @@ -3,6 +3,7 @@ #![allow(dead_code)] //@ compile-flags: -C codegen-units=3 //@ needs-threads +//@ ignore-backends: gcc // Test unwinding through multiple compilation units. diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs index caec607d6fe79..d67ff6b33b79b 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ ignore-backends: gcc #![feature(repr_simd, core_intrinsics)] #![allow(non_camel_case_types)] diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr index a27a8d721fb05..a2646c8e84872 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr @@ -1,167 +1,167 @@ error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:68:9 + --> $DIR/generic-arithmetic-2.rs:69:9 | LL | simd_add(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_sub` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:70:9 + --> $DIR/generic-arithmetic-2.rs:71:9 | LL | simd_sub(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_mul` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:72:9 + --> $DIR/generic-arithmetic-2.rs:73:9 | LL | simd_mul(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_div` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:74:9 + --> $DIR/generic-arithmetic-2.rs:75:9 | LL | simd_div(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:76:9 + --> $DIR/generic-arithmetic-2.rs:77:9 | LL | simd_shl(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:78:9 + --> $DIR/generic-arithmetic-2.rs:79:9 | LL | simd_shr(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_funnel_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:80:9 + --> $DIR/generic-arithmetic-2.rs:81:9 | LL | simd_funnel_shl(0, 0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_funnel_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:82:9 + --> $DIR/generic-arithmetic-2.rs:83:9 | LL | simd_funnel_shr(0, 0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:84:9 + --> $DIR/generic-arithmetic-2.rs:85:9 | LL | simd_and(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:86:9 + --> $DIR/generic-arithmetic-2.rs:87:9 | LL | simd_or(0, 0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:88:9 + --> $DIR/generic-arithmetic-2.rs:89:9 | LL | simd_xor(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_neg` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:91:9 + --> $DIR/generic-arithmetic-2.rs:92:9 | LL | simd_neg(0); | ^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:93:9 + --> $DIR/generic-arithmetic-2.rs:94:9 | LL | simd_bswap(0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:95:9 + --> $DIR/generic-arithmetic-2.rs:96:9 | LL | simd_bitreverse(0); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:97:9 + --> $DIR/generic-arithmetic-2.rs:98:9 | LL | simd_ctlz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:99:9 + --> $DIR/generic-arithmetic-2.rs:100:9 | LL | simd_cttz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:102:9 + --> $DIR/generic-arithmetic-2.rs:103:9 | LL | simd_shl(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:104:9 + --> $DIR/generic-arithmetic-2.rs:105:9 | LL | simd_shr(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_funnel_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:106:9 + --> $DIR/generic-arithmetic-2.rs:107:9 | LL | simd_funnel_shl(z, z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_funnel_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:108:9 + --> $DIR/generic-arithmetic-2.rs:109:9 | LL | simd_funnel_shr(z, z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:110:9 + --> $DIR/generic-arithmetic-2.rs:111:9 | LL | simd_and(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:112:9 + --> $DIR/generic-arithmetic-2.rs:113:9 | LL | simd_or(z, z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:114:9 + --> $DIR/generic-arithmetic-2.rs:115:9 | LL | simd_xor(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:116:9 + --> $DIR/generic-arithmetic-2.rs:117:9 | LL | simd_bswap(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:118:9 + --> $DIR/generic-arithmetic-2.rs:119:9 | LL | simd_bitreverse(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:120:9 + --> $DIR/generic-arithmetic-2.rs:121:9 | LL | simd_ctlz(z); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctpop` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:122:9 + --> $DIR/generic-arithmetic-2.rs:123:9 | LL | simd_ctpop(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:124:9 + --> $DIR/generic-arithmetic-2.rs:125:9 | LL | simd_cttz(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-elements.rs b/tests/ui/simd/intrinsic/generic-elements.rs index 905299a928971..08d1e3ce944d9 100644 --- a/tests/ui/simd/intrinsic/generic-elements.rs +++ b/tests/ui/simd/intrinsic/generic-elements.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ ignore-backends: gcc #![feature( repr_simd, diff --git a/tests/ui/simd/intrinsic/generic-elements.stderr b/tests/ui/simd/intrinsic/generic-elements.stderr index 3779aa86cee19..a32a923633b4c 100644 --- a/tests/ui/simd/intrinsic/generic-elements.stderr +++ b/tests/ui/simd/intrinsic/generic-elements.stderr @@ -1,125 +1,125 @@ error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:51:9 + --> $DIR/generic-elements.rs:52:9 | LL | simd_insert(0, 0, 0); | ^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected inserted type `i32` (element of input `i32x4`), found `f64` - --> $DIR/generic-elements.rs:53:9 + --> $DIR/generic-elements.rs:54:9 | LL | simd_insert(x, 0, 1.0); | ^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_extract` intrinsic: expected return type `i32` (element of input `i32x4`), found `f32` - --> $DIR/generic-elements.rs:55:9 + --> $DIR/generic-elements.rs:56:9 | LL | simd_extract::<_, f32>(x, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:59:9 + --> $DIR/generic-elements.rs:60:9 | LL | simd_shuffle::(0, 0, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:62:9 + --> $DIR/generic-elements.rs:63:9 | LL | simd_shuffle::(0, 0, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:65:9 + --> $DIR/generic-elements.rs:66:9 | LL | simd_shuffle::(0, 0, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x2` with element type `f32` - --> $DIR/generic-elements.rs:68:9 + --> $DIR/generic-elements.rs:69:9 | LL | simd_shuffle::<_, _, f32x2>(x, x, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x4` with element type `f32` - --> $DIR/generic-elements.rs:70:9 + --> $DIR/generic-elements.rs:71:9 | LL | simd_shuffle::<_, _, f32x4>(x, x, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x8` with element type `f32` - --> $DIR/generic-elements.rs:72:9 + --> $DIR/generic-elements.rs:73:9 | LL | simd_shuffle::<_, _, f32x8>(x, x, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 2, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:75:9 + --> $DIR/generic-elements.rs:76:9 | LL | simd_shuffle::<_, _, i32x8>(x, x, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 4, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:77:9 + --> $DIR/generic-elements.rs:78:9 | LL | simd_shuffle::<_, _, i32x8>(x, x, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 8, found `i32x2` with length 2 - --> $DIR/generic-elements.rs:79:9 + --> $DIR/generic-elements.rs:80:9 | LL | simd_shuffle::<_, _, i32x2>(x, x, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:83:9 + --> $DIR/generic-elements.rs:84:9 | LL | simd_shuffle_const_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:86:9 + --> $DIR/generic-elements.rs:87:9 | LL | simd_shuffle_const_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:89:9 + --> $DIR/generic-elements.rs:90:9 | LL | simd_shuffle_const_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x2` with element type `f32` - --> $DIR/generic-elements.rs:92:9 + --> $DIR/generic-elements.rs:93:9 | LL | simd_shuffle_const_generic::<_, f32x2, I2>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x4` with element type `f32` - --> $DIR/generic-elements.rs:94:9 + --> $DIR/generic-elements.rs:95:9 | LL | simd_shuffle_const_generic::<_, f32x4, I4>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x8` with element type `f32` - --> $DIR/generic-elements.rs:96:9 + --> $DIR/generic-elements.rs:97:9 | LL | simd_shuffle_const_generic::<_, f32x8, I8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return type of length 2, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:99:9 + --> $DIR/generic-elements.rs:100:9 | LL | simd_shuffle_const_generic::<_, i32x8, I2>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return type of length 4, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:101:9 + --> $DIR/generic-elements.rs:102:9 | LL | simd_shuffle_const_generic::<_, i32x8, I4>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_const_generic` intrinsic: expected return type of length 8, found `i32x2` with length 2 - --> $DIR/generic-elements.rs:103:9 + --> $DIR/generic-elements.rs:104:9 | LL | simd_shuffle_const_generic::<_, i32x2, I8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/masked-load-store-build-fail.rs b/tests/ui/simd/masked-load-store-build-fail.rs index 4b6cc17683c2c..c711b6dfd9770 100644 --- a/tests/ui/simd/masked-load-store-build-fail.rs +++ b/tests/ui/simd/masked-load-store-build-fail.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ ignore-backends: gcc #![feature(repr_simd, core_intrinsics)] use std::intrinsics::simd::{simd_masked_load, simd_masked_store}; diff --git a/tests/ui/simd/masked-load-store-build-fail.stderr b/tests/ui/simd/masked-load-store-build-fail.stderr index 7f09841b59710..b9158f46ea9aa 100644 --- a/tests/ui/simd/masked-load-store-build-fail.stderr +++ b/tests/ui/simd/masked-load-store-build-fail.stderr @@ -1,47 +1,47 @@ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected third argument with length 8 (same as input type `Simd`), found `Simd` with length 4 - --> $DIR/masked-load-store-build-fail.rs:15:9 + --> $DIR/masked-load-store-build-fail.rs:16:9 | LL | simd_masked_load(Simd::([-1, 0, -1, -1, 0, 0, 0, 0]), arr.as_ptr(), default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u8` of second argument `*const i8` to be a pointer to the element type `u8` of the first argument `Simd`, found `u8` != `*_ u8` - --> $DIR/masked-load-store-build-fail.rs:18:9 + --> $DIR/masked-load-store-build-fail.rs:19:9 | LL | simd_masked_load(Simd::([-1, 0, -1, -1]), arr.as_ptr() as *const i8, default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*_ u32` - --> $DIR/masked-load-store-build-fail.rs:21:9 + --> $DIR/masked-load-store-build-fail.rs:22:9 | LL | simd_masked_load(Simd::([-1, 0, -1, -1]), arr.as_ptr(), Simd::([9; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected mask element type to be an integer, found `f32` - --> $DIR/masked-load-store-build-fail.rs:24:9 + --> $DIR/masked-load-store-build-fail.rs:25:9 | LL | simd_masked_load(Simd::([1.0, 0.0, 1.0, 1.0]), arr.as_ptr(), default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*mut u32` - --> $DIR/masked-load-store-build-fail.rs:27:9 + --> $DIR/masked-load-store-build-fail.rs:28:9 | LL | simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u32; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd`, found `u8` != `*mut u8` - --> $DIR/masked-load-store-build-fail.rs:30:9 + --> $DIR/masked-load-store-build-fail.rs:31:9 | LL | simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u8; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected third argument with length 4 (same as input type `Simd`), found `Simd` with length 2 - --> $DIR/masked-load-store-build-fail.rs:33:9 + --> $DIR/masked-load-store-build-fail.rs:34:9 | LL | simd_masked_store(Simd([-1i8; 4]), arr.as_mut_ptr(), Simd([5u8; 2])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected mask element type to be an integer, found `f32` - --> $DIR/masked-load-store-build-fail.rs:36:9 + --> $DIR/masked-load-store-build-fail.rs:37:9 | LL | simd_masked_store(Simd([1f32; 4]), arr.as_mut_ptr(), Simd([5u8; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/monomorphize-shuffle-index.generic.stderr b/tests/ui/simd/monomorphize-shuffle-index.generic.stderr index b0742bc5ef806..c82adbf8d4c58 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.generic.stderr +++ b/tests/ui/simd/monomorphize-shuffle-index.generic.stderr @@ -1,5 +1,5 @@ error: overly complex generic constant - --> $DIR/monomorphize-shuffle-index.rs:36:51 + --> $DIR/monomorphize-shuffle-index.rs:37:51 | LL | return simd_shuffle_const_generic::<_, _, { &Self::I.0 }>(a, b); | ^^----------^^ diff --git a/tests/ui/simd/monomorphize-shuffle-index.rs b/tests/ui/simd/monomorphize-shuffle-index.rs index 1490f8e2319f0..ba952cdb0dc60 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.rs +++ b/tests/ui/simd/monomorphize-shuffle-index.rs @@ -1,6 +1,7 @@ //@ revisions: old generic generic_with_fn //@[old]run-pass //@[generic_with_fn]run-pass +//@ ignore-backends: gcc #![feature( repr_simd, core_intrinsics, diff --git a/tests/ui/simd/not-out-of-bounds.rs b/tests/ui/simd/not-out-of-bounds.rs index 4bd2a69edbf5e..c80c90e3ab9bd 100644 --- a/tests/ui/simd/not-out-of-bounds.rs +++ b/tests/ui/simd/not-out-of-bounds.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ ignore-backends: gcc #![allow(non_camel_case_types)] #![feature(repr_simd, core_intrinsics)] diff --git a/tests/ui/simd/not-out-of-bounds.stderr b/tests/ui/simd/not-out-of-bounds.stderr index 4b6bda93e4561..734c21fbf4158 100644 --- a/tests/ui/simd/not-out-of-bounds.stderr +++ b/tests/ui/simd/not-out-of-bounds.stderr @@ -1,5 +1,5 @@ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 4) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | test_shuffle_lanes!(2, u8x2, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 8) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | test_shuffle_lanes!(4, u8x4, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 16) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | test_shuffle_lanes!(8, u8x8, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 32) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL | test_shuffle_lanes!(16, u8x16, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 64) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | test_shuffle_lanes!(32, u8x32, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 128) - --> $DIR/not-out-of-bounds.rs:52:21 + --> $DIR/not-out-of-bounds.rs:53:21 | LL | $y(vec1, vec2, IDX) | ^^^^^^^^^^^^^^^^^^^ @@ -65,19 +65,19 @@ LL | test_shuffle_lanes!(64, u8x64, simd_shuffle); = note: this error originates in the macro `test_shuffle_lanes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: SIMD index #0 is out of bounds (limit 4) - --> $DIR/not-out-of-bounds.rs:77:23 + --> $DIR/not-out-of-bounds.rs:78:23 | LL | let _: u8x2 = simd_shuffle(v, v, I); | ^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_insert` intrinsic: SIMD index #1 is out of bounds (limit 2) - --> $DIR/not-out-of-bounds.rs:83:9 + --> $DIR/not-out-of-bounds.rs:84:9 | LL | simd_insert(v, 2, 0u8); | ^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_extract` intrinsic: SIMD index #1 is out of bounds (limit 2) - --> $DIR/not-out-of-bounds.rs:84:24 + --> $DIR/not-out-of-bounds.rs:85:24 | LL | let _val: u8 = simd_extract(v, 2); | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/structs-enums/unit-like-struct-drop-run.rs b/tests/ui/structs-enums/unit-like-struct-drop-run.rs index 3d00871837cf8..5e3fc5931bdc5 100644 --- a/tests/ui/structs-enums/unit-like-struct-drop-run.rs +++ b/tests/ui/structs-enums/unit-like-struct-drop-run.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-unwind //@ needs-threads +//@ ignore-backends: gcc // Make sure the destructor is run for unit-like structs. diff --git a/tests/ui/suggestions/issue-61963.rs b/tests/ui/suggestions/issue-61963.rs index 77e4b21f5dbd5..bf0680b986038 100644 --- a/tests/ui/suggestions/issue-61963.rs +++ b/tests/ui/suggestions/issue-61963.rs @@ -1,6 +1,7 @@ //@ edition: 2015 //@ proc-macro: issue-61963.rs //@ proc-macro: issue-61963-1.rs +//@ ignore-backends: gcc #![deny(bare_trait_objects)] #[macro_use] diff --git a/tests/ui/suggestions/issue-61963.stderr b/tests/ui/suggestions/issue-61963.stderr index ffdeef12bb7f1..f9146a619ec45 100644 --- a/tests/ui/suggestions/issue-61963.stderr +++ b/tests/ui/suggestions/issue-61963.stderr @@ -1,5 +1,5 @@ error: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-61963.rs:23:14 + --> $DIR/issue-61963.rs:24:14 | LL | bar: Box, | ^^^ @@ -7,7 +7,7 @@ LL | bar: Box, = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see note: the lint level is defined here - --> $DIR/issue-61963.rs:4:9 + --> $DIR/issue-61963.rs:5:9 | LL | #![deny(bare_trait_objects)] | ^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | bar: Box, | +++ error: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-61963.rs:19:1 + --> $DIR/issue-61963.rs:20:1 | LL | pub struct Foo { | ^^^ diff --git a/tests/ui/suggestions/suggest-ref-macro.rs b/tests/ui/suggestions/suggest-ref-macro.rs index 2f31af33782cb..41a2acf90628a 100644 --- a/tests/ui/suggestions/suggest-ref-macro.rs +++ b/tests/ui/suggestions/suggest-ref-macro.rs @@ -1,5 +1,6 @@ // run-check //@ proc-macro: proc-macro-type-error.rs +//@ ignore-backends: gcc extern crate proc_macro_type_error; diff --git a/tests/ui/suggestions/suggest-ref-macro.stderr b/tests/ui/suggestions/suggest-ref-macro.stderr index 08bc9e86a50f8..2a52c1e244502 100644 --- a/tests/ui/suggestions/suggest-ref-macro.stderr +++ b/tests/ui/suggestions/suggest-ref-macro.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/suggest-ref-macro.rs:8:1 + --> $DIR/suggest-ref-macro.rs:9:1 | LL | #[hello] | ^^^^^^^^ @@ -8,14 +8,14 @@ LL | #[hello] | arguments to this function are incorrect | note: function defined here - --> $DIR/suggest-ref-macro.rs:8:1 + --> $DIR/suggest-ref-macro.rs:9:1 | LL | #[hello] | ^^^^^^^^ = note: this error originates in the attribute macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types - --> $DIR/suggest-ref-macro.rs:15:11 + --> $DIR/suggest-ref-macro.rs:16:11 | LL | x(123); | - ^^^ expected `&mut i32`, found integer @@ -26,7 +26,7 @@ LL | bla!(); | ------ in this macro invocation | note: function defined here - --> $DIR/suggest-ref-macro.rs:11:4 + --> $DIR/suggest-ref-macro.rs:12:4 | LL | fn x(_: &mut i32) {} | ^ ----------- @@ -37,7 +37,7 @@ LL | x(&mut 123); | ++++ error[E0308]: mismatched types - --> $DIR/suggest-ref-macro.rs:26:10 + --> $DIR/suggest-ref-macro.rs:27:10 | LL | x($v) | - arguments to this function are incorrect @@ -46,7 +46,7 @@ LL | bla!(456); | ^^^ expected `&mut i32`, found integer | note: function defined here - --> $DIR/suggest-ref-macro.rs:11:4 + --> $DIR/suggest-ref-macro.rs:12:4 | LL | fn x(_: &mut i32) {} | ^ ----------- diff --git a/tests/ui/test-attrs/test-panic-while-printing.rs b/tests/ui/test-attrs/test-panic-while-printing.rs index a50a15fbe5af5..49e8d9c7afdf1 100644 --- a/tests/ui/test-attrs/test-panic-while-printing.rs +++ b/tests/ui/test-attrs/test-panic-while-printing.rs @@ -1,6 +1,7 @@ //@ compile-flags:--test //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc use std::fmt; use std::fmt::{Display, Formatter}; diff --git a/tests/ui/threads-sendsync/task-stderr.rs b/tests/ui/threads-sendsync/task-stderr.rs index 3934084e02a0a..f54b5a5cc5f68 100644 --- a/tests/ui/threads-sendsync/task-stderr.rs +++ b/tests/ui/threads-sendsync/task-stderr.rs @@ -1,6 +1,7 @@ //@ run-pass //@ needs-threads //@ needs-unwind +//@ ignore-backends: gcc #![feature(internal_output_capture)] diff --git a/tests/ui/threads-sendsync/unwind-resource.rs b/tests/ui/threads-sendsync/unwind-resource.rs index ec27a1846fef2..c5fbfc5bf5b03 100644 --- a/tests/ui/threads-sendsync/unwind-resource.rs +++ b/tests/ui/threads-sendsync/unwind-resource.rs @@ -3,6 +3,7 @@ #![allow(non_camel_case_types)] //@ needs-threads +//@ ignore-backends: gcc use std::sync::mpsc::{channel, Sender}; use std::thread; diff --git a/tests/ui/traits/clone-unwind-rc-cleanup.rs b/tests/ui/traits/clone-unwind-rc-cleanup.rs index cd02050ea2746..dab48a1b4c6cf 100644 --- a/tests/ui/traits/clone-unwind-rc-cleanup.rs +++ b/tests/ui/traits/clone-unwind-rc-cleanup.rs @@ -2,6 +2,7 @@ //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc #![allow(unused_variables)] #![allow(unused_imports)] diff --git a/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.rs b/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.rs index c08030fc5c150..81c2d778b057f 100644 --- a/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.rs +++ b/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.rs @@ -1,4 +1,5 @@ //@ proc-macro: derive-demo-issue-136343.rs +//@ ignore-backends: gcc #[macro_use] extern crate derive_demo_issue_136343; diff --git a/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.stderr b/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.stderr index 0b9c1d9123aab..d69eaae533551 100644 --- a/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.stderr +++ b/tests/ui/typeck/invalid-sugg-for-derive-macro-issue-136343.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/invalid-sugg-for-derive-macro-issue-136343.rs:6:10 + --> $DIR/invalid-sugg-for-derive-macro-issue-136343.rs:7:10 | LL | #[derive(Sample)] | ^^^^^^ From 1acd65cd6dffb34a2c8576ebc3874527a9f601e0 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 22 Sep 2025 14:09:03 +0200 Subject: [PATCH 0908/1900] predefined opaques use `List` --- compiler/rustc_middle/src/arena.rs | 1 - compiler/rustc_middle/src/traits/solve.rs | 45 +------------------ compiler/rustc_middle/src/ty/context.rs | 10 ++--- .../rustc_middle/src/ty/structural_impls.rs | 1 + .../src/canonical/mod.rs | 8 ++-- .../src/solve/eval_ctxt/mod.rs | 7 ++- compiler/rustc_type_ir/src/interner.rs | 8 ++-- compiler/rustc_type_ir/src/solve/mod.rs | 13 ------ 8 files changed, 16 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 52fbe19c9f2d7..fa6a2db38ef9c 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -109,7 +109,6 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, - [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index ef5223de0e825..3343f27033301 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -4,7 +4,7 @@ use rustc_type_ir as ir; pub use rustc_type_ir::solve::*; use crate::ty::{ - self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, try_visit, }; @@ -15,16 +15,7 @@ pub type CandidateSource<'tcx> = ir::solve::CandidateSource>; pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput, P>; pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse>; -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] -pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData>>); - -impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { - type Target = PredefinedOpaquesData>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} +pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] pub struct ExternalConstraints<'tcx>( @@ -93,35 +84,3 @@ impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { self.normalization_nested_goals.visit_with(visitor) } } - -// FIXME: Having to clone `region_constraints` for folding feels bad and -// probably isn't great wrt performance. -// -// Not sure how to fix this, maybe we should also intern `opaque_types` and -// `region_constraints` here or something. -impl<'tcx> TypeFoldable> for PredefinedOpaques<'tcx> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(FallibleTypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { - opaque_types: self - .opaque_types - .iter() - .map(|opaque| opaque.try_fold_with(folder)) - .collect::>()?, - })) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - TypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { - opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), - }) - } -} - -impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.opaque_types.visit_with(visitor) - } -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..aa34651464518 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -74,8 +74,7 @@ use crate::query::{IntoQueryParam, LocalCrate, Providers, TyCtxtAt}; use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ - self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, inspect, + self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -116,7 +115,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn mk_predefined_opaques_in_body( self, - data: PredefinedOpaquesData, + data: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], ) -> Self::PredefinedOpaques { self.mk_predefined_opaques_in_body(data) } @@ -941,7 +940,7 @@ pub struct CtxtInterners<'tcx> { layout: InternedSet<'tcx, LayoutData>, adt_def: InternedSet<'tcx, AdtDefData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData>>, - predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData>>, + predefined_opaques_in_body: InternedSet<'tcx, List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>>, fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, @@ -2748,8 +2747,6 @@ direct_interners! { adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, external_constraints: pub mk_external_constraints(ExternalConstraintsData>): ExternalConstraints -> ExternalConstraints<'tcx>, - predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData>): - PredefinedOpaques -> PredefinedOpaques<'tcx>, } macro_rules! slice_interners { @@ -2786,6 +2783,7 @@ slice_interners!( offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), patterns: pub mk_patterns(Pattern<'tcx>), outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>), + predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)), ); impl<'tcx> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 11d109b463d90..8dc4dfd677ba8 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -796,6 +796,7 @@ macro_rules! list_fold { list_fold! { &'tcx ty::List> : mk_poly_existential_predicates, + &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>: mk_predefined_opaques_in_body, &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index e3520e238ed37..b036ee6df7ecf 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -25,7 +25,7 @@ use crate::delegate::SolverDelegate; use crate::resolve::eager_resolve_vars; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, - NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, + NestedNormalizationGoals, QueryInput, Response, inspect, }; pub mod canonicalizer; @@ -53,7 +53,7 @@ impl ResponseT for inspect::State { pub(super) fn canonicalize_goal( delegate: &D, goal: Goal, - opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, + opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], ) -> (Vec, CanonicalInput) where D: SolverDelegate, @@ -65,9 +65,7 @@ where &mut orig_values, QueryInput { goal, - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), + predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types), }, ); let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index bb86357a85f8a..9b32e4876075d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -357,7 +357,7 @@ where f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal) -> R, ) -> R { let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input); - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + for (key, ty) in input.predefined_opaques_in_body.iter() { let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy()); // It may be possible that two entries in the opaque type storage end up // with the same key after resolving contained inference variables. @@ -467,7 +467,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -548,7 +548,6 @@ where .canonical .value .predefined_opaques_in_body - .opaque_types .len(), stalled_vars, sub_roots, @@ -1557,7 +1556,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, &opaque_types); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cf58aec14a680..886d1a78bcbfd 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,9 +11,7 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; -use crate::solve::{ - CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, inspect, -}; +use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect}; use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; @@ -70,10 +68,10 @@ pub trait Interner: + Hash + Eq + TypeFoldable - + Deref>; + + SliceLike, Self::Ty)>; fn mk_predefined_opaques_in_body( self, - data: PredefinedOpaquesData, + data: &[(ty::OpaqueTypeKey, Self::Ty)], ) -> Self::PredefinedOpaques; type LocalDefIds: Copy diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 1a1606d82685c..9d9a8e4984786 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -109,19 +109,6 @@ pub struct QueryInput { impl Eq for QueryInput {} -/// Opaques that are defined in the inference context before a query is called. -#[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub struct PredefinedOpaquesData { - pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, -} - -impl Eq for PredefinedOpaquesData {} - /// Possible ways the given goal can be proven. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] pub enum CandidateSource { From b70a15f5f62f17f1bea1a0260fe629143cf55316 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 22 Sep 2025 14:26:08 +0200 Subject: [PATCH 0909/1900] predefined opaques to `method_autoderef_steps` --- compiler/rustc_hir_typeck/src/method/probe.rs | 49 +++++++++++++++---- compiler/rustc_middle/src/query/mod.rs | 6 +-- compiler/rustc_middle/src/traits/query.rs | 12 ++++- compiler/rustc_middle/src/ty/context.rs | 11 ++++- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4185f7f6996c8..e0cd1f20080a6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -13,7 +13,7 @@ use rustc_hir::def::DefKind; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::traits::{ObligationCauseCode, query}; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; @@ -30,7 +30,7 @@ use rustc_span::edit_distance::{ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::traits::query::CanonicalMethodAutoderefStepsGoal; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, @@ -389,10 +389,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result>, { let mut orig_values = OriginalQueryValues::default(); - let query_input = self.canonicalize_query( - ParamEnvAnd { param_env: self.param_env, value: self_ty }, - &mut orig_values, - ); + let predefined_opaques_in_body = if self.next_trait_solver() { + self.tcx.mk_predefined_opaques_in_body_from_iter( + self.inner.borrow_mut().opaque_types().iter_opaque_types().map(|(k, v)| (k, v.ty)), + ) + } else { + ty::List::empty() + }; + let value = query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }; + let query_input = self + .canonicalize_query(ParamEnvAnd { param_env: self.param_env, value }, &mut orig_values); let steps = match mode { Mode::MethodCall => self.tcx.method_autoderef_steps(query_input), @@ -403,8 +409,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // special handling for this "trivial case" is a good idea. let infcx = &self.infcx; - let (ParamEnvAnd { param_env: _, value: self_ty }, var_values) = + let (ParamEnvAnd { param_env: _, value }, var_values) = infcx.instantiate_canonical(span, &query_input.canonical); + let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value; debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { @@ -553,12 +560,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn method_autoderef_steps<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalMethodAutoderefStepsGoal<'tcx>, ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let ParamEnvAnd { param_env, value: self_ty } = goal; + let ParamEnvAnd { + param_env, + value: query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }, + } = goal; + for (key, ty) in predefined_opaques_in_body { + let prev = + infcx.register_hidden_type_in_storage(key, ty::OpaqueHiddenType { span: DUMMY_SP, ty }); + // It may be possible that two entries in the opaque type storage end up + // with the same key after resolving contained inference variables. + // + // We could put them in the duplicate list but don't have to. The opaques we + // encounter here are already tracked in the caller, so there's no need to + // also store them here. We'd take them out when computing the query response + // and then discard them, as they're already present in the input. + // + // Ideally we'd drop duplicate opaque type definitions when computing + // the canonical input. This is more annoying to implement and may cause a + // perf regression, so we do it inside of the query for now. + if let Some(prev) = prev { + debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); + } + } // If arbitrary self types is not enabled, we follow the chain of // `Deref`. If arbitrary self types is enabled, we instead @@ -655,7 +683,8 @@ pub(crate) fn method_autoderef_steps<'tcx>( }; debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); - + // Need to empty the opaque types storage before it gets dropped. + let _ = infcx.take_opaque_types(); MethodAutoderefStepsResult { steps: tcx.arena.alloc_from_iter(steps), opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0e645a3aae45b..326df9239aa08 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -124,7 +124,7 @@ use crate::query::plumbing::{ }; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, - CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound, @@ -2559,9 +2559,9 @@ rustc_queries! { } query method_autoderef_steps( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalMethodAutoderefStepsGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{}`", goal.canonical.value.value } + desc { "computing autoderef types for `{}`", goal.canonical.value.value.self_ty } } /// Used by `-Znext-solver` to compute proof trees. diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 3f6faa1a572d9..c1b7c9b9b4e23 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -10,6 +10,7 @@ use rustc_span::Span; use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; +use crate::traits::solve; pub use crate::traits::solve::NoSolution; use crate::ty::{self, GenericArg, Ty, TyCtxt}; @@ -67,7 +68,16 @@ pub mod type_op { pub type CanonicalAliasGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; -pub type CanonicalTyGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; +pub type CanonicalMethodAutoderefStepsGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, MethodAutoderefSteps<'tcx>>>; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] +pub struct MethodAutoderefSteps<'tcx> { + /// The list of opaque types currently in the storage. + /// + /// Only used by the new solver for now. + pub predefined_opaques_in_body: solve::PredefinedOpaques<'tcx>, + pub self_ty: Ty<'tcx>, +} pub type CanonicalPredicateGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index aa34651464518..a42af7bb3e3f8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -74,7 +74,8 @@ use crate::query::{IntoQueryParam, LocalCrate, Providers, TyCtxtAt}; use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ - self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect, + self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, + QueryResult, inspect, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -3127,6 +3128,14 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_poly_existential_predicates(xs)) } + pub fn mk_predefined_opaques_in_body_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>), PredefinedOpaques<'tcx>>, + { + T::collect_and_apply(iter, |xs| self.mk_predefined_opaques_in_body(xs)) + } + pub fn mk_clauses_from_iter(self, iter: I) -> T::Output where I: Iterator, From 70f6493f261810b07d69ab0ffb661cf4ed8a97a7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 15:25:35 +0200 Subject: [PATCH 0910/1900] remove unnecessary structurally resolve `autoderef` already resolved and `method_autoderef_steps` makes sure we won't encounter an inference variable --- compiler/rustc_hir_typeck/src/method/confirm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a23910a2006c9..b23e7ae8e77bc 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); - let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { + let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { return Ty::new_error_with_message( self.tcx, DUMMY_SP, @@ -182,8 +182,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); - let mut target = self.structurally_resolve_type(autoderef.span(), ty); - match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { let region = self.next_region_var(RegionVariableOrigin::Autoref(self.span)); From 6b379b560df483307958f2a606a1dd514fff36ca Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 15:32:25 +0200 Subject: [PATCH 0911/1900] use `try_structurally_resolve_type` for method receiver We'll still error due to the `opt_bad_ty` of `method_autoderef_steps`. This slightly worsens the span of `infer_var.method()` which is now the same as for `Box::new(infer_var).method()`. Unlike `structurally_resolve_type`, `probe_op` does not check whether the infcx is already tainted, so this results in 2 previously not emitted errors. --- compiler/rustc_hir_typeck/src/expr.rs | 3 +-- compiler/rustc_hir_typeck/src/method/probe.rs | 2 ++ .../obligation-with-leaking-placeholders.next.stderr | 2 +- .../ui/impl-trait/call_method_ambiguous.next.stderr | 2 +- .../call_method_on_inherent_impl.next.stderr | 2 +- .../call_method_on_inherent_impl_ref.next.stderr | 2 +- .../hidden-type-is-opaque-2.default.stderr | 4 ++-- .../impl-trait/hidden-type-is-opaque-2.next.stderr | 4 ++-- tests/ui/impl-trait/method-resolution4.next.stderr | 4 ++-- tests/ui/impl-trait/recursive-bound-eval.next.stderr | 4 ++-- .../incompat-call-after-qualified-path-0.stderr | 2 +- .../incompat-call-after-qualified-path-1.stderr | 2 +- tests/ui/issues/issue-2151.stderr | 2 +- tests/ui/lazy-type-alias-impl-trait/branches3.stderr | 8 ++++---- tests/ui/proc-macro/quote/not-repeatable.rs | 4 +++- tests/ui/proc-macro/quote/not-repeatable.stderr | 11 +++++++++-- .../copy-inference-side-effects-are-lazy.stderr | 4 ++-- .../ui/span/issue-42234-unknown-receiver-type.stderr | 4 +++- .../closures_in_branches.stderr | 4 ++-- ...d_resolution_trait_method_from_opaque.next.stderr | 4 ++-- .../ui/type-inference/regression-issue-81317.stderr | 2 +- tests/ui/typeck/issue-13853.rs | 2 +- tests/ui/typeck/issue-13853.stderr | 12 +++++++++--- 23 files changed, 54 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7adbee7ff2855..d1ce0afddf91c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1633,8 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { let rcvr_t = self.check_expr(rcvr); - // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t); + let rcvr_t = self.try_structurally_resolve_type(rcvr.span, rcvr_t); match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) { Ok(method) => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e0cd1f20080a6..3311c14105549 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -486,6 +486,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(_)) => { let raw_ptr_call = bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types(); + // FIXME: Ideally we'd use the span of the self-expr here, + // not of the method path. let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, span, diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr index 3d667f12371ab..4bb9047b3035d 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr @@ -5,7 +5,7 @@ LL | needs_foo(|x| { | ^ ... LL | x.to_string(); - | - type must be known at this point + | --------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/call_method_ambiguous.next.stderr b/tests/ui/impl-trait/call_method_ambiguous.next.stderr index 5251555f57421..0def594daf1cc 100644 --- a/tests/ui/impl-trait/call_method_ambiguous.next.stderr +++ b/tests/ui/impl-trait/call_method_ambiguous.next.stderr @@ -5,7 +5,7 @@ LL | let mut iter = foo(n - 1, m); | ^^^^^^^^ LL | LL | assert_eq!(iter.get(), 1); - | ---- type must be known at this point + | --- type must be known at this point | help: consider giving `iter` an explicit type | diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr index 271051f120abc..7bbf5f5153a59 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr @@ -5,7 +5,7 @@ LL | let x = my_foo(); | ^ LL | LL | x.my_debug(); - | - type must be known at this point + | -------- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr index 7202cb6f90a6f..5dea3a715e9bc 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr @@ -5,7 +5,7 @@ LL | let x = my_foo(); | ^ LL | LL | x.my_debug(); - | - type must be known at this point + | -------- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr index dca0a7b0a1a9f..cb383b2db3893 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr index dca0a7b0a1a9f..cb383b2db3893 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/method-resolution4.next.stderr b/tests/ui/impl-trait/method-resolution4.next.stderr index 0524f49f98e58..47a9aac19ecc3 100644 --- a/tests/ui/impl-trait/method-resolution4.next.stderr +++ b/tests/ui/impl-trait/method-resolution4.next.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/method-resolution4.rs:13:9 + --> $DIR/method-resolution4.rs:13:20 | LL | foo(false).next().unwrap(); - | ^^^^^^^^^^ cannot infer type + | ^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/recursive-bound-eval.next.stderr b/tests/ui/impl-trait/recursive-bound-eval.next.stderr index 4bab290d71c3c..e94f0a59a1d22 100644 --- a/tests/ui/impl-trait/recursive-bound-eval.next.stderr +++ b/tests/ui/impl-trait/recursive-bound-eval.next.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/recursive-bound-eval.rs:20:13 + --> $DIR/recursive-bound-eval.rs:20:28 | LL | move || recursive_fn().parse() - | ^^^^^^^^^^^^^^ cannot infer type + | ^^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr index 10056bdf3d4f4..ba1c81c4518a7 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-0.rs:21:6 | LL | f(|a, b| a.cmp(b)); - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr index 632a9b99f84ef..93bba3625b540 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-1.rs:25:6 | LL | f(|a, b| a.cmp(b)); - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/issues/issue-2151.stderr b/tests/ui/issues/issue-2151.stderr index b130f162414d0..59fef42eb5e8b 100644 --- a/tests/ui/issues/issue-2151.stderr +++ b/tests/ui/issues/issue-2151.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x = panic!(); | ^ LL | x.clone(); - | - type must be known at this point + | ----- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index 117d189867bd7..539673bc343ce 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:9:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:18:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -24,7 +24,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:26:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -35,7 +35,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:33:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs index 0291e4ddf88d6..373f0e74dbdac 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.rs +++ b/tests/ui/proc-macro/quote/not-repeatable.rs @@ -8,5 +8,7 @@ struct Ipv4Addr; fn main() { let ip = Ipv4Addr; - let _ = quote! { $($ip)* }; //~ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied + let _ = quote! { $($ip)* }; + //~^ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied + //~| ERROR type annotations needed } diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index aeda08d7de68b..ff31799abb007 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -20,6 +20,13 @@ note: the traits `Iterator` and `ToTokens` must be implemented --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 1 previous error +error[E0282]: type annotations needed + --> $DIR/not-repeatable.rs:11:13 + | +LL | let _ = quote! { $($ip)* }; + | ^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0282, E0599. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr index ba44beb76dbb7..b8a8f927542f0 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -5,9 +5,9 @@ LL | let x = [Foo(PhantomData); 2]; | ^ LL | LL | extract(x).max(2); - | ---------- type must be known at this point + | --- type must be known at this point | -help: consider giving `x` an explicit type, where the type for type parameter `T` is specified +help: consider giving `x` an explicit type, where the placeholders `_` are specified | LL | let x: [Foo; 2] = [Foo(PhantomData); 2]; | +++++++++++++ diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.stderr index 10308ec07da5a..71ac4f53b3f4f 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ---------- type must be known at this point + | ------------------------------------ type must be known at this point | help: consider specifying the generic argument | @@ -16,6 +16,8 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` +LL | .to_string() + | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 849ffd214f07d..559bc57d90638 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr index bbdd3923821ff..5aae334ccbd2f 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/method_resolution_trait_method_from_opaque.rs:28:9 + --> $DIR/method_resolution_trait_method_from_opaque.rs:28:18 | LL | self.bar.next().unwrap(); - | ^^^^^^^^ cannot infer type + | ^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/type-inference/regression-issue-81317.stderr b/tests/ui/type-inference/regression-issue-81317.stderr index fcd3fca06e18b..a070b50e31175 100644 --- a/tests/ui/type-inference/regression-issue-81317.stderr +++ b/tests/ui/type-inference/regression-issue-81317.stderr @@ -5,7 +5,7 @@ LL | let iv = S ^ index.into(); | ^^ LL | LL | &iv.to_bytes_be(); - | -- type must be known at this point + | ----------- type must be known at this point | help: consider giving `iv` an explicit type | diff --git a/tests/ui/typeck/issue-13853.rs b/tests/ui/typeck/issue-13853.rs index ac9886d2e7249..ed44d5062614f 100644 --- a/tests/ui/typeck/issue-13853.rs +++ b/tests/ui/typeck/issue-13853.rs @@ -25,7 +25,7 @@ impl Node for Stuff { fn iterate>(graph: &G) { for node in graph.iter() { //~ ERROR no method named `iter` found - node.zomg(); + node.zomg(); //~ ERROR type annotations needed } } diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 45363c87d29df..9b8698d6ed2c0 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -17,6 +17,12 @@ error[E0599]: no method named `iter` found for reference `&G` in the current sco LL | for node in graph.iter() { | ^^^^ method not found in `&G` +error[E0282]: type annotations needed + --> $DIR/issue-13853.rs:28:14 + | +LL | node.zomg(); + | ^^^^ cannot infer type + error[E0308]: mismatched types --> $DIR/issue-13853.rs:37:13 | @@ -37,7 +43,7 @@ help: consider borrowing here LL | iterate(&graph); | + -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0599. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0282, E0308, E0599. +For more information about an error, try `rustc --explain E0282`. From 148fd9ad3c434c26a952e01e37c35aa26cb8315c Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Sep 2025 17:33:24 +0200 Subject: [PATCH 0912/1900] allow method calls on opaques --- compiler/rustc_hir_typeck/src/method/probe.rs | 209 +++++++++++++++--- .../src/infer/canonical/query_response.rs | 23 +- compiler/rustc_middle/src/traits/query.rs | 1 + .../src/solve/eval_ctxt/mod.rs | 3 +- .../rustc_next_trait_solver/src/solve/mod.rs | 1 + .../src/traits/query/evaluate_obligation.rs | 11 +- .../call_method_ambiguous.next.stderr | 17 -- tests/ui/impl-trait/call_method_ambiguous.rs | 3 +- .../call_method_on_inherent_impl.next.stderr | 17 -- .../call_method_on_inherent_impl.rs | 3 +- ...inherent_impl_on_rigid_type.current.stderr | 2 +- ...on_inherent_impl_on_rigid_type.next.stderr | 18 +- ...l_method_on_inherent_impl_on_rigid_type.rs | 3 +- ...d_on_inherent_impl_ref-err.current.stderr} | 4 +- ...ethod_on_inherent_impl_ref-err.next.stderr | 19 ++ .../call_method_on_inherent_impl_ref-err.rs | 24 ++ ...=> call_method_on_inherent_impl_ref-ok.rs} | 12 +- ...ll_method_on_inherent_impl_ref.next.stderr | 31 --- .../impl-trait/method-resolution4.next.stderr | 9 - tests/ui/impl-trait/method-resolution4.rs | 3 +- ...olution5-deref-no-constrain.current.stderr | 19 ++ .../method-resolution5-deref-no-constrain.rs | 23 ++ .../ui/impl-trait/method-resolution5-deref.rs | 30 +++ .../recursive-bound-eval.next.stderr | 9 - tests/ui/impl-trait/recursive-bound-eval.rs | 6 +- .../recursive-coroutine-boxed.next.stderr | 17 -- .../impl-trait/recursive-coroutine-boxed.rs | 3 +- ...ution_trait_method_from_opaque.next.stderr | 8 +- 28 files changed, 357 insertions(+), 171 deletions(-) delete mode 100644 tests/ui/impl-trait/call_method_ambiguous.next.stderr delete mode 100644 tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr rename tests/ui/impl-trait/{call_method_on_inherent_impl_ref.current.stderr => call_method_on_inherent_impl_ref-err.current.stderr} (84%) create mode 100644 tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr create mode 100644 tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.rs rename tests/ui/impl-trait/{call_method_on_inherent_impl_ref.rs => call_method_on_inherent_impl_ref-ok.rs} (54%) delete mode 100644 tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr delete mode 100644 tests/ui/impl-trait/method-resolution4.next.stderr create mode 100644 tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr create mode 100644 tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs create mode 100644 tests/ui/impl-trait/method-resolution5-deref.rs delete mode 100644 tests/ui/impl-trait/recursive-bound-eval.next.stderr delete mode 100644 tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3311c14105549..e34ee6258b85b 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -13,7 +13,7 @@ use rustc_hir::def::DefKind; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::{ObligationCauseCode, query}; +use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; @@ -30,6 +30,7 @@ use rustc_span::edit_distance::{ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::solve::Goal; use rustc_trait_selection::traits::query::CanonicalMethodAutoderefStepsGoal; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -417,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { self_ty: self .make_query_response_ignoring_pending_obligations(var_values, self_ty), + self_ty_is_opaque: false, autoderefs: 0, from_unsafe_deref: false, unsize: false, @@ -590,6 +592,17 @@ pub(crate) fn method_autoderef_steps<'tcx>( } } + // We accept not-yet-defined opaque types in the autoderef + // chain to support recursive calls. We do error if the final + // infer var is not an opaque. + let self_ty_is_opaque = |ty: Ty<'_>| { + if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { + infcx.has_opaques_with_sub_unified_hidden_type(vid) + } else { + false + } + }; + // If arbitrary self types is not enabled, we follow the chain of // `Deref`. If arbitrary self types is enabled, we instead // follow the chain of `Receiver`, but we also record whether @@ -623,6 +636,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( let step = CandidateStep { self_ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -643,6 +657,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( let step = CandidateStep { self_ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -659,7 +674,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( }; let final_ty = autoderef_via_deref.final_ty(); let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), }), @@ -670,6 +689,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), ), + self_ty_is_opaque: false, autoderefs, // this could be from an unsafe deref if we had // a *mut/const [T; N] @@ -1234,7 +1254,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { !step.self_ty.value.references_error() && !step.from_unsafe_deref }) .find_map(|step| { - let InferOk { value: self_ty, obligations: _ } = self + let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self .fcx .probe_instantiate_query_response( self.span, @@ -1245,7 +1265,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints); + let by_value_pick = self.pick_by_value_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ); // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing) if let Some(by_value_pick) = by_value_pick { @@ -1256,6 +1281,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { by_value_pick, step, self_ty, + &instantiate_self_ty_obligations, mutbl, track_unstable_candidates, ) { @@ -1270,6 +1296,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let autoref_pick = self.pick_autorefd_method( step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Not, pick_diag_hints, None, @@ -1283,6 +1310,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoref_pick, step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Mut, track_unstable_candidates, ) { @@ -1319,12 +1347,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.pick_autorefd_method( step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Mut, pick_diag_hints, None, ) - .or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints)) - .or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints)) + .or_else(|| { + self.pick_const_ptr_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ) + }) + .or_else(|| { + self.pick_reborrow_pin_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ) + }) }) } @@ -1348,6 +1391,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { possible_shadower: &Pick<'tcx>, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], mutbl: hir::Mutability, track_unstable_candidates: bool, ) -> Result<(), MethodError<'tcx>> { @@ -1412,6 +1456,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let potentially_shadowed_pick = self.pick_autorefd_method( step, self_ty, + instantiate_self_ty_obligations, mutbl, &mut pick_diag_hints, Some(&pick_constraints), @@ -1438,13 +1483,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option> { if step.unsize { return None; } - self.pick_method(self_ty, pick_diag_hints, None).map(|r| { + self.pick_method(self_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1481,6 +1527,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], mutbl: hir::Mutability, pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, @@ -1497,7 +1544,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = tcx.lifetimes.re_erased; let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); - self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| { + self.pick_method( + autoref_ty, + instantiate_self_ty_obligations, + pick_diag_hints, + pick_constraints, + ) + .map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1513,6 +1566,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option> { if !self.tcx.features().pin_ergonomics() { @@ -1534,14 +1588,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = self.tcx.lifetimes.re_erased; let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not); - self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = - Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not)); - pick - }) - }) + self.pick_method(autopin_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map( + |r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = + Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not)); + pick + }) + }, + ) } /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a @@ -1551,6 +1607,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option> { // Don't convert an unsized reference to ptr @@ -1563,18 +1620,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); - self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); - pick - }) - }) + self.pick_method(const_ptr_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map( + |r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); + pick + }) + }, + ) } fn pick_method( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option> { @@ -1584,8 +1644,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] { debug!("searching {} candidates", kind); - let res = - self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints); + let res = self.consider_candidates( + self_ty, + instantiate_self_ty_obligations, + candidates, + pick_diag_hints, + pick_constraints, + ); if let Some(pick) = res { return Some(pick); } @@ -1594,6 +1659,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if self.private_candidate.get().is_none() { if let Some(Ok(pick)) = self.consider_candidates( self_ty, + instantiate_self_ty_obligations, &self.private_candidates, &mut PickDiagHints { unstable_candidates: None, @@ -1610,6 +1676,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn consider_candidates( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], candidates: &[Candidate<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, @@ -1626,6 +1693,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { probe, self.consider_probe( self_ty, + instantiate_self_ty_obligations, probe, &mut pick_diag_hints.unsatisfied_predicates, ), @@ -1810,10 +1878,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)] + #[instrument(level = "debug", skip(self, possibly_unsatisfied_predicates), ret)] fn consider_probe( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], probe: &Candidate<'tcx>, possibly_unsatisfied_predicates: &mut Vec<( ty::Predicate<'tcx>, @@ -1821,8 +1890,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Option>, )>, ) -> ProbeResult { - debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); - self.probe(|snapshot| { let outer_universe = self.universe(); @@ -1830,6 +1897,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let cause = &self.misc(self.span); let ocx = ObligationCtxt::new_with_diagnostics(self); + // Subtle: we're not *really* instantiating the current self type while + // probing, but instead fully recompute the autoderef steps once we've got + // a final `Pick`. We can't nicely handle these obligations outside of a probe. + // + // We simply handle them for each candidate here for now. That's kinda scuffed + // and ideally we just put them into the `FnCtxt` right away. We need to consider + // them to deal with defining uses in `method_autoderef_steps`. + if self.next_trait_solver() { + ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned()); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + unreachable!("unexpected autoderef error {errors:?}"); + } + } + let mut trait_predicate = None; let (mut xform_self_ty, mut xform_ret_ty); @@ -2072,6 +2154,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + if self.infcx.next_trait_solver() { + if self.should_reject_candidate_due_to_opaque_treated_as_rigid(trait_predicate) { + result = ProbeResult::NoMatch; + } + } + // Previously, method probe used `evaluate_predicate` to determine if a predicate // was impossible to satisfy. This did a leak check, so we must also do a leak // check here to prevent backwards-incompatible ambiguity being introduced. See @@ -2085,6 +2173,71 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } + /// Trait candidates for not-yet-defined opaque types are a somewhat hacky. + /// + /// We want to only accept trait methods if they were hold even if the + /// opaque types were rigid. To handle this, we both check that for trait + /// candidates the goal were to hold even when treating opaques as rigid, + /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank). + /// + /// We also check that all opaque types encountered as self types in the + /// autoderef chain don't get constrained when applying the candidate. + /// Importantly, this also handles calling methods taking `&self` on + /// `impl Trait` to reject the "by-self" candidate. + /// + /// This needs to happen at the end of `consider_probe` as we need to take + /// all the constraints from that into account. + #[instrument(level = "debug", skip(self), ret)] + fn should_reject_candidate_due_to_opaque_treated_as_rigid( + &self, + trait_predicate: Option>, + ) -> bool { + // Check whether the trait candidate would not be applicable if the + // opaque type were rigid. + if let Some(predicate) = trait_predicate { + let goal = Goal { param_env: self.param_env, predicate }; + if !self.infcx.goal_may_hold_opaque_types_jank(goal) { + return true; + } + } + + // Check whether any opaque types in the autoderef chain have been + // constrained. + for step in self.steps { + if step.self_ty_is_opaque { + debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque"); + let constrained_opaque = self.probe(|_| { + // If we fail to instantiate the self type of this + // step, this part of the deref-chain is no longer + // reachable. In this case we don't care about opaque + // types there. + let Ok(ok) = self.fcx.probe_instantiate_query_response( + self.span, + self.orig_steps_var_values, + &step.self_ty, + ) else { + debug!("failed to instantiate self_ty"); + return false; + }; + let ocx = ObligationCtxt::new(self); + let self_ty = ocx.register_infer_ok_obligations(ok); + if !ocx.select_where_possible().is_empty() { + debug!("failed to prove instantiate self_ty obligations"); + return false; + } + + !self.resolve_vars_if_possible(self_ty).is_ty_var() + }); + if constrained_opaque { + debug!("opaque type has been constrained"); + return true; + } + } + } + + false + } + /// Sometimes we get in a situation where we have multiple probes that are all impls of the /// same trait, but we don't know which impl to use. In this case, since in all cases the /// external interface of the method can be determined from the trait, it's ok not to decide. diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 5d1b4be9e57bb..a20cd31113ab1 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -85,11 +85,32 @@ impl<'tcx> InferCtxt<'tcx> { where T: Debug + TypeFoldable>, { + // While we ignore region constraints and pending obligations, + // we do return constrained opaque types to avoid unconstrained + // inference variables in the response. This is still slightly + // insufficient as ambiguous `Projection` obligations have the + // same issue. + // + // FIXME(-Znext-solver): We could alternatively extend the `var_values` + // each time we call `make_query_response_ignoring_pending_obligations` + // and equate inference variables created inside of the query this way. + // This is what we do for `CanonicalState` and is probably a bit nicer. + let opaque_types = if self.next_trait_solver() { + self.inner + .borrow_mut() + .opaque_type_storage + .iter_opaque_types() + .map(|(k, v)| (k, v.ty)) + .collect() + } else { + vec![] + }; + self.canonicalize_response(QueryResponse { var_values: inference_vars, region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Proven, // Ambiguities are OK! - opaque_types: vec![], + opaque_types, value: answer, }) } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index c1b7c9b9b4e23..c5cd7c54e4e86 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -154,6 +154,7 @@ impl<'tcx> FromIterator> for DropckConstraint<'tcx> { #[derive(Debug, HashStable)] pub struct CandidateStep<'tcx> { pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + pub self_ty_is_opaque: bool, pub autoderefs: usize, /// `true` if the type results from a dereference of a raw pointer. /// when assembling candidates, we include these steps, but not when diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 9b32e4876075d..85110530ae9bc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -194,7 +194,7 @@ where D: SolverDelegate, I: Interner, { - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn evaluate_root_goal( &self, goal: Goal, @@ -206,6 +206,7 @@ where }) } + #[instrument(level = "debug", skip(self), ret)] fn root_goal_may_hold_opaque_types_jank( &self, goal: Goal::Predicate>, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index afb86aaf8ab21..a58caeecc33c5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -381,6 +381,7 @@ where } /// The result of evaluating a goal. +#[derive_where(Debug; I: Interner)] pub struct GoalEvaluation { /// The goal we've evaluated. This is the input goal, but potentially with its /// inference variables resolved. This never applies any inference constraints 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 ae731505abfa5..34e0176d213f1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,6 +1,6 @@ use rustc_infer::traits::solve::Goal; use rustc_macros::extension; -use rustc_middle::span_bug; +use rustc_middle::{span_bug, ty}; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use crate::infer::InferCtxt; @@ -22,7 +22,7 @@ impl<'tcx> InferCtxt<'tcx> { /// for more details. fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool { if self.next_trait_solver() { - <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new( + self.goal_may_hold_opaque_types_jank(Goal::new( self.tcx, obligation.param_env, obligation.predicate, @@ -32,6 +32,13 @@ impl<'tcx> InferCtxt<'tcx> { } } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn goal_may_hold_opaque_types_jank(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> bool { + assert!(self.next_trait_solver()); + <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(goal) + } + /// Evaluates whether the predicate can be satisfied in the given /// `ParamEnv`, and returns `false` if not certain. However, this is /// not entirely accurate if inference variables are involved. diff --git a/tests/ui/impl-trait/call_method_ambiguous.next.stderr b/tests/ui/impl-trait/call_method_ambiguous.next.stderr deleted file mode 100644 index 0def594daf1cc..0000000000000 --- a/tests/ui/impl-trait/call_method_ambiguous.next.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/call_method_ambiguous.rs:26:13 - | -LL | let mut iter = foo(n - 1, m); - | ^^^^^^^^ -LL | -LL | assert_eq!(iter.get(), 1); - | --- type must be known at this point - | -help: consider giving `iter` an explicit type - | -LL | let mut iter: /* Type */ = foo(n - 1, m); - | ++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_ambiguous.rs b/tests/ui/impl-trait/call_method_ambiguous.rs index 6bcafc8ce1494..021d6c22f79dd 100644 --- a/tests/ui/impl-trait/call_method_ambiguous.rs +++ b/tests/ui/impl-trait/call_method_ambiguous.rs @@ -1,6 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[current] run-pass +//@ run-pass trait Get { fn get(&mut self) -> u32; @@ -24,7 +24,6 @@ where fn foo(n: usize, m: &mut ()) -> impl Get + use<'_> { if n > 0 { let mut iter = foo(n - 1, m); - //[next]~^ ERROR type annotations needed assert_eq!(iter.get(), 1); } m diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr deleted file mode 100644 index 7bbf5f5153a59..0000000000000 --- a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/call_method_on_inherent_impl.rs:18:13 - | -LL | let x = my_foo(); - | ^ -LL | -LL | x.my_debug(); - | -------- type must be known at this point - | -help: consider giving `x` an explicit type - | -LL | let x: /* Type */ = my_foo(); - | ++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.rs b/tests/ui/impl-trait/call_method_on_inherent_impl.rs index 0e333c3260a2f..1dd38bc671730 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl.rs +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.rs @@ -1,6 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[current] check-pass +//@ check-pass trait MyDebug { fn my_debug(&self); @@ -16,7 +16,6 @@ where fn my_foo() -> impl std::fmt::Debug { if false { let x = my_foo(); - //[next]~^ ERROR type annotations needed x.my_debug(); } () diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr index 6ecb2b05fc56b..e710466447043 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `my_debug` found for reference `&impl Debug` in the current scope - --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:16:11 + --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:15:11 | LL | x.my_debug(); | ^^^^^^^^ method not found in `&impl Debug` diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr index 5fb0b8f1d14b2..de808259d40dc 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr @@ -1,17 +1,15 @@ -error[E0282]: type annotations needed for `&_` - --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:14:13 +error[E0599]: no method named `my_debug` found for reference `&_` in the current scope + --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:15:11 | -LL | let x = &my_foo(); - | ^ -LL | LL | x.my_debug(); - | -------- type must be known at this point + | ^^^^^^^^ method not found in `&_` | -help: consider giving `x` an explicit type, where the placeholders `_` are specified + = help: items from traits can only be used if the trait is implemented and in scope +help: trait `MyDebug` which provides `my_debug` is implemented but not in scope; perhaps you want to import it + | +LL + use MyDebug; | -LL | let x: &_ = &my_foo(); - | ++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs index 7fb2ff3b2bcc6..0c9909efa1ba1 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs @@ -12,9 +12,8 @@ impl MyDebug for &() { fn my_foo() -> impl std::fmt::Debug { if false { let x = &my_foo(); - //[next]~^ ERROR: type annotations needed x.my_debug(); - //[current]~^ ERROR: no method named `my_debug` + //~^ ERROR: no method named `my_debug` } () } diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr similarity index 84% rename from tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr rename to tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr index fb51bb7b4173b..71acbd1497d93 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `my_debug` found for opaque type `impl Debug` in the current scope - --> $DIR/call_method_on_inherent_impl_ref.rs:19:11 + --> $DIR/call_method_on_inherent_impl_ref-err.rs:18:11 | LL | fn my_debug(&self); | -------- the method is available for `&impl Debug` here @@ -9,7 +9,7 @@ LL | x.my_debug(); | = help: items from traits can only be used if the trait is implemented and in scope note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it - --> $DIR/call_method_on_inherent_impl_ref.rs:4:1 + --> $DIR/call_method_on_inherent_impl_ref-err.rs:4:1 | LL | trait MyDebug { | ^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr new file mode 100644 index 0000000000000..523505e980221 --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr @@ -0,0 +1,19 @@ +error[E0599]: no method named `my_debug` found for type `_` in the current scope + --> $DIR/call_method_on_inherent_impl_ref-err.rs:18:11 + | +LL | fn my_debug(&self); + | -------- the method is available for `&_` here +... +LL | x.my_debug(); + | ^^^^^^^^ method not found in `_` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it + --> $DIR/call_method_on_inherent_impl_ref-err.rs:4:1 + | +LL | trait MyDebug { + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.rs new file mode 100644 index 0000000000000..0ed09bc76a41e --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.rs @@ -0,0 +1,24 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait MyDebug { + fn my_debug(&self); +} + +impl MyDebug for &T +where + T: std::fmt::Debug, +{ + fn my_debug(&self) {} +} + +fn my_foo() -> impl std::fmt::Debug { + if false { + let x = my_foo(); + x.my_debug(); + //~^ ERROR no method named `my_debug` found + } + () +} + +fn main() {} diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-ok.rs similarity index 54% rename from tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs rename to tests/ui/impl-trait/call_method_on_inherent_impl_ref-ok.rs index 4e4098b37f93b..40739d6a0ce4b 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-ok.rs @@ -1,5 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver +//@ check-pass trait MyDebug { fn my_debug(&self); @@ -12,20 +13,9 @@ where fn my_debug(&self) {} } -fn my_foo() -> impl std::fmt::Debug { - if false { - let x = my_foo(); - //[next]~^ ERROR type annotations needed - x.my_debug(); - //[current]~^ ERROR no method named `my_debug` found - } - () -} - fn my_bar() -> impl std::fmt::Debug { if false { let x = &my_bar(); - //[next]~^ ERROR type annotations needed x.my_debug(); } () diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr deleted file mode 100644 index 5dea3a715e9bc..0000000000000 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/call_method_on_inherent_impl_ref.rs:17:13 - | -LL | let x = my_foo(); - | ^ -LL | -LL | x.my_debug(); - | -------- type must be known at this point - | -help: consider giving `x` an explicit type - | -LL | let x: /* Type */ = my_foo(); - | ++++++++++++ - -error[E0282]: type annotations needed for `&_` - --> $DIR/call_method_on_inherent_impl_ref.rs:27:13 - | -LL | let x = &my_bar(); - | ^ -LL | -LL | x.my_debug(); - | -------- type must be known at this point - | -help: consider giving `x` an explicit type, where the placeholders `_` are specified - | -LL | let x: &_ = &my_bar(); - | ++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/method-resolution4.next.stderr b/tests/ui/impl-trait/method-resolution4.next.stderr deleted file mode 100644 index 47a9aac19ecc3..0000000000000 --- a/tests/ui/impl-trait/method-resolution4.next.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/method-resolution4.rs:13:20 - | -LL | foo(false).next().unwrap(); - | ^^^^ cannot infer type - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/method-resolution4.rs b/tests/ui/impl-trait/method-resolution4.rs index 90e7850cad51e..f90a9309cdab8 100644 --- a/tests/ui/impl-trait/method-resolution4.rs +++ b/tests/ui/impl-trait/method-resolution4.rs @@ -6,12 +6,11 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[current] check-pass +//@ check-pass fn foo(b: bool) -> impl Iterator { if b { foo(false).next().unwrap(); - //[next]~^ ERROR type annotations needed } std::iter::empty() } diff --git a/tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr b/tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr new file mode 100644 index 0000000000000..08578de426ade --- /dev/null +++ b/tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/method-resolution5-deref-no-constrain.rs:20:5 + | +LL | fn via_deref() -> impl Deref { + | --- expected `&Foo` because of return type +... +LL | Box::new(Foo) + | ^^^^^^^^^^^^^ expected `&Foo`, found `Box` + | + = note: expected reference `&Foo` + found struct `Box` +help: consider borrowing here + | +LL | &Box::new(Foo) + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs b/tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs new file mode 100644 index 0000000000000..2c41f62b9fda5 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs @@ -0,0 +1,23 @@ +//! The recursive method call yields the opaque type. We want +//! to use the impl candidate for `Foo` here without constraining +//! the opaque to `&Foo`. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +use std::ops::Deref; +struct Foo; +impl Foo { + fn method(&self) {} +} +fn via_deref() -> impl Deref { + // Currently errors on stable, but should not + if false { + via_deref().method(); + } + + Box::new(Foo) + //[current]~^ ERROR mismatched types +} +fn main() {} diff --git a/tests/ui/impl-trait/method-resolution5-deref.rs b/tests/ui/impl-trait/method-resolution5-deref.rs new file mode 100644 index 0000000000000..6133a8efe244d --- /dev/null +++ b/tests/ui/impl-trait/method-resolution5-deref.rs @@ -0,0 +1,30 @@ +//! The recursive method call yields the opaque type. We want +//! to use the trait candidate for `impl Foo` here while not +//! applying it for the `impl Deref`. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +use std::ops::Deref; +trait Foo { + fn method(&self) {} +} +impl Foo for u32 {} +fn via_deref() -> impl Deref { + if false { + via_deref().method(); + } + + Box::new(1u32) +} + +fn via_deref_nested() -> Box> { + if false { + via_deref_nested().method(); + } + + Box::new(Box::new(1u32)) +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-bound-eval.next.stderr b/tests/ui/impl-trait/recursive-bound-eval.next.stderr deleted file mode 100644 index e94f0a59a1d22..0000000000000 --- a/tests/ui/impl-trait/recursive-bound-eval.next.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/recursive-bound-eval.rs:20:28 - | -LL | move || recursive_fn().parse() - | ^^^^^ cannot infer type - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/recursive-bound-eval.rs b/tests/ui/impl-trait/recursive-bound-eval.rs index 7859c8983fc89..058b12e5651e3 100644 --- a/tests/ui/impl-trait/recursive-bound-eval.rs +++ b/tests/ui/impl-trait/recursive-bound-eval.rs @@ -1,10 +1,9 @@ //! Test that we can evaluate nested obligations when invoking methods on recursive calls on //! an RPIT. -//@revisions: next current +//@ revisions: next current //@[next] compile-flags: -Znext-solver - -//@[current] check-pass +//@ check-pass pub trait Parser { fn parse(&self) -> E; @@ -18,7 +17,6 @@ impl E> Parser for T { pub fn recursive_fn() -> impl Parser { move || recursive_fn().parse() - //[next]~^ ERROR: type annotations needed } fn main() {} diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr deleted file mode 100644 index 5ce6eb0fc3989..0000000000000 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/recursive-coroutine-boxed.rs:11:23 - | -LL | let mut gen = Box::pin(foo()); - | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box` -LL | -LL | let mut r = gen.as_mut().resume(()); - | ------ type must be known at this point - | -help: consider specifying the generic argument - | -LL | let mut gen = Box::::pin(foo()); - | +++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs index 306edc3591e98..932023d103dc4 100644 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.rs +++ b/tests/ui/impl-trait/recursive-coroutine-boxed.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] check-pass //@[next] compile-flags: -Znext-solver +//@ check-pass #![feature(coroutines, coroutine_trait)] use std::ops::{Coroutine, CoroutineState}; @@ -9,7 +9,6 @@ use std::ops::{Coroutine, CoroutineState}; fn foo() -> impl Coroutine { #[coroutine] || { let mut gen = Box::pin(foo()); - //[next]~^ ERROR type annotations needed let mut r = gen.as_mut().resume(()); while let CoroutineState::Yielded(v) = r { yield v; diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr index 5aae334ccbd2f..37bde4b18a45b 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -2,7 +2,13 @@ error[E0282]: type annotations needed --> $DIR/method_resolution_trait_method_from_opaque.rs:28:18 | LL | self.bar.next().unwrap(); - | ^^^^ cannot infer type + | ^^^^ + | +help: try using a fully qualified path to specify the expected types + | +LL - self.bar.next().unwrap(); +LL + <_ as Iterator>::next(&mut self.bar).unwrap(); + | error: aborting due to 1 previous error From eebf871feaca10886f80a76a36b3771e960c047e Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 10:08:00 +0200 Subject: [PATCH 0913/1900] move tests --- tests/ui/impl-trait/{ => method}/method-resolution.rs | 0 tests/ui/impl-trait/{ => method}/method-resolution2.next.stderr | 0 tests/ui/impl-trait/{ => method}/method-resolution2.rs | 0 .../ui/impl-trait/{ => method}/method-resolution3.current.stderr | 0 tests/ui/impl-trait/{ => method}/method-resolution3.next.stderr | 0 tests/ui/impl-trait/{ => method}/method-resolution3.rs | 0 tests/ui/impl-trait/{ => method}/method-resolution4.rs | 0 .../method-resolution5-deref-no-constrain.current.stderr | 0 .../{ => method}/method-resolution5-deref-no-constrain.rs | 0 tests/ui/impl-trait/{ => method}/method-resolution5-deref.rs | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/impl-trait/{ => method}/method-resolution.rs (100%) rename tests/ui/impl-trait/{ => method}/method-resolution2.next.stderr (100%) rename tests/ui/impl-trait/{ => method}/method-resolution2.rs (100%) rename tests/ui/impl-trait/{ => method}/method-resolution3.current.stderr (100%) rename tests/ui/impl-trait/{ => method}/method-resolution3.next.stderr (100%) rename tests/ui/impl-trait/{ => method}/method-resolution3.rs (100%) rename tests/ui/impl-trait/{ => method}/method-resolution4.rs (100%) rename tests/ui/impl-trait/{ => method}/method-resolution5-deref-no-constrain.current.stderr (100%) rename tests/ui/impl-trait/{ => method}/method-resolution5-deref-no-constrain.rs (100%) rename tests/ui/impl-trait/{ => method}/method-resolution5-deref.rs (100%) diff --git a/tests/ui/impl-trait/method-resolution.rs b/tests/ui/impl-trait/method/method-resolution.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution.rs rename to tests/ui/impl-trait/method/method-resolution.rs diff --git a/tests/ui/impl-trait/method-resolution2.next.stderr b/tests/ui/impl-trait/method/method-resolution2.next.stderr similarity index 100% rename from tests/ui/impl-trait/method-resolution2.next.stderr rename to tests/ui/impl-trait/method/method-resolution2.next.stderr diff --git a/tests/ui/impl-trait/method-resolution2.rs b/tests/ui/impl-trait/method/method-resolution2.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution2.rs rename to tests/ui/impl-trait/method/method-resolution2.rs diff --git a/tests/ui/impl-trait/method-resolution3.current.stderr b/tests/ui/impl-trait/method/method-resolution3.current.stderr similarity index 100% rename from tests/ui/impl-trait/method-resolution3.current.stderr rename to tests/ui/impl-trait/method/method-resolution3.current.stderr diff --git a/tests/ui/impl-trait/method-resolution3.next.stderr b/tests/ui/impl-trait/method/method-resolution3.next.stderr similarity index 100% rename from tests/ui/impl-trait/method-resolution3.next.stderr rename to tests/ui/impl-trait/method/method-resolution3.next.stderr diff --git a/tests/ui/impl-trait/method-resolution3.rs b/tests/ui/impl-trait/method/method-resolution3.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution3.rs rename to tests/ui/impl-trait/method/method-resolution3.rs diff --git a/tests/ui/impl-trait/method-resolution4.rs b/tests/ui/impl-trait/method/method-resolution4.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution4.rs rename to tests/ui/impl-trait/method/method-resolution4.rs diff --git a/tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr b/tests/ui/impl-trait/method/method-resolution5-deref-no-constrain.current.stderr similarity index 100% rename from tests/ui/impl-trait/method-resolution5-deref-no-constrain.current.stderr rename to tests/ui/impl-trait/method/method-resolution5-deref-no-constrain.current.stderr diff --git a/tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs b/tests/ui/impl-trait/method/method-resolution5-deref-no-constrain.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution5-deref-no-constrain.rs rename to tests/ui/impl-trait/method/method-resolution5-deref-no-constrain.rs diff --git a/tests/ui/impl-trait/method-resolution5-deref.rs b/tests/ui/impl-trait/method/method-resolution5-deref.rs similarity index 100% rename from tests/ui/impl-trait/method-resolution5-deref.rs rename to tests/ui/impl-trait/method/method-resolution5-deref.rs From d6fe5334185385372d1fe8f1c9df8f5d3e7be788 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Sep 2025 10:28:52 +0200 Subject: [PATCH 0914/1900] add tests --- .../method/broken-deref-chain.current.stderr | 19 ++++++++ .../impl-trait/method/broken-deref-chain.rs | 47 +++++++++++++++++++ .../would-constrain-opaque.current.stderr | 29 ++++++++++++ .../method/would-constrain-opaque.next.stderr | 27 +++++++++++ .../method/would-constrain-opaque.rs | 39 +++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 tests/ui/impl-trait/method/broken-deref-chain.current.stderr create mode 100644 tests/ui/impl-trait/method/broken-deref-chain.rs create mode 100644 tests/ui/impl-trait/method/would-constrain-opaque.current.stderr create mode 100644 tests/ui/impl-trait/method/would-constrain-opaque.next.stderr create mode 100644 tests/ui/impl-trait/method/would-constrain-opaque.rs diff --git a/tests/ui/impl-trait/method/broken-deref-chain.current.stderr b/tests/ui/impl-trait/method/broken-deref-chain.current.stderr new file mode 100644 index 0000000000000..726f076b183bd --- /dev/null +++ b/tests/ui/impl-trait/method/broken-deref-chain.current.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/broken-deref-chain.rs:41:30 + | +LL | fn trait_method() -> impl Trait { + | ---------- the found opaque type +... +LL | x.trait_method(); + | - here the type of `x` is inferred to be `Foo` +LL | let _: Foo = x; // Test that we did not apply the deref step + | ----------- ^ expected `Foo`, found `Foo` + | | + | expected due to this + | + = note: expected struct `Foo` + found struct `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/method/broken-deref-chain.rs b/tests/ui/impl-trait/method/broken-deref-chain.rs new file mode 100644 index 0000000000000..8b45e044f4332 --- /dev/null +++ b/tests/ui/impl-trait/method/broken-deref-chain.rs @@ -0,0 +1,47 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// An annoying edge case of method selection. While computing the deref-chain +// constrains `T` to `u32`, the final method candidate does not and instead +// constrains to `i32`. In this case, we no longer check that the opaque +// remains unconstrained. Both method calls in this test constrain the opaque +// to `i32`. +use std::ops::Deref; + +struct Foo(T, U); +impl Deref for Foo { + type Target = U; + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +impl Foo { + fn method(&self) {} +} +fn inherent_method() -> impl Sized { + if false { + let x = Foo(Default::default(), inherent_method()); + x.method(); + let _: Foo = x; // Test that we did not apply the deref step + } + 1i32 +} + +trait Trait { + fn trait_method(&self) {} +} +impl Trait for Foo {} +impl Trait for i32 {} +fn trait_method() -> impl Trait { + if false { + let x = Foo(Default::default(), trait_method()); + x.trait_method(); + let _: Foo = x; // Test that we did not apply the deref step + //[current]~^ ERROR mismatched types + } + 1i32 +} + +fn main() {} diff --git a/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr new file mode 100644 index 0000000000000..60533a39c536f --- /dev/null +++ b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr @@ -0,0 +1,29 @@ +error[E0599]: no method named `method` found for reference `&impl Sized` in the current scope + --> $DIR/would-constrain-opaque.rs:28:11 + | +LL | x.method(); + | ^^^^^^ method not found in `&impl Sized` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Trait` defines an item `method`, perhaps you need to implement it + --> $DIR/would-constrain-opaque.rs:15:1 + | +LL | trait Trait: Sized { + | ^^^^^^^^^^^^^^^^^^ + +error[E0599]: no method named `method` found for reference `&impl Sized` in the current scope + --> $DIR/would-constrain-opaque.rs:30:11 + | +LL | x.method(); + | ^^^^^^ method not found in `&impl Sized` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Trait` defines an item `method`, perhaps you need to implement it + --> $DIR/would-constrain-opaque.rs:15:1 + | +LL | trait Trait: Sized { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/method/would-constrain-opaque.next.stderr b/tests/ui/impl-trait/method/would-constrain-opaque.next.stderr new file mode 100644 index 0000000000000..23a4ceb826a6e --- /dev/null +++ b/tests/ui/impl-trait/method/would-constrain-opaque.next.stderr @@ -0,0 +1,27 @@ +error[E0599]: no method named `method` found for reference `&_` in the current scope + --> $DIR/would-constrain-opaque.rs:28:11 + | +LL | x.method(); + | ^^^^^^ method not found in `&_` + | + = help: items from traits can only be used if the trait is implemented and in scope +help: trait `Trait` which provides `method` is implemented but not in scope; perhaps you want to import it + | +LL + use Trait; + | + +error[E0599]: no method named `method` found for reference `&_` in the current scope + --> $DIR/would-constrain-opaque.rs:30:11 + | +LL | x.method(); + | ^^^^^^ method not found in `&_` + | + = help: items from traits can only be used if the trait is implemented and in scope +help: trait `Trait` which provides `method` is implemented but not in scope; perhaps you want to import it + | +LL + use Trait; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/method/would-constrain-opaque.rs b/tests/ui/impl-trait/method/would-constrain-opaque.rs new file mode 100644 index 0000000000000..8dd322825296b --- /dev/null +++ b/tests/ui/impl-trait/method/would-constrain-opaque.rs @@ -0,0 +1,39 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// If we don't treat `impl Sized` as rigid, the first call would +// resolve to the trait method, constraining the opaque, while the +// second call would resolve to the inherent method. +// +// We avoid cases like this by rejecting candidates which constrain +// opaque types encountered in the autoderef chain. +// +// FIXME(-Znext-solver): ideally we would note that the inference variable +// is an opaque type in the error message and change this to a type annotations +// needed error. + +trait Trait: Sized { + fn method(self) {} +} +impl Trait for &Foo {} + +struct Foo; +impl Foo { + fn method(&self) {} +} + +fn define_opaque(b: bool) -> impl Sized { + if b { + let x = &define_opaque(false); + x.method(); + //~^ ERROR no method named `method` found for reference + x.method(); + //~^ ERROR no method named `method` found for reference + } + + Foo +} + +fn main() { + define_opaque(true); +} From c2e39c2f20c568b96fe89c751e65bbbe9116231c Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 26 Sep 2025 16:37:03 +0200 Subject: [PATCH 0915/1900] review --- compiler/rustc_hir_typeck/src/method/probe.rs | 9 +++++++++ .../rustc_infer/src/infer/canonical/query_response.rs | 11 ++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e34ee6258b85b..12f80a197b1b8 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2192,6 +2192,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, trait_predicate: Option>, ) -> bool { + // This function is what hacky and doesn't perfectly do what we want it to. + // It's not soundness critical and we should be able to freely improve this + // in the future. + // + // Some concrete edge cases include the fact that `goal_may_hold_opaque_types_jank` + // also fails if there are any constraints opaques which are never used as a self + // type. We also allow where-bounds which are currently ambiguous but end up + // constraining an opaque later on. + // Check whether the trait candidate would not be applicable if the // opaque type were rigid. if let Some(predicate) = trait_predicate { diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index a20cd31113ab1..b3959113d5dc9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -87,14 +87,11 @@ impl<'tcx> InferCtxt<'tcx> { { // While we ignore region constraints and pending obligations, // we do return constrained opaque types to avoid unconstrained - // inference variables in the response. This is still slightly - // insufficient as ambiguous `Projection` obligations have the - // same issue. + // inference variables in the response. This is important as we want + // to check that opaques in deref steps stay unconstrained. // - // FIXME(-Znext-solver): We could alternatively extend the `var_values` - // each time we call `make_query_response_ignoring_pending_obligations` - // and equate inference variables created inside of the query this way. - // This is what we do for `CanonicalState` and is probably a bit nicer. + // This doesn't handle the more general case for non-opaques as + // ambiguous `Projection` obligations have same the issue. let opaque_types = if self.next_trait_solver() { self.inner .borrow_mut() From 07bb89f98d485a888796fabb322af06d222a5cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 26 Sep 2025 17:48:16 +0200 Subject: [PATCH 0916/1900] Update memchr to 2.7.6 memchr 2.7.6 contains a bugfix for aarch64_be --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 9d0f63d2bcecb..086f38f06a52d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1334,9 +1334,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" From c0e0d4b68d38a92c19f42d3003074b5b6e7b65c8 Mon Sep 17 00:00:00 2001 From: Li-yao Xia Date: Fri, 26 Sep 2025 18:14:31 +0200 Subject: [PATCH 0917/1900] Make `def_path_hash_to_def_id` not panic when passed an invalid hash --- compiler/rustc_metadata/src/rmeta/decoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs | 9 +++++---- compiler/rustc_middle/src/hooks/mod.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 0c8d1f32e991e..b895feb906247 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1555,7 +1555,7 @@ impl<'a> CrateMetadataRef<'a> { } #[inline] - fn def_path_hash_to_def_index(self, hash: DefPathHash) -> DefIndex { + fn def_path_hash_to_def_index(self, hash: DefPathHash) -> Option { self.def_path_hash_map.def_path_hash_to_def_index(&hash) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 11fef3be5d09b..df3add316ec23 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -691,8 +691,8 @@ fn provide_cstore_hooks(providers: &mut Providers) { .get(&stable_crate_id) .unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}")); assert_ne!(cnum, LOCAL_CRATE); - let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash); - DefId { krate: cnum, index: def_index } + let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash)?; + Some(DefId { krate: cnum, index: def_index }) }; providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index f3917b55782b0..a17b3e1047d09 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -12,11 +12,12 @@ pub(crate) enum DefPathHashMapRef<'tcx> { impl DefPathHashMapRef<'_> { #[inline] - pub(crate) fn def_path_hash_to_def_index(&self, def_path_hash: &DefPathHash) -> DefIndex { + pub(crate) fn def_path_hash_to_def_index( + &self, + def_path_hash: &DefPathHash, + ) -> Option { match *self { - DefPathHashMapRef::OwnedFromMetadata(ref map) => { - map.get(&def_path_hash.local_hash()).unwrap() - } + DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()), DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") } diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 9d2f0a4523713..dc6a3334a4c8b 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -77,7 +77,7 @@ declare_hooks! { /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. /// Will fetch a DefId from a DefPathHash for a foreign crate. - hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; + hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> Option; /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..a961e04cf79c0 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2012,7 +2012,7 @@ impl<'tcx> TyCtxt<'tcx> { if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { Some(self.untracked.definitions.read().local_def_path_hash_to_def_id(hash)?.to_def_id()) } else { - Some(self.def_path_hash_to_def_id_extern(hash, stable_crate_id)) + self.def_path_hash_to_def_id_extern(hash, stable_crate_id) } } From 4c7292aba3c7b61708e49a5ec0061d5d901affad Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 26 Sep 2025 13:27:34 -0400 Subject: [PATCH 0918/1900] PassWrapper: drop unused variable for LLVM 22+ --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 136b6e9bf50fd..b7637b29bca53 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -569,7 +569,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( } std::optional PGOOpt; +#if LLVM_VERSION_LE(22, 0) auto FS = vfs::getRealFileSystem(); +#endif if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); PGOOpt = PGOOptions( From 99456cc015114874319744a07afc1ba94a32315a Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 26 Sep 2025 13:32:03 -0400 Subject: [PATCH 0919/1900] tests: use max-llvm-major-version instead of ignore-llvm-version --- .../codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs | 2 +- tests/codegen-llvm/vec_pop_push_noop.rs | 2 +- tests/codegen-llvm/vecdeque_pop_push.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs b/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs index 631468d8c217a..a0b453fac8e93 100644 --- a/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs +++ b/tests/codegen-llvm/issues/issue-122600-ptr-discriminant-update.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Copt-level=3 //@ revisions: new old -//@ [old] ignore-llvm-version: 22 - 99 +//@ [old] max-llvm-major-version: 21 //@ [new] min-llvm-version: 22 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/vec_pop_push_noop.rs b/tests/codegen-llvm/vec_pop_push_noop.rs index fadc6e99dc7a6..977c220b3baef 100644 --- a/tests/codegen-llvm/vec_pop_push_noop.rs +++ b/tests/codegen-llvm/vec_pop_push_noop.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Copt-level=3 //@ revisions: new old -//@ [old] ignore-llvm-version: 22 - 99 +//@ [old] max-llvm-major-version: 21 //@ [new] min-llvm-version: 22 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/vecdeque_pop_push.rs b/tests/codegen-llvm/vecdeque_pop_push.rs index 7886db3fedeba..6f9ad6674d6cd 100644 --- a/tests/codegen-llvm/vecdeque_pop_push.rs +++ b/tests/codegen-llvm/vecdeque_pop_push.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Copt-level=3 //@ revisions: new old -//@ [old] ignore-llvm-version: 22 - 99 +//@ [old] max-llvm-major-version: 21 //@ [new] min-llvm-version: 22 #![crate_type = "lib"] From 77c6acc74ecd44ba7eb5a73c934dffdff7340133 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Thu, 6 Mar 2025 22:10:42 -0500 Subject: [PATCH 0920/1900] debuginfo: add an unstable flag to write split DWARF to an explicit directory Bazel requires knowledge of outputs from actions at analysis time, including file or directory name. In order to work around the lack of predictable output name for dwo files, we group the dwo files in a subdirectory of --out-dir as a post-processing step before returning control to bazel. Unfortunately some debugging workflows rely on directly opening the dwo file rather than loading the merged dwp file, and our trick of moving the files breaks those users. We can't just hardlink the file or copy it, because with remote build execution we wouldn't end up with the un-moved file copied back to the developer's workstation. As a fix, we add this unstable flag that causes dwo files to be written to a build-system-controllable location, which then lets bazel hoover up the dwo files, but the objects also have the correct path for the dwo files. --- compiler/rustc_interface/src/util.rs | 2 ++ compiler/rustc_session/src/config.rs | 12 +++++++++++- compiler/rustc_session/src/options.rs | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 76ccd12797e5e..58ec72b5b45b5 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -542,6 +542,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu stem, None, sess.io.temps_dir.clone(), + sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) @@ -571,6 +572,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu out_filestem, ofile, sess.io.temps_dir.clone(), + sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ebb6a93b1dd18..93be50f0a26cb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1193,6 +1193,7 @@ pub struct OutputFilenames { filestem: String, pub single_output_file: Option, temps_directory: Option, + explicit_dwo_out_directory: Option, pub outputs: OutputTypes, } @@ -1225,6 +1226,7 @@ impl OutputFilenames { out_filestem: String, single_output_file: Option, temps_directory: Option, + explicit_dwo_out_directory: Option, extra: String, outputs: OutputTypes, ) -> Self { @@ -1232,6 +1234,7 @@ impl OutputFilenames { out_directory, single_output_file, temps_directory, + explicit_dwo_out_directory, outputs, crate_stem: format!("{out_crate_name}{extra}"), filestem: format!("{out_filestem}{extra}"), @@ -1281,7 +1284,14 @@ impl OutputFilenames { codegen_unit_name: &str, invocation_temp: Option<&str>, ) -> PathBuf { - self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp) + let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); + if let Some(dwo_out) = &self.explicit_dwo_out_directory { + let mut o = dwo_out.clone(); + o.push(p.file_name().unwrap()); + o + } else { + p + } } /// Like `temp_path`, but also supports things where there is no corresponding diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b2cc169f12cb7..9287e032dc4c9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2633,6 +2633,8 @@ written to standard error output)"), file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker"), + split_dwarf_out_dir : Option = (None, parse_opt_pathbuf, [TRACKED], + "location for writing split DWARF objects (`.dwo`) if enabled"), split_lto_unit: Option = (None, parse_opt_bool, [TRACKED], "enable LTO unit splitting (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], From 2e904c4d078967abff213637a45386e2676c3757 Mon Sep 17 00:00:00 2001 From: Joshua Rayton Date: Fri, 26 Sep 2025 18:17:31 +0100 Subject: [PATCH 0921/1900] update issue number for more_float_constants --- library/core/src/num/f128.rs | 12 ++++++------ library/core/src/num/f16.rs | 12 ++++++------ library/core/src/num/f32.rs | 12 ++++++------ library/core/src/num/f64.rs | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 73ca3fbb142c8..4fe4735e304c9 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -33,12 +33,12 @@ pub mod consts { /// The golden ratio (φ) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const PHI: f128 = 1.61803398874989484820458683436563811772030917980576286213545_f128; /// The Euler-Mascheroni constant (γ) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const EGAMMA: f128 = 0.577215664901532860606512090082402431042159335939923598805767_f128; /// π/2 @@ -67,14 +67,14 @@ pub mod consts { /// 1/sqrt(π) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_PI: f128 = 0.564189583547756286948079451560772585844050629328998856844086_f128; /// 1/sqrt(2π) #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_2PI: f128 = 0.398942280401432677939946059934381868475858631164934657665926_f128; @@ -98,12 +98,12 @@ pub mod consts { /// sqrt(3) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const SQRT_3: f128 = 1.73205080756887729352744634150587236694280525381038062805581_f128; /// 1/sqrt(3) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f128 = 0.577350269189625764509148780501957455647601751270126876018602_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index a9dbade0e6599..0bea6bc8801d8 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -35,12 +35,12 @@ pub mod consts { /// The golden ratio (φ) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const PHI: f16 = 1.618033988749894848204586834365638118_f16; /// The Euler-Mascheroni constant (γ) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const EGAMMA: f16 = 0.577215664901532860606512090082402431_f16; /// π/2 @@ -69,13 +69,13 @@ pub mod consts { /// 1/sqrt(π) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_PI: f16 = 0.564189583547756286948079451560772586_f16; /// 1/sqrt(2π) #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_2PI: f16 = 0.398942280401432677939946059934381868_f16; /// 2/π @@ -96,12 +96,12 @@ pub mod consts { /// sqrt(3) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const SQRT_3: f16 = 1.732050807568877293527446341505872367_f16; /// 1/sqrt(3) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f16 = 0.577350269189625764509148780501957456_f16; /// Euler's number (e) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 53474cd3e9058..e380cc698f574 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -291,11 +291,11 @@ pub mod consts { pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; /// The golden ratio (φ) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const PHI: f32 = 1.618033988749894848204586834365638118_f32; /// The Euler-Mascheroni constant (γ) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const EGAMMA: f32 = 0.577215664901532860606512090082402431_f32; /// π/2 @@ -323,12 +323,12 @@ pub mod consts { pub const FRAC_1_PI: f32 = 0.318309886183790671537767526745028724_f32; /// 1/sqrt(π) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32; /// 1/sqrt(2π) #[doc(alias = "FRAC_1_SQRT_TAU")] - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_2PI: f32 = 0.398942280401432677939946059934381868_f32; /// 2/π @@ -348,11 +348,11 @@ pub mod consts { pub const FRAC_1_SQRT_2: f32 = 0.707106781186547524400844362104849039_f32; /// sqrt(3) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const SQRT_3: f32 = 1.732050807568877293527446341505872367_f32; /// 1/sqrt(3) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32; /// Euler's number (e) diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 78113a60bbc28..ff7449fd996ce 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -291,11 +291,11 @@ pub mod consts { pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; /// The golden ratio (φ) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const PHI: f64 = 1.618033988749894848204586834365638118_f64; /// The Euler-Mascheroni constant (γ) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const EGAMMA: f64 = 0.577215664901532860606512090082402431_f64; /// π/2 @@ -323,12 +323,12 @@ pub mod consts { pub const FRAC_1_PI: f64 = 0.318309886183790671537767526745028724_f64; /// 1/sqrt(π) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64; /// 1/sqrt(2π) #[doc(alias = "FRAC_1_SQRT_TAU")] - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_2PI: f64 = 0.398942280401432677939946059934381868_f64; /// 2/π @@ -348,11 +348,11 @@ pub mod consts { pub const FRAC_1_SQRT_2: f64 = 0.707106781186547524400844362104849039_f64; /// sqrt(3) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const SQRT_3: f64 = 1.732050807568877293527446341505872367_f64; /// 1/sqrt(3) - #[unstable(feature = "more_float_constants", issue = "103883")] + #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64; /// Euler's number (e) From c0de794949f652b368fa107c3b52d0515f1f3859 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 29 Apr 2025 19:01:08 +0100 Subject: [PATCH 0922/1900] std::net: update tcp deferaccept delay type to Duration. --- library/std/src/os/net/linux_ext/tcp.rs | 23 +++++++++++-------- library/std/src/os/net/linux_ext/tests.rs | 11 +++++---- .../std/src/sys/net/connection/socket/unix.rs | 9 ++++---- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index fde53ec4257bd..dbefc91a979a5 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -4,6 +4,7 @@ use crate::sealed::Sealed; use crate::sys_common::AsInner; +use crate::time::Duration; use crate::{io, net}; /// Os-specific extensions for [`TcpStream`] @@ -59,11 +60,13 @@ pub trait TcpStreamExt: Sealed { /// A socket listener will be awakened solely when data arrives. /// - /// The `accept` argument set the delay in seconds until the + /// The `accept` argument set the maximum delay until the /// data is available to read, reducing the number of short lived /// connections without data to process. /// Contrary to other platforms `SO_ACCEPTFILTER` feature equivalent, there is /// no necessity to set it after the `listen` call. + /// Note that the delay is expressed as Duration from user's perspective + /// the call rounds it down to the nearest second expressible as a `c_int`. /// /// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) /// @@ -73,16 +76,17 @@ pub trait TcpStreamExt: Sealed { /// #![feature(tcp_deferaccept)] /// use std::net::TcpStream; /// use std::os::linux::net::TcpStreamExt; + /// use std::time::Duration; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); + /// stream.set_deferaccept(Duration::from_secs(1u64)).expect("set_deferaccept call failed"); /// ``` #[unstable(feature = "tcp_deferaccept", issue = "119639")] #[cfg(target_os = "linux")] - fn set_deferaccept(&self, accept: u32) -> io::Result<()>; + fn set_deferaccept(&self, accept: Duration) -> io::Result<()>; - /// Gets the accept delay value (in seconds) of the `TCP_DEFER_ACCEPT` option. + /// Gets the accept delay value of the `TCP_DEFER_ACCEPT` option. /// /// For more information about this option, see [`TcpStreamExt::set_deferaccept`]. /// @@ -92,15 +96,16 @@ pub trait TcpStreamExt: Sealed { /// #![feature(tcp_deferaccept)] /// use std::net::TcpStream; /// use std::os::linux::net::TcpStreamExt; + /// use std::time::Duration; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); - /// assert_eq!(stream.deferaccept().unwrap_or(0), 1); + /// stream.set_deferaccept(Duration::from_secs(1u64)).expect("set_deferaccept call failed"); + /// assert_eq!(stream.deferaccept().unwrap(), Duration::from_secs(1u64)); /// ``` #[unstable(feature = "tcp_deferaccept", issue = "119639")] #[cfg(target_os = "linux")] - fn deferaccept(&self) -> io::Result; + fn deferaccept(&self) -> io::Result; } #[stable(feature = "tcp_quickack", since = "1.89.0")] @@ -117,12 +122,12 @@ impl TcpStreamExt for net::TcpStream { } #[cfg(target_os = "linux")] - fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { self.as_inner().as_inner().set_deferaccept(accept) } #[cfg(target_os = "linux")] - fn deferaccept(&self) -> io::Result { + fn deferaccept(&self) -> io::Result { self.as_inner().as_inner().deferaccept() } } diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 12f35696abc5c..0758b426ccc59 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -32,6 +32,7 @@ fn deferaccept() { use crate::net::test::next_test_ip4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; + use crate::time::Duration; macro_rules! t { ($e:expr) => { @@ -43,10 +44,12 @@ fn deferaccept() { } let addr = next_test_ip4(); + let one = Duration::from_secs(1u64); + let zero = Duration::from_secs(0u64); let _listener = t!(TcpListener::bind(&addr)); let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - stream.set_deferaccept(1).expect("set_deferaccept failed"); - assert_eq!(stream.deferaccept().unwrap(), 1); - stream.set_deferaccept(0).expect("set_deferaccept failed"); - assert_eq!(stream.deferaccept().unwrap(), 0); + stream.set_deferaccept(one).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), one); + stream.set_deferaccept(zero).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), zero); } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 8b5970d1494e5..86b966242bf38 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -485,14 +485,15 @@ impl Socket { // bionic libc makes no use of this flag #[cfg(target_os = "linux")] - pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) + pub fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { + let val = cmp::min(accept.as_secs(), c_int::MAX as u64) as c_int; + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) } #[cfg(target_os = "linux")] - pub fn deferaccept(&self) -> io::Result { + pub fn deferaccept(&self) -> io::Result { let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; - Ok(raw as u32) + Ok(Duration::from_secs(raw as _)) } #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] From a4e87e940620bc0e61caa7c42b1edc53c0aee7cb Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 10 Sep 2025 17:11:47 -0400 Subject: [PATCH 0923/1900] Support `#[rustc_align_static]` inside `thread_local!` --- library/std/src/sys/thread_local/mod.rs | 2 +- .../std/src/sys/thread_local/native/eager.rs | 4 +- .../std/src/sys/thread_local/native/lazy.rs | 4 +- .../std/src/sys/thread_local/native/mod.rs | 12 +- .../std/src/sys/thread_local/no_threads.rs | 70 +++-- library/std/src/sys/thread_local/os.rs | 127 +++++++-- library/std/src/thread/local.rs | 229 +++++++++++++-- library/std/src/thread/mod.rs | 1 + library/std/tests/thread.rs | 2 + src/tools/miri/tests/pass/static_align.rs | 60 ++++ .../feature-gate-static_align-thread_local.rs | 11 + tests/ui/static/static-align.rs | 84 +++++- tests/ui/thread-local/long-docs.rs | 266 ++++++++++++++++++ tests/ui/thread-local/no-unstable.rs | 17 ++ tests/ui/thread-local/no-unstable.stderr | 57 ++++ 15 files changed, 872 insertions(+), 74 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-static_align-thread_local.rs create mode 100644 tests/ui/thread-local/long-docs.rs create mode 100644 tests/ui/thread-local/no-unstable.rs create mode 100644 tests/ui/thread-local/no-unstable.stderr diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index cff74857c4733..970022233cfaf 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -41,7 +41,7 @@ cfg_select! { } _ => { mod os; - pub use os::{Storage, thread_local_inner}; + pub use os::{Storage, thread_local_inner, value_align}; pub(crate) use os::{LocalPointer, local_pointer}; } } diff --git a/library/std/src/sys/thread_local/native/eager.rs b/library/std/src/sys/thread_local/native/eager.rs index fd48c4f720216..23abad645c1a3 100644 --- a/library/std/src/sys/thread_local/native/eager.rs +++ b/library/std/src/sys/thread_local/native/eager.rs @@ -10,9 +10,11 @@ enum State { } #[allow(missing_debug_implementations)] +#[repr(C)] pub struct Storage { - state: Cell, + // This field must be first, for correctness of `#[rustc_align_static]` val: UnsafeCell, + state: Cell, } impl Storage { diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs index b556dd9aa25ed..02939a74fc089 100644 --- a/library/std/src/sys/thread_local/native/lazy.rs +++ b/library/std/src/sys/thread_local/native/lazy.rs @@ -27,9 +27,11 @@ enum State { } #[allow(missing_debug_implementations)] +#[repr(C)] pub struct Storage { - state: Cell>, + // This field must be first, for correctness of `#[rustc_align_static]` value: UnsafeCell>, + state: Cell>, } impl Storage diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index a5dffe3c45883..9544721b923b8 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -54,7 +54,7 @@ pub macro thread_local_inner { // test in `tests/thread.rs` if these types are renamed. // Used to generate the `LocalKey` value for const-initialized thread locals. - (@key $t:ty, const $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ const __INIT: $t = $init; unsafe { @@ -62,6 +62,7 @@ pub macro thread_local_inner { if $crate::mem::needs_drop::<$t>() { |_| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::EagerStorage<$t> = $crate::thread::local_impl::EagerStorage::new(__INIT); VAL.get() @@ -69,6 +70,7 @@ pub macro thread_local_inner { } else { |_| { #[thread_local] + $(#[$align_attr])* static VAL: $t = __INIT; &VAL } @@ -78,7 +80,7 @@ pub macro thread_local_inner { }}, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] fn __init() -> $t { $init @@ -89,6 +91,7 @@ pub macro thread_local_inner { if $crate::mem::needs_drop::<$t>() { |init| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) @@ -96,6 +99,7 @@ pub macro thread_local_inner { } else { |init| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::LazyStorage<$t, !> = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) @@ -104,9 +108,9 @@ pub macro thread_local_inner { }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*); }, } diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 4da01a84acf68..4d6a4464cfa8c 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -2,6 +2,7 @@ //! thread locals and we can instead just use plain statics! use crate::cell::{Cell, UnsafeCell}; +use crate::mem::MaybeUninit; use crate::ptr; #[doc(hidden)] @@ -11,12 +12,13 @@ use crate::ptr; #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ const __INIT: $t = $init; // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|_| { + $(#[$align_attr])* static VAL: $crate::thread::local_impl::EagerStorage<$t> = $crate::thread::local_impl::EagerStorage { value: __INIT }; &VAL.value @@ -25,27 +27,27 @@ pub macro thread_local_inner { }}, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] fn __init() -> $t { $init } unsafe { - use $crate::thread::LocalKey; - use $crate::thread::local_impl::LazyStorage; - - LocalKey::new(|init| { - static VAL: LazyStorage<$t> = LazyStorage::new(); + $crate::thread::LocalKey::new(|init| { + $(#[$align_attr])* + static VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); VAL.get(init, __init) }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*); }, } #[allow(missing_debug_implementations)] +#[repr(transparent)] // Required for correctness of `#[rustc_align_static]` pub struct EagerStorage { pub value: T, } @@ -53,14 +55,27 @@ pub struct EagerStorage { // SAFETY: the target doesn't have threads. unsafe impl Sync for EagerStorage {} +#[derive(Clone, Copy, PartialEq, Eq)] +enum State { + Initial, + Alive, + Destroying, +} + #[allow(missing_debug_implementations)] +#[repr(C)] pub struct LazyStorage { - value: UnsafeCell>, + // This field must be first, for correctness of `#[rustc_align_static]` + value: UnsafeCell>, + state: Cell, } impl LazyStorage { pub const fn new() -> LazyStorage { - LazyStorage { value: UnsafeCell::new(None) } + LazyStorage { + value: UnsafeCell::new(MaybeUninit::uninit()), + state: Cell::new(State::Initial), + } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -70,24 +85,39 @@ impl LazyStorage { /// has occurred. #[inline] pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { - let value = unsafe { &*self.value.get() }; - match value { - Some(v) => v, - None => self.initialize(i, f), + if self.state.get() == State::Alive { + self.value.get() as *const T + } else { + self.initialize(i, f) } } #[cold] fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let value = i.and_then(Option::take).unwrap_or_else(f); - // Destroy the old value, after updating the TLS variable as the - // destructor might reference it. + + // Destroy the old value if it is initialized // FIXME(#110897): maybe panic on recursive initialization. + if self.state.get() == State::Alive { + self.state.set(State::Destroying); + // Safety: we check for no initialization during drop below + unsafe { + ptr::drop_in_place(self.value.get() as *mut T); + } + self.state.set(State::Initial); + } + + // Guard against initialization during drop + if self.state.get() == State::Destroying { + panic!("Attempted to initialize thread-local while it is being dropped"); + } + unsafe { - self.value.get().replace(Some(value)); + self.value.get().write(MaybeUninit::new(value)); } - // SAFETY: we just set this to `Some`. - unsafe { (*self.value.get()).as_ref().unwrap_unchecked() } + self.state.set(State::Alive); + + self.value.get() as *const T } } diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index fe6af27db3a17..77746b203a321 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,5 +1,6 @@ use super::key::{Key, LazyKey, get, set}; use super::{abort_on_dtor_unwind, guard}; +use crate::alloc::Layout; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; @@ -10,17 +11,12 @@ use crate::ptr; #[unstable(feature = "thread_local_internals", issue = "none")] #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => { - $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) - }, - // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user // provided type or type alias with a matching name. Please update the shadowing test in // `tests/thread.rs` if these types are renamed. // used to generate the `LocalKey` value for `thread_local!`. - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{ #[inline] fn __init() -> $t { $init } @@ -29,37 +25,99 @@ pub macro thread_local_inner { // in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|init| { - static VAL: $crate::thread::local_impl::Storage<$t> + static VAL: $crate::thread::local_impl::Storage<$t, { + $({ + // Ensure that attributes have valid syntax + // and that the proper feature gate is enabled + $(#[$($align_attr)*])+ + #[allow(unused)] + static DUMMY: () = (); + })? + + #[allow(unused_mut)] + let mut final_align = $crate::thread::local_impl::value_align::<$t>(); + $($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)? + final_align + }> = $crate::thread::local_impl::Storage::new(); VAL.get(init, __init) }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + + // process a single `rustc_align_static` attribute + (@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => { + let new_align: $crate::primitive::usize = $($align)*; + if new_align > $final_align { + $final_align = new_align; + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + // process a single `cfg_attr` attribute + // by translating it into a `cfg`ed block and recursing. + // https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate + + (@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg(true)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + (@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg(false)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + (@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg($cfg_pred)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$($align_attr:tt)*])*, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$($align_attr)*])*, $($init)*); }, } /// Use a regular global static to store this key; the state provided will then be /// thread-local. +/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::`. #[allow(missing_debug_implementations)] -pub struct Storage { +pub struct Storage { key: LazyKey, marker: PhantomData>, } -unsafe impl Sync for Storage {} +unsafe impl Sync for Storage {} +#[repr(C)] struct Value { + // This field must be first, for correctness of `#[rustc_align_static]` value: T, // INVARIANT: if this value is stored under a TLS key, `key` must be that `key`. key: Key, } -impl Storage { - pub const fn new() -> Storage { - Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } +pub const fn value_align() -> usize { + crate::mem::align_of::>() +} + +impl Storage { + pub const fn new() -> Storage { + Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -95,8 +153,15 @@ impl Storage { return ptr::null(); } - let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); - let ptr = Box::into_raw(value); + // Manually allocate with the requested alignment + let layout = Layout::new::>().align_to(ALIGN).unwrap(); + let ptr: *mut Value = (unsafe { crate::alloc::alloc(layout) }).cast(); + if ptr.is_null() { + crate::alloc::handle_alloc_error(layout); + } + unsafe { + ptr.write(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); + } // SAFETY: // * key came from a `LazyKey` and is thus correct. @@ -114,7 +179,10 @@ impl Storage { // initializer has already returned and the next scope only starts // after we return the pointer. Therefore, there can be no references // to the old value. - drop(unsafe { Box::from_raw(old) }); + unsafe { + old.drop_in_place(); + crate::alloc::dealloc(old.cast(), layout); + } } // SAFETY: We just created this value above. @@ -122,7 +190,7 @@ impl Storage { } } -unsafe extern "C" fn destroy_value(ptr: *mut u8) { +unsafe extern "C" fn destroy_value(ptr: *mut u8) { // SAFETY: // // The OS TLS ensures that this key contains a null value when this @@ -133,13 +201,22 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. abort_on_dtor_unwind(|| { - let ptr = unsafe { Box::from_raw(ptr as *mut Value) }; - let key = ptr.key; - // SAFETY: `key` is the TLS key `ptr` was stored under. - unsafe { set(key, ptr::without_provenance_mut(1)) }; - drop(ptr); - // SAFETY: `key` is the TLS key `ptr` was stored under. - unsafe { set(key, ptr::null_mut()) }; + let value_ptr: *mut Value = ptr.cast(); + unsafe { + let key = (*value_ptr).key; + + // SAFETY: `key` is the TLS key `ptr` was stored under. + set(key, ptr::without_provenance_mut(1)); + + // drop and deallocate the value + let layout = + Layout::from_size_align_unchecked(crate::mem::size_of::>(), ALIGN); + value_ptr.drop_in_place(); + crate::alloc::dealloc(ptr, layout); + + // SAFETY: `key` is the TLS key `ptr` was stored under. + set(key, ptr::null_mut()); + }; // Make sure that the runtime cleanup will be performed // after the next round of TLS destruction. guard::enable(); diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 0a6f2e5d5088b..70df7e724cafc 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -132,6 +132,212 @@ impl fmt::Debug for LocalKey { } } +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals)] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_process_attrs { + + // Parse `cfg_attr` to figure out whether it's a `rustc_align_static`. + // Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested. + + // finished parsing the `cfg_attr`, it had no `rustc_align_static` + ( + [] [$(#[$($prev_other_attrs:tt)*])*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]]; + $($rest)* + ); + ), + + // finished parsing the `cfg_attr`, it had nothing but `rustc_align_static` + ( + [$(#[$($prev_align_attrs:tt)*])+] []; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*]; + $($rest)* + ); + ), + + // finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs + ( + [$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]]; + $($rest)* + ); + ), + + // it's a `rustc_align_static` + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's a nested `cfg_attr(true, ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's a nested `cfg_attr(false, ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + + // it's a nested `cfg_attr(..., ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's some other attribute + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + + // Separate attributes into `rustc_align_static` and everything else: + + // `rustc_align_static` attribute + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(true, ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(false, ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(..., ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+]; + $vis static $($rest)* + ); + ), + + // 8 lines of doc comment; process them all at once to avoid blowing recursion limit + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + #[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*] + #[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*] + $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* + #[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*] + #[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]]; + $($rest)* + ); + ), + + // other attribute + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]]; + $($rest)* + ); + ), + + + // Delegate to `thread_local_inner` once attributes are fully categorized: + + // process `const` declaration and recurse + ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => ( + $crate::thread::local_impl::thread_local_inner!($($other_attrs)* $vis $name, $t, $($align_attrs)*, const $init); + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? + ), + + // process non-`const` declaration and recurse + ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => ( + $crate::thread::local_impl::thread_local_inner!($($other_attrs)* $vis $name, $t, $($align_attrs)*, $init); + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? + ), +} + /// Declare a new thread local storage key of type [`std::thread::LocalKey`]. /// /// # Syntax @@ -182,28 +388,11 @@ impl fmt::Debug for LocalKey { #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] macro_rules! thread_local { - // empty (base case for the recursion) () => {}; - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); - $crate::thread_local!($($rest)*); - ); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); - ); - - // process multiple declarations - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init); - $crate::thread_local!($($rest)*); - ); - - // handle a single declaration - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init); - ); + ($($tt:tt)+) => { + $crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+); + }; } /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4d09b2b4e9d2e..f320c287db32f 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -205,6 +205,7 @@ pub use self::local::{AccessError, LocalKey}; #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { + pub use super::local::thread_local_process_attrs; pub use crate::sys::thread_local::*; } diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 29f220d8a70a0..dc8eadd75148b 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -66,6 +66,8 @@ fn thread_local_hygeiene() { type Storage = (); type LazyStorage = (); type EagerStorage = (); + #[allow(non_camel_case_types)] + type usize = (); thread_local! { static A: LocalKey = const { () }; static B: Storage = const { () }; diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs index f292f028568b6..bc6a9bf8af7dc 100644 --- a/src/tools/miri/tests/pass/static_align.rs +++ b/src/tools/miri/tests/pass/static_align.rs @@ -1,4 +1,7 @@ #![feature(static_align)] +#![deny(non_upper_case_globals)] + +use std::cell::Cell; // When a static uses `align(N)`, its address should be a multiple of `N`. @@ -8,7 +11,64 @@ static FOO: u64 = 0; #[rustc_align_static(512)] static BAR: u64 = 0; +struct HasDrop(*const HasDrop); + +impl Drop for HasDrop { + fn drop(&mut self) { + assert_eq!(core::ptr::from_mut(self).cast_const(), self.0); + } +} + +thread_local! { + #[rustc_align_static(4096)] + static LOCAL: u64 = 0; + + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(true, rustc_align_static(4096))] + static CONST_LOCAL: u64 = const { 0 }; + + #[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))] + #[allow(unused_mut, reason = "test attribute handling")] + static HASDROP_LOCAL: Cell = Cell::new(HasDrop(core::ptr::null())); + + /// I love doc comments. + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(all(), + cfg_attr(any(true), + cfg_attr(true, rustc_align_static(4096))))] + #[allow(unused_mut, reason = "test attribute handling")] + /// I love doc comments. + static HASDROP_CONST_LOCAL: Cell = const { Cell::new(HasDrop(core::ptr::null())) }; + + #[cfg_attr(true,)] + #[cfg_attr(false,)] + #[cfg_attr( + true, + rustc_align_static(32), + cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")), + cfg_attr(false,) + )] + #[cfg_attr(false, rustc_align_static(0))] + static more_attr_testing: u64 = 0; +} + +fn thread_local_ptr(key: &'static std::thread::LocalKey) -> *const T { + key.with(|local| core::ptr::from_ref::(local)) +} + fn main() { assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256)); assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512)); + + assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32)); + + // Test that address (and therefore alignment) is maintained during drop + let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL); + core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast()))); + let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL); + core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast()))); } diff --git a/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs b/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs new file mode 100644 index 0000000000000..29d4facffce95 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs @@ -0,0 +1,11 @@ +// The feature gate error may be emitted twice, but only on certain targets +//@ dont-require-annotations: ERROR +//@ dont-check-compiler-stderr + +#![crate_type = "lib"] + +thread_local! { + //~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature + #[rustc_align_static(16)] + static THREAD_LOCAL: u16 = 0; +} diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs index 93241db09f949..e2db7c01adf29 100644 --- a/tests/ui/static/static-align.rs +++ b/tests/ui/static/static-align.rs @@ -1,10 +1,14 @@ //@ run-pass +//@ compile-flags: --cfg FOURTY_TWO="42" --cfg TRUE --check-cfg=cfg(FOURTY_TWO,values("42")) --check-cfg=cfg(TRUE) #![feature(static_align)] +#![deny(non_upper_case_globals)] + +use std::cell::Cell; #[rustc_align_static(64)] static A: u8 = 0; -#[rustc_align_static(64)] +#[rustc_align_static(4096)] static B: u8 = 0; #[rustc_align_static(128)] @@ -17,10 +21,86 @@ unsafe extern "C" { static C: u64; } +struct HasDrop(*const HasDrop); + +impl Drop for HasDrop { + fn drop(&mut self) { + assert_eq!(core::ptr::from_mut(self).cast_const(), self.0); + } +} + +thread_local! { + #[rustc_align_static(4096)] + static LOCAL: u64 = 0; + + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(true, rustc_align_static(4096))] + static CONST_LOCAL: u64 = const { 0 }; + + #[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))] + #[allow(unused_mut, reason = "test attribute handling")] + static HASDROP_LOCAL: Cell = Cell::new(HasDrop(core::ptr::null())); + + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(TRUE, + cfg_attr(FOURTY_TWO = "42", + cfg_attr(all(), + cfg_attr(any(true), + cfg_attr(true, rustc_align_static(4096))))))] + #[allow(unused_mut, reason = "test attribute handling")] + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + static HASDROP_CONST_LOCAL: Cell = const { Cell::new(HasDrop(core::ptr::null())) }; + + #[cfg_attr(TRUE,)] + #[cfg_attr(true,)] + #[cfg_attr(false,)] + #[cfg_attr( + TRUE, + rustc_align_static(32), + cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")), + cfg_attr(false,) + )] + #[cfg_attr(false, rustc_align_static(0))] + static more_attr_testing: u64 = 0; +} + +fn thread_local_ptr(key: &'static std::thread::LocalKey) -> *const T { + key.with(|local| core::ptr::from_ref::(local)) +} + fn main() { assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64)); - assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64)); + assert!(core::ptr::from_ref(&B).addr().is_multiple_of(4096)); assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128)); unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) }; + + assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32)); + + // Test that address (and therefore alignment) is maintained during drop + let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL); + core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast()))); + let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL); + core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast()))); } diff --git a/tests/ui/thread-local/long-docs.rs b/tests/ui/thread-local/long-docs.rs new file mode 100644 index 0000000000000..0577d0b27c285 --- /dev/null +++ b/tests/ui/thread-local/long-docs.rs @@ -0,0 +1,266 @@ +//@ check-pass + +thread_local! { + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + pub static LONG_DOCS: () = (); + + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + /// I love doc comments. + #[allow(unused_mut, reason = "testing")] + pub static LONG_DOCS_2: () = (); +} + +fn main() {} diff --git a/tests/ui/thread-local/no-unstable.rs b/tests/ui/thread-local/no-unstable.rs new file mode 100644 index 0000000000000..3de7985e62dbd --- /dev/null +++ b/tests/ui/thread-local/no-unstable.rs @@ -0,0 +1,17 @@ +thread_local! { + //~^ ERROR: use of an internal attribute [E0658] + //~| ERROR: use of an internal attribute [E0658] + //~| ERROR: `#[used(linker)]` is currently unstable [E0658] + //~| ERROR: `#[used]` attribute cannot be used on constants + + #[rustc_dummy = 17] + pub static FOO: () = (); + + #[cfg_attr(true, rustc_dummy = 17)] + pub static BAR: () = (); + + #[used(linker)] + pub static BAZ: () = (); +} + +fn main() {} diff --git a/tests/ui/thread-local/no-unstable.stderr b/tests/ui/thread-local/no-unstable.stderr new file mode 100644 index 0000000000000..fc2541894e743 --- /dev/null +++ b/tests/ui/thread-local/no-unstable.stderr @@ -0,0 +1,57 @@ +error[E0658]: use of an internal attribute + --> $DIR/no-unstable.rs:1:1 + | +LL | / thread_local! { +... | +LL | | pub static BAZ: () = (); +LL | | } + | |_^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_dummy]` attribute is used for rustc unit tests + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: use of an internal attribute + --> $DIR/no-unstable.rs:1:1 + | +LL | / thread_local! { +... | +LL | | pub static BAZ: () = (); +LL | | } + | |_^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_dummy]` attribute is used for rustc unit tests + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `#[used(linker)]` is currently unstable + --> $DIR/no-unstable.rs:1:1 + | +LL | / thread_local! { +... | +LL | | pub static BAZ: () = (); +LL | | } + | |_^ + | + = note: see issue #93798 for more information + = help: add `#![feature(used_with_arg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[used]` attribute cannot be used on constants + --> $DIR/no-unstable.rs:1:1 + | +LL | / thread_local! { +... | +LL | | pub static BAZ: () = (); +LL | | } + | |_^ + | + = help: `#[used]` can only be applied to statics + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From 852aa20c90cca64426516f46c3d89cfd57086ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 22:45:07 +0200 Subject: [PATCH 0924/1900] Rename `rust.use-lld` to `rust.bootstrap-override-lld` --- bootstrap.example.toml | 7 ++- src/bootstrap/src/core/build_steps/compile.rs | 4 +- src/bootstrap/src/core/config/config.rs | 20 ++++++-- src/bootstrap/src/core/config/mod.rs | 2 +- src/bootstrap/src/core/config/tests.rs | 36 +++++++++++--- src/bootstrap/src/core/config/toml/rust.rs | 49 ++++++++++++------- src/bootstrap/src/lib.rs | 12 ++--- src/bootstrap/src/utils/change_tracker.rs | 5 ++ src/bootstrap/src/utils/helpers.rs | 28 +++++++---- .../dist-aarch64-linux/Dockerfile | 2 +- .../host-x86_64/dist-x86_64-linux/Dockerfile | 2 +- 11 files changed, 113 insertions(+), 54 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 0cd571134ef15..0a39c6d8f247d 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -768,8 +768,7 @@ # make this default to false. #rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true -# Indicates whether LLD will be used to link Rust crates during bootstrap on -# supported platforms. +# Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD. # If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH # will be used. # If set to `"self-contained"`, rust-lld from the snapshot compiler will be used. @@ -777,7 +776,7 @@ # On MSVC, LLD will not be used if we're cross linking. # # Explicitly setting the linker for a target will override this option when targeting MSVC. -#rust.use-lld = false +#rust.bootstrap-override-lld = false # Indicates whether some LLVM tools, like llvm-objdump, will be made available in the # sysroot. @@ -950,7 +949,7 @@ # Linker to be used to bootstrap Rust code. Note that the # default value is platform specific, and if not specified it may also depend on # what platform is crossing to what platform. -# Setting this will override the `use-lld` option for Rust code when targeting MSVC. +# Setting this will override the `bootstrap-override-lld` option for Rust code when targeting MSVC. #linker = "cc" (path) # Should rustc and the standard library be built with split debuginfo? Default diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e699922f4dc05..cdd9a44a199c9 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1219,7 +1219,7 @@ pub fn rustc_cargo( // us a faster startup time. However GNU ld < 2.40 will error if we try to link a shared object // with direct references to protected symbols, so for now we only use protected symbols if // linking with LLD is enabled. - if builder.build.config.lld_mode.is_used() { + if builder.build.config.bootstrap_override_lld.is_used() { cargo.rustflag("-Zdefault-visibility=protected"); } @@ -1256,7 +1256,7 @@ pub fn rustc_cargo( // is already on by default in MSVC optimized builds, which is interpreted as --icf=all: // https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746 // https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827 - if builder.config.lld_mode.is_used() && !build_compiler.host.is_msvc() { + if builder.config.bootstrap_override_lld.is_used() && !build_compiler.host.is_msvc() { cargo.rustflag("-Clink-args=-Wl,--icf=all"); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 6d9e3b54156e0..271ce4cb950bb 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -41,7 +41,7 @@ use crate::core::config::toml::gcc::Gcc; use crate::core::config::toml::install::Install; use crate::core::config::toml::llvm::Llvm; use crate::core::config::toml::rust::{ - LldMode, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, + BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, default_lld_opt_in_targets, parse_codegen_backends, }; use crate::core::config::toml::target::Target; @@ -174,7 +174,7 @@ pub struct Config { pub llvm_from_ci: bool, pub llvm_build_config: HashMap, - pub lld_mode: LldMode, + pub bootstrap_override_lld: BootstrapOverrideLld, pub lld_enabled: bool, pub llvm_tools_enabled: bool, pub llvm_bitcode_linker_enabled: bool, @@ -567,7 +567,8 @@ impl Config { frame_pointers: rust_frame_pointers, stack_protector: rust_stack_protector, strip: rust_strip, - lld_mode: rust_lld_mode, + bootstrap_override_lld: rust_bootstrap_override_lld, + bootstrap_override_lld_legacy: rust_bootstrap_override_lld_legacy, std_features: rust_std_features, break_on_ice: rust_break_on_ice, } = toml.rust.unwrap_or_default(); @@ -615,6 +616,15 @@ impl Config { let Gcc { download_ci_gcc: gcc_download_ci_gcc } = toml.gcc.unwrap_or_default(); + if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() { + panic!( + "Cannot use both `rust.use-lld` and `rust.bootstrap-override-lld`. Please use only `rust.bootstrap-override-lld`" + ); + } + + let bootstrap_override_lld = + rust_bootstrap_override_lld.or(rust_bootstrap_override_lld_legacy).unwrap_or_default(); + if rust_optimize.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) { eprintln!( "WARNING: setting `optimize` to `false` is known to cause errors and \ @@ -960,7 +970,7 @@ impl Config { let initial_rustfmt = build_rustfmt.or_else(|| maybe_download_rustfmt(&dwn_ctx, &out)); - if matches!(rust_lld_mode.unwrap_or_default(), LldMode::SelfContained) + if matches!(bootstrap_override_lld, BootstrapOverrideLld::SelfContained) && !lld_enabled && flags_stage.unwrap_or(0) > 0 { @@ -1172,6 +1182,7 @@ impl Config { backtrace_on_ice: rust_backtrace_on_ice.unwrap_or(false), bindir: install_bindir.map(PathBuf::from).unwrap_or("bin".into()), bootstrap_cache_path: build_bootstrap_cache_path, + bootstrap_override_lld, bypass_bootstrap_lock: flags_bypass_bootstrap_lock, cargo_info, cargo_native_static: build_cargo_native_static.unwrap_or(false), @@ -1238,7 +1249,6 @@ impl Config { libdir: install_libdir.map(PathBuf::from), library_docs_private_items: build_library_docs_private_items.unwrap_or(false), lld_enabled, - lld_mode: rust_lld_mode.unwrap_or_default(), lldb: build_lldb.map(PathBuf::from), llvm_allow_old_toolchain: llvm_allow_old_toolchain.unwrap_or(false), llvm_assertions, diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 05a5dfc0bc5a8..56b87823a3654 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -37,7 +37,7 @@ use serde_derive::Deserialize; pub use target_selection::TargetSelection; pub use toml::BUILDER_CONFIG_FILENAME; pub use toml::change_id::ChangeId; -pub use toml::rust::LldMode; +pub use toml::rust::BootstrapOverrideLld; pub use toml::target::Target; use crate::Display; diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 4f2df76a15658..e19604d4ab12f 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -17,7 +17,9 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::build_steps::{llvm, test}; use crate::core::config::toml::TomlConfig; -use crate::core::config::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection}; +use crate::core::config::{ + BootstrapOverrideLld, CompilerBuiltins, StringOrBool, Target, TargetSelection, +}; use crate::utils::tests::TestCtx; use crate::utils::tests::git::git_test; @@ -222,11 +224,33 @@ fn verify_file_integrity() { #[test] fn rust_lld() { - assert!(matches!(parse("").lld_mode, LldMode::Unused)); - assert!(matches!(parse("rust.use-lld = \"self-contained\"").lld_mode, LldMode::SelfContained)); - assert!(matches!(parse("rust.use-lld = \"external\"").lld_mode, LldMode::External)); - assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External)); - assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused)); + assert!(matches!(parse("").bootstrap_override_lld, BootstrapOverrideLld::None)); + assert!(matches!( + parse("rust.bootstrap-override-lld = \"self-contained\"").bootstrap_override_lld, + BootstrapOverrideLld::SelfContained + )); + assert!(matches!( + parse("rust.bootstrap-override-lld = \"external\"").bootstrap_override_lld, + BootstrapOverrideLld::External + )); + assert!(matches!( + parse("rust.bootstrap-override-lld = true").bootstrap_override_lld, + BootstrapOverrideLld::External + )); + assert!(matches!( + parse("rust.bootstrap-override-lld = false").bootstrap_override_lld, + BootstrapOverrideLld::None + )); + + // Also check the legacy options + assert!(matches!( + parse("rust.use-lld = true").bootstrap_override_lld, + BootstrapOverrideLld::External + )); + assert!(matches!( + parse("rust.use-lld = false").bootstrap_override_lld, + BootstrapOverrideLld::None + )); } #[test] diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index e5987d7040aaf..ca9e0d0bc98e7 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -45,7 +45,9 @@ define_config! { codegen_backends: Option> = "codegen-backends", llvm_bitcode_linker: Option = "llvm-bitcode-linker", lld: Option = "lld", - lld_mode: Option = "use-lld", + bootstrap_override_lld: Option = "bootstrap-override-lld", + // FIXME: Remove this option in Spring 2026 + bootstrap_override_lld_legacy: Option = "use-lld", llvm_tools: Option = "llvm-tools", deny_warnings: Option = "deny-warnings", backtrace_on_ice: Option = "backtrace-on-ice", @@ -70,22 +72,33 @@ define_config! { } } -/// LLD in bootstrap works like this: -/// - Self-contained lld: use `rust-lld` from the compiler's sysroot +/// Determines if we should override the linker used for linking Rust code built +/// during the bootstrapping process to be LLD. +/// +/// The primary use-case for this is to make local (re)builds of Rust code faster +/// when using bootstrap. +/// +/// This does not affect the *behavior* of the built/distributed compiler when invoked +/// outside of bootstrap. +/// It might affect its performance/binary size though, as that can depend on the +/// linker that links rustc. +/// +/// There are two ways of overriding the linker to be LLD: +/// - Self-contained LLD: use `rust-lld` from the compiler's sysroot /// - External: use an external `lld` binary /// /// It is configured depending on the target: /// 1) Everything except MSVC -/// - Self-contained: `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker` -/// - External: `-Clinker-flavor=gnu-lld-cc` +/// - Self-contained: `-Clinker-features=+lld -Clink-self-contained=+linker` +/// - External: `-Clinker-features=+lld` /// 2) MSVC /// - Self-contained: `-Clinker=` /// - External: `-Clinker=lld` #[derive(Copy, Clone, Default, Debug, PartialEq)] -pub enum LldMode { - /// Do not use LLD +pub enum BootstrapOverrideLld { + /// Do not override the linker LLD #[default] - Unused, + None, /// Use `rust-lld` from the compiler's sysroot SelfContained, /// Use an externally provided `lld` binary. @@ -94,16 +107,16 @@ pub enum LldMode { External, } -impl LldMode { +impl BootstrapOverrideLld { pub fn is_used(&self) -> bool { match self { - LldMode::SelfContained | LldMode::External => true, - LldMode::Unused => false, + BootstrapOverrideLld::SelfContained | BootstrapOverrideLld::External => true, + BootstrapOverrideLld::None => false, } } } -impl<'de> Deserialize<'de> for LldMode { +impl<'de> Deserialize<'de> for BootstrapOverrideLld { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -111,7 +124,7 @@ impl<'de> Deserialize<'de> for LldMode { struct LldModeVisitor; impl serde::de::Visitor<'_> for LldModeVisitor { - type Value = LldMode; + type Value = BootstrapOverrideLld; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { formatter.write_str("one of true, 'self-contained' or 'external'") @@ -121,7 +134,7 @@ impl<'de> Deserialize<'de> for LldMode { where E: serde::de::Error, { - Ok(if v { LldMode::External } else { LldMode::Unused }) + Ok(if v { BootstrapOverrideLld::External } else { BootstrapOverrideLld::None }) } fn visit_str(self, v: &str) -> Result @@ -129,8 +142,8 @@ impl<'de> Deserialize<'de> for LldMode { E: serde::de::Error, { match v { - "external" => Ok(LldMode::External), - "self-contained" => Ok(LldMode::SelfContained), + "external" => Ok(BootstrapOverrideLld::External), + "self-contained" => Ok(BootstrapOverrideLld::SelfContained), _ => Err(E::custom(format!("unknown mode {v}"))), } } @@ -311,7 +324,6 @@ pub fn check_incompatible_options_for_ci_rustc( lto, stack_protector, strip, - lld_mode, jemalloc, rpath, channel, @@ -359,6 +371,8 @@ pub fn check_incompatible_options_for_ci_rustc( frame_pointers: _, break_on_ice: _, parallel_frontend_threads: _, + bootstrap_override_lld: _, + bootstrap_override_lld_legacy: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: @@ -374,7 +388,6 @@ pub fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust"); err!(current_rust_config.rpath, rpath, "rust"); err!(current_rust_config.strip, strip, "rust"); - err!(current_rust_config.lld_mode, lld_mode, "rust"); err!(current_rust_config.llvm_tools, llvm_tools, "rust"); err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker, "rust"); err!(current_rust_config.jemalloc, jemalloc, "rust"); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 4f4d35673d5d1..d798639cc967e 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -35,7 +35,7 @@ use utils::exec::ExecutionContext; use crate::core::builder; use crate::core::builder::Kind; -use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags}; +use crate::core::config::{BootstrapOverrideLld, DryRun, LlvmLibunwind, TargetSelection, flags}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo}; @@ -1358,14 +1358,14 @@ impl Build { && !target.is_msvc() { Some(self.cc(target)) - } else if self.config.lld_mode.is_used() + } else if self.config.bootstrap_override_lld.is_used() && self.is_lld_direct_linker(target) && self.host_target == target { - match self.config.lld_mode { - LldMode::SelfContained => Some(self.initial_lld.clone()), - LldMode::External => Some("lld".into()), - LldMode::Unused => None, + match self.config.bootstrap_override_lld { + BootstrapOverrideLld::SelfContained => Some(self.initial_lld.clone()), + BootstrapOverrideLld::External => Some("lld".into()), + BootstrapOverrideLld::None => None, } } else { None diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 6b187578c31ec..f311c84bec9b5 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -556,4 +556,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.", }, + ChangeInfo { + change_id: 99999, + severity: ChangeSeverity::Warning, + summary: "The `rust.use-lld` option has been renamed to `rust.bootstrap-override-lld`. Note that it only serves for overriding the linker used when building Rust code in bootstrap to be LLD.", + }, ]; diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index e802c0214ddcb..faada9a111db8 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -12,7 +12,7 @@ use std::{env, fs, io, panic, str}; use object::read::archive::ArchiveFile; -use crate::LldMode; +use crate::BootstrapOverrideLld; use crate::core::builder::Builder; use crate::core::config::{Config, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; @@ -357,15 +357,19 @@ pub fn get_clang_cl_resource_dir(builder: &Builder<'_>, clang_cl_path: &str) -> /// Returns a flag that configures LLD to use only a single thread. /// If we use an external LLD, we need to find out which version is it to know which flag should we /// pass to it (LLD older than version 10 had a different flag). -fn lld_flag_no_threads(builder: &Builder<'_>, lld_mode: LldMode, is_windows: bool) -> &'static str { +fn lld_flag_no_threads( + builder: &Builder<'_>, + bootstrap_override_lld: BootstrapOverrideLld, + is_windows: bool, +) -> &'static str { static LLD_NO_THREADS: OnceLock<(&'static str, &'static str)> = OnceLock::new(); let new_flags = ("/threads:1", "--threads=1"); let old_flags = ("/no-threads", "--no-threads"); let (windows_flag, other_flag) = LLD_NO_THREADS.get_or_init(|| { - let newer_version = match lld_mode { - LldMode::External => { + let newer_version = match bootstrap_override_lld { + BootstrapOverrideLld::External => { let mut cmd = command("lld"); cmd.arg("-flavor").arg("ld").arg("--version"); let out = cmd.run_capture_stdout(builder).stdout(); @@ -422,24 +426,28 @@ pub fn linker_flags( lld_threads: LldThreads, ) -> Vec { let mut args = vec![]; - if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() { - match builder.config.lld_mode { - LldMode::External => { + if !builder.is_lld_direct_linker(target) && builder.config.bootstrap_override_lld.is_used() { + match builder.config.bootstrap_override_lld { + BootstrapOverrideLld::External => { args.push("-Clinker-features=+lld".to_string()); args.push("-Zunstable-options".to_string()); } - LldMode::SelfContained => { + BootstrapOverrideLld::SelfContained => { args.push("-Clinker-features=+lld".to_string()); args.push("-Clink-self-contained=+linker".to_string()); args.push("-Zunstable-options".to_string()); } - LldMode::Unused => unreachable!(), + BootstrapOverrideLld::None => unreachable!(), }; if matches!(lld_threads, LldThreads::No) { args.push(format!( "-Clink-arg=-Wl,{}", - lld_flag_no_threads(builder, builder.config.lld_mode, target.is_windows()) + lld_flag_no_threads( + builder, + builder.config.bootstrap_override_lld, + target.is_windows() + ) )); } } diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index e726329753f24..3abca36fe70db 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -91,7 +91,7 @@ ENV RUST_CONFIGURE_ARGS \ --set llvm.ninja=false \ --set rust.debug-assertions=false \ --set rust.jemalloc \ - --set rust.use-lld=true \ + --set rust.bootstrap-override-lld=true \ --set rust.lto=thin \ --set rust.codegen-units=1 diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 01f19eac1d2fa..cb57478761994 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -92,7 +92,7 @@ ENV RUST_CONFIGURE_ARGS \ --set llvm.ninja=false \ --set llvm.libzstd=true \ --set rust.jemalloc \ - --set rust.use-lld=true \ + --set rust.bootstrap-override-lld=true \ --set rust.lto=thin \ --set rust.codegen-units=1 From b550688258ddc29618ebd2fa0b6b4a518f12c0e4 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:17:06 -0700 Subject: [PATCH 0925/1900] castkind::subtype in clippy --- src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 2bda6d50373cd..cc98fac45c7cb 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -141,7 +141,8 @@ fn check_rvalue<'tcx>( | CastKind::FloatToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _), + | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _) + | CastKind::Subtype, operand, _, ) => check_operand(cx, operand, span, body, msrv), @@ -312,7 +313,6 @@ fn check_place<'tcx>( | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) | ProjectionElem::UnwrapUnsafeBinder(_) => {}, } From 36d6327fa46a949d217b691fc7fb978359ca636b Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:17:06 -0700 Subject: [PATCH 0926/1900] castkind::subtype in clippy --- clippy_utils/src/qualify_min_const_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 2bda6d50373cd..cc98fac45c7cb 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -141,7 +141,8 @@ fn check_rvalue<'tcx>( | CastKind::FloatToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _), + | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _) + | CastKind::Subtype, operand, _, ) => check_operand(cx, operand, span, body, msrv), @@ -312,7 +313,6 @@ fn check_place<'tcx>( | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) | ProjectionElem::UnwrapUnsafeBinder(_) => {}, } From bd860bdf267644810c8efb4a8d74fdbd0ff8383c Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 26 Sep 2025 15:38:30 -0400 Subject: [PATCH 0927/1900] Fix typo in LLVM_VERSION_ macro use Co-authored-by: Nikita Popov --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index b7637b29bca53..2e9fd6754f124 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -569,7 +569,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( } std::optional PGOOpt; -#if LLVM_VERSION_LE(22, 0) +#if LLVM_VERSION_LT(22, 0) auto FS = vfs::getRealFileSystem(); #endif if (PGOGenPath) { From 9d5adff08cafbb93a53c6f72fcec8cdf89d06e49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:45:06 +0000 Subject: [PATCH 0928/1900] Bump tar-fs from 2.1.3 to 2.1.4 in /editors/code Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.3 to 2.1.4. - [Commits](https://github.com/mafintosh/tar-fs/compare/v2.1.3...v2.1.4) --- updated-dependencies: - dependency-name: tar-fs dependency-version: 2.1.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index ad8708e00c519..e35a159cbc3fd 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -6405,9 +6405,9 @@ } }, "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "dev": true, "license": "MIT", "optional": true, From eb3fb457f14c419b58fc8b20d72efcea94e06d9f Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 26 Sep 2025 15:37:44 -0400 Subject: [PATCH 0929/1900] split-dwarf: add documentation and test coverage --- .../src/compiler-flags/split-dwarf-out-dir.md | 7 + .../src/external_deps/rustc.rs | 7 + tests/run-make/split-debuginfo/rmake.rs | 150 ++++++++++++++++-- 3 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md diff --git a/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md b/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md new file mode 100644 index 0000000000000..a2070730b42f8 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md @@ -0,0 +1,7 @@ +# `split-dwarf-out-dir` + +On systems which use DWARF debug info this flag causes `.dwo` files produced +by `-C split-debuginfo` to be written to the specified directory rather than +placed next to the object files. This is mostly useful if you have a build +system which needs to control where to find compile outputs without running the +compiler and have to put your `.dwo` files in a separate directory. diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index b74b1d5e166fc..b461a24a06168 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -366,6 +366,13 @@ impl Rustc { self } + pub fn split_dwarf_out_dir(&mut self, out_dir: Option<&str>) -> &mut Self { + if let Some(out_dir) = out_dir { + self.cmd.arg(format!("-Zsplit-dwarf-out-dir={out_dir}")); + } + self + } + /// Pass the `--verbose` flag. pub fn verbose(&mut self) -> &mut Self { self.cmd.arg("--verbose"); diff --git a/tests/run-make/split-debuginfo/rmake.rs b/tests/run-make/split-debuginfo/rmake.rs index e8de5aed17260..e53b710107812 100644 --- a/tests/run-make/split-debuginfo/rmake.rs +++ b/tests/run-make/split-debuginfo/rmake.rs @@ -187,6 +187,25 @@ enum UnstableOptions { Unspecified, } +#[track_caller] +fn dwo_out_filenames(dwo_out: Option<&str>) -> BTreeSet { + let dwo_out = if let Some(d) = dwo_out { + d + } else { + return BTreeSet::new(); + }; + let files = shallow_find_files(dwo_out, |path| { + // Fiilter out source files + !has_extension(path, "rs") + }); + files + .iter() + .map(|p| { + format!("{}/{}", dwo_out, p.file_name().unwrap().to_os_string().into_string().unwrap()) + }) + .collect() +} + #[track_caller] fn cwd_filenames() -> BTreeSet { let files = shallow_find_files(cwd(), |path| { @@ -196,6 +215,17 @@ fn cwd_filenames() -> BTreeSet { files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() } +#[track_caller] +fn dwo_out_dwo_filenames(dwo_out: &str) -> BTreeSet { + let files = shallow_find_files(dwo_out, |p| has_extension(p, "dwo")); + files + .iter() + .map(|p| { + format!("{}/{}", dwo_out, p.file_name().unwrap().to_os_string().into_string().unwrap()) + }) + .collect() +} + #[track_caller] fn cwd_dwo_filenames() -> BTreeSet { let files = shallow_find_files(cwd(), |path| has_extension(path, "dwo")); @@ -376,17 +406,19 @@ mod shared_linux_other_tests { lto: LinkerPluginLto, remap_path_prefix: RemapPathPrefix, remap_path_scope: RemapPathScope, + split_dwarf_output_directory: Option<&str>, ) { run_in_tmpdir(|| { println!( - "checking: unstable_options={:?} + split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?}", + "checking: unstable_options={:?} + split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?} + split_dwarf_out_dir={:?}", unstable_options, split_kind, level, split_dwarf_kind, lto, remap_path_prefix, - remap_path_scope + remap_path_scope, + split_dwarf_output_directory, ); match cross_crate_test { @@ -398,6 +430,7 @@ mod shared_linux_other_tests { lto, remap_path_prefix, remap_path_scope, + split_dwarf_output_directory, ), CrossCrateTest::No => simple_split_debuginfo( unstable_options, @@ -407,6 +440,7 @@ mod shared_linux_other_tests { lto, remap_path_prefix, remap_path_scope, + split_dwarf_output_directory, ), } }); @@ -420,7 +454,11 @@ mod shared_linux_other_tests { lto: LinkerPluginLto, remap_path_prefix: RemapPathPrefix, remap_path_scope: RemapPathScope, + split_dwarf_output_directory: Option<&str>, ) { + if let Some(dwo_out) = split_dwarf_output_directory { + run_make_support::rfs::create_dir(dwo_out); + } match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { // packed-crosscrate-split // - Debuginfo in `.dwo` files @@ -531,13 +569,19 @@ mod shared_linux_other_tests { .input("bar.rs") .crate_type("lib") .split_debuginfo(split_kind.cli_value()) + .split_dwarf_out_dir(split_dwarf_output_directory) .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .run(); - let bar_found_files = cwd_filenames(); + let mut bar_found_files = cwd_filenames(); + bar_found_files.append(&mut dwo_out_filenames(split_dwarf_output_directory)); - let bar_dwo_files = cwd_dwo_filenames(); + let bar_dwo_files = if let Some(dwo_out) = split_dwarf_output_directory { + dwo_out_dwo_filenames(dwo_out) + } else { + cwd_dwo_filenames() + }; assert_eq!(bar_dwo_files.len(), 1); let mut bar_expected_files = BTreeSet::new(); @@ -553,13 +597,19 @@ mod shared_linux_other_tests { .extern_("bar", "libbar.rlib") .input("main.rs") .split_debuginfo(split_kind.cli_value()) + .split_dwarf_out_dir(split_dwarf_output_directory) .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .run(); - let overall_found_files = cwd_filenames(); + let mut overall_found_files = cwd_filenames(); + overall_found_files.append(&mut dwo_out_filenames(split_dwarf_output_directory)); - let overall_dwo_files = cwd_dwo_filenames(); + let overall_dwo_files = if let Some(dwo_out) = split_dwarf_output_directory { + dwo_out_dwo_filenames(dwo_out) + } else { + cwd_dwo_filenames() + }; assert_eq!(overall_dwo_files.len(), 2); let mut overall_expected_files = BTreeSet::new(); @@ -648,7 +698,11 @@ mod shared_linux_other_tests { lto: LinkerPluginLto, remap_path_prefix: RemapPathPrefix, remap_path_scope: RemapPathScope, + split_dwarf_output_directory: Option<&str>, ) { + if let Some(dwo_out) = split_dwarf_output_directory { + run_make_support::rfs::create_dir(dwo_out); + } match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { // off (unspecified): // - Debuginfo in `.o` files @@ -921,14 +975,19 @@ mod shared_linux_other_tests { rustc(unstable_options) .input("foo.rs") .split_debuginfo(split_kind.cli_value()) + .split_dwarf_out_dir(split_dwarf_output_directory) .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .run(); - let found_files = cwd_filenames(); - - let dwo_files = cwd_dwo_filenames(); + let mut found_files = cwd_filenames(); + found_files.append(&mut dwo_out_filenames(split_dwarf_output_directory)); + + let dwo_files = if let Some(dwo_dir) = split_dwarf_output_directory { + dwo_out_dwo_filenames(dwo_dir) + } else { + cwd_dwo_filenames() + }; assert_eq!(dwo_files.len(), 1); - let mut expected_files = BTreeSet::new(); expected_files.extend(dwo_files); expected_files.insert("foo".to_string()); @@ -1056,14 +1115,20 @@ mod shared_linux_other_tests { rustc(unstable_options) .input("foo.rs") .split_debuginfo(split_kind.cli_value()) + .split_dwarf_out_dir(split_dwarf_output_directory) .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .remap_path_prefix(cwd(), remapped_prefix) .run(); - let found_files = cwd_filenames(); + let mut found_files = cwd_filenames(); + found_files.append(&mut dwo_out_filenames(split_dwarf_output_directory)); - let dwo_files = cwd_dwo_filenames(); + let dwo_files = if let Some(dwo_out) = split_dwarf_output_directory { + dwo_out_dwo_filenames(dwo_out) + } else { + cwd_dwo_filenames() + }; assert_eq!(dwo_files.len(), 1); let mut expected_files = BTreeSet::new(); @@ -1358,6 +1423,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // off @@ -1370,6 +1436,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // packed-split @@ -1382,6 +1449,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // packed-single @@ -1394,6 +1462,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // packed-lto-split @@ -1406,6 +1475,7 @@ fn main() { LinkerPluginLto::Yes, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // packed-lto-single @@ -1418,6 +1488,7 @@ fn main() { LinkerPluginLto::Yes, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // FIXME: the remapping tests probably need to be reworked, see @@ -1433,6 +1504,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Unspecified, + None, ); // packed-remapped-single @@ -1445,6 +1517,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Unspecified, + None, ); // packed-remapped-scope @@ -1457,6 +1530,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Yes("debuginfo"), + None, ); // packed-remapped-wrong-scope @@ -1469,6 +1543,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Yes("macro"), + None, ); // packed-crosscrate-split @@ -1481,6 +1556,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // packed-crosscrate-single @@ -1493,6 +1569,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // unpacked-split @@ -1505,6 +1582,20 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, + ); + + // unpacked-split with split-dwarf-out-dir + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + UnstableOptions::Yes, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + Some("other-dir"), ); // unpacked-single @@ -1517,6 +1608,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // unpacked-lto-split @@ -1529,6 +1621,7 @@ fn main() { LinkerPluginLto::Yes, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // unpacked-lto-single @@ -1541,6 +1634,7 @@ fn main() { LinkerPluginLto::Yes, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); // unpacked-remapped-split @@ -1553,6 +1647,20 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Unspecified, + None, + ); + + // unpacked-remapped-split with split-dwarf-out-dir + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + UnstableOptions::Yes, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + Some("other-dir"), ); // unpacked-remapped-single @@ -1565,6 +1673,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Unspecified, + None, ); // unpacked-remapped-scope @@ -1577,6 +1686,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Yes("debuginfo"), + None, ); // unpacked-remapped-wrong-scope @@ -1589,6 +1699,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, RemapPathScope::Yes("macro"), + None, ); // unpacked-crosscrate-split @@ -1601,6 +1712,20 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, + ); + + // unpacked-crosscrate-split with split-dwarf-out-dir + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + UnstableOptions::Yes, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + Some("other-dir"), ); // unpacked-crosscrate-single @@ -1613,6 +1738,7 @@ fn main() { LinkerPluginLto::Unspecified, RemapPathPrefix::Unspecified, RemapPathScope::Unspecified, + None, ); } } From b7e444de16d5945908657200d59af608d8c6232c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 16:43:01 +0200 Subject: [PATCH 0930/1900] Add regression test for merged doctests compilation time display --- .../doctests-compilation-time-info/rmake.rs | 119 ++++++++++++++++++ .../doctests-merge/doctest-2024.stdout | 1 + tests/run-make/doctests-merge/rmake.rs | 2 + .../doctest/doctest-output.edition2015.stdout | 6 +- .../doctest/doctest-output.edition2024.stdout | 7 +- tests/rustdoc-ui/doctest/doctest-output.rs | 2 + .../doctest/merged-ignore-no_run.rs | 2 + .../doctest/merged-ignore-no_run.stdout | 5 +- 8 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 tests/run-make/doctests-compilation-time-info/rmake.rs diff --git a/tests/run-make/doctests-compilation-time-info/rmake.rs b/tests/run-make/doctests-compilation-time-info/rmake.rs new file mode 100644 index 0000000000000..2bcf664923f2a --- /dev/null +++ b/tests/run-make/doctests-compilation-time-info/rmake.rs @@ -0,0 +1,119 @@ +//@ ignore-cross-compile (needs to run doctests) + +use run_make_support::rfs::write; +use run_make_support::{cwd, rustdoc}; + +fn assert_presence_of_compilation_time_report( + content: &str, + success: bool, + should_contain_compile_time: bool, +) { + let mut cmd = rustdoc(); + let file = cwd().join("foo.rs"); + + write(&file, content); + cmd.input(&file).arg("--test").edition("2024").env("RUST_BACKTRACE", "0"); + let output = if success { cmd.run() } else { cmd.run_fail() }; + + assert_eq!( + output + .stdout_utf8() + .split("all doctests ran in ") + .last() + .is_some_and(|s| s.contains("; merged doctests compilation took")), + should_contain_compile_time, + ); +} + +fn main() { + // Checking with only successful merged doctests. + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! let x = 12; +//! ```", + true, + true, + ); + // Checking with only failing merged doctests. + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! panic!(); +//! ```", + false, + true, + ); + // Checking with mix of successful doctests. + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! let x = 12; +//! ``` +//! +//! ```compile_fail +//! let x +//! ```", + true, + true, + ); + // Checking with mix of failing doctests. + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! panic!(); +//! ``` +//! +//! ```compile_fail +//! let x +//! ```", + false, + true, + ); + // Checking with mix of failing doctests (v2). + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! let x = 12; +//! ``` +//! +//! ```compile_fail +//! let x = 12; +//! ```", + false, + true, + ); + // Checking with mix of failing doctests (v3). + assert_presence_of_compilation_time_report( + "\ +//! ``` +//! panic!(); +//! ``` +//! +//! ```compile_fail +//! let x = 12; +//! ```", + false, + true, + ); + // Checking with successful non-merged doctests. + assert_presence_of_compilation_time_report( + "\ +//! ```compile_fail +//! let x +//! ```", + true, + // If there is no merged doctests, then we should not display compilation time. + false, + ); + // Checking with failing non-merged doctests. + assert_presence_of_compilation_time_report( + "\ +//! ```compile_fail +//! let x = 12; +//! ```", + false, + // If there is no merged doctests, then we should not display compilation time. + false, + ); +} diff --git a/tests/run-make/doctests-merge/doctest-2024.stdout b/tests/run-make/doctests-merge/doctest-2024.stdout index 7da08d68faae3..a7e139bbd23dc 100644 --- a/tests/run-make/doctests-merge/doctest-2024.stdout +++ b/tests/run-make/doctests-merge/doctest-2024.stdout @@ -5,3 +5,4 @@ test doctest.rs - init (line 8) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/run-make/doctests-merge/rmake.rs b/tests/run-make/doctests-merge/rmake.rs index 7893d4988ebba..f2a1e8e13ddfe 100644 --- a/tests/run-make/doctests-merge/rmake.rs +++ b/tests/run-make/doctests-merge/rmake.rs @@ -20,6 +20,8 @@ fn test_and_compare(input_file: &str, stdout_file: &str, edition: &str, dep: &Pa .expected_file(stdout_file) .actual_text("output", output.stdout_utf8()) .normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME") + .normalize(r#"ran in \d+\.\d+s"#, "ran in $$TIME") + .normalize(r#"compilation took \d+\.\d+s"#, "compilation took $$TIME") .run(); } diff --git a/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout b/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout index 0e2e30390ad93..2ff7174577e87 100644 --- a/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout +++ b/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout @@ -1,8 +1,8 @@ running 3 tests -test $DIR/doctest-output.rs - (line 12) ... ok -test $DIR/doctest-output.rs - ExpandedStruct (line 28) ... ok -test $DIR/doctest-output.rs - foo::bar (line 22) ... ok +test $DIR/doctest-output.rs - (line 14) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 30) ... ok +test $DIR/doctest-output.rs - foo::bar (line 24) ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout b/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout index 0e2e30390ad93..20bfd7e7086eb 100644 --- a/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout +++ b/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout @@ -1,8 +1,9 @@ running 3 tests -test $DIR/doctest-output.rs - (line 12) ... ok -test $DIR/doctest-output.rs - ExpandedStruct (line 28) ... ok -test $DIR/doctest-output.rs - foo::bar (line 22) ... ok +test $DIR/doctest-output.rs - (line 14) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 30) ... ok +test $DIR/doctest-output.rs - foo::bar (line 24) ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/doctest-output.rs b/tests/rustdoc-ui/doctest/doctest-output.rs index 04bd1813b4c81..943f59e8b1565 100644 --- a/tests/rustdoc-ui/doctest/doctest-output.rs +++ b/tests/rustdoc-ui/doctest/doctest-output.rs @@ -7,6 +7,8 @@ //@[edition2024]compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ check-pass //! ``` diff --git a/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs index 7dac64e6de420..f92bea74bfef4 100644 --- a/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs +++ b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs @@ -2,6 +2,8 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ check-pass /// ```ignore (test) diff --git a/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout b/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout index a32da0aeb9649..6714cdb0b8098 100644 --- a/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout +++ b/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout @@ -1,7 +1,8 @@ running 2 tests -test $DIR/merged-ignore-no_run.rs - ignored (line 7) ... ignored -test $DIR/merged-ignore-no_run.rs - no_run (line 12) - compile ... ok +test $DIR/merged-ignore-no_run.rs - ignored (line 9) ... ignored +test $DIR/merged-ignore-no_run.rs - no_run (line 14) - compile ... ok test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME From d13a5b0a6c12837007c51f229cb8c58b614cfb5b Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 26 Sep 2025 20:44:06 +0000 Subject: [PATCH 0931/1900] Handle self-loops too. --- compiler/rustc_mir_transform/src/jump_threading.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index c69660108d9a3..68298767e7fd8 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -842,9 +842,6 @@ fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet { let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); let mut visited = DenseBitSet::new_empty(body.basic_blocks.len()); for (bb, bbdata) in traversal::postorder(body) { - let _new = visited.insert(bb); - debug_assert!(_new); - // Post-order means we visit successors before the block for acyclic CFGs. // If the successor is not visited yet, consider it a loop header. for succ in bbdata.terminator().successors() { @@ -852,6 +849,11 @@ fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet { maybe_loop_headers.insert(succ); } } + + // Only mark `bb` as visited after we checked the successors, in case we have a self-loop. + // bb1: goto -> bb1; + let _new = visited.insert(bb); + debug_assert!(_new); } maybe_loop_headers From e88fa086fb597041a207a9bb5df0654628d57b1a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 27 Sep 2025 01:11:01 +0300 Subject: [PATCH 0932/1900] move Reborrow to ops, fix fmt issues --- library/core/src/marker.rs | 8 -------- library/core/src/ops/mod.rs | 2 +- library/core/src/ops/reborrow.rs | 8 +++++++- tests/ui/feature-gates/feature-gate-reborrow.rs | 2 +- tests/ui/feature-gates/feature-gate-reborrow.stderr | 4 ++-- tests/ui/reborrow/custom_mut.rs | 2 +- tests/ui/reborrow/custom_mut_coerce_shared.rs | 3 +-- tests/ui/reborrow/custom_mut_coerce_shared.stderr | 4 ++-- tests/ui/reborrow/option_mut.rs | 2 +- tests/ui/reborrow/option_mut.stderr | 4 ++-- tests/ui/reborrow/pin_mut.rs | 2 +- tests/ui/reborrow/pin_mut.stderr | 4 ++-- tests/ui/reborrow/pin_mut_coerce_shared.rs | 1 - 13 files changed, 21 insertions(+), 25 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index d03d7a43469a7..fc715207d5dad 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1364,11 +1364,3 @@ pub macro CoercePointee($item:item) { pub trait CoercePointeeValidated { /* compiler built-in */ } - -/// Allows value to be reborrowed as exclusive, creating a copy of the value -/// that disables the source for reads and writes for the lifetime of the copy. -#[lang = "reborrow"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait Reborrow { - // Empty. -} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 9814f5d5795c6..ab1ad407ee282 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -191,7 +191,7 @@ pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "reborrow", issue = "145612")] -pub use self::reborrow::CoerceShared; +pub use self::reborrow::{CoerceShared, Reborrow}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs index 90288f766d502..f83f4233a4de5 100644 --- a/library/core/src/ops/reborrow.rs +++ b/library/core/src/ops/reborrow.rs @@ -1,4 +1,10 @@ -use crate::marker::Reborrow; +/// Allows value to be reborrowed as exclusive, creating a copy of the value +/// that disables the source for reads and writes for the lifetime of the copy. +#[lang = "reborrow"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait Reborrow { + // Empty. +} /// Allows reborrowable value to be reborrowed as shared, creating a copy /// that disables the source for writes for the lifetime of the copy. diff --git a/tests/ui/feature-gates/feature-gate-reborrow.rs b/tests/ui/feature-gates/feature-gate-reborrow.rs index f016f6c6bfa59..96eecfb28a106 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow.rs @@ -1,3 +1,3 @@ -use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` +use std::ops::Reborrow; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow.stderr b/tests/ui/feature-gates/feature-gate-reborrow.stderr index 5e3033f3bf1fe..1224909f564bc 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow.rs:1:5 | -LL | use std::marker::Reborrow; - | ^^^^^^^^^^^^^^^^^^^^^ +LL | use std::ops::Reborrow; + | ^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/reborrow/custom_mut.rs b/tests/ui/reborrow/custom_mut.rs index b55a5e6faa3dc..1e7c469323822 100644 --- a/tests/ui/reborrow/custom_mut.rs +++ b/tests/ui/reborrow/custom_mut.rs @@ -1,5 +1,5 @@ #![feature(reborrow)] -use std::marker::Reborrow; +use std::ops::Reborrow; struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index 28d802aabffe5..e2d25835c093a 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,6 +1,5 @@ #![feature(reborrow)] -use std::marker::Reborrow; -use std::ops::CoerceShared; +use std::ops::{CoerceShared, Reborrow}; struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr index 90d8a98160543..508651badc0a4 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.stderr +++ b/tests/ui/reborrow/custom_mut_coerce_shared.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/custom_mut_coerce_shared.rs:24:12 + --> $DIR/custom_mut_coerce_shared.rs:23:12 | LL | method(a); | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` @@ -9,7 +9,7 @@ LL | method(a); = note: expected struct `CustomRef<'_, ()>` found struct `CustomMut<'_, ()>` note: function defined here - --> $DIR/custom_mut_coerce_shared.rs:20:4 + --> $DIR/custom_mut_coerce_shared.rs:19:4 | LL | fn method(a: CustomRef<'_, ()>) {} | ^^^^^^ -------------------- diff --git a/tests/ui/reborrow/option_mut.rs b/tests/ui/reborrow/option_mut.rs index 60b7145a7b2e3..04d8301772de9 100644 --- a/tests/ui/reborrow/option_mut.rs +++ b/tests/ui/reborrow/option_mut.rs @@ -1,4 +1,4 @@ -fn method(a: Option<& mut ()>) {} +fn method(a: Option<&mut ()>) {} fn main() { let a = Some(&mut ()); diff --git a/tests/ui/reborrow/option_mut.stderr b/tests/ui/reborrow/option_mut.stderr index 497319d2e9035..d665e266079ea 100644 --- a/tests/ui/reborrow/option_mut.stderr +++ b/tests/ui/reborrow/option_mut.stderr @@ -11,8 +11,8 @@ LL | let _ = method(a); note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary --> $DIR/option_mut.rs:1:14 | -LL | fn method(a: Option<& mut ()>) {} - | ------ ^^^^^^^^^^^^^^^^ this parameter takes ownership of the value +LL | fn method(a: Option<&mut ()>) {} + | ------ ^^^^^^^^^^^^^^^ this parameter takes ownership of the value | | | in this function diff --git a/tests/ui/reborrow/pin_mut.rs b/tests/ui/reborrow/pin_mut.rs index 966106969943a..959cb14f8c9ad 100644 --- a/tests/ui/reborrow/pin_mut.rs +++ b/tests/ui/reborrow/pin_mut.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -fn method(a: Pin<& mut ()>) {} +fn method(a: Pin<&mut ()>) {} fn main() { let a = &mut (); diff --git a/tests/ui/reborrow/pin_mut.stderr b/tests/ui/reborrow/pin_mut.stderr index 8a10e2d9af269..64e3f603e1110 100644 --- a/tests/ui/reborrow/pin_mut.stderr +++ b/tests/ui/reborrow/pin_mut.stderr @@ -11,8 +11,8 @@ LL | let _ = method(a); note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary --> $DIR/pin_mut.rs:3:14 | -LL | fn method(a: Pin<& mut ()>) {} - | ------ ^^^^^^^^^^^^^ this parameter takes ownership of the value +LL | fn method(a: Pin<&mut ()>) {} + | ------ ^^^^^^^^^^^^ this parameter takes ownership of the value | | | in this function diff --git a/tests/ui/reborrow/pin_mut_coerce_shared.rs b/tests/ui/reborrow/pin_mut_coerce_shared.rs index fd529195fe01e..06af0b765d046 100644 --- a/tests/ui/reborrow/pin_mut_coerce_shared.rs +++ b/tests/ui/reborrow/pin_mut_coerce_shared.rs @@ -10,5 +10,4 @@ fn main() { //~| NOTE arguments to this function are incorrect //~| NOTE types differ in mutability //~| NOTE expected struct `Pin<&()>` - //~| NOTE found struct `Pin<&mut ()>` } From 413f095a85adb21331f6e64cf030ead124a6ba07 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:37:19 -0700 Subject: [PATCH 0933/1900] this ice now requires -Zvalidate-mir also slightly minimized the test --- tests/crashes/120016.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/crashes/120016.rs b/tests/crashes/120016.rs index 7eda330e7adea..12f54dbc3d92c 100644 --- a/tests/crashes/120016.rs +++ b/tests/crashes/120016.rs @@ -1,19 +1,19 @@ //@ known-bug: #120016 -//@ compile-flags: -Zcrate-attr=feature(const_async_blocks) +//@ compile-flags: -Zvalidate-mir //@ edition: 2021 -#![feature(type_alias_impl_trait, const_async_blocks)] +#![feature(type_alias_impl_trait)] struct Bug { V1: [(); { - type F = impl std::future::Future; + type F = impl Sized; #[define_opaque(F)] fn concrete_use() -> F { - //~^ ERROR to be a future that resolves to `u8`, but it resolves to `()` - async {} + //~^ ERROR + 1i32 } - let f: F = async { 1 }; - //~^ ERROR `async` blocks are not allowed in constants + let f: F = 0u32; + 1 }], } From 4e9716fbc5fb0f85d838ee9350ade2be5c2a6201 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 14 Sep 2025 10:30:29 -0400 Subject: [PATCH 0934/1900] Update CURRENT_RUSTC_VERSION post-bump --- compiler/rustc_feature/src/accepted.rs | 6 +- compiler/rustc_feature/src/removed.rs | 4 +- compiler/rustc_feature/src/unstable.rs | 18 +++--- library/alloc/src/boxed.rs | 2 +- library/alloc/src/collections/btree/map.rs | 10 +-- library/alloc/src/collections/btree/set.rs | 10 +-- library/alloc/src/rc.rs | 2 +- library/alloc/src/sync.rs | 2 +- library/core/src/any.rs | 2 +- library/core/src/array/mod.rs | 6 +- library/core/src/cell.rs | 4 +- library/core/src/iter/adapters/chain.rs | 2 +- library/core/src/iter/adapters/mod.rs | 2 +- library/core/src/iter/mod.rs | 2 +- library/core/src/iter/traits/accum.rs | 2 +- library/core/src/net/ip_addr.rs | 12 ++-- library/core/src/num/int_macros.rs | 56 ++++++++-------- library/core/src/num/uint_macros.rs | 64 +++++++++---------- library/core/src/ptr/mod.rs | 4 +- library/core/src/str/mod.rs | 8 +-- library/core/src/sync/atomic.rs | 14 ++-- library/core/src/time.rs | 8 +-- library/std/src/ffi/os_str.rs | 2 +- library/std/src/os/windows/ffi.rs | 2 +- library/std/src/panic.rs | 2 +- library/std/src/path.rs | 24 +++---- .../src/platform-support/apple-ios-macabi.md | 2 +- .../rustc/src/platform-support/apple-ios.md | 2 +- .../feature-gate-sanitize.stderr | 2 +- 29 files changed, 138 insertions(+), 138 deletions(-) diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 6af4cfb0e562e..364a1202b05c2 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -205,7 +205,7 @@ declare_features! ( (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions /// for functions with varargs. - (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), + (accepted, extended_varargs_abi_support, "1.91.0", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. @@ -400,7 +400,7 @@ declare_features! ( /// Allows use of `&foo[a..b]` as a slicing syntax. (accepted, slicing_syntax, "1.0.0", None), /// Allows use of `sse4a` target feature. - (accepted, sse4a_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (accepted, sse4a_target_feature, "1.91.0", Some(44839)), /// Allows elision of `'static` lifetimes in `static`s and `const`s. (accepted, static_in_const, "1.17.0", Some(35897)), /// Allows the definition recursive static items. @@ -414,7 +414,7 @@ declare_features! ( /// Allows the use of `#[target_feature]` on safe functions. (accepted, target_feature_11, "1.86.0", Some(69098)), /// Allows use of `tbm` target feature. - (accepted, tbm_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (accepted, tbm_target_feature, "1.91.0", Some(44839)), /// Allows `fn main()` with return types which implements `Termination` (RFC 1937). (accepted, termination_trait, "1.26.0", Some(43301)), /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937). diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index e37fc6b7bfcce..32115535e9941 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -192,7 +192,7 @@ declare_features! ( (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), // Allows the use of `no_sanitize` attribute. /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` - (removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), + (removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate /// `STABLE_REMOVED` list because it, uniquely, was once stable but was /// then removed. But there was no utility storing it separately, so now @@ -203,7 +203,7 @@ declare_features! ( (removed, object_safe_for_dispatch, "1.83.0", Some(43561), Some("renamed to `dyn_compatible_for_dispatch`"), 131511), /// Allows using `#[omit_gdb_pretty_printer_section]`. - (removed, omit_gdb_pretty_printer_section, "CURRENT_RUSTC_VERSION", None, None, 144738), + (removed, omit_gdb_pretty_printer_section, "1.91.0", None, None, 144738), /// Allows using `#[on_unimplemented(..)]` on traits. /// (Moved to `rustc_attrs`.) (removed, on_unimplemented, "1.40.0", None, None, 65794), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93e5588146e14..6ef0df72365ec 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -327,7 +327,7 @@ declare_features! ( (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, movrs_target_feature, "1.88.0", Some(137976)), - (unstable, nvptx_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (unstable, nvptx_target_feature, "1.91.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)), @@ -471,7 +471,7 @@ declare_features! ( /// Allows deref patterns. (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Allows deriving the From trait on single-field structs. - (unstable, derive_from, "CURRENT_RUSTC_VERSION", Some(144889)), + (unstable, derive_from, "1.91.0", Some(144889)), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. (unstable, doc_auto_cfg, "1.58.0", Some(43781)), /// Allows `#[doc(cfg(...))]`. @@ -481,7 +481,7 @@ declare_features! ( /// Allows `#[doc(masked)]`. (unstable, doc_masked, "1.21.0", Some(44027)), /// Allows features to allow target_feature to better interact with traits. - (incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)), + (incomplete, effective_target_features, "1.91.0", Some(143352)), /// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }` (incomplete, ergonomic_clones, "1.87.0", Some(132290)), /// Allows exhaustive pattern matching on types that contain uninhabited types. @@ -554,9 +554,9 @@ declare_features! ( /// Allows fused `loop`/`match` for direct intraprocedural jumps. (incomplete, loop_match, "1.90.0", Some(132306)), /// Allow `macro_rules!` attribute rules - (unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)), + (unstable, macro_attr, "1.91.0", Some(83527)), /// Allow `macro_rules!` derive rules - (unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)), + (unstable, macro_derive, "1.91.0", Some(143549)), /// Give access to additional metadata about declarative macro meta-variables. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. @@ -613,7 +613,7 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows the use of raw-dylibs on ELF platforms (incomplete, raw_dylib_elf, "1.87.0", Some(135694)), - (unstable, reborrow, "CURRENT_RUSTC_VERSION", Some(145612)), + (unstable, reborrow, "1.91.0", Some(145612)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant @@ -627,13 +627,13 @@ declare_features! ( /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows the use of the `sanitize` attribute. - (unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)), + (unstable, sanitize, "1.91.0", Some(39699)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). (incomplete, specialization, "1.7.0", Some(31844)), /// Allows using `#[rustc_align_static(...)]` on static items. - (unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)), + (unstable, static_align, "1.91.0", Some(146177)), /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. @@ -645,7 +645,7 @@ declare_features! ( /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), /// Allows the use of target_feature when a function is marked inline(always). - (unstable, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574)), + (unstable, target_feature_inline_always, "1.91.0", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1c549f7b6ba16..5a63d90b95fa0 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1706,7 +1706,7 @@ impl Default for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "pin_default_impls", since = "1.91.0")] impl Default for Pin> where T: ?Sized, diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e3b53b2fe291a..9dfbbd913225a 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1456,7 +1456,7 @@ impl BTreeMap { /// assert_eq!(low.keys().copied().collect::>(), [0, 1, 2, 3]); /// assert_eq!(high.keys().copied().collect::>(), [4, 5, 6, 7]); /// ``` - #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "btree_extract_if", since = "1.91.0")] pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A> where K: Ord, @@ -1943,7 +1943,7 @@ impl Default for Values<'_, K, V> { } /// An iterator produced by calling `extract_if` on BTreeMap. -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1976,7 +1976,7 @@ pub(super) struct ExtractIfInner<'a, K, V, R> { range: R, } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl fmt::Debug for ExtractIf<'_, K, V, R, F, A> where K: fmt::Debug, @@ -1988,7 +1988,7 @@ where } } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl Iterator for ExtractIf<'_, K, V, R, F, A> where K: PartialOrd, @@ -2062,7 +2062,7 @@ impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { } } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl FusedIterator for ExtractIf<'_, K, V, R, F> where K: PartialOrd, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index e6b0a1f632321..6e6996bcbd69b 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1218,7 +1218,7 @@ impl BTreeSet { /// assert_eq!(low.into_iter().collect::>(), [0, 1, 2, 3]); /// assert_eq!(high.into_iter().collect::>(), [4, 5, 6, 7]); /// ``` - #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "btree_extract_if", since = "1.91.0")] pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, T, R, F, A> where T: Ord, @@ -1553,7 +1553,7 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet { } /// An iterator produced by calling `extract_if` on BTreeSet. -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1568,7 +1568,7 @@ pub struct ExtractIf< alloc: A, } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl fmt::Debug for ExtractIf<'_, T, R, F, A> where T: fmt::Debug, @@ -1581,7 +1581,7 @@ where } } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl Iterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, @@ -1601,7 +1601,7 @@ where } } -#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "btree_extract_if", since = "1.91.0")] impl FusedIterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index aed3357afbf4b..627e5c7f976a5 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2377,7 +2377,7 @@ impl Default for Rc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "pin_default_impls", since = "1.91.0")] impl Default for Pin> where T: ?Sized, diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a466b74944c5d..3a8695d34a80b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3636,7 +3636,7 @@ impl Default for Arc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "pin_default_impls", since = "1.91.0")] impl Default for Pin> where T: ?Sized, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 76ea2d18a82f4..3ab95438c3ff3 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -774,7 +774,7 @@ impl TypeId { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_type_id", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_type_id", since = "1.91.0")] pub const fn of() -> TypeId { const { intrinsics::type_id::() } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index d713e575b58d3..0dc10758a8560 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -49,7 +49,7 @@ pub use iter::IntoIter; /// ``` #[inline] #[must_use = "cloning is often expensive and is not expected to have side effects"] -#[stable(feature = "array_repeat", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "array_repeat", since = "1.91.0")] pub fn repeat(val: T) -> [T; N] { from_trusted_iterator(repeat_n(val, N)) } @@ -627,7 +627,7 @@ impl [T; N] { /// assert_eq!(strings.len(), 3); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "1.91.0")] pub const fn each_ref(&self) -> [&T; N] { let mut buf = [null::(); N]; @@ -658,7 +658,7 @@ impl [T; N] { /// assert_eq!(floats, [0.0, 2.7, -1.0]); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "1.91.0")] pub const fn each_mut(&mut self) -> [&mut T; N] { let mut buf = [null_mut::(); N]; diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7d4a66640b106..6aadb7a86cd19 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -705,8 +705,8 @@ impl Cell<[T; N]> { /// let cell_array: &Cell<[i32; 3]> = Cell::from_mut(&mut array); /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` - #[stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "as_array_of_cells", since = "1.91.0")] + #[rustc_const_stable(feature = "as_array_of_cells", since = "1.91.0")] pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index 3ebdf7b472796..0ece54554d464 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -60,7 +60,7 @@ impl Chain { /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(iter.next(), None); /// ``` -#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "iter_chain", since = "1.91.0")] pub fn chain(a: A, b: B) -> Chain where A: IntoIterator, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 6c6de0a4e5c98..1ff5093922b6d 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -32,7 +32,7 @@ mod zip; pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; -#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "iter_chain", since = "1.91.0")] pub use self::chain::chain; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index bc07324f5204c..c7e1c4ef767ba 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -404,7 +404,7 @@ pub use self::adapters::StepBy; pub use self::adapters::TrustedRandomAccess; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::adapters::TrustedRandomAccessNoCoerce; -#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "iter_chain", since = "1.91.0")] pub use self::adapters::chain; pub(crate) use self::adapters::try_process; #[stable(feature = "iter_zip", since = "1.59.0")] diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 3b805139ded17..375b5ef52859f 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -148,7 +148,7 @@ macro_rules! saturating_integer_sum_product { saturating_integer_sum_product!(@impls Saturating(0), Saturating(1), "The short-circuiting behavior of this implementation is unspecified. If you care about \ short-circuiting, use [`Iterator::fold`] directly.", - #[stable(feature = "saturating_iter_arith", since = "CURRENT_RUSTC_VERSION")], + #[stable(feature = "saturating_iter_arith", since = "1.91.0")], $(Saturating<$a>)*); ); } diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 9779fb8fe4d5e..a1bfd774710d5 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -631,8 +631,8 @@ impl Ipv4Addr { /// let addr = Ipv4Addr::from_octets([13u8, 12u8, 11u8, 10u8]); /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); /// ``` - #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_from", since = "1.91.0")] + #[rustc_const_stable(feature = "ip_from", since = "1.91.0")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 4]) -> Ipv4Addr { @@ -1478,8 +1478,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_from", since = "1.91.0")] + #[rustc_const_stable(feature = "ip_from", since = "1.91.0")] #[must_use] #[inline] pub const fn from_segments(segments: [u16; 8]) -> Ipv6Addr { @@ -2043,8 +2043,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_from", since = "1.91.0")] + #[rustc_const_stable(feature = "ip_from", since = "1.91.0")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 16]) -> Ipv6Addr { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 0d80c40fb2363..c3460a6409069 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -519,8 +519,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -609,8 +609,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add_unsigned(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -659,8 +659,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MIN + 2).strict_sub(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -749,8 +749,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MIN + 2).strict_sub_unsigned(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -799,8 +799,8 @@ macro_rules! int_impl { /// ``` should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_mul(2);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -906,8 +906,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -973,8 +973,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div_euclid(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1139,8 +1139,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_rem(-1);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1205,8 +1205,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_rem_euclid(-1);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1286,8 +1286,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_neg();")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1342,8 +1342,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = 0x1", stringify!($SelfT), ".strict_shl(129);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1517,8 +1517,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shr(128);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1693,8 +1693,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_abs();")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1762,8 +1762,8 @@ macro_rules! int_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_pow(2);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d68c7be9865b1..752498bfbd815 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -667,8 +667,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -762,8 +762,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add_signed(3);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -821,8 +821,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 0", stringify!($SelfT), ".strict_sub(1);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -946,8 +946,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = (", stringify!($SelfT), "::MAX).strict_sub_signed(-1);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1002,8 +1002,8 @@ macro_rules! uint_impl { "::MAX), Some(0));" )] /// ``` - #[stable(feature = "unsigned_signed_diff", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsigned_signed_diff", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_signed_diff", since = "1.91.0")] + #[rustc_const_stable(feature = "unsigned_signed_diff", since = "1.91.0")] #[inline] pub const fn checked_signed_diff(self, rhs: Self) -> Option<$SignedT> { let res = self.wrapping_sub(rhs) as $SignedT; @@ -1055,8 +1055,8 @@ macro_rules! uint_impl { /// ``` should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_mul(2);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1151,8 +1151,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1205,8 +1205,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div_euclid(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1353,8 +1353,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 5", stringify!($SelfT), ".strict_rem(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1409,8 +1409,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 5", stringify!($SelfT), ".strict_rem_euclid(0);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1694,8 +1694,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 1", stringify!($SelfT), ".strict_neg();")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1750,8 +1750,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shl(129);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1922,8 +1922,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shr(129);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2104,8 +2104,8 @@ macro_rules! uint_impl { /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_pow(2);")] /// ``` - #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_overflow_ops", since = "1.91.0")] + #[rustc_const_stable(feature = "strict_overflow_ops", since = "1.91.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2682,7 +2682,7 @@ macro_rules! uint_impl { /// /// assert_eq!((sum1, sum0), (9, 6)); /// ``` - #[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -2774,7 +2774,7 @@ macro_rules! uint_impl { /// #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` - #[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -2991,7 +2991,7 @@ macro_rules! uint_impl { /// 789_u16.wrapping_mul(456).wrapping_add(123), /// ); /// ``` - #[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -3057,7 +3057,7 @@ macro_rules! uint_impl { /// u32::to_le_bytes(0xcffc982d) /// ); /// ``` - #[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 625024373ef47..b29d267654252 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -975,7 +975,7 @@ pub const fn dangling_mut() -> *mut T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "const_exposed_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance(addr: usize) -> *const T { @@ -1016,7 +1016,7 @@ pub const fn with_exposed_provenance(addr: usize) -> *const T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "const_exposed_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance_mut(addr: usize) -> *mut T { diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 2e473d348b0b2..3a5efa7d83511 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -404,8 +404,8 @@ impl str { /// assert_eq!(closest, 10); /// assert_eq!(&s[..closest], "❤️🧡"); /// ``` - #[stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "round_char_boundary", since = "1.91.0")] + #[rustc_const_stable(feature = "round_char_boundary", since = "1.91.0")] #[inline] pub const fn floor_char_boundary(&self, index: usize) -> usize { if index >= self.len() { @@ -447,8 +447,8 @@ impl str { /// assert_eq!(closest, 14); /// assert_eq!(&s[..closest], "❤️🧡💛"); /// ``` - #[stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "round_char_boundary", since = "1.91.0")] + #[rustc_const_stable(feature = "round_char_boundary", since = "1.91.0")] #[inline] pub const fn ceil_char_boundary(&self, index: usize) -> usize { if index >= self.len() { diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1b4a54b1b7a78..30a42d4eb5e64 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2208,7 +2208,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_add(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_add(val.wrapping_mul(size_of::()), order) @@ -2252,7 +2252,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_sub(val.wrapping_mul(size_of::()), order) @@ -2286,7 +2286,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2321,7 +2321,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2371,7 +2371,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2420,7 +2420,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2467,7 +2467,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. diff --git a/library/core/src/time.rs b/library/core/src/time.rs index d205bc376f125..f721fcd6156cf 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -416,8 +416,8 @@ impl Duration { /// assert_eq!(6 * 60 * 60, duration.as_secs()); /// assert_eq!(0, duration.subsec_nanos()); /// ``` - #[stable(feature = "duration_constructors_lite", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_constructors_lite", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_constructors_lite", since = "1.91.0")] + #[rustc_const_stable(feature = "duration_constructors_lite", since = "1.91.0")] #[must_use] #[inline] pub const fn from_hours(hours: u64) -> Duration { @@ -444,8 +444,8 @@ impl Duration { /// assert_eq!(10 * 60, duration.as_secs()); /// assert_eq!(0, duration.subsec_nanos()); /// ``` - #[stable(feature = "duration_constructors_lite", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_constructors_lite", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_constructors_lite", since = "1.91.0")] + #[rustc_const_stable(feature = "duration_constructors_lite", since = "1.91.0")] #[must_use] #[inline] pub const fn from_mins(mins: u64) -> Duration { diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index a39565d215924..6c098034eea3b 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -137,7 +137,7 @@ impl OsString { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] pub const fn new() -> OsString { OsString { inner: Buf::from_string(String::new()) } } diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 345d5b74285e1..20e5383dc09e0 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -141,7 +141,7 @@ impl OsStrExt for OsStr { pub struct EncodeWide<'a> { inner: alloc::wtf8::EncodeWide<'a>, } -#[stable(feature = "encode_wide_debug", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "encode_wide_debug", since = "1.91.0")] impl fmt::Debug for EncodeWide<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 5e8d2f8e78ec7..1997785885d34 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -122,7 +122,7 @@ impl<'a> PanicHookInfo<'a> { /// ``` #[must_use] #[inline] - #[stable(feature = "panic_payload_as_str", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_payload_as_str", since = "1.91.0")] pub fn payload_as_str(&self) -> Option<&str> { if let Some(s) = self.payload.downcast_ref::<&str>() { Some(s) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 88d8a4f21cac5..718c7c2e3b1a6 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1191,7 +1191,7 @@ impl PathBuf { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] pub const fn new() -> PathBuf { PathBuf { inner: OsString::new() } } @@ -1594,7 +1594,7 @@ impl PathBuf { /// p.add_extension(""); /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); /// ``` - #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_add_extension", since = "1.91.0")] pub fn add_extension>(&mut self, extension: S) -> bool { self._add_extension(extension.as_ref()) } @@ -2103,7 +2103,7 @@ impl PartialEq for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &str) -> bool { @@ -2111,7 +2111,7 @@ impl cmp::PartialEq for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for str { #[inline] fn eq(&self, other: &PathBuf) -> bool { @@ -2119,7 +2119,7 @@ impl cmp::PartialEq for str { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for PathBuf { #[inline] fn eq(&self, other: &String) -> bool { @@ -2127,7 +2127,7 @@ impl cmp::PartialEq for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &PathBuf) -> bool { @@ -2724,7 +2724,7 @@ impl Path { /// /// [`Path::file_stem`]: Path::file_stem /// - #[stable(feature = "path_file_prefix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_file_prefix", since = "1.91.0")] #[must_use] pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) @@ -2888,7 +2888,7 @@ impl Path { /// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz")); /// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt")); /// ``` - #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_add_extension", since = "1.91.0")] pub fn with_added_extension>(&self, extension: S) -> PathBuf { let mut new_path = self.to_path_buf(); new_path.add_extension(extension); @@ -3405,7 +3405,7 @@ impl PartialEq for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for Path { #[inline] fn eq(&self, other: &str) -> bool { @@ -3414,7 +3414,7 @@ impl cmp::PartialEq for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for str { #[inline] fn eq(&self, other: &Path) -> bool { @@ -3422,7 +3422,7 @@ impl cmp::PartialEq for str { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for Path { #[inline] fn eq(&self, other: &String) -> bool { @@ -3430,7 +3430,7 @@ impl cmp::PartialEq for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq for String { #[inline] fn eq(&self, other: &Path) -> bool { diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md index c6f68f7a1e812..0d0acaa3bef28 100644 --- a/src/doc/rustc/src/platform-support/apple-ios-macabi.md +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -57,7 +57,7 @@ $ rustc --target aarch64-apple-ios-macabi your-code.rs ``` The target can be differentiated from the iOS targets with the -`target_env = "macabi"` cfg (or `target_abi = "macabi"` before Rust CURRENT_RUSTC_VERSION). +`target_env = "macabi"` cfg (or `target_abi = "macabi"` before Rust 1.91.0). ```rust if cfg!(target_env = "macabi") { diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index 3ac1470475468..5de87dc349eb5 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -73,7 +73,7 @@ $ cargo +nightly build -Zbuild-std --target armv7s-apple-ios The simulator variants can be differentiated from the variants running on-device with the `target_env = "sim"` cfg (or `target_abi = "sim"` before -Rust CURRENT_RUSTC_VERSION). +Rust 1.91.0). ```rust if cfg!(all(target_vendor = "apple", target_env = "sim")) { diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr index 513999636a95c..59e8b69de2e3d 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.stderr +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -4,7 +4,7 @@ error[E0557]: feature has been removed LL | #![feature(no_sanitize)] | ^^^^^^^^^^^ feature has been removed | - = note: removed in CURRENT_RUSTC_VERSION; see for more information + = note: removed in 1.91.0; see for more information = note: renamed to sanitize(xyz = "on|off") error[E0658]: the `#[sanitize]` attribute is an experimental feature From 201f299ef6039886042ad8dd7c76a4fbd1a77a3f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 15 Sep 2025 22:01:36 -0400 Subject: [PATCH 0935/1900] Apply cfg(bootstrap) replacement --- compiler/rustc_const_eval/src/lib.rs | 1 - compiler/rustc_index/src/idx.rs | 6 ------ compiler/rustc_lint_defs/src/builtin.rs | 11 ++++------- compiler/rustc_middle/src/lib.rs | 1 - compiler/rustc_span/src/lib.rs | 1 - src/bootstrap/src/core/sanity.rs | 4 ---- src/librustdoc/lib.rs | 1 - src/tools/clippy/clippy_lints/src/lib.rs | 1 - src/tools/clippy/tests/missing-test-files.rs | 1 - src/tools/miri/src/lib.rs | 1 - src/tools/miri/tests/panic/mir-validation.rs | 5 ----- tests/ui/track-diagnostics/track.rs | 7 +------ tests/ui/track-diagnostics/track.stderr | 6 +++--- 13 files changed, 8 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 8ace560d85d16..9c0ec4e51a0c3 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,7 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![cfg_attr(bootstrap, feature(strict_overflow_ops))] #![doc(rust_logo)] #![feature(array_try_map)] #![feature(assert_matches)] diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs index 9cd7134659c55..2fb2008f9a3dc 100644 --- a/compiler/rustc_index/src/idx.rs +++ b/compiler/rustc_index/src/idx.rs @@ -130,12 +130,6 @@ impl IntoSliceIdx for core::range::RangeFrom { impl IntoSliceIdx for core::range::RangeInclusive { type Output = core::range::RangeInclusive; #[inline] - #[cfg(bootstrap)] - fn into_slice_idx(self) -> Self::Output { - core::range::RangeInclusive { start: self.start.index(), end: self.end.index() } - } - #[inline] - #[cfg(not(bootstrap))] fn into_slice_idx(self) -> Self::Output { core::range::RangeInclusive { start: self.start.index(), last: self.last.index() } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3d0974d5d2809..939f3d088b12f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2309,10 +2309,10 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![cfg_attr(not(bootstrap), feature(sanitize))] + /// #![feature(sanitize)] /// /// #[inline(always)] - /// #[cfg_attr(not(bootstrap), sanitize(address = "off"))] + /// #[sanitize(address = "off")] /// fn x() {} /// /// fn main() { @@ -4837,16 +4837,13 @@ declare_lint! { /// /// ### Example /// - #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] - #[cfg_attr(bootstrap, doc = "```rust")] + /// ```rust,compile_fail /// #![doc = in_root!()] /// /// macro_rules! in_root { () => { "" } } /// /// fn main() {} - #[cfg_attr(not(bootstrap), doc = "```")] - #[cfg_attr(bootstrap, doc = "```")] - // ^ Needed to avoid tidy warning about odd number of backticks + /// ``` /// /// {{produces}} /// diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5023b2740eff4..754a258eef93d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -29,7 +29,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::direct_use_of_rustc_type_ir)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index e95b743b1ce29..35dbbe58db9a4 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,7 +17,6 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 91d80c96e4268..eaa9e3a6a3e9d 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -33,11 +33,7 @@ pub struct Finder { // // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ - "armv7a-vex-v5", - "riscv64a23-unknown-linux-gnu", // just a dummy comment so the list doesn't get onelined - "aarch64_be-unknown-hermit", - "aarch64_be-unknown-none-softfloat", "x86_64-unknown-motor", ]; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 5d8f8f4bed125..c4f24e09ddbfa 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/" diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c56fa257b0689..b0083b99f175f 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -7,7 +7,6 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(never_type)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 63f960c92fa32..9fff3132498d8 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -1,6 +1,5 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 7f5f25b9f66b2..1f82f154b0b3f 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(bootstrap, feature(strict_overflow_ops))] #![feature(abort_unwind)] #![feature(cfg_select)] #![feature(rustc_private)] diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs index b9097f2e8c533..2d0d530754d8c 100644 --- a/src/tools/miri/tests/panic/mir-validation.rs +++ b/src/tools/miri/tests/panic/mir-validation.rs @@ -9,11 +9,6 @@ // and we don't even get a regular panic; rustc aborts with a different exit code instead. //@ignore-host: windows -// FIXME: this tests a crash in rustc. For stage1, rustc is built with the downloaded standard -// library which doesn't yet print the thread ID. Normalization can be removed at the stage bump. -// For the grep: cfg(bootstrap) -//@normalize-stderr-test: "thread 'rustc' panicked" -> "thread 'rustc' ($$TID) panicked" - #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; diff --git a/tests/ui/track-diagnostics/track.rs b/tests/ui/track-diagnostics/track.rs index 9ce0a4a555acd..2d6d6170fe0b4 100644 --- a/tests/ui/track-diagnostics/track.rs +++ b/tests/ui/track-diagnostics/track.rs @@ -13,13 +13,8 @@ // top of this file are present, then assume all args are present. //@ normalize-stderr: "note: compiler flags: .*-Z ui-testing.*-Z track-diagnostics" -> "note: compiler flags: ... -Z ui-testing ... -Z track-diagnostics" -// FIXME: this tests a crash in rustc. For stage1, rustc is built with the downloaded standard -// library which doesn't yet print the thread ID. Normalization can be removed at the stage bump. -// For the grep: cfg(bootstrap) -//@normalize-stderr: "thread 'rustc' panicked" -> "thread 'rustc' ($$TID) panicked" - fn main() { - break rust + break rust; //~^ ERROR cannot find value `rust` in this scope //~| NOTE created at //~| ERROR `break` outside of a loop or labeled block diff --git a/tests/ui/track-diagnostics/track.stderr b/tests/ui/track-diagnostics/track.stderr index bc04ded379df8..76b4bd14f5cd6 100644 --- a/tests/ui/track-diagnostics/track.stderr +++ b/tests/ui/track-diagnostics/track.stderr @@ -1,7 +1,7 @@ error[E0425]: cannot find value `rust` in this scope --> $DIR/track.rs:LL:CC | -LL | break rust +LL | break rust; | ^^^^ not found in this scope | = note: -Ztrack-diagnostics: created at compiler/rustc_resolve/src/late/diagnostics.rs:LL:CC @@ -9,7 +9,7 @@ LL | break rust error[E0268]: `break` outside of a loop or labeled block --> $DIR/track.rs:LL:CC | -LL | break rust +LL | break rust; | ^^^^^^^^^^ cannot `break` outside of a loop or labeled block | = note: -Ztrack-diagnostics: created at compiler/rustc_hir_typeck/src/loops.rs:LL:CC @@ -17,7 +17,7 @@ LL | break rust error: internal compiler error: It looks like you're trying to break rust; would you like some ICE? --> $DIR/track.rs:LL:CC | -LL | break rust +LL | break rust; | ^^^^^^^^^^ | = note: -Ztrack-diagnostics: created at compiler/rustc_hir_typeck/src/lib.rs:LL:CC From 54669d7db41952d4c04d735d2c6702d1b2022fed Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 15 Sep 2025 22:04:38 -0400 Subject: [PATCH 0936/1900] Bump stage0 --- src/stage0 | 1000 ++++++++++++++++++++++++++-------------------------- 1 file changed, 500 insertions(+), 500 deletions(-) diff --git a/src/stage0 b/src/stage0 index a705cd1c760d6..6d2f0402f4acf 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,506 +13,506 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2025-08-05 +compiler_date=2025-09-16 compiler_version=beta -rustfmt_date=2025-09-05 +rustfmt_date=2025-09-16 rustfmt_version=nightly -dist/2025-08-05/rustc-beta-aarch64-apple-darwin.tar.gz=b9d8f74da46aeadb6c650a4ccfc3c2de08e229e4211a198fa2914103f09f579d -dist/2025-08-05/rustc-beta-aarch64-apple-darwin.tar.xz=493ed87c65bac5593c104373a3277ddc2330072796704e4b6800c531326da860 -dist/2025-08-05/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=6c20a3b2e4d3ef9a54b1fe4f50c71895d4d8557d6960f887ef4958c0f2a19eab -dist/2025-08-05/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=d6bffc013745c0d69f4cf7ab4f747f8ad885e3b1b77fa56c2e7de8808e278294 -dist/2025-08-05/rustc-beta-aarch64-pc-windows-msvc.tar.gz=3b6bf7e2aff93854346c1d0970cf207c049c17c5ea6ee299dcdb1fd92e996fc0 -dist/2025-08-05/rustc-beta-aarch64-pc-windows-msvc.tar.xz=8241fdc7c673499091700cfcfafe10f3b70bf91918a3b7204a73d2b28445cfa5 -dist/2025-08-05/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=36a093e6c99f227e293ebcaebc89e58166e3da7447d2499069c616610fb4e691 -dist/2025-08-05/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=7cad77f36dcabbe77d603b8b3b4bfa83488a3af2e9b815f698f544de148c77a8 -dist/2025-08-05/rustc-beta-aarch64-unknown-linux-musl.tar.gz=421980aa1ef5467f59c1a8ad43415d69057c9277fdc91d1d5c2b53c246e8bbe9 -dist/2025-08-05/rustc-beta-aarch64-unknown-linux-musl.tar.xz=0ba5b4cd4f5975a58cf86331de5a3c80fb2fd620d50797360a5af4ebf2bba575 -dist/2025-08-05/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=13b3e61ca606bfdf90d46a8873ca4f14fa07ad8bd77364f1a85f557f31ba7299 -dist/2025-08-05/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=f6a1991e55af549d6cd59ddbebb6d2289d90d1e37b2a9449d7831b4c117a54bf -dist/2025-08-05/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=a5533df1ae642bcea571aa6c2b5eda16a56c6cd597906081840bb7531f7b02a3 -dist/2025-08-05/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=2a7187ad6d0215f07d0472880e117556508780e37df6e2a1a7fdbb67fd8dc87e -dist/2025-08-05/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=3ae6e7d83ae3e1c5c8030ba93b9103addaf11f0f8807f1fbf813305d8e6a9188 -dist/2025-08-05/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=324d6de1a8e4d9c1dd13cc86665fd06cb167d4a3ea55607cd5353300733f480e -dist/2025-08-05/rustc-beta-i686-pc-windows-gnu.tar.gz=0bc4b50638818008e054af245d4e987ce5e55fdc56e19d31c18e8c0165f860ab -dist/2025-08-05/rustc-beta-i686-pc-windows-gnu.tar.xz=b0e5343207a9684d5efe81e536d23135df07bebd869dcad69c632811e1e37137 -dist/2025-08-05/rustc-beta-i686-pc-windows-msvc.tar.gz=c3231335a50402989d4e08857fd7194a3fe5384d2aa34153a25a3f2955a942ef -dist/2025-08-05/rustc-beta-i686-pc-windows-msvc.tar.xz=be578fcbfe32e40fd9688e618927ddcce88e7e08a279778d4c3399eb46e8ae29 -dist/2025-08-05/rustc-beta-i686-unknown-linux-gnu.tar.gz=96b2cdda499b63aadb76b37a4895623a4806c5c8db8f6edeaeb1b812d337192f -dist/2025-08-05/rustc-beta-i686-unknown-linux-gnu.tar.xz=f6496eabe0efcc09c7a4994cc4825e9c3e494c8ca7679cfcbd38a699438e1b74 -dist/2025-08-05/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=3403d7be2f49387149ce075b427f8eaa306066db9fd4ec69de61dc01f6834037 -dist/2025-08-05/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=8cb3a329ef3632afc1f709b9f9b99a2ab7f5c18590590ddacf64f1b3e5a3ff69 -dist/2025-08-05/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=9485ce37e99a6a53b5521683bec65d015a730a41c4c3f3baa19226edd211fc39 -dist/2025-08-05/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=450d91ad61a23f69560e53c7001aec047bd17fb4831bafb97e7226162bb3c0f4 -dist/2025-08-05/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=dc9ff117a7dd3e685c634e8668d78022f66f6e58dc449fbe5f398688ed2dae0a -dist/2025-08-05/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=7dbc936f94d531040601d6fc02d9db87335a1b7927637494c9e208a43f012010 -dist/2025-08-05/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=4a68557cf53063817ee651f5838ad2c3de93a647b9002fe3c82f0372cbd71708 -dist/2025-08-05/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=f5ae9c4ba17d2941e8fa947d9712a8db8dbbdecbcfa4330988d02b9299fbc814 -dist/2025-08-05/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=ad33c48172c3b9abbd95b9dd3c1d38d5f6bc25fe84b95e492b6cdad27ef1bf19 -dist/2025-08-05/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=89d40fb02cded4e8d9b3e177f07e1b984481d493015a2532dfdb5b8aaf5ffa94 -dist/2025-08-05/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=a5ab7a321363502609a4ec5464be96629c693395503c8ce285990db3c556a775 -dist/2025-08-05/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=7e3b5afc7858857d5840bd6e788fd88449555e2fbe4c9a206ee3552e61dd79b0 -dist/2025-08-05/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=fde633217cf77d6fc6f901d84619f681a1fd1e42be75960809bab4806528a451 -dist/2025-08-05/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=69541c68f31a401229d770ef2ea6d032104fe928db845eae23f04c40db021a8d -dist/2025-08-05/rustc-beta-s390x-unknown-linux-gnu.tar.gz=dc257bbab4790dbc3f9df70853eadce198dd5f6f47c8e830254ee2b21c6c79a3 -dist/2025-08-05/rustc-beta-s390x-unknown-linux-gnu.tar.xz=d5a9d5c555241b231eacc0584db0b540ee2982e8bd66cf8568064aab4beb0c94 -dist/2025-08-05/rustc-beta-sparcv9-sun-solaris.tar.gz=96b25f8ce6db2f9d9b7e8da75839d1bd4c97011bebd2544633ab2061e4460bb3 -dist/2025-08-05/rustc-beta-sparcv9-sun-solaris.tar.xz=88438d143441da62bf5415857aab3490697f7f413b7de41cd113bac1480ea7de -dist/2025-08-05/rustc-beta-x86_64-apple-darwin.tar.gz=f59175ef402489c1dd2f6a8984ecb66314240a8e4f7c55e5d92017fc025fa4ee -dist/2025-08-05/rustc-beta-x86_64-apple-darwin.tar.xz=21de96e49395252e6eb5b175ee794acad78ccd4ac081303a8da7ec84bf2b6ab1 -dist/2025-08-05/rustc-beta-x86_64-pc-solaris.tar.gz=164a9b76347131d78fb81519627ab50f13a313d8da13d09f7fa479a41c519458 -dist/2025-08-05/rustc-beta-x86_64-pc-solaris.tar.xz=0f91f1f4b8880944341d82894b830a70f8a60837687829e0ab9626f3281ddd1b -dist/2025-08-05/rustc-beta-x86_64-pc-windows-gnu.tar.gz=634711ae22708a5ae57bb2022fd22257664a8510b59975f4b4ad4b9a8acf475b -dist/2025-08-05/rustc-beta-x86_64-pc-windows-gnu.tar.xz=b72c0d3f5800e92b91d1361e52b28ff622aeb1f1cbdff2efd22c2f2a3315dd68 -dist/2025-08-05/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=668f3a8b48092721bb6e1a01440776d6b906850a11e1bcc37a50bb13ef8d7b04 -dist/2025-08-05/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=e06a183829e09448e76daaf8c317b52d11fac7d16ad191ef61b8f7a806eb9414 -dist/2025-08-05/rustc-beta-x86_64-pc-windows-msvc.tar.gz=564849501d1e41fe776f5443b87c9500621adef97eff05d2c03d539ea3c886da -dist/2025-08-05/rustc-beta-x86_64-pc-windows-msvc.tar.xz=6a1c1cc47c0e8b6aaad215d4d4e8b770b96336e9f2a0317fcf0511b662966bfd -dist/2025-08-05/rustc-beta-x86_64-unknown-freebsd.tar.gz=560bcc4d150f4e22f5598d61fce86c9baeda1b57bda837270f7cac78cfc12b19 -dist/2025-08-05/rustc-beta-x86_64-unknown-freebsd.tar.xz=cdfe207645068b4659b0f979cae177723c5f211084f45ae9180b2d93ee83fce6 -dist/2025-08-05/rustc-beta-x86_64-unknown-illumos.tar.gz=5a362ca0c686b0e0666824df3f304ec49d7d419abb08473fece69d41e96ee625 -dist/2025-08-05/rustc-beta-x86_64-unknown-illumos.tar.xz=fb4e7b12b5223545f6fd57754744f03dd6807b6801e97f9f0fe07bc371efed62 -dist/2025-08-05/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=74225a1889120c0174a056e7ca7656f38e8788137ee3d29df857567ae0605692 -dist/2025-08-05/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=2d37542e88f84a0501841e12865262142fec0efef9ca729d26a3c333f16e465d -dist/2025-08-05/rustc-beta-x86_64-unknown-linux-musl.tar.gz=156eabc89e1ee9558b9de6f60b1bc47c81ab33ae20fa946c4ad4e32b7f30c221 -dist/2025-08-05/rustc-beta-x86_64-unknown-linux-musl.tar.xz=0cf9e649b9020fcfd25282ae1edb1ac59560b7d6d0f79ff7ff3b62871ff25d86 -dist/2025-08-05/rustc-beta-x86_64-unknown-netbsd.tar.gz=8db86a95b22efc2ff2f344e301171813375ccfd2aacad61d3aa84a63f573647a -dist/2025-08-05/rustc-beta-x86_64-unknown-netbsd.tar.xz=68c10c6431b4433d4de5d24a9bb6ebabe99769b077cdd80ab5e0ee67a273035e -dist/2025-08-05/rust-std-beta-aarch64-apple-darwin.tar.gz=872e61e6d6915c02de0b9437b910f6f37f5e11c83347bbe2284a59c31aa27ac3 -dist/2025-08-05/rust-std-beta-aarch64-apple-darwin.tar.xz=748516af852836f06efa927cc96bdd2ad6b012d0262e87bdec97a112212cc24a -dist/2025-08-05/rust-std-beta-aarch64-apple-ios.tar.gz=2c1cf24819ec790d124c7ace59c12a903250eefdad6362b40c779a97b732459e -dist/2025-08-05/rust-std-beta-aarch64-apple-ios.tar.xz=b2fd6df9653b6c0bc13d4331355b3b9a4756886ba46d6c744687bf7bbd8e4630 -dist/2025-08-05/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=c266b75c66ea17b174ce8a746bbad78bca58bd72c3cdda603f20a868f9b3b00c -dist/2025-08-05/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=da5558b25c82a5fc1b66786b035212c5d0df2d4124da3e581e15636f29547dd0 -dist/2025-08-05/rust-std-beta-aarch64-apple-ios-sim.tar.gz=8441032f41e142faebe78e84501344180447121602a2000d1310d7a716cf3695 -dist/2025-08-05/rust-std-beta-aarch64-apple-ios-sim.tar.xz=48713adfa5605addd97e13312f6bc4cf103c06623f67705732684b51d0bed8b1 -dist/2025-08-05/rust-std-beta-aarch64-linux-android.tar.gz=1eb2bbc5fac670aa5867546f1caf1378591d5c0d3a3800ca1dd052645fea0cd6 -dist/2025-08-05/rust-std-beta-aarch64-linux-android.tar.xz=ae5e8aedcc5c8bf719e8ed788d52cc69daf64dfcf878e8497b45454c1c582c56 -dist/2025-08-05/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=bedf5d7663acb4e1afed9dea4b5b8e9b7f0e0dd68e311d3597819ddd028576f7 -dist/2025-08-05/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=e19fd9dc6d9e774e30a9e4a16ac5d6d1fd3300eb880c09f6b61cc63d52370629 -dist/2025-08-05/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=8ad111b1e8e19fdc5bd33d82a1f6d88b9c1809e27531af9d9fed2e37f75edeb4 -dist/2025-08-05/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=332976524e42b4d7b2763a28cae3ebbc3eec03465c098df26e81242294277de4 -dist/2025-08-05/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=92f4737a1775c1fed9311b8949950c6606ca34d272f787d222923555fb69f98b -dist/2025-08-05/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=2f1ebaa974dc3c6b39c43e38cf6d9f5c6fba0d46229a0423be676d817905850c -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=798dafd6f581f367dfed9763b62b35e77466f87ae8252a52f503f1c1bf58f1a5 -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=c4434727604a2773f73e8919a6e967c7c487d75684514cacbe59fb2d6a5f0d29 -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=c48d4e44b0e94f341e7ab2f9d47b08280930152a53dff20d6c9140739fbd2898 -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=8cac54d6a39c13dd0f38fde523a852c5db7f61a7f05b3e3ad0f78c7f59513d02 -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=5c3d7ea7ac6ab60311fb49c5a2f04a92266bc8a675d7f333219177a91b400f9b -dist/2025-08-05/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=f1f9f8a71fc5903bf720d39bedaadab16363b37f9e99817d7cf27b7122ad1ad0 -dist/2025-08-05/rust-std-beta-aarch64-unknown-none.tar.gz=ceffb671e87556b304e63cf01572e1cad8c8cfa0b33ccd1a373b033c60696376 -dist/2025-08-05/rust-std-beta-aarch64-unknown-none.tar.xz=a9a9b17f2b4fdf45f46359726e0c28f6a1289a7abf81fdbe1ae180b2f550aa60 -dist/2025-08-05/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=9f6f6a121e3b19b7b3b2c85dcd13544c12af18cc5544403e29ea8bbd5b13fecc -dist/2025-08-05/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=5b0f0059dd5d837fad74aaf2971fb135229b030a832268106be512557cc7a611 -dist/2025-08-05/rust-std-beta-aarch64-unknown-uefi.tar.gz=67166c7d6d7210ca97c3610abfa126234653d0e26658bbea64d574852fad04fe -dist/2025-08-05/rust-std-beta-aarch64-unknown-uefi.tar.xz=05f72e7c0ebbf7b41bf3e773bbbc073ca9c71417a80dec8f3916dafbe0cdcf7b -dist/2025-08-05/rust-std-beta-arm-linux-androideabi.tar.gz=bdf103a29035659972f35bb9060ba8df5ca9b7b068e3c094d758331a5e667144 -dist/2025-08-05/rust-std-beta-arm-linux-androideabi.tar.xz=8fa8b6994b4e04fec77a6657db0fde4e4cb9336466ce0c4f3e2b154709969c93 -dist/2025-08-05/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=4298510905c958e45291c2fbc4c54bfed9fdafbd48636896fe00a73e94f700ba -dist/2025-08-05/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=79e0e393f5286429755faee738ed110fb1cc51b83aec3c94194e903f0b938d73 -dist/2025-08-05/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=37278a621e2449b9030045c174c71f3ddf74e70b49b5f247c36fea1b1654979f -dist/2025-08-05/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=3ac094e855f7593a22a56ec40923384a0e25265645d05b2a46dde2612d9c6cf9 -dist/2025-08-05/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=b5bc7d83a3c59764cdc169ae349e01cd052b8ab054eb13b4f2a1cd02ddd7fd6c -dist/2025-08-05/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=2f8558fee897543da620531e500f3a73c5aac4ea815b7bd418945103dedde530 -dist/2025-08-05/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=2e812427b5169e7de22b720776208ae92f9075c5509f6b9ad8666b9866232735 -dist/2025-08-05/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=e544363209177357386f220d6c4101b1d86d84c03e51254ff459ca42ef187107 -dist/2025-08-05/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=778c947235bb0023ca26dc0592e4d3cb9ad9665c3316d57822c173ba2b5e231e -dist/2025-08-05/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=3a5bf7620e1b24e1f72968f5cc28cc58acc9b5739f2c08f5c4b9e449d8c551a1 -dist/2025-08-05/rust-std-beta-armebv7r-none-eabi.tar.gz=fcace82dc77156a6e7c658fc5abe4992075cfe822fb18a1edfca1967102a6adc -dist/2025-08-05/rust-std-beta-armebv7r-none-eabi.tar.xz=9616372693902e89b55de55b62009a59baccb11ccb63710a475856deca70655c -dist/2025-08-05/rust-std-beta-armebv7r-none-eabihf.tar.gz=2c596de7d22a4762908676d4e048f5075cbf2d66c5f7a03afb96e709f2d080ca -dist/2025-08-05/rust-std-beta-armebv7r-none-eabihf.tar.xz=b1ff9e3fe11acc22fa5ad46530dff62bfceac9df6fcbd3da7999535a00dd2e3e -dist/2025-08-05/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=e033e4bfc11a5bcb7f0645426fac899f7d071236a228300ca2022935997b17fd -dist/2025-08-05/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=919587b40e2bc6c6e8f496244c357c04d5e53b8adb9b3f274432943fd789a1a4 -dist/2025-08-05/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=392a1f0528e4b783e5fd0be74efbec58eb3b0ebd69c3855675301ebf96b76c4a -dist/2025-08-05/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=183144eb89cc1a035c04d50c4060e159ca5099fec71f4f25801a924fa013d04a -dist/2025-08-05/rust-std-beta-armv7-linux-androideabi.tar.gz=f968b761773b76f151b39dce0f3757f59eee2d8b70373d1419e0986429885d7d -dist/2025-08-05/rust-std-beta-armv7-linux-androideabi.tar.xz=fecda678541a151b76f3874e710e875a662a9165eaf1cf12b081ea55ea18a38b -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=300d7e8adaad86ddeff643109d0c83a87e41a056171f9d48b0b6108719003325 -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=acb8f61c97efae6e95aaabe1cab1300bc3cc3a1dc2066c7e2376ad6a9994971c -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=5e90333cb68f3161f8cb30e69d4ebe46f6653998651c72a87a795ab02c11dade -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=355bc516a7a6454eaacc66eadaa4d640cb3ffc7b5400a01bb4bdccf4470ae650 -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=8daa2c4c4dd9e8a1b9ee8a60f2cab0dab81aaf1e7a9d732093979ccdeac8bf60 -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=52b78d85f8e9e19da83504bb523aecf7b641d15c1de2f9b988bedf52912636d4 -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=c63000f15b52881c20f40cf1468afd4f3a2a6a84e944357fe6532c7d0e281b3a -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=53fc3486e4d805386c1ac4d6a1007a9b5461ae606c9c820951b720b45dc8f35c -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=315fc371ac2cddeb65c87bd50369e28247c16ca55fdab527e88899e01adc9efe -dist/2025-08-05/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=d7999aff0a39c63709977f5c18d79b575f8bfb467fbacf4f1b41cd26b52a6701 -dist/2025-08-05/rust-std-beta-armv7a-none-eabi.tar.gz=0707721586e8929811c2904e4136d31f5c415e3f00bfa88dbb920360aa9deea9 -dist/2025-08-05/rust-std-beta-armv7a-none-eabi.tar.xz=bedfd1a808f758f5088ea0fcb746d3ccf11730945e2b07220e93829c0d5c472a -dist/2025-08-05/rust-std-beta-armv7r-none-eabi.tar.gz=315fadb161b3be540b7a276ebe15b1f8d4bdf73b46c1633e39f698014aca8eb1 -dist/2025-08-05/rust-std-beta-armv7r-none-eabi.tar.xz=4bc2fcd9dee2ee44914da0e6af3763c7ddcbe3ebd9fb20c1d552a0226cd877d7 -dist/2025-08-05/rust-std-beta-armv7r-none-eabihf.tar.gz=9c8be30130709ff94a9718091559a752530f0eeb21be80fc3cca0665e85ae0dc -dist/2025-08-05/rust-std-beta-armv7r-none-eabihf.tar.xz=f0dd0bd30ed70c3a022016d8bbed54a6e942571f2e4c773bd8b4198d7dccdb5c -dist/2025-08-05/rust-std-beta-i586-unknown-linux-gnu.tar.gz=ca6c3b8af1c44deaf7dce9e8d4c8786a5801226b30beaa646067d393eeaa0ee8 -dist/2025-08-05/rust-std-beta-i586-unknown-linux-gnu.tar.xz=4e96f0e5f2e3eda60ca2b6d9ce234ae74c5eb2412a7e2c0c571eaf792dca6e28 -dist/2025-08-05/rust-std-beta-i586-unknown-linux-musl.tar.gz=60c736e3ac2aa5b9ceedcd73e39efa12bc9b889ef85f548170f80fbf2b05dfa0 -dist/2025-08-05/rust-std-beta-i586-unknown-linux-musl.tar.xz=fe636d893e38c32a163a88ece160d5b5ea61a3fa63463d4e4f425d229c2927f1 -dist/2025-08-05/rust-std-beta-i686-linux-android.tar.gz=9e62d61041187a91b74c81fe77cd6802a7e38c5a535412c71850426f6a48f37c -dist/2025-08-05/rust-std-beta-i686-linux-android.tar.xz=862d3d5442fb011b917f565aaadb201c22e7b2ecd6a68c0c410b3335741c1c22 -dist/2025-08-05/rust-std-beta-i686-pc-windows-gnu.tar.gz=05a8da51c477e2c2ce4ea12d41c8afaaf0d226a6b933b6c55fd3584b39103366 -dist/2025-08-05/rust-std-beta-i686-pc-windows-gnu.tar.xz=cf15b3d2011ceb57064d0b2285defee7df8628c3bf2b95f7f2ac92a449546d4f -dist/2025-08-05/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=fe11b777eae25d823f40352e47272222c2de8edc2d271eb4f6e7ff508efa198d -dist/2025-08-05/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=a7bb6f223e3542e613eaa7f2b9d9974be71cd2debf8426faa50cfb63fde681fd -dist/2025-08-05/rust-std-beta-i686-pc-windows-msvc.tar.gz=5fbd709698d80b3227a8bc6cbecfc597e99dede3824c751e1d166cac2c5862dc -dist/2025-08-05/rust-std-beta-i686-pc-windows-msvc.tar.xz=92fd2a6a5dbe53f68e9ba3ce40cd3beea95ba9d6a2f1293f7f3d917f34739a66 -dist/2025-08-05/rust-std-beta-i686-unknown-freebsd.tar.gz=182acad6cea45855f66646d437ee44ddb1f85c2c998cc5c7a4bbb025ca0d9da9 -dist/2025-08-05/rust-std-beta-i686-unknown-freebsd.tar.xz=53f8bfaabff1dbc47929a99a92025a31c1e272bf6a8091c4f95d33557dfe9ea1 -dist/2025-08-05/rust-std-beta-i686-unknown-linux-gnu.tar.gz=b80dd4e77c56256f7a7f837bf84129d19e1a4aa08a0ca7e2881402371a7e4395 -dist/2025-08-05/rust-std-beta-i686-unknown-linux-gnu.tar.xz=52efb657f28303b0747cf281c972653abfbeb4bf6d0b841f8bbab7f08c5d7310 -dist/2025-08-05/rust-std-beta-i686-unknown-linux-musl.tar.gz=323abc9766231dca10c225b3ec567c694c0ff6f6eddcc30d728a0f08aa6d2186 -dist/2025-08-05/rust-std-beta-i686-unknown-linux-musl.tar.xz=8998ce49f1be28bcd837831f1d7b79b0b339bc74ced42adb6d997ed016e90e88 -dist/2025-08-05/rust-std-beta-i686-unknown-uefi.tar.gz=07261ce98c95839b8714f40e07cbffa32c10fc7c59394dc87b07e144564c5fef -dist/2025-08-05/rust-std-beta-i686-unknown-uefi.tar.xz=fb6eb023b9722a44e63bcd48fd88c61cf41453842a211107c84039c6883409e5 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=d0a52888f5ef3107ecdbf28b918eb516a9176ae8695079a81a22d1b7ca0e29bd -dist/2025-08-05/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=c14a477558a6c924e05e1b58bd9df60f5e4966c7f0da294dd38e3bd89c1dc5f6 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=bbb93219393948d055df44edcdfff4b03ca251205c545d6f1bd53ade5f314d52 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=48b437a8afe240828c0377a6baa276500f125661f1bc888ebd1ea546f0497f4a -dist/2025-08-05/rust-std-beta-loongarch64-unknown-none.tar.gz=fcec6b60a1a22dcd04b8409d38103496d76bb4297ded9f1f092743bd05f0bd54 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-none.tar.xz=2f4aacb734b5a1dd522b4836035ab213db675508b9ff9a1bdc0c2df3ea9d39d1 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=aad8445e9a5deb4a466ebed84cab101bbe8ef49530315c0349d93e2062ae65a8 -dist/2025-08-05/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=906e1cbd1e3b22eb5c378417646baf18b00acb274ee4198ea59ea356f4f1a0da -dist/2025-08-05/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=b0d7e8daae2eff0c660b6e01bc76258550b9bfbdbf95c104019db7c797339ef5 -dist/2025-08-05/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=c0bffdbb4de90486cad1a26df997006c142f1acc7ed39419975c10b0d09c6217 -dist/2025-08-05/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=3c845cade37fe2b1cfe3087c69f9ecb3e0eec32d2558701c677d4c21ea9e08db -dist/2025-08-05/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=15830d827258f6c3386fa09da66e06ff0460098b46432e28b4e96bd36d61e787 -dist/2025-08-05/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=4b63d9260253f1d3a0168ed367792284584b87aa936fc76262e9fe0ad83c7fa1 -dist/2025-08-05/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=dfba70ad94524437fc8ec5e4684239eceb76678112776915f02502b80cb0afac -dist/2025-08-05/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=3d96bebe611a0167a43060312cbfa2fe4000b9949772ee44ffd27226acd006c8 -dist/2025-08-05/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=44e95c2756f52129b8bd21d7d4ad7ec8e05e43192f3bc894cba4a371b579a6d8 -dist/2025-08-05/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=ca5b7c47b63fa8e005078cb73d111462c438b764909ca106933837ac93d5780f -dist/2025-08-05/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=b102756451c18003ad1b132d25d333ed1a0e4959b87d2904a6e407fc02a7e422 -dist/2025-08-05/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=a592e995b40d3b1eb69cb1f7cd3c100713ea092742ab6ec5769e8df8551ffa16 -dist/2025-08-05/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=af9cd91be3d667cf31b535862882537a406f49932f58308f283228b20fc7bd76 -dist/2025-08-05/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=bf9e9d0f8c4ce8041c6e628e1b637ac0cb316f865f97a43cf2bf522e255c5ec1 -dist/2025-08-05/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=c3e56d71c1586559212f701508ee94f0bfa7801e7d2fdc62c062dcce8a0d040d -dist/2025-08-05/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=068023ed1f1dea01f2523f3b2b9ef41b22a301ceafa0526ebaa757481d14408a -dist/2025-08-05/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=1e3db6625ebb12a8c735cf4e8658a33bac7bca461de1e516860843d50027ee7d -dist/2025-08-05/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=aeb986eef375fa1ebb179668c6778c587d1af8b9e1ff50e5b56f9a3b48f1d8df -dist/2025-08-05/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=45d314189b9327645f6490628157fce32b7b59eccdf57676be0c31e1247f5385 -dist/2025-08-05/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=82d6e6549c80e62d392654693a28528f2ea540652f3ec0810b6646968cae6509 -dist/2025-08-05/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=805ffe0a6dfbc886f0ba93ac9ce796c110ea6d0a64b2d6209cdadd56cd2a570f -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=941c683ef7d013166c7f3439ee1229f80a367405f55bab9072dd12725364db6b -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=8b666222443b3954a7550b14ef919b7ab038e6a4a2355386e42c9acbe28f2024 -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=80f740bd004b98d5c090fe280ef5e372e4bff7a34dc2ba4940204cf02f50bb93 -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=b69ad00f5d60c63fa6d7b32c4d7006d195540d902f8390754c7db92a9221973d -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=ed52f0f9bac7b9d7ec49226eea471e44fecf0416608a5b169d35b12d009e9c1b -dist/2025-08-05/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=d4dc1b096b26814f14e1e23717cef10f3f63cdc6902e345916e015a99851cb69 -dist/2025-08-05/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=aa1de02a79f16bb4affb50c3ba0e719352c9925fc57f22f989eed3f7df1a8e5c -dist/2025-08-05/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=a36e2749e118f995417f0de9c9835db4b36f8ed6d76d8805510853984f648c5b -dist/2025-08-05/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=32e6e3672d3c379a1defb6c661eca6f2ce342784feaceafec004bdaa89a0b226 -dist/2025-08-05/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=a449f0392193ab8a48a30b0a8c0c57b0a02747ae8302d08b3be89d475f1b8291 -dist/2025-08-05/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=a4e0901c13d3c534e176fdf824a97e5a6ca66a198b73a132b957d41b1f198261 -dist/2025-08-05/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=64720eab59f7c958aadd360b8e2dc5696760d62e9f5f44daba890fc55a4fb6a1 -dist/2025-08-05/rust-std-beta-sparcv9-sun-solaris.tar.gz=8d5d3be06cfe5431b9a8e965fe06837efe531c365e8d46ee8cdc8e9da19099f0 -dist/2025-08-05/rust-std-beta-sparcv9-sun-solaris.tar.xz=913d7fc4aa75ac2175aa52891f9086d48065d96007885e0caf5feb628953a86d -dist/2025-08-05/rust-std-beta-thumbv6m-none-eabi.tar.gz=ca59366093c472f19381fdc71aacf6b3d659750a8a3bd8894191a42c8c3b82f9 -dist/2025-08-05/rust-std-beta-thumbv6m-none-eabi.tar.xz=e16dc610520f4748ffca99b235e58a544e7f97ca4cf99cbebbeb106ed4acffd1 -dist/2025-08-05/rust-std-beta-thumbv7em-none-eabi.tar.gz=08a281c1bd56149ebd05531fe405a621383ad440fcf273fec04e0792f325d669 -dist/2025-08-05/rust-std-beta-thumbv7em-none-eabi.tar.xz=a9f7eadfa375061835f139bbb870a5692b727de8a85fb8177d8fabd0588e28cd -dist/2025-08-05/rust-std-beta-thumbv7em-none-eabihf.tar.gz=f996d8b1aae894af11407ac90c277e161acd58378307548ffaa2fa0a4314f3d7 -dist/2025-08-05/rust-std-beta-thumbv7em-none-eabihf.tar.xz=48b9e7d257ad1fac0b23b3a7d6b3ae8afb5dd19db7b5dd2a59ddfe51364db72f -dist/2025-08-05/rust-std-beta-thumbv7m-none-eabi.tar.gz=1061c6b8794aa4e1f66ff17d91934bb9711c6064362cca7bca1d7cdd4f9189cb -dist/2025-08-05/rust-std-beta-thumbv7m-none-eabi.tar.xz=974b1078724ac06757d7899fde62f623e61c86ac0853cdbf02a252b13383e55a -dist/2025-08-05/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=a4fd4047a744bea871a54af311f27a08b5f7c8f04e5e62f7abf292689378ab4f -dist/2025-08-05/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=19c31d2a0982689228eb58522ac7610d33cfcc1b5f72ee2e41e218695a49d09d -dist/2025-08-05/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=8b5e150d6950866734b025e58b3714c4acfe631785fc464e6fe3cbedd98709d0 -dist/2025-08-05/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=23ca585e084fb548488e17adaea92e16ac98340fe146073046d1bfbe6faa325f -dist/2025-08-05/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=d31bcb162cd2ee40a69acb2c201c07c233b8c2710bc07ad322263121f0d422db -dist/2025-08-05/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=67354653ab11222806f4a688c11be6dc80468785e14a8b58f2285a695c53c5a2 -dist/2025-08-05/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=ad61d8510d82ca1094a893879d7148446e2880dd1d172b9e8a420772b0b4611b -dist/2025-08-05/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=5c1ddf66949a40265effbc76ac3da59efb1bb3349dbe2a8037b8215375647fdb -dist/2025-08-05/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=4895c9a6659e831cdacd314ff2ca4c968fd368c6bf9308f334468cb07892ae56 -dist/2025-08-05/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=f64a05715457288b36dd16fcbbdd91816829b889047c277841f3f4972bcc6076 -dist/2025-08-05/rust-std-beta-wasm32-unknown-emscripten.tar.gz=3b144570ddc44868a6609f921028b23f994de337c54a96fccaf976abe4e2ceff -dist/2025-08-05/rust-std-beta-wasm32-unknown-emscripten.tar.xz=355a1bc09dd4163a416cb78e55ec998e95b8acbb9b072dbd3a8e34f5e95d3378 -dist/2025-08-05/rust-std-beta-wasm32-unknown-unknown.tar.gz=52213b11d29f02d4531495c9d35ee7022ef6b8400a8386b8728156b33d2a9eed -dist/2025-08-05/rust-std-beta-wasm32-unknown-unknown.tar.xz=1f282c355a1499fc2a212705700fbb8de7e568dbdc5d43d3c895af86fe9f735b -dist/2025-08-05/rust-std-beta-wasm32-wasip1.tar.gz=f40da6445acb1e854625a02b1078f670874e75d763168430d0ca17ef3b9bae26 -dist/2025-08-05/rust-std-beta-wasm32-wasip1.tar.xz=a0e59495bacc1bceaeec940273fcc6d1505c283de9e2a60ee7d492f2a7efec3d -dist/2025-08-05/rust-std-beta-wasm32-wasip1-threads.tar.gz=4b4eb08ab33ff2a300828c65a9636f32428dfec784bf115aa53856b5336d61d5 -dist/2025-08-05/rust-std-beta-wasm32-wasip1-threads.tar.xz=54ae55557d66f922112a42aa2c296841f5919907ccd81354f0dbe1b0517867f8 -dist/2025-08-05/rust-std-beta-wasm32-wasip2.tar.gz=b625337e6180ec57abbed063de5bf384949254c46a5fbbb12804a3dbd0d1c3a6 -dist/2025-08-05/rust-std-beta-wasm32-wasip2.tar.xz=5a098a042f5586e7e1b7444bf64edf3dcc535d75226fa44be420c0d42b90c25c -dist/2025-08-05/rust-std-beta-wasm32v1-none.tar.gz=b11ed27b745437b39ea9699f7fd5413bdb25019720569b9940f1cbac4849344c -dist/2025-08-05/rust-std-beta-wasm32v1-none.tar.xz=3dd3f07214429f36a088a89c3de7404659d1b584895ff5b7938845fd4a669f27 -dist/2025-08-05/rust-std-beta-x86_64-apple-darwin.tar.gz=14522f13786b81727646acfcb18c81b3f78b24bf522ebaf65adba416971d9939 -dist/2025-08-05/rust-std-beta-x86_64-apple-darwin.tar.xz=b78a1df21a97c25d9977a69bf778190fbf34947c6837f895aeeb53c870083438 -dist/2025-08-05/rust-std-beta-x86_64-apple-ios.tar.gz=a5d57bef3b09c4a4e6789d756cbec9e9459261ab70c94a406d4519eb2da992ec -dist/2025-08-05/rust-std-beta-x86_64-apple-ios.tar.xz=1b461aaf03e808d26bcae49417f04b71ad1432f266f0b25b3d6b26a67b7f8953 -dist/2025-08-05/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=6c7a3326abd3fb7c878af095699164237f97ce5827bd428d8aad5c9818b2098c -dist/2025-08-05/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=427684c9613ce04737837a594987bb1eb81d1d3f5ea2a1b19c2b76b3be32ab62 -dist/2025-08-05/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=2e10607e6eb7fb3168fe593f1d260b52ac578d590cc6788555346cf9bac9f586 -dist/2025-08-05/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=256331077a036bfed1650177cd1a886aeb4ce9aa9ce2a35f5450767f5e06aee6 -dist/2025-08-05/rust-std-beta-x86_64-linux-android.tar.gz=b5943cc4d10bf039d9b52e56713a99e8edb21d9de3655450d16c557c9013f47e -dist/2025-08-05/rust-std-beta-x86_64-linux-android.tar.xz=ceeb89fa082b98c8d50c043cfd2e4bb8ac1d98943859a75d74a555ffda8d0a5d -dist/2025-08-05/rust-std-beta-x86_64-pc-solaris.tar.gz=98d9d51da4b74a2a1899be7f0dd8d3c0f980fb4ce96fddd1c2dedb76e174984b -dist/2025-08-05/rust-std-beta-x86_64-pc-solaris.tar.xz=0fed1f0452475bf10d3ec0bfef12e9fe05bb117910d871f4099bdd4ca947d74b -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=f458eab66adc91aba026a434cab47bbbd98a9d5e7d0f5a1a1979e0e6a89c8e7e -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=bfdc3bb5b66a525281236b01513f49d96d644e4cd62ce89eb59a8179fe4707b0 -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=b543f21106bc3a72d31a5c49118553187cbb0d2e630ea943aa97d3ae5bb4c40f -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=9056c113ee03defb6cd33acbed9829712b57ef3606623169d28416be4110a31a -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=c66ff2e88c79f3fe574f9ef7822d5d2e6f73efe3ebe67c6bd35096622b668d1c -dist/2025-08-05/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=45765252e930a96badbf06eb04ec092bb989c0ce2067aaab52b7ddc72ea511b8 -dist/2025-08-05/rust-std-beta-x86_64-unknown-freebsd.tar.gz=9b3d68e86e0ce6a484bf615313f98bd289db73899a55cecfa5b7955b4b0878f4 -dist/2025-08-05/rust-std-beta-x86_64-unknown-freebsd.tar.xz=bd48292b8582167a5e89ebe521c9754495403968c184b925df8b2ec1da344fc3 -dist/2025-08-05/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=b4c6c046299391beb2f50eff198f4c9b6571b6c1748dd621bdd154694fffce3a -dist/2025-08-05/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=a30857c8f066191b64d7b52378fae8790814a251ca452c80710bd9a49c5c0c85 -dist/2025-08-05/rust-std-beta-x86_64-unknown-illumos.tar.gz=1ee4b264021b510342c2ed96da0dacf5cf1704874de3bf9380642433defb3e0a -dist/2025-08-05/rust-std-beta-x86_64-unknown-illumos.tar.xz=ca431d4426cfba2efd408b8822f9aeb0961d81373c6154a0b7eeb957abebc33b -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=ebba9fa476d5b0a42054a6b6ca51526efd7c2cf5ac34eb8af119bfa69f3f0a5c -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=d4498920cce484a8b3a5cdf8ee856d80cf1379f9782169340dfff2597b530598 -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=20c37745f3ee13c2c81dfc77a80919cc0448180f6be0be56d7fb5239e5651294 -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=dd319a6c381b372ba230d86bd07a089cd2431656c7c765f029e8e10d60bbd778 -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=d0c20b13113eb62c9a78a796418386d0352f4221095de272018af6d5ec6bd9f1 -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=d0e1001e8e5af571f0fd53115ec873091a33e4943dd27a16ccd74dcd8c71abce -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=4be537d5fb6c0d867a131539ef4b0872f9f6d175ba0517fed50b1d463c615157 -dist/2025-08-05/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=ca034c852a4c9099a062f55c5e479bd700f2ffd89a3b2c2c7354df54e41057a8 -dist/2025-08-05/rust-std-beta-x86_64-unknown-netbsd.tar.gz=11b5a73da1f560c218cecf9a71d6b2173df1fbe276e63e20e1e85f2dc48579bf -dist/2025-08-05/rust-std-beta-x86_64-unknown-netbsd.tar.xz=2de6a8850076e9c1edd8aa3e13902ebbc599da9637f88da347858007f8e5c212 -dist/2025-08-05/rust-std-beta-x86_64-unknown-none.tar.gz=dacb8aa48387ad15380a094104bbcfabdcdd5f88e189d9203fd3e3f466b92fa3 -dist/2025-08-05/rust-std-beta-x86_64-unknown-none.tar.xz=ce2eb95efe252de2ecbe619b3805b01ec84863a9b30330dc4ad5683d67d7c9d8 -dist/2025-08-05/rust-std-beta-x86_64-unknown-redox.tar.gz=bf28f90b1b24eabd80da75262bd260ee811ef30a1ba94bdeb7a005f132ceeead -dist/2025-08-05/rust-std-beta-x86_64-unknown-redox.tar.xz=99aa3603b7fdc84893a02e66a774e147439a1cfd77ba63818c58b11ae692058d -dist/2025-08-05/rust-std-beta-x86_64-unknown-uefi.tar.gz=75c57e4a9367a6fbee02f8857da2dd4bce8bd20c8946a3c2460a77cb95af0972 -dist/2025-08-05/rust-std-beta-x86_64-unknown-uefi.tar.xz=552c14c20d1f786c8350882a32618951de0a06e0636fa3b8d69f2ffab7e7561d -dist/2025-08-05/cargo-beta-aarch64-apple-darwin.tar.gz=4723292f91e645d3f86474ed55e52eae4f35af7458602d3da9d38b0a513cfeef -dist/2025-08-05/cargo-beta-aarch64-apple-darwin.tar.xz=d0150ce874376c41950966b0385f011ebbbd5ef4955deec7829d8ccb669e9e86 -dist/2025-08-05/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=fb0a8a8dff4d42f9491ed9a0223a9541bbaf8691c831b5536220494c479b21e3 -dist/2025-08-05/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=6bd35ea43ab877d84bff4b32b965371b942b10c6f6feabb3a5b481a4c84513fc -dist/2025-08-05/cargo-beta-aarch64-pc-windows-msvc.tar.gz=3437221155f338e81f55dea9d715b3958fe7d3a260d77d14725e62d0780bfc76 -dist/2025-08-05/cargo-beta-aarch64-pc-windows-msvc.tar.xz=94886636f7bf805809a8a1ac99b514036c5db1755ccfed61cb6cd01d57d244a3 -dist/2025-08-05/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=7f167793fc72f5fdb6bbed97e96684cfa089f9932d3a64239bc755fe7603e7a3 -dist/2025-08-05/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=1018ea99c4142db9fbf386547dee8396dc27d3d3608085a1b0b0e97e2d0172c7 -dist/2025-08-05/cargo-beta-aarch64-unknown-linux-musl.tar.gz=8e03af7a838e81c12c395ed76151aa6ead12b3e60effa3b0d775508118149058 -dist/2025-08-05/cargo-beta-aarch64-unknown-linux-musl.tar.xz=507de5fbe92e144dd37dc34123ee58b9e46805c85eccd4a759a117020578d742 -dist/2025-08-05/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=263ad4a9ed084dd76b6ea62d377fa470043f78e0343f7fb80d5c9b50659d8977 -dist/2025-08-05/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=ed850f484ee870172b721ab6824f0a15b41dd80ffc623557aa58a5b839d992c5 -dist/2025-08-05/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=afb4cdac4a3c28fe08fbba8b98962eec6c625f6a10a52ee8cc988881852b79dd -dist/2025-08-05/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=7f332d11e74d76efe236a7858021a626d31fb856d9ad0745369b99d9fdfe3b44 -dist/2025-08-05/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=5c0c79bbf734c0ce18001cf27605f6728d83d24bc97ea5c78b423fb9faf46d96 -dist/2025-08-05/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=3aff39ef7b9e8adc2e6bca19c2940287c4e091ad7ce4503c46334e6969ce0c95 -dist/2025-08-05/cargo-beta-i686-pc-windows-gnu.tar.gz=af99a5778ab4c9cea122897bcd3ea1626893156fb71346d66a584553d6531469 -dist/2025-08-05/cargo-beta-i686-pc-windows-gnu.tar.xz=8425eda844728c0353b4c7edc4636141dad265e461addf009cfa1a224df0e7cd -dist/2025-08-05/cargo-beta-i686-pc-windows-msvc.tar.gz=46d1b318b6cf826d8e7e694e54ce5b9c651dc2f8facf32ddebe21fc32e1e8dc4 -dist/2025-08-05/cargo-beta-i686-pc-windows-msvc.tar.xz=bef58a9f31aa3434152f79b2e271958fb07e925c938a569d1c9431f7764d19f1 -dist/2025-08-05/cargo-beta-i686-unknown-linux-gnu.tar.gz=c432ae4d909a21336a6645b85a90ec541818424bb76da16b19979a61a11845b2 -dist/2025-08-05/cargo-beta-i686-unknown-linux-gnu.tar.xz=053c02b341219d583caba881e525eae2cbb125ecc188e1b43d641fd7f3f027f2 -dist/2025-08-05/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=40542d76adaebdbc3fb16047a8324d439abba0485d227253614beddcc3cee2dd -dist/2025-08-05/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=d25210467dabf91917eefad9a5a415a3912a31b37ce1fd3d755b6c72b3a6ce8a -dist/2025-08-05/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=09755de73380c39daf64208df9708613ed6f8790e2f5cf79e80cb7826fd74f28 -dist/2025-08-05/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e8eab1aa5b41c04b518d43a86849307cbfec76df13c834a460c546ab6b170089 -dist/2025-08-05/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=93398391d308bd0c08fa2a7bab7bb6a38b78400280cbe765604a3da9d6caeb47 -dist/2025-08-05/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=ed37a7c5a8c62db06e709779b81d1e013975feeb82c185c76bb3d218aa142cc4 -dist/2025-08-05/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=a398b3ff0967b1ec2fdc2716a6b2c3a04defc14ebed577d93e45040aa5552dc8 -dist/2025-08-05/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=51d6a1a3e71713157a4e6291023b8393e21334a952a947f82f9636a725989281 -dist/2025-08-05/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=cef935747fe5205c3c5944f4dcf80e3111d2859616e7d727b8a5c77abe2d9fef -dist/2025-08-05/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=fcb8aee743adcc3796b564570e0ad6d9950031160ba9a6cafbd92af2f0a0c213 -dist/2025-08-05/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=73b2c9676122e842a73a8a9890f1e1aac426f75449a99b4fc0ae3f5dd5ce238e -dist/2025-08-05/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=ba293bb860349ee4732c5363d38b5e386544a25f65ef8ee33850061bc84bfe64 -dist/2025-08-05/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=bff3ac251a42b1664a544706185826a4d9137cde990620dc73951252d2d7fb41 -dist/2025-08-05/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=61056d4405af01b4b1c3134af8e83ed86473d0846beb41d3ab72df92edf316a6 -dist/2025-08-05/cargo-beta-s390x-unknown-linux-gnu.tar.gz=f89a30322a3621c4737f932788f4ca78c83b9f2845e324c4944522f35d44baf1 -dist/2025-08-05/cargo-beta-s390x-unknown-linux-gnu.tar.xz=394d522c9553182cf5f496e2b5324499c1845c0a0621fa527aaa925946b58d21 -dist/2025-08-05/cargo-beta-sparcv9-sun-solaris.tar.gz=0b18adbb22b34448576e6a3ba637c7565d369e1c994474337bed48b3cd0b0231 -dist/2025-08-05/cargo-beta-sparcv9-sun-solaris.tar.xz=c57709b87524d29661f77df3e3585bae4776fb3fb6de3874edb942f724543a89 -dist/2025-08-05/cargo-beta-x86_64-apple-darwin.tar.gz=c8faf66575d43fcbc03376225ac22d571def08ed1fc239d468c15929d9ecd393 -dist/2025-08-05/cargo-beta-x86_64-apple-darwin.tar.xz=538d81c3fe2b5a9edfc1e99655120d37fa159dcf761e1ddbe5233115e39b38b1 -dist/2025-08-05/cargo-beta-x86_64-pc-solaris.tar.gz=a2fb63b0a2cc3d3ea9523c8ffe61ba9ccb367dff136e6fc39aeea6400034363c -dist/2025-08-05/cargo-beta-x86_64-pc-solaris.tar.xz=02e8990865ef8f14a31e4d0f17be4cc0cbecda7e82e062b4b9cfdb99dd45156d -dist/2025-08-05/cargo-beta-x86_64-pc-windows-gnu.tar.gz=ba86f300cd40cb3cb23ac41970246ce54c03ee153f86127a379fecda930c020b -dist/2025-08-05/cargo-beta-x86_64-pc-windows-gnu.tar.xz=3bc0bf2da392ac52bcf2aa1dc19bea1a86bd7a4fe246feaae862a792c82ec476 -dist/2025-08-05/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=68a2a34e656395fabd42d20b7008d96b2a86e9a47caa52e6e2613ccb3b1b2d8f -dist/2025-08-05/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=1e2e31fe2306e26bfe58c49a99cc89664e8a7857c2c18ad74c20cdb35bd3c586 -dist/2025-08-05/cargo-beta-x86_64-pc-windows-msvc.tar.gz=5fa21f665aa40fab1896bd4a49dc5608b4b453d26f4b975771908634c699ab8e -dist/2025-08-05/cargo-beta-x86_64-pc-windows-msvc.tar.xz=adc5900506d399246960445c1e2d59f0c4b2a5cfeff9972f1617de030ce82bfa -dist/2025-08-05/cargo-beta-x86_64-unknown-freebsd.tar.gz=a2bbb1d5fa283e77ddbbc0c8d69e36b9c2bbb01912f302f522af48c2187327b3 -dist/2025-08-05/cargo-beta-x86_64-unknown-freebsd.tar.xz=11e1a51740a728f5825364a8679b28454a68e7efc96320730f9b58a8fc2e6fae -dist/2025-08-05/cargo-beta-x86_64-unknown-illumos.tar.gz=9993f4130b5ce50898e530e7411efe6923a36b5d56459ab672b1395717fe69bb -dist/2025-08-05/cargo-beta-x86_64-unknown-illumos.tar.xz=0a41315ced9f4fdce9c1877a4c27e5cca6e494f5dc8c2560334a5b75d42f495e -dist/2025-08-05/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=6b8f74b1c850093acb7227306caaed4581d70d015ebb0bb5f924af1c8bee7bd1 -dist/2025-08-05/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=f6f7bb4e4f1156329946d83bad5893e508645dd76b9ce522a53ea673791da006 -dist/2025-08-05/cargo-beta-x86_64-unknown-linux-musl.tar.gz=68c829663d6166661563704e25a6e85a973705e12efc9265a23264b9ffbff4d7 -dist/2025-08-05/cargo-beta-x86_64-unknown-linux-musl.tar.xz=2c40e68c9978e58250f0e017b5cdb2fc390f26ef96324129c489f55898784488 -dist/2025-08-05/cargo-beta-x86_64-unknown-netbsd.tar.gz=65166585138bc6f09f54f88ee889aea6d4e63019d64a684798341d6b332ce99d -dist/2025-08-05/cargo-beta-x86_64-unknown-netbsd.tar.xz=97491edef98b3a13b0571907555bf5867be13ec8fd4af69142db92a8deaf8586 -dist/2025-08-05/clippy-beta-aarch64-apple-darwin.tar.gz=b933611d47ccbcf5aad43f1134cc72ac708fdf59bace0dab75cfe139e2357373 -dist/2025-08-05/clippy-beta-aarch64-apple-darwin.tar.xz=c3d473e366db3b271cbe438b3a5531e9c5a72e28248d94de0f81ee93a8a2e7cd -dist/2025-08-05/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=e5b4fc054121eb13b21171b2e851dc1c824e11aad1257dc92b4ca9e332898b40 -dist/2025-08-05/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=40bddcdd9186cfb9f8763e8e289087c15c2c8b8ab78f41ba7380cdb08fedb0da -dist/2025-08-05/clippy-beta-aarch64-pc-windows-msvc.tar.gz=7e7c47305708ae073ed8d86e621a3c0be0e135b2480508814665f24121588a56 -dist/2025-08-05/clippy-beta-aarch64-pc-windows-msvc.tar.xz=c4a5bfe2d48a2301adb4f7524cccd64f4a3ccecf985f131972a13bd221346454 -dist/2025-08-05/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=dd26d49359f6010e94e04198067f83c83e81618546d1cf51606d157a02b4938f -dist/2025-08-05/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=e7c9834067311a87f547450108718582991498a102dfcba0e1a99671205e9bc2 -dist/2025-08-05/clippy-beta-aarch64-unknown-linux-musl.tar.gz=b584c26e267b0f7e105f65c436c12cfd6d9b8f2b92f9662a4797fa5e95455e11 -dist/2025-08-05/clippy-beta-aarch64-unknown-linux-musl.tar.xz=6b0eaadfea879cfc8c758fce002ffe77e34484e167c496ac0659dcc789dfc080 -dist/2025-08-05/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=eebd0632971888cb3bcc3b07a06f25a47af594ce5606e8e1e069fe85ec702e5c -dist/2025-08-05/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=946d6791e4e15ffca6195bd7c9bd2d160b9c65468fddc800948f41adc8fec597 -dist/2025-08-05/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=3862815aa14e8122b70b39c1137088e0c2a724900d2d13ac2b37a1a430b23a1f -dist/2025-08-05/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=8278e11b7ea2bc035e6de21f826a775d66273a9031195d8b887752137424f2e0 -dist/2025-08-05/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=83a847698eeafcbd3288d59013157d3d2a11b90b521ebadf1b26dac90877d11f -dist/2025-08-05/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=308de9cedc422c2051661df74024ab26c59b86368fbf50ce4dd7b2b2907ccc8f -dist/2025-08-05/clippy-beta-i686-pc-windows-gnu.tar.gz=95fe71a1f8e68f0f3a5306dfa04e269c636ad036908e201c1b4ed7a3f99b1dc7 -dist/2025-08-05/clippy-beta-i686-pc-windows-gnu.tar.xz=5b8c928be909433fb005498a92d2fb3ff535f31c68a5e134523f9731cacb029a -dist/2025-08-05/clippy-beta-i686-pc-windows-msvc.tar.gz=d736ec4f447f6b204f250b044b406b4e4b96f014887b6f7755b037e211aad3af -dist/2025-08-05/clippy-beta-i686-pc-windows-msvc.tar.xz=8a4ac568284aabb994964323465c7287715d3dd4ab881271a4746d2ae5390ffc -dist/2025-08-05/clippy-beta-i686-unknown-linux-gnu.tar.gz=d8b87338abdb4123eb25dd778d038c516c5bd472e1426b5a5f74f25126957039 -dist/2025-08-05/clippy-beta-i686-unknown-linux-gnu.tar.xz=ebb6dcc1b038deff6a966062ca3a966d3426f8932e5aeba398636a2d2e9be0c0 -dist/2025-08-05/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=51bf4e84be5b677ad9afba442d9567b96f59209219cddad5eb3f49b788bd8fe2 -dist/2025-08-05/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=e63e3346089c7f300bdc73521def20cd2a012f8de98b8b05a653e85f3516371c -dist/2025-08-05/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=aeb3782ca34a0ac47420109c241041ebbc029050a690a5c828c865108f56ca18 -dist/2025-08-05/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=80dabbd511bd5bdfc944d7613a02c9bdac702abf2de916222a37e59a92c2e577 -dist/2025-08-05/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=9307396973615663330474ec145efebd4246c6bae5d979a80b6d93f832af0137 -dist/2025-08-05/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=fb2a34f9472d2c1beb1381e9ff8bca23d9058c978fdb2e52a3c7f0cba8305c59 -dist/2025-08-05/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=e985eebd182d78604861ce60aba5078e98a9a078b76752da8fabcfc5fa471fc3 -dist/2025-08-05/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=1092ddf60ba04418f91c1e51f9c9da1e0d6b61dbdb104fc8028043dc1a33caec -dist/2025-08-05/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=bf0238a4909fa15034f067d5a51e38e43c91e69f29f51c39246d5c0b23925042 -dist/2025-08-05/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=39caa6aedcd746ed1c4745e207cf8cd65de7b774f15e8892987ce2c3fdde543e -dist/2025-08-05/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=b19ef1a8cdc21925887ced0c594ff5ab658bf66a0d137c01b6375fcdd0de8e35 -dist/2025-08-05/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=0ec300c1e791f946503db692e602680acf11857715b9ecc87cb446ac10d2527c -dist/2025-08-05/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=e7c8fe214ffd70599648cfacb50b3457597b479d320bf8383869dda8b0559d42 -dist/2025-08-05/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=d8ba0c42074c2a94dff3b05f2388f17225044485abd0459e155928f4762c8988 -dist/2025-08-05/clippy-beta-s390x-unknown-linux-gnu.tar.gz=510b9c9ca885a8e91c3d25f14cbfb34a7a927d374fa1a9149678d7ed9c4e4c2c -dist/2025-08-05/clippy-beta-s390x-unknown-linux-gnu.tar.xz=b53697799d99beb46fc17b3cca8ccfdc4ecbf7f3e1fd47f031852f07fb749ea0 -dist/2025-08-05/clippy-beta-sparcv9-sun-solaris.tar.gz=f3109a1dd87c23256057fcc94d3fade0b49d20a51040b4fbdda366f5b7c9b58e -dist/2025-08-05/clippy-beta-sparcv9-sun-solaris.tar.xz=ba265d781254d0b032d836a440c94c31ca33bc136e027ad05332cfc0cf40bf54 -dist/2025-08-05/clippy-beta-x86_64-apple-darwin.tar.gz=74c49a7cd4b6605b9a43961415fcaed1197b8f5aca798efd4b62a15d837b956b -dist/2025-08-05/clippy-beta-x86_64-apple-darwin.tar.xz=69128daabb11fd29720752bb13d83ef4bb3faa1d4aea8d525da2cb04f42b6010 -dist/2025-08-05/clippy-beta-x86_64-pc-solaris.tar.gz=e1975507778e448ac9b3040f330340f3a7d54e6dd40357494e3d891008375364 -dist/2025-08-05/clippy-beta-x86_64-pc-solaris.tar.xz=43c142c870116f4c2408f4b3208680b81390a4b37805f5a32696ad17bb313b48 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-gnu.tar.gz=ccd8806b6614edb270a2816cf0dc3b6376046fe58d258d296ffb222929d42d91 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-gnu.tar.xz=21148cd754493da3cdf72adc8da19ebfca1da8d642e1bef51782772241b48084 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=5a7cecb6c054a71ec5b1fb284f6bf2c069925fffcc1757ac631e8a2d08b116b0 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=b451057a4a75341924072fe26334eefce8b6eaa3edd79d3226eb02c1c99fcdb3 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-msvc.tar.gz=74e9cea693203c6217934549694a240460dda2818b144bac5777f41c44a06d53 -dist/2025-08-05/clippy-beta-x86_64-pc-windows-msvc.tar.xz=bd90fc3fc80f28ce415dc1cfd105d17ec5ecfc63fae017baeec734bf94f5d71b -dist/2025-08-05/clippy-beta-x86_64-unknown-freebsd.tar.gz=fb61d60d6c66a4632911944b5c7858b8c055ab8ae5a194d78e7d7ba18b65e940 -dist/2025-08-05/clippy-beta-x86_64-unknown-freebsd.tar.xz=bbc7b2aa6f05ecf391a757ffc5b6fa6e0989d00f7e8fa48b83faca8996f99dd1 -dist/2025-08-05/clippy-beta-x86_64-unknown-illumos.tar.gz=10e8be6eb15269cb2d0573e19e3a5004dbbd2b14e2f016d6b9c60713e22f716b -dist/2025-08-05/clippy-beta-x86_64-unknown-illumos.tar.xz=c3748db93829d3f5d9c5498592d247468125ca301ef238c41e42d37b7b90c6a4 -dist/2025-08-05/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=5a0365eda14ac1a366f3c480a2358eccbfcbfce86323711d7fcfdcfd85652b42 -dist/2025-08-05/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=908576635e79fe589583f18bd6ace4c488cd11ed0c59501082bb0b397df24f39 -dist/2025-08-05/clippy-beta-x86_64-unknown-linux-musl.tar.gz=79fd42cffac98024308c511144b716d18693b902dbdc1c4b88645bc7d4ae7109 -dist/2025-08-05/clippy-beta-x86_64-unknown-linux-musl.tar.xz=a3a616554ed25630df9f8cef37a5d36573e6f722b1f6b4220ff4885e2d3a60de -dist/2025-08-05/clippy-beta-x86_64-unknown-netbsd.tar.gz=ad1849cb72ccfd52ba17a34d90f65226726e5044f7ffbe975c74f23643d87d98 -dist/2025-08-05/clippy-beta-x86_64-unknown-netbsd.tar.xz=608001728598164e234f176d0c6cfe7317dde27fb4cbb8ad1c2452289e83bf30 -dist/2025-09-05/rustfmt-nightly-aarch64-apple-darwin.tar.gz=6fd7eece7221e76c2596e0472e7311fdced87e9fab26d2a4673a3242fe779bd3 -dist/2025-09-05/rustfmt-nightly-aarch64-apple-darwin.tar.xz=1a662a55931f58be9ac05841360305f277f8b1e36f99bd0a2ee4d2cc92b7ad14 -dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=d1c3c52cf61522697d726b32ed28d7b8b4cfadf30ec57f242e8c7f9f8e09f692 -dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=2cc7cbbfa06803a2fe422ed3266f6eb519360b403c83f92259cc1b83f5ddca45 -dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=61f525d050d1ff4a29cc7240912d84c9c091f25195b58411af9ef176175a3200 -dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=504b8ace2ab7ac13be143d95ed74d94940e8706ef9f53b7778da215840337e20 -dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=ff48bd98d109310638822f5813042583900e2b70edd45fccd871c7c03dd1c2e6 -dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=b3de2bba7858e76cdafd326f3072e4c5b534ca9b679ea62caeffb07722e9a3c9 -dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=8ca4caedc50f09995dad7bc6e001cc863c524e28c45c186022ded589f3728709 -dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=8f2cfb052f9697052d89bb729d17d74583af3fa0be98f18a3c44ea518a8f4855 -dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=b12ac2a38b379bf0de4a92f29ca40e1955c45273e798edd1a72bd40253de70f1 -dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=87fb7185aa46f3810e09479dc8fafb66d13b41f4f40e9c4e866ea77fa47b1bb6 -dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=be1e8377f3d10f4f8a07ae06ab9f649f9d2fc9cfc395abaa5f0ad10a95c4fe7a -dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=60646799fdacdafec9e0ed81357b5db70f85bb1241fe775e71b8ad3feb686116 -dist/2025-09-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=ed8cade9b846efb5ac121aa70ac188fbd2e61fa9402fe68c80b2cbd3ee32ccbd -dist/2025-09-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=0d0bfbd9cd4123e0404fe476a6f16eec6e435ce28d328dc0dd0aad257b295d64 -dist/2025-09-05/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bd411db34707c36d5b60f14bba776b0b89b69d4266007a3b591c467a17ef053c -dist/2025-09-05/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=f0f2a6a81177ae6d06ff9b8f4a5bdf3bc8b26710aaf0f09258b32f7f710722c0 -dist/2025-09-05/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=b49130da444e01fe4ef997b649aada8978b8dcca60dd38cf9e7400a7c7569e1b -dist/2025-09-05/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=0195cdddc74cba2bf17eaa1d53edb1a2bc0e34cdf13c7b25a34ad9729a2635b8 -dist/2025-09-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=1141897495ddca10fd6d9476a624b6a340fc2bfc619148e183bcf355a0078b18 -dist/2025-09-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=81b31bc8b3d431120005c3c8eeff3ed194dd18e56220c175c3250855cbdcddbe -dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=10a27070239e7dfcf701669c8d93ecb2d310b9fde768639a2bf5be62cd13528d -dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=0ac4a529f4f62a94d5ae4cc8a4a52f0e7d57296ac0884258dcc5478e6b0b1785 -dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=9d0ed6778fc4f0601be1e317438cf95c414fcab6d3207c635babb4f3a6faf2b0 -dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=e40b607faf2f37c9d654cc0b60c47ea155893a3b62236cd66728f68665becb18 -dist/2025-09-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=46c029ebbfa35972b0b9e366d17c41ff8e278e01ce12634d5e3146cbf6d1a32e -dist/2025-09-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=8d1462fd09b04a94bfb1c1841c522430b644961995bf0e19e7c8fa150628e7c7 -dist/2025-09-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=5827684ccb4d38956e77858ddeadeaff2d92905c67093207bed0f38202268151 -dist/2025-09-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=975c7d7beb5b66caed7d507aaec092fdf33de2308f4dc7de46fe74e5e15b5352 -dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=8829677ab0f898c98badf22dad61094cf53c6d599b2cc76142d3d792a44f3770 -dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=3d961bead4010f8a488a065ac8a4153e3c747dfcd7d5f7eeba1cad00767a7ac5 -dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=0138c30ebe74e8ee838d9eef31c7882812bb52d2304f2de7c23c47dedd6a5032 -dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=bdb4b7b3c89a30c79f51b5fa33a2a29fc8313f8193bc43ee611e8ce7d80382d2 -dist/2025-09-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=8e440dd400bf3eb4a144318459e111069a64bb309a5a51eeb0f0383dc48ee55f -dist/2025-09-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f623e1d7d38d94965d7653fdf4a272630b2b6dec81662fbbe5d2573f2f3f3b8f -dist/2025-09-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=f46a8278352d5a981c6746b876fe19df3093090a866d20d24cd5cb081136b1c0 -dist/2025-09-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=fdf8b44c6f110a33ad7f969e651ad396c880a85545aadfee8a24ca3cbed35974 -dist/2025-09-05/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=59d778dea354867d64c809b40443ca0762c685dd0e5361971daab04aa7c5a5ad -dist/2025-09-05/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=18628b2888d77281fc9b2ac5636ce4ec444ab0e47bbe0e8a08238f90040c20a3 -dist/2025-09-05/rustfmt-nightly-x86_64-apple-darwin.tar.gz=169d9f2ee4a0c726040f4940370d1162502aa6568a0a442c92cad3fbc7bd95c2 -dist/2025-09-05/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7f955cfa1ab07819f31cd904b0a79c67cae70090aabc7dafffdc1f3f67463248 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-solaris.tar.gz=d2cc32d6be1d0f1a8654400f0418d16e789b62db3fbc0cca0d0d492615bcf6e2 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-solaris.tar.xz=3dbc29c923a6a2809a8ef561d2ad375211b09dcb107bceabbf510ab0d7b471f0 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=d9b4ca2abf1062e888b31f31f517ccc6b451bd2bfdae915ec3984bc88a8be91a -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=00c6d92b6e82ae58e682e72c974f2dcc865820567ba44ed008e4759dfdbdca87 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=e75424d0aece8d548b2c9d896291288615d08ff2a37f1c4844a70839c612e633 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=cce9578d9b35bd8192a26e2dc7d7f7e7d5b9f08c7d73b3f4dde08208b448b063 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=ac4282e06b0972033f974c63d2c6cbf194d4e66a1c811f443d3fa0b886868825 -dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=a2465b31855861d0e0eea072bb366480acf2bafdd7fdfdab79c809d8bbf8d8e4 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=30c22a97066a5711f207c1919a1d74a328540da0d9d6f85a0cc044174049c927 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=40987da0b665940f9c406cfbb4889dc101d73846730b0bdfa1382d051297ad08 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=443ba10092122fbba9854abb4aa9d2e71d8e5e65b99fae6dd572909bf50f28c5 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=10285942b9140efc9897965cb3a4204145e774bd1b0c2690d45d8b04498fb917 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=b09af4fe367df416bce622654d9e3dfb610c564f5e6d14b200d949340595673c -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=1f9a585a017cee5a05190377cab105603f039fbefd9b4934278dc555dfca91b0 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=24f911745fcc9f120e44acccb98f54dc6406e492915410aae17efdd00e2752c3 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=48eb58ba1d58701dbff341e19fb6d446ed937cc410207b35297babafa29dd889 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=c2d1cfdcd8a08bde3f9a635eaf2d6d2fbd82e4cabbbd459e3c47e64bf1581f11 -dist/2025-09-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=ed1775b4fd3d7d1203f8749f70328ea4ade802fa0a76c4d7ab3e2d63ca1535af -dist/2025-09-05/rustc-nightly-aarch64-apple-darwin.tar.gz=4b64c4148e5cdd6585a4200125cc394c6a998da3686ef758f37742ec2d33d573 -dist/2025-09-05/rustc-nightly-aarch64-apple-darwin.tar.xz=fd45182f9db059bdc17f2c465dbaae22589eb8e8d2d88776437a34ddca3b7153 -dist/2025-09-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=c14f2d7f391492d150f2f2f91af413266649cbdbdb042fc8b660b3cb141b80c7 -dist/2025-09-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=d72ea1c571fe2376ef05cfd15fb3207ee2d27c3c851f3391dfbb082c06a5bdd0 -dist/2025-09-05/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=3a485d8fd8d58fdfbc1216e51414aa4d90f0c7285a99abea0fa5935d2337251b -dist/2025-09-05/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=7317060a29eecd2e28d47f0ff8780150059756b07e2bc9137c3877be8af593b7 -dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=58a53ae147de1beb0a53629898bf7c51097351271be3713a2e2344b86a4080a4 -dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=f79d2730843dbea9e9588fd1c1b0d854441969d8f93f76021f06af2efe22b845 -dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=eddf23e28b8067021e80883faf2eb1d6d3005a6e8419a1232b84236bea286f78 -dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=4f0af1a66050f5e2d9d48b696349b9ccd420bdcfdb88b251a6655cc22a11949b -dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=fd2f0446e3c993d746e8a5f72ccebd8b0a49172316ac1d1c58bad10596becbf3 -dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=0b06e7ba47621819b4e59e048e5d336b3d6978e906c7363f06bbc804e49f1046 -dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=97d7c34b53628f28e6636fae738a18d0f1f4c60a9febddfb7145e6b91fcf3fdc -dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=323025215bf851024a7eb6566ad7dc5719832d81e8d46e70adaece98adc5644b -dist/2025-09-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=3017e03222d030448ffe2805624143540197bd6d3b3e93f9f73469ace25ae4be -dist/2025-09-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=4f6e86b185fb54f7a0b7d09a0faae7daac722351354f6abebd388efb3037dfa0 -dist/2025-09-05/rustc-nightly-i686-pc-windows-gnu.tar.gz=292c3770b96cde97072d70c58234488f955ed5582b7c3044c6de66891e73d639 -dist/2025-09-05/rustc-nightly-i686-pc-windows-gnu.tar.xz=6855d3fd9040fb4da554fd732eaf8a1c723921c35bb8a8efb31c78af69b2e4ec -dist/2025-09-05/rustc-nightly-i686-pc-windows-msvc.tar.gz=64a86a2971ed9934bbb6aaa0afc2a7f747da463afb55b51a7c5fdbdaa294e437 -dist/2025-09-05/rustc-nightly-i686-pc-windows-msvc.tar.xz=c98f1fc3e077b8c8eb3e526c416a6551c08c62e58be57b4a4c7d59670bc435f9 -dist/2025-09-05/rustc-nightly-i686-unknown-linux-gnu.tar.gz=5481c97d788899f896473380dde0877808489bc4a0ed6d98265558631fa67a57 -dist/2025-09-05/rustc-nightly-i686-unknown-linux-gnu.tar.xz=afadaae945c0b9a8b50dbdee28791e0c03c880cb22d3c20996eeb7fab94df0bf -dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=18b276a2464b6c5a817d72384f25442c7619cac05b2d8d0af212c0dad96ccfc6 -dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b74323cd2dbee966eebe8e63e24fae026ecd900a025e9167cca0341e50333cc3 -dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=916dc5144107d9173479b320b55b0a95d2d42156c69fbdb0f21377d825fe0892 -dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=3a83da72aa4a6553ecd957af35a05274528dc79f87d24d8470c20b8b4478b05b -dist/2025-09-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=59e031b6b79a1f11406c0640e1a357f2941967ea8c034a94148d60928038e58e -dist/2025-09-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=53f0e33cf2651d5dc8931ec5af8f04994188d8f9a10a5c61bd72cc822df34501 -dist/2025-09-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=0eec56e3b725d918cb21e494a803b2e38eb6d744f64f1a82481a4c704eb7c1f0 -dist/2025-09-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=5efc97abb235f349c6cc952b4a1e4dae7d56d70b0f8b8da2a1060b85f9b734fd -dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=cb45bbcdf8841b1ac184a0aacc909f153c830e8051260e09ca4e32c1f048e2fb -dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=1c9ddddb90d45612e4fd190fb71e527b523df13146343dde200580fb2b638794 -dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=3a9bdd4d14e8f121d3051944ee83a901b5ca4c0921f2d01e34596fb7450b49e3 -dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=732b9abb8a80191884fe1ff1d4d923cc1b74c21b81e6327bc5979ae526337400 -dist/2025-09-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=1826e74200fe286478e1659ab88ea86b43efa7b023074d00dbc814d80bebc883 -dist/2025-09-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=c30b52d0f474fd6193bb1b3e147fb79fa8cc31e5db38444f023d84d1c2d93d12 -dist/2025-09-05/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=c330067621ed25d8383f27e494346eca4d7d4866e48f331f2ec897ff1c386e56 -dist/2025-09-05/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=cb4097ea582a83a94cab80ff2f36b6f7141c140d75c30b1d261a1ddd4ea45bd4 -dist/2025-09-05/rustc-nightly-sparcv9-sun-solaris.tar.gz=0af19e764f10333017a3ab66020b82c7185ad648d1230b68f10977e0affb937f -dist/2025-09-05/rustc-nightly-sparcv9-sun-solaris.tar.xz=a22cfb55cdd122dd99bf3566eabd781bb2ecded90c71a41fd33b1e0588bcc39c -dist/2025-09-05/rustc-nightly-x86_64-apple-darwin.tar.gz=163a07b91e36e85c6c41368598793667414cdc6cadc980866811234539f3aec3 -dist/2025-09-05/rustc-nightly-x86_64-apple-darwin.tar.xz=76711800685b39b3b75945395682062c40efe3195f9979784bf318837e21768a -dist/2025-09-05/rustc-nightly-x86_64-pc-solaris.tar.gz=69142a6c04703c3c8309c6fdf66b25831bf9efa3ee70cc93da4b5b75f901b29a -dist/2025-09-05/rustc-nightly-x86_64-pc-solaris.tar.xz=7e535f4aa136284e4bdfd4d56891caac6844dab91e1b711fa3914a5974dfcb60 -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ff0ea563126ff28df297258001d9fac4dbd85788b5d27a0f5d6be75306f21139 -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=6b66adcaa9a5332979591464242423897f59276e9e2cbeb9ab038a72e72c3a5c -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=64cac5e377bc115a8f8176719575903e695337941db43cfb35546d65c0723130 -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=11e2765e4b3e2296ea05ecf735cf7b5f7beb08d76d12449cfae67f88bab02819 -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=c7d15ae7cd5af774ae70e63fec3ba8723b85fa4c4640917b3960907eedb30b39 -dist/2025-09-05/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=55036567af270cdac046fb4306e515787ca6ef5073617311fac79fb87ffe9366 -dist/2025-09-05/rustc-nightly-x86_64-unknown-freebsd.tar.gz=2d24470d2bb4c4d2605c15f1b2654cc45805384babb73b1960e8aea0b8cc227d -dist/2025-09-05/rustc-nightly-x86_64-unknown-freebsd.tar.xz=fa41782cb2e22aba30d1619db1f478c0305944ceb4de1d1001f221c5c68c104e -dist/2025-09-05/rustc-nightly-x86_64-unknown-illumos.tar.gz=d0f9785f76c59f3a67a499cfff4a5639f3ae05cbc76750b867faaa60b7d67f78 -dist/2025-09-05/rustc-nightly-x86_64-unknown-illumos.tar.xz=925e473c6e31d8811879a805358cfd2e5ab8f4de836c270d02dc8403771bed3a -dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=ebac845114b89dfe7d0efc0cfe8820902faad617ed21eb2a701d73cf7b544a85 -dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=2512a5462e3f46c95ed4aba4228282a357b3e0694e9db117a857196448fe7bcc -dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b4a5d364b84464e9a92140fff50349d4942b8d970b64150a4bc6d720cc6c4a4e -dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=6c57c2edc305737530f8551d789ee79ff952f42a0d52d6f8d7d7f1ea2027cfca -dist/2025-09-05/rustc-nightly-x86_64-unknown-netbsd.tar.gz=f3192ded327875d5a27fb50c690e3fce36669e8f71c47de304dc21edad573ff9 -dist/2025-09-05/rustc-nightly-x86_64-unknown-netbsd.tar.xz=11359e4731866f6a788e5699867e45befdf1ad49ef35c0aba22af76d3f327cc3 +dist/2025-09-16/rustc-beta-aarch64-apple-darwin.tar.gz=3e3fbcf51f2632b8890b38537ea5446147b85fd62b21798b4fb2a032a4abd6c9 +dist/2025-09-16/rustc-beta-aarch64-apple-darwin.tar.xz=296ef2ae4dc8b4f411423a4384821e5b8e305be809b55522a0f02c97b83ce14a +dist/2025-09-16/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=fe7026f1310a84f3490d0ffb8476bddc36992fcb2d7e450c036aae6bbbb218ef +dist/2025-09-16/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=70bd356d7f39ff1ef8511941671caae84f43d3a9ab493ed131467d27bf5c53f1 +dist/2025-09-16/rustc-beta-aarch64-pc-windows-msvc.tar.gz=9b0f42d2713c20d1df9e401410f874f04ffbff2e0c61d19d2f8689d4e55da562 +dist/2025-09-16/rustc-beta-aarch64-pc-windows-msvc.tar.xz=086ff4c532289581325b1cb5c629f899418dd392249d3c0bd2137a0d73ebccca +dist/2025-09-16/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=4860148593cc5f5662cb89c9484d71af472223b8a9f9644e6709802154e7d02d +dist/2025-09-16/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=a7f290455c134c72518267e7dfc5a51d550497ffcf539dac3b41c57486e1d370 +dist/2025-09-16/rustc-beta-aarch64-unknown-linux-musl.tar.gz=7a17d7a65ca7c933de8829e5991f991d917df7007c122fc55d64efa0fe53344a +dist/2025-09-16/rustc-beta-aarch64-unknown-linux-musl.tar.xz=6ab69fc25888475048dc5eac7adc7f6b02295dc7de7d0e92aad5ff562c500322 +dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=00ff0a967ee41642e6b316b190485a7d04c6749ff571e55cf73548b0e7d74b05 +dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=ce2c11e633e367544a70b0a3ad998a2d680277e62ece3eacbf350fa1ada6332a +dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=ded847d958f0417fba13e71ded42cee387387076b4426b2f2002d53dd998c27c +dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=54ee1d25f57db5d1c2b153533362ecb7fd326b8dd46b8240367ea865a059ca68 +dist/2025-09-16/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=8f964d41f6608ba04d2d84f5532a057654f3f328b19bc6ee4b9a88f6f64dcec7 +dist/2025-09-16/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=b6a34332cb027345f54f75e03cee1c65345e8671df081e8dd23111d2233bf6e6 +dist/2025-09-16/rustc-beta-i686-pc-windows-gnu.tar.gz=760c5f1bc081a14b4df2e0c6a1ce80d96b63e15e0b751cfe535eb5775aa9e34b +dist/2025-09-16/rustc-beta-i686-pc-windows-gnu.tar.xz=4376609352714e1e30a4b0c57fe43beb2b0edccda412e18e11c3b8b3092a87d0 +dist/2025-09-16/rustc-beta-i686-pc-windows-msvc.tar.gz=3d0f08e30f4ae2cb513548f66cc6405ba0c2cc74e826c04cf0bdad3d3c14bcfa +dist/2025-09-16/rustc-beta-i686-pc-windows-msvc.tar.xz=a68b01fb491496bb2b8861af9b900a2960f4ea9f64f0946caa09c043ecb3df2f +dist/2025-09-16/rustc-beta-i686-unknown-linux-gnu.tar.gz=3b56ab8e485b3e4bd5c0f1e1ff30389acb9d0d0e0662ff834fb3a855157c965a +dist/2025-09-16/rustc-beta-i686-unknown-linux-gnu.tar.xz=fe871e45e0d987dcaa6a30df8394c54da0204965686a089d6903fff75b8a01e5 +dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=71d53cb70f1962379614c86b07fdea7c1b343587a56734d39aa7de29667d6629 +dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=658f204ab2536ad3b56f238c55c5c6cf3bd7794aceff379f0f5492d28841b5bc +dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=0fe154b5b305259a36b80b9b53894336ceba1562155c05cf92debc5d7bb7e645 +dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=27357f4331d5c357a7b20002571276e2d77e58384095fd3a6910d44a0d85aa87 +dist/2025-09-16/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=bf87cb1717fad96abbb86091c44de6bdeed2bd60437ffa9037d27dda1c71cdbe +dist/2025-09-16/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=778d126b47dc5ef07085c5c760399130219cda6221f6f4ec57c26ebdd7e33299 +dist/2025-09-16/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=eae03019370901fb49b708c76d001ec9e3df27e7fa1ea9ceb2200640e9d6dfea +dist/2025-09-16/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=61c1b21acc668bdf36cbf64f2b53348d997ffc9997c6ad282d10226e86740044 +dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=057eb2050b31110529a80e27c6df0c95a14bb1f7c568b487dd3a718755a74d27 +dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=cb3078b44dbb6793ac98a28bd6bd7076c28ae13c5ddd1aa02310ec89130b6575 +dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=9a5109124546788b802be36d9415bf0d5bfcc087389156c65a2e30672e10e7b1 +dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=6987de797339aa2f231485897b0a498a841a49f1d0b83fa7e98c0a76767cbe44 +dist/2025-09-16/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=17cd423d53f6d8fe53dff7abada43d545a2de14136eae7cd02b958f9fe93f5a0 +dist/2025-09-16/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=740945fe14eeea917d64ff47e997ca17be1e0f14dca30632a9fbfb560405b040 +dist/2025-09-16/rustc-beta-s390x-unknown-linux-gnu.tar.gz=52e15e3a56a16b2989b34f0a051ff06fb8dd51be3e6874c41a4ba1e2128d35ff +dist/2025-09-16/rustc-beta-s390x-unknown-linux-gnu.tar.xz=6f998aede2a805d8159f2ea63dda76a7d25eb9cdbd26863394aacfa6d80de084 +dist/2025-09-16/rustc-beta-sparcv9-sun-solaris.tar.gz=45c85ec3de0a773b2c2f4b624abe28c3a906020e1ce6ceded75c0099a1fc1899 +dist/2025-09-16/rustc-beta-sparcv9-sun-solaris.tar.xz=d811ff717ebc59aa9bc5c26ee4b8dcfdb3d2f5a1759179eccf549c8a765fb3dd +dist/2025-09-16/rustc-beta-x86_64-apple-darwin.tar.gz=d31302c29a9efe362d2be87b83b229d5369b2462cea133f9b1b4c7c4cf0f26dd +dist/2025-09-16/rustc-beta-x86_64-apple-darwin.tar.xz=b8f9f0b2e4c681da9bf7fbac2fc22ec2f44c136b368821bfea5a5ab29d85527a +dist/2025-09-16/rustc-beta-x86_64-pc-solaris.tar.gz=498902a6de8cae1494aae6223604381c4e109a38c14649f6e27b075d3f26a685 +dist/2025-09-16/rustc-beta-x86_64-pc-solaris.tar.xz=1cfaeae163fcd4dd2ff44a80c245012576ecb4257d7d763a51f2d7e58878a29b +dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnu.tar.gz=d4c6fa5158c83500d30ef44e129889408dbc49ec862c985a1c36820d3550e94d +dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnu.tar.xz=17bd90bfd6603b24d26397acac55e4b2c23b0dc574834de7bc1f8c2c720aab97 +dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=d270c52fec5ef2e9f009cc3e697b6188320fbeb5445092c7a11af67e520cc9f0 +dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=81f91fc07821af63606268821a039578a7c9b09b8c0e96b85163831fee92e6a5 +dist/2025-09-16/rustc-beta-x86_64-pc-windows-msvc.tar.gz=233356bbd9a46168663752d7dcab67e7ae3ae732fab410bc8adb84230e22398f +dist/2025-09-16/rustc-beta-x86_64-pc-windows-msvc.tar.xz=5f33d8b3a064235c23dfd2edfa8b5f6f2c5075d26529f81881501b8323eae60d +dist/2025-09-16/rustc-beta-x86_64-unknown-freebsd.tar.gz=fda4fa10690458ee7db6081267458e47113e854d65695c95813039400772e263 +dist/2025-09-16/rustc-beta-x86_64-unknown-freebsd.tar.xz=570cd878e440b5d70a0d5fc90917b92a6b8b544a2e6844baafee1fef22d32732 +dist/2025-09-16/rustc-beta-x86_64-unknown-illumos.tar.gz=f8b7273e16de291fad24f2ebbd928b55007a75605fc25ddc35a4967f17e709f6 +dist/2025-09-16/rustc-beta-x86_64-unknown-illumos.tar.xz=77d7f5de006a9d0c56a2cf79423a5702bff445f6e34b4935326d3db30ac8b086 +dist/2025-09-16/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=f61a57a75252d63930630af7d597f3046b7f7f09691cd6ff8c3a080871fa19de +dist/2025-09-16/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=b56298c4ba7a955ecb9135bba06ff6bdb7f4df94b6d64c99460e90e5a022fb87 +dist/2025-09-16/rustc-beta-x86_64-unknown-linux-musl.tar.gz=c6fec77bf71bf35ded56cf70eca28a94e083e0af458b00ed665eedf9270abd73 +dist/2025-09-16/rustc-beta-x86_64-unknown-linux-musl.tar.xz=bce669d29a995189e9d99f686b1f9535ad82cc877a106c89af5b423bef8a9d6f +dist/2025-09-16/rustc-beta-x86_64-unknown-netbsd.tar.gz=cb1a0f5ff6d9c28b83f9c82fcfbf404b2920a7da8233d957e43fe001d5190318 +dist/2025-09-16/rustc-beta-x86_64-unknown-netbsd.tar.xz=25953b44e3b196e89b5dc6028805517f42f6367c6b990a930ba44f63dce0a855 +dist/2025-09-16/rust-std-beta-aarch64-apple-darwin.tar.gz=8ada740b1af444b98c3df74f5f810a05036b6195e6b54115a7e34bed23a30d1c +dist/2025-09-16/rust-std-beta-aarch64-apple-darwin.tar.xz=1211a726975c1a9fbd679156644b83c446cab4d6eb7728d3dc38db6c949b997f +dist/2025-09-16/rust-std-beta-aarch64-apple-ios.tar.gz=58e7ec536d9bee83dcbcdad819c4a27689f02a20acbc75ffcb6ceeec7929da4f +dist/2025-09-16/rust-std-beta-aarch64-apple-ios.tar.xz=f0afe719f83531e0c22d0621b9137d30bacd17ffcc9d0188e9cee2a71f10afa0 +dist/2025-09-16/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=e1f067b248dfcf1d6efbc589a0938ceeaa81087d041889d65b90f4f82aeb35ff +dist/2025-09-16/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=182ad0986236f21ba93d49ed3dbec72e54cbc88d18adf77848e87f497b7b2f05 +dist/2025-09-16/rust-std-beta-aarch64-apple-ios-sim.tar.gz=cd367dce5dc4a5d209db298f97070e55ec16caa8931ef17a189bf5d7f5c21212 +dist/2025-09-16/rust-std-beta-aarch64-apple-ios-sim.tar.xz=b51aba09d7ced8b69e15ec9ca381379cf9f910c1f89109c8764cc15d51fd8293 +dist/2025-09-16/rust-std-beta-aarch64-linux-android.tar.gz=89154dba00da80cd67cff7e9beb1f68721bae9686e1fcff1379a2a4f549eec79 +dist/2025-09-16/rust-std-beta-aarch64-linux-android.tar.xz=d373ed69ed30521b2f724deac051662bcc7d4a919c1ff9f56b59ef028f07dcf0 +dist/2025-09-16/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=9e84923928671f76762752a559d0e9a54f513b2f518eaad3da6f5772e46183c0 +dist/2025-09-16/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=9170e87332e2a74624fa1dbaddb725f6cb331a8f65a5b0b2781c04aa58bc48fe +dist/2025-09-16/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=929786491660eedf2cb6c02036ce560043a7bec349bde762effa53ba59d17c81 +dist/2025-09-16/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=72b395a15c0af1c92434a95010fec5435343190c3b90d55d2c7d17d3d4c6da52 +dist/2025-09-16/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=36245bab64c4757821ca0732fe88fb5620601035f50bcd2d061386903abe7027 +dist/2025-09-16/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=4c1bfe4453d36d554956f7ebb169bb81912815a28960bd7caf977940109573f1 +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=080603e3b117d3654b2e0b88ec172f4c5319ebadf9386074e7e6f0c466e9c90a +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=4ea35b78b18a121bd3b0165d9f3278f3a454e108564e02b6c7d7133a33a99788 +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=9c5b7188fed949b0462f9d6c45221b8b98e7feebdc4f8bc2d6d0d652cf3b3c24 +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=31514db13f19dea13e47b8eff9fd8daec6bd0a73816aefadb94ad4e771d6a902 +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=a540735416f0b264444281865e1ed450c5bd58275f73426191cfb547b674ea7a +dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=9922ad22f002f5df232d8ed1dcdcd23a5e1a0a5b4b27e27c3b5fbd5b39087d03 +dist/2025-09-16/rust-std-beta-aarch64-unknown-none.tar.gz=618073f52e27d3ebd15b028398524a12e3bd91d4147e70b2160430e03ecdb178 +dist/2025-09-16/rust-std-beta-aarch64-unknown-none.tar.xz=3fa92c9383ac122c4b9ea2a00ff9f38d94b52c384b418b13ea573115daa07563 +dist/2025-09-16/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=33c4825523ef0e27b038111d3504cfbb4e049fcc3e136ddec186268346b647d9 +dist/2025-09-16/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=4487b93a8c7ac712328c98a660948e795e833f4edd3c92449df5472de6e320b9 +dist/2025-09-16/rust-std-beta-aarch64-unknown-uefi.tar.gz=f0c275d8bd220dc066541e6f2c7e41b6d019d698fa2b613c1f9c87ab4e29f4af +dist/2025-09-16/rust-std-beta-aarch64-unknown-uefi.tar.xz=daed68a82600fe2f3652ae87e4c491460891906fcd06ae206bc794c354771564 +dist/2025-09-16/rust-std-beta-arm-linux-androideabi.tar.gz=9fa058b5bd0ac5af231cc5ee31f67cf3f16b361a41aceb43b17c81d8e180d16e +dist/2025-09-16/rust-std-beta-arm-linux-androideabi.tar.xz=c6868144f5f5fdfc0d993f66fa91a01a91e0288014a7ab5d6233a93447508852 +dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=5cd076f756c63e54067d780167da3e35bcb04f487fefa093028b0cd3435a2fcb +dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=3322a270f3696fc2254536608ebc49e047ad59e1a01493381c9c52f813bcb8ae +dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=8bed8176ed380d974c2bc6df5314ac385ca6b72a623a8d16c00b753932a64db9 +dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=d557f55446ba39a1b3335ff1e4196ab40effa4ba42b2ce91bbde93738fd93da0 +dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=12a56e9f5c9caf4e262b9361af5ea5b62ec29b18ffdb807cda83655c531670b5 +dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=82b28e64f6278cb24573d699b14826a51be465b68cb48e767a26f1938221f5c4 +dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=a3076f1f12895062e42a8ceacd5c06a3156b5a553c708615fddb975366d9424a +dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=4bf81edd4a193e6850bccbffb66c6005b1b99322192e87183d684dd9ec528cad +dist/2025-09-16/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=2170d924f4ccc579baf862171934029505d476170825397580154dfab860d4a8 +dist/2025-09-16/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=dc3bdf6f93b32aa19987d149ed7da7616f3785f67a63fcf1e0d1aebeb8007d67 +dist/2025-09-16/rust-std-beta-armebv7r-none-eabi.tar.gz=2863a22ce3663edd346d730cac4bbdef3b9cd2ea2cd5907dcbc61220833e24e8 +dist/2025-09-16/rust-std-beta-armebv7r-none-eabi.tar.xz=74a752daef0446ed260845b016b3e57d20de4e914102713e0c3b652329f9f6f1 +dist/2025-09-16/rust-std-beta-armebv7r-none-eabihf.tar.gz=8fc3fcf203da0fa6f321abbda405ba0121dbd898ea02513431a5ca3dd5e6e16d +dist/2025-09-16/rust-std-beta-armebv7r-none-eabihf.tar.xz=65cfd5061c4db0963b28f7bc636ece115ff7b812710ba9f902977f5014bdeafd +dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=1cb760fdde8e9d84c5caa787488f0bb05c5e649f411b666f2d6a5440126f5992 +dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=9146b64438a68c506a93ae3c4d4d1a928358b562fac3b7513a95c8098cebdae0 +dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=9df691f77c95a0bdf5c45fc546c2319863cdca4d433780fe8c7cd146496382b8 +dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=bba043357ba6c78fd46430c169710a264109ee5dadb79b98075744bac273418c +dist/2025-09-16/rust-std-beta-armv7-linux-androideabi.tar.gz=ab5726ae0472a8563b7e1e0dd6de32e1e9ee2dca45259bd1d382a24167919c61 +dist/2025-09-16/rust-std-beta-armv7-linux-androideabi.tar.xz=5af2a58f78fcd9dbcf1fac95cb50633ab9128e1e728960c0025e310a5528326f +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=9ff1f79f8c03756b41f8386995dbb011387982a657262c013579d0866a18ff1d +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=32dd8b2be52449b4e592a87acfba122b80990d2993fa81b48b81c0de4afb1533 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=7420a4ecd9ebb55b6864a22fedec9def0585f8460eaebbcd9e236cac2ae0a6a8 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=75de468d6d4041d77e5757118c3c93df6319f30ba6372fdc8e859b9e48f27d81 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=96f7dbe5d4bfe852b22f971cb59b7fa0981fd7fdc9e8d28fd1b6f6ae3bc59bfa +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=4166e6b0c28eca8a507f1d3c83948ba09361198466eeb648f491d9850e884df9 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=035c4db6428b912280ac56bb520bad29255b3c30bec0e89839721443109a7ce5 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=4a94449dd1f02d748c79ec1aed3907cbce97dc5641e5be3b3011234ef3517987 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=94006dd7c1a1d6cb28972369a0b0c429de992d541549d4b292fd6b737eacce24 +dist/2025-09-16/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=bb5cce4d13f2ae9d9d6f254d021d516189f29b6e78f3c1824e0803f7856ce7e9 +dist/2025-09-16/rust-std-beta-armv7a-none-eabi.tar.gz=453d62c4392ffd108233b7ce9b6abf0cc04b28a63bbbc5b979927ed7e9f8481f +dist/2025-09-16/rust-std-beta-armv7a-none-eabi.tar.xz=9b4093e7735ffe3c79f16f99a412492588a6652242e2a18c0f278e24e34cb333 +dist/2025-09-16/rust-std-beta-armv7r-none-eabi.tar.gz=293dcff7ac56a7f52fbfa1afef9fa2b4a3e14b9b418c5a56e12e8c7cc76c963f +dist/2025-09-16/rust-std-beta-armv7r-none-eabi.tar.xz=75f3737b88d197769d999b484aeb6aff705696fdccec8f3772c7a1e10630b146 +dist/2025-09-16/rust-std-beta-armv7r-none-eabihf.tar.gz=3d988af87a2c90efc5674f77baf67f5933fe60ae6ab9f4eb4256f14d98446824 +dist/2025-09-16/rust-std-beta-armv7r-none-eabihf.tar.xz=e91a15900421a8a445a7a0038923ad197fea4ec19e972ba7ff75fd4dd7d7ac12 +dist/2025-09-16/rust-std-beta-i586-unknown-linux-gnu.tar.gz=19795fe21f305ed033354d127f8e9e7aa0e9d7c67f756d22beb161aaeb9a0742 +dist/2025-09-16/rust-std-beta-i586-unknown-linux-gnu.tar.xz=8afa409edf8dfb45eab3565add6dde2fa71118008ee1e7e1416fb8dc11f66fde +dist/2025-09-16/rust-std-beta-i586-unknown-linux-musl.tar.gz=43aaa9101863b6a419330ec75f0ffde61faf2a27f050947d2a200e5a705dd085 +dist/2025-09-16/rust-std-beta-i586-unknown-linux-musl.tar.xz=86ac890f2aaee4382a126ad9e667d53039dbca550aa548090706bdc308749962 +dist/2025-09-16/rust-std-beta-i686-linux-android.tar.gz=c3df364048b9af096c607a8e5639dc37d470ab125441ea04a2f2241766338bd5 +dist/2025-09-16/rust-std-beta-i686-linux-android.tar.xz=556bb5e7e4effb543c04a64cba683b81a6ffaa319cf1d053482968767480b9b9 +dist/2025-09-16/rust-std-beta-i686-pc-windows-gnu.tar.gz=8c141a8c3b4cdbc2eff61d2a557fa602b9039dd54ff2000d0a0b790ae58c0879 +dist/2025-09-16/rust-std-beta-i686-pc-windows-gnu.tar.xz=3995a3c391e242663533680600a38343396e2a73b6876b1dd691d83dd143eac7 +dist/2025-09-16/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=3da461c03124d6ca148ed1d4cc80be4bd0d9f4610df95770ca800d72ef6c2b4f +dist/2025-09-16/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=7eaba102d1ce8ea0461657dcce62a5f8826f2154f56f89e9385e75fdc763c399 +dist/2025-09-16/rust-std-beta-i686-pc-windows-msvc.tar.gz=d1e31c04ff2145122df2ab05af6e2ba1091d1788741234cc39524f1289886187 +dist/2025-09-16/rust-std-beta-i686-pc-windows-msvc.tar.xz=330aca62a0cb38342ce6eda427c6074651ddd0c83c4b28b53ddc736edf049ee5 +dist/2025-09-16/rust-std-beta-i686-unknown-freebsd.tar.gz=48dabb6775c14d23bf04a8c5c26f2e4d6af369dff6d506aa08cf2a08d75a5d8d +dist/2025-09-16/rust-std-beta-i686-unknown-freebsd.tar.xz=db97db17addbf5d4e314f77ac9bb232a661042edf7beeed73b252e44c6439fe8 +dist/2025-09-16/rust-std-beta-i686-unknown-linux-gnu.tar.gz=3abbec5a87dfe240bbdaf6e9a92acb2d8e6e684983803daadc7750290da71264 +dist/2025-09-16/rust-std-beta-i686-unknown-linux-gnu.tar.xz=568aec0d1427a2d866c7550dfc770be13401902741999410d4dc2e7772ec0a57 +dist/2025-09-16/rust-std-beta-i686-unknown-linux-musl.tar.gz=0739e8dcab50c87156cb3e52a18b57da6040fffd9509561fb3046e81be953a40 +dist/2025-09-16/rust-std-beta-i686-unknown-linux-musl.tar.xz=8ed8a4b1f402593db73fc2e9540664d9e4e12b8e1b5f027e56b2f3c4784a8995 +dist/2025-09-16/rust-std-beta-i686-unknown-uefi.tar.gz=40a2726131ef0e6102667bcc8ea9a64b4b19f12d58696ab434b74f9abf37cee0 +dist/2025-09-16/rust-std-beta-i686-unknown-uefi.tar.xz=500b0018fe01e9a9949ca077bb69426078a2774e83b95319d09bab5d3f6d0e04 +dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=64aadbe1da544e2f08cafb217ec1ad341b8e2adc660f60502f700773983d059a +dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=e864959561ee49a0ae680f93307929d0e16013d2ef0743a525c5d592faa10704 +dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=c709c58bb18b5a9385e07998dfe31e3ade0661fbfd4811907a6e7cfffbfe2e6c +dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=e797481997def9fe0a6466b31439d1eeb4b299c92cde3a25a7fdd557611f0888 +dist/2025-09-16/rust-std-beta-loongarch64-unknown-none.tar.gz=79d43f32d6830a0d3c9e1e8fca3ba87db44e692cc70431ac6cabea5bc59b8f0b +dist/2025-09-16/rust-std-beta-loongarch64-unknown-none.tar.xz=4d158a9d4f4e1943b2d00fd19adad762c35af24200742440bccfea62304272b8 +dist/2025-09-16/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=f0c460fee60c913463d9c1fbe369287ab436cc7e2882487afef457c78cf86ad3 +dist/2025-09-16/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=f15e2baff9b0ce68ca7e1595f00b21c45bff33a1b75fbf05a8f04b096295aa0f +dist/2025-09-16/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=102c4017e11cc3407f4871aaade7f3292c8cdc92707a243788e0e4a62604cce0 +dist/2025-09-16/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=6eaad8877694af02a10d18ce609b6678d96e75f87a1684b67279c9915601bd3b +dist/2025-09-16/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=26139715f7faac9be6e458ee1be840db044692a518cd0fde29c59454405d3063 +dist/2025-09-16/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=0edf1c0a3b8ce5dc76f1ade2dceb91f96a5afa3e54c7f25f3d3320dabeab06f2 +dist/2025-09-16/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=dbdd4f2cb5cacdf2656f83f0dc0b2101613cc344819e9ba8e790406e17d16b16 +dist/2025-09-16/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=a6dd7f0416ceea63ac723d4aeaf9cbd8d7ed8ed7d5b8925b3317cee75b9138df +dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=424eabcd66e922cb61757842c46afd298cac2e1ff809b593f6f409732261f4a3 +dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=7ac3a7e79c5b3c498daad88a6f926544eaf59d04711cf20ba05c1391544a4335 +dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=9e21d985a6702a96161aeb9ea6d1e12eb52136c7433b2ca5597c07b85a954939 +dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=69658c2818ff575eefbda5e64a2a6f253ce21f587a72afb2727466a923ec508f +dist/2025-09-16/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=25da1a37707d9dbac16d91f5177eaa5244f4280758f3b5a0bfbb09b5ae190720 +dist/2025-09-16/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=52f6a0752e9db84c0efa49826988dd64f32930799ce2e057b3a6a20a5c696181 +dist/2025-09-16/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=9f4f18ca2d1d4d7e8785f3ae61084aeaee7581d7eac2c7c883557cf30888cb57 +dist/2025-09-16/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=2ef831047ada8f706fa30a655e8a2b17c15c1f10e65c66a0d3577085ff1e80c2 +dist/2025-09-16/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=9420edabe9112cb1ab9fee01dea1bdb30a866b4633205f287767c07fb57f4630 +dist/2025-09-16/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=ed7b43b375dc7c30e1bd1add31940192978ad86c5c6684b54b24252117e92008 +dist/2025-09-16/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=cbb03936548c947633b7dff7952653aef98ae25d1ff79b8f8549e87158b6cac6 +dist/2025-09-16/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=c957ec9bc4b1345bc805b506caeeee9350e9f07d31d4177796f461cce7e7a341 +dist/2025-09-16/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=46fd5976d8bd59a89ebd2c74cc246b9727516b28cb9b1a839ac06340d738a4bf +dist/2025-09-16/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=a7640744bef500db2e7ebaafc4c93e73acb31b61faf47decef61ed46db73c001 +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=537dd37985108561d6e0afe76238193fa33f1e600aba44b484a15d1f40d68142 +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=bda3e2c4b93d864dd75bf64e08ba3d2ac9719581fb890c62073078d2005bd634 +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=3edbf40890f0f52cb61c0ec8665a43d230e6e148dd4afbdbd7ca8ad53a0a2b8d +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=a515cc391e794e397ce34eb994f3a1c35cff7e7e31d54a859141000a52397c70 +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=d45fbe65a55b3010fd8994d3effec67f96d86593d37b6fefeb956f4a6b282e50 +dist/2025-09-16/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=13f9c87c6bed3b905ec7564fb79517e891b4a70d6c5191a876844199780f7a81 +dist/2025-09-16/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=e55f12443bf8c44e27d585bc1c38472e3175bb9d7913af55ab3bcd8f23498146 +dist/2025-09-16/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=5cde9db69b110f1766da147105e3a8d879dd36c69b2367416228024d183b12b0 +dist/2025-09-16/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=3fb23f12e07d3b4ef59a637fe68c2ad1799aead28c65cf3d5d136dfea4c12eb9 +dist/2025-09-16/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=999b7f7813d0d868bbda410fa37d26ba737d443a99ad74d8c2b0ecbc24fb1af3 +dist/2025-09-16/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=12e756fe1f5b57a752916b7397b981e288cc30b5861b16c5a710778c372be42a +dist/2025-09-16/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=0d0ad94937379ac571397b7f10ec5aa20cccf5401aae16ad4d18582dc609055e +dist/2025-09-16/rust-std-beta-sparcv9-sun-solaris.tar.gz=4bbe087dae754754f4dc1c05aaf664c5e0dd4429932feba88aab744e7c5b7472 +dist/2025-09-16/rust-std-beta-sparcv9-sun-solaris.tar.xz=dfa49be70f7ee3989cec7f634cf84e3b418f48882af8b69540e41f2ffb8cc216 +dist/2025-09-16/rust-std-beta-thumbv6m-none-eabi.tar.gz=598622e28c6ce111f4d47e2172a0657c3b0ca62c42895836376b1959fc2e094a +dist/2025-09-16/rust-std-beta-thumbv6m-none-eabi.tar.xz=359bb98d293e1cea22a505377ce49ba95095c735b50b7d404ce4f36d84bdfec4 +dist/2025-09-16/rust-std-beta-thumbv7em-none-eabi.tar.gz=d1f994c835b1d72a665eeb7aa4683a09ac60c1977766b066c817c3eb37ad3a4a +dist/2025-09-16/rust-std-beta-thumbv7em-none-eabi.tar.xz=22e8605af621866d82f2bdcd8089a561e10f4055624aa6dff5d704b953b7d547 +dist/2025-09-16/rust-std-beta-thumbv7em-none-eabihf.tar.gz=8e862615db8daf2c063d27f1860bd083fadefd459634e61b946ca05b8747308d +dist/2025-09-16/rust-std-beta-thumbv7em-none-eabihf.tar.xz=8c9b3d2913242cd2e7fc690e02bd28690fa50b7278befa8b57ec9b7eec4f34e4 +dist/2025-09-16/rust-std-beta-thumbv7m-none-eabi.tar.gz=fa79d4e07630821149b151bc5673205a479570bfd971e66e874e75b5046bdfc5 +dist/2025-09-16/rust-std-beta-thumbv7m-none-eabi.tar.xz=f6f3c14f35d3b5fd01a6244f802d99e261e529bc6d4a6c5749b1cfccd524c16b +dist/2025-09-16/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=194bc6660b3d4809d5f709e7f82159141e90b31d2318cd550b4deab65e108fe6 +dist/2025-09-16/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=36534004068562d8678a3cd73f8f82b50406aad6daeecf348246375da3bae720 +dist/2025-09-16/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=622ec13126461d4d6c1f7eb3fc387279c03ffbb96fdbbfcc7c22c2433e62232d +dist/2025-09-16/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=a8162987ddefbde68e2931fc824371aa71d595106488a48732b67ec45f36ac4a +dist/2025-09-16/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=6d7f31ee7fd8633d8497cddfbe05a1ca064c4d45f50066e86cd82fe4b40b93d5 +dist/2025-09-16/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=9b3309e290007406d410b8ee86ecef887d5c48b515b11cf7827baef23a0c4a7d +dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=74fd7db50be0b77e7a44d1a32afc76f2a438fb789ea32f3ce1ceff00ccbf030c +dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=e7736bc6c78eba1947ca6ec4ae5d875de192f44755ade737ce1e367f294576d8 +dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=d8905fddbae4ae3e39439fe5234cd05454034d9f2a847056766bbe41126405f1 +dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=94628c4c60615b15f1c5b331a803d4d1963289b770ba54adc4458e52d17397fe +dist/2025-09-16/rust-std-beta-wasm32-unknown-emscripten.tar.gz=68c53c1069b29461573c68b5df7ae2e30b4b7dd682cde70f26a200854525c012 +dist/2025-09-16/rust-std-beta-wasm32-unknown-emscripten.tar.xz=cc1a4d61bc45832fa1ae19ecc1347d084249f1be3553fb33d33f0e5d8df5fe5d +dist/2025-09-16/rust-std-beta-wasm32-unknown-unknown.tar.gz=a33ed15817407053144b8a6041dab287a390b3338dafcd1b02418b3dbe18b024 +dist/2025-09-16/rust-std-beta-wasm32-unknown-unknown.tar.xz=f0df12d2767da7528c614f5bdf6a9c6bec831b4020eed357409d3fc7fb0c869c +dist/2025-09-16/rust-std-beta-wasm32-wasip1.tar.gz=86634e652b087947b914b590a2e978ef1d29d09b69afff63a682f26b6dcac41d +dist/2025-09-16/rust-std-beta-wasm32-wasip1.tar.xz=04e2a9c6914b17664443b674827ab706869a81200c885e97779df0da98682612 +dist/2025-09-16/rust-std-beta-wasm32-wasip1-threads.tar.gz=3fef167485111064b1d468e5664d9173e24dbf20406165f92293f304457aeee0 +dist/2025-09-16/rust-std-beta-wasm32-wasip1-threads.tar.xz=7e12cabce073a54fbd4c97d3169e34e4beb3872a161c7c082e148b0d4adb2f7b +dist/2025-09-16/rust-std-beta-wasm32-wasip2.tar.gz=3b8f3e6d4ce34bc8c5300b77bdf875b04a69f87f73d930439bacf56f061f667f +dist/2025-09-16/rust-std-beta-wasm32-wasip2.tar.xz=056ad523469276f90f185b72696b20c6b47f57b5721544a20f6e765cc51769fc +dist/2025-09-16/rust-std-beta-wasm32v1-none.tar.gz=fee626762c4bee195d800883ba15d890604280f0cddb08aa64a6199fc4591635 +dist/2025-09-16/rust-std-beta-wasm32v1-none.tar.xz=7bc289363efb2d5555e9742bf619decc2454cb784c133db55a71be4c0aed9f90 +dist/2025-09-16/rust-std-beta-x86_64-apple-darwin.tar.gz=51cf2f8f249da5df653efb073c81047583bc7bf952a4b03d6d05c9fecb7fa0c7 +dist/2025-09-16/rust-std-beta-x86_64-apple-darwin.tar.xz=4e380037df6b548916136138069e76139dbc069688ec717632c30a07854d0739 +dist/2025-09-16/rust-std-beta-x86_64-apple-ios.tar.gz=b232b73befda81ffaf0d1db7838e2057c503cf2f4f1e95f0706cfd0faf77d853 +dist/2025-09-16/rust-std-beta-x86_64-apple-ios.tar.xz=1431fa936f5909e4e92894c7e2074d3db879c10cf3f2344b050f9c77965d1373 +dist/2025-09-16/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=fb0a1f0228c051a85440933358f7c00d47c4ce44a15a0b0025b00331ec53f876 +dist/2025-09-16/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=b725c62998a3b28caa1f89549230ff275af854a3d04cdb6562f19be7e8a1af31 +dist/2025-09-16/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=b9588bd0c684a50e0d73717f5ae5c2f0e36a90052126d2c4a539e5a116eeb4e0 +dist/2025-09-16/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=70affff2a14340109f86ec585b65bed16e77612ac7b939336245cc15f7f6c300 +dist/2025-09-16/rust-std-beta-x86_64-linux-android.tar.gz=fb9fc9bbfef414092ad1082d7529fa2efc52adc849a7f45da3d5688d5f6df1d2 +dist/2025-09-16/rust-std-beta-x86_64-linux-android.tar.xz=3fd9b4d89e7ee604f842f7915b90bb02a0c44f8a4694a2d10f94efe7bbf0840c +dist/2025-09-16/rust-std-beta-x86_64-pc-solaris.tar.gz=caa39b90ac5ff710cc76828ca52c784e48518b9c3f1c58cb6f6603a7d05f87b7 +dist/2025-09-16/rust-std-beta-x86_64-pc-solaris.tar.xz=6034b44e5e7bca888027a21d8a4b1229c08fd22b744b83ab817d487aa5e6db56 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=6e7a9aa27a85a09c603fb624ea1cc56120ccbf5b4fed1be8d0097232cd1bf849 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=00f6c500331f2f75ba9c56f896db1420c353e8c0fae450da6b5806794cd58fc7 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=9279908bfa629467d619143e5e0abbcec7a843dddd0f6737994f636edb171e90 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=f1eca139e01d2a8a2627c064f3cc14d7a2016c50738fdc19d3d586f225066f79 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=1fdcf7de3772c24733474e5c0f3e64b56ca627e5a235b890ba0efa2265648d79 +dist/2025-09-16/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=23f9e2447316fda77b7a5af90c160c89d9f5ed735c7d5715cc2850b92fd1cfe6 +dist/2025-09-16/rust-std-beta-x86_64-unknown-freebsd.tar.gz=8dbdbe296c35e64170b9d1e7361a90aa260e456ea7ae4bd969db1dbcad976532 +dist/2025-09-16/rust-std-beta-x86_64-unknown-freebsd.tar.xz=9469e49e425349514f2bd224ccc64903c163bb481a425e01122bd3bf00ebc90d +dist/2025-09-16/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=6e97622b123772e4deaf91b81d1925fe2d39b87f567b96437c03c16a0982b709 +dist/2025-09-16/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=0affcbb44870daa9410887cea10844ad1f48b4e1aec053274e86e39b42aba360 +dist/2025-09-16/rust-std-beta-x86_64-unknown-illumos.tar.gz=3023abaa7f221946e1e9cd40d4a607e8c57bddf8a250a27e0d58dd95a51ef877 +dist/2025-09-16/rust-std-beta-x86_64-unknown-illumos.tar.xz=1e76ba54bdd6673428efae1407e0e5991115f59885159e68e9d63cdfda9ca7dc +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=92db1c0d785820c128bf4ad05b567b17871d2f01ef902052a90839e3fe744979 +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=233d981cbd309a03b16c5a80037faf992c47548cedf5ab4593db41554ffb8525 +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=fe3fd22ad099bae73ffcf1a4881ee2f2c520c6108ac27ca0c64fc5111ec02230 +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=027738f65f61fb77ceff38d9342915ca8344e0a1323e7659a1a5159c99c4a811 +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=e6527497065541fc4701e71efd0a896a8048b653454a75f27765e5b7b6ac748d +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=807862fb874b7bee60b2740ed2df2454d8be50b0877d34b7bea796cfda69d583 +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=17259bb2f4bb1183ecb187296ae9afef58aa2f8e816d2764caf48aa3900fd66c +dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=e459cff284cd0ee388ab65c945bfca4c64b32fd268ee66c2cd8abc325580ed12 +dist/2025-09-16/rust-std-beta-x86_64-unknown-netbsd.tar.gz=5a60ff084d1da42324d6956510a68b3b83a47b432b48e2228e58f52dc8c01b69 +dist/2025-09-16/rust-std-beta-x86_64-unknown-netbsd.tar.xz=9d6823b8b1605f748f1d7af5c52bed1dd926a1d5fbd01ea9a8f939f3648b7800 +dist/2025-09-16/rust-std-beta-x86_64-unknown-none.tar.gz=57b0a2838c59feb0808462fc043c9a85a292f0025104dd6de3c5e78d4d71894d +dist/2025-09-16/rust-std-beta-x86_64-unknown-none.tar.xz=e5a5cd9c861b29d11ae110dd4b239d5069dd0aac57202bfa440e15869f5c7e9e +dist/2025-09-16/rust-std-beta-x86_64-unknown-redox.tar.gz=f9cdee694af41422f785126fda566891357295b5fae56268188bbf2978b92089 +dist/2025-09-16/rust-std-beta-x86_64-unknown-redox.tar.xz=8c7668ceb1611dafd9bb9ffa6de9a11e39d049cebeba05e2dfa889e69d94a54c +dist/2025-09-16/rust-std-beta-x86_64-unknown-uefi.tar.gz=33045879d1aebfb17faf93bd471bb4c4ee3b9a787c57704814ab2dd301ac8ba6 +dist/2025-09-16/rust-std-beta-x86_64-unknown-uefi.tar.xz=28018cd9cd16e428790600decff5c7df749fe500861ecd3853895ebe69fb7100 +dist/2025-09-16/cargo-beta-aarch64-apple-darwin.tar.gz=f0642e83a5a6c8654d724e4929c0e58a4c07c330eae58fc4794ab94721b2732c +dist/2025-09-16/cargo-beta-aarch64-apple-darwin.tar.xz=13cd17e3f35cc0214b25b65463b2394f3642ff8acf4e13af20b442b0100556a3 +dist/2025-09-16/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=fa047b7d7ad4ab64b85ca7beef052a66a89c60f976b099364eb3acfd2867c3fc +dist/2025-09-16/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=df3a9254c4f329454dae9428d88f6c3e69ee8a120bf2eb380193eee63620ed69 +dist/2025-09-16/cargo-beta-aarch64-pc-windows-msvc.tar.gz=e5b608ae84d9258bccee0ecbefaefbaf34b989cd656f096692ea84c6f8d78645 +dist/2025-09-16/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8ad0c0d49f7f6ebc5cce4a2b76036c2d0eb8adb38ce11125e2b36a1291b8ebd7 +dist/2025-09-16/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=f454f83f4ffe0095d3285011b3abae11b0331d87d398ff95622e9091ee09826a +dist/2025-09-16/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=29c83085c7d51874e943b429999cf7709c0cdc422b1254a5e587aecd653430b4 +dist/2025-09-16/cargo-beta-aarch64-unknown-linux-musl.tar.gz=8f85db99809ae593abb4806ffced52abf048dfae81891ab68ef4c83f11f03fb2 +dist/2025-09-16/cargo-beta-aarch64-unknown-linux-musl.tar.xz=e5f004172331f658c3027084950360e1d60bd0cf3cca83fa43cf7fc251cefd37 +dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=1aae91a92e1d41e9c53ede795f91e7dccd3dea687d84151e507f4d3af089f15a +dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=369b8686197e4be3883c3225bb9c186b17839552c91117750abf428255df9c8f +dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=b2e9a3c467ca2847f4ef55241b05a4f5151ca2608e05a43e5101c62d9d6a399d +dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=aaf6bfeb90e732627ae98f4b86083b3a902e31a951b98b3b5e94b67ffa6771a8 +dist/2025-09-16/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=75bcce21bcd655a537b77aeca644f5279ace4f782a97d1e82de3cbe76a7df6ee +dist/2025-09-16/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=287800d221906551a50922caf0637049f3490e17c65d62119c6a26ce48d5ffa8 +dist/2025-09-16/cargo-beta-i686-pc-windows-gnu.tar.gz=89a6059faab9a3e912dc2baae766ed232d38c240c90f503bb2b486225457e8f5 +dist/2025-09-16/cargo-beta-i686-pc-windows-gnu.tar.xz=96e5b7238f543edbcfb16022ec6aa6d3643add1f7b4b0febc3e35ff9d173c075 +dist/2025-09-16/cargo-beta-i686-pc-windows-msvc.tar.gz=39ed55c1619ade6fc49fefa5ba5bfbb67aa34e53d35384a35c37d0681596b9ba +dist/2025-09-16/cargo-beta-i686-pc-windows-msvc.tar.xz=d9ce7d301af311d58d1b4342855b70dfbe0fa8bb365b78bd83822e58a7ce9086 +dist/2025-09-16/cargo-beta-i686-unknown-linux-gnu.tar.gz=2dc0a357f58462c5474cb5c83445bb17d22450056fc77d3a92c844f104e1c96e +dist/2025-09-16/cargo-beta-i686-unknown-linux-gnu.tar.xz=c2e4f00fc345a77da5f13642d6b65a77df553ac0cb596fa34f837d76fac460c8 +dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=e1b5e490a00eecf93d7c591628c8d231eadb8c68104978b623fc16c5369248e8 +dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=c2f91d72d2977168eb12179abd98b4d18f67de4ed455856f7af18a860ff5b1c5 +dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=7fa229d54c917a4872efbc2e9eb9181f5b1133aa44508548e94e4529ab099cd4 +dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=359d7ec4bc7e0eed1c936bff9cee56bc3a132a488499cb83a8e7561222893c66 +dist/2025-09-16/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=fe514bf5afccb535517decb4c3120375eefc36def2190d60f4f0f6d796af1b79 +dist/2025-09-16/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=d3d1307f3f60e9d8ee9f199441185bcb52a7bf5de460302291b178ef282a457e +dist/2025-09-16/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=e7c77c9baf683994bf180b8906d55a2117f5bff85fec6c602a6f1912c39b2817 +dist/2025-09-16/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=207e6fe0baedb7f19e8a0437e24cfb9b46d4131a141863c93a52d00f1b049d8d +dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=87433380a70642842f7dcf4a4c8614cbc136a18198837e631dce419e99ae71b8 +dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=207106c6e7c129fe19940da029360f8ce42f29468836a094a2b02156121888ed +dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=ad90e19faff850cbb03eb04195943a9f65169d6a7b030422f0310f8c053e61c3 +dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=7abcb3c3cf3710fcf41d67664137bd0c823792a70cbd82004e0cbbef58446f44 +dist/2025-09-16/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=7acc6031840a56c47dd2d52a6ce169663ffadb108e92743b22a6dd9a7227baa4 +dist/2025-09-16/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=a2ae500b6c44abeb6cbb42a07a37a8e0ded707fdc1e3f4d6159aaec404912f5c +dist/2025-09-16/cargo-beta-s390x-unknown-linux-gnu.tar.gz=3a69ae63bde95a6dd4020395b90e821e4342fa1b45b00d12ea30f3b1c643ef59 +dist/2025-09-16/cargo-beta-s390x-unknown-linux-gnu.tar.xz=231b1b052018ab851c57563d3c3b4a943cc01f99e3ca6795dfb6a1315d2577c8 +dist/2025-09-16/cargo-beta-sparcv9-sun-solaris.tar.gz=41677b7f137d6509bf7e0fa2eaeefd91d03316e566136d37d006fd98a2a4c28d +dist/2025-09-16/cargo-beta-sparcv9-sun-solaris.tar.xz=f5b4057e69c50258c3edf00dd71f47951833ae892b121641d8d3a0bd38991838 +dist/2025-09-16/cargo-beta-x86_64-apple-darwin.tar.gz=edfe3b23fe5824b5ae763871f133cce684ace0999afd38e9a7b2a08f17b12ac0 +dist/2025-09-16/cargo-beta-x86_64-apple-darwin.tar.xz=173ec4d3c129530c33623f2ec6a5fdc5d739ecfd24f7372f3713ea94aeab8eb1 +dist/2025-09-16/cargo-beta-x86_64-pc-solaris.tar.gz=fe0f57ea7eafb5e157a04cd753b4f6b733245fa8e9baddccf581cec0a7ea2ed4 +dist/2025-09-16/cargo-beta-x86_64-pc-solaris.tar.xz=61ac8a524964f2179c8195d821382597bc8641eace9ba1c27e81453ab8c347e0 +dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnu.tar.gz=44200f95def8558a8b7b85c11533841786d3fd17331e13056dd7ce25de667b2c +dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnu.tar.xz=7144031f2f69bb3bba08cca81e15dc557424c096f9555678b1bd1dd5f4891360 +dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=a7a9c76ec0cb87a725c44336cad0182cbf2c1e06d19cbfa0311501541c08c852 +dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=0b66de9c68f0ec42a410a898468dd67f147284911803207053a01fb2d29b65d7 +dist/2025-09-16/cargo-beta-x86_64-pc-windows-msvc.tar.gz=de26ef4d9091775cceb21af53cb1177922f8eb93763c0e333e59205dcb05ffa5 +dist/2025-09-16/cargo-beta-x86_64-pc-windows-msvc.tar.xz=e6823ffc56d1136d02384acd09e73b6e45ee197c2126b49aa95df832704b9d24 +dist/2025-09-16/cargo-beta-x86_64-unknown-freebsd.tar.gz=dd0656bae2fbf483ebe641ed952499f0127897b09fe14273046cc8e0c7611825 +dist/2025-09-16/cargo-beta-x86_64-unknown-freebsd.tar.xz=6c8b65ac66bf730f9c5bb7caffc75ab1912439d033130438d98ebdaa1a2b922a +dist/2025-09-16/cargo-beta-x86_64-unknown-illumos.tar.gz=5c3f38613b4d2f26643bab1a6db5b769f58878e47168dc92bc2c7b2fe9559ae1 +dist/2025-09-16/cargo-beta-x86_64-unknown-illumos.tar.xz=046ee0c80efe70375179c95461af5724cb00eb9ecc1d995e4a629d316d853686 +dist/2025-09-16/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=b6da1a94fa5718c5a1be4d67d6422d28e62365a433d82f11fe7f09149690e78e +dist/2025-09-16/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=5b8aeabb11e88e4cb02c766c11c0d3d826ded4d08874a11127d8f73f033b0072 +dist/2025-09-16/cargo-beta-x86_64-unknown-linux-musl.tar.gz=00f191b30c29d46e503bfdc49c112ac5295bb67caa091333b0caf210e08d3f01 +dist/2025-09-16/cargo-beta-x86_64-unknown-linux-musl.tar.xz=750060cc199fac6990597a9859234392f4c0a5405d87d1234fc7990032b4c138 +dist/2025-09-16/cargo-beta-x86_64-unknown-netbsd.tar.gz=12513081e2bd88c666881e77e3c4935c710cd6132d9ec48cf9049ac7ece5dd9f +dist/2025-09-16/cargo-beta-x86_64-unknown-netbsd.tar.xz=b1338ed261e33b0fa3e8a63c8930835d87d352ec10011bd4d626c44fa0b0392b +dist/2025-09-16/clippy-beta-aarch64-apple-darwin.tar.gz=668e7fb88b52ad92634fc4d39885843b4893fbb9f34062e8c4fa870f0e71fbbe +dist/2025-09-16/clippy-beta-aarch64-apple-darwin.tar.xz=a0fbf1903d1a95c6ca98ce98a3e639110022480d56c20e175e4fadf211baadda +dist/2025-09-16/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=6593c10385d78ba68b38b8815e5c7aead991d83d9f689a42f4a2911a5f7f0450 +dist/2025-09-16/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=856303f96ed6bd1be89295dffccfc944cd70ce3d1921afc9366f19c6ac800b60 +dist/2025-09-16/clippy-beta-aarch64-pc-windows-msvc.tar.gz=e60f30cba9fe154e7446cf5b7114e7f8c153937eb58cca4cb24dad1b8dd453af +dist/2025-09-16/clippy-beta-aarch64-pc-windows-msvc.tar.xz=24ae9f98df0f2320271b8250234f571b64a4b5ce983a083f45966e58f39b1c94 +dist/2025-09-16/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=aeaf3ed45a8feda550b2043fcbc55ca9fa3eb95141dfecabb794fa9003d76a3b +dist/2025-09-16/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=3187bb767e36aee12059f1d2b77697aa57f71a3c478930c4901dfcd25d414369 +dist/2025-09-16/clippy-beta-aarch64-unknown-linux-musl.tar.gz=c2c23a91b2cccd4978fe612f22c33773833c87719259bd0d501e5a2523f3c689 +dist/2025-09-16/clippy-beta-aarch64-unknown-linux-musl.tar.xz=b7902a5d4dafd1301fb573148b6d1164b9365e976a9019151b66aa93b88e93bf +dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=69e95f21ef2f59df25ee56c2dc10ed5306ca5b41299acea22d2d87d967d17575 +dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=24343efbed86314a6c292659ee0625cdc30bd655eec8009d8e2209a659dcda12 +dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=404b0db1d121ad8fb1a43ecadaa84a79f976a68dbf088ed47fb31245e293445e +dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=f4fbf3a39e1b5d61a481109da644e44e58371c00a3224b187dfbe39827615a33 +dist/2025-09-16/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=fabfcb98b0d9d69e5d022e40ab572d2e84c604dc8d4ba2c88d44501c2a7390ff +dist/2025-09-16/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=8c289d02256ff36412231b320c723becc8d83d5acf9c2325541367eb66f6443a +dist/2025-09-16/clippy-beta-i686-pc-windows-gnu.tar.gz=13838c1fa366173219023000810ba180899b843f7d5c54ffb415c7a517f5af80 +dist/2025-09-16/clippy-beta-i686-pc-windows-gnu.tar.xz=a86b22e78175f89ff2b4dd40b9fbe91b4447914b226d7ef7d020903ed2c9581f +dist/2025-09-16/clippy-beta-i686-pc-windows-msvc.tar.gz=fc2a9397912c6651e04dca0475dc74fcb9a50f370a0ef7f9c7987158dfc58d55 +dist/2025-09-16/clippy-beta-i686-pc-windows-msvc.tar.xz=bf4be32251bac244540759795afe760be13a134580cae8e3fd5956d8ed45b254 +dist/2025-09-16/clippy-beta-i686-unknown-linux-gnu.tar.gz=10580cdb4078d6a27e511ef71ed1a50f67202c1ba145fe315b63f2786ecf6428 +dist/2025-09-16/clippy-beta-i686-unknown-linux-gnu.tar.xz=5ef51556bc3b002a97e7b9f092ce280ee3b5c4a6b02a8d382d87a3f56d90eb04 +dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=34756f99178d5be61df2375f4161aacfb05fbe4d38e9259bb7a01f71e842d67d +dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=1d10a5f1ac3bb2bbd84c421cdd1978865fccd214c08a341c7b6527c6674205bb +dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=c0fc0dcc1a78960b53de22703e7764bb64bf35762127a8f272b45e95e64c7bc1 +dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=f41d1ac85d5714c6fbab9986ced2c80bafdf8251f37f16a5a9da994356b5912e +dist/2025-09-16/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=6f2cae468cc556a4083771c6e3e610f759cad00b7af7255db32d461d6e4f3dcd +dist/2025-09-16/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=6df81ef270b67457b96ae04da2801c2a80f9604df9c902e74140ab4b154a10ab +dist/2025-09-16/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=fa4b89d3144ae42ac8c69f85f7b63f46cbbaefa1c3063f719a75dbf8e3ec39b2 +dist/2025-09-16/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=944976f337f4c27176ac614abc918a2bb037d30c554ba5e92e180d7247089cb9 +dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=30da372a7d750fc5e8509cd2265522b7d3d24ab8c37af24076fd0c6f48d47ea4 +dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=dfc5382049b525b461b5bd343f214f357bd3cf1a66d4d226658538639f4ca85a +dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=f5f500c286fc90f4b7808d45a77beaecd7ae3d5c99522dbc80f09132f078fbe3 +dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=5a5da7891fae2f8fd1fba6a537cb3f26cf0aa73f0e36d01181fa20a82be3536c +dist/2025-09-16/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=538b09d45dbcfed24f521a29f5107a1b39efd8c3217991c21eb6c55603ab267c +dist/2025-09-16/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=e95cc43b991ab4d0f16c593171231bc134538f64ca40ddcdcbf5d2b73bce3307 +dist/2025-09-16/clippy-beta-s390x-unknown-linux-gnu.tar.gz=37b1903fc1e58f57574a2a6ad23014443d72d6e8aec35df0168028acc813c8ba +dist/2025-09-16/clippy-beta-s390x-unknown-linux-gnu.tar.xz=04d65883b79cda42a26c0581349fbeb7023a25bb73db2e525483fc4895386e97 +dist/2025-09-16/clippy-beta-sparcv9-sun-solaris.tar.gz=057cb0d5ba11a7679a17d87304d1301cf583c86ad2faa23fc449d3cedf0ea1be +dist/2025-09-16/clippy-beta-sparcv9-sun-solaris.tar.xz=8cf1abf779a8a5a18561f643a8fff9863909be7c3f8bc84be2d817fc262a1215 +dist/2025-09-16/clippy-beta-x86_64-apple-darwin.tar.gz=a1301c459894d6d9837b98ec8af238b26c401b860a78d458e34c3d747a8b5de3 +dist/2025-09-16/clippy-beta-x86_64-apple-darwin.tar.xz=af863a3fcae2193f8cc26d9b62854bcba6e029b0bef0f2151321bbabfc931c3a +dist/2025-09-16/clippy-beta-x86_64-pc-solaris.tar.gz=5a3176859e8f783d8bf687d578100dabdd8196e52290a9de6b6902463b4f193e +dist/2025-09-16/clippy-beta-x86_64-pc-solaris.tar.xz=93e37f354b89c94434c3c294cc54522ed503687ef62bb70a146196262993ce1c +dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6403ea6cc8c3c3f83f29d894153ba3bc956b19fc213c11ed13d1117620fa543b +dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnu.tar.xz=a9ed248496eacebd88e33715b30253d9d2730087cff24427b34cf07b2eb89a1f +dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=5a72c9e26e4d59d013243d0d2416fe5280f2eb915f760470b4501731d8e8e05e +dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=078dd02a254accc28ee8b26a5e4b3349f013c629bd1da49948f0071184727ab9 +dist/2025-09-16/clippy-beta-x86_64-pc-windows-msvc.tar.gz=288ff79cc4cc4aec96d6ecd92c733e18aca2c846c87359e77d825cf57817c1e0 +dist/2025-09-16/clippy-beta-x86_64-pc-windows-msvc.tar.xz=fbc1421027dc77265be5d106d6db139a6a33b4e5ade0b1eba17e17a4ea59483f +dist/2025-09-16/clippy-beta-x86_64-unknown-freebsd.tar.gz=1fc82034c6b8b38d60788ea3addaeea8ef4448d467def0a92bea750d060c4d39 +dist/2025-09-16/clippy-beta-x86_64-unknown-freebsd.tar.xz=3a3b7d4f98df124bc7c73ae46f0c776320c1a92c0d1dd7d7caa0d138e7068a3f +dist/2025-09-16/clippy-beta-x86_64-unknown-illumos.tar.gz=f02880e995ed24f200b5c1711f2951e7eaeecc4ab144e50d866f11959e8fbeec +dist/2025-09-16/clippy-beta-x86_64-unknown-illumos.tar.xz=a99652b13a6c3570bcdc41638a59b6d68b76562000ceb60a57128ee5065778f1 +dist/2025-09-16/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=657275ab960c8c65b55b22bdbff5040440a039c9631017645a37506771f3d443 +dist/2025-09-16/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=4ababcc7ffc0976218e62bd55666ff06e01350ecc790ae06effb2767521e169c +dist/2025-09-16/clippy-beta-x86_64-unknown-linux-musl.tar.gz=5c454d363e6f8615aaf4d93e39306972e65a544f3a973c034333022aee321f33 +dist/2025-09-16/clippy-beta-x86_64-unknown-linux-musl.tar.xz=327cb34aff228c3fbc3efb389ddab0b50e5e135145602a8f9e5227fb70a462a8 +dist/2025-09-16/clippy-beta-x86_64-unknown-netbsd.tar.gz=87426803e7fc0bebdec6ae46f8ce536d947bdc57294e592599cb54d9d98d3d50 +dist/2025-09-16/clippy-beta-x86_64-unknown-netbsd.tar.xz=021d69fe47201dc89f394d084b70491d15718b24ceeba56e7cda18e3572f0309 +dist/2025-09-16/rustfmt-nightly-aarch64-apple-darwin.tar.gz=fa289216a17ce0b4fce43862f80087e4bcdaae7b20eac1f3776488b3986db45c +dist/2025-09-16/rustfmt-nightly-aarch64-apple-darwin.tar.xz=1dade784b9caf54bc3ec5c9021d7c4ed1307f11f71d12af46961926a4ad6f7d4 +dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=0f221f2e4d5bece40307c4f7b095dd377ffadce916750c27bd7a2d5663eb6b37 +dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=3d7cdda977ddd4a49f27a2fcf9e0027ce9aebcc5f250e25c3788b6bb5169e8b0 +dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=28a44723d3c4da225592786db74f58af4af952b13112469910b5a2f58a7eb20f +dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=7b4b9f2804488f90bdc9b4f5ac19aaaa9b82cff02c86b95aa0b7df0c805502b5 +dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=df6b42adce548127a2a549bc7b6e574de86c22533641c5bc725c1d5ad62af651 +dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=07d680aa64743ec6e8c417ff14747567fa6056738d6ed8c06db2c63c544f5201 +dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=66262bcc6e4fdffef1e9966bedaae64f27169980987425323fbf3a73ad46792d +dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=f8125405f6437be0357eb594fc74842d6995e113b2475fd8fef6f778dddba539 +dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=bcf9fdad0b5209b1e12fd81a18927698f468995e708dd56e8212f977d4ac39a8 +dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=f7a7289e5f48ecf9ddbb214e6ab47280789a14065960f40a92d8dde3e1df57b4 +dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=e463b2bd6236463c68b2d0377bb33adff0daf17fc47dfe8f0857043b793a847d +dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=96c15e4bbfb17be75003ada804b2da5bd750889c783e110a5a3481f045ecbfc7 +dist/2025-09-16/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=6e53b146000b91f569aa5807e69dca726068d2c23e4a44c344592ef7a96927ec +dist/2025-09-16/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=a59f630b27fb33d188ba0dfcd3563a69bced49dfbc0fc1545b0c750bed41506f +dist/2025-09-16/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=ab6b4c2728dc0381f92af8b9f9c730507709e40ca7da89700bfca9b7adb93034 +dist/2025-09-16/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=71950a1d4c6b04868cec2fcdd7c80bbdc675ed5c25917c774e3b6445bdff21c3 +dist/2025-09-16/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=fd25c154f34b662d69a6d5cace59ebd605a1c4085a14ff8b8d597f5cb7324e95 +dist/2025-09-16/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=15a2a897028e11992d2cae90d2a00f730a65f8b62ad500ff82c2fa83767b866e +dist/2025-09-16/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=9516f97a5807b07d37e95a258b9cd60305c2b0429911c2425716e7f77a556eb2 +dist/2025-09-16/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=f665d43854449cb4ade4c518671ac5fef4a9640d276ff7d90c51c1bed08f9909 +dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=5de565b08f2641f3a7b58f79f5c63905859d39573f105ba05addcd4db5cb30de +dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=0f3d7e4b91b6cab8ec9c534ae0f82ba79c57d621f25baa2b408bd2844edbc017 +dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=7a33a965a41fd3958df003506da405a42cd459bc8689aea530b4a66122eccd3f +dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=b38a0cd88bec44e60b52e414237f50352900afb66e3f2b829acf92de5c060e3c +dist/2025-09-16/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=64cc62b309d4be3b004356637d726776c6765cee590cb0ca244e2bc3feb6368a +dist/2025-09-16/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=bf873d8101d8f2f3574387011749b3375298c7a828ba1ebed91536adf50be1be +dist/2025-09-16/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=3fba5ef7cdf564801de103e7724ac0d9096cdfd8340c75df09bc0ddc1e71a74f +dist/2025-09-16/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=c5a8af0f0689aabd52a337eb11a601877cd9f74efa2734d872f2752413551aea +dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=4f1d6e182fbe1fc1a87949651e64a7f6896c3b359b7937b02d2c02b8e9db952e +dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=29f7583b6f0af1239194c8d936a9f7e38f58920f6a4888d26ea1a48ab8c72235 +dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=f937a74b730cc1d064e0b4be538fa5bd51f636f8a5979367cff3a611d7893f45 +dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=d208b1998db3a896ea9c7c79ded0ad6ca4de452d3b6e696ffeeca44fe9fbc58b +dist/2025-09-16/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=6816dc3cdd53c883a7cad8e7237c1e22264dfdf6e1a8033b41887a52cd43a8bc +dist/2025-09-16/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=904b26d880f6b364458e9d0f682cab3a136b3498f27c913d6f422de6fa822e60 +dist/2025-09-16/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=373a93410cc2fae30b952b036487417ec9e6211cd2e1980e60352329444a4376 +dist/2025-09-16/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=8e1e328f2312cf458f8d7288624c11cc5f39bd7c8feef62147216689a5c28a13 +dist/2025-09-16/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=8f22b6753dcad78a46cdc98251b27672bfa94f2ee11a655705fd7fa8a469ae82 +dist/2025-09-16/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=b719b7167321d4be13eaea674f713269c35e4d34dcecaca38e32cb72aa8632c4 +dist/2025-09-16/rustfmt-nightly-x86_64-apple-darwin.tar.gz=63006d28e754c6e6afc5cea25527d4ea628ba084f0af52a36b676faf73f22e07 +dist/2025-09-16/rustfmt-nightly-x86_64-apple-darwin.tar.xz=b74a4e9726b5acf7133511b855088aa3ca9eaf5e5cd73a6d3b054e8a263ed6f1 +dist/2025-09-16/rustfmt-nightly-x86_64-pc-solaris.tar.gz=a3625dc34b971fd11e3d2f895a4933f4ec6b1e01475385cfd02e4eded528a99f +dist/2025-09-16/rustfmt-nightly-x86_64-pc-solaris.tar.xz=1309352fa233debe6b1a06664da7f8d6db9e8b18afb60ca5709ca6a5ec657198 +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=b765f3a632f74d7624ad10afb1875716b836dd6e5836736fbed6ce5fe7b00594 +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=b74f1b6e563790926629952d25199eed14fa15e94845c962c79ac56df64cd18e +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=365e182e871e46e9f54b2265996c4531ae44b5f93a2208a5debcff5367026692 +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=34712b98cd806caf8507b3afb292cd7bbf41599a395847336f617a9c952f911a +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=d04e6573a9253d4c5bf512f7b65415f08389c499d0024ac3990110ec1859b24e +dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=98de7fe5863ce17a6f3c35ef9baff92124c5a398a008d80394a1ddc61fd29390 +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=01f052f3ec99702f7747610c3a5fd0a440a7a38bc5f4da93be9e6db48b599f3e +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=38dcd9d9fedce55d75ea7151937cbdc4f6f76d92eb8039840e520faaa870182f +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=0341b4b7e5287aecafb3097334eeff773c83d5267c3a1c6edcea6c6ae1ee29fd +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=42ac68f632ca2d78b9b4647bde709eab84765f4376722ecb560d099cde89c7f8 +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=af7491f1d5fee8a52768397966b75ca4104400e7023f373dd8b188aab4ae1b35 +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=38cae1811a0e18ddaf094d3931d8d532c5eaba03fa2cc1149380ff9393e8f84a +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=631cf4d825237406f2992f44f26086672886b61fac0d7ff7a4991b25ec756ebb +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=ee52ab0c86918b2a155098cac489f5e639c0abd7064539d0e8a6a7502ca33c54 +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=67de842ff1f8cf99b0ad88ecefc17fa36359fa718e3fc04f243ad8c1595987bd +dist/2025-09-16/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=349480a812b9861112e80ab86d3e80318d239af5212b314b3a7299dbbaffbf8e +dist/2025-09-16/rustc-nightly-aarch64-apple-darwin.tar.gz=fe1ffcb90bdca92a61470c4f7cdf7bcdb4e7bf7c685ac0b92eb3a22509d88963 +dist/2025-09-16/rustc-nightly-aarch64-apple-darwin.tar.xz=b55d2837dcd2bdfbe8444e29d0f36ea584451c4f0b7fb7e9bcd77279e8cd820c +dist/2025-09-16/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=c303271dfc8ccd2c997e5ad88e2c3fbab6b9a188331217dbd46a6d96a53852ff +dist/2025-09-16/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=0d82ddfbeaf9380773bc91f294651609bc928cf53ce95fa89fc4a0f04e54ac67 +dist/2025-09-16/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=6a8a14c5180e4cbd23bf144cf11dec5f47b769a717aeac03546d5cda83b766ab +dist/2025-09-16/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=915239fd57032010740ba9aebea3e018581993d2ae5d2ebab9ca42d73e48077d +dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=932a75ac114cb08c0eef0ef2189b463885a6d64334e25899ff386196ec59fc4e +dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=fcff7229b11ec8925d39830ce62892265f655ff272d9e3a167f102cb0cd8f098 +dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=3ae873418d17f15d46a1de95e945aa1e00d737b688e196d565031947b9dc420d +dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=036eb7c219c6e1c447c5e6ee6d4635dcbcf4c1274c5a14f0dbe5eaee3f175f1b +dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=16484c845c672c775970f0108a8c336a11d2a2e4dd5f76bb5d1b74da1e436484 +dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=9004db0b31113fa8ef0cd44318d0df62e76b8d30ac2a6332f6d483513e4ee7dd +dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=6b3c245279a006b0516df26ca686f56cdf6233740d8c8b18bc19c5aefa84dbad +dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=ecd58877e63d7d209a67ccb5a6e7693af38f2e249ce04001605ac49fcec45edf +dist/2025-09-16/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=d5370d72328bd22b2fc79661061352c99d3288604bd1ed58ae2b798586db5f70 +dist/2025-09-16/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=5b8a6f255601c33bfd215b646b8ff164390e1eb7df69aeaf7be65d97dd9f426d +dist/2025-09-16/rustc-nightly-i686-pc-windows-gnu.tar.gz=74ff396b8dfb28aa91692aced5ffa76f3e270d4c5a2e47b12eda5c4534e92bf3 +dist/2025-09-16/rustc-nightly-i686-pc-windows-gnu.tar.xz=953e50c365d084ffeac61d1279f89943e81b43922c6721f5e55f211d0fc61902 +dist/2025-09-16/rustc-nightly-i686-pc-windows-msvc.tar.gz=c3485374a990da572cab859f6cadbdf252d32e07d550e0f20fcee24be543050c +dist/2025-09-16/rustc-nightly-i686-pc-windows-msvc.tar.xz=ea4afaf63f5b5c92f7feea4b82ae751f6f8bf8f54126d7007bc72d4815352628 +dist/2025-09-16/rustc-nightly-i686-unknown-linux-gnu.tar.gz=0635e9a84313f572d1883841f91bb49c7e7fed9a6dc5db751f00e7b00f140ce2 +dist/2025-09-16/rustc-nightly-i686-unknown-linux-gnu.tar.xz=8904638b6ab37416eedacfcb03c7b87fdcbe1fcd0e3a80e0e5f410c671959d44 +dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=3fab80f207dd5e8f5c276deb11512d55b5e97b3dffe44c1f0249e0c331ce2b68 +dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=95dd7df11dd2b9f27f8fc9e88f37ada794f9e0443cf1291c554f47485eee84dd +dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=a4e7d3c55c7acce10b1f0b59afb9e70d32c651b87a4d1381e945fc07543e474d +dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=f2ca377351093829123525578451c60987c3943be1783e396ab3d5f34e7735cd +dist/2025-09-16/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=b7e5d9c1ecde1b69b17d29a3ac6d15af0e734a7666381f1adefd0bc31ea2c397 +dist/2025-09-16/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=1c18dca6d3ac47b8223ab1e67e94640f402ba8f2ab787b38ba85c49ca8143acb +dist/2025-09-16/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=0850c609f864638538f4a960ba34307045d74d10d462949e9728b2e14cf5eafc +dist/2025-09-16/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=ed4433fd1004935913d5a48faf78bcc4d2517f45e08032995414c69e4ede78ef +dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=9f54367ce8c0f2f1cbb29f84e403a8aebf9a3ec7d2675faa744e8c28b2adcd0f +dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=c1ebb094ca29b4fbb30326ae9ef99804d9f6ecee0bcb5633ce0236ea90058a13 +dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=c39f9dd4ad6f7939d114a9204a72cc22cf9a3d97ef3b496eff2bd78f55ff4e1d +dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=cf218f8da409b2cdfe3820db1ce882e5d10f8a24a77b600cee9f00e85e747d13 +dist/2025-09-16/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=b64731d52f06f0b08fcc6dd64e9319d91fde2e48fd71d2b193f32ff770b81357 +dist/2025-09-16/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=07fac144d37f87e5f7b3a9521ef79789d7ab33cd529bb27c1b95238a28602857 +dist/2025-09-16/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=727ca4ef70eb8cae09f73762202aee2b7998bc97c000571223991347a5408712 +dist/2025-09-16/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=787ae447f764bf727c7debf6543b1ec22ebcf463832eb4d1279ec0f6824c2955 +dist/2025-09-16/rustc-nightly-sparcv9-sun-solaris.tar.gz=9f80d6add413e65dc6ab0e3d44b62f24134c6b3ee0bcc604ab66b3fdcd4fa0fe +dist/2025-09-16/rustc-nightly-sparcv9-sun-solaris.tar.xz=3075e68b1f8a7b7a1c9489afdc2c9a38b1f0107865229497b2c9a7215853ef08 +dist/2025-09-16/rustc-nightly-x86_64-apple-darwin.tar.gz=685ffc70ff08c696321ebae272d09ba7f9e62f221929fa703e8cf21ba790df58 +dist/2025-09-16/rustc-nightly-x86_64-apple-darwin.tar.xz=29bd3bbb0f082cebf2dfef6d89a50dc5f33be231fe9d3e5bd2bb9b0b9bf53f90 +dist/2025-09-16/rustc-nightly-x86_64-pc-solaris.tar.gz=720e21f5ece47547b4cd91d5be89d67ce5334fa23a70921e7d60b0642b53a2d2 +dist/2025-09-16/rustc-nightly-x86_64-pc-solaris.tar.xz=d1e4d96e6f390ab2b6b1f435f5be61a69e01555f5bb66d58b2571f363cce8d26 +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=2e1a315af7faa1f67c35e927fd0f3a0c96fecf0da1a2b3269ced7846d99d9100 +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=7cb11ec687244e19a31996a3a802a8d0851cecd444abc5a8e65a10a037f67f4f +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=89010bc8a19cfafa6bc9714dd90cdcbb31cb85362846b8072cb41a3e8e7583ee +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=e84050c777e73941cb28f39e72a992b6a4a63100541f5b3fb288cf91fa1b0bd2 +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=ffd857605275d71bf7e09eff94bc5fbc9cef222b1c076f1b7aee72f980e4c470 +dist/2025-09-16/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=f28f28f07e98cb1bb43e93501cb43bd88b91b9913257f4de5c7154f0f9653d41 +dist/2025-09-16/rustc-nightly-x86_64-unknown-freebsd.tar.gz=66fe447d3f4526514b146633375efd40373dc28fe0262302be75afdf736986c2 +dist/2025-09-16/rustc-nightly-x86_64-unknown-freebsd.tar.xz=4d929f5079cd7a1c878631288765441aed538956d496881c89465aede623d94a +dist/2025-09-16/rustc-nightly-x86_64-unknown-illumos.tar.gz=feed1caa5d31ab01cd4dc3c4b6b893c5d291fca9c470430f83c2cc90d26418b5 +dist/2025-09-16/rustc-nightly-x86_64-unknown-illumos.tar.xz=2f2b4e9e9a42f255cf66565012c63b2b2c0a13309dbce94d5135cd3280a8fc67 +dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=f96e122b71d41b4889b18a0cee173822d509d8be9eaf28a3fec111df1a5dc5be +dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=ee8914df087f4e20c322851f322220e2f4885a316627c464881d086024e84b54 +dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b226b4edc64f4ed91a9cb616600ece61c12f5da72ca4b62f13669e849de7ebe5 +dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=9a3d98e0fec50ab589027f1d5973ec583b062581b1a1cd59470bb4fb6c51c463 +dist/2025-09-16/rustc-nightly-x86_64-unknown-netbsd.tar.gz=bd0d47aaa630abc01d2f36f9c2a99577254aa3428abf1d666d5520c4fa990844 +dist/2025-09-16/rustc-nightly-x86_64-unknown-netbsd.tar.xz=863edf8589fe1f3f1728222ee49960457d9edd2abc521557f28562e546e27b81 From 714fdacdf8f2c07950e3f3ddd1999cba10b828af Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 15 Sep 2025 22:01:36 -0400 Subject: [PATCH 0937/1900] Apply cfg(bootstrap) replacement --- clippy_lints/src/lib.rs | 1 - tests/missing-test-files.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..b0083b99f175f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -7,7 +7,6 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(never_type)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 63f960c92fa32..9fff3132498d8 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,6 +1,5 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; From 97f64374a9f34012c8884dbd629a896ec6ed8475 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Fri, 26 Sep 2025 16:30:08 -0700 Subject: [PATCH 0938/1900] formatting_options: fix alternate docs 0b/0o mixup The descriptions of the alternate forms of Octal and Binary were swapped in the doc comment for FormattingOptions::alternate(). --- library/core/src/fmt/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index b6de892530892..fcd2e52101ff0 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -386,8 +386,8 @@ impl FormattingOptions { /// used. The alternate forms are: /// - [`Debug`] : pretty-print the [`Debug`] formatting (adds linebreaks and indentation) /// - [`LowerHex`] as well as [`UpperHex`] - precedes the argument with a `0x` - /// - [`Octal`] - precedes the argument with a `0b` - /// - [`Binary`] - precedes the argument with a `0o` + /// - [`Octal`] - precedes the argument with a `0o` + /// - [`Binary`] - precedes the argument with a `0b` #[unstable(feature = "formatting_options", issue = "118117")] pub const fn alternate(&mut self, alternate: bool) -> &mut Self { if alternate { From bb48c162b62ce20c5f51b6a435ca2ba7bb918d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 24 Sep 2025 23:25:29 +0000 Subject: [PATCH 0939/1900] Simplify logic slightly --- compiler/rustc_parse/src/parser/expr.rs | 37 +++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2f4158450d838..8046abcd70bd0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3612,20 +3612,36 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1) } + fn is_likely_struct_lit(&self) -> bool { + // `{ ident, ` and `{ ident: ` cannot start a block. + self.look_ahead(1, |t| t.is_ident()) + && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon) + } + fn maybe_parse_struct_expr( &mut self, qself: &Option>, path: &ast::Path, ) -> Option>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); - let is_ident = self.look_ahead(1, |t| t.is_ident()); - let is_comma = self.look_ahead(2, |t| t == &token::Comma); - let is_colon = self.look_ahead(2, |t| t == &token::Colon); - match (struct_allowed, is_ident, is_comma, is_colon) { - (false, true, true, _) | (false, true, _, true) => { + match (struct_allowed, self.is_likely_struct_lit()) { + // A struct literal isn't expected and one is pretty much assured not to be present. The + // only situation that isn't detected is when a struct with a single field was attempted + // in a place where a struct literal wasn't expected, but regular parser errors apply. + // Happy path. + (false, false) => None, + (true, _) => { + // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for + // any kind of recovery. Happy path. + if let Err(err) = self.expect(exp!(OpenBrace)) { + return Some(Err(err)); + } + Some(self.parse_expr_struct(qself.clone(), path.clone(), true)) + } + (false, true) => { // We have something like `match foo { bar,` or `match foo { bar:`, which means the // user might have meant to write a struct literal as part of the `match` - // discriminant. + // discriminant. This is done purely for error recovery. let snapshot = self.create_snapshot_for_diagnostic(); if let Err(err) = self.expect(exp!(OpenBrace)) { return Some(Err(err)); @@ -3651,15 +3667,6 @@ impl<'a> Parser<'a> { } } } - (true, _, _, _) => { - // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for - // any kind of recovery. - if let Err(err) = self.expect(exp!(OpenBrace)) { - return Some(Err(err)); - } - Some(self.parse_expr_struct(qself.clone(), path.clone(), true)) - } - (false, _, _, _) => None, } } From fe3e124e8a4e5ba3dd92b99359bd6f4905eded19 Mon Sep 17 00:00:00 2001 From: "U. Lasiotus" Date: Fri, 26 Sep 2025 17:05:30 -0700 Subject: [PATCH 0940/1900] doc: fix a typo in platform-support.md --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 619eebd15bd37..67882bb38132a 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -121,6 +121,7 @@ - [\*-unknown-hermit](platform-support/hermit.md) - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-managarm-mlibc](platform-support/managarm.md) + - [\*-unknown-motor](platform-support/motor.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - [\*-unknown-openbsd](platform-support/openbsd.md) - [\*-unknown-redox](platform-support/redox.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 99c8e365f5cf5..b29d01a138733 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -431,7 +431,7 @@ target | std | host | notes `x86_64-unknown-l4re-uclibc` | ? | | [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`x86_64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | x86_64 Managarm -[`x86_64-unknown-motor`[(platform-support/motor.md) | ? | | x86_64 Motor OS +[`x86_64-unknown-motor`](platform-support/motor.md) | ? | | x86_64 Motor OS [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD [`x86_64-unknown-trusty`](platform-support/trusty.md) | ✓ | | `x86_64-uwp-windows-gnu` | ✓ | | From d8a1edc718537fc595dc731fcb89f17378de6af2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 27 Sep 2025 18:23:18 +1000 Subject: [PATCH 0941/1900] Use `PanicHookInfo::payload_as_str` now that it's stable in beta --- src/tools/compiletest/src/panic_hook.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/tools/compiletest/src/panic_hook.rs b/src/tools/compiletest/src/panic_hook.rs index 1661ca6dabe8a..4f1e2547518f0 100644 --- a/src/tools/compiletest/src/panic_hook.rs +++ b/src/tools/compiletest/src/panic_hook.rs @@ -42,7 +42,7 @@ fn custom_panic_hook(default_hook: &PanicHook, info: &panic::PanicHookInfo<'_>) let thread = thread::current().name().unwrap_or("(test runner)").to_owned(); let location = get_location(info); - let payload = payload_as_str(info).unwrap_or("Box"); + let payload = info.payload_as_str().unwrap_or("Box"); let backtrace = Backtrace::capture(); writeln!(out, "\nthread '{thread}' panicked at {location}:\n{payload}").unwrap(); @@ -72,19 +72,6 @@ fn get_location<'a>(info: &'a PanicHookInfo<'_>) -> &'a dyn Display { } } -/// FIXME(Zalathar): Replace with `PanicHookInfo::payload_as_str` when that's -/// stable in beta. -fn payload_as_str<'a>(info: &'a PanicHookInfo<'_>) -> Option<&'a str> { - let payload = info.payload(); - if let Some(s) = payload.downcast_ref::<&str>() { - Some(s) - } else if let Some(s) = payload.downcast_ref::() { - Some(s) - } else { - None - } -} - fn rust_backtrace_full() -> bool { static RUST_BACKTRACE_FULL: LazyLock = LazyLock::new(|| matches!(env::var("RUST_BACKTRACE").as_deref(), Ok("full"))); From 95c146a0c1ff987c8263a2ddb7dced4e5b545842 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 26 Sep 2025 15:55:42 +0200 Subject: [PATCH 0942/1900] Fix tracking issue number for feature(macro_attr) The ability to define an attribute macro with `macro_rules!` is tracked at https://github.com/rust-lang/rust/issues/143547, not https://github.com/rust-lang/rust/issues/83527 --- compiler/rustc_feature/src/unstable.rs | 2 +- tests/ui/feature-gates/feature-gate-macro-attr.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 6ef0df72365ec..158cf8d2db32f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -554,7 +554,7 @@ declare_features! ( /// Allows fused `loop`/`match` for direct intraprocedural jumps. (incomplete, loop_match, "1.90.0", Some(132306)), /// Allow `macro_rules!` attribute rules - (unstable, macro_attr, "1.91.0", Some(83527)), + (unstable, macro_attr, "1.91.0", Some(143547)), /// Allow `macro_rules!` derive rules (unstable, macro_derive, "1.91.0", Some(143549)), /// Give access to additional metadata about declarative macro meta-variables. diff --git a/tests/ui/feature-gates/feature-gate-macro-attr.stderr b/tests/ui/feature-gates/feature-gate-macro-attr.stderr index b58418527c5b0..75bc93e80271a 100644 --- a/tests/ui/feature-gates/feature-gate-macro-attr.stderr +++ b/tests/ui/feature-gates/feature-gate-macro-attr.stderr @@ -4,7 +4,7 @@ error[E0658]: `macro_rules!` attributes are unstable LL | macro_rules! myattr { attr() {} => {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #83527 for more information + = note: see issue #143547 for more information = help: add `#![feature(macro_attr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date From aa6332efc2c6f30360419dc40351dc2cab245729 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Sep 2025 10:58:19 +0200 Subject: [PATCH 0943/1900] silence unused import error --- src/tools/miri/src/bin/miri.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 8b15a7863476e..40ac5c3ff385c 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -352,6 +352,7 @@ fn fatal_error_(msg: &impl std::fmt::Display) -> ! { macro_rules! fatal_error { ($($tt:tt)*) => { $crate::fatal_error_(&format_args!($($tt)*)) }; } +#[allow(unused)] // use depends on cfg use fatal_error; /// Execute a compiler with the given CLI arguments and callbacks. From 7c00bccd3b3eb6717e3c801123107962e671e48f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Mar 2025 00:53:59 +0100 Subject: [PATCH 0944/1900] Implement RFC 3631 --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 - compiler/rustc_passes/messages.ftl | 9 + compiler/rustc_passes/src/check_attr.rs | 53 ++- compiler/rustc_passes/src/errors.rs | 14 +- compiler/rustc_span/src/symbol.rs | 4 + library/alloc/src/lib.rs | 29 +- library/core/src/lib.rs | 69 ++-- library/std/src/lib.rs | 16 +- src/librustdoc/clean/cfg.rs | 30 ++ src/librustdoc/clean/inline.rs | 15 +- src/librustdoc/clean/mod.rs | 10 +- src/librustdoc/clean/types.rs | 302 ++++++++++++++---- src/librustdoc/doctest/rust.rs | 11 +- src/librustdoc/formats/cache.rs | 2 - src/librustdoc/passes/propagate_doc_cfg.rs | 134 +++++--- src/librustdoc/visit_ast.rs | 27 -- 16 files changed, 518 insertions(+), 209 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9ab5b0b354726..fe9cc8e61ed5b 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -182,8 +182,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_doc!( "experimental" { - cfg => doc_cfg - cfg_hide => doc_cfg_hide masked => doc_masked notable_trait => doc_notable_trait } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 870e0a90b5421..19014f37c6628 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -143,6 +143,15 @@ passes_doc_attribute_not_attribute = passes_doc_cfg_hide_takes_list = `#[doc(cfg_hide(...))]` takes a list of attributes +passes_doc_auto_cfg_expects_hide_or_show = + `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"` + +passes_doc_auto_cfg_hide_show_expects_list = + `#![doc(auto_cfg({$attr_name}(...)))]` only expects a list of items + +passes_doc_auto_cfg_wrong_literal = + `expected boolean for #[doc(auto_cfg = ...)]` + passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 007353f136d56..3dc232a35ec3a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1160,16 +1160,43 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. - /// - fn check_doc_cfg_hide(&self, meta: &MetaItemInner, hir_id: HirId) { - if meta.meta_item_list().is_none() { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - errors::DocCfgHideTakesList, - ); + /// Check that the `#![doc(auto_cfg(..))]` attribute has expected input. + fn check_doc_auto_cfg(&self, meta: &MetaItemInner, hir_id: HirId) { + let MetaItemInner::MetaItem(meta) = meta else { + unreachable!(); + }; + match &meta.kind { + MetaItemKind::Word => {} + MetaItemKind::NameValue(lit) => { + if !matches!(lit.kind, LitKind::Bool(_)) { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgWrongLiteral, + ); + } + } + MetaItemKind::List(list) => { + for item in list { + let Some(attr_name) = item.name() else { continue }; + if attr_name != sym::hide && attr_name != sym::show { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgExpectsHideOrShow, + ); + } else if item.meta_item_list().is_none() { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgHideShowExpectsList { attr_name: attr_name.as_str() }, + ); + } + } + } } } @@ -1245,10 +1272,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_attr_crate_level(attr, style, meta, hir_id); } - Some(sym::cfg_hide) => { - if self.check_attr_crate_level(attr, style, meta, hir_id) { - self.check_doc_cfg_hide(meta, hir_id); - } + Some(sym::auto_cfg) => { + self.check_doc_auto_cfg(meta, hir_id); } Some(sym::inline | sym::no_inline) => { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index cfd6b9e6dffaf..6cc0bd6ce48bd 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -309,8 +309,18 @@ pub(crate) struct DocTestLiteral; pub(crate) struct DocTestTakesList; #[derive(LintDiagnostic)] -#[diag(passes_doc_cfg_hide_takes_list)] -pub(crate) struct DocCfgHideTakesList; +#[diag(passes_doc_auto_cfg_wrong_literal)] +pub(crate) struct DocAutoCfgWrongLiteral; + +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_expects_hide_or_show)] +pub(crate) struct DocAutoCfgExpectsHideOrShow; + +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_hide_show_expects_list)] +pub(crate) struct DocAutoCfgHideShowExpectsList<'a> { + pub attr_name: &'a str, +} #[derive(LintDiagnostic)] #[diag(passes_doc_test_unknown_any)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index faf32523baae7..e80a98994dbba 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -545,6 +545,7 @@ symbols! { attributes, audit_that, augmented_assignments, + auto_cfg, auto_traits, autodiff, autodiff_forward, @@ -1149,6 +1150,8 @@ symbols! { hashset_iter_ty, hexagon_target_feature, hidden, + hidden_cfg, + hide, hint, homogeneous_aggregate, host, @@ -1987,6 +1990,7 @@ symbols! { shl_assign, shorter_tail_lifetimes, should_panic, + show, shr, shr_assign, sig_dfl, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index cba1ce40f75d5..243fdc2e8436f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -64,14 +64,27 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))) )] -#![doc(cfg_hide( - not(test), - no_global_oom_handling, - not(no_global_oom_handling), - not(no_rc), - not(no_sync), - target_has_atomic = "ptr" -))] +#![cfg_attr( + bootstrap, + doc(cfg_hide( + not(test), + no_global_oom_handling, + not(no_global_oom_handling), + not(no_rc), + not(no_sync), + target_has_atomic = "ptr" + )) +)] +#![cfg_attr( + not(bootstrap), + doc(auto_cfg(hide( + test, + no_global_oom_handling, + no_rc, + no_sync, + target_has_atomic = "ptr" + ))) +)] #![doc(rust_logo)] #![feature(rustdoc_internals)] #![no_std] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 5d52bfb1b125f..32a3d6c7042aa 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -51,27 +51,54 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![doc(rust_logo)] -#![doc(cfg_hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_equal_alignment = "8", - target_has_atomic_equal_alignment = "16", - target_has_atomic_equal_alignment = "32", - target_has_atomic_equal_alignment = "64", - target_has_atomic_equal_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", -))] +#![cfg_attr( + bootstrap, + doc(cfg_hide( + no_fp_fmt_parse, + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64", + target_has_atomic = "8", + target_has_atomic = "16", + target_has_atomic = "32", + target_has_atomic = "64", + target_has_atomic = "ptr", + target_has_atomic_equal_alignment = "8", + target_has_atomic_equal_alignment = "16", + target_has_atomic_equal_alignment = "32", + target_has_atomic_equal_alignment = "64", + target_has_atomic_equal_alignment = "ptr", + target_has_atomic_load_store = "8", + target_has_atomic_load_store = "16", + target_has_atomic_load_store = "32", + target_has_atomic_load_store = "64", + target_has_atomic_load_store = "ptr", + )) +)] +#![cfg_attr( + not(bootstrap), + doc(auto_cfg(hide( + no_fp_fmt_parse, + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64", + target_has_atomic = "8", + target_has_atomic = "16", + target_has_atomic = "32", + target_has_atomic = "64", + target_has_atomic = "ptr", + target_has_atomic_equal_alignment = "8", + target_has_atomic_equal_alignment = "16", + target_has_atomic_equal_alignment = "32", + target_has_atomic_equal_alignment = "64", + target_has_atomic_equal_alignment = "ptr", + target_has_atomic_load_store = "8", + target_has_atomic_load_store = "16", + target_has_atomic_load_store = "32", + target_has_atomic_load_store = "64", + target_has_atomic_load_store = "ptr", + ))) +)] #![no_core] #![rustc_coherence_is_core] #![rustc_preserve_ub_checks] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 233e41aa34535..93c91b615258d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -235,7 +235,21 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![doc(rust_logo)] -#![doc(cfg_hide(not(test), no_global_oom_handling, not(no_global_oom_handling)))] +#![cfg_attr( + bootstrap, + doc(cfg_hide( + not(test), + no_global_oom_handling, + not(no_global_oom_handling) + )) +)] +#![cfg_attr( + not(bootstrap), + doc(auto_cfg(hide( + test, + no_global_oom_handling, + ))) +)] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 8feca1367fc9e..aa614b73dea72 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -256,6 +256,36 @@ impl Cfg { fn omit_preposition(&self) -> bool { matches!(self, Cfg::True | Cfg::False) } + + pub(crate) fn strip_hidden(&self, hidden: &FxHashSet) -> Option { + match self { + Self::True | Self::False => Some(self.clone()), + Self::Cfg(..) => { + if !hidden.contains(self) { + Some(self.clone()) + } else { + None + } + } + Self::Not(cfg) => { + if let Some(cfg) = cfg.strip_hidden(hidden) { + Some(Self::Not(Box::new(cfg))) + } else { + None + } + } + Self::Any(cfgs) => { + let cfgs = + cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::>(); + if cfgs.is_empty() { None } else { Some(Self::Any(cfgs)) } + } + Self::All(cfgs) => { + let cfgs = + cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::>(); + if cfgs.is_empty() { None } else { Some(Self::All(cfgs)) } + } + } + } } impl ops::Not for Cfg { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 8461e15c6c3f5..8ffa6033c9b0d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -19,10 +19,10 @@ use tracing::{debug, trace}; use super::{Item, extract_cfg_from_attrs}; use crate::clean::{ - self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item, - clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig, - clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics, - clean_variant_def, utils, + self, Attributes, CfgInfo, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, + clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, + clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, + clean_ty_generics, clean_variant_def, utils, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -409,6 +409,7 @@ pub(crate) fn merge_attrs( cx: &mut DocContext<'_>, old_attrs: &[hir::Attribute], new_attrs: Option<(&[hir::Attribute], Option)>, + cfg_info: &mut CfgInfo, ) -> (clean::Attributes, Option>) { // NOTE: If we have additional attributes (from a re-export), // always insert them first. This ensure that re-export @@ -423,12 +424,12 @@ pub(crate) fn merge_attrs( } else { Attributes::from_hir(&both) }, - extract_cfg_from_attrs(both.iter(), cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(both.iter(), cx.tcx, cfg_info), ) } else { ( Attributes::from_hir(old_attrs), - extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, cfg_info), ) } } @@ -604,7 +605,7 @@ pub(crate) fn build_impl( }); } - let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); + let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs, &mut CfgInfo::default()); trace!("merged_attrs={merged_attrs:?}"); trace!( diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0afb969d5c84f..c6339dd475593 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -212,18 +212,10 @@ fn generate_item_with_correct_attrs( // We only keep the item's attributes. target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() }; - let cfg = extract_cfg_from_attrs( - attrs.iter().map(move |(attr, _)| match attr { - Cow::Borrowed(attr) => *attr, - Cow::Owned(attr) => attr, - }), - cx.tcx, - &cx.cache.hidden_cfg, - ); let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let name = renamed.or(Some(name)); - let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg); + let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, None); // FIXME (GuillaumeGomez): Should we also make `inline_stmt_id` a `Vec` instead of an `Option`? item.inner.inline_stmt_id = import_ids.first().copied(); item diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ff513c71035d7..63e8357801c05 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -6,7 +6,8 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use itertools::Either; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_ast::ast::{LitKind, MetaItemInner, MetaItemKind}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -467,7 +468,7 @@ impl Item { name, kind, Attributes::from_hir(hir_attrs), - extract_cfg_from_attrs(hir_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), + None, ) } @@ -902,30 +903,6 @@ impl ItemKind { | AttributeItem => [].iter(), } } - - /// Returns `true` if this item does not appear inside an impl block. - pub(crate) fn is_non_assoc(&self) -> bool { - matches!( - self, - StructItem(_) - | UnionItem(_) - | EnumItem(_) - | TraitItem(_) - | ModuleItem(_) - | ExternCrateItem { .. } - | FunctionItem(_) - | TypeAliasItem(_) - | StaticItem(_) - | ConstantItem(_) - | TraitAliasItem(_) - | ForeignFunctionItem(_, _) - | ForeignStaticItem(_, _) - | ForeignTypeItem - | MacroItem(_) - | ProcMacroItem(_) - | PrimitiveItem(_) - ) - } } #[derive(Clone, Debug)] @@ -945,14 +922,85 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( .flatten() } +#[derive(Clone, Debug)] +pub(crate) struct CfgInfo { + hidden_cfg: FxHashSet, + current_cfg: Cfg, + doc_auto_cfg_active: bool, + parent_is_doc_cfg: bool, +} + +impl Default for CfgInfo { + fn default() -> Self { + Self { + hidden_cfg: [ + Cfg::Cfg(sym::test, None), + Cfg::Cfg(sym::doc, None), + Cfg::Cfg(sym::doctest, None), + ] + .into_iter() + .collect(), + current_cfg: Cfg::True, + doc_auto_cfg_active: true, + parent_is_doc_cfg: false, + } + } +} + +fn show_hide_show_conflict_error( + tcx: TyCtxt<'_>, + item_span: rustc_span::Span, + previous: rustc_span::Span, +) { + let mut diag = tcx.sess.dcx().struct_span_err( + item_span, + format!( + "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" + ), + ); + diag.span_note(previous, "first change was here"); + diag.emit(); +} + +fn handle_auto_cfg_hide_show( + tcx: TyCtxt<'_>, + cfg_info: &mut CfgInfo, + sub_attr: &MetaItemInner, + is_show: bool, + new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, + new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, +) { + if let MetaItemInner::MetaItem(item) = sub_attr + && let MetaItemKind::List(items) = &item.kind + { + for item in items { + // Cfg parsing errors should already have been reported in `rustc_passes::check_attr`. + if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) { + if is_show { + if let Some(span) = new_hide_attrs.get(&(key, value)) { + show_hide_show_conflict_error(tcx, item.span(), *span); + } else { + new_show_attrs.insert((key, value), item.span()); + } + cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value)); + } else { + if let Some(span) = new_show_attrs.get(&(key, value)) { + show_hide_show_conflict_error(tcx, item.span(), *span); + } else { + new_hide_attrs.insert((key, value), item.span()); + } + cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value)); + } + } + } + } +} + pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator + Clone>( attrs: I, tcx: TyCtxt<'_>, - hidden_cfg: &FxHashSet, + cfg_info: &mut CfgInfo, ) -> Option> { - let doc_cfg_active = tcx.features().doc_cfg(); - let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); - fn single(it: T) -> Option { let mut iter = it.into_iter(); let item = iter.next()?; @@ -962,56 +1010,168 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator Some(item) } - let mut cfg = if doc_cfg_active || doc_auto_cfg_active { - let mut doc_cfg = attrs - .clone() - .filter(|attr| attr.has_name(sym::doc)) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) - .filter(|attr| attr.has_name(sym::cfg)) - .peekable(); - if doc_cfg.peek().is_some() && doc_cfg_active { - let sess = tcx.sess; - - doc_cfg.fold(Cfg::True, |mut cfg, item| { - if let Some(cfg_mi) = - item.meta_item().and_then(|item| rustc_expand::config::parse_cfg(item, sess)) + let mut new_show_attrs = FxHashMap::default(); + let mut new_hide_attrs = FxHashMap::default(); + + let mut doc_cfg = attrs + .clone() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)) + .peekable(); + // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes. + if doc_cfg.peek().is_some() { + let sess = tcx.sess; + // We overwrite existing `cfg`. + if !cfg_info.parent_is_doc_cfg { + cfg_info.current_cfg = Cfg::True; + cfg_info.parent_is_doc_cfg = true; + } + for attr in doc_cfg { + if let Some(cfg_mi) = + attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess)) + { + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg_info.current_cfg &= new_cfg, + Err(e) => { + sess.dcx().span_err(e.span, e.msg); + } + } + } + } + } else { + cfg_info.parent_is_doc_cfg = false; + } + + let mut changed_auto_active_status = None; + + // First we get all `doc(auto_cfg)` attributes. + for attr in attrs.clone() { + if let Some(ident) = attr.ident() + && ident.name == sym::doc + && let Some(attrs) = attr.meta_item_list() + { + for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) { + let MetaItemInner::MetaItem(attr) = attr else { + continue; + }; + match &attr.kind { + MetaItemKind::Word => { + if let Some(first_change) = changed_auto_active_status { + if !cfg_info.doc_auto_cfg_active { + tcx.sess.dcx().struct_span_err( + vec![first_change, attr.span], + "`auto_cfg` was disabled and enabled more than once on the same item", + ).emit(); + return None; + } + } else { + changed_auto_active_status = Some(attr.span); + } + cfg_info.doc_auto_cfg_active = true; + } + MetaItemKind::NameValue(lit) => { + if let LitKind::Bool(value) = lit.kind { + if let Some(first_change) = changed_auto_active_status { + if cfg_info.doc_auto_cfg_active != value { + tcx.sess.dcx().struct_span_err( + vec![first_change, attr.span], + "`auto_cfg` was disabled and enabled more than once on the same item", + ).emit(); + return None; + } + } else { + changed_auto_active_status = Some(attr.span); + } + cfg_info.doc_auto_cfg_active = value; + } + } + MetaItemKind::List(sub_attrs) => { + if let Some(first_change) = changed_auto_active_status { + if !cfg_info.doc_auto_cfg_active { + tcx.sess.dcx().struct_span_err( + vec![first_change, attr.span], + "`auto_cfg` was disabled and enabled more than once on the same item", + ).emit(); + return None; + } + } else { + changed_auto_active_status = Some(attr.span); + } + // Whatever happens next, the feature is enabled again. + cfg_info.doc_auto_cfg_active = true; + for sub_attr in sub_attrs.iter() { + if let Some(ident) = sub_attr.ident() + && (ident.name == sym::show || ident.name == sym::hide) + { + handle_auto_cfg_hide_show( + tcx, + cfg_info, + &sub_attr, + ident.name == sym::show, + &mut new_show_attrs, + &mut new_hide_attrs, + ); + } + } + } + } + } + } + } + + // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because + // `doc(cfg())` overrides `cfg()`). + for attr in attrs { + let Some(ident) = attr.ident() else { continue }; + match ident.name { + sym::cfg | sym::cfg_trace if !cfg_info.parent_is_doc_cfg => { + if let Some(attr) = single(attr.meta_item_list()?) + && let Ok(new_cfg) = Cfg::parse(&attr) { - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => { - sess.dcx().span_err(e.span, e.msg); + cfg_info.current_cfg &= new_cfg; + } + } + // treat #[target_feature(enable = "feat")] attributes as if they were + // #[doc(cfg(target_feature = "feat"))] attributes as well + sym::target_feature + if let Some(attrs) = attr.meta_item_list() => + { + for attr in attrs { + if attr.has_name(sym::enable) && attr.value_str().is_some() { + // Clone `enable = "feat"`, change to `target_feature = "feat"`. + // Unwrap is safe because `value_str` succeeded above. + let mut meta = attr.meta_item().unwrap().clone(); + meta.path = + ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); + + if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { + cfg_info.current_cfg &= feat_cfg; } } } - cfg - }) - } else if doc_auto_cfg_active { - // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because - // `doc(cfg())` overrides `cfg()`). - attrs - .clone() - .filter(|attr| attr.has_name(sym::cfg_trace)) - .filter_map(|attr| single(attr.meta_item_list()?)) - .filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + } + _ => {} + } + } + + // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing + // to be done here. + if !cfg_info.doc_auto_cfg_active && !cfg_info.parent_is_doc_cfg { + None + } else if cfg_info.parent_is_doc_cfg { + if cfg_info.current_cfg == Cfg::True { + None } else { - Cfg::True + Some(Arc::new(cfg_info.current_cfg.clone())) } } else { - Cfg::True - }; - - // treat #[target_feature(enable = "feat")] attributes as if they were - // #[doc(cfg(target_feature = "feat"))] attributes as well - if let Some(features) = - find_attr!(attrs, AttributeKind::TargetFeature { features, .. } => features) - { - for (feature, _) in features { - cfg &= Cfg::Cfg(sym::target_feature, Some(*feature)); + // Since we always want to collect all `cfg` items, we remove the hidden ones afterward. + match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) { + None | Some(Cfg::True) => None, + Some(cfg) => Some(Arc::new(cfg)), } } - - if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } } pub(crate) trait NestedAttributesExt { diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index f5ec828187a50..b9367b880057f 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -5,7 +5,6 @@ use std::env; use std::sync::Arc; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; use rustc_middle::hir::nested_filter; @@ -15,7 +14,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span, sym}; use super::{DocTestVisitor, ScrapedDocTest}; -use crate::clean::{Attributes, extract_cfg_from_attrs}; +use crate::clean::{Attributes, CfgInfo, extract_cfg_from_attrs}; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { @@ -120,9 +119,11 @@ impl HirCollector<'_> { nested: F, ) { let ast_attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = - extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &FxHashSet::default()) - && !cfg.matches(&self.tcx.sess.psess) + if let Some(ref cfg) = extract_cfg_from_attrs( + ast_attrs.iter(), + self.tcx, + &mut CfgInfo::default(), + ) && !cfg.matches(&self.tcx.sess.psess) { return; } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 29b4c4caaf86d..a19d254ea95f8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -125,8 +125,6 @@ pub(crate) struct Cache { /// /// Links are indexed by the DefId of the item they document. pub(crate) intra_doc_links: FxHashMap>, - /// Cfg that have been hidden via #![doc(cfg_hide(...))] - pub(crate) hidden_cfg: FxHashSet, /// Contains the list of `DefId`s which have been inlined. It is used when generating files /// to check if a stripped item should get its file generated or not: if it's inside a diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index eddafa9ba8e49..957a1f56c7189 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -2,11 +2,15 @@ use std::sync::Arc; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_hir::def_id::LocalDefId; +use rustc_hir::{AttrArgs, Attribute}; +use rustc_span::symbol::sym; use crate::clean::cfg::Cfg; use crate::clean::inline::{load_attrs, merge_attrs}; -use crate::clean::{Crate, Item, ItemKind}; +use crate::clean::{CfgInfo, Crate, Item, ItemKind}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::passes::Pass; @@ -18,70 +22,119 @@ pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { }; pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { - CfgPropagator { parent_cfg: None, parent: None, cx }.fold_crate(cr) + CfgPropagator { parent_cfg: None, parent: None, cx, cfg_info: CfgInfo::default() } + .fold_crate(cr) } struct CfgPropagator<'a, 'tcx> { parent_cfg: Option>, parent: Option, cx: &'a mut DocContext<'tcx>, + cfg_info: CfgInfo, } -impl CfgPropagator<'_, '_> { - // Some items need to merge their attributes with their parents' otherwise a few of them - // (mostly `cfg` ones) will be missing. - fn merge_with_parent_attributes(&mut self, item: &mut Item) { - let check_parent = match &item.kind { - // impl blocks can be in different modules with different cfg and we need to get them - // as well. - ItemKind::ImplItem(_) => false, - kind if kind.is_non_assoc() => true, - _ => return, - }; +fn should_retain(token: &TokenTree) -> bool { + // We only keep `doc(cfg)` items. + matches!( + token, + TokenTree::Token( + Token { + kind: TokenKind::Ident( + ident, + _, + ), + .. + }, + _, + ) if *ident == sym::cfg, + ) +} - let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) else { - return; - }; +fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec { + let mut tokens = Vec::with_capacity(args_tokens.len()); + let mut skip_next_delimited = false; + for token in args_tokens.iter() { + match token { + TokenTree::Delimited(..) => { + if !skip_next_delimited { + tokens.push(token.clone()); + } + skip_next_delimited = false; + } + token if should_retain(token) => { + skip_next_delimited = false; + tokens.push(token.clone()); + } + _ => { + skip_next_delimited = true; + } + } + } + tokens +} - if check_parent { - let expected_parent = self.cx.tcx.opt_local_parent(def_id); - // If parents are different, it means that `item` is a reexport and we need - // to compute the actual `cfg` by iterating through its "real" parents. - if self.parent.is_some() && self.parent == expected_parent { - return; +// We only care about `#[cfg()]` and `#[doc(cfg())]`, we discard everything else. +fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) { + for attr in new_attrs { + if attr.is_doc_comment() { + continue; + } + let mut attr = attr.clone(); + if let Attribute::Unparsed(ref mut normal) = attr + && let [ident] = &*normal.path.segments + { + let ident = ident.name; + if ident == sym::doc + && let AttrArgs::Delimited(args) = &mut normal.args + { + let tokens = filter_tokens_from_list(&args.tokens); + args.tokens = TokenStream::new(tokens); + attrs.push(attr); + } else if ident == sym::cfg_trace { + // If it's a `cfg()` attribute, we keep it. + attrs.push(attr); } } + } +} +impl CfgPropagator<'_, '_> { + // Some items need to merge their attributes with their parents' otherwise a few of them + // (mostly `cfg` ones) will be missing. + fn merge_with_parent_attributes(&mut self, item: &mut Item) { let mut attrs = Vec::new(); - let mut next_def_id = def_id; - while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { - attrs.extend_from_slice(load_attrs(self.cx, parent_def_id.to_def_id())); - next_def_id = parent_def_id; + // We only need to merge an item attributes with its parent's in case it's an impl as an + // impl might not be defined in the same module as the item it implements. + // + // Otherwise, `cfg_info` already tracks everything we need so nothing else to do! + if matches!(item.kind, ItemKind::ImplItem(_)) + && let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) + { + let mut next_def_id = def_id; + while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { + let x = load_attrs(self.cx, parent_def_id.to_def_id()); + add_only_cfg_attributes(&mut attrs, x); + next_def_id = parent_def_id; + } } - let (_, cfg) = - merge_attrs(self.cx, item.attrs.other_attrs.as_slice(), Some((&attrs, None))); + let (_, cfg) = merge_attrs( + self.cx, + item.attrs.other_attrs.as_slice(), + Some((&attrs, None)), + &mut self.cfg_info, + ); item.inner.cfg = cfg; } } impl DocFolder for CfgPropagator<'_, '_> { fn fold_item(&mut self, mut item: Item) -> Option { + let old_cfg_info = self.cfg_info.clone(); let old_parent_cfg = self.parent_cfg.clone(); self.merge_with_parent_attributes(&mut item); - - let new_cfg = match (self.parent_cfg.take(), item.inner.cfg.take()) { - (None, None) => None, - (Some(rc), None) | (None, Some(rc)) => Some(rc), - (Some(mut a), Some(b)) => { - let b = Arc::try_unwrap(b).unwrap_or_else(|rc| Cfg::clone(&rc)); - *Arc::make_mut(&mut a) &= b; - Some(a) - } - }; - self.parent_cfg = new_cfg.clone(); - item.inner.cfg = new_cfg; + self.parent_cfg = item.inner.cfg.clone(); let old_parent = if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { @@ -90,6 +143,7 @@ impl DocFolder for CfgPropagator<'_, '_> { self.parent.take() }; let result = self.fold_item_recur(item); + self.cfg_info = old_cfg_info; self.parent_cfg = old_parent_cfg; self.parent = old_parent; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index cd28322f5901c..ac67871b0cb73 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -17,7 +17,6 @@ use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::debug; -use crate::clean::cfg::Cfg; use crate::clean::utils::{inherits_doc_hidden, should_ignore_res}; use crate::clean::{NestedAttributesExt, hir_attr_lists, reexport_chain}; use crate::core; @@ -178,32 +177,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } - self.cx.cache.hidden_cfg = self - .cx - .tcx - .hir_attrs(CRATE_HIR_ID) - .iter() - .filter(|attr| attr.has_name(sym::doc)) - .flat_map(|attr| attr.meta_item_list().into_iter().flatten()) - .filter(|attr| attr.has_name(sym::cfg_hide)) - .flat_map(|attr| { - attr.meta_item_list() - .unwrap_or(&[]) - .iter() - .filter_map(|attr| { - Cfg::parse(attr) - .map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg)) - .ok() - }) - .collect::>() - }) - .chain([ - Cfg::Cfg(sym::test, None), - Cfg::Cfg(sym::doc, None), - Cfg::Cfg(sym::doctest, None), - ]) - .collect(); - self.cx.cache.exact_paths = self.exact_paths; top_level_module } From 6537278e114a98ea6f2c80d7d7ffacb2868be770 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Mar 2025 00:54:22 +0100 Subject: [PATCH 0945/1900] Update rustdoc tests --- tests/rustdoc-ui/cfg-hide-show-conflict.rs | 2 ++ .../rustdoc-ui/cfg-hide-show-conflict.stderr | 14 +++++++++ tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs | 5 ++-- .../feature-gate-doc_cfg_hide.stderr | 11 +++---- tests/rustdoc-ui/invalid-cfg.rs | 2 +- tests/rustdoc-ui/lints/doc_cfg_hide.rs | 9 ++---- tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 29 ++++++------------- tests/rustdoc/doc-cfg/doc-cfg-hide.rs | 8 ++--- .../rustdoc/doc-cfg/doc-cfg-implicit-gate.rs | 2 +- tests/rustdoc/doc_auto_cfg_reexports.rs | 21 ++++++++++++++ 10 files changed, 61 insertions(+), 42 deletions(-) create mode 100644 tests/rustdoc-ui/cfg-hide-show-conflict.rs create mode 100644 tests/rustdoc-ui/cfg-hide-show-conflict.stderr create mode 100644 tests/rustdoc/doc_auto_cfg_reexports.rs diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs new file mode 100644 index 0000000000000..a8a50fe15c7e1 --- /dev/null +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.rs @@ -0,0 +1,2 @@ +#![doc(auto_cfg(hide(target_os = "linux")))] +#![doc(auto_cfg(show(windows, target_os = "linux")))] //~ ERROR diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr new file mode 100644 index 0000000000000..d2d0564606a54 --- /dev/null +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr @@ -0,0 +1,14 @@ +error: same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item + --> $DIR/cfg-hide-show-conflict.rs:2:31 + | +LL | #![doc(auto_cfg(show(windows, target_os = "linux")))] + | ^^^^^^^^^^^^^^^^^^^ + | +note: first change was here + --> $DIR/cfg-hide-show-conflict.rs:1:22 + | +LL | #![doc(auto_cfg(hide(target_os = "linux")))] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs index 17812018b9b7a..e49285a01b812 100644 --- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs +++ b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs @@ -1,5 +1,6 @@ -#![doc(cfg_hide(test))] -//~^ ERROR `#[doc(cfg_hide)]` is experimental +// FIXME: Remove this file once feature is removed + +#![doc(cfg_hide(test))] //~ ERROR #[cfg(not(test))] pub fn public_fn() {} diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr index 55135986ffe76..b3eee2af7f733 100644 --- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr @@ -1,13 +1,10 @@ -error[E0658]: `#[doc(cfg_hide)]` is experimental - --> $DIR/feature-gate-doc_cfg_hide.rs:1:1 +error: unknown `doc` attribute `cfg_hide` + --> $DIR/feature-gate-doc_cfg_hide.rs:3:8 | LL | #![doc(cfg_hide(test))] - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | - = note: see issue #43781 for more information - = help: add `#![feature(doc_cfg_hide)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[deny(invalid_doc_attributes)]` on by default error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/rustdoc-ui/invalid-cfg.rs b/tests/rustdoc-ui/invalid-cfg.rs index d237b8605c068..aff36286c535c 100644 --- a/tests/rustdoc-ui/invalid-cfg.rs +++ b/tests/rustdoc-ui/invalid-cfg.rs @@ -1,4 +1,4 @@ #![feature(doc_cfg)] #[doc(cfg = "x")] //~ ERROR not followed by parentheses #[doc(cfg(x, y))] //~ ERROR multiple `cfg` predicates -struct S {} +pub struct S {} diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 9a8bce2a92aa5..abf5631847906 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,7 +1,2 @@ -#![feature(doc_cfg_hide)] - -#![doc(cfg_hide = "test")] //~ ERROR -#![doc(cfg_hide)] //~ ERROR - -#[doc(cfg_hide(doc))] //~ ERROR -pub fn foo() {} +#![doc(auto_cfg(hide = "test"))] //~ ERROR +#![doc(auto_cfg(hide))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index 0c9d0879b0ac7..bb0e52eb666ad 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,27 +1,16 @@ -error: this attribute can only be applied at the crate level - --> $DIR/doc_cfg_hide.rs:6:7 +error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items + --> $DIR/doc_cfg_hide.rs:1:8 | -LL | #[doc(cfg_hide(doc))] - | ^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(hide = "test"))] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: read for more information = note: `#[deny(invalid_doc_attributes)]` on by default -help: to apply to the crate, use an inner attribute - | -LL | #![doc(cfg_hide(doc))] - | + - -error: `#[doc(cfg_hide(...))]` takes a list of attributes - --> $DIR/doc_cfg_hide.rs:3:8 - | -LL | #![doc(cfg_hide = "test")] - | ^^^^^^^^^^^^^^^^^ -error: `#[doc(cfg_hide(...))]` takes a list of attributes - --> $DIR/doc_cfg_hide.rs:4:8 +error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items + --> $DIR/doc_cfg_hide.rs:2:8 | -LL | #![doc(cfg_hide)] - | ^^^^^^^^ +LL | #![doc(auto_cfg(hide))] + | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/rustdoc/doc-cfg/doc-cfg-hide.rs b/tests/rustdoc/doc-cfg/doc-cfg-hide.rs index ceb1f99fae0d1..cf906ede7e13f 100644 --- a/tests/rustdoc/doc-cfg/doc-cfg-hide.rs +++ b/tests/rustdoc/doc-cfg/doc-cfg-hide.rs @@ -1,7 +1,7 @@ #![crate_name = "oud"] -#![feature(doc_auto_cfg, doc_cfg, doc_cfg_hide)] +#![feature(doc_auto_cfg, doc_cfg, doc_cfg_hide, no_core)] -#![doc(cfg_hide(feature = "solecism"))] +#![doc(auto_cfg(hide(feature = "solecism")))] //@ has 'oud/struct.Solecism.html' //@ count - '//*[@class="stab portability"]' 0 @@ -18,7 +18,7 @@ pub struct Scribacious; //@ has 'oud/struct.Hyperdulia.html' //@ count - '//*[@class="stab portability"]' 1 -//@ matches - '//*[@class="stab portability"]' 'crate feature hyperdulia' +//@ matches - '//*[@class="stab portability"]' 'crate features hyperdulia only' //@ compile-flags:--cfg feature="hyperdulia" #[cfg(feature = "solecism")] #[cfg(feature = "hyperdulia")] @@ -26,7 +26,7 @@ pub struct Hyperdulia; //@ has 'oud/struct.Oystercatcher.html' //@ count - '//*[@class="stab portability"]' 1 -//@ matches - '//*[@class="stab portability"]' 'crate feature oystercatcher only' +//@ matches - '//*[@class="stab portability"]' 'crate features oystercatcher only' //@ compile-flags:--cfg feature="oystercatcher" #[cfg(all(feature = "solecism", feature = "oystercatcher"))] pub struct Oystercatcher; diff --git a/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs b/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs index b5b8d0f427bf7..27923893bddae 100644 --- a/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs +++ b/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs @@ -2,6 +2,6 @@ #![crate_name = "xenogenous"] //@ has 'xenogenous/struct.Worricow.html' -//@ count - '//*[@class="stab portability"]' 0 +//@ count - '//*[@class="stab portability"]' 1 #[cfg(feature = "worricow")] pub struct Worricow; diff --git a/tests/rustdoc/doc_auto_cfg_reexports.rs b/tests/rustdoc/doc_auto_cfg_reexports.rs new file mode 100644 index 0000000000000..f226c52e2ebf9 --- /dev/null +++ b/tests/rustdoc/doc_auto_cfg_reexports.rs @@ -0,0 +1,21 @@ +// Checks that `cfg` are correctly applied on inlined reexports. + +#![crate_name = "foo"] + +// Check with `std` item. +//@ has 'foo/index.html' '//*[@class="stab portability"]' 'Non-moustache' +//@ has 'foo/struct.C.html' '//*[@class="stab portability"]' \ +// 'Available on non-crate feature moustache only.' +#[cfg(not(feature = "moustache"))] +pub use std::cell::RefCell as C; + +// Check with local item. +mod x { + pub struct B; +} + +//@ has 'foo/index.html' '//*[@class="stab portability"]' 'Non-pistache' +//@ has 'foo/struct.B.html' '//*[@class="stab portability"]' \ +// 'Available on non-crate feature pistache only.' +#[cfg(not(feature = "pistache"))] +pub use crate::x::B; From 2d82c99f1e4a4e50b47f75d767b0d1d957357c1a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Apr 2025 17:01:00 +0200 Subject: [PATCH 0946/1900] Add "global" rustdoc test for RFC 3631 --- tests/rustdoc/doc_auto_cfg.rs | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/rustdoc/doc_auto_cfg.rs diff --git a/tests/rustdoc/doc_auto_cfg.rs b/tests/rustdoc/doc_auto_cfg.rs new file mode 100644 index 0000000000000..cb7c0e3f5950c --- /dev/null +++ b/tests/rustdoc/doc_auto_cfg.rs @@ -0,0 +1,80 @@ +// Test covering RFC 3631 features. + +#![crate_name = "foo"] +#![feature(no_core)] +#![no_core] +#![no_std] + +#![doc(auto_cfg(hide(feature = "hidden")))] + +//@ has 'foo/index.html' +//@ !has - '//*[@class="stab portability"]' 'Non-moustache' +//@ has - '//*[@class="stab portability"]' 'Non-pistache' +//@ count - '//*[@class="stab portability"]' 1 + +//@ has 'foo/m/index.html' +//@ count - '//*[@title="Available on non-crate feature `hidden` only"]' 2 +#[cfg(not(feature = "hidden"))] +pub mod m { + //@ count 'foo/m/struct.A.html' '//*[@class="stab portability"]' 0 + pub struct A; + + //@ has 'foo/m/inner/index.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' + #[doc(auto_cfg(show(feature = "hidden")))] + pub mod inner { + //@ has 'foo/m/inner/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' + pub struct B; + + //@ count 'foo/m/inner/struct.A.html' '//*[@class="stab portability"]' 0 + #[doc(auto_cfg(hide(feature = "hidden")))] + pub struct A; + } + + //@ has 'foo/m/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' + #[doc(auto_cfg(show(feature = "hidden")))] + pub struct B; +} + +//@ count 'foo/n/index.html' '//*[@title="Available on non-crate feature `moustache` only"]' 3 +//@ count - '//dl/dt' 4 +#[cfg(not(feature = "moustache"))] +#[doc(auto_cfg = false)] +pub mod n { + // Should not have `moustache` listed. + //@ count 'foo/n/struct.X.html' '//*[@class="stab portability"]' 0 + pub struct X; + + // Should re-enable `auto_cfg` and make `moustache` listed. + //@ has 'foo/n/struct.Y.html' '//*[@class="stab portability"]' \ + // 'Available on non-crate feature moustache only.' + #[doc(auto_cfg)] + pub struct Y; + + // Should re-enable `auto_cfg` and make `moustache` listed for itself + // and for `Y`. + //@ has 'foo/n/inner/index.html' '//*[@class="stab portability"]' \ + // 'Available on non-crate feature moustache only.' + #[doc(auto_cfg = true)] + pub mod inner { + //@ has 'foo/n/inner/struct.Y.html' '//*[@class="stab portability"]' \ + // 'Available on non-crate feature moustache only.' + pub struct Y; + } + + // Should re-enable `auto_cfg` and make `moustache` listed. + //@ has 'foo/n/struct.Z.html' '//*[@class="stab portability"]' \ + // 'Available on non-crate feature moustache only.' + #[doc(auto_cfg(hide(feature = "hidden")))] + pub struct Z; +} + +// Checking inheritance. +//@ has 'foo/o/index.html' '//*[@class="stab portability"]' \ +// 'Available on non-crate feature pistache only.' +#[doc(cfg(not(feature = "pistache")))] +pub mod o { + //@ has 'foo/o/struct.A.html' '//*[@class="stab portability"]' \ + // 'Available on non-crate feature pistache and non-crate feature tarte only.' + #[doc(cfg(not(feature = "tarte")))] + pub struct A; +} From 63aefe0737d0df886955f5a3aa68db865cd97f1b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Apr 2025 17:24:28 +0200 Subject: [PATCH 0947/1900] Strenghten checks for `doc(auto_cfg(show/hide))` attributes --- compiler/rustc_passes/messages.ftl | 3 +++ compiler/rustc_passes/src/check_attr.rs | 15 ++++++++++++++- compiler/rustc_passes/src/errors.rs | 6 ++++++ library/alloc/src/lib.rs | 1 - library/std/src/lib.rs | 8 +------- tests/rustdoc-ui/lints/doc_cfg_hide.rs | 1 + tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 8 +++++++- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 19014f37c6628..8326ddaf194d7 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -149,6 +149,9 @@ passes_doc_auto_cfg_expects_hide_or_show = passes_doc_auto_cfg_hide_show_expects_list = `#![doc(auto_cfg({$attr_name}(...)))]` only expects a list of items +passes_doc_auto_cfg_hide_show_unexpected_item = + `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/values items + passes_doc_auto_cfg_wrong_literal = `expected boolean for #[doc(auto_cfg = ...)]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3dc232a35ec3a..38b67e2f9dc5f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1187,7 +1187,20 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span, errors::DocAutoCfgExpectsHideOrShow, ); - } else if item.meta_item_list().is_none() { + } else if let Some(list) = item.meta_item_list() { + for item in list { + if item.meta_item_list().is_some() { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + item.span(), + errors::DocAutoCfgHideShowUnexpectedItem { + attr_name: attr_name.as_str(), + }, + ); + } + } + } else { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6cc0bd6ce48bd..1d2428c4f9a05 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -322,6 +322,12 @@ pub(crate) struct DocAutoCfgHideShowExpectsList<'a> { pub attr_name: &'a str, } +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_hide_show_unexpected_item)] +pub(crate) struct DocAutoCfgHideShowUnexpectedItem<'a> { + pub attr_name: &'a str, +} + #[derive(LintDiagnostic)] #[diag(passes_doc_test_unknown_any)] pub(crate) struct DocTestUnknownAny { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 243fdc2e8436f..dc5d243e8823a 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -78,7 +78,6 @@ #![cfg_attr( not(bootstrap), doc(auto_cfg(hide( - test, no_global_oom_handling, no_rc, no_sync, diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 93c91b615258d..ecd354f599e4c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -243,13 +243,7 @@ not(no_global_oom_handling) )) )] -#![cfg_attr( - not(bootstrap), - doc(auto_cfg(hide( - test, - no_global_oom_handling, - ))) -)] +#![cfg_attr(not(bootstrap), doc(auto_cfg(hide(no_global_oom_handling))))] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index abf5631847906..4f2625d00ce43 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,2 +1,3 @@ #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR +#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index bb0e52eb666ad..22501d63c3fb9 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -12,5 +12,11 @@ error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items LL | #![doc(auto_cfg(hide))] | ^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/values items + --> $DIR/doc_cfg_hide.rs:3:22 + | +LL | #![doc(auto_cfg(hide(not(windows))))] + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors From 4847eaf6d25473ad1b8dab4d8d6cd62520f55e0e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Apr 2025 17:49:08 +0200 Subject: [PATCH 0948/1900] Remove useless code in `propagate_doc_cfg.rs` --- src/librustdoc/passes/propagate_doc_cfg.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 957a1f56c7189..1425687cd2698 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -1,14 +1,10 @@ //! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items. -use std::sync::Arc; - use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -use rustc_hir::def_id::LocalDefId; use rustc_hir::{AttrArgs, Attribute}; use rustc_span::symbol::sym; -use crate::clean::cfg::Cfg; use crate::clean::inline::{load_attrs, merge_attrs}; use crate::clean::{CfgInfo, Crate, Item, ItemKind}; use crate::core::DocContext; @@ -22,13 +18,10 @@ pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { }; pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { - CfgPropagator { parent_cfg: None, parent: None, cx, cfg_info: CfgInfo::default() } - .fold_crate(cr) + CfgPropagator { cx, cfg_info: CfgInfo::default() }.fold_crate(cr) } struct CfgPropagator<'a, 'tcx> { - parent_cfg: Option>, - parent: Option, cx: &'a mut DocContext<'tcx>, cfg_info: CfgInfo, } @@ -131,21 +124,11 @@ impl CfgPropagator<'_, '_> { impl DocFolder for CfgPropagator<'_, '_> { fn fold_item(&mut self, mut item: Item) -> Option { let old_cfg_info = self.cfg_info.clone(); - let old_parent_cfg = self.parent_cfg.clone(); self.merge_with_parent_attributes(&mut item); - self.parent_cfg = item.inner.cfg.clone(); - let old_parent = - if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { - self.parent.replace(def_id) - } else { - self.parent.take() - }; let result = self.fold_item_recur(item); self.cfg_info = old_cfg_info; - self.parent_cfg = old_parent_cfg; - self.parent = old_parent; Some(result) } From 18df897221c57941956f77ef1f92261cf983577d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 7 Apr 2025 16:18:27 +0200 Subject: [PATCH 0949/1900] Remove ui test for doc_cfg feature gate --- tests/ui/feature-gates/feature-gate-doc_cfg.rs | 6 +++++- tests/ui/feature-gates/feature-gate-doc_cfg.stderr | 13 ------------- 2 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-doc_cfg.stderr diff --git a/tests/ui/feature-gates/feature-gate-doc_cfg.rs b/tests/ui/feature-gates/feature-gate-doc_cfg.rs index b12b8a1057182..83053e8c8bfd6 100644 --- a/tests/ui/feature-gates/feature-gate-doc_cfg.rs +++ b/tests/ui/feature-gates/feature-gate-doc_cfg.rs @@ -1,2 +1,6 @@ -#[doc(cfg(unix))] //~ ERROR: `#[doc(cfg)]` is experimental +//@ build-pass + +// FIXME: Remove this test once `doc_cfg` feature is completely removed. + +#[doc(cfg(unix))] fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-doc_cfg.stderr b/tests/ui/feature-gates/feature-gate-doc_cfg.stderr deleted file mode 100644 index 5315aaeeb3edb..0000000000000 --- a/tests/ui/feature-gates/feature-gate-doc_cfg.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: `#[doc(cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:1:1 - | -LL | #[doc(cfg(unix))] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #43781 for more information - = help: add `#![feature(doc_cfg)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. From d6ba93a3fee59a08820ee09ab7b3096ccfe99219 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 7 Apr 2025 17:07:21 +0200 Subject: [PATCH 0950/1900] Remove `doc_cfg` related content from rustdoc book unstable features chapter --- src/doc/rustdoc/src/unstable-features.md | 82 ------------------------ 1 file changed, 82 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 4327a80cd0832..a8856c2ecc776 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -56,88 +56,6 @@ It is also not emitted for foreign items, aliases, extern crates and imports. These features operate by extending the `#[doc]` attribute, and thus can be caught by the compiler and enabled with a `#![feature(...)]` attribute in your crate. -### `#[doc(cfg)]`: Recording what platforms or features are required for code to be present - - * Tracking issue: [#43781](https://github.com/rust-lang/rust/issues/43781) - -You can use `#[doc(cfg(...))]` to tell Rustdoc exactly which platform items appear on. -This has two effects: - -1. doctests will only run on the appropriate platforms, and -2. When Rustdoc renders documentation for that item, it will be accompanied by a banner explaining - that the item is only available on certain platforms. - -`#[doc(cfg)]` is intended to be used alongside [`#[cfg(doc)]`][cfg-doc]. -For example, `#[cfg(any(windows, doc))]` will preserve the item either on Windows or during the -documentation process. Then, adding a new attribute `#[doc(cfg(windows))]` will tell Rustdoc that -the item is supposed to be used on Windows. For example: - -```rust -#![feature(doc_cfg)] - -/// Token struct that can only be used on Windows. -#[cfg(any(windows, doc))] -#[doc(cfg(windows))] -pub struct WindowsToken; - -/// Token struct that can only be used on Unix. -#[cfg(any(unix, doc))] -#[doc(cfg(unix))] -pub struct UnixToken; - -/// Token struct that is only available with the `serde` feature -#[cfg(feature = "serde")] -#[doc(cfg(feature = "serde"))] -#[derive(serde::Deserialize)] -pub struct SerdeToken; -``` - -In this sample, the tokens will only appear on their respective platforms, but they will both appear -in documentation. - -`#[doc(cfg(...))]` was introduced to be used by the standard library and currently requires the -`#![feature(doc_cfg)]` feature gate. For more information, see [its chapter in the Unstable -Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. - -### `doc_auto_cfg`: Automatically generate `#[doc(cfg)]` - - * Tracking issue: [#43781](https://github.com/rust-lang/rust/issues/43781) - -`doc_auto_cfg` is an extension to the `#[doc(cfg)]` feature. With it, you don't need to add -`#[doc(cfg(...)]` anymore unless you want to override the default behaviour. So if we take the -previous source code: - -```rust -#![feature(doc_auto_cfg)] - -/// Token struct that can only be used on Windows. -#[cfg(any(windows, doc))] -pub struct WindowsToken; - -/// Token struct that can only be used on Unix. -#[cfg(any(unix, doc))] -pub struct UnixToken; - -/// Token struct that is only available with the `serde` feature -#[cfg(feature = "serde")] -#[derive(serde::Deserialize)] -pub struct SerdeToken; -``` - -It'll render almost the same, the difference being that `doc` will also be displayed. To fix this, -you can use `doc_cfg_hide`: - -```rust -#![feature(doc_cfg_hide)] -#![doc(cfg_hide(doc))] -``` - -And `doc` won't show up anymore! - -[cfg-doc]: ./advanced-features.md -[unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html -[issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781 - ### Adding your trait to the "Notable traits" dialog * Tracking issue: [#45040](https://github.com/rust-lang/rust/issues/45040) From bb34332858fa6c675cbbaf421ad3986df22d86ef Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 11 Apr 2025 11:53:49 +0200 Subject: [PATCH 0951/1900] Update book for `doc_cfg` feature --- src/doc/rustdoc/src/unstable-features.md | 268 +++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index a8856c2ecc776..cfa1936f3d7fc 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -719,3 +719,271 @@ pass `--doctest-build-arg ARG` for each argument `ARG`. ## `--generate-macro-expansion`: Generate macros expansion toggles in source code This flag enables the generation of toggles to expand macros in the HTML source code pages. + +## `#[doc(cfg)]` + +This feature aims at providing rustdoc users the possibility to add visual markers to the rendered documentation to know under which conditions an item is available (currently possible through the following unstable features: `doc_cfg`, `doc_auto_cfg` and `doc_cfg_hide`). + +It does not aim to allow having a same item with different `cfg`s to appear more than once in the generated documentation. + +It does not aim to document items which are *inactive* under the current configuration (i.e., “`cfg`ed out”). + +This features adds the following attributes: + + * `#[doc(auto_cfg)]`/`#[doc(auto_cfg = true)]`/`#[doc(auto_cfg = false)]` + * `#[doc(cfg(...))]` + * `#![doc(auto_cfg(hide(...)))]` / `#[doc(auto_cfg(show(...)))]` + +All of these attributes can be added to a module or to the crate root, and they will be inherited by the child items unless another attribute overrides it. This is why "opposite" attributes like `auto_cfg(hide(...))` and `auto_cfg(show(...))` are provided: they allow a child item to override its parent. + +### `#[doc(auto_cfg)`/`#[doc(auto_cfg = true)]`/`#[doc(auto_cfg = false)]` + +By default, `#[doc(auto_cfg)]` is enabled at the crate-level. When it's enabled, Rustdoc will automatically display `cfg(...)` compatibility information as-if the same `#[doc(cfg(...))]` had been specified. + +This attribute impacts the item on which it is used and its descendants. + +So if we take back the previous example: + +```rust +#[cfg(feature = "futures-io")] +pub mod futures {} +``` + +There's no need to "duplicate" the `cfg` into a `doc(cfg())` to make Rustdoc display it. + +In some situations, the detailed conditional compilation rules used to implement the feature might not serve as good documentation (for example, the list of supported platforms might be very long, and it might be better to document them in one place). To turn it off, add the `#[doc(auto_cfg = false)]` attribute on the item. + +If no argument is specified (ie `#[doc(auto_cfg)]`), it's the same as writing `#[doc(auto_cfg = true)]`. + +### `#[doc(cfg(...))]` + +This attribute provides a standardized format to override `#[cfg()]` attributes to document conditionally available items. Example: + +```rust,ignore (nightly) +// the "real" cfg condition +#[cfg(feature = "futures-io")] +// the `doc(cfg())` so it's displayed to the readers +#[doc(cfg(feature = "futures-io"))] +pub mod futures {} +``` + +It will display in the documentation for this module: + +```text +This is supported on feature="futures-io" only. +``` + +You can use it to display information in generated documentation, whether or not there is a `#[cfg()]` attribute: + +```rust,ignore (nightly) +#[doc(cfg(feature = "futures-io"))] +pub mod futures {} +``` + +It will be displayed exactly the same as the previous code. + +This attribute has the same syntax as conditional compilation, but it only causes documentation to be added. This means `#[doc(cfg(not(windows)))]` will not cause your docs to be hidden on non-windows targets, even though `#[cfg(not(windows))]` does do that. + +If `doc(auto_cfg)` is enabled on the item, `doc(cfg)` will override it anyway so in the two previous examples, even if the `doc(auto_cfg)` feature was enabled, it would still display the same thing. + +This attribute works on modules and on items. + +### `#[doc(auto_cfg(hide(...)))]` + +This attribute is used to prevent some `cfg` to be generated in the visual markers. It only applies to `#[doc(auto_cfg = true)]`, not to `#[doc(cfg(...))]`. So in the previous example: + +```rust,ignore (nightly) +#[cfg(any(unix, feature = "futures-io"))] +pub mod futures {} +``` + +It currently displays both `unix` and `feature = "futures-io"` into the documentation, which is not great. To prevent the `unix` cfg to ever be displayed, you can use this attribute at the crate root level: + +```rust,ignore (nightly) +#![doc(auto_cfg(hide(unix)))] +``` + +Or directly on a given item/module as it covers any of the item's descendants: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(unix)))] +#[cfg(any(unix, feature = "futures-io"))] +pub mod futures { + // `futures` and all its descendants won't display "unix" in their cfgs. +} +``` + +Then, the `unix` cfg will never be displayed into the documentation. + +Rustdoc currently hides `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. + +The attribute accepts only a list of identifiers or key/value items. So you can write: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(unix, doctest, feature = "something")))] +#[doc(auto_cfg(hide()))] +``` + +But you cannot write: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(not(unix))))] +``` + +So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix`: + +```rust,ignore (nightly) +#[cfg(unix)] // nothing displayed +#[cfg(any(unix))] // nothing displayed +#[cfg(any(unix, windows))] // only `windows` displayed +``` + +However, it only impacts the `unix` cfg, not the feature: + +```rust,ignore (nightly) +#[cfg(feature = "unix")] // `feature = "unix"` is displayed +``` + +If `cfg_auto(show(...))` and `cfg_auto(hide(...))` are used to show/hide a same `cfg` on a same item, it'll emit an error. Example: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(unix)))] +#[doc(auto_cfg(show(unix)))] // Error! +pub fn foo() {} +``` + +Using this attribute will re-enable `auto_cfg` if it was disabled at this location: + +```rust,ignore (nightly) +#[doc(auto_cfg = false)] // Disabling `auto_cfg` +pub fn foo() {} +``` + +And using `doc(auto_cfg)` will re-enable it: + +```rust,ignore (nightly) +#[doc(auto_cfg = false)] // Disabling `auto_cfg` +pub mod module { + #[doc(auto_cfg(hide(unix)))] // `auto_cfg` is re-enabled. + pub fn foo() {} +} +``` + +However, using `doc(auto_cfg = ...)` and `doc(auto_cfg(...))` on the same item will emit an error: + +```rust,ignore (nightly) +#[doc(auto_cfg = false)] +#[doc(auto_cfg(hide(unix)))] // error +pub fn foo() {} +``` + +The reason behind this is that `doc(auto_cfg = ...)` enables or disables the feature, whereas `doc(auto_cfg(...))` enables it unconditionally, making the first attribute to appear useless as it will be overidden by the next `doc(auto_cfg)` attribute. + +### `#[doc(auto_cfg(show(...)))]` + +This attribute does the opposite of `#[doc(auto_cfg(hide(...)))]`: if you used `#[doc(auto_cfg(hide(...)))]` and want to revert its effect on an item and its descendants, you can use `#[doc(auto_cfg(show(...)))]`. +It only applies to `#[doc(auto_cfg = true)]`, not to `#[doc(cfg(...))]`. + +For example: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(unix)))] +#[cfg(any(unix, feature = "futures-io"))] +pub mod futures { + // `futures` and all its descendants won't display "unix" in their cfgs. + #[doc(auto_cfg(show(unix)))] + pub mod child { + // `child` and all its descendants will display "unix" in their cfgs. + } +} +``` + +The attribute accepts only a list of identifiers or key/value items. So you can write: + +```rust,ignore (nightly) +#[doc(auto_cfg(show(unix, doctest, feature = "something")))] +#[doc(auto_cfg(show()))] +``` + +But you cannot write: + +```rust,ignore (nightly) +#[doc(auto_cfg(show(not(unix))))] +``` + +If `auto_cfg(show(...))` and `auto_cfg(hide(...))` are used to show/hide a same `cfg` on a same item, it'll emit an error. Example: + +```rust,ignore (nightly) +#[doc(auto_cfg(show(unix)))] +#[doc(auto_cfg(hide(unix)))] // Error! +pub fn foo() {} +``` + +Using this attribute will re-enable `auto_cfg` if it was disabled at this location: + +```rust,ignore (nightly) +#[doc(auto_cfg = false)] // Disabling `auto_cfg` +#[doc(auto_cfg(show(unix)))] // `auto_cfg` is re-enabled. +pub fn foo() {} +``` + +## Inheritance + +Rustdoc merges `cfg` attributes from parent modules to its children. For example, in this case, the module `non_unix` will describe the entire compatibility matrix for the module, and not just its directly attached information: + +```rust,ignore (nightly) +#[doc(cfg(any(windows, unix)))] +pub mod desktop { + #[doc(cfg(not(unix)))] + pub mod non_unix { + // ... + } +} +``` + +This code will display: + +```text +Available on (Windows or Unix) and non-Unix only. +``` + +### Re-exports and inlining + +`cfg` attributes of a re-export are never merged with the re-exported item(s) attributes except if the re-export has the `#[doc(inline)]` attribute. In this case, the `cfg` of the re-exported item will be merged with the re-export's. + +When talking about "attributes merge", we mean that if the re-export has `#[cfg(unix)]` and the re-exported item has `#[cfg(feature = "foo")]`, you will only see `cfg(unix)` on the re-export and only `cfg(feature = "foo")` on the re-exported item, unless the re-export has `#[doc(inline)]`, then you will only see the re-exported item with both `cfg(unix)` and `cfg(feature = "foo")`. + +Example: + +```rust,ignore (nightly) +#[doc(cfg(any(windows, unix)))] +pub mod desktop { + #[doc(cfg(not(unix)))] + pub mod non_unix { + // code + } +} + +#[doc(cfg(target_os = "freebsd"))] +pub use desktop::non_unix as non_unix_desktop; +#[doc(cfg(target_os = "macos"))] +#[doc(inline)] +pub use desktop::non_unix as inlined_non_unix_desktop; +``` + +In this example, `non_unix_desktop` will only display `cfg(target_os = "freeebsd")` and not display any `cfg` from `desktop::non_unix`. + +On the contrary, `inlined_non_unix_desktop` will have cfgs from both the re-export and the re-exported item. + +So that also means that if a crate re-exports a foreign item, unless it has `#[doc(inline)]`, the `cfg` and `doc(cfg)` attributes will not be visible: + +```rust,ignore (nightly) +// dep: +#[cfg(feature = "a")] +pub struct S; + +// crate using dep: + +// There will be no mention of `feature = "a"` in the documentation. +pub use dep::S as Y; +``` From ed05315867b7dd17a456beaabc3faf36357f9447 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 11 Apr 2025 12:02:48 +0200 Subject: [PATCH 0952/1900] Rename `CfgInfo::doc_auto_cfg_active` into `auto_cfg_active` --- src/librustdoc/clean/types.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 63e8357801c05..2ec97a57087a1 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -926,7 +926,7 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( pub(crate) struct CfgInfo { hidden_cfg: FxHashSet, current_cfg: Cfg, - doc_auto_cfg_active: bool, + auto_cfg_active: bool, parent_is_doc_cfg: bool, } @@ -941,7 +941,7 @@ impl Default for CfgInfo { .into_iter() .collect(), current_cfg: Cfg::True, - doc_auto_cfg_active: true, + auto_cfg_active: true, parent_is_doc_cfg: false, } } @@ -1058,7 +1058,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator match &attr.kind { MetaItemKind::Word => { if let Some(first_change) = changed_auto_active_status { - if !cfg_info.doc_auto_cfg_active { + if !cfg_info.auto_cfg_active { tcx.sess.dcx().struct_span_err( vec![first_change, attr.span], "`auto_cfg` was disabled and enabled more than once on the same item", @@ -1068,12 +1068,12 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } else { changed_auto_active_status = Some(attr.span); } - cfg_info.doc_auto_cfg_active = true; + cfg_info.auto_cfg_active = true; } MetaItemKind::NameValue(lit) => { if let LitKind::Bool(value) = lit.kind { if let Some(first_change) = changed_auto_active_status { - if cfg_info.doc_auto_cfg_active != value { + if cfg_info.auto_cfg_active != value { tcx.sess.dcx().struct_span_err( vec![first_change, attr.span], "`auto_cfg` was disabled and enabled more than once on the same item", @@ -1083,12 +1083,12 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } else { changed_auto_active_status = Some(attr.span); } - cfg_info.doc_auto_cfg_active = value; + cfg_info.auto_cfg_active = value; } } MetaItemKind::List(sub_attrs) => { if let Some(first_change) = changed_auto_active_status { - if !cfg_info.doc_auto_cfg_active { + if !cfg_info.auto_cfg_active { tcx.sess.dcx().struct_span_err( vec![first_change, attr.span], "`auto_cfg` was disabled and enabled more than once on the same item", @@ -1099,7 +1099,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator changed_auto_active_status = Some(attr.span); } // Whatever happens next, the feature is enabled again. - cfg_info.doc_auto_cfg_active = true; + cfg_info.auto_cfg_active = true; for sub_attr in sub_attrs.iter() { if let Some(ident) = sub_attr.ident() && (ident.name == sym::show || ident.name == sym::hide) @@ -1157,7 +1157,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing // to be done here. - if !cfg_info.doc_auto_cfg_active && !cfg_info.parent_is_doc_cfg { + if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg { None } else if cfg_info.parent_is_doc_cfg { if cfg_info.current_cfg == Cfg::True { From c06a076634e4feab47e133fe6325af9659bab082 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 11 Apr 2025 14:32:31 +0200 Subject: [PATCH 0953/1900] Put back the `doc_cfg` code behind a nightly feature --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + src/librustdoc/passes/propagate_doc_cfg.rs | 6 +++++- tests/rustdoc-ui/cfg-hide-show-conflict.rs | 1 + tests/rustdoc-ui/cfg-hide-show-conflict.stderr | 4 ++-- tests/rustdoc-ui/lints/doc_cfg_hide.rs | 1 + tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 6 +++--- tests/rustdoc/doc-auto-cfg.rs | 2 +- tests/rustdoc/doc-cfg/doc-cfg-hide.rs | 2 +- tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs | 1 + tests/rustdoc/doc_auto_cfg.rs | 5 +---- tests/rustdoc/doc_auto_cfg_reexports.rs | 1 + tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs | 2 +- .../doc_auto_cfg-reexport-foreign-113982.rs | 2 +- .../glob-reexport-attribute-merge-doc-auto-cfg.rs | 2 +- tests/ui/feature-gates/feature-gate-doc_cfg.rs | 6 +----- tests/ui/feature-gates/feature-gate-doc_cfg.stderr | 13 +++++++++++++ 16 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-doc_cfg.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index fe9cc8e61ed5b..35531378784b3 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -182,6 +182,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_doc!( "experimental" { + cfg => doc_cfg masked => doc_masked notable_trait => doc_notable_trait } diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 1425687cd2698..802f2fbe569cd 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -18,7 +18,11 @@ pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { }; pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { - CfgPropagator { cx, cfg_info: CfgInfo::default() }.fold_crate(cr) + if cx.tcx.features().doc_cfg() { + CfgPropagator { cx, cfg_info: CfgInfo::default() }.fold_crate(cr) + } else { + cr + } } struct CfgPropagator<'a, 'tcx> { diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs index a8a50fe15c7e1..8e98b95c85bb9 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.rs +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.rs @@ -1,2 +1,3 @@ +#![feature(doc_cfg)] #![doc(auto_cfg(hide(target_os = "linux")))] #![doc(auto_cfg(show(windows, target_os = "linux")))] //~ ERROR diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr index d2d0564606a54..22231e82cd7bf 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr @@ -1,11 +1,11 @@ error: same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item - --> $DIR/cfg-hide-show-conflict.rs:2:31 + --> $DIR/cfg-hide-show-conflict.rs:3:31 | LL | #![doc(auto_cfg(show(windows, target_os = "linux")))] | ^^^^^^^^^^^^^^^^^^^ | note: first change was here - --> $DIR/cfg-hide-show-conflict.rs:1:22 + --> $DIR/cfg-hide-show-conflict.rs:2:22 | LL | #![doc(auto_cfg(hide(target_os = "linux")))] | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 4f2625d00ce43..397b21393e5c7 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,3 +1,4 @@ +#![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR #![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index 22501d63c3fb9..0e9db5a30d83b 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,5 +1,5 @@ error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items - --> $DIR/doc_cfg_hide.rs:1:8 + --> $DIR/doc_cfg_hide.rs:2:8 | LL | #![doc(auto_cfg(hide = "test"))] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | #![doc(auto_cfg(hide = "test"))] = note: `#[deny(invalid_doc_attributes)]` on by default error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items - --> $DIR/doc_cfg_hide.rs:2:8 + --> $DIR/doc_cfg_hide.rs:3:8 | LL | #![doc(auto_cfg(hide))] | ^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/values items - --> $DIR/doc_cfg_hide.rs:3:22 + --> $DIR/doc_cfg_hide.rs:4:22 | LL | #![doc(auto_cfg(hide(not(windows))))] | ^^^^^^^^^^^^ diff --git a/tests/rustdoc/doc-auto-cfg.rs b/tests/rustdoc/doc-auto-cfg.rs index b3fe8922fd788..e56cf18d08a0c 100644 --- a/tests/rustdoc/doc-auto-cfg.rs +++ b/tests/rustdoc/doc-auto-cfg.rs @@ -1,4 +1,4 @@ -#![feature(doc_auto_cfg)] +#![feature(doc_cfg)] #![crate_name = "foo"] //@ has foo/fn.foo.html diff --git a/tests/rustdoc/doc-cfg/doc-cfg-hide.rs b/tests/rustdoc/doc-cfg/doc-cfg-hide.rs index cf906ede7e13f..e919206d3a478 100644 --- a/tests/rustdoc/doc-cfg/doc-cfg-hide.rs +++ b/tests/rustdoc/doc-cfg/doc-cfg-hide.rs @@ -1,5 +1,5 @@ #![crate_name = "oud"] -#![feature(doc_auto_cfg, doc_cfg, doc_cfg_hide, no_core)] +#![feature(doc_cfg)] #![doc(auto_cfg(hide(feature = "solecism")))] diff --git a/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs b/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs index 27923893bddae..9ae8b8fca4f7a 100644 --- a/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs +++ b/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs @@ -1,4 +1,5 @@ //@ compile-flags:--cfg feature="worricow" +#![feature(doc_cfg)] #![crate_name = "xenogenous"] //@ has 'xenogenous/struct.Worricow.html' diff --git a/tests/rustdoc/doc_auto_cfg.rs b/tests/rustdoc/doc_auto_cfg.rs index cb7c0e3f5950c..19ef174c1778d 100644 --- a/tests/rustdoc/doc_auto_cfg.rs +++ b/tests/rustdoc/doc_auto_cfg.rs @@ -1,10 +1,7 @@ // Test covering RFC 3631 features. #![crate_name = "foo"] -#![feature(no_core)] -#![no_core] -#![no_std] - +#![feature(doc_cfg)] #![doc(auto_cfg(hide(feature = "hidden")))] //@ has 'foo/index.html' diff --git a/tests/rustdoc/doc_auto_cfg_reexports.rs b/tests/rustdoc/doc_auto_cfg_reexports.rs index f226c52e2ebf9..f6315e9d49dde 100644 --- a/tests/rustdoc/doc_auto_cfg_reexports.rs +++ b/tests/rustdoc/doc_auto_cfg_reexports.rs @@ -1,6 +1,7 @@ // Checks that `cfg` are correctly applied on inlined reexports. #![crate_name = "foo"] +#![feature(doc_cfg)] // Check with `std` item. //@ has 'foo/index.html' '//*[@class="stab portability"]' 'Non-moustache' diff --git a/tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs b/tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs index f85d7b236372d..f24ebcd52acb6 100644 --- a/tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs +++ b/tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs @@ -1,6 +1,6 @@ // Regression test for . -#![feature(doc_auto_cfg)] +#![feature(doc_cfg)] #![crate_type = "lib"] #![crate_name = "foo"] diff --git a/tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs b/tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs index 76b25127a9c64..f8ec4afc0313e 100644 --- a/tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs +++ b/tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs @@ -1,7 +1,7 @@ //@ aux-build: issue-113982-doc_auto_cfg-reexport-foreign.rs // https://github.com/rust-lang/rust/issues/113982 -#![feature(no_core, doc_auto_cfg)] +#![feature(no_core, doc_cfg)] #![no_core] #![crate_name = "foo"] diff --git a/tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs b/tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs index d0a2165ec8abb..0aed2b0c46208 100644 --- a/tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs +++ b/tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs @@ -2,7 +2,7 @@ // the reexported item whereas glob reexports do with the `doc_auto_cfg` feature. #![crate_name = "foo"] -#![feature(doc_auto_cfg)] +#![feature(doc_cfg)] //@ has 'foo/index.html' // There are two items. diff --git a/tests/ui/feature-gates/feature-gate-doc_cfg.rs b/tests/ui/feature-gates/feature-gate-doc_cfg.rs index 83053e8c8bfd6..213a5a8c5a988 100644 --- a/tests/ui/feature-gates/feature-gate-doc_cfg.rs +++ b/tests/ui/feature-gates/feature-gate-doc_cfg.rs @@ -1,6 +1,2 @@ -//@ build-pass - -// FIXME: Remove this test once `doc_cfg` feature is completely removed. - -#[doc(cfg(unix))] +#[doc(cfg(unix))] //~ ERROR fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-doc_cfg.stderr b/tests/ui/feature-gates/feature-gate-doc_cfg.stderr new file mode 100644 index 0000000000000..5315aaeeb3edb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-doc_cfg.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[doc(cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:1:1 + | +LL | #[doc(cfg(unix))] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 1561efe41afe40c1afaf3d11316ef754d9a8f9a9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 27 May 2025 17:14:46 +0200 Subject: [PATCH 0954/1900] Add code documentation, improve code and improve error message --- compiler/rustc_passes/messages.ftl | 2 +- compiler/rustc_passes/src/check_attr.rs | 9 +++------ src/librustdoc/clean/types.rs | 13 +++++++++++++ src/librustdoc/passes/propagate_doc_cfg.rs | 14 +++++++++----- tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 4 ++-- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 8326ddaf194d7..a9a0f6d8b516b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -147,7 +147,7 @@ passes_doc_auto_cfg_expects_hide_or_show = `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"` passes_doc_auto_cfg_hide_show_expects_list = - `#![doc(auto_cfg({$attr_name}(...)))]` only expects a list of items + `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items passes_doc_auto_cfg_hide_show_unexpected_item = `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/values items diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 38b67e2f9dc5f..c521467b556c1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast}; +use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; @@ -1161,10 +1161,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Check that the `#![doc(auto_cfg(..))]` attribute has expected input. - fn check_doc_auto_cfg(&self, meta: &MetaItemInner, hir_id: HirId) { - let MetaItemInner::MetaItem(meta) = meta else { - unreachable!(); - }; + fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) { match &meta.kind { MetaItemKind::Word => {} MetaItemKind::NameValue(lit) => { @@ -1286,7 +1283,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Some(sym::auto_cfg) => { - self.check_doc_auto_cfg(meta, hir_id); + self.check_doc_auto_cfg(i_meta, hir_id); } Some(sym::inline | sym::no_inline) => { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2ec97a57087a1..545f1ef6da4a3 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -922,11 +922,19 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( .flatten() } +/// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { + /// List of `doc(auto_cfg(hide(...)))` cfgs. hidden_cfg: FxHashSet, + /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while + /// taking into account the `hidden_cfg` information. current_cfg: Cfg, + /// Whether the `doc(auto_cfg())` feature is enabled or not at this point. auto_cfg_active: bool, + /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`, + /// instead we will concatenate with it. However, if it's not the case, we need to overwrite + /// `current_cfg`. parent_is_doc_cfg: bool, } @@ -962,6 +970,11 @@ fn show_hide_show_conflict_error( diag.emit(); } +/// This function checks if a same `cfg` is present in both `auto_cfg(hide(...))` and +/// `auto_cfg(show(...))` on the same item. If so, it emits an error. +/// +/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` +/// and in `new_hide_attrs` arguments. fn handle_auto_cfg_hide_show( tcx: TyCtxt<'_>, cfg_info: &mut CfgInfo, diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 802f2fbe569cd..ea77065ac0799 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -30,7 +30,8 @@ struct CfgPropagator<'a, 'tcx> { cfg_info: CfgInfo, } -fn should_retain(token: &TokenTree) -> bool { +/// Returns true if the provided `token` is a `cfg` ident. +fn is_cfg_token(token: &TokenTree) -> bool { // We only keep `doc(cfg)` items. matches!( token, @@ -47,7 +48,9 @@ fn should_retain(token: &TokenTree) -> bool { ) } -fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec { +/// We only want to keep `#[cfg()]` and `#[doc(cfg())]` attributes so we rebuild a vec of +/// `TokenTree` with only the tokens we're interested into. +fn filter_non_cfg_tokens_from_list(args_tokens: &TokenStream) -> Vec { let mut tokens = Vec::with_capacity(args_tokens.len()); let mut skip_next_delimited = false; for token in args_tokens.iter() { @@ -58,7 +61,7 @@ fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec { } skip_next_delimited = false; } - token if should_retain(token) => { + token if is_cfg_token(token) => { skip_next_delimited = false; tokens.push(token.clone()); } @@ -70,7 +73,8 @@ fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec { tokens } -// We only care about `#[cfg()]` and `#[doc(cfg())]`, we discard everything else. +/// This function goes through the attributes list (`new_attrs`) and extract the `cfg` tokens from +/// it and put them into `attrs`. fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) { for attr in new_attrs { if attr.is_doc_comment() { @@ -84,7 +88,7 @@ fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) if ident == sym::doc && let AttrArgs::Delimited(args) = &mut normal.args { - let tokens = filter_tokens_from_list(&args.tokens); + let tokens = filter_non_cfg_tokens_from_list(&args.tokens); args.tokens = TokenStream::new(tokens); attrs.push(attr); } else if ident == sym::cfg_trace { diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index 0e9db5a30d83b..9e820a77b5e54 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,4 +1,4 @@ -error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items +error: `#![doc(auto_cfg(hide(...)))]` expects a list of items --> $DIR/doc_cfg_hide.rs:2:8 | LL | #![doc(auto_cfg(hide = "test"))] @@ -6,7 +6,7 @@ LL | #![doc(auto_cfg(hide = "test"))] | = note: `#[deny(invalid_doc_attributes)]` on by default -error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items +error: `#![doc(auto_cfg(hide(...)))]` expects a list of items --> $DIR/doc_cfg_hide.rs:3:8 | LL | #![doc(auto_cfg(hide))] From 553308b11503eafac6341b82f345bd62b09ba317 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 29 May 2025 18:39:08 +0200 Subject: [PATCH 0955/1900] Improve code and better check `doc(cfg(...))` attributes --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_passes/messages.ftl | 2 +- compiler/rustc_passes/src/check_attr.rs | 23 ++++- compiler/rustc_span/src/symbol.rs | 1 - src/librustdoc/clean/inline.rs | 4 + src/librustdoc/clean/types.rs | 89 +++++++++++-------- tests/rustdoc-ui/doc-cfg.rs | 9 ++ tests/rustdoc-ui/doc-cfg.stderr | 66 ++++++++++---- tests/rustdoc-ui/feature-gate-doc_cfg.rs | 6 ++ tests/rustdoc-ui/feature-gate-doc_cfg.stderr | 63 +++++++++++++ tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs | 8 -- .../feature-gate-doc_cfg_hide.stderr | 10 --- tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 2 +- 13 files changed, 209 insertions(+), 75 deletions(-) create mode 100644 tests/rustdoc-ui/feature-gate-doc_cfg.rs create mode 100644 tests/rustdoc-ui/feature-gate-doc_cfg.stderr delete mode 100644 tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs delete mode 100644 tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 35531378784b3..608ccfefeb69d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -183,6 +183,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_doc!( "experimental" { cfg => doc_cfg + auto_cfg => doc_cfg masked => doc_masked notable_trait => doc_notable_trait } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index a9a0f6d8b516b..a5ff169e638c9 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -150,7 +150,7 @@ passes_doc_auto_cfg_hide_show_expects_list = `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items passes_doc_auto_cfg_hide_show_unexpected_item = - `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/values items + `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items passes_doc_auto_cfg_wrong_literal = `expected boolean for #[doc(auto_cfg = ...)]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c521467b556c1..88b49c781e75c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1176,7 +1176,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } MetaItemKind::List(list) => { for item in list { - let Some(attr_name) = item.name() else { continue }; + let Some(attr_name) = item.name() else { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgExpectsHideOrShow, + ); + continue; + }; if attr_name != sym::hide && attr_name != sym::show { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1195,6 +1203,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_name: attr_name.as_str(), }, ); + } else if match item { + MetaItemInner::Lit(_) => true, + // We already checked above that it's not a list. + MetaItemInner::MetaItem(meta) => meta.path.segments.len() != 1, + } { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + item.span(), + errors::DocAutoCfgHideShowUnexpectedItem { + attr_name: attr_name.as_str(), + }, + ); } } } else { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e80a98994dbba..18c3faed9329a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1150,7 +1150,6 @@ symbols! { hashset_iter_ty, hexagon_target_feature, hidden, - hidden_cfg, hide, hint, homogeneous_aggregate, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 8ffa6033c9b0d..8beea0580de2d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -605,6 +605,10 @@ pub(crate) fn build_impl( }); } + // In here, we pass an empty `CfgInfo` because the computation of `cfg` happens later, so it + // doesn't matter at this point. + // + // We need to pass this empty `CfgInfo` because `merge_attrs` is used when computing the `cfg`. let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs, &mut CfgInfo::default()); trace!("merged_attrs={merged_attrs:?}"); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 545f1ef6da4a3..605b77109a061 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -970,8 +970,10 @@ fn show_hide_show_conflict_error( diag.emit(); } -/// This function checks if a same `cfg` is present in both `auto_cfg(hide(...))` and -/// `auto_cfg(show(...))` on the same item. If so, it emits an error. +/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. +/// +/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and +/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. /// /// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` /// and in `new_hide_attrs` arguments. @@ -1023,6 +1025,31 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator Some(item) } + fn check_changed_auto_active_status( + changed_auto_active_status: &mut Option, + attr: &ast::MetaItem, + cfg_info: &mut CfgInfo, + tcx: TyCtxt<'_>, + new_value: bool, + ) -> bool { + if let Some(first_change) = changed_auto_active_status { + if cfg_info.auto_cfg_active != new_value { + tcx.sess + .dcx() + .struct_span_err( + vec![*first_change, attr.span], + "`auto_cfg` was disabled and enabled more than once on the same item", + ) + .emit(); + return true; + } + } else { + *changed_auto_active_status = Some(attr.span); + } + cfg_info.auto_cfg_active = new_value; + false + } + let mut new_show_attrs = FxHashMap::default(); let mut new_hide_attrs = FxHashMap::default(); @@ -1070,49 +1097,39 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator }; match &attr.kind { MetaItemKind::Word => { - if let Some(first_change) = changed_auto_active_status { - if !cfg_info.auto_cfg_active { - tcx.sess.dcx().struct_span_err( - vec![first_change, attr.span], - "`auto_cfg` was disabled and enabled more than once on the same item", - ).emit(); - return None; - } - } else { - changed_auto_active_status = Some(attr.span); + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + true, + ) { + return None; } - cfg_info.auto_cfg_active = true; } MetaItemKind::NameValue(lit) => { if let LitKind::Bool(value) = lit.kind { - if let Some(first_change) = changed_auto_active_status { - if cfg_info.auto_cfg_active != value { - tcx.sess.dcx().struct_span_err( - vec![first_change, attr.span], - "`auto_cfg` was disabled and enabled more than once on the same item", - ).emit(); - return None; - } - } else { - changed_auto_active_status = Some(attr.span); + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + value, + ) { + return None; } - cfg_info.auto_cfg_active = value; } } MetaItemKind::List(sub_attrs) => { - if let Some(first_change) = changed_auto_active_status { - if !cfg_info.auto_cfg_active { - tcx.sess.dcx().struct_span_err( - vec![first_change, attr.span], - "`auto_cfg` was disabled and enabled more than once on the same item", - ).emit(); - return None; - } - } else { - changed_auto_active_status = Some(attr.span); + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + true, + ) { + return None; } - // Whatever happens next, the feature is enabled again. - cfg_info.auto_cfg_active = true; for sub_attr in sub_attrs.iter() { if let Some(ident) = sub_attr.ident() && (ident.name == sym::show || ident.name == sym::hide) diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index 14943bbc3418e..9840c305290ae 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -8,4 +8,13 @@ //~^^ WARN unexpected `cfg` condition name: `bar` #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR +#[doc(auto_cfg(42))] //~ ERROR +#[doc(auto_cfg(hide(true)))] //~ ERROR +#[doc(auto_cfg(hide(42)))] //~ ERROR +#[doc(auto_cfg(hide("a")))] //~ ERROR +#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR +// Shouldn't lint +#[doc(auto_cfg(hide(windows)))] +#[doc(auto_cfg(hide(feature = "windows")))] +#[doc(auto_cfg(hide(foo)))] pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index 1233ee010de21..36ca18eed8fc8 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -1,26 +1,34 @@ -error: `cfg` predicate is not specified - --> $DIR/doc-cfg.rs:3:7 +error: `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"` + --> $DIR/doc-cfg.rs:11:7 | -LL | #[doc(cfg(), cfg(foo, bar))] - | ^^^^^ help: expected syntax is: `cfg(/* predicate */)` +LL | #[doc(auto_cfg(42))] + | ^^^^^^^^^^^^ + | + = note: `#[deny(invalid_doc_attributes)]` on by default -error: multiple `cfg` predicates are specified - --> $DIR/doc-cfg.rs:3:23 +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items + --> $DIR/doc-cfg.rs:12:21 | -LL | #[doc(cfg(), cfg(foo, bar))] - | ^^^ +LL | #[doc(auto_cfg(hide(true)))] + | ^^^^ -error: `cfg` predicate is not specified - --> $DIR/doc-cfg.rs:9:7 +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items + --> $DIR/doc-cfg.rs:13:21 | -LL | #[doc(cfg())] - | ^^^^^ help: expected syntax is: `cfg(/* predicate */)` +LL | #[doc(auto_cfg(hide(42)))] + | ^^ -error: multiple `cfg` predicates are specified - --> $DIR/doc-cfg.rs:10:16 +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items + --> $DIR/doc-cfg.rs:14:21 | -LL | #[doc(cfg(foo, bar))] - | ^^^ +LL | #[doc(auto_cfg(hide("a")))] + | ^^^ + +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items + --> $DIR/doc-cfg.rs:15:21 + | +LL | #[doc(auto_cfg(hide(foo::bar)))] + | ^^^^^^^^ warning: unexpected `cfg` condition name: `foo` --> $DIR/doc-cfg.rs:6:11 @@ -42,5 +50,29 @@ LL | #[doc(cfg(foo), cfg(bar))] = help: to expect this configuration use `--check-cfg=cfg(bar)` = note: see for more information about checking conditional configuration -error: aborting due to 4 previous errors; 2 warnings emitted +error: `cfg` predicate is not specified + --> $DIR/doc-cfg.rs:3:7 + | +LL | #[doc(cfg(), cfg(foo, bar))] + | ^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: multiple `cfg` predicates are specified + --> $DIR/doc-cfg.rs:3:23 + | +LL | #[doc(cfg(), cfg(foo, bar))] + | ^^^ + +error: `cfg` predicate is not specified + --> $DIR/doc-cfg.rs:9:7 + | +LL | #[doc(cfg())] + | ^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: multiple `cfg` predicates are specified + --> $DIR/doc-cfg.rs:10:16 + | +LL | #[doc(cfg(foo, bar))] + | ^^^ + +error: aborting due to 9 previous errors; 2 warnings emitted diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg.rs b/tests/rustdoc-ui/feature-gate-doc_cfg.rs new file mode 100644 index 0000000000000..b474a1524bc19 --- /dev/null +++ b/tests/rustdoc-ui/feature-gate-doc_cfg.rs @@ -0,0 +1,6 @@ +#![doc(auto_cfg)] //~ ERROR +#![doc(auto_cfg(false))] //~ ERROR +#![doc(auto_cfg(true))] //~ ERROR +#![doc(auto_cfg(hide(feature = "solecism")))] //~ ERROR +#![doc(auto_cfg(show(feature = "bla")))] //~ ERROR +#![doc(cfg(feature = "solecism"))] //~ ERROR diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr new file mode 100644 index 0000000000000..68a86c1abb777 --- /dev/null +++ b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr @@ -0,0 +1,63 @@ +error[E0658]: `#[doc(auto_cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:1:1 + | +LL | #![doc(auto_cfg)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[doc(auto_cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:2:1 + | +LL | #![doc(auto_cfg(false))] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[doc(auto_cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:3:1 + | +LL | #![doc(auto_cfg(true))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[doc(auto_cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:4:1 + | +LL | #![doc(auto_cfg(hide(feature = "solecism")))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[doc(auto_cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:5:1 + | +LL | #![doc(auto_cfg(show(feature = "bla")))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[doc(cfg)]` is experimental + --> $DIR/feature-gate-doc_cfg.rs:6:1 + | +LL | #![doc(cfg(feature = "solecism"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #43781 for more information + = help: add `#![feature(doc_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs deleted file mode 100644 index e49285a01b812..0000000000000 --- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs +++ /dev/null @@ -1,8 +0,0 @@ -// FIXME: Remove this file once feature is removed - -#![doc(cfg_hide(test))] //~ ERROR - -#[cfg(not(test))] -pub fn public_fn() {} -#[cfg(test)] -pub fn internal_use_only() {} diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr deleted file mode 100644 index b3eee2af7f733..0000000000000 --- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unknown `doc` attribute `cfg_hide` - --> $DIR/feature-gate-doc_cfg_hide.rs:3:8 - | -LL | #![doc(cfg_hide(test))] - | ^^^^^^^^^^^^^^ - | - = note: `#[deny(invalid_doc_attributes)]` on by default - -error: aborting due to 1 previous error - diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index 9e820a77b5e54..c63c8d607fa02 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -12,7 +12,7 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items LL | #![doc(auto_cfg(hide))] | ^^^^^^^^^^^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/values items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items --> $DIR/doc_cfg_hide.rs:4:22 | LL | #![doc(auto_cfg(hide(not(windows))))] From fccba2c341b65b62f513c0b7ee8842f134f3cbcd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 29 May 2025 21:54:16 +0200 Subject: [PATCH 0956/1900] Remove `doc_cfg_hide` feature --- compiler/rustc_feature/src/unstable.rs | 2 -- compiler/rustc_span/src/symbol.rs | 1 - library/alloc/src/lib.rs | 2 +- library/core/src/lib.rs | 2 +- library/std/src/lib.rs | 3 ++- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 6ef0df72365ec..4dc961cb92773 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -476,8 +476,6 @@ declare_features! ( (unstable, doc_auto_cfg, "1.58.0", Some(43781)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), - /// Allows `#[doc(cfg_hide(...))]`. - (unstable, doc_cfg_hide, "1.57.0", Some(43781)), /// Allows `#[doc(masked)]`. (unstable, doc_masked, "1.21.0", Some(44027)), /// Allows features to allow target_feature to better interact with traits. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 18c3faed9329a..79f00e2e94dea 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -878,7 +878,6 @@ symbols! { doc_alias, doc_auto_cfg, doc_cfg, - doc_cfg_hide, doc_keyword, doc_masked, doc_notable_trait, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index dc5d243e8823a..24d7e6216a0dd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -207,7 +207,7 @@ // // Rustdoc features: #![feature(doc_cfg)] -#![feature(doc_cfg_hide)] +#![cfg_attr(bootstrap, feature(doc_cfg_hide))] // Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` // blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad // that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 32a3d6c7042aa..b1e559065fe49 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -161,6 +161,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(doc_cfg_hide))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -176,7 +177,6 @@ #![feature(deprecated_suggestion)] #![feature(derive_const)] #![feature(doc_cfg)] -#![feature(doc_cfg_hide)] #![feature(doc_notable_trait)] #![feature(extern_types)] #![feature(f16)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ecd354f599e4c..ff997ff7603be 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -278,6 +278,8 @@ // tidy-alphabetical-start // stabilization was reverted after it hit beta +#![cfg_attr(bootstrap, feature(doc_cfg_hide))] +#![cfg_attr(not(bootstrap), feature(autodiff))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -293,7 +295,6 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] -#![feature(doc_cfg_hide)] #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] From a7ed9bf6c7473827b7c876679a2a633facec09b8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 2 Jun 2025 22:19:33 +0200 Subject: [PATCH 0957/1900] fmt --- library/alloc/src/lib.rs | 7 +------ library/std/src/lib.rs | 6 +----- src/librustdoc/doctest/rust.rs | 8 +++----- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 24d7e6216a0dd..7499db8bf2c1d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -77,12 +77,7 @@ )] #![cfg_attr( not(bootstrap), - doc(auto_cfg(hide( - no_global_oom_handling, - no_rc, - no_sync, - target_has_atomic = "ptr" - ))) + doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr"))) )] #![doc(rust_logo)] #![feature(rustdoc_internals)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ff997ff7603be..c8fe5c55e9709 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -237,11 +237,7 @@ #![doc(rust_logo)] #![cfg_attr( bootstrap, - doc(cfg_hide( - not(test), - no_global_oom_handling, - not(no_global_oom_handling) - )) + doc(cfg_hide(not(test), no_global_oom_handling, not(no_global_oom_handling))) )] #![cfg_attr(not(bootstrap), doc(auto_cfg(hide(no_global_oom_handling))))] // Don't link to std. We are std. diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index b9367b880057f..4d3f976c2a6d5 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -119,11 +119,9 @@ impl HirCollector<'_> { nested: F, ) { let ast_attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = extract_cfg_from_attrs( - ast_attrs.iter(), - self.tcx, - &mut CfgInfo::default(), - ) && !cfg.matches(&self.tcx.sess.psess) + if let Some(ref cfg) = + extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &mut CfgInfo::default()) + && !cfg.matches(&self.tcx.sess.psess) { return; } From ec00723ba153c72124f8271b6dc15540f23e6832 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Jun 2025 13:34:41 +0200 Subject: [PATCH 0958/1900] Fix autodiff feature activation --- library/std/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c8fe5c55e9709..501c22cfb4947 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -275,7 +275,6 @@ // stabilization was reverted after it hit beta #![cfg_attr(bootstrap, feature(doc_cfg_hide))] -#![cfg_attr(not(bootstrap), feature(autodiff))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] From 254a2139f6c4b251d9210843d2e7d7952ef9f40d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 10 Jun 2025 17:44:14 +0200 Subject: [PATCH 0959/1900] Remove `cfg(bootstrap)` for `doc_cfg` feature following #141925 --- library/alloc/src/lib.rs | 17 +--------- library/core/src/lib.rs | 70 ++++++++++++---------------------------- library/std/src/lib.rs | 7 +--- 3 files changed, 23 insertions(+), 71 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7499db8bf2c1d..fc3266b74793c 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -64,21 +64,7 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr( - bootstrap, - doc(cfg_hide( - not(test), - no_global_oom_handling, - not(no_global_oom_handling), - not(no_rc), - not(no_sync), - target_has_atomic = "ptr" - )) -)] -#![cfg_attr( - not(bootstrap), - doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr"))) -)] +#![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))] #![doc(rust_logo)] #![feature(rustdoc_internals)] #![no_std] @@ -202,7 +188,6 @@ // // Rustdoc features: #![feature(doc_cfg)] -#![cfg_attr(bootstrap, feature(doc_cfg_hide))] // Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` // blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad // that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b1e559065fe49..54adf97f10020 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -51,54 +51,27 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![doc(rust_logo)] -#![cfg_attr( - bootstrap, - doc(cfg_hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_equal_alignment = "8", - target_has_atomic_equal_alignment = "16", - target_has_atomic_equal_alignment = "32", - target_has_atomic_equal_alignment = "64", - target_has_atomic_equal_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", - )) -)] -#![cfg_attr( - not(bootstrap), - doc(auto_cfg(hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_equal_alignment = "8", - target_has_atomic_equal_alignment = "16", - target_has_atomic_equal_alignment = "32", - target_has_atomic_equal_alignment = "64", - target_has_atomic_equal_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", - ))) -)] +#![doc(auto_cfg(hide( + no_fp_fmt_parse, + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64", + target_has_atomic = "8", + target_has_atomic = "16", + target_has_atomic = "32", + target_has_atomic = "64", + target_has_atomic = "ptr", + target_has_atomic_equal_alignment = "8", + target_has_atomic_equal_alignment = "16", + target_has_atomic_equal_alignment = "32", + target_has_atomic_equal_alignment = "64", + target_has_atomic_equal_alignment = "ptr", + target_has_atomic_load_store = "8", + target_has_atomic_load_store = "16", + target_has_atomic_load_store = "32", + target_has_atomic_load_store = "64", + target_has_atomic_load_store = "ptr", +)))] #![no_core] #![rustc_coherence_is_core] #![rustc_preserve_ub_checks] @@ -161,7 +134,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(doc_cfg_hide))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 501c22cfb4947..61b7d170b8ba6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -235,11 +235,7 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![doc(rust_logo)] -#![cfg_attr( - bootstrap, - doc(cfg_hide(not(test), no_global_oom_handling, not(no_global_oom_handling))) -)] -#![cfg_attr(not(bootstrap), doc(auto_cfg(hide(no_global_oom_handling))))] +#![doc(auto_cfg(hide(no_global_oom_handling)))] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind @@ -274,7 +270,6 @@ // tidy-alphabetical-start // stabilization was reverted after it hit beta -#![cfg_attr(bootstrap, feature(doc_cfg_hide))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] From 6fecff45d915b31eee3170f32fedb6db045611f1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Jul 2025 17:00:21 +0200 Subject: [PATCH 0960/1900] Fix `tests/rustdoc/target-feature.rs` test by adding missing `#![feature(doc_cfg)]` --- tests/rustdoc/target-feature.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/rustdoc/target-feature.rs b/tests/rustdoc/target-feature.rs index 59a08a0ca949e..f2686f81fbff0 100644 --- a/tests/rustdoc/target-feature.rs +++ b/tests/rustdoc/target-feature.rs @@ -1,3 +1,5 @@ +#![feature(doc_cfg)] + #![crate_name = "foo"] //@ has 'foo/index.html' From ef8b2a26caccba46584e4753c3091218d4a2e17d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 3 Jul 2025 20:05:02 +0200 Subject: [PATCH 0961/1900] Correctly handle target_feature in rustdoc cfg --- src/librustdoc/clean/types.rs | 37 +++++++++++------------------------ 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 605b77109a061..c8e9f3dc479f6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1153,35 +1153,20 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because // `doc(cfg())` overrides `cfg()`). for attr in attrs { - let Some(ident) = attr.ident() else { continue }; - match ident.name { - sym::cfg | sym::cfg_trace if !cfg_info.parent_is_doc_cfg => { - if let Some(attr) = single(attr.meta_item_list()?) - && let Ok(new_cfg) = Cfg::parse(&attr) - { - cfg_info.current_cfg &= new_cfg; - } - } + if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { // treat #[target_feature(enable = "feat")] attributes as if they were // #[doc(cfg(target_feature = "feat"))] attributes as well - sym::target_feature - if let Some(attrs) = attr.meta_item_list() => - { - for attr in attrs { - if attr.has_name(sym::enable) && attr.value_str().is_some() { - // Clone `enable = "feat"`, change to `target_feature = "feat"`. - // Unwrap is safe because `value_str` succeeded above. - let mut meta = attr.meta_item().unwrap().clone(); - meta.path = - ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - - if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { - cfg_info.current_cfg &= feat_cfg; - } - } - } + for (feature, _) in features { + cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature)); } - _ => {} + continue; + } else if !cfg_info.parent_is_doc_cfg + && let Some(ident) = attr.ident() + && matches!(ident.name, sym::cfg | sym::cfg_trace) + && let Some(attr) = single(attr.meta_item_list()?) + && let Ok(new_cfg) = Cfg::parse(&attr) + { + cfg_info.current_cfg &= new_cfg; } } From 77885fef2cd9a7f9daa4f95336f0cfdeb62b3016 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 26 Aug 2025 16:03:12 +0200 Subject: [PATCH 0962/1900] Improve code comments and extend tests for `doc_cfg` feature --- src/librustdoc/clean/types.rs | 6 ++++-- tests/rustdoc-ui/doc-cfg.rs | 2 ++ tests/rustdoc-ui/doc-cfg.stderr | 14 +++++++++++++- tests/rustdoc/doc_auto_cfg_reexports.rs | 13 +++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c8e9f3dc479f6..8b8cf19d8e418 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -925,7 +925,8 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( /// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { - /// List of `doc(auto_cfg(hide(...)))` cfgs. + /// List of currently active `doc(auto_cfg(hide(...)))` cfgs,minus currently active + /// `doc(auto_cfg(show(...)))` cfgs. hidden_cfg: FxHashSet, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. @@ -1181,7 +1182,8 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator Some(Arc::new(cfg_info.current_cfg.clone())) } } else { - // Since we always want to collect all `cfg` items, we remove the hidden ones afterward. + // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the + // hidden ones afterward. match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) { None | Some(Cfg::True) => None, Some(cfg) => Some(Arc::new(cfg)), diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index 9840c305290ae..d72643e23556b 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -13,6 +13,8 @@ #[doc(auto_cfg(hide(42)))] //~ ERROR #[doc(auto_cfg(hide("a")))] //~ ERROR #[doc(auto_cfg(hide(foo::bar)))] //~ ERROR +#[doc(auto_cfg = 42)] //~ ERROR +#[doc(auto_cfg = "a")] //~ ERROR // Shouldn't lint #[doc(auto_cfg(hide(windows)))] #[doc(auto_cfg(hide(feature = "windows")))] diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index 36ca18eed8fc8..13f62e50d907c 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -30,6 +30,18 @@ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value item LL | #[doc(auto_cfg(hide(foo::bar)))] | ^^^^^^^^ +error: `expected boolean for #[doc(auto_cfg = ...)]` + --> $DIR/doc-cfg.rs:16:7 + | +LL | #[doc(auto_cfg = 42)] + | ^^^^^^^^^^^^^ + +error: `expected boolean for #[doc(auto_cfg = ...)]` + --> $DIR/doc-cfg.rs:17:7 + | +LL | #[doc(auto_cfg = "a")] + | ^^^^^^^^^^^^^^ + warning: unexpected `cfg` condition name: `foo` --> $DIR/doc-cfg.rs:6:11 | @@ -74,5 +86,5 @@ error: multiple `cfg` predicates are specified LL | #[doc(cfg(foo, bar))] | ^^^ -error: aborting due to 9 previous errors; 2 warnings emitted +error: aborting due to 11 previous errors; 2 warnings emitted diff --git a/tests/rustdoc/doc_auto_cfg_reexports.rs b/tests/rustdoc/doc_auto_cfg_reexports.rs index f6315e9d49dde..ecfe9aabcfed4 100644 --- a/tests/rustdoc/doc_auto_cfg_reexports.rs +++ b/tests/rustdoc/doc_auto_cfg_reexports.rs @@ -20,3 +20,16 @@ mod x { // 'Available on non-crate feature pistache only.' #[cfg(not(feature = "pistache"))] pub use crate::x::B; + +// Now checking that `cfg`s are not applied on non-inlined reexports. +pub mod pub_sub_mod { + //@ has 'foo/pub_sub_mod/index.html' + // There should be only only item with `cfg` note. + //@ count - '//*[@class="stab portability"]' 1 + // And obviously the item should be "blabla". + //@ has - '//dt' 'blablaNon-pistache' + #[cfg(not(feature = "pistache"))] + pub fn blabla() {} + + pub use self::blabla as another; +} From 653e1036ed5a343a7be7b2a73096c138efc51523 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 22 Sep 2025 13:58:02 +0200 Subject: [PATCH 0963/1900] Apply first review round suggestions --- compiler/rustc_feature/src/removed.rs | 2 ++ compiler/rustc_passes/messages.ftl | 4 ++-- compiler/rustc_span/src/symbol.rs | 1 + src/librustdoc/passes/propagate_doc_cfg.rs | 14 +------------- src/librustdoc/visit_ast.rs | 2 +- tests/rustdoc-ui/doc-cfg.stderr | 6 +++--- 6 files changed, 10 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 32115535e9941..71365da316cef 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -101,6 +101,8 @@ declare_features! ( Some("never properly implemented; requires significant design work"), 127655), /// Allows deriving traits as per `SmartPointer` specification (removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284), + /// Allows `#[doc(cfg_hide(...))]`. + (removed, doc_cfg_hide, "1.57.0", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.58.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`"), 90420), diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index a5ff169e638c9..21fc1124c2f5d 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -144,7 +144,7 @@ passes_doc_cfg_hide_takes_list = `#[doc(cfg_hide(...))]` takes a list of attributes passes_doc_auto_cfg_expects_hide_or_show = - `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"` + only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` passes_doc_auto_cfg_hide_show_expects_list = `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items @@ -153,7 +153,7 @@ passes_doc_auto_cfg_hide_show_unexpected_item = `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items passes_doc_auto_cfg_wrong_literal = - `expected boolean for #[doc(auto_cfg = ...)]` + expected boolean for `#[doc(auto_cfg = ...)]` passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 79f00e2e94dea..18c3faed9329a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -878,6 +878,7 @@ symbols! { doc_alias, doc_auto_cfg, doc_cfg, + doc_cfg_hide, doc_keyword, doc_masked, doc_notable_trait, diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index ea77065ac0799..4bcb60a9a40de 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -33,19 +33,7 @@ struct CfgPropagator<'a, 'tcx> { /// Returns true if the provided `token` is a `cfg` ident. fn is_cfg_token(token: &TokenTree) -> bool { // We only keep `doc(cfg)` items. - matches!( - token, - TokenTree::Token( - Token { - kind: TokenKind::Ident( - ident, - _, - ), - .. - }, - _, - ) if *ident == sym::cfg, - ) + matches!(token, TokenTree::Token(Token { kind: TokenKind::Ident(sym::cfg, _,), .. }, _,),) } /// We only want to keep `#[cfg()]` and `#[doc(cfg())]` attributes so we rebuild a vec of diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index ac67871b0cb73..dc9889cec2193 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -9,7 +9,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{CRATE_HIR_ID, Node, find_attr}; +use rustc_hir::{Node, find_attr}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index 13f62e50d907c..49e8c324facfd 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -1,4 +1,4 @@ -error: `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"` +error: only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` --> $DIR/doc-cfg.rs:11:7 | LL | #[doc(auto_cfg(42))] @@ -30,13 +30,13 @@ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value item LL | #[doc(auto_cfg(hide(foo::bar)))] | ^^^^^^^^ -error: `expected boolean for #[doc(auto_cfg = ...)]` +error: expected boolean for `#[doc(auto_cfg = ...)]` --> $DIR/doc-cfg.rs:16:7 | LL | #[doc(auto_cfg = 42)] | ^^^^^^^^^^^^^ -error: `expected boolean for #[doc(auto_cfg = ...)]` +error: expected boolean for `#[doc(auto_cfg = ...)]` --> $DIR/doc-cfg.rs:17:7 | LL | #[doc(auto_cfg = "a")] From 9362ab549f87e535eeba12c5d42f584ff55ff886 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Sep 2025 11:33:58 +0200 Subject: [PATCH 0964/1900] Improve code and fix typo --- compiler/rustc_passes/messages.ftl | 3 -- compiler/rustc_passes/src/check_attr.rs | 41 +++++++-------------- compiler/rustc_passes/src/errors.rs | 8 ++--- compiler/rustc_span/src/symbol.rs | 1 - src/doc/rustdoc/src/unstable-features.md | 42 +++++++++++----------- src/librustdoc/clean/types.rs | 10 +++--- src/librustdoc/passes/propagate_doc_cfg.rs | 3 +- 7 files changed, 43 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 21fc1124c2f5d..df4016dfa1b82 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -140,9 +140,6 @@ passes_doc_attribute_not_attribute = nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]` .help = only existing builtin attributes are allowed in core/std -passes_doc_cfg_hide_takes_list = - `#[doc(cfg_hide(...))]` takes a list of attributes - passes_doc_auto_cfg_expects_hide_or_show = only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 88b49c781e75c..4ea237cfa0321 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1160,7 +1160,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Check that the `#![doc(auto_cfg(..))]` attribute has expected input. + /// Check that the `#![doc(auto_cfg)]` attribute has the expected input. fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) { match &meta.kind { MetaItemKind::Word => {} @@ -1176,7 +1176,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } MetaItemKind::List(list) => { for item in list { - let Some(attr_name) = item.name() else { + let Some(attr_name @ (sym::hide | sym::show)) = item.name() else { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, @@ -1185,36 +1185,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); continue; }; - if attr_name != sym::hide && attr_name != sym::show { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span, - errors::DocAutoCfgExpectsHideOrShow, - ); - } else if let Some(list) = item.meta_item_list() { + if let Some(list) = item.meta_item_list() { for item in list { - if item.meta_item_list().is_some() { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - item.span(), - errors::DocAutoCfgHideShowUnexpectedItem { - attr_name: attr_name.as_str(), - }, - ); - } else if match item { - MetaItemInner::Lit(_) => true, - // We already checked above that it's not a list. - MetaItemInner::MetaItem(meta) => meta.path.segments.len() != 1, - } { + let valid = item.meta_item().is_some_and(|meta| { + meta.path.segments.len() == 1 + && matches!( + &meta.kind, + MetaItemKind::Word | MetaItemKind::NameValue(_) + ) + }); + if !valid { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, item.span(), - errors::DocAutoCfgHideShowUnexpectedItem { - attr_name: attr_name.as_str(), - }, + errors::DocAutoCfgHideShowUnexpectedItem { attr_name }, ); } } @@ -1223,7 +1208,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { INVALID_DOC_ATTRIBUTES, hir_id, meta.span, - errors::DocAutoCfgHideShowExpectsList { attr_name: attr_name.as_str() }, + errors::DocAutoCfgHideShowExpectsList { attr_name }, ); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 1d2428c4f9a05..f0726014e0afa 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -318,14 +318,14 @@ pub(crate) struct DocAutoCfgExpectsHideOrShow; #[derive(LintDiagnostic)] #[diag(passes_doc_auto_cfg_hide_show_expects_list)] -pub(crate) struct DocAutoCfgHideShowExpectsList<'a> { - pub attr_name: &'a str, +pub(crate) struct DocAutoCfgHideShowExpectsList { + pub attr_name: Symbol, } #[derive(LintDiagnostic)] #[diag(passes_doc_auto_cfg_hide_show_unexpected_item)] -pub(crate) struct DocAutoCfgHideShowUnexpectedItem<'a> { - pub attr_name: &'a str, +pub(crate) struct DocAutoCfgHideShowUnexpectedItem { + pub attr_name: Symbol, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 18c3faed9329a..cd422da0c1c83 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -629,7 +629,6 @@ symbols! { cfg_emscripten_wasm_eh, cfg_eval, cfg_fmt_debug, - cfg_hide, cfg_overflow_checks, cfg_panic, cfg_relocation_model, diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index cfa1936f3d7fc..04d3c0cd630f5 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -720,9 +720,9 @@ pass `--doctest-build-arg ARG` for each argument `ARG`. This flag enables the generation of toggles to expand macros in the HTML source code pages. -## `#[doc(cfg)]` +## `#[doc(cfg)]` and `#[doc(auto_cfg)]` -This feature aims at providing rustdoc users the possibility to add visual markers to the rendered documentation to know under which conditions an item is available (currently possible through the following unstable features: `doc_cfg`, `doc_auto_cfg` and `doc_cfg_hide`). +This feature aims at providing rustdoc users the possibility to add visual markers to the rendered documentation to know under which conditions an item is available (currently possible through the following unstable feature: `doc_cfg`). It does not aim to allow having a same item with different `cfg`s to appear more than once in the generated documentation. @@ -736,25 +736,6 @@ This features adds the following attributes: All of these attributes can be added to a module or to the crate root, and they will be inherited by the child items unless another attribute overrides it. This is why "opposite" attributes like `auto_cfg(hide(...))` and `auto_cfg(show(...))` are provided: they allow a child item to override its parent. -### `#[doc(auto_cfg)`/`#[doc(auto_cfg = true)]`/`#[doc(auto_cfg = false)]` - -By default, `#[doc(auto_cfg)]` is enabled at the crate-level. When it's enabled, Rustdoc will automatically display `cfg(...)` compatibility information as-if the same `#[doc(cfg(...))]` had been specified. - -This attribute impacts the item on which it is used and its descendants. - -So if we take back the previous example: - -```rust -#[cfg(feature = "futures-io")] -pub mod futures {} -``` - -There's no need to "duplicate" the `cfg` into a `doc(cfg())` to make Rustdoc display it. - -In some situations, the detailed conditional compilation rules used to implement the feature might not serve as good documentation (for example, the list of supported platforms might be very long, and it might be better to document them in one place). To turn it off, add the `#[doc(auto_cfg = false)]` attribute on the item. - -If no argument is specified (ie `#[doc(auto_cfg)]`), it's the same as writing `#[doc(auto_cfg = true)]`. - ### `#[doc(cfg(...))]` This attribute provides a standardized format to override `#[cfg()]` attributes to document conditionally available items. Example: @@ -927,6 +908,25 @@ Using this attribute will re-enable `auto_cfg` if it was disabled at this locati pub fn foo() {} ``` +### `#[doc(auto_cfg)`/`#[doc(auto_cfg = true)]`/`#[doc(auto_cfg = false)]` + +By default, `#[doc(auto_cfg)]` is enabled at the crate-level. When it's enabled, Rustdoc will automatically display `cfg(...)` compatibility information as-if the same `#[doc(cfg(...))]` had been specified. + +This attribute impacts the item on which it is used and its descendants. + +So if we take back the previous example: + +```rust +#[cfg(feature = "futures-io")] +pub mod futures {} +``` + +There's no need to "duplicate" the `cfg` into a `doc(cfg())` to make Rustdoc display it. + +In some situations, the detailed conditional compilation rules used to implement the feature might not serve as good documentation (for example, the list of supported platforms might be very long, and it might be better to document them in one place). To turn it off, add the `#[doc(auto_cfg = false)]` attribute on the item. + +If no argument is specified (ie `#[doc(auto_cfg)]`), it's the same as writing `#[doc(auto_cfg = true)]`. + ## Inheritance Rustdoc merges `cfg` attributes from parent modules to its children. For example, in this case, the module `non_unix` will describe the entire compatibility matrix for the module, and not just its directly attached information: diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 8b8cf19d8e418..c2cf39c4be06e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -925,7 +925,7 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( /// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { - /// List of currently active `doc(auto_cfg(hide(...)))` cfgs,minus currently active + /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. hidden_cfg: FxHashSet, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while @@ -942,13 +942,11 @@ pub(crate) struct CfgInfo { impl Default for CfgInfo { fn default() -> Self { Self { - hidden_cfg: [ + hidden_cfg: FxHashSet::from_iter([ Cfg::Cfg(sym::test, None), Cfg::Cfg(sym::doc, None), Cfg::Cfg(sym::doctest, None), - ] - .into_iter() - .collect(), + ]), current_cfg: Cfg::True, auto_cfg_active: true, parent_is_doc_cfg: false, @@ -990,7 +988,7 @@ fn handle_auto_cfg_hide_show( && let MetaItemKind::List(items) = &item.kind { for item in items { - // Cfg parsing errors should already have been reported in `rustc_passes::check_attr`. + // FIXME: Report in case `Cfg::parse` reports an error? if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) { if is_show { if let Some(span) = new_hide_attrs.get(&(key, value)) { diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 4bcb60a9a40de..d5b20f2b9410c 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -97,9 +97,8 @@ impl CfgPropagator<'_, '_> { // // Otherwise, `cfg_info` already tracks everything we need so nothing else to do! if matches!(item.kind, ItemKind::ImplItem(_)) - && let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) + && let Some(mut next_def_id) = item.item_id.as_local_def_id() { - let mut next_def_id = def_id; while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { let x = load_attrs(self.cx, parent_def_id.to_def_id()); add_only_cfg_attributes(&mut attrs, x); From 6cccea8731c5f8540d65c2458a36655a4b8793f3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Sep 2025 14:46:04 +0200 Subject: [PATCH 0965/1900] Remove `doc_auto_cfg` feature as well --- compiler/rustc_feature/src/removed.rs | 2 ++ compiler/rustc_feature/src/unstable.rs | 2 -- tests/rustdoc-gui/src/lib2/lib.rs | 1 - tests/rustdoc/doc-cfg/doc-cfg-implicit.rs | 2 +- tests/rustdoc/reexport/reexport-cfg.rs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 71365da316cef..3c51f91331a6a 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -101,6 +101,8 @@ declare_features! ( Some("never properly implemented; requires significant design work"), 127655), /// Allows deriving traits as per `SmartPointer` specification (removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284), + /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. + (removed, doc_auto_cfg, "1.58.0", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows `#[doc(cfg_hide(...))]`. (removed, doc_cfg_hide, "1.57.0", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows using `#[doc(keyword = "...")]`. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4dc961cb92773..b286ae3eb23ba 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -472,8 +472,6 @@ declare_features! ( (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Allows deriving the From trait on single-field structs. (unstable, derive_from, "1.91.0", Some(144889)), - /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. - (unstable, doc_auto_cfg, "1.58.0", Some(43781)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs index 8db754f91ce61..400488cbe8570 100644 --- a/tests/rustdoc-gui/src/lib2/lib.rs +++ b/tests/rustdoc-gui/src/lib2/lib.rs @@ -1,7 +1,6 @@ // ignore-tidy-linelength #![feature(doc_cfg)] -#![feature(doc_auto_cfg)] pub mod another_folder; pub mod another_mod; diff --git a/tests/rustdoc/doc-cfg/doc-cfg-implicit.rs b/tests/rustdoc/doc-cfg/doc-cfg-implicit.rs index 69b10867ee3ac..c092675ee5cf9 100644 --- a/tests/rustdoc/doc-cfg/doc-cfg-implicit.rs +++ b/tests/rustdoc/doc-cfg/doc-cfg-implicit.rs @@ -1,5 +1,5 @@ #![crate_name = "funambulism"] -#![feature(doc_auto_cfg, doc_cfg)] +#![feature(doc_cfg)] //@ has 'funambulism/struct.Disorbed.html' //@ count - '//*[@class="stab portability"]' 1 diff --git a/tests/rustdoc/reexport/reexport-cfg.rs b/tests/rustdoc/reexport/reexport-cfg.rs index 73b6682431663..b624e5acf502e 100644 --- a/tests/rustdoc/reexport/reexport-cfg.rs +++ b/tests/rustdoc/reexport/reexport-cfg.rs @@ -2,7 +2,7 @@ // include `cfg`s from the previous chained items. #![crate_name = "foo"] -#![feature(doc_auto_cfg, doc_cfg)] +#![feature(doc_cfg)] mod foo { #[cfg(not(feature = "foo"))] From 9e984cdfde19d4235bab070c28870d907af3a4c2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Sep 2025 12:31:42 +0200 Subject: [PATCH 0966/1900] alloc_addresses: track more explicitly whether we are in charge of generating addresses --- src/tools/miri/src/alloc_addresses/mod.rs | 79 ++++++++++++----------- src/tools/miri/src/machine.rs | 5 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index f011ee717850d..b3ea51bec6fcd 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -42,20 +42,19 @@ pub struct GlobalStateInner { /// they do not have an `AllocExtra`. /// This is the inverse of `int_to_ptr_map`. base_addr: FxHashMap, - /// Temporarily store prepared memory space for global allocations the first time their memory - /// address is required. This is used to ensure that the memory is allocated before Miri assigns - /// it an internal address, which is important for matching the internal address to the machine - /// address so FFI can read from pointers. - prepared_alloc_bytes: FxHashMap, - /// A pool of addresses we can reuse for future allocations. - reuse: ReusePool, - /// Whether an allocation has been exposed or not. This cannot be put + /// The set of exposed allocations. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// The generator for new addresses in a given range. - address_generator: AddressGenerator, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, + /// The generator for new addresses in a given range, and a pool for address reuse. This is + /// `None` if addresses are generated elsewhere (in native-lib mode or with GenMC). + address_generation: Option<(AddressGenerator, ReusePool)>, + /// Native-lib mode only: Temporarily store prepared memory space for global allocations the + /// first time their memory address is required. This is used to ensure that the memory is + /// allocated before Miri assigns it an internal address, which is important for matching the + /// internal address to the machine address so FFI can read from pointers. + prepared_alloc_bytes: Option>, } impl VisitProvenance for GlobalStateInner { @@ -64,9 +63,8 @@ impl VisitProvenance for GlobalStateInner { int_to_ptr_map: _, base_addr: _, prepared_alloc_bytes: _, - reuse: _, exposed: _, - address_generator: _, + address_generation: _, provenance_mode: _, } = self; // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. @@ -83,11 +81,16 @@ impl GlobalStateInner { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - prepared_alloc_bytes: FxHashMap::default(), - reuse: ReusePool::new(config), exposed: FxHashSet::default(), - address_generator: AddressGenerator::new(stack_addr..tcx.target_usize_max()), provenance_mode: config.provenance_mode, + address_generation: (config.native_lib.is_empty() && config.genmc_config.is_none()) + .then(|| { + ( + AddressGenerator::new(stack_addr..tcx.target_usize_max()), + ReusePool::new(config), + ) + }), + prepared_alloc_bytes: (!config.native_lib.is_empty()).then(FxHashMap::default), } } @@ -147,6 +150,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Store prepared allocation to be picked up for use later. global_state .prepared_alloc_bytes + .as_mut() + .unwrap() .try_insert(alloc_id, prepared_bytes) .unwrap(); ptr @@ -173,29 +178,25 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`. return interp_ok(base_ptr.addr().to_u64()); } - // We are not in native lib mode, so we control the addresses ourselves. + // We are not in native lib or genmc mode, so we control the addresses ourselves. + let (addr_gen, reuse) = global_state.address_generation.as_mut().unwrap(); let mut rng = this.machine.rng.borrow_mut(); - if let Some((reuse_addr, clock)) = global_state.reuse.take_addr( - &mut *rng, - info.size, - info.align, - memory_kind, - this.active_thread(), - ) { + if let Some((reuse_addr, clock)) = + reuse.take_addr(&mut *rng, info.size, info.align, memory_kind, this.active_thread()) + { if let Some(clock) = clock { this.acquire_clock(&clock)?; } interp_ok(reuse_addr) } else { // We have to pick a fresh address. - let new_addr = - global_state.address_generator.generate(info.size, info.align, &mut rng)?; + let new_addr = addr_gen.generate(info.size, info.align, &mut rng)?; // If we filled up more than half the address space, start aggressively reusing // addresses to avoid running out. - let remaining_range = global_state.address_generator.get_remaining(); + let remaining_range = addr_gen.get_remaining(); if remaining_range.start > remaining_range.end / 2 { - global_state.reuse.address_space_shortage(); + reuse.address_space_shortage(); } interp_ok(new_addr) @@ -414,6 +415,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut global_state = this.machine.alloc_addresses.borrow_mut(); let mut prepared_alloc_bytes = global_state .prepared_alloc_bytes + .as_mut() + .unwrap() .remove(&id) .unwrap_or_else(|| panic!("alloc bytes for {id:?} have not been prepared")); // Sanity-check that the prepared allocation has the right size and alignment. @@ -496,15 +499,17 @@ impl<'tcx> MiriMachine<'tcx> { // `alloc_id_from_addr` any more. global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - let thread = self.threads.active_thread(); - global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { - // We already excluded GenMC above. We cannot use `self.release_clock` as - // `self.alloc_addresses` is borrowed. - if let Some(data_race) = self.data_race.as_vclocks_ref() { - data_race.release_clock(&self.threads, |clock| clock.clone()) - } else { - VClock::default() - } - }) + if let Some((_addr_gen, reuse)) = global_state.address_generation.as_mut() { + let thread = self.threads.active_thread(); + reuse.add_addr(rng, addr, size, align, kind, thread, || { + // We cannot be in GenMC mode as then `address_generation` is `None`. We cannot use + // `self.release_clock` as `self.alloc_addresses` is borrowed. + if let Some(data_race) = self.data_race.as_vclocks_ref() { + data_race.release_clock(&self.threads, |clock| clock.clone()) + } else { + VClock::default() + } + }) + } } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 04c8bee72c038..af8b40da14ade 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -777,9 +777,8 @@ impl<'tcx> MiriMachine<'tcx> { local_crates, extern_statics: FxHashMap::default(), rng: RefCell::new(rng), - allocator: if !config.native_lib.is_empty() { - Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))) - } else { None }, + allocator: (!config.native_lib.is_empty()) + .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))), tracked_alloc_ids: config.tracked_alloc_ids.clone(), track_alloc_accesses: config.track_alloc_accesses, check_alignment: config.check_alignment, From 660a3486fc6da2c3599a167d73d849178f98bc17 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:37:29 +0200 Subject: [PATCH 0967/1900] Skip stack overflow handler for panic=immediate-abort std installs guard pages and a signal handler to ensure that stackoverflows 1) terminate abruptly and 2) print an nice message. Even for panic=immediate-abort, 1) is desirable, we don't want silent data corruption there. But 2) is completely unnecessary, as users deliberately *don't* want nice messages, they want minimum binary size. Therefore, skip the entire guard signal handler setup, which saves a lot of bytes. I tested this with a hello world binary using fat LTO, build-std, panic=immediate-abort, opt-level=s, strip=debuginfo. `size` reports significant savings: ``` text data bss dec hex filename 15252 1032 104 16388 4004 tiny-before 6881 964 48 7893 1ed5 tiny-after2 ``` `nm -U` goes from 71 to 56, getting rid of a bunch of stack overflow related symbols. The disk size goes from `31k` to `24k`. The impact on the error message is minimal, as the message was already missing. before: ``` fish: Job 1, './tiny-so-before' terminated by signal SIGABRT (Abort) ``` after: ``` fish: Job 1, './tiny-so-after' terminated by signal SIGSEGV (Address boundary error) ``` --- library/std/src/sys/pal/unix/stack_overflow.rs | 12 +++++++++++- library/std/src/sys/pal/windows/mod.rs | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0d2100d66bc09..08a3cee64bab2 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -148,6 +148,13 @@ mod imp { let mut guard_page_range = unsafe { install_main_guard() }; + // Even for panic=immediate-abort, installing the guard pages is important for soundness. + // That said, we do not care about giving nice stackoverflow messages via our custom + // signal handler, just exit early and let the user enjoy the segfault. + if cfg!(panic = "immediate-abort") { + return; + } + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { @@ -179,6 +186,9 @@ mod imp { /// Must be called only once #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn cleanup() { + if cfg!(panic = "immediate-abort") { + return; + } // FIXME: I probably cause more bugs than I'm worth! // see https://github.com/rust-lang/rust/issues/111272 unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; @@ -230,7 +240,7 @@ mod imp { /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool, thread_name: Option>) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Acquire) { + if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3357946b8f71d..b7578b01584bf 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,7 +22,8 @@ pub mod os; pub mod pipe; pub mod time; cfg_select! { - not(target_vendor = "uwp") => { + // We don't care about printing nice error messages for panic=immediate-abort + all(not(target_vendor = "uwp"), not(panic = "immediate-abort")) => { pub mod stack_overflow; } _ => { From 07ed247d3c28670449f142b54b025fc429ac318a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 27 Sep 2025 14:40:38 +0200 Subject: [PATCH 0968/1900] Ignore crash test that doesn't crash on Apple platforms This wasn't caught by CI, because debug assertions aren't enabled there. --- tests/crashes/120175.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/crashes/120175.rs b/tests/crashes/120175.rs index e441454bed294..e06da5a8e0ae0 100644 --- a/tests/crashes/120175.rs +++ b/tests/crashes/120175.rs @@ -1,5 +1,6 @@ //@ known-bug: #120175 //@ needs-rustc-debug-assertions +//@ ignore-apple (raw-dylib doesn't work on Apple targets yet) #![feature(extern_types)] #![feature(raw_dylib_elf)] From d42acf522ff234e187aef2de8c6903bc94efe444 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 7 Jun 2025 03:24:35 +0000 Subject: [PATCH 0969/1900] Include additional hashes in src/stage0 This patch changes `bump-stage0` to include: * The sha256 hash of the channel manifest used to create `src/stage0`. * The rust and rustfmt git commit in `src/stage0`. * Hashes of all the artifacts, like the source tarball, in `src/stage0`. Combined this will allow for: * Projects that bootstrap their own compiler, such as Fuchsia, or users of [bootstrap], to build their compilers offline without needing to communicate with static.rust-lang.org. * Auditors to detect if the channel manifest, and all the artifacts inside the manifest, were modified after it was used to generate `src/stage0`. Furthermore, if they did find modified artifacts, they could determine if the Rust Signing Key was compromised by checking if any modified file was signed properly. Finally, it allows regeneration of `src/stage0` when specifying both the day of the build for rust, and the day of the build for rustfmt, which can allow a maintainer to regenerate `src/stage0` to verify nothing changed. [bootstrap]: https://github.com/dtolnay/bootstrap [mrustc]: https://github.com/thepowersgang/mrustc --- Cargo.lock | 2 + src/bootstrap/src/core/download.rs | 2 +- src/build_helper/src/stage0_parser.rs | 14 ++++ src/tools/bump-stage0/Cargo.toml | 2 + src/tools/bump-stage0/src/main.rs | 97 ++++++++++++++++++++++++--- 5 files changed, 105 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d4a1bf6a7880..715d580e051d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,8 +334,10 @@ dependencies = [ "anyhow", "build_helper", "curl", + "hex", "indexmap", "serde", + "sha2", "toml 0.8.23", ] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 37871f0fe1e28..a096d116e73fa 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -506,7 +506,7 @@ pub(crate) fn maybe_download_rustfmt<'a>( return Some(PathBuf::new()); } - let VersionMetadata { date, version } = dwn_ctx.stage0_metadata.rustfmt.as_ref()?; + let VersionMetadata { date, version, .. } = dwn_ctx.stage0_metadata.rustfmt.as_ref()?; let channel = format!("{version}-{date}"); let host = dwn_ctx.host_target; diff --git a/src/build_helper/src/stage0_parser.rs b/src/build_helper/src/stage0_parser.rs index 2723f4aa7b914..3f3297dcd2bb1 100644 --- a/src/build_helper/src/stage0_parser.rs +++ b/src/build_helper/src/stage0_parser.rs @@ -10,6 +10,8 @@ pub struct Stage0 { #[derive(Default, Clone)] pub struct VersionMetadata { + pub channel_manifest_hash: String, + pub git_commit_hash: String, pub date: String, pub version: String, } @@ -50,9 +52,21 @@ pub fn parse_stage0_file() -> Stage0 { "git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(), "nightly_branch" => stage0.config.nightly_branch = value.to_owned(), + "compiler_channel_manifest_hash" => { + stage0.compiler.channel_manifest_hash = value.to_owned() + } + "compiler_git_commit_hash" => stage0.compiler.git_commit_hash = value.to_owned(), "compiler_date" => stage0.compiler.date = value.to_owned(), "compiler_version" => stage0.compiler.version = value.to_owned(), + "rustfmt_channel_manifest_hash" => { + stage0.rustfmt.get_or_insert(VersionMetadata::default()).channel_manifest_hash = + value.to_owned(); + } + "rustfmt_git_commit_hash" => { + stage0.rustfmt.get_or_insert(VersionMetadata::default()).git_commit_hash = + value.to_owned(); + } "rustfmt_date" => { stage0.rustfmt.get_or_insert(VersionMetadata::default()).date = value.to_owned(); } diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml index 79097f2c18916..943b8453ef850 100644 --- a/src/tools/bump-stage0/Cargo.toml +++ b/src/tools/bump-stage0/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" anyhow = "1.0.34" build_helper = { path = "../../build_helper" } curl = "0.4.38" +hex = "0.4.3" indexmap = { version = "2.0.0", features = ["serde"] } serde = { version = "1.0.125", features = ["derive"] } toml = "0.8.23" +sha2 = "0.10.1" diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index faed748785f46..079e7b1ce71ee 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -4,6 +4,7 @@ use anyhow::{Context, Error}; use build_helper::stage0_parser::{Stage0Config, VersionMetadata, parse_stage0_file}; use curl::easy::Easy; use indexmap::IndexMap; +use sha2::{Digest, Sha256}; const PATH: &str = "src/stage0"; const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo", "clippy-preview"]; @@ -13,13 +14,14 @@ struct Tool { config: Stage0Config, channel: Channel, - date: Option, + compiler_date: Option, + rustfmt_date: Option, version: [u16; 3], checksums: IndexMap, } impl Tool { - fn new(date: Option) -> Result { + fn new(compiler_date: Option, rustfmt_date: Option) -> Result { let channel = match std::fs::read_to_string("src/ci/channel")?.trim() { "stable" => Channel::Stable, "beta" => Channel::Beta, @@ -38,7 +40,14 @@ impl Tool { let existing = parse_stage0_file(); - Ok(Self { channel, version, date, config: existing.config, checksums: IndexMap::new() }) + Ok(Self { + channel, + version, + compiler_date, + rustfmt_date, + config: existing.config, + checksums: IndexMap::new(), + }) } fn update_stage0_file(mut self) -> Result<(), Error> { @@ -78,10 +87,21 @@ impl Tool { file_content.push_str("\n"); let compiler = self.detect_compiler()?; + file_content.push_str(&format!( + "compiler_channel_manifest_hash={}\n", + compiler.channel_manifest_hash + )); + file_content.push_str(&format!("compiler_git_commit_hash={}\n", compiler.git_commit_hash)); file_content.push_str(&format!("compiler_date={}\n", compiler.date)); file_content.push_str(&format!("compiler_version={}\n", compiler.version)); if let Some(rustfmt) = self.detect_rustfmt()? { + file_content.push_str(&format!( + "rustfmt_channel_manifest_hash={}\n", + rustfmt.channel_manifest_hash + )); + file_content + .push_str(&format!("rustfmt_git_commit_hash={}\n", rustfmt.git_commit_hash)); file_content.push_str(&format!("rustfmt_date={}\n", rustfmt.date)); file_content.push_str(&format!("rustfmt_version={}\n", rustfmt.version)); } @@ -112,9 +132,16 @@ impl Tool { Channel::Nightly => "beta".to_string(), }; - let manifest = fetch_manifest(&self.config, &channel, self.date.as_deref())?; + let (manifest, manifest_hash) = + fetch_manifest(&self.config, &channel, self.compiler_date.as_deref())?; self.collect_checksums(&manifest, COMPILER_COMPONENTS)?; Ok(VersionMetadata { + channel_manifest_hash: manifest_hash, + git_commit_hash: manifest.pkg["rust"] + .git_commit_hash + .as_ref() + .expect("invalid git_commit_hash") + .into(), date: manifest.date, version: if self.channel == Channel::Nightly { "beta".to_string() @@ -138,9 +165,19 @@ impl Tool { return Ok(None); } - let manifest = fetch_manifest(&self.config, "nightly", self.date.as_deref())?; + let (manifest, manifest_hash) = + fetch_manifest(&self.config, "nightly", self.rustfmt_date.as_deref())?; self.collect_checksums(&manifest, RUSTFMT_COMPONENTS)?; - Ok(Some(VersionMetadata { date: manifest.date, version: "nightly".into() })) + Ok(Some(VersionMetadata { + channel_manifest_hash: manifest_hash, + git_commit_hash: manifest.pkg["rust"] + .git_commit_hash + .as_ref() + .expect("invalid git_commit_hash") + .into(), + date: manifest.date, + version: "nightly".into(), + })) } fn collect_checksums(&mut self, manifest: &Manifest, components: &[&str]) -> Result<(), Error> { @@ -164,12 +201,29 @@ impl Tool { } } } + for artifact in manifest.artifacts.values() { + for targets in artifact.target.values() { + for target in targets { + let url = target + .url + .strip_prefix(&prefix) + .ok_or_else(|| { + anyhow::anyhow!( + "url doesn't start with dist server base: {}", + target.url + ) + })? + .to_string(); + self.checksums.insert(url, target.hash_sha256.clone()); + } + } + } Ok(()) } } fn main() -> Result<(), Error> { - let tool = Tool::new(std::env::args().nth(1))?; + let tool = Tool::new(std::env::args().nth(1), std::env::args().nth(2))?; tool.update_stage0_file()?; Ok(()) } @@ -178,18 +232,24 @@ fn fetch_manifest( config: &Stage0Config, channel: &str, date: Option<&str>, -) -> Result { +) -> Result<(Manifest, String), Error> { let url = if let Some(date) = date { format!("{}/dist/{}/channel-rust-{}.toml", config.dist_server, date, channel) } else { format!("{}/dist/channel-rust-{}.toml", config.dist_server, channel) }; + let manifest_bytes = http_get(&url)?; + + let mut sha256 = Sha256::new(); + sha256.update(&manifest_bytes); + let manifest_hash = hex::encode(sha256.finalize()); + // FIXME: on newer `toml` (>= `0.9.*`), use `toml::from_slice`. For now, we use the most recent // `toml` available in-tree which is `0.8.*`, so we have to do an additional dance here. - let response = http_get(&url)?; - let response = String::from_utf8(response)?; - Ok(toml::from_str(&response)?) + let manifest_str = String::from_utf8(manifest_bytes)?; + let manifest = toml::from_str(&manifest_str)?; + Ok((manifest, manifest_hash)) } fn http_get(url: &str) -> Result, Error> { @@ -219,11 +279,14 @@ enum Channel { struct Manifest { date: String, pkg: IndexMap, + artifacts: IndexMap, } #[derive(Debug, serde::Serialize, serde::Deserialize)] struct ManifestPackage { version: String, + #[serde(default)] + git_commit_hash: Option, target: IndexMap, } @@ -234,3 +297,15 @@ struct ManifestTargetPackage { xz_url: Option, xz_hash: Option, } + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct ManifestArtifact { + target: IndexMap>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct ManifestTargetArtifact { + url: String, + hash_sha256: String, +} From 76bb0d1870b0023d6184ee4b714e2cf4ad1d96d4 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 27 Sep 2025 08:45:40 -0400 Subject: [PATCH 0970/1900] Update stage0 per previous commmit --- src/stage0 | 1022 +++++++++++++++++++++++++++------------------------- 1 file changed, 522 insertions(+), 500 deletions(-) diff --git a/src/stage0 b/src/stage0 index 6d2f0402f4acf..556ced41d3da8 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,506 +13,528 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2025-09-16 +compiler_channel_manifest_hash=55f3014ea3a3b17cfa5060d9188fd13d13b065456661a3d670dd061719713f32 +compiler_git_commit_hash=bb624dcb4c8ab987e10c0808d92d76f3b84dd117 +compiler_date=2025-09-21 compiler_version=beta -rustfmt_date=2025-09-16 +rustfmt_channel_manifest_hash=2f19b9b74a17d0283264a227ed552d7df6729929fcb73b2d5bb321feed8a5c85 +rustfmt_git_commit_hash=54a8a1db604e4caff93e26e167ad4a6fde9f0681 +rustfmt_date=2025-09-27 rustfmt_version=nightly -dist/2025-09-16/rustc-beta-aarch64-apple-darwin.tar.gz=3e3fbcf51f2632b8890b38537ea5446147b85fd62b21798b4fb2a032a4abd6c9 -dist/2025-09-16/rustc-beta-aarch64-apple-darwin.tar.xz=296ef2ae4dc8b4f411423a4384821e5b8e305be809b55522a0f02c97b83ce14a -dist/2025-09-16/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=fe7026f1310a84f3490d0ffb8476bddc36992fcb2d7e450c036aae6bbbb218ef -dist/2025-09-16/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=70bd356d7f39ff1ef8511941671caae84f43d3a9ab493ed131467d27bf5c53f1 -dist/2025-09-16/rustc-beta-aarch64-pc-windows-msvc.tar.gz=9b0f42d2713c20d1df9e401410f874f04ffbff2e0c61d19d2f8689d4e55da562 -dist/2025-09-16/rustc-beta-aarch64-pc-windows-msvc.tar.xz=086ff4c532289581325b1cb5c629f899418dd392249d3c0bd2137a0d73ebccca -dist/2025-09-16/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=4860148593cc5f5662cb89c9484d71af472223b8a9f9644e6709802154e7d02d -dist/2025-09-16/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=a7f290455c134c72518267e7dfc5a51d550497ffcf539dac3b41c57486e1d370 -dist/2025-09-16/rustc-beta-aarch64-unknown-linux-musl.tar.gz=7a17d7a65ca7c933de8829e5991f991d917df7007c122fc55d64efa0fe53344a -dist/2025-09-16/rustc-beta-aarch64-unknown-linux-musl.tar.xz=6ab69fc25888475048dc5eac7adc7f6b02295dc7de7d0e92aad5ff562c500322 -dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=00ff0a967ee41642e6b316b190485a7d04c6749ff571e55cf73548b0e7d74b05 -dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=ce2c11e633e367544a70b0a3ad998a2d680277e62ece3eacbf350fa1ada6332a -dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=ded847d958f0417fba13e71ded42cee387387076b4426b2f2002d53dd998c27c -dist/2025-09-16/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=54ee1d25f57db5d1c2b153533362ecb7fd326b8dd46b8240367ea865a059ca68 -dist/2025-09-16/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=8f964d41f6608ba04d2d84f5532a057654f3f328b19bc6ee4b9a88f6f64dcec7 -dist/2025-09-16/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=b6a34332cb027345f54f75e03cee1c65345e8671df081e8dd23111d2233bf6e6 -dist/2025-09-16/rustc-beta-i686-pc-windows-gnu.tar.gz=760c5f1bc081a14b4df2e0c6a1ce80d96b63e15e0b751cfe535eb5775aa9e34b -dist/2025-09-16/rustc-beta-i686-pc-windows-gnu.tar.xz=4376609352714e1e30a4b0c57fe43beb2b0edccda412e18e11c3b8b3092a87d0 -dist/2025-09-16/rustc-beta-i686-pc-windows-msvc.tar.gz=3d0f08e30f4ae2cb513548f66cc6405ba0c2cc74e826c04cf0bdad3d3c14bcfa -dist/2025-09-16/rustc-beta-i686-pc-windows-msvc.tar.xz=a68b01fb491496bb2b8861af9b900a2960f4ea9f64f0946caa09c043ecb3df2f -dist/2025-09-16/rustc-beta-i686-unknown-linux-gnu.tar.gz=3b56ab8e485b3e4bd5c0f1e1ff30389acb9d0d0e0662ff834fb3a855157c965a -dist/2025-09-16/rustc-beta-i686-unknown-linux-gnu.tar.xz=fe871e45e0d987dcaa6a30df8394c54da0204965686a089d6903fff75b8a01e5 -dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=71d53cb70f1962379614c86b07fdea7c1b343587a56734d39aa7de29667d6629 -dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=658f204ab2536ad3b56f238c55c5c6cf3bd7794aceff379f0f5492d28841b5bc -dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=0fe154b5b305259a36b80b9b53894336ceba1562155c05cf92debc5d7bb7e645 -dist/2025-09-16/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=27357f4331d5c357a7b20002571276e2d77e58384095fd3a6910d44a0d85aa87 -dist/2025-09-16/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=bf87cb1717fad96abbb86091c44de6bdeed2bd60437ffa9037d27dda1c71cdbe -dist/2025-09-16/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=778d126b47dc5ef07085c5c760399130219cda6221f6f4ec57c26ebdd7e33299 -dist/2025-09-16/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=eae03019370901fb49b708c76d001ec9e3df27e7fa1ea9ceb2200640e9d6dfea -dist/2025-09-16/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=61c1b21acc668bdf36cbf64f2b53348d997ffc9997c6ad282d10226e86740044 -dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=057eb2050b31110529a80e27c6df0c95a14bb1f7c568b487dd3a718755a74d27 -dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=cb3078b44dbb6793ac98a28bd6bd7076c28ae13c5ddd1aa02310ec89130b6575 -dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=9a5109124546788b802be36d9415bf0d5bfcc087389156c65a2e30672e10e7b1 -dist/2025-09-16/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=6987de797339aa2f231485897b0a498a841a49f1d0b83fa7e98c0a76767cbe44 -dist/2025-09-16/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=17cd423d53f6d8fe53dff7abada43d545a2de14136eae7cd02b958f9fe93f5a0 -dist/2025-09-16/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=740945fe14eeea917d64ff47e997ca17be1e0f14dca30632a9fbfb560405b040 -dist/2025-09-16/rustc-beta-s390x-unknown-linux-gnu.tar.gz=52e15e3a56a16b2989b34f0a051ff06fb8dd51be3e6874c41a4ba1e2128d35ff -dist/2025-09-16/rustc-beta-s390x-unknown-linux-gnu.tar.xz=6f998aede2a805d8159f2ea63dda76a7d25eb9cdbd26863394aacfa6d80de084 -dist/2025-09-16/rustc-beta-sparcv9-sun-solaris.tar.gz=45c85ec3de0a773b2c2f4b624abe28c3a906020e1ce6ceded75c0099a1fc1899 -dist/2025-09-16/rustc-beta-sparcv9-sun-solaris.tar.xz=d811ff717ebc59aa9bc5c26ee4b8dcfdb3d2f5a1759179eccf549c8a765fb3dd -dist/2025-09-16/rustc-beta-x86_64-apple-darwin.tar.gz=d31302c29a9efe362d2be87b83b229d5369b2462cea133f9b1b4c7c4cf0f26dd -dist/2025-09-16/rustc-beta-x86_64-apple-darwin.tar.xz=b8f9f0b2e4c681da9bf7fbac2fc22ec2f44c136b368821bfea5a5ab29d85527a -dist/2025-09-16/rustc-beta-x86_64-pc-solaris.tar.gz=498902a6de8cae1494aae6223604381c4e109a38c14649f6e27b075d3f26a685 -dist/2025-09-16/rustc-beta-x86_64-pc-solaris.tar.xz=1cfaeae163fcd4dd2ff44a80c245012576ecb4257d7d763a51f2d7e58878a29b -dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnu.tar.gz=d4c6fa5158c83500d30ef44e129889408dbc49ec862c985a1c36820d3550e94d -dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnu.tar.xz=17bd90bfd6603b24d26397acac55e4b2c23b0dc574834de7bc1f8c2c720aab97 -dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=d270c52fec5ef2e9f009cc3e697b6188320fbeb5445092c7a11af67e520cc9f0 -dist/2025-09-16/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=81f91fc07821af63606268821a039578a7c9b09b8c0e96b85163831fee92e6a5 -dist/2025-09-16/rustc-beta-x86_64-pc-windows-msvc.tar.gz=233356bbd9a46168663752d7dcab67e7ae3ae732fab410bc8adb84230e22398f -dist/2025-09-16/rustc-beta-x86_64-pc-windows-msvc.tar.xz=5f33d8b3a064235c23dfd2edfa8b5f6f2c5075d26529f81881501b8323eae60d -dist/2025-09-16/rustc-beta-x86_64-unknown-freebsd.tar.gz=fda4fa10690458ee7db6081267458e47113e854d65695c95813039400772e263 -dist/2025-09-16/rustc-beta-x86_64-unknown-freebsd.tar.xz=570cd878e440b5d70a0d5fc90917b92a6b8b544a2e6844baafee1fef22d32732 -dist/2025-09-16/rustc-beta-x86_64-unknown-illumos.tar.gz=f8b7273e16de291fad24f2ebbd928b55007a75605fc25ddc35a4967f17e709f6 -dist/2025-09-16/rustc-beta-x86_64-unknown-illumos.tar.xz=77d7f5de006a9d0c56a2cf79423a5702bff445f6e34b4935326d3db30ac8b086 -dist/2025-09-16/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=f61a57a75252d63930630af7d597f3046b7f7f09691cd6ff8c3a080871fa19de -dist/2025-09-16/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=b56298c4ba7a955ecb9135bba06ff6bdb7f4df94b6d64c99460e90e5a022fb87 -dist/2025-09-16/rustc-beta-x86_64-unknown-linux-musl.tar.gz=c6fec77bf71bf35ded56cf70eca28a94e083e0af458b00ed665eedf9270abd73 -dist/2025-09-16/rustc-beta-x86_64-unknown-linux-musl.tar.xz=bce669d29a995189e9d99f686b1f9535ad82cc877a106c89af5b423bef8a9d6f -dist/2025-09-16/rustc-beta-x86_64-unknown-netbsd.tar.gz=cb1a0f5ff6d9c28b83f9c82fcfbf404b2920a7da8233d957e43fe001d5190318 -dist/2025-09-16/rustc-beta-x86_64-unknown-netbsd.tar.xz=25953b44e3b196e89b5dc6028805517f42f6367c6b990a930ba44f63dce0a855 -dist/2025-09-16/rust-std-beta-aarch64-apple-darwin.tar.gz=8ada740b1af444b98c3df74f5f810a05036b6195e6b54115a7e34bed23a30d1c -dist/2025-09-16/rust-std-beta-aarch64-apple-darwin.tar.xz=1211a726975c1a9fbd679156644b83c446cab4d6eb7728d3dc38db6c949b997f -dist/2025-09-16/rust-std-beta-aarch64-apple-ios.tar.gz=58e7ec536d9bee83dcbcdad819c4a27689f02a20acbc75ffcb6ceeec7929da4f -dist/2025-09-16/rust-std-beta-aarch64-apple-ios.tar.xz=f0afe719f83531e0c22d0621b9137d30bacd17ffcc9d0188e9cee2a71f10afa0 -dist/2025-09-16/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=e1f067b248dfcf1d6efbc589a0938ceeaa81087d041889d65b90f4f82aeb35ff -dist/2025-09-16/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=182ad0986236f21ba93d49ed3dbec72e54cbc88d18adf77848e87f497b7b2f05 -dist/2025-09-16/rust-std-beta-aarch64-apple-ios-sim.tar.gz=cd367dce5dc4a5d209db298f97070e55ec16caa8931ef17a189bf5d7f5c21212 -dist/2025-09-16/rust-std-beta-aarch64-apple-ios-sim.tar.xz=b51aba09d7ced8b69e15ec9ca381379cf9f910c1f89109c8764cc15d51fd8293 -dist/2025-09-16/rust-std-beta-aarch64-linux-android.tar.gz=89154dba00da80cd67cff7e9beb1f68721bae9686e1fcff1379a2a4f549eec79 -dist/2025-09-16/rust-std-beta-aarch64-linux-android.tar.xz=d373ed69ed30521b2f724deac051662bcc7d4a919c1ff9f56b59ef028f07dcf0 -dist/2025-09-16/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=9e84923928671f76762752a559d0e9a54f513b2f518eaad3da6f5772e46183c0 -dist/2025-09-16/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=9170e87332e2a74624fa1dbaddb725f6cb331a8f65a5b0b2781c04aa58bc48fe -dist/2025-09-16/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=929786491660eedf2cb6c02036ce560043a7bec349bde762effa53ba59d17c81 -dist/2025-09-16/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=72b395a15c0af1c92434a95010fec5435343190c3b90d55d2c7d17d3d4c6da52 -dist/2025-09-16/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=36245bab64c4757821ca0732fe88fb5620601035f50bcd2d061386903abe7027 -dist/2025-09-16/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=4c1bfe4453d36d554956f7ebb169bb81912815a28960bd7caf977940109573f1 -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=080603e3b117d3654b2e0b88ec172f4c5319ebadf9386074e7e6f0c466e9c90a -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=4ea35b78b18a121bd3b0165d9f3278f3a454e108564e02b6c7d7133a33a99788 -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=9c5b7188fed949b0462f9d6c45221b8b98e7feebdc4f8bc2d6d0d652cf3b3c24 -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=31514db13f19dea13e47b8eff9fd8daec6bd0a73816aefadb94ad4e771d6a902 -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=a540735416f0b264444281865e1ed450c5bd58275f73426191cfb547b674ea7a -dist/2025-09-16/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=9922ad22f002f5df232d8ed1dcdcd23a5e1a0a5b4b27e27c3b5fbd5b39087d03 -dist/2025-09-16/rust-std-beta-aarch64-unknown-none.tar.gz=618073f52e27d3ebd15b028398524a12e3bd91d4147e70b2160430e03ecdb178 -dist/2025-09-16/rust-std-beta-aarch64-unknown-none.tar.xz=3fa92c9383ac122c4b9ea2a00ff9f38d94b52c384b418b13ea573115daa07563 -dist/2025-09-16/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=33c4825523ef0e27b038111d3504cfbb4e049fcc3e136ddec186268346b647d9 -dist/2025-09-16/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=4487b93a8c7ac712328c98a660948e795e833f4edd3c92449df5472de6e320b9 -dist/2025-09-16/rust-std-beta-aarch64-unknown-uefi.tar.gz=f0c275d8bd220dc066541e6f2c7e41b6d019d698fa2b613c1f9c87ab4e29f4af -dist/2025-09-16/rust-std-beta-aarch64-unknown-uefi.tar.xz=daed68a82600fe2f3652ae87e4c491460891906fcd06ae206bc794c354771564 -dist/2025-09-16/rust-std-beta-arm-linux-androideabi.tar.gz=9fa058b5bd0ac5af231cc5ee31f67cf3f16b361a41aceb43b17c81d8e180d16e -dist/2025-09-16/rust-std-beta-arm-linux-androideabi.tar.xz=c6868144f5f5fdfc0d993f66fa91a01a91e0288014a7ab5d6233a93447508852 -dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=5cd076f756c63e54067d780167da3e35bcb04f487fefa093028b0cd3435a2fcb -dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=3322a270f3696fc2254536608ebc49e047ad59e1a01493381c9c52f813bcb8ae -dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=8bed8176ed380d974c2bc6df5314ac385ca6b72a623a8d16c00b753932a64db9 -dist/2025-09-16/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=d557f55446ba39a1b3335ff1e4196ab40effa4ba42b2ce91bbde93738fd93da0 -dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=12a56e9f5c9caf4e262b9361af5ea5b62ec29b18ffdb807cda83655c531670b5 -dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=82b28e64f6278cb24573d699b14826a51be465b68cb48e767a26f1938221f5c4 -dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=a3076f1f12895062e42a8ceacd5c06a3156b5a553c708615fddb975366d9424a -dist/2025-09-16/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=4bf81edd4a193e6850bccbffb66c6005b1b99322192e87183d684dd9ec528cad -dist/2025-09-16/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=2170d924f4ccc579baf862171934029505d476170825397580154dfab860d4a8 -dist/2025-09-16/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=dc3bdf6f93b32aa19987d149ed7da7616f3785f67a63fcf1e0d1aebeb8007d67 -dist/2025-09-16/rust-std-beta-armebv7r-none-eabi.tar.gz=2863a22ce3663edd346d730cac4bbdef3b9cd2ea2cd5907dcbc61220833e24e8 -dist/2025-09-16/rust-std-beta-armebv7r-none-eabi.tar.xz=74a752daef0446ed260845b016b3e57d20de4e914102713e0c3b652329f9f6f1 -dist/2025-09-16/rust-std-beta-armebv7r-none-eabihf.tar.gz=8fc3fcf203da0fa6f321abbda405ba0121dbd898ea02513431a5ca3dd5e6e16d -dist/2025-09-16/rust-std-beta-armebv7r-none-eabihf.tar.xz=65cfd5061c4db0963b28f7bc636ece115ff7b812710ba9f902977f5014bdeafd -dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=1cb760fdde8e9d84c5caa787488f0bb05c5e649f411b666f2d6a5440126f5992 -dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=9146b64438a68c506a93ae3c4d4d1a928358b562fac3b7513a95c8098cebdae0 -dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=9df691f77c95a0bdf5c45fc546c2319863cdca4d433780fe8c7cd146496382b8 -dist/2025-09-16/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=bba043357ba6c78fd46430c169710a264109ee5dadb79b98075744bac273418c -dist/2025-09-16/rust-std-beta-armv7-linux-androideabi.tar.gz=ab5726ae0472a8563b7e1e0dd6de32e1e9ee2dca45259bd1d382a24167919c61 -dist/2025-09-16/rust-std-beta-armv7-linux-androideabi.tar.xz=5af2a58f78fcd9dbcf1fac95cb50633ab9128e1e728960c0025e310a5528326f -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=9ff1f79f8c03756b41f8386995dbb011387982a657262c013579d0866a18ff1d -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=32dd8b2be52449b4e592a87acfba122b80990d2993fa81b48b81c0de4afb1533 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=7420a4ecd9ebb55b6864a22fedec9def0585f8460eaebbcd9e236cac2ae0a6a8 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=75de468d6d4041d77e5757118c3c93df6319f30ba6372fdc8e859b9e48f27d81 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=96f7dbe5d4bfe852b22f971cb59b7fa0981fd7fdc9e8d28fd1b6f6ae3bc59bfa -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=4166e6b0c28eca8a507f1d3c83948ba09361198466eeb648f491d9850e884df9 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=035c4db6428b912280ac56bb520bad29255b3c30bec0e89839721443109a7ce5 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=4a94449dd1f02d748c79ec1aed3907cbce97dc5641e5be3b3011234ef3517987 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=94006dd7c1a1d6cb28972369a0b0c429de992d541549d4b292fd6b737eacce24 -dist/2025-09-16/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=bb5cce4d13f2ae9d9d6f254d021d516189f29b6e78f3c1824e0803f7856ce7e9 -dist/2025-09-16/rust-std-beta-armv7a-none-eabi.tar.gz=453d62c4392ffd108233b7ce9b6abf0cc04b28a63bbbc5b979927ed7e9f8481f -dist/2025-09-16/rust-std-beta-armv7a-none-eabi.tar.xz=9b4093e7735ffe3c79f16f99a412492588a6652242e2a18c0f278e24e34cb333 -dist/2025-09-16/rust-std-beta-armv7r-none-eabi.tar.gz=293dcff7ac56a7f52fbfa1afef9fa2b4a3e14b9b418c5a56e12e8c7cc76c963f -dist/2025-09-16/rust-std-beta-armv7r-none-eabi.tar.xz=75f3737b88d197769d999b484aeb6aff705696fdccec8f3772c7a1e10630b146 -dist/2025-09-16/rust-std-beta-armv7r-none-eabihf.tar.gz=3d988af87a2c90efc5674f77baf67f5933fe60ae6ab9f4eb4256f14d98446824 -dist/2025-09-16/rust-std-beta-armv7r-none-eabihf.tar.xz=e91a15900421a8a445a7a0038923ad197fea4ec19e972ba7ff75fd4dd7d7ac12 -dist/2025-09-16/rust-std-beta-i586-unknown-linux-gnu.tar.gz=19795fe21f305ed033354d127f8e9e7aa0e9d7c67f756d22beb161aaeb9a0742 -dist/2025-09-16/rust-std-beta-i586-unknown-linux-gnu.tar.xz=8afa409edf8dfb45eab3565add6dde2fa71118008ee1e7e1416fb8dc11f66fde -dist/2025-09-16/rust-std-beta-i586-unknown-linux-musl.tar.gz=43aaa9101863b6a419330ec75f0ffde61faf2a27f050947d2a200e5a705dd085 -dist/2025-09-16/rust-std-beta-i586-unknown-linux-musl.tar.xz=86ac890f2aaee4382a126ad9e667d53039dbca550aa548090706bdc308749962 -dist/2025-09-16/rust-std-beta-i686-linux-android.tar.gz=c3df364048b9af096c607a8e5639dc37d470ab125441ea04a2f2241766338bd5 -dist/2025-09-16/rust-std-beta-i686-linux-android.tar.xz=556bb5e7e4effb543c04a64cba683b81a6ffaa319cf1d053482968767480b9b9 -dist/2025-09-16/rust-std-beta-i686-pc-windows-gnu.tar.gz=8c141a8c3b4cdbc2eff61d2a557fa602b9039dd54ff2000d0a0b790ae58c0879 -dist/2025-09-16/rust-std-beta-i686-pc-windows-gnu.tar.xz=3995a3c391e242663533680600a38343396e2a73b6876b1dd691d83dd143eac7 -dist/2025-09-16/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=3da461c03124d6ca148ed1d4cc80be4bd0d9f4610df95770ca800d72ef6c2b4f -dist/2025-09-16/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=7eaba102d1ce8ea0461657dcce62a5f8826f2154f56f89e9385e75fdc763c399 -dist/2025-09-16/rust-std-beta-i686-pc-windows-msvc.tar.gz=d1e31c04ff2145122df2ab05af6e2ba1091d1788741234cc39524f1289886187 -dist/2025-09-16/rust-std-beta-i686-pc-windows-msvc.tar.xz=330aca62a0cb38342ce6eda427c6074651ddd0c83c4b28b53ddc736edf049ee5 -dist/2025-09-16/rust-std-beta-i686-unknown-freebsd.tar.gz=48dabb6775c14d23bf04a8c5c26f2e4d6af369dff6d506aa08cf2a08d75a5d8d -dist/2025-09-16/rust-std-beta-i686-unknown-freebsd.tar.xz=db97db17addbf5d4e314f77ac9bb232a661042edf7beeed73b252e44c6439fe8 -dist/2025-09-16/rust-std-beta-i686-unknown-linux-gnu.tar.gz=3abbec5a87dfe240bbdaf6e9a92acb2d8e6e684983803daadc7750290da71264 -dist/2025-09-16/rust-std-beta-i686-unknown-linux-gnu.tar.xz=568aec0d1427a2d866c7550dfc770be13401902741999410d4dc2e7772ec0a57 -dist/2025-09-16/rust-std-beta-i686-unknown-linux-musl.tar.gz=0739e8dcab50c87156cb3e52a18b57da6040fffd9509561fb3046e81be953a40 -dist/2025-09-16/rust-std-beta-i686-unknown-linux-musl.tar.xz=8ed8a4b1f402593db73fc2e9540664d9e4e12b8e1b5f027e56b2f3c4784a8995 -dist/2025-09-16/rust-std-beta-i686-unknown-uefi.tar.gz=40a2726131ef0e6102667bcc8ea9a64b4b19f12d58696ab434b74f9abf37cee0 -dist/2025-09-16/rust-std-beta-i686-unknown-uefi.tar.xz=500b0018fe01e9a9949ca077bb69426078a2774e83b95319d09bab5d3f6d0e04 -dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=64aadbe1da544e2f08cafb217ec1ad341b8e2adc660f60502f700773983d059a -dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=e864959561ee49a0ae680f93307929d0e16013d2ef0743a525c5d592faa10704 -dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=c709c58bb18b5a9385e07998dfe31e3ade0661fbfd4811907a6e7cfffbfe2e6c -dist/2025-09-16/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=e797481997def9fe0a6466b31439d1eeb4b299c92cde3a25a7fdd557611f0888 -dist/2025-09-16/rust-std-beta-loongarch64-unknown-none.tar.gz=79d43f32d6830a0d3c9e1e8fca3ba87db44e692cc70431ac6cabea5bc59b8f0b -dist/2025-09-16/rust-std-beta-loongarch64-unknown-none.tar.xz=4d158a9d4f4e1943b2d00fd19adad762c35af24200742440bccfea62304272b8 -dist/2025-09-16/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=f0c460fee60c913463d9c1fbe369287ab436cc7e2882487afef457c78cf86ad3 -dist/2025-09-16/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=f15e2baff9b0ce68ca7e1595f00b21c45bff33a1b75fbf05a8f04b096295aa0f -dist/2025-09-16/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=102c4017e11cc3407f4871aaade7f3292c8cdc92707a243788e0e4a62604cce0 -dist/2025-09-16/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=6eaad8877694af02a10d18ce609b6678d96e75f87a1684b67279c9915601bd3b -dist/2025-09-16/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=26139715f7faac9be6e458ee1be840db044692a518cd0fde29c59454405d3063 -dist/2025-09-16/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=0edf1c0a3b8ce5dc76f1ade2dceb91f96a5afa3e54c7f25f3d3320dabeab06f2 -dist/2025-09-16/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=dbdd4f2cb5cacdf2656f83f0dc0b2101613cc344819e9ba8e790406e17d16b16 -dist/2025-09-16/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=a6dd7f0416ceea63ac723d4aeaf9cbd8d7ed8ed7d5b8925b3317cee75b9138df -dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=424eabcd66e922cb61757842c46afd298cac2e1ff809b593f6f409732261f4a3 -dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=7ac3a7e79c5b3c498daad88a6f926544eaf59d04711cf20ba05c1391544a4335 -dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=9e21d985a6702a96161aeb9ea6d1e12eb52136c7433b2ca5597c07b85a954939 -dist/2025-09-16/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=69658c2818ff575eefbda5e64a2a6f253ce21f587a72afb2727466a923ec508f -dist/2025-09-16/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=25da1a37707d9dbac16d91f5177eaa5244f4280758f3b5a0bfbb09b5ae190720 -dist/2025-09-16/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=52f6a0752e9db84c0efa49826988dd64f32930799ce2e057b3a6a20a5c696181 -dist/2025-09-16/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=9f4f18ca2d1d4d7e8785f3ae61084aeaee7581d7eac2c7c883557cf30888cb57 -dist/2025-09-16/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=2ef831047ada8f706fa30a655e8a2b17c15c1f10e65c66a0d3577085ff1e80c2 -dist/2025-09-16/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=9420edabe9112cb1ab9fee01dea1bdb30a866b4633205f287767c07fb57f4630 -dist/2025-09-16/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=ed7b43b375dc7c30e1bd1add31940192978ad86c5c6684b54b24252117e92008 -dist/2025-09-16/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=cbb03936548c947633b7dff7952653aef98ae25d1ff79b8f8549e87158b6cac6 -dist/2025-09-16/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=c957ec9bc4b1345bc805b506caeeee9350e9f07d31d4177796f461cce7e7a341 -dist/2025-09-16/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=46fd5976d8bd59a89ebd2c74cc246b9727516b28cb9b1a839ac06340d738a4bf -dist/2025-09-16/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=a7640744bef500db2e7ebaafc4c93e73acb31b61faf47decef61ed46db73c001 -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=537dd37985108561d6e0afe76238193fa33f1e600aba44b484a15d1f40d68142 -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=bda3e2c4b93d864dd75bf64e08ba3d2ac9719581fb890c62073078d2005bd634 -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=3edbf40890f0f52cb61c0ec8665a43d230e6e148dd4afbdbd7ca8ad53a0a2b8d -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=a515cc391e794e397ce34eb994f3a1c35cff7e7e31d54a859141000a52397c70 -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=d45fbe65a55b3010fd8994d3effec67f96d86593d37b6fefeb956f4a6b282e50 -dist/2025-09-16/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=13f9c87c6bed3b905ec7564fb79517e891b4a70d6c5191a876844199780f7a81 -dist/2025-09-16/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=e55f12443bf8c44e27d585bc1c38472e3175bb9d7913af55ab3bcd8f23498146 -dist/2025-09-16/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=5cde9db69b110f1766da147105e3a8d879dd36c69b2367416228024d183b12b0 -dist/2025-09-16/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=3fb23f12e07d3b4ef59a637fe68c2ad1799aead28c65cf3d5d136dfea4c12eb9 -dist/2025-09-16/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=999b7f7813d0d868bbda410fa37d26ba737d443a99ad74d8c2b0ecbc24fb1af3 -dist/2025-09-16/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=12e756fe1f5b57a752916b7397b981e288cc30b5861b16c5a710778c372be42a -dist/2025-09-16/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=0d0ad94937379ac571397b7f10ec5aa20cccf5401aae16ad4d18582dc609055e -dist/2025-09-16/rust-std-beta-sparcv9-sun-solaris.tar.gz=4bbe087dae754754f4dc1c05aaf664c5e0dd4429932feba88aab744e7c5b7472 -dist/2025-09-16/rust-std-beta-sparcv9-sun-solaris.tar.xz=dfa49be70f7ee3989cec7f634cf84e3b418f48882af8b69540e41f2ffb8cc216 -dist/2025-09-16/rust-std-beta-thumbv6m-none-eabi.tar.gz=598622e28c6ce111f4d47e2172a0657c3b0ca62c42895836376b1959fc2e094a -dist/2025-09-16/rust-std-beta-thumbv6m-none-eabi.tar.xz=359bb98d293e1cea22a505377ce49ba95095c735b50b7d404ce4f36d84bdfec4 -dist/2025-09-16/rust-std-beta-thumbv7em-none-eabi.tar.gz=d1f994c835b1d72a665eeb7aa4683a09ac60c1977766b066c817c3eb37ad3a4a -dist/2025-09-16/rust-std-beta-thumbv7em-none-eabi.tar.xz=22e8605af621866d82f2bdcd8089a561e10f4055624aa6dff5d704b953b7d547 -dist/2025-09-16/rust-std-beta-thumbv7em-none-eabihf.tar.gz=8e862615db8daf2c063d27f1860bd083fadefd459634e61b946ca05b8747308d -dist/2025-09-16/rust-std-beta-thumbv7em-none-eabihf.tar.xz=8c9b3d2913242cd2e7fc690e02bd28690fa50b7278befa8b57ec9b7eec4f34e4 -dist/2025-09-16/rust-std-beta-thumbv7m-none-eabi.tar.gz=fa79d4e07630821149b151bc5673205a479570bfd971e66e874e75b5046bdfc5 -dist/2025-09-16/rust-std-beta-thumbv7m-none-eabi.tar.xz=f6f3c14f35d3b5fd01a6244f802d99e261e529bc6d4a6c5749b1cfccd524c16b -dist/2025-09-16/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=194bc6660b3d4809d5f709e7f82159141e90b31d2318cd550b4deab65e108fe6 -dist/2025-09-16/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=36534004068562d8678a3cd73f8f82b50406aad6daeecf348246375da3bae720 -dist/2025-09-16/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=622ec13126461d4d6c1f7eb3fc387279c03ffbb96fdbbfcc7c22c2433e62232d -dist/2025-09-16/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=a8162987ddefbde68e2931fc824371aa71d595106488a48732b67ec45f36ac4a -dist/2025-09-16/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=6d7f31ee7fd8633d8497cddfbe05a1ca064c4d45f50066e86cd82fe4b40b93d5 -dist/2025-09-16/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=9b3309e290007406d410b8ee86ecef887d5c48b515b11cf7827baef23a0c4a7d -dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=74fd7db50be0b77e7a44d1a32afc76f2a438fb789ea32f3ce1ceff00ccbf030c -dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=e7736bc6c78eba1947ca6ec4ae5d875de192f44755ade737ce1e367f294576d8 -dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=d8905fddbae4ae3e39439fe5234cd05454034d9f2a847056766bbe41126405f1 -dist/2025-09-16/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=94628c4c60615b15f1c5b331a803d4d1963289b770ba54adc4458e52d17397fe -dist/2025-09-16/rust-std-beta-wasm32-unknown-emscripten.tar.gz=68c53c1069b29461573c68b5df7ae2e30b4b7dd682cde70f26a200854525c012 -dist/2025-09-16/rust-std-beta-wasm32-unknown-emscripten.tar.xz=cc1a4d61bc45832fa1ae19ecc1347d084249f1be3553fb33d33f0e5d8df5fe5d -dist/2025-09-16/rust-std-beta-wasm32-unknown-unknown.tar.gz=a33ed15817407053144b8a6041dab287a390b3338dafcd1b02418b3dbe18b024 -dist/2025-09-16/rust-std-beta-wasm32-unknown-unknown.tar.xz=f0df12d2767da7528c614f5bdf6a9c6bec831b4020eed357409d3fc7fb0c869c -dist/2025-09-16/rust-std-beta-wasm32-wasip1.tar.gz=86634e652b087947b914b590a2e978ef1d29d09b69afff63a682f26b6dcac41d -dist/2025-09-16/rust-std-beta-wasm32-wasip1.tar.xz=04e2a9c6914b17664443b674827ab706869a81200c885e97779df0da98682612 -dist/2025-09-16/rust-std-beta-wasm32-wasip1-threads.tar.gz=3fef167485111064b1d468e5664d9173e24dbf20406165f92293f304457aeee0 -dist/2025-09-16/rust-std-beta-wasm32-wasip1-threads.tar.xz=7e12cabce073a54fbd4c97d3169e34e4beb3872a161c7c082e148b0d4adb2f7b -dist/2025-09-16/rust-std-beta-wasm32-wasip2.tar.gz=3b8f3e6d4ce34bc8c5300b77bdf875b04a69f87f73d930439bacf56f061f667f -dist/2025-09-16/rust-std-beta-wasm32-wasip2.tar.xz=056ad523469276f90f185b72696b20c6b47f57b5721544a20f6e765cc51769fc -dist/2025-09-16/rust-std-beta-wasm32v1-none.tar.gz=fee626762c4bee195d800883ba15d890604280f0cddb08aa64a6199fc4591635 -dist/2025-09-16/rust-std-beta-wasm32v1-none.tar.xz=7bc289363efb2d5555e9742bf619decc2454cb784c133db55a71be4c0aed9f90 -dist/2025-09-16/rust-std-beta-x86_64-apple-darwin.tar.gz=51cf2f8f249da5df653efb073c81047583bc7bf952a4b03d6d05c9fecb7fa0c7 -dist/2025-09-16/rust-std-beta-x86_64-apple-darwin.tar.xz=4e380037df6b548916136138069e76139dbc069688ec717632c30a07854d0739 -dist/2025-09-16/rust-std-beta-x86_64-apple-ios.tar.gz=b232b73befda81ffaf0d1db7838e2057c503cf2f4f1e95f0706cfd0faf77d853 -dist/2025-09-16/rust-std-beta-x86_64-apple-ios.tar.xz=1431fa936f5909e4e92894c7e2074d3db879c10cf3f2344b050f9c77965d1373 -dist/2025-09-16/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=fb0a1f0228c051a85440933358f7c00d47c4ce44a15a0b0025b00331ec53f876 -dist/2025-09-16/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=b725c62998a3b28caa1f89549230ff275af854a3d04cdb6562f19be7e8a1af31 -dist/2025-09-16/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=b9588bd0c684a50e0d73717f5ae5c2f0e36a90052126d2c4a539e5a116eeb4e0 -dist/2025-09-16/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=70affff2a14340109f86ec585b65bed16e77612ac7b939336245cc15f7f6c300 -dist/2025-09-16/rust-std-beta-x86_64-linux-android.tar.gz=fb9fc9bbfef414092ad1082d7529fa2efc52adc849a7f45da3d5688d5f6df1d2 -dist/2025-09-16/rust-std-beta-x86_64-linux-android.tar.xz=3fd9b4d89e7ee604f842f7915b90bb02a0c44f8a4694a2d10f94efe7bbf0840c -dist/2025-09-16/rust-std-beta-x86_64-pc-solaris.tar.gz=caa39b90ac5ff710cc76828ca52c784e48518b9c3f1c58cb6f6603a7d05f87b7 -dist/2025-09-16/rust-std-beta-x86_64-pc-solaris.tar.xz=6034b44e5e7bca888027a21d8a4b1229c08fd22b744b83ab817d487aa5e6db56 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=6e7a9aa27a85a09c603fb624ea1cc56120ccbf5b4fed1be8d0097232cd1bf849 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=00f6c500331f2f75ba9c56f896db1420c353e8c0fae450da6b5806794cd58fc7 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=9279908bfa629467d619143e5e0abbcec7a843dddd0f6737994f636edb171e90 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=f1eca139e01d2a8a2627c064f3cc14d7a2016c50738fdc19d3d586f225066f79 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=1fdcf7de3772c24733474e5c0f3e64b56ca627e5a235b890ba0efa2265648d79 -dist/2025-09-16/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=23f9e2447316fda77b7a5af90c160c89d9f5ed735c7d5715cc2850b92fd1cfe6 -dist/2025-09-16/rust-std-beta-x86_64-unknown-freebsd.tar.gz=8dbdbe296c35e64170b9d1e7361a90aa260e456ea7ae4bd969db1dbcad976532 -dist/2025-09-16/rust-std-beta-x86_64-unknown-freebsd.tar.xz=9469e49e425349514f2bd224ccc64903c163bb481a425e01122bd3bf00ebc90d -dist/2025-09-16/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=6e97622b123772e4deaf91b81d1925fe2d39b87f567b96437c03c16a0982b709 -dist/2025-09-16/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=0affcbb44870daa9410887cea10844ad1f48b4e1aec053274e86e39b42aba360 -dist/2025-09-16/rust-std-beta-x86_64-unknown-illumos.tar.gz=3023abaa7f221946e1e9cd40d4a607e8c57bddf8a250a27e0d58dd95a51ef877 -dist/2025-09-16/rust-std-beta-x86_64-unknown-illumos.tar.xz=1e76ba54bdd6673428efae1407e0e5991115f59885159e68e9d63cdfda9ca7dc -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=92db1c0d785820c128bf4ad05b567b17871d2f01ef902052a90839e3fe744979 -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=233d981cbd309a03b16c5a80037faf992c47548cedf5ab4593db41554ffb8525 -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=fe3fd22ad099bae73ffcf1a4881ee2f2c520c6108ac27ca0c64fc5111ec02230 -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=027738f65f61fb77ceff38d9342915ca8344e0a1323e7659a1a5159c99c4a811 -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=e6527497065541fc4701e71efd0a896a8048b653454a75f27765e5b7b6ac748d -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=807862fb874b7bee60b2740ed2df2454d8be50b0877d34b7bea796cfda69d583 -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=17259bb2f4bb1183ecb187296ae9afef58aa2f8e816d2764caf48aa3900fd66c -dist/2025-09-16/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=e459cff284cd0ee388ab65c945bfca4c64b32fd268ee66c2cd8abc325580ed12 -dist/2025-09-16/rust-std-beta-x86_64-unknown-netbsd.tar.gz=5a60ff084d1da42324d6956510a68b3b83a47b432b48e2228e58f52dc8c01b69 -dist/2025-09-16/rust-std-beta-x86_64-unknown-netbsd.tar.xz=9d6823b8b1605f748f1d7af5c52bed1dd926a1d5fbd01ea9a8f939f3648b7800 -dist/2025-09-16/rust-std-beta-x86_64-unknown-none.tar.gz=57b0a2838c59feb0808462fc043c9a85a292f0025104dd6de3c5e78d4d71894d -dist/2025-09-16/rust-std-beta-x86_64-unknown-none.tar.xz=e5a5cd9c861b29d11ae110dd4b239d5069dd0aac57202bfa440e15869f5c7e9e -dist/2025-09-16/rust-std-beta-x86_64-unknown-redox.tar.gz=f9cdee694af41422f785126fda566891357295b5fae56268188bbf2978b92089 -dist/2025-09-16/rust-std-beta-x86_64-unknown-redox.tar.xz=8c7668ceb1611dafd9bb9ffa6de9a11e39d049cebeba05e2dfa889e69d94a54c -dist/2025-09-16/rust-std-beta-x86_64-unknown-uefi.tar.gz=33045879d1aebfb17faf93bd471bb4c4ee3b9a787c57704814ab2dd301ac8ba6 -dist/2025-09-16/rust-std-beta-x86_64-unknown-uefi.tar.xz=28018cd9cd16e428790600decff5c7df749fe500861ecd3853895ebe69fb7100 -dist/2025-09-16/cargo-beta-aarch64-apple-darwin.tar.gz=f0642e83a5a6c8654d724e4929c0e58a4c07c330eae58fc4794ab94721b2732c -dist/2025-09-16/cargo-beta-aarch64-apple-darwin.tar.xz=13cd17e3f35cc0214b25b65463b2394f3642ff8acf4e13af20b442b0100556a3 -dist/2025-09-16/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=fa047b7d7ad4ab64b85ca7beef052a66a89c60f976b099364eb3acfd2867c3fc -dist/2025-09-16/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=df3a9254c4f329454dae9428d88f6c3e69ee8a120bf2eb380193eee63620ed69 -dist/2025-09-16/cargo-beta-aarch64-pc-windows-msvc.tar.gz=e5b608ae84d9258bccee0ecbefaefbaf34b989cd656f096692ea84c6f8d78645 -dist/2025-09-16/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8ad0c0d49f7f6ebc5cce4a2b76036c2d0eb8adb38ce11125e2b36a1291b8ebd7 -dist/2025-09-16/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=f454f83f4ffe0095d3285011b3abae11b0331d87d398ff95622e9091ee09826a -dist/2025-09-16/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=29c83085c7d51874e943b429999cf7709c0cdc422b1254a5e587aecd653430b4 -dist/2025-09-16/cargo-beta-aarch64-unknown-linux-musl.tar.gz=8f85db99809ae593abb4806ffced52abf048dfae81891ab68ef4c83f11f03fb2 -dist/2025-09-16/cargo-beta-aarch64-unknown-linux-musl.tar.xz=e5f004172331f658c3027084950360e1d60bd0cf3cca83fa43cf7fc251cefd37 -dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=1aae91a92e1d41e9c53ede795f91e7dccd3dea687d84151e507f4d3af089f15a -dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=369b8686197e4be3883c3225bb9c186b17839552c91117750abf428255df9c8f -dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=b2e9a3c467ca2847f4ef55241b05a4f5151ca2608e05a43e5101c62d9d6a399d -dist/2025-09-16/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=aaf6bfeb90e732627ae98f4b86083b3a902e31a951b98b3b5e94b67ffa6771a8 -dist/2025-09-16/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=75bcce21bcd655a537b77aeca644f5279ace4f782a97d1e82de3cbe76a7df6ee -dist/2025-09-16/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=287800d221906551a50922caf0637049f3490e17c65d62119c6a26ce48d5ffa8 -dist/2025-09-16/cargo-beta-i686-pc-windows-gnu.tar.gz=89a6059faab9a3e912dc2baae766ed232d38c240c90f503bb2b486225457e8f5 -dist/2025-09-16/cargo-beta-i686-pc-windows-gnu.tar.xz=96e5b7238f543edbcfb16022ec6aa6d3643add1f7b4b0febc3e35ff9d173c075 -dist/2025-09-16/cargo-beta-i686-pc-windows-msvc.tar.gz=39ed55c1619ade6fc49fefa5ba5bfbb67aa34e53d35384a35c37d0681596b9ba -dist/2025-09-16/cargo-beta-i686-pc-windows-msvc.tar.xz=d9ce7d301af311d58d1b4342855b70dfbe0fa8bb365b78bd83822e58a7ce9086 -dist/2025-09-16/cargo-beta-i686-unknown-linux-gnu.tar.gz=2dc0a357f58462c5474cb5c83445bb17d22450056fc77d3a92c844f104e1c96e -dist/2025-09-16/cargo-beta-i686-unknown-linux-gnu.tar.xz=c2e4f00fc345a77da5f13642d6b65a77df553ac0cb596fa34f837d76fac460c8 -dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=e1b5e490a00eecf93d7c591628c8d231eadb8c68104978b623fc16c5369248e8 -dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=c2f91d72d2977168eb12179abd98b4d18f67de4ed455856f7af18a860ff5b1c5 -dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=7fa229d54c917a4872efbc2e9eb9181f5b1133aa44508548e94e4529ab099cd4 -dist/2025-09-16/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=359d7ec4bc7e0eed1c936bff9cee56bc3a132a488499cb83a8e7561222893c66 -dist/2025-09-16/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=fe514bf5afccb535517decb4c3120375eefc36def2190d60f4f0f6d796af1b79 -dist/2025-09-16/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=d3d1307f3f60e9d8ee9f199441185bcb52a7bf5de460302291b178ef282a457e -dist/2025-09-16/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=e7c77c9baf683994bf180b8906d55a2117f5bff85fec6c602a6f1912c39b2817 -dist/2025-09-16/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=207e6fe0baedb7f19e8a0437e24cfb9b46d4131a141863c93a52d00f1b049d8d -dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=87433380a70642842f7dcf4a4c8614cbc136a18198837e631dce419e99ae71b8 -dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=207106c6e7c129fe19940da029360f8ce42f29468836a094a2b02156121888ed -dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=ad90e19faff850cbb03eb04195943a9f65169d6a7b030422f0310f8c053e61c3 -dist/2025-09-16/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=7abcb3c3cf3710fcf41d67664137bd0c823792a70cbd82004e0cbbef58446f44 -dist/2025-09-16/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=7acc6031840a56c47dd2d52a6ce169663ffadb108e92743b22a6dd9a7227baa4 -dist/2025-09-16/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=a2ae500b6c44abeb6cbb42a07a37a8e0ded707fdc1e3f4d6159aaec404912f5c -dist/2025-09-16/cargo-beta-s390x-unknown-linux-gnu.tar.gz=3a69ae63bde95a6dd4020395b90e821e4342fa1b45b00d12ea30f3b1c643ef59 -dist/2025-09-16/cargo-beta-s390x-unknown-linux-gnu.tar.xz=231b1b052018ab851c57563d3c3b4a943cc01f99e3ca6795dfb6a1315d2577c8 -dist/2025-09-16/cargo-beta-sparcv9-sun-solaris.tar.gz=41677b7f137d6509bf7e0fa2eaeefd91d03316e566136d37d006fd98a2a4c28d -dist/2025-09-16/cargo-beta-sparcv9-sun-solaris.tar.xz=f5b4057e69c50258c3edf00dd71f47951833ae892b121641d8d3a0bd38991838 -dist/2025-09-16/cargo-beta-x86_64-apple-darwin.tar.gz=edfe3b23fe5824b5ae763871f133cce684ace0999afd38e9a7b2a08f17b12ac0 -dist/2025-09-16/cargo-beta-x86_64-apple-darwin.tar.xz=173ec4d3c129530c33623f2ec6a5fdc5d739ecfd24f7372f3713ea94aeab8eb1 -dist/2025-09-16/cargo-beta-x86_64-pc-solaris.tar.gz=fe0f57ea7eafb5e157a04cd753b4f6b733245fa8e9baddccf581cec0a7ea2ed4 -dist/2025-09-16/cargo-beta-x86_64-pc-solaris.tar.xz=61ac8a524964f2179c8195d821382597bc8641eace9ba1c27e81453ab8c347e0 -dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnu.tar.gz=44200f95def8558a8b7b85c11533841786d3fd17331e13056dd7ce25de667b2c -dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnu.tar.xz=7144031f2f69bb3bba08cca81e15dc557424c096f9555678b1bd1dd5f4891360 -dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=a7a9c76ec0cb87a725c44336cad0182cbf2c1e06d19cbfa0311501541c08c852 -dist/2025-09-16/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=0b66de9c68f0ec42a410a898468dd67f147284911803207053a01fb2d29b65d7 -dist/2025-09-16/cargo-beta-x86_64-pc-windows-msvc.tar.gz=de26ef4d9091775cceb21af53cb1177922f8eb93763c0e333e59205dcb05ffa5 -dist/2025-09-16/cargo-beta-x86_64-pc-windows-msvc.tar.xz=e6823ffc56d1136d02384acd09e73b6e45ee197c2126b49aa95df832704b9d24 -dist/2025-09-16/cargo-beta-x86_64-unknown-freebsd.tar.gz=dd0656bae2fbf483ebe641ed952499f0127897b09fe14273046cc8e0c7611825 -dist/2025-09-16/cargo-beta-x86_64-unknown-freebsd.tar.xz=6c8b65ac66bf730f9c5bb7caffc75ab1912439d033130438d98ebdaa1a2b922a -dist/2025-09-16/cargo-beta-x86_64-unknown-illumos.tar.gz=5c3f38613b4d2f26643bab1a6db5b769f58878e47168dc92bc2c7b2fe9559ae1 -dist/2025-09-16/cargo-beta-x86_64-unknown-illumos.tar.xz=046ee0c80efe70375179c95461af5724cb00eb9ecc1d995e4a629d316d853686 -dist/2025-09-16/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=b6da1a94fa5718c5a1be4d67d6422d28e62365a433d82f11fe7f09149690e78e -dist/2025-09-16/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=5b8aeabb11e88e4cb02c766c11c0d3d826ded4d08874a11127d8f73f033b0072 -dist/2025-09-16/cargo-beta-x86_64-unknown-linux-musl.tar.gz=00f191b30c29d46e503bfdc49c112ac5295bb67caa091333b0caf210e08d3f01 -dist/2025-09-16/cargo-beta-x86_64-unknown-linux-musl.tar.xz=750060cc199fac6990597a9859234392f4c0a5405d87d1234fc7990032b4c138 -dist/2025-09-16/cargo-beta-x86_64-unknown-netbsd.tar.gz=12513081e2bd88c666881e77e3c4935c710cd6132d9ec48cf9049ac7ece5dd9f -dist/2025-09-16/cargo-beta-x86_64-unknown-netbsd.tar.xz=b1338ed261e33b0fa3e8a63c8930835d87d352ec10011bd4d626c44fa0b0392b -dist/2025-09-16/clippy-beta-aarch64-apple-darwin.tar.gz=668e7fb88b52ad92634fc4d39885843b4893fbb9f34062e8c4fa870f0e71fbbe -dist/2025-09-16/clippy-beta-aarch64-apple-darwin.tar.xz=a0fbf1903d1a95c6ca98ce98a3e639110022480d56c20e175e4fadf211baadda -dist/2025-09-16/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=6593c10385d78ba68b38b8815e5c7aead991d83d9f689a42f4a2911a5f7f0450 -dist/2025-09-16/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=856303f96ed6bd1be89295dffccfc944cd70ce3d1921afc9366f19c6ac800b60 -dist/2025-09-16/clippy-beta-aarch64-pc-windows-msvc.tar.gz=e60f30cba9fe154e7446cf5b7114e7f8c153937eb58cca4cb24dad1b8dd453af -dist/2025-09-16/clippy-beta-aarch64-pc-windows-msvc.tar.xz=24ae9f98df0f2320271b8250234f571b64a4b5ce983a083f45966e58f39b1c94 -dist/2025-09-16/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=aeaf3ed45a8feda550b2043fcbc55ca9fa3eb95141dfecabb794fa9003d76a3b -dist/2025-09-16/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=3187bb767e36aee12059f1d2b77697aa57f71a3c478930c4901dfcd25d414369 -dist/2025-09-16/clippy-beta-aarch64-unknown-linux-musl.tar.gz=c2c23a91b2cccd4978fe612f22c33773833c87719259bd0d501e5a2523f3c689 -dist/2025-09-16/clippy-beta-aarch64-unknown-linux-musl.tar.xz=b7902a5d4dafd1301fb573148b6d1164b9365e976a9019151b66aa93b88e93bf -dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=69e95f21ef2f59df25ee56c2dc10ed5306ca5b41299acea22d2d87d967d17575 -dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=24343efbed86314a6c292659ee0625cdc30bd655eec8009d8e2209a659dcda12 -dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=404b0db1d121ad8fb1a43ecadaa84a79f976a68dbf088ed47fb31245e293445e -dist/2025-09-16/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=f4fbf3a39e1b5d61a481109da644e44e58371c00a3224b187dfbe39827615a33 -dist/2025-09-16/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=fabfcb98b0d9d69e5d022e40ab572d2e84c604dc8d4ba2c88d44501c2a7390ff -dist/2025-09-16/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=8c289d02256ff36412231b320c723becc8d83d5acf9c2325541367eb66f6443a -dist/2025-09-16/clippy-beta-i686-pc-windows-gnu.tar.gz=13838c1fa366173219023000810ba180899b843f7d5c54ffb415c7a517f5af80 -dist/2025-09-16/clippy-beta-i686-pc-windows-gnu.tar.xz=a86b22e78175f89ff2b4dd40b9fbe91b4447914b226d7ef7d020903ed2c9581f -dist/2025-09-16/clippy-beta-i686-pc-windows-msvc.tar.gz=fc2a9397912c6651e04dca0475dc74fcb9a50f370a0ef7f9c7987158dfc58d55 -dist/2025-09-16/clippy-beta-i686-pc-windows-msvc.tar.xz=bf4be32251bac244540759795afe760be13a134580cae8e3fd5956d8ed45b254 -dist/2025-09-16/clippy-beta-i686-unknown-linux-gnu.tar.gz=10580cdb4078d6a27e511ef71ed1a50f67202c1ba145fe315b63f2786ecf6428 -dist/2025-09-16/clippy-beta-i686-unknown-linux-gnu.tar.xz=5ef51556bc3b002a97e7b9f092ce280ee3b5c4a6b02a8d382d87a3f56d90eb04 -dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=34756f99178d5be61df2375f4161aacfb05fbe4d38e9259bb7a01f71e842d67d -dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=1d10a5f1ac3bb2bbd84c421cdd1978865fccd214c08a341c7b6527c6674205bb -dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=c0fc0dcc1a78960b53de22703e7764bb64bf35762127a8f272b45e95e64c7bc1 -dist/2025-09-16/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=f41d1ac85d5714c6fbab9986ced2c80bafdf8251f37f16a5a9da994356b5912e -dist/2025-09-16/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=6f2cae468cc556a4083771c6e3e610f759cad00b7af7255db32d461d6e4f3dcd -dist/2025-09-16/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=6df81ef270b67457b96ae04da2801c2a80f9604df9c902e74140ab4b154a10ab -dist/2025-09-16/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=fa4b89d3144ae42ac8c69f85f7b63f46cbbaefa1c3063f719a75dbf8e3ec39b2 -dist/2025-09-16/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=944976f337f4c27176ac614abc918a2bb037d30c554ba5e92e180d7247089cb9 -dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=30da372a7d750fc5e8509cd2265522b7d3d24ab8c37af24076fd0c6f48d47ea4 -dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=dfc5382049b525b461b5bd343f214f357bd3cf1a66d4d226658538639f4ca85a -dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=f5f500c286fc90f4b7808d45a77beaecd7ae3d5c99522dbc80f09132f078fbe3 -dist/2025-09-16/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=5a5da7891fae2f8fd1fba6a537cb3f26cf0aa73f0e36d01181fa20a82be3536c -dist/2025-09-16/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=538b09d45dbcfed24f521a29f5107a1b39efd8c3217991c21eb6c55603ab267c -dist/2025-09-16/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=e95cc43b991ab4d0f16c593171231bc134538f64ca40ddcdcbf5d2b73bce3307 -dist/2025-09-16/clippy-beta-s390x-unknown-linux-gnu.tar.gz=37b1903fc1e58f57574a2a6ad23014443d72d6e8aec35df0168028acc813c8ba -dist/2025-09-16/clippy-beta-s390x-unknown-linux-gnu.tar.xz=04d65883b79cda42a26c0581349fbeb7023a25bb73db2e525483fc4895386e97 -dist/2025-09-16/clippy-beta-sparcv9-sun-solaris.tar.gz=057cb0d5ba11a7679a17d87304d1301cf583c86ad2faa23fc449d3cedf0ea1be -dist/2025-09-16/clippy-beta-sparcv9-sun-solaris.tar.xz=8cf1abf779a8a5a18561f643a8fff9863909be7c3f8bc84be2d817fc262a1215 -dist/2025-09-16/clippy-beta-x86_64-apple-darwin.tar.gz=a1301c459894d6d9837b98ec8af238b26c401b860a78d458e34c3d747a8b5de3 -dist/2025-09-16/clippy-beta-x86_64-apple-darwin.tar.xz=af863a3fcae2193f8cc26d9b62854bcba6e029b0bef0f2151321bbabfc931c3a -dist/2025-09-16/clippy-beta-x86_64-pc-solaris.tar.gz=5a3176859e8f783d8bf687d578100dabdd8196e52290a9de6b6902463b4f193e -dist/2025-09-16/clippy-beta-x86_64-pc-solaris.tar.xz=93e37f354b89c94434c3c294cc54522ed503687ef62bb70a146196262993ce1c -dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6403ea6cc8c3c3f83f29d894153ba3bc956b19fc213c11ed13d1117620fa543b -dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnu.tar.xz=a9ed248496eacebd88e33715b30253d9d2730087cff24427b34cf07b2eb89a1f -dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=5a72c9e26e4d59d013243d0d2416fe5280f2eb915f760470b4501731d8e8e05e -dist/2025-09-16/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=078dd02a254accc28ee8b26a5e4b3349f013c629bd1da49948f0071184727ab9 -dist/2025-09-16/clippy-beta-x86_64-pc-windows-msvc.tar.gz=288ff79cc4cc4aec96d6ecd92c733e18aca2c846c87359e77d825cf57817c1e0 -dist/2025-09-16/clippy-beta-x86_64-pc-windows-msvc.tar.xz=fbc1421027dc77265be5d106d6db139a6a33b4e5ade0b1eba17e17a4ea59483f -dist/2025-09-16/clippy-beta-x86_64-unknown-freebsd.tar.gz=1fc82034c6b8b38d60788ea3addaeea8ef4448d467def0a92bea750d060c4d39 -dist/2025-09-16/clippy-beta-x86_64-unknown-freebsd.tar.xz=3a3b7d4f98df124bc7c73ae46f0c776320c1a92c0d1dd7d7caa0d138e7068a3f -dist/2025-09-16/clippy-beta-x86_64-unknown-illumos.tar.gz=f02880e995ed24f200b5c1711f2951e7eaeecc4ab144e50d866f11959e8fbeec -dist/2025-09-16/clippy-beta-x86_64-unknown-illumos.tar.xz=a99652b13a6c3570bcdc41638a59b6d68b76562000ceb60a57128ee5065778f1 -dist/2025-09-16/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=657275ab960c8c65b55b22bdbff5040440a039c9631017645a37506771f3d443 -dist/2025-09-16/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=4ababcc7ffc0976218e62bd55666ff06e01350ecc790ae06effb2767521e169c -dist/2025-09-16/clippy-beta-x86_64-unknown-linux-musl.tar.gz=5c454d363e6f8615aaf4d93e39306972e65a544f3a973c034333022aee321f33 -dist/2025-09-16/clippy-beta-x86_64-unknown-linux-musl.tar.xz=327cb34aff228c3fbc3efb389ddab0b50e5e135145602a8f9e5227fb70a462a8 -dist/2025-09-16/clippy-beta-x86_64-unknown-netbsd.tar.gz=87426803e7fc0bebdec6ae46f8ce536d947bdc57294e592599cb54d9d98d3d50 -dist/2025-09-16/clippy-beta-x86_64-unknown-netbsd.tar.xz=021d69fe47201dc89f394d084b70491d15718b24ceeba56e7cda18e3572f0309 -dist/2025-09-16/rustfmt-nightly-aarch64-apple-darwin.tar.gz=fa289216a17ce0b4fce43862f80087e4bcdaae7b20eac1f3776488b3986db45c -dist/2025-09-16/rustfmt-nightly-aarch64-apple-darwin.tar.xz=1dade784b9caf54bc3ec5c9021d7c4ed1307f11f71d12af46961926a4ad6f7d4 -dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=0f221f2e4d5bece40307c4f7b095dd377ffadce916750c27bd7a2d5663eb6b37 -dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=3d7cdda977ddd4a49f27a2fcf9e0027ce9aebcc5f250e25c3788b6bb5169e8b0 -dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=28a44723d3c4da225592786db74f58af4af952b13112469910b5a2f58a7eb20f -dist/2025-09-16/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=7b4b9f2804488f90bdc9b4f5ac19aaaa9b82cff02c86b95aa0b7df0c805502b5 -dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=df6b42adce548127a2a549bc7b6e574de86c22533641c5bc725c1d5ad62af651 -dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=07d680aa64743ec6e8c417ff14747567fa6056738d6ed8c06db2c63c544f5201 -dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=66262bcc6e4fdffef1e9966bedaae64f27169980987425323fbf3a73ad46792d -dist/2025-09-16/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=f8125405f6437be0357eb594fc74842d6995e113b2475fd8fef6f778dddba539 -dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=bcf9fdad0b5209b1e12fd81a18927698f468995e708dd56e8212f977d4ac39a8 -dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=f7a7289e5f48ecf9ddbb214e6ab47280789a14065960f40a92d8dde3e1df57b4 -dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=e463b2bd6236463c68b2d0377bb33adff0daf17fc47dfe8f0857043b793a847d -dist/2025-09-16/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=96c15e4bbfb17be75003ada804b2da5bd750889c783e110a5a3481f045ecbfc7 -dist/2025-09-16/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=6e53b146000b91f569aa5807e69dca726068d2c23e4a44c344592ef7a96927ec -dist/2025-09-16/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=a59f630b27fb33d188ba0dfcd3563a69bced49dfbc0fc1545b0c750bed41506f -dist/2025-09-16/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=ab6b4c2728dc0381f92af8b9f9c730507709e40ca7da89700bfca9b7adb93034 -dist/2025-09-16/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=71950a1d4c6b04868cec2fcdd7c80bbdc675ed5c25917c774e3b6445bdff21c3 -dist/2025-09-16/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=fd25c154f34b662d69a6d5cace59ebd605a1c4085a14ff8b8d597f5cb7324e95 -dist/2025-09-16/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=15a2a897028e11992d2cae90d2a00f730a65f8b62ad500ff82c2fa83767b866e -dist/2025-09-16/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=9516f97a5807b07d37e95a258b9cd60305c2b0429911c2425716e7f77a556eb2 -dist/2025-09-16/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=f665d43854449cb4ade4c518671ac5fef4a9640d276ff7d90c51c1bed08f9909 -dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=5de565b08f2641f3a7b58f79f5c63905859d39573f105ba05addcd4db5cb30de -dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=0f3d7e4b91b6cab8ec9c534ae0f82ba79c57d621f25baa2b408bd2844edbc017 -dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=7a33a965a41fd3958df003506da405a42cd459bc8689aea530b4a66122eccd3f -dist/2025-09-16/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=b38a0cd88bec44e60b52e414237f50352900afb66e3f2b829acf92de5c060e3c -dist/2025-09-16/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=64cc62b309d4be3b004356637d726776c6765cee590cb0ca244e2bc3feb6368a -dist/2025-09-16/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=bf873d8101d8f2f3574387011749b3375298c7a828ba1ebed91536adf50be1be -dist/2025-09-16/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=3fba5ef7cdf564801de103e7724ac0d9096cdfd8340c75df09bc0ddc1e71a74f -dist/2025-09-16/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=c5a8af0f0689aabd52a337eb11a601877cd9f74efa2734d872f2752413551aea -dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=4f1d6e182fbe1fc1a87949651e64a7f6896c3b359b7937b02d2c02b8e9db952e -dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=29f7583b6f0af1239194c8d936a9f7e38f58920f6a4888d26ea1a48ab8c72235 -dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=f937a74b730cc1d064e0b4be538fa5bd51f636f8a5979367cff3a611d7893f45 -dist/2025-09-16/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=d208b1998db3a896ea9c7c79ded0ad6ca4de452d3b6e696ffeeca44fe9fbc58b -dist/2025-09-16/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=6816dc3cdd53c883a7cad8e7237c1e22264dfdf6e1a8033b41887a52cd43a8bc -dist/2025-09-16/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=904b26d880f6b364458e9d0f682cab3a136b3498f27c913d6f422de6fa822e60 -dist/2025-09-16/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=373a93410cc2fae30b952b036487417ec9e6211cd2e1980e60352329444a4376 -dist/2025-09-16/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=8e1e328f2312cf458f8d7288624c11cc5f39bd7c8feef62147216689a5c28a13 -dist/2025-09-16/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=8f22b6753dcad78a46cdc98251b27672bfa94f2ee11a655705fd7fa8a469ae82 -dist/2025-09-16/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=b719b7167321d4be13eaea674f713269c35e4d34dcecaca38e32cb72aa8632c4 -dist/2025-09-16/rustfmt-nightly-x86_64-apple-darwin.tar.gz=63006d28e754c6e6afc5cea25527d4ea628ba084f0af52a36b676faf73f22e07 -dist/2025-09-16/rustfmt-nightly-x86_64-apple-darwin.tar.xz=b74a4e9726b5acf7133511b855088aa3ca9eaf5e5cd73a6d3b054e8a263ed6f1 -dist/2025-09-16/rustfmt-nightly-x86_64-pc-solaris.tar.gz=a3625dc34b971fd11e3d2f895a4933f4ec6b1e01475385cfd02e4eded528a99f -dist/2025-09-16/rustfmt-nightly-x86_64-pc-solaris.tar.xz=1309352fa233debe6b1a06664da7f8d6db9e8b18afb60ca5709ca6a5ec657198 -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=b765f3a632f74d7624ad10afb1875716b836dd6e5836736fbed6ce5fe7b00594 -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=b74f1b6e563790926629952d25199eed14fa15e94845c962c79ac56df64cd18e -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=365e182e871e46e9f54b2265996c4531ae44b5f93a2208a5debcff5367026692 -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=34712b98cd806caf8507b3afb292cd7bbf41599a395847336f617a9c952f911a -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=d04e6573a9253d4c5bf512f7b65415f08389c499d0024ac3990110ec1859b24e -dist/2025-09-16/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=98de7fe5863ce17a6f3c35ef9baff92124c5a398a008d80394a1ddc61fd29390 -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=01f052f3ec99702f7747610c3a5fd0a440a7a38bc5f4da93be9e6db48b599f3e -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=38dcd9d9fedce55d75ea7151937cbdc4f6f76d92eb8039840e520faaa870182f -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=0341b4b7e5287aecafb3097334eeff773c83d5267c3a1c6edcea6c6ae1ee29fd -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=42ac68f632ca2d78b9b4647bde709eab84765f4376722ecb560d099cde89c7f8 -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=af7491f1d5fee8a52768397966b75ca4104400e7023f373dd8b188aab4ae1b35 -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=38cae1811a0e18ddaf094d3931d8d532c5eaba03fa2cc1149380ff9393e8f84a -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=631cf4d825237406f2992f44f26086672886b61fac0d7ff7a4991b25ec756ebb -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=ee52ab0c86918b2a155098cac489f5e639c0abd7064539d0e8a6a7502ca33c54 -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=67de842ff1f8cf99b0ad88ecefc17fa36359fa718e3fc04f243ad8c1595987bd -dist/2025-09-16/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=349480a812b9861112e80ab86d3e80318d239af5212b314b3a7299dbbaffbf8e -dist/2025-09-16/rustc-nightly-aarch64-apple-darwin.tar.gz=fe1ffcb90bdca92a61470c4f7cdf7bcdb4e7bf7c685ac0b92eb3a22509d88963 -dist/2025-09-16/rustc-nightly-aarch64-apple-darwin.tar.xz=b55d2837dcd2bdfbe8444e29d0f36ea584451c4f0b7fb7e9bcd77279e8cd820c -dist/2025-09-16/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=c303271dfc8ccd2c997e5ad88e2c3fbab6b9a188331217dbd46a6d96a53852ff -dist/2025-09-16/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=0d82ddfbeaf9380773bc91f294651609bc928cf53ce95fa89fc4a0f04e54ac67 -dist/2025-09-16/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=6a8a14c5180e4cbd23bf144cf11dec5f47b769a717aeac03546d5cda83b766ab -dist/2025-09-16/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=915239fd57032010740ba9aebea3e018581993d2ae5d2ebab9ca42d73e48077d -dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=932a75ac114cb08c0eef0ef2189b463885a6d64334e25899ff386196ec59fc4e -dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=fcff7229b11ec8925d39830ce62892265f655ff272d9e3a167f102cb0cd8f098 -dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=3ae873418d17f15d46a1de95e945aa1e00d737b688e196d565031947b9dc420d -dist/2025-09-16/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=036eb7c219c6e1c447c5e6ee6d4635dcbcf4c1274c5a14f0dbe5eaee3f175f1b -dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=16484c845c672c775970f0108a8c336a11d2a2e4dd5f76bb5d1b74da1e436484 -dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=9004db0b31113fa8ef0cd44318d0df62e76b8d30ac2a6332f6d483513e4ee7dd -dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=6b3c245279a006b0516df26ca686f56cdf6233740d8c8b18bc19c5aefa84dbad -dist/2025-09-16/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=ecd58877e63d7d209a67ccb5a6e7693af38f2e249ce04001605ac49fcec45edf -dist/2025-09-16/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=d5370d72328bd22b2fc79661061352c99d3288604bd1ed58ae2b798586db5f70 -dist/2025-09-16/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=5b8a6f255601c33bfd215b646b8ff164390e1eb7df69aeaf7be65d97dd9f426d -dist/2025-09-16/rustc-nightly-i686-pc-windows-gnu.tar.gz=74ff396b8dfb28aa91692aced5ffa76f3e270d4c5a2e47b12eda5c4534e92bf3 -dist/2025-09-16/rustc-nightly-i686-pc-windows-gnu.tar.xz=953e50c365d084ffeac61d1279f89943e81b43922c6721f5e55f211d0fc61902 -dist/2025-09-16/rustc-nightly-i686-pc-windows-msvc.tar.gz=c3485374a990da572cab859f6cadbdf252d32e07d550e0f20fcee24be543050c -dist/2025-09-16/rustc-nightly-i686-pc-windows-msvc.tar.xz=ea4afaf63f5b5c92f7feea4b82ae751f6f8bf8f54126d7007bc72d4815352628 -dist/2025-09-16/rustc-nightly-i686-unknown-linux-gnu.tar.gz=0635e9a84313f572d1883841f91bb49c7e7fed9a6dc5db751f00e7b00f140ce2 -dist/2025-09-16/rustc-nightly-i686-unknown-linux-gnu.tar.xz=8904638b6ab37416eedacfcb03c7b87fdcbe1fcd0e3a80e0e5f410c671959d44 -dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=3fab80f207dd5e8f5c276deb11512d55b5e97b3dffe44c1f0249e0c331ce2b68 -dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=95dd7df11dd2b9f27f8fc9e88f37ada794f9e0443cf1291c554f47485eee84dd -dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=a4e7d3c55c7acce10b1f0b59afb9e70d32c651b87a4d1381e945fc07543e474d -dist/2025-09-16/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=f2ca377351093829123525578451c60987c3943be1783e396ab3d5f34e7735cd -dist/2025-09-16/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=b7e5d9c1ecde1b69b17d29a3ac6d15af0e734a7666381f1adefd0bc31ea2c397 -dist/2025-09-16/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=1c18dca6d3ac47b8223ab1e67e94640f402ba8f2ab787b38ba85c49ca8143acb -dist/2025-09-16/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=0850c609f864638538f4a960ba34307045d74d10d462949e9728b2e14cf5eafc -dist/2025-09-16/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=ed4433fd1004935913d5a48faf78bcc4d2517f45e08032995414c69e4ede78ef -dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=9f54367ce8c0f2f1cbb29f84e403a8aebf9a3ec7d2675faa744e8c28b2adcd0f -dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=c1ebb094ca29b4fbb30326ae9ef99804d9f6ecee0bcb5633ce0236ea90058a13 -dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=c39f9dd4ad6f7939d114a9204a72cc22cf9a3d97ef3b496eff2bd78f55ff4e1d -dist/2025-09-16/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=cf218f8da409b2cdfe3820db1ce882e5d10f8a24a77b600cee9f00e85e747d13 -dist/2025-09-16/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=b64731d52f06f0b08fcc6dd64e9319d91fde2e48fd71d2b193f32ff770b81357 -dist/2025-09-16/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=07fac144d37f87e5f7b3a9521ef79789d7ab33cd529bb27c1b95238a28602857 -dist/2025-09-16/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=727ca4ef70eb8cae09f73762202aee2b7998bc97c000571223991347a5408712 -dist/2025-09-16/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=787ae447f764bf727c7debf6543b1ec22ebcf463832eb4d1279ec0f6824c2955 -dist/2025-09-16/rustc-nightly-sparcv9-sun-solaris.tar.gz=9f80d6add413e65dc6ab0e3d44b62f24134c6b3ee0bcc604ab66b3fdcd4fa0fe -dist/2025-09-16/rustc-nightly-sparcv9-sun-solaris.tar.xz=3075e68b1f8a7b7a1c9489afdc2c9a38b1f0107865229497b2c9a7215853ef08 -dist/2025-09-16/rustc-nightly-x86_64-apple-darwin.tar.gz=685ffc70ff08c696321ebae272d09ba7f9e62f221929fa703e8cf21ba790df58 -dist/2025-09-16/rustc-nightly-x86_64-apple-darwin.tar.xz=29bd3bbb0f082cebf2dfef6d89a50dc5f33be231fe9d3e5bd2bb9b0b9bf53f90 -dist/2025-09-16/rustc-nightly-x86_64-pc-solaris.tar.gz=720e21f5ece47547b4cd91d5be89d67ce5334fa23a70921e7d60b0642b53a2d2 -dist/2025-09-16/rustc-nightly-x86_64-pc-solaris.tar.xz=d1e4d96e6f390ab2b6b1f435f5be61a69e01555f5bb66d58b2571f363cce8d26 -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=2e1a315af7faa1f67c35e927fd0f3a0c96fecf0da1a2b3269ced7846d99d9100 -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=7cb11ec687244e19a31996a3a802a8d0851cecd444abc5a8e65a10a037f67f4f -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=89010bc8a19cfafa6bc9714dd90cdcbb31cb85362846b8072cb41a3e8e7583ee -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=e84050c777e73941cb28f39e72a992b6a4a63100541f5b3fb288cf91fa1b0bd2 -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=ffd857605275d71bf7e09eff94bc5fbc9cef222b1c076f1b7aee72f980e4c470 -dist/2025-09-16/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=f28f28f07e98cb1bb43e93501cb43bd88b91b9913257f4de5c7154f0f9653d41 -dist/2025-09-16/rustc-nightly-x86_64-unknown-freebsd.tar.gz=66fe447d3f4526514b146633375efd40373dc28fe0262302be75afdf736986c2 -dist/2025-09-16/rustc-nightly-x86_64-unknown-freebsd.tar.xz=4d929f5079cd7a1c878631288765441aed538956d496881c89465aede623d94a -dist/2025-09-16/rustc-nightly-x86_64-unknown-illumos.tar.gz=feed1caa5d31ab01cd4dc3c4b6b893c5d291fca9c470430f83c2cc90d26418b5 -dist/2025-09-16/rustc-nightly-x86_64-unknown-illumos.tar.xz=2f2b4e9e9a42f255cf66565012c63b2b2c0a13309dbce94d5135cd3280a8fc67 -dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=f96e122b71d41b4889b18a0cee173822d509d8be9eaf28a3fec111df1a5dc5be -dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=ee8914df087f4e20c322851f322220e2f4885a316627c464881d086024e84b54 -dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b226b4edc64f4ed91a9cb616600ece61c12f5da72ca4b62f13669e849de7ebe5 -dist/2025-09-16/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=9a3d98e0fec50ab589027f1d5973ec583b062581b1a1cd59470bb4fb6c51c463 -dist/2025-09-16/rustc-nightly-x86_64-unknown-netbsd.tar.gz=bd0d47aaa630abc01d2f36f9c2a99577254aa3428abf1d666d5520c4fa990844 -dist/2025-09-16/rustc-nightly-x86_64-unknown-netbsd.tar.xz=863edf8589fe1f3f1728222ee49960457d9edd2abc521557f28562e546e27b81 +dist/2025-09-21/rustc-beta-aarch64-apple-darwin.tar.gz=08f8aee2085d8da9041fa9f4c7c6d79b5b1c06c544a3e2309f353844e1250bd0 +dist/2025-09-21/rustc-beta-aarch64-apple-darwin.tar.xz=a36bed31d0f600ca8e8efc19322fe05a88e31bc218078e79c8ca0e7c3d582b20 +dist/2025-09-21/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=2b6b8f275d1b03ed7bc05e631378c0b462d274b7f1f038f2feec752b29993b10 +dist/2025-09-21/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=13adf0b39c176761adcf754671911d5309cf04348ef9f93fcf8c09afa6b70da0 +dist/2025-09-21/rustc-beta-aarch64-pc-windows-msvc.tar.gz=568566c82dd296babbd5588d0c69f23c5b5bfd32b3b25e493e6d45f15d645db7 +dist/2025-09-21/rustc-beta-aarch64-pc-windows-msvc.tar.xz=8357fb4ec176279416cabc0edbb2f7c3d4c812975867c8dd490fd2ee30ed1d1f +dist/2025-09-21/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=a885d01f6f5043bacb3bf4820777e29ab45aac4dbdfed75ee71a3de01b056e05 +dist/2025-09-21/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=3a2bed859214bbea2cdd13675eaf480e62c01646efed26067ba7078e6dd8591f +dist/2025-09-21/rustc-beta-aarch64-unknown-linux-musl.tar.gz=4b66e79a48d172eb674ba7e6b4eea91ebda2351d6d253deef90010ffc48d4801 +dist/2025-09-21/rustc-beta-aarch64-unknown-linux-musl.tar.xz=131f270aee35b36ae02959abe032c77e1de0c75f23f7c61bbca1e2c18a23f4f9 +dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=d8c38594dfd1ef17c9ceb2ea614be730f4647fa5e75e80b5bc12d235216ecbf4 +dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=1643434757b590b1586e9074e82be3cc9e50e5551212d5f2040fdd8feba8f1e2 +dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=1619461791fa6c2b8750043a41acd285bdf1f32d376af675343be3449bb7e5b8 +dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=cd2ed3ae30cf77b5530a2ebee13daeb1419ceec2ab18f754d07b081dd6a607c1 +dist/2025-09-21/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=b0703e79530bd836df864facbfb5065c3e5e8b3a457e4ef55b4f7a4d362b9ba8 +dist/2025-09-21/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=a647780abbe8d36049edd90857b3baab556ac9b61caaef1d98307fe92fc20453 +dist/2025-09-21/rustc-beta-i686-pc-windows-gnu.tar.gz=c7ec1f853b96edbf1a914b8345b025c87641225e0c49507bbffd88f2da05b8f4 +dist/2025-09-21/rustc-beta-i686-pc-windows-gnu.tar.xz=53803baae3061eb164f34900f5867cfdf3bf50733ca0a6bda674b491bc0250b8 +dist/2025-09-21/rustc-beta-i686-pc-windows-msvc.tar.gz=a0f9192059b9989db0c4dba57b5eae9cace1b8e6f8bb2362b028c7f36e34d44c +dist/2025-09-21/rustc-beta-i686-pc-windows-msvc.tar.xz=f8c237af5f0e2fe293671ddfe7fcf90f6e2b161a52314b2eb622f2a1b23ba3fc +dist/2025-09-21/rustc-beta-i686-unknown-linux-gnu.tar.gz=da2a42e5a76e95460a348ba70cdf1c5c6ade82eb6ad3796935158bbf5859b602 +dist/2025-09-21/rustc-beta-i686-unknown-linux-gnu.tar.xz=c98df2f0156c3065179f50d55dafda8c5db1f2eae99ecb3f568a8861299be289 +dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=1149494d96b4ce949308620a360a365c4304b8ee8f8c9512a35f08048aa13c78 +dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=c906f519bc65a3a7514a62f8920d334bc10623a76dd2d3464af88065b79cb334 +dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=e3a9dd7ae0179ebb7659024532b9f3cca9ac4cdf62c0ae411b00d8f8768aaa34 +dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=d9f3428d80a7b72b15c62bd3306628820f73b64f48de37ea079699b733bda048 +dist/2025-09-21/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=4b52241a8b65a9a42db2a75e057176a99e3b434907f498c4b6b9da104e138c72 +dist/2025-09-21/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=40f3a5fc7478b40153ab20e3f14a6d838c80dda529481735e898de989a31f963 +dist/2025-09-21/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=f5aa411dfe614ed288351fa4b17d1e2935501c804c0ad51f22e3db71617b17ea +dist/2025-09-21/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=b9efeb2e185e43775d309d083d8c4f45e30e18b7af71b9c45e397af6bc723fcf +dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=38b6a69885e4dac48f7c3128ff1e88d0bf2d0ce1fbd6a5baa9dda62bca0b2b08 +dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=8576ae6d787b0f8b127bb2dbeee213cc6093ba92dc7d5ff08f463d56e2e6ce90 +dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=5e91ce627e900da2606782ae60598843a6ba17593a7eb0dcc8f5f9a1cc20676d +dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=fe24fad781f829838592e6670655dcff52002ae720f987868fd4b17eb9ed631e +dist/2025-09-21/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=170ccaaa94eb7c813bcedf2afb37cb0c696c5f48ca9d93238ee8cf26efc5bafa +dist/2025-09-21/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=db5a862a260fe8688e3b1da1161948eed5361723a296bb27dcc62758eaa2b873 +dist/2025-09-21/rustc-beta-s390x-unknown-linux-gnu.tar.gz=c764aa7b70dacf7ac3ef5d2184d021304b633309b771f24d62375fa64791614f +dist/2025-09-21/rustc-beta-s390x-unknown-linux-gnu.tar.xz=69eedf71d20924d9729684e27a3a8cebc60efa7c0e7a3f8b6fe664f469a36788 +dist/2025-09-21/rustc-beta-sparcv9-sun-solaris.tar.gz=efb9d78cc395774e05353615c029ed674c1ba55204ae9be3d022abda9f5c6d9c +dist/2025-09-21/rustc-beta-sparcv9-sun-solaris.tar.xz=e8de37de871888886bb58d915f3a27bfd8c30a912ea3f3af4abf52f66708268f +dist/2025-09-21/rustc-beta-x86_64-apple-darwin.tar.gz=b945ef94a4efdc0fdd4a66c3708cb95a97592c922652af06d8d1b6bbaaf71660 +dist/2025-09-21/rustc-beta-x86_64-apple-darwin.tar.xz=876a6080177df194c14d0984f112692295a21186b05bd03a77d0a304dec5ad51 +dist/2025-09-21/rustc-beta-x86_64-pc-solaris.tar.gz=d7c0b4fb19c785ed3a0c16d903cef266438031c3a43b1721d19864a1923d3cb4 +dist/2025-09-21/rustc-beta-x86_64-pc-solaris.tar.xz=b4a5494890bd92b85f66054523b26e9aae5e74b3177c9eae64740ed7fa1d4da4 +dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnu.tar.gz=ea55aab0bd57f0cd6568a6e78c9d0a3727fb7cfaf8ada9379084091244b3221b +dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnu.tar.xz=426009c68fa78001b34f8b329cac7634dd8921c569061f45972058b770206f9f +dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=c79a925f6f2b406db97732e22e1b245ef2c7d1291a574b0d55a861d3c7e5d766 +dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=0f46e118b67656fe16c484589ee0213654cd6edfbe5a29d641aa810bddcc8c62 +dist/2025-09-21/rustc-beta-x86_64-pc-windows-msvc.tar.gz=d7b5fb269841039af498a6d0be2cbd70cb7b65f6a9918a2b6c022cadfdb30fcd +dist/2025-09-21/rustc-beta-x86_64-pc-windows-msvc.tar.xz=dfef15ca4fcf06622953296ebec960e446402ce542e2f264c12c0c03b9a476ce +dist/2025-09-21/rustc-beta-x86_64-unknown-freebsd.tar.gz=09994a33e03f50212cabee85294dfb684fafcef95e1de5b082540d01d92df1ce +dist/2025-09-21/rustc-beta-x86_64-unknown-freebsd.tar.xz=a0e3409ec6f6b02517c8f9d0e00a0627434f6b06a5360da286c46ceab9d12ab1 +dist/2025-09-21/rustc-beta-x86_64-unknown-illumos.tar.gz=01daeea1f1f10d84444b56e8e74e3a451c54e65832234b2caa2ce0a63c0a9ea1 +dist/2025-09-21/rustc-beta-x86_64-unknown-illumos.tar.xz=6299fa81533b9362d1cdbf7984506fcbc26f7d9e857a942f081f5325c87cc4c4 +dist/2025-09-21/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=cafaecb5158fcf39b676baf2d0060fb8b31558be0e442389755ae33a3e7bb42f +dist/2025-09-21/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=5d3c1fceba5285bc54f5a327841ecb8893e719ba874e483c904b903f4dc293ed +dist/2025-09-21/rustc-beta-x86_64-unknown-linux-musl.tar.gz=290a07f32c769f74d21c57a856b3aa39da89e2f086e53c42f3333d1f9ffb5673 +dist/2025-09-21/rustc-beta-x86_64-unknown-linux-musl.tar.xz=8bb354230d3da85bb0fc280c3e95ceadd9f7522d231fe787be8c82aa072ad506 +dist/2025-09-21/rustc-beta-x86_64-unknown-netbsd.tar.gz=af3c98fb99b419cfe50433f5f4a046065066183b248708ec8800253867c678df +dist/2025-09-21/rustc-beta-x86_64-unknown-netbsd.tar.xz=0ea09297621d3b735b2931f6472f95e1587d38f6fb0df7fdf5e427fa3baec4a1 +dist/2025-09-21/rust-std-beta-aarch64-apple-darwin.tar.gz=8c08542fe69e9fd5b2256d17d84427ac206c9e79e265fddbcdf12d1af4e5d913 +dist/2025-09-21/rust-std-beta-aarch64-apple-darwin.tar.xz=7ddd43d1e32a829ffa9a7a798e1339d0569e773d841d8b7ad33701664373b8ae +dist/2025-09-21/rust-std-beta-aarch64-apple-ios.tar.gz=bf3df6d2eb7e5515ae86bb970b5c145140f8e9d503e636fcfc435d47797b650a +dist/2025-09-21/rust-std-beta-aarch64-apple-ios.tar.xz=fbb88375a8c0c5e41d35e4838ecbd31e4ad1b96e22eb689ae37dd50322545d39 +dist/2025-09-21/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=26e9fb3607dfeab90500bac3e9eaa23170f7f22a4738ae6b58e2e89e0c87f72a +dist/2025-09-21/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=6c9622771203bf342d59673bb1f53fe715b4255f0860feb6b19be57de2ef9f92 +dist/2025-09-21/rust-std-beta-aarch64-apple-ios-sim.tar.gz=a9c15e05b58adede5d08d7628de75d47d5c38ca60fad87dca8b8c9801050ee1a +dist/2025-09-21/rust-std-beta-aarch64-apple-ios-sim.tar.xz=bd847e7952c02312e36900766a71a5284c221b177ddef0b9cb071c5f6186a70b +dist/2025-09-21/rust-std-beta-aarch64-linux-android.tar.gz=2dbfa47893553b2868c375bedda6c4e08c33bbfa9cc96ff6e89ccf0525f8b14b +dist/2025-09-21/rust-std-beta-aarch64-linux-android.tar.xz=6b83da57cd768bad91a820d698bd18ae60586b0920950ea14105d6f23b4b1db8 +dist/2025-09-21/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=a71a57f26812c2e1529c10e2e02b8d9b466564065a8a10ceb69f22cb76e83814 +dist/2025-09-21/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=29f8789707c545e45680acd30a329fa28613dd247ce7c91d65b13a10c0c21202 +dist/2025-09-21/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=07067546648ac6e784f9d5f6455534b62b9eb5cd86805c327b98505a0caeb2d8 +dist/2025-09-21/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=f224f6b979ceef56ef8a10eaf063a6ea072d405a52ef123b720d44925b530d36 +dist/2025-09-21/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=756534ef3e62c80481e4886348bc80632ca68ec9c49045d71838dc7ef0751373 +dist/2025-09-21/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=c6eee692a79106d7733c1cbc525b241b6d88a53ee08242e494d286eb0ad3024a +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=4843b5a6d3e8b89a54ab91e44768bb023178c8cc7cd745639d9a616f2331d9a2 +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=89a808aade0a91043bef23f3e3f38d193b4f02b6556f01cbaf103c43ce428478 +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=6c09b424925e89957fb1921887b7034c3d3adf012571415b9930e1d0119bed41 +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=3c34d71b74adb145302772db2a5c565235576a14c42eca0a270b0e9e67ac9032 +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=70225984fa7ad361126efd9cbd40fe9dcf4756866b23079e4dbde5ec51f3a10d +dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=e696cf9ac5b4b0e16e3947377424515c761697541351d0924b61a37557394828 +dist/2025-09-21/rust-std-beta-aarch64-unknown-none.tar.gz=7b754105300a911c24f7483b7eb482d076f5f98e16f685215d06df3dab1fdcba +dist/2025-09-21/rust-std-beta-aarch64-unknown-none.tar.xz=8168916ec457eaeb32ad1d274ce67666d6ff8fdf662f32bc6e17656ef4ff7c81 +dist/2025-09-21/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=1ff511547185b42b801ce51f84985767d580c08f183b15033e8ae05274b7f90c +dist/2025-09-21/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=263e10d4226c5328cb41299466b82e154fa1c94c67fc3a185a0bb9a6639bebad +dist/2025-09-21/rust-std-beta-aarch64-unknown-uefi.tar.gz=eb3710b6ccbde9a265217a4e64f2c62bb22bcc79dd58a48381cc81c5e95073d4 +dist/2025-09-21/rust-std-beta-aarch64-unknown-uefi.tar.xz=b1c231790f55fd9a3dfcb9e6c946f34afb3bf96c57bb9d8615894d81aed44f95 +dist/2025-09-21/rust-std-beta-arm-linux-androideabi.tar.gz=0d12ed3a2980fce0d71d728d6c2840eac36a3e3ae93f2dfc3cb391d56c20cbf1 +dist/2025-09-21/rust-std-beta-arm-linux-androideabi.tar.xz=2219966a5e884a94c324b74872a2a4a12378d595cae10b53958190acc05ccd45 +dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=286c12975e333cdf9c7ad0b21217e3b83682047c37c1dba766cff4a201bab40b +dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=9e56db5ac01ae18077551117348fe56050426f521e41f217ac3d71c23eff4d88 +dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=f52e699638e352d9f816a21965a5696acc5fd77325ff074f93c27587292bca19 +dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=d0a4abaa489bfc6f2f7d2305e8edbd502b12f247357ba4cda541786377648108 +dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=7ee043969e94b14ea74e083fd3edb9e0639d68c2bdd7ebdb188e98b36c854fcb +dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=8ce6bafaae0e8217bdb138d104955ce7f0a747ef915aebb181cf3047beb4456f +dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=8e5c901df3f932bac7dad635bc1197ffbee4884c0a101632d51a32f23c875cdb +dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=166a1d949b6847e9bf102a89f9a417a30d8ac25a35fe3f732d6a513a7674af85 +dist/2025-09-21/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=8660de6d0d97fdffe3bbcfd5ffc7e11bbfe9c43f80067ba17845414958778845 +dist/2025-09-21/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=8b563eb032535c58c487d4ad992a12a106208861e45ea78c342e940b9356ebf1 +dist/2025-09-21/rust-std-beta-armebv7r-none-eabi.tar.gz=cd761b3f761198cc6ac64809eaa28ac299b67ba48d964298db3f5b4ea52f3623 +dist/2025-09-21/rust-std-beta-armebv7r-none-eabi.tar.xz=071864464c52c37bd102fad26b5d28f31aa9e06d34ee868a33ead297c3e20a4d +dist/2025-09-21/rust-std-beta-armebv7r-none-eabihf.tar.gz=402c044cdaad16d2b60365b6894250aa43424902c0b3f0526e849d7d0d452315 +dist/2025-09-21/rust-std-beta-armebv7r-none-eabihf.tar.xz=0028e17d0164bbb8166ddaad815874123fcc326dffef4740389ff2cb069a3e2b +dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=0b9be6bfa168756361aa5feb7802133c4cbebd3fd20d75d32a4385b716417a9f +dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=1893fcd9b16f0c334257cbc78dc416cc048d7c1603ba632ed7300bbf6c0bffb0 +dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=5aa03609f57d6c959673963b633adf194ea240b3604ba6635a6ef5fbe5c519d3 +dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=8f075eb3b1ed0bdfde8af08ee69778af2d2e896d1cdf17035657d1a84c85e856 +dist/2025-09-21/rust-std-beta-armv7-linux-androideabi.tar.gz=1499f0b4c3a4838dbd7b0df7303fbe7e157dfaec396b5ee1a6ae6a727ea3122a +dist/2025-09-21/rust-std-beta-armv7-linux-androideabi.tar.xz=f0ad36dd56abf6c03395bfc246209bce90aba1887dee81a2841f7e1013f93850 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=4a430fa04ef3d23dcf1d5e1f69c37127a5fb58e883ac52c8885125e0f578cde9 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=6fab12ecab36252aa1a4f6aa523ae1546f534548394e2585e96a869b87656806 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=4081b51910cb95fbacafc9518ee5891e1131af79a8348635c13765706c18c3ea +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=ed9a56e0e8b4e0f639afd9c6c75c4adfeddc7ce0aaa9132591f774754f412b6e +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=571a08bf8eaa522017b0aa67fb78b344590fde57ab3425575c01ceb3b258c557 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=e50728c440ae8fc9d154a3893914126d4486ca7dd197891b78531f7d5d081211 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=1f8570de307fbc59e962ef6b419009d57fb05b2e1d5fc9ade9d425e3c9977cfe +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=35db980cea1ba70003374a738f20af63d54796e9181b8cf0e9d0626e0935a9a2 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=ba5a8487dbb60851fc927dc24ee58186aa6e74d42dbf5202df7981a456b5f8f7 +dist/2025-09-21/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=f763ae3e33f4785ff505eb068ed6515aff8ffcdb9895595d23c2cea519e26355 +dist/2025-09-21/rust-std-beta-armv7a-none-eabi.tar.gz=3cd90f1a7c14a732a951026458a976fd5e833f212c6f6433f8de348b7b742b9c +dist/2025-09-21/rust-std-beta-armv7a-none-eabi.tar.xz=237f44f2b9984743f71ef0cab8d693092748fd2da25cabd3468e3e45ca20e2bc +dist/2025-09-21/rust-std-beta-armv7r-none-eabi.tar.gz=ec38043fd7877d45331414d059d0198d055ab724e84c077ca75a2902afbb2d6b +dist/2025-09-21/rust-std-beta-armv7r-none-eabi.tar.xz=f511909286f3e1cb52f0e698722bec1a3cfb750e19bb2fa781bfff225620ca8c +dist/2025-09-21/rust-std-beta-armv7r-none-eabihf.tar.gz=556365cb3ed473222e1b135be77086214f3f94f863817de4a87ee7d75456b824 +dist/2025-09-21/rust-std-beta-armv7r-none-eabihf.tar.xz=8483d2550782e253cdace51fe24249fbd6bd0b10a850c75e62dc60f803be17b0 +dist/2025-09-21/rust-std-beta-i586-unknown-linux-gnu.tar.gz=0ed5faa9e8e73f4e7b9e75741d000954558bafeaf776a6e61a4e44ac120b91e9 +dist/2025-09-21/rust-std-beta-i586-unknown-linux-gnu.tar.xz=665d5a0debd829f3682572e4c3578d41bec58b01df10cc8c71ca66d326a3579f +dist/2025-09-21/rust-std-beta-i586-unknown-linux-musl.tar.gz=c59dcbce2435a5826161d4319dcf84e128f9fa4c0bf075fab2a26c2bfb5d9887 +dist/2025-09-21/rust-std-beta-i586-unknown-linux-musl.tar.xz=13a2292936e289941be4a02903051eadb076bb44368494d530cf66832978f46f +dist/2025-09-21/rust-std-beta-i686-linux-android.tar.gz=5d23e660218e04a7dc4aaf940959619ec9aa14bf5574a554c0d8f377910ed017 +dist/2025-09-21/rust-std-beta-i686-linux-android.tar.xz=cffd08cc85df3cc661d8a572e940316da06754b61893efcd9ad3b7db09a0e6ee +dist/2025-09-21/rust-std-beta-i686-pc-windows-gnu.tar.gz=91743434207475f4404707cf7a203b46f032a041184a729ddcaeca280b2fac05 +dist/2025-09-21/rust-std-beta-i686-pc-windows-gnu.tar.xz=1f0240a71bf5a3bd74e1ae960c1aae440c3b3e32e6c62835287f78cc777f0d7f +dist/2025-09-21/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=ec5907cfb6faafcc20f3d7cdb22fd7836c9c2d7cb4871c48e64732bb7f5dcba5 +dist/2025-09-21/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=e7e8453b9dafc3c8c222455f5327fc8cde127f8dc877991688afd3c2f23675f5 +dist/2025-09-21/rust-std-beta-i686-pc-windows-msvc.tar.gz=50dd40b16e8ea85fd7ca67583d5cae80910d36531a7babe13a94cb638015a1d3 +dist/2025-09-21/rust-std-beta-i686-pc-windows-msvc.tar.xz=dafeb013333acc8a3a4181358584851b47c5f21138d8164ccfd6863b171309ba +dist/2025-09-21/rust-std-beta-i686-unknown-freebsd.tar.gz=91d741bfd158f22f4dea8bf768c5fb60ca05f5dc64cd5a848428b8dfe8beccbf +dist/2025-09-21/rust-std-beta-i686-unknown-freebsd.tar.xz=12ddbbb201a973148979a99ccbac3c65690010dd2f6984fa390fe5e63a28dbda +dist/2025-09-21/rust-std-beta-i686-unknown-linux-gnu.tar.gz=975e30f37f03afb47777a38edcd535df6729311cc0acb587d417ebff694df796 +dist/2025-09-21/rust-std-beta-i686-unknown-linux-gnu.tar.xz=bc95dd6129e90c9275e0340962993de7a0842040bdfcde9aa419f227d79dbf31 +dist/2025-09-21/rust-std-beta-i686-unknown-linux-musl.tar.gz=1c937cce8b40567851578790512fe079c0aa828374a3bb76423d685357388576 +dist/2025-09-21/rust-std-beta-i686-unknown-linux-musl.tar.xz=b42d227d63f0b3352d4d66f1198294c2f4df574c48fff794ac3483cef869c2bf +dist/2025-09-21/rust-std-beta-i686-unknown-uefi.tar.gz=0e8c239ce3b8701c4a26b46aca9a700083667ffc3228d796ba0ba6d0728c6826 +dist/2025-09-21/rust-std-beta-i686-unknown-uefi.tar.xz=54cba2405dfa2a23164bb8e7de5e0d6a6a6523f36b0763f077d2bfec1f303576 +dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=72db70ab9289bce8ace5da246432d2a00552b4cd9ebef7930b563e04d1cdebf1 +dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=5b578548a62dfd3902920719acd17550f45d0e9106049cbdc1f36c8907a8291f +dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=993fdc6d1894f823b3782fe180ac40a3ad7baba110f2eff68d9e38e8f79a95a4 +dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=b5161bd0064bfb312cf156ec4689a3f5922c7df24468660e1046798c8984938c +dist/2025-09-21/rust-std-beta-loongarch64-unknown-none.tar.gz=670ef40754ac30a2edb65384de65f028a4f8e96dca49fd0bb5eb2d9d6020e906 +dist/2025-09-21/rust-std-beta-loongarch64-unknown-none.tar.xz=b9aa6311d6a3e428f151fc6720147ea8759092545b05cad3f16b6e563d523813 +dist/2025-09-21/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=5c4c2e6bdc6d3c79578a3fd581064ba6aeb21fd9311a39a62bf58b36e7ea00cc +dist/2025-09-21/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=8e272682e98ff8b139da621d7273cf71efa407538ea176d873a1ea7e22246ebd +dist/2025-09-21/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=0781275b356176417c21c1bd1a4068fe2a42dc6de9b34695c937d5ba94b98ad6 +dist/2025-09-21/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=8747e3e686fbf41389a8ad958596577670f0626a610d380b0a775e704bc6c6be +dist/2025-09-21/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=95e8443355571edf645d75d31a33277f0d6f7161f8592ec213a407bc4839819c +dist/2025-09-21/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=4abcda8b23d17f6e6681329b54215f37cca5fc1f3383e58dd60c38220f5528de +dist/2025-09-21/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=a24d3b7db647c114c95f7da40ca2001085d935ebffcc17e269af5be636ec1f2a +dist/2025-09-21/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=7579f587df01fb55211e6a0a61ed96f14955b7f56990e679715157b06b49fe79 +dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=ea071da90747334a786f1e4784e39c11058d7f7719e498a8b6ae29672a999abb +dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=d612ed01011c1e7f24b08318029523f6b7ffb12ec38b1f41ebcedf463f924430 +dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=722b93d6c5e1f9a326461bb920fafef62cc8257ff67732c3f65ecc540782a504 +dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=5b5010f550b317facbd599fa363d633e0540836145717f35ffa0bff14ec80558 +dist/2025-09-21/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=7dea77dc10830754d5aa9a6e5ae3272e4955cab8df1e20f0784901ca6a60c49d +dist/2025-09-21/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=f212b4afaaa954809af920d3fb3de76a611d387910e6162b902fad8f38f36c49 +dist/2025-09-21/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=3f81a7134f44a87b7724a510e4cd4209ab52fb03fee3dc051c26bc0612e4b1af +dist/2025-09-21/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=cd9db852c6f7e454e94161379c032e3ccabfcdaeddd74e8f612870ef39eb230f +dist/2025-09-21/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=6da7d7c9cdc05fc3b86930d98fe9828ecef02b5b3cead51252fe9f131ab5f9e2 +dist/2025-09-21/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=97ad71f7f63f2c3b01ba822164df457d88331880bd21837a18354fffd1b38918 +dist/2025-09-21/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=9a5d94e1a77159a4bbf4fe7490019fff763daeb24dc2b8c732442275619f9ffd +dist/2025-09-21/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=dc5826fef922a6987650b491957b17693c49d1ab26b618efacbb1bb0b5a9b1bc +dist/2025-09-21/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=1b9c1cc0648fc86fdaaf23e6793fa826f3639bab9d42e1bbe2c70f19cecc11a8 +dist/2025-09-21/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=90cb9c376894a122f3872a77a653e3decf95f1eef54ba7980846165e6f34377f +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=22069b14b3eab5f3bd24a0f10185a5484022ac60fb7b2b5cb0019281bee79a4d +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=395f2975bc86a63736ba7661985db038efa5f5982459add18201c97e4b1a9200 +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=7932c8dbc9727a85dbf2ad28066cef1da46cf0ced358aea0e78a254fc1e423f9 +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=69b8ce57a0c459ca57353cd8302deba6791a19dcf54e16b8d07f76b44e3c65fa +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=27717f0b8b51f90c7e1579a2e3fa781f2a19064872133a951e60200c05db1df8 +dist/2025-09-21/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=d6cccdb4ca0856ce1d314c03779c082ee0dff153aa6bf9ea050ca3d0a395dc1c +dist/2025-09-21/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=9fd58b7c529d530e8b894a24e7f3a33d291d7305357c7cf52bbe708cde28c381 +dist/2025-09-21/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=f62de7e74f558a27bc2ef04897ad2f4fdfc162a17f21fde8efb2ba15435d80f2 +dist/2025-09-21/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=3e97b06bc72aa51aafd2d2f65b4c4d9ab08599c2616729b79dbd9c51886ab6f4 +dist/2025-09-21/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=8f149e17d654de210a71ace9db03f23bd1a80d0e2c17f8336da2b1ec2315c8a0 +dist/2025-09-21/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=ae6f3a738f1793fb9659e7613811b2ac151e91e3d8e470166b6ae615e5a285b2 +dist/2025-09-21/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=92af1bc3beaf80a763aac682a15957d82771fc619d446fb4327f4e8be229438d +dist/2025-09-21/rust-std-beta-sparcv9-sun-solaris.tar.gz=b1f5ef77e28e9ed25050b130299a1c431a851df8b11bd457393fd464a7a9c35a +dist/2025-09-21/rust-std-beta-sparcv9-sun-solaris.tar.xz=e5d744447649c57f8238c7d020f405559185d644b9739cae53c6963cdb380ea1 +dist/2025-09-21/rust-std-beta-thumbv6m-none-eabi.tar.gz=964c824519d8f352ea049766c428e6409549f7a4921c50f91dc548f2ec7f65f0 +dist/2025-09-21/rust-std-beta-thumbv6m-none-eabi.tar.xz=2f3b6d4781b21902e5f6986b75f3a0617198bad4741d4a9b957ab5ae2beab05d +dist/2025-09-21/rust-std-beta-thumbv7em-none-eabi.tar.gz=7a95faa851ac8dff7f57cfa42169018b29683fbe06dbcf29e2cb311a0c880e84 +dist/2025-09-21/rust-std-beta-thumbv7em-none-eabi.tar.xz=deb1eafb4cdad0391bad8dd0657577d4c0960fb7fad7b7552ef1e662c4c1f12a +dist/2025-09-21/rust-std-beta-thumbv7em-none-eabihf.tar.gz=1b35c7e25986065e63dfe8e8a1824bf13b62daa5777f32b140f03d0f4fe5cd1e +dist/2025-09-21/rust-std-beta-thumbv7em-none-eabihf.tar.xz=335b28756025f8454ae7c6ef6760a512f0b59385cdeacc7dca0ea1bfe5e9a703 +dist/2025-09-21/rust-std-beta-thumbv7m-none-eabi.tar.gz=2291b4c7f27fa0408f394c48390cfd6c7144db5cc4e51c8891c3bb24300b8421 +dist/2025-09-21/rust-std-beta-thumbv7m-none-eabi.tar.xz=16b6104ae79bc0f3fa6d862c72cb3f35a9f67bbea7f9aee7b2a3b0c810225c6b +dist/2025-09-21/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=6a7f167dd4c457d6185ee47dc206d19d6ca93e3e0418b21c0745d84c53995e64 +dist/2025-09-21/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=4fb366719cdadec26e88d64154b2b1b459affe5b894b426a0509681d173cf823 +dist/2025-09-21/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=3ff578be0c8b1171c5c2d0aaa3f4fc20f3a252f5adf050bd5856b201cc22841f +dist/2025-09-21/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=16c518c3daf87722a5e2556e92e97d429a06b2ed2c79380989db04ffa4791279 +dist/2025-09-21/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=b1d67e62ac198fcff25c29e731f2dca9eba3fbb09adb29db68d823b0ad63e85b +dist/2025-09-21/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=ac586b0a3cd91eb2928861ded895b96a85880851df2f3e63c2391cb38d98d140 +dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=93cfb0ceb07879366ecb4e00caf5b4459574852943363b0d6fd3293c4a0c27eb +dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=879724fc40ca55193760b3739387dc237587e91c30e334709d5453e07840d4d0 +dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=7eb1217837173f0974f7a0fc69b0e9fea484f2d457f3b193ca3b2c04ed83bcd9 +dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=c38af1e6560589be7a0733508b800e68bb5b57f2ec3c5452fb14000cf9ef2fa0 +dist/2025-09-21/rust-std-beta-wasm32-unknown-emscripten.tar.gz=9475cb292c64491c545a02df4deae77d4174d77db18a84c8db635ae6de691b8e +dist/2025-09-21/rust-std-beta-wasm32-unknown-emscripten.tar.xz=6e477e9ea0e5bac0b567deacfba3c236ceda28fab4d63c011d6bc54ac22c8570 +dist/2025-09-21/rust-std-beta-wasm32-unknown-unknown.tar.gz=eb7bf16e819eabe3f685bb8dd09bfff31d35d87cf03535195c411ec1738b6647 +dist/2025-09-21/rust-std-beta-wasm32-unknown-unknown.tar.xz=05a2bc1539b02ef314b268fc2860836c111705b872d5d56ba6ea511cb47e7169 +dist/2025-09-21/rust-std-beta-wasm32-wasip1.tar.gz=aa34f89676c72a3ce5df82cd819466631ed91896dd7a1b64fb4ca9a97595e254 +dist/2025-09-21/rust-std-beta-wasm32-wasip1.tar.xz=ad5756f4ce3e0309d04746609abdee2152fae66383b2b13d338c900b8f787060 +dist/2025-09-21/rust-std-beta-wasm32-wasip1-threads.tar.gz=36c42b952305d381718c36f34c4d5c1705aec71f946eee56c685eae56f9c40d1 +dist/2025-09-21/rust-std-beta-wasm32-wasip1-threads.tar.xz=c0285e26be272e3e832a74f22960899ac0f350dc6764701df748541ddbf69377 +dist/2025-09-21/rust-std-beta-wasm32-wasip2.tar.gz=198d4cb5195fa1e992cec8bf84716eed1ade0e9a8cc3981f3fb3cb9971e2796d +dist/2025-09-21/rust-std-beta-wasm32-wasip2.tar.xz=0fd2cd8923741931aa17d1571a5f8c20c9b0e96d74dc75ab47cd9245586bfa03 +dist/2025-09-21/rust-std-beta-wasm32v1-none.tar.gz=cef69dbdfbd0352bf781c1e59129c29c17a6c1367aa00184be309c56f8f29dfe +dist/2025-09-21/rust-std-beta-wasm32v1-none.tar.xz=a412840ff9550e447a2608a9c26ec02e969b2579bfe5c635a3af0cccd011922f +dist/2025-09-21/rust-std-beta-x86_64-apple-darwin.tar.gz=290fefcf45ff24a79459c44523bfbbeeaf9eb9bf3e7e64fcab64368fe21ed2d7 +dist/2025-09-21/rust-std-beta-x86_64-apple-darwin.tar.xz=176634d6797df21873c317b93cecfc32f415b3248139a32bfdbee83607e734c1 +dist/2025-09-21/rust-std-beta-x86_64-apple-ios.tar.gz=639916204bcc229bd5d5fd1ccb455d9a962a11d05388252c1e5e310d424f1ef6 +dist/2025-09-21/rust-std-beta-x86_64-apple-ios.tar.xz=c0c597f428fdc8f2f89e26c0e5d9debef45ec449b869ea0a738102a8727e8da4 +dist/2025-09-21/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=debfb6dfe448e345cc934e5a0d09715ca899ed9593c26eab07c58c41683113f4 +dist/2025-09-21/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=a9b6b2de9e26182f5a37a8ff56487916379809b2afe9e14d34ee55f98d526267 +dist/2025-09-21/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=2517ded939281e8722e5ca6d2cdff6a78a4fa39b5828a3048d9f25a3ec40bbea +dist/2025-09-21/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=7e8efa9cb373f580c46fa348b1f76acb46456c71fb6afea2b22d5a16b90ce28a +dist/2025-09-21/rust-std-beta-x86_64-linux-android.tar.gz=2bb9470fe62c5c1e1d31f63544b2bedb833c07c5305448e46283b48d8a575d65 +dist/2025-09-21/rust-std-beta-x86_64-linux-android.tar.xz=66a024fd9bda49ff4db5d70a2dc094708ef73c027ad0aa7dcbd7cea8449b151f +dist/2025-09-21/rust-std-beta-x86_64-pc-solaris.tar.gz=104b17a08a01593195921a56153a2b54782640f9dbf9e59c7da9f29afe3fe4aa +dist/2025-09-21/rust-std-beta-x86_64-pc-solaris.tar.xz=c3950a3a8bdd1326ab7d0ac08dc2a4f5c354e9ef6447324145cbe9fdef54f026 +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=d1d998991c9c8107f919c851d327d730beb6d4f4937a9f8dd2de2fbade1c1dd6 +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=654322ad813f9414e7ba2c5c5cb141db234d73b9ad237595d844dad564917a98 +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=6b9323f0bc1055dbf3e5fb4ec5fa09f28b7a0cd04ee8bb40e727d85d1a5225b5 +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=3ea958ef88fc3334e98556fd3bcc00264d9dd75cccf6f19f6f5514ec447d0557 +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=ddfbb760544eb8a7562cc8fab7cf313d45f490dacde3575329f627546971db0b +dist/2025-09-21/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=25f8e1279fc8647e117c6f3dbf3f4059e7ddc058cf6e02b43f499a72bee6ebbe +dist/2025-09-21/rust-std-beta-x86_64-unknown-freebsd.tar.gz=2941d17a2370ecab1e839236ba092c065cfa1b94e448a77a5851dab9ec2f1a59 +dist/2025-09-21/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ff2aae7c2e37e48f500df5876c3a26d3dd10affd04e888ce54a4635a5345efa6 +dist/2025-09-21/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=1cdbbbdf1aa9c6e764493576adbd962e004ff029b064089be35910768f409579 +dist/2025-09-21/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=8196d32e28630a3ccc1dc96d9abb3efb5f2090b7bdce9963b2579995f575435c +dist/2025-09-21/rust-std-beta-x86_64-unknown-illumos.tar.gz=bbe4419e2d9f5bee75f6c1f7b0cf272100e3a37aebc28bc626820c886fabec47 +dist/2025-09-21/rust-std-beta-x86_64-unknown-illumos.tar.xz=2fc8f8ccd022152a87a447079169340218d7541b3513eed36cf7af20d5f565ce +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=222198fa6b782010beac1710693ee1aeac1ad7eb9ac183625128de788a1a4bfd +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=b60da22feb82c21128a151013c690cdef1c291de33e1b6ada5dcc95d3bff3899 +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=31ab3940e428fe58ac584c33072be16d31edb0c16df379d9847cb904947126cc +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=8304e2e4440e0a91b05bfe58bd44e7087c28c2682a1a5f5b659e2aba708463fb +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=c15ecaa46a814cfd5fa27b29aed9e0e578a652b8f6392b916341d30172da7ede +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=08a84716ed6bc70a58841c5d61216a781b8a947bbb5fb5ebde757e537a2e5dd3 +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=5f1a8ed2093099b18cc83eddb304234f201f8ab137ae950c73329156570ba975 +dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=ebcd581394fd243eac3d683e334d73ef3d3bbaf7de28bd4082329683e2c770c1 +dist/2025-09-21/rust-std-beta-x86_64-unknown-netbsd.tar.gz=430f4b7f7eceb5e633bccafa9acf08095c1aa4b3dfaa94734fcd331b3d69ca44 +dist/2025-09-21/rust-std-beta-x86_64-unknown-netbsd.tar.xz=2e403587de5c02ba9c5f9f2515d4c9fdffde59cec28c8dcafdfe40d03e4f3152 +dist/2025-09-21/rust-std-beta-x86_64-unknown-none.tar.gz=84d695e6f19706fdd7c01dbfc4607f310e8495f57c29bad2476e00c7bb269646 +dist/2025-09-21/rust-std-beta-x86_64-unknown-none.tar.xz=6e12698afd8a6743a9a6a011ad67ab16d5a40b6dbf1d09104b8294ea95fc2636 +dist/2025-09-21/rust-std-beta-x86_64-unknown-redox.tar.gz=cadafa58684734fc43417742d9151aea36b62f82aa3cd7b858140ce31e9a6ce6 +dist/2025-09-21/rust-std-beta-x86_64-unknown-redox.tar.xz=f1089cab004cb67134bbac6d8acb09b4dd5e02010e069790e13970b004ca4ab5 +dist/2025-09-21/rust-std-beta-x86_64-unknown-uefi.tar.gz=2dbc6eec98b7d730fe2ba982d78f7331346e9018146597200340256d28c0aaf2 +dist/2025-09-21/rust-std-beta-x86_64-unknown-uefi.tar.xz=8a5896f3301a6238984114cf52f7f234bdcb712cb6d914093159ecc82904ba7e +dist/2025-09-21/cargo-beta-aarch64-apple-darwin.tar.gz=4c6172e8523576deaa6c83274dbd993338d545a794e42aca3c074450d7e7cea0 +dist/2025-09-21/cargo-beta-aarch64-apple-darwin.tar.xz=5e8978daaaed1304e94c071ab5414ce90eb9c7bd1c4f1c8c5f4ff515f6558851 +dist/2025-09-21/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=49cba73291916ddf2e4912d4ea02add165f2786ad7f7b8885628d92579cbebd8 +dist/2025-09-21/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=d60a0a176b7d15606f6ee31b67d4a5ac6735e5a0b012022e9212fe723bddec48 +dist/2025-09-21/cargo-beta-aarch64-pc-windows-msvc.tar.gz=dfd4aa83d38a6236789676ef02c81382f0741671ed9a973cd74d37c65b3f111a +dist/2025-09-21/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8ab6cd565993b58c6e2169bfb468c441dd385c5336081c45f6a60608522ce549 +dist/2025-09-21/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=eb361e2d12c90c9380112ef48b81db1b41f04b4ae08cd061fe1caa46cca9ce6b +dist/2025-09-21/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=e9f4c66995b8e955e86a67c44fd8d7f6e7349645393bfa605c6c1bb0afc7b930 +dist/2025-09-21/cargo-beta-aarch64-unknown-linux-musl.tar.gz=dc88806e5ac4004a9a3cb24f0c850fde2c22b0e38e6ad84bd570069043485bfc +dist/2025-09-21/cargo-beta-aarch64-unknown-linux-musl.tar.xz=e103f1d074ab105d03a88066363d2b103508ec95c18cbf8b1f92d0f473ddbf40 +dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=e35bcf36bd7578cdbccb60d554feb19f8376fd41850e4e8046e0b2f931040c01 +dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=ff1b46781284948aaf8c8f582203877ffda5a78d86c266bf724fbb08503a6e80 +dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=dd657b5eb50264c90fafbd967b20768d9e4df14ef179902420b3f9a3e2145271 +dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=3f821c437963aec534cdbd686f719eb86bfe41cf254ed5395730f7827d45a68a +dist/2025-09-21/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=7874b945f3d77e2a8ca308e5400a2411ab4f615f45a036bd9fab8a74434c309d +dist/2025-09-21/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=39523f09c76473d10b91ee946523976e01dc337d2af067f08168f1d9cb44226a +dist/2025-09-21/cargo-beta-i686-pc-windows-gnu.tar.gz=43b095acb25cf5c0dbfffc6fbc864c2b2415251931b149b282d5e70844fc2c50 +dist/2025-09-21/cargo-beta-i686-pc-windows-gnu.tar.xz=6cac1a1a6d74765f4233908920d295761570ddcd8cf3638bbc8f8eb427084b92 +dist/2025-09-21/cargo-beta-i686-pc-windows-msvc.tar.gz=7f4314596e6ea01a35b9e2e250227a74b5d4bd772ac8d33d12bd44f8c11b37e5 +dist/2025-09-21/cargo-beta-i686-pc-windows-msvc.tar.xz=eeaca23bf3cafbd01cdcef890d02ecd622d3ccfd6d9830f1e599d29acfa371bb +dist/2025-09-21/cargo-beta-i686-unknown-linux-gnu.tar.gz=e79e3a25bb790c5f6ed9e81a0559a55750a1a3e35250f0fc5fd92c195625aa28 +dist/2025-09-21/cargo-beta-i686-unknown-linux-gnu.tar.xz=1fe31a0e463736a9ae90ef11c1e3c7b7972eb82779ecdf5b6bff1f643684a014 +dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=917db09ef343b6702c1410c2c68070c4bcfd90f6951591490a6a237290a4aed3 +dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=222010f8428a3d165801d95a820b639ac930747af3cb4a42a25548330585f73e +dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=7e949bc169a58e450e58088fd716aac9a05f5fca0790d94dd211ce823c2c5d36 +dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=dfeb99ac76e18160aee7ff1c878b44b9fdb725f7be28609e637bd372aab448a3 +dist/2025-09-21/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=523103d950944aed52578002dd372207b3bb38e4130b4b11097b03f7d55345c9 +dist/2025-09-21/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=db5c4ce1a1a4d87cca4fb64a6c533cc5ab1c94e25e69b26e13808b0fa5e853e9 +dist/2025-09-21/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=68479082544f7f68a2fe073ed3d35e1895643f8ab9abe9d0e968efa9f342de36 +dist/2025-09-21/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=8878cf473faf120efb80bac0564b193f3baa14a9027fb4c060574e6fc921edcc +dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=eb37177cdbc9e2b7f9b74856b351bb764e5c2603366fd92a5c863cbad26e6940 +dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=92734c444e0156e16c8d8998a8432b24d9d01b82da15123508d0002eb008b9bb +dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=502d5d2ec61d9fcd5b92caa0b4f0aaa11f27fccb7ec4736e05beca313f306585 +dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=e00bc0ef1784b2f7b1fdbb757cd50342cacc49f7f5d2d3f7b36f9f4eca23882c +dist/2025-09-21/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=3e00c4c0d64977ddd2fcece9407a01f92ec9b44ea37d72ebbdb77cf0c532163c +dist/2025-09-21/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=3be5932af030c758a84bc09cbb1c2bc5abecc4e7d8a82de58c2a069ad36e737e +dist/2025-09-21/cargo-beta-s390x-unknown-linux-gnu.tar.gz=aefcadb257bbcf93dda58526390962316b4e579707c043c45e52bfd4d7a097dc +dist/2025-09-21/cargo-beta-s390x-unknown-linux-gnu.tar.xz=3d163f9fdc2b8b0f5cbbf846caf1dccaee07984d8783250e8988ef447e53663d +dist/2025-09-21/cargo-beta-sparcv9-sun-solaris.tar.gz=bc1692d8d75654012a823adb40b87d3b5721b19beb49a30b404a6c78f431c944 +dist/2025-09-21/cargo-beta-sparcv9-sun-solaris.tar.xz=ff7f36d7832b094b9ca2132df4851cf0ca50c9fc2de3d55bb6c75b46dd028f10 +dist/2025-09-21/cargo-beta-x86_64-apple-darwin.tar.gz=1a67e618eeadf362e868bf2cb35c1a312db83d1a59cee38f61794e45cba3ba4e +dist/2025-09-21/cargo-beta-x86_64-apple-darwin.tar.xz=28c0ae4f78f37abe27a3db5e5fb8c78c51a98b71cd0c4c69f9256b5d4064e78d +dist/2025-09-21/cargo-beta-x86_64-pc-solaris.tar.gz=c2da94328a164d889ebbbcd5f403068126e8f28ebc0c4ff7bf5cde1e8cc380b4 +dist/2025-09-21/cargo-beta-x86_64-pc-solaris.tar.xz=4fb30f600f8a10f43bfbf4361fbc7e906217007d46d65731b1bae0007eaca783 +dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnu.tar.gz=86e23a551906f961a8a05b50185185de683f824a69bc739c3786e4f2004d83f8 +dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnu.tar.xz=860562d5c50c60233d088886dd22b23c0c40504107b04cdfb51506c631d948ba +dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=b568148f13e609e6cbb7e2b424c13a8b85126c8ef84f3b884043aab204352615 +dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=7983768e6c77334eedd563cea4cd51cbf85d5234d1952801783016f07f1d6ce7 +dist/2025-09-21/cargo-beta-x86_64-pc-windows-msvc.tar.gz=839f7866c75750a5fdc0e7b9fdf37e0b60e71be916b496a9be3ecedc87473c2c +dist/2025-09-21/cargo-beta-x86_64-pc-windows-msvc.tar.xz=5d0e3c8e9082a00be80cc3924e12b7d9d067f9ecfbe14dd1e1bfadff55d2bccd +dist/2025-09-21/cargo-beta-x86_64-unknown-freebsd.tar.gz=8c22ee4fb01955f20d04dba271b44e69718266d70610fbd979565d95df316e6b +dist/2025-09-21/cargo-beta-x86_64-unknown-freebsd.tar.xz=6356f4d133c3820736f82c4eb2857548b5255af4ead57f1f8e66ebc6aaa628ed +dist/2025-09-21/cargo-beta-x86_64-unknown-illumos.tar.gz=43523fa8da79aca1e5a618c10ea031404250cdf1a41b0da369ed6efd05c4190e +dist/2025-09-21/cargo-beta-x86_64-unknown-illumos.tar.xz=c9a1b43c762b3658b0fac5145c6314a1c9e416d025ac22958fc0809fbb24d1e0 +dist/2025-09-21/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=ba780983f067e7dbcce49dd4d39a0d3c0002dbe7dba73eb2a98d7eae17f70931 +dist/2025-09-21/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=07aa13a5411a49238b31191a0797d63b74120a1fa9b5658a67f6c1065271c30c +dist/2025-09-21/cargo-beta-x86_64-unknown-linux-musl.tar.gz=96e6367138d6ff9ae2ca4343f3d5277b5fce39fe6909cfccdd57f1867eb9b021 +dist/2025-09-21/cargo-beta-x86_64-unknown-linux-musl.tar.xz=83e6fb5196805c9bdfca4e80e76e185a885da0820108e98e1fc7ef4aeea7f1e5 +dist/2025-09-21/cargo-beta-x86_64-unknown-netbsd.tar.gz=422fdb2cc97767f235d6abb29dbb0e802b320e11c743f794f8ad13160e4c7c7c +dist/2025-09-21/cargo-beta-x86_64-unknown-netbsd.tar.xz=75c4aee9a720fa55ac5e80c58a890efbf88c57fbd2c57043b9f29bdbd6ae0e3b +dist/2025-09-21/clippy-beta-aarch64-apple-darwin.tar.gz=de39b5014bffa7e20ae1f981616664703828428b6b1a74a6fee80fbab446e74e +dist/2025-09-21/clippy-beta-aarch64-apple-darwin.tar.xz=d5ad3181f6978604f725db8607daf39ee20cbbb6ade35bb50ae7b032b0b62e9f +dist/2025-09-21/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=a4f5538776b2f1f31bef81f37615d9bc3495080174fe83be0c549508923c9e9b +dist/2025-09-21/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=a78a56cf381483703f120c596d6921b04aface91847310e20da53aa887a2e603 +dist/2025-09-21/clippy-beta-aarch64-pc-windows-msvc.tar.gz=06a6ee3aa204812322d0b9946ea31dbc5045e59253891fea7e079d4c7e1de894 +dist/2025-09-21/clippy-beta-aarch64-pc-windows-msvc.tar.xz=a4f0add69dad90f0dd7c47966b12f6cb7a4c6e34cc1b44e4a816d359659ae012 +dist/2025-09-21/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=9d88fade821957052581f65765ae84286eee07e0985504d5a7324f615649a506 +dist/2025-09-21/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=0c278a9aaa1ae41bd9bd96f52fed50b1a11a65822024fd01a9eacfa3aa8f1de9 +dist/2025-09-21/clippy-beta-aarch64-unknown-linux-musl.tar.gz=7d04aeb77402ca2ad964b6430ad75d0ec08a68efb505573f5e134664a5aae044 +dist/2025-09-21/clippy-beta-aarch64-unknown-linux-musl.tar.xz=d28207a804219edccb110160ffdf1c1525248ac225df89f4d11e3538a5dd0dcb +dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=2433be238da05b6dbf44a74537e48a1dcd96fc03a8059ab78e553833546f1b97 +dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=74d5920785504fbc0c1e0237a4ee4e8355ffeba2c4bd9471c38d44e3ae52ef4d +dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=f83867145c740302ad81912f8e39433aac19fa5312f14d35aee2b59638660299 +dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=420c7b7b6cf54eb27fc3446223ab03a3f90628b47d6b4ae66e432380b57661ad +dist/2025-09-21/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=14eefaa624591a49d6d2a3af9663ea4f3aca804d3563f668c734d9e18cc0b39b +dist/2025-09-21/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=14e8371566643fdf146117d06b9fa77aa886360d3696d9e43482f288338822b6 +dist/2025-09-21/clippy-beta-i686-pc-windows-gnu.tar.gz=b7ceb33faebadc67294e1df3f08d8d9760a6a17ca1ad30f26da3c586487a14c6 +dist/2025-09-21/clippy-beta-i686-pc-windows-gnu.tar.xz=f0a3f41a65d90119a4c66c6a2007d1f1a75a24e86d9a572837c4410b02af426b +dist/2025-09-21/clippy-beta-i686-pc-windows-msvc.tar.gz=dbdf0cae38daed8bee11eb63d7c3f1c5d019777c238495149baa5ccb10af0f37 +dist/2025-09-21/clippy-beta-i686-pc-windows-msvc.tar.xz=adc49c09b72ff46d3d03f31c8c641675af389ba99c4c517149a10ae471c37c25 +dist/2025-09-21/clippy-beta-i686-unknown-linux-gnu.tar.gz=793ace0c8927a48caf443b794de097895f9e503299da07da13238a56ea8ac07e +dist/2025-09-21/clippy-beta-i686-unknown-linux-gnu.tar.xz=22b9b2b27d0b6b1fd88d67b18d34a4a91207e6b64ba8d47dbfd0c58763d429b3 +dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=101437e0f1bdc8ca07455d92c87bc32914a5047f6c9d7b7ab9e34799c5d4a5a3 +dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=afd8c55fa82482a852b564511c4fdddf12abbffc0bbee1b0b4155fd1d6c04105 +dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=df33c329856ed057d069a479181b4fa97fd4a11d109abfa32d6b46c36215e6f3 +dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=ca8451dfcb5b919c1a6510616c8e93dfb15914e689cb30f7debf4c1a4aef58fe +dist/2025-09-21/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=1b738f58256186f0b530375ea2da804aa1834a908412e56767c9a44b134cfd68 +dist/2025-09-21/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=93333f47a041d4ddea4fd9ad3fb3ab43c40fcee4fabe6405190fa26d6bfe3e2a +dist/2025-09-21/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=2f0da38cf8efcda85634249df5398bb99f3b34982fb4509a0a3171437d809ab0 +dist/2025-09-21/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=fb3d68e09e40cbf7d6c330c3866c37c759ed728c1d8cbeb6e8e834f6a1fce1c9 +dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=7cb5fdfebbc0565e2d883da09815dfb626104afe39c01b169a919a82f62df607 +dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=d6705b1e6722e3faf5b863fb319cd811fcb27f4a564e633f164f02f8699c9255 +dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=ea33f22a67f7c8354e7421129bfcbfb4bce7d909fcfa6a64a3107d82be69d213 +dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=081e303cf123ddc162633d4d1e3adef4e6fd39598f60ac9dd75c76230df39ddb +dist/2025-09-21/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=76ac5dc8b8284437e5fe81cb4978460a6aa5c4a857c4f14246dfabf1831998f4 +dist/2025-09-21/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=2ed67a738a07e9d07c1db7789cc5ebe7af033334670fcb1ce84441b9e474ec0c +dist/2025-09-21/clippy-beta-s390x-unknown-linux-gnu.tar.gz=d5a7e6cfdd099ed18e54d88dc1d740b90d1f7d2f22d1fe6ca7960b7319b0783a +dist/2025-09-21/clippy-beta-s390x-unknown-linux-gnu.tar.xz=78231b81d8e612a4c41828428ba2e9af926f2119308b291c8ce81a5233c3c6a6 +dist/2025-09-21/clippy-beta-sparcv9-sun-solaris.tar.gz=a1b0086259586a26f6ca65b02adea83b953989a508385d58fa56c7eafb770227 +dist/2025-09-21/clippy-beta-sparcv9-sun-solaris.tar.xz=b58151b098d58b19bc900f72813138799e2e568a5ad3038528045e5ac562606e +dist/2025-09-21/clippy-beta-x86_64-apple-darwin.tar.gz=62ecc253fa747ec67ae11c7a1672661cbac7d78c1001654e17ca5c0e3bd72d91 +dist/2025-09-21/clippy-beta-x86_64-apple-darwin.tar.xz=b7e9785d3ab00163a0070b7772a4354e9503cdb8456d1a2b0708920658aac614 +dist/2025-09-21/clippy-beta-x86_64-pc-solaris.tar.gz=783d47012b943cd4497c2e0e854cd7727b0957518178165cc1cbc4dc5e6509ff +dist/2025-09-21/clippy-beta-x86_64-pc-solaris.tar.xz=94efccbbe73b2f15f5f86c90324b3adbd1b58bbdb81ea9c32d7efaf067bc6795 +dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnu.tar.gz=8b12cc5e7b9b7e0b234a29886c81455878e806067c025cf3d26eef4a52e08bc5 +dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnu.tar.xz=35fc298fd25949b491c54bfa2f40c963d7ca530b65ac8e52031edf17624b3d05 +dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=824e1590e12bcad69b43912068e27585466fcc5cf7a2f92f41f727aa39cbcaad +dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=6fad67e180d0eb0d551b2101dc27bf6846ae2840c63d1ef05588691d055e3806 +dist/2025-09-21/clippy-beta-x86_64-pc-windows-msvc.tar.gz=1353d8c3310d53576d94aa744fe0844b5527d8b54fe43a692042be78b0fca6f5 +dist/2025-09-21/clippy-beta-x86_64-pc-windows-msvc.tar.xz=a7ca6fecd77dc44f3102abad7fbe1fa3846d9ff6ea98a25d4c3bd703800894d2 +dist/2025-09-21/clippy-beta-x86_64-unknown-freebsd.tar.gz=33b5f8dd6a0ef045ad19df4327259a468ece00b250d9fbfe1be7c0f293f874ce +dist/2025-09-21/clippy-beta-x86_64-unknown-freebsd.tar.xz=1bd56197e30fc325c7482aa7a42006a7ad9a0ffad9f3d74d209e98582d2897e4 +dist/2025-09-21/clippy-beta-x86_64-unknown-illumos.tar.gz=0ba3c497472c34de44bda2485d3b964cdab83e3700b44ffd8b41037ccf59a932 +dist/2025-09-21/clippy-beta-x86_64-unknown-illumos.tar.xz=040302a04decb3cfcd599b329db3f17e5f96b7aa4b8174d6f2b17ba19c991853 +dist/2025-09-21/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=21fde20675c5f786b5da4f1be39785d1106f748d88a6609fd4976bfe372e6817 +dist/2025-09-21/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=217255d6ea157f7b06aa66d033dca6239bbc296bc14ff3f0017d5c68bb4d1022 +dist/2025-09-21/clippy-beta-x86_64-unknown-linux-musl.tar.gz=7be4202a658df30aeba451e6dd4f740068dbcc769fe0eaa9a7eb8cb2c2e264ff +dist/2025-09-21/clippy-beta-x86_64-unknown-linux-musl.tar.xz=d2cf3cfa0c5c67d57867586709c274e320c1a76418ffe7dcf65b271448d4de06 +dist/2025-09-21/clippy-beta-x86_64-unknown-netbsd.tar.gz=91f510466f2a8606efc746a5be209a1f0ffe1e20b803f9c54ee91786053cabbc +dist/2025-09-21/clippy-beta-x86_64-unknown-netbsd.tar.xz=2c17d3a00885495f81cb8606ceb78674f63396b3c2a0b3415bb2e62ab39f9d87 +dist/2025-09-21/rust-beta-aarch64-pc-windows-msvc.msi=d5e39b0a1deaaeaf956e57da755e16255b265e80722428625783e7be0835cbb8 +dist/2025-09-21/rust-beta-i686-pc-windows-gnu.msi=edcb39b92d1e84c7d6b0d2559e37be673795a14e807e77e40b32dcaac8b9d415 +dist/2025-09-21/rust-beta-i686-pc-windows-msvc.msi=dac7d64336aa8fcc77761910392efc845aa2137fff8be8df980b02d48809bbd4 +dist/2025-09-21/rust-beta-x86_64-pc-windows-gnu.msi=44e1e8298714b11bc7cc44184f2b20aa39fbadc23f8b2b86005e74879b8430f8 +dist/2025-09-21/rust-beta-x86_64-pc-windows-msvc.msi=4c673f514c7f0f9bf780c2448fa4a4bbe4e4db618d6a9931bd092a6116d432fa +dist/2025-09-21/rust-beta-aarch64-apple-darwin.pkg=4a23353da7a58deac032341011c7bdb78f069ff4bda97d837c67e54454e6e1af +dist/2025-09-21/rust-beta-x86_64-apple-darwin.pkg=5e02da3f6ab8791426060ca40ac7c719451f6f5acba06ec27c273e6f2590cad6 +dist/2025-09-21/rustc-beta-src.tar.gz=22b0288ca9f949cac41260370afd4e6e487c1e3430f6aef23340b50ec4e4ea9b +dist/2025-09-21/rustc-beta-src.tar.xz=31f4b8b4b3471e7063da5038fe5072e44293705ec65b2c272f8d4cdd37875ff1 +dist/2025-09-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=78627de068d788f65482cdb2763b27fb7570a197b97056ad16f9f6117fccff8a +dist/2025-09-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=d6c4252e895d303337ce1c8edf2fcfd02078b81007e785ff7a15f773a1789e3e +dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=d65ef7c1348a74dc1b042c30281ec57c2619a25bdfd8151223415f9d6e067fc5 +dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=dcd986e9560c45eae6f1d0ee0bce9ad2365d101f4c9b792062557cb26a26152e +dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=8b5a164ee78ee9bf76c1ac9d95f63743cc0b05cff9823d42b88d596ee34c9b52 +dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=da675f08931285b2d59be0b8cda46f7489855ec9cc07a608d17e4c0f1e6de486 +dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=7a1b11c66f3832e0ccd390441a921cd50a25ae87e641bb856966fd81cd3d5d59 +dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=5f6aa12529624b66f1de643afe6805cf5484c57e3a7c791f85023d28b590dac2 +dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=0cc213fabdad76e6ff699f2f0462c8b3dfe5bdc6b14131fc2c87d915a8fdabbb +dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=ca84ce0de6d11b69ddc691f4edca1474e66b513f695fab738374942d57ab8b83 +dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=dc0f391a0ac09a8ae2271443584dc8f1338bc0b89b50ee82d47599912fb74c52 +dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=d4bebefbc157ecde2fbf7f7ef6a6d8c703d264f56e2ca8a80b7c241b8e14f862 +dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=672fd91b880195a0fb2eb294129c0ec465aa3be217451fd4b835b2c3294d4c1b +dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=4aa45c993b82f9d9f6b8bf79db2d04acb83cd70147c9ecb1804a3c8258a6c022 +dist/2025-09-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=3a8fef72bf471ea1c575c3a6d3a0ffb957fd862f55afb0d40b39c85ff7fc1f13 +dist/2025-09-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=08854b212790685caa928e37aa7fe50009590050873c390d2999d6b814bcd2bc +dist/2025-09-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=e99f0d4b314c59b7564e85be580477e751e46acf30752b970c36aa9719e10995 +dist/2025-09-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ba3b9a0e0c44c6edc1396915034efe9e7f59e0724271fd6c1fd4805382e95677 +dist/2025-09-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=40f42081d1d2eec00bf49f62c12d75e5e5c345e2a4d8da4fa0741239aea72218 +dist/2025-09-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=1b9ef88c7ea98880835d8c298625e2bdd219af46eabb18b8c18c92882d81d054 +dist/2025-09-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=2ff88b8231c70044e9b35c3855515d143aac1b3d7a82bfc84833f76f45539c97 +dist/2025-09-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=e07bab9116c10576b7ab01e26af72bdc97bd34a56aa2468e188e58864b030c33 +dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=766a69e69be097f710a7c175dbfa39b20970135a6fe420457191e095de5fab1e +dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=ad4a38853cb9e6bb6029dbb2ffedf4b49dfc7cb696edbcb561b204bfa89fd8d8 +dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=f08d5f5ac31fda285b81069709a74eb382450543c4d22289980a9ef94a473fac +dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=40bb1e41db10d4c6b22e46c0f8b5fa1a6ad06cd5f3102c189705380383444323 +dist/2025-09-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=d3bf8c8c186c94a0190ae73386839e53dd6ea76cd81e9132438fb7f245d955c5 +dist/2025-09-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=665dce6b1a464e1969e3901d7bd293d35a85d5a50ad976600566dcc2a9c46b58 +dist/2025-09-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=a28c82ea8a7e2bbd61043e89994cf2be71ead745b3fa782d0653a99fd81bfa64 +dist/2025-09-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=3c5cc78a7e311f73c39030f42b8f1d3dd0e54e09f4d636be6a581a829f15483d +dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=372c476dc902ffb7ebb8ab8934a89d1bbddf9df9c810bc6d90d3afab984b8205 +dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=cf2e738c44ea95d71090bc3526d8c7c70e4554667449f4705614c93444e817a9 +dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=47f215d0c639f0a4bb67423c65c5b87a06cbecd47ea53484b57c9b7d87c6791b +dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=9085b66b2e8e3460f0993896ca3d684395001ab4ed37a16947ce1d15d5aa224b +dist/2025-09-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=0f07ac40b25eeef46a4f4a0d34cf50c9336407f2d7f23c05c47fe35f3a7a1d49 +dist/2025-09-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=ce08e9b33e75eb504f28ba23e1cc3003c0aa503fbdceb04271bd533613713160 +dist/2025-09-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4e64fc0ec680a294308f897131f8ab185872dc68cd1312fbe1a306ed6e53ba26 +dist/2025-09-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=b46d423db54a90944276cee172b8cf0ea70562c01537c37c65f3ea17c13a47fe +dist/2025-09-27/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=3a84501e05cc7430f91903dbb0de0946621d05c095459c47dde3cf7e662e771f +dist/2025-09-27/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=0107d3c129e1a18a814d5c213b6445aa7ecb7dd95507641d2cb3d0c39293818c +dist/2025-09-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=c58e0a2db9b3933539a20614b143e6575f6aa1459ee35af4d67210dd572e6af0 +dist/2025-09-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=0cd4d7a8cfedc2787bacebbb2fa481d5efe3d56ba476ef8799c34325c40283e1 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-solaris.tar.gz=ad3fdf81b7b00ee670b05ed2bdc05f79a9c066104d797dc7eaa4d767dfe2eeae +dist/2025-09-27/rustfmt-nightly-x86_64-pc-solaris.tar.xz=df79594ece4b8753d8215672004e9071a8c10c8ece8c86d1d3608c8d7c3f0486 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=cb800c92a8f899d148adc96283818aa81c115b73555c047e07a67d738e9cd2c9 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=09f6d95c49725c36bace10c8e119d6850dabee1dcdebac264074e296f9e8ab48 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=e061a3925b95a99dffb17d34c85803bbcac4604f95da2674872f0725d82cdda4 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=1090793fe09cd2ec4c54063600c1999f5e53a9ddc5c5d74e4f5e85dc6f2ef98f +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=f3978135d4a9bf2537625e38866fca74ca1f0655fc9fae736bf87d257d6cd0d5 +dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=03814722fe9798b503ab7d8284c67e84cf18a9a2f179fe227e3313d0ae3e2cff +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=d5a477ce3f220016f097f8949fc2eb1c700c612e97105804156e82264e7ba787 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=0fe90bad20ee599e4e57c46d4bf700c5775c484f0a8bfb2ce4957d2aa2df90cb +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=6d48ed9c944fb01655d4025c4aa3b719813cfef040fecff1f59b8b51a0b9510d +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=34d5a066b9f5bcef81b38badcc96f295150c2b2a96c35621235bdcc54ce92158 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=61a6a8feaf0490e3db169e86e85989538bff994fb76481a81a1ae02222c7ba59 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=cd94e20b4985964442b080454c2b628bcb435898e50bc2de55799cc51cd75f16 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=c2912263d24904ee5b1014a98d5b349754a6fa1bd66498f607cc62ebcf903cc3 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=8ad5b1284c91798a01fd25b3b690f88b55026e109471e759d4cecdefd1f83a39 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=e0f52a6511c36c2ece175bc993861cffe0cc72a2e1b56b1def246e09f70d3a75 +dist/2025-09-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=229b92a2c0ef8ab1ac588858bb371ea0ed3449dec82a11ca7386df6efb2f65b7 +dist/2025-09-27/rustc-nightly-aarch64-apple-darwin.tar.gz=de7af74b8c91fb87b20df2d65b536fe6f49cc632b1f0c52a1e65a215fd5e4a06 +dist/2025-09-27/rustc-nightly-aarch64-apple-darwin.tar.xz=7a3e8c68f0bf4d393393628bd85d22242eee59605e3d56e0e94d06163ee2d4e9 +dist/2025-09-27/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=2826132a82eb5adaabe2fdadc76ddc21460834365085ff2a113d934c11870a41 +dist/2025-09-27/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=47980ea13cb887d85f8e501ca2b5d6e4b77ba8f229b2cfb9a1f28426c60d87a9 +dist/2025-09-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=10dc98065c0b19d737ea93506df1ac399c33190edb3f6bbc51d6c1697e910f8a +dist/2025-09-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d791bf9c54ccdb02da34e408aa93e0680f19a3bfbed1e5dbd61b57f1e1f38fdd +dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=ba6e33f6efa2f5a97790e29bb72c89bd460d758244dc9dfa4684e01bc75b6656 +dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=4e38e862770ed0215720445e56fb027570e4f3c09d63a7f68cdacbff482b4cec +dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=b126fbef234c0b67df42fb0568580b3d95ce98b7346095c3762214fcdece14a5 +dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=07af694d0ab0b55b18bd437ec9edb965f451f1bbb8334e1667f87d1d8e8354b2 +dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=6901b57b0fe7182a45b1934e1d7a006ba353daf114ea7601563caade4de1b2c2 +dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=c1af7bcb75c1ce5975e606aacb2d3decaf7a8470cd347d4caf75f11f84d3122f +dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=8f29e493bd15175ed2a72d50857cbcc07992194c0b38d2b0a4660217b04b8276 +dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=fe478ade162b6b5d33808f4872de54e0b9dedd84e9e420480a370a2555d28cbc +dist/2025-09-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=f03a3368f41969d061a9ec2e87af512c346f9e82b6286eea65dbce33de90391e +dist/2025-09-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c771ab34e5bab9344be3dab9315225296e41b3fa70cfe59fd3e0b287c4985fc2 +dist/2025-09-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=9f5555633f1f1462285c0eb39aa42d0beb45cdb3b45c524483e4e4c6b76b6551 +dist/2025-09-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=ae8c171fa20a49d7323bb5e6a36b262947caae260adb942204206aada00bcfaf +dist/2025-09-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=2b50b6d9027d5b480dcd2693a551bf80db7d3dae802bfd9a825b68a50ab022a6 +dist/2025-09-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=d4b396eb0256cd62718751f3a52498dba992ba063ed77e5d675da8dc06a6751e +dist/2025-09-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=f74acd9ecd35d10040e388d5224a9c88e66348dca09930d89068e87a0371a7d8 +dist/2025-09-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=92bb07e968cbbbfcf1bc7d0ecdd1a088b8c2975691bbf6ed846bc69708e34f13 +dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=7b756904c495de2d37993d71fe1e70e182c232aa408296c6ba05f71a94423406 +dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=522114675fb36949b953491d9a5fa0db39d22118f015f8ce92a120eab39244b0 +dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=2309e49988ec8c35ef17f7293d6b2a787589eb38bba217a8f9429446713cc2a4 +dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=820950e1cbfe6d973e1835532f9e201fe215d149bc415ac7ea011b16bf6b7bc8 +dist/2025-09-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=6fe053425d6b840c72352a88021c3b2b6deb389986575cb5e7b8c5991e86d039 +dist/2025-09-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=d62cad3e6a7dbab7cbefa493e78a0b7d7e8f724dcd766ae03b6715c325594fe5 +dist/2025-09-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=8bc6b3d521f5117bd3f9321d9d086e928fecf548be58edc71b257269e68ad21c +dist/2025-09-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=d08a0ed4adb7fdf451d39c1dd56171d6ce345b10cf905515c07ac5eb66f7d030 +dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=02ac6a9c23c1dfaf12e26b466bb33057787c28f2bfe8503b998a5d5aa55a4370 +dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=77709b47a9d99657e03c77f32183b2127e75488df59cd000ed20cad5868afd5d +dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=b811bfae5380ffe89e2f48f6c0e6f293e8db33461a1fda94a85759d3464100c4 +dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=bc41de8c0c65d912b5d6be06f3b12b3e4be1c20c1dc6ce1b7f5226e7d3ab3ae2 +dist/2025-09-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=110b42065218c2607b01edb83d41425176d7f065fac52c5836bed0d2215fc5b3 +dist/2025-09-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=97e3a41b0718ea14be4b7320aa4efc7f19b3feeabc7aa9079ce4ea487cad8064 +dist/2025-09-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=4e1fd4ed9df5ae921380e3396159053c87623a9ee1c7bcc1f897674c9165714d +dist/2025-09-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=1e2833f165f7b255731fb1d26bd6026f5b6152ed91ac70d8dceb4f692ea9a66f +dist/2025-09-27/rustc-nightly-sparcv9-sun-solaris.tar.gz=8113fa75d9ad92c411c71b6769f2af4450ed3ae285be1ebf10afe022abe52661 +dist/2025-09-27/rustc-nightly-sparcv9-sun-solaris.tar.xz=150128e8dde149bfbb2071cc933844ff87931cb856939db922eab98230ab7bb1 +dist/2025-09-27/rustc-nightly-x86_64-apple-darwin.tar.gz=727f7ae1f1e5fe51a3722105211cef3eb92f792cd054857ffef7bf858d0963cd +dist/2025-09-27/rustc-nightly-x86_64-apple-darwin.tar.xz=295672b0d6afb6e80f25dfd6d1643414f976eab6da00a5babf377ecede580e56 +dist/2025-09-27/rustc-nightly-x86_64-pc-solaris.tar.gz=3505cebc0659388e110d1e55a5eca94ac945d75b3320f16ed9ded08629a91638 +dist/2025-09-27/rustc-nightly-x86_64-pc-solaris.tar.xz=515d5a5046dd2c4b3ac2b21a6dd4bc834eba20d08b902ed396e0b62101978210 +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=838ce3f625b6dfb87f0271770515988d3b3f1535d75353b8f0f4a69074c1ceac +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=186eae6c01ecfc67facc96ac75d8518c31de1bf8897d82bc587941c3f686f4c3 +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=76e5d092c78b663c2d75ee9d95f6c60d1ecb509b440312f4a8ad333d58de54b8 +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=69ffcda8f985b3c5b78b18f0eea037890f2efc205f0b7cc4b788f1b35a3b7eb1 +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=2b08c563daa21d817bdac9c8dd81021a80967e7e671a312c2990575e3622b928 +dist/2025-09-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=2e54f6a6b6e096be1f717361d2e474b2ca94957ee006d5fa62910ff3d85cf05b +dist/2025-09-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=6e00949c5d3a2f0ba86f1d89f54278f09e58f043cfd00d1f5df984835228d28d +dist/2025-09-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=46d9945d5361b758448454c4778a42ce01b4cff7370b9988d5e7b2c7d889d24f +dist/2025-09-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=83f3d4d069729a72da4b96067400b812367e0a81284bfe3cd73b1939fb81db9c +dist/2025-09-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=110ca4f2630368f1c94084332d825964f3852bc9e70db8ec738de2cd4f450f2a +dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=e1ad313cbe777997222bbdd4b26a5b4c21da50b6378e434501c58219137dad77 +dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=2ab176057835fabd55e6e2372b036c245be44c0705198557ef2a16d187ea9457 +dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=3e643ce549e9db3768c478b37f088afbf9b2f63dc0275bfdf7c2cbb48ac4fef8 +dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=597c47ff84de68f317b0672d5564a3121edd88cbf5dd3d27192d133ca4ac05a8 +dist/2025-09-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=5f948f48d64420119f1bd1a90952a04cec074ca45bda8d46f020163cb2809016 +dist/2025-09-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=977612fd1ed20d57b95e577dd9b3632209fb1f376f46c66467e2a2ccdd7d29f0 +dist/2025-09-27/rust-nightly-aarch64-pc-windows-msvc.msi=b39edbdc83f4329be0e194be1b7e002e764153628bb107bdc77e6f8c2331abe1 +dist/2025-09-27/rust-nightly-i686-pc-windows-gnu.msi=d023b88f94d2d25b4a29c03d4e1243484fd7a20d67753fd3e9a10e6f39069df8 +dist/2025-09-27/rust-nightly-i686-pc-windows-msvc.msi=8441a5f6e8650613b5b9c0c2778bc88bcf259fd4f3acd226a6ec52f1b4a960cb +dist/2025-09-27/rust-nightly-x86_64-pc-windows-gnu.msi=15a83b7056623d30dc1d47560151ec79e2cb7db1d229069085e73b782347e8e7 +dist/2025-09-27/rust-nightly-x86_64-pc-windows-msvc.msi=607a9219272d8b41fd6bedf884515d3584471c75be19f9353c1c67826c115aea +dist/2025-09-27/rust-nightly-aarch64-apple-darwin.pkg=55cc7129e581244dcbc567eb905183ff3e45edc8847fc58cb350394e6df55e96 +dist/2025-09-27/rust-nightly-x86_64-apple-darwin.pkg=a6add14a01bfd77634e67425db47cf63144dbc0b618beeaa4f37be2d7103146c +dist/2025-09-27/rustc-nightly-src.tar.gz=419d5aea9252c3a9377fcfefe0a0e91b7be1354b9c33e36e346c547c4a9ec3eb +dist/2025-09-27/rustc-nightly-src.tar.xz=fd454f13408f045e3ba1d4618699a3c6e42fcc66902c37972aa3729bb681d951 From 760ed37769c4902c97c874d324978472ce02f9ba Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 27 Sep 2025 08:25:47 -0600 Subject: [PATCH 0971/1900] redox: switch to colon as path separator --- library/std/src/sys/pal/unix/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index f0b6068e06c0f..7c9f3b7992f77 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -16,7 +16,7 @@ use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; -const PATH_SEPARATOR: u8 = if cfg!(target_os = "redox") { b';' } else { b':' }; +const PATH_SEPARATOR: u8 = b':'; unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] From 35e582f9828a515c94946eb03910d563d6d38427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 27 Sep 2025 17:03:26 +0200 Subject: [PATCH 0972/1900] Library: Remove remaining private `#[repr]` workarounds Co-authored-by: David Tolnay --- library/core/src/ffi/mod.rs | 2 +- library/core/src/ffi/va_list.rs | 8 ++++---- library/core/src/os/darwin/objc.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 0bc98e2ea8645..1356ca217c9a2 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -56,7 +56,7 @@ pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t}; // be UB. #[doc = include_str!("c_void.md")] #[lang = "c_void"] -#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +#[repr(u8)] #[stable(feature = "core_c_void", since = "1.30.0")] pub enum c_void { #[unstable( diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0d4ccb5aeb28c..46ccf330d1c22 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -25,7 +25,7 @@ crate::cfg_select! { /// /// [AArch64 Procedure Call Standard]: /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf - #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[repr(C)] #[derive(Debug)] #[lang = "va_list"] pub struct VaListImpl<'f> { @@ -39,7 +39,7 @@ crate::cfg_select! { } all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => { /// PowerPC ABI implementation of a `va_list`. - #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[repr(C)] #[derive(Debug)] #[lang = "va_list"] pub struct VaListImpl<'f> { @@ -53,7 +53,7 @@ crate::cfg_select! { } target_arch = "s390x" => { /// s390x ABI implementation of a `va_list`. - #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[repr(C)] #[derive(Debug)] #[lang = "va_list"] pub struct VaListImpl<'f> { @@ -66,7 +66,7 @@ crate::cfg_select! { } all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => { /// x86_64 ABI implementation of a `va_list`. - #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[repr(C)] #[derive(Debug)] #[lang = "va_list"] pub struct VaListImpl<'f> { diff --git a/library/core/src/os/darwin/objc.rs b/library/core/src/os/darwin/objc.rs index 928cb54e82c79..df3aab867e83d 100644 --- a/library/core/src/os/darwin/objc.rs +++ b/library/core/src/os/darwin/objc.rs @@ -6,7 +6,7 @@ use crate::fmt; /// Equivalent to Objective-C’s `struct objc_class` type. -#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +#[repr(u8)] pub enum objc_class { #[unstable( feature = "objc_class_variant", @@ -31,7 +31,7 @@ impl fmt::Debug for objc_class { } /// Equivalent to Objective-C’s `struct objc_selector` type. -#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +#[repr(u8)] pub enum objc_selector { #[unstable( feature = "objc_selector_variant", From 9878be7787a6239b9651eeaccb040778051038f6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 13 Sep 2025 17:20:34 +0200 Subject: [PATCH 0973/1900] Re-enable assertions on macOS --- src/ci/github-actions/jobs.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index b3e3fe7d96aab..8303699ce8a68 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -431,9 +431,6 @@ auto: MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 SELECT_XCODE: /Applications/Xcode_15.2.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos @@ -449,9 +446,6 @@ auto: MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 SELECT_XCODE: /Applications/Xcode_15.2.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 <<: *job-macos - name: dist-aarch64-apple @@ -471,9 +465,6 @@ auto: # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos @@ -493,9 +484,6 @@ auto: # supports the hardware, so only need to test it there. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 <<: *job-macos ###################### From aef976ed4c73dab044002b39817509110ddf2cf2 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 27 Sep 2025 14:10:06 +0100 Subject: [PATCH 0974/1900] Add auto extra-checks in pre-push script It enables automatic check changes of Python/C++/JS before pushing the changes to remote repository. Those checks happen only when the target type of file is changed. Otherwise it does not install any dependencies (venv and/or node_modules). Note that shellcheck and spellcheck are not included in this change, because: 1. Unlike venv/node_modules, shellcheck is not installed automatically by the command, and 2. spellcheck is built whenever pre-push script is run, it forces developer to wait extra time So not to break the current productivity, this commit skips them. --- src/etc/pre-push.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index 7bacc943f258b..33ed2f0e406b1 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -26,7 +26,10 @@ ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" cd "$ROOT_DIR" -./x test tidy --set build.locked-deps=true +# The env var is necessary for printing diffs in py (fmt/lint) and cpp. +TIDY_PRINT_DIFF=1 ./x test tidy \ + --set build.locked-deps=true \ + --extra-checks auto:py,auto:cpp,auto:js if [ $? -ne 0 ]; then echo "You may use \`git push --no-verify\` to skip this check." exit 1 From b3631e1174e222bf1dadf1549cfd0f717ebf6d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 16 Sep 2025 11:52:17 -0700 Subject: [PATCH 0975/1900] improve empty attribute diagnostic --- compiler/rustc_attr_parsing/messages.ftl | 10 +++++++++- compiler/rustc_attr_parsing/src/context.rs | 7 ++++++- compiler/rustc_attr_parsing/src/lints.rs | 18 ++++++++++++------ .../src/session_diagnostics.rs | 3 +++ compiler/rustc_hir/src/lints.rs | 15 ++++++++------- tests/ui/attributes/empty-repr.stderr | 1 + tests/ui/empty/empty-attributes.stderr | 4 ++++ tests/ui/macros/macro-use-all-and-none.stderr | 3 ++- tests/ui/repr/repr-empty-packed.stderr | 1 + 9 files changed, 46 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 81ec17077c13c..6c5346e83554b 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -8,7 +8,15 @@ attr_parsing_deprecated_item_suggestion = attr_parsing_empty_attribute = unused attribute - .suggestion = remove this attribute + .suggestion = {$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + } + .note = {$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + } + attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} .help = `#[{$name}]` can {$only}be applied to {$applied} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d7ccf3c78069c..e8bb4caa41664 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -597,7 +597,12 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { - self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span); + let attr_path = self.attr_path.clone(); + let valid_without_list = self.template.word; + self.emit_lint( + AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list }, + span, + ); } } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index ab8ba0daf1f10..3a2a370466961 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -43,12 +43,18 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi ), }, ), - AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - *id, - *first_span, - session_diagnostics::EmptyAttributeList { attr_span: *first_span }, - ), + AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { + lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + *id, + *first_span, + session_diagnostics::EmptyAttributeList { + attr_span: *first_span, + attr_path: attr_path.clone(), + valid_without_list: *valid_without_list, + }, + ) + } AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter .emit_node_span_lint( // This check is here because `deprecated` had its own lint group and removing this would be a breaking change diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2c2b14c8a68bc..1194ac5872cb2 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -503,9 +503,12 @@ pub(crate) struct EmptyConfusables { #[derive(LintDiagnostic)] #[diag(attr_parsing_empty_attribute)] +#[note] pub(crate) struct EmptyAttributeList { #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, + pub attr_path: AttrPath, + pub valid_without_list: bool, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index b7a0a6a0c197f..c9de6f6b5d526 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,6 +31,12 @@ pub struct AttributeLint { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see + InvalidMacroExportArguments { + suggestions: Vec, + }, UnusedDuplicate { this: Span, other: Span, @@ -41,13 +47,8 @@ pub enum AttributeLintKind { }, EmptyAttribute { first_span: Span, - }, - - /// Copy of `IllFormedAttributeInput` - /// specifically for the `invalid_macro_export_arguments` lint until that is removed, - /// see - InvalidMacroExportArguments { - suggestions: Vec, + attr_path: AttrPath, + valid_without_list: bool, }, InvalidTarget { name: AttrPath, diff --git a/tests/ui/attributes/empty-repr.stderr b/tests/ui/attributes/empty-repr.stderr index 92901fa170c25..6dfa2df75b737 100644 --- a/tests/ui/attributes/empty-repr.stderr +++ b/tests/ui/attributes/empty-repr.stderr @@ -4,6 +4,7 @@ error: unused attribute LL | #[repr()] | ^^^^^^^^^ help: remove this attribute | + = note: using `repr` with an empty list has no effect note: the lint level is defined here --> $DIR/empty-repr.rs:4:9 | diff --git a/tests/ui/empty/empty-attributes.stderr b/tests/ui/empty/empty-attributes.stderr index f0be56ddc6aa4..41dc790737dd1 100644 --- a/tests/ui/empty/empty-attributes.stderr +++ b/tests/ui/empty/empty-attributes.stderr @@ -56,12 +56,16 @@ error: unused attribute | LL | #[repr()] | ^^^^^^^^^ help: remove this attribute + | + = note: using `repr` with an empty list has no effect error: unused attribute --> $DIR/empty-attributes.rs:12:1 | LL | #[target_feature()] | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: using `target_feature` with an empty list has no effect error: aborting due to 8 previous errors diff --git a/tests/ui/macros/macro-use-all-and-none.stderr b/tests/ui/macros/macro-use-all-and-none.stderr index a5efb065a21b1..b4c05adcb33d0 100644 --- a/tests/ui/macros/macro-use-all-and-none.stderr +++ b/tests/ui/macros/macro-use-all-and-none.stderr @@ -2,8 +2,9 @@ warning: unused attribute --> $DIR/macro-use-all-and-none.rs:7:12 | LL | #[macro_use()] - | ^^ help: remove this attribute + | ^^ help: remove these parentheses | + = note: using `macro_use` with an empty list is equivalent to not using a list at all note: the lint level is defined here --> $DIR/macro-use-all-and-none.rs:4:9 | diff --git a/tests/ui/repr/repr-empty-packed.stderr b/tests/ui/repr/repr-empty-packed.stderr index 6565b2e8c1dca..adf32c9552967 100644 --- a/tests/ui/repr/repr-empty-packed.stderr +++ b/tests/ui/repr/repr-empty-packed.stderr @@ -15,6 +15,7 @@ error: unused attribute LL | #[repr()] | ^^^^^^^^^ help: remove this attribute | + = note: using `repr` with an empty list has no effect note: the lint level is defined here --> $DIR/repr-empty-packed.rs:2:9 | From fa53de656ec9318d9462dcadcfd851685c3dcaf1 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 27 Sep 2025 19:40:48 +0200 Subject: [PATCH 0976/1900] tests: Remove ignore-android directive for fixed issue --- tests/ui/test-attrs/test-panic-abort-nocapture.rs | 1 - tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr | 4 ++-- tests/ui/test-attrs/test-panic-abort.rs | 1 - tests/ui/test-attrs/test-panic-abort.run.stdout | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.rs b/tests/ui/test-attrs/test-panic-abort-nocapture.rs index 6a1025ea087c0..7c78d432fa087 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.rs +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.rs @@ -6,7 +6,6 @@ //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ ignore-android #120567 //@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr index 8d7c62f8ec700..d8f65a78261f8 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr @@ -1,11 +1,11 @@ -thread 'main' ($TID) panicked at $DIR/test-panic-abort-nocapture.rs:32:5: +thread 'main' ($TID) panicked at $DIR/test-panic-abort-nocapture.rs:31:5: assertion `left == right` failed left: 2 right: 4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread 'main' ($TID) panicked at $DIR/test-panic-abort-nocapture.rs:26:5: +thread 'main' ($TID) panicked at $DIR/test-panic-abort-nocapture.rs:25:5: assertion `left == right` failed left: 2 right: 4 diff --git a/tests/ui/test-attrs/test-panic-abort.rs b/tests/ui/test-attrs/test-panic-abort.rs index 6c9b641fb6fba..13a30223399e5 100644 --- a/tests/ui/test-attrs/test-panic-abort.rs +++ b/tests/ui/test-attrs/test-panic-abort.rs @@ -6,7 +6,6 @@ //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ ignore-android #120567 //@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort.run.stdout b/tests/ui/test-attrs/test-panic-abort.run.stdout index 4d65c05b94402..ca247f7da4180 100644 --- a/tests/ui/test-attrs/test-panic-abort.run.stdout +++ b/tests/ui/test-attrs/test-panic-abort.run.stdout @@ -18,7 +18,7 @@ testing123 ---- it_fails stderr ---- testing321 -thread 'main' ($TID) panicked at $DIR/test-panic-abort.rs:37:5: +thread 'main' ($TID) panicked at $DIR/test-panic-abort.rs:36:5: assertion `left == right` failed left: 2 right: 5 From 19d0e728496afa1ba6a5f9817201b7d57337c14c Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 27 Sep 2025 20:38:45 +0100 Subject: [PATCH 0977/1900] fix build for android --- library/std/src/os/net/linux_ext/tcp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index dbefc91a979a5..3f9b2bd3f4b43 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -4,6 +4,7 @@ use crate::sealed::Sealed; use crate::sys_common::AsInner; +#[cfg(target_os = "linux")] use crate::time::Duration; use crate::{io, net}; From 4d32b9a1783343d42a9864fe3d2115daa2cb425e Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 27 Sep 2025 15:47:06 -0400 Subject: [PATCH 0978/1900] Hoist non-platform-specific code out of `thread_local_inner!` --- library/std/src/sys/thread_local/native/mod.rs | 4 ---- library/std/src/sys/thread_local/no_threads.rs | 5 ----- library/std/src/sys/thread_local/os.rs | 5 ----- library/std/src/thread/local.rs | 8 ++++++-- tests/ui/macros/macro-local-data-key-priv.stderr | 2 +- tests/ui/thread-local/no-unstable.stderr | 6 +++--- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 9544721b923b8..5dc142408047e 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -108,10 +108,6 @@ pub macro thread_local_inner { }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*); - }, } #[rustc_macro_transparency = "semitransparent"] diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 4d6a4464cfa8c..409dfb19518d9 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -39,11 +39,6 @@ pub macro thread_local_inner { }) } }}, - - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*); - }, } #[allow(missing_debug_implementations)] diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 77746b203a321..b488f12fe841f 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -85,11 +85,6 @@ pub macro thread_local_inner { $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? }, - - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$($align_attr:tt)*])*, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$($align_attr)*])*, $($init)*); - }, } /// Use a regular global static to store this key; the state provided will then be diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 70df7e724cafc..4259a4d1f3b7c 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -327,13 +327,17 @@ pub macro thread_local_process_attrs { // process `const` declaration and recurse ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => ( - $crate::thread::local_impl::thread_local_inner!($($other_attrs)* $vis $name, $t, $($align_attrs)*, const $init); + $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init); + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? ), // process non-`const` declaration and recurse ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => ( - $crate::thread::local_impl::thread_local_inner!($($other_attrs)* $vis $name, $t, $($align_attrs)*, $init); + $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init); + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? ), } diff --git a/tests/ui/macros/macro-local-data-key-priv.stderr b/tests/ui/macros/macro-local-data-key-priv.stderr index e93bd11046d09..8df1aec140d0e 100644 --- a/tests/ui/macros/macro-local-data-key-priv.stderr +++ b/tests/ui/macros/macro-local-data-key-priv.stderr @@ -9,7 +9,7 @@ note: the constant `baz` is defined here | LL | thread_local!(static baz: f64 = 0.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/thread-local/no-unstable.stderr b/tests/ui/thread-local/no-unstable.stderr index fc2541894e743..fbcd804d91785 100644 --- a/tests/ui/thread-local/no-unstable.stderr +++ b/tests/ui/thread-local/no-unstable.stderr @@ -10,7 +10,7 @@ LL | | } = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_dummy]` attribute is used for rustc unit tests - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: use of an internal attribute --> $DIR/no-unstable.rs:1:1 @@ -38,7 +38,7 @@ LL | | } = note: see issue #93798 for more information = help: add `#![feature(used_with_arg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[used]` attribute cannot be used on constants --> $DIR/no-unstable.rs:1:1 @@ -50,7 +50,7 @@ LL | | } | |_^ | = help: `#[used]` can only be applied to statics - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors From 4d411775139e1bc39c2752d78e40f2bcecb5304b Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Sat, 27 Sep 2025 22:58:02 +0100 Subject: [PATCH 0979/1900] Rename various "concrete opaque type" terminology to say "hidden type" --- compiler/rustc_borrowck/src/lib.rs | 7 +- .../rustc_borrowck/src/region_infer/mod.rs | 4 +- .../src/region_infer/opaque_types/mod.rs | 79 +++++++------------ compiler/rustc_borrowck/src/root_cx.rs | 22 +++--- .../rustc_hir_analysis/src/check/check.rs | 4 +- .../src/collect/type_of/opaque.rs | 12 +-- compiler/rustc_hir_typeck/src/opaque_types.rs | 19 +++-- compiler/rustc_hir_typeck/src/writeback.rs | 28 +++---- compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 7 +- compiler/rustc_middle/src/query/mod.rs | 2 +- .../rustc_middle/src/ty/typeck_results.rs | 4 +- compiler/rustc_pattern_analysis/src/rustc.rs | 4 +- .../src/traits/select/mod.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 2 +- 15 files changed, 82 insertions(+), 116 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4c380ddcf7084..d799eb1f8c6ef 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -116,10 +116,7 @@ pub fn provide(providers: &mut Providers) { /// Provider for `query mir_borrowck`. Similar to `typeck`, this must /// only be called for typeck roots which will then borrowck all /// nested bodies as well. -fn mir_borrowck( - tcx: TyCtxt<'_>, - def: LocalDefId, -) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> { +fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<&HiddenTypes<'_>, ErrorGuaranteed> { assert!(!tcx.is_typeck_child(def.to_def_id())); let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); @@ -130,7 +127,7 @@ fn mir_borrowck( Err(guar) } else if input_body.should_skip() { debug!("Skipping borrowck because of injected body"); - let opaque_types = ConcreteOpaqueTypes(Default::default()); + let opaque_types = HiddenTypes(Default::default()); Ok(tcx.arena.alloc(opaque_types)) } else { let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0910e8ef4b37b..e98c60e633805 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1382,10 +1382,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// The constraints we get from equating the hidden type of each use of an opaque - /// with its final concrete type may end up getting preferred over other, potentially + /// with its final hidden type may end up getting preferred over other, potentially /// longer constraint paths. /// - /// Given that we compute the final concrete type by relying on this existing constraint + /// Given that we compute the final hidden type by relying on this existing constraint /// path, this can easily end up hiding the actual reason for why we require these regions /// to be equal. /// diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 0af636aa734ca..6aa3345d4d423 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries}; use rustc_infer::traits::ObligationCause; use rustc_macros::extension; -use rustc_middle::mir::{Body, ConcreteOpaqueTypes, ConstraintCategory}; +use rustc_middle::mir::{Body, ConstraintCategory, HiddenTypes}; use rustc_middle::ty::{ self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, @@ -129,9 +129,9 @@ fn nll_var_to_universal_region<'tcx>( /// Collect all defining uses of opaque types inside of this typeck root. This /// expects the hidden type to be mapped to the definition parameters of the opaque /// and errors if we end up with distinct hidden types. -fn add_concrete_opaque_type<'tcx>( +fn add_hidden_type<'tcx>( tcx: TyCtxt<'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut HiddenTypes<'tcx>, def_id: LocalDefId, hidden_ty: OpaqueHiddenType<'tcx>, ) { @@ -139,7 +139,7 @@ fn add_concrete_opaque_type<'tcx>( // back to the opaque type definition. E.g. we may have `OpaqueType` mapped to // `(X, Y)` and `OpaqueType` mapped to `(Y, X)`, and those are the same, but we // only know that once we convert the generic parameters to those of the opaque type. - if let Some(prev) = concrete_opaque_types.0.get_mut(&def_id) { + if let Some(prev) = hidden_types.0.get_mut(&def_id) { if prev.ty != hidden_ty.ty { let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| { let (Ok(e) | Err(e)) = prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit()); @@ -151,15 +151,15 @@ fn add_concrete_opaque_type<'tcx>( // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. prev.span = prev.span.substitute_dummy(hidden_ty.span); } else { - concrete_opaque_types.0.insert(def_id, hidden_ty); + hidden_types.0.insert(def_id, hidden_ty); } } -fn get_concrete_opaque_type<'tcx>( - concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>, +fn get_hidden_type<'tcx>( + hidden_types: &HiddenTypes<'tcx>, def_id: LocalDefId, ) -> Option>> { - concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) + hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) } #[derive(Debug)] @@ -173,22 +173,22 @@ struct DefiningUse<'tcx> { } /// This computes the actual hidden types of the opaque types and maps them to their -/// definition sites. Outside of registering the computed concrete types this function +/// definition sites. Outside of registering the computed hidden types this function /// does not mutate the current borrowck state. /// /// While it may fail to infer the hidden type and return errors, we always apply -/// the computed concrete hidden type to all opaque type uses to check whether they +/// the computed hidden type to all opaque type uses to check whether they /// are correct. This is necessary to support non-defining uses of opaques in their /// defining scope. /// /// It also means that this whole function is not really soundness critical as we /// recheck all uses of the opaques regardless. -pub(crate) fn compute_concrete_opaque_types<'tcx>( +pub(crate) fn compute_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, universal_region_relations: &Frozen>, constraints: &MirTypeckRegionConstraints<'tcx>, location_map: Rc, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut HiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec> { let mut errors = Vec::new(); @@ -201,8 +201,7 @@ pub(crate) fn compute_concrete_opaque_types<'tcx>( // We start by checking each use of an opaque type during type check and // check whether the generic arguments of the opaque type are fully // universal, if so, it's a defining use. - let defining_uses = - collect_defining_uses(&mut rcx, concrete_opaque_types, opaque_types, &mut errors); + let defining_uses = collect_defining_uses(&mut rcx, hidden_types, opaque_types, &mut errors); // We now compute and apply member constraints for all regions in the hidden // types of each defining use. This mutates the region values of the `rcx` which @@ -210,21 +209,16 @@ pub(crate) fn compute_concrete_opaque_types<'tcx>( apply_member_constraints(&mut rcx, &defining_uses); // After applying member constraints, we now check whether all member regions ended - // up equal to one of their choice regions and compute the actual concrete type of + // up equal to one of their choice regions and compute the actual hidden type of // the opaque type definition. This is stored in the `root_cx`. - compute_concrete_types_from_defining_uses( - &rcx, - concrete_opaque_types, - &defining_uses, - &mut errors, - ); + compute_hidden_types_from_defining_uses(&rcx, hidden_types, &defining_uses, &mut errors); errors } #[instrument(level = "debug", skip_all, ret)] fn collect_defining_uses<'tcx>( rcx: &mut RegionCtxt<'_, 'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut HiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], errors: &mut Vec>, ) -> Vec> { @@ -244,9 +238,9 @@ fn collect_defining_uses<'tcx>( // with `TypingMode::Borrowck`. if infcx.tcx.use_typing_mode_borrowck() { match err { - NonDefiningUseReason::Tainted(guar) => add_concrete_opaque_type( + NonDefiningUseReason::Tainted(guar) => add_hidden_type( infcx.tcx, - concrete_opaque_types, + hidden_types, opaque_type_key.def_id, OpaqueHiddenType::new_error(infcx.tcx, guar), ), @@ -277,9 +271,9 @@ fn collect_defining_uses<'tcx>( defining_uses } -fn compute_concrete_types_from_defining_uses<'tcx>( +fn compute_hidden_types_from_defining_uses<'tcx>( rcx: &RegionCtxt<'_, 'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut HiddenTypes<'tcx>, defining_uses: &[DefiningUse<'tcx>], errors: &mut Vec>, ) { @@ -358,9 +352,9 @@ fn compute_concrete_types_from_defining_uses<'tcx>( }, )); } - add_concrete_opaque_type( + add_hidden_type( tcx, - concrete_opaque_types, + hidden_types, opaque_type_key.def_id, OpaqueHiddenType { span: hidden_type.span, ty }, ); @@ -489,20 +483,20 @@ impl<'tcx> FallibleTypeFolder> for ToArgRegionsFolder<'_, 'tcx> { /// /// It does this by equating the hidden type of each use with the instantiated final /// hidden type of the opaque. -pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( +pub(crate) fn apply_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, universal_regions: &UniversalRegions<'tcx>, region_bound_pairs: &RegionBoundPairs<'tcx>, known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], constraints: &mut MirTypeckRegionConstraints<'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut HiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec> { let tcx = infcx.tcx; let mut errors = Vec::new(); for &(key, hidden_type) in opaque_types { - let Some(expected) = get_concrete_opaque_type(concrete_opaque_types, key.def_id) else { + let Some(expected) = get_hidden_type(hidden_types, key.def_id) else { if !tcx.use_typing_mode_borrowck() { if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() && alias_ty.def_id == key.def_id.to_def_id() @@ -521,12 +515,7 @@ pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( hidden_type.span, "non-defining use in the defining scope with no defining uses", ); - add_concrete_opaque_type( - tcx, - concrete_opaque_types, - key.def_id, - OpaqueHiddenType::new_error(tcx, guar), - ); + add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar)); continue; }; @@ -566,18 +555,13 @@ pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( "equating opaque types", ), ) { - add_concrete_opaque_type( - tcx, - concrete_opaque_types, - key.def_id, - OpaqueHiddenType::new_error(tcx, guar), - ); + add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar)); } } errors } -/// In theory `apply_concrete_opaque_types` could introduce new uses of opaque types. +/// In theory `apply_hidden_types` could introduce new uses of opaque types. /// We do not check these new uses so this could be unsound. /// /// We detect any new uses and simply delay a bug if they occur. If this results in @@ -682,13 +666,6 @@ impl<'tcx> InferCtxt<'tcx> { /// /// (*) C1 and C2 were introduced in the comments on /// `register_member_constraints`. Read that comment for more context. - /// - /// # Parameters - /// - /// - `def_id`, the `impl Trait` type - /// - `args`, the args used to instantiate this opaque type - /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of - /// `opaque_defn.concrete_ty` #[instrument(level = "debug", skip(self))] fn infer_opaque_definition_from_instantiation( &self, diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index cd4e9683f2d87..d9599bbdd4611 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -12,12 +12,12 @@ use smallvec::SmallVec; use crate::consumers::BorrowckConsumer; use crate::nll::compute_closure_requirements_modulo_opaques; use crate::region_infer::opaque_types::{ - apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types, - compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types, + apply_hidden_types, clone_and_resolve_opaque_types, compute_hidden_types, + detect_opaque_types_added_while_handling_opaque_types, }; use crate::type_check::{Locations, constraint_conversion}; use crate::{ - ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes, + ClosureRegionRequirements, CollectRegionConstraintsResult, HiddenTypes, PropagatedBorrowCheckResults, borrowck_check_region_constraints, borrowck_collect_region_constraints, }; @@ -27,7 +27,7 @@ use crate::{ pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, - concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, + hidden_types: HiddenTypes<'tcx>, /// The region constraints computed by [borrowck_collect_region_constraints]. This uses /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before /// their parents. @@ -49,7 +49,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { BorrowCheckRootCtxt { tcx, root_def_id, - concrete_opaque_types: Default::default(), + hidden_types: Default::default(), collect_region_constraints_results: Default::default(), propagated_borrowck_results: Default::default(), tainted_by_errors: None, @@ -72,11 +72,11 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars } - pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { + pub(super) fn finalize(self) -> Result<&'tcx HiddenTypes<'tcx>, ErrorGuaranteed> { if let Some(guar) = self.tainted_by_errors { Err(guar) } else { - Ok(self.tcx.arena.alloc(self.concrete_opaque_types)) + Ok(self.tcx.arena.alloc(self.hidden_types)) } } @@ -88,12 +88,12 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &input.universal_region_relations, &mut input.constraints, ); - input.deferred_opaque_type_errors = compute_concrete_opaque_types( + input.deferred_opaque_type_errors = compute_hidden_types( &input.infcx, &input.universal_region_relations, &input.constraints, Rc::clone(&input.location_map), - &mut self.concrete_opaque_types, + &mut self.hidden_types, &opaque_types, ); per_body_info.push((num_entries, opaque_types)); @@ -103,14 +103,14 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { self.collect_region_constraints_results.values_mut().zip(per_body_info) { if input.deferred_opaque_type_errors.is_empty() { - input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types( + input.deferred_opaque_type_errors = apply_hidden_types( &input.infcx, &input.body_owned, &input.universal_region_relations.universal_regions, &input.region_bound_pairs, &input.known_type_outlives_obligations, &mut input.constraints, - &mut self.concrete_opaque_types, + &mut self.hidden_types, &opaque_types, ); } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 886ebddc75c97..e1e6860e43004 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -219,7 +219,7 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // Since rustdoc doesn't care about the hidden type behind `impl Trait`, just don't look at it! // See https://github.com/rust-lang/rust/issues/75100 if tcx.sess.opts.actually_rustdoc { return; @@ -252,7 +252,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( Ok(()) } -/// Check that the concrete type behind `impl Trait` actually implements `Trait`. +/// Check that the hidden type behind `impl Trait` actually implements `Trait`. /// /// This is mostly checked at the places that specify the opaque type, but we /// check those cases in the `param_env` of that function, which may have diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index b6d898886ac4d..a02990fe4abaf 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -177,7 +177,7 @@ impl<'tcx> TaitConstraintLocator<'tcx> { let tables = tcx.typeck(item_def_id); if let Some(guar) = tables.tainted_by_errors { self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)); - } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) { + } else if let Some(&hidden_type) = tables.hidden_types.get(&self.def_id) { self.insert_found(hidden_type); } else { self.non_defining_use_in_defining_scope(item_def_id); @@ -185,8 +185,8 @@ impl<'tcx> TaitConstraintLocator<'tcx> { } DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) { Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)), - Ok(concrete_opaque_types) => { - if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) { + Ok(hidden_types) => { + if let Some(&hidden_type) = hidden_types.0.get(&self.def_id) { debug!(?hidden_type, "found constraint"); self.insert_found(hidden_type); } else if let Err(guar) = tcx @@ -247,7 +247,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( let tables = tcx.typeck(owner_def_id); if let Some(guar) = tables.tainted_by_errors { Ty::new_error(tcx, guar) - } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) { + } else if let Some(hidden_ty) = tables.hidden_types.get(&def_id) { hidden_ty.ty } else { assert!(!tcx.next_trait_solver_globally()); @@ -261,8 +261,8 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( } } DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) { - Ok(concrete_opaque_types) => { - if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) { + Ok(hidden_types) => { + if let Some(hidden_ty) = hidden_types.0.get(&def_id) { hidden_ty.ty } else { let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity(); diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 5cefa506b5a06..a47fa202acfe3 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -15,7 +15,7 @@ use crate::FnCtxt; impl<'tcx> FnCtxt<'_, 'tcx> { /// This takes all the opaque type uses during HIR typeck. It first computes - /// the concrete hidden type by iterating over all defining uses. + /// the hidden type by iterating over all defining uses. /// /// A use during HIR typeck is defining if all non-lifetime arguments are /// unique generic parameters and the hidden type does not reference any @@ -35,8 +35,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } debug!(?opaque_types); - self.compute_concrete_opaque_types(&opaque_types); - self.apply_computed_concrete_opaque_types(&opaque_types); + self.compute_hidden_types(&opaque_types); + self.apply_hidden_types(&opaque_types); } } @@ -71,7 +71,7 @@ impl<'tcx> UsageKind<'tcx> { } impl<'tcx> FnCtxt<'_, 'tcx> { - fn compute_concrete_opaque_types( + fn compute_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { @@ -142,7 +142,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.typeck_results .borrow_mut() - .concrete_opaque_types + .hidden_types .insert(def_id, OpaqueHiddenType::new_error(tcx, guar)); self.set_tainted_by_errors(guar); } @@ -161,7 +161,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { match err { NonDefiningUseReason::Tainted(guar) => { - self.typeck_results.borrow_mut().concrete_opaque_types.insert( + self.typeck_results.borrow_mut().hidden_types.insert( opaque_type_key.def_id, OpaqueHiddenType::new_error(self.tcx, guar), ); @@ -197,20 +197,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let prev = self .typeck_results .borrow_mut() - .concrete_opaque_types + .hidden_types .insert(opaque_type_key.def_id, hidden_type); assert!(prev.is_none()); UsageKind::HasDefiningUse } - fn apply_computed_concrete_opaque_types( + fn apply_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { let tcx = self.tcx; for &(key, hidden_type) in opaque_types { - let expected = - *self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id).unwrap(); + let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap(); let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index d01eeb9a4b691..697029e55f7cb 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -550,13 +550,12 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_opaque_types_next(&mut self) { let mut fcx_typeck_results = self.fcx.typeck_results.borrow_mut(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - for hidden_ty in fcx_typeck_results.concrete_opaque_types.values() { + for hidden_ty in fcx_typeck_results.hidden_types.values() { assert!(!hidden_ty.has_infer()); } - assert_eq!(self.typeck_results.concrete_opaque_types.len(), 0); - self.typeck_results.concrete_opaque_types = - mem::take(&mut fcx_typeck_results.concrete_opaque_types); + assert_eq!(self.typeck_results.hidden_types.len(), 0); + self.typeck_results.hidden_types = mem::take(&mut fcx_typeck_results.hidden_types); } #[instrument(skip(self), level = "debug")] @@ -588,7 +587,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { hidden_type.span, DefiningScopeKind::HirTypeck, ) { - self.typeck_results.concrete_opaque_types.insert( + self.typeck_results.hidden_types.insert( opaque_type_key.def_id, ty::OpaqueHiddenType::new_error(tcx, err.report(self.fcx)), ); @@ -600,16 +599,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { DefiningScopeKind::HirTypeck, ); - if let Some(prev) = self - .typeck_results - .concrete_opaque_types - .insert(opaque_type_key.def_id, hidden_type) + if let Some(prev) = + self.typeck_results.hidden_types.insert(opaque_type_key.def_id, hidden_type) { - let entry = &mut self - .typeck_results - .concrete_opaque_types - .get_mut(&opaque_type_key.def_id) - .unwrap(); + let entry = + &mut self.typeck_results.hidden_types.get_mut(&opaque_type_key.def_id).unwrap(); if prev.ty != hidden_type.ty { if let Some(guar) = self.typeck_results.tainted_by_errors { entry.ty = Ty::new_error(tcx, guar); @@ -628,7 +622,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let recursive_opaques: Vec<_> = self .typeck_results - .concrete_opaque_types + .hidden_types .iter() .filter(|&(&def_id, hidden_ty)| { hidden_ty @@ -636,7 +630,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .visit_with(&mut HasRecursiveOpaque { def_id, seen: Default::default(), - opaques: &self.typeck_results.concrete_opaque_types, + opaques: &self.typeck_results.hidden_types, tcx, }) .is_break() @@ -651,7 +645,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .with_code(E0720) .emit(); self.typeck_results - .concrete_opaque_types + .hidden_types .insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) }); } } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index fa6a2db38ef9c..a63030b114527 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -27,7 +27,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_middle::mir::ConcreteOpaqueTypes<'tcx>, + [decode] borrowck_result: rustc_middle::mir::HiddenTypes<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, std::sync::Arc, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index a509c40c89cd6..791565e387e8d 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -84,11 +84,10 @@ impl Debug for CoroutineLayout<'_> { } } -/// All the opaque types that are restricted to concrete types -/// by this function. Unlike the value in `TypeckResults`, this has -/// unerased regions. +/// All the opaque types that have had their hidden type fully computed. +/// Unlike the value in `TypeckResults`, this has unerased regions. #[derive(Default, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct ConcreteOpaqueTypes<'tcx>(pub FxIndexMap>); +pub struct HiddenTypes<'tcx>(pub FxIndexMap>); /// The result of the `mir_const_qualif` query. /// diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 326df9239aa08..1efb29c788d49 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1244,7 +1244,7 @@ rustc_queries! { /// Borrow-checks the given typeck root, e.g. functions, const/static items, /// and its children, e.g. closures, inline consts. - query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { + query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::HiddenTypes<'tcx>, ErrorGuaranteed> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 8dd80aab946d4..944bd9756a957 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -167,7 +167,7 @@ pub struct TypeckResults<'tcx> { /// We also store the type here, so that the compiler can use it as a hint /// for figuring out hidden types, even if they are only set in dead code /// (which doesn't show up in MIR). - pub concrete_opaque_types: FxIndexMap>, + pub hidden_types: FxIndexMap>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. @@ -250,7 +250,7 @@ impl<'tcx> TypeckResults<'tcx> { coercion_casts: Default::default(), used_trait_imports: Default::default(), tainted_by_errors: None, - concrete_opaque_types: Default::default(), + hidden_types: Default::default(), closure_min_captures: Default::default(), closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d9f8085083ebd..0652461e97501 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -120,7 +120,7 @@ impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> { impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Type inference occasionally gives us opaque types in places where corresponding patterns /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited - /// types, we use the corresponding concrete type if possible. + /// types, we use the corresponding hidden type if possible. // FIXME(#132279): This will be unnecessary once we have a TypingMode which supports revealing // opaque types defined in a body. #[inline] @@ -146,7 +146,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// know it. fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option> { self.typeck_results - .concrete_opaque_types + .hidden_types .get(&key.def_id) .map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args)) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1dd31990ab73e..fb4f28412d428 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2359,7 +2359,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { if self.infcx.can_define_opaque_ty(def_id) { unreachable!() } else { - // We can resolve the `impl Trait` to its concrete type, + // We can resolve the opaque type to its hidden type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. match self.tcx().type_of_opaque(def_id) { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index f743b84bce689..feafcee7bad9e 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -80,7 +80,7 @@ pub enum TypingMode { /// the old solver as well. PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds }, /// After analysis, mostly during codegen and MIR optimizations, we're able to - /// reveal all opaque types. As the concrete type should *never* be observable + /// reveal all opaque types. As the hidden type should *never* be observable /// directly by the user, this should not be used by checks which may expose /// such details to the user. /// From 3a20a4d0a5463edb5fca2adfbe0522042a9ce752 Mon Sep 17 00:00:00 2001 From: Sebastian Speitel Date: Sun, 28 Sep 2025 00:51:57 +0200 Subject: [PATCH 0980/1900] Fix typo --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index a45edd08e8ccc..25a4661a0bc9c 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3234,7 +3234,7 @@ fn inlined_slow_read_byte(reader: &mut R) -> Option> { } } -// Used by `BufReader::spec_read_byte`, for which the `inline(ever)` is +// Used by `BufReader::spec_read_byte`, for which the `inline(never)` is // important. #[inline(never)] fn uninlined_slow_read_byte(reader: &mut R) -> Option> { From 1d7c1afde014291082f5e68f8134581f927482e1 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 22 Aug 2025 18:57:00 -0700 Subject: [PATCH 0981/1900] Implement `volatile_composites` lint Volatile reads and writes to non-primitive types are not well-defined, and can cause problems. See https://github.com/rust-lang/rust-clippy/issues/15529 for more details. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/volatile_composites.rs | 180 +++++++++++++++++++ clippy_utils/src/sym.rs | 2 + tests/ui/volatile_composites.rs | 221 ++++++++++++++++++++++++ tests/ui/volatile_composites.stderr | 89 ++++++++++ 7 files changed, 496 insertions(+) create mode 100644 clippy_lints/src/volatile_composites.rs create mode 100644 tests/ui/volatile_composites.rs create mode 100644 tests/ui/volatile_composites.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..b6b374e26c962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6798,6 +6798,7 @@ Released 2018-09-13 [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads +[`volatile_composites`]: https://rust-lang.github.io/rust-clippy/master/index.html#volatile_composites [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake [`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7645ac2dd3f7b..6a24aaf59777a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -780,6 +780,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::visibility::NEEDLESS_PUB_SELF_INFO, crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, crate::write::PRINTLN_EMPTY_STRING_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9fb..6d6aa310ace9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -400,6 +400,7 @@ mod useless_conversion; mod vec; mod vec_init_then_push; mod visibility; +mod volatile_composites; mod wildcard_imports; mod write; mod zero_div_zero; @@ -831,5 +832,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); + store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/volatile_composites.rs b/clippy_lints/src/volatile_composites.rs new file mode 100644 index 0000000000000..c65b47c0853f4 --- /dev/null +++ b/clippy_lints/src/volatile_composites.rs @@ -0,0 +1,180 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// This lint warns when volatile load/store operations + /// (`write_volatile`/`read_volatile`) are applied to composite types. + /// + /// ### Why is this bad? + /// + /// Volatile operations are typically used with memory mapped IO devices, + /// where the precise number and ordering of load and store instructions is + /// important because they can have side effects. This is well defined for + /// primitive types like `u32`, but less well defined for structures and + /// other composite types. In practice it's implementation defined, and the + /// behavior can be rustc-version dependent. + /// + /// As a result, code should only apply `write_volatile`/`read_volatile` to + /// primitive types to be fully well-defined. + /// + /// ### Example + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// device.write_volatile(MyDevice { addr, count }); + /// } + /// } + /// ``` + /// Instead, operate on each primtive field individually: + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// (&raw mut (*device).addr).write_volatile(addr); + /// (&raw mut (*device).count).write_volatile(count); + /// } + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub VOLATILE_COMPOSITES, + nursery, + "warn about volatile read/write applied to composite types" +} +declare_lint_pass!(VolatileComposites => [VOLATILE_COMPOSITES]); + +/// Zero-sized types are intrinsically safe to use volatile on since they won't +/// actually generate *any* loads or stores. But this is also used to skip zero-sized +/// fields of `#[repr(transparent)]` structures. +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.layout_of(ty).is_ok_and(|layout| layout.is_zst()) +} + +/// A thin raw pointer or reference. +fn is_narrow_ptr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::RawPtr(inner, _) | ty::Ref(_, inner, _) => inner.has_trivial_sizedness(cx.tcx, ty::SizedTraitKind::Sized), + _ => false, + } +} + +/// Enum with some fixed representation and no data-carrying variants. +fn is_enum_repr_c<'tcx>(_cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.ty_adt_def().is_some_and(|adt_def| { + adt_def.is_enum() && adt_def.repr().inhibit_struct_field_reordering() && adt_def.is_payloadfree() + }) +} + +/// `#[repr(transparent)]` structures are also OK if the only non-zero +/// sized field contains a volatile-safe type. +fn is_struct_repr_transparent<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().transparent() + && let [fieldty] = adt_def + .all_fields() + .filter_map(|field| { + let fty = field.ty(cx.tcx, args); + if is_zero_sized_ty(cx, fty) { None } else { Some(fty) } + }) + .collect::>() + .as_slice() + { + is_volatile_safe_ty(cx, *fieldty) + } else { + false + } +} + +/// SIMD can be useful to get larger single loads/stores, though this is still +/// pretty machine-dependent. +fn is_simd_repr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, _args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().simd() + { + let (_size, simdty) = ty.simd_size_and_type(cx.tcx); + is_volatile_safe_ty(cx, simdty) + } else { + false + } +} + +/// Top-level predicate for whether a type is volatile-safe or not. +fn is_volatile_safe_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_primitive() + || is_narrow_ptr(cx, ty) + || is_zero_sized_ty(cx, ty) + || is_enum_repr_c(cx, ty) + || is_simd_repr(cx, ty) + || is_struct_repr_transparent(cx, ty) + // We can't know about a generic type, so just let it pass to avoid noise + || ty.has_non_region_param() +} + +/// Print diagnostic for volatile read/write on non-volatile-safe types. +fn report_volatile_safe<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if !is_volatile_safe_ty(cx, ty) { + span_lint( + cx, + VOLATILE_COMPOSITES, + expr.span, + format!("type `{ty}` is not volatile-compatible"), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for VolatileComposites { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + // Check our expr is calling a method with pattern matching + match expr.kind { + // Look for method calls to `write_volatile`/`read_volatile`, which + // apply to both raw pointers and std::ptr::NonNull. + ExprKind::MethodCall(name, self_arg, _, _) + if matches!(name.ident.name, sym::read_volatile | sym::write_volatile) => + { + let self_ty = cx.typeck_results().expr_ty(self_arg); + match self_ty.kind() { + // Raw pointers + ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), + // std::ptr::NonNull + ty::Adt(_, args) if is_type_diagnostic_item(cx, self_ty, sym::NonNull) => { + report_volatile_safe(cx, expr, args.type_at(0)); + }, + _ => (), + } + }, + + // Also plain function calls to std::ptr::{read,write}_volatile + ExprKind::Call(func, [arg_ptr, ..]) => { + if let ExprKind::Path(ref qpath) = func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_read_volatile | sym::ptr_write_volatile) + ) + && let ty::RawPtr(ptrty, _) = cx.typeck_results().expr_ty_adjusted(arg_ptr).kind() + { + report_volatile_safe(cx, expr, *ptrty); + } + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 4ba0e52572ddf..16858d7d32b39 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -264,6 +264,7 @@ generate! { read_to_end, read_to_string, read_unaligned, + read_volatile, redundant_imports, redundant_pub_crate, regex, @@ -373,6 +374,7 @@ generate! { wrapping_offset, write, write_unaligned, + write_volatile, writeln, zip, } diff --git a/tests/ui/volatile_composites.rs b/tests/ui/volatile_composites.rs new file mode 100644 index 0000000000000..e7e7dafe18afa --- /dev/null +++ b/tests/ui/volatile_composites.rs @@ -0,0 +1,221 @@ +#![feature(ptr_metadata)] +#![feature(portable_simd)] +#![warn(clippy::volatile_composites)] + +use std::ptr::null_mut; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +struct MyDevRegisters { + baseaddr: usize, + count: usize, +} + +#[repr(transparent)] +struct Wrapper((), T, ()); + +// Not to be confused with std::ptr::NonNull +struct NonNull(T); + +impl NonNull { + fn write_volatile(&self, _arg: &T) { + unimplemented!("Something entirely unrelated to std::ptr::NonNull"); + } +} + +fn main() { + let regs = MyDevRegisters { + baseaddr: 0xabc123, + count: 42, + }; + + const DEVICE_ADDR: *mut MyDevRegisters = 0xdead as *mut _; + + // Raw pointer methods + unsafe { + (&raw mut (*DEVICE_ADDR).baseaddr).write_volatile(regs.baseaddr); // OK + (&raw mut (*DEVICE_ADDR).count).write_volatile(regs.count); // OK + + DEVICE_ADDR.write_volatile(regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: (&raw const (*DEVICE_ADDR).baseaddr).read_volatile(), // OK + count: (&raw const (*DEVICE_ADDR).count).read_volatile(), // OK + }; + + let _regs = DEVICE_ADDR.read_volatile(); + //~^ volatile_composites + } + + // std::ptr functions + unsafe { + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + std::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = std::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // core::ptr functions + unsafe { + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + core::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = core::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // std::ptr::NonNull + unsafe { + let ptr = std::ptr::NonNull::new(DEVICE_ADDR).unwrap(); + + ptr.write_volatile(regs); + //~^ volatile_composites + + let _regs = ptr.read_volatile(); + //~^ volatile_composites + } + + // Red herring + { + let thing = NonNull("hello".to_string()); + + thing.write_volatile(&"goodbye".into()); // OK + } + + // Zero size types OK + unsafe { + struct Empty; + + (0xdead as *mut Empty).write_volatile(Empty); // OK + // Note that this is OK because Wrapper is itself ZST, not because of the repr transparent + // handling tested below. + (0xdead as *mut Wrapper).write_volatile(Wrapper((), Empty, ())); // OK + } + + // Via repr transparent newtype + unsafe { + (0xdead as *mut Wrapper).write_volatile(Wrapper((), 123, ())); // OK + (0xdead as *mut Wrapper>).write_volatile(Wrapper((), Wrapper((), 123, ()), ())); // OK + + (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + //~^ volatile_composites + } + + // Plain type alias OK + unsafe { + type MyU64 = u64; + + (0xdead as *mut MyU64).write_volatile(123); // OK + } + + // Wide pointers are not OK as data + unsafe { + let things: &[u32] = &[1, 2, 3]; + + (0xdead as *mut *const u32).write_volatile(things.as_ptr()); // OK + + let wideptr: *const [u32] = std::ptr::from_raw_parts(things.as_ptr(), things.len()); + (0xdead as *mut *const [u32]).write_volatile(wideptr); + //~^ volatile_composites + } + + // Plain pointers and pointers with lifetimes are OK + unsafe { + let v: u32 = 123; + let rv: &u32 = &v; + + (0xdead as *mut &u32).write_volatile(rv); // OK + } + + // C-style enums are OK + unsafe { + // Bad: need some specific repr + enum PlainEnum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + //~^ volatile_composites + + // OK + #[repr(u32)] + enum U32Enum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut U32Enum).write_volatile(U32Enum::A); // OK + + // OK + #[repr(C)] + enum CEnum { + A = 1, + B = 2, + C = 3, + } + (0xdead as *mut CEnum).write_volatile(CEnum::A); // OK + + // Nope + enum SumType { + A(String), + B(u32), + C, + } + (0xdead as *mut SumType).write_volatile(SumType::C); + //~^ volatile_composites + + // A repr on a complex sum type is not good enough + #[repr(C)] + enum ReprSumType { + A(String), + B(u32), + C, + } + (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + //~^ volatile_composites + } + + // SIMD is OK + unsafe { + (0xdead as *mut std::simd::u32x4).write_volatile(std::simd::u32x4::splat(1)); // OK + } + + // Can't see through generic wrapper + unsafe { + do_device_write::(0xdead as *mut _, Default::default()); // OK + } + + let mut s = String::from("foo"); + unsafe { + std::ptr::write_volatile(&mut s, String::from("bar")); + //~^ volatile_composites + } +} + +// Generic OK +unsafe fn do_device_write(ptr: *mut T, v: T) { + unsafe { + ptr.write_volatile(v); // OK + } +} diff --git a/tests/ui/volatile_composites.stderr b/tests/ui/volatile_composites.stderr new file mode 100644 index 0000000000000..1545fc913ed00 --- /dev/null +++ b/tests/ui/volatile_composites.stderr @@ -0,0 +1,89 @@ +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:39:9 + | +LL | DEVICE_ADDR.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::volatile-composites` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::volatile_composites)]` + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:47:21 + | +LL | let _regs = DEVICE_ADDR.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:56:9 + | +LL | std::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:64:21 + | +LL | let _regs = std::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:73:9 + | +LL | core::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:81:21 + | +LL | let _regs = core::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:89:9 + | +LL | ptr.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:92:21 + | +LL | let _regs = ptr.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^ + +error: type `Wrapper` is not volatile-compatible + --> tests/ui/volatile_composites.rs:118:9 + | +LL | (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `*const [u32]` is not volatile-compatible + --> tests/ui/volatile_composites.rs:136:9 + | +LL | (0xdead as *mut *const [u32]).write_volatile(wideptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::PlainEnum` is not volatile-compatible + --> tests/ui/volatile_composites.rs:157:9 + | +LL | (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::SumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:185:9 + | +LL | (0xdead as *mut SumType).write_volatile(SumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::ReprSumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:195:9 + | +LL | (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `std::string::String` is not volatile-compatible + --> tests/ui/volatile_composites.rs:211:9 + | +LL | std::ptr::write_volatile(&mut s, String::from("bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + From fdacc0cf5f11769427cb86c3133051314f9e8485 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 28 Sep 2025 08:41:15 +0800 Subject: [PATCH 0982/1900] Add `all` `any` and `not` attribute completions Example --- `#[cfg($0)]` -> `#[cfg(any($0))]` --- .../src/completions/attribute/cfg.rs | 30 +++++++++++++---- .../ide-completion/src/tests/attribute.rs | 32 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 1676a8467c85f..b2e8efde8bebc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -53,15 +53,33 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { acc.add(item.build(ctx.db)); }), }, - None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { - let s = s.as_str(); - let item = - CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s, ctx.edition); - acc.add(item.build(ctx.db)); - }), + None => ctx + .krate + .potential_cfg(ctx.db) + .get_cfg_keys() + .unique() + .map(|s| (s.as_str(), "")) + .chain(CFG_CONDITION.iter().copied()) + .for_each(|(s, snippet)| { + let mut item = CompletionItem::new( + SymbolKind::BuiltinAttr, + ctx.source_range(), + s, + ctx.edition, + ); + if let Some(cap) = ctx.config.snippet_cap + && !snippet.is_empty() + { + item.insert_snippet(cap, snippet); + } + acc.add(item.build(ctx.db)); + }), } } +const CFG_CONDITION: &[(&str, &str)] = + &[("all", "all($0)"), ("any", "any($0)"), ("not", "not($0)")]; + const KNOWN_ARCH: [&str; 20] = [ "aarch64", "arm", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 30e1e108c6c40..1d2a9c7c8d3ce 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -815,7 +815,10 @@ mod cfg { #[cfg($0)] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -827,7 +830,10 @@ mod cfg { #[cfg(b$0)] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -843,7 +849,10 @@ mod cfg { #[cfg_attr($0)] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -855,7 +864,10 @@ mod cfg { #[cfg_attr(b$0)] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -867,7 +879,10 @@ mod cfg { #[cfg_attr($0, allow(deprecated))] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -879,7 +894,10 @@ mod cfg { #[cfg_attr(b$0, allow(deprecated))] "#, expect![[r#" + ba all + ba any ba dbg + ba not ba opt_level ba test ba true @@ -904,6 +922,20 @@ mod cfg { "#]], ); } + + #[test] + fn inside_conditional() { + check_edit( + "all", + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0)] +"#, + r#" +#[cfg(all($0))] +"#, + ); + } } mod derive { From c1259aa26fead9a9d365c4436d5ceb00cad88bbe Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 28 Aug 2025 23:31:46 +0800 Subject: [PATCH 0983/1900] Add LSX accelerated implementation for source file analysis This patch introduces an LSX-optimized version of `analyze_source_file` for the `loongarch64` target. Similar to existing SSE2 implementation for x86, this version: - Processes 16-byte chunks at a time using LSX vector intrinsics. - Quickly identifies newlines in ASCII-only chunks. - Falls back to the generic implementation when multi-byte UTF-8 characters are detected or in the tail portion. --- .../rustc_span/src/analyze_source_file.rs | 109 +++++++++++++++++- compiler/rustc_span/src/lib.rs | 1 + 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index c32593a6d95ab..bb2cda77dffff 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -81,8 +81,8 @@ cfg_select! { // use `loadu`, which supports unaligned loading. let chunk = unsafe { _mm_loadu_si128(chunk.as_ptr() as *const __m128i) }; - // For character in the chunk, see if its byte value is < 0, which - // indicates that it's part of a UTF-8 char. + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); // Create a bit mask from the comparison results. let multibyte_mask = _mm_movemask_epi8(multibyte_test); @@ -132,8 +132,111 @@ cfg_select! { } } } + target_arch = "loongarch64" => { + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + use std::arch::is_loongarch_feature_detected; + + if is_loongarch_feature_detected!("lsx") { + unsafe { + analyze_source_file_lsx(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// LSX intrinsics to quickly find all newlines. + #[target_feature(enable = "lsx")] + unsafe fn analyze_source_file_lsx( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + use std::arch::loongarch64::*; + + const CHUNK_SIZE: usize = 16; + + let (chunks, tail) = src.as_bytes().as_chunks::(); + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for (chunk_index, chunk) in chunks.iter().enumerate() { + // All LSX memory instructions support unaligned access, so using + // vld is fine. + let chunk = unsafe { lsx_vld::<0>(chunk.as_ptr() as *const i8) }; + + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. + let multibyte_mask = lsx_vmskltz_b(chunk); + // Create a bit mask from the comparison results. + let multibyte_mask = lsx_vpickve2gr_w::<0>(multibyte_mask); + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check for newlines in the chunk + let newlines_test = lsx_vseqi_b::<{b'\n' as i32}>(chunk); + let newlines_mask = lsx_vmskltz_b(newlines_test); + let mut newlines_mask = lsx_vpickve2gr_w::<0>(newlines_mask); + + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + while newlines_mask != 0 { + let index = newlines_mask.trailing_zeros(); + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= newlines_mask - 1; + } + } else { + // The slow path. + // There are multibyte chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); + } + } + + // There might still be a tail left to analyze + let tail_start = src.len() - tail.len() + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + RelativeBytePos::from_usize(tail_start), + lines, + multi_byte_chars, + ); + } + } + } _ => { - // The target (or compiler version) does not support SSE2 ... + // The target (or compiler version) does not support vector instructions + // our specialized implementations need (x86 SSE2, loongarch64 LSX)... fn analyze_source_file_dispatch( src: &str, lines: &mut Vec, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 35dbbe58db9a4..ededbea57e966 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,6 +17,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] From 0d658fe156c077e0f37068d5788f6c375d555b54 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sun, 28 Sep 2025 00:55:47 +0100 Subject: [PATCH 0984/1900] test: add `keyword/soup.rs` --- tests/ui/keyword/soup.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/keyword/soup.rs diff --git a/tests/ui/keyword/soup.rs b/tests/ui/keyword/soup.rs new file mode 100644 index 0000000000000..c4dbe3fb48341 --- /dev/null +++ b/tests/ui/keyword/soup.rs @@ -0,0 +1,30 @@ +//@ edition:2024 +//@ check-pass + +#![allow(unused_imports)] +#![allow(missing_abi)] +#![allow(unused_macros)] +#![allow(non_camel_case_types)] +#![allow(unreachable_code)] +#![allow(unused_variables)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +// all 48 keywords in 300 characters +mod x { + pub(super) struct X; + use Ok; + impl X { + pub(in crate) async fn x(self: Self, x: &'static &'_ dyn for<> Fn()) where { + unsafe extern { safe fn x(); } + macro_rules! x { () => {}; } + if 'x: loop { + return match while let true = break 'x false { continue } { + ref x => { &raw mut x; async { const { enum A {} } }.await as () }, + }; + } { type x = X; } else { move || { trait x { } union B { x: () } }; } + } + } +} + +fn main() {} From 5debc4368cde86b1c794725030f65de215d3ece4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 28 Sep 2025 04:52:54 +0000 Subject: [PATCH 0985/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 848e6746fe03dfd703075c5077312b63877d51d6. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index c06de8db0a513..2bf64d62060da 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b733736ea2feb7798c99cbb9a769bce74be108df +848e6746fe03dfd703075c5077312b63877d51d6 From b3f3e36c72952736be5b9e0360ee5b86148f2c29 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sat, 27 Sep 2025 20:00:54 -0700 Subject: [PATCH 0986/1900] compiler: remove AbiAlign inside TargetDataLayout This maintains AbiAlign usage in public API and most of the compiler, but direct access of these fields is now in terms of Align only. --- compiler/rustc_abi/src/callconv/reg.rs | 22 ++--- compiler/rustc_abi/src/layout.rs | 65 +++++++------- compiler/rustc_abi/src/layout/simple.rs | 27 +++--- compiler/rustc_abi/src/lib.rs | 85 +++++++++---------- compiler/rustc_codegen_llvm/src/consts.rs | 4 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 8 +- compiler/rustc_session/src/config/cfg.rs | 10 +-- compiler/rustc_target/src/callconv/mips.rs | 2 +- compiler/rustc_target/src/callconv/mips64.rs | 4 +- compiler/rustc_target/src/callconv/mod.rs | 2 +- compiler/rustc_target/src/callconv/sparc.rs | 2 +- compiler/rustc_target/src/callconv/sparc64.rs | 2 +- 13 files changed, 115 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 8cf140dbaad4e..66c8056d0c2af 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -42,22 +42,22 @@ impl Reg { let dl = cx.data_layout(); match self.kind { RegKind::Integer => match self.size.bits() { - 1 => dl.i1_align.abi, - 2..=8 => dl.i8_align.abi, - 9..=16 => dl.i16_align.abi, - 17..=32 => dl.i32_align.abi, - 33..=64 => dl.i64_align.abi, - 65..=128 => dl.i128_align.abi, + 1 => dl.i1_align, + 2..=8 => dl.i8_align, + 9..=16 => dl.i16_align, + 17..=32 => dl.i32_align, + 33..=64 => dl.i64_align, + 65..=128 => dl.i128_align, _ => panic!("unsupported integer: {self:?}"), }, RegKind::Float => match self.size.bits() { - 16 => dl.f16_align.abi, - 32 => dl.f32_align.abi, - 64 => dl.f64_align.abi, - 128 => dl.f128_align.abi, + 16 => dl.f16_align, + 32 => dl.f32_align, + 64 => dl.f64_align, + 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, - RegKind::Vector => dl.llvmlike_vector_align(self.size).abi, + RegKind::Vector => dl.llvmlike_vector_align(self.size), } } } diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5004d0c80220f..09b4322e299b8 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -174,11 +174,11 @@ impl LayoutCalculator { // Non-power-of-two vectors have padding up to the next power-of-two. // If we're a packed repr, remove the padding while keeping the alignment as close // to a vector as possible. - (BackendRepr::Memory { sized: true }, AbiAlign { abi: Align::max_aligned_factor(size) }) + (BackendRepr::Memory { sized: true }, Align::max_aligned_factor(size)) } else { (BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size)) }; - let size = size.align_to(align.abi); + let size = size.align_to(align); Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, @@ -190,7 +190,7 @@ impl LayoutCalculator { largest_niche: elt.largest_niche, uninhabited: false, size, - align, + align: AbiAlign::new(align), max_repr_align: None, unadjusted_abi_align: elt.align.abi, randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)), @@ -388,7 +388,7 @@ impl LayoutCalculator { return Err(LayoutCalculatorError::UnexpectedUnsized(*field)); } - align = align.max(field.align); + align = align.max(field.align.abi); max_repr_align = max_repr_align.max(field.max_repr_align); size = cmp::max(size, field.size); @@ -423,13 +423,13 @@ impl LayoutCalculator { } if let Some(pack) = repr.pack { - align = align.min(AbiAlign::new(pack)); + align = align.min(pack); } // The unadjusted ABI alignment does not include repr(align), but does include repr(pack). // See documentation on `LayoutData::unadjusted_abi_align`. - let unadjusted_abi_align = align.abi; + let unadjusted_abi_align = align; if let Some(repr_align) = repr.align { - align = align.max(AbiAlign::new(repr_align)); + align = align.max(repr_align); } // `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate. let align = align; @@ -441,14 +441,12 @@ impl LayoutCalculator { Ok(Some((repr, _))) => match repr { // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) - if repr.scalar_align(dl).unwrap() != align.abi => + if repr.scalar_align(dl).unwrap() != align => { BackendRepr::Memory { sized: true } } // Vectors require at least element alignment, else disable the opt - BackendRepr::SimdVector { element, count: _ } - if element.align(dl).abi > align.abi => - { + BackendRepr::SimdVector { element, count: _ } if element.align(dl).abi > align => { BackendRepr::Memory { sized: true } } // the alignment tests passed and we can use this @@ -474,8 +472,8 @@ impl LayoutCalculator { backend_repr, largest_niche: None, uninhabited: false, - align, - size: size.align_to(align.abi), + align: AbiAlign::new(align), + size: size.align_to(align), max_repr_align, unadjusted_abi_align, randomization_seed: combined_seed, @@ -611,7 +609,7 @@ impl LayoutCalculator { let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; - let mut unadjusted_abi_align = align.abi; + let mut unadjusted_abi_align = align; let mut variant_layouts = variants .iter_enumerated() @@ -619,7 +617,7 @@ impl LayoutCalculator { let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?; st.variants = Variants::Single { index: j }; - align = align.max(st.align); + align = align.max(st.align.abi); max_repr_align = max_repr_align.max(st.max_repr_align); unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align); @@ -646,7 +644,7 @@ impl LayoutCalculator { let (niche_start, niche_scalar) = niche.reserve(dl, count)?; let niche_offset = niche.offset; let niche_size = niche.value.size(dl); - let size = variant_layouts[largest_variant_index].size.align_to(align.abi); + let size = variant_layouts[largest_variant_index].size.align_to(align); let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| { if i == largest_variant_index { @@ -699,7 +697,7 @@ impl LayoutCalculator { .iter_enumerated() .all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO); let same_size = size == variant_layouts[largest_variant_index].size; - let same_align = align == variant_layouts[largest_variant_index].align; + let same_align = align == variant_layouts[largest_variant_index].align.abi; let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited()); let abi = if same_size && same_align && others_zst { @@ -746,7 +744,7 @@ impl LayoutCalculator { largest_niche, uninhabited, size, - align, + align: AbiAlign::new(align), max_repr_align, unadjusted_abi_align, randomization_seed: combined_seed, @@ -818,7 +816,7 @@ impl LayoutCalculator { let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; - let mut unadjusted_abi_align = align.abi; + let mut unadjusted_abi_align = align; let mut size = Size::ZERO; @@ -860,7 +858,7 @@ impl LayoutCalculator { } } size = cmp::max(size, st.size); - align = align.max(st.align); + align = align.max(st.align.abi); max_repr_align = max_repr_align.max(st.max_repr_align); unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align); Ok(st) @@ -868,7 +866,7 @@ impl LayoutCalculator { .collect::, _>>()?; // Align the maximum variant size to the largest alignment. - size = size.align_to(align.abi); + size = size.align_to(align); // FIXME(oli-obk): deduplicate and harden these checks if size.bytes() >= dl.obj_size_bound() { @@ -1042,7 +1040,7 @@ impl LayoutCalculator { }; if pair_offsets[FieldIdx::new(0)] == Size::ZERO && pair_offsets[FieldIdx::new(1)] == *offset - && align == pair.align + && align == pair.align.abi && size == pair.size { // We can use `ScalarPair` only when it matches our @@ -1066,7 +1064,7 @@ impl LayoutCalculator { // Also need to bump up the size and alignment, so that the entire value fits // in here. variant.size = cmp::max(variant.size, size); - variant.align.abi = cmp::max(variant.align.abi, align.abi); + variant.align.abi = cmp::max(variant.align.abi, align); } } } @@ -1092,7 +1090,7 @@ impl LayoutCalculator { largest_niche, uninhabited, backend_repr: abi, - align, + align: AbiAlign::new(align), size, max_repr_align, unadjusted_abi_align, @@ -1288,7 +1286,7 @@ impl LayoutCalculator { if let StructKind::Prefixed(prefix_size, prefix_align) = kind { let prefix_align = if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; - align = align.max(AbiAlign::new(prefix_align)); + align = align.max(prefix_align); offset = prefix_size.align_to(prefix_align); } for &i in &inverse_memory_index { @@ -1312,7 +1310,7 @@ impl LayoutCalculator { field.align }; offset = offset.align_to(field_align.abi); - align = align.max(field_align); + align = align.max(field_align.abi); max_repr_align = max_repr_align.max(field.max_repr_align); debug!("univariant offset: {:?} field: {:#?}", offset, field); @@ -1339,9 +1337,9 @@ impl LayoutCalculator { // The unadjusted ABI alignment does not include repr(align), but does include repr(pack). // See documentation on `LayoutData::unadjusted_abi_align`. - let unadjusted_abi_align = align.abi; + let unadjusted_abi_align = align; if let Some(repr_align) = repr.align { - align = align.max(AbiAlign::new(repr_align)); + align = align.max(repr_align); } // `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate. let align = align; @@ -1360,7 +1358,7 @@ impl LayoutCalculator { debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices())); inverse_memory_index.into_iter().map(|it| it.index() as u32).collect() }; - let size = min_size.align_to(align.abi); + let size = min_size.align_to(align); // FIXME(oli-obk): deduplicate and harden these checks if size.bytes() >= dl.obj_size_bound() { return Err(LayoutCalculatorError::SizeOverflow); @@ -1383,8 +1381,7 @@ impl LayoutCalculator { layout_of_single_non_zst_field = Some(field); // Field fills the struct and it has a scalar or scalar pair ABI. - if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size - { + if offsets[i].bytes() == 0 && align == field.align.abi && size == field.size { match field.backend_repr { // For plain scalars, or vectors of them, we can't unpack // newtypes for `#[repr(C)]`, as that affects C ABIs. @@ -1428,7 +1425,7 @@ impl LayoutCalculator { }; if offsets[i] == pair_offsets[FieldIdx::new(0)] && offsets[j] == pair_offsets[FieldIdx::new(1)] - && align == pair.align + && align == pair.align.abi && size == pair.size { // We can use `ScalarPair` only when it matches our @@ -1450,7 +1447,7 @@ impl LayoutCalculator { Some(l) => l.unadjusted_abi_align, None => { // `repr(transparent)` with all ZST fields. - align.abi + align } } } else { @@ -1465,7 +1462,7 @@ impl LayoutCalculator { backend_repr: abi, largest_niche, uninhabited, - align, + align: AbiAlign::new(align), size, max_repr_align, unadjusted_abi_align, diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index 0d0706defc2e5..b3807c8727396 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -4,7 +4,8 @@ use rustc_hashes::Hash64; use rustc_index::{Idx, IndexVec}; use crate::{ - BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants, + AbiAlign, BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, + Variants, }; /// "Simple" layout constructors that cannot fail. @@ -20,10 +21,10 @@ impl LayoutData { backend_repr: BackendRepr::Memory { sized }, largest_niche: None, uninhabited: false, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::new(0), } } @@ -37,10 +38,10 @@ impl LayoutData { backend_repr: BackendRepr::Memory { sized: true }, largest_niche: None, uninhabited: true, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::ZERO, } } @@ -89,10 +90,10 @@ impl LayoutData { pub fn scalar_pair(cx: &C, a: Scalar, b: Scalar) -> Self { let dl = cx.data_layout(); - let b_align = b.align(dl); - let align = a.align(dl).max(b_align).max(dl.aggregate_align); - let b_offset = a.size(dl).align_to(b_align.abi); - let size = (b_offset + b.size(dl)).align_to(align.abi); + let b_align = b.align(dl).abi; + let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align); + let b_offset = a.size(dl).align_to(b_align); + let size = (b_offset + b.size(dl)).align_to(align); // HACK(nox): We iter on `b` and then `a` because `max_by_key` // returns the last maximum. @@ -112,10 +113,10 @@ impl LayoutData { backend_repr: BackendRepr::ScalarPair(a, b), largest_niche, uninhabited: false, - align, + align: AbiAlign::new(align), size, max_repr_align: None, - unadjusted_abi_align: align.abi, + unadjusted_abi_align: align, randomization_seed: Hash64::new(combined_seed), } } @@ -138,10 +139,10 @@ impl LayoutData { backend_repr: BackendRepr::Memory { sized: true }, largest_niche: None, uninhabited: true, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::ZERO, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 369874521e57e..fe7148d1d5729 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -229,7 +229,7 @@ pub struct PointerSpec { /// The size of the bitwise representation of the pointer. pointer_size: Size, /// The alignment of pointers for this address space - pointer_align: AbiAlign, + pointer_align: Align, /// The size of the value a pointer can be offset by in this address space. pointer_offset: Size, /// Pointers into this address space contain extra metadata @@ -242,20 +242,20 @@ pub struct PointerSpec { #[derive(Debug, PartialEq, Eq)] pub struct TargetDataLayout { pub endian: Endian, - pub i1_align: AbiAlign, - pub i8_align: AbiAlign, - pub i16_align: AbiAlign, - pub i32_align: AbiAlign, - pub i64_align: AbiAlign, - pub i128_align: AbiAlign, - pub f16_align: AbiAlign, - pub f32_align: AbiAlign, - pub f64_align: AbiAlign, - pub f128_align: AbiAlign, - pub aggregate_align: AbiAlign, + pub i1_align: Align, + pub i8_align: Align, + pub i16_align: Align, + pub i32_align: Align, + pub i64_align: Align, + pub i128_align: Align, + pub f16_align: Align, + pub f32_align: Align, + pub f64_align: Align, + pub f128_align: Align, + pub aggregate_align: Align, /// Alignments for vector types. - pub vector_align: Vec<(Size, AbiAlign)>, + pub vector_align: Vec<(Size, Align)>, pub default_address_space: AddressSpace, pub default_address_space_pointer_spec: PointerSpec, @@ -282,25 +282,25 @@ impl Default for TargetDataLayout { let align = |bits| Align::from_bits(bits).unwrap(); TargetDataLayout { endian: Endian::Big, - i1_align: AbiAlign::new(align(8)), - i8_align: AbiAlign::new(align(8)), - i16_align: AbiAlign::new(align(16)), - i32_align: AbiAlign::new(align(32)), - i64_align: AbiAlign::new(align(32)), - i128_align: AbiAlign::new(align(32)), - f16_align: AbiAlign::new(align(16)), - f32_align: AbiAlign::new(align(32)), - f64_align: AbiAlign::new(align(64)), - f128_align: AbiAlign::new(align(128)), - aggregate_align: AbiAlign { abi: align(8) }, + i1_align: align(8), + i8_align: align(8), + i16_align: align(16), + i32_align: align(32), + i64_align: align(32), + i128_align: align(32), + f16_align: align(16), + f32_align: align(32), + f64_align: align(64), + f128_align: align(128), + aggregate_align: align(8), vector_align: vec![ - (Size::from_bits(64), AbiAlign::new(align(64))), - (Size::from_bits(128), AbiAlign::new(align(128))), + (Size::from_bits(64), align(64)), + (Size::from_bits(128), align(128)), ], default_address_space: AddressSpace::ZERO, default_address_space_pointer_spec: PointerSpec { pointer_size: Size::from_bits(64), - pointer_align: AbiAlign::new(align(64)), + pointer_align: align(64), pointer_offset: Size::from_bits(64), _is_fat: false, }, @@ -360,7 +360,7 @@ impl TargetDataLayout { .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err }) }; let abi = parse_bits(s, "alignment", cause)?; - Ok(AbiAlign::new(align_from_bits(abi)?)) + Ok(align_from_bits(abi)?) }; // Parse an alignment sequence, possibly in the form `[:]`, @@ -596,7 +596,7 @@ impl TargetDataLayout { /// psABI-mandated alignment for a vector type, if any #[inline] - fn cabi_vector_align(&self, vec_size: Size) -> Option { + fn cabi_vector_align(&self, vec_size: Size) -> Option { self.vector_align .iter() .find(|(size, _align)| *size == vec_size) @@ -605,10 +605,9 @@ impl TargetDataLayout { /// an alignment resembling the one LLVM would pick for a vector #[inline] - pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAlign { - self.cabi_vector_align(vec_size).unwrap_or(AbiAlign::new( - Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(), - )) + pub fn llvmlike_vector_align(&self, vec_size: Size) -> Align { + self.cabi_vector_align(vec_size) + .unwrap_or(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) } /// Get the pointer size in the default data address space. @@ -654,21 +653,19 @@ impl TargetDataLayout { /// Get the pointer alignment in the default data address space. #[inline] pub fn pointer_align(&self) -> AbiAlign { - self.default_address_space_pointer_spec.pointer_align + AbiAlign::new(self.default_address_space_pointer_spec.pointer_align) } /// Get the pointer alignment in a specific address space. #[inline] pub fn pointer_align_in(&self, c: AddressSpace) -> AbiAlign { - if c == self.default_address_space { - return self.default_address_space_pointer_spec.pointer_align; - } - - if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) { + AbiAlign::new(if c == self.default_address_space { + self.default_address_space_pointer_spec.pointer_align + } else if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) { e.1.pointer_align } else { panic!("Use of unknown address space {c:?}"); - } + }) } } @@ -1185,13 +1182,13 @@ impl Integer { use Integer::*; let dl = cx.data_layout(); - match self { + AbiAlign::new(match self { I8 => dl.i8_align, I16 => dl.i16_align, I32 => dl.i32_align, I64 => dl.i64_align, I128 => dl.i128_align, - } + }) } /// Returns the largest signed value that can be represented by this Integer. @@ -1311,12 +1308,12 @@ impl Float { use Float::*; let dl = cx.data_layout(); - match self { + AbiAlign::new(match self { F16 => dl.f16_align, F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, - } + }) } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a110ecbb75d95..40375ef651092 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -564,7 +564,7 @@ impl<'ll> CodegenCx<'ll, '_> { let g = self.define_global(&sym, llty).unwrap_or_else(|| { bug!("symbol `{}` is already defined", sym); }); - set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi); + set_global_alignment(self, g, self.tcx.data_layout.i8_align); llvm::set_initializer(g, llval); llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); llvm::set_section(g, c"__TEXT,__cstring,cstring_literals"); @@ -680,7 +680,7 @@ impl<'ll> CodegenCx<'ll, '_> { let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| { bug!("symbol `{}` is already defined", methname_sym); }); - set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi); + set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align); llvm::set_initializer(methname_g, methname_llval); llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage); llvm::set_section( diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 50398a32142eb..94a74e27bbcb4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1047,7 +1047,7 @@ fn codegen_emcc_try<'ll, 'tcx>( // create an alloca and pass a pointer to that. let ptr_size = bx.tcx().data_layout.pointer_size(); let ptr_align = bx.tcx().data_layout.pointer_align().abi; - let i8_align = bx.tcx().data_layout.i8_align.abi; + let i8_align = bx.tcx().data_layout.i8_align; // Required in order for there to be no padding between the fields. assert!(i8_align <= ptr_align); let catch_data = bx.alloca(2 * ptr_size, ptr_align); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index ab08125217ff5..4d6fc3e19c5c6 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -291,7 +291,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) // fpr }; - let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align.abi); + let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align); // "Align" the register count when the type is passed as `i64`. if is_i64 || (is_f64 && is_soft_float_abi) { @@ -329,7 +329,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Increase the used-register count. let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 }; let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr)); - bx.store(new_num_regs, num_regs_addr, dl.i8_align.abi); + bx.store(new_num_regs, num_regs_addr, dl.i8_align); bx.br(end); @@ -339,7 +339,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( let mem_addr = { bx.switch_to_block(in_mem); - bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align.abi); + bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align); // Everything in the overflow area is rounded up to a size of at least 4. let overflow_area_align = Align::from_bytes(4).unwrap(); @@ -813,7 +813,7 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( let va_ndx_offset = va_reg_offset + 4; let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset)); - let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); + let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align); let offset = round_up_to_alignment(bx, offset, layout.align.abi); let slot_size = layout.size.align_to(Align::from_bytes(4).unwrap()).bytes() as i32; diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index f3d91ce4a5dd8..a72f6201dcea2 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -259,11 +259,11 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { }); let mut has_atomic = false; for (i, align) in [ - (8, layout.i8_align.abi), - (16, layout.i16_align.abi), - (32, layout.i32_align.abi), - (64, layout.i64_align.abi), - (128, layout.i128_align.abi), + (8, layout.i8_align), + (16, layout.i16_align), + (32, layout.i32_align), + (64, layout.i64_align), + (128, layout.i128_align), ] { if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() { if !has_atomic { diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 48a01da865b7c..8ffd7bd177849 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -24,7 +24,7 @@ where } let dl = cx.data_layout(); let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 0209838bec181..8386a15933c98 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -110,9 +110,9 @@ where // We only care about aligned doubles if let BackendRepr::Scalar(scalar) = field.backend_repr { if scalar.primitive() == Primitive::Float(Float::F64) { - if offset.is_aligned(dl.f64_align.abi) { + if offset.is_aligned(dl.f64_align) { // Insert enough integers to cover [last_offset, offset) - assert!(last_offset.is_aligned(dl.f64_align.abi)); + assert!(last_offset.is_aligned(dl.f64_align)); for _ in 0..((offset - last_offset).bits() / 64) .min((prefix.len() - prefix_index) as u64) { diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7a7c63c475b04..c59af581a1fe4 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -332,7 +332,7 @@ impl CastTarget { self.prefix .iter() .filter_map(|x| x.map(|reg| reg.align(cx))) - .fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| { + .fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), |acc, align| { acc.max(align) }) } diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 48a01da865b7c..8ffd7bd177849 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -24,7 +24,7 @@ where } let dl = cx.data_layout(); let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index ecc9067ced35e..62c8ed1dc21b1 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -29,7 +29,7 @@ where data.has_float = true; - if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset { + if !data.last_offset.is_aligned(dl.f64_align) && data.last_offset < offset { if data.prefix_index == data.prefix.len() { return data; } From 77c96c031b40298a013befa2d3ebf11f459f9275 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 23:07:11 +0200 Subject: [PATCH 0987/1900] clean-up --- tests/ui/double_parens.rs | 9 +-------- tests/ui/double_parens.stderr | 10 +++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 7c976015b4e79..1e03033615950 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code, clippy::eq_op)] +#![expect(clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -14,32 +14,26 @@ impl DummyStruct { fn simple_double_parens() -> i32 { ((0)) //~^ double_parens - - } fn fn_double_parens() { dummy_fn((0)); //~^ double_parens - } fn method_double_parens(x: DummyStruct) { x.dummy_method((0)); //~^ double_parens - } fn tuple_double_parens() -> (i32, i32) { ((1, 2)) //~^ double_parens - } fn unit_double_parens() { (()) //~^ double_parens - } fn fn_tuple_ok() { @@ -63,7 +57,6 @@ fn inside_macro() { assert_eq!((1, 2), (1, 2), "Error"); assert_eq!(((1, 2)), (1, 2), "Error"); //~^ double_parens - } fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index e119f54949b1f..da48219c97e10 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -8,31 +8,31 @@ LL | ((0)) = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:22:14 + --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); | ^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:28:20 + --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); | ^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:34:5 + --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) | ^^^^^^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:40:5 + --> tests/ui/double_parens.rs:35:5 | LL | (()) | ^^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:64:16 + --> tests/ui/double_parens.rs:58:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ From dbef783309a6692bd0e6573055f3e6252d444708 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:13:08 +0200 Subject: [PATCH 0988/1900] call args "args" in the `Call` case as well since that's what they are --- clippy_lints/src/double_parens.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 4dd8f01ee7090..73078547c695d 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -42,11 +42,11 @@ impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { let span = match &expr.kind { ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, params) - if let [param] = &**params - && let ExprKind::Paren(_) = param.kind => + ExprKind::Call(_, args) + if let [args] = &**args + && let ExprKind::Paren(_) = args.kind => { - param.span + args.span }, ExprKind::MethodCall(call) if let [arg] = &*call.args From 6a6b4a3ba8ef0793f275e9eb8351f8acea72e8f1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:14:04 +0200 Subject: [PATCH 0989/1900] combine the `Call` and `MethodCall` cases --- clippy_lints/src/double_parens.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 73078547c695d..d014efcde48dd 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -42,18 +42,12 @@ impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { let span = match &expr.kind { ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, args) + ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) if let [args] = &**args && let ExprKind::Paren(_) = args.kind => { args.span }, - ExprKind::MethodCall(call) - if let [arg] = &*call.args - && let ExprKind::Paren(_) = arg.kind => - { - arg.span - }, _ => return, }; if !expr.span.from_expansion() { From 82b2eb13f8ed8777c97cdceb94137a4ed73d43f6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:18:03 +0200 Subject: [PATCH 0990/1900] add structured suggestions --- clippy_lints/src/double_parens.rs | 62 ++++++++++++++++++++++-------- tests/ui/double_parens.fixed | 63 +++++++++++++++++++++++++++++++ tests/ui/double_parens.rs | 1 + tests/ui/double_parens.stderr | 28 +++++++------- 4 files changed, 124 insertions(+), 30 deletions(-) create mode 100644 tests/ui/double_parens.fixed diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index d014efcde48dd..ab6e477121207 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{HasSession, snippet_with_applicability}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -24,7 +26,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn simple_no_parens() -> i32 { - /// 0 + /// (0) /// } /// /// # fn foo(bar: usize) {} @@ -40,23 +42,51 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - let span = match &expr.kind { - ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, + if expr.span.from_expansion() { + return; + } + + match &expr.kind { + // ((..)) + // ^^^^^^ expr + // ^^^^ inner + ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { + // suggest removing the outer parens + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + }, + + // func((n)) + // ^^^^^^^^^ expr + // ^^^ arg + // ^ inner ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) - if let [args] = &**args - && let ExprKind::Paren(_) = args.kind => + if let [arg] = &**args + && let ExprKind::Paren(inner) = &arg.kind => { - args.span + // suggest removing the inner parens + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, - _ => return, - }; - if !expr.span.from_expansion() { - span_lint( - cx, - DOUBLE_PARENS, - span, - "consider removing unnecessary double parentheses", - ); + _ => {}, } } } diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed new file mode 100644 index 0000000000000..7fa116942fe85 --- /dev/null +++ b/tests/ui/double_parens.fixed @@ -0,0 +1,63 @@ +#![warn(clippy::double_parens)] +#![expect(clippy::eq_op)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method(self, _: T) {} +} + +fn simple_double_parens() -> i32 { + (0) + //~^ double_parens +} + +fn fn_double_parens() { + dummy_fn(0); + //~^ double_parens +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method(0); + //~^ double_parens +} + +fn tuple_double_parens() -> (i32, i32) { + (1, 2) + //~^ double_parens +} + +#[allow(clippy::unused_unit)] +fn unit_double_parens() { + () + //~^ double_parens +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!((1, 2), (1, 2), "Error"); + //~^ double_parens +} + +fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 1e03033615950..8730b4b124689 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -31,6 +31,7 @@ fn tuple_double_parens() -> (i32, i32) { //~^ double_parens } +#[allow(clippy::unused_unit)] fn unit_double_parens() { (()) //~^ double_parens diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index da48219c97e10..2b37877ccdd86 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,41 +1,41 @@ -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:15:5 | LL | ((0)) - | ^^^^^ + | ^^^^^ help: remove them: `(0)` | = note: `-D clippy::double-parens` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:35:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:36:5 | LL | (()) - | ^^^^ + | ^^^^ help: remove them: `()` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:58:16 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:59:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` error: aborting due to 6 previous errors From cc7bd291cdd76f1e1ef83223ec7bec4077646ac7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 28 Sep 2025 17:17:44 +1000 Subject: [PATCH 0991/1900] Fix change-tracker entry for 147046 --- src/bootstrap/src/utils/change_tracker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index f311c84bec9b5..0123bd6340e50 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -557,7 +557,7 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.", }, ChangeInfo { - change_id: 99999, + change_id: 147046, severity: ChangeSeverity::Warning, summary: "The `rust.use-lld` option has been renamed to `rust.bootstrap-override-lld`. Note that it only serves for overriding the linker used when building Rust code in bootstrap to be LLD.", }, From e99364ea357957b33460664fc496a3a8c218494e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 23 Sep 2025 18:12:33 +1000 Subject: [PATCH 0992/1900] Remove old-output-capture from compiletest --- src/tools/compiletest/src/common.rs | 9 ------- src/tools/compiletest/src/executor.rs | 35 ++++++++------------------- src/tools/compiletest/src/lib.rs | 32 ------------------------ 3 files changed, 10 insertions(+), 66 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6da102b1b5f11..6d9599489180b 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -661,18 +661,10 @@ pub struct Config { pub builtin_cfg_names: OnceLock>, pub supported_crate_types: OnceLock>, - /// FIXME: this is why we still need to depend on *staged* `std`, it's because we currently rely - /// on `#![feature(internal_output_capture)]` for [`std::io::set_output_capture`] to implement - /// `libtest`-esque `--no-capture`. - /// /// FIXME: rename this to the more canonical `no_capture`, or better, invert this to `capture` /// to avoid `!nocapture` double-negatives. pub nocapture: bool, - /// True if the experimental new output-capture implementation should be - /// used, avoiding the need for `#![feature(internal_output_capture)]`. - pub new_output_capture: bool, - /// Needed both to construct [`build_helper::git::GitConfig`]. pub nightly_branch: String, pub git_merge_commit_email: String, @@ -790,7 +782,6 @@ impl Config { builtin_cfg_names: Default::default(), supported_crate_types: Default::default(), nocapture: Default::default(), - new_output_capture: Default::default(), nightly_branch: Default::default(), git_merge_commit_email: Default::default(), profiler_runtime: Default::default(), diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index c8e13d4457383..c7aca6d1c5aaa 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -9,8 +9,8 @@ use std::borrow::Cow; use std::collections::HashMap; use std::hash::{BuildHasherDefault, DefaultHasher}; use std::num::NonZero; -use std::sync::{Arc, Mutex, mpsc}; -use std::{env, hint, io, mem, panic, thread}; +use std::sync::{Arc, mpsc}; +use std::{env, hint, mem, panic, thread}; use camino::Utf8PathBuf; @@ -130,10 +130,6 @@ fn run_test_inner( panic_hook::set_capture_buf(Default::default()); } - if let CaptureKind::Old { ref buf } = capture { - io::set_output_capture(Some(Arc::clone(buf))); - } - let stdout = capture.stdout(); let stderr = capture.stderr(); @@ -144,9 +140,6 @@ fn run_test_inner( // Forward any captured panic message to (captured) stderr. write!(stderr, "{panic_buf}"); } - if matches!(capture, CaptureKind::Old { .. }) { - io::set_output_capture(None); - } let outcome = match (should_panic, panic_payload) { (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestOutcome::Succeeded, @@ -167,31 +160,24 @@ enum CaptureKind { /// runners, whose output is always captured.) None, - /// Use the old output-capture implementation, which relies on the unstable - /// library feature `#![feature(internal_output_capture)]`. - Old { buf: Arc>> }, - - /// Use the new output-capture implementation, which only uses stable Rust. - New { buf: output_capture::CaptureBuf }, + /// Capture all console output that would be printed by test runners via + /// their `stdout` and `stderr` trait objects, or via the custom panic hook. + Capture { buf: output_capture::CaptureBuf }, } impl CaptureKind { fn for_config(config: &Config) -> Self { if config.nocapture { Self::None - } else if config.new_output_capture { - Self::New { buf: output_capture::CaptureBuf::new() } } else { - // Create a capure buffer for `io::set_output_capture`. - Self::Old { buf: Default::default() } + Self::Capture { buf: output_capture::CaptureBuf::new() } } } fn should_set_panic_hook(&self) -> bool { match self { Self::None => false, - Self::Old { .. } => true, - Self::New { .. } => true, + Self::Capture { .. } => true, } } @@ -205,16 +191,15 @@ impl CaptureKind { fn capture_buf_or<'a>(&'a self, fallback: &'a dyn ConsoleOut) -> &'a dyn ConsoleOut { match self { - Self::None | Self::Old { .. } => fallback, - Self::New { buf } => buf, + Self::None => fallback, + Self::Capture { buf } => buf, } } fn into_inner(self) -> Option> { match self { Self::None => None, - Self::Old { buf } => Some(buf.lock().unwrap_or_else(|e| e.into_inner()).to_vec()), - Self::New { buf } => Some(buf.into_inner().into()), + Self::Capture { buf } => Some(buf.into_inner().into()), } } } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index ce2a3d4b5fbb2..15e31dadf9711 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -1,9 +1,4 @@ #![crate_name = "compiletest"] -// Needed by the "new" test executor that does not depend on libtest. -// FIXME(Zalathar): We should be able to get rid of `internal_output_capture`, -// by having `runtest` manually capture all of its println-like output instead. -// That would result in compiletest being written entirely in stable Rust! -#![feature(internal_output_capture)] #[cfg(test)] mod tests; @@ -178,12 +173,6 @@ pub fn parse_config(args: Vec) -> Config { // FIXME: Temporarily retained so we can point users to `--no-capture` .optflag("", "nocapture", "") .optflag("", "no-capture", "don't capture stdout/stderr of tests") - .optopt( - "N", - "new-output-capture", - "enables or disables the new output-capture implementation", - "off|on", - ) .optflag("", "profiler-runtime", "is the profiler runtime enabled for this target") .optflag("h", "help", "show this message") .reqopt("", "channel", "current Rust channel", "CHANNEL") @@ -480,14 +469,6 @@ pub fn parse_config(args: Vec) -> Config { supported_crate_types: OnceLock::new(), nocapture: matches.opt_present("no-capture"), - new_output_capture: { - let value = matches - .opt_str("new-output-capture") - .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok()) - .unwrap_or_else(|| "on".to_owned()); - parse_bool_option(&value) - .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given")) - }, nightly_branch: matches.opt_str("nightly-branch").unwrap(), git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), @@ -503,19 +484,6 @@ pub fn parse_config(args: Vec) -> Config { } } -/// Parses the same set of boolean values accepted by rustc command-line arguments. -/// -/// Accepting all of these values is more complicated than just picking one -/// pair, but has the advantage that contributors who are used to rustc -/// shouldn't have to think about which values are legal. -fn parse_bool_option(value: &str) -> Option { - match value { - "off" | "no" | "n" | "false" => Some(false), - "on" | "yes" | "y" | "true" => Some(true), - _ => None, - } -} - pub fn opt_str(maybestr: &Option) -> &str { match *maybestr { None => "(none)", From 7b8ae74da119e9ba025baeca60c22376f30d1307 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 23 Sep 2025 21:38:11 +1000 Subject: [PATCH 0993/1900] Add a bootstrap snapshot test for `x test compiletest` --- src/bootstrap/src/core/builder/tests.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index a3c8380a3c7d6..66771fbf41c0a 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2145,6 +2145,18 @@ mod snapshot { "); } + #[test] + fn test_compiletest_self_test() { + let ctx = TestCtx::new(); + let steps = ctx.config("test").arg("compiletest").render_steps(); + insta::assert_snapshot!(steps, @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustdoc 1 + "); + } + #[test] fn test_compiletest_suites_stage1() { let ctx = TestCtx::new(); From 193f7179cca1c3b01eb7f2587616a95999e51903 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 23 Sep 2025 18:22:56 +1000 Subject: [PATCH 0994/1900] Build and test compiletest as a stage0 bootstrap tool --- src/bootstrap/src/core/build_steps/check.rs | 11 +++----- src/bootstrap/src/core/build_steps/test.rs | 29 ++++++++++----------- src/bootstrap/src/core/build_steps/tool.rs | 18 +++---------- src/bootstrap/src/core/builder/tests.rs | 18 +------------ src/bootstrap/src/core/config/config.rs | 7 ++--- src/bootstrap/src/core/config/mod.rs | 10 +++++-- src/bootstrap/src/core/config/toml/build.rs | 2 ++ src/bootstrap/src/lib.rs | 4 +-- src/bootstrap/src/utils/change_tracker.rs | 5 ++++ 9 files changed, 40 insertions(+), 64 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 043457f64e57a..27f259d420798 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -8,8 +8,8 @@ use crate::core::build_steps::compile::{ }; use crate::core::build_steps::tool; use crate::core::build_steps::tool::{ - COMPILETEST_ALLOW_FEATURES, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode, - get_tool_target_compiler, prepare_tool_cargo, + SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode, get_tool_target_compiler, + prepare_tool_cargo, }; use crate::core::builder::{ self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, @@ -825,12 +825,7 @@ tool_check_step!(CoverageDump { // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", - mode: |builder: &Builder<'_>| if builder.config.compiletest_use_stage0_libtest { - Mode::ToolBootstrap - } else { - Mode::ToolStd - }, - allow_features: COMPILETEST_ALLOW_FEATURES, + mode: |_builder| Mode::ToolBootstrap, default: false, }); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index ca2731819e7e0..00aea8feab7de 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -18,8 +18,8 @@ use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::get_completion_paths; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{ - self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, - TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler, + self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, + ToolTargetBuildMode, get_tool_target_compiler, }; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; @@ -36,7 +36,7 @@ use crate::utils::helpers::{ linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, debug, envify}; +use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; @@ -786,26 +786,26 @@ impl Step for CompiletestTest { fn run(self, builder: &Builder<'_>) { let host = self.host; + // Now that compiletest uses only stable Rust, building it always uses + // the stage 0 compiler. However, some of its unit tests need to be able + // to query information from an in-tree compiler, so we treat `--stage` + // as selecting the stage of that secondary compiler. + if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 { eprintln!("\ -ERROR: `--stage 0` runs compiletest self-tests against the stage0 (precompiled) compiler, not the in-tree compiler, and will almost always cause tests to fail +ERROR: `--stage 0` causes compiletest to query information from the stage0 (precompiled) compiler, instead of the in-tree compiler, which can cause some tests to fail inappropriately NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`." ); crate::exit!(1); } - let compiler = builder.compiler(builder.top_stage, host); - debug!(?compiler); + let bootstrap_compiler = builder.compiler(0, host); + let staged_compiler = builder.compiler(builder.top_stage, host); - // We need `ToolStd` for the locally-built sysroot because - // compiletest uses unstable features of the `test` crate. - builder.std(compiler, host); let mut cargo = tool::prepare_tool_cargo( builder, - compiler, - // compiletest uses libtest internals; make it use the in-tree std to make sure it never - // breaks when std sources change. - Mode::ToolStd, + bootstrap_compiler, + Mode::ToolBootstrap, host, Kind::Test, "src/tools/compiletest", @@ -816,9 +816,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // Used for `compiletest` self-tests to have the path to the *staged* compiler. Getting this // right is important, as `compiletest` is intended to only support one target spec JSON // format, namely that of the staged compiler. - cargo.env("TEST_RUSTC", builder.rustc(compiler)); + cargo.env("TEST_RUSTC", builder.rustc(staged_compiler)); - cargo.allow_features(COMPILETEST_ALLOW_FEATURES); run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder); } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 4f096d50ea5c3..819e903020c8e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -380,7 +380,6 @@ macro_rules! bootstrap_tool { ($( $name:ident, $path:expr, $tool_name:expr $(,is_external_tool = $external:expr)* - $(,is_unstable_tool = $unstable:expr)* $(,allow_features = $allow_features:expr)? $(,submodules = $submodules:expr)? $(,artifact_kind = $artifact_kind:expr)? @@ -438,19 +437,11 @@ macro_rules! bootstrap_tool { } )* - let is_unstable = false $(|| $unstable)*; - let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest; - builder.ensure(ToolBuild { build_compiler: self.compiler, target: self.target, tool: $tool_name, - mode: if is_unstable && !compiletest_wants_stage0 { - // use in-tree libraries for unstable features - Mode::ToolStd - } else { - Mode::ToolBootstrap - }, + mode: Mode::ToolBootstrap, path: $path, source_type: if false $(|| $external)* { SourceType::Submodule @@ -483,8 +474,6 @@ macro_rules! bootstrap_tool { } } -pub(crate) const COMPILETEST_ALLOW_FEATURES: &str = "internal_output_capture"; - bootstrap_tool!( // This is marked as an external tool because it includes dependencies // from submodules. Trying to keep the lints in sync between all the repos @@ -495,7 +484,7 @@ bootstrap_tool!( Tidy, "src/tools/tidy", "tidy"; Linkchecker, "src/tools/linkchecker", "linkchecker"; CargoTest, "src/tools/cargotest", "cargotest"; - Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES; + Compiletest, "src/tools/compiletest", "compiletest"; BuildManifest, "src/tools/build-manifest", "build-manifest"; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; RustInstaller, "src/tools/rust-installer", "rust-installer"; @@ -509,8 +498,7 @@ bootstrap_tool!( CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata"; GenerateCopyright, "src/tools/generate-copyright", "generate-copyright"; GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; - // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features. - RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES; + RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator"; FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump"; diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 66771fbf41c0a..88df469e9a099 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2004,21 +2004,6 @@ mod snapshot { .render_steps(), @"[check] rustc 0 -> Compiletest 1 "); } - #[test] - fn check_compiletest_stage1_libtest() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("check") - .path("compiletest") - .args(&["--set", "build.compiletest-use-stage0-libtest=false"]) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [check] rustc 1 -> Compiletest 2 - "); - } - #[test] fn check_codegen() { let ctx = TestCtx::new(); @@ -2152,8 +2137,7 @@ mod snapshot { insta::assert_snapshot!(steps, @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustdoc 1 + [build] rustdoc 0 "); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 271ce4cb950bb..1fcc1174e856c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -310,9 +310,6 @@ pub struct Config { /// sources. pub compiletest_allow_stage0: bool, - /// Whether to use the precompiled stage0 libtest with compiletest. - pub compiletest_use_stage0_libtest: bool, - /// Default value for `--extra-checks` pub tidy_extra_checks: Option, pub is_running_on_ci: bool, @@ -497,7 +494,8 @@ impl Config { optimized_compiler_builtins: build_optimized_compiler_builtins, jobs: build_jobs, compiletest_diff_tool: build_compiletest_diff_tool, - compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest, + // No longer has any effect; kept (for now) to avoid breaking people's configs. + compiletest_use_stage0_libtest: _, tidy_extra_checks: build_tidy_extra_checks, ccache: build_ccache, exclude: build_exclude, @@ -1197,7 +1195,6 @@ impl Config { compiler_docs: build_compiler_docs.unwrap_or(false), compiletest_allow_stage0: build_compiletest_allow_stage0.unwrap_or(false), compiletest_diff_tool: build_compiletest_diff_tool, - compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest.unwrap_or(true), config: toml_path, configure_args: build_configure_args.unwrap_or_default(), control_flow_guard: rust_control_flow_guard.unwrap_or(false), diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 56b87823a3654..007ed4aaba13f 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -47,11 +47,17 @@ use crate::str::FromStr; #[macro_export] macro_rules! define_config { ($(#[$attr:meta])* struct $name:ident { - $($field:ident: Option<$field_ty:ty> = $field_key:literal,)* + $( + $(#[$field_attr:meta])* + $field:ident: Option<$field_ty:ty> = $field_key:literal, + )* }) => { $(#[$attr])* pub struct $name { - $(pub $field: Option<$field_ty>,)* + $( + $(#[$field_attr])* + pub $field: Option<$field_ty>, + )* } impl Merge for $name { diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index a9d4d3961c9b7..c63673dd98089 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -70,6 +70,8 @@ define_config! { jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", compiletest_allow_stage0: Option = "compiletest-allow-stage0", + /// No longer has any effect; kept (for now) to avoid breaking people's configs. + /// FIXME(#146929): Remove this in 2026. compiletest_use_stage0_libtest: Option = "compiletest-use-stage0-libtest", tidy_extra_checks: Option = "tidy-extra-checks", ccache: Option = "ccache", diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d798639cc967e..dd30f05b72832 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -327,8 +327,8 @@ pub enum Mode { ToolTarget, /// Build a tool which uses the locally built std, placing output in the - /// "stageN-tools" directory. Its usage is quite rare, mainly used by - /// compiletest which needs libtest. + /// "stageN-tools" directory. Its usage is quite rare; historically it was + /// needed by compiletest, but now it is mainly used by `test-float-parse`. ToolStd, /// Build a tool which uses the `rustc_private` mechanism, and thus diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 0123bd6340e50..853fc4e6623e9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -561,4 +561,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "The `rust.use-lld` option has been renamed to `rust.bootstrap-override-lld`. Note that it only serves for overriding the linker used when building Rust code in bootstrap to be LLD.", }, + ChangeInfo { + change_id: 146929, + severity: ChangeSeverity::Info, + summary: "`compiletest` is now always built with the stage 0 compiler, so `build.compiletest-use-stage0-libtest` has no effect.", + }, ]; From 8b1879864b4487c89ee414fa64abe42440fa922b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 23 Sep 2025 22:05:12 +1000 Subject: [PATCH 0995/1900] `tool_check_step!` no longer needs a builder to determine mode --- src/bootstrap/src/core/build_steps/check.rs | 44 +++++++++------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 27f259d420798..20c54a28dda83 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -654,7 +654,7 @@ macro_rules! tool_check_step { // The part of this path after the final '/' is also used as a display name. path: $path:literal $(, alt_path: $alt_path:literal )* - // Closure that returns `Mode` based on the passed `&Builder<'_>` + // `Mode` to use when checking this tool , mode: $mode:expr // Subset of nightly features that are allowed to be used when checking $(, allow_features: $allow_features:expr )? @@ -682,8 +682,7 @@ macro_rules! tool_check_step { fn make_run(run: RunConfig<'_>) { let target = run.target; - let builder = run.builder; - let mode = $mode(builder); + let mode: Mode = $mode; let compiler = prepare_compiler_for_check(run.builder, target, mode); @@ -704,7 +703,7 @@ macro_rules! tool_check_step { _value }; let extra_features: &[&str] = &[$($($enable_features),*)?]; - let mode = $mode(builder); + let mode: Mode = $mode; run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features); } @@ -767,57 +766,50 @@ fn run_tool_check_step( tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc", - mode: |_builder| Mode::ToolRustcPrivate + mode: Mode::ToolRustcPrivate }); // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead // of a submodule. Since the SourceType only drives the deny-warnings // behavior, treat it as in-tree so that any new warnings in clippy will be // rejected. -tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustcPrivate }); -tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustcPrivate }); -tool_check_step!(CargoMiri { - path: "src/tools/miri/cargo-miri", - mode: |_builder| Mode::ToolRustcPrivate -}); -tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustcPrivate }); +tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate }); +tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustcPrivate }); +tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate }); +tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate }); tool_check_step!(RustAnalyzer { path: "src/tools/rust-analyzer", - mode: |_builder| Mode::ToolRustcPrivate, + mode: Mode::ToolRustcPrivate, allow_features: tool::RustAnalyzer::ALLOW_FEATURES, enable_features: ["in-rust-tree"], }); tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools", - mode: |_builder| Mode::ToolBootstrap + mode: Mode::ToolBootstrap }); // We want to test the local std tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse", - mode: |_builder| Mode::ToolStd, + mode: Mode::ToolStd, allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES }); tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump", - mode: |_builder| Mode::ToolBootstrap + mode: Mode::ToolBootstrap }); -tool_check_step!(Bootstrap { - path: "src/bootstrap", - mode: |_builder| Mode::ToolBootstrap, - default: false -}); +tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false }); // `run-make-support` will be built as part of suitable run-make compiletest test steps, but support // check to make it easier to work on. tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", - mode: |_builder| Mode::ToolBootstrap, + mode: Mode::ToolBootstrap, default: false }); tool_check_step!(CoverageDump { path: "src/tools/coverage-dump", - mode: |_builder| Mode::ToolBootstrap, + mode: Mode::ToolBootstrap, default: false }); @@ -825,18 +817,18 @@ tool_check_step!(CoverageDump { // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", - mode: |_builder| Mode::ToolBootstrap, + mode: Mode::ToolBootstrap, default: false, }); tool_check_step!(Linkchecker { path: "src/tools/linkchecker", - mode: |_builder| Mode::ToolBootstrap, + mode: Mode::ToolBootstrap, default: false }); tool_check_step!(BumpStage0 { path: "src/tools/bump-stage0", - mode: |_builder| Mode::ToolBootstrap, + mode: Mode::ToolBootstrap, default: false }); From 5e9cab39213b892fdb5eb24d2d27f1ab0918ce46 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sun, 28 Sep 2025 09:46:20 +0100 Subject: [PATCH 0996/1900] modify ensure_version_or_cargo_install to check existing binary Current implementation uses bin_name to check if it exists, but it should use tool_root_dir/tool_bin_dir/bin_name instead. Otherwise the check fails every time, hence the function falls back to install the binary. --- src/tools/tidy/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 0bfee93796be1..874a758bd9b38 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -167,12 +167,16 @@ pub fn ensure_version_or_cargo_install( bin_name: &str, version: &str, ) -> io::Result { + let tool_root_dir = build_dir.join("misc-tools"); + let tool_bin_dir = tool_root_dir.join("bin"); + let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); + // ignore the process exit code here and instead just let the version number check fail. // we also importantly don't return if the program wasn't installed, // instead we want to continue to the fallback. 'ck: { // FIXME: rewrite as if-let chain once this crate is 2024 edition. - let Ok(output) = Command::new(bin_name).arg("--version").output() else { + let Ok(output) = Command::new(&bin_path).arg("--version").output() else { break 'ck; }; let Ok(s) = str::from_utf8(&output.stdout) else { @@ -182,12 +186,10 @@ pub fn ensure_version_or_cargo_install( break 'ck; }; if v == version { - return Ok(PathBuf::from(bin_name)); + return Ok(bin_path); } } - let tool_root_dir = build_dir.join("misc-tools"); - let tool_bin_dir = tool_root_dir.join("bin"); eprintln!("building external tool {bin_name} from package {pkg_name}@{version}"); // use --force to ensure that if the required version is bumped, we update it. // use --target-dir to ensure we have a build cache so repeated invocations aren't slow. @@ -213,7 +215,6 @@ pub fn ensure_version_or_cargo_install( if !cargo_exit_code.success() { return Err(io::Error::other("cargo install failed")); } - let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); assert!( matches!(bin_path.try_exists(), Ok(true)), "cargo install did not produce the expected binary" From b613be223dfc2151bc7aea15019b5ee04607621c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:19:10 +0200 Subject: [PATCH 0997/1900] fix a bug while we're at it --- clippy_lints/src/double_parens.rs | 57 ++++++++++++++++--------------- tests/ui/double_parens.fixed | 40 ++++++++++++++++++++-- tests/ui/double_parens.rs | 40 ++++++++++++++++++++-- tests/ui/double_parens.stderr | 31 ++++++++++++++++- 4 files changed, 136 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index ab6e477121207..bddf4702fb340 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{HasSession, snippet_with_applicability}; +use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -42,27 +42,28 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.from_expansion() { - return; - } - match &expr.kind { // ((..)) // ^^^^^^ expr // ^^^^ inner ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { // suggest removing the outer parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - expr.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); + if expr.span.eq_ctxt(inner.span) { + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, // func((n)) @@ -74,17 +75,19 @@ impl EarlyLintPass for DoubleParens { && let ExprKind::Paren(inner) = &arg.kind => { // suggest removing the inner parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - arg.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); + if expr.span.eq_ctxt(arg.span) { + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, _ => {}, } diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed index 7fa116942fe85..dedc513438d11 100644 --- a/tests/ui/double_parens.fixed +++ b/tests/ui/double_parens.fixed @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![expect(clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,7 +8,7 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { @@ -60,4 +60,40 @@ fn inside_macro() { //~^ double_parens } +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {(100)} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + (vec![1, 2]); + //~^ double_parens + dummy_fn(vec![1, 2]); + //~^ double_parens + x.dummy_method(vec![1, 2]); + //~^ double_parens +} + fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 8730b4b124689..27f252485b714 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![expect(clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,7 +8,7 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { @@ -60,4 +60,40 @@ fn inside_macro() { //~^ double_parens } +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {((100))} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + ((vec![1, 2])); + //~^ double_parens + dummy_fn((vec![1, 2])); + //~^ double_parens + x.dummy_method((vec![1, 2])); + //~^ double_parens +} + fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 2b37877ccdd86..3a740e44cacf3 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -37,5 +37,34 @@ error: unnecessary parentheses LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ help: remove them: `(1, 2)` -error: aborting due to 6 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:84:16 + | +LL | () => {((100))} + | ^^^^^^^ help: remove them: `(100)` +... +LL | bar!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:91:5 + | +LL | ((vec![1, 2])); + | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:93:14 + | +LL | dummy_fn((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:95:20 + | +LL | x.dummy_method((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: aborting due to 10 previous errors From bb1081e5a9a85081d944c885939809a08db84dd3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 11:58:48 +0200 Subject: [PATCH 0998/1900] move unfixable stuff to a separate file --- tests/ui/mut_mut.rs | 9 +++----- tests/ui/mut_mut.stderr | 18 ++++++++-------- tests/ui/mut_mut_unfixable.rs | 34 ++++++++++++++++++++++++++++++ tests/ui/mut_mut_unfixable.stderr | 35 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 tests/ui/mut_mut_unfixable.rs create mode 100644 tests/ui/mut_mut_unfixable.stderr diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index bbcdbc89b6a4b..d006578b3d284 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -12,9 +12,8 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) -> bool { +fn fun(x: &mut &mut u32) { //~^ mut_mut - **x > 0 } fn less_fun(x: *mut *mut u32) { @@ -37,19 +36,17 @@ fn main() { //~^ mut_mut } - if fun(x) { + { let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - **y + **x; } - if fun(x) { + { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut //~| mut_mut - ***y + **x; } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 74b0c9ba145a9..8517c4e5ad89f 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,20 +1,20 @@ error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:15:11 | -LL | fn fun(x: &mut &mut u32) -> bool { +LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:33:17 + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:55:25 + --> tests/ui/mut_mut.rs:52:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -22,37 +22,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:36:21 + --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:32 + --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:16 + --> tests/ui/mut_mut.rs:40:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:37 + --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:16 + --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:21 + --> tests/ui/mut_mut.rs:46:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs new file mode 100644 index 0000000000000..db0552a86ec9b --- /dev/null +++ b/tests/ui/mut_mut_unfixable.rs @@ -0,0 +1,34 @@ +//@no-rustfix + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![expect(clippy::no_effect)] + +//! removing the extra `&mut`s will break the derefs + +fn fun(x: &mut &mut u32) -> bool { + //~^ mut_mut + **x > 0 +} + +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut x; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + let y = &mut &mut 2; + //~^ mut_mut + **y + **x; + } + + if fun(x) { + let y = &mut &mut &mut 2; + //~^ mut_mut + ***y + **x; + } +} diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr new file mode 100644 index 0000000000000..bb0363ef7ed9c --- /dev/null +++ b/tests/ui/mut_mut_unfixable.stderr @@ -0,0 +1,35 @@ +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:9:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:15:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ + +error: this expression mutably borrows a mutable reference. Consider reborrowing + --> tests/ui/mut_mut_unfixable.rs:18:21 + | +LL | let mut y = &mut x; + | ^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:24:17 + | +LL | let y = &mut &mut 2; + | ^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:30:17 + | +LL | let y = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + From a4fba850a90a3a7f9a33d872de09795d9801743f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 23:32:07 +0200 Subject: [PATCH 0999/1900] sugg on expr 1 --- clippy_lints/src/mut_mut.rs | 13 ++++- tests/ui/mut_mut.fixed | 94 +++++++++++++++++++++++++++++++ tests/ui/mut_mut.stderr | 4 +- tests/ui/mut_mut_unfixable.stderr | 4 +- 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 tests/ui/mut_mut.fixed diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d98c70e7f5a85..c8b4ad3f7722c 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; @@ -80,12 +82,17 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability).mut_addr_deref(); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", + "this expression mutably borrows a mutable reference", + |diag| { + diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability); + }, ); } } diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed new file mode 100644 index 0000000000000..cbce826b6e85d --- /dev/null +++ b/tests/ui/mut_mut.fixed @@ -0,0 +1,94 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unnecessary_operation, + clippy::needless_pass_by_ref_mut +)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +fn fun(x: &mut &mut u32) { + //~^ mut_mut +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +#[inline_macros] +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut *x; + //~^ mut_mut + } + + { + let y: &mut &mut u32 = &mut &mut 2; + //~^ mut_mut + //~| mut_mut + } + + { + let y: &mut &mut &mut u32 = &mut &mut &mut 2; + //~^ mut_mut + //~| mut_mut + //~| mut_mut + } + + let mut z = inline!(&mut $(&mut 3u32)); + //~^ mut_mut +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} + +fn issue6922() { + // do not lint from an external macro + external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;); +} + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 8517c4e5ad89f..aa4731e8fe041 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -21,11 +21,11 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); | = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) -error: this expression mutably borrows a mutable reference. Consider reborrowing +error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:40:32 diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index bb0363ef7ed9c..835b272258d39 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -13,11 +13,11 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ -error: this expression mutably borrows a mutable reference. Consider reborrowing +error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut_unfixable.rs:18:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut_unfixable.rs:24:17 From 70be286cefb2dae0a708e0351c09a6a1170cbd54 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 12:14:09 +0200 Subject: [PATCH 1000/1900] sugg on expr 2 --- clippy_lints/src/mut_mut.rs | 11 ++++++++--- tests/ui/mut_mut.fixed | 8 ++++---- tests/ui/mut_mut.stderr | 16 ++++++++-------- tests/ui/mut_mut_unfixable.stderr | 12 ++++++------ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index c8b4ad3f7722c..0feed460d2ed7 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; @@ -72,12 +72,17 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "generally you want to avoid `&mut &mut _` if possible", + "an expression of form `&mut &mut _`", + |diag| { + diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); + }, ); } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index cbce826b6e85d..c6b318d765104 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -29,7 +29,7 @@ macro_rules! mut_ptr { #[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { - let mut x = &mut &mut 1u32; + let mut x = &mut 1u32; //~^ mut_mut { let mut y = &mut *x; @@ -37,19 +37,19 @@ fn main() { } { - let y: &mut &mut u32 = &mut &mut 2; + let y: &mut &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } { - let y: &mut &mut &mut u32 = &mut &mut &mut 2; + let y: &mut &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut //~| mut_mut } - let mut z = inline!(&mut $(&mut 3u32)); + let mut z = inline!(&mut 3u32mut $(&mut 3u32)); //~^ mut_mut } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index aa4731e8fe041..f21de359e64f7 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -7,17 +7,17 @@ LL | fn fun(x: &mut &mut u32) { = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:52:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); - | ^ + | ^ help: remove the extra `&mut`: `&mut 3u32` | = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -27,11 +27,11 @@ error: this expression mutably borrows a mutable reference LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:40:16 @@ -39,11 +39,11 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:46:16 diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index 835b272258d39..129e5b24387e1 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -7,11 +7,11 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:15:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut_unfixable.rs:18:21 @@ -19,17 +19,17 @@ error: this expression mutably borrows a mutable reference LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:24:17 | LL | let y = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:30:17 | LL | let y = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` error: aborting due to 5 previous errors From 269870a5f9b48117ba9bac65e8b9549df421e9c2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 4 Aug 2025 20:21:19 +0200 Subject: [PATCH 1001/1900] fix: don't lint in macro calls --- clippy_lints/src/mut_mut.rs | 3 +++ tests/ui/mut_mut.fixed | 3 +-- tests/ui/mut_mut.rs | 1 - tests/ui/mut_mut.stderr | 10 +--------- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 0feed460d2ed7..376d7214c267d 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -72,6 +72,9 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { + if !expr.span.eq_ctxt(e.span) { + return; + } let mut applicability = Applicability::MaybeIncorrect; let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); span_lint_hir_and_then( diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index c6b318d765104..137306064b439 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -49,8 +49,7 @@ fn main() { //~| mut_mut } - let mut z = inline!(&mut 3u32mut $(&mut 3u32)); - //~^ mut_mut + let mut z = inline!(&mut $(&mut 3u32)); } fn issue939() { diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index d006578b3d284..92ecfbfc02d12 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -50,7 +50,6 @@ fn main() { } let mut z = inline!(&mut $(&mut 3u32)); - //~^ mut_mut } fn issue939() { diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index f21de359e64f7..5f7c9b4fe23f0 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -13,14 +13,6 @@ error: an expression of form `&mut &mut _` LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:52:25 - | -LL | let mut z = inline!(&mut $(&mut 3u32)); - | ^ help: remove the extra `&mut`: `&mut 3u32` - | - = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) - error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut.rs:35:21 | @@ -57,5 +49,5 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors From 729d92cf0405ef8ee06e76b9e59b4892caa173f0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 4 Aug 2025 00:12:47 +0200 Subject: [PATCH 1002/1900] sugg on ty --- clippy_lints/src/mut_mut.rs | 16 +++++++++++----- tests/ui/mut_mut.stderr | 16 ++++++++-------- tests/ui/mut_mut_unfixable.stderr | 4 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 376d7214c267d..7d7d4fa542c19 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg, intravisit}; @@ -37,15 +38,20 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) { if let hir::TyKind::Ref(_, mty) = ty.kind && mty.mutbl == hir::Mutability::Mut - && let hir::TyKind::Ref(_, mty) = mty.ty.kind - && mty.mutbl == hir::Mutability::Mut + && let hir::TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == hir::Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { - span_lint( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); + span_lint_and_sugg( cx, MUT_MUT, ty.span, - "generally you want to avoid `&mut &mut _` if possible", + "a type of form `&mut &mut _`", + "remove the extra `&mut`", + sugg.to_string(), + applicability, ); } } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 5f7c9b4fe23f0..341ac643417c7 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,8 +1,8 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:15:11 | LL | fn fun(x: &mut &mut u32) { - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` @@ -25,11 +25,11 @@ error: an expression of form `&mut &mut _` LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:40:16 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 @@ -37,17 +37,17 @@ error: an expression of form `&mut &mut _` LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: aborting due to 8 previous errors diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index 129e5b24387e1..da820f766d027 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -1,8 +1,8 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:9:11 | LL | fn fun(x: &mut &mut u32) -> bool { - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` From c6971c22c1450abaa9f4913db60725df85632e21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 12:37:20 +0200 Subject: [PATCH 1003/1900] only lint once on 3+ `&mut`s for some reason this seems to already be the case with exprs, so only do it for tys --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/mut_mut.rs | 20 +++++++++++++++++--- tests/ui/mut_mut.fixed | 7 +++---- tests/ui/mut_mut.rs | 1 - tests/ui/mut_mut.stderr | 8 +------- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9fb..733113fe0d332 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -482,7 +482,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); - store.register_late_pass(|_| Box::new(mut_mut::MutMut)); + store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 7d7d4fa542c19..968d99143c4b1 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg, intravisit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -28,7 +29,12 @@ declare_clippy_lint! { "usage of double-mut refs, e.g., `&mut &mut ...`" } -declare_lint_pass!(MutMut => [MUT_MUT]); +impl_lint_pass!(MutMut => [MUT_MUT]); + +#[derive(Default)] +pub(crate) struct MutMut { + seen_tys: FxHashSet, +} impl<'tcx> LateLintPass<'tcx> for MutMut { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { @@ -42,6 +48,14 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { && mty2.mutbl == hir::Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { + if self.seen_tys.contains(&ty.hir_id) { + // we have 2+ `&mut`s, e.g., `&mut &mut &mut x` + // and we have already flagged on the outermost `&mut &mut (&mut x)`, + // so don't flag the inner `&mut &mut (x)` + return; + } + self.seen_tys.insert(mty.ty.hir_id); + let mut applicability = Applicability::MaybeIncorrect; let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); span_lint_and_sugg( diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index 137306064b439..d89129e70b004 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -12,7 +12,7 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) { +fn fun(x: &mut u32) { //~^ mut_mut } @@ -37,16 +37,15 @@ fn main() { } { - let y: &mut &mut u32 = &mut 2; + let y: &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } { - let y: &mut &mut &mut u32 = &mut &mut 2; + let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index 92ecfbfc02d12..bbec48011a17e 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -46,7 +46,6 @@ fn main() { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 341ac643417c7..b30a64d8fe74c 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -43,11 +43,5 @@ error: a type of form `&mut &mut _` LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` -error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:21 - | -LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` - -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors From 4053c446541940e878b7a9b4a92cb2c4696b3340 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 13:31:04 +0200 Subject: [PATCH 1004/1900] misc: import more from `hir` --- clippy_lints/src/mut_mut.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 968d99143c4b1..574c0c8f8de0b 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg, HirId, intravisit}; +use rustc_hir::{self as hir, AmbigArg, BorrowKind, Expr, ExprKind, HirId, Mutability, TyKind, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) { - if let hir::TyKind::Ref(_, mty) = ty.kind - && mty.mutbl == hir::Mutability::Mut - && let hir::TyKind::Ref(_, mty2) = mty.ty.kind - && mty2.mutbl == hir::Mutability::Mut + if let TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == Mutability::Mut + && let TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { if self.seen_tys.contains(&ty.hir_id) { @@ -76,7 +76,7 @@ pub struct MutVisitor<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(self.cx.sess().source_map()) { return; } @@ -90,8 +90,8 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { + } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = e.kind { if !expr.span.eq_ctxt(e.span) { return; } @@ -107,7 +107,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); }, ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { let mut applicability = Applicability::MaybeIncorrect; From 9a1b2bcc9f82f14e8a5215a2a7b9229c7f1df390 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 13:49:43 +0200 Subject: [PATCH 1005/1900] peel _all_ extra `&mut`s see https://rust-lang.zulipchat.com/#narrow/channel/257328-clippy/topic/problem.20when.20suggestion.20only.20removes.20one.20.22level.22.20of.20a.20lint/near/532889973 --- clippy_lints/src/mut_mut.rs | 47 ++++++++++++++++++++++++++++--- tests/ui/mut_mut.fixed | 2 +- tests/ui/mut_mut.stderr | 4 +-- tests/ui/mut_mut_unfixable.stderr | 2 +- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 574c0c8f8de0b..d66daf0c785f8 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -56,14 +56,30 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } self.seen_tys.insert(mty.ty.hir_id); + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut t, mut t2) = (mty.ty, mty2.ty); + let mut many_muts = false; + loop { + if let TyKind::Ref(_, next) = t2.kind + && next.mutbl == Mutability::Mut + { + (t, t2) = (t2, next.ty); + many_muts = true; + } else { + break; + } + } + let mut applicability = Applicability::MaybeIncorrect; - let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); + let sugg = snippet_with_applicability(cx.sess(), t.span, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; span_lint_and_sugg( cx, MUT_MUT, ty.span, "a type of form `&mut &mut _`", - "remove the extra `&mut`", + format!("remove the extra `&mut`{suffix}"), sugg.to_string(), applicability, ); @@ -91,12 +107,30 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = e.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e2) = e.kind { if !expr.span.eq_ctxt(e.span) { return; } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut e, mut e2) = (e, e2); + let mut many_muts = false; + loop { + if !e.span.eq_ctxt(e2.span) { + return; + } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, next) = e2.kind { + (e, e2) = (e2, next); + many_muts = true; + } else { + break; + } + } + let mut applicability = Applicability::MaybeIncorrect; let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; span_lint_hir_and_then( self.cx, MUT_MUT, @@ -104,7 +138,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { expr.span, "an expression of form `&mut &mut _`", |diag| { - diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); + diag.span_suggestion( + expr.span, + format!("remove the extra `&mut`{suffix}"), + sugg, + applicability, + ); }, ); } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index d89129e70b004..f9a7f5dcb5a15 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -43,7 +43,7 @@ fn main() { } { - let y: &mut &mut u32 = &mut &mut 2; + let y: &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index b30a64d8fe74c..85e9c5649b89a 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -35,13 +35,13 @@ error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` error: aborting due to 7 previous errors diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index da820f766d027..e967ed3537256 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -29,7 +29,7 @@ error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:30:17 | LL | let y = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: aborting due to 5 previous errors From 554680683f69f142e97d502b941c0ce8797ad0c6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:06:50 +0200 Subject: [PATCH 1006/1900] remember all the nested types while we loop see https://rust-lang.zulipchat.com/#narrow/channel/25732 --- clippy_lints/src/mut_mut.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d66daf0c785f8..75b8aa9c90cc1 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -54,13 +54,15 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { // so don't flag the inner `&mut &mut (x)` return; } - self.seen_tys.insert(mty.ty.hir_id); // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off // all extra ones at once let (mut t, mut t2) = (mty.ty, mty2.ty); let mut many_muts = false; loop { + // this should allow us to remember all the nested types, so that the `contains` + // above fails faster + self.seen_tys.insert(t.hir_id); if let TyKind::Ref(_, next) = t2.kind && next.mutbl == Mutability::Mut { From 75b68606bc11529c2dd0e0f12dd317b74d40409c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:12:41 +0200 Subject: [PATCH 1007/1900] add an unfixable case as recommended in https://rust-lang.zulipchat.com/#narrow/channel/257328-clippy/topic/problem.20when.20suggestion.20only.20removes.20one.20.22level.22.20of.20a.20lint/near/532894687 --- tests/ui/mut_mut_unfixable.rs | 8 ++++++++ tests/ui/mut_mut_unfixable.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs index db0552a86ec9b..271cb7b968898 100644 --- a/tests/ui/mut_mut_unfixable.rs +++ b/tests/ui/mut_mut_unfixable.rs @@ -31,4 +31,12 @@ fn main() { //~^ mut_mut ***y + **x; } + + if fun(x) { + // The lint will remove the extra `&mut`, but the result will still be a `&mut` of an expr + // of type `&mut _` (x), so the lint will fire again. That's because we've decided that + // doing both fixes in one run is not worth it, given how improbable code like this is. + let y = &mut &mut x; + //~^ mut_mut + } } diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index e967ed3537256..cf66eb2ed1eca 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -31,5 +31,11 @@ error: an expression of form `&mut &mut _` LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` -error: aborting due to 5 previous errors +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:39:17 + | +LL | let y = &mut &mut x; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` + +error: aborting due to 6 previous errors From dc534c49b3ce818ade236527149a94a51ea91a34 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:14:41 +0200 Subject: [PATCH 1008/1900] doc: add more examples, and a "Use instead" section, don't be condescending --- clippy_lints/src/mut_mut.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 75b8aa9c90cc1..588afd85afb02 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -14,19 +14,30 @@ declare_clippy_lint! { /// Checks for instances of `mut mut` references. /// /// ### Why is this bad? - /// Multiple `mut`s don't add anything meaningful to the - /// source. This is either a copy'n'paste error, or it shows a fundamental - /// misunderstanding of references. + /// This is usually just a typo or a misunderstanding of how references work. /// /// ### Example /// ```no_run - /// # let mut y = 1; - /// let x = &mut &mut y; + /// let x = &mut &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut x; + /// + /// fn foo(x: &mut &mut u32) {} + /// ``` + /// Use instead + /// ```no_run + /// let x = &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut *x; // reborrow + /// + /// fn foo(x: &mut u32) {} /// ``` #[clippy::version = "pre 1.29.0"] pub MUT_MUT, pedantic, - "usage of double-mut refs, e.g., `&mut &mut ...`" + "usage of double mut-refs, e.g., `&mut &mut ...`" } impl_lint_pass!(MutMut => [MUT_MUT]); From 4c128d9ee0b2aa1131c90267be8368e23708c97f Mon Sep 17 00:00:00 2001 From: Pietro Nuti Date: Mon, 21 Apr 2025 14:07:29 +0200 Subject: [PATCH 1009/1900] Add lint unnecessary_option_map_or_else --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 27 +++++ .../methods/unnecessary_option_map_or_else.rs | 73 ++++++++++++++ tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 58 +++++------ tests/ui/or_fun_call.fixed | 1 + tests/ui/or_fun_call.rs | 1 + tests/ui/or_fun_call.stderr | 98 +++++++++---------- tests/ui/unnecessary_option_map_or_else.fixed | 47 +++++++++ tests/ui/unnecessary_option_map_or_else.rs | 54 ++++++++++ .../ui/unnecessary_option_map_or_else.stderr | 35 +++++++ 13 files changed, 322 insertions(+), 80 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_option_map_or_else.rs create mode 100644 tests/ui/unnecessary_option_map_or_else.fixed create mode 100644 tests/ui/unnecessary_option_map_or_else.rs create mode 100644 tests/ui/unnecessary_option_map_or_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..ebc2beb8d266c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6738,6 +6738,7 @@ Released 2018-09-13 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_option_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_option_map_or_else [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7645ac2dd3f7b..441464378c070 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -488,6 +488,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, crate::methods::UNNECESSARY_MAP_OR_INFO, crate::methods::UNNECESSARY_MIN_OR_MAX_INFO, + crate::methods::UNNECESSARY_OPTION_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a1cdab9cc491b..2e35d1a2f3eae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -130,6 +130,7 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_map_or; mod unnecessary_min_or_max; +mod unnecessary_option_map_or_else; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; @@ -4637,6 +4638,30 @@ declare_clippy_lint! { "detects redundant calls to `Iterator::cloned`" } +declare_clippy_lint! { + /// Checks for usage of `.map_or_else()` "map closure" for `Option` type. + /// + /// ### Why is this bad? + /// This can be written more concisely by using `unwrap_or_else()`. + /// + /// ### Example + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.map_or_else(|| 2 * k, |n| n); + /// ``` + /// Use instead: + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.unwrap_or_else(|| 2 * k); + /// ``` + #[clippy::version = "1.88.0"] + pub UNNECESSARY_OPTION_MAP_OR_ELSE, + suspicious, + "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4818,6 +4843,7 @@ impl_lint_pass!(Methods => [ SWAP_WITH_TEMPORARY, IP_CONSTANT, REDUNDANT_ITER_CLONED, + UNNECESSARY_OPTION_MAP_OR_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -5354,6 +5380,7 @@ impl Methods { }, (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); + unnecessary_option_map_or_else::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, (sym::next, []) => { diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..ec709e1ab5cdc --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -0,0 +1,73 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::UNNECESSARY_OPTION_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { + let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; + let self_snippet = snippet(cx, recv.span, ".."); + let err_snippet = snippet(cx, def_arg.span, ".."); + span_lint_and_sugg( + cx, + UNNECESSARY_OPTION_MAP_OR_ELSE, + expr.span, + msg, + "consider using `unwrap_or_else`", + format!("{self_snippet}.unwrap_or_else({err_snippet})"), + Applicability::MachineApplicable, + ); +} + +fn handle_qpath( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + expected_hir_id: HirId, + qpath: QPath<'_>, +) { + if let QPath::Resolved(_, path) = qpath + && let rustc_hir::def::Res::Local(hir_id) = path.res + && expected_hir_id == hir_id + { + emit_lint(cx, expr, recv, def_arg); + } +} + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) + && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind + && let body = cx.tcx.hir_body(body) + && let Some(first_param) = body.params.first() + { + let body_expr = peel_blocks(body.value); + + match body_expr.kind { + ExprKind::Path(qpath) => { + handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); + }, + // If this is a block (that wasn't peeled off), then it means there are statements. + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) + && let ExprKind::Path(qpath) = block_expr.kind + { + handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); + } + }, + _ => {}, + } + } +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 6ce067f5c2462..a2ecea773efe5 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 096d3aabf28db..3adbc785237f6 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 21a80ae038d8c..f5578f63c946b 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:13:5 + --> tests/ui/option_if_let_else.rs:14:5 | LL | / if let Some(x) = string { LL | | @@ -13,19 +13,19 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:32:13 + --> tests/ui/option_if_let_else.rs:33:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:34:13 + --> tests/ui/option_if_let_else.rs:35:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -47,13 +47,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:43:13 + --> tests/ui/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:45:13 + --> tests/ui/option_if_let_else.rs:46:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -75,7 +75,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:13 + --> tests/ui/option_if_let_else.rs:53:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -97,7 +97,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:62:5 + --> tests/ui/option_if_let_else.rs:63:5 | LL | / if let Some(x) = arg { LL | | @@ -118,7 +118,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:76:13 + --> tests/ui/option_if_let_else.rs:77:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:86:13 + --> tests/ui/option_if_let_else.rs:87:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:120:13 + --> tests/ui/option_if_let_else.rs:121:13 | LL | / if let Some(idx) = s.find('.') { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:132:5 + --> tests/ui/option_if_let_else.rs:133:5 | LL | / if let Ok(binding) = variable { LL | | @@ -189,7 +189,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:5 + --> tests/ui/option_if_let_else.rs:158:5 | LL | / match r { LL | | @@ -199,7 +199,7 @@ LL | | } | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:166:5 + --> tests/ui/option_if_let_else.rs:167:5 | LL | / if let Ok(s) = r { s.to_owned() } LL | | @@ -207,13 +207,13 @@ LL | | else { Vec::new() } | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:173:13 + --> tests/ui/option_if_let_else.rs:174:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -235,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:213:13 + --> tests/ui/option_if_let_else.rs:214:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:218:13 + --> tests/ui/option_if_let_else.rs:219:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -263,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:13 + --> tests/ui/option_if_let_else.rs:259:13 | LL | let _ = match s { | _____________^ @@ -274,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:263:13 + --> tests/ui/option_if_let_else.rs:264:13 | LL | let _ = match Some(10) { | _____________^ @@ -285,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:270:13 + --> tests/ui/option_if_let_else.rs:271:13 | LL | let _ = match res { | _____________^ @@ -296,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:275:13 + --> tests/ui/option_if_let_else.rs:276:13 | LL | let _ = match res { | _____________^ @@ -307,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:280:13 + --> tests/ui/option_if_let_else.rs:281:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:298:17 + --> tests/ui/option_if_let_else.rs:299:17 | LL | let _ = match initial { | _________________^ @@ -324,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:306:17 + --> tests/ui/option_if_let_else.rs:307:17 | LL | let _ = match initial { | _________________^ @@ -335,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:330:24 + --> tests/ui/option_if_let_else.rs:331:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -347,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:337:19 + --> tests/ui/option_if_let_else.rs:338:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:388:22 + --> tests/ui/option_if_let_else.rs:389:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:394:13 + --> tests/ui/option_if_let_else.rs:395:13 | LL | let _ = match res { | _____________^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 386351aa39f51..314da0804a5fa 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index e27f9aa65c37a..2a19614026ec5 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 6bce06ab20eb6..3d55f2cd1f9fc 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:52:22 + --> tests/ui/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:56:14 + --> tests/ui/or_fun_call.rs:57:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:60:21 + --> tests/ui/or_fun_call.rs:61:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:64:14 + --> tests/ui/or_fun_call.rs:65:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:68:19 + --> tests/ui/or_fun_call.rs:69:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:72:24 + --> tests/ui/or_fun_call.rs:73:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:76:23 + --> tests/ui/or_fun_call.rs:77:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:96:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:100:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:104:14 + --> tests/ui/or_fun_call.rs:105:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:108:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:112:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:116:23 + --> tests/ui/or_fun_call.rs:117:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:120:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:124:25 + --> tests/ui/or_fun_call.rs:125:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:128:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:133:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:141:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:166:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:209:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:217:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:220:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:296:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:298:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:301:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:332:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:336:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:340:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:344:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:348:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:352:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:356:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:398:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:403:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:408:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,79 +235,79 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:414:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:419:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:426:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:441:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:443:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:446:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:457:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:467:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:477:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:483:17 + --> tests/ui/or_fun_call.rs:484:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:485:17 + --> tests/ui/or_fun_call.rs:486:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:491:17 + --> tests/ui/or_fun_call.rs:492:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:493:17 + --> tests/ui/or_fun_call.rs:494:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` diff --git a/tests/ui/unnecessary_option_map_or_else.fixed b/tests/ui/unnecessary_option_map_or_else.fixed new file mode 100644 index 0000000000000..5f0039f9fea5f --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.unwrap_or_else(|| ()); + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); +} diff --git a/tests/ui/unnecessary_option_map_or_else.rs b/tests/ui/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..a748b33018148 --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.rs @@ -0,0 +1,54 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.map_or_else(|| (), |x| x); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.map_or_else(|| (), |x: ()| x); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |x| x); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.map_or_else( + //~^ ERROR: unused "map closure" when calling + || (), + |x| { + let tmp = x; + tmp + }, + ); + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); +} diff --git a/tests/ui/unnecessary_option_map_or_else.stderr b/tests/ui/unnecessary_option_map_or_else.stderr new file mode 100644 index 0000000000000..3fc4cdc73d0f3 --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.stderr @@ -0,0 +1,35 @@ +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:13:5 + | +LL | option.map_or_else(|| (), |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | + = note: `-D clippy::unnecessary-option-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:17:5 + | +LL | option.map_or_else(|| (), |x: ()| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:22:19 + | +LL | let _: &str = option.map_or_else(|| &string, |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:26:5 + | +LL | / option.map_or_else( +LL | | +LL | | || (), +LL | | |x| { +... | +LL | | }, +LL | | ); + | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: aborting due to 4 previous errors + From e410d6f09e42555b2d537c32d99b10cab79e59ac Mon Sep 17 00:00:00 2001 From: Pietro Nuti Date: Sun, 13 Jul 2025 13:39:17 +0200 Subject: [PATCH 1010/1900] Add test cases for closure binding and function identity --- .../methods/unnecessary_option_map_or_else.rs | 68 +++++++++++++++---- tests/ui/unnecessary_option_map_or_else.fixed | 28 ++++++++ tests/ui/unnecessary_option_map_or_else.rs | 28 ++++++++ .../ui/unnecessary_option_map_or_else.stderr | 22 ++++-- 4 files changed, 126 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs index ec709e1ab5cdc..35b72fe8fc194 100644 --- a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Body, BodyId, Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -12,8 +13,9 @@ use super::utils::get_last_chain_binding_hir_id; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; - let self_snippet = snippet(cx, recv.span, ".."); - let err_snippet = snippet(cx, def_arg.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let self_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); span_lint_and_sugg( cx, UNNECESSARY_OPTION_MAP_OR_ELSE, @@ -34,23 +36,21 @@ fn handle_qpath( qpath: QPath<'_>, ) { if let QPath::Resolved(_, path) = qpath - && let rustc_hir::def::Res::Local(hir_id) = path.res + && let Res::Local(hir_id) = path.res && expected_hir_id == hir_id { emit_lint(cx, expr, recv, def_arg); } } -/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { - // lint if the caller of `map_or_else()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) - && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind - && let body = cx.tcx.hir_body(body) - && let Some(first_param) = body.params.first() - { - let body_expr = peel_blocks(body.value); +fn handle_closure(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body_id: BodyId) { + let body = cx.tcx.hir_body(body_id); + handle_fn_body(cx, expr, recv, def_arg, body); +} +fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body: &Body<'_>) { + if let Some(first_param) = body.params.first() { + let body_expr = peel_blocks(body.value); match body_expr.kind { ExprKind::Path(qpath) => { handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); @@ -71,3 +71,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_ } } } + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + return; + } + match map_arg.kind { + // If the second argument is a closure, we can check its body. + ExprKind::Closure(&Closure { body, .. }) => { + handle_closure(cx, expr, recv, def_arg, body); + }, + ExprKind::Path(qpath) => { + let res = cx.qpath_res(&qpath, map_arg.hir_id); + match res { + // Case 1: Local variable (could be a closure) + Res::Local(hir_id) => { + if let Some(init_expr) = find_binding_init(cx, hir_id) { + let origin = expr_or_init(cx, init_expr); + if let ExprKind::Closure(&Closure { body, .. }) = origin.kind { + handle_closure(cx, expr, recv, def_arg, body); + } + } + }, + // Case 2: Function definition + Res::Def(DefKind::Fn, def_id) => { + if let Some(local_def_id) = def_id.as_local() + && let Some(body) = cx.tcx.hir_maybe_body_owned_by(local_def_id) + { + handle_fn_body(cx, expr, recv, def_arg, body); + } + }, + _ => (), + } + }, + _ => (), + } +} diff --git a/tests/ui/unnecessary_option_map_or_else.fixed b/tests/ui/unnecessary_option_map_or_else.fixed index 5f0039f9fea5f..9974ee2d08eb5 100644 --- a/tests/ui/unnecessary_option_map_or_else.fixed +++ b/tests/ui/unnecessary_option_map_or_else.fixed @@ -6,6 +6,14 @@ clippy::unnecessary_literal_unwrap )] +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + fn main() { // Expected errors // Basic scenario @@ -25,6 +33,17 @@ fn main() { let option = Some(()); option.unwrap_or_else(|| ()); + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.unwrap_or_else(|| string); //~ ERROR: unused "map closure" when calling + // Correct usages let option = Some(()); option.map_or_else(|| (), |_| ()); @@ -44,4 +63,13 @@ fn main() { tmp }, ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); } diff --git a/tests/ui/unnecessary_option_map_or_else.rs b/tests/ui/unnecessary_option_map_or_else.rs index a748b33018148..9b53f3fcd521c 100644 --- a/tests/ui/unnecessary_option_map_or_else.rs +++ b/tests/ui/unnecessary_option_map_or_else.rs @@ -6,6 +6,14 @@ clippy::unnecessary_literal_unwrap )] +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + fn main() { // Expected errors // Basic scenario @@ -32,6 +40,17 @@ fn main() { }, ); + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, identity); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.map_or_else(|| string, do_nothing); //~ ERROR: unused "map closure" when calling + // Correct usages let option = Some(()); option.map_or_else(|| (), |_| ()); @@ -51,4 +70,13 @@ fn main() { tmp }, ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); } diff --git a/tests/ui/unnecessary_option_map_or_else.stderr b/tests/ui/unnecessary_option_map_or_else.stderr index 3fc4cdc73d0f3..d90875e4efc7c 100644 --- a/tests/ui/unnecessary_option_map_or_else.stderr +++ b/tests/ui/unnecessary_option_map_or_else.stderr @@ -1,5 +1,5 @@ error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:13:5 + --> tests/ui/unnecessary_option_map_or_else.rs:21:5 | LL | option.map_or_else(|| (), |x| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` @@ -8,19 +8,19 @@ LL | option.map_or_else(|| (), |x| x); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:17:5 + --> tests/ui/unnecessary_option_map_or_else.rs:25:5 | LL | option.map_or_else(|| (), |x: ()| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:22:19 + --> tests/ui/unnecessary_option_map_or_else.rs:30:19 | LL | let _: &str = option.map_or_else(|| &string, |x| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:26:5 + --> tests/ui/unnecessary_option_map_or_else.rs:34:5 | LL | / option.map_or_else( LL | | @@ -31,5 +31,17 @@ LL | | }, LL | | ); | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` -error: aborting due to 4 previous errors +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:46:19 + | +LL | let _: &str = option.map_or_else(|| &string, identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:52:21 + | +LL | let _: String = option.map_or_else(|| string, do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)` + +error: aborting due to 6 previous errors From e52fe65d9a34c7759ff22c210126c22d5a95fc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Sun, 28 Sep 2025 15:19:19 +0200 Subject: [PATCH 1011/1900] Move more code to `RawVec::finish_grow` This move a branch and more code into the cold method `finish_grow`, which means that less code is inlined at each `try_reserve` site. Additionally, this reduces the amount of parameters, so they can all be passed by registers. --- library/alloc/src/raw_vec/mod.rs | 95 ++++++++++++++------------------ 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index bc9692f5b6c2f..236e33e2f450e 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -668,8 +668,7 @@ impl RawVecInner
{ /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment - /// - The sum of `len` and `additional` must be greater than or equal to - /// `self.capacity(elem_layout.size())` + /// - The sum of `len` and `additional` must be greater than the current capacity unsafe fn grow_amortized( &mut self, len: usize, @@ -693,16 +692,12 @@ impl RawVecInner { let cap = cmp::max(self.cap.as_inner() * 2, required_cap); let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); - let new_layout = layout_array(cap, elem_layout)?; - // SAFETY: - // - For the `current_memory` call: Precondition passed to caller - // - For the `finish_grow` call: Precondition passed to caller - // + `current_memory` does the right thing - let ptr = - unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? }; + // - cap >= len + additional + // - other preconditions passed to caller + let ptr = unsafe { self.finish_grow(cap, elem_layout)? }; - // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + // SAFETY: `finish_grow` would have failed if `cap > isize::MAX` unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } @@ -711,8 +706,7 @@ impl RawVecInner { /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment - /// - The sum of `len` and `additional` must be greater than or equal to - /// `self.capacity(elem_layout.size())` + /// - The sum of `len` and `additional` must be greater than the current capacity unsafe fn grow_exact( &mut self, len: usize, @@ -726,21 +720,44 @@ impl RawVecInner { } let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - let new_layout = layout_array(cap, elem_layout)?; - // SAFETY: - // - For the `current_memory` call: Precondition passed to caller - // - For the `finish_grow` call: Precondition passed to caller - // + `current_memory` does the right thing - let ptr = - unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? }; - // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items - unsafe { - self.set_ptr_and_cap(ptr, cap); - } + // SAFETY: preconditions passed to caller + let ptr = unsafe { self.finish_grow(cap, elem_layout)? }; + + // SAFETY: `finish_grow` would have failed if `cap > isize::MAX` + unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be greater than the current capacity + // not marked inline(never) since we want optimizers to be able to observe the specifics of this + // function, see tests/codegen-llvm/vec-reserve-extend.rs. + #[cold] + unsafe fn finish_grow( + &self, + cap: usize, + elem_layout: Layout, + ) -> Result, TryReserveError> { + let new_layout = layout_array(cap, elem_layout)?; + + let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } { + debug_assert_eq!(old_layout.align(), new_layout.align()); + unsafe { + // The allocator checks for alignment equality + hint::assert_unchecked(old_layout.align() == new_layout.align()); + self.alloc.grow(ptr, old_layout, new_layout) + } + } else { + self.alloc.allocate(new_layout) + }; + + memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) + } + /// # Safety /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` @@ -820,38 +837,6 @@ impl RawVecInner { } } -/// # Safety -/// If `current_memory` matches `Some((ptr, old_layout))`: -/// - `ptr` must denote a block of memory *currently allocated* via `alloc` -/// - `old_layout` must *fit* that block of memory -/// - `new_layout` must have the same alignment as `old_layout` -/// - `new_layout.size()` must be greater than or equal to `old_layout.size()` -/// If `current_memory` is `None`, this function is safe. -// not marked inline(never) since we want optimizers to be able to observe the specifics of this -// function, see tests/codegen-llvm/vec-reserve-extend.rs. -#[cold] -unsafe fn finish_grow( - new_layout: Layout, - current_memory: Option<(NonNull, Layout)>, - alloc: &mut A, -) -> Result, TryReserveError> -where - A: Allocator, -{ - let memory = if let Some((ptr, old_layout)) = current_memory { - debug_assert_eq!(old_layout.align(), new_layout.align()); - unsafe { - // The allocator checks for alignment equality - hint::assert_unchecked(old_layout.align() == new_layout.align()); - alloc.grow(ptr, old_layout, new_layout) - } - } else { - alloc.allocate(new_layout) - }; - - memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) -} - // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] #[cold] From 6b4febeeb7be931c9317bf987604bbd94967cd94 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 28 Sep 2025 15:39:57 +0200 Subject: [PATCH 1012/1900] simplify and inline `is_async_fn` --- clippy_lints/src/cognitive_complexity.rs | 4 ++-- clippy_lints/src/implicit_return.rs | 4 ++-- clippy_utils/src/lib.rs | 11 +---------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 7646aa48b7726..c0f30e456d8db 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; +use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { def_id: LocalDefId, ) { if !cx.tcx.has_attr(def_id, sym::test) { - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(b) => b, None => { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 076017a247b4b..6ed478b2708a3 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(e) => e, None => return, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d6586625a1e5e..708491df77079 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -98,7 +98,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, @@ -1854,15 +1854,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// Checks if the given function kind is an async function. -pub fn is_async_fn(kind: FnKind<'_>) -> bool { - match kind { - FnKind::ItemFn(_, _, header) => header.asyncness.is_async(), - FnKind::Method(_, sig) => sig.header.asyncness.is_async(), - FnKind::Closure => false, - } -} - /// Peels away all the compiler generated code surrounding the body of an async closure. pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Closure(&Closure { From 7a7cb05f11a81e261e22f9ae291755b6343f7095 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 27 Sep 2025 13:28:25 +0000 Subject: [PATCH 1013/1900] Do not validate MIR if code does not type-check. --- compiler/rustc_interface/src/passes.rs | 26 ++++++++++++++------------ tests/crashes/129095.rs | 13 ------------- tests/crashes/134174.rs | 17 ----------------- tests/crashes/134654.rs | 15 --------------- tests/crashes/135570.rs | 15 --------------- tests/crashes/136381.rs | 18 ------------------ tests/crashes/137190-1.rs | 10 ---------- tests/crashes/137468.rs | 16 ---------------- 8 files changed, 14 insertions(+), 116 deletions(-) delete mode 100644 tests/crashes/129095.rs delete mode 100644 tests/crashes/134174.rs delete mode 100644 tests/crashes/134654.rs delete mode 100644 tests/crashes/135570.rs delete mode 100644 tests/crashes/136381.rs delete mode 100644 tests/crashes/137190-1.rs delete mode 100644 tests/crashes/137468.rs diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 761a5c8091822..c1bba0b01975e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1122,18 +1122,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); - - // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item - // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs - // in MIR optimizations that may only be reachable through codegen, or other codepaths - // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. - if tcx.sess.opts.unstable_opts.validate_mir { - sess.time("ensuring_final_MIR_is_computable", || { - tcx.par_hir_body_owners(|def_id| { - tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); - }); - }); - } } /// Runs the type-checking, region checking and other miscellaneous analysis @@ -1199,6 +1187,20 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. let _ = tcx.all_diagnostic_items(()); }); + + // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item + // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs + // in MIR optimizations that may only be reachable through codegen, or other codepaths + // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. + // Nevertheless, wait after type checking is finished, as optimizing code that does not + // type-check is very prone to ICEs. + if tcx.sess.opts.unstable_opts.validate_mir { + sess.time("ensuring_final_MIR_is_computable", || { + tcx.par_hir_body_owners(|def_id| { + tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); + }); + }); + } } /// Runs the codegen backend, after which the AST and analysis can diff --git a/tests/crashes/129095.rs b/tests/crashes/129095.rs deleted file mode 100644 index b1bb74708c2d7..0000000000000 --- a/tests/crashes/129095.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: rust-lang/rust#129095 -//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+Inline -Zvalidate-mir - -#![feature(adt_const_params, unsized_const_params)] -#![allow(incomplete_features)] - -pub fn function_with_bytes() -> &'static [u8] { - BYTES -} - -pub fn main() { - assert_eq!(function_with_bytes::(), &[0x41, 0x41, 0x41, 0x41]); -} diff --git a/tests/crashes/134174.rs b/tests/crashes/134174.rs deleted file mode 100644 index 899cdc6faf358..0000000000000 --- a/tests/crashes/134174.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #134175 -//@compile-flags: -Zvalidate-mir -Zinline-mir=yes -use std::vec::IntoIter; - -pub(crate) trait Foo: Iterator::Key> { - type Key; -} - -impl Foo for IntoIter {} - -fn sum_foo>(f: F) -> i32 { - f.fold(0, |a, b| a + b) -} - -fn main() { - let x = sum_foo(vec![11, 10, 1].into_iter()); -} diff --git a/tests/crashes/134654.rs b/tests/crashes/134654.rs deleted file mode 100644 index f2323fe4ecdcd..0000000000000 --- a/tests/crashes/134654.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #134654 -//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+Inline -Zvalidate-mir -//@ only-x86_64 - -#![feature(adt_const_params, unsized_const_params)] -#![allow(incomplete_features)] - -fn function_with_bytes() -> &'static [u8] { - BYTES -} - -fn main() { - function_with_bytes::() == &[]; -} diff --git a/tests/crashes/135570.rs b/tests/crashes/135570.rs deleted file mode 100644 index 7919ceb26d50d..0000000000000 --- a/tests/crashes/135570.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #135570 -//@compile-flags: -Zvalidate-mir -Zmir-enable-passes=+Inline -Copt-level=0 -Zmir-enable-passes=+GVN -//@ only-x86_64 - -#![feature(adt_const_params, unsized_const_params)] -#![allow(incomplete_features)] - -fn function_with_bytes( -) -> &'static [u8] { - BYTES -} - -fn main() { - function_with_bytes::() == &[]; -} diff --git a/tests/crashes/136381.rs b/tests/crashes/136381.rs deleted file mode 100644 index 13ccc14a2c5be..0000000000000 --- a/tests/crashes/136381.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #136381 -//@ compile-flags: -Zvalidate-mir -Zmir-enable-passes=+GVN -#![feature(trait_upcasting)] - -trait A {} -trait B: A { - fn c(&self); -} -impl B for i32 { - fn c(self) { - todo!(); - } -} - -fn main() { - let baz: &dyn B = &1; - let bar: &dyn A = baz; -} diff --git a/tests/crashes/137190-1.rs b/tests/crashes/137190-1.rs deleted file mode 100644 index bdfe883b71207..0000000000000 --- a/tests/crashes/137190-1.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ known-bug: #137190 -//@ compile-flags: -Zmir-opt-level=2 -Zvalidate-mir -trait A { - fn b(&self); -} -trait C: A {} -impl C for () {} -fn main() { - (&() as &dyn C as &dyn A).b(); -} diff --git a/tests/crashes/137468.rs b/tests/crashes/137468.rs deleted file mode 100644 index cceb0502bd21b..0000000000000 --- a/tests/crashes/137468.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #137468 -//@ compile-flags: -Copt-level=0 -Zmir-enable-passes=+GVN -Zvalidate-mir -trait Supertrait {} - -trait Identity { - type Selff; -} - -trait Trait

: Supertrait<()> + Supertrait<

::Selff> {} - -impl

Trait

for () {} - -fn main() { - let x: &dyn Trait<()> = &(); - let x: &dyn Supertrait<()> = x; -} From 7fcbc5ea465a4e280d3b7a84fc3f781e9a120ed0 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 28 Sep 2025 12:08:44 -0400 Subject: [PATCH 1014/1900] Add a leading dash to linker plugin arguments in the gcc codegen --- compiler/rustc_codegen_cranelift/src/lib.rs | 4 ++++ compiler/rustc_codegen_gcc/src/lib.rs | 4 ++++ compiler/rustc_codegen_llvm/src/lib.rs | 13 ++++++++++++- compiler/rustc_codegen_ssa/src/back/link.rs | 6 ++++++ compiler/rustc_codegen_ssa/src/back/linker.rs | 13 +++++++++++-- compiler/rustc_codegen_ssa/src/traits/backend.rs | 11 ++++++++++- .../codegen-backend/auxiliary/the_backend.rs | 4 ++++ 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8e34436fb5e0a..5fd7c4d4f41bb 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -165,6 +165,10 @@ impl CodegenBackend for CraneliftCodegenBackend { "" } + fn name(&self) -> &'static str { + "cranelift" + } + fn init(&self, sess: &Session) { use rustc_session::config::{InstrumentCoverage, Lto}; match sess.lto() { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f76f933cad4a5..ec7eab8489ab8 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -184,6 +184,10 @@ impl CodegenBackend for GccCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } + fn name(&self) -> &'static str { + "gcc" + } + fn init(&self, _sess: &Session) { #[cfg(feature = "master")] { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 13bdb7cb1a274..e96eb304c50cb 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -231,6 +231,10 @@ impl CodegenBackend for LlvmCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } + fn name(&self) -> &'static str { + "llvm" + } + fn init(&self, sess: &Session) { llvm_util::init(sess); // Make sure llvm is inited } @@ -349,7 +353,14 @@ impl CodegenBackend for LlvmCodegenBackend { // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, metadata, outputs); + link_binary( + sess, + &LlvmArchiveBuilderBuilder, + codegen_results, + metadata, + outputs, + self.name(), + ); } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d6c304c1b1475..db2f2dd65b0b5 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -79,6 +79,7 @@ pub fn link_binary( codegen_results: CodegenResults, metadata: EncodedMetadata, outputs: &OutputFilenames, + codegen_backend: &'static str, ) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); @@ -154,6 +155,7 @@ pub fn link_binary( &codegen_results, &metadata, path.as_ref(), + codegen_backend, ); } } @@ -680,6 +682,7 @@ fn link_natively( codegen_results: &CodegenResults, metadata: &EncodedMetadata, tmpdir: &Path, + codegen_backend: &'static str, ) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); @@ -705,6 +708,7 @@ fn link_natively( codegen_results, metadata, self_contained_components, + codegen_backend, ); linker::disable_localization(&mut cmd); @@ -2208,6 +2212,7 @@ fn linker_with_args( codegen_results: &CodegenResults, metadata: &EncodedMetadata, self_contained_components: LinkSelfContainedComponents, + codegen_backend: &'static str, ) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( @@ -2216,6 +2221,7 @@ fn linker_with_args( flavor, self_contained_components.are_any_components_enabled(), &codegen_results.crate_info.target_cpu, + codegen_backend, ); let link_output_kind = link_output_kind(sess, crate_type); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 624ab1b50848d..e644a43f88340 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -52,6 +52,7 @@ pub(crate) fn get_linker<'a>( flavor: LinkerFlavor, self_contained: bool, target_cpu: &'a str, + codegen_backend: &'static str, ) -> Box { let msvc_tool = find_msvc_tools::find_tool(&sess.target.arch, "link.exe"); @@ -154,6 +155,7 @@ pub(crate) fn get_linker<'a>( is_ld: cc == Cc::No, is_gnu: flavor.is_gnu(), uses_lld: flavor.uses_lld(), + codegen_backend, }) as Box, LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box, LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box, @@ -367,6 +369,7 @@ struct GccLinker<'a> { is_ld: bool, is_gnu: bool, uses_lld: bool, + codegen_backend: &'static str, } impl<'a> GccLinker<'a> { @@ -423,9 +426,15 @@ impl<'a> GccLinker<'a> { if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use { self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; + let prefix = if self.codegen_backend == "gcc" { + // The GCC linker plugin requires a leading dash. + "-" + } else { + "" + }; self.link_args(&[ - &format!("-plugin-opt={opt_level}"), - &format!("-plugin-opt=mcpu={}", self.target_cpu), + &format!("-plugin-opt={prefix}{opt_level}"), + &format!("-plugin-opt={prefix}mcpu={}", self.target_cpu), ]); } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 29ec7eb1da3b3..2400160075e2d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -41,6 +41,8 @@ pub trait CodegenBackend { /// Called before `init` so that all other functions are able to emit translatable diagnostics. fn locale_resource(&self) -> &'static str; + fn name(&self) -> &'static str; + fn init(&self, _sess: &Session) {} fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} @@ -96,7 +98,14 @@ pub trait CodegenBackend { metadata: EncodedMetadata, outputs: &OutputFilenames, ) { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, metadata, outputs); + link_binary( + sess, + &ArArchiveBuilderBuilder, + codegen_results, + metadata, + outputs, + self.name(), + ); } } diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 8449479287f0f..48f328f4fad36 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -33,6 +33,10 @@ impl CodegenBackend for TheBackend { "" } + fn name(&self) -> &'static str { + "the-backend" + } + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { Box::new(CodegenResults { modules: vec![], From 599e8db8389111d5c27934c5294879d9caf262e1 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 27 Sep 2025 00:14:32 +0000 Subject: [PATCH 1015/1900] Use MirPatch in simplify_branches. --- compiler/rustc_mir_transform/src/patch.rs | 30 +++++++++++++++---- .../src/simplify_branches.rs | 19 +++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index d831ab50b1ac0..cc8ea76011bf6 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -11,6 +11,8 @@ use tracing::debug; /// once with `apply`. This is useful for MIR transformation passes. pub(crate) struct MirPatch<'tcx> { term_patch_map: FxHashMap>, + /// Set of statements that should be replaced by `Nop`. + nop_statements: Vec, new_blocks: Vec>, new_statements: Vec<(Location, StatementKind<'tcx>)>, new_locals: Vec>, @@ -33,6 +35,7 @@ impl<'tcx> MirPatch<'tcx> { pub(crate) fn new(body: &Body<'tcx>) -> Self { let mut result = MirPatch { term_patch_map: Default::default(), + nop_statements: vec![], new_blocks: vec![], new_statements: vec![], new_locals: vec![], @@ -212,6 +215,15 @@ impl<'tcx> MirPatch<'tcx> { self.term_patch_map.insert(block, new); } + /// Mark given statement to be replaced by a `Nop`. + /// + /// This method only works on statements from the initial body, and cannot be used to remove + /// statements from `add_statement` or `add_assign`. + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn nop_statement(&mut self, loc: Location) { + self.nop_statements.push(loc); + } + /// Queues the insertion of a statement at a given location. The statement /// currently at that location, and all statements that follow, are shifted /// down. If multiple statements are queued for addition at the same @@ -257,11 +269,8 @@ impl<'tcx> MirPatch<'tcx> { bbs.extend(self.new_blocks); body.local_decls.extend(self.new_locals); - // The order in which we patch terminators does not change the result. - #[allow(rustc::potential_query_instability)] - for (src, patch) in self.term_patch_map { - debug!("MirPatch: patching block {:?}", src); - bbs[src].terminator_mut().kind = patch; + for loc in self.nop_statements { + bbs[loc.block].statements[loc.statement_index].make_nop(); } let mut new_statements = self.new_statements; @@ -285,6 +294,17 @@ impl<'tcx> MirPatch<'tcx> { .insert(loc.statement_index, Statement::new(source_info, stmt)); delta += 1; } + + // The order in which we patch terminators does not change the result. + #[allow(rustc::potential_query_instability)] + for (src, patch) in self.term_patch_map { + debug!("MirPatch: patching block {:?}", src); + let bb = &mut bbs[src]; + if let TerminatorKind::Unreachable = patch { + bb.statements.clear(); + } + bb.terminator_mut().kind = patch; + } } fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo { diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index 886f4d6e50900..ed94a058ec6d9 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -2,6 +2,8 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::trace; +use crate::patch::MirPatch; + pub(super) enum SimplifyConstCondition { AfterConstProp, Final, @@ -19,8 +21,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running SimplifyConstCondition on {:?}", body.source); let typing_env = body.typing_env(tcx); - 'blocks: for block in body.basic_blocks_mut() { - for stmt in block.statements.iter_mut() { + let mut patch = MirPatch::new(body); + + 'blocks: for (bb, block) in body.basic_blocks.iter_enumerated() { + for (statement_index, stmt) in block.statements.iter().enumerate() { // Simplify `assume` of a known value: either a NOP or unreachable. if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind && let NonDivergingIntrinsic::Assume(discr) = intrinsic @@ -28,17 +32,16 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { && let Some(constant) = c.const_.try_eval_bool(tcx, typing_env) { if constant { - stmt.make_nop(); + patch.nop_statement(Location { block: bb, statement_index }); } else { - block.statements.clear(); - block.terminator_mut().kind = TerminatorKind::Unreachable; + patch.patch_terminator(bb, TerminatorKind::Unreachable); continue 'blocks; } } } - let terminator = block.terminator_mut(); - terminator.kind = match terminator.kind { + let terminator = block.terminator(); + let terminator = match terminator.kind { TerminatorKind::SwitchInt { discr: Operand::Constant(ref c), ref targets, .. } => { @@ -58,7 +61,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { }, _ => continue, }; + patch.patch_terminator(bb, terminator); } + patch.apply(body); } fn is_required(&self) -> bool { From 632f2cb8a47cc16d7e1e6231ac25ba4711c32be8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 28 Sep 2025 22:32:21 +0200 Subject: [PATCH 1016/1900] Remove one loop in `extract_cfg_from_attrs` --- src/librustdoc/clean/types.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c2cf39c4be06e..d4f0a196eda8d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1085,7 +1085,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator let mut changed_auto_active_status = None; // First we get all `doc(auto_cfg)` attributes. - for attr in attrs.clone() { + for attr in attrs { if let Some(ident) = attr.ident() && ident.name == sym::doc && let Some(attrs) = attr.meta_item_list() @@ -1146,13 +1146,9 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } } } - } - } - - // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because - // `doc(cfg())` overrides `cfg()`). - for attr in attrs { - if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { + // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because + // `doc(cfg())` overrides `cfg()`). + } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { // treat #[target_feature(enable = "feat")] attributes as if they were // #[doc(cfg(target_feature = "feat"))] attributes as well for (feature, _) in features { From c3e0b29e79f347edf7e0ae559bfec2d3cdf353d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 Aug 2025 19:35:22 +0000 Subject: [PATCH 1017/1900] Point at fn bound that introduced lifetime obligation ``` error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:19:20 | LL | fn simple<'a>(x: &'a i32) { | -- lifetime `'a` defined here ... LL | let c = async move || { println!("{}", *x); }; | - binding `c` declared here LL | outlives::<'a>(c()); | ---------------^--- | | | | | borrowed value does not live long enough | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | note: requirement that `c` is borrowed for `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} | ^^ ``` When encountering a `ConstraintCategory::Predicate` in a funtion call, point at the `Span` for that `Predicate` to explain where the lifetime obligation originates from. --- .../rustc_borrowck/src/borrowck_errors.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 9 +++ ...thout-precise-captures-we-are-powerless.rs | 14 +++++ ...t-precise-captures-we-are-powerless.stderr | 57 ++++++++++++++++++- ...ation-not-general-enough-ice-133252.stderr | 6 ++ .../precise-capturing/migration-note.rs | 2 + .../precise-capturing/migration-note.stderr | 54 +++++++++++------- ...er-to-static-comparing-against-free.stderr | 6 ++ .../propagate-multiple-requirements.stderr | 6 ++ .../nll/local-outlives-static-via-hrtb.stderr | 10 ++++ ...insensitive-scopes-issue-117146.nll.stderr | 5 ++ ...sitive-scopes-issue-117146.polonius.stderr | 5 ++ .../regions-infer-proc-static-upvar.stderr | 6 ++ .../regions-pattern-typing-issue-19552.stderr | 6 ++ tests/ui/static/static-lifetime-bound.stderr | 6 ++ 15 files changed, 170 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index c9be5575da5ce..bc985cbe13353 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -426,7 +426,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { } pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'infcx> { - struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path,) + struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path) } pub(crate) fn cannot_return_reference_to_local( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7e20a5133e07f..3b268f538cf2d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3031,6 +3031,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut err = self.path_does_not_live_long_enough(borrow_span, &name); + if let BorrowExplanation::MustBeValidFor { ref path, region_name, .. } = explanation { + for constraint in path { + if let ConstraintCategory::Predicate(pred) = constraint.category + && !pred.is_dummy() + { + err.span_note(pred, format!("requirement that {name} is borrowed for `{region_name}` introduced here")); + } + } + } if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { let region_name = annotation.emit(self, &mut err); diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs index 19a31d1889b84..f97ec779b32cf 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -44,4 +44,18 @@ fn through_field_and_ref_move<'a>(x: &S<'a>) { outlives::<'a>(call_once(c)); //~ ERROR explicit lifetime required in the type of `x` } +struct T; +impl T { + fn outlives<'a>(&'a self, _: impl Sized + 'a) {} +} +fn through_method<'a>(x: &'a i32) { + let c = async || { println!("{}", *x); }; //~ ERROR `x` does not live long enough + T.outlives::<'a>(c()); + T.outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x); }; + T.outlives::<'a>(c()); //~ ERROR `c` does not live long enough + T.outlives::<'a>(call_once(c)); +} + fn main() {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index b7259074bf64b..e99ff763b9a2d 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -28,6 +28,12 @@ LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed + | +note: requirement that `c` is borrowed for `'a` introduced here + --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 + | +LL | fn outlives<'a>(_: impl Sized + 'a) {} + | ^^ error[E0597]: `x` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:26:13 @@ -73,6 +79,12 @@ LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed + | +note: requirement that `c` is borrowed for `'a` introduced here + --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 + | +LL | fn outlives<'a>(_: impl Sized + 'a) {} + | ^^ error[E0505]: cannot move out of `c` because it is borrowed --> $DIR/without-precise-captures-we-are-powerless.rs:32:30 @@ -129,6 +141,12 @@ LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed + | +note: requirement that `c` is borrowed for `'a` introduced here + --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 + | +LL | fn outlives<'a>(_: impl Sized + 'a) {} + | ^^ error[E0621]: explicit lifetime required in the type of `x` --> $DIR/without-precise-captures-we-are-powerless.rs:44:5 @@ -141,7 +159,44 @@ help: add explicit lifetime `'a` to the type of `x` LL | fn through_field_and_ref_move<'a>(x: &'a S<'a>) { | ++ -error: aborting due to 10 previous errors +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:52:13 + | +LL | fn through_method<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | T.outlives::<'a>(c()); +LL | T.outlives::<'a>(call_once(c)); + | ------------------------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:57:22 + | +LL | fn through_method<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x); }; + | - binding `c` declared here +LL | T.outlives::<'a>(c()); + | -----------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | T.outlives::<'a>(call_once(c)); +LL | } + | - `c` dropped here while still borrowed + | +note: requirement that `c` is borrowed for `'a` introduced here + --> $DIR/without-precise-captures-we-are-powerless.rs:49:47 + | +LL | fn outlives<'a>(&'a self, _: impl Sized + 'a) {} + | ^^ + +error: aborting due to 12 previous errors Some errors have detailed explanations: E0505, E0597, E0621. For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index 5389226f7a7a4..393f7b1a61dfa 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -22,6 +22,12 @@ LL | force_send(async_load(¬_static)); ... LL | } | - `not_static` dropped here while still borrowed + | +note: requirement that `not_static` is borrowed for `'1` introduced here + --> $DIR/implementation-not-general-enough-ice-133252.rs:16:18 + | +LL | fn force_send(_: T) {} + | ^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs index 7587e89409aaa..25b594e9b1ec7 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.rs +++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs @@ -32,6 +32,7 @@ fn needs_static() { //~| NOTE borrowed value does not live long enoug fn needs_static(_: impl Sized + 'static) {} + //~^ NOTE requirement that `x` is borrowed for `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } @@ -79,6 +80,7 @@ fn needs_static_mut() { //~| NOTE borrowed value does not live long enough fn needs_static(_: impl Sized + 'static) {} + //~^ NOTE requirement that `x` is borrowed for `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index aa0f640009158..e42a6f15c7e58 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -1,5 +1,5 @@ error[E0597]: `x` does not live long enough - --> $DIR/migration-note.rs:182:17 + --> $DIR/migration-note.rs:184:17 | LL | let x = vec![0]; | - binding `x` declared here @@ -50,6 +50,11 @@ LL | LL | } | - `x` dropped here while still borrowed | +note: requirement that `x` is borrowed for `'static` introduced here + --> $DIR/migration-note.rs:34:37 + | +LL | fn needs_static(_: impl Sized + 'static) {} + | ^^^^^^^ note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules --> $DIR/migration-note.rs:29:13 | @@ -61,7 +66,7 @@ LL | fn display_len(x: &Vec) -> impl Display + use { | ++++++++ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/migration-note.rs:48:8 + --> $DIR/migration-note.rs:49:8 | LL | let x = vec![1]; | - binding `x` declared here @@ -76,7 +81,7 @@ LL | } | - borrow might be used here, when `a` is dropped and runs the destructor for type `impl std::fmt::Display` | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:43:13 + --> $DIR/migration-note.rs:44:13 | LL | let a = display_len(&x); | ^^^^^^^^^^^^^^^ @@ -90,7 +95,7 @@ LL | let a = display_len(&x.clone()); | ++++++++ error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/migration-note.rs:66:5 + --> $DIR/migration-note.rs:67:5 | LL | let a = display_len_mut(&mut x); | ------ first mutable borrow occurs here @@ -102,7 +107,7 @@ LL | println!("{a}"); | - first borrow later used here | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:63:13 + --> $DIR/migration-note.rs:64:13 | LL | let a = display_len_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +117,7 @@ LL | fn display_len_mut(x: &mut Vec) -> impl Display + use { | ++++++++ error[E0597]: `x` does not live long enough - --> $DIR/migration-note.rs:76:29 + --> $DIR/migration-note.rs:77:29 | LL | let mut x = vec![1]; | ----- binding `x` declared here @@ -126,8 +131,13 @@ LL | LL | } | - `x` dropped here while still borrowed | +note: requirement that `x` is borrowed for `'static` introduced here + --> $DIR/migration-note.rs:82:37 + | +LL | fn needs_static(_: impl Sized + 'static) {} + | ^^^^^^^ note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:76:13 + --> $DIR/migration-note.rs:77:13 | LL | let a = display_len_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,7 +147,7 @@ LL | fn display_len_mut(x: &mut Vec) -> impl Display + use { | ++++++++ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/migration-note.rs:95:8 + --> $DIR/migration-note.rs:97:8 | LL | let mut x = vec![1]; | ----- binding `x` declared here @@ -152,7 +162,7 @@ LL | } | - borrow might be used here, when `a` is dropped and runs the destructor for type `impl std::fmt::Display` | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:90:13 + --> $DIR/migration-note.rs:92:13 | LL | let a = display_len_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -166,7 +176,7 @@ LL | let a = display_len_mut(&mut x.clone()); | ++++++++ error[E0506]: cannot assign to `s.f` because it is borrowed - --> $DIR/migration-note.rs:115:5 + --> $DIR/migration-note.rs:117:5 | LL | let a = display_field(&s.f); | ---- `s.f` is borrowed here @@ -178,7 +188,7 @@ LL | println!("{a}"); | - borrow later used here | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:112:13 + --> $DIR/migration-note.rs:114:13 | LL | let a = display_field(&s.f); | ^^^^^^^^^^^^^^^^^^^ @@ -188,7 +198,7 @@ LL | fn display_field(t: &T) -> impl Display + use { | ++++++++ error[E0506]: cannot assign to `s.f` because it is borrowed - --> $DIR/migration-note.rs:131:5 + --> $DIR/migration-note.rs:133:5 | LL | let a = display_field(&mut s.f); | -------- `s.f` is borrowed here @@ -200,7 +210,7 @@ LL | println!("{a}"); | - borrow later used here | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:128:13 + --> $DIR/migration-note.rs:130:13 | LL | let a = display_field(&mut s.f); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +220,7 @@ LL | fn display_field(t: &T) -> impl Display + use { | ++++++++ error[E0503]: cannot use `s.f` because it was mutably borrowed - --> $DIR/migration-note.rs:143:5 + --> $DIR/migration-note.rs:145:5 | LL | let a = display_field(&mut s.f); | -------- `s.f` is borrowed here @@ -222,7 +232,7 @@ LL | println!("{a}"); | - borrow later used here | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:140:13 + --> $DIR/migration-note.rs:142:13 | LL | let a = display_field(&mut s.f); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,7 +242,7 @@ LL | fn display_field(t: &T) -> impl Display + use { | ++++++++ error[E0597]: `z.f` does not live long enough - --> $DIR/migration-note.rs:159:25 + --> $DIR/migration-note.rs:161:25 | LL | let z = Z { f: vec![1] }; | - binding `z` declared here @@ -248,7 +258,7 @@ LL | } | = note: values in a scope are dropped in the opposite order they are defined note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:159:13 + --> $DIR/migration-note.rs:161:13 | LL | x = display_len(&z.f); | ^^^^^^^^^^^^^^^^^ @@ -258,7 +268,7 @@ LL | fn display_len(x: &Vec) -> impl Display + use { | ++++++++ error[E0716]: temporary value dropped while borrowed - --> $DIR/migration-note.rs:170:40 + --> $DIR/migration-note.rs:172:40 | LL | let x = { let x = display_len(&mut vec![0]); x }; | ^^^^^^^ - - borrow later used here @@ -268,7 +278,7 @@ LL | let x = { let x = display_len(&mut vec![0]); x }; | = note: consider using a `let` binding to create a longer lived value note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:170:23 + --> $DIR/migration-note.rs:172:23 | LL | let x = { let x = display_len(&mut vec![0]); x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +289,7 @@ LL | fn display_len(x: &Vec) -> impl Display + use { | ++++++++ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/migration-note.rs:198:10 + --> $DIR/migration-note.rs:200:10 | LL | let x = String::new(); | - binding `x` declared here @@ -294,12 +304,12 @@ LL | } | - borrow might be used here, when `y` is dropped and runs the destructor for type `impl Sized` | note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules - --> $DIR/migration-note.rs:195:13 + --> $DIR/migration-note.rs:197:13 | LL | let y = capture_apit(&x); | ^^^^^^^^^^^^^^^^ note: you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable - --> $DIR/migration-note.rs:189:21 + --> $DIR/migration-note.rs:191:21 | LL | fn capture_apit(x: &impl Sized) -> impl Sized {} | ^^^^^^^^^^ diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index e13653f342342..136da57daec1b 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -69,6 +69,12 @@ LL | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> LL | }) LL | } | - `a` dropped here while still borrowed + | +note: requirement that `a` is borrowed for `'static` introduced here + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:13:8 + | +LL | F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr index 15f48d88c379b..8e18f546ebd4d 100644 --- a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr +++ b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr @@ -13,6 +13,12 @@ LL | z = &local_arr; ... LL | } | - `local_arr` dropped here while still borrowed + | +note: requirement that `local_arr` is borrowed for `'static` introduced here + --> $DIR/propagate-multiple-requirements.rs:4:21 + | +LL | fn once U>(f: F, s: S, t: T) -> U { + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/nll/local-outlives-static-via-hrtb.stderr b/tests/ui/nll/local-outlives-static-via-hrtb.stderr index a98f11ce51363..cc01a3cfc0b90 100644 --- a/tests/ui/nll/local-outlives-static-via-hrtb.stderr +++ b/tests/ui/nll/local-outlives-static-via-hrtb.stderr @@ -12,6 +12,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); LL | } | - `local` dropped here while still borrowed | +note: requirement that `local` is borrowed for `'static` introduced here + --> $DIR/local-outlives-static-via-hrtb.rs:15:53 + | +LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} + | ^^^^^^^^^^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/local-outlives-static-via-hrtb.rs:15:42 | @@ -32,6 +37,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); LL | } | - `local` dropped here while still borrowed | +note: requirement that `local` is borrowed for `'static` introduced here + --> $DIR/local-outlives-static-via-hrtb.rs:19:30 + | +LL | for<'a> &'a T: Reference, + | ^^^^^^^^^^^^^^^^^^^^^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/local-outlives-static-via-hrtb.rs:19:5 | diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr index 6e47b8e59f5c6..56ad78ba4b424 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr @@ -13,6 +13,11 @@ LL | let b = |_| &a; LL | } | - `a` dropped here while still borrowed | +note: requirement that `a` is borrowed for `'static` introduced here + --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 + | +LL | fn bad &()>(_: F) {} + | ^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr index 6e47b8e59f5c6..56ad78ba4b424 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr @@ -13,6 +13,11 @@ LL | let b = |_| &a; LL | } | - `a` dropped here while still borrowed | +note: requirement that `a` is borrowed for `'static` introduced here + --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 + | +LL | fn bad &()>(_: F) {} + | ^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | diff --git a/tests/ui/regions/regions-infer-proc-static-upvar.stderr b/tests/ui/regions/regions-infer-proc-static-upvar.stderr index 919fcffdc531e..1a76b5f00f943 100644 --- a/tests/ui/regions/regions-infer-proc-static-upvar.stderr +++ b/tests/ui/regions/regions-infer-proc-static-upvar.stderr @@ -11,6 +11,12 @@ LL | | }); | |______- argument requires that `x` is borrowed for `'static` LL | } | - `x` dropped here while still borrowed + | +note: requirement that `x` is borrowed for `'static` introduced here + --> $DIR/regions-infer-proc-static-upvar.rs:4:19 + | +LL | fn foo(_p: F) { } + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr index 1d3d5e831c39c..2c8ce20f09e46 100644 --- a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr +++ b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr @@ -10,6 +10,12 @@ LL | [ word ] => { assert_static(word); } LL | } LL | } | - `line` dropped here while still borrowed + | +note: requirement that `line` is borrowed for `'static` introduced here + --> $DIR/regions-pattern-typing-issue-19552.rs:1:21 + | +LL | fn assert_static(_t: T) {} + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/static/static-lifetime-bound.stderr b/tests/ui/static/static-lifetime-bound.stderr index 8b0d3a0bf4cf4..e530e4669d7f6 100644 --- a/tests/ui/static/static-lifetime-bound.stderr +++ b/tests/ui/static/static-lifetime-bound.stderr @@ -10,6 +10,12 @@ LL | f(&x); | argument requires that `x` is borrowed for `'static` LL | } | - `x` dropped here while still borrowed + | +note: requirement that `x` is borrowed for `'static` introduced here + --> $DIR/static-lifetime-bound.rs:1:10 + | +LL | fn f<'a: 'static>(_: &'a i32) {} + | ^^^^^^^ error: aborting due to 1 previous error From 7a0319f01d08e541c8e16febaa68c5fa8b66b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 Aug 2025 20:14:01 +0000 Subject: [PATCH 1018/1900] Point at lifetime requirement origin in more cases --- .../rustc_borrowck/src/borrowck_errors.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 17 +--------------- .../src/diagnostics/explain_borrow.rs | 13 ++++++++++++ ...t-precise-captures-we-are-powerless.stderr | 14 +++++++++---- .../borrowck/fn-item-check-type-params.stderr | 12 +++++++++++ ...ation-not-general-enough-ice-133252.stderr | 2 +- tests/ui/borrowck/issue-17545.stderr | 3 +++ .../bugs/hrtb-implied-1.stderr | 5 +++++ .../precise-capturing/migration-note.rs | 4 ++-- .../precise-capturing/migration-note.stderr | 4 ++-- ...er-to-static-comparing-against-free.stderr | 2 +- .../propagate-multiple-requirements.stderr | 2 +- .../nll/local-outlives-static-via-hrtb.stderr | 20 +++++++++---------- ...insensitive-scopes-issue-117146.nll.stderr | 10 +++++----- ...sitive-scopes-issue-117146.polonius.stderr | 10 +++++----- .../regions-infer-proc-static-upvar.stderr | 2 +- .../regions-pattern-typing-issue-19552.stderr | 2 +- tests/ui/static/static-lifetime-bound.stderr | 2 +- tests/ui/static/static-region-bound.stderr | 6 ++++++ .../wf-in-where-clause-static.current.stderr | 6 ++++++ .../wf/wf-in-where-clause-static.next.stderr | 6 ++++++ 21 files changed, 93 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index bc985cbe13353..7c9011505d64c 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -480,7 +480,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { } pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> { - struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",) + struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed") } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 3b268f538cf2d..efb622e2155ca 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2992,6 +2992,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.buffer_error(err); } + #[tracing::instrument(level = "debug", skip(self, explanation))] fn report_local_value_does_not_live_long_enough( &self, location: Location, @@ -3001,13 +3002,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { - debug!( - "report_local_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}\ - )", - location, name, borrow, drop_span, borrow_spans - ); - let borrow_span = borrow_spans.var_or_use_path_span(); if let BorrowExplanation::MustBeValidFor { category, @@ -3031,15 +3025,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut err = self.path_does_not_live_long_enough(borrow_span, &name); - if let BorrowExplanation::MustBeValidFor { ref path, region_name, .. } = explanation { - for constraint in path { - if let ConstraintCategory::Predicate(pred) = constraint.category - && !pred.is_dummy() - { - err.span_note(pred, format!("requirement that {name} is borrowed for `{region_name}` introduced here")); - } - } - } if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { let region_name = annotation.emit(self, &mut err); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 7ca07bb9b4348..a0ea8b0855699 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -416,6 +416,19 @@ impl<'tcx> BorrowExplanation<'tcx> { { self.add_object_lifetime_default_note(tcx, err, unsize_ty); } + + for constraint in path { + if let ConstraintCategory::Predicate(pred) = constraint.category + && !pred.is_dummy() + { + err.span_note( + pred, + format!("requirement for `{region_name}` introduced here"), + ); + break; + } + } + self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index e99ff763b9a2d..e0776c81dff3d 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -29,7 +29,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement that `c` is borrowed for `'a` introduced here +note: requirement for `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -80,7 +80,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement that `c` is borrowed for `'a` introduced here +note: requirement for `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -101,6 +101,12 @@ LL | outlives::<'a>(c()); | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); | ^ move out of `c` occurs here + | +note: requirement for `'a` introduced here + --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 + | +LL | fn outlives<'a>(_: impl Sized + 'a) {} + | ^^ error[E0597]: `x` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:36:13 @@ -142,7 +148,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement that `c` is borrowed for `'a` introduced here +note: requirement for `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -190,7 +196,7 @@ LL | T.outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement that `c` is borrowed for `'a` introduced here +note: requirement for `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:49:47 | LL | fn outlives<'a>(&'a self, _: impl Sized + 'a) {} diff --git a/tests/ui/borrowck/fn-item-check-type-params.stderr b/tests/ui/borrowck/fn-item-check-type-params.stderr index aafb7e66ef55f..8da0201134b03 100644 --- a/tests/ui/borrowck/fn-item-check-type-params.stderr +++ b/tests/ui/borrowck/fn-item-check-type-params.stderr @@ -27,6 +27,12 @@ LL | want(&String::new(), extend_lt); | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` + | +note: requirement for `'static` introduced here + --> $DIR/fn-item-check-type-params.rs:47:33 + | +LL | fn want(_: I, _: impl Fn(I) -> O) {} + | ^^^^^^^^^^ error[E0716]: temporary value dropped while borrowed --> $DIR/fn-item-check-type-params.rs:54:26 @@ -36,6 +42,12 @@ LL | let val = extend_lt(&String::from("blah blah blah")); | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` + | +note: requirement for `'static` introduced here + --> $DIR/fn-item-check-type-params.rs:22:21 + | +LL | (T, Option): Displayable, + | ^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index 393f7b1a61dfa..dab969586efd8 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -23,7 +23,7 @@ LL | force_send(async_load(¬_static)); LL | } | - `not_static` dropped here while still borrowed | -note: requirement that `not_static` is borrowed for `'1` introduced here +note: requirement for `'1` introduced here --> $DIR/implementation-not-general-enough-ice-133252.rs:16:18 | LL | fn force_send(_: T) {} diff --git a/tests/ui/borrowck/issue-17545.stderr b/tests/ui/borrowck/issue-17545.stderr index 45e977e39477e..dc0a84b0ab42a 100644 --- a/tests/ui/borrowck/issue-17545.stderr +++ b/tests/ui/borrowck/issue-17545.stderr @@ -10,6 +10,9 @@ LL | | )); | | -- temporary value is freed at the end of this statement | |______| | argument requires that borrow lasts for `'a` + | +note: requirement for `'a` introduced here + --> $SRC_DIR/core/src/ops/function.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr b/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr index 8bb72833e301c..72f9a477dafcc 100644 --- a/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr +++ b/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr @@ -14,6 +14,11 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | for<'a> I::Item<'a>: Debug, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: requirement for `'static` introduced here + --> $DIR/hrtb-implied-1.rs:26:26 + | +LL | for<'a> I::Item<'a>: Debug, + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs index 25b594e9b1ec7..211def3332002 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.rs +++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs @@ -32,7 +32,7 @@ fn needs_static() { //~| NOTE borrowed value does not live long enoug fn needs_static(_: impl Sized + 'static) {} - //~^ NOTE requirement that `x` is borrowed for `'static` introduced here + //~^ NOTE requirement for `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } @@ -80,7 +80,7 @@ fn needs_static_mut() { //~| NOTE borrowed value does not live long enough fn needs_static(_: impl Sized + 'static) {} - //~^ NOTE requirement that `x` is borrowed for `'static` introduced here + //~^ NOTE requirement for `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index e42a6f15c7e58..c06b14a41139b 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -50,7 +50,7 @@ LL | LL | } | - `x` dropped here while still borrowed | -note: requirement that `x` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/migration-note.rs:34:37 | LL | fn needs_static(_: impl Sized + 'static) {} @@ -131,7 +131,7 @@ LL | LL | } | - `x` dropped here while still borrowed | -note: requirement that `x` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/migration-note.rs:82:37 | LL | fn needs_static(_: impl Sized + 'static) {} diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 136da57daec1b..eb110e868aa58 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -70,7 +70,7 @@ LL | }) LL | } | - `a` dropped here while still borrowed | -note: requirement that `a` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:13:8 | LL | F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>), diff --git a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr index 8e18f546ebd4d..51e4d77c91a1b 100644 --- a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr +++ b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr @@ -14,7 +14,7 @@ LL | z = &local_arr; LL | } | - `local_arr` dropped here while still borrowed | -note: requirement that `local_arr` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/propagate-multiple-requirements.rs:4:21 | LL | fn once U>(f: F, s: S, t: T) -> U { diff --git a/tests/ui/nll/local-outlives-static-via-hrtb.stderr b/tests/ui/nll/local-outlives-static-via-hrtb.stderr index cc01a3cfc0b90..ff2be3a2b28ec 100644 --- a/tests/ui/nll/local-outlives-static-via-hrtb.stderr +++ b/tests/ui/nll/local-outlives-static-via-hrtb.stderr @@ -12,16 +12,16 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); LL | } | - `local` dropped here while still borrowed | -note: requirement that `local` is borrowed for `'static` introduced here - --> $DIR/local-outlives-static-via-hrtb.rs:15:53 - | -LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} - | ^^^^^^^^^^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/local-outlives-static-via-hrtb.rs:15:42 | LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^ +note: requirement for `'static` introduced here + --> $DIR/local-outlives-static-via-hrtb.rs:15:53 + | +LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} + | ^^^^^^^^^^^^ error[E0597]: `local` does not live long enough --> $DIR/local-outlives-static-via-hrtb.rs:25:45 @@ -37,16 +37,16 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); LL | } | - `local` dropped here while still borrowed | -note: requirement that `local` is borrowed for `'static` introduced here - --> $DIR/local-outlives-static-via-hrtb.rs:19:30 - | -LL | for<'a> &'a T: Reference, - | ^^^^^^^^^^^^^^^^^^^^^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/local-outlives-static-via-hrtb.rs:19:5 | LL | for<'a> &'a T: Reference, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: requirement for `'static` introduced here + --> $DIR/local-outlives-static-via-hrtb.rs:19:30 + | +LL | for<'a> &'a T: Reference, + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr index 56ad78ba4b424..fdc3fc059c534 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr @@ -13,16 +13,16 @@ LL | let b = |_| &a; LL | } | - `a` dropped here while still borrowed | -note: requirement that `a` is borrowed for `'static` introduced here - --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 - | -LL | fn bad &()>(_: F) {} - | ^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | LL | fn bad &()>(_: F) {} | ^^^^^^^^^^^^^^ +note: requirement for `'static` introduced here + --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 + | +LL | fn bad &()>(_: F) {} + | ^^^ error: implementation of `Fn` is not general enough --> $DIR/location-insensitive-scopes-issue-117146.rs:13:5 diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr index 56ad78ba4b424..fdc3fc059c534 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr @@ -13,16 +13,16 @@ LL | let b = |_| &a; LL | } | - `a` dropped here while still borrowed | -note: requirement that `a` is borrowed for `'static` introduced here - --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 - | -LL | fn bad &()>(_: F) {} - | ^^^ note: due to a current limitation of the type system, this implies a `'static` lifetime --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | LL | fn bad &()>(_: F) {} | ^^^^^^^^^^^^^^ +note: requirement for `'static` introduced here + --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 + | +LL | fn bad &()>(_: F) {} + | ^^^ error: implementation of `Fn` is not general enough --> $DIR/location-insensitive-scopes-issue-117146.rs:13:5 diff --git a/tests/ui/regions/regions-infer-proc-static-upvar.stderr b/tests/ui/regions/regions-infer-proc-static-upvar.stderr index 1a76b5f00f943..ff833de0ad012 100644 --- a/tests/ui/regions/regions-infer-proc-static-upvar.stderr +++ b/tests/ui/regions/regions-infer-proc-static-upvar.stderr @@ -12,7 +12,7 @@ LL | | }); LL | } | - `x` dropped here while still borrowed | -note: requirement that `x` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/regions-infer-proc-static-upvar.rs:4:19 | LL | fn foo(_p: F) { } diff --git a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr index 2c8ce20f09e46..76cd18dfa1329 100644 --- a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr +++ b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr @@ -11,7 +11,7 @@ LL | } LL | } | - `line` dropped here while still borrowed | -note: requirement that `line` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/regions-pattern-typing-issue-19552.rs:1:21 | LL | fn assert_static(_t: T) {} diff --git a/tests/ui/static/static-lifetime-bound.stderr b/tests/ui/static/static-lifetime-bound.stderr index e530e4669d7f6..354a1327beead 100644 --- a/tests/ui/static/static-lifetime-bound.stderr +++ b/tests/ui/static/static-lifetime-bound.stderr @@ -11,7 +11,7 @@ LL | f(&x); LL | } | - `x` dropped here while still borrowed | -note: requirement that `x` is borrowed for `'static` introduced here +note: requirement for `'static` introduced here --> $DIR/static-lifetime-bound.rs:1:10 | LL | fn f<'a: 'static>(_: &'a i32) {} diff --git a/tests/ui/static/static-region-bound.stderr b/tests/ui/static/static-region-bound.stderr index a47c94571022d..e7747216a57b1 100644 --- a/tests/ui/static/static-region-bound.stderr +++ b/tests/ui/static/static-region-bound.stderr @@ -7,6 +7,12 @@ LL | f(x); | ---- argument requires that borrow lasts for `'static` LL | } | - temporary value is freed at the end of this statement + | +note: requirement for `'static` introduced here + --> $DIR/static-region-bound.rs:3:8 + | +LL | fn f(_: T) {} + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/wf/wf-in-where-clause-static.current.stderr b/tests/ui/wf/wf-in-where-clause-static.current.stderr index d0bb89884c68a..53cc6093f540b 100644 --- a/tests/ui/wf/wf-in-where-clause-static.current.stderr +++ b/tests/ui/wf/wf-in-where-clause-static.current.stderr @@ -6,6 +6,12 @@ LL | let s = foo(&String::from("blah blah blah")); | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` + | +note: requirement for `'static` introduced here + --> $DIR/wf-in-where-clause-static.rs:12:17 + | +LL | &'static S: Static, + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/wf/wf-in-where-clause-static.next.stderr b/tests/ui/wf/wf-in-where-clause-static.next.stderr index d0bb89884c68a..53cc6093f540b 100644 --- a/tests/ui/wf/wf-in-where-clause-static.next.stderr +++ b/tests/ui/wf/wf-in-where-clause-static.next.stderr @@ -6,6 +6,12 @@ LL | let s = foo(&String::from("blah blah blah")); | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` + | +note: requirement for `'static` introduced here + --> $DIR/wf-in-where-clause-static.rs:12:17 + | +LL | &'static S: Static, + | ^^^^^^ error: aborting due to 1 previous error From 4973903cd204dd2cd36e15267de72ed6d954e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 31 Aug 2025 20:42:01 +0000 Subject: [PATCH 1019/1900] reword note --- .../rustc_borrowck/src/diagnostics/explain_borrow.rs | 2 +- .../without-precise-captures-we-are-powerless.stderr | 10 +++++----- tests/ui/borrowck/fn-item-check-type-params.stderr | 4 ++-- ...implementation-not-general-enough-ice-133252.stderr | 2 +- tests/ui/borrowck/issue-17545.stderr | 2 +- .../bugs/hrtb-implied-1.stderr | 2 +- .../ui/impl-trait/precise-capturing/migration-note.rs | 4 ++-- .../impl-trait/precise-capturing/migration-note.stderr | 4 ++-- ...ted-shorter-to-static-comparing-against-free.stderr | 2 +- .../propagate-multiple-requirements.stderr | 2 +- tests/ui/nll/local-outlives-static-via-hrtb.stderr | 4 ++-- ...location-insensitive-scopes-issue-117146.nll.stderr | 2 +- ...ion-insensitive-scopes-issue-117146.polonius.stderr | 2 +- .../ui/regions/regions-infer-proc-static-upvar.stderr | 2 +- .../regions/regions-pattern-typing-issue-19552.stderr | 2 +- tests/ui/static/static-lifetime-bound.stderr | 2 +- tests/ui/static/static-region-bound.stderr | 2 +- tests/ui/wf/wf-in-where-clause-static.current.stderr | 2 +- tests/ui/wf/wf-in-where-clause-static.next.stderr | 2 +- 19 files changed, 27 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index a0ea8b0855699..66c43a07c80ce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -423,7 +423,7 @@ impl<'tcx> BorrowExplanation<'tcx> { { err.span_note( pred, - format!("requirement for `{region_name}` introduced here"), + format!("requirement that the value outlives `{region_name}` introduced here"), ); break; } diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index e0776c81dff3d..4aae9807dd2e4 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -29,7 +29,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -80,7 +80,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -102,7 +102,7 @@ LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); | ^ move out of `c` occurs here | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -148,7 +148,7 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:7:33 | LL | fn outlives<'a>(_: impl Sized + 'a) {} @@ -196,7 +196,7 @@ LL | T.outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $DIR/without-precise-captures-we-are-powerless.rs:49:47 | LL | fn outlives<'a>(&'a self, _: impl Sized + 'a) {} diff --git a/tests/ui/borrowck/fn-item-check-type-params.stderr b/tests/ui/borrowck/fn-item-check-type-params.stderr index 8da0201134b03..7a0a7752a14b2 100644 --- a/tests/ui/borrowck/fn-item-check-type-params.stderr +++ b/tests/ui/borrowck/fn-item-check-type-params.stderr @@ -28,7 +28,7 @@ LL | want(&String::new(), extend_lt); | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/fn-item-check-type-params.rs:47:33 | LL | fn want(_: I, _: impl Fn(I) -> O) {} @@ -43,7 +43,7 @@ LL | let val = extend_lt(&String::from("blah blah blah")); | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/fn-item-check-type-params.rs:22:21 | LL | (T, Option): Displayable, diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index dab969586efd8..7b840d54ed038 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -23,7 +23,7 @@ LL | force_send(async_load(¬_static)); LL | } | - `not_static` dropped here while still borrowed | -note: requirement for `'1` introduced here +note: requirement that the value outlives `'1` introduced here --> $DIR/implementation-not-general-enough-ice-133252.rs:16:18 | LL | fn force_send(_: T) {} diff --git a/tests/ui/borrowck/issue-17545.stderr b/tests/ui/borrowck/issue-17545.stderr index dc0a84b0ab42a..63fd57cd2336f 100644 --- a/tests/ui/borrowck/issue-17545.stderr +++ b/tests/ui/borrowck/issue-17545.stderr @@ -11,7 +11,7 @@ LL | | )); | |______| | argument requires that borrow lasts for `'a` | -note: requirement for `'a` introduced here +note: requirement that the value outlives `'a` introduced here --> $SRC_DIR/core/src/ops/function.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr b/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr index 72f9a477dafcc..77a637c470cc2 100644 --- a/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr +++ b/tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr @@ -14,7 +14,7 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | for<'a> I::Item<'a>: Debug, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/hrtb-implied-1.rs:26:26 | LL | for<'a> I::Item<'a>: Debug, diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs index 211def3332002..412d8af98845e 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.rs +++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs @@ -32,7 +32,7 @@ fn needs_static() { //~| NOTE borrowed value does not live long enoug fn needs_static(_: impl Sized + 'static) {} - //~^ NOTE requirement for `'static` introduced here + //~^ NOTE requirement that the value outlives `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } @@ -80,7 +80,7 @@ fn needs_static_mut() { //~| NOTE borrowed value does not live long enough fn needs_static(_: impl Sized + 'static) {} - //~^ NOTE requirement for `'static` introduced here + //~^ NOTE requirement that the value outlives `'static` introduced here needs_static(a); //~^ NOTE argument requires that `x` is borrowed for `'static` } diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index c06b14a41139b..880e7878477af 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -50,7 +50,7 @@ LL | LL | } | - `x` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/migration-note.rs:34:37 | LL | fn needs_static(_: impl Sized + 'static) {} @@ -131,7 +131,7 @@ LL | LL | } | - `x` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/migration-note.rs:82:37 | LL | fn needs_static(_: impl Sized + 'static) {} diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index eb110e868aa58..af07745a00af4 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -70,7 +70,7 @@ LL | }) LL | } | - `a` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:13:8 | LL | F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>), diff --git a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr index 51e4d77c91a1b..4136ac418deb4 100644 --- a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr +++ b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr @@ -14,7 +14,7 @@ LL | z = &local_arr; LL | } | - `local_arr` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/propagate-multiple-requirements.rs:4:21 | LL | fn once U>(f: F, s: S, t: T) -> U { diff --git a/tests/ui/nll/local-outlives-static-via-hrtb.stderr b/tests/ui/nll/local-outlives-static-via-hrtb.stderr index ff2be3a2b28ec..263d271b6b3d0 100644 --- a/tests/ui/nll/local-outlives-static-via-hrtb.stderr +++ b/tests/ui/nll/local-outlives-static-via-hrtb.stderr @@ -17,7 +17,7 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^ -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/local-outlives-static-via-hrtb.rs:15:53 | LL | fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} @@ -42,7 +42,7 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | for<'a> &'a T: Reference, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/local-outlives-static-via-hrtb.rs:19:30 | LL | for<'a> &'a T: Reference, diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr index fdc3fc059c534..804b3f00a2642 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr @@ -18,7 +18,7 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | fn bad &()>(_: F) {} | ^^^^^^^^^^^^^^ -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 | LL | fn bad &()>(_: F) {} diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr index fdc3fc059c534..804b3f00a2642 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr @@ -18,7 +18,7 @@ note: due to a current limitation of the type system, this implies a `'static` l | LL | fn bad &()>(_: F) {} | ^^^^^^^^^^^^^^ -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 | LL | fn bad &()>(_: F) {} diff --git a/tests/ui/regions/regions-infer-proc-static-upvar.stderr b/tests/ui/regions/regions-infer-proc-static-upvar.stderr index ff833de0ad012..158d74ed06d94 100644 --- a/tests/ui/regions/regions-infer-proc-static-upvar.stderr +++ b/tests/ui/regions/regions-infer-proc-static-upvar.stderr @@ -12,7 +12,7 @@ LL | | }); LL | } | - `x` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/regions-infer-proc-static-upvar.rs:4:19 | LL | fn foo(_p: F) { } diff --git a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr index 76cd18dfa1329..a8fd827bc6951 100644 --- a/tests/ui/regions/regions-pattern-typing-issue-19552.stderr +++ b/tests/ui/regions/regions-pattern-typing-issue-19552.stderr @@ -11,7 +11,7 @@ LL | } LL | } | - `line` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/regions-pattern-typing-issue-19552.rs:1:21 | LL | fn assert_static(_t: T) {} diff --git a/tests/ui/static/static-lifetime-bound.stderr b/tests/ui/static/static-lifetime-bound.stderr index 354a1327beead..51be79be5db8a 100644 --- a/tests/ui/static/static-lifetime-bound.stderr +++ b/tests/ui/static/static-lifetime-bound.stderr @@ -11,7 +11,7 @@ LL | f(&x); LL | } | - `x` dropped here while still borrowed | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/static-lifetime-bound.rs:1:10 | LL | fn f<'a: 'static>(_: &'a i32) {} diff --git a/tests/ui/static/static-region-bound.stderr b/tests/ui/static/static-region-bound.stderr index e7747216a57b1..8472738daa49c 100644 --- a/tests/ui/static/static-region-bound.stderr +++ b/tests/ui/static/static-region-bound.stderr @@ -8,7 +8,7 @@ LL | f(x); LL | } | - temporary value is freed at the end of this statement | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/static-region-bound.rs:3:8 | LL | fn f(_: T) {} diff --git a/tests/ui/wf/wf-in-where-clause-static.current.stderr b/tests/ui/wf/wf-in-where-clause-static.current.stderr index 53cc6093f540b..788fe2c3faa07 100644 --- a/tests/ui/wf/wf-in-where-clause-static.current.stderr +++ b/tests/ui/wf/wf-in-where-clause-static.current.stderr @@ -7,7 +7,7 @@ LL | let s = foo(&String::from("blah blah blah")); | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/wf-in-where-clause-static.rs:12:17 | LL | &'static S: Static, diff --git a/tests/ui/wf/wf-in-where-clause-static.next.stderr b/tests/ui/wf/wf-in-where-clause-static.next.stderr index 53cc6093f540b..788fe2c3faa07 100644 --- a/tests/ui/wf/wf-in-where-clause-static.next.stderr +++ b/tests/ui/wf/wf-in-where-clause-static.next.stderr @@ -7,7 +7,7 @@ LL | let s = foo(&String::from("blah blah blah")); | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` | -note: requirement for `'static` introduced here +note: requirement that the value outlives `'static` introduced here --> $DIR/wf-in-where-clause-static.rs:12:17 | LL | &'static S: Static, From 58f5260b960004090bfa9e7ef5068d6554ac9f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 28 Sep 2025 20:58:44 +0000 Subject: [PATCH 1020/1900] Address review comment --- .../src/diagnostics/explain_borrow.rs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 66c43a07c80ce..67e33b37416c4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -417,16 +417,20 @@ impl<'tcx> BorrowExplanation<'tcx> { self.add_object_lifetime_default_note(tcx, err, unsize_ty); } - for constraint in path { - if let ConstraintCategory::Predicate(pred) = constraint.category - && !pred.is_dummy() - { - err.span_note( - pred, - format!("requirement that the value outlives `{region_name}` introduced here"), - ); - break; - } + if let Some(pred) = path + .iter() + .filter_map(|constraint| match constraint.category { + ConstraintCategory::Predicate(pred) if !pred.is_dummy() => Some(pred), + _ => None, + }) + .next() + { + err.span_note( + pred, + format!( + "requirement that the value outlives `{region_name}` introduced here" + ), + ); } self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); From c5313fed76a99942edb4c7f94607fa3d2d6da21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 28 Sep 2025 21:13:53 +0000 Subject: [PATCH 1021/1900] Point at multiple outlives requirements instead of just the first one ``` error[E0716]: temporary value dropped while borrowed --> $DIR/multiple-sources-for-outlives-requirement.rs:5:38 | LL | fn foo<'b>() { | -- lifetime `'b` defined here LL | outlives_indir::<'_, 'b, _>(&mut 1u32); | ---------------------------------^^^^-- temporary value is freed at the end of this statement | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'b` | note: requirements that the value outlives `'b` introduced here --> $DIR/multiple-sources-for-outlives-requirement.rs:1:23 | LL | fn outlives_indir<'a: 'b, 'b, T: 'a>(_x: T) {} | ^^ ^^ ``` --- .../src/diagnostics/explain_borrow.rs | 13 +++++++----- ...ltiple-sources-for-outlives-requirement.rs | 11 ++++++++++ ...le-sources-for-outlives-requirement.stderr | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 tests/ui/regions/multiple-sources-for-outlives-requirement.rs create mode 100644 tests/ui/regions/multiple-sources-for-outlives-requirement.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 67e33b37416c4..638d89f5bcbdb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -417,18 +417,21 @@ impl<'tcx> BorrowExplanation<'tcx> { self.add_object_lifetime_default_note(tcx, err, unsize_ty); } - if let Some(pred) = path + let mut preds = path .iter() .filter_map(|constraint| match constraint.category { ConstraintCategory::Predicate(pred) if !pred.is_dummy() => Some(pred), _ => None, }) - .next() - { + .collect::>(); + preds.sort(); + preds.dedup(); + if !preds.is_empty() { + let s = if preds.len() == 1 { "" } else { "s" }; err.span_note( - pred, + preds, format!( - "requirement that the value outlives `{region_name}` introduced here" + "requirement{s} that the value outlives `{region_name}` introduced here" ), ); } diff --git a/tests/ui/regions/multiple-sources-for-outlives-requirement.rs b/tests/ui/regions/multiple-sources-for-outlives-requirement.rs new file mode 100644 index 0000000000000..720cd1cf6eecb --- /dev/null +++ b/tests/ui/regions/multiple-sources-for-outlives-requirement.rs @@ -0,0 +1,11 @@ +fn outlives_indir<'a: 'b, 'b, T: 'a>(_x: T) {} +//~^ NOTE: requirements that the value outlives `'b` introduced here + +fn foo<'b>() { //~ NOTE: lifetime `'b` defined here + outlives_indir::<'_, 'b, _>(&mut 1u32); //~ ERROR: temporary value dropped while borrowed + //~^ NOTE: argument requires that borrow lasts for `'b` + //~| NOTE: creates a temporary value which is freed while still in use + //~| NOTE: temporary value is freed at the end of this statement +} + +fn main() {} diff --git a/tests/ui/regions/multiple-sources-for-outlives-requirement.stderr b/tests/ui/regions/multiple-sources-for-outlives-requirement.stderr new file mode 100644 index 0000000000000..4cdaf950e155c --- /dev/null +++ b/tests/ui/regions/multiple-sources-for-outlives-requirement.stderr @@ -0,0 +1,20 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/multiple-sources-for-outlives-requirement.rs:5:38 + | +LL | fn foo<'b>() { + | -- lifetime `'b` defined here +LL | outlives_indir::<'_, 'b, _>(&mut 1u32); + | ---------------------------------^^^^-- temporary value is freed at the end of this statement + | | | + | | creates a temporary value which is freed while still in use + | argument requires that borrow lasts for `'b` + | +note: requirements that the value outlives `'b` introduced here + --> $DIR/multiple-sources-for-outlives-requirement.rs:1:23 + | +LL | fn outlives_indir<'a: 'b, 'b, T: 'a>(_x: T) {} + | ^^ ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. From 8f7d61b9efe1f44701f87fe4647eb1f39d20f434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 25 Nov 2024 21:52:08 +0000 Subject: [PATCH 1022/1900] Detect unconstructable re-exported tuple structs When a tuple-struct is re-exported that has inaccessible fields at the `use` scope, the type's constructor cannot be accessed through that re-export. We now account for this case and extend the resulting resolution error. We also check if the constructor would be accessible directly, not through the re-export, and if so, we suggest using the full path instead. ``` error[E0423]: cannot initialize a tuple struct which contains private fields --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:33 | LL | let crate::Foo(x) = crate::Foo(42); | ^^^^^^^^^^ | note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9 | LL | pub use my_mod::Foo; | ^^^^^^^^^^^ help: the type can be constructed directly, because its fields are available from the current scope | LL | let crate::Foo(x) = crate::my_mod::Foo(42); | ~~~~~~~~~~~~~~~~~~ ``` Fix #133343. --- compiler/rustc_resolve/src/ident.rs | 33 +++++++ .../rustc_resolve/src/late/diagnostics.rs | 99 ++++++++++++------- compiler/rustc_resolve/src/lib.rs | 6 ++ ...ue-to-inaccessible-field-in-reexport.fixed | 20 ++++ ...e-due-to-inaccessible-field-in-reexport.rs | 20 ++++ ...e-to-inaccessible-field-in-reexport.stderr | 36 +++++++ 6 files changed, 181 insertions(+), 33 deletions(-) create mode 100644 tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.fixed create mode 100644 tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs create mode 100644 tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.stderr diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 51489019950ad..4415300777f98 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -901,6 +901,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding, if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None }, parent_scope, + module, finalize, shadowing, ); @@ -1025,6 +1026,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding: Option>, shadowed_glob: Option>, parent_scope: &ParentScope<'ra>, + module: Module<'ra>, finalize: Finalize, shadowing: Shadowing, ) -> Result, (Determinacy, Weak)> { @@ -1076,6 +1078,37 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); } + // If we encounter a re-export for a type with private fields, it will not be able to + // be constructed through this re-export. We track that case here to expand later + // privacy errors with appropriate information. + if let Res::Def(_, def_id) = binding.res() { + let struct_ctor = match def_id.as_local() { + Some(def_id) => self.struct_constructors.get(&def_id).cloned(), + None => { + let ctor = self.cstore().ctor_untracked(def_id); + ctor.map(|(ctor_kind, ctor_def_id)| { + let ctor_res = Res::Def( + DefKind::Ctor(rustc_hir::def::CtorOf::Struct, ctor_kind), + ctor_def_id, + ); + let ctor_vis = self.tcx.visibility(ctor_def_id); + let field_visibilities = self + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|field_id| self.tcx.visibility(field_id)) + .collect(); + (ctor_res, ctor_vis, field_visibilities) + }) + } + }; + if let Some((_, _, fields)) = struct_ctor + && fields.iter().any(|vis| !self.is_accessible_from(*vis, module)) + { + self.inaccessible_ctor_reexport.insert(path_span, binding.span); + } + } + self.record_use(ident, binding, used); return Ok(binding); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9e3c093883661..8c2ddda7f9834 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1942,44 +1942,77 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return true; }; + let update_message = + |this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| { + match source { + // e.g. `if let Enum::TupleVariant(field1, field2) = _` + PathSource::TupleStruct(_, pattern_spans) => { + err.primary_message( + "cannot match against a tuple struct which contains private fields", + ); + + // Use spans of the tuple struct pattern. + Some(Vec::from(*pattern_spans)) + } + // e.g. `let _ = Enum::TupleVariant(field1, field2);` + PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, args), + span: call_span, + .. + })) => { + err.primary_message( + "cannot initialize a tuple struct which contains private fields", + ); + this.suggest_alternative_construction_methods( + def_id, + err, + path.span, + *call_span, + &args[..], + ); + // Use spans of the tuple struct definition. + this.r + .field_idents(def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::>()) + } + _ => None, + } + }; let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); + if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span) + && is_accessible + { + err.span_note( + *use_span, + "the type is accessed through this re-export, but the type's constructor \ + is not visible in this import's scope due to private fields", + ); + if is_accessible + && fields + .iter() + .all(|vis| self.r.is_accessible_from(*vis, self.parent_scope.module)) + { + err.span_suggestion_verbose( + span, + "the type can be constructed directly, because its fields are \ + available from the current scope", + // Using `tcx.def_path_str` causes the compiler to hang. + // We don't need to handle foreign crate types because in that case you + // can't access the ctor either way. + format!( + "crate{}", // The method already has leading `::`. + self.r.tcx.def_path(def_id).to_string_no_crate_verbose(), + ), + Applicability::MachineApplicable, + ); + } + update_message(self, err, &source); + } if !is_expected(ctor_def) || is_accessible { return true; } - let field_spans = match source { - // e.g. `if let Enum::TupleVariant(field1, field2) = _` - PathSource::TupleStruct(_, pattern_spans) => { - err.primary_message( - "cannot match against a tuple struct which contains private fields", - ); - - // Use spans of the tuple struct pattern. - Some(Vec::from(pattern_spans)) - } - // e.g. `let _ = Enum::TupleVariant(field1, field2);` - PathSource::Expr(Some(Expr { - kind: ExprKind::Call(path, args), - span: call_span, - .. - })) => { - err.primary_message( - "cannot initialize a tuple struct which contains private fields", - ); - self.suggest_alternative_construction_methods( - def_id, - err, - path.span, - *call_span, - &args[..], - ); - // Use spans of the tuple struct definition. - self.r - .field_idents(def_id) - .map(|fields| fields.iter().map(|f| f.span).collect::>()) - } - _ => None, - }; + let field_spans = update_message(self, err, &source); if let Some(spans) = field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8959068b2a677..b44b1c966a431 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1167,6 +1167,11 @@ pub struct Resolver<'ra, 'tcx> { /// Crate-local macro expanded `macro_export` referred to by a module-relative path. macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(), + /// When a type is re-exported that has an inaccessible constructor because it has fields that + /// are inaccessible from the import's scope, we mark that as the type won't be able to be built + /// through the re-export. We use this information to extend the existing diagnostic. + inaccessible_ctor_reexport: FxHashMap, + arenas: &'ra ResolverArenas<'ra>, dummy_binding: NameBinding<'ra>, builtin_types_bindings: FxHashMap>, @@ -1595,6 +1600,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { glob_map: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), + inaccessible_ctor_reexport: Default::default(), arenas, dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT), diff --git a/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.fixed b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.fixed new file mode 100644 index 0000000000000..63cc3333b6b77 --- /dev/null +++ b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.fixed @@ -0,0 +1,20 @@ +#![allow(dead_code, unused_variables)] +//@ run-rustfix +pub use my_mod::Foo; +//~^ NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields +//~| NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields + +mod my_mod { + pub struct Foo(u32); + + mod my_sub_mod { + fn my_func() { + let crate::my_mod::Foo(x) = crate::my_mod::Foo(42); + //~^ ERROR cannot initialize a tuple struct which contains private fields + //~| HELP the type can be constructed directly, because its fields are available from the current scope + //~| ERROR cannot match against a tuple struct which contains private fields + //~| HELP the type can be constructed directly, because its fields are available from the current scope + } + } +} +fn main() {} diff --git a/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs new file mode 100644 index 0000000000000..0b695f9065459 --- /dev/null +++ b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs @@ -0,0 +1,20 @@ +#![allow(dead_code, unused_variables)] +//@ run-rustfix +pub use my_mod::Foo; +//~^ NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields +//~| NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields + +mod my_mod { + pub struct Foo(u32); + + mod my_sub_mod { + fn my_func() { + let crate::Foo(x) = crate::Foo(42); + //~^ ERROR cannot initialize a tuple struct which contains private fields + //~| HELP the type can be constructed directly, because its fields are available from the current scope + //~| ERROR cannot match against a tuple struct which contains private fields + //~| HELP the type can be constructed directly, because its fields are available from the current scope + } + } +} +fn main() {} diff --git a/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.stderr b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.stderr new file mode 100644 index 0000000000000..6ab324cb32f39 --- /dev/null +++ b/tests/ui/privacy/ctor-not-accessible-due-to-inaccessible-field-in-reexport.stderr @@ -0,0 +1,36 @@ +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:33 + | +LL | let crate::Foo(x) = crate::Foo(42); + | ^^^^^^^^^^ + | +note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields + --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9 + | +LL | pub use my_mod::Foo; + | ^^^^^^^^^^^ +help: the type can be constructed directly, because its fields are available from the current scope + | +LL | let crate::Foo(x) = crate::my_mod::Foo(42); + | ++++++++ + +error[E0532]: cannot match against a tuple struct which contains private fields + --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:17 + | +LL | let crate::Foo(x) = crate::Foo(42); + | ^^^^^^^^^^ + | +note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields + --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9 + | +LL | pub use my_mod::Foo; + | ^^^^^^^^^^^ +help: the type can be constructed directly, because its fields are available from the current scope + | +LL | let crate::my_mod::Foo(x) = crate::Foo(42); + | ++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0423, E0532. +For more information about an error, try `rustc --explain E0423`. From 0c9d0dfe046f0674f0507df564504ac3bac862d9 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 28 Sep 2025 14:40:39 -0700 Subject: [PATCH 1023/1900] remove explicit deref of AbiAlign for most methods Much of the compiler calls functions on Align projected from AbiAlign. AbiAlign impls Deref to its inner Align, so we can simplify these away. Also, it will minimize disruption when AbiAlign is removed. For now, preserve usages that might resolve to PartialOrd or PartialEq, as those have odd inference. --- compiler/rustc_abi/src/layout.rs | 6 +++--- compiler/rustc_abi/src/lib.rs | 2 +- compiler/rustc_codegen_cranelift/src/abi/comments.rs | 2 +- compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs | 2 +- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs | 2 +- .../rustc_codegen_cranelift/src/debuginfo/types.rs | 6 ++---- compiler/rustc_codegen_cranelift/src/unsize.rs | 6 +++--- .../rustc_codegen_cranelift/src/value_and_place.rs | 6 +++--- compiler/rustc_codegen_gcc/src/context.rs | 6 +++--- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 2 +- .../src/debuginfo/metadata/enums/native.rs | 4 ++-- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 4 ++-- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 2 +- compiler/rustc_codegen_ssa/src/size_of_val.rs | 8 ++++---- compiler/rustc_const_eval/src/interpret/operator.rs | 2 +- compiler/rustc_const_eval/src/util/alignment.rs | 2 +- .../src/util/check_validity_requirement.rs | 2 +- .../rustc_hir_analysis/src/hir_ty_lowering/cmse.rs | 2 +- compiler/rustc_middle/src/ty/vtable.rs | 2 +- .../rustc_mir_transform/src/dataflow_const_prop.rs | 2 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- compiler/rustc_mir_transform/src/known_panics_lint.rs | 2 +- compiler/rustc_target/src/callconv/arm.rs | 2 +- compiler/rustc_target/src/callconv/loongarch.rs | 2 +- compiler/rustc_target/src/callconv/nvptx64.rs | 4 ++-- compiler/rustc_target/src/callconv/powerpc64.rs | 2 +- compiler/rustc_target/src/callconv/riscv.rs | 2 +- compiler/rustc_target/src/callconv/xtensa.rs | 2 +- compiler/rustc_transmute/src/layout/tree.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 10 +++++----- compiler/rustc_ty_utils/src/layout/invariant.rs | 6 +++--- .../clippy_lints/src/casts/cast_ptr_alignment.rs | 4 ++-- .../clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- src/tools/miri/src/machine.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/layout/tests.rs | 4 ++-- src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 6 +++--- .../rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 40 files changed, 66 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5004d0c80220f..d56fbb5cb94f2 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1169,7 +1169,7 @@ impl LayoutCalculator { // To allow unsizing `&Foo` -> `&Foo`, the layout of the struct must // not depend on the layout of the tail. let max_field_align = - fields_excluding_tail.iter().map(|f| f.align.abi.bytes()).max().unwrap_or(1); + fields_excluding_tail.iter().map(|f| f.align.bytes()).max().unwrap_or(1); let largest_niche_size = fields_excluding_tail .iter() .filter_map(|f| f.largest_niche) @@ -1189,7 +1189,7 @@ impl LayoutCalculator { } else { // Returns `log2(effective-align)`. The calculation assumes that size is an // integer multiple of align, except for ZSTs. - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); let size = layout.size.bytes(); let niche_size = layout.largest_niche.map(|n| n.available(dl)).unwrap_or(0); // Group [u8; 4] with align-4 or [u8; 6] with align-2 fields. @@ -1488,7 +1488,7 @@ impl LayoutCalculator { for i in layout.fields.index_by_increasing_offset() { let offset = layout.fields.offset(i); let f = &fields[FieldIdx::new(i)]; - write!(s, "[o{}a{}s{}", offset.bytes(), f.align.abi.bytes(), f.size.bytes()).unwrap(); + write!(s, "[o{}a{}s{}", offset.bytes(), f.align.bytes(), f.size.bytes()).unwrap(); if let Some(n) = f.largest_niche { write!( s, diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 369874521e57e..9a364f9be184a 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -2159,7 +2159,7 @@ impl LayoutData { /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). pub fn is_1zst(&self) -> bool { - self.is_sized() && self.size.bytes() == 0 && self.align.abi.bytes() == 1 + self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1 } /// Returns `true` if the type is a ZST and not unsized. diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs index c74efeb59f3fc..d1b2b9a502ac2 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/comments.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -89,7 +89,7 @@ pub(super) fn add_local_place_comments<'tcx>( format!("{:?}", local), format!("{:?}", ty), size.bytes(), - align.abi.bytes(), + align.bytes(), if extra.is_empty() { "" } else { " " }, extra, )); diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 2031842062d97..7a909a740b054 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -233,7 +233,7 @@ pub(super) fn from_casted_value<'tcx>( // It may also be smaller for example when the type is a wrapper around an integer with a // larger alignment than the integer. std::cmp::max(abi_param_size, layout_size), - u32::try_from(layout.align.abi.bytes()).unwrap(), + u32::try_from(layout.align.bytes()).unwrap(), ); let mut block_params_iter = block_params.iter().copied(); for (offset, _) in abi_params { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 41e11e1de6163..2cc5b82ddd345 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -846,7 +846,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let layout = fx.layout_of(fx.monomorphize(ty)); let val = match null_op { NullOp::SizeOf => layout.size.bytes(), - NullOp::AlignOf => layout.align.abi.bytes(), + NullOp::AlignOf => layout.align.bytes(), NullOp::OffsetOf(fields) => fx .tcx .offset_of_subfield( diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 286e02b986b3c..4c438742f3d22 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -304,7 +304,7 @@ impl DebugContext { entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); - entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(static_layout.align.abi.bytes())); + entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(static_layout.align.bytes())); let mut expr = Expression::new(); expr.op_addr(address_for_data(data_id)); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 25b922c8be4c7..0d49f32373caa 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -166,7 +166,7 @@ impl DebugContext { let tuple_entry = self.dwarf.unit.get_mut(tuple_type_id); tuple_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); tuple_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); - tuple_entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(layout.align.abi.bytes())); + tuple_entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(layout.align.bytes())); for (i, (ty, dw_ty)) in components.into_iter().enumerate() { let member_id = self.dwarf.unit.add(tuple_type_id, gimli::DW_TAG_member); @@ -178,9 +178,7 @@ impl DebugContext { member_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); member_entry.set( gimli::DW_AT_alignment, - AttributeValue::Udata( - FullyMonomorphizedLayoutCx(tcx).layout_of(ty).align.abi.bytes(), - ), + AttributeValue::Udata(FullyMonomorphizedLayoutCx(tcx).layout_of(ty).align.bytes()), ); member_entry.set( gimli::DW_AT_data_member_location, diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 643c7feb89a26..d994f3e32ec3c 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -167,7 +167,7 @@ pub(crate) fn size_and_align_of<'tcx>( if layout.is_sized() { return ( fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64), - fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64), + fx.bcx.ins().iconst(fx.pointer_type, layout.align.bytes() as i64), ); } @@ -186,7 +186,7 @@ pub(crate) fn size_and_align_of<'tcx>( // times the unit size. ( fx.bcx.ins().imul_imm(info.unwrap(), unit.size.bytes() as i64), - fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64), + fx.bcx.ins().iconst(fx.pointer_type, unit.align.bytes() as i64), ) } ty::Foreign(_) => { @@ -224,7 +224,7 @@ pub(crate) fn size_and_align_of<'tcx>( let unsized_offset_unadjusted = layout.fields.offset(i).bytes(); let unsized_offset_unadjusted = fx.bcx.ins().iconst(fx.pointer_type, unsized_offset_unadjusted as i64); - let sized_align = layout.align.abi.bytes(); + let sized_align = layout.align.bytes(); let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64); // Recurse to get the size of the dynamically sized field (must be diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 4519fa1a270e4..04e10cf17088c 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -383,7 +383,7 @@ impl<'tcx> CPlace<'tcx> { let stack_slot = fx.create_stack_slot( u32::try_from(layout.size.bytes()).unwrap(), - u32::try_from(layout.align.abi.bytes()).unwrap(), + u32::try_from(layout.align.bytes()).unwrap(), ); CPlace { inner: CPlaceInner::Addr(stack_slot, None), layout } } @@ -641,8 +641,8 @@ impl<'tcx> CPlace<'tcx> { let size = dst_layout.size.bytes(); // `emit_small_memory_copy` uses `u8` for alignments, just use the maximum // alignment that fits in a `u8` if the actual alignment is larger. - let src_align = src_layout.align.abi.bytes().try_into().unwrap_or(128); - let dst_align = dst_layout.align.abi.bytes().try_into().unwrap_or(128); + let src_align = src_layout.align.bytes().try_into().unwrap_or(128); + let dst_align = dst_layout.align.bytes().try_into().unwrap_or(128); fx.bcx.emit_small_memory_copy( fx.target_config, to_addr, diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 9815fb07eaae9..c9ae96777de44 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -147,7 +147,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let layout = tcx .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rust_type)) .unwrap(); - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); // For types with size 1, the alignment can be 1 and only 1 // So, we can skip the call to ``get_aligned`. // In the future, we can add a GCC API to query the type align, @@ -186,9 +186,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { (i128_type, u128_type) } else { /*let layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.i128)).unwrap(); - let i128_align = layout.align.abi.bytes(); + let i128_align = layout.align.bytes(); let layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.u128)).unwrap(); - let u128_align = layout.align.abi.bytes();*/ + let u128_align = layout.align.bytes();*/ // TODO(antoyo): re-enable the alignment when libgccjit fixed the issue in // gcc_jit_context_new_array_constructor (it should not use reinterpret_cast). diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 4b74c04ed7ae0..1e4ace4ca922c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1043,7 +1043,7 @@ fn create_member_type<'ll, 'tcx>( file_metadata, line_number, layout.size.bits(), - layout.align.abi.bits() as u32, + layout.align.bits() as u32, offset.bits(), flags, type_di_node, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 62d38d463aba7..1ae6e6e5eecab 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -289,7 +289,7 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>( file_metadata, line_number, enum_type_and_layout.size.bits(), - enum_type_and_layout.align.abi.bits() as u32, + enum_type_and_layout.align.bits() as u32, DIFlags::FlagZero, tag_member_di_node, create_DIArray(DIB(cx), &[]), @@ -449,7 +449,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( file_di_node, line_number, enum_type_and_layout.size.bits(), - enum_type_and_layout.align.abi.bits() as u32, + enum_type_and_layout.align.bits() as u32, Size::ZERO.bits(), discr, DIFlags::FlagZero, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 013108d1286e1..ba80352916b08 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -297,7 +297,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let align = if name == sym::unaligned_volatile_load { 1 } else { - result.layout.align.abi.bytes() as u32 + result.layout.align.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index d48c7cf874a0a..b2c60db9c679a 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -193,7 +193,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( // the offset again. bx.switch_to_block(maybe_reg); - if gr_type && layout.align.abi.bytes() > 8 { + if gr_type && layout.align.bytes() > 8 { reg_off_v = bx.add(reg_off_v, bx.const_i32(15)); reg_off_v = bx.and(reg_off_v, bx.const_i32(-16)); } @@ -761,7 +761,7 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( // byte boundary if alignment needed by type exceeds 8 byte boundary. // It isn't stated explicitly in the standard, but in practice we use // alignment greater than 16 where necessary. - if layout.layout.align.abi.bytes() > 8 { + if layout.layout.align.bytes() > 8 { unreachable!("all instances of VaArgSafe have an alignment <= 8"); } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 2602bf82095c5..0a4b0f8d49498 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -617,7 +617,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::NullOp::AlignOf => { assert!(bx.cx().type_is_sized(ty)); - let val = layout.align.abi.bytes(); + let val = layout.align.bytes(); bx.cx().const_usize(val) } mir::NullOp::OffsetOf(fields) => { diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 577012151e49f..e1bd8014d7a2f 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -21,7 +21,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( trace!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); if layout.is_sized() { let size = bx.const_usize(layout.size.bytes()); - let align = bx.const_usize(layout.align.abi.bytes()); + let align = bx.const_usize(layout.align.bytes()); return (size, align); } match t.kind() { @@ -49,7 +49,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // All slice sizes must fit into `isize`, so this multiplication cannot // wrap -- neither signed nor unsigned. bx.unchecked_sumul(info.unwrap(), bx.const_usize(unit.size.bytes())), - bx.const_usize(unit.align.abi.bytes()), + bx.const_usize(unit.align.bytes()), ) } ty::Foreign(_) => { @@ -82,7 +82,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); - let align = bx.const_usize(layout.align.abi.bytes()); + let align = bx.const_usize(layout.align.bytes()); (size, align) } ty::Adt(..) | ty::Tuple(..) => { @@ -94,7 +94,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let i = layout.fields.count() - 1; let unsized_offset_unadjusted = layout.fields.offset(i).bytes(); - let sized_align = layout.align.abi.bytes(); + let sized_align = layout.align.bytes(); debug!( "DST {} offset of dyn field: {}, statically sized align: {}", t, unsized_offset_unadjusted, sized_align diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 74f8a0a7b093c..f0819423aa0fa 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -528,7 +528,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !layout.is_sized() { span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`"); } - let val = layout.align.abi.bytes(); + let val = layout.align.bytes(); ImmTy::from_uint(val, usize_layout()) } OffsetOf(fields) => { diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 9507b24f603eb..9aafc7efd8a6a 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -37,7 +37,7 @@ where debug!( "is_disaligned({:?}) - align = {}, packed = {}; not disaligned", place, - layout.align.abi.bytes(), + layout.align.bytes(), pack.bytes() ); false diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index b1f2959875051..1dea7e4252d7f 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -129,7 +129,7 @@ fn check_validity_requirement_lax<'tcx>( if let Some(pointee) = this.ty.builtin_deref(false) { let pointee = cx.layout_of(pointee)?; // We need to ensure that the LLVM attributes `aligned` and `dereferenceable(size)` are satisfied. - if pointee.align.abi.bytes() > 1 { + if pointee.align.bytes() > 1 { // 0x01-filling is not aligned. return Ok(false); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 81deb35920afb..0458fa1204e81 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -138,7 +138,7 @@ fn is_valid_cmse_inputs<'tcx>( for (index, ty) in fn_sig.inputs().iter().enumerate() { let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?; - let align = layout.layout.align().abi.bytes(); + let align = layout.layout.align().bytes(); let size = layout.layout.size().bytes(); accum += size; diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index e2f09fdcb4b43..a3e9054fdcb8b 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -104,7 +104,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( .expect("failed to build vtable representation"); assert!(layout.is_sized(), "can't create a vtable for an unsized type"); let size = layout.size.bytes(); - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); let ptr_size = tcx.data_layout.pointer_size(); let ptr_align = tcx.data_layout.pointer_align().abi; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 5c984984d3cc3..491e910ff6f84 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -476,7 +476,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { }; let val = match null_op { NullOp::SizeOf if layout.is_sized() => layout.size.bytes(), - NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(), + NullOp::AlignOf if layout.is_sized() => layout.align.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index ebec3d125003d..29f6879aacd0f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -618,7 +618,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let val = match null_op { NullOp::SizeOf => arg_layout.size.bytes(), - NullOp::AlignOf => arg_layout.align.abi.bytes(), + NullOp::AlignOf => arg_layout.align.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index aaacc5866a2ae..5fffba55f17b6 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -609,7 +609,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let op_layout = self.ecx.layout_of(ty).ok()?; let val = match null_op { NullOp::SizeOf => op_layout.size.bytes(), - NullOp::AlignOf => op_layout.align.abi.bytes(), + NullOp::AlignOf => op_layout.align.bytes(), NullOp::OffsetOf(fields) => self .tcx .offset_of_subfield(self.typing_env, op_layout, fields.iter()) diff --git a/compiler/rustc_target/src/callconv/arm.rs b/compiler/rustc_target/src/callconv/arm.rs index 70830fa07b6ea..abc9a404e2ea4 100644 --- a/compiler/rustc_target/src/callconv/arm.rs +++ b/compiler/rustc_target/src/callconv/arm.rs @@ -77,7 +77,7 @@ where } } - let align = arg.layout.align.abi.bytes(); + let align = arg.layout.align.bytes(); let total = arg.layout.size; arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total)); } diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 9213d73e24eac..bc3c9601fa3d3 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -322,7 +322,7 @@ fn classify_arg<'a, Ty, C>( } let total = arg.layout.size; - let align = arg.layout.align.abi.bits(); + let align = arg.layout.align.bits(); // "Scalars wider than 2✕XLEN are passed by reference and are replaced in // the argument list with the address." diff --git a/compiler/rustc_target/src/callconv/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index 44977de7fcbc2..dc32dd87a7e76 100644 --- a/compiler/rustc_target/src/callconv/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs @@ -21,7 +21,7 @@ fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { /// the pass mode used for aggregates in arg and ret position fn classify_aggregate(arg: &mut ArgAbi<'_, Ty>) { - let align_bytes = arg.layout.align.abi.bytes(); + let align_bytes = arg.layout.align.bytes(); let size = arg.layout.size; let reg = match align_bytes { @@ -60,7 +60,7 @@ where // "`extern \"ptx-kernel\"` doesn't allow passing types other than primitives and structs" // ); - let align_bytes = arg.layout.align.abi.bytes(); + let align_bytes = arg.layout.align.bytes(); let unit = match align_bytes { 1 => Reg::i8(), diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 89ec85e4b6664..be1d13816eff7 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -89,7 +89,7 @@ where // Aggregates larger than i64 should be padded at the tail to fill out a whole number // of i64s or i128s, depending on the aggregate alignment. Always use an array for // this, even if there is only a single element. - let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() }; + let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() }; arg.cast_to(Uniform::consecutive( reg, size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()), diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index 161e2c1645f9a..16de3fe070dd4 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -328,7 +328,7 @@ fn classify_arg<'a, Ty, C>( } let total = arg.layout.size; - let align = arg.layout.align.abi.bits(); + let align = arg.layout.align.bits(); // "Scalars wider than 2✕XLEN are passed by reference and are replaced in // the argument list with the address." diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index a73a70a1a0c07..561ee98787dee 100644 --- a/compiler/rustc_target/src/callconv/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs @@ -48,7 +48,7 @@ where } let size = arg.layout.size.bits(); - let needed_align = arg.layout.align.abi.bits(); + let needed_align = arg.layout.align.bits(); let mut must_use_stack = false; // Determine the number of GPRs needed to pass the current argument diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index a02e8ecf613f4..7f626e8c4e886 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -361,7 +361,7 @@ pub(crate) mod rustc { ty::Ref(region, ty, mutability) => { let layout = layout_of(cx, *ty)?; - let referent_align = layout.align.abi.bytes_usize(); + let referent_align = layout.align.bytes_usize(); let referent_size = layout.size.bytes_usize(); Ok(Tree::Ref(Reference { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index f59bc2117d537..317d101dafe05 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -795,7 +795,7 @@ fn variant_info_for_adt<'tcx>( name, offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), type_name: None, } }) @@ -804,7 +804,7 @@ fn variant_info_for_adt<'tcx>( VariantInfo { name: n, kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), + align: layout.align.bytes(), size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, fields: field_info, } @@ -877,7 +877,7 @@ fn variant_info_for_coroutine<'tcx>( name: *name, offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), type_name: None, } }) @@ -905,7 +905,7 @@ fn variant_info_for_coroutine<'tcx>( }), offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), // Include the type name if there is no field name, or if the name is the // __awaitee placeholder symbol which means a child future being `.await`ed. type_name: (field_name.is_none() || field_name == Some(sym::__awaitee)) @@ -946,7 +946,7 @@ fn variant_info_for_coroutine<'tcx>( name: Some(Symbol::intern(&ty::CoroutineArgs::variant_name(variant_idx))), kind: SizeKind::Exact, size: variant_size.bytes(), - align: variant_layout.align.abi.bytes(), + align: variant_layout.align.bytes(), fields, } }) diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 1311ee31182c6..b768269215fa2 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); - if !layout.size.bytes().is_multiple_of(layout.align.abi.bytes()) { + if !layout.size.bytes().is_multiple_of(layout.align.bytes()) { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } if layout.size.bytes() >= tcx.data_layout.obj_size_bound() { @@ -300,8 +300,8 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou if variant.align.abi > layout.align.abi { bug!( "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}", - layout.align.abi.bytes(), - variant.align.abi.bytes(), + layout.align.bytes(), + variant.align.bytes(), ) } // Skip empty variants. diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index d78da9396faf3..7d14ba7fcf132 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -43,8 +43,8 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f expr.span, format!( "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), + from_layout.align.bytes(), + to_layout.align.bytes(), ), ); } diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index 92910cf8adf5d..ff5320719aa28 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -72,7 +72,7 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) cx.tcx .layout_of(cx.typing_env().as_query_input(to_mid_ty)) .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); + let align = u128::from(layout.align.bytes()); u128::from(val) <= align }) } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index d307636e782fc..412640a112c09 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1341,7 +1341,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { name = ecx.tcx.def_path_str(def_id), krate = ecx.tcx.crate_name(def_id.krate), decl_size = extern_decl_layout.size.bytes(), - decl_align = extern_decl_layout.align.abi.bytes(), + decl_align = extern_decl_layout.align.bytes(), shim_size = info.size.bytes(), shim_align = info.align.bytes(), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 523ddad94666b..8be5eaca63b80 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -150,7 +150,7 @@ fn check_size_and_align( ) { let l = eval_goal(ra_fixture, minicore).unwrap(); assert_eq!(l.size.bytes(), size, "size mismatch"); - assert_eq!(l.align.abi.bytes(), align, "align mismatch"); + assert_eq!(l.align.bytes(), align, "align mismatch"); } #[track_caller] @@ -162,7 +162,7 @@ fn check_size_and_align_expr( ) { let l = eval_expr(ra_fixture, minicore).unwrap(); assert_eq!(l.size.bytes(), size, "size mismatch"); - assert_eq!(l.align.abi.bytes(), align, "align mismatch"); + assert_eq!(l.align.bytes(), align, "align mismatch"); } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 3e658cb93ed8a..fc7d97fff4659 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2085,7 +2085,7 @@ impl<'db> Evaluator<'db> { if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) { return Ok(layout .is_sized() - .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))); + .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))); } if let DefWithBodyId::VariantId(f) = locals.body.owner && let Some((AdtId::EnumId(e), _)) = ty.as_adt() @@ -2104,7 +2104,7 @@ impl<'db> Evaluator<'db> { let layout = layout?; Ok(layout .is_sized() - .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))) + .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))) } /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should @@ -2797,7 +2797,7 @@ impl<'db> Evaluator<'db> { )?; // FIXME: there is some leak here let size = layout.size.bytes_usize(); - let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?; + let addr = self.heap_allocate(size, layout.align.bytes() as usize)?; self.write_memory(addr, &result)?; IntervalAndTy { interval: Interval { addr, size }, ty } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index f67778b0f12f3..38480493048ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -767,7 +767,7 @@ impl Evaluator<'_> { "align_of generic arg is not provided".into(), )); }; - let align = self.layout(ty.to_nextsolver(interner))?.align.abi.bytes(); + let align = self.layout(ty.to_nextsolver(interner))?.align.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { @@ -1431,7 +1431,7 @@ impl Evaluator<'_> { field_types.iter().next_back().unwrap().1.clone().substitute(Interner, subst); let sized_part_size = layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); - let sized_part_align = layout.align.abi.bytes() as usize; + let sized_part_align = layout.align.bytes() as usize; let (unsized_part_size, unsized_part_align) = self.size_align_of_unsized(&last_field_ty, metadata, locals)?; let align = sized_part_align.max(unsized_part_align) as isize; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 027a386abe8ce..17767955d474f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6094,7 +6094,7 @@ impl Layout { } pub fn align(&self) -> u64 { - self.0.align.abi.bytes() + self.0.align.bytes() } pub fn niches(&self) -> Option { From 30f284d15befc66143fee59918ffac209a0ae657 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 28 Sep 2025 14:40:39 -0700 Subject: [PATCH 1024/1900] remove explicit deref of AbiAlign for most methods Much of the compiler calls functions on Align projected from AbiAlign. AbiAlign impls Deref to its inner Align, so we can simplify these away. Also, it will minimize disruption when AbiAlign is removed. For now, preserve usages that might resolve to PartialOrd or PartialEq, as those have odd inference. --- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 ++-- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index d78da9396faf3..7d14ba7fcf132 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -43,8 +43,8 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f expr.span, format!( "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), + from_layout.align.bytes(), + to_layout.align.bytes(), ), ); } diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 92910cf8adf5d..ff5320719aa28 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -72,7 +72,7 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) cx.tcx .layout_of(cx.typing_env().as_query_input(to_mid_ty)) .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); + let align = u128::from(layout.align.bytes()); u128::from(val) <= align }) } From b71c4690409e8ce98a502216623a9264efaaa3e4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 28 Sep 2025 18:44:19 +1000 Subject: [PATCH 1025/1900] Fix some crash-test directives --- tests/crashes/125772.rs | 2 +- tests/crashes/131292.rs | 2 +- tests/crashes/34127.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/crashes/125772.rs b/tests/crashes/125772.rs index 2965cfc9e7c3c..2b6cffd9463a9 100644 --- a/tests/crashes/125772.rs +++ b/tests/crashes/125772.rs @@ -1,5 +1,5 @@ //@ known-bug: rust-lang/rust#125772 -//@ only-x86_64 +//@ only-64bit #![feature(generic_const_exprs)] struct Outer(); diff --git a/tests/crashes/131292.rs b/tests/crashes/131292.rs index 01e0eca0bd6d6..05b93d06b0553 100644 --- a/tests/crashes/131292.rs +++ b/tests/crashes/131292.rs @@ -1,5 +1,5 @@ //@ known-bug: #131292 -//@ only-x86_64 +//@ needs-asm-support use std::arch::asm; unsafe fn f6() { diff --git a/tests/crashes/34127.rs b/tests/crashes/34127.rs index ea36b48ecba07..26ebe722475f3 100644 --- a/tests/crashes/34127.rs +++ b/tests/crashes/34127.rs @@ -1,6 +1,6 @@ //@ compile-flags: -g -Copt-level=0 -Z verify-llvm-ir //@ known-bug: #34127 -//@ only-x86_64 +//@ only-64bit pub fn main() { let _a = [(); 1 << 63]; From eceb48534a4fdabfaf378ea02a2c5cd43b8c52a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 29 Sep 2025 03:28:52 +0000 Subject: [PATCH 1026/1900] Make replacement suggestion `_` in type verbose ``` error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/in-signature.rs:6:21 | LL | fn arr_fn() -> [u8; _] { | ^ not allowed in type signatures | help: replace with the correct return type | LL - fn arr_fn() -> [u8; _] { LL + fn arr_fn() -> [u8; 3] { | ``` --- compiler/rustc_hir_analysis/src/collect.rs | 4 +- .../generic_arg_infer/in-signature.stderr | 36 +++-- tests/ui/error-codes/E0121.stderr | 11 +- tests/ui/fn/issue-80179.stderr | 21 ++- tests/ui/fn/suggest-return-closure.stderr | 30 ++-- tests/ui/fn/suggest-return-future.stderr | 22 ++- ...r-return-ty-for-fn-sig-issue-125488.stderr | 44 ++++-- tests/ui/suggestions/return-cycle-2.stderr | 11 +- tests/ui/suggestions/return-cycle.stderr | 11 +- ...uggest-fn-ptr-for-fn-item-in-fn-ret.stderr | 11 +- .../type-alias-impl-trait/issue-77179.stderr | 11 +- tests/ui/typeck/issue-80779.stderr | 22 ++- tests/ui/typeck/issue-98260.stderr | 11 +- .../typeck_type_placeholder_item.stderr | 141 +++++++++++------- .../typeck_type_placeholder_item_help.stderr | 11 +- tests/ui/variance/leaking-unnameables.stderr | 11 +- 16 files changed, 258 insertions(+), 150 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b72e743f95b0f..02baaec37138c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1140,7 +1140,7 @@ fn recover_infer_ret_ty<'tcx>( // recursive function definition to leak out into the fn sig. let mut recovered_ret_ty = None; if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { - diag.span_suggestion( + diag.span_suggestion_verbose( infer_ret_ty.span, "replace with the correct return type", suggestable_ret_ty, @@ -1152,7 +1152,7 @@ fn recover_infer_ret_ty<'tcx>( tcx.param_env(def_id), ret_ty, ) { - diag.span_suggestion( + diag.span_suggestion_verbose( infer_ret_ty.span, "replace with an appropriate return type", sugg, diff --git a/tests/ui/const-generics/generic_arg_infer/in-signature.stderr b/tests/ui/const-generics/generic_arg_infer/in-signature.stderr index b6f2662a93932..d7a7ab52c83de 100644 --- a/tests/ui/const-generics/generic_arg_infer/in-signature.stderr +++ b/tests/ui/const-generics/generic_arg_infer/in-signature.stderr @@ -2,29 +2,39 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/in-signature.rs:6:21 | LL | fn arr_fn() -> [u8; _] { - | -----^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `[u8; 3]` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn arr_fn() -> [u8; _] { +LL + fn arr_fn() -> [u8; 3] { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/in-signature.rs:11:24 | LL | fn ty_fn() -> Bar { - | ---------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `Bar` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn ty_fn() -> Bar { +LL + fn ty_fn() -> Bar { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/in-signature.rs:16:25 | LL | fn ty_fn_mixed() -> Bar<_, _> { - | ----^--^- - | | | | - | | | not allowed in type signatures - | | not allowed in type signatures - | help: replace with the correct return type: `Bar` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn ty_fn_mixed() -> Bar<_, _> { +LL + fn ty_fn_mixed() -> Bar { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/in-signature.rs:21:20 diff --git a/tests/ui/error-codes/E0121.stderr b/tests/ui/error-codes/E0121.stderr index b169373f64399..074929c4e74f9 100644 --- a/tests/ui/error-codes/E0121.stderr +++ b/tests/ui/error-codes/E0121.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/E0121.rs:1:13 | LL | fn foo() -> _ { 5 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `i32` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn foo() -> _ { 5 } +LL + fn foo() -> i32 { 5 } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables --> $DIR/E0121.rs:3:13 diff --git a/tests/ui/fn/issue-80179.stderr b/tests/ui/fn/issue-80179.stderr index f5d6c44db7511..95158da3cff6c 100644 --- a/tests/ui/fn/issue-80179.stderr +++ b/tests/ui/fn/issue-80179.stderr @@ -2,21 +2,26 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-80179.rs:10:24 | LL | fn returns_fn_ptr() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `fn() -> i32` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn returns_fn_ptr() -> _ { +LL + fn returns_fn_ptr() -> fn() -> i32 { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/issue-80179.rs:18:25 | LL | fn returns_closure() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl Fn() -> i32` + | ^ not allowed in type signatures | = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html +help: replace with an appropriate return type + | +LL - fn returns_closure() -> _ { +LL + fn returns_closure() -> impl Fn() -> i32 { + | error: aborting due to 2 previous errors diff --git a/tests/ui/fn/suggest-return-closure.stderr b/tests/ui/fn/suggest-return-closure.stderr index 1860d1ca5d927..0639c23c75ade 100644 --- a/tests/ui/fn/suggest-return-closure.stderr +++ b/tests/ui/fn/suggest-return-closure.stderr @@ -2,34 +2,40 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/suggest-return-closure.rs:1:17 | LL | fn fn_once() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl FnOnce()` + | ^ not allowed in type signatures | = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html +help: replace with an appropriate return type + | +LL - fn fn_once() -> _ { +LL + fn fn_once() -> impl FnOnce() { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/suggest-return-closure.rs:13:16 | LL | fn fn_mut() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl FnMut(char)` + | ^ not allowed in type signatures | = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html +help: replace with an appropriate return type + | +LL - fn fn_mut() -> _ { +LL + fn fn_mut() -> impl FnMut(char) { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/suggest-return-closure.rs:33:13 | LL | fn fun() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl Fn() -> i32` + | ^ not allowed in type signatures | = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html +help: replace with an appropriate return type + | +LL - fn fun() -> _ { +LL + fn fun() -> impl Fn() -> i32 { + | error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable --> $DIR/suggest-return-closure.rs:24:9 diff --git a/tests/ui/fn/suggest-return-future.stderr b/tests/ui/fn/suggest-return-future.stderr index a4c8b5d8c4b5d..7c097e5067132 100644 --- a/tests/ui/fn/suggest-return-future.stderr +++ b/tests/ui/fn/suggest-return-future.stderr @@ -2,19 +2,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/suggest-return-future.rs:7:13 | LL | fn foo() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl Future` + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn foo() -> _ { +LL + fn foo() -> impl Future { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/suggest-return-future.rs:15:13 | LL | fn bar() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl Future` + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn bar() -> _ { +LL + fn bar() -> impl Future { + | error: aborting due to 2 previous errors diff --git a/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr index 8b7c5e1681ad1..839e4265e031c 100644 --- a/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr +++ b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr @@ -2,37 +2,49 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:8:24 | LL | fn f1(s: S<'_>) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `S<'_>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn f1(s: S<'_>) -> _ { +LL + fn f1(s: S<'_>) -> S<'_> { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:13:24 | LL | fn f2(s: S<'_>) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `S<'_>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn f2(s: S<'_>) -> _ { +LL + fn f2(s: S<'_>) -> S<'_> { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:23:24 | LL | fn f3(s: S<'_>) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `S<'_>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn f3(s: S<'_>) -> _ { +LL + fn f3(s: S<'_>) -> S<'_> { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:28:24 | LL | fn f4(s: S<'_>) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `S<'_>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn f4(s: S<'_>) -> _ { +LL + fn f4(s: S<'_>) -> S<'_> { + | error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/return-cycle-2.stderr b/tests/ui/suggestions/return-cycle-2.stderr index 23de2309e8778..e852cd34a72a3 100644 --- a/tests/ui/suggestions/return-cycle-2.stderr +++ b/tests/ui/suggestions/return-cycle-2.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/return-cycle-2.rs:6:34 | LL | fn as_ref(_: i32, _: i32) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `Token<&'static T>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn as_ref(_: i32, _: i32) -> _ { +LL + fn as_ref(_: i32, _: i32) -> Token<&'static T> { + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/return-cycle.stderr b/tests/ui/suggestions/return-cycle.stderr index 604704904410b..cd46c2daa40bc 100644 --- a/tests/ui/suggestions/return-cycle.stderr +++ b/tests/ui/suggestions/return-cycle.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/return-cycle.rs:6:17 | LL | fn new() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `Token<()>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn new() -> _ { +LL + fn new() -> Token<()> { + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr index bf7790e2307e0..9880dd95d8644 100644 --- a/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr +++ b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/suggest-fn-ptr-for-fn-item-in-fn-ret.rs:7:13 | LL | fn bar() -> _ { Wrapper(foo) } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `Wrapper` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn bar() -> _ { Wrapper(foo) } +LL + fn bar() -> Wrapper { Wrapper(foo) } + | error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index c0f197ec48c37..222edfb90a7b3 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -11,10 +11,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-77179.rs:8:22 | LL | fn test() -> Pointer<_> { - | --------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `Pointer` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test() -> Pointer<_> { +LL + fn test() -> Pointer { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/issue-77179.rs:19:25 diff --git a/tests/ui/typeck/issue-80779.stderr b/tests/ui/typeck/issue-80779.stderr index 2261ba616545f..90c80fa2ea6fd 100644 --- a/tests/ui/typeck/issue-80779.stderr +++ b/tests/ui/typeck/issue-80779.stderr @@ -2,19 +2,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-80779.rs:10:28 | LL | pub fn g(_: T<'static>) -> _ {} - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - pub fn g(_: T<'static>) -> _ {} +LL + pub fn g(_: T<'static>) -> () {} + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/issue-80779.rs:5:29 | LL | pub fn f<'a>(val: T<'a>) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - pub fn f<'a>(val: T<'a>) -> _ { +LL + pub fn f<'a>(val: T<'a>) -> () { + | error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-98260.stderr b/tests/ui/typeck/issue-98260.stderr index b7debd335b0fb..f380db55cdffc 100644 --- a/tests/ui/typeck/issue-98260.stderr +++ b/tests/ui/typeck/issue-98260.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-98260.rs:3:27 | LL | fn a(aa: B) -> Result<_, B> { - | -------^---- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `Result<(), B>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn a(aa: B) -> Result<_, B> { +LL + fn a(aa: B) -> Result<(), B> { + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 87750ee6dc140..240dc1ae8ab9e 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -48,20 +48,27 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:7:14 | LL | fn test() -> _ { 5 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `i32` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test() -> _ { 5 } +LL + fn test() -> i32 { 5 } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:10:16 | LL | fn test2() -> (_, _) { (5, 5) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test2() -> (_, _) { (5, 5) } +LL + fn test2() -> (i32, i32) { (5, 5) } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables --> $DIR/typeck_type_placeholder_item.rs:13:15 @@ -189,19 +196,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:47:26 | LL | fn test11(x: &usize) -> &_ { - | -^ - | || - | |not allowed in type signatures - | help: replace with the correct return type: `&&usize` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test11(x: &usize) -> &_ { +LL + fn test11(x: &usize) -> &&usize { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:52:52 | LL | unsafe fn test12(x: *const usize) -> *const *const _ { - | --------------^ - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `*const *const usize` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - unsafe fn test12(x: *const usize) -> *const *const _ { +LL + unsafe fn test12(x: *const usize) -> *const *const usize { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:58:24 @@ -261,20 +274,27 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:80:21 | LL | fn fn_test() -> _ { 5 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `i32` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn fn_test() -> _ { 5 } +LL + fn fn_test() -> i32 { 5 } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:83:23 | LL | fn fn_test2() -> (_, _) { (5, 5) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn fn_test2() -> (_, _) { (5, 5) } +LL + fn fn_test2() -> (i32, i32) { (5, 5) } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables --> $DIR/typeck_type_placeholder_item.rs:86:22 @@ -374,20 +394,27 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:134:30 | LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn fn_test12(x: i32) -> (_, _) { (x, x) } +LL + fn fn_test12(x: i32) -> (i32, i32) { (x, x) } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:137:33 | LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } - | ------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn fn_test13(x: _) -> (i32, _) { (x, x) } +LL + fn fn_test13(x: _) -> (i32, i32) { (x, x) } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:142:31 @@ -528,10 +555,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:226:31 | LL | fn value() -> Option<&'static _> { - | ----------------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `Option<&'static u8>` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn value() -> Option<&'static _> { +LL + fn value() -> Option<&'static u8> { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/typeck_type_placeholder_item.rs:231:17 @@ -549,10 +579,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:235:31 | LL | fn evens_squared(n: usize) -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with an appropriate return type: `impl Iterator` + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn evens_squared(n: usize) -> _ { +LL + fn evens_squared(n: usize) -> impl Iterator { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/typeck_type_placeholder_item.rs:240:10 @@ -570,10 +603,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:40:24 | LL | fn test9(&self) -> _ { () } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test9(&self) -> _ { () } +LL + fn test9(&self) -> () { () } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:43:27 @@ -585,10 +621,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:107:31 | LL | fn fn_test9(&self) -> _ { () } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn fn_test9(&self) -> _ { () } +LL + fn fn_test9(&self) -> () { () } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:110:34 diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr index 2fce00e7a8e9f..3f21ff6d4ec9f 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item_help.rs:4:15 | LL | fn test1() -> _ { Some(42) } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `Option` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - fn test1() -> _ { Some(42) } +LL + fn test1() -> Option { Some(42) } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/typeck_type_placeholder_item_help.rs:7:14 diff --git a/tests/ui/variance/leaking-unnameables.stderr b/tests/ui/variance/leaking-unnameables.stderr index 92afe952801d0..59bdc33040de7 100644 --- a/tests/ui/variance/leaking-unnameables.stderr +++ b/tests/ui/variance/leaking-unnameables.stderr @@ -2,10 +2,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/leaking-unnameables.rs:8:18 | LL | pub fn f() -> _ { - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `fn()` + | ^ not allowed in type signatures + | +help: replace with the correct return type + | +LL - pub fn f() -> _ { +LL + pub fn f() -> fn() { + | error: aborting due to 1 previous error From 50171eb172a9418e8a20ad7d813d256305add5f0 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 29 Sep 2025 04:07:42 +0000 Subject: [PATCH 1027/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to f957826bff7a68b267ce75b1ea56352aed0cca0a. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 8854fb95997bd..1f90d4e5e498a 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -caccb4d0368bd918ef6668af8e13834d07040417 +f957826bff7a68b267ce75b1ea56352aed0cca0a From 2d038a0ed3ab4995ee863f7a54673cc7ef68f816 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 29 Sep 2025 04:53:44 +0000 Subject: [PATCH 1028/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to f957826bff7a68b267ce75b1ea56352aed0cca0a. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2bf64d62060da..1f90d4e5e498a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -848e6746fe03dfd703075c5077312b63877d51d6 +f957826bff7a68b267ce75b1ea56352aed0cca0a From 68a7c250788833305f73f816b284aafa9e62370a Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 27 Sep 2025 20:57:50 +0300 Subject: [PATCH 1029/1900] Use `Iterator::eq` and (dogfood) `eq_by` in compiler and library --- compiler/rustc_ast/src/ast.rs | 3 +-- compiler/rustc_ast/src/lib.rs | 1 + compiler/rustc_ast/src/tokenstream.rs | 4 +--- compiler/rustc_attr_parsing/src/parser.rs | 2 +- compiler/rustc_builtin_macros/src/autodiff.rs | 3 +-- .../src/deriving/coerce_pointee.rs | 15 ++++----------- compiler/rustc_builtin_macros/src/format.rs | 2 +- compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_hir/src/hir.rs | 5 +---- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 4 +--- compiler/rustc_hir_typeck/src/lib.rs | 1 + compiler/rustc_hir_typeck/src/method/suggest.rs | 4 +--- compiler/rustc_parse/src/lib.rs | 1 + .../rustc_parse/src/parser/tokenstream/tests.rs | 2 +- library/alloc/src/collections/btree/map.rs | 2 +- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/lib.rs | 1 + 17 files changed, 20 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3e8fddd9954e2..082d5e88ac754 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -114,8 +114,7 @@ impl PartialEq for Path { impl PartialEq<&[Symbol]> for Path { #[inline] fn eq(&self, names: &&[Symbol]) -> bool { - self.segments.len() == names.len() - && self.segments.iter().zip(names.iter()).all(|(s1, s2)| s1 == s2) + self.segments.iter().eq(*names) } } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index f1951049b4760..5fe218776e539 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -15,6 +15,7 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_order_by)] #![feature(macro_metavar_expr)] #![feature(rustdoc_internals)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index a5d8fbfac612a..4111182c3b7dc 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -48,9 +48,7 @@ impl TokenTree { match (self, other) { (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => { - delim == delim2 - && tts.len() == tts2.len() - && tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b)) + delim == delim2 && tts.iter().eq_by(tts2.iter(), |a, b| a.eq_unspanned(b)) } _ => false, } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 4f903594225e0..3f4f567901575 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -49,7 +49,7 @@ impl<'a> PathParser<'a> { } pub fn segments_is(&self, segments: &[Symbol]) -> bool { - self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b) + self.segments().map(|segment| &segment.name).eq(segments) } pub fn word(&self) -> Option { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index f4a923797e2d3..ddc59bfe1414a 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -377,8 +377,7 @@ mod llvm_enzyme { (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { let a = &a.item.path; let b = &b.item.path; - a.segments.len() == b.segments.len() - && a.segments.iter().zip(b.segments.iter()).all(|(a, b)| a.ident == b.ident) + a.segments.iter().eq_by(&b.segments, |a, b| a.ident == b.ident) } _ => false, } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 75db5d77783eb..5b378de8bbddc 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -356,21 +356,14 @@ fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool { bounds.iter().any(is_maybe_sized_bound) } -fn path_segment_is_exact_match(path_segments: &[ast::PathSegment], syms: &[Symbol]) -> bool { - path_segments.iter().zip(syms).all(|(segment, &symbol)| segment.ident.name == symbol) -} - fn is_sized_marker(path: &ast::Path) -> bool { const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized]; const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized]; - if path.segments.len() == 4 && path.is_global() { - path_segment_is_exact_match(&path.segments[1..], &CORE_UNSIZE) - || path_segment_is_exact_match(&path.segments[1..], &STD_UNSIZE) - } else if path.segments.len() == 3 { - path_segment_is_exact_match(&path.segments, &CORE_UNSIZE) - || path_segment_is_exact_match(&path.segments, &STD_UNSIZE) + let segments = || path.segments.iter().map(|segment| segment.ident.name); + if path.is_global() { + segments().skip(1).eq(CORE_UNSIZE) || segments().skip(1).eq(STD_UNSIZE) } else { - *path == sym::Sized + segments().eq(CORE_UNSIZE) || segments().eq(STD_UNSIZE) || *path == sym::Sized } } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index d70888205a51b..bffc0407e8112 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -768,7 +768,7 @@ fn report_missing_placeholders( if !found_foreign && invalid_refs.is_empty() { // Show example if user didn't use any format specifiers - let show_example = used.iter().all(|used| !used); + let show_example = !used.contains(&true); if !show_example { if unused.len() > 1 { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 4541e2cd3b41d..57cf62ea61212 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -11,6 +11,7 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] +#![feature(iter_order_by)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 493236718a86a..bc1c47e95c3ae 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1298,10 +1298,7 @@ impl AttributeExt for Attribute { #[inline] fn path_matches(&self, name: &[Symbol]) -> bool { match &self { - Attribute::Unparsed(n) => { - n.path.segments.len() == name.len() - && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) - } + Attribute::Unparsed(n) => n.path.segments.iter().map(|ident| &ident.name).eq(name), _ => false, } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 7ca8580e09866..c8943d4634e29 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2803,9 +2803,7 @@ impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> { if let Some((assoc, fn_sig)) = self.similar_assoc(call_name) && fn_sig.inputs()[1..] .iter() - .zip(input_types.iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == input_types.len() + .eq_by(input_types, |expected, found| self.may_coerce(*expected, found)) { let assoc_name = assoc.name(); err.span_suggestion_verbose( diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 43a23822fd1e3..acc0481e457fd 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -5,6 +5,7 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(iter_order_by)] #![feature(never_type)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 024b9ee08c222..44602e6289940 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1914,9 +1914,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(ref args) = call_args && fn_sig.inputs()[1..] .iter() - .zip(args.into_iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == args.len() + .eq_by(args, |expected, found| self.may_coerce(*expected, *found)) { err.span_suggestion_verbose( item_name.span, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 88b67d792deb9..c26c7b9122afa 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -9,6 +9,7 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(iter_order_by)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index 19b2c98f5af82..63177a727449e 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -15,7 +15,7 @@ fn sp(a: u32, b: u32) -> Span { } fn cmp_token_stream(a: &TokenStream, b: &TokenStream) -> bool { - a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.eq_unspanned(y)) + a.iter().eq_by(b.iter(), |x, y| x.eq_unspanned(y)) } #[test] diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 9dfbbd913225a..adcb444d08c66 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2413,7 +2413,7 @@ impl Default for BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for BTreeMap { fn eq(&self, other: &BTreeMap) -> bool { - self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) + self.iter().eq(other) } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c2cf39c4be06e..4eb32585ffb97 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1685,7 +1685,7 @@ impl Type { match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { - a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.is_doc_subtype_of(b, cache)) + a.iter().eq_by(b, |a, b| a.is_doc_subtype_of(b, cache)) } (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cache), (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cache), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c4f24e09ddbfa..d7ffb25f8bd81 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -13,6 +13,7 @@ #![feature(if_let_guard)] #![feature(iter_advance_by)] #![feature(iter_intersperse)] +#![feature(iter_order_by)] #![feature(rustc_private)] #![feature(test)] #![warn(rustc::internal)] From 97333f8c9a0f774cc8d0025bbc51848e1f60427d Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 23 Sep 2025 17:14:51 +0200 Subject: [PATCH 1030/1900] std: implement `hostname` --- library/std/src/net/hostname.rs | 22 +++++++ library/std/src/net/mod.rs | 6 +- library/std/src/sys/net/hostname/mod.rs | 14 +++++ library/std/src/sys/net/hostname/unix.rs | 62 +++++++++++++++++++ .../std/src/sys/net/hostname/unsupported.rs | 6 ++ library/std/src/sys/net/hostname/windows.rs | 24 +++++++ library/std/src/sys/net/mod.rs | 3 + .../std/src/sys/pal/windows/c/bindings.txt | 2 + .../std/src/sys/pal/windows/c/windows_sys.rs | 2 + 9 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 library/std/src/net/hostname.rs create mode 100644 library/std/src/sys/net/hostname/mod.rs create mode 100644 library/std/src/sys/net/hostname/unix.rs create mode 100644 library/std/src/sys/net/hostname/unsupported.rs create mode 100644 library/std/src/sys/net/hostname/windows.rs diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs new file mode 100644 index 0000000000000..b1010cec60058 --- /dev/null +++ b/library/std/src/net/hostname.rs @@ -0,0 +1,22 @@ +use crate::ffi::OsString; + +/// Returns the system hostname. +/// +/// This can error out in platform-specific error cases; +/// for example, uefi and wasm, where hostnames aren't +/// supported. +/// +/// # Underlying system calls +/// +/// | Platform | System call | +/// |----------|---------------------------------------------------------------------------------------------------------| +/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) | +/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) | +/// +/// Note that platform-specific behavior [may change in the future][changes]. +/// +/// [changes]: crate::io#platform-specific-behavior +#[unstable(feature = "gethostname", issue = "135142")] +pub fn hostname() -> crate::io::Result { + crate::sys::net::hostname() +} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 40f1a93e39ded..3e4447eb33f2a 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -1,7 +1,8 @@ //! Networking primitives for TCP/UDP communication. //! //! This module provides networking functionality for the Transmission Control and User -//! Datagram Protocols, as well as types for IP and socket addresses. +//! Datagram Protocols, as well as types for IP and socket addresses and functions related +//! to network properties. //! //! # Organization //! @@ -24,6 +25,8 @@ #[stable(feature = "rust1", since = "1.0.0")] pub use core::net::AddrParseError; +#[unstable(feature = "gethostname", issue = "135142")] +pub use self::hostname::hostname; #[stable(feature = "rust1", since = "1.0.0")] pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] @@ -35,6 +38,7 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; +mod hostname; mod ip_addr; mod socket_addr; mod tcp; diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs new file mode 100644 index 0000000000000..a4b5b76059d30 --- /dev/null +++ b/library/std/src/sys/net/hostname/mod.rs @@ -0,0 +1,14 @@ +cfg_select! { + target_family = "unix" => { + mod unix; + pub use unix::hostname; + } + target_os = "windows" => { + mod windows; + pub use windows::hostname; + } + _ => { + mod unsupported; + pub use unsupported::hostname; + } +} diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs new file mode 100644 index 0000000000000..bc6fa82a38f0d --- /dev/null +++ b/library/std/src/sys/net/hostname/unix.rs @@ -0,0 +1,62 @@ +use crate::ffi::OsString; +use crate::io; +use crate::os::unix::ffi::OsStringExt; +use crate::sys::pal::os::errno; + +pub fn hostname() -> io::Result { + // Query the system for the maximum host name length. + let host_name_max = match unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } { + // If this fails (possibly because there is no maximum length), then + // assume a maximum length of _POSIX_HOST_NAME_MAX (255). + -1 => 255, + max => max as usize, + }; + + // Reserve space for the nul terminator too. + let mut buf = Vec::::try_with_capacity(host_name_max + 1)?; + loop { + // SAFETY: `buf.capacity()` bytes of `buf` are writable. + let r = unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.capacity()) }; + match (r != 0).then(errno) { + None => { + // Unfortunately, the UNIX specification says that the name will + // be truncated if it does not fit in the buffer, without returning + // an error. As additionally, the truncated name may still be null- + // terminated, there is no reliable way to detect truncation. + // Fortunately, most platforms ignore what the specification says + // and return an error (mostly ENAMETOOLONG). Should that not be + // the case, the following detects truncation if the null-terminator + // was omitted. Note that this check does not impact performance at + // all as we need to find the length of the string anyways. + // + // Use `strnlen` as it does not place an initialization requirement + // on the bytes after the nul terminator. + // + // SAFETY: `buf.capacity()` bytes of `buf` are accessible, and are + // initialized up to and including a possible nul terminator. + let len = unsafe { libc::strnlen(buf.as_ptr().cast(), buf.capacity()) }; + if len < buf.capacity() { + // If the string is nul-terminated, we assume that is has not + // been truncated, as the capacity *should be* enough to hold + // `HOST_NAME_MAX` bytes. + // SAFETY: `len + 1` bytes have been initialized (we exclude + // the nul terminator from the string). + unsafe { buf.set_len(len) }; + return Ok(OsString::from_vec(buf)); + } + } + // As `buf.capacity()` is always less than or equal to `isize::MAX` + // (Rust allocations cannot exceed that limit), the only way `EINVAL` + // can be returned is if the system uses `EINVAL` to report that the + // name does not fit in the provided buffer. In that case (or in the + // case of `ENAMETOOLONG`), resize the buffer and try again. + Some(libc::EINVAL | libc::ENAMETOOLONG) => {} + // Other error codes (e.g. EPERM) have nothing to do with the buffer + // size and should be returned to the user. + Some(err) => return Err(io::Error::from_raw_os_error(err)), + } + + // Resize the buffer (according to `Vec`'s resizing rules) and try again. + buf.try_reserve(buf.capacity() + 1)?; + } +} diff --git a/library/std/src/sys/net/hostname/unsupported.rs b/library/std/src/sys/net/hostname/unsupported.rs new file mode 100644 index 0000000000000..d868f68f32dd2 --- /dev/null +++ b/library/std/src/sys/net/hostname/unsupported.rs @@ -0,0 +1,6 @@ +use crate::ffi::OsString; +use crate::io::{Error, Result}; + +pub fn hostname() -> Result { + Err(Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/net/hostname/windows.rs b/library/std/src/sys/net/hostname/windows.rs new file mode 100644 index 0000000000000..24eed100f32d4 --- /dev/null +++ b/library/std/src/sys/net/hostname/windows.rs @@ -0,0 +1,24 @@ +use crate::ffi::OsString; +use crate::io::Result; +use crate::mem::MaybeUninit; +use crate::os::windows::ffi::OsStringExt; +use crate::sys::pal::c; +use crate::sys::pal::winsock::{self, cvt}; + +pub fn hostname() -> Result { + winsock::startup(); + + // The documentation of GetHostNameW says that a buffer size of 256 is + // always enough. + let mut buffer = [const { MaybeUninit::::uninit() }; 256]; + // SAFETY: these parameters specify a valid, writable region of memory. + cvt(unsafe { c::GetHostNameW(buffer.as_mut_ptr().cast(), buffer.len() as i32) })?; + // Use `lstrlenW` here as it does not require the bytes after the nul + // terminator to be initialized. + // SAFETY: if `GetHostNameW` returns successfully, the name is nul-terminated. + let len = unsafe { c::lstrlenW(buffer.as_ptr().cast()) }; + // SAFETY: the length of the name is `len`, hence `len` bytes have been + // initialized by `GetHostNameW`. + let name = unsafe { buffer[..len as usize].assume_init_ref() }; + Ok(OsString::from_wide(name)) +} diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index dffc4ea7f81ad..bfe5cf5312875 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -2,3 +2,6 @@ /// `UdpSocket` as well as related functionality like DNS resolving. mod connection; pub use connection::*; + +mod hostname; +pub use hostname::hostname; diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index abc1c19827fe7..9009aa09f48ed 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2170,6 +2170,7 @@ GetFileType GETFINALPATHNAMEBYHANDLE_FLAGS GetFinalPathNameByHandleW GetFullPathNameW +GetHostNameW GetLastError GetModuleFileNameW GetModuleHandleA @@ -2270,6 +2271,7 @@ LPPROGRESS_ROUTINE LPPROGRESS_ROUTINE_CALLBACK_REASON LPTHREAD_START_ROUTINE LPWSAOVERLAPPED_COMPLETION_ROUTINE +lstrlenW M128A MAX_PATH MAXIMUM_REPARSE_DATA_BUFFER_SIZE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 989a1246650cd..98f277b33780c 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -49,6 +49,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE); windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32); +windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32); windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE); @@ -134,6 +135,7 @@ windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : * windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32); +windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32); windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32); windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32); From 04ee9915897ba66a768440e1aaeaf1e63f14ba02 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 28 Sep 2025 13:16:10 +0100 Subject: [PATCH 1031/1900] Fix removed version of `doc_auto_cfg`, `doc_cfg_hide` --- compiler/rustc_feature/src/removed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 3c51f91331a6a..539d67e0b6bc8 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -102,9 +102,9 @@ declare_features! ( /// Allows deriving traits as per `SmartPointer` specification (removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. - (removed, doc_auto_cfg, "1.58.0", Some(43781), Some("merged into `doc_cfg`"), 138907), + (removed, doc_auto_cfg, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows `#[doc(cfg_hide(...))]`. - (removed, doc_cfg_hide, "1.57.0", Some(43781), Some("merged into `doc_cfg`"), 138907), + (removed, doc_cfg_hide, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.58.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`"), 90420), From 9c97ba700e8d4059f8394efa19e6e9487def7afb Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 29 Sep 2025 11:20:40 +0200 Subject: [PATCH 1032/1900] add joboet to library review rotation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 2d58c616bc278..a04f8d2807235 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1382,6 +1382,7 @@ libs = [ "@tgross35", "@thomcc", "@ibraheemdev", + "@joboet", ] infra-ci = [ "@Mark-Simulacrum", From af8fd78142bf394ac904402adb736f4c2f1efbc6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 29 Sep 2025 11:24:20 +0200 Subject: [PATCH 1033/1900] Emit allocator attributes for allocator shim This emits the same attributes we place on allocator declarations (and allocator definitions using `#[global_allocator]`) on the definitions in the allocator shim as well, making sure that the attributes are not lost if the allocator shim participates in LTO. --- compiler/rustc_codegen_llvm/src/allocator.rs | 30 +++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index abd6312039733..896d6755c7527 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -5,9 +5,10 @@ use rustc_ast::expand::allocator::{ }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; +use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; use crate::attributes::llfn_attrs_from_instance; @@ -59,7 +60,26 @@ pub(crate) unsafe fn codegen( let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - create_wrapper_function(tcx, &cx, &from_name, Some(&to_name), &args, output, false); + let alloc_attr_flag = match method.name { + sym::alloc => CodegenFnAttrFlags::ALLOCATOR, + sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, + sym::realloc => CodegenFnAttrFlags::REALLOCATOR, + sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, + _ => unreachable!("Unknown allocator method!"), + }; + + let mut attrs = CodegenFnAttrs::new(); + attrs.flags |= alloc_attr_flag; + create_wrapper_function( + tcx, + &cx, + &from_name, + Some(&to_name), + &args, + output, + false, + &attrs, + ); } } @@ -72,6 +92,7 @@ pub(crate) unsafe fn codegen( &[usize, usize], // size, align None, true, + &CodegenFnAttrs::new(), ); unsafe { @@ -93,6 +114,7 @@ pub(crate) unsafe fn codegen( &[], None, false, + &CodegenFnAttrs::new(), ); } @@ -139,6 +161,7 @@ fn create_wrapper_function( args: &[&Type], output: Option<&Type>, no_return: bool, + attrs: &CodegenFnAttrs, ) { let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void())); let llfn = declare_simple_fn( @@ -150,8 +173,7 @@ fn create_wrapper_function( ty, ); - let attrs = CodegenFnAttrs::new(); - llfn_attrs_from_instance(cx, tcx, llfn, &attrs, None); + llfn_attrs_from_instance(cx, tcx, llfn, attrs, None); let no_return = if no_return { // -> ! DIFlagNoReturn From 2e63708d39553ed882a20a198cdc0f269bb6cd30 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Sep 2025 13:43:25 +0200 Subject: [PATCH 1034/1900] Improve code comments --- src/librustdoc/clean/types.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index d4f0a196eda8d..e4d33416883cb 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1084,7 +1084,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator let mut changed_auto_active_status = None; - // First we get all `doc(auto_cfg)` attributes. + // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes. for attr in attrs { if let Some(ident) = attr.ident() && ident.name == sym::doc @@ -1146,11 +1146,9 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } } } - // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because - // `doc(cfg())` overrides `cfg()`). } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { - // treat #[target_feature(enable = "feat")] attributes as if they were - // #[doc(cfg(target_feature = "feat"))] attributes as well + // Treat `#[target_feature(enable = "feat")]` attributes as if they were + // `#[doc(cfg(target_feature = "feat"))]` attributes as well. for (feature, _) in features { cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature)); } From f6e223cdb5b848f2eca1ce06aac9f9f1b764abf2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 00:21:27 +0200 Subject: [PATCH 1035/1900] clean-up --- clippy_lints/src/methods/mod.rs | 181 ++++++++---------- .../src/methods/wrong_self_convention.rs | 36 ++-- 2 files changed, 97 insertions(+), 120 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a1cdab9cc491b..195d91c4e66b1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -149,13 +149,12 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; +use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; @@ -4889,13 +4888,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name; - let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let name = impl_item.ident.name; + let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; + let item = cx.tcx.hir_expect_item(parent); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); @@ -4908,9 +4907,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { && method_config.output_type.matches(&sig.decl.output) // in case there is no first arg, since we already have checked the number of arguments // it's should be always true - && first_arg_ty_opt.is_none_or(|first_arg_ty| method_config - .self_kind.matches(cx, self_ty, first_arg_ty) - ) + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) && fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(impl_item) { @@ -4948,21 +4946,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, ); } - } - - // if this impl block implements a trait, lint in trait definition instead - if implements_trait { - return; - } - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { - let ret_ty = return_ty(cx, impl_item.owner_id); - - if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { - return; - } - - if name == sym::new && ret_ty != self_ty { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { span_lint( cx, NEW_RET_NO_SELF, @@ -4978,41 +4969,41 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if let TraitItemKind::Fn(ref sig, _) = item.kind - && sig.decl.implicit_self.has_implicit_self() - && let Some(first_arg_hir_ty) = sig.decl.inputs.first() - && let Some(&first_arg_ty) = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .inputs() - .skip_binder() - .first() - { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name, - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } + if let TraitItemKind::Fn(ref sig, _) = item.kind { + if sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first() + { + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + item.ident.name, + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); + } - if item.ident.name == sym::new - && let TraitItemKind::Fn(_, _) = item.kind - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); + if item.ident.name == sym::new + && let ret_ty = return_ty(cx, item.owner_id) + && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } } } } @@ -5776,36 +5767,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), ]; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -5823,12 +5814,10 @@ impl SelfKind { true } else if let Some(boxed_ty) = ty.boxed_ty() { boxed_ty == parent_ty - } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, args) = ty.kind() { - args.types().next() == Some(parent_ty) - } else { - false - } + } else if let ty::Adt(adt, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) } else { false } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index ad9b3c3645425..249119d549bac 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,6 +1,7 @@ use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; +use itertools::Itertools; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; @@ -61,20 +62,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{this}`").fmt(f), - Self::StartsWith(this) => format!("`{this}*`").fmt(f), - Self::EndsWith(this) => format!("`*{this}`").fmt(f), - Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), + Self::Eq(this) => write!(f, "`{this}`"), + Self::StartsWith(this) => write!(f, "`{this}*`"), + Self::EndsWith(this) => write!(f, "`*{this}`"), + Self::NotEndsWith(this) => write!(f, "`~{this}`"), Self::IsSelfTypeCopy(is_true) => { - format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + write!(f, "`self` type is{} `Copy`", if is_true { "" } else { " not" }) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{negation} implement{s_suffix} a trait").fmt(f) + write!(f, "method{negation} implement{s_suffix} a trait") }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{suffix} a trait item").fmt(f) + write!(f, "method{suffix} a trait item") }, } } @@ -115,18 +116,9 @@ pub(super) fn check<'tcx>( let s = conventions .iter() - .filter_map(|conv| { - if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) - || matches!(conv, Convention::ImplementsTrait(_)) - || matches!(conv, Convention::IsTraitItem(_)) - { - None - } else { - Some(conv.to_string()) - } - }) - .collect::>() - .join(" and "); + .filter(|conv| !(cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))) + .filter(|conv| !matches!(conv, Convention::ImplementsTrait(_) | Convention::IsTraitItem(_))) + .format(" and "); format!("methods with the following characteristics: ({s})") } else { @@ -140,11 +132,7 @@ pub(super) fn check<'tcx>( first_arg_span, format!( "{suggestion} usually take {}", - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") + self_kinds.iter().map(|k| k.description()).format(" or ") ), None, "consider choosing a less ambiguous name", From 26a9a371ee3fa8731815cbff5b397d66457afc4c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 28 Sep 2025 23:46:30 +0200 Subject: [PATCH 1036/1900] clean-up: extract `should_implement_trait` Also put `SelfKind` into `lib.rs`, since it's shared with `wrong_self_convention` -- not `utils.rs`, because it's not really a utility function --- clippy_lints/src/methods/lib.rs | 70 ++++++ clippy_lints/src/methods/mod.rs | 229 +----------------- .../src/methods/should_implement_trait.rs | 165 +++++++++++++ .../src/methods/wrong_self_convention.rs | 2 +- 4 files changed, 247 insertions(+), 219 deletions(-) create mode 100644 clippy_lints/src/methods/lib.rs create mode 100644 clippy_lints/src/methods/should_implement_trait.rs diff --git a/clippy_lints/src/methods/lib.rs b/clippy_lints/src/methods/lib.rs new file mode 100644 index 0000000000000..84038283bcf8f --- /dev/null +++ b/clippy_lints/src/methods/lib.rs @@ -0,0 +1,70 @@ +use clippy_utils::sym; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::Mutability; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum SelfKind { + Value, + Ref, + RefMut, + No, // When we want the first argument type to be different than `Self` +} + +impl SelfKind { + pub(super) fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if ty == parent_ty { + true + } else if let Some(boxed_ty) = ty.boxed_ty() { + boxed_ty == parent_ty + } else if let ty::Adt(adt_def, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt_def.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) + } else { + false + } + } + + fn matches_ref<'a>(cx: &LateContext<'a>, mutability: Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if let ty::Ref(_, t, m) = *ty.kind() { + return m == mutability && t == parent_ty; + } + + let trait_sym = match mutability { + Mutability::Not => sym::AsRef, + Mutability::Mut => sym::AsMut, + }; + + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false; + }; + implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) + } + + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, Mutability::Not, parent_ty, ty) + && !matches_ref(cx, Mutability::Mut, parent_ty, ty) + } + + match self { + Self::Value => matches_value(cx, parent_ty, ty), + Self::Ref => matches_ref(cx, Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), + Self::RefMut => matches_ref(cx, Mutability::Mut, parent_ty, ty), + Self::No => matches_none(cx, parent_ty, ty), + } + } + + #[must_use] + pub(super) fn description(self) -> &'static str { + match self { + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 195d91c4e66b1..8c2948e4a74e6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -55,6 +55,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod lib; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -102,6 +103,7 @@ mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; +mod should_implement_trait; mod single_char_add_str; mod skip_while_next; mod sliced_string_as_bytes; @@ -146,19 +148,18 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; -use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, TraitRef, Ty}; +use rustc_middle::ty::TraitRef; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, kw}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -4888,47 +4889,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { - let name = impl_item.ident.name; let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt - .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } - } + should_implement_trait::check_impl_item(cx, impl_item, self_ty, implements_trait, first_arg_ty_opt, sig); if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api @@ -4938,7 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { { wrong_self_convention::check( cx, - name, + impl_item.ident.name, self_ty, first_arg_ty, first_arg.pat.span, @@ -5711,181 +5682,3 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } - -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - -struct ShouldImplTraitCase { - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - // implicit self kind expected (none, self, &self, ...) - self_kind: SelfKind, - // checks against the output type - output_type: OutType, - // certain methods with explicit lifetimes can't implement the equivalent trait method - lint_explicit_lifetime: bool, -} -impl ShouldImplTraitCase { - const fn new( - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - self_kind: SelfKind, - output_type: OutType, - lint_explicit_lifetime: bool, - ) -> ShouldImplTraitCase { - ShouldImplTraitCase { - trait_name, - method_name, - param_count, - fn_header, - self_kind, - output_type, - lint_explicit_lifetime, - } - } - - fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { - self.lint_explicit_lifetime - || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) - } -} - -#[rustfmt::skip] -const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), -]; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum SelfKind { - Value, - Ref, - RefMut, - No, // When we want the first argument type to be different than `Self` -} - -impl SelfKind { - fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if ty == parent_ty { - true - } else if let Some(boxed_ty) = ty.boxed_ty() { - boxed_ty == parent_ty - } else if let ty::Adt(adt, args) = ty.kind() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) - { - args.types().next() == Some(parent_ty) - } else { - false - } - } - - fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = *ty.kind() { - return m == mutability && t == parent_ty; - } - - let trait_sym = match mutability { - hir::Mutability::Not => sym::AsRef, - hir::Mutability::Mut => sym::AsMut, - }; - - let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { - return false; - }; - implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) - } - - fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - !matches_value(cx, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) - } - - match self { - Self::Value => matches_value(cx, parent_ty, ty), - Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), - Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => matches_none(cx, parent_ty, ty), - } - } - - #[must_use] - fn description(self) -> &'static str { - match self { - Self::Value => "`self` by value", - Self::Ref => "`self` by reference", - Self::RefMut => "`self` by mutable reference", - Self::No => "no `self`", - } - } -} - -#[derive(Clone, Copy)] -enum OutType { - Unit, - Bool, - Any, - Ref, -} - -impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); - match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), - _ => false, - } - } -} - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs new file mode 100644 index 0000000000000..3f6600159a2f0 --- /dev/null +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -0,0 +1,165 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_bool, sym}; +use rustc_abi::ExternAbi; +use rustc_hir as hir; +use rustc_hir::{FnSig, ImplItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Symbol, kw}; + +use super::SHOULD_IMPLEMENT_TRAIT; +use super::lib::SelfKind; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + impl_implements_trait: bool, + first_arg_ty_opt: Option>, + sig: &FnSig<'_>, +) { + // if this impl block implements a trait, lint in trait definition instead + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if impl_item.ident.name == method_config.method_name + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && fn_header_equals(method_config.fn_header, sig.header) + && method_config.lifetime_param_cond(impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); + } + } + } +} + +const FN_HEADER: hir::FnHeader = hir::FnHeader { + safety: hir::HeaderSafety::Normal(hir::Safety::Safe), + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + abi: ExternAbi::Rust, +}; + +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + fn_header: hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + fn_header: hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + +#[rustfmt::skip] +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), +]; + +#[derive(Clone, Copy)] +enum OutType { + Unit, + Bool, + Any, + Ref, +} + +impl OutType { + fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); + match (self, ty) { + (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + _ => false, + } + } +} + +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { + expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness +} diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 249119d549bac..74b297c13621e 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,4 +1,3 @@ -use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; use itertools::Itertools; @@ -8,6 +7,7 @@ use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; +use super::lib::SelfKind; #[rustfmt::skip] const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ From 2d2d143a18249dbdf2709991d95e29e378f0c66c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 01:45:13 +0200 Subject: [PATCH 1037/1900] fix: respect crate's edition --- .../src/methods/should_implement_trait.rs | 66 ++++--- ...tderr => method_list_1.edition2015.stderr} | 30 +-- .../method_list_1.edition2021.stderr | 184 ++++++++++++++++++ tests/ui/should_impl_trait/method_list_1.rs | 3 + .../method_list_2.edition2015.stderr | 172 ++++++++++++++++ .../method_list_2.edition2021.stderr | 184 ++++++++++++++++++ tests/ui/should_impl_trait/method_list_2.rs | 5 +- 7 files changed, 598 insertions(+), 46 deletions(-) rename tests/ui/should_impl_trait/{method_list_1.stderr => method_list_1.edition2015.stderr} (86%) create mode 100644 tests/ui/should_impl_trait/method_list_1.edition2021.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2015.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2021.stderr diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs index 3f6600159a2f0..5f13b8c7e91df 100644 --- a/clippy_lints/src/methods/should_implement_trait.rs +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -5,6 +5,7 @@ use rustc_hir as hir; use rustc_hir::{FnSig, ImplItem}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; +use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; use rustc_span::{Symbol, kw}; use super::SHOULD_IMPLEMENT_TRAIT; @@ -31,6 +32,7 @@ pub(super) fn check_impl_item<'tcx>( .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) && fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() { span_lint_and_help( cx, @@ -69,8 +71,10 @@ struct ShouldImplTraitCase { output_type: OutType, // certain methods with explicit lifetimes can't implement the equivalent trait method lint_explicit_lifetime: bool, + in_prelude_since: Edition, } impl ShouldImplTraitCase { + #[expect(clippy::too_many_arguments)] const fn new( trait_name: &'static str, method_name: Symbol, @@ -79,6 +83,7 @@ impl ShouldImplTraitCase { self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, + in_prelude_since: Edition, ) -> ShouldImplTraitCase { ShouldImplTraitCase { trait_name, @@ -88,6 +93,7 @@ impl ShouldImplTraitCase { self_kind, output_type, lint_explicit_lifetime, + in_prelude_since, } } @@ -106,36 +112,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), ]; #[derive(Clone, Copy)] diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr similarity index 86% rename from tests/ui/should_impl_trait/method_list_1.stderr rename to tests/ui/should_impl_trait/method_list_1.edition2015.stderr index 5609d6a21a360..0312fa8f04fae 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:24:5 + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:30:5 + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> tests/ui/should_impl_trait/method_list_1.rs:36:5 + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> tests/ui/should_impl_trait/method_list_1.rs:42:5 + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> tests/ui/should_impl_trait/method_list_1.rs:48:5 + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> tests/ui/should_impl_trait/method_list_1.rs:54:5 + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> tests/ui/should_impl_trait/method_list_1.rs:60:5 + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:66:5 + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> tests/ui/should_impl_trait/method_list_1.rs:72:5 + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> tests/ui/should_impl_trait/method_list_1.rs:78:5 + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:84:5 + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:90:5 + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:96:5 + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> tests/ui/should_impl_trait/method_list_1.rs:102:5 + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> tests/ui/should_impl_trait/method_list_1.rs:108:5 + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 | LL | / pub fn drop(&mut self) { LL | | diff --git a/tests/ui/should_impl_trait/method_list_1.edition2021.stderr b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr new file mode 100644 index 0000000000000..0312fa8f04fae --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 + | +LL | / pub fn default() -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 + | +LL | / pub fn drop(&mut self) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index e8de0e04c0c4c..bbb04c0c5aa12 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/tests/ui/should_impl_trait/method_list_2.edition2015.stderr b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr new file mode 100644 index 0000000000000..259815908fee0 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr @@ -0,0 +1,172 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.edition2021.stderr b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr new file mode 100644 index 0000000000000..2f90b61e7a176 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:34:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 1f25ab3938a3d..4dfbe7e0f9f4d 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, @@ -29,7 +32,7 @@ impl T { } pub fn from_iter(iter: T) -> Self { - //~^ should_implement_trait + //~[edition2021]^ should_implement_trait unimplemented!() } From 5df0be398845fa8b239f3e722c25b8369498df5c Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Tue, 26 Aug 2025 08:52:54 -0400 Subject: [PATCH 1038/1900] Make macOS dist build configuration match where reasonable --- src/ci/github-actions/jobs.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 8303699ce8a68..4384ec7676975 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -430,7 +430,8 @@ auto: # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.2.app + SELECT_XCODE: /Applications/Xcode_15.4.app + USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos @@ -450,21 +451,24 @@ auto: - name: dist-aarch64-apple env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin + SCRIPT: >- + ./x.py dist bootstrap + --include-default-paths + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc - --set llvm.ninja=false --set rust.lto=thin --set rust.codegen-units=1 - SELECT_XCODE: /Applications/Xcode_15.4.app - USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + SELECT_XCODE: /Applications/Xcode_15.4.app + USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos From 098a56890f25eaca5063988b0b0840fad83db250 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 29 Sep 2025 13:52:33 +0200 Subject: [PATCH 1039/1900] Fn-trait goals, eagerly instantiate binder to avoid overflow from proving `for<'a> opaque<'a>: Sized` --- .../src/solve/assembly/structural_traits.rs | 5 +- .../src/solve/effect_goals.rs | 21 ++- .../src/solve/normalizes_to/mod.rs | 135 ++++++++---------- .../src/solve/trait_goals.rs | 46 +++--- 4 files changed, 90 insertions(+), 117 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index c40739d12e680..9b3dc1f691fb3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -664,7 +664,7 @@ fn coroutine_closure_to_ambiguous_coroutine( pub(in crate::solve) fn extract_fn_def_from_const_callable( cx: I, self_ty: I::Ty, -) -> Result<(ty::Binder, I::FunctionId, I::GenericArgs), NoSolution> { +) -> Result<(ty::Binder, I::FunctionId, I::GenericArgs), NoSolution> { match self_ty.kind() { ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); @@ -673,7 +673,8 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( && cx.fn_is_const(def_id) { Ok(( - sig.instantiate(cx, args).map_bound(|sig| (sig.inputs(), sig.output())), + sig.instantiate(cx, args) + .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), def_id, args, )) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index cb72c1cd92b84..65a5edf6b7250 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -234,12 +234,12 @@ where let self_ty = goal.predicate.self_ty(); let (inputs_and_output, def_id, args) = structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?; + let (inputs, output) = ecx.instantiate_binder_with_infer(inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); let requirements = cx .const_conditions(def_id.into()) .iter_instantiated(cx, args) @@ -251,15 +251,12 @@ where }) .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]); - let pred = inputs_and_output - .map_bound(|(inputs, _)| { - ty::TraitRef::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())], - ) - }) - .to_host_effect_clause(cx, goal.predicate.constness); + let pred = ty::Binder::dummy(ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + )) + .to_host_effect_clause(cx, goal.predicate.constness); Self::probe_and_consider_implied_clause( ecx, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 653c59c5d4241..0674b3d42ab4d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -451,23 +451,22 @@ where return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; + let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), inputs], - ), - term: output.into(), - }) - .upcast(cx); + let pred = ty::ProjectionPredicate { + projection_term: ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + ), + term: output.into(), + } + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, @@ -497,76 +496,56 @@ where goal_kind, env_region, )?; + let AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty, + } = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine); // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( - |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { - ty::TraitRef::new( - cx, - cx.require_trait_lang_item(SolverTraitLangItem::Sized), - [output_ty], - ) - }, + let output_is_sized_pred = ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [output_coroutine_ty], ); - let pred = tupled_inputs_and_output_and_coroutine - .map_bound( - |AsyncCallableRelevantTypes { - tupled_inputs_ty, - output_coroutine_ty, - coroutine_return_ty, - }| { - let (projection_term, term) = if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - output_coroutine_ty.into(), - ) - } else if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ], - ), - output_coroutine_ty.into(), - ) - } else if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - ], - ), - coroutine_return_ty.into(), - ) - } else { - panic!( - "no such associated type in `AsyncFn*`: {:?}", - goal.predicate.def_id() - ) - }; - ty::ProjectionPredicate { projection_term, term } - }, - ) - .upcast(cx); + let (projection_term, term) = + if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + coroutine_return_ty.into(), + ) + } else { + panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) + }; + let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx); Self::probe_and_consider_implied_clause( ecx, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 3974114e9b43c..e790ecd595be7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -369,18 +369,16 @@ where return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; + let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, _)| { - ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) - }) - .upcast(cx); + let pred = + ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), @@ -408,28 +406,26 @@ where // This region doesn't matter because we're throwing away the coroutine type Region::new_static(cx), )?; + let AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty: _, + } = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine); // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( - |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { - ty::TraitRef::new( - cx, - cx.require_trait_lang_item(SolverTraitLangItem::Sized), - [output_coroutine_ty], - ) - }, + let output_is_sized_pred = ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [output_coroutine_ty], ); - let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| { - ty::TraitRef::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), tupled_inputs_ty], - ) - }) - .upcast(cx); + let pred = ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ) + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), From 0f2b79c36dcc4ec66125d6fa87694c7f5d39ebe8 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 29 Sep 2025 13:57:41 +0200 Subject: [PATCH 1040/1900] add tests --- .../opaques/overflow-hr-fn-trait-sized-1.rs | 25 +++++++++++++++++++ .../opaques/overflow-hr-fn-trait-sized-2.rs | 14 +++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-1.rs create mode 100644 tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-2.rs diff --git a/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-1.rs b/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-1.rs new file mode 100644 index 0000000000000..e35e48dfcecbd --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-1.rs @@ -0,0 +1,25 @@ +//@ ignore-compare-mode-next-solver +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#220. Builtin `Fn`-trait +// candidates required `for<'latebound> Output<'latebound>: Sized` which ended +// up resulting in overflow if the return type is an opaque in the defining scope. +// +// We now eagerly instantiate the binder of the function definition which avoids +// that overflow by relating the lifetime of the opaque to something from the +// input. +fn flat_map(_: F, _: G) +where + F: FnOnce(T) -> I, + I: Iterator, + G: Fn(::Item) -> usize, +{ +} + +fn rarw<'a>(_: &'a ()) -> impl Iterator { + flat_map(rarw, |x| x.len()); + std::iter::empty() +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-2.rs b/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-2.rs new file mode 100644 index 0000000000000..1d64e422d8938 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/overflow-hr-fn-trait-sized-2.rs @@ -0,0 +1,14 @@ +//@ ignore-compare-mode-next-solver +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#204, see +// the sibling test for more details. + +fn constrain<'a, F: FnOnce(&'a ())>(_: F) {} +fn foo<'a>(_: &'a ()) -> impl Sized + use<'a> { + constrain(foo); + () +} + +fn main() {} From 07806a1132f156ffad8c9edaed08825cd09fbce5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 29 Sep 2025 13:57:51 +0200 Subject: [PATCH 1041/1900] cleanup `try_evaluate_added_goals` --- .../src/solve/eval_ctxt/mod.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 85110530ae9bc..f25003bbfe92a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -633,28 +633,19 @@ where // the certainty of all the goals. #[instrument(level = "trace", skip(self))] pub(super) fn try_evaluate_added_goals(&mut self) -> Result { - let mut response = Ok(Certainty::overflow(false)); for _ in 0..FIXPOINT_STEP_LIMIT { - // FIXME: This match is a bit ugly, it might be nice to change the inspect - // stuff to use a closure instead. which should hopefully simplify this a bit. match self.evaluate_added_goals_step() { - Ok(Some(cert)) => { - response = Ok(cert); - break; - } Ok(None) => {} + Ok(Some(cert)) => return Ok(cert), Err(NoSolution) => { - response = Err(NoSolution); - break; + self.tainted = Err(NoSolution); + return Err(NoSolution); } } } - if response.is_err() { - self.tainted = Err(NoSolution); - } - - response + debug!("try_evaluate_added_goals: encountered overflow"); + Ok(Certainty::overflow(false)) } /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. From 9f667cdd243d905848ccfc031f4c1373828c9a7d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 29 Sep 2025 10:34:19 -0400 Subject: [PATCH 1042/1900] Add `overlapping_assoc_constraints` param to `lower_bounds` --- .../src/collect/item_bounds.rs | 20 ++++++++++++++++--- .../src/collect/predicates_of.rs | 18 +++++++++++++++-- .../src/hir_ty_lowering/bounds.rs | 4 +++- .../src/hir_ty_lowering/mod.rs | 1 + 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index ba54fa8cc0dbf..9841fafc82c1b 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -12,7 +12,7 @@ use tracing::{debug, instrument}; use super::ItemCtxt; use super::predicates_of::assert_only_contains_predicates_from; -use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; +use crate::hir_ty_lowering::{HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter}; /// For associated types we include both bounds written on the type /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`. @@ -37,7 +37,14 @@ fn associated_type_bounds<'tcx>( let icx = ItemCtxt::new(tcx, assoc_item_def_id); let mut bounds = Vec::new(); - icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); + icx.lowerer().lower_bounds( + item_ty, + hir_bounds, + &mut bounds, + ty::List::empty(), + filter, + OverlappingAsssocItemConstraints::Allowed, + ); match filter { PredicateFilter::All @@ -347,7 +354,14 @@ fn opaque_type_bounds<'tcx>( ty::print::with_reduced_queries!({ let icx = ItemCtxt::new(tcx, opaque_def_id); let mut bounds = Vec::new(); - icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); + icx.lowerer().lower_bounds( + item_ty, + hir_bounds, + &mut bounds, + ty::List::empty(), + filter, + OverlappingAsssocItemConstraints::Allowed, + ); // Implicit bounds are added to opaque types unless a `?Trait` bound is found match filter { PredicateFilter::All diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index dd3590f9ac5dc..ffdf2a2f4c0c8 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -18,7 +18,9 @@ use super::item_bounds::explicit_item_bounds_with_filter; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_predicates_for_delegation_item; -use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason}; +use crate::hir_ty_lowering::{ + HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, +}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus @@ -187,6 +189,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen &mut bounds, ty::List::empty(), PredicateFilter::All, + OverlappingAsssocItemConstraints::Allowed, ); icx.lowerer().add_sizedness_bounds( &mut bounds, @@ -289,6 +292,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen &mut bounds, bound_vars, PredicateFilter::All, + OverlappingAsssocItemConstraints::Allowed, ); predicates.extend(bounds); } @@ -659,7 +663,14 @@ pub(super) fn implied_predicates_with_filter<'tcx>( let self_param_ty = tcx.types.self_param; let mut bounds = Vec::new(); - icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter); + icx.lowerer().lower_bounds( + self_param_ty, + superbounds, + &mut bounds, + ty::List::empty(), + filter, + OverlappingAsssocItemConstraints::Allowed, + ); match filter { PredicateFilter::All | PredicateFilter::SelfOnly @@ -984,6 +995,7 @@ impl<'tcx> ItemCtxt<'tcx> { &mut bounds, bound_vars, filter, + OverlappingAsssocItemConstraints::Allowed, ); } @@ -1063,6 +1075,7 @@ pub(super) fn const_conditions<'tcx>( &mut bounds, bound_vars, PredicateFilter::ConstIfConst, + OverlappingAsssocItemConstraints::Allowed, ); } _ => {} @@ -1083,6 +1096,7 @@ pub(super) fn const_conditions<'tcx>( &mut bounds, ty::List::empty(), PredicateFilter::ConstIfConst, + OverlappingAsssocItemConstraints::Allowed, ); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index a59520f16feb9..8682fdc54942b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -339,6 +339,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, bound_vars: &'tcx ty::List, predicate_filter: PredicateFilter, + overlapping_assoc_constraints: OverlappingAsssocItemConstraints, ) where 'tcx: 'hir, { @@ -363,7 +364,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { param_ty, bounds, predicate_filter, - OverlappingAsssocItemConstraints::Allowed, + overlapping_assoc_constraints, ); } hir::GenericBound::Outlives(lifetime) => { @@ -604,6 +605,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds, projection_ty.bound_vars(), predicate_filter, + OverlappingAsssocItemConstraints::Allowed, ); } PredicateFilter::SelfOnly diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index cc5c0d0ad6ce9..eb660804c2b52 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2497,6 +2497,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &mut bounds, ty::List::empty(), PredicateFilter::All, + OverlappingAsssocItemConstraints::Allowed, ); self.add_sizedness_bounds( &mut bounds, From adf9cbd69ccd9c1e973fe179fbf3e53b23b4a5ae Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:31:54 +0000 Subject: [PATCH 1043/1900] Add a dummy codegen backend This allows building a rustc capable of running the frontend without any backend present. While this may not seem all that useful, it allows running the frontend of rustc to report errors or running miri to interpret a program without any backend present. This is useful when you are trying to say run miri in the browser as upstream LLVM can't be compiled for wasm yet. Or to run rustc itself in miri like I did a while ago and caught some UB. --- compiler/rustc_interface/src/util.rs | 74 ++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 76ccd12797e5e..6a6e64773f353 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -6,13 +7,20 @@ use std::{env, thread}; use rustc_ast as ast; use rustc_attr_parsing::{ShouldEmit, validate_attr}; +use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder; +use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CodegenResults, CrateInfo}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; use rustc_errors::LintBuffer; -use rustc_metadata::{DylibError, load_symbol_from_dylib}; -use rustc_middle::ty::CurrentGcx; -use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; +use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::ty::{CurrentGcx, TyCtxt}; +use rustc_session::config::{ + Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, +}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; use rustc_session::{EarlyDiagCtxt, Session, filesearch, lint}; use rustc_span::edit_distance::find_best_match_for_name; @@ -316,12 +324,13 @@ pub fn get_codegen_backend( let backend = backend_name .or(target.default_codegen_backend.as_deref()) .or(option_env!("CFG_DEFAULT_CODEGEN_BACKEND")) - .unwrap_or("llvm"); + .unwrap_or("dummy"); match backend { filename if filename.contains('.') => { load_backend_from_dylib(early_dcx, filename.as_ref()) } + "dummy" => || Box::new(DummyCodegenBackend), #[cfg(feature = "llvm")] "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new, backend_name => get_codegen_sysroot(early_dcx, sysroot, backend_name), @@ -334,6 +343,63 @@ pub fn get_codegen_backend( unsafe { load() } } +struct DummyCodegenBackend; + +impl CodegenBackend for DummyCodegenBackend { + fn locale_resource(&self) -> &'static str { + "" + } + + fn name(&self) -> &'static str { + "dummy" + } + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + Box::new(CodegenResults { + modules: vec![], + allocator_module: None, + crate_info: CrateInfo::new(tcx, String::new()), + }) + } + + fn join_codegen( + &self, + ongoing_codegen: Box, + _sess: &Session, + _outputs: &OutputFilenames, + ) -> (CodegenResults, FxIndexMap) { + (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default()) + } + + fn link( + &self, + sess: &Session, + codegen_results: CodegenResults, + metadata: EncodedMetadata, + outputs: &OutputFilenames, + ) { + // JUSTIFICATION: TyCtxt no longer available here + #[allow(rustc::bad_opt_access)] + if sess.opts.crate_types.iter().any(|&crate_type| crate_type != CrateType::Rlib) { + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + sess.dcx().fatal(format!( + "crate type {} not supported by the dummy codegen backend", + sess.opts.crate_types[0], + )); + } + + link_binary( + sess, + &ArArchiveBuilderBuilder, + codegen_results, + metadata, + outputs, + self.name(), + ); + } +} + // This is used for rustdoc, but it uses similar machinery to codegen backend // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. From 66b664c9961e54282fbaccef72f90dfa7dd1418f Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Mon, 29 Sep 2025 16:05:44 +0100 Subject: [PATCH 1044/1900] more rename --- compiler/rustc_borrowck/src/lib.rs | 7 +++-- .../src/region_infer/opaque_types/mod.rs | 29 +++++++++++-------- compiler/rustc_borrowck/src/root_cx.rs | 14 ++++----- compiler/rustc_hir_typeck/src/opaque_types.rs | 8 ++--- compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 2 +- 7 files changed, 36 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d799eb1f8c6ef..268cb47fd1261 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -116,7 +116,10 @@ pub fn provide(providers: &mut Providers) { /// Provider for `query mir_borrowck`. Similar to `typeck`, this must /// only be called for typeck roots which will then borrowck all /// nested bodies as well. -fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<&HiddenTypes<'_>, ErrorGuaranteed> { +fn mir_borrowck( + tcx: TyCtxt<'_>, + def: LocalDefId, +) -> Result<&DefinitionSiteHiddenTypes<'_>, ErrorGuaranteed> { assert!(!tcx.is_typeck_child(def.to_def_id())); let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); @@ -127,7 +130,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<&HiddenTypes<'_>, Er Err(guar) } else if input_body.should_skip() { debug!("Skipping borrowck because of injected body"); - let opaque_types = HiddenTypes(Default::default()); + let opaque_types = DefinitionSiteHiddenTypes(Default::default()); Ok(tcx.arena.alloc(opaque_types)) } else { let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 6aa3345d4d423..8d89f3e0d8700 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries}; use rustc_infer::traits::ObligationCause; use rustc_macros::extension; -use rustc_middle::mir::{Body, ConstraintCategory, HiddenTypes}; +use rustc_middle::mir::{Body, ConstraintCategory, DefinitionSiteHiddenTypes}; use rustc_middle::ty::{ self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, @@ -131,7 +131,7 @@ fn nll_var_to_universal_region<'tcx>( /// and errors if we end up with distinct hidden types. fn add_hidden_type<'tcx>( tcx: TyCtxt<'tcx>, - hidden_types: &mut HiddenTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, def_id: LocalDefId, hidden_ty: OpaqueHiddenType<'tcx>, ) { @@ -156,7 +156,7 @@ fn add_hidden_type<'tcx>( } fn get_hidden_type<'tcx>( - hidden_types: &HiddenTypes<'tcx>, + hidden_types: &DefinitionSiteHiddenTypes<'tcx>, def_id: LocalDefId, ) -> Option>> { hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) @@ -183,12 +183,12 @@ struct DefiningUse<'tcx> { /// /// It also means that this whole function is not really soundness critical as we /// recheck all uses of the opaques regardless. -pub(crate) fn compute_hidden_types<'tcx>( +pub(crate) fn compute_definition_site_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, universal_region_relations: &Frozen>, constraints: &MirTypeckRegionConstraints<'tcx>, location_map: Rc, - hidden_types: &mut HiddenTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec> { let mut errors = Vec::new(); @@ -211,14 +211,19 @@ pub(crate) fn compute_hidden_types<'tcx>( // After applying member constraints, we now check whether all member regions ended // up equal to one of their choice regions and compute the actual hidden type of // the opaque type definition. This is stored in the `root_cx`. - compute_hidden_types_from_defining_uses(&rcx, hidden_types, &defining_uses, &mut errors); + compute_definition_site_hidden_types_from_defining_uses( + &rcx, + hidden_types, + &defining_uses, + &mut errors, + ); errors } #[instrument(level = "debug", skip_all, ret)] fn collect_defining_uses<'tcx>( rcx: &mut RegionCtxt<'_, 'tcx>, - hidden_types: &mut HiddenTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], errors: &mut Vec>, ) -> Vec> { @@ -271,9 +276,9 @@ fn collect_defining_uses<'tcx>( defining_uses } -fn compute_hidden_types_from_defining_uses<'tcx>( +fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( rcx: &RegionCtxt<'_, 'tcx>, - hidden_types: &mut HiddenTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, defining_uses: &[DefiningUse<'tcx>], errors: &mut Vec>, ) { @@ -483,14 +488,14 @@ impl<'tcx> FallibleTypeFolder> for ToArgRegionsFolder<'_, 'tcx> { /// /// It does this by equating the hidden type of each use with the instantiated final /// hidden type of the opaque. -pub(crate) fn apply_hidden_types<'tcx>( +pub(crate) fn apply_definition_site_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, universal_regions: &UniversalRegions<'tcx>, region_bound_pairs: &RegionBoundPairs<'tcx>, known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], constraints: &mut MirTypeckRegionConstraints<'tcx>, - hidden_types: &mut HiddenTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec> { let tcx = infcx.tcx; @@ -561,7 +566,7 @@ pub(crate) fn apply_hidden_types<'tcx>( errors } -/// In theory `apply_hidden_types` could introduce new uses of opaque types. +/// In theory `apply_definition_site_hidden_types` could introduce new uses of opaque types. /// We do not check these new uses so this could be unsound. /// /// We detect any new uses and simply delay a bug if they occur. If this results in diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index d9599bbdd4611..21c11e1287353 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -12,12 +12,12 @@ use smallvec::SmallVec; use crate::consumers::BorrowckConsumer; use crate::nll::compute_closure_requirements_modulo_opaques; use crate::region_infer::opaque_types::{ - apply_hidden_types, clone_and_resolve_opaque_types, compute_hidden_types, - detect_opaque_types_added_while_handling_opaque_types, + apply_definition_site_hidden_types, clone_and_resolve_opaque_types, + compute_definition_site_hidden_types, detect_opaque_types_added_while_handling_opaque_types, }; use crate::type_check::{Locations, constraint_conversion}; use crate::{ - ClosureRegionRequirements, CollectRegionConstraintsResult, HiddenTypes, + ClosureRegionRequirements, CollectRegionConstraintsResult, DefinitionSiteHiddenTypes, PropagatedBorrowCheckResults, borrowck_check_region_constraints, borrowck_collect_region_constraints, }; @@ -27,7 +27,7 @@ use crate::{ pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, - hidden_types: HiddenTypes<'tcx>, + hidden_types: DefinitionSiteHiddenTypes<'tcx>, /// The region constraints computed by [borrowck_collect_region_constraints]. This uses /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before /// their parents. @@ -72,7 +72,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars } - pub(super) fn finalize(self) -> Result<&'tcx HiddenTypes<'tcx>, ErrorGuaranteed> { + pub(super) fn finalize(self) -> Result<&'tcx DefinitionSiteHiddenTypes<'tcx>, ErrorGuaranteed> { if let Some(guar) = self.tainted_by_errors { Err(guar) } else { @@ -88,7 +88,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &input.universal_region_relations, &mut input.constraints, ); - input.deferred_opaque_type_errors = compute_hidden_types( + input.deferred_opaque_type_errors = compute_definition_site_hidden_types( &input.infcx, &input.universal_region_relations, &input.constraints, @@ -103,7 +103,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { self.collect_region_constraints_results.values_mut().zip(per_body_info) { if input.deferred_opaque_type_errors.is_empty() { - input.deferred_opaque_type_errors = apply_hidden_types( + input.deferred_opaque_type_errors = apply_definition_site_hidden_types( &input.infcx, &input.body_owned, &input.universal_region_relations.universal_regions, diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index a47fa202acfe3..4c1fe69405e91 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -35,8 +35,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } debug!(?opaque_types); - self.compute_hidden_types(&opaque_types); - self.apply_hidden_types(&opaque_types); + self.compute_definition_site_hidden_types(&opaque_types); + self.apply_definition_site_hidden_types(&opaque_types); } } @@ -71,7 +71,7 @@ impl<'tcx> UsageKind<'tcx> { } impl<'tcx> FnCtxt<'_, 'tcx> { - fn compute_hidden_types( + fn compute_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { @@ -203,7 +203,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { UsageKind::HasDefiningUse } - fn apply_hidden_types( + fn apply_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a63030b114527..feaad5bb96eb9 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -27,7 +27,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_middle::mir::HiddenTypes<'tcx>, + [decode] borrowck_result: rustc_middle::mir::DefinitionSiteHiddenTypes<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, std::sync::Arc, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 791565e387e8d..2e6c9f207e26b 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -87,7 +87,7 @@ impl Debug for CoroutineLayout<'_> { /// All the opaque types that have had their hidden type fully computed. /// Unlike the value in `TypeckResults`, this has unerased regions. #[derive(Default, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct HiddenTypes<'tcx>(pub FxIndexMap>); +pub struct DefinitionSiteHiddenTypes<'tcx>(pub FxIndexMap>); /// The result of the `mir_const_qualif` query. /// diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1efb29c788d49..895c8c0295a04 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1244,7 +1244,7 @@ rustc_queries! { /// Borrow-checks the given typeck root, e.g. functions, const/static items, /// and its children, e.g. closures, inline consts. - query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::HiddenTypes<'tcx>, ErrorGuaranteed> { + query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::DefinitionSiteHiddenTypes<'tcx>, ErrorGuaranteed> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } } From f4a865fea72cbbd1478cada7af19c1bcf0b9d588 Mon Sep 17 00:00:00 2001 From: Oblarg Date: Mon, 29 Sep 2025 11:04:24 -0400 Subject: [PATCH 1045/1900] move test per review feedback --- .../crates/ide/src/hover/tests.rs | 42 ++++++++ .../rust-analyzer/crates/mbe/src/tests.rs | 97 ------------------- 2 files changed, 42 insertions(+), 97 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 1ea11a215f83d..8bc0b3f6ab3b9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -4796,6 +4796,48 @@ fn main() { ); } +#[test] +fn const_generic_negative_literal_macro_expansion() { + // Test that negative literals work correctly in const generics + // when used through macro expansion. This ensures the transcriber + // doesn't wrap negative literals in parentheses, which would create + // invalid syntax like Foo::<(-1)> instead of Foo::<-1>. + check( + r#" +struct Foo { + pub value: i16, +} + +impl Foo { + pub fn new(value: i16) -> Self { + Self { value } + } +} + +macro_rules! create_foo { + ($val:expr) => { + Foo::<$val>::new($val) + }; +} + +fn main() { + let v$0alue = create_foo!(-1); +} +"#, + expect![[r#" + *value* + + ```rust + let value: Foo<-1> + ``` + + --- + + size = 2, align = 2, no Drop + "#]], + ); +} + #[test] fn hover_self_param_shows_type() { check( diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 589e169d30a3d..56034516ef3b2 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -476,100 +476,3 @@ fn minus_belongs_to_literal() { --"#]], ); } - -#[test] -fn negative_literals_in_const_generics() { - // Test that negative literals work correctly in declarative macros - // when used as const generic arguments. The issue was that expressions - // like -1 would be wrapped in parentheses, creating invalid syntax - // Foo::<(-1)> instead of the correct Foo::<-1>. - let decl = r#" -($val:expr) => { - struct Foo { - pub value: i16, - } - - impl Foo { - pub fn new(value: i16) -> Self { - Self { value } - } - } - - Foo::<$val>::new($val) -}; -"#; - let check = |args, expect| check(Edition::CURRENT, Edition::CURRENT, decl, args, expect); - - // Test negative integer literal - should produce Foo::<-1>, not Foo::<(-1)> - check( - "-1", - expect![[r#" - SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024 - IDENT struct 0:Root[0000, 0]@22..28#ROOT2024 - IDENT Foo 0:Root[0000, 0]@29..32#ROOT2024 - PUNCH < [alone] 0:Root[0000, 0]@32..33#ROOT2024 - IDENT const 0:Root[0000, 0]@33..38#ROOT2024 - IDENT I 0:Root[0000, 0]@39..40#ROOT2024 - PUNCH : [alone] 0:Root[0000, 0]@40..41#ROOT2024 - IDENT i16 0:Root[0000, 0]@42..45#ROOT2024 - PUNCH > [alone] 0:Root[0000, 0]@45..46#ROOT2024 - SUBTREE {} 0:Root[0000, 0]@47..48#ROOT2024 0:Root[0000, 0]@77..78#ROOT2024 - IDENT pub 0:Root[0000, 0]@57..60#ROOT2024 - IDENT value 0:Root[0000, 0]@61..66#ROOT2024 - PUNCH : [alone] 0:Root[0000, 0]@66..67#ROOT2024 - IDENT i16 0:Root[0000, 0]@68..71#ROOT2024 - PUNCH , [alone] 0:Root[0000, 0]@71..72#ROOT2024 - IDENT impl 0:Root[0000, 0]@84..88#ROOT2024 - PUNCH < [alone] 0:Root[0000, 0]@88..89#ROOT2024 - IDENT const 0:Root[0000, 0]@89..94#ROOT2024 - IDENT I 0:Root[0000, 0]@95..96#ROOT2024 - PUNCH : [alone] 0:Root[0000, 0]@96..97#ROOT2024 - IDENT i16 0:Root[0000, 0]@98..101#ROOT2024 - PUNCH > [alone] 0:Root[0000, 0]@101..102#ROOT2024 - IDENT Foo 0:Root[0000, 0]@103..106#ROOT2024 - PUNCH < [alone] 0:Root[0000, 0]@106..107#ROOT2024 - IDENT I 0:Root[0000, 0]@107..108#ROOT2024 - PUNCH > [alone] 0:Root[0000, 0]@108..109#ROOT2024 - SUBTREE {} 0:Root[0000, 0]@110..111#ROOT2024 0:Root[0000, 0]@194..195#ROOT2024 - IDENT pub 0:Root[0000, 0]@120..123#ROOT2024 - IDENT fn 0:Root[0000, 0]@124..126#ROOT2024 - IDENT new 0:Root[0000, 0]@127..130#ROOT2024 - SUBTREE () 0:Root[0000, 0]@130..131#ROOT2024 0:Root[0000, 0]@141..142#ROOT2024 - IDENT value 0:Root[0000, 0]@131..136#ROOT2024 - PUNCH : [alone] 0:Root[0000, 0]@136..137#ROOT2024 - IDENT i16 0:Root[0000, 0]@138..141#ROOT2024 - PUNCH - [joint] 0:Root[0000, 0]@143..144#ROOT2024 - PUNCH > [alone] 0:Root[0000, 0]@144..145#ROOT2024 - IDENT Self 0:Root[0000, 0]@146..150#ROOT2024 - SUBTREE {} 0:Root[0000, 0]@151..152#ROOT2024 0:Root[0000, 0]@188..189#ROOT2024 - IDENT Self 0:Root[0000, 0]@165..169#ROOT2024 - SUBTREE {} 0:Root[0000, 0]@170..171#ROOT2024 0:Root[0000, 0]@178..179#ROOT2024 - IDENT value 0:Root[0000, 0]@172..177#ROOT2024 - IDENT Foo 0:Root[0000, 0]@201..204#ROOT2024 - PUNCH : [joint] 0:Root[0000, 0]@204..205#ROOT2024 - PUNCH : [joint] 0:Root[0000, 0]@205..206#ROOT2024 - PUNCH < [joint] 0:Root[0000, 0]@206..207#ROOT2024 - PUNCH - [alone] 1:Root[0000, 0]@0..1#ROOT2024 - LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024 - PUNCH > [joint] 0:Root[0000, 0]@211..212#ROOT2024 - PUNCH : [joint] 0:Root[0000, 0]@212..213#ROOT2024 - PUNCH : [alone] 0:Root[0000, 0]@213..214#ROOT2024 - IDENT new 0:Root[0000, 0]@214..217#ROOT2024 - SUBTREE () 0:Root[0000, 0]@217..218#ROOT2024 0:Root[0000, 0]@222..223#ROOT2024 - PUNCH - [alone] 1:Root[0000, 0]@0..1#ROOT2024 - LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024 - - struct Foo{ - pub value:i16, - } - impl Foo{ - pub fn new(value:i16) -> Self { - Self { - value - } - } - - } - Foo::<-1>::new(-1)"#]], - ); -} From 1a16755ea02e36828b1a235c3051a8f8341a741d Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Mon, 29 Sep 2025 15:55:38 +0000 Subject: [PATCH 1046/1900] flatten conditional block --- .../rustc_hir_analysis/src/constrained_generic_params.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 44d7f3a5e8bc2..6bcf06399e06e 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -209,11 +209,8 @@ pub(crate) fn setup_constraining_predicates<'tcx>( // `<::Baz as Iterator>::Output = ::Output` // then the projection only applies if `T` is known, but it still // does not determine `U`. + parameters_for(tcx, projection.projection_term, true).iter().all(|p| input_parameters.contains(p)) { - let inputs = parameters_for(tcx, projection.projection_term, true); - let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); - relies_only_on_inputs - } { input_parameters.extend(parameters_for(tcx, projection.term, false)); predicates.swap(i, j); From 06a6dcd4d276826a7600302c08a0d448e23c1d33 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Sep 2025 14:27:21 +0200 Subject: [PATCH 1047/1900] Move doc cfg propagation pass before items stripping passes --- src/librustdoc/passes/mod.rs | 4 ++-- tests/rustdoc-ui/issues/issue-91713.stdout | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 475d05b7d0e76..f45df8d2d0d5c 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -77,11 +77,11 @@ pub(crate) enum Condition { pub(crate) const PASSES: &[Pass] = &[ CHECK_DOC_CFG, CHECK_DOC_TEST_VISIBILITY, + PROPAGATE_DOC_CFG, STRIP_ALIASED_NON_LOCAL, STRIP_HIDDEN, STRIP_PRIVATE, STRIP_PRIV_IMPORTS, - PROPAGATE_DOC_CFG, PROPAGATE_STABILITY, COLLECT_INTRA_DOC_LINKS, COLLECT_TRAIT_IMPLS, @@ -94,11 +94,11 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::always(COLLECT_TRAIT_IMPLS), ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY), ConditionalPass::always(CHECK_DOC_CFG), + ConditionalPass::always(COLLECT_INTRA_DOC_LINKS), ConditionalPass::always(STRIP_ALIASED_NON_LOCAL), ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate), ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate), - ConditionalPass::always(COLLECT_INTRA_DOC_LINKS), ConditionalPass::always(PROPAGATE_DOC_CFG), ConditionalPass::always(PROPAGATE_STABILITY), ConditionalPass::always(RUN_LINTS), diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout index 30aadfe89f424..d34714be6c942 100644 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ b/tests/rustdoc-ui/issues/issue-91713.stdout @@ -1,11 +1,11 @@ Available passes for running rustdoc: check-doc-cfg - checks `#[doc(cfg(...))]` for stability feature and unexpected cfgs check_doc_test_visibility - run various visibility-related lints on doctests + propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items strip-aliased-non-local - strips all non-local private aliased items from the output strip-hidden - strips all `#[doc(hidden)]` items from the output strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate - propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items propagate-stability - propagates stability to child items collect-intra-doc-links - resolves intra-doc links collect-trait-impls - retrieves trait impls for items in the crate @@ -16,11 +16,11 @@ Default passes for rustdoc: collect-trait-impls check_doc_test_visibility check-doc-cfg +collect-intra-doc-links strip-aliased-non-local strip-hidden (when not --document-hidden-items) strip-private (when not --document-private-items) strip-priv-imports (when --document-private-items) -collect-intra-doc-links propagate-doc-cfg propagate-stability run-lints From 9119eba24de69902ba421151691d7a294d96fa04 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Sep 2025 14:31:21 +0200 Subject: [PATCH 1048/1900] Add regression test for doc cfg applied on public items inside private items --- tests/rustdoc/doc-auto-cfg-public-in-private.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/rustdoc/doc-auto-cfg-public-in-private.rs diff --git a/tests/rustdoc/doc-auto-cfg-public-in-private.rs b/tests/rustdoc/doc-auto-cfg-public-in-private.rs new file mode 100644 index 0000000000000..b78e3f1b932c2 --- /dev/null +++ b/tests/rustdoc/doc-auto-cfg-public-in-private.rs @@ -0,0 +1,16 @@ +// This test ensures that even though private items are removed from generated docs, +// their `cfg`s will still impact their child items. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +pub struct X; + +#[cfg(not(feature = "blob"))] +fn foo() { + impl X { + //@ has 'foo/struct.X.html' + //@ has - '//*[@class="stab portability"]' 'Available on non-crate feature blob only.' + pub fn bar() {} + } +} From 92e65dc4d485e464204346557c228374a8828f7c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 29 Sep 2025 18:39:39 +0200 Subject: [PATCH 1049/1900] how-to-build-and-run.md: replace "rm build" with "x clean --all" --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 36610f28854b5..f8ee9629f0474 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -187,7 +187,7 @@ Alternatively, you can write `bootstrap.toml` by hand. See `bootstrap.example.to settings and explanations of them. See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to -execute `rm -rf build` for subsequent configuration changes to take effect. Note that `./x +execute `./x clean --all` for subsequent configuration changes to take effect. Note that `./x clean` will not cause a rebuild of LLVM. ## Common `x` commands From eaeca967785ef51145016839bb7f8ceec876ae1c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 29 Sep 2025 18:58:03 +0200 Subject: [PATCH 1050/1900] how-to-build-and-run.md: update "building compiler" section --- .../src/building/how-to-build-and-run.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 36610f28854b5..fb4da9c187eaf 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -226,16 +226,17 @@ Once you've created a `bootstrap.toml`, you are now ready to run `x`. There are a lot of options here, but let's start with what is probably the best "go to" command for building a local compiler: -```bash -./x build library +```console +./x build rustc ``` -This may *look* like it only builds the standard library, but that is not the case. -What this command does is the following: +What this command does is build `rustc` using the stage0 compiler and stage0 `std`. + +To build `rustc` with the in-tree `std`, use this command instead: -- Build `rustc` using the stage0 compiler - - This produces the stage1 compiler -- Build `std` using the stage1 compiler +```console +./x build rustc --stage 2 +``` This final product (stage1 compiler + libs built using that compiler) is what you need to build other Rust programs (unless you use `#![no_std]` or @@ -253,7 +254,7 @@ signature of some function, you can use `./x check` instead for a much faster bu Note that this whole command just gives you a subset of the full `rustc` build. The **full** `rustc` build (what you get with `./x build ---stage 2 compiler/rustc`) has quite a few more steps: +--stage 2 rustc`) has quite a few more steps: - Build `rustc` with the stage1 compiler. - The resulting compiler here is called the "stage2" compiler, which uses stage1 std from the previous command. From 8b38760176abe1f3f6db5dd10e469158f82830f0 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 1051/1900] Implement Pointer conversions to and from GenMC. Limitation: Borrow tracking must be disabled. Remove hacks for keeping all memory allocations in GenMC mode. --- src/tools/miri/genmc-sys/build.rs | 16 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 6 +- .../cpp/src/MiriInterface/EventHandling.cpp | 5 +- .../cpp/src/MiriInterface/Exploration.cpp | 4 +- .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 18 +- src/tools/miri/genmc-sys/src/lib.rs | 20 +- src/tools/miri/src/alloc_addresses/mod.rs | 27 +-- .../miri/src/concurrency/genmc/helper.rs | 54 +++-- src/tools/miri/src/concurrency/genmc/mod.rs | 46 ++-- .../miri/src/concurrency/genmc/scheduling.rs | 8 + src/tools/miri/src/shims/native_lib/mod.rs | 8 +- .../fail/atomics/atomic_ptr_double_free.rs | 47 ++++ .../atomics/atomic_ptr_double_free.stderr | 39 ++++ .../atomic_ptr_invalid_provenance.make.stderr | 21 ++ .../atomics/atomic_ptr_invalid_provenance.rs | 61 ++++++ .../atomic_ptr_invalid_provenance.send.stderr | 21 ++ .../atomic_ptr_alloc_race.dealloc.stderr | 24 +++ .../fail/data_race/atomic_ptr_alloc_race.rs | 51 +++++ .../atomic_ptr_alloc_race.write.stderr | 22 ++ .../atomic_ptr_dealloc_write_race.rs | 42 ++++ .../atomic_ptr_dealloc_write_race.stderr | 32 +++ .../atomic_ptr_write_dealloc_race.rs | 50 +++++ .../atomic_ptr_write_dealloc_race.stderr | 22 ++ .../genmc/pass/atomics/atomic_ptr_ops.rs | 173 +++++++++++++++ .../genmc/pass/atomics/atomic_ptr_ops.stderr | 2 + .../pass/atomics/atomic_ptr_roundtrip.rs | 60 ++++++ .../pass/atomics/atomic_ptr_roundtrip.stderr | 2 + .../ms_queue_dynamic.default_R1W1.stderr | 3 + .../ms_queue_dynamic.default_R1W2.stderr | 3 + .../pass/data-structures/ms_queue_dynamic.rs | 200 ++++++++++++++++++ ..._queue_dynamic.spinloop_assume_R1W1.stderr | 3 + ..._queue_dynamic.spinloop_assume_R1W2.stderr | 5 + .../treiber_stack_dynamic.default_R1W1.stderr | 3 + .../treiber_stack_dynamic.default_R1W2.stderr | 3 + .../treiber_stack_dynamic.default_R1W3.stderr | 3 + .../data-structures/treiber_stack_dynamic.rs | 150 +++++++++++++ ..._stack_dynamic.spinloop_assume_R1W1.stderr | 3 + ..._stack_dynamic.spinloop_assume_R1W2.stderr | 5 + ..._stack_dynamic.spinloop_assume_R1W3.stderr | 5 + .../miri/tests/genmc/pass/litmus/IRIWish.rs | 2 +- .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 2 +- .../miri/tests/genmc/pass/litmus/Z6_U.rs | 3 +- .../tests/genmc/pass/thread/thread_locals.rs | 47 ++++ .../genmc/pass/thread/thread_locals.stderr | 2 + 44 files changed, 1235 insertions(+), 88 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr create mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.rs create mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 8d437c20a0926..ef49be151bf09 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -28,7 +28,7 @@ mod downloading { /// The GenMC repository the we get our commit from. pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; + pub(crate) const GENMC_COMMIT: &str = "cd01c12032bdd71df742b41c7817f99acc72e7ab"; /// Ensure that a local GenMC repo is present and set to the correct commit. /// Return the path of the GenMC repo and whether the checked out commit was changed. @@ -227,12 +227,16 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); + // These are all the C++ files we need to compile, which needs to be updated if more C++ files are added to Miri. + // We use absolute paths since relative paths can confuse IDEs when attempting to go-to-source on a path in a compiler error. + let cpp_files_base_path = Path::new("cpp/src/"); let cpp_files = [ - "./cpp/src/MiriInterface/EventHandling.cpp", - "./cpp/src/MiriInterface/Exploration.cpp", - "./cpp/src/MiriInterface/Setup.cpp", - "./cpp/src/MiriInterface/ThreadManagement.cpp", - ]; + "MiriInterface/EventHandling.cpp", + "MiriInterface/Exploration.cpp", + "MiriInterface/Setup.cpp", + "MiriInterface/ThreadManagement.cpp", + ] + .map(|file| std::path::absolute(cpp_files_base_path.join(file)).unwrap()); let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 8972341d491df..5f73e5d3d3d06 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -126,7 +126,7 @@ struct MiriGenmcShim : private GenMCDriver { /**** Memory (de)allocation ****/ auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; - void handle_free(ThreadId thread_id, uint64_t address); + auto handle_free(ThreadId thread_id, uint64_t address) -> bool; /**** Thread management ****/ void handle_thread_create(ThreadId thread_id, ThreadId parent_id); @@ -257,6 +257,7 @@ namespace GenmcScalarExt { inline GenmcScalar uninit() { return GenmcScalar { .value = 0, + .extra = 0, .is_init = false, }; } @@ -264,13 +265,14 @@ inline GenmcScalar uninit() { inline GenmcScalar from_sval(SVal sval) { return GenmcScalar { .value = sval.get(), + .extra = sval.getExtra(), .is_init = true, }; } inline SVal to_sval(GenmcScalar scalar) { ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); - return SVal(scalar.value); + return SVal(scalar.value, scalar.extra); } } // namespace GenmcScalarExt diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index fac7217ea5c8b..c9e7f7a1a8a1c 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -250,8 +250,9 @@ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t al return ret_val.get(); } -void MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) { +auto MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) -> bool { const auto pos = inc_pos(thread_id); GenMCDriver::handleFree(pos, SAddr(address), EventDeps()); - // FIXME(genmc): add error handling once GenMC returns errors from `handleFree` + // FIXME(genmc): use returned error from `handleFree` once implemented in GenMC. + return getResult().status.has_value(); } diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp index 5e7188f17e0d2..0f64083ddda68 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -24,8 +24,10 @@ auto MiriGenmcShim::schedule_next( if (const auto result = GenMCDriver::scheduleNext(threads_action_)) return SchedulingResult { ExecutionState::Ok, static_cast(result.value()) }; - if (GenMCDriver::isExecutionBlocked()) + if (getExec().getGraph().isBlocked()) return SchedulingResult { ExecutionState::Blocked, 0 }; + if (getResult().status.has_value()) // the "value" here is a `VerificationError` + return SchedulingResult { ExecutionState::Error, 0 }; return SchedulingResult { ExecutionState::Finished, 0 }; } diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index af13f0d07746e..5455b1a8de7f8 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -170,9 +170,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // From a Miri perspective, this API doesn't work very well: most memory starts out // "uninitialized"; // only statics have an initial value. And their initial value is just a sequence of bytes, - // but GenMC - // expect this to be already split into separate atomic variables. So we return a dummy - // value. + // but GenMC expect this to be already split into separate atomic variables. So we return a + // dummy value. // This value should never be visible to the interpreted program. // GenMC does not understand uninitialized memory the same way Miri does, which may cause // this function to be called. The returned value can be visible to Miri or the user: @@ -183,13 +182,14 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Currently, atomic loads can see this value, unless initialized by an *atomic* store. // FIXME(genmc): update this comment once mixed atomic-non-atomic support is added. // - // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the - // initial value getter would return an `optional`, since the memory location may be - // uninitialized. + // FIXME(genmc): implement proper support for uninitialized memory in GenMC. + // Ideally, the initial value getter would return an `optional`, since the memory + // location may be uninitialized. .initValGetter = [](const AAccess& a) { return SVal(0xDEAD); }, - // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in - // that case. This should no longer be required with proper mixed-size access support. - .skipUninitLoadChecks = [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in that + // case. This should no longer be required with proper mixed-size access support. + .skipUninitLoadChecks = [](const MemAccessLabel* access_label + ) { return access_label->getOrdering() == MemOrdering::NotAtomic; }, }; driver->setInterpCallbacks(std::move(interpreter_callbacks)); diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 385afb9eaba00..619d4ab67b2f2 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -45,10 +45,14 @@ pub fn create_genmc_driver_handle( } impl GenmcScalar { - pub const UNINIT: Self = Self { value: 0, is_init: false }; + pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; pub const fn from_u64(value: u64) -> Self { - Self { value, is_init: true } + Self { value, extra: 0, is_init: true } + } + + pub const fn has_provenance(&self) -> bool { + self.extra != 0 } } @@ -162,10 +166,16 @@ mod ffi { } /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. - /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. #[derive(Debug, Clone, Copy)] struct GenmcScalar { + /// The raw byte-level value (discarding provenance, if any) of this scalar. value: u64, + /// This is zero for integer values. For pointers, this encodes the provenance by + /// storing the base address of the allocation that this pointer belongs to. + /// Operations on `SVal` in GenMC (e.g., `fetch_add`) preserve the `extra` of the left argument (`left.fetch_add(right, ...)`). + extra: u64, + /// Indicates whether this value is initialized. If this is `false`, the other fields do not matter. + /// (Ideally we'd use `std::optional` but CXX does not support that.) is_init: bool, } @@ -173,6 +183,7 @@ mod ffi { #[derive(Debug, Clone, Copy)] enum ExecutionState { Ok, + Error, Blocked, Finished, } @@ -406,7 +417,8 @@ mod ffi { size: u64, alignment: u64, ) -> u64; - fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64); + /// Returns true if an error was found. + fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64) -> bool; /**** Thread management ****/ fn handle_thread_create(self: Pin<&mut MiriGenmcShim>, thread_id: i32, parent_id: i32); diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index f011ee717850d..7ad4782cd2c6b 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -207,13 +207,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Returns the `AllocId` that corresponds to the specified addr, // or `None` if the addr is out of bounds. - // Setting `only_exposed_allocations` selects whether only exposed allocations are considered. - fn alloc_id_from_addr( - &self, - addr: u64, - size: i64, - only_exposed_allocations: bool, - ) -> Option { + fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option { let this = self.eval_context_ref(); let global_state = this.machine.alloc_addresses.borrow(); assert!(global_state.provenance_mode != ProvenanceMode::Strict); @@ -242,13 +236,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } }?; - // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations - if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { + // We only use this provenance if it has been exposed. + if global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - // In GenMC mode, we keep all allocations, so this check doesn't apply there. - if this.machine.data_race.as_genmc_ref().is_none() { - debug_assert!(this.is_alloc_live(alloc_id)); - } + debug_assert!(this.is_alloc_live(alloc_id)); Some(alloc_id) } else { None @@ -443,8 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { alloc_id } else { // A wildcard pointer. - let only_exposed_allocations = true; - this.alloc_id_from_addr(addr.bytes(), size, only_exposed_allocations)? + this.alloc_id_from_addr(addr.bytes(), size)? }; // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer @@ -465,13 +455,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> MiriMachine<'tcx> { pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { - // In GenMC mode, we can't remove dead allocation info since such pointers can - // still be stored in atomics and we need this info to convert GenMC pointers to Miri pointers. - // `global_state.reuse` is also unused so we can just skip this entire function. - if self.data_race.as_genmc_ref().is_some() { - return; - } - let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index 48a5ec8bb2608..ebbef23a2a54c 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -5,16 +5,18 @@ use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; +use rustc_middle::mir::interpret; use rustc_middle::ty::ScalarInt; use rustc_span::Span; use tracing::debug; use super::GenmcScalar; -use crate::diagnostics::EvalContextExt; +use crate::alloc_addresses::EvalContextExt as _; +use crate::diagnostics::EvalContextExt as _; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, InterpCx, MiriInterpCx, - MiriMachine, NonHaltingDiagnostic, Scalar, throw_unsup_format, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BorTag, GenmcCtx, InterpCx, + MiriInterpCx, MiriMachine, NonHaltingDiagnostic, Scalar, machine, throw_unsup_format, }; /// Maximum size memory access in bytes that GenMC supports. @@ -80,19 +82,30 @@ pub fn split_access(address: Size, size: Size) -> impl Iterator( - _ecx: &MiriInterpCx<'tcx>, + ecx: &MiriInterpCx<'tcx>, + genmc_ctx: &GenmcCtx, scalar: Scalar, ) -> InterpResult<'tcx, GenmcScalar> { interp_ok(match scalar { rustc_const_eval::interpret::Scalar::Int(scalar_int) => { // FIXME(genmc): Add u128 support once GenMC supports it. let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); - GenmcScalar { value, is_init: true } + GenmcScalar { value, extra: 0, is_init: true } + } + rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => { + // FIXME(genmc,borrow tracking): Borrow tracking information is lost. + let addr = crate::Pointer::from(pointer).addr(); + if let crate::Provenance::Wildcard = pointer.provenance { + throw_unsup_format!("Pointers with wildcard provenance not allowed in GenMC mode"); + } + let (alloc_id, _size, _prov_extra) = + rustc_const_eval::interpret::Machine::ptr_get_alloc(ecx, pointer, size.into()) + .unwrap(); + let base_addr = ecx.addr_from_alloc_id(alloc_id, None)?; + // Add the base_addr alloc_id pair to the map. + genmc_ctx.exec_state.genmc_shared_allocs_map.borrow_mut().insert(base_addr, alloc_id); + GenmcScalar { value: addr.bytes(), extra: base_addr, is_init: true } } - rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) => - throw_unsup_format!( - "FIXME(genmc): Implement sending pointers (with provenance) to GenMC." - ), }) } @@ -101,16 +114,25 @@ pub fn scalar_to_genmc_scalar<'tcx>( /// Convert a `GenmcScalar` back into a Miri `Scalar`. /// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. pub fn genmc_scalar_to_scalar<'tcx>( - _ecx: &MiriInterpCx<'tcx>, + ecx: &MiriInterpCx<'tcx>, + genmc_ctx: &GenmcCtx, scalar: GenmcScalar, size: Size, ) -> InterpResult<'tcx, Scalar> { - // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion. - - // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. - // FIXME(genmc): GenMC should be doing the truncation, not Miri. - let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); - interp_ok(Scalar::Int(value_scalar_int)) + // If `extra` is zero, we have a regular integer. + if scalar.extra == 0 { + // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. + // FIXME(genmc): GenMC should be doing the truncation, not Miri. + let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); + return interp_ok(Scalar::from(value_scalar_int)); + } + // `extra` is non-zero, we have a pointer. + // When we get a pointer from GenMC, then we must have sent it to GenMC before in the same execution (since the reads-from relation is always respected). + let alloc_id = genmc_ctx.exec_state.genmc_shared_allocs_map.borrow()[&scalar.extra]; + // FIXME(genmc,borrow tracking): Borrow tracking not yet supported. + let provenance = machine::Provenance::Concrete { alloc_id, tag: BorTag::default() }; + let ptr = interpret::Pointer::new(provenance, Size::from_bytes(scalar.value)); + interp_ok(Scalar::from_pointer(ptr, &ecx.tcx)) } impl AtomicReadOrd { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 3e8e8fbfd491a..b2f8ab6f972eb 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -7,6 +7,7 @@ use genmc_sys::{ }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; // FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). use tracing::{debug, info}; @@ -85,6 +86,9 @@ struct PerExecutionState { /// we cover all possible executions. /// `None` if no thread has called `exit` and the main thread isn't finished yet. exit_status: Cell>, + + /// Allocations in this map have been sent to GenMC, and should thus be kept around, since future loads from GenMC may return this allocation again. + genmc_shared_allocs_map: RefCell>, } impl PerExecutionState { @@ -92,6 +96,7 @@ impl PerExecutionState { self.allow_data_races.replace(false); self.thread_id_manager.borrow_mut().reset(); self.exit_status.set(None); + self.genmc_shared_allocs_map.borrow_mut().clear(); } } @@ -268,13 +273,13 @@ impl GenmcCtx { ) -> InterpResult<'tcx, Scalar> { assert!(!self.get_alloc_data_races(), "atomic load with data race checking disabled."); let genmc_old_value = if let Some(scalar) = old_val { - scalar_to_genmc_scalar(ecx, scalar)? + scalar_to_genmc_scalar(ecx, self, scalar)? } else { GenmcScalar::UNINIT }; let read_value = self.handle_load(&ecx.machine, address, size, ordering.to_genmc(), genmc_old_value)?; - genmc_scalar_to_scalar(ecx, read_value, size) + genmc_scalar_to_scalar(ecx, self, read_value, size) } /// Inform GenMC about an atomic store. @@ -291,9 +296,9 @@ impl GenmcCtx { ordering: AtomicWriteOrd, ) -> InterpResult<'tcx, bool> { assert!(!self.get_alloc_data_races(), "atomic store with data race checking disabled."); - let genmc_value = scalar_to_genmc_scalar(ecx, value)?; + let genmc_value = scalar_to_genmc_scalar(ecx, self, value)?; let genmc_old_value = if let Some(scalar) = old_value { - scalar_to_genmc_scalar(ecx, scalar)? + scalar_to_genmc_scalar(ecx, self, scalar)? } else { GenmcScalar::UNINIT }; @@ -345,8 +350,8 @@ impl GenmcCtx { size, ordering, to_genmc_rmw_op(atomic_op, is_signed), - scalar_to_genmc_scalar(ecx, rhs_scalar)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, ) } @@ -368,8 +373,8 @@ impl GenmcCtx { size, ordering, /* genmc_rmw_op */ RMWBinOp::Xchg, - scalar_to_genmc_scalar(ecx, rhs_scalar)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, ) } @@ -441,9 +446,9 @@ impl GenmcCtx { genmc_tid, address.bytes(), size.bytes(), - scalar_to_genmc_scalar(ecx, expected_old_value)?, - scalar_to_genmc_scalar(ecx, new_value)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, expected_old_value)?, + scalar_to_genmc_scalar(ecx, self, new_value)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, upgraded_success_ordering.to_genmc(), fail.to_genmc(), can_fail_spuriously, @@ -454,7 +459,7 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); } - let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; + let return_scalar = genmc_scalar_to_scalar(ecx, self, cas_result.old_value, size)?; debug!( "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" ); @@ -644,7 +649,11 @@ impl GenmcCtx { let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()); + if self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()) { + // FIXME(genmc): improve error handling. + // An error was detected, so we get the error string from GenMC. + throw_ub_format!("{}", self.try_get_error().unwrap()); + } interp_ok(()) } @@ -879,10 +888,10 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); } - let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; + let old_value_scalar = genmc_scalar_to_scalar(ecx, self, rmw_result.old_value, size)?; let new_value_scalar = if rmw_result.is_coherence_order_maximal_write { - Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) + Some(genmc_scalar_to_scalar(ecx, self, rmw_result.new_value, size)?) } else { None }; @@ -905,7 +914,10 @@ impl GenmcCtx { } impl VisitProvenance for GenmcCtx { - fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // We don't have any tags. + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + let genmc_shared_allocs_map = self.exec_state.genmc_shared_allocs_map.borrow(); + for alloc_id in genmc_shared_allocs_map.values().copied() { + visit(Some(alloc_id), None); + } } } diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index 7fb3d8fa3ef39..be7df8682f07c 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -117,6 +117,14 @@ impl GenmcCtx { leak_check: exit_status.do_leak_check() }); } + ExecutionState::Error => { + // GenMC found an error in one of the `handle_*` functions, but didn't return the detected error from the function immediately. + // This is still an bug in the user program, so we print the error string. + panic!( + "GenMC found an error ({:?}), but didn't report it immediately, so we cannot provide an appropriate source code location for where it happened.", + self.try_get_error().unwrap() + ); + } _ => unreachable!(), } } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index da8f785e37345..a0e871d87d3a0 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -219,11 +219,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // so we cannot assume 1 access = 1 allocation. :( let mut rg = evt_rg.addr..evt_rg.end(); while let Some(curr) = rg.next() { - let Some(alloc_id) = this.alloc_id_from_addr( - curr.to_u64(), - rg.len().try_into().unwrap(), - /* only_exposed_allocations */ true, - ) else { + let Some(alloc_id) = + this.alloc_id_from_addr(curr.to_u64(), rg.len().try_into().unwrap()) + else { throw_ub_format!("Foreign code did an out-of-bounds access!") }; let alloc = this.get_alloc_raw(alloc_id)?; diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs new file mode 100644 index 0000000000000..d712822a6d00c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can detect a double-free bug across two threads, which only shows up if the second thread reads an atomic pointer at a very specific moment. +// GenMC can detect this error consistently, without having to run the buggy code with multiple RNG seeds or in a loop. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +unsafe fn free(ptr: *mut u64) { + dealloc(ptr as *mut u8, Layout::new::()) //~ ERROR: Undefined Behavior +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + X.store(a, SeqCst); + // We have to yield to the other thread exactly here to reproduce the double-free. + let b = X.swap(std::ptr::null_mut(), SeqCst); + free(b); + }), + spawn_pthread_closure(|| { + let b = X.load(SeqCst); + if !b.is_null() { + free(b); + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr new file mode 100644 index 0000000000000..7d03bd9a8eb84 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr @@ -0,0 +1,39 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | dealloc(ptr as *mut u8, Layout::new::()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | dealloc(ptr as *mut u8, Layout::new::()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside `free` at tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC +note: inside closure + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | free(b); + | ^^^^^^^ + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC}>` + --> tests/genmc/fail/atomics/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr new file mode 100644 index 0000000000000..d74fa12256bbf --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr @@ -0,0 +1,21 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: attempting to access 8 bytes, but got ALLOC-$HEX which points to before the beginning of the allocation + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | *ptr = 44; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | let mut b = Box::new(0u64); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `miri_start` at tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs new file mode 100644 index 0000000000000..87223e990bde9 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs @@ -0,0 +1,61 @@ +//@revisions: send make +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can distinguish two pointers with the same address, but different provenance, after they are sent to GenMC and back. +// We have two variants, one where we send such a pointer to GenMC, and one where we make it on the GenMC side. + +#![no_main] +#![feature(box_as_ptr)] + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let atomic_ptr = AtomicPtr::new(std::ptr::null_mut()); + let mut a = Box::new(0u64); + let mut b = Box::new(0u64); + let a_ptr: *mut u64 = Box::as_mut_ptr(&mut a); + let b_ptr: *mut u64 = Box::as_mut_ptr(&mut b); + + // Store valid pointer to `a`: + atomic_ptr.store(a_ptr, Relaxed); + let ptr = atomic_ptr.load(Relaxed); + *ptr = 42; + if *a != 42 { + std::process::abort(); + } + // Store valid pointer to `b`: + atomic_ptr.store(b_ptr, Relaxed); + let ptr = atomic_ptr.load(Relaxed); + *ptr = 43; + if *b != 43 { + std::process::abort(); + } + + // Make `atomic_ptr` contain a pointer with the provenance of `b`, but the address of `a`. + if cfg!(send) { + // Variant 1: create the invalid pointer non-atomically, then send it to GenMC. + let fake_a_ptr = b_ptr.with_addr(a_ptr.addr()); + if a_ptr.addr() != fake_a_ptr.addr() { + std::process::abort(); + } + atomic_ptr.store(fake_a_ptr, Relaxed); + } else { + // Variant 2: send `b_ptr` to GenMC, then create the invalid pointer to `a` using atomic operations. + atomic_ptr.store(b_ptr, Relaxed); + atomic_ptr.fetch_byte_add(a_ptr.addr(), Relaxed); + atomic_ptr.fetch_byte_sub(b_ptr.addr(), Relaxed); + } + let ptr = atomic_ptr.load(Relaxed); + if a_ptr.addr() != ptr.addr() { + std::process::abort(); + } + // This pointer has the same address as `a_ptr`, but not the same + // provenance, so writing to it fails. + *ptr = 44; //~ ERROR: points to before the beginning of the allocation + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr new file mode 100644 index 0000000000000..d74fa12256bbf --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr @@ -0,0 +1,21 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: attempting to access 8 bytes, but got ALLOC-$HEX which points to before the beginning of the allocation + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | *ptr = 44; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | let mut b = Box::new(0u64); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `miri_start` at tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr new file mode 100644 index 0000000000000..bde793014bbf2 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr @@ -0,0 +1,24 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access freed memory + --> tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + | +LL | dealloc(b as *mut u8, Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs new file mode 100644 index 0000000000000..d82fbf53dac7f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs @@ -0,0 +1,51 @@ +//@revisions: write dealloc +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-ignore-leaks + +// Test that we can detect data races between an allocation and an unsynchronized action in another thread. +// We have two variants, an alloc-dealloc race and an alloc-write race. +// +// We never deallocate the memory, so leak-checks must be disabled. +// +// FIXME(genmc): The error message is currently suboptimal, since it mentions non-allocated/freed memory, instead of pointing towards the missing synchronization with the allocation. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + X.store(a, Relaxed); // Relaxed ordering does not synchronize the alloc with the other thread. + }), + spawn_pthread_closure(|| { + let b = X.load(Relaxed); + if !b.is_null() { + if cfg!(dealloc) { + // Variant: alloc-dealloc race + dealloc(b as *mut u8, Layout::new::()); //~[dealloc] ERROR: Undefined Behavior + } else { + // Variant: alloc-write race + *b = 42; //~[write] ERROR: Undefined Behavior + } + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr new file mode 100644 index 0000000000000..7bfafe0ca086c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr @@ -0,0 +1,22 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access non-allocated memory + --> tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + | +LL | *b = 42; + | ^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs new file mode 100644 index 0000000000000..c9bf1f755925d --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that use-after-free bugs involving atomic pointers are detected in GenMC mode. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +static mut Y: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let mut z: u64 = 1234; + X.store(&raw mut z, SeqCst); // The other thread can read this value and then access `z` after it is deallocated. + X.store(&raw mut Y, SeqCst); + }), + spawn_pthread_closure(|| { + let ptr = X.load(SeqCst); + miri_genmc_assume(!ptr.is_null()); + *ptr = 42; //~ ERROR: Undefined Behavior + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr new file mode 100644 index 0000000000000..0facf6a5d177c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr @@ -0,0 +1,32 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | *ptr = 42; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | let mut z: u64 = 1234; + | ^^^^^ +help: ALLOC was deallocated here: + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | }), + | ^ + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs new file mode 100644 index 0000000000000..2253bac95de8c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs @@ -0,0 +1,50 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that use-after-free bugs involving atomic pointers are detected in GenMC mode. +// Compared to `atomic_ptr_dealloc_write_race.rs`, this variant checks that the data race is still detected, even if the write happens before the free. +// +// FIXME(genmc): We currently cannot show the two spans related to this error (only the second one), +// since there is no mapping between GenMC events (shown with `-Zmiri-genmc-print-genmc-output`) and Miri spans. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +static Y: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let ptr = X.load(SeqCst); + miri_genmc_assume(!ptr.is_null()); + *ptr = 42; + }), + spawn_pthread_closure(|| { + let mut z: u64 = 1234; + X.store(&raw mut z, SeqCst); // The other thread can read this value and then access `z` after it is deallocated. + for _ in 0..10 { + // We do some extra loads here so GenMC schedules the pointer write on the other thread first. + Y.load(Relaxed); + } + // `z` gets dropped on the next line, at which point GenMC will detect the data race with the write in the other thread. + }), //~ ERROR: Undefined Behavior + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr new file mode 100644 index 0000000000000..8e4ed1aba0430 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr @@ -0,0 +1,22 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access freed memory + --> tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC + | +LL | }), + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs new file mode 100644 index 0000000000000..9db58b1f9501b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs @@ -0,0 +1,173 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test several operations on atomic pointers. + +#![no_main] + +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::{Debug, Write}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::utils::*; + +static mut X: u64 = 0; +static mut Y: u64 = 0; + +fn assert_equals(a: T, b: T) { + if a != b { + writeln!(MiriStderr, "{:?}, {:?}", a, b).ok(); + std::process::abort(); + } +} + +/// Check that two pointers are equal and stores to one update the value read from the other. +unsafe fn pointers_equal(a: *mut u64, b: *mut u64) { + assert_equals(a, b); + assert_equals(*a, *b); + *a = 42; + assert_equals(*a, 42); + assert_equals(*b, 42); + *b = 0xAA; + assert_equals(*a, 0xAA); + assert_equals(*b, 0xAA); +} + +unsafe fn test_load_store_exchange() { + let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + // FIXME(genmc): Add test cases with temporal mixing of atomics/non-atomics. + atomic_ptr.store(&raw mut X, SeqCst); + + // Load can read the initial value. + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); + // Store works as expected. + atomic_ptr.store(&raw mut Y, SeqCst); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + + // Atomic swap must return the old value and store the new one. + pointers_equal(atomic_ptr.swap(&raw mut X, SeqCst), &raw mut Y); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); + + // Failing compare_exchange (wrong expected pointer). + match atomic_ptr.compare_exchange(&raw mut Y, std::ptr::null_mut(), SeqCst, SeqCst) { + Ok(_ptr) => std::process::abort(), + Err(ptr) => pointers_equal(ptr, &raw mut X), + } + // Failing compare_exchange (null). + match atomic_ptr.compare_exchange(std::ptr::null_mut(), std::ptr::null_mut(), SeqCst, SeqCst) { + Ok(_ptr) => std::process::abort(), + Err(ptr) => pointers_equal(ptr, &raw mut X), + } + // Successful compare_exchange. + match atomic_ptr.compare_exchange(&raw mut X, &raw mut Y, SeqCst, SeqCst) { + Ok(ptr) => pointers_equal(ptr, &raw mut X), + Err(_ptr) => std::process::abort(), + } + // compare_exchange should update the pointer. + pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); +} + +unsafe fn test_add_sub() { + const LEN: usize = 16; + let mut array: [u64; LEN] = std::array::from_fn(|i| i as u64); + + let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut array[0]); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + atomic_ptr.store(&raw mut array[0], SeqCst); + + // Each element of the array should be reachable using `fetch_ptr_add`. + // All pointers must stay valid. + for i in 0..LEN { + let ptr = atomic_ptr.fetch_ptr_add(1, SeqCst); + pointers_equal(ptr, &raw mut array[i]); + } + // This should return the pointer back to the start of the array. + let ptr = atomic_ptr.fetch_ptr_sub(LEN, SeqCst); + pointers_equal(ptr.offset(-(LEN as isize)), &raw mut array[0]); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut array[0]); + + let array_mid_ptr = &raw mut array[LEN / 2]; + for i in 0..size_of::() { + atomic_ptr.store(array_mid_ptr, SeqCst); // Reset to test `byte_add` and `byte_sub`. + pointers_equal(array_mid_ptr, atomic_ptr.fetch_byte_add(i, SeqCst)); + if array_mid_ptr.byte_add(i) != atomic_ptr.load(SeqCst) { + std::process::abort(); + } + if array_mid_ptr.byte_add(i) != atomic_ptr.fetch_byte_sub(i, SeqCst) { + std::process::abort(); + } + pointers_equal(array_mid_ptr, atomic_ptr.load(SeqCst)); + } +} + +unsafe fn test_and_or_xor() { + const LEN: usize = 16; + #[repr(align(1024))] // Aligned to size 16 * 8 bytes. + struct AlignedArray([u64; LEN]); + + let mut array = AlignedArray(std::array::from_fn(|i| i as u64 * 10)); + let array_ptr = &raw mut array.0[0]; + + let atomic_ptr: AtomicPtr = AtomicPtr::new(array_ptr); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + atomic_ptr.store(array_ptr, SeqCst); + + // Test no-op arguments. + assert_equals(array_ptr, atomic_ptr.fetch_or(0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_xor(0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_and(!0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + + // Test identity arguments. + let array_addr = array_ptr as usize; + assert_equals(array_ptr, atomic_ptr.fetch_or(array_addr, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_and(array_addr, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_xor(array_addr, SeqCst)); + assert_equals(std::ptr::null_mut(), atomic_ptr.load(SeqCst)); // `null_mut` is guaranteed to have address 0. + + // Test moving within an allocation. + // The array is aligned to 64 bytes, so we can change which element we point by or/and/xor-ing the address. + let index = LEN / 2; // Choose an index in the middle of the array. + let offset = index * size_of::(); + let array_mid_ptr = &raw mut array.0[index]; + + atomic_ptr.store(array_ptr, SeqCst); // Reset to test `or`. + assert_equals(array_ptr, atomic_ptr.fetch_or(offset, SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.load(SeqCst)); + + atomic_ptr.store(array_ptr, SeqCst); // Reset to test `xor`. + assert_equals(array_ptr, atomic_ptr.fetch_xor(offset, SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.fetch_xor(offset, SeqCst)); // two xor should yield original value. + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + + let mask = !(u64::BITS as usize - 1); + for i in 0..size_of::() { + // We offset the pointer by `i` bytes, making it unaligned. + let offset_ptr = array_ptr.byte_add(i); + atomic_ptr.store(array_ptr, SeqCst); + // `fetch_byte_add` should return the old value. + assert_equals(array_ptr, atomic_ptr.fetch_byte_add(i, SeqCst)); + // `ptr::byte_add` and `AtomicPtr::fetch_byte_add` should give the same result. + if offset_ptr != atomic_ptr.fetch_and(mask, SeqCst) { + std::process::abort(); + } + // Masking off the last bits should restore the pointer. + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + test_load_store_exchange(); + test_add_sub(); + test_and_or_xor(); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs new file mode 100644 index 0000000000000..75ee5ede9c969 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs @@ -0,0 +1,60 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can send pointers with any alignment to GenMC and back, even across threads. +// After a round-trip, the pointers should still work properly (no missing provenance). + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use utils::*; + +static PTR: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +static mut X: [u8; 16] = [0; 16]; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + PTR.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + for i in 0..X.len() { + X[i] = i.try_into().unwrap(); + PTR.store(&raw mut X[i], SeqCst); + // Wait for the other thread to reset the AtomicPtr. + miri_genmc_assume(PTR.load(SeqCst).is_null()); + // Check that we see the update the other thread did through the pointer. + if X[i] != (i + 1) as u8 { + std::process::abort(); + } + } + }), + spawn_pthread_closure(|| { + for i in 0..X.len() { + let x = PTR.load(SeqCst); + // Wait for the other thread to store the next pointer. + miri_genmc_assume(!x.is_null()); + // Check that we see the update when reading from the pointer. + if usize::from(*x) != i { + std::process::abort(); + } + *x = (i + 1) as u8; + PTR.store(std::ptr::null_mut(), SeqCst); + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr new file mode 100644 index 0000000000000..67d88dfaed711 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 33 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr new file mode 100644 index 0000000000000..5b60672a09d71 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 4 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr new file mode 100644 index 0000000000000..1a025216a22d6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 188 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs new file mode 100644 index 0000000000000..fda517ae7d231 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -0,0 +1,200 @@ +//@ revisions: default_R1W1 default_R1W2 spinloop_assume_R1W1 spinloop_assume_R1W2 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test is a translations of the GenMC test `ms-queue-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. +// +// Test variant naming convention: "[VARIANT_NAME]_R[#reader_threads]_W[#writer_threads]". +// We test different numbers of writer threads to see the scaling. +// Implementing optimizations such as automatic spinloop-assume transformation or symmetry reduction should reduce the number of explored executions. +// We also test variants using manual spinloop replacement, which should yield fewer executions in total compared to the unmodified code. +// +// FIXME(genmc): Add revisions `default_R1W3` and `spinloop_assume_R1W3` once Miri-GenMC performance is improved. These currently slow down the test suite too much. +// +// The test uses verbose output to see the difference between blocked and explored executions. + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[allow(unused)] +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use libc::pthread_t; + +const MAX_THREADS: usize = 32; + +static mut QUEUE: MyStack = MyStack::new(); +static mut INPUT: [u64; MAX_THREADS] = [0; MAX_THREADS]; +static mut OUTPUT: [Option; MAX_THREADS] = [None; MAX_THREADS]; + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + head: AtomicPtr, + tail: AtomicPtr, +} + +impl Node { + pub unsafe fn alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } +} + +impl MyStack { + pub const fn new() -> Self { + let head = AtomicPtr::new(std::ptr::null_mut()); + let tail = AtomicPtr::new(std::ptr::null_mut()); + Self { head, tail } + } + + pub unsafe fn init_queue(&mut self, _num_threads: usize) { + let dummy = Node::alloc(); + + (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); + self.head = AtomicPtr::new(dummy); + self.tail = AtomicPtr::new(dummy); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + (*dummy).next.store(std::ptr::null_mut(), Relaxed); + self.head.store(dummy, Relaxed); + self.tail.store(dummy, Relaxed); + } + + pub unsafe fn clear_queue(&mut self, _num_threads: usize) { + let mut next; + let mut head = *self.head.get_mut(); + while !head.is_null() { + next = *(*head).next.get_mut(); + Node::free(head); + head = next; + } + } + + pub unsafe fn enqueue(&self, value: u64) { + let mut tail; + let node = Node::alloc(); + (*node).value = value; + (*node).next = AtomicPtr::new(std::ptr::null_mut()); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + (*node).next.store(std::ptr::null_mut(), Relaxed); + + loop { + tail = self.tail.load(Acquire); + let next = (*tail).next.load(Acquire); + if tail != self.tail.load(Acquire) { + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + continue; + } + + if next.is_null() { + if (*tail).next.compare_exchange(next, node, Release, Relaxed).is_ok() { + break; + } + } else { + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } + } + + let _ = self.tail.compare_exchange(tail, node, Release, Relaxed); + } + + pub unsafe fn dequeue(&self) -> Option { + loop { + let head = self.head.load(Acquire); + let tail = self.tail.load(Acquire); + + let next = (*head).next.load(Acquire); + if self.head.load(Acquire) != head { + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + continue; + } + if head == tail { + if next.is_null() { + return None; + } + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } else { + let ret_val = (*next).value; + if self.head.compare_exchange(head, next, Release, Relaxed).is_ok() { + // NOTE: The popped `Node` is leaked. + return Some(ret_val); + } + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + // All operations in the loop leading to here are either loads, or failed compare-exchange operations. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // We try multiple different parameters for the number and types of threads: + let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { + (1, 3) + } else if cfg!(any(default_R1W2, spinloop_assume_R1W2)) { + (1, 2) + } else { + // default_R1W1, spinloop_assume_R1W1 + (1, 1) + }; + + let num_threads = readers + writers; + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + MyStack::init_queue(&mut QUEUE, num_threads); + + /* Spawn threads */ + let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + for _ in 0..readers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + OUTPUT[pid as usize] = QUEUE.dequeue(); + }); + i += 1; + } + for _ in 0..writers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + INPUT[pid as usize] = pid * 10; + QUEUE.enqueue(INPUT[pid as usize]); + }); + i += 1; + } + + for i in 0..num_threads { + join_pthread(thread_ids[i]); + } + + MyStack::clear_queue(&mut QUEUE, num_threads); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr new file mode 100644 index 0000000000000..5b60672a09d71 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 4 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr new file mode 100644 index 0000000000000..44332a2f027e4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 152 executions. No errors found. +Number of complete executions explored: 128 +Number of blocked executions seen: 24 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr new file mode 100644 index 0000000000000..5452d10bb2608 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr new file mode 100644 index 0000000000000..98b0ced1c89aa --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 22 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr new file mode 100644 index 0000000000000..ba085e45e57fc --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 1002 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs new file mode 100644 index 0000000000000..2101f9ae66bb6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -0,0 +1,150 @@ +//@ revisions: default_R1W1 default_R1W2 default_R1W3 spinloop_assume_R1W1 spinloop_assume_R1W2 spinloop_assume_R1W3 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test is a translations of the GenMC test `treiber-stack-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. +// +// Test variant naming convention: "[VARIANT_NAME]_R[#reader_threads]_W[#writer_threads]". +// We test different numbers of writer threads to see the scaling. +// Implementing optimizations such as automatic spinloop-assume transformation or symmetry reduction should reduce the number of explored executions. +// We also test variants using manual spinloop replacement, which should yield fewer executions in total compared to the unmodified code. +// +// The test uses verbose output to see the difference between blocked and explored executions. + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[allow(unused)] +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use libc::pthread_t; + +const MAX_THREADS: usize = 32; + +static mut STACK: MyStack = MyStack::new(); + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + top: AtomicPtr, +} + +impl Node { + pub unsafe fn alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } +} + +impl MyStack { + pub const fn new() -> Self { + Self { top: AtomicPtr::new(std::ptr::null_mut()) } + } + + pub unsafe fn clear_stack(&mut self, _num_threads: usize) { + let mut next; + let mut top = *self.top.get_mut(); + while !top.is_null() { + next = *(*top).next.get_mut(); + Node::free(top); + top = next; + } + } + + pub unsafe fn push(&self, value: u64) { + let node = Node::alloc(); + (*node).value = value; + + loop { + let top = self.top.load(Acquire); + (*node).next.store(top, Relaxed); + if self.top.compare_exchange(top, node, Release, Relaxed).is_ok() { + break; + } + // We manually limit the number of iterations of this spinloop to 1. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } + + pub unsafe fn pop(&self) -> u64 { + loop { + let top = self.top.load(Acquire); + if top.is_null() { + return 0; + } + + let next = (*top).next.load(Relaxed); + if self.top.compare_exchange(top, next, Release, Relaxed).is_ok() { + // NOTE: The popped `Node` is leaked. + return (*top).value; + } + // We manually limit the number of iterations of this spinloop to 1. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + unsafe { STACK.top.store(std::ptr::null_mut(), Relaxed) }; + + // We try multiple different parameters for the number and types of threads: + let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { + (1, 3) + } else if cfg!(any(default_R1W2, spinloop_assume_R1W2)) { + (1, 2) + } else { + // default_R1W1, spinloop_assume_R1W1 + (1, 1) + }; + + let num_threads = readers + writers; + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + for _ in 0..readers { + thread_ids[i] = spawn_pthread_closure(move || { + let _idx = STACK.pop(); + }); + i += 1; + } + for _ in 0..writers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + STACK.push(pid); + }); + i += 1; + } + + for i in 0..num_threads { + join_pthread(thread_ids[i]); + } + + MyStack::clear_stack(&mut STACK, num_threads); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr new file mode 100644 index 0000000000000..5452d10bb2608 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr new file mode 100644 index 0000000000000..9082acd027d4c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 16 executions. No errors found. +Number of complete executions explored: 8 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr new file mode 100644 index 0000000000000..02ac5a982f3f0 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 195 executions. No errors found. +Number of complete executions explored: 36 +Number of blocked executions seen: 159 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index c81573d59d1da..fa717b4cf18a8 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -57,7 +57,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - writeln!(MiriStderr, "{results:?}").unwrap_or_else(|_| std::process::abort()); + writeln!(MiriStderr, "{results:?}").ok(); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs index 9bb156a99970f..a1b13e3f3c56f 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -60,7 +60,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { X.load(Relaxed), Y.load(Relaxed) ) - .unwrap_or_else(|_| std::process::abort()); + .ok(); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index f96679b23a5cd..a991ccb0d4e04 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -55,8 +55,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) - .unwrap_or_else(|_| std::process::abort()); + writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)).ok(); 0 } } diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs b/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs new file mode 100644 index 0000000000000..cabc8ec92da1c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc}; +use std::cell::Cell; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +thread_local! { + static R: Cell<*mut u64> = Cell::new(std::ptr::null_mut()); +} + +pub unsafe fn malloc() -> *mut u64 { + alloc(Layout::new::()) as *mut u64 +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + spawn_pthread_closure(|| { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); + }); + spawn_pthread_closure(|| { + R.set(malloc()); + }); + spawn_pthread_closure(|| { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); + }); + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr new file mode 100644 index 0000000000000..bde951866d013 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. From 17cecfd311848f10c60014fe4714b1475ee545d5 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 02:00:06 +0800 Subject: [PATCH 1052/1900] fix: `new_without_default` FP on private type with trait impl --- clippy_lints/src/new_without_default.rs | 2 +- tests/ui/new_without_default.fixed | 20 ++++++++++++++++++++ tests/ui/new_without_default.rs | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index b598a390005ba..ab7b09c48a7fe 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { return; } if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && self_ty == return_ty(cx, impl_item.owner_id) && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 277c335cd8851..b5558cd3086cc 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -322,3 +322,23 @@ where Self { _kv: None } } } + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index f2844897c93d9..be598c81718f5 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -265,3 +265,23 @@ where Self { _kv: None } } } + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} From 642feb7e562edcc5d95bbc2e6ca27438fcd3b35f Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 02:28:54 +0800 Subject: [PATCH 1053/1900] fix: `if_then_some_else_none` FP when return exists in block expr --- clippy_lints/src/if_then_some_else_none.rs | 1 + tests/ui/if_then_some_else_none.fixed | 12 ++++++++++++ tests/ui/if_then_some_else_none.rs | 12 ++++++++++++ 3 files changed, 25 insertions(+) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index b50d91f101463..f9fee292837ea 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -79,6 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) + && then_block.expr.is_none_or(|expr| !contains_return(expr)) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { sym::then_some diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index 0fd130609aee2..7da9401a308f7 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -206,3 +206,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 640828aa9bf6b..02962f83ce8aa 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -262,3 +262,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} From c3e4d2bb90c71040adc120e3322fa2e3696152ab Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 19:21:21 +0200 Subject: [PATCH 1054/1900] clean-up --- clippy_lints/src/unit_types/let_unit_value.rs | 25 +++++++++++++++++-- tests/ui/let_unit.fixed | 15 +++++------ tests/ui/let_unit.rs | 15 +++++------ tests/ui/let_unit.stderr | 10 ++++---- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d5b6c17585494..1f05617a746f3 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -89,15 +89,30 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag walk_body(&mut visitor, body); let mut has_in_format_capture = false; - suggestions.extend(visitor.spans.iter().filter_map(|span| match span { + suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { MaybeInFormatCapture::Yes => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + MaybeInFormatCapture::No(span) => Some((span, "()".to_string())), })); if has_in_format_capture { + // In a case like this: + // ``` + // let unit = returns_unit(); + // eprintln!("{unit}"); + // ``` + // we can't remove the `unit` binding and replace its uses with a `()`, + // because the `eprintln!` would break. + // + // So do the following instead: + // ``` + // let unit = (); + // returns_unit(); + // eprintln!("{unit}"); + // ``` + // TODO: find a less awkward way to do this suggestions.push(( init.span, format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), @@ -132,6 +147,12 @@ struct UnitVariableCollector<'a, 'tcx> { macro_call: Option<&'a FormatArgs>, } +/// Whether the unit variable is captured in a `format!`: +/// +/// ```ignore +/// let unit = (); +/// eprintln!("{unit}"); +/// ``` enum MaybeInFormatCapture { Yes, No(Span), diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 381d4cac4622b..d640c6a916d03 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,5 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +15,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +30,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +181,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -200,11 +198,10 @@ pub fn issue12594() { } fn issue15061() { - fn return_unit() {} fn do_something(x: ()) {} let res = (); - return_unit(); + returns_unit(); //~^ let_unit_value do_something(()); println!("{res:?}"); diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index cdfc74991c40d..d966d2ec0a5c1 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,5 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +15,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +30,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +181,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -200,10 +198,9 @@ pub fn issue12594() { } fn issue15061() { - fn return_unit() {} fn do_something(x: ()) {} - let res = return_unit(); + let res = returns_unit(); //~^ let_unit_value do_something(res); println!("{res:?}"); diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 637c9ff686bdb..4d9bbbe310749 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -55,7 +55,7 @@ LL + }; | error: this let-binding has unit value - --> tests/ui/let_unit.rs:192:9 + --> tests/ui/let_unit.rs:190:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,15 +69,15 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:206:5 + --> tests/ui/let_unit.rs:203:5 | -LL | let res = return_unit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let res = returns_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace variable usages with `()` | LL ~ let res = (); -LL ~ return_unit(); +LL ~ returns_unit(); LL | LL ~ do_something(()); | From 48ee4705ddf68c452525b931355cf546a1bba023 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 21:16:15 +0200 Subject: [PATCH 1055/1900] fix: actually use the `Applicability` --- clippy_lints/src/unit_types/let_unit_value.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 1f05617a746f3..80a99cfd0544b 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -117,11 +117,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag init.span, format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), )); - diag.multipart_suggestion( - "replace variable usages with `()`", - suggestions, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion("replace variable usages with `()`", suggestions, app); return; } } @@ -132,7 +128,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(message, suggestions, app); }, ); } From f6dd112a0ae3e017828dd49a5dc4e5edd648bca6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 19:20:47 +0200 Subject: [PATCH 1056/1900] fix: create the suggestion "differentially" --- clippy_lints/src/unit_types/let_unit_value.rs | 18 +++-- tests/ui/let_unit.fixed | 31 +++++++- tests/ui/let_unit.rs | 30 ++++++- tests/ui/let_unit.stderr | 78 +++++++++++++------ 4 files changed, 118 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 80a99cfd0544b..424aa14cd6863 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{snippet_indent, walk_span_to_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_ast::{FormatArgs, FormatArgumentKind}; @@ -74,10 +74,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag "this let-binding has unit value", |diag| { let mut suggestions = Vec::new(); + let init_new_span = walk_span_to_context(init.span, local.span.ctxt()).unwrap(); // Suggest omitting the `let` binding - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; + let app = Applicability::MachineApplicable; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -114,21 +114,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag // ``` // TODO: find a less awkward way to do this suggestions.push(( - init.span, - format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + init_new_span.shrink_to_lo(), + format!("();\n{}", snippet_indent(cx, local.span).as_deref().unwrap_or("")), )); - diag.multipart_suggestion("replace variable usages with `()`", suggestions, app); + diag.multipart_suggestion_verbose("replace variable usages with `()`", suggestions, app); return; } } - suggestions.push((local.span, format!("{snip};"))); + // let local = returns_unit(); + // ^^^^^^^^^^^^ remove this + suggestions.push((local.span.until(init_new_span), String::new())); let message = if suggestions.len() == 1 { "omit the `let` binding" } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, app); + diag.multipart_suggestion_verbose(message, suggestions, app); }, ); } diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index d640c6a916d03..e8517f18e788b 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -197,12 +202,30 @@ pub fn issue12594() { } } -fn issue15061() { - fn do_something(x: ()) {} +fn takes_unit(x: ()) {} +fn issue15061() { let res = (); returns_unit(); //~^ let_unit_value - do_something(()); + takes_unit(()); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => returns_unit(), + //~^ let_unit_value + } + + if true {} + //~^ let_unit_value +} + +fn issue_15784() { + let res = (); + eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(()); println!("{res:?}"); } diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index d966d2ec0a5c1..3f6f0139b2fec 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -197,11 +202,28 @@ pub fn issue12594() { } } -fn issue15061() { - fn do_something(x: ()) {} +fn takes_unit(x: ()) {} +fn issue15061() { let res = returns_unit(); //~^ let_unit_value - do_something(res); + takes_unit(res); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => _ = returns_unit(), + //~^ let_unit_value + } + + _ = if true {} + //~^ let_unit_value +} + +fn issue_15784() { + let res = eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(res); println!("{res:?}"); } diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 4d9bbbe310749..8ced32ab828f7 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -1,14 +1,19 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:16:5 | LL | let _x = println!("x"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::let-unit-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` +help: omit the `let` binding + | +LL - let _x = println!("x"); +LL + println!("x"); + | error: this let-binding has unit value - --> tests/ui/let_unit.rs:60:5 + --> tests/ui/let_unit.rs:65:5 | LL | / let _ = v LL | | @@ -21,18 +26,12 @@ LL | | .unwrap(); | help: omit the `let` binding | -LL ~ v -LL + -LL + .into_iter() -LL + .map(|i| i * 2) -LL + .filter(|i| i.is_multiple_of(2)) -LL + .map(|_| ()) -LL + .next() -LL + .unwrap(); +LL - let _ = v +LL + v | error: this let-binding has unit value - --> tests/ui/let_unit.rs:110:5 + --> tests/ui/let_unit.rs:115:5 | LL | / let x = match Some(0) { LL | | @@ -45,17 +44,12 @@ LL | | }; | help: omit the `let` binding | -LL ~ match Some(0) { -LL + -LL + None => f2(1), -LL + Some(0) => f(), -LL + Some(1) => f2(3), -LL + Some(_) => (), -LL + }; +LL - let x = match Some(0) { +LL + match Some(0) { | error: this let-binding has unit value - --> tests/ui/let_unit.rs:190:9 + --> tests/ui/let_unit.rs:195:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,7 +63,7 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:203:5 + --> tests/ui/let_unit.rs:208:5 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -79,8 +73,46 @@ help: replace variable usages with `()` LL ~ let res = (); LL ~ returns_unit(); LL | -LL ~ do_something(()); +LL ~ takes_unit(()); + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:216:14 + | +LL | _ => _ = returns_unit(), + | ^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ => _ = returns_unit(), +LL + _ => returns_unit(), + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:220:5 + | +LL | _ = if true {} + | ^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ = if true {} +LL + if true {} + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:225:5 + | +LL | let res = eprintln!("I return unit"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ eprintln!("I return unit"); +LL | +LL ~ takes_unit(()); | -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors From cfefa9bc883dda6fe09a5041e35b33d17aa0969e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 29 Sep 2025 19:07:01 +0200 Subject: [PATCH 1057/1900] genmc: bail out for non-64bit-little-endian targets --- src/tools/miri/src/bin/miri.rs | 10 +++++----- src/tools/miri/src/concurrency/genmc/config.rs | 11 ++++++++--- src/tools/miri/src/concurrency/genmc/dummy.rs | 4 +++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 40ac5c3ff385c..3c759521c6ca0 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -188,6 +188,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Run in GenMC mode if enabled. if config.genmc_config.is_some() { + // Validate GenMC settings. + if let Err(err) = GenmcConfig::validate(&mut config, tcx) { + fatal_error!("Invalid settings: {err}"); + } + // This is the entry point used in GenMC mode. // This closure will be called multiple times to explore the concurrent execution space of the program. let eval_entry_once = |genmc_ctx: Rc| { @@ -745,11 +750,6 @@ fn main() { let many_seeds = many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); - // Validate settings for data race detection and GenMC mode. - if let Err(err) = GenmcConfig::validate_genmc_mode_settings(&mut miri_config) { - fatal_error!("Invalid settings: {err}"); - } - if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index c7cfa6012b8dc..a05cda46f3e74 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,4 +1,6 @@ use genmc_sys::LogLevel; +use rustc_abi::Endian; +use rustc_middle::ty::TyCtxt; use super::GenmcParams; use crate::{IsolatedOp, MiriConfig, RejectOpWith}; @@ -32,8 +34,6 @@ impl GenmcConfig { genmc_config: &mut Option, trimmed_arg: &str, ) -> Result<(), String> { - // FIXME(genmc): Ensure host == target somewhere. - if genmc_config.is_none() { *genmc_config = Some(Default::default()); } @@ -86,11 +86,16 @@ impl GenmcConfig { /// /// Unsupported configurations return an error. /// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user. - pub fn validate_genmc_mode_settings(miri_config: &mut MiriConfig) -> Result<(), &'static str> { + pub fn validate(miri_config: &mut MiriConfig, tcx: TyCtxt<'_>) -> Result<(), &'static str> { let Some(genmc_config) = miri_config.genmc_config.as_mut() else { return Ok(()); }; + // Check for supported target. + if tcx.data_layout.endian != Endian::Little || tcx.data_layout.pointer_size().bits() != 64 { + return Err("GenMC only supports 64bit little-endian targets"); + } + // Check for disallowed configurations. if !miri_config.data_race_detector { return Err("Cannot disable data race detection in GenMC mode"); diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index e260af7c4a16b..ef5b24d5abad5 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,5 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; +use rustc_middle::ty::TyCtxt; pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; @@ -230,8 +231,9 @@ impl GenmcConfig { } } - pub fn validate_genmc_mode_settings( + pub fn validate( _miri_config: &mut crate::MiriConfig, + _tcx: TyCtxt<'_>, ) -> Result<(), &'static str> { Ok(()) } From 9bb4081fb0977c1a8923fff6ebe60fc882bcac1d Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 29 Sep 2025 23:42:01 +0200 Subject: [PATCH 1058/1900] remove `reverse_{encode, decode}!` --- library/proc_macro/src/bridge/client.rs | 2 +- library/proc_macro/src/bridge/mod.rs | 20 -------------------- library/proc_macro/src/bridge/server.rs | 2 +- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index e7d547966a5d5..4e519e56a1ed4 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -144,7 +144,7 @@ macro_rules! define_client_side { buf.clear(); api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); - reverse_encode!(buf; $($arg),*); + $($arg.encode(&mut buf, &mut ());)* buf = bridge.dispatch.call(buf); diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index d60a76fff5dc5..7fd67ba465ef4 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -119,26 +119,6 @@ macro_rules! with_api_handle_types { }; } -// FIXME(eddyb) this calls `encode` for each argument, but in reverse, -// to match the ordering in `reverse_decode`. -macro_rules! reverse_encode { - ($writer:ident;) => {}; - ($writer:ident; $first:ident $(, $rest:ident)*) => { - reverse_encode!($writer; $($rest),*); - $first.encode(&mut $writer, &mut ()); - } -} - -// FIXME(eddyb) this calls `decode` for each argument, but in reverse, -// to avoid borrow conflicts from borrows started by `&mut` arguments. -macro_rules! reverse_decode { - ($reader:ident, $s:ident;) => {}; - ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { - reverse_decode!($reader, $s; $($rest: $rest_ty),*); - let $first = <$first_ty>::decode(&mut $reader, $s); - } -} - #[allow(unsafe_code)] mod arena; #[allow(unsafe_code)] diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 5beda7c3c96e5..724ccbd96c59b 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -178,7 +178,7 @@ macro_rules! define_dispatcher_impl { $(api_tags::Method::$name(m) => match m { $(api_tags::$name::$method => { let mut call_method = || { - reverse_decode!(reader, handle_store; $($arg: $arg_ty),*); + $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* $name::$method(server, $($arg),*) }; // HACK(eddyb) don't use `panic::catch_unwind` in a panic. From 23f340061395300415d551fdac92d28ca2f2fbfb Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 30 Sep 2025 00:01:17 +0200 Subject: [PATCH 1059/1900] remove unused `#![feature(stmt_expr_attributes)]` --- library/proc_macro/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 162b4fdcc8ae2..613abd7024e3f 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,7 +27,6 @@ #![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![feature(extend_one)] #![recursion_limit = "256"] #![allow(internal_features)] From d7773f6b1c220d43946bce8010847837ad334b74 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 30 Sep 2025 00:09:12 +0200 Subject: [PATCH 1060/1900] explicitly implement `!Send` and `!Sync` --- library/proc_macro/src/bridge/client.rs | 17 ++++++----------- library/proc_macro/src/bridge/closure.rs | 4 +--- library/proc_macro/src/bridge/mod.rs | 8 +++----- library/proc_macro/src/bridge/server.rs | 14 ++------------ 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 4e519e56a1ed4..92558f2b7d9cc 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -26,18 +26,16 @@ macro_rules! define_client_handles { $( pub(crate) struct $oty { handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, } + impl !Send for $oty {} + impl !Sync for $oty {} + // Forward `Drop::drop` to the inherent `drop` method. impl Drop for $oty { fn drop(&mut self) { $oty { handle: self.handle, - _marker: PhantomData, }.drop(); } } @@ -64,7 +62,6 @@ macro_rules! define_client_handles { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $oty { handle: handle::Handle::decode(r, s), - _marker: PhantomData, } } } @@ -74,12 +71,11 @@ macro_rules! define_client_handles { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct $ity { handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, } + impl !Send for $ity {} + impl !Sync for $ity {} + impl Encode for $ity { fn encode(self, w: &mut Writer, s: &mut S) { self.handle.encode(w, s); @@ -90,7 +86,6 @@ macro_rules! define_client_handles { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $ity { handle: handle::Handle::decode(r, s), - _marker: PhantomData, } } } diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index e0e688434dce5..e5133907854b2 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -6,9 +6,7 @@ use std::marker::PhantomData; pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. + // Prevent Send and Sync impls. // // The `'a` lifetime parameter represents the lifetime of `Env`. _marker: PhantomData<*mut &'a mut ()>, diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 7fd67ba465ef4..1b09deb6bfe60 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -160,13 +160,11 @@ pub struct BridgeConfig<'a> { /// If 'true', always invoke the default panic hook force_show_panics: bool, - - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. - _marker: marker::PhantomData<*mut ()>, } +impl !Send for BridgeConfig<'_> {} +impl !Sync for BridgeConfig<'_> {} + #[forbid(unsafe_code)] #[allow(non_camel_case_types)] mod api_tags { diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 724ccbd96c59b..0bb30698aa1d7 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -295,12 +295,7 @@ impl ExecutionStrategy for SameThread { let mut dispatch = |buf| dispatcher.dispatch(buf); - run_client(BridgeConfig { - input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) + run_client(BridgeConfig { input, dispatch: (&mut dispatch).into(), force_show_panics }) } } @@ -331,12 +326,7 @@ where client.recv().expect("server died while client waiting for reply") }; - run_client(BridgeConfig { - input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) + run_client(BridgeConfig { input, dispatch: (&mut dispatch).into(), force_show_panics }) }); while let Some(b) = server.recv() { From 73f6b08022944d24fb5b623494a18cc95df21802 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 22 Sep 2025 10:59:39 -0700 Subject: [PATCH 1061/1900] Don't condition RUSTDOC_LIBDIR on `--no-doc` In d94e7ff065cd393a645eb3e9c96ce0418856e95d, `rustdoc_path` was changed to ignore `want_rustdoc` (which is just whether `--no-doc` was passed). But RUSTDOC_LIBDIR wasn't kept in sync. Rather than trying to keep `rustdoc_path` in sync with `RUSTDOC_LIBDIR`, just pass LIBDIR to the rustc shim unconditionally. This fix allows calling `ensure(doc::Step)` from a non-doc top-level Step, even if `--no-doc` was present in the command line. --- src/bootstrap/src/core/builder/cargo.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 9fc4ce669c2a5..a404aec512091 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -10,7 +10,7 @@ use crate::core::config::flags::Color; use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ - BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + BootstrapCommand, CLang, Compiler, Config, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; @@ -851,8 +851,6 @@ impl Builder<'_> { rustflags.arg("-Zmacro-backtrace"); - let want_rustdoc = self.doc_tests != DocTests::No; - // Clear the output directory if the real rustc we're using has changed; // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. // @@ -881,7 +879,8 @@ impl Builder<'_> { .env("RUSTC_REAL", self.rustc(compiler)) .env("RUSTC_STAGE", build_compiler_stage.to_string()) .env("RUSTC_SYSROOT", sysroot) - .env("RUSTC_LIBDIR", libdir) + .env("RUSTC_LIBDIR", &libdir) + .env("RUSTDOC_LIBDIR", libdir) .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env("RUSTDOC_REAL", rustdoc_path) .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()); @@ -919,11 +918,6 @@ impl Builder<'_> { rustflags.arg(&format!("-Zstack-protector={stack_protector}")); } - if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc - { - cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); - } - let debuginfo_level = match mode { Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, Mode::Std => self.config.rust_debuginfo_level_std, From 93355f1c7b279866718f76609450cfa1b8cc9973 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 20:17:07 +0200 Subject: [PATCH 1062/1900] clean-up - reduce indentation - use `snippet_with_applicability` --- clippy_lints/src/new_without_default.rs | 170 ++++++++++++------------ tests/ui/new_without_default.fixed | 1 - tests/ui/new_without_default.rs | 1 - tests/ui/new_without_default.stderr | 18 +-- 4 files changed, 92 insertions(+), 98 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index ab7b09c48a7fe..5a409a811c30a 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; @@ -58,103 +58,99 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if let hir::ItemKind::Impl(hir::Impl { + let hir::ItemKind::Impl(hir::Impl { of_trait: None, generics, self_ty: impl_self_ty, .. }) = item.kind + else { + return; + }; + + for assoc_item in cx + .tcx + .associated_items(item.owner_id.def_id) + .filter_by_name_unhygienic(sym::new) { - for assoc_item in cx - .tcx - .associated_items(item.owner_id.def_id) - .filter_by_name_unhygienic(sym::new) + if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind + && let impl_item = cx + .tcx + .hir_node_by_def_id(assoc_item.def_id.expect_local()) + .expect_impl_item() + && !impl_item.span.in_external_macro(cx.sess().source_map()) + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind + && let id = impl_item.owner_id + // can't be implemented for unsafe new + && !sig.header.is_unsafe() + // shouldn't be implemented when it is hidden in docs + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) + // when the result of `new()` depends on a parameter we should not require + // an impl of `Default` + && impl_item.generics.params.is_empty() + && sig.decl.inputs.is_empty() + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && self_ty == return_ty(cx, impl_item.owner_id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { - if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind { - let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item(); - if impl_item.span.in_external_macro(cx.sess().source_map()) { - return; - } - if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { - let id = impl_item.owner_id; - if sig.header.is_unsafe() { - // can't be implemented for unsafe new - return; - } - if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { - // shouldn't be implemented when it is hidden in docs - return; - } - if !impl_item.generics.params.is_empty() { - // when the result of `new()` depends on a parameter we should not require - // an impl of `Default` - return; - } - if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() - && self_ty == return_ty(cx, impl_item.owner_id) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + for &d in cx.tcx.local_trait_impls(default_trait_id) { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - for &d in cx.tcx.local_trait_impls(default_trait_id) { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() - && let Some(local_def_id) = ty_def.did().as_local() - { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } - } - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if let Some(ref impling_types) = self.impling_types - && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() - && let Some(self_def) = self_def.ty_adt_def() - && let Some(self_local_did) = self_def.did().as_local() - && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) - && impling_types.contains(&self_id) - { - return; - } - - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg, - ), - Applicability::MachineApplicable, - ); - }, - ); + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } } + self.impling_types = Some(impls); } + + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(self_def) = self_def.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) + && impling_types.contains(&self_id) + { + return; + } + + let mut app = Applicability::MachineApplicable; + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!( + "\n{}\n", + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) + ) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut app); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + app, + ); + }, + ); } } } diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index b5558cd3086cc..4eb2a9903b9a8 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index be598c81718f5..cfcff1e3ac3d0 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 70a65aba464b8..a0b4d3704f56c 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:13:5 + --> tests/ui/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | @@ -20,7 +20,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> tests/ui/new_without_default.rs:23:5 + --> tests/ui/new_without_default.rs:22:5 | LL | / pub fn new() -> Self { LL | | @@ -39,7 +39,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> tests/ui/new_without_default.rs:89:5 + --> tests/ui/new_without_default.rs:88:5 | LL | / pub fn new() -> LtKo<'c> { LL | | @@ -58,7 +58,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Const` - --> tests/ui/new_without_default.rs:123:5 + --> tests/ui/new_without_default.rs:122:5 | LL | / pub const fn new() -> Const { LL | | @@ -76,7 +76,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> tests/ui/new_without_default.rs:184:5 + --> tests/ui/new_without_default.rs:183:5 | LL | / pub fn new() -> Self { LL | | @@ -95,7 +95,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> tests/ui/new_without_default.rs:194:5 + --> tests/ui/new_without_default.rs:193:5 | LL | / pub fn new() -> Self { LL | | @@ -114,7 +114,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> tests/ui/new_without_default.rs:203:5 + --> tests/ui/new_without_default.rs:202:5 | LL | / pub fn new() -> Self { LL | | @@ -133,7 +133,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:216:9 + --> tests/ui/new_without_default.rs:215:9 | LL | / pub fn new() -> Self { LL | | @@ -154,7 +154,7 @@ LL ~ impl Foo { | error: you should consider adding a `Default` implementation for `MyStruct` - --> tests/ui/new_without_default.rs:263:5 + --> tests/ui/new_without_default.rs:262:5 | LL | / pub fn new() -> Self { LL | | From 7a04adae7d68f9a404e5843039479b954cfa0bb5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 22:21:16 +0200 Subject: [PATCH 1063/1900] fix(new_without_default): if `new` has `#[cfg]`, copy that onto `impl Default` --- clippy_lints/src/new_without_default.rs | 30 +++++++-- tests/ui/new_without_default.fixed | 68 +++++++++++++++++++++ tests/ui/new_without_default.rs | 40 ++++++++++++ tests/ui/new_without_default.stderr | 81 ++++++++++++++++++++++++- 4 files changed, 213 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 5a409a811c30a..6fc034b6fc5d2 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -74,10 +74,8 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { .filter_by_name_unhygienic(sym::new) { if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind - && let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item() + && let assoc_item_hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local()) + && let impl_item = cx.tcx.hir_node(assoc_item_hir_id).expect_impl_item() && !impl_item.span.in_external_macro(cx.sess().source_map()) && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind && let id = impl_item.owner_id @@ -120,6 +118,26 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } let mut app = Applicability::MachineApplicable; + let attrs_sugg = { + let mut sugg = String::new(); + for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { + if !attr.has_name(sym::cfg_trace) { + // This might be some other attribute that the `impl Default` ought to inherit. + // But it could also be one of the many attributes that: + // - can't be put on an impl block -- like `#[inline]` + // - we can't even build a suggestion for, since `Attribute::span` may panic. + // + // Because of all that, remain on the safer side -- don't inherit this attr, and just + // reduce the applicability + app = Applicability::MaybeIncorrect; + continue; + } + + sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); + sugg.push('\n'); + } + sugg + }; let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); let where_clause_sugg = if generics.has_where_clause_predicates { format!( @@ -143,6 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { item.span, "try adding this", &create_new_without_default_suggest_msg( + &attrs_sugg, &self_type_snip, &generics_sugg, &where_clause_sugg, @@ -157,13 +176,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } fn create_new_without_default_suggest_msg( + attrs_sugg: &str, self_type_snip: &str, generics_sugg: &str, where_clause_sugg: &str, ) -> String { #[rustfmt::skip] format!( -"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ +"{attrs_sugg}impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ fn default() -> Self {{ Self::new() }} diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 4eb2a9903b9a8..9a5e90b48065c 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -322,6 +322,74 @@ where } } +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +#[cfg(not(test))] +impl Default for NewWithCfg { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +#[cfg(not(test))] +#[cfg(panic = "unwind")] +impl Default for NewWith2Cfgs { + fn default() -> Self { + Self::new() + } +} + +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl Default for NewWithExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +#[cfg(not(test))] +impl Default for NewWithCfgAndExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + mod issue15778 { pub struct Foo(Vec); diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index cfcff1e3ac3d0..f7466aa321896 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -265,6 +265,46 @@ where } } +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + mod issue15778 { pub struct Foo(Vec); diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index a0b4d3704f56c..1e0d5e2131994 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -174,5 +174,84 @@ LL + } LL + } | -error: aborting due to 9 previous errors +error: you should consider adding a `Default` implementation for `NewWithCfg` + --> tests/ui/new_without_default.rs:273:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfg { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfg { + | + +error: you should consider adding a `Default` implementation for `NewWith2Cfgs` + --> tests/ui/new_without_default.rs:283:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + #[cfg(panic = "unwind")] +LL + impl Default for NewWith2Cfgs { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWith2Cfgs { + | + +error: you should consider adding a `Default` implementation for `NewWithExtraneous` + --> tests/ui/new_without_default.rs:292:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + impl Default for NewWithExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } + | + +error: you should consider adding a `Default` implementation for `NewWithCfgAndExtraneous` + --> tests/ui/new_without_default.rs:302:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfgAndExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfgAndExtraneous { + | + +error: aborting due to 13 previous errors From 505a2084e683662e1521b3ba64e23e9271f8e192 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 29 Sep 2025 22:57:14 +1000 Subject: [PATCH 1064/1900] Split off a separate name/value parser for debuginfo test commands --- src/tools/compiletest/src/directives.rs | 2 +- src/tools/compiletest/src/runtest/debugger.rs | 27 ++++++++++--------- .../compiletest/src/runtest/debuginfo.rs | 6 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index e84a22787668d..e864594ade2e8 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1121,7 +1121,7 @@ impl Config { line.starts_with("no-") && self.parse_name_directive(&line[3..], directive) } - pub fn parse_name_value_directive( + fn parse_name_value_directive( &self, line: &str, directive: &str, diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index ba824124e875d..3d439e98eb7e1 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader}; use camino::{Utf8Path, Utf8PathBuf}; -use crate::common::Config; use crate::runtest::ProcRes; /// Representation of information to invoke a debugger and check its output @@ -20,11 +19,7 @@ pub(super) struct DebuggerCommands { } impl DebuggerCommands { - pub fn parse_from( - file: &Utf8Path, - config: &Config, - debugger_prefix: &str, - ) -> Result { + pub fn parse_from(file: &Utf8Path, debugger_prefix: &str) -> Result { let command_directive = format!("{debugger_prefix}-command"); let check_directive = format!("{debugger_prefix}-check"); @@ -47,14 +42,10 @@ impl DebuggerCommands { continue; }; - if let Some(command) = - config.parse_name_value_directive(&line, &command_directive, file, line_no) - { + if let Some(command) = parse_name_value(&line, &command_directive) { commands.push(command); } - if let Some(pattern) = - config.parse_name_value_directive(&line, &check_directive, file, line_no) - { + if let Some(pattern) = parse_name_value(&line, &check_directive) { check_lines.push((line_no, pattern)); } } @@ -114,6 +105,18 @@ impl DebuggerCommands { } } +/// Split off from the main `parse_name_value_directive`, so that improvements +/// to directive handling aren't held back by debuginfo test commands. +fn parse_name_value(line: &str, name: &str) -> Option { + if let Some(after_name) = line.strip_prefix(name) + && let Some(value) = after_name.strip_prefix(':') + { + Some(value.to_owned()) + } else { + None + } +} + /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match. fn check_single_line(line: &str, check_line: &str) -> bool { // Allow check lines to leave parts unspecified (e.g., uninitialized diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 071c0863b7e9f..9175a38ffa5c9 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -59,7 +59,7 @@ impl TestCx<'_> { } // Parse debugger commands etc from test files - let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "cdb") + let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "cdb") .unwrap_or_else(|e| self.fatal(&e)); // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands @@ -130,7 +130,7 @@ impl TestCx<'_> { } fn run_debuginfo_gdb_test_no_opt(&self) { - let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "gdb") + let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "gdb") .unwrap_or_else(|e| self.fatal(&e)); let mut cmds = dbg_cmds.commands.join("\n"); @@ -397,7 +397,7 @@ impl TestCx<'_> { } // Parse debugger commands etc from test files - let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "lldb") + let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb") .unwrap_or_else(|e| self.fatal(&e)); // Write debugger script: From ffaf607cf2743f1206a739e02f7b194a4767f9a8 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 30 Sep 2025 12:20:42 +1000 Subject: [PATCH 1065/1900] Remove `parse_negative_name_directive` This isn't actually used for anything, and its presence complicates the migration to `DirectiveLine`. --- src/tools/compiletest/src/directives.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index e864594ade2e8..01a663b016f7d 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1117,10 +1117,6 @@ impl Config { && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) } - fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool { - line.starts_with("no-") && self.parse_name_directive(&line[3..], directive) - } - fn parse_name_value_directive( &self, line: &str, @@ -1149,18 +1145,8 @@ impl Config { } fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) { - match value { - true => { - if self.parse_negative_name_directive(line, directive) { - *value = false; - } - } - false => { - if self.parse_name_directive(line, directive) { - *value = true; - } - } - } + // If the flag is already true, don't bother looking at the directive. + *value = *value || self.parse_name_directive(line, directive); } fn set_name_value_directive( From e491056ac5722bd404dd6c326c76f457eb08b467 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 29 Sep 2025 22:25:23 +1000 Subject: [PATCH 1066/1900] Pass around `DirectiveLine` instead of bare strings --- src/tools/compiletest/src/directives.rs | 197 +++++++----------- .../compiletest/src/directives/auxiliary.rs | 35 ++-- src/tools/compiletest/src/directives/cfg.rs | 14 +- src/tools/compiletest/src/directives/needs.rs | 6 +- 4 files changed, 107 insertions(+), 145 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 01a663b016f7d..e6916610190e6 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -63,9 +63,10 @@ impl EarlyProps { &mut poisoned, testfile, rdr, - &mut |DirectiveLine { line_number, raw_directive: ln, .. }| { - parse_and_update_aux(config, ln, testfile, line_number, &mut props.aux); - config.parse_and_update_revisions(testfile, line_number, ln, &mut props.revisions); + // (dummy comment to force args into vertical layout) + &mut |ref ln: DirectiveLine<'_>| { + parse_and_update_aux(config, ln, testfile, &mut props.aux); + config.parse_and_update_revisions(testfile, ln, &mut props.revisions); }, ); @@ -367,8 +368,8 @@ impl TestProps { &mut poisoned, testfile, file, - &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| { - if !directive.applies_to_test_revision(test_revision) { + &mut |ref ln: DirectiveLine<'_>| { + if !ln.applies_to_test_revision(test_revision) { return; } @@ -378,7 +379,6 @@ impl TestProps { ln, ERROR_PATTERN, testfile, - line_number, &mut self.error_patterns, |r| r, ); @@ -386,7 +386,6 @@ impl TestProps { ln, REGEX_ERROR_PATTERN, testfile, - line_number, &mut self.regex_error_patterns, |r| r, ); @@ -395,7 +394,6 @@ impl TestProps { ln, DOC_FLAGS, testfile, - line_number, &mut self.doc_flags, |r| r, ); @@ -414,7 +412,7 @@ impl TestProps { } if let Some(flags) = - config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile, line_number) + config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile) { let flags = split_flags(&flags); for flag in &flags { @@ -425,39 +423,28 @@ impl TestProps { self.compile_flags.extend(flags); } if config - .parse_name_value_directive( - ln, - INCORRECT_COMPILER_FLAGS, - testfile, - line_number, - ) + .parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS, testfile) .is_some() { panic!("`compiler-flags` directive should be spelled `compile-flags`"); } - if let Some(edition) = config.parse_edition(ln, testfile, line_number) { + if let Some(edition) = config.parse_edition(ln, testfile) { // The edition is added at the start, since flags from //@compile-flags must // be passed to rustc last. self.compile_flags.insert(0, format!("--edition={}", edition.trim())); has_edition = true; } - config.parse_and_update_revisions( - testfile, - line_number, - ln, - &mut self.revisions, - ); + config.parse_and_update_revisions(testfile, ln, &mut self.revisions); - if let Some(flags) = - config.parse_name_value_directive(ln, RUN_FLAGS, testfile, line_number) + if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS, testfile) { self.run_flags.extend(split_flags(&flags)); } if self.pp_exact.is_none() { - self.pp_exact = config.parse_pp_exact(ln, testfile, line_number); + self.pp_exact = config.parse_pp_exact(ln, testfile); } config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice); @@ -479,9 +466,7 @@ impl TestProps { ); config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic); - if let Some(m) = - config.parse_name_value_directive(ln, PRETTY_MODE, testfile, line_number) - { + if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE, testfile) { self.pretty_mode = m; } @@ -492,13 +477,12 @@ impl TestProps { ); // Call a helper method to deal with aux-related directives. - parse_and_update_aux(config, ln, testfile, line_number, &mut self.aux); + parse_and_update_aux(config, ln, testfile, &mut self.aux); config.push_name_value_directive( ln, EXEC_ENV, testfile, - line_number, &mut self.exec_env, Config::parse_env, ); @@ -506,7 +490,6 @@ impl TestProps { ln, UNSET_EXEC_ENV, testfile, - line_number, &mut self.unset_exec_env, |r| r.trim().to_owned(), ); @@ -514,7 +497,6 @@ impl TestProps { ln, RUSTC_ENV, testfile, - line_number, &mut self.rustc_env, Config::parse_env, ); @@ -522,7 +504,6 @@ impl TestProps { ln, UNSET_RUSTC_ENV, testfile, - line_number, &mut self.unset_rustc_env, |r| r.trim().to_owned(), ); @@ -530,7 +511,6 @@ impl TestProps { ln, FORBID_OUTPUT, testfile, - line_number, &mut self.forbid_output, |r| r, ); @@ -566,7 +546,7 @@ impl TestProps { } if let Some(code) = config - .parse_name_value_directive(ln, FAILURE_STATUS, testfile, line_number) + .parse_name_value_directive(ln, FAILURE_STATUS, testfile) .and_then(|code| code.trim().parse::().ok()) { self.failure_status = Some(code); @@ -588,7 +568,6 @@ impl TestProps { ln, ASSEMBLY_OUTPUT, testfile, - line_number, &mut self.assembly_output, |r| r.trim().to_string(), ); @@ -602,7 +581,7 @@ impl TestProps { // Unlike the other `name_value_directive`s this needs to be handled manually, // because it sets a `bool` flag. if let Some(known_bug) = - config.parse_name_value_directive(ln, KNOWN_BUG, testfile, line_number) + config.parse_name_value_directive(ln, KNOWN_BUG, testfile) { let known_bug = known_bug.trim(); if known_bug == "unknown" @@ -632,24 +611,20 @@ impl TestProps { ln, TEST_MIR_PASS, testfile, - line_number, &mut self.mir_unit_test, |s| s.trim().to_string(), ); config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base); if let Some(flags) = - config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile, line_number) + config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile) { self.llvm_cov_flags.extend(split_flags(&flags)); } - if let Some(flags) = config.parse_name_value_directive( - ln, - FILECHECK_FLAGS, - testfile, - line_number, - ) { + if let Some(flags) = + config.parse_name_value_directive(ln, FILECHECK_FLAGS, testfile) + { self.filecheck_flags.extend(split_flags(&flags)); } @@ -661,7 +636,6 @@ impl TestProps { ln, directives::CORE_STUBS_COMPILE_FLAGS, testfile, - line_number, ) { let flags = split_flags(&flags); for flag in &flags { @@ -672,12 +646,9 @@ impl TestProps { self.core_stubs_compile_flags.extend(flags); } - if let Some(err_kind) = config.parse_name_value_directive( - ln, - DONT_REQUIRE_ANNOTATIONS, - testfile, - line_number, - ) { + if let Some(err_kind) = + config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS, testfile) + { self.dont_require_annotations .insert(ErrorKind::expect_from_user_str(err_kind.trim())); } @@ -734,7 +705,7 @@ impl TestProps { } } - fn update_fail_mode(&mut self, ln: &str, config: &Config) { + fn update_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) { let check_ui = |mode: &str| { // Mode::Crashes may need build-fail in order to trigger llvm errors or stack overflows if config.mode != TestMode::Ui && config.mode != TestMode::Crashes { @@ -769,7 +740,12 @@ impl TestProps { } } - fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) { + fn update_pass_mode( + &mut self, + ln: &DirectiveLine<'_>, + revision: Option<&str>, + config: &Config, + ) { let check_no_run = |s| match (config.mode, s) { (TestMode::Ui, _) => (), (TestMode::Crashes, _) => (), @@ -814,7 +790,7 @@ impl TestProps { self.pass_mode } - pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) { + fn update_add_core_stubs(&mut self, ln: &DirectiveLine<'_>, config: &Config) { let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS); if add_core_stubs { if !matches!(config.mode, TestMode::Ui | TestMode::Codegen | TestMode::Assembly) { @@ -905,10 +881,12 @@ pub(crate) struct CheckDirectiveResult<'ln> { trailing_directive: Option<&'ln str>, } -pub(crate) fn check_directive<'a>( - directive_ln: &'a str, +fn check_directive<'a>( + directive_ln: &DirectiveLine<'a>, mode: TestMode, ) -> CheckDirectiveResult<'a> { + let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln; + let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, "")); let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name) @@ -980,7 +958,7 @@ fn iter_directives( // Perform unknown directive check on Rust files. if testfile.extension() == Some("rs") { let CheckDirectiveResult { is_known_directive, trailing_directive } = - check_directive(directive_line.raw_directive, mode); + check_directive(&directive_line, mode); if !is_known_directive { *poisoned = true; @@ -1014,8 +992,7 @@ impl Config { fn parse_and_update_revisions( &self, testfile: &Utf8Path, - line_number: usize, - line: &str, + line: &DirectiveLine<'_>, existing: &mut Vec, ) { const FORBIDDEN_REVISION_NAMES: [&str; 2] = [ @@ -1028,8 +1005,7 @@ impl Config { const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] = ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"]; - if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number) - { + if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile) { if self.mode == TestMode::RunMake { panic!("`run-make` mode tests do not support revisions: {}", testfile); } @@ -1074,13 +1050,8 @@ impl Config { (name.to_owned(), value.to_owned()) } - fn parse_pp_exact( - &self, - line: &str, - testfile: &Utf8Path, - line_number: usize, - ) -> Option { - if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile, line_number) { + fn parse_pp_exact(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option { + if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile) { Some(Utf8PathBuf::from(&s)) } else if self.parse_name_directive(line, "pp-exact") { testfile.file_name().map(Utf8PathBuf::from) @@ -1089,7 +1060,9 @@ impl Config { } } - fn parse_custom_normalization(&self, raw_directive: &str) -> Option { + fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option { + let &DirectiveLine { raw_directive, .. } = line; + // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine` // instead of doing it here. let (directive_name, raw_value) = raw_directive.split_once(':')?; @@ -1110,7 +1083,9 @@ impl Config { Some(NormalizeRule { kind, regex, replacement }) } - fn parse_name_directive(&self, line: &str, directive: &str) -> bool { + fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool { + let &DirectiveLine { raw_directive: line, .. } = line; + // Ensure the directive is a whole word. Do not match "ignore-x86" when // the line says "ignore-x86_64". line.starts_with(directive) @@ -1119,11 +1094,12 @@ impl Config { fn parse_name_value_directive( &self, - line: &str, + line: &DirectiveLine<'_>, directive: &str, testfile: &Utf8Path, - line_number: usize, ) -> Option { + let &DirectiveLine { line_number, raw_directive: line, .. } = line; + let colon = directive.len(); if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { let value = line[(colon + 1)..].to_owned(); @@ -1140,42 +1116,37 @@ impl Config { } } - fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option { - self.parse_name_value_directive(line, "edition", testfile, line_number) + fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option { + self.parse_name_value_directive(line, "edition", testfile) } - fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) { + fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { // If the flag is already true, don't bother looking at the directive. *value = *value || self.parse_name_directive(line, directive); } fn set_name_value_directive( &self, - line: &str, + line: &DirectiveLine<'_>, directive: &str, testfile: &Utf8Path, - line_number: usize, value: &mut Option, parse: impl FnOnce(String) -> T, ) { if value.is_none() { - *value = - self.parse_name_value_directive(line, directive, testfile, line_number).map(parse); + *value = self.parse_name_value_directive(line, directive, testfile).map(parse); } } fn push_name_value_directive( &self, - line: &str, + line: &DirectiveLine<'_>, directive: &str, testfile: &Utf8Path, - line_number: usize, values: &mut Vec, parse: impl FnOnce(String) -> T, ) { - if let Some(value) = - self.parse_name_value_directive(line, directive, testfile, line_number).map(parse) - { + if let Some(value) = self.parse_name_value_directive(line, directive, testfile).map(parse) { values.push(value); } } @@ -1468,8 +1439,8 @@ pub(crate) fn make_test_description( &mut local_poisoned, path, src, - &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| { - if !directive.applies_to_test_revision(test_revision) { + &mut |ref ln @ DirectiveLine { line_number, .. }| { + if !ln.applies_to_test_revision(test_revision) { return; } @@ -1493,9 +1464,9 @@ pub(crate) fn make_test_description( decision!(cfg::handle_ignore(config, ln)); decision!(cfg::handle_only(config, ln)); decision!(needs::handle_needs(&cache.needs, config, ln)); - decision!(ignore_llvm(config, path, ln, line_number)); - decision!(ignore_backends(config, path, ln, line_number)); - decision!(needs_backends(config, path, ln, line_number)); + decision!(ignore_llvm(config, path, ln)); + decision!(ignore_backends(config, path, ln)); + decision!(needs_backends(config, path, ln)); decision!(ignore_cdb(config, ln)); decision!(ignore_gdb(config, ln)); decision!(ignore_lldb(config, ln)); @@ -1535,7 +1506,9 @@ pub(crate) fn make_test_description( } } -fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision { +fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { + let &DirectiveLine { raw_directive: line, .. } = line; + if config.debugger != Some(Debugger::Cdb) { return IgnoreDecision::Continue; } @@ -1558,7 +1531,9 @@ fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision { +fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { + let &DirectiveLine { raw_directive: line, .. } = line; + if config.debugger != Some(Debugger::Gdb) { return IgnoreDecision::Continue; } @@ -1606,7 +1581,9 @@ fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { +fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { + let &DirectiveLine { raw_directive: line, .. } = line; + if config.debugger != Some(Debugger::Lldb) { return IgnoreDecision::Continue; } @@ -1628,14 +1605,9 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_backends( - config: &Config, - path: &Utf8Path, - line: &str, - line_number: usize, -) -> IgnoreDecision { +fn ignore_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision { if let Some(backends_to_ignore) = - config.parse_name_value_directive(line, "ignore-backends", path, line_number) + config.parse_name_value_directive(line, "ignore-backends", path) { for backend in backends_to_ignore.split_whitespace().map(|backend| { match CodegenBackend::try_from(backend) { @@ -1655,15 +1627,8 @@ fn ignore_backends( IgnoreDecision::Continue } -fn needs_backends( - config: &Config, - path: &Utf8Path, - line: &str, - line_number: usize, -) -> IgnoreDecision { - if let Some(needed_backends) = - config.parse_name_value_directive(line, "needs-backends", path, line_number) - { +fn needs_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision { + if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends", path) { if !needed_backends .split_whitespace() .map(|backend| match CodegenBackend::try_from(backend) { @@ -1685,9 +1650,9 @@ fn needs_backends( IgnoreDecision::Continue } -fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) -> IgnoreDecision { +fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision { if let Some(needed_components) = - config.parse_name_value_directive(line, "needs-llvm-components", path, line_number) + config.parse_name_value_directive(line, "needs-llvm-components", path) { let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); if let Some(missing_component) = needed_components @@ -1709,7 +1674,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) // Note that these `min` versions will check for not just major versions. if let Some(version_string) = - config.parse_name_value_directive(line, "min-llvm-version", path, line_number) + config.parse_name_value_directive(line, "min-llvm-version", path) { let min_version = extract_llvm_version(&version_string); // Ignore if actual version is smaller than the minimum required version. @@ -1721,7 +1686,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) }; } } else if let Some(version_string) = - config.parse_name_value_directive(line, "max-llvm-major-version", path, line_number) + config.parse_name_value_directive(line, "max-llvm-major-version", path) { let max_version = extract_llvm_version(&version_string); // Ignore if actual major version is larger than the maximum required major version. @@ -1735,7 +1700,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) }; } } else if let Some(version_string) = - config.parse_name_value_directive(line, "min-system-llvm-version", path, line_number) + config.parse_name_value_directive(line, "min-system-llvm-version", path) { let min_version = extract_llvm_version(&version_string); // Ignore if using system LLVM and actual version @@ -1748,7 +1713,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) }; } } else if let Some(version_range) = - config.parse_name_value_directive(line, "ignore-llvm-version", path, line_number) + config.parse_name_value_directive(line, "ignore-llvm-version", path) { // Syntax is: "ignore-llvm-version: [- ]" let (v_min, v_max) = @@ -1774,7 +1739,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) } } } else if let Some(version_string) = - config.parse_name_value_directive(line, "exact-llvm-major-version", path, line_number) + config.parse_name_value_directive(line, "exact-llvm-major-version", path) { // Syntax is "exact-llvm-major-version: " let version = extract_llvm_version(&version_string); diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs index 7c1ed2e700624..0675a6feac3f0 100644 --- a/src/tools/compiletest/src/directives/auxiliary.rs +++ b/src/tools/compiletest/src/directives/auxiliary.rs @@ -7,6 +7,7 @@ use camino::Utf8Path; use super::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO}; use crate::common::Config; +use crate::directives::DirectiveLine; /// Properties parsed from `aux-*` test directives. #[derive(Clone, Debug, Default)] @@ -45,40 +46,28 @@ impl AuxProps { /// and update [`AuxProps`] accordingly. pub(super) fn parse_and_update_aux( config: &Config, - ln: &str, + directive_line: &DirectiveLine<'_>, testfile: &Utf8Path, - line_number: usize, aux: &mut AuxProps, ) { + let &DirectiveLine { raw_directive: ln, .. } = directive_line; + if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) { return; } - config.push_name_value_directive(ln, AUX_BUILD, testfile, line_number, &mut aux.builds, |r| { + let ln = directive_line; + + config.push_name_value_directive(ln, AUX_BUILD, testfile, &mut aux.builds, |r| { r.trim().to_string() }); - config.push_name_value_directive(ln, AUX_BIN, testfile, line_number, &mut aux.bins, |r| { + config + .push_name_value_directive(ln, AUX_BIN, testfile, &mut aux.bins, |r| r.trim().to_string()); + config.push_name_value_directive(ln, AUX_CRATE, testfile, &mut aux.crates, parse_aux_crate); + config.push_name_value_directive(ln, PROC_MACRO, testfile, &mut aux.proc_macros, |r| { r.trim().to_string() }); - config.push_name_value_directive( - ln, - AUX_CRATE, - testfile, - line_number, - &mut aux.crates, - parse_aux_crate, - ); - config.push_name_value_directive( - ln, - PROC_MACRO, - testfile, - line_number, - &mut aux.proc_macros, - |r| r.trim().to_string(), - ); - if let Some(r) = - config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile, line_number) - { + if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile) { aux.codegen_backend = Some(r.trim().to_owned()); } } diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs index 802a1d63d1f20..62a4b88a33a6e 100644 --- a/src/tools/compiletest/src/directives/cfg.rs +++ b/src/tools/compiletest/src/directives/cfg.rs @@ -1,12 +1,14 @@ use std::collections::HashSet; use crate::common::{CompareMode, Config, Debugger}; -use crate::directives::IgnoreDecision; +use crate::directives::{DirectiveLine, IgnoreDecision}; const EXTRA_ARCHS: &[&str] = &["spirv"]; -pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision { +pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { let parsed = parse_cfg_name_directive(config, line, "ignore"); + let &DirectiveLine { raw_directive: line, .. } = line; + match parsed.outcome { MatchOutcome::NoMatch => IgnoreDecision::Continue, MatchOutcome::Match => IgnoreDecision::Ignore { @@ -21,8 +23,10 @@ pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision { } } -pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision { +pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { let parsed = parse_cfg_name_directive(config, line, "only"); + let &DirectiveLine { raw_directive: line, .. } = line; + match parsed.outcome { MatchOutcome::Match => IgnoreDecision::Continue, MatchOutcome::NoMatch => IgnoreDecision::Ignore { @@ -43,9 +47,11 @@ pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision { /// or `only-windows`. fn parse_cfg_name_directive<'a>( config: &Config, - line: &'a str, + line: &'a DirectiveLine<'a>, prefix: &str, ) -> ParsedNameDirective<'a> { + let &DirectiveLine { raw_directive: line, .. } = line; + if !line.as_bytes().starts_with(prefix.as_bytes()) { return ParsedNameDirective::not_a_directive(); } diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 3b7a9478717f4..c8a729d8aab68 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,10 +1,10 @@ use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; -use crate::directives::{IgnoreDecision, llvm_has_libzstd}; +use crate::directives::{DirectiveLine, IgnoreDecision, llvm_has_libzstd}; pub(super) fn handle_needs( cache: &CachedNeedsConditions, config: &Config, - ln: &str, + ln: &DirectiveLine<'_>, ) -> IgnoreDecision { // Note that we intentionally still put the needs- prefix here to make the file show up when // grepping for a directive name, even though we could technically strip that. @@ -181,6 +181,8 @@ pub(super) fn handle_needs( }, ]; + let &DirectiveLine { raw_directive: ln, .. } = ln; + let (name, rest) = match ln.split_once([':', ' ']) { Some((name, rest)) => (name, Some(rest)), None => (ln, None), From 8b51a63dbbe2663403e644009dee9e35f611ae32 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 30 Sep 2025 04:53:35 +0000 Subject: [PATCH 1067/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 29b7717de23f3969ceeb5bef5b01d9223f807655. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 1f90d4e5e498a..70374af27e9c9 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f957826bff7a68b267ce75b1ea56352aed0cca0a +29b7717de23f3969ceeb5bef5b01d9223f807655 From 0fd6f1113b7209d24d4954ef99165aba09ed27f7 Mon Sep 17 00:00:00 2001 From: Tomoaki Kobayashi Date: Thu, 18 Sep 2025 14:23:58 +0900 Subject: [PATCH 1068/1900] Add test for unuseful span in type error in some format_args!() invocations --- .../errors/span-format_args-issue-140578.rs | 31 ++++++++++++ .../span-format_args-issue-140578.stderr | 49 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/ui/errors/span-format_args-issue-140578.rs create mode 100644 tests/ui/errors/span-format_args-issue-140578.stderr diff --git a/tests/ui/errors/span-format_args-issue-140578.rs b/tests/ui/errors/span-format_args-issue-140578.rs new file mode 100644 index 0000000000000..d086fde09f7b4 --- /dev/null +++ b/tests/ui/errors/span-format_args-issue-140578.rs @@ -0,0 +1,31 @@ +fn check_format_args() { + print!("{:?} {a} {a:?}", [], a = 1 + 1); + //~^ ERROR type annotations needed +} + +fn check_format_args_nl() { + println!("{:?} {a} {a:?}", [], a = 1 + 1); + //~^ ERROR type annotations needed +} + +fn check_multi1() { + println!("{:?} {:?} {a} {a:?}", [], [], a = 1 + 1); + //~^ ERROR type annotations needed +} + +fn check_multi2() { + println!("{:?} {:?} {a} {a:?} {b:?}", [], [], a = 1 + 1, b = []); + //~^ ERROR type annotations needed +} + +fn check_unformatted() { + println!(" //~ ERROR type annotations needed + {:?} {:?} +{a} +{a:?}", + [], + [], +a = 1 + 1); +} + +fn main() {} diff --git a/tests/ui/errors/span-format_args-issue-140578.stderr b/tests/ui/errors/span-format_args-issue-140578.stderr new file mode 100644 index 0000000000000..4c19b4919594e --- /dev/null +++ b/tests/ui/errors/span-format_args-issue-140578.stderr @@ -0,0 +1,49 @@ +error[E0282]: type annotations needed + --> $DIR/span-format_args-issue-140578.rs:2:3 + | +LL | print!("{:?} {a} {a:?}", [], a = 1 + 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: this error originates in the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed + --> $DIR/span-format_args-issue-140578.rs:7:3 + | +LL | println!("{:?} {a} {a:?}", [], a = 1 + 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed + --> $DIR/span-format_args-issue-140578.rs:12:3 + | +LL | println!("{:?} {:?} {a} {a:?}", [], [], a = 1 + 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed + --> $DIR/span-format_args-issue-140578.rs:17:3 + | +LL | println!("{:?} {:?} {a} {a:?} {b:?}", [], [], a = 1 + 1, b = []); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed + --> $DIR/span-format_args-issue-140578.rs:22:3 + | +LL | / println!(" +LL | | {:?} {:?} +LL | | {a} +LL | | {a:?}", +LL | | [], +LL | | [], +LL | | a = 1 + 1); + | |__________^ cannot infer type + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0282`. From 1f8bef51e30a087a8b8843c19762952a50087a71 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Tue, 30 Sep 2025 03:04:23 -0500 Subject: [PATCH 1069/1900] fix tuple child creation --- src/etc/lldb_providers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f18baa937e7..3eb964d2fbab9 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -761,7 +761,8 @@ def get_child_index(self, name: str) -> int: def get_child_at_index(self, index: int) -> SBValue: child: SBValue = self.valobj.GetChildAtIndex(index) - return child.CreateChildAtOffset(str(index), 0, child.GetType()) + offset = self.valobj.GetType().GetFieldAtIndex(index).byte_offset + return self.valobj.CreateChildAtOffset(str(index), offset, child.GetType()) def update(self): pass @@ -772,7 +773,7 @@ def has_children(self) -> bool: def get_type_name(self) -> str: name = self.valobj.GetTypeName() # remove "tuple$<" and ">", str.removeprefix and str.removesuffix require python 3.9+ - name = name[7:-1] + name = name[7:-1].strip() return "(" + name + ")" From b13b87a1c3ee3c61b21c4273a87f0b65ccabdde8 Mon Sep 17 00:00:00 2001 From: Tomoaki Kobayashi Date: Thu, 18 Sep 2025 14:42:56 +0900 Subject: [PATCH 1070/1900] Fix unuseful span in type error in some format_args!() invocations --- .../src/error_reporting/traits/mod.rs | 45 +++++++++++++++---- .../errors/span-format_args-issue-140578.rs | 3 +- .../span-format_args-issue-140578.stderr | 42 ++++++++--------- ...745-avoid-expr-from-macro-expansion.stderr | 6 +-- 4 files changed, 60 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index b3d1b8e3888a1..9052031ce4fd8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -21,7 +21,7 @@ use rustc_infer::traits::{ }; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; +use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span}; use tracing::{info, instrument}; pub use self::overflow::*; @@ -154,9 +154,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .collect(); - // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last. + // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last, + // and `Subtype` obligations from `FormatLiteral` desugarings come first. // This lets us display diagnostics with more relevant type information and hide redundant // E0282 errors. + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] + enum ErrorSortKey { + SubtypeFormat(usize, usize), + OtherKind, + SizedTrait, + MetaSizedTrait, + PointeeSizedTrait, + Coerce, + WellFormed, + } errors.sort_by_key(|e| { let maybe_sizedness_did = match e.obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred.def_id()), @@ -165,12 +176,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; match e.obligation.predicate.kind().skip_binder() { - _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => 1, - _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => 2, - _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => 3, - ty::PredicateKind::Coerce(_) => 4, - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 5, - _ => 0, + ty::PredicateKind::Subtype(_) + if matches!( + e.obligation.cause.span.desugaring_kind(), + Some(DesugaringKind::FormatLiteral { .. }) + ) => + { + let (_, row, col, ..) = + self.tcx.sess.source_map().span_to_location_info(e.obligation.cause.span); + ErrorSortKey::SubtypeFormat(row, col) + } + _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => { + ErrorSortKey::SizedTrait + } + _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => { + ErrorSortKey::MetaSizedTrait + } + _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => { + ErrorSortKey::PointeeSizedTrait + } + ty::PredicateKind::Coerce(_) => ErrorSortKey::Coerce, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { + ErrorSortKey::WellFormed + } + _ => ErrorSortKey::OtherKind, } }); diff --git a/tests/ui/errors/span-format_args-issue-140578.rs b/tests/ui/errors/span-format_args-issue-140578.rs index d086fde09f7b4..8c91ded833753 100644 --- a/tests/ui/errors/span-format_args-issue-140578.rs +++ b/tests/ui/errors/span-format_args-issue-140578.rs @@ -19,11 +19,12 @@ fn check_multi2() { } fn check_unformatted() { - println!(" //~ ERROR type annotations needed + println!(" {:?} {:?} {a} {a:?}", [], + //~^ ERROR type annotations needed [], a = 1 + 1); } diff --git a/tests/ui/errors/span-format_args-issue-140578.stderr b/tests/ui/errors/span-format_args-issue-140578.stderr index 4c19b4919594e..6a273e5cd515c 100644 --- a/tests/ui/errors/span-format_args-issue-140578.stderr +++ b/tests/ui/errors/span-format_args-issue-140578.stderr @@ -1,48 +1,42 @@ error[E0282]: type annotations needed - --> $DIR/span-format_args-issue-140578.rs:2:3 + --> $DIR/span-format_args-issue-140578.rs:2:28 | LL | print!("{:?} {a} {a:?}", [], a = 1 + 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type | - = note: this error originates in the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed - --> $DIR/span-format_args-issue-140578.rs:7:3 + --> $DIR/span-format_args-issue-140578.rs:7:30 | LL | println!("{:?} {a} {a:?}", [], a = 1 + 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed - --> $DIR/span-format_args-issue-140578.rs:12:3 + --> $DIR/span-format_args-issue-140578.rs:12:35 | LL | println!("{:?} {:?} {a} {a:?}", [], [], a = 1 + 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed - --> $DIR/span-format_args-issue-140578.rs:17:3 + --> $DIR/span-format_args-issue-140578.rs:17:41 | LL | println!("{:?} {:?} {a} {a:?} {b:?}", [], [], a = 1 + 1, b = []); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed - --> $DIR/span-format_args-issue-140578.rs:22:3 - | -LL | / println!(" -LL | | {:?} {:?} -LL | | {a} -LL | | {a:?}", -LL | | [], -LL | | [], -LL | | a = 1 + 1); - | |__________^ cannot infer type - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $DIR/span-format_args-issue-140578.rs:26:9 + | +LL | [], + | ^^ cannot infer type + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr index a78941f9e11be..3de317d2af6d1 100644 --- a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr @@ -1,10 +1,10 @@ error[E0282]: type annotations needed - --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:5 + --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:22 | LL | println!("{:?}", []); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error From d615d2ff34566c399862cfe0feb96d05767ce5ce Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 11:59:08 +0200 Subject: [PATCH 1071/1900] std: call WinSock cleanup function directly instead of through its reexport --- library/std/src/sys/net/connection/socket/windows.rs | 2 +- library/std/src/sys/pal/windows/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 5b6f4cedf1b77..2221023a21252 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -111,7 +111,7 @@ pub(super) mod netc { } } -pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init}; +pub use crate::sys::pal::winsock::{cvt, cvt_gai, cvt_r, startup as init}; #[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 18ab34982676c..a5f060080130f 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -58,7 +58,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - crate::sys::net::cleanup(); + winsock::cleanup(); } #[inline] From 5139facb365c40e5b0ddedf221cd62347a2a4504 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 30 Sep 2025 12:02:43 +0200 Subject: [PATCH 1072/1900] add tests --- .../src/solve/assembly/mod.rs | 5 +- .../ui/indexing/ambiguity-after-deref-step.rs | 9 +++ .../ambiguity-after-deref-step.stderr | 17 ++++++ .../forced-ambiguity-typenum-ice.rs | 60 +++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/ui/indexing/ambiguity-after-deref-step.rs create mode 100644 tests/ui/indexing/ambiguity-after-deref-step.stderr create mode 100644 tests/ui/traits/next-solver/forced-ambiguity-typenum-ice.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index a2e6ef6f0fe8b..d58c264841c85 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -473,7 +473,10 @@ where // fails to reach a fixpoint but ends up getting an error after // running for some additional step. // - // cc trait-system-refactor-initiative#105 + // FIXME(@lcnr): While I believe an error here to be possible, we + // currently don't have any test which actually triggers it. @lqd + // created a minimization for an ICE in typenum, but that one no + // longer fails here. cc trait-system-refactor-initiative#105. let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood }; self.probe_trait_candidate(source) diff --git a/tests/ui/indexing/ambiguity-after-deref-step.rs b/tests/ui/indexing/ambiguity-after-deref-step.rs new file mode 100644 index 0000000000000..2dd95eed097ca --- /dev/null +++ b/tests/ui/indexing/ambiguity-after-deref-step.rs @@ -0,0 +1,9 @@ +// Regression test making sure that indexing fails with an ambiguity +// error if one of the deref-steps encounters an inference variable. + +fn main() { + let x = &Default::default(); + //~^ ERROR type annotations needed for `&_` + x[1]; + let _: &Vec<()> = x; +} diff --git a/tests/ui/indexing/ambiguity-after-deref-step.stderr b/tests/ui/indexing/ambiguity-after-deref-step.stderr new file mode 100644 index 0000000000000..c7ddd4731c7cb --- /dev/null +++ b/tests/ui/indexing/ambiguity-after-deref-step.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed for `&_` + --> $DIR/ambiguity-after-deref-step.rs:5:9 + | +LL | let x = &Default::default(); + | ^ +LL | +LL | x[1]; + | - type must be known at this point + | +help: consider giving `x` an explicit type, where the placeholders `_` are specified + | +LL | let x: &_ = &Default::default(); + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/forced-ambiguity-typenum-ice.rs b/tests/ui/traits/next-solver/forced-ambiguity-typenum-ice.rs new file mode 100644 index 0000000000000..679d6b1fb165a --- /dev/null +++ b/tests/ui/traits/next-solver/forced-ambiguity-typenum-ice.rs @@ -0,0 +1,60 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#105. We previously encountered +// an ICE in typenum as `forced_ambiguity` failed. While this test no longer causes +// `forced_ambiguity` to error, we still want to use it as a regression test. + +pub struct UInt { + _msb: U, + _lsb: B, +} +pub struct B1; +pub trait Sub { + type Output; +} +impl Sub for UInt, B1> { + type Output = (); +} +impl Sub for UInt +where + U: Sub, + U::Output: Send, +{ + type Output = (); +} + +pub trait Op { + fn op(&self) { + unimplemented!() + } +} +trait OpIf {} + +impl Op, I> for () +where + N: Sub, + (): OpIf, N::Output>, I>, +{ +} +impl OpIf> for () +where + UInt: Sub, + (): Op as Sub>::Output>, +{ +} +impl OpIf for () where R: Sub {} + +pub trait Compute { + type Output; +} + +pub fn repro() +where + UInt: Compute, + as Compute>::Output: Sub, + (): Op, (), ()>, +{ + ().op(); +} +fn main() {} From cd40bbfe2965a42ae8336ebd70fc7fb9c81d22e0 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 29 Sep 2025 20:53:06 +1000 Subject: [PATCH 1073/1900] Move `MetadataKindId` into its own submodule --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 11 +---------- .../rustc_codegen_llvm/src/llvm/metadata_kind.rs | 13 +++++++++++++ compiler/rustc_codegen_llvm/src/llvm/mod.rs | 2 ++ 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index afd2991a09c3a..83867cc2dce66 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -29,6 +29,7 @@ use super::debuginfo::{ DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; +use crate::llvm::MetadataKindId; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. @@ -1035,16 +1036,6 @@ pub(crate) type GetSymbolsCallback = unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void; pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void; -#[derive(Copy, Clone)] -#[repr(transparent)] -pub(crate) struct MetadataKindId(c_uint); - -impl From for MetadataKindId { - fn from(value: MetadataType) -> Self { - Self(value as c_uint) - } -} - unsafe extern "C" { // Create and destroy contexts. pub(crate) fn LLVMContextDispose(C: &'static mut Context); diff --git a/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs new file mode 100644 index 0000000000000..926eb78935205 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs @@ -0,0 +1,13 @@ +use libc::c_uint; + +use crate::llvm::MetadataType; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub(crate) struct MetadataKindId(c_uint); + +impl From for MetadataKindId { + fn from(value: MetadataType) -> Self { + Self(value as c_uint) + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 1115d82fa85d3..2333fbe7a7656 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -13,11 +13,13 @@ pub(crate) use self::CallConv::*; pub(crate) use self::CodeGenOptSize::*; pub(crate) use self::MetadataType::*; pub(crate) use self::ffi::*; +pub(crate) use self::metadata_kind::MetadataKindId; use crate::common::AsCCharPtr; pub(crate) mod diagnostic; pub(crate) mod enzyme_ffi; mod ffi; +mod metadata_kind; pub(crate) use self::enzyme_ffi::*; From 906bf49ade52d7485a6c8a0756f020ade3a342b6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 29 Sep 2025 20:21:33 +1000 Subject: [PATCH 1074/1900] Declare all "fixed" metadata kinds as `MetadataKindId` --- .../src/llvm/metadata_kind.rs | 64 +++++++++++++++++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 52 +++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs index 926eb78935205..4d00356c61627 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs @@ -11,3 +11,67 @@ impl From for MetadataKindId { Self(value as c_uint) } } + +macro_rules! declare_fixed_metadata_kinds { + ( + $( + FIXED_MD_KIND($variant:ident, $value:literal) + )* + ) => { + // Use a submodule to group all declarations into one `#[expect(..)]`. + #[expect(dead_code)] + mod fixed_kinds { + use super::MetadataKindId; + $( + #[expect(non_upper_case_globals)] + pub(crate) const $variant: MetadataKindId = MetadataKindId($value); + )* + } + }; +} + +// Must be kept in sync with the corresponding static assertions in `RustWrapper.cpp`. +declare_fixed_metadata_kinds! { + FIXED_MD_KIND(MD_dbg, 0) + FIXED_MD_KIND(MD_tbaa, 1) + FIXED_MD_KIND(MD_prof, 2) + FIXED_MD_KIND(MD_fpmath, 3) + FIXED_MD_KIND(MD_range, 4) + FIXED_MD_KIND(MD_tbaa_struct, 5) + FIXED_MD_KIND(MD_invariant_load, 6) + FIXED_MD_KIND(MD_alias_scope, 7) + FIXED_MD_KIND(MD_noalias, 8) + FIXED_MD_KIND(MD_nontemporal, 9) + FIXED_MD_KIND(MD_mem_parallel_loop_access, 10) + FIXED_MD_KIND(MD_nonnull, 11) + FIXED_MD_KIND(MD_dereferenceable, 12) + FIXED_MD_KIND(MD_dereferenceable_or_null, 13) + FIXED_MD_KIND(MD_make_implicit, 14) + FIXED_MD_KIND(MD_unpredictable, 15) + FIXED_MD_KIND(MD_invariant_group, 16) + FIXED_MD_KIND(MD_align, 17) + FIXED_MD_KIND(MD_loop, 18) + FIXED_MD_KIND(MD_type, 19) + FIXED_MD_KIND(MD_section_prefix, 20) + FIXED_MD_KIND(MD_absolute_symbol, 21) + FIXED_MD_KIND(MD_associated, 22) + FIXED_MD_KIND(MD_callees, 23) + FIXED_MD_KIND(MD_irr_loop, 24) + FIXED_MD_KIND(MD_access_group, 25) + FIXED_MD_KIND(MD_callback, 26) + FIXED_MD_KIND(MD_preserve_access_index, 27) + FIXED_MD_KIND(MD_vcall_visibility, 28) + FIXED_MD_KIND(MD_noundef, 29) + FIXED_MD_KIND(MD_annotation, 30) + FIXED_MD_KIND(MD_nosanitize, 31) + FIXED_MD_KIND(MD_func_sanitize, 32) + FIXED_MD_KIND(MD_exclude, 33) + FIXED_MD_KIND(MD_memprof, 34) + FIXED_MD_KIND(MD_callsite, 35) + FIXED_MD_KIND(MD_kcfi_type, 36) + FIXED_MD_KIND(MD_pcsections, 37) + FIXED_MD_KIND(MD_DIAssignID, 38) + FIXED_MD_KIND(MD_coro_outside_frame, 39) + FIXED_MD_KIND(MD_mmra, 40) + FIXED_MD_KIND(MD_noalias_addrspace, 41) +} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 4a77812591893..2b83ea24ac61c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1824,3 +1824,55 @@ extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return 6; // Default fallback depth } #endif + +// Statically assert that the fixed metadata kind IDs declared in +// `metadata_kind.rs` match the ones actually used by LLVM. +#define FIXED_MD_KIND(VARIANT, VALUE) \ + static_assert(::llvm::LLVMContext::VARIANT == VALUE); +// Must be kept in sync with the corresponding list in `metadata_kind.rs`. +FIXED_MD_KIND(MD_dbg, 0) +FIXED_MD_KIND(MD_tbaa, 1) +FIXED_MD_KIND(MD_prof, 2) +FIXED_MD_KIND(MD_fpmath, 3) +FIXED_MD_KIND(MD_range, 4) +FIXED_MD_KIND(MD_tbaa_struct, 5) +FIXED_MD_KIND(MD_invariant_load, 6) +FIXED_MD_KIND(MD_alias_scope, 7) +FIXED_MD_KIND(MD_noalias, 8) +FIXED_MD_KIND(MD_nontemporal, 9) +FIXED_MD_KIND(MD_mem_parallel_loop_access, 10) +FIXED_MD_KIND(MD_nonnull, 11) +FIXED_MD_KIND(MD_dereferenceable, 12) +FIXED_MD_KIND(MD_dereferenceable_or_null, 13) +FIXED_MD_KIND(MD_make_implicit, 14) +FIXED_MD_KIND(MD_unpredictable, 15) +FIXED_MD_KIND(MD_invariant_group, 16) +FIXED_MD_KIND(MD_align, 17) +FIXED_MD_KIND(MD_loop, 18) +FIXED_MD_KIND(MD_type, 19) +FIXED_MD_KIND(MD_section_prefix, 20) +FIXED_MD_KIND(MD_absolute_symbol, 21) +FIXED_MD_KIND(MD_associated, 22) +FIXED_MD_KIND(MD_callees, 23) +FIXED_MD_KIND(MD_irr_loop, 24) +FIXED_MD_KIND(MD_access_group, 25) +FIXED_MD_KIND(MD_callback, 26) +FIXED_MD_KIND(MD_preserve_access_index, 27) +FIXED_MD_KIND(MD_vcall_visibility, 28) +FIXED_MD_KIND(MD_noundef, 29) +FIXED_MD_KIND(MD_annotation, 30) +FIXED_MD_KIND(MD_nosanitize, 31) +FIXED_MD_KIND(MD_func_sanitize, 32) +FIXED_MD_KIND(MD_exclude, 33) +FIXED_MD_KIND(MD_memprof, 34) +FIXED_MD_KIND(MD_callsite, 35) +FIXED_MD_KIND(MD_kcfi_type, 36) +FIXED_MD_KIND(MD_pcsections, 37) +FIXED_MD_KIND(MD_DIAssignID, 38) +FIXED_MD_KIND(MD_coro_outside_frame, 39) +FIXED_MD_KIND(MD_mmra, 40) +FIXED_MD_KIND(MD_noalias_addrspace, 41) +// If some fixed metadata kinds are not present and consistent in all supported +// LLVM versions, it's fine to omit them from this list; in that case Rust-side +// code cannot declare them as fixed IDs and must look them up by name instead. +#undef FIXED_MD_KIND From cc6329a9bcd78c4531e9713775e2936fe223e601 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 30 Sep 2025 20:05:23 +1000 Subject: [PATCH 1075/1900] Replace `MetadataType` with the `MetadataKindId` constants --- compiler/rustc_codegen_llvm/src/context.rs | 6 ++-- .../src/debuginfo/metadata.rs | 8 ++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 33 ++++--------------- .../src/llvm/metadata_kind.rs | 8 +---- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 3 +- compiler/rustc_codegen_llvm/src/type_.rs | 8 ++--- 6 files changed, 17 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index aa5c17269fb84..b1da6f7c74061 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -34,7 +34,7 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; -use crate::llvm::Metadata; +use crate::llvm::{Metadata, MetadataKindId}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; @@ -1006,11 +1006,11 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn set_metadata<'a>( &self, val: &'a Value, - kind_id: impl Into, + kind_id: MetadataKindId, md: &'ll Metadata, ) { let node = self.get_metadata_value(md); - llvm::LLVMSetMetadata(val, kind_id.into(), node); + llvm::LLVMSetMetadata(val, kind_id, node); } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 1e4ace4ca922c..bc20c75941349 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1611,16 +1611,12 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; llvm::LLVMRustGlobalAddMetadata( vtable, - llvm::MD_type as c_uint, + llvm::MD_type, llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()), ); let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); - llvm::LLVMGlobalSetMetadata( - vtable, - llvm::MetadataType::MD_vcall_visibility as c_uint, - vcall_visibility_metadata, - ); + llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 83867cc2dce66..f17de168ca434 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -514,31 +514,6 @@ pub(crate) enum FileType { ObjectFile, } -/// LLVMMetadataType -#[derive(Copy, Clone)] -#[repr(C)] -#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")] -pub(crate) enum MetadataType { - MD_dbg = 0, - MD_tbaa = 1, - MD_prof = 2, - MD_fpmath = 3, - MD_range = 4, - MD_tbaa_struct = 5, - MD_invariant_load = 6, - MD_alias_scope = 7, - MD_noalias = 8, - MD_nontemporal = 9, - MD_mem_parallel_loop_access = 10, - MD_nonnull = 11, - MD_unpredictable = 15, - MD_align = 17, - MD_type = 19, - MD_vcall_visibility = 28, - MD_noundef = 29, - MD_kcfi_type = 36, -} - /// Must match the layout of `LLVMInlineAsmDialect`. #[derive(Copy, Clone, PartialEq)] #[repr(C)] @@ -1130,7 +1105,11 @@ unsafe extern "C" { pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value); - pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); + pub(crate) fn LLVMGlobalSetMetadata<'a>( + Val: &'a Value, + KindID: MetadataKindId, + Metadata: &'a Metadata, + ); pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type @@ -2050,7 +2029,7 @@ unsafe extern "C" { // Operations on all values pub(crate) fn LLVMRustGlobalAddMetadata<'a>( Val: &'a Value, - KindID: c_uint, + KindID: MetadataKindId, Metadata: &'a Metadata, ); pub(crate) fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool; diff --git a/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs index 4d00356c61627..a8a671b5c85f1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/metadata_kind.rs @@ -1,17 +1,11 @@ use libc::c_uint; -use crate::llvm::MetadataType; +pub(crate) use self::fixed_kinds::*; #[derive(Copy, Clone)] #[repr(transparent)] pub(crate) struct MetadataKindId(c_uint); -impl From for MetadataKindId { - fn from(value: MetadataType) -> Self { - Self(value as c_uint) - } -} - macro_rules! declare_fixed_metadata_kinds { ( $( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2333fbe7a7656..9a53dacb1dfdd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,9 +11,8 @@ use rustc_llvm::RustString; pub(crate) use self::CallConv::*; pub(crate) use self::CodeGenOptSize::*; -pub(crate) use self::MetadataType::*; pub(crate) use self::ffi::*; -pub(crate) use self::metadata_kind::MetadataKindId; +pub(crate) use self::metadata_kind::*; use crate::common::AsCCharPtr; pub(crate) mod diagnostic; diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 9ecaf5f24fe15..5b97898a4b888 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -306,7 +306,7 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMRustGlobalAddMetadata( function, - llvm::MD_type as c_uint, + llvm::MD_type, llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), ) } @@ -318,7 +318,7 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMGlobalSetMetadata( function, - llvm::MD_type as c_uint, + llvm::MD_type, llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), ) } @@ -333,7 +333,7 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMRustGlobalAddMetadata( function, - llvm::MD_kcfi_type as c_uint, + llvm::MD_kcfi_type, llvm::LLVMMDNodeInContext2( self.llcx, &llvm::LLVMValueAsMetadata(kcfi_type_metadata), @@ -348,7 +348,7 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMGlobalSetMetadata( function, - llvm::MD_kcfi_type as c_uint, + llvm::MD_kcfi_type, llvm::LLVMMDNodeInContext2( self.llcx, &llvm::LLVMValueAsMetadata(kcfi_type_metadata), From b2071862aed0360da92dbc42d8c2818af4f37bc8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 30 Sep 2025 10:36:23 +0300 Subject: [PATCH 1076/1900] Migrate inference to next solver --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/hir-def/src/expr_store/path.rs | 2 +- .../crates/hir-ty/src/autoderef.rs | 56 +- .../crates/hir-ty/src/builder.rs | 16 +- .../crates/hir-ty/src/chalk_db.rs | 15 +- .../crates/hir-ty/src/consteval.rs | 102 +- .../crates/hir-ty/src/consteval/tests.rs | 8 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 20 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 72 +- .../crates/hir-ty/src/diagnostics/expr.rs | 46 +- .../hir-ty/src/diagnostics/match_check.rs | 37 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 24 +- .../crates/hir-ty/src/display.rs | 11 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 13 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 1246 +++++++---------- .../crates/hir-ty/src/infer/autoderef.rs | 14 +- .../crates/hir-ty/src/infer/cast.rs | 347 +++-- .../crates/hir-ty/src/infer/closure.rs | 78 +- .../hir-ty/src/infer/closure/analysis.rs | 264 ++-- .../crates/hir-ty/src/infer/coerce.rs | 240 ++-- .../crates/hir-ty/src/infer/diagnostics.rs | 46 +- .../crates/hir-ty/src/infer/expr.rs | 1244 +++++++--------- .../crates/hir-ty/src/infer/fallback.rs | 4 +- .../crates/hir-ty/src/infer/mutability.rs | 43 +- .../crates/hir-ty/src/infer/pat.rs | 323 +++-- .../crates/hir-ty/src/infer/path.rs | 217 ++- .../crates/hir-ty/src/infer/unify.rs | 964 ++++--------- .../rust-analyzer/crates/hir-ty/src/layout.rs | 17 +- .../crates/hir-ty/src/layout/tests.rs | 13 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 123 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 38 +- .../crates/hir-ty/src/lower/path.rs | 190 +-- .../crates/hir-ty/src/lower_nextsolver.rs | 176 ++- .../hir-ty/src/lower_nextsolver/path.rs | 57 +- .../crates/hir-ty/src/method_resolution.rs | 740 +++++----- .../rust-analyzer/crates/hir-ty/src/mir.rs | 6 +- .../crates/hir-ty/src/mir/borrowck.rs | 29 +- .../crates/hir-ty/src/mir/eval.rs | 226 +-- .../crates/hir-ty/src/mir/eval/shim.rs | 33 +- .../crates/hir-ty/src/mir/eval/shim/simd.rs | 6 +- .../crates/hir-ty/src/mir/eval/tests.rs | 4 +- .../crates/hir-ty/src/mir/lower.rs | 196 +-- .../crates/hir-ty/src/mir/lower/as_place.rs | 33 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 69 +- .../crates/hir-ty/src/mir/lower/tests.rs | 11 +- .../crates/hir-ty/src/mir/monomorphization.rs | 44 +- .../crates/hir-ty/src/next_solver.rs | 3 + .../crates/hir-ty/src/next_solver/def_id.rs | 11 + .../crates/hir-ty/src/next_solver/fold.rs | 23 + .../hir-ty/src/next_solver/generic_arg.rs | 68 +- .../hir-ty/src/next_solver/infer/mod.rs | 116 +- .../hir-ty/src/next_solver/infer/resolve.rs | 52 +- .../hir-ty/src/next_solver/infer/select.rs | 78 ++ .../src/next_solver/infer/snapshot/mod.rs | 13 + .../hir-ty/src/next_solver/infer/traits.rs | 36 +- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- .../crates/hir-ty/src/next_solver/mapping.rs | 40 + .../crates/hir-ty/src/next_solver/region.rs | 12 +- .../crates/hir-ty/src/next_solver/ty.rs | 67 +- .../crates/hir-ty/src/next_solver/util.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 512 +++---- .../hir-ty/src/tests/closure_captures.rs | 15 +- .../crates/hir-ty/src/tests/coercion.rs | 16 +- .../hir-ty/src/tests/display_source_code.rs | 8 +- .../crates/hir-ty/src/tests/incremental.rs | 19 +- .../crates/hir-ty/src/tests/macros.rs | 8 +- .../hir-ty/src/tests/method_resolution.rs | 6 +- .../crates/hir-ty/src/tests/opaque_types.rs | 1 + .../crates/hir-ty/src/tests/regression.rs | 8 +- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 35 +- .../crates/hir-ty/src/tests/traits.rs | 54 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 35 +- .../crates/hir/src/diagnostics.rs | 29 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 74 +- .../rust-analyzer/crates/hir/src/semantics.rs | 32 +- .../crates/hir/src/source_analyzer.rs | 188 ++- .../src/handlers/convert_closure_to_fn.rs | 2 +- .../crates/ide-completion/src/render.rs | 2 +- .../src/handlers/type_mismatch.rs | 2 +- .../crates/ide-ssr/src/matching.rs | 60 +- .../rust-analyzer/crates/ide-ssr/src/tests.rs | 146 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 4 +- .../rust-analyzer/crates/ide/src/interpret.rs | 2 +- .../crates/ide/src/static_index.rs | 111 +- .../crates/ide/src/syntax_highlighting.rs | 9 +- .../crates/ide/src/view_memory_layout.rs | 26 +- .../crates/rust-analyzer/Cargo.toml | 2 + .../rust-analyzer/src/cli/analysis_stats.rs | 61 +- .../crates/rust-analyzer/src/lib.rs | 2 + 90 files changed, 4558 insertions(+), 4902 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 086f38f06a52d..5a0d209602dfe 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2040,6 +2040,7 @@ dependencies = [ "process-wrap", "profile", "project-model", + "ra-ap-rustc_type_ir", "rayon", "rustc-hash 2.1.1", "scip", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs index 55e738b58bd00..fbbaa587b095d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs @@ -88,7 +88,7 @@ pub struct AssociatedTypeBinding { } /// A single generic argument. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum GenericArg { Type(TypeRefId), Lifetime(LifetimeRefId), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 21a86d3e43727..0be00afe7bf45 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -5,12 +5,15 @@ use std::fmt; +use hir_def::TraitId; use hir_def::{TypeAliasId, lang_item::LangItem}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use tracing::debug; use triomphe::Arc; +use crate::next_solver::TraitRef; use crate::next_solver::infer::InferOk; +use crate::next_solver::infer::traits::Obligation; use crate::{ TraitEnvironment, db::HirDatabase, @@ -38,14 +41,14 @@ pub fn autoderef<'db>( ty: crate::Canonical, ) -> impl Iterator + use<> { let mut table = InferenceTable::new(db, env); - let interner = table.interner; - let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty.to_nextsolver(interner)); + let interner = table.interner(); + let ty = table.instantiate_canonical(ty.to_nextsolver(interner)); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be // resolved, just replace with fallback type. - let resolved = autoderef.table.resolve_completely(ty.to_chalk(interner)); + let resolved = autoderef.table.resolve_completely(ty).to_chalk(interner); // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. @@ -101,6 +104,7 @@ struct AutoderefSnapshot<'db, Steps> { #[derive(Clone, Copy)] struct AutoderefTraits { + trait_: TraitId, trait_target: TypeAliasId, } @@ -215,16 +219,26 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { Some(it) => Some(*it), None => { let traits = if self.use_receiver_trait { - AutoderefTraits { - trait_target: LangItem::ReceiverTarget - .resolve_type_alias(self.table.db, self.table.trait_env.krate) - .or_else(|| { - LangItem::DerefTarget - .resolve_type_alias(self.table.db, self.table.trait_env.krate) - })?, - } + (|| { + Some(AutoderefTraits { + trait_: LangItem::Receiver + .resolve_trait(self.table.db, self.table.trait_env.krate)?, + trait_target: LangItem::ReceiverTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + }) + })() + .or_else(|| { + Some(AutoderefTraits { + trait_: LangItem::Deref + .resolve_trait(self.table.db, self.table.trait_env.krate)?, + trait_target: LangItem::DerefTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + }) + })? } else { AutoderefTraits { + trait_: LangItem::Deref + .resolve_trait(self.table.db, self.table.trait_env.krate)?, trait_target: LangItem::DerefTarget .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, } @@ -236,10 +250,22 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option> { debug!("overloaded_deref_ty({:?})", ty); - let interner = self.table.interner; + let interner = self.table.interner(); // , or whatever the equivalent trait is that we've been asked to walk. - let AutoderefTraits { trait_target } = self.autoderef_traits()?; + let AutoderefTraits { trait_, trait_target } = self.autoderef_traits()?; + + let trait_ref = TraitRef::new(interner, trait_.into(), [ty]); + let obligation = + Obligation::new(interner, ObligationCause::new(), self.table.trait_env.env, trait_ref); + // We detect whether the self type implements `Deref` before trying to + // structurally normalize. We use `predicate_may_hold_opaque_types_jank` + // to support not-yet-defined opaque types. It will succeed for `impl Deref` + // but fail for `impl OtherTrait`. + if !self.table.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation) { + debug!("overloaded_deref_ty: cannot match obligation"); + return None; + } let (normalized_ty, obligations) = structurally_normalize_ty( self.table, @@ -316,7 +342,7 @@ pub(crate) fn overloaded_deref_ty<'db>( table: &InferenceTable<'db>, ty: Ty<'db>, ) -> Option>> { - let interner = table.interner; + let interner = table.interner(); let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 5511587c71a46..798b0f2c0c246 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -15,7 +15,10 @@ use crate::{ error_lifetime, generics::generics, infer::unify::InferenceTable, - next_solver::{DbInterner, EarlyBinder, mapping::ChalkToNextSolver}, + next_solver::{ + DbInterner, EarlyBinder, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, primitive, to_assoc_type_id, to_chalk_trait_id, }; @@ -141,10 +144,13 @@ impl TyBuilder { #[tracing::instrument(skip_all)] pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self { - self.fill(|x| match x { - ParamKind::Type => table.new_type_var().cast(Interner), - ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), - ParamKind::Lifetime => table.new_lifetime_var().cast(Interner), + self.fill(|x| { + match x { + ParamKind::Type => crate::next_solver::GenericArg::Ty(table.next_ty_var()), + ParamKind::Const(_) => table.next_const_var().into(), + ParamKind::Lifetime => table.next_region_var().into(), + } + .to_chalk(table.interner()) }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 546991cf6571e..3d06b52106709 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -2,7 +2,7 @@ //! about the code that Chalk needs. use hir_def::{CallableDefId, GenericDefId}; -use crate::{Interner, Substitution, db::HirDatabase, mapping::from_chalk}; +use crate::{Interner, db::HirDatabase, mapping::from_chalk}; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; @@ -53,16 +53,3 @@ pub(crate) fn adt_variance_query(db: &dyn HirDatabase, adt_id: hir_def::AdtId) - }), ) } - -/// Returns instantiated predicates. -pub(super) fn convert_where_clauses( - db: &dyn HirDatabase, - def: GenericDefId, - substs: &Substitution, -) -> Vec> { - db.generic_predicates(def) - .iter() - .cloned() - .map(|pred| pred.substitute(Interner, substs)) - .collect() -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index b2daed425ef1c..8b12c5fd890c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -4,8 +4,8 @@ use base_db::Crate; use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast}; use hir_def::{ EnumVariantId, GeneralConstId, HasModule as _, StaticId, - expr_store::{Body, HygieneId, path::Path}, - hir::{Expr, ExprId}, + expr_store::{HygieneId, path::Path}, + hir::Expr, resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, }; @@ -19,13 +19,12 @@ use crate::{ db::HirDatabase, display::DisplayTarget, generics::Generics, - infer::InferenceContext, lower::ParamLoweringMode, next_solver::{DbInterner, mapping::ChalkToNextSolver}, to_placeholder_idx, }; -use super::mir::{MirEvalError, MirLowerError, interpret_mir, lower_to_mir, pad16}; +use super::mir::{MirEvalError, MirLowerError, interpret_mir, pad16}; /// Extension trait for [`Const`] pub trait ConstExt { @@ -56,12 +55,12 @@ impl ConstExt for Const { } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum ConstEvalError { - MirLowerError(MirLowerError), - MirEvalError(MirEvalError), +pub enum ConstEvalError<'db> { + MirLowerError(MirLowerError<'db>), + MirEvalError(MirEvalError<'db>), } -impl ConstEvalError { +impl ConstEvalError<'_> { pub fn pretty_print( &self, f: &mut String, @@ -80,8 +79,8 @@ impl ConstEvalError { } } -impl From for ConstEvalError { - fn from(value: MirLowerError) -> Self { +impl<'db> From> for ConstEvalError<'db> { + fn from(value: MirLowerError<'db>) -> Self { match value { MirLowerError::ConstEvalError(_, e) => *e, _ => ConstEvalError::MirLowerError(value), @@ -89,8 +88,8 @@ impl From for ConstEvalError { } } -impl From for ConstEvalError { - fn from(value: MirEvalError) -> Self { +impl<'db> From> for ConstEvalError<'db> { + fn from(value: MirEvalError<'db>) -> Self { ConstEvalError::MirEvalError(value) } } @@ -225,35 +224,35 @@ pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { } } -pub(crate) fn const_eval_cycle_result( - _: &dyn HirDatabase, +pub(crate) fn const_eval_cycle_result<'db>( + _: &'db dyn HirDatabase, _: GeneralConstId, _: Substitution, - _: Option>>, -) -> Result { + _: Option>>, +) -> Result> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } -pub(crate) fn const_eval_static_cycle_result( - _: &dyn HirDatabase, +pub(crate) fn const_eval_static_cycle_result<'db>( + _: &'db dyn HirDatabase, _: StaticId, -) -> Result { +) -> Result> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } -pub(crate) fn const_eval_discriminant_cycle_result( - _: &dyn HirDatabase, +pub(crate) fn const_eval_discriminant_cycle_result<'db>( + _: &'db dyn HirDatabase, _: EnumVariantId, -) -> Result { +) -> Result> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } -pub(crate) fn const_eval_query( - db: &dyn HirDatabase, +pub(crate) fn const_eval_query<'db>( + db: &'db dyn HirDatabase, def: GeneralConstId, subst: Substitution, - trait_env: Option>>, -) -> Result { + trait_env: Option>>, +) -> Result> { let body = match def { GeneralConstId::ConstId(c) => { db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))? @@ -267,10 +266,10 @@ pub(crate) fn const_eval_query( Ok(c) } -pub(crate) fn const_eval_static_query( - db: &dyn HirDatabase, +pub(crate) fn const_eval_static_query<'db>( + db: &'db dyn HirDatabase, def: StaticId, -) -> Result { +) -> Result> { let body = db.monomorphized_mir_body( def.into(), Substitution::empty(Interner), @@ -280,10 +279,10 @@ pub(crate) fn const_eval_static_query( Ok(c) } -pub(crate) fn const_eval_discriminant_variant( - db: &dyn HirDatabase, +pub(crate) fn const_eval_discriminant_variant<'db>( + db: &'db dyn HirDatabase, variant_id: EnumVariantId, -) -> Result { +) -> Result> { let def = variant_id.into(); let body = db.body(def); let loc = variant_id.lookup(db); @@ -317,44 +316,5 @@ pub(crate) fn const_eval_discriminant_variant( Ok(c) } -// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should -// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here -// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. -pub(crate) fn eval_to_const( - expr: ExprId, - mode: ParamLoweringMode, - ctx: &mut InferenceContext<'_>, - debruijn: DebruijnIndex, -) -> Const { - let db = ctx.db; - let infer = ctx.fixme_resolve_all_clone(); - fn has_closure(body: &Body, expr: ExprId) -> bool { - if matches!(body[expr], Expr::Closure { .. }) { - return true; - } - let mut r = false; - body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); - r - } - if has_closure(ctx.body, expr) { - // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. - return unknown_const(infer[expr].clone()); - } - if let Expr::Path(p) = &ctx.body[expr] { - let resolver = &ctx.resolver; - if let Some(c) = - path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone()) - { - return c; - } - } - if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) - && let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) - { - return result; - } - unknown_const(infer[expr].clone()) -} - #[cfg(test)] mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 1586846bbe58d..5a214eabcdbd4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -21,7 +21,7 @@ use super::{ mod intrinsics; -fn simplify(e: ConstEvalError) -> ConstEvalError { +fn simplify(e: ConstEvalError<'_>) -> ConstEvalError<'_> { match e { ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => { simplify(ConstEvalError::MirEvalError(*e)) @@ -33,7 +33,7 @@ fn simplify(e: ConstEvalError) -> ConstEvalError { #[track_caller] fn check_fail( #[rust_analyzer::rust_fixture] ra_fixture: &str, - error: impl FnOnce(ConstEvalError) -> bool, + error: impl FnOnce(ConstEvalError<'_>) -> bool, ) { let (db, file_id) = TestDB::with_single_file(ra_fixture); salsa::attach(&db, || match eval_goal(&db, file_id) { @@ -100,7 +100,7 @@ fn check_answer( }); } -fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String { +fn pretty_print_err(e: ConstEvalError<'_>, db: &TestDB) -> String { let mut err = String::new(); let span_formatter = |file, range| format!("{file:?} {range:?}"); let display_target = @@ -117,7 +117,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String { err } -fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result { +fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result> { let _tracing = setup_tracing(); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 155f1336e41d4..2509ba2ef2603 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -178,10 +178,10 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< } } -pub(crate) fn const_eval_discriminant_variant( - db: &dyn HirDatabase, +pub(crate) fn const_eval_discriminant_variant<'db>( + db: &'db dyn HirDatabase, variant_id: EnumVariantId, -) -> Result { +) -> Result> { let interner = DbInterner::new_with(db, None, None); let def = variant_id.into(); let body = db.body(def); @@ -220,7 +220,7 @@ pub(crate) fn const_eval_discriminant_variant( // FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should // get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here // and make this function private. See the fixme comment on `InferenceContext::resolve_all`. -pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> { +pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> { let interner = DbInterner::new_with(ctx.db, None, None); let infer = ctx.fixme_resolve_all_clone(); fn has_closure(body: &Body, expr: ExprId) -> bool { @@ -233,17 +233,11 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) } if has_closure(ctx.body, expr) { // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. - return unknown_const(infer[expr].clone().to_nextsolver(interner)); + return unknown_const(infer[expr]); } if let Expr::Path(p) = &ctx.body[expr] { let resolver = &ctx.resolver; - if let Some(c) = path_to_const( - ctx.db, - resolver, - p, - || ctx.generics(), - infer[expr].to_nextsolver(interner), - ) { + if let Some(c) = path_to_const(ctx.db, resolver, p, || ctx.generics(), infer[expr]) { return c; } } @@ -252,5 +246,5 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) { return result.to_nextsolver(interner); } - unknown_const(infer[expr].to_nextsolver(interner)) + unknown_const(infer[expr]) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 71fb3d44fb707..82d17cc618cd2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -32,62 +32,71 @@ use crate::{ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::infer::infer_query)] #[salsa::cycle(cycle_result = crate::infer::infer_cycle_result)] - fn infer(&self, def: DefWithBodyId) -> Arc; + fn infer<'db>(&'db self, def: DefWithBodyId) -> Arc>; // region:mir #[salsa::invoke(crate::mir::mir_body_query)] #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)] - fn mir_body(&self, def: DefWithBodyId) -> Result, MirLowerError>; + fn mir_body<'db>(&'db self, def: DefWithBodyId) -> Result, MirLowerError<'db>>; #[salsa::invoke(crate::mir::mir_body_for_closure_query)] - fn mir_body_for_closure(&self, def: InternedClosureId) -> Result, MirLowerError>; + fn mir_body_for_closure<'db>( + &'db self, + def: InternedClosureId, + ) -> Result, MirLowerError<'db>>; #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)] - fn monomorphized_mir_body( - &self, + fn monomorphized_mir_body<'db>( + &'db self, def: DefWithBodyId, subst: Substitution, - env: Arc>, - ) -> Result, MirLowerError>; + env: Arc>, + ) -> Result, MirLowerError<'db>>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] - fn monomorphized_mir_body_for_closure( - &self, + fn monomorphized_mir_body_for_closure<'db>( + &'db self, def: InternedClosureId, subst: Substitution, - env: Arc>, - ) -> Result, MirLowerError>; + env: Arc>, + ) -> Result, MirLowerError<'db>>; #[salsa::invoke(crate::mir::borrowck_query)] #[salsa::lru(2024)] - fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; + fn borrowck<'db>( + &'db self, + def: DefWithBodyId, + ) -> Result, MirLowerError<'db>>; #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_cycle_result)] - fn const_eval( - &self, + fn const_eval<'db>( + &'db self, def: GeneralConstId, subst: Substitution, - trait_env: Option>>, - ) -> Result; + trait_env: Option>>, + ) -> Result>; #[salsa::invoke(crate::consteval::const_eval_static_query)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_static_cycle_result)] - fn const_eval_static(&self, def: StaticId) -> Result; + fn const_eval_static<'db>(&'db self, def: StaticId) -> Result>; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] - fn const_eval_discriminant(&self, def: EnumVariantId) -> Result; + fn const_eval_discriminant<'db>( + &'db self, + def: EnumVariantId, + ) -> Result>; #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] - fn lookup_impl_method( - &self, - env: Arc>, + fn lookup_impl_method<'db>( + &'db self, + env: Arc>, func: FunctionId, - fn_subst: Substitution, - ) -> (FunctionId, Substitution); + fn_subst: crate::next_solver::GenericArgs<'db>, + ) -> (FunctionId, crate::next_solver::GenericArgs<'db>); // endregion:mir @@ -370,6 +379,23 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &'db self, def: GenericDefId, ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke(crate::lower_nextsolver::generic_defaults_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_defaults_with_diagnostics_cycle_result)] + fn generic_defaults_ns_with_diagnostics<'db>( + &'db self, + def: GenericDefId, + ) -> (crate::lower_nextsolver::GenericDefaults<'db>, Diagnostics); + + /// This returns an empty list if no parameter has default. + /// + /// The binders of the returned defaults are only up to (not including) this parameter. + #[salsa::invoke(crate::lower_nextsolver::generic_defaults_query)] + #[salsa::transparent] + fn generic_defaults_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericDefaults<'db>; } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index d05814e0e7e40..7b6fb994ecaf5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -23,6 +23,8 @@ use tracing::debug; use triomphe::Arc; use typed_arena::Arena; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind, db::HirDatabase, @@ -74,8 +76,16 @@ impl BodyValidationDiagnostic { let infer = db.infer(owner); let body = db.body(owner); let env = db.trait_environment_for_body(owner); - let mut validator = - ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env }; + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let mut validator = ExprValidator { + owner, + body, + infer, + diagnostics: Vec::new(), + validate_lints, + env, + interner, + }; validator.validate_body(db); validator.diagnostics } @@ -84,10 +94,11 @@ impl BodyValidationDiagnostic { struct ExprValidator<'db> { owner: DefWithBodyId, body: Arc, - infer: Arc, + infer: Arc>, env: Arc>, diagnostics: Vec, validate_lints: bool, + interner: DbInterner<'db>, } impl<'db> ExprValidator<'db> { @@ -175,7 +186,7 @@ impl<'db> ExprValidator<'db> { } if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) { - checker.prev_receiver_ty = Some(receiver_ty.clone()); + checker.prev_receiver_ty = Some(receiver_ty.to_chalk(self.interner)); } } } @@ -190,6 +201,7 @@ impl<'db> ExprValidator<'db> { let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else { return; }; + let scrut_ty = scrut_ty.to_chalk(self.interner); if scrut_ty.contains_unknown() { return; } @@ -205,6 +217,7 @@ impl<'db> ExprValidator<'db> { let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { return; }; + let pat_ty = pat_ty.to_chalk(self.interner); if pat_ty.contains_unknown() { return; } @@ -222,7 +235,7 @@ impl<'db> ExprValidator<'db> { if (pat_ty == scrut_ty || scrut_ty .as_reference() - .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) + .map(|(match_expr_ty, ..)| *match_expr_ty == pat_ty) .unwrap_or(false)) && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) { @@ -264,7 +277,7 @@ impl<'db> ExprValidator<'db> { match_expr, uncovered_patterns: missing_match_arms( &cx, - scrut_ty, + &scrut_ty, witnesses, m_arms.is_empty(), self.owner.krate(db), @@ -298,10 +311,12 @@ impl<'db> ExprValidator<'db> { ); value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } - Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { - TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false, - _ => self.is_known_valid_scrutinee(*expr, db), - }, + Expr::Field { expr, .. } => { + match self.infer.type_of_expr[*expr].to_chalk(self.interner).kind(Interner) { + TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false, + _ => self.is_known_valid_scrutinee(*expr, db), + } + } Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db), Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db), Expr::Missing => false, @@ -327,6 +342,7 @@ impl<'db> ExprValidator<'db> { } let Some(initializer) = initializer else { continue }; let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue }; + let ty = ty.to_chalk(self.interner); if ty.contains_unknown() { continue; } @@ -357,7 +373,7 @@ impl<'db> ExprValidator<'db> { pat, uncovered_patterns: missing_match_arms( &cx, - ty, + &ty, witnesses, false, self.owner.krate(db), @@ -542,7 +558,7 @@ impl FilterMapNextChecker { pub fn record_literal_missing_fields( db: &dyn HirDatabase, - infer: &InferenceResult, + infer: &InferenceResult<'_>, id: ExprId, expr: &Expr, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { @@ -572,7 +588,7 @@ pub fn record_literal_missing_fields( pub fn record_pattern_missing_fields( db: &dyn HirDatabase, - infer: &InferenceResult, + infer: &InferenceResult<'_>, id: PatId, pat: &Pat, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { @@ -600,8 +616,8 @@ pub fn record_pattern_missing_fields( Some((variant_def, missed_fields, exhaustive)) } -fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool { - fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { +fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult<'_>) -> bool { + fn walk(pat: PatId, body: &Body, infer: &InferenceResult<'_>, has_type_mismatches: &mut bool) { match infer.type_mismatch_for_pat(pat) { Some(_) => *has_type_mismatches = true, None if *has_type_mismatches => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index e803b56a1ed8f..af541ffa342ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -20,6 +20,8 @@ use hir_expand::name::Name; use span::Edition; use stdx::{always, never}; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, db::HirDatabase, @@ -93,16 +95,21 @@ pub(crate) enum PatKind { }, } -pub(crate) struct PatCtxt<'a> { - db: &'a dyn HirDatabase, - infer: &'a InferenceResult, - body: &'a Body, +pub(crate) struct PatCtxt<'db> { + db: &'db dyn HirDatabase, + infer: &'db InferenceResult<'db>, + body: &'db Body, pub(crate) errors: Vec, + interner: DbInterner<'db>, } impl<'a> PatCtxt<'a> { - pub(crate) fn new(db: &'a dyn HirDatabase, infer: &'a InferenceResult, body: &'a Body) -> Self { - Self { db, infer, body, errors: Vec::new() } + pub(crate) fn new( + db: &'a dyn HirDatabase, + infer: &'a InferenceResult<'a>, + body: &'a Body, + ) -> Self { + Self { db, infer, body, errors: Vec::new(), interner: DbInterner::new_with(db, None, None) } } pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat { @@ -115,14 +122,14 @@ impl<'a> PatCtxt<'a> { self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( unadjusted_pat, |subpattern, ref_ty| Pat { - ty: ref_ty.clone(), + ty: ref_ty.to_chalk(self.interner).clone(), kind: Box::new(PatKind::Deref { subpattern }), }, ) } fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat { - let mut ty = &self.infer[pat]; + let mut ty = self.infer[pat].to_chalk(self.interner); let variant = self.infer.variant_resolution_for_pat(pat); let kind = match self.body[pat] { @@ -140,7 +147,7 @@ impl<'a> PatCtxt<'a> { _ => { never!("unexpected type for tuple pattern: {:?}", ty); self.errors.push(PatternError::UnexpectedType); - return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; + return Pat { ty, kind: PatKind::Wild.into() }; } }; let subpatterns = self.lower_tuple_subpats(args, arity, ellipsis); @@ -149,10 +156,10 @@ impl<'a> PatCtxt<'a> { hir_def::hir::Pat::Bind { id, subpat, .. } => { let bm = self.infer.binding_modes[pat]; - ty = &self.infer[id]; + ty = self.infer[id].to_chalk(self.interner); let name = &self.body[id].name; match (bm, ty.kind(Interner)) { - (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty, + (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty.clone(), (BindingMode::Ref(_), _) => { never!( "`ref {}` has wrong type {:?}", @@ -170,7 +177,7 @@ impl<'a> PatCtxt<'a> { hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { let expected_len = variant.unwrap().fields(self.db).fields().len(); let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis); - self.lower_variant_or_leaf(pat, ty, subpatterns) + self.lower_variant_or_leaf(pat, &ty, subpatterns) } hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => { @@ -186,7 +193,7 @@ impl<'a> PatCtxt<'a> { }) .collect(); match subpatterns { - Some(subpatterns) => self.lower_variant_or_leaf(pat, ty, subpatterns), + Some(subpatterns) => self.lower_variant_or_leaf(pat, &ty, subpatterns), None => { self.errors.push(PatternError::MissingField); PatKind::Wild @@ -271,12 +278,12 @@ impl<'a> PatCtxt<'a> { } fn lower_path(&mut self, pat: PatId, _path: &Path) -> Pat { - let ty = &self.infer[pat]; + let ty = self.infer[pat].to_chalk(self.interner); let pat_from_kind = |kind| Pat { ty: ty.clone(), kind: Box::new(kind) }; match self.infer.variant_resolution_for_pat(pat) { - Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, ty, Vec::new())), + Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, &ty, Vec::new())), None => { self.errors.push(PatternError::UnresolvedVariant); pat_from_kind(PatKind::Wild) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3c78f5ef38704..31100e17f8465 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -14,6 +14,8 @@ use hir_def::{ }; use span::Edition; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::utils::TargetFeatureIsSafeInTarget; use crate::{ InferenceResult, Interner, TargetFeatures, TyExt, TyKind, @@ -96,9 +98,9 @@ enum UnsafeDiagnostic { DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock }, } -pub fn unsafe_operations_for_body( - db: &dyn HirDatabase, - infer: &InferenceResult, +pub fn unsafe_operations_for_body<'db>( + db: &'db dyn HirDatabase, + infer: &InferenceResult<'db>, def: DefWithBodyId, body: &Body, callback: &mut dyn FnMut(ExprOrPatId), @@ -115,9 +117,9 @@ pub fn unsafe_operations_for_body( } } -pub fn unsafe_operations( - db: &dyn HirDatabase, - infer: &InferenceResult, +pub fn unsafe_operations<'db>( + db: &'db dyn HirDatabase, + infer: &InferenceResult<'db>, def: DefWithBodyId, body: &Body, current: ExprId, @@ -135,7 +137,7 @@ pub fn unsafe_operations( struct UnsafeVisitor<'db> { db: &'db dyn HirDatabase, - infer: &'db InferenceResult, + infer: &'db InferenceResult<'db>, body: &'db Body, resolver: Resolver<'db>, def: DefWithBodyId, @@ -149,12 +151,13 @@ struct UnsafeVisitor<'db> { /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when /// the target feature is not enabled. This flag encodes that. target_feature_is_safe: TargetFeatureIsSafeInTarget, + interner: DbInterner<'db>, } impl<'db> UnsafeVisitor<'db> { fn new( db: &'db dyn HirDatabase, - infer: &'db InferenceResult, + infer: &'db InferenceResult<'db>, body: &'db Body, def: DefWithBodyId, unsafe_expr_cb: &'db mut dyn FnMut(UnsafeDiagnostic), @@ -183,6 +186,7 @@ impl<'db> UnsafeVisitor<'db> { def_target_features, edition, target_feature_is_safe, + interner: DbInterner::new_with(db, None, None), } } @@ -285,7 +289,7 @@ impl<'db> UnsafeVisitor<'db> { let inside_assignment = mem::replace(&mut self.inside_assignment, false); match expr { &Expr::Call { callee, .. } => { - let callee = &self.infer[callee]; + let callee = self.infer[callee].to_chalk(self.interner); if let Some(func) = callee.as_fn_def(self.db) { self.check_call(current, func); } @@ -338,7 +342,7 @@ impl<'db> UnsafeVisitor<'db> { } } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) { + if let TyKind::Raw(..) = &self.infer[*expr].to_chalk(self.interner).kind(Interner) { self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index e11ce51cdb8f0..d79069901ebb8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1545,14 +1545,17 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { never!("Only `impl Fn` is valid for displaying closures in source code"); } } - let chalk_id: chalk_ir::ClosureId<_> = id.into(); match f.closure_style { ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), ClosureStyle::ClosureWithId => { - return write!(f, "{{closure#{:?}}}", chalk_id.0.index()); + return write!( + f, + "{{closure#{:?}}}", + salsa::plumbing::AsId::as_id(&id).index() + ); } ClosureStyle::ClosureWithSubst => { - write!(f, "{{closure#{:?}}}", chalk_id.0.index())?; + write!(f, "{{closure#{:?}}}", salsa::plumbing::AsId::as_id(&id).index())?; return hir_fmt_generics(f, substs.as_slice(Interner), None, None); } _ => (), @@ -1561,7 +1564,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { if let Some(sig) = sig { let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); - let (_, kind) = infer.closure_info(&chalk_id); + let (_, kind) = infer.closure_info(id); match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, ClosureStyle::RANotation => write!(f, "|")?, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 413f70532a548..0618fd17d55d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -8,7 +8,7 @@ use stdx::never; use triomphe::Arc; use crate::next_solver::DbInterner; -use crate::next_solver::mapping::NextSolverToChalk; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment, Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase, @@ -120,13 +120,20 @@ pub(crate) fn has_drop_glue( } TyKind::Slice(ty) => db.has_drop_glue(ty.clone(), env), TyKind::Closure(closure_id, subst) => { - let owner = db.lookup_intern_closure((*closure_id).into()).0; + let closure_id = (*closure_id).into(); + let owner = db.lookup_intern_closure(closure_id).0; let infer = db.infer(owner); let (captures, _) = infer.closure_info(closure_id); let env = db.trait_environment_for_body(owner); + let interner = DbInterner::conjure(); captures .iter() - .map(|capture| db.has_drop_glue(capture.ty(db, subst), env.clone())) + .map(|capture| { + db.has_drop_glue( + capture.ty(db, subst.to_nextsolver(interner)).to_chalk(interner), + env.clone(), + ) + }) .max() .unwrap_or(DropGlue::None) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0282b7a936324..72498681aca50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -28,19 +28,11 @@ pub(crate) mod unify; use std::{cell::OnceCell, convert::identity, iter, ops::Index}; use base_db::Crate; -use chalk_ir::{ - DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, - cast::Cast, - fold::TypeFoldable, - interner::HasInterner, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; use either::Either; use hir_def::{ AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, - builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::{LangItem, LangItemTarget, lang_item}, @@ -53,18 +45,19 @@ use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; use intern::sym; use la_arena::{ArenaMap, Entry}; +use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::Ty as _; -use stdx::{always, never}; +use rustc_type_ir::{ + AliasTyKind, Flags, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, + inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _}, +}; +use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, - IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, - PathLoweringDiagnostic, ProjectionTy, Substitution, TargetFeatures, TraitEnvironment, Ty, - TyBuilder, TyExt, - db::{HirDatabase, InternedClosureId}, - fold_tys, + ImplTraitId, IncorrectGenericsLenKind, Interner, PathLoweringDiagnostic, TargetFeatures, + TraitEnvironment, + db::{HirDatabase, InternedClosureId, InternedOpaqueTyId}, generics::Generics, infer::{ coerce::{CoerceMany, DynamicCoerceMany}, @@ -72,16 +65,22 @@ use crate::{ expr::ExprIsRead, unify::InferenceTable, }, - lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic}, + lower::diagnostics::TyLoweringDiagnostic, + lower_nextsolver::{ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind}, mir::MirSpan, next_solver::{ - self, DbInterner, - infer::{DefineOpaqueTypes, traits::ObligationCause}, + AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind, + Tys, + abi::Safety, + fold::fold_tys, + infer::{ + DefineOpaqueTypes, + traits::{Obligation, ObligationCause}, + }, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, - static_lifetime, to_assoc_type_id, traits::FnTrait, - utils::{TargetFeatureIsSafeInTarget, UnevaluatedConstEvaluatorFolder}, + utils::TargetFeatureIsSafeInTarget, }; // This lint has a false positive here. See the link below for details. @@ -96,7 +95,7 @@ use cast::{CastCheck, CastError}; pub(crate) use closure::analysis::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. -pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { +pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc> { let _p = tracing::info_span!("infer_query").entered(); let resolver = def.resolver(db); let body = db.body(def); @@ -109,30 +108,28 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_const(c, &db.const_signature(c)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), + ctx.return_ty = match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => ctx.types.isize, + false => ctx.types.usize, + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => match size { + Integer::I8 => ctx.types.i8, + Integer::I16 => ctx.types.i16, + Integer::I32 => ctx.types.i32, + Integer::I64 => ctx.types.i64, + Integer::I128 => ctx.types.i128, }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), + false => match size { + Integer::I8 => ctx.types.u8, + Integer::I16 => ctx.types.u16, + Integer::I32 => ctx.types.u32, + Integer::I64 => ctx.types.u64, + Integer::I128 => ctx.types.u128, }, }, - ); + }; } } @@ -159,8 +156,14 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc Arc { - Arc::new(InferenceResult { has_errors: true, ..Default::default() }) +pub(crate) fn infer_cycle_result( + db: &dyn HirDatabase, + _: DefWithBodyId, +) -> Arc> { + Arc::new(InferenceResult { + has_errors: true, + ..InferenceResult::new(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)) + }) } /// Fully normalize all the types found within `ty` in context of `owner` body definition. @@ -168,20 +171,24 @@ pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc>, ty: Ty) -> Ty { +pub(crate) fn normalize( + db: &dyn HirDatabase, + trait_env: Arc>, + ty: crate::Ty, +) -> crate::Ty { // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only // works for the type case, so we check array unconditionally. Remove the array part // when the bug in chalk becomes fixed. - if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) - && !matches!(ty.kind(Interner), TyKind::Array(..)) + if !ty.data(Interner).flags.intersects(crate::TypeFlags::HAS_PROJECTION) + && !matches!(ty.kind(Interner), crate::TyKind::Array(..)) { return ty; } let mut table = unify::InferenceTable::new(db, trait_env); - let ty_with_vars = table.normalize_associated_types_in(ty); + let ty_with_vars = table.normalize_associated_types_in(ty.to_nextsolver(table.interner())); table.select_obligations_where_possible(); - table.resolve_completely(ty_with_vars) + table.resolve_completely(ty_with_vars).to_chalk(table.interner()) } /// Binding modes inferred for patterns. @@ -203,14 +210,6 @@ impl BindingMode { } } -// FIXME: Remove this `InferOk`, switch all code to the second one, that uses `Obligation` instead of `Goal`. -#[derive(Debug)] -pub(crate) struct InferOk<'db, T> { - #[allow(dead_code)] - value: T, - goals: Vec>>, -} - #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -219,12 +218,8 @@ pub enum InferenceTyDiagnosticSource { Signature, } -#[derive(Debug)] -pub(crate) struct TypeError; -pub(crate) type InferResult<'db, T> = Result, TypeError>; - #[derive(Debug, PartialEq, Eq, Clone)] -pub enum InferenceDiagnostic { +pub enum InferenceDiagnostic<'db> { NoSuchField { field: ExprOrPatId, private: Option, @@ -240,16 +235,16 @@ pub enum InferenceDiagnostic { }, UnresolvedField { expr: ExprId, - receiver: Ty, + receiver: Ty<'db>, name: Name, method_with_same_name_exists: bool, }, UnresolvedMethodCall { expr: ExprId, - receiver: Ty, + receiver: Ty<'db>, name: Name, /// Contains the type the field resolves to - field_with_same_name: Option, + field_with_same_name: Option>, assoc_func_with_same_name: Option, }, UnresolvedAssocItem { @@ -276,21 +271,21 @@ pub enum InferenceDiagnostic { }, ExpectedFunction { call_expr: ExprId, - found: Ty, + found: Ty<'db>, }, TypedHole { expr: ExprId, - expected: Ty, + expected: Ty<'db>, }, CastToUnsized { expr: ExprId, - cast_ty: Ty, + cast_ty: Ty<'db>, }, InvalidCast { expr: ExprId, error: CastError, - expr_ty: Ty, - cast_ty: Ty, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, }, TyDiagnostic { source: InferenceTyDiagnosticSource, @@ -318,29 +313,11 @@ pub enum InferenceDiagnostic { /// A mismatch between an expected and an inferred type. #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeMismatch { - pub expected: Ty, - pub actual: Ty, +pub struct TypeMismatch<'db> { + pub expected: Ty<'db>, + pub actual: Ty<'db>, } -#[derive(Clone, PartialEq, Eq, Debug)] -struct InternedStandardTypes { - unknown: Ty, - bool_: Ty, - unit: Ty, - never: Ty, -} - -impl Default for InternedStandardTypes { - fn default() -> Self { - InternedStandardTypes { - unknown: TyKind::Error.intern(Interner), - bool_: TyKind::Scalar(Scalar::Bool).intern(Interner), - unit: TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner), - never: TyKind::Never.intern(Interner), - } - } -} /// Represents coercing a value to a different type of value. /// /// We transform values by following a number of `Adjust` steps in order. @@ -382,14 +359,14 @@ impl Default for InternedStandardTypes { /// case this is analogous to transforming a struct. E.g., Box<[i32; 4]> -> /// Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Adjustment { - pub kind: Adjust, - pub target: Ty, +pub struct Adjustment<'db> { + pub kind: Adjust<'db>, + pub target: Ty<'db>, } -impl Adjustment { - pub fn borrow(m: Mutability, ty: Ty, lt: Lifetime) -> Self { - let ty = TyKind::Ref(m, lt.clone(), ty).intern(Interner); +impl<'db> Adjustment<'db> { + pub fn borrow(interner: DbInterner<'db>, m: Mutability, ty: Ty<'db>, lt: Region<'db>) -> Self { + let ty = Ty::new_ref(interner, lt, ty, m); Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty } } } @@ -415,13 +392,13 @@ pub(crate) enum AllowTwoPhase { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Adjust { +pub enum Adjust<'db> { /// Go from ! to any type. NeverToAny, /// Dereference once, producing a place. Deref(Option), /// Take the address and produce either a `&` or `*` pointer. - Borrow(AutoBorrow), + Borrow(AutoBorrow<'db>), Pointer(PointerCast), } @@ -433,14 +410,14 @@ pub enum Adjust { pub struct OverloadedDeref(pub Option); #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum AutoBorrow { +pub enum AutoBorrow<'db> { /// Converts from T to &T. - Ref(Lifetime, Mutability), + Ref(Region<'db>, Mutability), /// Converts from T to *T. RawPtr(Mutability), } -impl AutoBorrow { +impl<'db> AutoBorrow<'db> { fn mutability(&self) -> Mutability { let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self; *m @@ -484,41 +461,41 @@ pub enum PointerCast { /// When you add a field that stores types (including `Substitution` and the like), don't forget /// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must /// not appear in the final inference result. -#[derive(Clone, PartialEq, Eq, Debug, Default)] -pub struct InferenceResult { +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct InferenceResult<'db> { /// For each method call expr, records the function it resolves to. - method_resolutions: FxHashMap, + method_resolutions: FxHashMap)>, /// For each field access expr, records the field it resolves to. field_resolutions: FxHashMap>, /// For each struct literal or pattern, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to - assoc_resolutions: FxHashMap, + assoc_resolutions: FxHashMap)>, /// Whenever a tuple field expression access a tuple field, we allocate a tuple id in /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of /// that which allows us to resolve a [`TupleFieldId`]s type. - tuple_field_access_types: FxHashMap, + tuple_field_access_types: FxHashMap>, /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. - diagnostics: Vec, - pub(crate) type_of_expr: ArenaMap, + diagnostics: Vec>, + pub(crate) type_of_expr: ArenaMap>, /// For each pattern record the type it resolves to. /// /// **Note**: When a pattern type is resolved it may still contain /// unresolved or missing subpatterns or subpatterns of mismatched types. - pub(crate) type_of_pat: ArenaMap, - pub(crate) type_of_binding: ArenaMap, - pub(crate) type_of_rpit: ArenaMap, - type_mismatches: FxHashMap, + pub(crate) type_of_pat: ArenaMap>, + pub(crate) type_of_binding: ArenaMap>, + pub(crate) type_of_rpit: ArenaMap, Ty<'db>>, + type_mismatches: FxHashMap>, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to // `TyKind::Error`. // Which will then mark this field. pub(crate) has_errors: bool, - /// Interned common types to return references to. - // FIXME: Move this into `InferenceContext` - standard_types: InternedStandardTypes, + /// Interned `Error` type to return references to. + // FIXME: Remove this. + error_ty: Ty<'db>, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub(crate) pat_adjustments: FxHashMap>, + pub(crate) pat_adjustments: FxHashMap>>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. /// /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an @@ -533,16 +510,40 @@ pub struct InferenceResult { /// ``` /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. pub(crate) binding_modes: ArenaMap, - pub(crate) expr_adjustments: FxHashMap>, - pub(crate) closure_info: FxHashMap, FnTrait)>, + pub(crate) expr_adjustments: FxHashMap]>>, + pub(crate) closure_info: FxHashMap>, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, pub(crate) coercion_casts: FxHashSet, } -impl InferenceResult { - pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> { - self.method_resolutions.get(&expr).cloned() +impl<'db> InferenceResult<'db> { + fn new(error_ty: Ty<'db>) -> Self { + Self { + method_resolutions: Default::default(), + field_resolutions: Default::default(), + variant_resolutions: Default::default(), + assoc_resolutions: Default::default(), + tuple_field_access_types: Default::default(), + diagnostics: Default::default(), + type_of_expr: Default::default(), + type_of_pat: Default::default(), + type_of_binding: Default::default(), + type_of_rpit: Default::default(), + type_mismatches: Default::default(), + has_errors: Default::default(), + error_ty, + pat_adjustments: Default::default(), + binding_modes: Default::default(), + expr_adjustments: Default::default(), + closure_info: Default::default(), + mutated_bindings_in_closure: Default::default(), + coercion_casts: Default::default(), + } + } + + pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, GenericArgs<'db>)> { + self.method_resolutions.get(&expr).copied() } pub fn field_resolution(&self, expr: ExprId) -> Option> { self.field_resolutions.get(&expr).copied() @@ -559,46 +560,49 @@ impl InferenceResult { ExprOrPatId::PatId(id) => self.variant_resolution_for_pat(id), } } - pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> { - self.assoc_resolutions.get(&id.into()).cloned() + pub fn assoc_resolutions_for_expr( + &self, + id: ExprId, + ) -> Option<(AssocItemId, GenericArgs<'db>)> { + self.assoc_resolutions.get(&id.into()).copied() } - pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> { - self.assoc_resolutions.get(&id.into()).cloned() + pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, GenericArgs<'db>)> { + self.assoc_resolutions.get(&id.into()).copied() } pub fn assoc_resolutions_for_expr_or_pat( &self, id: ExprOrPatId, - ) -> Option<(AssocItemId, Substitution)> { + ) -> Option<(AssocItemId, GenericArgs<'db>)> { match id { ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id), ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id), } } - pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { + pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch<'db>> { self.type_mismatches.get(&expr.into()) } - pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> { + pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch<'db>> { self.type_mismatches.get(&pat.into()) } - pub fn type_mismatches(&self) -> impl Iterator { + pub fn type_mismatches(&self) -> impl Iterator)> { self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) } - pub fn expr_type_mismatches(&self) -> impl Iterator { + pub fn expr_type_mismatches(&self) -> impl Iterator)> { self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat { ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), _ => None, }) } - pub fn closure_info(&self, closure: &ClosureId) -> &(Vec, FnTrait) { - self.closure_info.get(closure).unwrap() + pub fn closure_info(&self, closure: InternedClosureId) -> &(Vec>, FnTrait) { + self.closure_info.get(&closure).unwrap() } - pub fn type_of_expr_or_pat(&self, id: ExprOrPatId) -> Option<&Ty> { + pub fn type_of_expr_or_pat(&self, id: ExprOrPatId) -> Option> { match id { - ExprOrPatId::ExprId(id) => self.type_of_expr.get(id), - ExprOrPatId::PatId(id) => self.type_of_pat.get(id), + ExprOrPatId::ExprId(id) => self.type_of_expr.get(id).copied(), + ExprOrPatId::PatId(id) => self.type_of_pat.get(id).copied(), } } - pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option<&Ty> { + pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option> { match self.expr_adjustments.get(&id).and_then(|adjustments| { adjustments .iter() @@ -614,33 +618,33 @@ impl InferenceResult { }) .next_back() }) { - Some(adjustment) => Some(&adjustment.target), - None => self.type_of_expr.get(id), + Some(adjustment) => Some(adjustment.target), + None => self.type_of_expr.get(id).copied(), } } - pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option<&Ty> { + pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option> { match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { - adjusted @ Some(_) => adjusted, - None => self.type_of_pat.get(id), + Some(adjusted) => Some(*adjusted), + None => self.type_of_pat.get(id).copied(), } } pub fn is_erroneous(&self) -> bool { self.has_errors && self.type_of_expr.iter().count() == 0 } - pub fn diagnostics(&self) -> &[InferenceDiagnostic] { + pub fn diagnostics(&self) -> &[InferenceDiagnostic<'db>] { &self.diagnostics } - pub fn tuple_field_access_type(&self, id: TupleId) -> &Substitution { - &self.tuple_field_access_types[&id] + pub fn tuple_field_access_type(&self, id: TupleId) -> Tys<'db> { + self.tuple_field_access_types[&id] } - pub fn pat_adjustment(&self, id: PatId) -> Option<&[Ty]> { + pub fn pat_adjustment(&self, id: PatId) -> Option<&[Ty<'db>]> { self.pat_adjustments.get(&id).map(|it| &**it) } - pub fn expr_adjustment(&self, id: ExprId) -> Option<&[Adjustment]> { + pub fn expr_adjustment(&self, id: ExprId) -> Option<&[Adjustment<'db>]> { self.expr_adjustments.get(&id).map(|it| &**it) } @@ -649,83 +653,138 @@ impl InferenceResult { } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. - pub fn expression_types(&self) -> impl Iterator { - self.type_of_expr.iter() + pub fn expression_types(&self) -> impl Iterator)> { + self.type_of_expr.iter().map(|(k, v)| (k, *v)) } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. - pub fn pattern_types(&self) -> impl Iterator { - self.type_of_pat.iter() + pub fn pattern_types(&self) -> impl Iterator)> { + self.type_of_pat.iter().map(|(k, v)| (k, *v)) } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. - pub fn binding_types(&self) -> impl Iterator { - self.type_of_binding.iter() + pub fn binding_types(&self) -> impl Iterator)> { + self.type_of_binding.iter().map(|(k, v)| (k, *v)) } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. - pub fn return_position_impl_trait_types(&self) -> impl Iterator { - self.type_of_rpit.iter() + pub fn return_position_impl_trait_types( + &self, + ) -> impl Iterator, Ty<'db>)> { + self.type_of_rpit.iter().map(|(k, v)| (k, *v)) } } -impl Index for InferenceResult { - type Output = Ty; +impl<'db> Index for InferenceResult<'db> { + type Output = Ty<'db>; - fn index(&self, expr: ExprId) -> &Ty { - self.type_of_expr.get(expr).unwrap_or(&self.standard_types.unknown) + fn index(&self, expr: ExprId) -> &Ty<'db> { + self.type_of_expr.get(expr).unwrap_or(&self.error_ty) } } -impl Index for InferenceResult { - type Output = Ty; +impl<'db> Index for InferenceResult<'db> { + type Output = Ty<'db>; - fn index(&self, pat: PatId) -> &Ty { - self.type_of_pat.get(pat).unwrap_or(&self.standard_types.unknown) + fn index(&self, pat: PatId) -> &Ty<'db> { + self.type_of_pat.get(pat).unwrap_or(&self.error_ty) } } -impl Index for InferenceResult { - type Output = Ty; +impl<'db> Index for InferenceResult<'db> { + type Output = Ty<'db>; - fn index(&self, id: ExprOrPatId) -> &Ty { - self.type_of_expr_or_pat(id).unwrap_or(&self.standard_types.unknown) + fn index(&self, id: ExprOrPatId) -> &Ty<'db> { + match id { + ExprOrPatId::ExprId(id) => &self[id], + ExprOrPatId::PatId(id) => &self[id], + } } } -impl Index for InferenceResult { - type Output = Ty; +impl<'db> Index for InferenceResult<'db> { + type Output = Ty<'db>; - fn index(&self, b: BindingId) -> &Ty { - self.type_of_binding.get(b).unwrap_or(&self.standard_types.unknown) + fn index(&self, b: BindingId) -> &Ty<'db> { + self.type_of_binding.get(b).unwrap_or(&self.error_ty) } } #[derive(Debug, Clone)] -struct InternedStandardTypesNextSolver<'db> { - unit: crate::next_solver::Ty<'db>, - never: crate::next_solver::Ty<'db>, - i32: crate::next_solver::Ty<'db>, - f64: crate::next_solver::Ty<'db>, +struct InternedStandardTypes<'db> { + unit: Ty<'db>, + never: Ty<'db>, + char: Ty<'db>, + bool: Ty<'db>, + i8: Ty<'db>, + i16: Ty<'db>, + i32: Ty<'db>, + i64: Ty<'db>, + i128: Ty<'db>, + isize: Ty<'db>, + u8: Ty<'db>, + u16: Ty<'db>, + u32: Ty<'db>, + u64: Ty<'db>, + u128: Ty<'db>, + usize: Ty<'db>, + f16: Ty<'db>, + f32: Ty<'db>, + f64: Ty<'db>, + f128: Ty<'db>, + static_str_ref: Ty<'db>, + error: Ty<'db>, + + re_static: Region<'db>, + re_error: Region<'db>, + + empty_args: GenericArgs<'db>, + empty_tys: Tys<'db>, } -impl<'db> InternedStandardTypesNextSolver<'db> { +impl<'db> InternedStandardTypes<'db> { fn new(interner: DbInterner<'db>) -> Self { + let str = Ty::new(interner, rustc_type_ir::TyKind::Str); + let re_static = Region::new_static(interner); Self { - unit: crate::next_solver::Ty::new_unit(interner), - never: crate::next_solver::Ty::new(interner, crate::next_solver::TyKind::Never), - i32: crate::next_solver::Ty::new_int(interner, rustc_type_ir::IntTy::I32), - f64: crate::next_solver::Ty::new_float(interner, rustc_type_ir::FloatTy::F64), + unit: Ty::new_unit(interner), + never: Ty::new(interner, TyKind::Never), + char: Ty::new(interner, TyKind::Char), + bool: Ty::new(interner, TyKind::Bool), + i8: Ty::new_int(interner, rustc_type_ir::IntTy::I8), + i16: Ty::new_int(interner, rustc_type_ir::IntTy::I16), + i32: Ty::new_int(interner, rustc_type_ir::IntTy::I32), + i64: Ty::new_int(interner, rustc_type_ir::IntTy::I64), + i128: Ty::new_int(interner, rustc_type_ir::IntTy::I128), + isize: Ty::new_int(interner, rustc_type_ir::IntTy::Isize), + u8: Ty::new_uint(interner, rustc_type_ir::UintTy::U8), + u16: Ty::new_uint(interner, rustc_type_ir::UintTy::U16), + u32: Ty::new_uint(interner, rustc_type_ir::UintTy::U32), + u64: Ty::new_uint(interner, rustc_type_ir::UintTy::U64), + u128: Ty::new_uint(interner, rustc_type_ir::UintTy::U128), + usize: Ty::new_uint(interner, rustc_type_ir::UintTy::Usize), + f16: Ty::new_float(interner, rustc_type_ir::FloatTy::F16), + f32: Ty::new_float(interner, rustc_type_ir::FloatTy::F32), + f64: Ty::new_float(interner, rustc_type_ir::FloatTy::F64), + f128: Ty::new_float(interner, rustc_type_ir::FloatTy::F128), + static_str_ref: Ty::new_ref(interner, re_static, str, Mutability::Not), + error: Ty::new_error(interner, ErrorGuaranteed), + + re_static, + re_error: Region::error(interner), + + empty_args: GenericArgs::new_from_iter(interner, []), + empty_tys: Tys::new_from_iter(interner, []), } } } /// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] -pub(crate) struct InferenceContext<'db> { +pub(crate) struct InferenceContext<'body, 'db> { pub(crate) db: &'db dyn HirDatabase, pub(crate) owner: DefWithBodyId, - pub(crate) body: &'db Body, + pub(crate) body: &'body Body, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, @@ -735,32 +794,32 @@ pub(crate) struct InferenceContext<'db> { table: unify::InferenceTable<'db>, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, - pub(crate) result: InferenceResult, + pub(crate) result: InferenceResult<'db>, tuple_field_accesses_rev: - IndexSet>, + IndexSet, std::hash::BuildHasherDefault>, /// The return type of the function being inferred, the closure or async block if we're /// currently within one. /// /// We might consider using a nested inference context for checking /// closures so we can swap all shared things out at once. - return_ty: Ty, + return_ty: Ty<'db>, /// If `Some`, this stores coercion information for returned /// expressions. If `None`, this is in a context where return is /// inappropriate, such as a const expression. return_coercion: Option>, /// The resume type and the yield type, respectively, of the coroutine being inferred. - resume_yield_tys: Option<(Ty, Ty)>, + resume_yield_tys: Option<(Ty<'db>, Ty<'db>)>, diverges: Diverges, breakables: Vec>, - types: InternedStandardTypesNextSolver<'db>, + types: InternedStandardTypes<'db>, /// Whether we are inside the pattern of a destructuring assignment. inside_assignment: bool, - deferred_cast_checks: Vec, + deferred_cast_checks: Vec>, // fields related to closure capture - current_captures: Vec, + current_captures: Vec>, /// A stack that has an entry for each projection in the current capture. /// /// For example, in `a.b.c`, we capture the spans of `a`, `a.b`, and `a.b.c`. @@ -771,9 +830,9 @@ pub(crate) struct InferenceContext<'db> { /// Stores the list of closure ids that need to be analyzed before this closure. See the /// comment on `InferenceContext::sort_closures` closure_dependencies: FxHashMap>, - deferred_closures: FxHashMap, ExprId)>>, + deferred_closures: FxHashMap, Ty<'db>, Vec>, ExprId)>>, - diagnostics: Diagnostics, + diagnostics: Diagnostics<'db>, } #[derive(Clone, Debug)] @@ -820,28 +879,29 @@ fn find_continuable<'a, 'db>( } } -enum ImplTraitReplacingMode { - ReturnPosition(FxHashSet), +enum ImplTraitReplacingMode<'db> { + ReturnPosition(FxHashSet>), TypeAlias, } -impl<'db> InferenceContext<'db> { +impl<'body, 'db> InferenceContext<'body, 'db> { fn new( db: &'db dyn HirDatabase, owner: DefWithBodyId, - body: &'db Body, + body: &'body Body, resolver: Resolver<'db>, ) -> Self { let trait_env = db.trait_environment_for_body(owner); let table = unify::InferenceTable::new(db, trait_env); + let types = InternedStandardTypes::new(table.interner()); InferenceContext { - types: InternedStandardTypesNextSolver::new(table.interner), + result: InferenceResult::new(types.error), + return_ty: types.error, // set in collect_* calls + types, target_features: OnceCell::new(), generics: OnceCell::new(), - result: InferenceResult::default(), table, tuple_field_accesses_rev: Default::default(), - return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, return_coercion: None, db, @@ -904,7 +964,7 @@ impl<'db> InferenceContext<'db> { /// Clones `self` and calls `resolve_all()` on it. // FIXME: Remove this. - pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult { + pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult<'db> { let mut ctx = self.clone(); ctx.type_inference_fallback(); @@ -928,7 +988,7 @@ impl<'db> InferenceContext<'db> { // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. - pub(crate) fn resolve_all(self) -> InferenceResult { + pub(crate) fn resolve_all(self) -> InferenceResult<'db> { let InferenceContext { mut table, mut result, tuple_field_accesses_rev, diagnostics, .. } = self; @@ -946,7 +1006,7 @@ impl<'db> InferenceContext<'db> { type_of_rpit, type_mismatches, has_errors, - standard_types: _, + error_ty: _, pat_adjustments, binding_modes: _, expr_adjustments, @@ -961,39 +1021,32 @@ impl<'db> InferenceContext<'db> { } = &mut result; for ty in type_of_expr.values_mut() { - *ty = table.resolve_completely(ty.clone()); - *has_errors = *has_errors || ty.contains_unknown(); + *ty = table.resolve_completely(*ty); + *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_expr.shrink_to_fit(); for ty in type_of_pat.values_mut() { - *ty = table.resolve_completely(ty.clone()); - *has_errors = *has_errors || ty.contains_unknown(); + *ty = table.resolve_completely(*ty); + *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_pat.shrink_to_fit(); for ty in type_of_binding.values_mut() { - *ty = table.resolve_completely(ty.clone()); - *has_errors = *has_errors || ty.contains_unknown(); + *ty = table.resolve_completely(*ty); + *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_binding.shrink_to_fit(); for ty in type_of_rpit.values_mut() { - *ty = table.resolve_completely(ty.clone()); - *has_errors = *has_errors || ty.contains_unknown(); + *ty = table.resolve_completely(*ty); + *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_rpit.shrink_to_fit(); *has_errors |= !type_mismatches.is_empty(); - type_mismatches.retain(|_, mismatch| { - mismatch.expected = table.resolve_completely(mismatch.expected.clone()); - mismatch.actual = table.resolve_completely(mismatch.actual.clone()); - chalk_ir::zip::Zip::zip_with( - &mut UnknownMismatch(self.db), - Variance::Invariant, - &mismatch.expected, - &mismatch.actual, - ) - .is_ok() - }); + for mismatch in (*type_mismatches).values_mut() { + mismatch.expected = table.resolve_completely(mismatch.expected); + mismatch.actual = table.resolve_completely(mismatch.actual); + } type_mismatches.shrink_to_fit(); diagnostics.retain_mut(|diagnostic| { use InferenceDiagnostic::*; @@ -1001,23 +1054,23 @@ impl<'db> InferenceContext<'db> { ExpectedFunction { found: ty, .. } | UnresolvedField { receiver: ty, .. } | UnresolvedMethodCall { receiver: ty, .. } => { - *ty = table.resolve_completely(ty.clone()); + *ty = table.resolve_completely(*ty); // FIXME: Remove this when we are on par with rustc in terms of inference - if ty.contains_unknown() { + if ty.references_non_lt_error() { return false; } if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic && let Some(ty) = field_with_same_name { - *ty = table.resolve_completely(ty.clone()); - if ty.contains_unknown() { + *ty = table.resolve_completely(*ty); + if ty.references_non_lt_error() { *field_with_same_name = None; } } } TypedHole { expected: ty, .. } => { - *ty = table.resolve_completely(ty.clone()); + *ty = table.resolve_completely(*ty); } _ => (), } @@ -1025,41 +1078,31 @@ impl<'db> InferenceContext<'db> { }); diagnostics.shrink_to_fit(); for (_, subst) in method_resolutions.values_mut() { - *subst = - table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); - *has_errors = - *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + *subst = table.resolve_completely(*subst); + *has_errors = *has_errors || subst.types().any(|ty| ty.references_non_lt_error()); } method_resolutions.shrink_to_fit(); for (_, subst) in assoc_resolutions.values_mut() { - *subst = - table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); - *has_errors = - *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + *subst = table.resolve_completely(*subst); + *has_errors = *has_errors || subst.types().any(|ty| ty.references_non_lt_error()); } assoc_resolutions.shrink_to_fit(); for adjustment in expr_adjustments.values_mut().flatten() { - adjustment.target = table.resolve_completely(adjustment.target.clone()); - *has_errors = *has_errors || adjustment.target.contains_unknown(); + adjustment.target = table.resolve_completely(adjustment.target); + *has_errors = *has_errors || adjustment.target.references_non_lt_error(); } expr_adjustments.shrink_to_fit(); for adjustment in pat_adjustments.values_mut().flatten() { - *adjustment = table.resolve_completely(adjustment.clone()); - *has_errors = *has_errors || adjustment.contains_unknown(); + *adjustment = table.resolve_completely(*adjustment); + *has_errors = *has_errors || adjustment.references_non_lt_error(); } pat_adjustments.shrink_to_fit(); result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() - .map(|(idx, subst)| { - ( - TupleId(idx as u32), - table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst), - ) - }) + .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) .inspect(|(_, subst)| { - *has_errors = - *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + *has_errors = *has_errors || subst.iter().any(|ty| ty.references_non_lt_error()); }) .collect(); result.tuple_field_access_types.shrink_to_fit(); @@ -1074,11 +1117,11 @@ impl<'db> InferenceContext<'db> { data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature, - LifetimeElisionKind::for_const(id.loc(self.db).container), + LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container), ); // Constants might be defining usage sites of TAITs. - self.make_tait_coercion_table(iter::once(&return_ty)); + self.make_tait_coercion_table(iter::once(return_ty)); self.return_ty = return_ty; } @@ -1088,11 +1131,11 @@ impl<'db> InferenceContext<'db> { data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature, - LifetimeElisionKind::Elided(static_lifetime()), + LifetimeElisionKind::Elided(self.types.re_static), ); // Statics might be defining usage sites of TAITs. - self.make_tait_coercion_table(iter::once(&return_ty)); + self.make_tait_coercion_table(iter::once(return_ty)); self.return_ty = return_ty; } @@ -1103,25 +1146,28 @@ impl<'db> InferenceContext<'db> { &data.store, InferenceTyDiagnosticSource::Signature, LifetimeElisionKind::for_fn_params(&data), - |ctx| { - ctx.type_param_mode(ParamLoweringMode::Placeholder); - data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::>() - }, + |ctx| data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::>(), ); // Check if function contains a va_list, if it does then we append it to the parameter types // that are collected from the function data if data.is_varargs() { let va_list_ty = match self.resolve_va_list() { - Some(va_list) => TyBuilder::adt(self.db, va_list) - .fill_with_defaults(self.db, || self.table.new_type_var()) - .build(), + Some(va_list) => Ty::new_adt( + self.interner(), + va_list, + GenericArgs::for_item_with_defaults( + self.interner(), + va_list.into(), + |_, _, id, _| self.table.next_var_for_param(id), + ), + ), None => self.err_ty(), }; param_tys.push(va_list_ty); } - let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var())); + let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var())); if let Some(self_param) = self.body.self_param && let Some(ty) = param_tys.next() { @@ -1132,12 +1178,8 @@ impl<'db> InferenceContext<'db> { for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.process_user_written_ty(ty); - self.infer_top_pat(*pat, &ty, None); - if ty - .data(Interner) - .flags - .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) - { + self.infer_top_pat(*pat, ty, None); + if ty.flags().intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) { tait_candidates.insert(ty); } } @@ -1146,31 +1188,24 @@ impl<'db> InferenceContext<'db> { let return_ty = self.with_ty_lowering( &data.store, InferenceTyDiagnosticSource::Signature, - LifetimeElisionKind::for_fn_ret(), + LifetimeElisionKind::for_fn_ret(self.interner()), |ctx| { - ctx.type_param_mode(ParamLoweringMode::Placeholder) - .impl_trait_mode(ImplTraitLoweringMode::Opaque); + ctx.impl_trait_mode(ImplTraitLoweringMode::Opaque); ctx.lower_ty(return_ty) }, ); let return_ty = self.insert_type_vars(return_ty); - if let Some(rpits) = self.db.return_type_impl_traits(func) { - // RPIT opaque types use substitution of their parent function. - let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); + if let Some(rpits) = self.db.return_type_impl_traits_ns(func) { let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default()); - let result = self.insert_inference_vars_for_impl_trait( - return_ty, - fn_placeholders, - &mut mode, - ); + let result = self.insert_inference_vars_for_impl_trait(return_ty, &mut mode); if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { tait_candidates.extend(taits); } - let rpits = rpits.skip_binders(); + let rpits = (*rpits).as_ref().skip_binder(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { never!("Missed RPIT in `insert_inference_vars_for_rpit`"); - e.insert(TyKind::Error.intern(Interner)); + e.insert(self.types.error); } } result @@ -1178,111 +1213,97 @@ impl<'db> InferenceContext<'db> { return_ty } } - None => self.result.standard_types.unit.clone(), + None => self.types.unit, }; self.return_ty = self.process_user_written_ty(return_ty); - self.return_coercion = - Some(CoerceMany::new(self.return_ty.to_nextsolver(self.table.interner))); + self.return_coercion = Some(CoerceMany::new(self.return_ty)); // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. // So, it suffices to check for params and return types. - fold_tys( - self.return_ty.clone(), - |ty, _| { - match ty.kind(Interner) { - TyKind::OpaqueType(..) - | TyKind::Alias(AliasTy::Opaque(..)) - | TyKind::InferenceVar(..) => { - tait_candidates.insert(self.return_ty.clone()); - } - _ => {} + fold_tys(self.interner(), self.return_ty, |ty| { + match ty.kind() { + TyKind::Alias(AliasTyKind::Opaque, _) | TyKind::Infer(..) => { + tait_candidates.insert(self.return_ty); } - ty - }, - DebruijnIndex::INNERMOST, - ); + _ => {} + } + ty + }); + + self.make_tait_coercion_table(tait_candidates.iter().copied()); + } - self.make_tait_coercion_table(tait_candidates.iter()); + #[inline] + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.table.interner() } fn insert_inference_vars_for_impl_trait( &mut self, t: T, - placeholders: Substitution, - mode: &mut ImplTraitReplacingMode, + mode: &mut ImplTraitReplacingMode<'db>, ) -> T where - T: crate::HasInterner + crate::TypeFoldable, + T: TypeFoldable>, { - fold_tys( - t, - |ty, _| { - let ty = self.table.structurally_resolve_type(&ty); - let opaque_ty_id = match ty.kind(Interner) { - TyKind::OpaqueType(opaque_ty_id, _) - | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id, .. })) => { - *opaque_ty_id - } - _ => return ty, - }; - let (impl_traits, idx) = - match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { - // We don't replace opaque types from other kind with inference vars - // because `insert_inference_vars_for_impl_traits` for each kinds - // and unreplaced opaque types of other kind are resolved while - // inferencing because of `tait_coercion_table`. - // Moreover, calling `insert_inference_vars_for_impl_traits` with same - // `placeholders` for other kind may cause trouble because - // the substs for the bounds of each impl traits do not match - ImplTraitId::ReturnTypeImplTrait(def, idx) => { - if matches!(mode, ImplTraitReplacingMode::TypeAlias) { - // RPITs don't have `tait_coercion_table`, so use inserted inference - // vars for them. - if let Some(ty) = self.result.type_of_rpit.get(idx) { - return ty.clone(); - } - return ty; - } - (self.db.return_type_impl_traits(def), idx) - } - ImplTraitId::TypeAliasImplTrait(def, idx) => { - if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { - // Gather TAITs while replacing RPITs because TAITs inside RPITs - // may not visited while replacing TAITs - taits.insert(ty.clone()); - return ty; - } - (self.db.type_alias_impl_traits(def), idx) + fold_tys(self.interner(), t, |ty| { + let ty = self.table.try_structurally_resolve_type(ty); + let opaque_ty_id = match ty.kind() { + TyKind::Alias(AliasTyKind::Opaque, alias_ty) => alias_ty.def_id.expect_opaque_ty(), + _ => return ty, + }; + let (impl_traits, idx) = match self.db.lookup_intern_impl_trait_id(opaque_ty_id) { + // We don't replace opaque types from other kind with inference vars + // because `insert_inference_vars_for_impl_traits` for each kinds + // and unreplaced opaque types of other kind are resolved while + // inferencing because of `tait_coercion_table`. + ImplTraitId::ReturnTypeImplTrait(def, idx) => { + if matches!(mode, ImplTraitReplacingMode::TypeAlias) { + // RPITs don't have `tait_coercion_table`, so use inserted inference + // vars for them. + if let Some(ty) = + self.result.type_of_rpit.get(idx.to_nextsolver(self.interner())) + { + return *ty; } - _ => unreachable!(), - }; - let Some(impl_traits) = impl_traits else { - return ty; - }; - let bounds = (*impl_traits) - .map_ref(|its| its.impl_traits[idx].bounds.map_ref(|it| it.iter())); - let var = self.table.new_type_var(); - let var_subst = Substitution::from1(Interner, var.clone()); - for bound in bounds { - let predicate = bound.map(|it| it.cloned()); - let predicate = predicate.substitute(Interner, &placeholders); - let (var_predicate, binders) = - predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); - always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_impl_trait( - var_predicate, - placeholders.clone(), - mode, - ); - self.push_obligation(var_predicate.cast(Interner)); + return ty; + } + (self.db.return_type_impl_traits_ns(def), idx) } - self.result.type_of_rpit.insert(idx, var.clone()); - var - }, - DebruijnIndex::INNERMOST, - ) + ImplTraitId::TypeAliasImplTrait(def, idx) => { + if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { + // Gather TAITs while replacing RPITs because TAITs inside RPITs + // may not visited while replacing TAITs + taits.insert(ty); + return ty; + } + (self.db.type_alias_impl_traits_ns(def), idx) + } + _ => unreachable!(), + }; + let Some(impl_traits) = impl_traits else { + return ty; + }; + let bounds = (*impl_traits).as_ref().map_bound(|its| { + its.impl_traits[idx.to_nextsolver(self.interner())].predicates.as_slice() + }); + let var = match self.result.type_of_rpit.entry(idx.to_nextsolver(self.interner())) { + Entry::Occupied(entry) => return *entry.get(), + Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()), + }; + for clause in bounds.iter_identity_copied() { + let clause = self.insert_inference_vars_for_impl_trait(clause, mode); + self.table.register_predicate(Obligation::new( + self.interner(), + ObligationCause::new(), + self.table.trait_env.env, + clause, + )); + } + var + }) } /// The coercion of a non-inference var into an opaque type should fail, @@ -1296,51 +1317,38 @@ impl<'db> InferenceContext<'db> { /// - We are pushing `impl Trait` bounds into it /// /// This function inserts a map that maps the opaque type to that proxy inference var. - fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator) { - struct TypeAliasImplTraitCollector<'a, 'b> { - db: &'b dyn HirDatabase, - table: &'b mut InferenceTable<'a>, - assocs: FxHashMap, - non_assocs: FxHashMap, + fn make_tait_coercion_table(&mut self, tait_candidates: impl Iterator>) { + struct TypeAliasImplTraitCollector<'a, 'db> { + db: &'a dyn HirDatabase, + table: &'a mut InferenceTable<'db>, + assocs: FxHashMap)>, + non_assocs: FxHashMap>, } - impl TypeVisitor for TypeAliasImplTraitCollector<'_, '_> { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } + impl<'db> TypeVisitor> for TypeAliasImplTraitCollector<'_, 'db> { + type Result = (); - fn visit_ty( - &mut self, - ty: &chalk_ir::Ty, - outer_binder: DebruijnIndex, - ) -> std::ops::ControlFlow { - let ty = self.table.structurally_resolve_type(ty); + fn visit_ty(&mut self, ty: Ty<'db>) { + let ty = self.table.try_structurally_resolve_type(ty); - if let TyKind::OpaqueType(id, _) - | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id: id, .. })) = - ty.kind(Interner) + if let TyKind::Alias(AliasTyKind::Opaque, alias_ty) = ty.kind() + && let id = alias_ty.def_id.expect_opaque_ty() && let ImplTraitId::TypeAliasImplTrait(alias_id, _) = - self.db.lookup_intern_impl_trait_id((*id).into()) + self.db.lookup_intern_impl_trait_id(id) { let loc = self.db.lookup_intern_type_alias(alias_id); match loc.container { ItemContainerId::ImplId(impl_id) => { - self.assocs.insert(*id, (impl_id, ty.clone())); + self.assocs.insert(id, (impl_id, ty)); } ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => { - self.non_assocs.insert(*id, ty.clone()); + self.non_assocs.insert(id, ty); } _ => {} } } - ty.super_visit_with(self, outer_binder) + ty.super_visit_with(self) } } @@ -1351,7 +1359,7 @@ impl<'db> InferenceContext<'db> { non_assocs: FxHashMap::default(), }; for ty in tait_candidates { - _ = ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + ty.visit_with(&mut collector); } // Non-assoc TAITs can be define-used everywhere as long as they are @@ -1405,13 +1413,10 @@ impl<'db> InferenceContext<'db> { let tait_coercion_table: FxHashMap<_, _> = taits .into_iter() .filter_map(|(id, ty)| { - if let ImplTraitId::TypeAliasImplTrait(alias_id, _) = - self.db.lookup_intern_impl_trait_id(id.into()) + if let ImplTraitId::TypeAliasImplTrait(..) = self.db.lookup_intern_impl_trait_id(id) { - let subst = TyBuilder::placeholder_subst(self.db, alias_id); let ty = self.insert_inference_vars_for_impl_trait( ty, - subst, &mut ImplTraitReplacingMode::TypeAlias, ); Some((id, ty)) @@ -1432,18 +1437,18 @@ impl<'db> InferenceContext<'db> { None => { _ = self.infer_expr_coerce( self.body.body_expr, - &Expectation::has_type(self.return_ty.clone()), + &Expectation::has_type(self.return_ty), ExprIsRead::Yes, ) } } } - fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) { + fn write_expr_ty(&mut self, expr: ExprId, ty: Ty<'db>) { self.result.type_of_expr.insert(expr, ty); } - fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment]>) { + fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment<'db>]>) { if adjustments.is_empty() { return; } @@ -1456,7 +1461,7 @@ impl<'db> InferenceContext<'db> { ) => { // NeverToAny coercion can target any type, so instead of adding a new // adjustment on top we can change the target. - *target = new_target.clone(); + *target = *new_target; } _ => { *entry.get_mut() = adjustments; @@ -1469,14 +1474,14 @@ impl<'db> InferenceContext<'db> { } } - fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[Ty]>) { + fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[Ty<'db>]>) { if adjustments.is_empty() { return; } self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); } - fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { + fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: GenericArgs<'db>) { self.result.method_resolutions.insert(expr, (func, subst)); } @@ -1484,19 +1489,24 @@ impl<'db> InferenceContext<'db> { self.result.variant_resolutions.insert(id, variant); } - fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId, subs: Substitution) { + fn write_assoc_resolution( + &mut self, + id: ExprOrPatId, + item: AssocItemId, + subs: GenericArgs<'db>, + ) { self.result.assoc_resolutions.insert(id, (item, subs)); } - fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { + fn write_pat_ty(&mut self, pat: PatId, ty: Ty<'db>) { self.result.type_of_pat.insert(pat, ty); } - fn write_binding_ty(&mut self, id: BindingId, ty: Ty) { + fn write_binding_ty(&mut self, id: BindingId, ty: Ty<'db>) { self.result.type_of_binding.insert(id, ty); } - fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) { + fn push_diagnostic(&self, diagnostic: InferenceDiagnostic<'db>) { self.diagnostics.push(diagnostic); } @@ -1504,8 +1514,8 @@ impl<'db> InferenceContext<'db> { &mut self, store: &ExpressionStore, types_source: InferenceTyDiagnosticSource, - lifetime_elision: LifetimeElisionKind, - f: impl FnOnce(&mut TyLoweringContext<'_>) -> R, + lifetime_elision: LifetimeElisionKind<'db>, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, ) -> R { let mut ctx = TyLoweringContext::new( self.db, @@ -1519,7 +1529,10 @@ impl<'db> InferenceContext<'db> { f(&mut ctx) } - fn with_body_ty_lowering(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R { + fn with_body_ty_lowering( + &mut self, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, + ) -> R { self.with_ty_lowering( self.body, InferenceTyDiagnosticSource::Body, @@ -1533,14 +1546,14 @@ impl<'db> InferenceContext<'db> { type_ref: TypeRefId, store: &ExpressionStore, type_source: InferenceTyDiagnosticSource, - lifetime_elision: LifetimeElisionKind, - ) -> Ty { + lifetime_elision: LifetimeElisionKind<'db>, + ) -> Ty<'db> { let ty = self .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); self.process_user_written_ty(ty) } - fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty { + fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { self.make_ty( type_ref, self.body, @@ -1549,37 +1562,31 @@ impl<'db> InferenceContext<'db> { ) } - fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty) -> Const { + fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> { let const_ = self.with_ty_lowering( self.body, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, - |ctx| { - ctx.type_param_mode = ParamLoweringMode::Placeholder; - ctx.lower_const(&const_ref, ty) - }, + |ctx| ctx.lower_const(const_ref, ty), ); self.insert_type_vars(const_) } - fn make_path_as_body_const(&mut self, path: &Path, ty: Ty) -> Const { + fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> { let const_ = self.with_ty_lowering( self.body, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, - |ctx| { - ctx.type_param_mode = ParamLoweringMode::Placeholder; - ctx.lower_path_as_const(path, ty) - }, + |ctx| ctx.lower_path_as_const(path, ty), ); self.insert_type_vars(const_) } - fn err_ty(&self) -> Ty { - self.result.standard_types.unknown.clone() + fn err_ty(&self) -> Ty<'db> { + self.types.error } - fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Lifetime { + fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> { let lt = self.with_ty_lowering( self.body, InferenceTyDiagnosticSource::Body, @@ -1590,44 +1597,25 @@ impl<'db> InferenceContext<'db> { } /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { + fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { self.table.insert_type_vars_shallow(ty) } fn insert_type_vars(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: TypeFoldable>, { self.table.insert_type_vars(ty) } - fn push_obligation(&mut self, o: DomainGoal) { - let goal: crate::Goal = o.cast(Interner); - self.table.register_obligation(goal.to_nextsolver(self.table.interner)); - } - - fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - let ty1 = ty1 - .clone() - .try_fold_with( - &mut UnevaluatedConstEvaluatorFolder { db: self.db }, - DebruijnIndex::INNERMOST, - ) - .unwrap(); - let ty2 = ty2 - .clone() - .try_fold_with( - &mut UnevaluatedConstEvaluatorFolder { db: self.db }, - DebruijnIndex::INNERMOST, - ) - .unwrap(); - self.table.unify(&ty1, &ty2) + fn unify(&mut self, ty1: Ty<'db>, ty2: Ty<'db>) -> bool { + self.table.unify(ty1, ty2) } /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. - fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty { + fn struct_tail_without_normalization(&mut self, ty: Ty<'db>) -> Ty<'db> { self.struct_tail_with_normalize(ty, identity) } @@ -1640,37 +1628,34 @@ impl<'db> InferenceContext<'db> { /// function to indicate no normalization should take place. fn struct_tail_with_normalize( &mut self, - mut ty: Ty, - mut normalize: impl FnMut(Ty) -> Ty, - ) -> Ty { + mut ty: Ty<'db>, + mut normalize: impl FnMut(Ty<'db>) -> Ty<'db>, + ) -> Ty<'db> { // FIXME: fetch the limit properly let recursion_limit = 10; for iteration in 0.. { if iteration > recursion_limit { return self.err_ty(); } - match ty.kind(Interner) { - TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => { - match self.db.field_types((*struct_id).into()).values().next_back().cloned() { - Some(field) => { - ty = field.substitute(Interner, substs); + match ty.kind() { + TyKind::Adt(adt_def, substs) => match adt_def.def_id().0 { + AdtId::StructId(struct_id) => { + match self.db.field_types_ns(struct_id.into()).values().next_back().copied() + { + Some(field) => { + ty = field.instantiate(self.interner(), substs); + } + None => break, } - None => break, - } - } - TyKind::Adt(..) => break, - TyKind::Tuple(_, substs) => { - match substs - .as_slice(Interner) - .split_last() - .and_then(|(last_ty, _)| last_ty.ty(Interner)) - { - Some(last_ty) => ty = last_ty.clone(), - None => break, } - } + _ => break, + }, + TyKind::Tuple(substs) => match substs.as_slice().split_last() { + Some((last_ty, _)) => ty = *last_ty, + None => break, + }, TyKind::Alias(..) => { - let normalized = normalize(ty.clone()); + let normalized = normalize(ty); if ty == normalized { return ty; } else { @@ -1684,41 +1669,35 @@ impl<'db> InferenceContext<'db> { } /// Whenever you lower a user-written type, you should call this. - fn process_user_written_ty(&mut self, ty: T) -> T + fn process_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + T: TypeFoldable>, { self.table.process_user_written_ty(ty) } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, /// while `process_user_written_ty()` should (but doesn't currently). - fn process_remote_user_written_ty(&mut self, ty: T) -> T + fn process_remote_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + T: TypeFoldable>, { self.table.process_remote_user_written_ty(ty) } - fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { - self.table.resolve_ty_shallow(ty) - } - - fn shallow_resolve(&self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { + fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { self.table.shallow_resolve(ty) } - fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option) -> Ty { + fn resolve_associated_type( + &mut self, + inner_ty: Ty<'db>, + assoc_ty: Option, + ) -> Ty<'db> { self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) } - fn demand_eqtype( - &mut self, - expected: crate::next_solver::Ty<'db>, - actual: crate::next_solver::Ty<'db>, - ) { + fn demand_eqtype(&mut self, expected: Ty<'db>, actual: Ty<'db>) { let result = self .table .infer_ctxt @@ -1732,34 +1711,24 @@ impl<'db> InferenceContext<'db> { fn resolve_associated_type_with_params( &mut self, - inner_ty: Ty, + inner_ty: Ty<'db>, assoc_ty: Option, // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be // handled when we support them. - params: &[GenericArg], - ) -> Ty { + params: &[GenericArg<'db>], + ) -> Ty<'db> { match assoc_ty { Some(res_assoc_ty) => { - let trait_ = match res_assoc_ty.lookup(self.db).container { - hir_def::ItemContainerId::TraitId(trait_) => trait_, - _ => panic!("resolve_associated_type called with non-associated type"), - }; - let ty = self.table.new_type_var(); - let mut param_iter = params.iter().cloned(); - let trait_ref = TyBuilder::trait_ref(self.db, trait_) - .push(inner_ty) - .fill(|_| param_iter.next().unwrap()) - .build(); - let alias_eq = AliasEq { - alias: AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(res_assoc_ty), - substitution: trait_ref.substitution.clone(), - }), - ty: ty.clone(), - }; - self.push_obligation(trait_ref.cast(Interner)); - self.push_obligation(alias_eq.cast(Interner)); - ty + let alias = Ty::new_alias( + self.interner(), + AliasTyKind::Projection, + AliasTy::new( + self.interner(), + res_assoc_ty.into(), + iter::once(inner_ty.into()).chain(params.iter().copied()), + ), + ); + self.table.try_structurally_resolve_type(alias) } None => self.err_ty(), } @@ -1770,7 +1739,7 @@ impl<'db> InferenceContext<'db> { node: ExprOrPatId, path: Option<&Path>, value_ns: bool, - ) -> (Ty, Option) { + ) -> (Ty<'db>, Option) { let path = match path { Some(path) => path, None => return (self.err_ty(), None), @@ -1793,28 +1762,19 @@ impl<'db> InferenceContext<'db> { match res { ResolveValueResult::ValueNs(value, _) => match value { ValueNs::EnumVariantId(var) => { - let substs = path_ctx.substs_from_path(var.into(), true, false); + let args = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); - let args: crate::next_solver::GenericArgs<'_> = - substs.to_nextsolver(interner); let ty = self .db .ty(var.lookup(self.db).parent.into()) - .instantiate(interner, args) - .to_chalk(interner); + .instantiate(interner, args); let ty = self.insert_type_vars(ty); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { - let substs = path_ctx.substs_from_path(strukt.into(), true, false); + let args = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); - let args: crate::next_solver::GenericArgs<'_> = - substs.to_nextsolver(interner); - let ty = self - .db - .ty(strukt.into()) - .instantiate(interner, args) - .to_chalk(interner); + let ty = self.db.ty(strukt.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty); return (ty, Some(strukt.into())); } @@ -1834,39 +1794,28 @@ impl<'db> InferenceContext<'db> { }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { - let substs = path_ctx.substs_from_path(strukt.into(), true, false); + let args = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = self.db.ty(strukt.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.db.ty(strukt.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty); - forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) + forbid_unresolved_segments(self, (ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { - let substs = path_ctx.substs_from_path(u.into(), true, false); + let args = path_ctx.substs_from_path(u.into(), true, false); drop(ctx); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = self.db.ty(u.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.db.ty(u.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty); - forbid_unresolved_segments((ty, Some(u.into())), unresolved) + forbid_unresolved_segments(self, (ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { - let substs = path_ctx.substs_from_path(var.into(), true, false); + let args = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = self - .db - .ty(var.lookup(self.db).parent.into()) - .instantiate(interner, args) - .to_chalk(interner); + let ty = self.db.ty(var.lookup(self.db).parent.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty); - forbid_unresolved_segments((ty, Some(var.into())), unresolved) + forbid_unresolved_segments(self, (ty, Some(var.into())), unresolved) } TypeNs::SelfType(impl_id) => { - let generics = crate::generics::generics(self.db, impl_id.into()); - let substs = generics.placeholder_subst(self.db); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let mut ty = - self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); + let mut ty = self.db.impl_self_ty(impl_id).instantiate_identity(); let Some(remaining_idx) = unresolved else { drop(ctx); @@ -1889,7 +1838,9 @@ impl<'db> InferenceContext<'db> { while let Some(current_segment) = remaining_segments.first() { // If we can resolve to an enum variant, it takes priority over associated type // of the same name. - if let Some((AdtId::EnumId(id), _)) = ty.as_adt() { + if let TyKind::Adt(adt_def, _) = ty.kind() + && let AdtId::EnumId(id) = adt_def.def_id().0 + { let enum_data = id.enum_variants(self.db); if let Some(variant) = enum_data.variant(current_segment.name) { return if remaining_segments.len() == 1 { @@ -1917,8 +1868,8 @@ impl<'db> InferenceContext<'db> { ty = self.table.insert_type_vars(ty); ty = self.table.normalize_associated_types_in(ty); - ty = self.table.structurally_resolve_type(&ty); - if ty.is_unknown() { + ty = self.table.structurally_resolve_type(ty); + if ty.is_ty_error() { return (self.err_ty(), None); } @@ -1941,11 +1892,10 @@ impl<'db> InferenceContext<'db> { never!("resolver should always resolve lang item paths"); return (self.err_ty(), None); }; - let substs = path_ctx.substs_from_path_segment(it.into(), true, None, false); + let args = path_ctx.substs_from_path_segment(it.into(), true, None, false); drop(ctx); let interner = DbInterner::conjure(); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = self.db.ty(it.into()).instantiate(interner, args).to_chalk(interner); + let ty = self.db.ty(it.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty); self.resolve_variant_on_alias(ty, unresolved, mod_path) @@ -1967,33 +1917,28 @@ impl<'db> InferenceContext<'db> { } }; - fn forbid_unresolved_segments( - result: (Ty, Option), + fn forbid_unresolved_segments<'db>( + ctx: &InferenceContext<'_, 'db>, + result: (Ty<'db>, Option), unresolved: Option, - ) -> (Ty, Option) { + ) -> (Ty<'db>, Option) { if unresolved.is_none() { result } else { // FIXME diagnostic - (TyKind::Error.intern(Interner), None) + (ctx.types.error, None) } } } fn resolve_variant_on_alias( &mut self, - ty: Ty, + ty: Ty<'db>, unresolved: Option, path: &ModPath, - ) -> (Ty, Option) { + ) -> (Ty<'db>, Option) { let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); - let ty = match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - let ty = self.table.normalize_projection_ty(proj_ty.clone()); - self.table.structurally_resolve_type(&ty) - } - _ => ty, - }; + let ty = self.table.try_structurally_resolve_type(ty); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -2115,19 +2060,18 @@ impl<'db> InferenceContext<'db> { /// When inferring an expression, we propagate downward whatever type hint we /// are able in the form of an `Expectation`. #[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) enum Expectation { +pub(crate) enum Expectation<'db> { None, - HasType(Ty), - #[allow(dead_code)] - Castable(Ty), - RValueLikeUnsized(Ty), + HasType(Ty<'db>), + Castable(Ty<'db>), + RValueLikeUnsized(Ty<'db>), } -impl Expectation { +impl<'db> Expectation<'db> { /// The expectation that the type of the expression needs to equal the given /// type. - fn has_type(ty: Ty) -> Self { - if ty.is_unknown() { + fn has_type(ty: Ty<'db>) -> Self { + if ty.is_ty_error() { // FIXME: get rid of this? Expectation::None } else { @@ -2155,9 +2099,11 @@ impl Expectation { /// which still is useful, because it informs integer literals and the like. /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. - fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self { - match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) { - TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty), + fn rvalue_hint(ctx: &mut InferenceContext<'_, 'db>, ty: Ty<'db>) -> Self { + match ctx.struct_tail_without_normalization(ty).kind() { + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(..) => { + Expectation::RValueLikeUnsized(ty) + } _ => Expectation::has_type(ty), } } @@ -2167,18 +2113,18 @@ impl Expectation { Expectation::None } - fn resolve(&self, table: &mut unify::InferenceTable<'_>) -> Expectation { + fn resolve(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { match self { Expectation::None => Expectation::None, - Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)), - Expectation::Castable(t) => Expectation::Castable(table.resolve_ty_shallow(t)), + Expectation::HasType(t) => Expectation::HasType(table.shallow_resolve(*t)), + Expectation::Castable(t) => Expectation::Castable(table.shallow_resolve(*t)), Expectation::RValueLikeUnsized(t) => { - Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t)) + Expectation::RValueLikeUnsized(table.shallow_resolve(*t)) } } } - fn to_option(&self, table: &mut unify::InferenceTable<'_>) -> Option { + fn to_option(&self, table: &mut unify::InferenceTable<'db>) -> Option> { match self.resolve(table) { Expectation::None => None, Expectation::HasType(t) @@ -2187,17 +2133,17 @@ impl Expectation { } } - fn only_has_type(&self, table: &mut unify::InferenceTable<'_>) -> Option { + fn only_has_type(&self, table: &mut unify::InferenceTable<'db>) -> Option> { match self { - Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)), + Expectation::HasType(t) => Some(table.shallow_resolve(*t)), Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => { None } } } - fn coercion_target_type(&self, table: &mut unify::InferenceTable<'_>) -> Ty { - self.only_has_type(table).unwrap_or_else(|| table.new_type_var()) + fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>) -> Ty<'db> { + self.only_has_type(table).unwrap_or_else(|| table.next_ty_var()) } /// Comment copied from rustc: @@ -2217,13 +2163,13 @@ impl Expectation { /// an expected type. Otherwise, we might write parts of the type /// when checking the 'then' block which are incompatible with the /// 'else' branch. - fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'_>) -> Expectation { - match self { + fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { + match *self { Expectation::HasType(ety) => { let ety = table.structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } - Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()), + Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), _ => Expectation::None, } } @@ -2266,123 +2212,3 @@ impl std::ops::BitOrAssign for Diverges { *self = *self | other; } } - -/// A zipper that checks for unequal occurrences of `{unknown}` and unresolved projections -/// in the two types. Used to filter out mismatch diagnostics that only differ in -/// `{unknown}` and unresolved projections. These mismatches are usually not helpful. -/// As the cause is usually an underlying name resolution problem -struct UnknownMismatch<'db>(&'db dyn HirDatabase); -impl chalk_ir::zip::Zipper for UnknownMismatch<'_> { - fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> { - let zip_substs = |this: &mut Self, - variances, - sub_a: &Substitution, - sub_b: &Substitution| { - this.zip_substs(variance, variances, sub_a.as_slice(Interner), sub_b.as_slice(Interner)) - }; - match (a.kind(Interner), b.kind(Interner)) { - (TyKind::Adt(id_a, sub_a), TyKind::Adt(id_b, sub_b)) if id_a == id_b => zip_substs( - self, - Some(self.unification_database().adt_variance(*id_a)), - sub_a, - sub_b, - )?, - ( - TyKind::AssociatedType(assoc_ty_a, sub_a), - TyKind::AssociatedType(assoc_ty_b, sub_b), - ) if assoc_ty_a == assoc_ty_b => zip_substs(self, None, sub_a, sub_b)?, - (TyKind::Tuple(arity_a, sub_a), TyKind::Tuple(arity_b, sub_b)) - if arity_a == arity_b => - { - zip_substs(self, None, sub_a, sub_b)? - } - (TyKind::OpaqueType(opaque_ty_a, sub_a), TyKind::OpaqueType(opaque_ty_b, sub_b)) - if opaque_ty_a == opaque_ty_b => - { - zip_substs(self, None, sub_a, sub_b)? - } - (TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => self.zip_tys(variance, ty_a, ty_b)?, - (TyKind::FnDef(fn_def_a, sub_a), TyKind::FnDef(fn_def_b, sub_b)) - if fn_def_a == fn_def_b => - { - zip_substs( - self, - Some(self.unification_database().fn_def_variance(*fn_def_a)), - sub_a, - sub_b, - )? - } - (TyKind::Ref(mutability_a, _, ty_a), TyKind::Ref(mutability_b, _, ty_b)) - if mutability_a == mutability_b => - { - self.zip_tys(variance, ty_a, ty_b)? - } - (TyKind::Raw(mutability_a, ty_a), TyKind::Raw(mutability_b, ty_b)) - if mutability_a == mutability_b => - { - self.zip_tys(variance, ty_a, ty_b)? - } - (TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) if const_a == const_b => { - self.zip_tys(variance, ty_a, ty_b)? - } - (TyKind::Closure(id_a, sub_a), TyKind::Closure(id_b, sub_b)) if id_a == id_b => { - zip_substs(self, None, sub_a, sub_b)? - } - (TyKind::Coroutine(coroutine_a, sub_a), TyKind::Coroutine(coroutine_b, sub_b)) - if coroutine_a == coroutine_b => - { - zip_substs(self, None, sub_a, sub_b)? - } - ( - TyKind::CoroutineWitness(coroutine_a, sub_a), - TyKind::CoroutineWitness(coroutine_b, sub_b), - ) if coroutine_a == coroutine_b => zip_substs(self, None, sub_a, sub_b)?, - (TyKind::Function(fn_ptr_a), TyKind::Function(fn_ptr_b)) - if fn_ptr_a.sig == fn_ptr_b.sig && fn_ptr_a.num_binders == fn_ptr_b.num_binders => - { - zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)? - } - (TyKind::Error, TyKind::Error) => (), - (TyKind::Error, _) - | (_, TyKind::Error) - | (TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _), _) - | (_, TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)) => { - return Err(chalk_ir::NoSolution); - } - _ => (), - } - - Ok(()) - } - - fn zip_lifetimes(&mut self, _: Variance, _: &Lifetime, _: &Lifetime) -> chalk_ir::Fallible<()> { - Ok(()) - } - - fn zip_consts(&mut self, _: Variance, _: &Const, _: &Const) -> chalk_ir::Fallible<()> { - Ok(()) - } - - fn zip_binders( - &mut self, - variance: Variance, - a: &Binders, - b: &Binders, - ) -> chalk_ir::Fallible<()> - where - T: Clone - + HasInterner - + chalk_ir::zip::Zip - + TypeFoldable, - { - chalk_ir::zip::Zip::zip_with(self, variance, a.skip_binders(), b.skip_binders()) - } - - fn interner(&self) -> Interner { - Interner - } - - fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { - &self.0 - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs index 77b1ae6a94a46..ba133aa553d7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs @@ -2,6 +2,8 @@ use std::iter; +use rustc_ast_ir::Mutability; + use crate::{ Adjust, Adjustment, OverloadedDeref, autoderef::{Autoderef, AutoderefKind}, @@ -9,7 +11,6 @@ use crate::{ next_solver::{ Ty, infer::{InferOk, traits::PredicateObligations}, - mapping::NextSolverToChalk, }, }; @@ -21,12 +22,12 @@ impl<'db> InferenceTable<'db> { impl<'db> Autoderef<'_, 'db> { /// Returns the adjustment steps. - pub(crate) fn adjust_steps(mut self) -> Vec { + pub(crate) fn adjust_steps(mut self) -> Vec> { let infer_ok = self.adjust_steps_as_infer_ok(); self.table.register_infer_ok(infer_ok) } - pub(crate) fn adjust_steps_as_infer_ok(&mut self) -> InferOk<'db, Vec> { + pub(crate) fn adjust_steps_as_infer_ok(&mut self) -> InferOk<'db, Vec>> { let steps = self.steps(); if steps.is_empty() { return InferOk { obligations: PredicateObligations::new(), value: vec![] }; @@ -37,16 +38,13 @@ impl<'db> Autoderef<'_, 'db> { .iter() .map(|&(_source, kind)| { if let AutoderefKind::Overloaded = kind { - Some(OverloadedDeref(Some(chalk_ir::Mutability::Not))) + Some(OverloadedDeref(Some(Mutability::Not))) } else { None } }) .zip(targets) - .map(|(autoderef, target)| Adjustment { - kind: Adjust::Deref(autoderef), - target: target.to_chalk(self.table.interner), - }) + .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) .collect(); InferOk { obligations: self.take_obligations(), value: steps } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 4cd6144a14cba..017f45f43df46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,17 +1,18 @@ //! Type cast logic. Basically coercion + additional casts. -use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + Flags, InferTy, TypeFlags, UintTy, + inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, SliceLike, Ty as _}, +}; use stdx::never; -use crate::infer::coerce::CoerceNever; use crate::{ - Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, QuantifiedWhereClauses, Ty, - TyExt, TyKind, TypeFlags, WhereClause, + InferenceDiagnostic, db::HirDatabase, - from_chalk_trait_id, - infer::{AllowTwoPhase, InferenceContext}, - next_solver::mapping::ChalkToNextSolver, + infer::{AllowTwoPhase, InferenceContext, coerce::CoerceNever}, + next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind}, }; #[derive(Debug)] @@ -25,24 +26,24 @@ pub(crate) enum Int { } #[derive(Debug)] -pub(crate) enum CastTy { +pub(crate) enum CastTy<'db> { Int(Int), Float, FnPtr, - Ptr(Ty, Mutability), + Ptr(Ty<'db>, Mutability), // `DynStar` is Not supported yet in r-a } -impl CastTy { - pub(crate) fn from_ty(db: &dyn HirDatabase, t: &Ty) -> Option { - match t.kind(Interner) { - TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), - TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), - TyKind::Scalar(Scalar::Int(_)) => Some(Self::Int(Int::I)), - TyKind::Scalar(Scalar::Uint(it)) => Some(Self::Int(Int::U(*it))), - TyKind::InferenceVar(_, TyVariableKind::Integer) => Some(Self::Int(Int::InferenceVar)), - TyKind::InferenceVar(_, TyVariableKind::Float) => Some(Self::Float), - TyKind::Scalar(Scalar::Float(_)) => Some(Self::Float), +impl<'db> CastTy<'db> { + pub(crate) fn from_ty(db: &dyn HirDatabase, t: Ty<'db>) -> Option { + match t.kind() { + TyKind::Bool => Some(Self::Int(Int::Bool)), + TyKind::Char => Some(Self::Int(Int::Char)), + TyKind::Int(_) => Some(Self::Int(Int::I)), + TyKind::Uint(it) => Some(Self::Int(Int::U(it))), + TyKind::Infer(InferTy::IntVar(_)) => Some(Self::Int(Int::InferenceVar)), + TyKind::Infer(InferTy::FloatVar(_)) => Some(Self::Float), + TyKind::Float(_) => Some(Self::Float), TyKind::Adt(..) => { let (AdtId::EnumId(id), _) = t.as_adt()? else { return None; @@ -50,8 +51,8 @@ impl CastTy { let enum_data = id.enum_variants(db); if enum_data.is_payload_free(db) { Some(Self::Int(Int::CEnum)) } else { None } } - TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)), - TyKind::Function(_) => Some(Self::FnPtr), + TyKind::RawPtr(ty, m) => Some(Self::Ptr(ty, m)), + TyKind::FnPtr(..) => Some(Self::FnPtr), _ => None, } } @@ -77,37 +78,47 @@ pub enum CastError { } impl CastError { - fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic { + fn into_diagnostic<'db>( + self, + expr: ExprId, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, + ) -> InferenceDiagnostic<'db> { InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty } } } #[derive(Clone, Debug)] -pub(super) struct CastCheck { +pub(super) struct CastCheck<'db> { expr: ExprId, source_expr: ExprId, - expr_ty: Ty, - cast_ty: Ty, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, } -impl CastCheck { - pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self { +impl<'db> CastCheck<'db> { + pub(super) fn new( + expr: ExprId, + source_expr: ExprId, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, + ) -> Self { Self { expr, source_expr, expr_ty, cast_ty } } pub(super) fn check( &mut self, - ctx: &mut InferenceContext<'_>, - ) -> Result<(), InferenceDiagnostic> { - self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); - self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + ctx: &mut InferenceContext<'_, 'db>, + ) -> Result<(), InferenceDiagnostic<'db>> { + self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty); + self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty); // This should always come first so that we apply the coercion, which impacts infer vars. if ctx .coerce( self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - self.cast_ty.to_nextsolver(ctx.table.interner), + self.expr_ty, + self.cast_ty, AllowTwoPhase::No, CoerceNever::Yes, ) @@ -117,83 +128,82 @@ impl CastCheck { return Ok(()); } - if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + if self.expr_ty.references_non_lt_error() || self.cast_ty.references_non_lt_error() { return Ok(()); } - if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) - && !ctx.table.is_sized(&self.cast_ty) + if !self.cast_ty.flags().contains(TypeFlags::HAS_TY_INFER) + && !ctx.table.is_sized(self.cast_ty) { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, - cast_ty: self.cast_ty.clone(), + cast_ty: self.cast_ty, }); } // Chalk doesn't support trait upcasting and fails to solve some obvious goals // when the trait environment contains some recursive traits (See issue #18047) // We skip cast checks for such cases for now, until the next-gen solver. - if contains_dyn_trait(&self.cast_ty) { + if contains_dyn_trait(self.cast_ty) { return Ok(()); } - self.do_check(ctx) - .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) + self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty)) } - fn do_check(&self, ctx: &mut InferenceContext<'_>) -> Result<(), CastError> { - let (t_from, t_cast) = match ( - CastTy::from_ty(ctx.db, &self.expr_ty), - CastTy::from_ty(ctx.db, &self.cast_ty), - ) { - (Some(t_from), Some(t_cast)) => (t_from, t_cast), - (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { - TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(ctx.db).expect("FnDef had no sig"); - let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); - let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if ctx - .coerce( - self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - fn_ptr.to_nextsolver(ctx.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) - .is_ok() - { - } else { - return Err(CastError::IllegalCast); - } + fn do_check(&self, ctx: &mut InferenceContext<'_, 'db>) -> Result<(), CastError> { + let (t_from, t_cast) = + match (CastTy::from_ty(ctx.db, self.expr_ty), CastTy::from_ty(ctx.db, self.cast_ty)) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind() { + TyKind::FnDef(..) => { + let sig = + self.expr_ty.callable_sig(ctx.interner()).expect("FnDef had no sig"); + let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); + let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty, + fn_ptr, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + } else { + return Err(CastError::IllegalCast); + } - (CastTy::FnPtr, t_cast) - } - TyKind::Ref(mutbl, _, inner_ty) => { - return match t_cast { - CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { - TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float, - ) => Err(CastError::NeedDeref), - - _ => Err(CastError::NeedViaPtr), - }, - // array-ptr-cast - CastTy::Ptr(t, m) => { - let t = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); - if !ctx.table.is_sized(&t) { - return Err(CastError::IllegalCast); + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(_, inner_ty, mutbl) => { + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind() { + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + Err(CastError::NeedDeref) + } + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); + if !ctx.table.is_sized(t) { + return Err(CastError::IllegalCast); + } + self.check_ref_cast(ctx, inner_ty, mutbl, t, m) } - self.check_ref_cast(ctx, inner_ty, *mutbl, &t, m) - } - _ => Err(CastError::NonScalar), - }; - } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + }, _ => return Err(CastError::NonScalar), - }, - _ => return Err(CastError::NonScalar), - }; + }; // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym @@ -207,10 +217,10 @@ impl CastCheck { } (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), - (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, &src, &dst), - (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, &src), - (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, &dst), - (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, &dst), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, dst), (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), @@ -220,23 +230,23 @@ impl CastCheck { fn check_ref_cast( &self, - ctx: &mut InferenceContext<'_>, - t_expr: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + t_expr: Ty<'db>, m_expr: Mutability, - t_cast: &Ty, + t_cast: Ty<'db>, m_cast: Mutability, ) -> Result<(), CastError> { // Mutability order is opposite to rustc. `Mut < Not` if m_expr <= m_cast - && let TyKind::Array(ety, _) = t_expr.kind(Interner) + && let TyKind::Array(ety, _) = t_expr.kind() { // Coerce to a raw pointer so that we generate RawPtr in MIR. - let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); + let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx .coerce( self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - array_ptr_type.to_nextsolver(ctx.table.interner), + self.expr_ty, + array_ptr_type, AllowTwoPhase::No, CoerceNever::Yes, ) @@ -253,13 +263,7 @@ impl CastCheck { // This is a less strict condition than rustc's `demand_eqtype`, // but false negative is better than false positive if ctx - .coerce( - self.source_expr.into(), - ety.to_nextsolver(ctx.table.interner), - t_cast.to_nextsolver(ctx.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) + .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, CoerceNever::Yes) .is_ok() { return Ok(()); @@ -271,9 +275,9 @@ impl CastCheck { fn check_ptr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - src: &Ty, - dst: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + src: Ty<'db>, + dst: Ty<'db>, ) -> Result<(), CastError> { let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; @@ -286,24 +290,13 @@ impl CastCheck { (_, Some(PointerKind::Thin)) => Ok(()), (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { - let principal = |tty: &Binders| { - tty.skip_binders().as_slice(Interner).first().and_then(|pred| { - if let WhereClause::Implemented(tr) = pred.skip_binders() { - Some(tr.trait_id) - } else { - None - } - }) - }; - match (principal(&src_tty), principal(&dst_tty)) { + match (src_tty.principal_def_id(), dst_tty.principal_def_id()) { (Some(src_principal), Some(dst_principal)) => { if src_principal == dst_principal { return Ok(()); } - let src_principal = - ctx.db.trait_signature(from_chalk_trait_id(src_principal)); - let dst_principal = - ctx.db.trait_signature(from_chalk_trait_id(dst_principal)); + let src_principal = ctx.db.trait_signature(src_principal.0); + let dst_principal = ctx.db.trait_signature(dst_principal.0); if src_principal.flags.contains(TraitFlags::AUTO) && dst_principal.flags.contains(TraitFlags::AUTO) { @@ -322,8 +315,8 @@ impl CastCheck { fn check_ptr_addr_cast( &self, - ctx: &mut InferenceContext<'_>, - expr_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + expr_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), @@ -336,8 +329,8 @@ impl CastCheck { fn check_addr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), @@ -352,8 +345,8 @@ impl CastCheck { fn check_fptr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), @@ -366,30 +359,34 @@ impl CastCheck { } #[derive(Debug, PartialEq, Eq)] -enum PointerKind { +enum PointerKind<'db> { // thin pointer Thin, // trait object - VTable(Binders), + VTable(BoundExistentialPredicates<'db>), // slice Length, OfAlias, - OfParam(PlaceholderIndex), + OfParam(ParamTy), Error, } -fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result, ()> { - let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); +fn pointer_kind<'db>( + ty: Ty<'db>, + ctx: &mut InferenceContext<'_, 'db>, +) -> Result>, ()> { + let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty); - if ctx.table.is_sized(&ty) { + if ctx.table.is_sized(ty) { return Ok(Some(PointerKind::Thin)); } - match ty.kind(Interner) { + match ty.kind() { TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), - TyKind::Dyn(DynTy { bounds, .. }) => Ok(Some(PointerKind::VTable(bounds.clone()))), - TyKind::Adt(chalk_ir::AdtId(id), subst) => { - let AdtId::StructId(id) = *id else { + TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))), + TyKind::Adt(adt_def, subst) => { + let id = adt_def.def_id().0; + let AdtId::StructId(id) = id else { never!("`{:?}` should be sized but is not?", ty); return Err(()); }; @@ -397,69 +394,63 @@ fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result { - match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { - None => Ok(Some(PointerKind::Thin)), - Some(ty) => pointer_kind(ty, ctx), - } - } + TyKind::Tuple(subst) => match subst.iter().last() { + None => Ok(Some(PointerKind::Thin)), + Some(ty) => pointer_kind(ty, ctx), + }, TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), - TyKind::Alias(_) | TyKind::AssociatedType(..) | TyKind::OpaqueType(..) => { - Ok(Some(PointerKind::OfAlias)) - } - TyKind::Error => Ok(Some(PointerKind::Error)), - TyKind::Placeholder(idx) => Ok(Some(PointerKind::OfParam(*idx))), - TyKind::BoundVar(_) | TyKind::InferenceVar(..) => Ok(None), - TyKind::Scalar(_) + TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)), + TyKind::Error(_) => Ok(Some(PointerKind::Error)), + TyKind::Param(idx) => Ok(Some(PointerKind::OfParam(idx))), + TyKind::Bound(..) | TyKind::Placeholder(..) | TyKind::Infer(..) => Ok(None), + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Bool + | TyKind::Char | TyKind::Array(..) | TyKind::CoroutineWitness(..) - | TyKind::Raw(..) + | TyKind::RawPtr(..) | TyKind::Ref(..) | TyKind::FnDef(..) - | TyKind::Function(_) + | TyKind::FnPtr(..) | TyKind::Closure(..) | TyKind::Coroutine(..) + | TyKind::CoroutineClosure(..) | TyKind::Never => { never!("`{:?}` should be sized but is not?", ty); Err(()) } + TyKind::UnsafeBinder(..) | TyKind::Pat(..) => { + never!("we don't produce these types: {ty:?}"); + Err(()) + } } } -fn contains_dyn_trait(ty: &Ty) -> bool { +fn contains_dyn_trait<'db>(ty: Ty<'db>) -> bool { use std::ops::ControlFlow; - use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, - }; + use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; struct DynTraitVisitor; - impl TypeVisitor for DynTraitVisitor { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } + impl<'db> TypeVisitor> for DynTraitVisitor { + type Result = ControlFlow<()>; - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - match ty.kind(Interner) { - TyKind::Dyn(_) => ControlFlow::Break(()), - _ => ty.super_visit_with(self.as_dyn(), outer_binder), + fn visit_ty(&mut self, ty: Ty<'db>) -> ControlFlow<()> { + match ty.kind() { + TyKind::Dynamic(..) => ControlFlow::Break(()), + _ => ty.super_visit_with(self), } } } - ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break() + ty.visit_with(&mut DynTraitVisitor).is_break() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 4a57b2f37510e..2637ed6b3ec93 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -2,8 +2,7 @@ pub(crate) mod analysis; -use std::ops::ControlFlow; -use std::{iter, mem}; +use std::{iter, mem, ops::ControlFlow}; use hir_def::{ TraitId, @@ -18,7 +17,6 @@ use rustc_type_ir::{ }; use tracing::debug; -use crate::traits::FnTrait; use crate::{ FnAbi, db::{InternedClosure, InternedCoroutine}, @@ -31,25 +29,25 @@ use crate::{ BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult, traits::{ObligationCause, PredicateObligations}, }, - mapping::{ChalkToNextSolver, NextSolverToChalk}, util::explicit_item_bounds, }, + traits::FnTrait, }; use super::{Expectation, InferenceContext}; #[derive(Debug)] -struct ClosureSignatures<'tcx> { +struct ClosureSignatures<'db> { /// The signature users of the closure see. - bound_sig: PolyFnSig<'tcx>, + bound_sig: PolyFnSig<'db>, /// The signature within the function body. /// This mostly differs in the sense that lifetimes are now early bound and any /// opaque types from the signature expectation are overridden in case there are /// explicit hidden types written by the user in the closure signature. - liberated_sig: FnSig<'tcx>, + liberated_sig: FnSig<'db>, } -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { pub(super) fn infer_closure( &mut self, body: ExprId, @@ -58,15 +56,13 @@ impl<'db> InferenceContext<'db> { arg_types: &[Option], closure_kind: ClosureKind, tgt_expr: ExprId, - expected: &Expectation, - ) -> crate::Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { assert_eq!(args.len(), arg_types.len()); - let interner = self.table.interner; + let interner = self.interner(); let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { - Some(expected_ty) => { - self.deduce_closure_signature(expected_ty.to_nextsolver(interner), closure_kind) - } + Some(expected_ty) => self.deduce_closure_signature(expected_ty, closure_kind), None => (None, None), }; @@ -79,10 +75,7 @@ impl<'db> InferenceContext<'db> { let (id, ty, resume_yield_tys) = match closure_kind { ClosureKind::Coroutine(_) => { let yield_ty = self.table.next_ty_var(); - let resume_ty = liberated_sig - .inputs() - .get(0) - .unwrap_or(self.result.standard_types.unit.to_nextsolver(interner)); + let resume_ty = liberated_sig.inputs().get(0).unwrap_or(self.types.unit); // FIXME: Infer the upvars later. let parts = CoroutineArgsParts { @@ -102,11 +95,7 @@ impl<'db> InferenceContext<'db> { CoroutineArgs::new(interner, parts).args, ); - ( - None, - coroutine_ty, - Some((resume_ty.to_chalk(interner), yield_ty.to_chalk(interner))), - ) + (None, coroutine_ty, Some((resume_ty, yield_ty))) } // FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`. // But for now we treat it as a closure. @@ -115,7 +104,7 @@ impl<'db> InferenceContext<'db> { match expected_kind { Some(kind) => { self.result.closure_info.insert( - closure_id.into(), + closure_id, ( Vec::new(), match kind { @@ -151,13 +140,13 @@ impl<'db> InferenceContext<'db> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { - self.infer_top_pat(*arg_pat, &arg_ty.to_chalk(interner), None); + self.infer_top_pat(*arg_pat, arg_ty, None); } // FIXME: lift these out into a struct let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_closure = mem::replace(&mut self.current_closure, id); - let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.to_chalk(interner)); + let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty); let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty)); let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys); @@ -171,7 +160,7 @@ impl<'db> InferenceContext<'db> { self.current_closure = prev_closure; self.resume_yield_tys = prev_resume_yield_tys; - ty.to_chalk(interner) + ty } fn fn_trait_kind_from_def_id(&self, trait_id: TraitId) -> Option { @@ -209,14 +198,13 @@ impl<'db> InferenceContext<'db> { .deduce_closure_signature_from_predicates( expected_ty, closure_kind, - explicit_item_bounds(self.table.interner, def_id) - .iter_instantiated(self.table.interner, args) + explicit_item_bounds(self.interner(), def_id) + .iter_instantiated(self.interner(), args) .map(|clause| clause.as_predicate()), ), TyKind::Dynamic(object_type, ..) => { let sig = object_type.projection_bounds().into_iter().find_map(|pb| { - let pb = - pb.with_self_ty(self.table.interner, Ty::new_unit(self.table.interner)); + let pb = pb.with_self_ty(self.interner(), Ty::new_unit(self.interner())); self.deduce_sig_from_projection(closure_kind, pb) }); let kind = object_type @@ -226,7 +214,7 @@ impl<'db> InferenceContext<'db> { } TyKind::Infer(rustc_type_ir::TyVar(vid)) => self .deduce_closure_signature_from_predicates( - Ty::new_var(self.table.interner, self.table.infer_ctxt.root_var(vid)), + Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)), closure_kind, self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), ), @@ -251,7 +239,7 @@ impl<'db> InferenceContext<'db> { let mut expected_kind = None; for pred in rustc_type_ir::elaborate::elaborate( - self.table.interner, + self.interner(), // Reverse the obligations here, since `elaborate_*` uses a stack, // and we want to keep inference generally in the same order of // the registered obligations. @@ -313,7 +301,7 @@ impl<'db> InferenceContext<'db> { // even though the normalized form may not name `expected_ty`. However, this matches the existing // behaviour of the old solver and would be technically a breaking change to fix. let generalized_fnptr_sig = self.table.next_ty_var(); - let inferred_fnptr_sig = Ty::new_fn_ptr(self.table.interner, inferred_sig); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.interner(), inferred_sig); // FIXME: Report diagnostics. _ = self .table @@ -326,7 +314,7 @@ impl<'db> InferenceContext<'db> { self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = Some(resolved_sig.fn_sig(self.table.interner)); + expected_sig = Some(resolved_sig.fn_sig(self.interner())); } } else if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { expected_sig = inferred_sig; @@ -339,7 +327,7 @@ impl<'db> InferenceContext<'db> { // many viable options, so pick the most restrictive. let trait_def_id = match bound_predicate.skip_binder() { PredicateKind::Clause(ClauseKind::Projection(data)) => { - Some(data.projection_term.trait_def_id(self.table.interner).0) + Some(data.projection_term.trait_def_id(self.interner()).0) } PredicateKind::Clause(ClauseKind::Trait(data)) => Some(data.def_id().0), _ => None, @@ -427,7 +415,7 @@ impl<'db> InferenceContext<'db> { let ret_param_ty = projection.skip_binder().term.expect_type(); debug!(?ret_param_ty); - let sig = projection.rebind(self.table.interner.mk_fn_sig( + let sig = projection.rebind(self.interner().mk_fn_sig( input_tys, ret_param_ty, false, @@ -515,7 +503,7 @@ impl<'db> InferenceContext<'db> { // that does not misuse a `FnSig` type, but that can be done separately. let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); - let sig = projection.rebind(self.table.interner.mk_fn_sig( + let sig = projection.rebind(self.interner().mk_fn_sig( input_tys, return_ty, false, @@ -619,7 +607,7 @@ impl<'db> InferenceContext<'db> { // in this binder we are creating. assert!(!expected_sig.skip_binder().has_vars_bound_above(rustc_type_ir::INNERMOST)); let bound_sig = expected_sig.map_bound(|sig| { - self.table.interner.mk_fn_sig( + self.interner().mk_fn_sig( sig.inputs(), sig.output(), sig.c_variadic, @@ -631,7 +619,7 @@ impl<'db> InferenceContext<'db> { // `deduce_expectations_from_expected_type` introduces // late-bound lifetimes defined elsewhere, which we now // anonymize away, so as not to confuse the user. - let bound_sig = self.table.interner.anonymize_bound_vars(bound_sig); + let bound_sig = self.interner().anonymize_bound_vars(bound_sig); let closure_sigs = self.closure_sigs(bound_sig); @@ -723,7 +711,7 @@ impl<'db> InferenceContext<'db> { .into_iter() .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); - expected_sigs.liberated_sig = table.interner.mk_fn_sig( + expected_sigs.liberated_sig = table.interner().mk_fn_sig( inputs, supplied_output_ty, expected_sigs.liberated_sig.c_variadic, @@ -744,12 +732,12 @@ impl<'db> InferenceContext<'db> { decl_inputs: &[Option], decl_output: Option, ) -> PolyFnSig<'db> { - let interner = self.table.interner; + let interner = self.interner(); let supplied_return = match decl_output { Some(output) => { let output = self.make_body_ty(output); - self.process_user_written_ty(output).to_nextsolver(interner) + self.process_user_written_ty(output) } None => self.table.next_ty_var(), }; @@ -757,7 +745,7 @@ impl<'db> InferenceContext<'db> { let supplied_arguments = decl_inputs.iter().map(|&input| match input { Some(input) => { let input = self.make_body_ty(input); - self.process_user_written_ty(input).to_nextsolver(interner) + self.process_user_written_ty(input) } None => self.table.next_ty_var(), }); @@ -779,7 +767,7 @@ impl<'db> InferenceContext<'db> { decl_inputs: &[Option], decl_output: Option, ) -> PolyFnSig<'db> { - let interner = self.table.interner; + let interner = self.interner(); let err_ty = Ty::new_error(interner, ErrorGuaranteed); if let Some(output) = decl_output { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index fd14b9e2de571..8a6ce0a69bda7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -2,10 +2,6 @@ use std::{cmp, convert::Infallible, mem}; -use chalk_ir::{ - BoundVar, DebruijnIndex, Mutability, TyKind, - fold::{FallibleTypeFolder, TypeFoldable}, -}; use either::Either; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, @@ -20,44 +16,47 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; +use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _}; use smallvec::{SmallVec, smallvec}; use stdx::{format_to, never}; use syntax::utils::is_raw_identifier; -use crate::db::InternedClosureId; -use crate::infer::InferenceContext; use crate::{ - Adjust, Adjustment, Binders, BindingMode, ClosureId, Interner, Substitution, Ty, TyExt, - db::{HirDatabase, InternedClosure}, - error_lifetime, from_placeholder_idx, - generics::Generics, - make_binders, + Adjust, Adjustment, BindingMode, + db::{HirDatabase, InternedClosure, InternedClosureId}, + infer::InferenceContext, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, + next_solver::{ + DbInterner, EarlyBinder, GenericArgs, Ty, TyKind, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, traits::FnTrait, - utils, }; // The below functions handle capture and closure kind (Fn, FnMut, ..) #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct HirPlace { +pub(crate) struct HirPlace<'db> { pub(crate) local: BindingId, - pub(crate) projections: Vec>, + pub(crate) projections: Vec>>, } -impl HirPlace { - fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { - let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); +impl<'db> HirPlace<'db> { + fn ty(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> { + let mut ty = ctx.table.resolve_completely(ctx.result[self.local]); for p in &self.projections { - ty = p.projected_ty( - ty, - ctx.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - ctx.owner.module(ctx.db).krate(), - ); + ty = p + .projected_ty( + ty.to_chalk(ctx.interner()), + ctx.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db).krate(), + ) + .to_nextsolver(ctx.interner()); } ty } @@ -86,8 +85,8 @@ pub enum CaptureKind { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct CapturedItem { - pub(crate) place: HirPlace, +pub struct CapturedItem<'db> { + pub(crate) place: HirPlace<'db>, pub(crate) kind: CaptureKind, /// The inner vec is the stacks; the outer vec is for each capture reference. /// @@ -96,10 +95,10 @@ pub struct CapturedItem { /// copy all captures of the inner closure to the outer closure, and then we may /// truncate them, and we want the correct span to be reported. span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, - pub(crate) ty: Binders, + pub(crate) ty: EarlyBinder<'db, Ty<'db>>, } -impl CapturedItem { +impl<'db> CapturedItem<'db> { pub fn local(&self) -> BindingId { self.place.local } @@ -109,8 +108,9 @@ impl CapturedItem { self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref)) } - pub fn ty(&self, db: &dyn HirDatabase, subst: &Substitution) -> Ty { - self.ty.clone().substitute(Interner, &utils::ClosureSubst(subst).parent_subst(db)) + pub fn ty(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> { + let interner = DbInterner::new_with(db, None, None); + self.ty.instantiate(interner, subst.split_closure_args_untupled().parent_args) } pub fn kind(&self) -> CaptureKind { @@ -279,15 +279,15 @@ impl CapturedItem { } #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct CapturedItemWithoutTy { - pub(crate) place: HirPlace, +pub(crate) struct CapturedItemWithoutTy<'db> { + pub(crate) place: HirPlace<'db>, pub(crate) kind: CaptureKind, /// The inner vec is the stacks; the outer vec is for each capture reference. pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, } -impl CapturedItemWithoutTy { - fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { +impl<'db> CapturedItemWithoutTy<'db> { + fn with_ty(self, ctx: &mut InferenceContext<'_, 'db>) -> CapturedItem<'db> { let ty = self.place.ty(ctx); let ty = match &self.kind { CaptureKind::ByValue => ty, @@ -296,66 +296,20 @@ impl CapturedItemWithoutTy { BorrowKind::Mut { .. } => Mutability::Mut, _ => Mutability::Not, }; - TyKind::Ref(m, error_lifetime(), ty).intern(Interner) + Ty::new_ref(ctx.interner(), ctx.types.re_error, ty, m) } }; - return CapturedItem { + CapturedItem { place: self.place, kind: self.kind, span_stacks: self.span_stacks, - ty: replace_placeholder_with_binder(ctx, ty), - }; - - fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { - struct Filler<'a> { - db: &'a dyn HirDatabase, - generics: &'a Generics, - } - impl FallibleTypeFolder for Filler<'_> { - type Error = (); - - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn try_fold_free_placeholder_const( - &mut self, - ty: chalk_ir::Ty, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) - } - - fn try_fold_free_placeholder_ty( - &mut self, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) - } - } - let filler = &mut Filler { db: ctx.db, generics: ctx.generics() }; - let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); - make_binders(ctx.db, filler.generics, result) + ty: EarlyBinder::bind(ty), } } } -impl InferenceContext<'_> { - fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { +impl<'db> InferenceContext<'_, 'db> { + fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option> { let r = self.place_of_expr_without_adjust(tgt_expr)?; let adjustments = self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default(); @@ -363,7 +317,7 @@ impl InferenceContext<'_> { } /// Pushes the span into `current_capture_span_stack`, *without clearing it first*. - fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option { + fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option> { if path.type_anchor().is_some() { return None; } @@ -384,7 +338,7 @@ impl InferenceContext<'_> { } /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. - fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { + fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option> { self.current_capture_span_stack.clear(); match &self.body[tgt_expr] { Expr::Path(p) => { @@ -403,8 +357,8 @@ impl InferenceContext<'_> { } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) + self.expr_ty_after_adjustments(*expr).kind(), + TyKind::Ref(..) | TyKind::RawPtr(..) ) { let mut place = self.place_of_expr(*expr)?; self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); @@ -417,7 +371,7 @@ impl InferenceContext<'_> { None } - fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) { + fn push_capture(&mut self, place: HirPlace<'db>, kind: CaptureKind) { self.current_captures.push(CapturedItemWithoutTy { place, kind, @@ -425,7 +379,11 @@ impl InferenceContext<'_> { }); } - fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) { + fn truncate_capture_spans( + &self, + capture: &mut CapturedItemWithoutTy<'db>, + mut truncate_to: usize, + ) { // The first span is the identifier, and it must always remain. truncate_to += 1; for span_stack in &mut capture.span_stacks { @@ -450,14 +408,14 @@ impl InferenceContext<'_> { } } - fn ref_expr(&mut self, expr: ExprId, place: Option) { + fn ref_expr(&mut self, expr: ExprId, place: Option>) { if let Some(place) = place { self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); } self.walk_expr(expr); } - fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { + fn add_capture(&mut self, place: HirPlace<'db>, kind: CaptureKind) { if self.is_upvar(&place) { self.push_capture(place, kind); } @@ -473,7 +431,7 @@ impl InferenceContext<'_> { } } - fn mutate_expr(&mut self, expr: ExprId, place: Option) { + fn mutate_expr(&mut self, expr: ExprId, place: Option>) { if let Some(place) = place { self.add_capture( place, @@ -490,7 +448,7 @@ impl InferenceContext<'_> { self.walk_expr(expr); } - fn consume_place(&mut self, place: HirPlace) { + fn consume_place(&mut self, place: HirPlace<'db>) { if self.is_upvar(&place) { let ty = place.ty(self); let kind = if self.is_ty_copy(ty) { @@ -502,7 +460,7 @@ impl InferenceContext<'_> { } } - fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { + fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment<'db>]) { if let Some((last, rest)) = adjustment.split_last() { match &last.kind { Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { @@ -523,7 +481,12 @@ impl InferenceContext<'_> { } } - fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { + fn ref_capture_with_adjusts( + &mut self, + m: Mutability, + tgt_expr: ExprId, + rest: &[Adjustment<'db>], + ) { let capture_kind = match m { Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), @@ -652,8 +615,8 @@ impl InferenceContext<'_> { Expr::Field { expr, name: _ } => self.select_from_expr(*expr), Expr::UnaryOp { expr, op: UnaryOp::Deref } => { if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) + self.expr_ty_after_adjustments(*expr).kind(), + TyKind::Ref(..) | TyKind::RawPtr(..) ) { self.select_from_expr(*expr); } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { @@ -728,12 +691,12 @@ impl InferenceContext<'_> { } Expr::Closure { .. } => { let ty = self.expr_ty(tgt_expr); - let TyKind::Closure(id, _) = ty.kind(Interner) else { + let TyKind::Closure(id, _) = ty.kind() else { never!("closure type is always closure"); return; }; let (captures, _) = - self.result.closure_info.get(id).expect( + self.result.closure_info.get(&id.0).expect( "We sort closures, so we should always have data for inner closures", ); let mut cc = mem::take(&mut self.current_captures); @@ -830,7 +793,7 @@ impl InferenceContext<'_> { } Pat::Bind { id, .. } => match self.result.binding_modes[p] { crate::BindingMode::Move => { - if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { + if self.is_ty_copy(self.result.type_of_binding[*id]) { update_result(CaptureKind::ByRef(BorrowKind::Shared)); } else { update_result(CaptureKind::ByValue); @@ -848,21 +811,21 @@ impl InferenceContext<'_> { self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); } - fn expr_ty(&self, expr: ExprId) -> Ty { - self.result[expr].clone() + fn expr_ty(&self, expr: ExprId) -> Ty<'db> { + self.result[expr] } - fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> { let mut ty = None; if let Some(it) = self.result.expr_adjustments.get(&e) && let Some(it) = it.last() { - ty = Some(it.target.clone()); + ty = Some(it.target); } ty.unwrap_or_else(|| self.expr_ty(e)) } - fn is_upvar(&self, place: &HirPlace) -> bool { + fn is_upvar(&self, place: &HirPlace<'db>) -> bool { if let Some(c) = self.current_closure { let InternedClosure(_, root) = self.db.lookup_intern_closure(c); return self.body.is_binding_upvar(place.local, root); @@ -870,14 +833,20 @@ impl InferenceContext<'_> { false } - fn is_ty_copy(&mut self, ty: Ty) -> bool { - if let TyKind::Closure(id, _) = ty.kind(Interner) { + fn is_ty_copy(&mut self, ty: Ty<'db>) -> bool { + if let TyKind::Closure(id, _) = ty.kind() { // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We // should probably let chalk know which closures are copy, but I don't know how doing it // without creating query cycles. - return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); + return self + .result + .closure_info + .get(&id.0) + .map(|it| it.1 == FnTrait::Fn) + .unwrap_or(true); } - self.table.resolve_completely(ty).is_copy(self.db, self.owner) + let ty = self.table.resolve_completely(ty); + self.table.type_is_copy_modulo_regions(ty) } fn select_from_expr(&mut self, expr: ExprId) { @@ -888,23 +857,25 @@ impl InferenceContext<'_> { // FIXME: Borrow checker problems without this. let mut current_captures = std::mem::take(&mut self.current_captures); for capture in &mut current_captures { - let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); - if ty.as_raw_ptr().is_some() || ty.is_union() { + let mut ty = self.table.resolve_completely(self.result[capture.place.local]); + if ty.is_raw_ptr() || ty.is_union() { capture.kind = CaptureKind::ByRef(BorrowKind::Shared); self.truncate_capture_spans(capture, 0); capture.place.projections.truncate(0); continue; } for (i, p) in capture.place.projections.iter().enumerate() { - ty = p.projected_ty( - ty, - self.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - self.owner.module(self.db).krate(), - ); - if ty.as_raw_ptr().is_some() || ty.is_union() { + ty = p + .projected_ty( + ty.to_chalk(self.interner()), + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db).krate(), + ) + .to_nextsolver(self.interner()); + if ty.is_raw_ptr() || ty.is_union() { capture.kind = CaptureKind::ByRef(BorrowKind::Shared); self.truncate_capture_spans(capture, i + 1); capture.place.projections.truncate(i + 1); @@ -932,7 +903,7 @@ impl InferenceContext<'_> { fn minimize_captures(&mut self) { self.current_captures.sort_unstable_by_key(|it| it.place.projections.len()); - let mut hash_map = FxHashMap::::default(); + let mut hash_map = FxHashMap::, usize>::default(); let result = mem::take(&mut self.current_captures); for mut item in result { let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; @@ -967,7 +938,7 @@ impl InferenceContext<'_> { } } - fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) { + fn consume_with_pat(&mut self, mut place: HirPlace<'db>, tgt_pat: PatId) { let adjustments_count = self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default(); place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref)); @@ -978,8 +949,8 @@ impl InferenceContext<'_> { Pat::Missing | Pat::Wild => (), Pat::Tuple { args, ellipsis } => { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); - let field_count = match self.result[tgt_pat].kind(Interner) { - TyKind::Tuple(_, s) => s.len(Interner), + let field_count = match self.result[tgt_pat].kind() { + TyKind::Tuple(s) => s.len(), _ => break 'reset_span_stack, }; let fields = 0..field_count; @@ -1125,9 +1096,9 @@ impl InferenceContext<'_> { r } - fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { - let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into()); - self.current_closure = Some(closure.into()); + fn analyze_closure(&mut self, closure: InternedClosureId) -> FnTrait { + let InternedClosure(_, root) = self.db.lookup_intern_closure(closure); + self.current_closure = Some(closure); let Expr::Closure { body, capture_by, .. } = &self.body[root] else { unreachable!("Closure expression id is always closure"); }; @@ -1193,9 +1164,9 @@ impl InferenceContext<'_> { self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); self.write_fn_trait_method_resolution( kind, - &derefed_callee, + derefed_callee, &mut adjustments, - &callee_ty, + callee_ty, ¶ms, expr, ); @@ -1213,27 +1184,26 @@ impl InferenceContext<'_> { /// /// These dependencies are collected in the main inference. We do a topological sort in this function. It /// will consume the `deferred_closures` field and return its content in a sorted vector. - fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { + fn sort_closures( + &mut self, + ) -> Vec<(InternedClosureId, Vec<(Ty<'db>, Ty<'db>, Vec>, ExprId)>)> { let mut deferred_closures = mem::take(&mut self.deferred_closures); - let mut dependents_count: FxHashMap = - deferred_closures.keys().map(|it| ((*it).into(), 0)).collect(); + let mut dependents_count: FxHashMap = + deferred_closures.keys().map(|it| (*it, 0)).collect(); for deps in self.closure_dependencies.values() { for dep in deps { - *dependents_count.entry((*dep).into()).or_default() += 1; + *dependents_count.entry(*dep).or_default() += 1; } } - let mut queue: Vec<_> = deferred_closures - .keys() - .copied() - .filter(|&it| dependents_count[&it.into()] == 0) - .collect(); + let mut queue: Vec<_> = + deferred_closures.keys().copied().filter(|&it| dependents_count[&it] == 0).collect(); let mut result = vec![]; while let Some(it) = queue.pop() { if let Some(d) = deferred_closures.remove(&it) { - result.push((it.into(), d)); + result.push((it, d)); } for &dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { - let cnt = dependents_count.get_mut(&dep.into()).unwrap(); + let cnt = dependents_count.get_mut(&dep).unwrap(); *cnt -= 1; if *cnt == 0 { queue.push(dep); @@ -1279,11 +1249,11 @@ impl InferenceContext<'_> { } /// Call this only when the last span in the stack isn't a split. -fn apply_adjusts_to_place( +fn apply_adjusts_to_place<'db>( current_capture_span_stack: &mut Vec, - mut r: HirPlace, - adjustments: &[Adjustment], -) -> Option { + mut r: HirPlace<'db>, + adjustments: &[Adjustment<'db>], +) -> Option> { let span = *current_capture_span_stack.last().expect("empty capture span stack"); for adj in adjustments { match &adj.kind { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 62ce00a2e33d4..10c1f9c980917 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -35,7 +35,6 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use chalk_ir::cast::Cast; use hir_def::{ CallableDefId, hir::{ExprId, ExprOrPatId}, @@ -45,22 +44,23 @@ use hir_def::{ use intern::sym; use rustc_ast_ir::Mutability; use rustc_type_ir::{ - TypeAndMut, + BoundVar, TypeAndMut, error::TypeError, - inherent::{IntoKind, Safety, Ty as _}, + inherent::{Const as _, GenericArg as _, IntoKind, Region as _, Safety, SliceLike, Ty as _}, }; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use triomphe::Arc; use crate::{ - Adjust, Adjustment, AutoBorrow, Interner, PointerCast, TargetFeatures, TraitEnvironment, + Adjust, Adjustment, AutoBorrow, PointerCast, TargetFeatures, TraitEnvironment, autoderef::Autoderef, db::{HirDatabase, InternedClosureId}, infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable}, next_solver::{ - Binder, CallableIdWrapper, ClauseKind, CoercePredicate, DbInterner, ErrorGuaranteed, - GenericArgs, PolyFnSig, PredicateKind, Region, SolverDefId, TraitRef, Ty, TyKind, + Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper, + ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, + PolyFnSig, PredicateKind, Region, RegionKind, SolverDefId, TraitRef, Ty, TyKind, infer::{ DefineOpaqueTypes, InferCtxt, InferOk, InferResult, relate::RelateResult, @@ -93,7 +93,7 @@ struct Coerce<'a, 'b, 'db> { cause: ObligationCause, } -type CoerceResult<'db> = InferResult<'db, (Vec, Ty<'db>)>; +type CoerceResult<'db> = InferResult<'db, (Vec>, Ty<'db>)>; /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. @@ -103,7 +103,7 @@ fn coerce_mutbls<'db>(from_mutbl: Mutability, to_mutbl: Mutability) -> RelateRes /// This always returns `Ok(...)`. fn success<'db>( - adj: Vec, + adj: Vec>, target: Ty<'db>, obligations: PredicateObligations<'db>, ) -> CoerceResult<'db> { @@ -118,7 +118,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { #[inline] fn interner(&self) -> DbInterner<'db> { - self.table.interner + self.table.interner() } #[inline] @@ -182,17 +182,14 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { &mut self, a: Ty<'db>, b: Ty<'db>, - adjustments: impl IntoIterator, - final_adjustment: Adjust, + adjustments: impl IntoIterator>, + final_adjustment: Adjust<'db>, ) -> CoerceResult<'db> { self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { success( adjustments .into_iter() - .chain(std::iter::once(Adjustment { - target: ty.to_chalk(self.interner()), - kind: final_adjustment, - })) + .chain(std::iter::once(Adjustment { target: ty, kind: final_adjustment })) .collect(), ty, obligations, @@ -216,10 +213,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { if self.coerce_never { return success( - vec![Adjustment { - kind: Adjust::NeverToAny, - target: b.to_chalk(self.interner()), - }], + vec![Adjustment { kind: Adjust::NeverToAny, target: b }], b, PredicateObligations::new(), ); @@ -241,10 +235,9 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { && let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind() && let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id && !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _)) - && let Some(ty) = tait_table.get(&opaque_ty_id.into()) + && let Some(ty) = tait_table.get(&opaque_ty_id) { - b = ty.to_nextsolver(self.interner()); - b = self.table.shallow_resolve(b); + b = self.table.shallow_resolve(*ty); } let b = b; @@ -474,7 +467,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { r_borrow_var.unwrap() }; let derefd_ty_a = Ty::new_ref( - autoderef.table.interner, + autoderef.table.interner(), r, referent_ty, mutbl_b, // [1] above @@ -547,11 +540,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { panic!("expected a ref type, got {:?}", ty); }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref( - region.to_chalk(self.interner()), - mutbl_b.to_chalk(self.interner()), - )), - target: ty.to_chalk(self.interner()), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl_b)), + target: ty, }); debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); @@ -655,20 +645,13 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { // implementation. If it happens that this coercion is a function argument, // the reborrow in coerce_borrowed_ptr will pick it up. // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); - let mutbl = mutbl_b.to_chalk(self.interner()); + let mutbl = mutbl_b; Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Deref(None), - target: ty_a.to_chalk(self.interner()), - }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref( - r_borrow.to_chalk(self.interner()), - mutbl, - )), - target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b) - .to_chalk(self.interner()), + kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), + target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b), }, )) } @@ -676,20 +659,16 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { coerce_mutbls(mt_a, mt_b)?; Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Deref(None), - target: ty_a.to_chalk(self.interner()), - }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b.to_chalk(self.interner()))), - target: Ty::new_ptr(self.interner(), ty_a, mt_b).to_chalk(self.interner()), + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), + target: Ty::new_ptr(self.interner(), ty_a, mt_b), }, )) } _ => None, }; - let coerce_source = - reborrow.as_ref().map_or(source, |(_, r)| r.target.to_nextsolver(self.interner())); + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); // Setup either a subtyping or a LUB relationship between // the `CoerceUnsized` target type and the expected type. @@ -834,7 +813,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { &mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>, - adjustment: Option, + adjustment: Option>, ) -> CoerceResult<'db> { debug_assert!(self.table.shallow_resolve(b) == b); @@ -849,7 +828,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { b, adjustment.map(|kind| Adjustment { kind, - target: Ty::new_fn_ptr(this.interner(), fn_ty_a).to_chalk(this.interner()), + target: Ty::new_fn_ptr(this.interner(), fn_ty_a), }), Adjust::Pointer(PointerCast::UnsafeFnPointer), ) @@ -961,9 +940,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { pointer_ty, b, [], - Adjust::Pointer(PointerCast::ClosureFnPointer( - safety.to_chalk(self.interner()), - )), + Adjust::Pointer(PointerCast::ClosureFnPointer(safety)), ) } _ => self.unify(a, b), @@ -991,11 +968,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { self.unify_and( a_raw, b, - [Adjustment { - kind: Adjust::Deref(None), - target: mt_a.ty.to_chalk(self.interner()), - }], - Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b.to_chalk(self.interner()))), + [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ) } else if mt_a.mutbl != mutbl_b { self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCast::MutToConstPointer)) @@ -1011,7 +985,7 @@ pub(crate) enum CoerceNever { Yes, } -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { /// Attempt to coerce an expression to a type, and return the /// adjusted type of the expression, if successful. /// Adjustments are only recorded if the coercion succeeded. @@ -1128,13 +1102,13 @@ impl<'db> InferenceContext<'db> { // We have a LUB of prev_ty and new_ty, just return it. Ok(ok) => return Ok(self.table.register_infer_ok(ok)), Err(_) => ( - Some(prev_ty.fn_sig(self.table.interner)), - Some(new_ty.fn_sig(self.table.interner)), + Some(prev_ty.fn_sig(self.table.interner())), + Some(new_ty.fn_sig(self.table.interner())), ), } } (TyKind::Closure(_, args), TyKind::FnDef(..)) => { - let b_sig = new_ty.fn_sig(self.table.interner); + let b_sig = new_ty.fn_sig(self.table.interner()); let a_sig = args.closure_sig_untupled().map_bound(|mut sig| { sig.safety = b_sig.safety(); sig @@ -1142,7 +1116,7 @@ impl<'db> InferenceContext<'db> { (Some(a_sig), Some(b_sig)) } (TyKind::FnDef(..), TyKind::Closure(_, args)) => { - let a_sig = prev_ty.fn_sig(self.table.interner); + let a_sig = prev_ty.fn_sig(self.table.interner()); let b_sig = args.closure_sig_untupled().map_bound(|mut sig| { sig.safety = a_sig.safety(); sig @@ -1166,36 +1140,30 @@ impl<'db> InferenceContext<'db> { .map(|ok| self.table.register_infer_ok(ok))?; // Reify both sides and return the reified fn pointer type. - let fn_ptr = Ty::new_fn_ptr(self.table.interner, sig); + let fn_ptr = Ty::new_fn_ptr(self.table.interner(), sig); let prev_adjustment = match prev_ty.kind() { - TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( - a_sig.safety().to_chalk(self.table.interner), - )), + TyKind::Closure(..) => { + Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.safety())) + } TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => panic!("should not try to coerce a {prev_ty:?} to a fn pointer"), }; let next_adjustment = match new_ty.kind() { - TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( - b_sig.safety().to_chalk(self.table.interner), - )), + TyKind::Closure(..) => { + Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.safety())) + } TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => panic!("should not try to coerce a {new_ty:?} to a fn pointer"), }; for &expr in exprs { self.write_expr_adj( expr, - Box::new([Adjustment { - kind: prev_adjustment.clone(), - target: fn_ptr.to_chalk(self.table.interner), - }]), + Box::new([Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }]), ); } self.write_expr_adj( new, - Box::new([Adjustment { - kind: next_adjustment, - target: fn_ptr.to_chalk(self.table.interner), - }]), + Box::new([Adjustment { kind: next_adjustment, target: fn_ptr }]), ); return Ok(fn_ptr); } @@ -1382,7 +1350,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// if necessary. pub(crate) fn coerce( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, cause: &ObligationCause, expression: ExprId, expression_ty: Ty<'db>, @@ -1404,19 +1372,12 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// removing a `;`). pub(crate) fn coerce_forced_unit( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, expr: ExprId, cause: &ObligationCause, label_unit_as_expected: bool, ) { - self.coerce_inner( - icx, - cause, - expr, - icx.result.standard_types.unit.to_nextsolver(icx.table.interner), - true, - label_unit_as_expected, - ) + self.coerce_inner(icx, cause, expr, icx.types.unit, true, label_unit_as_expected) } /// The inner coercion "engine". If `expression` is `None`, this @@ -1424,7 +1385,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// `Nil`. pub(crate) fn coerce_inner( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, cause: &ObligationCause, expression: ExprId, mut expression_ty: Ty<'db>, @@ -1533,20 +1494,14 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // emit or provide suggestions on how to fix the initial error. icx.set_tainted_by_errors(); - self.final_ty = Some(Ty::new_error(icx.table.interner, ErrorGuaranteed)); + self.final_ty = Some(icx.types.error); icx.result.type_mismatches.insert( expression.into(), if label_expression_as_expected { - TypeMismatch { - expected: found.to_chalk(icx.table.interner), - actual: expected.to_chalk(icx.table.interner), - } + TypeMismatch { expected: found, actual: expected } } else { - TypeMismatch { - expected: expected.to_chalk(icx.table.interner), - actual: found.to_chalk(icx.table.interner), - } + TypeMismatch { expected, actual: found } }, ); } @@ -1555,14 +1510,14 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { self.pushed += 1; } - pub(crate) fn complete(self, icx: &mut InferenceContext<'db>) -> Ty<'db> { + pub(crate) fn complete(self, icx: &mut InferenceContext<'_, 'db>) -> Ty<'db> { if let Some(final_ty) = self.final_ty { final_ty } else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. assert_eq!(self.pushed, 0); - icx.result.standard_types.never.to_nextsolver(icx.table.interner) + icx.types.never } } } @@ -1579,11 +1534,11 @@ fn coerce<'db>( db: &'db dyn HirDatabase, env: Arc>, tys: &crate::Canonical<(crate::Ty, crate::Ty)>, -) -> Result<(Vec, crate::Ty), TypeError>> { +) -> Result<(Vec>, crate::Ty), TypeError>> { let mut table = InferenceTable::new(db, env); - let vars = table.fresh_subst(tys.binders.as_slice(Interner)); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); + let interner = table.interner(); + let tys = tys.to_nextsolver(interner); + let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(&tys); let cause = ObligationCause::new(); // FIXME: Target features. @@ -1597,36 +1552,67 @@ fn coerce<'db>( use_lub: false, target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No), }; - let InferOk { value: (adjustments, ty), obligations } = coerce.coerce( - ty1_with_vars.to_nextsolver(coerce.table.interner), - ty2_with_vars.to_nextsolver(coerce.table.interner), - )?; + let InferOk { value: (adjustments, ty), obligations } = + coerce.coerce(ty1_with_vars, ty2_with_vars)?; table.register_predicates(obligations); // default any type vars that weren't unified back to their original bound vars // (kind of hacky) - let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), - } == Some(iv)) + let mut fallback_ty = |debruijn, infer| { + let var = vars.var_values.iter().position(|arg| { + arg.as_type().is_some_and(|ty| match ty.kind() { + TyKind::Infer(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Ty::new_error(interner, ErrorGuaranteed), + |i| { + Ty::new_bound( + interner, + debruijn, + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) }, + ) + }, + ) + }; + let mut fallback_const = |debruijn, infer| { + let var = vars.var_values.iter().position(|arg| { + arg.as_const().is_some_and(|ty| match ty.kind() { + ConstKind::Infer(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Const::new_error(interner, ErrorGuaranteed), + |i| Const::new_bound(interner, debruijn, BoundConst { var: BoundVar::from_usize(i) }), + ) }; - let fallback = |iv, kind, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else( - || chalk_ir::TyKind::Error.intern(Interner).cast(Interner), - |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner), - ), - chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else( - || crate::LifetimeData::Error.intern(Interner).cast(Interner), - |i| crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner), - ), - chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else( - || crate::unknown_const(ty.clone()).cast(Interner), - |i| crate::BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner), - ), + let mut fallback_region = |debruijn, infer| { + let var = vars.var_values.iter().position(|arg| { + arg.as_region().is_some_and(|ty| match ty.kind() { + RegionKind::ReVar(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Region::error(interner), + |i| { + Region::new_bound( + interner, + debruijn, + BoundRegion { kind: BoundRegionKind::Anon, var: BoundVar::from_usize(i) }, + ) + }, + ) }; // FIXME also map the types in the adjustments // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`. - Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback))) + let ty = table.resolve_with_fallback( + ty, + &mut fallback_ty, + &mut fallback_const, + &mut fallback_region, + ); + Ok((adjustments, ty.to_chalk(interner))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 003364d4336b6..39e70c262a24a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -12,11 +12,11 @@ use hir_def::expr_store::path::Path; use hir_def::{hir::ExprOrPatId, resolver::Resolver}; use la_arena::{Idx, RawIdx}; -use crate::lower::LifetimeElisionKind; use crate::{ - InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic, + InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, db::HirDatabase, - lower::path::{PathDiagnosticCallback, PathLoweringContext}, + lower_nextsolver::path::{PathDiagnosticCallback, PathLoweringContext}, + lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}, }; // Unfortunately, this struct needs to use interior mutability (but we encapsulate it) @@ -24,10 +24,10 @@ use crate::{ // to our resolver and so we cannot have mutable reference, but we really want to have // ability to dispatch diagnostics during this work otherwise the code becomes a complete mess. #[derive(Debug, Default, Clone)] -pub(super) struct Diagnostics(RefCell>); +pub(super) struct Diagnostics<'db>(RefCell>>); -impl Diagnostics { - pub(super) fn push(&self, diagnostic: InferenceDiagnostic) { +impl<'db> Diagnostics<'db> { + pub(super) fn push(&self, diagnostic: InferenceDiagnostic<'db>) { self.0.borrow_mut().push(diagnostic); } @@ -41,32 +41,32 @@ impl Diagnostics { ); } - pub(super) fn finish(self) -> Vec { + pub(super) fn finish(self) -> Vec> { self.0.into_inner() } } -pub(crate) struct PathDiagnosticCallbackData<'a> { +pub(crate) struct PathDiagnosticCallbackData<'a, 'db> { node: ExprOrPatId, - diagnostics: &'a Diagnostics, + diagnostics: &'a Diagnostics<'db>, } -pub(super) struct InferenceTyLoweringContext<'a> { - ctx: TyLoweringContext<'a>, - diagnostics: &'a Diagnostics, +pub(super) struct InferenceTyLoweringContext<'db, 'a> { + ctx: TyLoweringContext<'db, 'a>, + diagnostics: &'a Diagnostics<'db>, source: InferenceTyDiagnosticSource, } -impl<'a> InferenceTyLoweringContext<'a> { +impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { #[inline] pub(super) fn new( - db: &'a dyn HirDatabase, - resolver: &'a Resolver<'_>, + db: &'db dyn HirDatabase, + resolver: &'a Resolver<'db>, store: &'a ExpressionStore, - diagnostics: &'a Diagnostics, + diagnostics: &'a Diagnostics<'db>, source: InferenceTyDiagnosticSource, generic_def: GenericDefId, - lifetime_elision: LifetimeElisionKind, + lifetime_elision: LifetimeElisionKind<'db>, ) -> Self { Self { ctx: TyLoweringContext::new(db, resolver, store, generic_def, lifetime_elision), @@ -80,7 +80,7 @@ impl<'a> InferenceTyLoweringContext<'a> { &'b mut self, path: &'b Path, node: ExprOrPatId, - ) -> PathLoweringContext<'b, 'a> { + ) -> PathLoweringContext<'b, 'a, 'db> { let on_diagnostic = PathDiagnosticCallback { data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }), callback: |data, _, diag| { @@ -96,7 +96,7 @@ impl<'a> InferenceTyLoweringContext<'a> { pub(super) fn at_path_forget_diagnostics<'b>( &'b mut self, path: &'b Path, - ) -> PathLoweringContext<'b, 'a> { + ) -> PathLoweringContext<'b, 'a, 'db> { let on_diagnostic = PathDiagnosticCallback { data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, @@ -113,8 +113,8 @@ impl<'a> InferenceTyLoweringContext<'a> { } } -impl<'a> Deref for InferenceTyLoweringContext<'a> { - type Target = TyLoweringContext<'a>; +impl<'db, 'a> Deref for InferenceTyLoweringContext<'db, 'a> { + type Target = TyLoweringContext<'db, 'a>; #[inline] fn deref(&self) -> &Self::Target { @@ -122,14 +122,14 @@ impl<'a> Deref for InferenceTyLoweringContext<'a> { } } -impl DerefMut for InferenceTyLoweringContext<'_> { +impl DerefMut for InferenceTyLoweringContext<'_, '_> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.ctx } } -impl Drop for InferenceTyLoweringContext<'_> { +impl Drop for InferenceTyLoweringContext<'_, '_> { #[inline] fn drop(&mut self) { self.diagnostics diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index ddf632c1c81b5..b4140d88db349 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2,12 +2,11 @@ use std::{iter::repeat_with, mem}; -use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast}; use either::Either; use hir_def::hir::ClosureKind; use hir_def::{ BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, - expr_store::path::{GenericArg, GenericArgs, Path}, + expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs, Path}, hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId, Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef, @@ -17,19 +16,19 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; -use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; -use stdx::always; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + AliasTyKind, InferTy, Interner, + inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _}, +}; use syntax::ast::RangeOp; use tracing::debug; -use crate::autoderef::overloaded_deref_ty; -use crate::next_solver::infer::DefineOpaqueTypes; -use crate::next_solver::obligation_ctxt::ObligationCtxt; -use crate::next_solver::{DbInterner, ErrorGuaranteed}; use crate::{ - Adjust, Adjustment, AdtId, AutoBorrow, CallableDefId, CallableSig, DeclContext, DeclOrigin, - IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval, + Adjust, Adjustment, AutoBorrow, CallableDefId, DeclContext, DeclOrigin, + IncorrectGenericsLenKind, Rawness, TraitEnvironment, + autoderef::overloaded_deref_ty, + consteval_nextsolver, generics::generics, infer::{ AllowTwoPhase, BreakableKind, @@ -38,18 +37,20 @@ use crate::{ pat::contains_explicit_ref_binding, }, lang_items::lang_items_for_bin_op, - lower::{ - ParamLoweringMode, lower_to_chalk_mutability, + lower_nextsolver::{ + LifetimeElisionKind, lower_mutability, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, }, - mapping::{ToChalk, from_chalk}, method_resolution::{self, VisibleFromModule}, next_solver::{ - infer::traits::ObligationCause, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind, + TypeError, + infer::{ + DefineOpaqueTypes, InferOk, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, - primitive::{self, UintTy}, - static_lifetime, to_chalk_trait_id, traits::FnTrait, }; @@ -64,27 +65,30 @@ pub(crate) enum ExprIsRead { No, } -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { pub(crate) fn infer_expr( &mut self, tgt_expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(&ty, &expected_ty); + let could_unify = self.unify(ty, expected_ty); if !could_unify { - self.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: expected_ty, actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(tgt_expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } ty } - pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId, is_read: ExprIsRead) -> Ty { + pub(crate) fn infer_expr_no_expect( + &mut self, + tgt_expr: ExprId, + is_read: ExprIsRead, + ) -> Ty<'db> { self.infer_expr_inner(tgt_expr, &Expectation::None, is_read) } @@ -93,9 +97,9 @@ impl<'db> InferenceContext<'db> { pub(super) fn infer_expr_coerce( &mut self, expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) { @@ -103,19 +107,12 @@ impl<'db> InferenceContext<'db> { } else { CoerceNever::No }; - match self.coerce( - expr.into(), - ty.to_nextsolver(self.table.interner), - target.to_nextsolver(self.table.interner), - AllowTwoPhase::No, - coerce_never, - ) { - Ok(res) => res.to_chalk(self.table.interner), + match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, coerce_never) { + Ok(res) => res, Err(_) => { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: target.clone(), actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(expr.into(), TypeMismatch { expected: target, actual: ty }); target } } @@ -255,42 +252,34 @@ impl<'db> InferenceContext<'db> { fn infer_expr_coerce_never( &mut self, expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from `!` to `expected`. if ty.is_never() { if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { - target.clone() + *target } else { self.err_ty() }; } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce( - expr.into(), - ty.to_nextsolver(self.table.interner), - target.to_nextsolver(self.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) - .expect("never-to-any coercion should always succeed") - .to_chalk(self.table.interner) + self.coerce(expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes) + .expect("never-to-any coercion should always succeed") } else { ty } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(&ty, &expected_ty); + let could_unify = self.unify(ty, expected_ty); if !could_unify { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: expected_ty, actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } ty @@ -301,9 +290,9 @@ impl<'db> InferenceContext<'db> { fn infer_expr_inner( &mut self, tgt_expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { self.db.unwind_if_revision_cancelled(); let expr = &self.body[tgt_expr]; @@ -314,7 +303,7 @@ impl<'db> InferenceContext<'db> { let expected = &expected.adjust_for_branches(&mut self.table); self.infer_expr_coerce_never( condition, - &Expectation::HasType(self.result.standard_types.bool_.clone()), + &Expectation::HasType(self.types.bool), ExprIsRead::Yes, ); @@ -327,27 +316,15 @@ impl<'db> InferenceContext<'db> { coercion_sites[1] = else_branch; } let mut coerce = CoerceMany::with_coercion_sites( - expected - .coercion_target_type(&mut self.table) - .to_nextsolver(self.table.interner), + expected.coercion_target_type(&mut self.table), &coercion_sites, ); - coerce.coerce( - self, - &ObligationCause::new(), - then_branch, - then_ty.to_nextsolver(self.table.interner), - ); + coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - coerce.coerce( - self, - &ObligationCause::new(), - else_branch, - else_ty.to_nextsolver(self.table.interner), - ); + coerce.coerce(self, &ObligationCause::new(), else_branch, else_ty); self.diverges = condition_diverges | then_diverges & else_diverges; } None => { @@ -356,7 +333,7 @@ impl<'db> InferenceContext<'db> { } } - coerce.complete(self).to_chalk(self.table.interner) + coerce.complete(self) } &Expr::Let { pat, expr } => { let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) { @@ -367,10 +344,10 @@ impl<'db> InferenceContext<'db> { let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); self.infer_top_pat( pat, - &input_ty, + input_ty, Some(DeclContext { origin: DeclOrigin::LetExpr }), ); - self.result.standard_types.bool_.clone() + self.types.bool } Expr::Block { statements, tail, label, id } => { self.infer_block(tgt_expr, *id, statements, *tail, *label, expected) @@ -390,12 +367,12 @@ impl<'db> InferenceContext<'db> { &Expr::Loop { body, label } => { // FIXME: should be: // let ty = expected.coercion_target_type(&mut self.table); - let ty = self.table.new_type_var(); + let ty = self.table.next_ty_var(); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( body, - &Expectation::HasType(TyBuilder::unit()), + &Expectation::HasType(this.types.unit), ExprIsRead::Yes, ); }); @@ -405,7 +382,7 @@ impl<'db> InferenceContext<'db> { self.diverges = Diverges::Maybe; breaks } - None => self.result.standard_types.never.clone(), + None => self.types.never, } } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self @@ -438,32 +415,30 @@ impl<'db> InferenceContext<'db> { if arms.is_empty() { self.diverges = Diverges::Always; - self.result.standard_types.never.clone() + self.types.never } else { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - let input_ty = self.table.structurally_resolve_type(&input_ty); - self.infer_top_pat(arm.pat, &input_ty, None); + let input_ty = self.table.structurally_resolve_type(input_ty); + self.infer_top_pat(arm.pat, input_ty, None); } let expected = expected.adjust_for_branches(&mut self.table); let result_ty = match &expected { // We don't coerce to `()` so that if the match expression is a // statement it's branches can have any consistent type. - Expectation::HasType(ty) if *ty != self.result.standard_types.unit => { - ty.clone() - } - _ => self.table.new_type_var(), + Expectation::HasType(ty) if *ty != self.types.unit => *ty, + _ => self.table.next_ty_var(), }; - let mut coerce = CoerceMany::new(result_ty.to_nextsolver(self.table.interner)); + let mut coerce = CoerceMany::new(result_ty); for arm in arms.iter() { if let Some(guard_expr) = arm.guard { self.diverges = Diverges::Maybe; self.infer_expr_coerce_never( guard_expr, - &Expectation::HasType(self.result.standard_types.bool_.clone()), + &Expectation::HasType(self.types.bool), ExprIsRead::Yes, ); } @@ -471,17 +446,12 @@ impl<'db> InferenceContext<'db> { let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes); all_arms_diverge &= self.diverges; - coerce.coerce( - self, - &ObligationCause::new(), - arm.expr, - arm_ty.to_nextsolver(self.table.interner), - ); + coerce.coerce(self, &ObligationCause::new(), arm.expr, arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; - coerce.complete(self).to_chalk(self.table.interner) + coerce.complete(self) } } Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr), @@ -493,13 +463,13 @@ impl<'db> InferenceContext<'db> { bad_value_break: false, }); }; - self.result.standard_types.never.clone() + self.types.never } &Expr::Break { expr, label } => { let val_ty = if let Some(expr) = expr { let opt_coerce_to = match find_breakable(&mut self.breakables, label) { Some(ctxt) => match &ctxt.coerce { - Some(coerce) => coerce.expected_ty().to_chalk(self.table.interner), + Some(coerce) => coerce.expected_ty(), None => { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, @@ -517,7 +487,7 @@ impl<'db> InferenceContext<'db> { ExprIsRead::Yes, ) } else { - TyBuilder::unit() + self.types.unit }; match find_breakable(&mut self.breakables, label) { @@ -527,7 +497,7 @@ impl<'db> InferenceContext<'db> { self, &ObligationCause::new(), expr.unwrap_or(tgt_expr), - val_ty.to_nextsolver(self.table.interner), + val_ty, ); // Avoiding borrowck @@ -546,12 +516,12 @@ impl<'db> InferenceContext<'db> { }); } } - self.result.standard_types.never.clone() + self.types.never } &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), &Expr::Become { expr } => self.infer_expr_become(expr), Expr::Yield { expr } => { - if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { + if let Some((resume_ty, yield_ty)) = self.resume_yield_tys { if let Some(expr) = expr { self.infer_expr_coerce( *expr, @@ -559,11 +529,11 @@ impl<'db> InferenceContext<'db> { ExprIsRead::Yes, ); } else { - let unit = self.result.standard_types.unit.clone(); + let unit = self.types.unit; let _ = self.coerce( tgt_expr.into(), - unit.to_nextsolver(self.table.interner), - yield_ty.to_nextsolver(self.table.interner), + unit, + yield_ty, AllowTwoPhase::No, CoerceNever::Yes, ); @@ -571,33 +541,30 @@ impl<'db> InferenceContext<'db> { resume_ty } else { // FIXME: report error (yield expr in non-coroutine) - self.result.standard_types.unknown.clone() + self.types.error } } Expr::Yeet { expr } => { if let &Some(expr) = expr { self.infer_expr_no_expect(expr, ExprIsRead::Yes); } - self.result.standard_types.never.clone() + self.types.never } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); if let Some(t) = expected.only_has_type(&mut self.table) { - self.unify(&ty, &t); + self.unify(ty, t); } - let substs = ty - .as_adt() - .map(|(_, s)| s.clone()) - .unwrap_or_else(|| Substitution::empty(Interner)); + let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty_args); if let Some(variant) = def_id { self.write_variant_resolution(tgt_expr.into(), variant); } match def_id { _ if fields.is_empty() => {} Some(def) => { - let field_types = self.db.field_types(def); + let field_types = self.db.field_types_ns(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); for field in fields.iter() { @@ -628,7 +595,7 @@ impl<'db> InferenceContext<'db> { } }; let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it].clone().substitute(Interner, &substs) + field_types[it].instantiate(self.interner(), &substs) }); // Field type might have some unknown types @@ -649,7 +616,7 @@ impl<'db> InferenceContext<'db> { } } if let Some(expr) = spread { - self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); + self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes); } ty } @@ -660,21 +627,13 @@ impl<'db> InferenceContext<'db> { } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); - let expr_ty = self.infer_expr( - *expr, - &Expectation::Castable(cast_ty.clone()), - ExprIsRead::Yes, - ); - self.deferred_cast_checks.push(CastCheck::new( - tgt_expr, - *expr, - expr_ty, - cast_ty.clone(), - )); + let expr_ty = + self.infer_expr(*expr, &Expectation::Castable(cast_ty), ExprIsRead::Yes); + self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty)); cast_ty } Expr::Ref { expr, rawness, mutability } => { - let mutability = lower_to_chalk_mutability(*mutability); + let mutability = lower_mutability(*mutability); let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected .only_has_type(&mut self.table) .as_ref() @@ -688,24 +647,23 @@ impl<'db> InferenceContext<'db> { // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } - Expectation::rvalue_hint(self, Ty::clone(exp_inner)) + Expectation::rvalue_hint(self, exp_inner) } else { Expectation::none() }; let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes); match rawness { - Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), + Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability), Rawness::Ref => { - let lt = self.table.new_lifetime_var(); - TyKind::Ref(mutability, lt, inner_ty) + let lt = self.table.next_region_var(); + Ty::new_ref(self.interner(), lt, inner_ty, mutability) } } - .intern(Interner) } &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - let inner_ty = self.table.structurally_resolve_type(&inner_ty); + let inner_ty = self.table.try_structurally_resolve_type(inner_ty); // FIXME: Note down method resolution her match op { UnaryOp::Deref => { @@ -716,49 +674,38 @@ impl<'db> InferenceContext<'db> { { // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that // the mutability is not wrong, and will be fixed in `self.infer_mut`). - self.write_method_resolution( - tgt_expr, - deref_fn, - Substitution::empty(Interner), - ); + self.write_method_resolution(tgt_expr, deref_fn, self.types.empty_args); } - if let Some(derefed) = - inner_ty.to_nextsolver(self.table.interner).builtin_deref(self.db, true) - { - self.table - .structurally_resolve_type(&derefed.to_chalk(self.table.interner)) + if let Some(derefed) = inner_ty.builtin_deref(self.db, true) { + self.table.try_structurally_resolve_type(derefed) } else { - let infer_ok = overloaded_deref_ty( - &self.table, - inner_ty.to_nextsolver(self.table.interner), - ); + let infer_ok = overloaded_deref_ty(&self.table, inner_ty); match infer_ok { - Some(infer_ok) => self - .table - .register_infer_ok(infer_ok) - .to_chalk(self.table.interner), + Some(infer_ok) => self.table.register_infer_ok(infer_ok), None => self.err_ty(), } } } UnaryOp::Neg => { - match inner_ty.kind(Interner) { + match inner_ty.kind() { // Fast path for builtins - TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float, - ) => inner_ty, + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty, // Otherwise we resolve via the std::ops::Neg trait _ => self .resolve_associated_type(inner_ty, self.resolve_ops_neg_output()), } } UnaryOp::Not => { - match inner_ty.kind(Interner) { + match inner_ty.kind() { // Fast path for builtins - TyKind::Scalar(Scalar::Bool | Scalar::Int(_) | Scalar::Uint(_)) - | TyKind::InferenceVar(_, TyVariableKind::Integer) => inner_ty, + TyKind::Bool + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty, // Otherwise we resolve via the std::ops::Not trait _ => self .resolve_associated_type(inner_ty, self.resolve_ops_not_output()), @@ -768,18 +715,10 @@ impl<'db> InferenceContext<'db> { } Expr::BinaryOp { lhs, rhs, op } => match op { Some(BinaryOp::LogicOp(_)) => { - let bool_ty = self.result.standard_types.bool_.clone(); - self.infer_expr_coerce( - *lhs, - &Expectation::HasType(bool_ty.clone()), - ExprIsRead::Yes, - ); + let bool_ty = self.types.bool; + self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes); let lhs_diverges = self.diverges; - self.infer_expr_coerce( - *rhs, - &Expectation::HasType(bool_ty.clone()), - ExprIsRead::Yes, - ); + self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes); // Depending on the LHS' value, the RHS can never execute. self.diverges = lhs_diverges; bool_ty @@ -826,14 +765,14 @@ impl<'db> InferenceContext<'db> { let is_destructuring_assignment = lhs_ty.is_none(); if let Some(lhs_ty) = lhs_ty { - self.write_pat_ty(target, lhs_ty.clone()); + self.write_pat_ty(target, lhs_ty); self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No); } else { let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes); let resolver_guard = self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, &rhs_ty, None); + self.infer_top_pat(target, rhs_ty, None); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -844,43 +783,49 @@ impl<'db> InferenceContext<'db> { // assignments into blocks. self.table.new_maybe_never_var() } else { - self.result.standard_types.unit.clone() + self.types.unit } } Expr::Range { lhs, rhs, range_type } => { let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes)); - let rhs_expect = lhs_ty - .as_ref() - .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); + let rhs_expect = + lhs_ty.as_ref().map_or_else(Expectation::none, |ty| Expectation::has_type(*ty)); let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes)); + let single_arg_adt = |adt, ty: Ty<'db>| { + Ty::new_adt( + self.interner(), + adt, + GenericArgs::new_from_iter(self.interner(), [ty.into()]), + ) + }; match (range_type, lhs_ty, rhs_ty) { (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { - Some(adt) => TyBuilder::adt(self.db, adt).build(), + Some(adt) => Ty::new_adt(self.interner(), adt, self.types.empty_args), None => self.err_ty(), }, (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, None, Some(ty)) => { match self.resolve_range_to_inclusive() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), } } (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, Some(_), Some(ty)) => { match self.resolve_range_inclusive() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), } } (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, _, None) => self.err_ty(), @@ -891,8 +836,7 @@ impl<'db> InferenceContext<'db> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = - self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized = self.canonicalize(base_ty); let receiver_adjustments = method_resolution::resolve_indexing_op( &mut self.table, canonicalized, @@ -905,142 +849,133 @@ impl<'db> InferenceContext<'db> { // mutability will be fixed up in `InferenceContext::infer_mut`; adj.push(Adjustment::borrow( + self.interner(), Mutability::Not, - self_ty.clone(), - self.table.new_lifetime_var(), + self_ty, + self.table.next_region_var(), )); self.write_expr_adj(*base, adj.into_boxed_slice()); if let Some(func) = index_trait .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::index)) { - let subst = TyBuilder::subst_for_def(self.db, index_trait, None); - if subst.remaining() != 2 { - return self.err_ty(); - } - let subst = subst.push(self_ty.clone()).push(index_ty.clone()).build(); + let subst = GenericArgs::new_from_iter( + self.interner(), + [self_ty.into(), index_ty.into()], + ); self.write_method_resolution(tgt_expr, func, subst); } let assoc = self.resolve_ops_index_output(); - self.resolve_associated_type_with_params( - self_ty, - assoc, - &[index_ty.cast(Interner)], - ) + self.resolve_associated_type_with_params(self_ty, assoc, &[index_ty.into()]) } else { self.err_ty() } } Expr::Tuple { exprs, .. } => { - let mut tys = match expected - .only_has_type(&mut self.table) - .as_ref() - .map(|t| t.kind(Interner)) - { - Some(TyKind::Tuple(_, substs)) => substs - .iter(Interner) - .map(|a| a.assert_ty_ref(Interner).clone()) - .chain(repeat_with(|| self.table.new_type_var())) - .take(exprs.len()) - .collect::>(), - _ => (0..exprs.len()).map(|_| self.table.new_type_var()).collect(), - }; + let mut tys = + match expected.only_has_type(&mut self.table).as_ref().map(|t| t.kind()) { + Some(TyKind::Tuple(substs)) => substs + .iter() + .chain(repeat_with(|| self.table.next_ty_var())) + .take(exprs.len()) + .collect::>(), + _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(), + }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - *ty = self.infer_expr_coerce( - *expr, - &Expectation::has_type(ty.clone()), - ExprIsRead::Yes, - ); + *ty = + self.infer_expr_coerce(*expr, &Expectation::has_type(*ty), ExprIsRead::Yes); } - TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) + Ty::new_tup(self.interner(), &tys) } Expr::Array(array) => self.infer_expr_array(array, expected), Expr::Literal(lit) => match lit { - Literal::Bool(..) => self.result.standard_types.bool_.clone(), - Literal::String(..) => { - TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner)) - .intern(Interner) - } + Literal::Bool(..) => self.types.bool, + Literal::String(..) => self.types.static_str_ref, Literal::ByteString(bs) => { - let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); + let byte_type = self.types.u8; - let len = consteval::usize_const( + let len = consteval_nextsolver::usize_const( self.db, Some(bs.len() as u128), self.resolver.krate(), ); - let array_type = TyKind::Array(byte_type, len).intern(Interner); - TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner) + let array_type = Ty::new_array_with_const_len(self.interner(), byte_type, len); + Ty::new_ref(self.interner(), self.types.re_static, array_type, Mutability::Not) } - Literal::CString(..) => TyKind::Ref( - Mutability::Not, - static_lifetime(), + Literal::CString(..) => Ty::new_ref( + self.interner(), + self.types.re_static, self.resolve_lang_item(LangItem::CStr) .and_then(LangItemTarget::as_struct) .map_or_else( || self.err_ty(), |strukt| { - TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner)) - .intern(Interner) + Ty::new_adt(self.interner(), strukt.into(), self.types.empty_args) }, ), - ) - .intern(Interner), - Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner), + Mutability::Not, + ), + Literal::Char(..) => self.types.char, Literal::Int(_v, ty) => match ty { - Some(int_ty) => { - TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty))) - .intern(Interner) - } + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinInt::Isize => self.types.isize, + hir_def::builtin_type::BuiltinInt::I8 => self.types.i8, + hir_def::builtin_type::BuiltinInt::I16 => self.types.i16, + hir_def::builtin_type::BuiltinInt::I32 => self.types.i32, + hir_def::builtin_type::BuiltinInt::I64 => self.types.i64, + hir_def::builtin_type::BuiltinInt::I128 => self.types.i128, + }, None => { let expected_ty = expected.to_option(&mut self.table); tracing::debug!(?expected_ty); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { - Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, - Some(TyKind::Scalar(Scalar::Char)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) - } - Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.usize) } _ => None, }; - opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + opt_ty.unwrap_or_else(|| self.table.next_int_var()) } }, Literal::Uint(_v, ty) => match ty { - Some(int_ty) => { - TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty))) - .intern(Interner) - } + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinUint::Usize => self.types.usize, + hir_def::builtin_type::BuiltinUint::U8 => self.types.u8, + hir_def::builtin_type::BuiltinUint::U16 => self.types.u16, + hir_def::builtin_type::BuiltinUint::U32 => self.types.u32, + hir_def::builtin_type::BuiltinUint::U64 => self.types.u64, + hir_def::builtin_type::BuiltinUint::U128 => self.types.u128, + }, None => { let expected_ty = expected.to_option(&mut self.table); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { - Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, - Some(TyKind::Scalar(Scalar::Char)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) - } - Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.usize) } _ => None, }; - opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + opt_ty.unwrap_or_else(|| self.table.next_int_var()) } }, Literal::Float(_v, ty) => match ty { - Some(float_ty) => { - TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty))) - .intern(Interner) - } + Some(float_ty) => match float_ty { + hir_def::builtin_type::BuiltinFloat::F16 => self.types.f16, + hir_def::builtin_type::BuiltinFloat::F32 => self.types.f32, + hir_def::builtin_type::BuiltinFloat::F64 => self.types.f64, + hir_def::builtin_type::BuiltinFloat::F128 => self.types.f128, + }, None => { - let opt_ty = expected.to_option(&mut self.table).filter(|ty| { - matches!(ty.kind(Interner), TyKind::Scalar(Scalar::Float(_))) - }); - opt_ty.unwrap_or_else(|| self.table.new_float_var()) + let opt_ty = expected + .to_option(&mut self.table) + .filter(|ty| matches!(ty.kind(), TyKind::Float(_))); + opt_ty.unwrap_or_else(|| self.table.next_float_var()) } }, }, @@ -1048,13 +983,10 @@ impl<'db> InferenceContext<'db> { // Underscore expression is an error, we render a specialized diagnostic // to let the user know what type is expected though. let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty()); - self.push_diagnostic(InferenceDiagnostic::TypedHole { - expr: tgt_expr, - expected: expected.clone(), - }); + self.push_diagnostic(InferenceDiagnostic::TypedHole { expr: tgt_expr, expected }); expected } - Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), + Expr::OffsetOf(_) => self.types.usize, Expr::InlineAsm(asm) => { let check_expr_asm_operand = |this: &mut Self, expr, is_input: bool| { let ty = this.infer_expr_no_expect(expr, ExprIsRead::Yes); @@ -1067,27 +999,29 @@ impl<'db> InferenceContext<'db> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.table.structurally_resolve_type(&ty); - match ty.kind(Interner) { + let ty = this.table.structurally_resolve_type(ty); + match ty.kind() { TyKind::FnDef(def, parameters) => { - let fnptr_ty = TyKind::Function( - CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(), - ) - .intern(Interner); + let fnptr_ty = Ty::new_fn_ptr( + this.interner(), + this.interner() + .fn_sig(def) + .instantiate(this.interner(), parameters), + ); _ = this.coerce( expr.into(), - ty.to_nextsolver(this.table.interner), - fnptr_ty.to_nextsolver(this.table.interner), + ty, + fnptr_ty, AllowTwoPhase::No, CoerceNever::Yes, ); } - TyKind::Ref(mutbl, _, base_ty) => { - let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); + TyKind::Ref(_, base_ty, mutbl) => { + let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl); _ = this.coerce( expr.into(), - ty.to_nextsolver(this.table.interner), - ptr_ty.to_nextsolver(this.table.interner), + ty, + ptr_ty, AllowTwoPhase::No, CoerceNever::Yes, ); @@ -1113,7 +1047,7 @@ impl<'db> InferenceContext<'db> { AsmOperand::Label(expr) => { self.infer_expr( expr, - &Expectation::HasType(self.result.standard_types.unit.clone()), + &Expectation::HasType(self.types.unit), ExprIsRead::No, ); } @@ -1123,17 +1057,13 @@ impl<'db> InferenceContext<'db> { // FIXME: `sym` should report for things that are not functions or statics. AsmOperand::Sym(_) => (), }); - if diverge { - self.result.standard_types.never.clone() - } else { - self.result.standard_types.unit.clone() - } + if diverge { self.types.never } else { self.types.unit } } }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); - self.write_expr_ty(tgt_expr, ty.clone()); - if self.resolve_ty_shallow(&ty).is_never() + self.write_expr_ty(tgt_expr, ty); + if self.shallow_resolve(ty).is_never() && self.expr_guaranteed_to_constitute_read_for_never(tgt_expr, is_read) { // Any expression that produces a value of type `!` must have diverged @@ -1142,7 +1072,7 @@ impl<'db> InferenceContext<'db> { ty } - fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty { + fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> { let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id); let ty = match self.infer_path(path, id) { Some(ty) => ty, @@ -1164,32 +1094,24 @@ impl<'db> InferenceContext<'db> { id: &Option, statements: &[Statement], tail: &Option, - ) -> Ty { - let ret_ty = self.table.new_type_var(); + ) -> Ty<'db> { + let ret_ty = self.table.next_ty_var(); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = self - .return_coercion - .replace(CoerceMany::new(ret_ty.to_nextsolver(self.table.interner))); + let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty); + let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty)); // FIXME: We should handle async blocks like we handle closures let expected = &Expectation::has_type(ret_ty); let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce( - tgt_expr.into(), - ty.to_nextsolver(this.table.interner), - target.to_nextsolver(this.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) { - Ok(res) => res.to_chalk(this.table.interner), + match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes) + { + Ok(res) => res, Err(_) => { - this.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: target.clone(), actual: ty.clone() }, - ); + this.result + .type_mismatches + .insert(tgt_expr.into(), TypeMismatch { expected: target, actual: ty }); target } } @@ -1207,57 +1129,67 @@ impl<'db> InferenceContext<'db> { pub(crate) fn lower_async_block_type_impl_trait( &mut self, - inner_ty: Ty, + inner_ty: Ty<'db>, tgt_expr: ExprId, - ) -> Ty { + ) -> Ty<'db> { // Use the first type parameter as the output type of future. // existential type AsyncBlockImplTrait: Future let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) + Ty::new_alias( + self.interner(), + AliasTyKind::Opaque, + AliasTy::new( + self.interner(), + opaque_ty_id, + GenericArgs::new_from_iter(self.interner(), [inner_ty.into()]), + ), + ) } pub(crate) fn write_fn_trait_method_resolution( &mut self, fn_x: FnTrait, - derefed_callee: &Ty, - adjustments: &mut Vec, - callee_ty: &Ty, - params: &[Ty], + derefed_callee: Ty<'db>, + adjustments: &mut Vec>, + callee_ty: Ty<'db>, + params: &[Ty<'db>], tgt_expr: ExprId, ) { match fn_x { FnTrait::FnOnce | FnTrait::AsyncFnOnce => (), FnTrait::FnMut | FnTrait::AsyncFnMut => { - if let TyKind::Ref(Mutability::Mut, lt, inner) = derefed_callee.kind(Interner) { + if let TyKind::Ref(lt, inner, Mutability::Mut) = derefed_callee.kind() { if adjustments .last() .map(|it| matches!(it.kind, Adjust::Borrow(_))) .unwrap_or(true) { // prefer reborrow to move - adjustments - .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() }); + adjustments.push(Adjustment { kind: Adjust::Deref(None), target: inner }); adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Mut, - inner.clone(), - lt.clone(), + inner, + lt, )) } } else { adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Mut, - derefed_callee.clone(), - self.table.new_lifetime_var(), + derefed_callee, + self.table.next_region_var(), )); } } FnTrait::Fn | FnTrait::AsyncFn => { - if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) { + if !matches!(derefed_callee.kind(), TyKind::Ref(_, _, Mutability::Not)) { adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Not, - derefed_callee.clone(), - self.table.new_lifetime_var(), + derefed_callee, + self.table.next_region_var(), )); } } @@ -1267,57 +1199,48 @@ impl<'db> InferenceContext<'db> { }; let trait_data = trait_.trait_items(self.db); if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(callee_ty.clone()) - .push(TyBuilder::tuple_with(params.iter().cloned())) - .build(); + let subst = GenericArgs::new_from_iter( + self.interner(), + [ + callee_ty.into(), + Ty::new_tup_from_iter(self.interner(), params.iter().copied()).into(), + ], + ); self.write_method_resolution(tgt_expr, func, subst); } } - fn infer_expr_array( - &mut self, - array: &Array, - expected: &Expectation, - ) -> chalk_ir::Ty { - let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) { - Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(), - _ => self.table.new_type_var(), + fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> { + let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind()) { + Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, + _ => self.table.next_ty_var(), }; let krate = self.resolver.krate(); - let expected = Expectation::has_type(elem_ty.clone()); + let expected = Expectation::has_type(elem_ty); let (elem_ty, len) = match array { Array::ElementList { elements, .. } if elements.is_empty() => { - (elem_ty, consteval::usize_const(self.db, Some(0), krate)) + (elem_ty, consteval_nextsolver::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::with_coercion_sites( - elem_ty.to_nextsolver(self.table.interner), - elements, - ); + let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce( - self, - &ObligationCause::new(), - expr, - cur_elem_ty.to_nextsolver(self.table.interner), - ); + coerce.coerce(self, &ObligationCause::new(), expr, cur_elem_ty); } ( - coerce.complete(self).to_chalk(self.table.interner), - consteval::usize_const(self.db, Some(elements.len() as u128), krate), + coerce.complete(self), + consteval_nextsolver::usize_const(self.db, Some(elements.len() as u128), krate), ) } &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce( initializer, - &Expectation::has_type(elem_ty.clone()), + &Expectation::has_type(elem_ty), ExprIsRead::Yes, ); - let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner); + let usize = self.types.usize; match self.body[repeat] { Expr::Underscore => { self.write_expr_ty(repeat, usize); @@ -1325,20 +1248,12 @@ impl<'db> InferenceContext<'db> { _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes), } - ( - elem_ty, - consteval::eval_to_const( - repeat, - ParamLoweringMode::Placeholder, - self, - DebruijnIndex::INNERMOST, - ), - ) + (elem_ty, consteval_nextsolver::eval_to_const(repeat, self)) } }; // Try to evaluate unevaluated constant, and insert variable if is not possible. let len = self.table.insert_const_vars_shallow(len); - TyKind::Array(elem_ty, len).intern(Interner) + Ty::new_array_with_const_len(self.interner(), elem_ty, len) } pub(super) fn infer_return(&mut self, expr: ExprId) { @@ -1346,21 +1261,15 @@ impl<'db> InferenceContext<'db> { .return_coercion .as_mut() .expect("infer_return called outside function body") - .expected_ty() - .to_chalk(self.table.interner); + .expected_ty(); let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce( - self, - &ObligationCause::new(), - expr, - return_expr_ty.to_nextsolver(self.table.interner), - ); + coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty); self.return_coercion = Some(coerce_many); } - fn infer_expr_return(&mut self, ret: ExprId, expr: Option) -> Ty { + fn infer_expr_return(&mut self, ret: ExprId, expr: Option) -> Ty<'db> { match self.return_coercion { Some(_) => { if let Some(expr) = expr { @@ -1378,23 +1287,20 @@ impl<'db> InferenceContext<'db> { } } } - self.result.standard_types.never.clone() + self.types.never } - fn infer_expr_become(&mut self, expr: ExprId) -> Ty { + fn infer_expr_become(&mut self, expr: ExprId) -> Ty<'db> { match &self.return_coercion { Some(return_coercion) => { - let ret_ty = return_coercion.expected_ty().to_chalk(self.table.interner); + let ret_ty = return_coercion.expected_ty(); - let call_expr_ty = self.infer_expr_inner( - expr, - &Expectation::HasType(ret_ty.clone()), - ExprIsRead::Yes, - ); + let call_expr_ty = + self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). - self.unify(&call_expr_ty, &ret_ty); + self.unify(call_expr_ty, ret_ty); } None => { // FIXME: diagnose `become` outside of functions @@ -1402,10 +1308,10 @@ impl<'db> InferenceContext<'db> { } } - self.result.standard_types.never.clone() + self.types.never } - fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation) -> Ty { + fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation<'db>) -> Ty<'db> { if let Some(box_id) = self.resolve_boxed_box() { let table = &mut self.table; let inner_exp = expected @@ -1414,16 +1320,22 @@ impl<'db> InferenceContext<'db> { .and_then(|e| e.as_adt()) .filter(|(e_adt, _)| e_adt == &box_id) .map(|(_, subts)| { - let g = subts.at(Interner, 0); - Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner))) + let g = subts.type_at(0); + Expectation::rvalue_hint(self, g) }) .unwrap_or_else(Expectation::none); let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); - TyBuilder::adt(self.db, box_id) - .push(inner_ty) - .fill_with_defaults(self.db, || self.table.new_type_var()) - .build() + Ty::new_adt( + self.interner(), + box_id, + GenericArgs::fill_with_defaults( + self.interner(), + box_id.into(), + [inner_ty.into()], + |_, _, id, _| self.table.next_var_for_param(id), + ), + ) } else { self.err_ty() } @@ -1435,7 +1347,7 @@ impl<'db> InferenceContext<'db> { op: BinaryOp, rhs: ExprId, tgt_expr: ExprId, - ) -> Ty { + ) -> Ty<'db> { let lhs_expectation = Expectation::none(); let is_read = if matches!(op, BinaryOp::Assignment { .. }) { ExprIsRead::Yes @@ -1443,24 +1355,24 @@ impl<'db> InferenceContext<'db> { ExprIsRead::No }; let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read); - let rhs_ty = self.table.new_type_var(); + let rhs_ty = self.table.next_ty_var(); let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| { let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?; let func = trait_id.trait_items(self.db).method_by_name(&name)?; Some((trait_id, func)) }); - let (trait_, func) = match trait_func { - Some(it) => it, + let func = match trait_func { + Some((_, it)) => it, None => { // HACK: `rhs_ty` is a general inference variable with no clue at all at this // point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin // type applicable to `op`. - let ret_ty = if self.is_builtin_binop(&lhs_ty, &lhs_ty, op) { + let ret_ty = if self.is_builtin_binop(lhs_ty, lhs_ty, op) { // Assume both operands are builtin so we can continue inference. No guarantee // on the correctness, rustc would complain as necessary lang items don't seem // to exist anyway. - self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op) + self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op) } else { self.err_ty() }; @@ -1473,59 +1385,53 @@ impl<'db> InferenceContext<'db> { // HACK: We can use this substitution for the function because the function itself doesn't // have its own generic parameters. - let subst = TyBuilder::subst_for_def(self.db, trait_, None); - if subst.remaining() != 2 { - return Ty::new(Interner, TyKind::Error); - } - let subst = subst.push(lhs_ty.clone()).push(rhs_ty.clone()).build(); + let args = GenericArgs::new_from_iter(self.interner(), [lhs_ty.into(), rhs_ty.into()]); - self.write_method_resolution(tgt_expr, func, subst.clone()); + self.write_method_resolution(tgt_expr, func, args); - let interner = DbInterner::new_with(self.db, None, None); - let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); - let method_ty = - self.db.value_ty(func.into()).unwrap().instantiate(interner, args).to_chalk(interner); - self.register_obligations_for_call(&method_ty); + let method_ty = self.db.value_ty(func.into()).unwrap().instantiate(self.interner(), args); + self.register_obligations_for_call(method_ty); - self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes); + self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes); - let ret_ty = match method_ty.callable_sig(self.db) { + let ret_ty = match method_ty.callable_sig(self.interner()) { Some(sig) => { - let p_left = &sig.params()[0]; + let sig = sig.skip_binder(); + let p_left = sig.inputs_and_output.as_slice()[0]; if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) - && let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) + && let TyKind::Ref(lt, _, mtbl) = p_left.kind() { self.write_expr_adj( lhs, Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_left.clone(), + kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)), + target: p_left, }]), ); } - let p_right = &sig.params()[1]; + let p_right = sig.inputs_and_output.as_slice()[1]; if matches!(op, BinaryOp::CmpOp(..)) - && let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) + && let TyKind::Ref(lt, _, mtbl) = p_right.kind() { self.write_expr_adj( rhs, Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_right.clone(), + kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)), + target: p_right, }]), ); } - sig.ret().clone() + sig.output() } None => self.err_ty(), }; let ret_ty = self.process_remote_user_written_ty(ret_ty); - if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) { + if self.is_builtin_binop(lhs_ty, rhs_ty, op) { // use knowledge of built-in binary ops, which can sometimes help inference - let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op); - self.unify(&builtin_ret, &ret_ty); + let builtin_ret = self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op); + self.unify(builtin_ret, ret_ty); builtin_ret } else { ret_ty @@ -1539,8 +1445,8 @@ impl<'db> InferenceContext<'db> { statements: &[Statement], tail: Option, label: Option, - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { let coerce_ty = expected.coercion_target_type(&mut self.table); let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr); let prev_env = block_id.map(|block_id| { @@ -1557,7 +1463,7 @@ impl<'db> InferenceContext<'db> { let decl_ty = type_ref .as_ref() .map(|&tr| this.make_body_ty(tr)) - .unwrap_or_else(|| this.table.new_type_var()); + .unwrap_or_else(|| this.table.next_ty_var()); let ty = if let Some(expr) = initializer { // If we have a subpattern that performs a read, we want to consider this @@ -1571,13 +1477,13 @@ impl<'db> InferenceContext<'db> { let ty = if contains_explicit_ref_binding(this.body, *pat) { this.infer_expr( *expr, - &Expectation::has_type(decl_ty.clone()), + &Expectation::has_type(decl_ty), target_is_read, ) } else { this.infer_expr_coerce( *expr, - &Expectation::has_type(decl_ty.clone()), + &Expectation::has_type(decl_ty), target_is_read, ) }; @@ -1590,13 +1496,13 @@ impl<'db> InferenceContext<'db> { origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, }; - this.infer_top_pat(*pat, &ty, Some(decl)); + this.infer_top_pat(*pat, ty, Some(decl)); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); this.infer_expr_coerce( *expr, - &Expectation::HasType(this.result.standard_types.never.clone()), + &Expectation::HasType(this.types.never), ExprIsRead::Yes, ); this.diverges = previous_diverges; @@ -1608,7 +1514,7 @@ impl<'db> InferenceContext<'db> { } else { this.infer_expr_coerce( expr, - &Expectation::HasType(this.result.standard_types.unit.clone()), + &Expectation::HasType(this.types.unit), ExprIsRead::Yes, ); } @@ -1642,8 +1548,8 @@ impl<'db> InferenceContext<'db> { if this .coerce( expr.into(), - this.result.standard_types.unit.to_nextsolver(this.table.interner), - t.to_nextsolver(this.table.interner), + this.types.unit, + t, AllowTwoPhase::No, coerce_never, ) @@ -1651,15 +1557,12 @@ impl<'db> InferenceContext<'db> { { this.result.type_mismatches.insert( expr.into(), - TypeMismatch { - expected: t.clone(), - actual: this.result.standard_types.unit.clone(), - }, + TypeMismatch { expected: t, actual: this.types.unit }, ); } t } else { - this.result.standard_types.unit.clone() + this.types.unit } } }); @@ -1673,32 +1576,30 @@ impl<'db> InferenceContext<'db> { fn lookup_field( &mut self, - receiver_ty: &Ty, + receiver_ty: Ty<'db>, name: &Name, - ) -> Option<(Ty, Either, Vec, bool)> { - let interner = self.table.interner; - let mut autoderef = self.table.autoderef(receiver_ty.to_nextsolver(self.table.interner)); + ) -> Option<(Ty<'db>, Either, Vec>, bool)> { + let interner = self.interner(); + let mut autoderef = self.table.autoderef(receiver_ty); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind() { - crate::next_solver::TyKind::Tuple(substs) => { + TyKind::Tuple(substs) => { return name.as_tuple_index().and_then(|idx| { substs.as_slice().get(idx).copied().map(|ty| { ( Either::Right(TupleFieldId { tuple: TupleId( - self.tuple_field_accesses_rev - .insert_full(substs.to_chalk(interner)) - .0 as u32, + self.tuple_field_accesses_rev.insert_full(substs).0 as u32, ), index: idx as u32, }), - ty.to_chalk(interner), + ty, ) }) }); } - crate::next_solver::TyKind::Adt(adt, parameters) => match adt.def_id().0 { + TyKind::Adt(adt, parameters) => match adt.def_id().0 { hir_def::AdtId::StructId(s) => { let local_id = s.fields(self.db).field(name)?; let field = FieldId { parent: s.into(), local_id }; @@ -1713,18 +1614,16 @@ impl<'db> InferenceContext<'db> { }, _ => return None, }; - let parameters: crate::Substitution = parameters.to_chalk(interner); let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] .is_visible_from(self.db, self.resolver.module()); if !is_visible { if private_field.is_none() { - private_field = Some((field_id, parameters.clone())); + private_field = Some((field_id, parameters)); } return None; } - let ty = self.db.field_types(field_id.parent)[field_id.local_id] - .clone() - .substitute(Interner, ¶meters); + let ty = self.db.field_types_ns(field_id.parent)[field_id.local_id] + .instantiate(interner, parameters); Some((Either::Left(field_id), ty)) }); @@ -1738,9 +1637,8 @@ impl<'db> InferenceContext<'db> { None => { let (field_id, subst) = private_field?; let adjustments = autoderef.adjust_steps(); - let ty = self.db.field_types(field_id.parent)[field_id.local_id] - .clone() - .substitute(Interner, &subst); + let ty = self.db.field_types_ns(field_id.parent)[field_id.local_id] + .instantiate(self.interner(), subst); let ty = self.process_remote_user_written_ty(ty); (ty, Either::Left(field_id), adjustments, false) @@ -1753,8 +1651,8 @@ impl<'db> InferenceContext<'db> { tgt_expr: ExprId, receiver: ExprId, name: &Name, - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { // Field projections don't constitute reads. let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); @@ -1764,7 +1662,7 @@ impl<'db> InferenceContext<'db> { return self.err_ty(); } - match self.lookup_field(&receiver_ty, name) { + match self.lookup_field(receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); self.result.field_resolutions.insert(tgt_expr, field_id); @@ -1780,8 +1678,7 @@ impl<'db> InferenceContext<'db> { None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = - self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = self.canonicalize(receiver_ty); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1792,28 +1689,24 @@ impl<'db> InferenceContext<'db> { ); self.push_diagnostic(InferenceDiagnostic::UnresolvedField { expr: tgt_expr, - receiver: receiver_ty.clone(), + receiver: receiver_ty, name: name.clone(), method_with_same_name_exists: resolved.is_some(), }); match resolved { Some((adjust, func, _)) => { let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); - let substs = self.substs_for_method_call(tgt_expr, func.into(), None); + let args = self.substs_for_method_call(tgt_expr, func.into(), None); self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - self.write_method_resolution(tgt_expr, func, substs.clone()); + self.write_method_resolution(tgt_expr, func, args); - let interner = DbInterner::new_with(self.db, None, None); - let args: crate::next_solver::GenericArgs<'_> = - substs.to_nextsolver(interner); self.check_method_call( tgt_expr, &[], self.db .value_ty(func.into()) .unwrap() - .instantiate(interner, args) - .to_chalk(interner), + .instantiate(self.interner(), args), ty, expected, ) @@ -1829,47 +1722,42 @@ impl<'db> InferenceContext<'db> { tgt_expr: ExprId, callee: ExprId, args: &[ExprId], - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes); - let interner = self.table.interner; - let mut derefs = self.table.autoderef(callee_ty.to_nextsolver(interner)); + let interner = self.interner(); + let mut derefs = self.table.autoderef(callee_ty); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { - break (None, callee_ty.clone()); + break (None, callee_ty); }; - let callee_deref_ty = callee_deref_ty.to_chalk(interner); - if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { + if let Some(res) = derefs.table.callable_sig(callee_deref_ty, args.len()) { break (Some(res), callee_deref_ty); } }; // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here - let is_varargs = - derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); + let is_varargs = derefed_callee.callable_sig(interner).is_some_and(|sig| sig.c_variadic()) + || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let params_chalk = - params.iter().map(|param| param.to_chalk(interner)).collect::>(); let mut adjustments = derefs.adjust_steps(); if let Some(fn_x) = func { self.write_fn_trait_method_resolution( fn_x, - &derefed_callee, + derefed_callee, &mut adjustments, - &callee_ty, - ¶ms_chalk, + callee_ty, + ¶ms, tgt_expr, ); } - if let &TyKind::Closure(c, _) = - self.table.resolve_completely(callee_ty.clone()).kind(Interner) - { + if let TyKind::Closure(c, _) = self.table.resolve_completely(callee_ty).kind() { self.add_current_closure_dependency(c.into()); self.deferred_closures.entry(c.into()).or_default().push(( - derefed_callee.clone(), - callee_ty.clone(), - params_chalk, + derefed_callee, + callee_ty, + params.clone(), tgt_expr, )); } @@ -1879,9 +1767,9 @@ impl<'db> InferenceContext<'db> { None => { self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { call_expr: tgt_expr, - found: callee_ty.clone(), + found: callee_ty, }); - (Vec::new(), crate::next_solver::Ty::new_error(interner, ErrorGuaranteed)) + (Vec::new(), Ty::new_error(interner, ErrorGuaranteed)) } }; let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args); @@ -1901,14 +1789,14 @@ impl<'db> InferenceContext<'db> { &mut self, tgt_expr: ExprId, args: &[ExprId], - callee_ty: Ty, - param_tys: &[crate::next_solver::Ty<'db>], - ret_ty: crate::next_solver::Ty<'db>, + callee_ty: Ty<'db>, + param_tys: &[Ty<'db>], + ret_ty: Ty<'db>, indices_to_skip: &[u32], is_varargs: bool, - expected: &Expectation, - ) -> Ty { - self.register_obligations_for_call(&callee_ty); + expected: &Expectation<'db>, + ) -> Ty<'db> { + self.register_obligations_for_call(callee_ty); self.check_call_arguments( tgt_expr, @@ -1919,7 +1807,7 @@ impl<'db> InferenceContext<'db> { indices_to_skip, is_varargs, ); - self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(self.table.interner) + self.table.normalize_associated_types_in(ret_ty) } fn infer_method_call( @@ -1928,16 +1816,13 @@ impl<'db> InferenceContext<'db> { receiver: ExprId, args: &[ExprId], method_name: &Name, - generic_args: Option<&GenericArgs>, - expected: &Expectation, - ) -> Ty { + generic_args: Option<&HirGenericArgs>, + expected: &Expectation<'db>, + ) -> Ty<'db> { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let receiver_ty = self.table.structurally_resolve_type(&receiver_ty); + let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty); - if matches!( - receiver_ty.kind(Interner), - TyKind::Error | TyKind::InferenceVar(_, TyVariableKind::General) - ) { + if matches!(receiver_ty.kind(), TyKind::Error(_) | TyKind::Infer(InferTy::TyVar(_))) { // Don't probe on error type, or on a fully unresolved infer var. // FIXME: Emit an error if we're probing on an infer var (type annotations needed). for &arg in args { @@ -1947,8 +1832,7 @@ impl<'db> InferenceContext<'db> { return receiver_ty; } - let canonicalized_receiver = - self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = self.canonicalize(receiver_ty); let resolved = method_resolution::lookup_method( self.db, @@ -1970,18 +1854,16 @@ impl<'db> InferenceContext<'db> { let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args); - self.write_method_resolution(tgt_expr, func, substs.clone()); + let gen_args = self.substs_for_method_call(tgt_expr, func.into(), generic_args); + self.write_method_resolution(tgt_expr, func, gen_args); let interner = DbInterner::new_with(self.db, None, None); - let gen_args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); self.check_method_call( tgt_expr, args, self.db .value_ty(func.into()) .expect("we have a function def") - .instantiate(interner, gen_args) - .to_chalk(interner), + .instantiate(interner, gen_args), ty, expected, ) @@ -1989,7 +1871,7 @@ impl<'db> InferenceContext<'db> { // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function None => { - let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name) + let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name) { Some((ty, field_id, adjustments, _public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); @@ -2017,48 +1899,46 @@ impl<'db> InferenceContext<'db> { self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall { expr: tgt_expr, - receiver: receiver_ty.clone(), + receiver: receiver_ty, name: method_name.clone(), - field_with_same_name: field_with_same_name_exists.clone(), + field_with_same_name: field_with_same_name_exists, assoc_func_with_same_name, }); let recovered = match assoc_func_with_same_name { Some(f) => { - let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args); + let args = self.substs_for_method_call(tgt_expr, f.into(), generic_args); let interner = DbInterner::new_with(self.db, None, None); - let args: crate::next_solver::GenericArgs<'_> = - substs.to_nextsolver(interner); let f = self .db .value_ty(f.into()) .expect("we have a function def") - .instantiate(interner, args) - .to_chalk(interner); - let sig = f.callable_sig(self.db).expect("we have a function def"); + .instantiate(interner, args); + let sig = f.callable_sig(self.interner()).expect("we have a function def"); Some((f, sig, true)) } None => field_with_same_name_exists.and_then(|field_ty| { - let callable_sig = field_ty.callable_sig(self.db)?; + let callable_sig = field_ty.callable_sig(self.interner())?; Some((field_ty, callable_sig, false)) }), }; match recovered { - Some((callee_ty, sig, strip_first)) => self.check_call( - tgt_expr, - args, - callee_ty, - &sig.params() - .get(strip_first as usize..) - .unwrap_or(&[]) - .iter() - .map(|param| param.to_nextsolver(self.table.interner)) - .collect::>(), - sig.ret().to_nextsolver(self.table.interner), - &[], - true, - expected, - ), + Some((callee_ty, sig, strip_first)) => { + let sig = sig.skip_binder(); + self.check_call( + tgt_expr, + args, + callee_ty, + sig.inputs_and_output + .inputs() + .get(strip_first as usize..) + .unwrap_or(&[]), + sig.output(), + &[], + true, + expected, + ) + } None => { for &arg in args.iter() { self.infer_expr_no_expect(arg, ExprIsRead::Yes); @@ -2074,39 +1954,38 @@ impl<'db> InferenceContext<'db> { &mut self, tgt_expr: ExprId, args: &[ExprId], - method_ty: Ty, - receiver_ty: Ty, - expected: &Expectation, - ) -> Ty { - self.register_obligations_for_call(&method_ty); - let interner = self.table.interner; + method_ty: Ty<'db>, + receiver_ty: Ty<'db>, + expected: &Expectation<'db>, + ) -> Ty<'db> { + self.register_obligations_for_call(method_ty); let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = - match method_ty.callable_sig(self.db) { - Some(sig) => ( - if !sig.params().is_empty() { - ( - sig.params()[0].to_nextsolver(interner), - sig.params()[1..] - .iter() - .map(|param| param.to_nextsolver(interner)) - .collect(), - ) - } else { - (crate::next_solver::Ty::new_error(interner, ErrorGuaranteed), Vec::new()) - }, - sig.ret().to_nextsolver(interner), - sig.is_varargs, - ), + match method_ty.callable_sig(self.interner()) { + Some(sig) => { + let sig = sig.skip_binder(); + ( + if !sig.inputs_and_output.inputs().is_empty() { + ( + sig.inputs_and_output.as_slice()[0], + sig.inputs_and_output.inputs()[1..].to_vec(), + ) + } else { + (self.types.error, Vec::new()) + }, + sig.output(), + sig.c_variadic, + ) + } None => { let formal_receiver_ty = self.table.next_ty_var(); let ret_ty = self.table.next_ty_var(); ((formal_receiver_ty, Vec::new()), ret_ty, true) } }; - self.table.unify_ns(formal_receiver_ty, receiver_ty.to_nextsolver(interner)); + self.table.unify(formal_receiver_ty, receiver_ty); self.check_call_arguments(tgt_expr, ¶m_tys, ret_ty, expected, args, &[], is_varargs); - self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(interner) + self.table.normalize_associated_types_in(ret_ty) } /// Generic function that factors out common logic from function calls, @@ -2115,18 +1994,16 @@ impl<'db> InferenceContext<'db> { &mut self, call_expr: ExprId, // Types (as defined in the *signature* of the target function) - formal_input_tys: &[crate::next_solver::Ty<'db>], - formal_output: crate::next_solver::Ty<'db>, + formal_input_tys: &[Ty<'db>], + formal_output: Ty<'db>, // Expected output from the parent expression or statement - expectation: &Expectation, + expectation: &Expectation<'db>, // The expressions for each provided argument provided_args: &[ExprId], skip_indices: &[u32], // Whether the function is variadic, for example when imported from C c_variadic: bool, ) { - let interner = self.table.interner; - // First, let's unify the formal method signature with the expectation eagerly. // We use this to guide coercion inference; it's output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This @@ -2145,14 +2022,9 @@ impl<'db> InferenceContext<'db> { // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = ObligationCause::new(); - ocx.sup( - &origin, - self.table.trait_env.env, - expected_output.to_nextsolver(interner), - formal_output, - )?; + ocx.sup(&origin, self.table.trait_env.env, expected_output, formal_output)?; if !ocx.select_where_possible().is_empty() { - return Err(crate::next_solver::TypeError::Mismatch); + return Err(TypeError::Mismatch); } // Record all the argument types, with the args @@ -2200,9 +2072,9 @@ impl<'db> InferenceContext<'db> { // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types - let demand_compatible = |this: &mut InferenceContext<'db>, idx| { - let formal_input_ty: crate::next_solver::Ty<'db> = formal_input_tys[idx]; - let expected_input_ty: crate::next_solver::Ty<'db> = expected_input_tys[idx]; + let demand_compatible = |this: &mut InferenceContext<'_, 'db>, idx| { + let formal_input_ty: Ty<'db> = formal_input_tys[idx]; + let expected_input_ty: Ty<'db> = expected_input_tys[idx]; let provided_arg = provided_args[idx]; debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); @@ -2210,19 +2082,14 @@ impl<'db> InferenceContext<'db> { // We're on the happy path here, so we'll do a more involved check and write back types // To check compatibility, we'll do 3 things: // 1. Unify the provided argument with the expected type - let expectation = Expectation::rvalue_hint(this, expected_input_ty.to_chalk(interner)); + let expectation = Expectation::rvalue_hint(this, expected_input_ty); - let checked_ty = this - .infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes) - .to_nextsolver(interner); + let checked_ty = this.infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes); // 2. Coerce to the most detailed type that could be coerced // to, which is `expected_ty` if `rvalue_hint` returns an // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerced_ty = expectation - .only_has_type(&mut this.table) - .map(|it| it.to_nextsolver(interner)) - .unwrap_or(formal_input_ty); + let coerced_ty = expectation.only_has_type(&mut this.table).unwrap_or(formal_input_ty); // Cause selection errors caused by resolving a single argument to point at the // argument and not the call. This lets us customize the span pointed to in the @@ -2259,7 +2126,7 @@ impl<'db> InferenceContext<'db> { // If neither check failed, the types are compatible match formal_ty_error { - Ok(crate::next_solver::infer::InferOk { obligations, value: () }) => { + Ok(InferOk { obligations, value: () }) => { this.table.register_predicates(obligations); Ok(()) } @@ -2313,13 +2180,9 @@ impl<'db> InferenceContext<'db> { && args_count_matches { // Don't report type mismatches if there is a mismatch in args count. - self.result.type_mismatches.insert( - (*arg).into(), - TypeMismatch { - expected: expected.to_chalk(interner), - actual: found.to_chalk(interner), - }, - ); + self.result + .type_mismatches + .insert((*arg).into(), TypeMismatch { expected, actual: found }); } } } @@ -2331,14 +2194,14 @@ impl<'db> InferenceContext<'db> { &mut self, expr: ExprId, def: GenericDefId, - generic_args: Option<&GenericArgs>, - ) -> Substitution { - struct LowererCtx<'a, 'b> { - ctx: &'a mut InferenceContext<'b>, + generic_args: Option<&HirGenericArgs>, + ) -> GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'db> { + ctx: &'a mut InferenceContext<'b, 'db>, expr: ExprId, } - impl GenericArgsLowerer for LowererCtx<'_, '_> { + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, 'db> { fn report_len_mismatch( &mut self, def: GenericDefId, @@ -2373,21 +2236,22 @@ impl<'db> InferenceContext<'db> { &mut self, param_id: GenericParamId, param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::GenericArg { + arg: &HirGenericArg, + ) -> GenericArg<'db> { match (param, arg) { - (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { - self.ctx.make_body_lifetime(*lifetime).cast(Interner) + ( + GenericParamDataRef::LifetimeParamData(_), + HirGenericArg::Lifetime(lifetime), + ) => self.ctx.make_body_lifetime(*lifetime).into(), + (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => { + self.ctx.make_body_ty(*type_ref).into() } - (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { - self.ctx.make_body_ty(*type_ref).cast(Interner) - } - (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; - let const_ty = self.ctx.db.const_param_ty(const_id); - self.ctx.make_body_const(*konst, const_ty).cast(Interner) + let const_ty = self.ctx.db.const_param_ty_ns(const_id); + self.ctx.make_body_const(*konst, const_ty).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -2395,12 +2259,12 @@ impl<'db> InferenceContext<'db> { fn provided_type_like_const( &mut self, - const_ty: Ty, + const_ty: Ty<'db>, arg: TypeLikeConst<'_>, - ) -> crate::Const { + ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty), - TypeLikeConst::Infer => self.ctx.table.new_const_var(const_ty), + TypeLikeConst::Infer => self.ctx.table.next_const_var(), } } @@ -2410,35 +2274,15 @@ impl<'db> InferenceContext<'db> { param_id: GenericParamId, _param: GenericParamDataRef<'_>, _infer_args: bool, - _preceding_args: &[crate::GenericArg], - ) -> crate::GenericArg { + _preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db> { // Always create an inference var, even when `infer_args == false`. This helps with diagnostics, // and I think it's also required in the presence of `impl Trait` (that must be inferred). - match param_id { - GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner), - GenericParamId::ConstParamId(const_id) => self - .ctx - .table - .new_const_var(self.ctx.db.const_param_ty(const_id)) - .cast(Interner), - GenericParamId::LifetimeParamId(_) => { - self.ctx.table.new_lifetime_var().cast(Interner) - } - } + self.ctx.table.next_var_for_param(param_id) } - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg { - match param_id { - GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner), - GenericParamId::ConstParamId(const_id) => self - .ctx - .table - .new_const_var(self.ctx.db.const_param_ty(const_id)) - .cast(Interner), - GenericParamId::LifetimeParamId(_) => { - self.ctx.table.new_lifetime_var().cast(Interner) - } - } + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> { + self.ctx.table.next_var_for_param(param_id) } fn report_elided_lifetimes_in_path( @@ -2472,36 +2316,34 @@ impl<'db> InferenceContext<'db> { ) } - fn register_obligations_for_call(&mut self, callable_ty: &Ty) { - let callable_ty = self.table.structurally_resolve_type(callable_ty); - if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) { - let def: CallableDefId = from_chalk(self.db, *fn_def); + fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) { + let callable_ty = self.table.try_structurally_resolve_type(callable_ty); + if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() { let generic_predicates = - self.db.generic_predicates(GenericDefId::from_callable(self.db, def)); - for predicate in generic_predicates.iter() { - let (predicate, binders) = predicate - .clone() - .substitute(Interner, parameters) - .into_value_and_skipped_binders(); - always!(binders.len(Interner) == 0); // quantified where clauses not yet handled - self.push_obligation(predicate.cast(Interner)); + self.db.generic_predicates_ns(GenericDefId::from_callable(self.db, fn_def.0)); + if let Some(predicates) = generic_predicates.instantiate(self.interner(), parameters) { + let interner = self.interner(); + let param_env = self.table.trait_env.env; + self.table.register_predicates(predicates.map(|predicate| { + Obligation::new(interner, ObligationCause::new(), param_env, predicate) + })); } // add obligation for trait implementation, if this is a trait method - match def { + match fn_def.0 { CallableDefId::FunctionId(f) => { if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container { // construct a TraitRef let trait_params_len = generics(self.db, trait_.into()).len(); - let substs = Substitution::from_iter( - Interner, - // The generic parameters for the trait come after those for the - // function. - ¶meters.as_slice(Interner)[..trait_params_len], - ); - self.push_obligation( - TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } - .cast(Interner), + let substs = GenericArgs::new_from_iter( + self.interner(), + parameters.as_slice()[..trait_params_len].iter().copied(), ); + self.table.register_predicate(Obligation::new( + self.interner(), + ObligationCause::new(), + self.table.trait_env.env, + TraitRef::new(self.interner(), trait_.into(), substs), + )); } } CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} @@ -2510,11 +2352,10 @@ impl<'db> InferenceContext<'db> { } /// Returns the argument indices to skip. - fn check_legacy_const_generics(&mut self, callee: Ty, args: &[ExprId]) -> Box<[u32]> { - let (func, subst) = match callee.kind(Interner) { - TyKind::FnDef(fn_id, subst) => { - let callable = CallableDefId::from_chalk(self.db, *fn_id); - let func = match callable { + fn check_legacy_const_generics(&mut self, callee: Ty<'db>, args: &[ExprId]) -> Box<[u32]> { + let (func, _subst) = match callee.kind() { + TyKind::FnDef(callable, subst) => { + let func = match callable.0 { CallableDefId::FunctionId(f) => f, _ => return Default::default(), }; @@ -2542,15 +2383,10 @@ impl<'db> InferenceContext<'db> { } // check legacy const parameters - for (subst_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() { - let arg = match subst.at(Interner, subst_idx).constant(Interner) { - Some(c) => c, - None => continue, // not a const parameter? - }; + for arg_idx in legacy_const_generics_indices.iter().copied() { if arg_idx >= args.len() as u32 { continue; } - let _ty = arg.data(Interner).ty.clone(); let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const @@ -2561,17 +2397,19 @@ impl<'db> InferenceContext<'db> { } /// Dereferences a single level of immutable referencing. - fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty { - let ty = self.table.structurally_resolve_type(ty); - match ty.kind(Interner) { - TyKind::Ref(Mutability::Not, _, inner) => self.table.structurally_resolve_type(inner), + fn deref_ty_if_possible(&mut self, ty: Ty<'db>) -> Ty<'db> { + let ty = self.table.try_structurally_resolve_type(ty); + match ty.kind() { + TyKind::Ref(_, inner, Mutability::Not) => { + self.table.try_structurally_resolve_type(inner) + } _ => ty, } } /// Enforces expectations on lhs type and rhs type depending on the operator and returns the /// output type of the binary op. - fn enforce_builtin_binop_types(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> Ty { + fn enforce_builtin_binop_types(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> Ty<'db> { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447). let lhs = self.deref_ty_if_possible(lhs); let rhs = self.deref_ty_if_possible(rhs); @@ -2583,9 +2421,9 @@ impl<'db> InferenceContext<'db> { let output_ty = match op { BinaryOp::LogicOp(_) => { - let bool_ = self.result.standard_types.bool_.clone(); - self.unify(&lhs, &bool_); - self.unify(&rhs, &bool_); + let bool_ = self.types.bool; + self.unify(lhs, bool_); + self.unify(rhs, bool_); bool_ } @@ -2596,14 +2434,14 @@ impl<'db> InferenceContext<'db> { BinaryOp::ArithOp(_) => { // LHS, RHS, and result will have the same type - self.unify(&lhs, &rhs); + self.unify(lhs, rhs); lhs } BinaryOp::CmpOp(_) => { // LHS and RHS will have the same type - self.unify(&lhs, &rhs); - self.result.standard_types.bool_.clone() + self.unify(lhs, rhs); + self.types.bool } BinaryOp::Assignment { op: None } => { @@ -2614,10 +2452,10 @@ impl<'db> InferenceContext<'db> { BinaryOp::Assignment { .. } => unreachable!("handled above"), }; - if is_assign { self.result.standard_types.unit.clone() } else { output_ty } + if is_assign { self.types.unit } else { output_ty } } - fn is_builtin_binop(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> bool { + fn is_builtin_binop(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> bool { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447). let lhs = self.deref_ty_if_possible(lhs); let rhs = self.deref_ty_if_possible(rhs); @@ -2644,27 +2482,25 @@ impl<'db> InferenceContext<'db> { BinaryOp::ArithOp(ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) => { lhs.is_integral() && rhs.is_integral() || lhs.is_floating_point() && rhs.is_floating_point() - || matches!( - (lhs.kind(Interner), rhs.kind(Interner)), - (TyKind::Scalar(Scalar::Bool), TyKind::Scalar(Scalar::Bool)) - ) + || matches!((lhs.kind(), rhs.kind()), (TyKind::Bool, TyKind::Bool)) } BinaryOp::CmpOp(_) => { let is_scalar = |kind| { matches!( kind, - &TyKind::Scalar(_) + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) | TyKind::FnDef(..) - | TyKind::Function(_) - | TyKind::Raw(..) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float - ) + | TyKind::FnPtr(..) + | TyKind::RawPtr(..) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) ) }; - is_scalar(lhs.kind(Interner)) && is_scalar(rhs.kind(Interner)) + is_scalar(lhs.kind()) && is_scalar(rhs.kind()) } BinaryOp::Assignment { op: None } => { @@ -2679,27 +2515,15 @@ impl<'db> InferenceContext<'db> { pub(super) fn with_breakable_ctx( &mut self, kind: BreakableKind, - ty: Option, + ty: Option>, label: Option, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option, T) { + ) -> (Option>, T) { self.breakables.push({ - BreakableContext { - kind, - may_break: false, - coerce: ty.map(|ty| CoerceMany::new(ty.to_nextsolver(self.table.interner))), - label, - } + BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - ( - if ctx.may_break { - ctx.coerce.map(|ctx| ctx.complete(self).to_chalk(self.table.interner)) - } else { - None - }, - res, - ) + (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs index 2022447ad4339..b1c9146cc8b85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs @@ -28,7 +28,7 @@ pub(crate) enum DivergingFallbackBehavior { ToNever, } -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { pub(super) fn type_inference_fallback(&mut self) { debug!( "type-inference-fallback start obligations: {:#?}", @@ -324,7 +324,7 @@ impl<'db> InferenceContext<'db> { FxHashMap::with_capacity_and_hasher(diverging_vids.len(), FxBuildHasher); for &diverging_vid in &diverging_vids { - let diverging_ty = Ty::new_var(self.table.interner, diverging_vid); + let diverging_ty = Ty::new_var(self.interner(), diverging_vid); let root_vid = self.table.infer_ctxt.root_var(diverging_vid); let can_reach_non_diverging = Dfs::new(&coercion_graph, root_vid.as_u32().into()) .iter(&coercion_graph) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index c798e9e050a18..9edbc9dda0f10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -1,7 +1,6 @@ //! Finds if an expression is an immutable context or a mutable context, which is used in selecting //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar. -use chalk_ir::{Mutability, cast::Cast}; use hir_def::{ hir::{ Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, @@ -11,14 +10,19 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; +use rustc_ast_ir::Mutability; +use rustc_type_ir::inherent::IntoKind; +use crate::next_solver::infer::traits::{Obligation, ObligationCause}; +use crate::next_solver::{GenericArgs, TraitRef}; use crate::{ - Adjust, Adjustment, AutoBorrow, Interner, OverloadedDeref, TyBuilder, TyKind, + Adjust, Adjustment, AutoBorrow, OverloadedDeref, infer::{Expectation, InferenceContext, expr::ExprIsRead}, - lower::lower_to_chalk_mutability, + lower_nextsolver::lower_mutability, + next_solver::TyKind, }; -impl InferenceContext<'_> { +impl<'db> InferenceContext<'_, 'db> { pub(crate) fn infer_mut_body(&mut self) { self.infer_mut_expr(self.body.body_expr, Mutability::Not); } @@ -141,8 +145,8 @@ impl InferenceContext<'_> { target, }) = base_adjustments { - if let TyKind::Ref(_, _, ty) = target.kind(Interner) { - base_ty = Some(ty.clone()); + if let TyKind::Ref(_, ty, _) = target.kind() { + base_ty = Some(ty); } *mutability = Mutability::Mut; } @@ -150,15 +154,24 @@ impl InferenceContext<'_> { // Apply `IndexMut` obligation for non-assignee expr if let Some(base_ty) = base_ty { let index_ty = if let Some(ty) = self.result.type_of_expr.get(index) { - ty.clone() + *ty } else { self.infer_expr(index, &Expectation::none(), ExprIsRead::Yes) }; - let trait_ref = TyBuilder::trait_ref(self.db, index_trait) - .push(base_ty) - .fill(|_| index_ty.clone().cast(Interner)) - .build(); - self.push_obligation(trait_ref.cast(Interner)); + let trait_ref = TraitRef::new( + self.interner(), + index_trait.into(), + GenericArgs::new_from_iter( + self.interner(), + [base_ty.into(), index_ty.into()], + ), + ); + self.table.register_predicate(Obligation::new( + self.interner(), + ObligationCause::new(), + self.table.trait_env.env, + trait_ref, + )); } } self.infer_mut_expr(base, mutability); @@ -173,8 +186,8 @@ impl InferenceContext<'_> { { let ty = self.result.type_of_expr.get(*expr); let is_mut_ptr = ty.is_some_and(|ty| { - let ty = self.table.resolve_ty_shallow(ty); - matches!(ty.kind(Interner), chalk_ir::TyKind::Raw(Mutability::Mut, _)) + let ty = self.table.shallow_resolve(*ty); + matches!(ty.kind(), TyKind::RawPtr(_, Mutability::Mut)) }); if is_mut_ptr { mutability = Mutability::Not; @@ -200,7 +213,7 @@ impl InferenceContext<'_> { self.infer_mut_expr(*expr, Mutability::Not); } Expr::Ref { expr, rawness: _, mutability } => { - let mutability = lower_to_chalk_mutability(*mutability); + let mutability = lower_mutability(*mutability); self.infer_mut_expr(*expr, mutability); } Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 6e11fa942bdfb..9f2f86dd3ef50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -8,37 +8,35 @@ use hir_def::{ hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, }; use hir_expand::name::Name; +use rustc_ast_ir::Mutability; +use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Ty as _}; use stdx::TupleExt; -use crate::infer::AllowTwoPhase; -use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ - DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, - TyBuilder, TyExt, TyKind, - consteval::{self, try_const_usize, usize_const}, + DeclContext, DeclOrigin, InferenceDiagnostic, + consteval_nextsolver::{self, try_const_usize, usize_const}, infer::{ - BindingMode, Expectation, InferenceContext, TypeMismatch, coerce::CoerceNever, - expr::ExprIsRead, + AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, + coerce::CoerceNever, expr::ExprIsRead, }, - lower::lower_to_chalk_mutability, - primitive::UintTy, - static_lifetime, + lower_nextsolver::lower_mutability, + next_solver::{GenericArgs, Ty, TyKind}, }; -impl InferenceContext<'_> { +impl<'db> InferenceContext<'_, 'db> { /// Infers type for tuple struct pattern or its corresponding assignee expression. /// /// Ellipses found in the original pattern or expression must be filtered out. pub(super) fn infer_tuple_struct_pat_like( &mut self, path: Option<&Path>, - expected: &Ty, + expected: Ty<'db>, default_bm: BindingMode, id: PatId, ellipsis: Option, subs: &[PatId], decl: Option, - ) -> Ty { + ) -> Ty<'db> { let (ty, def) = self.resolve_variant(id.into(), path, true); let var_data = def.map(|it| it.fields(self.db)); if let Some(variant) = def { @@ -56,12 +54,12 @@ impl InferenceContext<'_> { } } - self.unify(&ty, expected); + self.unify(ty, expected); match def { _ if subs.is_empty() => {} Some(def) => { - let field_types = self.db.field_types(def); + let field_types = self.db.field_types_ns(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); @@ -85,10 +83,10 @@ impl InferenceContext<'_> { { // FIXME(DIAGNOSE): private tuple field } - let f = field_types[local_id].clone(); + let f = field_types[local_id]; let expected_ty = match substs { - Some(substs) => f.substitute(Interner, substs), - None => f.substitute(Interner, &Substitution::empty(Interner)), + Some(substs) => f.instantiate(self.interner(), substs), + None => f.instantiate(self.interner(), &[]), }; self.process_remote_user_written_ty(expected_ty) } @@ -96,13 +94,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(subpat, &expected_ty, default_bm, decl); + self.infer_pat(subpat, expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for &inner in subs { - self.infer_pat(inner, &err_ty, default_bm, decl); + self.infer_pat(inner, err_ty, default_bm, decl); } } } @@ -114,23 +112,23 @@ impl InferenceContext<'_> { pub(super) fn infer_record_pat_like( &mut self, path: Option<&Path>, - expected: &Ty, + expected: Ty<'db>, default_bm: BindingMode, id: PatId, subs: impl ExactSizeIterator, decl: Option, - ) -> Ty { + ) -> Ty<'db> { let (ty, def) = self.resolve_variant(id.into(), path, false); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } - self.unify(&ty, expected); + self.unify(ty, expected); match def { _ if subs.len() == 0 => {} Some(def) => { - let field_types = self.db.field_types(def); + let field_types = self.db.field_types_ns(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); @@ -149,10 +147,10 @@ impl InferenceContext<'_> { variant: def, }); } - let f = field_types[local_id].clone(); + let f = field_types[local_id]; let expected_ty = match substs { - Some(substs) => f.substitute(Interner, substs), - None => f.substitute(Interner, &Substitution::empty(Interner)), + Some(substs) => f.instantiate(self.interner(), substs), + None => f.instantiate(self.interner(), &[]), }; self.process_remote_user_written_ty(expected_ty) } @@ -167,13 +165,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(inner, &expected_ty, default_bm, decl); + self.infer_pat(inner, expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for (_, inner) in subs { - self.infer_pat(inner, &err_ty, default_bm, decl); + self.infer_pat(inner, err_ty, default_bm, decl); } } } @@ -186,16 +184,16 @@ impl InferenceContext<'_> { /// Ellipses found in the original pattern or expression must be filtered out. pub(super) fn infer_tuple_pat_like( &mut self, - expected: &Ty, + expected: Ty<'db>, default_bm: BindingMode, ellipsis: Option, subs: &[PatId], decl: Option, - ) -> Ty { + ) -> Ty<'db> { let expected = self.table.structurally_resolve_type(expected); - let expectations = match expected.as_tuple() { - Some(parameters) => parameters.as_slice(Interner), - _ => &[], + let expectations = match expected.kind() { + TyKind::Tuple(parameters) => parameters, + _ => self.types.empty_tys, }; let ((pre, post), n_uncovered_patterns) = match ellipsis { @@ -204,10 +202,8 @@ impl InferenceContext<'_> { } None => ((subs, &[][..]), 0), }; - let mut expectations_iter = expectations - .iter() - .map(|a| a.assert_ty_ref(Interner).clone()) - .chain(repeat_with(|| self.table.new_type_var())); + let mut expectations_iter = + expectations.iter().chain(repeat_with(|| self.table.next_ty_var())); let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + subs.len()); @@ -215,31 +211,35 @@ impl InferenceContext<'_> { // Process pre for (ty, pat) in inner_tys.iter_mut().zip(pre) { - *ty = self.infer_pat(*pat, ty, default_bm, decl); + *ty = self.infer_pat(*pat, *ty, default_bm, decl); } // Process post for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) { - *ty = self.infer_pat(*pat, ty, default_bm, decl); + *ty = self.infer_pat(*pat, *ty, default_bm, decl); } - TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) - .intern(Interner) + Ty::new_tup_from_iter(self.interner(), inner_tys.into_iter()) } /// The resolver needs to be updated to the surrounding expression when inside assignment /// (because there, `Pat::Path` can refer to a variable). - pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option) { + pub(super) fn infer_top_pat( + &mut self, + pat: PatId, + expected: Ty<'db>, + decl: Option, + ) { self.infer_pat(pat, expected, BindingMode::default(), decl); } fn infer_pat( &mut self, pat: PatId, - expected: &Ty, + expected: Ty<'db>, mut default_bm: BindingMode, decl: Option, - ) -> Ty { + ) -> Ty<'db> { let mut expected = self.table.structurally_resolve_type(expected); if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { @@ -251,9 +251,9 @@ impl InferenceContext<'_> { default_bm = BindingMode::Move; } else if self.is_non_ref_pat(self.body, pat) { let mut pat_adjustments = Vec::new(); - while let Some((inner, _lifetime, mutability)) = expected.as_reference() { - pat_adjustments.push(expected.clone()); - expected = self.table.structurally_resolve_type(inner); + while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() { + pat_adjustments.push(expected); + expected = self.table.try_structurally_resolve_type(inner); default_bm = match default_bm { BindingMode::Move => BindingMode::Ref(mutability), BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), @@ -273,25 +273,21 @@ impl InferenceContext<'_> { let ty = match &self.body[pat] { Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl) + self.infer_tuple_pat_like(expected, default_bm, *ellipsis, args, decl) } Pat::Or(pats) => { for pat in pats.iter() { - self.infer_pat(*pat, &expected, default_bm, decl); + self.infer_pat(*pat, expected, default_bm, decl); } - expected.clone() + expected + } + &Pat::Ref { pat, mutability } => { + self.infer_ref_pat(pat, lower_mutability(mutability), expected, default_bm, decl) } - &Pat::Ref { pat, mutability } => self.infer_ref_pat( - pat, - lower_to_chalk_mutability(mutability), - &expected, - default_bm, - decl, - ), Pat::TupleStruct { path: p, args: subpats, ellipsis } => self .infer_tuple_struct_pat_like( p.as_deref(), - &expected, + expected, default_bm, pat, *ellipsis, @@ -300,29 +296,26 @@ impl InferenceContext<'_> { ), Pat::Record { path: p, args: fields, ellipsis: _ } => { let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl) + self.infer_record_pat_like(p.as_deref(), expected, default_bm, pat, subs, decl) } Pat::Path(path) => { let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); - let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone()); + let ty_inserted_vars = self.insert_type_vars_shallow(ty); match self.coerce( pat.into(), - expected.to_nextsolver(self.table.interner), - ty_inserted_vars.to_nextsolver(self.table.interner), + expected, + ty_inserted_vars, AllowTwoPhase::No, CoerceNever::Yes, ) { Ok(coerced_ty) => { - self.write_pat_ty(pat, coerced_ty.to_chalk(self.table.interner)); + self.write_pat_ty(pat, coerced_ty); return self.pat_ty_after_adjustment(pat); } Err(_) => { self.result.type_mismatches.insert( pat.into(), - TypeMismatch { - expected: expected.clone(), - actual: ty_inserted_vars.clone(), - }, + TypeMismatch { expected, actual: ty_inserted_vars }, ); self.write_pat_ty(pat, ty); // We return `expected` to prevent cascading errors. I guess an alternative is to @@ -332,81 +325,77 @@ impl InferenceContext<'_> { } } Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl); + return self.infer_bind_pat(pat, *id, default_bm, *subpat, expected, decl); } Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl) + self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl) } - Pat::Wild => expected.clone(), + Pat::Wild => expected, Pat::Range { .. } => { // FIXME: do some checks here. - expected.clone() + expected } &Pat::Lit(expr) => { // Don't emit type mismatches again, the expression lowering already did that. - let ty = self.infer_lit_pat(expr, &expected); + let ty = self.infer_lit_pat(expr, expected); self.write_pat_ty(pat, ty); return self.pat_ty_after_adjustment(pat); } Pat::Box { inner } => match self.resolve_boxed_box() { Some(box_adt) => { let (inner_ty, alloc_ty) = match expected.as_adt() { - Some((adt, subst)) if adt == box_adt => ( - subst.at(Interner, 0).assert_ty_ref(Interner).clone(), - subst.as_slice(Interner).get(1).and_then(|a| a.ty(Interner).cloned()), - ), - _ => (self.result.standard_types.unknown.clone(), None), + Some((adt, subst)) if adt == box_adt => { + (subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type())) + } + _ => (self.types.error, None), }; - let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl); - let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty); - - if let Some(alloc_ty) = alloc_ty { - b = b.push(alloc_ty); - } - b.fill_with_defaults(self.db, || self.table.new_type_var()).build() + let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl); + Ty::new_adt( + self.interner(), + box_adt, + GenericArgs::fill_with_defaults( + self.interner(), + box_adt.into(), + std::iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)), + |_, _, id, _| self.table.next_var_for_param(id), + ), + ) } None => self.err_ty(), }, Pat::ConstBlock(expr) => { let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - let result = self.infer_expr( - *expr, - &Expectation::has_type(expected.clone()), - ExprIsRead::Yes, - ); + let result = + self.infer_expr(*expr, &Expectation::has_type(expected), ExprIsRead::Yes); self.inside_assignment = old_inside_assign; result } Pat::Expr(expr) => { let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); // LHS of assignment doesn't constitute reads. - let result = self.infer_expr_coerce( - *expr, - &Expectation::has_type(expected.clone()), - ExprIsRead::No, - ); + let result = + self.infer_expr_coerce(*expr, &Expectation::has_type(expected), ExprIsRead::No); // We are returning early to avoid the unifiability check below. let lhs_ty = self.insert_type_vars_shallow(result); let ty = match self.coerce( pat.into(), - expected.to_nextsolver(self.table.interner), - lhs_ty.to_nextsolver(self.table.interner), + expected, + lhs_ty, AllowTwoPhase::No, CoerceNever::Yes, ) { - Ok(ty) => ty.to_chalk(self.table.interner), + Ok(ty) => ty, Err(_) => { - self.result.type_mismatches.insert( - pat.into(), - TypeMismatch { expected: expected.clone(), actual: lhs_ty.clone() }, - ); + self.result + .type_mismatches + .insert(pat.into(), TypeMismatch { expected, actual: lhs_ty }); // `rhs_ty` is returned so no further type mismatches are // reported because of this mismatch. expected } }; - self.write_pat_ty(pat, ty.clone()); + self.write_pat_ty(pat, ty); self.inside_assignment = old_inside_assign; return ty; } @@ -415,46 +404,43 @@ impl InferenceContext<'_> { // use a new type variable if we got error type here let ty = self.insert_type_vars_shallow(ty); // FIXME: This never check is odd, but required with out we do inference right now - if !expected.is_never() && !self.unify(&ty, &expected) { - self.result - .type_mismatches - .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() }); + if !expected.is_never() && !self.unify(ty, expected) { + self.result.type_mismatches.insert(pat.into(), TypeMismatch { expected, actual: ty }); } self.write_pat_ty(pat, ty); self.pat_ty_after_adjustment(pat) } - fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty { - self.result + fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> { + *self + .result .pat_adjustments .get(&pat) .and_then(|it| it.first()) .unwrap_or(&self.result.type_of_pat[pat]) - .clone() } fn infer_ref_pat( &mut self, inner_pat: PatId, mutability: Mutability, - expected: &Ty, + expected: Ty<'db>, default_bm: BindingMode, decl: Option, - ) -> Ty { - let (expectation_type, expectation_lt) = match expected.as_reference() { - Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime), - None => { - let inner_ty = self.table.new_type_var(); - let inner_lt = self.table.new_lifetime_var(); - let ref_ty = - TyKind::Ref(mutability, inner_lt.clone(), inner_ty.clone()).intern(Interner); + ) -> Ty<'db> { + let (expectation_type, expectation_lt) = match expected.kind() { + TyKind::Ref(lifetime, inner_ty, _exp_mut) => (inner_ty, lifetime), + _ => { + let inner_ty = self.table.next_ty_var(); + let inner_lt = self.table.next_region_var(); + let ref_ty = Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability); // Unification failure will be reported by the caller. - self.unify(&ref_ty, expected); + self.unify(ref_ty, expected); (inner_ty, inner_lt) } }; - let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl); - TyKind::Ref(mutability, expectation_lt, subty).intern(Interner) + let subty = self.infer_pat(inner_pat, expectation_type, default_bm, decl); + Ty::new_ref(self.interner(), expectation_lt, subty, mutability) } fn infer_bind_pat( @@ -463,9 +449,9 @@ impl InferenceContext<'_> { binding: BindingId, default_bm: BindingMode, subpat: Option, - expected: &Ty, + expected: Ty<'db>, decl: Option, - ) -> Ty { + ) -> Ty<'db> { let Binding { mode, .. } = self.body[binding]; let mode = if mode == BindingAnnotation::Unannotated { default_bm @@ -476,31 +462,31 @@ impl InferenceContext<'_> { let inner_ty = match subpat { Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), - None => expected.clone(), + None => expected, }; let inner_ty = self.insert_type_vars_shallow(inner_ty); let bound_ty = match mode { BindingMode::Ref(mutability) => { - let inner_lt = self.table.new_lifetime_var(); - TyKind::Ref(mutability, inner_lt, inner_ty.clone()).intern(Interner) + let inner_lt = self.table.next_region_var(); + Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability) } - BindingMode::Move => inner_ty.clone(), + BindingMode::Move => inner_ty, }; - self.write_pat_ty(pat, inner_ty.clone()); + self.write_pat_ty(pat, inner_ty); self.write_binding_ty(binding, bound_ty); inner_ty } fn infer_slice_pat( &mut self, - expected: &Ty, + expected: Ty<'db>, prefix: &[PatId], - slice: &Option, + slice: Option, suffix: &[PatId], default_bm: BindingMode, decl: Option, - ) -> Ty { + ) -> Ty<'db> { let expected = self.table.structurally_resolve_type(expected); // If `expected` is an infer ty, we try to equate it to an array if the given pattern @@ -510,56 +496,61 @@ impl InferenceContext<'_> { && let Some(resolved_array_ty) = self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) { - self.unify(&expected, &resolved_array_ty); + self.unify(expected, resolved_array_ty); } - let expected = self.table.structurally_resolve_type(&expected); - let elem_ty = match expected.kind(Interner) { - TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), + let expected = self.table.try_structurally_resolve_type(expected); + let elem_ty = match expected.kind() { + TyKind::Array(st, _) | TyKind::Slice(st) => st, _ => self.err_ty(), }; for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, &elem_ty, default_bm, decl); + self.infer_pat(pat_id, elem_ty, default_bm, decl); } - if let &Some(slice_pat_id) = slice { - let rest_pat_ty = match expected.kind(Interner) { + if let Some(slice_pat_id) = slice { + let rest_pat_ty = match expected.kind() { TyKind::Array(_, length) => { let len = try_const_usize(self.db, length); let len = len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); - TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate())) + Ty::new_array_with_const_len( + self.interner(), + elem_ty, + usize_const(self.db, len, self.resolver.krate()), + ) } - _ => TyKind::Slice(elem_ty.clone()), - } - .intern(Interner); - self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl); + _ => Ty::new_slice(self.interner(), elem_ty), + }; + self.infer_pat(slice_pat_id, rest_pat_ty, default_bm, decl); } - match expected.kind(Interner) { - TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()), - _ => TyKind::Slice(elem_ty), + match expected.kind() { + TyKind::Array(_, const_) => { + Ty::new_array_with_const_len(self.interner(), elem_ty, const_) + } + _ => Ty::new_slice(self.interner(), elem_ty), } - .intern(Interner) } - fn infer_lit_pat(&mut self, expr: ExprId, expected: &Ty) -> Ty { + fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] - && let Some((inner, ..)) = expected.as_reference() + && let TyKind::Ref(_, inner, _) = expected.kind() { - let inner = self.table.structurally_resolve_type(inner); - if matches!(inner.kind(Interner), TyKind::Slice(_)) { - let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); - let slice_ty = TyKind::Slice(elem_ty).intern(Interner); - let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty).intern(Interner); - self.write_expr_ty(expr, ty.clone()); + let inner = self.table.try_structurally_resolve_type(inner); + if matches!(inner.kind(), TyKind::Slice(_)) { + let elem_ty = self.types.u8; + let slice_ty = Ty::new_slice(self.interner(), elem_ty); + let ty = + Ty::new_ref(self.interner(), self.types.re_static, slice_ty, Mutability::Not); + self.write_expr_ty(expr, ty); return ty; } } - self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) + self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) } fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool { @@ -593,17 +584,21 @@ impl InferenceContext<'_> { &mut self, before: &[PatId], suffix: &[PatId], - slice: &Option, - ) -> Option { - if !slice.is_none() { + slice: Option, + ) -> Option> { + if slice.is_some() { return None; } let len = before.len() + suffix.len(); - let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db)); - - let elem_ty = self.table.new_type_var(); - let array_ty = TyKind::Array(elem_ty, size).intern(Interner); + let size = consteval_nextsolver::usize_const( + self.db, + Some(len as u128), + self.owner.krate(self.db), + ); + + let elem_ty = self.table.next_ty_var(); + let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size); Some(array_ty) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 733f3c278806f..7517272362b2c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -1,52 +1,50 @@ //! Path expression resolution. -use chalk_ir::cast::Cast; use hir_def::{ - AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup, + AdtId, AssocItemId, GenericDefId, GenericParamId, ItemContainerId, Lookup, expr_store::path::{Path, PathSegment}, resolver::{ResolveValueResult, TypeNs, ValueNs}, }; use hir_expand::name::Name; +use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, - TyBuilder, TyExt, TyKind, ValueTyDefId, - builder::ParamKind, - consteval, error_lifetime, + InferenceDiagnostic, ValueTyDefId, consteval_nextsolver, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, + lower_nextsolver::LifetimeElisionKind, method_resolution::{self, VisibleFromModule}, next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + GenericArg, GenericArgs, TraitRef, Ty, + infer::traits::{Obligation, ObligationCause}, }, - to_chalk_trait_id, }; use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; -impl<'db> InferenceContext<'db> { - pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { +impl<'db> InferenceContext<'_, 'db> { + pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option> { let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { (value_def, generic_def, substs) } ValuePathResolution::NonGeneric(ty) => return Some(ty), }; - let substs = - self.process_remote_user_written_ty::<_, crate::next_solver::GenericArgs<'db>>(substs); + let args = self.process_remote_user_written_ty(substs); - self.add_required_obligations_for_value_path(generic_def, &substs); + self.add_required_obligations_for_value_path(generic_def, args); - let interner = DbInterner::new_with(self.db, None, None); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = self.db.value_ty(value_def)?.instantiate(interner, args).to_chalk(interner); + let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args); let ty = self.process_remote_user_written_ty(ty); Some(ty) } - fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { + fn resolve_value_path( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option> { let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; let value_def: ValueTyDefId = match value { @@ -65,7 +63,7 @@ impl<'db> InferenceContext<'db> { } ValueNs::LocalBinding(pat) => { return match self.result.type_of_binding.get(pat) { - Some(ty) => Some(ValuePathResolution::NonGeneric(ty.clone())), + Some(ty) => Some(ValuePathResolution::NonGeneric(*ty)), None => { never!("uninferred pattern?"); None @@ -73,17 +71,12 @@ impl<'db> InferenceContext<'db> { }; } ValueNs::ImplSelf(impl_id) => { - let generics = crate::generics::generics(self.db, impl_id.into()); - let interner = DbInterner::new_with(self.db, None, None); - let substs = generics.placeholder_subst(self.db); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let ty = - self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); + let ty = self.db.impl_self_ty(impl_id).instantiate_identity(); return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { Some(ValuePathResolution::GenericDef( struct_id.into(), struct_id.into(), - substs.clone(), + substs, )) } else { // FIXME: report error, invalid Self reference @@ -91,15 +84,14 @@ impl<'db> InferenceContext<'db> { }; } ValueNs::GenericParam(it) => { - return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))); + return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it))); } }; let generic_def = value_def.to_generic_def_id(self.db); if let GenericDefId::StaticId(_) = generic_def { - let interner = DbInterner::new_with(self.db, None, None); // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. - let ty = self.db.value_ty(value_def)?.skip_binder().to_chalk(interner); + let ty = self.db.value_ty(value_def)?.skip_binder(); return Some(ValuePathResolution::NonGeneric(ty)); }; @@ -111,57 +103,34 @@ impl<'db> InferenceContext<'db> { } path_ctx.substs_from_path(value_def, true, false) }); - let substs = substs.as_slice(Interner); - - if let ValueNs::EnumVariantId(_) = value { - let mut it = substs - .iter() - .chain(self_subst.as_ref().map_or(&[][..], |s| s.as_slice(Interner))) - .cloned(); - let builder = TyBuilder::subst_for_def(self.db, generic_def, None); - let substs = builder - .fill(|x| { - it.next().unwrap_or_else(|| match x { - ParamKind::Type => { - self.result.standard_types.unknown.clone().cast(Interner) - } - ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), - ParamKind::Lifetime => error_lifetime().cast(Interner), - }) - }) - .build(); - return Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)); - } - - let parent_substs = self_subst.or_else(|| { - let generics = generics(self.db, generic_def); - let parent_params_len = generics.parent_generics()?.len(); - let parent_args = &substs[..parent_params_len]; - Some(Substitution::from_iter(Interner, parent_args)) - }); - let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner)); - let mut it = substs.iter().skip(parent_substs_len).cloned(); - let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs); - let substs = builder - .fill(|x| { - it.next().unwrap_or_else(|| match x { - ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), - ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), - ParamKind::Lifetime => error_lifetime().cast(Interner), - }) - }) - .build(); + let parent_substs_len = self_subst.map_or(0, |it| it.len()); + let substs = GenericArgs::fill_rest( + self.interner(), + generic_def.into(), + self_subst.iter().flat_map(|it| it.iter()).chain(substs.iter().skip(parent_substs_len)), + |_, _, id, _| self.error_param(id), + ); Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) } + fn error_param(&mut self, id: GenericParamId) -> GenericArg<'db> { + match id { + GenericParamId::TypeParamId(_) => self.types.error.into(), + GenericParamId::ConstParamId(id) => { + consteval_nextsolver::unknown_const_as_generic(self.db.const_param_ty_ns(id)) + } + GenericParamId::LifetimeParamId(_) => self.types.re_error.into(), + } + } + pub(super) fn resolve_value_path_inner( &mut self, path: &Path, id: ExprOrPatId, no_diagnostics: bool, - ) -> Option<(ValueNs, Option>)> { + ) -> Option<(ValueNs, Option>)> { // Don't use `self.make_ty()` here as we need `orig_ns`. let mut ctx = TyLoweringContext::new( self.db, @@ -211,7 +180,7 @@ impl<'db> InferenceContext<'db> { let (resolution, substs) = match (def, is_before_last) { (TypeNs::TraitId(trait_), true) => { - let self_ty = self.table.new_type_var(); + let self_ty = self.table.next_ty_var(); let trait_ref = path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true); drop_ctx(ctx, no_diagnostics); @@ -225,7 +194,7 @@ impl<'db> InferenceContext<'db> { path_ctx.ignore_last_segment(); let (ty, _) = path_ctx.lower_partly_resolved_path(def, true); drop_ctx(ctx, no_diagnostics); - if ty.is_unknown() { + if ty.is_ty_error() { return None; } @@ -241,21 +210,25 @@ impl<'db> InferenceContext<'db> { return Some((value, self_subst)); #[inline] - fn drop_ctx(mut ctx: TyLoweringContext<'_>, no_diagnostics: bool) { + fn drop_ctx(mut ctx: TyLoweringContext<'_, '_>, no_diagnostics: bool) { if no_diagnostics { ctx.forget_diagnostics(); } } } - fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) { - let predicates = self.db.generic_predicates(def); - for predicate in predicates.iter() { - let (predicate, binders) = - predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders(); - // Quantified where clauses are not yet handled. - stdx::always!(binders.is_empty(Interner)); - self.push_obligation(predicate.cast(Interner)); + fn add_required_obligations_for_value_path( + &mut self, + def: GenericDefId, + subst: GenericArgs<'db>, + ) { + let predicates = self.db.generic_predicates_ns(def); + let interner = self.interner(); + let param_env = self.table.trait_env.env; + if let Some(predicates) = predicates.instantiate(self.interner(), subst) { + self.table.register_predicates(predicates.map(|predicate| { + Obligation::new(interner, ObligationCause::new(), param_env, predicate) + })); } // We need to add `Self: Trait` obligation when `def` is a trait assoc item. @@ -267,21 +240,27 @@ impl<'db> InferenceContext<'db> { if let ItemContainerId::TraitId(trait_) = container { let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self()); - let parent_subst = - Substitution::from_iter(Interner, subst.iter(Interner).take(parent_len)); - let trait_ref = - TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst }; - self.push_obligation(trait_ref.cast(Interner)); + let parent_subst = GenericArgs::new_from_iter( + interner, + subst.as_slice()[..parent_len].iter().copied(), + ); + let trait_ref = TraitRef::new(interner, trait_.into(), parent_subst); + self.table.register_predicate(Obligation::new( + interner, + ObligationCause::new(), + param_env, + trait_ref, + )); } } fn resolve_trait_assoc_item( &mut self, - trait_ref: TraitRef, + trait_ref: TraitRef<'db>, segment: PathSegment<'_>, id: ExprOrPatId, - ) -> Option<(ValueNs, Substitution)> { - let trait_ = trait_ref.hir_trait_id(); + ) -> Option<(ValueNs, GenericArgs<'db>)> { + let trait_ = trait_ref.def_id.0; let item = trait_.trait_items(self.db).items.iter().map(|(_name, id)| *id).find_map(|item| { match item { @@ -309,25 +288,25 @@ impl<'db> InferenceContext<'db> { AssocItemId::TypeAliasId(_) => unreachable!(), }; - self.write_assoc_resolution(id, item, trait_ref.substitution.clone()); - Some((def, trait_ref.substitution)) + self.write_assoc_resolution(id, item, trait_ref.args); + Some((def, trait_ref.args)) } fn resolve_ty_assoc_item( &mut self, - ty: Ty, + ty: Ty<'db>, name: &Name, id: ExprOrPatId, - ) -> Option<(ValueNs, Substitution)> { - if let TyKind::Error = ty.kind(Interner) { + ) -> Option<(ValueNs, GenericArgs<'db>)> { + if ty.is_ty_error() { return None; } - if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { + if let Some(result) = self.resolve_enum_variant_on_ty(ty, name, id) { return Some(result); } - let canonical_ty = self.canonicalize(ty.clone().to_nextsolver(self.table.interner)); + let canonical_ty = self.canonicalize(ty); let mut not_visible = None; let res = method_resolution::iterate_method_candidates( @@ -362,24 +341,28 @@ impl<'db> InferenceContext<'db> { }; let substs = match container { ItemContainerId::ImplId(impl_id) => { - let interner = DbInterner::new_with(self.db, None, None); - let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None) - .fill_with_inference_vars(&mut self.table) - .build(); - let args: crate::next_solver::GenericArgs<'_> = impl_substs.to_nextsolver(interner); + let impl_substs = self.table.fresh_args_for_item(impl_id.into()); let impl_self_ty = - self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); - self.unify(&impl_self_ty, &ty); + self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs); + self.unify(impl_self_ty, ty); impl_substs } ItemContainerId::TraitId(trait_) => { // we're picking this method - let trait_ref = TyBuilder::trait_ref(self.db, trait_) - .push(ty.clone()) - .fill_with_inference_vars(&mut self.table) - .build(); - self.push_obligation(trait_ref.clone().cast(Interner)); - trait_ref.substitution + let args = GenericArgs::fill_rest( + self.interner(), + trait_.into(), + [ty.into()], + |_, _, id, _| self.table.next_var_for_param(id), + ); + let trait_ref = TraitRef::new(self.interner(), trait_.into(), args); + self.table.register_predicate(Obligation::new( + self.interner(), + ObligationCause::new(), + self.table.trait_env.env, + trait_ref, + )); + args } ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { never!("assoc item contained in module/extern block"); @@ -387,7 +370,7 @@ impl<'db> InferenceContext<'db> { } }; - self.write_assoc_resolution(id, item, substs.clone()); + self.write_assoc_resolution(id, item, substs); if !visible { self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item }); } @@ -396,11 +379,11 @@ impl<'db> InferenceContext<'db> { fn resolve_enum_variant_on_ty( &mut self, - ty: &Ty, + ty: Ty<'db>, name: &Name, id: ExprOrPatId, - ) -> Option<(ValueNs, Substitution)> { - let ty = self.table.structurally_resolve_type(ty); + ) -> Option<(ValueNs, GenericArgs<'db>)> { + let ty = self.table.try_structurally_resolve_type(ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, @@ -408,14 +391,14 @@ impl<'db> InferenceContext<'db> { let enum_data = enum_id.enum_variants(self.db); let variant = enum_data.variant(name)?; self.write_variant_resolution(id, variant.into()); - Some((ValueNs::EnumVariantId(variant), subst.clone())) + Some((ValueNs::EnumVariantId(variant), subst)) } } #[derive(Debug)] -enum ValuePathResolution { +enum ValuePathResolution<'db> { // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible // conversion between them + `unwrap()`. - GenericDef(ValueTyDefId, GenericDefId, Substitution), - NonGeneric(Ty), + GenericDef(ValueTyDefId, GenericDefId, GenericArgs<'db>), + NonGeneric(Ty<'db>), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 108cf5b1a2b8e..89bab4e59c94c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -2,49 +2,46 @@ use std::fmt; -use chalk_ir::{ - CanonicalVarKind, TyVariableKind, cast::Cast, fold::TypeFoldable, interner::HasInterner, -}; -use either::Either; +use chalk_ir::cast::Cast; +use hir_def::GenericParamId; use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::{DebruijnIndex, InferConst, InferTy, RegionVid}; use rustc_type_ir::{ - TyVid, TypeVisitableExt, UpcastFrom, - inherent::{IntoKind, Span, Term as _, Ty as _}, - relate::{Relate, solver_relating::RelateExt}, + TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UpcastFrom, + inherent::{Const as _, IntoKind, Ty as _}, solve::{Certainty, GoalSource}, }; use smallvec::SmallVec; use triomphe::Arc; -use super::{InferResult, InferenceContext, TypeError}; +use crate::next_solver::{Binder, ConstKind, GenericArgs, RegionKind, SolverDefId}; use crate::{ - AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData, - InferenceVar, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, TraitEnvironment, Ty, - TyExt, TyKind, VariableKind, - consteval::unknown_const, - db::HirDatabase, - fold_generic_args, fold_tys_and_consts, + Interner, TraitEnvironment, + db::{HirDatabase, InternedOpaqueTyId}, + infer::InferenceContext, next_solver::{ - self, ClauseKind, DbInterner, ErrorGuaranteed, Predicate, PredicateKind, SolverDefIds, - Term, TraitRef, - fulfill::FulfillmentCtxt, + self, AliasTy, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, Predicate, + PredicateKind, Region, SolverDefIds, TraitRef, Ty, TyKind, TypingMode, + fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, InferOk, + DbInternerInferExt, DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + at::ToTrace, snapshot::CombinedSnapshot, - traits::{Obligation, ObligationCause}, + traits::{Obligation, ObligationCause, PredicateObligation}, }, inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, }, traits::{ FnTrait, NextTraitSolveResult, next_trait_solve_canonical_in_ctxt, next_trait_solve_in_ctxt, }, }; -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { pub(super) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where T: rustc_type_ir::TypeFoldable>, @@ -77,7 +74,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { return; } - let db = self.ctx.interner; + let db = self.ctx.interner(); let goal = inspect_goal.goal(); if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) // We do not push the instantiated forms of goals as it would cause any @@ -121,9 +118,9 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { pub fn could_unify( db: &dyn HirDatabase, env: Arc>, - tys: &Canonical<(Ty, Ty)>, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, ) -> bool { - unify(db, env, tys).is_some() + could_unify_impl(db, env, tys, |ctxt| ctxt.select_where_possible()) } /// Check if types unify eagerly making sure there are no unresolved goals. @@ -133,94 +130,65 @@ pub fn could_unify( pub fn could_unify_deeply( db: &dyn HirDatabase, env: Arc>, - tys: &Canonical<(Ty, Ty)>, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, ) -> bool { - let mut table = InferenceTable::new(db, env); - let vars = make_substitutions(tys, &mut table); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); - let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); - let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); - table.select_obligations_where_possible(); - let ty1_with_vars = table.resolve_completely(ty1_with_vars); - let ty2_with_vars = table.resolve_completely(ty2_with_vars); - table.unify_deeply(&ty1_with_vars, &ty2_with_vars) + could_unify_impl(db, env, tys, |ctxt| ctxt.select_all_or_error()) } -pub(crate) fn unify( +fn could_unify_impl( db: &dyn HirDatabase, env: Arc>, - tys: &Canonical<(Ty, Ty)>, -) -> Option { - let mut table = InferenceTable::new(db, env); - let vars = make_substitutions(tys, &mut table); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); - if !table.unify(&ty1_with_vars, &ty2_with_vars) { - return None; - } - // default any type vars that weren't unified back to their original bound vars - // (kind of hacky) - let find_var = |iv| { - vars.iter(Interner).position(|v| match v.data(Interner) { - GenericArgData::Ty(ty) => ty.inference_var(Interner), - GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - GenericArgData::Const(c) => c.inference_var(Interner), - } == Some(iv)) - }; - let fallback = |iv, kind, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else( - || TyKind::Error.intern(Interner).cast(Interner), - |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner), - ), - chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else( - || crate::error_lifetime().cast(Interner), - |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner), - ), - chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else( - || crate::unknown_const(ty.clone()).cast(Interner), - |i| BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner), - ), - }; - Some(Substitution::from_iter( - Interner, - vars.iter(Interner).map(|v| table.resolve_with_fallback(v.clone(), &fallback)), - )) + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, + select: for<'a, 'db> fn(&mut ObligationCtxt<'a, 'db>) -> Vec>, +) -> bool { + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + // FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things), + // but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT + // and async blocks. + let infcx = interner.infer_ctxt().build(TypingMode::Analysis { + defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), + }); + let cause = ObligationCause::dummy(); + let at = infcx.at(&cause, env.env); + let vars = make_substitutions(tys, &infcx); + let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner).to_nextsolver(interner); + let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner).to_nextsolver(interner); + let mut ctxt = ObligationCtxt::new(&infcx); + let can_unify = at + .eq(DefineOpaqueTypes::No, ty1_with_vars, ty2_with_vars) + .map(|infer_ok| ctxt.register_infer_ok_obligations(infer_ok)) + .is_ok(); + can_unify && select(&mut ctxt).is_empty() } fn make_substitutions( - tys: &chalk_ir::Canonical<(chalk_ir::Ty, chalk_ir::Ty)>, - table: &mut InferenceTable<'_>, -) -> chalk_ir::Substitution { - Substitution::from_iter( + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, + infcx: &InferCtxt<'_>, +) -> crate::Substitution { + let interner = infcx.interner; + crate::Substitution::from_iter( Interner, tys.binders.iter(Interner).map(|it| match &it.kind { - chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner), + chalk_ir::VariableKind::Ty(_) => infcx.next_ty_var().to_chalk(interner).cast(Interner), // FIXME: maybe wrong? - chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner), - chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), + chalk_ir::VariableKind::Lifetime => { + infcx.next_ty_var().to_chalk(interner).cast(Interner) + } + chalk_ir::VariableKind::Const(_ty) => { + infcx.next_const_var().to_chalk(interner).cast(Interner) + } }), ) } -bitflags::bitflags! { - #[derive(Default, Clone, Copy)] - pub(crate) struct TypeVariableFlags: u8 { - const DIVERGING = 1 << 0; - const INTEGER = 1 << 1; - const FLOAT = 1 << 2; - } -} - #[derive(Clone)] pub(crate) struct InferenceTable<'db> { pub(crate) db: &'db dyn HirDatabase, - pub(crate) interner: DbInterner<'db>, pub(crate) trait_env: Arc>, - pub(crate) tait_coercion_table: Option>, + pub(crate) tait_coercion_table: Option>>, pub(crate) infer_ctxt: InferCtxt<'db>, pub(super) fulfillment_cx: FulfillmentCtxt<'db>, - pub(super) diverging_type_vars: FxHashSet>, + pub(super) diverging_type_vars: FxHashSet>, } pub(crate) struct InferenceTableSnapshot<'db> { @@ -236,7 +204,6 @@ impl<'db> InferenceTable<'db> { }); InferenceTable { db, - interner, trait_env, tait_coercion_table: None, fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), @@ -245,15 +212,22 @@ impl<'db> InferenceTable<'db> { } } + #[inline] + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.infer_ctxt.interner + } + + pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'db>) -> bool { + self.infer_ctxt.type_is_copy_modulo_regions(self.trait_env.env, ty) + } + pub(crate) fn type_var_is_sized(&self, self_ty: TyVid) -> bool { let Some(sized_did) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else { return true; }; self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { match obligation.predicate.kind().skip_binder() { - crate::next_solver::PredicateKind::Clause( - crate::next_solver::ClauseKind::Trait(data), - ) => data.def_id().0 == sized_did, + PredicateKind::Clause(ClauseKind::Trait(data)) => data.def_id().0 == sized_did, _ => false, } }) @@ -309,28 +283,24 @@ impl<'db> InferenceTable<'db> { } } - fn type_matches_expected_vid( - &self, - expected_vid: TyVid, - ty: crate::next_solver::Ty<'db>, - ) -> bool { + fn type_matches_expected_vid(&self, expected_vid: TyVid, ty: Ty<'db>) -> bool { let ty = self.shallow_resolve(ty); match ty.kind() { - crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { + TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) } _ => false, } } - pub(super) fn set_diverging(&mut self, ty: crate::next_solver::Ty<'db>) { + pub(super) fn set_diverging(&mut self, ty: Ty<'db>) { self.diverging_type_vars.insert(ty); } pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: rustc_type_ir::TypeFoldable>, + T: TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables @@ -338,26 +308,11 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.canonicalize_response(t) } - /// Recurses through the given type, normalizing associated types mentioned - /// in it by replacing them by type variables and registering obligations to - /// resolve later. This should be done once for every type we get from some - /// type annotation (e.g. from a let type annotation, field type or function - /// call). `make_ty` handles this already, but e.g. for field types we need - /// to do it as well. - pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T - where - T: ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, - { - self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner)) - .to_chalk(self.interner) - } - // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing. // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed. - pub(crate) fn normalize_associated_types_in_ns(&mut self, ty: T) -> T + pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: rustc_type_ir::TypeFoldable> + Clone, + T: TypeFoldable> + Clone, { let ty = self.resolve_vars_with_obligations(ty); self.infer_ctxt @@ -370,176 +325,130 @@ impl<'db> InferenceTable<'db> { /// the inference variables pub(crate) fn eagerly_normalize_and_resolve_shallow_in(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: TypeFoldable>, { - fn eagerly_resolve_ty( - table: &mut InferenceTable<'_>, - ty: Ty, - mut tys: SmallVec<[Ty; N]>, - ) -> Ty { - if tys.contains(&ty) { - return ty; - } - tys.push(ty.clone()); - - match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - let ty = table.normalize_projection_ty(proj_ty.clone()); - eagerly_resolve_ty(table, ty, tys) - } - TyKind::InferenceVar(..) => { - let ty = table.resolve_ty_shallow(&ty); - eagerly_resolve_ty(table, ty, tys) - } - _ => ty, - } - } - - fold_tys_and_consts( - ty, - |e, _| match e { - Either::Left(ty) => { - Either::Left(eagerly_resolve_ty::<8>(self, ty, SmallVec::new())) - } - Either::Right(c) => Either::Right(match &c.data(Interner).value { - chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { - crate::ConstScalar::UnevaluatedConst(c_id, subst) => { - // FIXME: same as `normalize_associated_types_in` - if subst.len(Interner) == 0 { - if let Ok(eval) = self.db.const_eval(*c_id, subst.clone(), None) { - eval - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } - _ => c, - }, - _ => c, - }), - }, - DebruijnIndex::INNERMOST, - ) - } - - pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { - let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(proj_ty)) - .intern(Interner) - .to_nextsolver(self.interner); - self.normalize_alias_ty(ty).to_chalk(self.interner) - } - - pub(crate) fn normalize_alias_ty( - &mut self, - alias: crate::next_solver::Ty<'db>, - ) -> crate::next_solver::Ty<'db> { - let infer_term = self.infer_ctxt.next_ty_var(); - let obligation = crate::next_solver::Predicate::new( - self.interner, - crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( - alias.into(), - infer_term.into(), - rustc_type_ir::AliasRelationDirection::Equate, - )), - ); - self.register_obligation(obligation); - self.resolve_vars_with_obligations(infer_term) - } - - fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { - let var = match kind { - TyVariableKind::General => { - let var = self.infer_ctxt.next_ty_vid(); - InferenceVar::from(var.as_u32()) - } - TyVariableKind::Integer => { - let var = self.infer_ctxt.next_int_vid(); - InferenceVar::from(var.as_u32()) - } - TyVariableKind::Float => { - let var = self.infer_ctxt.next_float_vid(); - InferenceVar::from(var.as_u32()) - } - }; - - let ty = var.to_ty(Interner, kind); - if diverging { - self.diverging_type_vars.insert(ty.to_nextsolver(self.interner)); - } - ty + let ty = self.resolve_vars_with_obligations(ty); + let ty = self.normalize_associated_types_in(ty); + self.resolve_vars_with_obligations(ty) } - pub(crate) fn new_type_var(&mut self) -> Ty { - self.new_var(TyVariableKind::General, false) + pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { + self.infer_ctxt + .at(&ObligationCause::new(), self.trait_env.env) + .structurally_normalize_ty(alias, &mut self.fulfillment_cx) + .unwrap_or(alias) } - pub(crate) fn next_ty_var(&mut self) -> crate::next_solver::Ty<'db> { + pub(crate) fn next_ty_var(&mut self) -> Ty<'db> { self.infer_ctxt.next_ty_var() } - pub(crate) fn new_integer_var(&mut self) -> Ty { - self.new_var(TyVariableKind::Integer, false) + pub(crate) fn next_const_var(&mut self) -> Const<'db> { + self.infer_ctxt.next_const_var() } - pub(crate) fn new_float_var(&mut self) -> Ty { - self.new_var(TyVariableKind::Float, false) + pub(crate) fn next_int_var(&mut self) -> Ty<'db> { + self.infer_ctxt.next_int_var() } - pub(crate) fn new_maybe_never_var(&mut self) -> Ty { - self.new_var(TyVariableKind::General, true) + pub(crate) fn next_float_var(&mut self) -> Ty<'db> { + self.infer_ctxt.next_float_var() } - pub(crate) fn new_const_var(&mut self, ty: Ty) -> Const { - let var = self.infer_ctxt.next_const_vid(); - let var = InferenceVar::from(var.as_u32()); - var.to_const(Interner, ty) + pub(crate) fn new_maybe_never_var(&mut self) -> Ty<'db> { + let var = self.next_ty_var(); + self.set_diverging(var); + var } - pub(crate) fn new_lifetime_var(&mut self) -> Lifetime { - let var = self.infer_ctxt.next_region_vid(); - let var = InferenceVar::from(var.as_u32()); - var.to_lifetime(Interner) + pub(crate) fn next_region_var(&mut self) -> Region<'db> { + self.infer_ctxt.next_region_var() } - pub(crate) fn next_region_var(&mut self) -> crate::next_solver::Region<'db> { - self.infer_ctxt.next_region_var() + pub(crate) fn next_var_for_param(&mut self, id: GenericParamId) -> GenericArg<'db> { + match id { + GenericParamId::TypeParamId(_) => self.next_ty_var().into(), + GenericParamId::ConstParamId(_) => self.next_const_var().into(), + GenericParamId::LifetimeParamId(_) => self.next_region_var().into(), + } } pub(crate) fn resolve_with_fallback( &mut self, t: T, - fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, + fallback_ty: &mut dyn FnMut(DebruijnIndex, InferTy) -> Ty<'db>, + fallback_const: &mut dyn FnMut(DebruijnIndex, InferConst) -> Const<'db>, + fallback_region: &mut dyn FnMut(DebruijnIndex, RegionVid) -> Region<'db>, ) -> T where - T: HasInterner + TypeFoldable, + T: TypeFoldable>, { - self.resolve_with_fallback_inner(t, &fallback) - } + struct Resolver<'a, 'db> { + table: &'a mut InferenceTable<'db>, + binder: DebruijnIndex, + fallback_ty: &'a mut dyn FnMut(DebruijnIndex, InferTy) -> Ty<'db>, + fallback_const: &'a mut dyn FnMut(DebruijnIndex, InferConst) -> Const<'db>, + fallback_region: &'a mut dyn FnMut(DebruijnIndex, RegionVid) -> Region<'db>, + } + + impl<'db> TypeFolder> for Resolver<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.table.interner() + } - pub(crate) fn fresh_subst(&mut self, binders: &[CanonicalVarKind]) -> Substitution { - Substitution::from_iter( - Interner, - binders.iter().map(|kind| match &kind.kind { - chalk_ir::VariableKind::Ty(ty_variable_kind) => { - self.new_var(*ty_variable_kind, false).cast(Interner) + fn fold_binder(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + T: TypeFoldable>, + { + self.binder.shift_in(1); + let result = t.super_fold_with(self); + self.binder.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_infer() { + return t; } - chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner), - chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner), - }), - ) - } - pub(crate) fn instantiate_canonical(&mut self, canonical: Canonical) -> T - where - T: HasInterner + TypeFoldable + std::fmt::Debug, - { - let subst = self.fresh_subst(canonical.binders.as_slice(Interner)); - subst.apply(canonical.value, Interner) + if let TyKind::Infer(infer) = t.kind() { + (self.fallback_ty)(self.binder, infer) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_infer() { + return c; + } + + if let ConstKind::Infer(infer) = c.kind() { + (self.fallback_const)(self.binder, infer) + } else { + c.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if let RegionKind::ReVar(infer) = r.kind() { + (self.fallback_region)(self.binder, infer) + } else { + r + } + } + } + + t.fold_with(&mut Resolver { + table: self, + binder: DebruijnIndex::ZERO, + fallback_ty, + fallback_const, + fallback_region, + }) } - pub(crate) fn instantiate_canonical_ns( + pub(crate) fn instantiate_canonical( &mut self, canonical: rustc_type_ir::Canonical, T>, ) -> T @@ -549,112 +458,35 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.instantiate_canonical(&canonical).0 } - fn resolve_with_fallback_inner( - &mut self, - t: T, - fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, - ) -> T + pub(crate) fn resolve_completely(&mut self, value: T) -> T where - T: HasInterner + TypeFoldable, + T: TypeFoldable>, { - let var_stack = &mut vec![]; - t.fold_with( - &mut resolve::Resolver { table: self, var_stack, fallback }, - DebruijnIndex::INNERMOST, - ) - } - - pub(crate) fn resolve_completely(&mut self, t: T) -> T - where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, - { - let value = t.to_nextsolver(self.interner); let value = self.infer_ctxt.resolve_vars_if_possible(value); let mut goals = vec![]; - let value = value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)); // FIXME(next-solver): Handle `goals`. - value.to_chalk(self.interner) + value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify, U: Relate>>( - &mut self, - ty1: &T, - ty2: &T, - ) -> bool { - let result = match self.try_unify(ty1, ty2) { - Ok(r) => r, - Err(_) => return false, - }; - self.register_obligations(result.goals); - true - } - - pub(crate) fn unify_ns>>(&mut self, lhs: T, rhs: T) -> bool { - let Ok(infer_ok) = self.try_unify_ns(lhs, rhs) else { - return false; - }; - self.register_obligations(infer_ok.goals); - true - } - - /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply, U: Relate>>( - &mut self, - ty1: &T, - ty2: &T, - ) -> bool { - let result = match self.try_unify(ty1, ty2) { - Ok(r) => r, - Err(_) => return false, - }; - result.goals.into_iter().all(|goal| { - matches!(next_trait_solve_in_ctxt(&self.infer_ctxt, goal), Ok((_, Certainty::Yes))) - }) + pub(crate) fn unify>(&mut self, ty1: T, ty2: T) -> bool { + self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok() } /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify, U: Relate>>( - &mut self, - t1: &T, - t2: &T, - ) -> InferResult<'db, ()> { - let lhs = t1.to_nextsolver(self.interner); - let rhs = t2.to_nextsolver(self.interner); - self.try_unify_ns(lhs, rhs) - } - - /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the - /// caller needs to deal with them. - pub(crate) fn try_unify_ns>>( - &mut self, - lhs: T, - rhs: T, - ) -> InferResult<'db, ()> { - let variance = rustc_type_ir::Variance::Invariant; - let span = crate::next_solver::Span::dummy(); - match self.infer_ctxt.relate(self.trait_env.env, lhs, variance, rhs, span) { - Ok(goals) => Ok(crate::infer::InferOk { goals, value: () }), - Err(_) => Err(TypeError), - } - } - - /// If `ty` is a type variable with known type, returns that type; - /// otherwise, return ty. - #[tracing::instrument(skip(self))] - pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { - self.shallow_resolve(ty.to_nextsolver(self.interner)).to_chalk(self.interner) + pub(crate) fn try_unify>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> { + self.infer_ctxt.at(&ObligationCause::new(), self.trait_env.env).eq( + DefineOpaqueTypes::Yes, + t1, + t2, + ) } - pub(crate) fn shallow_resolve( - &self, - ty: crate::next_solver::Ty<'db>, - ) -> crate::next_solver::Ty<'db> { + pub(crate) fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { self.infer_ctxt.shallow_resolve(ty) } @@ -662,8 +494,6 @@ impl<'db> InferenceTable<'db> { where T: rustc_type_ir::TypeFoldable>, { - use rustc_type_ir::TypeVisitableExt; - if !t.has_non_region_infer() { return t; } @@ -678,26 +508,18 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.resolve_vars_if_possible(t) } - pub(crate) fn structurally_resolve_type(&mut self, ty: &Ty) -> Ty { - if let TyKind::Alias(chalk_ir::AliasTy::Projection(..)) = ty.kind(Interner) { - self.structurally_normalize_ty(ty) - } else { - self.resolve_vars_with_obligations(ty.to_nextsolver(self.interner)) - .to_chalk(self.interner) - } - } - - fn structurally_normalize_ty(&mut self, ty: &Ty) -> Ty { - self.structurally_normalize_term(ty.to_nextsolver(self.interner).into()) - .expect_ty() - .to_chalk(self.interner) + /// Create a `GenericArgs` full of infer vars for `def`. + pub(crate) fn fresh_args_for_item(&self, def: SolverDefId) -> GenericArgs<'db> { + self.infer_ctxt.fresh_args_for_item(def) } - fn structurally_normalize_term(&mut self, term: Term<'db>) -> Term<'db> { - self.infer_ctxt - .at(&ObligationCause::new(), self.trait_env.env) - .structurally_normalize_term(term, &mut self.fulfillment_cx) - .unwrap_or(term) + /// Like `fresh_args_for_item()`, but first uses the args from `first`. + pub(crate) fn fill_rest_fresh_args( + &self, + def_id: SolverDefId, + first: impl IntoIterator>, + ) -> GenericArgs<'db> { + self.infer_ctxt.fill_rest_fresh_args(def_id, first) } /// Try to resolve `ty` to a structural type, normalizing aliases. @@ -705,11 +527,8 @@ impl<'db> InferenceTable<'db> { /// In case there is still ambiguity, the returned type may be an inference /// variable. This is different from `structurally_resolve_type` which errors /// in this case. - pub(crate) fn try_structurally_resolve_type( - &mut self, - ty: crate::next_solver::Ty<'db>, - ) -> crate::next_solver::Ty<'db> { - if let crate::next_solver::TyKind::Alias(..) = ty.kind() { + pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Alias(..) = ty.kind() { // We need to use a separate variable here as otherwise the temporary for // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting // in a reentrant borrow, causing an ICE. @@ -719,13 +538,18 @@ impl<'db> InferenceTable<'db> { .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, - Err(_errors) => crate::next_solver::Ty::new_error(self.interner, ErrorGuaranteed), + Err(_errors) => Ty::new_error(self.interner(), ErrorGuaranteed), } } else { self.resolve_vars_with_obligations(ty) } } + pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { + self.try_structurally_resolve_type(ty) + // FIXME: Err if it still contain infer vars. + } + pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); let obligations = self.fulfillment_cx.clone(); @@ -794,7 +618,7 @@ impl<'db> InferenceTable<'db> { self.fulfillment_cx.register_predicate_obligation( &self.infer_ctxt, Obligation::new( - self.interner, + self.interner(), ObligationCause::new(), goal.param_env, goal.predicate, @@ -810,21 +634,11 @@ impl<'db> InferenceTable<'db> { value } - pub(crate) fn register_obligations( - &mut self, - obligations: Vec>>, - ) { - obligations.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); - } - pub(crate) fn select_obligations_where_possible(&mut self) { self.fulfillment_cx.select_where_possible(&self.infer_ctxt); } - pub(super) fn register_predicate( - &mut self, - obligation: crate::next_solver::infer::traits::PredicateObligation<'db>, - ) { + pub(super) fn register_predicate(&mut self, obligation: PredicateObligation<'db>) { if obligation.has_escaping_bound_vars() { panic!("escaping bound vars in predicate {:?}", obligation); } @@ -834,7 +648,7 @@ impl<'db> InferenceTable<'db> { pub(super) fn register_predicates(&mut self, obligations: I) where - I: IntoIterator>, + I: IntoIterator>, { obligations.into_iter().for_each(|obligation| { self.register_predicate(obligation); @@ -843,16 +657,14 @@ impl<'db> InferenceTable<'db> { pub(crate) fn callable_sig( &mut self, - ty: &Ty, + ty: Ty<'db>, num_args: usize, - ) -> Option<(Option, Vec>, crate::next_solver::Ty<'db>)> - { - match ty.callable_sig(self.db) { - Some(sig) => Some(( - None, - sig.params().iter().map(|param| param.to_nextsolver(self.interner)).collect(), - sig.ret().to_nextsolver(self.interner), - )), + ) -> Option<(Option, Vec>, Ty<'db>)> { + match ty.callable_sig(self.interner()) { + Some(sig) => { + let sig = sig.skip_binder(); + Some((None, sig.inputs_and_output.inputs().to_vec(), sig.output())) + } None => { let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?; Some((Some(f), args_ty, return_ty)) @@ -862,9 +674,9 @@ impl<'db> InferenceTable<'db> { fn callable_sig_from_fn_trait( &mut self, - ty: &Ty, + ty: Ty<'db>, num_args: usize, - ) -> Option<(FnTrait, Vec>, next_solver::Ty<'db>)> { + ) -> Option<(FnTrait, Vec>, Ty<'db>)> { for (fn_trait_name, output_assoc_name, subtraits) in [ (FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]), (FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]), @@ -877,8 +689,8 @@ impl<'db> InferenceTable<'db> { trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?; let mut arg_tys = Vec::with_capacity(num_args); - let arg_ty = next_solver::Ty::new_tup_from_iter( - self.interner, + let arg_ty = Ty::new_tup_from_iter( + self.interner(), std::iter::repeat_with(|| { let ty = self.next_ty_var(); arg_tys.push(ty); @@ -886,24 +698,23 @@ impl<'db> InferenceTable<'db> { }) .take(num_args), ); - let args = [ty.to_nextsolver(self.interner), arg_ty]; - let trait_ref = crate::next_solver::TraitRef::new(self.interner, fn_trait.into(), args); + let args = [ty, arg_ty]; + let trait_ref = TraitRef::new(self.interner(), fn_trait.into(), args); - let projection = crate::next_solver::Ty::new_alias( - self.interner, + let projection = Ty::new_alias( + self.interner(), rustc_type_ir::AliasTyKind::Projection, - crate::next_solver::AliasTy::new(self.interner, output_assoc_type.into(), args), + AliasTy::new(self.interner(), output_assoc_type.into(), args), ); - let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner); + let pred = Predicate::upcast_from(trait_ref, self.interner()); if !self.try_obligation(pred).no_solution() { self.register_obligation(pred); let return_ty = self.normalize_alias_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; - let trait_ref = - crate::next_solver::TraitRef::new(self.interner, fn_x_trait.into(), args); - let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner); + let trait_ref = TraitRef::new(self.interner(), fn_x_trait.into(), args); + let pred = Predicate::upcast_from(trait_ref, self.interner()); if !self.try_obligation(pred).no_solution() { return Some((fn_x, arg_tys, return_ty)); } @@ -916,40 +727,53 @@ impl<'db> InferenceTable<'db> { pub(super) fn insert_type_vars(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: TypeFoldable>, { - fold_generic_args( - ty, - |arg, _| match arg { - GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)), - // FIXME: insert lifetime vars once LifetimeData::InferenceVar - // and specific error variant for lifetimes start being constructed - GenericArgData::Lifetime(lt) => GenericArgData::Lifetime(lt), - GenericArgData::Const(c) => { - GenericArgData::Const(self.insert_const_vars_shallow(c)) + struct Folder<'a, 'db> { + table: &'a mut InferenceTable<'db>, + } + impl<'db> TypeFolder> for Folder<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.table.interner() + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + if !ty.references_error() { + return ty; } - }, - DebruijnIndex::INNERMOST, - ) - } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { - match ty.kind(Interner) { - TyKind::Error => self.new_type_var(), - TyKind::InferenceVar(..) => { - let ty_resolved = self.structurally_resolve_type(&ty); - if ty_resolved.is_unknown() { self.new_type_var() } else { ty } + if ty.is_ty_error() { self.table.next_ty_var() } else { ty.super_fold_with(self) } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.references_error() { + return ct; + } + + if ct.is_ct_error() { + self.table.next_const_var() + } else { + ct.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_error() { self.table.next_region_var() } else { r } } - _ => ty, } + + ty.fold_with(&mut Folder { table: self }) + } + + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. + pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { + if ty.is_ty_error() { self.next_ty_var() } else { ty } } /// Whenever you lower a user-written type, you should call this. - pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T + pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + T: TypeFoldable>, { self.process_remote_user_written_ty(ty) // FIXME: Register a well-formed obligation. @@ -957,10 +781,9 @@ impl<'db> InferenceTable<'db> { /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, /// while `process_user_written_ty()` should (but doesn't currently). - pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T + pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + T: TypeFoldable>, { let ty = self.insert_type_vars(ty); // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: @@ -971,44 +794,33 @@ impl<'db> InferenceTable<'db> { } /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { - let data = c.data(Interner); - match &data.value { - ConstValue::Concrete(cc) => match &cc.interned { - crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()), - // try to evaluate unevaluated const. Replace with new var if const eval failed. - crate::ConstScalar::UnevaluatedConst(id, subst) => { - if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { - eval - } else { - self.new_const_var(data.ty.clone()) - } - } - _ => c, - }, - _ => c, - } + pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { + if c.is_ct_error() { self.next_const_var() } else { c } } /// Check if given type is `Sized` or not - pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { - fn short_circuit_trivial_tys(ty: &Ty) -> Option { - match ty.kind(Interner) { - TyKind::Scalar(..) + pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool { + fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option { + match ty.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) | TyKind::Ref(..) - | TyKind::Raw(..) + | TyKind::RawPtr(..) | TyKind::Never | TyKind::FnDef(..) | TyKind::Array(..) - | TyKind::Function(..) => Some(true), - TyKind::Slice(..) | TyKind::Str | TyKind::Dyn(..) => Some(false), + | TyKind::FnPtr(..) => Some(true), + TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false), _ => None, } } - let mut ty = ty.clone(); + let mut ty = ty; ty = self.eagerly_normalize_and_resolve_shallow_in(ty); - if let Some(sized) = short_circuit_trivial_tys(&ty) { + if let Some(sized) = short_circuit_trivial_tys(ty) { return sized; } @@ -1019,9 +831,8 @@ impl<'db> InferenceTable<'db> { while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { let struct_data = id.fields(self.db); if let Some((last_field, _)) = struct_data.fields().iter().next_back() { - let last_field_ty = self.db.field_types(id.into())[last_field] - .clone() - .substitute(Interner, subst); + let last_field_ty = self.db.field_types_ns(id.into())[last_field] + .instantiate(self.interner(), subst); if structs.contains(&ty) { // A struct recursively contains itself as a tail field somewhere. return true; // Don't overload the users with too many errors. @@ -1031,7 +842,7 @@ impl<'db> InferenceTable<'db> { // as unsized by the chalk, so we do this manually. ty = last_field_ty; ty = self.eagerly_normalize_and_resolve_shallow_in(ty); - if let Some(sized) = short_circuit_trivial_tys(&ty) { + if let Some(sized) = short_circuit_trivial_tys(ty) { return sized; } } else { @@ -1044,8 +855,8 @@ impl<'db> InferenceTable<'db> { return false; }; let sized_pred = Predicate::upcast_from( - TraitRef::new(self.interner, sized.into(), [ty.to_nextsolver(self.interner)]), - self.interner, + TraitRef::new(self.interner(), sized.into(), [ty]), + self.interner(), ); self.try_obligation(sized_pred).certain() } @@ -1060,189 +871,14 @@ impl fmt::Debug for InferenceTable<'_> { } } -mod resolve { - use super::InferenceTable; - use crate::{ - Const, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, - VariableKind, next_solver::mapping::NextSolverToChalk, - }; - use chalk_ir::fold::{TypeFoldable, TypeFolder}; - use rustc_type_ir::{FloatVid, IntVid, TyVid}; - - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub(super) enum VarKind { - Ty(TyVariableKind), - Const, - } - - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - pub(super) struct Resolver< - 'a, - 'b, - F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, - > { - pub(super) table: &'a mut InferenceTable<'b>, - pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>, - pub(super) fallback: F, - } - impl TypeFolder for Resolver<'_, '_, F> - where - F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg, - { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn fold_inference_ty( - &mut self, - var: InferenceVar, - kind: TyVariableKind, - outer_binder: DebruijnIndex, - ) -> Ty { - match kind { - TyVariableKind::General => { - let vid = self.table.infer_ctxt.root_var(TyVid::from(var.index())); - let var = InferenceVar::from(vid.as_u32()); - if self.var_stack.contains(&(var, VarKind::Ty(kind))) { - // recursive type - return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone(); - } - if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) { - let known_ty: Ty = known_ty.to_chalk(self.table.interner); - // known_ty may contain other variables that are known by now - self.var_stack.push((var, VarKind::Ty(kind))); - let result = known_ty.fold_with(self, outer_binder); - self.var_stack.pop(); - result - } else { - (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone() - } - } - TyVariableKind::Integer => { - let vid = self - .table - .infer_ctxt - .inner - .borrow_mut() - .int_unification_table() - .find(IntVid::from(var.index())); - let var = InferenceVar::from(vid.as_u32()); - if self.var_stack.contains(&(var, VarKind::Ty(kind))) { - // recursive type - return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone(); - } - if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) { - let known_ty: Ty = known_ty.to_chalk(self.table.interner); - // known_ty may contain other variables that are known by now - self.var_stack.push((var, VarKind::Ty(kind))); - let result = known_ty.fold_with(self, outer_binder); - self.var_stack.pop(); - result - } else { - (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone() - } - } - TyVariableKind::Float => { - let vid = self - .table - .infer_ctxt - .inner - .borrow_mut() - .float_unification_table() - .find(FloatVid::from(var.index())); - let var = InferenceVar::from(vid.as_u32()); - if self.var_stack.contains(&(var, VarKind::Ty(kind))) { - // recursive type - return (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone(); - } - if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) { - let known_ty: Ty = known_ty.to_chalk(self.table.interner); - // known_ty may contain other variables that are known by now - self.var_stack.push((var, VarKind::Ty(kind))); - let result = known_ty.fold_with(self, outer_binder); - self.var_stack.pop(); - result - } else { - (self.fallback)(var, VariableKind::Ty(kind), outer_binder) - .assert_ty_ref(Interner) - .clone() - } - } - } - } - - fn fold_inference_const( - &mut self, - ty: Ty, - var: InferenceVar, - outer_binder: DebruijnIndex, - ) -> Const { - let vid = self - .table - .infer_ctxt - .root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); - let var = InferenceVar::from(vid.as_u32()); - if self.var_stack.contains(&(var, VarKind::Const)) { - // recursive - return (self.fallback)(var, VariableKind::Const(ty), outer_binder) - .assert_const_ref(Interner) - .clone(); - } - if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) { - let known_const: Const = known_const.to_chalk(self.table.interner); - // known_ty may contain other variables that are known by now - self.var_stack.push((var, VarKind::Const)); - let result = known_const.fold_with(self, outer_binder); - self.var_stack.pop(); - result - } else { - (self.fallback)(var, VariableKind::Const(ty), outer_binder) - .assert_const_ref(Interner) - .clone() - } - } - - fn fold_inference_lifetime( - &mut self, - _var: InferenceVar, - _outer_binder: DebruijnIndex, - ) -> Lifetime { - // fall back all lifetimes to 'error -- currently we don't deal - // with any lifetimes, but we can sometimes get some lifetime - // variables through Chalk's unification, and this at least makes - // sure we don't leak them outside of inference - crate::error_lifetime() - } - } -} - mod resolve_completely { - use rustc_type_ir::{ - DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable, - inherent::{Const as _, Ty as _}, - }; + use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable}; - use crate::next_solver::Region; use crate::{ infer::unify::InferenceTable, next_solver::{ - Const, DbInterner, ErrorGuaranteed, Goal, Predicate, Term, Ty, - infer::traits::ObligationCause, + Const, DbInterner, Goal, Predicate, Region, Term, Ty, + infer::{resolve::ReplaceInferWithError, traits::ObligationCause}, normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }, }; @@ -1291,17 +927,17 @@ mod resolve_completely { value }; - value.fold_with(&mut ReplaceInferWithError { interner: self.ctx.interner }) + value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner())) } } impl<'cx, 'db> TypeFolder> for Resolver<'cx, 'db> { fn cx(&self) -> DbInterner<'db> { - self.ctx.interner + self.ctx.interner() } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_var() { Region::error(self.ctx.interner) } else { r } + if r.is_var() { Region::error(self.ctx.interner()) } else { r } } fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { @@ -1320,34 +956,4 @@ mod resolve_completely { predicate.super_fold_with(self) } } - - struct ReplaceInferWithError<'db> { - interner: DbInterner<'db>, - } - - impl<'db> TypeFolder> for ReplaceInferWithError<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { - if t.is_infer() { - Ty::new_error(self.interner, ErrorGuaranteed) - } else { - t.super_fold_with(self) - } - } - - fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { - if c.is_ct_infer() { - Const::new_error(self.interner, ErrorGuaranteed) - } else { - c.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_var() { Region::error(self.interner) } else { r } - } - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 4071b9a1d5e9d..d97d2af080732 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -19,15 +19,13 @@ use rustc_type_ir::{ }; use triomphe::Arc; -use crate::utils::ClosureSubst; use crate::{ - Interner, TraitEnvironment, + TraitEnvironment, consteval_nextsolver::try_const_usize, db::HirDatabase, next_solver::{ DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, convert_args_for_result}, }, }; @@ -325,19 +323,12 @@ pub fn layout_of_ty_query<'db>( TyKind::Closure(id, args) => { let def = db.lookup_intern_closure(id.0); let infer = db.infer(def.0); - let (captures, _) = infer.closure_info(&id.0.into()); + let (captures, _) = infer.closure_info(id.0); let fields = captures .iter() .map(|it| { - let ty = it - .ty - .clone() - .substitute( - Interner, - &ClosureSubst(&convert_args_for_result(interner, args.inner())) - .parent_subst(db), - ) - .to_nextsolver(interner); + let ty = + it.ty.instantiate(interner, args.split_closure_args_untupled().parent_args); db.layout_of_ty(ty, trait_env.clone()) }) .collect::, _>>()?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 6960e230a6850..d6f0ed3dc0752 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -3,7 +3,7 @@ use either::Either; use hir_def::db::DefDatabase; use project_model::{Sysroot, toolchain_info::QueryConfig}; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{GenericArgs as _, Ty as _}; +use rustc_type_ir::inherent::GenericArgs as _; use syntax::ToSmolStr; use test_fixture::WithFixture; use triomphe::Arc; @@ -11,7 +11,7 @@ use triomphe::Arc; use crate::{ db::HirDatabase, layout::{Layout, LayoutError}, - next_solver::{AdtDef, DbInterner, GenericArgs, mapping::ChalkToNextSolver}, + next_solver::{DbInterner, GenericArgs}, setup_tracing, test_db::TestDB, }; @@ -84,7 +84,7 @@ fn eval_goal( let goal_ty = match adt_or_type_alias_id { Either::Left(adt_id) => crate::next_solver::Ty::new_adt( interner, - AdtDef::new(adt_id, interner), + adt_id, GenericArgs::identity_for_item(interner, adt_id.into()), ), Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity(), @@ -133,11 +133,8 @@ fn eval_expr( .unwrap() .0; let infer = db.infer(function_id.into()); - let goal_ty = infer.type_of_binding[b].clone(); - salsa::attach(&db, || { - let interner = DbInterner::new_with(&db, None, None); - db.layout_of_ty(goal_ty.to_nextsolver(interner), db.trait_environment(function_id.into())) - }) + let goal_ty = infer.type_of_binding[b]; + salsa::attach(&db, || db.layout_of_ty(goal_ty, db.trait_environment(function_id.into()))) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 281cf6b2d4b32..2aa9b8fa192d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -64,7 +64,6 @@ use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, }; -use either::Either; use hir_def::{CallableDefId, GeneralConstId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness}; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; @@ -233,7 +232,7 @@ impl ComplexMemoryMap<'_> { } impl<'db> MemoryMap<'db> { - pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError> { + pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError<'db>> { match self { MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)), MemoryMap::Complex(cm) => cm.vtable.ty(id), @@ -249,8 +248,8 @@ impl<'db> MemoryMap<'db> { /// allocator function as `f` and it will return a mapping of old addresses to new addresses. fn transform_addresses( &self, - mut f: impl FnMut(&[u8], usize) -> Result, - ) -> Result, MirEvalError> { + mut f: impl FnMut(&[u8], usize) -> Result>, + ) -> Result, MirEvalError<'db>> { let mut transform = |(addr, val): (&usize, &[u8])| { let addr = *addr; let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) }; @@ -646,7 +645,7 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), // FIXME(next-solver): Should be crate::nextsolver::ImplTraitIdx. TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } @@ -713,102 +712,6 @@ pub(crate) fn fold_free_vars + TypeFoldable< t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST) } -pub(crate) fn fold_tys + TypeFoldable>( - t: T, - mut for_ty: impl FnMut(Ty, DebruijnIndex) -> Ty, - binders: DebruijnIndex, -) -> T { - fold_tys_and_consts( - t, - |x, d| match x { - Either::Left(x) => Either::Left(for_ty(x, d)), - Either::Right(x) => Either::Right(x), - }, - binders, - ) -} - -pub(crate) fn fold_tys_and_consts + TypeFoldable>( - t: T, - f: impl FnMut(Either, DebruijnIndex) -> Either, - binders: DebruijnIndex, -) -> T { - use chalk_ir::fold::{TypeFolder, TypeSuperFoldable}; - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - struct TyFolder, DebruijnIndex) -> Either>(F); - impl, DebruijnIndex) -> Either> TypeFolder - for TyFolder - { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { - let ty = ty.super_fold_with(self.as_dyn(), outer_binder); - self.0(Either::Left(ty), outer_binder).left().unwrap() - } - - fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const { - self.0(Either::Right(c), outer_binder).right().unwrap() - } - } - t.fold_with(&mut TyFolder(f), binders) -} - -pub(crate) fn fold_generic_args + TypeFoldable>( - t: T, - f: impl FnMut(GenericArgData, DebruijnIndex) -> GenericArgData, - binders: DebruijnIndex, -) -> T { - use chalk_ir::fold::{TypeFolder, TypeSuperFoldable}; - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - struct TyFolder GenericArgData>(F); - impl GenericArgData> TypeFolder - for TyFolder - { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { - let ty = ty.super_fold_with(self.as_dyn(), outer_binder); - self.0(GenericArgData::Ty(ty), outer_binder) - .intern(Interner) - .ty(Interner) - .unwrap() - .clone() - } - - fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const { - self.0(GenericArgData::Const(c), outer_binder) - .intern(Interner) - .constant(Interner) - .unwrap() - .clone() - } - - fn fold_lifetime(&mut self, lt: Lifetime, outer_binder: DebruijnIndex) -> Lifetime { - let lt = lt.super_fold_with(self.as_dyn(), outer_binder); - self.0(GenericArgData::Lifetime(lt), outer_binder) - .intern(Interner) - .lifetime(Interner) - .unwrap() - .clone() - } - } - t.fold_with(&mut TyFolder(f), binders) -} - /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also /// ensures there are no unbound variables or inference variables anywhere in /// the `t`. @@ -942,31 +845,31 @@ pub fn callable_sig_from_fn_trait<'db>( // - Self: FnOnce // - >::Output == ?ret_ty let args_ty = table.next_ty_var(); - let args = [self_ty.to_nextsolver(table.interner), args_ty]; - let trait_ref = crate::next_solver::TraitRef::new(table.interner, fn_once_trait.into(), args); + let args = [self_ty.to_nextsolver(table.interner()), args_ty]; + let trait_ref = crate::next_solver::TraitRef::new(table.interner(), fn_once_trait.into(), args); let projection = crate::next_solver::Ty::new_alias( - table.interner, + table.interner(), rustc_type_ir::AliasTyKind::Projection, - crate::next_solver::AliasTy::new(table.interner, output_assoc_type.into(), args), + crate::next_solver::AliasTy::new(table.interner(), output_assoc_type.into(), args), ); - let pred = crate::next_solver::Predicate::upcast_from(trait_ref, table.interner); + let pred = crate::next_solver::Predicate::upcast_from(trait_ref, table.interner()); if !table.try_obligation(pred).no_solution() { table.register_obligation(pred); let return_ty = table.normalize_alias_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; let trait_ref = - crate::next_solver::TraitRef::new(table.interner, fn_x_trait.into(), args); + crate::next_solver::TraitRef::new(table.interner(), fn_x_trait.into(), args); if !table .try_obligation(crate::next_solver::Predicate::upcast_from( trait_ref, - table.interner, + table.interner(), )) .no_solution() { - let ret_ty = table.resolve_completely(return_ty.to_chalk(table.interner)); - let args_ty = table.resolve_completely(args_ty.to_chalk(table.interner)); + let ret_ty = table.resolve_completely(return_ty).to_chalk(table.interner()); + let args_ty = table.resolve_completely(args_ty).to_chalk(table.interner()); let params = args_ty .as_tuple()? .iter(Interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 0c197b27034bf..a9b523a4a6edf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -25,14 +25,14 @@ use chalk_ir::{ use either::Either; use hir_def::{ AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, - TypeOrConstParamId, UnionId, VariantId, + GenericParamId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, + UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, lang_item::LangItem, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, - signatures::{FunctionSignature, TraitFlags}, + signatures::TraitFlags, type_ref::{ ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, TypeBound, TypeRef, TypeRefId, @@ -124,26 +124,6 @@ pub enum LifetimeElisionKind { } impl LifetimeElisionKind { - #[inline] - pub(crate) fn for_const(const_parent: ItemContainerId) -> LifetimeElisionKind { - match const_parent { - ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { - LifetimeElisionKind::Elided(static_lifetime()) - } - ItemContainerId::ImplId(_) => { - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } - } - ItemContainerId::TraitId(_) => { - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } - } - } - } - - #[inline] - pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind { - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } - } - #[inline] pub(crate) fn for_fn_ret() -> LifetimeElisionKind { // FIXME: We should use the elided lifetime here, or `ElisionFailure`. @@ -268,7 +248,7 @@ pub enum ParamLoweringMode { Variable, } -impl<'a> TyLoweringContext<'a> { +impl<'db> TyLoweringContext<'db> { pub fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty { self.lower_ty_ext(type_ref).0 } @@ -512,7 +492,7 @@ impl<'a> TyLoweringContext<'a> { } #[inline] - fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static> { + fn on_path_diagnostic_callback<'a>(type_ref: TypeRefId) -> PathDiagnosticCallback<'a, 'db> { PathDiagnosticCallback { data: Either::Left(PathDiagnosticCallbackData(type_ref)), callback: |data, this, diag| { @@ -523,7 +503,7 @@ impl<'a> TyLoweringContext<'a> { } #[inline] - fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a> { + fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'db> { PathLoweringContext::new( self, Self::on_path_diagnostic_callback(path_id.type_ref()), @@ -559,7 +539,7 @@ impl<'a> TyLoweringContext<'a> { &mut self, path_id: PathId, explicit_self_ty: Ty, - ) -> Option<(TraitRef, PathLoweringContext<'_, 'a>)> { + ) -> Option<(TraitRef, PathLoweringContext<'_, 'db>)> { let mut ctx = self.at_path(path_id); let resolved = match ctx.resolve_path_in_type_ns_fully()? { // FIXME(trait_alias): We need to handle trait alias here. @@ -576,7 +556,7 @@ impl<'a> TyLoweringContext<'a> { &'b mut self, where_predicate: &'b WherePredicate, ignore_bindings: bool, - ) -> impl Iterator + use<'a, 'b> { + ) -> impl Iterator + use<'db, 'b> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -598,7 +578,7 @@ impl<'a> TyLoweringContext<'a> { bound: &'b TypeBound, self_ty: Ty, ignore_bindings: bool, - ) -> impl Iterator + use<'b, 'a> { + ) -> impl Iterator + use<'b, 'db> { let mut assoc_bounds = None; let mut clause = None; match bound { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index da9dd21183e8f..cdac1c9829810 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -3,15 +3,15 @@ use chalk_ir::{BoundVar, cast::Cast, fold::Shift}; use either::Either; use hir_def::{ - GenericDefId, GenericParamId, Lookup, TraitId, + GenericDefId, GenericParamId, TraitId, expr_store::{ - ExpressionStore, HygieneId, + ExpressionStore, path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, }, hir::generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, }, - resolver::{ResolveValueResult, TypeNs, ValueNs}, + resolver::TypeNs, signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; @@ -22,7 +22,7 @@ use crate::{ AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind, Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind, - TyLoweringContext, ValueTyDefId, WhereClause, + TyLoweringContext, WhereClause, consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, error_lifetime, @@ -36,21 +36,22 @@ use crate::{ utils::associated_type_by_name_including_super_traits, }; -type CallbackData<'a> = Either< +type CallbackData<'a, 'db> = Either< super::PathDiagnosticCallbackData, - crate::infer::diagnostics::PathDiagnosticCallbackData<'a>, + crate::infer::diagnostics::PathDiagnosticCallbackData<'a, 'db>, >; // We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box` // because of the allocation, so we create a lifetime-less callback, tailored for our needs. -pub(crate) struct PathDiagnosticCallback<'a> { - pub(crate) data: CallbackData<'a>, - pub(crate) callback: fn(&CallbackData<'_>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic), +pub(crate) struct PathDiagnosticCallback<'a, 'db> { + pub(crate) data: CallbackData<'a, 'db>, + pub(crate) callback: + fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic), } pub(crate) struct PathLoweringContext<'a, 'b> { ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a>, + on_diagnostic: PathDiagnosticCallback<'a, 'b>, path: &'a Path, segments: PathSegments<'a>, current_segment_idx: usize, @@ -62,7 +63,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { #[inline] pub(crate) fn new( ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a>, + on_diagnostic: PathDiagnosticCallback<'a, 'b>, path: &'a Path, ) -> Self { let segments = path.segments(); @@ -109,20 +110,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); } - #[inline] - pub(crate) fn ignore_last_segment(&mut self) { - self.segments = self.segments.strip_last(); - } - - #[inline] - pub(crate) fn set_current_segment(&mut self, segment: usize) { - self.current_segment_idx = segment; - self.current_or_prev_segment = self - .segments - .get(segment) - .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); - } - #[inline] fn with_lifetime_elision( &mut self, @@ -390,103 +377,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { Some((resolution, remaining_index)) } - pub(crate) fn resolve_path_in_value_ns( - &mut self, - hygiene_id: HygieneId, - ) -> Option { - let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( - self.ctx.db, - self.path, - hygiene_id, - )?; - - let segments = self.segments; - if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { - // `segments.is_empty()` can occur with `self`. - return Some(res); - } - - let (mod_segments, enum_segment, resolved_segment_idx) = match res { - ResolveValueResult::Partial(_, unresolved_segment, _) => { - (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) - } - ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) - if prefix_info.enum_variant => - { - (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) - } - ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), - }; - - self.current_segment_idx = resolved_segment_idx; - self.current_or_prev_segment = - segments.get(resolved_segment_idx).expect("should have resolved segment"); - - for (i, mod_segment) in mod_segments.iter().enumerate() { - if mod_segment.args_and_bindings.is_some() { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: i as u32, - reason: GenericArgsProhibitedReason::Module, - }); - } - } - - if let Some(enum_segment) = enum_segment - && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) - && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) - { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: (enum_segment + 1) as u32, - reason: GenericArgsProhibitedReason::EnumVariant, - }); - } - - match &res { - ResolveValueResult::ValueNs(resolution, _) => { - let resolved_segment_idx = self.current_segment_u32(); - let resolved_segment = self.current_or_prev_segment; - - let mut prohibit_generics_on_resolved = |reason| { - if resolved_segment.args_and_bindings.is_some() { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: resolved_segment_idx, - reason, - }); - } - }; - - match resolution { - ValueNs::ImplSelf(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) - } - // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not - // E0109 (generic arguments provided for a type that doesn't accept them) for - // consts and statics, presumably as a defense against future in which consts - // and statics can be generic, or just because it was easier for rustc implementors. - // That means we'll show the wrong error code. Because of us it's easier to do it - // this way :) - ValueNs::GenericParam(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) - } - ValueNs::StaticId(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) - } - ValueNs::LocalBinding(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) - } - ValueNs::FunctionId(_) - | ValueNs::StructId(_) - | ValueNs::EnumVariantId(_) - | ValueNs::ConstId(_) => {} - } - } - ResolveValueResult::Partial(resolution, _, _) => { - self.handle_type_ns_resolution(resolution); - } - }; - Some(res) - } - fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty { let Some(res) = res else { return TyKind::Error.intern(Interner); @@ -556,62 +446,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner) } - /// Collect generic arguments from a path into a `Substs`. See also - /// `create_substs_for_ast_path` and `def_to_ty` in rustc. - pub(crate) fn substs_from_path( - &mut self, - // Note that we don't call `db.value_type(resolved)` here, - // `ValueTyDefId` is just a convenient way to pass generics and - // special-case enum variants - resolved: ValueTyDefId, - infer_args: bool, - lowering_assoc_type_generics: bool, - ) -> Substitution { - let prev_current_segment_idx = self.current_segment_idx; - let prev_current_segment = self.current_or_prev_segment; - - let generic_def = match resolved { - ValueTyDefId::FunctionId(it) => it.into(), - ValueTyDefId::StructId(it) => it.into(), - ValueTyDefId::UnionId(it) => it.into(), - ValueTyDefId::ConstId(it) => it.into(), - ValueTyDefId::StaticId(_) => return Substitution::empty(Interner), - ValueTyDefId::EnumVariantId(var) => { - // the generic args for an enum variant may be either specified - // on the segment referring to the enum, or on the segment - // referring to the variant. So `Option::::None` and - // `Option::None::` are both allowed (though the former is - // FIXME: This isn't strictly correct, enum variants may be used not through the enum - // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result - // available here. The worst that can happen is that we will show some confusing diagnostics to the user, - // if generics exist on the module and they don't match with the variant. - // preferred). See also `def_ids_for_path_segments` in rustc. - // - // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. - // This simplifies the code a bit. - let penultimate_idx = self.current_segment_idx.wrapping_sub(1); - let penultimate = self.segments.get(penultimate_idx); - if let Some(penultimate) = penultimate - && self.current_or_prev_segment.args_and_bindings.is_none() - && penultimate.args_and_bindings.is_some() - { - self.current_segment_idx = penultimate_idx; - self.current_or_prev_segment = penultimate; - } - var.lookup(self.ctx.db).parent.into() - } - }; - let result = self.substs_from_path_segment( - generic_def, - infer_args, - None, - lowering_assoc_type_generics, - ); - self.current_segment_idx = prev_current_segment_idx; - self.current_or_prev_segment = prev_current_segment; - result - } - pub(crate) fn substs_from_path_segment( &mut self, def: GenericDefId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 84cd216b81218..98881004bb9a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -17,6 +17,7 @@ use std::{ use base_db::Crate; use either::Either; +use hir_def::hir::generics::GenericParamDataRef; use hir_def::item_tree::FieldsShape; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, @@ -35,9 +36,9 @@ use hir_def::{ TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; -use hir_def::{ConstId, StaticId}; +use hir_def::{ConstId, LifetimeParamId, StaticId, TypeParamId}; use hir_expand::name::Name; -use intern::sym; +use intern::{Symbol, sym}; use la_arena::{Arena, ArenaMap, Idx}; use path::{PathDiagnosticCallback, PathLoweringContext, builtin}; use rustc_ast_ir::Mutability; @@ -50,12 +51,14 @@ use rustc_type_ir::{ TypeVisitableExt, inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, }; +use rustc_type_ir::{TypeFoldable, TypeFolder, Upcast}; use salsa::plumbing::AsId; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::ValueTyDefId; +use crate::next_solver::ParamConst; use crate::{ FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, @@ -79,11 +82,11 @@ pub struct ImplTraits<'db> { } #[derive(PartialEq, Eq, Debug, Hash)] -pub(crate) struct ImplTrait<'db> { +pub struct ImplTrait<'db> { pub(crate) predicates: Vec>, } -pub(crate) type ImplTraitIdx<'db> = Idx>; +pub type ImplTraitIdx<'db> = Idx>; #[derive(Debug, Default)] struct ImplTraitLoweringState<'db> { @@ -184,6 +187,8 @@ pub(crate) struct TyLoweringContext<'db, 'a> { pub(crate) unsized_types: FxHashSet>, pub(crate) diagnostics: Vec, lifetime_elision: LifetimeElisionKind<'db>, + /// We disallow referencing generic parameters that have an index greater than or equal to this number. + disallow_params_after: u32, } impl<'db, 'a> TyLoweringContext<'db, 'a> { @@ -208,6 +213,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { unsized_types: FxHashSet::default(), diagnostics: Vec::new(), lifetime_elision, + disallow_params_after: u32::MAX, } } @@ -243,6 +249,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self } + pub(crate) fn disallow_params_after(&mut self, after: u32) { + self.disallow_params_after = after; + } + pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); } @@ -265,7 +275,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_ty_ext(type_ref).0 } - pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty<'db>) -> Const<'db> { + pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { let const_ref = &self.store[const_ref.expr]; match const_ref { hir_def::hir::Expr::Path(path) => { @@ -323,6 +333,33 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.generics.get_or_init(|| generics(self.db, self.def)) } + fn type_param(&mut self, id: TypeParamId, index: u32, name: Symbol) -> Ty<'db> { + if index >= self.disallow_params_after { + // FIXME: Report an error. + Ty::new_error(self.interner, ErrorGuaranteed) + } else { + Ty::new_param(self.interner, id, index, name) + } + } + + fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> { + if index >= self.disallow_params_after { + // FIXME: Report an error. + Const::error(self.interner) + } else { + Const::new_param(self.interner, ParamConst { id, index }) + } + } + + fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { + if index >= self.disallow_params_after { + // FIXME: Report an error. + Region::error(self.interner) + } else { + Region::new_early_param(self.interner, EarlyParamRegion { id, index }) + } + } + #[tracing::instrument(skip(self), ret)] pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option) { let interner = self.interner; @@ -351,8 +388,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { TypeOrConstParamData::TypeParamData(ty) => ty, _ => unreachable!(), }; - Ty::new_param( - self.interner, + self.type_param( type_param_id, idx as u32, type_data @@ -367,7 +403,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeRef::Array(array) => { let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(&array.len, Ty::new_usize(interner)); + let const_len = self.lower_const(array.len, Ty::new_usize(interner)); Ty::new_array_with_const_len(interner, inner_ty, const_len) } &TypeRef::Slice(inner) => { @@ -491,7 +527,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } #[inline] - fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static, 'db> { + fn on_path_diagnostic_callback<'b>(type_ref: TypeRefId) -> PathDiagnosticCallback<'b, 'db> { PathDiagnosticCallback { data: Either::Left(PathDiagnosticCallbackData(type_ref)), callback: |data, this, diag| { @@ -515,7 +551,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { if let Some(type_ref) = path.type_anchor() { let (ty, res) = self.lower_ty_ext(type_ref); let mut ctx = self.at_path(path_id); - return ctx.lower_ty_relative_path(ty, res); + return ctx.lower_ty_relative_path(ty, res, false); } let mut ctx = self.at_path(path_id); @@ -545,7 +581,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { TypeNs::TraitId(tr) => tr, _ => return None, }; - Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx)) + Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx)) } fn lower_trait_ref( @@ -869,7 +905,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ImplTrait { predicates } } - pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Region<'db> { + pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { match self.resolver.resolve_lifetime(&self.store[lifetime]) { Some(resolution) => match resolution { LifetimeNs::Static => Region::new_static(self.interner), @@ -878,10 +914,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { None => return Region::error(self.interner), Some(idx) => idx, }; - Region::new_early_param( - self.interner, - EarlyParamRegion { index: idx as u32, id }, - ) + self.region_param(id, idx as u32) } }, None => Region::error(self.interner), @@ -983,7 +1016,7 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind TyDefId::BuiltinType(it) => EarlyBinder::bind(builtin(interner, it)), TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( interner, - AdtDef::new(it, interner), + it, GenericArgs::identity_for_item(interner, it.into()), )), TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, @@ -1748,6 +1781,113 @@ pub(crate) fn lower_generic_arg<'a, 'db, T>( } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericDefaults<'db>( + Option>>]>>, +); + +impl<'db> GenericDefaults<'db> { + #[inline] + pub fn get(&self, idx: usize) -> Option>> { + self.0.as_ref()?[idx] + } +} + +pub(crate) fn generic_defaults_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> GenericDefaults<'_> { + db.generic_defaults_ns_with_diagnostics(def).0 +} + +/// Resolve the default type params from generics. +/// +/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). +pub(crate) fn generic_defaults_with_diagnostics_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> (GenericDefaults<'_>, Diagnostics) { + let generic_params = generics(db, def); + if generic_params.is_empty() { + return (GenericDefaults(None), None); + } + let resolver = def.resolver(db); + + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generic_params.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); + let mut idx = 0; + let mut has_any_default = false; + let mut defaults = generic_params + .iter_parents_with_store() + .map(|((id, p), store)| { + ctx.store = store; + let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); + has_any_default |= has_default; + idx += 1; + result + }) + .collect::>(); + ctx.diagnostics.clear(); // Don't include diagnostics from the parent. + defaults.extend(generic_params.iter_self().map(|(id, p)| { + let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); + has_any_default |= has_default; + idx += 1; + result + })); + let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics)); + let defaults = if has_any_default { + GenericDefaults(Some(Arc::from_iter(defaults))) + } else { + GenericDefaults(None) + }; + return (defaults, diagnostics); + + fn handle_generic_param<'db>( + ctx: &mut TyLoweringContext<'db, '_>, + idx: usize, + id: GenericParamId, + p: GenericParamDataRef<'_>, + generic_params: &Generics, + ) -> (Option>>, bool) { + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden. + ctx.disallow_params_after(idx as u32); + match p { + GenericParamDataRef::TypeParamData(p) => { + let ty = p.default.map(|ty| ctx.lower_ty(ty)); + (ty.map(|ty| EarlyBinder::bind(ty.into())), p.default.is_some()) + } + GenericParamDataRef::ConstParamData(p) => { + let GenericParamId::ConstParamId(id) = id else { + unreachable!("Unexpected lifetime or type argument") + }; + + let mut val = p.default.map(|c| { + let param_ty = ctx.lower_ty(p.ty); + let c = ctx.lower_const(c, param_ty); + c.into() + }); + (val.map(EarlyBinder::bind), p.default.is_some()) + } + GenericParamDataRef::LifetimeParamData(_) => (None, false), + } + } +} + +pub(crate) fn generic_defaults_with_diagnostics_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, +) -> (GenericDefaults<'_>, Diagnostics) { + (GenericDefaults(None), None) +} + /// Build the signature of a callable item (function, struct or enum variant). pub(crate) fn callable_item_signature_query<'db>( db: &'db dyn HirDatabase, @@ -1804,7 +1944,7 @@ fn fn_sig_for_fn<'db>( fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { let interner = DbInterner::new_with(db, None, None); let args = GenericArgs::identity_for_item(interner, adt.into()); - let ty = Ty::new_adt(interner, AdtDef::new(adt, interner), args); + let ty = Ty::new_adt(interner, adt, args); EarlyBinder::bind(ty) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index 0a9f34c9dabd6..babc39694ff1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -51,15 +51,17 @@ use super::{ const_param_ty_query, ty_query, }; -type CallbackData<'a> = - Either>; +type CallbackData<'a, 'db> = Either< + PathDiagnosticCallbackData, + crate::infer::diagnostics::PathDiagnosticCallbackData<'a, 'db>, +>; // We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box` // because of the allocation, so we create a lifetime-less callback, tailored for our needs. pub(crate) struct PathDiagnosticCallback<'a, 'db> { - pub(crate) data: CallbackData<'a>, + pub(crate) data: CallbackData<'a, 'db>, pub(crate) callback: - fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), + fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), } pub(crate) struct PathLoweringContext<'a, 'b, 'db> { @@ -155,13 +157,14 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ty: Ty<'db>, // We need the original resolution to lower `Self::AssocTy` correctly res: Option, + infer_args: bool, ) -> (Ty<'db>, Option) { let remaining_segments = self.segments.len() - self.current_segment_idx; match remaining_segments { 0 => (ty, res), 1 => { // resolve unselected assoc types - (self.select_associated_type(res), None) + (self.select_associated_type(res, infer_args), None) } _ => { // FIXME report error (ambiguous associated type) @@ -204,6 +207,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let trait_ref = self.lower_trait_ref_from_resolved_path( trait_, Ty::new_error(self.ctx.interner, ErrorGuaranteed), + false, ); tracing::debug!(?trait_ref); self.skip_resolved_segment(); @@ -276,8 +280,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { GenericParamDataRef::TypeParamData(p) => p, _ => unreachable!(), }; - Ty::new_param( - self.ctx.interner, + self.ctx.type_param( param_id, idx as u32, p.name @@ -293,7 +296,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { self.ctx.interner, adt.into(), ); - Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), args) + Ty::new_adt(self.ctx.interner, adt, args) } TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), @@ -308,7 +311,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { tracing::debug!(?ty); self.skip_resolved_segment(); - self.lower_ty_relative_path(ty, Some(resolution)) + self.lower_ty_relative_path(ty, Some(resolution), infer_args) } fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { @@ -480,14 +483,19 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // and statics can be generic, or just because it was easier for rustc implementors. // That means we'll show the wrong error code. Because of us it's easier to do it // this way :) - ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + ValueNs::GenericParam(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) } ValueNs::StaticId(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) } - ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} - ValueNs::LocalBinding(_) => {} + ValueNs::LocalBinding(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) + } + ValueNs::FunctionId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + | ValueNs::ConstId(_) => {} } } ResolveValueResult::Partial(resolution, _, _) => { @@ -498,7 +506,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } #[tracing::instrument(skip(self), ret)] - fn select_associated_type(&mut self, res: Option) -> Ty<'db> { + fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty<'db> { let interner = self.ctx.interner; let Some(res) = res else { return Ty::new_error(self.ctx.interner, ErrorGuaranteed); @@ -516,7 +524,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // generic params. It's inefficient to splice the `Substitution`s, so we may want // that method to optionally take parent `Substitution` as we already know them at // this point (`t.substitution`). - let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true); + let substs = + self.substs_from_path_segment(associated_ty.into(), infer_args, None, true); let substs = crate::next_solver::GenericArgs::new_from_iter( interner, @@ -715,12 +724,12 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { param: GenericParamDataRef<'_>, arg: &GenericArg, ) -> crate::next_solver::GenericArg<'db> { - match (param, arg) { + match (param, *arg) { (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { - self.ctx.ctx.lower_lifetime(*lifetime).into() + self.ctx.ctx.lower_lifetime(lifetime).into() } (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { - self.ctx.ctx.lower_ty(*type_ref).into() + self.ctx.ctx.lower_ty(type_ref).into() } (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { let GenericParamId::ConstParamId(const_id) = param_id else { @@ -859,8 +868,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { &mut self, resolved: TraitId, explicit_self_ty: Ty<'db>, + infer_args: bool, ) -> TraitRef<'db> { - let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty); + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) } @@ -868,8 +878,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { &mut self, resolved: TraitId, explicit_self_ty: Ty<'db>, + infer_args: bool, ) -> crate::next_solver::GenericArgs<'db> { - self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) + self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false) } pub(super) fn assoc_type_bindings_from_type_bound<'c>( @@ -1039,8 +1050,12 @@ fn check_generic_args_len<'db>( } let lifetime_args_len = def_generics.len_lifetimes_self(); - if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics { - // In generic associated types, we never allow inferring the lifetimes. + if provided_lifetimes_count == 0 + && lifetime_args_len > 0 + && (!lowering_assoc_type_generics || infer_args) + { + // In generic associated types, we never allow inferring the lifetimes, but only in type context, that is + // when `infer_args == false`. In expression/pattern context we always allow inferring them, even for GATs. match lifetime_elision { &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 61d3091a0c1d0..086abc95917ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -8,39 +8,42 @@ use arrayvec::ArrayVec; use base_db::Crate; use chalk_ir::{UniverseIndex, WithKind, cast::Cast}; use hir_def::{ - AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, - ModuleId, TraitId, + AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, + ModuleId, TraitId, TypeAliasId, nameres::{DefMap, assoc::ImplItems, block_def_map, crate_def_map}, signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags}, }; use hir_expand::name::Name; use intern::sym; +use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _}; +use rustc_type_ir::{ + FloatTy, IntTy, UintTy, + inherent::{ + AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _, + }, +}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + CanonicalVarKinds, DebruijnIndex, GenericArgData, InEnvironment, Interner, TraitEnvironment, + TyBuilder, VariableKind, autoderef::{self, AutoderefKind}, db::HirDatabase, - from_chalk_trait_id, from_foreign_def_id, infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, lang_items::is_box, next_solver::{ - self, DbInterner, SolverDefId, + Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId, + TraitRef, Ty, TyKind, infer::{ DefineOpaqueTypes, traits::{ObligationCause, PredicateObligation}, }, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + mapping::NextSolverToChalk, obligation_ctxt::ObligationCtxt, }, - primitive::{FloatTy, IntTy, UintTy}, - to_chalk_trait_id, traits::next_trait_solve_canonical_in_ctxt, utils::all_super_traits, }; @@ -55,11 +58,15 @@ pub enum TyFingerprint { Never, Ref(Mutability), RawPtr(Mutability), - Scalar(Scalar), + Bool, + Char, + Int(IntTy), + Uint(UintTy), + Float(FloatTy), // These can have user-defined impls: Adt(hir_def::AdtId), Dyn(TraitId), - ForeignType(ForeignDefId), + ForeignType(TypeAliasId), // These only exist for trait impls Unit, Unnameable, @@ -71,140 +78,73 @@ impl TyFingerprint { /// types can have inherent impls: if we have some `struct S`, we can have /// an `impl S`, but not `impl &S`. Hence, this will return `None` for /// reference types and such. - pub fn for_inherent_impl(ty: &Ty) -> Option { - let fp = match ty.kind(Interner) { + pub fn for_inherent_impl<'db>(ty: Ty<'db>) -> Option { + let fp = match ty.kind() { TyKind::Str => TyFingerprint::Str, TyKind::Never => TyFingerprint::Never, TyKind::Slice(..) => TyFingerprint::Slice, TyKind::Array(..) => TyFingerprint::Array, - TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar), - TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), - TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), - TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), - TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, + TyKind::Bool => TyFingerprint::Bool, + TyKind::Char => TyFingerprint::Char, + TyKind::Int(int) => TyFingerprint::Int(int), + TyKind::Uint(int) => TyFingerprint::Uint(int), + TyKind::Float(float) => TyFingerprint::Float(float), + TyKind::Adt(adt_def, _) => TyFingerprint::Adt(adt_def.def_id().0), + TyKind::RawPtr(_, mutability) => TyFingerprint::RawPtr(mutability), + TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(alias_id.0), + TyKind::Dynamic(bounds, _) => { + bounds.principal_def_id().map(|trait_| TyFingerprint::Dyn(trait_.0))? + } _ => return None, }; Some(fp) } /// Creates a TyFingerprint for looking up a trait impl. - pub fn for_trait_impl(ty: &Ty) -> Option { - let fp = match ty.kind(Interner) { + pub fn for_trait_impl<'db>(ty: Ty<'db>) -> Option { + let fp = match ty.kind() { TyKind::Str => TyFingerprint::Str, TyKind::Never => TyFingerprint::Never, TyKind::Slice(..) => TyFingerprint::Slice, TyKind::Array(..) => TyFingerprint::Array, - TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar), - TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), - TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), - TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), - TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, - TyKind::Ref(mutability, _, _) => TyFingerprint::Ref(*mutability), - TyKind::Tuple(_, subst) => { - let first_ty = subst.interned().first().map(|arg| arg.assert_ty_ref(Interner)); + TyKind::Bool => TyFingerprint::Bool, + TyKind::Char => TyFingerprint::Char, + TyKind::Int(int) => TyFingerprint::Int(int), + TyKind::Uint(int) => TyFingerprint::Uint(int), + TyKind::Float(float) => TyFingerprint::Float(float), + TyKind::Adt(adt_def, _) => TyFingerprint::Adt(adt_def.def_id().0), + TyKind::RawPtr(_, mutability) => TyFingerprint::RawPtr(mutability), + TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(alias_id.0), + TyKind::Dynamic(bounds, _) => { + bounds.principal_def_id().map(|trait_| TyFingerprint::Dyn(trait_.0))? + } + TyKind::Ref(_, _, mutability) => TyFingerprint::Ref(mutability), + TyKind::Tuple(subst) => { + let first_ty = subst.as_slice().first(); match first_ty { - Some(ty) => return TyFingerprint::for_trait_impl(ty), + Some(ty) => return TyFingerprint::for_trait_impl(*ty), None => TyFingerprint::Unit, } } - TyKind::AssociatedType(_, _) // FIXME(next-solver): Putting `Alias` here is *probably* incorrect, AFAIK it should return `None`. But this breaks // flyimport, which uses an incorrect but fast method resolution algorithm. Therefore we put it here, // because this function is only called by flyimport, and anyway we should get rid of `TyFingerprint` // and switch to `rustc_type_ir`'s `SimplifiedType`. - | TyKind::Alias(_) - | TyKind::OpaqueType(_, _) + TyKind::Alias(..) | TyKind::FnDef(_, _) | TyKind::Closure(_, _) | TyKind::Coroutine(..) + | TyKind::CoroutineClosure(..) | TyKind::CoroutineWitness(..) => TyFingerprint::Unnameable, - TyKind::Function(fn_ptr) => { - TyFingerprint::Function(fn_ptr.substitution.0.len(Interner) as u32) - } - TyKind::Placeholder(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) - | TyKind::Error => return None, - }; - Some(fp) - } - - /// Creates a TyFingerprint for looking up a trait impl. - pub fn for_trait_impl_ns<'db>(ty: &next_solver::Ty<'db>) -> Option { - use rustc_type_ir::TyKind; - let fp = match (*ty).kind() { - TyKind::Str => TyFingerprint::Str, - TyKind::Never => TyFingerprint::Never, - TyKind::Slice(..) => TyFingerprint::Slice, - TyKind::Array(..) => TyFingerprint::Array, - TyKind::Int(int) => TyFingerprint::Scalar(Scalar::Int(match int { - rustc_type_ir::IntTy::Isize => IntTy::Isize, - rustc_type_ir::IntTy::I8 => IntTy::I8, - rustc_type_ir::IntTy::I16 => IntTy::I16, - rustc_type_ir::IntTy::I32 => IntTy::I32, - rustc_type_ir::IntTy::I64 => IntTy::I64, - rustc_type_ir::IntTy::I128 => IntTy::I128, - })), - TyKind::Uint(uint) => TyFingerprint::Scalar(Scalar::Uint(match uint { - rustc_type_ir::UintTy::Usize => UintTy::Usize, - rustc_type_ir::UintTy::U8 => UintTy::U8, - rustc_type_ir::UintTy::U16 => UintTy::U16, - rustc_type_ir::UintTy::U32 => UintTy::U32, - rustc_type_ir::UintTy::U64 => UintTy::U64, - rustc_type_ir::UintTy::U128 => UintTy::U128, - })), - TyKind::Float(float) => TyFingerprint::Scalar(Scalar::Float(match float { - rustc_type_ir::FloatTy::F16 => FloatTy::F16, - rustc_type_ir::FloatTy::F32 => FloatTy::F32, - rustc_type_ir::FloatTy::F64 => FloatTy::F64, - rustc_type_ir::FloatTy::F128 => FloatTy::F128, - })), - TyKind::Bool => TyFingerprint::Scalar(Scalar::Bool), - TyKind::Char => TyFingerprint::Scalar(Scalar::Char), - TyKind::Adt(def, _) => TyFingerprint::Adt(def.inner().id), - TyKind::RawPtr(.., mutability) => match mutability { - rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), - rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), - }, - TyKind::Foreign(def) => TyFingerprint::ForeignType(crate::to_foreign_def_id(def.0)), - TyKind::Dynamic(bounds, _) => { - let trait_ref = bounds - .as_slice() - .iter() - .map(|b| (*b).skip_binder()) - .filter_map(|b| match b { - rustc_type_ir::ExistentialPredicate::Trait(t) => Some(t.def_id), - _ => None, - }) - .next()?; - TyFingerprint::Dyn(trait_ref.0) - } - TyKind::Ref(_, _, mutability) => match mutability { - rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), - rustc_ast_ir::Mutability::Not => TyFingerprint::Ref(Mutability::Not), - }, - TyKind::Tuple(tys) => { - let first_ty = tys.as_slice().iter().next(); - match first_ty { - Some(ty) => return TyFingerprint::for_trait_impl_ns(ty), - None => TyFingerprint::Unit, - } - } - TyKind::FnDef(_, _) - | TyKind::Closure(_, _) - | TyKind::Coroutine(..) - | TyKind::CoroutineWitness(..) - | TyKind::Pat(..) - | TyKind::CoroutineClosure(..) => TyFingerprint::Unnameable, TyKind::FnPtr(sig, _) => { - TyFingerprint::Function(sig.inputs().skip_binder().len() as u32) + TyFingerprint::Function(sig.skip_binder().inputs_and_output.inner().len() as u32) } - TyKind::Alias(..) - | TyKind::Placeholder(_) + TyKind::Param(_) | TyKind::Bound(..) + | TyKind::Placeholder(..) | TyKind::Infer(_) | TyKind::Error(_) - | TyKind::Param(..) + | TyKind::Pat(..) | TyKind::UnsafeBinder(..) => return None, }; Some(fp) @@ -212,25 +152,25 @@ impl TyFingerprint { } pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ - TyFingerprint::Scalar(Scalar::Int(IntTy::I8)), - TyFingerprint::Scalar(Scalar::Int(IntTy::I16)), - TyFingerprint::Scalar(Scalar::Int(IntTy::I32)), - TyFingerprint::Scalar(Scalar::Int(IntTy::I64)), - TyFingerprint::Scalar(Scalar::Int(IntTy::I128)), - TyFingerprint::Scalar(Scalar::Int(IntTy::Isize)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::U8)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::U16)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::U32)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::U64)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::U128)), - TyFingerprint::Scalar(Scalar::Uint(UintTy::Usize)), + TyFingerprint::Int(IntTy::I8), + TyFingerprint::Int(IntTy::I16), + TyFingerprint::Int(IntTy::I32), + TyFingerprint::Int(IntTy::I64), + TyFingerprint::Int(IntTy::I128), + TyFingerprint::Int(IntTy::Isize), + TyFingerprint::Uint(UintTy::U8), + TyFingerprint::Uint(UintTy::U16), + TyFingerprint::Uint(UintTy::U32), + TyFingerprint::Uint(UintTy::U64), + TyFingerprint::Uint(UintTy::U128), + TyFingerprint::Uint(UintTy::Usize), ]; pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 4] = [ - TyFingerprint::Scalar(Scalar::Float(FloatTy::F16)), - TyFingerprint::Scalar(Scalar::Float(FloatTy::F32)), - TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)), - TyFingerprint::Scalar(Scalar::Float(FloatTy::F128)), + TyFingerprint::Float(FloatTy::F16), + TyFingerprint::Float(FloatTy::F32), + TyFingerprint::Float(FloatTy::F64), + TyFingerprint::Float(FloatTy::F128), ]; type TraitFpMap = FxHashMap, Box<[ImplId]>>>; @@ -300,9 +240,8 @@ impl TraitImpls { Some(tr) => tr.skip_binder().def_id.0, None => continue, }; - let interner = DbInterner::new_with(db, None, None); - let self_ty = db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner); - let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty); + let self_ty = db.impl_self_ty(impl_id); + let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.instantiate_identity()); map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id); } @@ -415,8 +354,8 @@ impl InherentImpls { continue; } - let interner = DbInterner::new_with(db, None, None); - let self_ty = &db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner); + let self_ty = db.impl_self_ty(impl_id); + let self_ty = self_ty.instantiate_identity(); match is_inherent_impl_coherent(db, def_map, impl_id, self_ty) { true => { @@ -440,7 +379,7 @@ impl InherentImpls { } } - pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { + pub fn for_self_ty<'db>(&self, self_ty: Ty<'db>) -> &[ImplId] { match TyFingerprint::for_inherent_impl(self_ty) { Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]), None => &[], @@ -476,9 +415,14 @@ pub(crate) fn incoherent_inherent_impl_crates( res } -pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option> { - match ty.kind(Interner) { - &TyKind::Adt(AdtId(def_id), _) => { +pub fn def_crates<'db>( + db: &'db dyn HirDatabase, + ty: Ty<'db>, + cur_crate: Crate, +) -> Option> { + match ty.kind() { + TyKind::Adt(adt_def, _) => { + let def_id = adt_def.def_id().0; let rustc_has_incoherent_inherent_impls = match def_id { hir_def::AdtId::StructId(id) => db .struct_signature(id) @@ -499,22 +443,22 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option { - let alias = from_foreign_def_id(id); + TyKind::Foreign(alias) => { + let alias = alias.0; Some( if db .type_alias_signature(alias) .flags .contains(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL) { - db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id)) + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(alias)) } else { smallvec![alias.module(db).krate()] }, ) } - TyKind::Dyn(_) => { - let trait_id = ty.dyn_trait()?; + TyKind::Dynamic(bounds, _) => { + let trait_id = bounds.principal_def_id()?.0; Some( if db .trait_signature(trait_id) @@ -530,11 +474,15 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option Some(db.incoherent_inherent_impl_crates( + | TyKind::RawPtr(..) => Some(db.incoherent_inherent_impl_crates( cur_crate, TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"), )), @@ -545,7 +493,7 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option( db: &'db dyn HirDatabase, - ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &Canonical<'db, Ty<'db>>, env: Arc>, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -626,23 +574,23 @@ pub struct ReceiverAdjustments { } impl ReceiverAdjustments { - pub(crate) fn apply( + pub(crate) fn apply<'db>( &self, - table: &mut InferenceTable<'_>, - mut ty: Ty, - ) -> (Ty, Vec) { + table: &mut InferenceTable<'db>, + mut ty: Ty<'db>, + ) -> (Ty<'db>, Vec>) { let mut adjust = Vec::new(); - let mut autoderef = table.autoderef(ty.to_nextsolver(table.interner)); + let mut autoderef = table.autoderef(ty); autoderef.next(); for _ in 0..self.autoderefs { match autoderef.next() { None => { never!("autoderef not possible for {:?}", ty); - ty = TyKind::Error.intern(Interner); + ty = Ty::new_error(table.interner(), ErrorGuaranteed); break; } Some((new_ty, _)) => { - ty = new_ty.to_chalk(autoderef.table.interner); + ty = new_ty; let mutbl = match self.autoref { Some(AutorefOrPtrAdjustment::Autoref(m)) => Some(m), Some(AutorefOrPtrAdjustment::ToConstPtr) => Some(Mutability::Not), @@ -654,26 +602,26 @@ impl ReceiverAdjustments { AutoderefKind::Overloaded => Some(OverloadedDeref(mutbl)), AutoderefKind::Builtin => None, }), - target: ty.clone(), + target: ty, }); } } } if let Some(autoref) = &self.autoref { - let lt = table.new_lifetime_var(); + let lt = table.next_region_var(); match autoref { AutorefOrPtrAdjustment::Autoref(m) => { - let a = Adjustment::borrow(*m, ty, lt); - ty = a.target.clone(); + let a = Adjustment::borrow(table.interner(), *m, ty, lt); + ty = a.target; adjust.push(a); } AutorefOrPtrAdjustment::ToConstPtr => { - if let TyKind::Raw(Mutability::Mut, pointee) = ty.kind(Interner) { + if let TyKind::RawPtr(pointee, Mutability::Mut) = ty.kind() { let a = Adjustment { kind: Adjust::Pointer(PointerCast::MutToConstPointer), - target: TyKind::Raw(Mutability::Not, pointee.clone()).intern(Interner), + target: Ty::new_ptr(table.interner(), pointee, Mutability::Not), }; - ty = a.target.clone(); + ty = a.target; adjust.push(a); } else { never!("`ToConstPtr` target is not a raw mutable pointer"); @@ -683,23 +631,20 @@ impl ReceiverAdjustments { } if self.unsize_array { ty = 'it: { - if let TyKind::Ref(m, l, inner) = ty.kind(Interner) - && let TyKind::Array(inner, _) = inner.kind(Interner) + if let TyKind::Ref(l, inner, m) = ty.kind() + && let TyKind::Array(inner, _) = inner.kind() { - break 'it TyKind::Ref( - *m, - l.clone(), - TyKind::Slice(inner.clone()).intern(Interner), - ) - .intern(Interner); + break 'it Ty::new_ref( + table.interner(), + l, + Ty::new_slice(table.interner(), inner), + m, + ); } // FIXME: report diagnostic if array unsizing happens without indirection. ty }; - adjust.push(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), - target: ty.clone(), - }); + adjust.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: ty }); } (ty, adjust) } @@ -713,7 +658,7 @@ impl ReceiverAdjustments { // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? pub(crate) fn iterate_method_candidates<'db, T>( - ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &Canonical<'db, Ty<'db>>, db: &'db dyn HirDatabase, env: Arc>, traits_in_scope: &FxHashSet, @@ -744,17 +689,18 @@ pub(crate) fn iterate_method_candidates<'db, T>( } pub fn lookup_impl_const<'db>( - db: &'db dyn HirDatabase, + interner: DbInterner<'db>, env: Arc>, const_id: ConstId, - subs: Substitution, -) -> (ConstId, Substitution) { + subs: GenericArgs<'db>, +) -> (ConstId, GenericArgs<'db>) { + let db = interner.db; + let trait_id = match const_id.lookup(db).container { ItemContainerId::TraitId(id) => id, _ => return (const_id, subs), }; - let substitution = Substitution::from_iter(Interner, subs.iter(Interner)); - let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution }; + let trait_ref = TraitRef::new(interner, trait_id.into(), subs); let const_signature = db.const_signature(const_id); let name = match const_signature.name.as_ref() { @@ -772,37 +718,30 @@ pub fn lookup_impl_const<'db>( /// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should /// call the method using the vtable. pub fn is_dyn_method<'db>( - db: &'db dyn HirDatabase, + interner: DbInterner<'db>, _env: Arc>, func: FunctionId, - fn_subst: Substitution, + fn_subst: GenericArgs<'db>, ) -> Option { + let db = interner.db; + let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else { return None; }; let trait_params = db.generic_params(trait_id.into()).len(); - let fn_params = fn_subst.len(Interner) - trait_params; - let trait_ref = TraitRef { - trait_id: to_chalk_trait_id(trait_id), - substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).take(trait_params)), - }; - let self_ty = trait_ref.self_type_parameter(Interner); - if let TyKind::Dyn(d) = self_ty.kind(Interner) { + let fn_params = fn_subst.len() - trait_params; + let trait_ref = TraitRef::new( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, fn_subst.iter().take(trait_params)), + ); + let self_ty = trait_ref.self_ty(); + if let TyKind::Dynamic(d, _) = self_ty.kind() { + // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter + // what the generics are, we are sure that the method is come from the vtable. let is_my_trait_in_bounds = d - .bounds - .skip_binders() - .as_slice(Interner) - .iter() - .map(|it| it.skip_binders()) - .flat_map(|it| match it { - WhereClause::Implemented(tr) => { - all_super_traits(db, from_chalk_trait_id(tr.trait_id)) - } - _ => smallvec![], - }) - // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter - // what the generics are, we are sure that the method is come from the vtable. - .any(|x| x == trait_id); + .principal_def_id() + .is_some_and(|trait_| all_super_traits(db, trait_.0).contains(&trait_id)); if is_my_trait_in_bounds { return Some(fn_params); } @@ -817,16 +756,19 @@ pub(crate) fn lookup_impl_method_query<'db>( db: &'db dyn HirDatabase, env: Arc>, func: FunctionId, - fn_subst: Substitution, -) -> (FunctionId, Substitution) { + fn_subst: GenericArgs<'db>, +) -> (FunctionId, GenericArgs<'db>) { + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else { return (func, fn_subst); }; let trait_params = db.generic_params(trait_id.into()).len(); - let trait_ref = TraitRef { - trait_id: to_chalk_trait_id(trait_id), - substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).take(trait_params)), - }; + let trait_ref = TraitRef::new( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, fn_subst.iter().take(trait_params)), + ); let name = &db.function_signature(func).name; let Some((impl_fn, impl_subst)) = @@ -839,28 +781,28 @@ pub(crate) fn lookup_impl_method_query<'db>( ( impl_fn, - Substitution::from_iter( - Interner, - impl_subst.iter(Interner).chain(fn_subst.iter(Interner).skip(trait_params)), + GenericArgs::new_from_iter( + interner, + impl_subst.iter().chain(fn_subst.iter().skip(trait_params)), ), ) } fn lookup_impl_assoc_item_for_trait_ref<'db>( - trait_ref: TraitRef, + trait_ref: TraitRef<'db>, db: &'db dyn HirDatabase, env: Arc>, name: &Name, -) -> Option<(AssocItemId, Substitution)> { - let hir_trait_id = trait_ref.hir_trait_id(); - let self_ty = trait_ref.self_type_parameter(Interner); - let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; +) -> Option<(AssocItemId, GenericArgs<'db>)> { + let hir_trait_id = trait_ref.def_id.0; + let self_ty = trait_ref.self_ty(); + let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?; let impls = db.trait_impls_in_deps(env.krate); let trait_module = hir_trait_id.module(db); let type_module = match self_ty_fp { TyFingerprint::Adt(adt_id) => Some(adt_id.module(db)), - TyFingerprint::ForeignType(type_id) => Some(from_foreign_def_id(type_id).module(db)), + TyFingerprint::ForeignType(type_id) => Some(type_id.module(db)), TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db)), _ => None, }; @@ -888,84 +830,85 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>( Some((item, impl_subst)) } -fn find_matching_impl( +fn find_matching_impl<'db>( mut impls: impl Iterator, - mut table: InferenceTable<'_>, - actual_trait_ref: TraitRef, -) -> Option<(&ImplItems, Substitution)> { + mut table: InferenceTable<'db>, + actual_trait_ref: TraitRef<'db>, +) -> Option<(&'db ImplItems, GenericArgs<'db>)> { let db = table.db; impls.find_map(|impl_| { table.run_in_snapshot(|table| { - let impl_substs = - TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build(); - let args: crate::next_solver::GenericArgs<'_> = - impl_substs.to_nextsolver(table.interner); + let impl_substs = table.fresh_args_for_item(impl_.into()); let trait_ref = db .impl_trait(impl_) .expect("non-trait method in find_matching_impl") - .instantiate(table.interner, args) - .to_chalk(table.interner); + .instantiate(table.interner(), impl_substs); - if !table.unify(&trait_ref, &actual_trait_ref) { + if !table.unify(trait_ref, actual_trait_ref) { return None; } - let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs) - .into_iter() - .map(|b| -> Goal { b.cast(Interner) }); - for goal in wcs { - let goal = goal.to_nextsolver(table.interner); - if table.try_obligation(goal).no_solution() { - return None; + if let Some(predicates) = + db.generic_predicates_ns(impl_.into()).instantiate(table.interner(), impl_substs) + { + for predicate in predicates { + if table.try_obligation(predicate.0).no_solution() { + return None; + } + table.register_obligation(predicate.0); } - table.register_obligation(goal); } - Some(( - impl_.impl_items(db), - table.resolve_completely::<_, crate::next_solver::GenericArgs<'_>>(impl_substs), - )) + Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) }) } -fn is_inherent_impl_coherent( - db: &dyn HirDatabase, +fn is_inherent_impl_coherent<'db>( + db: &'db dyn HirDatabase, def_map: &DefMap, impl_id: ImplId, - self_ty: &Ty, + self_ty: Ty<'db>, ) -> bool { - let self_ty = self_ty.kind(Interner); + let self_ty = self_ty.kind(); let impl_allowed = match self_ty { - TyKind::Tuple(_, _) + TyKind::Tuple(_) | TyKind::FnDef(_, _) | TyKind::Array(_, _) | TyKind::Never - | TyKind::Raw(_, _) + | TyKind::RawPtr(_, _) | TyKind::Ref(_, _, _) | TyKind::Slice(_) | TyKind::Str - | TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(), + | TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(), - &TyKind::Adt(AdtId(adt), _) => adt.module(db).krate() == def_map.krate(), - TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { - from_chalk_trait_id(trait_id).module(db).krate() == def_map.krate() - }), + TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate() == def_map.krate(), + TyKind::Dynamic(it, _) => it + .principal_def_id() + .is_some_and(|trait_id| trait_id.0.module(db).krate() == def_map.krate()), _ => true, }; impl_allowed || { let rustc_has_incoherent_inherent_impls = match self_ty { - TyKind::Tuple(_, _) + TyKind::Tuple(_) | TyKind::FnDef(_, _) | TyKind::Array(_, _) | TyKind::Never - | TyKind::Raw(_, _) + | TyKind::RawPtr(_, _) | TyKind::Ref(_, _, _) | TyKind::Slice(_) | TyKind::Str - | TyKind::Scalar(_) => true, + | TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) => true, - &TyKind::Adt(AdtId(adt), _) => match adt { + TyKind::Adt(adt_def, _) => match adt_def.def_id().0 { hir_def::AdtId::StructId(id) => db .struct_signature(id) .flags @@ -979,8 +922,8 @@ fn is_inherent_impl_coherent( .flags .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS), }, - TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { - db.trait_signature(from_chalk_trait_id(trait_id)) + TyKind::Dynamic(it, _) => it.principal_def_id().is_some_and(|trait_id| { + db.trait_signature(trait_id.0) .flags .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) }), @@ -1012,8 +955,7 @@ fn is_inherent_impl_coherent( /// - All of /// - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. /// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) -pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { - let substs = TyBuilder::placeholder_subst(db, impl_); +pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool { let Some(impl_trait) = db.impl_trait(impl_) else { // not a trait impl return true; @@ -1022,24 +964,25 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { let local_crate = impl_.lookup(db).container.krate(); let is_local = |tgt_crate| tgt_crate == local_crate; - let interner = DbInterner::new_with(db, None, None); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let trait_ref = impl_trait.instantiate(interner, args).to_chalk(interner); - let trait_id = from_chalk_trait_id(trait_ref.trait_id); + let trait_ref = impl_trait.instantiate_identity(); + let trait_id = trait_ref.def_id.0; if is_local(trait_id.module(db).krate()) { // trait to be implemented is local return true; } - let unwrap_fundamental = |mut ty: Ty| { + let unwrap_fundamental = |mut ty: Ty<'db>| { // Unwrap all layers of fundamental types with a loop. loop { - match ty.kind(Interner) { - TyKind::Ref(_, _, referenced) => ty = referenced.clone(), - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => { + match ty.kind() { + TyKind::Ref(_, referenced, _) => ty = referenced, + TyKind::Adt(adt_def, subs) => { + let AdtId::StructId(s) = adt_def.def_id().0 else { + break ty; + }; let struct_signature = db.struct_signature(s); if struct_signature.flags.contains(StructFlags::FUNDAMENTAL) { - let next = subs.type_parameters(Interner).next(); + let next = subs.types().next(); match next { Some(it) => ty = it, None => break ty, @@ -1056,22 +999,20 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { // FIXME: param coverage // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) - let is_not_orphan = trait_ref.substitution.type_parameters(Interner).any(|ty| { - match unwrap_fundamental(ty).kind(Interner) { - &TyKind::Adt(AdtId(id), _) => is_local(id.module(db).krate()), - TyKind::Error => true, - TyKind::Dyn(it) => it - .principal_id() - .is_some_and(|trait_id| is_local(from_chalk_trait_id(trait_id).module(db).krate())), - _ => false, + let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() { + TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate()), + TyKind::Error(_) => true, + TyKind::Dynamic(it, _) => { + it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate())) } + _ => false, }); #[allow(clippy::let_and_return)] is_not_orphan } pub fn iterate_path_candidates<'db>( - ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &Canonical<'db, Ty<'db>>, db: &'db dyn HirDatabase, env: Arc>, traits_in_scope: &FxHashSet, @@ -1093,7 +1034,7 @@ pub fn iterate_path_candidates<'db>( } pub fn iterate_method_candidates_dyn<'db>( - ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &Canonical<'db, Ty<'db>>, db: &'db dyn HirDatabase, env: Arc>, traits_in_scope: &FxHashSet, @@ -1132,7 +1073,7 @@ pub fn iterate_method_candidates_dyn<'db>( // types*. let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical_ns(*ty); + let ty = table.instantiate_canonical(*ty); let deref_chain = autoderef_method_receiver(&mut table, ty); deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { @@ -1165,14 +1106,14 @@ pub fn iterate_method_candidates_dyn<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_with_autoref<'db>( table: &mut InferenceTable<'db>, - receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: Canonical<'db, Ty<'db>>, first_adjustment: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - let interner = table.interner; + let interner = table.interner(); let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { iterate_method_candidates_by_receiver( @@ -1187,11 +1128,7 @@ fn iterate_method_candidates_with_autoref<'db>( }; let mut maybe_reborrowed = first_adjustment.clone(); - if let rustc_type_ir::TyKind::Ref(_, _, m) = receiver_ty.value.kind() { - let m = match m { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; + if let TyKind::Ref(_, _, m) = receiver_ty.value.kind() { // Prefer reborrow of references to move maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m)); maybe_reborrowed.autoderefs += 1; @@ -1199,15 +1136,10 @@ fn iterate_method_candidates_with_autoref<'db>( iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; - let refed = next_solver::Canonical { + let refed = Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: next_solver::Ty::new_ref( - interner, - next_solver::Region::error(interner), - receiver_ty.value, - rustc_ast_ir::Mutability::Not, - ), + value: Ty::new_ref(interner, Region::error(interner), receiver_ty.value, Mutability::Not), }; iterate_method_candidates_by_receiver( @@ -1215,15 +1147,10 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)), )?; - let ref_muted = next_solver::Canonical { + let ref_muted = Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: next_solver::Ty::new_ref( - interner, - next_solver::Region::error(interner), - receiver_ty.value, - rustc_ast_ir::Mutability::Mut, - ), + value: Ty::new_ref(interner, Region::error(interner), receiver_ty.value, Mutability::Mut), }; iterate_method_candidates_by_receiver( @@ -1231,12 +1158,10 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)), )?; - if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = - receiver_ty.value.kind() - { + if let TyKind::RawPtr(ty, Mutability::Mut) = receiver_ty.value.kind() { let const_ptr_ty = rustc_type_ir::Canonical { max_universe: rustc_type_ir::UniverseIndex::ZERO, - value: next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), + value: Ty::new_ptr(interner, ty, Mutability::Not), variables: receiver_ty.variables, }; iterate_method_candidates_by_receiver( @@ -1290,30 +1215,27 @@ where #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver<'db>( table: &mut InferenceTable<'db>, - receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: Canonical<'db, Ty<'db>>, receiver_adjustments: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - let interner = table.interner; - let receiver_ty = table.instantiate_canonical_ns(receiver_ty); - let receiver_ty: crate::Ty = receiver_ty.to_chalk(interner); + let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { - let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) - .include_raw_pointers() - .use_receiver_trait(); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( - &self_ty.to_chalk(interner), + self_ty, autoderef.table, name, - Some(&receiver_ty), + Some(receiver_ty), Some(receiver_adjustments.clone()), visible_from_module, LookupMode::MethodCall, @@ -1325,23 +1247,21 @@ fn iterate_method_candidates_by_receiver<'db>( ControlFlow::Continue(()) })?; table.run_in_snapshot(|table| { - let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) - .include_raw_pointers() - .use_receiver_trait(); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { - if matches!(self_ty.kind(), crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(_))) - { + if matches!(self_ty.kind(), TyKind::Infer(rustc_type_ir::TyVar(_))) { // don't try to resolve methods on unknown types return ControlFlow::Continue(()); } iterate_trait_method_candidates( - &self_ty.to_chalk(interner), + self_ty, autoderef.table, traits_in_scope, name, - Some(&receiver_ty), + Some(receiver_ty), Some(receiver_adjustments.clone()), LookupMode::MethodCall, &mut |adjustments, item, is_visible| { @@ -1355,7 +1275,7 @@ fn iterate_method_candidates_by_receiver<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_for_self_ty<'db>( - self_ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + self_ty: &Canonical<'db, Ty<'db>>, db: &'db dyn HirDatabase, env: Arc>, traits_in_scope: &FxHashSet, @@ -1364,9 +1284,9 @@ fn iterate_method_candidates_for_self_ty<'db>( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); - let self_ty = table.instantiate_canonical_ns(*self_ty).to_chalk(table.interner); + let self_ty = table.instantiate_canonical(*self_ty); iterate_inherent_methods( - &self_ty, + self_ty, &mut table, name, None, @@ -1378,7 +1298,7 @@ fn iterate_method_candidates_for_self_ty<'db>( }, )?; iterate_trait_method_candidates( - &self_ty, + self_ty, &mut table, traits_in_scope, name, @@ -1392,19 +1312,19 @@ fn iterate_method_candidates_for_self_ty<'db>( } #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))] -fn iterate_trait_method_candidates( - self_ty: &Ty, - table: &mut InferenceTable<'_>, +fn iterate_trait_method_candidates<'db>( + self_ty: Ty<'db>, + table: &mut InferenceTable<'db>, traits_in_scope: &FxHashSet, name: Option<&Name>, - receiver_ty: Option<&Ty>, + receiver_ty: Option>, receiver_adjustments: Option, mode: LookupMode, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; - let canonical_self_ty = table.canonicalize(self_ty.clone().to_nextsolver(table.interner)); + let canonical_self_ty = table.canonicalize(self_ty); let krate = table.trait_env.krate; 'traits: for &t in traits_in_scope { @@ -1416,7 +1336,7 @@ fn iterate_trait_method_candidates( // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for // arrays. if data.flags.contains(TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH) - && matches!(self_ty.kind(Interner), TyKind::Array(..)) + && matches!(self_ty.kind(), TyKind::Array(..)) { // FIXME: this should really be using the edition of the method name's span, in case it // comes from a macro @@ -1426,9 +1346,9 @@ fn iterate_trait_method_candidates( } if data.flags.contains(TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) && matches!( - self_ty.kind(Interner), TyKind::Adt(AdtId(def), subst) - if is_box(table.db, *def) - && matches!(subst.at(Interner, 0).assert_ty_ref(Interner).kind(Interner), TyKind::Slice(..)) + self_ty.kind(), TyKind::Adt(adt_def, subst) + if is_box(table.db, adt_def.def_id().0) + && matches!(subst.type_at(0).kind(), TyKind::Slice(..)) ) { // FIXME: this should really be using the edition of the method name's span, in case it @@ -1472,11 +1392,11 @@ fn iterate_trait_method_candidates( } #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))] -fn iterate_inherent_methods( - self_ty: &Ty, - table: &mut InferenceTable<'_>, +fn iterate_inherent_methods<'db>( + self_ty: Ty<'db>, + table: &mut InferenceTable<'db>, name: Option<&Name>, - receiver_ty: Option<&Ty>, + receiver_ty: Option>, receiver_adjustments: Option, visible_from_module: VisibleFromModule, mode: LookupMode, @@ -1489,11 +1409,11 @@ fn iterate_inherent_methods( // its super traits are considered inherent methods. This matters because these methods have // higher priority than the other traits' methods, which would be considered in // `iterate_trait_method_candidates()` only after this function. - match self_ty.kind(Interner) { - TyKind::Placeholder(_) => { + match self_ty.kind() { + TyKind::Param(_) => { let env = table.trait_env.clone(); let traits = env - .traits_in_scope_from_clauses(self_ty.clone()) + .traits_in_scope_from_clauses(self_ty.to_chalk(table.interner())) .flat_map(|t| all_super_traits(db, t)); iterate_inherent_trait_methods( self_ty, @@ -1506,9 +1426,9 @@ fn iterate_inherent_methods( mode, )?; } - TyKind::Dyn(_) => { - if let Some(principal_trait) = self_ty.dyn_trait() { - let traits = all_super_traits(db, principal_trait); + TyKind::Dynamic(bounds, _) => { + if let Some(principal_trait) = bounds.principal_def_id() { + let traits = all_super_traits(db, principal_trait.0); iterate_inherent_trait_methods( self_ty, table, @@ -1568,11 +1488,11 @@ fn iterate_inherent_methods( return ControlFlow::Continue(()); #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))] - fn iterate_inherent_trait_methods( - self_ty: &Ty, - table: &mut InferenceTable<'_>, + fn iterate_inherent_trait_methods<'db>( + self_ty: Ty<'db>, + table: &mut InferenceTable<'db>, name: Option<&Name>, - receiver_ty: Option<&Ty>, + receiver_ty: Option>, receiver_adjustments: Option, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, traits: impl Iterator, @@ -1603,12 +1523,12 @@ fn iterate_inherent_methods( } #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))] - fn impls_for_self_ty( + fn impls_for_self_ty<'db>( impls: &InherentImpls, - self_ty: &Ty, - table: &mut InferenceTable<'_>, + self_ty: Ty<'db>, + table: &mut InferenceTable<'db>, name: Option<&Name>, - receiver_ty: Option<&Ty>, + receiver_ty: Option>, receiver_adjustments: Option, visible_from_module: Option, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, @@ -1639,10 +1559,10 @@ fn iterate_inherent_methods( /// Returns the receiver type for the index trait call. pub(crate) fn resolve_indexing_op<'db>( table: &mut InferenceTable<'db>, - ty: next_solver::Canonical<'db, next_solver::Ty<'db>>, + ty: Canonical<'db, Ty<'db>>, index_trait: TraitId, ) -> Option { - let ty = table.instantiate_canonical_ns(ty); + let ty = table.instantiate_canonical(ty); let deref_chain = autoderef_method_receiver(table, ty); for (ty, adj) in deref_chain { let goal = generic_implements_goal_ns(table, index_trait, ty); @@ -1670,10 +1590,10 @@ enum IsValidCandidate { } #[tracing::instrument(skip_all, fields(name))] -fn is_valid_impl_method_candidate( - table: &mut InferenceTable<'_>, - self_ty: &Ty, - receiver_ty: Option<&Ty>, +fn is_valid_impl_method_candidate<'db>( + table: &mut InferenceTable<'db>, + self_ty: Ty<'db>, + receiver_ty: Option>, visible_from_module: Option, name: Option<&Name>, impl_id: ImplId, @@ -1705,9 +1625,8 @@ fn is_valid_impl_method_candidate( let self_ty_matches = table.run_in_snapshot(|table| { let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) .fill_with_inference_vars(table) - .build(DbInterner::conjure()) - .to_chalk(DbInterner::conjure()); - table.unify(&expected_self_ty, self_ty) + .build(table.interner()); + table.unify(expected_self_ty, self_ty) }); if !self_ty_matches { cov_mark::hit!(const_candidate_self_type_mismatch); @@ -1721,13 +1640,13 @@ fn is_valid_impl_method_candidate( /// Checks whether a given `AssocItemId` is applicable for `receiver_ty`. #[tracing::instrument(skip_all, fields(name))] -fn is_valid_trait_method_candidate( - table: &mut InferenceTable<'_>, +fn is_valid_trait_method_candidate<'db>( + table: &mut InferenceTable<'db>, trait_id: TraitId, name: Option<&Name>, - receiver_ty: Option<&Ty>, + receiver_ty: Option>, item: AssocItemId, - self_ty: &Ty, + self_ty: Ty<'db>, mode: LookupMode, ) -> IsValidCandidate { let db = table.db; @@ -1738,27 +1657,20 @@ fn is_valid_trait_method_candidate( check_that!(name.is_none_or(|n| n == &data.name)); table.run_in_snapshot(|table| { - let impl_subst = TyBuilder::subst_for_def(db, trait_id, None) - .fill_with_inference_vars(table) - .build(); - let expect_self_ty = impl_subst.at(Interner, 0).assert_ty_ref(Interner).clone(); + let impl_subst = table.fresh_args_for_item(trait_id.into()); + let expect_self_ty = impl_subst.type_at(0); - check_that!(table.unify(&expect_self_ty, self_ty)); + check_that!(table.unify(expect_self_ty, self_ty)); if let Some(receiver_ty) = receiver_ty { check_that!(data.has_self_param()); - let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst)) - .fill_with_inference_vars(table) - .build(); + let args = table.fill_rest_fresh_args(fn_id.into(), impl_subst); - let args: crate::next_solver::GenericArgs<'_> = - fn_subst.to_nextsolver(table.interner); let sig = db.callable_item_signature(fn_id.into()); let expected_receiver = sig .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0]) - .instantiate(table.interner, args) - .to_chalk(table.interner); + .instantiate(table.interner(), args); // FIXME: Clean up this mess with some context struct like rustc's `ProbeContext` let variance = match mode { @@ -1767,16 +1679,8 @@ fn is_valid_trait_method_candidate( }; let res = table .infer_ctxt - .at( - &next_solver::infer::traits::ObligationCause::dummy(), - table.trait_env.env, - ) - .relate( - DefineOpaqueTypes::No, - expected_receiver.to_nextsolver(table.interner), - variance, - receiver_ty.to_nextsolver(table.interner), - ); + .at(&ObligationCause::dummy(), table.trait_env.env) + .relate(DefineOpaqueTypes::No, expected_receiver, variance, receiver_ty); let Ok(infer_ok) = res else { return IsValidCandidate::No; }; @@ -1788,7 +1692,7 @@ fn is_valid_trait_method_candidate( check_that!(ctxt.select_where_possible().is_empty()); } - check_that!(table.unify(receiver_ty, &expected_receiver)); + check_that!(table.unify(receiver_ty, expected_receiver)); } IsValidCandidate::Yes @@ -1805,13 +1709,13 @@ fn is_valid_trait_method_candidate( } #[tracing::instrument(skip_all, fields(name))] -fn is_valid_impl_fn_candidate( - table: &mut InferenceTable<'_>, +fn is_valid_impl_fn_candidate<'db>( + table: &mut InferenceTable<'db>, impl_id: ImplId, fn_id: FunctionId, name: Option<&Name>, - receiver_ty: Option<&Ty>, - self_ty: &Ty, + receiver_ty: Option>, + self_ty: Ty<'db>, visible_from_module: Option, item_name: &Name, ) -> IsValidCandidate { @@ -1829,34 +1733,28 @@ fn is_valid_impl_fn_candidate( table.run_in_snapshot(|table| { let _p = tracing::info_span!("subst_for_def").entered(); let impl_subst = table.infer_ctxt.fresh_args_for_item(impl_id.into()); - let expect_self_ty = db - .impl_self_ty(impl_id) - .instantiate(table.interner, &impl_subst) - .to_chalk(table.interner); + let expect_self_ty = db.impl_self_ty(impl_id).instantiate(table.interner(), &impl_subst); - check_that!(table.unify(&expect_self_ty, self_ty)); + check_that!(table.unify(expect_self_ty, self_ty)); if let Some(receiver_ty) = receiver_ty { let _p = tracing::info_span!("check_receiver_ty").entered(); check_that!(data.has_self_param()); - let fn_subst: crate::Substitution = - table.infer_ctxt.fresh_args_for_item(fn_id.into()).to_chalk(table.interner); + let args = table.infer_ctxt.fresh_args_for_item(fn_id.into()); - let args: crate::next_solver::GenericArgs<'_> = fn_subst.to_nextsolver(table.interner); let sig = db.callable_item_signature(fn_id.into()); let expected_receiver = sig .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0]) - .instantiate(table.interner, args) - .to_chalk(table.interner); + .instantiate(table.interner(), args); - check_that!(table.unify(receiver_ty, &expected_receiver)); + check_that!(table.unify(receiver_ty, expected_receiver)); } // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. let predicates = db.generic_predicates_ns(impl_id.into()); - let Some(predicates) = predicates.instantiate(table.interner, impl_subst) else { + let Some(predicates) = predicates.instantiate(table.interner(), impl_subst) else { return IsValidCandidate::Yes; }; @@ -1864,7 +1762,7 @@ fn is_valid_impl_fn_candidate( ctxt.register_obligations(predicates.into_iter().map(|p| { PredicateObligation::new( - table.interner, + table.interner(), ObligationCause::new(), table.trait_env.env, p.0, @@ -1880,7 +1778,7 @@ fn is_valid_impl_fn_candidate( } pub fn implements_trait_unique<'db>( - ty: &Canonical, + ty: &crate::Canonical, db: &'db dyn HirDatabase, env: &TraitEnvironment<'db>, trait_: TraitId, @@ -1896,8 +1794,8 @@ fn generic_implements_goal<'db>( db: &'db dyn HirDatabase, env: &TraitEnvironment<'db>, trait_: TraitId, - self_ty: &Canonical, -) -> Canonical> { + self_ty: &crate::Canonical, +) -> crate::Canonical> { let binders = self_ty.binders.interned(); let trait_ref = TyBuilder::trait_ref(db, trait_) .push(self_ty.value.clone()) @@ -1920,7 +1818,7 @@ fn generic_implements_goal<'db>( &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)), obligation, ); - Canonical { binders, value } + crate::Canonical { binders, value } } /// This creates Substs for a trait with the given Self type and type variables @@ -1929,23 +1827,23 @@ fn generic_implements_goal<'db>( fn generic_implements_goal_ns<'db>( table: &mut InferenceTable<'db>, trait_: TraitId, - self_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, -) -> next_solver::Canonical<'db, next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { + self_ty: Canonical<'db, Ty<'db>>, +) -> Canonical<'db, Goal<'db, Predicate<'db>>> { let args = table.infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); - let self_ty = table.instantiate_canonical_ns(self_ty); + let self_ty = table.instantiate_canonical(self_ty); let trait_ref = rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args) .with_replaced_self_ty(table.infer_ctxt.interner, self_ty); - let goal = next_solver::Goal::new(table.infer_ctxt.interner, table.trait_env.env, trait_ref); + let goal = Goal::new(table.infer_ctxt.interner, table.trait_env.env, trait_ref); table.canonicalize(goal) } fn autoderef_method_receiver<'db>( table: &mut InferenceTable<'db>, - ty: next_solver::Ty<'db>, -) -> Vec<(next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { - let interner = table.interner; + ty: Ty<'db>, +) -> Vec<(Canonical<'db, Ty<'db>>, ReceiverAdjustments)> { + let interner = table.interner(); let mut deref_chain = Vec::new(); let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty).use_receiver_trait(); while let Some((ty, derefs)) = autoderef.next() { @@ -1958,9 +1856,9 @@ fn autoderef_method_receiver<'db>( if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) = deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables, d.0.max_universe, d.1.clone())) { - let unsized_ty = next_solver::Ty::new_slice(interner, parameters); + let unsized_ty = Ty::new_slice(interner, parameters); deref_chain.push(( - next_solver::Canonical { max_universe, value: unsized_ty, variables }, + Canonical { max_universe, value: unsized_ty, variables }, ReceiverAdjustments { unsize_array: true, ..adj.clone() }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 6465099dffd7f..a05cc2a02b70e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -708,10 +708,10 @@ impl BorrowKind { } } - fn from_chalk(m: Mutability) -> Self { + fn from_rustc(m: rustc_ast_ir::Mutability) -> Self { match m { - Mutability::Not => BorrowKind::Shared, - Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, + rustc_ast_ir::Mutability::Not => BorrowKind::Shared, + rustc_ast_ir::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 2c09fb9a89e78..08b1e03726d70 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -11,6 +11,8 @@ use rustc_hash::FxHashMap; use stdx::never; use triomphe::Arc; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags, db::{HirDatabase, InternedClosure}, @@ -61,16 +63,16 @@ pub struct BorrowckResult { pub borrow_regions: Vec, } -fn all_mir_bodies( - db: &dyn HirDatabase, +fn all_mir_bodies<'db>( + db: &'db dyn HirDatabase, def: DefWithBodyId, mut cb: impl FnMut(Arc), -) -> Result<(), MirLowerError> { - fn for_closure( - db: &dyn HirDatabase, +) -> Result<(), MirLowerError<'db>> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, c: ClosureId, cb: &mut impl FnMut(Arc), - ) -> Result<(), MirLowerError> { + ) -> Result<(), MirLowerError<'db>> { match db.mir_body_for_closure(c.into()) { Ok(body) => { cb(body.clone()); @@ -88,10 +90,10 @@ fn all_mir_bodies( } } -pub fn borrowck_query( - db: &dyn HirDatabase, +pub fn borrowck_query<'db>( + db: &'db dyn HirDatabase, def: DefWithBodyId, -) -> Result, MirLowerError> { +) -> Result, MirLowerError<'db>> { let _p = tracing::info_span!("borrowck_query").entered(); let mut res = vec![]; all_mir_bodies(db, def, |body| { @@ -112,14 +114,17 @@ fn make_fetch_closure_field( |c: ClosureId, subst: &Substitution, f: usize| { let InternedClosure(def, _) = db.lookup_intern_closure(c.into()); let infer = db.infer(def); - let (captures, _) = infer.closure_info(&c); + let (captures, _) = infer.closure_info(c.into()); let parent_subst = ClosureSubst(subst).parent_subst(db); + let interner = DbInterner::new_with(db, None, None); + let parent_subst: crate::next_solver::GenericArgs<'_> = + parent_subst.to_nextsolver(interner); captures .get(f) .expect("broken closure field") .ty - .clone() - .substitute(Interner, &parent_subst) + .instantiate(interner, parent_subst) + .to_chalk(interner) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index fa63baa5d2ab8..e46490e5262ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -32,6 +32,7 @@ use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, @@ -102,13 +103,13 @@ impl<'db> VTableMap<'db> { id } - pub(crate) fn ty(&self, id: usize) -> Result> { + pub(crate) fn ty(&self, id: usize) -> Result<'db, crate::next_solver::Ty<'db>> { id.checked_sub(VTableMap::OFFSET) .and_then(|id| self.id_to_ty.get(id).copied()) .ok_or(MirEvalError::InvalidVTableId(id)) } - fn ty_of_bytes(&self, bytes: &[u8]) -> Result> { + fn ty_of_bytes(&self, bytes: &[u8]) -> Result<'db, crate::next_solver::Ty<'db>> { let id = from_bytes!(usize, bytes); self.ty(id) } @@ -134,14 +135,14 @@ impl TlsData { self.keys.len() - 1 } - fn get_key(&mut self, key: usize) -> Result { + fn get_key(&mut self, key: usize) -> Result<'static, u128> { let r = self.keys.get(key).ok_or_else(|| { MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}")) })?; Ok(*r) } - fn set_key(&mut self, key: usize, value: u128) -> Result<()> { + fn set_key(&mut self, key: usize, value: u128) -> Result<'static, ()> { let r = self.keys.get_mut(key).ok_or_else(|| { MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}")) })?; @@ -202,6 +203,7 @@ pub struct Evaluator<'a> { stack_depth_limit: usize, /// Maximum count of bytes that heap and stack can grow memory_limit: usize, + interner: DbInterner<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -230,15 +232,19 @@ impl Interval { Self { addr, size } } - fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { memory.read_memory(self.addr, self.size) } - fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> { + fn write_from_bytes<'db>(&self, memory: &mut Evaluator<'db>, bytes: &[u8]) -> Result<'db, ()> { memory.write_memory(self.addr, bytes) } - fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> { + fn write_from_interval<'db>( + &self, + memory: &mut Evaluator<'db>, + interval: Interval, + ) -> Result<'db, ()> { memory.copy_from_interval(self.addr, interval) } @@ -248,16 +254,16 @@ impl Interval { } impl IntervalAndTy { - fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { memory.read_memory(self.interval.addr, self.interval.size) } - fn new( + fn new<'db>( addr: Address, ty: Ty, - evaluator: &Evaluator<'_>, + evaluator: &Evaluator<'db>, locals: &Locals, - ) -> Result { + ) -> Result<'db, IntervalAndTy> { let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) } @@ -275,7 +281,7 @@ impl From for IntervalOrOwned { } impl IntervalOrOwned { - fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, IntervalOrOwned::Borrowed(b) => b.get(memory)?, @@ -295,7 +301,7 @@ const HEAP_OFFSET: usize = 1 << 29; impl Address { #[allow(clippy::double_parens)] - fn from_bytes(it: &[u8]) -> Result { + fn from_bytes<'db>(it: &[u8]) -> Result<'db, Self> { Ok(Address::from_usize(from_bytes!(usize, it))) } @@ -335,8 +341,8 @@ impl Address { } #[derive(Clone, PartialEq, Eq)] -pub enum MirEvalError { - ConstEvalError(String, Box), +pub enum MirEvalError<'db> { + ConstEvalError(String, Box>), LayoutError(LayoutError, Ty), TargetDataLayoutNotAvailable(TargetLoadError), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected @@ -344,12 +350,15 @@ pub enum MirEvalError { UndefinedBehavior(String), Panic(String), // FIXME: This should be folded into ConstEvalError? - MirLowerError(FunctionId, MirLowerError), - MirLowerErrorForClosure(ClosureId, MirLowerError), + MirLowerError(FunctionId, MirLowerError<'db>), + MirLowerErrorForClosure(ClosureId, MirLowerError<'db>), TypeIsUnsized(Ty, &'static str), NotSupported(String), InvalidConst(Const), - InFunction(Box, Vec<(Either, MirSpan, DefWithBodyId)>), + InFunction( + Box>, + Vec<(Either, MirSpan, DefWithBodyId)>, + ), ExecutionLimitExceeded, StackOverflow, /// FIXME: Fold this into InternalError @@ -360,7 +369,7 @@ pub enum MirEvalError { InternalError(Box), } -impl MirEvalError { +impl MirEvalError<'_> { pub fn pretty_print( &self, f: &mut String, @@ -492,7 +501,7 @@ impl MirEvalError { } } -impl std::fmt::Debug for MirEvalError { +impl std::fmt::Debug for MirEvalError<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ConstEvalError(arg0, arg1) => { @@ -534,7 +543,7 @@ impl std::fmt::Debug for MirEvalError { } } -type Result = std::result::Result; +type Result<'db, T> = std::result::Result>; #[derive(Debug, Default)] struct DropFlags { @@ -595,10 +604,10 @@ pub fn interpret_mir<'db>( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option>>, -) -> Result<(Result, MirOutput)> { +) -> Result<'db, (Result<'db, Const>, MirOutput)> { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; - let it: Result = (|| { + let it: Result<'db, Const> = (|| { if evaluator.ptr_size() != size_of::() { not_supported!("targets with different pointer size from host"); } @@ -639,13 +648,14 @@ impl<'db> Evaluator<'db> { owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, trait_env: Option>>, - ) -> Result> { + ) -> Result<'db, Evaluator<'db>> { let crate_id = owner.module(db).krate(); let target_data_layout = match db.target_data_layout(crate_id) { Ok(target_data_layout) => target_data_layout, Err(e) => return Err(MirEvalError::TargetDataLayoutNotAvailable(e)), }; let cached_ptr_size = target_data_layout.pointer_size().bytes_usize(); + let interner = DbInterner::new_with(db, None, None); Ok(Evaluator { target_data_layout, stack: vec![0], @@ -679,14 +689,15 @@ impl<'db> Evaluator<'db> { cached_fn_once_trait_func: LangItem::FnOnce.resolve_trait(db, crate_id).and_then(|x| { x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) }), + interner, }) } - fn place_addr(&self, p: &Place, locals: &Locals) -> Result

{ + fn place_addr(&self, p: &Place, locals: &Locals) -> Result<'db, Address> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals) -> Result { + fn place_interval(&self, p: &Place, locals: &Locals) -> Result<'db, Interval> { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -714,14 +725,20 @@ impl<'db> Evaluator<'db> { |c, subst, f| { let InternedClosure(def, _) = self.db.lookup_intern_closure(c.into()); let infer = self.db.infer(def); - let (captures, _) = infer.closure_info(&c); + let (captures, _) = infer.closure_info(c.into()); let parent_subst = ClosureSubst(subst).parent_subst(self.db); captures .get(f) .expect("broken closure field") .ty - .clone() - .substitute(Interner, &parent_subst) + .instantiate( + self.interner, + <_ as ChalkToNextSolver<'db, crate::next_solver::GenericArgs<'db>>>::to_nextsolver( + &parent_subst, + self.interner, + ), + ) + .to_chalk(self.interner) }, self.crate_id, ); @@ -733,7 +750,7 @@ impl<'db> Evaluator<'db> { &'a self, p: &Place, locals: &'a Locals, - ) -> Result<(Address, Ty, Option)> { + ) -> Result<'db, (Address, Ty, Option)> { let interner = DbInterner::new_with(self.db, None, None); let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); @@ -851,7 +868,7 @@ impl<'db> Evaluator<'db> { Ok((addr, ty, metadata)) } - fn layout(&self, ty: crate::next_solver::Ty<'db>) -> Result> { + fn layout(&self, ty: crate::next_solver::Ty<'db>) -> Result<'db, Arc> { if let Some(x) = self.layout_cache.borrow().get(&ty) { return Ok(x.clone()); } @@ -864,7 +881,7 @@ impl<'db> Evaluator<'db> { Ok(r) } - fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { + fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<'db, Arc> { let interner = DbInterner::new_with(self.db, None, None); self.layout(crate::next_solver::Ty::new( interner, @@ -875,22 +892,27 @@ impl<'db> Evaluator<'db> { )) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result { + fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<'db, Ty> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result { + fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<'db, Ty> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, OperandKind::Constant(c) => c.data(Interner).ty.clone(), &OperandKind::Static(s) => { - let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone(); + let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr] + .to_chalk(self.interner); TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner) } }) } - fn operand_ty_and_eval(&mut self, o: &Operand, locals: &mut Locals) -> Result { + fn operand_ty_and_eval( + &mut self, + o: &Operand, + locals: &mut Locals, + ) -> Result<'db, IntervalAndTy> { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, ty: self.operand_ty(o, locals)?, @@ -901,7 +923,7 @@ impl<'db> Evaluator<'db> { &mut self, body: Arc, args: impl Iterator, - ) -> Result { + ) -> Result<'db, Interval> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = it; } else { @@ -962,7 +984,7 @@ impl<'db> Evaluator<'db> { let args = args .iter() .map(|it| self.operand_ty_and_eval(it, locals)) - .collect::>>()?; + .collect::>>()?; let stack_frame = match &fn_ty.kind(Interner) { TyKind::Function(_) => { let bytes = self.eval_operand(func, locals)?; @@ -1066,7 +1088,7 @@ impl<'db> Evaluator<'db> { body: &MirBody, locals: &mut Locals, args: impl Iterator, - ) -> Result<()> { + ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { locals.drop_flags.add_place(l.into(), &locals.body.projection_store); @@ -1089,7 +1111,7 @@ impl<'db> Evaluator<'db> { &mut self, body: &Arc, destination: Option, - ) -> Result<(Locals, usize)> { + ) -> Result<'db, (Locals, usize)> { let mut locals = match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() { None => Locals { @@ -1136,7 +1158,7 @@ impl<'db> Evaluator<'db> { Ok((locals, prev_stack_pointer)) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<'db, IntervalOrOwned> { let interner = DbInterner::new_with(self.db, None, None); use IntervalOrOwned::*; Ok(match r { @@ -1450,7 +1472,7 @@ impl<'db> Evaluator<'db> { let values = values .iter() .map(|it| self.eval_operand(it, locals)) - .collect::>>()?; + .collect::>>()?; match kind { AggregateKind::Array(_) => { let mut r = vec![]; @@ -1649,7 +1671,7 @@ impl<'db> Evaluator<'db> { }) } - fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result { + fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result<'db, i128> { let interner = DbInterner::new_with(self.db, None, None); let layout = self.layout(ty.to_nextsolver(interner))?; let &TyKind::Adt(chalk_ir::AdtId(AdtId::EnumId(e)), _) = ty.kind(Interner) else { @@ -1696,7 +1718,7 @@ impl<'db> Evaluator<'db> { &self, ty: &Ty, goal: impl Fn(&TyKind) -> Option, - ) -> Result { + ) -> Result<'db, T> { let kind = ty.kind(Interner); if let Some(it) = goal(kind) { return Ok(it); @@ -1719,7 +1741,7 @@ impl<'db> Evaluator<'db> { addr: Interval, current_ty: &Ty, target_ty: &Ty, - ) -> Result { + ) -> Result<'db, IntervalOrOwned> { fn for_ptr(it: &TyKind) -> Option { match it { TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), @@ -1738,7 +1760,7 @@ impl<'db> Evaluator<'db> { target_ty: Ty, current_ty: Ty, addr: Interval, - ) -> Result { + ) -> Result<'db, IntervalOrOwned> { use IntervalOrOwned::*; Ok(match &target_ty.kind(Interner) { TyKind::Slice(_) => match ¤t_ty.kind(Interner) { @@ -1806,7 +1828,7 @@ impl<'db> Evaluator<'db> { it: VariantId, subst: Substitution, locals: &Locals, - ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { + ) -> Result<'db, (usize, Arc, Option<(usize, usize, i128)>)> { let interner = DbInterner::new_with(self.db, None, None); let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner @@ -1874,7 +1896,7 @@ impl<'db> Evaluator<'db> { variant_layout: &Layout, tag: Option<(usize, usize, i128)>, values: impl Iterator, - ) -> Result> { + ) -> Result<'db, Vec> { let mut result = vec![0; size]; if let Some((offset, size, value)) = tag { match result.get_mut(offset..offset + size) { @@ -1904,7 +1926,7 @@ impl<'db> Evaluator<'db> { Ok(result) } - fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { locals.drop_flags.remove_place(p, &locals.body.projection_store); @@ -1919,8 +1941,8 @@ impl<'db> Evaluator<'db> { } #[allow(clippy::double_parens)] - fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { - let interner = DbInterner::new_with(self.db, None, None); + fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<'db, Interval> { + let interner = self.interner; let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner) else { not_supported!("evaluating non concrete constant"); @@ -1932,9 +1954,14 @@ impl<'db> Evaluator<'db> { let mut const_id = *const_id; let mut subst = subst.clone(); if let hir_def::GeneralConstId::ConstId(c) = const_id { - let (c, s) = lookup_impl_const(self.db, self.trait_env.clone(), c, subst); + let (c, s) = lookup_impl_const( + self.interner, + self.trait_env.clone(), + c, + subst.to_nextsolver(self.interner), + ); const_id = hir_def::GeneralConstId::ConstId(c); - subst = s; + subst = s.to_chalk(self.interner); } result_owner = self .db @@ -1987,7 +2014,7 @@ impl<'db> Evaluator<'db> { Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result { + fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<'db, Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -1995,7 +2022,7 @@ impl<'db> Evaluator<'db> { )) } - fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> { + fn read_memory(&self, addr: Address, size: usize) -> Result<'db, &[u8]> { if size == 0 { return Ok(&[]); } @@ -2012,7 +2039,7 @@ impl<'db> Evaluator<'db> { .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_owned())) } - fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> { + fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<'db, &mut [u8]> { let (mem, pos) = match addr { Stack(it) => (&mut self.stack, it), Heap(it) => (&mut self.heap, it), @@ -2026,7 +2053,7 @@ impl<'db> Evaluator<'db> { .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory write".to_owned())) } - fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { + fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<'db, ()> { if r.is_empty() { return Ok(()); } @@ -2034,14 +2061,18 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn copy_from_interval_or_owned(&mut self, addr: Address, r: IntervalOrOwned) -> Result<()> { + fn copy_from_interval_or_owned( + &mut self, + addr: Address, + r: IntervalOrOwned, + ) -> Result<'db, ()> { match r { IntervalOrOwned::Borrowed(r) => self.copy_from_interval(addr, r), IntervalOrOwned::Owned(r) => self.write_memory(addr, &r), } } - fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> { + fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<'db, ()> { if r.size == 0 { return Ok(()); } @@ -2083,7 +2114,7 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { + fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<'db, Option<(usize, usize)>> { let interner = DbInterner::new_with(self.db, None, None); if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) { return Ok(layout @@ -2112,7 +2143,7 @@ impl<'db> Evaluator<'db> { /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` - fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result { + fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result<'db, usize> { match self.size_align_of(ty, locals)? { Some(it) => Ok(it.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), @@ -2126,14 +2157,14 @@ impl<'db> Evaluator<'db> { ty: &Ty, locals: &Locals, what: &'static str, - ) -> Result<(usize, usize)> { + ) -> Result<'db, (usize, usize)> { match self.size_align_of(ty, locals)? { Some(it) => Ok(it), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } - fn heap_allocate(&mut self, size: usize, align: usize) -> Result
{ + fn heap_allocate(&mut self, size: usize, align: usize) -> Result<'db, Address> { if !align.is_power_of_two() || align > 10000 { return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid"))); } @@ -2166,7 +2197,7 @@ impl<'db> Evaluator<'db> { bytes: &[u8], ty: &Ty, locals: &Locals, - ) -> Result> { + ) -> Result<'db, ComplexMemoryMap<'db>> { fn rec<'db>( this: &Evaluator<'db>, bytes: &[u8], @@ -2174,7 +2205,7 @@ impl<'db> Evaluator<'db> { locals: &Locals, mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, - ) -> Result<()> { + ) -> Result<'db, ()> { let interner = DbInterner::new_with(this.db, None, None); if stack_depth_limit.checked_sub(1).is_none() { return Err(MirEvalError::StackOverflow); @@ -2333,11 +2364,11 @@ impl<'db> Evaluator<'db> { fn patch_addresses( &mut self, patch_map: &FxHashMap, - ty_of_bytes: impl Fn(&[u8]) -> Result> + Copy, + ty_of_bytes: impl Fn(&[u8]) -> Result<'db, crate::next_solver::Ty<'db>> + Copy, addr: Address, ty: crate::next_solver::Ty<'db>, locals: &Locals, - ) -> Result<()> { + ) -> Result<'db, ()> { let interner = DbInterner::new_with(self.db, None, None); // FIXME: support indirect references let layout = self.layout(ty)?; @@ -2469,7 +2500,7 @@ impl<'db> Evaluator<'db> { locals: &Locals, target_bb: Option, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?; let interner = DbInterner::new_with(self.db, None, None); @@ -2506,7 +2537,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy], locals: &Locals, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -2523,7 +2554,7 @@ impl<'db> Evaluator<'db> { }; let arg_bytes = iter::once(Ok(closure_data)) .chain(args.iter().map(|it| Ok(it.get(self)?.to_owned()))) - .collect::>>()?; + .collect::>>()?; let interval = self .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned)) .map_err(|e| { @@ -2545,7 +2576,7 @@ impl<'db> Evaluator<'db> { locals: &Locals, target_bb: Option, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { @@ -2603,23 +2634,33 @@ impl<'db> Evaluator<'db> { generic_args: Substitution, locals: &Locals, span: MirSpan, - ) -> Result { + ) -> Result<'db, MirOrDynIndex> { let pair = (def, generic_args); if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) { return Ok(r.clone()); } let (def, generic_args) = pair; - let r = if let Some(self_ty_idx) = - is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) - { + let r = if let Some(self_ty_idx) = is_dyn_method( + self.interner, + self.trait_env.clone(), + def, + generic_args.to_nextsolver(self.interner), + ) { MirOrDynIndex::Dyn(self_ty_idx) } else { - let (imp, generic_args) = - self.db.lookup_impl_method(self.trait_env.clone(), def, generic_args.clone()); + let (imp, generic_args) = self.db.lookup_impl_method( + self.trait_env.clone(), + def, + generic_args.to_nextsolver(self.interner), + ); let mir_body = self .db - .monomorphized_mir_body(imp.into(), generic_args, self.trait_env.clone()) + .monomorphized_mir_body( + imp.into(), + generic_args.to_chalk(self.interner), + self.trait_env.clone(), + ) .map_err(|e| { MirEvalError::InFunction( Box::new(MirEvalError::MirLowerError(imp, e)), @@ -2641,7 +2682,7 @@ impl<'db> Evaluator<'db> { destination: Interval, target_bb: Option, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let interner = DbInterner::new_with(self.db, None, None); if self.detect_and_exec_special_function( def, @@ -2713,7 +2754,7 @@ impl<'db> Evaluator<'db> { span: MirSpan, destination: Interval, target_bb: Option, - ) -> Result> { + ) -> Result<'db, Option> { Ok(if let Some(target_bb) = target_bb { let (mut locals, prev_stack_ptr) = self.create_locals_for_body(&mir_body, Some(destination))?; @@ -2741,7 +2782,7 @@ impl<'db> Evaluator<'db> { destination: Interval, target_bb: Option, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let interner = DbInterner::new_with(self.db, None, None); let func = args .first() @@ -2817,7 +2858,7 @@ impl<'db> Evaluator<'db> { } } - fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result
{ + fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<'db, Address> { if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -2828,8 +2869,9 @@ impl<'db> Evaluator<'db> { })?; self.allocate_const_in_heap(locals, &konst)? } else { - let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr]; - let Some((size, align)) = self.size_align_of(ty, locals)? else { + let ty = + self.db.infer(st.into())[self.db.body(st.into()).body_expr].to_chalk(self.interner); + let Some((size, align)) = self.size_align_of(&ty, locals)? else { not_supported!("unsized extern static"); }; let addr = self.heap_allocate(size, align)?; @@ -2841,7 +2883,7 @@ impl<'db> Evaluator<'db> { Ok(addr) } - fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result { + fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> { let r = self.db.const_eval_discriminant(variant); match r { Ok(r) => Ok(r), @@ -2863,7 +2905,7 @@ impl<'db> Evaluator<'db> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> { + fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); @@ -2882,7 +2924,7 @@ impl<'db> Evaluator<'db> { addr: Address, _metadata: &[u8], span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { let Some(drop_fn) = (|| { let drop_trait = LangItem::Drop.resolve_trait(self.db, self.crate_id)?; drop_trait.trait_items(self.db).method_by_name(&Name::new_symbol_root(sym::drop)) @@ -2962,22 +3004,22 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn write_to_stdout(&mut self, interval: Interval) -> Result<()> { + fn write_to_stdout(&mut self, interval: Interval) -> Result<'db, ()> { self.stdout.extend(interval.get(self)?.to_vec()); Ok(()) } - fn write_to_stderr(&mut self, interval: Interval) -> Result<()> { + fn write_to_stderr(&mut self, interval: Interval) -> Result<'db, ()> { self.stderr.extend(interval.get(self)?.to_vec()); Ok(()) } } -pub fn render_const_using_debug_impl( - db: &dyn HirDatabase, +pub fn render_const_using_debug_impl<'db>( + db: &'db dyn HirDatabase, owner: DefWithBodyId, c: &Const, -) -> Result { +) -> Result<'db, String> { let interner = DbInterner::new_with(db, None, None); let mut evaluator = Evaluator::new(db, owner, false, None)?; let locals = &Locals { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index bb0d1f70fbb29..4653610eddda1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -49,7 +49,7 @@ macro_rules! not_supported { }; } -impl Evaluator<'_> { +impl<'db> Evaluator<'db> { pub(super) fn detect_and_exec_special_function( &mut self, def: FunctionId, @@ -58,7 +58,7 @@ impl Evaluator<'_> { locals: &Locals, destination: Interval, span: MirSpan, - ) -> Result { + ) -> Result<'db, bool> { if self.not_special_fn_cache.borrow().contains(&def) { return Ok(false); } @@ -142,7 +142,7 @@ impl Evaluator<'_> { pub(super) fn detect_and_redirect_special_function( &mut self, def: FunctionId, - ) -> Result> { + ) -> Result<'db, Option> { // `PanicFmt` is redirected to `ConstPanicFmt` if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) { let resolver = CrateRootModuleId::from(self.crate_id).resolver(self.db); @@ -166,8 +166,8 @@ impl Evaluator<'_> { locals: &Locals, destination: Interval, span: MirSpan, - ) -> Result<()> { - let interner = DbInterner::new_with(self.db, None, None); + ) -> Result<'db, ()> { + let interner = self.interner; match self_ty.kind(Interner) { TyKind::Function(_) => { let [arg] = args else { @@ -184,9 +184,12 @@ impl Evaluator<'_> { let addr = Address::from_bytes(arg.get(self)?)?; let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into()); let infer = self.db.infer(closure_owner); - let (captures, _) = infer.closure_info(id); + let (captures, _) = infer.closure_info((*id).into()); let layout = self.layout(self_ty.to_nextsolver(interner))?; - let ty_iter = captures.iter().map(|c| c.ty(self.db, subst)); + let db = self.db; + let ty_iter = captures + .iter() + .map(|c| c.ty(db, subst.to_nextsolver(interner)).to_chalk(interner)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } TyKind::Tuple(_, subst) => { @@ -222,7 +225,7 @@ impl Evaluator<'_> { locals: &Locals, destination: Interval, span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { let interner = DbInterner::new_with(self.db, None, None); for (i, ty) in ty_iter.enumerate() { let size = self.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); @@ -250,7 +253,7 @@ impl Evaluator<'_> { alloc_fn: &Symbol, args: &[IntervalAndTy], destination: Interval, - ) -> Result<()> { + ) -> Result<'db, ()> { match alloc_fn { _ if *alloc_fn == sym::rustc_allocator_zeroed || *alloc_fn == sym::rustc_allocator => { let [size, align] = args else { @@ -314,7 +317,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], locals: &Locals, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Vec> { use LangItem::*; let mut args = args.iter(); match it { @@ -391,7 +394,7 @@ impl Evaluator<'_> { destination: Interval, _locals: &Locals, _span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { match id { 318 => { // SYS_getrandom @@ -422,7 +425,7 @@ impl Evaluator<'_> { destination: Interval, locals: &Locals, span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { match as_str { "memcmp" => { let [ptr1, ptr2, size] = args else { @@ -589,7 +592,7 @@ impl Evaluator<'_> { locals: &Locals, span: MirSpan, needs_override: bool, - ) -> Result { + ) -> Result<'db, bool> { let interner = DbInterner::new_with(self.db, None, None); if let Some(name) = name.strip_prefix("atomic_") { return self @@ -1405,7 +1408,7 @@ impl Evaluator<'_> { ty: &Ty, metadata: Interval, locals: &Locals, - ) -> Result<(usize, usize)> { + ) -> Result<'db, (usize, usize)> { let interner = DbInterner::new_with(self.db, None, None); Ok(match ty.kind(Interner) { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), @@ -1461,7 +1464,7 @@ impl Evaluator<'_> { destination: Interval, locals: &Locals, _span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { let interner = DbInterner::new_with(self.db, None, None); // We are a single threaded runtime with no UB checking and no optimization, so // we can implement atomic intrinsics as normal functions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index f554772904537..8e62c764b99bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -22,8 +22,8 @@ macro_rules! not_supported { }; } -impl Evaluator<'_> { - fn detect_simd_ty(&self, ty: &Ty) -> Result<(usize, Ty)> { +impl<'db> Evaluator<'db> { + fn detect_simd_ty(&self, ty: &Ty) -> Result<'db, (usize, Ty)> { match ty.kind(Interner) { TyKind::Adt(id, subst) => { let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner)) @@ -74,7 +74,7 @@ impl Evaluator<'_> { destination: Interval, _locals: &Locals, _span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { match name { "and" | "or" | "xor" => { let [left, right] = args else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 2a6e3a147a7d7..9e948d1b43c7a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -11,7 +11,7 @@ use crate::{ use super::{MirEvalError, interpret_mir}; -fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError> { +fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError<'_>> { salsa::attach(db, || { let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); @@ -114,7 +114,7 @@ fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: fn check_error_with( #[rust_analyzer::rust_fixture] ra_fixture: &str, - expect_err: impl FnOnce(MirEvalError) -> bool, + expect_err: impl FnOnce(MirEvalError<'_>) -> bool, ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); salsa::attach(&db, || { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 3e44e8c68ddaa..ec7bff708297d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -20,6 +20,7 @@ use hir_expand::name::Name; use la_arena::ArenaMap; use rustc_apfloat::Float; use rustc_hash::FxHashMap; +use rustc_type_ir::inherent::IntoKind; use span::{Edition, FileId}; use syntax::TextRange; use triomphe::Arc; @@ -74,24 +75,25 @@ struct DropScope { locals: Vec, } -struct MirLowerCtx<'db> { +struct MirLowerCtx<'a, 'db> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, labeled_loop_blocks: FxHashMap, discr_temp: Option, db: &'db dyn HirDatabase, - body: &'db Body, - infer: &'db InferenceResult, + body: &'a Body, + infer: &'a InferenceResult<'db>, resolver: Resolver<'db>, drop_scopes: Vec, env: Arc>, + interner: DbInterner<'db>, } // FIXME: Make this smaller, its stored in database queries #[derive(Debug, Clone, PartialEq, Eq)] -pub enum MirLowerError { - ConstEvalError(Box, Box), +pub enum MirLowerError<'db> { + ConstEvalError(Box, Box>), LayoutError(LayoutError), IncompleteExpr, IncompletePattern, @@ -103,7 +105,7 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(TypeMismatch), + TypeMismatch(TypeMismatch<'db>), HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -128,7 +130,7 @@ struct DropScopeToken; impl DropScopeToken { fn pop_and_drop( self, - ctx: &mut MirLowerCtx<'_>, + ctx: &mut MirLowerCtx<'_, '_>, current: BasicBlockId, span: MirSpan, ) -> BasicBlockId { @@ -140,7 +142,7 @@ impl DropScopeToken { /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't /// do anything) - fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) { + fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_, '_>) { std::mem::forget(self); ctx.pop_drop_scope_assume_dropped_internal(); } @@ -161,7 +163,7 @@ impl Drop for DropScopeToken { // } // } -impl MirLowerError { +impl MirLowerError<'_> { pub fn pretty_print( &self, f: &mut String, @@ -248,13 +250,13 @@ macro_rules! implementation_error { }}; } -impl From for MirLowerError { +impl From for MirLowerError<'_> { fn from(value: LayoutError) -> Self { MirLowerError::LayoutError(value) } } -impl MirLowerError { +impl MirLowerError<'_> { fn unresolved_path( db: &dyn HirDatabase, p: &Path, @@ -267,14 +269,14 @@ impl MirLowerError { } } -type Result = std::result::Result; +type Result<'db, T> = std::result::Result>; -impl<'ctx> MirLowerCtx<'ctx> { +impl<'a, 'db> MirLowerCtx<'a, 'db> { fn new( - db: &'ctx dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, - body: &'ctx Body, - infer: &'ctx InferenceResult, + body: &'a Body, + infer: &'a InferenceResult<'db>, ) -> Self { let mut basic_blocks = Arena::new(); let start_block = basic_blocks.alloc(BasicBlock { @@ -296,6 +298,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let resolver = owner.resolver(db); let env = db.trait_environment_for_body(owner); + let interner = DbInterner::new_with(db, Some(env.krate), env.block); MirLowerCtx { result: mir, @@ -309,10 +312,11 @@ impl<'ctx> MirLowerCtx<'ctx> { discr_temp: None, drop_scopes: vec![DropScope::default()], env, + interner, } } - fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result { + fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<'db, LocalId> { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { return Err(MirLowerError::UnsizedTemporary(ty)); } @@ -325,7 +329,7 @@ impl<'ctx> MirLowerCtx<'ctx> { &mut self, expr_id: ExprId, current: BasicBlockId, - ) -> Result> { + ) -> Result<'db, Option<(Operand, BasicBlockId)>> { if !self.has_adjustments(expr_id) && let Expr::Literal(l) = &self.body[expr_id] { @@ -343,8 +347,8 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id: ExprId, place: Place, current: BasicBlockId, - adjustments: &[Adjustment], - ) -> Result> { + adjustments: &[Adjustment<'db>], + ) -> Result<'db, Option> { match adjustments.split_last() { Some((last, rest)) => match &last.kind { Adjust::NeverToAny => { @@ -372,7 +376,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; - let bk = BorrowKind::from_chalk(*m); + let bk = BorrowKind::from_rustc(*m); self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); Ok(Some(current)) } @@ -388,7 +392,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Rvalue::Cast( CastKind::PointerCoercion(*cast), Operand { kind: OperandKind::Copy(p), span: None }, - last.target.clone(), + last.target.to_chalk(self.interner), ), expr_id.into(), ); @@ -404,7 +408,7 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id: ExprId, place: Place, prev_block: BasicBlockId, - ) -> Result> { + ) -> Result<'db, Option> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments); } @@ -416,7 +420,7 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id: ExprId, place: Place, mut current: BasicBlockId, - ) -> Result> { + ) -> Result<'db, Option> { match &self.body[expr_id] { Expr::OffsetOf(_) => { not_supported!("builtin#offset_of") @@ -443,7 +447,7 @@ impl<'ctx> MirLowerCtx<'ctx> { c.into(), current, place, - subst, + subst.to_chalk(self.interner), expr_id.into(), self.expr_ty_without_adjust(expr_id), )?; @@ -511,12 +515,12 @@ impl<'ctx> MirLowerCtx<'ctx> { ValueNs::EnumVariantId(variant_id) => { let variant_fields = variant_id.fields(self.db); if variant_fields.shape == FieldsShape::Unit { - let ty = self.infer.type_of_expr[expr_id].clone(); + let ty = self.infer.type_of_expr[expr_id]; current = self.lower_enum_variant( variant_id, current, place, - ty, + ty.to_chalk(self.interner), Box::new([]), expr_id.into(), )?; @@ -651,7 +655,7 @@ impl<'ctx> MirLowerCtx<'ctx> { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { let ty = chalk_ir::TyKind::FnDef( CallableDefId::FunctionId(func_id).to_chalk(self.db), - generic_args, + generic_args.to_chalk(self.interner), ) .intern(Interner); let func = Operand::from_bytes(Box::default(), ty); @@ -710,7 +714,7 @@ impl<'ctx> MirLowerCtx<'ctx> { method_name.display(self.db, self.edition()).to_string(), ) })?; - let func = Operand::from_fn(self.db, func_id, generic_args); + let func = Operand::from_fn(self.db, func_id, generic_args.to_chalk(self.interner)); self.lower_call_and_args( func, iter::once(*receiver).chain(args.iter().copied()), @@ -948,8 +952,8 @@ impl<'ctx> MirLowerCtx<'ctx> { let rvalue = if self.infer.coercion_casts.contains(expr) { Rvalue::Use(it) } else { - let source_ty = self.infer[*expr].clone(); - let target_ty = self.infer[expr_id].clone(); + let source_ty = self.infer[*expr].to_chalk(self.interner); + let target_ty = self.infer[expr_id].to_chalk(self.interner); let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { @@ -1047,7 +1051,8 @@ impl<'ctx> MirLowerCtx<'ctx> { if !is_builtin && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { - let func = Operand::from_fn(self.db, func_id, generic_args); + let func = + Operand::from_fn(self.db, func_id, generic_args.to_chalk(self.interner)); return self.lower_call_and_args( func, [*lhs, *rhs].into_iter(), @@ -1195,7 +1200,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; o.ok_or(MirLowerError::UnresolvedField) }) - .collect::>()?, + .collect::>()?, ), expr_id.into(), ); @@ -1207,7 +1212,7 @@ impl<'ctx> MirLowerCtx<'ctx> { not_supported!("closure with non closure type"); }; self.result.closures.push(*id); - let (captures, _) = self.infer.closure_info(id); + let (captures, _) = self.infer.closure_info((*id).into()); let mut operands = vec![]; for capture in captures.iter() { let p = Place { @@ -1231,7 +1236,7 @@ impl<'ctx> MirLowerCtx<'ctx> { ProjectionElem::Subslice { from, to } } ProjectionElem::OpaqueCast(it) => { - ProjectionElem::OpaqueCast(it) + ProjectionElem::OpaqueCast(it.to_chalk(self.interner)) } #[allow(unreachable_patterns)] ProjectionElem::Index(it) => match it {}, @@ -1241,9 +1246,12 @@ impl<'ctx> MirLowerCtx<'ctx> { }; match &capture.kind { CaptureKind::ByRef(bk) => { - let placeholder_subst = self.placeholder_subst(); - let tmp_ty = - capture.ty.clone().substitute(Interner, &placeholder_subst); + let placeholder_subst: crate::next_solver::GenericArgs<'db> = + self.placeholder_subst().to_nextsolver(self.interner); + let tmp_ty = capture + .ty + .instantiate(self.interner, placeholder_subst) + .to_chalk(self.interner); // FIXME: Handle more than one span. let capture_spans = capture.spans(); let tmp: Place = self.temp(tmp_ty, current, capture_spans[0])?.into(); @@ -1278,7 +1286,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current = c; Ok(Some(o)) }) - .collect::>>()? + .collect::>>()? else { return Ok(None); }; @@ -1309,7 +1317,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current = c; Ok(Some(o)) }) - .collect::>>()? + .collect::>>()? else { return Ok(None); }; @@ -1353,7 +1361,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> { + fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { let index = @@ -1378,7 +1386,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(()) } - fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result { + fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<'db, Operand> { match &self.body[*loc] { Expr::Literal(l) => self.lower_literal_to_operand(ty, l), Expr::Path(c) => { @@ -1415,7 +1423,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { + fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<'db, Operand> { let interner = DbInterner::new_with(self.db, None, None); let size = || { self.db @@ -1479,7 +1487,7 @@ impl<'ctx> MirLowerCtx<'ctx> { subst: Substitution, span: MirSpan, ty: Ty, - ) -> Result<()> { + ) -> Result<'db, ()> { let c = self.lower_const_to_operand(subst, const_id, ty)?; self.push_assignment(prev_block, place, c.into(), span); Ok(()) @@ -1490,7 +1498,7 @@ impl<'ctx> MirLowerCtx<'ctx> { subst: Substitution, const_id: GeneralConstId, ty: Ty, - ) -> Result { + ) -> Result<'db, Operand> { let c = if subst.len(Interner) != 0 { // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) @@ -1510,7 +1518,7 @@ impl<'ctx> MirLowerCtx<'ctx> { cv: Box<[u8]>, ty: Ty, span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span); Ok(()) } @@ -1523,7 +1531,7 @@ impl<'ctx> MirLowerCtx<'ctx> { ty: Ty, fields: Box<[Operand]>, span: MirSpan, - ) -> Result { + ) -> Result<'db, BasicBlockId> { let subst = match ty.kind(Interner) { TyKind::Adt(_, subst) => subst.clone(), _ => implementation_error!("Non ADT enum"), @@ -1545,7 +1553,7 @@ impl<'ctx> MirLowerCtx<'ctx> { mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let Some(args) = args .map(|arg| { if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { @@ -1555,7 +1563,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(None) } }) - .collect::>>>()? + .collect::>>>()? else { return Ok(None); }; @@ -1570,7 +1578,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current: BasicBlockId, is_uninhabited: bool, span: MirSpan, - ) -> Result> { + ) -> Result<'db, Option> { let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; self.set_terminator( current, @@ -1600,7 +1608,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn expr_ty_without_adjust(&self, e: ExprId) -> Ty { - self.infer[e].clone() + self.infer[e].to_chalk(self.interner) } fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { @@ -1608,7 +1616,7 @@ impl<'ctx> MirLowerCtx<'ctx> { if let Some(it) = self.infer.expr_adjustments.get(&e) && let Some(it) = it.last() { - ty = Some(it.target.clone()); + ty = Some(it.target.to_chalk(self.interner)); } ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) } @@ -1651,8 +1659,8 @@ impl<'ctx> MirLowerCtx<'ctx> { place: Place, label: Option, span: MirSpan, - f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, - ) -> Result> { + f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, + ) -> Result<'db, Option> { let begin = self.new_basic_block(); let prev = self.current_loop_blocks.replace(LoopBlocks { begin, @@ -1703,7 +1711,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn current_loop_end(&mut self) -> Result { + fn current_loop_end(&mut self) -> Result<'db, BasicBlockId> { let r = match self .current_loop_blocks .as_mut() @@ -1730,7 +1738,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn is_uninhabited(&self, expr_id: ExprId) -> bool { is_ty_uninhabited_from( self.db, - &self.infer[expr_id], + &self.infer[expr_id].to_chalk(self.interner), self.owner.module(self.db), self.env.clone(), ) @@ -1738,7 +1746,7 @@ impl<'ctx> MirLowerCtx<'ctx> { /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and /// `Drop` in the appropriated places. - fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<'db, ()> { let l = self.binding_local(b)?; self.push_storage_live_for_local(l, current, MirSpan::BindingId(b)) } @@ -1748,13 +1756,13 @@ impl<'ctx> MirLowerCtx<'ctx> { l: LocalId, current: BasicBlockId, span: MirSpan, - ) -> Result<()> { + ) -> Result<'db, ()> { self.drop_scopes.last_mut().unwrap().locals.push(l); self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); Ok(()) } - fn resolve_lang_item(&self, item: LangItem) -> Result { + fn resolve_lang_item(&self, item: LangItem) -> Result<'db, LangItemTarget> { let crate_id = self.owner.module(self.db).krate(); lang_item(self.db, crate_id, item).ok_or(MirLowerError::LangItemNotFound(item)) } @@ -1766,7 +1774,7 @@ impl<'ctx> MirLowerCtx<'ctx> { tail: Option, place: Place, span: MirSpan, - ) -> Result>> { + ) -> Result<'db, Option>> { let scope = self.push_drop_scope(); for statement in statements.iter() { match statement { @@ -1842,7 +1850,7 @@ impl<'ctx> MirLowerCtx<'ctx> { params: impl Iterator + Clone, self_binding: Option<(BindingId, Ty)>, pick_binding: impl Fn(BindingId) -> bool, - ) -> Result { + ) -> Result<'db, BasicBlockId> { let base_param_count = self.result.param_locals.len(); let self_binding = match self_binding { Some((self_binding, ty)) => { @@ -1873,9 +1881,10 @@ impl<'ctx> MirLowerCtx<'ctx> { continue; } if !self.result.binding_locals.contains_idx(id) { - self.result - .binding_locals - .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() })); + self.result.binding_locals.insert( + id, + self.result.locals.alloc(Local { ty: self.infer[id].to_chalk(self.interner) }), + ); } } let mut current = self.result.start_block; @@ -1910,7 +1919,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(current) } - fn binding_local(&self, b: BindingId) -> Result { + fn binding_local(&self, b: BindingId) -> Result<'db, LocalId> { match self.result.binding_locals.get(b) { Some(it) => Ok(*it), None => { @@ -1922,7 +1931,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result { + fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> { let r = self.db.const_eval_discriminant(variant); match r { Ok(r) => Ok(r), @@ -1993,7 +2002,7 @@ impl<'ctx> MirLowerCtx<'ctx> { &mut self, mut current: BasicBlockId, span: MirSpan, - ) -> Result { + ) -> Result<'db, BasicBlockId> { current = self.pop_drop_scope_internal(current, span); if !self.drop_scopes.is_empty() { implementation_error!("Mismatched count between drop scope push and pops"); @@ -2021,9 +2030,14 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(db: &dyn HirDatabase, source_ty: &Ty, target_ty: &Ty) -> Result { - let from = CastTy::from_ty(db, source_ty); - let cast = CastTy::from_ty(db, target_ty); +fn cast_kind<'db>( + db: &'db dyn HirDatabase, + source_ty: &Ty, + target_ty: &Ty, +) -> Result<'db, CastKind> { + let interner = DbInterner::new_with(db, None, None); + let from = CastTy::from_ty(db, source_ty.to_nextsolver(interner)); + let cast = CastTy::from_ty(db, target_ty.to_nextsolver(interner)); Ok(match (from, cast) { (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { CastKind::PointerExposeAddress @@ -2039,31 +2053,34 @@ fn cast_kind(db: &dyn HirDatabase, source_ty: &Ty, target_ty: &Ty) -> Result( + db: &'db dyn HirDatabase, closure: InternedClosureId, -) -> Result> { +) -> Result<'db, Arc> { let InternedClosure(owner, expr) = db.lookup_intern_closure(closure); let body = db.body(owner); let infer = db.infer(owner); let Expr::Closure { args, body: root, .. } = &body[expr] else { implementation_error!("closure expression is not closure"); }; - let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else { + let crate::next_solver::TyKind::Closure(_, substs) = infer[expr].kind() else { implementation_error!("closure expression is not closure"); }; - let (captures, kind) = infer.closure_info(&closure.into()); + let (captures, kind) = infer.closure_info(closure); let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); + let substs: &Substitution = &substs.to_chalk(ctx.interner); // 0 is return local - ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); + ctx.result.locals.alloc(Local { ty: infer[*root].to_chalk(ctx.interner) }); let closure_local = ctx.result.locals.alloc(Local { ty: match kind { - FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer[expr].clone(), + FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer[expr].to_chalk(ctx.interner), FnTrait::FnMut | FnTrait::AsyncFnMut => { - TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].clone()).intern(Interner) + TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].to_chalk(ctx.interner)) + .intern(Interner) } FnTrait::Fn | FnTrait::AsyncFn => { - TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].clone()).intern(Interner) + TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].to_chalk(ctx.interner)) + .intern(Interner) } }, }); @@ -2082,7 +2099,7 @@ pub fn mir_body_for_closure_query( let current = ctx.pop_drop_scope_assert_finished(current, root.into())?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } - let mut upvar_map: FxHashMap> = FxHashMap::default(); + let mut upvar_map: FxHashMap, usize)>> = FxHashMap::default(); for (i, capture) in captures.iter().enumerate() { let local = ctx.binding_local(capture.place.local)?; upvar_map.entry(local).or_default().push((capture, i)); @@ -2144,7 +2161,10 @@ pub fn mir_body_for_closure_query( Ok(Arc::new(ctx.result)) } -pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result> { +pub fn mir_body_query<'db>( + db: &'db dyn HirDatabase, + def: DefWithBodyId, +) -> Result<'db, Arc> { let krate = def.krate(db); let edition = krate.data(db).edition; let detail = match def { @@ -2177,22 +2197,22 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result( + _db: &'db dyn HirDatabase, _def: DefWithBodyId, -) -> Result> { +) -> Result<'db, Arc> { Err(MirLowerError::Loop) } -pub fn lower_to_mir( - db: &dyn HirDatabase, +pub fn lower_to_mir<'db>( + db: &'db dyn HirDatabase, owner: DefWithBodyId, body: &Body, - infer: &InferenceResult, + infer: &InferenceResult<'db>, // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we // need to take this input explicitly. root_expr: ExprId, -) -> Result { +) -> Result<'db, MirBody> { if infer.type_mismatches().next().is_some() || infer.is_erroneous() { return Err(MirLowerError::HasErrors); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 42a14664626f0..bd7b644618e7a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -12,12 +12,12 @@ macro_rules! not_supported { }; } -impl MirLowerCtx<'_> { +impl<'db> MirLowerCtx<'_, 'db> { fn lower_expr_to_some_place_without_adjust( &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result> { + ) -> Result<'db, Option<(Place, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = @@ -32,11 +32,11 @@ impl MirLowerCtx<'_> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - adjustments: &[Adjustment], - ) -> Result> { + adjustments: &[Adjustment<'db>], + ) -> Result<'db, Option<(Place, BasicBlockId)>> { let ty = adjustments .last() - .map(|it| it.target.clone()) + .map(|it| it.target.to_chalk(self.interner)) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = @@ -52,9 +52,9 @@ impl MirLowerCtx<'_> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - adjustments: &[Adjustment], - ) -> Result> { - let try_rvalue = |this: &mut MirLowerCtx<'_>| { + adjustments: &[Adjustment<'db>], + ) -> Result<'db, Option<(Place, BasicBlockId)>> { + let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); } @@ -89,11 +89,11 @@ impl MirLowerCtx<'_> { current, r, rest.last() - .map(|it| it.target.clone()) + .map(|it| it.target.to_chalk(self.interner)) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)), - last.target.clone(), + last.target.to_chalk(self.interner), expr_id.into(), - match od.0 { + match od.0.to_chalk(self.interner) { Some(Mutability::Mut) => true, Some(Mutability::Not) => false, None => { @@ -114,7 +114,7 @@ impl MirLowerCtx<'_> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result> { + ) -> Result<'db, Option<(Place, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -126,8 +126,8 @@ impl MirLowerCtx<'_> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result> { - let try_rvalue = |this: &mut MirLowerCtx<'_>| { + ) -> Result<'db, Option<(Place, BasicBlockId)>> { + let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); } @@ -230,6 +230,7 @@ impl MirLowerCtx<'_> { "[overloaded index]".to_owned(), )); }; + let index_fn = (index_fn.0, index_fn.1.to_chalk(self.interner)); let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { @@ -285,7 +286,7 @@ impl MirLowerCtx<'_> { index_operand: Operand, span: MirSpan, index_fn: (FunctionId, Substitution), - ) -> Result> { + ) -> Result<'db, Option<(Place, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, @@ -319,7 +320,7 @@ impl MirLowerCtx<'_> { target_ty: Ty, span: MirSpan, mutability: bool, - ) -> Result> { + ) -> Result<'db, Option<(Place, BasicBlockId)>> { let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { ( Mutability::Not, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 0440d85022321..359c4fbb2e052 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -2,16 +2,17 @@ use hir_def::{AssocItemId, hir::ExprId, signatures::VariantFields}; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ BindingMode, mir::{ LocalId, MutBorrowKind, Operand, OperandKind, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, Interner, - MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Mutability, Pat, PatId, Place, - PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, - Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, - ValueNs, VariantId, + MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, + ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, Substitution, + SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, ValueNs, + VariantId, }, }, }; @@ -50,7 +51,7 @@ enum MatchingMode { Assign, } -impl MirLowerCtx<'_> { +impl<'db> MirLowerCtx<'_, 'db> { /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the @@ -66,7 +67,7 @@ impl MirLowerCtx<'_> { current_else: Option, cond_place: Place, pattern: PatId, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { let (current, current_else) = self.pattern_match_inner( current, current_else, @@ -89,7 +90,7 @@ impl MirLowerCtx<'_> { current: BasicBlockId, value: Place, pattern: PatId, - ) -> Result { + ) -> Result<'db, BasicBlockId> { let (current, _) = self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?; Ok(current) @@ -100,7 +101,7 @@ impl MirLowerCtx<'_> { id: BindingId, current: BasicBlockId, local: LocalId, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { self.pattern_match_binding( id, BindingMode::Move, @@ -118,7 +119,7 @@ impl MirLowerCtx<'_> { mut cond_place: Place, pattern: PatId, mode: MatchingMode, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); cond_place.projection = self.result.projection_store.intern( cond_place @@ -134,8 +135,8 @@ impl MirLowerCtx<'_> { Pat::Missing => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { - let subst = match self.infer[pattern].kind(Interner) { - TyKind::Tuple(_, s) => s, + let subst = match self.infer[pattern].to_chalk(self.interner).kind(Interner) { + TyKind::Tuple(_, s) => s.clone(), _ => { return Err(MirLowerError::TypeError( "non tuple type matched with tuple pattern", @@ -207,9 +208,11 @@ impl MirLowerCtx<'_> { )? } Pat::Range { start, end } => { - let mut add_check = |l: &ExprId, binop| -> Result<()> { - let lv = - self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?; + let mut add_check = |l: &ExprId, binop| -> Result<'db, ()> { + let lv = self.lower_literal_or_const_to_operand( + self.infer[pattern].to_chalk(self.interner), + l, + )?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); let discr: Place = @@ -249,7 +252,9 @@ impl MirLowerCtx<'_> { Pat::Slice { prefix, slice, suffix } => { if mode == MatchingMode::Check { // emit runtime length check for slice - if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) { + if let TyKind::Slice(_) = + self.infer[pattern].to_chalk(self.interner).kind(Interner) + { let pattern_len = prefix.len() + suffix.len(); let place_len: Place = self.temp(TyBuilder::usize(), current, pattern.into())?.into(); @@ -393,15 +398,16 @@ impl MirLowerCtx<'_> { if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) && let AssocItemId::ConstId(c) = x.0 { - break 'b (c, x.1); + break 'b (c, x.1.to_chalk(self.interner)); } if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr { break 'b (c, Substitution::empty(Interner)); } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = - self.temp(self.infer[pattern].clone(), current, pattern.into())?.into(); + let tmp: Place = self + .temp(self.infer[pattern].to_chalk(self.interner), current, pattern.into())? + .into(); let span = pattern.into(); self.lower_const( c.into(), @@ -409,7 +415,7 @@ impl MirLowerCtx<'_> { tmp, subst, span, - self.infer[pattern].clone(), + self.infer[pattern].to_chalk(self.interner), )?; let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); self.push_assignment( @@ -438,7 +444,10 @@ impl MirLowerCtx<'_> { Pat::Lit(l) => match &self.body[*l] { Expr::Literal(l) => { if mode == MatchingMode::Check { - let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?; + let c = self.lower_literal_to_operand( + self.infer[pattern].to_chalk(self.interner), + l, + )?; self.pattern_match_const(current_else, current, c, cond_place, pattern)? } else { (current, current_else) @@ -514,7 +523,7 @@ impl MirLowerCtx<'_> { span: MirSpan, current: BasicBlockId, current_else: Option, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { let target_place = self.binding_local(id)?; self.push_storage_live(id, current)?; self.push_match_assignment(current, target_place, mode, cond_place, span); @@ -536,8 +545,10 @@ impl MirLowerCtx<'_> { BindingMode::Move => { Operand { kind: OperandKind::Copy(cond_place), span: None }.into() } - BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place), - BindingMode::Ref(Mutability::Mut) => { + BindingMode::Ref(rustc_ast_ir::Mutability::Not) => { + Rvalue::Ref(BorrowKind::Shared, cond_place) + } + BindingMode::Ref(rustc_ast_ir::Mutability::Mut) => { Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) } }, @@ -552,7 +563,7 @@ impl MirLowerCtx<'_> { c: Operand, cond_place: Place, pattern: Idx, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); @@ -587,7 +598,7 @@ impl MirLowerCtx<'_> { mut current_else: Option, shape: AdtPatternShape<'_>, mode: MatchingMode, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { Ok(match variant { VariantId::EnumVariantId(v) => { if mode == MatchingMode::Check { @@ -640,7 +651,7 @@ impl MirLowerCtx<'_> { current_else: Option, cond_place: &Place, mode: MatchingMode, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { Ok(match shape { AdtPatternShape::Record { args } => { let it = args @@ -656,7 +667,7 @@ impl MirLowerCtx<'_> { x.pat, )) }) - .collect::>>()?; + .collect::>>()?; self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { @@ -684,7 +695,7 @@ impl MirLowerCtx<'_> { args: impl Iterator, cond_place: &Place, mode: MatchingMode, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { for (proj, arg) in args { let cond_place = cond_place.project(proj, &mut self.result.projection_store); (current, current_else) = @@ -702,7 +713,7 @@ impl MirLowerCtx<'_> { fields: impl DoubleEndedIterator + Clone, cond_place: &Place, mode: MatchingMode, - ) -> Result<(BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); let it = al .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index 1d7a16ed72d07..bac694eabb704 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -4,16 +4,11 @@ use span::Edition; use test_fixture::WithFixture; use triomphe::Arc; -use crate::{ - db::HirDatabase, - mir::{MirBody, MirLowerError}, - setup_tracing, - test_db::TestDB, -}; +use crate::{db::HirDatabase, mir::MirBody, setup_tracing, test_db::TestDB}; fn lower_mir( #[rust_analyzer::rust_fixture] ra_fixture: &str, -) -> FxHashMap, MirLowerError>> { +) -> FxHashMap, ()>> { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); @@ -28,7 +23,7 @@ fn lower_mir( .map(|func| { let name = db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); let mir = db.mir_body(func.into()); - (name, mir) + (name, mir.map_err(drop)) }) .collect() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index f293f38c76980..4bc81a4806ec6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -16,6 +16,8 @@ use chalk_ir::{ use hir_def::DefWithBodyId; use triomphe::Arc; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, consteval::{intern_const_scalar, unknown_const}, @@ -33,14 +35,15 @@ macro_rules! not_supported { }; } -struct Filler<'a> { - db: &'a dyn HirDatabase, - trait_env: Arc>, +struct Filler<'a, 'db> { + db: &'db dyn HirDatabase, + trait_env: Arc>, subst: &'a Substitution, generics: Option, + interner: DbInterner<'db>, } -impl FallibleTypeFolder for Filler<'_> { - type Error = MirLowerError; +impl<'a, 'db> FallibleTypeFolder for Filler<'a, 'db> { + type Error = MirLowerError<'db>; fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { self @@ -80,8 +83,13 @@ impl FallibleTypeFolder for Filler<'_> { trait_env: self.trait_env.clone(), subst: &subst, generics: Some(generics(self.db, func.into())), + interner: self.interner, }; - filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) + filler.try_fold_ty( + infer.type_of_rpit[idx.to_nextsolver(self.interner)] + .to_chalk(self.interner), + outer_binder, + ) } crate::ImplTraitId::TypeAliasImplTrait(..) => { not_supported!("type alias impl trait"); @@ -148,8 +156,8 @@ impl FallibleTypeFolder for Filler<'_> { } } -impl Filler<'_> { - fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> { +impl<'a, 'db> Filler<'a, 'db> { + fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError<'db>> { let tmp = mem::replace(ty, TyKind::Error.intern(Interner)); *ty = normalize( self.db, @@ -159,19 +167,19 @@ impl Filler<'_> { Ok(()) } - fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> { + fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError<'db>> { let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone())); *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; Ok(()) } - fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> { + fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError<'db>> { let tmp = mem::replace(ty, Substitution::empty(Interner)); *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; Ok(()) } - fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> { + fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError<'db>> { match &mut op.kind { OperandKind::Constant(c) => { match &c.data(Interner).value { @@ -222,7 +230,7 @@ impl Filler<'_> { Ok(()) } - fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> { + fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError<'db>> { for (_, l) in body.locals.iter_mut() { self.fill_ty(&mut l.ty)?; } @@ -306,9 +314,10 @@ pub fn monomorphized_mir_body_query<'db>( owner: DefWithBodyId, subst: Substitution, trait_env: Arc>, -) -> Result, MirLowerError> { +) -> Result, MirLowerError<'db>> { let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics }; + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner }; let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; @@ -320,7 +329,7 @@ pub(crate) fn monomorphized_mir_body_cycle_result<'db>( _: DefWithBodyId, _: Substitution, _: Arc>, -) -> Result, MirLowerError> { +) -> Result, MirLowerError<'db>> { Err(MirLowerError::Loop) } @@ -329,10 +338,11 @@ pub fn monomorphized_mir_body_for_closure_query<'db>( closure: InternedClosureId, subst: Substitution, trait_env: Arc>, -) -> Result, MirLowerError> { +) -> Result, MirLowerError<'db>> { let InternedClosure(owner, _) = db.lookup_intern_closure(closure); let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics }; + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner }; let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index ab167e88af2ef..776e0d956f404 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -33,6 +33,9 @@ pub use region::*; pub use solver::*; pub use ty::*; +pub use crate::lower_nextsolver::ImplTraitIdx; +pub use rustc_ast_ir::Mutability; + pub type Binder<'db, T> = rustc_type_ir::Binder, T>; pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder, T>; pub type Canonical<'db, T> = rustc_type_ir::Canonical, T>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 1ae59beca2728..918a311ea9ccc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -139,6 +139,17 @@ impl TryFrom for GenericDefId { } } +impl SolverDefId { + #[inline] + #[track_caller] + pub fn expect_opaque_ty(self) -> InternedOpaqueTyId { + match self { + SolverDefId::InternedOpaqueTyId(it) => it, + _ => panic!("expected opaque type, found {self:?}"), + } + } +} + impl<'db> inherent::DefId> for SolverDefId { fn as_local(self) -> Option { Some(self) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index 405a57d9e898c..a42fdb0943041 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -129,3 +129,26 @@ where if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } } + +pub fn fold_tys<'db, T: TypeFoldable>>( + interner: DbInterner<'db>, + t: T, + callback: impl FnMut(Ty<'db>) -> Ty<'db>, +) -> T { + struct Folder<'db, F> { + interner: DbInterner<'db>, + callback: F, + } + impl<'db, F: FnMut(Ty<'db>) -> Ty<'db>> TypeFolder> for Folder<'db, F> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + let t = t.super_fold_with(self); + (self.callback)(t) + } + } + + t.fold_with(&mut Folder { interner, callback }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 097bb85cbd491..89a4d9202a75d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -1,7 +1,8 @@ //! Things related to generic args in the next-trait-solver. -use hir_def::GenericParamId; +use hir_def::{GenericDefId, GenericParamId}; use intern::{Interned, Symbol}; +use rustc_type_ir::inherent::Const as _; use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, @@ -216,6 +217,59 @@ impl<'db> GenericArgs<'db> { interner.mk_args(&args) } + /// Like `for_item`, but prefers the default of a parameter if it has any. + pub fn for_item_with_defaults( + interner: DbInterner<'db>, + def_id: GenericDefId, + mut fallback: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let defaults = interner.db.generic_defaults_ns(def_id); + Self::for_item(interner, def_id.into(), |name, idx, id, prev| { + match defaults.get(idx as usize) { + Some(default) => default.instantiate(interner, prev), + None => fallback(name, idx, id, prev), + } + }) + } + + /// Like `for_item()`, but calls first uses the args from `first`. + pub fn fill_rest( + interner: DbInterner<'db>, + def_id: SolverDefId, + first: impl IntoIterator>, + mut fallback: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let mut iter = first.into_iter(); + Self::for_item(interner, def_id, |name, idx, id, prev| { + iter.next().unwrap_or_else(|| fallback(name, idx, id, prev)) + }) + } + + /// Appends default param values to `first` if needed. Params without default will call `fallback()`. + pub fn fill_with_defaults( + interner: DbInterner<'db>, + def_id: GenericDefId, + first: impl IntoIterator>, + mut fallback: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let defaults = interner.db.generic_defaults_ns(def_id); + Self::fill_rest(interner, def_id.into(), first, |name, idx, id, prev| { + defaults + .get(idx as usize) + .map(|default| default.instantiate(interner, prev)) + .unwrap_or_else(|| fallback(name, idx, id, prev)) + }) + } + fn fill_item( args: &mut SmallVec<[GenericArg<'db>; 8]>, interner: DbInterner<'_>, @@ -271,6 +325,18 @@ impl<'db> GenericArgs<'db> { } } } + + pub fn types(self) -> impl Iterator> { + self.iter().filter_map(|it| it.as_type()) + } + + pub fn consts(self) -> impl Iterator> { + self.iter().filter_map(|it| it.as_const()) + } + + pub fn regions(self) -> impl Iterator> { + self.iter().filter_map(|it| it.as_region()) + } } impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 8e922abacb206..1bb6934e073ed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -10,6 +10,7 @@ pub use at::DefineOpaqueTypes; use ena::undo_log::UndoLogs; use ena::unify as ut; use hir_def::GenericParamId; +use hir_def::lang_item::LangItem; use intern::Symbol; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{ @@ -18,6 +19,7 @@ use region_constraints::{ pub use relate::StructurallyRelateAliases; pub use relate::combine::PredicateEmittingRelation; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use rustc_pattern_analysis::Captures; use rustc_type_ir::error::{ExpectedFound, TypeError}; use rustc_type_ir::inherent::{ @@ -38,7 +40,10 @@ use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::next_solver::fold::BoundVarReplacerDelegate; use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; -use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind}; +use crate::next_solver::infer::select::EvaluationResult; +use crate::next_solver::infer::traits::PredicateObligation; +use crate::next_solver::obligation_ctxt::ObligationCtxt; +use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext}; use super::generics::GenericParamDef; use super::{ @@ -62,7 +67,7 @@ pub(crate) mod traits; mod type_variable; mod unify_key; -/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper +/// `InferOk<'db, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations`, but it has one important property: /// because `InferOk` is marked with `#[must_use]`, if you have a method /// `InferCtxt::f` that returns `InferResult<()>` and you call it with @@ -395,6 +400,102 @@ impl<'db> InferCtxt<'db> { self.typing_mode } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + pub fn predicate_may_hold_opaque_types_jank( + &self, + obligation: &PredicateObligation<'db>, + ) -> bool { + <&SolverContext<'db>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new( + self.interner, + obligation.param_env, + obligation.predicate, + )) + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version may conservatively fail when outlives obligations + /// are required. Therefore, this version should only be used for + /// optimizations or diagnostics and be treated as if it can always + /// return `false`. + /// + /// # Example + /// + /// ``` + /// # #![allow(dead_code)] + /// trait Trait {} + /// + /// fn check() {} + /// + /// fn foo() + /// where + /// &'static T: Trait, + /// { + /// // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation, + /// // which means that `predicate_must_hold_considering_regions` will return + /// // `false`. + /// check::<&'_ T>(); + /// } + /// ``` + fn predicate_must_hold_considering_regions( + &self, + obligation: &PredicateObligation<'db>, + ) -> bool { + self.evaluate_obligation(obligation).must_apply_considering_regions() + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version ignores all outlives constraints. + fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'db>) -> bool { + self.evaluate_obligation(obligation).must_apply_modulo_regions() + } + + /// Evaluate a given predicate, capturing overflow and propagating it back. + fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult { + let param_env = obligation.param_env; + + self.probe(|snapshot| { + let mut ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation.clone()); + let mut result = EvaluationResult::EvaluatedToOk; + for error in ocx.select_all_or_error() { + if error.is_true_error() { + return EvaluationResult::EvaluatedToErr; + } else { + result = result.max(EvaluationResult::EvaluatedToAmbig); + } + } + if self.opaque_types_added_in_snapshot(snapshot) { + result = result.max(EvaluationResult::EvaluatedToOkModuloOpaqueTypes); + } else if self.region_constraints_added_in_snapshot(snapshot) { + result = result.max(EvaluationResult::EvaluatedToOkModuloRegions); + } + result + }) + } + + pub fn type_is_copy_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { + let ty = self.resolve_vars_if_possible(ty); + + let Some(copy_def_id) = + LangItem::Copy.resolve_trait(self.interner.db, self.interner.krate.unwrap()) + else { + return false; + }; + + // This can get called from typeck (by euv), and `moves_by_default` + // rightly refuses to work with inference variables, but + // moves_by_default has a cache, which we want to use in other + // cases. + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) + } + pub fn unresolved_variables(&self) -> Vec> { let mut inner = self.inner.borrow_mut(); let mut vars: Vec> = inner @@ -682,6 +783,17 @@ impl<'db> InferCtxt<'db> { }) } + /// Like `fresh_args_for_item()`, but first uses the args from `first`. + pub fn fill_rest_fresh_args( + &self, + def_id: SolverDefId, + first: impl IntoIterator>, + ) -> GenericArgs<'db> { + GenericArgs::fill_rest(self.interner, def_id, first, |name, index, kind, _| { + self.var_for_def(kind, name) + }) + } + /// Returns `true` if errors have been reported since this infcx was /// created. This is sometimes used as a heuristic to skip /// reporting errors that often occur as a result of earlier diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs index 84338ade6e354..4bd3fbd4985d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs @@ -2,11 +2,12 @@ use rustc_type_ir::{ ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable, - TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap, - inherent::IntoKind, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, + data_structures::DelayedMap, + inherent::{Const as _, IntoKind, Ty as _}, }; -use crate::next_solver::{Const, DbInterner, Region, Ty}; +use crate::next_solver::{Const, DbInterner, ErrorGuaranteed, Region, Ty}; use super::{FixupError, FixupResult, InferCtxt}; @@ -60,3 +61,48 @@ impl<'a, 'db> TypeFolder> for OpportunisticVarResolver<'a, 'db> } } } + +pub struct ReplaceInferWithError<'db> { + interner: DbInterner<'db>, +} + +impl<'db> ReplaceInferWithError<'db> { + #[inline] + pub fn new(interner: DbInterner<'db>) -> Self { + Self { interner } + } +} + +impl<'db> TypeFolder> for ReplaceInferWithError<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_infer() { + return t; + } + + if t.is_infer() { + Ty::new_error(self.interner, ErrorGuaranteed) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_infer() { + return c; + } + + if c.is_ct_infer() { + Const::new_error(self.interner, ErrorGuaranteed) + } else { + c.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { Region::error(self.interner) } else { r } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index 4f111fa662668..392e2b9329a04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -12,6 +12,7 @@ use crate::{ Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError, infer::{ InferCtxt, + select::EvaluationResult::*, traits::{Obligation, ObligationCause, PredicateObligation, TraitObligation}, }, inspect::{InspectCandidate, InspectGoal, ProofTreeVisitor}, @@ -47,6 +48,83 @@ pub enum NotConstEvaluatable { MentionsParam, } +/// The result of trait evaluation. The order is important +/// here as the evaluation of a list is the maximum of the +/// evaluations. +/// +/// The evaluation results are ordered: +/// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` +/// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent` +/// - the "union" of evaluation results is equal to their maximum - +/// all the "potential success" candidates can potentially succeed, +/// so they are noops when unioned with a definite error, and within +/// the categories it's easy to see that the unions are correct. +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +pub enum EvaluationResult { + /// Evaluation successful. + EvaluatedToOk, + /// Evaluation successful, but there were unevaluated region obligations. + EvaluatedToOkModuloRegions, + /// Evaluation successful, but need to rerun because opaque types got + /// hidden types assigned without it being known whether the opaque types + /// are within their defining scope + EvaluatedToOkModuloOpaqueTypes, + /// Evaluation is known to be ambiguous -- it *might* hold for some + /// assignment of inference variables, but it might not. + /// + /// While this has the same meaning as `EvaluatedToAmbigStackDependent` -- we can't + /// know whether this obligation holds or not -- it is the result we + /// would get with an empty stack, and therefore is cacheable. + EvaluatedToAmbig, + /// Evaluation failed because of recursion involving inference + /// variables. We are somewhat imprecise there, so we don't actually + /// know the real result. + /// + /// This can't be trivially cached because the result depends on the + /// stack results. + EvaluatedToAmbigStackDependent, + /// Evaluation failed. + EvaluatedToErr, +} + +impl EvaluationResult { + /// Returns `true` if this evaluation result is known to apply, even + /// considering outlives constraints. + pub fn must_apply_considering_regions(self) -> bool { + self == EvaluatedToOk + } + + /// Returns `true` if this evaluation result is known to apply, ignoring + /// outlives constraints. + pub fn must_apply_modulo_regions(self) -> bool { + self <= EvaluatedToOkModuloRegions + } + + pub fn may_apply(self) -> bool { + match self { + EvaluatedToOkModuloOpaqueTypes + | EvaluatedToOk + | EvaluatedToOkModuloRegions + | EvaluatedToAmbig + | EvaluatedToAmbigStackDependent => true, + + EvaluatedToErr => false, + } + } + + pub fn is_stack_dependent(self) -> bool { + match self { + EvaluatedToAmbigStackDependent => true, + + EvaluatedToOkModuloOpaqueTypes + | EvaluatedToOk + | EvaluatedToOkModuloRegions + | EvaluatedToAmbig + | EvaluatedToErr => false, + } + } +} + /// Indicates that trait evaluation caused overflow and in which pass. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum OverflowError { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 7b9ca96c51406..705aa43fb1199 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -109,4 +109,17 @@ impl<'db> InferCtxt<'db> { self.rollback_to(snapshot); r } + + /// Scan the constraints produced since `snapshot` and check whether + /// we added any region constraints. + pub fn region_constraints_added_in_snapshot(&self, snapshot: &CombinedSnapshot) -> bool { + self.inner + .borrow_mut() + .unwrap_region_constraints() + .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) + } + + pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot) -> bool { + self.inner.borrow().undo_log.opaque_types_in_snapshot(&snapshot.undo_snapshot) + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 68aa12d7bb0cb..9fa1fa7fb4404 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -7,16 +7,18 @@ use std::{ hash::{Hash, Hasher}, }; +use hir_def::TraitId; use rustc_type_ir::elaborate::Elaboratable; use rustc_type_ir::{ PredicatePolarity, Upcast, solve::{Certainty, NoSolution}, }; use rustc_type_ir::{TypeFoldable, TypeVisitable}; +use tracing::debug; use crate::next_solver::{ Binder, Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, Span, - TraitPredicate, Ty, + TraitPredicate, TraitRef, Ty, }; use super::InferCtxt; @@ -237,3 +239,35 @@ impl<'db, O> Obligation<'db, O> { Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) } } + +/// Determines whether the type `ty` is known to meet `bound` and +/// returns true if so. Returns false if `ty` either does not meet +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). +pub fn type_known_to_meet_bound_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: TraitId, +) -> bool { + let trait_ref = TraitRef::new(infcx.interner, def_id.into(), [ty]); + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) +} + +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +fn pred_known_to_hold_modulo_regions<'db>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + pred: impl Upcast, Predicate<'db>>, +) -> bool { + let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), param_env, pred); + + let result = infcx.evaluate_obligation(&obligation); + debug!(?result); + + result.must_apply_modulo_regions() +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index b72504a19cf0f..6b91ee35eba3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1616,7 +1616,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { mut f: impl FnMut(Self::ImplId), ) { let trait_ = trait_.0; - let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); + let self_ty_fp = TyFingerprint::for_trait_impl(self_ty); let fps: &[TyFingerprint] = match self_ty.kind() { TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS, @@ -1907,7 +1907,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let infer = self.db().infer(func.into()); - EarlyBinder::bind(infer.type_of_rpit[idx].to_nextsolver(self)) + EarlyBinder::bind(infer.type_of_rpit[idx.to_nextsolver(self)]) } crate::ImplTraitId::TypeAliasImplTrait(..) | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index f3f74f67c04de..b32a5ec2928bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -143,10 +143,29 @@ pub trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; } +impl<'db, A, OutA, B, OutB> ChalkToNextSolver<'db, (OutA, OutB)> for (A, B) +where + A: ChalkToNextSolver<'db, OutA>, + B: ChalkToNextSolver<'db, OutB>, +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> (OutA, OutB) { + (self.0.to_nextsolver(interner), self.1.to_nextsolver(interner)) + } +} + pub trait NextSolverToChalk<'db, Out> { fn to_chalk(self, interner: DbInterner<'db>) -> Out; } +impl<'db, T, Out> NextSolverToChalk<'db, Option> for Option +where + T: NextSolverToChalk<'db, Out>, +{ + fn to_chalk(self, interner: DbInterner<'db>) -> Option { + self.map(|it| it.to_chalk(interner)) + } +} + impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Mutability { match self { @@ -633,6 +652,16 @@ impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg NextSolverToChalk<'db, crate::GenericArg> for GenericArg<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::GenericArg { + match self { + GenericArg::Ty(ty) => ty.to_chalk(interner).cast(Interner), + GenericArg::Lifetime(region) => region.to_chalk(interner).cast(Interner), + GenericArg::Const(konst) => konst.to_chalk(interner).cast(Interner), + } + } +} + impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { GenericArgs::new_from_iter( @@ -642,6 +671,17 @@ impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution ChalkToNextSolver<'db, crate::lower_nextsolver::ImplTraitIdx<'db>> + for crate::ImplTraitIdx +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> crate::lower_nextsolver::ImplTraitIdx<'db> { + crate::lower_nextsolver::ImplTraitIdx::from_raw(self.into_raw()) + } +} + impl<'db> NextSolverToChalk<'db, chalk_ir::Substitution> for GenericArgs<'db> { fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Substitution { convert_args_for_result(interner, self.as_slice()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index 0bfd2b8003d05..32c30d19c7849 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -17,12 +17,18 @@ use super::{ pub type RegionKind<'db> = rustc_type_ir::RegionKind>; -#[salsa::interned(constructor = new_, debug)] +#[salsa::interned(constructor = new_)] pub struct Region<'db> { #[returns(ref)] kind_: RegionKind<'db>, } +impl std::fmt::Debug for Region<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.kind().fmt(f) + } +} + impl<'db> Region<'db> { pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { Region::new_(interner.db(), kind) @@ -69,6 +75,10 @@ impl<'db> Region<'db> { matches!(self.inner(), RegionKind::ReVar(_)) } + pub fn is_error(&self) -> bool { + matches!(self.inner(), RegionKind::ReError(_)) + } + pub fn error(interner: DbInterner<'db>) -> Self { Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index a25996ab485c9..5ccd84af8d2f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -3,7 +3,8 @@ use std::iter; use std::ops::ControlFlow; -use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; +use hir_def::type_ref::Rawness; +use hir_def::{AdtId, GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; @@ -13,7 +14,7 @@ use rustc_type_ir::{ IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo, inherent::{ - Abi, AdtDef, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, + Abi, AdtDef as _, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, @@ -23,6 +24,7 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; +use crate::next_solver::{AdtDef, Binder}; use crate::{ FnAbi, db::HirDatabase, @@ -75,6 +77,10 @@ impl<'db> Ty<'db> { .unwrap() } + pub fn new_adt(interner: DbInterner<'db>, adt_id: AdtId, args: GenericArgs<'db>) -> Self { + Ty::new(interner, TyKind::Adt(AdtDef::new(adt_id, interner), args)) + } + pub fn new_param(interner: DbInterner<'db>, id: TypeParamId, index: u32, name: Symbol) -> Self { Ty::new(interner, TyKind::Param(ParamTy { id, index })) } @@ -337,6 +343,23 @@ impl<'db> Ty<'db> { matches!(self.kind(), TyKind::Tuple(tys) if tys.inner().is_empty()) } + #[inline] + pub fn is_raw_ptr(self) -> bool { + matches!(self.kind(), TyKind::RawPtr(..)) + } + + pub fn is_union(self) -> bool { + self.as_adt().is_some_and(|(adt, _)| matches!(adt, AdtId::UnionId(_))) + } + + #[inline] + pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> { + match self.kind() { + TyKind::Adt(adt_def, args) => Some((adt_def.def_id().0, args)), + _ => None, + } + } + #[inline] pub fn ty_vid(self) -> Option { match self.kind() { @@ -372,6 +395,38 @@ impl<'db> Ty<'db> { pub fn references_non_lt_error(self) -> bool { self.references_error() && self.visit_with(&mut ReferencesNonLifetimeError).is_break() } + + pub fn callable_sig(self, interner: DbInterner<'db>) -> Option>> { + match self.kind() { + TyKind::FnDef(callable, args) => { + Some(interner.fn_sig(callable).instantiate(interner, args)) + } + TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)), + TyKind::Closure(closure_id, closure_args) => closure_args + .split_closure_args_untupled() + .closure_sig_as_fn_ptr_ty + .callable_sig(interner), + _ => None, + } + } + + pub fn as_reference_or_ptr(self) -> Option<(Ty<'db>, Rawness, Mutability)> { + match self.kind() { + TyKind::Ref(_, ty, mutability) => Some((ty, Rawness::Ref, mutability)), + TyKind::RawPtr(ty, mutability) => Some((ty, Rawness::RawPtr, mutability)), + _ => None, + } + } + + /// Replace infer vars with errors. + /// + /// This needs to be called for every type that may contain infer vars and is yielded to outside inference, + /// as things other than inference do not expect to see infer vars. + pub fn replace_infer_with_error(self, interner: DbInterner<'db>) -> Ty<'db> { + self.fold_with(&mut crate::next_solver::infer::resolve::ReplaceInferWithError::new( + interner, + )) + } } struct ReferencesNonLifetimeError; @@ -928,11 +983,17 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { interned_vec_db!(Tys, Ty); +impl<'db> Tys<'db> { + pub fn inputs(&self) -> &[Ty<'db>] { + self.as_slice().split_last().unwrap().1 + } +} + impl<'db> rustc_type_ir::inherent::Tys> for Tys<'db> { fn inputs(self) -> as rustc_type_ir::Interner>::FnInputTys { Tys::new_from_iter( DbInterner::conjure(), - self.as_slice().split_last().unwrap().1.iter().cloned(), + self.as_slice().split_last().unwrap().1.iter().copied(), ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index a7f9817f9c08e..750d09e1a739c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -413,7 +413,7 @@ pub(crate) fn for_trait_impls( let trait_module = trait_id.module(db); let type_module = match self_ty_fp { Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), - Some(TyFingerprint::ForeignType(type_id)) => Some(from_foreign_def_id(type_id).module(db)), + Some(TyFingerprint::ForeignType(type_id)) => Some(type_id.module(db)), Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)), _ => None, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 1c3da438cb364..5dd9ab75326dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -36,10 +36,11 @@ use test_fixture::WithFixture; use triomphe::Arc; use crate::{ - InferenceResult, Ty, + InferenceResult, db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::{Adjustment, TypeMismatch}, + next_solver::Ty, setup_tracing, test_db::TestDB, }; @@ -78,172 +79,172 @@ fn check_impl( let _tracing = setup_tracing(); let (db, files) = TestDB::with_many_files(ra_fixture); - let mut had_annotations = false; - let mut mismatches = FxHashMap::default(); - let mut types = FxHashMap::default(); - let mut adjustments = FxHashMap::default(); - for (file_id, annotations) in db.extract_annotations() { - for (range, expected) in annotations { - let file_range = FileRange { file_id, range }; - if only_types { - types.insert(file_range, expected); - } else if expected.starts_with("type: ") { - types.insert(file_range, expected.trim_start_matches("type: ").to_owned()); - } else if expected.starts_with("expected") { - mismatches.insert(file_range, expected); - } else if expected.starts_with("adjustments:") { - adjustments.insert( - file_range, - expected.trim_start_matches("adjustments:").trim().to_owned(), - ); - } else { - panic!("unexpected annotation: {expected} @ {range:?}"); + salsa::attach(&db, || { + let mut had_annotations = false; + let mut mismatches = FxHashMap::default(); + let mut types = FxHashMap::default(); + let mut adjustments = FxHashMap::default(); + for (file_id, annotations) in db.extract_annotations() { + for (range, expected) in annotations { + let file_range = FileRange { file_id, range }; + if only_types { + types.insert(file_range, expected); + } else if expected.starts_with("type: ") { + types.insert(file_range, expected.trim_start_matches("type: ").to_owned()); + } else if expected.starts_with("expected") { + mismatches.insert(file_range, expected); + } else if expected.starts_with("adjustments:") { + adjustments.insert( + file_range, + expected.trim_start_matches("adjustments:").trim().to_owned(), + ); + } else { + panic!("unexpected annotation: {expected} @ {range:?}"); + } + had_annotations = true; } - had_annotations = true; } - } - assert!(had_annotations || allow_none, "no `//^` annotations found"); - - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - for file_id in files { - let module = db.module_for_file_opt(file_id.file_id(&db)); - let module = match module { - Some(m) => m, - None => continue, - }; - let def_map = module.def_map(&db); - visit_module(&db, def_map, module.local_id, &mut |it| { - let def = match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, + assert!(had_annotations || allow_none, "no `//^` annotations found"); + + let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); + for file_id in files { + let module = db.module_for_file_opt(file_id.file_id(&db)); + let module = match module { + Some(m) => m, + None => continue, }; - defs.push((def, module.krate())) - }); - } - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() + let def_map = module.def_map(&db); + visit_module(&db, def_map, module.local_id, &mut |it| { + let def = match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }; + defs.push((def, module.krate())) + }); } - }); - let mut unexpected_type_mismatches = String::new(); - for (def, krate) in defs { - let display_target = DisplayTarget::from_crate(&db, krate); - let (body, body_source_map) = db.body_with_source_map(def); - let inference_result = db.infer(def); - - for (pat, mut ty) in inference_result.type_of_pat.iter() { - if let Pat::Bind { id, .. } = body[pat] { - ty = &inference_result.type_of_binding[id]; + defs.sort_by_key(|(def, _)| match def { + DefWithBodyId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() } - let node = match pat_node(&body_source_map, pat, &db) { - Some(value) => value, - None => continue, - }; - let range = node.as_ref().original_file_range_rooted(&db); - if let Some(expected) = types.remove(&range) { - let actual = salsa::attach(&db, || { - if display_source { + DefWithBodyId::ConstId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + DefWithBodyId::StaticId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + DefWithBodyId::VariantId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + }); + let mut unexpected_type_mismatches = String::new(); + for (def, krate) in defs { + let display_target = DisplayTarget::from_crate(&db, krate); + let (body, body_source_map) = db.body_with_source_map(def); + let inference_result = db.infer(def); + + for (pat, mut ty) in inference_result.type_of_pat.iter() { + if let Pat::Bind { id, .. } = body[pat] { + ty = &inference_result.type_of_binding[id]; + } + let node = match pat_node(&body_source_map, pat, &db) { + Some(value) => value, + None => continue, + }; + let range = node.as_ref().original_file_range_rooted(&db); + if let Some(expected) = types.remove(&range) { + let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() } else { ty.display_test(&db, display_target).to_string() - } - }); - assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); + }; + assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); + } } - } - for (expr, ty) in inference_result.type_of_expr.iter() { - let node = match expr_node(&body_source_map, expr, &db) { - Some(value) => value, - None => continue, - }; - let range = node.as_ref().original_file_range_rooted(&db); - if let Some(expected) = types.remove(&range) { - let actual = salsa::attach(&db, || { - if display_source { + for (expr, ty) in inference_result.type_of_expr.iter() { + let node = match expr_node(&body_source_map, expr, &db) { + Some(value) => value, + None => continue, + }; + let range = node.as_ref().original_file_range_rooted(&db); + if let Some(expected) = types.remove(&range) { + let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() } else { ty.display_test(&db, display_target).to_string() - } - }); - assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); - } - if let Some(expected) = adjustments.remove(&range) { - let adjustments = inference_result - .expr_adjustments - .get(&expr) - .map_or_else(Default::default, |it| &**it); - assert_eq!( - expected, - adjustments - .iter() - .map(|Adjustment { kind, .. }| format!("{kind:?}")) - .join(", ") - ); + }; + assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); + } + if let Some(expected) = adjustments.remove(&range) { + let adjustments = inference_result + .expr_adjustments + .get(&expr) + .map_or_else(Default::default, |it| &**it); + assert_eq!( + expected, + adjustments + .iter() + .map(|Adjustment { kind, .. }| format!("{kind:?}")) + .join(", ") + ); + } } - } - for (expr_or_pat, mismatch) in inference_result.type_mismatches() { - let Some(node) = (match expr_or_pat { - hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), - hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), - }) else { - continue; - }; - let range = node.as_ref().original_file_range_rooted(&db); - let actual = salsa::attach(&db, || { - format!( + for (expr_or_pat, mismatch) in inference_result.type_mismatches() { + let Some(node) = (match expr_or_pat { + hir_def::hir::ExprOrPatId::ExprId(expr) => { + expr_node(&body_source_map, expr, &db) + } + hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), + }) else { + continue; + }; + let range = node.as_ref().original_file_range_rooted(&db); + let actual = format!( "expected {}, got {}", mismatch.expected.display_test(&db, display_target), mismatch.actual.display_test(&db, display_target) - ) - }); - match mismatches.remove(&range) { - Some(annotation) => assert_eq!(actual, annotation), - None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual), + ); + match mismatches.remove(&range) { + Some(annotation) => assert_eq!(actual, annotation), + None => { + format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual) + } + } } } - } - let mut buf = String::new(); - if !unexpected_type_mismatches.is_empty() { - format_to!(buf, "Unexpected type mismatches:\n{}", unexpected_type_mismatches); - } - if !mismatches.is_empty() { - format_to!(buf, "Unchecked mismatch annotations:\n"); - for m in mismatches { - format_to!(buf, "{:?}: {}\n", m.0.range, m.1); + let mut buf = String::new(); + if !unexpected_type_mismatches.is_empty() { + format_to!(buf, "Unexpected type mismatches:\n{}", unexpected_type_mismatches); } - } - if !types.is_empty() { - format_to!(buf, "Unchecked type annotations:\n"); - for t in types { - format_to!(buf, "{:?}: type {}\n", t.0.range, t.1); + if !mismatches.is_empty() { + format_to!(buf, "Unchecked mismatch annotations:\n"); + for m in mismatches { + format_to!(buf, "{:?}: {}\n", m.0.range, m.1); + } } - } - if !adjustments.is_empty() { - format_to!(buf, "Unchecked adjustments annotations:\n"); - for t in adjustments { - format_to!(buf, "{:?}: type {:?}\n", t.0.range, t.1); + if !types.is_empty() { + format_to!(buf, "Unchecked type annotations:\n"); + for t in types { + format_to!(buf, "{:?}: type {}\n", t.0.range, t.1); + } } - } - assert!(buf.is_empty(), "{}", buf); + if !adjustments.is_empty() { + format_to!(buf, "Unchecked adjustments annotations:\n"); + for t in adjustments { + format_to!(buf, "{:?}: type {:?}\n", t.0.range, t.1); + } + } + assert!(buf.is_empty(), "{}", buf); + }); } fn expr_node( @@ -282,139 +283,140 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(content); - let mut buf = String::new(); - - let mut infer_def = |inference_result: Arc, - body: Arc, - body_source_map: Arc, - krate: Crate| { - let display_target = DisplayTarget::from_crate(&db, krate); - let mut types: Vec<(InFile, &Ty)> = Vec::new(); - let mut mismatches: Vec<(InFile, &TypeMismatch)> = Vec::new(); - - if let Some(self_param) = body.self_param { - let ty = &inference_result.type_of_binding[self_param]; - if let Some(syntax_ptr) = body_source_map.self_param_syntax() { - let root = db.parse_or_expand(syntax_ptr.file_id); - let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); - types.push((node, ty)); + salsa::attach(&db, || { + let mut buf = String::new(); + + let mut infer_def = |inference_result: Arc>, + body: Arc, + body_source_map: Arc, + krate: Crate| { + let display_target = DisplayTarget::from_crate(&db, krate); + let mut types: Vec<(InFile, &Ty<'_>)> = Vec::new(); + let mut mismatches: Vec<(InFile, &TypeMismatch<'_>)> = Vec::new(); + + if let Some(self_param) = body.self_param { + let ty = &inference_result.type_of_binding[self_param]; + if let Some(syntax_ptr) = body_source_map.self_param_syntax() { + let root = db.parse_or_expand(syntax_ptr.file_id); + let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); + types.push((node, ty)); + } } - } - for (pat, mut ty) in inference_result.type_of_pat.iter() { - if let Pat::Bind { id, .. } = body[pat] { - ty = &inference_result.type_of_binding[id]; - } - let node = match body_source_map.pat_syntax(pat) { - Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| ptr.to_node(&root).syntax().clone()) + for (pat, mut ty) in inference_result.type_of_pat.iter() { + if let Pat::Bind { id, .. } = body[pat] { + ty = &inference_result.type_of_binding[id]; + } + let node = match body_source_map.pat_syntax(pat) { + Ok(sp) => { + let root = db.parse_or_expand(sp.file_id); + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) + } + Err(SyntheticSyntax) => continue, + }; + types.push((node.clone(), ty)); + if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { + mismatches.push((node, mismatch)); } - Err(SyntheticSyntax) => continue, - }; - types.push((node.clone(), ty)); - if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { - mismatches.push((node, mismatch)); } - } - for (expr, ty) in inference_result.type_of_expr.iter() { - let node = match body_source_map.expr_syntax(expr) { - Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| ptr.to_node(&root).syntax().clone()) + for (expr, ty) in inference_result.type_of_expr.iter() { + let node = match body_source_map.expr_syntax(expr) { + Ok(sp) => { + let root = db.parse_or_expand(sp.file_id); + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) + } + Err(SyntheticSyntax) => continue, + }; + types.push((node.clone(), ty)); + if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { + mismatches.push((node, mismatch)); } - Err(SyntheticSyntax) => continue, - }; - types.push((node.clone(), ty)); - if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { - mismatches.push((node, mismatch)); } - } - // sort ranges for consistency - types.sort_by_key(|(node, _)| { - let range = node.value.text_range(); - (range.start(), range.end()) - }); - for (node, ty) in &types { - let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) { - (self_param.name().unwrap().syntax().text_range(), "self".to_owned()) - } else { - (node.value.text_range(), node.value.text().to_string().replace('\n', " ")) - }; - let macro_prefix = if node.file_id != file_id { "!" } else { "" }; - format_to!( - buf, - "{}{:?} '{}': {}\n", - macro_prefix, - range, - ellipsize(text, 15), - ty.display_test(&db, display_target) - ); - } - if include_mismatches { - mismatches.sort_by_key(|(node, _)| { + // sort ranges for consistency + types.sort_by_key(|(node, _)| { let range = node.value.text_range(); (range.start(), range.end()) }); - for (src_ptr, mismatch) in &mismatches { - let range = src_ptr.value.text_range(); - let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" }; + for (node, ty) in &types { + let (range, text) = + if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) { + (self_param.name().unwrap().syntax().text_range(), "self".to_owned()) + } else { + (node.value.text_range(), node.value.text().to_string().replace('\n', " ")) + }; + let macro_prefix = if node.file_id != file_id { "!" } else { "" }; format_to!( buf, - "{}{:?}: expected {}, got {}\n", + "{}{:?} '{}': {}\n", macro_prefix, range, - mismatch.expected.display_test(&db, display_target), - mismatch.actual.display_test(&db, display_target), + ellipsize(text, 15), + ty.display_test(&db, display_target) ); } - } - }; + if include_mismatches { + mismatches.sort_by_key(|(node, _)| { + let range = node.value.text_range(); + (range.start(), range.end()) + }); + for (src_ptr, mismatch) in &mismatches { + let range = src_ptr.value.text_range(); + let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" }; + format_to!( + buf, + "{}{:?}: expected {}, got {}\n", + macro_prefix, + range, + mismatch.expected.display_test(&db, display_target), + mismatch.actual.display_test(&db, display_target), + ); + } + } + }; - let module = db.module_for_file(file_id.file_id(&db)); - let def_map = module.def_map(&db); + let module = db.module_for_file(file_id.file_id(&db)); + let def_map = module.def_map(&db); - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - visit_module(&db, def_map, module.local_id, &mut |it| { - let def = match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }; - defs.push((def, module.krate())) - }); - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - }); - for (def, krate) in defs { - let (body, source_map) = db.body_with_source_map(def); - let infer = db.infer(def); - salsa::attach(&db, || { + let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); + visit_module(&db, def_map, module.local_id, &mut |it| { + let def = match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }; + defs.push((def, module.krate())) + }); + defs.sort_by_key(|(def, _)| match def { + DefWithBodyId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + DefWithBodyId::ConstId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + DefWithBodyId::StaticId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + DefWithBodyId::VariantId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.syntax().text_range().start() + } + }); + for (def, krate) in defs { + let (body, source_map) = db.body_with_source_map(def); + let infer = db.infer(def); infer_def(infer, body, source_map, krate); - }) - } + } - buf.truncate(buf.trim_end().len()); - buf + buf.truncate(buf.trim_end().len()); + buf + }) } pub(crate) fn visit_module( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index b001ac1e82ea2..d3bc26abd7b0a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -2,15 +2,16 @@ use expect_test::{Expect, expect}; use hir_def::db::DefDatabase; use hir_expand::{HirFileId, files::InFileWrapper}; use itertools::Itertools; -use salsa::plumbing::FromId; use span::TextRange; use syntax::{AstNode, AstPtr}; use test_fixture::WithFixture; -use crate::db::{HirDatabase, InternedClosureId}; -use crate::display::{DisplayTarget, HirDisplay}; -use crate::mir::MirSpan; -use crate::test_db::TestDB; +use crate::{ + db::HirDatabase, + display::{DisplayTarget, HirDisplay}, + mir::MirSpan, + test_db::TestDB, +}; use super::{setup_tracing, visit_module}; @@ -35,7 +36,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec let infer = db.infer(def); let db = &db; captures_info.extend(infer.closure_info.iter().flat_map(|(closure_id, (captures, _))| { - let closure = db.lookup_intern_closure(InternedClosureId::from_id(closure_id.0)); + let closure = db.lookup_intern_closure(*closure_id); let source_map = db.body_with_source_map(closure.0).1; let closure_text_range = source_map .expr_syntax(closure.1) @@ -70,7 +71,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec let capture_ty = salsa::attach(db, || { capture .ty - .skip_binders() + .skip_binder() .display_test(db, DisplayTarget::from_crate(db, module.krate())) .to_string() }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 1735f550b8ad7..5a53db4b7ab15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -49,7 +49,7 @@ fn let_stmt_coerce() { //- minicore: coerce_unsized fn test() { let x: &[isize] = &[1]; - // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize) let x: *const [isize] = &[1]; // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize) } @@ -268,7 +268,7 @@ fn takes_ref_str(x: &str) {} fn returns_string() -> String { loop {} } fn test() { takes_ref_str(&{ returns_string() }); - // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('{error}, Not)) + // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('{region error}, Not)) } "#, ); @@ -567,7 +567,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &'? (dyn Foo + '?), got &'? impl Foo + ?Sized + //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized } "#, ); @@ -833,11 +833,11 @@ struct V { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + 'static)>,) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + 'static)>,) } "#, ); @@ -854,8 +854,8 @@ impl core::cmp::PartialEq for Struct { } fn test() { Struct == Struct; - // ^^^^^^ adjustments: Borrow(Ref('{error}, Not)) - // ^^^^^^ adjustments: Borrow(Ref('{error}, Not)) + // ^^^^^^ adjustments: Borrow(Ref('{region error}, Not)) + // ^^^^^^ adjustments: Borrow(Ref('{region error}, Not)) }", ); } @@ -871,7 +871,7 @@ impl core::ops::AddAssign for Struct { } fn test() { Struct += Struct; - // ^^^^^^ adjustments: Borrow(Ref('{error}, Mut)) + // ^^^^^^ adjustments: Borrow(Ref('{region error}, Mut)) // ^^^^^^ adjustments: }", ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index 6e3faa05a629b..a986b54a7b064 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -67,11 +67,11 @@ trait B: A {} fn test<'a>( _: &(dyn A + Send), - //^ &(dyn A + Send) + //^ &(dyn A + Send + 'static) _: &'a (dyn Send + A), - //^ &'a (dyn A + Send) + //^ &'a (dyn A + Send + 'static) _: &dyn B, - //^ &(dyn B) + //^ &(dyn B + 'static) ) {} "#, ); @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo<'?> + // ^^^ &(dyn Foo<'?> + 'static) "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 8587c13e87f77..ce3de06127e49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -44,7 +44,7 @@ fn foo() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "expr_scopes_shim", "lang_item", "crate_lang_items", @@ -131,7 +131,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "expr_scopes_shim", "lang_item", "crate_lang_items", @@ -143,7 +143,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "expr_scopes_shim", "infer_shim", "function_signature_shim", @@ -151,7 +151,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "expr_scopes_shim", ] "#]], @@ -586,7 +586,7 @@ fn main() { "attrs_shim", "attrs_shim", "generic_predicates_ns_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -594,7 +594,7 @@ fn main() { "expr_scopes_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", - "generic_predicates_shim", + "generic_predicates_ns_shim", "value_ty_shim", "VariantFields::firewall_", "VariantFields::query_", @@ -610,7 +610,7 @@ fn main() { "impl_self_ty_with_diagnostics_shim", "generic_predicates_ns_shim", "value_ty_shim", - "generic_predicates_shim", + "generic_predicates_ns_shim", ] "#]], ); @@ -683,11 +683,12 @@ fn main() { "attrs_shim", "attrs_shim", "generic_predicates_ns_shim", - "return_type_impl_traits_shim", + "return_type_impl_traits_ns_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", "struct_signature_with_source_map_shim", + "generic_predicates_ns_shim", "VariantFields::query_", "inherent_impls_in_crate_shim", "impl_signature_with_source_map_shim", @@ -697,7 +698,7 @@ fn main() { "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", "generic_predicates_ns_shim", - "generic_predicates_shim", + "generic_predicates_ns_shim", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 25b938c7078aa..2f41de64cbbdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -199,8 +199,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': ::IntoIter - 100..119 'for _ ...!() {}': &'? mut ::IntoIter + 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': &'? mut {unknown} 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': () @@ -293,8 +293,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': ::IntoIter - 114..133 'for _ ...!() {}': &'? mut ::IntoIter + 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': &'? mut {unknown} 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 2f8f666475668..b0afd60406676 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1157,9 +1157,9 @@ fn dyn_trait_super_trait_not_in_scope() { 51..55 'self': &'? Self 64..69 '{ 0 }': u32 66..67 '0': u32 - 176..177 'd': &'? (dyn Trait + '?) + 176..177 'd': &'? (dyn Trait + 'static) 191..207 '{ ...o(); }': () - 197..198 'd': &'? (dyn Trait + '?) + 197..198 'd': &'? (dyn Trait + 'static) 197..204 'd.foo()': u32 "#]], ); @@ -2050,7 +2050,7 @@ impl dyn Error + Send { /// Attempts to downcast the box to a concrete type. pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; - // ^^^^ expected Box, got Box + // ^^^^ expected Box, got Box // FIXME, type mismatch should not occur ::downcast(err).map_err(|_| loop {}) //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box) -> Result, Box> diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 40e4c28fcc0b9..5cdd170198ba2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -31,6 +31,7 @@ fn test() { } #[test] +#[ignore = "FIXME(next-solver): This currently generates a type mismatch, need to switch opaque type handling to the solver"] fn associated_type_impl_traits_complex() { check_types( r#" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 00835aa03130c..7c79393e65ac9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1257,8 +1257,8 @@ fn test() { 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! - 16..66 'for _ ... }': <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': &'? mut <() as IntoIterator>::IntoIter + 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': &'? mut {unknown} 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': () @@ -2363,8 +2363,8 @@ fn test() { 108..125 '{ ... }': usize 118..119 'N': usize 139..157 '{ ...= N; }': () - 149..150 '_': Foo<_> - 153..154 'N': Foo<_> + 149..150 '_': Foo + 153..154 'N': Foo "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index c7711f31bf262..e7fadd0363acb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -84,7 +84,7 @@ fn test() -> i32 { 307..359 'core::...n Foo)': DynMetadata 327..328 '0': usize 327..340 '0 as *const F': *const F - 327..358 '0 as *...yn Foo': *const (dyn Foo + '?) + 327..358 '0 as *...yn Foo': *const (dyn Foo + 'static) 370..371 'f': F 374..378 'F {}': F 388..395 'fat_ptr': *const (dyn Foo + '?) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 9d02a44c37c97..38af7cb7248f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2743,7 +2743,7 @@ impl B for Astruct {} 725..754 '#[rust...1i32])': Box<[i32; 1], Global> 747..753 '[1i32]': [i32; 1] 748..752 '1i32': i32 - 765..766 'v': Vec, Global> + 765..766 'v': Vec, Global> 786..803 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> 786..860 '<[_]> ...ct)]))': Vec, Global> 804..859 '#[rust...uct)])': Box<[Box; 1], Global> @@ -3692,39 +3692,6 @@ fn main() { ); } -#[test] -fn infer_bad_lang_item() { - check_infer( - r#" -#[lang="eq"] -pub trait Eq { - fn eq(&self, ) -> bool; - -} - -#[lang="shr"] -pub trait Shr { - fn shr(&self, rhs: &RHS) -> Result; -} - -fn test() -> bool { - 1 >> 1; - 1 == 1; -} -"#, - expect![[r#" - 39..43 'self': &'? Self - 114..118 'self': &'? Self - 120..123 'rhs': &'? RHS - 163..190 '{ ...= 1; }': bool - 169..170 '1': i32 - 169..175 '1 >> 1': {unknown} - 181..182 '1': i32 - 181..187 '1 == 1': {unknown} - "#]], - ); -} - #[test] fn macro_semitransparent_hygiene() { check_types( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 66faac09cc299..0cf723e8514d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1480,24 +1480,24 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 206..208 '{}': Box + '?> - 218..219 'x': Box + '?> - 242..243 'y': &'? (dyn Trait + '?) + 206..208 '{}': Box + 'static> + 218..219 'x': Box + 'static> + 242..243 'y': &'? (dyn Trait + 'static) 262..379 '{ ...2(); }': () - 268..269 'x': Box + '?> - 275..276 'y': &'? (dyn Trait + '?) + 268..269 'x': Box + 'static> + 275..276 'y': &'? (dyn Trait + 'static) 286..287 'z': Box + '?> 290..293 'bar': fn bar() -> Box + 'static> 290..295 'bar()': Box + 'static> - 301..302 'x': Box + '?> + 301..302 'x': Box + 'static> 301..308 'x.foo()': u64 - 314..315 'y': &'? (dyn Trait + '?) + 314..315 'y': &'? (dyn Trait + 'static) 314..321 'y.foo()': u64 327..328 'z': Box + '?> 327..334 'z.foo()': u64 - 340..341 'x': Box + '?> + 340..341 'x': Box + 'static> 340..348 'x.foo2()': i64 - 354..355 'y': &'? (dyn Trait + '?) + 354..355 'y': &'? (dyn Trait + 'static) 354..362 'y.foo2()': i64 368..369 'z': Box + '?> 368..376 'z.foo2()': i64 @@ -1528,7 +1528,7 @@ fn test(s: S) { expect![[r#" 32..36 'self': &'? Self 102..106 'self': &'? S - 128..139 '{ loop {} }': &'? (dyn Trait + '?) + 128..139 '{ loop {} }': &'? (dyn Trait + 'static) 130..137 'loop {}': ! 135..137 '{}': () 175..179 'self': &'? Self @@ -1561,18 +1561,18 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + '? - 72..73 'x': dyn Trait + '? - 82..83 'y': &'? (dyn Trait + '?) + 60..62 '{}': dyn Trait + 'static + 72..73 'x': dyn Trait + 'static + 82..83 'y': &'? (dyn Trait + 'static) 100..175 '{ ...o(); }': u64 - 106..107 'x': dyn Trait + '? - 113..114 'y': &'? (dyn Trait + '?) + 106..107 'x': dyn Trait + 'static + 113..114 'y': &'? (dyn Trait + 'static) 124..125 'z': dyn Trait + '? 128..131 'bar': fn bar() -> dyn Trait + 'static 128..133 'bar()': dyn Trait + 'static - 139..140 'x': dyn Trait + '? + 139..140 'x': dyn Trait + 'static 139..146 'x.foo()': u64 - 152..153 'y': &'? (dyn Trait + '?) + 152..153 'y': &'? (dyn Trait + 'static) 152..159 'y.foo()': u64 165..166 'z': dyn Trait + '? 165..172 'z.foo()': u64 @@ -1594,7 +1594,7 @@ fn main() { expect![[r#" 31..35 'self': &'? S 37..39 '{}': () - 47..48 '_': &'? (dyn Fn(S) + '?) + 47..48 '_': &'? (dyn Fn(S) + 'static) 58..60 '{}': () 71..105 '{ ...()); }': () 77..78 'f': fn f(&'? (dyn Fn(S) + 'static)) @@ -2948,13 +2948,13 @@ fn test(x: &dyn Foo) { foo(x); }"#, expect![[r#" - 21..22 'x': &'? (dyn Foo + '?) + 21..22 'x': &'? (dyn Foo + 'static) 34..36 '{}': () - 46..47 'x': &'? (dyn Foo + '?) + 46..47 'x': &'? (dyn Foo + 'static) 59..74 '{ foo(x); }': () 65..68 'foo': fn foo(&'? (dyn Foo + 'static)) 65..71 'foo(x)': () - 69..70 'x': &'? (dyn Foo + '?) + 69..70 'x': &'? (dyn Foo + 'static) "#]], ); } @@ -3230,13 +3230,13 @@ fn foo() { 218..324 '{ ...&s); }': () 228..229 's': Option 232..236 'None': Option - 246..247 'f': Box) + '?> - 281..310 'Box { ... {}) }': Box) + '?> + 246..247 'f': Box) + 'static> + 281..310 'Box { ... {}) }': Box) + 'static> 294..308 '&mut (|ps| {})': &'? mut impl FnOnce(&'? Option) 300..307 '|ps| {}': impl FnOnce(&'? Option) 301..303 'ps': &'? Option 305..307 '{}': () - 316..317 'f': Box) + '?> + 316..317 'f': Box) + 'static> 316..321 'f(&s)': () 318..320 '&s': &'? Option 319..320 's': Option @@ -4272,10 +4272,10 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { "#, expect![[r#" 90..94 'self': &'? Self - 127..128 'v': &'? (dyn Trait = &'a i32> + '?) + 127..128 'v': &'? (dyn Trait = &'a i32> + 'static) 164..195 '{ ...f(); }': () - 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc + 170..171 'v': &'? (dyn Trait = &'a i32> + 'static) + 170..184 'v.get::()': = &'a i32> + 'static as Trait>::Assoc 170..192 'v.get:...eref()': {unknown} "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 8ac152341e753..16ad54a2f2c06 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -12,7 +12,7 @@ use intern::sym; use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ InferCtxtLike, TypingMode, - inherent::{SliceLike, Span as _}, + inherent::{SliceLike, Span as _, Ty as _}, solve::Certainty, }; use span::Edition; @@ -23,11 +23,11 @@ use crate::{ AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - infer::unify::InferenceTable, + from_assoc_type_id, next_solver::{ DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span, - infer::{DbInternerInferExt, InferCtxt}, - mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_canonical_args_for_result}, util::mini_canonicalize, }, utils::UnevaluatedConstEvaluatorFolder, @@ -93,9 +93,30 @@ pub(crate) fn normalize_projection_query<'db>( return TyKind::Error.intern(Interner); } - let mut table = InferenceTable::new(db, env); - let ty = table.normalize_projection_ty(projection); - table.resolve_completely(ty) + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + // FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things), + // but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT + // and async blocks. + let infcx = interner.infer_ctxt().build(TypingMode::Analysis { + defining_opaque_types_and_generators: crate::next_solver::SolverDefIds::new_from_iter( + interner, + [], + ), + }); + let alias_ty = crate::next_solver::Ty::new_alias( + interner, + rustc_type_ir::AliasTyKind::Projection, + crate::next_solver::AliasTy::new( + interner, + from_assoc_type_id(projection.associated_ty_id).into(), + >>::to_nextsolver(&projection.substitution, interner), + ), + ); + let mut ctxt = crate::next_solver::obligation_ctxt::ObligationCtxt::new(&infcx); + let normalized = ctxt + .structurally_normalize_ty(&ObligationCause::dummy(), env.env, alias_ty) + .unwrap_or(alias_ty); + normalized.replace_infer_with_error(interner).to_chalk(interner) } fn identity_subst( diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index fca0162765ecf..c094487a8709e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -19,6 +19,7 @@ use hir_ty::{ PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, + next_solver::{DbInterner, mapping::NextSolverToChalk}, }; use syntax::{ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, @@ -620,7 +621,7 @@ impl<'db> AnyDiagnostic<'db> { pub(crate) fn inference_diagnostic( db: &'db dyn HirDatabase, def: DefWithBodyId, - d: &InferenceDiagnostic, + d: &InferenceDiagnostic<'db>, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, ) -> Option> { @@ -640,6 +641,7 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let interner = DbInterner::new_with(db, None, None); Some(match d { &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { @@ -666,8 +668,11 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr)?; - ExpectedFunction { call: call_expr, found: Type::new(db, def, found.clone()) } - .into() + ExpectedFunction { + call: call_expr, + found: Type::new(db, def, found.to_chalk(interner)), + } + .into() } InferenceDiagnostic::UnresolvedField { expr, @@ -679,7 +684,7 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedField { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.clone()), + receiver: Type::new(db, def, receiver.to_chalk(interner)), method_with_same_name_exists: *method_with_same_name_exists, } .into() @@ -695,10 +700,9 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedMethodCall { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.clone()), - field_with_same_name: field_with_same_name - .clone() - .map(|ty| Type::new(db, def, ty)), + receiver: Type::new(db, def, receiver.to_chalk(interner)), + field_with_same_name: (*field_with_same_name) + .map(|ty| Type::new(db, def, ty.to_chalk(interner))), assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into), } .into() @@ -725,7 +729,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; - TypedHole { expr, expected: Type::new(db, def, expected.clone()) }.into() + TypedHole { expr, expected: Type::new(db, def, expected.to_chalk(interner)) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let expr_or_pat = match pat { @@ -742,12 +746,13 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { let expr = expr_syntax(*expr)?; - CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into() + CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.to_chalk(interner)) } + .into() } InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { let expr = expr_syntax(*expr)?; - let expr_ty = Type::new(db, def, expr_ty.clone()); - let cast_ty = Type::new(db, def, cast_ty.clone()); + let expr_ty = Type::new(db, def, expr_ty.to_chalk(interner)); + let cast_ty = Type::new(db, def, cast_ty.to_chalk(interner)); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } InferenceDiagnostic::TyDiagnostic { source, diag } => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9198086e9e6cb..d6527777117e0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -157,7 +157,7 @@ pub use { tt, }, hir_ty::{ - CastError, DropGlue, FnAbi, PointerCast, Safety, Variance, + CastError, DropGlue, FnAbi, PointerCast, Variance, consteval::ConstEvalError, diagnostics::UnsafetyReason, display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite}, @@ -165,6 +165,7 @@ pub use { layout::LayoutError, method_resolution::TyFingerprint, mir::{MirEvalError, MirLowerError}, + next_solver::abi::Safety, }, // FIXME: Properly encapsulate mir hir_ty::{Interner as ChalkTyInterner, mir}, @@ -1287,9 +1288,11 @@ impl TupleField { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { + let interner = DbInterner::new_with(db, None, None); let ty = db .infer(self.owner) .tuple_field_access_type(self.tuple) + .to_chalk(interner) .as_slice(Interner) .get(self.index as usize) .and_then(|arg| arg.ty(Interner)) @@ -1720,7 +1723,7 @@ impl Variant { self.source(db)?.value.expr() } - pub fn eval(self, db: &dyn HirDatabase) -> Result { + pub fn eval(self, db: &dyn HirDatabase) -> Result> { db.const_eval_discriminant(self.into()) } @@ -2012,6 +2015,7 @@ impl DefWithBody { style_lints: bool, ) { let krate = self.module(db).id.krate(); + let interner = DbInterner::new_with(db, Some(krate), None); let (body, source_map) = db.body_with_source_map(self.into()); let sig_source_map = match self { @@ -2061,8 +2065,16 @@ impl DefWithBody { acc.push( TypeMismatch { expr_or_pat, - expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.clone()), - actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.clone()), + expected: Type::new( + db, + DefWithBodyId::from(self), + mismatch.expected.to_chalk(interner), + ), + actual: Type::new( + db, + DefWithBodyId::from(self), + mismatch.actual.to_chalk(interner), + ), } .into(), ); @@ -2628,7 +2640,7 @@ impl Function { self, db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, - ) -> Result { + ) -> Result> { let body = db.monomorphized_mir_body( self.id.into(), Substitution::empty(Interner), @@ -2912,7 +2924,7 @@ impl Const { } /// Evaluate the constant. - pub fn eval(self, db: &dyn HirDatabase) -> Result { + pub fn eval(self, db: &dyn HirDatabase) -> Result> { db.const_eval(self.id.into(), Substitution::empty(Interner), None) .map(|it| EvaluatedConst { const_: it, def: self.id.into() }) } @@ -2934,7 +2946,7 @@ impl EvaluatedConst { format!("{}", self.const_.display(db, display_target)) } - pub fn render_debug(&self, db: &dyn HirDatabase) -> Result { + pub fn render_debug<'db>(&self, db: &'db dyn HirDatabase) -> Result> { let data = self.const_.data(Interner); if let TyKind::Scalar(s) = data.ty.kind(Interner) && matches!(s, Scalar::Int(_) | Scalar::Uint(_)) @@ -2990,7 +3002,7 @@ impl Static { } /// Evaluate the static initializer. - pub fn eval(self, db: &dyn HirDatabase) -> Result { + pub fn eval(self, db: &dyn HirDatabase) -> Result> { db.const_eval(self.id.into(), Substitution::empty(Interner), None) .map(|it| EvaluatedConst { const_: it, def: self.id.into() }) } @@ -4021,8 +4033,9 @@ impl Local { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let def = self.parent; let infer = db.infer(def); - let ty = infer[self.binding_id].clone(); - Type::new(db, def, ty) + let ty = infer[self.binding_id]; + let interner = DbInterner::new_with(db, None, None); + Type::new(db, def, ty.to_chalk(interner)) } /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` @@ -4466,7 +4479,9 @@ impl Impl { db: &'db dyn HirDatabase, Type { ty, env, _pd: _ }: Type<'db>, ) -> Vec { - let def_crates = match method_resolution::def_crates(db, &ty, env.krate) { + let interner = DbInterner::new_with(db, None, None); + let ty_ns = ty.to_nextsolver(interner); + let def_crates = match method_resolution::def_crates(db, ty_ns, env.krate) { Some(def_crates) => def_crates, None => return Vec::new(), }; @@ -4477,7 +4492,7 @@ impl Impl { ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty)) }; - let fp = TyFingerprint::for_inherent_impl(&ty); + let fp = TyFingerprint::for_inherent_impl(ty_ns); let fp = match fp { Some(fp) => fp, None => return Vec::new(), @@ -4487,7 +4502,7 @@ impl Impl { def_crates.iter().for_each(|&id| { all.extend( db.inherent_impls_in_crate(id) - .for_self_ty(&ty) + .for_self_ty(ty_ns) .iter() .cloned() .map(Self::from) @@ -4512,7 +4527,12 @@ impl Impl { { if let Some(inherent_impls) = db.inherent_impls_in_block(block) { all.extend( - inherent_impls.for_self_ty(&ty).iter().cloned().map(Self::from).filter(filter), + inherent_impls + .for_self_ty(ty_ns) + .iter() + .cloned() + .map(Self::from) + .filter(filter), ); } if let Some(trait_impls) = db.trait_impls_in_block(block) { @@ -4691,10 +4711,10 @@ impl Closure { .to_string() } - pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec { + pub fn captured_items<'db>(&self, db: &'db dyn HirDatabase) -> Vec> { let owner = db.lookup_intern_closure((self.id).into()).0; let infer = &db.infer(owner); - let info = infer.closure_info(&self.id); + let info = infer.closure_info(self.id.into()); info.0 .iter() .cloned() @@ -4705,12 +4725,13 @@ impl Closure { pub fn capture_types<'db>(&self, db: &'db dyn HirDatabase) -> Vec> { let owner = db.lookup_intern_closure((self.id).into()).0; let infer = &db.infer(owner); - let (captures, _) = infer.closure_info(&self.id); + let (captures, _) = infer.closure_info(self.id.into()); + let interner = DbInterner::new_with(db, None, None); captures .iter() .map(|capture| Type { env: db.trait_environment_for_body(owner), - ty: capture.ty(db, &self.subst), + ty: capture.ty(db, self.subst.to_nextsolver(interner)).to_chalk(interner), _pd: PhantomCovariantLifetime::new(), }) .collect() @@ -4719,19 +4740,19 @@ impl Closure { pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait { let owner = db.lookup_intern_closure((self.id).into()).0; let infer = &db.infer(owner); - let info = infer.closure_info(&self.id); + let info = infer.closure_info(self.id.into()); info.1 } } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ClosureCapture { +pub struct ClosureCapture<'db> { owner: DefWithBodyId, closure: ClosureId, - capture: hir_ty::CapturedItem, + capture: hir_ty::CapturedItem<'db>, } -impl ClosureCapture { +impl<'db> ClosureCapture<'db> { pub fn local(&self) -> Local { Local { parent: self.owner, binding_id: self.capture.local() } } @@ -5443,7 +5464,8 @@ impl<'db> Type<'db> { } pub fn fingerprint_for_trait_impl(&self) -> Option { - TyFingerprint::for_trait_impl(&self.ty) + let interner = DbInterner::conjure(); + TyFingerprint::for_trait_impl(self.ty.to_nextsolver(interner)) } pub(crate) fn canonical(&self) -> Canonical { @@ -5487,14 +5509,16 @@ impl<'db> Type<'db> { krate: Crate, callback: &mut dyn FnMut(AssocItemId) -> bool, ) { - let def_crates = match method_resolution::def_crates(db, &self.ty, krate.id) { + let interner = DbInterner::new_with(db, None, None); + let ty_ns = self.ty.to_nextsolver(interner); + let def_crates = match method_resolution::def_crates(db, ty_ns, krate.id) { Some(it) => it, None => return, }; for krate in def_crates { let impls = db.inherent_impls_in_crate(krate); - for impl_def in impls.for_self_ty(&self.ty) { + for impl_def in impls.for_self_ty(ty_ns) { for &(_, item) in impl_def.impl_items(db).items.iter() { if callback(item) { return; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 5af8659ca669a..45c2020bc8cea 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -29,6 +29,8 @@ use hir_expand::{ name::AsName, }; use hir_ty::diagnostics::{unsafe_operations, unsafe_operations_for_body}; +use hir_ty::next_solver::DbInterner; +use hir_ty::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; @@ -1553,19 +1555,24 @@ impl<'db> SemanticsImpl<'db> { pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option>> { let mutability = |m| match m { - hir_ty::Mutability::Not => Mutability::Shared, - hir_ty::Mutability::Mut => Mutability::Mut, + hir_ty::next_solver::Mutability::Not => Mutability::Shared, + hir_ty::next_solver::Mutability::Mut => Mutability::Mut, }; let analyzer = self.analyze(expr.syntax())?; let (mut source_ty, _) = analyzer.type_of_expr(self.db, expr)?; + let interner = DbInterner::new_with(self.db, None, None); + analyzer.expr_adjustments(expr).map(|it| { it.iter() .map(|adjust| { - let target = - Type::new_with_resolver(self.db, &analyzer.resolver, adjust.target.clone()); + let target = Type::new_with_resolver( + self.db, + &analyzer.resolver, + adjust.target.to_chalk(interner), + ); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1652,11 +1659,18 @@ impl<'db> SemanticsImpl<'db> { func: Function, subst: impl IntoIterator>, ) -> Option { - let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); - for s in subst { - substs = substs.push(s.ty); - } - Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) + let interner = DbInterner::new_with(self.db, None, None); + let mut subst = subst.into_iter(); + let substs = hir_ty::next_solver::GenericArgs::for_item( + interner, + trait_.id.into(), + |_, _, id, _| { + assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); + subst.next().expect("too few subst").ty.to_nextsolver(interner).into() + }, + ); + assert!(subst.next().is_none(), "too many subst"); + Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index c6b7e84dc20f1..5a2849220ed3f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -18,7 +18,7 @@ use base_db::salsa; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, - ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, + LocalFieldId, ModuleDefId, StructId, TraitId, VariantId, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId, lower::ExprCollector, @@ -36,9 +36,10 @@ use hir_expand::{ mod_path::{ModPath, PathKind, path}, name::{AsName, Name}, }; +use hir_ty::next_solver::GenericArgs; use hir_ty::{ Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy, - Substitution, ToChalk, TraitEnvironment, Ty, TyExt, TyKind, TyLoweringContext, + Substitution, ToChalk, TraitEnvironment, Ty, TyKind, TyLoweringContext, diagnostics::{ InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations, @@ -67,16 +68,16 @@ use triomphe::Arc; pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, - pub(crate) body_or_sig: Option, + pub(crate) body_or_sig: Option>, } #[derive(Debug)] -pub(crate) enum BodyOrSig { +pub(crate) enum BodyOrSig<'db> { Body { def: DefWithBodyId, body: Arc, source_map: Arc, - infer: Option>, + infer: Option>>, }, // To be folded into body once it is considered one VariantFields { @@ -116,7 +117,7 @@ impl<'db> SourceAnalyzer<'db> { def: DefWithBodyId, node @ InFile { file_id, .. }: InFile<&SyntaxNode>, offset: Option, - infer: Option>, + infer: Option>>, ) -> SourceAnalyzer<'db> { let (body, source_map) = db.body_with_source_map(def); let scopes = db.expr_scopes(def); @@ -182,7 +183,9 @@ impl<'db> SourceAnalyzer<'db> { } // FIXME: Remove this - fn body_(&self) -> Option<(DefWithBodyId, &Body, &BodySourceMap, Option<&InferenceResult>)> { + fn body_( + &self, + ) -> Option<(DefWithBodyId, &Body, &BodySourceMap, Option<&InferenceResult<'db>>)> { self.body_or_sig.as_ref().and_then(|it| match it { BodyOrSig::Body { def, body, source_map, infer } => { Some((*def, &**body, &**source_map, infer.as_deref())) @@ -191,7 +194,7 @@ impl<'db> SourceAnalyzer<'db> { }) } - fn infer(&self) -> Option<&InferenceResult> { + fn infer(&self) -> Option<&InferenceResult<'db>> { self.body_or_sig.as_ref().and_then(|it| match it { BodyOrSig::Sig { .. } => None, BodyOrSig::VariantFields { .. } => None, @@ -250,7 +253,7 @@ impl<'db> SourceAnalyzer<'db> { if let Pat::Bind { id, .. } = self.store()?[pat_id.as_pat()?] { Some(id) } else { None } } - pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> { + pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment<'db>]> { // It is safe to omit destructuring assignments here because they have no adjustments (neither // expressions nor patterns). let expr_id = self.expr_id(expr.clone())?.as_expr()?; @@ -288,9 +291,11 @@ impl<'db> SourceAnalyzer<'db> { let coerced = expr_id .as_expr() .and_then(|expr_id| infer.expr_adjustment(expr_id)) - .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone())); - let ty = infer[expr_id].clone(); - let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty); + .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target)); + let ty = infer[expr_id]; + let mk_ty = |ty: hir_ty::next_solver::Ty<'_>| { + Type::new_with_resolver(db, &self.resolver, ty.to_chalk(DbInterner::conjure())) + }; Some((mk_ty(ty), coerced.map(mk_ty))) } @@ -311,8 +316,10 @@ impl<'db> SourceAnalyzer<'db> { } }; - let ty = infer[expr_or_pat_id].clone(); - let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty); + let ty = infer[expr_or_pat_id]; + let mk_ty = |ty: hir_ty::next_solver::Ty<'db>| { + Type::new_with_resolver(db, &self.resolver, ty.to_chalk(DbInterner::conjure())) + }; Some((mk_ty(ty), coerced.map(mk_ty))) } @@ -323,8 +330,10 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option> { let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer()?; - let ty = infer[binding_id].clone(); - let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty); + let ty = infer[binding_id]; + let mk_ty = |ty: hir_ty::next_solver::Ty<'db>| { + Type::new_with_resolver(db, &self.resolver, ty.to_chalk(DbInterner::conjure())) + }; Some(mk_ty(ty)) } @@ -334,8 +343,8 @@ impl<'db> SourceAnalyzer<'db> { _param: &ast::SelfParam, ) -> Option> { let binding = self.body()?.self_param?; - let ty = self.infer()?[binding].clone(); - Some(Type::new_with_resolver(db, &self.resolver, ty)) + let ty = self.infer()?[binding]; + Some(Type::new_with_resolver(db, &self.resolver, ty.to_chalk(DbInterner::conjure()))) } pub(crate) fn binding_mode_of_pat( @@ -347,8 +356,10 @@ impl<'db> SourceAnalyzer<'db> { let infer = self.infer()?; infer.binding_mode(id.as_pat()?).map(|bm| match bm { hir_ty::BindingMode::Move => BindingMode::Move, - hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut), - hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => { + hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Mut) => { + BindingMode::Ref(Mutability::Mut) + } + hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Not) => { BindingMode::Ref(Mutability::Shared) } }) @@ -364,7 +375,9 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone())) + .map(|ty| { + Type::new_with_resolver(db, &self.resolver, ty.to_chalk(DbInterner::conjure())) + }) .collect(), ) } @@ -375,9 +388,8 @@ impl<'db> SourceAnalyzer<'db> { call: &ast::MethodCallExpr, ) -> Option> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; - let (func, substs) = self.infer()?.method_resolution(expr_id)?; + let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_with(db, None, None); - let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); let ty = db.value_ty(func.into())?.instantiate(interner, args); let ty = Type::new_with_resolver(db, &self.resolver, ty.to_chalk(interner)); let mut res = ty.as_callable(db)?; @@ -403,13 +415,18 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<(Either, Option>)> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; let inference_result = self.infer()?; + let interner = DbInterner::new_with(db, None, None); match inference_result.method_resolution(expr_id) { Some((f_in_trait, substs)) => { let (fn_, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); Some(( Either::Left(fn_.into()), - Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))), + Some(GenericSubstitution::new( + fn_.into(), + subst.to_chalk(interner), + self.trait_environment(db), + )), )) } None => { @@ -443,7 +460,7 @@ impl<'db> SourceAnalyzer<'db> { fn field_subst( &self, field_expr: ExprId, - infer: &InferenceResult, + infer: &InferenceResult<'db>, db: &'db dyn HirDatabase, ) -> Option> { let body = self.store()?; @@ -451,7 +468,7 @@ impl<'db> SourceAnalyzer<'db> { let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; return Some(GenericSubstitution::new( adt.into(), - subst.clone(), + subst.to_chalk(DbInterner::conjure()), self.trait_environment(db), )); } @@ -467,6 +484,7 @@ impl<'db> SourceAnalyzer<'db> { let (def, ..) = self.body_()?; let expr_id = self.expr_id(field.clone().into())?.as_expr()?; let inference_result = self.infer()?; + let interner = DbInterner::new_with(db, None, None); match inference_result.field_resolution(expr_id) { Some(field) => match field { Either::Left(field) => Some(( @@ -486,7 +504,11 @@ impl<'db> SourceAnalyzer<'db> { let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); ( Either::Right(f.into()), - Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))), + Some(GenericSubstitution::new( + f.into(), + subst.to_chalk(interner), + self.trait_environment(db), + )), ) }), } @@ -560,11 +582,11 @@ impl<'db> SourceAnalyzer<'db> { } } - let future_trait = LangItem::Future.resolve_trait(db, self.resolver.krate())?; let poll_fn = LangItem::FuturePoll.resolve_function(db, self.resolver.krate())?; // HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself // doesn't have any generic parameters, so we skip building another subst for `poll()`. - let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build(); + let interner = DbInterner::new_with(db, None, None); + let substs = GenericArgs::new_from_iter(interner, [ty.to_nextsolver(interner).into()]); Some(self.resolve_impl_method_or_trait_def(db, poll_fn, substs)) } @@ -573,7 +595,7 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, ) -> Option { - let (op_trait, op_fn) = match prefix_expr.op_kind()? { + let (_op_trait, op_fn) = match prefix_expr.op_kind()? { ast::UnaryOp::Deref => { // This can be either `Deref::deref` or `DerefMut::deref_mut`. // Since deref kind is inferenced and stored in `InferenceResult.method_resolution`, @@ -603,9 +625,10 @@ impl<'db> SourceAnalyzer<'db> { let ty = self.ty_of_expr(prefix_expr.expr()?)?; + let interner = DbInterner::new_with(db, None, None); // HACK: subst for all methods coincides with that for their trait because the methods // don't have any generic parameters, so we skip building another subst for the methods. - let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build(); + let substs = GenericArgs::new_from_iter(interner, [ty.to_nextsolver(interner).into()]); Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs)) } @@ -618,27 +641,28 @@ impl<'db> SourceAnalyzer<'db> { let base_ty = self.ty_of_expr(index_expr.base()?)?; let index_ty = self.ty_of_expr(index_expr.index()?)?; - let (index_trait, index_fn) = + let (_index_trait, index_fn) = self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index))?; - let (op_trait, op_fn) = self + let op_fn = self .infer() .and_then(|infer| { let expr = self.expr_id(index_expr.clone().into())?.as_expr()?; let (func, _) = infer.method_resolution(expr)?; - let (index_mut_trait, index_mut_fn) = self.lang_trait_fn( + let (_index_mut_trait, index_mut_fn) = self.lang_trait_fn( db, LangItem::IndexMut, &Name::new_symbol_root(sym::index_mut), )?; - if func == index_mut_fn { Some((index_mut_trait, index_mut_fn)) } else { None } + if func == index_mut_fn { Some(index_mut_fn) } else { None } }) - .unwrap_or((index_trait, index_fn)); + .unwrap_or(index_fn); // HACK: subst for all methods coincides with that for their trait because the methods // don't have any generic parameters, so we skip building another subst for the methods. - let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None) - .push(base_ty.clone()) - .push(index_ty.clone()) - .build(); + let interner = DbInterner::new_with(db, None, None); + let substs = GenericArgs::new_from_iter( + interner, + [base_ty.to_nextsolver(interner).into(), index_ty.to_nextsolver(interner).into()], + ); Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs)) } @@ -651,14 +675,15 @@ impl<'db> SourceAnalyzer<'db> { let lhs = self.ty_of_expr(binop_expr.lhs()?)?; let rhs = self.ty_of_expr(binop_expr.rhs()?)?; - let (op_trait, op_fn) = lang_items_for_bin_op(op) + let (_op_trait, op_fn) = lang_items_for_bin_op(op) .and_then(|(name, lang_item)| self.lang_trait_fn(db, lang_item, &name))?; // HACK: subst for `index()` coincides with that for `Index` because `index()` itself // doesn't have any generic parameters, so we skip building another subst for `index()`. - let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None) - .push(lhs.clone()) - .push(rhs.clone()) - .build(); + let interner = DbInterner::new_with(db, None, None); + let substs = GenericArgs::new_from_iter( + interner, + [lhs.to_nextsolver(interner).into(), rhs.to_nextsolver(interner).into()], + ); Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs)) } @@ -671,13 +696,10 @@ impl<'db> SourceAnalyzer<'db> { let ty = self.ty_of_expr(try_expr.expr()?)?; let op_fn = LangItem::TryTraitBranch.resolve_function(db, self.resolver.krate())?; - let op_trait = match op_fn.lookup(db).container { - ItemContainerId::TraitId(id) => id, - _ => return None, - }; // HACK: subst for `branch()` coincides with that for `Try` because `branch()` itself // doesn't have any generic parameters, so we skip building another subst for `branch()`. - let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build(); + let interner = DbInterner::new_with(db, None, None); + let substs = GenericArgs::new_from_iter(interner, [ty.to_nextsolver(interner).into()]); Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs)) } @@ -690,6 +712,7 @@ impl<'db> SourceAnalyzer<'db> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let expr = ast::Expr::from(record_expr); let expr_id = self.store_sm()?.node_expr(InFile::new(self.file_id, &expr))?; + let interner = DbInterner::new_with(db, None, None); let ast_name = field.field_name()?; let local_name = ast_name.as_name(); @@ -713,16 +736,17 @@ impl<'db> SourceAnalyzer<'db> { } }; let (adt, subst) = self.infer()?.type_of_expr_or_pat(expr_id)?.as_adt()?; + let subst = subst.to_chalk(interner); let variant = self.infer()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = - db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); + db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, &subst); Some(( field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), )) } @@ -731,6 +755,7 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, field: &ast::RecordPatField, ) -> Option<(Field, Type<'db>, GenericSubstitution<'db>)> { + let interner = DbInterner::new_with(db, None, None); let field_name = field.field_name()?.as_name(); let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; @@ -738,12 +763,13 @@ impl<'db> SourceAnalyzer<'db> { let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; let (adt, subst) = self.infer()?[pat_id.as_pat()?].as_adt()?; + let subst = subst.to_chalk(interner); let field_ty = - db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); + db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, &subst); Some(( field.into(), Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), )) } @@ -859,6 +885,7 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, path: &ast::Path, ) -> Option<(PathResolution, Option>)> { + let interner = DbInterner::new_with(db, None, None); let parent = path.syntax().parent(); let parent = || parent.clone(); @@ -874,29 +901,31 @@ impl<'db> SourceAnalyzer<'db> { None => { let subst = GenericSubstitution::new( f_in_trait.into(), - subs, + subs.to_chalk(interner), self.trait_environment(db), ); (assoc, subst) } Some(func_ty) => { - if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) { + if let TyKind::FnDef(_fn_def, subs) = + func_ty.to_chalk(interner).kind(Interner) + { let (fn_, subst) = self .resolve_impl_method_or_trait_def_with_subst( db, f_in_trait, - subs.clone(), + subs.to_nextsolver(interner), ); let subst = GenericSubstitution::new( fn_.into(), - subst, + subst.to_chalk(interner), self.trait_environment(db), ); (fn_.into(), subst) } else { let subst = GenericSubstitution::new( f_in_trait.into(), - subs, + subs.to_chalk(interner), self.trait_environment(db), ); (assoc, subst) @@ -909,7 +938,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); let subst = GenericSubstitution::new( konst.into(), - subst, + subst.to_chalk(interner), self.trait_environment(db), ); (konst.into(), subst) @@ -918,7 +947,7 @@ impl<'db> SourceAnalyzer<'db> { assoc, GenericSubstitution::new( type_alias.into(), - subs, + subs.to_chalk(interner), self.trait_environment(db), ), ), @@ -942,7 +971,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); let subst = GenericSubstitution::new( konst.into(), - subst, + subst.to_chalk(interner), self.trait_environment(db), ); (konst.into(), subst) @@ -951,7 +980,7 @@ impl<'db> SourceAnalyzer<'db> { assoc, GenericSubstitution::new( assoc.into(), - subs, + subs.to_chalk(interner), self.trait_environment(db), ), ), @@ -1149,10 +1178,10 @@ impl<'db> SourceAnalyzer<'db> { let parent = parent()?; let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { let expr_id = self.expr_id(expr)?; - self.infer()?.type_of_expr_or_pat(expr_id)? + self.infer()?.type_of_expr_or_pat(expr_id)?.to_chalk(interner) } else if let Some(pat) = ast::Pat::cast(parent) { let pat_id = self.pat_id(&pat)?; - &self.infer()?[pat_id] + self.infer()?[pat_id].to_chalk(interner) } else { return None; }; @@ -1232,7 +1261,8 @@ impl<'db> SourceAnalyzer<'db> { record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])? } }; - let res = self.missing_fields(db, substs, variant, missing_fields); + let interner = DbInterner::new_with(db, None, None); + let res = self.missing_fields(db, &substs.to_chalk(interner), variant, missing_fields); Some(res) } @@ -1249,7 +1279,8 @@ impl<'db> SourceAnalyzer<'db> { let (variant, missing_fields, _exhaustive) = record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; - let res = self.missing_fields(db, substs, variant, missing_fields); + let interner = DbInterner::new_with(db, None, None); + let res = self.missing_fields(db, &substs.to_chalk(interner), variant, missing_fields); Some(res) } @@ -1379,7 +1410,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, func: FunctionId, - substs: Substitution, + substs: GenericArgs<'db>, ) -> FunctionId { self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0 } @@ -1388,8 +1419,8 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, func: FunctionId, - substs: Substitution, - ) -> (FunctionId, Substitution) { + substs: GenericArgs<'db>, + ) -> (FunctionId, GenericArgs<'db>) { let owner = match self.resolver.body_owner() { Some(it) => it, None => return (func, substs), @@ -1402,14 +1433,19 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, const_id: ConstId, - subs: Substitution, - ) -> (ConstId, Substitution) { + subs: GenericArgs<'db>, + ) -> (ConstId, GenericArgs<'db>) { let owner = match self.resolver.body_owner() { Some(it) => it, None => return (const_id, subs), }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_const(db, env, const_id, subs) + method_resolution::lookup_impl_const( + DbInterner::new_with(db, None, None), + env, + const_id, + subs, + ) } fn lang_trait_fn( @@ -1423,8 +1459,10 @@ impl<'db> SourceAnalyzer<'db> { Some((trait_id, fn_id)) } - fn ty_of_expr(&self, expr: ast::Expr) -> Option<&Ty> { - self.infer()?.type_of_expr_or_pat(self.expr_id(expr)?) + fn ty_of_expr(&self, expr: ast::Expr) -> Option { + self.infer()? + .type_of_expr_or_pat(self.expr_id(expr)?) + .map(|ty| ty.to_chalk(DbInterner::conjure())) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 3dd435d9423b2..2cda6d6f1c0a4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -506,7 +506,7 @@ fn wrap_capture_in_deref_if_needed( make::expr_prefix(T![*], capture_name).into() } -fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr { +fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr { let place = parse_expr_from_str(&capture.display_place_source_code(ctx.db()), ctx.edition()) .expect("`display_place_source_code()` produced an invalid expr"); let needs_mut = match capture.kind() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index dbf68dbe33afe..094e679501fc2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -2987,7 +2987,6 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not !expr [snippet] - me not() fn(self) -> ::Output [type_could_unify+requires_import] sn box Box::new(expr) [] sn call function(expr) [] sn const const {} [] @@ -3001,6 +3000,7 @@ fn main() { sn return return expr [] sn unsafe unsafe {} [] sn while while expr {} [] + me not() fn(self) -> ::Output [requires_import] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 8613581292f75..e6702ccf13999 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1170,7 +1170,7 @@ trait B {} fn test(a: &dyn A) -> &dyn B { a - //^ error: expected &dyn B, found &dyn A + //^ error: expected &(dyn B + 'static), found &(dyn A + 'static) } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 595f0bb5fa8af..9c0b9a6ff99bb 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -791,6 +791,8 @@ impl PatternIterator { #[cfg(test)] mod tests { + use ide_db::base_db::salsa; + use crate::{MatchFinder, SsrRule}; #[test] @@ -799,33 +801,35 @@ mod tests { let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }"; let (db, position, selections) = crate::tests::single_file(input); - let position = ide_db::FilePosition { - file_id: position.file_id.file_id(&db), - offset: position.offset, - }; - let mut match_finder = MatchFinder::in_context( - &db, - position, - selections - .into_iter() - .map(|frange| ide_db::FileRange { - file_id: frange.file_id.file_id(&db), - range: frange.range, - }) - .collect(), - ) - .unwrap(); - match_finder.add_rule(rule).unwrap(); - let matches = match_finder.matches(); - assert_eq!(matches.matches.len(), 1); - assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)"); - assert_eq!(matches.matches[0].placeholder_values.len(), 1); - - let edits = match_finder.edits(); - assert_eq!(edits.len(), 1); - let edit = &edits[&position.file_id]; - let mut after = input.to_owned(); - edit.apply(&mut after); - assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); + salsa::attach(&db, || { + let position = ide_db::FilePosition { + file_id: position.file_id.file_id(&db), + offset: position.offset, + }; + let mut match_finder = MatchFinder::in_context( + &db, + position, + selections + .into_iter() + .map(|frange| ide_db::FileRange { + file_id: frange.file_id.file_id(&db), + range: frange.range, + }) + .collect(), + ) + .unwrap(); + match_finder.add_rule(rule).unwrap(); + let matches = match_finder.matches(); + assert_eq!(matches.matches.len(), 1); + assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)"); + assert_eq!(matches.matches[0].placeholder_values.len(), 1); + + let edits = match_finder.edits(); + assert_eq!(edits.len(), 1); + let edit = &edits[&position.file_id]; + let mut after = input.to_owned(); + edit.apply(&mut after); + assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); + }); } } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 875b4d9b06cec..24ad3ba7efe0b 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -101,33 +101,37 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { let (db, position, selections) = single_file(input); - let position = - ide_db::FilePosition { file_id: position.file_id.file_id(&db), offset: position.offset }; - let mut match_finder = MatchFinder::in_context( - &db, - position, - selections - .into_iter() - .map(|selection| ide_db::FileRange { - file_id: selection.file_id.file_id(&db), - range: selection.range, - }) - .collect(), - ) - .unwrap(); - for rule in rules { - let rule: SsrRule = rule.parse().unwrap(); - match_finder.add_rule(rule).unwrap(); - } - let edits = salsa::attach(&db, || match_finder.edits()); - if edits.is_empty() { - panic!("No edits were made"); - } - // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters - // stuff. - let mut actual = db.file_text(position.file_id).text(&db).to_string(); - edits[&position.file_id].apply(&mut actual); - expected.assert_eq(&actual); + salsa::attach(&db, || { + let position = ide_db::FilePosition { + file_id: position.file_id.file_id(&db), + offset: position.offset, + }; + let mut match_finder = MatchFinder::in_context( + &db, + position, + selections + .into_iter() + .map(|selection| ide_db::FileRange { + file_id: selection.file_id.file_id(&db), + range: selection.range, + }) + .collect(), + ) + .unwrap(); + for rule in rules { + let rule: SsrRule = rule.parse().unwrap(); + match_finder.add_rule(rule).unwrap(); + } + let edits = match_finder.edits(); + if edits.is_empty() { + panic!("No edits were made"); + } + // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters + // stuff. + let mut actual = db.file_text(position.file_id).text(&db).to_string(); + edits[&position.file_id].apply(&mut actual); + expected.assert_eq(&actual); + }) } #[allow(clippy::print_stdout)] @@ -145,51 +149,57 @@ fn print_match_debug_info(match_finder: &MatchFinder<'_>, file_id: EditionedFile fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { let (db, position, selections) = single_file(code); - let mut match_finder = MatchFinder::in_context( - &db, - ide_db::FilePosition { file_id: position.file_id.file_id(&db), offset: position.offset }, - selections - .into_iter() - .map(|selection| ide_db::FileRange { - file_id: selection.file_id.file_id(&db), - range: selection.range, - }) - .collect(), - ) - .unwrap(); - match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); - let matched_strings: Vec = salsa::attach(&db, || match_finder.matches()) - .flattened() - .matches - .iter() - .map(|m| m.matched_text()) - .collect(); - if matched_strings != expected && !expected.is_empty() { - print_match_debug_info(&match_finder, position.file_id, expected[0]); - } - assert_eq!(matched_strings, expected); + salsa::attach(&db, || { + let mut match_finder = MatchFinder::in_context( + &db, + ide_db::FilePosition { + file_id: position.file_id.file_id(&db), + offset: position.offset, + }, + selections + .into_iter() + .map(|selection| ide_db::FileRange { + file_id: selection.file_id.file_id(&db), + range: selection.range, + }) + .collect(), + ) + .unwrap(); + match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); + let matched_strings: Vec = + match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); + if matched_strings != expected && !expected.is_empty() { + print_match_debug_info(&match_finder, position.file_id, expected[0]); + } + assert_eq!(matched_strings, expected); + }) } fn assert_no_match(pattern: &str, code: &str) { let (db, position, selections) = single_file(code); - let mut match_finder = MatchFinder::in_context( - &db, - ide_db::FilePosition { file_id: position.file_id.file_id(&db), offset: position.offset }, - selections - .into_iter() - .map(|selection| ide_db::FileRange { - file_id: selection.file_id.file_id(&db), - range: selection.range, - }) - .collect(), - ) - .unwrap(); - match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); - let matches = match_finder.matches().flattened().matches; - if !matches.is_empty() { - print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); - panic!("Got {} matches when we expected none: {matches:#?}", matches.len()); - } + salsa::attach(&db, || { + let mut match_finder = MatchFinder::in_context( + &db, + ide_db::FilePosition { + file_id: position.file_id.file_id(&db), + offset: position.offset, + }, + selections + .into_iter() + .map(|selection| ide_db::FileRange { + file_id: selection.file_id.file_id(&db), + range: selection.range, + }) + .collect(), + ) + .unwrap(); + match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); + let matches = match_finder.matches().flattened().matches; + if !matches.is_empty() { + print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); + panic!("Got {} matches when we expected none: {matches:#?}", matches.len()); + } + }); } fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index b7c124139608b..121b16b97e871 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -646,9 +646,9 @@ auto trait Sync {} fn main() { // The block expression wrapping disables the constructor hint hiding logic let _v = { Vec::>::new() }; - //^^ Vec> + //^^ Vec> let _v = { Vec::>::new() }; - //^^ Vec> + //^^ Vec> let _v = { Vec::>::new() }; //^^ Vec> } diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index 8f9d2d6bf111b..05cd145033bf4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -60,7 +60,7 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura pub(crate) fn render_const_eval_error( db: &RootDatabase, - e: ConstEvalError, + e: ConstEvalError<'_>, display_target: DisplayTarget, ) -> String { let span_formatter = |file_id, text_range: TextRange| { diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 9911b85799b62..3f3d36bde2fa6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -228,32 +228,30 @@ impl StaticIndex<'_> { let id = if let Some(it) = self.def_map.get(&def) { *it } else { - let it = salsa::attach(sema.db, || { - self.tokens.insert(TokenStaticData { - documentation: documentation_for_definition(&sema, def, scope_node), - hover: Some(hover_for_definition( - &sema, - file_id, - def, - None, - scope_node, - None, - false, - &hover_config, - edition, - display_target, - )), - definition: def.try_to_nav(&sema).map(UpmappingResult::call_site).map( - |it| FileRange { file_id: it.file_id, range: it.focus_or_full_range() }, - ), - references: vec![], - moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), - display_name: def - .name(self.db) - .map(|name| name.display(self.db, edition).to_string()), - signature: Some(def.label(self.db, display_target)), - kind: def_to_kind(self.db, def), - }) + let it = self.tokens.insert(TokenStaticData { + documentation: documentation_for_definition(&sema, def, scope_node), + hover: Some(hover_for_definition( + &sema, + file_id, + def, + None, + scope_node, + None, + false, + &hover_config, + edition, + display_target, + )), + definition: def.try_to_nav(&sema).map(UpmappingResult::call_site).map(|it| { + FileRange { file_id: it.file_id, range: it.focus_or_full_range() } + }), + references: vec![], + moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), + display_name: def + .name(self.db) + .map(|name| name.display(self.db, edition).to_string()), + signature: Some(def.label(self.db, display_target)), + kind: def_to_kind(self.db, def), }); self.def_map.insert(def, it); it @@ -295,37 +293,40 @@ impl StaticIndex<'_> { vendored_libs_config: VendoredLibrariesConfig<'_>, ) -> StaticIndex<'a> { let db = &analysis.db; - let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source_file_id(db).original_file(db); - let source_root = db.file_source_root(file_id.file_id(&analysis.db)).source_root_id(db); - let source_root = db.source_root(source_root).source_root(db); - let is_vendored = match vendored_libs_config { - VendoredLibrariesConfig::Included { workspace_root } => source_root - .path_for_file(&file_id.file_id(&analysis.db)) - .is_some_and(|module_path| module_path.starts_with(workspace_root)), - VendoredLibrariesConfig::Excluded => false, + salsa::attach(db, || { + let work = all_modules(db).into_iter().filter(|module| { + let file_id = module.definition_source_file_id(db).original_file(db); + let source_root = + db.file_source_root(file_id.file_id(&analysis.db)).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); + let is_vendored = match vendored_libs_config { + VendoredLibrariesConfig::Included { workspace_root } => source_root + .path_for_file(&file_id.file_id(&analysis.db)) + .is_some_and(|module_path| module_path.starts_with(workspace_root)), + VendoredLibrariesConfig::Excluded => false, + }; + + !source_root.is_library || is_vendored + }); + let mut this = StaticIndex { + files: vec![], + tokens: Default::default(), + analysis, + db, + def_map: Default::default(), }; - - !source_root.is_library || is_vendored - }); - let mut this = StaticIndex { - files: vec![], - tokens: Default::default(), - analysis, - db, - def_map: Default::default(), - }; - let mut visited_files = FxHashSet::default(); - for module in work { - let file_id = module.definition_source_file_id(db).original_file(db); - if visited_files.contains(&file_id) { - continue; + let mut visited_files = FxHashSet::default(); + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + if visited_files.contains(&file_id) { + continue; + } + this.add_file(file_id.file_id(&analysis.db)); + // mark the file + visited_files.insert(file_id); } - this.add_file(file_id.file_id(&analysis.db)); - // mark the file - visited_files.insert(file_id); - } - this + this + }) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 4e43387f8d9da..720183ac5478d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -426,9 +426,12 @@ fn traverse( let edition = descended_element.file_id.edition(sema.db); let (unsafe_ops, bindings_shadow_count) = match current_body { Some(current_body) => { - let (ops, bindings) = per_body_cache - .entry(current_body) - .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default())); + let (ops, bindings) = per_body_cache.entry(current_body).or_insert_with(|| { + ( + salsa::attach(sema.db, || sema.get_unsafe_ops(current_body)), + Default::default(), + ) + }); (&*ops, Some(bindings)) } None => (&empty, None), diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index ddd58a0a3c9fb..b014261981769 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -3,7 +3,6 @@ use std::fmt; use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ RootDatabase, - base_db::salsa, defs::Definition, helpers::{get_definition, pick_best_token}, }; @@ -94,14 +93,14 @@ pub(crate) fn view_memory_layout( let def = get_definition(&sema, token)?; let ty = match def { - Definition::Adt(it) => salsa::attach(db, || it.ty(db)), - Definition::TypeAlias(it) => salsa::attach(db, || it.ty(db)), + Definition::Adt(it) => it.ty(db), + Definition::TypeAlias(it) => it.ty(db), Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => salsa::attach(db, || it.ty(db).to_type(db)), - Definition::Const(it) => salsa::attach(db, || it.ty(db)), - Definition::Static(it) => salsa::attach(db, || it.ty(db)), + Definition::Field(it) => it.ty(db).to_type(db), + Definition::Const(it) => it.ty(db), + Definition::Static(it) => it.ty(db), _ => return None, }; @@ -139,12 +138,10 @@ pub(crate) fn view_memory_layout( nodes[parent_idx].children_len = fields.len() as u64; for (field, child_ty) in fields.iter() { - if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { + if let Ok(child_layout) = child_ty.layout(db) { nodes.push(MemoryLayoutNode { item_name: field.name(db), - typename: salsa::attach(db, || { - child_ty.display(db, display_target).to_string() - }), + typename: { child_ty.display(db, display_target).to_string() }, size: child_layout.size(), alignment: child_layout.align(), offset: match *field { @@ -172,13 +169,13 @@ pub(crate) fn view_memory_layout( } for (i, (_, child_ty)) in fields.iter().enumerate() { - if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { + if let Ok(child_layout) = child_ty.layout(db) { read_layout(nodes, db, child_ty, &child_layout, children_start + i, display_target); } } } - salsa::attach(db, || ty.layout(db)) + ty.layout(db) .map(|layout| { let item_name = match def { // def is a datatype @@ -191,7 +188,7 @@ pub(crate) fn view_memory_layout( def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()), }; - let typename = salsa::attach(db, || ty.display(db, display_target).to_string()); + let typename = ty.display(db, display_target).to_string(); let mut nodes = vec![MemoryLayoutNode { item_name, @@ -216,13 +213,14 @@ mod tests { use crate::fixture; use expect_test::expect; + use ide_db::base_db::salsa; fn make_memory_layout( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> Option { let (analysis, position, _) = fixture::annotations(ra_fixture); - view_memory_layout(&analysis.db, position) + salsa::attach(&analysis.db, || view_memory_layout(&analysis.db, position)) } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index b301a7189b3c9..c746f848b6a06 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -75,6 +75,8 @@ vfs-notify.workspace = true vfs.workspace = true paths.workspace = true +ra-ap-rustc_type_ir.workspace = true + [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.60", features = [ "Win32_System_Diagnostics_Debug", diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 9551536cf4a51..9c8743536877b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -20,14 +20,16 @@ use hir_def::{ expr_store::BodySourceMap, hir::{ExprId, PatId}, }; -use hir_ty::{Interner, TyExt, TypeFlags}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RootDatabase, }; use ide_db::{ EditionedFileId, LineIndexDatabase, SnippetCap, - base_db::{SourceDatabase, salsa::Database}, + base_db::{ + SourceDatabase, + salsa::{self, Database}, + }, }; use itertools::Itertools; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; @@ -36,6 +38,7 @@ use profile::StopWatch; use project_model::{CargoConfig, CfgOverrides, ProjectManifest, ProjectWorkspace, RustLibSource}; use rayon::prelude::*; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::Ty as _; use syntax::AstNode; use vfs::{AbsPathBuf, Vfs, VfsPath}; @@ -312,33 +315,35 @@ impl flags::AnalysisStats { shuffle(&mut rng, &mut bodies); } - if !self.skip_lowering { - self.run_body_lowering(db, &vfs, &bodies, verbosity); - } + salsa::attach(db, || { + if !self.skip_lowering { + self.run_body_lowering(db, &vfs, &bodies, verbosity); + } - if !self.skip_inference { - self.run_inference(db, &vfs, &bodies, verbosity); - } + if !self.skip_inference { + self.run_inference(db, &vfs, &bodies, verbosity); + } - if !self.skip_mir_stats { - self.run_mir_lowering(db, &bodies, verbosity); - } + if !self.skip_mir_stats { + self.run_mir_lowering(db, &bodies, verbosity); + } - if !self.skip_data_layout { - self.run_data_layout(db, &adts, verbosity); - } + if !self.skip_data_layout { + self.run_data_layout(db, &adts, verbosity); + } - if !self.skip_const_eval { - self.run_const_eval(db, &bodies, verbosity); - } + if !self.skip_const_eval { + self.run_const_eval(db, &bodies, verbosity); + } - if self.run_all_ide_things { - self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); - } + if self.run_all_ide_things { + self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); + } - if self.run_term_search { - self.run_term_search(&workspace, db, &vfs, file_ids, verbosity); - } + if self.run_term_search { + self.run_term_search(&workspace, db, &vfs, file_ids, verbosity); + } + }); let db = host.raw_database_mut(); db.trigger_lru_eviction(); @@ -814,7 +819,7 @@ impl flags::AnalysisStats { for (expr_id, _) in body.exprs() { let ty = &inference_result[expr_id]; num_exprs += 1; - let unknown_or_partial = if ty.is_unknown() { + let unknown_or_partial = if ty.is_ty_error() { num_exprs_unknown += 1; if verbosity.is_spammy() { if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm(), expr_id) @@ -836,8 +841,7 @@ impl flags::AnalysisStats { } true } else { - let is_partially_unknown = - ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR); + let is_partially_unknown = ty.references_non_lt_error(); if is_partially_unknown { num_exprs_partially_unknown += 1; } @@ -919,7 +923,7 @@ impl flags::AnalysisStats { for (pat_id, _) in body.pats() { let ty = &inference_result[pat_id]; num_pats += 1; - let unknown_or_partial = if ty.is_unknown() { + let unknown_or_partial = if ty.is_ty_error() { num_pats_unknown += 1; if verbosity.is_spammy() { if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm(), pat_id) { @@ -940,8 +944,7 @@ impl flags::AnalysisStats { } true } else { - let is_partially_unknown = - ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR); + let is_partially_unknown = ty.references_non_lt_error(); if is_partially_unknown { num_pats_partially_unknown += 1; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 0dea285e97bd4..44af8fbddf300 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -9,6 +9,8 @@ //! The `cli` submodule implements some batch-processing analysis, primarily as //! a debugging aid. +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + /// Any toolchain less than this version will likely not work with rust-analyzer built from this revision. pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version { major: 1, From eba1416c015803aab6d3ac0b7efebb28165b50ab Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 12:43:36 +0200 Subject: [PATCH 1077/1900] std: improve internal socket functions --- .../std/src/sys/net/connection/socket/mod.rs | 96 +++++++++++++------ 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1dd06e97bbabd..8a758947cc2d6 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -3,6 +3,7 @@ mod tests; use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::mem::MaybeUninit; use crate::net::{ Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, }; @@ -177,6 +178,11 @@ fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { } } +/// Converts the C socket address stored in `storage` to a Rust `SocketAddr`. +/// +/// # Safety +/// * `storage` must contain a valid C socket address whose length is no larger +/// than `len`. unsafe fn socket_addr_from_c( storage: *const c::sockaddr_storage, len: usize, @@ -202,49 +208,85 @@ unsafe fn socket_addr_from_c( // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// -pub fn setsockopt( +/// Sets the value of a socket option. +/// +/// # Safety +/// `T` must be the type associated with the given socket option. +pub unsafe fn setsockopt( sock: &Socket, level: c_int, option_name: c_int, option_value: T, ) -> io::Result<()> { - unsafe { - cvt(c::setsockopt( + let option_len = size_of::() as c::socklen_t; + // SAFETY: + // * `sock` is opened for the duration of this call, as `sock` owns the socket. + // * the pointer to `option_value` is readable at a size of `size_of::` + // bytes + // * the value of `option_value` has a valid type for the given socket option + // (guaranteed by caller). + cvt(unsafe { + c::setsockopt( sock.as_raw(), level, option_name, (&raw const option_value) as *const _, - size_of::() as c::socklen_t, - ))?; - Ok(()) - } + option_len, + ) + })?; + Ok(()) } -pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { - unsafe { - let mut option_value: T = mem::zeroed(); - let mut option_len = size_of::() as c::socklen_t; - cvt(c::getsockopt( +/// Gets the value of a socket option. +/// +/// # Safety +/// `T` must be the type associated with the given socket option. +pub unsafe fn getsockopt( + sock: &Socket, + level: c_int, + option_name: c_int, +) -> io::Result { + let mut option_value = MaybeUninit::::zeroed(); + let mut option_len = size_of::() as c::socklen_t; + + // SAFETY: + // * `sock` is opened for the duration of this call, as `sock` owns the socket. + // * the pointer to `option_value` is writable and the stack allocation has + // space for `size_of::` bytes. + cvt(unsafe { + c::getsockopt( sock.as_raw(), level, option_name, - (&raw mut option_value) as *mut _, + option_value.as_mut_ptr().cast(), &mut option_len, - ))?; - Ok(option_value) - } -} - -fn sockname(f: F) -> io::Result + ) + })?; + + // SAFETY: the `getsockopt` call succeeded and the caller guarantees that + // `T` is the type of this option, thus `option_value` must have + // been initialized by the system. + Ok(unsafe { option_value.assume_init() }) +} + +/// Wraps a call to a platform function that returns a socket address. +/// +/// # Safety +/// * if `f` returns a success (i.e. `cvt` returns `Ok` when called on the +/// return value), the buffer provided to `f` must have been initialized +/// with a valid C socket address, the length of which must be written +/// to the second argument. +unsafe fn sockname(f: F) -> io::Result where F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, { - unsafe { - let mut storage: c::sockaddr_storage = mem::zeroed(); - let mut len = size_of_val(&storage) as c::socklen_t; - cvt(f((&raw mut storage) as *mut _, &mut len))?; - socket_addr_from_c(&storage, len as usize) - } + let mut storage = MaybeUninit::::zeroed(); + let mut len = size_of::() as c::socklen_t; + cvt(f(storage.as_mut_ptr().cast(), &mut len))?; + // SAFETY: + // The caller guarantees that the storage has been successfully initialized + // and its size written to `len` if `f` returns a success. + unsafe { socket_addr_from_c(storage.as_ptr(), len as usize) } } #[cfg(target_os = "android")] @@ -546,8 +588,8 @@ impl TcpListener { // The `accept` function will fill in the storage with the address, // so we don't need to zero it here. // reference: https://linux.die.net/man/2/accept4 - let mut storage: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut len = size_of_val(&storage) as c::socklen_t; + let mut storage = MaybeUninit::::uninit(); + let mut len = size_of::() as c::socklen_t; let sock = self.inner.accept(storage.as_mut_ptr() as *mut _, &mut len)?; let addr = unsafe { socket_addr_from_c(storage.as_ptr(), len as usize)? }; Ok((TcpStream { inner: sock }, addr)) From 817e181ee80256d93156bbb8ba0a1daafb3cf4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 29 Sep 2025 15:00:10 +0200 Subject: [PATCH 1078/1900] test bevy compute_implied_bounds hack with new trait solver --- tests/ui/implied-bounds/bevy_world_query.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/implied-bounds/bevy_world_query.rs b/tests/ui/implied-bounds/bevy_world_query.rs index 6548c03d1b00c..e2750bcf957ea 100644 --- a/tests/ui/implied-bounds/bevy_world_query.rs +++ b/tests/ui/implied-bounds/bevy_world_query.rs @@ -1,6 +1,8 @@ -#![crate_name = "bevy_ecs"] - //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +#![crate_name = "bevy_ecs"] // We currently special case bevy from erroring on incorrect implied bounds // from normalization (issue #109628). From fef865731cdf89995f8e8667641701843b4a3168 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 30 Sep 2025 15:40:54 +0300 Subject: [PATCH 1079/1900] Impl `std::error::Error` for `SyntaxError` --- src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs index dc6130bd6415c..1c902893abc64 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs @@ -42,3 +42,5 @@ impl fmt::Display for SyntaxError { self.0.fmt(f) } } + +impl std::error::Error for SyntaxError {} From 198777a08e74e2b7a59ed9b7a8b2561eaddc160e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 30 Sep 2025 15:03:40 +0200 Subject: [PATCH 1080/1900] remove unnecessary test directives --- .../trait-upcasting/illegal-upcast-to-impl-opaque.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs index 2760c1696b56d..f603ff1ec80ec 100644 --- a/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs @@ -1,12 +1,5 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[next] failure-status: 101 -//@[next] known-bug: unknown -//@[next] normalize-stderr: "note: .*\n\n" -> "" -//@[next] normalize-stderr: "thread 'rustc' panicked.*\n.*\n" -> "" -//@[next] normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@[next] normalize-stderr: "delayed at .*" -> "" -//@[next] rustc-env:RUST_BACKTRACE=0 //@ check-pass trait Super { From 9e79fac0354b2a87515c30e94754413ef99b3675 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 30 Sep 2025 22:39:10 +0900 Subject: [PATCH 1081/1900] Add repr(align(2)) to RcInner and ArcInner --- library/alloc/src/rc.rs | 4 +++- library/alloc/src/sync.rs | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index fcb466778a3fb..e8527b18f0706 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -277,7 +277,9 @@ use crate::vec::Vec; // This is repr(C) to future-proof against possible field-reordering, which // would interfere with otherwise safe [into|from]_raw() of transmutable // inner types. -#[repr(C)] +// repr(align(2)) (forcing alignment to at least 2) is required because usize +// has 1-byte alignment on AVR. +#[repr(C, align(2))] struct RcInner { strong: Cell, weak: Cell, diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 32396cccb8fca..6bab3b7056f67 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -341,7 +341,7 @@ pub struct Weak< // but it is not necessarily a valid pointer. // `Weak::new` sets this to `usize::MAX` so that it doesn’t need // to allocate space on the heap. That's not a value a real pointer - // will ever have because RcInner has alignment at least 2. + // will ever have because ArcInner has alignment at least 2. ptr: NonNull>, alloc: A, } @@ -366,7 +366,9 @@ impl fmt::Debug for Weak { // This is repr(C) to future-proof against possible field-reordering, which // would interfere with otherwise safe [into|from]_raw() of transmutable // inner types. -#[repr(C)] +// Unlike RcInner, repr(align(2)) is not strictly required because atomic types +// have the alignment same as its size, but we use it for consistency and clarity. +#[repr(C, align(2))] struct ArcInner { strong: Atomic, @@ -1622,9 +1624,9 @@ impl Arc { pub fn as_ptr(this: &Self) -> *const T { let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); - // SAFETY: This cannot go through Deref::deref or RcInnerPtr::inner because + // SAFETY: This cannot go through Deref::deref or ArcInnerPtr::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can - // write through the pointer after the Rc is recovered through `from_raw`. + // write through the pointer after the Arc is recovered through `from_raw`. unsafe { &raw mut (*ptr).data } } @@ -2459,7 +2461,7 @@ impl Arc { /// If any other `Arc` or [`Weak`] pointers to the same allocation exist, then /// they must not be dereferenced or have active borrows for the duration /// of the returned borrow, and their inner type must be exactly the same as the - /// inner type of this Rc (including lifetimes). This is trivially the case if no + /// inner type of this Arc (including lifetimes). This is trivially the case if no /// such pointers exist, for example immediately after `Arc::new`. /// /// # Examples @@ -3031,7 +3033,7 @@ impl Weak { // Otherwise, we're guaranteed the pointer came from a nondangling Weak. // SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T. let offset = unsafe { data_offset(ptr) }; - // Thus, we reverse the offset to get the whole RcInner. + // Thus, we reverse the offset to get the whole ArcInner. // SAFETY: the pointer originated from a Weak, so this offset is safe. unsafe { ptr.byte_sub(offset) as *mut ArcInner } }; @@ -4024,7 +4026,7 @@ impl Unpin for Arc {} /// valid instance of T, but the T is allowed to be dropped. unsafe fn data_offset(ptr: *const T) -> usize { // Align the unsized value to the end of the ArcInner. - // Because RcInner is repr(C), it will always be the last field in memory. + // Because ArcInner is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, // and extern types, the input safety requirement is currently enough to // satisfy the requirements of align_of_val_raw; this is an implementation From 083d279d2e58cdc4ed7ae50b7de0b078e465db13 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 27 Sep 2025 15:38:24 +0800 Subject: [PATCH 1082/1900] Add else-block support for convert_to_guarded_return Add support for else-block of never-type for `convert_to_guarded_return` Example --- ```rust fn main() { if$0 let Ok(x) = Err(92) { foo(x); } else { return } } ``` **Before this PR**: Assist not applicable **After this PR**: ```rust fn main() { let Ok(x) = Err(92) else { return }; foo(x); } ``` --- .../src/handlers/convert_to_guarded_return.rs | 89 +++++++++++++++---- .../crates/ide-assists/src/utils.rs | 16 ++++ 2 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 82213ae3217e7..1ebb114cdbb60 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -17,7 +17,7 @@ use syntax::{ use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::invert_boolean_expression_legacy, + utils::{invert_boolean_expression_legacy, is_never_block}, }; // Assist: convert_to_guarded_return @@ -54,9 +54,13 @@ fn if_expr_to_guarded_return( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - if if_expr.else_branch().is_some() { - return None; - } + let else_block = match if_expr.else_branch() { + Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => { + Some(block_expr) + } + Some(_) => return None, + _ => None, + }; let cond = if_expr.condition()?; @@ -96,7 +100,11 @@ fn if_expr_to_guarded_return( let parent_container = parent_block.syntax().parent()?; - let early_expression: ast::Expr = early_expression(parent_container, &ctx.sema)?; + let early_expression = else_block + .or_else(|| { + early_expression(parent_container, &ctx.sema).map(ast::make::tail_only_block_expr) + })? + .reset_indent(); then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?; @@ -123,21 +131,14 @@ fn if_expr_to_guarded_return( && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr()) { // If-let. - let let_else_stmt = make::let_else_stmt( - pat, - None, - expr, - ast::make::tail_only_block_expr(early_expression.clone()), - ); + let let_else_stmt = + make::let_else_stmt(pat, None, expr, early_expression.clone()); let let_else_stmt = let_else_stmt.indent(if_indent_level); let_else_stmt.syntax().clone() } else { // If. let new_expr = { - let then_branch = make::block_expr( - once(make::expr_stmt(early_expression.clone()).into()), - None, - ); + let then_branch = clean_stmt_block(&early_expression); let cond = invert_boolean_expression_legacy(expr); make::expr_if(cond, then_branch, None).indent(if_indent_level) }; @@ -272,6 +273,17 @@ fn flat_let_chain(mut expr: ast::Expr) -> Vec { chains } +fn clean_stmt_block(block: &ast::BlockExpr) -> ast::BlockExpr { + if block.statements().next().is_none() + && let Some(tail_expr) = block.tail_expr() + && block.modifier().is_none() + { + make::block_expr(once(make::expr_stmt(tail_expr).into()), None) + } else { + block.clone() + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -421,6 +433,53 @@ fn main() { ); } + #[test] + fn convert_if_let_has_never_type_else_block() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 let Ok(x) = Err(92) { + foo(x); + } else { + // needless comment + return; + } +} +"#, + r#" +fn main() { + let Ok(x) = Err(92) else { + // needless comment + return; + }; + foo(x); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return + } +} +"#, + r#" +fn main() { + let Ok(x) = Err(92) else { + return + }; + foo(x); +} +"#, + ); + } + #[test] fn convert_if_let_result_inside_let() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 20e0302b57d70..36b44554f1494 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1150,3 +1150,19 @@ pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bo }); is_const } + +// FIXME: #20460 When hir-ty can analyze the `never` statement at the end of block, remove it +pub(crate) fn is_never_block( + sema: &Semantics<'_, RootDatabase>, + block_expr: &ast::BlockExpr, +) -> bool { + if let Some(tail_expr) = block_expr.tail_expr() { + sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never()) + } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last() + && let Some(expr) = expr_stmt.expr() + { + sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) + } else { + false + } +} From 5f54d8bfd8e5921dc5eb5bbe0799907ed4ca1916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 30 Sep 2025 16:08:48 +0200 Subject: [PATCH 1083/1900] Remove usage of `compiletest-use-stage0-libtest` from CI --- src/bootstrap/defaults/bootstrap.dist.toml | 2 -- src/ci/citool/tests/test-jobs.yml | 2 +- src/ci/docker/host-x86_64/pr-check-1/Dockerfile | 1 - src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index b111a20f8d868..bb0592ce947ab 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -7,8 +7,6 @@ test-stage = 2 doc-stage = 2 # When compiling from source, you usually want all tools. extended = true -# Use libtest built from the source tree instead of the precompiled one from stage 0. -compiletest-use-stage0-libtest = false # Most users installing from source want to build all parts of the project from source. [llvm] diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index d82b3e7648e17..512c806285746 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -27,7 +27,7 @@ runners: <<: *base-job envs: env-x86_64-apple-tests: &env-x86_64-apple-tests - SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact + SCRIPT: ./x.py check compiletest && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is tested on our minimum supported macOS version. diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile index 04ac0f33daf05..776bbb12e445b 100644 --- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile @@ -43,7 +43,6 @@ ENV SCRIPT \ python3 ../x.py check bootstrap && \ /scripts/check-default-config-profiles.sh && \ python3 ../x.py build src/tools/build-manifest && \ - python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/validate-toolstate.sh && \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 95357d229374c..278e40eb71fac 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -90,5 +90,4 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu COPY scripts/shared.sh /scripts/ ENV SCRIPT /tmp/checktools.sh ../x.py && \ - python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'" From aa1263e7684341a73b600eaf0bbc70067e196243 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 16:55:21 +0200 Subject: [PATCH 1084/1900] std: add missing unsafe blocks --- .../src/sys/net/connection/socket/hermit.rs | 14 +- .../std/src/sys/net/connection/socket/mod.rs | 88 +++++----- .../src/sys/net/connection/socket/solid.rs | 14 +- .../std/src/sys/net/connection/socket/unix.rs | 152 +++++++++--------- .../src/sys/net/connection/socket/wasip2.rs | 10 +- .../src/sys/net/connection/socket/windows.rs | 14 +- 6 files changed, 156 insertions(+), 136 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 5200eaa5786ad..0c105ed20fa7e 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -242,11 +242,11 @@ impl Socket { None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: i32) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -272,22 +272,22 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, }; - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + let val: netc::linger = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { let value: i32 = if nodelay { 1 } else { 0 }; - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) } } pub fn nodelay(&self) -> io::Result { - let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: i32 = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -304,7 +304,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 8a758947cc2d6..d01a52b861091 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -442,11 +442,11 @@ impl TcpStream { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getpeername(self.inner.as_raw(), buf, len)) } } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -474,11 +474,11 @@ impl TcpStream { } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } @@ -545,7 +545,9 @@ impl TcpListener { // which allows “socket hijacking”, so we explicitly don't set it here. // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + unsafe { + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)? + }; // Bind our new socket let (addr, len) = socket_addr_to_c(addr); @@ -581,7 +583,7 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { @@ -600,20 +602,20 @@ impl TcpListener { } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) } } pub fn only_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)? }; Ok(raw != 0) } @@ -676,11 +678,11 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getpeername(self.inner.as_raw(), buf, len)) } } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { @@ -728,48 +730,62 @@ impl UdpSocket { } pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + unsafe { setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) } } pub fn broadcast(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)? }; Ok(raw != 0) } pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_LOOP, - multicast_loop_v4 as IpV4MultiCastType, - ) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + multicast_loop_v4 as IpV4MultiCastType, + ) + } } pub fn multicast_loop_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; + let raw: IpV4MultiCastType = + unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)? }; Ok(raw != 0) } pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_TTL, - multicast_ttl_v4 as IpV4MultiCastType, - ) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_TTL, + multicast_ttl_v4 as IpV4MultiCastType, + ) + } } pub fn multicast_ttl_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; + let raw: IpV4MultiCastType = + unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)? }; Ok(raw as u32) } pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IPV6, + c::IPV6_MULTICAST_LOOP, + multicast_loop_v6 as c_int, + ) + } } pub fn multicast_loop_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; + let raw: c_int = + unsafe { getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)? }; Ok(raw != 0) } @@ -778,7 +794,7 @@ impl UdpSocket { imr_multiaddr: ip_v4_addr_to_c(multiaddr), imr_interface: ip_v4_addr_to_c(interface), }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) } } pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { @@ -786,7 +802,7 @@ impl UdpSocket { ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) } } pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { @@ -794,7 +810,7 @@ impl UdpSocket { imr_multiaddr: ip_v4_addr_to_c(multiaddr), imr_interface: ip_v4_addr_to_c(interface), }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) } } pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { @@ -802,15 +818,15 @@ impl UdpSocket { ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) } } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 94bb605c1007c..dfde5513f909f 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -303,11 +303,11 @@ impl Socket { } None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -333,21 +333,21 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, }; - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + let val: netc::linger = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -360,7 +360,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index a191576d93b9d..3f05449903fd1 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -72,47 +72,45 @@ impl Socket { } pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - cfg_select! { - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "nto", - target_os = "solaris", - ) => { - // On platforms that support it we pass the SOCK_CLOEXEC - // flag to atomically create the socket and set it as - // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - let socket = Socket(FileDesc::from_raw_fd(fd)); - - // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } - _ => { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - let socket = Socket(fd); + cfg_select! { + any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "nto", + target_os = "solaris", + ) => { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. + let fd = cvt(unsafe { libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0) })?; + let socket = Socket(unsafe { FileDesc::from_raw_fd(fd) }); + + // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] + unsafe { setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)? }; + + Ok(socket) + } + _ => { + let fd = cvt(unsafe { libc::socket(fam, ty, 0) })?; + let fd = unsafe { FileDesc::from_raw_fd(fd) }; + fd.set_cloexec()?; + let socket = Socket(fd); - // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(target_vendor = "apple")] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + unsafe { setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)? }; - Ok(socket) - } + Ok(socket) } } } @@ -413,11 +411,11 @@ impl Socket { } None => libc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, libc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + let raw: libc::timeval = unsafe { getsockopt(self, libc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -444,7 +442,7 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, }; - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } } #[cfg(target_os = "cygwin")] @@ -454,32 +452,32 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_ushort, }; - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; + let val: libc::linger = unsafe { getsockopt(self, libc::SOL_SOCKET, SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)? }; Ok(raw != 0) } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) } } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn quickack(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)? }; Ok(raw != 0) } @@ -487,12 +485,12 @@ impl Socket { #[cfg(target_os = "linux")] pub fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { let val = cmp::min(accept.as_secs(), c_int::MAX as u64) as c_int; - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) } } #[cfg(target_os = "linux")] pub fn deferaccept(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)? }; Ok(Duration::from_secs(raw as _)) } @@ -506,21 +504,23 @@ impl Socket { } let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; arg.af_name = buf; - setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) } } else { - setsockopt( - self, - libc::SOL_SOCKET, - libc::SO_ACCEPTFILTER, - core::ptr::null_mut() as *mut c_void, - ) + unsafe { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } } } #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] pub fn acceptfilter(&self) -> io::Result<&CStr> { let arg: libc::accept_filter_arg = - getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)? }; let s: &[u8] = unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; let name = CStr::from_bytes_with_nul(s).unwrap(); @@ -531,53 +531,57 @@ impl Socket { pub fn set_exclbind(&self, excl: bool) -> io::Result<()> { // not yet on libc crate const SO_EXCLBIND: i32 = 0x1015; - setsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND, excl) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND, excl) } } #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn exclbind(&self) -> io::Result { // not yet on libc crate const SO_EXCLBIND: i32 = 0x1015; - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND)? }; Ok(raw != 0) } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) } } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; + let passcred: libc::c_int = + unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)? }; Ok(passcred != 0) } #[cfg(target_os = "netbsd")] pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { - setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) + unsafe { setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) } } #[cfg(target_os = "netbsd")] pub fn local_creds(&self) -> io::Result { - let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + let local_creds: libc::c_int = + unsafe { getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)? }; Ok(local_creds != 0) } #[cfg(target_os = "freebsd")] pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { - setsockopt( - self, - libc::AF_LOCAL, - libc::LOCAL_CREDS_PERSISTENT, - local_creds_persistent as libc::c_int, - ) + unsafe { + setsockopt( + self, + libc::AF_LOCAL, + libc::LOCAL_CREDS_PERSISTENT, + local_creds_persistent as libc::c_int, + ) + } } #[cfg(target_os = "freebsd")] pub fn local_creds_persistent(&self) -> io::Result { let local_creds_persistent: libc::c_int = - getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + unsafe { getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)? }; Ok(local_creds_persistent != 0) } @@ -590,7 +594,7 @@ impl Socket { #[cfg(target_os = "vita")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let option = nonblocking as libc::c_int; - setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) } } #[cfg(any(target_os = "solaris", target_os = "illumos"))] @@ -608,11 +612,11 @@ impl Socket { let option = libc::SO_USER_COOKIE; #[cfg(target_os = "openbsd")] let option = libc::SO_RTABLE; - setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + unsafe { setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) } } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index c77c50fece1a9..fbd89f3c8b054 100644 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -270,11 +270,11 @@ impl Socket { } None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -303,11 +303,11 @@ impl Socket { } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -317,7 +317,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 2221023a21252..2fdebe6c90d84 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -384,11 +384,11 @@ impl Socket { } None => 0, }; - setsockopt(self, c::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, c::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = unsafe { getsockopt(self, c::SOL_SOCKET, kind)? }; if raw == 0 { Ok(None) } else { @@ -421,26 +421,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + unsafe { setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + unsafe { setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = unsafe { getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)? }; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } From 1da0092218e4b7bbc25034c3e5b98cb85a01a619 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 30 Sep 2025 13:05:45 +0200 Subject: [PATCH 1085/1900] `redundant_pattern_match`: clean-up - cast `&[Arm]` to `&[Arm; 2]` early on to hopefully avoid bounds checks - use `if let [pattern] = X { .. }` instead of `if let patterns = X { let pattern = patterns[0]; .. }` - use `Symbol`s instead of `&str`s - reduce indentation --- .../src/matches/redundant_pattern_match.rs | 147 ++++++++---------- 1 file changed, 67 insertions(+), 80 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c936c96f9719a..9d0115791838c 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -269,66 +269,61 @@ fn find_method_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); - let result_expr = match &op.kind { - ExprKind::AddrOf(_, _, borrowed) => borrowed, - _ => op, - }; - let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); - let mut sugg = format!("{receiver_sugg}.{good_method}"); - - if let Some(guard) = maybe_guard { - // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! - // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, - // counter to the intuition that it should be `Guard::IfLet`, so we need another check - // to see that there aren't any let chains anywhere in the guard, as that would break - // if we suggest `t.is_none() && (let X = y && z)` for: - // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { - if matches!(expr.kind, ExprKind::Let(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some(); - - if has_nested_let_chain { - return; + if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized + && let Some((good_method, maybe_guard)) = found_good_method(cx, arms) + { + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); + + if let Some(guard) = maybe_guard { + // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! + // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, + // counter to the intuition that it should be `Guard::IfLet`, so we need another check + // to see that there aren't any let chains anywhere in the guard, as that would break + // if we suggest `t.is_none() && (let X = y && z)` for: + // `match t { None if let X = y && z => true, _ => false }` + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { + if matches!(expr.kind, ExprKind::Let(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } + }) + .is_some(); - let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_paren()); + if has_nested_let_chain { + return; } - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN_MATCHING, - span, - format!("redundant pattern matching, consider using `{good_method}`"), - "try", - sugg, - app, - ); + let guard = Sugg::hir(cx, guard, ".."); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } + + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN_MATCHING, + span, + format!("redundant pattern matching, consider using `{good_method}`"), + "try", + sugg, + app, + ); } } fn found_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], - node: (&PatKind<'_>, &PatKind<'_>), + arms: &'tcx [Arm<'tcx>; 2], ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - match node { - (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) - if patterns_left.len() == 1 && patterns_right.len() == 1 => - { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + match (&arms[0].pat.kind, &arms[1].pat.kind) { + (PatKind::TupleStruct(path_left, [pattern_left], _), PatKind::TupleStruct(path_right, [pattern_right], _)) => { + if let (PatKind::Wild, PatKind::Wild) = (&pattern_left.kind, &pattern_right.kind) { find_good_method_for_match( cx, arms, @@ -356,7 +351,7 @@ fn found_good_method<'tcx>( } }, ( - PatKind::TupleStruct(path_left, patterns, _), + PatKind::TupleStruct(path_left, [pattern], _), PatKind::Expr(PatExpr { kind: PatExprKind::Path(path_right), .. @@ -367,9 +362,9 @@ fn found_good_method<'tcx>( kind: PatExprKind::Path(path_left), .. }), - PatKind::TupleStruct(path_right, patterns, _), - ) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(path_right, [pattern], _), + ) => { + if let PatKind::Wild = pattern.kind { find_good_method_for_match( cx, arms, @@ -396,8 +391,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + (PatKind::TupleStruct(path_left, [pattern], _), PatKind::Wild) => { + if let PatKind::Wild = pattern.kind { get_good_method(cx, arms, path_left) } else { None @@ -426,31 +421,23 @@ fn get_ident(path: &QPath<'_>) -> Option { fn get_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - if let Some(name) = get_ident(path_left) { - let (expected_item_left, should_be_left, should_be_right) = match name.as_str() { - "Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"), - "Err" => (Item::Lang(ResultErr), "is_err()", "is_ok()"), - "Some" => (Item::Lang(OptionSome), "is_some()", "is_none()"), - "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), - "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), - "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), - _ => return None, - }; - return find_good_method_for_matches_macro( - cx, - arms, - path_left, - expected_item_left, - should_be_left, - should_be_right, - ); - } - None + let ident = get_ident(path_left)?; + + let (expected_item_left, should_be_left, should_be_right) = match ident.name { + sym::Ok => (Item::Lang(ResultOk), "is_ok()", "is_err()"), + sym::Err => (Item::Lang(ResultErr), "is_err()", "is_ok()"), + sym::Some => (Item::Lang(OptionSome), "is_some()", "is_none()"), + sym::None => (Item::Lang(OptionNone), "is_none()", "is_some()"), + sym::Ready => (Item::Lang(PollReady), "is_ready()", "is_pending()"), + sym::Pending => (Item::Lang(PollPending), "is_pending()", "is_ready()"), + sym::V4 => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + sym::V6 => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), + _ => return None, + }; + find_good_method_for_matches_macro(cx, arms, path_left, expected_item_left, should_be_left, should_be_right) } #[derive(Clone, Copy)] @@ -490,7 +477,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte #[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, path_right: &QPath<'_>, expected_item_left: Item, @@ -525,7 +512,7 @@ fn find_good_method_for_match<'a, 'tcx>( fn find_good_method_for_matches_macro<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, expected_item_left: Item, should_be_left: &'a str, From bf6f75f98ebb5304800d45e58d08ed8a5cc7c584 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 30 Sep 2025 18:07:27 +0300 Subject: [PATCH 1086/1900] Add regression test for another (long-standing) bug fixed by the new solver --- .../hir-ty/src/tests/regression/new_solver.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index c7711f31bf262..adc35cc9bc1cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -1,6 +1,6 @@ use expect_test::expect; -use crate::tests::{check_infer, check_no_mismatches}; +use crate::tests::{check_infer, check_no_mismatches, check_types}; #[test] fn regression_20365() { @@ -450,3 +450,25 @@ fn main() { "#, ); } + +#[test] +fn double_into_iter() { + check_types( + r#" +//- minicore: iterator + +fn intoiter_issue(foo: A) +where + A: IntoIterator, + B: IntoIterator, +{ + for x in foo { + // ^ B + for m in x { + // ^ usize + } + } +} +"#, + ); +} From 3534594029ed1495290e013647a1f53da561f7f1 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 17:10:22 +0200 Subject: [PATCH 1087/1900] std: merge address family computation --- library/std/src/os/unix/net/datagram.rs | 2 +- library/std/src/os/unix/net/listener.rs | 4 ++-- library/std/src/os/unix/net/stream.rs | 4 ++-- .../std/src/sys/net/connection/socket/hermit.rs | 10 +--------- library/std/src/sys/net/connection/socket/mod.rs | 15 +++++++++++---- .../std/src/sys/net/connection/socket/solid.rs | 16 +++------------- .../std/src/sys/net/connection/socket/unix.rs | 14 +++----------- .../std/src/sys/net/connection/socket/wasip2.rs | 12 ++---------- .../std/src/sys/net/connection/socket/windows.rs | 6 +----- 9 files changed, 26 insertions(+), 57 deletions(-) diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 469bfbb0d837f..163267be1e5c1 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -159,7 +159,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn unbound() -> io::Result { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_DGRAM)?; Ok(UnixDatagram(inner)) } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 27428c9eb2855..5b4659e261883 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -71,7 +71,7 @@ impl UnixListener { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind>(path: P) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; #[cfg(any( target_os = "windows", @@ -136,7 +136,7 @@ impl UnixListener { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; #[cfg(target_os = "linux")] const backlog: core::ffi::c_int = -1; #[cfg(not(target_os = "linux"))] diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index ea4171a7d2877..851ff7f08795b 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -105,7 +105,7 @@ impl UnixStream { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect>(path: P) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; cvt(libc::connect(inner.as_raw_fd(), (&raw const addr) as *const _, len))?; @@ -139,7 +139,7 @@ impl UnixStream { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; cvt(libc::connect( inner.as_raw_fd(), (&raw const socket_addr.addr) as *const _, diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 0c105ed20fa7e..2f5c6fa31d407 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -37,15 +37,7 @@ pub fn init() {} pub struct Socket(FileDesc); impl Socket { - pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: i32, ty: i32) -> io::Result { + pub fn new(fam: i32, ty: i32) -> io::Result { let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index d01a52b861091..d0a4a2fab497d 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -178,6 +178,13 @@ fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { } } +fn addr_family(addr: &SocketAddr) -> c_int { + match addr { + SocketAddr::V4(..) => c::AF_INET, + SocketAddr::V6(..) => c::AF_INET6, + } +} + /// Converts the C socket address stored in `storage` to a Rust `SocketAddr`. /// /// # Safety @@ -364,7 +371,7 @@ impl TcpStream { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; sock.connect(addr)?; Ok(TcpStream { inner: sock }) } @@ -373,7 +380,7 @@ impl TcpStream { pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { init(); - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; sock.connect_timeout(addr, timeout)?; Ok(TcpStream { inner: sock }) } @@ -535,7 +542,7 @@ impl TcpListener { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; // On platforms with Berkeley-derived sockets, this allows to quickly // rebind a socket, without needing to wait for the OS to clean up the @@ -661,7 +668,7 @@ impl UdpSocket { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_DGRAM)?; let (addr, len) = socket_addr_to_c(addr); cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; Ok(UdpSocket { inner: sock }) diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index dfde5513f909f..14cf75adcc06f 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -115,19 +115,9 @@ pub fn init() {} pub struct Socket(OwnedFd); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(netc::socket(fam, ty, 0))?; - Ok(Self::from_raw_fd(fd)) - } + pub fn new(fam: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) } pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 3f05449903fd1..559e27604a9d3 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -63,15 +63,7 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { } impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + pub fn new(family: c_int, ty: c_int) -> io::Result { cfg_select! { any( target_os = "android", @@ -89,7 +81,7 @@ impl Socket { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(unsafe { libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0) })?; + let fd = cvt(unsafe { libc::socket(family, ty | libc::SOCK_CLOEXEC, 0) })?; let socket = Socket(unsafe { FileDesc::from_raw_fd(fd) }); // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` @@ -100,7 +92,7 @@ impl Socket { Ok(socket) } _ => { - let fd = cvt(unsafe { libc::socket(fam, ty, 0) })?; + let fd = cvt(unsafe { libc::socket(family, ty, 0) })?; let fd = unsafe { FileDesc::from_raw_fd(fd) }; fd.set_cloexec()?; let socket = Socket(fd); diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index fbd89f3c8b054..a1b08609eb024 100644 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -74,16 +74,8 @@ pub struct WasiSocket(OwnedFd); pub struct Socket(WasiSocket); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + pub fn new(family: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(family, ty, 0) })?; Ok(unsafe { Self::from_raw_fd(fd) }) } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 2fdebe6c90d84..6dbebc5e276ec 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -117,11 +117,7 @@ pub use crate::sys::pal::winsock::{cvt, cvt_gai, cvt_r, startup as init}; pub struct Socket(OwnedSocket); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let family = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; + pub fn new(family: c_int, ty: c_int) -> io::Result { let socket = unsafe { c::WSASocketW( family, From ba13b6ec6f919430412d99a3d50ff87042a787a7 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 29 Sep 2025 21:02:52 -0700 Subject: [PATCH 1088/1900] bootstrap: build bootstrap docs with in-tree rustdoc All of the docs need to be built with the same rustdoc. Otherwise, any change to the search index breaks everything, because the two rustdocs don't agree on the format. --- src/bootstrap/src/core/build_steps/doc.rs | 21 +++++++++++---------- src/bootstrap/src/core/builder/tests.rs | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 7865b68565930..37462c63f1b09 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1024,12 +1024,9 @@ macro_rules! tool_doc { run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); compilers.build_compiler() } - Mode::ToolBootstrap => { - // bootstrap/host tools should be documented with the stage 0 compiler - prepare_doc_compiler(run.builder, run.builder.host_target, 1) - } Mode::ToolTarget => { - // target tools should be documented with the in-tree compiler + // when shipping multiple docs together in one folder, + // they all need to use the same rustdoc version prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage) } _ => { @@ -1132,7 +1129,11 @@ macro_rules! tool_doc { tool_doc!( BuildHelper, "src/build_helper", - mode = Mode::ToolBootstrap, + // ideally, this would use ToolBootstrap, + // but we distribute these docs together in the same folder + // as a bunch of stage1 tools, and you can't mix rustdoc versions + // because that breaks cross-crate data (particularly search) + mode = Mode::ToolTarget, is_library = true, crates = ["build_helper"] ); @@ -1175,25 +1176,25 @@ tool_doc!( // "specialization" feature in its build script when it detects a nightly toolchain. allow_features: "specialization" ); -tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]); +tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]); tool_doc!( Bootstrap, "src/bootstrap", - mode = Mode::ToolBootstrap, + mode = Mode::ToolTarget, is_library = true, crates = ["bootstrap"] ); tool_doc!( RunMakeSupport, "src/tools/run-make-support", - mode = Mode::ToolBootstrap, + mode = Mode::ToolTarget, is_library = true, crates = ["run_make_support"] ); tool_doc!( Compiletest, "src/tools/compiletest", - mode = Mode::ToolBootstrap, + mode = Mode::ToolTarget, is_library = true, crates = ["compiletest"] ); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 88df469e9a099..3306435758b45 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1158,13 +1158,12 @@ mod snapshot { [doc] embedded-book (book) [doc] edition-guide (book) [doc] style-guide (book) - [build] rustdoc 0 - [doc] rustc 0 -> Tidy 1 - [doc] rustc 0 -> Bootstrap 1 + [doc] rustc 1 -> Tidy 2 + [doc] rustc 1 -> Bootstrap 2 [doc] rustc 1 -> releases 2 - [doc] rustc 0 -> RunMakeSupport 1 - [doc] rustc 0 -> BuildHelper 1 - [doc] rustc 0 -> Compiletest 1 + [doc] rustc 1 -> RunMakeSupport 2 + [doc] rustc 1 -> BuildHelper 2 + [doc] rustc 1 -> Compiletest 2 [build] rustc 0 -> RustInstaller 1 " ); @@ -2686,8 +2685,11 @@ mod snapshot { .path("src/tools/compiletest") .stage(2) .render_steps(), @r" - [build] rustdoc 0 - [doc] rustc 0 -> Compiletest 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustdoc 1 + [doc] rustc 1 -> Compiletest 2 "); } From ca0f3d16c4accc292848cf429a5ea41806d5f277 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 30 Sep 2025 18:48:54 +0300 Subject: [PATCH 1089/1900] Ignore impl trait safety errors when the trait is unresolved --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++++++-- .../src/handlers/trait_impl_incorrect_safety.rs | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9198086e9e6cb..2c1e05ca44464 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -753,7 +753,7 @@ impl Module { GenericDef::Impl(impl_def).diagnostics(db, acc); let loc = impl_def.id.lookup(db); - let source_map = db.impl_signature_with_source_map(impl_def.id).1; + let (impl_signature, source_map) = db.impl_signature_with_source_map(impl_def.id); expr_store_diagnostics(db, acc, &source_map); let file_id = loc.id.file_id; @@ -782,10 +782,17 @@ impl Module { } let trait_ = impl_def.trait_(db); - let trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); + let mut trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); let impl_is_negative = impl_def.is_negative(db); let impl_is_unsafe = impl_def.is_unsafe(db); + let trait_is_unresolved = trait_.is_none() && impl_signature.target_trait.is_some(); + if trait_is_unresolved { + // Ignore trait safety errors when the trait is unresolved, as otherwise we'll treat it as safe, + // which may not be correct. + trait_is_unsafe = impl_is_unsafe; + } + let drop_maybe_dangle = (|| { // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper let trait_ = trait_?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index dd142db8590fb..3414e972d5c90 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -127,4 +127,13 @@ struct S; "#, ); } + + #[test] + fn unsafe_unresolved_trait() { + check_diagnostics( + r#" +unsafe impl TestTrait for u32 {} + "#, + ); + } } From 089fbd35aa7c606bc61c4fef6bf3da747d507b75 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 10:53:35 +0800 Subject: [PATCH 1090/1900] fix: `let_unit_value` suggests wrongly for field init shorthand --- clippy_lints/src/unit_types/let_unit_value.rs | 48 +++++++++++++------ tests/ui/let_unit.fixed | 10 ++++ tests/ui/let_unit.rs | 10 ++++ tests/ui/let_unit.stderr | 16 ++++++- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 424aa14cd6863..2645e94358e11 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -90,11 +90,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag let mut has_in_format_capture = false; suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { - MaybeInFormatCapture::Yes => { + VariableUsage::FormatCapture => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((span, "()".to_string())), + VariableUsage::Normal(span) => Some((span, "()".to_string())), + VariableUsage::FieldShorthand(span) => Some((span.shrink_to_hi(), ": ()".to_string())), })); if has_in_format_capture { @@ -141,19 +142,30 @@ struct UnitVariableCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, macro_call: Option<&'a FormatArgs>, } -/// Whether the unit variable is captured in a `format!`: -/// -/// ```ignore -/// let unit = (); -/// eprintln!("{unit}"); -/// ``` -enum MaybeInFormatCapture { - Yes, - No(Span), +/// How the unit variable is used +enum VariableUsage { + Normal(Span), + /// Captured in a `format!`: + /// + /// ```ignore + /// let unit = (); + /// eprintln!("{unit}"); + /// ``` + FormatCapture, + /// In a field shorthand init: + /// + /// ```ignore + /// struct Foo { + /// unit: (), + /// } + /// let unit = (); + /// Foo { unit }; + /// ``` + FieldShorthand(Span), } impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { @@ -193,9 +205,17 @@ impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() }) { - self.spans.push(MaybeInFormatCapture::Yes); + self.spans.push(VariableUsage::FormatCapture); } else { - self.spans.push(MaybeInFormatCapture::No(path.span)); + let parent = self.cx.tcx.parent_hir_node(ex.hir_id); + match parent { + Node::ExprField(expr_field) if expr_field.is_shorthand => { + self.spans.push(VariableUsage::FieldShorthand(ex.span)); + }, + _ => { + self.spans.push(VariableUsage::Normal(path.span)); + }, + } } } diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index e8517f18e788b..6d984a495d2bd 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -229,3 +229,13 @@ fn issue_15784() { takes_unit(()); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + println!(); + //~^ let_unit_value + + Foo { value: () }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 3f6f0139b2fec..a0e32f0b67a02 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -227,3 +227,13 @@ fn issue_15784() { takes_unit(res); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + let value = println!(); + //~^ let_unit_value + + Foo { value }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 8ced32ab828f7..6e7b958df4d9a 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -114,5 +114,19 @@ LL | LL ~ takes_unit(()); | -error: aborting due to 8 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:235:5 + | +LL | let value = println!(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding and replace variable usages with `()` + | +LL ~ println!(); +LL | +LL | +LL ~ Foo { value: () }; + | + +error: aborting due to 9 previous errors From 195ff236c36df93fed8bee9106b3ecbfb4874df0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 01:03:16 +0200 Subject: [PATCH 1091/1900] `new_ret_no_self`: extract to a separate module --- clippy_lints/src/methods/mod.rs | 33 ++------------- clippy_lints/src/methods/new_ret_no_self.rs | 46 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 29 deletions(-) create mode 100644 clippy_lints/src/methods/new_ret_no_self.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8c2948e4a74e6..157fafa1c6155 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -80,6 +80,7 @@ mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; +mod new_ret_no_self; mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; @@ -148,11 +149,9 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::contains_ty_adt_constructor_opaque; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -4918,20 +4917,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); } - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait - && impl_item.ident.name == sym::new - && let ret_ty = return_ty(cx, impl_item.owner_id) - && ret_ty != self_ty - && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - impl_item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_impl_item(cx, impl_item, self_ty, implements_trait); } } @@ -4963,18 +4949,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); } - if item.ident.name == sym::new - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_trait_item(cx, item); } } } diff --git a/clippy_lints/src/methods/new_ret_no_self.rs b/clippy_lints/src/methods/new_ret_no_self.rs new file mode 100644 index 0000000000000..2aa28fbb5f406 --- /dev/null +++ b/clippy_lints/src/methods/new_ret_no_self.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::return_ty; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use rustc_hir::{ImplItem, TraitItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::NEW_RET_NO_SELF; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + implements_trait: bool, +) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + impl_item.span, + "methods called `new` usually return `Self`", + ); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + if trait_item.ident.name == sym::new + && let ret_ty = return_ty(cx, trait_item.owner_id) + && let self_ty = ty::TraitRef::identity(cx.tcx, trait_item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + trait_item.span, + "methods called `new` usually return `Self`", + ); + } +} From b19df975432b98c37fd37733d3c37b38e4a02b2f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 22 Sep 2025 05:13:24 +0300 Subject: [PATCH 1092/1900] Prevent rustup from automatically installing toolchains By setting RUSTUP_AUTO_INSTALL=0. --- .../crates/project-model/src/cargo_workspace.rs | 9 +++++---- .../rust-analyzer/crates/project-model/src/workspace.rs | 5 +++-- src/tools/rust-analyzer/crates/toolchain/src/lib.rs | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index adc0cc5094166..04fb2275893c7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -13,12 +13,12 @@ use serde_derive::Deserialize; use serde_json::from_value; use span::Edition; use stdx::process::spawn_with_streaming_output; -use toolchain::Tool; +use toolchain::{NO_RUSTUP_AUTO_INSTALL_ENV, Tool}; use triomphe::Arc; -use crate::cargo_config_file::make_lockfile_copy; -use crate::{CfgOverrides, InvocationStrategy}; -use crate::{ManifestPath, Sysroot}; +use crate::{ + CfgOverrides, InvocationStrategy, ManifestPath, Sysroot, cargo_config_file::make_lockfile_copy, +}; pub(crate) const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = semver::Version { @@ -632,6 +632,7 @@ impl FetchMetadata { ) -> Self { let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env); let mut command = MetadataCommand::new(); + command.env(NO_RUSTUP_AUTO_INSTALL_ENV.0, NO_RUSTUP_AUTO_INSTALL_ENV.1); command.cargo_path(cargo.get_program()); cargo.get_envs().for_each(|(var, val)| _ = command.env(var, val.unwrap_or_default())); command.manifest_path(cargo_toml.to_path_buf()); diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index e0d2105c8df89..0649ce9eeb9df 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -16,8 +16,9 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use span::{Edition, FileId}; -use toolchain::Tool; +use toolchain::{NO_RUSTUP_AUTO_INSTALL_ENV, Tool}; use tracing::instrument; +use tracing::{debug, error, info}; use triomphe::Arc; use crate::{ @@ -33,7 +34,6 @@ use crate::{ toolchain_info::{QueryConfig, rustc_cfg, target_data, target_tuple, version}, utf8_stdout, }; -use tracing::{debug, error, info}; pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option; @@ -1907,6 +1907,7 @@ fn cargo_target_dir( ) -> Option { let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env); let mut meta = cargo_metadata::MetadataCommand::new(); + meta.env(NO_RUSTUP_AUTO_INSTALL_ENV.0, NO_RUSTUP_AUTO_INSTALL_ENV.1); meta.cargo_path(cargo.get_program()); meta.manifest_path(manifest); // `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve. diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 8b7bf1a806415..39319886cfe4a 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -71,6 +71,9 @@ impl Tool { } } +// Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. +pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ pub fn command( cmd: impl AsRef, @@ -81,6 +84,7 @@ pub fn command( #[allow(clippy::disallowed_methods)] let mut cmd = Command::new(cmd); cmd.current_dir(working_directory); + cmd.env(NO_RUSTUP_AUTO_INSTALL_ENV.0, NO_RUSTUP_AUTO_INSTALL_ENV.1); for env in extra_env { match env { (key, Some(val)) => cmd.env(key, val), From b3219502d71776bd2ab5be92734f1a05c2b01eb2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 30 Sep 2025 17:43:02 +0200 Subject: [PATCH 1093/1900] `map_identity`: simplify lint emission Instead of calling `span_lint_and_*` in multiple places, call `span_lint_and_then` once and then use the `Diag` methods. --- clippy_lints/src/methods/map_identity.rs | 80 +++++++++++------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index a98cfff8bfbd1..6190c43578e9b 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; @@ -27,48 +27,46 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { - let main_sugg = (call_span, String::new()); - let mut app = if is_copy(cx, caller_ty) { - // there is technically a behavioral change here for `Copy` iterators, where - // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and - // changing it to `iter.next()` mutates iter directly - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; - let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); - if needs_to_be_mutable && !is_mutable(cx, caller) { - if let Some(hir_id) = path_to_local_with_projections(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && let PatKind::Binding(_, _, ident, _) = pat.kind - { - // We can reach the binding -- suggest making it mutable - let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; - let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.multipart_suggestion( format!("remove the call to `{name}`, and make `{ident}` mutable"), suggs, app, ); - }); - } else { - // If we can't make the binding mutable, prevent the suggestion from being automatically applied, - // and add a complementary help message. - app = Applicability::Unspecified; - - let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::MethodCall(method, ..) = expr.kind - { - Some(method.ident) } else { - None - }; + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); let note = if let Some(method_requiring_mut) = method_requiring_mut { @@ -77,18 +75,10 @@ pub(super) fn check( "this must be made mutable".to_string() }; diag.span_note(caller.span, note); - }); + } + } else { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); } - } else { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - main_sugg.0, - MSG, - format!("remove the call to `{name}`"), - main_sugg.1, - app, - ); - } + }); } } From e9ede27a5d880e5b8075ffb98dd381f4d5ce09ca Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 24 Aug 2025 09:31:06 +0200 Subject: [PATCH 1094/1900] test: remove extraneous `#[allow(clippy::uninlined_format_args)]` These were probably added automatically to avoid code churn. I'd like to get rid of them bit by bit, so here's the first bit --- .../conf_deprecated_key.rs | 4 +- .../conf_deprecated_key.stderr | 2 +- .../index_refutable_slice.fixed | 1 - .../index_refutable_slice.rs | 1 - .../index_refutable_slice.stderr | 4 +- tests/ui/bind_instead_of_map.fixed | 1 - tests/ui/bind_instead_of_map.rs | 1 - tests/ui/bind_instead_of_map.stderr | 6 +- .../shared_at_top_and_bottom.rs | 5 +- .../shared_at_top_and_bottom.stderr | 24 ++-- tests/ui/cast_abs_to_unsigned.fixed | 4 +- tests/ui/cast_abs_to_unsigned.rs | 4 +- tests/ui/crashes/ice-4775.rs | 2 +- tests/ui/default_trait_access.fixed | 2 +- tests/ui/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 +-- tests/ui/explicit_counter_loop.rs | 24 ++-- tests/ui/explicit_write.fixed | 5 +- tests/ui/explicit_write.rs | 5 +- tests/ui/explicit_write.stderr | 34 ++--- tests/ui/fallible_impl_from.rs | 3 +- tests/ui/fallible_impl_from.stderr | 20 +-- .../if_let_slice_binding.fixed | 14 +- .../if_let_slice_binding.rs | 14 +- tests/ui/infinite_iter.rs | 4 +- tests/ui/infinite_iter.stderr | 4 +- tests/ui/issue_2356.fixed | 3 +- tests/ui/issue_2356.rs | 3 +- tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 3 +- tests/ui/issue_4266.stderr | 6 +- tests/ui/match_single_binding.fixed | 37 +++-- tests/ui/match_single_binding.rs | 37 +++-- tests/ui/match_single_binding.stderr | 130 +++++++++--------- tests/ui/match_single_binding2.fixed | 3 +- tests/ui/match_single_binding2.rs | 3 +- tests/ui/match_single_binding2.stderr | 12 +- 37 files changed, 214 insertions(+), 231 deletions(-) diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index ecb43dc34a8a0..b28e46af0a467 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,5 +1,3 @@ -#![allow(clippy::uninlined_format_args)] - fn main() {} #[warn(clippy::cognitive_complexity)] @@ -8,7 +6,7 @@ fn cognitive_complexity() { let x = vec![1, 2, 3]; for i in x { if i == 1 { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 627498dc175cd..95b0508189e9c 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -11,7 +11,7 @@ LL | blacklisted-names = [ "..", "wibble" ] | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) - --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4 + --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:4:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 2877871d0bf4c..36540bf1dcf73 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index f958b92a102a3..da76bb20fd961 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index e1a8941e102f5..022deb330e6e3 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 80e010e2dfd74..fa35a01242d1d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 09aa8480cbd9e..403077e72ff98 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 08f85fb58549c..3f8d631591e93 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:8:13 + --> tests/ui/bind_instead_of_map.rs:7:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> tests/ui/bind_instead_of_map.rs:10:13 + --> tests/ui/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:17:13 + --> tests/ui/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index e848f0601e32d..1646b5705b6dd 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,6 +1,5 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] //@no-rustfix // branches_sharing_code at the top and bottom of the if blocks @@ -70,7 +69,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("From the a `{}` to the b `{}`", a, b); + println!("From the a `{a}` to the b `{b}`"); let pack = DataPack { id: e_id, @@ -83,7 +82,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("The new ID is '{}'", e_id); + println!("The new ID is '{e_id}'"); let pack = DataPack { id: e_id, diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 40f3453edb9ae..f5c51f7888d04 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:17:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { LL | | @@ -10,7 +10,7 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:31:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:30:5 | LL | / let _u = 9; LL | | } @@ -34,7 +34,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:35:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:34:5 | LL | / if x == 99 { LL | | @@ -45,7 +45,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:48:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:47:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -67,7 +67,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:66:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:65:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | @@ -78,7 +78,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:88:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:87:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -108,7 +108,7 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:101:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:100:5 | LL | / let _ = if x == 7 { ... | @@ -116,7 +116,7 @@ LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:112:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:111:5 | LL | / x << 2 LL | | }; @@ -134,7 +134,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:115:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:114:5 | LL | / if x == 9 { ... | @@ -142,7 +142,7 @@ LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:126:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:125:5 | LL | / x * 4 LL | | } @@ -160,7 +160,7 @@ LL + x * 4 | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:157:9 | LL | / if false { LL | | @@ -168,7 +168,7 @@ LL | | let x = 1; | |______________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:165:9 | LL | / let y = 1; LL | | } diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index b55c22f5ca83c..44cb66a8d1848 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.unsigned_abs() as usize; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 466aa6aeb1fbb..555b9090fe437 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.abs() as usize; diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index dd6c6b8de25af..e8c47b4f3a77b 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -7,7 +7,7 @@ pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { pub fn ice(&self) { for i in self.0.iter() { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index d3fe09a052ef3..e3bf603da80f3 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index cdffb2a2ee8ca..8cc065e5bced2 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index aa7eb4f89558c..aa89516f175c4 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,5 +1,5 @@ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:13:22 + --> tests/ui/default_trait_access.rs:12:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` @@ -11,43 +11,43 @@ LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:18:22 + --> tests/ui/default_trait_access.rs:17:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:21:22 + --> tests/ui/default_trait_access.rs:20:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:26:22 + --> tests/ui/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:37:46 + --> tests/ui/default_trait_access.rs:36:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:44:36 + --> tests/ui/default_trait_access.rs:43:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:47:36 + --> tests/ui/default_trait_access.rs:46:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:52:42 + --> tests/ui/default_trait_access.rs:51:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 13934785d7b15..ec4cecf377667 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,5 +1,5 @@ #![warn(clippy::explicit_counter_loop)] -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] +#![allow(clippy::useless_vec)] //@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; @@ -89,13 +89,13 @@ mod issue_1219 { for _v in &vec { index += 1 } - println!("index: {}", index); + println!("index: {index}"); // should not trigger the lint because the count is conditional #1219 let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { continue; } @@ -106,7 +106,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { count += 1; } @@ -118,7 +118,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; if ch == 'a' { continue; @@ -131,7 +131,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { let _ = 123; @@ -142,7 +142,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { count += 1; @@ -157,7 +157,7 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); while erasures.contains(&(i + skips)) { skips += 1; } @@ -166,7 +166,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); let mut j = 0; while j < 5 { skips += 1; @@ -177,7 +177,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); for j in 0..5 { skips += 1; } @@ -205,7 +205,7 @@ mod issue_4732 { for _v in slice { index += 1 } - let _closure = || println!("index: {}", index); + let _closure = || println!("index: {index}"); } } @@ -217,7 +217,7 @@ mod issue_4677 { let mut count = 0; for _i in slice { count += 1; - println!("{}", count); + println!("{count}"); } } } diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 024999fc609ee..ab28c1ccd8e0d 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - eprintln!("with {}", value); + eprintln!("with {value}"); //~^ explicit_write eprintln!("with {} {}", 2, value); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { eprintln!("macro arg {}", one!()); //~^ explicit_write let width = 2; - eprintln!("{:w$}", value, w = width); + eprintln!("{value:w$}", w = width); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index c83c760d48c8b..975ee103b6278 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - writeln!(std::io::stderr(), "with {}", value).unwrap(); + writeln!(std::io::stderr(), "with {value}").unwrap(); //~^ explicit_write writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); //~^ explicit_write let width = 2; - writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); + writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 670a0411b3107..ef4b2a049a6d9 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:23:9 + --> tests/ui/explicit_write.rs:22:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` @@ -8,76 +8,76 @@ LL | write!(std::io::stdout(), "test").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::explicit_write)]` error: use of `write!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:25:9 + --> tests/ui/explicit_write.rs:24:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:27:9 + --> tests/ui/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:29:9 + --> tests/ui/explicit_write.rs:28:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:31:9 + --> tests/ui/explicit_write.rs:30:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:33:9 + --> tests/ui/explicit_write.rs:32:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:37:9 + --> tests/ui/explicit_write.rs:36:9 | LL | writeln!(std::io::stdout(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:39:9 + --> tests/ui/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:43:9 + --> tests/ui/explicit_write.rs:42:9 | -LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {}", value)` +LL | writeln!(std::io::stderr(), "with {value}").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:45:9 + --> tests/ui/explicit_write.rs:44:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:47:9 + --> tests/ui/explicit_write.rs:46:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:49:9 + --> tests/ui/explicit_write.rs:48:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:52:9 + --> tests/ui/explicit_write.rs:51:9 | -LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{:w$}", value, w = width)` +LL | writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{value:w$}", w = width)` error: aborting due to 13 previous errors diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 1c62c1e937b63..28bb1157f6e52 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,5 +1,4 @@ #![deny(clippy::fallible_impl_from)] -#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); @@ -62,7 +61,7 @@ impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { if s.parse::().ok().unwrap() != 42 { - panic!("{:?}", s); + panic!("{s:?}"); } Invalid } diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 402494b39f30b..25ecc8b0a39a2 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:6:1 + --> tests/ui/fallible_impl_from.rs:5:1 | LL | / impl From for Foo { LL | | @@ -11,7 +11,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:10:13 + --> tests/ui/fallible_impl_from.rs:9:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:29:1 + --> tests/ui/fallible_impl_from.rs:28:1 | LL | / impl From for Invalid { LL | | @@ -34,13 +34,13 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:34:13 + --> tests/ui/fallible_impl_from.rs:33:13 | LL | panic!(); | ^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:40:1 + --> tests/ui/fallible_impl_from.rs:39:1 | LL | / impl From> for Invalid { LL | | @@ -52,7 +52,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:44:17 + --> tests/ui/fallible_impl_from.rs:43:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:60:1 + --> tests/ui/fallible_impl_from.rs:59:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | @@ -77,12 +77,12 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:64:12 + --> tests/ui/fallible_impl_from.rs:63:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^ +LL | panic!("{s:?}"); + | ^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index 050cdfcba966e..dc7e09bbdc7d2 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice_1); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice_0); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice_0); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 91429bfea2762..f39ace101b45c 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice[1]); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice[0]); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice[0]); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 701a86534ba00..4e1668ed04fb7 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] +#![allow(clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { @@ -30,7 +30,7 @@ fn infinite_iters() { .rev() .cycle() .map(|x| x + 1_u32) - .for_each(|x| println!("{}", x)); + .for_each(|x| println!("{x}")); // infinite iter (0..3_u32).flat_map(|x| x..).sum::(); // infinite iter diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index b9e7c008f93e0..3db97313b621d 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -30,8 +30,8 @@ LL | | LL | | .rev() LL | | .cycle() LL | | .map(|x| x + 1_u32) -LL | | .for_each(|x| println!("{}", x)); - | |________________________________________^ +LL | | .for_each(|x| println!("{x}")); + | |______________________________________^ error: infinite iteration detected --> tests/ui/infinite_iter.rs:37:5 diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 46ba653eba2cf..3e066df77bfb4 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { for e in it { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index defe2584a93e1..98600d17c6df7 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { while let Some(e) = it.next() { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index eae2ce97fc6b1..ddee91fcfcd52 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> tests/ui/issue_2356.rs:17:9 + --> tests/ui/issue_2356.rs:16:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index 664f0b84a2077..b2a01124995c5 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint //~^ needless_lifetimes @@ -39,7 +38,7 @@ impl Foo { // rust-lang/rust#61115 // ok async fn print(s: &str) { - println!("{}", s); + println!("{s}"); } fn main() {} diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 0e181025430f3..b80a738a50bee 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:4:16 + --> tests/ui/issue_4266.rs:3:16 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^ ^^ @@ -8,13 +8,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = help: to override `-D warnings` add `#[allow(clippy::needless_lifetimes)]` error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:10:21 + --> tests/ui/issue_4266.rs:9:21 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^ ^^ ^^ error: methods called `new` usually take no `self` - --> tests/ui/issue_4266.rs:32:22 + --> tests/ui/issue_4266.rs:31:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e29fb87dbc300..7e899a4766661 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -32,11 +31,11 @@ fn main() { // Lint let (x, y, z) = (a, b, c); { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } // Lint let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); // Ok foo!(a); // Ok @@ -47,7 +46,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -55,7 +54,7 @@ fn main() { // Lint { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); } // Lint { @@ -67,18 +66,18 @@ fn main() { // Lint let p = Point { x: 0, y: 7 }; let Point { x, y } = p; - println!("Coords: ({}, {})", x, y); + println!("Coords: ({x}, {y})"); // Lint let Point { x: x1, y: y1 } = p; - println!("Coords: ({}, {})", x1, y1); + println!("Coords: ({x1}, {y1})"); // Lint let x = 5; let ref r = x; - println!("Got a reference to {}", r); + println!("Got a reference to {r}"); // Lint let mut x = 5; let ref mut mr = x; - println!("Got a mutable reference to {}", mr); + println!("Got a mutable reference to {mr}"); // Lint let Point { x, y } = coords(); let product = x * y; @@ -122,7 +121,7 @@ fn issue_8723() { let (pre, suf) = val.split_at(idx); val = { - println!("{}", pre); + println!("{pre}"); suf }; @@ -210,20 +209,20 @@ mod issue15018 { let x = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } println!("x = {x}"); } fn not_used_later(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let x = 1; println!("x = {x}"); } @@ -231,27 +230,27 @@ mod issue15018 { #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {x} {y}"); if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index ede1ab32beb5f..37a96f2287c85 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -33,13 +32,13 @@ fn main() { match (a, b, c) { //~^ match_single_binding (x, y, z) => { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); }, } // Lint match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } // Ok foo!(a); @@ -51,7 +50,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -64,7 +63,7 @@ fn main() { //~^ match_single_binding _ => { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); }, } // Lint @@ -81,24 +80,24 @@ fn main() { let p = Point { x: 0, y: 7 }; match p { //~^ match_single_binding - Point { x, y } => println!("Coords: ({}, {})", x, y), + Point { x, y } => println!("Coords: ({x}, {y})"), } // Lint match p { //~^ match_single_binding - Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), } // Lint let x = 5; match x { //~^ match_single_binding - ref r => println!("Got a reference to {}", r), + ref r => println!("Got a reference to {r}"), } // Lint let mut x = 5; match x { //~^ match_single_binding - ref mut mr => println!("Got a mutable reference to {}", mr), + ref mut mr => println!("Got a mutable reference to {mr}"), } // Lint let product = match coords() { @@ -150,7 +149,7 @@ fn issue_8723() { val = match val.split_at(idx) { //~^ match_single_binding (pre, suf) => { - println!("{}", pre); + println!("{pre}"); suf }, }; @@ -273,7 +272,7 @@ mod issue15018 { let x = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } println!("x = {x}"); } @@ -281,7 +280,7 @@ mod issue15018 { fn not_used_later(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } } @@ -289,7 +288,7 @@ mod issue15018 { fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let x = 1; println!("x = {x}"); @@ -299,30 +298,30 @@ mod issue15018 { fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {x} {y}"), } if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index eea71777890e5..82fc43aaa5eae 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,10 +1,10 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:33:5 + --> tests/ui/match_single_binding.rs:32:5 | LL | / match (a, b, c) { LL | | LL | | (x, y, z) => { -LL | | println!("{} {} {}", x, y, z); +LL | | println!("{x} {y} {z}"); LL | | }, LL | | } | |_____^ @@ -15,27 +15,27 @@ help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:40:5 + --> tests/ui/match_single_binding.rs:39:5 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:58:5 + --> tests/ui/match_single_binding.rs:57:5 | LL | / match a { LL | | @@ -44,13 +44,13 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:63:5 + --> tests/ui/match_single_binding.rs:62:5 | LL | / match a { LL | | LL | | _ => { LL | | let x = 29; -LL | | println!("x has a value of {}", x); +LL | | println!("x has a value of {x}"); LL | | }, LL | | } | |_____^ @@ -59,12 +59,12 @@ help: consider using the match body instead | LL ~ { LL + let x = 29; -LL + println!("x has a value of {}", x); +LL + println!("x has a value of {x}"); LL + } | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:71:5 + --> tests/ui/match_single_binding.rs:70:5 | LL | / match a { LL | | @@ -86,67 +86,67 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:82:5 + --> tests/ui/match_single_binding.rs:81:5 | LL | / match p { LL | | -LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | Point { x, y } => println!("Coords: ({x}, {y})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x, y } = p; -LL + println!("Coords: ({}, {})", x, y); +LL + println!("Coords: ({x}, {y})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:87:5 + --> tests/ui/match_single_binding.rs:86:5 | LL | / match p { LL | | -LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; -LL + println!("Coords: ({}, {})", x1, y1); +LL + println!("Coords: ({x1}, {y1})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:93:5 + --> tests/ui/match_single_binding.rs:92:5 | LL | / match x { LL | | -LL | | ref r => println!("Got a reference to {}", r), +LL | | ref r => println!("Got a reference to {r}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref r = x; -LL + println!("Got a reference to {}", r); +LL + println!("Got a reference to {r}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:99:5 + --> tests/ui/match_single_binding.rs:98:5 | LL | / match x { LL | | -LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | ref mut mr => println!("Got a mutable reference to {mr}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref mut mr = x; -LL + println!("Got a mutable reference to {}", mr); +LL + println!("Got a mutable reference to {mr}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:104:5 + --> tests/ui/match_single_binding.rs:103:5 | LL | / let product = match coords() { LL | | @@ -161,7 +161,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:113:18 + --> tests/ui/match_single_binding.rs:112:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -179,7 +179,7 @@ LL ~ }) | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:140:5 + --> tests/ui/match_single_binding.rs:139:5 | LL | / match x { LL | | @@ -189,12 +189,12 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> tests/ui/match_single_binding.rs:150:5 + --> tests/ui/match_single_binding.rs:149:5 | LL | / val = match val.split_at(idx) { LL | | LL | | (pre, suf) => { -LL | | println!("{}", pre); +LL | | println!("{pre}"); LL | | suf LL | | }, LL | | }; @@ -204,13 +204,13 @@ help: consider removing the `match` expression | LL ~ let (pre, suf) = val.split_at(idx); LL + val = { -LL + println!("{}", pre); +LL + println!("{pre}"); LL + suf LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:164:16 + --> tests/ui/match_single_binding.rs:163:16 | LL | let _ = || match side_effects() { | ________________^ @@ -228,7 +228,7 @@ LL ~ }; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:171:5 + --> tests/ui/match_single_binding.rs:170:5 | LL | / match r { LL | | @@ -253,7 +253,7 @@ LL ~ }; | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:185:5 + --> tests/ui/match_single_binding.rs:184:5 | LL | / match 1 { LL | | @@ -262,7 +262,7 @@ LL | | } | |_____^ help: consider using the match body instead: `();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:190:13 + --> tests/ui/match_single_binding.rs:189:13 | LL | let a = match 1 { | _____________^ @@ -272,7 +272,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:195:5 + --> tests/ui/match_single_binding.rs:194:5 | LL | / match 1 { LL | | @@ -281,7 +281,7 @@ LL | | } | |_____^ help: consider using the match body instead: `side_effects();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:200:13 + --> tests/ui/match_single_binding.rs:199:13 | LL | let b = match 1 { | _____________^ @@ -291,7 +291,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:205:5 + --> tests/ui/match_single_binding.rs:204:5 | LL | / match 1 { LL | | @@ -300,7 +300,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("1");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:210:13 + --> tests/ui/match_single_binding.rs:209:13 | LL | let c = match 1 { | _____________^ @@ -310,7 +310,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:216:9 + --> tests/ui/match_single_binding.rs:215:9 | LL | / match 1 { LL | | @@ -319,7 +319,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:220:9 + --> tests/ui/match_single_binding.rs:219:9 | LL | / match 1 { LL | | @@ -328,7 +328,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:224:9 + --> tests/ui/match_single_binding.rs:223:9 | LL | / match 1 { LL | | @@ -337,7 +337,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:239:5 + --> tests/ui/match_single_binding.rs:238:5 | LL | / match dbg!(3) { LL | | _ => println!("here"), @@ -351,7 +351,7 @@ LL + println!("here"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:243:5 + --> tests/ui/match_single_binding.rs:242:5 | LL | / match dbg!(3) { LL | | id!(a) => println!("found {a}"), @@ -365,7 +365,7 @@ LL + println!("found {a}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:247:5 + --> tests/ui/match_single_binding.rs:246:5 | LL | / let id!(_a) = match dbg!(3) { LL | | id!(b) => dbg!(b + 1), @@ -379,7 +379,7 @@ LL + let id!(_a) = dbg!(b + 1); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:255:21 + --> tests/ui/match_single_binding.rs:254:21 | LL | inner: [(); match 1 { | _____________________^ @@ -397,7 +397,7 @@ LL ~ }], | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:263:13 + --> tests/ui/match_single_binding.rs:262:13 | LL | / match 1 { LL | | @@ -412,11 +412,11 @@ LL + 42 | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:274:9 + --> tests/ui/match_single_binding.rs:273:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | @@ -424,61 +424,61 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:282:9 + --> tests/ui/match_single_binding.rs:281:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z) +LL + println!("{x} {y} {z}") | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:290:9 + --> tests/ui/match_single_binding.rs:289:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:300:9 + --> tests/ui/match_single_binding.rs:299:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {x} {y}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {x} {y}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:310:13 + --> tests/ui/match_single_binding.rs:309:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | @@ -486,27 +486,27 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:320:13 + --> tests/ui/match_single_binding.rs:319:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:335:12 + --> tests/ui/match_single_binding.rs:334:12 | LL | && match b { | ____________^ @@ -516,7 +516,7 @@ LL | | }; | |_________^ help: consider using the match body instead: `b < c` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:341:12 + --> tests/ui/match_single_binding.rs:340:12 | LL | && match (a, b) { | ____________^ diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index 988121f50d0fa..f00987470ae1b 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -29,7 +28,7 @@ fn main() { #[rustfmt::skip] Some((first, _second)) => { let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b) + println!("a {a:?} and b {b:?}") }, None => println!("nothing"), } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index a4fb2bd6f3817..5416f647b4e6c 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -30,7 +29,7 @@ fn main() { Some((first, _second)) => { match get_tup() { //~^ match_single_binding - (a, b) => println!("a {:?} and b {:?}", a, b), + (a, b) => println!("a {a:?} and b {b:?}"), } }, None => println!("nothing"), diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index a24cbe3eed766..65b8aa6acd5e1 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:17:36 + --> tests/ui/match_single_binding2.rs:16:36 | LL | Some((iter, _item)) => match iter.size_hint() { | ____________________________________^ @@ -19,22 +19,22 @@ LL ~ }, | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:31:13 + --> tests/ui/match_single_binding2.rs:30:13 | LL | / match get_tup() { LL | | -LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | (a, b) => println!("a {a:?} and b {b:?}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); -LL + println!("a {:?} and b {:?}", a, b) +LL + println!("a {a:?} and b {b:?}") | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:43:5 + --> tests/ui/match_single_binding2.rs:42:5 | LL | / match side_effects() { LL | | @@ -49,7 +49,7 @@ LL + println!("Side effects"); | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:51:5 + --> tests/ui/match_single_binding2.rs:50:5 | LL | / match match x { LL | | From f9852613a965f2f3daccec46d63b85fb0407960d Mon Sep 17 00:00:00 2001 From: yanglsh Date: Mon, 25 Aug 2025 08:32:42 +0800 Subject: [PATCH 1095/1900] fix: `mem_replace_with_default` wrongly unmangled macros --- clippy_lints/src/mem_replace.rs | 13 +++++-------- tests/ui/mem_replace.fixed | 6 ++++++ tests/ui/mem_replace.rs | 6 ++++++ tests/ui/mem_replace.stderr | 8 +++++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e39916f733d59..95ecef5987007 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{ @@ -269,14 +269,11 @@ fn check_replace_with_default( ), |diag| { if !expr.span.from_expansion() { - let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); + let mut applicability = Applicability::MachineApplicable; + let (dest_snip, _) = snippet_with_context(cx, dest.span, expr.span.ctxt(), "", &mut applicability); + let suggestion = format!("{top_crate}::mem::take({dest_snip})"); - diag.span_suggestion( - expr.span, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + diag.span_suggestion(expr.span, "consider using", suggestion, applicability); } }, ); diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 870ef23113a2f..94ad1aad3eb75 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::take(dbg!(&mut text)); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index b4ed5eafea953..ac79660f0f1ee 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::replace(dbg!(&mut text), String::default()); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index fb4a367266d30..104c985400282 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -181,5 +181,11 @@ error: replacing an `Option` with `Some(..)` LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` -error: aborting due to 29 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> tests/ui/mem_replace.rs:185:20 + | +LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` + +error: aborting due to 30 previous errors From d1bbd39c59523d7a5499816a9da200a5910f8b7f Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 28 Sep 2025 23:04:55 +0000 Subject: [PATCH 1096/1900] Split Bound into Canonical and Bound --- compiler/rustc_borrowck/src/lib.rs | 2 +- .../src/collect/item_bounds.rs | 16 ++-- .../src/hir_ty_lowering/bounds.rs | 6 +- .../src/infer/canonical/canonicalizer.rs | 53 ++++--------- .../src/infer/canonical/instantiate.rs | 74 +++++-------------- .../src/infer/canonical/query_response.rs | 18 ++--- .../src/infer/outlives/test_type_match.rs | 6 +- compiler/rustc_middle/src/ty/consts.rs | 14 +++- compiler/rustc_middle/src/ty/context.rs | 47 +++++++++++- compiler/rustc_middle/src/ty/fold.rs | 14 +++- compiler/rustc_middle/src/ty/print/pretty.rs | 4 +- compiler/rustc_middle/src/ty/region.rs | 36 +++++++-- compiler/rustc_middle/src/ty/sty.rs | 22 +++++- .../rustc_middle/src/ty/typeck_results.rs | 12 +-- compiler/rustc_middle/src/ty/visit.rs | 6 +- .../src/canonical/canonicalizer.rs | 34 ++------- .../src/canonical/mod.rs | 12 +-- .../src/placeholder.rs | 18 +++-- .../src/unstable/convert/stable/ty.rs | 7 +- .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 5 +- compiler/rustc_symbol_mangling/src/v0.rs | 5 +- .../src/traits/dyn_compatibility.rs | 2 +- .../src/traits/query/normalize.rs | 4 +- compiler/rustc_ty_utils/src/ty.rs | 2 +- compiler/rustc_type_ir/src/binder.rs | 43 ++++++++++- compiler/rustc_type_ir/src/canonical.rs | 23 +++--- compiler/rustc_type_ir/src/const_kind.rs | 4 +- compiler/rustc_type_ir/src/flags.rs | 18 ++++- compiler/rustc_type_ir/src/fold.rs | 24 ++++-- compiler/rustc_type_ir/src/inherent.rs | 6 ++ compiler/rustc_type_ir/src/lib.rs | 17 +++-- compiler/rustc_type_ir/src/region_kind.rs | 4 +- compiler/rustc_type_ir/src/ty_kind.rs | 4 +- compiler/rustc_type_ir/src/ty_kind/closure.rs | 5 +- .../clippy_lints/src/pass_by_ref_or_value.rs | 4 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 4 +- ..._of_reborrow.SimplifyCfg-initial.after.mir | 18 ++--- ...st-mono-higher-ranked-hang.current.stderr} | 6 +- .../post-mono-higher-ranked-hang.next.stderr | 21 ++++++ .../post-mono-higher-ranked-hang.rs | 4 + .../trait-bounds/issue-59311.stderr | 2 +- .../trait-bounds/trivial-does-not-hold.stderr | 2 +- tests/ui/lifetimes/re-empty-in-error.stderr | 2 +- .../ui/nll/user-annotations/dump-fn-method.rs | 4 +- .../user-annotations/dump-fn-method.stderr | 4 +- 45 files changed, 391 insertions(+), 247 deletions(-) rename tests/ui/async-await/async-closures/{post-mono-higher-ranked-hang.stderr => post-mono-higher-ranked-hang.current.stderr} (85%) create mode 100644 tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4c380ddcf7084..8b23bc2822bf3 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -278,7 +278,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>, ) -> Ty<'tcx> { fold_regions(tcx, self.inner, |r, depth| match r.kind() { - ty::ReBound(debruijn, br) => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) => { debug_assert_eq!(debruijn, depth); map(ty::RegionVid::from_usize(br.var.index())) } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index ba54fa8cc0dbf..664ca35ddfbc9 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -174,21 +174,25 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( for (param, var) in std::iter::zip(&generics.own_params, gat_vars) { let existing = match var.kind() { ty::GenericArgKind::Lifetime(re) => { - if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() { + if let ty::RegionKind::ReBound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) = + re.kind() + { mapping.insert(bv.var, tcx.mk_param_from_def(param)) } else { return None; } } ty::GenericArgKind::Type(ty) => { - if let ty::Bound(ty::INNERMOST, bv) = *ty.kind() { + if let ty::Bound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) = *ty.kind() { mapping.insert(bv.var, tcx.mk_param_from_def(param)) } else { return None; } } ty::GenericArgKind::Const(ct) => { - if let ty::ConstKind::Bound(ty::INNERMOST, bv) = ct.kind() { + if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) = + ct.kind() + { mapping.insert(bv.var, tcx.mk_param_from_def(param)) } else { return None; @@ -253,7 +257,7 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { return ty; } - if let ty::Bound(binder, old_bound) = *ty.kind() + if let ty::Bound(ty::BoundVarIndexKind::Bound(binder), old_bound) = *ty.kind() && self.binder == binder { let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { @@ -279,7 +283,7 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { } fn fold_region(&mut self, re: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReBound(binder, old_bound) = re.kind() + if let ty::ReBound(ty::BoundVarIndexKind::Bound(binder), old_bound) = re.kind() && self.binder == binder { let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { @@ -307,7 +311,7 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { return ct; } - if let ty::ConstKind::Bound(binder, old_bound) = ct.kind() + if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(binder), old_bound) = ct.kind() && self.binder == binder { let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 99dc8e6e52217..4b2e20a78b9ce 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -915,7 +915,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 't ty::Param(param) => { self.params.insert(param.index); } - ty::Bound(db, bt) if *db >= self.depth => { + ty::Bound(ty::BoundVarIndexKind::Bound(db), bt) if *db >= self.depth => { self.vars.insert(match bt.kind { ty::BoundTyKind::Param(def_id) => def_id, ty::BoundTyKind::Anon => { @@ -938,7 +938,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 't ty::ReEarlyParam(param) => { self.params.insert(param.index); } - ty::ReBound(db, br) if db >= self.depth => { + ty::ReBound(ty::BoundVarIndexKind::Bound(db), br) if db >= self.depth => { self.vars.insert(match br.kind { ty::BoundRegionKind::Named(def_id) => def_id, ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => { @@ -961,7 +961,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 't ty::ConstKind::Param(param) => { self.params.insert(param.index); } - ty::ConstKind::Bound(db, _) if db >= self.depth => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(db), _) if db >= self.depth => { let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var"); return ControlFlow::Break(guar); } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 3c5e4a91c98c7..e445def4faa73 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -303,8 +303,6 @@ struct Canonicalizer<'cx, 'tcx> { sub_root_lookup_table: SsoHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, - - binder_index: ty::DebruijnIndex, } impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { @@ -312,24 +310,12 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { self.tcx } - fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> - where - T: TypeFoldable>, - { - self.binder_index.shift_in(1); - let t = t.super_fold_with(self); - self.binder_index.shift_out(1); - t - } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match r.kind() { - ty::ReBound(index, ..) => { - if index >= self.binder_index { - bug!("escaping late-bound region during canonicalization"); - } else { - r - } + ty::ReBound(ty::BoundVarIndexKind::Bound(_), ..) => r, + + ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => { + bug!("canonicalized bound var found during canonicalization"); } ty::ReStatic @@ -403,12 +389,10 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t) } - ty::Bound(debruijn, _) => { - if debruijn >= self.binder_index { - bug!("escaping bound type during canonicalization") - } else { - t - } + ty::Bound(ty::BoundVarIndexKind::Bound(_), _) => t, + + ty::Bound(ty::BoundVarIndexKind::Canonical, _) => { + bug!("canonicalized bound var found during canonicalization"); } ty::Closure(..) @@ -479,12 +463,11 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("encountered a fresh const during canonicalization") } - ty::ConstKind::Bound(debruijn, _) => { - if debruijn >= self.binder_index { - bug!("escaping bound const during canonicalization") - } else { - return ct; - } + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(_), _) => { + return ct; + } + ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => { + bug!("canonicalized bound var found during canonicalization"); } ty::ConstKind::Placeholder(placeholder) => { return self @@ -569,7 +552,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { query_state, indices: FxHashMap::default(), sub_root_lookup_table: Default::default(), - binder_index: ty::INNERMOST, }; if canonicalizer.query_state.var_values.spilled() { canonicalizer.indices = canonicalizer @@ -751,8 +733,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { let var = self.canonical_var(var_kind, r.into()); - let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }; - ty::Region::new_bound(self.cx(), self.binder_index, br) + ty::Region::new_canonical_bound(self.cx(), var) } /// Given a type variable `ty_var` of the given kind, first check @@ -766,8 +747,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { ) -> Ty<'tcx> { debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); let var = self.canonical_var(var_kind, ty_var.into()); - let bt = ty::BoundTy { var, kind: ty::BoundTyKind::Anon }; - Ty::new_bound(self.tcx, self.binder_index, bt) + Ty::new_canonical_bound(self.tcx, var) } /// Given a type variable `const_var` of the given kind, first check @@ -783,7 +763,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var)) ); let var = self.canonical_var(var_kind, ct_var.into()); - let bc = ty::BoundConst { var }; - ty::Const::new_bound(self.tcx, self.binder_index, bc) + ty::Const::new_canonical_bound(self.tcx, var) } } diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index cc052fbd85c12..c215a9db2a0af 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{ self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_type_ir::TypeVisitable; +use rustc_type_ir::{TypeFlags, TypeVisitable}; use crate::infer::canonical::{Canonical, CanonicalVarValues}; @@ -66,7 +66,6 @@ where value.fold_with(&mut CanonicalInstantiator { tcx, - current_index: ty::INNERMOST, var_values: var_values.var_values, cache: Default::default(), }) @@ -79,12 +78,9 @@ struct CanonicalInstantiator<'tcx> { // The values that the bound vars are are being instantiated with. var_values: ty::GenericArgsRef<'tcx>, - /// As with `BoundVarReplacer`, represents the index of a binder *just outside* - /// the ones we have visited. - current_index: ty::DebruijnIndex, - - // Instantiation is a pure function of `DebruijnIndex` and `Ty`. - cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, + // Because we use `ty::BoundVarIndexKind::Canonical`, we can cache + // based only on the entire ty, not worrying about a `DebruijnIndex` + cache: DelayedMap, Ty<'tcx>>, } impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { @@ -92,29 +88,19 @@ impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { self.tcx } - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) => { self.var_values[bound_ty.var.as_usize()].expect_ty() } _ => { - if !t.has_vars_bound_at_or_above(self.current_index) { + if !t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { t - } else if let Some(&t) = self.cache.get(&(self.current_index, t)) { + } else if let Some(&t) = self.cache.get(&t) { t } else { let res = t.super_fold_with(self); - assert!(self.cache.insert((self.current_index, t), res)); + assert!(self.cache.insert(t, res)); res } } @@ -123,7 +109,7 @@ impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match r.kind() { - ty::ReBound(debruijn, br) if debruijn == self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Canonical, br) => { self.var_values[br.var.as_usize()].expect_region() } _ => r, @@ -132,7 +118,7 @@ impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { - ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) => { self.var_values[bound_const.var.as_usize()].expect_const() } _ => ct.super_fold_with(self), @@ -140,22 +126,14 @@ impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { } fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { p.super_fold_with(self) } else { p } } fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { - if !c.has_vars_bound_at_or_above(self.current_index) { + if !c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { return c; } - // Since instantiation is a function of `DebruijnIndex`, we don't want - // to have to cache more copies of clauses when we're inside of binders. - // Since we currently expect to only have clauses in the outermost - // debruijn index, we just fold if we're inside of a binder. - if self.current_index > ty::INNERMOST { - return c.super_fold_with(self); - } - // Our cache key is `(clauses, var_values)`, but we also don't care about // var values that aren't named in the clauses, since they can change without // affecting the output. Since `ParamEnv`s are cached first, we compute the @@ -185,45 +163,29 @@ impl<'tcx> TypeFolder> for CanonicalInstantiator<'tcx> { fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize { struct HighestVarInClauses { max_var: usize, - current_index: ty::DebruijnIndex, } impl<'tcx> TypeVisitor> for HighestVarInClauses { - fn visit_binder>>( - &mut self, - t: &ty::Binder<'tcx, T>, - ) -> Self::Result { - self.current_index.shift_in(1); - let t = t.super_visit_with(self); - self.current_index.shift_out(1); - t - } fn visit_ty(&mut self, t: Ty<'tcx>) { - if let ty::Bound(debruijn, bound_ty) = *t.kind() - && debruijn == self.current_index - { + if let ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) = *t.kind() { self.max_var = self.max_var.max(bound_ty.var.as_usize()); - } else if t.has_vars_bound_at_or_above(self.current_index) { + } else if t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { t.super_visit_with(self); } } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReBound(debruijn, bound_region) = r.kind() - && debruijn == self.current_index - { + if let ty::ReBound(ty::BoundVarIndexKind::Canonical, bound_region) = r.kind() { self.max_var = self.max_var.max(bound_region.var.as_usize()); } } fn visit_const(&mut self, ct: ty::Const<'tcx>) { - if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind() - && debruijn == self.current_index - { + if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) = ct.kind() { self.max_var = self.max_var.max(bound_const.var.as_usize()); - } else if ct.has_vars_bound_at_or_above(self.current_index) { + } else if ct.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { ct.super_visit_with(self); } } } - let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST }; + let mut visitor = HighestVarInClauses { max_var: 0 }; c.visit_with(&mut visitor); visitor.max_var } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 5d1b4be9e57bb..3c20bdd9391ae 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -422,28 +422,28 @@ impl<'tcx> InferCtxt<'tcx> { // and only use it for placeholders. We need to handle the // `sub_root` of type inference variables which would make this // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = *result_value.kind() + if let ty::Bound(index_kind, b) = *result_value.kind() && !matches!( query_response.variables[b.var.as_usize()], CanonicalVarKind::Ty { .. } ) { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(debruijn, ty::INNERMOST); + // We only allow a `Canonical` index in generic parameters. + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[b.var] = Some(*original_value); } } GenericArgKind::Lifetime(result_value) => { - if let ty::ReBound(debruijn, b) = result_value.kind() { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(debruijn, ty::INNERMOST); + if let ty::ReBound(index_kind, b) = result_value.kind() { + // We only allow a `Canonical` index in generic parameters. + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[b.var] = Some(*original_value); } } GenericArgKind::Const(result_value) => { - if let ty::ConstKind::Bound(debruijn, b) = result_value.kind() { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(debruijn, ty::INNERMOST); + if let ty::ConstKind::Bound(index_kind, b) = result_value.kind() { + // We only allow a `Canonical` index in generic parameters. + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[b.var] = Some(*original_value); } } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index f06eb58a371c2..6592360cf0a5c 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -44,8 +44,8 @@ pub fn extract_verify_if_eq<'tcx>( let verify_if_eq = verify_if_eq_b.skip_binder(); m.relate(verify_if_eq.ty, test_ty).ok()?; - if let ty::RegionKind::ReBound(depth, br) = verify_if_eq.bound.kind() { - assert!(depth == ty::INNERMOST); + if let ty::RegionKind::ReBound(index_kind, br) = verify_if_eq.bound.kind() { + assert!(matches!(index_kind, ty::BoundVarIndexKind::Bound(ty::INNERMOST))); match m.map.get(&br) { Some(&r) => Some(r), None => { @@ -156,7 +156,7 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> pattern: ty::Region<'tcx>, value: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - if let ty::RegionKind::ReBound(depth, br) = pattern.kind() + if let ty::RegionKind::ReBound(ty::BoundVarIndexKind::Bound(depth), br) = pattern.kind() && depth == self.pattern_depth { self.bind(br, value) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 614b6471f188a..95adb561c704d 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -95,7 +95,15 @@ impl<'tcx> Const<'tcx> { debruijn: ty::DebruijnIndex, bound_const: ty::BoundConst, ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Bound(debruijn, bound_const)) + Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)) + } + + #[inline] + pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> { + Const::new( + tcx, + ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst { var }), + ) } #[inline] @@ -180,6 +188,10 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_bound(tcx, debruijn, ty::BoundConst { var }) } + fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self { + Const::new_canonical_bound(tcx, var) + } + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Self { Const::new_placeholder(tcx, placeholder) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..161fec9245a84 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1110,6 +1110,15 @@ const NUM_PREINTERNED_FRESH_TYS: u32 = 20; const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3; const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3; const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3; + +// From general profiling of the *max vars during canonicalization* of a value: +// - about 90% of the time, there are no canonical vars +// - about 9% of the time, there is only one canonical var +// - there are rarely more than 3-5 canonical vars (with exceptions in particularly pathological cases) +// This may not match the number of bound vars found in `for`s. +// Given that this is all heap interned, it seems likely that interning fewer +// vars here won't make an appreciable difference. Though, if we were to inline the data (in an array), +// we may want to consider reducing the number for canonicalized vars down to 4 or so. const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20; // This number may seem high, but it is reached in all but the smallest crates. @@ -1160,9 +1169,14 @@ pub struct CommonTypes<'tcx> { pub fresh_float_tys: Vec>, /// Pre-interned values of the form: - /// `Bound(DebruijnIndex(i), BoundTy { var: v, kind: BoundTyKind::Anon})` + /// `Bound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundTy { var: v, kind: BoundTyKind::Anon})` /// for small values of `i` and `v`. pub anon_bound_tys: Vec>>, + + // Pre-interned values of the form: + // `Bound(BoundVarIndexKind::Canonical, BoundTy { var: v, kind: BoundTyKind::Anon })` + // for small values of `v`. + pub anon_canonical_bound_tys: Vec>, } pub struct CommonLifetimes<'tcx> { @@ -1176,9 +1190,14 @@ pub struct CommonLifetimes<'tcx> { pub re_vars: Vec>, /// Pre-interned values of the form: - /// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BoundRegionKind::Anon })` + /// `ReBound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundRegion { var: v, kind: BoundRegionKind::Anon })` /// for small values of `i` and `v`. pub anon_re_bounds: Vec>>, + + // Pre-interned values of the form: + // `ReBound(BoundVarIndexKind::Canonical, BoundRegion { var: v, kind: BoundRegionKind::Anon })` + // for small values of `v`. + pub anon_re_canonical_bounds: Vec>, } pub struct CommonConsts<'tcx> { @@ -1211,7 +1230,7 @@ impl<'tcx> CommonTypes<'tcx> { (0..NUM_PREINTERNED_ANON_BOUND_TYS_V) .map(|v| { mk(ty::Bound( - ty::DebruijnIndex::from(i), + ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)), ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon }, )) }) @@ -1219,6 +1238,15 @@ impl<'tcx> CommonTypes<'tcx> { }) .collect(); + let anon_canonical_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_V) + .map(|v| { + mk(ty::Bound( + ty::BoundVarIndexKind::Canonical, + ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon }, + )) + }) + .collect(); + CommonTypes { unit: mk(Tuple(List::empty())), bool: mk(Bool), @@ -1250,6 +1278,7 @@ impl<'tcx> CommonTypes<'tcx> { fresh_int_tys, fresh_float_tys, anon_bound_tys, + anon_canonical_bound_tys, } } } @@ -1270,7 +1299,7 @@ impl<'tcx> CommonLifetimes<'tcx> { (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V) .map(|v| { mk(ty::ReBound( - ty::DebruijnIndex::from(i), + ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)), ty::BoundRegion { var: ty::BoundVar::from(v), kind: ty::BoundRegionKind::Anon, @@ -1281,11 +1310,21 @@ impl<'tcx> CommonLifetimes<'tcx> { }) .collect(); + let anon_re_canonical_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V) + .map(|v| { + mk(ty::ReBound( + ty::BoundVarIndexKind::Canonical, + ty::BoundRegion { var: ty::BoundVar::from(v), kind: ty::BoundRegionKind::Anon }, + )) + }) + .collect(); + CommonLifetimes { re_static: mk(ty::ReStatic), re_erased: mk(ty::ReErased), re_vars, anon_re_bounds, + anon_re_canonical_bounds, } } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 7d56ec1635f8d..ee29afcff638d 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -125,7 +125,9 @@ where fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn == self.current_index => + { let ty = self.delegate.replace_ty(bound_ty); debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); ty::shift_vars(self.tcx, ty, self.current_index.as_u32()) @@ -146,9 +148,11 @@ where fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match r.kind() { - ty::ReBound(debruijn, br) if debruijn == self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) + if debruijn == self.current_index => + { let region = self.delegate.replace_region(br); - if let ty::ReBound(debruijn1, br) = region.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn1), br) = region.kind() { // If the callback returns a bound region, // that region should always use the INNERMOST // debruijn index. Then we adjust it to the @@ -165,7 +169,9 @@ where fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { - ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const) + if debruijn == self.current_index => + { let ct = self.delegate.replace_const(bound_const); debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST)); ty::shift_vars(self.tcx, ct, self.current_index.as_u32()) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8f7c8170f7adf..4d1fcaeda5e26 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2664,7 +2664,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { let name = &mut self.name; let region = match r.kind() { - ty::ReBound(db, br) if db >= self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(db), br) if db >= self.current_index => { *self.region_map.entry(br).or_insert_with(|| name(Some(db), self.current_index, br)) } ty::RePlaceholder(ty::PlaceholderRegion { @@ -2687,7 +2687,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { } _ => return r, }; - if let ty::ReBound(debruijn1, br) = region.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn1), br) = region.kind() { assert_eq!(debruijn1, ty::INNERMOST); ty::Region::new_bound(self.tcx, self.current_index, br) } else { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 3a7852dea0680..f0687f2bc726d 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -31,7 +31,7 @@ impl<'tcx> rustc_type_ir::Flags for Region<'tcx> { fn outer_exclusive_binder(&self) -> ty::DebruijnIndex { match self.kind() { - ty::ReBound(debruijn, _) => debruijn.shifted_in(1), + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) => debruijn.shifted_in(1), _ => ty::INNERMOST, } } @@ -59,7 +59,20 @@ impl<'tcx> Region<'tcx> { { re } else { - tcx.intern_region(ty::ReBound(debruijn, bound_region)) + tcx.intern_region(ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), bound_region)) + } + } + + #[inline] + pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Region<'tcx> { + // Use a pre-interned one when possible. + if let Some(re) = tcx.lifetimes.anon_re_canonical_bounds.get(var.as_usize()).copied() { + re + } else { + tcx.intern_region(ty::ReBound( + ty::BoundVarIndexKind::Canonical, + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + )) } } @@ -122,7 +135,12 @@ impl<'tcx> Region<'tcx> { pub fn new_from_kind(tcx: TyCtxt<'tcx>, kind: RegionKind<'tcx>) -> Region<'tcx> { match kind { ty::ReEarlyParam(region) => Region::new_early_param(tcx, region), - ty::ReBound(debruijn, region) => Region::new_bound(tcx, debruijn, region), + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), region) => { + Region::new_bound(tcx, debruijn, region) + } + ty::ReBound(ty::BoundVarIndexKind::Canonical, region) => { + Region::new_canonical_bound(tcx, region.var) + } ty::ReLateParam(ty::LateParamRegion { scope, kind }) => { Region::new_late_param(tcx, scope, kind) } @@ -148,6 +166,10 @@ impl<'tcx> rustc_type_ir::inherent::Region> for Region<'tcx> { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }) } + fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self { + Region::new_canonical_bound(tcx, var) + } + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Self { Region::new_placeholder(tcx, placeholder) } @@ -223,7 +245,7 @@ impl<'tcx> Region<'tcx> { #[inline] pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool { match self.kind() { - ty::ReBound(debruijn, _) => debruijn >= index, + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) => debruijn >= index, _ => false, } } @@ -254,7 +276,11 @@ impl<'tcx> Region<'tcx> { ty::ReStatic => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } - ty::ReBound(..) => { + ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => { + flags = flags | TypeFlags::HAS_RE_BOUND; + flags = flags | TypeFlags::HAS_CANONICAL_BOUND; + } + ty::ReBound(ty::BoundVarIndexKind::Bound(..), _) => { flags = flags | TypeFlags::HAS_RE_BOUND; } ty::ReErased => { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index de35e5e847c81..a3fdd4e35b6aa 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -487,7 +487,23 @@ impl<'tcx> Ty<'tcx> { { ty } else { - Ty::new(tcx, Bound(index, bound_ty)) + Ty::new(tcx, Bound(ty::BoundVarIndexKind::Bound(index), bound_ty)) + } + } + + #[inline] + pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: BoundVar) -> Ty<'tcx> { + // Use a pre-interned one when possible. + if let Some(ty) = tcx.types.anon_canonical_bound_tys.get(var.as_usize()).copied() { + ty + } else { + Ty::new( + tcx, + Bound( + ty::BoundVarIndexKind::Canonical, + ty::BoundTy { var, kind: ty::BoundTyKind::Anon }, + ), + ) } } @@ -952,6 +968,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_bound(tcx, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon }) } + fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Self { + Ty::new_canonical_bound(tcx, var) + } + fn new_alias( interner: TyCtxt<'tcx>, kind: ty::AliasTyKind, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 8dd80aab946d4..370ef364bf5cf 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -798,8 +798,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { match arg.kind() { GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(*debruijn, ty::INNERMOST); + // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters. + assert_eq!(*debruijn, ty::BoundVarIndexKind::Canonical); cvar == b.var } _ => false, @@ -807,8 +807,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { GenericArgKind::Lifetime(r) => match r.kind() { ty::ReBound(debruijn, b) => { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(debruijn, ty::INNERMOST); + // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters. + assert_eq!(debruijn, ty::BoundVarIndexKind::Canonical); cvar == b.var } _ => false, @@ -816,8 +816,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { GenericArgKind::Const(ct) => match ct.kind() { ty::ConstKind::Bound(debruijn, b) => { - // We only allow a `ty::INNERMOST` index in generic parameters. - assert_eq!(debruijn, ty::INNERMOST); + // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters. + assert_eq!(debruijn, ty::BoundVarIndexKind::Canonical); cvar == b.var } _ => false, diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index f0c47f257cc4e..e84ac56b31dfd 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -78,7 +78,9 @@ impl<'tcx> TyCtxt<'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { match r.kind() { - ty::ReBound(debruijn, _) if debruijn < self.outer_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) + if debruijn < self.outer_index => + { ControlFlow::Continue(()) } _ => { @@ -205,7 +207,7 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReBound(debruijn, br) = r.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) = r.kind() { if debruijn == self.current_index { self.regions.insert(br.kind); } diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index b25671d676b9c..9162284422d0d 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -74,12 +74,10 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner /// we set the `sub_root` of the second variable to the position of the first. /// Otherwise the `sub_root` of each type variable is just its own position. sub_root_lookup_table: HashMap, - binder_index: ty::DebruijnIndex, - /// We only use the debruijn index during lookup. We don't need to - /// track the `variables` as each generic arg only results in a single - /// bound variable regardless of how many times it is encountered. - cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>, + /// We can simply cache based on the ty itself, because we use + /// `ty::BoundVarIndexKind::Canonical`. + cache: HashMap, } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { @@ -97,7 +95,6 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), - binder_index: ty::INNERMOST, cache: Default::default(), }; @@ -141,12 +138,10 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), - binder_index: ty::INNERMOST, cache: Default::default(), }; let param_env = param_env.fold_with(&mut env_canonicalizer); - debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); CanonicalParamEnvCacheEntry { param_env, @@ -175,12 +170,10 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), - binder_index: ty::INNERMOST, cache: Default::default(), }; let param_env = param_env.fold_with(&mut env_canonicalizer); - debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds) } @@ -212,7 +205,6 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variable_lookup_table, sub_root_lookup_table: Default::default(), var_kinds, - binder_index: ty::INNERMOST, // We do not reuse the cache as it may contain entries whose canonicalized // value contains `'static`. While we could alternatively handle this by @@ -409,7 +401,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let var = self.get_or_insert_bound_var(t, kind); - Ty::new_anon_bound(self.cx(), self.binder_index, var) + Ty::new_canonical_bound(self.cx(), var) } } @@ -418,16 +410,6 @@ impl, I: Interner> TypeFolder for Canonicaliz self.delegate.cx() } - fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder - where - T: TypeFoldable, - { - self.binder_index.shift_in(1); - let t = t.super_fold_with(self); - self.binder_index.shift_out(1); - t - } - fn fold_region(&mut self, r: I::Region) -> I::Region { let kind = match r.kind() { ty::ReBound(..) => return r, @@ -491,15 +473,15 @@ impl, I: Interner> TypeFolder for Canonicaliz let var = self.get_or_insert_bound_var(r, kind); - Region::new_anon_bound(self.cx(), self.binder_index, var) + Region::new_canonical_bound(self.cx(), var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let Some(&ty) = self.cache.get(&t) { ty } else { let res = self.inner_fold_ty(t); - let old = self.cache.insert((self.binder_index, t), res); + let old = self.cache.insert(t, res); assert_eq!(old, None); res } @@ -552,7 +534,7 @@ impl, I: Interner> TypeFolder for Canonicaliz let var = self.get_or_insert_bound_var(c, kind); - Const::new_anon_bound(self.cx(), self.binder_index, var) + Const::new_canonical_bound(self.cx(), var) } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index e3520e238ed37..4c1569e478f88 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -167,25 +167,25 @@ where // and only use it for placeholders. We need to handle the // `sub_root` of type inference variables which would make this // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = t.kind() + if let ty::Bound(index_kind, b) = t.kind() && !matches!( response.variables.get(b.var().as_usize()).unwrap(), CanonicalVarKind::Ty { .. } ) { - assert_eq!(debruijn, ty::INNERMOST); + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[b.var()] = Some(*original_value); } } ty::GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = r.kind() { - assert_eq!(debruijn, ty::INNERMOST); + if let ty::ReBound(index_kind, br) = r.kind() { + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[br.var()] = Some(*original_value); } } ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { - assert_eq!(debruijn, ty::INNERMOST); + if let ty::ConstKind::Bound(index_kind, bv) = c.kind() { + assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); opt_values[bv.var()] = Some(*original_value); } } diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index c88fb8defae7b..c8016759f239a 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -90,7 +90,7 @@ where fn fold_region(&mut self, r: I::Region) -> I::Region { match r.kind() { - ty::ReBound(debruijn, _) + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) if debruijn.as_usize() >= self.current_index.as_usize() + self.universe_indices.len() => { @@ -99,7 +99,9 @@ where self.universe_indices ); } - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) + if debruijn >= self.current_index => + { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, br); self.mapped_regions.insert(p, br); @@ -111,7 +113,7 @@ where fn fold_ty(&mut self, t: I::Ty) -> I::Ty { match t.kind() { - ty::Bound(debruijn, _) + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { @@ -120,7 +122,9 @@ where self.universe_indices ); } - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn >= self.current_index => + { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, bound_ty); self.mapped_types.insert(p, bound_ty); @@ -133,7 +137,7 @@ where fn fold_const(&mut self, ct: I::Const) -> I::Const { match ct.kind() { - ty::ConstKind::Bound(debruijn, _) + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { @@ -142,7 +146,9 @@ where self.universe_indices ); } - ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const) + if debruijn >= self.current_index => + { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, bound_const); self.mapped_consts.insert(p, bound_const); diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 7f14f878d3737..59440e5407f40 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -453,7 +453,10 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { TyKind::Alias(alias_kind.stable(tables, cx), alias_ty.stable(tables, cx)) } ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables, cx)), - ty::Bound(debruijn_idx, bound_ty) => { + ty::Bound(ty::BoundVarIndexKind::Canonical, _) => { + unreachable!() + } + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn_idx), bound_ty) => { TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables, cx)) } ty::CoroutineWitness(def_id, args) => TyKind::RigidTy(RigidTy::CoroutineWitness( @@ -907,7 +910,7 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> { index: early_reg.index, name: early_reg.name.to_string(), }), - ty::ReBound(db_index, bound_reg) => RegionKind::ReBound( + ty::ReBound(ty::BoundVarIndexKind::Bound(db_index), bound_reg) => RegionKind::ReBound( db_index.as_u32(), BoundRegion { var: bound_reg.var.as_u32(), diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index ec7a4a81a71aa..621cc0fb3ef16 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -287,7 +287,7 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, // u6region[I[][]E] as vendor extended type let mut s = String::new(); match region.kind() { - RegionKind::ReBound(debruijn, r) => { + RegionKind::ReBound(ty::BoundVarIndexKind::Bound(debruijn), r) => { s.push_str("u6regionI"); // Debruijn index, which identifies the binder, as region disambiguator let num = debruijn.index() as u64; @@ -303,7 +303,8 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, s.push_str("u6region"); compress(dict, DictKey::Region(region), &mut s); } - RegionKind::ReEarlyParam(..) + RegionKind::ReBound(ty::BoundVarIndexKind::Canonical, _) + | RegionKind::ReEarlyParam(..) | RegionKind::ReLateParam(..) | RegionKind::ReStatic | RegionKind::ReError(_) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 9fa7e2f100393..d808ade58e693 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -412,7 +412,10 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // Bound lifetimes use indices starting at 1, // see `BinderLevel` for more details. - ty::ReBound(debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }) => { + ty::ReBound( + ty::BoundVarIndexKind::Bound(debruijn), + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + ) => { let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; let depth = binder.lifetime_depths.start + var.as_u32(); diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 3260dd712b9e4..6ab92531e4eff 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -830,7 +830,7 @@ impl<'tcx> TypeFolder> for EraseEscapingBoundRegions<'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReBound(debruijn, _) = r.kind() + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind() && debruijn < self.binder { r diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a54eb80fedc25..c6eb0caee1a6f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -145,7 +145,9 @@ impl<'tcx> TypeVisitor> for MaxEscapingBoundVarVisitor { #[inline] fn visit_region(&mut self, r: ty::Region<'tcx>) { match r.kind() { - ty::ReBound(debruijn, _) if debruijn > self.outer_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) + if debruijn > self.outer_index => + { self.escaping = self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index e91e5055e9053..bb25a14ef7443 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -235,7 +235,7 @@ impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { // bounds of the RPITIT. Shift these binders back out when // constructing the top-level projection predicate. let shifted_alias_ty = fold_regions(self.tcx, unshifted_alias_ty, |re, depth| { - if let ty::ReBound(index, bv) = re.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(index), bv) = re.kind() { if depth != ty::INNERMOST { return ty::Region::new_error_with_message( self.tcx, diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 6591d3148cb14..94b950357e1e4 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -4,6 +4,7 @@ use std::ops::{ControlFlow, Deref}; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; use crate::data_structures::SsoHashSet; @@ -11,7 +12,7 @@ use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldabl use crate::inherent::*; use crate::lift::Lift; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::{self as ty, Interner}; +use crate::{self as ty, DebruijnIndex, Interner}; /// `Binder` is a binder for higher-ranked lifetimes or types. It is part of the /// compiler's representation for things like `for<'a> Fn(&'a isize)` @@ -299,7 +300,9 @@ impl TypeVisitor for ValidateBoundVars { return ControlFlow::Break(()); } match t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn == self.binder_index => + { let idx = bound_ty.var().as_usize(); if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); @@ -317,7 +320,9 @@ impl TypeVisitor for ValidateBoundVars { return ControlFlow::Break(()); } match c.kind() { - ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.binder_index => { + ty::ConstKind::Bound(debruijn, bound_const) + if debruijn == ty::BoundVarIndexKind::Bound(self.binder_index) => + { let idx = bound_const.var().as_usize(); if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", c, self.bound_vars); @@ -332,7 +337,7 @@ impl TypeVisitor for ValidateBoundVars { fn visit_region(&mut self, r: I::Region) -> Self::Result { match r.kind() { - ty::ReBound(index, br) if index == self.binder_index => { + ty::ReBound(index, br) if index == ty::BoundVarIndexKind::Bound(self.binder_index) => { let idx = br.var().as_usize(); if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars); @@ -913,3 +918,33 @@ impl<'a, I: Interner> ArgFolder<'a, I> { } } } + +/// Okay, we do something fun for `Bound` types/regions/consts: +/// Specifically, we distinguish between *canonically* bound things and +/// `for<>` bound things. And, really, it comes down to caching during +/// canonicalization and instantiation. +/// +/// To understand why we do this, imagine we have a type `(T, for<> fn(T))`. +/// If we just tracked canonically bound types with a `DebruijnIndex` (as we +/// used to), then the canonicalized type would be something like +/// `for<0> (^0.0, for<> fn(^1.0))` and so we can't cache `T -> ^0.0`, +/// we have to also factor in binder level. (Of course, we don't cache that +/// exactly, but rather the entire enclosing type, but the point stands.) +/// +/// Of course, this is okay because we don't ever nest canonicalization, so +/// `BoundVarIndexKind::Canonical` is unambiguous. We, alternatively, could +/// have some sentinel `DebruijinIndex`, but that just seems too scary. +/// +/// This doesn't seem to have a huge perf swing either way, but in the next +/// solver, canonicalization is hot and there are some pathological cases where +/// this is needed (`post-mono-higher-ranked-hang`). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub enum BoundVarIndexKind { + Bound(DebruijnIndex), + Canonical, +} diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index ecf3ae4f8b2c9..7b4b953b2cf65 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -230,13 +230,13 @@ impl CanonicalVarValues { pub fn is_identity(&self) -> bool { self.var_values.iter().enumerate().all(|(bv, arg)| match arg.kind() { ty::GenericArgKind::Lifetime(r) => { - matches!(r.kind(), ty::ReBound(ty::INNERMOST, br) if br.var().as_usize() == bv) + matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if br.var().as_usize() == bv) } ty::GenericArgKind::Type(ty) => { - matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var().as_usize() == bv) + matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if bt.var().as_usize() == bv) } ty::GenericArgKind::Const(ct) => { - matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.var().as_usize() == bv) + matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if bc.var().as_usize() == bv) } }) } @@ -246,21 +246,23 @@ impl CanonicalVarValues { for arg in self.var_values.iter() { match arg.kind() { ty::GenericArgKind::Lifetime(r) => { - if matches!(r.kind(), ty::ReBound(ty::INNERMOST, br) if var == br.var()) { + if matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if var == br.var()) + { var = var + 1; } else { // It's ok if this region var isn't an identity variable } } ty::GenericArgKind::Type(ty) => { - if matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if var == bt.var()) { + if matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if var == bt.var()) + { var = var + 1; } else { return false; } } ty::GenericArgKind::Const(ct) => { - if matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if var == bc.var()) + if matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if var == bc.var()) { var = var + 1; } else { @@ -284,16 +286,13 @@ impl CanonicalVarValues { | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) => { - Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) - .into() + Ty::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into() } CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => { - Region::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) - .into() + Region::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into() } CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => { - Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) - .into() + Const::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into() } } }, diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 6de41b47bde08..273b609600876 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -7,7 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; -use crate::{self as ty, DebruijnIndex, Interner}; +use crate::{self as ty, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] @@ -23,7 +23,7 @@ pub enum ConstKind { Infer(InferConst), /// Bound const variable, used only when preparing a trait query. - Bound(DebruijnIndex, I::BoundConst), + Bound(BoundVarIndexKind, I::BoundConst), /// A placeholder const - universally quantified higher-ranked const. Placeholder(I::PlaceholderConst), diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 24704c5bb5358..03099b3319843 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -133,6 +133,9 @@ bitflags::bitflags! { /// Does this type have any coroutines in it? const HAS_TY_CORO = 1 << 24; + + /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? + const HAS_CANONICAL_BOUND = 1 << 25; } } @@ -254,7 +257,12 @@ impl FlagComputation { self.add_args(args.as_slice()); } - ty::Bound(debruijn, _) => { + ty::Bound(ty::BoundVarIndexKind::Canonical, _) => { + self.add_flags(TypeFlags::HAS_TY_BOUND); + self.add_flags(TypeFlags::HAS_CANONICAL_BOUND); + } + + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) => { self.add_bound_var(debruijn); self.add_flags(TypeFlags::HAS_TY_BOUND); } @@ -434,7 +442,7 @@ impl FlagComputation { fn add_region(&mut self, r: I::Region) { self.add_flags(r.flags()); - if let ty::ReBound(debruijn, _) = r.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind() { self.add_bound_var(debruijn); } } @@ -454,10 +462,14 @@ impl FlagComputation { ty::InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), ty::InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), }, - ty::ConstKind::Bound(debruijn, _) => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) => { self.add_bound_var(debruijn); self.add_flags(TypeFlags::HAS_CT_BOUND); } + ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => { + self.add_flags(TypeFlags::HAS_CT_BOUND); + self.add_flags(TypeFlags::HAS_CANONICAL_BOUND); + } ty::ConstKind::Param(_) => { self.add_flags(TypeFlags::HAS_CT_PARAM); } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index a5eb8699e5fc6..d1a50599e8b9c 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -55,7 +55,7 @@ use tracing::{debug, instrument}; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, Interner, TypeFlags}; +use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -398,7 +398,9 @@ impl TypeFolder for Shifter { fn fold_region(&mut self, r: I::Region) -> I::Region { match r.kind() { - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) + if debruijn >= self.current_index => + { let debruijn = debruijn.shifted_in(self.amount); Region::new_bound(self.cx, debruijn, br) } @@ -408,7 +410,9 @@ impl TypeFolder for Shifter { fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { match ty.kind() { - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + ty::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn >= self.current_index => + { let debruijn = debruijn.shifted_in(self.amount); Ty::new_bound(self.cx, debruijn, bound_ty) } @@ -420,7 +424,9 @@ impl TypeFolder for Shifter { fn fold_const(&mut self, ct: I::Const) -> I::Const { match ct.kind() { - ty::ConstKind::Bound(debruijn, bound_ct) if debruijn >= self.current_index => { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ct) + if debruijn >= self.current_index => + { let debruijn = debruijn.shifted_in(self.amount); Const::new_bound(self.cx, debruijn, bound_ct) } @@ -435,7 +441,7 @@ impl TypeFolder for Shifter { pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Region { match region.kind() { - ty::ReBound(debruijn, br) if amount > 0 => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) if amount > 0 => { Region::new_bound(cx, debruijn.shifted_in(amount), br) } _ => region, @@ -515,7 +521,13 @@ where #[instrument(skip(self), level = "debug", ret)] fn fold_region(&mut self, r: I::Region) -> I::Region { match r.kind() { - ty::ReBound(debruijn, _) if debruijn < self.current_index => { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) + if debruijn < self.current_index => + { + debug!(?self.current_index, "skipped bound region"); + r + } + ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => { debug!(?self.current_index, "skipped bound region"); r } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index b5b552dbaec09..75ba0231d98cb 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -48,6 +48,8 @@ pub trait Ty>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self; + fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy) -> Self; fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self { @@ -230,6 +232,8 @@ pub trait Region>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self; + fn new_static(interner: I) -> Self; fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self; @@ -260,6 +264,8 @@ pub trait Const>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self; + fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self; fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self; diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 61e0b67b1639d..c1e3019612676 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -196,13 +196,20 @@ impl DebruijnIndex { pub fn debug_bound_var( fmt: &mut T, - debruijn: DebruijnIndex, + bound_index: BoundVarIndexKind, var: impl std::fmt::Debug, ) -> Result<(), std::fmt::Error> { - if debruijn == INNERMOST { - write!(fmt, "^{var:?}") - } else { - write!(fmt, "^{}_{:?}", debruijn.index(), var) + match bound_index { + BoundVarIndexKind::Bound(debruijn) => { + if debruijn == INNERMOST { + write!(fmt, "^{var:?}") + } else { + write!(fmt, "^{}_{:?}", debruijn.index(), var) + } + } + BoundVarIndexKind::Canonical => { + write!(fmt, "^c_{:?}", var) + } } } diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 06048af043624..1e8585cf52ce8 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -7,7 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use self::RegionKind::*; -use crate::{DebruijnIndex, Interner}; +use crate::{BoundVarIndexKind, Interner}; rustc_index::newtype_index! { /// A **region** **v**ariable **ID**. @@ -147,7 +147,7 @@ pub enum RegionKind { /// Bound regions inside of types **must not** be erased, as they impact trait /// selection and the `TypeId` of that type. `for<'a> fn(&'a ())` and /// `fn(&'static ())` are different types and have to be treated as such. - ReBound(DebruijnIndex, I::BoundRegion), + ReBound(BoundVarIndexKind, I::BoundRegion), /// Late-bound function parameters are represented using a `ReBound`. When /// inside of a function, we convert these bound variables to placeholder diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index dda59283677cd..bb80e2cf46d45 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -15,7 +15,7 @@ pub use self::closure::*; use crate::inherent::*; #[cfg(feature = "nightly")] use crate::visit::TypeVisitable; -use crate::{self as ty, DebruijnIndex, FloatTy, IntTy, Interner, UintTy}; +use crate::{self as ty, BoundVarIndexKind, FloatTy, IntTy, Interner, UintTy}; mod closure; @@ -229,7 +229,7 @@ pub enum TyKind { /// /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html - Bound(DebruijnIndex, I::BoundTy), + Bound(BoundVarIndexKind, I::BoundTy), /// A placeholder type, used during higher ranked subtyping to instantiate /// bound variables. diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 3a6d1acfa8d93..4d9fd6040d8bf 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -344,7 +344,8 @@ impl TypeVisitor for HasRegionsBoundAt { } fn visit_region(&mut self, r: I::Region) -> Self::Result { - if matches!(r.kind(), ty::ReBound(binder, _) if self.binder == binder) { + if matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Bound(binder), _) if self.binder == binder) + { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -531,7 +532,7 @@ impl TypeFolder for FoldEscapingRegions { } fn fold_region(&mut self, r: ::Region) -> ::Region { - if let ty::ReBound(debruijn, _) = r.kind() { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind() { assert!( debruijn <= self.debruijn, "cannot instantiate binder with escaping bound vars" diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 1b1e77bbea8fb..6e9142b22e0ef 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -14,7 +14,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, RegionKind, TyCtxt}; +use rustc_middle::ty::{self, BoundVarIndexKind, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; @@ -151,7 +151,7 @@ impl PassByRefOrValue { match *ty.skip_binder().kind() { ty::Ref(lt, ty, Mutability::Not) => { match lt.kind() { - RegionKind::ReBound(index, region) + RegionKind::ReBound(BoundVarIndexKind::Bound(index), region) if index.as_u32() == 0 && output_regions.contains(®ion) => { continue; diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index e4bc3b7682942..c03469c2b8851 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; @@ -826,7 +826,7 @@ pub fn for_each_top_level_late_bound_region( impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow> TypeVisitor> for V { type Result = ControlFlow; fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { - if let RegionKind::ReBound(idx, bound) = r.kind() + if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind() && idx.as_u32() == self.index { (self.f)(bound) diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index a18d7e7478fce..a1fe278c65207 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,30 +1,30 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 0: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] | 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 2: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 10: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] | 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 12: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 20: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] | 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 22: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr similarity index 85% rename from tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr rename to tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr index 486e5f9416550..f3938ff606f4d 100644 --- a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr @@ -1,5 +1,5 @@ -error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:43:45: 43:67}>` - --> $DIR/post-mono-higher-ranked-hang.rs:43:21 +error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:47:45: 47:67}>` + --> $DIR/post-mono-higher-ranked-hang.rs:47:21 | LL | / self.perm_pairs(l, &mut async move |left_pair| { LL | | @@ -8,7 +8,7 @@ LL | | }) | |______________________^ | note: `ToChain::<'env, 'db>::perm_pairs` defined here - --> $DIR/post-mono-higher-ranked-hang.rs:34:5 + --> $DIR/post-mono-higher-ranked-hang.rs:38:5 | LL | / fn perm_pairs<'l>( LL | | &'l self, diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr new file mode 100644 index 0000000000000..f3938ff606f4d --- /dev/null +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr @@ -0,0 +1,21 @@ +error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:47:45: 47:67}>` + --> $DIR/post-mono-higher-ranked-hang.rs:47:21 + | +LL | / self.perm_pairs(l, &mut async move |left_pair| { +LL | | +LL | | self.perm_pairs(r, yield_chain).await +LL | | }) + | |______________________^ + | +note: `ToChain::<'env, 'db>::perm_pairs` defined here + --> $DIR/post-mono-higher-ranked-hang.rs:38:5 + | +LL | / fn perm_pairs<'l>( +LL | | &'l self, +LL | | perm: &'l SymPerm<'db>, +LL | | yield_chain: &'l mut impl AsyncFnMut(&SymPerm<'db>), +LL | | ) -> Pin + 'l>> { + | |____________________________________________________________^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs index f6ebf787f81b9..55d7cc30ec964 100644 --- a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs @@ -2,6 +2,10 @@ //@ aux-build:block-on.rs //@ edition:2021 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // Regression test for . extern crate block_on; diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr index a26c617dc931d..f8f64dcc545bc 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr @@ -37,7 +37,7 @@ error: higher-ranked lifetime error LL | v.t(|| {}); | ^^^^^ | - = note: could not prove `for<'a> &'a V: 'b` + = note: could not prove `for<'a> &'a V: '_` error: aborting due to 3 previous errors diff --git a/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr b/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr index 9e0d7e4b7be09..a04c6d770d73e 100644 --- a/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr +++ b/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr @@ -4,7 +4,7 @@ error: higher-ranked lifetime error LL | || {}; | ^^^^^ | - = note: could not prove `for<'a> &'a (): 'b` + = note: could not prove `for<'a> &'a (): '_` error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/re-empty-in-error.stderr b/tests/ui/lifetimes/re-empty-in-error.stderr index 554bcb5451fc0..b3b6d3d269c9b 100644 --- a/tests/ui/lifetimes/re-empty-in-error.stderr +++ b/tests/ui/lifetimes/re-empty-in-error.stderr @@ -4,7 +4,7 @@ error: higher-ranked lifetime error LL | foo(&10); | ^^^^^^^^ | - = note: could not prove `for<'b> &'b (): 'a` + = note: could not prove `for<'b> &'b (): '_` error: aborting due to 1 previous error diff --git a/tests/ui/nll/user-annotations/dump-fn-method.rs b/tests/ui/nll/user-annotations/dump-fn-method.rs index 26714b6ffe3a6..ec349e36839ad 100644 --- a/tests/ui/nll/user-annotations/dump-fn-method.rs +++ b/tests/ui/nll/user-annotations/dump-fn-method.rs @@ -31,7 +31,7 @@ fn main() { // Here: we only want the `T` to be given, the rest should be variables. // // (`T` refers to the declaration of `Bazoom`) - let x = <_ as Bazoom>::method::<_>; //~ ERROR [^0, u32, ^1] + let x = <_ as Bazoom>::method::<_>; //~ ERROR [^c_0, u32, ^c_1] x(&22, 44, 66); // Here: all are given and definitely contain no lifetimes, so we @@ -48,7 +48,7 @@ fn main() { // // (`U` refers to the declaration of `Bazoom`) let y = 22_u32; - y.method::(44, 66); //~ ERROR [^0, ^1, u32] + y.method::(44, 66); //~ ERROR [^c_0, ^c_1, u32] // Here: nothing is given, so we don't have any annotation. let y = 22_u32; diff --git a/tests/ui/nll/user-annotations/dump-fn-method.stderr b/tests/ui/nll/user-annotations/dump-fn-method.stderr index 8e847b464e181..f00fb0013dfd8 100644 --- a/tests/ui/nll/user-annotations/dump-fn-method.stderr +++ b/tests/ui/nll/user-annotations/dump-fn-method.stderr @@ -4,7 +4,7 @@ error: user args: UserArgs { args: [&'static u32], user_self_ty: None } LL | let x = foo::<&'static u32>; | ^^^^^^^^^^^^^^^^^^^ -error: user args: UserArgs { args: [^0, u32, ^1], user_self_ty: None } +error: user args: UserArgs { args: [^c_0, u32, ^c_1], user_self_ty: None } --> $DIR/dump-fn-method.rs:34:13 | LL | let x = <_ as Bazoom>::method::<_>; @@ -16,7 +16,7 @@ error: user args: UserArgs { args: [u8, &'static u16, u32], user_self_ty: None } LL | let x = >::method::; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user args: UserArgs { args: [^0, ^1, u32], user_self_ty: None } +error: user args: UserArgs { args: [^c_0, ^c_1, u32], user_self_ty: None } --> $DIR/dump-fn-method.rs:51:5 | LL | y.method::(44, 66); From 1db4d8ebfdf14896c43fc714507e37b97db5d289 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 28 Sep 2025 23:04:55 +0000 Subject: [PATCH 1097/1900] Split Bound into Canonical and Bound --- clippy_lints/src/pass_by_ref_or_value.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 1b1e77bbea8fb..6e9142b22e0ef 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -14,7 +14,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, RegionKind, TyCtxt}; +use rustc_middle::ty::{self, BoundVarIndexKind, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; @@ -151,7 +151,7 @@ impl PassByRefOrValue { match *ty.skip_binder().kind() { ty::Ref(lt, ty, Mutability::Not) => { match lt.kind() { - RegionKind::ReBound(index, region) + RegionKind::ReBound(BoundVarIndexKind::Bound(index), region) if index.as_u32() == 0 && output_regions.contains(®ion) => { continue; diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index e4bc3b7682942..c03469c2b8851 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; @@ -826,7 +826,7 @@ pub fn for_each_top_level_late_bound_region( impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow> TypeVisitor> for V { type Result = ControlFlow; fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { - if let RegionKind::ReBound(idx, bound) = r.kind() + if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind() && idx.as_u32() == self.index { (self.f)(bound) From 50398e2e7505779323e5371b1905c4ad56a29097 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 30 Sep 2025 17:56:18 +0000 Subject: [PATCH 1098/1900] iter repeat: add tests for new count and last behavior --- library/coretests/tests/iter/sources.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/coretests/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs index 506febaa056a8..5a391cb67751d 100644 --- a/library/coretests/tests/iter/sources.rs +++ b/library/coretests/tests/iter/sources.rs @@ -30,6 +30,17 @@ fn test_repeat_take_collect() { assert_eq!(v, vec![42, 42, 42]); } +#[test] +#[should_panic = "iterator is infinite"] +fn test_repeat_count() { + repeat(42).count(); +} + +#[test] +fn test_repeat_last() { + assert_eq!(repeat(42).last(), Some(42)); +} + #[test] fn test_repeat_with() { #[derive(PartialEq, Debug)] From 61b1f0e37fad6842c24c41b0de2cb7dbf90f658f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 06:51:50 -0400 Subject: [PATCH 1099/1900] `assertions_on_constants`: Don't suggest removing assertions with non-local constants. --- clippy_lints/src/assertions_on_constants.rs | 102 +++++++++++--------- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/msrvs.rs | 3 +- tests/ui/assertions_on_constants.rs | 29 +++++- tests/ui/assertions_on_constants.stderr | 88 +++++++++++------ 5 files changed, 145 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b6684825835a9..a6518216aa820 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,10 +1,13 @@ +use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::{is_inside_always_const_context, msrvs}; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::sym; declare_clippy_lint! { @@ -28,56 +31,59 @@ declare_clippy_lint! { "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`" } -declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +pub struct AssertionsOnConstants { + msrv: Msrv, +} +impl AssertionsOnConstants { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { - return; - }; - let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { - Some(sym::debug_assert_macro) => true, - Some(sym::assert_macro) => false, - _ => return, - }; - let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { - return; - }; - let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { - return; - }; + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, + } + && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) + && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition) + && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) + && (const_src.is_local() || !in_const_context) + && !(is_debug && as_bool_lit(condition) == Some(false)) + { + let (msg, help) = if !const_src.is_local() { + let help = if self.msrv.meets(cx, msrvs::CONST_BLOCKS) { + "consider moving this into a const block: `const { assert!(..) }`" + } else if self.msrv.meets(cx, msrvs::CONST_PANIC) { + "consider moving this to an anonymous constant: `const _: () = { assert!(..); }`" + } else { + return; + }; + ("this assertion has a constant value", help) + } else if assert_val { + ("this assertion is always `true`", "remove the assertion") + } else { + ( + "this assertion is always `false`", + "replace this with `panic!()` or `unreachable!()`", + ) + }; - match condition.kind { - ExprKind::Path(..) | ExprKind::Lit(_) => {}, - _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return, - _ => {}, + span_lint_and_help(cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, msg, None, help); } + } +} - if val { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!( - "`{}!(true)` will be optimized out by the compiler", - cx.tcx.item_name(macro_call.def_id) - ), - None, - "remove it", - ); - } else if !is_debug { - let (assert_arg, panic_arg) = match panic_expn { - PanicExpn::Empty => ("", ""), - _ => (", ..", ".."), - }; - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!("`assert!(false{assert_arg})` should probably be replaced"), - None, - format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), - ); - } +fn as_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Lit(l) = e.kind + && let LitKind::Bool(b) = l.node + { + Some(b) + } else { + None } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9fb..0a955d2383194 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -595,7 +595,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 5ce28a582f4ab..995deba077788 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -31,6 +31,7 @@ msrv_aliases! { 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } + 1,79,0 { CONST_BLOCKS } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } @@ -46,7 +47,7 @@ msrv_aliases! { 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } - 1,57,0 { MAP_WHILE } + 1,57,0 { MAP_WHILE, CONST_PANIC } 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index c2516c5414753..b613892f206a6 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -42,7 +42,6 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); let flag: bool = cfg!(not(feature = "asdf")); @@ -62,9 +61,37 @@ fn main() { const _: () = assert!(N.is_power_of_two()); } +const C: bool = true; + const _: () = { assert!(true); //~^ assertions_on_constants assert!(8 == (7 + 1)); + //~^ assertions_on_constants + + assert!(C); }; + +#[clippy::msrv = "1.57"] +fn _f1() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.56"] +fn _f2() { + assert!(C); +} + +#[clippy::msrv = "1.79"] +fn _f3() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.78"] +fn _f4() { + assert!(C); + //~^ assertions_on_constants +} diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 8b7440ec4832c..9aed1405fa043 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -1,100 +1,132 @@ -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::assertions_on_constants)]` -error: `assert!(false)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:13:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:16:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:19:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:23:5 | LL | assert!(false, "{}", msg.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:27:5 | LL | assert!(B); | ^^^^^^^^^^ | - = help: remove it + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:31:5 | LL | assert!(C); | ^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false, ..)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:34:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `debug_assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:37:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:54:19 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:53:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:57:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:56:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:66:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:67:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: aborting due to 12 previous errors +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:70:5 + | +LL | assert!(8 == (7 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove the assertion + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:78:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:89:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:95:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: aborting due to 16 previous errors From 6edf05b740070429e57ec18e5c027167dbb17ab4 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 30 Sep 2025 00:14:03 +0300 Subject: [PATCH 1100/1900] Add `#[bench]` for librustdoc's syntax highlighter --- src/bootstrap/src/core/builder/mod.rs | 2 +- src/librustdoc/html/highlight/tests.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 006dea4b98d13..fc06db8f80b9d 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1145,7 +1145,7 @@ impl<'a> Builder<'a> { test::RunMakeCargo, ), Kind::Miri => describe!(test::Crate), - Kind::Bench => describe!(test::Crate, test::CrateLibrustc), + Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc), Kind::Doc => describe!( doc::UnstableBook, doc::UnstableBookGen, diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 2603e887bead5..4d1bee9b3a1b3 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,6 +1,7 @@ use expect_test::expect_file; use rustc_data_structures::fx::FxIndexMap; use rustc_span::create_default_session_globals_then; +use test::Bencher; use super::{DecorationInfo, write_code}; @@ -81,3 +82,16 @@ let a = 4;"; expect_file!["fixtures/decorations.html"].assert_eq(&html); }); } + +#[bench] +fn bench_html_highlighting(b: &mut Bencher) { + let src = include_str!("../../../../compiler/rustc_ast/src/visit.rs"); + + create_default_session_globals_then(|| { + b.iter(|| { + let mut out = String::new(); + write_code(&mut out, src, None, None, None); + out + }); + }); +} From cbaec31c10c5eff7342e5273360521911fbf7631 Mon Sep 17 00:00:00 2001 From: Josh Simmons Date: Thu, 26 Jun 2025 21:31:05 +0200 Subject: [PATCH 1101/1900] Add fast-path for accessing the current thread id Accessing the thread id is often used in profiling and debugging, as well as some approaches for sound single-threaded access to data. Currently the only way to access the thread id is by first obtaining a handle to the current thread. While this is not exactly slow, it does require an atomic inc-ref and dec-ref operation, as well as the injection of `Thread`'s drop code into the caller. This publicly exposes the existing fast-path for accessing the current thread id. --- library/std/src/thread/current.rs | 24 ++++++++++++++++++++++-- library/std/src/thread/mod.rs | 4 +++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 7da1621da45ce..f00212bfcb617 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -133,12 +133,32 @@ pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { Ok(()) } -/// Gets the id of the thread that invokes it. +/// Gets the unique identifier of the thread which invokes it. +/// +/// Calling this function may be more efficient than accessing the current +/// thread id through the current thread handle. i.e. `thread::current().id()`. /// /// This function will always succeed, will always return the same value for /// one thread and is guaranteed not to call the global allocator. +/// +/// # Examples +/// +/// ``` +/// #![feature(current_thread_id)] +/// +/// use std::thread; +/// +/// let other_thread = thread::spawn(|| { +/// thread::current_id() +/// }); +/// +/// let other_thread_id = other_thread.join().unwrap(); +/// assert_ne!(thread::current_id(), other_thread_id); +/// ``` #[inline] -pub(crate) fn current_id() -> ThreadId { +#[must_use] +#[unstable(feature = "current_thread_id", issue = "147194")] +pub fn current_id() -> ThreadId { // If accessing the persistent thread ID takes multiple TLS accesses, try // to retrieve it from the current thread handle, which will only take one // TLS access. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4d09b2b4e9d2e..1768369792ae2 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -183,7 +183,9 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, current_os_id, drop_current}; +#[unstable(feature = "current_thread_id", issue = "147194")] +pub use current::current_id; +pub(crate) use current::{current_or_unnamed, current_os_id, drop_current}; use current::{set_current, try_with_current}; mod spawnhook; From 2d03ab1486c11f2c5f8a19ae040b94edc090b279 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 30 Sep 2025 17:12:31 +0300 Subject: [PATCH 1102/1900] Replace `rustc_span::Span` with a stripped down version for librustdoc's highlighter --- src/librustdoc/html/highlight.rs | 3 +- src/librustdoc/html/render/context.rs | 3 +- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/span_map.rs | 68 ++++++++++++++++++++------ src/librustdoc/html/sources.rs | 7 ++- 5 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index fad15573cde0c..1dcb4dcc3ff83 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -12,15 +12,16 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; +use rustc_span::BytePos; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, DUMMY_SP, Span}; use super::format; use crate::clean::PrimitiveType; use crate::display::Joined as _; use crate::html::escape::EscapeBodyText; use crate::html::macro_expansion::ExpandedCode; +use crate::html::render::span_map::{DUMMY_SP, Span}; use crate::html::render::{Context, LinkFromSrc}; /// This type is needed in case we want to render links on items to allow to go to their definition. diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5f92ab2fada9c..4c06d0da47013 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -30,6 +30,7 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::macro_expansion::ExpandedCode; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; +use crate::html::render::span_map::Span; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; @@ -139,7 +140,7 @@ pub(crate) struct SharedContext<'tcx> { /// Correspondence map used to link types used in the source code pages to allow to click on /// links to jump to the type's definition. - pub(crate) span_correspondence_map: FxHashMap, + pub(crate) span_correspondence_map: FxHashMap, pub(crate) expanded_codes: FxHashMap>, /// The [`Cache`] used during rendering. pub(crate) cache: Cache, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 97dcaf57cdfa6..d6371e4dbab3e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -36,7 +36,7 @@ mod ordered_json; mod print_item; pub(crate) mod sidebar; mod sorted_template; -mod span_map; +pub(crate) mod span_map; mod type_layout; mod write_shared; diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index ef7ce33298d91..bc9417b1bb1de 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -8,11 +8,48 @@ use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; -use rustc_span::{BytePos, ExpnKind, Span}; +use rustc_span::{BytePos, ExpnKind}; use crate::clean::{self, PrimitiveType, rustc_span}; use crate::html::sources; +/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end byte positions of the span. +/// +/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, and since we +/// never actually use the context and parent that are stored in a normal `Span`, we can replace its usages with this +/// one, which is much cheaper to construct. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct Span { + lo: BytePos, + hi: BytePos, +} + +impl From for Span { + fn from(value: rustc_span::Span) -> Self { + Self { lo: value.lo(), hi: value.hi() } + } +} + +impl Span { + pub(crate) fn lo(self) -> BytePos { + self.lo + } + + pub(crate) fn hi(self) -> BytePos { + self.hi + } + + pub(crate) fn with_lo(self, lo: BytePos) -> Self { + Self { lo, hi: self.hi() } + } + + pub(crate) fn with_hi(self, hi: BytePos) -> Self { + Self { lo: self.lo(), hi } + } +} + +pub(crate) const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0) }; + /// This enum allows us to store two different kinds of information: /// /// In case the `span` definition comes from the same crate, we can simply get the `span` and use @@ -96,7 +133,7 @@ impl SpanMapVisitor<'_> { }) .unwrap_or(path.span) }; - self.matches.insert(span, link); + self.matches.insert(span.into(), link); } Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => { let path_span = if only_use_last_segment @@ -106,11 +143,12 @@ impl SpanMapVisitor<'_> { } else { path.span }; - self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span))); + self.matches.insert(path_span.into(), LinkFromSrc::Local(clean::Span::new(span))); } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. - self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p))); + self.matches + .insert(path.span.into(), LinkFromSrc::Primitive(PrimitiveType::from(p))); } Res::Err => {} _ => {} @@ -127,7 +165,7 @@ impl SpanMapVisitor<'_> { if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE { return; } - self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id())); + self.matches.insert(span.into(), LinkFromSrc::Doc(item.owner_id.to_def_id())); } } @@ -138,7 +176,7 @@ impl SpanMapVisitor<'_> { /// so, we loop until we find the macro definition by using `outer_expn_data` in a loop. /// Finally, we get the information about the macro itself (`span` if "local", `DefId` /// otherwise) and store it inside the span map. - fn handle_macro(&mut self, span: Span) -> bool { + fn handle_macro(&mut self, span: rustc_span::Span) -> bool { if !span.from_expansion() { return false; } @@ -176,7 +214,7 @@ impl SpanMapVisitor<'_> { // The "call_site" includes the whole macro with its "arguments". We only want // the macro name. let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); - self.matches.insert(new_span, link_from_src); + self.matches.insert(new_span.into(), link_from_src); true } @@ -233,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_path(self, path); } - fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) { + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: rustc_span::Span) { match *qpath { QPath::TypeRelative(qself, path) => { if matches!(path.res, Res::Err) { @@ -249,7 +287,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { self.handle_path(&path, false); } } else { - self.infer_id(path.hir_id, Some(id), path.ident.span); + self.infer_id(path.hir_id, Some(id), path.ident.span.into()); } rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself)); @@ -267,7 +305,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } } - fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { + fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: rustc_span::Span, id: HirId) { // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another // file, we want to link to it. Otherwise no need to create a link. if !span.overlaps(m.spans.inner_span) { @@ -275,8 +313,10 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { // name only and not all the "mod foo;". if let Node::Item(item) = self.tcx.hir_node(id) { let (ident, _) = item.expect_mod(); - self.matches - .insert(ident.span, LinkFromSrc::Local(clean::Span::new(m.spans.inner_span))); + self.matches.insert( + ident.span.into(), + LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)), + ); } } else { // If it's a "mod foo {}", we want to look to its documentation page. @@ -288,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { match expr.kind { ExprKind::MethodCall(segment, ..) => { - self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span) + self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span.into()) } - ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span), + ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span.into()), _ => { if self.handle_macro(expr.span) { // We don't want to go deeper into the macro. diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 9c5518a780e44..c79f63fbc2032 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -348,7 +348,12 @@ pub(crate) fn print_src( highlight::write_code( fmt, s, - Some(highlight::HrefContext { context, file_span, root_path, current_href }), + Some(highlight::HrefContext { + context, + file_span: file_span.into(), + root_path, + current_href, + }), Some(decoration_info), Some(line_info), ); From 80e598bb12297c5df04e5b380a4f51aa4dfc5d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 30 Sep 2025 13:29:39 +0200 Subject: [PATCH 1103/1900] clone region obligations instead of taking in implied bounds hack --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 4 ++++ .../src/traits/query/type_op/implied_outlives_bounds.rs | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index b989d419057b1..a640dcb1b4e1e 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -170,6 +170,10 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } + pub fn clone_registered_region_obligations(&self) -> Vec> { + self.inner.borrow().region_obligations.clone() + } + pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) { let mut inner = self.inner.borrow_mut(); inner.undo_log.push(UndoLog::PushRegionAssumption); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 7540cbe3fd1a4..e55ffb4d5fdb9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -55,6 +55,12 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( span: Span, disable_implied_bounds_hack: bool, ) -> Result>, NoSolution> { + // Inside mir borrowck, each computation starts with an empty list. + assert!( + ocx.infcx.inner.borrow().region_obligations().is_empty(), + "compute_implied_outlives_bounds assumes region obligations are empty before starting" + ); + let normalize_ty = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait`, @@ -143,7 +149,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break() { for TypeOutlivesConstraint { sup_type, sub_region, .. } in - ocx.infcx.take_registered_region_obligations() + ocx.infcx.clone_registered_region_obligations() { let mut components = smallvec![]; push_outlives_components(ocx.infcx.tcx, sup_type, &mut components); From bdebd479acb692319ace3f88bf874ea365503199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 30 Sep 2025 14:01:05 +0200 Subject: [PATCH 1104/1900] remove outdated context (inner) infctx --- compiler/rustc_infer/src/infer/mod.rs | 17 ----------------- compiler/rustc_trait_selection/src/regions.rs | 2 ++ 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c9fc124d3bf8d..f3ebfde06ab6b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -131,23 +131,6 @@ pub struct InferCtxtInner<'tcx> { /// `$0: 'static`. This will get checked later by regionck. (We /// can't generally check these things right away because we have /// to wait until types are resolved.) - /// - /// These are stored in a map keyed to the id of the innermost - /// enclosing fn body / static initializer expression. This is - /// because the location where the obligation was incurred can be - /// relevant with respect to which sublifetime assumptions are in - /// place. The reason that we store under the fn-id, and not - /// something more fine-grained, is so that it is easier for - /// regionck to be sure that it has found *all* the region - /// obligations (otherwise, it's easy to fail to walk to a - /// particular node-id). - /// - /// Before running `resolve_regions_and_report_errors`, the creator - /// of the inference context is expected to invoke - /// [`InferCtxt::process_registered_region_obligations`] - /// for each body-id in this map, which will process the - /// obligations within. This is expected to be done 'late enough' - /// that all type inference variables have been bound and so forth. region_obligations: Vec>, /// The outlives bounds that we assume must hold about placeholders that diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 2b33b8ac9f86c..debc4fda15a56 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -77,6 +77,8 @@ impl<'tcx> InferCtxt<'tcx> { /// /// Prefer this method over `resolve_regions_with_normalize`, unless you are /// doing something specific for normalization. + /// + /// This function assumes that all infer variables are already constrained. fn resolve_regions( &self, body_id: LocalDefId, From c1318053e3ab0578420ba93720dc479e3b526f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 1 Oct 2025 00:10:49 +0200 Subject: [PATCH 1105/1900] add test for trait-system-refactor-initiative/239 --- .../coroutine/handle_opaques_before_coroutines.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/ui/coroutine/handle_opaques_before_coroutines.rs diff --git a/tests/ui/coroutine/handle_opaques_before_coroutines.rs b/tests/ui/coroutine/handle_opaques_before_coroutines.rs new file mode 100644 index 0000000000000..2771c77429cac --- /dev/null +++ b/tests/ui/coroutine/handle_opaques_before_coroutines.rs @@ -0,0 +1,15 @@ +// test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/239 +//@edition: 2024 +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +fn foo<'a>() -> impl Send { + if false { + foo(); + } + async {} +} + +fn main() {} From 422f6bb7420e1f1fe1cd920ed0c6b22f5042ae35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 1 Oct 2025 01:10:24 +0200 Subject: [PATCH 1106/1900] enable tests on next-solver for rust-lang/trait-system-refactor-initiative/237 --- tests/ui/type/pattern_types/const_generics.rs | 3 +++ .../{transmute.stderr => transmute.current.stderr} | 4 ++-- tests/ui/type/pattern_types/transmute.rs | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) rename tests/ui/type/pattern_types/{transmute.stderr => transmute.current.stderr} (92%) diff --git a/tests/ui/type/pattern_types/const_generics.rs b/tests/ui/type/pattern_types/const_generics.rs index 79d46c010d733..f5eb90e94d4fd 100644 --- a/tests/ui/type/pattern_types/const_generics.rs +++ b/tests/ui/type/pattern_types/const_generics.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![feature(pattern_types, generic_pattern_types, pattern_type_macro)] #![expect(incomplete_features)] diff --git a/tests/ui/type/pattern_types/transmute.stderr b/tests/ui/type/pattern_types/transmute.current.stderr similarity index 92% rename from tests/ui/type/pattern_types/transmute.stderr rename to tests/ui/type/pattern_types/transmute.current.stderr index 578549b515c10..edec542e5e151 100644 --- a/tests/ui/type/pattern_types/transmute.stderr +++ b/tests/ui/type/pattern_types/transmute.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:20:14 + --> $DIR/transmute.rs:23:14 | LL | unsafe { std::mem::transmute(x) } | ^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { std::mem::transmute(x) } = note: target type: `u32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:28:14 + --> $DIR/transmute.rs:31:14 | LL | unsafe { std::mem::transmute(x) } | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type/pattern_types/transmute.rs b/tests/ui/type/pattern_types/transmute.rs index 43dd62a19e706..4e686245f9375 100644 --- a/tests/ui/type/pattern_types/transmute.rs +++ b/tests/ui/type/pattern_types/transmute.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![feature(pattern_types, pattern_type_macro, generic_pattern_types)] #![expect(incomplete_features)] From ddbaca521edf274b1462fced5b50ee6983fb8d01 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 30 Sep 2025 17:56:12 -0400 Subject: [PATCH 1107/1900] fix void and empty struct ret --- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index c3485f563916f..4a749642265df 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -378,5 +378,12 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); - builder.store_to_place(call, dest.val); + let fn_ret_ty = builder.cx.val_ty(call); + if fn_ret_ty != builder.cx.type_void() && fn_ret_ty != builder.cx.type_struct(&[], false) { + // If we return void or an empty struct, then our caller (due to how we generated it) + // does not expect a return value. As such, we have no pointer (or place) into which + // we could store our value, and would store into an undef, which would cause UB. + // As such, we just ignore the return value in those cases. + builder.store_to_place(call, dest.val); + } } From 28ffbab35382164167a5f101bb0181d7f77bffc2 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 30 Sep 2025 17:56:26 -0400 Subject: [PATCH 1108/1900] add empty struct ret testcase --- tests/codegen-llvm/autodiff/void_ret.rs | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/codegen-llvm/autodiff/void_ret.rs diff --git a/tests/codegen-llvm/autodiff/void_ret.rs b/tests/codegen-llvm/autodiff/void_ret.rs new file mode 100644 index 0000000000000..98c6b98eef4e3 --- /dev/null +++ b/tests/codegen-llvm/autodiff/void_ret.rs @@ -0,0 +1,41 @@ +//@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C no-prepopulate-passes -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme + +#![feature(autodiff)] +use std::autodiff::*; + +// Usually we would store the return value of the differentiated function. +// However, if the return type is void or an empty struct, +// we don't need to store anything. Verify this, since it caused a bug. + +// CHECK:; void_ret::main +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal +// CHECK-NOT: store {} undef, ptr undef +// CHECK: ret void + +#[autodiff_reverse(bar, Duplicated, Duplicated)] +pub fn foo(r: &[f64; 10], res: &mut f64) { + let mut output = [0.0; 10]; + output[0] = r[0]; + output[1] = r[1] * r[2]; + output[2] = r[4] * r[5]; + output[3] = r[2] * r[6]; + output[4] = r[1] * r[7]; + output[5] = r[2] * r[8]; + output[6] = r[1] * r[9]; + output[7] = r[5] * r[6]; + output[8] = r[5] * r[7]; + output[9] = r[4] * r[8]; + *res = output.iter().sum(); +} +fn main() { + let inputs = Box::new([3.1; 10]); + let mut d_inputs = Box::new([0.0; 10]); + let mut res = Box::new(0.0); + let mut d_res = Box::new(1.0); + + bar(&inputs, &mut d_inputs, &mut res, &mut d_res); + dbg!(&d_inputs); +} From de189fa982ad8830c326fac443ab830728567aff Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 28 Sep 2025 19:45:12 -0400 Subject: [PATCH 1109/1900] updating tests to not break from new typetree metadata --- tests/codegen-llvm/autodiff/abi_handling.rs | 4 ++-- tests/codegen-llvm/autodiff/batched.rs | 2 +- tests/codegen-llvm/autodiff/scalar.rs | 2 +- tests/codegen-llvm/autodiff/sret.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs index 454ec698b917c..5c8126898a8d7 100644 --- a/tests/codegen-llvm/autodiff/abi_handling.rs +++ b/tests/codegen-llvm/autodiff/abi_handling.rs @@ -1,7 +1,7 @@ //@ revisions: debug release -//@[debug] compile-flags: -Zautodiff=Enable -C opt-level=0 -Clto=fat -//@[release] compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@[debug] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=0 -Clto=fat +//@[release] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs index 306a6ed9d1f4f..dc82403212fb0 100644 --- a/tests/codegen-llvm/autodiff/batched.rs +++ b/tests/codegen-llvm/autodiff/batched.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme // diff --git a/tests/codegen-llvm/autodiff/scalar.rs b/tests/codegen-llvm/autodiff/scalar.rs index 55b989f920da3..53672a89230ab 100644 --- a/tests/codegen-llvm/autodiff/scalar.rs +++ b/tests/codegen-llvm/autodiff/scalar.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme #![feature(autodiff)] diff --git a/tests/codegen-llvm/autodiff/sret.rs b/tests/codegen-llvm/autodiff/sret.rs index dbc253ce89434..498cd3fea012d 100644 --- a/tests/codegen-llvm/autodiff/sret.rs +++ b/tests/codegen-llvm/autodiff/sret.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme From 3f0ae50c42f019df9182168bc533641e892a9eee Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 1 Oct 2025 12:53:31 +0800 Subject: [PATCH 1110/1900] Fix not applicable match inside if for pull_assignment_up Example --- ```rust fn foo() { let x; if true { match true { true => $0x = 2, false => x = 3, } } } ``` **Before this PR**: Assist not applicable **After this PR**: ```rust fn foo() { let x; if true { x = match true { true => 2, false => 3, }; } } ``` --- .../src/handlers/pull_assignment_up.rs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 00902fafe8278..812ebf6c6e289 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ AstNode, algo::find_node_at_range, @@ -52,14 +53,15 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> assignments: Vec::new(), }; - let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::() { + let node: Either = ctx.find_node_at_offset()?; + let tgt: ast::Expr = if let Either::Left(if_expr) = node { let if_expr = std::iter::successors(Some(if_expr), |it| { it.syntax().parent().and_then(ast::IfExpr::cast) }) .last()?; collector.collect_if(&if_expr)?; if_expr.into() - } else if let Some(match_expr) = ctx.find_node_at_offset::() { + } else if let Either::Right(match_expr) = node { collector.collect_match(&match_expr)?; match_expr.into() } else { @@ -311,6 +313,33 @@ fn foo() { ); } + #[test] + fn test_pull_assignment_up_match_in_if_expr() { + check_assist( + pull_assignment_up, + r#" +fn foo() { + let x; + if true { + match true { + true => $0x = 2, + false => x = 3, + } + } +}"#, + r#" +fn foo() { + let x; + if true { + x = match true { + true => 2, + false => 3, + }; + } +}"#, + ); + } + #[test] fn test_pull_assignment_up_assignment_expressions() { check_assist( From ec893d1a646bc0a1e09511ca80d71141da3b6997 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 1 Oct 2025 13:31:42 +0800 Subject: [PATCH 1111/1900] tests: remove `no-remap-src-base` Previously in the `//`-compiletest-directive times, this was implemented as a special `no-*` directive parsing. In the migration from `//` -> `//@`, the `// no-remap-src-base` directive was lost, most likely because it had no effect -- the default is not remapping `src-base`. So remove occurrences of `no-remap-src-base`, as these are not valid directives. --- tests/ui-fulldeps/mod_dir_path_canonicalized.rs | 1 - tests/ui/errors/auxiliary/remapped_dep.rs | 2 +- tests/ui/errors/remap-path-prefix-reverse.local-self.stderr | 2 +- tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr | 2 +- tests/ui/errors/remap-path-prefix-reverse.rs | 1 - tests/ui/errors/remap-path-prefix.rs | 2 +- tests/ui/proc-macro/expand-expr.rs | 2 +- tests/ui/proc-macro/pretty-print-hack-show.rs | 1 - 8 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs index df5f29e35fe69..86f2d5f5954a4 100644 --- a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -2,7 +2,6 @@ // Testing that a librustc_ast can parse modules with canonicalized base path //@ ignore-cross-compile //@ ignore-remote -// no-remap-src-base: Reading `file!()` (expectedly) fails when enabled. #![feature(rustc_private)] diff --git a/tests/ui/errors/auxiliary/remapped_dep.rs b/tests/ui/errors/auxiliary/remapped_dep.rs index 36d4699a30600..997118f822c33 100644 --- a/tests/ui/errors/auxiliary/remapped_dep.rs +++ b/tests/ui/errors/auxiliary/remapped_dep.rs @@ -1,4 +1,4 @@ //@ compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux -// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file. +// Manually remap, so the remapped path remains in .stderr file. pub struct SomeStruct {} // This line should be show as part of the error. diff --git a/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr b/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr index b4f83f6bfc0a6..b2651f3e03a6d 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr +++ b/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr @@ -1,5 +1,5 @@ error[E0423]: expected value, found struct `remapped_dep::SomeStruct` - --> $DIR/remap-path-prefix-reverse.rs:16:13 + --> $DIR/remap-path-prefix-reverse.rs:15:13 | LL | let _ = remapped_dep::SomeStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}` diff --git a/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr b/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr index b4f83f6bfc0a6..b2651f3e03a6d 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr +++ b/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr @@ -1,5 +1,5 @@ error[E0423]: expected value, found struct `remapped_dep::SomeStruct` - --> $DIR/remap-path-prefix-reverse.rs:16:13 + --> $DIR/remap-path-prefix-reverse.rs:15:13 | LL | let _ = remapped_dep::SomeStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}` diff --git a/tests/ui/errors/remap-path-prefix-reverse.rs b/tests/ui/errors/remap-path-prefix-reverse.rs index 28fdabb8f4df1..562e44690f768 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.rs +++ b/tests/ui/errors/remap-path-prefix-reverse.rs @@ -2,7 +2,6 @@ //@ compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux //@ revisions: local-self remapped-self -// [local-self] no-remap-src-base: The hack should work regardless of remapping. //@ [remapped-self] remap-src-base // Verify that the expected source code is shown. diff --git a/tests/ui/errors/remap-path-prefix.rs b/tests/ui/errors/remap-path-prefix.rs index 7e38e16280f51..de18aa8cc204b 100644 --- a/tests/ui/errors/remap-path-prefix.rs +++ b/tests/ui/errors/remap-path-prefix.rs @@ -2,7 +2,7 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped //@ [with-diagnostic-scope]compile-flags: -Zremap-path-scope=diagnostics //@ [without-diagnostic-scope]compile-flags: -Zremap-path-scope=object -// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file. +// Manually remap, so the remapped path remains in .stderr file. // The remapped paths are not normalized by compiletest. //@ normalize-stderr: "\\(errors)" -> "/$1" diff --git a/tests/ui/proc-macro/expand-expr.rs b/tests/ui/proc-macro/expand-expr.rs index 1e058abe3bc1b..c3dddd8e45940 100644 --- a/tests/ui/proc-macro/expand-expr.rs +++ b/tests/ui/proc-macro/expand-expr.rs @@ -1,6 +1,6 @@ //@ proc-macro: expand-expr.rs //@ ignore-backends: gcc -// no-remap-src-base: check_expand_expr_file!() fails when enabled. +// No `remap-src-base`, since `check_expand_expr_file!()` fails when enabled. #![feature(concat_bytes)] extern crate expand_expr; diff --git a/tests/ui/proc-macro/pretty-print-hack-show.rs b/tests/ui/proc-macro/pretty-print-hack-show.rs index 70f0d5f6ea975..08e26c8114276 100644 --- a/tests/ui/proc-macro/pretty-print-hack-show.rs +++ b/tests/ui/proc-macro/pretty-print-hack-show.rs @@ -1,7 +1,6 @@ //@ proc-macro: test-macros.rs //@ compile-flags: -Z span-debug //@ revisions: local remapped -// [local] no-remap-src-base: The hack should work regardless of remapping. //@ [remapped] remap-src-base #![no_std] // Don't load unnecessary hygiene information from std From f1079915b46f159e822a3f943ecafed8b7db8c6f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 18:22:08 +0200 Subject: [PATCH 1112/1900] Do not suggest using a `if let` chain if it is not supported This might be due to a low edition (< 2024) or too low a MSRV. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/collapsible_if.rs | 7 ++- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unwrap.rs | 50 +++++++++++++------- clippy_utils/src/lib.rs | 6 +++ tests/ui/checked_unwrap/if_let_chains.rs | 24 ++++++++++ tests/ui/checked_unwrap/if_let_chains.stderr | 29 ++++++++++++ 8 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 tests/ui/checked_unwrap/if_let_chains.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.stderr diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f8263bb8852ab..e7e0670357b3c 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -898,6 +898,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [`unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index e14e15a022f71..df8495dfe0ecf 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -794,6 +794,7 @@ define_Conf! { unchecked_duration_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, + unnecessary_unwrap, unnested_or_patterns, unused_trait_names, use_self, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index b239c20ab4d89..b13e307a3f9c8 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, sym, tokenize_with_text}; +use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; @@ -216,8 +216,7 @@ impl CollapsibleIf { } fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { - !matches!(cond.kind, ExprKind::Let(..)) - || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) + !matches!(cond.kind, ExprKind::Let(..)) || can_use_if_let_chains(cx, self.msrv) } // Check that nothing significant can be found between the initial `{` of `inner_if` and diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b0689..1a706df567e55 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -589,7 +589,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); - store.register_late_pass(|_| Box::new(unwrap::Unwrap)); + store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 34dfe5b6546fa..aee8028a75de7 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,9 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::msrvs::Msrv; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -10,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol}; @@ -72,10 +74,21 @@ declare_clippy_lint! { "checks for calls of `unwrap[_err]()` that will always fail" } +pub(crate) struct Unwrap { + msrv: Msrv, +} + +impl Unwrap { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + /// Visitor that keeps track of which variables are unwrappable. struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, + msrv: Msrv, } /// What kind of unwrappable this is. @@ -133,12 +146,14 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) - } - - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) + fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { + match (get_type_diagnostic_name(cx, ty)?, method_name) { + (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), + (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), + (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), + (sym::Result, sym::is_err) => Some((UnwrappableKind::Result, false)), + _ => None, + } } match expr.kind { @@ -157,15 +172,9 @@ fn collect_unwrap_info<'tcx>( if let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name - && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) => + && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => { - let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; vec![UnwrapInfo { local_id, @@ -357,7 +366,11 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { ); } else { diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); + if can_use_if_let_chains(self.cx, self.msrv) { + diag.help("try using `if let` or `match`"); + } else { + diag.help("try using `match`"); + } } }, ); @@ -383,7 +396,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { } } -declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); +impl_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); impl<'tcx> LateLintPass<'tcx> for Unwrap { fn check_fn( @@ -402,6 +415,7 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { let mut v = UnwrappableVariablesVisitor { unwrappables: Vec::new(), cx, + msrv: self.msrv, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659a..1e0bd3a2c4dc8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -129,6 +129,7 @@ use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; +use crate::msrvs::Msrv; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -3659,3 +3660,8 @@ pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { }) ) } + +/// Checks if the chosen edition and `msrv` allows using `if let` chains. +pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool { + cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS) +} diff --git a/tests/ui/checked_unwrap/if_let_chains.rs b/tests/ui/checked_unwrap/if_let_chains.rs new file mode 100644 index 0000000000000..cfa7715965cdb --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.rs @@ -0,0 +1,24 @@ +//@require-annotations-for-level: ERROR +#![deny(clippy::unnecessary_unwrap)] + +#[clippy::msrv = "1.85"] +fn if_let_chains_unsupported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `match` + } +} + +#[clippy::msrv = "1.88"] +fn if_let_chains_supported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `if let` or `match` + } +} diff --git a/tests/ui/checked_unwrap/if_let_chains.stderr b/tests/ui/checked_unwrap/if_let_chains.stderr new file mode 100644 index 0000000000000..8a4137de37a3c --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.stderr @@ -0,0 +1,29 @@ +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:9:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `match` +note: the lint level is defined here + --> tests/ui/checked_unwrap/if_let_chains.rs:2:9 + | +LL | #![deny(clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:20:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `if let` or `match` + +error: aborting due to 2 previous errors + From 7a8a5763291aca2f6ba9609849ebbaaaaab951b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 1 Oct 2025 09:10:56 +0300 Subject: [PATCH 1113/1900] Build x86_64-apple-darwin binaries on macos-14 --- src/tools/rust-analyzer/.github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 5bd90130f4c21..7ccbdd91de391 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -52,7 +52,7 @@ jobs: target: arm-unknown-linux-gnueabihf zig_target: arm-unknown-linux-gnueabihf.2.28 code-target: linux-armhf - - os: macos-13 + - os: macos-14 target: x86_64-apple-darwin code-target: darwin-x64 pgo: clap-rs/clap@v4.5.36 From 981da22e5e009cec48eb93ce254c15d5cb1a3570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 1 Oct 2025 09:11:51 +0300 Subject: [PATCH 1114/1900] Bump MACOSX_DEPLOYMENT_TARGET to 14.0 --- src/tools/rust-analyzer/.github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 7ccbdd91de391..b6f430f14051e 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -16,7 +16,7 @@ env: RUSTFLAGS: "-D warnings -W unreachable-pub" RUSTUP_MAX_RETRIES: 10 FETCH_DEPTH: 0 # pull in the tags for the version string - MACOSX_DEPLOYMENT_TARGET: 13.0 + MACOSX_DEPLOYMENT_TARGET: 14.0 ZIG_VERSION: 0.13.0 ZIGBUILD_VERSION: 0.19.8 From 3186902bfca12d492ebbac70e2a41b15f0fb2b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 1 Oct 2025 09:05:30 +0200 Subject: [PATCH 1115/1900] Remove mention of `compiletest-use-stage0-libtest` from the bootstrap example config --- bootstrap.example.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 0a39c6d8f247d..f623a3db0029d 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -476,9 +476,6 @@ # when the stage 0 compiler is actually built from in-tree sources. #build.compiletest-allow-stage0 = false -# Whether to use the precompiled stage0 libtest with compiletest. -#build.compiletest-use-stage0-libtest = true - # Default value for the `--extra-checks` flag of tidy. # # See `./x test tidy --help` for details. From 86f2d424c8cda473aa1bf91365f499cfe520c54a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Mon, 15 Sep 2025 11:35:08 +0000 Subject: [PATCH 1116/1900] indexing: reword help Co-authored-by: beef --- .../src/error_codes/E0608.md | 15 ++++++++--- compiler/rustc_hir_typeck/src/expr.rs | 27 +++++-------------- tests/ui/indexing/index_message.stderr | 4 ++- tests/ui/issues/issue-27842.stderr | 12 ++++----- tests/ui/span/suggestion-non-ascii.stderr | 4 ++- ...for-indexing-expression-issue-40861.stderr | 2 +- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0608.md b/compiler/rustc_error_codes/src/error_codes/E0608.md index d0ebc3a26f082..3c29484f575ce 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0608.md +++ b/compiler/rustc_error_codes/src/error_codes/E0608.md @@ -1,5 +1,5 @@ -An attempt to use index on a type which doesn't implement the `std::ops::Index` -trait was performed. +Attempted to index a value whose type doesn't implement the +`std::ops::Index` trait. Erroneous code example: @@ -7,8 +7,8 @@ Erroneous code example: 0u8[2]; // error: cannot index into a value of type `u8` ``` -To be able to index into a type it needs to implement the `std::ops::Index` -trait. Example: +Only values with types that implement the `std::ops::Index` trait +can be indexed with square brackets. Example: ``` let v: Vec = vec![0, 1, 2, 3]; @@ -16,3 +16,10 @@ let v: Vec = vec![0, 1, 2, 3]; // The `Vec` type implements the `Index` trait so you can do: println!("{}", v[2]); ``` + +Tuples and structs are indexed with dot (`.`), not with brackets (`[]`), +and tuple element names are their positions: +```ignore(pseudo code) +// this (pseudo code) expression is true for any tuple: +tuple == (tuple.0, tuple.1, ...) +``` diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d1ce0afddf91c..f9cdc923670f0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3551,35 +3551,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Try to give some advice about indexing tuples. if let ty::Tuple(types) = base_t.kind() { - let mut needs_note = true; - // If the index is an integer, we can show the actual - // fixed expression: + err.help( + "tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.", + ); + // If index is an unsuffixed integer, show the fixed expression: if let ExprKind::Lit(lit) = idx.kind && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node - && i.get() - < types - .len() - .try_into() - .expect("expected tuple index to be < usize length") + && i.get() < types.len().try_into().expect("tuple length fits in u128") { err.span_suggestion( brackets_span, - "to access tuple elements, use", + format!("to access tuple element `{i}`, use"), format!(".{i}"), Applicability::MachineApplicable, ); - needs_note = false; - } else if let ExprKind::Path(..) = idx.peel_borrows().kind { - err.span_label( - idx.span, - "cannot access tuple elements at a variable index", - ); - } - if needs_note { - err.help( - "to access tuple elements, use tuple indexing \ - syntax (e.g., `tuple.0`)", - ); } } diff --git a/tests/ui/indexing/index_message.stderr b/tests/ui/indexing/index_message.stderr index 6affb1ed96252..b6f61379f2af5 100644 --- a/tests/ui/indexing/index_message.stderr +++ b/tests/ui/indexing/index_message.stderr @@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)` --> $DIR/index_message.rs:3:14 | LL | let _ = z[0]; - | ^^^ help: to access tuple elements, use: `.0` + | ^^^ help: to access tuple element `0`, use: `.0` + | + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/issues/issue-27842.stderr index b18fe1512b50b..f388fdf85cde5 100644 --- a/tests/ui/issues/issue-27842.stderr +++ b/tests/ui/issues/issue-27842.stderr @@ -2,17 +2,17 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer --> $DIR/issue-27842.rs:4:16 | LL | let _ = tup[0]; - | ^^^ help: to access tuple elements, use: `.0` + | ^^^ help: to access tuple element `0`, use: `.0` + | + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` --> $DIR/issue-27842.rs:9:16 | LL | let _ = tup[i]; - | ^-^ - | | - | cannot access tuple elements at a variable index + | ^^^ | - = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer},)` --> $DIR/issue-27842.rs:14:16 @@ -20,7 +20,7 @@ error[E0608]: cannot index into a value of type `({integer},)` LL | let _ = tup[3]; | ^^^ | - = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error: aborting due to 3 previous errors diff --git a/tests/ui/span/suggestion-non-ascii.stderr b/tests/ui/span/suggestion-non-ascii.stderr index 6e6e31a5698b8..361f744ee8eb6 100644 --- a/tests/ui/span/suggestion-non-ascii.stderr +++ b/tests/ui/span/suggestion-non-ascii.stderr @@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)` --> $DIR/suggestion-non-ascii.rs:3:24 | LL | println!("☃{}", tup[0]); - | ^^^ help: to access tuple elements, use: `.0` + | ^^^ help: to access tuple element `0`, use: `.0` + | + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error: aborting due to 1 previous error diff --git a/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr index 13bc0cd94f37c..ef5f1786801a1 100644 --- a/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr +++ b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr @@ -4,7 +4,7 @@ error[E0608]: cannot index into a value of type `()` LL | ()[f(&[1.0])]; | ^^^^^^^^^^^ | - = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) + = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error: aborting due to 1 previous error From b1c212f8502e8e7fcb66da0e8da98db770cb7c02 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 1 Oct 2025 08:12:44 +0000 Subject: [PATCH 1117/1900] Fix broken STD build for ESP-IDF --- library/std/src/sys/net/hostname/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs index a4b5b76059d30..8ffe4894d7181 100644 --- a/library/std/src/sys/net/hostname/mod.rs +++ b/library/std/src/sys/net/hostname/mod.rs @@ -1,5 +1,5 @@ cfg_select! { - target_family = "unix" => { + all(target_family = "unix", not(target_os = "espidf")) => { mod unix; pub use unix::hostname; } From 3e24d50407e29f750ae8dc10e84bfef142b715e2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 04:33:46 -0400 Subject: [PATCH 1118/1900] Rename `eval_simple` to `eval_local` and take the `SyntaxContext` as an argument. --- clippy_lints/src/assertions_on_constants.rs | 3 +- clippy_lints/src/floating_point_arithmetic.rs | 13 ++--- clippy_lints/src/if_not_else.rs | 12 +++-- .../src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/manual_rem_euclid.rs | 20 +++++--- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- .../src/methods/is_digit_ascii_radix.rs | 2 +- .../src/methods/unnecessary_min_or_max.rs | 5 +- clippy_lints/src/minmax.rs | 12 +++-- .../src/operators/arithmetic_side_effects.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 5 +- clippy_lints/src/operators/identity_op.rs | 32 ++++++------ .../src/operators/manual_is_multiple_of.rs | 9 ++-- .../src/operators/numeric_arithmetic.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 5 +- clippy_utils/src/consts.rs | 49 +++++++++++++------ clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +-- tests/ui/assertions_on_constants.rs | 1 + tests/ui/assertions_on_constants.stderr | 24 ++++++--- 21 files changed, 134 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a6518216aa820..2586c89bc8680 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition) + && let Some((Constant::Bool(assert_val), const_src)) = + ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) && (const_src.is_local() || !in_const_context) && !(is_debug && as_bool_lit(condition) == Some(false)) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 84d39dd81c919..5052bbb3ca033 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; @@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } /// Returns true iff expr is some zero literal -fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match ConstEvalCtxt::new(cx).eval_simple(expr) { +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index e8afa69b537ea..54e9538fcb993 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse { ), // Don't lint on `… != 0`, as these are likely to be bit tests. // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. - ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => ( - "unnecessary `!=` operation", - "change to `==` and swap the blocks of the `if`/`else`", - ), + ExprKind::Binary(op, _, rhs) + if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) => + { + ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ) + }, _ => return, }; diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 1666e8e5ae324..885649074ab63 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds - && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 41e07e26bff0a..1e91a429fe45c 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_lhs.span.ctxt() == ctxt && add_rhs.span.ctxt() == ctxt && !expr.span.in_external_macro(cx.sess().source_map()) - && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) - && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) + && let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs) + && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 && let Some(hir_id) = path_to_local(rem2_lhs) - && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs) + && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 && rem2_lhs.span.ctxt() == ctxt @@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // constant along with the other expression unchanged if so fn check_for_either_unsigned_int_constant<'a>( cx: &'a LateContext<'_>, + ctxt: SyntaxContext, left: &'a Expr<'_>, right: &'a Expr<'_>, ) -> Option<(u128, &'a Expr<'a>)> { - check_for_unsigned_int_constant(cx, left) + check_for_unsigned_int_constant(cx, ctxt, left) .map(|int_const| (int_const, right)) - .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left))) + .or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left))) } -fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; +fn check_for_unsigned_int_constant<'a>( + cx: &'a LateContext<'_>, + ctxt: SyntaxContext, + expr: &'a Expr<'_>, +) -> Option { + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d917..ac9e51890362b 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -155,7 +155,7 @@ fn handle( && cx.typeck_results().expr_adjustments(body_some).is_empty() && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 9c32e9ac539d9..2aeb6f42d05cd 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix, expr.span.ctxt()) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index b87d81b710269..130e4970a7163 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -25,8 +25,9 @@ pub(super) fn check<'tcx>( && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + let ctxt = expr.span.ctxt(); + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv, ctxt) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 64eafc0ebccdc..8f76e6c9048e1 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -4,6 +4,7 @@ use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -68,8 +69,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM .qpath_res(qpath, path.hir_id) .opt_def_id() .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min), - Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max), + Some(sym::cmp_min) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Min), + Some(sym::cmp_max) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Max), _ => None, }) } else { @@ -79,8 +80,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { match path.ident.name { - sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), _ => None, } } else { @@ -93,6 +94,7 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM fn fetch_const<'a, 'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, @@ -104,7 +106,7 @@ fn fetch_const<'a, 'tcx>( return None; } let ecx = ConstEvalCtxt::new(cx); - match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + match (ecx.eval_local(first_arg, ctxt), ecx.eval_local(second_arg, ctxt)) { (Some(c), None) => Some((m, c, second_arg)), (None, Some(c)) => Some((m, c, first_arg)), // otherwise ignore diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index ea5b81aec31ea..e062e55dad894 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -190,7 +190,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_some() { return; } if !matches!( @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_local(receiver, expr.span.ctxt()).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 8f5ee390f7222..ae1a94a3e23f0 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -39,7 +39,9 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_local(op, parent.span.ctxt()) + == Some(Constant::Int(0)) + { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index ded161c8576a1..03b2cf055d919 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -18,12 +18,13 @@ pub(crate) fn check<'tcx>( ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let ecx = ConstEvalCtxt::new(cx); - let left_is_local = match ecx.eval_with_source(left) { + let ctxt = expr.span.ctxt(); + let left_is_local = match ecx.eval_with_source(left, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match ecx.eval_with_source(right) { + let right_is_local = match ecx.eval_with_source(right, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 3efbb8963587f..43c62e1e131a3 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, kw}; +use rustc_span::{Span, SyntaxContext, kw}; use super::IDENTITY_OP; @@ -41,42 +41,43 @@ pub(crate) fn check<'tcx>( (span, is_coerced) }; + let ctxt = expr.span.ctxt(); match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if is_redundant_op(cx, left, 0) { + if is_redundant_op(cx, left, 0, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 0) { + } else if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0) { + if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Mul => { - if is_redundant_op(cx, left, 1) { + if is_redundant_op(cx, left, 1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 1) { + } else if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Div => { - if is_redundant_op(cx, right, 1) { + if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::BitAnd => { - if is_redundant_op(cx, left, -1) { + if is_redundant_op(cx, left, -1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, -1) { + } else if is_redundant_op(cx, right, -1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } @@ -184,14 +185,17 @@ fn is_allowed<'tcx>( // This lint applies to integers and their references cx.typeck_results().expr_ty(left).peel_refs().is_integral() - && cx.typeck_results().expr_ty(right).peel_refs().is_integral() + && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1)) + && !(cmp == BinOpKind::Shl + && is_zero_integer_const(cx, right, expr.span.ctxt()) + && integer_const(cx, left, expr.span.ctxt()) == Some(1)) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { let ecx = ConstEvalCtxt::new(cx); - if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { + let ctxt = span.ctxt(); + if match (ecx.eval_full_int(left, ctxt), ecx.eval_full_int(right, ctxt)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -200,8 +204,8 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool { - if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { +fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, ctxt: SyntaxContext) -> bool { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_local(e, ctxt).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs index 55bb78cfce5fc..0b9bd4fb6d320 100644 --- a/clippy_lints/src/operators/manual_is_multiple_of.rs +++ b/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -13,14 +13,14 @@ use super::MANUAL_IS_MULTIPLE_OF; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, msrv: Msrv, ) { if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF) - && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs) + && let Some(operand) = uint_compare_to_zero(cx, expr, op, lhs, rhs) && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind && operand_op.node == BinOpKind::Rem && matches!( @@ -57,18 +57,19 @@ pub(super) fn check<'tcx>( // If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand fn uint_compare_to_zero<'tcx>( cx: &LateContext<'tcx>, + e: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, ) -> Option<&'tcx Expr<'tcx>> { let operand = if matches!(lhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt) - && is_zero_integer_const(cx, rhs) + && is_zero_integer_const(cx, rhs, e.span.ctxt()) { lhs } else if matches!(rhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt) - && is_zero_integer_const(cx, lhs) + && is_zero_integer_const(cx, lhs, e.span.ctxt()) { rhs } else { diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 9b1b063c4737d..622f328f369a5 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 5eb207a0aedb0..bb0cab3a30757 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -37,8 +37,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. && let ecx = ConstEvalCtxt::new(cx) - && let Some(lhs_value) = ecx.eval_simple(left) - && let Some(rhs_value) = ecx.eval_simple(right) + && let ctxt = expr.span.ctxt() + && let Some(lhs_value) = ecx.eval_local(left, ctxt) + && let Some(rhs_value) = ecx.eval_local(right, ctxt) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index ecd88daa6b39f..29f4988b4211b 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -387,6 +387,7 @@ pub struct ConstEvalCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>, source: Cell, + ctxt: Cell, } impl<'tcx> ConstEvalCtxt<'tcx> { @@ -398,6 +399,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env: cx.typing_env(), typeck: cx.typeck_results(), source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } @@ -408,13 +410,15 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env, typeck, source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant<'tcx>, ConstantSource)> { self.source.set(ConstantSource::Local); + self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } @@ -424,16 +428,16 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_simple(&self, e: &Expr<'_>) -> Option> { - match self.eval_with_source(e) { + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option> { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, } } /// Attempts to evaluate the expression as an integer without accessing other items. - pub fn eval_full_int(&self, e: &Expr<'_>) -> Option { - match self.eval_with_source(e) { + pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), _ => None, } @@ -455,6 +459,14 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + fn check_ctxt(&self, ctxt: SyntaxContext) { + self.source.set(if self.ctxt.get() != ctxt { + ConstantSource::Constant + } else { + self.source.get() + }); + } + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { self.tcx.crate_name(def_id.krate) == sym::core @@ -477,17 +489,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option> { + self.check_ctxt(e.span.ctxt()); match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), - ExprKind::Block(block, _) => self.block(block), + ExprKind::Block(block, _) => { + self.check_ctxt(block.span.ctxt()); + self.block(block) + }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, sym::cfg).is_some() { - None - } else { - Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) - } + self.check_ctxt(lit.span.ctxt()); + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), @@ -504,7 +517,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), + ExprKind::Binary(op, left, right) => { + self.check_ctxt(e.span.ctxt()); + self.binop(op.node, left, right) + }, ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -525,6 +541,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), ExprKind::Field(local_expr, ref field) => { + self.check_ctxt(field.span.ctxt()); let result = self.expr(local_expr); if let Some(Constant::Adt(constant)) = &self.expr(local_expr) && let ty::Adt(adt_def, _) = constant.ty().kind() @@ -958,8 +975,8 @@ fn field_of_struct<'tcx>( } /// If `expr` evaluates to an integer constant, return its value. -pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) { +pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) } else { None @@ -968,6 +985,6 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { /// Check if `expr` evaluates to an integer constant of 0. #[inline] -pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - integer_const(cx, expr) == Some(0) +pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + integer_const(cx, expr, ctxt) == Some(0) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index bda28a663fb07..6f1bc28fbab81 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -454,7 +454,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name == sym::with_capacity { let arg = args.first()?; - return match ConstEvalCtxt::new(cx).eval_simple(arg) { + return match ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b79e15cd7170b..a6d821c99c1b5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -290,8 +290,10 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) + .eval_local(left, self.left_ctxt), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_local(right, self.right_ctxt), ) && l == r { @@ -842,7 +844,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { - ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e) + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_local(e, e.span.ctxt()) }); // const hashing may result in the same hash as some unrelated node, so add a sort of diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index b613892f206a6..f467d4966aef7 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -43,6 +43,7 @@ fn main() { assert_const!(-1); assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + //~^ assertions_on_constants let flag: bool = cfg!(not(feature = "asdf")); assert!(flag); diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 9aed1405fa043..a996c41b69420 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -72,8 +72,16 @@ LL | debug_assert!(true); | = help: remove the assertion +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:45:5 + | +LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:53:19 + --> tests/ui/assertions_on_constants.rs:54:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ @@ -81,7 +89,7 @@ LL | const _: () = assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:56:5 + --> tests/ui/assertions_on_constants.rs:57:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +97,7 @@ LL | assert!(8 == (7 + 1)); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:67:5 + --> tests/ui/assertions_on_constants.rs:68:5 | LL | assert!(true); | ^^^^^^^^^^^^^ @@ -97,7 +105,7 @@ LL | assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:70:5 + --> tests/ui/assertions_on_constants.rs:71:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +113,7 @@ LL | assert!(8 == (7 + 1)); = help: remove the assertion error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:78:5 + --> tests/ui/assertions_on_constants.rs:79:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -113,7 +121,7 @@ LL | assert!(C); = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:89:5 + --> tests/ui/assertions_on_constants.rs:90:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -121,12 +129,12 @@ LL | assert!(C); = help: consider moving this into a const block: `const { assert!(..) }` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:95:5 + --> tests/ui/assertions_on_constants.rs:96:5 | LL | assert!(C); | ^^^^^^^^^^ | = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From 574ff896d63a02d29e2bb5f8348978692a9a81e2 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 1119/1900] Implement support for temporal mixing of atomic/non-atomic accesses in GenMC mode. Remove atomic initialization dummy writes from tests. --- src/tools/miri/genmc-sys/build.rs | 2 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 11 +- .../cpp/src/MiriInterface/EventHandling.cpp | 44 ++-- .../fail/atomics/atomic_ptr_double_free.rs | 3 - .../fail/data_race/atomic_ptr_alloc_race.rs | 3 - .../atomic_ptr_dealloc_write_race.rs | 3 - .../atomic_ptr_write_dealloc_race.rs | 3 - .../miri/tests/genmc/fail/loom/buggy_inc.rs | 3 - .../genmc/pass/atomics/atomic_ptr_ops.rs | 27 ++- .../pass/atomics/atomic_ptr_roundtrip.rs | 3 - .../atomics/cas_failure_ord_racy_key_init.rs | 3 - .../pass/atomics/mixed_atomic_non_atomic.rs | 41 ++++ .../atomics/mixed_atomic_non_atomic.stderr | 2 + .../genmc/pass/atomics/read_initial_value.rs | 41 ++++ .../pass/atomics/read_initial_value.stderr | 2 + .../miri/tests/genmc/pass/atomics/rmw_ops.rs | 5 +- .../pass/data-structures/ms_queue_dynamic.rs | 8 - .../data-structures/treiber_stack_dynamic.rs | 3 - .../genmc/pass/intercept/spinloop_assume.rs | 4 - .../miri/tests/genmc/pass/litmus/2cowr.rs | 4 - .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 4 - .../miri/tests/genmc/pass/litmus/IRIWish.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/LB.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/MP.rs | 4 - .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 4 - .../tests/genmc/pass/litmus/MPU_rels_acq.rs | 4 - .../tests/genmc/pass/litmus/MP_rels_acqf.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/SB.rs | 4 - .../miri/tests/genmc/pass/litmus/Z6_U.rs | 4 - .../miri/tests/genmc/pass/litmus/casdep.rs | 5 - src/tools/miri/tests/genmc/pass/litmus/ccr.rs | 3 - src/tools/miri/tests/genmc/pass/litmus/cii.rs | 3 - .../miri/tests/genmc/pass/litmus/corr.rs | 4 - .../miri/tests/genmc/pass/litmus/corr0.rs | 3 - .../miri/tests/genmc/pass/litmus/corr1.rs | 3 - .../miri/tests/genmc/pass/litmus/corr2.rs | 3 - .../miri/tests/genmc/pass/litmus/corw.rs | 3 - .../tests/genmc/pass/litmus/cumul-release.rs | 5 - .../miri/tests/genmc/pass/litmus/default.rs | 3 - .../miri/tests/genmc/pass/litmus/detour.rs | 5 - .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 3 - .../miri/tests/genmc/pass/litmus/inc2w.rs | 3 - .../genmc/pass/std/arc.check_count.stderr | 163 +++++++++++++++ src/tools/miri/tests/genmc/pass/std/arc.rs | 49 +++++ .../genmc/pass/std/arc.try_upgrade.stderr | 191 ++++++++++++++++++ .../miri/tests/genmc/pass/std/empty_main.rs | 5 + .../tests/genmc/pass/std/empty_main.stderr | 60 ++++++ .../tests/genmc/pass/std/spawn_std_threads.rs | 13 ++ .../genmc/pass/std/spawn_std_threads.stderr | 167 +++++++++++++++ .../pass/{thread => std}/thread_locals.rs | 38 ++-- .../tests/genmc/pass/std/thread_locals.stderr | 184 +++++++++++++++++ .../genmc/pass/thread/thread_locals.stderr | 2 - 52 files changed, 978 insertions(+), 191 deletions(-) create mode 100644 src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/empty_main.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/empty_main.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr rename src/tools/miri/tests/genmc/pass/{thread => std}/thread_locals.rs (53%) create mode 100644 src/tools/miri/tests/genmc/pass/std/thread_locals.stderr delete mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index ef49be151bf09..964382d824089 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -28,7 +28,7 @@ mod downloading { /// The GenMC repository the we get our commit from. pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "cd01c12032bdd71df742b41c7817f99acc72e7ab"; + pub(crate) const GENMC_COMMIT: &str = "ce775ccd7866db820fa12ffca66463087a11dd96"; /// Ensure that a local GenMC repo is present and set to the correct commit. /// Return the path of the GenMC repo and whether the checked out commit was changed. diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 5f73e5d3d3d06..c9da718aabd61 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -214,9 +214,10 @@ struct MiriGenmcShim : private GenMCDriver { * Automatically calls `inc_pos` and `dec_pos` where needed for the given thread. */ template - auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult { + auto handle_load_reset_if_none(ThreadId tid, std::optional old_val, Ts&&... params) + -> HandleResult { const auto pos = inc_pos(tid); - const auto ret = GenMCDriver::handleLoad(pos, std::forward(params)...); + const auto ret = GenMCDriver::handleLoad(pos, old_val, std::forward(params)...); // If we didn't get a value, we have to reset the index of the current thread. if (!std::holds_alternative(ret)) { dec_pos(tid); @@ -274,6 +275,12 @@ inline SVal to_sval(GenmcScalar scalar) { ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); return SVal(scalar.value, scalar.extra); } + +inline std::optional try_to_sval(GenmcScalar scalar) { + if (scalar.is_init) + return { SVal(scalar.value, scalar.extra) }; + return std::nullopt; +} } // namespace GenmcScalarExt namespace LoadResultExt { diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index c9e7f7a1a8a1c..a8f8b9cc24f9e 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -49,6 +49,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { const auto type = AType::Unsigned; const auto ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), ord, SAddr(address), ASize(size), @@ -74,6 +75,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { const auto pos = inc_pos(thread_id); const auto ret = GenMCDriver::handleStore( pos, + GenmcScalarExt::try_to_sval(old_val), ord, SAddr(address), ASize(size), @@ -84,15 +86,13 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { if (const auto* err = std::get_if(&ret)) return StoreResultExt::from_error(format_error(*err)); - if (!std::holds_alternative(ret)) - ERROR("store returned unexpected result"); - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); - return StoreResultExt::ok( - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos + const bool* is_coherence_order_maximal_write = std::get_if(&ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: Store returned unexpected result." ); + return StoreResultExt::ok(*is_coherence_order_maximal_write); } void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { @@ -117,6 +117,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { // `FaiRead` and `FaiWrite`. const auto load_ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), ordering, SAddr(address), ASize(size), @@ -139,6 +140,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto storePos = inc_pos(thread_id); const auto store_ret = GenMCDriver::handleStore( storePos, + GenmcScalarExt::try_to_sval(old_val), ordering, SAddr(address), ASize(size), @@ -148,16 +150,15 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&store_ret)) return ReadModifyWriteResultExt::from_error(format_error(*err)); - const auto* store_ret_val = std::get_if(&store_ret); - ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result."); - - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: RMW store returned unexpected result." + ); return ReadModifyWriteResultExt::ok( /* old_value: */ read_old_val, new_value, - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + *is_coherence_order_maximal_write ); } @@ -183,6 +184,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto load_ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), success_ordering, SAddr(address), ASize(size), @@ -203,6 +205,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto storePos = inc_pos(thread_id); const auto store_ret = GenMCDriver::handleStore( storePos, + GenmcScalarExt::try_to_sval(old_val), success_ordering, SAddr(address), ASize(size), @@ -211,19 +214,12 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { ); if (const auto* err = std::get_if(&store_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); - const auto* store_ret_val = std::get_if(&store_ret); + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); ERROR_ON( - nullptr == store_ret_val, + nullptr == is_coherence_order_maximal_write, "Unimplemented: compare-exchange store returned unexpected result." ); - - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); - return CompareExchangeResultExt::success( - read_old_val, - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos - ); + return CompareExchangeResultExt::success(read_old_val, *is_coherence_order_maximal_write); } /**** Memory (de)allocation ****/ diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs index d712822a6d00c..c18675931719f 100644 --- a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs @@ -22,9 +22,6 @@ unsafe fn free(ptr: *mut u64) { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs index d82fbf53dac7f..e453c16b157d2 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs @@ -23,9 +23,6 @@ static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs index c9bf1f755925d..10e0d8d854c25 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs @@ -20,9 +20,6 @@ static mut Y: u64 = 0; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs index 2253bac95de8c..e2d3057a5b0df 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs @@ -24,9 +24,6 @@ static Y: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs index 508eae756f320..2e614e6a360ba 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs @@ -42,9 +42,6 @@ impl BuggyInc { fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { unsafe { static BUGGY_INC: BuggyInc = BuggyInc::new(); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - BUGGY_INC.num.store(0, Relaxed); - let ids = [ spawn_pthread_closure(|| { BUGGY_INC.inc(); diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs index 9db58b1f9501b..aa40e193dbfd0 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs @@ -37,18 +37,22 @@ unsafe fn pointers_equal(a: *mut u64, b: *mut u64) { unsafe fn test_load_store_exchange() { let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - // FIXME(genmc): Add test cases with temporal mixing of atomics/non-atomics. - atomic_ptr.store(&raw mut X, SeqCst); - // Load can read the initial value. + // Atomic load can read the initial value. pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); - // Store works as expected. + // Atomic store works as expected. atomic_ptr.store(&raw mut Y, SeqCst); pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + // We can read the value of the atomic store non-atomically. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y); + // We can read the value of a non-atomic store atomically. + *atomic_ptr.as_ptr() = &raw mut X; + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); // Atomic swap must return the old value and store the new one. + *atomic_ptr.as_ptr() = &raw mut Y; // Test that we can read this non-atomic store using `swap`. pointers_equal(atomic_ptr.swap(&raw mut X, SeqCst), &raw mut Y); + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); // Failing compare_exchange (wrong expected pointer). @@ -56,11 +60,17 @@ unsafe fn test_load_store_exchange() { Ok(_ptr) => std::process::abort(), Err(ptr) => pointers_equal(ptr, &raw mut X), } + // Non-atomic read value should also be unchanged by a failing compare_exchange. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); + // Failing compare_exchange (null). match atomic_ptr.compare_exchange(std::ptr::null_mut(), std::ptr::null_mut(), SeqCst, SeqCst) { Ok(_ptr) => std::process::abort(), Err(ptr) => pointers_equal(ptr, &raw mut X), } + // Non-atomic read value should also be unchanged by a failing compare_exchange. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); + // Successful compare_exchange. match atomic_ptr.compare_exchange(&raw mut X, &raw mut Y, SeqCst, SeqCst) { Ok(ptr) => pointers_equal(ptr, &raw mut X), @@ -68,15 +78,13 @@ unsafe fn test_load_store_exchange() { } // compare_exchange should update the pointer. pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y); } unsafe fn test_add_sub() { const LEN: usize = 16; let mut array: [u64; LEN] = std::array::from_fn(|i| i as u64); - let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut array[0]); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - atomic_ptr.store(&raw mut array[0], SeqCst); // Each element of the array should be reachable using `fetch_ptr_add`. // All pointers must stay valid. @@ -110,10 +118,7 @@ unsafe fn test_and_or_xor() { let mut array = AlignedArray(std::array::from_fn(|i| i as u64 * 10)); let array_ptr = &raw mut array.0[0]; - let atomic_ptr: AtomicPtr = AtomicPtr::new(array_ptr); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - atomic_ptr.store(array_ptr, SeqCst); // Test no-op arguments. assert_equals(array_ptr, atomic_ptr.fetch_or(0, SeqCst)); diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs index 75ee5ede9c969..d846a55cbc31a 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs @@ -23,9 +23,6 @@ static mut X: [u8; 16] = [0; 16]; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - PTR.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs index 8a77d54a64f8b..e17a988cb3736 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs @@ -26,9 +26,6 @@ static mut VALUES: [usize; 2] = [0, 0]; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - KEY.store(KEY_SENTVAL, Relaxed); - unsafe { let mut a = 0; let mut b = 0; diff --git a/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs new file mode 100644 index 0000000000000..7601b354b1c00 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can read the value of a non-atomic store atomically and an of an atomic value non-atomically. + +#![no_main] + +use std::sync::atomic::Ordering::*; +use std::sync::atomic::{AtomicI8, AtomicU64}; + +static X: AtomicU64 = AtomicU64::new(1234); +static Y: AtomicI8 = AtomicI8::new(0xB); + +fn assert_equals(a: T, b: T) { + if a != b { + std::process::abort(); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // 8 byte unsigned integer: + // Read initial value. + assert_equals(1234, X.load(Relaxed)); + // Atomic store, non-atomic load. + X.store(0xFFFF, Relaxed); + assert_equals(0xFFFF, unsafe { *X.as_ptr() }); + // Non-atomic store, atomic load. + unsafe { *X.as_ptr() = 0xAAAA }; + assert_equals(0xAAAA, X.load(Relaxed)); + + // 1 byte signed integer: + // Read initial value. + assert_equals(0xB, Y.load(Relaxed)); + // Atomic store, non-atomic load. + Y.store(42, Relaxed); + assert_equals(42, unsafe { *Y.as_ptr() }); + // Non-atomic store, atomic load. + unsafe { *Y.as_ptr() = -42 }; + assert_equals(-42, Y.load(Relaxed)); + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs new file mode 100644 index 0000000000000..18e039fdd0dfe --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can read the initial value of global, heap and stack allocations in GenMC mode. + +#![no_main] + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +static X: AtomicU64 = AtomicU64::new(1234); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // Read initial value of global allocation. + if 1234 != unsafe { *X.as_ptr() } { + std::process::abort(); + } + if 1234 != X.load(SeqCst) { + std::process::abort(); + } + + // Read initial value of stack allocation. + let a = AtomicU64::new(0xBB); + if 0xBB != unsafe { *a.as_ptr() } { + std::process::abort(); + } + if 0xBB != a.load(SeqCst) { + std::process::abort(); + } + + // Read initial value of heap allocation. + let b = Box::new(AtomicU64::new(0xCCC)); + if 0xCCC != unsafe { *b.as_ptr() } { + std::process::abort(); + } + if 0xCCC != b.load(SeqCst) { + std::process::abort(); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs index f48466e8ce10c..7e6e33c8a7b1c 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs @@ -21,11 +21,8 @@ fn assert_eq(x: T, y: T) { macro_rules! test_rmw_edge_cases { ($int:ty, $atomic:ty) => {{ - let x = <$atomic>::new(123); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - x.store(123, ORD); - // MAX, ADD + let x = <$atomic>::new(123); assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs index fda517ae7d231..934fc977366dc 100644 --- a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -70,11 +70,6 @@ impl MyStack { (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); self.head = AtomicPtr::new(dummy); self.tail = AtomicPtr::new(dummy); - - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - (*dummy).next.store(std::ptr::null_mut(), Relaxed); - self.head.store(dummy, Relaxed); - self.tail.store(dummy, Relaxed); } pub unsafe fn clear_queue(&mut self, _num_threads: usize) { @@ -93,9 +88,6 @@ impl MyStack { (*node).value = value; (*node).next = AtomicPtr::new(std::ptr::null_mut()); - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - (*node).next.store(std::ptr::null_mut(), Relaxed); - loop { tail = self.tail.load(Acquire); let next = (*tail).next.load(Acquire); diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs index 2101f9ae66bb6..8bdd2a371f51c 100644 --- a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -104,9 +104,6 @@ impl MyStack { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - unsafe { STACK.top.store(std::ptr::null_mut(), Relaxed) }; - // We try multiple different parameters for the number and types of threads: let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { (1, 3) diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs index 9d7c823b1e485..cf19e92994421 100644 --- a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs @@ -58,10 +58,6 @@ fn spin_until(value: u64) { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - unsafe { X = 0 }; - FLAG.store(0, Relaxed); - unsafe { let t0 = || { X = 42; diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs index d3fdb470ed367..d9b582bb4362d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs index 1ec62ff21342d..6d2dfd4f273e2 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index fa717b4cf18a8..6f1d37962d10a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -28,10 +28,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut results = [1234; 5]; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.rs b/src/tools/miri/tests/genmc/pass/litmus/LB.rs index 1cee3230b127c..107121ef4e3c7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.rs b/src/tools/miri/tests/genmc/pass/litmus/MP.rs index e245cdd15eef2..5f9d1b01c37b0 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs index a1b13e3f3c56f..6f812bf8a8ac9 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -22,10 +22,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = Ok(1234); let mut b = Ok(1234); diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs index b36c8a288f426..4f20b2cf9def2 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs @@ -18,10 +18,6 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { X.store(1, Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs index cf9f5f2dbfa34..19065d3308f8c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs @@ -18,10 +18,6 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { X.store(1, Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.rs b/src/tools/miri/tests/genmc/pass/litmus/SB.rs index e592fe05c4e49..74d45c22a2953 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index a991ccb0d4e04..cbbaa82d6fb5a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -31,10 +31,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs index c376d96b111cc..8b8f6e793c1f7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs @@ -18,11 +18,6 @@ static Z: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let a = X.load(Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs index 865895b4dcd96..4537f3d6830ce 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let expected = 0; diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.rs b/src/tools/miri/tests/genmc/pass/litmus/cii.rs index 663c806e667c4..18f56860f9604 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cii.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let expected = 1; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.rs b/src/tools/miri/tests/genmc/pass/litmus/corr.rs index d6c95100fc241..b586e2e0fa8a8 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.rs @@ -6,7 +6,6 @@ #[path = "../../../utils/genmc.rs"] mod genmc; - use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering::*; @@ -16,9 +15,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs index f722131fda846..856d566ca8bd3 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs index a4e8249bac309..ccd849802911e 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs index 2f490d3637797..36616bf36371f 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.rs b/src/tools/miri/tests/genmc/pass/litmus/corw.rs index 7acc20822a4f4..9216a4f8368f6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corw.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs index 2387976a8ca61..4034f7634e870 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs @@ -18,11 +18,6 @@ static Z: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.rs b/src/tools/miri/tests/genmc/pass/litmus/default.rs index 55fb1ac34acb4..0ab26dce419ad 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/default.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/default.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.rs b/src/tools/miri/tests/genmc/pass/litmus/detour.rs index 7136c029bbb54..85c456d5c54e5 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.rs @@ -22,11 +22,6 @@ static Z: AtomicI64 = AtomicI64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { // Make these static so we can exit the main thread while the other threads still run. // If these are `let mut` like the other tests, this will cause a use-after-free bug. diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs index 796ffbf97f96b..c8d3d409cf04d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut result = [1234; 4]; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs index fd8574c4f7c78..eb84304a1986e 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr new file mode 100644 index 0000000000000..f2c574283cbf0 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -0,0 +1,163 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | handle.join().unwrap(); + | ^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.rs b/src/tools/miri/tests/genmc/pass/std/arc.rs new file mode 100644 index 0000000000000..addf6408c006f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.rs @@ -0,0 +1,49 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: check_count try_upgrade + +// Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. +// +// The number of explored executions in the expected output of this test may change if +// the implementation of `Arc` is ever changed, or additional optimizations are added to GenMC mode. +// +// The revision that tries to upgrade the `Weak` should never explore fewer executions compared to the revision that just accesses the `strong_count`, +// since `upgrade` needs to access the `strong_count` internally. +// There should always be more than 1 execution for both, since there is always the possibilility that the `Arc` has already been dropped, or it hasn't. + +use std::sync::Arc; + +fn main() { + let data = Arc::new(42); + + // Clone the Arc, drop the original, check that memory still valid. + let data_clone = Arc::clone(&data); + drop(data); + assert!(*data_clone == 42); + + // Create a Weak reference. + let weak = Arc::downgrade(&data_clone); + + // Spawn a thread that uses the Arc. + let weak_ = weak.clone(); + let handle = std::thread::spawn(move || { + // Try to upgrade weak reference. + // Depending on execution schedule, this may fail or succeed depending on whether this runs before or after the `drop` in the main thread. + + #[cfg(check_count)] + let _strong_count = weak_.strong_count(); + + #[cfg(try_upgrade)] + if let Some(strong) = weak_.upgrade() { + assert_eq!(*strong, 42); + } + }); + + // Drop the last strong reference to the data. + drop(data_clone); + + // Wait for the thread to finish. + handle.join().unwrap(); + + // The upgrade should fail now (all Arcs dropped). + assert!(weak.upgrade().is_none()); +} diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr new file mode 100644 index 0000000000000..a7dacdeba4e45 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -0,0 +1,191 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | handle.join().unwrap(); + | ^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `std::sync::Weak::::upgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | if let Some(strong) = weak_.upgrade() { + | ^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `std::sync::Weak::::upgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | if let Some(strong) = weak_.upgrade() { + | ^^^^^^^^^^^^^^^ + +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.rs b/src/tools/miri/tests/genmc/pass/std/empty_main.rs new file mode 100644 index 0000000000000..2ffc3388fb36c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.rs @@ -0,0 +1,5 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// A lot of code runs before main, which we should be able to handle in GenMC mode. + +fn main() {} diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr new file mode 100644 index 0000000000000..36ba5481f472c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -0,0 +1,60 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs new file mode 100644 index 0000000000000..dadbee47b9860 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// We should be able to spawn and join standard library threads in GenMC mode. +// Since these threads do nothing, we should only explore 1 program execution. + +const N: usize = 2; + +fn main() { + let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + handles.into_iter().for_each(|handle| handle.join().unwrap()); +} + +fn thread_func() {} diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr new file mode 100644 index 0000000000000..d6195b4e9b212 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -0,0 +1,167 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `std::vec::Vec::>::extend_trusted::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC + = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `std::vec::Vec::>::extend_trusted::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC + = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/alloc/src/vec/into_iter.rs:LL:CC + = note: inside `> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs similarity index 53% rename from src/tools/miri/tests/genmc/pass/thread/thread_locals.rs rename to src/tools/miri/tests/genmc/pass/std/thread_locals.rs index cabc8ec92da1c..d76975d2e92c2 100644 --- a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs @@ -1,17 +1,10 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - use std::alloc::{Layout, alloc}; use std::cell::Cell; use std::sync::atomic::AtomicPtr; use std::sync::atomic::Ordering::*; -use crate::genmc::*; - static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); thread_local! { @@ -22,26 +15,21 @@ pub unsafe fn malloc() -> *mut u64 { alloc(Layout::new::()) as *mut u64 } -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - - unsafe { - spawn_pthread_closure(|| { - R.set(malloc()); +fn main() { + let handles = [ + std::thread::spawn(|| { + R.set(unsafe { malloc() }); let r_ptr = R.get(); let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); - }); - spawn_pthread_closure(|| { - R.set(malloc()); - }); - spawn_pthread_closure(|| { - R.set(malloc()); + }), + std::thread::spawn(|| { + R.set(unsafe { malloc() }); + }), + std::thread::spawn(|| { + R.set(unsafe { malloc() }); let r_ptr = R.get(); let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); - }); - - 0 - } + }), + ]; + handles.into_iter().for_each(|handle| handle.join().unwrap()); } diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr new file mode 100644 index 0000000000000..ff971301bc66a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -0,0 +1,184 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/thread/mod.rs:LL:CC + | +LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/thread/mod.rs:LL:CC + | +LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside closure at RUSTLIB/core/src/ops/try_trait.rs:LL:CC + = note: inside closure at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `::try_fold::<(), {closure@std::array::iter::iter_inner::PolymorphicIter<[std::mem::MaybeUninit>]>::try_fold<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/ops/index_range.rs:LL:CC + = note: inside `std::array::iter::iter_inner::PolymorphicIter::<[std::mem::MaybeUninit>]>::try_fold::<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `std::array::iter::iter_inner::PolymorphicIter::<[std::mem::MaybeUninit>]>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `, 3> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter.rs:LL:CC + = note: inside `, 3> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr deleted file mode 100644 index bde951866d013..0000000000000 --- a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr +++ /dev/null @@ -1,2 +0,0 @@ -Running GenMC Verification... -Verification complete with 2 executions. No errors found. From e6429c74548aa2a5e20b78dca51c5f74ad59dea8 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 1 Oct 2025 21:05:28 +1000 Subject: [PATCH 1120/1900] Don't create a top-level `true` directory when running UI tests --- .../cmse-nonsecure-call/undeclared-lifetime.rs | 5 +++-- .../cmse-nonsecure-call/undeclared-lifetime.stderr | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs index 5fa5b74c0c0b9..0a0dca804ef35 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs @@ -1,5 +1,6 @@ //@ add-core-stubs -//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Cincremental=true +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ incremental (required to trigger the bug) //@ needs-llvm-components: arm #![feature(abi_cmse_nonsecure_call, no_core)] #![no_core] @@ -8,7 +9,7 @@ extern crate minicore; use minicore::*; // A regression test for https://github.com/rust-lang/rust/issues/131639. -// NOTE: -Cincremental=true was required for triggering the bug. +// NOTE: `-Cincremental` was required for triggering the bug. fn foo() { id::(PhantomData); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr index 4aca17e73544d..7300bdb72cdda 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/undeclared-lifetime.rs:14:43 + --> $DIR/undeclared-lifetime.rs:15:43 | LL | id::(PhantomData); | ^^ undeclared lifetime From b810a68197515bfbe5afd8557b566d6817035389 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Wed, 1 Oct 2025 11:53:47 +0000 Subject: [PATCH 1121/1900] added error for closures case in impl --- .../src/coherence/inherent_impls.rs | 9 ++++----- .../rustc_hir_analysis/src/coherence/orphan.rs | 10 ++++++---- tests/ui/closures/impl-closure-147146.rs | 7 +++++++ tests/ui/closures/impl-closure-147146.stderr | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/ui/closures/impl-closure-147146.rs create mode 100644 tests/ui/closures/impl-closure-147146.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 38ae7852ca99f..b069a74bf5adc 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -195,11 +195,10 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Alias(ty::Free, _) - | ty::Bound(..) - | ty::Placeholder(_) - | ty::Infer(_) => { + | ty::CoroutineWitness(..) => { + Err(self.tcx.dcx().delayed_bug("cannot define inherent `impl` for closure types")) + } + ty::Alias(ty::Free, _) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { bug!("unexpected impl self type of impl: {:?} {:?}", id, self_ty); } // We could bail out here, but that will silence other useful errors. diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 621431ae2343c..5a61248cab8fe 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -230,10 +230,12 @@ pub(crate) fn orphan_check_impl( ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) => { + | ty::CoroutineWitness(..) => { + return Err(tcx + .dcx() + .delayed_bug("cannot define inherent `impl` for closure types")); + } + ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => { let sp = tcx.def_span(impl_def_id); span_bug!(sp, "weird self type for autotrait impl") } diff --git a/tests/ui/closures/impl-closure-147146.rs b/tests/ui/closures/impl-closure-147146.rs new file mode 100644 index 0000000000000..b709e577354e5 --- /dev/null +++ b/tests/ui/closures/impl-closure-147146.rs @@ -0,0 +1,7 @@ +impl typeof(|| {}) {} +//~^ ERROR `typeof` is a reserved keyword but unimplemented + +unsafe impl Send for typeof(|| {}) {} +//~^ ERROR `typeof` is a reserved keyword but unimplemented + +fn main() {} diff --git a/tests/ui/closures/impl-closure-147146.stderr b/tests/ui/closures/impl-closure-147146.stderr new file mode 100644 index 0000000000000..6da16b5d450fd --- /dev/null +++ b/tests/ui/closures/impl-closure-147146.stderr @@ -0,0 +1,15 @@ +error[E0516]: `typeof` is a reserved keyword but unimplemented + --> $DIR/impl-closure-147146.rs:1:6 + | +LL | impl typeof(|| {}) {} + | ^^^^^^^^^^^^^ reserved keyword + +error[E0516]: `typeof` is a reserved keyword but unimplemented + --> $DIR/impl-closure-147146.rs:4:22 + | +LL | unsafe impl Send for typeof(|| {}) {} + | ^^^^^^^^^^^^^ reserved keyword + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0516`. From 5ade7647b7ec9080aa91e174f4b12a1eac8e9bd5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 30 Sep 2025 17:47:40 -0500 Subject: [PATCH 1122/1900] Change ArrayWindows to use a slice --- library/core/src/slice/iter.rs | 69 +++++++++++----------------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index ae910e0525209..7053ae86e732f 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2203,16 +2203,13 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} #[unstable(feature = "array_windows", issue = "75027")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ArrayWindows<'a, T: 'a, const N: usize> { - slice_head: *const T, - num: usize, - marker: PhantomData<&'a [T; N]>, + v: &'a [T], } impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { #[inline] pub(super) const fn new(slice: &'a [T]) -> Self { - let num_windows = slice.len().saturating_sub(N - 1); - Self { slice_head: slice.as_ptr(), num: num_windows, marker: PhantomData } + Self { v: slice } } } @@ -2222,49 +2219,34 @@ impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { #[inline] fn next(&mut self) -> Option { - if self.num == 0 { - return None; + let ret = self.v.first_chunk(); + if ret.is_some() { + self.v = &self.v[1..]; } - // SAFETY: - // This is safe because it's indexing into a slice guaranteed to be length > N. - let ret = unsafe { &*self.slice_head.cast::<[T; N]>() }; - // SAFETY: Guaranteed that there are at least 1 item remaining otherwise - // earlier branch would've been hit - self.slice_head = unsafe { self.slice_head.add(1) }; - - self.num -= 1; - Some(ret) + ret } #[inline] fn size_hint(&self) -> (usize, Option) { - (self.num, Some(self.num)) + let size = self.v.len().saturating_sub(N - 1); + (size, Some(size)) } #[inline] fn count(self) -> usize { - self.num + self.len() } #[inline] fn nth(&mut self, n: usize) -> Option { - if self.num <= n { - self.num = 0; - return None; - } - // SAFETY: - // This is safe because it's indexing into a slice guaranteed to be length > N. - let ret = unsafe { &*self.slice_head.add(n).cast::<[T; N]>() }; - // SAFETY: Guaranteed that there are at least n items remaining - self.slice_head = unsafe { self.slice_head.add(n + 1) }; - - self.num -= n + 1; - Some(ret) + let idx = n.min(self.v.len()); + self.v = &self.v[idx..]; + self.next() } #[inline] - fn last(mut self) -> Option { - self.nth(self.num.checked_sub(1)?) + fn last(self) -> Option { + self.v.last_chunk() } } @@ -2272,32 +2254,25 @@ impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { impl<'a, T, const N: usize> DoubleEndedIterator for ArrayWindows<'a, T, N> { #[inline] fn next_back(&mut self) -> Option<&'a [T; N]> { - if self.num == 0 { - return None; + let ret = self.v.last_chunk(); + if ret.is_some() { + self.v = &self.v[..self.v.len() - 1]; } - // SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing. - let ret = unsafe { &*self.slice_head.add(self.num - 1).cast::<[T; N]>() }; - self.num -= 1; - Some(ret) + ret } #[inline] fn nth_back(&mut self, n: usize) -> Option<&'a [T; N]> { - if self.num <= n { - self.num = 0; - return None; - } - // SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing. - let ret = unsafe { &*self.slice_head.add(self.num - (n + 1)).cast::<[T; N]>() }; - self.num -= n + 1; - Some(ret) + let idx = self.v.len().saturating_sub(n); + self.v = &self.v[..idx]; + self.next_back() } } #[unstable(feature = "array_windows", issue = "75027")] impl ExactSizeIterator for ArrayWindows<'_, T, N> { fn is_empty(&self) -> bool { - self.num == 0 + self.v.len() < N } } From 5b809b355c5c134e6848f800a2f8e692a3c10d44 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 1 Oct 2025 14:47:42 +0200 Subject: [PATCH 1123/1900] Don't enable shared memory with Wasm atomics --- compiler/rustc_codegen_ssa/src/back/linker.rs | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e644a43f88340..ac12314373828 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -17,7 +17,6 @@ use rustc_middle::middle::exported_symbols::{ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; -use rustc_span::sym; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld}; use tracing::{debug, warn}; @@ -1324,37 +1323,7 @@ struct WasmLd<'a> { impl<'a> WasmLd<'a> { fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> { - // If the atomics feature is enabled for wasm then we need a whole bunch - // of flags: - // - // * `--shared-memory` - the link won't even succeed without this, flags - // the one linear memory as `shared` - // - // * `--max-memory=1G` - when specifying a shared memory this must also - // be specified. We conservatively choose 1GB but users should be able - // to override this with `-C link-arg`. - // - // * `--import-memory` - it doesn't make much sense for memory to be - // exported in a threaded module because typically you're - // sharing memory and instantiating the module multiple times. As a - // result if it were exported then we'd just have no sharing. - // - // On wasm32-unknown-unknown, we also export symbols for glue code to use: - // * `--export=*tls*` - when `#[thread_local]` symbols are used these - // symbols are how the TLS segments are initialized and configured. - let mut wasm_ld = WasmLd { cmd, sess }; - if sess.target_features.contains(&sym::atomics) { - wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]); - if sess.target.os == "unknown" || sess.target.os == "none" { - wasm_ld.link_args(&[ - "--export=__wasm_init_tls", - "--export=__tls_size", - "--export=__tls_align", - "--export=__tls_base", - ]); - } - } - wasm_ld + WasmLd { cmd, sess } } } From 96fb1b3b559ccc8ed051c95fa5385f9ba4030a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 1 Oct 2025 16:11:21 +0200 Subject: [PATCH 1124/1900] include outer_inclusive_binder of pattern types --- compiler/rustc_type_ir/src/flags.rs | 1 + .../type/pattern_types/transmute.next.stderr | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/type/pattern_types/transmute.next.stderr diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 24704c5bb5358..ea3903d401e39 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -347,6 +347,7 @@ impl FlagComputation { fn add_ty_pat(&mut self, pat: ::Pat) { self.add_flags(pat.flags()); + self.add_exclusive_binder(pat.outer_exclusive_binder()); } fn add_predicate(&mut self, binder: ty::Binder>) { diff --git a/tests/ui/type/pattern_types/transmute.next.stderr b/tests/ui/type/pattern_types/transmute.next.stderr new file mode 100644 index 0000000000000..edec542e5e151 --- /dev/null +++ b/tests/ui/type/pattern_types/transmute.next.stderr @@ -0,0 +1,21 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:23:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:31:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `Option` (64 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. From 0435b16f3b8d4b6139993a60528658416aa36d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 1 Oct 2025 00:20:47 +0200 Subject: [PATCH 1125/1900] swap order of resolve_coroutine_interiors and handle_opaque_type_uses --- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 18 ++++++------------ compiler/rustc_hir_typeck/src/lib.rs | 9 +++------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 833ce433d56f0..35253e4c29195 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -611,19 +611,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { typeck_results.rvalue_scopes = rvalue_scopes; } - /// Unify the inference variables corresponding to coroutine witnesses, and save all the - /// predicates that were stalled on those inference variables. - /// - /// This process allows to conservatively save all predicates that do depend on the coroutine - /// interior types, for later processing by `check_coroutine_obligations`. - /// - /// We must not attempt to select obligations after this method has run, or risk query cycle - /// ICE. + /// Drain all obligations that are stalled on coroutines defined in this body. #[instrument(level = "debug", skip(self))] - pub(crate) fn resolve_coroutine_interiors(&self) { - // Try selecting all obligations that are not blocked on inference variables. - // Once we start unifying coroutine witnesses, trying to select obligations on them will - // trigger query cycle ICEs, as doing so requires MIR. + pub(crate) fn drain_stalled_coroutine_obligations(&self) { + // Make as much inference progress as possible before + // draining the stalled coroutine obligations as this may + // change obligations from being stalled on infer vars to + // being stalled on a coroutine. self.select_obligations_where_possible(|_| {}); let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index acc0481e457fd..9f5a85b692642 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -243,18 +243,15 @@ fn typeck_with_inspect<'tcx>( debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - // This must be the last thing before `report_ambiguity_errors`. - fcx.resolve_coroutine_interiors(); - - debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - // We need to handle opaque types before emitting ambiguity errors as applying // defining uses may guide type inference. if fcx.next_trait_solver() { fcx.handle_opaque_type_uses_next(); } - fcx.select_obligations_where_possible(|_| {}); + // This must be the last thing before `report_ambiguity_errors` below except `select_obligations_where_possible`. + // So don't put anything after this. + fcx.drain_stalled_coroutine_obligations(); if fcx.infcx.tainted_by_errors().is_none() { fcx.report_ambiguity_errors(); } From 84864bcf89592a1c85b6646d600ca111291fbc66 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Wed, 1 Oct 2025 11:07:42 -0400 Subject: [PATCH 1126/1900] Fix typo in 'unfulfilled_lint_expectation' to plural --- src/doc/rustc/src/lints/levels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md index 5b002b435a51e..09b55da741d6c 100644 --- a/src/doc/rustc/src/lints/levels.md +++ b/src/doc/rustc/src/lints/levels.md @@ -38,7 +38,7 @@ talk about later in this section. Sometimes, it can be helpful to suppress lints, but at the same time ensure that the code in question still emits them. The 'expect' level does exactly this. If -the lint in question is not emitted, the `unfulfilled_lint_expectation` lint +the lint in question is not emitted, the `unfulfilled_lint_expectations` lint triggers on the `expect` attribute, notifying you that the expectation is no longer fulfilled. From 8dfea22c974fe18aca8322f1c73f8455dc42c66f Mon Sep 17 00:00:00 2001 From: edwloef Date: Wed, 1 Oct 2025 16:03:46 +0200 Subject: [PATCH 1127/1900] implement `Box::take` --- library/alloc/src/boxed.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 5a63d90b95fa0..49ff768bed1b2 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -619,6 +619,37 @@ impl Box { pub fn into_inner(boxed: Self) -> T { *boxed } + + /// Consumes the `Box` without consuming its allocation, returning the wrapped value and a `Box` + /// to the uninitialized memory where the wrapped value used to live. + /// + /// This can be used together with [`write`](Box::write) to reuse the allocation for multiple + /// boxed values. + /// + /// # Examples + /// + /// ``` + /// #![feature(box_take)] + /// + /// let c = Box::new(5); + /// + /// // take the value out of the box + /// let (value, uninit) = Box::take(c); + /// assert_eq!(value, 5); + /// + /// // reuse the box for a second value + /// let c = Box::write(uninit, 6); + /// assert_eq!(*c, 6); + /// ``` + #[unstable(feature = "box_take", issue = "147212")] + pub fn take(boxed: Self) -> (T, Box, A>) { + unsafe { + let (raw, alloc) = Box::into_raw_with_allocator(boxed); + let value = raw.read(); + let uninit = Box::from_raw_in(raw.cast::>(), alloc); + (value, uninit) + } + } } impl Box<[T]> { From 69619535d9dbad6a7f5e622bfd3e3c3fb190e0aa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Oct 2025 17:36:53 +0200 Subject: [PATCH 1128/1900] Switch `citool` to 2024 edition --- src/ci/citool/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index f61243a4d712b..078b877e44b1c 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "citool" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] anyhow = "1" From 4baf9208a129e5ea8b3973ce2eeb55fdd7404aea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Oct 2025 17:38:36 +0200 Subject: [PATCH 1129/1900] Initialize llvm submodule if not already the case to run citool --- src/ci/citool/src/main.rs | 4 +++- src/ci/citool/src/utils.rs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index fe1b36673a1b0..255d39846da1f 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -24,7 +24,7 @@ use crate::github::JobInfoResolver; use crate::jobs::RunType; use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics}; use crate::test_dashboard::generate_test_dashboard; -use crate::utils::{load_env_var, output_details}; +use crate::utils::{init_submodule_if_needed, load_env_var, output_details}; const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/.."); pub const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker"); @@ -121,6 +121,8 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any (key.clone(), value) })); + init_submodule_if_needed("src/llvm-project/")?; + let mut cmd = Command::new(Path::new(DOCKER_DIRECTORY).join("run.sh")); cmd.arg(job.image()); cmd.envs(custom_env); diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs index 3176cb62f6066..43b220255dc71 100644 --- a/src/ci/citool/src/utils.rs +++ b/src/ci/citool/src/utils.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use std::convert::AsRef; use std::path::Path; +use std::process::Command; use anyhow::Context; @@ -34,3 +36,19 @@ where pub fn normalize_path_delimiters(name: &str) -> Cow<'_, str> { if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() } } + +pub fn init_submodule_if_needed>(path_to_submodule: P) -> anyhow::Result<()> { + let path_to_submodule = path_to_submodule.as_ref(); + + if let Ok(mut iter) = path_to_submodule.read_dir() + && iter.any(|entry| entry.is_ok()) + { + // Seems like the submodule is already initialized, nothing to be done here. + return Ok(()); + } + let mut child = Command::new("git") + .args(&["submodule", "update", "--init"]) + .arg(path_to_submodule) + .spawn()?; + if !child.wait()?.success() { Err(anyhow::anyhow!("git command failed")) } else { Ok(()) } +} From 3e8ce2bda9e9563c546287b4715fcde61d008d3e Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 1 Oct 2025 17:43:53 +0200 Subject: [PATCH 1130/1900] Adjust WASI and WALI targets --- .../src/spec/targets/wasm32_wali_linux_musl.rs | 13 ++++++++++--- .../src/spec/targets/wasm32_wasip1_threads.rs | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs index a0eb4a254fc0e..06e5cfaed92da 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs @@ -6,11 +6,18 @@ use crate::spec::{Cc, LinkerFlavor, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut options = base::linux_wasm::opts(); - options - .add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--export-memory", "--shared-memory"]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &["--export-memory", "--shared-memory", "--max-memory=1073741824"], + ); options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::Yes), - &["--target=wasm32-wasi-threads", "-Wl,--export-memory,", "-Wl,--shared-memory"], + &[ + "--target=wasm32-wasi-threads", + "-Wl,--export-memory,", + "-Wl,--shared-memory", + "-Wl,--max-memory=1073741824", + ], ); Target { diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs index 44d906a507de1..c735c72cb1cb1 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), - &["--import-memory", "--export-memory", "--shared-memory"], + &["--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824"], ); options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::Yes), @@ -28,6 +28,7 @@ pub(crate) fn target() -> Target { "-Wl,--import-memory", "-Wl,--export-memory,", "-Wl,--shared-memory", + "-Wl,--max-memory=1073741824", ], ); From a7eed086b913d708c9f38ce9c833e403f00fc15d Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 30 Sep 2025 19:45:26 +0200 Subject: [PATCH 1131/1900] Add the check-pass and check-fail tests. --- .../same-res-ambigious-extern-fail.rs | 16 +++++++++++++++ .../same-res-ambigious-extern-macro.rs | 8 ++++++++ .../auxiliary/same-res-ambigious-extern.rs | 11 ++++++++++ .../ui/imports/same-res-ambigious.fail.stderr | 20 +++++++++++++++++++ .../same-res-ambigious.nightly-fail.stderr | 20 +++++++++++++++++++ tests/ui/imports/same-res-ambigious.rs | 11 ++++++++++ 6 files changed, 86 insertions(+) create mode 100644 tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs create mode 100644 tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs create mode 100644 tests/ui/imports/auxiliary/same-res-ambigious-extern.rs create mode 100644 tests/ui/imports/same-res-ambigious.fail.stderr create mode 100644 tests/ui/imports/same-res-ambigious.nightly-fail.stderr create mode 100644 tests/ui/imports/same-res-ambigious.rs diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs new file mode 100644 index 0000000000000..61a8d8f0054a8 --- /dev/null +++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs @@ -0,0 +1,16 @@ +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +macro_rules! globbing{ + () => { + pub use same_res_ambigious_extern_macro::*; + } +} + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs new file mode 100644 index 0000000000000..4e9b8427092f5 --- /dev/null +++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs @@ -0,0 +1,8 @@ +//@ edition: 2018 +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_derive(RustEmbed)] +pub fn rust_embed_derive(_input: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs new file mode 100644 index 0000000000000..5269dcd0b17a2 --- /dev/null +++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs @@ -0,0 +1,11 @@ +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +// this imports the same `RustEmbed` macro with `pub` visibility +pub use same_res_ambigious_extern_macro::*; + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; diff --git a/tests/ui/imports/same-res-ambigious.fail.stderr b/tests/ui/imports/same-res-ambigious.fail.stderr new file mode 100644 index 0000000000000..dfd7c5a5f94e0 --- /dev/null +++ b/tests/ui/imports/same-res-ambigious.fail.stderr @@ -0,0 +1,20 @@ +error[E0603]: derive macro `Embed` is private + --> $DIR/same-res-ambigious.rs:8:28 + | +LL | #[derive(ambigious_extern::Embed)] + | ^^^^^ private derive macro + | +note: the derive macro `Embed` is defined here + --> $DIR/auxiliary/same-res-ambigious-extern-fail.rs:16:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ +help: import `Embed` directly + | +LL - #[derive(ambigious_extern::Embed)] +LL + #[derive(same_res_ambigious_extern_macro::RustEmbed)] + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/same-res-ambigious.nightly-fail.stderr b/tests/ui/imports/same-res-ambigious.nightly-fail.stderr new file mode 100644 index 0000000000000..dfd7c5a5f94e0 --- /dev/null +++ b/tests/ui/imports/same-res-ambigious.nightly-fail.stderr @@ -0,0 +1,20 @@ +error[E0603]: derive macro `Embed` is private + --> $DIR/same-res-ambigious.rs:8:28 + | +LL | #[derive(ambigious_extern::Embed)] + | ^^^^^ private derive macro + | +note: the derive macro `Embed` is defined here + --> $DIR/auxiliary/same-res-ambigious-extern-fail.rs:16:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ +help: import `Embed` directly + | +LL - #[derive(ambigious_extern::Embed)] +LL + #[derive(same_res_ambigious_extern_macro::RustEmbed)] + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/same-res-ambigious.rs b/tests/ui/imports/same-res-ambigious.rs new file mode 100644 index 0000000000000..b5c13a15b7c9c --- /dev/null +++ b/tests/ui/imports/same-res-ambigious.rs @@ -0,0 +1,11 @@ +//@ edition: 2018 +//@ revisions: fail pass +//@[pass] check-pass +//@[pass] aux-crate: ambigious_extern=same-res-ambigious-extern.rs +//@[fail] aux-crate: ambigious_extern=same-res-ambigious-extern-fail.rs +// see https://github.com/rust-lang/rust/pull/147196 + +#[derive(ambigious_extern::Embed)] //[fail]~ ERROR: derive macro `Embed` is private +struct Foo{} + +fn main(){} From 94f00f4e4a0240bc7b8284c78482e37af252309a Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 1 Oct 2025 11:32:54 -0400 Subject: [PATCH 1132/1900] Fix memory leak in `os` impl --- library/std/src/sys/thread_local/os.rs | 103 ++++++++++++------ .../miri/tests/pass/thread_local-panic.rs | 8 ++ .../miri/tests/pass/thread_local-panic.stderr | 5 + 3 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 src/tools/miri/tests/pass/thread_local-panic.rs create mode 100644 src/tools/miri/tests/pass/thread_local-panic.stderr diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index b488f12fe841f..88bb5ae7c650d 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,9 +1,12 @@ use super::key::{Key, LazyKey, get, set}; use super::{abort_on_dtor_unwind, guard}; -use crate::alloc::Layout; +use crate::alloc::{self, Layout}; use crate::cell::Cell; use crate::marker::PhantomData; -use crate::ptr; +use crate::mem::ManuallyDrop; +use crate::ops::Deref; +use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; +use crate::ptr::{self, NonNull}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -110,6 +113,60 @@ pub const fn value_align() -> usize { crate::mem::align_of::>() } +/// Equivalent to `Box>`, but potentially over-aligned. +struct AlignedBox { + ptr: NonNull>, +} + +impl AlignedBox { + #[inline] + fn new(v: Value) -> Self { + let layout = Layout::new::>().align_to(ALIGN).unwrap(); + + let ptr: *mut Value = (unsafe { alloc::alloc(layout) }).cast(); + let Some(ptr) = NonNull::new(ptr) else { + alloc::handle_alloc_error(layout); + }; + unsafe { ptr.write(v) }; + Self { ptr } + } + + #[inline] + fn into_raw(b: Self) -> *mut Value { + let md = ManuallyDrop::new(b); + md.ptr.as_ptr() + } + + #[inline] + unsafe fn from_raw(ptr: *mut Value) -> Self { + Self { ptr: unsafe { NonNull::new_unchecked(ptr) } } + } +} + +impl Deref for AlignedBox { + type Target = Value; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { &*(self.ptr.as_ptr()) } + } +} + +impl Drop for AlignedBox { + #[inline] + fn drop(&mut self) { + let layout = Layout::new::>().align_to(ALIGN).unwrap(); + + unsafe { + let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place())); + alloc::dealloc(self.ptr.as_ptr().cast(), layout); + if let Err(payload) = unwind_result { + resume_unwind(payload); + } + } + } +} + impl Storage { pub const fn new() -> Storage { Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } @@ -148,15 +205,11 @@ impl Storage { return ptr::null(); } - // Manually allocate with the requested alignment - let layout = Layout::new::>().align_to(ALIGN).unwrap(); - let ptr: *mut Value = (unsafe { crate::alloc::alloc(layout) }).cast(); - if ptr.is_null() { - crate::alloc::handle_alloc_error(layout); - } - unsafe { - ptr.write(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); - } + let value = AlignedBox::::new(Value { + value: i.and_then(Option::take).unwrap_or_else(f), + key, + }); + let ptr = AlignedBox::into_raw(value); // SAFETY: // * key came from a `LazyKey` and is thus correct. @@ -174,10 +227,7 @@ impl Storage { // initializer has already returned and the next scope only starts // after we return the pointer. Therefore, there can be no references // to the old value. - unsafe { - old.drop_in_place(); - crate::alloc::dealloc(old.cast(), layout); - } + drop(unsafe { AlignedBox::::from_raw(old) }); } // SAFETY: We just created this value above. @@ -196,22 +246,13 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. abort_on_dtor_unwind(|| { - let value_ptr: *mut Value = ptr.cast(); - unsafe { - let key = (*value_ptr).key; - - // SAFETY: `key` is the TLS key `ptr` was stored under. - set(key, ptr::without_provenance_mut(1)); - - // drop and deallocate the value - let layout = - Layout::from_size_align_unchecked(crate::mem::size_of::>(), ALIGN); - value_ptr.drop_in_place(); - crate::alloc::dealloc(ptr, layout); - - // SAFETY: `key` is the TLS key `ptr` was stored under. - set(key, ptr::null_mut()); - }; + let ptr = unsafe { AlignedBox::::from_raw(ptr as *mut Value) }; + let key = ptr.key; + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::without_provenance_mut(1)) }; + drop(ptr); + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::null_mut()) }; // Make sure that the runtime cleanup will be performed // after the next round of TLS destruction. guard::enable(); diff --git a/src/tools/miri/tests/pass/thread_local-panic.rs b/src/tools/miri/tests/pass/thread_local-panic.rs new file mode 100644 index 0000000000000..549acd23987e9 --- /dev/null +++ b/src/tools/miri/tests/pass/thread_local-panic.rs @@ -0,0 +1,8 @@ +thread_local! { + static LOCAL: u64 = panic!(); + +} + +fn main() { + let _ = std::panic::catch_unwind(|| LOCAL.with(|_| {})); +} diff --git a/src/tools/miri/tests/pass/thread_local-panic.stderr b/src/tools/miri/tests/pass/thread_local-panic.stderr new file mode 100644 index 0000000000000..e69340a810268 --- /dev/null +++ b/src/tools/miri/tests/pass/thread_local-panic.stderr @@ -0,0 +1,5 @@ + +thread 'main' ($TID) panicked at tests/pass/thread_local-panic.rs:LL:CC: +explicit panic +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect From 5991c8703d01cc73c31c51fef5d92b93ac1391b4 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:14:45 +0200 Subject: [PATCH 1133/1900] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index 33f1af40cc44d..1d7c3e6abec2d 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 33f1af40cc44dde7e3e892f7a508e6f427d2cbc6 +Subproject commit 1d7c3e6abec2d5a9bfac798b29b7855b95025426 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index aa6ce337c0adf..e2ed891f00361 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit aa6ce337c0adf7a63e33960d184270f2a45ab9ef +Subproject commit e2ed891f00361efc26616d82590b1c85d7a8920e diff --git a/src/doc/nomicon b/src/doc/nomicon index f17a018b99894..23fc2682f8fcb 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit f17a018b9989430967d1c58e9a12c51169abc744 +Subproject commit 23fc2682f8fcb887f77d0eaabba708809f834c11 diff --git a/src/doc/reference b/src/doc/reference index cc7247d8dfaef..e11adf6016a36 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit cc7247d8dfaef4c39000bb12c55c32ba5b5ba976 +Subproject commit e11adf6016a362766eea5a3f9832e193994dd0c8 From 6eef2859c73592967222f13c2d0ad599be75fe6b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 1 Oct 2025 18:20:01 +0000 Subject: [PATCH 1134/1900] Add an ACP list item to the library tracking issue template Most new API has an associated ACP that is useful to reference, but it doesn't appear anywhere on the template for new tracking issues. Update this template to include a link to the ACP. --- .github/ISSUE_TEMPLATE/library_tracking_issue.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md index d56da9d5d025a..de823dc300d15 100644 --- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -51,6 +51,7 @@ If the feature is changed later, please add those PRs here as well. (Remember to update the `S-tracking-*` label when checking boxes.) +- [ ] ACP: rust-lang/libs-team#... - [ ] Implementation: #... - [ ] Final comment period (FCP)[^1] - [ ] Stabilization PR From a7bd285a6a3de46ec32c355865b449a56861bfd9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 26 Sep 2025 14:21:08 +0200 Subject: [PATCH 1135/1900] cmse: use BackendRepr to determine valid 64-bit return types --- .../src/hir_ty_lowering/cmse.rs | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 0a41659ec6685..378771c05ee33 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -1,8 +1,8 @@ -use rustc_abi::ExternAbi; +use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar}; use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err}; use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; -use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt}; use crate::errors; @@ -164,44 +164,34 @@ fn is_valid_cmse_output<'tcx>( ) -> Result> { // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); let typing_env = ty::TypingEnv::fully_monomorphized(); + let layout = tcx.layout_of(typing_env.as_query_input(fn_sig.output()))?; + + Ok(is_valid_cmse_output_layout(layout)) +} - let mut ret_ty = fn_sig.output(); - let layout = tcx.layout_of(typing_env.as_query_input(ret_ty))?; +/// Returns whether the output will fit into the available registers +fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool { let size = layout.layout.size().bytes(); if size <= 4 { - return Ok(true); + return true; } else if size > 8 { - return Ok(false); + return false; } - // next we need to peel any repr(transparent) layers off - 'outer: loop { - let ty::Adt(adt_def, args) = ret_ty.kind() else { - break; - }; - - if !adt_def.repr().transparent() { - break; - } + // Accept scalar 64-bit types. + let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else { + return false; + }; - // the first field with non-trivial size and alignment must be the data - for variant_def in adt_def.variants() { - for field_def in variant_def.fields.iter() { - let ty = field_def.ty(tcx, args); - let layout = tcx.layout_of(typing_env.as_query_input(ty))?; - - if !layout.layout.is_1zst() { - ret_ty = ty; - continue 'outer; - } - } - } - } + let Scalar::Initialized { value, .. } = scalar else { + return false; + }; - Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64) + matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64)) } fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool { From 4735c2184c72b8b3a8bad642407a74d4b19838fb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 2 Jul 2025 17:18:57 +0800 Subject: [PATCH 1136/1900] Fix diagnostics str::replace comma to bar --- .../rustc_parse/src/parser/diagnostics.rs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a28af7833c387..5d6da60931200 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2947,26 +2947,24 @@ impl<'a> Parser<'a> { } let seq_span = lo.to(self.prev_token.span); let mut err = self.dcx().struct_span_err(comma_span, "unexpected `,` in pattern"); - if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { - err.multipart_suggestion( - format!( - "try adding parentheses to match on a tuple{}", - if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, - ), - vec![ - (seq_span.shrink_to_lo(), "(".to_string()), - (seq_span.shrink_to_hi(), ")".to_string()), - ], + err.multipart_suggestion( + format!( + "try adding parentheses to match on a tuple{}", + if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, + ), + vec![ + (seq_span.shrink_to_lo(), "(".to_string()), + (seq_span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + if let CommaRecoveryMode::EitherTupleOrPipe = rt { + err.span_suggestion( + comma_span, + "...or a vertical bar to match on alternative", + " |", Applicability::MachineApplicable, ); - if let CommaRecoveryMode::EitherTupleOrPipe = rt { - err.span_suggestion( - seq_span, - "...or a vertical bar to match on multiple alternatives", - seq_snippet.replace(',', " |"), - Applicability::MachineApplicable, - ); - } } Err(err) } From a08228d284a91e5b2514035b723e82deb9179985 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sat, 27 Sep 2025 22:57:13 +0200 Subject: [PATCH 1137/1900] bless tests --- ...ssue-48492-tuple-destructure-missing-parens.stderr | 4 ++-- .../feature-gates/feature-gate-never_patterns.stderr | 2 +- tests/ui/parser/match-arm-without-body.rs | 4 ++-- tests/ui/parser/match-arm-without-body.stderr | 11 ++++------- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index c74cb89f85cb9..dc875ad27ea6e 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -32,10 +32,10 @@ help: try adding parentheses to match on a tuple... | LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Nucleotide::Adenine, Nucleotide::Cytosine, _ => true -LL + Nucleotide::Adenine | Nucleotide::Cytosine | _ => true +LL + Nucleotide::Adenine | Nucleotide::Cytosine, _ => true | error: unexpected `,` in pattern diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 473e263c79650..5e810249b9063 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs index 4723abff8b6ae..7fe5b6d253980 100644 --- a/tests/ui/parser/match-arm-without-body.rs +++ b/tests/ui/parser/match-arm-without-body.rs @@ -17,13 +17,13 @@ fn main() { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple - //~| HELP or a vertical bar to match on multiple alternatives + //~| HELP or a vertical bar to match on alternative } match Some(false) { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple - //~| HELP or a vertical bar to match on multiple alternatives + //~| HELP or a vertical bar to match on alternative _ => {} } match Some(false) { diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index a65875b787a39..85ff7db8d4aa3 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -16,7 +16,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Some(_), LL + Some(_) | @@ -36,13 +36,10 @@ LL | LL | LL ~ _) => {} | -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | -LL ~ Some(_) | -LL + -LL + -LL + -LL ~ _ => {} +LL - Some(_), +LL + Some(_) | | error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_` From 934ad740270944800d03f6290bcc131a8697e59f Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Wed, 1 Oct 2025 22:40:23 +0200 Subject: [PATCH 1138/1900] regression test --- ...place-intended-bar-not-all-in-pattern.fixed | 18 ++++++++++++++++++ ...-replace-intended-bar-not-all-in-pattern.rs | 18 ++++++++++++++++++ ...lace-intended-bar-not-all-in-pattern.stderr | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed new file mode 100644 index 0000000000000..5abc1edd381bf --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed @@ -0,0 +1,18 @@ +//@ run-rustfix + +// Regression test for issue #143330. +// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. + +fn main() { + struct Foo { x: i32, ch: char } + let pos = Foo { x: 2, ch: 'x' }; + match pos { + // All commas here were replaced with bars. + // Foo { x: 2 | ch: ' |' } | Foo { x: 3 | ch: '@' } => (), + (Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' }) => (), + //~^ ERROR unexpected `,` in pattern + //~| HELP try adding parentheses to match on a tuple... + //~| HELP ...or a vertical bar to match on alternative + _ => todo!(), + } +} diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs new file mode 100644 index 0000000000000..5bd267c3bc402 --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs @@ -0,0 +1,18 @@ +//@ run-rustfix + +// Regression test for issue #143330. +// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. + +fn main() { + struct Foo { x: i32, ch: char } + let pos = Foo { x: 2, ch: 'x' }; + match pos { + // All commas here were replaced with bars. + // Foo { x: 2 | ch: ' |' } | Foo { x: 3 | ch: '@' } => (), + Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), + //~^ ERROR unexpected `,` in pattern + //~| HELP try adding parentheses to match on a tuple... + //~| HELP ...or a vertical bar to match on alternative + _ => todo!(), + } +} diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr new file mode 100644 index 0000000000000..db0bb8c50e865 --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr @@ -0,0 +1,18 @@ +error: unexpected `,` in pattern + --> $DIR/only-replace-intended-bar-not-all-in-pattern.rs:12:30 + | +LL | Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | (Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' }) => (), + | + + +help: ...or a vertical bar to match on alternative + | +LL - Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), +LL + Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' } => (), + | + +error: aborting due to 1 previous error + From 951d35eeb6aa3bd0ba6fd399290cbdeceec5a97a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 21 Sep 2025 17:30:57 +0200 Subject: [PATCH 1139/1900] Do not trigger `inefficient_to_string` after Rust 1.82 Starting with Rust version 1.82.0, the compiler generates similar code with and without the `with_ref` cfg: ```rust fn f(x: impl IntoIterator) { for y in x { println!("{y}"); } } fn main() { #[cfg(with_ref)] let a = ["foo", "bar"].iter().map(|&s| s.to_string()); #[cfg(not(with_ref))] let a = ["foo", "bar"].iter().map(|s| s.to_string()); f(a); } ``` The generated code is strictly identical with `-O`, and identical modulo some minor reordering without. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/methods/inefficient_to_string.rs | 4 ++++ clippy_lints/src/methods/mod.rs | 11 +++++++---- clippy_utils/src/msrvs.rs | 2 +- tests/ui/inefficient_to_string.fixed | 8 ++++++++ tests/ui/inefficient_to_string.rs | 8 ++++++++ tests/ui/inefficient_to_string.stderr | 12 ++++++------ 8 files changed, 36 insertions(+), 11 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1d..5953e5048e4a6 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -845,6 +845,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [`inefficient_to_string`](https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string) * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad82..6e8752c5f932d 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -741,6 +741,7 @@ define_Conf! { from_over_into, if_then_some_else_none, index_refutable_slice, + inefficient_to_string, io_other_error, iter_kv_map, legacy_numeric_constants, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 47195fdd65f5f..ab21515f47f78 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ pub fn check( method_name: Symbol, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], + msrv: Msrv, ) { if args.is_empty() && method_name == sym::to_string @@ -26,6 +28,8 @@ pub fn check( && let self_ty = args.type_at(0) && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 + // Since Rust 1.82, the specialized `ToString` is properly called + && !msrv.meets(cx, msrvs::SPECIALIZED_TO_STRING_FOR_REFS) && specializes_tostring(cx, deref_self_ty) { span_lint_and_then( diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4d..5448a266ab7d0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -477,6 +477,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `ok().expect(..)`. /// + /// Note: This lint only triggers for code marked compatible + /// with versions of the compiler older than Rust 1.82.0. + /// /// ### Why is this bad? /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. @@ -1080,9 +1083,9 @@ declare_clippy_lint! { /// `T` implements `ToString` directly (like `&&str` or `&&String`). /// /// ### Why is this bad? - /// This bypasses the specialized implementation of - /// `ToString` and instead goes through the more expensive string formatting - /// facilities. + /// In versions of the compiler before Rust 1.82.0, this bypasses the specialized + /// implementation of`ToString` and instead goes through the more expensive string + /// formatting facilities. /// /// ### Example /// ```no_run @@ -4868,7 +4871,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); + inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4d..43e9a8f505191 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -28,7 +28,7 @@ msrv_aliases! { 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } - 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } + 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } diff --git a/tests/ui/inefficient_to_string.fixed b/tests/ui/inefficient_to_string.fixed index a0d34e58a9255..29cf6de6ae5e8 100644 --- a/tests/ui/inefficient_to_string.fixed +++ b/tests/ui/inefficient_to_string.fixed @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = (**rrrcow).to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.rs b/tests/ui/inefficient_to_string.rs index cbe90d4a125b0..724955c60f795 100644 --- a/tests/ui/inefficient_to_string.rs +++ b/tests/ui/inefficient_to_string.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = rrrcow.to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.stderr b/tests/ui/inefficient_to_string.stderr index 8593c0addc5f8..ea3dd7e0ae2f8 100644 --- a/tests/ui/inefficient_to_string.stderr +++ b/tests/ui/inefficient_to_string.stderr @@ -1,5 +1,5 @@ error: calling `to_string` on `&&str` - --> tests/ui/inefficient_to_string.rs:10:21 + --> tests/ui/inefficient_to_string.rs:11:21 | LL | let _: String = rrstr.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` @@ -12,7 +12,7 @@ LL | #![deny(clippy::inefficient_to_string)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `to_string` on `&&&str` - --> tests/ui/inefficient_to_string.rs:12:21 + --> tests/ui/inefficient_to_string.rs:13:21 | LL | let _: String = rrrstr.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` @@ -20,7 +20,7 @@ LL | let _: String = rrrstr.to_string(); = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` error: calling `to_string` on `&&std::string::String` - --> tests/ui/inefficient_to_string.rs:21:21 + --> tests/ui/inefficient_to_string.rs:22:21 | LL | let _: String = rrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` @@ -28,7 +28,7 @@ LL | let _: String = rrstring.to_string(); = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::string::String` - --> tests/ui/inefficient_to_string.rs:23:21 + --> tests/ui/inefficient_to_string.rs:24:21 | LL | let _: String = rrrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` @@ -36,7 +36,7 @@ LL | let _: String = rrrstring.to_string(); = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:32:21 + --> tests/ui/inefficient_to_string.rs:33:21 | LL | let _: String = rrcow.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` @@ -44,7 +44,7 @@ LL | let _: String = rrcow.to_string(); = help: `&std::borrow::Cow<'_, str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<'_, str>` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:34:21 + --> tests/ui/inefficient_to_string.rs:35:21 | LL | let _: String = rrrcow.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` From 8fcb7e18c108294b06fd4c27ab6f007d68a7ef1b Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Wed, 1 Oct 2025 21:22:53 +0000 Subject: [PATCH 1140/1900] extended doc comment --- library/alloc/src/collections/btree/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index adcb444d08c66..ca5b46c9b0fd0 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1368,7 +1368,8 @@ impl BTreeMap { } /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. + /// including the key. If the key is not present, the split will occur at the nearest + /// greater key, or return an empty map if no such key exists. /// /// # Examples /// From 08b4641f50f6f8fa3aa222fa606a0c1a2482b72d Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Wed, 20 Aug 2025 18:20:53 -0400 Subject: [PATCH 1141/1900] add CloneFromCell and Cell::get_cloned --- library/alloc/src/lib.rs | 1 + library/alloc/src/rc.rs | 10 ++++- library/alloc/src/sync.rs | 9 ++++ library/core/src/cell.rs | 92 ++++++++++++++++++++++++++++++++++++++- library/core/src/tuple.rs | 10 +++++ 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index cba1ce40f75d5..e7cfd01e654e4 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -103,6 +103,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] +#![feature(cell_get_cloned)] #![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index aed3357afbf4b..783b6147e9569 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -242,7 +242,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::any::Any; -use core::cell::Cell; +use core::cell::{Cell, CloneFromCell}; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -338,6 +338,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> for #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Rc {} +// SAFETY: `Rc::clone` doesn't access any `Cell`s which could contain the `Rc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Rc {} + impl Rc { #[inline] unsafe fn from_inner(ptr: NonNull>) -> Self { @@ -3011,6 +3015,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Weak {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Weak {} + impl Weak { /// Constructs a new `Weak`, without allocating any memory. /// Calling [`upgrade`] on the return value always gives [`None`]. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a466b74944c5d..eddc9047f21b8 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -9,6 +9,7 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::any::Any; +use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -281,6 +282,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> fo #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Arc {} +// SAFETY: `Arc::clone` doesn't access any `Cell`s which could contain the `Arc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Arc {} + impl Arc { unsafe fn from_inner(ptr: NonNull>) -> Self { unsafe { Self::from_inner_in(ptr, Global) } @@ -356,6 +361,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Weak {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Weak {} + #[stable(feature = "arc_weak", since = "1.4.0")] impl fmt::Debug for Weak { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7d4a66640b106..a991bbf46b1f2 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -253,11 +253,12 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; -use crate::mem; -use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{self, CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::panic::const_panic; use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; +use crate::range; mod lazy; mod once; @@ -713,6 +714,93 @@ impl Cell<[T; N]> { } } +/// Types for which cloning `Cell` is sound. +/// +/// # Safety +/// +/// Implementing this trait for a type is sound if and only if the following code is sound for T = +/// that type. +/// +/// ``` +/// #![feature(cell_get_cloned)] +/// # use std::cell::{CloneFromCell, Cell}; +/// fn clone_from_cell(cell: &Cell) -> T { +/// unsafe { T::clone(&*cell.as_ptr()) } +/// } +/// ``` +/// +/// Importantly, you can't just implement `CloneFromCell` for any arbitrary `Copy` type, e.g. the +/// following is unsound: +/// +/// ```rust +/// #![feature(cell_get_cloned)] +/// # use std::cell::Cell; +/// +/// #[derive(Copy, Debug)] +/// pub struct Bad<'a>(Option<&'a Cell>>, u8); +/// +/// impl Clone for Bad<'_> { +/// fn clone(&self) -> Self { +/// let a: &u8 = &self.1; +/// // when self.0 points to self, we write to self.1 while we have a live `&u8` pointing to +/// // it -- this is UB +/// self.0.unwrap().set(Self(None, 1)); +/// dbg!((a, self)); +/// Self(None, 0) +/// } +/// } +/// +/// // this is not sound +/// // unsafe impl CloneFromCell for Bad<'_> {} +/// ``` +#[unstable(feature = "cell_get_cloned", issue = "145329")] +// Allow potential overlapping implementations in user code +#[marker] +pub unsafe trait CloneFromCell: Clone {} + +// `CloneFromCell` can be implemented for types that don't have indirection and which don't access +// `Cell`s in their `Clone` implementation. A commonly-used subset is covered here. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for [T; N] {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Option {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Result {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for PhantomData {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for ManuallyDrop {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for ops::Range {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for range::Range {} + +#[unstable(feature = "cell_get_cloned", issue = "145329")] +impl Cell { + /// Get a clone of the `Cell` that contains a copy of the original value. + /// + /// This allows a cheaply `Clone`-able type like an `Rc` to be stored in a `Cell`, exposing the + /// cheaper `clone()` method. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_get_cloned)] + /// + /// use core::cell::Cell; + /// use std::rc::Rc; + /// + /// let rc = Rc::new(1usize); + /// let c1 = Cell::new(rc); + /// let c2 = c1.get_cloned(); + /// assert_eq!(*c2.into_inner(), 1); + /// ``` + pub fn get_cloned(&self) -> Self { + // SAFETY: T is CloneFromCell, which guarantees that this is sound. + Cell::new(T::clone(unsafe { &*self.as_ptr() })) + } +} + /// A mutable memory location with dynamically checked borrow rules /// /// See the [module-level documentation](self) for more. diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index c57a8d81ade18..58f81372aff75 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,5 +1,6 @@ // See core/src/primitive_docs.rs for documentation. +use crate::cell::CloneFromCell; use crate::cmp::Ordering::{self, *}; use crate::marker::{ConstParamTy_, StructuralPartialEq}; use crate::ops::ControlFlow::{self, Break, Continue}; @@ -155,6 +156,15 @@ macro_rules! tuple_impls { } } } + + maybe_tuple_doc! { + $($T)+ @ + // SAFETY: tuples introduce no additional indirection, so they can be copied whenever T + // can. + #[unstable(feature = "cell_get_cloned", issue = "145329")] + unsafe impl<$($T: CloneFromCell),+> CloneFromCell for ($($T,)+) + {} + } } } From 30d57abccf7d55e9dd4e36be583c55f6b0090302 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 14 Sep 2025 14:32:56 +0800 Subject: [PATCH 1142/1900] mbe: Rename a local variable to match corresponding field names This simplifies subsequent initialization of enum variants. --- compiler/rustc_expand/src/mbe/macro_rules.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index d4504ba720e46..8ed46a9fe9f67 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -741,10 +741,10 @@ pub fn compile_declarative_macro( if let Some(guar) = check_no_eof(sess, &p, "expected right-hand side of macro rule") { return dummy_syn_ext(guar); } - let rhs_tt = p.parse_token_tree(); - let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition); - check_emission(check_rhs(sess, &rhs_tt)); - check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs_tt)); + let rhs = p.parse_token_tree(); + let rhs = parse_one_tt(rhs, RulePart::Body, sess, node_id, features, edition); + check_emission(check_rhs(sess, &rhs)); + check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs)); let lhs_span = lhs_tt.span(); // Convert the lhs into `MatcherLoc` form, which is better for doing the // actual matching. @@ -760,11 +760,11 @@ pub fn compile_declarative_macro( }; let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; - rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt }); + rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs }); } else if is_derive { - rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt }); + rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs }); } else { - rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt }); + rules.push(MacroRule::Func { lhs, lhs_span, rhs }); } if p.token == token::Eof { break; From 6bff1abf9d3c171d2380aacb8b4c6f52580192b3 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 14 Sep 2025 16:17:51 +0800 Subject: [PATCH 1143/1900] mbe: Support `unsafe` attribute rules --- .../rustc_attr_parsing/src/validate_attr.rs | 12 ++--- compiler/rustc_expand/src/base.rs | 17 ++++++- compiler/rustc_expand/src/expand.rs | 6 ++- compiler/rustc_expand/src/mbe/macro_rules.rs | 46 +++++++++++++++++-- .../unsafe/double-unsafe-attributes.rs | 2 +- .../unsafe/double-unsafe-attributes.stderr | 6 +-- .../unsafe-safe-attribute_diagnostic.rs | 2 +- .../unsafe-safe-attribute_diagnostic.stderr | 6 +-- 8 files changed, 75 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 7a7624893bd21..927417f89f8c0 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -207,10 +207,9 @@ pub fn check_attribute_safety( } } - // - Normal builtin attribute, or any non-builtin attribute - // - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is - // not permitted on non-builtin attributes or normal builtin attributes - (Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => { + // - Normal builtin attribute + // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes + (Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { psess.dcx().emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: attr_item.path.clone(), @@ -224,9 +223,8 @@ pub fn check_attribute_safety( } // - Non-builtin attribute - // - No explicit `#[unsafe(..)]` written. - (None, Safety::Default) => { - // OK + (None, Safety::Unsafe(_) | Safety::Default) => { + // OK (not checked here) } ( diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 33b712e3aedfc..810a5a21a055b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -10,7 +10,7 @@ use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::token::MetaVarKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; +use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -345,6 +345,21 @@ pub trait AttrProcMacro { annotation: TokenStream, annotated: TokenStream, ) -> Result; + + // Default implementation for safe attributes; override if the attribute can be unsafe. + fn expand_with_safety<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + safety: Safety, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + if let Safety::Unsafe(span) = safety { + ecx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute"); + } + self.expand(ecx, span, annotation, annotated) + } } impl AttrProcMacro for F diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 172bc3d1d9f86..a035896d5542c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -812,11 +812,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => item.to_tokens(), }; let attr_item = attr.get_normal_item(); + let safety = attr_item.unsafety; if let AttrArgs::Eq { .. } = attr_item.args { self.cx.dcx().emit_err(UnsupportedKeyValue { span }); } let inner_tokens = attr_item.args.inner_tokens(); - match expander.expand(self.cx, span, inner_tokens, tokens) { + match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) { Ok(tok_result) => { let fragment = self.parse_ast_fragment( tok_result, @@ -882,6 +883,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } } else if let SyntaxExtensionKind::NonMacroAttr = ext { + if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety { + self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute"); + } // `-Zmacro-stats` ignores these because they don't do any real expansion. self.cx.expanded_inert_attrs.mark(&attr); item.visit_attrs(|attrs| attrs.insert(pos, attr)); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8ed46a9fe9f67..c548cea537f40 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -8,7 +8,7 @@ use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{self, DelimSpan, TokenStream}; -use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; +use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId, Safety}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; @@ -131,6 +131,7 @@ pub(super) enum MacroRule { Func { lhs: Vec, lhs_span: Span, rhs: mbe::TokenTree }, /// An attr rule, for use with `#[m]` Attr { + unsafe_rule: bool, args: Vec, args_span: Span, body: Vec, @@ -247,8 +248,19 @@ impl TTMacroExpander for MacroRulesMacroExpander { impl AttrProcMacro for MacroRulesMacroExpander { fn expand( + &self, + _cx: &mut ExtCtxt<'_>, + _sp: Span, + _args: TokenStream, + _body: TokenStream, + ) -> Result { + unreachable!("`expand` called on `MacroRulesMacroExpander`, expected `expand_with_safety`") + } + + fn expand_with_safety( &self, cx: &mut ExtCtxt<'_>, + safety: Safety, sp: Span, args: TokenStream, body: TokenStream, @@ -260,6 +272,7 @@ impl AttrProcMacro for MacroRulesMacroExpander { self.node_id, self.name, self.transparency, + safety, args, body, &self.rules, @@ -408,6 +421,7 @@ fn expand_macro_attr( node_id: NodeId, name: Ident, transparency: Transparency, + safety: Safety, args: TokenStream, body: TokenStream, rules: &[MacroRule], @@ -429,13 +443,26 @@ fn expand_macro_attr( // Track nothing for the best performance. match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) { Ok((i, rule, named_matches)) => { - let MacroRule::Attr { rhs, .. } = rule else { + let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else { panic!("try_macro_match_attr returned non-attr rule"); }; let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { cx.dcx().span_bug(sp, "malformed macro rhs"); }; + match (safety, unsafe_rule) { + (Safety::Default, false) | (Safety::Unsafe(_), true) => {} + (Safety::Default, true) => { + cx.dcx().span_err(sp, "unsafe attribute invocation requires `unsafe`"); + } + (Safety::Unsafe(span), false) => { + cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute invocation"); + } + (Safety::Safe(span), _) => { + cx.dcx().span_bug(span, "unexpected `safe` keyword"); + } + } + let id = cx.current_expansion.id; let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) .map_err(|e| e.emit())?; @@ -681,6 +708,11 @@ pub fn compile_declarative_macro( let mut rules = Vec::new(); while p.token != token::Eof { + let unsafe_rule = p.eat_keyword_noexpect(kw::Unsafe); + let unsafe_keyword_span = p.prev_token.span; + if unsafe_rule && let Some(guar) = check_no_eof(sess, &p, "expected `attr`") { + return dummy_syn_ext(guar); + } let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) { kinds |= MacroKinds::ATTR; if !features.macro_attr() { @@ -705,6 +737,10 @@ pub fn compile_declarative_macro( feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable") .emit(); } + if unsafe_rule { + sess.dcx() + .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules"); + } if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { return dummy_syn_ext(guar); } @@ -730,6 +766,10 @@ pub fn compile_declarative_macro( (None, true) } else { kinds |= MacroKinds::BANG; + if unsafe_rule { + sess.dcx() + .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules"); + } (None, false) }; let lhs_tt = p.parse_token_tree(); @@ -760,7 +800,7 @@ pub fn compile_declarative_macro( }; let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; - rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs }); + rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs }); } else if is_derive { rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs }); } else { diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs index 894d1327da799..c0181d960539c 100644 --- a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs @@ -1,7 +1,7 @@ #[unsafe(unsafe(no_mangle))] //~^ ERROR expected identifier, found keyword `unsafe` //~| ERROR cannot find attribute `r#unsafe` in this scope -//~| ERROR `r#unsafe` is not an unsafe attribute +//~| ERROR unnecessary `unsafe` fn a() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr index 0825cf794083d..846800daa5465 100644 --- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr @@ -9,13 +9,11 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(r#unsafe(no_mangle))] | ++ -error: `r#unsafe` is not an unsafe attribute +error: unnecessary `unsafe` on safe attribute --> $DIR/double-unsafe-attributes.rs:1:3 | LL | #[unsafe(unsafe(no_mangle))] - | ^^^^^^ this is not an unsafe attribute - | - = note: extraneous unsafe is not allowed in attributes + | ^^^^^^ error: cannot find attribute `r#unsafe` in this scope --> $DIR/double-unsafe-attributes.rs:1:10 diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs index 0f241cc439f34..d9054248a292c 100644 --- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs @@ -1,4 +1,4 @@ -#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute +#[unsafe(diagnostic::on_unimplemented( //~ ERROR: unnecessary `unsafe` message = "testing", ))] trait Foo {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr index 3bc291db5acf8..a7662f5ee6c7d 100644 --- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr @@ -1,10 +1,8 @@ -error: `diagnostic::on_unimplemented` is not an unsafe attribute +error: unnecessary `unsafe` on safe attribute --> $DIR/unsafe-safe-attribute_diagnostic.rs:1:3 | LL | #[unsafe(diagnostic::on_unimplemented( - | ^^^^^^ this is not an unsafe attribute - | - = note: extraneous unsafe is not allowed in attributes + | ^^^^^^ error: aborting due to 1 previous error From 05c5b8779704e20620e18c822a18f28e4961e86e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 14 Sep 2025 16:23:39 +0800 Subject: [PATCH 1144/1900] mbe: Add parsing tests for `unsafe` macro rules --- tests/ui/parser/macro/bad-macro-definition.rs | 3 +++ .../parser/macro/bad-macro-definition.stderr | 8 +++++- tests/ui/parser/macro/macro-attr-bad.rs | 6 +++++ tests/ui/parser/macro/macro-attr-bad.stderr | 26 ++++++++++++++----- tests/ui/parser/macro/macro-derive-bad.rs | 3 +++ tests/ui/parser/macro/macro-derive-bad.stderr | 8 +++++- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/tests/ui/parser/macro/bad-macro-definition.rs b/tests/ui/parser/macro/bad-macro-definition.rs index 3c5c93ea3b3e4..12df6e64bd2ca 100644 --- a/tests/ui/parser/macro/bad-macro-definition.rs +++ b/tests/ui/parser/macro/bad-macro-definition.rs @@ -20,3 +20,6 @@ macro_rules! e { {} } macro_rules! f {} //~^ ERROR: macros must contain at least one rule + +macro_rules! g { unsafe {} => {} } +//~^ ERROR: `unsafe` is only supported on `attr` rules diff --git a/tests/ui/parser/macro/bad-macro-definition.stderr b/tests/ui/parser/macro/bad-macro-definition.stderr index de6d9d6a38b15..d15f33f708ded 100644 --- a/tests/ui/parser/macro/bad-macro-definition.stderr +++ b/tests/ui/parser/macro/bad-macro-definition.stderr @@ -52,5 +52,11 @@ error: macros must contain at least one rule LL | macro_rules! f {} | ^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: `unsafe` is only supported on `attr` rules + --> $DIR/bad-macro-definition.rs:24:18 + | +LL | macro_rules! g { unsafe {} => {} } + | ^^^^^^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/parser/macro/macro-attr-bad.rs b/tests/ui/parser/macro/macro-attr-bad.rs index 9f50b057a7a49..0ac46c8b76812 100644 --- a/tests/ui/parser/macro/macro-attr-bad.rs +++ b/tests/ui/parser/macro/macro-attr-bad.rs @@ -13,6 +13,12 @@ macro_rules! attr_incomplete_3 { attr() {} } macro_rules! attr_incomplete_4 { attr() {} => } //~^ ERROR macro definition ended unexpectedly +macro_rules! attr_incomplete_5 { unsafe } +//~^ ERROR macro definition ended unexpectedly + +macro_rules! attr_incomplete_6 { unsafe attr } +//~^ ERROR macro definition ended unexpectedly + macro_rules! attr_noparens_1 { attr{} {} => {} } //~^ ERROR `attr` rule argument matchers require parentheses diff --git a/tests/ui/parser/macro/macro-attr-bad.stderr b/tests/ui/parser/macro/macro-attr-bad.stderr index bf0ed13cd553f..481ef8118aebb 100644 --- a/tests/ui/parser/macro/macro-attr-bad.stderr +++ b/tests/ui/parser/macro/macro-attr-bad.stderr @@ -22,8 +22,20 @@ error: macro definition ended unexpectedly LL | macro_rules! attr_incomplete_4 { attr() {} => } | ^ expected right-hand side of macro rule +error: macro definition ended unexpectedly + --> $DIR/macro-attr-bad.rs:16:40 + | +LL | macro_rules! attr_incomplete_5 { unsafe } + | ^ expected `attr` + +error: macro definition ended unexpectedly + --> $DIR/macro-attr-bad.rs:19:45 + | +LL | macro_rules! attr_incomplete_6 { unsafe attr } + | ^ expected macro attr args + error: `attr` rule argument matchers require parentheses - --> $DIR/macro-attr-bad.rs:16:36 + --> $DIR/macro-attr-bad.rs:22:36 | LL | macro_rules! attr_noparens_1 { attr{} {} => {} } | ^^ @@ -35,7 +47,7 @@ LL + macro_rules! attr_noparens_1 { attr() {} => {} } | error: `attr` rule argument matchers require parentheses - --> $DIR/macro-attr-bad.rs:19:36 + --> $DIR/macro-attr-bad.rs:25:36 | LL | macro_rules! attr_noparens_2 { attr[] {} => {} } | ^^ @@ -47,13 +59,13 @@ LL + macro_rules! attr_noparens_2 { attr() {} => {} } | error: invalid macro matcher; matchers must be contained in balanced delimiters - --> $DIR/macro-attr-bad.rs:22:37 + --> $DIR/macro-attr-bad.rs:28:37 | LL | macro_rules! attr_noparens_3 { attr _ {} => {} } | ^ error: duplicate matcher binding - --> $DIR/macro-attr-bad.rs:25:52 + --> $DIR/macro-attr-bad.rs:31:52 | LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} } | -------- ^^^^^^^^ duplicate binding @@ -61,7 +73,7 @@ LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} } | previous binding error: duplicate matcher binding - --> $DIR/macro-attr-bad.rs:28:49 + --> $DIR/macro-attr-bad.rs:34:49 | LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} } | -------- ^^^^^^^^ duplicate binding @@ -69,12 +81,12 @@ LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} } | previous binding error: duplicate matcher binding - --> $DIR/macro-attr-bad.rs:31:51 + --> $DIR/macro-attr-bad.rs:37:51 | LL | macro_rules! attr_dup_matcher_3 { attr($x:ident) {$x:ident} => {} } | -------- ^^^^^^^^ duplicate binding | | | previous binding -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/parser/macro/macro-derive-bad.rs b/tests/ui/parser/macro/macro-derive-bad.rs index 79b9eb8c113ca..74e7d9acdafc3 100644 --- a/tests/ui/parser/macro/macro-derive-bad.rs +++ b/tests/ui/parser/macro/macro-derive-bad.rs @@ -41,3 +41,6 @@ macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} } //~^ ERROR duplicate matcher binding //~| NOTE duplicate binding //~| NOTE previous binding + +macro_rules! derive_unsafe { unsafe derive() {} => {} } +//~^ ERROR `unsafe` is only supported on `attr` rules diff --git a/tests/ui/parser/macro/macro-derive-bad.stderr b/tests/ui/parser/macro/macro-derive-bad.stderr index ec750c9ac8220..c98535f4031f3 100644 --- a/tests/ui/parser/macro/macro-derive-bad.stderr +++ b/tests/ui/parser/macro/macro-derive-bad.stderr @@ -86,5 +86,11 @@ LL | macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} } | | | previous binding -error: aborting due to 12 previous errors +error: `unsafe` is only supported on `attr` rules + --> $DIR/macro-derive-bad.rs:45:30 + | +LL | macro_rules! derive_unsafe { unsafe derive() {} => {} } + | ^^^^^^ + +error: aborting due to 13 previous errors From ea0e00c5738f56970ad3ed3cc3d27c19715cd9ea Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 14 Sep 2025 16:28:50 +0800 Subject: [PATCH 1145/1900] mbe: Add tests for `unsafe` attr invocation --- tests/ui/macros/macro-rules-attr-error.rs | 19 +++++++++++++++++++ tests/ui/macros/macro-rules-attr-error.stderr | 14 +++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/ui/macros/macro-rules-attr-error.rs b/tests/ui/macros/macro-rules-attr-error.rs index 81eadb6692f22..60290b883cb86 100644 --- a/tests/ui/macros/macro-rules-attr-error.rs +++ b/tests/ui/macros/macro-rules-attr-error.rs @@ -50,3 +50,22 @@ macro_rules! forward_referenced_attr { macro_rules! cyclic_attr { attr() {} => {} } + +macro_rules! attr_with_safety { + unsafe attr() { struct RequiresUnsafe; } => {}; + attr() { struct SafeInvocation; } => {}; +} + +#[attr_with_safety] +struct SafeInvocation; + +//~v ERROR: unnecessary `unsafe` on safe attribute invocation +#[unsafe(attr_with_safety)] +struct SafeInvocation; + +//~v ERROR: unsafe attribute invocation requires `unsafe` +#[attr_with_safety] +struct RequiresUnsafe; + +#[unsafe(attr_with_safety)] +struct RequiresUnsafe; diff --git a/tests/ui/macros/macro-rules-attr-error.stderr b/tests/ui/macros/macro-rules-attr-error.stderr index 674d35091b68d..27527a2da7ef2 100644 --- a/tests/ui/macros/macro-rules-attr-error.stderr +++ b/tests/ui/macros/macro-rules-attr-error.stderr @@ -9,6 +9,18 @@ LL | #[local_attr] | = note: this error originates in the attribute macro `local_attr` (in Nightly builds, run with -Z macro-backtrace for more info) +error: unnecessary `unsafe` on safe attribute invocation + --> $DIR/macro-rules-attr-error.rs:63:3 + | +LL | #[unsafe(attr_with_safety)] + | ^^^^^^ + +error: unsafe attribute invocation requires `unsafe` + --> $DIR/macro-rules-attr-error.rs:67:1 + | +LL | #[attr_with_safety] + | ^^^^^^^^^^^^^^^^^^^ + error: cannot find macro `local_attr` in this scope --> $DIR/macro-rules-attr-error.rs:27:5 | @@ -59,5 +71,5 @@ note: a macro with the same name exists, but it appears later LL | macro_rules! cyclic_attr { | ^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors From 4fc0a0d42a66e9b248fa527f7f063ec7226803dd Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 28 Sep 2025 09:15:02 -0700 Subject: [PATCH 1146/1900] mbe: `expand_invoc`: Add comment about not needing to check safety of `LegacyAttr` here `LegacyAttr` is only used for builtin attributes, and builtin attributes have their safety checked by `check_attribute_safety`, so we don't need to check `unsafety` here. --- compiler/rustc_expand/src/expand.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a035896d5542c..3dfa3cdcc3560 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -841,6 +841,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext { + // `LegacyAttr` is only used for builtin attribute macros, which have their + // safety checked by `check_builtin_meta_item`, so we don't need to check + // `unsafety` here. match validate_attr::parse_meta(&self.cx.sess.psess, &attr) { Ok(meta) => { let item_clone = macro_stats.then(|| item.clone()); From 3e26e2a3a9f047060d7c098c3c82a77d7c33c2aa Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Wed, 24 Sep 2025 15:09:16 -0700 Subject: [PATCH 1147/1900] Don't unconditionally build alloc for `no-std` targets It's possible for targets to only support `core` and not `alloc`. Instead of building alloc unconditionally, pass a list of crates to build into `std_cargo`, and only pass `-p alloc` if the list of crates wasn't already filtered to a subset. The original use case was to reuse `std_cargo` for a rustc_driver that doesn't emit metadata. But this seems like a reasonable change regardless. --- src/bootstrap/src/core/build_steps/check.rs | 18 +++++------------ src/bootstrap/src/core/build_steps/clippy.rs | 6 +----- src/bootstrap/src/core/build_steps/compile.rs | 20 +++++++++++++------ src/bootstrap/src/core/build_steps/doc.rs | 6 +----- src/bootstrap/src/core/build_steps/test.rs | 2 +- 5 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 20c54a28dda83..d0edf6c90899c 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -61,6 +61,9 @@ impl Step for Std { return; } + // Explicitly pass -p for all dependencies crates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. let crates = std_crates_for_run_make(&run); run.builder.ensure(Std { build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std) @@ -83,16 +86,12 @@ impl Step for Std { Kind::Check, ); - std_cargo(builder, target, &mut cargo); + std_cargo(builder, target, &mut cargo, &self.crates); if matches!(builder.config.cmd, Subcommand::Fix) { // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot. cargo.arg("--lib"); } - for krate in &*self.crates { - cargo.arg("-p").arg(krate); - } - let _guard = builder.msg( Kind::Check, format_args!("library artifacts{}", crate_description(&self.crates)), @@ -135,14 +134,7 @@ impl Step for Std { Kind::Check, ); - std_cargo(builder, target, &mut cargo); - - // Explicitly pass -p for all dependencies krates -- this will force cargo - // to also check the tests/benches/examples for these crates, rather - // than just the leaf crate. - for krate in &*self.crates { - cargo.arg("-p").arg(krate); - } + std_cargo(builder, target, &mut cargo, &self.crates); let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test"); diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index d5b15d7908646..290a00aa3c13a 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -195,11 +195,7 @@ impl Step for Std { Kind::Clippy, ); - std_cargo(builder, target, &mut cargo); - - for krate in &*self.crates { - cargo.arg("-p").arg(krate); - } + std_cargo(builder, target, &mut cargo, &self.crates); let _guard = builder.msg( Kind::Clippy, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 96b4e15433f7e..03046b1126beb 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -266,10 +266,7 @@ impl Step for Std { target, Kind::Build, ); - std_cargo(builder, target, &mut cargo); - for krate in &*self.crates { - cargo.arg("-p").arg(krate); - } + std_cargo(builder, target, &mut cargo, &self.crates); cargo }; @@ -497,7 +494,12 @@ fn compiler_rt_for_profiler(builder: &Builder<'_>) -> PathBuf { /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. -pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Cargo) { +pub fn std_cargo( + builder: &Builder<'_>, + target: TargetSelection, + cargo: &mut Cargo, + crates: &[String], +) { // rustc already ensures that it builds with the minimum deployment // target, so ideally we shouldn't need to do anything here. // @@ -605,6 +607,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1"); } + for krate in crates { + cargo.args(["-p", krate]); + } + let mut features = String::new(); if builder.no_std(target) == Some(true) { @@ -614,8 +620,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car } // for no-std targets we only compile a few no_std crates + if crates.is_empty() { + cargo.args(["-p", "alloc"]); + } cargo - .args(["-p", "alloc"]) .arg("--manifest-path") .arg(builder.src.join("library/alloc/Cargo.toml")) .arg("--features") diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 7865b68565930..7fb0fe17eb0cf 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -783,7 +783,7 @@ fn doc_std( Kind::Doc, ); - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, &mut cargo, requested_crates); cargo .arg("--no-deps") .arg("--target-dir") @@ -803,10 +803,6 @@ fn doc_std( cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items"); } - for krate in requested_crates { - cargo.arg("-p").arg(krate); - } - let description = format!("library{} in {} format", crate_description(requested_crates), format.as_str()); let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 00aea8feab7de..2910de5e29ef7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2963,7 +2963,7 @@ impl Step for Crate { .arg("--manifest-path") .arg(builder.src.join("library/sysroot/Cargo.toml")); } else { - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, &mut cargo, &[]); } } Mode::Rustc => { From 59c4dfe59d587f0f09746958bf9a16c271cc02a7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 1 Oct 2025 21:45:50 +1000 Subject: [PATCH 1148/1900] Forbid `//@ compile-flags: -Cincremental=` in tests Tests should not try to manually enable incremental compilation with `-Cincremental`, because that typically results in stray directories being created in the repository root. Instead, use the `//@ incremental` directive, which instructs compiletest to handle the details of passing `-Cincremental` with a fresh directory. --- src/tools/compiletest/src/directives.rs | 10 +++++++++- .../compile-flags-incremental.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/ui/compiletest-self-test/compile-flags-incremental.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index e6916610190e6..0d85e76c2cc09 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -415,10 +415,18 @@ impl TestProps { config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile) { let flags = split_flags(&flags); - for flag in &flags { + for (i, flag) in flags.iter().enumerate() { if flag == "--edition" || flag.starts_with("--edition=") { panic!("you must use `//@ edition` to configure the edition"); } + if (flag == "-C" + && flags.get(i + 1).is_some_and(|v| v.starts_with("incremental="))) + || flag.starts_with("-Cincremental=") + { + panic!( + "you must use `//@ incremental` to enable incremental compilation" + ); + } } self.compile_flags.extend(flags); } diff --git a/tests/ui/compiletest-self-test/compile-flags-incremental.rs b/tests/ui/compiletest-self-test/compile-flags-incremental.rs new file mode 100644 index 0000000000000..62a1ad84d8f7d --- /dev/null +++ b/tests/ui/compiletest-self-test/compile-flags-incremental.rs @@ -0,0 +1,17 @@ +//@ revisions: good bad bad-space +//@ check-pass + +//@[bad] compile-flags: -Cincremental=true +//@[bad] should-fail + +//@[bad-space] compile-flags: -C incremental=dir +//@[bad-space] should-fail + +fn main() {} + +// Tests should not try to manually enable incremental compilation with +// `-Cincremental`, because that typically results in stray directories being +// created in the repository root. +// +// Instead, use the `//@ incremental` directive, which instructs compiletest +// to handle the details of passing `-Cincremental` with a fresh directory. From 0ea569f7ae45464970a83dde09cd64ea6f247608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=2E=20Martins?= Date: Thu, 2 Oct 2025 13:33:53 +1000 Subject: [PATCH 1149/1900] fixes small typos in docs and indent issue --- .../docs/book/src/configuration.md | 2 +- .../docs/book/src/contributing/guide.md | 2 +- .../book/src/contributing/lsp-extensions.md | 22 +++++++++---------- .../docs/book/src/contributing/syntax.md | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md index fd94a4221a927..3f4cb197141fe 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration.md @@ -13,7 +13,7 @@ Vim](./other_editors.md#coc-rust-analyzer) provide `rust-analyzer` specific conf UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`. -For the later category, it might help to know that the initial +For the latter category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the [`InitializeParams` message, in the LSP protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize). diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/guide.md b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md index 2a2a39af2641c..d06840368ccbf 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/guide.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md @@ -235,7 +235,7 @@ of type `V`. Queries come in two basic varieties: intelligently) when we can re-use these memoized values and when we have to recompute them. -For further discussion, its important to understand one bit of "fairly +For further discussion, it's important to understand one bit of "fairly intelligently". Suppose we have two functions, `f1` and `f2`, and one input, `z`. We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)` returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that and diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index 8c06f33a9f7bf..91154b6f1c87d 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -455,7 +455,7 @@ interface TestItem { // A human readable name for this test label: string; // The kind of this test item. Based on the kind, - // an icon is chosen by the editor. + // an icon is chosen by the editor. kind: "package" | "module" | "test"; // True if this test may have children not available eagerly canResolveChildren: boolean; @@ -467,7 +467,7 @@ interface TestItem { // like debugging, this field is useful. // Note that this field includes some information about label and location as well, but // those exist just for keeping things in sync with other methods of running runnables - // (for example using one consistent name in the vscode's launch.json) so for any propose + // (for example using one consistent name in the vscode's launch.json) so for any purpose // other than running tests this field should not be used. runnable?: Runnable | undefined; }; @@ -475,11 +475,11 @@ interface TestItem { interface DiscoverTestResults { // The discovered tests. tests: TestItem[]; - // For each test which its id is in this list, the response + // For each test whose id is in this list, the response // contains all tests that are children of this test, and // client should remove old tests not included in the response. scope: string[] | undefined; - // For each file which its uri is in this list, the response + // For each file whose uri is in this list, the response // contains all tests that are located in this file, and // client should remove old tests not included in the response. scopeFile: lc.TextDocumentIdentifier[] | undefined; @@ -491,7 +491,7 @@ interface DiscoverTestResults { **Notification:** `DiscoverTestResults` This notification is sent from the server to the client when the -server detect changes in the existing tests. The `DiscoverTestResults` is +server detects changes in the existing tests. The `DiscoverTestResults` is the same as the one in `experimental/discoverTest` response. **Method:** `experimental/runTest` @@ -527,7 +527,7 @@ after this. This notification is sent from the client to the server when the user is no longer interested in the test results. The server should clean up its resources and send -a `experimental/endRunTest` when is done. +a `experimental/endRunTest` when it is done. **Method:** `experimental/changeTestState` @@ -648,8 +648,8 @@ interface ServerStatusParams { ``` This notification is sent from server to client. -The client can use it to display *persistent* status to the user (in modline). -It is similar to the `showMessage`, but is intended for stares rather than point-in-time events. +The client can use it to display *persistent* status to the user (in the mode line). +It is similar to the `showMessage`, but is intended for status rather than point-in-time events. Note that this functionality is intended primarily to inform the end user about the state of the server. In particular, it's valid for the client to completely ignore this extension. @@ -1070,13 +1070,13 @@ export interface RecursiveMemoryLayoutNode = { size: number; /// Alignment of the type in bytes alignment: number; - /// Offset of the type relative to its parent (or 0 if its the root) + /// Offset of the type relative to its parent (or 0 if it's the root) offset: number; - /// Index of the node's parent (or -1 if its the root) + /// Index of the node's parent (or -1 if it's the root) parent_idx: number; /// Index of the node's children (or -1 if it does not have children) children_start: number; - /// Number of child nodes (unspecified it does not have children) + /// Number of child nodes (unspecified if it does not have children) children_len: number; }; diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md b/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md index 3dcd430cea5d5..93eed7228d05f 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md @@ -192,7 +192,7 @@ Modeling this with immutable trees is possible, but annoying. A function green tree is not super-convenient to use. The biggest problem is accessing parents (there are no parent pointers!). -But there are also "identify" issues. +But there are also "identity" issues. Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet`. For the input like From 1c234917634f3522b3a44551c49095dcadf04f8d Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Wed, 1 Oct 2025 22:41:01 -0500 Subject: [PATCH 1150/1900] Fix two small things clippy was complaining about --- .../ide-completion/src/completions/attribute/cfg.rs | 2 +- src/tools/rust-analyzer/crates/ide-db/src/assists.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index b2e8efde8bebc..b6739c9f75a28 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -39,7 +39,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { "target_os" => KNOWN_OS.iter().copied().for_each(add_completion), "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion), "target_endian" => ["little", "big"].into_iter().for_each(add_completion), - name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { + name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).for_each(|s| { let s = s.as_str(); let insert_text = format!(r#""{s}""#); let mut item = CompletionItem::new( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs index 384eb57c0fd59..12406efc39cee 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs @@ -170,14 +170,10 @@ impl AssistResolveStrategy { #[derive(Clone, Debug)] pub struct GroupLabel(pub String); -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub enum ExprFillDefaultMode { + #[default] Todo, Default, Underscore, } -impl Default for ExprFillDefaultMode { - fn default() -> Self { - Self::Todo - } -} From 4fdd9bf5804e9549e435ddca73932f25e3faad76 Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Wed, 1 Oct 2025 23:00:16 -0500 Subject: [PATCH 1151/1900] Get rid of unfulfilled expectation --- src/tools/rust-analyzer/crates/stdx/src/thread.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs index a34e9e4a65515..37b7a9f5edfa8 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs @@ -101,7 +101,6 @@ impl Drop for JoinHandle { } } -#[expect(clippy::min_ident_chars, reason = "trait impl")] impl fmt::Debug for JoinHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("JoinHandle { .. }") From eb2877b38064f79d889efde5f0b11e36bdc8d413 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 2 Oct 2025 04:07:01 +0000 Subject: [PATCH 1152/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 3369e82c6bc03c5cdb66f730dba6f738b74c8e1d. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 1f90d4e5e498a..5e2e5d7d335d0 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -f957826bff7a68b267ce75b1ea56352aed0cca0a +3369e82c6bc03c5cdb66f730dba6f738b74c8e1d From 306527dac25eddb4dd2138a6fad9a1c8e57fa1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 2 Oct 2025 07:16:40 +0300 Subject: [PATCH 1153/1900] Format code --- src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 2c31ba1ec3331..e1983b37472ce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2136,9 +2136,7 @@ impl<'db> Evaluator<'db> { return Ok(Some((0, 1))); } let layout = layout?; - Ok(layout - .is_sized() - .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))) + Ok(layout.is_sized().then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))) } /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should From 571412f8190089c36758031fe09fc0ece59be6b7 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 8 Jun 2025 15:30:09 +0800 Subject: [PATCH 1154/1900] mir-opt: Eliminate dead ref statements --- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 4 + compiler/rustc_middle/src/mir/mod.rs | 45 ++- compiler/rustc_middle/src/mir/pretty.rs | 19 + compiler/rustc_middle/src/mir/statement.rs | 103 +++++- compiler/rustc_middle/src/mir/visit.rs | 40 ++- compiler/rustc_mir_dataflow/src/debuginfo.rs | 10 +- .../rustc_mir_dataflow/src/impls/liveness.rs | 78 ++-- .../src/cleanup_post_borrowck.rs | 4 +- compiler/rustc_mir_transform/src/copy_prop.rs | 4 +- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- .../src/dead_store_elimination.rs | 55 ++- compiler/rustc_mir_transform/src/dest_prop.rs | 4 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../rustc_mir_transform/src/large_enums.rs | 2 +- compiler/rustc_mir_transform/src/patch.rs | 2 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/ref_prop.rs | 2 +- .../src/remove_place_mention.rs | 2 +- .../src/remove_storage_markers.rs | 2 +- .../rustc_mir_transform/src/remove_zsts.rs | 2 +- compiler/rustc_mir_transform/src/simplify.rs | 29 +- .../src/simplify_comparison_integral.rs | 4 +- compiler/rustc_mir_transform/src/sroa.rs | 10 +- tests/codegen-llvm/debug-fndef-size.rs | 4 +- ...ad_first.DeadStoreElimination-initial.diff | 31 ++ tests/mir-opt/dead-store-elimination/ref.rs | 33 ++ ...ef.tuple.DeadStoreElimination-initial.diff | 26 ++ ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 26 ++ ...reserve_debuginfo_1.SimplifyCfg-final.diff | 30 ++ ...reserve_debuginfo_2.SimplifyCfg-final.diff | 29 ++ .../inline_shims.drop.Inline.panic-abort.diff | 56 +-- ...y.run2-{closure#0}.Inline.panic-abort.diff | 6 +- ....run2-{closure#0}.Inline.panic-unwind.diff | 6 +- ...implifyComparisonIntegral.panic-abort.diff | 6 +- ...mplifyComparisonIntegral.panic-unwind.diff | 6 +- ...git.PreCodegen.after.32bit.panic-abort.mir | 4 + ...it.PreCodegen.after.32bit.panic-unwind.mir | 4 + ...git.PreCodegen.after.64bit.panic-abort.mir | 4 + ...it.PreCodegen.after.64bit.panic-unwind.mir | 4 + ...p_forward.PreCodegen.after.panic-abort.mir | 40 ++- ..._forward.PreCodegen.after.panic-unwind.mir | 40 ++- ...as_copy.clone_as_copy.PreCodegen.after.mir | 2 +- ...py.enum_clone_as_copy.PreCodegen.after.mir | 6 +- ...ace.PreCodegen.after.32bit.panic-abort.mir | 38 +- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 38 +- ...ace.PreCodegen.after.64bit.panic-abort.mir | 38 +- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 38 +- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- .../loops.filter_mapped.PreCodegen.after.mir | 44 +-- .../loops.int_range.PreCodegen.after.mir | 100 +++--- .../loops.mapped.PreCodegen.after.mir | 84 ++--- tests/mir-opt/pre-codegen/loops.rs | 1 + .../loops.vec_move.PreCodegen.after.mir | 340 ++++++++++++++++-- ...variant_a-{closure#0}.PreCodegen.after.mir | 199 +++++----- ...ated_loop.PreCodegen.after.panic-abort.mir | 14 + ...ward_loop.PreCodegen.after.panic-abort.mir | 14 + ...ard_loop.PreCodegen.after.panic-unwind.mir | 14 + ...erse_loop.PreCodegen.after.panic-abort.mir | 223 ++++++++++-- ...rse_loop.PreCodegen.after.panic-unwind.mir | 231 ++++++++++-- ..._is_empty.PreCodegen.after.panic-abort.mir | 8 + ...is_empty.PreCodegen.after.panic-unwind.mir | 8 + ...next_back.PreCodegen.after.panic-abort.mir | 189 +++++++++- ...ext_back.PreCodegen.after.panic-unwind.mir | 189 +++++++++- ...iter_next.PreCodegen.after.panic-abort.mir | 14 + ...ter_next.PreCodegen.after.panic-unwind.mir | 14 + ...ans.outer.PreCodegen.after.panic-abort.mir | 2 +- ...ns.outer.PreCodegen.after.panic-unwind.mir | 2 +- ..._to_slice.PreCodegen.after.panic-abort.mir | 8 + ...to_slice.PreCodegen.after.panic-unwind.mir | 8 + 69 files changed, 2139 insertions(+), 511 deletions(-) create mode 100644 tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff create mode 100644 tests/mir-opt/dead-store-elimination/ref.rs create mode 100644 tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 45bc545194682..0a37a904193fa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -260,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(), } } + + fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) { + // Debuginfo does not generate actual code. + } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 28142382b130b..8eb7aa71fcdea 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1298,6 +1298,10 @@ pub struct BasicBlockData<'tcx> { /// List of statements in this block. pub statements: Vec>, + /// All debuginfos happen before the statement. + /// Put debuginfos here when the last statement is eliminated. + pub after_last_stmt_debuginfos: StmtDebugInfos<'tcx>, + /// Terminator for this block. /// /// N.B., this should generally ONLY be `None` during construction. @@ -1325,7 +1329,12 @@ impl<'tcx> BasicBlockData<'tcx> { terminator: Option>, is_cleanup: bool, ) -> BasicBlockData<'tcx> { - BasicBlockData { statements, terminator, is_cleanup } + BasicBlockData { + statements, + after_last_stmt_debuginfos: StmtDebugInfos::default(), + terminator, + is_cleanup, + } } /// Accessor for terminator. @@ -1360,6 +1369,36 @@ impl<'tcx> BasicBlockData<'tcx> { self.terminator().successors() } } + + pub fn retain_statements(&mut self, mut f: F) + where + F: FnMut(&Statement<'tcx>) -> bool, + { + // Place debuginfos into the next retained statement, + // this `debuginfos` variable is used to cache debuginfos between two retained statements. + let mut debuginfos = StmtDebugInfos::default(); + self.statements.retain_mut(|stmt| { + let retain = f(stmt); + if retain { + stmt.debuginfos.prepend(&mut debuginfos); + } else { + debuginfos.append(&mut stmt.debuginfos); + } + retain + }); + self.after_last_stmt_debuginfos.prepend(&mut debuginfos); + } + + pub fn strip_nops(&mut self) { + self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + } + + pub fn drop_debuginfo(&mut self) { + self.after_last_stmt_debuginfos.drop_debuginfo(); + for stmt in self.statements.iter_mut() { + stmt.debuginfos.drop_debuginfo(); + } + } } /////////////////////////////////////////////////////////////////////////// @@ -1664,10 +1703,10 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(BasicBlockData<'_>, 128); + static_assert_size!(BasicBlockData<'_>, 152); static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(SourceScopeData<'_>, 64); - static_assert_size!(Statement<'_>, 32); + static_assert_size!(Statement<'_>, 56); static_assert_size!(Terminator<'_>, 96); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 350d75c2ee772..8cf89f778a2aa 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -719,6 +719,11 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> { let mut current_location = Location { block, statement_index: 0 }; for statement in &data.statements { (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; + + for debuginfo in statement.debuginfos.iter() { + writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?; + } + let indented_body = format!("{INDENT}{INDENT}{statement:?};"); if self.options.include_extra_comments { writeln!( @@ -749,6 +754,10 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> { current_location.statement_index += 1; } + for debuginfo in data.after_last_stmt_debuginfos.iter() { + writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?; + } + // Terminator at the bottom. (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; if data.terminator.is_some() { @@ -829,6 +838,16 @@ impl Debug for Statement<'_> { } } +impl Debug for StmtDebugInfo<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self { + StmtDebugInfo::AssignRef(local, place) => { + write!(fmt, "{local:?} = &{place:?}") + } + } + } +} + impl Display for NonDivergingIntrinsic<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index e009fe05b53e6..9deb43eb7084b 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -1,5 +1,7 @@ //! Functionality for statements, operands, places, and things that appear in them. +use std::ops; + use tracing::{debug, instrument}; use super::interpret::GlobalAlloc; @@ -15,17 +17,34 @@ use crate::ty::CoroutineArgsExt; pub struct Statement<'tcx> { pub source_info: SourceInfo, pub kind: StatementKind<'tcx>, + /// Some debuginfos appearing before the primary statement. + pub debuginfos: StmtDebugInfos<'tcx>, } impl<'tcx> Statement<'tcx> { /// Changes a statement to a nop. This is both faster than deleting instructions and avoids /// invalidating statement indices in `Location`s. - pub fn make_nop(&mut self) { - self.kind = StatementKind::Nop + pub fn make_nop(&mut self, drop_debuginfo: bool) { + if matches!(self.kind, StatementKind::Nop) { + return; + } + let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop); + if !drop_debuginfo { + match replaced_stmt { + StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) + if let Some(local) = place.as_local() => + { + self.debuginfos.push(StmtDebugInfo::AssignRef(local, ref_place)); + } + _ => { + bug!("debuginfo is not yet supported.") + } + } + } } pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self { - Statement { source_info, kind } + Statement { source_info, kind, debuginfos: StmtDebugInfos::default() } } } @@ -63,6 +82,17 @@ impl<'tcx> StatementKind<'tcx> { _ => None, } } + + pub fn as_debuginfo(&self) -> Option> { + match self { + StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) + if let Some(local) = place.as_local() => + { + Some(StmtDebugInfo::AssignRef(local, *ref_place)) + } + _ => None, + } + } } /////////////////////////////////////////////////////////////////////////// @@ -967,3 +997,70 @@ impl RawPtrKind { } } } + +#[derive(Default, Debug, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub struct StmtDebugInfos<'tcx>(Vec>); + +impl<'tcx> StmtDebugInfos<'tcx> { + pub fn push(&mut self, debuginfo: StmtDebugInfo<'tcx>) { + self.0.push(debuginfo); + } + + pub fn drop_debuginfo(&mut self) { + self.0.clear(); + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn prepend(&mut self, debuginfos: &mut Self) { + if debuginfos.is_empty() { + return; + }; + debuginfos.0.append(self); + std::mem::swap(debuginfos, self); + } + + pub fn append(&mut self, debuginfos: &mut Self) { + if debuginfos.is_empty() { + return; + }; + self.0.append(debuginfos); + } + + pub fn extend(&mut self, debuginfos: &Self) { + if debuginfos.is_empty() { + return; + }; + self.0.extend_from_slice(debuginfos); + } + + pub fn retain(&mut self, f: F) + where + F: FnMut(&StmtDebugInfo<'tcx>) -> bool, + { + self.0.retain(f); + } +} + +impl<'tcx> ops::Deref for StmtDebugInfos<'tcx> { + type Target = Vec>; + + #[inline] + fn deref(&self) -> &Vec> { + &self.0 + } +} + +impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> { + #[inline] + fn deref_mut(&mut self) -> &mut Vec> { + &mut self.0 + } +} + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum StmtDebugInfo<'tcx> { + AssignRef(Local, Place<'tcx>), +} diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index f392347780051..47ae23afd556e 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -95,6 +95,14 @@ macro_rules! make_mir_visitor { self.super_source_scope_data(scope_data); } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, + location: Location + ) { + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_statement( &mut self, statement: & $($mutability)? Statement<'tcx>, @@ -301,6 +309,7 @@ macro_rules! make_mir_visitor { { let BasicBlockData { statements, + after_last_stmt_debuginfos, terminator, is_cleanup: _ } = data; @@ -312,8 +321,11 @@ macro_rules! make_mir_visitor { index += 1; } + let location = Location { block, statement_index: index }; + for debuginfo in after_last_stmt_debuginfos as & $($mutability)? [_] { + self.visit_statement_debuginfo(debuginfo, location); + } if let Some(terminator) = terminator { - let location = Location { block, statement_index: index }; self.visit_terminator(terminator, location); } } @@ -376,14 +388,38 @@ macro_rules! make_mir_visitor { } } + fn super_statement_debuginfo( + &mut self, + stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, + location: Location + ) { + match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, place) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + }, + } + } + fn super_statement( &mut self, statement: & $($mutability)? Statement<'tcx>, location: Location ) { - let Statement { source_info, kind } = statement; + let Statement { source_info, kind, debuginfos } = statement; self.visit_source_info(source_info); + for debuginfo in debuginfos as & $($mutability)? [_] { + self.visit_statement_debuginfo(debuginfo, location); + } match kind { StatementKind::Assign(box (place, rvalue)) => { self.visit_assign(place, rvalue, location); diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index 0d25ce91c9a9e..274c5943946c2 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -5,16 +5,16 @@ use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); - for debuginfo in body.var_debug_info.iter() { - visitor.visit_var_debug_info(debuginfo); - } + visitor.visit_body(body); visitor.0 } struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { - fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { - self.0.insert(local); + fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) { + if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) { + self.0.insert(local); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 5eba474a60c7a..24da4b0bea28a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -210,6 +210,7 @@ impl DefUse { /// All of the caveats of `MaybeLiveLocals` apply. pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a DenseBitSet, + debuginfo_locals: &'a DenseBitSet, } impl<'a> MaybeTransitiveLiveLocals<'a> { @@ -217,8 +218,46 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { /// considered live. /// /// This should include at least all locals that are ever borrowed. - pub fn new(always_live: &'a DenseBitSet) -> Self { - MaybeTransitiveLiveLocals { always_live } + pub fn new( + always_live: &'a DenseBitSet, + debuginfo_locals: &'a DenseBitSet, + ) -> Self { + MaybeTransitiveLiveLocals { always_live, debuginfo_locals } + } + + pub fn can_be_removed_if_dead<'tcx>( + stmt_kind: &StatementKind<'tcx>, + always_live: &DenseBitSet, + debuginfo_locals: &'a DenseBitSet, + ) -> Option> { + // Compute the place that we are storing to, if any + let destination = match stmt_kind { + StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove() + && (!debuginfo_locals.contains(place.local) + || (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..))))) + .then_some(*place), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + (!debuginfo_locals.contains(place.local)).then_some(**place) + } + StatementKind::FakeRead(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(..) + | StatementKind::AscribeUserType(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::Nop => None, + }; + if let Some(destination) = destination + && !destination.is_indirect() + && !always_live.contains(destination.local) + { + return Some(destination); + } + None } } @@ -243,32 +282,15 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { statement: &mir::Statement<'tcx>, location: Location, ) { - // Compute the place that we are storing to, if any - let destination = match &statement.kind { - StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0), - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { - Some(**place) - } - StatementKind::FakeRead(_) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Retag(..) - | StatementKind::AscribeUserType(..) - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::Intrinsic(..) - | StatementKind::ConstEvalCounter - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => None, - }; - if let Some(destination) = destination { - if !destination.is_indirect() - && !state.contains(destination.local) - && !self.always_live.contains(destination.local) - { - // This store is dead - return; - } + if let Some(destination) = + Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) + && !state.contains(destination.local) + // FIXME: We can eliminate the statement, but we'll need the statements it depends on + // for debuginfos. We need a way to handle this. + && !self.debuginfo_locals.contains(destination.local) + { + // This store is dead + return; } TransferFunction(state).visit_statement(statement, location); } diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 4be67b873f737..b0bf7f484bedf 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) | StatementKind::FakeRead(..) - | StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(), + | StatementKind::BackwardIncompatibleDropHint { .. } => { + statement.make_nop(true) + } StatementKind::Assign(box ( _, Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index cddeefca68174..f0bc286a94022 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -138,7 +138,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind && self.storage_to_remove.contains(l) { - stmt.make_nop(); + stmt.make_nop(true); return; } @@ -150,7 +150,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { *rhs && lhs == rhs { - stmt.make_nop(); + stmt.make_nop(true); } } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c5cd06f170c47..814eded910df5 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -411,7 +411,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind && self.remap.contains(l) { - s.make_nop(); + s.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index eea2b0990d730..a5f8a22e83c85 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -33,10 +33,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // If the user requests complete debuginfo, mark the locals that appear in it as live, so // we don't remove assignments to them. - let mut always_live = debuginfo_locals(body); - always_live.union(&borrowed_locals); + let debuginfo_locals = debuginfo_locals(body); - let mut live = MaybeTransitiveLiveLocals::new(&always_live) + let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -75,35 +74,23 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } 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 { - if !assign.1.is_safe_to_remove() { - continue; - } - } - match &statement.kind { - StatementKind::Assign(box (place, _)) - | StatementKind::SetDiscriminant { place: box place, .. } - | StatementKind::Deinit(box place) => { - if !place.is_indirect() && !always_live.contains(place.local) { - live.seek_before_primary_effect(loc); - if !live.get().contains(place.local) { - patch.push(loc); - } - } - } - StatementKind::Retag(_, _) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Coverage(_) - | StatementKind::Intrinsic(_) - | StatementKind::ConstEvalCounter - | StatementKind::PlaceMention(_) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => {} - - StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { - bug!("{:?} not found in this MIR phase!", statement.kind) + if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead( + &statement.kind, + &borrowed_locals, + &debuginfo_locals, + ) { + let loc = Location { block: bb, statement_index }; + live.seek_before_primary_effect(loc); + if !live.get().contains(destination.local) { + let drop_debuginfo = !debuginfo_locals.contains(destination.local); + // When eliminating a dead statement, we need to address + // the debug information for that statement. + assert!( + drop_debuginfo || statement.kind.as_debuginfo().is_some(), + "don't know how to retain the debug information for {:?}", + statement.kind + ); + patch.push((loc, drop_debuginfo)); } } } @@ -114,8 +101,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } let bbs = body.basic_blocks.as_mut_preserves_cfg(); - for Location { block, statement_index } in patch { - bbs[block].statements[statement_index].make_nop(); + for (Location { block, statement_index }, drop_debuginfo) in patch { + bbs[block].statements[statement_index].make_nop(drop_debuginfo); } for (block, argument_index) in call_operands_to_move { let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 74c22ff10c198..1f38433fa5a41 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -276,7 +276,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { StatementKind::StorageDead(local) | StatementKind::StorageLive(local) if self.merged_locals.contains(*local) => { - statement.make_nop(); + statement.make_nop(true); return; } _ => (), @@ -291,7 +291,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { // (this includes the original statement we wanted to eliminate). if dest == place { debug!("{:?} turned into self-assignment, deleting", location); - statement.make_nop(); + statement.make_nop(true); } } _ => {} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 99691d9e045b4..3ff8dc6dbb378 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1877,7 +1877,7 @@ impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.reused_locals.contains(l) => { - stmt.make_nop() + stmt.make_nop(true) } _ => self.super_statement(stmt, loc), } diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 1a91d6bd7da98..1b90e9158f6b8 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -156,7 +156,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { patch.add_statement(location, stmt); } - st.make_nop(); + st.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index cc8ea76011bf6..2c535d011a0e2 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -270,7 +270,7 @@ impl<'tcx> MirPatch<'tcx> { body.local_decls.extend(self.new_locals); for loc in self.nop_statements { - bbs[loc.block].statements[loc.statement_index].make_nop(); + bbs[loc.block].statements[loc.statement_index].make_nop(true); } let mut new_statements = self.new_statements; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 48ddf5a1bcabb..c7dc18a4a1343 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -1049,7 +1049,7 @@ fn promote_candidates<'tcx>( // Eliminate assignments to, and drops of promoted temps. let promoted = |index: Local| temps[index] == TempState::PromotedOut; for block in body.basic_blocks_mut() { - block.statements.retain(|statement| match &statement.kind { + block.retain_statements(|statement| match &statement.kind { StatementKind::Assign(box (place, _)) => { if let Some(index) = place.as_local() { !promoted(index) diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index b9d6e74ecae89..deb0a146476c3 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -435,7 +435,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.storage_to_remove.contains(l) => { - stmt.make_nop(); + stmt.make_nop(true); } // Do not remove assignments as they may still be useful for debuginfo. _ => self.super_statement(stmt, loc), diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index cb598ceb4dfea..d56b51bb496e4 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemovePlaceMention on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::PlaceMention(..) | StatementKind::Nop => false, _ => true, }) diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 1ae33c0096875..cb97d2c865ac9 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveStorageMarkers on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Nop => false, diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index c4dc8638b26ab..90c1b3520b96e 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -141,7 +141,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty && self.known_to_be_zst(ty) { - statement.make_nop(); + statement.make_nop(true); } else { self.super_statement(statement, loc); } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 75917d23883be..9f7bb3b03795a 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -36,7 +36,9 @@ use itertools::Itertools as _; use rustc_index::{Idx, IndexSlice, IndexVec}; -use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{ + MutVisitor, MutatingUseContext, NonUseContext, PlaceContext, Visitor, +}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_span::DUMMY_SP; @@ -303,7 +305,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) } } } @@ -539,12 +541,20 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { self.super_statement(statement, location); } - StatementKind::ConstEvalCounter | StatementKind::Nop => {} - - StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) => { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } + } StatementKind::Assign(box (ref place, ref rvalue)) => { if rvalue.is_safe_to_remove() { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } self.visit_lhs(place, location); self.visit_rvalue(rvalue, location); } else { @@ -555,15 +565,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::SetDiscriminant { ref place, variant_index: _ } | StatementKind::Deinit(ref place) | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } self.visit_lhs(place, location); } } } - fn visit_local(&mut self, local: Local, _ctx: PlaceContext, _location: Location) { + fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) { if self.increment { self.use_count[local] += 1; - } else { + } else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) { assert_ne!(self.use_count[local], 0); self.use_count[local] -= 1; } @@ -583,7 +596,7 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod for data in body.basic_blocks.as_mut_preserves_cfg() { // Remove unnecessary StorageLive and StorageDead annotations. - data.statements.retain(|statement| { + data.retain_statements(|statement| { let keep = match &statement.kind { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { used_locals.is_used(*local) diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index c60eb566521c5..4597439e269ff 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { // delete comparison statement if it the value being switched on was moved, which means // it can not be user later on if opt.can_remove_bin_op_stmt { - bb.statements[opt.bin_op_stmt_idx].make_nop(); + bb.statements[opt.bin_op_stmt_idx].make_nop(true); } else { // if the integer being compared to a const integral is being moved into the // comparison, e.g `_2 = Eq(move _3, const 'x');` @@ -136,7 +136,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { } for (idx, bb_idx) in storage_deads_to_remove { - body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(); + body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(true); } for (idx, stmt) in storage_deads_to_insert { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 38769885f368b..99f10b8d91d2c 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -318,7 +318,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageLive(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -327,7 +327,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageDead(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -337,7 +337,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { self.patch .add_statement(location, StatementKind::Deinit(Box::new(fl.into()))); } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -367,7 +367,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { ); } } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -429,7 +429,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { StatementKind::Assign(Box::new((new_local.into(), rvalue))), ); } - statement.make_nop(); + statement.make_nop(true); return; } } diff --git a/tests/codegen-llvm/debug-fndef-size.rs b/tests/codegen-llvm/debug-fndef-size.rs index 8f716c34e7b74..02629bd748c45 100644 --- a/tests/codegen-llvm/debug-fndef-size.rs +++ b/tests/codegen-llvm/debug-fndef-size.rs @@ -16,5 +16,5 @@ pub fn main() { // CHECK: %compare.dbg.spill = alloca [0 x i8], align 1 // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %compare.dbg.spill, {{(metadata )?}}![[VAR:.*]], {{(metadata )?}}!DIExpression() -// CHECK: ![[TYPE:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "fn(&i32, &i32) -> core::cmp::Ordering", baseType: !{{.*}}, align: 8, dwarfAddressSpace: {{.*}}) -// CHECK: ![[VAR]] = !DILocalVariable(name: "compare", scope: !{{.*}}, file: !{{.*}}, line: {{.*}}, type: ![[TYPE]], align: 8) +// CHECK-DAG: ![[TYPE:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "fn(&i32, &i32) -> core::cmp::Ordering", baseType: !{{.*}}, align: 8, dwarfAddressSpace: {{.*}}) +// CHECK-DAG: ![[VAR]] = !DILocalVariable(name: "compare", scope: !{{.*}}, file: !{{.*}}, line: {{.*}}, type: ![[TYPE]], align: 8) diff --git a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff new file mode 100644 index 0000000000000..d823241bc620a --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff @@ -0,0 +1,31 @@ +- // MIR for `dead_first` before DeadStoreElimination-initial ++ // MIR for `dead_first` after DeadStoreElimination-initial + + fn dead_first(_1: &Foo) -> &i32 { + debug v => _1; + let mut _0: &i32; + let mut _2: &i32; + let mut _3: &i32; + let _4: &i32; + scope 1 { + debug a => _2; + } + + bb0: { + StorageLive(_2); +- _2 = &((*_1).2: i32); ++ // DBG: _2 = &((*_1).2: i32); ++ nop; + StorageLive(_3); + StorageLive(_4); + _4 = &((*_1).0: i32); + _3 = &(*_4); + _2 = move _3; + StorageDead(_3); + StorageDead(_4); + _0 = &(*_2); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/ref.rs b/tests/mir-opt/dead-store-elimination/ref.rs new file mode 100644 index 0000000000000..18d9ea8b84d7d --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.rs @@ -0,0 +1,33 @@ +//@ test-mir-pass: DeadStoreElimination-initial + +pub struct Foo { + a: i32, + b: i64, + c: i32, +} + +// EMIT_MIR ref.tuple.DeadStoreElimination-initial.diff +pub fn tuple(v: (i32, &Foo)) -> i32 { + // CHECK-LABEL: fn tuple + // CHECK: debug _dead => [[dead:_[0-9]+]]; + // CHECK: bb0: + // FIXME: Preserve `tmp` for debuginfo, but we can merge it into the debuginfo. + // CHECK: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo); + // CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32) + let _dead = &v.1.c; + v.1.a +} + +// EMIT_MIR ref.dead_first.DeadStoreElimination-initial.diff +pub fn dead_first(v: &Foo) -> &i32 { + // CHECK-LABEL: fn dead_first + // CHECK: debug a => [[var_a:_[0-9]+]]; + // CHECK: bb0: + // CHECK: DBG: [[var_a]] = &((*_1).2: i32) + // CHECK: [[tmp_4:_[0-9]+]] = &((*_1).0: i32) + // CHECK: [[tmp_3:_[0-9]+]] = &(*[[tmp_4]]) + // CHECK: [[var_a]] = move [[tmp_3]] + let mut a = &v.c; + a = &v.a; + a +} diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff new file mode 100644 index 0000000000000..0547a42cab246 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -0,0 +1,26 @@ +- // MIR for `tuple` before DeadStoreElimination-initial ++ // MIR for `tuple` after DeadStoreElimination-initial + + fn tuple(_1: (i32, &Foo)) -> i32 { + debug v => _1; + let mut _0: i32; + let _2: &i32; + let mut _3: &Foo; + let mut _4: &Foo; + scope 1 { + debug _dead => _2; + } + + bb0: { + StorageLive(_2); + _3 = deref_copy (_1.1: &Foo); +- _2 = &((*_3).2: i32); ++ // DBG: _2 = &((*_3).2: i32); ++ nop; + _4 = deref_copy (_1.1: &Foo); + _0 = copy ((*_4).0: i32); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff new file mode 100644 index 0000000000000..d4a73351ee4a5 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -0,0 +1,26 @@ +- // MIR for `drop_debuginfo` before SimplifyCfg-final ++ // MIR for `drop_debuginfo` after SimplifyCfg-final + + fn drop_debuginfo(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb2]; +- } +- +- bb1: { +- // DBG: _3 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff new file mode 100644 index 0000000000000..1c12358ad8934 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -0,0 +1,30 @@ +- // MIR for `preserve_debuginfo_1` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_1` after SimplifyCfg-final + + fn preserve_debuginfo_1(_1: &Foo, _2: &mut bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- goto -> bb1; +- } +- +- bb1: { + (*_2) = const true; + // DBG: _3 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + // DBG: _5 = &((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff new file mode 100644 index 0000000000000..de8e5612c8788 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -0,0 +1,29 @@ +- // MIR for `preserve_debuginfo_2` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_2` after SimplifyCfg-final + + fn preserve_debuginfo_2(_1: &Foo) -> i32 { + debug foo_a => _2; + debug foo_b => _3; + debug foo_c => _4; + let mut _0: i32; + let mut _2: &i32; + let mut _3: &i64; + let mut _4: &i32; + + bb0: { +- goto -> bb1; +- } +- +- bb1: { + // DBG: _2 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _3 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + // DBG: _4 = &((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index f6c111a2228a9..f7729c7c5fa6d 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -16,10 +16,12 @@ + let mut _9: *mut A; + let mut _10: usize; + scope 3 (inlined Vec::::as_mut_ptr) { ++ let mut _11: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { ++ let mut _12: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { -+ let mut _11: std::ptr::NonNull; ++ let mut _13: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + scope 8 (inlined NonNull::::cast::) { + scope 9 (inlined NonNull::::as_ptr) { @@ -39,15 +41,15 @@ + } + } + scope 14 (inlined drop_in_place::<[A]> - shim(Some([A]))) { -+ let mut _12: usize; -+ let mut _13: *mut A; -+ let mut _14: bool; ++ let mut _14: usize; ++ let mut _15: *mut A; ++ let mut _16: bool; + } + } + } + scope 15 (inlined drop_in_place::> - shim(Some(Option))) { -+ let mut _15: isize; -+ let mut _16: isize; ++ let mut _17: isize; ++ let mut _18: isize; + } bb0: { @@ -62,16 +64,22 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ _11 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _9 = copy _11 as *mut A (Transmute); ++ // DBG: _11 = &((*_6).0: alloc::raw_vec::RawVec); ++ StorageLive(_12); ++ // DBG: _12 = &(((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); ++ StorageLive(_13); ++ _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _9 = copy _13 as *mut A (Transmute); ++ StorageDead(_13); ++ StorageDead(_12); + StorageDead(_11); + _10 = copy ((*_6).1: usize); + _8 = *mut [A] from (copy _9, copy _10); + StorageDead(_9); -+ StorageLive(_12); -+ StorageLive(_13); + StorageLive(_14); -+ _12 = const 0_usize; ++ StorageLive(_15); ++ StorageLive(_16); ++ _14 = const 0_usize; + goto -> bb4; } @@ -83,35 +91,35 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_15); -+ StorageLive(_16); -+ _15 = discriminant((*_5)); -+ switchInt(move _15) -> [0: bb5, otherwise: bb6]; ++ StorageLive(_17); ++ StorageLive(_18); ++ _17 = discriminant((*_5)); ++ switchInt(move _17) -> [0: bb5, otherwise: bb6]; } bb2: { ++ StorageDead(_16); ++ StorageDead(_15); + StorageDead(_14); -+ StorageDead(_13); -+ StorageDead(_12); + StorageDead(_8); + StorageDead(_10); + drop(((*_4).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { -+ _13 = &raw mut (*_8)[_12]; -+ _12 = Add(move _12, const 1_usize); -+ drop((*_13)) -> [return: bb4, unwind unreachable]; ++ _15 = &raw mut (*_8)[_14]; ++ _14 = Add(move _14, const 1_usize); ++ drop((*_15)) -> [return: bb4, unwind unreachable]; + } + + bb4: { -+ _14 = Eq(copy _12, copy _10); -+ switchInt(move _14) -> [0: bb3, otherwise: bb2]; ++ _16 = Eq(copy _14, copy _10); ++ switchInt(move _16) -> [0: bb3, otherwise: bb2]; + } + + bb5: { -+ StorageDead(_16); -+ StorageDead(_15); ++ StorageDead(_18); ++ StorageDead(_17); StorageDead(_5); return; + } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 2ae86e2eb8bbd..649b5a652027d 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -228,12 +228,12 @@ + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ _45 = &mut _19; ++ // DBG: _45 = &_19; + StorageLive(_46); -+ _46 = &mut (_19.0: &mut std::future::Ready<()>); ++ // DBG: _46 = &(_19.0: &mut std::future::Ready<()>); + _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_46); -+ _43 = &mut ((*_44).0: std::option::Option<()>); ++ // DBG: _43 = &((*_44).0: std::option::Option<()>); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index d7ae931aaae58..32021b20ef44b 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -245,12 +245,12 @@ + StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ _47 = &mut _19; ++ // DBG: _47 = &_19; + StorageLive(_48); -+ _48 = &mut (_19.0: &mut std::future::Ready<()>); ++ // DBG: _48 = &(_19.0: &mut std::future::Ready<()>); + _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_48); -+ _45 = &mut ((*_46).0: std::option::Option<()>); ++ // DBG: _45 = &((*_46).0: std::option::Option<()>); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index 5cf36b9aebf2f..a294c1b4588db 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -34,11 +34,11 @@ bb2: { StorageLive(_5); - _5 = &(*_2)[0 of 3]; + // DBG: _5 = &(*_2)[0 of 3]; StorageLive(_6); - _6 = &(*_2)[1 of 3]; + // DBG: _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[2 of 3]; + // DBG: _7 = &(*_2)[2 of 3]; StorageDead(_7); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index 0598a3aa3f19a..691271cf94edf 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -34,11 +34,11 @@ bb2: { StorageLive(_5); - _5 = &(*_2)[0 of 3]; + // DBG: _5 = &(*_2)[0 of 3]; StorageLive(_6); - _6 = &(*_2)[1 of 3]; + // DBG: _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[2 of 3]; + // DBG: _7 = &(*_2)[2 of 3]; StorageDead(_7); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir index b5c23822162c9..d4248f5d87fbc 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir index f22b8835735d1..a834932df5a82 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir index b5c23822162c9..d4248f5d87fbc 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir index f22b8835735d1..a834932df5a82 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 83478e60b5d4e..23dbc066dd528 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -5,7 +5,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let mut _8: u16; + let _8: std::option::Option; + let mut _10: u16; + let mut _11: &std::option::Option; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -13,8 +15,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; + let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _7: (); + let _9: (); } } } @@ -35,6 +38,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); + StorageLive(_11); + StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -53,34 +58,55 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); + StorageLive(_7); + _7 = AddUnchecked(copy _1, copy _4); + _8 = Option::::Some(move _7); + StorageDead(_7); + // DBG: _11 = &_8; + StorageDead(_8); + StorageDead(_11); goto -> bb7; } bb3: { - _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb6: { + StorageDead(_8); + StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { - StorageLive(_8); - _8 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _8); - StorageDead(_8); + StorageLive(_10); + _10 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _10); + StorageDead(_10); StorageDead(_4); return; } } + +ALLOC0 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} + +ALLOC1 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac7a6e0445191..ac15f070597ea 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -5,7 +5,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let mut _8: u16; + let _8: std::option::Option; + let mut _10: u16; + let mut _11: &std::option::Option; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -13,8 +15,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; + let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _7: (); + let _9: (); } } } @@ -35,6 +38,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); + StorageLive(_11); + StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -53,34 +58,55 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); + StorageLive(_7); + _7 = AddUnchecked(copy _1, copy _4); + _8 = Option::::Some(move _7); + StorageDead(_7); + // DBG: _11 = &_8; + StorageDead(_8); + StorageDead(_11); goto -> bb7; } bb3: { - _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb6: { + StorageDead(_8); + StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { - StorageLive(_8); - _8 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _8); - StorageDead(_8); + StorageLive(_10); + _10 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _10); + StorageDead(_10); StorageDead(_4); return; } } + +ALLOC0 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} + +ALLOC1 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir index 34747e5a92854..66239a0ef6637 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir @@ -13,7 +13,7 @@ fn clone_as_copy(_1: &NestCopy) -> NestCopy { bb0: { StorageLive(_2); - _2 = &((*_1).1: AllCopy); + // DBG: _2 = &((*_1).1: AllCopy); _0 = copy (*_1); StorageDead(_2); return; diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index e67f362ee04ae..aa03cec070ad3 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -35,15 +35,15 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { } bb1: { - _3 = &(((*_1) as A).0: AllCopy); + // DBG: _3 = &(((*_1) as A).0: AllCopy); _0 = copy (*_1); goto -> bb3; } bb2: { - _4 = &(((*_1) as B).0: NestCopy); + // DBG: _4 = &(((*_1) as B).0: NestCopy); StorageLive(_5); - _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); + // DBG: _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); StorageDead(_5); _0 = copy (*_1); goto -> bb3; diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index ba6ce0ee5286f..0dd2125dbe0d2 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index ba6ce0ee5286f..0dd2125dbe0d2 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index ba6ce0ee5286f..0dd2125dbe0d2 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index ba6ce0ee5286f..0dd2125dbe0d2 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index 9ceba9444b8da..c291366a694e7 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,7 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = AlignOf(T); // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) diff --git a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir index 75e8cb1d8618c..8f30ad30fccdf 100644 --- a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir @@ -6,20 +6,20 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () let mut _0: (); let mut _3: std::iter::FilterMap, impl Fn(T) -> Option>; let mut _4: std::iter::FilterMap, impl Fn(T) -> Option>; - let mut _5: &mut std::iter::FilterMap, impl Fn(T) -> Option>; - let mut _8: std::option::Option; - let mut _9: isize; - let _11: (); + let mut _7: std::option::Option; + let mut _8: isize; + let _10: (); + let mut _11: &mut std::iter::FilterMap, impl Fn(T) -> Option>; scope 1 { debug iter => _4; - let _10: U; + let _9: U; scope 2 { - debug x => _10; + debug x => _9; } scope 4 (inlined , impl Fn(T) -> Option> as Iterator>::next) { - debug self => _5; - let mut _6: &mut impl Iterator; - let mut _7: &mut impl Fn(T) -> Option; + debug self => _11; + let mut _5: &mut impl Iterator; + let mut _6: &mut impl Fn(T) -> Option; } } scope 3 (inlined , impl Fn(T) -> Option> as IntoIterator>::into_iter) { @@ -37,24 +37,24 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () } bb2: { - StorageLive(_8); - _5 = &mut _4; - StorageLive(_6); - _6 = &mut (_4.0: impl Iterator); StorageLive(_7); - _7 = &mut (_4.1: impl Fn(T) -> Option); - _8 = as Iterator>::find_map:: Option>(move _6, move _7) -> [return: bb3, unwind: bb9]; + // DBG: _11 = &_4; + StorageLive(_5); + _5 = &mut (_4.0: impl Iterator); + StorageLive(_6); + _6 = &mut (_4.1: impl Fn(T) -> Option); + _7 = as Iterator>::find_map:: Option>(move _5, move _6) -> [return: bb3, unwind: bb9]; } bb3: { - StorageDead(_7); StorageDead(_6); - _9 = discriminant(_8); - switchInt(move _9) -> [0: bb4, 1: bb6, otherwise: bb8]; + StorageDead(_5); + _8 = discriminant(_7); + switchInt(move _8) -> [0: bb4, 1: bb6, otherwise: bb8]; } bb4: { - StorageDead(_8); + StorageDead(_7); drop(_4) -> [return: bb5, unwind continue]; } @@ -64,12 +64,12 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () } bb6: { - _10 = move ((_8 as Some).0: U); - _11 = opaque::(move _10) -> [return: bb7, unwind: bb9]; + _9 = move ((_7 as Some).0: U); + _10 = opaque::(move _9) -> [return: bb7, unwind: bb9]; } bb7: { - StorageDead(_8); + StorageDead(_7); goto -> bb2; } diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index 154cbd3791cbd..b1af3141ee610 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -5,32 +5,31 @@ fn int_range(_1: usize, _2: usize) -> () { debug end => _2; let mut _0: (); let mut _3: std::ops::Range; - let mut _4: std::ops::Range; - let mut _5: &mut std::ops::Range; - let mut _13: std::option::Option; - let _15: (); + let mut _9: std::option::Option; + let _11: (); + let mut _12: &mut std::ops::Range; scope 1 { - debug iter => _4; - let _14: usize; + debug iter => _3; + let _10: usize; scope 2 { - debug i => _14; + debug i => _10; } scope 4 (inlined iter::range::>::next) { - debug self => _5; + debug self => _12; scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - debug self => _5; - let mut _6: &usize; - let mut _7: &usize; - let mut _10: bool; - let _11: usize; - let mut _12: usize; + debug self => _12; + let mut _6: bool; + let _7: usize; + let mut _8: usize; + let mut _13: &usize; + let mut _14: &usize; scope 6 { - debug old => _11; + debug old => _7; scope 8 (inlined ::forward_unchecked) { - debug start => _11; + debug start => _7; debug n => const 1_usize; scope 9 (inlined #[track_caller] core::num::::unchecked_add) { - debug self => _11; + debug self => _7; debug rhs => const 1_usize; scope 10 (inlined core::ub_checks::check_language_ub) { scope 11 (inlined core::ub_checks::check_language_ub::runtime) { @@ -40,10 +39,10 @@ fn int_range(_1: usize, _2: usize) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - debug self => _6; - debug other => _7; - let mut _8: usize; - let mut _9: usize; + debug self => _13; + debug other => _14; + let mut _4: usize; + let mut _5: usize; } } } @@ -54,54 +53,51 @@ fn int_range(_1: usize, _2: usize) -> () { bb0: { _3 = std::ops::Range:: { start: copy _1, end: copy _2 }; - StorageLive(_4); - _4 = copy _3; goto -> bb1; } bb1: { - StorageLive(_13); - _5 = &mut _4; - StorageLive(_10); - StorageLive(_6); - _6 = &(_4.0: usize); - StorageLive(_7); - _7 = &(_4.1: usize); - StorageLive(_8); - _8 = copy (_4.0: usize); StorageLive(_9); - _9 = copy (_4.1: usize); - _10 = Lt(move _8, move _9); - StorageDead(_9); - StorageDead(_8); - switchInt(move _10) -> [0: bb2, otherwise: bb3]; + // DBG: _12 = &_3; + StorageLive(_6); + StorageLive(_13); + // DBG: _13 = &(_3.0: usize); + StorageLive(_14); + // DBG: _14 = &(_3.1: usize); + StorageLive(_4); + _4 = copy (_3.0: usize); + StorageLive(_5); + _5 = copy (_3.1: usize); + _6 = Lt(move _4, move _5); + StorageDead(_5); + StorageDead(_4); + switchInt(move _6) -> [0: bb2, otherwise: bb3]; } bb2: { - StorageDead(_7); - StorageDead(_6); - StorageDead(_10); + StorageDead(_14); StorageDead(_13); - StorageDead(_4); + StorageDead(_6); + StorageDead(_9); return; } bb3: { - StorageDead(_7); + StorageDead(_14); + StorageDead(_13); + _7 = copy (_3.0: usize); + StorageLive(_8); + _8 = AddUnchecked(copy _7, const 1_usize); + (_3.0: usize) = move _8; + StorageDead(_8); + _9 = Option::::Some(copy _7); StorageDead(_6); - _11 = copy (_4.0: usize); - StorageLive(_12); - _12 = AddUnchecked(copy _11, const 1_usize); - (_4.0: usize) = move _12; - StorageDead(_12); - _13 = Option::::Some(copy _11); - StorageDead(_10); - _14 = copy ((_13 as Some).0: usize); - _15 = opaque::(move _14) -> [return: bb4, unwind continue]; + _10 = copy ((_9 as Some).0: usize); + _11 = opaque::(move _10) -> [return: bb4, unwind continue]; } bb4: { - StorageDead(_13); + StorageDead(_9); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir index d22ea54004c91..406c96fc32f48 100644 --- a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir @@ -6,32 +6,32 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { let mut _0: (); let mut _3: std::iter::Map, impl Fn(T) -> U>; let mut _4: std::iter::Map, impl Fn(T) -> U>; - let mut _5: &mut std::iter::Map, impl Fn(T) -> U>; - let mut _13: std::option::Option; - let _15: (); + let mut _12: std::option::Option; + let _14: (); + let mut _15: &mut std::iter::Map, impl Fn(T) -> U>; scope 1 { debug iter => _4; - let _14: U; + let _13: U; scope 2 { - debug x => _14; + debug x => _13; } scope 4 (inlined , impl Fn(T) -> U> as Iterator>::next) { - debug self => _5; - let mut _6: &mut impl Iterator; - let mut _7: std::option::Option; - let mut _8: &mut impl Fn(T) -> U; + debug self => _15; + let mut _5: &mut impl Iterator; + let mut _6: std::option::Option; + let mut _7: &mut impl Fn(T) -> U; scope 5 (inlined Option::::map:: U>) { - debug self => _7; - debug f => _8; - let mut _9: isize; - let _10: T; - let mut _11: (T,); - let mut _12: U; + debug self => _6; + debug f => _7; + let mut _8: isize; + let _9: T; + let mut _10: (T,); + let mut _11: U; scope 6 { - debug x => _10; + debug x => _9; scope 7 (inlined ops::function::impls:: for &mut impl Fn(T) -> U>::call_once) { - debug self => _8; - debug args => _11; + debug self => _7; + debug args => _10; } } } @@ -52,30 +52,30 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb2: { - StorageLive(_13); - _5 = &mut _4; - StorageLive(_8); + StorageLive(_12); + // DBG: _15 = &_4; StorageLive(_7); StorageLive(_6); - _6 = &mut (_4.0: impl Iterator); - _7 = as Iterator>::next(move _6) -> [return: bb3, unwind: bb10]; + StorageLive(_5); + _5 = &mut (_4.0: impl Iterator); + _6 = as Iterator>::next(move _5) -> [return: bb3, unwind: bb10]; } bb3: { - StorageDead(_6); - _8 = &mut (_4.1: impl Fn(T) -> U); + StorageDead(_5); + _7 = &mut (_4.1: impl Fn(T) -> U); + StorageLive(_8); StorageLive(_9); - StorageLive(_10); - _9 = discriminant(_7); - switchInt(move _9) -> [0: bb4, 1: bb6, otherwise: bb9]; + _8 = discriminant(_6); + switchInt(move _8) -> [0: bb4, 1: bb6, otherwise: bb9]; } bb4: { - StorageDead(_10); StorageDead(_9); - StorageDead(_7); StorageDead(_8); - StorageDead(_13); + StorageDead(_6); + StorageDead(_7); + StorageDead(_12); drop(_4) -> [return: bb5, unwind continue]; } @@ -85,27 +85,27 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb6: { - _10 = move ((_7 as Some).0: T); - StorageLive(_12); + _9 = move ((_6 as Some).0: T); StorageLive(_11); - _11 = (copy _10,); - _12 = U as FnMut<(T,)>>::call_mut(move _8, move _11) -> [return: bb7, unwind: bb10]; + StorageLive(_10); + _10 = (copy _9,); + _11 = U as FnMut<(T,)>>::call_mut(move _7, move _10) -> [return: bb7, unwind: bb10]; } bb7: { - StorageDead(_11); - _13 = Option::::Some(move _12); - StorageDead(_12); StorageDead(_10); + _12 = Option::::Some(move _11); + StorageDead(_11); StorageDead(_9); - StorageDead(_7); StorageDead(_8); - _14 = move ((_13 as Some).0: U); - _15 = opaque::(move _14) -> [return: bb8, unwind: bb10]; + StorageDead(_6); + StorageDead(_7); + _13 = move ((_12 as Some).0: U); + _14 = opaque::(move _13) -> [return: bb8, unwind: bb10]; } bb8: { - StorageDead(_13); + StorageDead(_12); goto -> bb2; } diff --git a/tests/mir-opt/pre-codegen/loops.rs b/tests/mir-opt/pre-codegen/loops.rs index d0b8cc8db7a90..952dd8cac60fd 100644 --- a/tests/mir-opt/pre-codegen/loops.rs +++ b/tests/mir-opt/pre-codegen/loops.rs @@ -1,5 +1,6 @@ // skip-filecheck //@ compile-flags: -O -Zmir-opt-level=2 -g +//@ ignore-std-debug-assertions (debug assertions result in different inlines) //@ needs-unwind #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index e537dd6a28ef8..03cdc221c3086 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -3,72 +3,354 @@ fn vec_move(_1: Vec) -> () { debug v => _1; let mut _0: (); - let mut _2: std::vec::IntoIter; - let mut _3: std::vec::IntoIter; - let mut _4: &mut std::vec::IntoIter; - let mut _5: std::option::Option; - let mut _6: isize; - let _8: (); + let mut _22: std::vec::IntoIter; + let mut _23: std::vec::IntoIter; + let mut _24: &mut std::vec::IntoIter; + let mut _25: std::option::Option; + let mut _26: isize; + let _28: (); scope 1 { - debug iter => _3; - let _7: impl Sized; + debug iter => _23; + let _27: impl Sized; scope 2 { - debug x => _7; + debug x => _27; + } + } + scope 3 (inlined as IntoIterator>::into_iter) { + debug self => _1; + let _2: std::mem::ManuallyDrop>; + let mut _3: *const std::alloc::Global; + let mut _8: usize; + let mut _10: *mut impl Sized; + let mut _11: *const impl Sized; + let mut _12: usize; + let _29: &std::vec::Vec; + let mut _30: &std::mem::ManuallyDrop>; + let mut _31: &alloc::raw_vec::RawVec; + let mut _32: &std::mem::ManuallyDrop>; + let _33: &std::vec::Vec; + let mut _34: &std::mem::ManuallyDrop>; + let _35: &std::vec::Vec; + let mut _36: &std::mem::ManuallyDrop>; + let mut _37: &alloc::raw_vec::RawVec; + let mut _38: &std::mem::ManuallyDrop>; + scope 4 { + debug me => _2; + scope 5 { + debug alloc => const ManuallyDrop:: {{ value: std::alloc::Global }}; + let _6: std::ptr::NonNull; + scope 6 { + debug buf => _6; + let _7: *mut impl Sized; + scope 7 { + debug begin => _7; + scope 8 { + debug end => _11; + let _20: usize; + scope 9 { + debug cap => _20; + } + scope 39 (inlined > as Deref>::deref) { + debug self => _38; + } + scope 40 (inlined alloc::raw_vec::RawVec::::capacity) { + debug self => _37; + let mut _19: usize; + let mut _42: &alloc::raw_vec::RawVecInner; + scope 41 (inlined std::mem::size_of::) { + } + scope 42 (inlined alloc::raw_vec::RawVecInner::capacity) { + debug self => _42; + debug elem_size => _19; + let mut _21: core::num::niche_types::UsizeNoHighBit; + scope 43 (inlined core::num::niche_types::UsizeNoHighBit::as_inner) { + debug self => _21; + } + } + } + } + scope 25 (inlined > as Deref>::deref) { + debug self => _34; + } + scope 26 (inlined Vec::::len) { + debug self => _33; + let mut _13: bool; + scope 27 { + } + } + scope 28 (inlined std::ptr::mut_ptr::::wrapping_byte_add) { + debug self => _7; + debug count => _12; + let mut _14: *mut u8; + let mut _18: *mut u8; + scope 29 (inlined std::ptr::mut_ptr::::cast::) { + debug self => _7; + } + scope 30 (inlined std::ptr::mut_ptr::::wrapping_add) { + debug self => _14; + debug count => _12; + let mut _15: isize; + scope 31 (inlined std::ptr::mut_ptr::::wrapping_offset) { + debug self => _14; + debug count => _15; + let mut _16: *const u8; + let mut _17: *const u8; + } + } + scope 32 (inlined std::ptr::mut_ptr::::with_metadata_of::) { + debug self => _18; + debug meta => _5; + scope 33 (inlined std::ptr::metadata::) { + debug ptr => _5; + } + scope 34 (inlined std::ptr::from_raw_parts_mut::) { + } + } + } + scope 35 (inlined > as Deref>::deref) { + debug self => _36; + } + scope 36 (inlined Vec::::len) { + debug self => _35; + let mut _9: bool; + scope 37 { + } + } + scope 38 (inlined #[track_caller] std::ptr::mut_ptr::::add) { + debug self => _7; + debug count => _8; + } + } + scope 24 (inlined NonNull::::as_ptr) { + debug self => _6; + } + } + scope 17 (inlined > as Deref>::deref) { + debug self => _32; + } + scope 18 (inlined alloc::raw_vec::RawVec::::non_null) { + debug self => _31; + let mut _41: &alloc::raw_vec::RawVecInner; + scope 19 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _4: std::ptr::NonNull; + scope 20 (inlined Unique::::cast::) { + scope 21 (inlined NonNull::::cast::) { + let mut _5: *const impl Sized; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined Unique::::as_non_null_ptr) { + } + } + } + } + scope 11 (inlined > as Deref>::deref) { + debug self => _30; + } + scope 12 (inlined Vec::::allocator) { + debug self => _29; + let mut _39: &alloc::raw_vec::RawVec; + scope 13 (inlined alloc::raw_vec::RawVec::::allocator) { + let mut _40: &alloc::raw_vec::RawVecInner; + scope 14 (inlined alloc::raw_vec::RawVecInner::allocator) { + } + } + } + scope 15 (inlined #[track_caller] std::ptr::read::) { + debug src => _3; + } + scope 16 (inlined ManuallyDrop::::new) { + debug value => const std::alloc::Global; + } + } + scope 10 (inlined ManuallyDrop::>::new) { + debug value => _1; } } bb0: { + StorageLive(_22); + StorageLive(_29); + StorageLive(_6); + StorageLive(_7); + StorageLive(_33); + StorageLive(_11); + StorageLive(_35); + StorageLive(_20); + StorageLive(_5); + StorageLive(_4); + StorageLive(_17); StorageLive(_2); - _2 = as IntoIterator>::into_iter(move _1) -> [return: bb1, unwind continue]; + _2 = ManuallyDrop::> { value: copy _1 }; + StorageLive(_3); + StorageLive(_30); + // DBG: _30 = &_2; + // DBG: _29 = &(_2.0: std::vec::Vec); + StorageDead(_30); + StorageLive(_39); + // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_40); + // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageDead(_40); + StorageDead(_39); + _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); + StorageDead(_3); + StorageLive(_31); + StorageLive(_32); + // DBG: _32 = &_2; + StorageDead(_32); + // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_41); + // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = copy _4 as *const impl Sized (Transmute); + _6 = NonNull:: { pointer: copy _5 }; + StorageDead(_41); + StorageDead(_31); + _7 = copy _4 as *mut impl Sized (Transmute); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_3); - _3 = move _2; - goto -> bb2; + StorageLive(_10); + StorageLive(_8); + StorageLive(_36); + // DBG: _36 = &_2; + // DBG: _35 = &(_2.0: std::vec::Vec); + StorageDead(_36); + _8 = copy ((_2.0: std::vec::Vec).1: usize); + StorageLive(_9); + _9 = Le(copy _8, const ::MAX_SLICE_LEN); + assume(move _9); + StorageDead(_9); + _10 = Offset(copy _7, copy _8); + _11 = copy _10 as *const impl Sized (PtrToPtr); + StorageDead(_8); + StorageDead(_10); + goto -> bb4; } bb2: { - StorageLive(_5); - _4 = &mut _3; - _5 = as Iterator>::next(move _4) -> [return: bb3, unwind: bb9]; + StorageLive(_12); + StorageLive(_34); + // DBG: _34 = &_2; + // DBG: _33 = &(_2.0: std::vec::Vec); + StorageDead(_34); + _12 = copy ((_2.0: std::vec::Vec).1: usize); + StorageLive(_13); + _13 = Le(copy _12, const ::MAX_SLICE_LEN); + assume(move _13); + StorageDead(_13); + StorageLive(_18); + StorageLive(_14); + _14 = copy _4 as *mut u8 (Transmute); + StorageLive(_15); + _15 = copy _12 as isize (IntToInt); + StorageLive(_16); + _16 = copy _4 as *const u8 (Transmute); + _17 = arith_offset::(move _16, move _15) -> [return: bb3, unwind unreachable]; } bb3: { - _6 = discriminant(_5); - switchInt(move _6) -> [0: bb4, 1: bb6, otherwise: bb8]; + StorageDead(_16); + _18 = copy _17 as *mut u8 (PtrToPtr); + StorageDead(_15); + StorageDead(_14); + StorageDead(_18); + StorageDead(_12); + _11 = copy _17 as *const impl Sized (PtrToPtr); + goto -> bb4; } bb4: { - StorageDead(_5); - drop(_3) -> [return: bb5, unwind continue]; + StorageLive(_37); + StorageLive(_38); + // DBG: _38 = &_2; + StorageDead(_38); + // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_42); + // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_19); + _19 = SizeOf(impl Sized); + switchInt(move _19) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_3); - StorageDead(_2); - return; + _20 = const usize::MAX; + goto -> bb7; } bb6: { - _7 = move ((_5 as Some).0: impl Sized); - _8 = opaque::(move _7) -> [return: bb7, unwind: bb9]; + StorageLive(_21); + _21 = copy ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).1: core::num::niche_types::UsizeNoHighBit); + _20 = copy _21 as usize (Transmute); + StorageDead(_21); + goto -> bb7; } bb7: { + StorageDead(_19); + StorageDead(_42); + StorageDead(_37); + _22 = std::vec::IntoIter:: { buf: copy _6, phantom: const ZeroSized: PhantomData, cap: move _20, alloc: const ManuallyDrop:: {{ value: std::alloc::Global }}, ptr: copy _6, end: copy _11 }; + StorageDead(_2); + StorageDead(_17); + StorageDead(_4); StorageDead(_5); - goto -> bb2; + StorageDead(_20); + StorageDead(_35); + StorageDead(_11); + StorageDead(_33); + StorageDead(_7); + StorageDead(_6); + StorageDead(_29); + StorageLive(_23); + _23 = move _22; + goto -> bb8; } bb8: { + StorageLive(_25); + _24 = &mut _23; + _25 = as Iterator>::next(move _24) -> [return: bb9, unwind: bb15]; + } + + bb9: { + _26 = discriminant(_25); + switchInt(move _26) -> [0: bb10, 1: bb12, otherwise: bb14]; + } + + bb10: { + StorageDead(_25); + drop(_23) -> [return: bb11, unwind continue]; + } + + bb11: { + StorageDead(_23); + StorageDead(_22); + return; + } + + bb12: { + _27 = move ((_25 as Some).0: impl Sized); + _28 = opaque::(move _27) -> [return: bb13, unwind: bb15]; + } + + bb13: { + StorageDead(_25); + goto -> bb8; + } + + bb14: { unreachable; } - bb9 (cleanup): { - drop(_3) -> [return: bb10, unwind terminate(cleanup)]; + bb15 (cleanup): { + drop(_23) -> [return: bb16, unwind terminate(cleanup)]; } - bb10 (cleanup): { + bb16 (cleanup): { resume; } } diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index cbdd194afd3ab..9d4bacef954ef 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -3,183 +3,164 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2: &&(usize, usize, usize, usize)) -> bool { let mut _0: bool; let mut _3: &(usize, usize, usize, usize); - let _4: &usize; - let _5: &usize; - let _6: &usize; - let _7: &usize; - let mut _8: &&usize; - let _9: &usize; - let mut _10: &&usize; - let mut _13: bool; - let mut _14: &&usize; + let mut _6: bool; + let mut _9: bool; + let mut _10: bool; + let _13: &usize; + let _14: &usize; let _15: &usize; - let mut _16: &&usize; - let mut _19: bool; + let _16: &usize; + let mut _17: &&usize; + let mut _18: &&usize; + let mut _19: &&usize; let mut _20: &&usize; - let _21: &usize; + let mut _21: &&usize; let mut _22: &&usize; - let mut _23: bool; + let mut _23: &&usize; let mut _24: &&usize; - let _25: &usize; - let mut _26: &&usize; scope 1 { - debug a => _4; - debug b => _5; - debug c => _6; - debug d => _7; + debug a => _13; + debug b => _14; + debug c => _15; + debug d => _16; scope 2 (inlined std::cmp::impls::::le) { - debug self => _8; - debug other => _10; + debug self => _17; + debug other => _18; scope 3 (inlined std::cmp::impls::::le) { - debug self => _4; - debug other => _6; - let mut _11: usize; - let mut _12: usize; + debug self => _13; + debug other => _15; + let mut _4: usize; + let mut _5: usize; } } scope 4 (inlined std::cmp::impls::::le) { - debug self => _14; - debug other => _16; + debug self => _19; + debug other => _20; scope 5 (inlined std::cmp::impls::::le) { - debug self => _7; - debug other => _5; - let mut _17: usize; - let mut _18: usize; + debug self => _16; + debug other => _14; + let mut _7: usize; + let mut _8: usize; } } scope 6 (inlined std::cmp::impls::::le) { - debug self => _20; + debug self => _21; debug other => _22; scope 7 (inlined std::cmp::impls::::le) { - debug self => _6; - debug other => _4; + debug self => _15; + debug other => _13; } } scope 8 (inlined std::cmp::impls::::le) { - debug self => _24; - debug other => _26; + debug self => _23; + debug other => _24; scope 9 (inlined std::cmp::impls::::le) { - debug self => _5; - debug other => _7; - let mut _27: usize; - let mut _28: usize; + debug self => _14; + debug other => _16; + let mut _11: usize; + let mut _12: usize; } } } bb0: { _3 = copy (*_2); - _4 = &((*_3).0: usize); - _5 = &((*_3).1: usize); - _6 = &((*_3).2: usize); - _7 = &((*_3).3: usize); - StorageLive(_13); - StorageLive(_8); - _8 = &_4; - StorageLive(_10); - StorageLive(_9); - _9 = copy _6; - _10 = &_9; - _11 = copy ((*_3).0: usize); - _12 = copy ((*_3).2: usize); - _13 = Le(copy _11, copy _12); - switchInt(move _13) -> [0: bb1, otherwise: bb2]; + // DBG: _13 = &((*_3).0: usize); + // DBG: _14 = &((*_3).1: usize); + // DBG: _15 = &((*_3).2: usize); + // DBG: _16 = &((*_3).3: usize); + StorageLive(_6); + StorageLive(_17); + // DBG: _17 = &_13; + StorageLive(_18); + // DBG: _18 = &_15; + _4 = copy ((*_3).0: usize); + _5 = copy ((*_3).2: usize); + _6 = Le(copy _4, copy _5); + switchInt(move _6) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageDead(_9); - StorageDead(_10); - StorageDead(_8); + StorageDead(_18); + StorageDead(_17); goto -> bb4; } bb2: { - StorageDead(_9); - StorageDead(_10); - StorageDead(_8); - StorageLive(_19); - StorageLive(_14); - _14 = &_7; - StorageLive(_16); - StorageLive(_15); - _15 = copy _5; - _16 = &_15; - StorageLive(_17); - _17 = copy ((*_3).3: usize); - StorageLive(_18); - _18 = copy ((*_3).1: usize); - _19 = Le(move _17, move _18); StorageDead(_18); StorageDead(_17); - switchInt(move _19) -> [0: bb3, otherwise: bb8]; + StorageLive(_9); + StorageLive(_19); + // DBG: _19 = &_16; + StorageLive(_20); + // DBG: _20 = &_14; + StorageLive(_7); + _7 = copy ((*_3).3: usize); + StorageLive(_8); + _8 = copy ((*_3).1: usize); + _9 = Le(move _7, move _8); + StorageDead(_8); + StorageDead(_7); + switchInt(move _9) -> [0: bb3, otherwise: bb8]; } bb3: { - StorageDead(_15); - StorageDead(_16); - StorageDead(_14); + StorageDead(_20); + StorageDead(_19); goto -> bb4; } bb4: { - StorageLive(_23); - StorageLive(_20); - _20 = &_6; - StorageLive(_22); + StorageLive(_10); StorageLive(_21); - _21 = copy _4; - _22 = &_21; - _23 = Le(copy _12, copy _11); - switchInt(move _23) -> [0: bb5, otherwise: bb6]; + // DBG: _21 = &_15; + StorageLive(_22); + // DBG: _22 = &_13; + _10 = Le(copy _5, copy _4); + switchInt(move _10) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_21); StorageDead(_22); - StorageDead(_20); + StorageDead(_21); _0 = const false; goto -> bb7; } bb6: { - StorageDead(_21); StorageDead(_22); - StorageDead(_20); + StorageDead(_21); + StorageLive(_23); + // DBG: _23 = &_14; StorageLive(_24); - _24 = &_5; - StorageLive(_26); - StorageLive(_25); - _25 = copy _7; - _26 = &_25; - StorageLive(_27); - _27 = copy ((*_3).1: usize); - StorageLive(_28); - _28 = copy ((*_3).3: usize); - _0 = Le(move _27, move _28); - StorageDead(_28); - StorageDead(_27); - StorageDead(_25); - StorageDead(_26); + // DBG: _24 = &_16; + StorageLive(_11); + _11 = copy ((*_3).1: usize); + StorageLive(_12); + _12 = copy ((*_3).3: usize); + _0 = Le(move _11, move _12); + StorageDead(_12); + StorageDead(_11); StorageDead(_24); + StorageDead(_23); goto -> bb7; } bb7: { - StorageDead(_23); + StorageDead(_10); goto -> bb9; } bb8: { - StorageDead(_15); - StorageDead(_16); - StorageDead(_14); + StorageDead(_20); + StorageDead(_19); _0 = const true; goto -> bb9; } bb9: { - StorageDead(_19); - StorageDead(_13); + StorageDead(_9); + StorageDead(_6); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 104987b0fdda9..1b2e7b7163cd8 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -51,6 +51,9 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _16: bool; let mut _20: usize; let _22: &T; + let mut _34: &std::ptr::NonNull; + let mut _35: &std::ptr::NonNull; + let mut _36: &std::ptr::NonNull; scope 29 { let _12: *const T; scope 30 { @@ -186,7 +189,11 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb5: { StorageLive(_16); + StorageLive(_34); + // DBG: _34 = &_11; + StorageLive(_35); _13 = copy _12 as std::ptr::NonNull (Transmute); + // DBG: _35 = &_13; StorageLive(_14); _14 = copy _11 as *mut T (Transmute); StorageLive(_15); @@ -198,6 +205,8 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { + StorageDead(_35); + StorageDead(_34); StorageDead(_16); StorageLive(_18); StorageLive(_17); @@ -210,6 +219,8 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb7: { + StorageDead(_35); + StorageDead(_34); StorageDead(_16); StorageDead(_22); StorageDead(_13); @@ -255,10 +266,13 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb13: { + StorageLive(_36); + // DBG: _36 = &_11; StorageLive(_21); _21 = copy _11 as *const T (Transmute); _22 = &(*_21); StorageDead(_21); + StorageDead(_36); _23 = Option::<&T>::Some(copy _22); StorageDead(_22); StorageDead(_13); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 4d0e3548e7d6b..c18dd01a189ad 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -23,6 +23,9 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; + let mut _27: &std::ptr::NonNull; + let mut _28: &std::ptr::NonNull; + let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -148,7 +151,11 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); + StorageLive(_27); + // DBG: _27 = &_10; + StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); + // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -160,6 +167,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -172,6 +181,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -213,10 +224,13 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { + StorageLive(_29); + // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); + StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 2b5d8c27d7109..6334c611430ad 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -23,6 +23,9 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; + let mut _27: &std::ptr::NonNull; + let mut _28: &std::ptr::NonNull; + let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -148,7 +151,11 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); + StorageLive(_27); + // DBG: _27 = &_10; + StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); + // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -160,6 +167,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -172,6 +181,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -213,10 +224,13 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { + StorageLive(_29); + // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); + StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 0adf268d766dd..24c6618d31cae 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _10: std::slice::Iter<'_, T>; let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _14: std::option::Option<&T>; - let mut _15: isize; - let mut _17: &impl Fn(&T); - let mut _18: (&T,); - let _19: (); + let mut _33: std::option::Option<&T>; + let mut _35: &impl Fn(&T); + let mut _36: (&T,); + let _37: (); scope 1 { debug iter => _12; - let _16: &T; + let _34: &T; scope 2 { - debug x => _16; + debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _13: &mut std::slice::Iter<'_, T>; + scope 19 (inlined as DoubleEndedIterator>::next_back) { + let mut _13: *const T; + let mut _18: bool; + let mut _19: *const T; + let _32: &T; + let mut _38: &std::ptr::NonNull; + let mut _39: &std::ptr::NonNull; + scope 20 { + let _14: std::ptr::NonNull; + let _20: usize; + scope 21 { + } + scope 22 { + scope 25 (inlined as PartialEq>::eq) { + let mut _15: std::ptr::NonNull; + let mut _16: *mut T; + let mut _17: *mut T; + scope 26 (inlined NonNull::::as_ptr) { + } + scope 27 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined std::ptr::const_ptr::::addr) { + scope 24 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _26: std::ptr::NonNull; + let mut _40: &std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _21: *mut *const T; + let mut _22: *mut std::ptr::NonNull; + let mut _23: std::ptr::NonNull; + let mut _27: *mut *const T; + let mut _28: *mut usize; + let mut _29: usize; + let mut _30: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _24: *const T; + let mut _25: *const T; + scope 40 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 41 (inlined NonNull::::as_ref::<'_>) { + let _31: *const T; + scope 42 (inlined NonNull::::as_ptr) { + } + scope 43 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -105,45 +176,145 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { + StorageLive(_33); + StorageLive(_20); + StorageLive(_19); StorageLive(_14); - StorageLive(_13); - _13 = &mut (_12.0: std::slice::Iter<'_, T>); - _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind unreachable]; + StorageLive(_32); + StorageLive(_18); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_13); + _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - _15 = discriminant(_14); - switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageLive(_38); + // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + StorageLive(_39); + // DBG: _39 = &_14; + StorageLive(_16); + StorageLive(_15); + _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _16 = copy _15 as *mut T (Transmute); + StorageDead(_15); + StorageLive(_17); + _17 = copy _14 as *mut T (Transmute); + _18 = Eq(copy _16, copy _17); + StorageDead(_17); + StorageDead(_16); + StorageDead(_39); + StorageDead(_38); + goto -> bb7; } bb6: { - StorageDead(_14); - StorageDead(_12); - drop(_2) -> [return: bb7, unwind unreachable]; + _19 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _20 = copy _19 as usize (Transmute); + _18 = Eq(copy _20, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _18) -> [0: bb8, otherwise: bb15]; } bb8: { - _16 = copy ((_14 as Some).0: &T); - StorageLive(_17); - _17 = &_2; - StorageLive(_18); - _18 = (copy _16,); - _19 = >::call(move _17, move _18) -> [return: bb9, unwind unreachable]; + StorageLive(_26); + StorageLive(_40); + StorageLive(_28); + StorageLive(_22); + StorageLive(_23); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb12]; } bb9: { + StorageLive(_21); + _21 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _22 = copy _21 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_21); + _23 = copy (*_22); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; + } + + bb10: { + StorageLive(_25); + StorageLive(_24); + _24 = copy _23 as *const T (Transmute); + _25 = Offset(copy _24, const -1_isize); + StorageDead(_24); + _23 = NonNull:: { pointer: copy _25 }; + StorageDead(_25); + goto -> bb11; + } + + bb11: { + (*_22) = move _23; + _26 = copy (*_22); + goto -> bb13; + } + + bb12: { + StorageLive(_27); + _27 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _28 = copy _27 as *mut usize (PtrToPtr); + StorageDead(_27); + StorageLive(_30); + StorageLive(_29); + _29 = copy (*_28); + _30 = SubUnchecked(move _29, const 1_usize); + StorageDead(_29); + (*_28) = move _30; + StorageDead(_30); + _26 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb13; + } + + bb13: { + StorageDead(_23); + StorageDead(_22); + StorageDead(_28); + // DBG: _40 = &_26; + StorageLive(_31); + _31 = copy _26 as *const T (Transmute); + _32 = &(*_31); + StorageDead(_31); + StorageDead(_40); + StorageDead(_26); + _33 = Option::<&T>::Some(copy _32); StorageDead(_18); - StorageDead(_17); + StorageDead(_32); StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + _34 = copy ((_33 as Some).0: &T); + StorageLive(_35); + _35 = &_2; + StorageLive(_36); + _36 = (copy _34,); + _37 = >::call(move _35, move _36) -> [return: bb14, unwind unreachable]; + } + + bb14: { + StorageDead(_36); + StorageDead(_35); + StorageDead(_33); goto -> bb4; } - bb10: { - unreachable; + bb15: { + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + StorageDead(_33); + StorageDead(_12); + drop(_2) -> [return: bb16, unwind unreachable]; + } + + bb16: { + return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index cb0d640d445bb..e5c7bcca44b65 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _10: std::slice::Iter<'_, T>; let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _14: std::option::Option<&T>; - let mut _15: isize; - let mut _17: &impl Fn(&T); - let mut _18: (&T,); - let _19: (); + let mut _33: std::option::Option<&T>; + let mut _35: &impl Fn(&T); + let mut _36: (&T,); + let _37: (); scope 1 { debug iter => _12; - let _16: &T; + let _34: &T; scope 2 { - debug x => _16; + debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _13: &mut std::slice::Iter<'_, T>; + scope 19 (inlined as DoubleEndedIterator>::next_back) { + let mut _13: *const T; + let mut _18: bool; + let mut _19: *const T; + let _32: &T; + let mut _38: &std::ptr::NonNull; + let mut _39: &std::ptr::NonNull; + scope 20 { + let _14: std::ptr::NonNull; + let _20: usize; + scope 21 { + } + scope 22 { + scope 25 (inlined as PartialEq>::eq) { + let mut _15: std::ptr::NonNull; + let mut _16: *mut T; + let mut _17: *mut T; + scope 26 (inlined NonNull::::as_ptr) { + } + scope 27 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined std::ptr::const_ptr::::addr) { + scope 24 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _26: std::ptr::NonNull; + let mut _40: &std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _21: *mut *const T; + let mut _22: *mut std::ptr::NonNull; + let mut _23: std::ptr::NonNull; + let mut _27: *mut *const T; + let mut _28: *mut usize; + let mut _29: usize; + let mut _30: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _24: *const T; + let mut _25: *const T; + scope 40 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 41 (inlined NonNull::::as_ref::<'_>) { + let _31: *const T; + scope 42 (inlined NonNull::::as_ptr) { + } + scope 43 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -105,53 +176,153 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { + StorageLive(_33); + StorageLive(_20); + StorageLive(_19); StorageLive(_14); - StorageLive(_13); - _13 = &mut (_12.0: std::slice::Iter<'_, T>); - _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind: bb11]; + StorageLive(_32); + StorageLive(_18); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_13); + _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - _15 = discriminant(_14); - switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageLive(_38); + // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + StorageLive(_39); + // DBG: _39 = &_14; + StorageLive(_16); + StorageLive(_15); + _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _16 = copy _15 as *mut T (Transmute); + StorageDead(_15); + StorageLive(_17); + _17 = copy _14 as *mut T (Transmute); + _18 = Eq(copy _16, copy _17); + StorageDead(_17); + StorageDead(_16); + StorageDead(_39); + StorageDead(_38); + goto -> bb7; } bb6: { - StorageDead(_14); - StorageDead(_12); - drop(_2) -> [return: bb7, unwind continue]; + _19 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _20 = copy _19 as usize (Transmute); + _18 = Eq(copy _20, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _18) -> [0: bb8, otherwise: bb17]; } bb8: { - _16 = copy ((_14 as Some).0: &T); - StorageLive(_17); - _17 = &_2; - StorageLive(_18); - _18 = (copy _16,); - _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; + StorageLive(_26); + StorageLive(_40); + StorageLive(_28); + StorageLive(_22); + StorageLive(_23); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb12]; } bb9: { + StorageLive(_21); + _21 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _22 = copy _21 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_21); + _23 = copy (*_22); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; + } + + bb10: { + StorageLive(_25); + StorageLive(_24); + _24 = copy _23 as *const T (Transmute); + _25 = Offset(copy _24, const -1_isize); + StorageDead(_24); + _23 = NonNull:: { pointer: copy _25 }; + StorageDead(_25); + goto -> bb11; + } + + bb11: { + (*_22) = move _23; + _26 = copy (*_22); + goto -> bb13; + } + + bb12: { + StorageLive(_27); + _27 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _28 = copy _27 as *mut usize (PtrToPtr); + StorageDead(_27); + StorageLive(_30); + StorageLive(_29); + _29 = copy (*_28); + _30 = SubUnchecked(move _29, const 1_usize); + StorageDead(_29); + (*_28) = move _30; + StorageDead(_30); + _26 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb13; + } + + bb13: { + StorageDead(_23); + StorageDead(_22); + StorageDead(_28); + // DBG: _40 = &_26; + StorageLive(_31); + _31 = copy _26 as *const T (Transmute); + _32 = &(*_31); + StorageDead(_31); + StorageDead(_40); + StorageDead(_26); + _33 = Option::<&T>::Some(copy _32); StorageDead(_18); - StorageDead(_17); + StorageDead(_32); StorageDead(_14); - goto -> bb4; + StorageDead(_19); + StorageDead(_20); + _34 = copy ((_33 as Some).0: &T); + StorageLive(_35); + _35 = &_2; + StorageLive(_36); + _36 = (copy _34,); + _37 = >::call(move _35, move _36) -> [return: bb14, unwind: bb15]; } - bb10: { - unreachable; + bb14: { + StorageDead(_36); + StorageDead(_35); + StorageDead(_33); + goto -> bb4; } - bb11 (cleanup): { - drop(_2) -> [return: bb12, unwind terminate(cleanup)]; + bb15 (cleanup): { + drop(_2) -> [return: bb16, unwind terminate(cleanup)]; } - bb12 (cleanup): { + bb16 (cleanup): { resume; } + + bb17: { + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + StorageDead(_33); + StorageDead(_12); + drop(_2) -> [return: bb18, unwind continue]; + } + + bb18: { + return; + } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir index 9b510380b10b2..18a3d8e7c4ad3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir @@ -6,6 +6,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; + let mut _9: &std::ptr::NonNull; + let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -41,6 +43,10 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); + StorageLive(_9); + // DBG: _9 = &((*_1).0: std::ptr::NonNull); + StorageLive(_10); + // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -51,6 +57,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); + StorageDead(_10); + StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir index 9b510380b10b2..18a3d8e7c4ad3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir @@ -6,6 +6,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; + let mut _9: &std::ptr::NonNull; + let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -41,6 +43,10 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); + StorageLive(_9); + // DBG: _9 = &((*_1).0: std::ptr::NonNull); + StorageLive(_10); + // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -51,6 +57,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); + StorageDead(_10); + StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 78f96bf419559..590d4ac2c8381 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -3,12 +3,199 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *mut T; + let mut _7: bool; + let mut _8: *mut T; + let mut _21: &mut T; + let mut _22: &std::ptr::NonNull; + let mut _23: &std::ptr::NonNull; + scope 2 { + let _3: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 7 (inlined as PartialEq>::eq) { + let mut _4: std::ptr::NonNull; + let mut _5: *mut T; + let mut _6: *mut T; + scope 8 (inlined NonNull::::as_ptr) { + } + scope 9 (inlined NonNull::::as_ptr) { + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + } + scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _15: std::ptr::NonNull; + let mut _24: &mut std::ptr::NonNull; + scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _10: *mut *mut T; + let mut _11: *mut std::ptr::NonNull; + let mut _12: std::ptr::NonNull; + let mut _16: *mut *mut T; + let mut _17: *mut usize; + let mut _18: usize; + let mut _19: usize; + scope 12 { + scope 13 { + } + scope 14 { + scope 17 (inlined NonNull::::sub) { + scope 18 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 19 (inlined core::ub_checks::check_language_ub) { + scope 20 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 21 (inlined NonNull::::offset) { + let mut _13: *const T; + let mut _14: *const T; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 15 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 16 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _20: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable]; + StorageLive(_9); + StorageLive(_8); + StorageLive(_3); + StorageLive(_2); + StorageLive(_21); + StorageLive(_7); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + _2 = copy ((*_1).1: *mut T); + _3 = copy _2 as std::ptr::NonNull (Transmute); + StorageLive(_22); + // DBG: _22 = &((*_1).0: std::ptr::NonNull); + StorageLive(_23); + // DBG: _23 = &_3; + StorageLive(_5); + StorageLive(_4); + _4 = copy ((*_1).0: std::ptr::NonNull); + _5 = copy _4 as *mut T (Transmute); + StorageDead(_4); + StorageLive(_6); + _6 = copy _3 as *mut T (Transmute); + _7 = Eq(copy _5, copy _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_23); + StorageDead(_22); + goto -> bb3; + } + + bb2: { + _8 = copy ((*_1).1: *mut T); + _9 = copy _8 as usize (Transmute); + _7 = Eq(copy _9, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _7) -> [0: bb4, otherwise: bb10]; + } + + bb4: { + StorageLive(_15); + StorageLive(_24); + StorageLive(_17); + StorageLive(_11); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; + } + + bb5: { + StorageLive(_10); + _10 = &raw mut ((*_1).1: *mut T); + _11 = copy _10 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_10); + _12 = copy (*_11); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_14); + StorageLive(_13); + _13 = copy _12 as *const T (Transmute); + _14 = Offset(copy _13, const -1_isize); + StorageDead(_13); + _12 = NonNull:: { pointer: copy _14 }; + StorageDead(_14); + goto -> bb7; + } + + bb7: { + (*_11) = move _12; + _15 = copy (*_11); + goto -> bb9; + } + + bb8: { + StorageLive(_16); + _16 = &raw mut ((*_1).1: *mut T); + _17 = copy _16 as *mut usize (PtrToPtr); + StorageDead(_16); + StorageLive(_19); + StorageLive(_18); + _18 = copy (*_17); + _19 = SubUnchecked(move _18, const 1_usize); + StorageDead(_18); + (*_17) = move _19; + StorageDead(_19); + _15 = copy ((*_1).0: std::ptr::NonNull); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_17); + // DBG: _24 = &_15; + StorageLive(_20); + _20 = copy _15 as *mut T (Transmute); + _21 = &mut (*_20); + StorageDead(_20); + StorageDead(_24); + StorageDead(_15); + _0 = Option::<&mut T>::Some(copy _21); + goto -> bb11; + } + + bb10: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb11; + } + + bb11: { + StorageDead(_7); + StorageDead(_21); + StorageDead(_2); + StorageDead(_3); + StorageDead(_8); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index dfe5e206fadaf..590d4ac2c8381 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -3,12 +3,199 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *mut T; + let mut _7: bool; + let mut _8: *mut T; + let mut _21: &mut T; + let mut _22: &std::ptr::NonNull; + let mut _23: &std::ptr::NonNull; + scope 2 { + let _3: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 7 (inlined as PartialEq>::eq) { + let mut _4: std::ptr::NonNull; + let mut _5: *mut T; + let mut _6: *mut T; + scope 8 (inlined NonNull::::as_ptr) { + } + scope 9 (inlined NonNull::::as_ptr) { + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + } + scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _15: std::ptr::NonNull; + let mut _24: &mut std::ptr::NonNull; + scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _10: *mut *mut T; + let mut _11: *mut std::ptr::NonNull; + let mut _12: std::ptr::NonNull; + let mut _16: *mut *mut T; + let mut _17: *mut usize; + let mut _18: usize; + let mut _19: usize; + scope 12 { + scope 13 { + } + scope 14 { + scope 17 (inlined NonNull::::sub) { + scope 18 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 19 (inlined core::ub_checks::check_language_ub) { + scope 20 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 21 (inlined NonNull::::offset) { + let mut _13: *const T; + let mut _14: *const T; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 15 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 16 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _20: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue]; + StorageLive(_9); + StorageLive(_8); + StorageLive(_3); + StorageLive(_2); + StorageLive(_21); + StorageLive(_7); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + _2 = copy ((*_1).1: *mut T); + _3 = copy _2 as std::ptr::NonNull (Transmute); + StorageLive(_22); + // DBG: _22 = &((*_1).0: std::ptr::NonNull); + StorageLive(_23); + // DBG: _23 = &_3; + StorageLive(_5); + StorageLive(_4); + _4 = copy ((*_1).0: std::ptr::NonNull); + _5 = copy _4 as *mut T (Transmute); + StorageDead(_4); + StorageLive(_6); + _6 = copy _3 as *mut T (Transmute); + _7 = Eq(copy _5, copy _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_23); + StorageDead(_22); + goto -> bb3; + } + + bb2: { + _8 = copy ((*_1).1: *mut T); + _9 = copy _8 as usize (Transmute); + _7 = Eq(copy _9, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _7) -> [0: bb4, otherwise: bb10]; + } + + bb4: { + StorageLive(_15); + StorageLive(_24); + StorageLive(_17); + StorageLive(_11); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; + } + + bb5: { + StorageLive(_10); + _10 = &raw mut ((*_1).1: *mut T); + _11 = copy _10 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_10); + _12 = copy (*_11); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_14); + StorageLive(_13); + _13 = copy _12 as *const T (Transmute); + _14 = Offset(copy _13, const -1_isize); + StorageDead(_13); + _12 = NonNull:: { pointer: copy _14 }; + StorageDead(_14); + goto -> bb7; + } + + bb7: { + (*_11) = move _12; + _15 = copy (*_11); + goto -> bb9; + } + + bb8: { + StorageLive(_16); + _16 = &raw mut ((*_1).1: *mut T); + _17 = copy _16 as *mut usize (PtrToPtr); + StorageDead(_16); + StorageLive(_19); + StorageLive(_18); + _18 = copy (*_17); + _19 = SubUnchecked(move _18, const 1_usize); + StorageDead(_18); + (*_17) = move _19; + StorageDead(_19); + _15 = copy ((*_1).0: std::ptr::NonNull); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_17); + // DBG: _24 = &_15; + StorageLive(_20); + _20 = copy _15 as *mut T (Transmute); + _21 = &mut (*_20); + StorageDead(_20); + StorageDead(_24); + StorageDead(_15); + _0 = Option::<&mut T>::Some(copy _21); + goto -> bb11; + } + + bb10: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb11; + } + + bb11: { + StorageDead(_7); + StorageDead(_21); + StorageDead(_2); + StorageDead(_3); + StorageDead(_8); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index cc0fce26149e3..e9ed932cbafb6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -10,6 +10,9 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; + let mut _15: &std::ptr::NonNull; + let mut _16: &std::ptr::NonNull; + let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -67,7 +70,11 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); + StorageLive(_15); + // DBG: _15 = &_2; + StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); + // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -79,6 +86,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { + StorageDead(_16); + StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -94,6 +103,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { + StorageDead(_16); + StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -116,10 +127,13 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { + StorageLive(_17); + // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); + StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index cc0fce26149e3..e9ed932cbafb6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,6 +10,9 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; + let mut _15: &std::ptr::NonNull; + let mut _16: &std::ptr::NonNull; + let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -67,7 +70,11 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); + StorageLive(_15); + // DBG: _15 = &_2; + StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); + // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -79,6 +86,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { + StorageDead(_16); + StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -94,6 +103,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { + StorageDead(_16); + StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -116,10 +127,13 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { + StorageLive(_17); + // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); + StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir index fe4e2deab8706..79aa9c5ae1e36 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir @@ -9,7 +9,7 @@ fn outer(_1: u8) -> u8 { } bb0: { - _2 = &_1; // scope 0 at $DIR/spans.rs:11:11: 11:13 + // DBG: _2 = &_1; _0 = copy _1; // scope 1 at $DIR/spans.rs:15:5: 15:7 return; // scope 0 at $DIR/spans.rs:12:2: 12:2 } diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir index fe4e2deab8706..79aa9c5ae1e36 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir @@ -9,7 +9,7 @@ fn outer(_1: u8) -> u8 { } bb0: { - _2 = &_1; // scope 0 at $DIR/spans.rs:11:11: 11:13 + // DBG: _2 = &_1; _0 = copy _1; // scope 1 at $DIR/spans.rs:15:5: 15:7 return; // scope 0 at $DIR/spans.rs:12:2: 12:2 } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 2eee8a97db0d4..eebd4a7eb50e3 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -11,7 +11,9 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; + let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -55,8 +57,14 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); + StorageLive(_6); + // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_7); + // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 2eee8a97db0d4..eebd4a7eb50e3 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -11,7 +11,9 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; + let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -55,8 +57,14 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); + StorageLive(_6); + // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_7); + // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); From cc93132ae4f5477297ecddb0c07d2e8c74075f0c Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 10 Jul 2025 21:20:54 +0800 Subject: [PATCH 1155/1900] simplifycfg: Preserve debuginfos when merging bbs --- compiler/rustc_middle/src/mir/terminator.rs | 8 + compiler/rustc_mir_transform/src/simplify.rs | 30 ++- ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 2 + ...reserve_debuginfo_1.SimplifyCfg-final.diff | 3 + ...reserve_debuginfo_2.SimplifyCfg-final.diff | 3 + ...reserve_debuginfo_3.SimplifyCfg-final.diff | 40 ++++ ...nfo_identical_succs.SimplifyCfg-final.diff | 32 +++ tests/mir-opt/debuginfo/simplifycfg.rs | 207 ++++++++++++++++++ 8 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.rs diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 4034a3a06e943..4249914346cd4 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> { self.kind.successors() } + /// Return `Some` if all successors are identical. + #[inline] + pub fn identical_successor(&self) -> Option { + let mut successors = self.successors(); + let first_succ = successors.next()?; + if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None } + } + #[inline] pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) { self.kind.successors_mut(f) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 9f7bb3b03795a..edea5cb2c7241 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // statements itself to avoid moving the (relatively) large statements twice. // We do not push the statements directly into the target block (`bb`) as that is slower // due to additional reallocations - let mut merged_blocks = Vec::new(); + let mut merged_blocks: Vec = Vec::new(); let mut outer_changed = false; loop { let mut changed = false; @@ -159,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { let mut terminator = self.basic_blocks[bb].terminator.take().expect("invalid terminator state"); - terminator - .successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed)); + terminator.successors_mut(|successor| { + self.collapse_goto_chain(successor, &mut changed); + }); let mut inner_changed = true; merged_blocks.clear(); @@ -177,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { if statements_to_merge > 0 { let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); statements.reserve(statements_to_merge); + let mut parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos); for &from in &merged_blocks { + if let Some(stmt) = self.basic_blocks[from].statements.first_mut() { + stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos); + } statements.append(&mut self.basic_blocks[from].statements); + parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos); } self.basic_blocks[bb].statements = statements; + self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos; } self.basic_blocks[bb].terminator = Some(terminator); @@ -220,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // goto chains. We should probably benchmark different sizes. let mut terminators: SmallVec<[_; 1]> = Default::default(); let mut current = *start; + // If each successor has only one predecessor, it's a trivial goto chain. + // We can move all debuginfos to the last basic block. + let mut trivial_goto_chain = true; while let Some(terminator) = self.take_terminator_if_simple_goto(current) { let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else { unreachable!(); }; + trivial_goto_chain &= self.pred_count[target] == 1; terminators.push((current, terminator)); current = target; } @@ -235,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { else { unreachable!(); }; + if trivial_goto_chain { + let mut pred_debuginfos = + std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos); + let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut() + { + &mut stmt.debuginfos + } else { + &mut self.basic_blocks[last].after_last_stmt_debuginfos + }; + debuginfos.prepend(&mut pred_debuginfos); + } *changed |= *target != last; *target = last; debug!("collapsing goto chain from {:?} to {:?}", current, target); diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff index d4a73351ee4a5..7a1ac92f6dde9 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -14,11 +14,13 @@ - - bb1: { - // DBG: _3 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff index 1c12358ad8934..fa4d3aebf8384 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -17,13 +17,16 @@ - bb1: { (*_2) = const true; // DBG: _3 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); +- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff index de8e5612c8788..b01c91f196b65 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -16,13 +16,16 @@ - - bb1: { // DBG: _2 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _3 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); // DBG: _4 = &((*_1).2: i32); +- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff new file mode 100644 index 0000000000000..caa90d79a1c27 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff @@ -0,0 +1,40 @@ +- // MIR for `preserve_debuginfo_3` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_3` after SimplifyCfg-final + + fn preserve_debuginfo_3(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb2]; ++ switchInt(copy _2) -> [1: bb2, otherwise: bb1]; + } + + bb1: { +- // DBG: _3 = &((*_1).0: i32); +- nop; +- goto -> bb3; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); +- nop; + _0 = copy ((*_1).2: i32); + return; + } + +- bb3: { ++ bb2: { ++ // DBG: _3 = &((*_1).0: i32); + // DBG: _5 = &((*_1).2: i32); +- nop; + _0 = copy ((*_1).0: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff new file mode 100644 index 0000000000000..d8c36990a5ff0 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff @@ -0,0 +1,32 @@ +- // MIR for `preserve_debuginfo_identical_succs` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_identical_succs` after SimplifyCfg-final + + fn preserve_debuginfo_identical_succs(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb1]; +- } +- +- bb1: { + // DBG: _3 = &((*_1).0: i32); +- nop; +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); +- nop; + _0 = copy ((*_1).2: i32); + // DBG: _5 = &((*_1).2: i32); +- nop; + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.rs b/tests/mir-opt/debuginfo/simplifycfg.rs new file mode 100644 index 0000000000000..2bd510fd3b9db --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.rs @@ -0,0 +1,207 @@ +//@ test-mir-pass: SimplifyCfg-final +//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +pub struct Foo { + a: i32, + b: i64, + c: i32, +} + +// EMIT_MIR simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn drop_debuginfo(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn drop_debuginfo + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + { + match c { + true => tmp, + _ => ret, + } + } + tmp = { + // Because we don't know if `c` is always true, we must drop this debuginfo. + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_1(foo: &Foo, v: &mut bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_1 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: (*_2) = const true; + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + Goto(tmp) + } + tmp = { + *v = true; + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_2(foo: &Foo) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_2 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + Goto(tmp) + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_3(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_3 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: switchInt(copy _2) -> [1: bb2, otherwise: bb1]; + // CHECK: bb1: { + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: return; + // CHECK: bb2: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: _0 = copy ((*_1).0: i32); + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + match c { + true => tmp, + _ => ret, + } + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret_1) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + Return() + } + ret_1 = { + _foo_c = &(*foo).c; + RET = (*foo).a; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_identical_succs(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_identical_succs + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + match c { + true => tmp, + _ => tmp, + } + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} From 85b2f706939528b5796d98e82c183637f8338cd1 Mon Sep 17 00:00:00 2001 From: dianqk Date: Tue, 15 Jul 2025 22:54:54 +0800 Subject: [PATCH 1156/1900] mir-opt: Eliminate trivial unnecessary storage annotations --- compiler/rustc_middle/src/mir/statement.rs | 14 +-- .../rustc_mir_dataflow/src/impls/liveness.rs | 4 +- .../src/dead_store_elimination.rs | 16 ++- compiler/rustc_mir_transform/src/inline.rs | 8 +- compiler/rustc_mir_transform/src/simplify.rs | 118 ++++++++++++------ ...le.cycle.DeadStoreElimination-initial.diff | 4 - ...ad_first.DeadStoreElimination-initial.diff | 1 - ...ef.tuple.DeadStoreElimination-initial.diff | 5 +- ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 2 - ...reserve_debuginfo_1.SimplifyCfg-final.diff | 3 - ...reserve_debuginfo_2.SimplifyCfg-final.diff | 3 - ...reserve_debuginfo_3.SimplifyCfg-final.diff | 3 - ...nfo_identical_succs.SimplifyCfg-final.diff | 3 - .../inline_shims.drop.Inline.panic-abort.diff | 4 - ...inline_shims.drop.Inline.panic-unwind.diff | 2 - ...y.run2-{closure#0}.Inline.panic-abort.diff | 11 -- ....run2-{closure#0}.Inline.panic-unwind.diff | 11 -- .../issue_101973.inner.GVN.panic-abort.diff | 1 - .../issue_101973.inner.GVN.panic-unwind.diff | 1 - ...implifyComparisonIntegral.panic-abort.diff | 6 - ...mplifyComparisonIntegral.panic-unwind.diff | 6 - ...git.PreCodegen.after.32bit.panic-abort.mir | 4 - ...it.PreCodegen.after.32bit.panic-unwind.mir | 4 - ...git.PreCodegen.after.64bit.panic-abort.mir | 4 - ...it.PreCodegen.after.64bit.panic-unwind.mir | 4 - ...p_forward.PreCodegen.after.panic-abort.mir | 40 ++---- ..._forward.PreCodegen.after.panic-unwind.mir | 40 ++---- ...as_copy.clone_as_copy.PreCodegen.after.mir | 2 - ...py.enum_clone_as_copy.PreCodegen.after.mir | 46 ++----- tests/mir-opt/pre-codegen/clone_as_copy.rs | 16 +-- ...ace.PreCodegen.after.32bit.panic-abort.mir | 38 ++---- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 38 ++---- ...ace.PreCodegen.after.64bit.panic-abort.mir | 38 ++---- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 38 ++---- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- .../loops.int_range.PreCodegen.after.mir | 6 - .../loops.vec_move.PreCodegen.after.mir | 28 ----- ...variant_a-{closure#0}.PreCodegen.after.mir | 22 ---- ...ated_loop.PreCodegen.after.panic-abort.mir | 14 --- ...ward_loop.PreCodegen.after.panic-abort.mir | 14 --- ...ard_loop.PreCodegen.after.panic-unwind.mir | 14 --- ...erse_loop.PreCodegen.after.panic-abort.mir | 12 -- ...rse_loop.PreCodegen.after.panic-unwind.mir | 12 -- ..._is_empty.PreCodegen.after.panic-abort.mir | 8 -- ...is_empty.PreCodegen.after.panic-unwind.mir | 8 -- ...next_back.PreCodegen.after.panic-abort.mir | 12 -- ...ext_back.PreCodegen.after.panic-unwind.mir | 12 -- ...iter_next.PreCodegen.after.panic-abort.mir | 14 --- ...ter_next.PreCodegen.after.panic-unwind.mir | 14 --- tests/ui/extern/extern-types-field-offset.rs | 5 + 50 files changed, 179 insertions(+), 556 deletions(-) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 9deb43eb7084b..67b4448cfcddd 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -30,16 +30,10 @@ impl<'tcx> Statement<'tcx> { } let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop); if !drop_debuginfo { - match replaced_stmt { - StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) - if let Some(local) = place.as_local() => - { - self.debuginfos.push(StmtDebugInfo::AssignRef(local, ref_place)); - } - _ => { - bug!("debuginfo is not yet supported.") - } - } + let Some(debuginfo) = replaced_stmt.as_debuginfo() else { + bug!("debuginfo is not yet supported.") + }; + self.debuginfos.push(debuginfo); } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 24da4b0bea28a..037e2720f3651 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -233,8 +233,10 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { // Compute the place that we are storing to, if any let destination = match stmt_kind { StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove() + // FIXME: We are not sure how we should represent this debugging information for some statements, + // keep it for now. && (!debuginfo_locals.contains(place.local) - || (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..))))) + || (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some()))) .then_some(*place), StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { (!debuginfo_locals.contains(place.local)).then_some(**place) diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index a5f8a22e83c85..732c3dcd44ab8 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -22,13 +22,15 @@ use rustc_mir_dataflow::impls::{ LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals, }; +use crate::simplify::UsedInStmtLocals; use crate::util::is_within_packed; /// Performs the optimization on the body /// /// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this /// body. It can be generated via the [`borrowed_locals`] function. -fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +/// Returns true if any instruction is eliminated. +fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let borrowed_locals = borrowed_locals(body); // If the user requests complete debuginfo, mark the locals that appear in it as live, so @@ -97,8 +99,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } if patch.is_empty() && call_operands_to_move.is_empty() { - return; + return false; } + let eliminated = !patch.is_empty(); let bbs = body.basic_blocks.as_mut_preserves_cfg(); for (Location { block, statement_index }, drop_debuginfo) in patch { @@ -112,6 +115,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let Operand::Copy(place) = *arg else { bug!() }; *arg = Operand::Move(place); } + + eliminated } pub(super) enum DeadStoreElimination { @@ -132,7 +137,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - eliminate(tcx, body); + if eliminate(tcx, body) { + UsedInStmtLocals::new(body).remove_unused_storage_annotations(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + data.strip_nops(); + } + } } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 3d49eb4e8ef75..8593e25d6aa5e 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::{CostChecker, is_call_like}; use crate::deref_separator::deref_finder; -use crate::simplify::simplify_cfg; +use crate::simplify::{UsedInStmtLocals, simplify_cfg}; use crate::validate::validate_types; use crate::{check_inline, util}; @@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( in_cleanup_block: false, return_block, tcx, - always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()), + always_live_locals: UsedInStmtLocals::new(&callee_body).locals, }; // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones @@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // people working on rust can build with or without debuginfo while // still getting consistent results from the mir-opt tests. caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } else { + for bb in callee_body.basic_blocks_mut() { + bb.drop_debuginfo(); + } } caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index edea5cb2c7241..0f23482ea8b1b 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -35,12 +35,12 @@ //! pre-"runtime" MIR! use itertools::Itertools as _; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; -use rustc_middle::mir::visit::{ - MutVisitor, MutatingUseContext, NonUseContext, PlaceContext, Visitor, -}; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_span::DUMMY_SP; use smallvec::SmallVec; use tracing::{debug, trace}; @@ -329,7 +329,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + blk.strip_nops(); } } } @@ -502,17 +502,22 @@ fn make_local_map( /// Keeps track of used & unused locals. struct UsedLocals { increment: bool, - arg_count: u32, use_count: IndexVec, + always_used: DenseBitSet, } impl UsedLocals { /// Determines which locals are used & unused in the given body. fn new(body: &Body<'_>) -> Self { + let mut always_used = debuginfo_locals(body); + always_used.insert(RETURN_PLACE); + for arg in body.args_iter() { + always_used.insert(arg); + } let mut this = Self { increment: true, - arg_count: body.arg_count.try_into().unwrap(), use_count: IndexVec::from_elem(0, &body.local_decls), + always_used, }; this.visit_body(body); this @@ -520,10 +525,16 @@ impl UsedLocals { /// Checks if local is used. /// - /// Return place and arguments are always considered used. + /// Return place, arguments, var debuginfo are always considered used. fn is_used(&self, local: Local) -> bool { - trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]); - local.as_u32() <= self.arg_count || self.use_count[local] != 0 + trace!( + "is_used({:?}): use_count: {:?}, always_used: {}", + local, + self.use_count[local], + self.always_used.contains(local) + ); + // To keep things simple, we don't handle debugging information here, these are in DSE. + self.always_used.contains(local) || self.use_count[local] != 0 } /// Updates the use counts to reflect the removal of given statement. @@ -568,17 +579,9 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) => { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } - } - + | StatementKind::StorageDead(..) => {} StatementKind::Assign(box (ref place, ref rvalue)) => { if rvalue.is_safe_to_remove() { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } self.visit_lhs(place, location); self.visit_rvalue(rvalue, location); } else { @@ -589,18 +592,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::SetDiscriminant { ref place, variant_index: _ } | StatementKind::Deinit(ref place) | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } self.visit_lhs(place, location); } } } fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) { + if matches!(ctx, PlaceContext::NonUse(_)) { + return; + } if self.increment { self.use_count[local] += 1; - } else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) { + } else { assert_ne!(self.use_count[local], 0); self.use_count[local] -= 1; } @@ -620,28 +623,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod for data in body.basic_blocks.as_mut_preserves_cfg() { // Remove unnecessary StorageLive and StorageDead annotations. - data.retain_statements(|statement| { - let keep = match &statement.kind { + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { used_locals.is_used(*local) } - StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local), - - StatementKind::SetDiscriminant { place, .. } - | StatementKind::BackwardIncompatibleDropHint { place, reason: _ } - | StatementKind::Deinit(place) => used_locals.is_used(place.local), - StatementKind::Nop => false, - _ => true, + StatementKind::Assign(box (place, _)) + | StatementKind::SetDiscriminant { box place, .. } + | StatementKind::BackwardIncompatibleDropHint { box place, .. } + | StatementKind::Deinit(box place) => used_locals.is_used(place.local), + _ => continue, }; - - if !keep { - trace!("removing statement {:?}", statement); - modified = true; - used_locals.statement_removed(statement); + if keep_statement { + continue; } - - keep - }); + trace!("removing statement {:?}", statement); + modified = true; + used_locals.statement_removed(statement); + statement.make_nop(true); + } + data.strip_nops(); } } } @@ -660,3 +661,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { *l = self.map[*l].unwrap(); } } + +pub(crate) struct UsedInStmtLocals { + pub(crate) locals: DenseBitSet, +} + +impl UsedInStmtLocals { + pub(crate) fn new(body: &Body<'_>) -> Self { + let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) }; + this.visit_body(body); + this + } + + pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) { + for data in body.basic_blocks.as_mut_preserves_cfg() { + // Remove unnecessary StorageLive and StorageDead annotations. + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + self.locals.contains(*local) + } + _ => continue, + }; + if keep_statement { + continue; + } + statement.make_nop(true); + } + } + } +} + +impl<'tcx> Visitor<'tcx> for UsedInStmtLocals { + fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { + if matches!(context, PlaceContext::NonUse(_)) { + return; + } + self.locals.insert(local); + } +} diff --git a/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff index ff18df1efcfc9..d584de6861c0c 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff @@ -19,10 +19,6 @@ - _3 = copy _2; - _2 = copy _1; - _1 = copy _5; -+ nop; -+ nop; -+ nop; -+ nop; _4 = cond() -> [return: bb1, unwind continue]; } diff --git a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff index d823241bc620a..2a793e24990ae 100644 --- a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff @@ -15,7 +15,6 @@ StorageLive(_2); - _2 = &((*_1).2: i32); + // DBG: _2 = &((*_1).2: i32); -+ nop; StorageLive(_3); StorageLive(_4); _4 = &((*_1).0: i32); diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff index 0547a42cab246..af28c1b9aa87a 100644 --- a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -12,14 +12,13 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); _3 = deref_copy (_1.1: &Foo); - _2 = &((*_3).2: i32); + // DBG: _2 = &((*_3).2: i32); -+ nop; _4 = deref_copy (_1.1: &Foo); _0 = copy ((*_4).0: i32); - StorageDead(_2); +- StorageDead(_2); return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff index 7a1ac92f6dde9..d4a73351ee4a5 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -14,13 +14,11 @@ - - bb1: { - // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff index fa4d3aebf8384..1c12358ad8934 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -17,16 +17,13 @@ - bb1: { (*_2) = const true; // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff index b01c91f196b65..de8e5612c8788 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -16,16 +16,13 @@ - - bb1: { // DBG: _2 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _3 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _4 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff index caa90d79a1c27..11372a262a76e 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff @@ -17,13 +17,11 @@ bb1: { - // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb3; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); return; } @@ -32,7 +30,6 @@ + bb2: { + // DBG: _3 = &((*_1).0: i32); // DBG: _5 = &((*_1).2: i32); -- nop; _0 = copy ((*_1).0: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff index d8c36990a5ff0..0c6a75237d8b2 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff @@ -16,16 +16,13 @@ - - bb1: { // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index f7729c7c5fa6d..7fa22524f0082 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -64,9 +64,7 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ // DBG: _11 = &((*_6).0: alloc::raw_vec::RawVec); + StorageLive(_12); -+ // DBG: _12 = &(((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_13); + _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _9 = copy _13 as *mut A (Transmute); @@ -92,7 +90,6 @@ _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; + StorageLive(_17); -+ StorageLive(_18); + _17 = discriminant((*_5)); + switchInt(move _17) -> [0: bb5, otherwise: bb6]; } @@ -118,7 +115,6 @@ + } + + bb5: { -+ StorageDead(_18); + StorageDead(_17); StorageDead(_5); return; diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff index 1832427642528..34f89da19f51f 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff @@ -27,13 +27,11 @@ _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind continue]; + StorageLive(_6); -+ StorageLive(_7); + _6 = discriminant((*_5)); + switchInt(move _6) -> [0: bb2, otherwise: bb3]; } bb2: { -+ StorageDead(_7); + StorageDead(_6); StorageDead(_5); return; diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 649b5a652027d..bb81cb4e2600a 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -129,11 +129,8 @@ _10 = deref_copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; -+ StorageLive(_11); -+ StorageLive(_15); + StorageLive(_16); + StorageLive(_25); -+ StorageLive(_27); + StorageLive(_30); + StorageLive(_31); + StorageLive(_32); @@ -166,11 +163,8 @@ + StorageDead(_32); + StorageDead(_31); + StorageDead(_30); -+ StorageDead(_27); + StorageDead(_25); + StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_11); StorageDead(_9); StorageDead(_8); StorageDead(_7); @@ -223,17 +217,13 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_44); -+ StorageLive(_45); + StorageLive(_49); + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ // DBG: _45 = &_19; + StorageLive(_46); -+ // DBG: _46 = &(_19.0: &mut std::future::Ready<()>); + _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_46); -+ // DBG: _43 = &((*_44).0: std::option::Option<()>); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); @@ -315,7 +305,6 @@ + _18 = Poll::<()>::Ready(move _41); + StorageDead(_41); + StorageDead(_49); -+ StorageDead(_45); + StorageDead(_44); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 32021b20ef44b..520c54824e150 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -131,11 +131,8 @@ _10 = deref_copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; -+ StorageLive(_11); -+ StorageLive(_15); + StorageLive(_16); + StorageLive(_25); -+ StorageLive(_27); + StorageLive(_30); + StorageLive(_31); + StorageLive(_32); @@ -180,11 +177,8 @@ + StorageDead(_32); + StorageDead(_31); + StorageDead(_30); -+ StorageDead(_27); + StorageDead(_25); + StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_11); StorageDead(_9); StorageDead(_8); StorageDead(_7); @@ -240,17 +234,13 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_46); -+ StorageLive(_47); + StorageLive(_51); + StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ // DBG: _47 = &_19; + StorageLive(_48); -+ // DBG: _48 = &(_19.0: &mut std::future::Ready<()>); + _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_48); -+ // DBG: _45 = &((*_46).0: std::option::Option<()>); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); @@ -356,7 +346,6 @@ + _18 = Poll::<()>::Ready(move _43); + StorageDead(_43); + StorageDead(_51); -+ StorageDead(_47); + StorageDead(_46); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff index ac88fe67bb86f..3ea7387a48d3c 100644 --- a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff +++ b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff @@ -30,7 +30,6 @@ StorageLive(_4); StorageLive(_5); _5 = copy _1; - nop; - StorageLive(_14); - _14 = BitAnd(copy _5, const 255_u32); - _4 = BitOr(const 0_u32, move _14); diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff index 96c3cae2d334a..832db856b2cf9 100644 --- a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff +++ b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff @@ -30,7 +30,6 @@ StorageLive(_4); StorageLive(_5); _5 = copy _1; - nop; - StorageLive(_14); - _14 = BitAnd(copy _5, const 255_u32); - _4 = BitOr(const 0_u32, move _14); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index a294c1b4588db..614d9ad440d20 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -33,15 +33,9 @@ } bb2: { - StorageLive(_5); // DBG: _5 = &(*_2)[0 of 3]; - StorageLive(_6); // DBG: _6 = &(*_2)[1 of 3]; - StorageLive(_7); // DBG: _7 = &(*_2)[2 of 3]; - StorageDead(_7); - StorageDead(_6); - StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index 691271cf94edf..57a88cf898412 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -33,15 +33,9 @@ } bb2: { - StorageLive(_5); // DBG: _5 = &(*_2)[0 of 3]; - StorageLive(_6); // DBG: _6 = &(*_2)[1 of 3]; - StorageLive(_7); // DBG: _7 = &(*_2)[2 of 3]; - StorageDead(_7); - StorageDead(_6); - StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir index d4248f5d87fbc..b5c23822162c9 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir index a834932df5a82..f22b8835735d1 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir index d4248f5d87fbc..b5c23822162c9 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir index a834932df5a82..f22b8835735d1 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 23dbc066dd528..83478e60b5d4e 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -5,9 +5,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let _8: std::option::Option; - let mut _10: u16; - let mut _11: &std::option::Option; + let mut _8: u16; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -15,9 +13,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; - let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _9: (); + let _7: (); } } } @@ -38,8 +35,6 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); - StorageLive(_11); - StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -58,55 +53,34 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); - StorageLive(_7); - _7 = AddUnchecked(copy _1, copy _4); - _8 = Option::::Some(move _7); - StorageDead(_7); - // DBG: _11 = &_8; - StorageDead(_8); - StorageDead(_11); goto -> bb7; } bb3: { - _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb6: { - StorageDead(_8); - StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { - StorageLive(_10); - _10 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _10); - StorageDead(_10); + StorageLive(_8); + _8 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _8); + StorageDead(_8); StorageDead(_4); return; } } - -ALLOC0 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} - -ALLOC1 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac15f070597ea..ac7a6e0445191 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -5,9 +5,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let _8: std::option::Option; - let mut _10: u16; - let mut _11: &std::option::Option; + let mut _8: u16; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -15,9 +13,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; - let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _9: (); + let _7: (); } } } @@ -38,8 +35,6 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); - StorageLive(_11); - StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -58,55 +53,34 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); - StorageLive(_7); - _7 = AddUnchecked(copy _1, copy _4); - _8 = Option::::Some(move _7); - StorageDead(_7); - // DBG: _11 = &_8; - StorageDead(_8); - StorageDead(_11); goto -> bb7; } bb3: { - _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb6: { - StorageDead(_8); - StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { - StorageLive(_10); - _10 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _10); - StorageDead(_10); + StorageLive(_8); + _8 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _8); + StorageDead(_8); StorageDead(_4); return; } } - -ALLOC0 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} - -ALLOC1 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir index 66239a0ef6637..1e6e2ee1b8b73 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir @@ -12,10 +12,8 @@ fn clone_as_copy(_1: &NestCopy) -> NestCopy { } bb0: { - StorageLive(_2); // DBG: _2 = &((*_1).1: AllCopy); _0 = copy (*_1); - StorageDead(_2); return; } } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index aa03cec070ad3..76bb49bc9c1b3 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -5,58 +5,28 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { let mut _0: Enum1; scope 1 (inlined ::clone) { debug self => _1; - let mut _2: isize; - let _3: &AllCopy; - let _4: &NestCopy; + let _2: &AllCopy; + let _3: &NestCopy; scope 2 { - debug __self_0 => _3; + debug __self_0 => _2; scope 6 (inlined ::clone) { - debug self => _3; + debug self => _2; } } scope 3 { - debug __self_0 => _4; + debug __self_0 => _3; scope 4 (inlined ::clone) { - debug self => _4; - let _5: &AllCopy; + debug self => _3; + let _4: &AllCopy; scope 5 (inlined ::clone) { - debug self => _5; + debug self => _4; } } } } bb0: { - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; - } - - bb1: { - // DBG: _3 = &(((*_1) as A).0: AllCopy); _0 = copy (*_1); - goto -> bb3; - } - - bb2: { - // DBG: _4 = &(((*_1) as B).0: NestCopy); - StorageLive(_5); - // DBG: _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); - StorageDead(_5); - _0 = copy (*_1); - goto -> bb3; - } - - bb3: { - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); return; } - - bb4: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.rs b/tests/mir-opt/pre-codegen/clone_as_copy.rs index f5ff1854d387d..00f24754d5913 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.rs +++ b/tests/mir-opt/pre-codegen/clone_as_copy.rs @@ -25,19 +25,19 @@ enum Enum1 { // EMIT_MIR clone_as_copy.clone_as_copy.PreCodegen.after.mir fn clone_as_copy(v: &NestCopy) -> NestCopy { // CHECK-LABEL: fn clone_as_copy( - // CHECK-NOT: = AllCopy { {{.*}} }; - // CHECK-NOT: = NestCopy { {{.*}} }; - // CHECK: _0 = copy (*_1); - // CHECK: return; + // CHECK: let [[DEAD_VAR:_.*]]: &AllCopy; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[DEAD_VAR]] = &((*_1).1: AllCopy) + // CHECK-NEXT: _0 = copy (*_1); + // CHECK-NEXT: return; v.clone() } -// FIXME: We can merge into exactly one assignment statement. // EMIT_MIR clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir fn enum_clone_as_copy(v: &Enum1) -> Enum1 { // CHECK-LABEL: fn enum_clone_as_copy( - // CHECK-NOT: = Enum1:: - // CHECK: _0 = copy (*_1); - // CHECK: _0 = copy (*_1); + // CHECK: bb0: { + // CHECK-NEXT: _0 = copy (*_1); + // CHECK-NEXT: return; v.clone() } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 0dd2125dbe0d2..ba6ce0ee5286f 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 0dd2125dbe0d2..ba6ce0ee5286f 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 0dd2125dbe0d2..ba6ce0ee5286f 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index 0dd2125dbe0d2..ba6ce0ee5286f 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index c291366a694e7..9ceba9444b8da 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,7 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = AlignOf(T); // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index b1af3141ee610..beb7b936ccf74 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -60,9 +60,7 @@ fn int_range(_1: usize, _2: usize) -> () { StorageLive(_9); // DBG: _12 = &_3; StorageLive(_6); - StorageLive(_13); // DBG: _13 = &(_3.0: usize); - StorageLive(_14); // DBG: _14 = &(_3.1: usize); StorageLive(_4); _4 = copy (_3.0: usize); @@ -75,16 +73,12 @@ fn int_range(_1: usize, _2: usize) -> () { } bb2: { - StorageDead(_14); - StorageDead(_13); StorageDead(_6); StorageDead(_9); return; } bb3: { - StorageDead(_14); - StorageDead(_13); _7 = copy (_3.0: usize); StorageLive(_8); _8 = AddUnchecked(copy _7, const 1_usize); diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index 03cdc221c3086..f453741dc6c78 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -171,12 +171,9 @@ fn vec_move(_1: Vec) -> () { bb0: { StorageLive(_22); - StorageLive(_29); StorageLive(_6); StorageLive(_7); - StorageLive(_33); StorageLive(_11); - StorageLive(_35); StorageLive(_20); StorageLive(_5); StorageLive(_4); @@ -184,30 +181,18 @@ fn vec_move(_1: Vec) -> () { StorageLive(_2); _2 = ManuallyDrop::> { value: copy _1 }; StorageLive(_3); - StorageLive(_30); // DBG: _30 = &_2; // DBG: _29 = &(_2.0: std::vec::Vec); - StorageDead(_30); - StorageLive(_39); // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_40); // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); - StorageDead(_40); - StorageDead(_39); _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); StorageDead(_3); - StorageLive(_31); - StorageLive(_32); // DBG: _32 = &_2; - StorageDead(_32); // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_41); // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy _4 as *const impl Sized (Transmute); _6 = NonNull:: { pointer: copy _5 }; - StorageDead(_41); - StorageDead(_31); _7 = copy _4 as *mut impl Sized (Transmute); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -215,10 +200,8 @@ fn vec_move(_1: Vec) -> () { bb1: { StorageLive(_10); StorageLive(_8); - StorageLive(_36); // DBG: _36 = &_2; // DBG: _35 = &(_2.0: std::vec::Vec); - StorageDead(_36); _8 = copy ((_2.0: std::vec::Vec).1: usize); StorageLive(_9); _9 = Le(copy _8, const ::MAX_SLICE_LEN); @@ -233,10 +216,8 @@ fn vec_move(_1: Vec) -> () { bb2: { StorageLive(_12); - StorageLive(_34); // DBG: _34 = &_2; // DBG: _33 = &(_2.0: std::vec::Vec); - StorageDead(_34); _12 = copy ((_2.0: std::vec::Vec).1: usize); StorageLive(_13); _13 = Le(copy _12, const ::MAX_SLICE_LEN); @@ -264,12 +245,8 @@ fn vec_move(_1: Vec) -> () { } bb4: { - StorageLive(_37); - StorageLive(_38); // DBG: _38 = &_2; - StorageDead(_38); // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_42); // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); StorageLive(_19); _19 = SizeOf(impl Sized); @@ -291,20 +268,15 @@ fn vec_move(_1: Vec) -> () { bb7: { StorageDead(_19); - StorageDead(_42); - StorageDead(_37); _22 = std::vec::IntoIter:: { buf: copy _6, phantom: const ZeroSized: PhantomData, cap: move _20, alloc: const ManuallyDrop:: {{ value: std::alloc::Global }}, ptr: copy _6, end: copy _11 }; StorageDead(_2); StorageDead(_17); StorageDead(_4); StorageDead(_5); StorageDead(_20); - StorageDead(_35); StorageDead(_11); - StorageDead(_33); StorageDead(_7); StorageDead(_6); - StorageDead(_29); StorageLive(_23); _23 = move _22; goto -> bb8; diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index 9d4bacef954ef..86c729b3e95e3 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -70,9 +70,7 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 // DBG: _15 = &((*_3).2: usize); // DBG: _16 = &((*_3).3: usize); StorageLive(_6); - StorageLive(_17); // DBG: _17 = &_13; - StorageLive(_18); // DBG: _18 = &_15; _4 = copy ((*_3).0: usize); _5 = copy ((*_3).2: usize); @@ -81,18 +79,12 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb1: { - StorageDead(_18); - StorageDead(_17); goto -> bb4; } bb2: { - StorageDead(_18); - StorageDead(_17); StorageLive(_9); - StorageLive(_19); // DBG: _19 = &_16; - StorageLive(_20); // DBG: _20 = &_14; StorageLive(_7); _7 = copy ((*_3).3: usize); @@ -105,34 +97,24 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb3: { - StorageDead(_20); - StorageDead(_19); goto -> bb4; } bb4: { StorageLive(_10); - StorageLive(_21); // DBG: _21 = &_15; - StorageLive(_22); // DBG: _22 = &_13; _10 = Le(copy _5, copy _4); switchInt(move _10) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_22); - StorageDead(_21); _0 = const false; goto -> bb7; } bb6: { - StorageDead(_22); - StorageDead(_21); - StorageLive(_23); // DBG: _23 = &_14; - StorageLive(_24); // DBG: _24 = &_16; StorageLive(_11); _11 = copy ((*_3).1: usize); @@ -141,8 +123,6 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _0 = Le(move _11, move _12); StorageDead(_12); StorageDead(_11); - StorageDead(_24); - StorageDead(_23); goto -> bb7; } @@ -152,8 +132,6 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb8: { - StorageDead(_20); - StorageDead(_19); _0 = const true; goto -> bb9; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 1b2e7b7163cd8..104987b0fdda9 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -51,9 +51,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _16: bool; let mut _20: usize; let _22: &T; - let mut _34: &std::ptr::NonNull; - let mut _35: &std::ptr::NonNull; - let mut _36: &std::ptr::NonNull; scope 29 { let _12: *const T; scope 30 { @@ -189,11 +186,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb5: { StorageLive(_16); - StorageLive(_34); - // DBG: _34 = &_11; - StorageLive(_35); _13 = copy _12 as std::ptr::NonNull (Transmute); - // DBG: _35 = &_13; StorageLive(_14); _14 = copy _11 as *mut T (Transmute); StorageLive(_15); @@ -205,8 +198,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { - StorageDead(_35); - StorageDead(_34); StorageDead(_16); StorageLive(_18); StorageLive(_17); @@ -219,8 +210,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb7: { - StorageDead(_35); - StorageDead(_34); StorageDead(_16); StorageDead(_22); StorageDead(_13); @@ -266,13 +255,10 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb13: { - StorageLive(_36); - // DBG: _36 = &_11; StorageLive(_21); _21 = copy _11 as *const T (Transmute); _22 = &(*_21); StorageDead(_21); - StorageDead(_36); _23 = Option::<&T>::Some(copy _22); StorageDead(_22); StorageDead(_13); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index c18dd01a189ad..4d0e3548e7d6b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -23,9 +23,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; - let mut _27: &std::ptr::NonNull; - let mut _28: &std::ptr::NonNull; - let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -151,11 +148,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); - StorageLive(_27); - // DBG: _27 = &_10; - StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); - // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -167,8 +160,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -181,8 +172,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -224,13 +213,10 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { - StorageLive(_29); - // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); - StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 6334c611430ad..2b5d8c27d7109 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -23,9 +23,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; - let mut _27: &std::ptr::NonNull; - let mut _28: &std::ptr::NonNull; - let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -151,11 +148,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); - StorageLive(_27); - // DBG: _27 = &_10; - StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); - // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -167,8 +160,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -181,8 +172,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -224,13 +213,10 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { - StorageLive(_29); - // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); - StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 24c6618d31cae..3009be3f9dc67 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -23,8 +23,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _18: bool; let mut _19: *const T; let _32: &T; - let mut _38: &std::ptr::NonNull; - let mut _39: &std::ptr::NonNull; scope 20 { let _14: std::ptr::NonNull; let _20: usize; @@ -48,7 +46,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { let _26: std::ptr::NonNull; - let mut _40: &std::ptr::NonNull; scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { let mut _21: *mut *const T; let mut _22: *mut std::ptr::NonNull; @@ -190,10 +187,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - StorageLive(_38); - // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); - StorageLive(_39); - // DBG: _39 = &_14; StorageLive(_16); StorageLive(_15); _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); @@ -204,8 +197,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _18 = Eq(copy _16, copy _17); StorageDead(_17); StorageDead(_16); - StorageDead(_39); - StorageDead(_38); goto -> bb7; } @@ -222,7 +213,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb8: { StorageLive(_26); - StorageLive(_40); StorageLive(_28); StorageLive(_22); StorageLive(_23); @@ -275,12 +265,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_23); StorageDead(_22); StorageDead(_28); - // DBG: _40 = &_26; StorageLive(_31); _31 = copy _26 as *const T (Transmute); _32 = &(*_31); StorageDead(_31); - StorageDead(_40); StorageDead(_26); _33 = Option::<&T>::Some(copy _32); StorageDead(_18); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index e5c7bcca44b65..e40bff5ea3504 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -23,8 +23,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _18: bool; let mut _19: *const T; let _32: &T; - let mut _38: &std::ptr::NonNull; - let mut _39: &std::ptr::NonNull; scope 20 { let _14: std::ptr::NonNull; let _20: usize; @@ -48,7 +46,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { let _26: std::ptr::NonNull; - let mut _40: &std::ptr::NonNull; scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { let mut _21: *mut *const T; let mut _22: *mut std::ptr::NonNull; @@ -190,10 +187,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - StorageLive(_38); - // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); - StorageLive(_39); - // DBG: _39 = &_14; StorageLive(_16); StorageLive(_15); _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); @@ -204,8 +197,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _18 = Eq(copy _16, copy _17); StorageDead(_17); StorageDead(_16); - StorageDead(_39); - StorageDead(_38); goto -> bb7; } @@ -222,7 +213,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb8: { StorageLive(_26); - StorageLive(_40); StorageLive(_28); StorageLive(_22); StorageLive(_23); @@ -275,12 +265,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_23); StorageDead(_22); StorageDead(_28); - // DBG: _40 = &_26; StorageLive(_31); _31 = copy _26 as *const T (Transmute); _32 = &(*_31); StorageDead(_31); - StorageDead(_40); StorageDead(_26); _33 = Option::<&T>::Some(copy _32); StorageDead(_18); diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir index 18a3d8e7c4ad3..9b510380b10b2 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir @@ -6,8 +6,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; - let mut _9: &std::ptr::NonNull; - let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -43,10 +41,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); - StorageLive(_9); - // DBG: _9 = &((*_1).0: std::ptr::NonNull); - StorageLive(_10); - // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -57,8 +51,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_10); - StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir index 18a3d8e7c4ad3..9b510380b10b2 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir @@ -6,8 +6,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; - let mut _9: &std::ptr::NonNull; - let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -43,10 +41,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); - StorageLive(_9); - // DBG: _9 = &((*_1).0: std::ptr::NonNull); - StorageLive(_10); - // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -57,8 +51,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_10); - StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 590d4ac2c8381..62b738c36bf4b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -8,8 +8,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _7: bool; let mut _8: *mut T; let mut _21: &mut T; - let mut _22: &std::ptr::NonNull; - let mut _23: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _9: usize; @@ -33,7 +31,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut } scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { let mut _15: std::ptr::NonNull; - let mut _24: &mut std::ptr::NonNull; scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { let mut _10: *mut *mut T; let mut _11: *mut std::ptr::NonNull; @@ -88,10 +85,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb1: { _2 = copy ((*_1).1: *mut T); _3 = copy _2 as std::ptr::NonNull (Transmute); - StorageLive(_22); - // DBG: _22 = &((*_1).0: std::ptr::NonNull); - StorageLive(_23); - // DBG: _23 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -102,8 +95,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut _7 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_23); - StorageDead(_22); goto -> bb3; } @@ -120,7 +111,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb4: { StorageLive(_15); - StorageLive(_24); StorageLive(_17); StorageLive(_11); StorageLive(_12); @@ -173,12 +163,10 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut StorageDead(_12); StorageDead(_11); StorageDead(_17); - // DBG: _24 = &_15; StorageLive(_20); _20 = copy _15 as *mut T (Transmute); _21 = &mut (*_20); StorageDead(_20); - StorageDead(_24); StorageDead(_15); _0 = Option::<&mut T>::Some(copy _21); goto -> bb11; diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index 590d4ac2c8381..62b738c36bf4b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -8,8 +8,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _7: bool; let mut _8: *mut T; let mut _21: &mut T; - let mut _22: &std::ptr::NonNull; - let mut _23: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _9: usize; @@ -33,7 +31,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut } scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { let mut _15: std::ptr::NonNull; - let mut _24: &mut std::ptr::NonNull; scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { let mut _10: *mut *mut T; let mut _11: *mut std::ptr::NonNull; @@ -88,10 +85,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb1: { _2 = copy ((*_1).1: *mut T); _3 = copy _2 as std::ptr::NonNull (Transmute); - StorageLive(_22); - // DBG: _22 = &((*_1).0: std::ptr::NonNull); - StorageLive(_23); - // DBG: _23 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -102,8 +95,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut _7 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_23); - StorageDead(_22); goto -> bb3; } @@ -120,7 +111,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb4: { StorageLive(_15); - StorageLive(_24); StorageLive(_17); StorageLive(_11); StorageLive(_12); @@ -173,12 +163,10 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut StorageDead(_12); StorageDead(_11); StorageDead(_17); - // DBG: _24 = &_15; StorageLive(_20); _20 = copy _15 as *mut T (Transmute); _21 = &mut (*_20); StorageDead(_20); - StorageDead(_24); StorageDead(_15); _0 = Option::<&mut T>::Some(copy _21); goto -> bb11; diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index e9ed932cbafb6..cc0fce26149e3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -10,9 +10,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; - let mut _15: &std::ptr::NonNull; - let mut _16: &std::ptr::NonNull; - let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -70,11 +67,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); - StorageLive(_15); - // DBG: _15 = &_2; - StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); - // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -86,8 +79,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { - StorageDead(_16); - StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -103,8 +94,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { - StorageDead(_16); - StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -127,13 +116,10 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { - StorageLive(_17); - // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); - StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index e9ed932cbafb6..cc0fce26149e3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,9 +10,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; - let mut _15: &std::ptr::NonNull; - let mut _16: &std::ptr::NonNull; - let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -70,11 +67,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); - StorageLive(_15); - // DBG: _15 = &_2; - StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); - // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -86,8 +79,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { - StorageDead(_16); - StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -103,8 +94,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { - StorageDead(_16); - StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -127,13 +116,10 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { - StorageLive(_17); - // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); - StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/ui/extern/extern-types-field-offset.rs b/tests/ui/extern/extern-types-field-offset.rs index 7a5f36da20917..470ae07a0b552 100644 --- a/tests/ui/extern/extern-types-field-offset.rs +++ b/tests/ui/extern/extern-types-field-offset.rs @@ -23,12 +23,17 @@ fn main() { let x: &Newtype = unsafe { &*(&buf as *const _ as *const Newtype) }; // Projecting to the newtype works, because it is always at offset 0. let field = &x.0; + // Avoid being eliminated by DSE. + std::hint::black_box(field); let x: &S = unsafe { &*(&buf as *const _ as *const S) }; // Accessing sized fields is perfectly fine, even at non-zero offsets. let field = &x.i; + std::hint::black_box(field); let field = &x.j; + std::hint::black_box(field); // This needs to compute the field offset, but we don't know the type's alignment, // so this panics. let field = &x.a; + std::hint::black_box(field); } From 1bd89bd42e0bb6f29b8af5d6bdf3f756196bb8ee Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 18 Jun 2025 22:04:48 +0800 Subject: [PATCH 1157/1900] codegen: Generate `dbg_value` for the ref statement --- compiler/rustc_codegen_gcc/src/debuginfo.rs | 13 +- .../src/debuginfo/dwarf_const.rs | 3 + .../rustc_codegen_llvm/src/debuginfo/mod.rs | 53 +++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 9 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + .../rustc_codegen_ssa/src/mir/debuginfo.rs | 54 +++++- compiler/rustc_codegen_ssa/src/mir/operand.rs | 17 +- .../rustc_codegen_ssa/src/mir/statement.rs | 70 +++++++- .../rustc_codegen_ssa/src/traits/debuginfo.rs | 14 +- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 1 + tests/codegen-llvm/debuginfo-dse.rs | 160 ++++++++++++++++++ tests/debuginfo/opt/dead_refs.rs | 50 ++++++ 12 files changed, 431 insertions(+), 14 deletions(-) create mode 100644 tests/codegen-llvm/debuginfo-dse.rs create mode 100644 tests/debuginfo/opt/dead_refs.rs diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 4c8585192a1b1..0f015cc23f52f 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -29,13 +29,24 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size], - _fragment: Option>, + _fragment: &Option>, ) { // FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here. #[cfg(feature = "master")] _variable_alloca.set_location(_dbg_loc); } + fn dbg_var_value( + &mut self, + _dbg_var: Self::DIVariable, + _dbg_loc: Self::DILocation, + _value: Self::Value, + _direct_offset: Size, + _indirect_offsets: &[Size], + _fragment: &Option>, + ) { + } + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { // TODO(antoyo): insert reference to gdb debug scripts section global. } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs index 408429152223d..52d04625749b9 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64); /// Double-checked by a static assertion in `RustWrapper.cpp`. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; +// It describes the actual value of a source variable which might not exist in registers or in memory. +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_stack_value: u64 = 0x9f; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index af64e4ebed0f7..c6ad1c2e18ec7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -156,7 +156,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { variable_alloca: Self::Value, direct_offset: Size, indirect_offsets: &[Size], - fragment: Option>, + fragment: &Option>, ) { use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; @@ -187,7 +187,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) }; unsafe { - // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( di_builder, variable_alloca, @@ -199,6 +198,56 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { }; } + fn dbg_var_value( + &mut self, + dbg_var: &'ll DIVariable, + dbg_loc: &'ll DILocation, + value: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], + fragment: &Option>, + ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value}; + + // Convert the direct and indirect offsets and fragment byte range to address ops. + let mut addr_ops = SmallVec::<[u64; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(DW_OP_stack_value); + } + for &offset in indirect_offsets { + addr_ops.push(DW_OP_deref); + if offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(offset.bytes() as u64); + } + } + if let Some(fragment) = fragment { + // `DW_OP_LLVM_fragment` takes as arguments the fragment's + // offset and size, both of them in bits. + addr_ops.push(DW_OP_LLVM_fragment); + addr_ops.push(fragment.start.bits() as u64); + addr_ops.push((fragment.end - fragment.start).bits() as u64); + } + + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; + unsafe { + llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd( + di_builder, + value, + dbg_var, + addr_expr, + dbg_loc, + self.llbb(), + ); + } + } + fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { unsafe { llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e9f92267a7d4f..7fbba0294073f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1991,6 +1991,15 @@ unsafe extern "C" { Block: &'ll BasicBlock, ) -> &'ll DbgRecord; + pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>( + Builder: &DIBuilder<'ll>, + Val: &'ll Value, + VarInfo: &'ll Metadata, + Expr: &'ll Metadata, + DebugLoc: &'ll Metadata, + Block: &'ll BasicBlock, + ) -> &'ll DbgRecord; + pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>( Builder: &DIBuilder<'ll>, Scope: &'ll Metadata, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b2dc4fe32b0fe..e371f1a76231c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1320,6 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for statement in &data.statements { self.codegen_statement(bx, statement); } + self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos); let merging_succ = self.codegen_terminator(bx, bb, data.terminator()); if let MergingSucc::False = merging_succ { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index b8f635ab78161..38bb6f24b1c3b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -253,6 +253,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { spill_slot } + // Indicates that local is set to a new value. The `layout` and `projection` are used to + // calculate the offset. + pub(crate) fn debug_new_val_to_local( + &self, + bx: &mut Bx, + local: mir::Local, + base: PlaceValue, + layout: TyAndLayout<'tcx>, + projection: &[mir::PlaceElem<'tcx>], + ) { + let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; + if !full_debug_info { + return; + } + + let vars = match &self.per_local_var_debug_info { + Some(per_local) => &per_local[local], + None => return, + }; + + let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = + calculate_debuginfo_offset(bx, projection, layout); + for var in vars.iter() { + let Some(dbg_var) = var.dbg_var else { + continue; + }; + let Some(dbg_loc) = self.dbg_loc(var.source_info) else { + continue; + }; + bx.dbg_var_value( + dbg_var, + dbg_loc, + base.llval, + direct_offset, + &indirect_offsets, + &var.fragment, + ); + } + } + + pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) { + let ty = self.monomorphize(self.mir.local_decls[local].ty); + let layout = bx.cx().layout_of(ty); + let to_backend_ty = bx.cx().immediate_backend_type(layout); + let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout); + self.debug_new_val_to_local(bx, local, place_ref.val, layout, &[]); + } + /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { @@ -424,7 +472,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { alloca.val.llval, Size::ZERO, &[Size::ZERO], - var.fragment, + &var.fragment, ); } else { bx.dbg_var_addr( @@ -433,7 +481,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base.val.llval, direct_offset, &indirect_offsets, - var.fragment, + &var.fragment, ); } } @@ -455,7 +503,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx); bx.clear_dbg_loc(); - bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment); + bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment); } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5f7f87fc69206..88a8e2a844cbc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -71,16 +71,23 @@ pub enum OperandValue { } impl OperandValue { + /// Return the data pointer and optional metadata as backend values + /// if this value can be treat as a pointer. + pub(crate) fn try_pointer_parts(self) -> Option<(V, Option)> { + match self { + OperandValue::Immediate(llptr) => Some((llptr, None)), + OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))), + OperandValue::Ref(_) | OperandValue::ZeroSized => None, + } + } + /// Treat this value as a pointer and return the data pointer and /// optional metadata as backend values. /// /// If you're making a place, use [`Self::deref`] instead. pub(crate) fn pointer_parts(self) -> (V, Option) { - match self { - OperandValue::Immediate(llptr) => (llptr, None), - OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)), - _ => bug!("OperandValue cannot be a pointer: {self:?}"), - } + self.try_pointer_parts() + .unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}")) } /// Treat this value as a pointer and return the place to which it points. diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 0a50d7f18dbef..99bb31a68b688 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,13 +1,17 @@ -use rustc_middle::mir::{self, NonDivergingIntrinsic}; -use rustc_middle::span_bug; +use rustc_middle::mir::{self, NonDivergingIntrinsic, RETURN_PLACE, StmtDebugInfo}; +use rustc_middle::{bug, span_bug}; +use rustc_target::callconv::PassMode; use tracing::instrument; use super::{FunctionCx, LocalRef}; +use crate::common::TypeKind; +use crate::mir::place::PlaceRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "debug", skip(self, bx))] pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) { + self.codegen_stmt_debuginfos(bx, &statement.debuginfos); self.set_debug_loc(bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(box (ref place, ref rvalue)) => { @@ -101,4 +105,66 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | mir::StatementKind::Nop => {} } } + + pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) { + match debuginfo { + StmtDebugInfo::AssignRef(dest, place) => { + let local_ref = match self.locals[place.local] { + LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => { + Some(place_ref) + } + LocalRef::Operand(operand_ref) => operand_ref + .val + .try_pointer_parts() + .map(|(pointer, _)| PlaceRef::new_sized(pointer, operand_ref.layout)), + LocalRef::PendingOperand => None, + } + .filter(|place_ref| { + // For the reference of an argument (e.x. `&_1`), it's only valid if the pass mode is indirect, and its reference is + // llval. + let local_ref_pass_mode = place.as_local().and_then(|local| { + if local == RETURN_PLACE { + None + } else { + self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode) + } + }); + matches!(local_ref_pass_mode, Some(&PassMode::Indirect {..}) | None) && + // Drop unsupported projections. + place.projection.iter().all(|p| p.can_use_in_debuginfo()) && + // Only pointers can be calculated addresses. + bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer + }); + if let Some(local_ref) = local_ref { + let (base_layout, projection) = if place.is_indirect_first_projection() { + // For `_n = &((*_1).0: i32);`, we are calculating the address of `_1.0`, so + // we should drop the deref projection. + let projected_ty = local_ref + .layout + .ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("deref of non-pointer {:?}", local_ref)); + let layout = bx.cx().layout_of(projected_ty); + (layout, &place.projection[1..]) + } else { + (local_ref.layout, place.projection.as_slice()) + }; + self.debug_new_val_to_local(bx, *dest, local_ref.val, base_layout, projection); + } else { + // If the address cannot be computed, use poison to indicate that the value has been optimized out. + self.debug_poison_to_local(bx, *dest); + } + } + } + } + + pub(crate) fn codegen_stmt_debuginfos( + &mut self, + bx: &mut Bx, + debuginfos: &[StmtDebugInfo<'tcx>], + ) { + for debuginfo in debuginfos { + self.codegen_stmt_debuginfo(bx, debuginfo); + } + } } diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index b9d4950e0ad36..a4da6c915deec 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes { indirect_offsets: &[Size], // Byte range in the `dbg_var` covered by this fragment, // if this is a fragment of a composite `DIVariable`. - fragment: Option>, + fragment: &Option>, + ); + fn dbg_var_value( + &mut self, + dbg_var: Self::DIVariable, + dbg_loc: Self::DILocation, + value: Self::Value, + direct_offset: Size, + // NB: each offset implies a deref (i.e. they're steps in a pointer chain). + indirect_offsets: &[Size], + // Byte range in the `dbg_var` covered by this fragment, + // if this is a fragment of a composite `DIVariable`. + fragment: &Option>, ); fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation); fn clear_dbg_loc(&mut self); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2b83ea24ac61c..e38474f09ff7a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -58,6 +58,7 @@ using namespace llvm::object; // This opcode is an LLVM detail that could hypothetically change (?), so // verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); +static_assert(dwarf::DW_OP_stack_value == 0x9f); // LLVMAtomicOrdering is already an enum - don't create another // one. diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs new file mode 100644 index 0000000000000..1fcb5fcfc543a --- /dev/null +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -0,0 +1,160 @@ +//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir +//@ revisions: CODEGEN OPTIMIZED +//@[CODEGEN] compile-flags: -Cno-prepopulate-passes +// ignore-tidy-linelength + +#![crate_type = "lib"] + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Foo(i32, i64, i32); + +#[no_mangle] +fn r#ref(ref_foo: &Foo) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @ref + // CHECK-SAME: (ptr {{.*}} [[ARG_ref_foo:%.*]]) + // OPTIMIZED: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr poison, [[VAR_invalid_ref_of_ref_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v0:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let invalid_ref_of_ref_foo = &ref_foo; + let ref_v0 = &ref_foo.0; + let ref_v1 = &ref_foo.1; + let ref_v2 = &ref_foo.2; + ref_foo.0 +} + +#[no_mangle] +pub fn dead_first(dead_first_foo: &Foo) -> &i32 { + // CHECK-LABEL: def {{.*}} ptr @dead_first + // CHECK-SAME: (ptr {{.*}} [[ARG_dead_first_foo:%.*]]) + // CODEGEN: #dbg_declare(ptr %dead_first_foo.dbg.spill, [[ARG_dead_first_foo:![0-9]+]], !DIExpression() + // OPTIMIZED: #dbg_value(ptr %dead_first_foo, [[ARG_dead_first_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr %dead_first_foo, [[VAR_dead_first_v0:![0-9]+]], !DIExpression() + // CHECK: %dead_first_v0 = getelementptr{{.*}} i8, ptr %dead_first_foo, i64 16 + // CODEGEN: #dbg_declare(ptr %dead_first_v0.dbg.spill, [[VAR_dead_first_v0]], !DIExpression() + // OPTIMIZED: #dbg_value(ptr %dead_first_v0, [[VAR_dead_first_v0]], !DIExpression() + let mut dead_first_v0 = &dead_first_foo.0; + dead_first_v0 = &dead_first_foo.2; + dead_first_v0 +} + +#[no_mangle] +fn ptr(ptr_foo: Foo) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @ptr + // CHECK-SAME: (ptr {{.*}} [[ARG_ptr_foo:%.*]]) + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[ref_ptr_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v0:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_ptr_foo = &ptr_foo; + let ptr_v0 = &ptr_foo.0; + let ptr_v1 = &ptr_foo.1; + let ptr_v2 = &ptr_foo.2; + ptr_foo.2 +} + +#[no_mangle] +fn no_ptr(val: i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @no_ptr + // CODEGEN: #dbg_value(ptr poison, [[VAR_val_ref:![0-9]+]], !DIExpression() + let val_ref = &val; + val +} + +#[no_mangle] +pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { + // CHECK-LABEL: define void @fragment + // CHECK-SAME: (ptr {{.*}}, ptr {{.*}} [[ARG_fragment_v1:%.*]], ptr {{.*}} [[ARG_fragment_v2:%.*]]) + // CHECK: #dbg_declare(ptr [[ARG_fragment_v1]] + // CHECK-NEXT: #dbg_declare(ptr [[ARG_fragment_v2]] + // CHECK-NEXT: #dbg_value(ptr [[ARG_fragment_v2]], [[VAR_fragment_f:![0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 0, 64) + // CHECK-NEXT: #dbg_value(ptr [[ARG_fragment_v1]], [[VAR_fragment_f:![0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 64, 64) + let fragment_f = || { + fragment_v2 = fragment_v1; + }; + fragment_v2 = fragment_v1; + fragment_v2 +} + +#[no_mangle] +pub fn tuple(foo: (i32, &Foo)) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @tuple + // CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]]) + // CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let tuple_dead = &foo.1.2; + foo.1.0 +} + +pub struct ZST; + +#[no_mangle] +pub fn zst(zst: ZST, v: &i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @zst + // CHECK: #dbg_value(ptr poison, [[VAR_zst_ref:![0-9]+]], !DIExpression() + let zst_ref = &zst; + *v +} + +#[no_mangle] +fn index(slice: &[i32; 4], idx: usize) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @index + // CHECK: bb1: + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() + // CODEGEN: bb3: + // CHECK-NEXT: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() + let index_from_var = &slice[idx]; + let [ref const_index_from_start, .., ref const_index_from_end] = slice[..] else { + return 0; + }; + slice[0] +} + +unsafe extern "Rust" { + safe fn opaque_inner(_: *const core::ffi::c_void); +} + +#[inline(never)] +pub fn opaque_use(p: &T) { + opaque_inner(&raw const p as *const _); +} + +#[no_mangle] +pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @non_arg_ref + // CHECK: #dbg_value(ptr %non_arg_ref_scalar, [[VAR_non_arg_ref_scalar_ref:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref_2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let non_arg_ref_scalar = scalar; + let non_arg_ref_foo = foo; + opaque_use(&non_arg_ref_scalar); + opaque_use(&non_arg_ref_foo); + let non_arg_ref_scalar_ref = &non_arg_ref_scalar; + let non_arg_ref_foo_ref = &non_arg_ref_foo; + let non_arg_ref_foo_ref_2 = &non_arg_ref_foo.2; + *a +} + +// CHECK-DAG: [[VAR_invalid_ref_of_ref_foo]] = !DILocalVariable(name: "invalid_ref_of_ref_foo" +// OPTIMIZED-DAG: [[VAR_ref_foo]] = !DILocalVariable(name: "ref_foo" +// CHECK-DAG: [[VAR_ref_v0]] = !DILocalVariable(name: "ref_v0" +// CHECK-DAG: [[VAR_ref_v1]] = !DILocalVariable(name: "ref_v1" +// CHECK-DAG: [[VAR_ref_v2]] = !DILocalVariable(name: "ref_v2" +// CHECK-DAG: [[ref_ptr_foo]] = !DILocalVariable(name: "ref_ptr_foo" +// CHECK-DAG: [[VAR_ptr_v0]] = !DILocalVariable(name: "ptr_v0" +// CHECK-DAG: [[VAR_ptr_v1]] = !DILocalVariable(name: "ptr_v1" +// CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" +// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" +// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" +// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" +// CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" +// CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" +// CHECK-DAG: [[VAR_index_from_var]] = !DILocalVariable(name: "index_from_var" +// CHECK-DAG: [[VAR_const_index_from_start]] = !DILocalVariable(name: "const_index_from_start" +// CHECK-DAG: [[VAR_const_index_from_end]] = !DILocalVariable(name: "const_index_from_end" +// CHECK-DAG: [[VAR_zst_ref]] = !DILocalVariable(name: "zst_ref" +// CHECK-DAG: [[VAR_non_arg_ref_scalar_ref]] = !DILocalVariable(name: "non_arg_ref_scalar_ref" +// CHECK-DAG: [[VAR_non_arg_ref_foo_ref]] = !DILocalVariable(name: "non_arg_ref_foo_ref" +// CHECK-DAG: [[VAR_non_arg_ref_foo_ref_2]] = !DILocalVariable(name: "non_arg_ref_foo_ref_2" diff --git a/tests/debuginfo/opt/dead_refs.rs b/tests/debuginfo/opt/dead_refs.rs new file mode 100644 index 0000000000000..61e741573346e --- /dev/null +++ b/tests/debuginfo/opt/dead_refs.rs @@ -0,0 +1,50 @@ +//@ min-lldb-version: 1800 +//@ min-gdb-version: 13.0 +//@ compile-flags: -g -Copt-level=3 +//@ disable-gdb-pretty-printers + +// Checks that we still can access dead variables from debuginfos. + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:print *ref_v0 +// gdb-check:$1 = 0 + +// gdb-command:print *ref_v1 +// gdb-check:$2 = 1 + +// gdb-command:print *ref_v2 +// gdb-check:$3 = 2 + +// === LLDB TESTS ================================================================================== + +// lldb-command:run +// lldb-command:v *ref_v0 +// lldb-check:[...] 0 + +// lldb-command:v *ref_v1 +// lldb-check:[...] 1 + +// lldb-command:v *ref_v2 +// lldb-check:[...] 2 + +#![allow(unused_variables)] + +use std::hint::black_box; + +pub struct Foo(i32, i64, i32); + +#[inline(never)] +#[no_mangle] +fn test_ref(ref_foo: &Foo) -> i32 { + let ref_v0 = &ref_foo.0; + let ref_v1 = &ref_foo.1; + let ref_v2 = &ref_foo.2; + ref_foo.0 // #break +} + +fn main() { + let foo = black_box(Foo(0, 1, 2)); + black_box(test_ref(&foo)); +} From 8da04285cf6fe61587e16155a8b224dba64bf0be Mon Sep 17 00:00:00 2001 From: dianqk Date: Sat, 19 Jul 2025 18:49:47 +0800 Subject: [PATCH 1158/1900] mir-opt: Eliminate dead statements even if they are used by debuginfos --- .../rustc_codegen_ssa/src/mir/statement.rs | 3 ++ compiler/rustc_middle/src/mir/pretty.rs | 3 ++ compiler/rustc_middle/src/mir/statement.rs | 15 ++++++ compiler/rustc_middle/src/mir/visit.rs | 7 +++ compiler/rustc_mir_dataflow/src/debuginfo.rs | 10 ++-- .../rustc_mir_dataflow/src/impls/liveness.rs | 3 -- compiler/rustc_mir_transform/src/simplify.rs | 16 ++++++ .../src/strip_debuginfo.rs | 17 +++++++ tests/codegen-llvm/debuginfo-dse.rs | 23 +++++++-- tests/mir-opt/dead-store-elimination/ref.rs | 4 +- ...ef.tuple.DeadStoreElimination-initial.diff | 2 +- .../inline_shims.drop.Inline.panic-abort.diff | 50 ++++++++----------- ...y.run2-{closure#0}.Inline.panic-abort.diff | 4 -- ....run2-{closure#0}.Inline.panic-unwind.diff | 4 -- ...d_place.invalid_place.PreCodegen.after.mir | 13 +++++ .../pre-codegen/dead_on_invalid_place.rs | 27 ++++++++++ .../loops.vec_move.PreCodegen.after.mir | 12 ++--- ...variant_a-{closure#0}.PreCodegen.after.mir | 42 +++++++--------- ..._to_slice.PreCodegen.after.panic-abort.mir | 8 --- ...to_slice.PreCodegen.after.panic-unwind.mir | 8 --- 20 files changed, 170 insertions(+), 101 deletions(-) create mode 100644 tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/dead_on_invalid_place.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 99bb31a68b688..80f4f0fcda1ce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -155,6 +155,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.debug_poison_to_local(bx, *dest); } } + StmtDebugInfo::InvalidAssign(local) => { + self.debug_poison_to_local(bx, *local); + } } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8cf89f778a2aa..d87e3abe3b2ea 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -844,6 +844,9 @@ impl Debug for StmtDebugInfo<'_> { StmtDebugInfo::AssignRef(local, place) => { write!(fmt, "{local:?} = &{place:?}") } + StmtDebugInfo::InvalidAssign(local) => { + write!(fmt, "{local:?} = &?") + } } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 67b4448cfcddd..f310e1e576250 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -527,6 +527,20 @@ impl<'tcx> PlaceRef<'tcx> { }) } + /// Return the place accessed locals that include the base local. + pub fn accessed_locals(self) -> impl Iterator { + std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj { + ProjectionElem::Index(local) => Some(*local), + ProjectionElem::Deref + | ProjectionElem::Field(_, _) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(_, _) + | ProjectionElem::OpaqueCast(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => None, + })) + } + /// Generates a new place by appending `more_projections` to the existing ones /// and interning the result. pub fn project_deeper( @@ -1057,4 +1071,5 @@ impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> { #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum StmtDebugInfo<'tcx> { AssignRef(Local, Place<'tcx>), + InvalidAssign(Local), } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 47ae23afd556e..9654e189f2edf 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -406,6 +406,13 @@ macro_rules! make_mir_visitor { location ); }, + StmtDebugInfo::InvalidAssign(local) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + } } } diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index 274c5943946c2..0d25ce91c9a9e 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -5,16 +5,16 @@ use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); - visitor.visit_body(body); + for debuginfo in body.var_debug_info.iter() { + visitor.visit_var_debug_info(debuginfo); + } visitor.0 } struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { - fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) { - if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) { - self.0.insert(local); - } + fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { + self.0.insert(local); } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 037e2720f3651..f6aaa65ad9fdd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -287,9 +287,6 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { if let Some(destination) = Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) && !state.contains(destination.local) - // FIXME: We can eliminate the statement, but we'll need the statements it depends on - // for debuginfos. We need a way to handle this. - && !self.debuginfo_locals.contains(destination.local) { // This store is dead return; diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 0f23482ea8b1b..8b5efb7420582 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -657,6 +657,22 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { self.tcx } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &mut StmtDebugInfo<'tcx>, + location: Location, + ) { + match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, place) => { + if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) { + *stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local); + } + } + StmtDebugInfo::InvalidAssign(_) => {} + } + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { *l = self.map[*l].unwrap(); } diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 9ede8aa79c44a..7fec25ccb5218 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -1,5 +1,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_session::config::MirStripDebugInfo; /// Conditionally remove some of the VarDebugInfo in MIR. @@ -30,6 +31,22 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE, ) }); + + let debuginfo_locals = debuginfo_locals(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + for stmt in data.statements.iter_mut() { + stmt.debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } + data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } } fn is_required(&self) -> bool { diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs index 1fcb5fcfc543a..78e145a3cdf12 100644 --- a/tests/codegen-llvm/debuginfo-dse.rs +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir +//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir -Zmerge-functions=disabled //@ revisions: CODEGEN OPTIMIZED //@[CODEGEN] compile-flags: -Cno-prepopulate-passes // ignore-tidy-linelength @@ -9,6 +9,13 @@ #[derive(Clone, Copy)] pub struct Foo(i32, i64, i32); +#[repr(C)] +pub struct Bar<'a> { + a: i32, + b: i64, + foo: &'a Foo, +} + #[no_mangle] fn r#ref(ref_foo: &Foo) -> i32 { // CHECK-LABEL: define{{.*}} i32 @ref @@ -78,11 +85,20 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { fragment_v2 } +#[no_mangle] +pub fn deref(bar: Bar) -> i32 { + // CHECK-LABEL: define {{.*}} i32 @deref + // We are unable to represent dereference within this expression. + // CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression() + let deref_dead = &bar.foo.2; + bar.a +} + #[no_mangle] pub fn tuple(foo: (i32, &Foo)) -> i32 { // CHECK-LABEL: define{{.*}} i32 @tuple - // CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]]) - // CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + // Although there is no dereference here, there is a dereference in the MIR. + // CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression() let tuple_dead = &foo.1.2; foo.1.0 } @@ -148,6 +164,7 @@ pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { // CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" // CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" // CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" +// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" // CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" // CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" // CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" diff --git a/tests/mir-opt/dead-store-elimination/ref.rs b/tests/mir-opt/dead-store-elimination/ref.rs index 18d9ea8b84d7d..2d3200edab9f6 100644 --- a/tests/mir-opt/dead-store-elimination/ref.rs +++ b/tests/mir-opt/dead-store-elimination/ref.rs @@ -11,9 +11,7 @@ pub fn tuple(v: (i32, &Foo)) -> i32 { // CHECK-LABEL: fn tuple // CHECK: debug _dead => [[dead:_[0-9]+]]; // CHECK: bb0: - // FIXME: Preserve `tmp` for debuginfo, but we can merge it into the debuginfo. - // CHECK: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo); - // CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32) + // CHECK: DBG: [[dead]] = &((*_3).2: i32) let _dead = &v.1.c; v.1.a } diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff index af28c1b9aa87a..0b96569cbe4b1 100644 --- a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -13,7 +13,7 @@ bb0: { - StorageLive(_2); - _3 = deref_copy (_1.1: &Foo); +- _3 = deref_copy (_1.1: &Foo); - _2 = &((*_3).2: i32); + // DBG: _2 = &((*_3).2: i32); _4 = deref_copy (_1.1: &Foo); diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index 7fa22524f0082..9509739413b76 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -16,12 +16,10 @@ + let mut _9: *mut A; + let mut _10: usize; + scope 3 (inlined Vec::::as_mut_ptr) { -+ let mut _11: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { -+ let mut _12: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { -+ let mut _13: std::ptr::NonNull; ++ let mut _11: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + scope 8 (inlined NonNull::::cast::) { + scope 9 (inlined NonNull::::as_ptr) { @@ -41,15 +39,15 @@ + } + } + scope 14 (inlined drop_in_place::<[A]> - shim(Some([A]))) { -+ let mut _14: usize; -+ let mut _15: *mut A; -+ let mut _16: bool; ++ let mut _12: usize; ++ let mut _13: *mut A; ++ let mut _14: bool; + } + } + } + scope 15 (inlined drop_in_place::> - shim(Some(Option))) { -+ let mut _17: isize; -+ let mut _18: isize; ++ let mut _15: isize; ++ let mut _16: isize; + } bb0: { @@ -64,20 +62,16 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ StorageLive(_12); -+ StorageLive(_13); -+ _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _9 = copy _13 as *mut A (Transmute); -+ StorageDead(_13); -+ StorageDead(_12); ++ _11 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _9 = copy _11 as *mut A (Transmute); + StorageDead(_11); + _10 = copy ((*_6).1: usize); + _8 = *mut [A] from (copy _9, copy _10); + StorageDead(_9); ++ StorageLive(_12); ++ StorageLive(_13); + StorageLive(_14); -+ StorageLive(_15); -+ StorageLive(_16); -+ _14 = const 0_usize; ++ _12 = const 0_usize; + goto -> bb4; } @@ -89,33 +83,33 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_17); -+ _17 = discriminant((*_5)); -+ switchInt(move _17) -> [0: bb5, otherwise: bb6]; ++ StorageLive(_15); ++ _15 = discriminant((*_5)); ++ switchInt(move _15) -> [0: bb5, otherwise: bb6]; } bb2: { -+ StorageDead(_16); -+ StorageDead(_15); + StorageDead(_14); ++ StorageDead(_13); ++ StorageDead(_12); + StorageDead(_8); + StorageDead(_10); + drop(((*_4).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { -+ _15 = &raw mut (*_8)[_14]; -+ _14 = Add(move _14, const 1_usize); -+ drop((*_15)) -> [return: bb4, unwind unreachable]; ++ _13 = &raw mut (*_8)[_12]; ++ _12 = Add(move _12, const 1_usize); ++ drop((*_13)) -> [return: bb4, unwind unreachable]; + } + + bb4: { -+ _16 = Eq(copy _14, copy _10); -+ switchInt(move _16) -> [0: bb3, otherwise: bb2]; ++ _14 = Eq(copy _12, copy _10); ++ switchInt(move _14) -> [0: bb3, otherwise: bb2]; + } + + bb5: { -+ StorageDead(_17); ++ StorageDead(_15); StorageDead(_5); return; + } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index bb81cb4e2600a..0acb33febe52b 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -220,16 +220,12 @@ + StorageLive(_49); + StorageLive(_41); + StorageLive(_42); -+ StorageLive(_43); -+ StorageLive(_46); + _44 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_46); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); + ((*_44).0: std::option::Option<()>) = copy _47; + StorageDead(_47); -+ StorageDead(_43); + StorageLive(_48); + _48 = discriminant(_42); + switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 520c54824e150..98ee46c29b1be 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -237,16 +237,12 @@ + StorageLive(_51); + StorageLive(_43); + StorageLive(_44); -+ StorageLive(_45); -+ StorageLive(_48); + _46 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_48); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); + ((*_46).0: std::option::Option<()>) = copy _49; + StorageDead(_49); -+ StorageDead(_45); + StorageLive(_50); + _50 = discriminant(_44); + switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; diff --git a/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir new file mode 100644 index 0000000000000..4a2127178fb0b --- /dev/null +++ b/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir @@ -0,0 +1,13 @@ +// MIR for `invalid_place` after PreCodegen + +fn invalid_place(_1: bool) -> bool { + debug c1_ref => _2; + let mut _0: bool; + let mut _2: &bool; + + bb0: { + // DBG: _2 = &?; + _0 = copy _1; + return; + } +} diff --git a/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs b/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs new file mode 100644 index 0000000000000..5abe9fa43a5d2 --- /dev/null +++ b/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs @@ -0,0 +1,27 @@ +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR dead_on_invalid_place.invalid_place.PreCodegen.after.mir +#[custom_mir(dialect = "runtime")] +pub fn invalid_place(c: bool) -> bool { + // CHECK-LABEL: fn invalid_place + // CHECK: debug c1_ref => [[c1_ref:_[0-9]+]]; + // CHECK: bb0: { + // We cannot read the reference, since `c1` is dead. + // CHECK-NEXT: DBG: [[c1_ref]] = &? + // CHECK-NEXT: _0 = copy _1; + // CHECK-NEXT: return; + mir! { + let _c1_ref: &bool; + let c1: bool; + debug c1_ref => _c1_ref; + { + c1 = c; + _c1_ref = &c1; + RET = c; + Return() + } + } +} diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index f453741dc6c78..66eb1bcfaa665 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -56,11 +56,11 @@ fn vec_move(_1: Vec) -> () { scope 40 (inlined alloc::raw_vec::RawVec::::capacity) { debug self => _37; let mut _19: usize; - let mut _42: &alloc::raw_vec::RawVecInner; + let mut _39: &alloc::raw_vec::RawVecInner; scope 41 (inlined std::mem::size_of::) { } scope 42 (inlined alloc::raw_vec::RawVecInner::capacity) { - debug self => _42; + debug self => _39; debug elem_size => _19; let mut _21: core::num::niche_types::UsizeNoHighBit; scope 43 (inlined core::num::niche_types::UsizeNoHighBit::as_inner) { @@ -130,7 +130,6 @@ fn vec_move(_1: Vec) -> () { } scope 18 (inlined alloc::raw_vec::RawVec::::non_null) { debug self => _31; - let mut _41: &alloc::raw_vec::RawVecInner; scope 19 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _4: std::ptr::NonNull; scope 20 (inlined Unique::::cast::) { @@ -150,9 +149,7 @@ fn vec_move(_1: Vec) -> () { } scope 12 (inlined Vec::::allocator) { debug self => _29; - let mut _39: &alloc::raw_vec::RawVec; scope 13 (inlined alloc::raw_vec::RawVec::::allocator) { - let mut _40: &alloc::raw_vec::RawVecInner; scope 14 (inlined alloc::raw_vec::RawVecInner::allocator) { } } @@ -183,13 +180,10 @@ fn vec_move(_1: Vec) -> () { StorageLive(_3); // DBG: _30 = &_2; // DBG: _29 = &(_2.0: std::vec::Vec); - // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); StorageDead(_3); // DBG: _32 = &_2; // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy _4 as *const impl Sized (Transmute); _6 = NonNull:: { pointer: copy _5 }; @@ -247,7 +241,7 @@ fn vec_move(_1: Vec) -> () { bb4: { // DBG: _38 = &_2; // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + // DBG: _39 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); StorageLive(_19); _19 = SizeOf(impl Sized); switchInt(move _19) -> [0: bb5, otherwise: bb6]; diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index 86c729b3e95e3..2cab88182962f 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -71,21 +71,17 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 // DBG: _16 = &((*_3).3: usize); StorageLive(_6); // DBG: _17 = &_13; - // DBG: _18 = &_15; + // DBG: _18 = &?; _4 = copy ((*_3).0: usize); _5 = copy ((*_3).2: usize); _6 = Le(copy _4, copy _5); - switchInt(move _6) -> [0: bb1, otherwise: bb2]; + switchInt(move _6) -> [0: bb2, otherwise: bb1]; } bb1: { - goto -> bb4; - } - - bb2: { StorageLive(_9); // DBG: _19 = &_16; - // DBG: _20 = &_14; + // DBG: _20 = &?; StorageLive(_7); _7 = copy ((*_3).3: usize); StorageLive(_8); @@ -93,29 +89,25 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _9 = Le(move _7, move _8); StorageDead(_8); StorageDead(_7); - switchInt(move _9) -> [0: bb3, otherwise: bb8]; - } - - bb3: { - goto -> bb4; + switchInt(move _9) -> [0: bb2, otherwise: bb6]; } - bb4: { + bb2: { StorageLive(_10); // DBG: _21 = &_15; - // DBG: _22 = &_13; + // DBG: _22 = &?; _10 = Le(copy _5, copy _4); - switchInt(move _10) -> [0: bb5, otherwise: bb6]; + switchInt(move _10) -> [0: bb3, otherwise: bb4]; } - bb5: { + bb3: { _0 = const false; - goto -> bb7; + goto -> bb5; } - bb6: { + bb4: { // DBG: _23 = &_14; - // DBG: _24 = &_16; + // DBG: _24 = &?; StorageLive(_11); _11 = copy ((*_3).1: usize); StorageLive(_12); @@ -123,20 +115,20 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _0 = Le(move _11, move _12); StorageDead(_12); StorageDead(_11); - goto -> bb7; + goto -> bb5; } - bb7: { + bb5: { StorageDead(_10); - goto -> bb9; + goto -> bb7; } - bb8: { + bb6: { _0 = const true; - goto -> bb9; + goto -> bb7; } - bb9: { + bb7: { StorageDead(_9); StorageDead(_6); return; diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index eebd4a7eb50e3..2eee8a97db0d4 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -11,9 +11,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; - let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { - let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -57,14 +55,8 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); - StorageLive(_6); - // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); - StorageLive(_7); - // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); - StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index eebd4a7eb50e3..2eee8a97db0d4 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -11,9 +11,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; - let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { - let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -57,14 +55,8 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); - StorageLive(_6); - // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); - StorageLive(_7); - // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); - StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); From 38d129eeb4162c5b896c56ae68d5a8c80d690d65 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 Oct 2025 09:13:29 +0200 Subject: [PATCH 1159/1900] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/empty_main.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/thread_locals.stderr | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 70374af27e9c9..4e388e04ea44f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -29b7717de23f3969ceeb5bef5b01d9223f807655 +42b384ec0dfcd528d99a4db0a337d9188a9eecaa diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr index f2c574283cbf0..3dccd7059538e 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr index a7dacdeba4e45..dc59632558c8b 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr index 36ba5481f472c..44c307a6b3e41 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr index d6195b4e9b212..22a58f4e9cef0 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index ff971301bc66a..e9b780fc007f5 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC From ef237100f7ae2a16c20489e96e96c6d052c90f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 2 Oct 2025 10:18:47 +0300 Subject: [PATCH 1160/1900] Reformat code after rustc-pull --- src/tools/rust-analyzer/josh-sync.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/josh-sync.toml b/src/tools/rust-analyzer/josh-sync.toml index 51ff0d71e71a9..a12cfa1b5cbd5 100644 --- a/src/tools/rust-analyzer/josh-sync.toml +++ b/src/tools/rust-analyzer/josh-sync.toml @@ -1,2 +1,6 @@ repo = "rust-analyzer" filter = ":rev(55d9a533b309119c8acd13061581b43ae8840823:prefix=src/tools/rust-analyzer):/src/tools/rust-analyzer" + +[[post-pull]] +cmd = ["cargo", "fmt"] +commit-message = "Format code" From a5d16293706cdf02ea275e976ce9f6c3a20c3d3f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 Oct 2025 09:25:19 +0200 Subject: [PATCH 1161/1900] make cur_span and current_span harder to mix up --- .../borrow_tracker/stacked_borrows/diagnostics.rs | 8 ++++---- .../miri/src/borrow_tracker/tree_borrows/mod.rs | 12 ++++++------ src/tools/miri/src/concurrency/data_race.rs | 14 +++++++------- src/tools/miri/src/concurrency/genmc/helper.rs | 6 +++++- src/tools/miri/src/concurrency/thread.rs | 4 ++-- src/tools/miri/src/helpers.rs | 4 ++-- src/tools/miri/src/machine.rs | 6 +++--- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 63b18028a5b9d..997d7799a5f1c 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -221,7 +221,7 @@ impl AllocHistory { pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_>) -> Self { Self { id, - root: (item, machine.current_span()), + root: (item, machine.current_user_relevant_span()), creations: SmallVec::new(), invalidations: SmallVec::new(), protectors: SmallVec::new(), @@ -269,11 +269,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { }; self.history .creations - .push(Creation { retag: op.clone(), span: self.machine.current_span() }); + .push(Creation { retag: op.clone(), span: self.machine.current_user_relevant_span() }); } pub fn log_invalidation(&mut self, tag: BorTag) { - let mut span = self.machine.current_span(); + let mut span = self.machine.current_user_relevant_span(); let (range, cause) = match &self.operation { Operation::Retag(RetagOp { info, range, permission, .. }) => { if info.cause == RetagCause::FnEntry { @@ -298,7 +298,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { }; self.history .protectors - .push(Protection { tag: op.new_tag, span: self.machine.current_span() }); + .push(Protection { tag: op.new_tag, span: self.machine.current_user_relevant_span() }); } pub fn get_logs_relevant_to( diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 6e5b5c807aa22..865097af10181 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -33,7 +33,7 @@ impl<'tcx> Tree { machine: &MiriMachine<'tcx>, ) -> Self { let tag = state.root_ptr_tag(id, machine); // Fresh tag for the root - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); Tree::new(tag, size, span) } @@ -61,7 +61,7 @@ impl<'tcx> Tree { ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); self.perform_access( tag, Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))), @@ -86,7 +86,7 @@ impl<'tcx> Tree { ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span) } @@ -107,7 +107,7 @@ impl<'tcx> Tree { tag: BorTag, alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); // `None` makes it the magic on-protector-end operation self.perform_access(tag, None, global, alloc_id, span) } @@ -360,7 +360,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)), this.machine.borrow_tracker.as_ref().unwrap(), alloc_id, - this.machine.current_span(), + this.machine.current_user_relevant_span(), )?; // Also inform the data race model (but only if any bytes are actually affected). @@ -386,7 +386,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inside_perms, new_perm.outside_perm, protected, - this.machine.current_span(), + this.machine.current_user_relevant_span(), )?; drop(tree_borrows); diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 1ad9ace1b5d1a..1f30f9d7288a2 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -1208,7 +1208,7 @@ impl VClockAlloc { ty: Option>, machine: &MiriMachine<'_>, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return interp_ok(()); @@ -1250,7 +1250,7 @@ impl VClockAlloc { ty: Option>, machine: &mut MiriMachine<'_>, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_mut().unwrap(); if !global.race_detecting() { return interp_ok(()); @@ -1304,7 +1304,7 @@ impl Default for LocalClocks { impl FrameState { pub fn local_write(&self, local: mir::Local, storage_live: bool, machine: &MiriMachine<'_>) { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return; @@ -1334,7 +1334,7 @@ impl FrameState { } pub fn local_read(&self, local: mir::Local, machine: &MiriMachine<'_>) { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return; @@ -1573,7 +1573,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { size.bytes() ); - let current_span = this.machine.current_span(); + let current_span = this.machine.current_user_relevant_span(); // Perform the atomic operation. data_race.maybe_perform_sync_operation( &this.machine.threads, @@ -1827,7 +1827,7 @@ impl GlobalState { machine: &MiriMachine<'tcx>, atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); self.maybe_perform_sync_operation(&machine.threads, current_span, |index, mut clocks| { trace!("Atomic fence on {:?} with ordering {:?}", index, atomic); @@ -1915,7 +1915,7 @@ impl GlobalState { callback: impl FnOnce(&VClock) -> R, ) -> R { let thread = threads.active_thread(); - let span = threads.active_thread_ref().current_span(); + let span = threads.active_thread_ref().current_user_relevant_span(); let (index, mut clocks) = self.thread_state_mut(thread); let r = callback(&clocks.clock); // Increment the clock, so that all following events cannot be confused with anything that diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index ebbef23a2a54c..b79fe0e94299a 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -37,7 +37,11 @@ pub(super) fn emit_warning<'tcx>( cache: &WarningCache, diagnostic: impl FnOnce() -> NonHaltingDiagnostic, ) { - let span = ecx.machine.current_span(); + // FIXME: This is not the right span to use (it's always inside the local crates so if the same + // operation is invoked from multiple places it will warn multiple times). `cur_span` is not + // right either though (we should honor `#[track_caller]`). Ultimately what we want is "the + // primary span the warning would point at". + let span = ecx.machine.current_user_relevant_span(); if cache.read().unwrap().contains(&span) { return; } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 246300f8dda15..0d9dbe052e399 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -263,7 +263,7 @@ impl<'tcx> Thread<'tcx> { self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1)) } - pub fn current_span(&self) -> Span { + pub fn current_user_relevant_span(&self) -> Span { self.top_user_relevant_frame() .map(|frame_idx| self.stack[frame_idx].current_span()) .unwrap_or(rustc_span::DUMMY_SP) @@ -867,7 +867,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut state = tls::TlsDtorsState::default(); Box::new(move |m| state.on_stack_empty(m)) }); - let current_span = this.machine.current_span(); + let current_span = this.machine.current_user_relevant_span(); match &mut this.machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Vclocks(data_race) => diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index e0c077e99319a..3f736435bbc49 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1128,8 +1128,8 @@ impl<'tcx> MiriMachine<'tcx> { /// `#[track_caller]`. /// This function is backed by a cache, and can be assumed to be very fast. /// It will work even when the stack is empty. - pub fn current_span(&self) -> Span { - self.threads.active_thread_ref().current_span() + pub fn current_user_relevant_span(&self) -> Span { + self.threads.active_thread_ref().current_user_relevant_span() } /// Returns the span of the *caller* of the current operation, again diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 53b8fea979c78..a1e63f6ae4ece 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -919,7 +919,7 @@ impl<'tcx> MiriMachine<'tcx> { &ecx.machine.threads, size, kind, - ecx.machine.current_span(), + ecx.machine.current_user_relevant_span(), ), data_race.weak_memory.then(weak_memory::AllocState::new_allocation), ), @@ -943,7 +943,7 @@ impl<'tcx> MiriMachine<'tcx> { ecx.machine .allocation_spans .borrow_mut() - .insert(id, (ecx.machine.current_span(), None)); + .insert(id, (ecx.machine.current_user_relevant_span(), None)); } interp_ok(AllocExtra { borrow_tracker, data_race, backtrace, sync: FxHashMap::default() }) @@ -1566,7 +1566,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id) { - *deallocated_at = Some(machine.current_span()); + *deallocated_at = Some(machine.current_user_relevant_span()); } machine.free_alloc_id(alloc_id, size, align, kind); interp_ok(()) From ecb831dcf4c28d3b66c9bce7aa4c3a8eb221fa5e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 16:29:18 +1000 Subject: [PATCH 1162/1900] Extract helper method `set_metadata_node` --- compiler/rustc_codegen_llvm/src/asm.rs | 4 +- compiler/rustc_codegen_llvm/src/builder.rs | 54 +++++++--------------- compiler/rustc_codegen_llvm/src/context.rs | 19 ++++++++ 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index cc09fa5b69bac..93b1cf272ab7b 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -538,9 +538,7 @@ pub(crate) fn inline_asm_call<'ll>( bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), ) })); - let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) }; - let md = bx.get_metadata_value(md); - llvm::LLVMSetMetadata(call, kind, md); + bx.cx.set_metadata_node(call, kind, &srcloc); Some(call) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5271d0b4bb8a7..e7cb18ab22daf 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1,12 +1,12 @@ use std::borrow::{Borrow, Cow}; +use std::iter; use std::ops::Deref; -use std::{iter, ptr}; use rustc_ast::expand::typetree::FncTree; pub(crate) mod autodiff; pub(crate) mod gpu_offload; -use libc::{c_char, c_uint, size_t}; +use libc::{c_char, c_uint}; use rustc_abi as abi; use rustc_abi::{Align, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; @@ -396,10 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { md.push(weight(is_cold)); } - unsafe { - let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t); - self.cx.set_metadata(switch, llvm::MD_prof, md_node); - } + self.cx.set_metadata_node(switch, llvm::MD_prof, &md); } fn invoke( @@ -801,22 +798,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { return; } - unsafe { - let llty = self.cx.val_ty(load); - let md = [ - llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)), - llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))), - ]; - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len()); - self.set_metadata(load, llvm::MD_range, md); - } + let llty = self.cx.val_ty(load); + let md = [ + llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)), + llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))), + ]; + self.set_metadata_node(load, llvm::MD_range, &md); } fn nonnull_metadata(&mut self, load: &'ll Value) { - unsafe { - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); - self.set_metadata(load, llvm::MD_nonnull, md); - } + self.set_metadata_node(load, llvm::MD_nonnull, &[]); } fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value { @@ -865,8 +856,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // // [1]: https://llvm.org/docs/LangRef.html#store-instruction let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1)); - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1); - self.set_metadata(store, llvm::MD_nontemporal, md); + self.set_metadata_node(store, llvm::MD_nontemporal, &[one]); } } store @@ -1381,10 +1371,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn set_invariant_load(&mut self, load: &'ll Value) { - unsafe { - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); - self.set_metadata(load, llvm::MD_invariant_load, md); - } + self.set_metadata_node(load, llvm::MD_invariant_load, &[]); } fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) { @@ -1528,25 +1515,16 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn align_metadata(&mut self, load: &'ll Value, align: Align) { - unsafe { - let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len()); - self.set_metadata(load, llvm::MD_align, md); - } + let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; + self.set_metadata_node(load, llvm::MD_align, &md); } fn noundef_metadata(&mut self, load: &'ll Value) { - unsafe { - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); - self.set_metadata(load, llvm::MD_noundef, md); - } + self.set_metadata_node(load, llvm::MD_noundef, &[]); } pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) { - unsafe { - let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); - self.set_metadata(inst, llvm::MD_unpredictable, md); - } + self.set_metadata_node(inst, llvm::MD_unpredictable, &[]); } } impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b1da6f7c74061..03d9daeea3603 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1002,6 +1002,11 @@ impl CodegenCx<'_, '_> { } impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { + /// Wrapper for `LLVMMDNodeInContext2`, i.e. `llvm::MDNode::get`. + pub(crate) fn md_node_in_context(&self, md_list: &[&'ll Metadata]) -> &'ll Metadata { + unsafe { llvm::LLVMMDNodeInContext2(self.llcx(), md_list.as_ptr(), md_list.len()) } + } + /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. pub(crate) fn set_metadata<'a>( &self, @@ -1012,6 +1017,20 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { let node = self.get_metadata_value(md); llvm::LLVMSetMetadata(val, kind_id, node); } + + /// Helper method for the sequence of calls: + /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata) + /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`) + /// - `LLVMSetMetadata` (to set that node as metadata of `kind_id` for `instruction`) + pub(crate) fn set_metadata_node( + &self, + instruction: &'ll Value, + kind_id: MetadataKindId, + md_list: &[&'ll Metadata], + ) { + let md = self.md_node_in_context(md_list); + self.set_metadata(instruction, kind_id, md); + } } impl HasDataLayout for CodegenCx<'_, '_> { From ffeed2b94ed432c959e3d9cf9bd5f7016f9577d3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 16:39:58 +1000 Subject: [PATCH 1163/1900] Extract helper method `module_add_named_metadata_node` --- compiler/rustc_codegen_llvm/src/consts.rs | 11 +-------- compiler/rustc_codegen_llvm/src/context.rs | 26 ++++++++++++++-------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 40375ef651092..b4ca85a26c8b8 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -494,16 +494,7 @@ impl<'ll> CodegenCx<'ll, '_> { let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()); let alloc = self.create_metadata(bytes); let data = [section, alloc]; - let meta = - unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) }; - let val = self.get_metadata_value(meta); - unsafe { - llvm::LLVMAddNamedMetadataOperand( - self.llmod, - c"wasm.custom_sections".as_ptr(), - val, - ) - }; + self.module_add_named_metadata_node(self.llmod(), c"wasm.custom_sections", &data); } } else { base::set_link_section(g, attrs); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 03d9daeea3603..f1f3d6a8b62c0 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -34,7 +34,7 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; -use crate::llvm::{Metadata, MetadataKindId}; +use crate::llvm::{Metadata, MetadataKindId, Module}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; @@ -495,14 +495,7 @@ pub(crate) unsafe fn create_module<'ll>( format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); let name_metadata = cx.create_metadata(rustc_producer.as_bytes()); - - unsafe { - llvm::LLVMAddNamedMetadataOperand( - llmod, - c"llvm.ident".as_ptr(), - &cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)), - ); - } + cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]); // Emit RISC-V specific target-abi metadata // to workaround lld as the LTO plugin not @@ -1031,6 +1024,21 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { let md = self.md_node_in_context(md_list); self.set_metadata(instruction, kind_id, md); } + + /// Helper method for the sequence of calls: + /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata) + /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`) + /// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`) + pub(crate) fn module_add_named_metadata_node( + &self, + module: &'ll Module, + kind_name: &CStr, + md_list: &[&'ll Metadata], + ) { + let md = self.md_node_in_context(md_list); + let md_as_val = self.get_metadata_value(md); + unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) }; + } } impl HasDataLayout for CodegenCx<'_, '_> { From 6a58f80a3c4cf53f8092e75c07cebd5e913cccf0 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 17:19:30 +1000 Subject: [PATCH 1164/1900] Extract helper method `global_add_metadata_node` --- compiler/rustc_codegen_llvm/src/context.rs | 13 ++++++++++ .../src/debuginfo/metadata.rs | 9 +++---- compiler/rustc_codegen_llvm/src/type_.rs | 24 ++++--------------- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f1f3d6a8b62c0..d09121016d059 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1039,6 +1039,19 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { let md_as_val = self.get_metadata_value(md); unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) }; } + + /// Helper method for the sequence of calls: + /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata) + /// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`) + pub(crate) fn global_add_metadata_node( + &self, + global: &'ll Value, + kind_id: MetadataKindId, + md_list: &[&'ll Metadata], + ) { + let md = self.md_node_in_context(md_list); + unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) }; + } } impl HasDataLayout for CodegenCx<'_, '_> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index bc20c75941349..6b2fe27425e9e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1607,13 +1607,10 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref); let typeid = cx.create_metadata(trait_ref_typeid.as_bytes()); + let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; + cx.global_add_metadata_node(vtable, llvm::MD_type, &type_); + unsafe { - let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; - llvm::LLVMRustGlobalAddMetadata( - vtable, - llvm::MD_type, - llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()), - ); let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 5b97898a4b888..9f8b7d2efdb54 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -302,14 +302,8 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { let typeid_metadata = self.create_metadata(typeid); - unsafe { - let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; - llvm::LLVMRustGlobalAddMetadata( - function, - llvm::MD_type, - llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), - ) - } + let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; + self.global_add_metadata_node(function, llvm::MD_type, &v); } fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { @@ -329,18 +323,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) { - let kcfi_type_metadata = self.const_u32(kcfi_typeid); - unsafe { - llvm::LLVMRustGlobalAddMetadata( - function, - llvm::MD_kcfi_type, - llvm::LLVMMDNodeInContext2( - self.llcx, - &llvm::LLVMValueAsMetadata(kcfi_type_metadata), - 1, - ), - ) - } + let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))]; + self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata); } fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) { From 8ef9821bf2e27bf7ab4f9a0e3d0975dc59d3d932 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 17:33:28 +1000 Subject: [PATCH 1165/1900] Extract helper method `global_set_metadata_node` --- compiler/rustc_codegen_llvm/src/context.rs | 13 ++++++++++ .../src/debuginfo/metadata.rs | 7 ++---- compiler/rustc_codegen_llvm/src/type_.rs | 24 ++++--------------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d09121016d059..922575dd63cfa 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1052,6 +1052,19 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { let md = self.md_node_in_context(md_list); unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) }; } + + /// Helper method for the sequence of calls: + /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata) + /// - `LLVMGlobalSetMetadata` (to set that node as metadata of `kind_id` for `global`) + pub(crate) fn global_set_metadata_node( + &self, + global: &'ll Value, + kind_id: MetadataKindId, + md_list: &[&'ll Metadata], + ) { + let md = self.md_node_in_context(md_list); + unsafe { llvm::LLVMGlobalSetMetadata(global, kind_id, md) }; + } } impl HasDataLayout for CodegenCx<'_, '_> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 6b2fe27425e9e..2f9e7cae54f2b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1610,11 +1610,8 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; cx.global_add_metadata_node(vtable, llvm::MD_type, &type_); - unsafe { - let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); - let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); - llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata); - } + let vcall_visibility = [llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64))]; + cx.global_set_metadata_node(vtable, llvm::MD_vcall_visibility, &vcall_visibility); } /// Creates debug information for the given vtable, which is for the diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 9f8b7d2efdb54..1e5cf8374e379 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -308,14 +308,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { let typeid_metadata = self.create_metadata(typeid); - unsafe { - let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; - llvm::LLVMGlobalSetMetadata( - function, - llvm::MD_type, - llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), - ) - } + let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; + self.global_set_metadata_node(function, llvm::MD_type, &v); } fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> { @@ -328,17 +322,7 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) { - let kcfi_type_metadata = self.const_u32(kcfi_typeid); - unsafe { - llvm::LLVMGlobalSetMetadata( - function, - llvm::MD_kcfi_type, - llvm::LLVMMDNodeInContext2( - self.llcx, - &llvm::LLVMValueAsMetadata(kcfi_type_metadata), - 1, - ), - ) - } + let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))]; + self.global_set_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata); } } From 608c6614b170dcb5867952acc23e3eb16a79ecb7 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Tue, 30 Sep 2025 03:33:26 -0500 Subject: [PATCH 1166/1900] fix msvc enum summary --- src/etc/lldb_providers.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f18baa937e7..058450e3c3650 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -652,6 +652,21 @@ def get_type_name(self) -> str: return name +def StructSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: + output = [] + for i in range(valobj.GetNumChildren()): + child: SBValue = valobj.GetChildAtIndex(i) + summary = child.summary + if summary is None: + summary = child.value + if summary is None: + summary = StructSummaryProvider(child, _dict) + summary = child.GetName() + ":" + summary + output.append(summary) + + return "{" + ", ".join(output) + "}" + + def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict) variant_names: SBType = valobj.target.FindFirstType( @@ -695,16 +710,7 @@ def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: return name + TupleSummaryProvider(enum_synth.value, _dict) else: # enum variant is a regular struct - var_list = ( - str(enum_synth.value.GetNonSyntheticValue()).split("= ", 1)[1].splitlines() - ) - vars = [x.strip() for x in var_list if x not in ("{", "}")] - if vars[0][0] == "(": - vars[0] = vars[0][1:] - if vars[-1][-1] == ")": - vars[-1] = vars[-1][:-1] - - return f"{name}{{{', '.join(vars)}}}" + return name + StructSummaryProvider(enum_synth.value, _dict) class TupleSyntheticProvider: From 16cfde9703f74cfc96c0c0bd51958a2627dfb5ea Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 2 Oct 2025 16:44:40 +0800 Subject: [PATCH 1167/1900] triagebot: disable auto stable-regression compiler backport nominations No prejudice against re-enabling them if the nominations include a bit more context on _why_ it's automatically nominated and _which_ regression(s) are being addressed. Or as proposed, it could also simply become a reminder-to-nominate _comment_. --- triagebot.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index a04f8d2807235..79b5c2d1b7238 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -61,10 +61,6 @@ required-issue-label = "regression-from-stable-to-beta" # if the above conditions matches, the PR will receive these labels add-labels = ["beta-nominated"] -[backport.t-compiler-stable-backport] -required-pr-labels = ["T-compiler"] -required-issue-label = "regression-from-stable-to-stable" -add-labels = ["stable-nominated"] # ------------------------------------------------------------------------------ # Ping groups From 1e772f9681d4828cb10e0df3a37fc18594bfe992 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 2 Oct 2025 12:09:54 +0200 Subject: [PATCH 1168/1900] check for empty heads after adding parent --- compiler/rustc_type_ir/src/search_graph/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 7aa58d096d5c0..6d3da27acf9bd 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -969,10 +969,6 @@ impl, X: Cx> SearchGraph { return true; }; - let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { - return false; - }; - // We're rebasing an entry `e` over a head `p`. This head // has a number of own heads `h` it depends on. // @@ -1046,6 +1042,10 @@ impl, X: Cx> SearchGraph { }; } + let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { + return false; + }; + // We now care about the path from the next highest cycle head to the // provisional cache entry. *path_from_head = path_from_head.extend(Self::cycle_path_kind( From 36283bcb7e37b6e1cdf0c971f514a8212164a593 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 2 Oct 2025 12:10:58 +0200 Subject: [PATCH 1169/1900] fix `RebaseReason::Ambiguity` we should not get the certainty from the current result of the provisional cache entry --- .../src/solve/search_graph.rs | 14 +++--- compiler/rustc_type_ir/src/interner.rs | 3 +- .../rustc_type_ir/src/search_graph/mod.rs | 26 +++++++---- .../forced_ambiguity-use-head-maybe-cause.rs | 44 +++++++++++++++++++ ...rced_ambiguity-use-head-maybe-cause.stderr | 27 ++++++++++++ 5 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs create mode 100644 tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 109c8476ccb16..ea45d50969909 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -99,19 +99,23 @@ where response_no_constraints(cx, input, Certainty::overflow(false)) } - fn is_ambiguous_result(result: QueryResult) -> bool { - result.is_ok_and(|response| { - has_no_inference_or_external_constraints(response) + fn is_ambiguous_result(result: QueryResult) -> Option { + result.ok().and_then(|response| { + if has_no_inference_or_external_constraints(response) && matches!(response.value.certainty, Certainty::Maybe { .. }) + { + Some(response.value.certainty) + } else { + None + } }) } fn propagate_ambiguity( cx: I, for_input: CanonicalInput, - from_result: QueryResult, + certainty: Certainty, ) -> QueryResult { - let certainty = from_result.unwrap().value.certainty; response_no_constraints(cx, for_input, certainty) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 886d1a78bcbfd..322be31f4cb10 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,7 +11,7 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; -use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect}; +use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect}; use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; @@ -548,6 +548,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; type Result = QueryResult; + type AmbiguityInfo = Certainty; type DepNodeIndex = I::DepNodeIndex; type Tracked = I::Tracked; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 6d3da27acf9bd..ad8b643d976c7 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -40,6 +40,7 @@ pub use global_cache::GlobalCache; pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; + type AmbiguityInfo: Debug + Eq + Hash + Copy; type DepNodeIndex; type Tracked: Debug; @@ -96,11 +97,13 @@ pub trait Delegate: Sized { input: ::Input, ) -> ::Result; - fn is_ambiguous_result(result: ::Result) -> bool; + fn is_ambiguous_result( + result: ::Result, + ) -> Option<::AmbiguityInfo>; fn propagate_ambiguity( cx: Self::Cx, for_input: ::Input, - from_result: ::Result, + ambiguity_info: ::AmbiguityInfo, ) -> ::Result; fn compute_goal( @@ -913,9 +916,9 @@ impl, X: Cx> SearchGraph { /// heads from the stack. This may not necessarily mean that we've actually /// reached a fixpoint for that cycle head, which impacts the way we rebase /// provisional cache entries. -enum RebaseReason { +enum RebaseReason { NoCycleUsages, - Ambiguity, + Ambiguity(X::AmbiguityInfo), Overflow, /// We've actually reached a fixpoint. /// @@ -951,7 +954,7 @@ impl, X: Cx> SearchGraph { &mut self, cx: X, stack_entry: &StackEntry, - rebase_reason: RebaseReason, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -1029,8 +1032,8 @@ impl, X: Cx> SearchGraph { // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity => { - *result = D::propagate_ambiguity(cx, input, *result); + RebaseReason::Ambiguity(info) => { + *result = D::propagate_ambiguity(cx, input, info); } RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} @@ -1268,6 +1271,7 @@ impl, X: Cx> SearchGraph { } /// Whether we've reached a fixpoint when evaluating a cycle head. + #[instrument(level = "trace", skip(self, stack_entry), ret)] fn reached_fixpoint( &mut self, stack_entry: &StackEntry, @@ -1355,8 +1359,12 @@ impl, X: Cx> SearchGraph { // As we only get to this branch if we haven't yet reached a fixpoint, // we also taint all provisional cache entries which depend on the // current goal. - if D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity); + if let Some(info) = D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::Ambiguity(info), + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; diff --git a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs new file mode 100644 index 0000000000000..aa5893d621cdb --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Znext-solver +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +// A regression test making sure that when forcing dependent +// provisional cache entries to ambiguous, we use the `MaybeCause` +// of the cycle head. We ended up trying to use the current result +// of the provisional cache entry, which is incorrect and caused an +// ICE when trying to unwrap it. + +struct Root(T); +struct Head(T); +struct Error(T); +struct NotImplemented(T); + +#[rustc_coinductive] +trait Trait {} +impl Trait for Root +where + Head: Trait, +{} + +impl Trait for Head +where + Root: Trait, + T: Trait, // ambiguous +{} + +impl Trait for Head +where + Error: Trait, + NotImplemented: Trait, +{} + +impl Trait for Error +where + Head: Trait, + NotImplemented: Trait, +{} + +fn impls_trait() {} +fn main() { + impls_trait::>() //~ ERROR type annotations needed +} diff --git a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr new file mode 100644 index 0000000000000..dd4049a66d0b4 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr @@ -0,0 +1,27 @@ +error[E0283]: type annotations needed + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:43:19 + | +LL | impls_trait::>() + | ^^^^^^^ cannot infer type for struct `Head<_>` + | + = note: cannot satisfy `Head<_>: Trait` + = help: the trait `Trait` is implemented for `Head` +note: required for `Root<_>` to implement `Trait` + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:18:9 + | +LL | impl Trait for Root + | ^^^^^ ^^^^^^^ +LL | where +LL | Head: Trait, + | ----- unsatisfied trait bound introduced here + = note: 8 redundant requirements hidden + = note: required for `Root<_>` to implement `Trait` +note: required by a bound in `impls_trait` + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:41:19 + | +LL | fn impls_trait() {} + | ^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. From 5bf5e7116099d85a99d047cfa43db155074e594a Mon Sep 17 00:00:00 2001 From: Reuben Cruise Date: Thu, 25 Sep 2025 16:11:34 +0100 Subject: [PATCH 1170/1900] Extends `rustc_force_inline` to inherent methods - Changes parser to allow application to inherent methods. - Adds tests to confirm extended functionality works just as the existing. --- .../src/attributes/inline.rs | 6 ++- ...r-{closure#0}.ForceInline.panic-abort.diff | 21 ++++++++++ ...-{closure#0}.ForceInline.panic-unwind.diff | 21 ++++++++++ .../mir-opt/inline/forced_closure_inherent.rs | 19 ++++++++++ ...herent.caller.ForceInline.panic-abort.diff | 21 ++++++++++ ...erent.caller.ForceInline.panic-unwind.diff | 21 ++++++++++ tests/mir-opt/inline/forced_inherent.rs | 17 +++++++++ ...iguous.caller.ForceInline.panic-abort.diff | 21 ++++++++++ ...guous.caller.ForceInline.panic-unwind.diff | 21 ++++++++++ .../inline/forced_inherent_ambiguous.rs | 25 ++++++++++++ ..._async.caller.ForceInline.panic-abort.diff | 12 ++++++ ...async.caller.ForceInline.panic-unwind.diff | 12 ++++++ tests/mir-opt/inline/forced_inherent_async.rs | 18 +++++++++ ...d_code.caller.ForceInline.panic-abort.diff | 21 ++++++++++ ..._code.caller.ForceInline.panic-unwind.diff | 21 ++++++++++ .../inline/forced_inherent_dead_code.rs | 21 ++++++++++ tests/ui/force-inlining/inherent.rs | 21 ++++++++++ tests/ui/force-inlining/inherent.stderr | 13 +++++++ tests/ui/force-inlining/invalid.rs | 1 - tests/ui/force-inlining/invalid.stderr | 38 ++++++++----------- 20 files changed, 346 insertions(+), 25 deletions(-) create mode 100644 tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff create mode 100644 tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff create mode 100644 tests/mir-opt/inline/forced_closure_inherent.rs create mode 100644 tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff create mode 100644 tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff create mode 100644 tests/mir-opt/inline/forced_inherent.rs create mode 100644 tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff create mode 100644 tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff create mode 100644 tests/mir-opt/inline/forced_inherent_ambiguous.rs create mode 100644 tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-abort.diff create mode 100644 tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-unwind.diff create mode 100644 tests/mir-opt/inline/forced_inherent_async.rs create mode 100644 tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff create mode 100644 tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff create mode 100644 tests/mir-opt/inline/forced_inherent_dead_code.rs create mode 100644 tests/ui/force-inlining/inherent.rs create mode 100644 tests/ui/force-inlining/inherent.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index a73430c9d0096..a10ad27fcc68b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -72,7 +72,11 @@ impl SingleAttributeParser for RustcForceInlineParser { const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + ]); + const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { diff --git a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff new file mode 100644 index 0000000000000..8e03432c2af2d --- /dev/null +++ b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `caller::{closure#0}` before ForceInline ++ // MIR for `caller::{closure#0}` after ForceInline + + fn caller::{closure#0}(_1: &{closure@$DIR/forced_closure_inherent.rs:14:6: 14:8}) -> () { + let mut _0: (); + let _2: (); ++ scope 1 (inlined Foo::callee_forced) { ++ } + + bb0: { + StorageLive(_2); +- _2 = Foo::callee_forced() -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageDead(_2); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff new file mode 100644 index 0000000000000..0e41fd89dacf7 --- /dev/null +++ b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `caller::{closure#0}` before ForceInline ++ // MIR for `caller::{closure#0}` after ForceInline + + fn caller::{closure#0}(_1: &{closure@$DIR/forced_closure_inherent.rs:14:6: 14:8}) -> () { + let mut _0: (); + let _2: (); ++ scope 1 (inlined Foo::callee_forced) { ++ } + + bb0: { + StorageLive(_2); +- _2 = Foo::callee_forced() -> [return: bb1, unwind continue]; +- } +- +- bb1: { + StorageDead(_2); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_closure_inherent.rs b/tests/mir-opt/inline/forced_closure_inherent.rs new file mode 100644 index 0000000000000..949c7d6ecbf28 --- /dev/null +++ b/tests/mir-opt/inline/forced_closure_inherent.rs @@ -0,0 +1,19 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Copt-level=0 --crate-type=lib +#![feature(rustc_attrs)] + +struct Foo {} + +impl Foo { + #[rustc_force_inline] + pub fn callee_forced() {} +} + +// EMIT_MIR forced_closure_inherent.caller-{closure#0}.ForceInline.diff +pub fn caller() { + (|| { + Foo::callee_forced(); + // CHECK-LABEL: fn caller::{closure#0}( + // CHECK: (inlined Foo::callee_forced) + })(); +} diff --git a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff new file mode 100644 index 0000000000000..6ea1894af9895 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::bar) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::bar() -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff new file mode 100644 index 0000000000000..dd91c3387723f --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::bar) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::bar() -> [return: bb1, unwind continue]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent.rs b/tests/mir-opt/inline/forced_inherent.rs new file mode 100644 index 0000000000000..24bf8daa64450 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent.rs @@ -0,0 +1,17 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Copt-level=0 --crate-type=lib +#![feature(rustc_attrs)] + +struct Foo; + +impl Foo { + #[rustc_force_inline] + fn bar() {} +} + +// EMIT_MIR forced_inherent.caller.ForceInline.diff +fn caller() { + Foo::bar(); + // CHECK-LABEL: fn caller( + // CHECK: (inlined Foo::bar) +} diff --git a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff new file mode 100644 index 0000000000000..6ea1894af9895 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::bar) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::bar() -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff new file mode 100644 index 0000000000000..dd91c3387723f --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::bar) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::bar() -> [return: bb1, unwind continue]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_ambiguous.rs b/tests/mir-opt/inline/forced_inherent_ambiguous.rs new file mode 100644 index 0000000000000..e3c5d3e4f9e81 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_ambiguous.rs @@ -0,0 +1,25 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Copt-level=0 --crate-type=lib +#![feature(rustc_attrs)] + +struct Foo; + +impl Foo { + #[rustc_force_inline] + fn bar() {} +} + +trait Tr { + fn bar(); +} + +impl Tr for Foo { + fn bar() {} +} + +// EMIT_MIR forced_inherent_ambiguous.caller.ForceInline.diff +fn caller() { + Foo::bar(); + // CHECK-LABEL: fn caller( + // CHECK: (inlined Foo::bar) +} diff --git a/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-abort.diff new file mode 100644 index 0000000000000..6495ddbafba41 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-abort.diff @@ -0,0 +1,12 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> {async fn body of caller()} { + let mut _0: {async fn body of caller()}; + + bb0: { + _0 = {coroutine@$DIR/forced_inherent_async.rs:14:19: 18:2 (#0)}; + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-unwind.diff new file mode 100644 index 0000000000000..6495ddbafba41 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_async.caller.ForceInline.panic-unwind.diff @@ -0,0 +1,12 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> {async fn body of caller()} { + let mut _0: {async fn body of caller()}; + + bb0: { + _0 = {coroutine@$DIR/forced_inherent_async.rs:14:19: 18:2 (#0)}; + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_async.rs b/tests/mir-opt/inline/forced_inherent_async.rs new file mode 100644 index 0000000000000..ce58a0ac48f47 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_async.rs @@ -0,0 +1,18 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Copt-level=0 --crate-type=lib +//@ edition: 2021 +#![feature(rustc_attrs)] + +struct Foo {} + +impl Foo { + #[rustc_force_inline] + pub fn callee_forced() {} +} + +// EMIT_MIR forced_inherent_async.caller.ForceInline.diff +async fn caller() { + Foo::callee_forced(); + // CHECK-LABEL: fn caller( + // CHECK: (inlined Foo::callee_forced) +} diff --git a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff new file mode 100644 index 0000000000000..edaf2820d85c3 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::callee_forced) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::callee_forced() -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff new file mode 100644 index 0000000000000..22f8b14a724b8 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `caller` before ForceInline ++ // MIR for `caller` after ForceInline + + fn caller() -> () { + let mut _0: (); + let _1: (); ++ scope 1 (inlined Foo::callee_forced) { ++ } + + bb0: { + StorageLive(_1); +- _1 = Foo::callee_forced() -> [return: bb1, unwind continue]; +- } +- +- bb1: { + StorageDead(_1); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/inline/forced_inherent_dead_code.rs b/tests/mir-opt/inline/forced_inherent_dead_code.rs new file mode 100644 index 0000000000000..057a4cac52870 --- /dev/null +++ b/tests/mir-opt/inline/forced_inherent_dead_code.rs @@ -0,0 +1,21 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Copt-level=0 -Clink-dead-code +#![feature(rustc_attrs)] + +struct Foo {} + +impl Foo { + #[rustc_force_inline] + pub fn callee_forced() {} +} + +// EMIT_MIR forced_inherent_dead_code.caller.ForceInline.diff +pub fn caller() { + Foo::callee_forced(); + // CHECK-LABEL: fn caller( + // CHECK: (inlined Foo::callee_forced) +} + +fn main() { + caller(); +} diff --git a/tests/ui/force-inlining/inherent.rs b/tests/ui/force-inlining/inherent.rs new file mode 100644 index 0000000000000..25c76eaf37a91 --- /dev/null +++ b/tests/ui/force-inlining/inherent.rs @@ -0,0 +1,21 @@ +//@ check-fail +#![feature(rustc_attrs)] + +struct Foo; + +impl Foo { + #[rustc_force_inline] + //~^ ERROR: `Foo::bar` is incompatible with `#[rustc_force_inline]` + #[rustc_no_mir_inline] + fn bar() {} +} + +fn bar_caller() { + unsafe { + Foo::bar(); + } +} + +fn main() { + bar_caller(); +} diff --git a/tests/ui/force-inlining/inherent.stderr b/tests/ui/force-inlining/inherent.stderr new file mode 100644 index 0000000000000..1ffc78848fea0 --- /dev/null +++ b/tests/ui/force-inlining/inherent.stderr @@ -0,0 +1,13 @@ +error: `Foo::bar` is incompatible with `#[rustc_force_inline]` + --> $DIR/inherent.rs:7:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn bar() {} + | -------- `Foo::bar` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 1 previous error + diff --git a/tests/ui/force-inlining/invalid.rs b/tests/ui/force-inlining/invalid.rs index 6047739992f1a..eaacbb502090c 100644 --- a/tests/ui/force-inlining/invalid.rs +++ b/tests/ui/force-inlining/invalid.rs @@ -114,7 +114,6 @@ trait FooQux = FooBaz; //~^ ERROR attribute cannot be used on impl Bar { #[rustc_force_inline] -//~^ ERROR attribute cannot be used on fn foo() {} } diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr index 299a3ed4a462d..df3b646b6cec6 100644 --- a/tests/ui/force-inlining/invalid.stderr +++ b/tests/ui/force-inlining/invalid.stderr @@ -1,5 +1,5 @@ error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters - --> $DIR/invalid.rs:132:11 + --> $DIR/invalid.rs:131:11 | LL | fn barqux(#[rustc_force_inline] _x: u32) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ error: `#[rustc_force_inline]` attribute cannot be used on foreign functions LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_force_inline]` can only be applied to functions + = help: `#[rustc_force_inline]` can be applied to functions and inherent methods error: `#[rustc_force_inline]` attribute cannot be used on type aliases --> $DIR/invalid.rs:66:1 @@ -222,7 +222,7 @@ error: `#[rustc_force_inline]` attribute cannot be used on provided trait method LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_force_inline]` can only be applied to functions + = help: `#[rustc_force_inline]` can be applied to functions and inherent methods error: `#[rustc_force_inline]` attribute cannot be used on trait aliases --> $DIR/invalid.rs:109:1 @@ -240,16 +240,8 @@ LL | #[rustc_force_inline] | = help: `#[rustc_force_inline]` can only be applied to functions -error: `#[rustc_force_inline]` attribute cannot be used on inherent methods - --> $DIR/invalid.rs:116:5 - | -LL | #[rustc_force_inline] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: `#[rustc_force_inline]` can only be applied to functions - error: `#[rustc_force_inline]` attribute cannot be used on trait impl blocks - --> $DIR/invalid.rs:121:1 + --> $DIR/invalid.rs:120:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -257,7 +249,7 @@ LL | #[rustc_force_inline] = help: `#[rustc_force_inline]` can only be applied to functions error: `#[rustc_force_inline]` attribute cannot be used on macro defs - --> $DIR/invalid.rs:128:1 + --> $DIR/invalid.rs:127:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +257,7 @@ LL | #[rustc_force_inline] = help: `#[rustc_force_inline]` can only be applied to functions error: `#[rustc_force_inline]` attribute cannot be used on function params - --> $DIR/invalid.rs:132:11 + --> $DIR/invalid.rs:131:11 | LL | fn barqux(#[rustc_force_inline] _x: u32) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -273,15 +265,15 @@ LL | fn barqux(#[rustc_force_inline] _x: u32) {} = help: `#[rustc_force_inline]` can only be applied to functions error: `#[rustc_force_inline]` attribute cannot be used on closures - --> $DIR/invalid.rs:149:14 + --> $DIR/invalid.rs:148:14 | LL | let _x = #[rustc_force_inline] || { }; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_force_inline]` can only be applied to functions + = help: `#[rustc_force_inline]` can be applied to functions and inherent methods error: `#[rustc_force_inline]` attribute cannot be used on expressions - --> $DIR/invalid.rs:151:14 + --> $DIR/invalid.rs:150:14 | LL | let _y = #[rustc_force_inline] 3 + 4; | ^^^^^^^^^^^^^^^^^^^^^ @@ -289,7 +281,7 @@ LL | let _y = #[rustc_force_inline] 3 + 4; = help: `#[rustc_force_inline]` can only be applied to functions error: `#[rustc_force_inline]` attribute cannot be used on statements - --> $DIR/invalid.rs:153:5 + --> $DIR/invalid.rs:152:5 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -297,7 +289,7 @@ LL | #[rustc_force_inline] = help: `#[rustc_force_inline]` can only be applied to functions error: `#[rustc_force_inline]` attribute cannot be used on match arms - --> $DIR/invalid.rs:158:9 + --> $DIR/invalid.rs:157:9 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +297,7 @@ LL | #[rustc_force_inline] = help: `#[rustc_force_inline]` can only be applied to functions error: attribute cannot be applied to a `async`, `gen` or `async gen` function - --> $DIR/invalid.rs:136:1 + --> $DIR/invalid.rs:135:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +306,7 @@ LL | async fn async_foo() {} | -------------------- `async`, `gen` or `async gen` function error: attribute cannot be applied to a `async`, `gen` or `async gen` function - --> $DIR/invalid.rs:140:1 + --> $DIR/invalid.rs:139:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -323,7 +315,7 @@ LL | gen fn gen_foo() {} | ---------------- `async`, `gen` or `async gen` function error: attribute cannot be applied to a `async`, `gen` or `async gen` function - --> $DIR/invalid.rs:144:1 + --> $DIR/invalid.rs:143:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ @@ -331,7 +323,7 @@ LL | LL | async gen fn async_gen_foo() {} | ---------------------------- `async`, `gen` or `async gen` function -error: aborting due to 37 previous errors +error: aborting due to 36 previous errors Some errors have detailed explanations: E0539, E0805. For more information about an error, try `rustc --explain E0539`. From 6e225424a66886fa44e671dfa94063fb3a9f4529 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Sun, 28 Sep 2025 16:45:24 -0600 Subject: [PATCH 1171/1900] test: Add empty line to tests getting new flag in next commit --- .../non-1-width-unicode-multiline-label.ascii.stderr | 8 ++++---- .../non-1-width-unicode-multiline-label.rs | 1 + .../non-1-width-unicode-multiline-label.unicode.stderr | 8 ++++---- .../non-whitespace-trimming-2.ascii.stderr | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-2.rs | 1 + .../non-whitespace-trimming-2.unicode.stderr | 2 +- .../diagnostic-width/non-whitespace-trimming-unicode.rs | 1 + .../non-whitespace-trimming-unicode.stderr | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming.rs | 1 + tests/ui/diagnostic-width/non-whitespace-trimming.stderr | 2 +- 10 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr index 60ce0d9a14839..fe1ecfbe71d4c 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:7:237 + --> $DIR/non-1-width-unicode-multiline-label.rs:8:237 | LL | ...👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -14,7 +14,7 @@ LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:9:384 + --> $DIR/non-1-width-unicode-multiline-label.rs:10:384 | LL | ...👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -29,7 +29,7 @@ LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:11:260 + --> $DIR/non-1-width-unicode-multiline-label.rs:12:260 | LL | ...࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -44,7 +44,7 @@ LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓ | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:13:219 + --> $DIR/non-1-width-unicode-multiline-label.rs:14:219 | LL | ...xxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index 6b9b27f6297fc..0940b987fea11 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -1,4 +1,5 @@ //@ revisions: ascii unicode + //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr index 15b5dd9d7e2d7..4dff15642aeb4 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:237 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:8:237 │ LL │ …👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -14,7 +14,7 @@ LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:9:384 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:10:384 │ LL │ …👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -29,7 +29,7 @@ LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:11:260 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:12:260 │ LL │ …࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -44,7 +44,7 @@ LL │ let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓ ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:13:219 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:14:219 │ LL │ …xxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr index 70bd149545cfe..fe90cfc7b4168 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming-2.rs:6:311 + --> $DIR/non-whitespace-trimming-2.rs:7:311 | LL | ...13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let ... | -- ^^ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs index de2f42a4a7292..cdbef418f888b 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs @@ -1,4 +1,5 @@ //@ revisions: ascii unicode + //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr index 600d196de1673..b0151272cd120 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - ╭▸ $DIR/non-whitespace-trimming-2.rs:6:311 + ╭▸ $DIR/non-whitespace-trimming-2.rs:7:311 │ LL │ …= 13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let _:… │ ┬─ ━━ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs index 8d4d1b1627940..f5d1926e1eb62 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs @@ -1,3 +1,4 @@ + // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr index 18d80810ab0c3..e74afbfeac478 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming-unicode.rs:4:415 + --> $DIR/non-whitespace-trimming-unicode.rs:5:415 | LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽ ... | -- ^^ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.rs b/tests/ui/diagnostic-width/non-whitespace-trimming.rs index f6c8d345c652e..67066157a60a2 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.rs @@ -1,3 +1,4 @@ + // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming.stderr index 872c5ae3b7642..3403dc0e6f1fa 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming.rs:4:241 + --> $DIR/non-whitespace-trimming.rs:5:241 | LL | ... () = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ... | -- ^^ expected `()`, found integer From 0a5817aa2eb82f03fb8246eedaec089a81291aba Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Tue, 29 Jul 2025 18:56:32 -0600 Subject: [PATCH 1172/1900] test: Set diagnostic-width flag for long diagnostic width tests --- .../ui/diagnostic-width/non-1-width-unicode-multiline-label.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-2.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming.rs | 2 +- tests/ui/diagnostic-width/tabs-trimming.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index 0940b987fea11..7fb95a4ac1e21 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -1,5 +1,5 @@ //@ revisions: ascii unicode - +//@ compile-flags: --diagnostic-width=145 //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs index cdbef418f888b..6e80649fee86e 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs @@ -1,5 +1,5 @@ //@ revisions: ascii unicode - +//@ compile-flags: --diagnostic-width=145 //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs index f5d1926e1eb62..258eb090aae36 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs @@ -1,4 +1,4 @@ - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.rs b/tests/ui/diagnostic-width/non-whitespace-trimming.rs index 67066157a60a2..cb9450f0de0bd 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.rs @@ -1,4 +1,4 @@ - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/tabs-trimming.rs b/tests/ui/diagnostic-width/tabs-trimming.rs index 96babde33e9a4..5cf6bc89a1675 100644 --- a/tests/ui/diagnostic-width/tabs-trimming.rs +++ b/tests/ui/diagnostic-width/tabs-trimming.rs @@ -1,5 +1,5 @@ // Test for #78438: ensure underline alignment with many tabs on the left, long line on the right - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength // ignore-tidy-tab From 9c6897bd07ddb519d0c2f809bb1f83044a170b92 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 30 May 2025 16:22:56 -0600 Subject: [PATCH 1173/1900] test: Subtract code_offset from width for ui_testing --- compiler/rustc_errors/src/emitter.rs | 2 +- .../clippy/tests/ui/option_env_unwrap.stderr | 2 +- .../ui/too_long_first_doc_paragraph.stderr | 2 +- .../libc_pthread_join_joined.stderr | 4 +- .../concurrency/libc_pthread_join_main.stderr | 4 +- .../fail/data_race/dealloc_read_race1.stderr | 14 +++---- .../data_race/dealloc_read_race_stack.stderr | 4 +- .../fail/data_race/dealloc_write_race1.stderr | 14 +++---- .../data_race/dealloc_write_race_stack.stderr | 4 +- .../fail/intrinsics/copy_unaligned.stderr | 4 +- .../fail/intrinsics/simd-float-to-int.stderr | 4 +- .../retag_data_race_read.stderr | 4 +- .../ui/async-await/async-drop/ex-ice1.stderr | 8 ++-- .../ui/cast/func-pointer-issue-140491.stderr | 4 +- tests/ui/consts/offset_ub.stderr | 4 +- tests/ui/deprecation/deprecation-lint.stderr | 24 +++++------ .../ui/lint/lint-stability-deprecated.stderr | 40 +++++++++---------- tests/ui/parser/raw/too-many-hash.stderr | 4 +- .../empty-match.exhaustive_patterns.stderr | 4 +- .../usefulness/empty-match.normal.stderr | 4 +- tests/ui/simd/not-out-of-bounds.stderr | 4 +- .../alignment/align-fail.stderr | 4 +- 22 files changed, 81 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b1e6b761520..cc3db167316e0 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2021,7 +2021,7 @@ impl HumanEmitter { if let Some(width) = self.diagnostic_width { width.saturating_sub(code_offset) } else if self.ui_testing || cfg!(miri) { - DEFAULT_COLUMN_WIDTH + DEFAULT_COLUMN_WIDTH.saturating_sub(code_offset) } else { termize::dimensions() .map(|(w, _)| w.saturating_sub(code_offset)) diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr index bbcbfedb78822..c14a3ea23ff3e 100644 --- a/src/tools/clippy/tests/ui/option_env_unwrap.stderr +++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr @@ -19,7 +19,7 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set error: this will panic at run-time if the environment variable doesn't exist at compile-time --> tests/ui/option_env_unwrap.rs:14:13 | -LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your env... +LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in you... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr index f3f182aa54222..949ada30dc97d 100644 --- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr +++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr @@ -42,7 +42,7 @@ LL | | /// gravida non lacinia at, rhoncus eu lacus. error: first doc comment paragraph is too long --> tests/ui/too_long_first_doc_paragraph.rs:65:1 | -LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lore... +LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industr... LL | | LL | | /// LL | | /// Here's a second paragraph. It would be preferable to put the details here. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr index 354ad5dfc6022..e22f4dadf3f64 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: trying to join an already joined thread --> tests/fail-dep/concurrency/libc_pthread_join_joined.rs:LL:CC | -LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr index 41190f3f903eb..978a9bdac94d5 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: trying to join a detached thread --> tests/fail-dep/concurrency/libc_pthread_join_main.rs:LL:CC | -LL | assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr index 55e4c4a0d9e83..006946f3f5080 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_read_race1.rs:LL:CC | -LL | / __rust_dealloc( -LL | | -LL | | ptr.0 as *mut _, -LL | | std::mem::size_of::(), -LL | | std::mem::align_of::(), -LL | | ); - | |_____________^ (2) just happened here +LL | / ... __rust_dealloc( +LL | | ... +LL | | ... ptr.0 as *mut _, +LL | | ... std::mem::size_of::(), +LL | | ... std::mem::align_of::(), +LL | | ... ); + | |_______^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_read_race1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr index ea8fd73393c4e..f80e36d4b8fbf 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_read_race_stack.rs:LL:CC | -LL | } - | ^ (2) just happened here +LL | ... } + | ^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_read_race_stack.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr index b306f18123127..41482e4941c1b 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_write_race1.rs:LL:CC | -LL | / __rust_dealloc( -LL | | -LL | | ptr.0 as *mut _, -LL | | std::mem::size_of::(), -LL | | std::mem::align_of::(), -LL | | ); - | |_____________^ (2) just happened here +LL | / ... __rust_dealloc( +LL | | ... +LL | | ... ptr.0 as *mut _, +LL | | ... std::mem::size_of::(), +LL | | ... std::mem::align_of::(), +LL | | ... ); + | |_______^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_write_race1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr index f3a8707ed14ed..e6ebd5b6278ec 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_write_race_stack.rs:LL:CC | -LL | } - | ^ (2) just happened here +LL | ... } + | ^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_write_race_stack.rs:LL:CC diff --git a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr index f561e502b5146..d9b41f5b0581c 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr +++ b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required --> tests/fail/intrinsics/copy_unaligned.rs:LL:CC | -LL | std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr index 04e9cb338cdd3..64b8e166c1668 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: `simd_cast` intrinsic called on 3.40282347E+38f32 which cannot be represented in target type `i32` --> tests/fail/intrinsics/simd-float-to-int.rs:LL:CC | -LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr index d84fbc76056d6..ee82bbbb1ed14 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC --> tests/fail/stacked_borrows/retag_data_race_read.rs:LL:CC | -LL | *p = 5; - | ^^^^^^ (2) just happened here +LL | ... *p = 5; + | ^^^^^^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/stacked_borrows/retag_data_race_read.rs:LL:CC diff --git a/tests/ui/async-await/async-drop/ex-ice1.stderr b/tests/ui/async-await/async-drop/ex-ice1.stderr index 68533edeb086c..a1fa89f2c4001 100644 --- a/tests/ui/async-await/async-drop/ex-ice1.stderr +++ b/tests/ui/async-await/async-drop/ex-ice1.stderr @@ -1,14 +1,14 @@ error[E0423]: expected function, found module `core::future::async_drop` --> $DIR/ex-ice1.rs:9:35 | -LL | let async_drop_fut = pin!(core::future::async_drop(async {})); - | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function +LL | ... let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function error[E0603]: module `async_drop` is private --> $DIR/ex-ice1.rs:9:49 | -LL | let async_drop_fut = pin!(core::future::async_drop(async {})); - | ^^^^^^^^^^ private module +LL | ... let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^ private module | note: the module `async_drop` is defined here --> $SRC_DIR/core/src/future/mod.rs:LL:COL diff --git a/tests/ui/cast/func-pointer-issue-140491.stderr b/tests/ui/cast/func-pointer-issue-140491.stderr index e1c07010e691c..0b777905da1f9 100644 --- a/tests/ui/cast/func-pointer-issue-140491.stderr +++ b/tests/ui/cast/func-pointer-issue-140491.stderr @@ -1,8 +1,8 @@ error[E0605]: non-primitive cast: `&for<'a, 'b> fn(&'a Event<'b>) {my_fn}` as `&for<'a, 'b> fn(&'a Event<'b>)` --> $DIR/func-pointer-issue-140491.rs:6:34 | -LL | ..._>) = &my_fn as _; - | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +LL | ... = &my_fn as _; + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object | = note: casting reference expression `&my_fn` because `&` binds tighter than `as` diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index d92ca09223de7..f40d342758360 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -43,8 +43,8 @@ LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).of error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which points to before the beginning of the allocation --> $DIR/offset_ub.rs:16:49 | -LL | ...*const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `NEGATIVE_OFFSET` failed here +LL | ...nst u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `NEGATIVE_OFFSET` failed here error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes --> $DIR/offset_ub.rs:18:50 diff --git a/tests/ui/deprecation/deprecation-lint.stderr b/tests/ui/deprecation/deprecation-lint.stderr index 95ae1b04d8611..5a39b0cf31516 100644 --- a/tests/ui/deprecation/deprecation-lint.stderr +++ b/tests/ui/deprecation/deprecation-lint.stderr @@ -73,8 +73,8 @@ LL | let _ = nested::DeprecatedStruct { error: use of deprecated unit struct `deprecation_lint::nested::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:48:25 | -LL | let _ = nested::DeprecatedUnitStruct; - | ^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::DeprecatedUnitStruct; + | ^^^^^^^^^^^^^^^^^^^^ error: use of deprecated unit variant `deprecation_lint::nested::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:50:31 @@ -97,8 +97,8 @@ LL | macro_test_arg!(deprecated_text()); error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint.rs:60:41 | -LL | macro_test_arg!(macro_test_arg!(deprecated_text())); - | ^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(macro_test_arg!(deprecated_text())); + | ^^^^^^^^^^^^^^^ error: use of deprecated method `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:65:16 @@ -211,8 +211,8 @@ LL | Trait::trait_deprecated_text(&foo); error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:261:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated_future`: text --> $DIR/deprecation-lint.rs:264:9 @@ -295,8 +295,8 @@ LL | Trait::trait_deprecated_text(&foo); error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:299:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::test_fn_closure_body::{closure#0}::bar` --> $DIR/deprecation-lint.rs:317:13 @@ -391,8 +391,8 @@ LL | foo.method_deprecated_text(); error: use of deprecated method `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:27:14 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated method `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:28:16 @@ -589,8 +589,8 @@ LL | Foo::method_deprecated_text(&foo); error: use of deprecated method `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:257:16 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:258:13 diff --git a/tests/ui/lint/lint-stability-deprecated.stderr b/tests/ui/lint/lint-stability-deprecated.stderr index 0399fab746ec4..bda4ee82d1fc6 100644 --- a/tests/ui/lint/lint-stability-deprecated.stderr +++ b/tests/ui/lint/lint-stability-deprecated.stderr @@ -43,8 +43,8 @@ LL | Trait::trait_deprecated_text(&foo); warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:40:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:42:9 @@ -115,8 +115,8 @@ LL | let _ = Enum::DeprecatedVariant; warning: use of deprecated unit variant `lint_stability::Enum::DeprecatedUnstableVariant`: text --> $DIR/lint-stability-deprecated.rs:121:23 | -LL | let _ = Enum::DeprecatedUnstableVariant; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = Enum::DeprecatedUnstableVariant; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated tuple struct `lint_stability::DeprecatedTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:125:17 @@ -127,8 +127,8 @@ LL | let _ = DeprecatedTupleStruct (1); warning: use of deprecated tuple struct `lint_stability::DeprecatedUnstableTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:126:17 | -LL | let _ = DeprecatedUnstableTupleStruct (1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = DeprecatedUnstableTupleStruct (1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:135:25 @@ -139,14 +139,14 @@ LL | macro_test_arg!(deprecated_text()); warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:136:25 | -LL | macro_test_arg!(deprecated_unstable_text()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(deprecated_unstable_text()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:137:41 | -LL | macro_test_arg!(macro_test_arg!(deprecated_text())); - | ^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(macro_test_arg!(deprecated_text())); + | ^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:142:16 @@ -169,8 +169,8 @@ LL | Trait::trait_deprecated_text(&foo); warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:148:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:150:16 @@ -211,8 +211,8 @@ LL | trait LocalTrait2 : DeprecatedTrait { } warning: use of deprecated function `inheritance::inherited_stability::unstable_mod::deprecated`: text --> $DIR/lint-stability-deprecated.rs:205:23 | -LL | unstable_mod::deprecated(); - | ^^^^^^^^^^ +LL | ... unstable_mod::deprecated(); + | ^^^^^^^^^^ warning: use of deprecated function `this_crate::deprecated`: text --> $DIR/lint-stability-deprecated.rs:327:9 @@ -367,14 +367,14 @@ LL | foo.method_deprecated_text(); warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:35:14 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:36:16 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:37:13 @@ -397,8 +397,8 @@ LL | foo.method_deprecated_unstable(); warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:44:14 | -LL | Foo::method_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:45:16 diff --git a/tests/ui/parser/raw/too-many-hash.stderr b/tests/ui/parser/raw/too-many-hash.stderr index 6b3854eb4a237..499b21d3982c8 100644 --- a/tests/ui/parser/raw/too-many-hash.stderr +++ b/tests/ui/parser/raw/too-many-hash.stderr @@ -1,8 +1,8 @@ error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 --> $DIR/too-many-hash.rs:4:19 | -LL | ... = r####################################################...#######################################; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... = r###################################################...######################################; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index f2067f0341fcd..b649aa122b255 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -346,8 +346,8 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered --> $DIR/empty-match.rs:71:24 | -LL | match_guarded_arm!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered +LL | ...ded_arm!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here --> $DIR/empty-match.rs:38:10 diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index f2067f0341fcd..b649aa122b255 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -346,8 +346,8 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered --> $DIR/empty-match.rs:71:24 | -LL | match_guarded_arm!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered +LL | ...ded_arm!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here --> $DIR/empty-match.rs:38:10 diff --git a/tests/ui/simd/not-out-of-bounds.stderr b/tests/ui/simd/not-out-of-bounds.stderr index 734c21fbf4158..d5bcd15d97d6c 100644 --- a/tests/ui/simd/not-out-of-bounds.stderr +++ b/tests/ui/simd/not-out-of-bounds.stderr @@ -79,8 +79,8 @@ LL | simd_insert(v, 2, 0u8); error[E0511]: invalid monomorphization of `simd_extract` intrinsic: SIMD index #1 is out of bounds (limit 2) --> $DIR/not-out-of-bounds.rs:85:24 | -LL | let _val: u8 = simd_extract(v, 2); - | ^^^^^^^^^^^^^^^^^^ +LL | ... let _val: u8 = simd_extract(v, 2); + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 9 previous errors diff --git a/tests/ui/transmutability/alignment/align-fail.stderr b/tests/ui/transmutability/alignment/align-fail.stderr index 7b69820a3c6df..164c57a362442 100644 --- a/tests/ui/transmutability/alignment/align-fail.stderr +++ b/tests/ui/transmutability/alignment/align-fail.stderr @@ -1,8 +1,8 @@ error[E0277]: `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]` --> $DIR/align-fail.rs:21:55 | -LL | ...tatic [u8; 0], &'static [u16; 0]>(); - | ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2) +LL | ...ic [u8; 0], &'static [u16; 0]>(); + | ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2) | note: required by a bound in `is_maybe_transmutable` --> $DIR/align-fail.rs:9:14 From 6d06db51b6f487bcafcf58554bf471b90fb529c6 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 30 May 2025 16:22:56 -0600 Subject: [PATCH 1174/1900] test: Subtract code_offset from width for ui_testing --- tests/ui/option_env_unwrap.stderr | 2 +- tests/ui/too_long_first_doc_paragraph.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr index bbcbfedb78822..c14a3ea23ff3e 100644 --- a/tests/ui/option_env_unwrap.stderr +++ b/tests/ui/option_env_unwrap.stderr @@ -19,7 +19,7 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set error: this will panic at run-time if the environment variable doesn't exist at compile-time --> tests/ui/option_env_unwrap.rs:14:13 | -LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your env... +LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in you... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead diff --git a/tests/ui/too_long_first_doc_paragraph.stderr b/tests/ui/too_long_first_doc_paragraph.stderr index f3f182aa54222..949ada30dc97d 100644 --- a/tests/ui/too_long_first_doc_paragraph.stderr +++ b/tests/ui/too_long_first_doc_paragraph.stderr @@ -42,7 +42,7 @@ LL | | /// gravida non lacinia at, rhoncus eu lacus. error: first doc comment paragraph is too long --> tests/ui/too_long_first_doc_paragraph.rs:65:1 | -LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lore... +LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industr... LL | | LL | | /// LL | | /// Here's a second paragraph. It would be preferable to put the details here. From 6275a19255afb2a09b932986928e81cf431a7358 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 2 Oct 2025 15:24:04 +0200 Subject: [PATCH 1175/1900] Move doc_cfg-specific code into `cfg.rs` --- src/librustdoc/clean/cfg.rs | 267 +++++++++++++++++++++++++++++++++- src/librustdoc/clean/mod.rs | 1 + src/librustdoc/clean/types.rs | 264 +-------------------------------- 3 files changed, 268 insertions(+), 264 deletions(-) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index aa614b73dea72..881a81b22f0f7 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -3,14 +3,18 @@ // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619), // switch to use those structures instead. +use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::attrs::AttributeKind; +use rustc_middle::ty::TyCtxt; use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; +use {rustc_ast as ast, rustc_hir as hir}; use crate::display::{Joined as _, MaybeDisplay, Wrapped}; use crate::html::escape::Escape; @@ -600,3 +604,264 @@ impl fmt::Display for Display<'_> { } } } + +/// This type keeps track of (doc) cfg information as we go down the item tree. +#[derive(Clone, Debug)] +pub(crate) struct CfgInfo { + /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active + /// `doc(auto_cfg(show(...)))` cfgs. + hidden_cfg: FxHashSet, + /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while + /// taking into account the `hidden_cfg` information. + current_cfg: Cfg, + /// Whether the `doc(auto_cfg())` feature is enabled or not at this point. + auto_cfg_active: bool, + /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`, + /// instead we will concatenate with it. However, if it's not the case, we need to overwrite + /// `current_cfg`. + parent_is_doc_cfg: bool, +} + +impl Default for CfgInfo { + fn default() -> Self { + Self { + hidden_cfg: FxHashSet::from_iter([ + Cfg::Cfg(sym::test, None), + Cfg::Cfg(sym::doc, None), + Cfg::Cfg(sym::doctest, None), + ]), + current_cfg: Cfg::True, + auto_cfg_active: true, + parent_is_doc_cfg: false, + } + } +} + +fn show_hide_show_conflict_error( + tcx: TyCtxt<'_>, + item_span: rustc_span::Span, + previous: rustc_span::Span, +) { + let mut diag = tcx.sess.dcx().struct_span_err( + item_span, + format!( + "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" + ), + ); + diag.span_note(previous, "first change was here"); + diag.emit(); +} + +/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. +/// +/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and +/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. +/// +/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` +/// and in `new_hide_attrs` arguments. +fn handle_auto_cfg_hide_show( + tcx: TyCtxt<'_>, + cfg_info: &mut CfgInfo, + sub_attr: &MetaItemInner, + is_show: bool, + new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, + new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, +) { + if let MetaItemInner::MetaItem(item) = sub_attr + && let MetaItemKind::List(items) = &item.kind + { + for item in items { + // FIXME: Report in case `Cfg::parse` reports an error? + if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) { + if is_show { + if let Some(span) = new_hide_attrs.get(&(key, value)) { + show_hide_show_conflict_error(tcx, item.span(), *span); + } else { + new_show_attrs.insert((key, value), item.span()); + } + cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value)); + } else { + if let Some(span) = new_show_attrs.get(&(key, value)) { + show_hide_show_conflict_error(tcx, item.span(), *span); + } else { + new_hide_attrs.insert((key, value), item.span()); + } + cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value)); + } + } + } + } +} + +pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator + Clone>( + attrs: I, + tcx: TyCtxt<'_>, + cfg_info: &mut CfgInfo, +) -> Option> { + fn single(it: T) -> Option { + let mut iter = it.into_iter(); + let item = iter.next()?; + if iter.next().is_some() { + return None; + } + Some(item) + } + + fn check_changed_auto_active_status( + changed_auto_active_status: &mut Option, + attr: &ast::MetaItem, + cfg_info: &mut CfgInfo, + tcx: TyCtxt<'_>, + new_value: bool, + ) -> bool { + if let Some(first_change) = changed_auto_active_status { + if cfg_info.auto_cfg_active != new_value { + tcx.sess + .dcx() + .struct_span_err( + vec![*first_change, attr.span], + "`auto_cfg` was disabled and enabled more than once on the same item", + ) + .emit(); + return true; + } + } else { + *changed_auto_active_status = Some(attr.span); + } + cfg_info.auto_cfg_active = new_value; + false + } + + let mut new_show_attrs = FxHashMap::default(); + let mut new_hide_attrs = FxHashMap::default(); + + let mut doc_cfg = attrs + .clone() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)) + .peekable(); + // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes. + if doc_cfg.peek().is_some() { + let sess = tcx.sess; + // We overwrite existing `cfg`. + if !cfg_info.parent_is_doc_cfg { + cfg_info.current_cfg = Cfg::True; + cfg_info.parent_is_doc_cfg = true; + } + for attr in doc_cfg { + if let Some(cfg_mi) = + attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess)) + { + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg_info.current_cfg &= new_cfg, + Err(e) => { + sess.dcx().span_err(e.span, e.msg); + } + } + } + } + } else { + cfg_info.parent_is_doc_cfg = false; + } + + let mut changed_auto_active_status = None; + + // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes. + for attr in attrs { + if let Some(ident) = attr.ident() + && ident.name == sym::doc + && let Some(attrs) = attr.meta_item_list() + { + for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) { + let MetaItemInner::MetaItem(attr) = attr else { + continue; + }; + match &attr.kind { + MetaItemKind::Word => { + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + true, + ) { + return None; + } + } + MetaItemKind::NameValue(lit) => { + if let LitKind::Bool(value) = lit.kind { + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + value, + ) { + return None; + } + } + } + MetaItemKind::List(sub_attrs) => { + if check_changed_auto_active_status( + &mut changed_auto_active_status, + attr, + cfg_info, + tcx, + true, + ) { + return None; + } + for sub_attr in sub_attrs.iter() { + if let Some(ident) = sub_attr.ident() + && (ident.name == sym::show || ident.name == sym::hide) + { + handle_auto_cfg_hide_show( + tcx, + cfg_info, + &sub_attr, + ident.name == sym::show, + &mut new_show_attrs, + &mut new_hide_attrs, + ); + } + } + } + } + } + } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { + // Treat `#[target_feature(enable = "feat")]` attributes as if they were + // `#[doc(cfg(target_feature = "feat"))]` attributes as well. + for (feature, _) in features { + cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature)); + } + continue; + } else if !cfg_info.parent_is_doc_cfg + && let Some(ident) = attr.ident() + && matches!(ident.name, sym::cfg | sym::cfg_trace) + && let Some(attr) = single(attr.meta_item_list()?) + && let Ok(new_cfg) = Cfg::parse(&attr) + { + cfg_info.current_cfg &= new_cfg; + } + } + + // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing + // to be done here. + if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg { + None + } else if cfg_info.parent_is_doc_cfg { + if cfg_info.current_cfg == Cfg::True { + None + } else { + Some(Arc::new(cfg_info.current_cfg.clone())) + } + } else { + // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the + // hidden ones afterward. + match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) { + None | Some(Cfg::True) => None, + Some(cfg) => Some(Arc::new(cfg)), + } + } +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c6339dd475593..4fd8d245089e7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -58,6 +58,7 @@ use tracing::{debug, instrument}; use utils::*; use {rustc_ast as ast, rustc_hir as hir}; +pub(crate) use self::cfg::{CfgInfo, extract_cfg_from_attrs}; pub(crate) use self::types::*; pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; use crate::core::DocContext; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6e3dfa0ac221c..f3662a67bbea2 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -6,8 +6,7 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use itertools::Either; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_ast::ast::{LitKind, MetaItemInner, MetaItemKind}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -922,267 +921,6 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( .flatten() } -/// This type keeps track of (doc) cfg information as we go down the item tree. -#[derive(Clone, Debug)] -pub(crate) struct CfgInfo { - /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active - /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashSet, - /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while - /// taking into account the `hidden_cfg` information. - current_cfg: Cfg, - /// Whether the `doc(auto_cfg())` feature is enabled or not at this point. - auto_cfg_active: bool, - /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`, - /// instead we will concatenate with it. However, if it's not the case, we need to overwrite - /// `current_cfg`. - parent_is_doc_cfg: bool, -} - -impl Default for CfgInfo { - fn default() -> Self { - Self { - hidden_cfg: FxHashSet::from_iter([ - Cfg::Cfg(sym::test, None), - Cfg::Cfg(sym::doc, None), - Cfg::Cfg(sym::doctest, None), - ]), - current_cfg: Cfg::True, - auto_cfg_active: true, - parent_is_doc_cfg: false, - } - } -} - -fn show_hide_show_conflict_error( - tcx: TyCtxt<'_>, - item_span: rustc_span::Span, - previous: rustc_span::Span, -) { - let mut diag = tcx.sess.dcx().struct_span_err( - item_span, - format!( - "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" - ), - ); - diag.span_note(previous, "first change was here"); - diag.emit(); -} - -/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. -/// -/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and -/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. -/// -/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` -/// and in `new_hide_attrs` arguments. -fn handle_auto_cfg_hide_show( - tcx: TyCtxt<'_>, - cfg_info: &mut CfgInfo, - sub_attr: &MetaItemInner, - is_show: bool, - new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, - new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, -) { - if let MetaItemInner::MetaItem(item) = sub_attr - && let MetaItemKind::List(items) = &item.kind - { - for item in items { - // FIXME: Report in case `Cfg::parse` reports an error? - if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) { - if is_show { - if let Some(span) = new_hide_attrs.get(&(key, value)) { - show_hide_show_conflict_error(tcx, item.span(), *span); - } else { - new_show_attrs.insert((key, value), item.span()); - } - cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value)); - } else { - if let Some(span) = new_show_attrs.get(&(key, value)) { - show_hide_show_conflict_error(tcx, item.span(), *span); - } else { - new_hide_attrs.insert((key, value), item.span()); - } - cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value)); - } - } - } - } -} - -pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator + Clone>( - attrs: I, - tcx: TyCtxt<'_>, - cfg_info: &mut CfgInfo, -) -> Option> { - fn single(it: T) -> Option { - let mut iter = it.into_iter(); - let item = iter.next()?; - if iter.next().is_some() { - return None; - } - Some(item) - } - - fn check_changed_auto_active_status( - changed_auto_active_status: &mut Option, - attr: &ast::MetaItem, - cfg_info: &mut CfgInfo, - tcx: TyCtxt<'_>, - new_value: bool, - ) -> bool { - if let Some(first_change) = changed_auto_active_status { - if cfg_info.auto_cfg_active != new_value { - tcx.sess - .dcx() - .struct_span_err( - vec![*first_change, attr.span], - "`auto_cfg` was disabled and enabled more than once on the same item", - ) - .emit(); - return true; - } - } else { - *changed_auto_active_status = Some(attr.span); - } - cfg_info.auto_cfg_active = new_value; - false - } - - let mut new_show_attrs = FxHashMap::default(); - let mut new_hide_attrs = FxHashMap::default(); - - let mut doc_cfg = attrs - .clone() - .filter(|attr| attr.has_name(sym::doc)) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) - .filter(|attr| attr.has_name(sym::cfg)) - .peekable(); - // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes. - if doc_cfg.peek().is_some() { - let sess = tcx.sess; - // We overwrite existing `cfg`. - if !cfg_info.parent_is_doc_cfg { - cfg_info.current_cfg = Cfg::True; - cfg_info.parent_is_doc_cfg = true; - } - for attr in doc_cfg { - if let Some(cfg_mi) = - attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess)) - { - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg_info.current_cfg &= new_cfg, - Err(e) => { - sess.dcx().span_err(e.span, e.msg); - } - } - } - } - } else { - cfg_info.parent_is_doc_cfg = false; - } - - let mut changed_auto_active_status = None; - - // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes. - for attr in attrs { - if let Some(ident) = attr.ident() - && ident.name == sym::doc - && let Some(attrs) = attr.meta_item_list() - { - for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) { - let MetaItemInner::MetaItem(attr) = attr else { - continue; - }; - match &attr.kind { - MetaItemKind::Word => { - if check_changed_auto_active_status( - &mut changed_auto_active_status, - attr, - cfg_info, - tcx, - true, - ) { - return None; - } - } - MetaItemKind::NameValue(lit) => { - if let LitKind::Bool(value) = lit.kind { - if check_changed_auto_active_status( - &mut changed_auto_active_status, - attr, - cfg_info, - tcx, - value, - ) { - return None; - } - } - } - MetaItemKind::List(sub_attrs) => { - if check_changed_auto_active_status( - &mut changed_auto_active_status, - attr, - cfg_info, - tcx, - true, - ) { - return None; - } - for sub_attr in sub_attrs.iter() { - if let Some(ident) = sub_attr.ident() - && (ident.name == sym::show || ident.name == sym::hide) - { - handle_auto_cfg_hide_show( - tcx, - cfg_info, - &sub_attr, - ident.name == sym::show, - &mut new_show_attrs, - &mut new_hide_attrs, - ); - } - } - } - } - } - } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { - // Treat `#[target_feature(enable = "feat")]` attributes as if they were - // `#[doc(cfg(target_feature = "feat"))]` attributes as well. - for (feature, _) in features { - cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature)); - } - continue; - } else if !cfg_info.parent_is_doc_cfg - && let Some(ident) = attr.ident() - && matches!(ident.name, sym::cfg | sym::cfg_trace) - && let Some(attr) = single(attr.meta_item_list()?) - && let Ok(new_cfg) = Cfg::parse(&attr) - { - cfg_info.current_cfg &= new_cfg; - } - } - - // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing - // to be done here. - if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg { - None - } else if cfg_info.parent_is_doc_cfg { - if cfg_info.current_cfg == Cfg::True { - None - } else { - Some(Arc::new(cfg_info.current_cfg.clone())) - } - } else { - // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the - // hidden ones afterward. - match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) { - None | Some(Cfg::True) => None, - Some(cfg) => Some(Arc::new(cfg)), - } - } -} - pub(crate) trait NestedAttributesExt { /// Returns `true` if the attribute list contains a specific `word` fn has_word(self, word: Symbol) -> bool From 7715c0d648cade90c172ee73ca26c8d80ff752f0 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 2 Oct 2025 11:47:14 +0100 Subject: [PATCH 1176/1900] add arm-maintainers to various targets --- src/doc/rustc/src/SUMMARY.md | 2 + src/doc/rustc/src/platform-support.md | 6 +-- .../aarch64-unknown-linux-gnu.md | 50 ++++++++++++++++++ .../platform-support/aarch64-unknown-none.md | 5 +- .../rustc/src/platform-support/arm-linux.md | 4 +- .../armv7-unknown-linux-gnueabi.md | 51 +++++++++++++++++++ .../src/platform-support/armv7a-none-eabi.md | 5 +- .../src/platform-support/armv7r-none-eabi.md | 7 ++- .../platform-support/armv8r-none-eabihf.md | 7 ++- .../platform-support/thumbv7em-none-eabi.md | 6 ++- .../platform-support/thumbv7m-none-eabi.md | 6 ++- .../thumbv8m.base-none-eabi.md | 6 ++- .../thumbv8m.main-none-eabi.md | 6 ++- .../src/platform-support/unknown-uefi.md | 8 ++- 14 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md create mode 100644 src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 67882bb38132a..a9ce738a01387 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -47,6 +47,7 @@ - [\*-apple-watchos](platform-support/apple-watchos.md) - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) + - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) @@ -67,6 +68,7 @@ - [arm\*-unknown-linux-\*](./platform-support/arm-linux.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md) + - [armv7-unknown-linux-gnueabi](platform-support/armv7-unknown-linux-gnueabi.md) - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d0b6ed51bc163..263ea1ddb4250 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,7 +34,7 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) [`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC -`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+) +[`aarch64-unknown-linux-gnu`](platform-support/aarch64-unknown-linux-gnu.md) | ARM64 Linux (kernel 4.1+, glibc 2.17+) [`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) @@ -93,7 +93,7 @@ target | notes [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17) -`armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17) +[`armv7-unknown-linux-gnueabihf`](platform-support/armv7-unknown-linux-gnueabi.md) | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17) [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required @@ -159,7 +159,7 @@ target | std | notes [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23) `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android -`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27) +[`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27) `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md new file mode 100644 index 0000000000000..2003a3cb9eaa2 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md @@ -0,0 +1,50 @@ +# `aarch64-unknown-linux-gnu` + +**Tier: 1 (with Host Tools)** + +Target for 64-bit little endian ARMv8-A Linux 4.1+ programs using glibc 2.17+. + +## Target maintainers + +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com + +## Requirements + +Building the target itself requires a 64-bit little endian ARMv8-A compiler that is supported by +`cc-rs`. + +## Building the target + +The target can be built by enabling it for a `rustc` build: + +```toml +[build] +target = ["aarch64-unknown-linux-gnu"] +``` + +If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the +`bootstrap.toml`: + +```toml +[target.aarch64-unknown-linux-musl] +cc = "aarch64-linux-gnu-gcc" +cxx = "aarch64-linux-gnu-g++" +ar = "aarch64-linux-gnu-ar" +linker = "aarch64-linux-gnu-gcc" +``` + +## Building Rust programs + +This target is distributed through `rustup`, and otherwise requires no special configuration. + +## Cross-compilation + +This target can be cross-compiled from any host. + +## Testing + +This target can be tested as normal with `x.py` on a 64-bit little endian ARMv8-A host or via QEMU +emulation. diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index 7e18e8c157f74..3d776677d23e1 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -14,9 +14,12 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus ## Target maintainers -[Rust Embedded Devices Working Group Arm Team] +- [Rust Embedded Devices Working Group Arm Team] +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/arm-linux.md b/src/doc/rustc/src/platform-support/arm-linux.md index 5f40743f3d070..c461a1a3403d5 100644 --- a/src/doc/rustc/src/platform-support/arm-linux.md +++ b/src/doc/rustc/src/platform-support/arm-linux.md @@ -14,8 +14,8 @@ Linux (but not Android). Those targets are: * [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md) * `armv5te-unknown-linux-musleabi` * `armv5te-unknown-linux-uclibceabi` -* `armv7-unknown-linux-gnueabi` -* `armv7-unknown-linux-gnueabihf` +* [`armv7-unknown-linux-gnueabi`](armv7-unknown-linux-gnueabi.md) +* [`armv7-unknown-linux-gnueabihf`](armv7-unknown-linux-gnueabi.md) * `armv7-unknown-linux-musleabi` * `armv7-unknown-linux-musleabihf` * `armv7-unknown-linux-ohos` diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md new file mode 100644 index 0000000000000..c2fe63a49087c --- /dev/null +++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md @@ -0,0 +1,51 @@ +# `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-gnueabihf` + +* **Tier: 2 (with Host Tools)** for `armv7-unknown-linux-gnueabihf` +* **Tier: 2** for `armv7-unknown-linux-gnueabi` + +Target for 32-bit little endian ARMv7-A Linux 3.2+ programs using glibc 2.17+. + +## Target maintainers + +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com + +## Requirements + +Building the targets themselves requires a 32-bit little endian ARMv7-A compiler that is supported +by `cc-rs`. + +## Building the target + +These targets can be built by enabling it for a `rustc` build: + +```toml +[build] +target = ["armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabi"] +``` + +If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the +`bootstrap.toml`: + +```toml +[target.aarch64-unknown-linux-musl] +cc = "arm-linux-gnu-gcc" +cxx = "arm-linux-gnu-g++" +ar = "arm-linux-gnu-ar" +linker = "arm-linux-gnu-gcc" +``` + +## Building Rust programs + +These targets is distributed through `rustup`, and otherwise requires no special configuration. + +## Cross-compilation + +These targets can be cross-compiled from any host. + +## Testing + +These targets can be tested as normal with `x.py` on a 32-bit little endian ARMv7-A host or via +QEMU emulation. diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 3dadda86a5f54..22278a0a7dc71 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -19,9 +19,12 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[Rust Embedded Devices Working Group Arm Team] +- [Rust Embedded Devices Working Group Arm Team] +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Requirements diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index c1252b4a4bf57..9429eb6ab8aed 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -15,10 +15,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[@chrisnc](https://github.com/chrisnc) -[Rust Embedded Devices Working Group Arm Team] +- [@chrisnc](https://github.com/chrisnc) +- [Rust Embedded Devices Working Group Arm Team] +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Requirements diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index 0d5a36c3ee2d6..e465eb79f49d1 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -17,10 +17,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[@chrisnc](https://github.com/chrisnc) -[Rust Embedded Devices Working Group Arm Team] +- [@chrisnc](https://github.com/chrisnc) +- [Rust Embedded Devices Working Group Arm Team] +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Requirements diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md index 98dcf9bd3968a..192e013d3a43f 100644 --- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md @@ -22,7 +22,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md index d8f3970c8bfab..b04cb7bfacfac 100644 --- a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md @@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv7-M]. ## Target maintainers -[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md index b16d450275df5..104520854b49c 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md @@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv8-M] Baseline. ## Target maintainers -[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md index a2d515d07ea0d..5cc535ce376b3 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md @@ -26,7 +26,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 9587590d12d1c..e8989616b844b 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -13,8 +13,12 @@ Available targets: ## Target maintainers -[@dvdhrm](https://github.com/dvdhrm) -[@nicholasbishop](https://github.com/nicholasbishop) +- [@dvdhrm](https://github.com/dvdhrm) +- [@nicholasbishop](https://github.com/nicholasbishop) +- (for `aarch64-unknown-uefi` only) [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com ## Requirements From 78c2e58a7acb3a73e65b9ccfe4a73e79baa0d9ca Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Wed, 1 Oct 2025 19:58:49 +0300 Subject: [PATCH 1177/1900] [rustdoc] Optimize "highlight::end_expansion" --- src/librustdoc/html/highlight.rs | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1dcb4dcc3ff83..eeb916494d1e7 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display, Write}; -use std::iter; +use std::{cmp, iter}; use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; @@ -345,33 +345,26 @@ fn end_expansion<'a, W: Write>( token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); return Some(expanded_code); } - if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() { - // No need tag opened so we can just close expansion. - token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); - return None; - } - // If tags were opened inside the expansion, we need to close them and re-open them outside - // of the expansion span. - let mut out = String::new(); - let mut end = String::new(); + let skip = iter::zip(token_handler.closing_tags.as_slice(), expansion_start_tags) + .position(|(tag, start_tag)| tag != start_tag) + .unwrap_or_else(|| cmp::min(token_handler.closing_tags.len(), expansion_start_tags.len())); - let mut closing_tags = token_handler.closing_tags.iter().peekable(); - let mut start_closing_tags = expansion_start_tags.iter().peekable(); + let tags = iter::chain( + expansion_start_tags.iter().skip(skip), + token_handler.closing_tags.iter().skip(skip), + ); - while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek()) - && tag == start_tag - { - closing_tags.next(); - start_closing_tags.next(); + let mut elem = Cow::Borrowed(""); + + for (tag, _) in tags.clone() { + elem.to_mut().push_str(tag); } - for (tag, class) in start_closing_tags.chain(closing_tags) { - out.push_str(tag); - end.push_str(&format!("", class.as_html())); + for (_, class) in tags { + write!(elem.to_mut(), "", class.as_html()).unwrap(); } - token_handler - .pending_elems - .push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); + + token_handler.pending_elems.push((elem, Some(Class::Expansion))); None } From c61ec946d24c559708d4d57fbe860817e854c610 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 22 Apr 2025 14:39:59 +0200 Subject: [PATCH 1178/1900] implement range support in `//@ edition` --- src/tools/compiletest/src/common.rs | 6 +- src/tools/compiletest/src/directives.rs | 97 +++++++++++- src/tools/compiletest/src/directives/tests.rs | 142 +++++++++++++++++- src/tools/compiletest/src/edition.rs | 35 +++++ src/tools/compiletest/src/lib.rs | 4 +- 5 files changed, 270 insertions(+), 14 deletions(-) create mode 100644 src/tools/compiletest/src/edition.rs diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6d9599489180b..65db816ad1a87 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -7,6 +7,7 @@ use build_helper::git::GitConfig; use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; +use crate::edition::Edition; use crate::executor::ColorConfig; use crate::fatal; use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum}; @@ -612,10 +613,7 @@ pub struct Config { pub git_hash: bool, /// The default Rust edition. - /// - /// FIXME: perform stronger validation for this. There are editions that *definitely* exists, - /// but there might also be "future" edition. - pub edition: Option, + pub edition: Option, // Configuration for various run-make tests frobbing things like C compilers or querying about // various LLVM component information. diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 0d85e76c2cc09..a79978d036cea 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -16,10 +16,11 @@ use crate::directives::directive_names::{ KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, }; use crate::directives::needs::CachedNeedsConditions; +use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldPanic}; -use crate::help; use crate::util::static_regex; +use crate::{fatal, help}; pub(crate) mod auxiliary; mod cfg; @@ -437,10 +438,13 @@ impl TestProps { panic!("`compiler-flags` directive should be spelled `compile-flags`"); } - if let Some(edition) = config.parse_edition(ln, testfile) { + if let Some(range) = parse_edition_range(config, ln, testfile) { // The edition is added at the start, since flags from //@compile-flags must // be passed to rustc last. - self.compile_flags.insert(0, format!("--edition={}", edition.trim())); + self.compile_flags.insert( + 0, + format!("--edition={}", range.edition_to_test(config.edition)), + ); has_edition = true; } @@ -1124,10 +1128,6 @@ impl Config { } } - fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option { - self.parse_name_value_directive(line, "edition", testfile) - } - fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { // If the flag is already true, don't bother looking at the directive. *value = *value || self.parse_name_directive(line, directive); @@ -1769,3 +1769,86 @@ enum IgnoreDecision { Continue, Error { message: String }, } + +fn parse_edition_range( + config: &Config, + line: &DirectiveLine<'_>, + testfile: &Utf8Path, +) -> Option { + let raw = config.parse_name_value_directive(line, "edition", testfile)?; + let line_number = line.line_number; + + // Edition range is half-open: `[lower_bound, upper_bound)` + if let Some((lower_bound, upper_bound)) = raw.split_once("..") { + Some(match (maybe_parse_edition(lower_bound), maybe_parse_edition(upper_bound)) { + (Some(lower_bound), Some(upper_bound)) if upper_bound <= lower_bound => { + fatal!( + "{testfile}:{line_number}: the left side of `//@ edition` cannot be greater than or equal to the right side" + ); + } + (Some(lower_bound), Some(upper_bound)) => { + EditionRange::Range { lower_bound, upper_bound } + } + (Some(lower_bound), None) => EditionRange::RangeFrom(lower_bound), + (None, Some(_)) => { + fatal!( + "{testfile}:{line_number}: `..edition` is not a supported range in `//@ edition`" + ); + } + (None, None) => { + fatal!("{testfile}:{line_number}: `..` is not a supported range in `//@ edition`"); + } + }) + } else { + match maybe_parse_edition(&raw) { + Some(edition) => Some(EditionRange::Exact(edition)), + None => { + fatal!("{testfile}:{line_number}: empty value for `//@ edition`"); + } + } + } +} + +fn maybe_parse_edition(mut input: &str) -> Option { + input = input.trim(); + if input.is_empty() { + return None; + } + Some(parse_edition(input)) +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum EditionRange { + Exact(Edition), + RangeFrom(Edition), + /// Half-open range: `[lower_bound, upper_bound)` + Range { + lower_bound: Edition, + upper_bound: Edition, + }, +} + +impl EditionRange { + fn edition_to_test(&self, requested: impl Into>) -> Edition { + let min_edition = Edition::Year(2015); + let requested = requested.into().unwrap_or(min_edition); + + match *self { + EditionRange::Exact(exact) => exact, + EditionRange::RangeFrom(lower_bound) => { + if requested >= lower_bound { + requested + } else { + lower_bound + } + } + EditionRange::Range { lower_bound, upper_bound } => { + if requested >= lower_bound && requested < upper_bound { + requested + } else { + lower_bound + } + } + } + } +} diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 16cf76be9a53f..93621192d4bd6 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -4,10 +4,11 @@ use camino::Utf8Path; use semver::Version; use super::{ - DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives, - parse_normalize_rule, + DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, + extract_version_range, iter_directives, parse_normalize_rule, }; use crate::common::{Config, Debugger, TestMode}; +use crate::directives::parse_edition; use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description( @@ -73,6 +74,7 @@ fn test_parse_normalize_rule() { struct ConfigBuilder { mode: Option, channel: Option, + edition: Option, host: Option, target: Option, stage: Option, @@ -96,6 +98,11 @@ impl ConfigBuilder { self } + fn edition(&mut self, e: Edition) -> &mut Self { + self.edition = Some(e); + self + } + fn host(&mut self, s: &str) -> &mut Self { self.host = Some(s.to_owned()); self @@ -183,6 +190,10 @@ impl ConfigBuilder { ]; let mut args: Vec = args.iter().map(ToString::to_string).collect(); + if let Some(edition) = &self.edition { + args.push(format!("--edition={edition}")); + } + if let Some(ref llvm_version) = self.llvm_version { args.push("--llvm-version".to_owned()); args.push(llvm_version.clone()); @@ -941,3 +952,130 @@ fn test_needs_target_std() { let config = cfg().target("x86_64-unknown-linux-gnu").build(); assert!(!check_ignore(&config, "//@ needs-target-std")); } + +fn parse_edition_range(line: &str) -> Option { + let config = cfg().build(); + let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line }; + + super::parse_edition_range(&config, &line, "tmp.rs".into()) +} + +#[test] +fn test_parse_edition_range() { + assert_eq!(None, parse_edition_range("hello-world")); + assert_eq!(None, parse_edition_range("edition")); + + assert_eq!(Some(EditionRange::Exact(2018.into())), parse_edition_range("edition: 2018")); + assert_eq!(Some(EditionRange::Exact(2021.into())), parse_edition_range("edition:2021")); + assert_eq!(Some(EditionRange::Exact(2024.into())), parse_edition_range("edition: 2024 ")); + assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future")); + + assert_eq!(Some(EditionRange::RangeFrom(2018.into())), parse_edition_range("edition: 2018..")); + assert_eq!(Some(EditionRange::RangeFrom(2021.into())), parse_edition_range("edition:2021 ..")); + assert_eq!( + Some(EditionRange::RangeFrom(2024.into())), + parse_edition_range("edition: 2024 .. ") + ); + assert_eq!( + Some(EditionRange::RangeFrom(Edition::Future)), + parse_edition_range("edition: future.. ") + ); + + assert_eq!( + Some(EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }), + parse_edition_range("edition: 2018..2024") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2015.into(), upper_bound: 2021.into() }), + parse_edition_range("edition:2015 .. 2021 ") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: 2027.into() }), + parse_edition_range("edition: 2021 .. 2027 ") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: Edition::Future }), + parse_edition_range("edition: 2021..future") + ); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_empty() { + parse_edition_range("edition:"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_invalid_edition() { + parse_edition_range("edition: hello"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_double_dots() { + parse_edition_range("edition: .."); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_inverted_range() { + parse_edition_range("edition: 2021..2015"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_inverted_range_future() { + parse_edition_range("edition: future..2015"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_empty_range() { + parse_edition_range("edition: 2021..2021"); +} + +#[track_caller] +fn assert_edition_to_test( + expected: impl Into, + range: EditionRange, + default: Option, +) { + let mut cfg = cfg(); + if let Some(default) = default { + cfg.edition(default); + } + assert_eq!(expected.into(), range.edition_to_test(cfg.build().edition)); +} + +#[test] +fn test_edition_range_edition_to_test() { + let e2015 = parse_edition("2015"); + let e2018 = parse_edition("2018"); + let e2021 = parse_edition("2021"); + let e2024 = parse_edition("2024"); + let efuture = parse_edition("future"); + + let exact = EditionRange::Exact(2021.into()); + assert_edition_to_test(2021, exact, None); + assert_edition_to_test(2021, exact, Some(e2018)); + assert_edition_to_test(2021, exact, Some(efuture)); + + assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None); + + let greater_equal_than = EditionRange::RangeFrom(2021.into()); + assert_edition_to_test(2021, greater_equal_than, None); + assert_edition_to_test(2021, greater_equal_than, Some(e2015)); + assert_edition_to_test(2021, greater_equal_than, Some(e2018)); + assert_edition_to_test(2021, greater_equal_than, Some(e2021)); + assert_edition_to_test(2024, greater_equal_than, Some(e2024)); + assert_edition_to_test(Edition::Future, greater_equal_than, Some(efuture)); + + let range = EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }; + assert_edition_to_test(2018, range, None); + assert_edition_to_test(2018, range, Some(e2015)); + assert_edition_to_test(2018, range, Some(e2018)); + assert_edition_to_test(2021, range, Some(e2021)); + assert_edition_to_test(2018, range, Some(e2024)); + assert_edition_to_test(2018, range, Some(efuture)); +} diff --git a/src/tools/compiletest/src/edition.rs b/src/tools/compiletest/src/edition.rs new file mode 100644 index 0000000000000..36550cf5b2b95 --- /dev/null +++ b/src/tools/compiletest/src/edition.rs @@ -0,0 +1,35 @@ +use crate::fatal; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Edition { + // Note that the ordering here is load-bearing, as we want the future edition to be greater than + // any year-based edition. + Year(u32), + Future, +} + +impl std::fmt::Display for Edition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Edition::Year(year) => write!(f, "{year}"), + Edition::Future => f.write_str("future"), + } + } +} + +impl From for Edition { + fn from(value: u32) -> Self { + Edition::Year(value) + } +} + +pub fn parse_edition(mut input: &str) -> Edition { + input = input.trim(); + if input == "future" { + Edition::Future + } else { + Edition::Year(input.parse().unwrap_or_else(|_| { + fatal!("`{input}` doesn't look like an edition"); + })) + } +} diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 15e31dadf9711..2d759279f34bc 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -7,6 +7,7 @@ pub mod common; mod debuggers; pub mod diagnostics; pub mod directives; +pub mod edition; pub mod errors; mod executor; mod json; @@ -39,6 +40,7 @@ use crate::common::{ expected_output_path, output_base_dir, output_relative_path, }; use crate::directives::DirectivesCache; +use crate::edition::parse_edition; use crate::executor::{CollectedTest, ColorConfig}; /// Creates the `Config` instance for this invocation of compiletest. @@ -449,7 +451,7 @@ pub fn parse_config(args: Vec) -> Config { has_enzyme, channel: matches.opt_str("channel").unwrap(), git_hash: matches.opt_present("git-hash"), - edition: matches.opt_str("edition"), + edition: matches.opt_str("edition").as_deref().map(parse_edition), cc: matches.opt_str("cc").unwrap(), cxx: matches.opt_str("cxx").unwrap(), From 6631c8ef5569920daba2e4ac46ac783b3a7c9c47 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Sun, 21 Sep 2025 16:56:04 -0500 Subject: [PATCH 1179/1900] Document range syntax on the rustc dev guide --- src/doc/rustc-dev-guide/src/tests/directives.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 4be78fac4ec7f..3b6c891746455 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -262,6 +262,20 @@ Consider writing the test as a proper incremental test instead. +#### The edition directive + +The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example: + +- A test with the `//@ edition: 2018` directive will only run under the 2018 edition. +- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case). +- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case). + +You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`: + +- A test with the `//@ edition: 2018` will run with the 2018 edition. +- A test with the `//@ edition: 2015..2021` will be run with the 2015 edition. +- A test with the `//@ edition: 2018..` will run with the 2018 edition. + ### Rustdoc | Directive | Explanation | Supported test suites | Possible values | From 73ee45d13a633d33d59e86ea077187c28f7c3afa Mon Sep 17 00:00:00 2001 From: Supakorn Ieamgomol Date: Thu, 2 Oct 2025 23:56:02 +0700 Subject: [PATCH 1180/1900] Update license references to use markdown links --- src/tools/rust-analyzer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index cb3a41eec5a62..6d2a673286853 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -61,4 +61,4 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer rust-analyzer is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0). -See LICENSE-APACHE and LICENSE-MIT for details. +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. From fe66eaa67acc47525db6f13cf97d54780d87b805 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 7 Jul 2025 22:36:23 +0200 Subject: [PATCH 1181/1900] Fix backtraces with `-C panic=abort` on linux; emit unwind tables by default The linux backtrace unwinder relies on unwind tables to work properly, and generating and printing a backtrace is done by for example the default panic hook. Begin emitting unwind tables by default again with `-C panic=abort` (see history below) so that backtraces work. History ======= Backtraces with `-C panic=abort` used to work in Rust 1.22 but broke in Rust 1.23, because in 1.23 we stopped emitting unwind tables with `-C panic=abort` (see 24cc38e3b00). In 1.45 (see cda994633ee) a workaround in the form of `-C force-unwind-tables=yes` was added. `-C panic=abort` was added in [Rust 1.10](https://blog.rust-lang.org/2016/07/07/Rust-1.10/#what-s-in-1-10-stable) and the motivation was binary size and compile time. But given how confusing that behavior has turned out to be, it is better to make binary size optimization opt-in with `-C force-unwind-tables=no` rather than default since the current default breaks backtraces. Besides, if binary size is a primary concern, there are many other tricks that can be used that has a higher impact. --- compiler/rustc_session/src/session.rs | 3 +- .../rustc_target/src/spec/base/android.rs | 4 -- compiler/rustc_target/src/spec/base/linux.rs | 3 + .../targets/arm_unknown_linux_gnueabihf.rs | 5 ++ tests/run-make/panic-abort-eh_frame/rmake.rs | 11 ++-- ...panic-abort-backtrace-without-debuginfo.rs | 57 +++++++++++++++++++ 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 25b46241c52df..172672a80fbd7 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -758,7 +758,8 @@ impl Session { // ELF x86-64 abi, but it can be disabled for some compilation units. // // Typically when we're compiling with `-C panic=abort` we don't need - // `uwtable` because we can't generate any exceptions! + // `uwtable` because we can't generate any exceptions! But note that + // some targets require unwind tables to generate backtraces. // Unwind tables are needed when compiling with `-C panic=unwind`, but // LLVM won't omit unwind tables unless the function is also marked as // `nounwind`, so users are allowed to disable `uwtable` emission. diff --git a/compiler/rustc_target/src/spec/base/android.rs b/compiler/rustc_target/src/spec/base/android.rs index 0426ea44c6a21..df2757aaabf65 100644 --- a/compiler/rustc_target/src/spec/base/android.rs +++ b/compiler/rustc_target/src/spec/base/android.rs @@ -8,10 +8,6 @@ pub(crate) fn opts() -> TargetOptions { base.tls_model = TlsModel::Emulated; base.has_thread_local = false; base.supported_sanitizers = SanitizerSet::ADDRESS; - // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 - // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution - // was to always emit `uwtable`). - base.default_uwtable = true; base.crt_static_respected = true; base } diff --git a/compiler/rustc_target/src/spec/base/linux.rs b/compiler/rustc_target/src/spec/base/linux.rs index 9982c254eca52..26e4590cf5e69 100644 --- a/compiler/rustc_target/src/spec/base/linux.rs +++ b/compiler/rustc_target/src/spec/base/linux.rs @@ -12,6 +12,9 @@ pub(crate) fn opts() -> TargetOptions { relro_level: RelroLevel::Full, has_thread_local: true, crt_static_respected: true, + // We want backtraces to work by default and they rely on unwind tables + // (regardless of `-C panic` strategy). + default_uwtable: true, supported_split_debuginfo: Cow::Borrowed(&[ SplitDebuginfo::Packed, SplitDebuginfo::Unpacked, diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs index a3f5389f0aa76..0c711d5e71ab4 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs @@ -19,6 +19,11 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), + // The default on linux is to have `default_uwtable=true`, but on + // this target we get an "`__aeabi_unwind_cpp_pr0` not defined" + // linker error, so set it to `true` here. + // FIXME(#146996): Remove this override once #146996 has been fixed. + default_uwtable: false, ..base::linux_gnu::opts() }, } diff --git a/tests/run-make/panic-abort-eh_frame/rmake.rs b/tests/run-make/panic-abort-eh_frame/rmake.rs index 23d95dc577491..2eccde627955c 100644 --- a/tests/run-make/panic-abort-eh_frame/rmake.rs +++ b/tests/run-make/panic-abort-eh_frame/rmake.rs @@ -1,9 +1,11 @@ // An `.eh_frame` section in an object file is a symptom of an UnwindAction::Terminate // being inserted, useful for determining whether or not unwinding is necessary. -// This is useless when panics would NEVER unwind due to -C panic=abort. This section should -// therefore never appear in the emit file of a -C panic=abort compilation, and this test -// checks that this is respected. -// See https://github.com/rust-lang/rust/pull/112403 +// This is useless when panics would NEVER unwind due to -C panic=abort and when we don't need +// being able to generate backtraces (which depend on unwind tables on linux). This section should +// therefore never appear in the emit file of a -C panic=abort compilation +// with -C force-unwind-tables=no, and this test checks that this is respected. +// See https://github.com/rust-lang/rust/pull/112403 and +// https://github.com/rust-lang/rust/pull/143613. //@ only-linux // FIXME(Oneirical): the DW_CFA symbol appears on Windows-gnu, because uwtable @@ -19,6 +21,7 @@ fn main() { .panic("abort") .edition("2021") .arg("-Zvalidate-mir") + .arg("-Cforce-unwind-tables=no") .run(); llvm_objdump().arg("--dwarf=frames").input("foo.o").run().assert_stdout_not_contains("DW_CFA"); } diff --git a/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs b/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs new file mode 100644 index 0000000000000..a29afd68523be --- /dev/null +++ b/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs @@ -0,0 +1,57 @@ +//! Test that with `-C panic=abort` the backtrace is not cut off by default +//! (i.e. without using `-C force-unwind-tables=yes`) by ensuring that our own +//! functions are in the backtrace. If we just check one function it might be +//! the last function, so make sure the backtrace can continue by checking for +//! two functions. Regression test for +//! . + +//@ run-pass +//@ needs-subprocess +// We want to test if unwind tables are emitted by default. We must make sure +// to disable debuginfo to test that, because enabling debuginfo also means that +// unwind tables are emitted, which prevents us from testing what we want. +// We also need to set opt-level=0 to avoid optimizing away our functions. +//@ compile-flags: -C panic=abort -C opt-level=0 -C debuginfo=0 +//@ no-prefer-dynamic +//@ ignore-apple +//@ ignore-arm-unknown-linux-gnueabihf FIXME(#146996) Try removing this once #146996 has been fixed. +//@ ignore-msvc Backtraces on Windows requires debuginfo which we can't use here + +static FN_1: &str = "this_function_must_be_in_the_backtrace"; +fn this_function_must_be_in_the_backtrace() { + and_this_function_too(); +} + +static FN_2: &str = "and_this_function_too"; +fn and_this_function_too() { + panic!("generate panic backtrace"); +} + +fn run_test() { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .arg("whatever") + .env("RUST_BACKTRACE", "full") + .output() + .unwrap(); + let backtrace = std::str::from_utf8(&output.stderr).unwrap(); + + fn assert(function_name: &str, backtrace: &str) { + assert!( + backtrace.contains(function_name), + "ERROR: no `{}` in stderr! actual stderr: {}", + function_name, + backtrace + ); + } + assert(FN_1, backtrace); + assert(FN_2, backtrace); +} + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() == 1 { + run_test(); + } else { + this_function_must_be_in_the_backtrace(); + } +} From 5a8f963426c72c5bd306b2620a6c148b720217d0 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Thu, 2 Oct 2025 20:13:18 +0200 Subject: [PATCH 1182/1900] fix wording; we're replacing coma with a bar --- ...ixed => only-replace-intended-coma-not-all-in-pattern.fixed} | 2 +- ...tern.rs => only-replace-intended-coma-not-all-in-pattern.rs} | 2 +- ...err => only-replace-intended-coma-not-all-in-pattern.stderr} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.fixed => only-replace-intended-coma-not-all-in-pattern.fixed} (84%) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.rs => only-replace-intended-coma-not-all-in-pattern.rs} (84%) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.stderr => only-replace-intended-coma-not-all-in-pattern.stderr} (89%) diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed similarity index 84% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed index 5abc1edd381bf..0258f868f0072 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed @@ -1,7 +1,7 @@ //@ run-rustfix // Regression test for issue #143330. -// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. +// Ensure we suggest to replace only the intended coma with a bar, not all commas in the pattern. fn main() { struct Foo { x: i32, ch: char } diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs similarity index 84% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs index 5bd267c3bc402..7d5087fa0ffae 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs @@ -1,7 +1,7 @@ //@ run-rustfix // Regression test for issue #143330. -// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. +// Ensure we suggest to replace only the intended coma with a bar, not all commas in the pattern. fn main() { struct Foo { x: i32, ch: char } diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr similarity index 89% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr index db0bb8c50e865..050f1b8a2b33a 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr @@ -1,5 +1,5 @@ error: unexpected `,` in pattern - --> $DIR/only-replace-intended-bar-not-all-in-pattern.rs:12:30 + --> $DIR/only-replace-intended-coma-not-all-in-pattern.rs:12:30 | LL | Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), | ^ From d1d7b9472a18780499162b4a91beea729bc2c13b Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Thu, 2 Oct 2025 20:24:34 +0200 Subject: [PATCH 1183/1900] bring back plural 'alternatives' in suggestion message --- compiler/rustc_parse/src/parser/diagnostics.rs | 2 +- .../issue-48492-tuple-destructure-missing-parens.stderr | 2 +- tests/ui/feature-gates/feature-gate-never_patterns.stderr | 2 +- tests/ui/parser/match-arm-without-body.stderr | 4 ++-- .../only-replace-intended-coma-not-all-in-pattern.stderr | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 5d6da60931200..1cb1618e5844b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2961,7 +2961,7 @@ impl<'a> Parser<'a> { if let CommaRecoveryMode::EitherTupleOrPipe = rt { err.span_suggestion( comma_span, - "...or a vertical bar to match on alternative", + "...or a vertical bar to match on alternatives", " |", Applicability::MachineApplicable, ); diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index dc875ad27ea6e..286d602139665 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -32,7 +32,7 @@ help: try adding parentheses to match on a tuple... | LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Nucleotide::Adenine, Nucleotide::Cytosine, _ => true LL + Nucleotide::Adenine | Nucleotide::Cytosine, _ => true diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 5e810249b9063..e655209924a08 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index 85ff7db8d4aa3..59a323f2cc1cf 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -16,7 +16,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | @@ -36,7 +36,7 @@ LL | LL | LL ~ _) => {} | -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr index 050f1b8a2b33a..ee75e2db1334c 100644 --- a/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' }) => (), | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), LL + Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' } => (), From 20d62469ba7f71aab3c0fc4884cde08f7acc5b1c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 21 Aug 2025 22:12:04 +0200 Subject: [PATCH 1184/1900] the `#[track_caller]` shim should not inherit `#[no_mangle]` --- .../src/middle/codegen_fn_attrs.rs | 24 +++++++++++++++++ .../shim-does-not-modify-symbol.rs | 26 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/ui/rfcs/rfc-2091-track-caller/shim-does-not-modify-symbol.rs diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index f0d96c6ac8981..d47d811610a74 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -13,6 +13,8 @@ impl<'tcx> TyCtxt<'tcx> { self, instance_kind: InstanceKind<'_>, ) -> Cow<'tcx, CodegenFnAttrs> { + // NOTE: we try to not clone the `CodegenFnAttrs` when that is not needed. + // The `to_mut` method used below clones the inner value. let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id())); // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that @@ -23,6 +25,28 @@ impl<'tcx> TyCtxt<'tcx> { } } + // A shim created by `#[track_caller]` should not inherit any attributes + // that modify the symbol name. Failing to remove these attributes from + // the shim leads to errors like `symbol `foo` is already defined`. + // + // A `ClosureOnceShim` with the track_caller attribute does not have a symbol, + // and therefore can be skipped here. + if let InstanceKind::ReifyShim(_, _) = instance_kind + && attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) + { + if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::NO_MANGLE); + } + + if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + } + + if attrs.symbol_name.is_some() { + attrs.to_mut().symbol_name = None; + } + } + attrs } } diff --git a/tests/ui/rfcs/rfc-2091-track-caller/shim-does-not-modify-symbol.rs b/tests/ui/rfcs/rfc-2091-track-caller/shim-does-not-modify-symbol.rs new file mode 100644 index 0000000000000..255e2c159f119 --- /dev/null +++ b/tests/ui/rfcs/rfc-2091-track-caller/shim-does-not-modify-symbol.rs @@ -0,0 +1,26 @@ +//@ run-pass +#![feature(rustc_attrs)] + +// The shim that is generated for a function annotated with `#[track_caller]` should not inherit +// attributes that modify its symbol name. Failing to remove these attributes from the shim +// leads to errors like `symbol `foo` is already defined`. +// +// See also https://github.com/rust-lang/rust/issues/143162. + +#[unsafe(no_mangle)] +#[track_caller] +pub fn foo() {} + +#[unsafe(export_name = "bar")] +#[track_caller] +pub fn bar() {} + +#[rustc_std_internal_symbol] +#[track_caller] +pub fn baz() {} + +fn main() { + let _a = foo as fn(); + let _b = bar as fn(); + let _c = baz as fn(); +} From 7d999790777a83dda5fdc8f3470a5130a6abf002 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 21 Aug 2025 22:19:40 +0200 Subject: [PATCH 1185/1900] use `codegen_instance_attrs` in some additional places --- compiler/rustc_codegen_cranelift/src/abi/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 3 ++- compiler/rustc_mir_transform/src/inline.rs | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 29ee46194de19..9a9a1f4a2c0f8 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -467,7 +467,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( true } else { instance.is_some_and(|inst| { - fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD) + fx.tcx.codegen_instance_attrs(inst.def).flags.contains(CodegenFnAttrFlags::COLD) }) }; if is_cold { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b2dc4fe32b0fe..ce55f5e29132f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -200,10 +200,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() { - Some(bx.tcx().codegen_fn_attrs(fx.instance.def_id())) + Some(bx.tcx().codegen_instance_attrs(fx.instance.def)) } else { None }; + let fn_attrs = fn_attrs.as_deref(); if !fn_abi.can_unwind { unwind = mir::UnwindAction::Unreachable; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 3d49eb4e8ef75..87311adff2823 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -248,7 +248,7 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) { let tcx = self.tcx(); let InlineAttr::Force { attr_span, reason: justification } = - tcx.codegen_fn_attrs(callsite.callee.def_id()).inline + tcx.codegen_instance_attrs(callsite.callee.def).inline else { bug!("called on item without required inlining"); }; @@ -606,7 +606,8 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>( let tcx = inliner.tcx(); check_mir_is_available(inliner, caller_body, callsite.callee)?; - let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); + let callee_attrs = tcx.codegen_instance_attrs(callsite.callee.def); + let callee_attrs = callee_attrs.as_ref(); check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?; check_codegen_attributes(inliner, callsite, callee_attrs)?; inliner.check_codegen_attributes_extra(callee_attrs)?; From 9db778155ed7c96fc15886a5d76bb6bd3ee377fa Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 22 Aug 2025 01:51:17 +0200 Subject: [PATCH 1186/1900] add indirect call example to `track-caller-ffi.rs` --- .../rfc-2091-track-caller/track-caller-ffi.rs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs index ee8be90d14d9a..d82bd12e90a28 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs @@ -2,14 +2,14 @@ use std::panic::Location; -extern "Rust" { +unsafe extern "Rust" { #[track_caller] - fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>; - fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>; + safe fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>; + safe fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>; } fn rust_track_caller_ffi_test_nested_tracked() -> &'static Location<'static> { - unsafe { rust_track_caller_ffi_test_tracked() } + rust_track_caller_ffi_test_tracked() } mod provides { @@ -31,12 +31,12 @@ fn main() { assert_eq!(location.line(), 29); assert_eq!(location.column(), 20); - let tracked = unsafe { rust_track_caller_ffi_test_tracked() }; + let tracked = rust_track_caller_ffi_test_tracked(); assert_eq!(tracked.file(), file!()); assert_eq!(tracked.line(), 34); - assert_eq!(tracked.column(), 28); + assert_eq!(tracked.column(), 19); - let untracked = unsafe { rust_track_caller_ffi_test_untracked() }; + let untracked = rust_track_caller_ffi_test_untracked(); assert_eq!(untracked.file(), file!()); assert_eq!(untracked.line(), 24); assert_eq!(untracked.column(), 9); @@ -44,5 +44,10 @@ fn main() { let contained = rust_track_caller_ffi_test_nested_tracked(); assert_eq!(contained.file(), file!()); assert_eq!(contained.line(), 12); - assert_eq!(contained.column(), 14); + assert_eq!(contained.column(), 5); + + let indirect = (rust_track_caller_ffi_test_tracked as fn() -> &'static Location<'static>)(); + assert_eq!(indirect.file(), file!()); + assert_eq!(indirect.line(), 7); + assert_eq!(indirect.column(), 5); } From fab9906c21e8c751943760d44c4a12cac2bceb05 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 2 Oct 2025 22:15:37 +0900 Subject: [PATCH 1187/1900] test: Add regression test for ICE when projecting associated types through dyn traits --- .../projection-dyn-associated-type.rs | 28 ++++++++++ .../projection-dyn-associated-type.stderr | 52 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/ui/associated-types/projection-dyn-associated-type.rs create mode 100644 tests/ui/associated-types/projection-dyn-associated-type.stderr diff --git a/tests/ui/associated-types/projection-dyn-associated-type.rs b/tests/ui/associated-types/projection-dyn-associated-type.rs new file mode 100644 index 0000000000000..3b981e7987e0e --- /dev/null +++ b/tests/ui/associated-types/projection-dyn-associated-type.rs @@ -0,0 +1,28 @@ +// Regression test for the projection bug in +// +//@ compile-flags: -Zincremental-verify-ich=yes +//@ incremental + +pub trait A {} +pub trait B: A {} + +pub trait Mirror { + type Assoc: ?Sized; +} + +impl Mirror for A { + //~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207] + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + type Assoc = T; +} + +pub fn foo<'a>( + x: &'a ::Assoc +) -> &'a ::Assoc { + //~^ ERROR the trait bound `(dyn B + 'static): Mirror` is not satisfied [E0277] + //~| ERROR the trait bound `(dyn B + 'static): Mirror` is not satisfied [E0277] + static +} //~ ERROR expected identifier, found `}` + +pub fn main() {} diff --git a/tests/ui/associated-types/projection-dyn-associated-type.stderr b/tests/ui/associated-types/projection-dyn-associated-type.stderr new file mode 100644 index 0000000000000..1ac2beb0414e2 --- /dev/null +++ b/tests/ui/associated-types/projection-dyn-associated-type.stderr @@ -0,0 +1,52 @@ +error: expected identifier, found `}` + --> $DIR/projection-dyn-associated-type.rs:26:1 + | +LL | } + | ^ expected identifier + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/projection-dyn-associated-type.rs:13:28 + | +LL | impl Mirror for A { + | ^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(bare_trait_objects)]` (part of `#[warn(rust_2021_compatibility)]`) on by default +help: if this is a dyn-compatible trait, use `dyn` + | +LL | impl Mirror for dyn A { + | +++ +help: alternatively use a blanket implementation to implement `Mirror` for all types that also implement `A` + | +LL - impl Mirror for A { +LL + impl Mirror for U { + | + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/projection-dyn-associated-type.rs:13:6 + | +LL | impl Mirror for A { + | ^ unconstrained type parameter + +error[E0277]: the trait bound `(dyn B + 'static): Mirror` is not satisfied + --> $DIR/projection-dyn-associated-type.rs:22:6 + | +LL | ) -> &'a ::Assoc { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Mirror` is not implemented for `(dyn B + 'static)` + | + = help: the trait `Mirror` is implemented for `dyn A` + +error[E0277]: the trait bound `(dyn B + 'static): Mirror` is not satisfied + --> $DIR/projection-dyn-associated-type.rs:22:6 + | +LL | ) -> &'a ::Assoc { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Mirror` is not implemented for `(dyn B + 'static)` + | + = help: the trait `Mirror` is implemented for `dyn A` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. From 3d2a8df077dcf6f84a15a76dec511af286435e84 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 2 Oct 2025 13:29:14 -0400 Subject: [PATCH 1188/1900] Upgrade `toml` to 0.9.7 and remove the `serde` feature. --- Cargo.toml | 7 +- clippy_lints/Cargo.toml | 7 +- .../src/cargo/lint_groups_priority.rs | 186 +++++++++--------- lintcheck/Cargo.toml | 2 +- tests/compile-test.rs | 22 ++- 5 files changed, 119 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e06383499893a..bedcc300f8567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,13 +38,18 @@ ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" -toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } +[dev-dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 51e59ae205076..42486e182ee34 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,12 +18,17 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" url = "2.2" +[dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [dev-dependencies] walkdir = "2.3" diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index ffd6c520c9ae8..14c5e22fb9cd5 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,142 +4,103 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use toml::Spanned; +use toml::de::{DeTable, DeValue}; -#[derive(Deserialize, Serialize, Debug)] -struct LintConfigTable { - level: String, - priority: Option, +fn toml_span(range: Range, file: &SourceFile) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(range.start), + file.start_pos + BytePos::from_usize(range.end), + SyntaxContext::root(), + None, + ) } -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum LintConfig { - Level(String), - Table(LintConfigTable), +struct LintConfig<'a> { + sp: Range, + level: &'a str, + priority: Option, } - -impl LintConfig { - fn level(&self) -> &str { - match self { - LintConfig::Level(level) => level, - LintConfig::Table(table) => &table.level, - } - } - +impl<'a> LintConfig<'a> { fn priority(&self) -> i64 { - match self { - LintConfig::Level(_) => 0, - LintConfig::Table(table) => table.priority.unwrap_or(0), - } + self.priority.unwrap_or(0) } fn is_implicit(&self) -> bool { - if let LintConfig::Table(table) = self { - table.priority.is_none() - } else { - true - } + self.priority.is_none() } -} - -type LintTable = BTreeMap, Spanned>; - -#[derive(Deserialize, Debug, Default)] -struct Lints { - #[serde(default)] - rust: LintTable, - #[serde(default)] - clippy: LintTable, -} - -#[derive(Deserialize, Debug, Default)] -struct Workspace { - #[serde(default)] - lints: Lints, -} -#[derive(Deserialize, Debug)] -struct CargoToml { - #[serde(default)] - lints: Lints, - #[serde(default)] - workspace: Workspace, -} - -fn toml_span(range: Range, file: &SourceFile) -> Span { - Span::new( - file.start_pos + BytePos::from_usize(range.start), - file.start_pos + BytePos::from_usize(range.end), - SyntaxContext::root(), - None, - ) + fn parse(value: &'a Spanned>) -> Option { + let sp = value.span(); + let (level, priority) = match value.get_ref() { + DeValue::String(level) => (&**level, None), + DeValue::Table(tbl) => { + let level = tbl.get("level")?.get_ref().as_str()?; + let priority = if let Some(priority) = tbl.get("priority") { + let priority = priority.get_ref().as_integer()?; + Some(i64::from_str_radix(priority.as_str(), priority.radix()).ok()?) + } else { + None + }; + (level, priority) + }, + _ => return None, + }; + Some(Self { sp, level, priority }) + } } -fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { - if name.get_ref() == "warnings" { - continue; - } - - if known_groups.contains(name.get_ref().as_str()) { - groups.push((name, config)); - } else { - lints.push((name, config.into_inner())); + if name.get_ref() != "warnings" + && let Some(config) = LintConfig::parse(config) + { + if known_groups.contains(&**name.get_ref()) { + groups.push((name, config)); + } else { + lints.push((name, config)); + } } } for (group, group_config) in groups { - let priority = group_config.get_ref().priority(); - let level = group_config.get_ref().level(); - if let Some((conflict, _)) = lints - .iter() - .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) - { + if let Some((conflict, _)) = lints.iter().rfind(|(_, lint_config)| { + lint_config.priority() == group_config.priority() && lint_config.level != group_config.level + }) { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, toml_span(group.span(), file), format!( - "lint group `{}` has the same priority ({priority}) as a lint", - group.as_ref() + "lint group `{}` has the same priority ({}) as a lint", + group.as_ref(), + group_config.priority(), ), |diag| { - let config_span = toml_span(group_config.span(), file); + let config_span = toml_span(group_config.sp.clone(), file); - if group_config.as_ref().is_implicit() { + if group_config.is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); - let mut suggestion = String::new(); let low_priority = lints .iter() - .map(|(_, config)| config.priority().saturating_sub(1)) + .map(|(_, lint_config)| lint_config.priority().saturating_sub(1)) .min() .unwrap_or(-1); - Serialize::serialize( - &LintConfigTable { - level: level.into(), - priority: Some(low_priority), - }, - toml::ser::ValueSerializer::new(&mut suggestion), - ) - .unwrap(); diag.span_suggestion_verbose( config_span, format!( "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - suggestion, + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), Applicability::MaybeIncorrect, ); }, @@ -148,10 +109,29 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet< } } +struct LintTbls<'a> { + rust: Option<&'a DeTable<'a>>, + clippy: Option<&'a DeTable<'a>>, +} +fn get_lint_tbls<'a>(tbl: &'a DeTable<'a>) -> LintTbls<'a> { + if let Some(lints) = tbl.get("lints") + && let Some(lints) = lints.get_ref().as_table() + { + let rust = lints.get("rust").and_then(|x| x.get_ref().as_table()); + let clippy = lints.get("clippy").and_then(|x| x.get_ref().as_table()); + LintTbls { rust, clippy } + } else { + LintTbls { + rust: None, + clippy: None, + } + } +} + pub fn check(cx: &LateContext<'_>) { if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml")) && let Some(src) = file.src.as_deref() - && let Ok(cargo_toml) = toml::from_str::(src) + && let Ok(cargo_toml) = DeTable::parse(src) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); @@ -167,9 +147,23 @@ pub fn check(cx: &LateContext<'_>) { } } - check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); - check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); + let lints = get_lint_tbls(cargo_toml.get_ref()); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + if let Some(tbl) = cargo_toml.get_ref().get("workspace") + && let Some(tbl) = tbl.get_ref().as_table() + { + let lints = get_lint_tbls(tbl); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + } } } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 55e588f5ec736..0d0b80c309ddd 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -22,6 +22,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" strip-ansi-escapes = "0.2.0" tar = "0.4" -toml = "0.7.3" +toml = "0.9.7" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6b6dfd7b81eab..71cd8a6c03cc9 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -405,11 +405,18 @@ fn ui_cargo_toml_metadata() { continue; } - let toml = fs::read_to_string(path).unwrap().parse::().unwrap(); - - let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap(); - - let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_"); + let toml = fs::read_to_string(path).unwrap(); + let toml = toml::de::DeTable::parse(&toml).unwrap(); + + let package = toml.get_ref().get("package").unwrap().get_ref().as_table().unwrap(); + + let name = package + .get("name") + .unwrap() + .as_ref() + .as_str() + .unwrap() + .replace('-', "_"); assert!( path.parent() .unwrap() @@ -421,7 +428,10 @@ fn ui_cargo_toml_metadata() { path.display(), ); - let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); + let publish = package + .get("publish") + .and_then(|x| x.get_ref().as_bool()) + .unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), "`{}` lacks `publish = false`", From c407d340de1717a88537725c899c153c6fdda051 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 26 Aug 2025 13:41:33 -0500 Subject: [PATCH 1189/1900] if a trait item is shown in search results, hide the impl item for example, if we're showing `Iterator::next`, we don't need to also show `Range::next` in the results. Co-authored-by: Michael Howell --- src/librustdoc/formats/cache.rs | 31 ++++--- src/librustdoc/html/render/mod.rs | 2 + src/librustdoc/html/render/search_index.rs | 78 ++++++++++------ src/librustdoc/html/static/js/rustdoc.d.ts | 22 ++++- src/librustdoc/html/static/js/search.js | 102 +++++++++++++++++---- 5 files changed, 174 insertions(+), 61 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 29b4c4caaf86d..75c848742ea5d 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -148,6 +148,14 @@ impl Cache { Cache { document_private, document_hidden, ..Cache::default() } } + fn parent_stack_last_impl_and_trait_id(&self) -> (Option, Option) { + if let Some(ParentStackItem::Impl { item_id, trait_, .. }) = self.parent_stack.last() { + (item_id.as_def_id(), trait_.as_ref().map(|tr| tr.def_id())) + } else { + (None, None) + } + } + /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was /// in `krate` due to the data being moved into the `Cache`. pub(crate) fn populate(cx: &mut DocContext<'_>, mut krate: clean::Crate) -> clean::Crate { @@ -574,11 +582,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), _ => item_def_id, }; - let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - item_id.as_def_id() - } else { - None - }; + let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id(); let search_type = get_function_type_for_search( item, tcx, @@ -596,12 +600,15 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It desc, parent: parent_did, parent_idx: None, + trait_parent, + trait_parent_idx: None, exact_module_path: None, impl_id, search_type, aliases, deprecation, }; + cache.search_index.push(index_item); } @@ -610,19 +617,21 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It /// See [`Cache::orphan_impl_items`]. fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) { let impl_generics = clean_impl_generics(cache.parent_stack.last()); - let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - item_id.as_def_id() - } else { - None + let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id(); + let orphan_item = OrphanImplItem { + parent: parent_did, + trait_parent, + item: item.clone(), + impl_generics, + impl_id, }; - let orphan_item = - OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; cache.orphan_impl_items.push(orphan_item); } pub(crate) struct OrphanImplItem { pub(crate) parent: DefId, pub(crate) impl_id: Option, + pub(crate) trait_parent: Option, pub(crate) item: clean::Item, pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>, } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6d684449b6d2e..88184c9611ff0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -134,6 +134,8 @@ pub(crate) struct IndexItem { pub(crate) desc: String, pub(crate) parent: Option, pub(crate) parent_idx: Option, + pub(crate) trait_parent: Option, + pub(crate) trait_parent_idx: Option, pub(crate) exact_module_path: Option>, pub(crate) impl_id: Option, pub(crate) search_type: Option, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 2984f3ab50eea..81b38d157a868 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -580,6 +580,7 @@ impl SerializedSearchIndex { module_path, exact_module_path, parent, + trait_parent, deprecated, associated_item_disambiguator, }| EntryData { @@ -589,6 +590,7 @@ impl SerializedSearchIndex { exact_module_path: exact_module_path .and_then(|path_id| map.get(&path_id).copied()), parent: parent.and_then(|path_id| map.get(&path_id).copied()), + trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()), deprecated: *deprecated, associated_item_disambiguator: associated_item_disambiguator.clone(), }, @@ -870,6 +872,7 @@ struct EntryData { module_path: Option, exact_module_path: Option, parent: Option, + trait_parent: Option, deprecated: bool, associated_item_disambiguator: Option, } @@ -885,6 +888,7 @@ impl Serialize for EntryData { seq.serialize_element(&self.module_path.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&self.exact_module_path.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&self.parent.map(|id| id + 1).unwrap_or(0))?; + seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&if self.deprecated { 1 } else { 0 })?; if let Some(disambig) = &self.associated_item_disambiguator { seq.serialize_element(&disambig)?; @@ -916,6 +920,9 @@ impl<'de> Deserialize<'de> for EntryData { .ok_or_else(|| A::Error::missing_field("exact_module_path"))?; let parent: SerializedOptional32 = v.next_element()?.ok_or_else(|| A::Error::missing_field("parent"))?; + let trait_parent: SerializedOptional32 = + v.next_element()?.ok_or_else(|| A::Error::missing_field("trait_parent"))?; + let deprecated: u32 = v.next_element()?.unwrap_or(0); let associated_item_disambiguator: Option = v.next_element()?; Ok(EntryData { @@ -925,6 +932,7 @@ impl<'de> Deserialize<'de> for EntryData { exact_module_path: Option::::from(exact_module_path) .map(|path| path as usize), parent: Option::::from(parent).map(|path| path as usize), + trait_parent: Option::::from(trait_parent).map(|path| path as usize), deprecated: deprecated != 0, associated_item_disambiguator, }) @@ -1275,7 +1283,8 @@ pub(crate) fn build_index( // Attach all orphan items to the type's definition if the type // has since been learned. - for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items + for &OrphanImplItem { impl_id, parent, trait_parent, ref item, ref impl_generics } in + &cache.orphan_impl_items { if let Some((fqp, _)) = cache.paths.get(&parent) { let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); @@ -1287,6 +1296,8 @@ pub(crate) fn build_index( desc, parent: Some(parent), parent_idx: None, + trait_parent, + trait_parent_idx: None, exact_module_path: None, impl_id, search_type: get_function_type_for_search( @@ -1391,6 +1402,7 @@ pub(crate) fn build_index( module_path: None, exact_module_path: None, parent: None, + trait_parent: None, deprecated: false, associated_item_disambiguator: None, }), @@ -1404,39 +1416,46 @@ pub(crate) fn build_index( } }; - // First, populate associated item parents + // First, populate associated item parents and trait parents let crate_items: Vec<&mut IndexItem> = search_index .iter_mut() .map(|item| { - item.parent_idx = item.parent.and_then(|defid| { - cache.paths.get(&defid).map(|&(ref fqp, ty)| { - let pathid = serialized_index.names.len(); - match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - entry.insert(pathid); - let (name, path) = fqp.split_last().unwrap(); - serialized_index.push_path( - name.as_str().to_string(), - PathData { - ty, - module_path: path.to_vec(), - exact_module_path: if let Some(exact_path) = - cache.exact_paths.get(&defid) - && let Some((name2, exact_path)) = exact_path.split_last() - && name == name2 - { - Some(exact_path.to_vec()) - } else { - None + let mut defid_to_rowid = |defid, check_external: bool| { + cache + .paths + .get(&defid) + .or_else(|| check_external.then(|| cache.external_paths.get(&defid)).flatten()) + .map(|&(ref fqp, ty)| { + let pathid = serialized_index.names.len(); + match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + entry.insert(pathid); + let (name, path) = fqp.split_last().unwrap(); + serialized_index.push_path( + name.as_str().to_string(), + PathData { + ty, + module_path: path.to_vec(), + exact_module_path: if let Some(exact_path) = + cache.exact_paths.get(&defid) + && let Some((name2, exact_path)) = + exact_path.split_last() + && name == name2 + { + Some(exact_path.to_vec()) + } else { + None + }, }, - }, - ); - usize::try_from(pathid).unwrap() + ); + usize::try_from(pathid).unwrap() + } } - } - }) - }); + }) + }; + item.parent_idx = item.parent.and_then(|p| defid_to_rowid(p, false)); + item.trait_parent_idx = item.trait_parent.and_then(|p| defid_to_rowid(p, true)); if let Some(defid) = item.defid && item.parent_idx.is_none() @@ -1519,6 +1538,7 @@ pub(crate) fn build_index( Some(EntryData { ty: item.ty, parent: item.parent_idx, + trait_parent: item.trait_parent_idx, module_path, exact_module_path, deprecated: item.deprecation.is_some(), diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 951eb2291b89e..e206d6633e630 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -241,6 +241,7 @@ declare namespace rustdoc { modulePath: number?, exactModulePath: number?, parent: number?, + traitParent: number?, deprecated: boolean, associatedItemDisambiguator: string?, } @@ -291,9 +292,12 @@ declare namespace rustdoc { path: PathData?, functionData: FunctionData?, deprecated: boolean, - parent: { path: PathData, name: string}?, + parent: RowParent, + traitParent: RowParent, } + type RowParent = { path: PathData, name: string } | null; + type ItemType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26; @@ -316,7 +320,23 @@ declare namespace rustdoc { interface ResultObject { desc: Promise, displayPath: string, + /** + * path to where the item was defined (not inlined), + * then `|`, then the `ItemType` of the item. + * + * This is often a private path, so it should not be displayed, + * but this allows us to use it to reliably deduplicate reexported and inlined items + */ fullPath: string, + /** + * The `fullPath` of the corresponding item within a trait. + * For example, for `File::read`, this would be `std::io::Read::read|12` + * + * This is used to hide items from trait impls when the trait itself is in the search results. + * + * `null` if the item is not from a trait impl block. + */ + traitPath: string | null, href: string, id: number, dist: number, diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 482134933a61e..71bad967026e8 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1598,6 +1598,7 @@ class DocSearch { * module_path, * exact_module_path, * parent, + * trait_parent, * deprecated, * associated_item_disambiguator * @type {rustdoc.ArrayWithOptionals<[ @@ -1607,6 +1608,7 @@ class DocSearch { * number, * number, * number, + * number, * ], [string]>} */ const raw = JSON.parse(encoded); @@ -1616,8 +1618,9 @@ class DocSearch { modulePath: raw[2] === 0 ? null : raw[2] - 1, exactModulePath: raw[3] === 0 ? null : raw[3] - 1, parent: raw[4] === 0 ? null : raw[4] - 1, - deprecated: raw[5] === 1 ? true : false, - associatedItemDisambiguator: raw.length === 6 ? null : raw[6], + traitParent: raw[5] === 0 ? null : raw[5] - 1, + deprecated: raw[6] === 1 ? true : false, + associatedItemDisambiguator: raw.length === 7 ? null : raw[7], }; } @@ -1853,14 +1856,25 @@ class DocSearch { if (!entry && !path) { return null; } + /** @type {function("parent" | "traitParent"): Promise} */ + const buildParentLike = async field => { + const [name, path] = entry !== null && entry[field] !== null ? + await Promise.all([this.getName(entry[field]), this.getPathData(entry[field])]) : + [null, null]; + if (name !== null && path !== null) { + return { name, path }; + } + return null; + }; + const [ moduleName, modulePathData, exactModuleName, exactModulePathData, - parentName, - parentPath, - crate, + parent, + traitParent, + crateOrNull, ] = await Promise.all([ entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null, entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null, @@ -1870,14 +1884,11 @@ class DocSearch { entry && entry.exactModulePath !== null ? this.getPathData(entry.exactModulePath) : null, - entry && entry.parent !== null ? - this.getName(entry.parent) : - null, - entry && entry.parent !== null ? - this.getPathData(entry.parent) : - null, - entry ? nonnull(await this.getName(entry.krate)) : "", + buildParentLike("parent"), + buildParentLike("traitParent"), + entry ? this.getName(entry.krate) : "", ]); + const crate = crateOrNull === null ? "" : crateOrNull; const name = name_ === null ? "" : name_; const normalizedName = (name.indexOf("_") === -1 ? name : @@ -1886,6 +1897,7 @@ class DocSearch { (modulePathData.modulePath === "" ? moduleName : `${modulePathData.modulePath}::${moduleName}`); + return { id, crate, @@ -1901,9 +1913,8 @@ class DocSearch { path, functionData, deprecated: entry ? entry.deprecated : false, - parent: parentName !== null && parentPath !== null ? - { name: parentName, path: parentPath } : - null, + parent, + traitParent, }; } @@ -2101,11 +2112,12 @@ class DocSearch { /** * @param {rustdoc.Row} item - * @returns {[string, string, string]} + * @returns {[string, string, string, string|null]} */ const buildHrefAndPath = item => { let displayPath; let href; + let traitPath = null; const type = itemTypes[item.ty]; const name = item.name; let path = item.modulePath; @@ -2163,7 +2175,11 @@ class DocSearch { href = this.rootPath + item.modulePath.replace(/::/g, "/") + "/" + type + "." + name + ".html"; } - return [displayPath, href, `${exactPath}::${name}`]; + if (item.traitParent) { + const tparent = item.traitParent; + traitPath = `${tparent.path.exactModulePath}::${tparent.name}::${name}`; + } + return [displayPath, href, `${exactPath}::${name}`, traitPath]; }; /** @@ -2598,7 +2614,13 @@ class DocSearch { * @returns {rustdoc.ResultObject[]} */ const transformResults = (results, typeInfo, duplicates) => { - const out = []; + /** @type {rustdoc.ResultObject[]} */ + let out = []; + + // if we match a trait-associated item, we want to go back and + // remove all the items that are their equivalent but in an impl block. + /** @type {Map} */ + const traitImplIdxMap = new Map(); for (const result of results) { const item = result.item; @@ -2630,17 +2652,35 @@ class DocSearch { item, displayPath: pathSplitter(res[0]), fullPath: "", + traitPath: null, href: "", displayTypeSignature: null, }, result); + // unlike other items, methods have a different ty when they are + // in an impl block vs a trait. want to normalize this away. + let ty = obj.item.ty; + if (ty === TY_TYMETHOD) { + ty = TY_METHOD; + } // To be sure than it some items aren't considered as duplicate. - obj.fullPath = res[2] + "|" + obj.item.ty; + obj.fullPath = res[2] + "|" + ty; + if (res[3]) { + // "tymethod" is never used on impl blocks + // (this is the reason we need to normalize tymethod away). + obj.traitPath = res[3] + "|" + obj.item.ty; + } if (duplicates.has(obj.fullPath)) { continue; } + // If we're showing something like `Iterator::next`, + // we don't want to also show a bunch of `::next` + if (obj.traitPath && duplicates.has(obj.traitPath)) { + continue; + } + // Exports are specifically not shown if the items they point at // are already in the results. if (obj.item.ty === TY_IMPORT && duplicates.has(res[2])) { @@ -2661,14 +2701,36 @@ class DocSearch { ); } + // FIXME: if the trait item matches but is cut off due to MAX_RESULTS, + // this deduplication will not happen. obj.href = res[1]; + if (obj.traitPath) { + let list = traitImplIdxMap.get(obj.traitPath); + if (list === undefined) { + list = []; + } + list.push(out.length); + traitImplIdxMap.set(obj.traitPath, list); + } else { + // FIXME: this is `O(n*m)` because we're repeatedly + // shifting with Array.splice, but could be `O(n+m)` if + // we did the shifting manually in a more clever way. + const toRemoveList = traitImplIdxMap.get(obj.fullPath); + if (toRemoveList) { + // iterate in reverse order so we don't shift the indexes + for (let i = toRemoveList.length - 1; i >= 0; i--) { + const rmIdx = toRemoveList[i]; + out = out.splice(rmIdx, 1); + } + } + traitImplIdxMap.delete(obj.fullPath); + } out.push(obj); if (out.length >= MAX_RESULTS) { break; } } } - return out; }; From 2dd00d3391b82dd0928f50589eb37f6fbbe32980 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 2 Sep 2025 11:41:21 -0500 Subject: [PATCH 1190/1900] fix rustdoc tests broke by trait item filtering --- tests/rustdoc-gui/search-result-color.goml | 45 +++++++++++-------- tests/rustdoc-gui/search-tab.goml | 2 +- tests/rustdoc-gui/sidebar.goml | 2 + tests/rustdoc-js-std/asrawfd.js | 4 +- tests/rustdoc-js-std/quoted.js | 18 ++++---- .../{bufread-fill-buf.js => trait-unbox.js} | 6 +-- 6 files changed, 42 insertions(+), 35 deletions(-) rename tests/rustdoc-js-std/{bufread-fill-buf.js => trait-unbox.js} (58%) diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml index fe0f64010895c..e5c11651bd27d 100644 --- a/tests/rustdoc-gui/search-result-color.goml +++ b/tests/rustdoc-gui/search-result-color.goml @@ -5,7 +5,7 @@ include: "utils.goml" define-function: ( "check-search-color", [ - theme, count_color, desc_color, path_color, bottom_border_color, keyword_color, + theme, count_color, path_color, bottom_border_color, keyword_color, struct_color, associatedtype_color, tymethod_color, method_color, structfield_color, structfield_hover_color, macro_color, fn_color, hover_path_color, hover_background, attribute_color, grey @@ -21,10 +21,6 @@ define-function: ( {"color": |count_color|}, ALL, ) - assert-css: ( - "//*[@class='desc'][normalize-space()='Just a normal struct.']", - {"color": |desc_color|}, - ) assert-css: ( "//*[@class='result-name']//*[normalize-space()='test_docs::']", {"color": |path_color|}, @@ -97,16 +93,6 @@ define-function: ( ALL, ) - // Checking color and background on hover. - move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal struct.']" - assert-css: ( - "//*[@class='result-name']//*[normalize-space()='test_docs::']", - {"color": |hover_path_color|}, - ) - assert-css: ( - "//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a", - {"color": |hover_path_color|, "background-color": |hover_background|}, - ) } ) @@ -157,7 +143,6 @@ show-text: true call-function: ("check-search-color", { "theme": "ayu", "count_color": "#888", - "desc_color": "#c5c5c5", "path_color": "#0096cf", "bottom_border_color": "#aaa3", "keyword_color": "#39afd7", @@ -179,7 +164,6 @@ call-function: ("check-search-color", { call-function: ("check-search-color", { "theme": "dark", "count_color": "#888", - "desc_color": "#ddd", "path_color": "#ddd", "bottom_border_color": "#aaa3", "keyword_color": "#d2991d", @@ -201,7 +185,6 @@ call-function: ("check-search-color", { call-function: ("check-search-color", { "theme": "light", "count_color": "#888", - "desc_color": "#000", "path_color": "#000", "bottom_border_color": "#aaa3", "keyword_color": "#3873ad", @@ -226,12 +209,27 @@ call-function: ("perform-search", {"query": "thisisanalias"}) define-function: ( "check-alias", - [theme, alias, grey], + [theme, alias, grey, desc_color, hover_path_color, hover_background], block { call-function: ("switch-theme", {"theme": |theme|}) // Checking that the colors for the alias element are the ones expected. assert-css: (".result-name .path .alias", {"color": |alias|}) assert-css: (".result-name .path .alias > .grey", {"color": |grey|}) + assert-css: ( + "//*[@class='desc'][normalize-space()='Just a normal enum.']", + {"color": |desc_color|}, + ) + // Checking color and background on hover. + move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal enum.']" + assert-css: ( + "//*[@class='result-name']//*[normalize-space()='test_docs::']", + {"color": |hover_path_color|}, + ) + assert-css: ( + "//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a", + {"color": |hover_path_color|, "background-color": |hover_background|}, + ) + }, ) @@ -239,14 +237,23 @@ call-function: ("check-alias", { "theme": "ayu", "alias": "#c5c5c5", "grey": "#999", + "desc_color": "#c5c5c5", + "hover_path_color": "#fff", + "hover_background": "#3c3c3c", }) call-function: ("check-alias", { "theme": "dark", "alias": "#fff", "grey": "#ccc", + "desc_color": "#ddd", + "hover_path_color": "#ddd", + "hover_background": "#616161", }) call-function: ("check-alias", { "theme": "light", "alias": "#000", "grey": "#999", + "desc_color": "#000", + "hover_path_color": "#000", + "hover_background": "#ccc", }) diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index 00ca952033dc6..0a3cfc231e50a 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -79,7 +79,7 @@ call-function: ("check-colors", { set-window-size: (851, 600) // Check the size and count in tabs -assert-text: ("#search-tabs > button:nth-child(1) > .count", " (27) ") +assert-text: ("#search-tabs > button:nth-child(1) > .count", " (25) ") assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7)  ") assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ") store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth}) diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 5ec0008ad8afe..0d371c8c6a46f 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -1,5 +1,7 @@ // Checks multiple things on the sidebar display (width of its elements, colors, etc). include: "utils.goml" +// Disable animations so they don't mess up color assertions later. +emulate-media-features: { "prefers-reduced-motion": "reduce" } go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" assert-property: (".sidebar", {"clientWidth": "199"}) show-text: true diff --git a/tests/rustdoc-js-std/asrawfd.js b/tests/rustdoc-js-std/asrawfd.js index 5dbc4ba95d9a7..da08eeb8a53c8 100644 --- a/tests/rustdoc-js-std/asrawfd.js +++ b/tests/rustdoc-js-std/asrawfd.js @@ -1,12 +1,10 @@ // ignore-order const EXPECTED = { - 'query': 'RawFd::as_raw_fd', + 'query': 'method:RawFd::as_raw_fd', 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. - { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, { 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' }, ], }; diff --git a/tests/rustdoc-js-std/quoted.js b/tests/rustdoc-js-std/quoted.js index 8a9275019255c..a8ca6521208ef 100644 --- a/tests/rustdoc-js-std/quoted.js +++ b/tests/rustdoc-js-std/quoted.js @@ -1,21 +1,21 @@ +// make sure quoted search works both for items and and without generics // ignore-order const FILTER_CRATE = 'std'; const EXPECTED = { - 'query': '"error"', + 'query': '"result"', 'others': [ - { 'path': 'std', 'name': 'error' }, - { 'path': 'std::fmt', 'name': 'Error' }, - { 'path': 'std::io', 'name': 'Error' }, + { 'path': 'std', 'name': 'result' }, + { 'path': 'std::result', 'name': 'Result' }, + { 'path': 'std::fmt', 'name': 'Result' }, ], 'in_args': [ - { 'path': 'std::fmt::Error', 'name': 'eq' }, - { 'path': 'std::fmt::Error', 'name': 'cmp' }, - { 'path': 'std::fmt::Error', 'name': 'partial_cmp' }, - + { 'path': 'std::result::Result', 'name': 'branch' }, + { 'path': 'std::result::Result', 'name': 'ok' }, + { 'path': 'std::result::Result', 'name': 'unwrap' }, ], 'returned': [ - { 'path': 'std::fmt::LowerExp', 'name': 'fmt' }, + { 'path': 'std::bool', 'name': 'try_into' }, ], }; diff --git a/tests/rustdoc-js-std/bufread-fill-buf.js b/tests/rustdoc-js-std/trait-unbox.js similarity index 58% rename from tests/rustdoc-js-std/bufread-fill-buf.js rename to tests/rustdoc-js-std/trait-unbox.js index 6b9309f686408..44ddc0c1e75ce 100644 --- a/tests/rustdoc-js-std/bufread-fill-buf.js +++ b/tests/rustdoc-js-std/trait-unbox.js @@ -1,10 +1,10 @@ -// ignore-order +// make sure type-based searches with traits get unboxed too const EXPECTED = [ { - 'query': 'bufread -> result<[u8]>', + 'query': 'any -> result', 'others': [ - { 'path': 'std::boxed::Box', 'name': 'fill_buf' }, + { 'path': 'std::boxed::Box', 'name': 'downcast' }, ], }, { From a145dfff03d562584a4a00552fb5df67cdd0d2b6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 21 Sep 2025 16:10:32 -0500 Subject: [PATCH 1191/1900] search.js: introduce optimized removeIdxListAsc routine --- src/librustdoc/html/static/js/search.js | 39 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 71bad967026e8..9a6d4c710ff5d 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1076,6 +1076,34 @@ function isPathSeparator(c) { return c === ":" || c === " "; } +/** + * Given an array and an ascending list of indices, + * efficiently removes each index in the array. + * + * @template T + * @param {Array} a + * @param {Array} idxList + */ +function removeIdxListAsc(a, idxList) { + if (idxList.length === 0) { + return; + } + let removed = 0; + let i = idxList[0]; + let nextToRemove = idxList[0]; + while (i < a.length - idxList.length) { + while (i === nextToRemove && removed < idxList.length) { + removed++; + i++; + nextToRemove = idxList[removed]; + } + a[i] = a[i + removed]; + i++; + } + // truncate array + a.length -= idxList.length; +} + /** * @template T */ @@ -2615,7 +2643,7 @@ class DocSearch { */ const transformResults = (results, typeInfo, duplicates) => { /** @type {rustdoc.ResultObject[]} */ - let out = []; + const out = []; // if we match a trait-associated item, we want to go back and // remove all the items that are their equivalent but in an impl block. @@ -2712,16 +2740,9 @@ class DocSearch { list.push(out.length); traitImplIdxMap.set(obj.traitPath, list); } else { - // FIXME: this is `O(n*m)` because we're repeatedly - // shifting with Array.splice, but could be `O(n+m)` if - // we did the shifting manually in a more clever way. const toRemoveList = traitImplIdxMap.get(obj.fullPath); if (toRemoveList) { - // iterate in reverse order so we don't shift the indexes - for (let i = toRemoveList.length - 1; i >= 0; i--) { - const rmIdx = toRemoveList[i]; - out = out.splice(rmIdx, 1); - } + removeIdxListAsc(out, toRemoveList); } traitImplIdxMap.delete(obj.fullPath); } From d0dc603c3992cc4903bb77f949b0b9bebf1ea7a8 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 21 Sep 2025 16:42:12 -0500 Subject: [PATCH 1192/1900] rustdoc: add testcase for traitParent deduplication --- tests/rustdoc-js/trait-methods.js | 20 ++++++++++++++++++++ tests/rustdoc-js/trait-methods.rs | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tests/rustdoc-js/trait-methods.js b/tests/rustdoc-js/trait-methods.js index dafad5e43784f..083e52439f4ae 100644 --- a/tests/rustdoc-js/trait-methods.js +++ b/tests/rustdoc-js/trait-methods.js @@ -9,4 +9,24 @@ const EXPECTED = [ { 'path': 'trait_methods::MyTrait', 'name': 'next' }, ], }, + // the traitParent deduplication pass should remove + // Empty::next, as it would be redundant + { + 'query': 'next', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'trait_methods::MyTrait', 'name': 'next' }, + ], + }, + // if the trait does not match, no deduplication happens + { + 'query': '-> option<()>', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'trait_methods::Empty', 'name': 'next' }, + { 'path': 'trait_methods::Void', 'name': 'next' }, + ], + }, ]; diff --git a/tests/rustdoc-js/trait-methods.rs b/tests/rustdoc-js/trait-methods.rs index c88f5edfd55b7..a741b361a3392 100644 --- a/tests/rustdoc-js/trait-methods.rs +++ b/tests/rustdoc-js/trait-methods.rs @@ -2,3 +2,21 @@ pub trait MyTrait { type Item; fn next(&mut self) -> Option; } + +pub struct Empty; + +impl MyTrait for Empty { + type Item = (); + fn next(&mut self) -> Option<()> { + None + } +} + +pub struct Void; + +impl MyTrait for Void { + type Item = (); + fn next(&mut self) -> Option<()> { + Some(()) + } +} From c7d6857f750a8c957ebca81340faa379cc3d110f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 2 Oct 2025 10:19:42 -0700 Subject: [PATCH 1193/1900] rustdoc: use same stage for all pr check docs --- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index d6470e4deb8d1..0a95f428f5bd8 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -32,7 +32,7 @@ ENV SCRIPT \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ - python3 ../x.py doc bootstrap && \ + python3 ../x.py doc bootstrap --stage 1 && \ # Build both public and internal documentation. RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 1 && \ RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 1 && \ From db3c9b640e8adabbee20e212f9ebb473513bad8b Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 30 Sep 2025 04:32:19 +0000 Subject: [PATCH 1194/1900] When computing opaque types in the next solver, take an initial pass pre-fallback where we equate hidden types but do not report errors --- compiler/rustc_hir_typeck/src/lib.rs | 6 ++ compiler/rustc_hir_typeck/src/opaque_types.rs | 57 ++++++++++++++++--- .../hidden-types-equate-before-fallback.rs | 27 +++++++++ 3 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9f5a85b692642..a615ac9d912dd 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -219,6 +219,12 @@ fn typeck_with_inspect<'tcx>( // the future. fcx.check_repeat_exprs(); + // We need to handle opaque types before emitting ambiguity errors as applying + // defining uses may guide type inference. + if fcx.next_trait_solver() { + fcx.try_handle_opaque_type_uses_next(); + } + fcx.type_inference_fallback(); // Even though coercion casts provide type hints, we check casts after fallback for diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 4c1fe69405e91..9b5f26cefff79 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -22,6 +22,29 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// inference variables. /// /// It then uses these defining uses to guide inference for all other uses. + /// + /// Unlike `handle_opaque_type_uses_next`, this does not report errors. + pub(super) fn try_handle_opaque_type_uses_next(&mut self) { + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types(); + for entry in &mut opaque_types { + *entry = self.resolve_vars_if_possible(*entry); + } + debug!(?opaque_types); + + self.compute_definition_site_hidden_types(&opaque_types, true); + self.apply_definition_site_hidden_types(&opaque_types); + } + + /// This takes all the opaque type uses during HIR typeck. It first computes + /// the concrete hidden type by iterating over all defining uses. + /// + /// A use during HIR typeck is defining if all non-lifetime arguments are + /// unique generic parameters and the hidden type does not reference any + /// inference variables. + /// + /// It then uses these defining uses to guide inference for all other uses. #[instrument(level = "debug", skip(self))] pub(super) fn handle_opaque_type_uses_next(&mut self) { // We clone the opaques instead of stealing them here as they are still used for @@ -35,7 +58,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } debug!(?opaque_types); - self.compute_definition_site_hidden_types(&opaque_types); + self.compute_definition_site_hidden_types(&opaque_types, false); self.apply_definition_site_hidden_types(&opaque_types); } } @@ -74,6 +97,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fn compute_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + first_pass: bool, ) { let tcx = self.tcx; let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() @@ -94,12 +118,22 @@ impl<'tcx> FnCtxt<'_, 'tcx> { continue; } - usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type)); + usage_kind.merge(self.consider_opaque_type_use( + opaque_type_key, + hidden_type, + first_pass, + )); if let UsageKind::HasDefiningUse = usage_kind { break; } } + // If this the first pass (`try_handle_opaque_type_uses_next`), + // then do not report any errors. + if first_pass { + continue; + } + let guar = match usage_kind { UsageKind::None => { if let Some(guar) = self.tainted_by_errors() { @@ -152,6 +186,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { &mut self, opaque_type_key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, + first_pass: bool, ) -> UsageKind<'tcx> { if let Err(err) = opaque_type_has_defining_use_args( &self, @@ -199,7 +234,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .borrow_mut() .hidden_types .insert(opaque_type_key.def_id, hidden_type); - assert!(prev.is_none()); + + // We do want to insert opaque types the first pass, because we want to + // equate them. So, the second pass (where we report errors) will have + // a hidden type inserted. + if first_pass { + assert!(prev.is_none()); + } UsageKind::HasDefiningUse } @@ -209,10 +250,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { let tcx = self.tcx; for &(key, hidden_type) in opaque_types { - let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap(); - - let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); - self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + // On the first pass to this function, some opaque types may not + // have a hidden type assigned. + if let Some(expected) = self.typeck_results.borrow_mut().hidden_types.get(&key.def_id) { + let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); + self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + } } } diff --git a/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs new file mode 100644 index 0000000000000..9fdeecfa1a530 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#240. Hidden types should +// equate *before* inference var fallback, otherwise we can get mismatched types. + + +#[derive(Clone, Copy)] +struct FileSystem; +impl FileSystem { + fn build(self, commands: T) -> Option { + match false { + true => Some(commands), + false => { + drop(match self.build::<_>(commands) { + Some(x) => x, + None => return None, + }); + panic!() + }, + } + } +} + +fn main() {} From 9a5cef155f5bc337563bdd09ad2f6ea0c8c2f1c7 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 1 Oct 2025 22:51:17 +0000 Subject: [PATCH 1195/1900] Cleanup opaque_type computation a bit --- compiler/rustc_hir_typeck/src/opaque_types.rs | 103 ++++++++++-------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 9b5f26cefff79..0c8edbac4f2b5 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::{ self, DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, TypeVisitableExt, TypingMode, }; +use rustc_span::ErrorGuaranteed; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::opaque_types::{ NonDefiningUseReason, opaque_type_has_defining_use_args, report_item_does_not_constrain_error, @@ -24,6 +25,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// It then uses these defining uses to guide inference for all other uses. /// /// Unlike `handle_opaque_type_uses_next`, this does not report errors. + #[instrument(level = "debug", skip(self))] pub(super) fn try_handle_opaque_type_uses_next(&mut self) { // We clone the opaques instead of stealing them here as they are still used for // normalization in the next generation trait solver. @@ -34,7 +36,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { debug!(?opaque_types); self.compute_definition_site_hidden_types(&opaque_types, true); - self.apply_definition_site_hidden_types(&opaque_types); } /// This takes all the opaque type uses during HIR typeck. It first computes @@ -59,22 +60,29 @@ impl<'tcx> FnCtxt<'_, 'tcx> { debug!(?opaque_types); self.compute_definition_site_hidden_types(&opaque_types, false); - self.apply_definition_site_hidden_types(&opaque_types); } } +#[derive(Copy, Clone, Debug)] enum UsageKind<'tcx> { None, NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>), UnconstrainedHiddenType(OpaqueHiddenType<'tcx>), - HasDefiningUse, + HasDefiningUse(OpaqueHiddenType<'tcx>), + // `type_of_opaque_hir_typeck` reported an error + HasError(ErrorGuaranteed), } impl<'tcx> UsageKind<'tcx> { fn merge(&mut self, other: UsageKind<'tcx>) { match (&*self, &other) { - (UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(), + (UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(), (UsageKind::None, _) => *self = other, + // If `type_of_opaque_hir_typeck` reported an error, then the hidden + // type is an error, but we also want to still report errors for + // remaining hidden types (really just normalization errors). + (UsageKind::HasError(_), _) => {} + (_, UsageKind::HasError(_)) => *self = other, // When mergining non-defining uses, prefer earlier ones. This means // the error happens as early as possible. ( @@ -87,7 +95,7 @@ impl<'tcx> UsageKind<'tcx> { // intended to be defining. ( UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..), - UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse, + UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_), ) => *self = other, } } @@ -112,22 +120,55 @@ impl<'tcx> FnCtxt<'_, 'tcx> { _ => unreachable!("not opaque or generator: {def_id:?}"), } + // We do actually need to check this the second pass (we can't just + // store this), because we can go from `UnconstrainedHiddenType` to + // `HasDefiningUse` (because of fallback) let mut usage_kind = UsageKind::None; for &(opaque_type_key, hidden_type) in opaque_types { if opaque_type_key.def_id != def_id { continue; } - usage_kind.merge(self.consider_opaque_type_use( - opaque_type_key, - hidden_type, - first_pass, - )); - if let UsageKind::HasDefiningUse = usage_kind { + usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type)); + + if let UsageKind::HasDefiningUse(..) = usage_kind { break; } } + if let UsageKind::HasDefiningUse(first_use) = usage_kind { + for &(opaque_type_key, hidden_type) in opaque_types { + if opaque_type_key.def_id != def_id { + continue; + } + + let expected = + EarlyBinder::bind(first_use.ty).instantiate(tcx, opaque_type_key.args); + self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + } + } + + match usage_kind { + UsageKind::HasDefiningUse(hidden_type) => { + let prev = + self.typeck_results.borrow_mut().hidden_types.insert(def_id, hidden_type); + + // We do want to insert opaque types the first pass, because + // we want to equate them. So, the second pass (where we + // report errors) may have a hidden type inserted. + if first_pass { + assert!(prev.is_none()); + } + } + UsageKind::HasError(guar) => { + self.typeck_results + .borrow_mut() + .hidden_types + .insert(def_id, OpaqueHiddenType::new_error(self.tcx, guar)); + } + _ => {} + } + // If this the first pass (`try_handle_opaque_type_uses_next`), // then do not report any errors. if first_pass { @@ -135,6 +176,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } let guar = match usage_kind { + UsageKind::HasDefiningUse(_) | UsageKind::HasError(_) => continue, UsageKind::None => { if let Some(guar) = self.tainted_by_errors() { guar @@ -171,7 +213,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .emit() } } - UsageKind::HasDefiningUse => continue, }; self.typeck_results @@ -182,11 +223,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } + #[tracing::instrument(skip(self), ret)] fn consider_opaque_type_use( &mut self, opaque_type_key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, - first_pass: bool, ) -> UsageKind<'tcx> { if let Err(err) = opaque_type_has_defining_use_args( &self, @@ -196,11 +237,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { match err { NonDefiningUseReason::Tainted(guar) => { - self.typeck_results.borrow_mut().hidden_types.insert( - opaque_type_key.def_id, - OpaqueHiddenType::new_error(self.tcx, guar), - ); - return UsageKind::HasDefiningUse; + return UsageKind::HasError(guar); } _ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type), }; @@ -228,35 +265,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.tcx, DefiningScopeKind::HirTypeck, ); - - let prev = self - .typeck_results - .borrow_mut() - .hidden_types - .insert(opaque_type_key.def_id, hidden_type); - - // We do want to insert opaque types the first pass, because we want to - // equate them. So, the second pass (where we report errors) will have - // a hidden type inserted. - if first_pass { - assert!(prev.is_none()); - } - UsageKind::HasDefiningUse - } - - fn apply_definition_site_hidden_types( - &mut self, - opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], - ) { - let tcx = self.tcx; - for &(key, hidden_type) in opaque_types { - // On the first pass to this function, some opaque types may not - // have a hidden type assigned. - if let Some(expected) = self.typeck_results.borrow_mut().hidden_types.get(&key.def_id) { - let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); - self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); - } - } + UsageKind::HasDefiningUse(hidden_type) } /// We may in theory add further uses of an opaque after cloning the opaque From c6c58e3fff86747f8d8d67d2f83541099465ca88 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 1 Oct 2025 23:03:14 +0000 Subject: [PATCH 1196/1900] Remove UsageKind::HasError --- compiler/rustc_hir_typeck/src/opaque_types.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 0c8edbac4f2b5..8731a16da5027 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -4,7 +4,6 @@ use rustc_middle::ty::{ self, DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, TypeVisitableExt, TypingMode, }; -use rustc_span::ErrorGuaranteed; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::opaque_types::{ NonDefiningUseReason, opaque_type_has_defining_use_args, report_item_does_not_constrain_error, @@ -69,8 +68,6 @@ enum UsageKind<'tcx> { NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>), UnconstrainedHiddenType(OpaqueHiddenType<'tcx>), HasDefiningUse(OpaqueHiddenType<'tcx>), - // `type_of_opaque_hir_typeck` reported an error - HasError(ErrorGuaranteed), } impl<'tcx> UsageKind<'tcx> { @@ -78,11 +75,6 @@ impl<'tcx> UsageKind<'tcx> { match (&*self, &other) { (UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(), (UsageKind::None, _) => *self = other, - // If `type_of_opaque_hir_typeck` reported an error, then the hidden - // type is an error, but we also want to still report errors for - // remaining hidden types (really just normalization errors). - (UsageKind::HasError(_), _) => {} - (_, UsageKind::HasError(_)) => *self = other, // When mergining non-defining uses, prefer earlier ones. This means // the error happens as early as possible. ( @@ -160,12 +152,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { assert!(prev.is_none()); } } - UsageKind::HasError(guar) => { - self.typeck_results - .borrow_mut() - .hidden_types - .insert(def_id, OpaqueHiddenType::new_error(self.tcx, guar)); - } _ => {} } @@ -176,7 +162,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } let guar = match usage_kind { - UsageKind::HasDefiningUse(_) | UsageKind::HasError(_) => continue, + UsageKind::HasDefiningUse(_) => continue, UsageKind::None => { if let Some(guar) = self.tainted_by_errors() { guar @@ -237,7 +223,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { match err { NonDefiningUseReason::Tainted(guar) => { - return UsageKind::HasError(guar); + return UsageKind::HasDefiningUse(OpaqueHiddenType::new_error(self.tcx, guar)); } _ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type), }; From a3fbae599c756c80c9ff42f83afbed65d06fe8c6 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 1 Oct 2025 23:07:10 +0000 Subject: [PATCH 1197/1900] Merge match and if --- compiler/rustc_hir_typeck/src/opaque_types.rs | 23 +++++++------------ .../hidden-types-equate-before-fallback.rs | 1 - 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 8731a16da5027..9d0e6d5abe181 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -128,31 +128,24 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } - if let UsageKind::HasDefiningUse(first_use) = usage_kind { + if let UsageKind::HasDefiningUse(ty) = usage_kind { for &(opaque_type_key, hidden_type) in opaque_types { if opaque_type_key.def_id != def_id { continue; } - let expected = - EarlyBinder::bind(first_use.ty).instantiate(tcx, opaque_type_key.args); + let expected = EarlyBinder::bind(ty.ty).instantiate(tcx, opaque_type_key.args); self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); } - } - match usage_kind { - UsageKind::HasDefiningUse(hidden_type) => { - let prev = - self.typeck_results.borrow_mut().hidden_types.insert(def_id, hidden_type); + let prev = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty); - // We do want to insert opaque types the first pass, because - // we want to equate them. So, the second pass (where we - // report errors) may have a hidden type inserted. - if first_pass { - assert!(prev.is_none()); - } + // We do want to insert opaque types the first pass, because + // we want to equate them. So, the second pass (where we + // report errors) may have a hidden type inserted. + if first_pass { + assert!(prev.is_none()); } - _ => {} } // If this the first pass (`try_handle_opaque_type_uses_next`), diff --git a/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs index 9fdeecfa1a530..dd5ff26da4f7f 100644 --- a/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs +++ b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs @@ -6,7 +6,6 @@ // Regression test for trait-system-refactor-initiative#240. Hidden types should // equate *before* inference var fallback, otherwise we can get mismatched types. - #[derive(Clone, Copy)] struct FileSystem; impl FileSystem { From 99550fbc3ea172a3cf90d2776a217e1ba19a196f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 2 Oct 2025 14:36:57 -0700 Subject: [PATCH 1198/1900] Return to needs-llvm-components being info-only Partially revert a535042e80a38196a58c27a8c95552546affe5dc Even with non-LLVM codegen backends, we want to allow for annotations that express dependencies to LLVM-specific parts of the test suite. This includes `//@ needs-llvm-components`, which just allows checking that LLVM is built with relevant target support before the test is run. It does not assert the test cannot work with another codegen backend. --- src/tools/compiletest/src/directives/needs.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index c8a729d8aab68..9d72492e5b078 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -281,10 +281,7 @@ pub(super) fn handle_needs( // Handled elsewhere. if name == "needs-llvm-components" { - if config.default_codegen_backend.is_llvm() { - return IgnoreDecision::Continue; - } - return IgnoreDecision::Ignore { reason: "LLVM specific test".into() }; + return IgnoreDecision::Continue; } let mut found_valid = false; From a5c9030271d48456ffefca83a416f0ee863ba965 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Oct 2025 20:54:47 +0000 Subject: [PATCH 1199/1900] Extract common logic for iterating over features Two places doing the same thing is enough to motivate me to extract this to a method :) --- compiler/rustc_ast_passes/src/feature_gate.rs | 6 +----- compiler/rustc_feature/src/unstable.rs | 10 ++++++++++ compiler/rustc_lint/src/builtin.rs | 8 ++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 608ccfefeb69d..b8a29a9a08f43 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -622,11 +622,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) } fn check_incompatible_features(sess: &Session, features: &Features) { - let enabled_lang_features = - features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let enabled_lib_features = - features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let enabled_features = enabled_lang_features.chain(enabled_lib_features); + let enabled_features = features.enabled_features_iter_stable_order(); for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES .iter() diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e63f29a9570de..8397cd294e0a9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -93,6 +93,16 @@ impl Features { &self.enabled_features } + /// Returns a iterator of enabled features in stable order. + pub fn enabled_features_iter_stable_order( + &self, + ) -> impl Iterator + Clone { + self.enabled_lang_features + .iter() + .map(|feat| (feat.gate_name, feat.attr_sp)) + .chain(self.enabled_lib_features.iter().map(|feat| (feat.gate_name, feat.attr_sp))) + } + /// Is the given feature enabled (via `#[feature(...)]`)? pub fn enabled(&self, feature: Symbol) -> bool { self.enabled_features.contains(&feature) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 75a0f89321b97..8a525eb11f7bb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2331,13 +2331,9 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.builder.features(); - let lang_features = - features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let lib_features = - features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - lang_features - .chain(lib_features) + features + .enabled_features_iter_stable_order() .filter(|(name, _)| features.incomplete(*name) || features.internal(*name)) .for_each(|(name, span)| { if features.incomplete(name) { From ce2087692f3cb6ff25a2b56f33cef20f6aa437cb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 30 Sep 2025 13:40:27 -0700 Subject: [PATCH 1200/1900] Add a new `wasm32-wasip3` target to Rust This commit adds a new tier 3 target to rustc, `wasm32-wasip3`. This follows in the footsteps of the previous `wasm32-wasip2` target and is used to represent binding to the WASIp3 set of APIs managed by the WASI subgroup to the WebAssembly Community Group. As of now the WASIp3 set of APIs are not finalized nor standardized. They're in the process of doing so and the current trajectory is to have the APIs published in December of this year. The goal here is to get the wheels turning in Rust to have the target in a more-ready-than-nonexistent state by the time this happens in December. For now the `wasm32-wasip3` target looks exactly the same as `wasm32-wasip2` except that `target_env = "p3"` is specified. This indicates to crates in the ecosystem that WASIp3 APIs should be used, such as the [`wasip3` crate]. Over time this target will evolve as implementation in guest toolchains progress, notably: * The standard library will use WASIp3 APIs natively once they're finalized in the WASI subgroup. * Support through `wasi-libc` will be updated to use WASIp3 natively which Rust will then transitively use. * Longer-term, features such as cooperative multithreading will be added to the WASIp3-track of targets to enable using `std::thread`, for example, on this target. These changes are all expected to be non-breaking changes for users of this target. Runtimes supporting WASIp3, currently Wasmtime and Jco, support WASIp2 APIs as well and will work with components whether or not they import WASIp2, both WASIp2 and WASIp3, or just WASIp3 APIs. This means that changing the internal implementation details of libstd over time is expected to be a non-breaking change. [`wasip3` crate]: https://crates.io/crates/wasip3 --- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/targets/wasm32_wasip3.rs | 20 +++++ library/std/Cargo.toml | 5 ++ library/std/src/os/mod.rs | 2 +- library/std/src/sys/args/mod.rs | 2 +- library/std/src/sys/net/connection/mod.rs | 2 +- .../std/src/sys/net/connection/socket/mod.rs | 2 +- library/std/src/sys/pal/mod.rs | 2 +- library/std/src/sys/random/mod.rs | 4 +- library/std/src/sys/stdio/mod.rs | 2 +- library/std/src/sys/thread/mod.rs | 4 +- src/bootstrap/src/core/build_steps/compile.rs | 10 +++ src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../src/platform-support/wasm32-wasip3.md | 83 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 + tests/ui/check-cfg/well-known-values.stderr | 2 +- 17 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs create mode 100644 src/doc/rustc/src/platform-support/wasm32-wasip3.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4a82a8bd888e6..e4b44e8b686c1 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1624,6 +1624,7 @@ supported_targets! { ("wasm32v1-none", wasm32v1_none), ("wasm32-wasip1", wasm32_wasip1), ("wasm32-wasip2", wasm32_wasip2), + ("wasm32-wasip3", wasm32_wasip3), ("wasm32-wasip1-threads", wasm32_wasip1_threads), ("wasm32-wali-linux-musl", wasm32_wali_linux_musl), ("wasm64-unknown-unknown", wasm64_unknown_unknown), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs new file mode 100644 index 0000000000000..e3d5e6542c263 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs @@ -0,0 +1,20 @@ +//! The `wasm32-wasip3` target is the next in the chain of `wasm32-wasip1`, then +//! `wasm32-wasip2`, then WASIp3. The main feature of WASIp3 is native async +//! support in the component model itself. +//! +//! Like `wasm32-wasip2` this target produces a component by default. Support +//! for `wasm32-wasip3` is very early as of the time of this writing so +//! components produced will still import WASIp2 APIs, but that's ok since it's +//! all component-model-level imports anyway. Over time the imports of the +//! standard library will change to WASIp3. + +use crate::spec::Target; + +pub(crate) fn target() -> Target { + // As of now WASIp3 is a lightly edited wasip2 target, so start with that + // and this may grow over time as more features are supported. + let mut target = super::wasm32_wasip2::target(); + target.llvm_target = "wasm32-wasip3".into(); + target.options.env = "p3".into(); + target +} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 888914a2f772e..779b07ce240a6 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -85,6 +85,11 @@ wasip2 = { version = '0.14.4', features = [ 'rustc-dep-of-std', ], default-features = false, package = 'wasi' } +[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies] +wasip2 = { version = '0.14.4', features = [ + 'rustc-dep-of-std', +], default-features = false, package = 'wasi' } + [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 96d9bfae8ca32..fd7a11433af1b 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -103,7 +103,7 @@ pub mod linux; all(target_vendor = "fortanix", target_env = "sgx") ) )))] -#[cfg(any(target_os = "wasi", doc))] +#[cfg(any(target_os = "wasi", any(target_env = "p1", target_env = "p2"), doc))] pub mod wasi; #[cfg(any(all(target_os = "wasi", target_env = "p2"), doc))] diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index e11e8e5430f06..75c59da721e19 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -36,7 +36,7 @@ cfg_select! { mod wasip1; pub use wasip1::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 7f9636a8ccfd6..41e7159f909ae 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -3,7 +3,7 @@ cfg_select! { all(target_family = "unix", not(target_os = "l4re")), target_os = "windows", target_os = "hermit", - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")), target_os = "solid_asp3", ) => { mod socket; diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1dd06e97bbabd..e59d01bdd9bb1 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -25,7 +25,7 @@ cfg_select! { mod unix; pub use unix::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index dd5e83ee570b6..9e964540a87c1 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -49,7 +49,7 @@ cfg_select! { mod vexos; pub use self::vexos::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use self::wasip2::*; } diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 3c5a4c82a9f1e..ec81d89a0f3af 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -90,7 +90,7 @@ cfg_select! { mod wasip1; pub use wasip1::fill_bytes; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::{fill_bytes, hashmap_random_keys}; } @@ -115,7 +115,7 @@ cfg_select! { target_os = "linux", target_os = "android", all(target_family = "wasm", target_os = "unknown"), - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", not(target_env = "p1")), target_os = "xous", target_os = "vexos", )))] diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 404ac87792696..660317e3ea844 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -37,7 +37,7 @@ cfg_select! { mod wasip1; pub use wasip1::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index 3bd83dd760ac9..a20b2a3ddd8ce 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -99,7 +99,7 @@ cfg_select! { #[cfg(not(target_feature = "atomics"))] pub use unsupported::{Thread, available_parallelism}; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::{sleep, sleep_until}; #[expect(dead_code)] @@ -146,7 +146,7 @@ cfg_select! { target_os = "hurd", target_os = "fuchsia", target_os = "vxworks", - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", not(target_env = "p1")), )))] pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 96b4e15433f7e..715f7abe9477f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -430,6 +430,16 @@ fn copy_self_contained_objects( target.triple ) }); + + // wasm32-wasip3 doesn't exist in wasi-libc yet, so instead use libs + // from the wasm32-wasip2 target. Once wasi-libc supports wasip3 this + // should be deleted and the native objects should be used. + let srcdir = if target == "wasm32-wasip3" { + assert!(!srcdir.exists(), "wasip3 support is in wasi-libc, this should be updated now"); + builder.wasi_libdir(TargetSelection::from_user("wasm32-wasip2")).unwrap() + } else { + srcdir + }; for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] { copy_and_stamp( builder, diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 67882bb38132a..1965a1b25498b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -132,6 +132,7 @@ - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) - [wasm32-wasip2](platform-support/wasm32-wasip2.md) + - [wasm32-wasip3](platform-support/wasm32-wasip3.md) - [wasm32-wali-linux-musl](platform-support/wasm32-wali-linux.md) - [wasm32-unknown-emscripten](platform-support/wasm32-unknown-emscripten.md) - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d0b6ed51bc163..c23d7563aedb5 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -198,6 +198,7 @@ target | std | notes [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASIp1 [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | WebAssembly with WASIp2 +[`wasm32-wasip3`](platform-support/wasm32-wasip3.md) | ✓ | WebAssembly with WASIp3 [`wasm32v1-none`](platform-support/wasm32v1-none.md) | * | WebAssembly limited to 1.0 features and no imports [`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64 diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip3.md b/src/doc/rustc/src/platform-support/wasm32-wasip3.md new file mode 100644 index 0000000000000..e8063a1bdcfed --- /dev/null +++ b/src/doc/rustc/src/platform-support/wasm32-wasip3.md @@ -0,0 +1,83 @@ +# `wasm32-wasip3` + +**Tier: 3** + +The `wasm32-wasip3` target is the next stage of evolution of the +[`wasm32-wasip2`](./wasm32-wasip2.md) target. The `wasm32-wasip3` target enables +the Rust standard library to use WASIp3 APIs to implement various pieces of +functionality. WASIp3 brings native async support over WASIp2, which integrates +well with Rust's `async` ecosystem. + +> **Note**: As of 2025-10-01 WASIp3 has not yet been approved by the WASI +> subgroup of the WebAssembly Community Group. Development is expected to +> conclude in late 2025 or early 2026. Until then the Rust standard library +> won't actually use WASIp3 APIs on the `wasm32-wasip3` target as they are not +> yet stable and would reduce the stability of this target. Once WASIp3 is +> approved, however, the standard library will update to use WASIp3 natively. + +> **Note**: This target does not yet build as of 2025-10-01 due to and update +> needed in the `libc` crate. Using it will require a `[patch]` for now. + +> **Note**: Until the standard library is fully migrated to use the `wasip3` +> crate then components produced for `wasm32-wasip3` may import WASIp2 APIs. +> This is considered a transitionary phase until fully support of libstd is +> implemented. + +## Target maintainers + +[@alexcrichton](https://github.com/alexcrichton) + +## Requirements + +This target is cross-compiled. The target supports `std` fully. + +## Platform requirements + +The WebAssembly runtime should support both WASIp2 and WASIp3. Runtimes also +are required to support components since this target outputs a component as +opposed to a core wasm module. Two example runtimes for WASIp3 are [Wasmtime] +and [Jco]. + +[Wasmtime]: https://wasmtime.dev/ +[Jco]: https://github.com/bytecodealliance/jco + +## Building the target + +To build this target first acquire a copy of +[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 +is the minimum needed. + +Next configure the `WASI_SDK_PATH` environment variable to point to where this +is installed. For example: + +```text +export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 +``` + +Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` +driver for LLD is required when linking WebAssembly code together. Rust's build +system will automatically pick up any necessary binaries and programs from +`WASI_SDK_PATH`. + +## Testing + +This target is not tested in CI at this time. Locally it can be tested with a +`wasmtime` binary in `PATH` like so: + +```text +./x.py test --target wasm32-wasip3 tests/ui +``` + +## Conditionally compiling code + +It's recommended to conditionally compile code for this target with: + +```text +#[cfg(all(target_os = "wasi", target_env = "p3"))] +``` + +## Enabled WebAssembly features + +The default set of WebAssembly features enabled for compilation is currently the +same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the +documentation there for more information. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index ebea9fe40f518..e26c213837066 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -586,6 +586,9 @@ //@ revisions: wasm32_wasip2 //@ [wasm32_wasip2] compile-flags: --target wasm32-wasip2 //@ [wasm32_wasip2] needs-llvm-components: webassembly +//@ revisions: wasm32_wasip3 +//@ [wasm32_wasip3] compile-flags: --target wasm32-wasip3 +//@ [wasm32_wasip3] needs-llvm-components: webassembly //@ revisions: wasm32_wali_linux_musl //@ [wasm32_wali_linux_musl] compile-flags: --target wasm32-wali-linux-musl //@ [wasm32_wali_linux_musl] needs-llvm-components: webassembly diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index e62f741b3020c..3f14c7b08eac6 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` + = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From c2a03cefd8899941032940df0c6be3b364de0ed0 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 21 Sep 2025 20:58:34 +0800 Subject: [PATCH 1201/1900] debuginfo: Use `LocalRef` to simplify reference debuginfos If the `LocalRef` is `LocalRef::Place`, we can refer to it directly, because the local of place is an indirect pointer. Such a statement is `_1 = &(_2.1)`. If the `LocalRef` is `LocalRef::Operand`, the `OperandRef` should provide the pointer of the reference. Such a statement is `_1 = &((*_2).1)`. But there is a special case that hasn't been handled, scalar pairs like `(&[i32; 16], i32)`. --- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 9 +- .../rustc_codegen_ssa/src/mir/statement.rs | 61 +-- tests/codegen-llvm/debuginfo-dse.rs | 405 +++++++++++++----- 3 files changed, 318 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 38bb6f24b1c3b..0c9acf087f9f3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -259,8 +259,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, bx: &mut Bx, local: mir::Local, - base: PlaceValue, - layout: TyAndLayout<'tcx>, + base: PlaceRef<'tcx, Bx::Value>, projection: &[mir::PlaceElem<'tcx>], ) { let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; @@ -274,7 +273,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = - calculate_debuginfo_offset(bx, projection, layout); + calculate_debuginfo_offset(bx, projection, base.layout); for var in vars.iter() { let Some(dbg_var) = var.dbg_var else { continue; @@ -285,7 +284,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.dbg_var_value( dbg_var, dbg_loc, - base.llval, + base.val.llval, direct_offset, &indirect_offsets, &var.fragment, @@ -298,7 +297,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.cx().layout_of(ty); let to_backend_ty = bx.cx().immediate_backend_type(layout); let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout); - self.debug_new_val_to_local(bx, local, place_ref.val, layout, &[]); + self.debug_new_val_to_local(bx, local, place_ref, &[]); } /// Apply debuginfo and/or name, after creating the `alloca` for a local, diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 80f4f0fcda1ce..88590b6271bce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,11 +1,8 @@ -use rustc_middle::mir::{self, NonDivergingIntrinsic, RETURN_PLACE, StmtDebugInfo}; -use rustc_middle::{bug, span_bug}; -use rustc_target::callconv::PassMode; +use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo}; +use rustc_middle::span_bug; use tracing::instrument; use super::{FunctionCx, LocalRef}; -use crate::common::TypeKind; -use crate::mir::place::PlaceRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -110,48 +107,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match debuginfo { StmtDebugInfo::AssignRef(dest, place) => { let local_ref = match self.locals[place.local] { - LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => { - Some(place_ref) + // For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place. + // The place is an indirect pointer, we can refer to it directly. + LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())), + // For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`. + // The deref projection is no-op here. + LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => { + Some((operand_ref.deref(bx.cx()), &place.projection[1..])) } - LocalRef::Operand(operand_ref) => operand_ref - .val - .try_pointer_parts() - .map(|(pointer, _)| PlaceRef::new_sized(pointer, operand_ref.layout)), - LocalRef::PendingOperand => None, + // For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`, + // we cannot get the address. + // N.B. `non_ssa_locals` returns that this is an SSA local. + LocalRef::Operand(_) => None, + LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None, } - .filter(|place_ref| { - // For the reference of an argument (e.x. `&_1`), it's only valid if the pass mode is indirect, and its reference is - // llval. - let local_ref_pass_mode = place.as_local().and_then(|local| { - if local == RETURN_PLACE { - None - } else { - self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode) - } - }); - matches!(local_ref_pass_mode, Some(&PassMode::Indirect {..}) | None) && + .filter(|(_, projection)| { // Drop unsupported projections. - place.projection.iter().all(|p| p.can_use_in_debuginfo()) && - // Only pointers can be calculated addresses. - bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer + projection.iter().all(|p| p.can_use_in_debuginfo()) }); - if let Some(local_ref) = local_ref { - let (base_layout, projection) = if place.is_indirect_first_projection() { - // For `_n = &((*_1).0: i32);`, we are calculating the address of `_1.0`, so - // we should drop the deref projection. - let projected_ty = local_ref - .layout - .ty - .builtin_deref(true) - .unwrap_or_else(|| bug!("deref of non-pointer {:?}", local_ref)); - let layout = bx.cx().layout_of(projected_ty); - (layout, &place.projection[1..]) - } else { - (local_ref.layout, place.projection.as_slice()) - }; - self.debug_new_val_to_local(bx, *dest, local_ref.val, base_layout, projection); + if let Some((base, projection)) = local_ref { + self.debug_new_val_to_local(bx, *dest, base, projection); } else { - // If the address cannot be computed, use poison to indicate that the value has been optimized out. + // If the address cannot be calculated, use poison to indicate that the value has been optimized out. self.debug_poison_to_local(bx, *dest); } } diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs index 78e145a3cdf12..fd0c9f1c676f1 100644 --- a/tests/codegen-llvm/debuginfo-dse.rs +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -1,9 +1,249 @@ //@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir -Zmerge-functions=disabled //@ revisions: CODEGEN OPTIMIZED //@[CODEGEN] compile-flags: -Cno-prepopulate-passes +//@ only-64bit // ignore-tidy-linelength #![crate_type = "lib"] +#![feature(repr_simd, rustc_attrs)] + +// The pass mode is direct and the backend represent is scalar. +type Scalar = i32; // scalar(i32) +type Scalar_Ref = &'static i32; // scalar(ptr) + +// The pass modes are pair and the backend represents are scalar pair. +type Tuple_Scalar_Scalar = (i32, i32); +struct Tuple_Ref_Scalar(&'static i32, i32); +struct Tuple_ArrayRef_Scalar(&'static [i32; 16], i32); // pair(ptr, i32) +impl Default for Tuple_ArrayRef_Scalar { + fn default() -> Tuple_ArrayRef_Scalar { + Tuple_ArrayRef_Scalar(&[0; 16], 0) + } +} +struct Tuple_Scalar_ArrayRef(i32, &'static [i32; 16]); // pair(i32, ptr) +impl Default for Tuple_Scalar_ArrayRef { + fn default() -> Tuple_Scalar_ArrayRef { + Tuple_Scalar_ArrayRef(0, &[0; 16]) + } +} +// The pass mode is indirect and the backend represent is memory. +type Tuple_SliceRef_Scalar = (&'static [i32], i32); + +// The pass mode is pair and the backend represent is scalar pair. +type SliceRef = &'static [i32]; // pair(ptr, i32) +// The pass mode is indirect and the backend represent is memory. +type Array = [i32; 16]; +// The pass mode is direct and the backend represent is scalar. +type ArrayRef = &'static [i32; 16]; + +// The pass mode is indirect and the backend represent is memory. +type Typle_i32_i64_i8 = (i32, i64, i8); +// The pass mode is indirect and the backend represent is memory. +#[repr(C)] +struct Aggregate_i32_Array_i8(i32, &'static [i32; 16], i8); + +type ZST = (); + +impl Default for Aggregate_i32_Array_i8 { + fn default() -> Aggregate_i32_Array_i8 { + Aggregate_i32_Array_i8(0, &[0; 16], 0) + } +} +// The pass mode is cast and the backend represent is scalar. +#[derive(Default)] +struct Aggregate_4xi8(i8, i8, i8, i8); // scalar(i32) + +// The pass mode is indirect and the backend represent is simd vector. +#[repr(simd)] +struct Simd_i32x4([i32; 4]); + +unsafe extern "Rust" { + #[rustc_nounwind] + safe fn opaque_fn(); + #[rustc_nounwind] + safe fn opaque_ptr(_: *const core::ffi::c_void); +} + +#[inline(never)] +#[rustc_nounwind] +fn opaque_use(p: &T) { + opaque_ptr(&raw const p as *const _); +} + +#[inline(never)] +#[rustc_nounwind] +fn opaque_read() -> T { + core::hint::black_box(T::default()) +} + +#[unsafe(no_mangle)] +fn local_var() { + // CHECK-LABEL: define{{( dso_local)?}} void @local_var + let local_var_scalar: Scalar = opaque_read(); + opaque_use(&local_var_scalar); + let dead_local_var_scalar: Scalar = opaque_read(); + let local_var_aggregate_4xi8: Aggregate_4xi8 = opaque_read(); + opaque_use(&local_var_aggregate_4xi8); + let local_var_aggregate_i32_array_i8: Aggregate_i32_Array_i8 = opaque_read(); + opaque_use(&local_var_aggregate_i32_array_i8); + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr %local_var_scalar, [[ref_local_var_scalar:![0-9]+]], !DIExpression() + let ref_local_var_scalar = &local_var_scalar; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_dead_local_var_scalar:![0-9]+]], !DIExpression() + let ref_dead_local_var_scalar = &dead_local_var_scalar; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_local_var_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_0_local_var_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_0_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8.0; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_2_local_var_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 2, DW_OP_stack_value) + let ref_2_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8.2; + // This introduces an extra load instruction. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_1_local_var_aggregate_i32_array_i8:![0-9]+]], !DIExpression() + let ref_1_1_local_var_aggregate_i32_array_i8 = &local_var_aggregate_i32_array_i8.1[1]; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_i32_array_i8, [[ref_2_local_var_aggregate_i32_array_i8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_2_local_var_aggregate_i32_array_i8 = &local_var_aggregate_i32_array_i8.2; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +#[unsafe(no_mangle)] +fn zst(zst: ZST, zst_ref: &ZST) { + // CHECK-LABEL: define{{( dso_local)?}} void @zst + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_zst:![0-9]+]], !DIExpression() + let ref_zst = &zst; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_zst_ref:![0-9]+]], !DIExpression() + let ref_zst_ref = &zst_ref; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// It only makes sense if the argument is a reference and it refer to projections. +#[unsafe(no_mangle)] +fn direct( + scalar: Scalar, + scalar_ref: Scalar_Ref, + array_ref: ArrayRef, + aggregate_4xi8_ref: &Aggregate_4xi8, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @direct + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_scalar:![0-9]+]], !DIExpression() + let ref_scalar = &scalar; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_scalar_ref:![0-9]+]], !DIExpression() + let ref_scalar_ref = &scalar_ref; + // CHECK-NEXT: #dbg_value(ptr %array_ref, [[ref_0_array_ref:![0-9]+]], !DIExpression() + let ref_0_array_ref = &array_ref[0]; + // CHECK-NEXT: #dbg_value(ptr %array_ref, [[ref_1_array_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_array_ref = &array_ref[1]; + // CHECK-NEXT: #dbg_value(ptr %aggregate_4xi8_ref, [[ref_1_aggregate_4xi8_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + let ref_1_aggregate_4xi8_ref = &aggregate_4xi8_ref.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// Arguments are passed through registers, the final values are poison. +#[unsafe(no_mangle)] +fn cast(aggregate_4xi8: Aggregate_4xi8) { + // CHECK-LABEL: define{{( dso_local)?}} void @cast(i32 %0) + // CHECK: call void @opaque_fn() + opaque_fn(); + // The temporary allocated variable is eliminated. + // CODEGEN-NEXT: #dbg_value(ptr %aggregate_4xi8, [[ref_aggregate_4xi8:![0-9]+]], !DIExpression() + // OPTIMIZED-NEXT: #dbg_value(ptr undef, [[ref_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_aggregate_4xi8 = &aggregate_4xi8; + // CODEGEN-NEXT: #dbg_value(ptr %aggregate_4xi8, [[ref_0_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + // OPTIMIZED-NEXT: #dbg_value(ptr undef, [[ref_0_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + let ref_0_aggregate_4xi8 = &aggregate_4xi8.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// Arguments are passed indirectly via a pointer. +// The reference of argument is the pointer itself. +#[unsafe(no_mangle)] +fn indirect( + tuple_sliceref_scalar: Tuple_SliceRef_Scalar, + array: Array, + typle_i32_i64_i8: Typle_i32_i64_i8, + simd_i32x4: Simd_i32x4, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @indirect + // CHECK-SAME: (ptr{{.*}} %tuple_sliceref_scalar, ptr{{.*}} %array, ptr{{.*}} %typle_i32_i64_i8, ptr{{.*}} %simd_i32x4) + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr %tuple_sliceref_scalar, [[ref_tuple_sliceref_scalar:![0-9]+]], !DIExpression() + let ref_tuple_sliceref_scalar = &tuple_sliceref_scalar; + // CHECK-NEXT: #dbg_value(ptr %tuple_sliceref_scalar, [[ref_1_tuple_sliceref_scalar:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_1_tuple_sliceref_scalar = &tuple_sliceref_scalar.1; + // CHECK-NEXT: #dbg_value(ptr %array, [[ref_1_array:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_array = &array[1]; + // CHECK-NEXT: #dbg_value(ptr %typle_i32_i64_i8, [[ref_1_typle_i32_i64_i8:![0-9]+]], !DIExpression() + let ref_1_typle_i32_i64_i8 = &typle_i32_i64_i8.1; + // CHECK-NEXT: #dbg_value(ptr %simd_i32x4, [[ref_simd_i32x4:![0-9]+]], !DIExpression() + let ref_simd_i32x4 = &simd_i32x4; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// They are different MIR statements, but they have the same LLVM IR statement due to the ABI of arguments. +// Both `direct_ref` and `indirect_byval` are passed as a pointer here. +#[unsafe(no_mangle)] +fn direct_ref_and_indirect( + direct_ref: &Aggregate_i32_Array_i8, + indirect_byval: Aggregate_i32_Array_i8, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @direct_ref_and_indirect + // CHECK-SAME: (ptr{{.*}} %direct_ref, ptr{{.*}} %indirect_byval) + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_direct_ref:![0-9]+]], !DIExpression() + let ref_direct_ref: &&Aggregate_i32_Array_i8 = &direct_ref; + // CHECK-NEXT: #dbg_value(ptr %direct_ref, [[ref_1_direct_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + let ref_1_direct_ref = &direct_ref.1; + // CHECK-NEXT: #dbg_value(ptr %indirect_byval, [[ref_indirect_byval:![0-9]+]], !DIExpression() + let ref_indirect_byval: &Aggregate_i32_Array_i8 = &indirect_byval; + // CHECK-NEXT: #dbg_value(ptr %indirect_byval, [[ref_1_indirect_byval:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + let ref_1_indirect_byval = &indirect_byval.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +#[unsafe(no_mangle)] +fn pair( + tuple_scalar_scalar: Tuple_Scalar_Scalar, + tuple_ref_scalar: Tuple_Ref_Scalar, + tuple_arrayref_scalar: Tuple_ArrayRef_Scalar, + tuple_scalar_arrayref: Tuple_Scalar_ArrayRef, + sliceref: SliceRef, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @pair + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_scalar_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_scalar_scalar = &tuple_scalar_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_ref_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_ref_scalar = &tuple_ref_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_tuple_ref_scalar:![0-9]+]], !DIExpression() + let ref_1_tuple_ref_scalar = &tuple_ref_scalar.1; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_arrayref_scalar = &tuple_arrayref_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_1_tuple_arrayref_scalar = &tuple_arrayref_scalar.1; + // FIXME: This can be a valid value. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_1_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_0_1_tuple_arrayref_scalar = &tuple_arrayref_scalar.0[1]; + // FIXME: This can be a valid value. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_1_tuple_scalar_arrayref:![0-9]+]], !DIExpression() + let ref_1_1_tuple_scalar_arrayref = &tuple_scalar_arrayref.1[1]; + // CHECK: #dbg_value(ptr %sliceref.0, [[ref_1_sliceref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_sliceref = &sliceref[1]; + // CHECK: call void @opaque_fn() + opaque_fn(); +} #[repr(C)] #[derive(Clone, Copy)] @@ -16,23 +256,7 @@ pub struct Bar<'a> { foo: &'a Foo, } -#[no_mangle] -fn r#ref(ref_foo: &Foo) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @ref - // CHECK-SAME: (ptr {{.*}} [[ARG_ref_foo:%.*]]) - // OPTIMIZED: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr poison, [[VAR_invalid_ref_of_ref_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v0:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let invalid_ref_of_ref_foo = &ref_foo; - let ref_v0 = &ref_foo.0; - let ref_v1 = &ref_foo.1; - let ref_v2 = &ref_foo.2; - ref_foo.0 -} - -#[no_mangle] +#[unsafe(no_mangle)] pub fn dead_first(dead_first_foo: &Foo) -> &i32 { // CHECK-LABEL: def {{.*}} ptr @dead_first // CHECK-SAME: (ptr {{.*}} [[ARG_dead_first_foo:%.*]]) @@ -47,32 +271,9 @@ pub fn dead_first(dead_first_foo: &Foo) -> &i32 { dead_first_v0 } -#[no_mangle] -fn ptr(ptr_foo: Foo) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @ptr - // CHECK-SAME: (ptr {{.*}} [[ARG_ptr_foo:%.*]]) - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[ref_ptr_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v0:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let ref_ptr_foo = &ptr_foo; - let ptr_v0 = &ptr_foo.0; - let ptr_v1 = &ptr_foo.1; - let ptr_v2 = &ptr_foo.2; - ptr_foo.2 -} - -#[no_mangle] -fn no_ptr(val: i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @no_ptr - // CODEGEN: #dbg_value(ptr poison, [[VAR_val_ref:![0-9]+]], !DIExpression() - let val_ref = &val; - val -} - -#[no_mangle] +#[unsafe(no_mangle)] pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { - // CHECK-LABEL: define void @fragment + // CHECK-LABEL: define{{( dso_local)?}} void @fragment // CHECK-SAME: (ptr {{.*}}, ptr {{.*}} [[ARG_fragment_v1:%.*]], ptr {{.*}} [[ARG_fragment_v2:%.*]]) // CHECK: #dbg_declare(ptr [[ARG_fragment_v1]] // CHECK-NEXT: #dbg_declare(ptr [[ARG_fragment_v2]] @@ -85,93 +286,77 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { fragment_v2 } -#[no_mangle] +#[unsafe(no_mangle)] pub fn deref(bar: Bar) -> i32 { - // CHECK-LABEL: define {{.*}} i32 @deref + // CHECK-LABEL: define{{.*}} i32 @deref // We are unable to represent dereference within this expression. // CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression() let deref_dead = &bar.foo.2; bar.a } -#[no_mangle] -pub fn tuple(foo: (i32, &Foo)) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @tuple - // Although there is no dereference here, there is a dereference in the MIR. - // CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression() - let tuple_dead = &foo.1.2; - foo.1.0 -} - -pub struct ZST; - -#[no_mangle] -pub fn zst(zst: ZST, v: &i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @zst - // CHECK: #dbg_value(ptr poison, [[VAR_zst_ref:![0-9]+]], !DIExpression() - let zst_ref = &zst; - *v -} - -#[no_mangle] +#[unsafe(no_mangle)] fn index(slice: &[i32; 4], idx: usize) -> i32 { // CHECK-LABEL: define{{.*}} i32 @index - // CHECK: bb1: - // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() - // CODEGEN: bb3: - // CHECK-NEXT: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() let index_from_var = &slice[idx]; + // CHECK: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() let [ref const_index_from_start, .., ref const_index_from_end] = slice[..] else { return 0; }; slice[0] } -unsafe extern "Rust" { - safe fn opaque_inner(_: *const core::ffi::c_void); -} +// CHECK-DAG: [[ref_local_var_scalar]] = !DILocalVariable(name: "ref_local_var_scalar" +// CHECK-DAG: [[ref_dead_local_var_scalar]] = !DILocalVariable(name: "ref_dead_local_var_scalar" +// CHECK-DAG: [[ref_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_0_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_0_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_2_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_2_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_1_1_local_var_aggregate_i32_array_i8]] = !DILocalVariable(name: "ref_1_1_local_var_aggregate_i32_array_i8" +// CHECK-DAG: [[ref_2_local_var_aggregate_i32_array_i8]] = !DILocalVariable(name: "ref_2_local_var_aggregate_i32_array_i8" + +// CHECK-DAG: [[ref_zst]] = !DILocalVariable(name: "ref_zst" +// CHECK-DAG: [[ref_zst_ref]] = !DILocalVariable(name: "ref_zst_ref" + +// CHECK-DAG: [[ref_scalar]] = !DILocalVariable(name: "ref_scalar" +// CHECK-DAG: [[ref_scalar_ref]] = !DILocalVariable(name: "ref_scalar_ref" +// CHECK-DAG: [[ref_0_array_ref]] = !DILocalVariable(name: "ref_0_array_ref" +// CHECK-DAG: [[ref_1_array_ref]] = !DILocalVariable(name: "ref_1_array_ref" +// CHECK-DAG: [[ref_1_aggregate_4xi8_ref]] = !DILocalVariable(name: "ref_1_aggregate_4xi8_ref" + +// CHECK-DAG: [[ref_aggregate_4xi8]] = !DILocalVariable(name: "ref_aggregate_4xi8" +// CHECK-DAG: [[ref_0_aggregate_4xi8]] = !DILocalVariable(name: "ref_0_aggregate_4xi8" + +// CHECK-DAG: [[ref_tuple_sliceref_scalar]] = !DILocalVariable(name: "ref_tuple_sliceref_scalar" +// CHECK-DAG: [[ref_1_tuple_sliceref_scalar]] = !DILocalVariable(name: "ref_1_tuple_sliceref_scalar" +// CHECK-DAG: [[ref_1_array]] = !DILocalVariable(name: "ref_1_array" +// CHECK-DAG: [[ref_1_typle_i32_i64_i8]] = !DILocalVariable(name: "ref_1_typle_i32_i64_i8" +// CHECK-DAG: [[ref_simd_i32x4]] = !DILocalVariable(name: "ref_simd_i32x4" + +// CHECK-DAG: [[ref_direct_ref]] = !DILocalVariable(name: "ref_direct_ref" +// CHECK-DAG: [[ref_1_direct_ref]] = !DILocalVariable(name: "ref_1_direct_ref" +// CHECK-DAG: [[ref_indirect_byval]] = !DILocalVariable(name: "ref_indirect_byval" +// CHECK-DAG: [[ref_1_indirect_byval]] = !DILocalVariable(name: "ref_1_indirect_byval" + +// CHECK-DAG: [[ref_0_tuple_scalar_scalar]] = !DILocalVariable(name: "ref_0_tuple_scalar_scalar" +// CHECK-DAG: [[ref_0_tuple_ref_scalar]] = !DILocalVariable(name: "ref_0_tuple_ref_scalar" +// CHECK-DAG: [[ref_1_tuple_ref_scalar]] = !DILocalVariable(name: "ref_1_tuple_ref_scalar" +// CHECK-DAG: [[ref_0_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_0_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_1_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_1_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_0_1_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_0_1_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_1_1_tuple_scalar_arrayref]] = !DILocalVariable(name: "ref_1_1_tuple_scalar_arrayref" +// CHECK-DAG: [[ref_1_sliceref]] = !DILocalVariable(name: "ref_1_sliceref" -#[inline(never)] -pub fn opaque_use(p: &T) { - opaque_inner(&raw const p as *const _); -} - -#[no_mangle] -pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @non_arg_ref - // CHECK: #dbg_value(ptr %non_arg_ref_scalar, [[VAR_non_arg_ref_scalar_ref:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref_2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let non_arg_ref_scalar = scalar; - let non_arg_ref_foo = foo; - opaque_use(&non_arg_ref_scalar); - opaque_use(&non_arg_ref_foo); - let non_arg_ref_scalar_ref = &non_arg_ref_scalar; - let non_arg_ref_foo_ref = &non_arg_ref_foo; - let non_arg_ref_foo_ref_2 = &non_arg_ref_foo.2; - *a -} - -// CHECK-DAG: [[VAR_invalid_ref_of_ref_foo]] = !DILocalVariable(name: "invalid_ref_of_ref_foo" -// OPTIMIZED-DAG: [[VAR_ref_foo]] = !DILocalVariable(name: "ref_foo" -// CHECK-DAG: [[VAR_ref_v0]] = !DILocalVariable(name: "ref_v0" -// CHECK-DAG: [[VAR_ref_v1]] = !DILocalVariable(name: "ref_v1" -// CHECK-DAG: [[VAR_ref_v2]] = !DILocalVariable(name: "ref_v2" -// CHECK-DAG: [[ref_ptr_foo]] = !DILocalVariable(name: "ref_ptr_foo" -// CHECK-DAG: [[VAR_ptr_v0]] = !DILocalVariable(name: "ptr_v0" -// CHECK-DAG: [[VAR_ptr_v1]] = !DILocalVariable(name: "ptr_v1" -// CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" -// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" -// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" -// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" -// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" // CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" // CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" + +// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" + +// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" + // CHECK-DAG: [[VAR_index_from_var]] = !DILocalVariable(name: "index_from_var" // CHECK-DAG: [[VAR_const_index_from_start]] = !DILocalVariable(name: "const_index_from_start" // CHECK-DAG: [[VAR_const_index_from_end]] = !DILocalVariable(name: "const_index_from_end" -// CHECK-DAG: [[VAR_zst_ref]] = !DILocalVariable(name: "zst_ref" -// CHECK-DAG: [[VAR_non_arg_ref_scalar_ref]] = !DILocalVariable(name: "non_arg_ref_scalar_ref" -// CHECK-DAG: [[VAR_non_arg_ref_foo_ref]] = !DILocalVariable(name: "non_arg_ref_foo_ref" -// CHECK-DAG: [[VAR_non_arg_ref_foo_ref_2]] = !DILocalVariable(name: "non_arg_ref_foo_ref_2" From 48df0e283363cec26aba29eb5efc5d629f845ef2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 3 Oct 2025 06:54:32 +0300 Subject: [PATCH 1202/1900] Fix erroneous diagnostic incorrect_generics_len when there are generics on enum variant used through type alias --- .../crates/hir-ty/src/infer/path.rs | 25 +++++++++++++------ .../src/handlers/incorrect_generics_len.rs | 17 +++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 7517272362b2c..e649e381ddd4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -95,14 +95,23 @@ impl<'db> InferenceContext<'_, 'db> { return Some(ValuePathResolution::NonGeneric(ty)); }; - let substs = self.with_body_ty_lowering(|ctx| { - let mut path_ctx = ctx.at_path(path, id); - let last_segment = path.segments().len().checked_sub(1); - if let Some(last_segment) = last_segment { - path_ctx.set_current_segment(last_segment) - } - path_ctx.substs_from_path(value_def, true, false) - }); + let substs = if self_subst.is_some_and(|it| !it.is_empty()) + && matches!(value_def, ValueTyDefId::EnumVariantId(_)) + { + // This is something like `TypeAlias::::EnumVariant`. Do not call `substs_from_path()`, + // as it'll try to re-lower the previous segment assuming it refers to the enum, but it refers + // to the type alias and they may have different generics. + self.types.empty_args + } else { + self.with_body_ty_lowering(|ctx| { + let mut path_ctx = ctx.at_path(path, id); + let last_segment = path.segments().len().checked_sub(1); + if let Some(last_segment) = last_segment { + path_ctx.set_current_segment(last_segment) + } + path_ctx.substs_from_path(value_def, true, false) + }) + }; let parent_substs_len = self_subst.map_or(0, |it| it.len()); let substs = GenericArgs::fill_rest( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 7402133f74799..894e044642cc3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs @@ -203,6 +203,23 @@ impl WithSignals for Player { fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self> { Self::SignalCollection { _v: self } } +} + "#, + ); + } + + #[test] + fn enum_type_alias_default_param() { + check_diagnostics( + r#" +//- minicore: result + +struct Error; + +type Result = core::result::Result; + +fn main() { + let _ = Result::<()>::Ok(()); } "#, ); From d6fdadb8e93401b4c98763e90c2e181390d0bfd5 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 3 Oct 2025 00:00:39 -0400 Subject: [PATCH 1203/1900] Mitigate `thread_local!` shadowing issues Mitigates https://github.com/rust-lang/rust/issues/147006 and https://github.com/rust-lang/rust/issues/99018 --- .../std/src/sys/thread_local/native/mod.rs | 26 +++++++++---------- .../std/src/sys/thread_local/no_threads.rs | 16 ++++++------ library/std/src/sys/thread_local/os.rs | 8 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 5dc142408047e..38b373be56c9d 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -55,7 +55,7 @@ pub macro thread_local_inner { // Used to generate the `LocalKey` value for const-initialized thread locals. (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ - const __INIT: $t = $init; + const __RUST_STD_INTERNAL_INIT: $t = $init; unsafe { $crate::thread::LocalKey::new(const { @@ -63,16 +63,16 @@ pub macro thread_local_inner { |_| { #[thread_local] $(#[$align_attr])* - static VAL: $crate::thread::local_impl::EagerStorage<$t> - = $crate::thread::local_impl::EagerStorage::new(__INIT); - VAL.get() + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::EagerStorage<$t> + = $crate::thread::local_impl::EagerStorage::new(__RUST_STD_INTERNAL_INIT); + __RUST_STD_INTERNAL_VAL.get() } } else { |_| { #[thread_local] $(#[$align_attr])* - static VAL: $t = __INIT; - &VAL + static __RUST_STD_INTERNAL_VAL: $t = __RUST_STD_INTERNAL_INIT; + &__RUST_STD_INTERNAL_VAL } } }) @@ -82,27 +82,27 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] - fn __init() -> $t { + fn __rust_std_internal_init_fn() -> $t { $init } unsafe { $crate::thread::LocalKey::new(const { if $crate::mem::needs_drop::<$t>() { - |init| { + |__rust_std_internal_init| { #[thread_local] $(#[$align_attr])* - static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, ()> = $crate::thread::local_impl::LazyStorage::new(); - VAL.get_or_init(init, __init) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) } } else { - |init| { + |__rust_std_internal_init| { #[thread_local] $(#[$align_attr])* - static VAL: $crate::thread::local_impl::LazyStorage<$t, !> + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, !> = $crate::thread::local_impl::LazyStorage::new(); - VAL.get_or_init(init, __init) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) } } }) diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 409dfb19518d9..936d464be9f1c 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -13,15 +13,15 @@ use crate::ptr; pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ - const __INIT: $t = $init; + const __RUST_STD_INTERNAL_INIT: $t = $init; // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|_| { $(#[$align_attr])* - static VAL: $crate::thread::local_impl::EagerStorage<$t> = - $crate::thread::local_impl::EagerStorage { value: __INIT }; - &VAL.value + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::EagerStorage<$t> = + $crate::thread::local_impl::EagerStorage { value: __RUST_STD_INTERNAL_INIT }; + &__RUST_STD_INTERNAL_VAL.value }) } }}, @@ -29,13 +29,13 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] - fn __init() -> $t { $init } + fn __rust_std_internal_init_fn() -> $t { $init } unsafe { - $crate::thread::LocalKey::new(|init| { + $crate::thread::LocalKey::new(|__rust_std_internal_init| { $(#[$align_attr])* - static VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); - VAL.get(init, __init) + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) }) } }}, diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 88bb5ae7c650d..9f7a29236e926 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -21,14 +21,14 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!`. (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{ #[inline] - fn __init() -> $t { $init } + fn __rust_std_internal_init_fn() -> $t { $init } // NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow // user provided type or type alias with a matching name. Please update the shadowing test // in `tests/thread.rs` if these types are renamed. unsafe { - $crate::thread::LocalKey::new(|init| { - static VAL: $crate::thread::local_impl::Storage<$t, { + $crate::thread::LocalKey::new(|__rust_std_internal_init| { + static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::Storage<$t, { $({ // Ensure that attributes have valid syntax // and that the proper feature gate is enabled @@ -43,7 +43,7 @@ pub macro thread_local_inner { final_align }> = $crate::thread::local_impl::Storage::new(); - VAL.get(init, __init) + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) }) } }}, From 8583e1582e3a2ce7e165111c9208be0224174381 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 3 Oct 2025 04:52:46 +0000 Subject: [PATCH 1204/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 3b8665c5ab3aeced9b01672404c3764583e722ca. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4e388e04ea44f..77436ae2f8259 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -42b384ec0dfcd528d99a4db0a337d9188a9eecaa +3b8665c5ab3aeced9b01672404c3764583e722ca From 3d5f54ad55a32c0f53271597145b5cd7bae9c3e2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 11:27:57 +0200 Subject: [PATCH 1205/1900] Respect `-Z` unstable options in `rustdoc --test` --- src/librustdoc/doctest.rs | 1 + .../rustdoc-ui/doctest/check-attr-test.stderr | 164 +++++++++--------- ...led-doctest-extra-semicolon-on-item.stderr | 2 +- .../doctest/main-alongside-stmts.stderr | 4 +- .../doctest/standalone-warning-2024.stderr | 30 ++-- .../doctest/test-compile-fail1.stderr | 18 +- .../doctest/test-compile-fail2.stderr | 8 +- .../doctest/test-compile-fail3.stderr | 8 +- .../doctest/warn-main-not-called.stderr | 4 +- 9 files changed, 120 insertions(+), 119 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 95bd31729de18..38a0c925d2a45 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -173,6 +173,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions target_triple: options.target.clone(), crate_name: options.crate_name.clone(), remap_path_prefix: options.remap_path_prefix.clone(), + unstable_opts: options.unstable_opts.clone(), ..config::Options::default() }; diff --git a/tests/rustdoc-ui/doctest/check-attr-test.stderr b/tests/rustdoc-ui/doctest/check-attr-test.stderr index 257136d1633d3..4cf93033ae8a0 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.stderr +++ b/tests/rustdoc-ui/doctest/check-attr-test.stderr @@ -1,55 +1,55 @@ error: unknown attribute `compile-fail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` note: the lint level is defined here - --> $DIR/check-attr-test.rs:3:9 - | -3 | #![deny(rustdoc::invalid_codeblock_attributes)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/check-attr-test.rs:3:9 + | +LL | #![deny(rustdoc::invalid_codeblock_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unknown attribute `compilefail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `comPile_fail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `should-panic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -58,11 +58,11 @@ error: unknown attribute `should-panic` error: unknown attribute `shouldpanic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -71,11 +71,11 @@ error: unknown attribute `shouldpanic` error: unknown attribute `shOuld_panic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -84,11 +84,11 @@ error: unknown attribute `shOuld_panic` error: unknown attribute `no-run` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -97,11 +97,11 @@ error: unknown attribute `no-run` error: unknown attribute `norun` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -110,11 +110,11 @@ error: unknown attribute `norun` error: unknown attribute `nO_run` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -123,11 +123,11 @@ error: unknown attribute `nO_run` error: unknown attribute `test-harness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function @@ -136,11 +136,11 @@ error: unknown attribute `test-harness` error: unknown attribute `testharness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function @@ -149,11 +149,11 @@ error: unknown attribute `testharness` error: unknown attribute `tesT_harness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr index 113fb7ccb60ee..cffda43ba1c94 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr @@ -1,7 +1,7 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/failed-doctest-extra-semicolon-on-item.rs:11:1 | -11 | /// ```rust +LL | /// ```rust | ^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr index d90a289ca6989..3f347d96ab063 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr @@ -1,13 +1,13 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/main-alongside-stmts.rs:17:1 | -17 | //! ``` +LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/main-alongside-stmts.rs:26:1 | -26 | //! ``` +LL | //! ``` | ^^^^^^^ warning: 2 warnings emitted diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index ce65557c2c4a4..1af2ded58e8b4 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -1,13 +1,13 @@ error: unknown attribute `standalone` --> $DIR/standalone-warning-2024.rs:11:1 | -11 | / //! ```standalone -12 | | //! bla -13 | | //! ``` -14 | | //! -15 | | //! ```standalone-crate -16 | | //! bla -17 | | //! ``` +LL | / //! ```standalone +LL | | //! bla +LL | | //! ``` +LL | | //! +LL | | //! ```standalone-crate +LL | | //! bla +LL | | //! ``` | |_______^ | = help: use `standalone_crate` to compile this code block separately @@ -15,20 +15,20 @@ error: unknown attribute `standalone` note: the lint level is defined here --> $DIR/standalone-warning-2024.rs:9:9 | - 9 | #![deny(warnings)] +LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` error: unknown attribute `standalone-crate` --> $DIR/standalone-warning-2024.rs:11:1 | -11 | / //! ```standalone -12 | | //! bla -13 | | //! ``` -14 | | //! -15 | | //! ```standalone-crate -16 | | //! bla -17 | | //! ``` +LL | / //! ```standalone +LL | | //! bla +LL | | //! ``` +LL | | //! +LL | | //! ```standalone-crate +LL | | //! bla +LL | | //! ``` | |_______^ | = help: use `standalone_crate` to compile this code block separately diff --git a/tests/rustdoc-ui/doctest/test-compile-fail1.stderr b/tests/rustdoc-ui/doctest/test-compile-fail1.stderr index 02f4d8d754f9b..aa5cc2e14d6d6 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail1.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail1.stderr @@ -1,13 +1,13 @@ error[E0428]: the name `f` is defined multiple times - --> $DIR/test-compile-fail1.rs:8:1 - | -6 | pub fn f() {} - | ---------- previous definition of the value `f` here -7 | -8 | pub fn f() {} - | ^^^^^^^^^^ `f` redefined here - | - = note: `f` must be defined only once in the value namespace of this module + --> $DIR/test-compile-fail1.rs:8:1 + | +LL | pub fn f() {} + | ---------- previous definition of the value `f` here +LL | +LL | pub fn f() {} + | ^^^^^^^^^^ `f` redefined here + | + = note: `f` must be defined only once in the value namespace of this module error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/test-compile-fail2.stderr b/tests/rustdoc-ui/doctest/test-compile-fail2.stderr index f0ad40eb6ca81..9f50c857275c6 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail2.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail2.stderr @@ -1,8 +1,8 @@ error: expected one of `!` or `::`, found `` - --> $DIR/test-compile-fail2.rs:3:1 - | -3 | fail - | ^^^^ expected one of `!` or `::` + --> $DIR/test-compile-fail2.rs:3:1 + | +LL | fail + | ^^^^ expected one of `!` or `::` error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr index 09d78b2f34673..9f90a428d480f 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr @@ -1,8 +1,8 @@ error[E0765]: unterminated double quote string - --> $DIR/test-compile-fail3.rs:3:1 - | -3 | "fail - | ^^^^^ + --> $DIR/test-compile-fail3.rs:3:1 + | +LL | "fail + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr index 3a079f47555b6..b5b7bc57644a6 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr @@ -1,13 +1,13 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:10:1 | -10 | //! ``` +LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:19:1 | -19 | //! ``` +LL | //! ``` | ^^^^^^^ warning: 2 warnings emitted From cb0f969b623a7e12a0d8166c9a498e17a8b5a3c4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Oct 2025 16:57:24 +1000 Subject: [PATCH 1206/1900] Avoid getting `dep_dep_node` unnecessarily. It's gotten on a hot path but only for use within `debug!`. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 7e258aaa54f79..eafb38bd25bbb 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -876,14 +876,19 @@ impl DepGraphData { frame: Option<&MarkFrame<'_>>, ) -> Option<()> { let dep_dep_node_color = self.colors.get(parent_dep_node_index); - let dep_dep_node = &self.previous.index_to_node(parent_dep_node_index); + + let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); match dep_dep_node_color { Some(DepNodeColor::Green(_)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other // dependencies. - debug!("dependency {dep_dep_node:?} was immediately green"); + // + // This path is extremely hot. We don't want to get the + // `dep_dep_node` unless it's necessary. Hence the + // `get_dep_dep_node` closure. + debug!("dependency {:?} was immediately green", get_dep_dep_node()); return Some(()); } Some(DepNodeColor::Red) => { @@ -891,12 +896,14 @@ impl DepGraphData { // compared to the previous compilation session. We cannot // mark the DepNode as green and also don't need to bother // with checking any of the other dependencies. - debug!("dependency {dep_dep_node:?} was immediately red"); + debug!("dependency {:?} was immediately red", get_dep_dep_node()); return None; } None => {} } + let dep_dep_node = &get_dep_dep_node(); + // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. if !qcx.dep_context().is_eval_always(dep_dep_node.kind) { From 3a287e6034e1339198908cd7253fa414447c97d3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Oct 2025 16:58:03 +1000 Subject: [PATCH 1207/1900] Remove some unnecessary locals. They both have a single use. (They can't be united, though, because `self.colors` might change between the two `get` calls.) --- compiler/rustc_query_system/src/dep_graph/graph.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index eafb38bd25bbb..5e62dab0722c6 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -875,11 +875,9 @@ impl DepGraphData { parent_dep_node_index: SerializedDepNodeIndex, frame: Option<&MarkFrame<'_>>, ) -> Option<()> { - let dep_dep_node_color = self.colors.get(parent_dep_node_index); - let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); - match dep_dep_node_color { + match self.colors.get(parent_dep_node_index) { Some(DepNodeColor::Green(_)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other @@ -929,9 +927,7 @@ impl DepGraphData { return None; } - let dep_dep_node_color = self.colors.get(parent_dep_node_index); - - match dep_dep_node_color { + match self.colors.get(parent_dep_node_index) { Some(DepNodeColor::Green(_)) => { debug!("managed to FORCE dependency {dep_dep_node:?} to green"); return Some(()); From 74caed9a4502c97c44d9c578e8ba50500da3d8fb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 3 Oct 2025 15:15:45 +0200 Subject: [PATCH 1208/1900] cg_gcc.md: add missing quote --- .../rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index 74dbfb7d31b30..aa337754186d6 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -20,7 +20,7 @@ rust.codegen-backends = ["llvm", "gcc"] ``` If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x` -commands with `--set rust.codegen-backends=["llvm", "gcc"]'`. For example: +commands with `--set 'rust.codegen-backends=["llvm", "gcc"]'`. For example: ```bash ./x build --set 'rust.codegen-backends=["llvm", "gcc"]' From 87d2891e82707c8c9730997f2f4771d4cebdbb22 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 29 Sep 2025 22:31:41 +0200 Subject: [PATCH 1209/1900] Cleanup `should_implement_trait` lint code --- .../src/methods/should_implement_trait.rs | 153 ++++++++---------- 1 file changed, 69 insertions(+), 84 deletions(-) diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs index 5f13b8c7e91df..599ff696f6aed 100644 --- a/clippy_lints/src/methods/should_implement_trait.rs +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{is_bool, sym}; use rustc_abi::ExternAbi; -use rustc_hir as hir; -use rustc_hir::{FnSig, ImplItem}; +use rustc_hir::{self as hir, FnRetTy, FnSig, GenericParamKind, ImplItem, LifetimeParamKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; @@ -20,51 +19,43 @@ pub(super) fn check_impl_item<'tcx>( sig: &FnSig<'_>, ) { // if this impl block implements a trait, lint in trait definition instead - if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) // check missing trait implementations - for method_config in &TRAIT_METHODS { - if impl_item.ident.name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt - .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - && method_config.in_prelude_since <= cx.tcx.sess.edition() - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } + && let Some(method_config) = TRAIT_METHODS.iter().find(|case| case.method_name == impl_item.ident.name) + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && sig.header.is_safe() + && !sig.header.is_const() + && !sig.header.is_async() + && sig.header.abi == ExternAbi::Rust + && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); } } -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - struct ShouldImplTraitCase { trait_name: &'static str, method_name: Symbol, param_count: usize, - fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) self_kind: SelfKind, // checks against the output type @@ -73,13 +64,12 @@ struct ShouldImplTraitCase { lint_explicit_lifetime: bool, in_prelude_since: Edition, } + impl ShouldImplTraitCase { - #[expect(clippy::too_many_arguments)] const fn new( trait_name: &'static str, method_name: Symbol, param_count: usize, - fn_header: hir::FnHeader, self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, @@ -89,7 +79,6 @@ impl ShouldImplTraitCase { trait_name, method_name, param_count, - fn_header, self_kind, output_type, lint_explicit_lifetime, @@ -102,8 +91,8 @@ impl ShouldImplTraitCase { || !impl_item.generics.params.iter().any(|p| { matches!( p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit } ) }) @@ -112,36 +101,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true, Edition2015), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true, Edition2015), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2021), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false, Edition2015), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, SelfKind::Value, OutType::Any, true, Edition2015), ]; #[derive(Clone, Copy)] @@ -153,19 +142,15 @@ enum OutType { } impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + fn matches(self, ty: &FnRetTy<'_>) -> bool { let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + (Self::Unit, &FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), _ => false, } } } - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} From 1485e7887b6407ce6326e044d9b3983364db52ad Mon Sep 17 00:00:00 2001 From: Fletcher Porter Date: Wed, 18 Jun 2025 15:04:54 +0300 Subject: [PATCH 1210/1900] Document fully-qualified syntax in `as`' keyword doc --- library/std/src/keyword_docs.rs | 35 ++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 7ff4af8ede84b..dc0d11b07a9f3 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1,6 +1,8 @@ #[doc(keyword = "as")] // -/// Cast between types, or rename an import. +/// Cast between types, rename an import, or qualify paths to associated items. +/// +/// # Type casting /// /// `as` is most commonly used to turn primitive types into other primitive types, but it has other /// uses that include turning pointers into addresses, addresses into pointers, and pointers into @@ -30,6 +32,8 @@ /// `as *mut _` though the [`cast`][const-cast] method is recommended over `as *const _` and it is /// [the same][mut-cast] for `as *mut _`: those methods make the intent clearer. /// +/// # Renaming imports +/// /// `as` is also used to rename imports in [`use`] and [`extern crate`][`crate`] statements: /// /// ``` @@ -37,9 +41,34 @@ /// use std::{mem as memory, net as network}; /// // Now you can use the names `memory` and `network` to refer to `std::mem` and `std::net`. /// ``` -/// For more information on what `as` is capable of, see the [Reference]. /// -/// [Reference]: ../reference/expressions/operator-expr.html#type-cast-expressions +/// # Qualifying paths +/// +/// You'll also find with `From` and `Into`, and indeed all traits, that `as` is used for the +/// _fully qualified path_, a means of disambiguating associated items, i.e. functions, +/// constants, and types. For example, if you have a type which implements two traits with identical +/// method names (e.g. `Into::::into` and `Into::::into`), you can clarify which method +/// you'll use with `>::into(my_thing)`[^as-use-from]. This is quite verbose, +/// but fortunately, Rust's type inference usually saves you from needing this, although it is +/// occasionally necessary, especially with methods that return a generic type like `Into::into` or +/// methods that don't take `self`. It's more common to use in macros where it can provide necessary +/// hygiene. +/// +/// [^as-use-from]: You should probably never use this syntax with `Into` and instead write +/// `T::from(my_thing)`. It just happens that there aren't any great examples for this syntax in +/// the standard library. Also, at time of writing, the compiler tends to suggest fully-qualified +/// paths to fix ambiguous `Into::into` calls, so the example should hopefully be familiar. +/// +/// # Further reading +/// +/// For more information on what `as` is capable of, see the Reference on [type cast expressions], +/// [renaming imported entities], [renaming `extern` crates] +/// and [qualified paths]. +/// +/// [type cast expressions]: ../reference/expressions/operator-expr.html#type-cast-expressions +/// [renaming imported entities]: https://doc.rust-lang.org/reference/items/use-declarations.html#as-renames +/// [renaming `extern` crates]: https://doc.rust-lang.org/reference/items/extern-crates.html#r-items.extern-crate.as +/// [qualified paths]: ../reference/paths.html#qualified-paths /// [`crate`]: keyword.crate.html /// [`use`]: keyword.use.html /// [const-cast]: pointer::cast From e3f104608c0ad26e80b1ebedef2ab8a748189e52 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 Oct 2025 10:32:11 -0400 Subject: [PATCH 1211/1900] Don't normalize higher-ranked assumptions if they're not used --- .../src/traits/select/confirmation.rs | 31 ++++++----- .../higher-ranked-normalize-assumptions-2.rs | 38 ++++++++++++++ .../higher-ranked-normalize-assumptions.rs | 51 +++++++++++++++++++ 3 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs create mode 100644 tests/ui/async-await/higher-ranked-normalize-assumptions.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7ad65a1df8e9b..708a53f6c650d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -423,19 +423,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { constituents.types, ); - // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types` - // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really - // matter yet. - for assumption in constituents.assumptions { - let assumption = normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - assumption, - &mut obligations, - ); - self.infcx.register_region_assumption(assumption); + // Only normalize these goals if `-Zhigher-ranked-assumptions` is enabled, since + // we don't want to cause ourselves to do extra work if we're not even able to + // take advantage of these assumption clauses. + if self.tcx().sess.opts.unstable_opts.higher_ranked_assumptions { + // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types` + // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really + // matter yet. + for assumption in constituents.assumptions { + let assumption = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + assumption, + &mut obligations, + ); + self.infcx.register_region_assumption(assumption); + } } Ok(obligations) diff --git a/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs b/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs new file mode 100644 index 0000000000000..410d4e503b7c2 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs @@ -0,0 +1,38 @@ +//@ revisions: stock hr +//@[hr] compile-flags: -Zhigher-ranked-assumptions +//@ edition: 2024 +//@ check-pass + +// Test that we don't normalize the higher-ranked assumptions of an auto trait goal +// unless we have `-Zhigher-ranked-assumptions`, since obligations that result from +// this normalization may lead to higher-ranked lifetime errors when the flag is not +// enabled. + +// Regression test for . + +pub fn a() -> impl Future + Send { + async { + let queries = core::iter::empty().map(Thing::f); + b(queries).await; + } +} + +async fn b(queries: impl IntoIterator) { + c(queries).await; +} + +fn c<'a, I>(_queries: I) -> impl Future +where + I: IntoIterator, + I::IntoIter: 'a, +{ + async {} +} + +pub struct Thing<'a>(pub &'a ()); + +impl Thing<'_> { + fn f(_: &Self) {} +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-normalize-assumptions.rs b/tests/ui/async-await/higher-ranked-normalize-assumptions.rs new file mode 100644 index 0000000000000..ec9cf3a152214 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-normalize-assumptions.rs @@ -0,0 +1,51 @@ +//@ revisions: stock hr +//@[hr] compile-flags: -Zhigher-ranked-assumptions +//@ edition: 2024 +//@ check-pass + +// Test that we don't normalize the higher-ranked assumptions of an auto trait goal +// unless we have `-Zhigher-ranked-assumptions`, since obligations that result from +// this normalization may lead to higher-ranked lifetime errors when the flag is not +// enabled. + +// Regression test for . + +pub trait Service { + type Response; +} + +impl Service for T +where + T: FnMut() -> R, + R: 'static, +{ + type Response = R; +} + +async fn serve(_: C) +where + C: Service, + C::Response: 'static, +{ + connect::().await; +} + +async fn connect() +where + C: Service, + C::Response: 'static, +{ +} + +fn repro() -> impl Send { + async { + let server = || do_something(); + serve(server).await; + } +} + +fn do_something() -> Box { + unimplemented!() +} + +fn main() {} From e914a1a6e026d2c9db43300735a75ba25746e14c Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 11:57:04 +0200 Subject: [PATCH 1212/1900] Respect `--error-format` in `rustdoc --test` --- src/librustdoc/doctest.rs | 1 + tests/rustdoc-ui/doctest/check-attr-test.rs | 12 ++++++++++ .../rustdoc-ui/doctest/check-attr-test.stderr | 24 +++++++++---------- .../failed-doctest-extra-semicolon-on-item.rs | 1 + .../doctest/main-alongside-stmts.rs | 2 ++ .../doctest/main-alongside-stmts.stderr | 8 +++---- .../doctest/main-alongside-stmts.stdout | 4 ++-- .../doctest/standalone-warning-2024.rs | 2 ++ .../doctest/standalone-warning-2024.stderr | 12 +++++----- .../rustdoc-ui/doctest/test-compile-fail1.rs | 1 + .../rustdoc-ui/doctest/test-compile-fail2.rs | 1 + .../rustdoc-ui/doctest/test-compile-fail3.rs | 1 + .../doctest/test-compile-fail3.stderr | 5 ++-- .../doctest/warn-main-not-called.rs | 2 ++ .../doctest/warn-main-not-called.stderr | 4 ++-- 15 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 38a0c925d2a45..27761511038ec 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -174,6 +174,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions crate_name: options.crate_name.clone(), remap_path_prefix: options.remap_path_prefix.clone(), unstable_opts: options.unstable_opts.clone(), + error_format: options.error_format.clone(), ..config::Options::default() }; diff --git a/tests/rustdoc-ui/doctest/check-attr-test.rs b/tests/rustdoc-ui/doctest/check-attr-test.rs index 81281db624b3d..d69dae63860ee 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.rs +++ b/tests/rustdoc-ui/doctest/check-attr-test.rs @@ -2,6 +2,9 @@ #![deny(rustdoc::invalid_codeblock_attributes)] +//~vvv ERROR unknown attribute `compile-fail` +//~| ERROR unknown attribute `compilefail` +//~| ERROR unknown attribute `comPile_fail` /// foo /// /// ```compile-fail,compilefail,comPile_fail @@ -9,6 +12,9 @@ /// ``` pub fn foo() {} +//~vvv ERROR unknown attribute `should-panic` +//~| ERROR unknown attribute `shouldpanic` +//~| ERROR unknown attribute `shOuld_panic` /// bar /// /// ```should-panic,shouldpanic,shOuld_panic @@ -16,6 +22,9 @@ pub fn foo() {} /// ``` pub fn bar() {} +//~vvv ERROR unknown attribute `no-run` +//~| ERROR unknown attribute `norun` +//~| ERROR unknown attribute `nO_run` /// foobar /// /// ```no-run,norun,nO_run @@ -23,6 +32,9 @@ pub fn bar() {} /// ``` pub fn foobar() {} +//~vvv ERROR unknown attribute `test-harness` +//~| ERROR unknown attribute `testharness` +//~| ERROR unknown attribute `tesT_harness` /// b /// /// ```test-harness,testharness,tesT_harness diff --git a/tests/rustdoc-ui/doctest/check-attr-test.stderr b/tests/rustdoc-ui/doctest/check-attr-test.stderr index 4cf93033ae8a0..1fc7ab592de0c 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.stderr +++ b/tests/rustdoc-ui/doctest/check-attr-test.stderr @@ -1,5 +1,5 @@ error: unknown attribute `compile-fail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -17,7 +17,7 @@ LL | #![deny(rustdoc::invalid_codeblock_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unknown attribute `compilefail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -30,7 +30,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `comPile_fail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -43,7 +43,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `should-panic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -56,7 +56,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `shouldpanic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -69,7 +69,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `shOuld_panic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -82,7 +82,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `no-run` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -95,7 +95,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `norun` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -108,7 +108,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `nO_run` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -121,7 +121,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `test-harness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// @@ -134,7 +134,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `testharness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// @@ -147,7 +147,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `tesT_harness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs index ca5dd78746789..05e4a348d1190 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs @@ -9,6 +9,7 @@ /// /// /// ```rust +//~^ WARN the `main` function of this doctest won't be run /// struct S {}; /// /// fn main() { diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.rs b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs index 5965f928cdd13..595de13393293 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.rs +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs @@ -14,6 +14,7 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ check-pass +//~v WARN the `main` function of this doctest won't be run //! ``` //! # if cfg!(miri) { return; } //! use std::ops::Deref; @@ -22,6 +23,7 @@ //! assert!(false); //! } //! ``` +//~v WARN the `main` function of this doctest won't be run //! //! ``` //! let x = 2; diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr index 3f347d96ab063..b7a5421f8f737 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr @@ -1,14 +1,14 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function - --> $DIR/main-alongside-stmts.rs:17:1 + --> $DIR/main-alongside-stmts.rs:18:1 | LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function - --> $DIR/main-alongside-stmts.rs:26:1 + --> $DIR/main-alongside-stmts.rs:27:1 | -LL | //! ``` - | ^^^^^^^ +LL | //! + | ^^^ warning: 2 warnings emitted diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout index 9b9a3fe8a68f7..bebaeb49c5a90 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout @@ -1,7 +1,7 @@ running 2 tests -test $DIR/main-alongside-stmts.rs - (line 17) ... ok -test $DIR/main-alongside-stmts.rs - (line 26) ... ok +test $DIR/main-alongside-stmts.rs - (line 18) ... ok +test $DIR/main-alongside-stmts.rs - (line 27) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs index c53a8b48749ce..3bb0083d849ff 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs @@ -9,6 +9,8 @@ #![deny(warnings)] //! ```standalone +//~^ ERROR unknown attribute `standalone` +//~| ERROR unknown attribute `standalone-crate` //! bla //! ``` //! diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index 1af2ded58e8b4..db0d53a204ced 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -2,10 +2,10 @@ error: unknown attribute `standalone` --> $DIR/standalone-warning-2024.rs:11:1 | LL | / //! ```standalone +LL | | +LL | | LL | | //! bla -LL | | //! ``` -LL | | //! -LL | | //! ```standalone-crate +... | LL | | //! bla LL | | //! ``` | |_______^ @@ -23,10 +23,10 @@ error: unknown attribute `standalone-crate` --> $DIR/standalone-warning-2024.rs:11:1 | LL | / //! ```standalone +LL | | +LL | | LL | | //! bla -LL | | //! ``` -LL | | //! -LL | | //! ```standalone-crate +... | LL | | //! bla LL | | //! ``` | |_______^ diff --git a/tests/rustdoc-ui/doctest/test-compile-fail1.rs b/tests/rustdoc-ui/doctest/test-compile-fail1.rs index 278f01f4c8389..c692c4c61bcce 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail1.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail1.rs @@ -6,3 +6,4 @@ pub fn f() {} pub fn f() {} +//~^ ERROR the name `f` is defined multiple times diff --git a/tests/rustdoc-ui/doctest/test-compile-fail2.rs b/tests/rustdoc-ui/doctest/test-compile-fail2.rs index 7432cc9f8263b..8239440262d81 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail2.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail2.rs @@ -1,3 +1,4 @@ //@ compile-flags:--test fail +//~^ ERROR diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.rs b/tests/rustdoc-ui/doctest/test-compile-fail3.rs index a2486d9dc6f22..272ba95396c6d 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.rs @@ -1,3 +1,4 @@ //@ compile-flags:--test "fail +//~^ ERROR diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr index 9f90a428d480f..8061097e73acd 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr @@ -1,8 +1,9 @@ error[E0765]: unterminated double quote string --> $DIR/test-compile-fail3.rs:3:1 | -LL | "fail - | ^^^^^ +LL | / "fail +LL | | + | |___________^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.rs b/tests/rustdoc-ui/doctest/warn-main-not-called.rs index 25d92e9cee9f4..ec762486d5d42 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.rs +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.rs @@ -8,6 +8,7 @@ // won't be called. //! ``` +//~^ WARN the `main` function of this doctest won't be run //! macro_rules! bla { //! ($($x:tt)*) => {} //! } @@ -17,6 +18,7 @@ //! ``` //! //! ``` +//~^^ WARN the `main` function of this doctest won't be run //! let x = 12; //! fn main() {} //! ``` diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr index b5b7bc57644a6..5feca6f9175fe 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr @@ -7,8 +7,8 @@ LL | //! ``` warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:19:1 | -LL | //! ``` - | ^^^^^^^ +LL | //! + | ^^^ warning: 2 warnings emitted From c1443e2591c284f6ac8372d15c88f8d6c9470e4c Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 12:02:35 +0200 Subject: [PATCH 1213/1900] Add regression test for `-Zcrate-attr` in `rustdoc --test` --- .../rustdoc-ui/doctest/unstable-opts-143930.rs | 14 ++++++++++++++ .../doctest/unstable-opts-143930.stdout | 5 +++++ .../unstable-opts-147276.crate_attr.stdout | 5 +++++ .../doctest/unstable-opts-147276.normal.stderr | 13 +++++++++++++ .../rustdoc-ui/doctest/unstable-opts-147276.rs | 17 +++++++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-143930.rs create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-143930.stdout create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.rs diff --git a/tests/rustdoc-ui/doctest/unstable-opts-143930.rs b/tests/rustdoc-ui/doctest/unstable-opts-143930.rs new file mode 100644 index 0000000000000..30c47f5b7e9e3 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-143930.rs @@ -0,0 +1,14 @@ +// This test verifies that unstable options like `-Zcrate-attr` are respected when `--test` is +// passed. +// +// +// +// NOTE: If any of these command line arguments or features get stabilized, please replace with +// another unstable one. + +//@ check-pass +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ compile-flags: --test -Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(rapx) + +#[rapx::tag] +fn f() {} diff --git a/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout b/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout new file mode 100644 index 0000000000000..7326c0a25a069 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout b/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout new file mode 100644 index 0000000000000..7326c0a25a069 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr b/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr new file mode 100644 index 0000000000000..eebf4f307f127 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[used(linker)]` is currently unstable + --> $DIR/unstable-opts-147276.rs:15:1 + | +LL | #[used(linker)] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #93798 for more information + = help: add `#![feature(used_with_arg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.rs b/tests/rustdoc-ui/doctest/unstable-opts-147276.rs new file mode 100644 index 0000000000000..64cafcaaff4e3 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.rs @@ -0,0 +1,17 @@ +// This test verifies that unstable options like `-Zcrate-attr` are respected when `--test` is +// passed. +// +// +// +// NOTE: If any of these command line arguments or features get stabilized, please replace with +// another unstable one. + +//@ revisions: normal crate_attr +//@ compile-flags: --test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@[crate_attr] check-pass +//@[crate_attr] compile-flags: -Zcrate-attr=feature(used_with_arg) + +#[used(linker)] +//[normal]~^ ERROR `#[used(linker)]` is currently unstable +static REPRO: isize = 1; From 3f9c493da1fa46ff0ef6d23e60a37c89ef3f5933 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Fri, 3 Oct 2025 16:10:26 +0100 Subject: [PATCH 1214/1900] Add xtensa arch to object file creation --- compiler/rustc_target/src/spec/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4a82a8bd888e6..2f7109de804ae 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3182,6 +3182,7 @@ impl Target { "avr" => (Architecture::Avr, None), "msp430" => (Architecture::Msp430, None), "hexagon" => (Architecture::Hexagon, None), + "xtensa" => (Architecture::Xtensa, None), "bpf" => (Architecture::Bpf, None), "loongarch32" => (Architecture::LoongArch32, None), "loongarch64" => (Architecture::LoongArch64, None), From e43a0b6bdbdf5a4bab5be6b925698971c55dae1a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 3 Oct 2025 11:58:30 -0400 Subject: [PATCH 1215/1900] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index f2932725b045d..2394ea6cea8b2 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit f2932725b045d361ff5f18ba02b1409dd1f44e71 +Subproject commit 2394ea6cea8b26d717aa67eb1663a2dbf2d26078 From 58889457cac8225e6bae4855ca1911dd0a378f65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 16:08:14 +0200 Subject: [PATCH 1216/1900] Display merged doctests times as expected depending on the format specified --- src/librustdoc/doctest.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 9499258f983a1..5ea3ede78bc44 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -12,7 +12,7 @@ use std::process::{self, Command, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use std::{fmt, panic, str}; +use std::{panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; @@ -60,24 +60,15 @@ impl MergedDoctestTimes { self.added_compilation_times += 1; } - fn display_times(&self) { + /// Returns `(total_time, compilation_time)`. + fn times_in_secs(&self) -> Option<(f64, f64)> { // If no merged doctest was compiled, then there is nothing to display since the numbers // displayed by `libtest` for standalone tests are already accurate (they include both // compilation and runtime). - if self.added_compilation_times > 0 { - println!("{self}"); + if self.added_compilation_times == 0 { + return None; } - } -} - -impl fmt::Display for MergedDoctestTimes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "all doctests ran in {:.2}s; merged doctests compilation took {:.2}s", - self.total_time.elapsed().as_secs_f64(), - self.compilation_time.as_secs_f64(), - ) + Some((self.total_time.elapsed().as_secs_f64(), self.compilation_time.as_secs_f64())) } } @@ -400,15 +391,20 @@ pub(crate) fn run_tests( if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { + let times = times.times_in_secs(); // We ensure temp dir destructor is called. std::mem::drop(temp_dir.take()); - times.display_times(); + if let Some((total_time, compilation_time)) = times { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } }); } else { // If the first condition branch exited successfully, `test_main_with_exit_callback` will // not exit the process. So to prevent displaying the times twice, we put it behind an // `else` condition. - times.display_times(); + if let Some((total_time, compilation_time)) = times.times_in_secs() { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } } // We ensure temp dir destructor is called. std::mem::drop(temp_dir); From a9ab29cdb45005d69738c3a7cbec4c5cd0fceb73 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Fri, 12 Sep 2025 13:10:01 -0400 Subject: [PATCH 1217/1900] add mem::conjure_zst --- library/core/src/mem/mod.rs | 58 ++++++++++++++++++++++++++ tests/ui/consts/std/conjure_zst.rs | 10 +++++ tests/ui/consts/std/conjure_zst.stderr | 12 ++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/ui/consts/std/conjure_zst.rs create mode 100644 tests/ui/consts/std/conjure_zst.stderr diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index db4c8e9e55150..c484551187cc3 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -7,6 +7,7 @@ use crate::alloc::Layout; use crate::marker::DiscriminantKind; +use crate::panic::const_assert; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; mod manually_drop; @@ -1407,3 +1408,60 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { // The `{}` is for better error messages {builtin # offset_of($Container, $($fields)+)} } + +/// Create a fresh instance of the inhabited ZST type `T`. +/// +/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`] +/// in places where you know that `T` is zero-sized, but don't have a bound +/// (such as [`Default`]) that would allow you to instantiate it using safe code. +/// +/// If you're not sure whether `T` is an inhabited ZST, then you should be +/// using [`MaybeUninit`], not this function. +/// +/// # Panics +/// +/// If `size_of::() != 0`. +/// +/// # Safety +/// +/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types +/// like zero-variant enums and [`!`] are unsound to conjure. +/// - You must use the value only in ways which do not violate any *safety* +/// invariants of the type. +/// +/// While it's easy to create a *valid* instance of an inhabited ZST, since having +/// no bits in its representation means there's only one possible value, that +/// doesn't mean that it's always *sound* to do so. +/// +/// For example, a library could design zero-sized tokens that are `!Default + !Clone`, limiting +/// their creation to functions that initialize some state or establish a scope. Conjuring such a +/// token could break invariants and lead to unsoundness. +/// +/// # Examples +/// +/// ``` +/// #![feature(mem_conjure_zst)] +/// use std::mem::conjure_zst; +/// +/// assert_eq!(unsafe { conjure_zst::<()>() }, ()); +/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []); +/// ``` +/// +/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited +#[unstable(feature = "mem_conjure_zst", issue = "95383")] +pub const unsafe fn conjure_zst() -> T { + const_assert!( + size_of::() == 0, + "mem::conjure_zst invoked on a nonzero-sized type", + "mem::conjure_zst invoked on type {t}, which is not zero-sized", + t: &str = stringify!(T) + ); + + // SAFETY: because the caller must guarantee that it's inhabited and zero-sized, + // there's nothing in the representation that needs to be set. + // `assume_init` calls `assert_inhabited`, so we don't need to here. + unsafe { + #[allow(clippy::uninit_assumed_init)] + MaybeUninit::uninit().assume_init() + } +} diff --git a/tests/ui/consts/std/conjure_zst.rs b/tests/ui/consts/std/conjure_zst.rs new file mode 100644 index 0000000000000..c04deae502b0f --- /dev/null +++ b/tests/ui/consts/std/conjure_zst.rs @@ -0,0 +1,10 @@ +#![feature(mem_conjure_zst)] + +use std::{convert::Infallible, mem}; + +const INVALID: Infallible = unsafe { mem::conjure_zst() }; +//~^ ERROR attempted to instantiate uninhabited type + +const VALID: () = unsafe { mem::conjure_zst() }; + +fn main() {} diff --git a/tests/ui/consts/std/conjure_zst.stderr b/tests/ui/consts/std/conjure_zst.stderr new file mode 100644 index 0000000000000..0c4a978b81ee6 --- /dev/null +++ b/tests/ui/consts/std/conjure_zst.stderr @@ -0,0 +1,12 @@ +error[E0080]: evaluation panicked: aborted execution: attempted to instantiate uninhabited type `Infallible` + --> $DIR/conjure_zst.rs:5:38 + | +LL | const INVALID: Infallible = unsafe { mem::conjure_zst() }; + | ^^^^^^^^^^^^^^^^^^ evaluation of `INVALID` failed inside this call + | +note: inside `conjure_zst::` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From d5ba5c1c925f82de2efbfbbe92ffee56a6a6a334 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 3 Oct 2025 16:00:15 -0400 Subject: [PATCH 1218/1900] Mark `PatternTypo` suggestion as maybe incorrect --- compiler/rustc_passes/src/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f0726014e0afa..e01e9e384c035 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1365,7 +1365,7 @@ pub(crate) struct UnusedVarAssignedOnly { #[multipart_suggestion( passes_unused_var_typo, style = "verbose", - applicability = "machine-applicable" + applicability = "maybe-incorrect" )] pub(crate) struct PatternTypo { #[suggestion_part(code = "{code}")] From fd96a78092e77be45d5b60cedea0806b70d751fd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 3 Oct 2025 12:14:41 -0700 Subject: [PATCH 1219/1900] Add documentation about unwinding to wasm targets This commit adds some documentation about the state of `-Cpanic=unwind` for the following wasm targets: * `wasm32-unknown-unknown` * `wasm32-wasip1` * `wasm32-wasip2` * `wasm32v1-none` Notably it's possible to use `-Cpanic=unwind` with `-Zbuild-std` and it's also mentioned that there are no concrete proposals at this time to adding a new set of targets which support unwinding. My hunch is that in a few years' time it would make sense to enable it by default on these targets (except for `wasm32v1-none`) but that's a problem for future folks to debate. For now this is an attempt to document the status quo. --- .../wasm32-unknown-unknown.md | 62 ++++++++++++++++++- .../src/platform-support/wasm32-wasip1.md | 6 ++ .../src/platform-support/wasm32-wasip2.md | 6 ++ .../src/platform-support/wasm32v1-none.md | 11 ++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md index 1e74c47221c78..ec20672f65403 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md @@ -157,7 +157,7 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno ``` Here the `mvp` "cpu" is a placeholder in LLVM for disabling all supported -features by default. Cargo's `-Zbuild-std` feature, a Nightly Rust feature, is +features by default. Cargo's [`-Zbuild-std`] feature, a Nightly Rust feature, is then used to recompile the standard library in addition to your own code. This will produce a binary that uses only the original WebAssembly features by default and no proposals since its inception. @@ -207,3 +207,63 @@ conditionally compile code instead. This is notably different to the way native platforms such as x86\_64 work, and this is due to the fact that WebAssembly binaries must only contain code the engine understands. Native binaries work so long as the CPU doesn't execute unknown code dynamically at runtime. + +## Unwinding + +By default the `wasm32-unknown-unknown` target is compiled with `-Cpanic=abort`. +Historically this was due to the fact that there was no way to catch panics in +wasm, but since mid-2025 the WebAssembly [`exception-handling` +proposal](https://github.com/WebAssembly/exception-handling) reached +stabilization. LLVM has support for this proposal as well and when this is all +combined together it's possible to enable `-Cpanic=unwind` on wasm targets. + +Compiling wasm targets with `-Cpanic=unwind` is not as easy as just passing +`-Cpanic=unwind`, however: + +```sh +$ rustc foo.rs -Cpanic=unwind --target wasm32-unknown-unknown +error: the crate `panic_unwind` does not have the panic strategy `unwind` +``` + +Notably the precompiled standard library that is shipped through Rustup is +compiled with `-Cpanic=abort`, not `-Cpanic=unwind`. While this is the case +you're going to be required to use Cargo's [`-Zbuild-std`] feature to build with +unwinding support: + +```sh +$ RUSTFLAGS='-Cpanic=unwind' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std +``` + +Note, however, that as of 2025-10-03 LLVM is still using the "legacy exception +instructions" by default, not the officially standard version of the +exception-handling proposal: + +```sh +$ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm +error: /library/std/src/sys/backtrace.rs:161:5 +function `std::sys::backtrace::__rust_begin_short_backtrace` failed to validate + +Caused by: + 0: func 2 failed to validate + 1: legacy_exceptions feature required for try instruction (at offset 0x880) +``` + +Fixing this requires passing `-Cllvm-args=-wasm-use-legacy-eh=false` to the Rust +compiler as well: + +```sh +$ RUSTFLAGS='-Cpanic=unwind -Cllvm-args=-wasm-use-legacy-eh=false' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std +$ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm +``` + +At this time there are no concrete plans for adding new targets to the Rust +compiler which have `-Cpanic=unwind` enabled-by-default. The most likely route +to having this enabled is that in a few years when the `exception-handling` +target feature is enabled by default in LLVM (due to browsers/runtime support +propagating widely enough) the targets will switch to using `-Cpanic=unwind` by +default. This is not for certain, however, and will likely be accompanied with +either an MCP or an RFC about changing all wasm targets in the same manner. In +the meantime using `-Cpanic=unwind` will require using [`-Zbuild-std`] and +passing the appropriate flags to rustc. + +[`-Zbuild-std`]: ../../cargo/reference/unstable.html#build-std diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index a8a9e5505810b..958a34a86928c 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -133,3 +133,9 @@ to Rust 1.80 the `target_env` condition was not set. The default set of WebAssembly features enabled for compilation is currently the same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the documentation there for more information. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. For information on +using `-Cpanic=unwind` see the [documentation about unwinding for +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index dea33e62f2b43..861083ad4a61a 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -67,3 +67,9 @@ It's recommended to conditionally compile code for this target with: The default set of WebAssembly features enabled for compilation is currently the same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the documentation there for more information. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. For information on +using `-Cpanic=unwind` see the [documentation about unwinding for +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). diff --git a/src/doc/rustc/src/platform-support/wasm32v1-none.md b/src/doc/rustc/src/platform-support/wasm32v1-none.md index 51b00de5e578a..d2f9b3a1f9760 100644 --- a/src/doc/rustc/src/platform-support/wasm32v1-none.md +++ b/src/doc/rustc/src/platform-support/wasm32v1-none.md @@ -107,3 +107,14 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno Which not only rebuilds `std`, `core` and `alloc` (which is somewhat costly and annoying) but more importantly requires the use of nightly Rust toolchains (for the `-Zbuild-std` flag). This is very undesirable for the target audience, which consists of people targeting WebAssembly implementations that prioritize stability, simplicity and/or security over feature support. This `wasm32v1-none` target exists as an alternative option that works on stable Rust toolchains, without rebuilding the stdlib. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. Using `-Cpanic=unwind` +would require using the WebAssembly exception-handling proposal stabilized +mid-2025, and if that's desired then you most likely don't want to use this +target and instead want to use `wasm32-unknown-unknown` instead. It's unlikely +that this target will ever support unwinding with the precompiled artifacts +shipped through rustup. For documentation about using `-Zbuild-std` to enable +using `-Cpanic=unwind` see the [documentation of +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). From 55aeb1747d97b70c75220ade6cdc53523886defd Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 2 Oct 2025 00:51:44 +0000 Subject: [PATCH 1220/1900] Do not assert that a change in global cache only happens when concurrent --- compiler/rustc_middle/src/ty/context.rs | 5 +- compiler/rustc_type_ir/src/interner.rs | 8 +-- .../src/search_graph/global_cache.rs | 4 +- .../rustc_type_ir/src/search_graph/mod.rs | 2 +- ...rted.stderr => unsupported.current.stderr} | 28 +++++----- tests/ui/delegation/unsupported.next.stderr | 51 +++++++++++++++++++ tests/ui/delegation/unsupported.rs | 8 +++ 7 files changed, 84 insertions(+), 22 deletions(-) rename tests/ui/delegation/{unsupported.stderr => unsupported.current.stderr} (76%) create mode 100644 tests/ui/delegation/unsupported.next.stderr diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..9a0d2602a5f17 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -207,8 +207,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { from_entry(entry) } - fn evaluation_is_concurrent(&self) -> bool { - self.sess.threads() > 1 + fn assert_evaluation_is_concurrent(&self) { + // Turns out, the assumption for this function isn't perfect. + // See trait-system-refactor-initiative#234. } fn expand_abstract_consts>>(self, t: T) -> T { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cf58aec14a680..f12dcbfef98bc 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -185,7 +185,9 @@ pub trait Interner: from_entry: impl FnOnce(&CanonicalParamEnvCacheEntry) -> R, ) -> R; - fn evaluation_is_concurrent(&self) -> bool; + /// Useful for testing. If a cache entry is replaced, this should + /// (in theory) only happen when concurrent. + fn assert_evaluation_is_concurrent(&self); fn expand_abstract_consts>(self, t: T) -> T; @@ -569,7 +571,7 @@ impl search_graph::Cx for I { fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R { I::with_global_cache(self, f) } - fn evaluation_is_concurrent(&self) -> bool { - self.evaluation_is_concurrent() + fn assert_evaluation_is_concurrent(&self) { + self.assert_evaluation_is_concurrent() } } diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index eb56c1af408bb..7e438fefffca0 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -56,13 +56,13 @@ impl GlobalCache { let with_overflow = WithOverflow { nested_goals, result }; let prev = entry.with_overflow.insert(required_depth, with_overflow); if let Some(prev) = &prev { - assert!(cx.evaluation_is_concurrent()); + cx.assert_evaluation_is_concurrent(); assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } else { let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { - assert!(cx.evaluation_is_concurrent()); + cx.assert_evaluation_is_concurrent(); assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8f8f019510fe8..d7f91ef692c56 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -53,7 +53,7 @@ pub trait Cx: Copy { fn with_global_cache(self, f: impl FnOnce(&mut GlobalCache) -> R) -> R; - fn evaluation_is_concurrent(&self) -> bool; + fn assert_evaluation_is_concurrent(&self); } pub trait Delegate: Sized { diff --git a/tests/ui/delegation/unsupported.stderr b/tests/ui/delegation/unsupported.current.stderr similarity index 76% rename from tests/ui/delegation/unsupported.stderr rename to tests/ui/delegation/unsupported.current.stderr index f69be60133e2b..55564e8f231f7 100644 --- a/tests/ui/delegation/unsupported.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -1,43 +1,43 @@ -error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` - --> $DIR/unsupported.rs:22:25 +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/unsupported.rs:22:25 + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition - --> $DIR/unsupported.rs:22:25 + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle +note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ = 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 -error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` - --> $DIR/unsupported.rs:25:24 +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/unsupported.rs:25:24 + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition - --> $DIR/unsupported.rs:25:24 + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle +note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = 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 error: recursive delegation is not supported yet - --> $DIR/unsupported.rs:38:22 + --> $DIR/unsupported.rs:46:22 | LL | pub reuse to_reuse2::foo; | --- callee defined here @@ -46,7 +46,7 @@ LL | reuse to_reuse1::foo; | ^^^ error[E0283]: type annotations needed - --> $DIR/unsupported.rs:48:18 + --> $DIR/unsupported.rs:56:18 | LL | reuse Trait::foo; | ^^^ cannot infer type diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr new file mode 100644 index 0000000000000..606a25d4269a1 --- /dev/null +++ b/tests/ui/delegation/unsupported.next.stderr @@ -0,0 +1,51 @@ +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:30:25 + | +LL | reuse to_reuse::opaque_ret; + | ^^^^^^^^^^ + | +note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... + --> $DIR/unsupported.rs:30:25 + | +LL | reuse to_reuse::opaque_ret; + | ^^^^^^^^^^ + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle + = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) + = 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 + +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:33:24 + | +LL | reuse ToReuse::opaque_ret; + | ^^^^^^^^^^ + | +note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... + --> $DIR/unsupported.rs:33:24 + | +LL | reuse ToReuse::opaque_ret; + | ^^^^^^^^^^ + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle + = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) + = 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 + +error: recursive delegation is not supported yet + --> $DIR/unsupported.rs:46:22 + | +LL | pub reuse to_reuse2::foo; + | --- callee defined here +... +LL | reuse to_reuse1::foo; + | ^^^ + +error[E0283]: type annotations needed + --> $DIR/unsupported.rs:56:18 + | +LL | reuse Trait::foo; + | ^^^ cannot infer type + | + = note: cannot satisfy `_: effects::Trait` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0283, E0391. +For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/delegation/unsupported.rs b/tests/ui/delegation/unsupported.rs index af1c20976d7ad..79bab342da091 100644 --- a/tests/ui/delegation/unsupported.rs +++ b/tests/ui/delegation/unsupported.rs @@ -1,3 +1,11 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-fail + +// Next solver revision included because of trait-system-refactor-initiative#234. +// If we end up in a query cycle, it should be okay as long as results are the same. + #![feature(const_trait_impl)] #![feature(c_variadic)] #![feature(fn_delegation)] From 283ad6699cb806f6b5d38efc13a40dbd9a3321ca Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:56:33 -0400 Subject: [PATCH 1221/1900] Fix comment, change first_pass to error_on_missing_defining_use, remove the assert in favor over a comment, and change to & for &mut --- compiler/rustc_hir_typeck/src/opaque_types.rs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 9d0e6d5abe181..a46e1a5f75fa7 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -26,15 +26,15 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// Unlike `handle_opaque_type_uses_next`, this does not report errors. #[instrument(level = "debug", skip(self))] pub(super) fn try_handle_opaque_type_uses_next(&mut self) { - // We clone the opaques instead of stealing them here as they are still used for - // normalization in the next generation trait solver. + // We clone the opaques instead of stealing them here as we still need + // to use them after fallback. let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types(); for entry in &mut opaque_types { *entry = self.resolve_vars_if_possible(*entry); } debug!(?opaque_types); - self.compute_definition_site_hidden_types(&opaque_types, true); + self.compute_definition_site_hidden_types(&opaque_types, false); } /// This takes all the opaque type uses during HIR typeck. It first computes @@ -58,7 +58,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } debug!(?opaque_types); - self.compute_definition_site_hidden_types(&opaque_types, false); + self.compute_definition_site_hidden_types(&opaque_types, true); } } @@ -97,7 +97,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fn compute_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], - first_pass: bool, + error_on_missing_defining_use: bool, ) { let tcx = self.tcx; let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() @@ -138,19 +138,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); } - let prev = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty); - - // We do want to insert opaque types the first pass, because - // we want to equate them. So, the second pass (where we - // report errors) may have a hidden type inserted. - if first_pass { - assert!(prev.is_none()); - } + // Being explicit here: it may be possible that we in a + // previous call to this function we did an insert, but this + // should be just fine, since they all get equated anyways and + // we shouldn't ever go from `HasDefiningUse` to anyway else. + let _ = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty); } // If this the first pass (`try_handle_opaque_type_uses_next`), // then do not report any errors. - if first_pass { + if !error_on_missing_defining_use { continue; } @@ -204,7 +201,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { #[tracing::instrument(skip(self), ret)] fn consider_opaque_type_use( - &mut self, + &self, opaque_type_key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, ) -> UsageKind<'tcx> { From c721fa243825255c0f195d4282021e7c263c3e7f Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 28 Sep 2025 03:21:45 +0000 Subject: [PATCH 1222/1900] interpret #[used] as #[used(compiler)] on illumos illumos' `ld` does not support a flag like either SHF_GNU_RETAIN or SHF_SUNW_NODISCARD, so there is no way to communicate `#[used(linker)]` for that target. Setting `USED_LINKER` to try results in LLVM setting SHF_SUNW_NODISCARD for Solaris-like targets, which is an unknown section header flag for illumos `ld` and prevents sections from being merged that otherwise would. As a motivating example, the `inventory` crate produces `#[used]` items to merge into `.init_array`. Because those items have an unknown section header flag they are not merged with the default `.init_array` with `frame_dummy`, and end up never executed. Downgrading `#[used]` to `#[used(compiler)]` on illumos keeps so-attributed items as preserved as they had been before https://github.com/rust-lang/rust/pull/140872. As was the case before that change, because rustc passes `-z ignore` to illumos `ld`, it's possible that `used` sections are GC'd at link time. https://github.com/rust-lang/rust/issues/146169 describes this unfortunate circumstance. --- .../src/attributes/codegen_attrs.rs | 36 ++++++++++++++----- .../rustc_codegen_ssa/src/codegen_attrs.rs | 13 +++++++ .../rustc_hir/src/attrs/data_structures.rs | 3 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 262b8213977b3..7978bf28214cf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -370,6 +370,7 @@ impl NoArgsAttributeParser for NoMangleParser { pub(crate) struct UsedParser { first_compiler: Option, first_linker: Option, + first_default: Option, } // A custom `AttributeParser` is used rather than a Simple attribute parser because @@ -382,7 +383,7 @@ impl AttributeParser for UsedParser { template!(Word, List: &["compiler", "linker"]), |group: &mut Self, cx, args| { let used_by = match args { - ArgParser::NoArgs => UsedBy::Linker, + ArgParser::NoArgs => UsedBy::Default, ArgParser::List(list) => { let Some(l) = list.single() else { cx.expected_single_argument(list.span); @@ -423,12 +424,29 @@ impl AttributeParser for UsedParser { ArgParser::NameValue(_) => return, }; + let attr_span = cx.attr_span; + + // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the + // circumstances are more complicated). While we're checking `used_by`, also report + // these cross-`UsedBy` duplicates to warn. let target = match used_by { UsedBy::Compiler => &mut group.first_compiler, - UsedBy::Linker => &mut group.first_linker, + UsedBy::Linker => { + if let Some(prev) = group.first_default { + cx.warn_unused_duplicate(prev, attr_span); + return; + } + &mut group.first_linker + } + UsedBy::Default => { + if let Some(prev) = group.first_linker { + cx.warn_unused_duplicate(prev, attr_span); + return; + } + &mut group.first_default + } }; - let attr_span = cx.attr_span; if let Some(prev) = *target { cx.warn_unused_duplicate(prev, attr_span); } else { @@ -440,11 +458,13 @@ impl AttributeParser for UsedParser { AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { - // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker` - Some(match (self.first_compiler, self.first_linker) { - (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span }, - (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span }, - (None, None) => return None, + // If a specific form of `used` is specified, it takes precedence over generic `#[used]`. + // If both `linker` and `compiler` are specified, use `linker`. + Some(match (self.first_compiler, self.first_linker, self.first_default) { + (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span }, + (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span }, + (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span }, + (None, None, None) => return None, }) } } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 32ae810ecc892..2c7643e46ceaa 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -263,6 +263,19 @@ fn process_builtin_attrs( AttributeKind::Used { used_by, .. } => match used_by { UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, + UsedBy::Default => { + let used_form = if tcx.sess.target.os == "illumos" { + // illumos' `ld` doesn't support a section header that would represent + // `#[used(linker)]`, see + // https://github.com/rust-lang/rust/issues/146169. For that target, + // downgrade as if `#[used(compiler)]` was requested and hope for the + // best. + CodegenFnAttrFlags::USED_COMPILER + } else { + CodegenFnAttrFlags::USED_LINKER + }; + codegen_fn_attrs.flags |= used_form; + } }, AttributeKind::FfiConst(_) => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ddcbaeaad8803..beeca7332cb17 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -146,12 +146,13 @@ impl Deprecation { } /// There are three valid forms of the attribute: -/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable. +/// `#[used]`, which is equivalent to `#[used(linker)]` on targets that support it, but `#[used(compiler)]` if not. /// `#[used(compiler)]` /// `#[used(linker)]` #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic, PrintAttribute)] pub enum UsedBy { + Default, Compiler, Linker, } From 12cfad9a8b6aeed10ddc240b4923913246353ebb Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Fri, 3 Oct 2025 18:12:22 -0400 Subject: [PATCH 1223/1900] update autodiff batching test --- tests/codegen-llvm/autodiff/batched.rs | 74 +++++++++++++++----------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs index dc82403212fb0..0ff6134bc07d5 100644 --- a/tests/codegen-llvm/autodiff/batched.rs +++ b/tests/codegen-llvm/autodiff/batched.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat +//@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme // @@ -23,7 +23,7 @@ fn square(x: &f32) -> f32 { } // d_square2 -// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'") +// CHECK: define internal [4 x float] @fwddiffe4square(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") // CHECK-NEXT: start: // CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 // CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 @@ -33,23 +33,28 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 -// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val -// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 -// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 -// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val -// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 -// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 -// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val -// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 -// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 -// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val -// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 -// CHECK-NEXT: ret [4 x float] %15 +// CHECK-NEXT: %_2 = load float, ptr %x, align 4 +// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %12 = fadd fast float %4, %8 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = fadd fast float %5, %9 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = fadd fast float %6, %10 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = fadd fast float %7, %11 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: ret [4 x float] %19 // CHECK-NEXT: } // d_square3, the extra float is the original return value (x * x) -// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.1(float %x.0.val, [4 x ptr] %"x'") +// CHECK: define internal { float, [4 x float] } @fwddiffe4square.1(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") // CHECK-NEXT: start: // CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 // CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 @@ -59,22 +64,27 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %_0 = fmul float %x.0.val, %x.0.val -// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 -// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val -// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 -// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 -// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val -// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 -// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 -// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val -// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 -// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 -// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val -// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 -// CHECK-NEXT: %16 = insertvalue { float, [4 x float] } undef, float %_0, 0 -// CHECK-NEXT: %17 = insertvalue { float, [4 x float] } %16, [4 x float] %15, 1 -// CHECK-NEXT: ret { float, [4 x float] } %17 +// CHECK-NEXT: %_2 = load float, ptr %x, align 4 +// CHECK-NEXT: %_0 = fmul float %_2, %_2 +// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %12 = fadd fast float %4, %8 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = fadd fast float %5, %9 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = fadd fast float %6, %10 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = fadd fast float %7, %11 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: %20 = insertvalue { float, [4 x float] } undef, float %_0, 0 +// CHECK-NEXT: %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1 +// CHECK-NEXT: ret { float, [4 x float] } %21 // CHECK-NEXT: } fn main() { From 422b91443f1016aec2a1f00316d01a117ddc7829 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 4 Oct 2025 15:02:43 +0800 Subject: [PATCH 1224/1900] Fix top level ui tests check in tidy --- src/tools/tidy/src/ui_tests.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 12eca47c171dc..c74ecf3d43f46 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -81,21 +81,20 @@ fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) { // - // - - let top_level_ui_tests = walkdir::WalkDir::new(tests_path) - .min_depth(1) - .max_depth(1) + let top_level_ui_tests = ignore::WalkBuilder::new(tests_path) + .max_depth(Some(1)) .follow_links(false) - .same_file_system(true) - .into_iter() + .build() .flatten() .filter(|e| { let file_name = e.file_name(); file_name != ".gitattributes" && file_name != "README.md" }) - .filter(|e| !e.file_type().is_dir()); + .filter(|e| !e.file_type().is_some_and(|f| f.is_dir())); + for entry in top_level_ui_tests { check.error(format!( - "ui tests should be added under meaningful subdirectories: `{}`", + "ui tests should be added under meaningful subdirectories: `{}`, see https://github.com/rust-lang/compiler-team/issues/902", entry.path().display() )); } From f955c76a5b8ea3265d09139126d7f0df7fcebb18 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 14:31:32 +1000 Subject: [PATCH 1225/1900] Remove inherent methods from several LLVM FFI types Using a helper trait allows the conversions to remain in `rustc_codegen_llvm`, even if the FFI types are moved to a different crate. --- compiler/rustc_codegen_llvm/src/allocator.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 3 +- .../src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/declare.rs | 2 +- .../src/llvm/conversions.rs | 115 ++++++++++++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 107 ---------------- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 2 + 7 files changed, 122 insertions(+), 111 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/llvm/conversions.rs diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 896d6755c7527..824aa501036d4 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -14,7 +14,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use crate::attributes::llfn_attrs_from_instance; use crate::builder::SBuilder; use crate::declare::declare_simple_fn; -use crate::llvm::{self, FALSE, TRUE, Type, Value}; +use crate::llvm::{self, FALSE, FromGeneric, TRUE, Type, Value}; use crate::{SimpleCx, attributes, debuginfo}; pub(crate) unsafe fn codegen( diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index e7cb18ab22daf..08da90d535da1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -36,7 +36,8 @@ use crate::attributes; use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ - self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, GEPNoWrapFlags, Metadata, TRUE, ToLlvmBool, + self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, + ToLlvmBool, }; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 2f9e7cae54f2b..b8b5f3d30d7c8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -37,11 +37,11 @@ use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; -use crate::llvm; use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, DebugNameTableKind, }; +use crate::llvm::{self, FromGeneric}; use crate::value::Value; impl PartialEq for llvm::Metadata { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 36cdb49883957..f598763efcf22 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -26,7 +26,7 @@ use crate::abi::FnAbiLlvmExt; use crate::common::AsCCharPtr; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; -use crate::llvm::Visibility; +use crate::llvm::{FromGeneric, Visibility}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; diff --git a/compiler/rustc_codegen_llvm/src/llvm/conversions.rs b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs new file mode 100644 index 0000000000000..9e9f9339ade85 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs @@ -0,0 +1,115 @@ +//! Conversions from backend-independent data types to/from LLVM FFI types. + +use rustc_codegen_ssa::common::{AtomicRmwBinOp, IntPredicate, RealPredicate}; +use rustc_middle::ty::AtomicOrdering; +use rustc_session::config::DebugInfo; +use rustc_target::spec::SymbolVisibility; + +use crate::llvm; + +/// Helper trait for converting backend-independent types to LLVM-specific +/// types, for FFI purposes. +pub(crate) trait FromGeneric { + fn from_generic(other: T) -> Self; +} + +impl FromGeneric for llvm::Visibility { + fn from_generic(visibility: SymbolVisibility) -> Self { + match visibility { + SymbolVisibility::Hidden => Self::Hidden, + SymbolVisibility::Protected => Self::Protected, + SymbolVisibility::Interposable => Self::Default, + } + } +} + +impl FromGeneric for llvm::IntPredicate { + fn from_generic(int_pred: IntPredicate) -> Self { + match int_pred { + IntPredicate::IntEQ => Self::IntEQ, + IntPredicate::IntNE => Self::IntNE, + IntPredicate::IntUGT => Self::IntUGT, + IntPredicate::IntUGE => Self::IntUGE, + IntPredicate::IntULT => Self::IntULT, + IntPredicate::IntULE => Self::IntULE, + IntPredicate::IntSGT => Self::IntSGT, + IntPredicate::IntSGE => Self::IntSGE, + IntPredicate::IntSLT => Self::IntSLT, + IntPredicate::IntSLE => Self::IntSLE, + } + } +} + +impl FromGeneric for llvm::RealPredicate { + fn from_generic(real_pred: RealPredicate) -> Self { + match real_pred { + RealPredicate::RealPredicateFalse => Self::RealPredicateFalse, + RealPredicate::RealOEQ => Self::RealOEQ, + RealPredicate::RealOGT => Self::RealOGT, + RealPredicate::RealOGE => Self::RealOGE, + RealPredicate::RealOLT => Self::RealOLT, + RealPredicate::RealOLE => Self::RealOLE, + RealPredicate::RealONE => Self::RealONE, + RealPredicate::RealORD => Self::RealORD, + RealPredicate::RealUNO => Self::RealUNO, + RealPredicate::RealUEQ => Self::RealUEQ, + RealPredicate::RealUGT => Self::RealUGT, + RealPredicate::RealUGE => Self::RealUGE, + RealPredicate::RealULT => Self::RealULT, + RealPredicate::RealULE => Self::RealULE, + RealPredicate::RealUNE => Self::RealUNE, + RealPredicate::RealPredicateTrue => Self::RealPredicateTrue, + } + } +} + +impl FromGeneric for llvm::AtomicRmwBinOp { + fn from_generic(op: AtomicRmwBinOp) -> Self { + match op { + AtomicRmwBinOp::AtomicXchg => Self::AtomicXchg, + AtomicRmwBinOp::AtomicAdd => Self::AtomicAdd, + AtomicRmwBinOp::AtomicSub => Self::AtomicSub, + AtomicRmwBinOp::AtomicAnd => Self::AtomicAnd, + AtomicRmwBinOp::AtomicNand => Self::AtomicNand, + AtomicRmwBinOp::AtomicOr => Self::AtomicOr, + AtomicRmwBinOp::AtomicXor => Self::AtomicXor, + AtomicRmwBinOp::AtomicMax => Self::AtomicMax, + AtomicRmwBinOp::AtomicMin => Self::AtomicMin, + AtomicRmwBinOp::AtomicUMax => Self::AtomicUMax, + AtomicRmwBinOp::AtomicUMin => Self::AtomicUMin, + } + } +} + +impl FromGeneric for llvm::AtomicOrdering { + fn from_generic(ordering: AtomicOrdering) -> Self { + match ordering { + AtomicOrdering::Relaxed => Self::Monotonic, + AtomicOrdering::Acquire => Self::Acquire, + AtomicOrdering::Release => Self::Release, + AtomicOrdering::AcqRel => Self::AcquireRelease, + AtomicOrdering::SeqCst => Self::SequentiallyConsistent, + } + } +} + +impl FromGeneric for llvm::debuginfo::DebugEmissionKind { + fn from_generic(kind: DebugInfo) -> Self { + // We should be setting LLVM's emission kind to `LineTablesOnly` if + // we are compiling with "limited" debuginfo. However, some of the + // existing tools relied on slightly more debuginfo being generated than + // would be the case with `LineTablesOnly`, and we did not want to break + // these tools in a "drive-by fix", without a good idea or plan about + // what limited debuginfo should exactly look like. So for now we are + // instead adding a new debuginfo option "line-tables-only" so as to + // not break anything and to allow users to have 'limited' debug info. + // + // See https://github.com/rust-lang/rust/issues/60020 for details. + match kind { + DebugInfo::None => Self::NoDebug, + DebugInfo::LineDirectivesOnly => Self::DebugDirectivesOnly, + DebugInfo::LineTablesOnly => Self::LineTablesOnly, + DebugInfo::Limited | DebugInfo::Full => Self::FullDebug, + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7fbba0294073f..965e775bed704 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -20,7 +20,6 @@ use std::ptr; use bitflags::bitflags; use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use rustc_macros::TryFromU32; -use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ @@ -220,16 +219,6 @@ pub(crate) enum Visibility { Protected = 2, } -impl Visibility { - pub(crate) fn from_generic(visibility: SymbolVisibility) -> Self { - match visibility { - SymbolVisibility::Hidden => Visibility::Hidden, - SymbolVisibility::Protected => Visibility::Protected, - SymbolVisibility::Interposable => Visibility::Default, - } - } -} - /// LLVMUnnamedAddr #[repr(C)] pub(crate) enum UnnamedAddr { @@ -319,24 +308,6 @@ pub(crate) enum IntPredicate { IntSLE = 41, } -impl IntPredicate { - pub(crate) fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self { - use rustc_codegen_ssa::common::IntPredicate as Common; - match intpre { - Common::IntEQ => Self::IntEQ, - Common::IntNE => Self::IntNE, - Common::IntUGT => Self::IntUGT, - Common::IntUGE => Self::IntUGE, - Common::IntULT => Self::IntULT, - Common::IntULE => Self::IntULE, - Common::IntSGT => Self::IntSGT, - Common::IntSGE => Self::IntSGE, - Common::IntSLT => Self::IntSLT, - Common::IntSLE => Self::IntSLE, - } - } -} - /// LLVMRealPredicate #[derive(Copy, Clone)] #[repr(C)] @@ -359,30 +330,6 @@ pub(crate) enum RealPredicate { RealPredicateTrue = 15, } -impl RealPredicate { - pub(crate) fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self { - use rustc_codegen_ssa::common::RealPredicate as Common; - match realp { - Common::RealPredicateFalse => Self::RealPredicateFalse, - Common::RealOEQ => Self::RealOEQ, - Common::RealOGT => Self::RealOGT, - Common::RealOGE => Self::RealOGE, - Common::RealOLT => Self::RealOLT, - Common::RealOLE => Self::RealOLE, - Common::RealONE => Self::RealONE, - Common::RealORD => Self::RealORD, - Common::RealUNO => Self::RealUNO, - Common::RealUEQ => Self::RealUEQ, - Common::RealUGT => Self::RealUGT, - Common::RealUGE => Self::RealUGE, - Common::RealULT => Self::RealULT, - Common::RealULE => Self::RealULE, - Common::RealUNE => Self::RealUNE, - Common::RealPredicateTrue => Self::RealPredicateTrue, - } - } -} - /// Must match the layout of `LLVMTypeKind`. /// /// Use [`RawEnum`] for values of `LLVMTypeKind` returned from LLVM, @@ -458,25 +405,6 @@ pub(crate) enum AtomicRmwBinOp { AtomicUMin = 10, } -impl AtomicRmwBinOp { - pub(crate) fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self { - use rustc_codegen_ssa::common::AtomicRmwBinOp as Common; - match op { - Common::AtomicXchg => Self::AtomicXchg, - Common::AtomicAdd => Self::AtomicAdd, - Common::AtomicSub => Self::AtomicSub, - Common::AtomicAnd => Self::AtomicAnd, - Common::AtomicNand => Self::AtomicNand, - Common::AtomicOr => Self::AtomicOr, - Common::AtomicXor => Self::AtomicXor, - Common::AtomicMax => Self::AtomicMax, - Common::AtomicMin => Self::AtomicMin, - Common::AtomicUMax => Self::AtomicUMax, - Common::AtomicUMin => Self::AtomicUMin, - } - } -} - /// LLVMAtomicOrdering #[derive(Copy, Clone)] #[repr(C)] @@ -493,19 +421,6 @@ pub(crate) enum AtomicOrdering { SequentiallyConsistent = 7, } -impl AtomicOrdering { - pub(crate) fn from_generic(ao: rustc_middle::ty::AtomicOrdering) -> Self { - use rustc_middle::ty::AtomicOrdering as Common; - match ao { - Common::Relaxed => Self::Monotonic, - Common::Acquire => Self::Acquire, - Common::Release => Self::Release, - Common::AcqRel => Self::AcquireRelease, - Common::SeqCst => Self::SequentiallyConsistent, - } - } -} - /// LLVMRustFileType #[derive(Copy, Clone)] #[repr(C)] @@ -940,28 +855,6 @@ pub(crate) mod debuginfo { DebugDirectivesOnly, } - impl DebugEmissionKind { - pub(crate) fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { - // We should be setting LLVM's emission kind to `LineTablesOnly` if - // we are compiling with "limited" debuginfo. However, some of the - // existing tools relied on slightly more debuginfo being generated than - // would be the case with `LineTablesOnly`, and we did not want to break - // these tools in a "drive-by fix", without a good idea or plan about - // what limited debuginfo should exactly look like. So for now we are - // instead adding a new debuginfo option "line-tables-only" so as to - // not break anything and to allow users to have 'limited' debug info. - // - // See https://github.com/rust-lang/rust/issues/60020 for details. - use rustc_session::config::DebugInfo; - match kind { - DebugInfo::None => DebugEmissionKind::NoDebug, - DebugInfo::LineDirectivesOnly => DebugEmissionKind::DebugDirectivesOnly, - DebugInfo::LineTablesOnly => DebugEmissionKind::LineTablesOnly, - DebugInfo::Limited | DebugInfo::Full => DebugEmissionKind::FullDebug, - } - } - } - /// LLVMRustDebugNameTableKind #[derive(Clone, Copy)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 9a53dacb1dfdd..4c58a92106d5c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,10 +11,12 @@ use rustc_llvm::RustString; pub(crate) use self::CallConv::*; pub(crate) use self::CodeGenOptSize::*; +pub(crate) use self::conversions::*; pub(crate) use self::ffi::*; pub(crate) use self::metadata_kind::*; use crate::common::AsCCharPtr; +mod conversions; pub(crate) mod diagnostic; pub(crate) mod enzyme_ffi; mod ffi; From 0661a3d6fe4efd7127c66aeb6484f06b3441c63f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 17:11:53 +1000 Subject: [PATCH 1226/1900] Remove inherent methods from `coverageinfo::ffi::Counter` This patch also moves `Regions` to a different module, since that type can stay put when the FFI bindings move to another crate. --- .../src/coverageinfo/ffi.rs | 45 +------------------ .../src/coverageinfo/llvm_cov.rs | 27 ++++++++++- .../src/coverageinfo/mapgen/covfun.rs | 27 +++++++---- 3 files changed, 45 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index a4b60d420f3fb..caa36c5dd4992 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,5 +1,3 @@ -use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId}; - /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -26,30 +24,12 @@ pub(crate) enum CounterKind { pub(crate) struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. pub(crate) kind: CounterKind, - id: u32, + pub(crate) id: u32, } impl Counter { /// A `Counter` of kind `Zero`. For this counter kind, the `id` is not used. pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 }; - - /// Constructs a new `Counter` of kind `CounterValueReference`. - pub(crate) fn counter_value_reference(counter_id: CounterId) -> Self { - Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() } - } - - /// Constructs a new `Counter` of kind `Expression`. - pub(crate) fn expression(expression_id: ExpressionId) -> Self { - Self { kind: CounterKind::Expression, id: expression_id.as_u32() } - } - - pub(crate) fn from_term(term: CovTerm) -> Self { - match term { - CovTerm::Zero => Self::ZERO, - CovTerm::Counter(id) => Self::counter_value_reference(id), - CovTerm::Expression(id) => Self::expression(id), - } - } } /// Corresponds to enum `llvm::coverage::CounterExpression::ExprKind`. @@ -94,29 +74,6 @@ pub(crate) struct CoverageSpan { pub(crate) end_col: u32, } -/// Holds tables of the various region types in one struct. -/// -/// Don't pass this struct across FFI; pass the individual region tables as -/// pointer/length pairs instead. -/// -/// Each field name has a `_regions` suffix for improved readability after -/// exhaustive destructing, which ensures that all region types are handled. -#[derive(Clone, Debug, Default)] -pub(crate) struct Regions { - pub(crate) code_regions: Vec, - pub(crate) expansion_regions: Vec, - pub(crate) branch_regions: Vec, -} - -impl Regions { - /// Returns true if none of this structure's tables contain any regions. - pub(crate) fn has_no_regions(&self) -> bool { - let Self { code_regions, expansion_regions, branch_regions } = self; - - code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty() - } -} - /// Must match the layout of `LLVMRustCoverageCodeRegion`. #[derive(Clone, Debug)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index d50eb533ffdbb..a58202834cfa5 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -57,12 +57,35 @@ pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef]) -> Vec, + pub(crate) expansion_regions: Vec, + pub(crate) branch_regions: Vec, +} + +impl Regions { + /// Returns true if none of this structure's tables contain any regions. + pub(crate) fn has_no_regions(&self) -> bool { + let Self { code_regions, expansion_regions, branch_regions } = self; + + code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty() + } +} + pub(crate) fn write_function_mappings_to_buffer( virtual_file_mapping: &[u32], expressions: &[ffi::CounterExpression], - regions: &ffi::Regions, + regions: &Regions, ) -> Vec { - let ffi::Regions { code_regions, expansion_regions, branch_regions } = regions; + let Regions { code_regions, expansion_regions, branch_regions } = regions; // SAFETY: // - All types are FFI-compatible and have matching representations in Rust/C++. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index e0da8d368762b..7835d18046860 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods}; use rustc_middle::mir::coverage::{ - BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, - MappingKind, Op, + BasicCoverageBlock, CounterId, CovTerm, CoverageIdsInfo, Expression, ExpressionId, + FunctionCoverageInfo, Mapping, MappingKind, Op, }; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::{SourceFile, Span}; @@ -36,7 +36,7 @@ pub(crate) struct CovfunRecord<'tcx> { virtual_file_mapping: VirtualFileMapping, expressions: Vec, - regions: ffi::Regions, + regions: llvm_cov::Regions, } impl<'tcx> CovfunRecord<'tcx> { @@ -64,7 +64,7 @@ pub(crate) fn prepare_covfun_record<'tcx>( is_used, virtual_file_mapping: VirtualFileMapping::default(), expressions, - regions: ffi::Regions::default(), + regions: llvm_cov::Regions::default(), }; fill_region_tables(tcx, fn_cov_info, ids_info, &mut covfun); @@ -77,10 +77,21 @@ pub(crate) fn prepare_covfun_record<'tcx>( Some(covfun) } +pub(crate) fn counter_for_term(term: CovTerm) -> ffi::Counter { + use ffi::Counter; + match term { + CovTerm::Zero => Counter::ZERO, + CovTerm::Counter(id) => { + Counter { kind: ffi::CounterKind::CounterValueReference, id: CounterId::as_u32(id) } + } + CovTerm::Expression(id) => { + Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) } + } + } +} + /// Convert the function's coverage-counter expressions into a form suitable for FFI. fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec { - let counter_for_term = ffi::Counter::from_term; - // We know that LLVM will optimize out any unused expressions before // producing the final coverage map, so there's no need to do the same // thing on the Rust side unless we're confident we can do much better. @@ -113,7 +124,7 @@ fn fill_region_tables<'tcx>( } else { CovTerm::Zero }; - ffi::Counter::from_term(term) + counter_for_term(term) }; // Currently a function's mappings must all be in the same file, so use the @@ -136,7 +147,7 @@ fn fill_region_tables<'tcx>( if discard_all { None } else { spans::make_coords(source_map, &source_file, span) } }; - let ffi::Regions { + let llvm_cov::Regions { code_regions, expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions branch_regions, From 3831b60ef3dd46d11f63041016bd1e6f6f9039f5 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 4 Oct 2025 17:54:03 +1000 Subject: [PATCH 1227/1900] Remove inherent methods from `llvm::Type` --- compiler/rustc_codegen_llvm/src/back/write.rs | 4 ++-- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++-- compiler/rustc_codegen_llvm/src/type_.rs | 24 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 1950b8288d149..6ce1cbe072c06 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -44,7 +44,7 @@ use crate::errors::{ }; use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo}; -use crate::type_::Type; +use crate::type_::llvm_type_ptr; use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util}; pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! { @@ -1160,7 +1160,7 @@ fn create_msvc_imps( // underscores added in front). let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" }; - let ptr_ty = Type::ptr_llcx(llcx); + let ptr_ty = llvm_type_ptr(llcx); let globals = base::iter_globals(llmod) .filter(|&val| { llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage && !llvm::is_declaration(val) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 922575dd63cfa..cda5b3a5f9aaf 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -740,7 +740,7 @@ impl<'ll> SimpleCx<'ll> { llcx: &'ll llvm::Context, pointer_size: Size, ) -> Self { - let isize_ty = llvm::Type::ix_llcx(llcx, pointer_size.bits()); + let isize_ty = llvm::LLVMIntTypeInContext(llcx, pointer_size.bits() as c_uint); Self(SCx { llmod, llcx, isize_ty }, PhantomData) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 965e775bed704..ccbc999dad1f0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -954,7 +954,7 @@ unsafe extern "C" { pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; + pub(crate) safe fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; @@ -983,7 +983,7 @@ unsafe extern "C" { ) -> &'a Type; // Operations on array, pointer, and vector types (sequence types) - pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + pub(crate) safe fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 1e5cf8374e379..9de9e0ec44c67 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -63,7 +63,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { ///x Creates an integer type with the given number of bits, e.g., i24 pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) } + llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) } pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { @@ -178,7 +178,7 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } fn type_i128(&self) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx(), 128) } + self.type_ix(128) } fn type_isize(&self) -> &'ll Type { @@ -210,11 +210,11 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } fn type_ptr(&self) -> &'ll Type { - self.type_ptr_ext(AddressSpace::ZERO) + llvm_type_ptr(self.llcx()) } fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type { - unsafe { llvm::LLVMPointerTypeInContext(self.llcx(), address_space.0) } + llvm_type_ptr_in_address_space(self.llcx(), address_space) } fn element_type(&self, ty: &'ll Type) -> &'ll Type { @@ -253,15 +253,15 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } } -impl Type { - /// Creates an integer type with the given number of bits, e.g., i24 - pub(crate) fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type { - unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) } - } +pub(crate) fn llvm_type_ptr(llcx: &llvm::Context) -> &Type { + llvm_type_ptr_in_address_space(llcx, AddressSpace::ZERO) +} - pub(crate) fn ptr_llcx(llcx: &llvm::Context) -> &Type { - unsafe { llvm::LLVMPointerTypeInContext(llcx, AddressSpace::ZERO.0) } - } +pub(crate) fn llvm_type_ptr_in_address_space<'ll>( + llcx: &'ll llvm::Context, + addr_space: AddressSpace, +) -> &'ll Type { + llvm::LLVMPointerTypeInContext(llcx, addr_space.0) } impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { From f8c54d24e2f4ca8539cb6d9b174e4c873bbb3ed7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 17:25:03 +1000 Subject: [PATCH 1228/1900] Remove inherent methods from `llvm::CallConv::from_conv` --- compiler/rustc_codegen_llvm/src/abi.rs | 78 +++++++++++----------- compiler/rustc_codegen_llvm/src/context.rs | 6 +- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 1703cab942bdd..15b4d5247a978 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use std::cmp; use libc::c_uint; @@ -13,7 +12,7 @@ use rustc_codegen_ssa::traits::*; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::{bug, ty}; -use rustc_session::config; +use rustc_session::{Session, config}; use rustc_target::callconv::{ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, }; @@ -400,7 +399,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv { - llvm::CallConv::from_conv(self.conv, cx.tcx.sess.target.arch.borrow()) + to_llvm_calling_convention(cx.tcx.sess, self.conv) } fn apply_attrs_llfn( @@ -663,43 +662,44 @@ impl AbiBuilderMethods for Builder<'_, '_, '_> { } } -impl llvm::CallConv { - pub(crate) fn from_conv(conv: CanonAbi, arch: &str) -> Self { - match conv { - CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, - CanonAbi::RustCold => llvm::PreserveMost, - // Functions with this calling convention can only be called from assembly, but it is - // possible to declare an `extern "custom"` block, so the backend still needs a calling - // convention for declaring foreign functions. - CanonAbi::Custom => llvm::CCallConv, - CanonAbi::GpuKernel => { - if arch == "amdgpu" { - llvm::AmdgpuKernel - } else if arch == "nvptx64" { - llvm::PtxKernel - } else { - panic!("Architecture {arch} does not support GpuKernel calling convention"); - } +/// Determines the appropriate [`llvm::CallConv`] to use for a given function +/// ABI, for the current target. +pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm::CallConv { + match abi { + CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, + CanonAbi::RustCold => llvm::PreserveMost, + // Functions with this calling convention can only be called from assembly, but it is + // possible to declare an `extern "custom"` block, so the backend still needs a calling + // convention for declaring foreign functions. + CanonAbi::Custom => llvm::CCallConv, + CanonAbi::GpuKernel => { + let arch = sess.target.arch.as_ref(); + if arch == "amdgpu" { + llvm::AmdgpuKernel + } else if arch == "nvptx64" { + llvm::PtxKernel + } else { + panic!("Architecture {arch} does not support GpuKernel calling convention"); } - CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { - InterruptKind::Avr => llvm::AvrInterrupt, - InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt, - InterruptKind::Msp430 => llvm::Msp430Intr, - InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv, - InterruptKind::X86 => llvm::X86_Intr, - }, - CanonAbi::Arm(arm_call) => match arm_call { - ArmCall::Aapcs => llvm::ArmAapcsCallConv, - ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv, - }, - CanonAbi::X86(x86_call) => match x86_call { - X86Call::Fastcall => llvm::X86FastcallCallConv, - X86Call::Stdcall => llvm::X86StdcallCallConv, - X86Call::SysV64 => llvm::X86_64_SysV, - X86Call::Thiscall => llvm::X86_ThisCall, - X86Call::Vectorcall => llvm::X86_VectorCall, - X86Call::Win64 => llvm::X86_64_Win64, - }, } + CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { + InterruptKind::Avr => llvm::AvrInterrupt, + InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt, + InterruptKind::Msp430 => llvm::Msp430Intr, + InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv, + InterruptKind::X86 => llvm::X86_Intr, + }, + CanonAbi::Arm(arm_call) => match arm_call { + ArmCall::Aapcs => llvm::ArmAapcsCallConv, + ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv, + }, + CanonAbi::X86(x86_call) => match x86_call { + X86Call::Fastcall => llvm::X86FastcallCallConv, + X86Call::Stdcall => llvm::X86StdcallCallConv, + X86Call::SysV64 => llvm::X86_64_SysV, + X86Call::Thiscall => llvm::X86_ThisCall, + X86Call::Vectorcall => llvm::X86_VectorCall, + X86Call::Win64 => llvm::X86_64_Win64, + }, } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index cda5b3a5f9aaf..3ddbd81ef7580 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -31,6 +31,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; +use crate::abi::to_llvm_calling_convention; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; @@ -901,10 +902,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { if self.get_declared_value(entry_name).is_none() { let llfn = self.declare_entry_fn( entry_name, - llvm::CallConv::from_conv( - self.sess().target.entry_abi, - self.sess().target.arch.borrow(), - ), + to_llvm_calling_convention(self.sess(), self.sess().target.entry_abi), llvm::UnnamedAddr::Global, fn_type, ); From e9a45e66461f3c324a249d7352eb2f4d184a6b2a Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 4 Oct 2025 14:48:55 +0800 Subject: [PATCH 1229/1900] Avoid to suggest pattern match on the similarly named in fn signature --- compiler/rustc_passes/src/liveness.rs | 7 +++++-- .../ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs | 13 +++++++++++++ .../invalid-sugg-for-unused-fn-arg-147303.stderr | 14 ++++++++++++++ .../non-snake-case/lint-uppercase-variables.stderr | 11 +---------- 4 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs create mode 100644 tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 1b2ffb5b3db27..b628ff8a12dfd 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1691,7 +1691,10 @@ impl<'tcx> Liveness<'_, 'tcx> { if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; let mut typo = None; - for (hir_id, _, span) in &hir_ids_and_spans { + let filtered_hir_ids_and_spans = hir_ids_and_spans.iter().filter(|(hir_id, ..)| { + !matches!(self.ir.tcx.parent_hir_node(*hir_id), hir::Node::Param(_)) + }); + for (hir_id, _, span) in filtered_hir_ids_and_spans.clone() { let ty = self.typeck_results.node_type(*hir_id); if let ty::Adt(adt, _) = ty.peel_refs().kind() { let name = Symbol::intern(&name); @@ -1717,7 +1720,7 @@ impl<'tcx> Liveness<'_, 'tcx> { } } if typo.is_none() { - for (hir_id, _, span) in &hir_ids_and_spans { + for (hir_id, _, span) in filtered_hir_ids_and_spans { let ty = self.typeck_results.node_type(*hir_id); // Look for consts of the same type with similar names as well, not just unit // structs and variants. diff --git a/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs new file mode 100644 index 0000000000000..137ba8dc103e4 --- /dev/null +++ b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs @@ -0,0 +1,13 @@ +// Regression test for . + +#![deny(unused_assignments, unused_variables)] + +mod m1 { + const _MAX_FMTVER_X1X_EVENTNUM: i32 = 0; +} + +mod m2 { + fn fun(rough: i32) {} //~ERROR unused variable +} + +fn main() {} diff --git a/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr new file mode 100644 index 0000000000000..30e4a758c13ce --- /dev/null +++ b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr @@ -0,0 +1,14 @@ +error: unused variable: `rough` + --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:10:12 + | +LL | fn fun(rough: i32) {} + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_rough` + | +note: the lint level is defined here + --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:3:29 + | +LL | #![deny(unused_assignments, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index e5f2e65fd91d3..5dec20b2ac7ea 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -58,16 +58,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | fn in_param(_Foo: foo::Foo) {} - | + -help: you might have meant to pattern match on the similarly named variant `Foo` - | -LL | fn in_param(foo::Foo::Foo: foo::Foo) {} - | ++++++++++ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` error: structure field `X` should have a snake case name --> $DIR/lint-uppercase-variables.rs:10:5 From ba42380142e73765eab501b3ebc2b3e05ee6b0c0 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 4 Oct 2025 17:16:00 +0800 Subject: [PATCH 1230/1900] Implement non-poisoning `Mutex::with_mut`, `RwLock::with` and `RwLock::with_mut` ACP: https://github.com/rust-lang/libs-team/issues/497. --- library/std/src/sync/nonpoison/mutex.rs | 34 +++++++++++++ library/std/src/sync/nonpoison/rwlock.rs | 62 ++++++++++++++++++++++++ library/std/tests/sync/mutex.rs | 14 ++++++ library/std/tests/sync/rwlock.rs | 22 +++++++++ 4 files changed, 132 insertions(+) diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index eeecf5d710767..ed3f8cfed821a 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -376,6 +376,40 @@ impl Mutex { pub const fn data_ptr(&self) -> *mut T { self.data.get() } + + /// Acquires the mutex and provides mutable access to the underlying data by passing + /// a mutable reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a mutable reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(2); + /// + /// let result = mutex.with_mut(|data| { + /// *data += 3; + /// + /// *data + 5 + /// }); + /// + /// assert_eq!(*mutex.lock(), 5); + /// assert_eq!(result, 10); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn with_mut(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + f(&mut self.lock()) + } } #[unstable(feature = "nonpoison_mutex", issue = "134645")] diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index b2f26edc083e9..568c7f3868470 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -498,6 +498,68 @@ impl RwLock { pub const fn data_ptr(&self) -> *mut T { self.data.get() } + + /// Locks this `RwLock` with shared read access to the underlying data by passing + /// a reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// let result = rwlock.with(|data| *data + 3); + /// + /// assert_eq!(result, 5); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn with(&self, f: F) -> R + where + F: FnOnce(&T) -> R, + { + f(&self.read()) + } + + /// Locks this `RwLock` with exclusive write access to the underlying data by passing + /// a mutable reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a mutable reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// + /// let result = rwlock.with_mut(|data| { + /// *data += 3; + /// + /// *data + 5 + /// }); + /// + /// assert_eq!(*rwlock.read(), 5); + /// assert_eq!(result, 10); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn with_mut(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + f(&mut self.write()) + } } #[unstable(feature = "nonpoison_rwlock", issue = "134645")] diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 2445764001b8b..ff6aef717936f 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -549,3 +549,17 @@ fn panic_while_mapping_unlocked_poison() { drop(lock); } + +#[test] +fn test_mutex_with_mut() { + let mutex = std::sync::nonpoison::Mutex::new(2); + + let result = mutex.with_mut(|value| { + *value += 3; + + *value + 5 + }); + + assert_eq!(*mutex.lock(), 5); + assert_eq!(result, 10); +} diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 65d8bac719456..392c45c8ba05d 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -861,3 +861,25 @@ fn panic_while_mapping_write_unlocked_poison() { drop(lock); } + +#[test] +fn test_rwlock_with() { + let rwlock = std::sync::nonpoison::RwLock::new(2); + let result = rwlock.with(|value| *value + 3); + + assert_eq!(result, 5); +} + +#[test] +fn test_rwlock_with_mut() { + let rwlock = std::sync::nonpoison::RwLock::new(2); + + let result = rwlock.with_mut(|value| { + *value += 3; + + *value + 5 + }); + + assert_eq!(*rwlock.read(), 5); + assert_eq!(result, 10); +} From 5c95f8bea6ea37d750910e5b0ac7ae049579e437 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 2 Oct 2025 10:15:00 +0200 Subject: [PATCH 1231/1900] Add failing regression test for #[link="dl"] --- .../ui/attributes/link-dl.default_fcw.stderr | 26 +++++++++++++++++++ tests/ui/attributes/link-dl.rs | 19 ++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/ui/attributes/link-dl.default_fcw.stderr create mode 100644 tests/ui/attributes/link-dl.rs diff --git a/tests/ui/attributes/link-dl.default_fcw.stderr b/tests/ui/attributes/link-dl.default_fcw.stderr new file mode 100644 index 0000000000000..6bff216ad03f4 --- /dev/null +++ b/tests/ui/attributes/link-dl.default_fcw.stderr @@ -0,0 +1,26 @@ +error[E0539]: malformed `link` attribute input + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[link="dl"] +LL + #[link(name = "...")] + | +LL - #[link="dl"] +LL + #[link(name = "...", import_name_type = "decorated|noprefix|undecorated")] + | +LL - #[link="dl"] +LL + #[link(name = "...", kind = "dylib|static|...")] + | +LL - #[link="dl"] +LL + #[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")] + | + = and 1 other candidate + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/link-dl.rs b/tests/ui/attributes/link-dl.rs new file mode 100644 index 0000000000000..0785c83cb442f --- /dev/null +++ b/tests/ui/attributes/link-dl.rs @@ -0,0 +1,19 @@ +// Regression test for an issue discovered in https://github.com/rust-lang/rust/pull/143193/files and rediscovered in https://github.com/rust-lang/rust/issues/147254#event-20049906781 +// Malformed #[link] attribute was supposed to be deny-by-default report-in-deps FCW, +// but accidentally was landed as a hard error. +// +// revision `default_fcw` tests that with `ill_formed_attribute_input` (the default) denied, +// the attribute produces an FCW +// revision `allowed` tests that with `ill_formed_attribute_input` allowed the test passes + +//@ revisions: default_fcw allowed +//@[allowed] check-pass + +#[cfg_attr(allowed, allow(ill_formed_attribute_input))] + +#[link="dl"] +//[default_fcw]~^ ERROR valid forms for the attribute are +//[default_fcw]~| WARN previously accepted +extern "C" { } + +fn main() {} From 1c85a1dc2eaa2cbc55b5d0a6bbde1ca73706385d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 2 Oct 2025 10:20:08 +0200 Subject: [PATCH 1232/1900] Make `#[link="dl"]` a warning rather than an error --- .../src/attributes/link_attrs.rs | 29 ++++++++++----- compiler/rustc_span/src/symbol.rs | 1 + tests/ui/attributes/link-dl.allowed.stderr | 10 ++++++ .../ui/attributes/link-dl.default_fcw.stderr | 35 +++++++++---------- 4 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 tests/ui/attributes/link-dl.allowed.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index d4942e56f429d..04eaa485f736c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -65,10 +65,22 @@ impl CombineAttributeParser for LinkParser { cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser<'_>, ) -> impl IntoIterator + 'c { - let mut result = None; - let Some(items) = args.list() else { - cx.expected_list(cx.attr_span); - return result; + let items = match args { + ArgParser::List(list) => list, + // This is an edgecase added because making this a hard error would break too many crates + // Specifically `#[link = "dl"]` is accepted with a FCW + // For more information, see https://github.com/rust-lang/rust/pull/143193 + ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => { + let suggestions = >::TEMPLATE + .suggestions(cx.attr_style, "link"); + let span = cx.attr_span; + cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); + return None; + } + _ => { + cx.expected_list(cx.attr_span); + return None; + } }; let sess = cx.sess(); @@ -113,7 +125,7 @@ impl CombineAttributeParser for LinkParser { } }; if !cont { - return result; + return None; } } @@ -202,7 +214,7 @@ impl CombineAttributeParser for LinkParser { } let Some((name, name_span)) = name else { cx.emit_err(LinkRequiresName { span: cx.attr_span }); - return result; + return None; }; // Do this outside of the loop so that `import_name_type` can be specified before `kind`. @@ -218,15 +230,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(RawDylibNoNul { span: name_span }); } - result = Some(LinkEntry { + Some(LinkEntry { span: cx.attr_span, kind: kind.unwrap_or(NativeLibKind::Unspecified), name, cfg, verbatim, import_name_type, - }); - result + }) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b4..20cdd79c34707 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -873,6 +873,7 @@ symbols! { div, div_assign, diverging_block_default, + dl, do_not_recommend, doc, doc_alias, diff --git a/tests/ui/attributes/link-dl.allowed.stderr b/tests/ui/attributes/link-dl.allowed.stderr new file mode 100644 index 0000000000000..e0070d970595f --- /dev/null +++ b/tests/ui/attributes/link-dl.allowed.stderr @@ -0,0 +1,10 @@ +Future incompatibility report: Future breakage diagnostic: +warning: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + diff --git a/tests/ui/attributes/link-dl.default_fcw.stderr b/tests/ui/attributes/link-dl.default_fcw.stderr index 6bff216ad03f4..249895fd17d00 100644 --- a/tests/ui/attributes/link-dl.default_fcw.stderr +++ b/tests/ui/attributes/link-dl.default_fcw.stderr @@ -1,26 +1,23 @@ -error[E0539]: malformed `link` attribute input +error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` --> $DIR/link-dl.rs:14:1 | LL | #[link="dl"] - | ^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^^ | - = note: for more information, visit -help: try changing it to one of the following valid forms of the attribute - | -LL - #[link="dl"] -LL + #[link(name = "...")] - | -LL - #[link="dl"] -LL + #[link(name = "...", import_name_type = "decorated|noprefix|undecorated")] - | -LL - #[link="dl"] -LL + #[link(name = "...", kind = "dylib|static|...")] - | -LL - #[link="dl"] -LL + #[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")] - | - = and 1 other candidate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0539`. +Future incompatibility report: Future breakage diagnostic: +error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default + From 19331daca8c36841acc42656338c35b8abcd1686 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 7 Aug 2025 19:33:56 +0800 Subject: [PATCH 1233/1900] Add test sugg-swap-equality-in-macro Signed-off-by: xizheyin --- .../auxiliary/extern-macro-issue-139050.rs | 15 ++++++ ...ugg-swap-equality-in-macro-issue-139050.rs | 34 ++++++++++++++ ...swap-equality-in-macro-issue-139050.stderr | 46 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 tests/ui/typeck/auxiliary/extern-macro-issue-139050.rs create mode 100644 tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.rs create mode 100644 tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr diff --git a/tests/ui/typeck/auxiliary/extern-macro-issue-139050.rs b/tests/ui/typeck/auxiliary/extern-macro-issue-139050.rs new file mode 100644 index 0000000000000..9e20c86adabce --- /dev/null +++ b/tests/ui/typeck/auxiliary/extern-macro-issue-139050.rs @@ -0,0 +1,15 @@ +#[macro_export] +macro_rules! eq { + (assert $a:expr, $b:expr) => { + match (&$a, &$b) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + panic!( + "assertion failed: `(left == right)`\n left: `{:?}`,\n right: `{:?}`", + left_val, right_val + ); + } + } + } + }; +} diff --git a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.rs b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.rs new file mode 100644 index 0000000000000..5aa2bd7fddeb1 --- /dev/null +++ b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.rs @@ -0,0 +1,34 @@ +// if we use lhs == rhs in a macro, we should not suggest to swap the equality +// because the origin span of lhs and rhs can not be found. See issue #139050 + +//@ aux-build:extern-macro-issue-139050.rs +//@ aux-crate:ext=extern-macro-issue-139050.rs + +extern crate ext; + +use std::fmt::Debug; + +macro_rules! eq_local { + (assert $a:expr, $b:expr) => { + match (&$a, &$b) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + //~^ ERROR mismatched types [E0308] + panic!( + "assertion failed: `(left == right)`\n left: `{:?}`,\n right: `{:?}`", + left_val, right_val + ); + } + } + } + }; +} + +pub fn foo(mut iter: I, value: &I::Item) +where + Item: Eq + Debug, //~ ERROR cannot find type `Item` in this scope [E0412] +{ + ext::eq!(assert iter.next(), Some(value)); //~ ERROR mismatched types [E0308] + eq_local!(assert iter.next(), Some(value)); +} +fn main() {} diff --git a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr new file mode 100644 index 0000000000000..01fc980893bed --- /dev/null +++ b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr @@ -0,0 +1,46 @@ +error[E0412]: cannot find type `Item` in this scope + --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:29:5 + | +LL | Item: Eq + Debug, + | ^^^^ not found in this scope + +error[E0308]: mismatched types + --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:31:5 + | +LL | ext::eq!(assert iter.next(), Some(value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Option<::Item>`, found `Option<&::Item>` + | + = note: expected enum `Option<_>` + found enum `Option<&_>` + = note: `Option<&::Item>` implements `PartialEq::Item>>` + = note: this error originates in the macro `ext::eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider swapping the equality + --> $DIR/auxiliary/extern-macro-issue-139050.rs:6:22 + | +LL - if !(*left_val == *right_val) { +LL + if !(*right_val == *left_val) { + | + +error[E0308]: mismatched types + --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:15:35 + | +LL | if !(*left_val == *right_val) { + | ^^^^^^^^^^ expected `Option<::Item>`, found `Option<&::Item>` +... +LL | eq_local!(assert iter.next(), Some(value)); + | ------------------------------------------ in this macro invocation + | + = note: expected enum `Option<_>` + found enum `Option<&_>` + = note: `Option<&::Item>` implements `PartialEq::Item>>` + = note: this error originates in the macro `eq_local` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider swapping the equality + | +LL - if !(*left_val == *right_val) { +LL + if !(*right_val == *left_val) { + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0412. +For more information about an error, try `rustc --explain E0308`. From 94d66016e4c07a08bc2a04a3fd70a8e82a1ff53b Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 7 Aug 2025 19:46:24 +0800 Subject: [PATCH 1234/1900] Suppress suggest swapping the equality in extern macro Signed-off-by: xizheyin --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 6 +++++- .../typeck/sugg-swap-equality-in-macro-issue-139050.stderr | 7 ------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index a5c6a7f34ef99..709a8b74d0b7a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -3636,7 +3636,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .must_apply_modulo_regions() { let sm = self.tcx.sess.source_map(); - if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) + // If the span of rhs_expr or lhs_expr is in an external macro, + // we just suppress the suggestion. See issue #139050 + if !rhs_expr.span.in_external_macro(sm) + && !lhs_expr.span.in_external_macro(sm) + && let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span) { err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); diff --git a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr index 01fc980893bed..96a3bc65dfdc9 100644 --- a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr +++ b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr @@ -12,14 +12,7 @@ LL | ext::eq!(assert iter.next(), Some(value)); | = note: expected enum `Option<_>` found enum `Option<&_>` - = note: `Option<&::Item>` implements `PartialEq::Item>>` = note: this error originates in the macro `ext::eq` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider swapping the equality - --> $DIR/auxiliary/extern-macro-issue-139050.rs:6:22 - | -LL - if !(*left_val == *right_val) { -LL + if !(*right_val == *left_val) { - | error[E0308]: mismatched types --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:15:35 From 8607cfedc49e402d4301ce1f0f4721045f47203e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 4 Oct 2025 19:32:26 +0800 Subject: [PATCH 1235/1900] minor: Fix using `make::ty` for extract_type_alias --- .../ide-assists/src/handlers/extract_type_alias.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 7f93506685e18..59522458af9e6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -61,11 +61,13 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> generics.map(|it| make::generic_param_list(it.into_iter().cloned())); // Replace original type with the alias - let ty_args = generic_params - .as_ref() - .map_or(String::new(), |it| it.to_generic_args().to_string()); - // FIXME: replace with a `ast::make` constructor - let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update(); + let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); + let new_ty = if let Some(ty_args) = ty_args { + make::generic_ty_path_segment(make::name_ref("Type"), ty_args) + } else { + make::path_segment(make::name_ref("Type")) + } + .clone_for_update(); edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias From 24befed7279ae8be1c52e698172c1366e41e06b1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 3 Oct 2025 22:56:05 +0200 Subject: [PATCH 1236/1900] clean-up - inline `array_span_lint` - use `match` instead of `if-let` - give a more descriptive help message --- clippy_lints/src/zero_repeat_side_effects.rs | 104 ++++++------------- tests/ui/zero_repeat_side_effects.fixed | 4 +- tests/ui/zero_repeat_side_effects.rs | 4 +- tests/ui/zero_repeat_side_effects.stderr | 89 ++++++++++++---- 4 files changed, 105 insertions(+), 96 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index 30fdf22fdbb09..e51c4a6898164 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; @@ -7,9 +7,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -85,77 +83,39 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); - if let Node::LetStmt(l) = parent_hir_node { - array_span_lint( - cx, + let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let vec = if is_vec { "vec!" } else { "" }; + + let (span, sugg) = match parent_hir_node { + Node::LetStmt(l) => ( l.span, - inner_expr.span, - l.pat.span, - Some(return_type), - is_vec, - false, - ); - } else if let Node::Expr(x) = parent_hir_node - && let ExprKind::Assign(l, _, _) = x.kind - { - array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true); - } else { - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr.span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", format!( - "{{ {}; {}[] as {return_type} }}", - snippet(cx, inner_expr.span.source_callsite(), ".."), - if is_vec { "vec!" } else { "" }, + "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), - Applicability::Unspecified, - ); - } - } -} - -fn array_span_lint( - cx: &LateContext<'_>, - expr_span: Span, - func_call_span: Span, - variable_name_span: Span, - expr_ty: Option>, - is_vec: bool, - is_assign: bool, -) { - let has_ty = expr_ty.is_some(); - - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr_span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", - format!( - "{}; {}{}{} = {}[]{}{}", - snippet(cx, func_call_span.source_callsite(), ".."), - if has_ty && !is_assign { "let " } else { "" }, - snippet(cx, variable_name_span.source_callsite(), ".."), - if let Some(ty) = expr_ty - && !is_assign - { - format!(": {ty}") - } else { - String::new() - }, - if is_vec { "vec!" } else { "" }, - if let Some(ty) = expr_ty - && is_assign - { - format!(" as {ty}") - } else { - String::new() + ), + Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( + x.span, + format!( + "{inner_expr}; {var_name} = {vec}[] as {return_type}", + var_name = snippet(cx, l.span.source_callsite(), "..") + ), + ), + _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + }; + span_lint_and_then( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + |diag| { + diag.span_suggestion_verbose( + span.source_callsite(), + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); }, - if is_assign { "" } else { ";" } - ), - Applicability::Unspecified, - ); + ); + } } diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index fb9d7880a4a7f..5184249e942d9 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 8b22ff840244e..9c6e758f2e28c 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 2dba52e2112ec..dc99110932d89 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,59 +1,112 @@ error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:18:5 + --> tests/ui/zero_repeat_side_effects.rs:16:5 | LL | let a = [f(); 0]; - | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL - let a = [f(); 0]; +LL + f(); let a: [i32; 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:21:5 + --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - b = [f(); 0]; +LL + f(); b = [] as [i32; 0]; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:26:5 + --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec = vec![];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let c = vec![f(); 0]; +LL + f(); let c: std::vec::Vec = vec![]; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:29:5 + --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec` + | ^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - d = vec![f(); 0]; +LL + f(); d = vec![] as std::vec::Vec; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:33:5 + --> tests/ui/zero_repeat_side_effects.rs:31:5 | LL | let e = [println!("side effect"); 0]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let e = [println!("side effect"); 0]; +LL + println!("side effect"); let e: [(); 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:37:5 + --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let g = [{ f() }; 0]; +LL + { f() }; let g: [i32; 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:41:10 + --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - drop(vec![f(); 0]); +LL + drop({ f(); vec![] as std::vec::Vec }); + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:45:5 + --> tests/ui/zero_repeat_side_effects.rs:43:5 | LL | vec![f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - vec![f(); 0]; +LL + { f(); vec![] as std::vec::Vec }; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:47:5 + --> tests/ui/zero_repeat_side_effects.rs:45:5 | LL | [f(); 0]; - | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + | ^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - [f(); 0]; +LL + { f(); [] as [i32; 0] }; + | error: aborting due to 9 previous errors From adff9baeb3d16c5d9de8146a6d67f3b276429762 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 00:10:12 +0200 Subject: [PATCH 1237/1900] fix(zero_repeat_side_effects): better identify exprs with side effects --- clippy_lints/src/zero_repeat_side_effects.rs | 15 ++----- tests/ui/zero_repeat_side_effects.fixed | 24 +++++++++++ tests/ui/zero_repeat_side_effects.rs | 24 +++++++++++ tests/ui/zero_repeat_side_effects.stderr | 44 +++++++++++++++----- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index e51c4a6898164..cd6c11b512742 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -11,7 +10,7 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for array or vec initializations which call a function or method, + /// Checks for array or vec initializations which contain an expression with side effects, /// but which have a repeat count of zero. /// /// ### Why is this bad? @@ -71,15 +70,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it - if for_each_expr_without_closures(inner_expr, |x| { - if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { - std::ops::ControlFlow::Break(()) - } else { - std::ops::ControlFlow::Continue(()) - } - }) - .is_some() - { + if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); @@ -107,7 +98,7 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: cx, ZERO_REPEAT_SIDE_EFFECTS, span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "expression with side effects as the initial value in a zero-sized array initializer", |diag| { diag.span_suggestion_verbose( span.source_callsite(), diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index 5184249e942d9..e6c451ce7399e 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -77,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + //~^ zero_repeat_side_effects + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 9c6e758f2e28c..f8a497976aa43 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -77,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&[Some(f()); 0]); + //~^ zero_repeat_side_effects + foo(&[Some(Some(S::new())); 0]); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index dc99110932d89..771b71c686ae4 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,4 +1,4 @@ -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:16:5 | LL | let a = [f(); 0]; @@ -12,7 +12,7 @@ LL - let a = [f(); 0]; LL + f(); let a: [i32; 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; @@ -24,7 +24,7 @@ LL - b = [f(); 0]; LL + f(); b = [] as [i32; 0]; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; @@ -36,7 +36,7 @@ LL - let c = vec![f(); 0]; LL + f(); let c: std::vec::Vec = vec![]; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; @@ -48,7 +48,7 @@ LL - d = vec![f(); 0]; LL + f(); d = vec![] as std::vec::Vec; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:31:5 | LL | let e = [println!("side effect"); 0]; @@ -60,7 +60,7 @@ LL - let e = [println!("side effect"); 0]; LL + println!("side effect"); let e: [(); 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; @@ -72,7 +72,7 @@ LL - let g = [{ f() }; 0]; LL + { f() }; let g: [i32; 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); @@ -84,7 +84,7 @@ LL - drop(vec![f(); 0]); LL + drop({ f(); vec![] as std::vec::Vec }); | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:43:5 | LL | vec![f(); 0]; @@ -96,7 +96,7 @@ LL - vec![f(); 0]; LL + { f(); vec![] as std::vec::Vec }; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:45:5 | LL | [f(); 0]; @@ -108,5 +108,29 @@ LL - [f(); 0]; LL + { f(); [] as [i32; 0] }; | -error: aborting due to 9 previous errors +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:99:10 + | +LL | foo(&[Some(f()); 0]); + | ^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(f()); 0]); +LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:101:10 + | +LL | foo(&[Some(Some(S::new())); 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(Some(S::new())); 0]); +LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + | + +error: aborting due to 11 previous errors From 434fe184e6f65d993d810714275935dcc1b56eea Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 01:16:59 +0200 Subject: [PATCH 1238/1900] fix(zero_repeat_side_effects): don't suggest unsuggestable types --- clippy_lints/src/zero_repeat_side_effects.rs | 23 +++++++++++++------ ...ro_repeat_side_effects_never_pattern.fixed | 9 ++++++++ .../zero_repeat_side_effects_never_pattern.rs | 9 ++++++++ ...o_repeat_side_effects_never_pattern.stderr | 16 +++++++++++++ .../ui/zero_repeat_side_effects_unfixable.rs | 13 +++++++++++ .../zero_repeat_side_effects_unfixable.stderr | 20 ++++++++++++++++ 6 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.fixed create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.rs create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.stderr create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.rs create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.stderr diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index cd6c11b512742..db54ec023077a 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -6,6 +6,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::IsSuggestable; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -72,6 +73,7 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: // check if expr is a call or has a call inside it if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let inner_expr_ty = cx.typeck_results().expr_ty(inner_expr); let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); @@ -94,18 +96,25 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: ), _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), }; + let span = span.source_callsite(); span_lint_and_then( cx, ZERO_REPEAT_SIDE_EFFECTS, - span.source_callsite(), + span, "expression with side effects as the initial value in a zero-sized array initializer", |diag| { - diag.span_suggestion_verbose( - span.source_callsite(), - "consider performing the side effect separately", - sugg, - Applicability::Unspecified, - ); + if (!inner_expr_ty.is_never() || cx.tcx.features().never_type()) + && return_type.is_suggestable(cx.tcx, true) + { + diag.span_suggestion_verbose( + span, + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); + } else { + diag.help("consider performing the side effect separately"); + } }, ); } diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/tests/ui/zero_repeat_side_effects_never_pattern.fixed new file mode 100644 index 0000000000000..021265dac9844 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + panic!(); let _data: [!; 0] = []; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.rs b/tests/ui/zero_repeat_side_effects_never_pattern.rs new file mode 100644 index 0000000000000..3dc1929bcdc76 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.rs @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/tests/ui/zero_repeat_side_effects_never_pattern.stderr new file mode 100644 index 0000000000000..b3d3d2f88f541 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -0,0 +1,16 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_never_pattern.rs:7:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL - let _data = [panic!(); 0]; +LL + panic!(); let _data: [!; 0] = []; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/zero_repeat_side_effects_unfixable.rs b/tests/ui/zero_repeat_side_effects_unfixable.rs new file mode 100644 index 0000000000000..82f0884056ab0 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::zero_repeat_side_effects)] +#![expect(clippy::diverging_sub_expression)] + +fn issue_14998() { + // unnameable types, don't suggest + let _data = [|| 3i32; 0]; + //~^ zero_repeat_side_effects + + // unnameable type because `never_type` is not enabled, don't suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_unfixable.stderr b/tests/ui/zero_repeat_side_effects_unfixable.stderr new file mode 100644 index 0000000000000..450617f3782c4 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.stderr @@ -0,0 +1,20 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:7:5 + | +LL | let _data = [|| 3i32; 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:11:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + +error: aborting due to 2 previous errors + From 2688f601ddc29b75caa2c48a4badc00d15042d7a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 16:07:06 +0200 Subject: [PATCH 1239/1900] Make `fmt::Write` a diagnostic item --- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/fmt/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b4..083e04730bc37 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -236,6 +236,7 @@ symbols! { File, FileType, FmtArgumentsNew, + FmtWrite, Fn, FnMut, FnOnce, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index fcd2e52101ff0..0f255e57fe585 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -115,6 +115,7 @@ pub struct Error; /// [`std::io::Write`]: ../../std/io/trait.Write.html /// [flushable]: ../../std/io/trait.Write.html#tymethod.flush #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "FmtWrite"] pub trait Write { /// Writes a string slice into this writer, returning whether the write /// succeeded. From b4578775b471b723b2210a838d6a8521f45de1db Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 4 Oct 2025 10:22:41 -0400 Subject: [PATCH 1240/1900] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 2394ea6cea8b2..801d9b4981dd0 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 2394ea6cea8b26d717aa67eb1663a2dbf2d26078 +Subproject commit 801d9b4981dd07e3aecdca1ab86834c13615737e From 3d351c839dcb045adcc91372f78f0f0cbaf81033 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 09:45:53 -0400 Subject: [PATCH 1241/1900] Const eval changes: * Remove `CoreConstant`. * Treat most constants from core as though they were inlined. * Don't evaluate `is_empty` for named constants. --- clippy_lints/src/enum_clike.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/manual_float_methods.rs | 4 +- clippy_lints/src/matches/overlapping_arms.rs | 6 +- .../src/methods/unnecessary_min_or_max.rs | 6 +- clippy_lints/src/minmax.rs | 4 +- .../src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/float_cmp.rs | 2 +- clippy_lints/src/ranges.rs | 6 +- clippy_utils/src/consts.rs | 485 ++++++++++++------ clippy_utils/src/lib.rs | 10 +- clippy_utils/src/paths.rs | 18 +- clippy_utils/src/sym.rs | 1 + tests/ui/const_comparisons.rs | 14 +- tests/ui/const_comparisons.stderr | 62 +-- tests/ui/const_is_empty.rs | 50 +- tests/ui/const_is_empty.stderr | 108 +--- 17 files changed, 395 insertions(+), 393 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index c828fc57f7601..1a56c8f810ee7 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); - let constant = cx - .tcx - .const_eval_poly(def_id.to_def_id()) - .ok() - .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { + let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok(); + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) { if let ty::Adt(adt, _) = ty.kind() && adt.is_enum() { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 5052bbb3ca033..3ecc232caff43 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -206,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. #[expect(clippy::cast_possible_truncation)] -fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { +fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { if (-16_777_215.0..16_777_216.0).contains(num) { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index bd2785fea2709..7d6c30aa74ddd 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } -fn is_infinity(constant: &Constant<'_>) -> bool { +fn is_infinity(constant: &Constant) -> bool { match constant { // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::INFINITY, @@ -211,7 +211,7 @@ fn is_infinity(constant: &Constant<'_>) -> bool { } } -fn is_neg_infinity(constant: &Constant<'_>) -> bool { +fn is_neg_infinity(constant: &Constant) -> bool { match constant { // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::NEG_INFINITY, diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index d3136c89178e6..d76218e6305b0 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -35,12 +35,12 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) let lhs_const = if let Some(lhs) = lhs { ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { - mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)? + Constant::new_numeric_min(cx.tcx, ty)? }; let rhs_const = if let Some(rhs) = rhs { ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { - mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)? + Constant::new_numeric_max(cx.tcx, ty)? }; let lhs_val = lhs_const.int_value(cx.tcx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?; diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 130e4970a7163..bf91a469e7f01 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; @@ -26,8 +26,8 @@ pub(super) fn check<'tcx>( && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { let ctxt = expr.span.ctxt(); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv, ctxt) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg, ctxt) + if let Some(left) = ecx.eval_local(recv, ctxt) + && let Some(right) = ecx.eval_local(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 8f76e6c9048e1..35b6af0c20f7f 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -61,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -98,7 +98,7 @@ fn fetch_const<'a, 'tcx>( receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, -) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +) -> Option<(MinMax, Constant, &'a Expr<'a>)> { let mut args = receiver.into_iter().chain(args); let first_arg = args.next()?; let second_arg = args.next()?; diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 10455d3b93a00..56001a185771a 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -22,7 +22,7 @@ fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { +) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 03b2cf055d919..eb2353cfd90bf 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -85,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static } } -fn is_allowed(val: &Constant<'_>) -> bool { +fn is_allowed(val: &Constant) -> bool { match val { // FIXME(f16_f128): add when equality check is available on all platforms &Constant::F32(f) => f == 0.0 || f.is_infinite(), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 03d00ba849f3c..8a216afb82c9a 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -299,8 +299,8 @@ fn check_possible_range_contains( } } -struct RangeBounds<'a, 'tcx> { - val: Constant<'tcx>, +struct RangeBounds<'a> { + val: Constant, expr: &'a Expr<'a>, id: HirId, name_span: Span, @@ -312,7 +312,7 @@ struct RangeBounds<'a, 'tcx> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 29f4988b4211b..f5f3786164608 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,25 +5,21 @@ #![allow(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, sext, unsext}; +use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{ - BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, -}; +use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp}; use rustc_lexer::{FrontmatterAllowed, tokenize}; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; -use rustc_span::{SyntaxContext, sym}; +use rustc_span::{Symbol, SyntaxContext}; use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -31,8 +27,8 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] -pub enum Constant<'tcx> { - Adt(mir::Const<'tcx>), +pub enum Constant { + Adt(ConstValue), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -54,15 +50,15 @@ pub enum Constant<'tcx> { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec>), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box>, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec>), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box>), + Ref(Box), /// A literal with syntax error. Err, } @@ -124,7 +120,7 @@ impl IntTypeBounds for IntTy { } } -impl PartialEq for Constant<'_> { +impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Str(ls), Self::Str(rs)) => ls == rs, @@ -135,13 +131,13 @@ impl PartialEq for Constant<'_> { // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have // `Fw32 == Fw64`, so don’t compare them. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have // `Fw32 == Fw64`, so don’t compare them. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, @@ -153,7 +149,7 @@ impl PartialEq for Constant<'_> { } } -impl Hash for Constant<'_> { +impl Hash for Constant { fn hash(&self, state: &mut H) where H: Hasher, @@ -209,7 +205,7 @@ impl Hash for Constant<'_> { } } -impl Constant<'_> { +impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), @@ -297,10 +293,129 @@ impl Constant<'_> { let f: Quad = s.parse().unwrap(); Self::F128(f.to_bits()) } + + pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(_) => Some(Self::Int(0)), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MIN)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::NEG_INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::NEG_INFINITY)), + _ => None, + } + } + + pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return None, + })), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MAX)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::INFINITY)), + _ => None, + } + } + + pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(_)) => x == 0, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MIN, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::NEG_INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::NEG_INFINITY, + _ => false, + } + } + + pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return false, + }; + x == limit + }, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MAX, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_pos_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::INFINITY, + Constant::F64(x) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_neg_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::NEG_INFINITY, + Constant::F64(x) => x == f64::NEG_INFINITY, + _ => false, + } + } } /// Parses a `LitKind` to a `Constant`. -pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constant<'tcx> { +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -331,10 +446,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan pub enum ConstantSource { /// The value is determined solely from the expression. Local, - /// The value is dependent on a defined constant. - Constant, - /// The value is dependent on a constant defined in `core` crate. - CoreConstant, + /// The value is dependent on another definition that may change independently from the local + /// expression. + NonLocal, } impl ConstantSource { pub fn is_local(self) -> bool { @@ -416,19 +530,24 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant, ConstantSource)> { self.source.set(ConstantSource::Local); self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } /// Attempts to evaluate the expression. - pub fn eval(&self, e: &Expr<'_>) -> Option> { + pub fn eval(&self, e: &Expr<'_>) -> Option { self.expr(e) } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option> { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, @@ -436,6 +555,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// Attempts to evaluate the expression as an integer without accessing other items. + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), @@ -443,7 +567,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option { match &pat_expr.kind { PatExprKind::Lit { lit, negated } => { let ty = self.typeck.node_type_opt(pat_expr.hir_id); @@ -460,35 +584,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } fn check_ctxt(&self, ctxt: SyntaxContext) { - self.source.set(if self.ctxt.get() != ctxt { - ConstantSource::Constant - } else { - self.source.get() - }); + if self.ctxt.get() != ctxt { + self.source.set(ConstantSource::NonLocal); + } } - fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option { + self.fetch_path(qpath, hir_id) + .and_then(|c| mir_to_const(self.tcx, c, self.typeck.node_type(hir_id))) } /// Simple constant folding: Insert an expression, get a constant or none. - fn expr(&self, e: &Expr<'_>) -> Option> { + fn expr(&self, e: &Expr<'_>) -> Option { self.check_ctxt(e.span.ctxt()); match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value), @@ -540,18 +647,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }, ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), - ExprKind::Field(local_expr, ref field) => { - self.check_ctxt(field.span.ctxt()); - let result = self.expr(local_expr); - if let Some(Constant::Adt(constant)) = &self.expr(local_expr) - && let ty::Adt(adt_def, _) = constant.ty().kind() + ExprKind::Field(base, ref field) + if let base_ty = self.typeck.expr_ty(base) + && match self.typeck.expr_adjustments(base) { + [] => true, + [.., a] => a.target == base_ty, + } + && let Some(Constant::Adt(constant)) = self.expr(base) + && let ty::Adt(adt_def, _) = *base_ty.kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) - { - mir_to_const(self.tcx, desired_field) - } else { - result - } + && let Some((desired_field, ty)) = + field_of_struct(adt_def, self.tcx, constant, base_ty, field.name) => + { + self.check_ctxt(field.span.ctxt()); + mir_to_const(self.tcx, desired_field, ty) }, _ => None, } @@ -564,19 +673,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.eval_is_empty(e), - ExprKind::Path(ref qpath) => { - if !self - .typeck - .qpath_res(qpath, e.hir_id) - .opt_def_id() - .is_some_and(DefId::is_local) - { - return None; - } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - mir_is_empty(self_.tcx, result) - }) - }, ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, sym::cfg).is_some() { None @@ -601,7 +697,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } #[expect(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { Bool(b) => Some(Bool(!b)), @@ -617,7 +713,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{F32, F64, Int}; match *o { Int(value) => { @@ -643,48 +739,128 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&self, vec: &[Expr<'_>]) -> Option>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option> { vec.iter().map(|elem| self.expr(elem)).collect::>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option - where - F: FnOnce(&Self, mir::Const<'tcx>) -> Option, - { - let res = self.typeck.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - // Check if this constant is based on `cfg!(..)`, - // which is NOT constant for our purposes. - if let Some(node) = self.tcx.hir_get_if_local(def_id) - && let Node::Item(Item { - kind: ItemKind::Const(.., body_id), - .. - }) = node - && let Node::Expr(Expr { - kind: ExprKind::Lit(_), - span, - .. - }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, sym::cfg).is_some() - { - return None; - } - - let args = self.typeck.node_args(id); - let result = self - .tcx - .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) - .ok() - .map(|val| mir::Const::from_value(val, ty))?; - f(self, result) + #[expect(clippy::too_many_lines)] + fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option { + // Resolve the path to a constant and check if that constant is known to + // not change based on the target. + // + // This should be replaced with an attribute at some point. + let did = match *qpath { + QPath::Resolved(None, path) + if path.span.ctxt() == self.ctxt.get() + && path.segments.iter().all(|s| self.ctxt.get() == s.ident.span.ctxt()) + && let Res::Def(DefKind::Const, did) = path.res + && (matches!( + self.tcx.get_diagnostic_name(did), + Some( + sym::f32_legacy_const_digits + | sym::f32_legacy_const_epsilon + | sym::f32_legacy_const_infinity + | sym::f32_legacy_const_mantissa_dig + | sym::f32_legacy_const_max + | sym::f32_legacy_const_max_10_exp + | sym::f32_legacy_const_max_exp + | sym::f32_legacy_const_min + | sym::f32_legacy_const_min_10_exp + | sym::f32_legacy_const_min_exp + | sym::f32_legacy_const_min_positive + | sym::f32_legacy_const_nan + | sym::f32_legacy_const_neg_infinity + | sym::f32_legacy_const_radix + | sym::f64_legacy_const_digits + | sym::f64_legacy_const_epsilon + | sym::f64_legacy_const_infinity + | sym::f64_legacy_const_mantissa_dig + | sym::f64_legacy_const_max + | sym::f64_legacy_const_max_10_exp + | sym::f64_legacy_const_max_exp + | sym::f64_legacy_const_min + | sym::f64_legacy_const_min_10_exp + | sym::f64_legacy_const_min_exp + | sym::f64_legacy_const_min_positive + | sym::f64_legacy_const_nan + | sym::f64_legacy_const_neg_infinity + | sym::f64_legacy_const_radix + | sym::u8_legacy_const_min + | sym::u16_legacy_const_min + | sym::u32_legacy_const_min + | sym::u64_legacy_const_min + | sym::u128_legacy_const_min + | sym::usize_legacy_const_min + | sym::u8_legacy_const_max + | sym::u16_legacy_const_max + | sym::u32_legacy_const_max + | sym::u64_legacy_const_max + | sym::u128_legacy_const_max + | sym::i8_legacy_const_min + | sym::i16_legacy_const_min + | sym::i32_legacy_const_min + | sym::i64_legacy_const_min + | sym::i128_legacy_const_min + | sym::i8_legacy_const_max + | sym::i16_legacy_const_max + | sym::i32_legacy_const_max + | sym::i64_legacy_const_max + | sym::i128_legacy_const_max + ) + ) || self.tcx.opt_parent(did).is_some_and(|parent| { + paths::F16_CONSTS.matches(&self.tcx, parent) + || paths::F32_CONSTS.matches(&self.tcx, parent) + || paths::F64_CONSTS.matches(&self.tcx, parent) + || paths::F128_CONSTS.matches(&self.tcx, parent) + })) => + { + did }, - _ => None, - } + QPath::TypeRelative(ty, const_name) + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let [.., ty_name] = ty_path.segments + && (matches!( + ty_name.ident.name, + sym::i8 + | sym::i16 + | sym::i32 + | sym::i64 + | sym::i128 + | sym::u8 + | sym::u16 + | sym::u32 + | sym::u64 + | sym::u128 + | sym::f32 + | sym::f64 + | sym::char + ) || (ty_name.ident.name == sym::usize && const_name.ident.name == sym::MIN)) + && const_name.ident.span.ctxt() == self.ctxt.get() + && ty.span.ctxt() == self.ctxt.get() + && ty_name.ident.span.ctxt() == self.ctxt.get() + && matches!(ty_path.res, Res::PrimTy(_)) + && let Some((DefKind::AssocConst, did)) = self.typeck.type_dependent_def(id) => + { + did + }, + _ if let Res::Def(DefKind::Const | DefKind::AssocConst, did) = self.typeck.qpath_res(qpath, id) => { + self.source.set(ConstantSource::NonLocal); + did + }, + _ => return None, + }; + + self.tcx + .const_eval_resolve( + self.typing_env, + mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), + qpath.span(), + ) + .ok() } - fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { let lhs = self.expr(lhs); let index = self.expr(index); @@ -714,7 +890,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// A block can only yield a constant if it has exactly one constant expression. - fn block(&self, block: &Block<'_>) -> Option> { + fn block(&self, block: &Block<'_>) -> Option { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -733,11 +909,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } else { // Unable to access the source. Assume a non-local dependency. - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } @@ -747,7 +923,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -759,7 +935,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -795,6 +971,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitAnd => Some(zext(l & r)), + // FIXME: f32/f64 currently consider `0.0` and `-0.0` as different. BinOpKind::Eq => Some(Constant::Bool(l == r)), BinOpKind::Ne => Some(Constant::Bool(l != r)), BinOpKind::Lt => Some(Constant::Bool(l < r)), @@ -873,14 +1050,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } -pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option> { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option { + match (val, ty.kind()) { + (_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)), + (ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() { ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), @@ -894,7 +1067,6 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); let len = len.try_to_target_usize(tcx)?; @@ -919,62 +1091,30 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option } } -fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { - ty::Str | ty::Slice(_) => { - if let ConstValue::Indirect { alloc_id, offset } = val { - // Get the length from the slice, using the same formula as - // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. - let a = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = tcx.data_layout.pointer_size(); - if a.size() < offset + 2 * ptr_size { - // (partially) dangling reference - return None; - } - let len = a - .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) - .ok()? - .to_target_usize(&tcx) - .discard_err()?; - Some(len == 0) - } else { - None - } - }, - ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), - _ => None, - }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), - (ConstValue::ZeroSized, _) => Some(true), - _ => None, - } -} - fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, tcx: TyCtxt<'tcx>, - result: mir::Const<'tcx>, - field: &Ident, -) -> Option> { - if let mir::Const::Val(result, ty) = result - && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) + value: ConstValue, + ty: Ty<'tcx>, + field: Symbol, +) -> Option<(ConstValue, Ty<'tcx>)> { + if let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(value, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) - && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) - && let Some(&(val, ty)) = dc.fields.get(field_idx) + && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field) { - Some(mir::Const::Val(val, ty)) + dc.fields.get(field_idx).copied() } else { None } } /// If `expr` evaluates to an integer constant, return its value. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) @@ -984,6 +1124,11 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) } /// Check if `expr` evaluates to an integer constant of 0. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. #[inline] pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { integer_const(cx, expr, ctxt) == Some(0) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 708491df77079..a2c8ec34c6be2 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -126,7 +126,7 @@ use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1422,11 +1422,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx.tcx, min_const) && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { - start_const == min_const + start_const.is_numeric_min(cx.tcx, bnd_ty) } else { false } @@ -1435,11 +1433,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti RangeLimits::Closed => { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx.tcx, max_const) && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { - end_const == max_const + end_const.is_numeric_max(cx.tcx, bnd_ty) } else { false } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ea8cfc59356a7..5ab8e16d88ed3 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ItemKind, Node, UseKind}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; @@ -74,8 +75,8 @@ impl PathLookup { } /// Returns the list of [`DefId`]s that the path resolves to - pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { - self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + pub fn get<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(tcx.tcx(), self.ns, self.path)) } /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into @@ -90,8 +91,8 @@ impl PathLookup { } /// Checks if the path resolves to the given `def_id` - pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { - self.get(cx).contains(&def_id) + pub fn matches<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, def_id: DefId) -> bool { + self.get(&tcx.tcx()).contains(&def_id) } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it @@ -100,8 +101,8 @@ impl PathLookup { } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` - pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + pub fn matches_ty<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(&tcx.tcx(), adt.did())) } } @@ -126,6 +127,11 @@ path_macros! { macro_path: PathNS::Macro, } +pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); +pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); +pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); +pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); + // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 4ba0e52572ddf..2d0d4a5319f3c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -115,6 +115,7 @@ generate! { collapsible_if, collect, const_ptr, + consts, contains, copied, copy_from, diff --git a/tests/ui/const_comparisons.rs b/tests/ui/const_comparisons.rs index b732d7d142fc5..a2a1f0a7b4c09 100644 --- a/tests/ui/const_comparisons.rs +++ b/tests/ui/const_comparisons.rs @@ -1,9 +1,11 @@ -#![allow(unused)] -#![warn(clippy::impossible_comparisons)] -#![warn(clippy::redundant_comparisons)] -#![allow(clippy::no_effect)] -#![allow(clippy::short_circuit_statement)] -#![allow(clippy::manual_range_contains)] +#![allow( + unused, + clippy::identity_op, + clippy::manual_range_contains, + clippy::no_effect, + clippy::short_circuit_statement +)] +#![warn(clippy::impossible_comparisons, clippy::redundant_comparisons)] const STATUS_BAD_REQUEST: u16 = 400; const STATUS_SERVER_ERROR: u16 = 500; diff --git a/tests/ui/const_comparisons.stderr b/tests/ui/const_comparisons.stderr index 48a2c6e8d4879..1ce62c23ff293 100644 --- a/tests/ui/const_comparisons.stderr +++ b/tests/ui/const_comparisons.stderr @@ -1,5 +1,5 @@ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:45:5 + --> tests/ui/const_comparisons.rs:47:5 | LL | status_code <= 400 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | status_code <= 400 && status_code > 500; = help: to override `-D warnings` add `#[allow(clippy::impossible_comparisons)]` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:48:5 + --> tests/ui/const_comparisons.rs:50:5 | LL | status_code > 500 && status_code < 400; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | status_code > 500 && status_code < 400; = note: since `500` > `400`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:51:5 + --> tests/ui/const_comparisons.rs:53:5 | LL | status_code < 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | status_code < 500 && status_code > 500; = note: `status_code` cannot simultaneously be greater than and less than `500` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:55:5 + --> tests/ui/const_comparisons.rs:57:5 | LL | status_code < { 400 } && status_code > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | status_code < { 400 } && status_code > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:58:5 + --> tests/ui/const_comparisons.rs:60:5 | LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:61:5 + --> tests/ui/const_comparisons.rs:63:5 | LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:64:5 + --> tests/ui/const_comparisons.rs:66:5 | LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:68:5 + --> tests/ui/const_comparisons.rs:70:5 | LL | status < { 400 } && status > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | status < { 400 } && status > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:71:5 + --> tests/ui/const_comparisons.rs:73:5 | LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:74:5 + --> tests/ui/const_comparisons.rs:76:5 | LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:77:5 + --> tests/ui/const_comparisons.rs:79:5 | LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:86:5 + --> tests/ui/const_comparisons.rs:88:5 | LL | 500 >= status_code && 600 < status_code; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 500 >= status_code && 600 < status_code; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:90:5 + --> tests/ui/const_comparisons.rs:92:5 | LL | 500 >= status_code && status_code > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 500 >= status_code && status_code > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:99:5 + --> tests/ui/const_comparisons.rs:101:5 | LL | 500 >= status && 600 < status; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | 500 >= status && 600 < status; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:103:5 + --> tests/ui/const_comparisons.rs:105:5 | LL | 500 >= status && status > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,13 +121,13 @@ LL | 500 >= status && status > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:107:5 + --> tests/ui/const_comparisons.rs:109:5 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:107:23 + --> tests/ui/const_comparisons.rs:109:23 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^ @@ -135,67 +135,67 @@ LL | status_code < 200 && status_code <= 299; = help: to override `-D warnings` add `#[allow(clippy::redundant_comparisons)]` error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:118:5 + --> tests/ui/const_comparisons.rs:120:5 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:118:23 + --> tests/ui/const_comparisons.rs:120:23 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:126:5 + --> tests/ui/const_comparisons.rs:128:5 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:126:23 + --> tests/ui/const_comparisons.rs:128:23 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:131:5 + --> tests/ui/const_comparisons.rs:133:5 | LL | name < "Jennifer" && name > "Shannon"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -203,7 +203,7 @@ LL | name < "Jennifer" && name > "Shannon"; = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:135:5 + --> tests/ui/const_comparisons.rs:137:5 | LL | numbers < [3, 4] && numbers > [5, 6]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,7 +211,7 @@ LL | numbers < [3, 4] && numbers > [5, 6]; = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:139:5 + --> tests/ui/const_comparisons.rs:141:5 | LL | letter < 'b' && letter > 'c'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | letter < 'b' && letter > 'c'; = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:143:5 + --> tests/ui/const_comparisons.rs:145:5 | LL | area < std::f32::consts::E && area > std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 8bb4f0e5d9750..2ad1b5276def8 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -55,53 +55,6 @@ const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; const EMPTY_REF_ARRAY: &[u32; 0] = &[]; const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; -fn test_from_const() { - let _ = EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - //~^ const_is_empty -} - fn main() { let value = "foobar"; let _ = value.is_empty(); @@ -120,7 +73,7 @@ fn main() { fn str_from_arg(var: &str) { var.is_empty(); - // Do not lint, we know nothiny about var + // Do not lint, we know nothing about var } fn update_str() { @@ -200,6 +153,5 @@ fn issue_13106() { const { EMPTY_STR.is_empty(); - //~^ const_is_empty } } diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 2ba189058e832..e1837695bc1c9 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -37,131 +37,35 @@ error: this expression always evaluates to false LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:59:13 - | -LL | let _ = EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:62:13 - | -LL | let _ = NON_EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:65:13 - | -LL | let _ = EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:68:13 - | -LL | let _ = NON_EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:71:13 - | -LL | let _ = EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:74:13 - | -LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:77:13 - | -LL | let _ = EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 - | -LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:83:13 - | -LL | let _ = NON_EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 - | -LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:89:13 - | -LL | let _ = EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 - | -LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:95:13 - | -LL | let _ = EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:98:13 - | -LL | let _ = NON_EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:101:13 - | -LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:107:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:111:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:114:13 + --> tests/ui/const_is_empty.rs:67:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:117:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:171:13 + --> tests/ui/const_is_empty.rs:124:13 | LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 - | -LL | EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 27 previous errors +error: aborting due to 11 previous errors From 47b0f903a3b0e2a863f34a6bd1c33196f851c796 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 24 Aug 2025 10:38:06 -0400 Subject: [PATCH 1242/1900] Use `eval_local` in more places. --- clippy_lints/src/floating_point_arithmetic.rs | 6 +- clippy_lints/src/implicit_saturating_add.rs | 5 +- clippy_lints/src/manual_float_methods.rs | 29 +--- clippy_lints/src/manual_rotate.rs | 2 +- clippy_lints/src/manual_strip.rs | 24 ++- clippy_lints/src/methods/ip_constant.rs | 4 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iter_skip_zero.rs | 16 +- clippy_lints/src/methods/repeat_once.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/minmax.rs | 6 +- clippy_lints/src/operators/duration_subsec.rs | 2 +- .../src/operators/modulo_arithmetic.rs | 6 +- clippy_lints/src/ranges.rs | 2 +- clippy_utils/src/consts.rs | 8 +- tests/ui/duration_subsec.fixed | 3 +- tests/ui/duration_subsec.rs | 1 - tests/ui/duration_subsec.stderr | 8 +- tests/ui/floating_point_log.fixed | 6 +- tests/ui/floating_point_log.rs | 2 - tests/ui/floating_point_log.stderr | 60 +++---- tests/ui/ip_constant.fixed | 45 ++++-- tests/ui/ip_constant.rs | 9 -- tests/ui/ip_constant.stderr | 146 +----------------- tests/ui/manual_float_methods.rs | 15 +- tests/ui/manual_float_methods.stderr | 40 +---- tests/ui/manual_strip.stderr | 3 - tests/ui/repeat_once.fixed | 3 +- tests/ui/repeat_once.rs | 1 - tests/ui/repeat_once.stderr | 14 +- 30 files changed, 132 insertions(+), 340 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3ecc232caff43..407a3f1306739 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -111,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e -fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> { + if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -158,7 +158,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(method) = get_specialized_log_method(cx, &args[0]) { + if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) { span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 0fdbf67973813..4bf3a390b050c 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -117,10 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let ecx = ConstEvalCtxt::new(cx); - if let Some(Constant::Int(c)) = ecx.eval(r) { + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) { return Some((c, op.node, l)); } - if let Some(Constant::Int(c)) = ecx.eval(l) { + if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 7d6c30aa74ddd..60782f445ab91 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; @@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { ) && let [first, second, const_1, const_2] = exprs && let ecx = ConstEvalCtxt::new(cx) - && let Some(const_1) = ecx.eval(const_1) - && let Some(const_2) = ecx.eval(const_2) + && let ctxt = expr.span.ctxt() + && let Some(const_1) = ecx.eval_local(const_1, ctxt) + && let Some(const_2) = ecx.eval_local(const_2, ctxt) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason - && (is_infinity(&const_1) && is_neg_infinity(&const_2) - || is_neg_infinity(&const_1) && is_infinity(&const_2)) + && (const_1.is_pos_infinity() && const_2.is_neg_infinity() + || const_1.is_neg_infinity() && const_2.is_pos_infinity()) && let Some(local_snippet) = first.span.get_source_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { @@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } } - -fn is_infinity(constant: &Constant) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::INFINITY, - Constant::F64(float) => *float == f64::INFINITY, - _ => false, - } -} - -fn is_neg_infinity(constant: &Constant) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::NEG_INFINITY, - Constant::F64(float) => *float == f64::NEG_INFINITY, - _ => false, - } -} diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 06ee00c2cef3c..22e3407303f07 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval(r)?; + let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 07cce4046ca4f..f5d15310879a3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, SyntaxContext, sym}; use std::iter; declare_clippy_lint! { @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then); + let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt()); if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) { let kind_word = match strip_kind { StripKind::Prefix => "prefix", @@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E } // Returns the length of the `expr` if it's a constant string or char. -fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let value = ConstEvalCtxt::new(cx).eval(expr)?; +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } // Tests if `expr` equals the length of the pattern. -fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { +fn eq_pattern_length<'tcx>( + cx: &LateContext<'tcx>, + pattern: &Expr<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> bool { if let ExprKind::Lit(Spanned { node: LitKind::Int(n, _), .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| n == length) + constant_length(cx, pattern, ctxt).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } @@ -215,6 +220,7 @@ fn find_stripping<'tcx>( target: Res, pattern: &'tcx Expr<'_>, expr: &'tcx Expr<'tcx>, + ctxt: SyntaxContext, ) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap) { struct StrippingFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -223,6 +229,7 @@ fn find_stripping<'tcx>( pattern: &'tcx Expr<'tcx>, results: Vec<&'tcx Expr<'tcx>>, bindings: FxHashMap, + ctxt: SyntaxContext, } impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { @@ -236,7 +243,7 @@ fn find_stripping<'tcx>( { match (self.strip_kind, start, end) { (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { self.results.push(ex); return; } @@ -252,7 +259,7 @@ fn find_stripping<'tcx>( && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right) + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) { self.results.push(ex); return; @@ -280,6 +287,7 @@ fn find_stripping<'tcx>( pattern, results: vec![], bindings: FxHashMap::default(), + ctxt, }; walk_expr(&mut finder, expr); (finder.results, finder.bindings) diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index a2ac4e54334ee..bf602811009a2 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -17,10 +17,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args cx.tcx.get_diagnostic_name(func_def_id), Some(sym::Ipv4Addr | sym::Ipv6Addr) ) + && let ecx = ConstEvalCtxt::new(cx) + && let ctxt = expr.span.ctxt() && let Some(args) = args .iter() .map(|arg| { - if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) { + if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ecx.eval_local(arg, ctxt) { u8::try_from(constant).ok() } else { None diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 4bdf589f48762..0f8abd0172423 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 39e440e784f6d..663e34437a307 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -11,13 +11,15 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { - if let Constant::Int(arg) = constant { - Some(arg) - } else { - None - } - }) + && let Some(arg) = ConstEvalCtxt::new(cx) + .eval_local(arg_expr, expr.span.ctxt()) + .and_then(|constant| { + if let Constant::Int(arg) = constant { + Some(arg) + } else { + None + } + }) && arg == 0 && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 7837517ed5d8a..9111604ef53b7 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval_local(repeat_arg, expr.span.ctxt()) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 51dd4ac313a6d..8daa5db887acf 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -304,7 +304,7 @@ fn parse_iter_usage<'tcx>( }; }, (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval_local(idx_expr, ctxt) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 35b6af0c20f7f..f9a7c562c7a59 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -61,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -92,8 +92,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM } } -fn fetch_const<'a, 'tcx>( - cx: &LateContext<'tcx>, +fn fetch_const<'a>( + cx: &LateContext<'_>, ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 6c9be7c5e90b3..d897b0e8dd918 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index b79461663d7bf..ffe91fc2cef6d 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -39,7 +39,9 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && let BinOpKind::Eq | BinOpKind::Ne = op.node { let ecx = ConstEvalCtxt::new(cx); - matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + let ctxt = expr.span.ctxt(); + matches!(ecx.eval_local(lhs, ctxt), Some(Constant::Int(0))) + || matches!(ecx.eval_local(rhs, ctxt), Some(Constant::Int(0))) } else { false } @@ -55,7 +57,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> match ConstEvalCtxt::new(cx).eval(operand)? { Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { - let value = sext(cx.tcx, v, ity); + let value: i128 = sext(cx.tcx, v, ity); Some(OperandInfo { string_representation: Some(value.to_string()), is_negative: value < 0, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 8a216afb82c9a..0b2313cb7eeb9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -312,7 +312,7 @@ struct RangeBounds<'a> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index f5f3786164608..9ba796137cc3a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -128,15 +128,11 @@ impl PartialEq for Constant { (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, diff --git a/tests/ui/duration_subsec.fixed b/tests/ui/duration_subsec.fixed index a8c2f78ca383a..b6b2d156c0e0f 100644 --- a/tests/ui/duration_subsec.fixed +++ b/tests/ui/duration_subsec.fixed @@ -25,8 +25,7 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; - let _ = dur.subsec_micros(); - //~^ duration_subsec + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.rs b/tests/ui/duration_subsec.rs index 582f4717de27f..1061e6003c35e 100644 --- a/tests/ui/duration_subsec.rs +++ b/tests/ui/duration_subsec.rs @@ -26,7 +26,6 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - //~^ duration_subsec // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index 1a41742e1fa6d..27756bc1c20f6 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -25,11 +25,5 @@ error: calling `subsec_micros()` is more concise than this calculation LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: calling `subsec_micros()` is more concise than this calculation - --> tests/ui/duration_subsec.rs:28:13 - | -LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 275c9b4a3ab9a..e831e30a71d81 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -14,10 +14,8 @@ fn check_log_base() { //~^ suboptimal_flops let _ = x.ln(); //~^ suboptimal_flops - let _ = x.log2(); - //~^ suboptimal_flops - let _ = x.ln(); - //~^ suboptimal_flops + let _ = x.log(TWO); + let _ = x.log(E); let _ = (x as f32).log2(); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index a372ccbb9fb03..06cb1c8d96039 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -15,9 +15,7 @@ fn check_log_base() { let _ = x.log(std::f32::consts::E); //~^ suboptimal_flops let _ = x.log(TWO); - //~^ suboptimal_flops let _ = x.log(E); - //~^ suboptimal_flops let _ = (x as f32).log(2f32); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index e93b3af851cbe..3e141de626d94 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -19,44 +19,32 @@ error: logarithm for bases 2, 10 and e can be computed more accurately LL | let _ = x.log(std::f32::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:17:13 - | -LL | let _ = x.log(TWO); - | ^^^^^^^^^^ help: consider using: `x.log2()` - error: logarithm for bases 2, 10 and e can be computed more accurately --> tests/ui/floating_point_log.rs:19:13 | -LL | let _ = x.log(E); - | ^^^^^^^^ help: consider using: `x.ln()` - -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:21:13 - | LL | let _ = (x as f32).log(2f32); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:25:13 + --> tests/ui/floating_point_log.rs:23:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:27:13 + --> tests/ui/floating_point_log.rs:25:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:29:13 + --> tests/ui/floating_point_log.rs:27:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:35:13 + --> tests/ui/floating_point_log.rs:33:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -65,118 +53,118 @@ LL | let _ = (1f32 + 2.).ln(); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:37:13 + --> tests/ui/floating_point_log.rs:35:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:39:13 + --> tests/ui/floating_point_log.rs:37:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:41:13 + --> tests/ui/floating_point_log.rs:39:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:43:13 + --> tests/ui/floating_point_log.rs:41:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:45:13 + --> tests/ui/floating_point_log.rs:43:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:47:13 + --> tests/ui/floating_point_log.rs:45:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:49:13 + --> tests/ui/floating_point_log.rs:47:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:51:13 + --> tests/ui/floating_point_log.rs:49:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:53:13 + --> tests/ui/floating_point_log.rs:51:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:55:13 + --> tests/ui/floating_point_log.rs:53:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:64:13 + --> tests/ui/floating_point_log.rs:62:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:66:13 + --> tests/ui/floating_point_log.rs:64:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:68:13 + --> tests/ui/floating_point_log.rs:66:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:70:13 + --> tests/ui/floating_point_log.rs:68:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:72:13 + --> tests/ui/floating_point_log.rs:70:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:74:13 + --> tests/ui/floating_point_log.rs:72:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:76:13 + --> tests/ui/floating_point_log.rs:74:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:78:13 + --> tests/ui/floating_point_log.rs:76:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:80:13 + --> tests/ui/floating_point_log.rs:78:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index c947968213948..afdf581bacf7a 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -72,33 +72,44 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; - let _ = Ipv4Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); + let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); + let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - - let _ = Ipv6Addr::UNSPECIFIED; + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_1, + ); + + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + ); } fn const_test2() { use std::net::Ipv4Addr; let _ = Ipv4Addr::LOCALHOST; //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); + let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant + let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); + let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 69a5c3b4e9230..04fc2f0f6fdae 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -73,15 +73,11 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -93,7 +89,6 @@ fn const_test1() { ); let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -110,15 +105,11 @@ fn const_test2() { let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); //~^ ip_constant let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - //~^ ip_constant let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - //~^ ip_constant } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 07d912b18a57b..44e3d6448dbd8 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -241,101 +241,7 @@ LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:75:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); -LL + let _ = Ipv4Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:77:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:79:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:83:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_1, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_1, -LL - ); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:95:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_0, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - ); -LL + let _ = Ipv6Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:110:13 + --> tests/ui/ip_constant.rs:105:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -346,53 +252,5 @@ LL - let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); LL + let _ = Ipv4Addr::LOCALHOST; | -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:112:13 - | -LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:114:13 - | -LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:118:13 - | -LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:120:13 - | -LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: aborting due to 30 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index 62cdc1c141d0c..4b496a4932833 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -8,9 +8,6 @@ #[macro_use] extern crate proc_macros; -const INFINITE: f32 = f32::INFINITY; -const NEG_INFINITE: f32 = f32::NEG_INFINITY; - fn fn_test() -> f64 { f64::NEG_INFINITY } @@ -25,10 +22,6 @@ fn main() { //~^ manual_is_infinite if x != f32::INFINITY && x != f32::NEG_INFINITY {} //~^ manual_is_finite - if x == INFINITE || x == NEG_INFINITE {} - //~^ manual_is_infinite - if x != INFINITE && x != NEG_INFINITE {} - //~^ manual_is_finite let x = 1.0f64; if x == f64::INFINITY || x == f64::NEG_INFINITY {} //~^ manual_is_infinite @@ -64,4 +57,12 @@ fn main() { if x == f32::INFINITY || x == f32::NEG_INFINITY {} if x != f32::INFINITY && x != f32::NEG_INFINITY {} } + + { + let x = 1.0f32; + const X: f32 = f32::INFINITY; + const Y: f32 = f32::NEG_INFINITY; + if x == X || x == Y {} + if x != X && x != Y {} + } } diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr index 352c879c87d73..0a27e0eac48b3 100644 --- a/tests/ui/manual_float_methods.stderr +++ b/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:24:8 + --> tests/ui/manual_float_methods.rs:21:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:26:8 + --> tests/ui/manual_float_methods.rs:23:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,41 +32,13 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:28:8 - | -LL | if x == INFINITE || x == NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` - -error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:30:8 - | -LL | if x != INFINITE && x != NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use the dedicated method instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() {} - | -help: this will alter how it handles NaN; if that is a problem, use instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() || x.is_nan() {} - | -help: or, for conciseness - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if !x.is_infinite() {} - | - -error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:33:8 + --> tests/ui/manual_float_methods.rs:26:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:35:8 + --> tests/ui/manual_float_methods.rs:28:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,10 +60,10 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:50:12 + --> tests/ui/manual_float_methods.rs:43:12 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index a323ef700e767..d147cdae1f3b1 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -97,9 +97,6 @@ LL | if s.starts_with(PREFIX) { help: try using the `strip_prefix` method | LL ~ if let Some() = s.strip_prefix(PREFIX) { -LL ~ str::to_string(); -LL | -LL | LL ~ str::to_string(); | diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed index e739e176f0acd..c08d630a32f7f 100644 --- a/tests/ui/repeat_once.fixed +++ b/tests/ui/repeat_once.fixed @@ -10,8 +10,7 @@ fn main() { //~^ repeat_once let b = slice.to_vec(); //~^ repeat_once - let c = "hello".to_string(); - //~^ repeat_once + let c = "hello".repeat(N); let d = "hi".to_string(); //~^ repeat_once let e = s.to_string(); diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs index 89ab94bbaee85..d967fdc466ed7 100644 --- a/tests/ui/repeat_once.rs +++ b/tests/ui/repeat_once.rs @@ -11,7 +11,6 @@ fn main() { let b = slice.repeat(1); //~^ repeat_once let c = "hello".repeat(N); - //~^ repeat_once let d = "hi".repeat(1); //~^ repeat_once let e = s.repeat(1); diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr index 3db7a3568f8e6..62dbf7d233759 100644 --- a/tests/ui/repeat_once.stderr +++ b/tests/ui/repeat_once.stderr @@ -14,28 +14,22 @@ LL | let b = slice.repeat(1); | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:13:13 - | -LL | let c = "hello".repeat(N); - | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` - -error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:15:13 + --> tests/ui/repeat_once.rs:14:13 | LL | let d = "hi".repeat(1); | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:17:13 + --> tests/ui/repeat_once.rs:16:13 | LL | let e = s.repeat(1); | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` error: calling `repeat(1)` on a string literal - --> tests/ui/repeat_once.rs:19:13 + --> tests/ui/repeat_once.rs:18:13 | LL | let f = string.repeat(1); | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors From 11802781f7afc504f926c09a324ac5065a1746dc Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 2 Jul 2025 12:33:37 +0000 Subject: [PATCH 1243/1900] Make Value Copy by arena-allocating vectors. --- compiler/rustc_mir_transform/src/gvn.rs | 129 ++++++++++++------------ 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3ff8dc6dbb378..da36c3e6926ba 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -91,6 +91,7 @@ use either::Either; use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; +use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, @@ -129,7 +130,9 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { // Clone dominators because we need them while mutating the body. let dominators = body.basic_blocks.dominators().clone(); - let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls); + let arena = DroplessArena::default(); + let mut state = + VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { let opaque = state.new_opaque(body.local_decls[local].ty); @@ -183,8 +186,8 @@ enum AddressKind { Address(RawPtrKind), } -#[derive(Debug, PartialEq, Eq, Hash)] -enum Value<'tcx> { +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum Value<'a, 'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. @@ -201,7 +204,7 @@ enum Value<'tcx> { // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. - Aggregate(VariantIdx, Vec), + Aggregate(VariantIdx, &'a [VnIndex]), /// A raw pointer aggregate built from a thin pointer and metadata. RawPtr { /// Thin pointer component. This is field 0 in MIR. @@ -240,15 +243,15 @@ enum Value<'tcx> { /// This data structure is mostly a partial reimplementation of `FxIndexMap`. /// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, /// like opaque values, address with provenance and non-deterministic constants. -struct ValueSet<'tcx> { +struct ValueSet<'a, 'tcx> { indices: HashTable, hashes: IndexVec, - values: IndexVec>, + values: IndexVec>, types: IndexVec>, } -impl<'tcx> ValueSet<'tcx> { - fn new(num_values: usize) -> ValueSet<'tcx> { +impl<'a, 'tcx> ValueSet<'a, 'tcx> { + fn new(num_values: usize) -> ValueSet<'a, 'tcx> { ValueSet { indices: HashTable::with_capacity(num_values), hashes: IndexVec::with_capacity(num_values), @@ -263,7 +266,7 @@ impl<'tcx> ValueSet<'tcx> { fn insert_unique( &mut self, ty: Ty<'tcx>, - value: impl FnOnce(VnOpaque) -> Value<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>, ) -> VnIndex { let value = value(VnOpaque); @@ -284,7 +287,7 @@ impl<'tcx> ValueSet<'tcx> { /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. #[allow(rustc::pass_by_value)] // closures take `&VnIndex` - fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, Value::Constant { disambiguator, .. } => disambiguator.is_none(), @@ -319,8 +322,8 @@ impl<'tcx> ValueSet<'tcx> { /// Return the `Value` associated with the given `VnIndex`. #[inline] - fn value(&self, index: VnIndex) -> &Value<'tcx> { - &self.values[index] + fn value(&self, index: VnIndex) -> Value<'a, 'tcx> { + self.values[index] } /// Return the type associated with the given `VnIndex`. @@ -336,7 +339,7 @@ impl<'tcx> ValueSet<'tcx> { } } -struct VnState<'body, 'tcx> { +struct VnState<'body, 'a, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, local_decls: &'body LocalDecls<'tcx>, @@ -346,7 +349,7 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec>, - values: ValueSet<'tcx>, + values: ValueSet<'a, 'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, /// Cache the deref values. @@ -354,9 +357,10 @@ struct VnState<'body, 'tcx> { ssa: &'body SsaLocals, dominators: Dominators, reused_locals: DenseBitSet, + arena: &'a DroplessArena, } -impl<'body, 'tcx> VnState<'body, 'tcx> { +impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn new( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -364,6 +368,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ssa: &'body SsaLocals, dominators: Dominators, local_decls: &'body LocalDecls<'tcx>, + arena: &'a DroplessArena, ) -> Self { // Compute a rough estimate of the number of values in the body from the number of // statements. This is meant to reduce the number of allocations, but it's all right if @@ -385,6 +390,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ssa, dominators, reused_locals: DenseBitSet::new_empty(local_decls.len()), + arena, } } @@ -393,7 +399,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex { let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. @@ -464,7 +470,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[inline] - fn get(&self, index: VnIndex) -> &Value<'tcx> { + fn get(&self, index: VnIndex) -> Value<'a, 'tcx> { self.values.value(index) } @@ -495,8 +501,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.insert(ty, Value::Constant { value, disambiguator: None }) } - fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { - self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values)) + fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex { + self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values))) } fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { @@ -521,7 +527,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } else { return None; }; - let op = match *self.get(value) { + let op = match self.get(value) { _ if ty.is_zst() => ImmTy::uninit(ty).into(), Opaque(_) => return None, @@ -736,7 +742,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Value::Aggregate(_, fields) = self.get(value) { return Some((projection_ty, fields[f.as_usize()])); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(written_variant, fields) = self.get(*outer_value) + && let Value::Aggregate(written_variant, fields) = self.get(outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -761,7 +767,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { *from_non_ssa_index |= self.locals[idx].is_none(); - return Some((projection_ty, *inner)); + return Some((projection_ty, inner)); } let idx = self.locals[idx]?; ProjectionElem::Index(idx) @@ -769,7 +775,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { match self.get(value) { Value::Repeat(inner, _) => { - return Some((projection_ty, *inner)); + return Some((projection_ty, inner)); } Value::Aggregate(_, operands) => { let offset = if from_end { @@ -858,8 +864,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { - if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) + if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) + && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) && let AddressKind::Ref(BorrowKind::Shared) = kind && let Some(v) = self.simplify_place_value(&mut pointee, location) { @@ -882,8 +888,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; } - if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) + if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) + && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) && let AddressKind::Ref(BorrowKind::Shared) = kind && let Some(v) = self.simplify_place_value(&mut pointee, location) { @@ -994,7 +1000,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_discriminant(&mut self, place: VnIndex) -> Option { let enum_ty = self.ty(place); if enum_ty.is_enum() - && let Value::Aggregate(variant, _) = *self.get(place) + && let Value::Aggregate(variant, _) = self.get(place) { let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar())); @@ -1026,11 +1032,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fields: &[VnIndex], ) -> Option { let Some(&first_field) = fields.first() else { return None }; - let Value::Projection(copy_from_value, _) = *self.get(first_field) else { return None }; + let Value::Projection(copy_from_value, _) = self.get(first_field) else { return None }; // All fields must correspond one-to-one and come from the same aggregate value. if fields.iter().enumerate().any(|(index, &v)| { - if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v) + if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = self.get(v) && copy_from_value == pointer && from_index.index() == index { @@ -1042,7 +1048,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let mut copy_from_local_value = copy_from_value; - if let Value::Projection(pointer, proj) = *self.get(copy_from_value) + if let Value::Projection(pointer, proj) = self.get(copy_from_value) && let ProjectionElem::Downcast(_, read_variant) = proj { if variant_index == read_variant { @@ -1087,13 +1093,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let fields: Vec<_> = field_ops - .iter_mut() - .map(|op| { - self.simplify_operand(op, location) - .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) - }) - .collect(); + let fields = self.arena.alloc_from_iter(field_ops.iter_mut().map(|op| { + self.simplify_operand(op, location) + .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) + })); let variant_index = match *kind { AggregateKind::Array(..) | AggregateKind::Tuple => { @@ -1114,12 +1117,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let mut was_updated = false; while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } = self.get(pointer) - && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind() + && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(cast_value).kind() && let ty::RawPtr(_, output_mtbl) = ty.kind() && from_mtbl == output_mtbl && from_pointee_ty.is_sized(self.tcx, self.typing_env()) { - pointer = *cast_value; + pointer = cast_value; was_updated = true; } @@ -1184,16 +1187,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`, // it's fine to get a projection as the type. Value::Cast { kind: CastKind::PtrToPtr, value: inner } - if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => + if self.pointers_have_same_metadata(self.ty(inner), arg_ty) => { - *inner + inner } // We have an unsizing cast, which assigns the length to wide pointer metadata. Value::Cast { kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), value: from, - } if let Some(from) = self.ty(*from).builtin_deref(true) + } if let Some(from) = self.ty(from).builtin_deref(true) && let ty::Array(_, len) = from.kind() && let Some(to) = self.ty(arg_index).builtin_deref(true) && let ty::Slice(..) = to.kind() => @@ -1221,15 +1224,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let value = match (op, self.get(arg_index)) { - (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner), - (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner), + (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(inner), + (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(inner), (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => { - Value::BinaryOp(BinOp::Ne, *lhs, *rhs) + Value::BinaryOp(BinOp::Ne, lhs, rhs) } (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { - Value::BinaryOp(BinOp::Eq, *lhs, *rhs) + Value::BinaryOp(BinOp::Eq, lhs, rhs) } - (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata), + (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(metadata), // We have an unsizing cast, which assigns the length to wide pointer metadata. ( UnOp::PtrMetadata, @@ -1238,7 +1241,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { value: inner, }, ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind() - && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() => + && let ty::Array(_, len) = self.ty(inner).builtin_deref(true).unwrap().kind() => { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } @@ -1271,12 +1274,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && lhs_ty.is_any_ptr() && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs) && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs) - && let lhs_from = self.ty(*lhs_value) - && lhs_from == self.ty(*rhs_value) + && let lhs_from = self.ty(lhs_value) + && lhs_from == self.ty(rhs_value) && self.pointers_have_same_metadata(lhs_from, lhs_ty) { - lhs = *lhs_value; - rhs = *rhs_value; + lhs = lhs_value; + rhs = rhs_value; if let Some(lhs_op) = self.try_as_operand(lhs, location) && let Some(rhs_op) = self.try_as_operand(rhs, location) { @@ -1410,7 +1413,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if op.is_overflowing() { let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]); let false_val = self.insert_bool(false); - Some(self.insert_tuple(ty, vec![result, false_val])) + Some(self.insert_tuple(ty, &[result, false_val])) } else { Some(result) } @@ -1463,11 +1466,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let ty::RawPtr(to_pointee, _) = to.kind() && to_pointee.is_sized(self.tcx, self.typing_env()) { - from = self.ty(*pointer); - value = *pointer; + from = self.ty(pointer); + value = pointer; was_updated_this_iteration = true; if from == to { - return Some(*pointer); + return Some(pointer); } } @@ -1476,7 +1479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Transmute = kind && let Value::Aggregate(variant_idx, field_values) = self.get(value) && let Some((field_idx, field_ty)) = - self.value_is_all_in_one_field(from, *variant_idx) + self.value_is_all_in_one_field(from, variant_idx) { from = field_ty; value = field_values[field_idx.as_usize()]; @@ -1487,7 +1490,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Various cast-then-cast cases can be simplified. - if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) { + if let Value::Cast { kind: inner_kind, value: inner_value } = self.get(value) { let inner_from = self.ty(inner_value); let new_kind = match (inner_kind, kind) { // Even if there's a narrowing cast in here that's fine, because @@ -1686,7 +1689,7 @@ fn op_to_prop_const<'tcx>( None } -impl<'tcx> VnState<'_, 'tcx> { +impl<'tcx> VnState<'_, '_, 'tcx> { /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds, /// returns that result as an [`Operand`]. fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option> { @@ -1705,7 +1708,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: None } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } @@ -1749,7 +1752,7 @@ impl<'tcx> VnState<'_, 'tcx> { // If we are here, we failed to find a local, and we already have a `Deref`. // Trying to add projections will only result in an ill-formed place. return None; - } else if let Value::Projection(pointer, proj) = *self.get(index) + } else if let Value::Projection(pointer, proj) = self.get(index) && (allow_complex_projection || proj.is_stable_offset()) && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) { @@ -1772,7 +1775,7 @@ impl<'tcx> VnState<'_, 'tcx> { } } -impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { +impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } From 3d96e54656927e4870b7b4aaa0afa099e1fc012a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 1 Jul 2025 10:01:12 +0000 Subject: [PATCH 1244/1900] Use a VnIndex in Address projection. --- compiler/rustc_mir_transform/src/gvn.rs | 201 +++++++++++------- ....dereference_indexing.GVN.panic-abort.diff | 3 +- ...dereference_indexing.GVN.panic-unwind.diff | 3 +- tests/mir-opt/gvn.rs | 4 +- tests/mir-opt/gvn_repeat.index_place.GVN.diff | 15 ++ .../mir-opt/gvn_repeat.repeat_local.GVN.diff | 3 +- tests/mir-opt/gvn_repeat.rs | 21 +- 7 files changed, 171 insertions(+), 79 deletions(-) create mode 100644 tests/mir-opt/gvn_repeat.index_place.GVN.diff diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index da36c3e6926ba..03a40f83732ac 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -186,6 +186,14 @@ enum AddressKind { Address(RawPtrKind), } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AddressBase { + /// This address is based on this local. + Local(Local), + /// This address is based on the deref of this pointer. + Deref(VnIndex), +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum Value<'a, 'tcx> { // Root values. @@ -216,7 +224,10 @@ enum Value<'a, 'tcx> { Repeat(VnIndex, ty::Const<'tcx>), /// The address of a place. Address { - place: Place<'tcx>, + base: AddressBase, + // We do not use a plain `Place` as we want to be able to reason about indices. + // This does not contain any `Deref` projection. + projection: &'a [ProjectionElem>], kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. provenance: VnOpaque, @@ -426,7 +437,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex { + fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option { let pty = place.ty(self.local_decls, self.tcx).ty; let ty = match kind { AddressKind::Ref(bk) => { @@ -434,14 +445,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let index = - self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + + let mut projection = place.projection.iter(); + let base = if place.is_indirect_first_projection() { + let base = self.locals[place.local]?; + // Skip the initial `Deref`. + projection.next(); + AddressBase::Deref(base) + } else { + AddressBase::Local(place.local) + }; + // Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`. + let projection = + projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(())); + let projection = self.arena.try_alloc_from_iter(projection).ok()?; + + let index = self.values.insert_unique(ty, |provenance| Value::Address { + base, + projection, + kind, + provenance, + }); let evaluated = self.eval_to_const(index); let _index = self.evaluated.push(evaluated); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); - index + + Some(index) } #[instrument(level = "trace", skip(self), ret)] @@ -591,14 +622,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let elem = elem.try_map(|_| None, |()| ty.ty)?; self.ecx.project(base, elem).discard_err()? } - Address { place, kind: _, provenance: _ } => { - if !place.is_indirect_first_projection() { - return None; - } - let local = self.locals[place.local]?; - let pointer = self.evaluated[local].as_ref()?; + Address { base, projection, .. } => { + debug_assert!(!projection.contains(&ProjectionElem::Deref)); + let pointer = match base { + AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?, + // We have no stack to point to. + AddressBase::Local(_) => return None, + }; let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?; - for elem in place.projection.iter().skip(1) { + for elem in projection { // `Index` by constants should have been replaced by `ConstantIndex` by // `simplify_place_projection`. let elem = elem.try_map(|_| None, |ty| ty)?; @@ -717,12 +749,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Some(op) } + /// Represent the *value* we obtain by dereferencing an `Address` value. + #[instrument(level = "trace", skip(self), ret)] + fn dereference_address( + &mut self, + base: AddressBase, + projection: &[ProjectionElem>], + ) -> Option { + let (mut place_ty, mut value) = match base { + // The base is a local, so we take the local's value and project from it. + AddressBase::Local(local) => { + let local = self.locals[local]?; + let place_ty = PlaceTy::from_ty(self.ty(local)); + (place_ty, local) + } + // The base is a pointer's deref, so we introduce the implicit deref. + AddressBase::Deref(reborrow) => { + let place_ty = PlaceTy::from_ty(self.ty(reborrow)); + self.project(place_ty, reborrow, ProjectionElem::Deref)? + } + }; + for &proj in projection { + (place_ty, value) = self.project(place_ty, value, proj)?; + } + Some(value) + } + + #[instrument(level = "trace", skip(self), ret)] fn project( &mut self, place_ty: PlaceTy<'tcx>, value: VnIndex, - proj: PlaceElem<'tcx>, - from_non_ssa_index: &mut bool, + proj: ProjectionElem>, ) -> Option<(PlaceTy<'tcx>, VnIndex)> { let projection_ty = place_ty.projection_ty(self.tcx, proj); let proj = match proj { @@ -730,6 +788,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { if let Some(Mutability::Not) = place_ty.ty.ref_mutability() && projection_ty.ty.is_freeze(self.tcx, self.typing_env()) { + if let Value::Address { base, projection, .. } = self.get(value) + && let Some(value) = self.dereference_address(base, projection) + { + return Some((projection_ty, value)); + } + // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); @@ -766,10 +830,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { - *from_non_ssa_index |= self.locals[idx].is_none(); return Some((projection_ty, inner)); } - let idx = self.locals[idx]?; ProjectionElem::Index(idx) } ProjectionElem::ConstantIndex { offset, min_length, from_end } => { @@ -844,40 +906,23 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { trace!(?place); } - /// Represent the *value* which would be read from `place`, and point `place` to a preexisting - /// place with the same value (if that already exists). + /// Represent the *value* which would be read from `place`. If we succeed, return it. + /// If we fail, return a `PlaceRef` that contains the same value. #[instrument(level = "trace", skip(self), ret)] - fn simplify_place_value( + fn compute_place_value( &mut self, - place: &mut Place<'tcx>, + place: Place<'tcx>, location: Location, - ) -> Option { - self.simplify_place_projection(place, location); - + ) -> Result> { // Invariant: `place` and `place_ref` point to the same value, even if they point to // different memory locations. let mut place_ref = place.as_ref(); // Invariant: `value` holds the value up-to the `index`th projection excluded. - let mut value = self.locals[place.local]?; + let Some(mut value) = self.locals[place.local] else { return Err(place_ref) }; // Invariant: `value` has type `place_ty`, with optional downcast variant if needed. let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); - let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { - if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) - && let AddressKind::Ref(BorrowKind::Shared) = kind - && let Some(v) = self.simplify_place_value(&mut pointee, location) - { - value = v; - // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`. - // That local is SSA, but we otherwise have no guarantee on that local's value at - // the current location compared to its value where `pointee` was borrowed. - if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) { - place_ref = - pointee.project_deeper(&place.projection[index..], self.tcx).as_ref(); - } - } if let Some(local) = self.try_as_local(value, location) { // Both `local` and `Place { local: place.local, projection: projection[..index] }` // hold the same value. Therefore, following place holds the value in the original @@ -885,36 +930,50 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { place_ref = PlaceRef { local, projection: &place.projection[index..] }; } - (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; + let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else { + return Err(place_ref); + }; + let Some(ty_and_value) = self.project(place_ty, value, proj) else { + return Err(place_ref); + }; + (place_ty, value) = ty_and_value; } - if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) - && let AddressKind::Ref(BorrowKind::Shared) = kind - && let Some(v) = self.simplify_place_value(&mut pointee, location) - { - value = v; - // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`. - // That local is SSA, but we otherwise have no guarantee on that local's value at - // the current location compared to its value where `pointee` was borrowed. - if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) { - place_ref = pointee.project_deeper(&[], self.tcx).as_ref(); - } - } - if let Some(new_local) = self.try_as_local(value, location) { - place_ref = PlaceRef { local: new_local, projection: &[] }; - } else if from_non_ssa_index { - // If access to non-SSA locals is unavoidable, bail out. - return None; - } + Ok(value) + } - if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() { - // By the invariant on `place_ref`. - *place = place_ref.project_deeper(&[], self.tcx); - self.reused_locals.insert(place_ref.local); - } + /// Represent the *value* which would be read from `place`, and point `place` to a preexisting + /// place with the same value (if that already exists). + #[instrument(level = "trace", skip(self), ret)] + fn simplify_place_value( + &mut self, + place: &mut Place<'tcx>, + location: Location, + ) -> Option { + self.simplify_place_projection(place, location); - Some(value) + match self.compute_place_value(*place, location) { + Ok(value) => { + if let Some(new_place) = self.try_as_place(value, location, true) + && (new_place.local != place.local + || new_place.projection.len() < place.projection.len()) + { + *place = new_place; + self.reused_locals.insert(new_place.local); + } + Some(value) + } + Err(place_ref) => { + if place_ref.local != place.local + || place_ref.projection.len() < place.projection.len() + { + // By the invariant on `place_ref`. + *place = place_ref.project_deeper(&[], self.tcx); + self.reused_locals.insert(place_ref.local); + } + None + } + } } #[instrument(level = "trace", skip(self), ret)] @@ -961,11 +1020,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); - return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); + return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); } Rvalue::RawPtr(mutbl, ref mut place) => { self.simplify_place_projection(place, location); - return Some(self.new_pointer(*place, AddressKind::Address(mutbl))); + return self.new_pointer(*place, AddressKind::Address(mutbl)); } Rvalue::WrapUnsafeBinder(ref mut op, _) => { let value = self.simplify_operand(op, location)?; @@ -1205,12 +1264,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } // `&mut *p`, `&raw *p`, etc don't change metadata. - Value::Address { place, kind: _, provenance: _ } - if let PlaceRef { local, projection: [PlaceElem::Deref] } = - place.as_ref() - && let Some(local_index) = self.locals[local] => + Value::Address { base: AddressBase::Deref(reborrowed), projection, .. } + if projection.is_empty() => { - local_index + reborrowed } _ => break, diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff index 9bdcc2f108a6a..1a07638cbafea 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff @@ -43,7 +43,8 @@ + nop; StorageLive(_8); StorageLive(_9); - _9 = copy (*_3); +- _9 = copy (*_3); ++ _9 = copy _1[_4]; _8 = opaque::(move _9) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff index f38bc51adc480..6e67b66e783c4 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff @@ -43,7 +43,8 @@ + nop; StorageLive(_8); StorageLive(_9); - _9 = copy (*_3); +- _9 = copy (*_3); ++ _9 = copy _1[_4]; _8 = opaque::(move _9) -> [return: bb2, unwind continue]; } diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 98f94fbf0b146..3c3241fefe22e 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -1065,8 +1065,8 @@ fn dereference_indexing(array: [u8; 2], index: usize) { &array[i] }; - // CHECK-NOT: [{{.*}}] - // CHECK: [[tmp:_.*]] = copy (*[[a]]); + // CHECK-NOT: StorageDead([[i]]); + // CHECK: [[tmp:_.*]] = copy _1[[[i]]]; // CHECK: opaque::(move [[tmp]]) opaque(*a); } diff --git a/tests/mir-opt/gvn_repeat.index_place.GVN.diff b/tests/mir-opt/gvn_repeat.index_place.GVN.diff new file mode 100644 index 0000000000000..1eb7e9015cc3f --- /dev/null +++ b/tests/mir-opt/gvn_repeat.index_place.GVN.diff @@ -0,0 +1,15 @@ +- // MIR for `index_place` before GVN ++ // MIR for `index_place` after GVN + + fn index_place(_1: usize, _2: usize, _3: [i32; 5]) -> i32 { + let mut _0: i32; + let mut _4: &i32; + + bb0: { + _4 = &_3[_1]; + _1 = copy _2; + _0 = copy (*_4); + return; + } + } + diff --git a/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff index fd04782528117..eb3f885b8665b 100644 --- a/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff +++ b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff @@ -10,8 +10,7 @@ _4 = [copy _3; 5]; _5 = &_4[_1]; _1 = copy _2; -- _0 = copy (*_5); -+ _0 = copy _3; + _0 = copy (*_5); return; } } diff --git a/tests/mir-opt/gvn_repeat.rs b/tests/mir-opt/gvn_repeat.rs index bbbb2a7ccbaf5..49364c6bfd232 100644 --- a/tests/mir-opt/gvn_repeat.rs +++ b/tests/mir-opt/gvn_repeat.rs @@ -6,6 +6,23 @@ use std::intrinsics::mir::*; +// EMIT_MIR gvn_repeat.index_place.GVN.diff +#[custom_mir(dialect = "runtime")] +pub fn index_place(mut idx1: usize, idx2: usize, array: [i32; 5]) -> i32 { + // CHECK-LABEL: fn index_place( + // CHECK: let mut [[ELEM:.*]]: &i32; + // CHECK: _0 = copy (*[[ELEM]]) + mir! { + let elem; + { + elem = &array[idx1]; + idx1 = idx2; + RET = *elem; + Return() + } + } +} + // EMIT_MIR gvn_repeat.repeat_place.GVN.diff #[custom_mir(dialect = "runtime")] pub fn repeat_place(mut idx1: usize, idx2: usize, val: &i32) -> i32 { @@ -29,7 +46,8 @@ pub fn repeat_place(mut idx1: usize, idx2: usize, val: &i32) -> i32 { #[custom_mir(dialect = "runtime")] pub fn repeat_local(mut idx1: usize, idx2: usize, val: i32) -> i32 { // CHECK-LABEL: fn repeat_local( - // CHECK: _0 = copy _3 + // CHECK: let mut [[ELEM:.*]]: &i32; + // CHECK: _0 = copy (*[[ELEM]]); mir! { let array; let elem; @@ -44,6 +62,7 @@ pub fn repeat_local(mut idx1: usize, idx2: usize, val: i32) -> i32 { } fn main() { + assert_eq!(index_place(0, 5, [0; 5]), 0); assert_eq!(repeat_place(0, 5, &0), 0); assert_eq!(repeat_local(0, 5, 0), 0); } From 3350c27d911dd75d0b2c4fcbdba40776e482e42f Mon Sep 17 00:00:00 2001 From: David Barsky Date: Mon, 22 Sep 2025 11:54:22 -0400 Subject: [PATCH 1245/1900] internal: bump salsa to 0.24 --- src/tools/rust-analyzer/Cargo.lock | 463 ++++++++---------- src/tools/rust-analyzer/Cargo.toml | 5 +- .../crates/hir-def/src/test_db.rs | 3 +- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- .../crates/hir-ty/src/next_solver/ir_print.rs | 3 + .../crates/query-group-macro/src/queries.rs | 3 +- .../rust-analyzer/crates/span/src/hygiene.rs | 87 +++- 7 files changed, 287 insertions(+), 281 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5a0d209602dfe..5840d2ca6a723 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -31,15 +31,15 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -115,9 +115,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "borsh" @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "boxcar" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa" +checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" [[package]] name = "byteorder" @@ -142,11 +142,11 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "camino" -version = "1.1.10" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -184,7 +184,7 @@ dependencies = [ "serde", "serde-untagged", "serde-value", - "thiserror 2.0.12", + "thiserror 2.0.16", "toml", "unicode-xid", "url", @@ -202,7 +202,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -217,15 +217,16 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] name = "cc" -version = "1.2.26" +version = "1.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" dependencies = [ + "find-msvc-tools", "shlex", ] @@ -247,9 +248,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -275,24 +276,24 @@ version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "chalk-derive", ] [[package]] name = "clap" -version = "4.5.42" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstyle", "clap_lex", @@ -310,7 +311,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -321,15 +322,15 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" [[package]] name = "cov-mark" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4" +checksum = "3f1d92727879fb4f24cec33a35e3bff74035541326cbc12ad44ba8886d1927b0" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -385,12 +386,13 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "ctrlc" -version = "3.4.7" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3" dependencies = [ + "dispatch", "nix", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -409,18 +411,18 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] [[package]] name = "derive-where" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", @@ -429,9 +431,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", @@ -456,9 +458,15 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "displaydoc" version = "0.2.5" @@ -515,11 +523,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" dependencies = [ "serde", + "serde_core", "typeid", ] @@ -534,16 +543,10 @@ dependencies = [ ] [[package]] -name = "filetime" -version = "0.2.25" +name = "find-msvc-tools" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" [[package]] name = "fixedbitset" @@ -569,9 +572,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -625,22 +628,28 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -700,7 +709,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg", "cov-mark", "drop_bomb", @@ -767,7 +776,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.9.1", + "bitflags 2.9.4", "chalk-derive", "chalk-ir", "cov-mark", @@ -980,7 +989,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.9.1", + "bitflags 2.9.4", "cov-mark", "crossbeam-channel", "either", @@ -1047,9 +1056,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1068,13 +1077,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -1083,7 +1093,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "inotify-sys", "libc", ] @@ -1116,6 +1126,15 @@ dependencies = [ "memoffset", ] +[[package]] +name = "inventory" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +dependencies = [ + "rustversion", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1175,25 +1194,25 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-link 0.2.0", ] [[package]] name = "libmimalloc-sys" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" dependencies = [ "cc", "libc", @@ -1201,13 +1220,12 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "libc", - "redox_syscall", ] [[package]] @@ -1266,9 +1284,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lsp-server" @@ -1340,9 +1358,9 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" dependencies = [ "libc", ] @@ -1358,9 +1376,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.46" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" dependencies = [ "libmimalloc-sys", ] @@ -1388,11 +1406,11 @@ dependencies = [ [[package]] name = "miow" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.0", ] [[package]] @@ -1401,7 +1419,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -1415,12 +1433,11 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "notify" -version = "8.0.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.1", - "filetime", + "bitflags 2.9.4", "fsevent-sys", "inotify", "kqueue", @@ -1429,7 +1446,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1517,16 +1534,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "papaya" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f" -dependencies = [ - "equivalent", - "seize", -] - [[package]] name = "parking_lot" version = "0.12.4" @@ -1578,9 +1585,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf-event" @@ -1637,9 +1644,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -1706,9 +1713,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -1788,7 +1795,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "memchr", "unicase", ] @@ -1829,7 +1836,7 @@ version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597bb303548ddcca3a2eb05af254508aaf39cf334d4350bb5da51de1eb728859" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "ra-ap-rustc_hashes", "ra-ap-rustc_index", "tracing", @@ -1925,7 +1932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e35ee9e052406035016b8e6d54ca202bc39ccba1702780b33b2d5fb10d1da8" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.4", "derive-where", "ena", "indexmap", @@ -1952,9 +1959,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1962,9 +1969,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1972,22 +1979,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -2071,9 +2078,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2111,7 +2118,7 @@ version = "0.2.3+llvm-462a31f5a5ab" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486c2179b4796f65bfe2ee33679acf0927ac83ecf583ad6c91c3b4570911b9ad" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "smallvec", ] @@ -2124,6 +2131,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -2133,17 +2146,16 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e235afdb8e510f38a07138fbe5a0b64691894358a9c0cbd813b1aade110efc9" +source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" dependencies = [ "boxcar", "crossbeam-queue", "crossbeam-utils", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "hashlink", "indexmap", "intrusive-collections", - "papaya", + "inventory", "parking_lot", "portable-atomic", "rayon", @@ -2158,14 +2170,12 @@ dependencies = [ [[package]] name = "salsa-macro-rules" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2edb86a7e9c91f6d30c9ce054312721dbe773a162db27bbfae834d16177b30ce" +source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" [[package]] name = "salsa-macros" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0778d6e209051bc4e75acfe83bcd7848601ec3dbe9c3dbb982829020e9128af" +source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" dependencies = [ "proc-macro2", "quote", @@ -2203,42 +2213,35 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "seize" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b8d813387d566f627f3ea1b914c068aac94c40ae27ec43f5f33bde65abefe7" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde-untagged" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ "erased-serde", "serde", + "serde_core", "typeid", ] @@ -2252,11 +2255,20 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", @@ -2265,15 +2277,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "indexmap", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -2373,9 +2386,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.103" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -2491,11 +2504,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -2511,9 +2524,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -2562,9 +2575,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -2579,15 +2592,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -2665,9 +2678,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -2763,9 +2776,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-properties" @@ -2781,9 +2794,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -2850,11 +2863,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -2866,7 +2879,7 @@ dependencies = [ "windows-collections", "windows-core", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -2887,7 +2900,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -2899,7 +2912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -2931,6 +2944,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -2938,7 +2957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -2947,7 +2966,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -2956,16 +2975,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-link 0.1.3", ] [[package]] @@ -2992,22 +3002,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link 0.2.0", ] [[package]] @@ -3028,10 +3032,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -3048,15 +3053,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3069,12 +3068,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3087,12 +3080,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3117,12 +3104,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3135,12 +3116,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3153,12 +3128,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3171,12 +3140,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3191,9 +3154,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -3318,9 +3281,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -3340,9 +3303,9 @@ dependencies = [ [[package]] name = "zip" -version = "4.0.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" dependencies = [ "arbitrary", "crc32fast", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d3a4e375613eb..25af4dfa87af2 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -137,12 +137,13 @@ rayon = "1.10.0" rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.23.0", default-features = true, features = [ +salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "e257df12eabd566825ba53bb12d782560b9a4dcd", default-features = true, features = [ "rayon", "salsa_unstable", "macros", ] } -salsa-macros = "0.23.0" +# salsa-macros = "0.23.0" +salsa-macros = { git = "https://github.com/salsa-rs/salsa.git", rev = "e257df12eabd566825ba53bb12d782560b9a4dcd" } semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 1e2f354f975cb..61ca6f67e6143 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -1,5 +1,6 @@ //! Database used for testing `hir_def`. +use salsa::database::AsDynDatabase; use std::{fmt, panic, sync::Mutex}; use base_db::{ @@ -7,7 +8,7 @@ use base_db::{ SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_expand::{InFile, files::FilePosition}; -use salsa::{AsDynDatabase, Durability}; +use salsa::{Durability, database::AsDynDatabase}; use span::FileId; use syntax::{AstNode, algo, ast}; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 6b91ee35eba3b..2436d1ede8712 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -286,9 +286,7 @@ impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method pub fn conjure() -> DbInterner<'db> { salsa::with_attached_database(|db| DbInterner { - db: unsafe { - std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db.as_view()) - }, + db: unsafe { std::mem::transmute::<&dyn salsa::Database, &'db dyn HirDatabase>(db) }, krate: None, block: None, }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 407e157564397..757c599232b93 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -62,9 +62,12 @@ impl<'db> IrPrint> for DbInterner<'db> { let self_ty = &t.args.as_slice()[0]; let trait_args = &t.args.as_slice()[1..]; if trait_args.is_empty() { + let db = db.zalsa().views().downcaster_for::(); + db.downcast_unchecked(db) fmt.write_str(&format!( "{:?}: {}", self_ty, + db.as_dyn_database().as_dyn_database(), db.as_view::().trait_signature(trait_).name.as_str() )) } else { diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index 22a26c49fa530..7698ce5fff13e 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs @@ -327,7 +327,8 @@ impl ToTokens for Lookup { let wrapper_struct = self.interned_struct_path.to_token_stream(); let method = quote! { #sig { - #wrapper_struct::ingredient(self).data(self.as_dyn_database(), id.as_id()).0.clone() + let zalsa = self.zalsa(); + #wrapper_struct::ingredient(zalsa).data(zalsa, id.as_id()).0.clone() } }; diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index aef3fbf051775..67ff27e31c0c3 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -98,21 +98,38 @@ const _: () = { salsa::plumbing::Location { file: file!(), line: line!() }; const DEBUG_NAME: &'static str = "SyntaxContextData"; const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX; + const PERSIST: bool = false; + type Fields<'a> = SyntaxContextData; type Struct<'a> = SyntaxContext; - } - impl SyntaxContext { - pub fn ingredient(db: &Db) -> &zalsa_struct_::IngredientImpl + + fn serialize(_: &Self::Fields<'_>, _: S) -> Result where - Db: ?Sized + zalsa_::Database, + S: zalsa_::serde::Serializer, + { + unimplemented!("attempted to serialize value that set `PERSIST` to false") + } + + fn deserialize<'de, D>(_: D) -> Result, D::Error> + where + D: zalsa_::serde::Deserializer<'de>, { + unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false"); + } + } + + impl SyntaxContext { + pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl { static CACHE: zalsa_::IngredientCache> = zalsa_::IngredientCache::new(); - CACHE.get_or_create(db.zalsa(), || { - db.zalsa() - .lookup_jar_by_type::>() - .get_or_create() - }) + + // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only + // ingredient created by our jar is the struct ingredient. + unsafe { + CACHE.get_or_create(zalsa, || { + zalsa.lookup_jar_by_type::>() + }) + } } } impl zalsa_::AsId for SyntaxContext { @@ -132,13 +149,14 @@ const _: () = { impl zalsa_::SalsaStructInDb for SyntaxContext { type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; - fn lookup_or_create_ingredient_index( - zalsa: &salsa::plumbing::Zalsa, - ) -> salsa::plumbing::IngredientIndices { - zalsa - .lookup_jar_by_type::>() - .get_or_create() - .into() + fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { + aux.lookup_jar_by_type::>().into() + } + + fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator + '_ { + let _ingredient_index = + zalsa.lookup_jar_by_type::>(); + ::ingredient(zalsa).entries(zalsa).map(|entry| entry.key()) } #[inline] @@ -149,6 +167,18 @@ const _: () = { None } } + + #[inline] + unsafe fn memo_table( + zalsa: &zalsa_::Zalsa, + id: zalsa_::Id, + current_revision: zalsa_::Revision, + ) -> zalsa_::MemoTableWithTypes<'_> { + // SAFETY: Guaranteed by caller. + unsafe { + zalsa.table().memos::>(id, current_revision) + } + } } unsafe impl salsa::plumbing::Update for SyntaxContext { @@ -184,8 +214,11 @@ const _: () = { Edition: zalsa_::interned::HashEqLike, SyntaxContext: zalsa_::interned::HashEqLike, { - SyntaxContext::ingredient(db).intern( - db.as_dyn_database(), + let (zalsa, zalsa_local) = db.zalsas(); + + SyntaxContext::ingredient(zalsa).intern( + zalsa, + zalsa_local, StructKey::<'db>( outer_expn, outer_transparency, @@ -216,7 +249,8 @@ const _: () = { Db: ?Sized + zalsa_::Database, { let id = self.as_salsa_id()?; - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.outer_expn } @@ -225,7 +259,8 @@ const _: () = { Db: ?Sized + zalsa_::Database, { let Some(id) = self.as_salsa_id() else { return Transparency::Opaque }; - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.outer_transparency } @@ -235,7 +270,8 @@ const _: () = { { match self.as_salsa_id() { Some(id) => { - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.edition } None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()), @@ -248,7 +284,8 @@ const _: () = { { match self.as_salsa_id() { Some(id) => { - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.parent } None => self, @@ -262,7 +299,8 @@ const _: () = { { match self.as_salsa_id() { Some(id) => { - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.opaque } None => self, @@ -276,7 +314,8 @@ const _: () = { { match self.as_salsa_id() { Some(id) => { - let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id); + let zalsa = db.zalsa(); + let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.opaque_and_semitransparent } None => self, From cf341eac195d85f8193de5052b68960605247a29 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 18 Sep 2025 00:36:52 +0000 Subject: [PATCH 1246/1900] Refactor remove_noop_landing_pads in two loops. --- .../src/remove_noop_landing_pads.rs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index b53c1f6d20203..bbfa565a7b600 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,7 +1,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use tracing::debug; +use tracing::{debug, instrument}; use crate::patch::MirPatch; @@ -15,6 +15,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { sess.panic_strategy().unwinds() } + #[instrument(level = "debug", skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); debug!(?def_id); @@ -25,7 +26,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { .iter_enumerated() .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume)); if !has_resume { - debug!("remove_noop_landing_pads: no resume block in MIR"); + debug!("no resume block in MIR"); return; } @@ -36,42 +37,44 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { patch.apply(body); resume_block }; - debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); + debug!(?resume_block); - let mut jumps_folded = 0; - let mut landing_pads_removed = 0; let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. - let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); - for bb in postorder { - debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() + for &bb in body.basic_blocks.reverse_postorder().iter().rev() { + let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); + debug!("is_nop_landing_pad({bb:?}) = {is_nop_landing_pad}"); + if is_nop_landing_pad { + nop_landing_pads.insert(bb); + } + } + + if nop_landing_pads.is_empty() { + debug!("no nop landing pads in MIR"); + return; + } + + let basic_blocks = body.basic_blocks.as_mut(); + for (bb, bbdata) in basic_blocks.iter_enumerated_mut() { + debug!("processing {:?}", bb); + + if let Some(unwind) = bbdata.terminator_mut().unwind_mut() && let UnwindAction::Cleanup(unwind_bb) = *unwind && nop_landing_pads.contains(unwind_bb) { debug!(" removing noop landing pad"); - landing_pads_removed += 1; *unwind = UnwindAction::Continue; } - body[bb].terminator_mut().successors_mut(|target| { + bbdata.terminator_mut().successors_mut(|target| { if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); *target = resume_block; - jumps_folded += 1; } }); - - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); - if is_nop_landing_pad { - nop_landing_pads.insert(bb); - } - debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); } - - debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); } fn is_required(&self) -> bool { From 1ac5d4c2783a3ac23f19c8a71ba6e7fdf21034c7 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 27 Sep 2025 16:41:32 +0000 Subject: [PATCH 1247/1900] Create resume block later. --- .../src/remove_noop_landing_pads.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index bbfa565a7b600..0e6eef38cd2c5 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -30,15 +30,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { return; } - // make sure there's a resume block without any statements - let resume_block = { - let mut patch = MirPatch::new(body); - let resume_block = patch.resume_block(); - patch.apply(body); - resume_block - }; - debug!(?resume_block); - let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B @@ -56,6 +47,15 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { return; } + // make sure there's a resume block without any statements + let resume_block = { + let mut patch = MirPatch::new(body); + let resume_block = patch.resume_block(); + patch.apply(body); + resume_block + }; + debug!(?resume_block); + let basic_blocks = body.basic_blocks.as_mut(); for (bb, bbdata) in basic_blocks.iter_enumerated_mut() { debug!("processing {:?}", bb); From 94b2c303aa9c9c6a1a34d1b5587e0a7a481fa97f Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 4 Oct 2025 17:02:08 +0000 Subject: [PATCH 1248/1900] Use traversal::postorder. --- .../src/remove_noop_landing_pads.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 0e6eef38cd2c5..38752bde6b417 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -34,8 +34,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. - for &bb in body.basic_blocks.reverse_postorder().iter().rev() { - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); + for (bb, bbdata) in traversal::postorder(body) { + let is_nop_landing_pad = self.is_nop_landing_pad(bbdata, &nop_landing_pads); debug!("is_nop_landing_pad({bb:?}) = {is_nop_landing_pad}"); if is_nop_landing_pad { nop_landing_pads.insert(bb); @@ -85,11 +85,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { impl RemoveNoopLandingPads { fn is_nop_landing_pad( &self, - bb: BasicBlock, - body: &Body<'_>, + bbdata: &BasicBlockData<'_>, nop_landing_pads: &DenseBitSet, ) -> bool { - for stmt in &body[bb].statements { + for stmt in &bbdata.statements { match &stmt.kind { StatementKind::FakeRead(..) | StatementKind::StorageLive(_) @@ -122,7 +121,7 @@ impl RemoveNoopLandingPads { } } - let terminator = body[bb].terminator(); + let terminator = bbdata.terminator(); match terminator.kind { TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume From 82e2c674528e9f68590ae1408a4a1a00ce3df628 Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Sat, 4 Oct 2025 12:30:15 -0500 Subject: [PATCH 1249/1900] Move salsa attach to end before call to run_ide_things --- .../crates/rust-analyzer/src/cli/analysis_stats.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 9c8743536877b..36c4f281b7303 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -335,15 +335,15 @@ impl flags::AnalysisStats { if !self.skip_const_eval { self.run_const_eval(db, &bodies, verbosity); } + }); - if self.run_all_ide_things { - self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); - } + if self.run_all_ide_things { + self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); + } - if self.run_term_search { - self.run_term_search(&workspace, db, &vfs, file_ids, verbosity); - } - }); + if self.run_term_search { + self.run_term_search(&workspace, db, &vfs, file_ids, verbosity); + } let db = host.raw_database_mut(); db.trigger_lru_eviction(); From c44500b4a1a21699bad155572368bf88820e1fbe Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 4 Sep 2025 18:45:32 -0500 Subject: [PATCH 1250/1900] Remove boxes from ast Pat lists --- compiler/rustc_ast/src/ast.rs | 8 +-- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/pat.rs | 4 +- .../src/deriving/generic/mod.rs | 4 +- .../rustc_builtin_macros/src/pattern_type.rs | 4 +- compiler/rustc_expand/src/build.rs | 36 +++++------ compiler/rustc_expand/src/expand.rs | 4 +- .../rustc_parse/src/parser/diagnostics.rs | 22 +++++-- compiler/rustc_parse/src/parser/expr.rs | 12 ++-- compiler/rustc_parse/src/parser/item.rs | 2 +- .../rustc_parse/src/parser/nonterminal.rs | 3 +- compiler/rustc_parse/src/parser/pat.rs | 62 ++++++++++--------- compiler/rustc_resolve/src/late.rs | 10 +-- .../clippy_lints/src/unnested_or_patterns.rs | 38 ++++++------ src/tools/rustfmt/src/parse/macros/mod.rs | 2 +- src/tools/rustfmt/src/patterns.rs | 4 +- 16 files changed, 118 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 082d5e88ac754..926fb61315818 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -869,11 +869,11 @@ pub enum PatKind { Struct(Option>, Path, ThinVec, PatFieldsRest), /// A tuple struct/variant pattern (`Variant(x, y, .., z)`). - TupleStruct(Option>, Path, ThinVec>), + TupleStruct(Option>, Path, ThinVec), /// An or-pattern `A | B | C`. /// Invariant: `pats.len() >= 2`. - Or(ThinVec>), + Or(ThinVec), /// A possibly qualified path pattern. /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants @@ -882,7 +882,7 @@ pub enum PatKind { Path(Option>, Path), /// A tuple pattern (`(a, b)`). - Tuple(ThinVec>), + Tuple(ThinVec), /// A `box` pattern. Box(Box), @@ -900,7 +900,7 @@ pub enum PatKind { Range(Option>, Option>, Spanned), /// A slice pattern `[a, b, c]`. - Slice(ThinVec>), + Slice(ThinVec), /// A rest pattern `..`. /// diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 68b3d2b036865..7470bd9abbc7f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -389,7 +389,7 @@ macro_rules! common_visitor_and_walkers { ThinVec<(NodeId, Path)>, ThinVec, ThinVec, - ThinVec>, + ThinVec, ThinVec>, ThinVec>, ); diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ed159f37051c1..03aeb064d7f11 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -154,7 +154,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_pat_tuple( &mut self, - pats: &[Box], + pats: &[Pat], ctx: &str, ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) { let mut elems = Vec::with_capacity(pats.len()); @@ -209,7 +209,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// When encountering `($binding_mode $ident @)? ..` (`slice`), /// this is interpreted as a sub-slice pattern semantically. /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`. - fn lower_pat_slice(&mut self, pats: &[Box]) -> hir::PatKind<'hir> { + fn lower_pat_slice(&mut self, pats: &[Pat]) -> hir::PatKind<'hir> { let mut before = Vec::new(); let mut after = Vec::new(); let mut slice = None; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3fcf9da945070..36ce1b7204467 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1507,7 +1507,7 @@ impl<'a> TraitDef<'a> { struct_def: &'a VariantData, prefixes: &[String], by_ref: ByRef, - ) -> ThinVec> { + ) -> ThinVec { prefixes .iter() .map(|prefix| { @@ -1543,7 +1543,7 @@ impl<'a> TraitDef<'a> { attrs: ast::AttrVec::new(), id: ast::DUMMY_NODE_ID, span: pat.span.with_ctxt(self.span.ctxt()), - pat, + pat: Box::new(pat), is_placeholder: false, } }) diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 34faafdc07c61..dbd72f901997b 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -32,7 +32,7 @@ fn parse_pat_ty<'a>( let pat = pat_to_ty_pat( cx, - *parser.parse_pat_no_top_guard( + parser.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -59,7 +59,7 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> Box { include_end, ), ast::PatKind::Or(variants) => { - TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect()) + TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat)).collect()) } ast::PatKind::Err(guar) => TyPatKind::Err(guar), _ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")), diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index c3e86ec0614c7..55541cdf4d7e1 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -233,7 +233,7 @@ impl<'a> ExtCtxt<'a> { }; let local = Box::new(ast::Local { super_: None, - pat, + pat: Box::new(pat), ty, id: ast::DUMMY_NODE_ID, kind: LocalKind::Init(ex), @@ -249,7 +249,7 @@ impl<'a> ExtCtxt<'a> { pub fn stmt_let_type_only(&self, span: Span, ty: Box) -> ast::Stmt { let local = Box::new(ast::Local { super_: None, - pat: self.pat_wild(span), + pat: Box::new(self.pat_wild(span)), ty: Some(ty), id: ast::DUMMY_NODE_ID, kind: LocalKind::Decl, @@ -528,16 +528,16 @@ impl<'a> ExtCtxt<'a> { self.expr_match(sp, head, thin_vec![ok_arm, err_arm]) } - pub fn pat(&self, span: Span, kind: PatKind) -> Box { - Box::new(ast::Pat { id: ast::DUMMY_NODE_ID, kind, span, tokens: None }) + pub fn pat(&self, span: Span, kind: PatKind) -> ast::Pat { + ast::Pat { id: ast::DUMMY_NODE_ID, kind, span, tokens: None } } - pub fn pat_wild(&self, span: Span) -> Box { + pub fn pat_wild(&self, span: Span) -> ast::Pat { self.pat(span, PatKind::Wild) } - pub fn pat_lit(&self, span: Span, expr: Box) -> Box { + pub fn pat_lit(&self, span: Span, expr: Box) -> ast::Pat { self.pat(span, PatKind::Expr(expr)) } - pub fn pat_ident(&self, span: Span, ident: Ident) -> Box { + pub fn pat_ident(&self, span: Span, ident: Ident) -> ast::Pat { self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) } @@ -546,19 +546,19 @@ impl<'a> ExtCtxt<'a> { span: Span, ident: Ident, ann: ast::BindingMode, - ) -> Box { + ) -> ast::Pat { let pat = PatKind::Ident(ann, ident.with_span_pos(span), None); self.pat(span, pat) } - pub fn pat_path(&self, span: Span, path: ast::Path) -> Box { + pub fn pat_path(&self, span: Span, path: ast::Path) -> ast::Pat { self.pat(span, PatKind::Path(None, path)) } pub fn pat_tuple_struct( &self, span: Span, path: ast::Path, - subpats: ThinVec>, - ) -> Box { + subpats: ThinVec, + ) -> ast::Pat { self.pat(span, PatKind::TupleStruct(None, path, subpats)) } pub fn pat_struct( @@ -566,23 +566,23 @@ impl<'a> ExtCtxt<'a> { span: Span, path: ast::Path, field_pats: ThinVec, - ) -> Box { + ) -> ast::Pat { self.pat(span, PatKind::Struct(None, path, field_pats, ast::PatFieldsRest::None)) } - pub fn pat_tuple(&self, span: Span, pats: ThinVec>) -> Box { + pub fn pat_tuple(&self, span: Span, pats: ThinVec) -> ast::Pat { self.pat(span, PatKind::Tuple(pats)) } - pub fn pat_some(&self, span: Span, pat: Box) -> Box { + pub fn pat_some(&self, span: Span, pat: ast::Pat) -> ast::Pat { let some = self.std_path(&[sym::option, sym::Option, sym::Some]); let path = self.path_global(span, some); self.pat_tuple_struct(span, path, thin_vec![pat]) } - pub fn arm(&self, span: Span, pat: Box, expr: Box) -> ast::Arm { + pub fn arm(&self, span: Span, pat: ast::Pat, expr: Box) -> ast::Arm { ast::Arm { attrs: AttrVec::new(), - pat, + pat: Box::new(pat), guard: None, body: Some(expr), span, @@ -661,11 +661,11 @@ impl<'a> ExtCtxt<'a> { } pub fn param(&self, span: Span, ident: Ident, ty: Box) -> ast::Param { - let arg_pat = self.pat_ident(span, ident); + let pat = Box::new(self.pat_ident(span, ident)); ast::Param { attrs: AttrVec::default(), id: ast::DUMMY_NODE_ID, - pat: arg_pat, + pat, span, ty, is_placeholder: false, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3dfa3cdcc3560..8031e11afc752 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1152,12 +1152,12 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( + AstFragmentKind::Pat => AstFragment::Pat(Box::new(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, CommaRecoveryMode::LikelyTuple, - )?), + )?)), AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Arms | AstFragmentKind::ExprFields diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 8b87e4d969034..9e9a31d80474c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -73,6 +73,16 @@ pub(super) trait RecoverQPath: Sized + 'static { fn recovered(qself: Option>, path: ast::Path) -> Self; } +impl RecoverQPath for Box { + const PATH_STYLE: PathStyle = T::PATH_STYLE; + fn to_ty(&self) -> Option> { + T::to_ty(self) + } + fn recovered(qself: Option>, path: ast::Path) -> Self { + Box::new(T::recovered(qself, path)) + } +} + impl RecoverQPath for Ty { const PATH_STYLE: PathStyle = PathStyle::Type; fn to_ty(&self) -> Option> { @@ -1833,8 +1843,8 @@ impl<'a> Parser<'a> { /// tail, and combines them into a `::AssocItem` expression/pattern/type. pub(super) fn maybe_recover_from_bad_qpath( &mut self, - base: Box, - ) -> PResult<'a, Box> { + base: T, + ) -> PResult<'a, T> { if !self.may_recover() { return Ok(base); } @@ -1854,7 +1864,7 @@ impl<'a> Parser<'a> { &mut self, ty_span: Span, ty: Box, - ) -> PResult<'a, Box> { + ) -> PResult<'a, T> { self.expect(exp!(PathSep))?; let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; @@ -1867,7 +1877,7 @@ impl<'a> Parser<'a> { }); let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`. - Ok(Box::new(T::recovered(Some(Box::new(QSelf { ty, path_span, position: 0 })), path))) + Ok(T::recovered(Some(Box::new(QSelf { ty, path_span, position: 0 })), path)) } /// This function gets called in places where a semicolon is NOT expected and if there's a @@ -2742,9 +2752,9 @@ impl<'a> Parser<'a> { /// `for` loop, `let`, &c. (in contrast to subpatterns within such). pub(crate) fn maybe_recover_colon_colon_in_pat_typo( &mut self, - mut first_pat: Box, + mut first_pat: Pat, expected: Option, - ) -> Box { + ) -> Pat { if token::Colon != self.token.kind { return first_pat; } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8046abcd70bd0..57bd0d500d5af 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2587,7 +2587,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { - let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?; + let pat = Box::new(this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?); let ty = if this.eat(exp!(Colon)) { this.parse_ty()? } else { @@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> { let (expr, _) = self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?; let span = lo.to(expr.span); - Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) + Ok(self.mk_expr(span, ExprKind::Let(Box::new(pat), expr, span, recovered))) } /// Parses an `else { ... }` expression (`else` token already eaten). @@ -2897,7 +2897,7 @@ impl<'a> Parser<'a> { } // Public to use it for custom `for` expressions in rustfmt forks like https://github.com/tucant/rustfmt - pub fn parse_for_head(&mut self) -> PResult<'a, (Box, Box)> { + pub fn parse_for_head(&mut self) -> PResult<'a, (Pat, Box)> { let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` @@ -2974,6 +2974,7 @@ impl<'a> Parser<'a> { let kind = if is_await { ForLoopKind::ForAwait } else { ForLoopKind::For }; let (pat, expr) = self.parse_for_head()?; + let pat = Box::new(pat); // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) && self.token.kind != token::OpenBrace @@ -3142,7 +3143,7 @@ impl<'a> Parser<'a> { // Always push at least one arm to make the match non-empty arms.push(Arm { attrs: Default::default(), - pat: self.mk_pat(span, ast::PatKind::Err(guar)), + pat: Box::new(self.mk_pat(span, ast::PatKind::Err(guar))), guard: None, body: Some(self.mk_expr_err(span, guar)), span, @@ -3246,6 +3247,7 @@ impl<'a> Parser<'a> { self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let (pat, guard) = this.parse_match_arm_pat_and_guard()?; + let pat = Box::new(pat); let span_before_body = this.prev_token.span; let arm_body; @@ -3468,7 +3470,7 @@ impl<'a> Parser<'a> { Ok(Some(cond)) } - fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Box, Option>)> { + fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> { if self.token == token::OpenParen { let left = self.token.span; let pat = self.parse_pat_no_top_guard( diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index eb264f59fedbe..aa03d5956cd23 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -3111,7 +3111,7 @@ impl<'a> Parser<'a> { match ty { Ok(ty) => { let pat = this.mk_pat(ty.span, PatKind::Missing); - (pat, ty) + (Box::new(pat), ty) } // If this is a C-variadic argument and we hit an error, return the error. Err(err) if this.token == token::DotDotDot => return Err(err), diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 42eb07858c2fe..ea2b896f5fb9c 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -142,7 +142,8 @@ impl<'a> Parser<'a> { RecoverColon::No, CommaRecoveryMode::EitherTupleOrPipe, ), - })?, + }) + .map(Box::new)?, pat_kind, )), NonterminalKind::Expr(expr_kind) => { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index fda19d62bc774..3ef772af97dd6 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -107,7 +107,7 @@ impl<'a> Parser<'a> { rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, - ) -> PResult<'a, Box> { + ) -> PResult<'a, Pat> { let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; if self.eat_keyword(exp!(If)) { @@ -115,7 +115,7 @@ impl<'a> Parser<'a> { // Feature-gate guard patterns self.psess.gated_spans.gate(sym::guard_patterns, cond.span); let span = pat.span.to(cond.span); - Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + Ok(self.mk_pat(span, PatKind::Guard(Box::new(pat), cond))) } else { Ok(pat) } @@ -130,7 +130,7 @@ impl<'a> Parser<'a> { &mut self, expected: Option, syntax_loc: Option, - ) -> PResult<'a, Box> { + ) -> PResult<'a, Pat> { self.parse_pat_with_range_pat(true, expected, syntax_loc) } @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, - ) -> PResult<'a, Box> { + ) -> PResult<'a, Pat> { self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { ra: RecoverColon, rt: CommaRecoveryMode, syntax_loc: Option, - ) -> PResult<'a, (Box, bool)> { + ) -> PResult<'a, (Pat, bool)> { // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // suggestions (which bothers rustfix). // @@ -263,6 +263,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, Some(syntax_loc), )?; + let pat = Box::new(pat); let colon = self.eat(exp!(Colon)); if let PatKind::Or(pats) = &pat.kind { @@ -686,7 +687,7 @@ impl<'a> Parser<'a> { PatVisitor { parser: self, stmt, arm: None, field: None }.visit_stmt(stmt); } - fn eat_metavar_pat(&mut self) -> Option> { + fn eat_metavar_pat(&mut self) -> Option { // Must try both kinds of pattern nonterminals. if let Some(pat) = self.eat_metavar_seq_with_matcher( |mv_kind| matches!(mv_kind, MetaVarKind::Pat(PatParam { .. })), @@ -714,7 +715,7 @@ impl<'a> Parser<'a> { allow_range_pat: bool, expected: Option, syntax_loc: Option, - ) -> PResult<'a, Box> { + ) -> PResult<'a, Pat> { maybe_recover_from_interpolated_ty_qpath!(self, true); if let Some(pat) = self.eat_metavar_pat() { @@ -921,7 +922,7 @@ impl<'a> Parser<'a> { /// e.g. [F#][and] where they are called AND-patterns. /// /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching - fn recover_intersection_pat(&mut self, lhs: Box) -> PResult<'a, Box> { + fn recover_intersection_pat(&mut self, lhs: Pat) -> PResult<'a, Pat> { if self.token != token::At { // Next token is not `@` so it's not going to be an intersection pattern. return Ok(lhs); @@ -937,7 +938,7 @@ impl<'a> Parser<'a> { let lhs_span = lhs.span; // Move the LHS into the RHS as a subpattern. // The RHS is now the full pattern. - *sub = Some(lhs); + *sub = Some(Box::new(lhs)); self.dcx().emit_err(PatternOnWrongSideOfAt { whole_span, @@ -994,7 +995,7 @@ impl<'a> Parser<'a> { let mutbl = self.parse_mutability(); let subpat = self.parse_pat_with_range_pat(false, expected, None)?; - Ok(PatKind::Ref(subpat, mutbl)) + Ok(PatKind::Ref(Box::new(subpat), mutbl)) } /// Parse a tuple or parenthesis pattern. @@ -1052,7 +1053,7 @@ impl<'a> Parser<'a> { } // (pat) with optional parentheses - _ => PatKind::Paren(pat), + _ => PatKind::Paren(Box::new(pat)), } } else { PatKind::Tuple(fields) @@ -1103,7 +1104,7 @@ impl<'a> Parser<'a> { /// Turn all by-value immutable bindings in a pattern into mutable bindings. /// Returns `true` if any change was made. - fn make_all_value_bindings_mutable(pat: &mut Box) -> bool { + fn make_all_value_bindings_mutable(pat: &mut Pat) -> bool { struct AddMut(bool); impl MutVisitor for AddMut { fn visit_pat(&mut self, pat: &mut Pat) { @@ -1159,7 +1160,7 @@ impl<'a> Parser<'a> { &mut self, err: Diag<'a>, expected: Option, - ) -> PResult<'a, Box> { + ) -> PResult<'a, Pat> { err.cancel(); let expected = Expected::to_string_or_fallback(expected); @@ -1362,7 +1363,7 @@ impl<'a> Parser<'a> { } let sub = if self.eat(exp!(At)) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) + Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)) } else { None }; @@ -1451,12 +1452,14 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( - None, - RecoverComma::Yes, - RecoverColon::Yes, - CommaRecoveryMode::LikelyTuple, - )?)), + sym::deref => { + Some(ast::PatKind::Deref(Box::new(self_.parse_pat_allow_top_guard( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?))) + } _ => None, }) }) @@ -1478,14 +1481,14 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. let sub = if self.eat(exp!(At)) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) + Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)) } else { None }; Ok(PatKind::Ident(BindingMode::NONE, Ident::new(kw::Box, box_span), sub)) } else { - let pat = self.parse_pat_with_range_pat(false, None, None)?; + let pat = Box::new(self.parse_pat_with_range_pat(false, None, None)?); self.psess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); Ok(PatKind::Box(pat)) } @@ -1745,14 +1748,17 @@ impl<'a> Parser<'a> { hi = self.prev_token.span; let ann = BindingMode(by_ref, mutability); let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname); - let subpat = - if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; + let subpat = if is_box { + self.mk_pat(lo.to(hi), PatKind::Box(Box::new(fieldpat))) + } else { + fieldpat + }; (subpat, fieldname, true) }; Ok(PatField { ident: fieldname, - pat: subpat, + pat: Box::new(subpat), is_shorthand, attrs, id: ast::DUMMY_NODE_ID, @@ -1761,11 +1767,11 @@ impl<'a> Parser<'a> { }) } - pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingMode, ident: Ident) -> Box { + pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingMode, ident: Ident) -> Pat { self.mk_pat(span, PatKind::Ident(ann, ident, None)) } - pub(super) fn mk_pat(&self, span: Span, kind: PatKind) -> Box { - Box::new(Pat { kind, span, id: ast::DUMMY_NODE_ID, tokens: None }) + pub(super) fn mk_pat(&self, span: Span, kind: PatKind) -> Pat { + Pat { kind, span, id: ast::DUMMY_NODE_ID, tokens: None } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4d4acc60ca804..8c6e9f6fb3a40 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3664,7 +3664,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// pattern as a whole counts as a never pattern (since it's definitionallly unreachable). fn compute_and_check_or_pat_binding_map( &mut self, - pats: &[Box], + pats: &[Pat], ) -> Result, IsNeverPattern> { let mut missing_vars = FxIndexMap::default(); let mut inconsistent_vars = FxIndexMap::default(); @@ -3679,11 +3679,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .collect::>(); // 2) Record any missing bindings or binding mode inconsistencies. - for (map_outer, pat_outer) in not_never_pats.iter() { + for &(ref map_outer, pat_outer) in not_never_pats.iter() { // Check against all arms except for the same pattern which is always self-consistent. let inners = not_never_pats.iter().filter(|(_, pat)| pat.id != pat_outer.id); - for (map, pat) in inners { + for &(ref map, pat) in inners { for (&name, binding_inner) in map { match map_outer.get(&name) { None => { @@ -3695,8 +3695,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { target: Default::default(), could_be_path: name.as_str().starts_with(char::is_uppercase), }); - binding_error.origin.push((binding_inner.span, (***pat).clone())); - binding_error.target.push((***pat_outer).clone()); + binding_error.origin.push((binding_inner.span, pat.clone())); + binding_error.target.push(pat_outer.clone()); } Some(binding_outer) => { if binding_outer.annotation != binding_inner.annotation { diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index f3410c98973f5..5dd67e35eb18a 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -223,7 +223,7 @@ macro_rules! always_pat { /// Focus on `focus_idx` in `alternatives`, /// attempting to extend it with elements of the same constructor `C` /// in `alternatives[focus_idx + 1..]`. -fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: usize) -> bool { +fn transform_with_focus_on_idx(alternatives: &mut ThinVec, focus_idx: usize) -> bool { // Extract the kind; we'll need to make some changes in it. let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, Wild); // We'll focus on `alternatives[focus_idx]`, @@ -251,20 +251,20 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: Box(target) => extend_with_matching( target, start, alternatives, |k| matches!(k, Box(_)), - |k| always_pat!(k, Box(p) => p), + |k| always_pat!(k, Box(p) => *p), ), // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, |k| matches!(k, Ref(_, Mutability::Mut)), - |k| always_pat!(k, Ref(p, _) => p), + |k| always_pat!(k, Ref(p, _) => *p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. Ident(b1, i1, Some(target)) => extend_with_matching( target, start, alternatives, // Binding names must match. |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), - |k| always_pat!(k, Ident(_, _, Some(p)) => p), + |k| always_pat!(k, Ident(_, _, Some(p)) => *p), ), // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. Slice(ps1) => extend_with_matching_product( @@ -309,7 +309,7 @@ fn extend_with_struct_pat( fps1: &mut [ast::PatField], rest1: ast::PatFieldsRest, start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, ) -> bool { (0..fps1.len()).any(|idx| { let pos_in_2 = Cell::new(None); // The element `k`. @@ -339,7 +339,7 @@ fn extend_with_struct_pat( })) }, // Extract `p2_k`. - |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + |k| always_pat!(k, Struct(_, _, mut fps, _) => *fps.swap_remove(pos_in_2.take().unwrap()).pat), ); extend_with_tail_or(&mut fps1[idx].pat, tail_or) }) @@ -351,11 +351,11 @@ fn extend_with_struct_pat( /// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), /// where `~` denotes semantic equality. fn extend_with_matching_product( - targets: &mut [Box], + targets: &mut [Pat], start: usize, - alternatives: &mut ThinVec>, - predicate: impl Fn(&PatKind, &[Box], usize) -> bool, - extract: impl Fn(PatKind) -> ThinVec>, + alternatives: &mut ThinVec, + predicate: impl Fn(&PatKind, &[Pat], usize) -> bool, + extract: impl Fn(PatKind) -> ThinVec, ) -> bool { (0..targets.len()).any(|idx| { let tail_or = drain_matching( @@ -382,14 +382,14 @@ fn take_pat(from: &mut Pat) -> Pat { /// Extend `target` as an or-pattern with the alternatives /// in `tail_or` if there are any and return if there were. -fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec>) -> bool { - fn extend(target: &mut Pat, mut tail_or: ThinVec>) { +fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec) -> bool { + fn extend(target: &mut Pat, mut tail_or: ThinVec) { match target { // On an existing or-pattern in the target, append to it. Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), // Otherwise convert the target to an or-pattern. target => { - let mut init_or = thin_vec![Box::new(take_pat(target))]; + let mut init_or = thin_vec![take_pat(target)]; init_or.append(&mut tail_or); target.kind = Or(init_or); }, @@ -408,10 +408,10 @@ fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec>) -> bool { // Only elements beginning with `start` are considered for extraction. fn drain_matching( start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, predicate: impl Fn(&PatKind) -> bool, - extract: impl Fn(PatKind) -> Box, -) -> ThinVec> { + extract: impl Fn(PatKind) -> Pat, +) -> ThinVec { let mut tail_or = ThinVec::new(); let mut idx = 0; @@ -443,15 +443,15 @@ fn drain_matching( fn extend_with_matching( target: &mut Pat, start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, predicate: impl Fn(&PatKind) -> bool, - extract: impl Fn(PatKind) -> Box, + extract: impl Fn(PatKind) -> Pat, ) -> bool { extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) } /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? -fn eq_pre_post(ps1: &[Box], ps2: &[Box], idx: usize) -> bool { +fn eq_pre_post(ps1: &[Pat], ps2: &[Pat], idx: usize) -> bool { ps1.len() == ps2.len() && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 724d7a09e4afa..c063990dfa0b1 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -61,7 +61,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { Pat, NonterminalKind::Pat(PatParam { inferred: false }), |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), - |x: Box| Some(x) + |x: ast::Pat| Some(Box::new(x)) ); // `parse_item` returns `Option>`. parse_macro_arg!( diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 848bd0766e786..fa9a3e33914b6 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -504,7 +504,7 @@ impl Rewrite for PatField { #[derive(Debug)] pub(crate) enum TuplePatField<'a> { - Pat(&'a Box), + Pat(&'a ast::Pat), Dotdot(Span), } @@ -561,7 +561,7 @@ pub(crate) fn can_be_overflowed_pat( } fn rewrite_tuple_pat( - pats: &[Box], + pats: &[ast::Pat], path_str: Option, span: Span, context: &RewriteContext<'_>, From 703584604aa4ea39297f7e07051dfd40b43ab058 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 4 Sep 2025 20:00:46 -0500 Subject: [PATCH 1251/1900] Remove boxes from ast TyPat lists --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_builtin_macros/src/pattern_type.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 926fb61315818..0adf4f898ab6d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2579,7 +2579,7 @@ pub enum TyPatKind { /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), - Or(ThinVec>), + Or(ThinVec), /// Placeholder for a pattern that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 7470bd9abbc7f..ebd5aa6e93d83 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -391,7 +391,7 @@ macro_rules! common_visitor_and_walkers { ThinVec, ThinVec, ThinVec>, - ThinVec>, + ThinVec, ); // This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable` diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index dbd72f901997b..8b64bdf02d3b7 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -44,14 +44,14 @@ fn parse_pat_ty<'a>( parser.unexpected()?; } - Ok((ty, pat)) + Ok((ty, Box::new(pat))) } -fn ty_pat(kind: TyPatKind, span: Span) -> Box { - Box::new(TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None }) +fn ty_pat(kind: TyPatKind, span: Span) -> TyPat { + TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None } } -fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> Box { +fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat { let kind = match pat.kind { ast::PatKind::Range(start, end, include_end) => TyPatKind::Range( start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })), From c7d00075d9e65efbd7c5f9239d6ffe33ac3b937e Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Sat, 4 Oct 2025 13:00:09 -0500 Subject: [PATCH 1252/1900] Deduplicate sort+dedup calls --- .../rust-analyzer/src/cli/analysis_stats.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 36c4f281b7303..d365c56edb395 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -337,12 +337,15 @@ impl flags::AnalysisStats { } }); + file_ids.sort(); + file_ids.dedup(); + if self.run_all_ide_things { - self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); + self.run_ide_things(host.analysis(), &file_ids, db, &vfs, verbosity); } if self.run_term_search { - self.run_term_search(&workspace, db, &vfs, file_ids, verbosity); + self.run_term_search(&workspace, db, &vfs, &file_ids, verbosity); } let db = host.raw_database_mut(); @@ -438,12 +441,13 @@ impl flags::AnalysisStats { report_metric("const eval time", const_eval_time.time.as_millis() as u64, "ms"); } + /// Invariant: `file_ids` must be sorted and deduped before passing into here fn run_term_search( &self, ws: &ProjectWorkspace, db: &RootDatabase, vfs: &Vfs, - mut file_ids: Vec, + file_ids: &[EditionedFileId], verbosity: Verbosity, ) { let cargo_config = CargoConfig { @@ -461,9 +465,6 @@ impl flags::AnalysisStats { _ => ProgressReport::new(file_ids.len()), }; - file_ids.sort(); - file_ids.dedup(); - #[derive(Debug, Default)] struct Acc { tail_expr_syntax_hits: u64, @@ -477,7 +478,7 @@ impl flags::AnalysisStats { bar.tick(); let mut sw = self.stop_watch(); - for &file_id in &file_ids { + for &file_id in file_ids { let file_id = file_id.editioned_file_id(db); let sema = hir::Semantics::new(db); let display_target = match sema.first_crate(file_id.file_id()) { @@ -1109,10 +1110,11 @@ impl flags::AnalysisStats { report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms"); } + /// Invariant: `file_ids` must be sorted and deduped before passing into here fn run_ide_things( &self, analysis: Analysis, - mut file_ids: Vec, + file_ids: &[EditionedFileId], db: &RootDatabase, vfs: &Vfs, verbosity: Verbosity, @@ -1124,12 +1126,10 @@ impl flags::AnalysisStats { _ => ProgressReport::new(len), }; - file_ids.sort(); - file_ids.dedup(); let mut sw = self.stop_watch(); let mut bar = create_bar(); - for &file_id in &file_ids { + for &file_id in file_ids { let msg = format!("diagnostics: {}", vfs.file_path(file_id.file_id(db))); bar.set_message(move || msg.clone()); _ = analysis.full_diagnostics( @@ -1163,7 +1163,7 @@ impl flags::AnalysisStats { bar.finish_and_clear(); let mut bar = create_bar(); - for &file_id in &file_ids { + for &file_id in file_ids { let msg = format!("inlay hints: {}", vfs.file_path(file_id.file_id(db))); bar.set_message(move || msg.clone()); _ = analysis.inlay_hints( @@ -1206,7 +1206,7 @@ impl flags::AnalysisStats { bar.finish_and_clear(); let mut bar = create_bar(); - for &file_id in &file_ids { + for &file_id in file_ids { let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db))); bar.set_message(move || msg.clone()); analysis From 67bc0308336a115c3ea45130d9845a97f33b8196 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 4 Oct 2025 17:43:59 +0000 Subject: [PATCH 1253/1900] change flt back to ftl --- compiler/rustc_attr_parsing/messages.ftl | 157 ++++++++++----------- compiler/rustc_builtin_macros/messages.ftl | 18 +-- compiler/rustc_codegen_ssa/messages.ftl | 4 +- compiler/rustc_const_eval/messages.ftl | 8 +- compiler/rustc_expand/messages.ftl | 10 +- compiler/rustc_lint/messages.ftl | 16 +-- compiler/rustc_metadata/messages.ftl | 25 ++-- compiler/rustc_middle/messages.ftl | 6 +- compiler/rustc_parse/messages.ftl | 11 +- compiler/rustc_passes/messages.ftl | 8 +- compiler/rustc_resolve/messages.ftl | 12 +- compiler/rustc_ty_utils/messages.ftl | 4 - src/tools/tidy/src/fluent_alphabetical.rs | 2 +- 13 files changed, 133 insertions(+), 148 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 6c5346e83554b..a432fa099ef4a 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,3 +1,9 @@ +attr_parsing_as_needed_compatibility = + linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + +attr_parsing_bundle_needs_static = + linking modifier `bundle` is only compatible with `static` linking kind + attr_parsing_cfg_predicate_identifier = `cfg` predicate key must be an identifier @@ -18,16 +24,12 @@ attr_parsing_empty_attribute = } -attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute -attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} - .warn = {-attr_parsing_previously_accepted} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - attr_parsing_empty_confusables = expected at least one confusable name +attr_parsing_empty_link_name = + link name must not be empty + .label = empty link name + attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern @@ -48,6 +50,15 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions -> *[other] valid forms for the attribute are {$suggestions} } +attr_parsing_import_name_type_raw = + import name type can only be used with link kind `raw-dylib` + +attr_parsing_import_name_type_x86 = + import name type is only supported on x86 + +attr_parsing_incompatible_wasm_link = + `wasm_import_module` is incompatible with other arguments in `#[link]` attributes + attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -67,6 +78,11 @@ attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = attr_parsing_invalid_alignment_value = invalid alignment value: {$error_part} +attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .label = this is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead @@ -75,6 +91,13 @@ attr_parsing_invalid_issue_string = .pos_overflow = number too large to fit in target type .neg_overflow = number too small to fit in target type +attr_parsing_invalid_link_modifier = + invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed + +attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} + .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign + .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal + attr_parsing_invalid_predicate = invalid predicate `{$predicate}` @@ -100,9 +123,36 @@ attr_parsing_invalid_style = {$is_used_as_inner -> } .note = This attribute does not have an `!`, which means it is applied to this {$target} +attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} + .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute +attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} + .warn = {-attr_parsing_previously_accepted} + .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute + +attr_parsing_limit_invalid = + `limit` must be a non-negative integer + .label = {$error_str} +attr_parsing_link_arg_unstable = + link kind `link-arg` is unstable + +attr_parsing_link_cfg_unstable = + link cfg is unstable + +attr_parsing_link_framework_apple = + link kind `framework` is only supported on Apple targets + attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` .note = the value may not exceed `u16::MAX` +attr_parsing_link_requires_name = + `#[link]` attribute requires a `name = "string"` argument + .label = missing `name` argument + +attr_parsing_meta_bad_delim = wrong meta list delimiters +attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` + attr_parsing_missing_feature = missing 'feature' @@ -115,6 +165,9 @@ attr_parsing_missing_note = attr_parsing_missing_since = missing 'since' +attr_parsing_multiple_modifiers = + multiple `{$modifier}` modifiers in a single `modifiers` argument + attr_parsing_multiple_stability_levels = multiple stability levels @@ -138,6 +191,15 @@ attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a stri attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal +attr_parsing_raw_dylib_elf_unstable = + link kind `raw-dylib` is unstable on ELF platforms + +attr_parsing_raw_dylib_no_nul = + link name must not contain NUL characters if link kind is `raw-dylib` + +attr_parsing_raw_dylib_only_windows = + link kind `raw-dylib` is only supported on Windows targets + attr_parsing_repr_ident = meta item in `repr` must be an identifier @@ -152,6 +214,9 @@ attr_parsing_soft_no_args = attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library +attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes + .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} @@ -164,6 +229,10 @@ attr_parsing_unrecognized_repr_hint = .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` .note = for more information, visit +attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change @@ -193,77 +262,5 @@ attr_parsing_unused_multiple = -attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -attr_parsing_meta_bad_delim = wrong meta list delimiters -attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` - -attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - -attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} - .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - -attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds - -attr_parsing_bundle_needs_static = - linking modifier `bundle` is only compatible with `static` linking kind - -attr_parsing_empty_link_name = - link name must not be empty - .label = empty link name - -attr_parsing_import_name_type_raw = - import name type can only be used with link kind `raw-dylib` - -attr_parsing_import_name_type_x86 = - import name type is only supported on x86 - -attr_parsing_incompatible_wasm_link = - `wasm_import_module` is incompatible with other arguments in `#[link]` attributes - -attr_parsing_invalid_link_modifier = - invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed - -attr_parsing_link_arg_unstable = - link kind `link-arg` is unstable - -attr_parsing_link_cfg_unstable = - link cfg is unstable - -attr_parsing_link_framework_apple = - link kind `framework` is only supported on Apple targets - -attr_parsing_link_requires_name = - `#[link]` attribute requires a `name = "string"` argument - .label = missing `name` argument - -attr_parsing_multiple_modifiers = - multiple `{$modifier}` modifiers in a single `modifiers` argument - -attr_parsing_multiple_renamings = - multiple renamings were specified for library `{$lib_name}` -attr_parsing_raw_dylib_no_nul = - link name must not contain NUL characters if link kind is `raw-dylib` - -attr_parsing_raw_dylib_elf_unstable = - link kind `raw-dylib` is unstable on ELF platforms - -attr_parsing_raw_dylib_only_windows = - link kind `raw-dylib` is only supported on Windows targets - attr_parsing_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind - -attr_parsing_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 7c1a5f44e165d..cbdd8199d7ba3 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -135,6 +135,15 @@ builtin_macros_concat_missing_literal = expected a literal builtin_macros_default_arg = `#[default]` attribute does not accept a value .suggestion = try using `#[default]` +builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field + +builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields -> + [true] multiple fields + *[false] no fields +} + +builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind} + builtin_macros_derive_macro_call = `derive` cannot be used on items with type macros builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept arguments @@ -229,15 +238,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments builtin_macros_format_use_positional = consider using a positional formatting argument instead -builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind} - -builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields -> - [true] multiple fields - *[false] no fields -} - -builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field - builtin_macros_incomplete_include = include macro expected single expression in source builtin_macros_multiple_default_attrs = multiple `#[default]` attributes diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 91c3806df4c34..97ff04f66daec 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -97,10 +97,10 @@ codegen_ssa_invalid_literal_value = invalid literal value codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` -codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` - codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}` +codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` + codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}` diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 010ffa60c7a53..13a49e80cf2dc 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -231,9 +231,6 @@ const_eval_mutable_borrow_escaping = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} -const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} - .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value - const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_await = @@ -302,6 +299,9 @@ const_eval_panic = evaluation panicked: {$msg} const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` +const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} + .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + const_eval_partial_pointer_read = unable to read parts of a pointer from memory at {$ptr} const_eval_pointer_arithmetic_overflow = @@ -476,6 +476,7 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` +const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null @@ -485,7 +486,6 @@ const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null } reference -const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 47c00bff5c950..e914f24e294cd 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -95,6 +95,8 @@ expand_malformed_feature_attribute = malformed `feature` attribute input .expected = expected just one word +expand_meta_var_dif_seq_matchers = {$msg} + expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth .label = expected repetition @@ -102,8 +104,6 @@ expand_metavariable_wrong_operator = meta-variable repeats with different Kleene .binder_label = expected repetition .occurrence_label = conflicting repetition -expand_meta_var_dif_seq_matchers = {$msg} - expand_missing_fragment_specifier = missing fragment specifier .note = fragment specifiers must be provided .suggestion_add_fragspec = try adding a specifier here @@ -198,12 +198,12 @@ expand_trailing_semi_macro = trailing semicolon in macro used in expression posi expand_unknown_macro_variable = unknown macro variable `{$name}` +expand_unsupported_key_value = + key-value macro attributes are not supported + expand_unused_builtin_attribute = unused attribute `{$attr_name}` .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` .suggestion = remove the attribute -expand_unsupported_key_value = - key-value macro attributes are not supported - expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5b98c9fafaad4..bc0a98fc7aa8a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -392,6 +392,14 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance + .note = this is dangerous because dereferencing the resulting pointer is undefined behavior + .note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + .help_transmute = for more information about transmute, see + .help_exposed_provenance = for more information about exposed provenance, see + .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance + .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -439,14 +447,6 @@ lint_invalid_reference_casting_note_book = for more information, visit - .help_exposed_provenance = for more information about exposed provenance, see - .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance - .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` - lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index e624bfc5b8bda..fac7b6c21f60c 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -98,13 +98,6 @@ metadata_full_metadata_not_found = metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait -metadata_incompatible_with_immediate_abort = - the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` - -metadata_incompatible_with_immediate_abort_core = - the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` - .help = consider building the standard library from source with `cargo build -Zbuild-std` - metadata_incompatible_panic_in_drop_strategy = the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` @@ -132,6 +125,13 @@ metadata_incompatible_target_modifiers_r_missed = .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}` .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +metadata_incompatible_with_immediate_abort = + the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` + +metadata_incompatible_with_immediate_abort_core = + the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + .help = consider building the standard library from source with `cargo build -Zbuild-std` + metadata_install_missing_components = maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview` @@ -197,6 +197,8 @@ metadata_prev_alloc_error_handler = metadata_prev_global_alloc = previous global allocator defined here +metadata_raw_dylib_malformed = + link name must be well-formed if link kind is `raw-dylib` metadata_raw_dylib_unsupported_abi = ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture @@ -236,12 +238,3 @@ metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$fla metadata_wasm_c_abi = older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 - -metadata_wasm_import_form = - wasm import module must be of the form `wasm_import_module = "string"` - -metadata_whole_archive_needs_static = - linking modifier `whole-archive` is only compatible with `static` linking kind - -metadata_raw_dylib_malformed = - link name must be well-formed if link kind is `raw-dylib` diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 82f3df8da3b12..b46f43841c8e8 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -95,15 +95,15 @@ middle_layout_normalization_failure = middle_layout_references_error = the type has an unknown layout -middle_layout_size_overflow = - values of the type `{$ty}` are too big for the target architecture - middle_layout_simd_too_many = the SIMD type `{$ty}` has more elements than the limit {$max_lanes} middle_layout_simd_zero_length = the SIMD type `{$ty}` has zero elements +middle_layout_size_overflow = + values of the type `{$ty}` are too big for the target architecture + middle_layout_too_generic = the type `{$ty}` does not have a fixed layout middle_layout_unknown = diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f83cf645f829d..b1894ab92192e 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -68,19 +68,19 @@ parse_attr_after_generic = trailing attribute after generic parameter parse_attr_without_generics = attribute without generic parameters .label = attributes are only permitted when preceding parameters -parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type +parse_attribute_on_empty_type = attributes cannot be applied here .label = attributes are not allowed here -parse_attribute_on_type = attributes cannot be applied to types +parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments .label = attributes are not allowed here .suggestion = remove attribute from here -parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments +parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type .label = attributes are not allowed here - .suggestion = remove attribute from here -parse_attribute_on_empty_type = attributes cannot be applied here +parse_attribute_on_type = attributes cannot be applied to types .label = attributes are not allowed here + .suggestion = remove attribute from here parse_bad_assoc_type_bounds = bounds on associated types do not belong here .label = belongs in `where` clause @@ -868,7 +868,6 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up parse_too_short_hex_escape = numeric character escape is too short parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern -parse_trailing_vert_not_allowed_suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_const = trait aliases cannot be `const` diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index df4016dfa1b82..9971f0c3fa32e 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -13,10 +13,6 @@ passes_abi_ne = passes_abi_of = fn_abi_of({$fn_name}) = {$fn_abi} -passes_macro_only_attribute = - attribute should be applied to a macro - .label = not a macro - passes_attr_application_enum = attribute should be applied to an enum .label = not an enum @@ -392,6 +388,10 @@ passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items +passes_macro_only_attribute = + attribute should be applied to a macro + .label = not a macro + passes_may_dangle = `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index cc8fbc18b7377..5bf90d2637df5 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -93,9 +93,6 @@ resolve_consider_adding_a_derive = resolve_consider_adding_macro_export = consider adding a `#[macro_export]` to the macro in the imported module -resolve_consider_marking_as_pub_crate = - in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)` - resolve_consider_declaring_with_pub = consider declaring type or module `{$ident}` with `pub` @@ -108,6 +105,9 @@ resolve_consider_making_the_field_public = resolve_consider_marking_as_pub = consider marking `{$ident}` as `pub` in the imported module +resolve_consider_marking_as_pub_crate = + in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)` + resolve_consider_move_macro_position = consider moving the definition of `{$ident}` before this call @@ -248,15 +248,15 @@ resolve_lowercase_self = attempt to use a non-constant value in a constant .suggestion = try using `Self` -resolve_macro_cannot_use_as_fn_like = - `{$ident}` exists, but has no rules for function-like invocation - resolve_macro_cannot_use_as_attr = `{$ident}` exists, but has no `attr` rules resolve_macro_cannot_use_as_derive = `{$ident}` exists, but has no `derive` rules +resolve_macro_cannot_use_as_fn_like = + `{$ident}` exists, but has no rules for function-like invocation + resolve_macro_defined_later = a macro with the same name exists, but it appears later diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl index 8bc7bf10865f5..c1684bfb43b66 100644 --- a/compiler/rustc_ty_utils/messages.ftl +++ b/compiler/rustc_ty_utils/messages.ftl @@ -52,8 +52,6 @@ ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-p ty_utils_operation_not_supported = unsupported operation in generic constants -ty_utils_oversized_simd_type = monomorphising SIMD type `{$ty}` of length greater than {$max_lanes} - ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants ty_utils_tuple_not_supported = tuple construction is not supported in generic constants @@ -61,5 +59,3 @@ ty_utils_tuple_not_supported = tuple construction is not supported in generic co ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants - -ty_utils_zero_length_simd_type = monomorphising SIMD type `{$ty}` of zero length diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 02b914c21aebf..b1d1c16b4549c 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -15,7 +15,7 @@ fn message() -> &'static Regex { } fn is_fluent(path: &Path) -> bool { - path.extension().is_some_and(|ext| ext == "flt") + path.extension().is_some_and(|ext| ext == "ftl") } fn check_alphabetic( From 0ea65e0bcd98403bddf49ed4641ec47c32624ad5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 4 Oct 2025 10:37:22 -0500 Subject: [PATCH 1254/1900] Break out some parsing cold functions This recovers some instruction count regressions after changing most Pat parsing functions return a non-boxed Pat. It seems to be beneficial to break out a #[cold] parsing recovery function when the function includes more parsing, presumably because this requires more stack space and/or mem copies when LLVM optimizes the wrong path. --- .../rustc_parse/src/parser/diagnostics.rs | 24 ++++++++++++------- compiler/rustc_parse/src/parser/pat.rs | 21 +++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9e9a31d80474c..c36cd610c6a59 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1845,15 +1845,17 @@ impl<'a> Parser<'a> { &mut self, base: T, ) -> PResult<'a, T> { - if !self.may_recover() { - return Ok(base); + // Do not add `::` to expected tokens. + if self.may_recover() && self.token == token::PathSep { + return self.recover_from_bad_qpath(base); } + Ok(base) + } - // Do not add `::` to expected tokens. - if self.token == token::PathSep { - if let Some(ty) = base.to_ty() { - return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); - } + #[cold] + fn recover_from_bad_qpath(&mut self, base: T) -> PResult<'a, T> { + if let Some(ty) = base.to_ty() { + return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); } Ok(base) } @@ -2370,6 +2372,7 @@ impl<'a> Parser<'a> { None } + #[cold] pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (Box, Box)> { let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; self.expect(exp!(Colon))?; @@ -2750,7 +2753,8 @@ impl<'a> Parser<'a> { /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). - pub(crate) fn maybe_recover_colon_colon_in_pat_typo( + #[cold] + pub(crate) fn recover_colon_colon_in_pat_typo( &mut self, mut first_pat: Pat, expected: Option, @@ -2935,7 +2939,11 @@ impl<'a> Parser<'a> { if self.token != token::Comma { return Ok(()); } + self.recover_unexpected_comma(lo, rt) + } + #[cold] + fn recover_unexpected_comma(&mut self, lo: Span, rt: CommaRecoveryMode) -> PResult<'a, ()> { // An unexpected comma after a top-level pattern is a clue that the // user (perhaps more accustomed to some other language) forgot the // parentheses in what should have been a tuple pattern; return a diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 3ef772af97dd6..4f522d57e4140 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -199,8 +199,8 @@ impl<'a> Parser<'a> { // This complicated procedure is done purely for diagnostics UX. // Check if the user wrote `foo:bar` instead of `foo::bar`. - if ra == RecoverColon::Yes { - first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected); + if ra == RecoverColon::Yes && token::Colon == self.token.kind { + first_pat = self.recover_colon_colon_in_pat_typo(first_pat, expected); } if let Some(leading_vert_span) = leading_vert_span { @@ -874,9 +874,12 @@ impl<'a> Parser<'a> { } }; - let pat = self.mk_pat(lo.to(self.prev_token.span), pat); - let pat = self.maybe_recover_from_bad_qpath(pat)?; - let pat = self.recover_intersection_pat(pat)?; + let mut pat = self.mk_pat(lo.to(self.prev_token.span), pat); + + pat = self.maybe_recover_from_bad_qpath(pat)?; + if self.eat_noexpect(&token::At) { + pat = self.recover_intersection_pat(pat)?; + } if !allow_range_pat { self.ban_pat_range_if_ambiguous(&pat) @@ -922,14 +925,8 @@ impl<'a> Parser<'a> { /// e.g. [F#][and] where they are called AND-patterns. /// /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching + #[cold] fn recover_intersection_pat(&mut self, lhs: Pat) -> PResult<'a, Pat> { - if self.token != token::At { - // Next token is not `@` so it's not going to be an intersection pattern. - return Ok(lhs); - } - - // At this point we attempt to parse `@ $pat_rhs` and emit an error. - self.bump(); // `@` let mut rhs = self.parse_pat_no_top_alt(None, None)?; let whole_span = lhs.span.to(rhs.span); From 775b893b5ce80a33ae4ace718a13fb87f31b2e1d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 22 Sep 2025 08:56:15 -0500 Subject: [PATCH 1255/1900] Use ty reference --- compiler/rustc_lint/src/lifetime_syntax.rs | 35 ++++++---------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 413525eb6e514..ed253c6a26d23 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -517,9 +517,8 @@ fn build_mismatch_suggestion( #[derive(Debug)] struct Info<'tcx> { - type_span: Span, - referenced_type_span: Option, lifetime: &'tcx hir::Lifetime, + ty: &'tcx hir::Ty<'tcx>, } impl<'tcx> Info<'tcx> { @@ -543,7 +542,7 @@ impl<'tcx> Info<'tcx> { /// to include the type. Otherwise we end up pointing at nothing, /// which is a bit confusing. fn reporting_span(&self) -> Span { - if self.lifetime.is_implicit() { self.type_span } else { self.lifetime.ident.span } + if self.lifetime.is_implicit() { self.ty.span } else { self.lifetime.ident.span } } /// When removing an explicit lifetime from a reference, @@ -561,11 +560,9 @@ impl<'tcx> Info<'tcx> { // FIXME: Ideally, we'd also remove the lifetime declaration. fn removing_span(&self) -> Span { let mut span = self.suggestion("'dummy").0; - - if let Some(referenced_type_span) = self.referenced_type_span { - span = span.until(referenced_type_span); + if let hir::TyKind::Ref(_, mut_ty) = self.ty.kind { + span = span.until(mut_ty.ty.span); } - span } @@ -577,14 +574,13 @@ impl<'tcx> Info<'tcx> { type LifetimeInfoMap<'tcx> = FxIndexMap<&'tcx hir::LifetimeKind, Vec>>; struct LifetimeInfoCollector<'a, 'tcx> { - type_span: Span, - referenced_type_span: Option, map: &'a mut LifetimeInfoMap<'tcx>, + ty: &'tcx hir::Ty<'tcx>, } impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> { fn collect(ty: &'tcx hir::Ty<'tcx>, map: &'a mut LifetimeInfoMap<'tcx>) { - let mut this = Self { type_span: ty.span, referenced_type_span: None, map }; + let mut this = Self { map, ty }; intravisit::walk_unambig_ty(&mut this, ty); } @@ -593,27 +589,14 @@ impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> { #[instrument(skip(self))] fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) { - let type_span = self.type_span; - let referenced_type_span = self.referenced_type_span; - - let info = Info { type_span, referenced_type_span, lifetime }; - + let info = Info { lifetime, ty: self.ty }; self.map.entry(&lifetime.kind).or_default().push(info); } #[instrument(skip(self))] fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) -> Self::Result { - let old_type_span = self.type_span; - let old_referenced_type_span = self.referenced_type_span; - - self.type_span = ty.span; - if let hir::TyKind::Ref(_, ty) = ty.kind { - self.referenced_type_span = Some(ty.ty.span); - } - + let old_ty = std::mem::replace(&mut self.ty, ty.as_unambig_ty()); intravisit::walk_ty(self, ty); - - self.type_span = old_type_span; - self.referenced_type_span = old_referenced_type_span; + self.ty = old_ty; } } From c915c989f670590bf81e5b87bf745678c6b9a4d3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 22 Sep 2025 09:07:54 -0500 Subject: [PATCH 1256/1900] Eagerly evaluate lifetime syntax category --- compiler/rustc_lint/src/lifetime_syntax.rs | 42 ++++++---------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index ed253c6a26d23..5a8a324d846da 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -148,11 +148,11 @@ enum LifetimeSyntaxCategory { } impl LifetimeSyntaxCategory { - fn new(syntax_source: (hir::LifetimeSyntax, LifetimeSource)) -> Option { + fn new(lifetime: &hir::Lifetime) -> Option { use LifetimeSource::*; use hir::LifetimeSyntax::*; - match syntax_source { + match (lifetime.syntax, lifetime.source) { // E.g. `&T`. (Implicit, Reference) | // E.g. `&'_ T`. @@ -233,22 +233,8 @@ impl std::ops::Add for LifetimeSyntaxCategories { } fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool { - let mut syntax_counts = LifetimeSyntaxCategories::::default(); - - for info in input_info.iter().chain(output_info) { - if let Some(category) = info.lifetime_syntax_category() { - *syntax_counts.select(category) += 1; - } - } - - tracing::debug!(?syntax_counts); - - matches!( - syntax_counts, - LifetimeSyntaxCategories { hidden: _, elided: 0, named: 0 } - | LifetimeSyntaxCategories { hidden: 0, elided: _, named: 0 } - | LifetimeSyntaxCategories { hidden: 0, elided: 0, named: _ } - ) + let (first, inputs) = input_info.split_first().unwrap(); + std::iter::chain(inputs, output_info).all(|info| info.syntax_category == first.syntax_category) } fn emit_mismatch_diagnostic<'tcx>( @@ -312,11 +298,6 @@ fn emit_mismatch_diagnostic<'tcx>( let syntax_source = info.syntax_source(); - if let (_, Other) = syntax_source { - // Ignore any other kind of lifetime. - continue; - } - if let (ExplicitBound, _) = syntax_source { bound_lifetime = Some(info); } @@ -393,9 +374,7 @@ fn emit_mismatch_diagnostic<'tcx>( let categorize = |infos: &[Info<'_>]| { let mut categories = LifetimeSyntaxCategories::>::default(); for info in infos { - if let Some(category) = info.lifetime_syntax_category() { - categories.select(category).push(info.reporting_span()); - } + categories.select(info.syntax_category).push(info.reporting_span()); } categories }; @@ -518,6 +497,7 @@ fn build_mismatch_suggestion( #[derive(Debug)] struct Info<'tcx> { lifetime: &'tcx hir::Lifetime, + syntax_category: LifetimeSyntaxCategory, ty: &'tcx hir::Ty<'tcx>, } @@ -526,10 +506,6 @@ impl<'tcx> Info<'tcx> { (self.lifetime.syntax, self.lifetime.source) } - fn lifetime_syntax_category(&self) -> Option { - LifetimeSyntaxCategory::new(self.syntax_source()) - } - fn lifetime_name(&self) -> &str { self.lifetime.ident.as_str() } @@ -589,8 +565,10 @@ impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> { #[instrument(skip(self))] fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) { - let info = Info { lifetime, ty: self.ty }; - self.map.entry(&lifetime.kind).or_default().push(info); + if let Some(syntax_category) = LifetimeSyntaxCategory::new(lifetime) { + let info = Info { lifetime, syntax_category, ty: self.ty }; + self.map.entry(&lifetime.kind).or_default().push(info); + } } #[instrument(skip(self))] From ba5a42b48b0fe35a98f82ab982745e12ee87966c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 22 Sep 2025 08:24:30 -0500 Subject: [PATCH 1257/1900] LifetimeSyntax tweak data structure --- compiler/rustc_lint/src/lifetime_syntax.rs | 74 +++++++++++++--------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 5a8a324d846da..bcf8175cdf2e1 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -111,35 +111,47 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax { } fn check_fn_like<'tcx>(cx: &LateContext<'tcx>, fd: &'tcx hir::FnDecl<'tcx>) { - let mut input_map = Default::default(); - let mut output_map = Default::default(); - - for input in fd.inputs { - LifetimeInfoCollector::collect(input, &mut input_map); + if fd.inputs.is_empty() { + return; } + let hir::FnRetTy::Return(output) = fd.output else { + return; + }; - if let hir::FnRetTy::Return(output) = fd.output { - LifetimeInfoCollector::collect(output, &mut output_map); - } + let mut map: FxIndexMap> = FxIndexMap::default(); - report_mismatches(cx, &input_map, &output_map); -} + LifetimeInfoCollector::collect(output, |info| { + let group = map.entry(info.lifetime.kind).or_default(); + group.outputs.push(info); + }); + if map.is_empty() { + return; + } -#[instrument(skip_all)] -fn report_mismatches<'tcx>( - cx: &LateContext<'tcx>, - inputs: &LifetimeInfoMap<'tcx>, - outputs: &LifetimeInfoMap<'tcx>, -) { - for (resolved_lifetime, output_info) in outputs { - if let Some(input_info) = inputs.get(resolved_lifetime) { - if !lifetimes_use_matched_syntax(input_info, output_info) { - emit_mismatch_diagnostic(cx, input_info, output_info); + for input in fd.inputs { + LifetimeInfoCollector::collect(input, |info| { + if let Some(group) = map.get_mut(&info.lifetime.kind) { + group.inputs.push(info); } + }); + } + + for LifetimeGroup { ref inputs, ref outputs } in map.into_values() { + if inputs.is_empty() { + continue; + } + if !lifetimes_use_matched_syntax(inputs, outputs) { + emit_mismatch_diagnostic(cx, inputs, outputs); } } } +#[derive(Default)] +struct LifetimeGroup<'tcx> { + inputs: Vec>, + outputs: Vec>, +} + #[derive(Debug, Copy, Clone, PartialEq)] enum LifetimeSyntaxCategory { Hidden, @@ -547,27 +559,31 @@ impl<'tcx> Info<'tcx> { } } -type LifetimeInfoMap<'tcx> = FxIndexMap<&'tcx hir::LifetimeKind, Vec>>; - -struct LifetimeInfoCollector<'a, 'tcx> { - map: &'a mut LifetimeInfoMap<'tcx>, +struct LifetimeInfoCollector<'tcx, F> { + info_func: F, ty: &'tcx hir::Ty<'tcx>, } -impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> { - fn collect(ty: &'tcx hir::Ty<'tcx>, map: &'a mut LifetimeInfoMap<'tcx>) { - let mut this = Self { map, ty }; +impl<'tcx, F> LifetimeInfoCollector<'tcx, F> +where + F: FnMut(Info<'tcx>), +{ + fn collect(ty: &'tcx hir::Ty<'tcx>, info_func: F) { + let mut this = Self { info_func, ty }; intravisit::walk_unambig_ty(&mut this, ty); } } -impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> { +impl<'tcx, F> Visitor<'tcx> for LifetimeInfoCollector<'tcx, F> +where + F: FnMut(Info<'tcx>), +{ #[instrument(skip(self))] fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) { if let Some(syntax_category) = LifetimeSyntaxCategory::new(lifetime) { let info = Info { lifetime, syntax_category, ty: self.ty }; - self.map.entry(&lifetime.kind).or_default().push(info); + (self.info_func)(info); } } From f0eea4cc4f6f87b70bf6e29382a21ead33687b84 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 22 Sep 2025 08:37:01 -0500 Subject: [PATCH 1258/1900] LifetimeSyntax little things --- compiler/rustc_lint/src/lifetime_syntax.rs | 66 +++++++--------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index bcf8175cdf2e1..0cac91c234080 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -3,6 +3,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, LifetimeSource}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use tracing::instrument; use crate::{LateContext, LateLintPass, LintContext, lints}; @@ -78,11 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: hir::intravisit::FnKind<'tcx>, + _: intravisit::FnKind<'tcx>, fd: &'tcx hir::FnDecl<'tcx>, _: &'tcx hir::Body<'tcx>, - _: rustc_span::Span, - _: rustc_span::def_id::LocalDefId, + _: Span, + _: LocalDefId, ) { check_fn_like(cx, fd); } @@ -97,11 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax { } #[instrument(skip_all)] - fn check_foreign_item( - &mut self, - cx: &LateContext<'tcx>, - fi: &'tcx rustc_hir::ForeignItem<'tcx>, - ) { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, fi: &'tcx hir::ForeignItem<'tcx>) { match fi.kind { hir::ForeignItemKind::Fn(fn_sig, _idents, _generics) => check_fn_like(cx, fn_sig.decl), hir::ForeignItemKind::Static(..) => {} @@ -228,7 +225,7 @@ impl LifetimeSyntaxCategories> { pub fn iter_unnamed(&self) -> impl Iterator { let Self { hidden, elided, named: _ } = self; - [hidden.iter(), elided.iter()].into_iter().flatten() + std::iter::chain(hidden, elided) } } @@ -308,13 +305,13 @@ fn emit_mismatch_diagnostic<'tcx>( use LifetimeSource::*; use hir::LifetimeSyntax::*; - let syntax_source = info.syntax_source(); + let lifetime = info.lifetime; - if let (ExplicitBound, _) = syntax_source { + if lifetime.syntax == ExplicitBound { bound_lifetime = Some(info); } - match syntax_source { + match (lifetime.syntax, lifetime.source) { // E.g. `&T`. (Implicit, Reference) => { suggest_change_to_explicit_anonymous.push(info); @@ -334,8 +331,8 @@ fn emit_mismatch_diagnostic<'tcx>( suggest_change_to_explicit_bound.push(info); } - // E.g. `ContainsLifetime<'_>`. - (ExplicitAnonymous, Path { .. }) => { + // E.g. `ContainsLifetime<'_>`, `+ '_`, `+ use<'_>`. + (ExplicitAnonymous, Path { .. } | OutlivesBound | PreciseCapturing) => { suggest_change_to_explicit_bound.push(info); } @@ -346,8 +343,8 @@ fn emit_mismatch_diagnostic<'tcx>( suggest_change_to_explicit_anonymous.push(info); } - // E.g. `ContainsLifetime<'a>`. - (ExplicitBound, Path { .. }) => { + // E.g. `ContainsLifetime<'a>`, `+ 'a`, `+ use<'a>`. + (ExplicitBound, Path { .. } | OutlivesBound | PreciseCapturing) => { suggest_change_to_mixed_explicit_anonymous.push(info); suggest_change_to_explicit_anonymous.push(info); } @@ -356,29 +353,18 @@ fn emit_mismatch_diagnostic<'tcx>( panic!("This syntax / source combination is not possible"); } - // E.g. `+ '_`, `+ use<'_>`. - (ExplicitAnonymous, OutlivesBound | PreciseCapturing) => { - suggest_change_to_explicit_bound.push(info); - } - - // E.g. `+ 'a`, `+ use<'a>`. - (ExplicitBound, OutlivesBound | PreciseCapturing) => { - suggest_change_to_mixed_explicit_anonymous.push(info); - suggest_change_to_explicit_anonymous.push(info); - } - (_, Other) => { panic!("This syntax / source combination has already been skipped"); } } - if matches!(syntax_source, (_, Path { .. } | OutlivesBound | PreciseCapturing)) { + if matches!(lifetime.source, Path { .. } | OutlivesBound | PreciseCapturing) { allow_suggesting_implicit = false; } - match syntax_source { - (_, Reference) => saw_a_reference = true, - (_, Path { .. }) => saw_a_path = true, + match lifetime.source { + Reference => saw_a_reference = true, + Path { .. } => saw_a_path = true, _ => {} } } @@ -398,10 +384,10 @@ fn emit_mismatch_diagnostic<'tcx>( |infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::>(); let explicit_bound_suggestion = bound_lifetime.map(|info| { - build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound) + build_mismatch_suggestion(info.lifetime.ident.as_str(), &suggest_change_to_explicit_bound) }); - let is_bound_static = bound_lifetime.is_some_and(|info| info.is_static()); + let is_bound_static = bound_lifetime.is_some_and(|info| info.lifetime.is_static()); tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static); @@ -514,18 +500,6 @@ struct Info<'tcx> { } impl<'tcx> Info<'tcx> { - fn syntax_source(&self) -> (hir::LifetimeSyntax, LifetimeSource) { - (self.lifetime.syntax, self.lifetime.source) - } - - fn lifetime_name(&self) -> &str { - self.lifetime.ident.as_str() - } - - fn is_static(&self) -> bool { - self.lifetime.is_static() - } - /// When reporting a lifetime that is implicit, we expand the span /// to include the type. Otherwise we end up pointing at nothing, /// which is a bit confusing. @@ -547,7 +521,7 @@ impl<'tcx> Info<'tcx> { /// ``` // FIXME: Ideally, we'd also remove the lifetime declaration. fn removing_span(&self) -> Span { - let mut span = self.suggestion("'dummy").0; + let mut span = self.lifetime.ident.span; if let hir::TyKind::Ref(_, mut_ty) = self.ty.kind { span = span.until(mut_ty.ty.span); } From 8e3315f2926a8a0b2156ac99ff397d07dbecfee0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 17:58:39 +0200 Subject: [PATCH 1259/1900] misc: extend let-chain --- clippy_lints/src/manual_rotate.rs | 49 +++++++++++++------------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 22e3407303f07..c456920b198d4 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -80,38 +80,31 @@ impl LateLintPass<'_> for ManualRotate { && let BinOpKind::Add | BinOpKind::BitOr = op.node && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) - { - if l_shift_dir == r_shift_dir { - return; - } - if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { - return; - } - let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { + && l_shift_dir != r_shift_dir + && clippy_utils::eq_expr_value(cx, l_expr, r_expr) + && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { ty::Int(itype) => itype.bit_width(), ty::Uint(itype) => itype.bit_width(), _ => return, - }) else { - return; - }; - if l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { - (l_shift_dir, l_amount) - } else { - (r_shift_dir, r_amount) - }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); } + && l_amount + r_amount == u128::from(bit_width) + { + let (shift_function, amount) = if l_amount < r_amount { + (l_shift_dir, l_amount) + } else { + (r_shift_dir, r_amount) + }; + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } From c9ee445abd0427f0117a7ea183459dddbad2731b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 18:16:02 +0200 Subject: [PATCH 1260/1900] extract the constant codepath switch around `*_amount` and `*_expr` -- otherwise the order gets confusing now because of both of them being `Expr`s --- clippy_lints/src/manual_rotate.rs | 55 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index c456920b198d4..d531746830f64 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -56,20 +56,14 @@ impl Display for ShiftDirection { } } -fn parse_shift<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> { +fn parse_shift<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(ShiftDirection, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let dir = match op.node { BinOpKind::Shl => ShiftDirection::Left, BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; - if let Constant::Int(shift) = const_expr { - return Some((dir, shift, l)); - } + return Some((dir, l, r)); } None } @@ -78,8 +72,8 @@ impl LateLintPass<'_> for ManualRotate { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Binary(op, l, r) = expr.kind && let BinOpKind::Add | BinOpKind::BitOr = op.node - && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) - && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) + && let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l) + && let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r) && l_shift_dir != r_shift_dir && clippy_utils::eq_expr_value(cx, l_expr, r_expr) && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { @@ -87,24 +81,31 @@ impl LateLintPass<'_> for ManualRotate { ty::Uint(itype) => itype.bit_width(), _ => return, } - && l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { - (l_shift_dir, l_amount) - } else { - (r_shift_dir, r_amount) - }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); + let const_eval = ConstEvalCtxt::new(cx); + + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(l_amount)) = const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount)) = const_eval.eval_local(r_amount, ctxt) + && l_amount + r_amount == u128::from(bit_width) + { + let (shift_function, amount) = if l_amount < r_amount { + (l_shift_dir, l_amount) + } else { + (r_shift_dir, r_amount) + }; + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); + } } } } From 4423e05f2558e0c00ada7a4a46ed9cfaebd457ad Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 20:52:53 +0200 Subject: [PATCH 1261/1900] fix: print actual consts when shifting by them this is kind of a side-effect of the next commit --- clippy_lints/src/manual_rotate.rs | 9 +++++---- tests/ui/manual_rotate.fixed | 4 ++++ tests/ui/manual_rotate.rs | 4 ++++ tests/ui/manual_rotate.stderr | 30 ++++++++++++++++++------------ 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index d531746830f64..63bdf67a840f9 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -85,17 +85,18 @@ impl LateLintPass<'_> for ManualRotate { let const_eval = ConstEvalCtxt::new(cx); let ctxt = expr.span.ctxt(); - if let Some(Constant::Int(l_amount)) = const_eval.eval_local(l_amount, ctxt) - && let Some(Constant::Int(r_amount)) = const_eval.eval_local(r_amount, ctxt) - && l_amount + r_amount == u128::from(bit_width) + if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) + && l_amount_val + r_amount_val == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { + let (shift_function, amount) = if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) }; let mut applicability = Applicability::MachineApplicable; let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); span_lint_and_sugg( cx, MANUAL_ROTATE, diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed index 49db8661369e9..c11804a32c78c 100644 --- a/tests/ui/manual_rotate.fixed +++ b/tests/ui/manual_rotate.fixed @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = x_u8.rotate_right(3); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64).rotate_right(8); //~^ manual_rotate + // shift by a const + let _ = x_i64.rotate_right(N); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs index 6445e60aa25da..577d9aa20d879 100644 --- a/tests/ui/manual_rotate.rs +++ b/tests/ui/manual_rotate.rs @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = (x_u8 >> 3) | (x_u8 << 5); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); //~^ manual_rotate + // shift by a const + let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr index a28721fbb94c8..8bae69931cbfe 100644 --- a/tests/ui/manual_rotate.stderr +++ b/tests/ui/manual_rotate.stderr @@ -1,5 +1,5 @@ error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:8:16 + --> tests/ui/manual_rotate.rs:9:16 | LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)` @@ -8,64 +8,70 @@ LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:10:17 + --> tests/ui/manual_rotate.rs:11:17 | LL | let y_u16 = (x_u16 >> 7) | (x_u16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:12:17 + --> tests/ui/manual_rotate.rs:13:17 | LL | let y_u32 = (x_u32 >> 8) | (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:14:17 + --> tests/ui/manual_rotate.rs:15:17 | LL | let y_u64 = (x_u64 >> 9) | (x_u64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:16:16 + --> tests/ui/manual_rotate.rs:17:16 | LL | let y_i8 = (x_i8 >> 3) | (x_i8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:18:17 + --> tests/ui/manual_rotate.rs:19:17 | LL | let y_i16 = (x_i16 >> 7) | (x_i16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:20:17 + --> tests/ui/manual_rotate.rs:21:17 | LL | let y_i32 = (x_i32 >> 8) | (x_i32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:22:17 + --> tests/ui/manual_rotate.rs:23:17 | LL | let y_i64 = (x_i64 >> 9) | (x_i64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:25:22 + --> tests/ui/manual_rotate.rs:26:22 | LL | let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:28:25 + --> tests/ui/manual_rotate.rs:29:25 | LL | let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:30:20 + --> tests/ui/manual_rotate.rs:31:20 | LL | let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)` -error: aborting due to 11 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:34:13 + | +LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` + +error: aborting due to 12 previous errors From b249f134239f576ece015a9406f7ebb579c02f2f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 20:45:21 +0200 Subject: [PATCH 1262/1900] feat: also detect non-consts --- clippy_lints/src/manual_rotate.rs | 51 ++++++++++++++++++++++--------- tests/ui/manual_rotate.fixed | 17 +++++++++++ tests/ui/manual_rotate.rs | 17 +++++++++++ tests/ui/manual_rotate.stderr | 20 +++++++++++- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 63bdf67a840f9..e8db44698d9ce 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -85,28 +85,49 @@ impl LateLintPass<'_> for ManualRotate { let const_eval = ConstEvalCtxt::new(cx); let ctxt = expr.span.ctxt(); - if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt) + let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = + const_eval.eval_local(l_amount, ctxt) && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) && l_amount_val + r_amount_val == u128::from(bit_width) { - let (shift_function, amount) = if l_amount_val < r_amount_val { + if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) + } + } else { + let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) { + (_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir), + (ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir), + _ => return, }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); - } + + if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt) + && clippy_utils::eq_expr_value(cx, amount1, amount2) + // (x << s) | (x >> bit_width - s) + && ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend) + // (x << s) | (x >> (bit_width - 1) ^ s) + || (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1))) + { + // NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler + (shift_direction, amount1) + } else { + return; + } + }; + + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed index c11804a32c78c..1012ffc1aa2d8 100644 --- a/tests/ui/manual_rotate.fixed +++ b/tests/ui/manual_rotate.fixed @@ -44,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = x.rotate_left(s); + //~^ manual_rotate + let _ = x.rotate_left(s); + //~^ manual_rotate + // still works with consts + let _ = x.rotate_right(9); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs index 577d9aa20d879..3cdc79673c816 100644 --- a/tests/ui/manual_rotate.rs +++ b/tests/ui/manual_rotate.rs @@ -44,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = (x << s) | (x >> (32 - s)); + //~^ manual_rotate + let _ = (x << s) | (x >> (31 ^ s)); + //~^ manual_rotate + // still works with consts + let _ = (x >> 9) | (x << (32 - 9)); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr index 8bae69931cbfe..ea04ee028db67 100644 --- a/tests/ui/manual_rotate.stderr +++ b/tests/ui/manual_rotate.stderr @@ -73,5 +73,23 @@ error: there is no need to manually implement bit rotation LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` -error: aborting due to 12 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:53:13 + | +LL | let _ = (x << s) | (x >> (32 - s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:55:13 + | +LL | let _ = (x << s) | (x >> (31 ^ s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:58:13 + | +LL | let _ = (x >> 9) | (x << (32 - 9)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_right(9)` + +error: aborting due to 15 previous errors From b5fb01d67db9726180fd85b58d00e3b954fc7e45 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 4 Oct 2025 15:53:00 -0400 Subject: [PATCH 1263/1900] Improve the advice given by panic_immediate_abort --- library/core/src/panicking.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 3f30038dbc03e..b5150837e6a94 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -35,7 +35,8 @@ use crate::panic::{Location, PanicInfo}; #[cfg(feature = "panic_immediate_abort")] compile_error!( "panic_immediate_abort is now a real panic strategy! \ - Enable it with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" + Enable it with `panic = \"immediate-abort\"` in Cargo.toml, \ + or with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" ); // First we define the two main entry points that all panics go through. From 5a38eb1ba8fbab69e03ba8521daeca8ff8dcb905 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 4 Oct 2025 23:58:30 +0200 Subject: [PATCH 1264/1900] a consistency fix Others in the file skip the module name --- src/tools/compiletest/src/directives.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index a79978d036cea..3dd9416516d2e 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -644,11 +644,9 @@ impl TestProps { self.update_add_core_stubs(ln, config); - if let Some(flags) = config.parse_name_value_directive( - ln, - directives::CORE_STUBS_COMPILE_FLAGS, - testfile, - ) { + if let Some(flags) = + config.parse_name_value_directive(ln, CORE_STUBS_COMPILE_FLAGS, testfile) + { let flags = split_flags(&flags); for flag in &flags { if flag == "--edition" || flag.starts_with("--edition=") { From 757d98ce2b55162e22051be354418291e88f8d46 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 13:20:39 +1000 Subject: [PATCH 1265/1900] Move `DirectiveLine` into its own submodule --- src/tools/compiletest/src/directives.rs | 68 +------------------- src/tools/compiletest/src/directives/line.rs | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 src/tools/compiletest/src/directives/line.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index a79978d036cea..edb06dd7345d9 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -15,6 +15,7 @@ use crate::directives::auxiliary::{AuxProps, parse_and_update_aux}; use crate::directives::directive_names::{ KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, }; +use crate::directives::line::{DirectiveLine, line_directive}; use crate::directives::needs::CachedNeedsConditions; use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; @@ -25,6 +26,7 @@ use crate::{fatal, help}; pub(crate) mod auxiliary; mod cfg; mod directive_names; +mod line; mod needs; #[cfg(test)] mod tests; @@ -824,70 +826,6 @@ impl TestProps { } } -/// If the given line begins with the appropriate comment prefix for a directive, -/// returns a struct containing various parts of the directive. -fn line_directive<'line>( - line_number: usize, - original_line: &'line str, -) -> Option> { - // Ignore lines that don't start with the comment prefix. - let after_comment = - original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); - - let revision; - let raw_directive; - - if let Some(after_open_bracket) = after_comment.strip_prefix('[') { - // A comment like `//@[foo]` only applies to revision `foo`. - let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { - panic!( - "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" - ) - }; - - revision = Some(line_revision); - raw_directive = after_close_bracket.trim_start(); - } else { - revision = None; - raw_directive = after_comment; - }; - - Some(DirectiveLine { line_number, revision, raw_directive }) -} - -/// The (partly) broken-down contents of a line containing a test directive, -/// which [`iter_directives`] passes to its callback function. -/// -/// For example: -/// -/// ```text -/// //@ compile-flags: -O -/// ^^^^^^^^^^^^^^^^^ raw_directive -/// -/// //@ [foo] compile-flags: -O -/// ^^^ revision -/// ^^^^^^^^^^^^^^^^^ raw_directive -/// ``` -struct DirectiveLine<'ln> { - line_number: usize, - /// Some test directives start with a revision name in square brackets - /// (e.g. `[foo]`), and only apply to that revision of the test. - /// If present, this field contains the revision name (e.g. `foo`). - revision: Option<&'ln str>, - /// The main part of the directive, after removing the comment prefix - /// and the optional revision specifier. - /// - /// This is "raw" because the directive's name and colon-separated value - /// (if present) have not yet been extracted or checked. - raw_directive: &'ln str, -} - -impl<'ln> DirectiveLine<'ln> { - fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { - self.revision.is_none() || self.revision == test_revision - } -} - pub(crate) struct CheckDirectiveResult<'ln> { is_known_directive: bool, trailing_directive: Option<&'ln str>, @@ -920,8 +858,6 @@ fn check_directive<'a>( CheckDirectiveResult { is_known_directive, trailing_directive } } -const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; - fn iter_directives( mode: TestMode, poisoned: &mut bool, diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs new file mode 100644 index 0000000000000..584d5b5b35bec --- /dev/null +++ b/src/tools/compiletest/src/directives/line.rs @@ -0,0 +1,67 @@ +const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; + +/// If the given line begins with the appropriate comment prefix for a directive, +/// returns a struct containing various parts of the directive. +pub(crate) fn line_directive<'line>( + line_number: usize, + original_line: &'line str, +) -> Option> { + // Ignore lines that don't start with the comment prefix. + let after_comment = + original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); + + let revision; + let raw_directive; + + if let Some(after_open_bracket) = after_comment.strip_prefix('[') { + // A comment like `//@[foo]` only applies to revision `foo`. + let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { + panic!( + "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" + ) + }; + + revision = Some(line_revision); + raw_directive = after_close_bracket.trim_start(); + } else { + revision = None; + raw_directive = after_comment; + }; + + Some(DirectiveLine { line_number, revision, raw_directive }) +} + +/// The (partly) broken-down contents of a line containing a test directive, +/// which `iter_directives` passes to its callback function. +/// +/// For example: +/// +/// ```text +/// //@ compile-flags: -O +/// ^^^^^^^^^^^^^^^^^ raw_directive +/// +/// //@ [foo] compile-flags: -O +/// ^^^ revision +/// ^^^^^^^^^^^^^^^^^ raw_directive +/// ``` +pub(crate) struct DirectiveLine<'ln> { + pub(crate) line_number: usize, + + /// Some test directives start with a revision name in square brackets + /// (e.g. `[foo]`), and only apply to that revision of the test. + /// If present, this field contains the revision name (e.g. `foo`). + pub(crate) revision: Option<&'ln str>, + + /// The main part of the directive, after removing the comment prefix + /// and the optional revision specifier. + /// + /// This is "raw" because the directive's name and colon-separated value + /// (if present) have not yet been extracted or checked. + pub(crate) raw_directive: &'ln str, +} + +impl<'ln> DirectiveLine<'ln> { + pub(crate) fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { + self.revision.is_none() || self.revision == test_revision + } +} From 6783e9465b62e77c7e3cb76a747d92b1de4339f7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 20:08:40 +1000 Subject: [PATCH 1266/1900] Allow easy extraction of name/value from a `DirectiveLine` --- src/tools/compiletest/src/directives.rs | 10 +++-- src/tools/compiletest/src/directives/line.rs | 45 +++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index edb06dd7345d9..bbbeecc1b71b0 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -875,15 +875,17 @@ fn iter_directives( // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later. if mode == TestMode::CoverageRun { let extra_directives: &[&str] = &[ - "needs-profiler-runtime", + "//@ needs-profiler-runtime", // FIXME(pietroalbini): this test currently does not work on cross-compiled targets // because remote-test is not capable of sending back the *.profraw files generated by // the LLVM instrumentation. - "ignore-cross-compile", + "//@ ignore-cross-compile", ]; // Process the extra implied directives, with a dummy line number of 0. - for raw_directive in extra_directives { - it(DirectiveLine { line_number: 0, revision: None, raw_directive }); + for directive_str in extra_directives { + let directive_line = line_directive(0, directive_str) + .unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}")); + it(directive_line); } } diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 584d5b5b35bec..5c1b592d045d8 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -1,3 +1,7 @@ +#![expect(dead_code)] // (removed later in this PR) + +use std::fmt; + const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; /// If the given line begins with the appropriate comment prefix for a directive, @@ -28,7 +32,10 @@ pub(crate) fn line_directive<'line>( raw_directive = after_comment; }; - Some(DirectiveLine { line_number, revision, raw_directive }) + // The directive name ends at the first occurrence of colon, space, or end-of-string. + let name = raw_directive.split([':', ' ']).next().expect("split is never empty"); + + Some(DirectiveLine { line_number, revision, raw_directive, name }) } /// The (partly) broken-down contents of a line containing a test directive, @@ -39,10 +46,12 @@ pub(crate) fn line_directive<'line>( /// ```text /// //@ compile-flags: -O /// ^^^^^^^^^^^^^^^^^ raw_directive +/// ^^^^^^^^^^^^^ name /// /// //@ [foo] compile-flags: -O /// ^^^ revision /// ^^^^^^^^^^^^^^^^^ raw_directive +/// ^^^^^^^^^^^^^ name /// ``` pub(crate) struct DirectiveLine<'ln> { pub(crate) line_number: usize, @@ -58,10 +67,44 @@ pub(crate) struct DirectiveLine<'ln> { /// This is "raw" because the directive's name and colon-separated value /// (if present) have not yet been extracted or checked. pub(crate) raw_directive: &'ln str, + + /// Name of the directive. + /// + /// Invariant: `self.raw_directive.starts_with(self.name)` + pub(crate) name: &'ln str, } impl<'ln> DirectiveLine<'ln> { pub(crate) fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { self.revision.is_none() || self.revision == test_revision } + + /// Helper method used by `value_after_colon` and `remark_after_space`. + /// Don't call this directly. + fn rest_after_separator(&self, separator: u8) -> Option<&'ln str> { + let n = self.name.len(); + if self.raw_directive.as_bytes().get(n) != Some(&separator) { + return None; + } + + Some(&self.raw_directive[n + 1..]) + } + + /// If this directive uses `name: value` syntax, returns the part after + /// the colon character. + pub(crate) fn value_after_colon(&self) -> Option<&'ln str> { + self.rest_after_separator(b':') + } + + /// If this directive uses `name remark` syntax, returns the part after + /// the separating space. + pub(crate) fn remark_after_space(&self) -> Option<&'ln str> { + self.rest_after_separator(b' ') + } + + /// Allows callers to print `raw_directive` if necessary, + /// without accessing the field directly. + pub(crate) fn display(&self) -> impl fmt::Display { + self.raw_directive + } } From 33c99a0468302f1bb9bd7dd0d63d460b1a88ed52 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 20:16:59 +1000 Subject: [PATCH 1267/1900] Use new `DirectiveLine` features in directive parsing --- src/tools/compiletest/src/directives.rs | 102 +++++++++--------- .../compiletest/src/directives/auxiliary.rs | 4 +- src/tools/compiletest/src/directives/cfg.rs | 26 ++--- src/tools/compiletest/src/directives/line.rs | 4 +- src/tools/compiletest/src/directives/needs.rs | 15 +-- src/tools/compiletest/src/directives/tests.rs | 11 +- 6 files changed, 73 insertions(+), 89 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index bbbeecc1b71b0..dab6850e0d62a 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -835,9 +835,7 @@ fn check_directive<'a>( directive_ln: &DirectiveLine<'a>, mode: TestMode, ) -> CheckDirectiveResult<'a> { - let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln; - - let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, "")); + let &DirectiveLine { name: directive_name, .. } = directive_ln; let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name) || match mode { @@ -846,14 +844,13 @@ fn check_directive<'a>( _ => false, }; - let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); - let trailing_directive = { - // 1. is the directive name followed by a space? (to exclude `:`) - directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' ')) - // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") - && KNOWN_DIRECTIVE_NAMES.contains(&trailing) - } - .then_some(trailing); + // If it looks like the user tried to put two directives on the same line + // (e.g. `//@ only-linux only-x86_64`), signal an error, because the + // second "directive" would actually be ignored with no effect. + let trailing_directive = directive_ln + .remark_after_space() + .map(|remark| remark.trim_start().split(' ').next().unwrap()) + .filter(|token| KNOWN_DIRECTIVE_NAMES.contains(token)); CheckDirectiveResult { is_known_directive, trailing_directive } } @@ -915,7 +912,7 @@ fn iter_directives( error!( "{testfile}:{line_number}: detected unknown compiletest test directive `{}`", - directive_line.raw_directive, + directive_line.display(), ); return; @@ -1011,13 +1008,9 @@ impl Config { } fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option { - let &DirectiveLine { raw_directive, .. } = line; - - // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine` - // instead of doing it here. - let (directive_name, raw_value) = raw_directive.split_once(':')?; + let &DirectiveLine { name, .. } = line; - let kind = match directive_name { + let kind = match name { "normalize-stdout" => NormalizeKind::Stdout, "normalize-stderr" => NormalizeKind::Stderr, "normalize-stderr-32bit" => NormalizeKind::Stderr32bit, @@ -1025,21 +1018,20 @@ impl Config { _ => return None, }; - let Some((regex, replacement)) = parse_normalize_rule(raw_value) else { - error!("couldn't parse custom normalization rule: `{raw_directive}`"); - help!("expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`"); + let Some((regex, replacement)) = line.value_after_colon().and_then(parse_normalize_rule) + else { + error!("couldn't parse custom normalization rule: `{}`", line.display()); + help!("expected syntax is: `{name}: \"REGEX\" -> \"REPLACEMENT\"`"); panic!("invalid normalization rule detected"); }; Some(NormalizeRule { kind, regex, replacement }) } fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool { - let &DirectiveLine { raw_directive: line, .. } = line; - - // Ensure the directive is a whole word. Do not match "ignore-x86" when - // the line says "ignore-x86_64". - line.starts_with(directive) - && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) + // FIXME(Zalathar): Ideally, this should raise an error if a name-only + // directive is followed by a colon, since that's the wrong syntax. + // But we would need to fix tests that rely on the current behaviour. + line.name == directive } fn parse_name_value_directive( @@ -1048,22 +1040,26 @@ impl Config { directive: &str, testfile: &Utf8Path, ) -> Option { - let &DirectiveLine { line_number, raw_directive: line, .. } = line; - - let colon = directive.len(); - if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { - let value = line[(colon + 1)..].to_owned(); - debug!("{}: {}", directive, value); - let value = expand_variables(value, self); - if value.is_empty() { - error!("{testfile}:{line_number}: empty value for directive `{directive}`"); - help!("expected syntax is: `{directive}: value`"); - panic!("empty directive value detected"); - } - Some(value) - } else { - None + let &DirectiveLine { line_number, .. } = line; + + if line.name != directive { + return None; + }; + + // FIXME(Zalathar): This silently discards directives with a matching + // name but no colon. Unfortunately, some directives (e.g. "pp-exact") + // currently rely on _not_ panicking here. + let value = line.value_after_colon()?; + debug!("{}: {}", directive, value); + let value = expand_variables(value.to_owned(), self); + + if value.is_empty() { + error!("{testfile}:{line_number}: empty value for directive `{directive}`"); + help!("expected syntax is: `{directive}: value`"); + panic!("empty directive value detected"); } + + Some(value) } fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { @@ -1453,14 +1449,14 @@ pub(crate) fn make_test_description( } fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Cdb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.cdb_version { - if let Some(rest) = line.strip_prefix("min-cdb-version:").map(str::trim) { + if line.name == "min-cdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let min_version = extract_cdb_version(rest).unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); }); @@ -1478,14 +1474,14 @@ fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { } fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Gdb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.gdb_version { - if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { + if line.name == "min-gdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) .unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); @@ -1501,7 +1497,9 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { reason: format!("ignored when the GDB version is lower than {rest}"), }; } - } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { + } else if line.name == "ignore-gdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let (min_version, max_version) = extract_version_range(rest, extract_gdb_version) .unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); @@ -1528,14 +1526,14 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { } fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Lldb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.lldb_version { - if let Some(rest) = line.strip_prefix("min-lldb-version:").map(str::trim) { + if line.name == "min-lldb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let min_version = rest.parse().unwrap_or_else(|e| { panic!("Unexpected format of LLDB version string: {}\n{:?}", rest, e); }); diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs index 0675a6feac3f0..7cf98178e733f 100644 --- a/src/tools/compiletest/src/directives/auxiliary.rs +++ b/src/tools/compiletest/src/directives/auxiliary.rs @@ -50,9 +50,7 @@ pub(super) fn parse_and_update_aux( testfile: &Utf8Path, aux: &mut AuxProps, ) { - let &DirectiveLine { raw_directive: ln, .. } = directive_line; - - if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) { + if !(directive_line.name.starts_with("aux-") || directive_line.name == "proc-macro") { return; } diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs index 62a4b88a33a6e..3855ba7ac5f42 100644 --- a/src/tools/compiletest/src/directives/cfg.rs +++ b/src/tools/compiletest/src/directives/cfg.rs @@ -6,8 +6,8 @@ use crate::directives::{DirectiveLine, IgnoreDecision}; const EXTRA_ARCHS: &[&str] = &["spirv"]; pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let parsed = parse_cfg_name_directive(config, line, "ignore"); - let &DirectiveLine { raw_directive: line, .. } = line; + let parsed = parse_cfg_name_directive(config, line, "ignore-"); + let line = line.display(); match parsed.outcome { MatchOutcome::NoMatch => IgnoreDecision::Continue, @@ -24,8 +24,8 @@ pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> Ignore } pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let parsed = parse_cfg_name_directive(config, line, "only"); - let &DirectiveLine { raw_directive: line, .. } = line; + let parsed = parse_cfg_name_directive(config, line, "only-"); + let line = line.display(); match parsed.outcome { MatchOutcome::Match => IgnoreDecision::Continue, @@ -50,18 +50,14 @@ fn parse_cfg_name_directive<'a>( line: &'a DirectiveLine<'a>, prefix: &str, ) -> ParsedNameDirective<'a> { - let &DirectiveLine { raw_directive: line, .. } = line; - - if !line.as_bytes().starts_with(prefix.as_bytes()) { - return ParsedNameDirective::not_a_directive(); - } - if line.as_bytes().get(prefix.len()) != Some(&b'-') { + let Some(name) = line.name.strip_prefix(prefix) else { return ParsedNameDirective::not_a_directive(); - } - let line = &line[prefix.len() + 1..]; + }; - let (name, comment) = - line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None)); + // FIXME(Zalathar): This currently allows either a space or a colon, and + // treats any "value" after a colon as though it were a remark. + // We should instead forbid the colon syntax for these directives. + let comment = line.remark_after_space().or_else(|| line.value_after_colon()); // Some of the matchers might be "" depending on what the target information is. To avoid // problems we outright reject empty directives. @@ -269,7 +265,7 @@ fn parse_cfg_name_directive<'a>( message: "when performing tests on dist toolchain" } - if prefix == "ignore" && outcome == MatchOutcome::Invalid { + if prefix == "ignore-" && outcome == MatchOutcome::Invalid { // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest. if name.starts_with("tidy-") { outcome = MatchOutcome::External; diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 5c1b592d045d8..49907207d2e61 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -1,5 +1,3 @@ -#![expect(dead_code)] // (removed later in this PR) - use std::fmt; const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; @@ -66,7 +64,7 @@ pub(crate) struct DirectiveLine<'ln> { /// /// This is "raw" because the directive's name and colon-separated value /// (if present) have not yet been extracted or checked. - pub(crate) raw_directive: &'ln str, + raw_directive: &'ln str, /// Name of the directive. /// diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 9d72492e5b078..5e9fe59d8d1c3 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -181,17 +181,10 @@ pub(super) fn handle_needs( }, ]; - let &DirectiveLine { raw_directive: ln, .. } = ln; + let &DirectiveLine { name, .. } = ln; - let (name, rest) = match ln.split_once([':', ' ']) { - Some((name, rest)) => (name, Some(rest)), - None => (ln, None), - }; - - // FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to - // delineate value. if name == "needs-target-has-atomic" { - let Some(rest) = rest else { + let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), }; @@ -233,7 +226,7 @@ pub(super) fn handle_needs( // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. if name == "needs-crate-type" { - let Some(rest) = rest else { + let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { message: "expected `needs-crate-type` to have a comma-separated list of crate types" @@ -292,7 +285,7 @@ pub(super) fn handle_needs( break; } else { return IgnoreDecision::Ignore { - reason: if let Some(comment) = rest { + reason: if let Some(comment) = ln.remark_after_space() { format!("{} ({})", need.ignore_reason, comment.trim()) } else { need.ignore_reason.into() diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 93621192d4bd6..95dd46532ba88 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -3,12 +3,11 @@ use std::io::Read; use camino::Utf8Path; use semver::Version; -use super::{ +use crate::common::{Config, Debugger, TestMode}; +use crate::directives::{ DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, - extract_version_range, iter_directives, parse_normalize_rule, + extract_version_range, iter_directives, line_directive, parse_edition, parse_normalize_rule, }; -use crate::common::{Config, Debugger, TestMode}; -use crate::directives::parse_edition; use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description( @@ -955,7 +954,9 @@ fn test_needs_target_std() { fn parse_edition_range(line: &str) -> Option { let config = cfg().build(); - let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line }; + + let line_with_comment = format!("//@ {line}"); + let line = line_directive(0, &line_with_comment).unwrap(); super::parse_edition_range(&config, &line, "tmp.rs".into()) } From 34ad9198584b2245024e8735809e6a3567e504cb Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 5 Oct 2025 11:11:47 +0800 Subject: [PATCH 1268/1900] Remove extra space for missing associated type term --- compiler/rustc_parse/src/parser/path.rs | 19 ++++++++----------- .../recover-assoc-eq-missing-term.stderr | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 8604564885916..f7757921cd426 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -753,18 +753,13 @@ impl<'a> Parser<'a> { } let kind = if self.eat(exp!(Colon)) { AssocItemConstraintKind::Bound { bounds: self.parse_generic_bounds()? } - } else if self.eat(exp!(Eq)) { - self.parse_assoc_equality_term( - ident, - gen_args.as_ref(), - self.prev_token.span, - )? + } else if self.check(exp!(Eq)) { + self.parse_assoc_equality_term(ident, gen_args.as_ref())? } else { unreachable!(); }; let span = lo.to(self.prev_token.span); - let constraint = AssocItemConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) @@ -792,8 +787,10 @@ impl<'a> Parser<'a> { &mut self, ident: Ident, gen_args: Option<&GenericArgs>, - eq: Span, ) -> PResult<'a, AssocItemConstraintKind> { + let prev_token_span = self.prev_token.span; + let eq_span = self.token.span; + self.expect(exp!(Eq))?; let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); let term = match arg { @@ -814,20 +811,20 @@ impl<'a> Parser<'a> { self.mk_ty(lt.ident.span, ast::TyKind::Err(guar)).into() } None => { - let after_eq = eq.shrink_to_hi(); + let after_eq = eq_span.shrink_to_hi(); let before_next = self.token.span.shrink_to_lo(); let mut err = self .dcx() .struct_span_err(after_eq.to(before_next), "missing type to the right of `=`"); if matches!(self.token.kind, token::Comma | token::Gt) { err.span_suggestion( - self.psess.source_map().next_point(eq).to(before_next), + self.psess.source_map().next_point(eq_span).to(before_next), "to constrain the associated type, add a type after `=`", " TheType", Applicability::HasPlaceholders, ); err.span_suggestion( - eq.to(before_next), + prev_token_span.shrink_to_hi().to(before_next), format!("remove the `=` if `{ident}` is a type"), "", Applicability::MaybeIncorrect, diff --git a/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr index cf50c026665b5..2d8ab7a3e2f76 100644 --- a/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr +++ b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr @@ -11,7 +11,7 @@ LL | bar::(); help: remove the `=` if `Item` is a type | LL - bar::(); -LL + bar::(); +LL + bar::(); | error: aborting due to 1 previous error From a175921d5e67997e359e71dc1602bd1e3547c0d7 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sat, 4 Oct 2025 23:52:33 -0400 Subject: [PATCH 1269/1900] update libEnzyme file name, and add missing strict-aliasing flag --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index bd46a66fade47..a66d775d98264 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -16,7 +16,7 @@ Before generating the llvm-ir, keep in mind two techniques that can help ensure ## 1) Generate an llvm-ir reproducer ```sh -rustflags="-z autodiff=enable,printmodbefore" cargo +enzyme build --release &> out.ll +RUSTFLAGS="-Z autodiff=Enable,PrintModbefore" cargo +enzyme build --release &> out.ll ``` This also captures a few warnings and info messages above and below your module. open out.ll and remove every line above `; moduleid = `. Now look at the end of the file and remove everything that's not part of llvm-ir, i.e. remove errors and warnings. The last line of your llvm-ir should now start with `! = `, i.e. `!40831 = !{i32 0, i32 1037508, i32 1037538, i32 1037559}` or `!43760 = !dilocation(line: 297, column: 5, scope: !43746)`. @@ -25,10 +25,10 @@ The actual numbers will depend on your code. ## 2) Check your llvm-ir reproducer -To confirm that your previous step worked, we will use llvm's `opt` tool. find your path to the opt binary, with a path similar to `/rust/build//build/bin/opt`. also find `llvmenzyme-19.` path, similar to `/rust/build/target-tripple/enzyme/build/enzyme/llvmenzyme-19`. Please keep in mind that llvm frequently updates it's llvm backend, so the version number might be higher (20, 21, ...). Once you have both, run the following command: +To confirm that your previous step worked, we will use llvm's `opt` tool. find your path to the opt binary, with a path similar to `/rust/build//build/bin/opt`. also find `llvmenzyme-19.` path, similar to `/rust/build/target-triple/enzyme/build/enzyme/llvmenzyme-19`. Please keep in mind that llvm frequently updates it's llvm backend, so the version number might be higher (20, 21, ...). Once you have both, run the following command: ```sh - out.ll -load-pass-plugin=/path/to/llvmenzyme-19.so -passes="enzyme" -s + out.ll -load-pass-plugin=/path/to/build//stage1/lib/libEnzyme-21.so -passes="enzyme" -enzyme-strict-aliasing=0 -s ``` If the previous step succeeded, you are going to see the same error that you saw when compiling your rust code with cargo. From 8b178272c7f7c9df3e2b49d740614c3497de2fb8 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:50:19 -0400 Subject: [PATCH 1270/1900] Remove unneeded get_hidden_type --- .../src/region_infer/opaque_types/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 8d89f3e0d8700..fe9f9a37b862c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -155,13 +155,6 @@ fn add_hidden_type<'tcx>( } } -fn get_hidden_type<'tcx>( - hidden_types: &DefinitionSiteHiddenTypes<'tcx>, - def_id: LocalDefId, -) -> Option>> { - hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) -} - #[derive(Debug)] struct DefiningUse<'tcx> { /// The opaque type using non NLL vars. This uses the actual @@ -501,7 +494,8 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>( let tcx = infcx.tcx; let mut errors = Vec::new(); for &(key, hidden_type) in opaque_types { - let Some(expected) = get_hidden_type(hidden_types, key.def_id) else { + let Some(expected) = hidden_types.0.get(&key.def_id).map(|ty| EarlyBinder::bind(*ty)) + else { if !tcx.use_typing_mode_borrowck() { if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() && alias_ty.def_id == key.def_id.to_def_id() From 765e2fe6eed09e4fb61702ede01362336b6250e7 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 01:38:29 -0400 Subject: [PATCH 1271/1900] Add potential instability warning --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index a66d775d98264..97893535cfe54 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -30,6 +30,7 @@ To confirm that your previous step worked, we will use llvm's `opt` tool. find y ```sh out.ll -load-pass-plugin=/path/to/build//stage1/lib/libEnzyme-21.so -passes="enzyme" -enzyme-strict-aliasing=0 -s ``` +This command might fail for future versions or on your system, in which case you should replace libEnzyme-21.so with LLVMEnzyme-21.so. Look at the Enzyme docs for instructions on how to build it. You might need to also adjust how to build your LLVM version. If the previous step succeeded, you are going to see the same error that you saw when compiling your rust code with cargo. From b74760a66494d3a6169cd1a37423a4a4364972c7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 5 Oct 2025 13:53:40 +0800 Subject: [PATCH 1272/1900] minor: Fix CURRENT_FIXME for extract_expressions_from_format_string --- .../src/handlers/extract_expressions_from_format_string.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index e3c7ea1b09391..61af2de6ec683 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -108,9 +108,7 @@ pub(crate) fn extract_expressions_from_format_string( match arg { Arg::Expr(s) => { // insert arg - // FIXME: use the crate's edition for parsing - let expr = - ast::Expr::parse(&s, syntax::Edition::CURRENT_FIXME).syntax_node(); + let expr = ast::Expr::parse(&s, ctx.edition()).syntax_node(); let mut expr_tt = utils::tt_from_syntax(expr); new_tt_bits.append(&mut expr_tt); } From c7ed41ebd0ad30ac1405841a160080ef0a4faaa9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 4 Oct 2025 21:20:36 +0800 Subject: [PATCH 1273/1900] Fix parentheses for missing_unsafe I seem unable to use `Expr::needs_parens*` to complete it Example --- ```rust static mut STATIC_MUT: u8 = 0; fn foo() -> u8 { STATIC_MUT$0 * 2 } ``` **Before this PR**: ```rust static mut STATIC_MUT: u8 = 0; fn foo() -> u8 { unsafe { STATIC_MUT } * 2 } ``` **After this PR**: ```rust static mut STATIC_MUT: u8 = 0; fn foo() -> u8 { (unsafe { STATIC_MUT }) * 2 } ``` --- .../src/handlers/missing_unsafe.rs | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 4bb64747f5bb7..51a0641b90d9e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -50,7 +50,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option Option bool { + let node = expr.syntax(); + node.ancestors() + .skip(1) + .take_while(|it| it.text_range().start() == node.text_range().start()) + .map_while(ast::Expr::cast) + .last() + .and_then(|it| Some(it.syntax().parent()?.kind())) + .is_some_and(|kind| ast::ExprStmt::can_cast(kind) || ast::StmtList::can_cast(kind)) +} + #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_fix, check_no_fix}; @@ -527,6 +543,27 @@ fn main() { ) } + #[test] + fn needs_parentheses_for_unambiguous() { + check_fix( + r#" +//- minicore: copy +static mut STATIC_MUT: u8 = 0; + +fn foo() -> u8 { + STATIC_MUT$0 * 2 +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn foo() -> u8 { + (unsafe { STATIC_MUT }) * 2 +} +"#, + ) + } + #[test] fn ref_to_unsafe_expr() { check_fix( From 41dd2b69a6223bcbf9f6782cefa2610b2dc519a9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 09:55:50 +0300 Subject: [PATCH 1274/1900] Switch to home-made db attaching infrastructure Instead of using Salsa's, as we can no longer can a `dyn HirDatabase` from the `dyn salsa::Database` Salsa provides. --- src/tools/rust-analyzer/Cargo.lock | 18 +- src/tools/rust-analyzer/Cargo.toml | 5 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 6 +- .../rust-analyzer/crates/cfg/src/cfg_expr.rs | 2 +- .../crates/hir-def/src/test_db.rs | 6 +- .../crates/hir-ty/src/consteval/tests.rs | 10 +- .../hir-ty/src/dyn_compatibility/tests.rs | 2 +- .../crates/hir-ty/src/infer/cast.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 53 +++--- .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../crates/hir-ty/src/mir/eval/tests.rs | 8 +- .../crates/hir-ty/src/mir/lower/tests.rs | 33 ++-- .../crates/hir-ty/src/next_solver/consts.rs | 9 +- .../crates/hir-ty/src/next_solver/interner.rs | 151 ++++++++++++--- .../crates/hir-ty/src/next_solver/ir_print.rs | 47 ++--- .../crates/hir-ty/src/next_solver/opaques.rs | 6 +- .../hir-ty/src/next_solver/predicate.rs | 6 +- .../crates/hir-ty/src/next_solver/region.rs | 3 +- .../crates/hir-ty/src/next_solver/ty.rs | 3 +- .../crates/hir-ty/src/next_solver/util.rs | 2 +- .../crates/hir-ty/src/test_db.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 46 +++-- .../hir-ty/src/tests/closure_captures.rs | 180 +++++++++--------- .../crates/hir-ty/src/tests/incremental.rs | 4 +- .../crates/hir-ty/src/variance.rs | 158 +++++++-------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 3 +- .../crates/ide-assists/src/tests.rs | 6 +- .../ide-completion/src/context/analysis.rs | 3 +- .../ide-completion/src/context/tests.rs | 5 +- .../crates/ide-completion/src/tests.rs | 6 +- .../ide-completion/src/tests/flyimport.rs | 3 +- .../crates/ide-db/src/symbol_index.rs | 4 +- .../ide-db/src/syntax_helpers/suggest_name.rs | 2 +- .../crates/ide-diagnostics/src/tests.rs | 14 +- .../crates/ide-ssr/src/matching.rs | 4 +- .../crates/ide-ssr/src/resolving.rs | 4 +- .../rust-analyzer/crates/ide-ssr/src/tests.rs | 11 +- .../crates/ide/src/doc_links/tests.rs | 5 +- .../crates/ide/src/inlay_hints.rs | 8 +- .../crates/ide/src/inlay_hints/adjustment.rs | 4 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 6 +- .../crates/ide/src/navigation_target.rs | 6 +- .../rust-analyzer/crates/ide/src/runnables.rs | 6 +- .../crates/ide/src/signature_help.rs | 7 +- .../crates/ide/src/static_index.rs | 6 +- .../crates/ide/src/syntax_highlighting.rs | 8 +- .../ide/src/syntax_highlighting/inject.rs | 6 +- .../crates/ide/src/view_memory_layout.rs | 3 +- .../rust-analyzer/src/cli/analysis_stats.rs | 7 +- .../rust-analyzer/crates/span/src/hygiene.rs | 10 + 52 files changed, 499 insertions(+), 418 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5840d2ca6a723..8e2c371bceaa5 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -235,7 +235,6 @@ name = "cfg" version = "0.0.0" dependencies = [ "arbitrary", - "derive_arbitrary", "expect-test", "intern", "oorandom", @@ -1615,7 +1614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "indexmap", ] @@ -2145,8 +2144,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.23.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27956164373aeec733ac24ff1736de8541234e3a8e7e6f916b28175b5752af3b" dependencies = [ "boxcar", "crossbeam-queue", @@ -2169,13 +2169,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.23.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca3b9d6e47c08b5de4b218e0c5f7ec910b51bce6314e651c8e7b9d154d174da" [[package]] name = "salsa-macros" -version = "0.23.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=e257df12eabd566825ba53bb12d782560b9a4dcd#e257df12eabd566825ba53bb12d782560b9a4dcd" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6337b62f2968be6b8afa30017d7564ecbde6832ada47ed2261fb14d0fd402ff4" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 25af4dfa87af2..1438d4638922a 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -137,13 +137,12 @@ rayon = "1.10.0" rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "e257df12eabd566825ba53bb12d782560b9a4dcd", default-features = true, features = [ +salsa = { version = "0.24.0", default-features = true, features = [ "rayon", "salsa_unstable", "macros", ] } -# salsa-macros = "0.23.0" -salsa-macros = { git = "https://github.com/salsa-rs/salsa.git", rev = "e257df12eabd566825ba53bb12d782560b9a4dcd" } +salsa-macros = "0.24.0" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index ba34966614536..af95f86c83521 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -23,11 +23,7 @@ intern.workspace = true [dev-dependencies] expect-test = "1.5.1" oorandom = "11.1.5" -# We depend on both individually instead of using `features = ["derive"]` to microoptimize the -# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr` -# supports `arbitrary`. This way, we avoid feature unification. -arbitrary = "1.4.1" -derive_arbitrary = "1.4.1" +arbitrary = { version = "1.4.1", features = ["derive"] } # local deps syntax-bridge.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index f83c21eb8d64a..7a21015e14bec 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -47,7 +47,7 @@ impl fmt::Display for CfgAtom { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(test, derive(derive_arbitrary::Arbitrary))] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] pub enum CfgExpr { Invalid, Atom(CfgAtom), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 61ca6f67e6143..12a1c1554cc12 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -1,6 +1,5 @@ //! Database used for testing `hir_def`. -use salsa::database::AsDynDatabase; use std::{fmt, panic, sync::Mutex}; use base_db::{ @@ -8,7 +7,7 @@ use base_db::{ SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_expand::{InFile, files::FilePosition}; -use salsa::{Durability, database::AsDynDatabase}; +use salsa::Durability; use span::FileId; use syntax::{AstNode, algo, ast}; use triomphe::Arc; @@ -304,8 +303,7 @@ impl TestDB { // This is pretty horrible, but `Debug` is the only way to inspect // QueryDescriptor at the moment. salsa::EventKind::WillExecute { database_key } => { - let ingredient = self - .as_dyn_database() + let ingredient = (self as &dyn salsa::Database) .ingredient_debug_name(database_key.ingredient_index()); Some(ingredient.to_string()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 5a214eabcdbd4..70a8ae766c0ba 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -36,7 +36,7 @@ fn check_fail( error: impl FnOnce(ConstEvalError<'_>) -> bool, ) { let (db, file_id) = TestDB::with_single_file(ra_fixture); - salsa::attach(&db, || match eval_goal(&db, file_id) { + crate::attach_db(&db, || match eval_goal(&db, file_id) { Ok(_) => panic!("Expected fail, but it succeeded"), Err(e) => { assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db)) @@ -79,7 +79,7 @@ fn check_answer( check: impl FnOnce(&[u8], &MemoryMap<'_>), ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - salsa::attach(&db, || { + crate::attach_db(&db, || { let file_id = *file_ids.last().unwrap(); let r = match eval_goal(&db, file_id) { Ok(t) => t, @@ -2506,8 +2506,10 @@ fn enums() { const GOAL: E = E::A; "#, ); - let r = eval_goal(&db, file_id).unwrap(); - assert_eq!(try_const_usize(&db, &r), Some(1)); + crate::attach_db(&db, || { + let r = eval_goal(&db, file_id).unwrap(); + assert_eq!(try_const_usize(&db, &r), Some(1)); + }) } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs index 04a9ba79921ad..f90cd608e997e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -57,7 +57,7 @@ fn check_dyn_compatibility<'a>( }; let mut osvs = FxHashSet::default(); let db = &db; - salsa::attach(db, || { + crate::attach_db(db, || { _ = dyn_compatibility_with_callback(db, trait_id, &mut |osv| { osvs.insert(match osv { DynCompatibilityViolation::SizedSelf => SizedSelf, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 017f45f43df46..990281a7c8965 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -400,7 +400,7 @@ fn pointer_kind<'db>( Ok(Some(PointerKind::Thin)) } } - TyKind::Tuple(subst) => match subst.iter().last() { + TyKind::Tuple(subst) => match subst.iter().next_back() { None => Ok(Some(PointerKind::Thin)), Some(ty) => pointer_kind(ty, ctx), }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index d97d2af080732..eed36b1bb7f5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -385,7 +385,7 @@ fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) - } } TyKind::Tuple(tys) => { - if let Some(last_field_ty) = tys.iter().last() { + if let Some(last_field_ty) = tys.iter().next_back() { struct_tail_erasing_lifetimes(db, last_field_ty) } else { pointee diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 712f5a89a2342..f0fed83597298 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -79,7 +79,7 @@ fn eval_goal( Some(adt_or_type_alias_id) }) .unwrap(); - salsa::attach(&db, || { + crate::attach_db(&db, || { let interner = DbInterner::new_with(&db, None, None); let goal_ty = match adt_or_type_alias_id { Either::Left(adt_id) => crate::next_solver::Ty::new_adt( @@ -112,29 +112,34 @@ fn eval_expr( ); let (db, file_id) = TestDB::with_single_file(&ra_fixture); - let module_id = db.module_for_file(file_id.file_id(&db)); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id.local_id].scope; - let function_id = scope - .declarations() - .find_map(|x| match x { - hir_def::ModuleDefId::FunctionId(x) => { - let name = - db.function_signature(x).name.display_no_db(file_id.edition(&db)).to_smolstr(); - (name == "main").then_some(x) - } - _ => None, - }) - .unwrap(); - let hir_body = db.body(function_id.into()); - let b = hir_body - .bindings() - .find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal") - .unwrap() - .0; - let infer = db.infer(function_id.into()); - let goal_ty = infer.type_of_binding[b]; - salsa::attach(&db, || db.layout_of_ty(goal_ty, db.trait_environment(function_id.into()))) + crate::attach_db(&db, || { + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let function_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + let name = db + .function_signature(x) + .name + .display_no_db(file_id.edition(&db)) + .to_smolstr(); + (name == "main").then_some(x) + } + _ => None, + }) + .unwrap(); + let hir_body = db.body(function_id.into()); + let b = hir_body + .bindings() + .find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal") + .unwrap() + .0; + let infer = db.infer(function_id.into()); + let goal_ty = infer.type_of_binding[b]; + db.layout_of_ty(goal_ty, db.trait_environment(function_id.into())) + }) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2aa9b8fa192d0..2add66d02d235 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -114,6 +114,7 @@ pub use mapping::{ to_foreign_def_id, to_placeholder_idx, to_placeholder_idx_no_index, }; pub use method_resolution::check_orphan_rules; +pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; pub use traits::TraitEnvironment; pub use utils::{ diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 9e948d1b43c7a..82c0a8070c41a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -12,7 +12,7 @@ use crate::{ use super::{MirEvalError, interpret_mir}; fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError<'_>> { - salsa::attach(db, || { + crate::attach_db(db, || { let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; @@ -56,7 +56,7 @@ fn check_pass_and_stdio( ) { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); - salsa::attach(&db, || { + crate::attach_db(&db, || { let file_id = *file_ids.last().unwrap(); let x = eval_main(&db, file_id); match x { @@ -102,7 +102,7 @@ fn check_pass_and_stdio( fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - salsa::attach(&db, || { + crate::attach_db(&db, || { let file_id = *file_ids.last().unwrap(); let e = eval_main(&db, file_id).unwrap_err(); assert_eq!( @@ -117,7 +117,7 @@ fn check_error_with( expect_err: impl FnOnce(MirEvalError<'_>) -> bool, ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - salsa::attach(&db, || { + crate::attach_db(&db, || { let file_id = *file_ids.last().unwrap(); let e = eval_main(&db, file_id).unwrap_err(); assert!(expect_err(e)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index bac694eabb704..ee088bd06ca6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -11,21 +11,24 @@ fn lower_mir( ) -> FxHashMap, ()>> { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let module_id = db.module_for_file(file_id.file_id(&db)); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id.local_id].scope; - let funcs = scope.declarations().filter_map(|x| match x { - hir_def::ModuleDefId::FunctionId(it) => Some(it), - _ => None, - }); - funcs - .map(|func| { - let name = db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); - let mir = db.mir_body(func.into()); - (name, mir.map_err(drop)) - }) - .collect() + crate::attach_db(&db, || { + let file_id = *file_ids.last().unwrap(); + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let funcs = scope.declarations().filter_map(|x| match x { + hir_def::ModuleDefId::FunctionId(it) => Some(it), + _ => None, + }); + funcs + .map(|func| { + let name = + db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); + let mir = db.mir_body(func.into()); + (name, mir.map_err(drop)) + }) + .collect() + }) } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 0b3582051bc07..aae48eeaf9346 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -41,13 +41,12 @@ impl<'db> Const<'db> { } pub fn inner(&self) -> &WithCachedTypeInfo> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = &self.kind_(db).0; // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } pub fn error(interner: DbInterner<'db>) -> Self { @@ -197,21 +196,19 @@ pub struct Valtree<'db> { impl<'db> Valtree<'db> { pub fn new(bytes: ConstBytes<'db>) -> Self { - salsa::with_attached_database(|db| unsafe { + crate::with_attached_db(|db| unsafe { // SAFETY: ¯\_(ツ)_/¯ std::mem::transmute(Valtree::new_(db, bytes)) }) - .unwrap() } pub fn inner(&self) -> &ConstBytes<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.bytes_(db); // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 2436d1ede8712..4a0ede35ac752 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1,6 +1,8 @@ //! Things related to the Interner in the next-trait-solver. #![allow(unused)] +pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; + use base_db::Crate; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; use hir_def::lang_item::LangItem; @@ -127,11 +129,10 @@ macro_rules! _interned_vec_nolifetime_salsa { pub fn inner(&self) -> &smallvec::SmallVec<[$ty; 2]> { // SAFETY: ¯\_(ツ)_/¯ - salsa::with_attached_database(|db| { + $crate::with_attached_db(|db| { let inner = self.inner_(db); unsafe { std::mem::transmute(inner) } }) - .unwrap() } } @@ -230,11 +231,10 @@ macro_rules! _interned_vec_db { pub fn inner(&self) -> &smallvec::SmallVec<[$ty<'db>; 2]> { // SAFETY: ¯\_(ツ)_/¯ - salsa::with_attached_database(|db| { + $crate::with_attached_db(|db| { let inner = self.inner_(db); unsafe { std::mem::transmute(inner) } }) - .unwrap() } } @@ -285,12 +285,11 @@ unsafe impl Sync for DbInterner<'_> {} impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method pub fn conjure() -> DbInterner<'db> { - salsa::with_attached_database(|db| DbInterner { - db: unsafe { std::mem::transmute::<&dyn salsa::Database, &'db dyn HirDatabase>(db) }, + crate::with_attached_db(|db| DbInterner { + db: unsafe { std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db) }, krate: None, block: None, }) - .expect("db is expected to be attached") } pub fn new_with( @@ -583,12 +582,11 @@ impl AdtDef { } pub fn inner(&self) -> &AdtDefInner { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.data_(db); // SAFETY: ¯\_(ツ)_/¯ unsafe { std::mem::transmute(inner) } }) - .unwrap() } pub fn is_enum(&self) -> bool { @@ -706,21 +704,20 @@ impl<'db> inherent::AdtDef> for AdtDef { impl fmt::Debug for AdtDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - salsa::with_attached_database(|db| match self.inner().id { + crate::with_attached_db(|db| match self.inner().id { AdtId::StructId(struct_id) => { - let data = db.as_view::().struct_signature(struct_id); + let data = db.struct_signature(struct_id); f.write_str(data.name.as_str()) } AdtId::UnionId(union_id) => { - let data = db.as_view::().union_signature(union_id); + let data = db.union_signature(union_id); f.write_str(data.name.as_str()) } AdtId::EnumId(enum_id) => { - let data = db.as_view::().enum_signature(enum_id); + let data = db.enum_signature(enum_id); f.write_str(data.name.as_str()) } }) - .unwrap_or_else(|| f.write_str(&format!("AdtDef({:?})", self.inner().id))) } } @@ -776,13 +773,12 @@ impl<'db> Pattern<'db> { } pub fn inner(&self) -> &PatternKind<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = &self.kind_(db).0; // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } } @@ -1018,17 +1014,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache) -> R, ) -> R { - salsa::with_attached_database(|db| { - tls_cache::with_cache( - unsafe { - std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>( - db.as_view::(), - ) - }, - f, - ) - }) - .unwrap() + tls_cache::with_cache(self.db, f) } fn canonical_param_env_cache_get_or_insert( @@ -2104,6 +2090,117 @@ TrivialTypeTraversalImpls! { Placeholder, } +mod tls_db { + use std::{cell::Cell, ptr::NonNull}; + + use crate::db::HirDatabase; + + struct Attached { + database: Cell>>, + } + + impl Attached { + #[inline] + fn attach(&self, db: &dyn HirDatabase, op: impl FnOnce() -> R) -> R { + struct DbGuard<'s> { + state: Option<&'s Attached>, + } + + impl<'s> DbGuard<'s> { + #[inline] + fn new(attached: &'s Attached, db: &dyn HirDatabase) -> Self { + match attached.database.get() { + Some(current_db) => { + let new_db = NonNull::from(db); + if !std::ptr::addr_eq(current_db.as_ptr(), new_db.as_ptr()) { + panic!( + "Cannot change attached database. This is likely a bug.\n\ + If this is not a bug, you can use `attach_db_allow_change()`." + ); + } + Self { state: None } + } + None => { + // Otherwise, set the database. + attached.database.set(Some(NonNull::from(db))); + Self { state: Some(attached) } + } + } + } + } + + impl Drop for DbGuard<'_> { + #[inline] + fn drop(&mut self) { + // Reset database to null if we did anything in `DbGuard::new`. + if let Some(attached) = self.state { + attached.database.set(None); + } + } + } + + let _guard = DbGuard::new(self, db); + op() + } + + #[inline] + fn attach_allow_change(&self, db: &dyn HirDatabase, op: impl FnOnce() -> R) -> R { + struct DbGuard<'s> { + state: &'s Attached, + prev: Option>, + } + + impl<'s> DbGuard<'s> { + #[inline] + fn new(attached: &'s Attached, db: &dyn HirDatabase) -> Self { + let prev = attached.database.replace(Some(NonNull::from(db))); + Self { state: attached, prev } + } + } + + impl Drop for DbGuard<'_> { + #[inline] + fn drop(&mut self) { + self.state.database.set(self.prev); + } + } + + let _guard = DbGuard::new(self, db); + op() + } + + #[inline] + fn with(&self, op: impl FnOnce(&dyn HirDatabase) -> R) -> R { + let db = self.database.get().expect("Try to use attached db, but not db is attached"); + + // SAFETY: The db is attached, so it must be valid. + op(unsafe { db.as_ref() }) + } + } + + thread_local! { + static GLOBAL_DB: Attached = const { Attached { database: Cell::new(None) } }; + } + + #[inline] + pub fn attach_db(db: &dyn HirDatabase, op: impl FnOnce() -> R) -> R { + GLOBAL_DB.with(|global_db| global_db.attach(db, op)) + } + + #[inline] + pub fn attach_db_allow_change(db: &dyn HirDatabase, op: impl FnOnce() -> R) -> R { + GLOBAL_DB.with(|global_db| global_db.attach_allow_change(db, op)) + } + + #[inline] + pub fn with_attached_db(op: impl FnOnce(&dyn HirDatabase) -> R) -> R { + GLOBAL_DB.with( + #[inline] + |a| a.with(op), + ) + } +} + mod tls_cache { use crate::db::HirDatabase; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 757c599232b93..69afcf5dde90b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -16,10 +16,10 @@ impl<'db> IrPrint> for DbInterner<'db> { } fn print_debug(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - salsa::with_attached_database(|db| match t.def_id { + crate::with_attached_db(|db| match t.def_id { SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( "AliasTy({:?}[{:?}])", - db.as_view::().type_alias_signature(id).name.as_str(), + db.type_alias_signature(id).name.as_str(), t.args )), SolverDefId::InternedOpaqueTyId(id) => { @@ -27,7 +27,6 @@ impl<'db> IrPrint> for DbInterner<'db> { } _ => panic!("Expected TypeAlias or OpaqueTy."), }) - .unwrap_or_else(|| fmt.write_str(&format!("AliasTy({:?}[{:?}])", t.def_id, t.args))) } } @@ -37,10 +36,10 @@ impl<'db> IrPrint> for DbInterner<'db> { } fn print_debug(t: &ty::AliasTerm, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - salsa::with_attached_database(|db| match t.def_id { + crate::with_attached_db(|db| match t.def_id { SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( "AliasTerm({:?}[{:?}])", - db.as_view::().type_alias_signature(id).name.as_str(), + db.type_alias_signature(id).name.as_str(), t.args )), SolverDefId::InternedOpaqueTyId(id) => { @@ -48,7 +47,6 @@ impl<'db> IrPrint> for DbInterner<'db> { } _ => panic!("Expected TypeAlias or OpaqueTy."), }) - .unwrap_or_else(|| fmt.write_str(&format!("AliasTerm({:?}[{:?}])", t.def_id, t.args))) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -57,29 +55,25 @@ impl<'db> IrPrint> for DbInterner<'db> { } fn print_debug(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let trait_ = t.def_id.0; let self_ty = &t.args.as_slice()[0]; let trait_args = &t.args.as_slice()[1..]; if trait_args.is_empty() { - let db = db.zalsa().views().downcaster_for::(); - db.downcast_unchecked(db) fmt.write_str(&format!( "{:?}: {}", self_ty, - db.as_dyn_database().as_dyn_database(), - db.as_view::().trait_signature(trait_).name.as_str() + db.trait_signature(trait_).name.as_str() )) } else { fmt.write_str(&format!( "{:?}: {}<{:?}>", self_ty, - db.as_view::().trait_signature(trait_).name.as_str(), + db.trait_signature(trait_).name.as_str(), trait_args )) } }) - .unwrap_or_else(|| fmt.write_str(&format!("TraitRef({:?}[{:?}])", t.def_id, t.args))) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -121,17 +115,14 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::ExistentialTraitRef, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let trait_ = t.def_id.0; fmt.write_str(&format!( "ExistentialTraitRef({:?}[{:?}])", - db.as_view::().trait_signature(trait_).name.as_str(), + db.trait_signature(trait_).name.as_str(), t.args )) }) - .unwrap_or_else(|| { - fmt.write_str(&format!("ExistentialTraitRef({:?}[{:?}])", t.def_id, t.args)) - }) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -146,24 +137,18 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::ExistentialProjection, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let id = match t.def_id { SolverDefId::TypeAliasId(id) => id, _ => panic!("Expected trait."), }; fmt.write_str(&format!( "ExistentialProjection(({:?}[{:?}]) -> {:?})", - db.as_view::().type_alias_signature(id).name.as_str(), + db.type_alias_signature(id).name.as_str(), t.args, t.term )) }) - .unwrap_or_else(|| { - fmt.write_str(&format!( - "ExistentialProjection(({:?}[{:?}]) -> {:?})", - t.def_id, t.args, t.term - )) - }) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -178,24 +163,18 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::ProjectionPredicate, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let id = match t.projection_term.def_id { SolverDefId::TypeAliasId(id) => id, _ => panic!("Expected trait."), }; fmt.write_str(&format!( "ProjectionPredicate(({:?}[{:?}]) -> {:?})", - db.as_view::().type_alias_signature(id).name.as_str(), + db.type_alias_signature(id).name.as_str(), t.projection_term.args, t.term )) }) - .unwrap_or_else(|| { - fmt.write_str(&format!( - "ProjectionPredicate(({:?}[{:?}]) -> {:?})", - t.projection_term.def_id, t.projection_term.args, t.term - )) - }) } } impl<'db> IrPrint> for DbInterner<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs index 43589ab2ef139..0aee779ed04f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs @@ -24,12 +24,11 @@ impl<'db> PredefinedOpaques<'db> { } pub fn inner(&self) -> &PredefinedOpaquesData<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.kind_(db); // SAFETY: ¯\_(ツ)_/¯ unsafe { std::mem::transmute(inner) } }) - .unwrap() } } @@ -96,12 +95,11 @@ impl<'db> ExternalConstraints<'db> { } pub fn inner(&self) -> &ExternalConstraintsData<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.kind_(db); // SAFETY: ¯\_(ツ)_/¯ unsafe { std::mem::transmute(inner) } }) - .unwrap() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 99b1354b6335f..1623fa342a736 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -232,13 +232,12 @@ impl<'db> Predicate<'db> { } pub fn inner(&self) -> &WithCachedTypeInfo>> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = &self.kind_(db).0; // SAFETY: The caller already has access to a `Predicate<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } /// Flips the polarity of a Predicate. @@ -303,13 +302,12 @@ impl<'db> Clauses<'db> { } pub fn inner(&self) -> &InternedClausesWrapper<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.inner_(db); // SAFETY: The caller already has access to a `Clauses<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index 32c30d19c7849..13c333b9d54d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -35,13 +35,12 @@ impl<'db> Region<'db> { } pub fn inner(&self) -> &RegionKind<'db> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = self.kind_(db); // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) } }) - .unwrap() } pub fn new_early_param( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 5ccd84af8d2f1..11ca0b03eb3dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -68,13 +68,12 @@ impl<'db> Ty<'db> { } pub fn inner(&self) -> &WithCachedTypeInfo> { - salsa::with_attached_database(|db| { + crate::with_attached_db(|db| { let inner = &self.kind_(db).0; // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will // make sure that our returned value is valid for the lifetime `'db`. unsafe { std::mem::transmute(inner) } }) - .unwrap() } pub fn new_adt(interner: DbInterner<'db>, adt_id: AdtId, args: GenericArgs<'db>) -> Self { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 750d09e1a739c..ae240a942f576 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -486,7 +486,7 @@ pub fn sizedness_constraint_for_ty<'db>( Tuple(tys) => tys .into_iter() - .last() + .next_back() .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), Adt(adt, args) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 2a92aa52e0cd2..7044ca5d23ae1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -10,7 +10,7 @@ use base_db::{ use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; use hir_expand::EditionedFileId; use rustc_hash::FxHashMap; -use salsa::{AsDynDatabase, Durability}; +use salsa::Durability; use span::FileId; use syntax::TextRange; use test_utils::extract_annotations; @@ -191,8 +191,7 @@ impl TestDB { // This is pretty horrible, but `Debug` is the only way to inspect // QueryDescriptor at the moment. salsa::EventKind::WillExecute { database_key } => { - let ingredient = self - .as_dyn_database() + let ingredient = (self as &dyn salsa::Database) .ingredient_debug_name(database_key.ingredient_index()); Some(ingredient.to_string()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 5dd9ab75326dc..95a02d534b8ed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -79,7 +79,7 @@ fn check_impl( let _tracing = setup_tracing(); let (db, files) = TestDB::with_many_files(ra_fixture); - salsa::attach(&db, || { + crate::attach_db(&db, || { let mut had_annotations = false; let mut mismatches = FxHashMap::default(); let mut types = FxHashMap::default(); @@ -283,7 +283,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(content); - salsa::attach(&db, || { + crate::attach_db(&db, || { let mut buf = String::new(); let mut infer_def = |inference_result: Arc>, @@ -558,15 +558,17 @@ fn salsa_bug() { ", ); - let module = db.module_for_file(pos.file_id.file_id(&db)); - let crate_def_map = module.def_map(&db); - visit_module(&db, crate_def_map, module.local_id, &mut |def| { - db.infer(match def { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, + crate::attach_db(&db, || { + let module = db.module_for_file(pos.file_id.file_id(&db)); + let crate_def_map = module.def_map(&db); + visit_module(&db, crate_def_map, module.local_id, &mut |def| { + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); }); @@ -597,15 +599,17 @@ fn salsa_bug() { db.set_file_text(pos.file_id.file_id(&db), new_text); - let module = db.module_for_file(pos.file_id.file_id(&db)); - let crate_def_map = module.def_map(&db); - visit_module(&db, crate_def_map, module.local_id, &mut |def| { - db.infer(match def { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, + crate::attach_db(&db, || { + let module = db.module_for_file(pos.file_id.file_id(&db)); + let crate_def_map = module.def_map(&db); + visit_module(&db, crate_def_map, module.local_id, &mut |def| { + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); - }); + }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index d3bc26abd7b0a..8425c0dd89900 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -18,96 +18,105 @@ use super::{setup_tracing, visit_module}; fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(ra_fixture); - let module = db.module_for_file(file_id.file_id(&db)); - let def_map = module.def_map(&db); + crate::attach_db(&db, || { + let module = db.module_for_file(file_id.file_id(&db)); + let def_map = module.def_map(&db); - let mut defs = Vec::new(); - visit_module(&db, def_map, module.local_id, &mut |it| defs.push(it)); + let mut defs = Vec::new(); + visit_module(&db, def_map, module.local_id, &mut |it| defs.push(it)); - let mut captures_info = Vec::new(); - for def in defs { - let def = match def { - hir_def::ModuleDefId::FunctionId(it) => it.into(), - hir_def::ModuleDefId::EnumVariantId(it) => it.into(), - hir_def::ModuleDefId::ConstId(it) => it.into(), - hir_def::ModuleDefId::StaticId(it) => it.into(), - _ => continue, - }; - let infer = db.infer(def); - let db = &db; - captures_info.extend(infer.closure_info.iter().flat_map(|(closure_id, (captures, _))| { - let closure = db.lookup_intern_closure(*closure_id); - let source_map = db.body_with_source_map(closure.0).1; - let closure_text_range = source_map - .expr_syntax(closure.1) - .expect("failed to map closure to SyntaxNode") - .value - .text_range(); - captures.iter().map(move |capture| { - fn text_range( - db: &TestDB, - syntax: InFileWrapper>, - ) -> TextRange { - let root = syntax.file_syntax(db); - syntax.value.to_node(&root).syntax().text_range() - } - - // FIXME: Deduplicate this with hir::Local::sources(). - let (body, source_map) = db.body_with_source_map(closure.0); - let local_text_range = match body.self_param.zip(source_map.self_param_syntax()) { - Some((param, source)) if param == capture.local() => { - format!("{:?}", text_range(db, source)) - } - _ => source_map - .patterns_for_binding(capture.local()) - .iter() - .map(|&definition| { - text_range(db, source_map.pat_syntax(definition).unwrap()) - }) - .map(|it| format!("{it:?}")) - .join(", "), - }; - let place = capture.display_place(closure.0, db); - let capture_ty = salsa::attach(db, || { - capture - .ty - .skip_binder() - .display_test(db, DisplayTarget::from_crate(db, module.krate())) - .to_string() - }); - let spans = capture - .spans() - .iter() - .flat_map(|span| match *span { - MirSpan::ExprId(expr) => { - vec![text_range(db, source_map.expr_syntax(expr).unwrap())] - } - MirSpan::PatId(pat) => { - vec![text_range(db, source_map.pat_syntax(pat).unwrap())] + let mut captures_info = Vec::new(); + for def in defs { + let def = match def { + hir_def::ModuleDefId::FunctionId(it) => it.into(), + hir_def::ModuleDefId::EnumVariantId(it) => it.into(), + hir_def::ModuleDefId::ConstId(it) => it.into(), + hir_def::ModuleDefId::StaticId(it) => it.into(), + _ => continue, + }; + let infer = db.infer(def); + let db = &db; + captures_info.extend(infer.closure_info.iter().flat_map( + |(closure_id, (captures, _))| { + let closure = db.lookup_intern_closure(*closure_id); + let source_map = db.body_with_source_map(closure.0).1; + let closure_text_range = source_map + .expr_syntax(closure.1) + .expect("failed to map closure to SyntaxNode") + .value + .text_range(); + captures.iter().map(move |capture| { + fn text_range( + db: &TestDB, + syntax: InFileWrapper>, + ) -> TextRange { + let root = syntax.file_syntax(db); + syntax.value.to_node(&root).syntax().text_range() } - MirSpan::BindingId(binding) => source_map - .patterns_for_binding(binding) + + // FIXME: Deduplicate this with hir::Local::sources(). + let (body, source_map) = db.body_with_source_map(closure.0); + let local_text_range = + match body.self_param.zip(source_map.self_param_syntax()) { + Some((param, source)) if param == capture.local() => { + format!("{:?}", text_range(db, source)) + } + _ => source_map + .patterns_for_binding(capture.local()) + .iter() + .map(|&definition| { + text_range(db, source_map.pat_syntax(definition).unwrap()) + }) + .map(|it| format!("{it:?}")) + .join(", "), + }; + let place = capture.display_place(closure.0, db); + let capture_ty = capture + .ty + .skip_binder() + .display_test(db, DisplayTarget::from_crate(db, module.krate())) + .to_string(); + let spans = capture + .spans() .iter() - .map(|pat| text_range(db, source_map.pat_syntax(*pat).unwrap())) - .collect(), - MirSpan::SelfParam => { - vec![text_range(db, source_map.self_param_syntax().unwrap())] - } - MirSpan::Unknown => Vec::new(), - }) - .sorted_by_key(|it| it.start()) - .map(|it| format!("{it:?}")) - .join(","); + .flat_map(|span| match *span { + MirSpan::ExprId(expr) => { + vec![text_range(db, source_map.expr_syntax(expr).unwrap())] + } + MirSpan::PatId(pat) => { + vec![text_range(db, source_map.pat_syntax(pat).unwrap())] + } + MirSpan::BindingId(binding) => source_map + .patterns_for_binding(binding) + .iter() + .map(|pat| text_range(db, source_map.pat_syntax(*pat).unwrap())) + .collect(), + MirSpan::SelfParam => { + vec![text_range(db, source_map.self_param_syntax().unwrap())] + } + MirSpan::Unknown => Vec::new(), + }) + .sorted_by_key(|it| it.start()) + .map(|it| format!("{it:?}")) + .join(","); - (closure_text_range, local_text_range, spans, place, capture_ty, capture.kind()) - }) - })); - } - captures_info.sort_unstable_by_key(|(closure_text_range, local_text_range, ..)| { - (closure_text_range.start(), local_text_range.clone()) - }); + ( + closure_text_range, + local_text_range, + spans, + place, + capture_ty, + capture.kind(), + ) + }) + }, + )); + } + captures_info.sort_unstable_by_key(|(closure_text_range, local_text_range, ..)| { + (closure_text_range.start(), local_text_range.clone()) + }); - let rendered = captures_info + let rendered = captures_info .iter() .map(|(closure_text_range, local_text_range, spans, place, capture_ty, capture_kind)| { format!( @@ -116,7 +125,8 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec }) .join("\n"); - expect.assert_eq(&rendered); + expect.assert_eq(&rendered); + }) } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index ce3de06127e49..76cd5f7ab3302 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -710,8 +710,8 @@ fn execute_assert_events( required: &[(&str, usize)], expect: Expect, ) { - let (executed, events) = db.log_executed(f); - salsa::attach(db, || { + crate::attach_db(db, || { + let (executed, events) = db.log_executed(f); for (event, count) in required { let n = executed.iter().filter(|it| it.contains(event)).count(); assert_eq!( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index a17cf3782701a..0ff110106ebe5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -1001,84 +1001,86 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); // )); let (db, file_id) = TestDB::with_single_file(ra_fixture); - let mut defs: Vec = Vec::new(); - let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); - let def_map = module.def_map(&db); - crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::AdtId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::TraitId(it) => it.into(), - ModuleDefId::TypeAliasId(it) => it.into(), - _ => return, - }) - }); - let defs = defs - .into_iter() - .filter_map(|def| { - Some(( - def, - match def { - GenericDefId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::EnumId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::StructId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::UnionId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TraitId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TypeAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::ImplId(_) => return None, - GenericDefId::ConstId(_) => return None, - GenericDefId::StaticId(_) => return None, - }, - )) - }) - .sorted_by_key(|(_, n)| n.syntax().text_range().start()); - let mut res = String::new(); - for (def, name) in defs { - let Some(variances) = db.variances_of(def) else { - continue; - }; - format_to!( - res, - "{name}[{}]\n", - generics(&db, def) - .iter() - .map(|(_, param)| match param { - GenericParamDataRef::TypeParamData(type_param_data) => { - type_param_data.name.as_ref().unwrap() - } - GenericParamDataRef::ConstParamData(const_param_data) => - &const_param_data.name, - GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { - &lifetime_param_data.name - } - }) - .zip_eq(&*variances) - .format_with(", ", |(name, var), f| f(&format_args!( - "{}: {var}", - name.as_str() - ))) - ); - } + crate::attach_db(&db, || { + let mut defs: Vec = Vec::new(); + let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); + let def_map = module.def_map(&db); + crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::AdtId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::TraitId(it) => it.into(), + ModuleDefId::TypeAliasId(it) => it.into(), + _ => return, + }) + }); + let defs = defs + .into_iter() + .filter_map(|def| { + Some(( + def, + match def { + GenericDefId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::EnumId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::StructId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::UnionId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TypeAliasId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::ImplId(_) => return None, + GenericDefId::ConstId(_) => return None, + GenericDefId::StaticId(_) => return None, + }, + )) + }) + .sorted_by_key(|(_, n)| n.syntax().text_range().start()); + let mut res = String::new(); + for (def, name) in defs { + let Some(variances) = db.variances_of(def) else { + continue; + }; + format_to!( + res, + "{name}[{}]\n", + generics(&db, def) + .iter() + .map(|(_, param)| match param { + GenericParamDataRef::TypeParamData(type_param_data) => { + type_param_data.name.as_ref().unwrap() + } + GenericParamDataRef::ConstParamData(const_param_data) => + &const_param_data.name, + GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { + &lifetime_param_data.name + } + }) + .zip_eq(&*variances) + .format_with(", ", |(name, var), f| f(&format_args!( + "{}: {var}", + name.as_str() + ))) + ); + } - expected.assert_eq(&res); + expected.assert_eq(&res); + }) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index c36a8677ddb3a..ae52986008f11 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -157,7 +157,7 @@ pub use { tt, }, hir_ty::{ - CastError, DropGlue, FnAbi, PointerCast, Variance, + CastError, DropGlue, FnAbi, PointerCast, Variance, attach_db, attach_db_allow_change, consteval::ConstEvalError, diagnostics::UnsafetyReason, display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite}, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 5a2849220ed3f..1faa3c4165fdd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -14,7 +14,6 @@ use crate::{ db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; -use base_db::salsa; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, @@ -1637,7 +1636,7 @@ fn resolve_hir_path_( Some(unresolved) => resolver .generic_def() .and_then(|def| { - salsa::attach(db, || { + hir_ty::attach_db(db, || { hir_ty::associated_type_shorthand_candidates( db, def, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index c0637a7470f3e..ade60691b57bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -5,7 +5,7 @@ use hir::{Semantics, db::HirDatabase, setup_tracing}; use ide_db::{ EditionedFileId, FileRange, RootDatabase, SnippetCap, assists::ExprFillDefaultMode, - base_db::{SourceDatabase, salsa}, + base_db::SourceDatabase, imports::insert_use::{ImportGranularity, InsertUseConfig}, source_change::FileSystemEdit, }; @@ -109,7 +109,7 @@ fn assists( resolve: AssistResolveStrategy, range: ide_db::FileRange, ) -> Vec { - salsa::attach(db, || { + hir::attach_db(db, || { HirDatabase::zalsa_register_downcaster(db); crate::assists(db, config, resolve, range) }) @@ -332,7 +332,7 @@ fn check_with_config( _ => AssistResolveStrategy::All, }; let mut acc = Assists::new(&ctx, resolve); - salsa::attach(&db, || { + hir::attach_db(&db, || { HirDatabase::zalsa_register_downcaster(&db); handler(&mut acc, &ctx); }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 77a94403abb94..9ab7b7c82e432 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1,7 +1,6 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use base_db::salsa; use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant}; use ide_db::{RootDatabase, active_parameter::ActiveParameter}; use itertools::Either; @@ -86,7 +85,7 @@ pub(super) fn expand_and_analyze<'db>( let original_offset = expansion.original_offset + relative_offset; let token = expansion.original_file.token_at_offset(original_offset).left_biased()?; - salsa::attach(sema.db, || analyze(sema, expansion, original_token, &token)).map( + hir::attach_db(sema.db, || analyze(sema, expansion, original_token, &token)).map( |(analysis, expected, qualifier_ctx)| AnalysisResult { analysis, expected, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index d9ec7915e3c72..24647ab6279dc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -1,4 +1,3 @@ -use base_db::salsa; use expect_test::{Expect, expect}; use hir::HirDisplay; @@ -11,12 +10,12 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, let (db, pos) = position(ra_fixture); let config = TEST_CONFIG; let (completion_context, _analysis) = - salsa::attach(&db, || CompletionContext::new(&db, pos, &config).unwrap()); + hir::attach_db(&db, || CompletionContext::new(&db, pos, &config).unwrap()); let ty = completion_context .expected_type .map(|t| { - salsa::attach(&db, || { + hir::attach_db(&db, || { t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string() }) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b20b570c2b8df..ec9cd9fdf3782 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -24,7 +24,7 @@ mod type_pos; mod use_tree; mod visibility; -use base_db::{SourceDatabase, salsa}; +use base_db::SourceDatabase; use expect_test::Expect; use hir::db::HirDatabase; use hir::{PrefixKind, setup_tracing}; @@ -244,7 +244,7 @@ pub(crate) fn check_edit_with_config( let ra_fixture_after = trim_indent(ra_fixture_after); let (db, position) = position(ra_fixture_before); let completions: Vec = - salsa::attach(&db, || crate::completions(&db, &config, position, None).unwrap()); + hir::attach_db(&db, || crate::completions(&db, &config, position, None).unwrap()); let (completion,) = completions .iter() .filter(|it| it.lookup() == what) @@ -307,7 +307,7 @@ pub(crate) fn get_all_items( trigger_character: Option, ) -> Vec { let (db, position) = position(code); - let res = salsa::attach(&db, || { + let res = hir::attach_db(&db, || { HirDatabase::zalsa_register_downcaster(&db); crate::completions(&db, &config, position, trigger_character) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d78a3731011e8..2d3ebad9340c7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1,4 +1,3 @@ -use base_db::salsa; use expect_test::{Expect, expect}; use crate::{ @@ -20,7 +19,7 @@ fn check_with_config( let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); let mut acc = crate::completions::Completions::default(); - salsa::attach(ctx.db, || { + hir::attach_db(ctx.db, || { if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = &analysis { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 76b647f8e9f2d..c5ea9bcf5f107 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -134,7 +134,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar let _p = tracing::info_span!("library_symbols").entered(); // We call this without attaching because this runs in parallel, so we need to attach here. - salsa::attach(db, || { + hir::attach_db(db, || { let mut symbol_collector = SymbolCollector::new(db); db.source_root_crates(source_root_id) @@ -153,7 +153,7 @@ fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc let _p = tracing::info_span!("module_symbols").entered(); // We call this without attaching because this runs in parallel, so we need to attach here. - salsa::attach(db, || Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module)))) + hir::attach_db(db, || Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module)))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 2e03665765f38..1a0ef55a8b259 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -473,7 +473,7 @@ mod tests { frange.range, "selection is not an expression(yet contained in one)" ); - let name = salsa::attach(sema.db, || NameGenerator::default().for_variable(&expr, &sema)); + let name = hir::attach_db(sema.db, || NameGenerator::default().for_variable(&expr, &sema)); assert_eq!(&name, expected); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index 1839ab1c58c1e..37af05e0d1bbf 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -6,7 +6,7 @@ use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{SourceDatabase, salsa}, + base_db::SourceDatabase, }; use itertools::Itertools; use stdx::trim_indent; @@ -74,7 +74,7 @@ fn check_nth_fix_with_config( let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let diagnostic = salsa::attach(&db, || { + let diagnostic = hir::attach_db(&db, || { super::full_diagnostics( &db, &config, @@ -129,7 +129,7 @@ pub(crate) fn check_has_fix( let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; - let fix = salsa::attach(&db, || { + let fix = hir::attach_db(&db, || { super::full_diagnostics( &db, &conf, @@ -170,7 +170,7 @@ pub(crate) fn check_has_fix( /// Checks that there's a diagnostic *without* fix at `$0`. pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture); - let diagnostic = salsa::attach(&db, || { + let diagnostic = hir::attach_db(&db, || { super::full_diagnostics( &db, &DiagnosticsConfig::test_sample(), @@ -212,7 +212,7 @@ pub(crate) fn check_diagnostics_with_config( .iter() .copied() .flat_map(|file_id| { - salsa::attach(&db, || { + hir::attach_db(&db, || { super::full_diagnostics( &db, &config, @@ -288,12 +288,12 @@ fn test_disabled_diagnostics() { let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); let file_id = file_id.file_id(&db); - let diagnostics = salsa::attach(&db, || { + let diagnostics = hir::attach_db(&db, || { super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id) }); assert!(diagnostics.is_empty()); - let diagnostics = salsa::attach(&db, || { + let diagnostics = hir::attach_db(&db, || { super::full_diagnostics( &db, &DiagnosticsConfig::test_sample(), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 9c0b9a6ff99bb..264f0660d7f29 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -791,8 +791,6 @@ impl PatternIterator { #[cfg(test)] mod tests { - use ide_db::base_db::salsa; - use crate::{MatchFinder, SsrRule}; #[test] @@ -801,7 +799,7 @@ mod tests { let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }"; let (db, position, selections) = crate::tests::single_file(input); - salsa::attach(&db, || { + hir::attach_db(&db, || { let position = ide_db::FilePosition { file_id: position.file_id.file_id(&db), offset: position.offset, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index 1d5f5adf2eefe..a48c0f8a57ec4 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -1,7 +1,7 @@ //! This module is responsible for resolving paths within rules. use hir::AsAssocItem; -use ide_db::{FxHashMap, base_db::salsa}; +use ide_db::FxHashMap; use parsing::Placeholder; use syntax::{ SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, @@ -48,7 +48,7 @@ impl<'db> ResolvedRule<'db> { resolution_scope: &ResolutionScope<'db>, index: usize, ) -> Result, SsrError> { - salsa::attach(resolution_scope.scope.db, || { + hir::attach_db(resolution_scope.scope.db, || { let resolver = Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 24ad3ba7efe0b..1bb435f31f29d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -2,10 +2,7 @@ use expect_test::{Expect, expect}; use hir::{FilePosition, FileRange}; use ide_db::{ EditionedFileId, FxHashSet, - base_db::{ - SourceDatabase, - salsa::{self, Durability}, - }, + base_db::{SourceDatabase, salsa::Durability}, }; use test_utils::RangeOrOffset; use triomphe::Arc; @@ -101,7 +98,7 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { let (db, position, selections) = single_file(input); - salsa::attach(&db, || { + hir::attach_db(&db, || { let position = ide_db::FilePosition { file_id: position.file_id.file_id(&db), offset: position.offset, @@ -149,7 +146,7 @@ fn print_match_debug_info(match_finder: &MatchFinder<'_>, file_id: EditionedFile fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { let (db, position, selections) = single_file(code); - salsa::attach(&db, || { + hir::attach_db(&db, || { let mut match_finder = MatchFinder::in_context( &db, ide_db::FilePosition { @@ -177,7 +174,7 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { fn assert_no_match(pattern: &str, code: &str) { let (db, position, selections) = single_file(code); - salsa::attach(&db, || { + hir::attach_db(&db, || { let mut match_finder = MatchFinder::in_context( &db, ide_db::FilePosition { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 72436307d2cee..3fd885535a234 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -4,7 +4,6 @@ use expect_test::{Expect, expect}; use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, - base_db::salsa, defs::Definition, documentation::{DocsRangeMap, Documentation, HasDocs}, }; @@ -48,7 +47,7 @@ fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect let sema = &Semantics::new(&analysis.db); let (cursor_def, docs, range) = def_under_cursor(sema, &position); let res = - salsa::attach(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range))); + hir::attach_db(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range))); expect.assert_eq(&res) } @@ -65,7 +64,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { .flat_map(|(text_range, link, ns)| { let attr = range.map(text_range); let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); - let def = salsa::attach(sema.db, || { + let def = hir::attach_db(sema.db, || { resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) .unwrap_or_else(|| panic!("Failed to resolve {link}")) }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 507af41d84461..f7b09b43813d6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -8,9 +8,7 @@ use hir::{ ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{ - FileRange, RootDatabase, base_db::salsa, famous_defs::FamousDefs, text_edit::TextEditBuilder, -}; +use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; use smallvec::{SmallVec, smallvec}; @@ -107,7 +105,7 @@ pub(crate) fn inlay_hints( } }; let mut preorder = file.preorder(); - salsa::attach(sema.db, || { + hir::attach_db(sema.db, || { while let Some(event) = preorder.next() { if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) { @@ -739,7 +737,7 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - salsa::attach(sema.db, || { + hir::attach_db(sema.db, || { let iter_item_type = hint_iterator(sema, famous_defs, ty); match iter_item_type { Some((iter_trait, item, ty)) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 0fd587a728408..7231a3194d095 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -10,7 +10,7 @@ use hir::{ Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, }; -use ide_db::{base_db::salsa, famous_defs::FamousDefs}; +use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::TextEditBuilder; use syntax::ast::{self, AstNode, prec::ExprPrecedence}; @@ -216,7 +216,7 @@ pub(super) fn hints( text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, linked_location: None, tooltip: Some(config.lazy_tooltip(|| { - salsa::attach(sema.db, || { + hir::attach_db(sema.db, || { InlayTooltip::Markdown(format!( "`{}` → `{}`\n\n**{}**\n\n{}", source.display(sema.db, display_target), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 5febe4ee20bc6..f7d21c9479505 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -67,7 +67,7 @@ use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, - salsa::{self, Cancelled}, + salsa::Cancelled, }, prime_caches, symbol_index, }; @@ -480,7 +480,7 @@ impl Analysis { // if we were to attach it here. Cancelled::catch(|| { let symbols = symbol_index::world_symbols(&self.db, query); - salsa::attach(&self.db, || { + hir::attach_db(&self.db, || { symbols .into_iter() .filter_map(|s| s.try_to_nav(&Semantics::new(&self.db))) @@ -899,7 +899,7 @@ impl Analysis { where F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, { - salsa::attach(&self.db, || { + hir::attach_db(&self.db, || { // the trait solver code may invoke `as_view` outside of queries, // so technically we might run into a panic in salsa if the downcaster has not yet been registered. HirDatabase::zalsa_register_downcaster(&self.db); diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 46ff16f972625..db1298385b113 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, - base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb, salsa}, + base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb}, defs::{Definition, find_std_module}, documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, @@ -399,7 +399,7 @@ where ) .map(|mut res| { res.docs = self.docs(db); - res.description = salsa::attach(db, || { + res.description = hir::attach_db(db, || { Some(self.display(db, self.krate(db).to_display_target(db)).to_string()) }); res.container_name = self.container_name(db); @@ -520,7 +520,7 @@ impl TryToNav for hir::Field { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { res.docs = self.docs(db); - res.description = salsa::attach(db, || { + res.description = hir::attach_db(db, || { Some(self.display(db, krate.to_display_target(db)).to_string()) }); res diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index ec13ba0fde340..cc1bbfbe20d63 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -10,7 +10,7 @@ use hir::{ use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, - base_db::{RootQueryDb, salsa}, + base_db::RootQueryDb, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, @@ -413,7 +413,7 @@ pub(crate) fn runnable_impl( let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); - let params = salsa::attach(sema.db, || { + let params = hir::attach_db(sema.db, || { if ty_args.peek().is_some() { format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) } else { @@ -522,7 +522,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op let mut ty_args = ty.generic_parameters(db, display_target).peekable(); format_to!(path, "{}", name.display(db, edition)); if ty_args.peek().is_some() { - salsa::attach(db, || { + hir::attach_db(db, || { format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); }); } diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index e74d997e97c52..5f7e12cf53f84 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -11,7 +11,6 @@ use hir::{ use ide_db::{ FilePosition, FxIndexMap, active_parameter::{callable_for_arg_list, generic_def_for_node}, - base_db::salsa, documentation::{Documentation, HasDocs}, }; use itertools::Itertools; @@ -267,7 +266,7 @@ fn signature_help_for_call( // In that case, fall back to render definitions of the respective parameters. // This is overly conservative: we do not substitute known type vars // (see FIXME in tests::impl_trait) and falling back on any unknowns. - salsa::attach(db, || match (p.ty().contains_unknown(), fn_params.as_deref()) { + hir::attach_db(db, || match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => { format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target)) } @@ -730,7 +729,7 @@ fn signature_help_for_tuple_pat_ish<'db>( mod tests { use expect_test::{Expect, expect}; - use ide_db::{FilePosition, base_db::salsa}; + use ide_db::FilePosition; use stdx::format_to; use test_fixture::ChangeFixture; @@ -759,7 +758,7 @@ mod tests { "# ); let (db, position) = position(&fixture); - let sig_help = salsa::attach(&db, || crate::signature_help::signature_help(&db, position)); + let sig_help = hir::attach_db(&db, || crate::signature_help::signature_help(&db, position)); let actual = match sig_help { Some(sig_help) => { let mut rendered = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 3f3d36bde2fa6..453d6f537a8bf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -5,7 +5,7 @@ use arrayvec::ArrayVec; use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, - base_db::{RootQueryDb, SourceDatabase, VfsPath, salsa}, + base_db::{RootQueryDb, SourceDatabase, VfsPath}, defs::{Definition, IdentClass}, documentation::Documentation, famous_defs::FamousDefs, @@ -276,7 +276,7 @@ impl StaticIndex<'_> { for token in tokens { let range = token.text_range(); let node = token.parent().unwrap(); - match salsa::attach(self.db, || get_definitions(&sema, token.clone())) { + match hir::attach_db(self.db, || get_definitions(&sema, token.clone())) { Some(it) => { for i in it { add_token(i, range, &node); @@ -293,7 +293,7 @@ impl StaticIndex<'_> { vendored_libs_config: VendoredLibrariesConfig<'_>, ) -> StaticIndex<'a> { let db = &analysis.db; - salsa::attach(db, || { + hir::attach_db(db, || { let work = all_modules(db).into_iter().filter(|module| { let file_id = module.definition_source_file_id(db).original_file(db); let source_root = diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 720183ac5478d..079dedda000c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -16,7 +16,7 @@ use std::ops::ControlFlow; use either::Either; use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, base_db::salsa}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -428,7 +428,7 @@ fn traverse( Some(current_body) => { let (ops, bindings) = per_body_cache.entry(current_body).or_insert_with(|| { ( - salsa::attach(sema.db, || sema.get_unsafe_ops(current_body)), + hir::attach_db(sema.db, || sema.get_unsafe_ops(current_body)), Default::default(), ) }); @@ -440,7 +440,7 @@ fn traverse( |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); let element = match descended_element.value { NodeOrToken::Node(name_like) => { - let hl = salsa::attach(sema.db, || { + let hl = hir::attach_db(sema.db, || { highlight::name_like( sema, krate, @@ -458,7 +458,7 @@ fn traverse( } hl } - NodeOrToken::Token(token) => salsa::attach(sema.db, || { + NodeOrToken::Token(token) => hir::attach_db(sema.db, || { highlight::token(sema, token, edition, &is_unsafe_node, tt_level > 0) .zip(Some(None)) }), diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 4bb7308024144..efc77823a2a45 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -5,7 +5,7 @@ use std::mem; use either::Either; use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; use ide_db::{ - SymbolKind, active_parameter::ActiveParameter, base_db::salsa, defs::Definition, + SymbolKind, active_parameter::ActiveParameter, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence, }; use syntax::{ @@ -27,7 +27,7 @@ pub(super) fn ra_fixture( expanded: &ast::String, ) -> Option<()> { let active_parameter = - salsa::attach(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?; + hir::attach_db(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?; let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| { attrs.filter_map(|attr| attr.as_simple_path()).any(|path| { path.segments() @@ -128,7 +128,7 @@ pub(super) fn doc_comment( // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { - salsa::attach(sema.db, || { + hir::attach_db(sema.db, || { extract_definitions_from_docs(&docs) .into_iter() .filter_map(|(range, link, ns)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index b014261981769..47ca616f3199b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -213,14 +213,13 @@ mod tests { use crate::fixture; use expect_test::expect; - use ide_db::base_db::salsa; fn make_memory_layout( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> Option { let (analysis, position, _) = fixture::annotations(ra_fixture); - salsa::attach(&analysis.db, || view_memory_layout(&analysis.db, position)) + hir::attach_db(&analysis.db, || view_memory_layout(&analysis.db, position)) } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 9c8743536877b..24619004e1c0c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -26,10 +26,7 @@ use ide::{ }; use ide_db::{ EditionedFileId, LineIndexDatabase, SnippetCap, - base_db::{ - SourceDatabase, - salsa::{self, Database}, - }, + base_db::{SourceDatabase, salsa::Database}, }; use itertools::Itertools; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; @@ -315,7 +312,7 @@ impl flags::AnalysisStats { shuffle(&mut rng, &mut bodies); } - salsa::attach(db, || { + hir::attach_db(db, || { if !self.skip_lowering { self.run_body_lowering(db, &vfs, &bodies, verbosity); } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 67ff27e31c0c3..6c363825aa185 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -67,6 +67,16 @@ const _: () = { self.parent.hash(state); } } + + impl zalsa_::HasJar for SyntaxContext { + type Jar = zalsa_struct_::JarImpl; + const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct; + } + + zalsa_::register_jar! { + zalsa_::ErasedJar::erase::() + } + /// Key to use during hash lookups. Each field is some type that implements `Lookup` /// for the owned type. This permits interning with an `&str` when a `String` is required and so forth. #[derive(Hash)] From 7c8fe29fd65d994e25a8eed3d0e2bbed3415ca61 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 03:07:51 -0400 Subject: [PATCH 1275/1900] solve autodiffv2.rs FIXME and make identical_fnc test more robust --- tests/codegen-llvm/autodiff/autodiffv2.rs | 31 ++++++++------------ tests/codegen-llvm/autodiff/identical_fnc.rs | 4 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/codegen-llvm/autodiff/autodiffv2.rs b/tests/codegen-llvm/autodiff/autodiffv2.rs index 85aed6a183b63..c24a374148c34 100644 --- a/tests/codegen-llvm/autodiff/autodiffv2.rs +++ b/tests/codegen-llvm/autodiff/autodiffv2.rs @@ -18,11 +18,6 @@ // but each shadow argument is `width` times larger (thus 16 and 20 elements here). // `d_square3` instead takes `width` (4) shadow arguments, which are all the same size as the // original function arguments. -// -// FIXME(autodiff): We currently can't test `d_square1` and `d_square3` in the same file, since they -// generate the same dummy functions which get merged by LLVM, breaking pieces of our pipeline which -// try to rewrite the dummy functions later. We should consider to change to pure declarations both -// in our frontend and in the llvm backend to avoid these issues. #![feature(autodiff)] @@ -30,7 +25,7 @@ use std::autodiff::autodiff_forward; // CHECK: ; #[no_mangle] -//#[autodiff(d_square1, Forward, Dual, Dual)] +#[autodiff_forward(d_square1, Dual, Dual)] #[autodiff_forward(d_square2, 4, Dualv, Dualv)] #[autodiff_forward(d_square3, 4, Dual, Dual)] fn square(x: &[f32], y: &mut [f32]) { @@ -79,25 +74,25 @@ fn main() { let mut dy3_4 = std::hint::black_box(vec![0.0; 5]); // scalar. - //d_square1(&x1, &z1, &mut y1, &mut dy1_1); - //d_square1(&x1, &z2, &mut y2, &mut dy1_2); - //d_square1(&x1, &z3, &mut y3, &mut dy1_3); - //d_square1(&x1, &z4, &mut y4, &mut dy1_4); + d_square1(&x1, &z1, &mut y1, &mut dy1_1); + d_square1(&x1, &z2, &mut y2, &mut dy1_2); + d_square1(&x1, &z3, &mut y3, &mut dy1_3); + d_square1(&x1, &z4, &mut y4, &mut dy1_4); // assert y1 == y2 == y3 == y4 - //for i in 0..5 { - // assert_eq!(y1[i], y2[i]); - // assert_eq!(y1[i], y3[i]); - // assert_eq!(y1[i], y4[i]); - //} + for i in 0..5 { + assert_eq!(y1[i], y2[i]); + assert_eq!(y1[i], y3[i]); + assert_eq!(y1[i], y4[i]); + } // batch mode A) d_square2(&x1, &z5, &mut y5, &mut dy2); // assert y1 == y2 == y3 == y4 == y5 - //for i in 0..5 { - // assert_eq!(y1[i], y5[i]); - //} + for i in 0..5 { + assert_eq!(y1[i], y5[i]); + } // batch mode B) d_square3(&x1, &z1, &z2, &z3, &z4, &mut y6, &mut dy3_1, &mut dy3_2, &mut dy3_3, &mut dy3_4); diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs index 6066f8cb34fb9..1c18e7acc4b63 100644 --- a/tests/codegen-llvm/autodiff/identical_fnc.rs +++ b/tests/codegen-llvm/autodiff/identical_fnc.rs @@ -32,9 +32,9 @@ fn square2(x: &f64) -> f64 { // CHECK-NOT:br // CHECK-NOT:ret // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) fn main() { let x = std::hint::black_box(3.0); From 22c1eb85f88f6036c771b4ee89d128f9e05a7e9c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 5 Oct 2025 15:18:48 +0800 Subject: [PATCH 1276/1900] minor: Remove FIXME for test_tuple_field_inference This seems to have been fixed --- .../rust-analyzer/crates/ide-completion/src/completions/dot.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 8b4f315ac5733..72b245ccafd90 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -794,8 +794,7 @@ struct T(S); impl T { fn foo(&self) { - // FIXME: This doesn't work without the trailing `a` as `0.` is a float - self.0.a$0 + self.0.$0 } } "#, From 99c7959bb7fde07f1326c52312c12671239849b7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 16:59:58 +0200 Subject: [PATCH 1277/1900] Add regression test for rustdoc output format --- .../rustdoc-doctest-output-format/file.rs | 3 + .../rustdoc-doctest-output-format/rmake.rs | 83 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tests/run-make/rustdoc-doctest-output-format/file.rs create mode 100644 tests/run-make/rustdoc-doctest-output-format/rmake.rs diff --git a/tests/run-make/rustdoc-doctest-output-format/file.rs b/tests/run-make/rustdoc-doctest-output-format/file.rs new file mode 100644 index 0000000000000..51d17849fd718 --- /dev/null +++ b/tests/run-make/rustdoc-doctest-output-format/file.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-doctest-output-format/rmake.rs b/tests/run-make/rustdoc-doctest-output-format/rmake.rs new file mode 100644 index 0000000000000..61b5c0c4fd11d --- /dev/null +++ b/tests/run-make/rustdoc-doctest-output-format/rmake.rs @@ -0,0 +1,83 @@ +//! Regression test to ensure that the output format is respected for doctests. +//! +//! Regression test for . + +//@ ignore-cross-compile + +use run_make_support::{rustdoc, serde_json}; + +fn run_test(edition: &str, format: Option<&str>) -> String { + let mut r = rustdoc(); + r.input("file.rs").edition(edition).arg("--test"); + if let Some(format) = format { + r.args(&[ + "--test-args", + "-Zunstable-options", + "--test-args", + "--format", + "--test-args", + format, + ]); + } + r.run().stdout_utf8() +} + +fn check_json_output(edition: &str, expected_reports: usize) { + let out = run_test(edition, Some("json")); + let mut found_report = 0; + for (line_nb, line) in out.lines().enumerate() { + match serde_json::from_str::(&line) { + Ok(value) => { + if value.get("type") == Some(&serde_json::json!("report")) { + found_report += 1; + } + } + Err(error) => panic!( + "failed for {edition} edition (json format) at line {}: non-JSON value: {error}\n\ + ====== output ======\n{out}", + line_nb + 1, + ), + } + } + if found_report != expected_reports { + panic!( + "failed for {edition} edition (json format): expected {expected_reports} doctest \ + time `report`, found {found_report}\n====== output ======\n{out}", + ); + } +} + +fn check_non_json_output(edition: &str, expected_reports: usize) { + let out = run_test(edition, None); + let mut found_report = 0; + for (line_nb, line) in out.lines().enumerate() { + if line.starts_with('{') && serde_json::from_str::(&line).is_ok() { + panic!( + "failed for {edition} edition: unexpected json at line {}: `{line}`\n\ + ====== output ======\n{out}", + line_nb + 1 + ); + } + if line.starts_with("all doctests ran in") + && line.contains("; merged doctests compilation took ") + { + found_report += 1; + } + } + if found_report != expected_reports { + panic!( + "failed for {edition} edition: expected {expected_reports} doctest time `report`, \ + found {found_report}\n====== output ======\n{out}", + ); + } +} + +fn main() { + // Only the merged doctests generate the "times report". + check_json_output("2021", 0); + check_json_output("2024", 1); + + // Only the merged doctests generate the "times report". + check_non_json_output("2021", 0); + check_non_json_output("2024", 1); +} From 95445f9b9652c24a48493bafdc5a8fce12927aec Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 5 Oct 2025 17:27:45 +0800 Subject: [PATCH 1278/1900] Trivial code cleanup in resolve --- compiler/rustc_resolve/src/imports.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index ce90a1bcd313d..04eb312a0f8ba 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -741,14 +741,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { errors.retain(|(_import, err)| match err.module { // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, - _ => true, - }); - errors.retain(|(_import, err)| { // If we've encountered something like `use _;`, we've already emitted an error stating // that `_` is not a valid identifier, so we ignore that resolve error. - err.segment != Some(kw::Underscore) + _ => err.segment != Some(kw::Underscore), }); - if errors.is_empty() { self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); return; From 9ccaf080cbef74aae96760564fd23447d5aa906b Mon Sep 17 00:00:00 2001 From: nora <48135649+Noratrieb@users.noreply.github.com> Date: Sun, 5 Oct 2025 12:01:23 +0200 Subject: [PATCH 1279/1900] Fill out AVR target metadata This will make `-Zbuild-std` automatically build the right crates, notably not building `std` by default, which will both be useful for users and also fix the build for https://does-it-build.noratrieb.dev. --- compiler/rustc_target/src/spec/targets/avr_none.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs index ad056d0232670..65a171e4515af 100644 --- a/compiler/rustc_target/src/spec/targets/avr_none.rs +++ b/compiler/rustc_target/src/spec/targets/avr_none.rs @@ -5,9 +5,9 @@ pub(crate) fn target() -> Target { arch: "avr".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, - host_tools: None, - std: None, + tier: Some(3), + host_tools: Some(false), + std: Some(false), }, data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8".into(), llvm_target: "avr-unknown-unknown".into(), From a4d51da309d33461d4a6aabf2acda3454cdb2485 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Sun, 5 Oct 2025 13:40:25 +0200 Subject: [PATCH 1280/1900] docs: Mention editor-independent configuration --- src/tools/rust-analyzer/docs/book/src/configuration.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md index 3f4cb197141fe..708eecd43561c 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration.md @@ -46,6 +46,11 @@ To verify which configuration is actually used by `rust-analyzer`, set config-related messages. Logs should show both the JSON that `rust-analyzer` sees as well as the updated config. +(Work in progress:) It is also possible to place configuration in a +`rust-analyzer.toml` file. It should be located in the project root or in your +user configuration directory (e.g. `~/.config/rust-analyzer/`). This is a work in +progress, many configuration options aren't supported yet. + This is the list of config options `rust-analyzer` supports: {{#include configuration_generated.md}} From 0bc6945186ac733d959f761092fcf2be5f6e77a1 Mon Sep 17 00:00:00 2001 From: cyrgani <85427285+cyrgani@users.noreply.github.com> Date: Sun, 5 Oct 2025 13:58:11 +0200 Subject: [PATCH 1281/1900] give a better example why `std` modules named like primitives are needed --- library/std/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index da41c1216c4d5..46eb120d65de3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -63,10 +63,10 @@ //! type, but not the all-important methods. //! //! So for example there is a [page for the primitive type -//! `i32`](primitive::i32) that lists all the methods that can be called on -//! 32-bit integers (very useful), and there is a [page for the module -//! `std::i32`] that documents the constant values [`MIN`] and [`MAX`] (rarely -//! useful). +//! `char`](primitive::char) that lists all the methods that can be called on +//! characters (very useful), and there is a [page for the module +//! `std::char`] that documents iterator and error types created by these methods +//! (rarely useful). //! //! Note the documentation for the primitives [`str`] and [`[T]`][prim@slice] (also //! called 'slice'). Many method calls on [`String`] and [`Vec`] are actually From a316f5b72e6a9277f0fb912eab8e7a47074bb500 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sun, 5 Oct 2025 12:57:48 +0200 Subject: [PATCH 1282/1900] don't make empty ident when printing `'` ident from `extern "'"` --- compiler/rustc_span/src/symbol.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc37..35786d8932878 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2463,9 +2463,10 @@ pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, s #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { - // `name` should never be the empty symbol. If you are considering that, - // you are probably conflating "empty identifier with "no identifier" and - // you should use `Option` instead. + /// `name` should never be the empty symbol. If you are considering that, + /// you are probably conflating "empty identifier with "no identifier" and + /// you should use `Option` instead. + /// Trying to construct an `Ident` with an empty name will trigger debug assertions. pub name: Symbol, pub span: Span, } @@ -2508,6 +2509,8 @@ impl Ident { Ident::new(self.name, span.with_ctxt(self.span.ctxt())) } + /// Creates a new ident with the same span and name with leading quote removed, if any. + /// If called on an empty ident, or with name just a single quote, returns an empty ident which is invalid. pub fn without_first_quote(self) -> Ident { Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) } @@ -3095,10 +3098,15 @@ impl Ident { } pub fn is_raw_lifetime_guess(self) -> bool { - let name_without_apostrophe = self.without_first_quote(); - name_without_apostrophe.name != self.name - && name_without_apostrophe.name.can_be_raw() - && name_without_apostrophe.is_reserved_lifetime() + // Check that the name isn't just a single quote. + // `self.without_first_quote()` would return empty ident, which triggers debug assert. + if self.name.as_str() == "'" { + return false; + } + let ident_without_apostrophe = self.without_first_quote(); + ident_without_apostrophe.name != self.name + && ident_without_apostrophe.name.can_be_raw() + && ident_without_apostrophe.is_reserved_lifetime() } pub fn guess_print_mode(self) -> IdentPrintMode { From 87e8ec8114532e252fb93df5a115f7fb91c6d668 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 5 Oct 2025 14:12:49 +0200 Subject: [PATCH 1283/1900] power align: format test file --- tests/ui/layout/reprc-power-alignment.rs | 25 +++++++++---------- tests/ui/layout/reprc-power-alignment.stderr | 26 ++++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index f144094d43fbc..3456c4ef80297 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -5,12 +5,12 @@ #![feature(no_core)] #![no_core] #![no_std] +#![crate_type = "lib"] extern crate minicore; use minicore::*; #[warn(uses_power_alignment)] - #[repr(C)] pub struct Floats { a: f64, @@ -96,32 +96,32 @@ pub struct FloatAgg7 { #[repr(C)] pub struct A { - d: f64, + d: f64, } #[repr(C)] pub struct B { - a: A, - f: f32, - d: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + a: A, + f: f32, + d: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct C { - c: u8, - b: B, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + c: u8, + b: B, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct D { - x: f64, + x: f64, } #[repr(C)] pub struct E { - x: i32, - d: D, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + x: i32, + d: D, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct F { - a: u8, - b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + a: u8, + b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct G { @@ -173,4 +173,3 @@ pub struct M { b: K, c: L, } -fn main() { } diff --git a/tests/ui/layout/reprc-power-alignment.stderr b/tests/ui/layout/reprc-power-alignment.stderr index 18664e4d655d3..2f4612f3ff8f1 100644 --- a/tests/ui/layout/reprc-power-alignment.stderr +++ b/tests/ui/layout/reprc-power-alignment.stderr @@ -5,7 +5,7 @@ LL | c: f64, | ^^^^^^ | note: the lint level is defined here - --> $DIR/reprc-power-alignment.rs:12:8 + --> $DIR/reprc-power-alignment.rs:13:8 | LL | #[warn(uses_power_alignment)] | ^^^^^^^^^^^^^^^^^^^^ @@ -73,28 +73,28 @@ LL | y: Floats, | ^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:105:3 + --> $DIR/reprc-power-alignment.rs:105:5 | -LL | d: f64, - | ^^^^^^ +LL | d: f64, + | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:110:3 + --> $DIR/reprc-power-alignment.rs:110:5 | -LL | b: B, - | ^^^^ +LL | b: B, + | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:119:3 + --> $DIR/reprc-power-alignment.rs:119:5 | -LL | d: D, - | ^^^^ +LL | d: D, + | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:124:3 + --> $DIR/reprc-power-alignment.rs:124:5 | -LL | b: f64, - | ^^^^^^ +LL | b: f64, + | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type --> $DIR/reprc-power-alignment.rs:130:5 From 5a0ea3bfba56e4d1c768e25fa30088964b38277f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 5 Oct 2025 14:18:17 +0200 Subject: [PATCH 1284/1900] power align: ignore repr(C) unions and enums --- .../rustc_lint/src/types/improper_ctypes.rs | 20 +++++++++---------- tests/ui/layout/reprc-power-alignment.rs | 16 +++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 7ca57b0094ee3..2c88397086699 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -220,21 +220,21 @@ fn check_struct_for_power_alignment<'tcx>( adt_def: AdtDef<'tcx>, ) { let tcx = cx.tcx; - // repr(C) structs also with packed or aligned representation - // should be ignored. - if adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - && tcx.sess.target.os == "aix" - && !adt_def.all_fields().next().is_none() - { + + // Only consider structs (not enums or unions) on AIX. + if tcx.sess.target.os != "aix" || !adt_def.is_struct() { + return; + } + + // The struct must be repr(C), but ignore it if it explicitly specifies its alignment with + // either `align(N)` or `packed(N)`. + if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() { let struct_variant_data = item.expect_struct().2; for field_def in struct_variant_data.fields().iter().skip(1) { // Struct fields (after the first field) are checked for the // power alignment rule, as fields after the first are likely // to be the fields that are misaligned. - let def_id = field_def.def_id; - let ty = tcx.type_of(def_id).instantiate_identity(); + let ty = tcx.type_of(field_def.def_id).instantiate_identity(); if check_arg_for_power_alignment(cx, ty) { cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); } diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index 3456c4ef80297..ffe311a9a2a8f 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -173,3 +173,19 @@ pub struct M { b: K, c: L, } + +// The lint ignores unions +#[repr(C)] +pub union Union { + a: f64, + b: u8, + c: f64, + d: f32, +} + +// The lint ignores enums +#[repr(C)] +pub enum Enum { + A { a: f64, b: u8, c: f64, d: f32 }, + B, +} From 7ed476f51fe6d3240abb4d54435dc12c9da0b91d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 5 Oct 2025 19:58:56 +0800 Subject: [PATCH 1285/1900] bootstrap: don't build redirect pages during dry-run/test --- src/bootstrap/src/core/build_steps/doc.rs | 4 ++++ src/bootstrap/src/core/builder/tests.rs | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 37462c63f1b09..0a0f3b95b2cf8 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -274,6 +274,10 @@ impl Step for TheBook { // build the redirect pages let _guard = builder.msg(Kind::Doc, "book redirect pages", None, build_compiler, target); + if builder.config.dry_run() { + return; + } + for file in t!(fs::read_dir(redirect_path)) { let file = t!(file); let path = file.path(); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 3306435758b45..d0429387f824e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1270,12 +1270,12 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 [doc] book (book) [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] @@ -1409,12 +1409,12 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 [doc] book (book) [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] @@ -1493,8 +1493,8 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [doc] nomicon (book) @@ -1537,8 +1537,8 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] llvm From c13b87752a332d8a0d39b0ab63c9621a194fd87c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 15:19:52 +0300 Subject: [PATCH 1286/1900] Deprecate `preserve` import granularity option It didn't do anything (behaved like `item`), as with `enforceGranularity = false` (which is the default), the style of the current file is always preferred, regardless of the setting. We could make it fail when the setting is `preserve` and the current file's style could not be detected, but that makes little sense. It is a bit weird that the default is `crate` but `preserve` falls back to `item`, however that was the previous behavior. --- .../crates/ide-db/src/imports/insert_use.rs | 5 +-- .../crates/ide-diagnostics/src/lib.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 29 ++++++++++----- .../rust-analyzer/editors/code/package.json | 37 ++++++++++++------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index b174adfd7e448..caba1cdd70614 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -27,9 +27,6 @@ pub use hir::PrefixKind; /// How imports should be grouped into use statements. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ImportGranularity { - /// Do not change the granularity of any imports and preserve the original structure written - /// by the developer. - Preserve, /// Merge imports from the same crate into a single use statement. Crate, /// Merge imports from the same module into a single use statement. @@ -174,7 +171,7 @@ fn insert_use_with_alias_option( ImportGranularity::Crate => Some(MergeBehavior::Crate), ImportGranularity::Module => Some(MergeBehavior::Module), ImportGranularity::One => Some(MergeBehavior::One), - ImportGranularity::Item | ImportGranularity::Preserve => None, + ImportGranularity::Item => None, }; if !cfg.enforce_granularity { let file_granularity = guess_granularity_from_scope(scope); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index a1db92641f5ee..1530e64652464 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -253,7 +253,7 @@ impl DiagnosticsConfig { style_lints: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { - granularity: ImportGranularity::Preserve, + granularity: ImportGranularity::Item, enforce_granularity: false, prefix_kind: PrefixKind::Plain, group: false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index a88d228fcb60c..d4805dc1bdaf0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1944,8 +1944,9 @@ impl Config { fn insert_use_config(&self, source_root: Option) -> InsertUseConfig { InsertUseConfig { granularity: match self.imports_granularity_group(source_root) { - ImportGranularityDef::Preserve => ImportGranularity::Preserve, - ImportGranularityDef::Item => ImportGranularity::Item, + ImportGranularityDef::Item | ImportGranularityDef::Preserve => { + ImportGranularity::Item + } ImportGranularityDef::Crate => ImportGranularity::Crate, ImportGranularityDef::Module => ImportGranularity::Module, ImportGranularityDef::One => ImportGranularity::One, @@ -3504,13 +3505,23 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "ImportGranularityDef" => set! { "type": "string", - "enum": ["preserve", "crate", "module", "item", "one"], - "enumDescriptions": [ - "Do not change the granularity of any imports and preserve the original structure written by the developer.", - "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", - "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", - "Flatten imports so that each has its own use statement.", - "Merge all imports into a single use statement as long as they have the same visibility and attributes." + "anyOf": [ + { + "enum": ["crate", "module", "item", "one"], + "enumDescriptions": [ + "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", + "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", + "Flatten imports so that each has its own use statement.", + "Merge all imports into a single use statement as long as they have the same visibility and attributes." + ], + }, + { + "enum": ["preserve"], + "enumDescriptions": [ + "Deprecated - unless `enforceGranularity` is `true`, the style of the current file is preferred over this setting. Behaves like `item`.", + ], + "deprecated": true, + } ], }, "ImportPrefixDef" => set! { diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 745e0da4efef0..5c75a45ee9252 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2009,19 +2009,30 @@ "markdownDescription": "How imports should be grouped into use statements.", "default": "crate", "type": "string", - "enum": [ - "preserve", - "crate", - "module", - "item", - "one" - ], - "enumDescriptions": [ - "Do not change the granularity of any imports and preserve the original structure written by the developer.", - "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", - "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", - "Flatten imports so that each has its own use statement.", - "Merge all imports into a single use statement as long as they have the same visibility and attributes." + "anyOf": [ + { + "enum": [ + "crate", + "module", + "item", + "one" + ], + "enumDescriptions": [ + "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", + "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", + "Flatten imports so that each has its own use statement.", + "Merge all imports into a single use statement as long as they have the same visibility and attributes." + ] + }, + { + "enum": [ + "preserve" + ], + "enumDescriptions": [ + "Deprecated - unless `enforceGranularity` is `true`, the style of the current file is preferred over this setting. Behaves like `item`." + ], + "deprecated": true + } ] } } From 5234d36597b8f22aba25eef67d892315a439e927 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sun, 5 Oct 2025 13:18:02 +0200 Subject: [PATCH 1287/1900] add test --- .../extern/extern-single-quote-issue-147365.rs | 9 +++++++++ .../extern-single-quote-issue-147365.stderr | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ui/extern/extern-single-quote-issue-147365.rs create mode 100644 tests/ui/extern/extern-single-quote-issue-147365.stderr diff --git a/tests/ui/extern/extern-single-quote-issue-147365.rs b/tests/ui/extern/extern-single-quote-issue-147365.rs new file mode 100644 index 0000000000000..b50016bd65f31 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.rs @@ -0,0 +1,9 @@ +//@ needs-rustc-debug-assertions + +// https://github.com/rust-lang/rust/issues/147365 +// Ensures we don't trigger debug assert by creating an empty Ident when determining whether +// the single quote is a raw lifetime. + +extern "'" {} //~ ERROR invalid ABI: found `'` + +fn main() {} diff --git a/tests/ui/extern/extern-single-quote-issue-147365.stderr b/tests/ui/extern/extern-single-quote-issue-147365.stderr new file mode 100644 index 0000000000000..d761bc3ebf323 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.stderr @@ -0,0 +1,16 @@ +error[E0703]: invalid ABI: found `'` + --> $DIR/extern-single-quote-issue-147365.rs:7:8 + | +LL | extern "'" {} + | ^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL - extern "'" {} +LL + extern "C" {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. From a8b9a576745d4e6a13198952c7b4b4b8d825624d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 5 Oct 2025 20:30:24 +0800 Subject: [PATCH 1288/1900] bootstrap: relax `compiler-rt` root assertion Not needed during tests. --- src/bootstrap/src/core/build_steps/compile.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 96b4e15433f7e..d29d1041486bd 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -590,7 +590,12 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car ), ); let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); - assert!(compiler_builtins_root.exists()); + if !builder.config.dry_run() { + // This assertion would otherwise trigger during tests if `llvm-project` is not + // checked out. + assert!(compiler_builtins_root.exists()); + } + // The path to `compiler-rt` is also used by `profiler_builtins` (above), // so if you're changing something here please also change that as appropriate. cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); From f28b8d4f15f6e0f2f14c994911e2b2ec7850fd01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Oct 2025 15:37:04 +0200 Subject: [PATCH 1289/1900] deduplicate warnings globally --- src/tools/miri/src/alloc_addresses/mod.rs | 11 ++-- .../src/borrow_tracker/stacked_borrows/mod.rs | 4 +- .../miri/src/concurrency/genmc/helper.rs | 41 +------------ src/tools/miri/src/concurrency/genmc/mod.rs | 61 ++++++++----------- src/tools/miri/src/concurrency/genmc/run.rs | 10 +-- src/tools/miri/src/diagnostics.rs | 45 +++++++++++++- src/tools/miri/src/helpers.rs | 7 ++- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/machine.rs | 18 ------ src/tools/miri/src/shims/native_lib/mod.rs | 5 +- .../tests/genmc/pass/std/thread_locals.stderr | 34 ----------- 11 files changed, 88 insertions(+), 149 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index ecaa80ebb4da2..05d3444a4eb1a 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::TyCtxt; pub use self::address_generator::AddressGenerator; use self::reuse_pool::ReusePool; use crate::concurrency::VClock; +use crate::diagnostics::SpanDedupDiagnostic; use crate::*; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -341,12 +342,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match global_state.provenance_mode { ProvenanceMode::Default => { // The first time this happens at a particular location, print a warning. - let mut int2ptr_warned = this.machine.int2ptr_warned.borrow_mut(); - let first = int2ptr_warned.is_empty(); - if int2ptr_warned.insert(this.cur_span()) { - // Newly inserted, so first time we see this span. - this.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first }); - } + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + this.dedup_diagnostic(&DEDUP, |first| { + NonHaltingDiagnostic::Int2Ptr { details: first } + }); } ProvenanceMode::Strict => { throw_machine_stop!(TerminationInfo::Int2PtrWithStrictProvenance); diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 5fe00ab02c4b9..127d832f73e79 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -6,6 +6,7 @@ mod item; mod stack; use std::fmt::Write; +use std::sync::atomic::AtomicBool; use std::{cmp, mem}; use rustc_abi::{BackendRepr, Size}; @@ -822,7 +823,8 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { let size = match size { Some(size) => size, None => { - if !this.machine.sb_extern_type_warned.replace(true) { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); } return interp_ok(place.clone()); diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index b79fe0e94299a..b2e4b5aea5346 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,58 +1,19 @@ -use std::sync::RwLock; - use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; -use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; use rustc_middle::mir::interpret; use rustc_middle::ty::ScalarInt; -use rustc_span::Span; use tracing::debug; use super::GenmcScalar; use crate::alloc_addresses::EvalContextExt as _; -use crate::diagnostics::EvalContextExt as _; use crate::intrinsics::AtomicRmwOp; -use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BorTag, GenmcCtx, InterpCx, - MiriInterpCx, MiriMachine, NonHaltingDiagnostic, Scalar, machine, throw_unsup_format, -}; +use crate::*; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; -/// Type for storing spans for already emitted warnings. -pub(super) type WarningCache = RwLock>; - -#[derive(Default)] -pub(super) struct Warnings { - pub(super) compare_exchange_failure_ordering: WarningCache, - pub(super) compare_exchange_weak: WarningCache, -} - -/// Emit a warning if it hasn't already been reported for current span. -pub(super) fn emit_warning<'tcx>( - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - cache: &WarningCache, - diagnostic: impl FnOnce() -> NonHaltingDiagnostic, -) { - // FIXME: This is not the right span to use (it's always inside the local crates so if the same - // operation is invoked from multiple places it will warn multiple times). `cur_span` is not - // right either though (we should honor `#[track_caller]`). Ultimately what we want is "the - // primary span the warning would point at". - let span = ecx.machine.current_user_relevant_span(); - if cache.read().unwrap().contains(&span) { - return; - } - // This span has not yet been reported, so we insert it into the cache and report it. - let mut cache = cache.write().unwrap(); - if cache.insert(span) { - // Some other thread may have added this span while we didn't hold the lock, so we only emit it if the insertions succeeded. - ecx.emit_diagnostic(diagnostic()); - } -} - /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. pub fn split_access(address: Size, size: Size) -> impl Iterator { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index b2f8ab6f972eb..caec5e921cdfe 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -14,18 +14,15 @@ use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{ - MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, - maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, + MAX_ACCESS_SIZE, genmc_scalar_to_scalar, maybe_upgrade_compare_exchange_success_orderings, + scalar_to_genmc_scalar, to_genmc_rmw_op, }; use self::run::GenmcMode; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; +use crate::diagnostics::SpanDedupDiagnostic; use crate::intrinsics::AtomicRmwOp; -use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, MiriMemoryKind, NonHaltingDiagnostic, Scalar, TerminationInfo, ThreadId, - ThreadManager, VisitProvenance, VisitWith, -}; +use crate::*; mod config; mod global_allocations; @@ -104,18 +101,11 @@ struct GlobalState { /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: GlobalAllocationHandler, - - /// Cache for which warnings have already been shown to the user. - /// `None` if warnings are disabled. - warning_cache: Option, } impl GlobalState { - fn new(target_usize_max: u64, print_warnings: bool) -> Self { - Self { - global_allocations: GlobalAllocationHandler::new(target_usize_max), - warning_cache: print_warnings.then(Default::default), - } + fn new(target_usize_max: u64) -> Self { + Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } } } @@ -412,27 +402,24 @@ impl GenmcCtx { let upgraded_success_ordering = maybe_upgrade_compare_exchange_success_orderings(success, fail); - if let Some(warning_cache) = &self.global_state.warning_cache { - // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. - let (effective_failure_ordering, _) = - upgraded_success_ordering.split_memory_orderings(); - // Return a warning if the actual orderings don't match the upgraded ones. - if success != upgraded_success_ordering || effective_failure_ordering != fail { - emit_warning(ecx, &warning_cache.compare_exchange_failure_ordering, || { - NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { - success_ordering: success, - upgraded_success_ordering, - failure_ordering: fail, - effective_failure_ordering, - } - }); - } - // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. - if can_fail_spuriously { - emit_warning(ecx, &warning_cache.compare_exchange_weak, || { - NonHaltingDiagnostic::GenmcCompareExchangeWeak - }); - } + // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. + let (effective_failure_ordering, _) = upgraded_success_ordering.split_memory_orderings(); + // Return a warning if the actual orderings don't match the upgraded ones. + if success != upgraded_success_ordering || effective_failure_ordering != fail { + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + ecx.dedup_diagnostic(&DEDUP, |_first| { + NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { + success_ordering: success, + upgraded_success_ordering, + failure_ordering: fail, + effective_failure_ordering, + } + }); + } + // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. + if can_fail_spuriously { + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + ecx.dedup_diagnostic(&DEDUP, |_first| NonHaltingDiagnostic::GenmcCompareExchangeWeak); } debug!( diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 33c5b6b0a005f..6721a38c683fb 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -16,13 +16,6 @@ pub(super) enum GenmcMode { Verification, } -impl GenmcMode { - /// Return whether warnings on unsupported features should be printed in this mode. - fn print_unsupported_warnings(self) -> bool { - self == GenmcMode::Verification - } -} - /// Do a complete run of the program in GenMC mode. /// This will call `eval_entry` multiple times, until either: /// - An error is detected (indicated by a `None` return value) @@ -57,8 +50,7 @@ fn run_genmc_mode_impl<'tcx>( // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - let global_state = - Arc::new(GlobalState::new(tcx.target_usize_max(), mode.print_unsupported_warnings())); + let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state, mode)); // `rep` is used to report the progress, Miri will panic on wrap-around. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 15f7ccbabca6a..d3486dcbb19c9 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -1,9 +1,11 @@ use std::fmt::{self, Write}; use std::num::NonZero; +use std::sync::Mutex; use rustc_abi::{Align, Size}; use rustc_errors::{Diag, DiagMessage, Level}; -use rustc_span::{DUMMY_SP, SpanData, Symbol}; +use rustc_hash::FxHashSet; +use rustc_span::{DUMMY_SP, Span, SpanData, Symbol}; use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory; use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics; @@ -835,4 +837,45 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.machine, ); } + + /// Call `f` only if this is the first time we are seeing this span. + /// The `first` parameter indicates whether this is the first time *ever* that this diagnostic + /// is emitted. + fn dedup_diagnostic( + &self, + dedup: &SpanDedupDiagnostic, + f: impl FnOnce(/*first*/ bool) -> NonHaltingDiagnostic, + ) { + let this = self.eval_context_ref(); + // We want to deduplicate both based on where the error seems to be located "from the user + // perspective", and the location of the actual operation (to avoid warning about the same + // operation called from different places in the local code). + let span1 = this.machine.current_user_relevant_span(); + // For the "location of the operation", we still skip `track_caller` frames, to match the + // span that the diagnostic will point at. + let span2 = this + .active_thread_stack() + .iter() + .rev() + .find(|frame| !frame.instance().def.requires_caller_location(*this.tcx)) + .map(|frame| frame.current_span()) + .unwrap_or(span1); + + let mut lock = dedup.0.lock().unwrap(); + let first = lock.is_empty(); + // Avoid mutating the hashset unless both spans are new. + if !lock.contains(&span2) && lock.insert(span1) && (span1 == span2 || lock.insert(span2)) { + // Both of the two spans were newly inserted. + this.emit_diagnostic(f(first)); + } + } +} + +/// Helps deduplicate a diagnostic to ensure it is only shown once per span. +pub struct SpanDedupDiagnostic(Mutex>); + +impl SpanDedupDiagnostic { + pub const fn new() -> Self { + Self(Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher))) + } } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 3f736435bbc49..53f235a077f8f 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1,4 +1,5 @@ use std::num::NonZero; +use std::sync::Mutex; use std::time::Duration; use std::{cmp, iter}; @@ -6,6 +7,7 @@ use rand::RngCore; use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_hash::FxHashSet; use rustc_hir::Safety; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}; @@ -650,7 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match reject_with { RejectOpWith::Abort => isolation_abort_error(op_name), RejectOpWith::WarningWithoutBacktrace => { - let mut emitted_warnings = this.machine.reject_in_isolation_warned.borrow_mut(); + // Deduplicate these warnings *by shim* (not by span) + static DEDUP: Mutex> = + Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher)); + let mut emitted_warnings = DEDUP.lock().unwrap(); if !emitted_warnings.contains(op_name) { // First time we are seeing this. emitted_warnings.insert(op_name.to_owned()); diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 1f82f154b0b3f..ce72fc6e297dc 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -60,6 +60,7 @@ extern crate rustc_ast; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; +extern crate rustc_hash; extern crate rustc_hir; extern crate rustc_index; extern crate rustc_middle; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index a1e63f6ae4ece..7b93d0194119a 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -649,16 +649,6 @@ pub struct MiriMachine<'tcx> { pub(crate) pthread_rwlock_sanity: Cell, pub(crate) pthread_condvar_sanity: Cell, - /// Remembers whether we already warned about an extern type with Stacked Borrows. - pub(crate) sb_extern_type_warned: Cell, - /// Remember whether we already warned about sharing memory with a native call. - #[allow(unused)] - pub(crate) native_call_mem_warned: Cell, - /// Remembers which shims have already shown the warning about erroring in isolation. - pub(crate) reject_in_isolation_warned: RefCell>, - /// Remembers which int2ptr casts we have already warned about. - pub(crate) int2ptr_warned: RefCell>, - /// Cache for `mangle_internal_symbol`. pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>, @@ -826,10 +816,6 @@ impl<'tcx> MiriMachine<'tcx> { pthread_mutex_sanity: Cell::new(false), pthread_rwlock_sanity: Cell::new(false), pthread_condvar_sanity: Cell::new(false), - sb_extern_type_warned: Cell::new(false), - native_call_mem_warned: Cell::new(false), - reject_in_isolation_warned: Default::default(), - int2ptr_warned: Default::default(), mangle_internal_symbol_cache: Default::default(), force_intrinsic_fallback: config.force_intrinsic_fallback, float_nondet: config.float_nondet, @@ -1003,10 +989,6 @@ impl VisitProvenance for MiriMachine<'_> { pthread_mutex_sanity: _, pthread_rwlock_sanity: _, pthread_condvar_sanity: _, - sb_extern_type_warned: _, - native_call_mem_warned: _, - reject_in_isolation_warned: _, - int2ptr_warned: _, mangle_internal_symbol_cache: _, force_intrinsic_fallback: _, float_nondet: _, diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index a0e871d87d3a0..914c666adb302 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -1,6 +1,7 @@ //! Implements calling functions from a native library. use std::ops::Deref; +use std::sync::atomic::AtomicBool; use libffi::low::CodePtr; use libffi::middle::Type as FfiType; @@ -279,8 +280,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Helper to print a warning when a pointer is shared with the native code. let expose = |prov: Provenance| -> InterpResult<'tcx> { - // The first time this happens, print a warning. - if !this.machine.native_call_mem_warned.replace(true) { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { // Newly set, so first time we get here. this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing }); } diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index e9b780fc007f5..40faedf49c6e1 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -81,40 +81,6 @@ LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCs LL | | }), | |__________^ -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/mod.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | }), - | |__________^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/mod.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. --> RUSTLIB/alloc/src/sync.rs:LL:CC | From a958b84ea4df97608fcaf5d35ce8de91b1961109 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 15:48:03 +0300 Subject: [PATCH 1290/1900] Fix merging of import granularity setting with the granularity we infer Previously it was wrong for some combinations. --- .../crates/ide-db/src/imports/insert_use.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index caba1cdd70614..4444ef5d81d54 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -179,9 +179,18 @@ fn insert_use_with_alias_option( ImportGranularityGuess::Unknown => mb, ImportGranularityGuess::Item => None, ImportGranularityGuess::Module => Some(MergeBehavior::Module), - ImportGranularityGuess::ModuleOrItem => mb.and(Some(MergeBehavior::Module)), + // We use the user's setting to infer if this is module or item. + ImportGranularityGuess::ModuleOrItem => match mb { + Some(MergeBehavior::Module) | None => mb, + // There isn't really a way to decide between module or item here, so we just pick one. + // FIXME: Maybe it is possible to infer based on semantic analysis? + Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), + }, ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), - ImportGranularityGuess::CrateOrModule => mb.or(Some(MergeBehavior::Crate)), + ImportGranularityGuess::CrateOrModule => match mb { + Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, + Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), + }, ImportGranularityGuess::One => Some(MergeBehavior::One), }; } From 8a31837054e95417b7356848225583e33ec3c439 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Oct 2025 11:29:07 +0100 Subject: [PATCH 1291/1900] Set opt-level for installing tool only on CI ensure_version_or_cargo_install uses -Copt-level=0 for quicker installation. However, the flag affects the tool's performance. For example, typos-cli with opt-level=0 takes 15 seconds for checking ./compiler, but the tool with default opt-level only takes less than 1 sec. This commit enables the option only when the test tidy is run on CI. --- src/tools/tidy/src/lib.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 874a758bd9b38..0acbcd64f067c 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -194,8 +194,8 @@ pub fn ensure_version_or_cargo_install( // use --force to ensure that if the required version is bumped, we update it. // use --target-dir to ensure we have a build cache so repeated invocations aren't slow. // modify PATH so that cargo doesn't print a warning telling the user to modify the path. - let cargo_exit_code = Command::new(cargo) - .args(["install", "--locked", "--force", "--quiet"]) + let mut cmd = Command::new(cargo); + cmd.args(["install", "--locked", "--force", "--quiet"]) .arg("--root") .arg(&tool_root_dir) .arg("--target-dir") @@ -208,10 +208,16 @@ pub fn ensure_version_or_cargo_install( .chain(std::iter::once(tool_bin_dir.clone())), ) .expect("build dir contains invalid char"), - ) - .env("RUSTFLAGS", "-Copt-level=0") - .spawn()? - .wait()?; + ); + + // On CI, we set opt-level flag for quicker installation. + // Since lower opt-level decreases the tool's performance, + // we don't set this option on local. + if CiEnv::is_ci() { + cmd.env("RUSTFLAGS", "-Copt-level=0"); + } + + let cargo_exit_code = cmd.spawn()?.wait()?; if !cargo_exit_code.success() { return Err(io::Error::other("cargo install failed")); } From 2997070be183a613457bcba5a38f82a25030a19c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 5 Oct 2025 10:23:44 -0700 Subject: [PATCH 1292/1900] unused_must_use: Factor out a variable for the parent module DefId This simplifies the initial conditional, and will allow reusing the variable in subsequent checks. --- compiler/rustc_lint/src/unused.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 874c435402919..e70ec18340add 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -273,13 +273,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { expr: &hir::Expr<'_>, span: Span, ) -> Option { - if ty.is_unit() - || !ty.is_inhabited_from( - cx.tcx, - cx.tcx.parent_module(expr.hir_id).to_def_id(), - cx.typing_env(), - ) - { + if ty.is_unit() { + return Some(MustUsePath::Suppressed); + } + let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); + if !ty.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()) { return Some(MustUsePath::Suppressed); } From 7e1c00dbce512b260c396b4425527b00891168a7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 17 Sep 2025 17:06:14 +0000 Subject: [PATCH 1293/1900] Prevent downstream impl DerefMut for Pin --- library/core/src/pin.rs | 59 ++++++++++++++++++- ...y.run2-{closure#0}.Inline.panic-abort.diff | 56 ++++++++++-------- ....run2-{closure#0}.Inline.panic-unwind.diff | 56 ++++++++++-------- tests/ui/deref/pin-impl-deref.rs | 4 +- tests/ui/deref/pin-impl-deref.stderr | 22 ++++--- .../pin-unsound-issue-85099-derefmut.rs | 4 +- .../pin-unsound-issue-85099-derefmut.stderr | 14 +++++ 7 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 535830f2e749f..d7e348b71e1d0 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1689,9 +1689,66 @@ impl const Deref for Pin { } } +mod helper { + /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. + /// + /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in + /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + #[repr(transparent)] + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[allow(missing_debug_implementations)] + pub struct Pin { + pointer: Ptr, + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const trait DerefMut { + type Target: ?Sized; + fn deref_mut(&mut self) -> &mut Self::Target; + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const DerefMut for Pin + where + Ptr::Target: crate::marker::Unpin, + { + type Target = Ptr::Target; + + #[inline(always)] + fn deref_mut(&mut self) -> &mut Ptr::Target { + &mut self.pointer + } + } +} + #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -impl> const DerefMut for Pin { +#[cfg(not(doc))] +impl const DerefMut for Pin +where + Ptr: [const] Deref, + helper::Pin: [const] helper::DerefMut, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Ptr::Target { + // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // `&mut self.pointer` which is safe because `Target: Unpin`. + helper::DerefMut::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::Pin) + }) + } +} + +#[stable(feature = "pin", since = "1.33.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +#[cfg(doc)] +impl const DerefMut for Pin +where + Ptr: [const] DerefMut, + Ptr::Target: Unpin, +{ fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 0acb33febe52b..226a16d6eec39 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,27 +63,25 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _46: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _48: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { + } + } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _47: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _49: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _48: isize; -+ let mut _49: !; -+ scope 23 { ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _50: isize; ++ let mut _51: !; ++ scope 21 { + } + } + } @@ -217,18 +215,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_44); -+ StorageLive(_49); ++ StorageLive(_46); ++ StorageLive(_51); + StorageLive(_41); + StorageLive(_42); -+ _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_47); -+ _47 = Option::<()>::None; -+ _42 = copy ((*_44).0: std::option::Option<()>); -+ ((*_44).0: std::option::Option<()>) = copy _47; ++ _47 = &raw mut _19; ++ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); -+ StorageLive(_48); -+ _48 = discriminant(_42); -+ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _44 = copy ((*_46).0: &mut std::future::Ready<()>); ++ StorageLive(_49); ++ _49 = Option::<()>::None; ++ _42 = copy ((*_44).0: std::option::Option<()>); ++ ((*_44).0: std::option::Option<()>) = copy _49; ++ StorageDead(_49); ++ StorageLive(_50); ++ _50 = discriminant(_42); ++ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -291,16 +294,17 @@ + } + + bb11: { -+ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { + _41 = move ((_42 as Some).0: ()); -+ StorageDead(_48); ++ StorageDead(_50); + StorageDead(_42); + _18 = Poll::<()>::Ready(move _41); + StorageDead(_41); -+ StorageDead(_49); ++ StorageDead(_51); ++ StorageDead(_46); + StorageDead(_44); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 98ee46c29b1be..770b6e471ff8b 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,27 +65,25 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _48: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _50: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { + } + } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _49: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _51: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _50: isize; -+ let mut _51: !; -+ scope 23 { ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _52: isize; ++ let mut _53: !; ++ scope 21 { + } + } + } @@ -234,18 +232,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_46); -+ StorageLive(_51); ++ StorageLive(_48); ++ StorageLive(_53); + StorageLive(_43); + StorageLive(_44); -+ _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_49); -+ _49 = Option::<()>::None; -+ _44 = copy ((*_46).0: std::option::Option<()>); -+ ((*_46).0: std::option::Option<()>) = copy _49; ++ _49 = &raw mut _19; ++ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); -+ StorageLive(_50); -+ _50 = discriminant(_44); -+ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _46 = copy ((*_48).0: &mut std::future::Ready<()>); ++ StorageLive(_51); ++ _51 = Option::<()>::None; ++ _44 = copy ((*_46).0: std::option::Option<()>); ++ ((*_46).0: std::option::Option<()>) = copy _51; ++ StorageDead(_51); ++ StorageLive(_52); ++ _52 = discriminant(_44); ++ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -332,16 +335,17 @@ + } + + bb16: { -+ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { + _43 = move ((_44 as Some).0: ()); -+ StorageDead(_50); ++ StorageDead(_52); + StorageDead(_44); + _18 = Poll::<()>::Ready(move _43); + StorageDead(_43); -+ StorageDead(_51); ++ StorageDead(_53); ++ StorageDead(_48); + StorageDead(_46); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/ui/deref/pin-impl-deref.rs b/tests/ui/deref/pin-impl-deref.rs index b1dc8dea3f248..ccd8d0dfc72ae 100644 --- a/tests/ui/deref/pin-impl-deref.rs +++ b/tests/ui/deref/pin-impl-deref.rs @@ -22,7 +22,7 @@ impl MyPinType { fn impl_deref_mut(_: impl DerefMut) {} fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) { impl_deref_mut(r_unpin) - //~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied + //~^ ERROR: the trait bound `&MyUnpinType: DerefMut` is not satisfied } fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { impl_deref_mut(r_unpin) @@ -30,7 +30,7 @@ fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { fn pin_impl_ref(r_pin: Pin<&MyPinType>) { impl_deref_mut(r_pin) //~^ ERROR: `PhantomPinned` cannot be unpinned - //~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied + //~| ERROR: the trait bound `&MyPinType: DerefMut` is not satisfied } fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) { impl_deref_mut(r_pin) diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 106654641a117..918ff7ca91218 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -1,40 +1,36 @@ -error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyUnpinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:24:20 | LL | impl_deref_mut(r_unpin) - | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>` + | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `&MyUnpinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` + = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_unpin) - | ++++ -error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyPinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:31:20 | LL | impl_deref_mut(r_pin) - | -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>` + | -------------- ^^^^^ the trait `DerefMut` is not implemented for `&MyPinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_pin) - | ++++ error[E0277]: `PhantomPinned` cannot be unpinned --> $DIR/pin-impl-deref.rs:31:20 @@ -51,6 +47,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -73,6 +70,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs index f3ece563f5403..e8c3bbba1e458 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs @@ -1,5 +1,4 @@ -//@ check-pass -//@ known-bug: #85099 +//@ check-fail // Should fail. Can coerce `Pin` into `Pin` where // `T: Deref` and `U: Deref`, using the @@ -43,6 +42,7 @@ impl<'a, Fut: Future> SomeTrait<'a, Fut> for Fut { } impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { +//~^ ERROR: conflicting implementations of trait `DerefMut` fn deref_mut<'c>( self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>, ) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) { diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr new file mode 100644 index 0000000000000..cc56c77809eaa --- /dev/null +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `DerefMut` for type `Pin<&dyn SomeTrait<'_, _>>` + --> $DIR/pin-unsound-issue-85099-derefmut.rs:44:1 + | +LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl DerefMut for Pin + where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. From cd44cd0f63a7e67dcfc3618aa7bf6e2e70643561 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 25 Sep 2025 08:39:35 +0000 Subject: [PATCH 1294/1900] Improve docs for module --- library/core/src/pin.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d7e348b71e1d0..fdbeaea464194 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1692,8 +1692,22 @@ impl const Deref for Pin { mod helper { /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. /// - /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in - /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + /// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires + /// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this + /// helper module, downstream crates are able to write `impl DerefMut for Pin` as + /// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is + /// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not + /// implement today. + /// + /// However, this is problematic. Downstream crates could implement `DerefMut` for + /// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for + /// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the + /// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in + /// the future. Because of this, downstream crates can no longer provide an implementation of + /// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the + /// orphan rules, the stdlib could introduce without a breaking change in a future release. + /// + /// See for the issue this fixes. #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] From f65250b3dd0eada1ab25ad4f766048f2ebe24623 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 29 Sep 2025 09:34:18 +0000 Subject: [PATCH 1295/1900] Document workaround in docs --- library/core/src/pin.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index fdbeaea464194..23950acd97610 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1755,13 +1755,21 @@ where } } +/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference +/// to a pinned value. +/// +/// For soundness reasons, implementations of `DerefMut` for `Pin` are rejected even when `T` is +/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations +/// would normally be possible.) +/// +/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(doc)] impl const DerefMut for Pin where Ptr: [const] DerefMut, - Ptr::Target: Unpin, + ::Target: Unpin, { fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) From 76dcb39c240382a7d62cc8ee475e0c3866ad8025 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 1 Oct 2025 11:14:08 +0000 Subject: [PATCH 1296/1900] Adjust error messages --- compiler/rustc_span/src/symbol.rs | 1 + .../src/error_reporting/traits/suggestions.rs | 18 ++++++++++++++++++ library/core/src/pin.rs | 15 ++++++++------- ...dy.run2-{closure#0}.Inline.panic-abort.diff | 6 +++--- ...y.run2-{closure#0}.Inline.panic-unwind.diff | 6 +++--- tests/ui/deref/pin-impl-deref.stderr | 4 ---- .../pin-unsound-issue-85099-derefmut.stderr | 4 ++-- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc37..7f1ca96b2a75b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -309,6 +309,7 @@ symbols! { PathBuf, Pending, PinCoerceUnsized, + PinDerefMutHelper, Pointer, Poll, ProcMacro, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 37e622102e70f..5665229a4cbe6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3476,6 +3476,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // can do about it. As far as they are concerned, `?` is compiler magic. return; } + if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) { + let parent_predicate = + self.resolve_vars_if_possible(data.derived.parent_trait_pred); + + // Skip PinDerefMutHelper in suggestions, but still show downstream suggestions. + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.derived.parent_code, + obligated_types, + seen_requirements, + ) + }); + return; + } let self_ty_str = tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path()); let trait_name = tcx.short_string( diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 23950acd97610..81c2dabf0d1d8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1711,20 +1711,21 @@ mod helper { #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] - pub struct Pin { + pub struct PinHelper { pointer: Ptr, } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - pub const trait DerefMut { + #[rustc_diagnostic_item = "PinDerefMutHelper"] + pub const trait PinDerefMutHelper { type Target: ?Sized; fn deref_mut(&mut self) -> &mut Self::Target; } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - impl const DerefMut for Pin + impl const PinDerefMutHelper for PinHelper where Ptr::Target: crate::marker::Unpin, { @@ -1743,14 +1744,14 @@ mod helper { impl const DerefMut for Pin where Ptr: [const] Deref, - helper::Pin: [const] helper::DerefMut, + helper::PinHelper: [const] helper::PinDerefMutHelper, { #[inline] fn deref_mut(&mut self) -> &mut Ptr::Target { - // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // SAFETY: Pin and PinHelper have the same layout, so this is equivalent to // `&mut self.pointer` which is safe because `Target: Unpin`. - helper::DerefMut::deref_mut(unsafe { - &mut *(self as *mut Pin as *mut helper::Pin) + helper::PinDerefMutHelper::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::PinHelper) }) } } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 226a16d6eec39..0203ff52d63f8 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,9 +63,9 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _46: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _48: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -221,7 +221,7 @@ + StorageLive(_42); + StorageLive(_47); + _47 = &raw mut _19; -+ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _46 = copy _47 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); + _44 = copy ((*_46).0: &mut std::future::Ready<()>); + StorageLive(_49); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 770b6e471ff8b..6c8cad3e992e7 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,9 +65,9 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _48: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _50: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -238,7 +238,7 @@ + StorageLive(_44); + StorageLive(_49); + _49 = &raw mut _19; -+ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _48 = copy _49 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); + _46 = copy ((*_48).0: &mut std::future::Ready<()>); + StorageLive(_51); diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 918ff7ca91218..4143d66f42723 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -7,7 +7,6 @@ LL | impl_deref_mut(r_unpin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` - = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -24,7 +23,6 @@ LL | impl_deref_mut(r_pin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -47,7 +45,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -70,7 +67,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr index cc56c77809eaa..2bcd92b76a09d 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -6,8 +6,8 @@ LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { | = note: conflicting implementation in crate `core`: - impl DerefMut for Pin - where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; - = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + where as pin::helper::PinDerefMutHelper>::Target == as Deref>::Target, Ptr: Deref, pin::helper::PinHelper: pin::helper::PinDerefMutHelper, pin::helper::PinHelper: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::PinDerefMutHelper` for type `std::pin::helper::PinHelper<&dyn SomeTrait<'_, _>>` in future versions error: aborting due to 1 previous error From c7a6a799f026ea423ff08281166aec3256a77456 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 21:56:08 +0300 Subject: [PATCH 1297/1900] Add regression test for need-mut diagnostic --- .../src/handlers/mutability_errors.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 6e30bf92dbaa1..eefa1ac24add1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1302,6 +1302,22 @@ fn main() { let mut var = 1; let mut func = || (var,) = (2,); func(); +} + "#, + ); + } + + #[test] + fn regression_20662() { + check_diagnostics( + r#" +//- minicore: index +pub trait A: core::ops::IndexMut { + type T: A; +} + +fn func(a: &mut impl A, b: &mut [i32]) { + b[0] += 1; } "#, ); From ff91dbd1fd1897178314821d5928309ed989bf16 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sun, 5 Oct 2025 00:28:23 +0200 Subject: [PATCH 1298/1900] only call polymorphic array iter drop machinery when the type requires it --- library/core/src/array/iter.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index fdae5c08f1e8e..1c1f4d78c03fd 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -2,9 +2,9 @@ use crate::intrinsics::transmute_unchecked; use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}; -use crate::mem::MaybeUninit; +use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::num::NonZero; -use crate::ops::{IndexRange, Range, Try}; +use crate::ops::{Deref as _, DerefMut as _, IndexRange, Range, Try}; use crate::{fmt, ptr}; mod iter_inner; @@ -18,17 +18,17 @@ type InnerUnsized = iter_inner::PolymorphicIter<[MaybeUninit]>; #[rustc_diagnostic_item = "ArrayIntoIter"] #[derive(Clone)] pub struct IntoIter { - inner: InnerSized, + inner: ManuallyDrop>, } impl IntoIter { #[inline] fn unsize(&self) -> &InnerUnsized { - &self.inner + self.inner.deref() } #[inline] fn unsize_mut(&mut self) -> &mut InnerUnsized { - &mut self.inner + self.inner.deref_mut() } } @@ -69,7 +69,7 @@ impl IntoIterator for [T; N] { // SAFETY: The original array was entirely initialized and the the alive // range we're passing here represents that fact. let inner = unsafe { InnerSized::new_unchecked(IndexRange::zero_to(N), data) }; - IntoIter { inner } + IntoIter { inner: ManuallyDrop::new(inner) } } } @@ -146,7 +146,7 @@ impl IntoIter { let alive = unsafe { IndexRange::new_unchecked(initialized.start, initialized.end) }; // SAFETY: one of our safety condition is that these items are initialized. let inner = unsafe { InnerSized::new_unchecked(alive, buffer) }; - IntoIter { inner } + IntoIter { inner: ManuallyDrop::new(inner) } } /// Creates an iterator over `T` which returns no elements. @@ -205,7 +205,7 @@ impl IntoIter { #[inline] pub const fn empty() -> Self { let inner = InnerSized::empty(); - IntoIter { inner } + IntoIter { inner: ManuallyDrop::new(inner) } } /// Returns an immutable slice of all elements that have not been yielded @@ -320,11 +320,20 @@ impl DoubleEndedIterator for IntoIter { } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] +// Even though all the Drop logic could be completely handled by +// PolymorphicIter, this impl still serves two purposes: +// - Drop has been part of the public API, so we can't remove it +// - the partial_drop function doesn't always get fully optimized away +// for !Drop types and ends up as dead code in the final binary. +// Branching on needs_drop higher in the call-tree allows it to be +// removed by earlier optimization passes. impl Drop for IntoIter { #[inline] fn drop(&mut self) { - // `inner` now handles this, but it'd technically be a breaking change - // to remove this `impl`, even though it's useless. + if crate::mem::needs_drop::() { + // SAFETY: This is the only place where we drop this field. + unsafe { ManuallyDrop::drop(&mut self.inner) } + } } } From c4073c5ae3ffd3f673932f86c43f829eef7656f3 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Oct 2025 22:07:58 +0300 Subject: [PATCH 1299/1900] Replace `--show-output` with `--nocapture` The former does not show any output before the test is finished, which prevents long-running/stuck tests from showing any useful information. --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 +- .../crates/rust-analyzer/tests/slow-tests/main.rs | 4 ++-- .../rust-analyzer/docs/book/src/configuration_generated.md | 2 +- src/tools/rust-analyzer/editors/code/package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index a88d228fcb60c..8272ba7f6bf0b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -895,7 +895,7 @@ config_data! { /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), /// they will end up being interpreted as options to /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + runnables_extraTestBinaryArgs: Vec = vec!["--nocapture".to_owned()], /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private /// projects, or "discover" to try to automatically find it if the `rustc-dev` component diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 8a04bc7798f8b..5a4ad6f380f90 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -256,7 +256,7 @@ fn main() {} { "args": { "cargoArgs": ["test", "--package", "foo", "--test", "spam"], - "executableArgs": ["test_eggs", "--exact", "--show-output"], + "executableArgs": ["test_eggs", "--exact", "--nocapture"], "overrideCargo": null, "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo") @@ -289,7 +289,7 @@ fn main() {} ], "executableArgs": [ "", - "--show-output" + "--nocapture" ] }, "kind": "cargo", diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index e78f1b4ba3582..d768993f501fc 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1323,7 +1323,7 @@ tests or binaries. For example, it may be `--release`. Default: ```json [ - "--show-output" + "--nocapture" ] ``` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 745e0da4efef0..e9d00e4f1f346 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2789,7 +2789,7 @@ "rust-analyzer.runnables.extraTestBinaryArgs": { "markdownDescription": "Additional arguments to be passed through Cargo to launched tests, benchmarks, or\ndoc-tests.\n\nUnless the launched target uses a\n[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),\nthey will end up being interpreted as options to\n[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).", "default": [ - "--show-output" + "--nocapture" ], "type": "array", "items": { From 0a6462be908f00555dd7fadc6c7ed2fa751856ee Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 5 Oct 2025 23:25:08 +0200 Subject: [PATCH 1300/1900] remove unneeded imports --- library/proc_macro/src/bridge/arena.rs | 2 +- library/proc_macro/src/bridge/rpc.rs | 3 +-- library/proc_macro/src/bridge/symbol.rs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index bf5a5b5a81821..dee4e7b976de6 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -7,7 +7,7 @@ use std::cell::{Cell, RefCell}; use std::mem::MaybeUninit; use std::ops::Range; -use std::{cmp, ptr, slice, str}; +use std::{cmp, ptr, slice}; // The arenas start with PAGE-sized chunks, and then each new chunk is twice as // big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 85fd7d138585c..26ea42b49b7f8 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -3,7 +3,6 @@ use std::any::Any; use std::io::Write; use std::num::NonZero; -use std::str; pub(super) type Writer = super::buffer::Buffer; @@ -31,7 +30,7 @@ macro_rules! rpc_encode_decode { impl DecodeMut<'_, '_, S> for $ty { fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { - const N: usize = ::std::mem::size_of::<$ty>(); + const N: usize = size_of::<$ty>(); let mut bytes = [0; N]; bytes.copy_from_slice(&r[..N]); diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 57ca7db9fcdd7..eb7d30f9a6cc9 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -11,7 +11,6 @@ use std::cell::RefCell; use std::num::NonZero; -use std::str; use super::*; From 4808fd4c980417b34e28e4fae27009ccca30e3f1 Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Sun, 5 Oct 2025 18:24:19 -0500 Subject: [PATCH 1301/1900] Build rust-analyzer with specific target for install/pgo xtask --- src/tools/rust-analyzer/xtask/src/install.rs | 4 ++-- src/tools/rust-analyzer/xtask/src/pgo.rs | 25 +++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index f0cc445dfa23f..b794f53e761e9 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -145,12 +145,12 @@ fn install_server(sh: &Shell, opts: ServerOpt) -> anyhow::Result<()> { ); if let Some(train_crate) = opts.pgo { + let target = detect_target(sh); let build_cmd = cmd!( sh, - "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --profile={profile} --locked --features force-always-assert {features...}" + "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --profile={profile} --locked --features force-always-assert {features...}" ); - let target = detect_target(sh); let profile = crate::pgo::gather_pgo_profile(sh, build_cmd, &target, train_crate)?; install_cmd = crate::pgo::apply_pgo_to_cmd(install_cmd, &profile); } diff --git a/src/tools/rust-analyzer/xtask/src/pgo.rs b/src/tools/rust-analyzer/xtask/src/pgo.rs index 7f7b3311d9626..0c6f499811a24 100644 --- a/src/tools/rust-analyzer/xtask/src/pgo.rs +++ b/src/tools/rust-analyzer/xtask/src/pgo.rs @@ -53,14 +53,23 @@ pub(crate) fn gather_pgo_profile<'a>( // Merge profiles into a single file let merged_profile = pgo_dir.join("merged.profdata"); - let profile_files = std::fs::read_dir(pgo_dir)?.filter_map(|entry| { - let entry = entry.ok()?; - if entry.path().extension() == Some(OsStr::new("profraw")) { - Some(entry.path().to_str().unwrap().to_owned()) - } else { - None - } - }); + let profile_files = std::fs::read_dir(pgo_dir)? + .filter_map(|entry| { + let entry = entry.ok()?; + if entry.path().extension() == Some(OsStr::new("profraw")) { + Some(entry.path().to_str().unwrap().to_owned()) + } else { + None + } + }) + .collect::>(); + + if profile_files.is_empty() { + anyhow::bail!( + "rust-analyzer analysis-stats produced no pgo files. This is a bug in rust-analyzer; please file an issue." + ); + } + cmd!(sh, "{llvm_profdata} merge {profile_files...} -o {merged_profile}").run().context( "cannot merge PGO profiles. Do you have the rustup `llvm-tools` component installed?", )?; From 21dd997aec1af5200a8b726ae6645f40b87baf7c Mon Sep 17 00:00:00 2001 From: usamoi Date: Tue, 26 Aug 2025 16:24:29 +0800 Subject: [PATCH 1302/1900] support link modifier `as-needed` for raw-dylib-elf --- compiler/rustc_attr_parsing/messages.ftl | 2 +- .../src/attributes/link_attrs.rs | 9 ++-- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +++--- .../src/back/link/raw_dylib.rs | 24 ++++++----- .../rustc_hir/src/attrs/data_structures.rs | 18 +++++--- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../rustc_session/src/config/native_libs.rs | 3 +- .../linkage-attr/raw-dylib/elf/as_needed.rs | 43 +++++++++++++++++++ 8 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index a432fa099ef4a..8b6b762f43102 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,5 +1,5 @@ attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds attr_parsing_bundle_needs_static = linking modifier `bundle` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 04eaa485f736c..09f7bfa24e823 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -180,7 +180,8 @@ impl CombineAttributeParser for LinkParser { } (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => { + | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) + | (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => { report_unstable_modifier!(native_link_modifiers_as_needed); assign_modifier(as_needed) } @@ -219,12 +220,12 @@ impl CombineAttributeParser for LinkParser { // Do this outside of the loop so that `import_name_type` can be specified before `kind`. if let Some((_, span)) = import_name_type { - if kind != Some(NativeLibKind::RawDylib) { + if !matches!(kind, Some(NativeLibKind::RawDylib { .. })) { cx.emit_err(ImportNameTypeRaw { span }); } } - if let Some(NativeLibKind::RawDylib) = kind + if let Some(NativeLibKind::RawDylib { .. }) = kind && name.as_str().contains('\0') { cx.emit_err(RawDylibNoNul { span: name_span }); @@ -315,7 +316,7 @@ impl LinkParser { cx.emit_err(RawDylibOnlyWindows { span: nv.value_span }); } - NativeLibKind::RawDylib + NativeLibKind::RawDylib { as_needed: None } } sym::link_dash_arg => { if !features.link_arg_attribute() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index db2f2dd65b0b5..ea538d3d46981 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1490,7 +1490,7 @@ fn print_native_static_libs( NativeLibKind::Static { bundle: None | Some(true), .. } | NativeLibKind::LinkArg | NativeLibKind::WasmImportModule - | NativeLibKind::RawDylib => None, + | NativeLibKind::RawDylib { .. } => None, } }) // deduplication of consecutive repeated libraries, see rust-lang/rust#113209 @@ -2364,13 +2364,13 @@ fn linker_with_args( cmd.add_object(&output_path); } } else { - for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects( sess, codegen_results.crate_info.used_libraries.iter(), &raw_dylib_dir, ) { // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. - cmd.link_dylib_by_name(&link_path, true, false); + cmd.link_dylib_by_name(&link_path, true, as_needed); } } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case @@ -2411,13 +2411,13 @@ fn linker_with_args( cmd.add_object(&output_path); } } else { - for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects( sess, native_libraries_from_nonstatics, &raw_dylib_dir, ) { // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. - cmd.link_dylib_by_name(&link_path, true, false); + cmd.link_dylib_by_name(&link_path, true, as_needed); } } @@ -2726,7 +2726,7 @@ fn add_native_libs_from_crate( cmd.link_framework_by_name(name, verbatim, as_needed.unwrap_or(true)) } } - NativeLibKind::RawDylib => { + NativeLibKind::RawDylib { as_needed: _ } => { // Handled separately in `linker_with_args`. } NativeLibKind::WasmImportModule => {} diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 9f42991d4c06a..7321bc1da391a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -31,7 +31,7 @@ fn collate_raw_dylibs_windows<'a>( let mut dylib_table = FxIndexMap::>::default(); for lib in used_libraries { - if lib.kind == NativeLibKind::RawDylib { + if let NativeLibKind::RawDylib { .. } = lib.kind { let ext = if lib.verbatim { "" } else { ".dll" }; let name = format!("{}{}", lib.name, ext); let imports = dylib_table.entry(name.clone()).or_default(); @@ -128,12 +128,12 @@ pub(super) fn create_raw_dylib_dll_import_libs<'a>( fn collate_raw_dylibs_elf<'a>( sess: &Session, used_libraries: impl IntoIterator, -) -> Vec<(String, Vec)> { +) -> Vec<(String, Vec, bool)> { // Use index maps to preserve original order of imports and libraries. - let mut dylib_table = FxIndexMap::>::default(); + let mut dylib_table = FxIndexMap::, bool)>::default(); for lib in used_libraries { - if lib.kind == NativeLibKind::RawDylib { + if let NativeLibKind::RawDylib { as_needed } = lib.kind { let filename = if lib.verbatim { lib.name.as_str().to_owned() } else { @@ -142,17 +142,19 @@ fn collate_raw_dylibs_elf<'a>( format!("{prefix}{}{ext}", lib.name) }; - let imports = dylib_table.entry(filename.clone()).or_default(); + let (stub_imports, stub_as_needed) = + dylib_table.entry(filename.clone()).or_insert((Default::default(), true)); for import in &lib.dll_imports { - imports.insert(import.name, import); + stub_imports.insert(import.name, import); } + *stub_as_needed = *stub_as_needed && as_needed.unwrap_or(true); } } sess.dcx().abort_if_errors(); dylib_table .into_iter() - .map(|(name, imports)| { - (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) + .map(|(name, (imports, as_needed))| { + (name, imports.into_iter().map(|(_, import)| import.clone()).collect(), as_needed) }) .collect() } @@ -161,10 +163,10 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( sess: &Session, used_libraries: impl IntoIterator, raw_dylib_so_dir: &Path, -) -> Vec { +) -> Vec<(String, bool)> { collate_raw_dylibs_elf(sess, used_libraries) .into_iter() - .map(|(load_filename, raw_dylib_imports)| { + .map(|(load_filename, raw_dylib_imports, as_needed)| { use std::hash::Hash; // `load_filename` is the *target/loader* filename that will end up in NEEDED. @@ -205,7 +207,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( }); }; - temporary_lib_name + (temporary_lib_name, as_needed) }) .collect() } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index beeca7332cb17..2435363ef0eb8 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -309,7 +309,10 @@ pub enum NativeLibKind { }, /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. /// On Linux, it refers to a generated shared library stub. - RawDylib, + RawDylib { + /// Whether the dynamic library will be linked only if it satisfies some undefined symbols + as_needed: Option, + }, /// A macOS-specific kind of dynamic libraries. Framework { /// Whether the framework will be linked only if it satisfies some undefined symbols @@ -332,11 +335,10 @@ impl NativeLibKind { NativeLibKind::Static { bundle, whole_archive } => { bundle.is_some() || whole_archive.is_some() } - NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => { - as_needed.is_some() - } - NativeLibKind::RawDylib - | NativeLibKind::Unspecified + NativeLibKind::Dylib { as_needed } + | NativeLibKind::Framework { as_needed } + | NativeLibKind::RawDylib { as_needed } => as_needed.is_some(), + NativeLibKind::Unspecified | NativeLibKind::LinkArg | NativeLibKind::WasmImportModule => false, } @@ -349,7 +351,9 @@ impl NativeLibKind { pub fn is_dllimport(&self) -> bool { matches!( self, - NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified + NativeLibKind::Dylib { .. } + | NativeLibKind::RawDylib { .. } + | NativeLibKind::Unspecified ) } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 82738c68c5921..15572063d45a6 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -218,7 +218,7 @@ impl<'tcx> Collector<'tcx> { .flatten() { let dll_imports = match attr.kind { - NativeLibKind::RawDylib => foreign_items + NativeLibKind::RawDylib { .. } => foreign_items .iter() .map(|&child_item| { self.build_dll_import( diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 50a0593f88712..71d3e222c8a15 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -135,7 +135,8 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li ), ("as-needed", NativeLibKind::Dylib { as_needed }) - | ("as-needed", NativeLibKind::Framework { as_needed }) => { + | ("as-needed", NativeLibKind::Framework { as_needed }) + | ("as-needed", NativeLibKind::RawDylib { as_needed }) => { cx.on_unstable_value( "linking modifier `as-needed` is unstable", ", the `-Z unstable-options` flag must also be passed to use it", diff --git a/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs new file mode 100644 index 0000000000000..48ca39300f41c --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs @@ -0,0 +1,43 @@ +//@ only-elf +//@ needs-dynamic-linking + +//@ only-gnu +//@ only-x86_64 +//@ revisions: as_needed no_as_needed no_modifier merge_1 merge_2 merge_3 merge_4 + +//@ [as_needed] run-pass +//@ [no_as_needed] run-fail +//@ [no_modifier] run-pass +//@ [merge_1] run-fail +//@ [merge_2] run-fail +//@ [merge_3] run-fail +//@ [merge_4] run-pass + +#![allow(incomplete_features)] +#![feature(raw_dylib_elf)] +#![feature(native_link_modifiers_as_needed)] + +#[cfg_attr( + as_needed, + link(name = "taiqannf1y28z2rw", kind = "raw-dylib", modifiers = "+as-needed") +)] +#[cfg_attr( + no_as_needed, + link(name = "taiqannf1y28z2rw", kind = "raw-dylib", modifiers = "-as-needed") +)] +#[cfg_attr(no_modifier, link(name = "taiqannf1y28z2rw", kind = "raw-dylib"))] +unsafe extern "C" {} + +#[cfg_attr(merge_1, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +#[cfg_attr(merge_2, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_3, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_4, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +unsafe extern "C" {} + +#[cfg_attr(merge_1, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_2, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +#[cfg_attr(merge_3, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_4, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +unsafe extern "C" {} + +fn main() {} From 69a975faa9804a0b693c3b57c4d5b36073f9ac16 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 4 Oct 2025 16:51:58 +1000 Subject: [PATCH 1303/1900] Consistently import `llvm::Type` and `llvm::Value` --- compiler/rustc_codegen_llvm/src/abi.rs | 4 +--- compiler/rustc_codegen_llvm/src/asm.rs | 6 ++---- compiler/rustc_codegen_llvm/src/attributes.rs | 5 +++-- compiler/rustc_codegen_llvm/src/base.rs | 4 ++-- compiler/rustc_codegen_llvm/src/builder.rs | 4 +--- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 4 +--- compiler/rustc_codegen_llvm/src/callee.rs | 3 +-- compiler/rustc_codegen_llvm/src/common.rs | 4 +--- compiler/rustc_codegen_llvm/src/consts.rs | 5 ++--- compiler/rustc_codegen_llvm/src/context.rs | 6 ++---- compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs | 3 +-- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 3 +-- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 3 +-- compiler/rustc_codegen_llvm/src/declare.rs | 6 ++---- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +--- compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs | 2 +- compiler/rustc_codegen_llvm/src/type_.rs | 6 ++---- compiler/rustc_codegen_llvm/src/type_of.rs | 2 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 3 +-- compiler/rustc_codegen_llvm/src/value.rs | 3 +-- 20 files changed, 28 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 15b4d5247a978..680ad98593e7e 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -22,11 +22,9 @@ use smallvec::SmallVec; use crate::attributes::{self, llfn_attrs_from_instance}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::llvm::{self, Attribute, AttributePlace}; +use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; use crate::llvm_util; -use crate::type_::Type; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; trait ArgAttributesExt { fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 93b1cf272ab7b..2b6a0ac9757b5 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -13,14 +13,12 @@ use rustc_target::asm::*; use smallvec::SmallVec; use tracing::debug; +use crate::attributes; use crate::builder::Builder; use crate::common::Funclet; use crate::context::CodegenCx; -use crate::llvm::ToLlvmBool; -use crate::type_::Type; +use crate::llvm::{self, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{attributes, llvm}; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_inline_asm( diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8070ea0b3e927..209b8efa2c3b3 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,8 +13,9 @@ use smallvec::SmallVec; use crate::context::SimpleCx; use crate::errors::SanitizerMemtagRequiresMte; use crate::llvm::AttributePlace::Function; -use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects}; -use crate::value::Value; +use crate::llvm::{ + self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value, +}; use crate::{Session, attributes, llvm_util}; pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6d12b511e9c81..4523d629b1ef6 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -28,10 +28,10 @@ use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use super::ModuleLlvm; +use crate::attributes; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::value::Value; -use crate::{attributes, llvm}; +use crate::llvm::{self, Value}; pub(crate) struct ValueIter<'ll> { cur: Option<&'ll Value>, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 08da90d535da1..e65f3d292dbb8 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -37,11 +37,9 @@ use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, - ToLlvmBool, + ToLlvmBool, Type, Value, }; -use crate::type_::Type; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; #[must_use] pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 4a749642265df..bf70a3f4a071d 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -12,9 +12,7 @@ use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; -use crate::llvm; -use crate::llvm::{Metadata, TRUE, Type}; -use crate::value::Value; +use crate::llvm::{self, Metadata, TRUE, Type, Value}; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 791a71d73ae58..b86b32b92df04 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -10,8 +10,7 @@ use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; -use crate::llvm; -use crate::value::Value; +use crate::llvm::{self, Value}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index aa2df46329f22..175fc8535ac3f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -20,9 +20,7 @@ use tracing::debug; use crate::consts::const_alloc_to_llvm; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool}; -use crate::type_::Type; -use crate::value::Value; +use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b4ca85a26c8b8..e14f0098dd9b4 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -21,10 +21,9 @@ use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::type_::Type; +use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{base, debuginfo, llvm}; +use crate::{base, debuginfo}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 3ddbd81ef7580..808aaceab4d20 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -35,10 +35,8 @@ use crate::abi::to_llvm_calling_convention; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; -use crate::llvm::{Metadata, MetadataKindId, Module}; -use crate::type_::Type; -use crate::value::Value; -use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; +use crate::llvm::{self, Metadata, MetadataKindId, Module, Type, Value}; +use crate::{attributes, common, coverageinfo, debuginfo, llvm_util}; /// `TyCtxt` (and related cache datastructures) can't be move between threads. /// However, there are various cx related functions which we want to be available to the builder and diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 3081badb821fa..3a750b6b53eb1 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -9,8 +9,7 @@ use rustc_session::config::{CrateType, DebugInfo}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::llvm; -use crate::value::Value; +use crate::llvm::{self, Value}; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b8b5f3d30d7c8..014715dd4fda5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -41,8 +41,7 @@ use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, DebugNameTableKind, }; -use crate::llvm::{self, FromGeneric}; -use crate::value::Value; +use crate::llvm::{self, FromGeneric, Value}; impl PartialEq for llvm::Metadata { fn eq(&self, other: &Self) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c6ad1c2e18ec7..f61c25bccfad3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -35,12 +35,11 @@ use self::namespace::mangled_name_of_instance; use self::utils::{DIB, create_DIArray, is_node_local_to_unit}; use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; -use crate::llvm; use crate::llvm::debuginfo::{ DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DITemplateTypeParameter, DIType, DIVariable, }; -use crate::value::Value; +use crate::llvm::{self, Value}; mod create_scope_map; mod dwarf_const; diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index f598763efcf22..48b2d09afb674 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -23,13 +23,11 @@ use smallvec::SmallVec; use tracing::debug; use crate::abi::FnAbiLlvmExt; +use crate::attributes; use crate::common::AsCCharPtr; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; -use crate::llvm::{FromGeneric, Visibility}; -use crate::type_::Type; -use crate::value::Value; -use crate::{attributes, llvm}; +use crate::llvm::{self, FromGeneric, Type, Value, Visibility}; /// Declare a function with a SimpleCx. /// diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 467655b0bfcd7..c12383f19312d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -25,11 +25,9 @@ use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::context::CodegenCx; use crate::errors::AutoDiffWithoutEnable; -use crate::llvm::{self, Metadata}; -use crate::type_::Type; +use crate::llvm::{self, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; -use crate::value::Value; fn call_simple_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index 0e0f2b0eab016..59b2cd329ae76 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -6,7 +6,7 @@ use rustc_span::InnerSpan; pub(crate) use self::Diagnostic::*; use self::OptimizationDiagnosticKind::*; use super::{DiagnosticInfo, SMDiagnostic}; -use crate::value::Value; +use crate::llvm::Value; #[derive(Copy, Clone, Debug)] pub(crate) enum OptimizationDiagnosticKind { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 9de9e0ec44c67..81bb70c958790 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -13,12 +13,10 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiLlvmExt, LlvmType}; +use crate::common; use crate::context::{CodegenCx, GenericCx, SCx}; -pub(crate) use crate::llvm::Type; -use crate::llvm::{FALSE, Metadata, TRUE, ToLlvmBool}; +use crate::llvm::{self, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{common, llvm}; impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 84998b5499bdf..2b2ac1c6eb29f 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -11,7 +11,7 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::common::*; -use crate::type_::Type; +use crate::llvm::Type; fn uncached_llvm_type<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 234366e491c57..2d9abb412d4f1 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -9,9 +9,8 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use crate::builder::Builder; -use crate::type_::Type; +use crate::llvm::{Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; fn round_up_to_alignment<'ll>( bx: &mut Builder<'_, 'll, '_>, diff --git a/compiler/rustc_codegen_llvm/src/value.rs b/compiler/rustc_codegen_llvm/src/value.rs index 2eabac3be8c56..37f05f10c50cd 100644 --- a/compiler/rustc_codegen_llvm/src/value.rs +++ b/compiler/rustc_codegen_llvm/src/value.rs @@ -1,8 +1,7 @@ use std::hash::{Hash, Hasher}; use std::{fmt, ptr}; -use crate::llvm; -pub(crate) use crate::llvm::Value; +use crate::llvm::{self, Value}; impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { From c7d180cd60225779b5f2833f13d013f000f2efcb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 05:54:53 +0200 Subject: [PATCH 1304/1900] add multi-arch asm! label operand test --- .../src/directives/directive_names.rs | 2 + tests/ui/asm/label-operand.rs | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/asm/label-operand.rs diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 4fef899256739..1ab73fd87e1d0 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -191,6 +191,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-aarch64-unknown-linux-gnu", "only-apple", "only-arm", + "only-arm64ec", "only-avr", "only-beta", "only-bpf", @@ -217,6 +218,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-nightly", "only-nvptx64", "only-powerpc", + "only-riscv32", "only-riscv64", "only-rustc_abi-x86-sse2", "only-s390x", diff --git a/tests/ui/asm/label-operand.rs b/tests/ui/asm/label-operand.rs new file mode 100644 index 0000000000000..f1be5d7d85609 --- /dev/null +++ b/tests/ui/asm/label-operand.rs @@ -0,0 +1,53 @@ +//@ run-pass +//@ reference: asm.operand-type.supported-operands.label +//@ revisions: aarch64 arm arm64ec riscv32 riscv64 x86 x86_64 +//@ needs-asm-support +//@[aarch64] only-aarch64 +//@[arm64ec] only-arm64ec +//@[arm] only-arm +//@[riscv32] only-riscv32 +//@[riscv64] only-riscv64 +//@[x86] only-x86 +//@[x86_64] only-x86_64 + +#[cfg(any(aarch64, arm, arm64ec))] +fn make_true(value: &mut bool) { + unsafe { + core::arch::asm!( + "b {}", + label { + *value = true; + } + ); + } +} + +#[cfg(any(riscv32, riscv64))] +fn make_true(value: &mut bool) { + unsafe { + core::arch::asm!( + "j {}", + label { + *value = true; + } + ); + } +} + +#[cfg(any(x86, x86_64))] +fn make_true(value: &mut bool) { + unsafe { + core::arch::asm!( + "jmp {}", + label { + *value = true; + } + ); + } +} + +fn main() { + let mut value = false; + make_true(&mut value); + assert!(value); +} From f54139aa310b4cd2cb2bfc144eec865464a8cc09 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 6 Oct 2025 04:13:15 +0000 Subject: [PATCH 1305/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 4fa824bb78318a3cba8c7339d5754b4909922547. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 0dc9ce843e9c9..47552aee08f74 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -9f32ccf35fb877270bc44a86a126440f04d676d0 +4fa824bb78318a3cba8c7339d5754b4909922547 From eeb7cb1b8a42630458e6c6cdabf0c4264579aec7 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 22 Sep 2025 07:39:48 +0200 Subject: [PATCH 1306/1900] compiler: Hint at multiple crate versions if trait impl is for wrong ADT If a user does e.g. impl From for foo::Foo and get a compilation error about that `From` is not implemented for `Foo`, check if multiple versions of the crate with `Foo` is present in the dependency graph. If so, give a hint about it. I encountered this case in the wild and didn't realize I had multiple versions of a crate in my dependency graph. So I was a bit confused at first. This fix will make life easier for others. --- .../traits/fulfillment_errors.rs | 85 +++++++++++++++++-- .../run-make/duplicate-dependency/main.stderr | 6 ++ tests/run-make/duplicate-dependency/rmake.rs | 4 +- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 149f5e638b1ad..7728f657bedad 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -467,7 +467,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, leaf_trait_predicate); + self.note_trait_version_mismatch(&mut err, leaf_trait_predicate); + self.note_adt_version_mismatch(&mut err, leaf_trait_predicate); self.suggest_remove_await(&obligation, &mut err); self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); @@ -2406,7 +2407,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait /// with the same path as `trait_ref`, a help message about /// a probable version mismatch is added to `err` - fn note_version_mismatch( + fn note_trait_version_mismatch( &self, err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -2446,15 +2447,87 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_spans, 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 `{trait_crate}` are being used?"); - err.note(crate_msg); + self.note_two_crate_versions(trait_with_same_path, err); suggested = true; } suggested } + fn note_two_crate_versions(&self, did: DefId, err: &mut Diag<'_>) { + let crate_name = self.tcx.crate_name(did.krate); + let crate_msg = + format!("perhaps two different versions of crate `{crate_name}` are being used?"); + err.note(crate_msg); + } + + fn note_adt_version_mismatch( + &self, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let ty::Adt(impl_self_def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() + else { + return; + }; + + let impl_self_did = impl_self_def.did(); + + // We only want to warn about different versions of a dependency. + // If no dependency is involved, bail. + if impl_self_did.krate == LOCAL_CRATE { + return; + } + + let impl_self_path = self.comparable_path(impl_self_did); + let impl_self_crate_name = self.tcx.crate_name(impl_self_did.krate); + let similar_items: UnordSet<_> = self + .tcx + .visible_parent_map(()) + .items() + .filter_map(|(&item, _)| { + // If we found ourselves, ignore. + if impl_self_did == item { + return None; + } + // We only want to warn about different versions of a dependency. + // Ignore items from our own crate. + if item.krate == LOCAL_CRATE { + return None; + } + // We want to warn about different versions of a dependency. + // So make sure the crate names are the same. + if impl_self_crate_name != self.tcx.crate_name(item.krate) { + return None; + } + // Filter out e.g. constructors that often have the same path + // str as the relevant ADT. + if !self.tcx.def_kind(item).is_adt() { + return None; + } + let path = self.comparable_path(item); + // We don't know if our item or the one we found is the re-exported one. + // Check both cases. + let is_similar = path.ends_with(&impl_self_path) || impl_self_path.ends_with(&path); + is_similar.then_some((item, path)) + }) + .collect(); + + let mut similar_items = + similar_items.into_items().into_sorted_stable_ord_by_key(|(_, path)| path); + similar_items.dedup(); + + for (similar_item, _) in similar_items { + err.span_help(self.tcx.def_span(similar_item), "item with same name found"); + self.note_two_crate_versions(similar_item, err); + } + } + + /// Add a `::` prefix when comparing paths so that paths with just one item + /// like "Foo" does not equal the end of "OtherFoo". + fn comparable_path(&self, did: DefId) -> String { + format!("::{}", self.tcx.def_path_str(did)) + } + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the /// `trait_ref`. /// diff --git a/tests/run-make/duplicate-dependency/main.stderr b/tests/run-make/duplicate-dependency/main.stderr index 2c912f872fe35..36d54988788f4 100644 --- a/tests/run-make/duplicate-dependency/main.stderr +++ b/tests/run-make/duplicate-dependency/main.stderr @@ -6,6 +6,12 @@ LL | re_export_foo::into_foo(Bar); | | | required by a bound introduced by this call | +help: item with same name found + --> $DIR/foo-v1.rs:1:1 + | +LL | pub struct Foo; + | ^^^^^^^^^^^^^^ + = note: perhaps two different versions of crate `foo` are being used? = note: required for `Bar` to implement `Into` note: required by a bound in `into_foo` --> $DIR/re-export-foo.rs:3:25 diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs index 13ab4caaba56f..762d97e4311f4 100644 --- a/tests/run-make/duplicate-dependency/rmake.rs +++ b/tests/run-make/duplicate-dependency/rmake.rs @@ -1,3 +1,5 @@ +//@ needs-target-std + use run_make_support::{Rustc, cwd, diff, rust_lib_name, rustc}; fn rustc_with_common_args() -> Rustc { @@ -39,5 +41,5 @@ fn main() { .run_fail() .stderr_utf8(); - diff().expected_file("main.stderr").actual_text("(rustc)", &stderr).run(); + diff().expected_file("main.stderr").normalize(r"\\", "/").actual_text("(rustc)", &stderr).run(); } From 7b9e26ea6cd7a109f55f8d483528e06309c71e30 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 6 Oct 2025 12:48:38 +0800 Subject: [PATCH 1307/1900] Improve parsing error for `static` and `const` Example --- ```rust static C: u32 = 0; ``` -> ```diff -error 8: missing type for `const` or `static` +error 8: `static` may not have generic parameters ``` --- ```rust const C = 0; ``` -> ```diff -error 7: missing type for `const` or `static` +error 7: missing type for `const` ``` --- ```rust static C = 0; ``` -> ```diff -error 8: missing type for `const` or `static` +error 8: missing type for `static` ``` --- .../crates/parser/src/grammar/items/consts.rs | 12 ++++++++++-- .../crates/parser/test_data/generated/runner.rs | 8 ++++++++ .../test_data/parser/err/0044_item_modifiers.rast | 2 +- .../parser/inline/err/generic_static.rast | 2 +- .../parser/inline/err/missing_const_type.rast | 14 ++++++++++++++ .../parser/inline/err/missing_const_type.rs | 1 + .../parser/inline/err/missing_static_type.rast | 14 ++++++++++++++ .../parser/inline/err/missing_static_type.rs | 1 + 8 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs index 8e255985a205d..6b53493c9a36d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs @@ -32,14 +32,22 @@ fn const_or_static(p: &mut Parser<'_>, m: Marker, is_const: bool) { // const C<'a>: &'a () = &(); // } generic_params::opt_generic_param_list(p); + } else if p.at(T![<]) { + p.error("`static` may not have generic parameters"); } // test_err generic_static // static C: u32 = 0; if p.at(T![:]) { types::ascription(p); - } else { - p.error("missing type for `const` or `static`"); + } else if is_const { + // test_err missing_const_type + // const C = 0; + p.error("missing type for `const`"); + } else if !p.at(T![<]) { + // test_err missing_static_type + // static C = 0; + p.error("missing type for `static`"); } if p.eat(T![=]) { expressions::expr(p); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index a3cfe64e6e739..cd6d433d0efa7 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -824,10 +824,18 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/misplaced_label_err.rs"); } #[test] + fn missing_const_type() { + run_and_expect_errors("test_data/parser/inline/err/missing_const_type.rs"); + } + #[test] fn missing_fn_param_type() { run_and_expect_errors("test_data/parser/inline/err/missing_fn_param_type.rs"); } #[test] + fn missing_static_type() { + run_and_expect_errors("test_data/parser/inline/err/missing_static_type.rs"); + } + #[test] fn path_item_without_excl() { run_and_expect_errors("test_data/parser/inline/err/path_item_without_excl.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast index d6e3219c39577..7a3ca66476e32 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast @@ -42,7 +42,7 @@ SOURCE_FILE WHITESPACE "\n" error 6: expected fn, trait or impl error 38: expected a name -error 40: missing type for `const` or `static` +error 40: missing type for `const` error 40: expected SEMICOLON error 44: expected an item error 44: expected an item diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast index 485ad11f233ac..0c240b7214f43 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast @@ -30,7 +30,7 @@ SOURCE_FILE ERROR SEMICOLON ";" WHITESPACE "\n" -error 8: missing type for `const` or `static` +error 8: `static` may not have generic parameters error 8: expected SEMICOLON error 8: expected an item error 12: expected an item diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rast new file mode 100644 index 0000000000000..11097232a6831 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rast @@ -0,0 +1,14 @@ +SOURCE_FILE + CONST + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "C" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + SEMICOLON ";" + WHITESPACE "\n" +error 7: missing type for `const` diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rs new file mode 100644 index 0000000000000..e3ce44dadfbd2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_const_type.rs @@ -0,0 +1 @@ +const C = 0; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rast new file mode 100644 index 0000000000000..a9595401d6d62 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rast @@ -0,0 +1,14 @@ +SOURCE_FILE + STATIC + STATIC_KW "static" + WHITESPACE " " + NAME + IDENT "C" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + SEMICOLON ";" + WHITESPACE "\n" +error 8: missing type for `static` diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rs new file mode 100644 index 0000000000000..33ecedf80772b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_static_type.rs @@ -0,0 +1 @@ +static C = 0; From 98afeb86453540ebdea0af21ba5064caed6b089b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 07:06:27 +0200 Subject: [PATCH 1308/1900] fix text describing edition range testing --- src/doc/rustc-dev-guide/src/tests/directives.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 3b6c891746455..25c8a4c354ab6 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -264,11 +264,16 @@ Consider writing the test as a proper incremental test instead. #### The edition directive -The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example: +The `//@ edition` directive can take an exact edition, a bounded range of editions, or a left-bounded half-open range of editions. +This affects which edition is used by `./x test` to run the test. + +For example: - A test with the `//@ edition: 2018` directive will only run under the 2018 edition. -- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case). -- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case). +- A test with the `//@ edition: 2015..2021` directive can be run under the 2015, 2018, and 2021 editions. + However, CI will only run the test with the lowest edition in the range (which is 2015 in this example). +- A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. + However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`: From 2d6f590af115f16c57796162703cca62a5860ebd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 07:13:01 +0200 Subject: [PATCH 1309/1900] overlong --- src/doc/rustc-dev-guide/src/tests/directives.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 25c8a4c354ab6..2c6cfeb0ac702 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -264,7 +264,8 @@ Consider writing the test as a proper incremental test instead. #### The edition directive -The `//@ edition` directive can take an exact edition, a bounded range of editions, or a left-bounded half-open range of editions. +The `//@ edition` directive can take an exact edition, a bounded range of editions, +or a left-bounded half-open range of editions. This affects which edition is used by `./x test` to run the test. For example: @@ -275,7 +276,9 @@ For example: - A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). -You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`: +You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. +However, tests with the `//@ edition` directive will clamp the value passed to the argument. +For example, if we run `./x test -- --edition=2015`: - A test with the `//@ edition: 2018` will run with the 2018 edition. - A test with the `//@ edition: 2015..2021` will be run with the 2015 edition. From b6c9a6a442daa4ef187a23e6f4e4d5ab78702cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Sep 2025 18:25:00 +0200 Subject: [PATCH 1310/1900] Generalize setting `rust-lld` as the default linker on Linux --- compiler/rustc_target/src/spec/base/linux_gnu.rs | 13 +++++++++++-- .../src/spec/targets/x86_64_unknown_linux_gnu.rs | 7 ------- src/bootstrap/src/core/build_steps/compile.rs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/linux_gnu.rs b/compiler/rustc_target/src/spec/base/linux_gnu.rs index e9135b43ebc1f..2fcd8c61a9c62 100644 --- a/compiler/rustc_target/src/spec/base/linux_gnu.rs +++ b/compiler/rustc_target/src/spec/base/linux_gnu.rs @@ -1,5 +1,14 @@ -use crate::spec::{TargetOptions, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - TargetOptions { env: "gnu".into(), ..base::linux::opts() } + let mut base = TargetOptions { env: "gnu".into(), ..base::linux::opts() }; + + // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using + // linker flavor, and self-contained linker component. + if option_env!("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC").is_some() { + base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); + base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); + } + + base } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 0c8353fad182f..59bb7f5962101 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -20,13 +20,6 @@ pub(crate) fn target() -> Target { | SanitizerSet::THREAD; base.supports_xray = true; - // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using - // linker flavor, and self-contained linker component. - if option_env!("CFG_USE_SELF_CONTAINED_LINKER").is_some() { - base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); - base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); - } - Target { llvm_target: "x86_64-unknown-linux-gnu".into(), metadata: TargetMetadata { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d29d1041486bd..6c34b62491962 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1357,7 +1357,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS // Enable rustc's env var for `rust-lld` when requested. if builder.config.lld_enabled { - cargo.env("CFG_USE_SELF_CONTAINED_LINKER", "1"); + cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1"); } if builder.config.rust_verify_llvm_ir { From 8dd97f887ceb8376ddf24b8b34d36019d249b74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Sep 2025 17:39:30 +0200 Subject: [PATCH 1311/1900] Destructure `TomlConfig` and add missing `default_linker` config --- src/bootstrap/src/core/config/config.rs | 78 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1fcc1174e856c..84457e7c3345b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -44,7 +44,7 @@ use crate::core::config::toml::rust::{ BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, default_lld_opt_in_targets, parse_codegen_backends, }; -use crate::core::config::toml::target::Target; +use crate::core::config::toml::target::{Target, TomlTarget}; use crate::core::config::{ CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, @@ -831,9 +831,36 @@ impl Config { if let Some(t) = toml.target { for (triple, cfg) in t { + let TomlTarget { + cc: target_cc, + cxx: target_cxx, + ar: target_ar, + ranlib: target_ranlib, + default_linker: target_default_linker, + linker: target_linker, + split_debuginfo: target_split_debuginfo, + llvm_config: target_llvm_config, + llvm_has_rust_patches: target_llvm_has_rust_patches, + llvm_filecheck: target_llvm_filecheck, + llvm_libunwind: target_llvm_libunwind, + sanitizers: target_sanitizers, + profiler: target_profiler, + rpath: target_rpath, + crt_static: target_crt_static, + musl_root: target_musl_root, + musl_libdir: target_musl_libdir, + wasi_root: target_wasi_root, + qemu_rootfs: target_qemu_rootfs, + no_std: target_no_std, + codegen_backends: target_codegen_backends, + runner: target_runner, + optimized_compiler_builtins: target_optimized_compiler_builtins, + jemalloc: target_jemalloc, + } = cfg; + let mut target = Target::from_triple(&triple); - if let Some(ref s) = cfg.llvm_config { + if let Some(ref s) = target_llvm_config { if download_rustc_commit.is_some() && triple == *host_target.triple { panic!( "setting llvm_config for the host is incompatible with download-rustc" @@ -841,46 +868,47 @@ impl Config { } target.llvm_config = Some(src.join(s)); } - if let Some(patches) = cfg.llvm_has_rust_patches { + if let Some(patches) = target_llvm_has_rust_patches { assert!( - build_submodules == Some(false) || cfg.llvm_config.is_some(), + build_submodules == Some(false) || target_llvm_config.is_some(), "use of `llvm-has-rust-patches` is restricted to cases where either submodules are disabled or llvm-config been provided" ); target.llvm_has_rust_patches = Some(patches); } - if let Some(ref s) = cfg.llvm_filecheck { + if let Some(ref s) = target_llvm_filecheck { target.llvm_filecheck = Some(src.join(s)); } - target.llvm_libunwind = cfg.llvm_libunwind.as_ref().map(|v| { + target.llvm_libunwind = target_llvm_libunwind.as_ref().map(|v| { v.parse().unwrap_or_else(|_| { panic!("failed to parse target.{triple}.llvm-libunwind") }) }); - if let Some(s) = cfg.no_std { + if let Some(s) = target_no_std { target.no_std = s; } - target.cc = cfg.cc.map(PathBuf::from); - target.cxx = cfg.cxx.map(PathBuf::from); - target.ar = cfg.ar.map(PathBuf::from); - target.ranlib = cfg.ranlib.map(PathBuf::from); - target.linker = cfg.linker.map(PathBuf::from); - target.crt_static = cfg.crt_static; - target.musl_root = cfg.musl_root.map(PathBuf::from); - target.musl_libdir = cfg.musl_libdir.map(PathBuf::from); - target.wasi_root = cfg.wasi_root.map(PathBuf::from); - target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from); - target.runner = cfg.runner; - target.sanitizers = cfg.sanitizers; - target.profiler = cfg.profiler; - target.rpath = cfg.rpath; - target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; - target.jemalloc = cfg.jemalloc; - if let Some(backends) = cfg.codegen_backends { + target.cc = target_cc.map(PathBuf::from); + target.cxx = target_cxx.map(PathBuf::from); + target.ar = target_ar.map(PathBuf::from); + target.ranlib = target_ranlib.map(PathBuf::from); + target.linker = target_linker.map(PathBuf::from); + target.crt_static = target_crt_static; + target.default_linker = target_default_linker; + target.musl_root = target_musl_root.map(PathBuf::from); + target.musl_libdir = target_musl_libdir.map(PathBuf::from); + target.wasi_root = target_wasi_root.map(PathBuf::from); + target.qemu_rootfs = target_qemu_rootfs.map(PathBuf::from); + target.runner = target_runner; + target.sanitizers = target_sanitizers; + target.profiler = target_profiler; + target.rpath = target_rpath; + target.optimized_compiler_builtins = target_optimized_compiler_builtins; + target.jemalloc = target_jemalloc; + if let Some(backends) = target_codegen_backends { target.codegen_backends = Some(parse_codegen_backends(backends, &format!("target.{triple}"))) } - target.split_debuginfo = cfg.split_debuginfo.as_ref().map(|v| { + target.split_debuginfo = target_split_debuginfo.as_ref().map(|v| { v.parse().unwrap_or_else(|_| { panic!("invalid value for target.{triple}.split-debuginfo") }) From 902cec2bc6997748e2ca47c11dba8acc673806d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 6 Oct 2025 10:30:31 +0200 Subject: [PATCH 1312/1900] Allow manually opting in and out of Linux linker overrides --- bootstrap.example.toml | 22 +++-- src/bootstrap/src/core/build_steps/compile.rs | 12 ++- src/bootstrap/src/core/builder/tests.rs | 33 ++++++- src/bootstrap/src/core/config/config.rs | 94 ++++++++++++++----- src/bootstrap/src/core/config/toml/rust.rs | 25 ----- src/bootstrap/src/core/config/toml/target.rs | 62 ++++++++++++ 6 files changed, 185 insertions(+), 63 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index f623a3db0029d..6f37e51a47de2 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -758,12 +758,8 @@ # Currently, the only standard options supported here are `"llvm"`, `"cranelift"` and `"gcc"`. #rust.codegen-backends = ["llvm"] -# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and -# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be -# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from -# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will -# make this default to false. -#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true +# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, +#rust.lld = false, except for targets that opt into LLD (see `target.default-linker-linux-override`) # Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD. # If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH @@ -1067,3 +1063,17 @@ # Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. # This overrides the global `rust.jemalloc` option. See that option for more info. #jemalloc = rust.jemalloc (bool) + +# The linker configuration that will *override* the default linker used for Linux +# targets in the built compiler. +# +# The following values are supported: +# - `off` => do not apply any override and use the default linker. This can be used to opt out of +# linker overrides set by bootstrap for specific targets (see below). +# - `self-contained-lld-cc` => override the default linker to be self-contained LLD (`rust-lld`) +# that is invoked through `cc`. +# +# Currently, the following targets automatically opt into the self-contained LLD linker, unless you +# pass `off`: +# - x86_64-unknown-linux-gnu +#default-linker-linux-override = "off" (for most targets) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 6c34b62491962..dd90ac7cdda31 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -26,6 +26,7 @@ use crate::core::builder; use crate::core::builder::{ Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; +use crate::core::config::toml::target::DefaultLinuxLinkerOverride; use crate::core::config::{ CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection, }; @@ -1355,9 +1356,14 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("CFG_DEFAULT_LINKER", s); } - // Enable rustc's env var for `rust-lld` when requested. - if builder.config.lld_enabled { - cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1"); + // Enable rustc's env var to use a linker override on Linux when requested. + if let Some(linker) = target_config.map(|c| c.default_linker_linux_override) { + match linker { + DefaultLinuxLinkerOverride::Off => {} + DefaultLinuxLinkerOverride::SelfContainedLldCc => { + cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1"); + } + } } if builder.config.rust_verify_llvm_ir { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d0429387f824e..e0eb38d04aad0 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -514,7 +514,9 @@ mod snapshot { }; use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata}; use crate::core::config::TargetSelection; - use crate::core::config::toml::rust::with_lld_opt_in_targets; + use crate::core::config::toml::target::{ + DefaultLinuxLinkerOverride, with_default_linux_linker_overrides, + }; use crate::utils::cache::Cache; use crate::utils::helpers::get_host_target; use crate::utils::tests::{ConfigBuilder, TestCtx}; @@ -782,9 +784,11 @@ mod snapshot { #[test] fn build_compiler_lld_opt_in() { - with_lld_opt_in_targets(vec![host_target()], || { - let ctx = TestCtx::new(); - insta::assert_snapshot!( + with_default_linux_linker_overrides( + [(host_target(), DefaultLinuxLinkerOverride::SelfContainedLldCc)].into(), + || { + let ctx = TestCtx::new(); + insta::assert_snapshot!( ctx.config("build") .path("compiler") .render_steps(), @r" @@ -792,7 +796,26 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 0 -> LldWrapper 1 "); - }); + }, + ); + } + + #[test] + fn build_compiler_lld_opt_in_lld_disabled() { + with_default_linux_linker_overrides( + [(host_target(), DefaultLinuxLinkerOverride::SelfContainedLldCc)].into(), + || { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("build") + .path("compiler") + .args(&["--set", "rust.lld=false"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + "); + }, + ); } #[test] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 84457e7c3345b..0a5c342835028 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -42,9 +42,11 @@ use crate::core::config::toml::install::Install; use crate::core::config::toml::llvm::Llvm; use crate::core::config::toml::rust::{ BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, - default_lld_opt_in_targets, parse_codegen_backends, + parse_codegen_backends, +}; +use crate::core::config::toml::target::{ + DefaultLinuxLinkerOverride, Target, TomlTarget, default_linux_linker_overrides, }; -use crate::core::config::toml::target::{Target, TomlTarget}; use crate::core::config::{ CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, @@ -829,6 +831,11 @@ impl Config { .to_owned(); } + let mut lld_enabled = rust_lld_enabled.unwrap_or(false); + + // Linux targets for which the user explicitly overrode the used linker + let mut targets_with_user_linker_override = HashSet::new(); + if let Some(t) = toml.target { for (triple, cfg) in t { let TomlTarget { @@ -837,6 +844,7 @@ impl Config { ar: target_ar, ranlib: target_ranlib, default_linker: target_default_linker, + default_linker_linux: target_default_linker_linux_override, linker: target_linker, split_debuginfo: target_split_debuginfo, llvm_config: target_llvm_config, @@ -860,6 +868,33 @@ impl Config { let mut target = Target::from_triple(&triple); + if target_default_linker_linux_override.is_some() { + targets_with_user_linker_override.insert(triple.clone()); + } + + let default_linker_linux_override = match target_default_linker_linux_override { + Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) => { + if rust_default_linker.is_some() { + panic!( + "cannot set both `default-linker` and `default-linker-linux` for target `{triple}`" + ); + } + if !triple.contains("linux-gnu") { + panic!( + "`default-linker-linux` can only be set for Linux GNU targets, not for `{triple}`" + ); + } + if !lld_enabled { + panic!( + "Trying to override the default Linux linker for `{triple}` to be self-contained LLD, but LLD is not being built. Enable it with rust.lld = true." + ); + } + DefaultLinuxLinkerOverride::SelfContainedLldCc + } + Some(DefaultLinuxLinkerOverride::Off) => DefaultLinuxLinkerOverride::Off, + None => DefaultLinuxLinkerOverride::default(), + }; + if let Some(ref s) = target_llvm_config { if download_rustc_commit.is_some() && triple == *host_target.triple { panic!( @@ -893,6 +928,7 @@ impl Config { target.linker = target_linker.map(PathBuf::from); target.crt_static = target_crt_static; target.default_linker = target_default_linker; + target.default_linker_linux_override = default_linker_linux_override; target.musl_root = target_musl_root.map(PathBuf::from); target.musl_libdir = target_musl_libdir.map(PathBuf::from); target.wasi_root = target_wasi_root.map(PathBuf::from); @@ -918,6 +954,38 @@ impl Config { } } + for (target, linker_override) in default_linux_linker_overrides() { + // If the user overrode the default Linux linker, do not apply bootstrap defaults + if targets_with_user_linker_override.contains(&target) { + continue; + } + let default_linux_linker_override = match linker_override { + DefaultLinuxLinkerOverride::Off => continue, + DefaultLinuxLinkerOverride::SelfContainedLldCc => { + // If we automatically default to the self-contained LLD linker, + // we also need to handle the rust.lld option. + match rust_lld_enabled { + // If LLD was not enabled explicitly, we enable it + None => { + lld_enabled = true; + Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) + } + // If it was enabled already, we don't need to do anything + Some(true) => Some(DefaultLinuxLinkerOverride::SelfContainedLldCc), + // If it was explicitly disabled, we do not apply the + // linker override + Some(false) => None, + } + } + }; + if let Some(linker_override) = default_linux_linker_override { + target_config + .entry(TargetSelection::from_user(&target)) + .or_default() + .default_linker_linux_override = linker_override; + } + } + let llvm_from_ci = parse_download_ci_llvm( &dwn_ctx, &rust_info, @@ -926,28 +994,6 @@ impl Config { llvm_assertions, ); - // We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will - // build our internal lld and use it as the default linker, by setting the `rust.lld` config - // to true by default: - // - on the `x86_64-unknown-linux-gnu` target - // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that - // we're also able to build the corresponding lld - // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt - // lld - // - otherwise, we'd be using an external llvm, and lld would not necessarily available and - // thus, disabled - // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g. - // when the config sets `rust.lld = false` - let lld_enabled = if default_lld_opt_in_targets().contains(&host_target.triple.to_string()) - && hosts == [host_target] - { - let no_llvm_config = - target_config.get(&host_target).is_none_or(|config| config.llvm_config.is_none()); - rust_lld_enabled.unwrap_or(llvm_from_ci || no_llvm_config) - } else { - rust_lld_enabled.unwrap_or(false) - }; - if llvm_from_ci { let warn = |option: &str| { println!( diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index ca9e0d0bc98e7..5a2c6e1698698 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -438,28 +438,3 @@ pub(crate) fn parse_codegen_backends( } found_backends } - -#[cfg(not(test))] -pub fn default_lld_opt_in_targets() -> Vec { - vec!["x86_64-unknown-linux-gnu".to_string()] -} - -#[cfg(test)] -thread_local! { - static TEST_LLD_OPT_IN_TARGETS: std::cell::RefCell>> = std::cell::RefCell::new(None); -} - -#[cfg(test)] -pub fn default_lld_opt_in_targets() -> Vec { - TEST_LLD_OPT_IN_TARGETS.with(|cell| cell.borrow().clone()).unwrap_or_default() -} - -#[cfg(test)] -pub fn with_lld_opt_in_targets(targets: Vec, f: impl FnOnce() -> R) -> R { - TEST_LLD_OPT_IN_TARGETS.with(|cell| { - let prev = cell.replace(Some(targets)); - let result = f(); - cell.replace(prev); - result - }) -} diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 020602e6a1997..7b8d4f9601b53 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -9,6 +9,9 @@ //! * [`Target`]: This struct represents the processed and validated configuration for a //! build target, which is is stored in the main `Config` structure. +use std::collections::HashMap; + +use serde::de::Error; use serde::{Deserialize, Deserializer}; use crate::core::config::{ @@ -24,6 +27,7 @@ define_config! { ar: Option = "ar", ranlib: Option = "ranlib", default_linker: Option = "default-linker", + default_linker_linux: Option = "default-linker-linux-override", linker: Option = "linker", split_debuginfo: Option = "split-debuginfo", llvm_config: Option = "llvm-config", @@ -60,6 +64,7 @@ pub struct Target { pub ar: Option, pub ranlib: Option, pub default_linker: Option, + pub default_linker_linux_override: DefaultLinuxLinkerOverride, pub linker: Option, pub split_debuginfo: Option, pub sanitizers: Option, @@ -89,3 +94,60 @@ impl Target { target } } + +/// Overrides the default linker used on a Linux linker. +/// On Linux, the linker is usually invoked through `cc`, therefore this exists as a separate +/// configuration from simply setting `default-linker`, which corresponds to `-Clinker`. +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub enum DefaultLinuxLinkerOverride { + /// Do not apply any override and use the default linker for the given target. + #[default] + Off, + /// Use the self-contained `rust-lld` linker, invoked through `cc`. + /// Corresponds to `-Clinker-features=+lld -Clink-self-contained=+linker`. + SelfContainedLldCc, +} + +impl<'de> Deserialize<'de> for DefaultLinuxLinkerOverride { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = String::deserialize(deserializer)?; + match name.as_str() { + "off" => Ok(Self::Off), + "self-contained-lld-cc" => Ok(Self::SelfContainedLldCc), + other => Err(D::Error::unknown_variant(other, &["off", "self-contained-lld-cc"])), + } + } +} + +/// Set of linker overrides for selected Linux targets. +#[cfg(not(test))] +pub fn default_linux_linker_overrides() -> HashMap { + [("x86_64-unknown-linux-gnu".to_string(), DefaultLinuxLinkerOverride::SelfContainedLldCc)] + .into() +} + +#[cfg(test)] +thread_local! { + static TEST_LINUX_LINKER_OVERRIDES: std::cell::RefCell>> = std::cell::RefCell::new(None); +} + +#[cfg(test)] +pub fn default_linux_linker_overrides() -> HashMap { + TEST_LINUX_LINKER_OVERRIDES.with(|cell| cell.borrow().clone()).unwrap_or_default() +} + +#[cfg(test)] +pub fn with_default_linux_linker_overrides( + targets: HashMap, + f: impl FnOnce() -> R, +) -> R { + TEST_LINUX_LINKER_OVERRIDES.with(|cell| { + let prev = cell.replace(Some(targets)); + let result = f(); + cell.replace(prev); + result + }) +} From e7c9e12ce441766ad62f73d1b354edaa0a4ebc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Sep 2025 18:04:32 +0200 Subject: [PATCH 1313/1900] Update change tracker --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 853fc4e6623e9..ea5fc77c8a4a8 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -566,4 +566,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`compiletest` is now always built with the stage 0 compiler, so `build.compiletest-use-stage0-libtest` has no effect.", }, + ChangeInfo { + change_id: 147157, + severity: ChangeSeverity::Warning, + summary: "`rust.lld = true` no longer automatically causes the `x86_64-unknown-linux-gnu` target to default into using the self-contained LLD linker. This target now uses the LLD linker by default. To opt out, set `target.x86_64-unknown-linux-gnu.default-linker-linux-override = 'off'`.", + }, ]; From b07d88fc725a2aac8a402c7127a17e0d2119e822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 6 Oct 2025 10:43:16 +0200 Subject: [PATCH 1314/1900] Handle external LLVM config --- src/bootstrap/src/core/config/config.rs | 81 +++++++++++++------------ 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0a5c342835028..02dd3f0c05c50 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -954,38 +954,6 @@ impl Config { } } - for (target, linker_override) in default_linux_linker_overrides() { - // If the user overrode the default Linux linker, do not apply bootstrap defaults - if targets_with_user_linker_override.contains(&target) { - continue; - } - let default_linux_linker_override = match linker_override { - DefaultLinuxLinkerOverride::Off => continue, - DefaultLinuxLinkerOverride::SelfContainedLldCc => { - // If we automatically default to the self-contained LLD linker, - // we also need to handle the rust.lld option. - match rust_lld_enabled { - // If LLD was not enabled explicitly, we enable it - None => { - lld_enabled = true; - Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) - } - // If it was enabled already, we don't need to do anything - Some(true) => Some(DefaultLinuxLinkerOverride::SelfContainedLldCc), - // If it was explicitly disabled, we do not apply the - // linker override - Some(false) => None, - } - } - }; - if let Some(linker_override) = default_linux_linker_override { - target_config - .entry(TargetSelection::from_user(&target)) - .or_default() - .default_linker_linux_override = linker_override; - } - } - let llvm_from_ci = parse_download_ci_llvm( &dwn_ctx, &rust_info, @@ -993,6 +961,8 @@ impl Config { llvm_download_ci_llvm, llvm_assertions, ); + let is_host_system_llvm = + is_system_llvm(&target_config, llvm_from_ci, host_target, host_target); if llvm_from_ci { let warn = |option: &str| { @@ -1040,6 +1010,41 @@ impl Config { build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", host_target))); } + for (target, linker_override) in default_linux_linker_overrides() { + // If the user overrode the default Linux linker, do not apply bootstrap defaults + if targets_with_user_linker_override.contains(&target) { + continue; + } + + let default_linux_linker_override = match linker_override { + DefaultLinuxLinkerOverride::Off => continue, + DefaultLinuxLinkerOverride::SelfContainedLldCc => { + // If we automatically default to the self-contained LLD linker, + // we also need to handle the rust.lld option. + match rust_lld_enabled { + // If LLD was not enabled explicitly, we enable it, unless LLVM config has + // been set + None if !is_host_system_llvm => { + lld_enabled = true; + Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) + } + None => None, + // If it was enabled already, we don't need to do anything + Some(true) => Some(DefaultLinuxLinkerOverride::SelfContainedLldCc), + // If it was explicitly disabled, we do not apply the + // linker override + Some(false) => None, + } + } + }; + if let Some(linker_override) = default_linux_linker_override { + target_config + .entry(TargetSelection::from_user(&target)) + .or_default() + .default_linker_linux_override = linker_override; + } + } + let initial_rustfmt = build_rustfmt.or_else(|| maybe_download_rustfmt(&dwn_ctx, &out)); if matches!(bootstrap_override_lld, BootstrapOverrideLld::SelfContained) @@ -1051,7 +1056,7 @@ impl Config { ); } - if lld_enabled && is_system_llvm(&dwn_ctx, &target_config, llvm_from_ci, host_target) { + if lld_enabled && is_host_system_llvm { panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } @@ -1845,8 +1850,7 @@ impl Config { /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - let dwn_ctx = DownloadContext::from(self); - is_system_llvm(dwn_ctx, &self.target_config, self.llvm_from_ci, target) + is_system_llvm(&self.target_config, self.llvm_from_ci, self.host_target, target) } /// Returns `true` if this is our custom, patched, version of LLVM. @@ -2449,16 +2453,15 @@ pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> b /// In particular, we expect llvm sources to be available when this is false. /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. -pub fn is_system_llvm<'a>( - dwn_ctx: impl AsRef>, +pub fn is_system_llvm( target_config: &HashMap, llvm_from_ci: bool, + host_target: TargetSelection, target: TargetSelection, ) -> bool { - let dwn_ctx = dwn_ctx.as_ref(); match target_config.get(&target) { Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = llvm_from_ci && is_host_target(&dwn_ctx.host_target, &target); + let ci_llvm = llvm_from_ci && is_host_target(&host_target, &target); !ci_llvm } // We're building from the in-tree src/llvm-project sources. From a392656b172778f6b61190ef9e1e3cd3496d3d3a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 6 Oct 2025 11:38:23 +0200 Subject: [PATCH 1315/1900] feat: Log flycheck stdout and stderr to files --- .../crates/rust-analyzer/src/command.rs | 26 ++++++++++++++++--- .../crates/rust-analyzer/src/discover.rs | 2 +- .../crates/rust-analyzer/src/flycheck.rs | 17 +++++++++++- .../crates/rust-analyzer/src/test_runner.rs | 7 ++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index d6c80c399ba2e..674e8623b2d07 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -3,13 +3,15 @@ use std::{ ffi::OsString, - fmt, io, + fmt, + io::{self, BufWriter, Write}, marker::PhantomData, path::PathBuf, process::{ChildStderr, ChildStdout, Command, Stdio}, }; use crossbeam_channel::Sender; +use paths::Utf8PathBuf; use process_wrap::std::{StdChildWrapper, StdCommandWrap}; use stdx::process::streaming_output; @@ -40,7 +42,7 @@ impl CargoActor { } impl CargoActor { - fn run(self) -> io::Result<(bool, String)> { + fn run(self, outfile: Option) -> io::Result<(bool, String)> { // We manually read a line at a time, instead of using serde's // stream deserializers, because the deserializer cannot recover // from an error, resulting in it getting stuck, because we try to @@ -50,6 +52,15 @@ impl CargoActor { // simply skip a line if it doesn't parse, which just ignores any // erroneous output. + let mut stdout = outfile.as_ref().and_then(|path| { + _ = std::fs::create_dir_all(path); + Some(BufWriter::new(std::fs::File::create(path.join("stdout")).ok()?)) + }); + let mut stderr = outfile.as_ref().and_then(|path| { + _ = std::fs::create_dir_all(path); + Some(BufWriter::new(std::fs::File::create(path.join("stderr")).ok()?)) + }); + let mut stdout_errors = String::new(); let mut stderr_errors = String::new(); let mut read_at_least_one_stdout_message = false; @@ -67,11 +78,19 @@ impl CargoActor { self.stdout, self.stderr, &mut |line| { + if let Some(stdout) = &mut stdout { + _ = stdout.write_all(line.as_bytes()); + _ = stdout.write_all(b"\n"); + } if process_line(line, &mut stdout_errors) { read_at_least_one_stdout_message = true; } }, &mut |line| { + if let Some(stderr) = &mut stderr { + _ = stderr.write_all(line.as_bytes()); + _ = stderr.write_all(b"\n"); + } if process_line(line, &mut stderr_errors) { read_at_least_one_stderr_message = true; } @@ -130,6 +149,7 @@ impl CommandHandle { mut command: Command, parser: impl CargoParser, sender: Sender, + out_file: Option, ) -> std::io::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); @@ -150,7 +170,7 @@ impl CommandHandle { let actor = CargoActor::::new(parser, sender, stdout, stderr); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, "CommandHandle") - .spawn(move || actor.run()) + .spawn(move || actor.run(out_file)) .expect("failed to spawn thread"); Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 24c433610f1a3..4ec0c075dd5be 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -67,7 +67,7 @@ impl DiscoverCommand { cmd.args(args); Ok(DiscoverHandle { - _handle: CommandHandle::spawn(cmd, DiscoverProjectParser, self.sender.clone())?, + _handle: CommandHandle::spawn(cmd, DiscoverProjectParser, self.sender.clone(), None)?, span: info_span!("discover_command").entered(), }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index cded34be14a28..b545106fe1cfb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -423,7 +423,21 @@ impl FlycheckActor { tracing::debug!(?command, "will restart flycheck"); let (sender, receiver) = unbounded(); - match CommandHandle::spawn(command, CargoCheckParser, sender) { + match CommandHandle::spawn( + command, + CargoCheckParser, + sender, + match &self.config { + FlycheckConfig::CargoCommand { options, .. } => Some( + options + .target_dir + .as_deref() + .unwrap_or("target".as_ref()) + .join(format!("rust-analyzer/flycheck{}", self.id)), + ), + _ => None, + }, + ) { Ok(command_handle) => { tracing::debug!(command = formatted_command, "did restart flycheck"); self.command_handle = Some(command_handle); @@ -622,6 +636,7 @@ impl FlycheckActor { { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } + cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); cmd.arg(command); match scope { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index e7528dbc9396d..0c8658c75df0a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -136,7 +136,12 @@ impl CargoTestHandle { } Ok(Self { - _handle: CommandHandle::spawn(cmd, CargoTestOutputParser::new(&test_target), sender)?, + _handle: CommandHandle::spawn( + cmd, + CargoTestOutputParser::new(&test_target), + sender, + None, + )?, }) } } From d24ee20876dc3438810ddf640f6ceb732bd75ed3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 6 Oct 2025 12:09:57 +0200 Subject: [PATCH 1316/1900] use `fluent_syntax` to parse fluent files --- src/tools/tidy/src/fluent_alphabetical.rs | 41 ++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index b1d1c16b4549c..1fadc523591c7 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -5,6 +5,8 @@ use std::fs::OpenOptions; use std::io::Write; use std::path::Path; +use fluent_syntax::ast::Entry; +use fluent_syntax::parser; use regex::Regex; use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; @@ -24,30 +26,31 @@ fn check_alphabetic( check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) { - let mut matches = message().captures_iter(fluent).peekable(); - while let Some(m) = matches.next() { - let name = m.get(1).unwrap(); - if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - check.error(format!( - "{filename}: message `{}` is already defined in {defined_filename}", - name.as_str(), - )); - } + let Ok(resource) = parser::parse(fluent) else { + panic!("Errors encountered while parsing fluent file `{filename}`"); + }; - all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); + let mut prev: Option<&str> = None; - if let Some(next) = matches.peek() { - let next = next.get(1).unwrap(); - if name.as_str() > next.as_str() { + for entry in &resource.body { + if let Entry::Message(msg) = entry { + let name: &str = msg.id.name; + if let Some(defined_filename) = all_defined_msgs.get(name) { check.error(format!( - "{filename}: message `{}` appears before `{}`, but is alphabetically later than it -run `./x.py test tidy --bless` to sort the file correctly", - name.as_str(), - next.as_str() + "{filename}: message `{name}` is already defined in {defined_filename}", + )); + } else { + all_defined_msgs.insert(name.to_string(), filename.to_owned()); + } + if let Some(prev) = prev + && prev > name + { + check.error(format!( + "{filename}: message `{prev}` appears before `{name}`, but is alphabetically \ +later than it. Run `./x.py test tidy --bless` to sort the file correctly", )); } - } else { - break; + prev = Some(name); } } } From 831cdf3144f23871e9923ddeb9a36d0192e7244a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 6 Oct 2025 12:11:56 +0200 Subject: [PATCH 1317/1900] Fail if no fluent messages were found --- src/tools/tidy/src/fluent_alphabetical.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 1fadc523591c7..769f92d04f0af 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -118,5 +118,7 @@ pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { }, ); + assert!(!all_defined_msgs.is_empty()); + crate::fluent_used::check(path, all_defined_msgs, diag_ctx); } From f4b4fb9b7f1af29c3b9f6e25229a2514d5db35c7 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Mon, 6 Oct 2025 09:35:46 -0300 Subject: [PATCH 1318/1900] Fixed some lint deprecated versions --- clippy_lints/src/deprecated_lints.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 80b74f50223a4..2147f72889093 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -18,11 +18,11 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.54.0"] ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), @@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), From 04c2724866e1c7ea1c4195475ba3539e01a131da Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Sat, 13 Sep 2025 08:51:04 -0400 Subject: [PATCH 1319/1900] constify basic Clone impls --- library/core/src/clone.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 7f2a40f753fa6..06d2c93cc698f 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -575,7 +575,8 @@ mod impls { ($($t:ty)*) => { $( #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for $t { + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + impl const Clone for $t { #[inline(always)] fn clone(&self) -> Self { *self @@ -593,7 +594,8 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Clone for ! { + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + impl const Clone for ! { #[inline] fn clone(&self) -> Self { *self @@ -601,7 +603,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *const T { + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + impl const Clone for *const T { #[inline(always)] fn clone(&self) -> Self { *self @@ -609,7 +612,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *mut T { + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + impl const Clone for *mut T { #[inline(always)] fn clone(&self) -> Self { *self @@ -618,7 +622,8 @@ mod impls { /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for &T { + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + impl const Clone for &T { #[inline(always)] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { From 0ba022a858c200bc2e7ea481995ee194980c5711 Mon Sep 17 00:00:00 2001 From: Zihan Date: Sat, 4 Oct 2025 10:51:21 -0400 Subject: [PATCH 1320/1900] `legacy_numeric_constants`: add ctxt check for internal macro changelog: none Signed-off-by: Zihan --- clippy_lints/src/legacy_numeric_constants.rs | 30 +++++-------------- .../ui/legacy_numeric_constants_unfixable.rs | 11 +++++++ 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 42c636505c015..8a5d97294d2bd 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -113,35 +113,18 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { // since this would only require removing a `use` import (which is already linted). && !is_numeric_const_path_canonical(path, [*mod_name, *name]) { - ( - vec![(expr.span, format!("{mod_name}::{name}"))], - "usage of a legacy numeric constant", - ) + (format!("{mod_name}::{name}"), "usage of a legacy numeric constant") // `::xxx_value` check } else if let ExprKind::Call(func, []) = &expr.kind && let ExprKind::Path(qpath) = &func.kind && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) + && let Some(mod_name) = ty.span.get_source_text(cx) + && ty.span.eq_ctxt(last_segment.ident.span) { - let mut sugg = vec![ - // Replace the function name up to the end by the constant name - ( - last_segment.ident.span.to(expr.span.shrink_to_hi()), - last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(), - ), - ]; - let before_span = expr.span.shrink_to_lo().until(ty.span); - if !before_span.is_empty() { - // Remove everything before the type name - sugg.push((before_span, String::new())); - } - // Use `::` between the type name and the constant - let between_span = ty.span.shrink_to_hi().until(last_segment.ident.span); - if !between_span.check_source_text(cx, |s| s == "::") { - sugg.push((between_span, String::from("::"))); - } - (sugg, "usage of a legacy numeric method") + let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); + (format!("{mod_name}::{name}"), "usage of a legacy numeric method") } else { return; }; @@ -151,7 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && !is_from_proc_macro(cx, expr) { span_lint_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.span, msg, |diag| { - diag.multipart_suggestion_verbose( + diag.span_suggestion_verbose( + expr.span, "use the associated constant instead", sugg, Applicability::MaybeIncorrect, diff --git a/tests/ui/legacy_numeric_constants_unfixable.rs b/tests/ui/legacy_numeric_constants_unfixable.rs index 9bf0f7f355aee..084d97fdc0f74 100644 --- a/tests/ui/legacy_numeric_constants_unfixable.rs +++ b/tests/ui/legacy_numeric_constants_unfixable.rs @@ -77,3 +77,14 @@ fn msrv_juust_right() { use std::u32::MAX; //~^ ERROR: importing a legacy numeric constant } + +macro_rules! foo { + ($a: ty) => { + let _ = <$a>::max_value(); + let _ = (<$a>::max_value)(); + }; +} + +fn issue15805() { + foo!(u8); +} From 696b6ac14dd9c3aaae12c000cf5fa1bd0fec8c34 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 16 Aug 2025 22:08:41 +0800 Subject: [PATCH 1321/1900] use declarative macro for `#[derive(TryFromU32)]` --- .../src/coverageinfo/mapgen.rs | 3 +- compiler/rustc_codegen_llvm/src/lib.rs | 4 ++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +- compiler/rustc_codegen_llvm/src/macros.rs | 22 ++++++++ compiler/rustc_macros/src/lib.rs | 9 --- compiler/rustc_macros/src/try_from.rs | 55 ------------------- tests/ui-fulldeps/try-from-u32/errors.rs | 24 -------- tests/ui-fulldeps/try-from-u32/errors.stderr | 32 ----------- tests/ui-fulldeps/try-from-u32/hygiene.rs | 32 ----------- tests/ui-fulldeps/try-from-u32/values.rs | 36 ------------ 10 files changed, 28 insertions(+), 192 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/macros.rs delete mode 100644 compiler/rustc_macros/src/try_from.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/errors.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/errors.stderr delete mode 100644 tests/ui-fulldeps/try-from-u32/hygiene.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/values.rs diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index d1cb95507d91c..7e873347c82b0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -6,7 +6,6 @@ use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; -use rustc_macros::TryFromU32; use rustc_middle::ty::TyCtxt; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; @@ -16,7 +15,7 @@ use tracing::debug; use crate::common::CodegenCx; use crate::coverageinfo::llvm_cov; use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; -use crate::llvm; +use crate::{TryFromU32, llvm}; mod covfun; mod spans; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 2405a25c7020b..807049f08d367 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -14,6 +14,7 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] +#![feature(macro_derive)] #![feature(rustdoc_internals)] #![feature(slice_as_array)] #![feature(try_blocks)] @@ -65,6 +66,7 @@ mod errors; mod intrinsic; mod llvm; mod llvm_util; +mod macros; mod mono_item; mod type_; mod type_of; @@ -74,6 +76,8 @@ mod value; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +pub(crate) use macros::TryFromU32; + #[derive(Clone)] pub struct LlvmCodegenBackend(()); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ccbc999dad1f0..c4b5cf413a725 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -19,7 +19,6 @@ use std::ptr; use bitflags::bitflags; use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; -use rustc_macros::TryFromU32; use super::RustString; use super::debuginfo::{ @@ -27,8 +26,8 @@ use super::debuginfo::{ DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; -use crate::llvm; use crate::llvm::MetadataKindId; +use crate::{TryFromU32, llvm}; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. diff --git a/compiler/rustc_codegen_llvm/src/macros.rs b/compiler/rustc_codegen_llvm/src/macros.rs new file mode 100644 index 0000000000000..fddc428ca273b --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/macros.rs @@ -0,0 +1,22 @@ +macro_rules! TryFromU32 { + derive() ( + $(#[$meta:meta])* + $vis:vis enum $Type:ident { + $( + $(#[$varmeta:meta])* + $Variant:ident $(= $discr:expr)? + ),* $(,)? + } + ) => { + impl ::core::convert::TryFrom for $Type { + type Error = u32; + #[allow(deprecated)] // Don't warn about deprecated variants. + fn try_from(value: u32) -> ::core::result::Result<$Type, Self::Error> { + $( if value == const { $Type::$Variant as u32 } { return Ok($Type::$Variant) } )* + Err(value) + } + } + } +} + +pub(crate) use TryFromU32; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 803b3621c887e..a6f53d92e1006 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -18,7 +18,6 @@ mod print_attribute; mod query; mod serialize; mod symbols; -mod try_from; mod type_foldable; mod type_visitable; mod visitable; @@ -176,14 +175,6 @@ decl_derive!( applicability)] => diagnostics::subdiagnostic_derive ); -decl_derive! { - [TryFromU32] => - /// Derives `TryFrom` for the annotated `enum`, which must have no fields. - /// Each variant maps to the value it would produce under an `as u32` cast. - /// - /// The error type is `u32`. - try_from::try_from_u32 -} decl_derive! { [PrintAttribute] => /// Derives `PrintAttribute` for `AttributeKind`. diff --git a/compiler/rustc_macros/src/try_from.rs b/compiler/rustc_macros/src/try_from.rs deleted file mode 100644 index 9338c1c2b3361..0000000000000 --- a/compiler/rustc_macros/src/try_from.rs +++ /dev/null @@ -1,55 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; -use syn::Data; -use syn::spanned::Spanned; -use synstructure::Structure; - -pub(crate) fn try_from_u32(s: Structure<'_>) -> TokenStream { - let span_error = |span, message: &str| { - quote_spanned! { span => const _: () = ::core::compile_error!(#message); } - }; - - // Must be applied to an enum type. - if let Some(span) = match &s.ast().data { - Data::Enum(_) => None, - Data::Struct(s) => Some(s.struct_token.span()), - Data::Union(u) => Some(u.union_token.span()), - } { - return span_error(span, "type is not an enum (TryFromU32)"); - } - - // The enum's variants must not have fields. - let variant_field_errors = s - .variants() - .iter() - .filter_map(|v| v.ast().fields.iter().map(|f| f.span()).next()) - .map(|span| span_error(span, "enum variant cannot have fields (TryFromU32)")) - .collect::(); - if !variant_field_errors.is_empty() { - return variant_field_errors; - } - - let ctor = s - .variants() - .iter() - .map(|v| v.construct(|_, _| -> TokenStream { unreachable!() })) - .collect::>(); - // FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here? - #[allow(keyword_idents_2024)] - s.gen_impl(quote! { - // The surrounding code might have shadowed these identifiers. - use ::core::convert::TryFrom; - use ::core::primitive::u32; - use ::core::result::Result::{self, Ok, Err}; - - gen impl TryFrom for @Self { - type Error = u32; - - #[allow(deprecated)] // Don't warn about deprecated variants. - fn try_from(value: u32) -> Result { - #( if value == const { #ctor as u32 } { return Ok(#ctor) } )* - Err(value) - } - } - }) -} diff --git a/tests/ui-fulldeps/try-from-u32/errors.rs b/tests/ui-fulldeps/try-from-u32/errors.rs deleted file mode 100644 index a25069c0a53cb..0000000000000 --- a/tests/ui-fulldeps/try-from-u32/errors.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -//@ edition: 2021 - -// Checks the error messages produced by `#[derive(TryFromU32)]`. - -extern crate rustc_macros; - -use rustc_macros::TryFromU32; - -#[derive(TryFromU32)] -struct MyStruct {} //~ ERROR type is not an enum - -#[derive(TryFromU32)] -enum NonTrivial { - A, - B(), - C {}, - D(bool), //~ ERROR enum variant cannot have fields - E(bool, bool), //~ ERROR enum variant cannot have fields - F { x: bool }, //~ ERROR enum variant cannot have fields - G { x: bool, y: bool }, //~ ERROR enum variant cannot have fields -} - -fn main() {} diff --git a/tests/ui-fulldeps/try-from-u32/errors.stderr b/tests/ui-fulldeps/try-from-u32/errors.stderr deleted file mode 100644 index d20567061d7bb..0000000000000 --- a/tests/ui-fulldeps/try-from-u32/errors.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: type is not an enum (TryFromU32) - --> $DIR/errors.rs:11:1 - | -LL | struct MyStruct {} - | ^^^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:18:7 - | -LL | D(bool), - | ^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:19:7 - | -LL | E(bool, bool), - | ^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:20:9 - | -LL | F { x: bool }, - | ^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:21:9 - | -LL | G { x: bool, y: bool }, - | ^ - -error: aborting due to 5 previous errors - diff --git a/tests/ui-fulldeps/try-from-u32/hygiene.rs b/tests/ui-fulldeps/try-from-u32/hygiene.rs deleted file mode 100644 index e0655a64a64db..0000000000000 --- a/tests/ui-fulldeps/try-from-u32/hygiene.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![feature(rustc_private)] -//@ edition: 2021 -//@ check-pass - -// Checks that the derive macro still works even if the surrounding code has -// shadowed the relevant library types. - -extern crate rustc_macros; - -mod submod { - use rustc_macros::TryFromU32; - - struct Result; - trait TryFrom {} - #[allow(non_camel_case_types)] - struct u32; - struct Ok; - struct Err; - mod core {} - mod std {} - - #[derive(TryFromU32)] - pub(crate) enum MyEnum { - Zero, - One, - } -} - -fn main() { - use submod::MyEnum; - let _: Result = MyEnum::try_from(1u32); -} diff --git a/tests/ui-fulldeps/try-from-u32/values.rs b/tests/ui-fulldeps/try-from-u32/values.rs deleted file mode 100644 index 180a8f2beb75b..0000000000000 --- a/tests/ui-fulldeps/try-from-u32/values.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(assert_matches)] -#![feature(rustc_private)] -//@ edition: 2021 -//@ run-pass - -// Checks the values accepted by the `TryFrom` impl produced by `#[derive(TryFromU32)]`. - -extern crate rustc_macros; - -use core::assert_matches::assert_matches; -use rustc_macros::TryFromU32; - -#[derive(TryFromU32, Debug, PartialEq)] -#[repr(u32)] -enum Repr { - Zero, - One(), - Seven = 7, -} - -#[derive(TryFromU32, Debug)] -enum NoRepr { - Zero, - One, -} - -fn main() { - assert_eq!(Repr::try_from(0u32), Ok(Repr::Zero)); - assert_eq!(Repr::try_from(1u32), Ok(Repr::One())); - assert_eq!(Repr::try_from(2u32), Err(2)); - assert_eq!(Repr::try_from(7u32), Ok(Repr::Seven)); - - assert_matches!(NoRepr::try_from(0u32), Ok(NoRepr::Zero)); - assert_matches!(NoRepr::try_from(1u32), Ok(NoRepr::One)); - assert_matches!(NoRepr::try_from(2u32), Err(2)); -} From 085ddaaa97bf55914a33938462d133dd597c8f0b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 17:26:17 +0200 Subject: [PATCH 1322/1900] Bump nightly version -> 2025-10-06 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2c66fdc73f539..1f678a6a29f08 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-18 +nightly-2025-10-06 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c102de448200..e936f5dc3b7a5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-18" +channel = "nightly-2025-10-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 843f4d848809e1e3593b2c743232bd0bfe7d5db8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 17:49:36 +0200 Subject: [PATCH 1323/1900] Merge commit '2dc84cb744ad19d187871afb0385a616d80c209d' into clippy-subtree-update --- .github/workflows/clippy_mq.yml | 2 +- .github/workflows/lintcheck.yml | 2 +- .github/workflows/lintcheck_summary.yml | 4 +- CHANGELOG.md | 4 +- Cargo.toml | 7 +- book/src/development/trait_checking.md | 11 +- book/src/lint_configuration.md | 5 +- clippy_config/src/conf.rs | 5 +- clippy_lints/Cargo.toml | 7 +- clippy_lints/src/assertions_on_constants.rs | 103 ++-- .../src/cargo/lint_groups_priority.rs | 186 +++---- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collapsible_if.rs | 78 ++- clippy_lints/src/declared_lints.rs | 15 +- .../src/default_constructed_unit_structs.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 8 +- clippy_lints/src/dereference.rs | 10 +- .../src/derive/derive_ord_xor_partial_ord.rs | 7 +- .../derive/derive_partial_eq_without_eq.rs | 13 +- .../src/derive/derived_hash_with_manual_eq.rs | 10 +- .../src/derive/expl_impl_clone_on_copy.rs | 20 +- clippy_lints/src/derive/mod.rs | 16 +- .../src/derive/unsafe_derive_deserialize.rs | 12 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/double_parens.rs | 73 ++- clippy_lints/src/enum_clike.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 21 +- clippy_lints/src/if_not_else.rs | 12 +- clippy_lints/src/if_then_some_else_none.rs | 1 + .../branches_sharing_code.rs} | 283 +--------- clippy_lints/src/ifs/if_same_then_else.rs | 29 + clippy_lints/src/ifs/ifs_same_cond.rs | 46 ++ clippy_lints/src/ifs/mod.rs | 182 ++++++ .../src/ifs/same_functions_in_if_cond.rs | 31 ++ clippy_lints/src/implicit_return.rs | 4 +- clippy_lints/src/implicit_saturating_add.rs | 5 +- clippy_lints/src/instant_subtraction.rs | 151 ----- .../src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/item_name_repetitions.rs | 180 +++--- clippy_lints/src/lib.rs | 20 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 9 +- clippy_lints/src/manual_float_methods.rs | 29 +- clippy_lints/src/manual_rem_euclid.rs | 20 +- clippy_lints/src/manual_rotate.rs | 2 +- clippy_lints/src/manual_strip.rs | 24 +- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 54 +- clippy_lints/src/matches/overlapping_arms.rs | 6 +- .../src/matches/redundant_pattern_match.rs | 147 +++-- clippy_lints/src/matches/single_match.rs | 27 +- clippy_lints/src/mem_replace.rs | 13 +- clippy_lints/src/methods/filter_next.rs | 91 +-- .../src/methods/inefficient_to_string.rs | 4 + clippy_lints/src/methods/ip_constant.rs | 4 +- .../src/methods/is_digit_ascii_radix.rs | 2 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iter_skip_zero.rs | 16 +- clippy_lints/src/methods/lib.rs | 70 +++ clippy_lints/src/methods/map_identity.rs | 80 ++- clippy_lints/src/methods/mod.rs | 348 ++---------- clippy_lints/src/methods/new_ret_no_self.rs | 46 ++ clippy_lints/src/methods/or_fun_call.rs | 370 ++++++------ .../src/methods/range_zip_with_len.rs | 96 +++- clippy_lints/src/methods/repeat_once.rs | 2 +- .../src/methods/should_implement_trait.rs | 156 ++++++ .../src/methods/single_char_add_str.rs | 80 ++- .../src/methods/single_char_insert_string.rs | 67 --- .../src/methods/single_char_push_string.rs | 65 --- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_min_or_max.rs | 7 +- .../src/methods/wrong_self_convention.rs | 38 +- clippy_lints/src/minmax.rs | 20 +- clippy_lints/src/module_style.rs | 155 +++--- clippy_lints/src/mut_mut.rs | 135 ++++- clippy_lints/src/needless_pass_by_value.rs | 63 ++- clippy_lints/src/new_without_default.rs | 190 ++++--- clippy_lints/src/non_canonical_impls.rs | 291 +++++----- clippy_lints/src/nonstandard_macro_braces.rs | 91 ++- clippy_lints/src/only_used_in_recursion.rs | 153 +++-- .../src/operators/arithmetic_side_effects.rs | 4 +- .../src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/duration_subsec.rs | 2 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 7 +- clippy_lints/src/operators/identity_op.rs | 32 +- .../src/operators/manual_is_multiple_of.rs | 9 +- clippy_lints/src/operators/mod.rs | 2 +- .../src/operators/modulo_arithmetic.rs | 6 +- .../src/operators/numeric_arithmetic.rs | 2 +- clippy_lints/src/question_mark.rs | 10 +- clippy_lints/src/ranges.rs | 6 +- clippy_lints/src/time_subtraction.rs | 216 +++++++ clippy_lints/src/unit_types/let_unit_value.rs | 81 ++- ...reference.rs => unnecessary_mut_passed.rs} | 33 +- clippy_lints/src/unnecessary_semicolon.rs | 3 +- clippy_lints/src/unwrap.rs | 50 +- clippy_lints/src/zero_div_zero.rs | 5 +- clippy_lints/src/zero_repeat_side_effects.rs | 117 ++-- clippy_utils/README.md | 2 +- clippy_utils/src/consts.rs | 526 ++++++++++++------ clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +- clippy_utils/src/lib.rs | 28 +- clippy_utils/src/msrvs.rs | 9 +- clippy_utils/src/paths.rs | 18 +- clippy_utils/src/ptr.rs | 52 -- clippy_utils/src/sym.rs | 7 + clippy_utils/src/ty/mod.rs | 6 +- lintcheck/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/compile-test.rs | 22 +- .../duplicated_mod_names_14697/Cargo.stderr | 11 + .../duplicated_mod_names_14697/Cargo.toml | 11 + .../duplicated_mod_names_14697/src/foo.rs | 1 + .../duplicated_mod_names_14697/src/foo/bar.rs | 1 + .../duplicated_mod_names_14697/src/lib.rs | 4 + .../src/other/foo/mod.rs | 1 + .../src/other/mod.rs | 1 + .../module_style/fail_mod/Cargo.stderr | 16 +- .../Cargo.toml | 10 + .../foo/bar/Cargo.toml | 5 + .../foo/bar/src/foo.rs | 1 + .../foo/bar/src/lib.rs | 1 + .../src/lib.rs | 1 + .../with_path_attr_mod/Cargo.toml | 10 + .../with_path_attr_mod/src/bar/mod.rs | 1 + .../with_path_attr_mod/src/lib.rs | 4 + .../with_path_attr_no_mod/Cargo.toml | 10 + .../with_path_attr_no_mod/src/bar.rs | 1 + .../with_path_attr_no_mod/src/foo.rs | 2 + .../with_path_attr_no_mod/src/lib.rs | 3 + .../collapsible_if/collapsible_else_if.fixed | 19 +- .../collapsible_if/collapsible_else_if.rs | 19 +- .../conf_deprecated_key.rs | 4 +- .../conf_deprecated_key.stderr | 2 +- .../index_refutable_slice.fixed | 1 - .../index_refutable_slice.rs | 1 - .../index_refutable_slice.stderr | 4 +- .../conf_nonstandard_macro_braces.fixed | 8 + .../conf_nonstandard_macro_braces.rs | 8 + .../conf_nonstandard_macro_braces.stderr | 8 +- tests/ui/assertions_on_constants.rs | 30 +- tests/ui/assertions_on_constants.stderr | 92 ++- tests/ui/auxiliary/option_helpers.rs | 4 + tests/ui/bind_instead_of_map.fixed | 1 - tests/ui/bind_instead_of_map.rs | 1 - tests/ui/bind_instead_of_map.stderr | 6 +- .../shared_at_top_and_bottom.rs | 5 +- .../shared_at_top_and_bottom.stderr | 24 +- tests/ui/cast_abs_to_unsigned.fixed | 4 +- tests/ui/cast_abs_to_unsigned.rs | 4 +- tests/ui/checked_unwrap/if_let_chains.rs | 24 + tests/ui/checked_unwrap/if_let_chains.stderr | 29 + tests/ui/clone_on_copy.fixed | 65 ++- tests/ui/clone_on_copy.rs | 65 ++- tests/ui/clone_on_copy.stderr | 26 +- tests/ui/clone_on_ref_ptr.fixed | 51 ++ tests/ui/clone_on_ref_ptr.rs | 51 ++ tests/ui/clone_on_ref_ptr.stderr | 41 ++ tests/ui/collapsible_else_if.fixed | 27 + tests/ui/collapsible_else_if.rs | 27 + tests/ui/collapsible_else_if.stderr | 2 +- tests/ui/collapsible_else_if_unfixable.rs | 22 + tests/ui/collapsible_else_if_unfixable.stderr | 17 + tests/ui/collapsible_if.fixed | 18 + tests/ui/collapsible_if.rs | 18 + tests/ui/collapsible_if.stderr | 2 +- tests/ui/collapsible_if_unfixable.rs | 20 + tests/ui/collapsible_if_unfixable.stderr | 17 + tests/ui/const_comparisons.rs | 14 +- tests/ui/const_comparisons.stderr | 62 +-- tests/ui/const_is_empty.rs | 50 +- tests/ui/const_is_empty.stderr | 108 +--- tests/ui/crashes/ice-4775.rs | 2 +- tests/ui/default_trait_access.fixed | 2 +- tests/ui/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 +- tests/ui/derive.rs | 13 + tests/ui/derive.stderr | 10 +- tests/ui/derive_ord_xor_partial_ord.rs | 15 + tests/ui/derived_hash_with_manual_eq.rs | 16 + tests/ui/double_parens.fixed | 99 ++++ tests/ui/double_parens.rs | 46 +- tests/ui/double_parens.stderr | 65 ++- tests/ui/duration_subsec.fixed | 3 +- tests/ui/duration_subsec.rs | 1 - tests/ui/duration_subsec.stderr | 8 +- tests/ui/explicit_counter_loop.rs | 24 +- tests/ui/explicit_deref_methods.fixed | 15 + tests/ui/explicit_deref_methods.rs | 15 + tests/ui/explicit_deref_methods.stderr | 26 +- tests/ui/explicit_write.fixed | 5 +- tests/ui/explicit_write.rs | 5 +- tests/ui/explicit_write.stderr | 34 +- tests/ui/fallible_impl_from.rs | 3 +- tests/ui/fallible_impl_from.stderr | 20 +- tests/ui/floating_point_log.fixed | 6 +- tests/ui/floating_point_log.rs | 2 - tests/ui/floating_point_log.stderr | 60 +- tests/ui/if_then_some_else_none.fixed | 12 + tests/ui/if_then_some_else_none.rs | 12 + .../if_let_slice_binding.fixed | 14 +- .../if_let_slice_binding.rs | 14 +- tests/ui/inefficient_to_string.fixed | 8 + tests/ui/inefficient_to_string.rs | 8 + tests/ui/inefficient_to_string.stderr | 12 +- tests/ui/infinite_iter.rs | 4 +- tests/ui/infinite_iter.stderr | 4 +- tests/ui/ip_constant.fixed | 45 +- tests/ui/ip_constant.rs | 9 - tests/ui/ip_constant.stderr | 146 +---- tests/ui/issue_2356.fixed | 3 +- tests/ui/issue_2356.rs | 3 +- tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 3 +- tests/ui/issue_4266.stderr | 6 +- tests/ui/let_unit.fixed | 52 +- tests/ui/let_unit.rs | 51 +- tests/ui/let_unit.stderr | 98 +++- tests/ui/manual_float_methods.rs | 15 +- tests/ui/manual_float_methods.stderr | 40 +- tests/ui/manual_instant_elapsed.fixed | 2 +- tests/ui/manual_instant_elapsed.rs | 2 +- tests/ui/manual_strip.stderr | 3 - tests/ui/match_single_binding.fixed | 37 +- tests/ui/match_single_binding.rs | 37 +- tests/ui/match_single_binding.stderr | 130 ++--- tests/ui/match_single_binding2.fixed | 3 +- tests/ui/match_single_binding2.rs | 3 +- tests/ui/match_single_binding2.stderr | 12 +- tests/ui/mem_replace.fixed | 6 + tests/ui/mem_replace.rs | 6 + tests/ui/mem_replace.stderr | 8 +- tests/ui/methods.rs | 20 + tests/ui/methods.stderr | 13 +- tests/ui/methods_fixable.fixed | 14 + tests/ui/methods_fixable.rs | 14 + tests/ui/methods_fixable.stderr | 14 +- tests/ui/module_inception.rs | 9 + tests/ui/mut_mut.fixed | 92 +++ tests/ui/mut_mut.rs | 11 +- tests/ui/mut_mut.stderr | 58 +- tests/ui/mut_mut_unfixable.rs | 42 ++ tests/ui/mut_mut_unfixable.stderr | 41 ++ tests/ui/mut_reference.stderr | 77 --- tests/ui/new_without_default.fixed | 89 ++- tests/ui/new_without_default.rs | 61 +- tests/ui/new_without_default.stderr | 99 +++- tests/ui/only_used_in_recursion.rs | 3 +- tests/ui/only_used_in_recursion.stderr | 70 +-- tests/ui/or_fun_call.fixed | 16 +- tests/ui/or_fun_call.rs | 16 +- tests/ui/or_fun_call.stderr | 116 ++-- tests/ui/question_mark.fixed | 25 + tests/ui/question_mark.rs | 32 ++ tests/ui/question_mark.stderr | 22 +- tests/ui/range.fixed | 16 +- tests/ui/range.rs | 16 +- tests/ui/range.stderr | 29 +- tests/ui/range_unfixable.rs | 14 + tests/ui/range_unfixable.stderr | 12 + tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 154 ++--- tests/ui/repeat_once.fixed | 3 +- tests/ui/repeat_once.rs | 1 - tests/ui/repeat_once.stderr | 14 +- ...tderr => method_list_1.edition2015.stderr} | 30 +- .../method_list_1.edition2021.stderr | 184 ++++++ tests/ui/should_impl_trait/method_list_1.rs | 3 + .../method_list_2.edition2015.stderr | 172 ++++++ .../method_list_2.edition2021.stderr | 184 ++++++ tests/ui/should_impl_trait/method_list_2.rs | 5 +- tests/ui/single_match.fixed | 5 +- tests/ui/single_match.rs | 5 +- tests/ui/single_match.stderr | 20 +- tests/ui/unchecked_duration_subtraction.fixed | 20 - tests/ui/unchecked_duration_subtraction.rs | 20 - .../ui/unchecked_duration_subtraction.stderr | 29 - tests/ui/unchecked_time_subtraction.fixed | 37 ++ tests/ui/unchecked_time_subtraction.rs | 37 ++ tests/ui/unchecked_time_subtraction.stderr | 53 ++ .../unchecked_time_subtraction_unfixable.rs | 22 + ...nchecked_time_subtraction_unfixable.stderr | 29 + tests/ui/unnecessary_clone.rs | 111 ---- tests/ui/unnecessary_clone.stderr | 62 --- ...nce.fixed => unnecessary_mut_passed.fixed} | 20 + ...reference.rs => unnecessary_mut_passed.rs} | 20 + tests/ui/unnecessary_mut_passed.stderr | 220 ++++++++ ...micolon_feature_stmt_expr_attributes.fixed | 13 + ..._semicolon_feature_stmt_expr_attributes.rs | 13 + ...icolon_feature_stmt_expr_attributes.stderr | 11 + tests/ui/zero_repeat_side_effects.fixed | 28 +- tests/ui/zero_repeat_side_effects.rs | 28 +- tests/ui/zero_repeat_side_effects.stderr | 133 ++++- triagebot.toml | 1 - 296 files changed, 6949 insertions(+), 4035 deletions(-) rename clippy_lints/src/{copies.rs => ifs/branches_sharing_code.rs} (63%) create mode 100644 clippy_lints/src/ifs/if_same_then_else.rs create mode 100644 clippy_lints/src/ifs/ifs_same_cond.rs create mode 100644 clippy_lints/src/ifs/mod.rs create mode 100644 clippy_lints/src/ifs/same_functions_in_if_cond.rs delete mode 100644 clippy_lints/src/instant_subtraction.rs create mode 100644 clippy_lints/src/methods/lib.rs create mode 100644 clippy_lints/src/methods/new_ret_no_self.rs create mode 100644 clippy_lints/src/methods/should_implement_trait.rs delete mode 100644 clippy_lints/src/methods/single_char_insert_string.rs delete mode 100644 clippy_lints/src/methods/single_char_push_string.rs create mode 100644 clippy_lints/src/time_subtraction.rs rename clippy_lints/src/{mut_reference.rs => unnecessary_mut_passed.rs} (72%) delete mode 100644 clippy_utils/src/ptr.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.stderr create mode 100644 tests/ui/clone_on_ref_ptr.fixed create mode 100644 tests/ui/clone_on_ref_ptr.rs create mode 100644 tests/ui/clone_on_ref_ptr.stderr create mode 100644 tests/ui/collapsible_else_if_unfixable.rs create mode 100644 tests/ui/collapsible_else_if_unfixable.stderr create mode 100644 tests/ui/collapsible_if_unfixable.rs create mode 100644 tests/ui/collapsible_if_unfixable.stderr create mode 100644 tests/ui/double_parens.fixed create mode 100644 tests/ui/mut_mut.fixed create mode 100644 tests/ui/mut_mut_unfixable.rs create mode 100644 tests/ui/mut_mut_unfixable.stderr delete mode 100644 tests/ui/mut_reference.stderr create mode 100644 tests/ui/range_unfixable.rs create mode 100644 tests/ui/range_unfixable.stderr rename tests/ui/should_impl_trait/{method_list_1.stderr => method_list_1.edition2015.stderr} (86%) create mode 100644 tests/ui/should_impl_trait/method_list_1.edition2021.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2015.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2021.stderr delete mode 100644 tests/ui/unchecked_duration_subtraction.fixed delete mode 100644 tests/ui/unchecked_duration_subtraction.rs delete mode 100644 tests/ui/unchecked_duration_subtraction.stderr create mode 100644 tests/ui/unchecked_time_subtraction.fixed create mode 100644 tests/ui/unchecked_time_subtraction.rs create mode 100644 tests/ui/unchecked_time_subtraction.stderr create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.rs create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.stderr delete mode 100644 tests/ui/unnecessary_clone.rs delete mode 100644 tests/ui/unnecessary_clone.stderr rename tests/ui/{mut_reference.fixed => unnecessary_mut_passed.fixed} (87%) rename tests/ui/{mut_reference.rs => unnecessary_mut_passed.rs} (87%) create mode 100644 tests/ui/unnecessary_mut_passed.stderr create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index 0bcb713593593..9d099137449ef 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -181,7 +181,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: binaries path: target/debug diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 390d6a0f74758..45fd10ae76149 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -126,7 +126,7 @@ jobs: fail-on-cache-miss: true - name: Download JSON - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 - name: Store PR number run: echo ${{ github.event.pull_request.number }} > pr.txt diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml index 52f52e155a07b..6768cd65701ac 100644 --- a/.github/workflows/lintcheck_summary.yml +++ b/.github/workflows/lintcheck_summary.yml @@ -27,7 +27,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: summary path: untrusted @@ -35,7 +35,7 @@ jobs: github-token: ${{ github.token }} - name: Format comment - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require("fs"); diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f26b9470e826..30781d3d33fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Current stable, released 2025-09-18 Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see [#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. -## New Lints +### New Lints * Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) * Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) @@ -6598,6 +6598,7 @@ Released 2018-09-13 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files +[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block @@ -6703,6 +6704,7 @@ Released 2018-09-13 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unbuffered_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#unbuffered_bytes [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction +[`unchecked_time_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction [`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops diff --git a/Cargo.toml b/Cargo.toml index e06383499893a..bedcc300f8567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,13 +38,18 @@ ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" -toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } +[dev-dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index 6d01496eebe07..c6f6f6bd2f99e 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -24,12 +24,11 @@ use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]) - }); - if implements_iterator { - // [...] - } + let implements_iterator = (cx.tcx.get_diagnostic_item(sym::Iterator)) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])); + if implements_iterator { + // [...] + } } } diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1d..b2ba19631f133 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -845,6 +845,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [`inefficient_to_string`](https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string) * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) @@ -883,6 +884,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) +* [`or_fun_call`](https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -894,9 +896,10 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) -* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [`unchecked_time_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [`unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad82..9ad434604dfcf 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -741,6 +741,7 @@ define_Conf! { from_over_into, if_then_some_else_none, index_refutable_slice, + inefficient_to_string, io_other_error, iter_kv_map, legacy_numeric_constants, @@ -779,6 +780,7 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, + or_fun_call, ptr_as_ptr, question_mark, redundant_field_names, @@ -790,9 +792,10 @@ define_Conf! { transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, - unchecked_duration_subtraction, + unchecked_time_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, + unnecessary_unwrap, unnested_or_patterns, unused_trait_names, use_self, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 51e59ae205076..42486e182ee34 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,12 +18,17 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" url = "2.2" +[dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [dev-dependencies] walkdir = "2.3" diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b6684825835a9..2586c89bc8680 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,10 +1,13 @@ +use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::{is_inside_always_const_context, msrvs}; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::sym; declare_clippy_lint! { @@ -28,56 +31,60 @@ declare_clippy_lint! { "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`" } -declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +pub struct AssertionsOnConstants { + msrv: Msrv, +} +impl AssertionsOnConstants { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { - return; - }; - let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { - Some(sym::debug_assert_macro) => true, - Some(sym::assert_macro) => false, - _ => return, - }; - let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { - return; - }; - let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { - return; - }; + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, + } + && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) + && let Some((Constant::Bool(assert_val), const_src)) = + ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) + && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) + && (const_src.is_local() || !in_const_context) + && !(is_debug && as_bool_lit(condition) == Some(false)) + { + let (msg, help) = if !const_src.is_local() { + let help = if self.msrv.meets(cx, msrvs::CONST_BLOCKS) { + "consider moving this into a const block: `const { assert!(..) }`" + } else if self.msrv.meets(cx, msrvs::CONST_PANIC) { + "consider moving this to an anonymous constant: `const _: () = { assert!(..); }`" + } else { + return; + }; + ("this assertion has a constant value", help) + } else if assert_val { + ("this assertion is always `true`", "remove the assertion") + } else { + ( + "this assertion is always `false`", + "replace this with `panic!()` or `unreachable!()`", + ) + }; - match condition.kind { - ExprKind::Path(..) | ExprKind::Lit(_) => {}, - _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return, - _ => {}, + span_lint_and_help(cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, msg, None, help); } + } +} - if val { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!( - "`{}!(true)` will be optimized out by the compiler", - cx.tcx.item_name(macro_call.def_id) - ), - None, - "remove it", - ); - } else if !is_debug { - let (assert_arg, panic_arg) = match panic_expn { - PanicExpn::Empty => ("", ""), - _ => (", ..", ".."), - }; - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!("`assert!(false{assert_arg})` should probably be replaced"), - None, - format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), - ); - } +fn as_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Lit(l) = e.kind + && let LitKind::Bool(b) = l.node + { + Some(b) + } else { + None } } diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index ffd6c520c9ae8..14c5e22fb9cd5 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,142 +4,103 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use toml::Spanned; +use toml::de::{DeTable, DeValue}; -#[derive(Deserialize, Serialize, Debug)] -struct LintConfigTable { - level: String, - priority: Option, +fn toml_span(range: Range, file: &SourceFile) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(range.start), + file.start_pos + BytePos::from_usize(range.end), + SyntaxContext::root(), + None, + ) } -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum LintConfig { - Level(String), - Table(LintConfigTable), +struct LintConfig<'a> { + sp: Range, + level: &'a str, + priority: Option, } - -impl LintConfig { - fn level(&self) -> &str { - match self { - LintConfig::Level(level) => level, - LintConfig::Table(table) => &table.level, - } - } - +impl<'a> LintConfig<'a> { fn priority(&self) -> i64 { - match self { - LintConfig::Level(_) => 0, - LintConfig::Table(table) => table.priority.unwrap_or(0), - } + self.priority.unwrap_or(0) } fn is_implicit(&self) -> bool { - if let LintConfig::Table(table) = self { - table.priority.is_none() - } else { - true - } + self.priority.is_none() } -} - -type LintTable = BTreeMap, Spanned>; - -#[derive(Deserialize, Debug, Default)] -struct Lints { - #[serde(default)] - rust: LintTable, - #[serde(default)] - clippy: LintTable, -} - -#[derive(Deserialize, Debug, Default)] -struct Workspace { - #[serde(default)] - lints: Lints, -} -#[derive(Deserialize, Debug)] -struct CargoToml { - #[serde(default)] - lints: Lints, - #[serde(default)] - workspace: Workspace, -} - -fn toml_span(range: Range, file: &SourceFile) -> Span { - Span::new( - file.start_pos + BytePos::from_usize(range.start), - file.start_pos + BytePos::from_usize(range.end), - SyntaxContext::root(), - None, - ) + fn parse(value: &'a Spanned>) -> Option { + let sp = value.span(); + let (level, priority) = match value.get_ref() { + DeValue::String(level) => (&**level, None), + DeValue::Table(tbl) => { + let level = tbl.get("level")?.get_ref().as_str()?; + let priority = if let Some(priority) = tbl.get("priority") { + let priority = priority.get_ref().as_integer()?; + Some(i64::from_str_radix(priority.as_str(), priority.radix()).ok()?) + } else { + None + }; + (level, priority) + }, + _ => return None, + }; + Some(Self { sp, level, priority }) + } } -fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { - if name.get_ref() == "warnings" { - continue; - } - - if known_groups.contains(name.get_ref().as_str()) { - groups.push((name, config)); - } else { - lints.push((name, config.into_inner())); + if name.get_ref() != "warnings" + && let Some(config) = LintConfig::parse(config) + { + if known_groups.contains(&**name.get_ref()) { + groups.push((name, config)); + } else { + lints.push((name, config)); + } } } for (group, group_config) in groups { - let priority = group_config.get_ref().priority(); - let level = group_config.get_ref().level(); - if let Some((conflict, _)) = lints - .iter() - .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) - { + if let Some((conflict, _)) = lints.iter().rfind(|(_, lint_config)| { + lint_config.priority() == group_config.priority() && lint_config.level != group_config.level + }) { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, toml_span(group.span(), file), format!( - "lint group `{}` has the same priority ({priority}) as a lint", - group.as_ref() + "lint group `{}` has the same priority ({}) as a lint", + group.as_ref(), + group_config.priority(), ), |diag| { - let config_span = toml_span(group_config.span(), file); + let config_span = toml_span(group_config.sp.clone(), file); - if group_config.as_ref().is_implicit() { + if group_config.is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); - let mut suggestion = String::new(); let low_priority = lints .iter() - .map(|(_, config)| config.priority().saturating_sub(1)) + .map(|(_, lint_config)| lint_config.priority().saturating_sub(1)) .min() .unwrap_or(-1); - Serialize::serialize( - &LintConfigTable { - level: level.into(), - priority: Some(low_priority), - }, - toml::ser::ValueSerializer::new(&mut suggestion), - ) - .unwrap(); diag.span_suggestion_verbose( config_span, format!( "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - suggestion, + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), Applicability::MaybeIncorrect, ); }, @@ -148,10 +109,29 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet< } } +struct LintTbls<'a> { + rust: Option<&'a DeTable<'a>>, + clippy: Option<&'a DeTable<'a>>, +} +fn get_lint_tbls<'a>(tbl: &'a DeTable<'a>) -> LintTbls<'a> { + if let Some(lints) = tbl.get("lints") + && let Some(lints) = lints.get_ref().as_table() + { + let rust = lints.get("rust").and_then(|x| x.get_ref().as_table()); + let clippy = lints.get("clippy").and_then(|x| x.get_ref().as_table()); + LintTbls { rust, clippy } + } else { + LintTbls { + rust: None, + clippy: None, + } + } +} + pub fn check(cx: &LateContext<'_>) { if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml")) && let Some(src) = file.src.as_deref() - && let Ok(cargo_toml) = toml::from_str::(src) + && let Ok(cargo_toml) = DeTable::parse(src) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); @@ -167,9 +147,23 @@ pub fn check(cx: &LateContext<'_>) { } } - check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); - check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); + let lints = get_lint_tbls(cargo_toml.get_ref()); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + if let Some(tbl) = cargo_toml.get_ref().get("workspace") + && let Some(tbl) = tbl.get_ref().as_table() + { + let lints = get_lint_tbls(tbl); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + } } } diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 7646aa48b7726..c0f30e456d8db 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; +use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { def_id: LocalDefId, ) { if !cx.tcx.has_attr(def_id, sym::test) { - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(b) => b, None => { diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index ad610fbd8d2c3..b13e307a3f9c8 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,16 +1,16 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, tokenize_with_text}; -use rustc_ast::BinOpKind; +use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text}; +use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -95,14 +95,14 @@ impl CollapsibleIf { fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { if let Some(else_) = expr_block(else_block) - && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() && let ExprKind::If(else_if_cond, ..) = else_.kind - && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, else_block, else_, sym::collapsible_else_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_ELSE_IF, + else_.hir_id, else_block.span, "this `else { if .. }` block can be collapsed", |diag| { @@ -166,15 +166,15 @@ impl CollapsibleIf { fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { if let Some(inner) = expr_block(then) - && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind && self.eligible_condition(cx, check_inner) && expr.span.eq_ctxt(inner.span) - && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, then, inner, sym::collapsible_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_IF, + inner.hir_id, expr.span, "this `if` statement can be collapsed", |diag| { @@ -216,8 +216,46 @@ impl CollapsibleIf { } fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { - !matches!(cond.kind, ExprKind::Let(..)) - || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) + !matches!(cond.kind, ExprKind::Let(..)) || can_use_if_let_chains(cx, self.msrv) + } + + // Check that nothing significant can be found between the initial `{` of `inner_if` and + // the beginning of `inner_if_expr`... + // + // Unless it's only an `#[expect(clippy::collapsible{,_else}_if)]` attribute, in which case we + // _do_ need to lint, in order to actually fulfill its expectation (#13365) + fn check_significant_tokens_and_expect_attrs( + &self, + cx: &LateContext<'_>, + inner_if: &Block<'_>, + inner_if_expr: &Expr<'_>, + expected_lint_name: Symbol, + ) -> bool { + match cx.tcx.hir_attrs(inner_if_expr.hir_id) { + [] => { + // There aren't any attributes, so just check for significant tokens + let span = inner_if.span.split_at(1).1.until(inner_if_expr.span); + !span_contains_non_whitespace(cx, span, self.lint_commented_code) + }, + + [attr] + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let Some(metas) = attr.meta_item_list() + && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + { + // There is an `expect` attribute -- check that there is no _other_ significant text + let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); + let span_after_attr = attr.span().between(inner_if_expr.span); + !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) + && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + }, + + // There are other attributes, which are significant tokens -- check failed + _ => false, + } } } @@ -242,18 +280,6 @@ impl LateLintPass<'_> for CollapsibleIf { } } -// Check that nothing significant can be found but whitespaces between the initial `{` of `block` -// and the beginning of `stop_at`. -fn block_starts_with_significant_tokens( - cx: &LateContext<'_>, - block: &Block<'_>, - stop_at: &Expr<'_>, - lint_commented_code: bool, -) -> bool { - let span = block.span.split_at(1).1.until(stop_at.span); - span_contains_non_whitespace(cx, span, lint_commented_code) -} - /// If `block` is a block with either one expression or a statement containing an expression, /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2a4bedc184552..0ec0aaaad4536 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -84,10 +84,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::collapsible_if::COLLAPSIBLE_IF_INFO, crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, - crate::copies::BRANCHES_SHARING_CODE_INFO, - crate::copies::IFS_SAME_COND_INFO, - crate::copies::IF_SAME_THEN_ELSE_INFO, - crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::copy_iterator::COPY_ITERATOR_INFO, crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, crate::create_dir::CREATE_DIR_INFO, @@ -204,6 +200,10 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::ifs::BRANCHES_SHARING_CODE_INFO, + crate::ifs::IFS_SAME_COND_INFO, + crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, crate::implicit_hasher::IMPLICIT_HASHER_INFO, @@ -226,8 +226,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, @@ -535,7 +533,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, - crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, @@ -576,6 +573,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO, crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, @@ -704,6 +702,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, @@ -751,6 +751,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, + crate::unnecessary_mut_passed::UNNECESSARY_MUT_PASSED_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index f8a9037fc8047..641f8ae03b72a 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -75,7 +75,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + if expr.span.check_source_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 88aebc3e6a168..2147f72889093 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -18,11 +18,11 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.54.0"] ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), @@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), @@ -184,6 +184,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::transmute_int_to_float", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + #[clippy::version = "1.90.0"] + ("clippy::unchecked_duration_subtraction", "clippy::unchecked_time_subtraction"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a70105db19490..9ebb8e6e15d9e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ - DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, + path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -260,6 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; self.skip_expr = skip_expr; + if is_from_proc_macro(cx, expr) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data, cx.typeck_results()); + } + return; + } + match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index cbbcb2f7a3bab..274c699ff9d24 100644 --- a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -12,6 +12,7 @@ pub(super) fn check<'tcx>( span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, ord_is_automatically_derived: bool, ) { if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) @@ -38,7 +39,7 @@ pub(super) fn check<'tcx>( "you are deriving `Ord` but have implemented `PartialOrd` explicitly" }; - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs index ed7881c461ffc..fbace0bd73acf 100644 --- a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::has_non_exhaustive_attr; use clippy_utils::ty::implements_trait_with_env; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; use rustc_span::{Span, sym}; @@ -11,7 +11,13 @@ use rustc_span::{Span, sym}; use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { if let ty::Adt(adt, args) = ty.kind() && cx.tcx.visibility(adt.did()).is_public() && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) @@ -20,7 +26,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() @@ -30,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T span_lint_hir_and_then( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), + adt_hir_id, span.ctxt().outer_expn_data().call_site, "you are deriving `PartialEq` and can implement `Eq`", |diag| { diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs index 6f36a58025a2b..afc02ce32d48e 100644 --- a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{HirId, TraitRef}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -10,8 +10,9 @@ use super::DERIVED_HASH_WITH_MANUAL_EQ; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, hash_is_automatically_derived: bool, ) { if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() @@ -31,9 +32,10 @@ pub(super) fn check<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( + span_lint_hir_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, + adt_hir_id, span, "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index 6b97b4bd6b4d3..dfb723b86eb93 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,13 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{implements_trait, is_copy}; -use rustc_hir::{self as hir, Item}; +use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use super::EXPL_IMPL_CLONE_ON_COPY; /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -54,12 +60,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h return; } - span_lint_and_note( + span_lint_hir_and_then( cx, EXPL_IMPL_CLONE_ON_COPY, + adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", + |diag| { + diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); + }, ); } diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 1d63394ce37d4..06efc2709faa6 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,3 +1,5 @@ +use clippy_utils::path_res; +use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -194,21 +196,25 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), + self_ty, .. }) = item.kind + && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Some(local_def_id) = def_id.as_local() { + let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); let trait_ref = &of_trait.trait_ref; let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); - derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); if is_automatically_derived { - unsafe_derive_deserialize::check(cx, item, trait_ref, ty); - derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty, adt_hir_id); } else { - expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id); } } } diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs index c391e7b62289f..38f3251fd3891 100644 --- a/clippy_lints/src/derive/unsafe_derive_deserialize.rs +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_lint_allowed, paths}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -13,7 +13,13 @@ use rustc_span::{Span, sym}; use super::UNSAFE_DERIVE_DESERIALIZE; /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { let mut visitor = UnsafeVisitor { cx }; walk_item(&mut visitor, item).is_break() @@ -22,8 +28,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if let Some(trait_def_id) = trait_ref.trait_def_id() && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) && cx .tcx diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index eca3bc390d778..f8ae770b3a4db 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -315,7 +315,7 @@ declare_clippy_lint! { /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/) /// pub fn do_something() {} /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.90.0"] pub DOC_BROKEN_LINK, pedantic, "broken document link" diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 4dd8f01ee7090..bddf4702fb340 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Expr, ExprKind}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -24,7 +26,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn simple_no_parens() -> i32 { - /// 0 + /// (0) /// } /// /// # fn foo(bar: usize) {} @@ -40,29 +42,54 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - let span = match &expr.kind { - ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, params) - if let [param] = &**params - && let ExprKind::Paren(_) = param.kind => - { - param.span + match &expr.kind { + // ((..)) + // ^^^^^^ expr + // ^^^^ inner + ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { + // suggest removing the outer parens + if expr.span.eq_ctxt(inner.span) { + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, - ExprKind::MethodCall(call) - if let [arg] = &*call.args - && let ExprKind::Paren(_) = arg.kind => + + // func((n)) + // ^^^^^^^^^ expr + // ^^^ arg + // ^ inner + ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) + if let [arg] = &**args + && let ExprKind::Paren(inner) = &arg.kind => { - arg.span + // suggest removing the inner parens + if expr.span.eq_ctxt(arg.span) { + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, - _ => return, - }; - if !expr.span.from_expansion() { - span_lint( - cx, - DOUBLE_PARENS, - span, - "consider removing unnecessary double parentheses", - ); + _ => {}, } } } diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index c828fc57f7601..1a56c8f810ee7 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); - let constant = cx - .tcx - .const_eval_poly(def_id.to_def_id()) - .ok() - .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { + let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok(); + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) { if let ty::Adt(adt, _) = ty.kind() && adt.is_enum() { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 84d39dd81c919..407a3f1306739 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; @@ -110,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e -fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> { + if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -157,7 +158,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(method) = get_specialized_log_method(cx, &args[0]) { + if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) { span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, @@ -205,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. #[expect(clippy::cast_possible_truncation)] -fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { +fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { if (-16_777_215.0..16_777_216.0).contains(num) { @@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } /// Returns true iff expr is some zero literal -fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match ConstEvalCtxt::new(cx).eval_simple(expr) { +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index e8afa69b537ea..54e9538fcb993 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse { ), // Don't lint on `… != 0`, as these are likely to be bit tests. // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. - ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => ( - "unnecessary `!=` operation", - "change to `==` and swap the blocks of the `if`/`else`", - ), + ExprKind::Binary(op, _, rhs) + if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) => + { + ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ) + }, _ => return, }; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index b50d91f101463..f9fee292837ea 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -79,6 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) + && then_block.expr.is_none_or(|expr| !contains_return(expr)) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { sym::then_some diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/ifs/branches_sharing_code.rs similarity index 63% rename from clippy_lints/src/copies.rs rename to clippy_lints/src/ifs/branches_sharing_code.rs index 4fdb497950f8d..eb1025f71498e 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,218 +1,23 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; -use clippy_utils::higher::has_let_expr; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ - ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, - get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, - search_same, + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, + path_to_local, }; use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; -use rustc_session::impl_lint_pass; +use rustc_lint::LateContext; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; use rustc_span::{Span, Symbol}; -declare_clippy_lint! { - /// ### What it does - /// Checks for consecutive `if`s with the same condition. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// - /// ### Example - /// ```ignore - /// if a == b { - /// … - /// } else if a == b { - /// … - /// } - /// ``` - /// - /// Note that this lint ignores all conditions with a function call as it could - /// have side effects: - /// - /// ```ignore - /// if foo() { - /// … - /// } else if foo() { // not linted - /// … - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub IFS_SAME_COND, - correctness, - "consecutive `if`s with the same condition" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for consecutive `if`s with the same function call. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// Despite the fact that function can have side effects and `if` works as - /// intended, such an approach is implicit and can be considered a "code smell". - /// - /// ### Example - /// ```ignore - /// if foo() == bar { - /// … - /// } else if foo() == bar { - /// … - /// } - /// ``` - /// - /// This probably should be: - /// ```ignore - /// if foo() == bar { - /// … - /// } else if foo() == baz { - /// … - /// } - /// ``` - /// - /// or if the original code was not a typo and called function mutates a state, - /// consider move the mutation out of the `if` condition to avoid similarity to - /// a copy & paste error: - /// - /// ```ignore - /// let first = foo(); - /// if first == bar { - /// … - /// } else { - /// let second = foo(); - /// if second == bar { - /// … - /// } - /// } - /// ``` - #[clippy::version = "1.41.0"] - pub SAME_FUNCTIONS_IN_IF_CONDITION, - pedantic, - "consecutive `if`s with the same function call" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `if/else` with the same body as the *then* part - /// and the *else* part. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// - /// ### Example - /// ```ignore - /// let foo = if … { - /// 42 - /// } else { - /// 42 - /// }; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub IF_SAME_THEN_ELSE, - style, - "`if` with the same `then` and `else` blocks" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks if the `if` and `else` block contain shared code that can be - /// moved out of the blocks. - /// - /// ### Why is this bad? - /// Duplicate code is less maintainable. - /// - /// ### Example - /// ```ignore - /// let foo = if … { - /// println!("Hello World"); - /// 13 - /// } else { - /// println!("Hello World"); - /// 42 - /// }; - /// ``` - /// - /// Use instead: - /// ```ignore - /// println!("Hello World"); - /// let foo = if … { - /// 13 - /// } else { - /// 42 - /// }; - /// ``` - #[clippy::version = "1.53.0"] - pub BRANCHES_SHARING_CODE, - nursery, - "`if` statement with shared code in all blocks" -} - -pub struct CopyAndPaste<'tcx> { - interior_mut: InteriorMut<'tcx>, -} - -impl<'tcx> CopyAndPaste<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { - Self { - interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), - } - } -} - -impl_lint_pass!(CopyAndPaste<'_> => [ - IFS_SAME_COND, - SAME_FUNCTIONS_IN_IF_CONDITION, - IF_SAME_THEN_ELSE, - BRANCHES_SHARING_CODE -]); - -impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { - let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &mut self.interior_mut); - lint_same_fns_in_if_cond(cx, &conds); - let all_same = - !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); - if !all_same && conds.len() != blocks.len() { - lint_branches_sharing_code(cx, &conds, &blocks, expr); - } - } - } -} - -fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { - let mut eq = SpanlessEq::new(cx); - blocks - .array_windows::<2>() - .enumerate() - .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - lhs.span, - "this `if` has identical blocks", - Some(rhs.span), - "same as this", - ); - all_eq - } else { - false - } - }) -} +use super::BRANCHES_SHARING_CODE; -fn lint_branches_sharing_code<'tcx>( +pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, conds: &[&'tcx Expr<'_>], blocks: &[&'tcx Block<'_>], @@ -356,8 +161,8 @@ fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: & .is_some() } -/// Checks if the given statement should be considered equal to the statement in the same position -/// for each block. +/// Checks if the given statement should be considered equal to the statement in the same +/// position for each block. fn eq_stmts( stmt: &Stmt<'_>, blocks: &[&Block<'_>], @@ -516,9 +321,9 @@ fn scan_block_for_eq<'tcx>( } } -/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This -/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the -/// macro expansion is equal. +/// Adjusts the index for which the statements begin to differ to the closest macro callsite. +/// This avoids giving suggestions that requires splitting a macro call in half, when only a +/// part of the macro expansion is equal. /// /// For example, for the following macro: /// ```rust,ignore @@ -587,70 +392,6 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo }) } -fn method_caller_is_mutable<'tcx>( - cx: &LateContext<'tcx>, - caller_expr: &Expr<'_>, - interior_mut: &mut InteriorMut<'tcx>, -) -> bool { - let caller_ty = cx.typeck_results().expr_ty(caller_expr); - - interior_mut.is_interior_mut_ty(cx, caller_ty) - || caller_ty.is_mutable_ptr() - // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) - .and_then(|hid| find_binding_init(cx, hid)) - .is_none() -} - -/// Implementation of `IFS_SAME_COND`. -fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for group in search_same( - conds, - |e| hash_expr(cx, e), - |lhs, rhs| { - // Ignore eq_expr side effects iff one of the expression kind is a method call - // and the caller is not a mutable, including inner mutable type. - if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_mutable(cx, caller, interior_mut) { - false - } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) - } - } else { - eq_expr_value(cx, lhs, rhs) - } - }, - ) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); - } -} - -/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. -fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { - // Do not lint if any expr originates from a macro - if lhs.span.from_expansion() || rhs.span.from_expansion() { - return false; - } - // Do not spawn warning if `IFS_SAME_COND` already produced it. - if eq_expr_value(cx, lhs, rhs) { - return false; - } - SpanlessEq::new(cx).eq_expr(lhs, rhs) - }; - - for group in search_same(conds, |e| hash_expr(cx, e), eq) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint( - cx, - SAME_FUNCTIONS_IN_IF_CONDITION, - spans, - "these `if` branches have the same function call", - ); - } -} - fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let parent = cx.tcx.parent_hir_node(expr.hir_id); if let Node::LetStmt(LetStmt { init: Some(e), .. }) diff --git a/clippy_lints/src/ifs/if_same_then_else.rs b/clippy_lints/src/ifs/if_same_then_else.rs new file mode 100644 index 0000000000000..69402ec890768 --- /dev/null +++ b/clippy_lints/src/ifs/if_same_then_else.rs @@ -0,0 +1,29 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::has_let_expr; +use rustc_hir::{Block, Expr}; +use rustc_lint::LateContext; + +use super::IF_SAME_THEN_ELSE; + +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs new file mode 100644 index 0000000000000..ca76fc2587db9 --- /dev/null +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use super::IFS_SAME_COND; + +fn method_caller_is_mutable<'tcx>( + cx: &LateContext<'tcx>, + caller_expr: &Expr<'_>, + interior_mut: &mut InteriorMut<'tcx>, +) -> bool { + let caller_ty = cx.typeck_results().expr_ty(caller_expr); + + interior_mut.is_interior_mut_ty(cx, caller_ty) + || caller_ty.is_mutable_ptr() + // `find_binding_init` will return the binding iff its not mutable + || path_to_local(caller_expr) + .and_then(|hid| find_binding_init(cx, hid)) + .is_none() +} + +/// Implementation of `IFS_SAME_COND`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { + for group in search_same( + conds, + |e| hash_expr(cx, e), + |lhs, rhs| { + // Ignore eq_expr side effects iff one of the expression kind is a method call + // and the caller is not a mutable, including inner mutable type. + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { + if method_caller_is_mutable(cx, caller, interior_mut) { + false + } else { + SpanlessEq::new(cx).eq_expr(lhs, rhs) + } + } else { + eq_expr_value(cx, lhs, rhs) + } + }, + ) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); + } +} diff --git a/clippy_lints/src/ifs/mod.rs b/clippy_lints/src/ifs/mod.rs new file mode 100644 index 0000000000000..739f2fc917293 --- /dev/null +++ b/clippy_lints/src/ifs/mod.rs @@ -0,0 +1,182 @@ +use clippy_config::Conf; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{if_sequence, is_else_clause, is_lint_allowed}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; + +mod branches_sharing_code; +mod if_same_then_else; +mod ifs_same_cond; +mod same_functions_in_if_cond; + +declare_clippy_lint! { + /// ### What it does + /// Checks for consecutive `if`s with the same condition. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// + /// ### Example + /// ```ignore + /// if a == b { + /// … + /// } else if a == b { + /// … + /// } + /// ``` + /// + /// Note that this lint ignores all conditions with a function call as it could + /// have side effects: + /// + /// ```ignore + /// if foo() { + /// … + /// } else if foo() { // not linted + /// … + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub IFS_SAME_COND, + correctness, + "consecutive `if`s with the same condition" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for consecutive `if`s with the same function call. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// Despite the fact that function can have side effects and `if` works as + /// intended, such an approach is implicit and can be considered a "code smell". + /// + /// ### Example + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == bar { + /// … + /// } + /// ``` + /// + /// This probably should be: + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == baz { + /// … + /// } + /// ``` + /// + /// or if the original code was not a typo and called function mutates a state, + /// consider move the mutation out of the `if` condition to avoid similarity to + /// a copy & paste error: + /// + /// ```ignore + /// let first = foo(); + /// if first == bar { + /// … + /// } else { + /// let second = foo(); + /// if second == bar { + /// … + /// } + /// } + /// ``` + #[clippy::version = "1.41.0"] + pub SAME_FUNCTIONS_IN_IF_CONDITION, + pedantic, + "consecutive `if`s with the same function call" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `if/else` with the same body as the *then* part + /// and the *else* part. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// + /// ### Example + /// ```ignore + /// let foo = if … { + /// 42 + /// } else { + /// 42 + /// }; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub IF_SAME_THEN_ELSE, + style, + "`if` with the same `then` and `else` blocks" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// ### Why is this bad? + /// Duplicate code is less maintainable. + /// + /// ### Example + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Use instead: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + #[clippy::version = "1.53.0"] + pub BRANCHES_SHARING_CODE, + nursery, + "`if` statement with shared code in all blocks" +} + +pub struct CopyAndPaste<'tcx> { + interior_mut: InteriorMut<'tcx>, +} + +impl<'tcx> CopyAndPaste<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { + Self { + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), + } + } +} + +impl_lint_pass!(CopyAndPaste<'_> => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + BRANCHES_SHARING_CODE +]); + +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { + let (conds, blocks) = if_sequence(expr); + ifs_same_cond::check(cx, &conds, &mut self.interior_mut); + same_functions_in_if_cond::check(cx, &conds); + let all_same = + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && if_same_then_else::check(cx, &conds, &blocks); + if !all_same && conds.len() != blocks.len() { + branches_sharing_code::check(cx, &conds, &blocks, expr); + } + } + } +} diff --git a/clippy_lints/src/ifs/same_functions_in_if_cond.rs b/clippy_lints/src/ifs/same_functions_in_if_cond.rs new file mode 100644 index 0000000000000..1f6bf04e22e7d --- /dev/null +++ b/clippy_lints/src/ifs/same_functions_in_if_cond.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same}; +use rustc_hir::Expr; +use rustc_lint::LateContext; + +use super::SAME_FUNCTIONS_IN_IF_CONDITION; + +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if lhs.span.from_expansion() || rhs.span.from_expansion() { + return false; + } + // Do not spawn warning if `IFS_SAME_COND` already produced it. + if eq_expr_value(cx, lhs, rhs) { + return false; + } + SpanlessEq::new(cx).eq_expr(lhs, rhs) + }; + + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( + cx, + SAME_FUNCTIONS_IN_IF_CONDITION, + spans, + "these `if` branches have the same function call", + ); + } +} diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 076017a247b4b..6ed478b2708a3 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(e) => e, None => return, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 0fdbf67973813..4bf3a390b050c 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -117,10 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let ecx = ConstEvalCtxt::new(cx); - if let Some(Constant::Int(c)) = ecx.eval(r) { + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) { return Some((c, op.node, l)); } - if let Some(Constant::Int(c)) = ecx.eval(l) { + if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs deleted file mode 100644 index 13117f60abd52..0000000000000 --- a/clippy_lints/src/instant_subtraction.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; -use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; -use rustc_span::source_map::Spanned; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```no_run - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```no_run - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.65.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between an `Instant` and a `Duration`. - /// - /// ### Why is this bad? - /// Unchecked subtraction could cause underflow on certain platforms, leading to - /// unintentional panics. - /// - /// ### Example - /// ```no_run - /// # use std::time::{Instant, Duration}; - /// let time_passed = Instant::now() - Duration::from_secs(5); - /// ``` - /// - /// Use instead: - /// ```no_run - /// # use std::time::{Instant, Duration}; - /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); - /// ``` - #[clippy::version = "1.67.0"] - pub UNCHECKED_DURATION_SUBTRACTION, - pedantic, - "finds unchecked subtraction of a 'Duration' from an 'Instant'" -} - -pub struct InstantSubtraction { - msrv: Msrv, -} - -impl InstantSubtraction { - pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } - } -} - -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); - -impl LateLintPass<'_> for InstantSubtraction { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Sub, .. - }, - lhs, - rhs, - ) = expr.kind - && let typeck = cx.typeck_results() - && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) - { - let rhs_ty = typeck.expr_ty(rhs); - - if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) - && let Some(sugg) = Sugg::hir_opt(cx, rhs) - { - print_manual_instant_elapsed_sugg(cx, expr, sugg); - } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) - && !expr.span.from_expansion() - && self.msrv.meets(cx, msrvs::TRY_FROM) - { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); - } - } - } -} - -fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) - { - true - } else { - false - } -} - -fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_paren()), - Applicability::MachineApplicable, - ); -} - -fn print_unchecked_duration_subtraction_sugg( - cx: &LateContext<'_>, - left_expr: &Expr<'_>, - right_expr: &Expr<'_>, - expr: &Expr<'_>, -) { - let mut applicability = Applicability::MachineApplicable; - - let ctxt = expr.span.ctxt(); - let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; - let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; - - span_lint_and_sugg( - cx, - UNCHECKED_DURATION_SUBTRACTION, - expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", - "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap()"), - applicability, - ); -} diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 1666e8e5ae324..885649074ab63 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds - && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 945bb84708f85..76f5fdfaa8dcf 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,11 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; -use clippy_utils::is_bool; -use clippy_utils::macros::span_is_local; -use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -158,7 +156,8 @@ declare_clippy_lint! { } pub struct ItemNameRepetitions { - modules: Vec<(Symbol, String, OwnerId)>, + /// The module path the lint pass is in. + modules: Vec, enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, @@ -167,6 +166,17 @@ pub struct ItemNameRepetitions { allowed_prefixes: FxHashSet, } +struct ModInfo { + name: Symbol, + name_camel: String, + /// Does this module have the `pub` visibility modifier. + is_public: bool, + /// How many bodies are between this module and the current lint pass position. + /// + /// Only the most recently seen module is updated when entering/exiting a body. + in_body_count: u32, +} + impl ItemNameRepetitions { pub fn new(conf: &'static Conf) -> Self { Self { @@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_> } impl LateLintPass<'_> for ItemNameRepetitions { - fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { - let Some(_ident) = item.kind.ident() else { return }; - - let last = self.modules.pop(); - assert!(last.is_some()); + fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + if matches!(item.kind, ItemKind::Mod(..)) { + let prev = self.modules.pop(); + debug_assert!(prev.is_some()); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let Some(ident) = item.kind.ident() else { return }; - - let item_name = ident.name.as_str(); - let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) - && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules - // constants don't have surrounding modules - && !mod_camel.is_empty() - { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + fn check_body(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count += 1; + } + } - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + fn check_body_post(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count -= 1; + } + } - let both_are_public = - cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + let ident = match item.kind { + ItemKind::Mod(ident, _) => { + if let [.., prev] = &*self.modules + && prev.name == ident.name + && prev.in_body_count == 0 + && (!self.allow_private_module_inception || prev.is_public) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + ident + }, - if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name is the same as its containing module's name", - ); - } + ItemKind::Enum(ident, _, def) => { + if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { + self.check_variants(cx, item, &def); + } + ident + }, + ItemKind::Struct(ident, _, data) => { + if let VariantData::Struct { fields, .. } = data + && !ident.span.in_external_macro(cx.tcx.sess.source_map()) + { + self.check_fields(cx, item, fields); + } + ident + }, - let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _)); - if both_are_public && item_camel.len() > mod_camel.len() && !is_macro { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); + ItemKind::Const(ident, ..) + | ItemKind::ExternCrate(_, ident) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) + | ItemKind::TraitAlias(ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Use(_, UseKind::Single(ident)) => ident, + + ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) | ItemKind::Use(..) => return, + }; - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); + let item_name = ident.name.as_str(); + let item_camel = to_camel_case(item_name); - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( + if let [.., prev] = &*self.modules + && prev.is_public + && prev.in_body_count == 0 + && !item.span.from_expansion() + && !matches!(item.kind, ItemKind::Macro(..)) + && cx.tcx.visibility(item.owner_id).is_public() + { + if !self.allow_exact_repetitions && item_camel == prev.name_camel { + if !is_from_proc_macro(cx, item) { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + } else if item_camel.len() > prev.name_camel.len() { + if let Some(s) = item_camel.strip_prefix(&prev.name_camel) + && let Some(c) = s.chars().next() + && (c == '_' || c.is_uppercase() || c.is_numeric()) + { + if !is_from_proc_macro(cx, item) { + span_lint( cx, MODULE_NAME_REPETITIONS, ident.span, "item name starts with its containing module's name", - ), - _ => (), + ); } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + } else if let Some(s) = item_camel.strip_suffix(&prev.name_camel) + && !self.is_allowed_prefix(s) + && !is_from_proc_macro(cx, item) { span_lint( cx, @@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } - if span_is_local(item.span) { - match item.kind { - ItemKind::Enum(_, _, def) => { - self.check_variants(cx, item, &def); - }, - ItemKind::Struct(_, _, VariantData::Struct { fields, .. }) => { - self.check_fields(cx, item, fields); - }, - _ => (), - } + if matches!(item.kind, ItemKind::Mod(..)) { + self.modules.push(ModInfo { + name: ident.name, + name_camel: item_camel, + is_public: cx.tcx.visibility(item.owner_id).is_public(), + in_body_count: 0, + }); } - self.modules.push((ident.name, item_camel, item.owner_id)); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b0083b99f175f..815411348aa66 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -99,7 +99,6 @@ mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; mod comparison_chain; -mod copies; mod copy_iterator; mod crate_in_macro_def; mod create_dir; @@ -157,6 +156,7 @@ mod future_not_send; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; +mod ifs; mod ignored_unit_patterns; mod impl_hash_with_borrow_str_and_bytes; mod implicit_hasher; @@ -175,7 +175,6 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; -mod instant_subtraction; mod int_plus_one; mod integer_division_remainder_used; mod invalid_upcast_comparisons; @@ -252,7 +251,6 @@ mod multiple_bound_locations; mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; -mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; @@ -356,6 +354,7 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; +mod time_subtraction; mod to_digit_is_some; mod to_string_trait_impl; mod toplevel_ref_arg; @@ -374,6 +373,7 @@ mod unit_types; mod unnecessary_box_returns; mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; +mod unnecessary_mut_passed; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_semicolon; @@ -481,8 +481,8 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); - store.register_late_pass(|_| Box::new(mut_mut::MutMut)); - store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); + store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); @@ -548,7 +548,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); @@ -588,13 +588,13 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); - store.register_late_pass(|_| Box::new(unwrap::Unwrap)); + store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); @@ -670,7 +670,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_early_pass(move || Box::new(module_style::ModStyle::default())); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); @@ -717,7 +717,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index c5acaf0999332..a323c7cf8307c 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -2,11 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; -use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::find_attr; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource, find_attr}; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; @@ -148,8 +147,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - ( cx.effective_visibilities.is_exported(def_id) || - find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) + (cx.effective_visibilities.is_exported(def_id) + || find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. })) && !cx.tcx.is_doc_hidden(def_id) } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index bd2785fea2709..60782f445ab91 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; @@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { ) && let [first, second, const_1, const_2] = exprs && let ecx = ConstEvalCtxt::new(cx) - && let Some(const_1) = ecx.eval(const_1) - && let Some(const_2) = ecx.eval(const_2) + && let ctxt = expr.span.ctxt() + && let Some(const_1) = ecx.eval_local(const_1, ctxt) + && let Some(const_2) = ecx.eval_local(const_2, ctxt) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason - && (is_infinity(&const_1) && is_neg_infinity(&const_2) - || is_neg_infinity(&const_1) && is_infinity(&const_2)) + && (const_1.is_pos_infinity() && const_2.is_neg_infinity() + || const_1.is_neg_infinity() && const_2.is_pos_infinity()) && let Some(local_snippet) = first.span.get_source_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { @@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } } - -fn is_infinity(constant: &Constant<'_>) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::INFINITY, - Constant::F64(float) => *float == f64::INFINITY, - _ => false, - } -} - -fn is_neg_infinity(constant: &Constant<'_>) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::NEG_INFINITY, - Constant::F64(float) => *float == f64::NEG_INFINITY, - _ => false, - } -} diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 41e07e26bff0a..1e91a429fe45c 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_lhs.span.ctxt() == ctxt && add_rhs.span.ctxt() == ctxt && !expr.span.in_external_macro(cx.sess().source_map()) - && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) - && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) + && let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs) + && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 && let Some(hir_id) = path_to_local(rem2_lhs) - && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs) + && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 && rem2_lhs.span.ctxt() == ctxt @@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // constant along with the other expression unchanged if so fn check_for_either_unsigned_int_constant<'a>( cx: &'a LateContext<'_>, + ctxt: SyntaxContext, left: &'a Expr<'_>, right: &'a Expr<'_>, ) -> Option<(u128, &'a Expr<'a>)> { - check_for_unsigned_int_constant(cx, left) + check_for_unsigned_int_constant(cx, ctxt, left) .map(|int_const| (int_const, right)) - .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left))) + .or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left))) } -fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; +fn check_for_unsigned_int_constant<'a>( + cx: &'a LateContext<'_>, + ctxt: SyntaxContext, + expr: &'a Expr<'_>, +) -> Option { + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 06ee00c2cef3c..22e3407303f07 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval(r)?; + let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 07cce4046ca4f..f5d15310879a3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, SyntaxContext, sym}; use std::iter; declare_clippy_lint! { @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then); + let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt()); if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) { let kind_word = match strip_kind { StripKind::Prefix => "prefix", @@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E } // Returns the length of the `expr` if it's a constant string or char. -fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let value = ConstEvalCtxt::new(cx).eval(expr)?; +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } // Tests if `expr` equals the length of the pattern. -fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { +fn eq_pattern_length<'tcx>( + cx: &LateContext<'tcx>, + pattern: &Expr<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> bool { if let ExprKind::Lit(Spanned { node: LitKind::Int(n, _), .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| n == length) + constant_length(cx, pattern, ctxt).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } @@ -215,6 +220,7 @@ fn find_stripping<'tcx>( target: Res, pattern: &'tcx Expr<'_>, expr: &'tcx Expr<'tcx>, + ctxt: SyntaxContext, ) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap) { struct StrippingFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -223,6 +229,7 @@ fn find_stripping<'tcx>( pattern: &'tcx Expr<'tcx>, results: Vec<&'tcx Expr<'tcx>>, bindings: FxHashMap, + ctxt: SyntaxContext, } impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { @@ -236,7 +243,7 @@ fn find_stripping<'tcx>( { match (self.strip_kind, start, end) { (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { self.results.push(ex); return; } @@ -252,7 +259,7 @@ fn find_stripping<'tcx>( && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right) + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) { self.results.push(ex); return; @@ -280,6 +287,7 @@ fn find_stripping<'tcx>( pattern, results: vec![], bindings: FxHashMap::default(), + ctxt, }; walk_expr(&mut finder, expr); (finder.results, finder.bindings) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d917..ac9e51890362b 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -155,7 +155,7 @@ fn handle( && cx.typeck_results().expr_adjustments(body_some).is_empty() && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ae277da089fd2..818e504245549 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -18,11 +18,7 @@ use super::MATCH_SAME_ARMS; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(arm.body); - h.finish() - }; + let hash = |&(_, arm): &(_, &Arm<'_>)| hash_expr(cx, arm.body); let arena = DroplessArena::default(); let normalized_pats: Vec<_> = arms @@ -35,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[i + 1..] - .iter() - .enumerate() + (normalized_pats[i + 1..].iter().enumerate()) .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j)) .unwrap_or(normalized_pats.len()) }) @@ -48,16 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[..i] - .iter() - .enumerate() - .rev() - .zip(forwards_blocking_idxs[..i].iter().copied().rev()) - .skip_while(|&(_, forward_block)| forward_block > i) - .find_map(|((j, other), forward_block)| { - (forward_block == i || pat.has_overlapping_values(other)).then_some(j) - }) - .unwrap_or(0) + iter::zip( + normalized_pats[..i].iter().enumerate().rev(), + forwards_blocking_idxs[..i].iter().copied().rev(), + ) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then_some(j) + }) + .unwrap_or(0) }) .collect(); @@ -158,12 +151,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|(_, arm)| arm.pat.span.get_source_text(cx)) .collect::>>() { - let mut suggs = src + let suggs = src .iter() .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .chain([(dest.pat.span, pat_snippets.iter().join(" | "))]) .collect_vec(); - suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); diag.multipart_suggestion_verbose( "otherwise merge the patterns into a single arm", suggs, @@ -396,10 +389,7 @@ impl<'a> NormalizedPat<'a> { if lpath != rpath { return false; } - lpats - .iter() - .zip(rpats.iter()) - .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + iter::zip(lpats, rpats).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) }, (Self::Path(x), Self::Path(y)) => x == y, (Self::LitStr(x), Self::LitStr(y)) => x == y, @@ -409,7 +399,7 @@ impl<'a> NormalizedPat<'a> { (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { - lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) + lpats.len() == rpats.len() && iter::zip(lpats, rpats).all(|(x, y)| x.has_overlapping_values(y)) }, (Self::Slice(pats, None), Self::Slice(front, Some(back))) | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { @@ -418,16 +408,12 @@ impl<'a> NormalizedPat<'a> { if pats.len() < front.len() + back.len() { return false; } - pats[..front.len()] - .iter() - .zip(front.iter()) - .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + iter::zip(&pats[..front.len()], front) + .chain(iter::zip(&pats[pats.len() - back.len()..], back)) .all(|(x, y)| x.has_overlapping_values(y)) }, - (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront - .iter() - .zip(rfront.iter()) - .chain(lback.iter().rev().zip(rback.iter().rev())) + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => iter::zip(lfront, rfront) + .chain(iter::zip(lback.iter().rev(), rback.iter().rev())) .all(|(x, y)| x.has_overlapping_values(y)), // Enums can mix unit variants with tuple/struct variants. These can never overlap. diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index d3136c89178e6..d76218e6305b0 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -35,12 +35,12 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) let lhs_const = if let Some(lhs) = lhs { ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { - mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)? + Constant::new_numeric_min(cx.tcx, ty)? }; let rhs_const = if let Some(rhs) = rhs { ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { - mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)? + Constant::new_numeric_max(cx.tcx, ty)? }; let lhs_val = lhs_const.int_value(cx.tcx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?; diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c936c96f9719a..9d0115791838c 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -269,66 +269,61 @@ fn find_method_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); - let result_expr = match &op.kind { - ExprKind::AddrOf(_, _, borrowed) => borrowed, - _ => op, - }; - let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); - let mut sugg = format!("{receiver_sugg}.{good_method}"); - - if let Some(guard) = maybe_guard { - // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! - // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, - // counter to the intuition that it should be `Guard::IfLet`, so we need another check - // to see that there aren't any let chains anywhere in the guard, as that would break - // if we suggest `t.is_none() && (let X = y && z)` for: - // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { - if matches!(expr.kind, ExprKind::Let(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some(); - - if has_nested_let_chain { - return; + if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized + && let Some((good_method, maybe_guard)) = found_good_method(cx, arms) + { + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); + + if let Some(guard) = maybe_guard { + // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! + // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, + // counter to the intuition that it should be `Guard::IfLet`, so we need another check + // to see that there aren't any let chains anywhere in the guard, as that would break + // if we suggest `t.is_none() && (let X = y && z)` for: + // `match t { None if let X = y && z => true, _ => false }` + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { + if matches!(expr.kind, ExprKind::Let(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } + }) + .is_some(); - let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_paren()); + if has_nested_let_chain { + return; } - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN_MATCHING, - span, - format!("redundant pattern matching, consider using `{good_method}`"), - "try", - sugg, - app, - ); + let guard = Sugg::hir(cx, guard, ".."); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } + + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN_MATCHING, + span, + format!("redundant pattern matching, consider using `{good_method}`"), + "try", + sugg, + app, + ); } } fn found_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], - node: (&PatKind<'_>, &PatKind<'_>), + arms: &'tcx [Arm<'tcx>; 2], ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - match node { - (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) - if patterns_left.len() == 1 && patterns_right.len() == 1 => - { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + match (&arms[0].pat.kind, &arms[1].pat.kind) { + (PatKind::TupleStruct(path_left, [pattern_left], _), PatKind::TupleStruct(path_right, [pattern_right], _)) => { + if let (PatKind::Wild, PatKind::Wild) = (&pattern_left.kind, &pattern_right.kind) { find_good_method_for_match( cx, arms, @@ -356,7 +351,7 @@ fn found_good_method<'tcx>( } }, ( - PatKind::TupleStruct(path_left, patterns, _), + PatKind::TupleStruct(path_left, [pattern], _), PatKind::Expr(PatExpr { kind: PatExprKind::Path(path_right), .. @@ -367,9 +362,9 @@ fn found_good_method<'tcx>( kind: PatExprKind::Path(path_left), .. }), - PatKind::TupleStruct(path_right, patterns, _), - ) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(path_right, [pattern], _), + ) => { + if let PatKind::Wild = pattern.kind { find_good_method_for_match( cx, arms, @@ -396,8 +391,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + (PatKind::TupleStruct(path_left, [pattern], _), PatKind::Wild) => { + if let PatKind::Wild = pattern.kind { get_good_method(cx, arms, path_left) } else { None @@ -426,31 +421,23 @@ fn get_ident(path: &QPath<'_>) -> Option { fn get_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - if let Some(name) = get_ident(path_left) { - let (expected_item_left, should_be_left, should_be_right) = match name.as_str() { - "Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"), - "Err" => (Item::Lang(ResultErr), "is_err()", "is_ok()"), - "Some" => (Item::Lang(OptionSome), "is_some()", "is_none()"), - "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), - "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), - "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), - _ => return None, - }; - return find_good_method_for_matches_macro( - cx, - arms, - path_left, - expected_item_left, - should_be_left, - should_be_right, - ); - } - None + let ident = get_ident(path_left)?; + + let (expected_item_left, should_be_left, should_be_right) = match ident.name { + sym::Ok => (Item::Lang(ResultOk), "is_ok()", "is_err()"), + sym::Err => (Item::Lang(ResultErr), "is_err()", "is_ok()"), + sym::Some => (Item::Lang(OptionSome), "is_some()", "is_none()"), + sym::None => (Item::Lang(OptionNone), "is_none()", "is_some()"), + sym::Ready => (Item::Lang(PollReady), "is_ready()", "is_pending()"), + sym::Pending => (Item::Lang(PollPending), "is_pending()", "is_ready()"), + sym::V4 => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + sym::V6 => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), + _ => return None, + }; + find_good_method_for_matches_macro(cx, arms, path_left, expected_item_left, should_be_left, should_be_right) } #[derive(Clone, Copy)] @@ -490,7 +477,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte #[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, path_right: &QPath<'_>, expected_item_left: Item, @@ -525,7 +512,7 @@ fn find_good_method_for_match<'a, 'tcx>( fn find_good_method_for_matches_macro<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, expected_item_left: Item, should_be_left: &'a str, diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 83939d325794e..02f87512966ba 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -22,17 +22,16 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - if let Some(ff) = span.get_source_range(cx) - && let Some(text) = ff.as_str() - { - text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") - } else { - false - } + span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } -#[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { +pub(crate) fn check<'tcx>( + cx: &LateContext<'tcx>, + ex: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, + contains_comments: bool, +) { if let [arm1, arm2] = arms && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() @@ -224,13 +223,13 @@ enum PatState<'a> { Wild, /// A std enum we know won't be extended. Tracks the states of each variant separately. /// - /// This is not used for `Option` since it uses the current pattern to track it's state. + /// This is not used for `Option` since it uses the current pattern to track its state. StdEnum(&'a mut [PatState<'a>]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// /// For non-std enums there's no need to track the state of sub-patterns as the state of just - /// this pattern on it's own is enough for linting. Consider two cases: + /// this pattern on its own is enough for linting. Consider two cases: /// * This enum has no wild match. This case alone is enough to determine we can lint. /// * This enum has a wild match and therefore all sub-patterns also have a wild match. /// @@ -378,7 +377,11 @@ impl<'a> PatState<'a> { self.add_pat(cx, pat) }, PatKind::Tuple([sub_pat], pos) - if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + // `pat` looks like `(sub_pat)`, without a `..` -- has only one sub-pattern + if pos.as_opt_usize().is_none() + // `pat` looks like `(sub_pat, ..)` or `(.., sub_pat)`, but its type is a unary tuple, + // so it still only has one sub-pattern + || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => { self.add_pat(cx, sub_pat) }, diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e39916f733d59..95ecef5987007 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{ @@ -269,14 +269,11 @@ fn check_replace_with_default( ), |diag| { if !expr.span.from_expansion() { - let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); + let mut applicability = Applicability::MachineApplicable; + let (dest_snip, _) = snippet_with_context(cx, dest.span, expr.span.ctxt(), "", &mut applicability); + let suggestion = format!("{top_crate}::mem::take({dest_snip})"); - diag.span_suggestion( - expr.span, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + diag.span_suggestion(expr.span, "consider using", suggestion, applicability); } }, ); diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 72f83b245a0cb..e1a9a79e20eea 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -10,51 +10,68 @@ use rustc_span::sym; use super::FILTER_NEXT; -/// lint use of `filter().next()` for `Iterators` +#[derive(Copy, Clone)] +pub(super) enum Direction { + Forward, + Backward, +} + +/// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for +/// `DoubleEndedIterator` pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, filter_arg: &'tcx hir::Expr<'_>, + direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator - let recv_impls_iterator = cx + // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a + // DoubleEndedIterator + let (required_trait, next_method, find_method) = match direction { + Direction::Forward => (sym::Iterator, "next", "find"), + Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), + }; + if !cx .tcx - .get_diagnostic_item(sym::Iterator) - .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])); - if recv_impls_iterator { - let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead"; - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - (Applicability::Unspecified, Some((pat.span, ident))) - } else { - (Applicability::MachineApplicable, None) - }; + .get_diagnostic_item(required_trait) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])) + { + return; + } + let msg = format!( + "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ + `.{find_method}(..)` instead", + required_trait.as_str() + ); + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + // add note if not multi-line + span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { + let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) + && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; - diag.span_suggestion( - expr.span, - "try", - format!("{iter_snippet}.find({filter_snippet})"), - applicability, - ); + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.{find_method}({filter_snippet})"), + applicability, + ); - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"), - ); - } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), + ); + } + }); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); } } diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 47195fdd65f5f..ab21515f47f78 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ pub fn check( method_name: Symbol, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], + msrv: Msrv, ) { if args.is_empty() && method_name == sym::to_string @@ -26,6 +28,8 @@ pub fn check( && let self_ty = args.type_at(0) && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 + // Since Rust 1.82, the specialized `ToString` is properly called + && !msrv.meets(cx, msrvs::SPECIALIZED_TO_STRING_FOR_REFS) && specializes_tostring(cx, deref_self_ty) { span_lint_and_then( diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index a2ac4e54334ee..bf602811009a2 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -17,10 +17,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args cx.tcx.get_diagnostic_name(func_def_id), Some(sym::Ipv4Addr | sym::Ipv6Addr) ) + && let ecx = ConstEvalCtxt::new(cx) + && let ctxt = expr.span.ctxt() && let Some(args) = args .iter() .map(|arg| { - if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) { + if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ecx.eval_local(arg, ctxt) { u8::try_from(constant).ok() } else { None diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 9c32e9ac539d9..2aeb6f42d05cd 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix, expr.span.ctxt()) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 4bdf589f48762..0f8abd0172423 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 39e440e784f6d..663e34437a307 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -11,13 +11,15 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { - if let Constant::Int(arg) = constant { - Some(arg) - } else { - None - } - }) + && let Some(arg) = ConstEvalCtxt::new(cx) + .eval_local(arg_expr, expr.span.ctxt()) + .and_then(|constant| { + if let Constant::Int(arg) = constant { + Some(arg) + } else { + None + } + }) && arg == 0 && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/methods/lib.rs b/clippy_lints/src/methods/lib.rs new file mode 100644 index 0000000000000..84038283bcf8f --- /dev/null +++ b/clippy_lints/src/methods/lib.rs @@ -0,0 +1,70 @@ +use clippy_utils::sym; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::Mutability; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum SelfKind { + Value, + Ref, + RefMut, + No, // When we want the first argument type to be different than `Self` +} + +impl SelfKind { + pub(super) fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if ty == parent_ty { + true + } else if let Some(boxed_ty) = ty.boxed_ty() { + boxed_ty == parent_ty + } else if let ty::Adt(adt_def, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt_def.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) + } else { + false + } + } + + fn matches_ref<'a>(cx: &LateContext<'a>, mutability: Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if let ty::Ref(_, t, m) = *ty.kind() { + return m == mutability && t == parent_ty; + } + + let trait_sym = match mutability { + Mutability::Not => sym::AsRef, + Mutability::Mut => sym::AsMut, + }; + + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false; + }; + implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) + } + + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, Mutability::Not, parent_ty, ty) + && !matches_ref(cx, Mutability::Mut, parent_ty, ty) + } + + match self { + Self::Value => matches_value(cx, parent_ty, ty), + Self::Ref => matches_ref(cx, Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), + Self::RefMut => matches_ref(cx, Mutability::Mut, parent_ty, ty), + Self::No => matches_none(cx, parent_ty, ty), + } + } + + #[must_use] + pub(super) fn description(self) -> &'static str { + match self { + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", + } + } +} diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index a98cfff8bfbd1..6190c43578e9b 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; @@ -27,48 +27,46 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { - let main_sugg = (call_span, String::new()); - let mut app = if is_copy(cx, caller_ty) { - // there is technically a behavioral change here for `Copy` iterators, where - // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and - // changing it to `iter.next()` mutates iter directly - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; - let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); - if needs_to_be_mutable && !is_mutable(cx, caller) { - if let Some(hir_id) = path_to_local_with_projections(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && let PatKind::Binding(_, _, ident, _) = pat.kind - { - // We can reach the binding -- suggest making it mutable - let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; - let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.multipart_suggestion( format!("remove the call to `{name}`, and make `{ident}` mutable"), suggs, app, ); - }); - } else { - // If we can't make the binding mutable, prevent the suggestion from being automatically applied, - // and add a complementary help message. - app = Applicability::Unspecified; - - let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::MethodCall(method, ..) = expr.kind - { - Some(method.ident) } else { - None - }; + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); let note = if let Some(method_requiring_mut) = method_requiring_mut { @@ -77,18 +75,10 @@ pub(super) fn check( "this must be made mutable".to_string() }; diag.span_note(caller.span, note); - }); + } + } else { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); } - } else { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - main_sugg.0, - MSG, - format!("remove the call to `{name}`"), - main_sugg.1, - app, - ); - } + }); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4d..b0b4e5eedb084 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -55,6 +55,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod lib; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -79,6 +80,7 @@ mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; +mod new_ret_no_self; mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; @@ -102,9 +104,8 @@ mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; +mod should_implement_trait; mod single_char_add_str; -mod single_char_insert_string; -mod single_char_push_string; mod skip_while_next; mod sliced_string_as_bytes; mod stable_sort_primitive; @@ -148,20 +149,16 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; -use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; +use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, TraitRef, Ty}; +use rustc_middle::ty::TraitRef; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, kw}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -477,6 +474,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `ok().expect(..)`. /// + /// Note: This lint only triggers for code marked compatible + /// with versions of the compiler older than Rust 1.82.0. + /// /// ### Why is this bad? /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. @@ -1080,9 +1080,9 @@ declare_clippy_lint! { /// `T` implements `ToString` directly (like `&&str` or `&&String`). /// /// ### Why is this bad? - /// This bypasses the specialized implementation of - /// `ToString` and instead goes through the more expensive string formatting - /// facilities. + /// In versions of the compiler before Rust 1.82.0, this bypasses the specialized + /// implementation of`ToString` and instead goes through the more expensive string + /// formatting facilities. /// /// ### Example /// ```no_run @@ -4462,7 +4462,7 @@ declare_clippy_lint! { /// Checks for calls to `Read::bytes` on types which don't implement `BufRead`. /// /// ### Why is this bad? - /// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`. + /// The default implementation calls `read` for each byte, which can be very inefficient for data that's not in memory, such as `File`. /// /// ### Example /// ```no_run @@ -4856,7 +4856,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); expect_fun_call::check( cx, &self.format_args, @@ -4868,7 +4868,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); + inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); @@ -4891,48 +4891,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name; - let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; + let item = cx.tcx.hir_expect_item(parent); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt.is_none_or(|first_arg_ty| method_config - .self_kind.matches(cx, self_ty, first_arg_ty) - ) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } - } + should_implement_trait::check_impl_item(cx, impl_item, self_ty, implements_trait, first_arg_ty_opt, sig); if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api @@ -4942,7 +4911,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { { wrong_self_convention::check( cx, - name, + impl_item.ident.name, self_ty, first_arg_ty, first_arg.pat.span, @@ -4950,28 +4919,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, ); } - } - // if this impl block implements a trait, lint in trait definition instead - if implements_trait { - return; - } - - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { - let ret_ty = return_ty(cx, impl_item.owner_id); - - if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { - return; - } - - if name == sym::new && ret_ty != self_ty { - span_lint( - cx, - NEW_RET_NO_SELF, - impl_item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_impl_item(cx, impl_item, self_ty, implements_trait); } } @@ -4980,41 +4929,30 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if let TraitItemKind::Fn(ref sig, _) = item.kind - && sig.decl.implicit_self.has_implicit_self() - && let Some(first_arg_hir_ty) = sig.decl.inputs.first() - && let Some(&first_arg_ty) = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .inputs() - .skip_binder() - .first() - { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name, - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } + if let TraitItemKind::Fn(ref sig, _) = item.kind { + if sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first() + { + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + item.ident.name, + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); + } - if item.ident.name == sym::new - && let TraitItemKind::Fn(_, _) = item.kind - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); + new_ret_no_self::check_trait_item(cx, item); } } } @@ -5369,7 +5307,9 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter, [arg]) => { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Forward); + }, (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), @@ -5379,6 +5319,14 @@ impl Methods { } } }, + (sym::next_back, []) => { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) + && let (sym::filter, [arg]) = (name2, args2) + && self.msrv.meets(cx, msrvs::DOUBLE_ENDED_ITERATOR_RFIND) + { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Backward); + } + }, (sym::nth, [n_arg]) => match method_call(recv) { Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( @@ -5712,183 +5660,3 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } - -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - -struct ShouldImplTraitCase { - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - // implicit self kind expected (none, self, &self, ...) - self_kind: SelfKind, - // checks against the output type - output_type: OutType, - // certain methods with explicit lifetimes can't implement the equivalent trait method - lint_explicit_lifetime: bool, -} -impl ShouldImplTraitCase { - const fn new( - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - self_kind: SelfKind, - output_type: OutType, - lint_explicit_lifetime: bool, - ) -> ShouldImplTraitCase { - ShouldImplTraitCase { - trait_name, - method_name, - param_count, - fn_header, - self_kind, - output_type, - lint_explicit_lifetime, - } - } - - fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { - self.lint_explicit_lifetime - || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) - } -} - -#[rustfmt::skip] -const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), -]; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum SelfKind { - Value, - Ref, - RefMut, - No, // When we want the first argument type to be different than `Self` -} - -impl SelfKind { - fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if ty == parent_ty { - true - } else if let Some(boxed_ty) = ty.boxed_ty() { - boxed_ty == parent_ty - } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, args) = ty.kind() { - args.types().next() == Some(parent_ty) - } else { - false - } - } else { - false - } - } - - fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = *ty.kind() { - return m == mutability && t == parent_ty; - } - - let trait_sym = match mutability { - hir::Mutability::Not => sym::AsRef, - hir::Mutability::Mut => sym::AsMut, - }; - - let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { - return false; - }; - implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) - } - - fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - !matches_value(cx, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) - } - - match self { - Self::Value => matches_value(cx, parent_ty, ty), - Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), - Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => matches_none(cx, parent_ty, ty), - } - } - - #[must_use] - fn description(self) -> &'static str { - match self { - Self::Value => "`self` by value", - Self::Ref => "`self` by reference", - Self::RefMut => "`self` by mutable reference", - Self::No => "no `self`", - } - } -} - -#[derive(Clone, Copy)] -enum OutType { - Unit, - Bool, - Any, - Ref, -} - -impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); - match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), - _ => false, - } - } -} - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} diff --git a/clippy_lints/src/methods/new_ret_no_self.rs b/clippy_lints/src/methods/new_ret_no_self.rs new file mode 100644 index 0000000000000..2aa28fbb5f406 --- /dev/null +++ b/clippy_lints/src/methods/new_ret_no_self.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::return_ty; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use rustc_hir::{ImplItem, TraitItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::NEW_RET_NO_SELF; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + implements_trait: bool, +) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + impl_item.span, + "methods called `new` usually return `Self`", + ); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + if trait_item.ident.name == sym::new + && let ret_ty = return_ty(cx, trait_item.owner_id) + && let self_ty = ty::TraitRef::identity(cx.tcx, trait_item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + trait_item.span, + "methods called `new` usually return `Self`", + ); + } +} diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 71b2f251eded6..04e4503e4097f 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -18,7 +19,6 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -26,185 +26,8 @@ pub(super) fn check<'tcx>( name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], + msrv: Msrv, ) { - /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, - /// `or_insert(T::new())` or `or_insert(T::default())`. - /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, - /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: Symbol, - receiver: &hir::Expr<'_>, - fun: &hir::Expr<'_>, - call_expr: Option<&hir::Expr<'_>>, - span: Span, - method_span: Span, - ) -> bool { - if !expr_type_is_certain(cx, receiver) { - return false; - } - - let is_new = |fun: &hir::Expr<'_>| { - if let hir::ExprKind::Path(ref qpath) = fun.kind { - let path = last_path_segment(qpath).ident.name; - matches!(path, sym::new) - } else { - false - } - }; - - let output_type_implements_default = |fun| { - let fun_ty = cx.typeck_results().expr_ty(fun); - if let ty::FnDef(def_id, args) = fun_ty.kind() { - let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); - cx.tcx - .get_diagnostic_item(sym::Default) - .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) - } else { - false - } - }; - - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { - cx.tcx - .inherent_impls(adt_def.did()) - .iter() - .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .find_map(|assoc| { - if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 - { - Some(assoc.def_id) - } else { - None - } - }) - }) else { - return false; - }; - let in_sugg_method_implementation = { - matches!( - suggested_method_def_id.as_local(), - Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id - ) - }; - if in_sugg_method_implementation { - return false; - } - - // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a - // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. - if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { - return false; - } - - // needs to target Default::default in particular or be *::new and have a Default impl - // available - if (is_new(fun) && output_type_implements_default(fun)) - || match call_expr { - Some(call_expr) => is_default_equivalent(cx, call_expr), - None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), - } - { - span_lint_and_sugg( - cx, - UNWRAP_OR_DEFAULT, - method_span.with_hi(span.hi()), - format!("use of `{name}` to construct default value"), - "try", - format!("{sugg}()"), - Applicability::MachineApplicable, - ); - - true - } else { - false - } - } - - /// Checks for `*or(foo())`. - #[expect(clippy::too_many_arguments)] - fn check_or_fn_call<'tcx>( - cx: &LateContext<'tcx>, - name: Symbol, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - // `Some` if fn has second argument - second_arg: Option<&hir::Expr<'_>>, - span: Span, - // None if lambda is required - fun_span: Option, - ) -> bool { - // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ - (sym::BTreeEntry, false, &[sym::or_insert], "with"), - (sym::HashMapEntry, false, &[sym::or_insert], "with"), - ( - sym::Option, - false, - &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], - "else", - ), - (sym::Option, false, &[sym::get_or_insert], "with"), - (sym::Option, true, &[sym::and], "then"), - (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), - (sym::Result, true, &[sym::and], "then"), - ]; - - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) - && switch_to_lazy_eval(cx, arg) - && !contains_return(arg) - && let self_ty = cx.typeck_results().expr_ty(self_expr) - && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES - .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) - { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - format!("function call inside of `{name}`"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - true - } else { - false - } - } - if let [arg] = args { let inner_arg = peel_blocks(arg); for_each_expr(cx, inner_arg, |ex| { @@ -224,11 +47,11 @@ pub(super) fn check<'tcx>( }; (!inner_fun_has_args && !is_nested_expr - && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span, msrv)) || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) }, hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { - check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span, msrv) }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) @@ -272,6 +95,191 @@ pub(super) fn check<'tcx>( } } +/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, +/// `or_insert(T::new())` or `or_insert(T::default())`. +/// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, +/// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +#[expect(clippy::too_many_arguments)] +fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: Symbol, + receiver: &hir::Expr<'_>, + fun: &hir::Expr<'_>, + call_expr: Option<&hir::Expr<'_>>, + span: Span, + method_span: Span, + msrv: Msrv, +) -> bool { + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + + // Check MSRV, but only for `Result::unwrap_or_default` + if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + return false; + } + + if !expr_type_is_certain(cx, receiver) { + return false; + } + + let is_new = |fun: &hir::Expr<'_>| { + if let hir::ExprKind::Path(ref qpath) = fun.kind { + let path = last_path_segment(qpath).ident.name; + matches!(path, sym::new) + } else { + false + } + }; + + let output_type_implements_default = |fun| { + let fun_ty = cx.typeck_results().expr_ty(fun); + if let ty::FnDef(def_id, args) = fun_ty.kind() { + let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); + cx.tcx + .get_diagnostic_item(sym::Default) + .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) + } else { + false + } + }; + + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { + cx.tcx + .inherent_impls(adt_def.did()) + .iter() + .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) + .find_map(|assoc| { + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { + Some(assoc.def_id) + } else { + None + } + }) + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { + return false; + } + + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), + } + { + span_lint_and_sugg( + cx, + UNWRAP_OR_DEFAULT, + method_span.with_hi(span.hi()), + format!("use of `{name}` to construct default value"), + "try", + format!("{sugg}()"), + Applicability::MachineApplicable, + ); + + true + } else { + false + } +} + +/// Checks for `*or(foo())`. +#[expect(clippy::too_many_arguments)] +fn check_or_fn_call<'tcx>( + cx: &LateContext<'tcx>, + name: Symbol, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, + span: Span, + // None if lambda is required + fun_span: Option, +) -> bool { + // (path, fn_has_argument, methods, suffix) + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), + (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), + ]; + + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES + .iter() + .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() + }; + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + format!("function call inside of `{name}`"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); + true + } else { + false + } +} + fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { let body = cx.tcx.hir_body(body); diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 3a5e321720865..e13df18333e46 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; @@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) { - span_lint_and_sugg( + span_lint_and_then( cx, RANGE_ZIP_WITH_LEN, expr.span, "using `.zip()` with a range and `.len()`", - "try", - format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")), - Applicability::MachineApplicable, + |diag| { + // If the iterator content is consumed by a pattern with exactly two elements, swap + // the order of those elements. Otherwise, the suggestion will be marked as + // `Applicability::MaybeIncorrect` (because it will be), and a note will be added + // to the diagnostic to underline the swapping of the index and the content. + let pat = methods_pattern(cx, expr).or_else(|| for_loop_pattern(cx, expr)); + let invert_bindings = if let Some(pat) = pat + && pat.span.eq_ctxt(expr.span) + && let PatKind::Tuple([first, second], _) = pat.kind + { + Some((first.span, second.span)) + } else { + None + }; + let mut app = Applicability::MachineApplicable; + let mut suggestions = vec![( + expr.span, + format!( + "{}.iter().enumerate()", + snippet_with_applicability(cx, recv.span, "_", &mut app) + ), + )]; + if let Some((left, right)) = invert_bindings + && let Some(snip_left) = left.get_source_text(cx) + && let Some(snip_right) = right.get_source_text(cx) + { + suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); + } else { + app = Applicability::MaybeIncorrect; + } + diag.multipart_suggestion("use", suggestions, app); + if app != Applicability::MachineApplicable { + diag.note("the order of the element and the index will be swapped"); + } + }, ); } } + +/// If `expr` is the argument of a `for` loop, return the loop pattern. +fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let Node::Expr(ancestor_expr) = node + && let Some(for_loop) = higher::ForLoop::hir(ancestor_expr) + && for_loop.arg.hir_id == expr.hir_id + { + Some(for_loop.pat) + } else { + None + } + }) +} + +/// If `expr` is the receiver of an `Iterator` method which consumes the iterator elements and feed +/// them to a closure, return the pattern of the closure. +fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind + && recv.hir_id == expr.hir_id + && matches!( + method.ident.name, + sym::all + | sym::any + | sym::filter_map + | sym::find_map + | sym::flat_map + | sym::for_each + | sym::is_partitioned + | sym::is_sorted_by_key + | sym::map + | sym::map_while + | sym::position + | sym::rposition + | sym::try_for_each + ) + && let ExprKind::Closure(closure) = arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let [param] = body.params + { + Some(param.pat) + } else { + None + } +} diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 7837517ed5d8a..9111604ef53b7 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval_local(repeat_arg, expr.span.ctxt()) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs new file mode 100644 index 0000000000000..599ff696f6aed --- /dev/null +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -0,0 +1,156 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_bool, sym}; +use rustc_abi::ExternAbi; +use rustc_hir::{self as hir, FnRetTy, FnSig, GenericParamKind, ImplItem, LifetimeParamKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; +use rustc_span::{Symbol, kw}; + +use super::SHOULD_IMPLEMENT_TRAIT; +use super::lib::SelfKind; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + impl_implements_trait: bool, + first_arg_ty_opt: Option>, + sig: &FnSig<'_>, +) { + // if this impl block implements a trait, lint in trait definition instead + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + // check missing trait implementations + && let Some(method_config) = TRAIT_METHODS.iter().find(|case| case.method_name == impl_item.ident.name) + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && sig.header.is_safe() + && !sig.header.is_const() + && !sig.header.is_async() + && sig.header.abi == ExternAbi::Rust + && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); + } +} + +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, + in_prelude_since: Edition, +} + +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + in_prelude_since: Edition, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + self_kind, + output_type, + lint_explicit_lifetime, + in_prelude_since, + } + } + + fn lifetime_param_cond(&self, impl_item: &ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit + } + ) + }) + } +} + +#[rustfmt::skip] +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, SelfKind::Value, OutType::Any, true, Edition2015), +]; + +#[derive(Clone, Copy)] +enum OutType { + Unit, + Bool, + Any, + Ref, +} + +impl OutType { + fn matches(self, ty: &FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); + match (self, ty) { + (Self::Unit, &FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + _ => false, + } + } +} diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ef3d7acdc01ef..1248d1658d77e 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -1,14 +1,80 @@ -use crate::methods::{single_char_insert_string, single_char_push_string}; -use rustc_hir as hir; +use super::SINGLE_CHAR_ADD_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - match cx.tcx.get_diagnostic_name(fn_def_id) { - Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), - Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), - _ => {}, + let mut applicability = Applicability::MachineApplicable; + let (short_name, arg, extra) = match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_insert_str) => ( + "insert", + &args[1], + Some(|applicability| { + format!( + "{}, ", + snippet_with_applicability(cx, args[0].span, "..", applicability) + ) + }), + ), + Some(sym::string_push_str) => ("push", &args[0], None), + _ => return, + }; + + if let Some(extension_string) = str_literal_to_char_literal(cx, arg, &mut applicability, false) { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character string literal"), + format!("consider using `{short_name}` with a character literal"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); + } else if let ExprKind::AddrOf(BorrowKind::Ref, _, inner) = arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = inner.kind + && path_segment.ident.name == sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + let extension_string = match ( + snippet_with_applicability(cx, method_arg.span.source_callsite(), "_", &mut applicability), + is_ref_char(cx, method_arg), + ) { + (snippet, false) => snippet, + (snippet, true) => format!("*{snippet}").into(), + }; + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character converted to string"), + format!("consider using `{short_name}` without `to_string()`"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); } } } + +fn is_ref_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::Ref(_, ty, _) if ty.is_char()) +} + +fn is_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() +} diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs deleted file mode 100644 index 4a1d25deade90..0000000000000 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ /dev/null @@ -1,67 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `insert_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character converted to string", - "consider using `insert` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs deleted file mode 100644 index bc271d593925e..0000000000000 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `push_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{base_string_snippet}.push({extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character converted to string", - "consider using `push` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 51dd4ac313a6d..8daa5db887acf 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -304,7 +304,7 @@ fn parse_iter_usage<'tcx>( }; }, (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval_local(idx_expr, ctxt) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index b87d81b710269..bf91a469e7f01 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; @@ -25,8 +25,9 @@ pub(super) fn check<'tcx>( && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + let ctxt = expr.span.ctxt(); + if let Some(left) = ecx.eval_local(recv, ctxt) + && let Some(right) = ecx.eval_local(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index ad9b3c3645425..74b297c13621e 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,12 +1,13 @@ -use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; +use itertools::Itertools; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; +use super::lib::SelfKind; #[rustfmt::skip] const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ @@ -61,20 +62,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{this}`").fmt(f), - Self::StartsWith(this) => format!("`{this}*`").fmt(f), - Self::EndsWith(this) => format!("`*{this}`").fmt(f), - Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), + Self::Eq(this) => write!(f, "`{this}`"), + Self::StartsWith(this) => write!(f, "`{this}*`"), + Self::EndsWith(this) => write!(f, "`*{this}`"), + Self::NotEndsWith(this) => write!(f, "`~{this}`"), Self::IsSelfTypeCopy(is_true) => { - format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + write!(f, "`self` type is{} `Copy`", if is_true { "" } else { " not" }) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{negation} implement{s_suffix} a trait").fmt(f) + write!(f, "method{negation} implement{s_suffix} a trait") }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{suffix} a trait item").fmt(f) + write!(f, "method{suffix} a trait item") }, } } @@ -115,18 +116,9 @@ pub(super) fn check<'tcx>( let s = conventions .iter() - .filter_map(|conv| { - if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) - || matches!(conv, Convention::ImplementsTrait(_)) - || matches!(conv, Convention::IsTraitItem(_)) - { - None - } else { - Some(conv.to_string()) - } - }) - .collect::>() - .join(" and "); + .filter(|conv| !(cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))) + .filter(|conv| !matches!(conv, Convention::ImplementsTrait(_) | Convention::IsTraitItem(_))) + .format(" and "); format!("methods with the following characteristics: ({s})") } else { @@ -140,11 +132,7 @@ pub(super) fn check<'tcx>( first_arg_span, format!( "{suggestion} usually take {}", - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") + self_kinds.iter().map(|k| k.description()).format(" or ") ), None, "consider choosing a less ambiguous name", diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 64eafc0ebccdc..f9a7c562c7a59 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -4,6 +4,7 @@ use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -60,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -68,8 +69,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM .qpath_res(qpath, path.hir_id) .opt_def_id() .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min), - Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max), + Some(sym::cmp_min) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Min), + Some(sym::cmp_max) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Max), _ => None, }) } else { @@ -79,8 +80,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { match path.ident.name { - sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), _ => None, } } else { @@ -91,12 +92,13 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM } } -fn fetch_const<'a, 'tcx>( - cx: &LateContext<'tcx>, +fn fetch_const<'a>( + cx: &LateContext<'_>, + ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, -) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +) -> Option<(MinMax, Constant, &'a Expr<'a>)> { let mut args = receiver.into_iter().chain(args); let first_arg = args.next()?; let second_arg = args.next()?; @@ -104,7 +106,7 @@ fn fetch_const<'a, 'tcx>( return None; } let ecx = ConstEvalCtxt::new(cx); - match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + match (ecx.eval_local(first_arg, ctxt), ecx.eval_local(second_arg, ctxt)) { (Some(c), None) => Some((m, c, second_arg)), (None, Some(c)) => Some((m, c, first_arg)), // otherwise ignore diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 98614baffcea6..f132b90ac4f2e 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_ast::ast::{self, Inline, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{FileName, SourceFile, Span, SyntaxContext}; -use std::ffi::OsStr; -use std::path::{Component, Path}; +use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; declare_clippy_lint! { /// ### What it does @@ -60,107 +59,97 @@ declare_clippy_lint! { /// mod.rs /// lib.rs /// ``` - #[clippy::version = "1.57.0"] pub SELF_NAMED_MODULE_FILES, restriction, "checks that module layout is consistent" } -pub struct ModStyle; - impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); +pub struct ModState { + contains_external: bool, + has_path_attr: bool, + mod_file: Arc, +} + +#[derive(Default)] +pub struct ModStyle { + working_dir: Option, + module_stack: Vec, +} + impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf); + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, mod_spans, ..)) = &item.kind { + let has_path_attr = item.attrs.iter().any(|attr| attr.has_name(sym::path)); + if !has_path_attr && let Some(current) = self.module_stack.last_mut() { + current.contains_external = true; + } + let mod_file = cx.sess().source_map().lookup_source_file(mod_spans.inner_span.lo()); + self.module_stack.push(ModState { + contains_external: false, + has_path_attr, + mod_file, + }); + } + } - let files = cx.sess().source_map().files(); - - let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { + fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow + { return; - }; - - // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives - // `[path, to]` but not foo - let mut folder_segments = FxIndexSet::default(); - // `mod_folders` is all the unique folder names that contain a mod.rs file - let mut mod_folders = FxHashSet::default(); - // `file_map` maps file names to the full path including the file name - // `{ foo => path/to/foo.rs, .. } - let mut file_map = FxHashMap::default(); - for file in files.iter() { - if let FileName::Real(name) = &file.name - && let Some(lp) = name.local_path() - && file.cnum == LOCAL_CRATE - { - // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887) - // Only check files in the current crate. - // Fix false positive that crate dependency in workspace sub directory - // is checked unintentionally. - let path = if lp.is_relative() { - lp - } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { - relative - } else { - continue; - }; - - if let Some(stem) = path.file_stem() { - file_map.insert(stem, (file, path)); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - } } - for folder in &folder_segments { - if !mod_folders.contains(folder) - && let Some((file, path)) = file_map.get(folder) - { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, ..)) = &item.kind + && let Some(current) = self.module_stack.pop() + && !current.has_path_attr + { + let Some(path) = self + .working_dir + .as_ref() + .and_then(|src| try_trim_file_path_prefix(¤t.mod_file, src)) + else { + return; + }; + if current.contains_external { + check_self_named_module(cx, path, ¤t.mod_file); } + check_mod_module(cx, path, ¤t.mod_file); } } } -/// For each `path` we add each folder component to `folder_segments` and if the file name -/// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files<'a>( - path: &'a Path, - folder_segments: &mut FxIndexSet<&'a OsStr>, - mod_folders: &mut FxHashSet<&'a OsStr>, -) { - let mut comp = path.components().rev().peekable(); - let _: Option<_> = comp.next(); - if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); +fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if !path.ends_with("mod.rs") { + let mut mod_folder = path.with_extension(""); + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + mod_folder.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), mod_folder.display())); + }, + ); } - let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); - folder_segments.extend(folders); } -/// Checks every path for the presence of `mod.rs` files and emits the lint if found. /// We should not emit a lint for test modules in the presence of `mod.rs`. /// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) /// for code-sharing between tests. -fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { +fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { span_lint_and_then( cx, @@ -177,3 +166,17 @@ fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &Source ); } } + +fn try_trim_file_path_prefix<'a>(file: &'a SourceFile, prefix: &'a Path) -> Option<&'a Path> { + if let FileName::Real(name) = &file.name + && let Some(mut path) = name.local_path() + && file.cnum == LOCAL_CRATE + { + if !path.is_relative() { + path = path.strip_prefix(prefix).ok()?; + } + Some(path) + } else { + None + } +} diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d98c70e7f5a85..588afd85afb02 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,31 +1,51 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; -use rustc_hir::{self as hir, AmbigArg, intravisit}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, AmbigArg, BorrowKind, Expr, ExprKind, HirId, Mutability, TyKind, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does /// Checks for instances of `mut mut` references. /// /// ### Why is this bad? - /// Multiple `mut`s don't add anything meaningful to the - /// source. This is either a copy'n'paste error, or it shows a fundamental - /// misunderstanding of references. + /// This is usually just a typo or a misunderstanding of how references work. /// /// ### Example /// ```no_run - /// # let mut y = 1; - /// let x = &mut &mut y; + /// let x = &mut &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut x; + /// + /// fn foo(x: &mut &mut u32) {} + /// ``` + /// Use instead + /// ```no_run + /// let x = &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut *x; // reborrow + /// + /// fn foo(x: &mut u32) {} /// ``` #[clippy::version = "pre 1.29.0"] pub MUT_MUT, pedantic, - "usage of double-mut refs, e.g., `&mut &mut ...`" + "usage of double mut-refs, e.g., `&mut &mut ...`" } -declare_lint_pass!(MutMut => [MUT_MUT]); +impl_lint_pass!(MutMut => [MUT_MUT]); + +#[derive(Default)] +pub(crate) struct MutMut { + seen_tys: FxHashSet, +} impl<'tcx> LateLintPass<'tcx> for MutMut { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { @@ -33,17 +53,48 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) { - if let hir::TyKind::Ref(_, mty) = ty.kind - && mty.mutbl == hir::Mutability::Mut - && let hir::TyKind::Ref(_, mty) = mty.ty.kind - && mty.mutbl == hir::Mutability::Mut + if let TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == Mutability::Mut + && let TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { - span_lint( + if self.seen_tys.contains(&ty.hir_id) { + // we have 2+ `&mut`s, e.g., `&mut &mut &mut x` + // and we have already flagged on the outermost `&mut &mut (&mut x)`, + // so don't flag the inner `&mut &mut (x)` + return; + } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut t, mut t2) = (mty.ty, mty2.ty); + let mut many_muts = false; + loop { + // this should allow us to remember all the nested types, so that the `contains` + // above fails faster + self.seen_tys.insert(t.hir_id); + if let TyKind::Ref(_, next) = t2.kind + && next.mutbl == Mutability::Mut + { + (t, t2) = (t2, next.ty); + many_muts = true; + } else { + break; + } + } + + let mut applicability = Applicability::MaybeIncorrect; + let sugg = snippet_with_applicability(cx.sess(), t.span, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; + span_lint_and_sugg( cx, MUT_MUT, ty.span, - "generally you want to avoid `&mut &mut _` if possible", + "a type of form `&mut &mut _`", + format!("remove the extra `&mut`{suffix}"), + sugg.to_string(), + applicability, ); } } @@ -54,7 +105,7 @@ pub struct MutVisitor<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(self.cx.sess().source_map()) { return; } @@ -68,24 +119,60 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { - span_lint_hir( + } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e2) = e.kind { + if !expr.span.eq_ctxt(e.span) { + return; + } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut e, mut e2) = (e, e2); + let mut many_muts = false; + loop { + if !e.span.eq_ctxt(e2.span) { + return; + } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, next) = e2.kind { + (e, e2) = (e2, next); + many_muts = true; + } else { + break; + } + } + + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "generally you want to avoid `&mut &mut _` if possible", + "an expression of form `&mut &mut _`", + |diag| { + diag.span_suggestion( + expr.span, + format!("remove the extra `&mut`{suffix}"), + sugg, + applicability, + ); + }, ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability).mut_addr_deref(); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", + "this expression mutably borrows a mutable reference", + |diag| { + diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability); + }, ); } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 32ded96c1236f..455d1426aa8cb 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use clippy_utils::{is_self, peel_hir_ty_options}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Attribute, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, - PatKind, QPath, TyKind, + Attribute, BindingMode, Body, ExprKind, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, + Node, PatKind, QPath, TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_lint::{LateContext, LateLintPass}; @@ -19,10 +19,13 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; +use std::borrow::Cow; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for functions taking arguments by value, but not @@ -217,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -260,12 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = get_spans( - cx, - Some(body.id()), - idx, - &[(sym::clone, ".to_string()"), (sym::as_str, "")], - ) + && let Some(clone_spans) = + get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diag.span_suggestion( input.span, @@ -340,3 +339,43 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } + +fn get_spans<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Body<'_>, + idx: usize, + replacements: &[(Symbol, &'static str)], +) -> Option)>> { + if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { + extract_clone_suggestions(cx, binding_id, replacements, body) + } else { + Some(vec![]) + } +} + +fn extract_clone_suggestions<'tcx>( + cx: &LateContext<'tcx>, + id: HirId, + replace: &[(Symbol, &'static str)], + body: &'tcx Body<'_>, +) -> Option)>> { + let mut spans = Vec::new(); + for_each_expr_without_closures(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.name == sym::capacity { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.name == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }) + .is_none() + .then_some(spans) +} diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index b598a390005ba..6fc034b6fc5d2 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; @@ -58,116 +58,132 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if let hir::ItemKind::Impl(hir::Impl { + let hir::ItemKind::Impl(hir::Impl { of_trait: None, generics, self_ty: impl_self_ty, .. }) = item.kind + else { + return; + }; + + for assoc_item in cx + .tcx + .associated_items(item.owner_id.def_id) + .filter_by_name_unhygienic(sym::new) { - for assoc_item in cx - .tcx - .associated_items(item.owner_id.def_id) - .filter_by_name_unhygienic(sym::new) + if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind + && let assoc_item_hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local()) + && let impl_item = cx.tcx.hir_node(assoc_item_hir_id).expect_impl_item() + && !impl_item.span.in_external_macro(cx.sess().source_map()) + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind + && let id = impl_item.owner_id + // can't be implemented for unsafe new + && !sig.header.is_unsafe() + // shouldn't be implemented when it is hidden in docs + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) + // when the result of `new()` depends on a parameter we should not require + // an impl of `Default` + && impl_item.generics.params.is_empty() + && sig.decl.inputs.is_empty() + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && self_ty == return_ty(cx, impl_item.owner_id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { - if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind { - let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item(); - if impl_item.span.in_external_macro(cx.sess().source_map()) { - return; - } - if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { - let id = impl_item.owner_id; - if sig.header.is_unsafe() { - // can't be implemented for unsafe new - return; - } - if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { - // shouldn't be implemented when it is hidden in docs - return; - } - if !impl_item.generics.params.is_empty() { - // when the result of `new()` depends on a parameter we should not require - // an impl of `Default` - return; - } - if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) - && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() - && self_ty == return_ty(cx, impl_item.owner_id) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + for &d in cx.tcx.local_trait_impls(default_trait_id) { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - for &d in cx.tcx.local_trait_impls(default_trait_id) { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() - && let Some(local_def_id) = ty_def.did().as_local() - { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } - } - self.impling_types = Some(impls); - } + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); + } + } + self.impling_types = Some(impls); + } - // Check if a Default implementation exists for the Self type, regardless of - // generics - if let Some(ref impling_types) = self.impling_types - && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() - && let Some(self_def) = self_def.ty_adt_def() - && let Some(self_local_did) = self_def.did().as_local() - && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) - && impling_types.contains(&self_id) - { - return; - } + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(self_def) = self_def.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) + && impling_types.contains(&self_id) + { + return; + } - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg, - ), - Applicability::MachineApplicable, - ); - }, - ); + let mut app = Applicability::MachineApplicable; + let attrs_sugg = { + let mut sugg = String::new(); + for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { + if !attr.has_name(sym::cfg_trace) { + // This might be some other attribute that the `impl Default` ought to inherit. + // But it could also be one of the many attributes that: + // - can't be put on an impl block -- like `#[inline]` + // - we can't even build a suggestion for, since `Attribute::span` may panic. + // + // Because of all that, remain on the safer side -- don't inherit this attr, and just + // reduce the applicability + app = Applicability::MaybeIncorrect; + continue; } + + sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); + sugg.push('\n'); } - } + sugg + }; + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!( + "\n{}\n", + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) + ) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut app); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &attrs_sugg, + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + app, + ); + }, + ); } } } } fn create_new_without_default_suggest_msg( + attrs_sugg: &str, self_type_snip: &str, generics_sugg: &str, where_clause_sugg: &str, ) -> String { #[rustfmt::skip] format!( -"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ +"{attrs_sugg}impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ fn default() -> Self {{ Self::new() }} diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index ba67dc62abbda..e531f797272d2 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,9 +5,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::EarlyBinder; +use rustc_middle::ty::{EarlyBinder, TraitRef}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -112,140 +112,146 @@ declare_clippy_lint! { declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); impl LateLintPass<'_> for NonCanonicalImpls { - #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { - return; - }; - let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { - return; - }; - if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { - return; + if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind + && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) + // NOTE: check this early to avoid expensive checks that come after this one + && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) + && let body = cx.tcx.hir_body(impl_item_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + && !is_from_proc_macro(cx, impl_item) + { + if trait_name == Some(sym::Clone) + && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) + && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + { + check_clone_on_copy(cx, impl_item, block); + } else if trait_name == Some(sym::PartialOrd) + && impl_item.ident.name == sym::partial_cmp + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) + { + check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + } } - let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind else { - return; - }; - let body = cx.tcx.hir_body(impl_item_id); - let ExprKind::Block(block, ..) = body.value.kind else { - return; - }; - if block.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, impl_item) { + } +} + +fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &Block<'_>) { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + // this is the canonical implementation, `fn clone(&self) -> Self { *self }` return; } - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + } - return; - } - } + if impl_item.ident.name == sym::clone_from { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), + Applicability::MaybeIncorrect, + ); + } +} - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, - ); - } - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; +fn check_partial_ord_on_ord<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &ImplItem<'_>, + item: &Item<'_>, + trait_impl: &TraitRef<'_>, + body: &Body<'_>, + block: &Block<'tcx>, +) { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; + } + + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { + }; + let Some(std_or_core) = std_or_core(cx) else { return; - } - - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; - - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; + }; - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] }, - ); - } - } + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; + + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); } /// Return true if `expr_kind` is a `cmp` call. @@ -256,26 +262,27 @@ fn expr_is_cmp<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { let impl_item_did = impl_item.owner_id.def_id; - if let ExprKind::Call( - Expr { - kind: ExprKind::Path(some_path), - hir_id: some_hir_id, - .. - }, - [cmp_expr], - ) = expr.kind - { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) => { + is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) - } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) - } else { - false + }, + ExprKind::MethodCall(_, recv, [], _) => { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + }, + _ => false, } } diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 83f7d9319697b..3a8a4dd0c7137 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -16,8 +16,8 @@ declare_clippy_lint! { /// Checks that common macros are used with consistent bracing. /// /// ### Why is this bad? - /// This is mostly a consistency lint although using () or [] - /// doesn't give you a semicolon in item position, which can be unexpected. + /// Having non-conventional braces on well-stablished macros can be confusing + /// when debugging, and they bring incosistencies with the rest of the ecosystem. /// /// ### Example /// ```no_run @@ -33,8 +33,12 @@ declare_clippy_lint! { "check consistent use of braces in macro" } -/// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo = (Span, (char, char), SourceText); +struct MacroInfo { + callsite_span: Span, + callsite_snippet: SourceText, + old_open_brace: char, + braces: (char, char), +} pub struct MacroBraces { macro_braces: FxHashMap, @@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, item.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + old_open_brace, + }) = is_offending_macro(cx, stmt.span, self) + { + // if we turn `macro!{}` into `macro!()`/`macro![]`, we'll no longer get the implicit + // trailing semicolon, see #9913 + // NOTE: `stmt.kind != StmtKind::MacCall` because `EarlyLintPass` happens after macro expansion + let add_semi = matches!(stmt.kind, ast::StmtKind::Expr(..)) && old_open_brace == '{'; + emit_help(cx, &callsite_snippet, braces, callsite_span, add_semi); + self.done.insert(callsite_span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, expr.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + braces, + callsite_snippet, + .. + }) = is_offending_macro(cx, ty.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } } @@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace .last() .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; - let span_call_site = span.ctxt().outer_expn_data().call_site; + let callsite_span = span.ctxt().outer_expn_data().call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = span_call_site.get_source_text(cx) + && let Some(snip) = callsite_span.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - && snip.starts_with(&format!("{name}!")) + && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) + && let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next() + && old_open_brace != braces.0 && unnested_or_local() - // make formatting consistent - && let c = snip.replace(' ', "") - && !c.starts_with(&format!("{name}!{}", braces.0)) - && !mac_braces.done.contains(&span_call_site) + && !mac_braces.done.contains(&callsite_span) { - Some((span_call_site, braces, snip)) + Some(MacroInfo { + callsite_span, + callsite_snippet: snip, + old_open_brace, + braces, + }) } else { None } } -fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span) { +fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) { + let semi = if add_semi { ";" } else { "" }; if let Some((macro_name, macro_args_str)) = snip.split_once('!') { let mut macro_args = macro_args_str.trim().to_string(); // now remove the wrong braces - macro_args.remove(0); macro_args.pop(); + macro_args.remove(0); span_lint_and_sugg( cx, NONSTANDARD_MACRO_BRACES, span, format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", - format!("{macro_name}!{open}{macro_args}{close}"), + format!("{macro_name}!{open}{macro_args}{close}{semi}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a21c361356e83..ec8c2299d8cbb 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -24,6 +24,33 @@ declare_clippy_lint! { /// the calculations have no side-effects (function calls or mutating dereference) /// and the assigned variables are also only in recursion, it is useless. /// + /// ### Example + /// ```no_run + /// fn f(a: usize, b: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1, b + 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1, 1)); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(a: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1)); + /// # } + /// ``` + /// /// ### Known problems /// Too many code paths in the linting code are currently untested and prone to produce false /// positives or are prone to have performance implications. @@ -51,39 +78,90 @@ declare_clippy_lint! { /// - struct pattern binding /// /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.61.0"] + pub ONLY_USED_IN_RECURSION, + complexity, + "arguments that is only used in recursion can be removed" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `self` receiver that is only used in recursion with no side-effects. + /// + /// ### Why is this bad? + /// + /// It may be possible to remove the `self` argument, allowing the function to be + /// used without an object of type `Self`. /// /// ### Example /// ```no_run - /// fn f(a: usize, b: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1, b + 1) + /// struct Foo; + /// impl Foo { + /// fn f(&self, n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * self.f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1, 1)); + /// # print!("{}", Foo.f(10)); /// # } /// ``` /// Use instead: /// ```no_run - /// fn f(a: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1) + /// struct Foo; + /// impl Foo { + /// fn f(n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * Self::f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1)); + /// # print!("{}", Foo::f(10)); /// # } /// ``` - #[clippy::version = "1.61.0"] - pub ONLY_USED_IN_RECURSION, - complexity, - "arguments that is only used in recursion can be removed" + /// + /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// + /// In some cases, this would not catch all useless arguments. + /// + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn foo(&self, a: usize) -> usize { + /// let f = |x| x; + /// + /// if a == 0 { + /// 1 + /// } else { + /// f(self).foo(a) + /// } + /// } + /// } + /// ``` + /// + /// For example, here `self` is only used in recursion, but the lint would not catch it. + /// + /// List of some examples that can not be caught: + /// - binary operation of non-primitive types + /// - closure usage + /// - some `break` relative operations + /// - struct pattern binding + /// + /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.92.0"] + pub SELF_ONLY_USED_IN_RECURSION, + pedantic, + "self receiver only used to recursively call method can be removed" } -impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION, SELF_ONLY_USED_IN_RECURSION]); #[derive(Clone, Copy)] enum FnKind { @@ -357,26 +435,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { self.params.flag_for_linting(); for param in &self.params.params { if param.apply_lint.get() { - span_lint_and_then( - cx, - ONLY_USED_IN_RECURSION, - param.ident.span, - "parameter is only used in recursion", - |diag| { - if param.ident.name != kw::SelfLower { + if param.ident.name == kw::SelfLower { + span_lint_and_then( + cx, + SELF_ONLY_USED_IN_RECURSION, + param.ident.span, + "`self` is only used in recursion", + |diag| { + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "`self` used here", + ); + }, + ); + } else { + span_lint_and_then( + cx, + ONLY_USED_IN_RECURSION, + param.ident.span, + "parameter is only used in recursion", + |diag| { diag.span_suggestion( param.ident.span, "if this is intentional, prefix it with an underscore", format!("_{}", param.ident.name), Applicability::MaybeIncorrect, ); - } - diag.span_note( - param.uses.iter().map(|x| x.span).collect::>(), - "parameter used here", - ); - }, - ); + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "parameter used here", + ); + }, + ); + } } } self.params.clear(); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index ea5b81aec31ea..e062e55dad894 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -190,7 +190,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_some() { return; } if !matches!( @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_local(receiver, expr.span.ctxt()).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 10455d3b93a00..56001a185771a 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -22,7 +22,7 @@ fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { +) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 6c9be7c5e90b3..d897b0e8dd918 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 8f5ee390f7222..ae1a94a3e23f0 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -39,7 +39,9 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_local(op, parent.span.ctxt()) + == Some(Constant::Int(0)) + { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index ded161c8576a1..eb2353cfd90bf 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -18,12 +18,13 @@ pub(crate) fn check<'tcx>( ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let ecx = ConstEvalCtxt::new(cx); - let left_is_local = match ecx.eval_with_source(left) { + let ctxt = expr.span.ctxt(); + let left_is_local = match ecx.eval_with_source(left, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match ecx.eval_with_source(right) { + let right_is_local = match ecx.eval_with_source(right, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, @@ -84,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static } } -fn is_allowed(val: &Constant<'_>) -> bool { +fn is_allowed(val: &Constant) -> bool { match val { // FIXME(f16_f128): add when equality check is available on all platforms &Constant::F32(f) => f == 0.0 || f.is_infinite(), diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 3efbb8963587f..43c62e1e131a3 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, kw}; +use rustc_span::{Span, SyntaxContext, kw}; use super::IDENTITY_OP; @@ -41,42 +41,43 @@ pub(crate) fn check<'tcx>( (span, is_coerced) }; + let ctxt = expr.span.ctxt(); match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if is_redundant_op(cx, left, 0) { + if is_redundant_op(cx, left, 0, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 0) { + } else if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0) { + if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Mul => { - if is_redundant_op(cx, left, 1) { + if is_redundant_op(cx, left, 1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 1) { + } else if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Div => { - if is_redundant_op(cx, right, 1) { + if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::BitAnd => { - if is_redundant_op(cx, left, -1) { + if is_redundant_op(cx, left, -1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, -1) { + } else if is_redundant_op(cx, right, -1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } @@ -184,14 +185,17 @@ fn is_allowed<'tcx>( // This lint applies to integers and their references cx.typeck_results().expr_ty(left).peel_refs().is_integral() - && cx.typeck_results().expr_ty(right).peel_refs().is_integral() + && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1)) + && !(cmp == BinOpKind::Shl + && is_zero_integer_const(cx, right, expr.span.ctxt()) + && integer_const(cx, left, expr.span.ctxt()) == Some(1)) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { let ecx = ConstEvalCtxt::new(cx); - if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { + let ctxt = span.ctxt(); + if match (ecx.eval_full_int(left, ctxt), ecx.eval_full_int(right, ctxt)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -200,8 +204,8 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool { - if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { +fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, ctxt: SyntaxContext) -> bool { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_local(e, ctxt).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs index 55bb78cfce5fc..0b9bd4fb6d320 100644 --- a/clippy_lints/src/operators/manual_is_multiple_of.rs +++ b/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -13,14 +13,14 @@ use super::MANUAL_IS_MULTIPLE_OF; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, msrv: Msrv, ) { if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF) - && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs) + && let Some(operand) = uint_compare_to_zero(cx, expr, op, lhs, rhs) && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind && operand_op.node == BinOpKind::Rem && matches!( @@ -57,18 +57,19 @@ pub(super) fn check<'tcx>( // If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand fn uint_compare_to_zero<'tcx>( cx: &LateContext<'tcx>, + e: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, ) -> Option<&'tcx Expr<'tcx>> { let operand = if matches!(lhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt) - && is_zero_integer_const(cx, rhs) + && is_zero_integer_const(cx, rhs, e.span.ctxt()) { lhs } else if matches!(rhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt) - && is_zero_integer_const(cx, lhs) + && is_zero_integer_const(cx, lhs, e.span.ctxt()) { rhs } else { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index bdbbb3475cd5f..aaea4ff11fc37 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -854,7 +854,7 @@ declare_clippy_lint! { /// println!("{a} is divisible by {b}"); /// } /// ``` - #[clippy::version = "1.89.0"] + #[clippy::version = "1.90.0"] pub MANUAL_IS_MULTIPLE_OF, complexity, "manual implementation of `.is_multiple_of()`" diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index b79461663d7bf..ffe91fc2cef6d 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -39,7 +39,9 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && let BinOpKind::Eq | BinOpKind::Ne = op.node { let ecx = ConstEvalCtxt::new(cx); - matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + let ctxt = expr.span.ctxt(); + matches!(ecx.eval_local(lhs, ctxt), Some(Constant::Int(0))) + || matches!(ecx.eval_local(rhs, ctxt), Some(Constant::Int(0))) } else { false } @@ -55,7 +57,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> match ConstEvalCtxt::new(cx).eval(operand)? { Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { - let value = sext(cx.tcx, v, ity); + let value: i128 = sext(cx.tcx, v, ity); Some(OperandInfo { string_representation: Some(value.to_string()), is_negative: value < 0, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 9b1b063c4737d..622f328f369a5 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4aa100a50e053..d3a5a5dddfbeb 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -6,7 +6,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, @@ -483,6 +484,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .filter(|e| *e) .is_none() { + if !is_copy(cx, caller_ty) + && let Some(hir_id) = path_to_local(let_expr) + && local_used_after_expr(cx, hir_id, expr) + { + return; + } + let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 03d00ba849f3c..0b2313cb7eeb9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -299,8 +299,8 @@ fn check_possible_range_contains( } } -struct RangeBounds<'a, 'tcx> { - val: Constant<'tcx>, +struct RangeBounds<'a> { + val: Constant, expr: &'a Expr<'a>, id: HirId, name_span: Span, @@ -312,7 +312,7 @@ struct RangeBounds<'a, 'tcx> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs new file mode 100644 index 0000000000000..fde8c3d9a9a76 --- /dev/null +++ b/clippy_lints/src/time_subtraction.rs @@ -0,0 +1,216 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_path_diagnostic_item, ty}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```no_run + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```no_run + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between an `Instant` and a `Duration`, or between two `Duration` values. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to + /// unintentional panics. + /// + /// ### Example + /// ```no_run + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1 - dur2; + /// ``` + /// + /// Use instead: + /// ```no_run + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1.checked_sub(dur2); + /// ``` + #[clippy::version = "1.67.0"] + pub UNCHECKED_TIME_SUBTRACTION, + pedantic, + "finds unchecked subtraction involving 'Duration' or 'Instant'" +} + +pub struct UncheckedTimeSubtraction { + msrv: Msrv, +} + +impl UncheckedTimeSubtraction { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +impl_lint_pass!(UncheckedTimeSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); + +impl LateLintPass<'_> for UncheckedTimeSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + { + let typeck = cx.typeck_results(); + let lhs_ty = typeck.expr_ty(lhs); + let rhs_ty = typeck.expr_ty(rhs); + + if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + // Instant::now() - instant + if is_instant_now_call(cx, lhs) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } + // instant - duration + else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (instant - dur1) - dur2, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + ); + } else { + // instant - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (dur1 - dur2) - dur3, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction between 'Duration' values", + ); + } else { + // duration - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } + } +} + +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + { + true + } else { + false + } +} + +/// Returns true if this subtraction is part of a chain like `(a - b) - c` +fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { + if let ExprKind::Binary(op, inner_lhs, inner_rhs) = &lhs.kind + && matches!(op.node, BinOpKind::Sub) + { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(inner_lhs); + let right_ty = typeck.expr_ty(inner_rhs); + is_time_type(cx, left_ty) && is_time_type(cx, right_ty) + } else { + false + } +} + +/// Returns true if the type is Duration or Instant +fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) +} + +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_paren()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(left_expr); + + let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + "unchecked subtraction of a 'Duration' from an 'Instant'" + } else { + "unchecked subtraction between 'Duration' values" + }; + + let mut applicability = Applicability::MachineApplicable; + let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); + let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); + + span_lint_and_sugg( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + lint_msg, + "try", + format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), + applicability, + ); +} diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d5b6c17585494..2645e94358e11 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{snippet_indent, walk_span_to_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_ast::{FormatArgs, FormatArgumentKind}; @@ -74,10 +74,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag "this let-binding has unit value", |diag| { let mut suggestions = Vec::new(); + let init_new_span = walk_span_to_context(init.span, local.span.ctxt()).unwrap(); // Suggest omitting the `let` binding - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; + let app = Applicability::MachineApplicable; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -89,35 +89,49 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag walk_body(&mut visitor, body); let mut has_in_format_capture = false; - suggestions.extend(visitor.spans.iter().filter_map(|span| match span { - MaybeInFormatCapture::Yes => { + suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { + VariableUsage::FormatCapture => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + VariableUsage::Normal(span) => Some((span, "()".to_string())), + VariableUsage::FieldShorthand(span) => Some((span.shrink_to_hi(), ": ()".to_string())), })); if has_in_format_capture { + // In a case like this: + // ``` + // let unit = returns_unit(); + // eprintln!("{unit}"); + // ``` + // we can't remove the `unit` binding and replace its uses with a `()`, + // because the `eprintln!` would break. + // + // So do the following instead: + // ``` + // let unit = (); + // returns_unit(); + // eprintln!("{unit}"); + // ``` + // TODO: find a less awkward way to do this suggestions.push(( - init.span, - format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + init_new_span.shrink_to_lo(), + format!("();\n{}", snippet_indent(cx, local.span).as_deref().unwrap_or("")), )); - diag.multipart_suggestion( - "replace variable usages with `()`", - suggestions, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion_verbose("replace variable usages with `()`", suggestions, app); return; } } - suggestions.push((local.span, format!("{snip};"))); + // let local = returns_unit(); + // ^^^^^^^^^^^^ remove this + suggestions.push((local.span.until(init_new_span), String::new())); let message = if suggestions.len() == 1 { "omit the `let` binding" } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion_verbose(message, suggestions, app); }, ); } @@ -128,13 +142,30 @@ struct UnitVariableCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, macro_call: Option<&'a FormatArgs>, } -enum MaybeInFormatCapture { - Yes, - No(Span), +/// How the unit variable is used +enum VariableUsage { + Normal(Span), + /// Captured in a `format!`: + /// + /// ```ignore + /// let unit = (); + /// eprintln!("{unit}"); + /// ``` + FormatCapture, + /// In a field shorthand init: + /// + /// ```ignore + /// struct Foo { + /// unit: (), + /// } + /// let unit = (); + /// Foo { unit }; + /// ``` + FieldShorthand(Span), } impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { @@ -174,9 +205,17 @@ impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() }) { - self.spans.push(MaybeInFormatCapture::Yes); + self.spans.push(VariableUsage::FormatCapture); } else { - self.spans.push(MaybeInFormatCapture::No(path.span)); + let parent = self.cx.tcx.parent_hir_node(ex.hir_id); + match parent { + Node::ExprField(expr_field) if expr_field.is_shorthand => { + self.spans.push(VariableUsage::FieldShorthand(ex.span)); + }, + _ => { + self.spans.push(VariableUsage::Normal(path.span)); + }, + } } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/unnecessary_mut_passed.rs similarity index 72% rename from clippy_lints/src/mut_reference.rs rename to clippy_lints/src/unnecessary_mut_passed.rs index ec93ef97cfaf7..eb2d7639e91f7 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -87,16 +87,33 @@ fn check_arguments<'tcx>( if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - let mut applicability = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); - span_lint_and_sugg( + let applicability = Applicability::MachineApplicable; + + let span_to_remove = { + let span_until_arg = argument.span.until(arg.span); + if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| { + src + // we don't use `strip_prefix` here, because `argument` might be enclosed in parens, in + // which case `&` is no longer the prefix + .find('&') + // just a sanity check, in case some proc-macro messes up the spans + .filter(|ref_pos| src[*ref_pos..].contains("mut")) + }) && let Ok(lo) = u32::try_from(ref_pos + '&'.len_utf8()) + { + span_until_arg.split_at(lo).1 + } else { + return; + } + }; + + span_lint_and_then( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), - "remove this `mut`", - sugg.to_string(), - applicability, + |diag| { + diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability); + }, ); } } diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index 76e24b6bf8058..e1e450a52fdf0 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr).is_unit() // if a stmt has attrs, then turning it into an expr will break the code, since attrs aren't allowed on exprs - && cx.tcx.hir_attrs(stmt.hir_id).is_empty() + // -- unless the corresponding feature is enabled + && (cx.tcx.hir_attrs(stmt.hir_id).is_empty() || cx.tcx.features().stmt_expr_attributes()) { if let Some(block_is_unit) = self.is_last_in_block(stmt) { if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 34dfe5b6546fa..aee8028a75de7 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,9 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::msrvs::Msrv; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -10,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol}; @@ -72,10 +74,21 @@ declare_clippy_lint! { "checks for calls of `unwrap[_err]()` that will always fail" } +pub(crate) struct Unwrap { + msrv: Msrv, +} + +impl Unwrap { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + /// Visitor that keeps track of which variables are unwrappable. struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, + msrv: Msrv, } /// What kind of unwrappable this is. @@ -133,12 +146,14 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) - } - - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) + fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { + match (get_type_diagnostic_name(cx, ty)?, method_name) { + (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), + (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), + (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), + (sym::Result, sym::is_err) => Some((UnwrappableKind::Result, false)), + _ => None, + } } match expr.kind { @@ -157,15 +172,9 @@ fn collect_unwrap_info<'tcx>( if let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name - && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) => + && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => { - let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; vec![UnwrapInfo { local_id, @@ -357,7 +366,11 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { ); } else { diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); + if can_use_if_let_chains(self.cx, self.msrv) { + diag.help("try using `if let` or `match`"); + } else { + diag.help("try using `match`"); + } } }, ); @@ -383,7 +396,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { } } -declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); +impl_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); impl<'tcx> LateLintPass<'tcx> for Unwrap { fn check_fn( @@ -402,6 +415,7 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { let mut v = UnwrappableVariablesVisitor { unwrappables: Vec::new(), cx, + msrv: self.msrv, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 5eb207a0aedb0..bb0cab3a30757 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -37,8 +37,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. && let ecx = ConstEvalCtxt::new(cx) - && let Some(lhs_value) = ecx.eval_simple(left) - && let Some(rhs_value) = ecx.eval_simple(right) + && let ctxt = expr.span.ctxt() + && let Some(lhs_value) = ecx.eval_local(left, ctxt) + && let Some(rhs_value) = ecx.eval_local(right, ctxt) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index 30fdf22fdbb09..cd6c11b512742 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,19 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Checks for array or vec initializations which call a function or method, + /// Checks for array or vec initializations which contain an expression with side effects, /// but which have a repeat count of zero. /// /// ### Why is this bad? @@ -73,89 +70,43 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it - if for_each_expr_without_closures(inner_expr, |x| { - if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { - std::ops::ControlFlow::Break(()) - } else { - std::ops::ControlFlow::Continue(()) - } - }) - .is_some() - { + if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); - if let Node::LetStmt(l) = parent_hir_node { - array_span_lint( - cx, + let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let vec = if is_vec { "vec!" } else { "" }; + + let (span, sugg) = match parent_hir_node { + Node::LetStmt(l) => ( l.span, - inner_expr.span, - l.pat.span, - Some(return_type), - is_vec, - false, - ); - } else if let Node::Expr(x) = parent_hir_node - && let ExprKind::Assign(l, _, _) = x.kind - { - array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true); - } else { - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr.span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", format!( - "{{ {}; {}[] as {return_type} }}", - snippet(cx, inner_expr.span.source_callsite(), ".."), - if is_vec { "vec!" } else { "" }, + "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), - Applicability::Unspecified, - ); - } - } -} - -fn array_span_lint( - cx: &LateContext<'_>, - expr_span: Span, - func_call_span: Span, - variable_name_span: Span, - expr_ty: Option>, - is_vec: bool, - is_assign: bool, -) { - let has_ty = expr_ty.is_some(); - - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr_span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", - format!( - "{}; {}{}{} = {}[]{}{}", - snippet(cx, func_call_span.source_callsite(), ".."), - if has_ty && !is_assign { "let " } else { "" }, - snippet(cx, variable_name_span.source_callsite(), ".."), - if let Some(ty) = expr_ty - && !is_assign - { - format!(": {ty}") - } else { - String::new() - }, - if is_vec { "vec!" } else { "" }, - if let Some(ty) = expr_ty - && is_assign - { - format!(" as {ty}") - } else { - String::new() + ), + Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( + x.span, + format!( + "{inner_expr}; {var_name} = {vec}[] as {return_type}", + var_name = snippet(cx, l.span.source_callsite(), "..") + ), + ), + _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + }; + span_lint_and_then( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + span.source_callsite(), + "expression with side effects as the initial value in a zero-sized array initializer", + |diag| { + diag.span_suggestion_verbose( + span.source_callsite(), + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); }, - if is_assign { "" } else { ";" } - ), - Applicability::Unspecified, - ); + ); + } } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2c66fdc73f539..1f678a6a29f08 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-18 +nightly-2025-10-06 ``` diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index ecd88daa6b39f..9ba796137cc3a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,25 +5,21 @@ #![allow(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, sext, unsext}; +use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{ - BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, -}; +use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp}; use rustc_lexer::{FrontmatterAllowed, tokenize}; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; -use rustc_span::{SyntaxContext, sym}; +use rustc_span::{Symbol, SyntaxContext}; use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -31,8 +27,8 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] -pub enum Constant<'tcx> { - Adt(mir::Const<'tcx>), +pub enum Constant { + Adt(ConstValue), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -54,15 +50,15 @@ pub enum Constant<'tcx> { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec>), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box>, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec>), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box>), + Ref(Box), /// A literal with syntax error. Err, } @@ -124,7 +120,7 @@ impl IntTypeBounds for IntTy { } } -impl PartialEq for Constant<'_> { +impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Str(ls), Self::Str(rs)) => ls == rs, @@ -132,16 +128,12 @@ impl PartialEq for Constant<'_> { (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, @@ -153,7 +145,7 @@ impl PartialEq for Constant<'_> { } } -impl Hash for Constant<'_> { +impl Hash for Constant { fn hash(&self, state: &mut H) where H: Hasher, @@ -209,7 +201,7 @@ impl Hash for Constant<'_> { } } -impl Constant<'_> { +impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), @@ -297,10 +289,129 @@ impl Constant<'_> { let f: Quad = s.parse().unwrap(); Self::F128(f.to_bits()) } + + pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(_) => Some(Self::Int(0)), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MIN)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::NEG_INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::NEG_INFINITY)), + _ => None, + } + } + + pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return None, + })), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MAX)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::INFINITY)), + _ => None, + } + } + + pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(_)) => x == 0, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MIN, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::NEG_INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::NEG_INFINITY, + _ => false, + } + } + + pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return false, + }; + x == limit + }, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MAX, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_pos_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::INFINITY, + Constant::F64(x) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_neg_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::NEG_INFINITY, + Constant::F64(x) => x == f64::NEG_INFINITY, + _ => false, + } + } } /// Parses a `LitKind` to a `Constant`. -pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constant<'tcx> { +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -331,10 +442,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan pub enum ConstantSource { /// The value is determined solely from the expression. Local, - /// The value is dependent on a defined constant. - Constant, - /// The value is dependent on a constant defined in `core` crate. - CoreConstant, + /// The value is dependent on another definition that may change independently from the local + /// expression. + NonLocal, } impl ConstantSource { pub fn is_local(self) -> bool { @@ -387,6 +497,7 @@ pub struct ConstEvalCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>, source: Cell, + ctxt: Cell, } impl<'tcx> ConstEvalCtxt<'tcx> { @@ -398,6 +509,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env: cx.typing_env(), typeck: cx.typeck_results(), source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } @@ -408,38 +520,50 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env, typeck, source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant, ConstantSource)> { self.source.set(ConstantSource::Local); + self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } /// Attempts to evaluate the expression. - pub fn eval(&self, e: &Expr<'_>) -> Option> { + pub fn eval(&self, e: &Expr<'_>) -> Option { self.expr(e) } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_simple(&self, e: &Expr<'_>) -> Option> { - match self.eval_with_source(e) { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, } } /// Attempts to evaluate the expression as an integer without accessing other items. - pub fn eval_full_int(&self, e: &Expr<'_>) -> Option { - match self.eval_with_source(e) { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), _ => None, } } - pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option { match &pat_expr.kind { PatExprKind::Lit { lit, negated } => { let ty = self.typeck.node_type_opt(pat_expr.hir_id); @@ -455,39 +579,31 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) + fn check_ctxt(&self, ctxt: SyntaxContext) { + if self.ctxt.get() != ctxt { + self.source.set(ConstantSource::NonLocal); + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option { + self.fetch_path(qpath, hir_id) + .and_then(|c| mir_to_const(self.tcx, c, self.typeck.node_type(hir_id))) } /// Simple constant folding: Insert an expression, get a constant or none. - fn expr(&self, e: &Expr<'_>) -> Option> { + fn expr(&self, e: &Expr<'_>) -> Option { + self.check_ctxt(e.span.ctxt()); match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), - ExprKind::Block(block, _) => self.block(block), + ExprKind::Block(block, _) => { + self.check_ctxt(block.span.ctxt()); + self.block(block) + }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, sym::cfg).is_some() { - None - } else { - Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) - } + self.check_ctxt(lit.span.ctxt()); + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), @@ -504,7 +620,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), + ExprKind::Binary(op, left, right) => { + self.check_ctxt(e.span.ctxt()); + self.binop(op.node, left, right) + }, ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -524,17 +643,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }, ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), - ExprKind::Field(local_expr, ref field) => { - let result = self.expr(local_expr); - if let Some(Constant::Adt(constant)) = &self.expr(local_expr) - && let ty::Adt(adt_def, _) = constant.ty().kind() + ExprKind::Field(base, ref field) + if let base_ty = self.typeck.expr_ty(base) + && match self.typeck.expr_adjustments(base) { + [] => true, + [.., a] => a.target == base_ty, + } + && let Some(Constant::Adt(constant)) = self.expr(base) + && let ty::Adt(adt_def, _) = *base_ty.kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) - { - mir_to_const(self.tcx, desired_field) - } else { - result - } + && let Some((desired_field, ty)) = + field_of_struct(adt_def, self.tcx, constant, base_ty, field.name) => + { + self.check_ctxt(field.span.ctxt()); + mir_to_const(self.tcx, desired_field, ty) }, _ => None, } @@ -547,19 +669,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.eval_is_empty(e), - ExprKind::Path(ref qpath) => { - if !self - .typeck - .qpath_res(qpath, e.hir_id) - .opt_def_id() - .is_some_and(DefId::is_local) - { - return None; - } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - mir_is_empty(self_.tcx, result) - }) - }, ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, sym::cfg).is_some() { None @@ -584,7 +693,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } #[expect(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { Bool(b) => Some(Bool(!b)), @@ -600,7 +709,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{F32, F64, Int}; match *o { Int(value) => { @@ -626,48 +735,128 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&self, vec: &[Expr<'_>]) -> Option>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option> { vec.iter().map(|elem| self.expr(elem)).collect::>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option - where - F: FnOnce(&Self, mir::Const<'tcx>) -> Option, - { - let res = self.typeck.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - // Check if this constant is based on `cfg!(..)`, - // which is NOT constant for our purposes. - if let Some(node) = self.tcx.hir_get_if_local(def_id) - && let Node::Item(Item { - kind: ItemKind::Const(.., body_id), - .. - }) = node - && let Node::Expr(Expr { - kind: ExprKind::Lit(_), - span, - .. - }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, sym::cfg).is_some() - { - return None; - } - - let args = self.typeck.node_args(id); - let result = self - .tcx - .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) - .ok() - .map(|val| mir::Const::from_value(val, ty))?; - f(self, result) + #[expect(clippy::too_many_lines)] + fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option { + // Resolve the path to a constant and check if that constant is known to + // not change based on the target. + // + // This should be replaced with an attribute at some point. + let did = match *qpath { + QPath::Resolved(None, path) + if path.span.ctxt() == self.ctxt.get() + && path.segments.iter().all(|s| self.ctxt.get() == s.ident.span.ctxt()) + && let Res::Def(DefKind::Const, did) = path.res + && (matches!( + self.tcx.get_diagnostic_name(did), + Some( + sym::f32_legacy_const_digits + | sym::f32_legacy_const_epsilon + | sym::f32_legacy_const_infinity + | sym::f32_legacy_const_mantissa_dig + | sym::f32_legacy_const_max + | sym::f32_legacy_const_max_10_exp + | sym::f32_legacy_const_max_exp + | sym::f32_legacy_const_min + | sym::f32_legacy_const_min_10_exp + | sym::f32_legacy_const_min_exp + | sym::f32_legacy_const_min_positive + | sym::f32_legacy_const_nan + | sym::f32_legacy_const_neg_infinity + | sym::f32_legacy_const_radix + | sym::f64_legacy_const_digits + | sym::f64_legacy_const_epsilon + | sym::f64_legacy_const_infinity + | sym::f64_legacy_const_mantissa_dig + | sym::f64_legacy_const_max + | sym::f64_legacy_const_max_10_exp + | sym::f64_legacy_const_max_exp + | sym::f64_legacy_const_min + | sym::f64_legacy_const_min_10_exp + | sym::f64_legacy_const_min_exp + | sym::f64_legacy_const_min_positive + | sym::f64_legacy_const_nan + | sym::f64_legacy_const_neg_infinity + | sym::f64_legacy_const_radix + | sym::u8_legacy_const_min + | sym::u16_legacy_const_min + | sym::u32_legacy_const_min + | sym::u64_legacy_const_min + | sym::u128_legacy_const_min + | sym::usize_legacy_const_min + | sym::u8_legacy_const_max + | sym::u16_legacy_const_max + | sym::u32_legacy_const_max + | sym::u64_legacy_const_max + | sym::u128_legacy_const_max + | sym::i8_legacy_const_min + | sym::i16_legacy_const_min + | sym::i32_legacy_const_min + | sym::i64_legacy_const_min + | sym::i128_legacy_const_min + | sym::i8_legacy_const_max + | sym::i16_legacy_const_max + | sym::i32_legacy_const_max + | sym::i64_legacy_const_max + | sym::i128_legacy_const_max + ) + ) || self.tcx.opt_parent(did).is_some_and(|parent| { + paths::F16_CONSTS.matches(&self.tcx, parent) + || paths::F32_CONSTS.matches(&self.tcx, parent) + || paths::F64_CONSTS.matches(&self.tcx, parent) + || paths::F128_CONSTS.matches(&self.tcx, parent) + })) => + { + did }, - _ => None, - } + QPath::TypeRelative(ty, const_name) + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let [.., ty_name] = ty_path.segments + && (matches!( + ty_name.ident.name, + sym::i8 + | sym::i16 + | sym::i32 + | sym::i64 + | sym::i128 + | sym::u8 + | sym::u16 + | sym::u32 + | sym::u64 + | sym::u128 + | sym::f32 + | sym::f64 + | sym::char + ) || (ty_name.ident.name == sym::usize && const_name.ident.name == sym::MIN)) + && const_name.ident.span.ctxt() == self.ctxt.get() + && ty.span.ctxt() == self.ctxt.get() + && ty_name.ident.span.ctxt() == self.ctxt.get() + && matches!(ty_path.res, Res::PrimTy(_)) + && let Some((DefKind::AssocConst, did)) = self.typeck.type_dependent_def(id) => + { + did + }, + _ if let Res::Def(DefKind::Const | DefKind::AssocConst, did) = self.typeck.qpath_res(qpath, id) => { + self.source.set(ConstantSource::NonLocal); + did + }, + _ => return None, + }; + + self.tcx + .const_eval_resolve( + self.typing_env, + mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), + qpath.span(), + ) + .ok() } - fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { let lhs = self.expr(lhs); let index = self.expr(index); @@ -697,7 +886,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// A block can only yield a constant if it has exactly one constant expression. - fn block(&self, block: &Block<'_>) -> Option> { + fn block(&self, block: &Block<'_>) -> Option { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -716,11 +905,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } else { // Unable to access the source. Assume a non-local dependency. - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } @@ -730,7 +919,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -742,7 +931,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -778,6 +967,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitAnd => Some(zext(l & r)), + // FIXME: f32/f64 currently consider `0.0` and `-0.0` as different. BinOpKind::Eq => Some(Constant::Bool(l == r)), BinOpKind::Ne => Some(Constant::Bool(l != r)), BinOpKind::Lt => Some(Constant::Bool(l < r)), @@ -856,14 +1046,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } -pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option> { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option { + match (val, ty.kind()) { + (_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)), + (ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() { ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), @@ -877,7 +1063,6 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); let len = len.try_to_target_usize(tcx)?; @@ -902,64 +1087,32 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option } } -fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { - ty::Str | ty::Slice(_) => { - if let ConstValue::Indirect { alloc_id, offset } = val { - // Get the length from the slice, using the same formula as - // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. - let a = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = tcx.data_layout.pointer_size(); - if a.size() < offset + 2 * ptr_size { - // (partially) dangling reference - return None; - } - let len = a - .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) - .ok()? - .to_target_usize(&tcx) - .discard_err()?; - Some(len == 0) - } else { - None - } - }, - ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), - _ => None, - }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), - (ConstValue::ZeroSized, _) => Some(true), - _ => None, - } -} - fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, tcx: TyCtxt<'tcx>, - result: mir::Const<'tcx>, - field: &Ident, -) -> Option> { - if let mir::Const::Val(result, ty) = result - && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) + value: ConstValue, + ty: Ty<'tcx>, + field: Symbol, +) -> Option<(ConstValue, Ty<'tcx>)> { + if let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(value, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) - && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) - && let Some(&(val, ty)) = dc.fields.get(field_idx) + && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field) { - Some(mir::Const::Val(val, ty)) + dc.fields.get(field_idx).copied() } else { None } } /// If `expr` evaluates to an integer constant, return its value. -pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) { +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. +pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) } else { None @@ -967,7 +1120,12 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } /// Check if `expr` evaluates to an integer constant of 0. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. #[inline] -pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - integer_const(cx, expr) == Some(0) +pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + integer_const(cx, expr, ctxt) == Some(0) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index bda28a663fb07..6f1bc28fbab81 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -454,7 +454,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name == sym::with_capacity { let arg = args.first()?; - return match ConstEvalCtxt::new(cx).eval_simple(arg) { + return match ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b79e15cd7170b..a6d821c99c1b5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -290,8 +290,10 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) + .eval_local(left, self.left_ctxt), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_local(right, self.right_ctxt), ) && l == r { @@ -842,7 +844,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { - ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e) + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_local(e, e.span.ctxt()) }); // const hashing may result in the same hash as some unrelated node, so add a sort of diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659a..24864e8ef96dc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,7 +63,6 @@ pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; -pub mod ptr; pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; @@ -99,7 +98,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, @@ -127,8 +126,9 @@ use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; +use crate::msrvs::Msrv; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1423,11 +1423,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx.tcx, min_const) && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { - start_const == min_const + start_const.is_numeric_min(cx.tcx, bnd_ty) } else { false } @@ -1436,11 +1434,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti RangeLimits::Closed => { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx.tcx, max_const) && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { - end_const == max_const + end_const.is_numeric_max(cx.tcx, bnd_ty) } else { false } @@ -1855,15 +1851,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// Checks if the given function kind is an async function. -pub fn is_async_fn(kind: FnKind<'_>) -> bool { - match kind { - FnKind::ItemFn(_, _, header) => header.asyncness.is_async(), - FnKind::Method(_, sig) => sig.header.asyncness.is_async(), - FnKind::Closure => false, - } -} - /// Peels away all the compiler generated code surrounding the body of an async closure. pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Closure(&Closure { @@ -3659,3 +3646,8 @@ pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { }) ) } + +/// Checks if the chosen edition and `msrv` allows using `if let` chains. +pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool { + cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS) +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4d..62041fc631c04 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -28,9 +28,10 @@ msrv_aliases! { 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } - 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } + 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } + 1,79,0 { CONST_BLOCKS } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } @@ -46,7 +47,7 @@ msrv_aliases! { 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } - 1,57,0 { MAP_WHILE } + 1,57,0 { MAP_WHILE, CONST_PANIC } 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } @@ -72,12 +73,12 @@ msrv_aliases! { 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } - 1,27,0 { ITERATOR_TRY_FOLD } + 1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } - 1,16,0 { STR_REPEAT } + 1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT } 1,15,0 { MAYBE_BOUND_IN_WHERE } 1,13,0 { QUESTION_MARK_OPERATOR } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ea8cfc59356a7..5ab8e16d88ed3 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ItemKind, Node, UseKind}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; @@ -74,8 +75,8 @@ impl PathLookup { } /// Returns the list of [`DefId`]s that the path resolves to - pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { - self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + pub fn get<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(tcx.tcx(), self.ns, self.path)) } /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into @@ -90,8 +91,8 @@ impl PathLookup { } /// Checks if the path resolves to the given `def_id` - pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { - self.get(cx).contains(&def_id) + pub fn matches<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, def_id: DefId) -> bool { + self.get(&tcx.tcx()).contains(&def_id) } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it @@ -100,8 +101,8 @@ impl PathLookup { } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` - pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + pub fn matches_ty<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(&tcx.tcx(), adt.did())) } } @@ -126,6 +127,11 @@ path_macros! { macro_path: PathNS::Macro, } +pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); +pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); +pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); +pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); + // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs deleted file mode 100644 index 5847e916e340f..0000000000000 --- a/clippy_utils/src/ptr.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::source::snippet; -use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs, sym}; -use core::ops::ControlFlow; -use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; -use std::borrow::Cow; - -pub fn get_spans( - cx: &LateContext<'_>, - opt_body_id: Option, - idx: usize, - replacements: &[(Symbol, &'static str)], -) -> Option)>> { - if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { - if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { - extract_clone_suggestions(cx, binding_id, replacements, body) - } else { - Some(vec![]) - } - } else { - Some(vec![]) - } -} - -fn extract_clone_suggestions<'tcx>( - cx: &LateContext<'tcx>, - id: HirId, - replace: &[(Symbol, &'static str)], - body: &'tcx Body<'_>, -) -> Option)>> { - let mut spans = Vec::new(); - for_each_expr_without_closures(body, |e| { - if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) - { - if seg.ident.name == sym::capacity { - return ControlFlow::Break(()); - } - for &(fn_name, suffix) in replace { - if seg.ident.name == fn_name { - spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }) - .is_none() - .then_some(spans) -} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 7530d3bc7157d..2d0d4a5319f3c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -111,8 +111,11 @@ generate! { clone_into, cloned, cognitive_complexity, + collapsible_else_if, + collapsible_if, collect, const_ptr, + consts, contains, copied, copy_from, @@ -189,8 +192,10 @@ generate! { is_none, is_none_or, is_ok, + is_partitioned, is_some, is_some_and, + is_sorted_by_key, isqrt, itertools, join, @@ -208,6 +213,7 @@ generate! { map_continue, map_or, map_or_else, + map_while, match_indices, matches, max, @@ -344,6 +350,7 @@ generate! { trim_start, trim_start_matches, truncate, + try_for_each, unreachable_pub, unsafe_removed_from_name, unused, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c03469c2b8851..ebf4f2cd3263c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -21,9 +21,9 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, + GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 55e588f5ec736..0d0b80c309ddd 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -22,6 +22,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" strip-ansi-escapes = "0.2.0" tar = "0.4" -toml = "0.7.3" +toml = "0.9.7" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c102de448200..e936f5dc3b7a5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-18" +channel = "nightly-2025-10-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6b6dfd7b81eab..71cd8a6c03cc9 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -405,11 +405,18 @@ fn ui_cargo_toml_metadata() { continue; } - let toml = fs::read_to_string(path).unwrap().parse::().unwrap(); - - let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap(); - - let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_"); + let toml = fs::read_to_string(path).unwrap(); + let toml = toml::de::DeTable::parse(&toml).unwrap(); + + let package = toml.get_ref().get("package").unwrap().get_ref().as_table().unwrap(); + + let name = package + .get("name") + .unwrap() + .as_ref() + .as_str() + .unwrap() + .replace('-', "_"); assert!( path.parent() .unwrap() @@ -421,7 +428,10 @@ fn ui_cargo_toml_metadata() { path.display(), ); - let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); + let publish = package + .get("publish") + .and_then(|x| x.get_ref().as_bool()) + .unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), "`{}` lacks `publish = false`", diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr new file mode 100644 index 0000000000000..c7490c5da027b --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are required, found `src/foo.rs` + --> src/foo.rs:1:1 + | +1 | pub mod bar; + | ^ + | + = help: move `src/foo.rs` to `src/foo/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` + +error: could not compile `duplicated-mod-names-14697` (lib) due to 1 previous error diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml new file mode 100644 index 0000000000000..569082f2f6599 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml @@ -0,0 +1,11 @@ +# Should trigger when multiple mods with the same name exist and not all of them follow self-named convention. +# See issue #14697. +[package] +name = "duplicated-mod-names-14697" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs new file mode 100644 index 0000000000000..46f285ca47d69 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs @@ -0,0 +1 @@ +pub mod bar; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs new file mode 100644 index 0000000000000..a85dae5748165 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; +pub mod other; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index 902330e178530..f134943e69bfc 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `src/bad/inner.rs` - --> src/bad/inner.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` + --> src/bad/inner/stuff.rs:1:1 | -1 | pub mod stuff; +1 | pub mod most; | ^ | - = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` + = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` -error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` - --> src/bad/inner/stuff.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner.rs` + --> src/bad/inner.rs:1:1 | -1 | pub mod most; +1 | pub mod stuff; | ^ | - = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` + = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` error: could not compile `fail-mod` (bin "fail-mod") due to 2 previous errors diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml new file mode 100644 index 0000000000000..5c2fabd2283d3 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml @@ -0,0 +1,10 @@ +# Should not produce FP when irrelavant path segment shares the same name with module. +# See issue #10271 and #11916. +[package] +name = "segment-with-mod-name-10271-11916" +version = "0.1.0" +edition = "2024" +publish = false + +[workspace] +members = ["foo/bar"] \ No newline at end of file diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml new file mode 100644 index 0000000000000..1f68c0dccac54 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "bar" +version = "0.1.0" +edition = "2024" +publish = false diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml new file mode 100644 index 0000000000000..d867377545e15 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs new file mode 100644 index 0000000000000..a5c2109ece7db --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::mod_module_files)] + +#[path = "bar/mod.rs"] +pub mod foo; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml new file mode 100644 index 0000000000000..ddf2ac394cddc --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-no-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs new file mode 100644 index 0000000000000..3b12aefa3d5fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs @@ -0,0 +1,2 @@ +#[path = "bar.rs"] +mod bar; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs new file mode 100644 index 0000000000000..bf2a5d0193350 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs @@ -0,0 +1,3 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed index 0dc0fc230c8de..ec45dfd2033ad 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -48,3 +48,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.rs b/tests/ui-toml/collapsible_if/collapsible_else_if.rs index 8344c122f16c8..54315a3c32bf9 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.rs +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.rs @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -53,3 +53,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index ecb43dc34a8a0..b28e46af0a467 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,5 +1,3 @@ -#![allow(clippy::uninlined_format_args)] - fn main() {} #[warn(clippy::cognitive_complexity)] @@ -8,7 +6,7 @@ fn cognitive_complexity() { let x = vec![1, 2, 3]; for i in x { if i == 1 { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 627498dc175cd..95b0508189e9c 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -11,7 +11,7 @@ LL | blacklisted-names = [ "..", "wibble" ] | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) - --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4 + --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:4:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 2877871d0bf4c..36540bf1dcf73 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index f958b92a102a3..da76bb20fd961 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index e1a8941e102f5..022deb330e6e3 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed index 8da607ec65842..419e62f92f469 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println!("hello world"); + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index e35844a209fa5..b0bbced4ea3c2 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println! {"hello world"} + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index fda6addc7aa35..87325f05c9bcd 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -54,5 +54,11 @@ error: use of irregular braces for `eprint!` macro LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` -error: aborting due to 8 previous errors +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5 + | +LL | println! {"hello world"} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");` + +error: aborting due to 9 previous errors diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index c2516c5414753..f467d4966aef7 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -42,8 +42,8 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + //~^ assertions_on_constants let flag: bool = cfg!(not(feature = "asdf")); assert!(flag); @@ -62,9 +62,37 @@ fn main() { const _: () = assert!(N.is_power_of_two()); } +const C: bool = true; + const _: () = { assert!(true); //~^ assertions_on_constants assert!(8 == (7 + 1)); + //~^ assertions_on_constants + + assert!(C); }; + +#[clippy::msrv = "1.57"] +fn _f1() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.56"] +fn _f2() { + assert!(C); +} + +#[clippy::msrv = "1.79"] +fn _f3() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.78"] +fn _f4() { + assert!(C); + //~^ assertions_on_constants +} diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 8b7440ec4832c..a996c41b69420 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -1,100 +1,140 @@ -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::assertions_on_constants)]` -error: `assert!(false)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:13:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:16:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:19:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:23:5 | LL | assert!(false, "{}", msg.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:27:5 | LL | assert!(B); | ^^^^^^^^^^ | - = help: remove it + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:31:5 | LL | assert!(C); | ^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false, ..)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:34:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `debug_assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:37:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:45:5 + | +LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:54:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:57:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:66:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:68:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion + +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:71:5 + | +LL | assert!(8 == (7 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove the assertion + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:79:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:90:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:96:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` -error: aborting due to 12 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index f9bc9436b0792..796d4dabf04d2 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -25,6 +25,10 @@ impl IteratorFalsePositives { self } + pub fn next_back(self) -> IteratorFalsePositives { + self + } + pub fn find(self) -> Option { Some(self.foo) } diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 80e010e2dfd74..fa35a01242d1d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 09aa8480cbd9e..403077e72ff98 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 08f85fb58549c..3f8d631591e93 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:8:13 + --> tests/ui/bind_instead_of_map.rs:7:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> tests/ui/bind_instead_of_map.rs:10:13 + --> tests/ui/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:17:13 + --> tests/ui/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index e848f0601e32d..1646b5705b6dd 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,6 +1,5 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] //@no-rustfix // branches_sharing_code at the top and bottom of the if blocks @@ -70,7 +69,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("From the a `{}` to the b `{}`", a, b); + println!("From the a `{a}` to the b `{b}`"); let pack = DataPack { id: e_id, @@ -83,7 +82,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("The new ID is '{}'", e_id); + println!("The new ID is '{e_id}'"); let pack = DataPack { id: e_id, diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 40f3453edb9ae..f5c51f7888d04 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:17:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { LL | | @@ -10,7 +10,7 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:31:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:30:5 | LL | / let _u = 9; LL | | } @@ -34,7 +34,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:35:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:34:5 | LL | / if x == 99 { LL | | @@ -45,7 +45,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:48:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:47:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -67,7 +67,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:66:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:65:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | @@ -78,7 +78,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:88:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:87:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -108,7 +108,7 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:101:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:100:5 | LL | / let _ = if x == 7 { ... | @@ -116,7 +116,7 @@ LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:112:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:111:5 | LL | / x << 2 LL | | }; @@ -134,7 +134,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:115:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:114:5 | LL | / if x == 9 { ... | @@ -142,7 +142,7 @@ LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:126:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:125:5 | LL | / x * 4 LL | | } @@ -160,7 +160,7 @@ LL + x * 4 | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:157:9 | LL | / if false { LL | | @@ -168,7 +168,7 @@ LL | | let x = 1; | |______________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:165:9 | LL | / let y = 1; LL | | } diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index b55c22f5ca83c..44cb66a8d1848 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.unsigned_abs() as usize; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 466aa6aeb1fbb..555b9090fe437 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.abs() as usize; diff --git a/tests/ui/checked_unwrap/if_let_chains.rs b/tests/ui/checked_unwrap/if_let_chains.rs new file mode 100644 index 0000000000000..cfa7715965cdb --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.rs @@ -0,0 +1,24 @@ +//@require-annotations-for-level: ERROR +#![deny(clippy::unnecessary_unwrap)] + +#[clippy::msrv = "1.85"] +fn if_let_chains_unsupported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `match` + } +} + +#[clippy::msrv = "1.88"] +fn if_let_chains_supported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `if let` or `match` + } +} diff --git a/tests/ui/checked_unwrap/if_let_chains.stderr b/tests/ui/checked_unwrap/if_let_chains.stderr new file mode 100644 index 0000000000000..8a4137de37a3c --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.stderr @@ -0,0 +1,29 @@ +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:9:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `match` +note: the lint level is defined here + --> tests/ui/checked_unwrap/if_let_chains.rs:2:9 + | +LL | #![deny(clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:20:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `if let` or `match` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 2dd8af1525150..469121bbd740f 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42; //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = *****a; + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = (*opt)?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index a371afb04a78f..b05f1d3aa35e9 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42.clone(); //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'.clone()); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42.clone()); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = opt.clone()?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 92cdd635d20a8..c87d1d488dde5 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:23:5 + --> tests/ui/clone_on_copy.rs:14:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -8,52 +8,58 @@ LL | 42.clone(); = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:28:5 + --> tests/ui/clone_on_copy.rs:19:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:32:5 + --> tests/ui/clone_on_copy.rs:23:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `u32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:36:5 + --> tests/ui/clone_on_copy.rs:27:5 | LL | x.clone().rotate_left(1); | ^^^^^^^^^ help: try removing the `clone` call: `x` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:51:5 + --> tests/ui/clone_on_copy.rs:42:5 | LL | m!(42).clone(); | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` error: using `clone` on type `[u32; 2]` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:62:5 + --> tests/ui/clone_on_copy.rs:53:5 | LL | x.clone()[0]; | ^^^^^^^^^ help: try dereferencing it: `(*x)` +error: using `clone` on type `E` which implements the `Copy` trait + --> tests/ui/clone_on_copy.rs:92:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + error: using `clone` on type `char` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:73:14 + --> tests/ui/clone_on_copy.rs:107:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:78:14 + --> tests/ui/clone_on_copy.rs:114:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:83:17 + --> tests/ui/clone_on_copy.rs:120:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? | ^^^^^^^^^^^ help: try dereferencing it: `(*opt)` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed new file mode 100644 index 0000000000000..8ef4b36566363 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Weak::::clone(&rc_weak); + //~^ clone_on_ref_ptr + std::sync::Arc::::clone(&arc); + //~^ clone_on_ref_ptr + std::sync::Weak::::clone(&arc_weak); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = std::sync::Arc::::clone(&x); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(std::rc::Rc::::clone(&try_opt!(Some(rc)))) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs new file mode 100644 index 0000000000000..fbd787099aee3 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.rs @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + rc.clone(); + //~^ clone_on_ref_ptr + rc_weak.clone(); + //~^ clone_on_ref_ptr + arc.clone(); + //~^ clone_on_ref_ptr + arc_weak.clone(); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = x.clone(); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr new file mode 100644 index 0000000000000..b15f0e803a352 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -0,0 +1,41 @@ +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:9:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:11:5 + | +LL | rc_weak.clone(); + | ^^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:13:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:15:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:30:33 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:48:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 3d709fe9b8e0e..da958f76a5ca0 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -87,6 +87,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 51868e039086f..06af49f2f6f3b 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -103,6 +103,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 1a7bcec7fd5df..ce1da593a8e93 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -151,7 +151,7 @@ LL | | } | |_____^ help: collapse nested if block: `if false {}` error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:130:12 + --> tests/ui/collapsible_else_if.rs:157:12 | LL | } else { | ____________^ diff --git a/tests/ui/collapsible_else_if_unfixable.rs b/tests/ui/collapsible_else_if_unfixable.rs new file mode 100644 index 0000000000000..e5c18cf3ba901 --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.rs @@ -0,0 +1,22 @@ +//@no-rustfix +#![warn(clippy::collapsible_else_if)] + +fn issue_13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_else_if_unfixable.stderr b/tests/ui/collapsible_else_if_unfixable.stderr new file mode 100644 index 0000000000000..b461ceba6de7f --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:10:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:17:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 78354c2d7cf88..ca9d02ff2d4f8 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -144,6 +144,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 5d9afa109569d..9ac68ecd4cac0 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -154,6 +154,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index a685cc2e9291d..b1f26630f529b 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -191,7 +191,7 @@ LL ~ ; 3 | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:178:5 + --> tests/ui/collapsible_if.rs:196:5 | LL | / if true { LL | | (if true { diff --git a/tests/ui/collapsible_if_unfixable.rs b/tests/ui/collapsible_if_unfixable.rs new file mode 100644 index 0000000000000..643520ac0f5d9 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.rs @@ -0,0 +1,20 @@ +//@ no-rustfix +#![warn(clippy::collapsible_if)] + +fn issue13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + // don't collapsible because of this comment + #[expect(clippy::collapsible_if)] + if true {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + #[expect(clippy::collapsible_if)] + // don't collapsible because of this comment + if true {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_if_unfixable.stderr b/tests/ui/collapsible_if_unfixable.stderr new file mode 100644 index 0000000000000..64c8fb8da2b44 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:9:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:15:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const_comparisons.rs b/tests/ui/const_comparisons.rs index b732d7d142fc5..a2a1f0a7b4c09 100644 --- a/tests/ui/const_comparisons.rs +++ b/tests/ui/const_comparisons.rs @@ -1,9 +1,11 @@ -#![allow(unused)] -#![warn(clippy::impossible_comparisons)] -#![warn(clippy::redundant_comparisons)] -#![allow(clippy::no_effect)] -#![allow(clippy::short_circuit_statement)] -#![allow(clippy::manual_range_contains)] +#![allow( + unused, + clippy::identity_op, + clippy::manual_range_contains, + clippy::no_effect, + clippy::short_circuit_statement +)] +#![warn(clippy::impossible_comparisons, clippy::redundant_comparisons)] const STATUS_BAD_REQUEST: u16 = 400; const STATUS_SERVER_ERROR: u16 = 500; diff --git a/tests/ui/const_comparisons.stderr b/tests/ui/const_comparisons.stderr index 48a2c6e8d4879..1ce62c23ff293 100644 --- a/tests/ui/const_comparisons.stderr +++ b/tests/ui/const_comparisons.stderr @@ -1,5 +1,5 @@ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:45:5 + --> tests/ui/const_comparisons.rs:47:5 | LL | status_code <= 400 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | status_code <= 400 && status_code > 500; = help: to override `-D warnings` add `#[allow(clippy::impossible_comparisons)]` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:48:5 + --> tests/ui/const_comparisons.rs:50:5 | LL | status_code > 500 && status_code < 400; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | status_code > 500 && status_code < 400; = note: since `500` > `400`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:51:5 + --> tests/ui/const_comparisons.rs:53:5 | LL | status_code < 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | status_code < 500 && status_code > 500; = note: `status_code` cannot simultaneously be greater than and less than `500` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:55:5 + --> tests/ui/const_comparisons.rs:57:5 | LL | status_code < { 400 } && status_code > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | status_code < { 400 } && status_code > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:58:5 + --> tests/ui/const_comparisons.rs:60:5 | LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:61:5 + --> tests/ui/const_comparisons.rs:63:5 | LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:64:5 + --> tests/ui/const_comparisons.rs:66:5 | LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:68:5 + --> tests/ui/const_comparisons.rs:70:5 | LL | status < { 400 } && status > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | status < { 400 } && status > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:71:5 + --> tests/ui/const_comparisons.rs:73:5 | LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:74:5 + --> tests/ui/const_comparisons.rs:76:5 | LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:77:5 + --> tests/ui/const_comparisons.rs:79:5 | LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:86:5 + --> tests/ui/const_comparisons.rs:88:5 | LL | 500 >= status_code && 600 < status_code; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 500 >= status_code && 600 < status_code; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:90:5 + --> tests/ui/const_comparisons.rs:92:5 | LL | 500 >= status_code && status_code > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 500 >= status_code && status_code > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:99:5 + --> tests/ui/const_comparisons.rs:101:5 | LL | 500 >= status && 600 < status; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | 500 >= status && 600 < status; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:103:5 + --> tests/ui/const_comparisons.rs:105:5 | LL | 500 >= status && status > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,13 +121,13 @@ LL | 500 >= status && status > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:107:5 + --> tests/ui/const_comparisons.rs:109:5 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:107:23 + --> tests/ui/const_comparisons.rs:109:23 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^ @@ -135,67 +135,67 @@ LL | status_code < 200 && status_code <= 299; = help: to override `-D warnings` add `#[allow(clippy::redundant_comparisons)]` error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:118:5 + --> tests/ui/const_comparisons.rs:120:5 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:118:23 + --> tests/ui/const_comparisons.rs:120:23 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:126:5 + --> tests/ui/const_comparisons.rs:128:5 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:126:23 + --> tests/ui/const_comparisons.rs:128:23 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:131:5 + --> tests/ui/const_comparisons.rs:133:5 | LL | name < "Jennifer" && name > "Shannon"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -203,7 +203,7 @@ LL | name < "Jennifer" && name > "Shannon"; = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:135:5 + --> tests/ui/const_comparisons.rs:137:5 | LL | numbers < [3, 4] && numbers > [5, 6]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,7 +211,7 @@ LL | numbers < [3, 4] && numbers > [5, 6]; = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:139:5 + --> tests/ui/const_comparisons.rs:141:5 | LL | letter < 'b' && letter > 'c'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | letter < 'b' && letter > 'c'; = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:143:5 + --> tests/ui/const_comparisons.rs:145:5 | LL | area < std::f32::consts::E && area > std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 8bb4f0e5d9750..2ad1b5276def8 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -55,53 +55,6 @@ const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; const EMPTY_REF_ARRAY: &[u32; 0] = &[]; const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; -fn test_from_const() { - let _ = EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - //~^ const_is_empty -} - fn main() { let value = "foobar"; let _ = value.is_empty(); @@ -120,7 +73,7 @@ fn main() { fn str_from_arg(var: &str) { var.is_empty(); - // Do not lint, we know nothiny about var + // Do not lint, we know nothing about var } fn update_str() { @@ -200,6 +153,5 @@ fn issue_13106() { const { EMPTY_STR.is_empty(); - //~^ const_is_empty } } diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 2ba189058e832..e1837695bc1c9 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -37,131 +37,35 @@ error: this expression always evaluates to false LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:59:13 - | -LL | let _ = EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:62:13 - | -LL | let _ = NON_EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:65:13 - | -LL | let _ = EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:68:13 - | -LL | let _ = NON_EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:71:13 - | -LL | let _ = EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:74:13 - | -LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:77:13 - | -LL | let _ = EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 - | -LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:83:13 - | -LL | let _ = NON_EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 - | -LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:89:13 - | -LL | let _ = EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 - | -LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:95:13 - | -LL | let _ = EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:98:13 - | -LL | let _ = NON_EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:101:13 - | -LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:107:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:111:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:114:13 + --> tests/ui/const_is_empty.rs:67:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:117:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:171:13 + --> tests/ui/const_is_empty.rs:124:13 | LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 - | -LL | EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 27 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index dd6c6b8de25af..e8c47b4f3a77b 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -7,7 +7,7 @@ pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { pub fn ice(&self) { for i in self.0.iter() { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index d3fe09a052ef3..e3bf603da80f3 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index cdffb2a2ee8ca..8cc065e5bced2 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index aa7eb4f89558c..aa89516f175c4 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,5 +1,5 @@ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:13:22 + --> tests/ui/default_trait_access.rs:12:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` @@ -11,43 +11,43 @@ LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:18:22 + --> tests/ui/default_trait_access.rs:17:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:21:22 + --> tests/ui/default_trait_access.rs:20:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:26:22 + --> tests/ui/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:37:46 + --> tests/ui/default_trait_access.rs:36:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:44:36 + --> tests/ui/default_trait_access.rs:43:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:47:36 + --> tests/ui/default_trait_access.rs:46:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:52:42 + --> tests/ui/default_trait_access.rs:51:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index e334203c7b2a7..b8adb7601db64 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -131,3 +131,16 @@ fn issue14558() { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::expl_impl_clone_on_copy)] + #[derive(Copy)] + struct S; + + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 9004ced6849e5..7dbc38a2f621f 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,7 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { @@ -33,7 +33,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { @@ -55,7 +55,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { @@ -77,7 +77,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { @@ -99,7 +99,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 3ef4ee9463dca..b4bb24b0d2fe7 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -76,3 +76,18 @@ mod use_ord { } fn main() {} + +mod issue15708 { + use std::cmp::{Ord, Ordering}; + + // Check that the lint posts on the type definition node + #[expect(clippy::derive_ord_xor_partial_ord)] + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} diff --git a/tests/ui/derived_hash_with_manual_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs index 88b574add3f27..9f5c85d7fbc38 100644 --- a/tests/ui/derived_hash_with_manual_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -41,3 +41,19 @@ impl std::hash::Hash for Bah { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::derived_hash_with_manual_eq)] + #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)] + pub struct Span { + start: usize, + end: usize, + } + + impl PartialEq for Span { + fn eq(&self, other: &Self) -> bool { + self.start.cmp(&other.start).then(self.end.cmp(&other.end)).is_eq() + } + } +} diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed new file mode 100644 index 0000000000000..dedc513438d11 --- /dev/null +++ b/tests/ui/double_parens.fixed @@ -0,0 +1,99 @@ +#![warn(clippy::double_parens)] +#![expect(clippy::eq_op, clippy::no_effect)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method(&self, _: T) {} +} + +fn simple_double_parens() -> i32 { + (0) + //~^ double_parens +} + +fn fn_double_parens() { + dummy_fn(0); + //~^ double_parens +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method(0); + //~^ double_parens +} + +fn tuple_double_parens() -> (i32, i32) { + (1, 2) + //~^ double_parens +} + +#[allow(clippy::unused_unit)] +fn unit_double_parens() { + () + //~^ double_parens +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!((1, 2), (1, 2), "Error"); + //~^ double_parens +} + +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {(100)} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + (vec![1, 2]); + //~^ double_parens + dummy_fn(vec![1, 2]); + //~^ double_parens + x.dummy_method(vec![1, 2]); + //~^ double_parens +} + +fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 7c976015b4e79..27f252485b714 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code, clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,38 +8,33 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { ((0)) //~^ double_parens - - } fn fn_double_parens() { dummy_fn((0)); //~^ double_parens - } fn method_double_parens(x: DummyStruct) { x.dummy_method((0)); //~^ double_parens - } fn tuple_double_parens() -> (i32, i32) { ((1, 2)) //~^ double_parens - } +#[allow(clippy::unused_unit)] fn unit_double_parens() { (()) //~^ double_parens - } fn fn_tuple_ok() { @@ -63,7 +58,42 @@ fn inside_macro() { assert_eq!((1, 2), (1, 2), "Error"); assert_eq!(((1, 2)), (1, 2), "Error"); //~^ double_parens +} +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {((100))} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + ((vec![1, 2])); + //~^ double_parens + dummy_fn((vec![1, 2])); + //~^ double_parens + x.dummy_method((vec![1, 2])); + //~^ double_parens } fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index e119f54949b1f..3a740e44cacf3 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,41 +1,70 @@ -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:15:5 | LL | ((0)) - | ^^^^^ + | ^^^^^ help: remove them: `(0)` | = note: `-D clippy::double-parens` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:22:14 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:28:20 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:34:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:40:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:36:5 | LL | (()) - | ^^^^ + | ^^^^ help: remove them: `()` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:64:16 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:59:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: aborting due to 6 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:84:16 + | +LL | () => {((100))} + | ^^^^^^^ help: remove them: `(100)` +... +LL | bar!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:91:5 + | +LL | ((vec![1, 2])); + | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:93:14 + | +LL | dummy_fn((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:95:20 + | +LL | x.dummy_method((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: aborting due to 10 previous errors diff --git a/tests/ui/duration_subsec.fixed b/tests/ui/duration_subsec.fixed index a8c2f78ca383a..b6b2d156c0e0f 100644 --- a/tests/ui/duration_subsec.fixed +++ b/tests/ui/duration_subsec.fixed @@ -25,8 +25,7 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; - let _ = dur.subsec_micros(); - //~^ duration_subsec + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.rs b/tests/ui/duration_subsec.rs index 582f4717de27f..1061e6003c35e 100644 --- a/tests/ui/duration_subsec.rs +++ b/tests/ui/duration_subsec.rs @@ -26,7 +26,6 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - //~^ duration_subsec // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index 1a41742e1fa6d..27756bc1c20f6 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -25,11 +25,5 @@ error: calling `subsec_micros()` is more concise than this calculation LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: calling `subsec_micros()` is more concise than this calculation - --> tests/ui/duration_subsec.rs:28:13 - | -LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 13934785d7b15..ec4cecf377667 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,5 +1,5 @@ #![warn(clippy::explicit_counter_loop)] -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] +#![allow(clippy::useless_vec)] //@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; @@ -89,13 +89,13 @@ mod issue_1219 { for _v in &vec { index += 1 } - println!("index: {}", index); + println!("index: {index}"); // should not trigger the lint because the count is conditional #1219 let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { continue; } @@ -106,7 +106,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { count += 1; } @@ -118,7 +118,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; if ch == 'a' { continue; @@ -131,7 +131,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { let _ = 123; @@ -142,7 +142,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { count += 1; @@ -157,7 +157,7 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); while erasures.contains(&(i + skips)) { skips += 1; } @@ -166,7 +166,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); let mut j = 0; while j < 5 { skips += 1; @@ -177,7 +177,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); for j in 0..5 { skips += 1; } @@ -205,7 +205,7 @@ mod issue_4732 { for _v in slice { index += 1 } - let _closure = || println!("index: {}", index); + let _closure = || println!("index: {index}"); } } @@ -217,7 +217,7 @@ mod issue_4677 { let mut count = 0; for _i in slice { count += 1; - println!("{}", count); + println!("{count}"); } } } diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 52c4d1b1f301a..97e8e0bafe4fd 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(&*a); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 706d6cb2b79a6..b689649d49dd7 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(a.deref()); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 5036884366cfc..e2f2e68720b17 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:55:19 + --> tests/ui/explicit_deref_methods.rs:58:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,73 +8,73 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:58:23 + --> tests/ui/explicit_deref_methods.rs:61:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:39 + --> tests/ui/explicit_deref_methods.rs:65:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:50 + --> tests/ui/explicit_deref_methods.rs:65:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:66:20 + --> tests/ui/explicit_deref_methods.rs:69:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:70:11 + --> tests/ui/explicit_deref_methods.rs:73:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:75:28 + --> tests/ui/explicit_deref_methods.rs:78:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:13 + --> tests/ui/explicit_deref_methods.rs:81:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:28 + --> tests/ui/explicit_deref_methods.rs:84:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:121:31 + --> tests/ui/explicit_deref_methods.rs:124:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:139:14 + --> tests/ui/explicit_deref_methods.rs:154:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:141:14 + --> tests/ui/explicit_deref_methods.rs:156:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:142:14 + --> tests/ui/explicit_deref_methods.rs:157:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 024999fc609ee..ab28c1ccd8e0d 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - eprintln!("with {}", value); + eprintln!("with {value}"); //~^ explicit_write eprintln!("with {} {}", 2, value); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { eprintln!("macro arg {}", one!()); //~^ explicit_write let width = 2; - eprintln!("{:w$}", value, w = width); + eprintln!("{value:w$}", w = width); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index c83c760d48c8b..975ee103b6278 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - writeln!(std::io::stderr(), "with {}", value).unwrap(); + writeln!(std::io::stderr(), "with {value}").unwrap(); //~^ explicit_write writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); //~^ explicit_write let width = 2; - writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); + writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 670a0411b3107..ef4b2a049a6d9 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:23:9 + --> tests/ui/explicit_write.rs:22:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` @@ -8,76 +8,76 @@ LL | write!(std::io::stdout(), "test").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::explicit_write)]` error: use of `write!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:25:9 + --> tests/ui/explicit_write.rs:24:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:27:9 + --> tests/ui/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:29:9 + --> tests/ui/explicit_write.rs:28:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:31:9 + --> tests/ui/explicit_write.rs:30:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:33:9 + --> tests/ui/explicit_write.rs:32:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:37:9 + --> tests/ui/explicit_write.rs:36:9 | LL | writeln!(std::io::stdout(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:39:9 + --> tests/ui/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:43:9 + --> tests/ui/explicit_write.rs:42:9 | -LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {}", value)` +LL | writeln!(std::io::stderr(), "with {value}").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:45:9 + --> tests/ui/explicit_write.rs:44:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:47:9 + --> tests/ui/explicit_write.rs:46:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:49:9 + --> tests/ui/explicit_write.rs:48:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:52:9 + --> tests/ui/explicit_write.rs:51:9 | -LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{:w$}", value, w = width)` +LL | writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{value:w$}", w = width)` error: aborting due to 13 previous errors diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 1c62c1e937b63..28bb1157f6e52 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,5 +1,4 @@ #![deny(clippy::fallible_impl_from)] -#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); @@ -62,7 +61,7 @@ impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { if s.parse::().ok().unwrap() != 42 { - panic!("{:?}", s); + panic!("{s:?}"); } Invalid } diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 402494b39f30b..25ecc8b0a39a2 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:6:1 + --> tests/ui/fallible_impl_from.rs:5:1 | LL | / impl From for Foo { LL | | @@ -11,7 +11,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:10:13 + --> tests/ui/fallible_impl_from.rs:9:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:29:1 + --> tests/ui/fallible_impl_from.rs:28:1 | LL | / impl From for Invalid { LL | | @@ -34,13 +34,13 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:34:13 + --> tests/ui/fallible_impl_from.rs:33:13 | LL | panic!(); | ^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:40:1 + --> tests/ui/fallible_impl_from.rs:39:1 | LL | / impl From> for Invalid { LL | | @@ -52,7 +52,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:44:17 + --> tests/ui/fallible_impl_from.rs:43:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:60:1 + --> tests/ui/fallible_impl_from.rs:59:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | @@ -77,12 +77,12 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:64:12 + --> tests/ui/fallible_impl_from.rs:63:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^ +LL | panic!("{s:?}"); + | ^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 275c9b4a3ab9a..e831e30a71d81 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -14,10 +14,8 @@ fn check_log_base() { //~^ suboptimal_flops let _ = x.ln(); //~^ suboptimal_flops - let _ = x.log2(); - //~^ suboptimal_flops - let _ = x.ln(); - //~^ suboptimal_flops + let _ = x.log(TWO); + let _ = x.log(E); let _ = (x as f32).log2(); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index a372ccbb9fb03..06cb1c8d96039 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -15,9 +15,7 @@ fn check_log_base() { let _ = x.log(std::f32::consts::E); //~^ suboptimal_flops let _ = x.log(TWO); - //~^ suboptimal_flops let _ = x.log(E); - //~^ suboptimal_flops let _ = (x as f32).log(2f32); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index e93b3af851cbe..3e141de626d94 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -19,44 +19,32 @@ error: logarithm for bases 2, 10 and e can be computed more accurately LL | let _ = x.log(std::f32::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:17:13 - | -LL | let _ = x.log(TWO); - | ^^^^^^^^^^ help: consider using: `x.log2()` - error: logarithm for bases 2, 10 and e can be computed more accurately --> tests/ui/floating_point_log.rs:19:13 | -LL | let _ = x.log(E); - | ^^^^^^^^ help: consider using: `x.ln()` - -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:21:13 - | LL | let _ = (x as f32).log(2f32); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:25:13 + --> tests/ui/floating_point_log.rs:23:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:27:13 + --> tests/ui/floating_point_log.rs:25:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:29:13 + --> tests/ui/floating_point_log.rs:27:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:35:13 + --> tests/ui/floating_point_log.rs:33:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -65,118 +53,118 @@ LL | let _ = (1f32 + 2.).ln(); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:37:13 + --> tests/ui/floating_point_log.rs:35:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:39:13 + --> tests/ui/floating_point_log.rs:37:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:41:13 + --> tests/ui/floating_point_log.rs:39:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:43:13 + --> tests/ui/floating_point_log.rs:41:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:45:13 + --> tests/ui/floating_point_log.rs:43:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:47:13 + --> tests/ui/floating_point_log.rs:45:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:49:13 + --> tests/ui/floating_point_log.rs:47:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:51:13 + --> tests/ui/floating_point_log.rs:49:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:53:13 + --> tests/ui/floating_point_log.rs:51:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:55:13 + --> tests/ui/floating_point_log.rs:53:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:64:13 + --> tests/ui/floating_point_log.rs:62:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:66:13 + --> tests/ui/floating_point_log.rs:64:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:68:13 + --> tests/ui/floating_point_log.rs:66:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:70:13 + --> tests/ui/floating_point_log.rs:68:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:72:13 + --> tests/ui/floating_point_log.rs:70:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:74:13 + --> tests/ui/floating_point_log.rs:72:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:76:13 + --> tests/ui/floating_point_log.rs:74:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:78:13 + --> tests/ui/floating_point_log.rs:76:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:80:13 + --> tests/ui/floating_point_log.rs:78:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index 0fd130609aee2..7da9401a308f7 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -206,3 +206,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 640828aa9bf6b..02962f83ce8aa 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -262,3 +262,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index 050cdfcba966e..dc7e09bbdc7d2 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice_1); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice_0); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice_0); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 91429bfea2762..f39ace101b45c 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice[1]); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice[0]); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice[0]); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/inefficient_to_string.fixed b/tests/ui/inefficient_to_string.fixed index a0d34e58a9255..29cf6de6ae5e8 100644 --- a/tests/ui/inefficient_to_string.fixed +++ b/tests/ui/inefficient_to_string.fixed @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = (**rrrcow).to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.rs b/tests/ui/inefficient_to_string.rs index cbe90d4a125b0..724955c60f795 100644 --- a/tests/ui/inefficient_to_string.rs +++ b/tests/ui/inefficient_to_string.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = rrrcow.to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.stderr b/tests/ui/inefficient_to_string.stderr index 8593c0addc5f8..ea3dd7e0ae2f8 100644 --- a/tests/ui/inefficient_to_string.stderr +++ b/tests/ui/inefficient_to_string.stderr @@ -1,5 +1,5 @@ error: calling `to_string` on `&&str` - --> tests/ui/inefficient_to_string.rs:10:21 + --> tests/ui/inefficient_to_string.rs:11:21 | LL | let _: String = rrstr.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` @@ -12,7 +12,7 @@ LL | #![deny(clippy::inefficient_to_string)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `to_string` on `&&&str` - --> tests/ui/inefficient_to_string.rs:12:21 + --> tests/ui/inefficient_to_string.rs:13:21 | LL | let _: String = rrrstr.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` @@ -20,7 +20,7 @@ LL | let _: String = rrrstr.to_string(); = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` error: calling `to_string` on `&&std::string::String` - --> tests/ui/inefficient_to_string.rs:21:21 + --> tests/ui/inefficient_to_string.rs:22:21 | LL | let _: String = rrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` @@ -28,7 +28,7 @@ LL | let _: String = rrstring.to_string(); = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::string::String` - --> tests/ui/inefficient_to_string.rs:23:21 + --> tests/ui/inefficient_to_string.rs:24:21 | LL | let _: String = rrrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` @@ -36,7 +36,7 @@ LL | let _: String = rrrstring.to_string(); = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:32:21 + --> tests/ui/inefficient_to_string.rs:33:21 | LL | let _: String = rrcow.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` @@ -44,7 +44,7 @@ LL | let _: String = rrcow.to_string(); = help: `&std::borrow::Cow<'_, str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<'_, str>` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:34:21 + --> tests/ui/inefficient_to_string.rs:35:21 | LL | let _: String = rrrcow.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 701a86534ba00..4e1668ed04fb7 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] +#![allow(clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { @@ -30,7 +30,7 @@ fn infinite_iters() { .rev() .cycle() .map(|x| x + 1_u32) - .for_each(|x| println!("{}", x)); + .for_each(|x| println!("{x}")); // infinite iter (0..3_u32).flat_map(|x| x..).sum::(); // infinite iter diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index b9e7c008f93e0..3db97313b621d 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -30,8 +30,8 @@ LL | | LL | | .rev() LL | | .cycle() LL | | .map(|x| x + 1_u32) -LL | | .for_each(|x| println!("{}", x)); - | |________________________________________^ +LL | | .for_each(|x| println!("{x}")); + | |______________________________________^ error: infinite iteration detected --> tests/ui/infinite_iter.rs:37:5 diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index c947968213948..afdf581bacf7a 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -72,33 +72,44 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; - let _ = Ipv4Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); + let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); + let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - - let _ = Ipv6Addr::UNSPECIFIED; + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_1, + ); + + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + ); } fn const_test2() { use std::net::Ipv4Addr; let _ = Ipv4Addr::LOCALHOST; //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); + let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant + let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); + let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 69a5c3b4e9230..04fc2f0f6fdae 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -73,15 +73,11 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -93,7 +89,6 @@ fn const_test1() { ); let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -110,15 +105,11 @@ fn const_test2() { let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); //~^ ip_constant let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - //~^ ip_constant let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - //~^ ip_constant } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 07d912b18a57b..44e3d6448dbd8 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -241,101 +241,7 @@ LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:75:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); -LL + let _ = Ipv4Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:77:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:79:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:83:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_1, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_1, -LL - ); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:95:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_0, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - ); -LL + let _ = Ipv6Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:110:13 + --> tests/ui/ip_constant.rs:105:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -346,53 +252,5 @@ LL - let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); LL + let _ = Ipv4Addr::LOCALHOST; | -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:112:13 - | -LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:114:13 - | -LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:118:13 - | -LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:120:13 - | -LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: aborting due to 30 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 46ba653eba2cf..3e066df77bfb4 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { for e in it { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index defe2584a93e1..98600d17c6df7 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { while let Some(e) = it.next() { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index eae2ce97fc6b1..ddee91fcfcd52 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> tests/ui/issue_2356.rs:17:9 + --> tests/ui/issue_2356.rs:16:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index 664f0b84a2077..b2a01124995c5 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint //~^ needless_lifetimes @@ -39,7 +38,7 @@ impl Foo { // rust-lang/rust#61115 // ok async fn print(s: &str) { - println!("{}", s); + println!("{s}"); } fn main() {} diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 0e181025430f3..b80a738a50bee 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:4:16 + --> tests/ui/issue_4266.rs:3:16 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^ ^^ @@ -8,13 +8,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = help: to override `-D warnings` add `#[allow(clippy::needless_lifetimes)]` error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:10:21 + --> tests/ui/issue_4266.rs:9:21 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^ ^^ ^^ error: methods called `new` usually take no `self` - --> tests/ui/issue_4266.rs:32:22 + --> tests/ui/issue_4266.rs:31:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 381d4cac4622b..6d984a495d2bd 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +20,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +35,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +186,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -199,13 +202,40 @@ pub fn issue12594() { } } +fn takes_unit(x: ()) {} + fn issue15061() { - fn return_unit() {} - fn do_something(x: ()) {} + let res = (); + returns_unit(); + //~^ let_unit_value + takes_unit(()); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => returns_unit(), + //~^ let_unit_value + } + if true {} + //~^ let_unit_value +} + +fn issue_15784() { let res = (); - return_unit(); + eprintln!("I return unit"); //~^ let_unit_value - do_something(()); + takes_unit(()); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + println!(); + //~^ let_unit_value + + Foo { value: () }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index cdfc74991c40d..a0e32f0b67a02 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +20,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +35,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +186,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -199,12 +202,38 @@ pub fn issue12594() { } } +fn takes_unit(x: ()) {} + fn issue15061() { - fn return_unit() {} - fn do_something(x: ()) {} + let res = returns_unit(); + //~^ let_unit_value + takes_unit(res); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => _ = returns_unit(), + //~^ let_unit_value + } - let res = return_unit(); + _ = if true {} //~^ let_unit_value - do_something(res); +} + +fn issue_15784() { + let res = eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(res); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + let value = println!(); + //~^ let_unit_value + + Foo { value }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 637c9ff686bdb..6e7b958df4d9a 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -1,14 +1,19 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:16:5 | LL | let _x = println!("x"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::let-unit-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` +help: omit the `let` binding + | +LL - let _x = println!("x"); +LL + println!("x"); + | error: this let-binding has unit value - --> tests/ui/let_unit.rs:60:5 + --> tests/ui/let_unit.rs:65:5 | LL | / let _ = v LL | | @@ -21,18 +26,12 @@ LL | | .unwrap(); | help: omit the `let` binding | -LL ~ v -LL + -LL + .into_iter() -LL + .map(|i| i * 2) -LL + .filter(|i| i.is_multiple_of(2)) -LL + .map(|_| ()) -LL + .next() -LL + .unwrap(); +LL - let _ = v +LL + v | error: this let-binding has unit value - --> tests/ui/let_unit.rs:110:5 + --> tests/ui/let_unit.rs:115:5 | LL | / let x = match Some(0) { LL | | @@ -45,17 +44,12 @@ LL | | }; | help: omit the `let` binding | -LL ~ match Some(0) { -LL + -LL + None => f2(1), -LL + Some(0) => f(), -LL + Some(1) => f2(3), -LL + Some(_) => (), -LL + }; +LL - let x = match Some(0) { +LL + match Some(0) { | error: this let-binding has unit value - --> tests/ui/let_unit.rs:192:9 + --> tests/ui/let_unit.rs:195:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,18 +63,70 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:206:5 + --> tests/ui/let_unit.rs:208:5 | -LL | let res = return_unit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let res = returns_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace variable usages with `()` | LL ~ let res = (); -LL ~ return_unit(); +LL ~ returns_unit(); +LL | +LL ~ takes_unit(()); + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:216:14 + | +LL | _ => _ = returns_unit(), + | ^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ => _ = returns_unit(), +LL + _ => returns_unit(), + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:220:5 + | +LL | _ = if true {} + | ^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ = if true {} +LL + if true {} + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:225:5 + | +LL | let res = eprintln!("I return unit"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ eprintln!("I return unit"); +LL | +LL ~ takes_unit(()); + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:235:5 + | +LL | let value = println!(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding and replace variable usages with `()` + | +LL ~ println!(); +LL | LL | -LL ~ do_something(()); +LL ~ Foo { value: () }; | -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index 62cdc1c141d0c..4b496a4932833 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -8,9 +8,6 @@ #[macro_use] extern crate proc_macros; -const INFINITE: f32 = f32::INFINITY; -const NEG_INFINITE: f32 = f32::NEG_INFINITY; - fn fn_test() -> f64 { f64::NEG_INFINITY } @@ -25,10 +22,6 @@ fn main() { //~^ manual_is_infinite if x != f32::INFINITY && x != f32::NEG_INFINITY {} //~^ manual_is_finite - if x == INFINITE || x == NEG_INFINITE {} - //~^ manual_is_infinite - if x != INFINITE && x != NEG_INFINITE {} - //~^ manual_is_finite let x = 1.0f64; if x == f64::INFINITY || x == f64::NEG_INFINITY {} //~^ manual_is_infinite @@ -64,4 +57,12 @@ fn main() { if x == f32::INFINITY || x == f32::NEG_INFINITY {} if x != f32::INFINITY && x != f32::NEG_INFINITY {} } + + { + let x = 1.0f32; + const X: f32 = f32::INFINITY; + const Y: f32 = f32::NEG_INFINITY; + if x == X || x == Y {} + if x != X && x != Y {} + } } diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr index 352c879c87d73..0a27e0eac48b3 100644 --- a/tests/ui/manual_float_methods.stderr +++ b/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:24:8 + --> tests/ui/manual_float_methods.rs:21:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:26:8 + --> tests/ui/manual_float_methods.rs:23:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,41 +32,13 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:28:8 - | -LL | if x == INFINITE || x == NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` - -error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:30:8 - | -LL | if x != INFINITE && x != NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use the dedicated method instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() {} - | -help: this will alter how it handles NaN; if that is a problem, use instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() || x.is_nan() {} - | -help: or, for conciseness - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if !x.is_infinite() {} - | - -error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:33:8 + --> tests/ui/manual_float_methods.rs:26:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:35:8 + --> tests/ui/manual_float_methods.rs:28:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,10 +60,10 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:50:12 + --> tests/ui/manual_float_methods.rs:43:12 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 187802bb76c9e..a04c601e08c10 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 61e14e5a3d9d3..7c67f6acf85de 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index a323ef700e767..d147cdae1f3b1 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -97,9 +97,6 @@ LL | if s.starts_with(PREFIX) { help: try using the `strip_prefix` method | LL ~ if let Some() = s.strip_prefix(PREFIX) { -LL ~ str::to_string(); -LL | -LL | LL ~ str::to_string(); | diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e29fb87dbc300..7e899a4766661 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -32,11 +31,11 @@ fn main() { // Lint let (x, y, z) = (a, b, c); { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } // Lint let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); // Ok foo!(a); // Ok @@ -47,7 +46,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -55,7 +54,7 @@ fn main() { // Lint { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); } // Lint { @@ -67,18 +66,18 @@ fn main() { // Lint let p = Point { x: 0, y: 7 }; let Point { x, y } = p; - println!("Coords: ({}, {})", x, y); + println!("Coords: ({x}, {y})"); // Lint let Point { x: x1, y: y1 } = p; - println!("Coords: ({}, {})", x1, y1); + println!("Coords: ({x1}, {y1})"); // Lint let x = 5; let ref r = x; - println!("Got a reference to {}", r); + println!("Got a reference to {r}"); // Lint let mut x = 5; let ref mut mr = x; - println!("Got a mutable reference to {}", mr); + println!("Got a mutable reference to {mr}"); // Lint let Point { x, y } = coords(); let product = x * y; @@ -122,7 +121,7 @@ fn issue_8723() { let (pre, suf) = val.split_at(idx); val = { - println!("{}", pre); + println!("{pre}"); suf }; @@ -210,20 +209,20 @@ mod issue15018 { let x = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } println!("x = {x}"); } fn not_used_later(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let x = 1; println!("x = {x}"); } @@ -231,27 +230,27 @@ mod issue15018 { #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {x} {y}"); if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index ede1ab32beb5f..37a96f2287c85 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -33,13 +32,13 @@ fn main() { match (a, b, c) { //~^ match_single_binding (x, y, z) => { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); }, } // Lint match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } // Ok foo!(a); @@ -51,7 +50,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -64,7 +63,7 @@ fn main() { //~^ match_single_binding _ => { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); }, } // Lint @@ -81,24 +80,24 @@ fn main() { let p = Point { x: 0, y: 7 }; match p { //~^ match_single_binding - Point { x, y } => println!("Coords: ({}, {})", x, y), + Point { x, y } => println!("Coords: ({x}, {y})"), } // Lint match p { //~^ match_single_binding - Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), } // Lint let x = 5; match x { //~^ match_single_binding - ref r => println!("Got a reference to {}", r), + ref r => println!("Got a reference to {r}"), } // Lint let mut x = 5; match x { //~^ match_single_binding - ref mut mr => println!("Got a mutable reference to {}", mr), + ref mut mr => println!("Got a mutable reference to {mr}"), } // Lint let product = match coords() { @@ -150,7 +149,7 @@ fn issue_8723() { val = match val.split_at(idx) { //~^ match_single_binding (pre, suf) => { - println!("{}", pre); + println!("{pre}"); suf }, }; @@ -273,7 +272,7 @@ mod issue15018 { let x = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } println!("x = {x}"); } @@ -281,7 +280,7 @@ mod issue15018 { fn not_used_later(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } } @@ -289,7 +288,7 @@ mod issue15018 { fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let x = 1; println!("x = {x}"); @@ -299,30 +298,30 @@ mod issue15018 { fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {x} {y}"), } if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index eea71777890e5..82fc43aaa5eae 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,10 +1,10 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:33:5 + --> tests/ui/match_single_binding.rs:32:5 | LL | / match (a, b, c) { LL | | LL | | (x, y, z) => { -LL | | println!("{} {} {}", x, y, z); +LL | | println!("{x} {y} {z}"); LL | | }, LL | | } | |_____^ @@ -15,27 +15,27 @@ help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:40:5 + --> tests/ui/match_single_binding.rs:39:5 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:58:5 + --> tests/ui/match_single_binding.rs:57:5 | LL | / match a { LL | | @@ -44,13 +44,13 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:63:5 + --> tests/ui/match_single_binding.rs:62:5 | LL | / match a { LL | | LL | | _ => { LL | | let x = 29; -LL | | println!("x has a value of {}", x); +LL | | println!("x has a value of {x}"); LL | | }, LL | | } | |_____^ @@ -59,12 +59,12 @@ help: consider using the match body instead | LL ~ { LL + let x = 29; -LL + println!("x has a value of {}", x); +LL + println!("x has a value of {x}"); LL + } | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:71:5 + --> tests/ui/match_single_binding.rs:70:5 | LL | / match a { LL | | @@ -86,67 +86,67 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:82:5 + --> tests/ui/match_single_binding.rs:81:5 | LL | / match p { LL | | -LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | Point { x, y } => println!("Coords: ({x}, {y})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x, y } = p; -LL + println!("Coords: ({}, {})", x, y); +LL + println!("Coords: ({x}, {y})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:87:5 + --> tests/ui/match_single_binding.rs:86:5 | LL | / match p { LL | | -LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; -LL + println!("Coords: ({}, {})", x1, y1); +LL + println!("Coords: ({x1}, {y1})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:93:5 + --> tests/ui/match_single_binding.rs:92:5 | LL | / match x { LL | | -LL | | ref r => println!("Got a reference to {}", r), +LL | | ref r => println!("Got a reference to {r}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref r = x; -LL + println!("Got a reference to {}", r); +LL + println!("Got a reference to {r}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:99:5 + --> tests/ui/match_single_binding.rs:98:5 | LL | / match x { LL | | -LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | ref mut mr => println!("Got a mutable reference to {mr}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref mut mr = x; -LL + println!("Got a mutable reference to {}", mr); +LL + println!("Got a mutable reference to {mr}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:104:5 + --> tests/ui/match_single_binding.rs:103:5 | LL | / let product = match coords() { LL | | @@ -161,7 +161,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:113:18 + --> tests/ui/match_single_binding.rs:112:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -179,7 +179,7 @@ LL ~ }) | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:140:5 + --> tests/ui/match_single_binding.rs:139:5 | LL | / match x { LL | | @@ -189,12 +189,12 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> tests/ui/match_single_binding.rs:150:5 + --> tests/ui/match_single_binding.rs:149:5 | LL | / val = match val.split_at(idx) { LL | | LL | | (pre, suf) => { -LL | | println!("{}", pre); +LL | | println!("{pre}"); LL | | suf LL | | }, LL | | }; @@ -204,13 +204,13 @@ help: consider removing the `match` expression | LL ~ let (pre, suf) = val.split_at(idx); LL + val = { -LL + println!("{}", pre); +LL + println!("{pre}"); LL + suf LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:164:16 + --> tests/ui/match_single_binding.rs:163:16 | LL | let _ = || match side_effects() { | ________________^ @@ -228,7 +228,7 @@ LL ~ }; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:171:5 + --> tests/ui/match_single_binding.rs:170:5 | LL | / match r { LL | | @@ -253,7 +253,7 @@ LL ~ }; | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:185:5 + --> tests/ui/match_single_binding.rs:184:5 | LL | / match 1 { LL | | @@ -262,7 +262,7 @@ LL | | } | |_____^ help: consider using the match body instead: `();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:190:13 + --> tests/ui/match_single_binding.rs:189:13 | LL | let a = match 1 { | _____________^ @@ -272,7 +272,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:195:5 + --> tests/ui/match_single_binding.rs:194:5 | LL | / match 1 { LL | | @@ -281,7 +281,7 @@ LL | | } | |_____^ help: consider using the match body instead: `side_effects();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:200:13 + --> tests/ui/match_single_binding.rs:199:13 | LL | let b = match 1 { | _____________^ @@ -291,7 +291,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:205:5 + --> tests/ui/match_single_binding.rs:204:5 | LL | / match 1 { LL | | @@ -300,7 +300,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("1");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:210:13 + --> tests/ui/match_single_binding.rs:209:13 | LL | let c = match 1 { | _____________^ @@ -310,7 +310,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:216:9 + --> tests/ui/match_single_binding.rs:215:9 | LL | / match 1 { LL | | @@ -319,7 +319,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:220:9 + --> tests/ui/match_single_binding.rs:219:9 | LL | / match 1 { LL | | @@ -328,7 +328,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:224:9 + --> tests/ui/match_single_binding.rs:223:9 | LL | / match 1 { LL | | @@ -337,7 +337,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:239:5 + --> tests/ui/match_single_binding.rs:238:5 | LL | / match dbg!(3) { LL | | _ => println!("here"), @@ -351,7 +351,7 @@ LL + println!("here"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:243:5 + --> tests/ui/match_single_binding.rs:242:5 | LL | / match dbg!(3) { LL | | id!(a) => println!("found {a}"), @@ -365,7 +365,7 @@ LL + println!("found {a}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:247:5 + --> tests/ui/match_single_binding.rs:246:5 | LL | / let id!(_a) = match dbg!(3) { LL | | id!(b) => dbg!(b + 1), @@ -379,7 +379,7 @@ LL + let id!(_a) = dbg!(b + 1); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:255:21 + --> tests/ui/match_single_binding.rs:254:21 | LL | inner: [(); match 1 { | _____________________^ @@ -397,7 +397,7 @@ LL ~ }], | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:263:13 + --> tests/ui/match_single_binding.rs:262:13 | LL | / match 1 { LL | | @@ -412,11 +412,11 @@ LL + 42 | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:274:9 + --> tests/ui/match_single_binding.rs:273:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | @@ -424,61 +424,61 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:282:9 + --> tests/ui/match_single_binding.rs:281:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z) +LL + println!("{x} {y} {z}") | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:290:9 + --> tests/ui/match_single_binding.rs:289:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:300:9 + --> tests/ui/match_single_binding.rs:299:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {x} {y}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {x} {y}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:310:13 + --> tests/ui/match_single_binding.rs:309:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | @@ -486,27 +486,27 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:320:13 + --> tests/ui/match_single_binding.rs:319:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:335:12 + --> tests/ui/match_single_binding.rs:334:12 | LL | && match b { | ____________^ @@ -516,7 +516,7 @@ LL | | }; | |_________^ help: consider using the match body instead: `b < c` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:341:12 + --> tests/ui/match_single_binding.rs:340:12 | LL | && match (a, b) { | ____________^ diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index 988121f50d0fa..f00987470ae1b 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -29,7 +28,7 @@ fn main() { #[rustfmt::skip] Some((first, _second)) => { let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b) + println!("a {a:?} and b {b:?}") }, None => println!("nothing"), } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index a4fb2bd6f3817..5416f647b4e6c 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -30,7 +29,7 @@ fn main() { Some((first, _second)) => { match get_tup() { //~^ match_single_binding - (a, b) => println!("a {:?} and b {:?}", a, b), + (a, b) => println!("a {a:?} and b {b:?}"), } }, None => println!("nothing"), diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index a24cbe3eed766..65b8aa6acd5e1 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:17:36 + --> tests/ui/match_single_binding2.rs:16:36 | LL | Some((iter, _item)) => match iter.size_hint() { | ____________________________________^ @@ -19,22 +19,22 @@ LL ~ }, | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:31:13 + --> tests/ui/match_single_binding2.rs:30:13 | LL | / match get_tup() { LL | | -LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | (a, b) => println!("a {a:?} and b {b:?}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); -LL + println!("a {:?} and b {:?}", a, b) +LL + println!("a {a:?} and b {b:?}") | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:43:5 + --> tests/ui/match_single_binding2.rs:42:5 | LL | / match side_effects() { LL | | @@ -49,7 +49,7 @@ LL + println!("Side effects"); | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:51:5 + --> tests/ui/match_single_binding2.rs:50:5 | LL | / match match x { LL | | diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 870ef23113a2f..94ad1aad3eb75 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::take(dbg!(&mut text)); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index b4ed5eafea953..ac79660f0f1ee 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::replace(dbg!(&mut text), String::default()); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index fb4a367266d30..104c985400282 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -181,5 +181,11 @@ error: replacing an `Option` with `Some(..)` LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` -error: aborting due to 29 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> tests/ui/mem_replace.rs:185:20 + | +LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` + +error: aborting due to 30 previous errors diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index f73fe288b0f8c..9595888b99f83 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -135,6 +135,26 @@ fn filter_next() { let _ = foo.filter(42).next(); } +#[rustfmt::skip] +fn filter_next_back() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Multi-line case. + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + fn main() { filter_next(); + filter_next_back(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b226ce7c65dab..45efea4ee01cd 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -24,5 +24,16 @@ LL | | ).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 2 previous errors +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods.rs:143:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed index 49730d8115584..287d8d881ec28 100644 --- a/tests/ui/methods_fixable.fixed +++ b/tests/ui/methods_fixable.fixed @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().find(|&x| *x < 0); //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs index a499b63b6f5bd..11ce1b63560db 100644 --- a/tests/ui/methods_fixable.rs +++ b/tests/ui/methods_fixable.rs @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().filter(|&x| *x < 0).next(); //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 852e7a224a6e3..d26b5e9ac271e 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -7,5 +7,17 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 1 previous error +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:12:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:18:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` + +error: aborting due to 3 previous errors diff --git a/tests/ui/module_inception.rs b/tests/ui/module_inception.rs index 15b7fb8777700..5735dd5867d55 100644 --- a/tests/ui/module_inception.rs +++ b/tests/ui/module_inception.rs @@ -38,4 +38,13 @@ mod bar { mod bar {} } +mod with_inner_impl { + struct S; + impl S { + fn f() { + mod with_inner_impl {} + } + } +} + fn main() {} diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed new file mode 100644 index 0000000000000..f9a7f5dcb5a15 --- /dev/null +++ b/tests/ui/mut_mut.fixed @@ -0,0 +1,92 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unnecessary_operation, + clippy::needless_pass_by_ref_mut +)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +fn fun(x: &mut u32) { + //~^ mut_mut +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +#[inline_macros] +fn main() { + let mut x = &mut 1u32; + //~^ mut_mut + { + let mut y = &mut *x; + //~^ mut_mut + } + + { + let y: &mut u32 = &mut 2; + //~^ mut_mut + //~| mut_mut + } + + { + let y: &mut u32 = &mut 2; + //~^ mut_mut + //~| mut_mut + } + + let mut z = inline!(&mut $(&mut 3u32)); +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} + +fn issue6922() { + // do not lint from an external macro + external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;); +} + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index bbcdbc89b6a4b..bbec48011a17e 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -12,9 +12,8 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) -> bool { +fn fun(x: &mut &mut u32) { //~^ mut_mut - **x > 0 } fn less_fun(x: *mut *mut u32) { @@ -37,23 +36,19 @@ fn main() { //~^ mut_mut } - if fun(x) { + { let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - **y + **x; } - if fun(x) { + { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut - ***y + **x; } let mut z = inline!(&mut $(&mut 3u32)); - //~^ mut_mut } fn issue939() { diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 74b0c9ba145a9..85e9c5649b89a 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,61 +1,47 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:15:11 | -LL | fn fun(x: &mut &mut u32) -> bool { - | ^^^^^^^^^^^^^ +LL | fn fun(x: &mut &mut u32) { + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:33:17 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:55:25 - | -LL | let mut z = inline!(&mut $(&mut 3u32)); - | ^ - | - = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:36:21 +error: this expression mutably borrows a mutable reference + --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:32 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:16 +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut.rs:40:16 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^^^ - -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:37 - | -LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:16 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:21 +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs new file mode 100644 index 0000000000000..271cb7b968898 --- /dev/null +++ b/tests/ui/mut_mut_unfixable.rs @@ -0,0 +1,42 @@ +//@no-rustfix + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![expect(clippy::no_effect)] + +//! removing the extra `&mut`s will break the derefs + +fn fun(x: &mut &mut u32) -> bool { + //~^ mut_mut + **x > 0 +} + +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut x; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + let y = &mut &mut 2; + //~^ mut_mut + **y + **x; + } + + if fun(x) { + let y = &mut &mut &mut 2; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + // The lint will remove the extra `&mut`, but the result will still be a `&mut` of an expr + // of type `&mut _` (x), so the lint will fire again. That's because we've decided that + // doing both fixes in one run is not worth it, given how improbable code like this is. + let y = &mut &mut x; + //~^ mut_mut + } +} diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr new file mode 100644 index 0000000000000..cf66eb2ed1eca --- /dev/null +++ b/tests/ui/mut_mut_unfixable.stderr @@ -0,0 +1,41 @@ +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:9:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:15:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` + +error: this expression mutably borrows a mutable reference + --> tests/ui/mut_mut_unfixable.rs:18:21 + | +LL | let mut y = &mut x; + | ^^^^^^ help: reborrow instead: `&mut *x` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:24:17 + | +LL | let y = &mut &mut 2; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:30:17 + | +LL | let y = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:39:17 + | +LL | let y = &mut &mut x; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr deleted file mode 100644 index 5ecfaa37416ba..0000000000000 --- a/tests/ui/mut_reference.stderr +++ /dev/null @@ -1,77 +0,0 @@ -error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:56:15 - | -LL | takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - | - = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` - -error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:58:19 - | -LL | takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:60:22 - | -LL | takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:62:21 - | -LL | takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:66:12 - | -LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:69:12 - | -LL | as_ptr(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:72:12 - | -LL | as_ptr(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:75:12 - | -LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:80:25 - | -LL | my_struct.takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:82:29 - | -LL | my_struct.takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:84:32 - | -LL | my_struct.takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:86:31 - | -LL | my_struct.takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: aborting due to 12 previous errors - diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 277c335cd8851..9a5e90b48065c 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, @@ -322,3 +321,91 @@ where Self { _kv: None } } } + +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +#[cfg(not(test))] +impl Default for NewWithCfg { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +#[cfg(not(test))] +#[cfg(panic = "unwind")] +impl Default for NewWith2Cfgs { + fn default() -> Self { + Self::new() + } +} + +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl Default for NewWithExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +#[cfg(not(test))] +impl Default for NewWithCfgAndExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index f2844897c93d9..f7466aa321896 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, @@ -265,3 +264,63 @@ where Self { _kv: None } } } + +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 70a65aba464b8..1e0d5e2131994 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:13:5 + --> tests/ui/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | @@ -20,7 +20,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> tests/ui/new_without_default.rs:23:5 + --> tests/ui/new_without_default.rs:22:5 | LL | / pub fn new() -> Self { LL | | @@ -39,7 +39,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> tests/ui/new_without_default.rs:89:5 + --> tests/ui/new_without_default.rs:88:5 | LL | / pub fn new() -> LtKo<'c> { LL | | @@ -58,7 +58,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Const` - --> tests/ui/new_without_default.rs:123:5 + --> tests/ui/new_without_default.rs:122:5 | LL | / pub const fn new() -> Const { LL | | @@ -76,7 +76,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> tests/ui/new_without_default.rs:184:5 + --> tests/ui/new_without_default.rs:183:5 | LL | / pub fn new() -> Self { LL | | @@ -95,7 +95,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> tests/ui/new_without_default.rs:194:5 + --> tests/ui/new_without_default.rs:193:5 | LL | / pub fn new() -> Self { LL | | @@ -114,7 +114,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> tests/ui/new_without_default.rs:203:5 + --> tests/ui/new_without_default.rs:202:5 | LL | / pub fn new() -> Self { LL | | @@ -133,7 +133,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:216:9 + --> tests/ui/new_without_default.rs:215:9 | LL | / pub fn new() -> Self { LL | | @@ -154,7 +154,7 @@ LL ~ impl Foo { | error: you should consider adding a `Default` implementation for `MyStruct` - --> tests/ui/new_without_default.rs:263:5 + --> tests/ui/new_without_default.rs:262:5 | LL | / pub fn new() -> Self { LL | | @@ -174,5 +174,84 @@ LL + } LL + } | -error: aborting due to 9 previous errors +error: you should consider adding a `Default` implementation for `NewWithCfg` + --> tests/ui/new_without_default.rs:273:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfg { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfg { + | + +error: you should consider adding a `Default` implementation for `NewWith2Cfgs` + --> tests/ui/new_without_default.rs:283:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + #[cfg(panic = "unwind")] +LL + impl Default for NewWith2Cfgs { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWith2Cfgs { + | + +error: you should consider adding a `Default` implementation for `NewWithExtraneous` + --> tests/ui/new_without_default.rs:292:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + impl Default for NewWithExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } + | + +error: you should consider adding a `Default` implementation for `NewWithCfgAndExtraneous` + --> tests/ui/new_without_default.rs:302:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfgAndExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfgAndExtraneous { + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/only_used_in_recursion.rs b/tests/ui/only_used_in_recursion.rs index 7d6075ba9ea47..d58635969238b 100644 --- a/tests/ui/only_used_in_recursion.rs +++ b/tests/ui/only_used_in_recursion.rs @@ -1,4 +1,5 @@ #![warn(clippy::only_used_in_recursion)] +#![warn(clippy::self_only_used_in_recursion)] //@no-rustfix fn _simple(x: u32) -> u32 { x @@ -74,7 +75,7 @@ impl A { } fn _method_self(&self, flag: usize, a: usize) -> usize { - //~^ only_used_in_recursion + //~^ self_only_used_in_recursion //~| only_used_in_recursion if flag == 0 { 0 } else { self._method_self(flag - 1, a) } diff --git a/tests/ui/only_used_in_recursion.stderr b/tests/ui/only_used_in_recursion.stderr index ca08319e11200..5d1e3e9c50fd9 100644 --- a/tests/ui/only_used_in_recursion.stderr +++ b/tests/ui/only_used_in_recursion.stderr @@ -1,11 +1,11 @@ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:11:27 + --> tests/ui/only_used_in_recursion.rs:12:27 | LL | fn _one_unused(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:14:53 + --> tests/ui/only_used_in_recursion.rs:15:53 | LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } | ^ @@ -13,181 +13,183 @@ LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:27 + --> tests/ui/only_used_in_recursion.rs:18:27 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:53 + --> tests/ui/only_used_in_recursion.rs:22:53 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:35 + --> tests/ui/only_used_in_recursion.rs:18:35 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:56 + --> tests/ui/only_used_in_recursion.rs:22:56 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:24:26 + --> tests/ui/only_used_in_recursion.rs:25:26 | LL | fn _with_calc(flag: u32, a: i64) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:30:32 + --> tests/ui/only_used_in_recursion.rs:31:32 | LL | _with_calc(flag - 1, (-a + 10) * 5) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:33 + --> tests/ui/only_used_in_recursion.rs:40:33 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:38 + --> tests/ui/only_used_in_recursion.rs:47:38 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:41 + --> tests/ui/only_used_in_recursion.rs:40:41 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:45 + --> tests/ui/only_used_in_recursion.rs:47:45 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:35 + --> tests/ui/only_used_in_recursion.rs:51:35 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:39 + --> tests/ui/only_used_in_recursion.rs:58:39 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:43 + --> tests/ui/only_used_in_recursion.rs:51:43 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:43 + --> tests/ui/only_used_in_recursion.rs:58:43 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:61:30 + --> tests/ui/only_used_in_recursion.rs:62:30 | LL | fn _not_primitive(flag: u32, b: String) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:64:56 + --> tests/ui/only_used_in_recursion.rs:65:56 | LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:70:29 + --> tests/ui/only_used_in_recursion.rs:71:29 | LL | fn _method(flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:73:59 + --> tests/ui/only_used_in_recursion.rs:74:59 | LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) } | ^ -error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:22 +error: `self` is only used in recursion + --> tests/ui/only_used_in_recursion.rs:77:22 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^^^^ | -note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:35 +note: `self` used here + --> tests/ui/only_used_in_recursion.rs:81:35 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^^^^ + = note: `-D clippy::self-only-used-in-recursion` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:41 + --> tests/ui/only_used_in_recursion.rs:77:41 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:63 + --> tests/ui/only_used_in_recursion.rs:81:63 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:90:26 + --> tests/ui/only_used_in_recursion.rs:91:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:93:58 + --> tests/ui/only_used_in_recursion.rs:94:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:96:38 + --> tests/ui/only_used_in_recursion.rs:97:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:99:62 + --> tests/ui/only_used_in_recursion.rs:100:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:124:26 + --> tests/ui/only_used_in_recursion.rs:125:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:127:58 + --> tests/ui/only_used_in_recursion.rs:128:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:130:38 + --> tests/ui/only_used_in_recursion.rs:131:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:133:62 + --> tests/ui/only_used_in_recursion.rs:134:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7a0be97017ebc..386351aa39f51 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, @@ -479,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_else(|_| Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_default(); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 724af606de9cf..e27f9aa65c37a 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, @@ -479,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 40b25f91154dd..6bce06ab20eb6 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:53:22 + --> tests/ui/or_fun_call.rs:52:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:57:14 + --> tests/ui/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:61:21 + --> tests/ui/or_fun_call.rs:60:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:65:14 + --> tests/ui/or_fun_call.rs:64:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:69:19 + --> tests/ui/or_fun_call.rs:68:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:73:24 + --> tests/ui/or_fun_call.rs:72:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:77:23 + --> tests/ui/or_fun_call.rs:76:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:96:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:18 + --> tests/ui/or_fun_call.rs:100:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:105:14 + --> tests/ui/or_fun_call.rs:104:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:109:21 + --> tests/ui/or_fun_call.rs:108:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:19 + --> tests/ui/or_fun_call.rs:112:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:23 + --> tests/ui/or_fun_call.rs:116:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:21 + --> tests/ui/or_fun_call.rs:120:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:125:25 + --> tests/ui/or_fun_call.rs:124:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:129:21 + --> tests/ui/or_fun_call.rs:128:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:134:17 + --> tests/ui/or_fun_call.rs:133:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:139:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:142:21 + --> tests/ui/or_fun_call.rs:141:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:167:35 + --> tests/ui/or_fun_call.rs:166:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:210:18 + --> tests/ui/or_fun_call.rs:209:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:218:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:221:14 + --> tests/ui/or_fun_call.rs:220:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:297:25 + --> tests/ui/or_fun_call.rs:296:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:299:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:302:25 + --> tests/ui/or_fun_call.rs:301:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:18 + --> tests/ui/or_fun_call.rs:332:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:28 + --> tests/ui/or_fun_call.rs:336:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:27 + --> tests/ui/or_fun_call.rs:340:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:345:22 + --> tests/ui/or_fun_call.rs:344:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:23 + --> tests/ui/or_fun_call.rs:348:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:352:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:357:25 + --> tests/ui/or_fun_call.rs:356:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:398:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:403:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:17 + --> tests/ui/or_fun_call.rs:408:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,58 +235,82 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:415:17 + --> tests/ui/or_fun_call.rs:414:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:420:17 + --> tests/ui/or_fun_call.rs:419:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:427:21 + --> tests/ui/or_fun_call.rs:426:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:442:19 + --> tests/ui/or_fun_call.rs:441:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:444:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:447:19 + --> tests/ui/or_fun_call.rs:446:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:458:15 + --> tests/ui/or_fun_call.rs:457:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:468:15 + --> tests/ui/or_fun_call.rs:467:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:478:15 + --> tests/ui/or_fun_call.rs:477:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` -error: aborting due to 45 previous errors +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:483:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:485:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:491:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:493:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: aborting due to 49 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index ac81b324c2049..2c5ee02450389 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -475,3 +475,28 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = result?; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + result?; + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index b5866dac6b8f6..b9ff9d1565b2f 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -583,3 +583,35 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = match result { + //~^ question_mark + Ok(v) => v, + Err(e) => return Err(e), + }; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + //~^ question_mark + return Err(reason); + } + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1ecd936292e55..9b2896328e665 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -313,5 +313,25 @@ LL | | Err(err) => return Err(<&str as Into>::into(err)), LL | | }; | |_____^ help: try instead: `some_result?` -error: aborting due to 33 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:596:17 + | +LL | let x = match result { + | _________________^ +LL | | +LL | | Ok(v) => v, +LL | | Err(e) => return Err(e), +LL | | }; + | |_________^ help: try instead: `result?` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:609:9 + | +LL | / if let Err(reason) = result { +LL | | +LL | | return Err(reason); +LL | | } + | |_________^ help: replace it with: `result?;` + +error: aborting due to 35 previous errors diff --git a/tests/ui/range.fixed b/tests/ui/range.fixed index 0e951d88091b7..c8e654600045e 100644 --- a/tests/ui/range.fixed +++ b/tests/ui/range.fixed @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().enumerate(); //~^ range_zip_with_len + //~v range_zip_with_len + for (i, e) in v1.iter().enumerate() { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().enumerate().for_each(|(i, e)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.rs b/tests/ui/range.rs index 5343801647434..352d517eabdd7 100644 --- a/tests/ui/range.rs +++ b/tests/ui/range.rs @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().zip(0..v1.len()); //~^ range_zip_with_len + //~v range_zip_with_len + for (e, i) in v1.iter().zip(0..v1.len()) { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index 798ce1842d8b5..b0a852a69fc5a 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -2,10 +2,35 @@ error: using `.zip()` with a range and `.len()` --> tests/ui/range.rs:6:14 | LL | let _x = v1.iter().zip(0..v1.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v1.iter().enumerate()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` | + = note: the order of the element and the index will be swapped = note: `-D clippy::range-zip-with-len` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` -error: aborting due to 1 previous error +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:10:19 + | +LL | for (e, i) in v1.iter().zip(0..v1.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - for (e, i) in v1.iter().zip(0..v1.len()) { +LL + for (i, e) in v1.iter().enumerate() { + | + +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:16:5 + | +LL | v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - v1.iter().zip(0..v1.len()).for_each(|(e, i)| { +LL + v1.iter().enumerate().for_each(|(i, e)| { + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/range_unfixable.rs b/tests/ui/range_unfixable.rs new file mode 100644 index 0000000000000..259be23fa1e5a --- /dev/null +++ b/tests/ui/range_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix +#![allow(clippy::useless_vec)] +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; + + // Do not autofix, `filter()` would not consume the iterator. + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); +} diff --git a/tests/ui/range_unfixable.stderr b/tests/ui/range_unfixable.stderr new file mode 100644 index 0000000000000..fb03ea2c05f6b --- /dev/null +++ b/tests/ui/range_unfixable.stderr @@ -0,0 +1,12 @@ +error: using `.zip()` with a range and `.len()` + --> tests/ui/range_unfixable.rs:10:5 + | +LL | v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` + | + = note: the order of the element and the index will be swapped + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index ff81c64260274..fdd851414746b 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_time_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b5d5d07e639a0..591c8ca53ac22 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_duration_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 2487dfc8eba44..b54fec8c57949 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,436 +8,442 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` + --> tests/ui/rename.rs:135:9 + | +LL | #![warn(clippy::unchecked_duration_subtraction)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` + error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:135:9 + --> tests/ui/rename.rs:137:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:136:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 73 previous errors +error: aborting due to 74 previous errors diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed index e739e176f0acd..c08d630a32f7f 100644 --- a/tests/ui/repeat_once.fixed +++ b/tests/ui/repeat_once.fixed @@ -10,8 +10,7 @@ fn main() { //~^ repeat_once let b = slice.to_vec(); //~^ repeat_once - let c = "hello".to_string(); - //~^ repeat_once + let c = "hello".repeat(N); let d = "hi".to_string(); //~^ repeat_once let e = s.to_string(); diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs index 89ab94bbaee85..d967fdc466ed7 100644 --- a/tests/ui/repeat_once.rs +++ b/tests/ui/repeat_once.rs @@ -11,7 +11,6 @@ fn main() { let b = slice.repeat(1); //~^ repeat_once let c = "hello".repeat(N); - //~^ repeat_once let d = "hi".repeat(1); //~^ repeat_once let e = s.repeat(1); diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr index 3db7a3568f8e6..62dbf7d233759 100644 --- a/tests/ui/repeat_once.stderr +++ b/tests/ui/repeat_once.stderr @@ -14,28 +14,22 @@ LL | let b = slice.repeat(1); | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:13:13 - | -LL | let c = "hello".repeat(N); - | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` - -error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:15:13 + --> tests/ui/repeat_once.rs:14:13 | LL | let d = "hi".repeat(1); | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:17:13 + --> tests/ui/repeat_once.rs:16:13 | LL | let e = s.repeat(1); | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` error: calling `repeat(1)` on a string literal - --> tests/ui/repeat_once.rs:19:13 + --> tests/ui/repeat_once.rs:18:13 | LL | let f = string.repeat(1); | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr similarity index 86% rename from tests/ui/should_impl_trait/method_list_1.stderr rename to tests/ui/should_impl_trait/method_list_1.edition2015.stderr index 5609d6a21a360..0312fa8f04fae 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:24:5 + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:30:5 + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> tests/ui/should_impl_trait/method_list_1.rs:36:5 + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> tests/ui/should_impl_trait/method_list_1.rs:42:5 + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> tests/ui/should_impl_trait/method_list_1.rs:48:5 + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> tests/ui/should_impl_trait/method_list_1.rs:54:5 + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> tests/ui/should_impl_trait/method_list_1.rs:60:5 + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:66:5 + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> tests/ui/should_impl_trait/method_list_1.rs:72:5 + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> tests/ui/should_impl_trait/method_list_1.rs:78:5 + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:84:5 + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:90:5 + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:96:5 + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> tests/ui/should_impl_trait/method_list_1.rs:102:5 + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> tests/ui/should_impl_trait/method_list_1.rs:108:5 + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 | LL | / pub fn drop(&mut self) { LL | | diff --git a/tests/ui/should_impl_trait/method_list_1.edition2021.stderr b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr new file mode 100644 index 0000000000000..0312fa8f04fae --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 + | +LL | / pub fn default() -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 + | +LL | / pub fn drop(&mut self) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index e8de0e04c0c4c..bbb04c0c5aa12 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/tests/ui/should_impl_trait/method_list_2.edition2015.stderr b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr new file mode 100644 index 0000000000000..259815908fee0 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr @@ -0,0 +1,172 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.edition2021.stderr b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr new file mode 100644 index 0000000000000..2f90b61e7a176 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:34:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 1f25ab3938a3d..4dfbe7e0f9f4d 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, @@ -29,7 +32,7 @@ impl T { } pub fn from_iter(iter: T) -> Self { - //~^ should_implement_trait + //~[edition2021]^ should_implement_trait unimplemented!() } diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index db5107600ee6d..03982b069e675 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -218,7 +218,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { if let Some(v) = bar { unsafe { let r = &v as *const i32; println!("{}", *r); @@ -330,6 +330,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { println!(); //~^^^^ single_match @@ -367,7 +368,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index a367b94c4ca6b..e28128e35adab 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -269,7 +269,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { match bar { Some(v) => unsafe { let r = &v as *const i32; @@ -397,6 +397,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { match DATA { DATA => println!(), @@ -462,7 +463,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 1a4edc45c928d..ba8bc5af5a608 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -225,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:401:5 + --> tests/ui/single_match.rs:402:5 | LL | / match DATA { LL | | DATA => println!(), @@ -234,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:407:5 + --> tests/ui/single_match.rs:408:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -243,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:414:5 + --> tests/ui/single_match.rs:415:5 | LL | / match i { LL | | i => { @@ -263,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:423:5 + --> tests/ui/single_match.rs:424:5 | LL | / match i { LL | | i => {}, @@ -272,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:429:5 + --> tests/ui/single_match.rs:430:5 | LL | / match i { LL | | i => (), @@ -281,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:435:5 + --> tests/ui/single_match.rs:436:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -290,7 +290,7 @@ LL | | } | |_____^ help: try: `println!();` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:443:5 + --> tests/ui/single_match.rs:444:5 | LL | / match x.pop() { LL | | // bla @@ -302,7 +302,7 @@ LL | | } = note: you might want to preserve the comments from inside the `match` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:452:5 + --> tests/ui/single_match.rs:453:5 | LL | / match x.pop() { LL | | // bla @@ -322,7 +322,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:478:5 + --> tests/ui/single_match.rs:479:5 | LL | / match mac!(some) { LL | | Some(u) => println!("{u}"), @@ -331,7 +331,7 @@ LL | | } | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:486:5 + --> tests/ui/single_match.rs:487:5 | LL | / match mac!(str) { LL | | "foo" => println!("eq"), diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed deleted file mode 100644 index bddffe44ac4d0..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::unchecked_duration_subtraction)] - -use std::time::{Duration, Instant}; - -fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); - - let _ = _first.checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = Instant::now().checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction -} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs deleted file mode 100644 index bb0f712396424..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::unchecked_duration_subtraction)] - -use std::time::{Duration, Instant}; - -fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); - - let _ = _first - second; - //~^ unchecked_duration_subtraction - - let _ = Instant::now() - Duration::from_secs(5); - //~^ unchecked_duration_subtraction - - let _ = _first - Duration::from_secs(5); - //~^ unchecked_duration_subtraction - - let _ = Instant::now() - second; - //~^ unchecked_duration_subtraction -} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr deleted file mode 100644 index be291c320e689..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:9:13 - | -LL | let _ = _first - second; - | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` - | - = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:12:13 - | -LL | let _ = Instant::now() - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:15:13 - | -LL | let _ = _first - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:18:13 - | -LL | let _ = Instant::now() - second; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/unchecked_time_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed new file mode 100644 index 0000000000000..2f923fef4c25d --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -0,0 +1,37 @@ +#![warn(clippy::unchecked_time_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Instant::now().checked_sub(second).unwrap(); + //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1.checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = second.checked_sub(dur1).unwrap(); + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = (2 * dur1).checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs new file mode 100644 index 0000000000000..cf727f62aafa7 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.rs @@ -0,0 +1,37 @@ +#![warn(clippy::unchecked_time_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + //~^ unchecked_time_subtraction + + let _ = Instant::now() - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = _first - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = Instant::now() - second; + //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1 - dur2; + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10) - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = second - dur1; + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = 2 * dur1 - dur2; + //~^ unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr new file mode 100644 index 0000000000000..7a39712269cf7 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -0,0 +1,53 @@ +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:9:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:12:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:15:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:18:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:25:13 + | +LL | let _ = dur1 - dur2; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:28:13 + | +LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:31:13 + | +LL | let _ = second - dur1; + | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:35:13 + | +LL | let _ = 2 * dur1 - dur2; + | ^^^^^^^^^^^^^^^ help: try: `(2 * dur1).checked_sub(dur2).unwrap()` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/unchecked_time_subtraction_unfixable.rs b/tests/ui/unchecked_time_subtraction_unfixable.rs new file mode 100644 index 0000000000000..4b6a5ca156209 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unchecked_time_subtraction)] +//@no-rustfix + +use std::time::{Duration, Instant}; + +fn main() { + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + let dur3 = Duration::from_secs(1); + + // Chained Duration subtraction - should lint without suggestion due to complexity + let _ = dur1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction + + // Chained Instant - Duration subtraction - should lint without suggestion due to complexity + let instant1 = Instant::now(); + + let _ = instant1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr new file mode 100644 index 0000000000000..c25c112b06ce0 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -0,0 +1,29 @@ +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^ help: try: `instant1.checked_sub(dur2).unwrap()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs deleted file mode 100644 index 7335b6f9f0390..0000000000000 --- a/tests/ui/unnecessary_clone.rs +++ /dev/null @@ -1,111 +0,0 @@ -// does not test any rustfixable lints -#![warn(clippy::clone_on_ref_ptr)] -#![allow(unused)] -#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] -//@no-rustfix -use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; - -trait SomeTrait {} -struct SomeImpl; -impl SomeTrait for SomeImpl {} - -fn main() {} - -fn clone_on_ref_ptr() { - let rc = Rc::new(true); - let arc = Arc::new(true); - - let rcweak = Rc::downgrade(&rc); - let arc_weak = Arc::downgrade(&arc); - - rc.clone(); - //~^ clone_on_ref_ptr - - Rc::clone(&rc); - - arc.clone(); - //~^ clone_on_ref_ptr - - Arc::clone(&arc); - - rcweak.clone(); - //~^ clone_on_ref_ptr - - rc::Weak::clone(&rcweak); - - arc_weak.clone(); - //~^ clone_on_ref_ptr - - sync::Weak::clone(&arc_weak); - - let x = Arc::new(SomeImpl); - let _: Arc = x.clone(); - //~^ clone_on_ref_ptr -} - -fn clone_on_copy_generic(t: T) { - t.clone(); - //~^ clone_on_copy - - Some(t).clone(); - //~^ clone_on_copy -} - -mod many_derefs { - struct A; - struct B; - struct C; - struct D; - #[derive(Copy, Clone)] - struct E; - - macro_rules! impl_deref { - ($src:ident, $dst:ident) => { - impl std::ops::Deref for $src { - type Target = $dst; - fn deref(&self) -> &Self::Target { - &$dst - } - } - }; - } - - impl_deref!(A, B); - impl_deref!(B, C); - impl_deref!(C, D); - impl std::ops::Deref for D { - type Target = &'static E; - fn deref(&self) -> &Self::Target { - &&E - } - } - - fn go1() { - let a = A; - let _: E = a.clone(); - //~^ clone_on_copy - - let _: E = *****a; - } -} - -mod issue2076 { - use std::rc::Rc; - - macro_rules! try_opt { - ($expr: expr) => { - match $expr { - Some(value) => value, - None => return None, - } - }; - } - - fn func() -> Option> { - let rc = Rc::new(42); - Some(try_opt!(Some(rc)).clone()) - //~^ clone_on_ref_ptr - } -} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr deleted file mode 100644 index 17518e123d12e..0000000000000 --- a/tests/ui/unnecessary_clone.stderr +++ /dev/null @@ -1,62 +0,0 @@ -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:23:5 - | -LL | rc.clone(); - | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` - | - = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:28:5 - | -LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:33:5 - | -LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:38:5 - | -LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:44:33 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` - -error: using `clone` on type `T` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:49:5 - | -LL | t.clone(); - | ^^^^^^^^^ help: try removing the `clone` call: `t` - | - = note: `-D clippy::clone-on-copy` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` - -error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:52:5 - | -LL | Some(t).clone(); - | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` - -error: using `clone` on type `E` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:87:20 - | -LL | let _: E = a.clone(); - | ^^^^^^^^^ help: try dereferencing it: `*****a` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:108:14 - | -LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` - -error: aborting due to 9 previous errors - diff --git a/tests/ui/mut_reference.fixed b/tests/ui/unnecessary_mut_passed.fixed similarity index 87% rename from tests/ui/mut_reference.fixed rename to tests/ui/unnecessary_mut_passed.fixed index 03d854099e644..63bbadb01dcb2 100644 --- a/tests/ui/mut_reference.fixed +++ b/tests/ui/unnecessary_mut_passed.fixed @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&my_struct).takes_ref(&42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&my_struct).takes_ref((&42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/mut_reference.rs b/tests/ui/unnecessary_mut_passed.rs similarity index 87% rename from tests/ui/mut_reference.rs rename to tests/ui/unnecessary_mut_passed.rs index 80e3f50692770..b719ca1871b29 100644 --- a/tests/ui/mut_reference.rs +++ b/tests/ui/unnecessary_mut_passed.rs @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&mut my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&mut my_struct).takes_ref(&mut 42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&mut my_struct).takes_ref((&mut 42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&mut 42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.stderr b/tests/ui/unnecessary_mut_passed.stderr new file mode 100644 index 0000000000000..ace11027e3e25 --- /dev/null +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -0,0 +1,220 @@ +error: the function `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:57:15 + | +LL | takes_ref(&mut 42); + | ^^^^^^^ + | + = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +help: remove this `mut` + | +LL - takes_ref(&mut 42); +LL + takes_ref(&42); + | + +error: the function `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:59:19 + | +LL | takes_ref_ref(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_ref(&mut &42); +LL + takes_ref_ref(&&42); + | + +error: the function `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:61:22 + | +LL | takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_refmut(&mut &mut 42); +LL + takes_ref_refmut(&&mut 42); + | + +error: the function `takes_raw_const` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:63:21 + | +LL | takes_raw_const(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - takes_raw_const(&mut 42); +LL + takes_raw_const(&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:67:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:70:12 + | +LL | as_ptr(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &42); +LL + as_ptr(&&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:73:12 + | +LL | as_ptr(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &mut 42); +LL + as_ptr(&&mut 42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:76:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:81:25 + | +LL | my_struct.takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref(&mut 42); +LL + my_struct.takes_ref(&42); + | + +error: the method `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:83:29 + | +LL | my_struct.takes_ref_ref(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_ref(&mut &42); +LL + my_struct.takes_ref_ref(&&42); + | + +error: the method `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:85:32 + | +LL | my_struct.takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_refmut(&mut &mut 42); +LL + my_struct.takes_ref_refmut(&&mut 42); + | + +error: the method `takes_raw_const` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:87:31 + | +LL | my_struct.takes_raw_const(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_raw_const(&mut 42); +LL + my_struct.takes_raw_const(&42); + | + +error: the method `takes_nothing` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:175:5 + | +LL | (&mut my_struct).takes_nothing(); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_nothing(); +LL + (&my_struct).takes_nothing(); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:5 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&my_struct).takes_ref(&mut 42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:32 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&mut my_struct).takes_ref(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:5 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&my_struct).takes_ref((&mut 42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:32 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&mut my_struct).takes_ref((&42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:188:25 + | +LL | my_struct.takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref((&mut 42)); +LL + my_struct.takes_ref((&42)); + | + +error: aborting due to 18 previous errors + diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed new file mode 100644 index 0000000000000..b90ae1365bbf2 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + } + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs new file mode 100644 index 0000000000000..606c901c20d35 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + }; + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr new file mode 100644 index 0000000000000..3e98a92ef299c --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr @@ -0,0 +1,11 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs:11:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index fb9d7880a4a7f..e6c451ce7399e 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); @@ -79,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + //~^ zero_repeat_side_effects + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 8b22ff840244e..f8a497976aa43 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); @@ -79,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&[Some(f()); 0]); + //~^ zero_repeat_side_effects + foo(&[Some(Some(S::new())); 0]); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 2dba52e2112ec..771b71c686ae4 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,59 +1,136 @@ -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:18:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:16:5 | LL | let a = [f(); 0]; - | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL - let a = [f(); 0]; +LL + f(); let a: [i32; 0] = []; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:21:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - b = [f(); 0]; +LL + f(); b = [] as [i32; 0]; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:26:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec = vec![];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let c = vec![f(); 0]; +LL + f(); let c: std::vec::Vec = vec![]; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:29:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec` + | ^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - d = vec![f(); 0]; +LL + f(); d = vec![] as std::vec::Vec; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:33:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:31:5 | LL | let e = [println!("side effect"); 0]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let e = [println!("side effect"); 0]; +LL + println!("side effect"); let e: [(); 0] = []; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:37:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let g = [{ f() }; 0]; +LL + { f() }; let g: [i32; 0] = []; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:41:10 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - drop(vec![f(); 0]); +LL + drop({ f(); vec![] as std::vec::Vec }); + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:45:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:43:5 | LL | vec![f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - vec![f(); 0]; +LL + { f(); vec![] as std::vec::Vec }; + | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:47:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:45:5 | LL | [f(); 0]; - | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + | ^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - [f(); 0]; +LL + { f(); [] as [i32; 0] }; + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:99:10 + | +LL | foo(&[Some(f()); 0]); + | ^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(f()); 0]); +LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:101:10 + | +LL | foo(&[Some(Some(S::new())); 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(Some(S::new())); 0]); +LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + | -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/triagebot.toml b/triagebot.toml index 7b19f8658c088..b2fb50918f583 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,7 +60,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB%20users_on_vacation%20=%20["matthiaskrgr", "Manishearth", - "flip1995", ] [assign.owners] From 8c4be66084f62b6de4dece52f517b455b802edca Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 18:10:44 +0200 Subject: [PATCH 1324/1900] Update Cargo.lock --- Cargo.lock | 99 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2de9c84e3b031..92a9013ca2575 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow 0.7.12", + "winnow 0.7.13", ] [[package]] @@ -590,7 +590,7 @@ dependencies = [ "serde_json", "tempfile", "termize", - "toml 0.7.8", + "toml 0.9.7", "ui_test", "walkdir", ] @@ -632,7 +632,7 @@ dependencies = [ "regex-syntax 0.8.5", "semver", "serde", - "toml 0.7.8", + "toml 0.9.7", "unicode-normalization", "unicode-script", "url", @@ -1866,13 +1866,14 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", "serde", + "serde_core", ] [[package]] @@ -5003,10 +5004,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -5031,11 +5033,20 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5084,6 +5095,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +dependencies = [ + "serde_core", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5545,8 +5565,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.19.15", ] @@ -5558,11 +5578,26 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.2", + "toml_datetime 0.7.2", + "toml_parser", + "toml_writer", + "winnow 0.7.13", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -5572,6 +5607,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -5580,8 +5624,8 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -5593,10 +5637,19 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.12", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow 0.7.13", ] [[package]] @@ -5605,6 +5658,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" + [[package]] name = "tracing" version = "0.1.41" @@ -6569,9 +6628,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] From e652436f2adbb58f8754a12f8040b5093bd1e039 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 18:18:33 +0200 Subject: [PATCH 1325/1900] Allow serde_core as a dependency --- src/bootstrap/src/utils/proc_macro_deps.rs | 1 + src/tools/tidy/src/deps.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index db2369097d6ce..83e5580eaa913 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[ "rustc-hash", "self_cell", "serde", + "serde_core", "serde_derive_internals", "sha2", "smallvec", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c76b46ec2bf26..3f40bef821c9d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -458,6 +458,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "scopeguard", "self_cell", "serde", + "serde_core", "serde_derive", "serde_derive_internals", "serde_json", From d3c3e25674fb132a1c905117e829b016a7d0c3cd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 9 Sep 2025 14:23:28 -0700 Subject: [PATCH 1326/1900] rustdoc-search: remove animated throbber Complaints about it being distracting, and causing people to wait until all of the results are loaded instead of using the incremental results as they come in, make me think this is a bad idea. --- src/librustdoc/html/static/css/rustdoc.css | 64 ---------------------- 1 file changed, 64 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 09d289d570c06..a0b3b2de3dbf4 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1987,12 +1987,10 @@ a.tooltip:hover::after { color: inherit; } #search-tabs button:not(.selected) { - --search-tab-button-background: var(--search-tab-button-not-selected-background); background-color: var(--search-tab-button-not-selected-background); border-top-color: var(--search-tab-button-not-selected-border-top-color); } #search-tabs button:hover, #search-tabs button.selected { - --search-tab-button-background: var(--search-tab-button-selected-background); background-color: var(--search-tab-button-selected-background); border-top-color: var(--search-tab-button-selected-border-top-color); } @@ -2008,68 +2006,6 @@ a.tooltip:hover::after { color: transparent; } -.search-form.loading { - --search-tab-button-background: var(--button-background-color); -} - -#search-tabs .count.loading::before, -.search-form.loading::before -{ - width: 16px; - height: 16px; - border-radius: 16px; - background: radial-gradient( - var(--search-tab-button-background) 0 50%, - transparent 50% 100% - ), conic-gradient( - var(--code-highlight-kw-color) 0deg 30deg, - var(--code-highlight-prelude-color) 30deg 60deg, - var(--code-highlight-number-color) 90deg 120deg, - var(--code-highlight-lifetime-color ) 120deg 150deg, - var(--code-highlight-comment-color) 150deg 180deg, - var(--code-highlight-self-color) 180deg 210deg, - var(--code-highlight-attribute-color) 210deg 240deg, - var(--code-highlight-literal-color) 210deg 240deg, - var(--code-highlight-macro-color) 240deg 270deg, - var(--code-highlight-question-mark-color) 270deg 300deg, - var(--code-highlight-prelude-val-color) 300deg 330deg, - var(--code-highlight-doc-comment-color) 330deg 360deg - ); - content: ""; - position: absolute; - left: 2px; - top: 2px; - animation: rotating 1.25s linear infinite; -} -#search-tabs .count.loading::after, -.search-form.loading::after -{ - width: 18px; - height: 18px; - border-radius: 18px; - background: conic-gradient( - var(--search-tab-button-background) 0deg 180deg, - transparent 270deg 360deg - ); - content: ""; - position: absolute; - left: 1px; - top: 1px; - animation: rotating 0.66s linear infinite; -} - -.search-form.loading::before { - left: auto; - right: 9px; - top: 8px; -} - -.search-form.loading::after { - left: auto; - right: 8px; - top: 8px; -} - #search .error code { border-radius: 3px; background-color: var(--search-error-code-background-color); From 2773d24fb701cb17d587289cf75725b5d9bcd328 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 10 Sep 2025 11:23:40 -0700 Subject: [PATCH 1327/1900] rustdoc-search: put throbber at bottom of search results instead --- src/librustdoc/html/static/css/rustdoc.css | 52 ++++++++++++++++++++++ src/librustdoc/html/static/js/search.js | 19 ++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a0b3b2de3dbf4..d86dc66ab01be 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -339,6 +339,7 @@ summary.hideme, .scraped-example-list, .rustdoc-breadcrumbs, .search-switcher, +.search-throbber, /* This selector is for the items listed in the "all items" page. */ ul.all-items { font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; @@ -2006,6 +2007,57 @@ a.tooltip:hover::after { color: transparent; } +.search-throbber { + position: relative; + height: 34px; +} + +.search-throbber::before, +.search-form.loading::before +{ + width: 16px; + height: 16px; + border-radius: 16px; + background: radial-gradient( + var(--button-background-color) 0 50%, + transparent 50% 100% + ), conic-gradient( + var(--code-highlight-kw-color) 0deg 30deg, + var(--code-highlight-prelude-color) 30deg 60deg, + var(--code-highlight-number-color) 90deg 120deg, + var(--code-highlight-lifetime-color ) 120deg 150deg, + var(--code-highlight-comment-color) 150deg 180deg, + var(--code-highlight-self-color) 180deg 210deg, + var(--code-highlight-attribute-color) 210deg 240deg, + var(--code-highlight-literal-color) 210deg 240deg, + var(--code-highlight-macro-color) 240deg 270deg, + var(--code-highlight-question-mark-color) 270deg 300deg, + var(--code-highlight-prelude-val-color) 300deg 330deg, + var(--code-highlight-doc-comment-color) 330deg 360deg + ); + content: ""; + position: absolute; + right: 9px; + top: 8px; + animation: rotating 1.25s linear infinite; +} +.search-throbber::after, +.search-form.loading::after +{ + width: 18px; + height: 18px; + border-radius: 18px; + background: conic-gradient( + var(--button-background-color) 0deg 180deg, + transparent 270deg 360deg + ); + content: ""; + position: absolute; + right: 8px; + top: 8px; + animation: rotating 0.66s linear infinite; +} + #search .error code { border-radius: 3px; background-color: var(--search-error-code-background-color); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 9a6d4c710ff5d..ba8363b1a9147 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -4904,6 +4904,11 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) { let output = document.createElement("ul"); output.className = "search-results " + extraClass; + const throbber = document.createElement("div"); + throbber.className = "search-throbber"; + throbber.innerHTML = "Loading..."; + output.appendChild(throbber); + let count = 0; /** @type {Promise[]} */ @@ -5010,7 +5015,7 @@ ${obj.displayPath}${name}\ } link.appendChild(description); - output.appendChild(link); + output.insertBefore(link, throbber); results.next().then(async nextResult => { if (nextResult.value) { @@ -5019,7 +5024,10 @@ ${obj.displayPath}${name}\ await Promise.all(descList); // need to make sure the element is shown before // running this callback - yieldToBrowser().then(() => finishedCallback(count, output)); + yieldToBrowser().then(() => { + finishedCallback(count, output); + throbber.remove(); + }); } }); }; @@ -5215,9 +5223,14 @@ async function showResults(docSearch, results, goToFirst, filterCrates) { resultsElem.id = "results"; search.innerHTML = ""; - for (const [tab, output] of tabs) { + for (const [tabNb, [tab, output]] of tabs.entries()) { tabsElem.appendChild(tab); + const isCurrentTab = window.searchState.currentTab === tabNb; const placeholder = document.createElement("div"); + placeholder.className = isCurrentTab ? + "search-throbber search-results active" : + "search-throbber search-results"; + placeholder.innerHTML = "Loading..."; output.then(output => { if (placeholder.parentElement) { placeholder.parentElement.replaceChild(output, placeholder); From 490fd3ddd24418188dc6e419ed54ac0fa16f2cf4 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:01:25 +0200 Subject: [PATCH 1328/1900] Update books --- src/doc/reference | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference b/src/doc/reference index e11adf6016a36..8efb980568672 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit e11adf6016a362766eea5a3f9832e193994dd0c8 +Subproject commit 8efb9805686722dba511b7b27281bb6b77d32130 From 2a0600e32037f452b9433f79c2c4af3dda29b362 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 6 Oct 2025 19:20:23 +0200 Subject: [PATCH 1329/1900] Update to new `S-waiting-on-compiler` and `I-compiler-nominated` labels --- src/doc/rustc-dev-guide/src/compiler-team.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 8c0481f6ea8f5..79dfbdc8265fc 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -54,8 +54,8 @@ They are held on [Zulip][zulip-meetings]. It works roughly as follows: those that are sufficiently important for us to actively track progress. P-critical and P-high bugs should ideally always have an assignee. -- **Check S-waiting-on-team and I-nominated issues:** These are issues where feedback from - the team is desired. +- **Check `S-waiting-on-compiler` and `I-compiler-nominated` issues:** These are issues where + feedback from the team is desired. - **Look over the performance triage report:** We check for PRs that made the performance worse and try to decide if it's worth reverting the performance regression or if the regression can be addressed in a future PR. From e0c20a0476ef961121aa6c314a0f1c25870a97e3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 6 Oct 2025 19:25:40 +0200 Subject: [PATCH 1330/1900] Update `S-waiting-on-team` refs to new `S-waiting-on-{team}` labels --- rust-bors.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust-bors.toml b/rust-bors.toml index 56f48512b06a3..1014e31bae49e 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -15,5 +15,12 @@ labels_blocking_approval = [ "S-waiting-on-crater", "S-waiting-on-fcp", "S-waiting-on-MCP", - "S-waiting-on-team" + "S-waiting-on-lang", + "S-waiting-on-compiler", + "S-waiting-on-libs-api", + "S-waiting-on-types", + "S-waiting-on-opsem", + "S-waiting-on-rustdoc", + "S-waiting-on-rustdoc-frontend", + "S-waiting-on-clippy" ] From 5c31880034def5ce8fc22ded984a875fa39965d3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 6 Oct 2025 10:43:37 -0700 Subject: [PATCH 1331/1900] rustdoc-search: use a throbbler with no motion This change is a response to complaints about motion sickness. --- src/librustdoc/html/static/css/rustdoc.css | 48 ++++++---------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d86dc66ab01be..8078f87756dd2 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2012,50 +2012,28 @@ a.tooltip:hover::after { height: 34px; } -.search-throbber::before, -.search-form.loading::before -{ - width: 16px; - height: 16px; - border-radius: 16px; - background: radial-gradient( - var(--button-background-color) 0 50%, - transparent 50% 100% - ), conic-gradient( - var(--code-highlight-kw-color) 0deg 30deg, - var(--code-highlight-prelude-color) 30deg 60deg, - var(--code-highlight-number-color) 90deg 120deg, - var(--code-highlight-lifetime-color ) 120deg 150deg, - var(--code-highlight-comment-color) 150deg 180deg, - var(--code-highlight-self-color) 180deg 210deg, - var(--code-highlight-attribute-color) 210deg 240deg, - var(--code-highlight-literal-color) 210deg 240deg, - var(--code-highlight-macro-color) 240deg 270deg, - var(--code-highlight-question-mark-color) 270deg 300deg, - var(--code-highlight-prelude-val-color) 300deg 330deg, - var(--code-highlight-doc-comment-color) 330deg 360deg - ); - content: ""; - position: absolute; - right: 9px; - top: 8px; - animation: rotating 1.25s linear infinite; -} .search-throbber::after, .search-form.loading::after { width: 18px; height: 18px; border-radius: 18px; - background: conic-gradient( - var(--button-background-color) 0deg 180deg, - transparent 270deg 360deg - ); - content: ""; + /* hourglass */ + content: url('data:image/svg+xml,\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + '); position: absolute; right: 8px; top: 8px; - animation: rotating 0.66s linear infinite; + filter: var(--settings-menu-filter); } #search .error code { From 02126adc1b8d0b61ddaec2adb9c0d2c9dddcd493 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 6 Oct 2025 19:03:48 +0900 Subject: [PATCH 1332/1900] Improve diagnostics: replace 'non-inline module' with 'file module' and update note/help messages --- compiler/rustc_expand/messages.ftl | 5 +++-- compiler/rustc_expand/src/errors.rs | 3 ++- tests/ui/directory_ownership/file-mod-restriction.rs | 5 +++++ .../ui/directory_ownership/file-mod-restriction.stderr | 10 ++++++++++ tests/ui/directory_ownership/macro-expanded-mod.rs | 4 ++-- tests/ui/directory_ownership/macro-expanded-mod.stderr | 3 ++- .../directory_ownership/non-inline-mod-restriction.rs | 5 ----- .../non-inline-mod-restriction.stderr | 8 -------- 8 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 tests/ui/directory_ownership/file-mod-restriction.rs create mode 100644 tests/ui/directory_ownership/file-mod-restriction.stderr delete mode 100644 tests/ui/directory_ownership/non-inline-mod-restriction.rs delete mode 100644 tests/ui/directory_ownership/non-inline-mod-restriction.stderr diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 47c00bff5c950..7c2c9002a32c4 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -118,8 +118,9 @@ expand_module_file_not_found = .note = if there is a `mod {$name}` elsewhere in the crate already, import it with `use crate::...` instead expand_module_in_block = - cannot declare a non-inline module inside a block unless it has a path attribute - .note = maybe `use` the module `{$name}` instead of redeclaring it + cannot declare a file module inside a block unless it has a path attribute + .help = maybe `use` the module `{$name}` instead of redeclaring it + .note = file modules are usually placed outside of blocks, at the top level of the file expand_module_multiple_candidates = file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}" diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index c37c2d88d9cd2..6bae16b3bba15 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -265,6 +265,7 @@ pub(crate) struct ModuleCircular { #[derive(Diagnostic)] #[diag(expand_module_in_block)] +#[note] pub(crate) struct ModuleInBlock { #[primary_span] pub span: Span, @@ -273,7 +274,7 @@ pub(crate) struct ModuleInBlock { } #[derive(Subdiagnostic)] -#[note(expand_note)] +#[help(expand_help)] pub(crate) struct ModuleInBlockName { #[primary_span] pub span: Span, diff --git a/tests/ui/directory_ownership/file-mod-restriction.rs b/tests/ui/directory_ownership/file-mod-restriction.rs new file mode 100644 index 0000000000000..cff876941dbc8 --- /dev/null +++ b/tests/ui/directory_ownership/file-mod-restriction.rs @@ -0,0 +1,5 @@ +// Test that file modules are not allowed inside blocks. + +fn main() { + mod foo; //~ ERROR cannot declare a file module inside a block unless it has a path attribute +} diff --git a/tests/ui/directory_ownership/file-mod-restriction.stderr b/tests/ui/directory_ownership/file-mod-restriction.stderr new file mode 100644 index 0000000000000..4c1ff777f37fd --- /dev/null +++ b/tests/ui/directory_ownership/file-mod-restriction.stderr @@ -0,0 +1,10 @@ +error: cannot declare a file module inside a block unless it has a path attribute + --> $DIR/file-mod-restriction.rs:4:5 + | +LL | mod foo; + | ^^^^^^^^ + | + = note: file modules are usually placed outside of blocks, at the top level of the file + +error: aborting due to 1 previous error + diff --git a/tests/ui/directory_ownership/macro-expanded-mod.rs b/tests/ui/directory_ownership/macro-expanded-mod.rs index fa81769e5a800..1495764a9d8cb 100644 --- a/tests/ui/directory_ownership/macro-expanded-mod.rs +++ b/tests/ui/directory_ownership/macro-expanded-mod.rs @@ -1,8 +1,8 @@ -// Test that macro-expanded non-inline modules behave correctly +// Test that macro-expanded file modules behave correctly macro_rules! mod_decl { ($i:ident) => { - mod $i; //~ ERROR cannot declare a non-inline module inside a block + mod $i; //~ ERROR cannot declare a file module inside a block unless it has a path attribute }; } diff --git a/tests/ui/directory_ownership/macro-expanded-mod.stderr b/tests/ui/directory_ownership/macro-expanded-mod.stderr index 2cacd52b94ef6..cb00a3fe60823 100644 --- a/tests/ui/directory_ownership/macro-expanded-mod.stderr +++ b/tests/ui/directory_ownership/macro-expanded-mod.stderr @@ -1,4 +1,4 @@ -error: cannot declare a non-inline module inside a block unless it has a path attribute +error: cannot declare a file module inside a block unless it has a path attribute --> $DIR/macro-expanded-mod.rs:5:9 | LL | mod $i; @@ -7,6 +7,7 @@ LL | mod $i; LL | mod_decl!(foo); | -------------- in this macro invocation | + = note: file modules are usually placed outside of blocks, at the top level of the file = note: this error originates in the macro `mod_decl` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/directory_ownership/non-inline-mod-restriction.rs b/tests/ui/directory_ownership/non-inline-mod-restriction.rs deleted file mode 100644 index de4f816656cc4..0000000000000 --- a/tests/ui/directory_ownership/non-inline-mod-restriction.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Test that non-inline modules are not allowed inside blocks. - -fn main() { - mod foo; //~ ERROR cannot declare a non-inline module inside a block -} diff --git a/tests/ui/directory_ownership/non-inline-mod-restriction.stderr b/tests/ui/directory_ownership/non-inline-mod-restriction.stderr deleted file mode 100644 index 882c8652520b0..0000000000000 --- a/tests/ui/directory_ownership/non-inline-mod-restriction.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/non-inline-mod-restriction.rs:4:5 - | -LL | mod foo; - | ^^^^^^^^ - -error: aborting due to 1 previous error - From fc959e54646c956dda36d65c3f2e9a9dad01ecc4 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:09:20 -0700 Subject: [PATCH 1333/1900] remove DerefTemp and CopyFromDeref from runtime mir --- compiler/rustc_borrowck/src/lib.rs | 20 +------ .../src/polonius/legacy/loan_invalidations.rs | 7 +-- compiler/rustc_codegen_cranelift/src/base.rs | 6 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 +- .../src/check_consts/qualifs.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 5 +- compiler/rustc_middle/src/mir/mod.rs | 6 +- compiler/rustc_middle/src/mir/syntax.rs | 8 ++- compiler/rustc_mir_transform/src/copy_prop.rs | 9 +-- .../src/dataflow_const_prop.rs | 11 +--- compiler/rustc_mir_transform/src/dest_prop.rs | 5 +- .../src/erase_deref_temps.rs | 41 ++++++++++++++ compiler/rustc_mir_transform/src/gvn.rs | 7 +-- .../rustc_mir_transform/src/jump_threading.rs | 1 - compiler/rustc_mir_transform/src/lib.rs | 2 + compiler/rustc_mir_transform/src/ref_prop.rs | 3 +- .../src/shim/async_destructor_ctor.rs | 8 +-- compiler/rustc_mir_transform/src/ssa.rs | 4 +- compiler/rustc_mir_transform/src/validate.rs | 55 ++++++++++++++++--- 19 files changed, 122 insertions(+), 82 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/erase_deref_temps.rs diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a85dcf64d8d46..f05d34e47f8f8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1539,24 +1539,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { self.consume_operand(location, (operand, span), state) } - &Rvalue::CopyForDeref(place) => { - self.access_place( - location, - (place, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - state, - ); - - // Finally, check if path was already moved. - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (place.as_ref(), span), - state, - ); - } - &Rvalue::Discriminant(place) => { let af = match *rvalue { Rvalue::Discriminant(..) => None, @@ -1618,6 +1600,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { Rvalue::WrapUnsafeBinder(op, _) => { self.consume_operand(location, (op, span), state); } + + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index c2ad6fcb4b799..ebc9390914d61 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -301,11 +301,6 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) | Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand), - &Rvalue::CopyForDeref(place) => { - let op = &Operand::Copy(place); - self.consume_operand(location, op); - } - &Rvalue::Discriminant(place) => { self.access_place( location, @@ -331,6 +326,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { Rvalue::WrapUnsafeBinder(op, _) => { self.consume_operand(location, op); } + + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index ebf2ccf74de20..a529692c0743c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -600,11 +600,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let val = codegen_operand(fx, operand); lval.write_cvalue(fx, val); } - Rvalue::CopyForDeref(place) => { - let cplace = codegen_place(fx, place); - let val = cplace.to_cvalue(fx); - lval.write_cvalue(fx, val) - } Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let place = codegen_place(fx, place); let ref_ = place.place_ref(fx, lval.layout()); @@ -928,6 +923,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let operand = codegen_operand(fx, operand); lval.write_cvalue_transmute(fx, operand); } + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), } } StatementKind::StorageLive(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d629003bff5e8..86b5dea6c53fc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -504,9 +504,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place_to_pointer(bx, place, mk_ref) } - mir::Rvalue::CopyForDeref(place) => { - self.codegen_operand(bx, &mir::Operand::Copy(place)) - } mir::Rvalue::RawPtr(kind, place) => { let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) @@ -742,6 +739,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.cx().layout_of(binder_ty); OperandRef { val: operand.val, layout } } + mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 8a6827bca2bd6..2cb87c2fad71d 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -234,7 +234,7 @@ where Rvalue::Discriminant(place) => in_place::(cx, in_local, place.as_ref()), - Rvalue::CopyForDeref(place) => in_place::(cx, in_local, place.as_ref()), + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), Rvalue::Use(operand) | Rvalue::Repeat(operand, _) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 923e00ad4cf1a..e12eebb74a36a 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -188,10 +188,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&op, &dest)?; } - CopyForDeref(place) => { - let op = self.eval_place_to_op(place, Some(dest.layout))?; - self.copy_op(&op, &dest)?; - } + CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), BinaryOp(bin_op, box (ref left, ref right)) => { let layout = util::binop_left_homogeneous(bin_op).then_some(dest.layout); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 8eb7aa71fcdea..8eae8da4f44f8 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1065,7 +1065,11 @@ pub enum LocalInfo<'tcx> { /// A temporary created during evaluating `if` predicate, possibly for pattern matching for `let`s, /// and subject to Edition 2024 temporary lifetime rules IfThenRescopeTemp { if_then: HirId }, - /// A temporary created during the pass `Derefer` to avoid it's retagging + /// A temporary created during the pass `Derefer` treated as a transparent alias + /// for the place its copied from by analysis passes such as `AddRetag` and `ElaborateDrops`. + /// + /// It may only be written to by a `CopyForDeref` and otherwise only accessed through a deref. + /// In runtime MIR, it is replaced with a normal `Boring` local. DerefTemp, /// A temporary created for borrow checking. FakeBorrow, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index a823c365394f7..0e66824299079 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -130,7 +130,9 @@ pub enum RuntimePhase { /// * [`TerminatorKind::Yield`] /// * [`TerminatorKind::CoroutineDrop`] /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array` + /// * [`Rvalue::CopyForDeref`] /// * [`PlaceElem::OpaqueCast`] + /// * [`LocalInfo::DerefTemp`](super::LocalInfo::DerefTemp) /// /// And the following variants are allowed: /// * [`StatementKind::Retag`] @@ -1460,11 +1462,13 @@ pub enum Rvalue<'tcx> { /// A CopyForDeref is equivalent to a read from a place at the /// codegen level, but is treated specially by drop elaboration. When such a read happens, it /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator) - /// that the only use of the returned value is a deref operation, immediately - /// followed by one or more projections. Drop elaboration treats this rvalue as if the + /// that the returned value is written into a `DerefTemp` local and that its only use is a deref operation, + /// immediately followed by one or more projections. Drop elaboration treats this rvalue as if the /// read never happened and just projects further. This allows simplifying various MIR /// optimizations and codegen backends that previously had to handle deref operations anywhere /// in a place. + /// + /// Disallowed in runtime MIR and is replaced by normal copies. CopyForDeref(Place<'tcx>), /// Wraps a value in an unsafe binder. diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index f0bc286a94022..2e9c3a5bf3eeb 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -74,9 +74,7 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet { let mut fully_moved = DenseBitSet::new_filled(body.local_decls.len()); for (_, rvalue, _) in ssa.assignments(body) { - let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) - | Rvalue::CopyForDeref(place)) = rvalue - else { + let Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) = rvalue else { continue; }; @@ -85,7 +83,7 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet { continue; } - if let Rvalue::Use(Operand::Copy(_)) | Rvalue::CopyForDeref(_) = rvalue { + if let Rvalue::Use(Operand::Copy(_)) = rvalue { fully_moved.remove(rhs); } } @@ -146,8 +144,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { // Do not leave tautological assignments around. if let StatementKind::Assign(box (lhs, ref rhs)) = stmt.kind - && let Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)) | Rvalue::CopyForDeref(rhs) = - *rhs + && let Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)) = *rhs && lhs == rhs { stmt.make_nop(true); diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index e970f7ff81adb..8e3ec33e53bb6 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -309,12 +309,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { self.assign_operand(state, target, operand); } } - Rvalue::CopyForDeref(rhs) => { - state.flood(target.as_ref(), &self.map); - if let Some(target) = self.map.find(target.as_ref()) { - self.assign_operand(state, target, &Operand::Copy(*rhs)); - } - } + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), Rvalue::Aggregate(kind, operands) => { // If we assign `target = Enum::Variant#0(operand)`, // we must make sure that all `target as Variant#i` are `Top`. @@ -488,9 +483,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), - Rvalue::CopyForDeref(place) => { - return self.handle_operand(&Operand::Copy(*place), state); - } + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), Rvalue::Ref(..) | Rvalue::RawPtr(..) => { // We don't track such places. return ValueOrPlace::TOP; diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 1f38433fa5a41..ed52c60f3deb2 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -285,8 +285,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { match &statement.kind { StatementKind::Assign(box (dest, rvalue)) => { match rvalue { - Rvalue::CopyForDeref(place) - | Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { + Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { // These might've been turned into self-assignments by the replacement // (this includes the original statement we wanted to eliminate). if dest == place { @@ -400,7 +399,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { if let StatementKind::Assign(box ( lhs, - Rvalue::CopyForDeref(rhs) | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), )) = &statement.kind && let Some(src) = lhs.as_local() && let Some(dest) = rhs.as_local() diff --git a/compiler/rustc_mir_transform/src/erase_deref_temps.rs b/compiler/rustc_mir_transform/src/erase_deref_temps.rs new file mode 100644 index 0000000000000..ee0c7715c3cb5 --- /dev/null +++ b/compiler/rustc_mir_transform/src/erase_deref_temps.rs @@ -0,0 +1,41 @@ +//! This pass converts all `DerefTemp` locals into normal temporaries +//! and turns their `CopyForDeref` rvalues into normal copies. + +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +struct EraseDerefTempsVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for EraseDerefTempsVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, _: Location) { + if let &mut Rvalue::CopyForDeref(place) = rvalue { + *rvalue = Rvalue::Use(Operand::Copy(place)) + } + } + + fn visit_local_decl(&mut self, _: Local, local_decl: &mut LocalDecl<'tcx>) { + if local_decl.is_deref_temp() { + let info = local_decl.local_info.as_mut().unwrap_crate_local(); + **info = LocalInfo::Boring; + } + } +} + +pub(super) struct EraseDerefTemps; + +impl<'tcx> crate::MirPass<'tcx> for EraseDerefTemps { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + EraseDerefTempsVisitor { tcx }.visit_body_preserves_cfg(body); + } + + fn is_required(&self) -> bool { + true + } +} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3ff8dc6dbb378..8280b16f7c910 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -939,12 +939,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let value = match *rvalue { // Forward values. Rvalue::Use(ref mut operand) => return self.simplify_operand(operand, location), - Rvalue::CopyForDeref(place) => { - let mut operand = Operand::Copy(place); - let val = self.simplify_operand(&mut operand, location); - *rvalue = Rvalue::Use(operand); - return val; - } // Roots. Rvalue::Repeat(ref mut op, amount) => { @@ -986,6 +980,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Unsupported values. Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None, + Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), }; let ty = rvalue.ty(self.local_decls, self.tcx); Some(self.insert(ty, value)) diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 68298767e7fd8..58ca8dbc8cc74 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -455,7 +455,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match rhs { Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state), // Transfer the conditions on the copy rhs. - Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state), Rvalue::Discriminant(rhs) => { let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; state.insert_place_idx(rhs, lhs, &self.map); diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9ff7e0b550030..fbf78a1431ffc 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -139,6 +139,7 @@ declare_passes! { mod dest_prop : DestinationPropagation; pub mod dump_mir : Marker; mod early_otherwise_branch : EarlyOtherwiseBranch; + mod erase_deref_temps : EraseDerefTemps; mod elaborate_box_derefs : ElaborateBoxDerefs; mod elaborate_drops : ElaborateDrops; mod function_item_references : FunctionItemReferences; @@ -619,6 +620,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. // Otherwise it should run fairly late, but before optimizations begin. &add_retag::AddRetag, + &erase_deref_temps::EraseDerefTemps, &elaborate_box_derefs::ElaborateBoxDerefs, &coroutine::StateTransform, &Lint(known_panics_lint::KnownPanicsLint), diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index deb0a146476c3..2ba4df906b742 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -247,8 +247,7 @@ fn compute_replacement<'tcx>( // This is a copy, just use the value we have in store for the previous one. // As we are visiting in `assignment_order`, ie. reverse postorder, `rhs` should // have been visited before. - Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) - | Rvalue::CopyForDeref(place) => { + Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { if let Some(rhs) = place.as_local() && ssa.is_ssa(rhs) { diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 18d09473c191e..a0f1260cd986d 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -240,7 +240,7 @@ fn build_adrop_for_coroutine_shim<'tcx>( source_info, StatementKind::Assign(Box::new(( Place::from(proxy_ref_local), - Rvalue::CopyForDeref(proxy_ref_place), + Rvalue::Use(Operand::Copy(proxy_ref_place)), ))), ), ); @@ -261,7 +261,7 @@ fn build_adrop_for_coroutine_shim<'tcx>( source_info, StatementKind::Assign(Box::new(( Place::from(cor_ptr_local), - Rvalue::CopyForDeref(impl_ptr_place), + Rvalue::Use(Operand::Copy(impl_ptr_place)), ))), ), ); @@ -334,7 +334,7 @@ fn build_adrop_for_adrop_shim<'tcx>( source_info, StatementKind::Assign(Box::new(( Place::from(proxy_ref_local), - Rvalue::CopyForDeref(proxy_ref_place), + Rvalue::Use(Operand::Copy(proxy_ref_place)), ))), )); @@ -350,7 +350,7 @@ fn build_adrop_for_adrop_shim<'tcx>( source_info, StatementKind::Assign(Box::new(( Place::from(cor_ptr_local), - Rvalue::CopyForDeref(impl_ptr_place), + Rvalue::Use(Operand::Copy(impl_ptr_place)), ))), )); } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 73c249a3c8cdb..a56f04cf48422 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -297,9 +297,7 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) { let mut copies = IndexVec::from_fn_n(|l| l, body.local_decls.len()); for (local, rvalue, _) in ssa.assignments(body) { - let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) - | Rvalue::CopyForDeref(place)) = rvalue - else { + let Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) = rvalue else { continue; }; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index cbabb982df8b8..6a4c19dbd87c3 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -9,7 +9,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -906,6 +906,20 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } + if let ClearCrossCrate::Set(box LocalInfo::DerefTemp) = + self.body.local_decls[place.local].local_info + && !place.is_indirect_first_projection() + { + if cntxt != PlaceContext::MutatingUse(MutatingUseContext::Store) + || place.as_local().is_none() + { + self.fail( + location, + format!("`DerefTemp` locals must only be dereferenced or directly assigned to"), + ); + } + } + self.super_place(place, cntxt, location); } @@ -918,7 +932,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; } match rvalue { - Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {} + Rvalue::Use(_) => {} + Rvalue::CopyForDeref(_) => { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail(location, "`CopyForDeref` should have been removed in runtime MIR"); + } + } Rvalue::Aggregate(kind, fields) => match **kind { AggregateKind::Tuple => {} AggregateKind::Array(dest) => { @@ -1416,13 +1435,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ), ); } - if let Rvalue::CopyForDeref(place) = rvalue { - if place.ty(&self.body.local_decls, self.tcx).ty.builtin_deref(true).is_none() { - self.fail( - location, - "`CopyForDeref` should only be used for dereferenceable types", - ) - } + + if let Some(local) = dest.as_local() + && let ClearCrossCrate::Set(box LocalInfo::DerefTemp) = + self.body.local_decls[local].local_info + && !matches!(rvalue, Rvalue::CopyForDeref(_)) + { + self.fail(location, "assignment to a `DerefTemp` must use `CopyForDeref`") } } StatementKind::AscribeUserType(..) => { @@ -1594,4 +1613,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_terminator(terminator, location); } + + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + if let ClearCrossCrate::Set(box LocalInfo::DerefTemp) = local_decl.local_info { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + START_BLOCK.start_location(), + "`DerefTemp` should have been removed in runtime MIR", + ); + } else if local_decl.ty.builtin_deref(true).is_none() { + self.fail( + START_BLOCK.start_location(), + "`DerefTemp` should only be used for dereferenceable types", + ) + } + } + + self.super_local_decl(local, local_decl); + } } From a0e9cb7cb4388c2ed587d0ebc0265e4c8b682df7 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:34:04 -0700 Subject: [PATCH 1334/1900] erase coroutine shim dereftemps --- compiler/rustc_mir_transform/src/coroutine.rs | 8 +++---- .../src/deref_separator.rs | 24 +++++++++++++++---- .../src/elaborate_drops.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 4 ++-- compiler/rustc_mir_transform/src/shim.rs | 10 ++++---- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 814eded910df5..2192fd0c53ab6 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1625,19 +1625,19 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { let mut drop_shim = create_coroutine_drop_shim_async(tcx, &transform, body, drop_clean, can_unwind); // Run derefer to fix Derefs that are not in the first place - deref_finder(tcx, &mut drop_shim); + deref_finder(tcx, &mut drop_shim, false); body.coroutine.as_mut().unwrap().coroutine_drop_async = Some(drop_shim); } else { // If coroutine has no async drops, generating sync drop shim let mut drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); // Run derefer to fix Derefs that are not in the first place - deref_finder(tcx, &mut drop_shim); + deref_finder(tcx, &mut drop_shim, false); body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim); // For coroutine with sync drop, generating async proxy for `future_drop_poll` call let mut proxy_shim = create_coroutine_drop_shim_proxy_async(tcx, body); - deref_finder(tcx, &mut proxy_shim); + deref_finder(tcx, &mut proxy_shim, false); body.coroutine.as_mut().unwrap().coroutine_drop_proxy_async = Some(proxy_shim); } @@ -1645,7 +1645,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { create_coroutine_resume_function(tcx, transform, body, can_return, can_unwind); // Run derefer to fix Derefs that are not in the first place - deref_finder(tcx, body); + deref_finder(tcx, body, false); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index bc914ea656415..1f380302804b9 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -11,6 +11,7 @@ struct DerefChecker<'a, 'tcx> { tcx: TyCtxt<'tcx>, patcher: MirPatch<'tcx>, local_decls: &'a LocalDecls<'tcx>, + add_deref_metadata: bool, } impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { @@ -39,7 +40,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { let temp = self.patcher.new_local_with_info( ty, self.local_decls[p_ref.local].source_info.span, - LocalInfo::DerefTemp, + if self.add_deref_metadata { + LocalInfo::DerefTemp + } else { + LocalInfo::Boring + }, ); // We are adding current p_ref's projections to our @@ -50,7 +55,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { self.patcher.add_assign( loc, Place::from(temp), - Rvalue::CopyForDeref(deref_place), + if self.add_deref_metadata { + Rvalue::CopyForDeref(deref_place) + } else { + Rvalue::Use(Operand::Copy(deref_place)) + }, ); place_local = temp; last_len = p_ref.projection.len(); @@ -67,9 +76,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { } } -pub(super) fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(super) fn deref_finder<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + add_deref_metadata: bool, +) { let patch = MirPatch::new(body); - let mut checker = DerefChecker { tcx, patcher: patch, local_decls: &body.local_decls }; + let mut checker = + DerefChecker { tcx, patcher: patch, local_decls: &body.local_decls, add_deref_metadata }; for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { checker.visit_basic_block_data(bb, data); @@ -80,7 +94,7 @@ pub(super) fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { impl<'tcx> crate::MirPass<'tcx> for Derefer { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - deref_finder(tcx, body); + deref_finder(tcx, body, true); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 58dff4514a04d..c4ad29d663010 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -87,7 +87,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { .elaborate() }; elaborate_patch.apply(body); - deref_finder(tcx, body); + deref_finder(tcx, body, true); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 8593e25d6aa5e..3c997cfd4fb01 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -64,7 +64,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(tcx, body); - deref_finder(tcx, body); + deref_finder(tcx, body, false); } } @@ -100,7 +100,7 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(tcx, body); - deref_finder(tcx, body); + deref_finder(tcx, body, false); } } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index bca8ffb693b90..85e340c0a02ab 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,12 +17,13 @@ use rustc_span::source_map::{Spanned, dummy_spanned}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; +use crate::deref_separator::deref_finder; use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop}; use crate::patch::MirPatch; use crate::{ - abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline, - instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, - run_optimization_passes, simplify, + abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, inline, instsimplify, + mentioned_items, pass_manager as pm, remove_noop_landing_pads, run_optimization_passes, + simplify, }; mod async_destructor_ctor; @@ -222,6 +223,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); + deref_finder(tcx, &mut result, false); + // We don't validate MIR here because the shims may generate code that's // only valid in a `PostAnalysis` param-env. However, since we do initial // validation with the MirBuilt phase, which uses a user-facing param-env. @@ -232,7 +235,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &[ &mentioned_items::MentionedItems, &add_moves_for_packed_drops::AddMovesForPackedDrops, - &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, &instsimplify::InstSimplify::BeforeInline, From 957b415c9aade955be987a14ad1a379c43271892 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 20:12:34 +0200 Subject: [PATCH 1335/1900] name of the tool is lower case --- src/doc/rustc-dev-guide/src/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 3d196ae2308f0..b4c8f9e9a58c0 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -461,7 +461,7 @@ Please see . [S-tracking-]: https://github.com/rust-lang/rust/labels?q=s-tracking [the rustc-dev-guide working group documentation]: https://forge.rust-lang.org/wg-rustc-dev-guide/index.html#where-to-contribute-rustc-dev-guide-changes -### Rfcbot labels +### rfcbot labels [rfcbot] uses its own labels for tracking the process of coordinating asynchronous decisions, such as approving or rejecting a change. From f97b493fa5338d98e161908d21a0e0bd90c643c9 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sun, 5 Oct 2025 10:38:32 +0300 Subject: [PATCH 1336/1900] Remove no-rustfix --- tests/ui/char_lit_as_u8_unfixable.rs | 1 - tests/ui/char_lit_as_u8_unfixable.stderr | 2 +- tests/ui/must_use_unit_unfixable.rs | 2 -- tests/ui/must_use_unit_unfixable.stderr | 8 ++++---- tests/ui/needless_borrow_pat.fixed | 2 -- tests/ui/needless_borrow_pat.rs | 2 -- tests/ui/needless_borrow_pat.stderr | 24 ++++++++++++------------ 7 files changed, 17 insertions(+), 24 deletions(-) diff --git a/tests/ui/char_lit_as_u8_unfixable.rs b/tests/ui/char_lit_as_u8_unfixable.rs index e5c094f158ec7..c8774c7f30916 100644 --- a/tests/ui/char_lit_as_u8_unfixable.rs +++ b/tests/ui/char_lit_as_u8_unfixable.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::char_lit_as_u8)] fn main() { diff --git a/tests/ui/char_lit_as_u8_unfixable.stderr b/tests/ui/char_lit_as_u8_unfixable.stderr index 49e555ae638a2..4c2770cd2a4e0 100644 --- a/tests/ui/char_lit_as_u8_unfixable.stderr +++ b/tests/ui/char_lit_as_u8_unfixable.stderr @@ -1,5 +1,5 @@ error: casting a character literal to `u8` truncates - --> tests/ui/char_lit_as_u8_unfixable.rs:6:13 + --> tests/ui/char_lit_as_u8_unfixable.rs:5:13 | LL | let _ = '❤' as u8; | ^^^^^^^^^ diff --git a/tests/ui/must_use_unit_unfixable.rs b/tests/ui/must_use_unit_unfixable.rs index 0dba7996bac33..8eeaf36dca290 100644 --- a/tests/ui/must_use_unit_unfixable.rs +++ b/tests/ui/must_use_unit_unfixable.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #[cfg_attr(all(), must_use, deprecated)] fn issue_12320() {} //~^ must_use_unit diff --git a/tests/ui/must_use_unit_unfixable.stderr b/tests/ui/must_use_unit_unfixable.stderr index 087682199afb9..8b5e556b1b2ed 100644 --- a/tests/ui/must_use_unit_unfixable.stderr +++ b/tests/ui/must_use_unit_unfixable.stderr @@ -1,11 +1,11 @@ error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:4:1 + --> tests/ui/must_use_unit_unfixable.rs:2:1 | LL | fn issue_12320() {} | ^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:3:19 + --> tests/ui/must_use_unit_unfixable.rs:1:19 | LL | #[cfg_attr(all(), must_use, deprecated)] | ^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[cfg_attr(all(), must_use, deprecated)] = help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]` error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:8:1 + --> tests/ui/must_use_unit_unfixable.rs:6:1 | LL | fn issue_12320_2() {} | ^^^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:7:44 + --> tests/ui/must_use_unit_unfixable.rs:5:44 | LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] | ^^^^^^^^ diff --git a/tests/ui/needless_borrow_pat.fixed b/tests/ui/needless_borrow_pat.fixed index fe966a716df71..507186676c160 100644 --- a/tests/ui/needless_borrow_pat.fixed +++ b/tests/ui/needless_borrow_pat.fixed @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.rs b/tests/ui/needless_borrow_pat.rs index a6b43855cad10..ef0f97301bcf2 100644 --- a/tests/ui/needless_borrow_pat.rs +++ b/tests/ui/needless_borrow_pat.rs @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr index 25c570eb7ff73..34f167cca2235 100644 --- a/tests/ui/needless_borrow_pat.stderr +++ b/tests/ui/needless_borrow_pat.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:59:14 + --> tests/ui/needless_borrow_pat.rs:57:14 | LL | Some(ref x) => x, | ^^^^^ help: try: `x` @@ -8,7 +8,7 @@ LL | Some(ref x) => x, = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:66:14 + --> tests/ui/needless_borrow_pat.rs:64:14 | LL | Some(ref x) => *x, | ^^^^^ @@ -20,7 +20,7 @@ LL + Some(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:73:14 + --> tests/ui/needless_borrow_pat.rs:71:14 | LL | Some(ref x) => { | ^^^^^ @@ -35,19 +35,19 @@ LL ~ f1(x); | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:85:14 + --> tests/ui/needless_borrow_pat.rs:83:14 | LL | Some(ref x) => m1!(x), | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:91:15 + --> tests/ui/needless_borrow_pat.rs:89:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:98:10 + --> tests/ui/needless_borrow_pat.rs:96:10 | LL | let (ref y,) = (&x,); | ^^^^^ @@ -61,13 +61,13 @@ LL ~ let _: &String = y; | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:110:14 + --> tests/ui/needless_borrow_pat.rs:108:14 | LL | Some(ref x) => x.0, | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:121:14 + --> tests/ui/needless_borrow_pat.rs:119:14 | LL | E::A(ref x) | E::B(ref x) => *x, | ^^^^^ ^^^^^ @@ -79,13 +79,13 @@ LL + E::A(x) | E::B(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:128:21 + --> tests/ui/needless_borrow_pat.rs:126:21 | LL | if let Some(ref x) = Some(&String::new()); | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:138:12 + --> tests/ui/needless_borrow_pat.rs:136:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -100,13 +100,13 @@ LL ~ x | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:147:11 + --> tests/ui/needless_borrow_pat.rs:145:11 | LL | fn f(&ref x: &&String) { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:157:11 + --> tests/ui/needless_borrow_pat.rs:155:11 | LL | fn f(&ref x: &&String) { | ^^^^^ From 5407eb8ca2254db44b806ada123e389302eae04d Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 7 Sep 2025 20:51:16 +0000 Subject: [PATCH 1337/1900] GVN: Support unions. --- compiler/rustc_mir_transform/src/gvn.rs | 38 ++++++++++++++----- .../const_prop/invalid_constant.main.GVN.diff | 30 ++++++++++++--- tests/mir-opt/const_prop/transmute.rs | 4 +- ...mute.undef_union_as_integer.GVN.32bit.diff | 9 ++++- ...mute.undef_union_as_integer.GVN.64bit.diff | 9 ++++- ...in.DestinationPropagation.panic-abort.diff | 10 +++-- ...n.DestinationPropagation.panic-unwind.diff | 10 +++-- tests/mir-opt/dest-prop/union.rs | 5 ++- 8 files changed, 85 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 03a40f83732ac..bd8ea7dddad83 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -213,6 +213,8 @@ enum Value<'a, 'tcx> { /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, &'a [VnIndex]), + /// A union aggregate value. + Union(FieldIdx, VnIndex), /// A raw pointer aggregate built from a thin pointer and metadata. RawPtr { /// Thin pointer component. This is field 0 in MIR. @@ -600,6 +602,21 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { return None; } } + Union(active_field, field) => { + let field = self.evaluated[field].as_ref()?; + if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + { + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; + let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?; + self.ecx.copy_op(field, &field_dest).discard_err()?; + self.ecx + .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) + .discard_err()?; + dest.into() + } else { + return None; + } + } RawPtr { pointer, metadata } => { let pointer = self.evaluated[pointer].as_ref()?; let metadata = self.evaluated[metadata].as_ref()?; @@ -802,11 +819,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), - ProjectionElem::Field(f, _) => { - if let Value::Aggregate(_, fields) = self.get(value) { - return Some((projection_ty, fields[f.as_usize()])); - } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(written_variant, fields) = self.get(outer_value) + ProjectionElem::Field(f, _) => match self.get(value) { + Value::Aggregate(_, fields) => return Some((projection_ty, fields[f.as_usize()])), + Value::Union(active, field) if active == f => return Some((projection_ty, field)), + Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) + if let Value::Aggregate(written_variant, fields) = self.get(outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -822,12 +839,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { // accessing the wrong variant is not UB if the enum has repr. // So it's not impossible for a series of MIR opts to generate // a downcast to an inactive variant. - && written_variant == read_variant + && written_variant == read_variant => { return Some((projection_ty, fields[f.as_usize()])); } - ProjectionElem::Field(f, ()) - } + _ => ProjectionElem::Field(f, ()), + }, ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { return Some((projection_ty, inner)); @@ -1167,7 +1184,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { | AggregateKind::Coroutine(..) => FIRST_VARIANT, AggregateKind::Adt(_, variant_index, _, _, None) => variant_index, // Do not track unions. - AggregateKind::Adt(_, _, _, _, Some(_)) => return None, + AggregateKind::Adt(_, _, _, _, Some(active_field)) => { + let field = *fields.first()?; + return Some(self.insert(ty, Value::Union(active_field, field))); + } AggregateKind::RawPtr(..) => { assert_eq!(field_ops.len(), 2); let [mut pointer, metadata] = fields.try_into().unwrap(); diff --git a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff index 5e843da867923..a4900a1ac72ea 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff @@ -28,21 +28,27 @@ bb0: { StorageLive(_1); StorageLive(_2); - _2 = InvalidChar { int: const 1114113_u32 }; - _1 = copy (_2.1: char); +- _2 = InvalidChar { int: const 1114113_u32 }; +- _1 = copy (_2.1: char); ++ _2 = const InvalidChar {{ int: 1114113_u32, chr: {transmute(0x00110001): char} }}; ++ _1 = const {transmute(0x00110001): char}; StorageDead(_2); StorageLive(_3); StorageLive(_4); StorageLive(_5); - _5 = InvalidTag { int: const 4_u32 }; - _4 = copy (_5.1: E); - _3 = [move _4]; +- _5 = InvalidTag { int: const 4_u32 }; +- _4 = copy (_5.1: E); +- _3 = [move _4]; ++ _5 = const InvalidTag {{ int: 4_u32, e: Scalar(0x00000004): E }}; ++ _4 = const Scalar(0x00000004): E; ++ _3 = [const Scalar(0x00000004): E]; StorageDead(_4); StorageDead(_5); nop; nop; StorageLive(_8); - _8 = NoVariants { int: const 0_u32 }; +- _8 = NoVariants { int: const 0_u32 }; ++ _8 = const NoVariants {{ int: 0_u32, empty: ZeroSized: Empty }}; nop; nop; nop; @@ -55,5 +61,17 @@ StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ 00 00 00 00 │ .... ++ } ++ ++ ALLOC1 (size: 4, align: 4) { ++ 04 00 00 00 │ .... ++ } ++ ++ ALLOC2 (size: 4, align: 4) { ++ 01 00 11 00 │ .... } diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 33cbefbf053f5..ece6331ade279 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -43,8 +43,8 @@ pub unsafe fn invalid_bool() -> bool { // EMIT_MIR transmute.undef_union_as_integer.GVN.diff pub unsafe fn undef_union_as_integer() -> u32 { // CHECK-LABEL: fn undef_union_as_integer( - // CHECK: _1 = Union32 { - // CHECK: _0 = move _1 as u32 (Transmute); + // CHECK: _1 = const Union32 + // CHECK: _0 = const {{.*}}: u32; union Union32 { value: u32, unit: (), diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff index 2ac9769a0e773..be0450114b1c8 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff @@ -12,11 +12,16 @@ - _2 = (); - _1 = Union32 { value: move _2 }; + _2 = const (); -+ _1 = Union32 { value: const () }; ++ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }}; StorageDead(_2); - _0 = move _1 as u32 (Transmute); +- _0 = move _1 as u32 (Transmute); ++ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32; StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff index 2ac9769a0e773..be0450114b1c8 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff @@ -12,11 +12,16 @@ - _2 = (); - _1 = Union32 { value: move _2 }; + _2 = const (); -+ _1 = Union32 { value: const () }; ++ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }}; StorageDead(_2); - _0 = move _1 as u32 (Transmute); +- _0 = move _1 as u32 (Transmute); ++ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32; StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff index ef418798faaf7..013f1ea4e497d 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff @@ -6,9 +6,9 @@ let _1: main::Un; let mut _2: u32; scope 1 { - debug un => _1; + debug un => const Un {{ us: 1_u32 }}; scope 3 (inlined std::mem::drop::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,14 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; StorageLive(_2); - _2 = copy (_1.0: u32); StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff index ef418798faaf7..013f1ea4e497d 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff @@ -6,9 +6,9 @@ let _1: main::Un; let mut _2: u32; scope 1 { - debug un => _1; + debug un => const Un {{ us: 1_u32 }}; scope 3 (inlined std::mem::drop::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,14 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; StorageLive(_2); - _2 = copy (_1.0: u32); StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/dest-prop/union.rs index 85eded0998031..977ab9c57ad7e 100644 --- a/tests/mir-opt/dest-prop/union.rs +++ b/tests/mir-opt/dest-prop/union.rs @@ -8,7 +8,10 @@ fn val() -> u32 { // EMIT_MIR union.main.DestinationPropagation.diff fn main() { // CHECK-LABEL: fn main( - // CHECK: {{_.*}} = Un { us: const 1_u32 }; + // CHECK: debug un => const Un + // CHECK: debug _x => const 1_u32; + // CHECK: bb0: { + // CHECK-NEXT: return; union Un { us: u32, } From 96c3978eaa5d116a9ffff6b33c61e25d972a9aa2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 26 Sep 2025 21:06:22 +0000 Subject: [PATCH 1338/1900] Repurpose defunct test. --- tests/mir-opt/const_prop/union.main.GVN.diff | 42 +++++++++++++++++++ .../{dest-prop => const_prop}/union.rs | 12 +++--- ...in.DestinationPropagation.panic-abort.diff | 29 ------------- ...n.DestinationPropagation.panic-unwind.diff | 29 ------------- 4 files changed, 48 insertions(+), 64 deletions(-) create mode 100644 tests/mir-opt/const_prop/union.main.GVN.diff rename tests/mir-opt/{dest-prop => const_prop}/union.rs (52%) delete mode 100644 tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff delete mode 100644 tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff diff --git a/tests/mir-opt/const_prop/union.main.GVN.diff b/tests/mir-opt/const_prop/union.main.GVN.diff new file mode 100644 index 0000000000000..16a0432ab9013 --- /dev/null +++ b/tests/mir-opt/const_prop/union.main.GVN.diff @@ -0,0 +1,42 @@ +- // MIR for `main` before GVN ++ // MIR for `main` after GVN + + fn main() -> () { + let mut _0: (); + let _1: main::Un; + let mut _2: u32; + let _3: (); + let mut _4: u32; + scope 1 { + debug un => _1; + scope 3 (inlined std::mem::drop::) { + } + } + scope 2 (inlined val) { + } + + bb0: { + StorageLive(_1); +- StorageLive(_2); ++ nop; + _2 = const 1_u32; +- _1 = Un { us: move _2 }; +- StorageDead(_2); ++ _1 = const Un {{ us: 1_u32 }}; ++ nop; + StorageLive(_3); + StorageLive(_4); +- _4 = copy (_1.0: u32); ++ _4 = const 1_u32; + StorageDead(_4); + StorageDead(_3); + _0 = const (); + StorageDead(_1); + return; + } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/const_prop/union.rs similarity index 52% rename from tests/mir-opt/dest-prop/union.rs rename to tests/mir-opt/const_prop/union.rs index 977ab9c57ad7e..9f197a1a58338 100644 --- a/tests/mir-opt/dest-prop/union.rs +++ b/tests/mir-opt/const_prop/union.rs @@ -1,17 +1,17 @@ -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that we can propagate into places that are projections into unions -//@ compile-flags: -Zunsound-mir-opts -C debuginfo=full +//@ test-mir-pass: GVN +//@ compile-flags: -Zinline-mir + fn val() -> u32 { 1 } -// EMIT_MIR union.main.DestinationPropagation.diff +// EMIT_MIR union.main.GVN.diff fn main() { // CHECK-LABEL: fn main( - // CHECK: debug un => const Un - // CHECK: debug _x => const 1_u32; + // CHECK: debug un => [[un:_.*]]; // CHECK: bb0: { - // CHECK-NEXT: return; + // CHECK: [[un]] = const Un {{{{ us: 1_u32 }}}}; union Un { us: u32, } diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff deleted file mode 100644 index 013f1ea4e497d..0000000000000 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ /dev/null @@ -1,29 +0,0 @@ -- // MIR for `main` before DestinationPropagation -+ // MIR for `main` after DestinationPropagation - - fn main() -> () { - let mut _0: (); - let _1: main::Un; - let mut _2: u32; - scope 1 { - debug un => const Un {{ us: 1_u32 }}; - scope 3 (inlined std::mem::drop::) { - debug _x => const 1_u32; - } - } - scope 2 (inlined val) { - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageDead(_2); - StorageDead(_1); - return; - } - } - - ALLOC0 (size: 4, align: 4) { - 01 00 00 00 │ .... - } - diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff deleted file mode 100644 index 013f1ea4e497d..0000000000000 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ /dev/null @@ -1,29 +0,0 @@ -- // MIR for `main` before DestinationPropagation -+ // MIR for `main` after DestinationPropagation - - fn main() -> () { - let mut _0: (); - let _1: main::Un; - let mut _2: u32; - scope 1 { - debug un => const Un {{ us: 1_u32 }}; - scope 3 (inlined std::mem::drop::) { - debug _x => const 1_u32; - } - } - scope 2 (inlined val) { - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageDead(_2); - StorageDead(_1); - return; - } - } - - ALLOC0 (size: 4, align: 4) { - 01 00 00 00 │ .... - } - From cb06d91cd0ec0cd0393cee3a943f312ed849b530 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Mon, 6 Oct 2025 19:51:37 +0200 Subject: [PATCH 1339/1900] don't panic on extern with just multiple quotes in the name --- compiler/rustc_span/src/symbol.rs | 6 ++-- ....rs => extern-only-quotes-issue-147365.rs} | 6 +++- .../extern-only-quotes-issue-147365.stderr | 32 +++++++++++++++++++ .../extern-single-quote-issue-147365.stderr | 16 ---------- 4 files changed, 41 insertions(+), 19 deletions(-) rename tests/ui/extern/{extern-single-quote-issue-147365.rs => extern-only-quotes-issue-147365.rs} (63%) create mode 100644 tests/ui/extern/extern-only-quotes-issue-147365.stderr delete mode 100644 tests/ui/extern/extern-single-quote-issue-147365.stderr diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 92dd56f3d5894..36214dd1c3ed2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2511,9 +2511,11 @@ impl Ident { } /// Creates a new ident with the same span and name with leading quote removed, if any. - /// If called on an empty ident, or with name just a single quote, returns an empty ident which is invalid. + /// Calling it on a `'` ident will return an empty ident, which triggers debug assertions. pub fn without_first_quote(self) -> Ident { - Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) + self.as_str() + .strip_prefix('\'') + .map_or(self, |name| Ident::new(Symbol::intern(name), self.span)) } /// "Normalize" ident for use in comparisons using "item hygiene". diff --git a/tests/ui/extern/extern-single-quote-issue-147365.rs b/tests/ui/extern/extern-only-quotes-issue-147365.rs similarity index 63% rename from tests/ui/extern/extern-single-quote-issue-147365.rs rename to tests/ui/extern/extern-only-quotes-issue-147365.rs index b50016bd65f31..3a492b4fb4f86 100644 --- a/tests/ui/extern/extern-single-quote-issue-147365.rs +++ b/tests/ui/extern/extern-only-quotes-issue-147365.rs @@ -2,8 +2,12 @@ // https://github.com/rust-lang/rust/issues/147365 // Ensures we don't trigger debug assert by creating an empty Ident when determining whether -// the single quote is a raw lifetime. +// the quotes are a raw lifetime. extern "'" {} //~ ERROR invalid ABI: found `'` +extern "''" {} //~ ERROR invalid ABI: found `''` + +extern "'''" {} //~ ERROR invalid ABI: found `'''` + fn main() {} diff --git a/tests/ui/extern/extern-only-quotes-issue-147365.stderr b/tests/ui/extern/extern-only-quotes-issue-147365.stderr new file mode 100644 index 0000000000000..fabc0fd4916b7 --- /dev/null +++ b/tests/ui/extern/extern-only-quotes-issue-147365.stderr @@ -0,0 +1,32 @@ +error[E0703]: invalid ABI: found `'` + --> $DIR/extern-only-quotes-issue-147365.rs:7:8 + | +LL | extern "'" {} + | ^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL - extern "'" {} +LL + extern "C" {} + | + +error[E0703]: invalid ABI: found `''` + --> $DIR/extern-only-quotes-issue-147365.rs:9:8 + | +LL | extern "''" {} + | ^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error[E0703]: invalid ABI: found `'''` + --> $DIR/extern-only-quotes-issue-147365.rs:11:8 + | +LL | extern "'''" {} + | ^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/extern/extern-single-quote-issue-147365.stderr b/tests/ui/extern/extern-single-quote-issue-147365.stderr deleted file mode 100644 index d761bc3ebf323..0000000000000 --- a/tests/ui/extern/extern-single-quote-issue-147365.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0703]: invalid ABI: found `'` - --> $DIR/extern-single-quote-issue-147365.rs:7:8 - | -LL | extern "'" {} - | ^^^ invalid ABI - | - = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions -help: there's a similarly named valid ABI `C` - | -LL - extern "'" {} -LL + extern "C" {} - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0703`. From 4787834edaa66a596be6b78459b85e20363adb0c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 6 Oct 2025 22:31:38 +0200 Subject: [PATCH 1340/1900] Fix target list of `link_section` Signed-off-by: Jonathan Brouwer --- .../rustc_attr_parsing/src/attributes/link_attrs.rs | 10 ++++++++-- tests/ui/attributes/attr-on-mac-call.stderr | 2 +- .../issue-43106-gating-of-builtin-attrs.stderr | 12 ++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 09f7bfa24e823..40ecd91e5cfc1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -467,8 +467,14 @@ impl SingleAttributeParser for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = - AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::Static), + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); const TEMPLATE: AttributeTemplate = template!( NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute" diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index a08d305916856..02c0ec8ea1d6c 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -82,7 +82,7 @@ LL | #[link_section = "x"] | ^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_ordinal]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:33:5 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 5e2029c45165a..2e2eea65b817a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1236,7 +1236,7 @@ LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17 @@ -1245,7 +1245,7 @@ LL | mod inner { #![link_section="1800"] } | ^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 @@ -1254,7 +1254,7 @@ LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:697:5 @@ -1263,7 +1263,7 @@ LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 @@ -1272,7 +1272,7 @@ LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[must_use]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 @@ -1572,7 +1572,7 @@ LL | #![link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[must_use]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1 From a11fe5d389b08d62b138aedb31ed04e8c08cff91 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 6 Oct 2025 23:50:47 +0200 Subject: [PATCH 1341/1900] Add diagnostic items for `pub mod consts` of FP types They will be used in Clippy. --- compiler/rustc_span/src/symbol.rs | 4 ++++ library/core/src/num/f128.rs | 1 + library/core/src/num/f16.rs | 1 + library/core/src/num/f32.rs | 1 + library/core/src/num/f64.rs | 1 + 5 files changed, 8 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 92dd56f3d5894..29e708ae4e6d4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -980,10 +980,12 @@ symbols! { external_doc, f, f16, + f16_consts_mod, f16_epsilon, f16_nan, f16c_target_feature, f32, + f32_consts_mod, f32_epsilon, f32_legacy_const_digits, f32_legacy_const_epsilon, @@ -1001,6 +1003,7 @@ symbols! { f32_legacy_const_radix, f32_nan, f64, + f64_consts_mod, f64_epsilon, f64_legacy_const_digits, f64_legacy_const_epsilon, @@ -1018,6 +1021,7 @@ symbols! { f64_legacy_const_radix, f64_nan, f128, + f128_consts_mod, f128_epsilon, f128_nan, fabsf16, diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 4fe4735e304c9..e7101537b298f 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -18,6 +18,7 @@ use crate::{intrinsics, mem}; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] +#[rustc_diagnostic_item = "f128_consts_mod"] pub mod consts { // FIXME: replace with mathematical constants from cmath. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 0bea6bc8801d8..aa8342a22ad58 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -20,6 +20,7 @@ use crate::{intrinsics, mem}; /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] +#[rustc_diagnostic_item = "f16_consts_mod"] pub mod consts { // FIXME: replace with mathematical constants from cmath. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index e380cc698f574..3070e1dedbe43 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -277,6 +277,7 @@ pub const NEG_INFINITY: f32 = f32::NEG_INFINITY; /// Basic mathematical constants. #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "f32_consts_mod"] pub mod consts { // FIXME: replace with mathematical constants from cmath. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index ff7449fd996ce..dc8ccc551b2da 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -277,6 +277,7 @@ pub const NEG_INFINITY: f64 = f64::NEG_INFINITY; /// Basic mathematical constants. #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "f64_consts_mod"] pub mod consts { // FIXME: replace with mathematical constants from cmath. From f644f2c1f3844aa55d423c704164630035fdee81 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 6 Oct 2025 18:39:55 +1100 Subject: [PATCH 1342/1900] Avoid passing `Option<&MarkFrame<'_>>`. Two functions take arguments of this type, but the `Option` is always `Some`, so we can just pass `&MarkFrame<'_>` instead. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 10 +++++----- compiler/rustc_query_system/src/dep_graph/mod.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 5e62dab0722c6..ec9fc18c930cb 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -873,7 +873,7 @@ impl DepGraphData { &self, qcx: Qcx, parent_dep_node_index: SerializedDepNodeIndex, - frame: Option<&MarkFrame<'_>>, + frame: &MarkFrame<'_>, ) -> Option<()> { let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); @@ -911,7 +911,7 @@ impl DepGraphData { ); let node_index = - self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, frame); + self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -976,7 +976,7 @@ impl DepGraphData { let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); for dep_dep_node_index in prev_deps { - self.try_mark_parent_green(qcx, dep_dep_node_index, Some(&frame))?; + self.try_mark_parent_green(qcx, dep_dep_node_index, &frame)?; } // If we got here without hitting a `return` that means that all @@ -1397,14 +1397,14 @@ impl DepNodeColorMap { #[inline(never)] #[cold] -pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: Option<&MarkFrame<'_>>) { +pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFrame<'_>) { let data = graph.data.as_ref().unwrap(); eprintln!("there was a panic while trying to force a dep node"); eprintln!("try_mark_green dep node stack:"); let mut i = 0; - let mut current = frame; + let mut current = Some(frame); while let Some(frame) = current { let node = data.previous.index_to_node(frame.index); eprintln!("#{i} {node:?}"); diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 512034a8b2f52..d648415c9fc67 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -63,7 +63,7 @@ pub trait DepContext: Copy { self, dep_node: DepNode, prev_index: SerializedDepNodeIndex, - frame: Option<&MarkFrame<'_>>, + frame: &MarkFrame<'_>, ) -> bool { let cb = self.dep_kind_info(dep_node.kind); if let Some(f) = cb.force_from_dep_node { From a4162d9a8231702162068a25724d15c287da198e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 6 Oct 2025 20:26:36 +1100 Subject: [PATCH 1343/1900] Change `DepNodeColorMap::insert` to `insert_red`. Because it's only ever used for inserting red. (`None` is only used as a starting value, and `Green` is inserted by `try_mark_green` and friends.) --- .../rustc_query_system/src/dep_graph/graph.rs | 17 +++++------------ .../src/dep_graph/serialized.rs | 4 ++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index ec9fc18c930cb..550dd4a7b2deb 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -148,10 +148,9 @@ impl DepGraph { ); assert_eq!(red_node_index, DepNodeIndex::FOREVER_RED_NODE); if prev_graph_node_count > 0 { - colors.insert( - SerializedDepNodeIndex::from_u32(DepNodeIndex::FOREVER_RED_NODE.as_u32()), - DepNodeColor::Red, - ); + colors.insert_red(SerializedDepNodeIndex::from_u32( + DepNodeIndex::FOREVER_RED_NODE.as_u32(), + )); } DepGraph { @@ -1384,14 +1383,8 @@ impl DepNodeColorMap { } #[inline] - pub(super) fn insert(&self, index: SerializedDepNodeIndex, color: DepNodeColor) { - self.values[index].store( - match color { - DepNodeColor::Red => COMPRESSED_RED, - DepNodeColor::Green(index) => index.as_u32(), - }, - Ordering::Release, - ) + pub(super) fn insert_red(&self, index: SerializedDepNodeIndex) { + self.values[index].store(COMPRESSED_RED, Ordering::Release) } } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 79b99c52d0c01..453002dea4b39 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -59,7 +59,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use tracing::{debug, instrument}; -use super::graph::{CurrentDepGraph, DepNodeColor, DepNodeColorMap}; +use super::graph::{CurrentDepGraph, DepNodeColorMap}; use super::query::DepGraphQuery; use super::{DepKind, DepNode, DepNodeIndex, Deps}; use crate::dep_graph::edges::EdgesVec; @@ -906,7 +906,7 @@ impl GraphEncoder { Err(dep_node_index) => return dep_node_index, } } else { - colors.insert(prev_index, DepNodeColor::Red); + colors.insert_red(prev_index); } self.status.bump_index(&mut *local); From 659b758be75acf9c2a25e3e7b59dbc902bd9446d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 6 Oct 2025 19:58:58 +1100 Subject: [PATCH 1344/1900] Make `DepNodeColor` tri-value. Currently it's binary, either `Green` or `Red`. But it's almost always used within an `Option`. So it's a bit neater, and possibly slightly faster, to make it tri-value with `Unknown` as a first-class variant. --- .../rustc_query_system/src/dep_graph/graph.rs | 65 ++++++++----------- .../src/dep_graph/serialized.rs | 5 +- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 550dd4a7b2deb..9c2b498901d86 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -68,20 +68,11 @@ pub struct MarkFrame<'a> { #[derive(Debug)] pub(super) enum DepNodeColor { + Unknown, Red, Green(DepNodeIndex), } -impl DepNodeColor { - #[inline] - fn is_green(self) -> bool { - match self { - DepNodeColor::Red => false, - DepNodeColor::Green(_) => true, - } - } -} - pub(crate) struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the @@ -624,7 +615,7 @@ impl DepGraphData { ) { if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { let current = self.colors.get(prev_index); - assert!(current.is_none(), "{}", msg()) + assert_matches!(current, DepNodeColor::Unknown, "{}", msg()) } else if let Some(nodes_in_current_session) = &self.current.nodes_in_current_session { outline(|| { let seen = nodes_in_current_session.lock().contains_key(dep_node); @@ -633,12 +624,12 @@ impl DepGraphData { } } - fn node_color(&self, dep_node: &DepNode) -> Option { + fn node_color(&self, dep_node: &DepNode) -> DepNodeColor { if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { self.colors.get(prev_index) } else { // This is a node that did not exist in the previous compilation session. - None + DepNodeColor::Unknown } } @@ -646,7 +637,7 @@ impl DepGraphData { /// current compilation session. Used in various assertions #[inline] pub(crate) fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { - self.colors.get(prev_index).is_some_and(|c| c.is_green()) + matches!(self.colors.get(prev_index), DepNodeColor::Green(_)) } #[inline] @@ -820,12 +811,12 @@ impl DepGraph { self.data.as_ref()?.dep_node_debug.borrow().get(&dep_node).cloned() } - fn node_color(&self, dep_node: &DepNode) -> Option { + fn node_color(&self, dep_node: &DepNode) -> DepNodeColor { if let Some(ref data) = self.data { return data.node_color(dep_node); } - None + DepNodeColor::Unknown } pub fn try_mark_green>( @@ -854,9 +845,9 @@ impl DepGraphData { let prev_index = self.previous.node_to_index_opt(dep_node)?; match self.colors.get(prev_index) { - Some(DepNodeColor::Green(dep_node_index)) => Some((prev_index, dep_node_index)), - Some(DepNodeColor::Red) => None, - None => { + DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)), + DepNodeColor::Red => None, + DepNodeColor::Unknown => { // This DepNode and the corresponding query invocation existed // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its @@ -877,7 +868,7 @@ impl DepGraphData { let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); match self.colors.get(parent_dep_node_index) { - Some(DepNodeColor::Green(_)) => { + DepNodeColor::Green(_) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other // dependencies. @@ -888,7 +879,7 @@ impl DepGraphData { debug!("dependency {:?} was immediately green", get_dep_dep_node()); return Some(()); } - Some(DepNodeColor::Red) => { + DepNodeColor::Red => { // We found a dependency the value of which has changed // compared to the previous compilation session. We cannot // mark the DepNode as green and also don't need to bother @@ -896,7 +887,7 @@ impl DepGraphData { debug!("dependency {:?} was immediately red", get_dep_dep_node()); return None; } - None => {} + DepNodeColor::Unknown => {} } let dep_dep_node = &get_dep_dep_node(); @@ -927,15 +918,15 @@ impl DepGraphData { } match self.colors.get(parent_dep_node_index) { - Some(DepNodeColor::Green(_)) => { + DepNodeColor::Green(_) => { debug!("managed to FORCE dependency {dep_dep_node:?} to green"); return Some(()); } - Some(DepNodeColor::Red) => { + DepNodeColor::Red => { debug!("dependency {dep_dep_node:?} was red after forcing"); return None; } - None => {} + DepNodeColor::Unknown => {} } if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() { @@ -1000,13 +991,13 @@ impl DepGraph { /// Returns true if the given node has been marked as red during the /// current compilation session. Used in various assertions pub fn is_red(&self, dep_node: &DepNode) -> bool { - matches!(self.node_color(dep_node), Some(DepNodeColor::Red)) + matches!(self.node_color(dep_node), DepNodeColor::Red) } /// Returns true if the given node has been marked as green during the /// current compilation session. Used in various assertions pub fn is_green(&self, dep_node: &DepNode) -> bool { - self.node_color(dep_node).is_some_and(|c| c.is_green()) + matches!(self.node_color(dep_node), DepNodeColor::Green(_)) } pub fn assert_dep_node_not_yet_allocated_in_current_session( @@ -1033,11 +1024,11 @@ impl DepGraph { let data = self.data.as_ref().unwrap(); for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { - Some(DepNodeColor::Green(_)) => { + DepNodeColor::Green(_) => { let dep_node = data.previous.index_to_node(prev_index); tcx.try_load_from_on_disk_cache(dep_node); } - None | Some(DepNodeColor::Red) => { + DepNodeColor::Unknown | DepNodeColor::Red => { // We can skip red nodes because a node can only be marked // as red if the query result was recomputed and thus is // already in memory. @@ -1324,14 +1315,14 @@ pub(super) struct DepNodeColorMap { sync: bool, } -const COMPRESSED_NONE: u32 = u32::MAX; +const COMPRESSED_UNKNOWN: u32 = u32::MAX; const COMPRESSED_RED: u32 = u32::MAX - 1; impl DepNodeColorMap { fn new(size: usize) -> DepNodeColorMap { debug_assert!(COMPRESSED_RED > DepNodeIndex::MAX_AS_U32); DepNodeColorMap { - values: (0..size).map(|_| AtomicU32::new(COMPRESSED_NONE)).collect(), + values: (0..size).map(|_| AtomicU32::new(COMPRESSED_UNKNOWN)).collect(), sync: is_dyn_thread_safe(), } } @@ -1354,7 +1345,7 @@ impl DepNodeColorMap { let value = &self.values[prev_index]; if self.sync { match value.compare_exchange( - COMPRESSED_NONE, + COMPRESSED_UNKNOWN, index.as_u32(), Ordering::Relaxed, Ordering::Relaxed, @@ -1364,7 +1355,7 @@ impl DepNodeColorMap { } } else { let v = value.load(Ordering::Relaxed); - if v == COMPRESSED_NONE { + if v == COMPRESSED_UNKNOWN { value.store(index.as_u32(), Ordering::Relaxed); Ok(()) } else { @@ -1374,11 +1365,11 @@ impl DepNodeColorMap { } #[inline] - pub(super) fn get(&self, index: SerializedDepNodeIndex) -> Option { + pub(super) fn get(&self, index: SerializedDepNodeIndex) -> DepNodeColor { match self.values[index].load(Ordering::Acquire) { - COMPRESSED_NONE => None, - COMPRESSED_RED => Some(DepNodeColor::Red), - value => Some(DepNodeColor::Green(DepNodeIndex::from_u32(value))), + COMPRESSED_UNKNOWN => DepNodeColor::Unknown, + COMPRESSED_RED => DepNodeColor::Red, + value => DepNodeColor::Green(DepNodeIndex::from_u32(value)), } } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 453002dea4b39..43cac015c32af 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -914,8 +914,9 @@ impl GraphEncoder { index } - /// Encodes a node that was promoted from the previous graph. It reads the information directly from - /// the previous dep graph and expects all edges to already have a new dep node index assigned. + /// Encodes a node that was promoted from the previous graph. It reads the information directly + /// from the previous dep graph and expects all edges to already have a new dep node index + /// assigned. /// /// This will also ensure the dep node is marked green. #[inline] From bc930cd2d101d1b0b858e9fcf981c916721e1c2d Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 6 Oct 2025 11:56:08 -0400 Subject: [PATCH 1345/1900] collect-license-metadata: Print a diff of the expected output Previously, `x test collect-license-metadata` gave the following message on errors: ``` gathering license information from REUSE (this might take a minute...) finished gathering the license information from REUSE in 78.69s loading existing license information The existing /home/runner/work/ferrocene/ferrocene/license-metadata.json file is out of date. Run ./x run collect-license-metadata to update it. Error: The existing /home/runner/work/ferrocene/ferrocene/license-metadata.json file doesn't match what REUSE reports. Bootstrap failed while executing `test collect-license-metadata` ``` Notable, this doesn't actually say what went wrong. Print a diff in addition so it's more clear what broke: ``` ... "license": { "copyright": [ + "2010 The Rust Project Developers", "2016, 2017, 2018, 2019, 2020, 2021 AXE Consultants. All Rights", + "License. Subject to the terms and conditions of this", "Notice", - "The Ferrocene Developers" + "The Ferrocene Developers", + "[yyyy] [name of copyright owner]" ], ... ``` Currently, this prints the entire text of the JSON file as context. That's not ideal, but it's rare for this to fail, so I think it's ok for now. I considered using `assert_json_diff` instead of `similar`, but its errors are a lot harder to read IMO, even though they are better at omitting unnecessary context: ``` Diff: json atoms at path ".files.children[0].children[10].license.copyright[0]" are not equal: lhs: "2016 The Fuchsia Authors" rhs: "2019 The Crossbeam Project Developers" json atoms at path ".files.children[0].children[10].license.spdx" are not equal: lhs: "BSD-2-Clause AND (Apache-2.0 OR MIT)" rhs: "Apache-2.0 OR MIT" json atom at path ".files.children[0].children[10].children" is missing from lhs json atoms at path ".files.children[0].children[10].name" are not equal: lhs: "library/std/src/sys/sync/mutex/fuchsia.rs" rhs: "library/std/src/sync/mpmc" ... ``` --- Cargo.lock | 1 + src/tools/collect-license-metadata/Cargo.toml | 1 + src/tools/collect-license-metadata/src/main.rs | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2de9c84e3b031..68fd8a8385ecd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,6 +686,7 @@ dependencies = [ "anyhow", "serde", "serde_json", + "similar", "spdx-rs", ] diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index edf9e5c5393ea..f84da24428155 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -9,4 +9,5 @@ license = "MIT OR Apache-2.0" anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" +similar = "2.7.0" spdx-rs = "0.5.1" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index 08a30d0b8994f..4e218ea59fda6 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -5,9 +5,21 @@ mod reuse; use std::path::PathBuf; use anyhow::{Context, Error}; +use similar::{ChangeTag, TextDiff}; use crate::licenses::LicensesInterner; +fn diff_text(expected: &str, actual: &str) { + for change in TextDiff::from_lines(expected, actual).iter_all_changes() { + let sign = match change.tag() { + ChangeTag::Delete => "-", + ChangeTag::Insert => "+", + ChangeTag::Equal => " ", + }; + print!("{}{}", sign, change); + } +} + /// The entry point to the binary. /// /// You should probably let `bootstrap` execute this program instead of running it directly. @@ -41,6 +53,8 @@ fn main() -> Result<(), Error> { if existing_json != output { eprintln!("The existing {} file is out of date.", dest.display()); eprintln!("Run ./x run collect-license-metadata to update it."); + eprintln!("Diff:"); + diff_text(&existing, &serde_json::to_string_pretty(&output).unwrap()); anyhow::bail!("The existing {} file doesn't match what REUSE reports.", dest.display()); } println!("license information matches"); From 5318883d754567c851d40aec8a5c0e875a7b0aae Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Sat, 6 Sep 2025 19:17:10 -0400 Subject: [PATCH 1346/1900] Use expect for lint warnings --- clippy_config/src/conf.rs | 4 ++-- clippy_config/src/types.rs | 2 +- clippy_dev/src/dogfood.rs | 2 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 1 - .../src/arbitrary_source_item_ordering.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 1 - clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/format.rs | 1 - clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/functions/ref_option.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 6 +++--- clippy_lints/src/lifetimes.rs | 4 ++-- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches/collapsible_match.rs | 2 +- clippy_lints/src/methods/clone_on_copy.rs | 1 - clippy_lints/src/methods/filter_map.rs | 2 +- .../src/methods/map_all_any_identity.rs | 2 +- clippy_lints/src/methods/mod.rs | 3 +-- clippy_lints/src/methods/search_is_some.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 1 - .../src/methods/unnecessary_to_owned.rs | 2 +- .../src/methods/wrong_self_convention.rs | 1 - clippy_lints/src/needless_maybe_sized.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 -- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/operators/bit_mask.rs | 1 - clippy_lints/src/swap.rs | 2 +- clippy_lints/src/trait_bounds.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 2 -- clippy_lints/src/utils/author.rs | 3 +-- clippy_utils/src/ast_utils/mod.rs | 4 ++-- clippy_utils/src/check_proc_macro.rs | 19 ++++++++----------- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/lib.rs | 7 ++++--- clippy_utils/src/mir/mod.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 2 -- clippy_utils/src/mir/possible_origin.rs | 1 - clippy_utils/src/source.rs | 2 +- clippy_utils/src/ty/mod.rs | 1 - clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/visitors.rs | 2 +- lintcheck/src/config.rs | 2 +- lintcheck/src/input.rs | 2 +- lintcheck/src/main.rs | 4 ++-- src/driver.rs | 7 ++----- src/main.rs | 2 -- tests/compile-test.rs | 3 +-- tests/symbols-used.rs | 1 - 54 files changed, 57 insertions(+), 83 deletions(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9ad434604dfcf..843aa6a40f098 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -248,7 +248,7 @@ macro_rules! define_Conf { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] enum Field { $($name,)* third_party, } struct ConfVisitor<'a>(&'a SourceFile); @@ -1213,7 +1213,7 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); - #[allow(clippy::zero_sized_map_values)] + #[expect(clippy::zero_sized_map_values)] if let Ok(map) = toml::from_str::>(&file) { for name in map.keys() { names.remove(name.as_str()); diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index f64eefa0c232d..0dd65dfcfd6e0 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -131,7 +131,7 @@ impl DisallowedPathEnum { } /// Creates a map of disallowed items to the reason they were disallowed. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index d0fca952b9322..9eb323eaef5aa 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -4,7 +4,7 @@ use itertools::Itertools; /// # Panics /// /// Panics if unable to run the dogfood test -#[allow(clippy::fn_params_excessive_bools)] +#[expect(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 5fef231f6ca1c..1b6a590b896f4 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -192,7 +192,7 @@ enum DevCommand { /// Which lint's page to load initially (optional) lint: Option, }, - #[allow(clippy::doc_markdown)] + #[expect(clippy::doc_markdown)] /// Manually run clippy on a file or package /// /// ## Examples diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 4121daa85e6d7..a14afd8c5f416 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -443,7 +443,6 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R Ok(()) } -#[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { let lint_name_upper = lint.name.to_uppercase(); diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 36498adff502e..b8bf8b25b3233 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -167,7 +167,7 @@ declare_clippy_lint! { impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); #[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +#[expect(clippy::struct_excessive_bools, reason = "Bools are cached feature flags")] pub struct ArbitrarySourceItemOrdering { assoc_types_order: SourceItemOrderingTraitAssocItemKinds, enable_ordering_for_enum: bool, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 2147f72889093..f087a894d76ae 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -7,7 +7,6 @@ macro_rules! declare_with_version { $e:expr, )*]) => { pub static $name: &[(&str, &str)] = &[$($e),*]; - #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index f8ae770b3a4db..8fe3bc9e379fe 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -968,7 +968,7 @@ fn check_for_code_clusters<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 752f39b4e6dc1..7c083eab88942 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 94e66769eb265..098bf4ba42f93 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -39,7 +39,6 @@ declare_clippy_lint! { "useless use of `format!`" } -#[allow(clippy::module_name_repetitions)] pub struct UselessFormat { format_args: FormatArgsStorage, } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 3359aa603239e..35965f4977cfc 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -237,7 +237,7 @@ impl_lint_pass!(FormatArgs<'_> => [ POINTER_FORMAT, ]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] pub struct FormatArgs<'tcx> { format_args: FormatArgsStorage, msrv: Msrv, diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 8de68bfcb511b..68532de0368f8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -132,7 +132,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } // FIXME: needs to be an EARLY LINT. all attribute lints should be -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 5dc1b7269b761..cc9dc47e15f2b 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -62,7 +62,7 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, kind: FnKind<'a>, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index c634c12e1877b..678a29924e528 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_manual_check<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, @@ -165,7 +165,7 @@ fn check_manual_check<'tcx>( } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_gt( cx: &LateContext<'_>, condition_span: Span, @@ -196,7 +196,7 @@ fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { eq_expr_value(cx, expr, expr) } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d8b186b6787d1..519ec228c8840 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -540,7 +540,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 7d14aa87d820a..b5d6da478d8a3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -881,7 +881,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 544c3c34d029c..528cc64fa7bc5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -240,7 +240,7 @@ fn is_label_for_block(cx: &LateContext<'_>, dest: &Destination) -> bool { .is_ok_and(|hir_id| matches!(cx.tcx.hir_node(hir_id), Node::Block(_))) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..ae3d35b1200c9 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -35,7 +35,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 0a456d1057ad9..52ae5b7d01b3a 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -12,7 +12,6 @@ use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -#[allow(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 2da0f8341b17b..dc742fa058cb6 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -280,7 +280,7 @@ fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &E } /// lint use of `filter().map()` or `find().map()` for `Iterators` -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index ac11baa2d54ca..92b273f557186 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -8,7 +8,7 @@ use rustc_span::{Span, sym}; use super::MAP_ALL_ANY_IDENTITY; -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b0b4e5eedb084..a8f1ec7e91986 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4886,7 +4886,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; @@ -4958,7 +4957,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } impl Methods { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 855babb797a21..1478bc29aef51 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -14,7 +14,7 @@ use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` or `is_none()` -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8daa5db887acf..a1a482deb2c30 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -271,7 +271,6 @@ struct IterUsage { span: Span, } -#[allow(clippy::too_many_lines)] fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 640931a828998..6d927aef8b024 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -66,7 +66,7 @@ pub fn check<'tcx>( /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 74b297c13621e..12a6f345168fe 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -81,7 +81,6 @@ impl fmt::Display for Convention { } } -#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: Symbol, diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index ad6313e391bd9..4bcd26c74f57f 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { } declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Bound<'tcx> { /// The [`DefId`] of the type parameter the bound refers to param: DefId, diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7052e1d0fbe5d..3d2285efbe185 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -364,7 +364,6 @@ impl MutablyUsedVariablesCtxt<'_> { } impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { - #[allow(clippy::if_same_then_else)] fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { if let euv::Place { base: @@ -398,7 +397,6 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::if_same_then_else)] fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index ec8c2299d8cbb..b9cdae0f267ee 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -212,7 +212,7 @@ impl Usage { /// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the /// `DefId` of the function paired with the parameter's index. #[derive(Default)] -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Params { params: Vec, by_id: HirIdMap, diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index e87cfd103c301..d6af0234f010b 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -47,7 +47,6 @@ fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, } } -#[allow(clippy::too_many_lines)] fn check_bit_mask( cx: &LateContext<'_>, bit_op: BinOpKind, diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 76ab3cdae22ef..f5400286f8845 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn generate_swap_warning<'tcx>( block: &'tcx Block<'tcx>, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 9182a55081f40..352b8526b0217 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -238,7 +238,7 @@ impl TraitBounds { } } - #[allow(clippy::mutable_key_type)] + #[expect(clippy::mutable_key_type)] fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 515be5adeed0b..ccb027f77bf5f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -693,7 +693,7 @@ impl Types { } } -#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] +#[expect(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index ba0d4de5f3b3b..b37f2a27f9051 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -489,7 +489,6 @@ enum HasSafetyComment { } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, item.span) { HasSafetyComment::Maybe => (), @@ -551,7 +550,6 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn stmt_has_safety_comment( cx: &LateContext<'_>, span: Span, diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index ece29362a39f9..08210ae2cefb9 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -205,7 +205,6 @@ struct PrintVisitor<'a, 'tcx> { first: Cell, } -#[allow(clippy::unused_self)] impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { @@ -410,7 +409,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(field!(arm.body)); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { bind!(self, condition, body); diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index ad69e6eb184e1..b01e160e22972 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -143,7 +143,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { } } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, eq_attr) { @@ -328,7 +328,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[expect(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 948a7203402d5..ff3e7b94f03bf 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -490,17 +490,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { None => ident_search_pat(last.ident).1, } } else { - // this shouldn't be possible, but sure - #[allow( - clippy::collapsible_else_if, - reason = "we want to keep these cases together, since they are both impossible" - )] - if qself_path.is_some() { - // last `>` in `` - Pat::Str(">") - } else { - Pat::Str("") - } + // this shouldn't be possible + Pat::Str( + if qself_path.is_some() { + ">" // last `>` in `` + } else { + "" + } + ) }; (start, end) }, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9ba796137cc3a..e03ba2f73b437 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -2,7 +2,7 @@ //! //! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to //! executable MIR bodies, so we have to do this instead. -#![allow(clippy::float_cmp)] +#![expect(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 6f1bc28fbab81..95e6ecd40dec6 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -212,7 +212,7 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { match expr.kind { ExprKind::Call(path, [arg1, arg2]) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 24864e8ef96dc..9ae3663057720 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -31,8 +31,10 @@ extern crate rustc_ast; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; -// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. -#[allow(unused_extern_crates)] +#[expect( + unused_extern_crates, + reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate." +)] extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; @@ -2813,7 +2815,6 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtx moved_before_use, same_ctxt, }, - #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir_root_module()), diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 9ba644fdd20ec..a066427d6bc11 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -134,7 +134,7 @@ pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { } /// Returns the `mir::Body` containing the node associated with `hir_id`. -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> { let body_owner_local_def_id = tcx.hir_enclosing_body_owner(hir_id); if tcx.hir_body_owner_kind(body_owner_local_def_id).is_fn_or_closure() { diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 152b4272c26ca..f2bffc8156afc 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -15,7 +15,6 @@ use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. -#[allow(clippy::module_name_repetitions)] struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, @@ -167,7 +166,6 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } /// Result of `PossibleBorrowerVisitor`. -#[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index 3d253fd2bb148..fee22c436b0fb 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -8,7 +8,6 @@ use rustc_middle::mir; /// Collect possible borrowed for every `&mut` local. /// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked -#[allow(clippy::module_name_repetitions)] pub(super) struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, body: &'a mir::Body<'tcx>, diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 638d329031231..8b1ec7e62e4dc 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -143,7 +143,7 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } - #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] + #[expect(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] /// Extends the range to include all preceding whitespace characters. /// /// The range will not be expanded if it would cross a line boundary, the line the range would diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index ebf4f2cd3263c..c48b5b7c171ff 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -954,7 +954,6 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { } /// Asserts that the given arguments match the generic parameters of the given item. -#[allow(dead_code)] fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { let g = tcx.generics_of(did); let parent = g.parent.map(|did| tcx.generics_of(did)); diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d9c7e6eac9f67..d46c7bdcd4c1d 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -329,7 +329,7 @@ fn update_res( None } -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let Some(callee_def_id) = (match expr.kind { ExprKind::Call(callee, _) => { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c9f5401ebe776..96f3c1fbe3e5a 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -591,7 +591,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( // Calls the given function for every unconsumed temporary created by the expression. Note the // function is only guaranteed to be called for types which need to be dropped, but it may be called // for other types. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn for_each_unconsumed_temporary<'tcx, B>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 3b2ebf0c28ac2..5d5214805b968 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 1ed059d2fb114..7dda2b7b25f81 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -180,7 +180,7 @@ impl CrateWithSource { /// copies a local folder #[expect(clippy::too_many_lines)] fn download_and_extract(&self) -> Crate { - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 3a60cfa79f410..b30df79023784 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -66,7 +66,7 @@ struct Crate { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued - #[allow(clippy::too_many_arguments, clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn run_clippy_lints( &self, clippy_driver_path: &Path, @@ -314,7 +314,7 @@ fn main() { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { let clippy_ver = build_clippy(config.perf); let clippy_driver_path = fs::canonicalize(format!( diff --git a/src/driver.rs b/src/driver.rs index 6bddcbfd94ce4..102ca3fa69f7e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -133,8 +133,7 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { - // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` - #[allow(rustc::bad_opt_access)] + #[expect(rustc::bad_opt_access, reason = "necessary in clippy driver to set `mir_opt_level`")] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_config::lookup_conf_file(); let previous = config.register_lints.take(); @@ -182,15 +181,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } -#[allow(clippy::ignored_unit_patterns)] fn display_help() { println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -#[allow(clippy::too_many_lines)] -#[allow(clippy::ignored_unit_patterns)] +#[expect(clippy::too_many_lines)] pub fn main() { // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs // about jemalloc. diff --git a/src/main.rs b/src/main.rs index 3c2eec1f05b90..688161c7bfcb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,12 +10,10 @@ use std::process::{self, Command}; use anstream::println; -#[allow(clippy::ignored_unit_patterns)] fn show_help() { println!("{}", help_message()); } -#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 71cd8a6c03cc9..1ac6889352786 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -291,7 +291,6 @@ fn run_ui_toml(cx: &TestContext) { } // Allow `Default::default` as `OptWithSpan` is not nameable -#[allow(clippy::default_trait_access)] fn run_ui_cargo(cx: &TestContext) { if IS_RUSTC_TEST_SUITE { return; @@ -473,7 +472,7 @@ struct DiagnosticCollector { } impl DiagnosticCollector { - #[allow(clippy::assertions_on_constants)] + #[expect(clippy::assertions_on_constants)] fn spawn() -> (Self, thread::JoinHandle<()>) { assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS); diff --git a/tests/symbols-used.rs b/tests/symbols-used.rs index a1049ba64d549..f78f15103cc4e 100644 --- a/tests/symbols-used.rs +++ b/tests/symbols-used.rs @@ -18,7 +18,6 @@ type Result = std::result::Result; type AnyError = Box; #[test] -#[allow(clippy::case_sensitive_file_extension_comparisons)] fn all_symbols_are_used() -> Result<()> { if option_env!("RUSTC_TEST_SUITE").is_some() { return Ok(()); From 231c39545ee4610d946ad202545869d8f4e2efc4 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 10:43:07 +0800 Subject: [PATCH 1347/1900] Fix wrong span for hightlight for duplicated diff lines --- compiler/rustc_errors/src/emitter.rs | 2 +- ...g-highlight-span-extra-arguments-147070.rs | 30 ++++++++ ...-highlight-span-extra-arguments-147070.svg | 72 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs create mode 100644 tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b1e6b761520..aa36713834815 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2412,7 +2412,7 @@ impl HumanEmitter { // too bad to begin with, so we side-step that issue here. for (i, line) in snippet.lines().enumerate() { let line = normalize_whitespace(line); - let row = row_num - 2 - (newlines - i - 1); + let row = (row_num - 2 - (newlines - i - 1)).max(2); // On the first line, we highlight between the start of the part // span, and the end of that line. // On the last line, we highlight between the start of the line, and diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs new file mode 100644 index 0000000000000..65408fd028604 --- /dev/null +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs @@ -0,0 +1,30 @@ +//@ only-linux +//@ compile-flags: --error-format=human --color=always + +// The hightlight span should be correct. See #147070 +struct Thingie; + +impl Thingie { + pub(crate) fn new( + _a: String, + _b: String, + _c: String, + _d: String, + _e: String, + _f: String, + ) -> Self { + unimplemented!() + } +} + +fn main() { + let foo = Thingie::new( + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + ); +} diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg new file mode 100644 index 0000000000000..67f9436b6a527 --- /dev/null +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg @@ -0,0 +1,72 @@ + + + + + + + error[E0061]: this function takes 6 arguments but 7 arguments were supplied + + --> $DIR/wrong-highlight-span-extra-arguments-147070.rs:21:15 + + | + + LL | let foo = Thingie::new( + + | ^^^^^^^^^^^^ + + ... + + LL | String::from(""), + + | ---------------- unexpected argument #7 of type `String` + + | + + note: associated function defined here + + --> $DIR/wrong-highlight-span-extra-arguments-147070.rs:8:19 + + | + + LL | pub(crate) fn new( + + | ^^^ + + help: remove the extra argument + + | + + LL - String::from(""), + + | + + + + error: aborting due to 1 previous error + + + + For more information about this error, try `rustc --explain E0061`. + + + + + + From ed6a137a3219554a18fbfa3b8ee1bd39e48bbb82 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 6 Oct 2025 20:09:22 +1100 Subject: [PATCH 1348/1900] Tweak `DepNodeColorMap::get`. The most common `get` case is green. This commit changes `get` to use use `if`/`else` instead of match, so that getting green requires one comparison instead of two. --- .../rustc_query_system/src/dep_graph/graph.rs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9c2b498901d86..1ae6af0946689 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -68,9 +68,9 @@ pub struct MarkFrame<'a> { #[derive(Debug)] pub(super) enum DepNodeColor { - Unknown, - Red, Green(DepNodeIndex), + Red, + Unknown, } pub(crate) struct DepGraphData { @@ -1315,8 +1315,9 @@ pub(super) struct DepNodeColorMap { sync: bool, } -const COMPRESSED_UNKNOWN: u32 = u32::MAX; +// All values below `COMPRESSED_RED` are green. const COMPRESSED_RED: u32 = u32::MAX - 1; +const COMPRESSED_UNKNOWN: u32 = u32::MAX; impl DepNodeColorMap { fn new(size: usize) -> DepNodeColorMap { @@ -1366,10 +1367,16 @@ impl DepNodeColorMap { #[inline] pub(super) fn get(&self, index: SerializedDepNodeIndex) -> DepNodeColor { - match self.values[index].load(Ordering::Acquire) { - COMPRESSED_UNKNOWN => DepNodeColor::Unknown, - COMPRESSED_RED => DepNodeColor::Red, - value => DepNodeColor::Green(DepNodeIndex::from_u32(value)), + let value = self.values[index].load(Ordering::Acquire); + // Green is by far the most common case. Check for that first so we can succeed with a + // single comparison. + if value < COMPRESSED_RED { + DepNodeColor::Green(DepNodeIndex::from_u32(value)) + } else if value == COMPRESSED_RED { + DepNodeColor::Red + } else { + debug_assert_eq!(value, COMPRESSED_UNKNOWN); + DepNodeColor::Unknown } } From 19423b7f7fea2f0fdd0f4c5f02ead37754ebe78a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 6 Oct 2025 20:22:25 +1100 Subject: [PATCH 1349/1900] Simplify `DepNodeColorMap::try_mark_green`. It uses a different implementation depending on whether the compiler front-end is running single-threaded or multi-threaded. The two implementations are equivalent and I think the multi-threaded one expresses the intent more clearly, and I imagine the perf is similar. So this commit removes the single-threaded code. --- .../rustc_query_system/src/dep_graph/graph.rs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 1ae6af0946689..58bacb1d5d406 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -11,7 +11,7 @@ use rustc_data_structures::outline; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU64, Lock, is_dyn_thread_safe}; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_data_structures::unord::UnordMap; use rustc_errors::DiagInner; use rustc_index::IndexVec; @@ -1308,11 +1308,11 @@ impl Default for TaskDeps { } } } + // A data structure that stores Option values as a contiguous // array, using one u32 per entry. pub(super) struct DepNodeColorMap { values: IndexVec, - sync: bool, } // All values below `COMPRESSED_RED` are green. @@ -1322,10 +1322,7 @@ const COMPRESSED_UNKNOWN: u32 = u32::MAX; impl DepNodeColorMap { fn new(size: usize) -> DepNodeColorMap { debug_assert!(COMPRESSED_RED > DepNodeIndex::MAX_AS_U32); - DepNodeColorMap { - values: (0..size).map(|_| AtomicU32::new(COMPRESSED_UNKNOWN)).collect(), - sync: is_dyn_thread_safe(), - } + DepNodeColorMap { values: (0..size).map(|_| AtomicU32::new(COMPRESSED_UNKNOWN)).collect() } } #[inline] @@ -1344,24 +1341,14 @@ impl DepNodeColorMap { index: DepNodeIndex, ) -> Result<(), DepNodeIndex> { let value = &self.values[prev_index]; - if self.sync { - match value.compare_exchange( - COMPRESSED_UNKNOWN, - index.as_u32(), - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => Ok(()), - Err(v) => Err(DepNodeIndex::from_u32(v)), - } - } else { - let v = value.load(Ordering::Relaxed); - if v == COMPRESSED_UNKNOWN { - value.store(index.as_u32(), Ordering::Relaxed); - Ok(()) - } else { - Err(DepNodeIndex::from_u32(v)) - } + match value.compare_exchange( + COMPRESSED_UNKNOWN, + index.as_u32(), + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => Ok(()), + Err(v) => Err(DepNodeIndex::from_u32(v)), } } From 673fa837197a9e878ee78fbe3a1dfb325c27a8df Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 7 Oct 2025 14:18:19 +0800 Subject: [PATCH 1350/1900] Remove outdated recurring work item --- src/doc/rustc-dev-guide/src/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 87e26d3796881..685b97329bb0c 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -98,7 +98,7 @@ Some work is too large to be done by a single person. In this case, it's common issues" to co-ordinate the work between contributors. Here are some example tracking issues where it's easy to pick up work without a large time commitment: -- [Move UI tests to subdirectories](https://github.com/rust-lang/rust/issues/73494) +- *Add recurring work items here.* If you find more recurring work, please feel free to add it here! From 525ed4cc5cfc9a8230deccf92edd4a41fbebc8ee Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 7 Oct 2025 17:26:01 +1100 Subject: [PATCH 1351/1900] Read the whole test file before parsing directives Few tests are larger than a handful of kilobytes, and nowadays we scan the whole file for directives anyway, so there's little reason not to just read the whole thing up-front. This avoids having to deal with I/O within `iter_directives`, which should make it easier to overhaul directive processing. --- src/tools/compiletest/src/directives.rs | 37 +++++++------------ src/tools/compiletest/src/directives/tests.rs | 36 ++++++++---------- src/tools/compiletest/src/lib.rs | 5 ++- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index dab6850e0d62a..34ecbc2d59f10 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1,9 +1,6 @@ use std::collections::HashSet; -use std::env; -use std::fs::File; -use std::io::BufReader; -use std::io::prelude::*; use std::process::Command; +use std::{env, fs}; use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; @@ -54,18 +51,19 @@ pub struct EarlyProps { impl EarlyProps { pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { - let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops"); - Self::from_reader(config, testfile, file) + let file_contents = + fs::read_to_string(testfile).expect("read test file to parse earlyprops"); + Self::from_file_contents(config, testfile, &file_contents) } - pub fn from_reader(config: &Config, testfile: &Utf8Path, rdr: R) -> Self { + pub fn from_file_contents(config: &Config, testfile: &Utf8Path, file_contents: &str) -> Self { let mut props = EarlyProps::default(); let mut poisoned = false; iter_directives( config.mode, &mut poisoned, testfile, - rdr, + file_contents, // (dummy comment to force args into vertical layout) &mut |ref ln: DirectiveLine<'_>| { parse_and_update_aux(config, ln, testfile, &mut props.aux); @@ -362,7 +360,7 @@ impl TestProps { fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) { let mut has_edition = false; if !testfile.is_dir() { - let file = File::open(testfile.as_std_path()).unwrap(); + let file_contents = fs::read_to_string(testfile).unwrap(); let mut poisoned = false; @@ -370,7 +368,7 @@ impl TestProps { config.mode, &mut poisoned, testfile, - file, + &file_contents, &mut |ref ln: DirectiveLine<'_>| { if !ln.applies_to_test_revision(test_revision) { return; @@ -859,7 +857,7 @@ fn iter_directives( mode: TestMode, poisoned: &mut bool, testfile: &Utf8Path, - rdr: impl Read, + file_contents: &str, it: &mut dyn FnMut(DirectiveLine<'_>), ) { if testfile.is_dir() { @@ -886,16 +884,7 @@ fn iter_directives( } } - let mut rdr = BufReader::with_capacity(1024, rdr); - let mut ln = String::new(); - let mut line_number = 0; - - loop { - line_number += 1; - ln.clear(); - if rdr.read_line(&mut ln).unwrap() == 0 { - break; - } + for (line_number, ln) in (1..).zip(file_contents.lines()) { let ln = ln.trim(); let Some(directive_line) = line_directive(line_number, ln) else { @@ -1359,13 +1348,13 @@ where Some((min, max)) } -pub(crate) fn make_test_description( +pub(crate) fn make_test_description( config: &Config, cache: &DirectivesCache, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, test_revision: Option<&str>, poisoned: &mut bool, ) -> CollectedTestDesc { @@ -1380,7 +1369,7 @@ pub(crate) fn make_test_description( config.mode, &mut local_poisoned, path, - src, + file_contents, &mut |ref ln @ DirectiveLine { line_number, .. }| { if !ln.applies_to_test_revision(test_revision) { return; diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 95dd46532ba88..77080c7469371 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1,5 +1,3 @@ -use std::io::Read; - use camino::Utf8Path; use semver::Version; @@ -10,12 +8,12 @@ use crate::directives::{ }; use crate::executor::{CollectedTestDesc, ShouldPanic}; -fn make_test_description( +fn make_test_description( config: &Config, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, revision: Option<&str>, ) -> CollectedTestDesc { let cache = DirectivesCache::load(config); @@ -26,7 +24,7 @@ fn make_test_description( name, path, filterable_path, - src, + file_contents, revision, &mut poisoned, ); @@ -226,14 +224,13 @@ fn cfg() -> ConfigBuilder { } fn parse_rs(config: &Config, contents: &str) -> EarlyProps { - let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes) + EarlyProps::from_file_contents(config, Utf8Path::new("a.rs"), contents) } fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None); + let d = make_test_description(&config, tn, p, p, contents, None); d.ignore } @@ -243,9 +240,9 @@ fn should_fail() { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None); + let d = make_test_description(&config, tn.clone(), p, p, "", None); assert_eq!(d.should_panic, ShouldPanic::No); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None); + let d = make_test_description(&config, tn, p, p, "//@ should-fail", None); assert_eq!(d.should_panic, ShouldPanic::Yes); } @@ -778,9 +775,8 @@ fn threads_support() { } } -fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) { - let rdr = std::io::Cursor::new(&buf); - iter_directives(TestMode::Ui, poisoned, path, rdr, &mut |_| {}); +fn run_path(poisoned: &mut bool, path: &Utf8Path, file_contents: &str) { + iter_directives(TestMode::Ui, poisoned, path, file_contents, &mut |_| {}); } #[test] @@ -789,7 +785,7 @@ fn test_unknown_directive_check() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/unknown_directive.rs"), + include_str!("./test-auxillary/unknown_directive.rs"), ); assert!(poisoned); } @@ -800,7 +796,7 @@ fn test_known_directive_check_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/known_directive.rs"), + include_str!("./test-auxillary/known_directive.rs"), ); assert!(!poisoned); } @@ -811,7 +807,7 @@ fn test_error_annotation_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/error_annotation.rs"), + include_str!("./test-auxillary/error_annotation.rs"), ); assert!(!poisoned); } @@ -822,7 +818,7 @@ fn test_non_rs_unknown_directive_not_checked() { run_path( &mut poisoned, Utf8Path::new("a.Makefile"), - include_bytes!("./test-auxillary/not_rs.Makefile"), + include_str!("./test-auxillary/not_rs.Makefile"), ); assert!(!poisoned); } @@ -830,21 +826,21 @@ fn test_non_rs_unknown_directive_not_checked() { #[test] fn test_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm"); assert!(poisoned); } #[test] fn test_trailing_directive_with_comment() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm with comment"); assert!(poisoned); } #[test] fn test_not_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ revisions: incremental"); assert!(!poisoned); } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2d759279f34bc..ac1a8226112a9 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -892,7 +892,8 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te // `CollectedTest` that can be handed over to the test executor. collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. - let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); + let file_contents = + fs::read_to_string(&test_path).expect("read test file to parse ignores"); let (test_name, filterable_path) = make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. @@ -904,7 +905,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te test_name, &test_path, &filterable_path, - src_file, + &file_contents, revision, &mut collector.poisoned, ); From 1589c6c12d0f984343c6090c817e1dfbb9cf6d10 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 7 Oct 2025 09:40:27 +0200 Subject: [PATCH 1352/1900] Add regression test for `link_section` targets --- .../issue-43106-gating-of-builtin-attrs.rs | 26 +++ ...issue-43106-gating-of-builtin-attrs.stderr | 159 +++++++++--------- 2 files changed, 110 insertions(+), 75 deletions(-) diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index aa5aab41e7258..a0d3ed97142cb 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -705,6 +705,32 @@ mod link_section { //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute + + #[link_section = "1800"] + //~^ WARN attribute cannot be used on + //~| WARN previously accepted + //~| HELP can be applied to + //~| HELP remove the attribute + trait Tr { + #[link_section = "1800"] + fn inside_tr_no_default(&self); + + #[link_section = "1800"] + fn inside_tr_default(&self) { } + } + + impl S { + #[link_section = "1800"] + fn inside_abc_123(&self) { } + } + + impl Tr for S { + #[link_section = "1800"] + fn inside_tr_no_default(&self) { } + } + + #[link_section = "1800"] + fn should_always_link() { } } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 2e2eea65b817a..30b6d1c9180f1 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -203,7 +203,7 @@ LL | #![reexport_test_harness_main = "2900"] | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:1 | LL | #[link(name = "x")] | ^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -230,7 +230,7 @@ LL | #![windows_subsystem = "windows"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:865:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL | #![crate_type = "0800"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:863:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:889:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ @@ -252,7 +252,7 @@ LL | #![feature(x0600)] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:888:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:914:1 | LL | #[no_main] | ^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | #![no_main] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:912:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ @@ -340,7 +340,7 @@ LL | #![reexport_test_harness_main = "2900"] impl S { } | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:719:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:17 | LL | mod inner { #![link(name = "x")] } | ------------^^^^^^^^^^^^^^^^^^^^-- not an `extern` block @@ -348,7 +348,7 @@ LL | mod inner { #![link(name = "x")] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 | LL | #[link(name = "x")] fn f() { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -356,7 +356,7 @@ LL | #[link(name = "x")] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5 | LL | #[link(name = "x")] struct S; | ^^^^^^^^^^^^^^^^^^^ --------- not an `extern` block @@ -364,7 +364,7 @@ LL | #[link(name = "x")] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5 | LL | #[link(name = "x")] type T = S; | ^^^^^^^^^^^^^^^^^^^ ----------- not an `extern` block @@ -372,7 +372,7 @@ LL | #[link(name = "x")] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 | LL | #[link(name = "x")] impl S { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -380,7 +380,7 @@ LL | #[link(name = "x")] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 | LL | #[link(name = "x")] extern "Rust" {} | ^^^^^^^^^^^^^^^^^^^ @@ -388,13 +388,13 @@ LL | #[link(name = "x")] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -405,7 +405,7 @@ LL | #![windows_subsystem = "windows"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:800:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -416,7 +416,7 @@ LL | #![windows_subsystem = "windows"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | #![windows_subsystem = "windows"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -438,13 +438,13 @@ LL | #![windows_subsystem = "windows"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:869:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:872:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL | #![crate_type = "0800"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:850:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:876:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL | #![crate_type = "0800"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:854:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:880:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -477,7 +477,7 @@ LL | #![crate_type = "0800"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:858:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:884:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -488,13 +488,13 @@ LL | #![crate_type = "0800"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:893:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:896:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ @@ -505,7 +505,7 @@ LL | #![feature(x0600)] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:900:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #![feature(x0600)] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:878:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:904:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ @@ -527,7 +527,7 @@ LL | #![feature(x0600)] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:882:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:908:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ @@ -538,13 +538,13 @@ LL | #![feature(x0600)] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:895:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:921:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ @@ -555,7 +555,7 @@ LL | #![no_main] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:899:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ @@ -566,7 +566,7 @@ LL | #![no_main] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:903:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ @@ -577,7 +577,7 @@ LL | #![no_main] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:907:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ @@ -588,13 +588,13 @@ LL | #![no_main] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:919:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:945:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ @@ -605,7 +605,7 @@ LL | #![no_builtins] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:923:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ @@ -616,7 +616,7 @@ LL | #![no_builtins] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:927:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ @@ -627,7 +627,7 @@ LL | #![no_builtins] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:931:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ @@ -1274,8 +1274,17 @@ LL | #[link_section = "1800"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics +warning: `#[link_section]` attribute cannot be used on traits + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:5 + | +LL | #[link_section = "1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[link_section]` can be applied to functions and statics + warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:790:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -1284,7 +1293,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:795:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ @@ -1293,7 +1302,7 @@ LL | mod inner { #![must_use] } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ @@ -1302,7 +1311,7 @@ LL | #[must_use] type T = S; = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ @@ -1311,13 +1320,13 @@ LL | #[must_use] impl S { } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:841:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:1 | LL | / mod crate_name { LL | | @@ -1327,67 +1336,67 @@ LL | | } | |_^ warning: the `#![crate_name]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:845:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:848:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:848:28 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:28 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:28 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:28 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:1 | LL | / mod recursion_limit { LL | | @@ -1397,67 +1406,67 @@ LL | | } | |_^ warning: the `#![recursion_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:966:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:969:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:969:31 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:973:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:973:31 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:977:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:977:31 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:981:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:981:31 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:986:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:988:1 | LL | / mod type_length_limit { LL | | @@ -1467,55 +1476,55 @@ LL | | } | |_^ warning: the `#![type_length_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:990:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:993:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:993:33 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:997:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:997:33 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1001:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1001:33 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1005:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1005:33 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^ @@ -1583,5 +1592,5 @@ LL | #![must_use] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to functions, data types, unions, and traits -warning: 173 warnings emitted +warning: 174 warnings emitted From 0fe466cc25975a5d531a546ec7cce2f38f3e0d6a Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Tue, 7 Oct 2025 14:47:39 +0700 Subject: [PATCH 1353/1900] Fix doc comment --- compiler/rustc_middle/src/thir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f1d19800a7891..eed27345b747b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -839,7 +839,7 @@ pub enum PatKind<'tcx> { /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. /// * raw pointers derived from integers, other raw pointers will have already resulted in an - // error. + /// error. /// * `String`, if `string_deref_patterns` is enabled. Constant { value: ty::Value<'tcx>, From 43f7eaa0ba4cbd2537260fe223ce1e7883c405fc Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 6 Oct 2025 11:38:09 +0100 Subject: [PATCH 1354/1900] Fix; correct placement of type inference error for method calls --- compiler/rustc_hir_typeck/src/method/probe.rs | 21 ++++++++++++++----- ...eref-ambiguity-becomes-nonambiguous.stderr | 2 +- ...tion-with-leaking-placeholders.next.stderr | 2 +- .../hidden-type-is-opaque-2.default.stderr | 4 ++-- .../hidden-type-is-opaque-2.next.stderr | 4 ++-- ...ncompat-call-after-qualified-path-0.stderr | 2 +- ...ncompat-call-after-qualified-path-1.stderr | 2 +- tests/ui/issues/issue-20261.stderr | 4 ++-- tests/ui/issues/issue-2151.stderr | 2 +- .../branches3.stderr | 8 +++---- .../call_method_unknown_pointee.stderr | 18 +++++++--------- .../call_method_unknown_referent.stderr | 8 +++---- .../ui/proc-macro/quote/not-repeatable.stderr | 4 ++-- ...opy-inference-side-effects-are-lazy.stderr | 2 +- .../issue-42234-unknown-receiver-type.stderr | 4 +--- .../closures_in_branches.stderr | 4 ++-- .../regression-issue-81317.stderr | 2 +- tests/ui/typeck/issue-13853.stderr | 4 ++-- 18 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 12f80a197b1b8..45b87cee957c9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -7,9 +7,8 @@ use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def::DefKind; +use rustc_hir::{self as hir, ExprKind, HirId, Node}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; @@ -486,13 +485,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { ty::Infer(ty::TyVar(_)) => { + // We want to get the variable name that the method + // is being called on. If it is a method call. + let err_span = match (mode, self.tcx.hir_node(scope_expr_id)) { + ( + Mode::MethodCall, + Node::Expr(hir::Expr { + kind: ExprKind::MethodCall(_, recv, ..), + .. + }), + ) => recv.span, + _ => span, + }; + let raw_ptr_call = bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types(); - // FIXME: Ideally we'd use the span of the self-expr here, - // not of the method path. + let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, - span, + err_span, ty.into(), TypeAnnotationNeeded::E0282, !raw_ptr_call, diff --git a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr index 19c3c64181985..bad799b2550b9 100644 --- a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr +++ b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr @@ -5,7 +5,7 @@ LL | let var_fn = Value::wrap(); | ^^^^^^ ... LL | let _ = var_fn.clone(); - | ----- type must be known at this point + | ------ type must be known at this point | help: consider giving `var_fn` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr index 4bb9047b3035d..3d667f12371ab 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr @@ -5,7 +5,7 @@ LL | needs_foo(|x| { | ^ ... LL | x.to_string(); - | --------- type must be known at this point + | - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr index cb383b2db3893..dca0a7b0a1a9f 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr index cb383b2db3893..dca0a7b0a1a9f 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr index ba1c81c4518a7..10056bdf3d4f4 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-0.rs:21:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr index 93bba3625b540..632a9b99f84ef 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-1.rs:25:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/issues/issue-20261.stderr b/tests/ui/issues/issue-20261.stderr index 6738708ca225d..c5348abb3c501 100644 --- a/tests/ui/issues/issue-20261.stderr +++ b/tests/ui/issues/issue-20261.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/issue-20261.rs:4:11 + --> $DIR/issue-20261.rs:4:9 | LL | i.clone(); - | ^^^^^ cannot infer type + | ^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2151.stderr b/tests/ui/issues/issue-2151.stderr index 59fef42eb5e8b..b130f162414d0 100644 --- a/tests/ui/issues/issue-2151.stderr +++ b/tests/ui/issues/issue-2151.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x = panic!(); | ^ LL | x.clone(); - | ----- type must be known at this point + | - type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index 539673bc343ce..117d189867bd7 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:9:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:18:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -24,7 +24,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:26:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -35,7 +35,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:33:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index 9d0f38cf6b5c1..e20c6f8e8a17c 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -1,11 +1,10 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:10:41 + --> $DIR/call_method_unknown_pointee.rs:10:23 | LL | let _a: i32 = (ptr as *const _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*const _` --> $DIR/call_method_unknown_pointee.rs:12:13 @@ -22,13 +21,12 @@ LL | let b: *const _ = ptr as *const _; | ++++++++++ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:21:39 + --> $DIR/call_method_unknown_pointee.rs:21:23 | LL | let _a: i32 = (ptr as *mut _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*mut _` --> $DIR/call_method_unknown_pointee.rs:23:13 diff --git a/tests/ui/methods/call_method_unknown_referent.stderr b/tests/ui/methods/call_method_unknown_referent.stderr index 5d6974a00c695..35c7d9caf3efa 100644 --- a/tests/ui/methods/call_method_unknown_referent.stderr +++ b/tests/ui/methods/call_method_unknown_referent.stderr @@ -1,14 +1,14 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:20:31 + --> $DIR/call_method_unknown_referent.rs:20:19 | LL | let _a: i32 = (ptr as &_).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^ cannot infer type error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:26:37 + --> $DIR/call_method_unknown_referent.rs:26:14 | LL | let _b = (rc as std::rc::Rc<_>).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error[E0599]: no method named `read` found for struct `SmartPtr` in the current scope --> $DIR/call_method_unknown_referent.rs:46:35 diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index ff31799abb007..5943111efd585 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -21,10 +21,10 @@ note: the traits `Iterator` and `ToTokens` must be implemented --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0282]: type annotations needed - --> $DIR/not-repeatable.rs:11:13 + --> $DIR/not-repeatable.rs:11:25 | LL | let _ = quote! { $($ip)* }; - | ^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr index b8a8f927542f0..c98b9bb38fdb0 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -5,7 +5,7 @@ LL | let x = [Foo(PhantomData); 2]; | ^ LL | LL | extract(x).max(2); - | --- type must be known at this point + | ---------- type must be known at this point | help: consider giving `x` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.stderr index 71ac4f53b3f4f..10308ec07da5a 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ------------------------------------ type must be known at this point + | ---------- type must be known at this point | help: consider specifying the generic argument | @@ -16,8 +16,6 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` -LL | .to_string() - | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 559bc57d90638..849ffd214f07d 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/type-inference/regression-issue-81317.stderr b/tests/ui/type-inference/regression-issue-81317.stderr index a070b50e31175..fcd3fca06e18b 100644 --- a/tests/ui/type-inference/regression-issue-81317.stderr +++ b/tests/ui/type-inference/regression-issue-81317.stderr @@ -5,7 +5,7 @@ LL | let iv = S ^ index.into(); | ^^ LL | LL | &iv.to_bytes_be(); - | ----------- type must be known at this point + | -- type must be known at this point | help: consider giving `iv` an explicit type | diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 9b8698d6ed2c0..4a39b404770d0 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -18,10 +18,10 @@ LL | for node in graph.iter() { | ^^^^ method not found in `&G` error[E0282]: type annotations needed - --> $DIR/issue-13853.rs:28:14 + --> $DIR/issue-13853.rs:28:9 | LL | node.zomg(); - | ^^^^ cannot infer type + | ^^^^ cannot infer type error[E0308]: mismatched types --> $DIR/issue-13853.rs:37:13 From eb8d87d1d84c27876a6f780356be102ac4337ffa Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Mon, 6 Oct 2025 16:55:14 +0300 Subject: [PATCH 1355/1900] avoid in-process serializing&deserializing --- src/librustdoc/html/render/mod.rs | 75 +++++- src/librustdoc/html/render/search_index.rs | 268 ++++++++------------- 2 files changed, 168 insertions(+), 175 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 84d684e0c95f9..52617b6c43204 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -61,6 +61,8 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; +use serde::ser::SerializeSeq as _; +use serde::{Deserialize, Serialize}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -144,7 +146,7 @@ pub(crate) struct IndexItem { } /// A type used for the search index. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] struct RenderType { id: Option, generics: Option>, @@ -301,7 +303,7 @@ impl RenderTypeId { } /// Full type of functions/methods in the search index. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IndexItemFunctionType { inputs: Vec, output: Vec, @@ -423,6 +425,75 @@ impl IndexItemFunctionType { } } +impl Serialize for IndexItemFunctionType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + + struct ParamNames<'a>(&'a [Option]); + + impl<'a> Serialize for ParamNames<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|symbol| symbol.as_ref().map(ToString::to_string).unwrap_or_default()), + ) + } + } + + seq.serialize_element(&ParamNames(&self.param_names))?; + seq.end() + } +} + +impl<'de> Deserialize<'de> for IndexItemFunctionType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::{self, Error as _}; + + struct FunctionDataVisitor; + impl<'de> de::Visitor<'de> for FunctionDataVisitor { + type Value = IndexItemFunctionType; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "fn data") + } + fn visit_seq>(self, mut v: A) -> Result { + let (mut function_signature, _) = v + .next_element()? + .map(|fn_: String| { + IndexItemFunctionType::read_from_string_without_param_names(fn_.as_bytes()) + }) + .ok_or_else(|| A::Error::missing_field("function_signature"))?; + let param_names: Vec> = v + .next_element()? + .map(|param_names: Vec| { + param_names + .into_iter() + .map(|symbol| { + if symbol.is_empty() { None } else { Some(Symbol::intern(&symbol)) } + }) + .collect() + }) + .ok_or_else(|| A::Error::missing_field("param_names"))?; + function_signature.param_names = param_names; + Ok(function_signature) + } + } + deserializer.deserialize_any(FunctionDataVisitor) + } +} + #[derive(Debug, Clone)] pub(crate) struct StylePath { /// The path to the theme diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 253d90294687e..080faff3a6de6 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -34,7 +34,7 @@ pub(crate) struct SerializedSearchIndex { path_data: Vec>, entry_data: Vec>, descs: Vec, - function_data: Vec>, + function_data: Vec>, alias_pointers: Vec>, // inverted index for concrete types and generics type_data: Vec>, @@ -61,7 +61,7 @@ impl SerializedSearchIndex { let mut path_data: Vec> = Vec::new(); let mut entry_data: Vec> = Vec::new(); let mut descs: Vec = Vec::new(); - let mut function_data: Vec> = Vec::new(); + let mut function_data: Vec> = Vec::new(); let mut type_data: Vec> = Vec::new(); let mut alias_pointers: Vec> = Vec::new(); @@ -207,7 +207,7 @@ impl SerializedSearchIndex { path_data: Option, entry_data: Option, desc: String, - function_data: Option, + function_data: Option, type_data: Option, alias_pointer: Option, ) -> usize { @@ -446,73 +446,62 @@ impl SerializedSearchIndex { ..other_entry_data.clone() }), other.descs[other_entryid].clone(), - other.function_data[other_entryid].as_ref().map(|function_data| FunctionData { - function_signature: { - let (mut func, _offset) = - IndexItemFunctionType::read_from_string_without_param_names( - function_data.function_signature.as_bytes(), - ); - fn map_fn_sig_item( - map_other_pathid_to_self_pathid: &mut Vec, - ty: &mut RenderType, - ) { - match ty.id { - None => {} - Some(RenderTypeId::Index(generic)) if generic < 0 => {} - Some(RenderTypeId::Index(id)) => { - let id = usize::try_from(id).unwrap(); - let id = map_other_pathid_to_self_pathid[id]; - assert!(id != !0); - ty.id = - Some(RenderTypeId::Index(isize::try_from(id).unwrap())); - } - _ => unreachable!(), + other.function_data[other_entryid].clone().map(|mut func| { + fn map_fn_sig_item( + map_other_pathid_to_self_pathid: &mut Vec, + ty: &mut RenderType, + ) { + match ty.id { + None => {} + Some(RenderTypeId::Index(generic)) if generic < 0 => {} + Some(RenderTypeId::Index(id)) => { + let id = usize::try_from(id).unwrap(); + let id = map_other_pathid_to_self_pathid[id]; + assert!(id != !0); + ty.id = Some(RenderTypeId::Index(isize::try_from(id).unwrap())); } - if let Some(generics) = &mut ty.generics { - for generic in generics { - map_fn_sig_item(map_other_pathid_to_self_pathid, generic); - } + _ => unreachable!(), + } + if let Some(generics) = &mut ty.generics { + for generic in generics { + map_fn_sig_item(map_other_pathid_to_self_pathid, generic); } - if let Some(bindings) = &mut ty.bindings { - for (param, constraints) in bindings { - *param = match *param { - param @ RenderTypeId::Index(generic) if generic < 0 => { - param - } - RenderTypeId::Index(id) => { - let id = usize::try_from(id).unwrap(); - let id = map_other_pathid_to_self_pathid[id]; - assert!(id != !0); - RenderTypeId::Index(isize::try_from(id).unwrap()) - } - _ => unreachable!(), - }; - for constraint in constraints { - map_fn_sig_item( - map_other_pathid_to_self_pathid, - constraint, - ); + } + if let Some(bindings) = &mut ty.bindings { + for (param, constraints) in bindings { + *param = match *param { + param @ RenderTypeId::Index(generic) if generic < 0 => { + param + } + RenderTypeId::Index(id) => { + let id = usize::try_from(id).unwrap(); + let id = map_other_pathid_to_self_pathid[id]; + assert!(id != !0); + RenderTypeId::Index(isize::try_from(id).unwrap()) } + _ => unreachable!(), + }; + for constraint in constraints { + map_fn_sig_item( + map_other_pathid_to_self_pathid, + constraint, + ); } } } - for input in &mut func.inputs { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, input); - } - for output in &mut func.output { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, output); - } - for clause in &mut func.where_clause { - for entry in clause { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, entry); - } + } + for input in &mut func.inputs { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, input); + } + for output in &mut func.output { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, output); + } + for clause in &mut func.where_clause { + for entry in clause { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, entry); } - let mut result = - String::with_capacity(function_data.function_signature.len()); - func.write_to_string_without_param_names(&mut result); - result - }, - param_names: function_data.param_names.clone(), + } + func }), other.type_data[other_entryid].as_ref().map(|type_data| TypeData { inverted_function_inputs_index: type_data @@ -626,69 +615,55 @@ impl SerializedSearchIndex { }, ), self.descs[id].clone(), - self.function_data[id].as_ref().map( - |FunctionData { function_signature, param_names }| FunctionData { - function_signature: { - let (mut func, _offset) = - IndexItemFunctionType::read_from_string_without_param_names( - function_signature.as_bytes(), - ); - fn map_fn_sig_item(map: &FxHashMap, ty: &mut RenderType) { - match ty.id { - None => {} - Some(RenderTypeId::Index(generic)) if generic < 0 => {} - Some(RenderTypeId::Index(id)) => { + self.function_data[id].clone().map(|mut func| { + fn map_fn_sig_item(map: &FxHashMap, ty: &mut RenderType) { + match ty.id { + None => {} + Some(RenderTypeId::Index(generic)) if generic < 0 => {} + Some(RenderTypeId::Index(id)) => { + let id = usize::try_from(id).unwrap(); + let id = *map.get(&id).unwrap(); + assert!(id != !0); + ty.id = Some(RenderTypeId::Index(isize::try_from(id).unwrap())); + } + _ => unreachable!(), + } + if let Some(generics) = &mut ty.generics { + for generic in generics { + map_fn_sig_item(map, generic); + } + } + if let Some(bindings) = &mut ty.bindings { + for (param, constraints) in bindings { + *param = match *param { + param @ RenderTypeId::Index(generic) if generic < 0 => param, + RenderTypeId::Index(id) => { let id = usize::try_from(id).unwrap(); let id = *map.get(&id).unwrap(); assert!(id != !0); - ty.id = - Some(RenderTypeId::Index(isize::try_from(id).unwrap())); + RenderTypeId::Index(isize::try_from(id).unwrap()) } _ => unreachable!(), + }; + for constraint in constraints { + map_fn_sig_item(map, constraint); } - if let Some(generics) = &mut ty.generics { - for generic in generics { - map_fn_sig_item(map, generic); - } - } - if let Some(bindings) = &mut ty.bindings { - for (param, constraints) in bindings { - *param = match *param { - param @ RenderTypeId::Index(generic) if generic < 0 => { - param - } - RenderTypeId::Index(id) => { - let id = usize::try_from(id).unwrap(); - let id = *map.get(&id).unwrap(); - assert!(id != !0); - RenderTypeId::Index(isize::try_from(id).unwrap()) - } - _ => unreachable!(), - }; - for constraint in constraints { - map_fn_sig_item(map, constraint); - } - } - } - } - for input in &mut func.inputs { - map_fn_sig_item(&map, input); - } - for output in &mut func.output { - map_fn_sig_item(&map, output); } - for clause in &mut func.where_clause { - for entry in clause { - map_fn_sig_item(&map, entry); - } - } - let mut result = String::with_capacity(function_signature.len()); - func.write_to_string_without_param_names(&mut result); - result - }, - param_names: param_names.clone(), - }, - ), + } + } + for input in &mut func.inputs { + map_fn_sig_item(&map, input); + } + for output in &mut func.output { + map_fn_sig_item(&map, output); + } + for clause in &mut func.where_clause { + for entry in clause { + map_fn_sig_item(&map, entry); + } + } + func + }), self.type_data[id].as_ref().map( |TypeData { search_unbox, @@ -1259,48 +1234,6 @@ impl<'de> Deserialize<'de> for SerializedOptional32 { } } -#[derive(Clone, Debug)] -pub struct FunctionData { - function_signature: String, - param_names: Vec, -} - -impl Serialize for FunctionData { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.function_signature)?; - seq.serialize_element(&self.param_names)?; - seq.end() - } -} - -impl<'de> Deserialize<'de> for FunctionData { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FunctionDataVisitor; - impl<'de> de::Visitor<'de> for FunctionDataVisitor { - type Value = FunctionData; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "fn data") - } - fn visit_seq>(self, mut v: A) -> Result { - let function_signature: String = v - .next_element()? - .ok_or_else(|| A::Error::missing_field("function_signature"))?; - let param_names: Vec = - v.next_element()?.ok_or_else(|| A::Error::missing_field("param_names"))?; - Ok(FunctionData { function_signature, param_names }) - } - } - deserializer.deserialize_any(FunctionDataVisitor) - } -} - /// Builds the search index from the collected metadata pub(crate) fn build_index( krate: &clean::Crate, @@ -1927,18 +1860,7 @@ pub(crate) fn build_index( // because the postings list has to fill in an empty array for each // unoccupied size. if item.ty.is_fn_like() { 0 } else { 16 }; - serialized_index.function_data[new_entry_id] = Some(FunctionData { - function_signature: { - let mut function_signature = String::new(); - search_type.write_to_string_without_param_names(&mut function_signature); - function_signature - }, - param_names: search_type - .param_names - .iter() - .map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new())) - .collect::>(), - }); + serialized_index.function_data[new_entry_id] = Some(search_type.clone()); for index in used_in_function_inputs { let postings = if index >= 0 { assert!(serialized_index.path_data[index as usize].is_some()); From 978ab7c54229359f62593d4afd8249a09bf7607c Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 7 Oct 2025 12:57:59 +0300 Subject: [PATCH 1356/1900] nicer serde impls --- src/librustdoc/html/render/mod.rs | 103 +++++++++++++++++++----------- 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 52617b6c43204..d604526aa4a18 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -62,7 +62,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; use serde::ser::SerializeSeq as _; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -428,29 +428,31 @@ impl IndexItemFunctionType { impl Serialize for IndexItemFunctionType { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { - let mut seq = serializer.serialize_seq(Some(2))?; - let mut fn_type = String::new(); - self.write_to_string_without_param_names(&mut fn_type); - seq.serialize_element(&fn_type)?; - struct ParamNames<'a>(&'a [Option]); impl<'a> Serialize for ParamNames<'a> { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { serializer.collect_seq( self.0 .iter() - .map(|symbol| symbol.as_ref().map(ToString::to_string).unwrap_or_default()), + .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), ) } } + let mut seq = serializer.serialize_seq(Some(2))?; + + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + seq.serialize_element(&ParamNames(&self.param_names))?; + seq.end() } } @@ -458,39 +460,64 @@ impl Serialize for IndexItemFunctionType { impl<'de> Deserialize<'de> for IndexItemFunctionType { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { - use serde::de::{self, Error as _}; + use serde::de::{self, SeqAccess}; - struct FunctionDataVisitor; - impl<'de> de::Visitor<'de> for FunctionDataVisitor { - type Value = IndexItemFunctionType; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "fn data") - } - fn visit_seq>(self, mut v: A) -> Result { - let (mut function_signature, _) = v - .next_element()? - .map(|fn_: String| { - IndexItemFunctionType::read_from_string_without_param_names(fn_.as_bytes()) - }) - .ok_or_else(|| A::Error::missing_field("function_signature"))?; - let param_names: Vec> = v - .next_element()? - .map(|param_names: Vec| { - param_names - .into_iter() - .map(|symbol| { - if symbol.is_empty() { None } else { Some(Symbol::intern(&symbol)) } - }) - .collect() - }) - .ok_or_else(|| A::Error::missing_field("param_names"))?; - function_signature.param_names = param_names; - Ok(function_signature) + #[derive(Deserialize)] + struct Deserialized { + #[serde(deserialize_with = "function_signature")] + function_signature: IndexItemFunctionType, + #[serde(deserialize_with = "param_names")] + param_names: Vec>, + } + + fn function_signature<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + String::deserialize(deserializer).map(|sig| { + IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 + }) + } + + fn param_names<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Vec>; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("seq of param names") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(symbol) = seq.next_element::()? { + param_names.push(if symbol.is_empty() { + None + } else { + Some(Symbol::intern(&symbol)) + }); + } + + Ok(param_names) + } } + + deserializer.deserialize_seq(Visitor) } - deserializer.deserialize_any(FunctionDataVisitor) + + let Deserialized { mut function_signature, param_names } = + Deserialized::deserialize(deserializer)?; + function_signature.param_names = param_names; + + Ok(function_signature) } } From f8ee9f018c53c7555c07a035ff2efaad9723adc9 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 18:26:36 +0800 Subject: [PATCH 1357/1900] Print tip for human error format when check fail --- src/tools/compiletest/src/runtest.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index bd32bec383fa3..e9dea95cafbdc 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -878,6 +878,21 @@ impl<'test> TestCx<'test> { prefix = self.error_prefix(), n = not_found.len(), ); + + // FIXME: Ideally, we should check this at the place where we actually parse error annotations. + // it's better to use (negated) heuristic inside normalize_output if possible + if let Some(human_format) = self.props.compile_flags.iter().find(|flag| { + // `human`, `human-unicode`, `short` will not generate JSON output + flag.contains("error-format") + && (flag.contains("short") || flag.contains("human")) + }) { + let msg = format!( + "tests with compile flag `{}` should not have error annotations such as `//~ ERROR`", + human_format + ).color(Color::Red); + writeln!(self.stdout, "{}", msg); + } + for error in ¬_found { print_error(error); let mut suggestions = Vec::new(); From c5ab5307497269eae873750c7890b579bac03fb0 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 7 Oct 2025 13:37:49 +0300 Subject: [PATCH 1358/1900] move serde impls to separate module --- src/librustdoc/html/render/mod.rs | 98 ----------------- src/librustdoc/html/render/search_index.rs | 7 +- .../html/render/search_index/serde.rs | 102 ++++++++++++++++++ 3 files changed, 106 insertions(+), 101 deletions(-) create mode 100644 src/librustdoc/html/render/search_index/serde.rs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index d604526aa4a18..c3b1e3eb6c087 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -61,8 +61,6 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; -use serde::ser::SerializeSeq as _; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -425,102 +423,6 @@ impl IndexItemFunctionType { } } -impl Serialize for IndexItemFunctionType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - struct ParamNames<'a>(&'a [Option]); - - impl<'a> Serialize for ParamNames<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_seq( - self.0 - .iter() - .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), - ) - } - } - - let mut seq = serializer.serialize_seq(Some(2))?; - - let mut fn_type = String::new(); - self.write_to_string_without_param_names(&mut fn_type); - seq.serialize_element(&fn_type)?; - - seq.serialize_element(&ParamNames(&self.param_names))?; - - seq.end() - } -} - -impl<'de> Deserialize<'de> for IndexItemFunctionType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::{self, SeqAccess}; - - #[derive(Deserialize)] - struct Deserialized { - #[serde(deserialize_with = "function_signature")] - function_signature: IndexItemFunctionType, - #[serde(deserialize_with = "param_names")] - param_names: Vec>, - } - - fn function_signature<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result { - String::deserialize(deserializer).map(|sig| { - IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 - }) - } - - fn param_names<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result>, D::Error> { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = Vec>; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("seq of param names") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); - - while let Some(symbol) = seq.next_element::()? { - param_names.push(if symbol.is_empty() { - None - } else { - Some(Symbol::intern(&symbol)) - }); - } - - Ok(param_names) - } - } - - deserializer.deserialize_seq(Visitor) - } - - let Deserialized { mut function_signature, param_names } = - Deserialized::deserialize(deserializer)?; - function_signature.param_names = param_names; - - Ok(function_signature) - } -} - #[derive(Debug, Clone)] pub(crate) struct StylePath { /// The path to the theme diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 080faff3a6de6..67a88b1d2c57c 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,9 +1,13 @@ pub(crate) mod encode; +mod serde; use std::collections::BTreeSet; use std::collections::hash_map::Entry; use std::path::Path; +use ::serde::de::{self, Deserializer, Error as _}; +use ::serde::ser::{SerializeSeq, Serializer}; +use ::serde::{Deserialize, Serialize}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir::attrs::AttributeKind; @@ -12,9 +16,6 @@ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; use rustc_span::symbol::{Symbol, kw}; -use serde::de::{self, Deserializer, Error as _}; -use serde::ser::{SerializeSeq, Serializer}; -use serde::{Deserialize, Serialize}; use stringdex::internals as stringdex_internals; use thin_vec::ThinVec; use tracing::instrument; diff --git a/src/librustdoc/html/render/search_index/serde.rs b/src/librustdoc/html/render/search_index/serde.rs new file mode 100644 index 0000000000000..2ba91a56031d5 --- /dev/null +++ b/src/librustdoc/html/render/search_index/serde.rs @@ -0,0 +1,102 @@ +use std::fmt::{self, Formatter}; + +use rustc_span::Symbol; +use serde::de::{self, SeqAccess}; +use serde::ser::SerializeSeq as _; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::html::render::IndexItemFunctionType; + +impl Serialize for IndexItemFunctionType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct ParamNames<'a>(&'a [Option]); + + impl<'a> Serialize for ParamNames<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), + ) + } + } + + let mut seq = serializer.serialize_seq(Some(2))?; + + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + + seq.serialize_element(&ParamNames(&self.param_names))?; + + seq.end() + } +} + +impl<'de> Deserialize<'de> for IndexItemFunctionType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Deserialized { + #[serde(deserialize_with = "function_signature")] + function_signature: IndexItemFunctionType, + #[serde(deserialize_with = "param_names")] + param_names: Vec>, + } + + fn function_signature<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + String::deserialize(deserializer).map(|sig| { + IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 + }) + } + + fn param_names<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Vec>; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("seq of param names") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(symbol) = seq.next_element::()? { + param_names.push(if symbol.is_empty() { + None + } else { + Some(Symbol::intern(&symbol)) + }); + } + + Ok(param_names) + } + } + + deserializer.deserialize_seq(Visitor) + } + + let Deserialized { mut function_signature, param_names } = + Deserialized::deserialize(deserializer)?; + function_signature.param_names = param_names; + + Ok(function_signature) + } +} From 713e0dddfd22ef37765fd5907b4639f7a690f8ed Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 21:05:45 +0800 Subject: [PATCH 1359/1900] Fix comments error for CtfeProvenance --- compiler/rustc_middle/src/mir/interpret/pointer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c581b2ebf092d..2aac8852b7e84 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -167,7 +167,7 @@ impl CtfeProvenance { } impl Provenance for CtfeProvenance { - // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*, + // With the `CtfeProvenance` as provenance, the `offset` is interpreted *relative to the allocation*, // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; From 4203fe84fb45db376436d1f7f33d68bb024d6fc1 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 12 Sep 2025 14:05:17 +0200 Subject: [PATCH 1360/1900] Implement std::sync::Mutex interception in GenMC mode. --- src/tools/miri/genmc-sys/build.rs | 1 + .../genmc-sys/cpp/include/MiriInterface.hpp | 31 ++- .../cpp/src/MiriInterface/EventHandling.cpp | 11 +- .../genmc-sys/cpp/src/MiriInterface/Mutex.cpp | 159 ++++++++++++ .../src/MiriInterface/ThreadManagement.cpp | 1 + src/tools/miri/genmc-sys/src/lib.rs | 45 +++- src/tools/miri/src/concurrency/genmc/dummy.rs | 9 + .../miri/src/concurrency/genmc/intercept.rs | 38 --- src/tools/miri/src/concurrency/genmc/mod.rs | 86 +++---- .../miri/src/concurrency/genmc/scheduling.rs | 14 +- src/tools/miri/src/concurrency/genmc/shims.rs | 234 ++++++++++++++++++ src/tools/miri/src/concurrency/thread.rs | 38 +-- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/machine.rs | 15 +- .../fail/shims/mutex_diff_thread_unlock.rs | 28 +++ .../shims/mutex_diff_thread_unlock.stderr | 86 +++++++ .../genmc/fail/shims/mutex_double_unlock.rs | 22 ++ .../fail/shims/mutex_double_unlock.stderr | 23 ++ .../tests/genmc/pass/shims/mutex_deadlock.rs | 42 ++++ .../genmc/pass/shims/mutex_deadlock.stderr | 5 + .../tests/genmc/pass/shims/mutex_simple.rs | 68 +++++ .../genmc/pass/shims/mutex_simple.stderr | 3 + .../spinloop_assume.bounded123.stderr | 0 .../spinloop_assume.bounded321.stderr | 0 .../spinloop_assume.replaced123.stderr | 0 .../spinloop_assume.replaced321.stderr | 0 .../{intercept => shims}/spinloop_assume.rs | 0 27 files changed, 838 insertions(+), 122 deletions(-) create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp delete mode 100644 src/tools/miri/src/concurrency/genmc/intercept.rs create mode 100644 src/tools/miri/src/concurrency/genmc/shims.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.bounded123.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.bounded321.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.replaced123.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.replaced321.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.rs (100%) diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 964382d824089..f10e151a3d0bc 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -233,6 +233,7 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { let cpp_files = [ "MiriInterface/EventHandling.cpp", "MiriInterface/Exploration.cpp", + "MiriInterface/Mutex.cpp", "MiriInterface/Setup.cpp", "MiriInterface/ThreadManagement.cpp", ] diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index c9da718aabd61..b0bd397ab34b1 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -12,7 +12,6 @@ // GenMC headers: #include "ExecutionGraph/EventLabel.hpp" -#include "Static/ModuleID.hpp" #include "Support/MemOrdering.hpp" #include "Support/RMWOps.hpp" #include "Verification/Config.hpp" @@ -36,6 +35,7 @@ struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; struct CompareExchangeResult; +struct MutexLockResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -136,10 +136,13 @@ struct MiriGenmcShim : private GenMCDriver { /**** Blocking instructions ****/ /// Inform GenMC that the thread should be blocked. - /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user - /// supplied assume statements. This can become a parameter once more types of assumes are - /// added. - void handle_assume_block(ThreadId thread_id); + void handle_assume_block(ThreadId thread_id, AssumeType assume_type); + + /**** Mutex handling ****/ + auto handle_mutex_lock(ThreadId thread_id, uint64_t address, uint64_t size) -> MutexLockResult; + auto handle_mutex_try_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult; + auto handle_mutex_unlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult; /***** Exploration related functionality *****/ @@ -358,4 +361,22 @@ inline CompareExchangeResult from_error(std::unique_ptr error) { } } // namespace CompareExchangeResultExt +namespace MutexLockResultExt { +inline MutexLockResult ok(bool is_lock_acquired) { + return MutexLockResult { /* error: */ nullptr, /* is_reset: */ false, is_lock_acquired }; +} + +inline MutexLockResult reset() { + return MutexLockResult { /* error: */ nullptr, + /* is_reset: */ true, + /* is_lock_acquired: */ false }; +} + +inline MutexLockResult from_error(std::unique_ptr error) { + return MutexLockResult { /* error: */ std::move(error), + /* is_reset: */ false, + /* is_lock_acquired: */ false }; +} +} // namespace MutexLockResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index a8f8b9cc24f9e..2b6e5749d41a5 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -32,8 +32,9 @@ /**** Blocking instructions ****/ -void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { - GenMCDriver::handleAssume(inc_pos(thread_id), AssumeType::User); +void MiriGenmcShim::handle_assume_block(ThreadId thread_id, AssumeType assume_type) { + BUG_ON(getExec().getGraph().isThreadBlocked(thread_id)); + GenMCDriver::handleAssume(inc_pos(thread_id), assume_type); } /**** Memory access handling ****/ @@ -59,6 +60,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { if (const auto* err = std::get_if(&ret)) return LoadResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. if (ret_val == nullptr) ERROR("Unimplemented: load returned unexpected result."); return LoadResultExt::from_value(*ret_val); @@ -88,6 +90,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { return StoreResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: Store returned unexpected result." @@ -130,6 +133,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { return ReadModifyWriteResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&load_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. if (nullptr == ret_val) { ERROR("Unimplemented: read-modify-write returned unexpected result."); } @@ -151,6 +155,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { return ReadModifyWriteResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: RMW store returned unexpected result." @@ -195,6 +200,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&load_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&load_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON(nullptr == ret_val, "Unimplemented: load returned unexpected result."); const auto read_old_val = *ret_val; if (read_old_val != expectedVal) @@ -215,6 +221,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&store_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: compare-exchange store returned unexpected result." diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp new file mode 100644 index 0000000000000..fc3f5e6e09a67 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp @@ -0,0 +1,159 @@ +/** This file contains functionality related to handling mutexes. */ + +#include "MiriInterface.hpp" + +// GenMC headers: +#include "Static/ModuleID.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +#define MUTEX_UNLOCKED SVal(0) +#define MUTEX_LOCKED SVal(1) + +auto MiriGenmcShim::handle_mutex_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult { + // This annotation informs GenMC about the condition required to make this lock call succeed. + // It stands for `value_read_by_load != MUTEX_LOCKED`. + const auto size_bits = size * 8; + const auto annot = std::move(Annotation( + AssumeType::Spinloop, + Annotation::ExprVP( + NeExpr::create( + // `RegisterExpr` marks the value of the current expression, i.e., the loaded value. + // The `id` is ignored by GenMC; it is only used by the LLI frontend to substitute + // other variables from previous expressions that may be used here. + RegisterExpr::create(size_bits, /* id */ 0), + ConcreteExpr::create(size_bits, MUTEX_LOCKED) + ) + .release() + ) + )); + + // As usual, we need to tell GenMC which value was stored at this location before this atomic + // access, if there previously was a non-atomic initializing access. We set the initial state of + // a mutex to be "unlocked". + const auto old_val = MUTEX_UNLOCKED; + const auto load_ret = handle_load_reset_if_none( + thread_id, + old_val, + address, + size, + annot, + EventDeps() + ); + if (const auto* err = std::get_if(&load_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // If we get a `Reset`, GenMC decided that this lock operation should not yet run, since it + // would not acquire the mutex. Like the handling of the case further down where we read a `1` + // ("Mutex already locked"), Miri should call the handle function again once the current thread + // is scheduled by GenMC the next time. + if (std::holds_alternative(load_ret)) + return MutexLockResultExt::reset(); + + const auto* ret_val = std::get_if(&load_ret); + ERROR_ON(!ret_val, "Unimplemented: mutex lock returned unexpected result."); + ERROR_ON( + *ret_val != MUTEX_UNLOCKED && *ret_val != MUTEX_LOCKED, + "Mutex read value was neither 0 nor 1" + ); + const bool is_lock_acquired = *ret_val == MUTEX_UNLOCKED; + if (is_lock_acquired) { + const auto store_ret = GenMCDriver::handleStore( + inc_pos(thread_id), + old_val, + address, + size, + EventDeps() + ); + if (const auto* err = std::get_if(&store_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // We don't update Miri's memory for this operation so we don't need to know if the store + // was the co-maximal store, but we still check that we at least get a boolean as the result + // of the store. + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex try_lock returned unexpected result." + ); + } else { + // We did not acquire the mutex, so we tell GenMC to block the thread until we can acquire + // it. GenMC determines this based on the annotation we pass with the load further up in + // this function, namely when that load will read a value other than `MUTEX_LOCKED`. + this->handle_assume_block(thread_id, AssumeType::Spinloop); + } + return MutexLockResultExt::ok(is_lock_acquired); +} + +auto MiriGenmcShim::handle_mutex_try_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult { + auto& currPos = threads_action_[thread_id].event; + // As usual, we need to tell GenMC which value was stored at this location before this atomic + // access, if there previously was a non-atomic initializing access. We set the initial state of + // a mutex to be "unlocked". + const auto old_val = MUTEX_UNLOCKED; + const auto load_ret = GenMCDriver::handleLoad( + ++currPos, + old_val, + SAddr(address), + ASize(size) + ); + if (const auto* err = std::get_if(&load_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&load_ret); + if (nullptr == ret_val) { + ERROR("Unimplemented: mutex trylock load returned unexpected result."); + } + + ERROR_ON( + *ret_val != MUTEX_UNLOCKED && *ret_val != MUTEX_LOCKED, + "Mutex read value was neither 0 nor 1" + ); + const bool is_lock_acquired = *ret_val == MUTEX_UNLOCKED; + if (!is_lock_acquired) { + return MutexLockResultExt::ok(false); /* Lock already held. */ + } + + const auto store_ret = GenMCDriver::handleStore( + ++currPos, + old_val, + SAddr(address), + ASize(size) + ); + if (const auto* err = std::get_if(&store_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // We don't update Miri's memory for this operation so we don't need to know if the store was + // co-maximal, but we still check that we get a boolean result. + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex try_lock returned unexpected result." + ); + return MutexLockResultExt::ok(true); +} + +auto MiriGenmcShim::handle_mutex_unlock(ThreadId thread_id, uint64_t address, uint64_t size) + -> StoreResult { + const auto pos = inc_pos(thread_id); + const auto ret = GenMCDriver::handleStore( + pos, + // As usual, we need to tell GenMC which value was stored at this location before this + // atomic access, if there previously was a non-atomic initializing access. We set the + // initial state of a mutex to be "unlocked". + /* old_val */ MUTEX_UNLOCKED, + MemOrdering::Release, + SAddr(address), + ASize(size), + AType::Signed, + /* store_value */ MUTEX_UNLOCKED, + EventDeps() + ); + if (const auto* err = std::get_if(&ret)) + return StoreResultExt::from_error(format_error(*err)); + const bool* is_coherence_order_maximal_write = std::get_if(&ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex unlock returned unexpected result." + ); + return StoreResultExt::ok(*is_coherence_order_maximal_write); +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp index 352d27adc3e8b..d2061fcb406c9 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp @@ -38,6 +38,7 @@ void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) { if (!std::holds_alternative(ret)) { dec_pos(thread_id); } + // FIXME(genmc): handle `HandleResult::{Invalid, Reset, VerificationError}` return values. // NOTE: Thread return value is ignored, since Miri doesn't need it. } diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 619d4ab67b2f2..a34c7f2b3a667 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -254,6 +254,17 @@ mod ffi { is_coherence_order_maximal_write: bool, } + #[must_use] + #[derive(Debug)] + struct MutexLockResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// If true, GenMC determined that we should retry the mutex lock operation once the thread attempting to lock is scheduled again. + is_reset: bool, + /// Indicate whether the lock was acquired by this thread. + is_lock_acquired: bool, + } + /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ @@ -305,6 +316,13 @@ mod ffi { UMin = 10, } + #[derive(Debug)] + enum AssumeType { + User = 0, + Barrier = 1, + Spinloop = 2, + } + // # Safety // // This block is unsafe to allow defining safe methods inside. @@ -323,6 +341,7 @@ mod ffi { (This tells cxx that the enums defined above are already defined on the C++ side; it will emit assertions to ensure that the two definitions agree.) ****/ type ActionKind; + type AssumeType; type MemOrdering; type RMWBinOp; type SchedulePolicy; @@ -430,7 +449,31 @@ mod ffi { /// Inform GenMC that the thread should be blocked. /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user supplied assume statements. /// This can become a parameter once more types of assumes are added. - fn handle_assume_block(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + fn handle_assume_block( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + assume_type: AssumeType, + ); + + /**** Mutex handling ****/ + fn handle_mutex_lock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handle_mutex_try_lock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handle_mutex_unlock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> StoreResult; /***** Exploration related functionality *****/ diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index ef5b24d5abad5..b9e09e34dc360 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -43,6 +43,15 @@ mod intercept { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn genmc_intercept_function( + &mut self, + _instance: rustc_middle::ty::Instance<'tcx>, + _args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + _dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx, bool> { + unreachable!() + } + fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { unreachable!(); } diff --git a/src/tools/miri/src/concurrency/genmc/intercept.rs b/src/tools/miri/src/concurrency/genmc/intercept.rs deleted file mode 100644 index 4867b1dc21afb..0000000000000 --- a/src/tools/miri/src/concurrency/genmc/intercept.rs +++ /dev/null @@ -1,38 +0,0 @@ -use tracing::debug; - -use crate::concurrency::thread::EvalContextExt as _; -use crate::{ - BlockReason, InterpResult, MachineCallback, MiriInterpCx, OpTy, UnblockKind, VisitProvenance, - VisitWith, callback, interp_ok, -}; - -// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. - -/// Other functionality not directly related to event handling -impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} -pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. - /// Returns `true` if the current thread should be blocked in Miri too. - fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let condition_bool = this.read_scalar(condition)?.to_bool()?; - debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); - if condition_bool { - return interp_ok(()); - } - let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); - genmc_ctx.handle_assume_block(&this.machine)?; - this.block_thread( - BlockReason::Genmc, - None, - callback!( - @capture<'tcx> {} - |_this, unblock: UnblockKind| { - assert_eq!(unblock, UnblockKind::Ready); - unreachable!("GenMC should never unblock a thread blocked by an `assume`."); - } - ), - ); - interp_ok(()) - } -} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index caec5e921cdfe..bc475c680b5b1 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -27,16 +27,16 @@ use crate::*; mod config; mod global_allocations; mod helper; -mod intercept; mod run; pub(crate) mod scheduling; +mod shims; mod thread_id_map; pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; -pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; +pub use self::shims::EvalContextExt as GenmcEvalContextExt; #[derive(Debug)] pub enum ExecutionEndResult { @@ -200,6 +200,14 @@ impl GenmcCtx { fn get_alloc_data_races(&self) -> bool { self.exec_state.allow_data_races.get() } + + /// Get the GenMC id of the currently active thread. + #[must_use] + fn active_thread_genmc_tid<'tcx>(&self, machine: &MiriMachine<'tcx>) -> i32 { + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + thread_infos.get_genmc_tid(curr_thread) + } } /// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions. @@ -309,12 +317,10 @@ impl GenmcCtx { ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); - - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - self.handle.borrow_mut().pin_mut().handle_fence(genmc_tid, ordering.to_genmc()); + self.handle + .borrow_mut() + .pin_mut() + .handle_fence(self.active_thread_genmc_tid(machine), ordering.to_genmc()); interp_ok(()) } @@ -425,12 +431,8 @@ impl GenmcCtx { debug!( "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, orderings: {fail:?}), can fail spuriously: {can_fail_spuriously}" ); - - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let genmc_tid = thread_infos.get_genmc_tid(ecx.machine.threads.active_thread()); - let cas_result = self.handle.borrow_mut().pin_mut().handle_compare_exchange( - genmc_tid, + self.active_thread_genmc_tid(&ecx.machine), address.bytes(), size.bytes(), scalar_to_genmc_scalar(ecx, self, expected_old_value)?, @@ -591,14 +593,10 @@ impl GenmcCtx { return ecx .get_global_allocation_address(&self.global_state.global_allocations, alloc_id); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); - let chosen_address = self.handle.borrow_mut().pin_mut().handle_malloc( - genmc_tid, + self.active_thread_genmc_tid(machine), genmc_size, alignment.bytes(), ); @@ -632,11 +630,12 @@ impl GenmcCtx { !self.get_alloc_data_races(), "memory deallocation with data race checking disabled." ); - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - if self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()) { + if self + .handle + .borrow_mut() + .pin_mut() + .handle_free(self.active_thread_genmc_tid(machine), address.bytes()) + { // FIXME(genmc): improve error handling. // An error was detected, so we get the error string from GenMC. throw_ub_format!("{}", self.try_get_error().unwrap()); @@ -690,7 +689,7 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); - // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 + // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0. self.handle.borrow_mut().pin_mut().handle_thread_finish(genmc_tid, /* ret_val */ 0); } @@ -752,17 +751,12 @@ impl GenmcCtx { "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes.", ); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); - debug!( - "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", + "GenMC: load, address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", addr = address.bytes() ); - let load_result = self.handle.borrow_mut().pin_mut().handle_load( - genmc_tid, + self.active_thread_genmc_tid(machine), address.bytes(), size.bytes(), memory_ordering, @@ -803,17 +797,12 @@ impl GenmcCtx { "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes." ); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); - debug!( - "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", + "GenMC: store, address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", addr = address.bytes() ); - let store_result = self.handle.borrow_mut().pin_mut().handle_store( - genmc_tid, + self.active_thread_genmc_tid(machine), address.bytes(), size.bytes(), genmc_value, @@ -854,14 +843,11 @@ impl GenmcCtx { MAX_ACCESS_SIZE, size.bytes() ); - - let curr_thread_id = ecx.machine.threads.active_thread(); - let genmc_tid = self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread_id); debug!( - "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", + "GenMC: atomic_rmw_op (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", ); let rmw_result = self.handle.borrow_mut().pin_mut().handle_read_modify_write( - genmc_tid, + self.active_thread_genmc_tid(&ecx.machine), address.bytes(), size.bytes(), genmc_rmw_op, @@ -884,20 +870,6 @@ impl GenmcCtx { }; interp_ok((old_value_scalar, new_value_scalar)) } - - /**** Blocking functionality ****/ - - /// Handle a user thread getting blocked. - /// This may happen due to an manual `assume` statement added by a user - /// or added by some automated program transformation, e.g., for spinloops. - fn handle_assume_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - let curr_thread = machine.threads.active_thread(); - let genmc_curr_thread = - self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread); - debug!("GenMC: assume statement, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); - self.handle.borrow_mut().pin_mut().handle_assume_block(genmc_curr_thread); - interp_ok(()) - } } impl VisitProvenance for GenmcCtx { diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index be7df8682f07c..6ccbaf4f2482b 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -63,15 +63,25 @@ fn get_function_kind<'tcx>( ) -> InterpResult<'tcx, NextInstrKind> { use NextInstrKind::*; let callee_def_id = match func_ty.kind() { - ty::FnDef(def_id, _args) => def_id, + ty::FnDef(def_id, _args) => *def_id, _ => return interp_ok(MaybeAtomic(ActionKind::Load)), // we don't know the callee, might be pthread_join }; let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { - if ecx.tcx.is_foreign_item(*callee_def_id) { + if ecx.tcx.is_foreign_item(callee_def_id) { // Some shims, like pthread_join, must be considered loads. So just consider them all loads, // these calls are not *that* common. return interp_ok(MaybeAtomic(ActionKind::Load)); } + // NOTE: Functions intercepted by Miri in `concurrency/genmc/intercep.rs` must also be added here. + // Such intercepted functions, like `sys::Mutex::lock`, should be treated as atomics to ensure we call the scheduler when we encounter one of them. + // These functions must also be classified whether they may have load semantics. + if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_lock, callee_def_id) + || ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_try_lock, callee_def_id) + { + return interp_ok(MaybeAtomic(ActionKind::Load)); + } else if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_unlock, callee_def_id) { + return interp_ok(MaybeAtomic(ActionKind::NonLoad)); + } // The next step is a call to a regular Rust function. return interp_ok(NonAtomic); }; diff --git a/src/tools/miri/src/concurrency/genmc/shims.rs b/src/tools/miri/src/concurrency/genmc/shims.rs new file mode 100644 index 0000000000000..4685dfd1b8dd7 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/shims.rs @@ -0,0 +1,234 @@ +use genmc_sys::AssumeType; +use rustc_middle::ty; +use tracing::debug; + +use crate::concurrency::genmc::MAX_ACCESS_SIZE; +use crate::concurrency::thread::EvalContextExt as _; +use crate::*; + +impl GenmcCtx { + /// Handle a user thread getting blocked. + /// This may happen due to an manual `assume` statement added by a user + /// or added by some automated program transformation, e.g., for spinloops. + fn handle_assume_block<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + assume_type: AssumeType, + ) -> InterpResult<'tcx> { + debug!("GenMC: assume statement, blocking active thread."); + self.handle + .borrow_mut() + .pin_mut() + .handle_assume_block(self.active_thread_genmc_tid(machine), assume_type); + interp_ok(()) + } +} + +// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. + +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Small helper to get the arguments of an intercepted function call. + fn get_fn_args( + &self, + instance: ty::Instance<'tcx>, + args: &[FnArg<'tcx>], + ) -> InterpResult<'tcx, [OpTy<'tcx>; N]> { + let this = self.eval_context_ref(); + let args = this.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit? + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + panic!("{} is a diagnostic item expected to have {} arguments", instance, N); + } + + /**** Blocking functionality ****/ + + /// Handle a thread getting blocked by a user assume (not an automatically generated assume). + /// Unblocking this thread in the current execution will cause a panic. + /// Miri does not provide GenMC with the annotations to determine when to unblock the thread, so it should never be unblocked. + fn handle_user_assume_block(&mut self) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + debug!( + "GenMC: block thread {:?} due to failing assume statement.", + this.machine.threads.active_thread() + ); + assert!(this.machine.threads.active_thread_ref().is_enabled()); + // Block the thread on the GenMC side. + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + genmc_ctx.handle_assume_block(&this.machine, AssumeType::User)?; + // Block the thread on the Miri side. + this.block_thread( + BlockReason::Genmc, + None, + callback!( + @capture<'tcx> {} + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + unreachable!("GenMC should never unblock a thread blocked by an `assume`."); + } + ), + ); + interp_ok(()) + } + + fn intercept_mutex_lock(&mut self, mutex: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::lock()"); + let this = self.eval_context_mut(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + + let size = mutex.layout.size.bytes(); + assert!( + size <= MAX_ACCESS_SIZE, + "Mutex is larger than maximal size of a memory access supported by GenMC ({size} > {MAX_ACCESS_SIZE})" + ); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_lock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + size, + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + if result.is_reset { + debug!("GenMC: Mutex::lock: Reset"); + // GenMC informed us to reset and try the lock again later. + // We block the current thread until GenMC schedules it again. + this.block_thread( + crate::BlockReason::Genmc, + None, + crate::callback!( + @capture<'tcx> { + mutex: MPlaceTy<'tcx>, + } + |this, unblock: crate::UnblockKind| { + debug!("GenMC: Mutex::lock: unblocking callback called, attempting to lock the Mutex again."); + assert_eq!(unblock, crate::UnblockKind::Ready); + this.intercept_mutex_lock(mutex)?; + interp_ok(()) + } + ), + ); + } else if result.is_lock_acquired { + debug!("GenMC: Mutex::lock successfully acquired the Mutex."); + } else { + debug!("GenMC: Mutex::lock failed to acquire the Mutex, permanently blocking thread."); + // NOTE: `handle_mutex_lock` already blocked the current thread on the GenMC side. + this.block_thread( + crate::BlockReason::Genmc, + None, + crate::callback!( + @capture<'tcx> { + mutex: MPlaceTy<'tcx>, + } + |_this, _unblock: crate::UnblockKind| { + unreachable!("A thread blocked on `Mutex::lock` should not be unblocked again."); + } + ), + ); + } + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC. + interp_ok(()) + } + + fn intercept_mutex_try_lock( + &mut self, + mutex: MPlaceTy<'tcx>, + dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::try_lock()"); + let this = self.eval_context_mut(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let size = mutex.layout.size.bytes(); + assert!( + size <= MAX_ACCESS_SIZE, + "Mutex is larger than maximal size of a memory access supported by GenMC ({size} > {MAX_ACCESS_SIZE})" + ); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_try_lock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + size, + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + debug!( + "GenMC: Mutex::try_lock(): is_reset: {}, is_lock_acquired: {}", + result.is_reset, result.is_lock_acquired + ); + assert!(!result.is_reset, "GenMC returned 'reset' for a mutex try_lock."); + // Write the return value of try_lock, i.e., whether we acquired the mutex. + this.write_scalar(Scalar::from_bool(result.is_lock_acquired), dest)?; + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC. + interp_ok(()) + } + + fn intercept_mutex_unlock(&self, mutex: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::unlock()"); + let this = self.eval_context_ref(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_unlock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + mutex.layout.size.bytes(), + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC.} + interp_ok(()) + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Given a `ty::Instance<'tcx>`, do any required special handling. + /// Returns true if this `instance` should be skipped (i.e., no MIR should be executed for it). + fn genmc_intercept_function( + &mut self, + instance: rustc_middle::ty::Instance<'tcx>, + args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + assert!( + this.machine.data_race.as_genmc_ref().is_some(), + "This function should only be called in GenMC mode." + ); + + // NOTE: When adding new intercepted functions here, they must also be added to `fn get_function_kind` in `concurrency/genmc/scheduling.rs`. + use rustc_span::sym; + if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_lock(mutex)?; + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_try_lock(mutex, dest)?; + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_unlock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_unlock(mutex)?; + } else { + // Nothing to intercept. + return interp_ok(false); + } + interp_ok(true) + } + + /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. + /// Returns `true` if the current thread should be blocked in Miri too. + fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let condition_bool = this.read_scalar(condition)?.to_bool()?; + debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); + if condition_bool { + return interp_ok(()); + } + this.handle_user_assume_block() + } +} diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 0d9dbe052e399..13492c99294ea 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -708,21 +708,31 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // In GenMC mode, we let GenMC do the scheduling. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { - return interp_ok(SchedulingAction::ExecuteStep); - }; - // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. - if this.machine.threads.threads[next_thread_id].state.is_blocked_on(BlockReason::Genmc) - { - info!("GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now."); - this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + if this.machine.data_race.as_genmc_ref().is_some() { + loop { + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { + return interp_ok(SchedulingAction::ExecuteStep); + }; + // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. + if this.machine.threads.threads[next_thread_id] + .state + .is_blocked_on(BlockReason::Genmc) + { + info!( + "GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now." + ); + this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + } + // The thread we just unblocked may have been blocked again during the unblocking callback. + // In that case, we need to ask for a different thread to run next. + let thread_manager = &mut this.machine.threads; + if thread_manager.threads[next_thread_id].state.is_enabled() { + // Set the new active thread. + thread_manager.active_thread = next_thread_id; + return interp_ok(SchedulingAction::ExecuteStep); + } } - // Set the new active thread. - let thread_manager = &mut this.machine.threads; - thread_manager.active_thread = next_thread_id; - assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); - return interp_ok(SchedulingAction::ExecuteStep); } // We are not in GenMC mode, so we control the scheduling. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index ce72fc6e297dc..07af4dcaad115 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -110,6 +110,7 @@ pub type StrictPointer = interpret::Pointer; pub type Scalar = interpret::Scalar; pub type ImmTy<'tcx> = interpret::ImmTy<'tcx, machine::Provenance>; pub type OpTy<'tcx> = interpret::OpTy<'tcx, machine::Provenance>; +pub type FnArg<'tcx> = interpret::FnArg<'tcx, machine::Provenance>; pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>; pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7b93d0194119a..49c2351665367 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -31,7 +31,9 @@ use rustc_target::callconv::FnAbi; use crate::alloc_addresses::EvalContextExt; use crate::concurrency::cpu_affinity::{self, CpuAffinityMask}; use crate::concurrency::data_race::{self, NaReadType, NaWriteType}; -use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory}; +use crate::concurrency::{ + AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory, +}; use crate::*; /// First real-time signal. @@ -1163,7 +1165,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, - args: &[FnArg<'tcx, Provenance>], + args: &[FnArg<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, @@ -1182,6 +1184,13 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind); } + if ecx.machine.data_race.as_genmc_ref().is_some() + && ecx.genmc_intercept_function(instance, args, dest)? + { + ecx.return_to_block(ret)?; + return interp_ok(None); + } + // Otherwise, load the MIR. let _trace = enter_trace_span!("load_mir"); interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) @@ -1192,7 +1201,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, fn_val: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, - args: &[FnArg<'tcx, Provenance>], + args: &[FnArg<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs new file mode 100644 index 0000000000000..d2da722f1c02f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs @@ -0,0 +1,28 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@error-in-other-file: Undefined Behavior + +// Test that GenMC throws an error if a `std::sync::Mutex` is unlocked from a different thread than the one that locked it. +// +// This test will cause an error on all targets, even mutexes on that targets allow for unlocking on a different thread. +// GenMC always assumes a `pthread`-like API. + +#![no_main] + +use std::sync::Mutex; + +static MUTEX: Mutex = Mutex::new(0); + +#[derive(Copy, Clone)] +struct EvilSend(pub T); +unsafe impl Send for EvilSend {} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let guard = EvilSend(MUTEX.lock().unwrap()); + let handle = std::thread::spawn(move || { + let guard = guard; // avoid field capturing + drop(guard); + }); + handle.join().unwrap(); + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr new file mode 100644 index 0000000000000..e74b76ea415e6 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr @@ -0,0 +1,86 @@ +Running GenMC Verification... +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +LL | | let guard = guard; // avoid field capturing +LL | | drop(guard); +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +LL | | let guard = guard; // avoid field capturing +LL | | drop(guard); +LL | | }); + | |______^ + +error: Undefined Behavior: Invalid unlock() operation + --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + | +LL | self.lock.inner.unlock(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::sync::MutexGuard<'_, u64>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::ptr::drop_in_place::>> - shim(Some(EvilSend>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside closure + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | drop(guard); + | ^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs new file mode 100644 index 0000000000000..3daff38efbfdd --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@error-in-other-file: Undefined Behavior + +// Test that GenMC can detect a double unlock of a mutex. +// This test will cause an error even if the program actually would work entirely fine despite the double-unlock +// because GenMC always assumes a `pthread`-like API. + +#![no_main] + +use std::sync::Mutex; + +static MUTEX: Mutex = Mutex::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut guard = MUTEX.lock().unwrap(); + unsafe { + std::ptr::drop_in_place(&raw mut guard); + } + drop(guard); + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr new file mode 100644 index 0000000000000..3ba863668f1e8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr @@ -0,0 +1,23 @@ +Running GenMC Verification... +error: Undefined Behavior: Invalid unlock() operation + --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + | +LL | self.lock.inner.unlock(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::sync::MutexGuard<'_, u64>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_double_unlock.rs:LL:CC + | +LL | drop(guard); + | ^^^^^^^^^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs new file mode 100644 index 0000000000000..df47fbfbc1676 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// Test that we can detect a deadlock involving `std::sync::Mutex` in GenMC mode. +// FIXME(genmc): We cannot detect the deadlock currently. Instead, the deadlocked execution is treated like any other blocked execution. +// This behavior matches GenMC's on an equivalent program, and additional analysis is required to detect such deadlocks. +// This should become a `fail` test once this deadlock can be detected. +// +// FIXME(genmc): use `std::thread` once GenMC mode performance is better and produces fewer warnings for compare_exchange. + +#![no_main] +#![feature(abort_unwind)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::Mutex; + +use crate::genmc::*; + +static X: Mutex = Mutex::new(0); +static Y: Mutex = Mutex::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let t0 = spawn_pthread_closure(|| { + let mut x = X.lock().unwrap(); + let mut y = Y.lock().unwrap(); + *x += 1; + *y += 1; + }); + let t1 = spawn_pthread_closure(|| { + let mut y = Y.lock().unwrap(); + let mut x = X.lock().unwrap(); + *x += 1; + *y += 1; + }); + join_pthreads([t0, t1]); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr new file mode 100644 index 0000000000000..8b3957d18c01b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 3 executions. No errors found. +Number of complete executions explored: 2 +Number of blocked executions seen: 1 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs new file mode 100644 index 0000000000000..1f8bc81d85eb5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs @@ -0,0 +1,68 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// Test various features of the `std::sync::Mutex` API with GenMC. +// Miri running with GenMC intercepts the Mutex functions `lock`, `try_lock` and `unlock`, instead of running their actual implementation. +// This interception should not break any functionality. +// +// FIXME(genmc): Once GenMC supports mixed size accesses, add stack/heap allocated Mutexes to the test. +// FIXME(genmc): Once the actual implementation of mutexes can be used in GenMC mode and there is a setting to disable Mutex interception: Add test revision without interception. +// +// Miri provides annotations to GenMC for the condition required to unblock a thread blocked on a Mutex lock call. +// This massively reduces the number of blocked executions we need to explore (in this test we require zero blocked execution). +// We use verbose output to check that this test always explores zero blocked executions. + +#![no_main] +#![feature(abort_unwind)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::Mutex; + +use crate::genmc::*; + +const REPS: u64 = 3; + +static LOCK: Mutex = Mutex::new(0); +static OTHER_LOCK: Mutex = Mutex::new(1234); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::abort_unwind(main_); + 0 +} + +fn main_() { + // Two mutexes should not interfere, holding this guard does not affect the other mutex. + let other_guard = OTHER_LOCK.lock().unwrap(); + + let guard = LOCK.lock().unwrap(); + // Trying to lock should fail if the mutex is already held. + assert!(LOCK.try_lock().is_err()); + // Dropping the guard should unlock the mutex correctly. + drop(guard); + // Trying to lock now should succeed. + assert!(LOCK.try_lock().is_ok()); + + // Spawn multiple threads interacting with the same mutex. + unsafe { + let ids = [ + spawn_pthread_closure(|| { + for _ in 0..REPS { + *LOCK.lock().unwrap() += 2; + } + }), + spawn_pthread_closure(|| { + for _ in 0..REPS { + *LOCK.lock().unwrap() += 4; + } + }), + ]; + join_pthreads(ids); + } + // Due to the Mutex, all increments should be visible in every explored execution. + assert!(*LOCK.lock().unwrap() == REPS * 6); + + drop(other_guard); +} diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr new file mode 100644 index 0000000000000..76ddd42addf9d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 20 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded123.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded123.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded321.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded321.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced123.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced123.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced321.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced321.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.rs similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.rs From 0a3f1d5b9843c5e911a90ed9eb3010c29a407c66 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 7 Oct 2025 15:58:20 +0200 Subject: [PATCH 1361/1900] c-variadic: fix thir-print for `...` without a pattern --- compiler/rustc_mir_build/src/thir/print.rs | 4 +- tests/ui/thir-print/c-variadic.rs | 6 +++ tests/ui/thir-print/c-variadic.stdout | 61 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/ui/thir-print/c-variadic.rs create mode 100644 tests/ui/thir-print/c-variadic.stdout diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index b7e8d6fea40bb..188f179669cb4 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -689,7 +689,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "kind: PatKind {", depth_lvl); match pat_kind { - PatKind::Missing => unreachable!(), + PatKind::Missing => { + print_indented!(self, "Missing", depth_lvl + 1); + } PatKind::Wild => { print_indented!(self, "Wild", depth_lvl + 1); } diff --git a/tests/ui/thir-print/c-variadic.rs b/tests/ui/thir-print/c-variadic.rs new file mode 100644 index 0000000000000..e03c4b0838582 --- /dev/null +++ b/tests/ui/thir-print/c-variadic.rs @@ -0,0 +1,6 @@ +//@ compile-flags: -Zunpretty=thir-tree --crate-type=lib +//@ check-pass +#![feature(c_variadic)] + +// The `...` argument uses `PatKind::Missing`. +unsafe extern "C" fn foo(_: i32, ...) {} diff --git a/tests/ui/thir-print/c-variadic.stdout b/tests/ui/thir-print/c-variadic.stdout new file mode 100644 index 0000000000000..8363f06ebe887 --- /dev/null +++ b/tests/ui/thir-print/c-variadic.stdout @@ -0,0 +1,61 @@ +DefId(0:3 ~ c_variadic[a5de]::foo): +params: [ + Param { + ty: i32 + ty_span: Some($DIR/c-variadic.rs:6:29: 6:32 (#0)) + self_kind: None + hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).1)) + param: Some( + Pat: { + ty: i32 + span: $DIR/c-variadic.rs:6:26: 6:27 (#0) + kind: PatKind { + Wild + } + } + ) + } + Param { + ty: std::ffi::VaListImpl<'{erased}> + ty_span: None + self_kind: None + hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).3)) + param: Some( + Pat: { + ty: std::ffi::VaListImpl<'{erased}> + span: $DIR/c-variadic.rs:6:34: 6:37 (#0) + kind: PatKind { + Missing + } + } + ) + } +] +body: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(6)), backwards_incompatible: None } + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + kind: + Scope { + region_scope: Node(6) + lint_level: Explicit(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).6)) + value: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(6)), backwards_incompatible: None } + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + kind: + Block { + targeted_by_break: false + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + region_scope: Node(5) + safety_mode: Safe + stmts: [] + expr: [] + } + } + } + } + + From e98856b6bbb4b11fbb6755a80d9a24249172ff78 Mon Sep 17 00:00:00 2001 From: Arthur Carcano Date: Tue, 7 Oct 2025 15:49:27 +0200 Subject: [PATCH 1362/1900] Only allocate new string if there are DOS backlines --- src/librustdoc/html/highlight.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index eeb916494d1e7..f2055608aa9d2 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -408,7 +408,13 @@ pub(super) fn write_code( line_info: Option, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. - let src = src.replace("\r\n", "\n"); + let src = + // The first "\r\n" should be fairly close to the beginning of the string relatively + // to its overall length, and most strings handled by rustdoc likely don't have + // DOS backlines anyway. + // Checking for the single ASCII character '\r' is much more efficient than checking for + // the whole string "\r\n". + if src.contains('\r') { src.replace("\r\n", "\n").into() } else { Cow::Borrowed(src) }; let mut token_handler = TokenHandler { out, closing_tags: Vec::new(), From eb20c6abd6b7282c3636c97870ae4e8c50d76632 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Tue, 7 Oct 2025 11:02:13 -0400 Subject: [PATCH 1363/1900] PassWrapper: use non-deprecated lookupTarget method This avoids an extra trip through a triple string by directly passing the Triple, and has been available since LLVM 21. The string overload was deprecated today and throws an error on our CI for HEAD due to -Werror paranoia, so we may as well clean this up now and also skip the conversion on LLVM 21 since we can. @rustbot label llvm-main --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 2e9fd6754f124..8d9c4b48b5c4a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -283,7 +283,11 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( std::string Error; auto Trip = Triple(Triple::normalize(TripleStr)); const llvm::Target *TheTarget = +#if LLVM_VERSION_GE(21, 0) + TargetRegistry::lookupTarget(Trip, Error); +#else TargetRegistry::lookupTarget(Trip.getTriple(), Error); +#endif if (TheTarget == nullptr) { LLVMRustSetLastError(Error.c_str()); return nullptr; From a914f827c8c50866f97627863eb6fa4702f44541 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 7 Oct 2025 17:09:02 +0200 Subject: [PATCH 1364/1900] Set the minimum deployment target for aarch64-apple-watchos To match what's done in LLVM 21. --- compiler/rustc_target/src/spec/base/apple/mod.rs | 5 ++++- tests/run-make/apple-deployment-target/rmake.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 39e604bcce7ce..c32e333eb8b02 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -304,7 +304,7 @@ impl OSVersion { /// to raise the minimum OS version. /// /// This matches what LLVM does, see in part: - /// + /// pub fn minimum_deployment_target(target: &Target) -> Self { let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.env) { ("macos", "aarch64", _) => (11, 0, 0), @@ -315,6 +315,9 @@ impl OSVersion { ("ios", _, "macabi") => (13, 1, 0), ("tvos", "aarch64", "sim") => (14, 0, 0), ("watchos", "aarch64", "sim") => (7, 0, 0), + // True Aarch64 on watchOS (instead of their Aarch64 Ilp32 called `arm64_32`) has been + // available since Xcode 14, but it's only actually used more recently in watchOS 26. + ("watchos", "aarch64", "") if !target.llvm_target.starts_with("arm64_32") => (26, 0, 0), (os, _, _) => return Self::os_minimum_deployment_target(os), }; Self { major, minor, patch } diff --git a/tests/run-make/apple-deployment-target/rmake.rs b/tests/run-make/apple-deployment-target/rmake.rs index 7297a8622240d..5d4512843d534 100644 --- a/tests/run-make/apple-deployment-target/rmake.rs +++ b/tests/run-make/apple-deployment-target/rmake.rs @@ -33,6 +33,7 @@ fn main() { // armv7s-apple-ios and i386-apple-ios only supports iOS 10.0 "ios" if target() == "armv7s-apple-ios" || target() == "i386-apple-ios" => ("10.0", "10.0"), "ios" => ("15.0", "16.0"), + "watchos" if target() == "aarch64-apple-watchos" => ("28.0", "30.0"), "watchos" => ("7.0", "9.0"), "tvos" => ("14.0", "15.0"), "visionos" => ("1.1", "1.2"), From f7c9b30b1b74a0f8fc70aa6c947966aec03e3b0b Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Tue, 7 Oct 2025 11:20:36 -0400 Subject: [PATCH 1365/1900] collect-license-metadata: update submodules before running Previously, this could cause incorrect failures like the following: ``` diff --git a/license-metadata.json b/license-metadata.json index 4fb59210854..b1291c00b94 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -244,19 +172,6 @@ }, "name": "src/doc/rustc-dev-guide/mermaid.min.js", "type": "file" - }, - { - "children": [], - "license": { - "copyright": [ - "2003-2019 University of Illinois at Urbana-Champaign", - "2003-2019 by the contributors listed in CREDITS.TXT (https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", - "2010 Apple Inc" - ], - "spdx": "Apache-2.0 WITH LLVM-exception AND NCSA" - }, - "name": "src/llvm-project", - "type": "directory" ``` Additionally, this prints a warning if there were untracked files, which could cause a failure like the following: ``` diff --git a/license-metadata.json b/license-metadata.json index 4fb59210854..ebf1c478282 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -155,6 +155,22 @@ "name": "src/librustdoc/html/static/fonts", "type": "directory" }, + { + "directories": [], + "files": [ + "2015-edition.txt", + "diagnostics.json", + "license.diff", + "test.fixed.rs" + ], + "license": { + "copyright": [ + "NONE" + ], + "spdx": "NONE" + }, + "type": "group" + }, { "license": { "copyright": [ ``` --- src/bootstrap/src/core/build_steps/run.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 9f7248b80f763..b9a4c1bf9b44b 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use build_helper::exit; +use build_helper::git::get_git_untracked_files; use clap_complete::{Generator, shells}; use crate::core::build_steps::dist::distdir; @@ -208,6 +209,16 @@ impl Step for CollectLicenseMetadata { let dest = builder.src.join("license-metadata.json"); + if !builder.config.dry_run() { + builder.require_and_update_all_submodules(); + if let Ok(Some(untracked)) = get_git_untracked_files(None) { + eprintln!( + "Warning: {} untracked files may cause the license report to be incorrect.", + untracked.len() + ); + } + } + let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata); cmd.env("REUSE_EXE", reuse); cmd.env("DEST", &dest); From fbdc685ed09f8315f583f9ba846c056b3ed810a4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 1 Oct 2025 21:14:27 +0200 Subject: [PATCH 1366/1900] cmse: disallow `impl Trait` in `cmse-nonsecure-entry` return types --- .../src/hir_ty_lowering/cmse.rs | 18 +++++++++-- .../cmse-nonsecure-call/generics.rs | 3 +- .../cmse-nonsecure-call/generics.stderr | 24 ++++++++++----- .../cmse-nonsecure-entry/generics.rs | 12 ++++++++ .../cmse-nonsecure-entry/generics.stderr | 30 ++++++++++++++++++- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 378771c05ee33..91d1f89b74477 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -3,7 +3,7 @@ use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err}; use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use crate::errors; @@ -165,9 +165,23 @@ fn is_valid_cmse_output<'tcx>( // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); + let return_type = fn_sig.output(); + + // `impl Trait` is already disallowed with `cmse-nonsecure-call`, because that ABI is only + // allowed on function pointers, and function pointers cannot contain `impl Trait` in their + // signature. + // + // Here we explicitly disallow `impl Trait` in the `cmse-nonsecure-entry` return type too, to + // prevent query cycles when calculating the layout. This ABI is meant to be used with + // `#[no_mangle]` or similar, so generics in the type really don't make sense. + // + // see also https://github.com/rust-lang/rust/issues/147242. + if return_type.has_opaque_types() { + return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type))); + } let typing_env = ty::TypingEnv::fully_monomorphized(); - let layout = tcx.layout_of(typing_env.as_query_input(fn_sig.output()))?; + let layout = tcx.layout_of(typing_env.as_query_input(return_type))?; Ok(is_valid_cmse_output_layout(layout)) } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs index 4ce5890a2da30..2dc540675e2e6 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -14,8 +14,9 @@ struct Test { f1: extern "cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, //~^ ERROR cannot find type `U` in this scope //~| ERROR function pointer types may not have generic parameters - f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, + f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters + //~| ERROR `impl Trait` is not allowed in `fn` pointer return types f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index 156568535763b..750dbf8d5707c 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -25,25 +25,33 @@ LL | struct Test { error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters --> $DIR/generics.rs:17:41 | -LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, +LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, | ^^^^^^^^^ | = note: `impl Trait` is only allowed in arguments and return types of functions and methods +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types + --> $DIR/generics.rs:17:70 + | +LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:19:9 + --> $DIR/generics.rs:20:9 | LL | f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:20:9 + --> $DIR/generics.rs:21:9 | LL | f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:26:71 + --> $DIR/generics.rs:27:71 | LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait; | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -52,7 +60,7 @@ LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:30:60 + --> $DIR/generics.rs:31:60 | LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -61,7 +69,7 @@ LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Tra = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:37:60 + --> $DIR/generics.rs:38:60 | LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -70,12 +78,12 @@ LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspare = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0045]: C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported - --> $DIR/generics.rs:40:20 + --> $DIR/generics.rs:41:20 | LL | type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0045, E0412, E0562, E0798. For more information about an error, try `rustc --explain E0045`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index aee01e25ddc4f..116f0fdc972fc 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -65,3 +65,15 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } + +extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + 0u128 +} + +extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + v +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index df1a910704378..4158fef4553a4 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -16,6 +16,34 @@ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain gen LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:69:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:69:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:75:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:75:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:14:5 | @@ -61,6 +89,6 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error: aborting due to 7 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0798`. From 517ef604af21c2225ff32335f964cc806d6be8b5 Mon Sep 17 00:00:00 2001 From: +merlan #flirora Date: Tue, 7 Oct 2025 11:18:03 -0400 Subject: [PATCH 1367/1900] Add `replace_box` lint --- CHANGELOG.md | 373 ++++++++++++++++++++--------- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/replace_box.rs | 130 ++++++++++ tests/ui/replace_box.fixed | 72 ++++++ tests/ui/replace_box.rs | 72 ++++++ tests/ui/replace_box.stderr | 52 ++++ 7 files changed, 595 insertions(+), 107 deletions(-) create mode 100644 clippy_lints/src/replace_box.rs create mode 100644 tests/ui/replace_box.fixed create mode 100644 tests/ui/replace_box.rs create mode 100644 tests/ui/replace_box.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b374e26c962..89e3bd336c5a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,19 +28,19 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Enhancements -* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods - [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) - [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) -* [`incompatible_msrv`] now recognizes types exceeding MSRV +* [`incompatible_msrv`] now recognizes types exceeding MSRV [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) -* [`incompatible_msrv`] now checks the right MSRV when in a `const` context +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) -* [`zero_ptr`] now lints in `const` context as well +* [`zero_ptr`] now lints in `const` context as well [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) * [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) -* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) ### False Positive Fixes @@ -51,7 +51,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) * [`unused_async`] fixed FP on function with `todo!` [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) -* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes on `use` statements [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) * [`pattern_type_mismatch`] fixed FP in external macro @@ -64,7 +64,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) * [`ptr_as_ptr`] fixed wrong suggestions with turbo fish [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) -* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe to switch the range type [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) * [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument @@ -129,7 +129,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) * [`collapsible_else_if`] fixed FP on conditionally compiled stmt [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) -* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing non-`Sized` trait bounds [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) * [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` @@ -137,7 +137,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### ICE Fixes -* [`single_match`] fixed ICE with deref patterns and string literals +* [`single_match`] fixed ICE with deref patterns and string literals [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) * [`needless_doctest_main`] fixed panic when doctest is invalid [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) @@ -146,11 +146,11 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Documentation Improvements -* [`manual_is_variant_and`] improved documentation to include equality comparison patterns +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) * [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) -* [`undocumented_unsafe_blocks`] improved documentation wording +* [`undocumented_unsafe_blocks`] improved documentation wording [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -292,7 +292,7 @@ Current stable, released 2025-06-26 [#14408](https://github.com/rust-lang/rust-clippy/pull/14408) * [`iter_kv_map`] now recognizes references on maps [#14596](https://github.com/rust-lang/rust-clippy/pull/14596) -* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used +* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971) * [`needless_lifetimes`] now checks for lifetime uses in closures [#14608](https://github.com/rust-lang/rust-clippy/pull/14608) @@ -928,50 +928,50 @@ Released 2024-02-08 ### New Lints -- [`infinite_loop`] +* [`infinite_loop`] [#11829](https://github.com/rust-lang/rust-clippy/pull/11829) -- [`ineffective_open_options`] +* [`ineffective_open_options`] [#11902](https://github.com/rust-lang/rust-clippy/pull/11902) -- [`uninhabited_references`] +* [`uninhabited_references`] [#11878](https://github.com/rust-lang/rust-clippy/pull/11878) -- [`repeat_vec_with_capacity`] +* [`repeat_vec_with_capacity`] [#11597](https://github.com/rust-lang/rust-clippy/pull/11597) -- [`test_attr_in_doctest`] +* [`test_attr_in_doctest`] [#11872](https://github.com/rust-lang/rust-clippy/pull/11872) -- [`option_map_or_err_ok`] +* [`option_map_or_err_ok`] [#11864](https://github.com/rust-lang/rust-clippy/pull/11864) -- [`join_absolute_paths`] +* [`join_absolute_paths`] [#11453](https://github.com/rust-lang/rust-clippy/pull/11453) -- [`impl_hash_borrow_with_str_and_bytes`] +* [`impl_hash_borrow_with_str_and_bytes`] [#11781](https://github.com/rust-lang/rust-clippy/pull/11781) -- [`iter_over_hash_type`] +* [`iter_over_hash_type`] [#11791](https://github.com/rust-lang/rust-clippy/pull/11791) ### Moves and Deprecations -- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] +* Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] [#11853](https://github.com/rust-lang/rust-clippy/pull/11853) -- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) +* Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) [#11867](https://github.com/rust-lang/rust-clippy/pull/11867) -- Moved [`if_same_then_else`] to `style` (Now warn-by-default) +* Moved [`if_same_then_else`] to `style` (Now warn-by-default) [#11809](https://github.com/rust-lang/rust-clippy/pull/11809) ### Enhancements -- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: +* [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: Added the [`check-private-items`] configuration to enable lints on private items [#11842](https://github.com/rust-lang/rust-clippy/pull/11842) ### ICE Fixes -- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters +* [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters [#11804](https://github.com/rust-lang/rust-clippy/pull/11804) -- [`unused_enumerate_index`]: No longer crashes on empty tuples +* [`unused_enumerate_index`]: No longer crashes on empty tuples [#11756](https://github.com/rust-lang/rust-clippy/pull/11756) ### Others -- Clippy now respects the `CARGO` environment value +* Clippy now respects the `CARGO` environment value [#11944](https://github.com/rust-lang/rust-clippy/pull/11944) ## Rust 1.75 @@ -997,7 +997,6 @@ Released 2023-12-28 * [`manual_hash_one`] [#11556](https://github.com/rust-lang/rust-clippy/pull/11556) - ### Moves and Deprecations * Moved [`read_zero_byte_vec`] to `nursery` (Now allow-by-default) @@ -1073,7 +1072,7 @@ Released 2023-11-16 ### Enhancements -* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and +* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and [`accept-comment-above-attributes`] are now `true` by default [#11170](https://github.com/rust-lang/rust-clippy/pull/11170) * [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default @@ -2254,7 +2253,6 @@ Released 2022-09-22 * [`explicit_auto_deref`] [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) - ### Moves and Deprecations * Moved [`format_push_string`] to `restriction` (now allow-by-default) @@ -2455,10 +2453,10 @@ Released 2022-08-11 * [`redundant_allocation`]: No longer lints on fat pointers that would become thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813) * [`derive_partial_eq_without_eq`]: - * Handle differing predicates applied by `#[derive(PartialEq)]` and + * Handle differing predicates applied by `#[derive(PartialEq)]` and `#[derive(Eq)]` [#8869](https://github.com/rust-lang/rust-clippy/pull/8869) - * No longer lints on non-public types and better handles generics + * No longer lints on non-public types and better handles generics [#8950](https://github.com/rust-lang/rust-clippy/pull/8950) * [`empty_line_after_outer_attr`]: No longer lints empty lines in inner string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892) @@ -2952,12 +2950,12 @@ Released 2022-02-24 [#7957](https://github.com/rust-lang/rust-clippy/pull/7957) * [`needless_borrow`] [#7977](https://github.com/rust-lang/rust-clippy/pull/7977) - * Lint when a borrow is auto-dereffed more than once - * Lint in the trailing expression of a block for a match arm + * Lint when a borrow is auto-dereffed more than once + * Lint in the trailing expression of a block for a match arm * [`strlen_on_c_strings`] [8001](https://github.com/rust-lang/rust-clippy/pull/8001) - * Lint when used without a fully-qualified path - * Suggest removing the surrounding unsafe block when possible + * Lint when used without a fully-qualified path + * Suggest removing the surrounding unsafe block when possible * [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s [#8034](https://github.com/rust-lang/rust-clippy/pull/8034) * [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`, @@ -3068,7 +3066,7 @@ Released 2022-02-24 [#7813](https://github.com/rust-lang/rust-clippy/pull/7813) * New and improved issue templates [#8032](https://github.com/rust-lang/rust-clippy/pull/8032) -* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a +* *Dev:* Add `cargo dev lint` command, to run your modified Clippy version on a file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917) ## Rust 1.58 @@ -3278,15 +3276,15 @@ Released 2021-12-02 [#7566](https://github.com/rust-lang/rust-clippy/pull/7566) * [`option_if_let_else`]: Multiple fixes [#7573](https://github.com/rust-lang/rust-clippy/pull/7573) - * `break` and `continue` statements local to the would-be closure are + * `break` and `continue` statements local to the would-be closure are allowed - * Don't lint in const contexts - * Don't lint when yield expressions are used - * Don't lint when the captures made by the would-be closure conflict with + * Don't lint in const contexts + * Don't lint when yield expressions are used + * Don't lint when the captures made by the would-be closure conflict with the other branch - * Don't lint when a field of a local is used when the type could be + * Don't lint when a field of a local is used when the type could be potentially moved from - * In some cases, don't lint when scrutinee expression conflicts with the + * In some cases, don't lint when scrutinee expression conflicts with the captures of the would-be closure * [`redundant_allocation`]: No longer lints on `Box>` which replaces wide pointers with thin pointers @@ -3535,124 +3533,124 @@ Released 2021-07-29 ### New Lints -- [`ref_binding_to_reference`] +* [`ref_binding_to_reference`] [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`needless_bitwise_bool`] +* [`needless_bitwise_bool`] [#7133](https://github.com/rust-lang/rust-clippy/pull/7133) -- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) -- [`manual_str_repeat`] +* [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) +* [`manual_str_repeat`] [#7265](https://github.com/rust-lang/rust-clippy/pull/7265) -- [`suspicious_splitn`] +* [`suspicious_splitn`] [#7292](https://github.com/rust-lang/rust-clippy/pull/7292) ### Moves and Deprecations -- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of +* Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of the new `avoid-breaking-exported-api` config option (see [Enhancements](#1-54-enhancements)) [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- Move [`inconsistent_struct_constructor`] to `pedantic` +* Move [`inconsistent_struct_constructor`] to `pedantic` [#7193](https://github.com/rust-lang/rust-clippy/pull/7193) -- Move [`needless_borrow`] to `style` (now warn-by-default) +* Move [`needless_borrow`] to `style` (now warn-by-default) [#7254](https://github.com/rust-lang/rust-clippy/pull/7254) -- Move [`suspicious_operation_groupings`] to `nursery` +* Move [`suspicious_operation_groupings`] to `nursery` [#7266](https://github.com/rust-lang/rust-clippy/pull/7266) -- Move [`semicolon_if_nothing_returned`] to `pedantic` +* Move [`semicolon_if_nothing_returned`] to `pedantic` [#7268](https://github.com/rust-lang/rust-clippy/pull/7268) ### Enhancements -- [`while_let_on_iterator`]: Now also lints in nested loops +* [`while_let_on_iterator`]: Now also lints in nested loops [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` +* [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` [#7156](https://github.com/rust-lang/rust-clippy/pull/7156) -- [`needless_collect`]: Now also lints on assignments with type annotations +* [`needless_collect`]: Now also lints on assignments with type annotations [#7163](https://github.com/rust-lang/rust-clippy/pull/7163) -- [`if_then_some_else_none`]: Now works with the MSRV config +* [`if_then_some_else_none`]: Now works with the MSRV config [#7177](https://github.com/rust-lang/rust-clippy/pull/7177) -- Add `avoid-breaking-exported-api` config option for the lints +* Add `avoid-breaking-exported-api` config option for the lints [`enum_variant_names`], [`large_types_passed_by_value`], [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`], [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set this configuration option to `false` before a major release (1.0/2.0/...) to clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- [`needless_collect`]: Now lints on even more data structures +* [`needless_collect`]: Now lints on even more data structures [#7188](https://github.com/rust-lang/rust-clippy/pull/7188) -- [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like +* [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like attributes as sufficient documentation [#7281](https://github.com/rust-lang/rust-clippy/pull/7281) -- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: +* [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: Now work as expected when used with `allow` [#7282](https://github.com/rust-lang/rust-clippy/pull/7282) ### False Positive Fixes -- [`implicit_return`]: Now takes all diverging functions in account to avoid +* [`implicit_return`]: Now takes all diverging functions in account to avoid false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) -- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field +* [`while_let_on_iterator`]: No longer lints when the iterator is a struct field and the struct is used in the loop [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`multiple_inherent_impl`]: No longer lints with generic arguments +* [`multiple_inherent_impl`]: No longer lints with generic arguments [#7089](https://github.com/rust-lang/rust-clippy/pull/7089) -- [`comparison_chain`]: No longer lints in a `const` context +* [`comparison_chain`]: No longer lints in a `const` context [#7118](https://github.com/rust-lang/rust-clippy/pull/7118) -- [`while_immutable_condition`]: Fix false positive where mutation in the loop +* [`while_immutable_condition`]: Fix false positive where mutation in the loop variable wasn't picked up [#7144](https://github.com/rust-lang/rust-clippy/pull/7144) -- [`default_trait_access`]: No longer lints in macros +* [`default_trait_access`]: No longer lints in macros [#7150](https://github.com/rust-lang/rust-clippy/pull/7150) -- [`needless_question_mark`]: No longer lints when the inner value is implicitly +* [`needless_question_mark`]: No longer lints when the inner value is implicitly dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165) -- [`unused_unit`]: No longer lints when multiple macro contexts are involved +* [`unused_unit`]: No longer lints when multiple macro contexts are involved [#7167](https://github.com/rust-lang/rust-clippy/pull/7167) -- [`eval_order_dependence`]: Fix false positive in async context +* [`eval_order_dependence`]: Fix false positive in async context [#7174](https://github.com/rust-lang/rust-clippy/pull/7174) -- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the +* [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175) -- [`wrong_self_convention`]: No longer lints in trait implementations of +* [`wrong_self_convention`]: No longer lints in trait implementations of non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182) -- [`suboptimal_flops`]: No longer lints on `powi(2)` +* [`suboptimal_flops`]: No longer lints on `powi(2)` [#7201](https://github.com/rust-lang/rust-clippy/pull/7201) -- [`wrong_self_convention`]: No longer lints if there is no implicit `self` +* [`wrong_self_convention`]: No longer lints if there is no implicit `self` [#7215](https://github.com/rust-lang/rust-clippy/pull/7215) -- [`option_if_let_else`]: No longer lints on `else if let` pattern +* [`option_if_let_else`]: No longer lints on `else if let` pattern [#7216](https://github.com/rust-lang/rust-clippy/pull/7216) -- [`use_self`], [`useless_conversion`]: Fix false positives when generic +* [`use_self`], [`useless_conversion`]: Fix false positives when generic arguments are involved [#7223](https://github.com/rust-lang/rust-clippy/pull/7223) -- [`manual_unwrap_or`]: Fix false positive with deref coercion +* [`manual_unwrap_or`]: Fix false positive with deref coercion [#7233](https://github.com/rust-lang/rust-clippy/pull/7233) -- [`similar_names`]: No longer lints on `wparam`/`lparam` +* [`similar_names`]: No longer lints on `wparam`/`lparam` [#7255](https://github.com/rust-lang/rust-clippy/pull/7255) -- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a +* [`redundant_closure`]: No longer lints on using the `vec![]` macro in a closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263) ### Suggestion Fixes/Improvements -- [`implicit_return`] +* [`implicit_return`] [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) - - Fix suggestion for async functions - - Improve suggestion with macros - - Suggest to change `break` to `return` when appropriate -- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary + * Fix suggestion for async functions + * Improve suggestion with macros + * Suggest to change `break` to `return` when appropriate +* [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`match_single_binding`]: Improve suggestion when match scrutinee has side +* [`match_single_binding`]: Improve suggestion when match scrutinee has side effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095) -- [`needless_borrow`]: Now suggests to also change usage sites as needed +* [`needless_borrow`]: Now suggests to also change usage sites as needed [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`write_with_newline`]: Improve suggestion when only `\n` is written to the +* [`write_with_newline`]: Improve suggestion when only `\n` is written to the buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183) -- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also +* [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also when a `<_ as Trait>::_` is involved [#7264](https://github.com/rust-lang/rust-clippy/pull/7264) -- [`not_unsafe_ptr_arg_deref`]: Improved error message +* [`not_unsafe_ptr_arg_deref`]: Improved error message [#7294](https://github.com/rust-lang/rust-clippy/pull/7294) ### ICE Fixes -- Fix ICE when running Clippy on `libstd` +* Fix ICE when running Clippy on `libstd` [#7140](https://github.com/rust-lang/rust-clippy/pull/7140) -- [`implicit_return`] +* [`implicit_return`] [#7242](https://github.com/rust-lang/rust-clippy/pull/7242) ## Rust 1.53 @@ -3697,9 +3695,9 @@ Released 2021-06-17 [#6828](https://github.com/rust-lang/rust-clippy/pull/6828) * [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: [#6863](https://github.com/rust-lang/rust-clippy/pull/6863) - * Attempt to find a common path prefix in suggestion - * Don't lint on `Option` and `Result` - * Consider `Self` prefix + * Attempt to find a common path prefix in suggestion + * Don't lint on `Option` and `Result` + * Consider `Self` prefix * [`explicit_deref_methods`]: Also lint on chained `deref` calls [#6865](https://github.com/rust-lang/rust-clippy/pull/6865) * [`or_fun_call`]: Also lint on `unsafe` blocks @@ -3959,6 +3957,7 @@ Released 2021-05-06 [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) ### Others + * Running `cargo clippy` after `cargo check` now works as expected (`cargo clippy` and `cargo check` no longer shares the same build cache) [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) @@ -4174,7 +4173,6 @@ Released 2021-02-11 * [`field_reassign_with_default`] No longer lint for private fields [#6537](https://github.com/rust-lang/rust-clippy/pull/6537) - ### Suggestion Fixes/Improvements * [`vec_box`]: Provide correct type scope suggestion @@ -4319,8 +4317,8 @@ Released 2020-12-31 ### Documentation Improvements * Some doc improvements: - * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) - * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) + * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) + * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) * [`doc_markdown`]: Document problematic link text style [#6107](https://github.com/rust-lang/rust-clippy/pull/6107) @@ -4703,7 +4701,6 @@ Released 2020-06-04 * [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) * [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) - ### Moves and Deprecations * Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380) @@ -4718,7 +4715,7 @@ Released 2020-06-04 ### Enhancements -* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to +* On *nightly* you can now use `cargo clippy --fix -Z unstable-options` to auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363) * Make [`redundant_clone`] also trigger on cases where the cloned value is not consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304) @@ -4745,7 +4742,6 @@ Released 2020-06-04 * [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287) * [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451) - ### Suggestion Improvements * Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481) @@ -4823,7 +4819,6 @@ Released 2020-04-23 * Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190) - ## Rust 1.42 Released 2020-03-12 @@ -4890,7 +4885,6 @@ Released 2020-03-12 * Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`] - ## Rust 1.41 Released 2020-01-30 @@ -5106,7 +5100,6 @@ Released 2019-07-04 * Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960) * Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931) - ## Rust 1.35 Released 2019-05-20 @@ -5129,7 +5122,7 @@ Released 2019-05-20 * Fix false positive in [`needless_continue`] pertaining to loop labels * Fix false positive for [`boxed_local`] pertaining to arguments moved into closures * Fix false positive for [`use_self`] in nested functions -* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846) +* Fix suggestion for [`expect_fun_call`] () * Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables * Fix suggestion for [`single_char_pattern`] to correctly escape single quotes * Avoid triggering [`redundant_closure`] in macros @@ -5273,6 +5266,7 @@ Released 2018-12-06 Released 2018-10-25 [View all 88 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-08-02T16%3A54%3A12Z..2018-09-17T09%3A44%3A06Z+base%3Amaster) + * Deprecate `assign_ops` lint * New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], [`needless_collect`], [`copy_iterator`] @@ -5322,255 +5316,326 @@ Released 2018-09-13 * Improve website header ## 0.0.212 (2018-07-10) + * Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)* ## 0.0.211 + * Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)* ## 0.0.210 + * Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)* ## 0.0.209 + * Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)* ## 0.0.208 + * Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)* ## 0.0.207 + * Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)* ## 0.0.206 + * Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)* ## 0.0.205 + * Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)* * Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint ## 0.0.204 + * Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)* ## 0.0.203 + * Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)* * Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)` ## 0.0.202 + * Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)* ## 0.0.201 + * Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)* ## 0.0.200 + * Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)* ## 0.0.199 + * Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)* ## 0.0.198 + * Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)* ## 0.0.197 + * Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)* ## 0.0.196 + * Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)* ## 0.0.195 + * Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)* ## 0.0.194 + * Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)* * New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`] ## 0.0.193 + * Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)* ## 0.0.192 + * Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)* * New lint: [`print_literal`] ## 0.0.191 + * Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)* * Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction. ## 0.0.190 + * Fix a bunch of intermittent cargo bugs ## 0.0.189 + * Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)* ## 0.0.188 + * Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)* * New lint: [`while_immutable_condition`] ## 0.0.187 + * Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)* * New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`] ## 0.0.186 + * Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)* * Various false positive fixes ## 0.0.185 + * Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)* * New lint: [`question_mark`] ## 0.0.184 + * Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)* * New lints: [`double_comparisons`], [`empty_line_after_outer_attr`] ## 0.0.183 + * Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)* * New lint: [`misaligned_transmute`] ## 0.0.182 + * Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)* * New lint: [`decimal_literal_representation`] ## 0.0.181 + * Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)* * New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`] * Removed `unit_expr` * Various false positive fixes for [`needless_pass_by_value`] ## 0.0.180 + * Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)* ## 0.0.179 + * Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)* ## 0.0.178 + * Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)* ## 0.0.177 + * Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)* * New lint: [`match_as_ref`] ## 0.0.176 + * Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)* ## 0.0.175 + * Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)* ## 0.0.174 + * Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)* ## 0.0.173 + * Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)* ## 0.0.172 + * Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)* ## 0.0.171 + * Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)* ## 0.0.170 + * Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)* ## 0.0.169 + * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* * New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 + * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* ## 0.0.167 + * Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)* * New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`] ## 0.0.166 + * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* * New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] ## 0.0.165 + * Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27) * New lint: [`mut_range_bound`] ## 0.0.164 + * Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)* * New lint: [`int_plus_one`] ## 0.0.163 + * Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)* ## 0.0.162 + * Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)* * New lint: [`chars_last_cmp`] * Improved suggestions for [`needless_borrow`], [`ptr_arg`], ## 0.0.161 + * Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)* ## 0.0.160 + * Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)* ## 0.0.159 + * Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)* * New lint: [`clone_on_ref_ptr`] ## 0.0.158 + * New lint: [`manual_memcpy`] * [`cast_lossless`] no longer has redundant parentheses in its suggestions * Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)* ## 0.0.157 - 2017-09-04 + * Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* * New lint: `unit_expr` ## 0.0.156 - 2017-09-03 + * Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* ## 0.0.155 + * Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)* * New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`] ## 0.0.154 + * Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)* * Fix [`use_self`] triggering inside derives * Add support for linting an entire workspace with `cargo clippy --all` * New lint: [`naive_bytecount`] ## 0.0.153 + * Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)* * New lint: [`use_self`] ## 0.0.152 + * Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)* ## 0.0.151 + * Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)* ## 0.0.150 + * Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)* ## 0.0.148 + * Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)* * New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`] ## 0.0.147 + * Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)* ## 0.0.146 + * Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)* * Fixes false positives in `inline_always` * Fixes false negatives in `panic_params` ## 0.0.145 + * Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)* ## 0.0.144 + * Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)* ## 0.0.143 + * Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)* * Fix `cargo clippy` crashing on `dylib` projects * Fix false positives around `nested_while_let` and `never_loop` ## 0.0.142 + * Update to *rustc 1.20.0-nightly (067971139 2017-07-02)* ## 0.0.141 + * Rewrite of the `doc_markdown` lint. * Deprecated [`range_step_by_zero`] * New lint: [`iterator_step_by_zero`] @@ -5578,151 +5643,195 @@ Released 2018-09-13 * Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)* ## 0.0.140 - 2017-06-16 + * Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)* ## 0.0.139 — 2017-06-10 + * Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)* * Fix bugs with for loop desugaring * Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`] ## 0.0.138 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)* ## 0.0.137 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)* ## 0.0.136 — 2017—05—26 + * Update to *rustc 1.19.0-nightly (557967766 2017-05-26)* ## 0.0.135 — 2017—05—24 + * Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)* ## 0.0.134 — 2017—05—19 + * Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)* ## 0.0.133 — 2017—05—14 + * Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)* ## 0.0.132 — 2017—05—05 + * Fix various bugs and some ices ## 0.0.131 — 2017—05—04 + * Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)* ## 0.0.130 — 2017—05—03 + * Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)* ## 0.0.129 — 2017-05-01 + * Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)* ## 0.0.128 — 2017-04-28 + * Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)* ## 0.0.127 — 2017-04-27 + * Update to *rustc 1.18.0-nightly (036983201 2017-04-26)* * New lint: [`needless_continue`] ## 0.0.126 — 2017-04-24 + * Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)* ## 0.0.125 — 2017-04-19 + * Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)* ## 0.0.124 — 2017-04-16 + * Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)* ## 0.0.123 — 2017-04-07 + * Fix various false positives ## 0.0.122 — 2017-04-07 + * Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)* * New lint: [`op_ref`] ## 0.0.121 — 2017-03-21 + * Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)* ## 0.0.120 — 2017-03-17 + * Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)* ## 0.0.119 — 2017-03-13 + * Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)* ## 0.0.118 — 2017-03-05 + * Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)* ## 0.0.117 — 2017-03-01 + * Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)* ## 0.0.116 — 2017-02-28 + * Fix `cargo clippy` on 64 bit windows systems ## 0.0.115 — 2017-02-27 + * Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* * New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] ## 0.0.114 — 2017-02-08 + * Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)* * Tests are now ui tests (testing the exact output of rustc) ## 0.0.113 — 2017-02-04 + * Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)* * New lint: [`large_enum_variant`] * `explicit_into_iter_loop` provides suggestions ## 0.0.112 — 2017-01-27 + * Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)* ## 0.0.111 — 2017-01-21 + * Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)* ## 0.0.110 — 2017-01-20 + * Add badges and categories to `Cargo.toml` ## 0.0.109 — 2017-01-19 + * Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)* ## 0.0.108 — 2017-01-12 + * Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)* ## 0.0.107 — 2017-01-11 + * Update regex dependency * Fix FP when matching `&&mut` by `&ref` * Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()` * New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`] ## 0.0.106 — 2017-01-04 + * Fix FP introduced by rustup in [`wrong_self_convention`] ## 0.0.105 — 2017-01-04 + * Update to *rustc 1.16.0-nightly (468227129 2017-01-03)* * New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`] * Fix suggestion in [`new_without_default`] * FP fix in [`absurd_extreme_comparisons`] ## 0.0.104 — 2016-12-15 + * Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)* ## 0.0.103 — 2016-11-25 + * Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)* ## 0.0.102 — 2016-11-24 + * Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)* ## 0.0.101 — 2016-11-23 + * Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)* * New lint: [`string_extend_chars`] ## 0.0.100 — 2016-11-20 + * Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)* ## 0.0.99 — 2016-11-18 + * Update to rustc 1.15.0-nightly (0ed951993 2016-11-14) * New lint: [`get_unwrap`] ## 0.0.98 — 2016-11-08 + * Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` ## 0.0.97 — 2016-11-03 + * For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was previously added for a short time under the name `clippy` but removed for compatibility. @@ -5731,34 +5840,43 @@ Released 2018-09-13 * New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`] ## 0.0.96 — 2016-10-22 + * Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)* * New lint: [`iter_skip_next`] ## 0.0.95 — 2016-10-06 + * Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)* ## 0.0.94 — 2016-10-04 + * Fixes bustage on Windows due to forbidden directory name ## 0.0.93 — 2016-10-03 + * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* * `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] ## 0.0.92 — 2016-09-30 + * Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)* ## 0.0.91 — 2016-09-28 + * Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)* ## 0.0.90 — 2016-09-09 + * Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)* ## 0.0.89 — 2016-09-06 + * Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)* ## 0.0.88 — 2016-09-04 + * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` lint groups: [`filter_next`], `for_loop_over_option`, @@ -5767,30 +5885,37 @@ Released 2018-09-13 through `cargo clippy`. ## 0.0.87 — 2016-08-31 + * Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* * New lints: [`builtin_type_shadow`] * Fix FP in [`zero_prefixed_literal`] and `0b`/`0o` ## 0.0.86 — 2016-08-28 + * Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)* * New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`] ## 0.0.85 — 2016-08-19 + * Fix ICE with [`useless_attribute`] * [`useless_attribute`] ignores `unused_imports` on `use` statements ## 0.0.84 — 2016-08-18 + * Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)* ## 0.0.83 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)* * New lints: [`print_with_newline`], [`useless_attribute`] ## 0.0.82 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)* * New lint: [`module_inception`] ## 0.0.81 — 2016-08-14 + * Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)* * New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`] * False positive fix in [`too_many_arguments`] @@ -5800,14 +5925,17 @@ Released 2018-09-13 * Doc improvements ## 0.0.80 — 2016-07-31 + * Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)* * New lints: [`misrefactored_assign_op`], [`serde_api_misuse`] ## 0.0.79 — 2016-07-10 + * Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)* * Major suggestions refactoring ## 0.0.78 — 2016-07-02 + * Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)* * New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`] * For compatibility, `cargo clippy` does not defines the `clippy` feature @@ -5815,118 +5943,148 @@ Released 2018-09-13 * [`collapsible_if`] now considers `if let` ## 0.0.77 — 2016-06-21 + * Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* * New lints: `stutter` and [`iter_nth`] ## 0.0.76 — 2016-06-10 + * Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* * `cargo clippy` now automatically defines the `clippy` feature * New lint: [`not_unsafe_ptr_arg_deref`] ## 0.0.75 — 2016-06-08 + * Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)* ## 0.0.74 — 2016-06-07 + * Fix bug with `cargo-clippy` JSON parsing * Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the “for further information visit *lint-link*” message. ## 0.0.73 — 2016-06-05 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.72 — 2016-06-04 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.71 — 2016-05-31 + * Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)* * New lint: [`useless_let_if_seq`] ## 0.0.70 — 2016-05-28 + * Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)* * [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`, `RegexBuilder::new` and byte regexes ## 0.0.69 — 2016-05-20 + * Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)* * [`used_underscore_binding`] has been made `Allow` temporarily ## 0.0.68 — 2016-05-17 + * Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)* * New lint: [`unnecessary_operation`] ## 0.0.67 — 2016-05-12 + * Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)* ## 0.0.66 — 2016-05-11 + * New `cargo clippy` subcommand * New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`] ## 0.0.65 — 2016-05-08 + * Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)* * New lints: [`float_arithmetic`], [`integer_arithmetic`] ## 0.0.64 — 2016-04-26 + * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* * New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 + * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* ## 0.0.62 — 2016-04-07 + * Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)* ## 0.0.61 — 2016-04-03 + * Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)* * New lint: [`invalid_upcast_comparisons`] ## 0.0.60 — 2016-04-01 + * Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)* ## 0.0.59 — 2016-03-31 + * Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)* * New lints: [`logic_bug`], [`nonminimal_bool`] * Fixed: [`match_same_arms`] now ignores arms with guards * Improved: [`useless_vec`] now warns on `for … in vec![…]` ## 0.0.58 — 2016-03-27 + * Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)* * New lint: [`doc_markdown`] ## 0.0.57 — 2016-03-27 + * Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)* * Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`] * New lint: [`crosspointer_transmute`] ## 0.0.56 — 2016-03-23 + * Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)* * New lints: [`many_single_char_names`] and [`similar_names`] ## 0.0.55 — 2016-03-21 + * Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)* ## 0.0.54 — 2016-03-16 + * Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)* ## 0.0.53 — 2016-03-15 + * Add a [configuration file] ## ~~0.0.52~~ ## 0.0.51 — 2016-03-13 + * Add `str` to types considered by [`len_zero`] * New lints: [`indexing_slicing`] ## 0.0.50 — 2016-03-11 + * Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)* ## 0.0.49 — 2016-03-09 + * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* * New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 + * Fixed: ICE in [`needless_range_loop`] with globals ## 0.0.47 — 2016-03-07 + * Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)* * New lint: [`redundant_closure_call`] @@ -6573,6 +6731,7 @@ Released 2018-09-13 [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity +[`replace_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_box [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index dd5c5dcf4d1fd..2a9c6efb52eec 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -655,6 +655,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, + crate::replace_box::REPLACE_BOX_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e3168b8cfe02c..6d5116f1f0310 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,6 +323,7 @@ mod ref_patterns; mod reference; mod regex; mod repeat_vec_with_capacity; +mod replace_box; mod reserve_after_initialization; mod return_self_not_must_use; mod returns; @@ -832,5 +833,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); + store.register_late_pass(|_| Box::new(replace_box::ReplaceBox)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs new file mode 100644 index 0000000000000..9388f77a839d9 --- /dev/null +++ b/clippy_lints/src/replace_box.rs @@ -0,0 +1,130 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects assignments of `Default::default()` or `Box::new(value)` + /// to a place of type `Box`. + /// + /// ### Why is this bad? + /// This incurs an extra heap allocation compared to assigning the boxed + /// storage. + /// + /// ### Example + /// ```no_run + /// let mut b = Box::new(1u32); + /// b = Default::default(); + /// ``` + /// Use instead: + /// ```no_run + /// let mut b = Box::new(1u32); + /// *b = Default::default(); + /// ``` + #[clippy::version = "1.92.0"] + pub REPLACE_BOX, + perf, + "assigning a newly created box to `Box` is inefficient" +} +declare_lint_pass!(ReplaceBox => [REPLACE_BOX]); + +impl LateLintPass<'_> for ReplaceBox { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + let lhs_ty = cx.typeck_results().expr_ty(lhs); + + // No diagnostic for late-initialized locals + if let Some(local) = path_to_local(lhs) + && !local_is_initialized(cx, local) + { + return; + } + + let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) else { + return; + }; + + if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, inner_ty, default_trait_id, &[]) + && is_default_call(cx, rhs) + { + span_lint_and_then( + cx, + REPLACE_BOX, + expr.span, + "creating a new box with default content", + |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = Default::default()", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref() + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with default instead", + suggestion, + app, + ); + }, + ); + } + + if inner_ty.is_sized(cx.tcx, cx.typing_env()) + && let Some(rhs_inner) = get_box_new_payload(cx, rhs) + { + span_lint_and_then(cx, REPLACE_BOX, expr.span, "creating a new box", |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = {}", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref(), + Sugg::hir_with_context(cx, rhs_inner, expr.span.ctxt(), "_", &mut app), + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with inner value instead", + suggestion, + app, + ); + }); + } + } + } +} + +fn get_box_inner_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if let ty::Adt(def, args) = ty.kind() + && cx.tcx.is_lang_item(def.did(), LangItem::OwnedBox) + { + Some(args.type_at(0)) + } else { + None + } +} + +fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) +} + +fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && seg.ident.name == sym::new + && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + { + Some(arg) + } else { + None + } +} diff --git a/tests/ui/replace_box.fixed b/tests/ui/replace_box.fixed new file mode 100644 index 0000000000000..58c8ed1691d77 --- /dev/null +++ b/tests/ui/replace_box.fixed @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + **b = T::default(); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + **b = t; + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + *b = Default::default(); + //~^ replace_box + *b = Default::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + *b = 5; + //~^ replace_box + + *b = mac!(three); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.rs b/tests/ui/replace_box.rs new file mode 100644 index 0000000000000..e1fb223e4f211 --- /dev/null +++ b/tests/ui/replace_box.rs @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + *b = Box::new(T::default()); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + *b = Box::new(t); + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + b = Default::default(); + //~^ replace_box + b = Box::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + b = Box::new(5); + //~^ replace_box + + b = Box::new(mac!(three)); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.stderr b/tests/ui/replace_box.stderr new file mode 100644 index 0000000000000..7d9c85da17314 --- /dev/null +++ b/tests/ui/replace_box.stderr @@ -0,0 +1,52 @@ +error: creating a new box + --> tests/ui/replace_box.rs:4:5 + | +LL | *b = Box::new(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = T::default()` + | + = note: this creates a needless allocation + = note: `-D clippy::replace-box` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::replace_box)]` + +error: creating a new box + --> tests/ui/replace_box.rs:9:5 + | +LL | *b = Box::new(t); + | ^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = t` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:44:5 + | +LL | b = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:46:5 + | +LL | b = Box::default(); + | ^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:58:5 + | +LL | b = Box::new(5); + | ^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = 5` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:61:5 + | +LL | b = Box::new(mac!(three)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = mac!(three)` + | + = note: this creates a needless allocation + +error: aborting due to 6 previous errors + From 0415d96f1e0e20e6d5d92577857ebc719e08004e Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 02:27:18 +0800 Subject: [PATCH 1368/1900] Migrate `needless_continue` to late pass --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/loops/mod.rs | 5 +- clippy_lints/src/needless_continue.rs | 222 ++++++++++++++++++-------- clippy_utils/src/higher.rs | 11 +- 4 files changed, 166 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e3168b8cfe02c..bbc7c1f42afa2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -618,7 +618,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); - store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); + store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index b5d6da478d8a3..a064a5910ef98 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -871,7 +871,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { + if let Some(higher::While { + condition, body, span, .. + }) = higher::While::hir(expr) + { while_immutable_condition::check(cx, condition, body); while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index b8601f77e2492..2097038e1fde8 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,7 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::{Block, Label, ast}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_context}; +use clippy_utils::{higher, is_from_proc_macro}; +use rustc_ast::Label; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -127,9 +130,9 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); -impl EarlyLintPass for NeedlessContinue { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { +impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !expr.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, expr) { check_and_warn(cx, expr); } } @@ -188,19 +191,19 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { +fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), - ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), + ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } } -fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::Continue(ref l) = e.kind { - compare_labels(label, l.as_ref()) + StmtKind::Semi(ref e) | StmtKind::Expr(ref e) => { + if let ExprKind::Continue(ref l) = e.kind { + compare_labels(label, l.label.as_ref()) } else { false } @@ -223,19 +226,33 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with /// the AST object representing the loop block of `expr`. -fn with_loop_block(expr: &ast::Expr, mut func: F) +fn with_loop_block<'tcx, F>(expr: &Expr<'tcx>, mut func: F) where - F: FnMut(&Block, Option<&Label>), + F: FnMut(&Block<'tcx>, Option<&Label>), { - if let ast::ExprKind::While(_, loop_block, label) - | ast::ExprKind::ForLoop { - body: loop_block, - label, - .. + if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) + && let ExprKind::Block(block, _) = &body.kind + { + func(block, label.as_ref()); + return; } - | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind + + if let Some(higher::While { body, label, .. }) = higher::While::hir(expr) + && let ExprKind::Block(block, _) = &body.kind { - func(loop_block, label.as_ref()); + func(block, label.as_ref()); + return; + } + + if let Some(higher::WhileLet { if_then, label, .. }) = higher::WhileLet::hir(expr) + && let ExprKind::Block(block, _) = &if_then.kind + { + func(block, label.as_ref()); + return; + } + + if let ExprKind::Loop(block, label, LoopSource::Loop, ..) = expr.kind { + func(block, label.as_ref()); } } @@ -247,17 +264,18 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr(stmt: &ast::Stmt, mut func: F) +fn with_if_expr<'tcx, F>(expr: &Expr<'tcx>, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), + F: FnMut(&Expr<'tcx>, &Expr<'tcx>, &Block<'tcx>, &Expr<'tcx>), { - match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind { - func(e, cond, if_block, else_expr); - } - }, - _ => {}, + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let ExprKind::Block(then, _) = then.kind + { + func(expr, cond, then, r#else); } } @@ -269,20 +287,21 @@ enum LintType { } /// Data we pass around for construction of help messages. -struct LintData<'a> { +#[derive(Debug)] +struct LintData<'tcx> { /// The `if` expression encountered in the above loop. - if_expr: &'a ast::Expr, + if_expr: &'tcx Expr<'tcx>, /// The condition expression for the above `if`. - if_cond: &'a ast::Expr, + if_cond: &'tcx Expr<'tcx>, /// The `then` block of the `if` statement. - if_block: &'a Block, + if_block: &'tcx Block<'tcx>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'a ast::Expr, + else_expr: &'tcx Expr<'tcx>, /// The 0-based index of the `if` statement in the containing loop block. - stmt_idx: usize, + stmt_idx: Option, /// The statements of the loop block. - loop_block: &'a Block, + loop_block: &'tcx Block<'tcx>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -299,7 +318,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -325,8 +344,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L ); } -fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -339,7 +365,7 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD ) } -fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); // Region B @@ -352,18 +378,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin let indent = span_of_first_expr_in_block(data.if_block) .and_then(|span| indent_of(cx, span)) .unwrap_or(0); - let to_annex = data.loop_block.stmts[data.stmt_idx + 1..] - .iter() - .map(|stmt| { - let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let to_annex = if let Some(stmt_idx) = data.stmt_idx { + let mut lines = data.loop_block.stmts[stmt_idx + 1..] + .iter() + .map(|stmt| { + let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let snip = snippet_block(cx, span, "..", None); + snip.lines() + .map(|line| format!("{}{line}", " ".repeat(indent))) + .collect::>() + .join("\n") + }) + .collect::>(); + if let Some(expr) = data.loop_block.expr { + let span = expr.span; let snip = snippet_block(cx, span, "..", None); - snip.lines() + let expr_lines = snip + .lines() .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() - .join("\n") - }) - .collect::>() - .join("\n"); + .join("\n"); + lines.push(expr_lines); + } + lines.join("\n") + } else { + "".to_string() + }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( @@ -373,46 +413,49 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +fn check_last_stmt_in_expr(inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { match &inner_expr.kind { - ast::ExprKind::Continue(continue_label) => { - func(continue_label.as_ref(), inner_expr.span); + ExprKind::Continue(continue_label) => { + func(continue_label.label.as_ref(), inner_expr.span); }, - ast::ExprKind::If(_, then_block, else_block) => { + ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { check_last_stmt_in_block(then_block, func); if let Some(else_block) = else_block { check_last_stmt_in_expr(else_block, func); } }, - ast::ExprKind::Match(_, arms, _) => { - for arm in arms { - if let Some(expr) = &arm.body { - check_last_stmt_in_expr(expr, func); - } + ExprKind::Match(_, arms, _) => { + for arm in arms.iter() { + check_last_stmt_in_expr(arm.body, func); } }, - ast::ExprKind::Block(b, _) => { + ExprKind::Block(b, _) => { check_last_stmt_in_block(b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block, func: &F) +fn check_last_stmt_in_block(b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { + if let Some(expr) = b.expr { + check_last_stmt_in_expr(expr, func); + return; + } + if let Some(last_stmt) = b.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { check_last_stmt_in_expr(inner_expr, func); } } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { +fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { with_loop_block(expr, |loop_block, label| { let p = |continue_label: Option<&Label>, span: Span| { if compare_labels(label, continue_label) { @@ -427,16 +470,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }; - let stmts = &loop_block.stmts; + let stmts = loop_block.stmts; for (i, stmt) in stmts.iter().enumerate() { let mut maybe_emitted_in_if = false; - with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { + let data = &LintData { + if_expr, + if_cond: cond, + if_block: then_block, + else_expr, + stmt_idx: Some(i), + loop_block, + }; + + maybe_emitted_in_if = true; + if needless_continue_in_else(else_expr, label) { + emit_warning( + cx, + data, + DROP_ELSE_BLOCK_AND_MERGE_MSG, + LintType::ContinueInsideElseBlock, + ); + } else if is_first_block_stmt_continue(then_block, label) { + emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; + } + }); + } + + if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } + } + + if let Some(expr) = loop_block.expr { + let mut maybe_emitted_in_if = false; + + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { let data = &LintData { if_expr, if_cond: cond, if_block: then_block, else_expr, - stmt_idx: i, + stmt_idx: None, loop_block, }; @@ -455,7 +533,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }); - if i == stmts.len() - 1 && !maybe_emitted_in_if { + if !maybe_emitted_in_if { check_last_stmt_in_block(loop_block, &p); } } @@ -491,8 +569,12 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &Block) -> Option { - block.stmts.first().map(|stmt| stmt.span) +fn span_of_first_expr_in_block(block: &Block<'_>) -> Option { + block + .stmts + .first() + .map(|stmt| stmt.span) + .or(block.expr.map(|expr| expr.span)) } #[cfg(test)] diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 95e6ecd40dec6..9fe6d8c62d2a5 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -14,6 +14,7 @@ use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. +#[derive(Debug)] pub struct ForLoop<'tcx> { /// `for` loop item pub pat: &'tcx Pat<'tcx>, @@ -318,6 +319,7 @@ pub struct While<'hir> { pub body: &'hir Expr<'hir>, /// Span of the loop header pub span: Span, + pub label: Option, } impl<'hir> While<'hir> { @@ -333,13 +335,18 @@ impl<'hir> While<'hir> { }), .. }, - _, + label, LoopSource::While, span, ) = expr.kind && !has_let_expr(condition) { - return Some(Self { condition, body, span }); + return Some(Self { + condition, + body, + span, + label, + }); } None } From 7117bd9a271a43e5fb3c2c25b4a27feac3017eb7 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 02:48:30 +0800 Subject: [PATCH 1369/1900] fix: `needless_continue` FP when match type is not unit or never --- clippy_lints/src/needless_continue.rs | 24 ++++++++++++++---------- tests/ui/needless_continue.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 2097038e1fde8..4bdac7cfd0777 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -413,7 +413,7 @@ fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &Lint ) } -fn check_last_stmt_in_expr(inner_expr: &Expr<'_>, func: &F) +fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { @@ -422,36 +422,40 @@ where func(continue_label.label.as_ref(), inner_expr.span); }, ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { - check_last_stmt_in_block(then_block, func); + check_last_stmt_in_block(cx, then_block, func); if let Some(else_block) = else_block { - check_last_stmt_in_expr(else_block, func); + check_last_stmt_in_expr(cx, else_block, func); } }, ExprKind::Match(_, arms, _) => { + let match_ty = cx.typeck_results().expr_ty(inner_expr); + if !match_ty.is_unit() && !match_ty.is_never() { + return; + } for arm in arms.iter() { - check_last_stmt_in_expr(arm.body, func); + check_last_stmt_in_expr(cx, arm.body, func); } }, ExprKind::Block(b, _) => { - check_last_stmt_in_block(b, func); + check_last_stmt_in_block(cx, b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block<'_>, func: &F) +fn check_last_stmt_in_block(cx: &LateContext<'_>, b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { if let Some(expr) = b.expr { - check_last_stmt_in_expr(expr, func); + check_last_stmt_in_expr(cx, expr, func); return; } if let Some(last_stmt) = b.stmts.last() && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { - check_last_stmt_in_expr(inner_expr, func); + check_last_stmt_in_expr(cx, inner_expr, func); } } @@ -501,7 +505,7 @@ fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { } if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + check_last_stmt_in_block(cx, loop_block, &p); } } @@ -534,7 +538,7 @@ fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { }); if !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + check_last_stmt_in_block(cx, loop_block, &p); } } }); diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index e873db6dee14e..003ca64aa9bea 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -244,3 +244,18 @@ mod issue_4077 { true } } + +#[allow(clippy::let_unit_value)] +fn issue14550(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } +} From 13bd9b5d0f37c7bea5657692ce50664fd28289b1 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 03:04:23 +0800 Subject: [PATCH 1370/1900] fix: `needless_continue` wrongly unmangled macros --- clippy_lints/src/needless_continue.rs | 53 ++++++++------ tests/ui/needless_continue.rs | 101 +++++++++++++++++++++++--- tests/ui/needless_continue.stderr | 18 ++++- 3 files changed, 140 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 4bdac7cfd0777..55208ae708b93 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_context}; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::higher; +use clippy_utils::source::{indent_of, snippet_block, snippet_with_context}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{ExpnKind, Span}; declare_clippy_lint! { /// ### What it does @@ -132,7 +132,9 @@ declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, expr) { + // We cannot use `from_expansion` because for loops, while loops and while let loops are desugared + // into `loop` expressions. + if !matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(..)) { check_and_warn(cx, expr); } } @@ -193,7 +195,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { /// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Block(else_block, _) => is_first_block_stmt_continue(else_block, label), ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } @@ -201,8 +203,8 @@ fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> boo fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - StmtKind::Semi(ref e) | StmtKind::Expr(ref e) => { - if let ExprKind::Continue(ref l) = e.kind { + StmtKind::Semi(e) | StmtKind::Expr(e) => { + if let ExprKind::Continue(l) = e.kind { compare_labels(label, l.label.as_ref()) } else { false @@ -225,10 +227,10 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> } /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with -/// the AST object representing the loop block of `expr`. -fn with_loop_block<'tcx, F>(expr: &Expr<'tcx>, mut func: F) +/// the HIR object representing the loop block of `expr`. +fn with_loop_block(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Block<'tcx>, Option<&Label>), + F: FnMut(&Block<'_>, Option<&Label>), { if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) && let ExprKind::Block(block, _) = &body.kind @@ -264,9 +266,9 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr<'tcx, F>(expr: &Expr<'tcx>, mut func: F) +fn with_if_expr(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Expr<'tcx>, &Expr<'tcx>, &Block<'tcx>, &Expr<'tcx>), + F: FnMut(&Expr<'_>, &Expr<'_>, &Block<'_>, &Expr<'_>), { if let Some(higher::If { cond, @@ -288,20 +290,20 @@ enum LintType { /// Data we pass around for construction of help messages. #[derive(Debug)] -struct LintData<'tcx> { +struct LintData<'hir> { /// The `if` expression encountered in the above loop. - if_expr: &'tcx Expr<'tcx>, + if_expr: &'hir Expr<'hir>, /// The condition expression for the above `if`. - if_cond: &'tcx Expr<'tcx>, + if_cond: &'hir Expr<'hir>, /// The `then` block of the `if` statement. - if_block: &'tcx Block<'tcx>, + if_block: &'hir Block<'hir>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'tcx Expr<'tcx>, + else_expr: &'hir Expr<'hir>, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: Option, /// The statements of the loop block. - loop_block: &'tcx Block<'tcx>, + loop_block: &'hir Block<'hir>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -366,7 +368,14 @@ fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintDa } fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); // Region B let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span))); @@ -402,7 +411,7 @@ fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &Lint } lines.join("\n") } else { - "".to_string() + String::new() }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); @@ -417,7 +426,7 @@ fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: where F: Fn(Option<&Label>, Span), { - match &inner_expr.kind { + match inner_expr.kind { ExprKind::Continue(continue_label) => { func(continue_label.label.as_ref(), inner_expr.span); }, @@ -432,7 +441,7 @@ where if !match_ty.is_unit() && !match_ty.is_never() { return; } - for arm in arms.iter() { + for arm in arms { check_last_stmt_in_expr(cx, arm.body, func); } }, diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index 003ca64aa9bea..88b7905e7fa80 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -246,16 +246,99 @@ mod issue_4077 { } #[allow(clippy::let_unit_value)] -fn issue14550(mut producer: impl Iterator>) -> Result { - let mut counter = 2; - loop { - match producer.next().unwrap() { - Ok(ok) => break Ok((ok + 1) as u32), - Err(12) => { - counter -= 1; +mod issue14550 { + fn match_with_value(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } + } + + fn inside_macro() { + macro_rules! mac { + ($e:expr => $($rest:tt);*) => { + loop { + match $e { + 1 => continue, + 2 => break, + n => println!("{n}"), + } + $($rest;)* + } + }; + } + + mac!(2 => ); + mac!(1 => {println!("foobar")}); + } + + mod partially_inside_macro { + macro_rules! select { + ( + $expr:expr, + $( $pat:pat => $then:expr ),* + ) => { + fn foo() { + loop { + match $expr { + $( + $pat => $then, + )* + } + } + } + }; + } + + select!(Some(1), + Some(1) => { + println!("one"); continue; }, - err => err?, - }; + Some(2) => {}, + None => break, + _ => () + ); + + macro_rules! choose { + ( + $expr:expr, + $case:expr + ) => { + fn bar() { + loop { + match $expr { + $case => { + println!("matched"); + continue; + }, + _ => { + println!("not matched"); + break; + }, + } + } + } + }; + } + + choose!(todo!(), 5); + } +} + +fn issue15548() { + loop { + if todo!() { + } else { + //~^ needless_continue + continue; + } } } diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 878c1e731e32a..7a65872c85cd2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -220,5 +220,21 @@ LL | | } do_something(); } -error: aborting due to 15 previous errors +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:339:16 + | +LL | } else { + | ________________^ +LL | | +LL | | continue; +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if todo!() { + // merged code follows: + + } + +error: aborting due to 16 previous errors From 8a18afd29022508a22a3a52e12d6681b71e36e69 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 7 Oct 2025 18:13:13 +0200 Subject: [PATCH 1371/1900] Add `t-` prefix to `S-waiting-on-compiler` label --- src/doc/rustc-dev-guide/src/compiler-team.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 79dfbdc8265fc..9556811e842c2 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -54,7 +54,7 @@ They are held on [Zulip][zulip-meetings]. It works roughly as follows: those that are sufficiently important for us to actively track progress. P-critical and P-high bugs should ideally always have an assignee. -- **Check `S-waiting-on-compiler` and `I-compiler-nominated` issues:** These are issues where +- **Check `S-waiting-on-t-compiler` and `I-compiler-nominated` issues:** These are issues where feedback from the team is desired. - **Look over the performance triage report:** We check for PRs that made the performance worse and try to decide if it's worth reverting the performance regression or if From 659575bfbe295d1ca6c86e6fe72325631ec4cf27 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 7 Oct 2025 18:15:02 +0200 Subject: [PATCH 1372/1900] Add `t-` prefix to `S-waiting-on-{team}` labels --- rust-bors.toml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rust-bors.toml b/rust-bors.toml index 1014e31bae49e..e813c6c4b1166 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -15,12 +15,13 @@ labels_blocking_approval = [ "S-waiting-on-crater", "S-waiting-on-fcp", "S-waiting-on-MCP", - "S-waiting-on-lang", - "S-waiting-on-compiler", - "S-waiting-on-libs-api", - "S-waiting-on-types", - "S-waiting-on-opsem", - "S-waiting-on-rustdoc", - "S-waiting-on-rustdoc-frontend", - "S-waiting-on-clippy" + "S-waiting-on-t-lang", + "S-waiting-on-t-compiler", + "S-waiting-on-t-libs-api", + "S-waiting-on-t-libs", + "S-waiting-on-t-types", + "S-waiting-on-t-opsem", + "S-waiting-on-t-rustdoc", + "S-waiting-on-t-rustdoc-frontend", + "S-waiting-on-t-clippy" ] From e5e3bbd9a1fdf989023da5e8d37190beb987662a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 18:23:58 +0200 Subject: [PATCH 1373/1900] extend the let-chain --- clippy_lints/src/replace_box.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 9388f77a839d9..457c9a52a8574 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -40,20 +40,11 @@ impl LateLintPass<'_> for ReplaceBox { if let ExprKind::Assign(lhs, rhs, _) = &expr.kind && !lhs.span.from_expansion() && !rhs.span.from_expansion() - { - let lhs_ty = cx.typeck_results().expr_ty(lhs); - + && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals - if let Some(local) = path_to_local(lhs) - && !local_is_initialized(cx, local) - { - return; - } - - let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) else { - return; - }; - + && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) + && let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) + { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) && implements_trait(cx, inner_ty, default_trait_id, &[]) && is_default_call(cx, rhs) From 01d2adc2f994c23e920044ea1744867734f2661d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 18:25:56 +0200 Subject: [PATCH 1374/1900] replace `get_box_inner_type` with `Ty::boxed_ty` --- clippy_lints/src/replace_box.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 457c9a52a8574..9ad61f25dfcc4 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -3,9 +3,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -43,7 +42,7 @@ impl LateLintPass<'_> for ReplaceBox { && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) - && let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) + && let Some(inner_ty) = lhs_ty.boxed_ty() { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) && implements_trait(cx, inner_ty, default_trait_id, &[]) @@ -94,16 +93,6 @@ impl LateLintPass<'_> for ReplaceBox { } } -fn get_box_inner_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if let ty::Adt(def, args) = ty.kind() - && cx.tcx.is_lang_item(def.did(), LangItem::OwnedBox) - { - Some(args.type_at(0)) - } else { - None - } -} - fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) } From d52744e9fc502fcc5ac6fa2003505d9ca0fcb6a4 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 2 Oct 2025 07:12:08 +0000 Subject: [PATCH 1375/1900] iter repeat: panic on last --- library/core/src/iter/sources/repeat.rs | 3 ++- library/coretests/tests/iter/sources.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index 4bcd5b16aea6a..ac218e9b617a3 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -97,8 +97,9 @@ impl Iterator for Repeat { Some(self.element.clone()) } + #[track_caller] fn last(self) -> Option { - Some(self.element) + panic!("iterator is infinite"); } #[track_caller] diff --git a/library/coretests/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs index 5a391cb67751d..420f3088e6ee4 100644 --- a/library/coretests/tests/iter/sources.rs +++ b/library/coretests/tests/iter/sources.rs @@ -37,6 +37,7 @@ fn test_repeat_count() { } #[test] +#[should_panic = "iterator is infinite"] fn test_repeat_last() { assert_eq!(repeat(42).last(), Some(42)); } From 03cdcb5cd5383857b339af226471f96c46e26b47 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 6 Oct 2025 13:34:42 -0700 Subject: [PATCH 1376/1900] bootstrap: add `Builder::rustc_cmd` that includes the lib path When building with `rust.rpath = false`, every `rustc` invocation needs to include the library path as well. I particularly ran into this in `generate_target_spec_json_schema` when testing 1.91-beta in Fedora, where we do disable rpath for our system builds. The new helper function will hopefully encourage the right thing going forward. --- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/synthetic_targets.rs | 3 +-- src/bootstrap/src/core/builder/cargo.rs | 6 ++---- src/bootstrap/src/core/builder/mod.rs | 8 ++++++++ 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d29d1041486bd..b363b8f93b361 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -519,7 +519,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car // Query rustc for the deployment target, and the associated env var. // The env var is one of the standard `*_DEPLOYMENT_TARGET` vars, i.e. // `MACOSX_DEPLOYMENT_TARGET`, `IPHONEOS_DEPLOYMENT_TARGET`, etc. - let mut cmd = command(builder.rustc(cargo.compiler())); + let mut cmd = builder.rustc_cmd(cargo.compiler()); cmd.arg("--target").arg(target.rustc_target_arg()); cmd.arg("--print=deployment-target"); let output = cmd.run_capture_stdout(builder).stdout(); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b79d2cb413db7..411d42962644d 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -625,7 +625,7 @@ fn generate_target_spec_json_schema(builder: &Builder<'_>, sysroot: &Path) { // We do this by using the stage 1 compiler, which is always compiled for the host, // even in a cross build. let stage1_host = builder.compiler(1, builder.host_target); - let mut rustc = command(builder.rustc(stage1_host)).fail_fast(); + let mut rustc = builder.rustc_cmd(stage1_host).fail_fast(); rustc .env("RUSTC_BOOTSTRAP", "1") .args(["--print=target-spec-json-schema", "-Zunstable-options"]); diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 21733c5d9e3f7..2cc691832b5f0 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -10,7 +10,6 @@ use crate::Compiler; use crate::core::builder::{Builder, ShouldRun, Step}; use crate::core::config::TargetSelection; -use crate::utils::exec::command; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct MirOptPanicAbortSyntheticTarget { @@ -55,7 +54,7 @@ fn create_synthetic_target( return TargetSelection::create_synthetic(&name, path.to_str().unwrap()); } - let mut cmd = command(builder.rustc(compiler)); + let mut cmd = builder.rustc_cmd(compiler); cmd.arg("--target").arg(base.rustc_target_arg()); cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index a404aec512091..c2029f97391d4 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -728,10 +728,8 @@ impl Builder<'_> { // Build proc macros both for the host and the target unless proc-macros are not // supported by the target. if target != compiler.host && cmd_kind != Kind::Check { - let mut rustc_cmd = command(self.rustc(compiler)); - self.add_rustc_lib_path(compiler, &mut rustc_cmd); - - let error = rustc_cmd + let error = self + .rustc_cmd(compiler) .arg("--target") .arg(target.rustc_target_arg()) .arg("--print=file-names") diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index fc06db8f80b9d..1ddfbd51df523 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1605,6 +1605,14 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } } + /// Gets a command to run the compiler specified, including the dynamic library + /// path in case the executable has not been build with `rpath` enabled. + pub fn rustc_cmd(&self, compiler: Compiler) -> BootstrapCommand { + let mut cmd = command(self.rustc(compiler)); + self.add_rustc_lib_path(compiler, &mut cmd); + cmd + } + /// Gets the paths to all of the compiler's codegen backends. fn codegen_backends(&self, compiler: Compiler) -> impl Iterator { fs::read_dir(self.sysroot_codegen_backends(compiler)) From 81711747150a34d91b6642f973fe2379bf12907a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 7 Oct 2025 19:16:58 +0200 Subject: [PATCH 1377/1900] fix panic with extra-const-ub-checks --- compiler/rustc_const_eval/messages.ftl | 5 ++++- compiler/rustc_const_eval/src/errors.rs | 5 ++--- .../src/interpret/validity.rs | 8 +++----- .../rustc_middle/src/mir/interpret/error.rs | 5 ++++- .../consts/extra-const-ub/detect-extra-ub.rs | 10 ++++++++++ .../detect-extra-ub.with_flag.stderr | 20 +++++++++++++++---- 6 files changed, 39 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 13a49e80cf2dc..3a2b3f8843191 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -481,7 +481,10 @@ const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null } box -const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer +const_eval_validation_null_fn_ptr = {$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null + } function pointer const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index d352a6384245f..a0958a2b9ef3a 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -666,7 +666,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { PartialPointer => const_eval_validation_partial_pointer, MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable, MutableRefInConst => const_eval_validation_mutable_ref_in_const, - NullFnPtr => const_eval_validation_null_fn_ptr, + NullFnPtr { .. } => const_eval_validation_null_fn_ptr, NeverVal => const_eval_validation_never_val, NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range, PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, @@ -820,12 +820,11 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); err.arg("expected_dyn_type", expected_dyn_type.to_string()); } - NullPtr { maybe, .. } => { + NullPtr { maybe, .. } | NullFnPtr { maybe } => { err.arg("maybe", maybe); } MutableRefToImmutable | MutableRefInConst - | NullFnPtr | NonnullPtrMaybeNull | NeverVal | UnsafeCellInImmutable diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5f088fe37e80b..c62a23eb0b34b 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -757,14 +757,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ); // FIXME: Check if the signature matches } else { - // Otherwise (for standalone Miri), we have to still check it to be non-null. + // Otherwise (for standalone Miri and for `-Zextra-const-ub-checks`), + // we have to still check it to be non-null. if self.ecx.scalar_may_be_null(scalar)? { let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); - // This can't be a "maybe-null" pointer since the check for this being - // a fn ptr at all already ensures that the pointer is inbounds. - assert!(!maybe); - throw_validation_failure!(self.path, NullFnPtr); + throw_validation_failure!(self.path, NullFnPtr { maybe }); } } if self.reset_provenance_and_padding { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 951aac503fee8..976c209977b06 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -497,7 +497,10 @@ pub enum ValidationErrorKind<'tcx> { MutableRefToImmutable, UnsafeCellInImmutable, MutableRefInConst, - NullFnPtr, + NullFnPtr { + /// Records whether this pointer is definitely null or just may be null. + maybe: bool, + }, NeverVal, NonnullPtrMaybeNull, PtrOutOfRange { diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index e17bac603b415..60086acc3707e 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -52,6 +52,16 @@ const UNALIGNED_PTR: () = unsafe { //[with_flag]~^ ERROR: invalid value }; +// A function pointer offset to be maybe-null. +const MAYBE_NULL_FN_PTR: () = unsafe { + let _x: fn() = transmute({ + //[with_flag]~^ ERROR: invalid value + fn fun() {} + let ptr = fun as fn(); + (ptr as *const u8).wrapping_add(10) + }); +}; + const UNINHABITED_VARIANT: () = unsafe { let data = [1u8]; // Not using transmute, we want to hit the ImmTy code path. diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 6af72868045ac..96c1666a2d2eb 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -37,14 +37,26 @@ error[E0080]: constructing invalid value: encountered an unaligned reference (re LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ evaluation of `UNALIGNED_PTR` failed here +error[E0080]: constructing invalid value: encountered a maybe-null function pointer + --> $DIR/detect-extra-ub.rs:57:20 + | +LL | let _x: fn() = transmute({ + | ____________________^ +LL | | +LL | | fn fun() {} +LL | | let ptr = fun as fn(); +LL | | (ptr as *const u8).wrapping_add(10) +LL | | }); + | |______^ evaluation of `MAYBE_NULL_FN_PTR` failed here + error[E0080]: constructing invalid value at .: encountered an uninhabited enum variant - --> $DIR/detect-extra-ub.rs:58:13 + --> $DIR/detect-extra-ub.rs:68:13 | LL | let v = *addr_of!(data).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINHABITED_VARIANT` failed here error[E0080]: constructing invalid value at [0]: encountered a partial pointer or a mix of pointers - --> $DIR/detect-extra-ub.rs:77:16 + --> $DIR/detect-extra-ub.rs:87:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_POINTER` failed here @@ -53,11 +65,11 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/detect-extra-ub.rs:91:16 + --> $DIR/detect-extra-ub.rs:101:16 | LL | let _val = &*slice; | ^^^^^^^ evaluation of `OVERSIZED_REF` failed here -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. From 6d4b4d9ef080bd2fb2fe0cd81789998ff6064167 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 7 Oct 2025 11:17:44 -0700 Subject: [PATCH 1378/1900] rustdoc-search: keep hourglass in searchbar --- src/librustdoc/html/static/css/rustdoc.css | 7 ------ src/librustdoc/html/static/js/search.js | 29 ++++++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 8078f87756dd2..7f47856948493 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -339,7 +339,6 @@ summary.hideme, .scraped-example-list, .rustdoc-breadcrumbs, .search-switcher, -.search-throbber, /* This selector is for the items listed in the "all items" page. */ ul.all-items { font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; @@ -2007,12 +2006,6 @@ a.tooltip:hover::after { color: transparent; } -.search-throbber { - position: relative; - height: 34px; -} - -.search-throbber::after, .search-form.loading::after { width: 18px; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index ba8363b1a9147..0929d351463cc 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,5 +1,5 @@ // ignore-tidy-filelength -/* global addClass, getNakedUrl, getVar, nonnull, getSettingValue */ +/* global addClass, getNakedUrl, getVar, getSettingValue, hasClass, nonnull */ /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */ "use strict"; @@ -4804,6 +4804,15 @@ function printTab(nb) { if (nb === iter) { addClass(elem, "selected"); foundCurrentTab = true; + onEachLazy(document.querySelectorAll( + ".search-form", + ), form => { + if (hasClass(elem.firstElementChild, "loading")) { + addClass(form, "loading"); + } else { + removeClass(form, "loading"); + } + }); } else { removeClass(elem, "selected"); } @@ -4904,11 +4913,6 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) { let output = document.createElement("ul"); output.className = "search-results " + extraClass; - const throbber = document.createElement("div"); - throbber.className = "search-throbber"; - throbber.innerHTML = "Loading..."; - output.appendChild(throbber); - let count = 0; /** @type {Promise[]} */ @@ -5015,7 +5019,7 @@ ${obj.displayPath}${name}\ } link.appendChild(description); - output.insertBefore(link, throbber); + output.appendChild(link); results.next().then(async nextResult => { if (nextResult.value) { @@ -5026,7 +5030,6 @@ ${obj.displayPath}${name}\ // running this callback yieldToBrowser().then(() => { finishedCallback(count, output); - throbber.remove(); }); } }); @@ -5164,6 +5167,7 @@ function makeTab(tabNb, text, results, query, isTypeSearch, goToFirst) { count < 100 ? `\u{2007}(${count})\u{2007}` : `\u{2007}(${count})`; tabCount.innerHTML = fmtNbElems; tabCount.className = "count"; + printTab(window.searchState.currentTab); }, isTypeSearch), ]; } @@ -5227,9 +5231,7 @@ async function showResults(docSearch, results, goToFirst, filterCrates) { tabsElem.appendChild(tab); const isCurrentTab = window.searchState.currentTab === tabNb; const placeholder = document.createElement("div"); - placeholder.className = isCurrentTab ? - "search-throbber search-results active" : - "search-throbber search-results"; + placeholder.className = isCurrentTab ? "search-results active" : "search-results"; placeholder.innerHTML = "Loading..."; output.then(output => { if (placeholder.parentElement) { @@ -5487,11 +5489,6 @@ if (ROOT_PATH === null) { const database = await Stringdex.loadDatabase(hooks); if (typeof window !== "undefined") { docSearch = new DocSearch(ROOT_PATH, database); - onEachLazy(document.querySelectorAll( - ".search-form.loading", - ), form => { - removeClass(form, "loading"); - }); registerSearchEvents(); // If there's a search term in the URL, execute the search now. if (window.searchState.getQueryStringParams().search !== undefined) { From 74ec42e000d8385343a7b0ea96f28ed4371d4235 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 7 Oct 2025 21:21:07 +0200 Subject: [PATCH 1379/1900] refactor confusing loop in `proc_macro` arena --- library/proc_macro/src/bridge/arena.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index dee4e7b976de6..5e0393e98fdd2 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -90,14 +90,13 @@ impl Arena { return &mut []; } - loop { - if let Some(a) = self.alloc_raw_without_grow(bytes) { - break a; - } - // No free space left. Allocate a new chunk to satisfy the request. - // On failure the grow will panic or abort. - self.grow(bytes); + if let Some(a) = self.alloc_raw_without_grow(bytes) { + return a; } + // No free space left. Allocate a new chunk to satisfy the request. + // On failure the grow will panic or abort. + self.grow(bytes); + self.alloc_raw_without_grow(bytes).unwrap() } #[allow(clippy::mut_from_ref)] // arena allocator From 7aa52ea97afe436b013f6276f1d9a866fb027846 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 7 Oct 2025 21:21:40 +0200 Subject: [PATCH 1380/1900] remove `Decode` trait and only impl --- library/proc_macro/src/bridge/mod.rs | 2 +- library/proc_macro/src/bridge/rpc.rs | 4 ---- library/proc_macro/src/bridge/server.rs | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 1b09deb6bfe60..582c43c78fcbb 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -143,7 +143,7 @@ mod symbol; use buffer::Buffer; pub use rpc::PanicMessage; -use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; +use rpc::{DecodeMut, Encode, Reader, Writer}; /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 26ea42b49b7f8..7f4f5fc3a97d5 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -12,10 +12,6 @@ pub(super) trait Encode: Sized { pub(super) type Reader<'a> = &'a [u8]; -pub(super) trait Decode<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s S) -> Self; -} - pub(super) trait DecodeMut<'a, 's, S>: Sized { fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; } diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 0bb30698aa1d7..2850e1099b700 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -40,10 +40,10 @@ macro_rules! define_server_handles { } } - impl<'s, S: Types> Decode<'_, 's, HandleStore>> + impl<'s, S: Types> DecodeMut<'_, 's, HandleStore>> for &'s Marked { - fn decode(r: &mut Reader<'_>, s: &'s HandleStore>) -> Self { + fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { &s.$oty[handle::Handle::decode(r, &mut ())] } } From c9293bfdae35a9975dca633435ed1b17bbacbf87 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 7 Oct 2025 12:59:58 -0700 Subject: [PATCH 1381/1900] rustdoc-search: add test case for throbber --- tests/rustdoc-gui/search-throbber.goml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/rustdoc-gui/search-throbber.goml diff --git a/tests/rustdoc-gui/search-throbber.goml b/tests/rustdoc-gui/search-throbber.goml new file mode 100644 index 0000000000000..9d41f933af8c1 --- /dev/null +++ b/tests/rustdoc-gui/search-throbber.goml @@ -0,0 +1,23 @@ +// We are intentionally triggering errors for race-free throbber test +fail-on-request-error: false +fail-on-js-error: false + +// First, make sure the throbber goes away when done +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=" +wait-for: ".search-input" +wait-for-false: ".search-form.loading" +write-into: (".search-input", "test") +press-key: 'Enter' +wait-for-false: ".search-form.loading" + +// Make sure the throbber shows up if we prevent the search from +// ever finishing (this tactic is needed to make sure we don't get stuck +// with any race conditions). +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=" +block-network-request: "*/desc/*.js" +reload: +wait-for: ".search-input" +wait-for-false: ".search-form.loading" +write-into: (".search-input", "test") +press-key: 'Enter' +wait-for: ".search-form.loading" From 1dd0a01deb61bd8ec132c5f39943af2eb4b2eaf9 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Tue, 7 Oct 2025 13:06:56 -0700 Subject: [PATCH 1382/1900] Fix backtraces with -C panic=abort on qnx; emit unwind tables by default --- compiler/rustc_target/src/spec/base/nto_qnx.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/nto_qnx.rs index 3f35b8b801ab9..6498742917545 100644 --- a/compiler/rustc_target/src/spec/base/nto_qnx.rs +++ b/compiler/rustc_target/src/spec/base/nto_qnx.rs @@ -12,6 +12,9 @@ pub(crate) fn opts() -> TargetOptions { has_thread_local: false, linker: Some("qcc".into()), os: "nto".into(), + // We want backtraces to work by default and they rely on unwind tables + // (regardless of `-C panic` strategy). + default_uwtable: true, position_independent_executables: true, static_position_independent_executables: true, relro_level: RelroLevel::Full, From 1e6b444df7f1242ad9b0cc58620f101f34514734 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:07:23 -0700 Subject: [PATCH 1383/1900] library: fs: Factor out a `file_time_to_timespec` function in preparation for reusing it --- library/std/src/sys/fs/unix.rs | 49 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 33a1e7ff5e40e..bcd5ea6494447 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1604,24 +1604,6 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - #[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "nuttx", - )))] - let to_timespec = |time: Option| match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - }; cfg_select! { any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { // Redox doesn't appear to support `UTIME_OMIT`. @@ -1639,17 +1621,17 @@ impl File { let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); + buf[num_times].write(file_time_to_timespec(times.created)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_CRTIME; } if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); + buf[num_times].write(file_time_to_timespec(times.modified)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_MODTIME; } if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); + buf[num_times].write(file_time_to_timespec(times.accessed)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; } @@ -1663,7 +1645,7 @@ impl File { Ok(()) } target_os = "android" => { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; // futimens requires Android API level 19 cvt(unsafe { weak!( @@ -1697,7 +1679,7 @@ impl File { return Ok(()); } } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; Ok(()) } @@ -1705,6 +1687,27 @@ impl File { } } +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nuttx", +)))] +fn file_time_to_timespec(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + } +}; + impl DirBuilder { pub fn new() -> DirBuilder { DirBuilder { mode: 0o777 } From 1bf555c947296016d78de926e989d306b4cfde45 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:18:38 -0700 Subject: [PATCH 1384/1900] library: fs: Factor out the Apple file time to attrlist code for reuse --- library/std/src/sys/fs/unix.rs | 74 +++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bcd5ea6494447..bed9ea9139834 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1616,30 +1616,12 @@ impl File { )) } target_vendor = "apple" => { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - if times.created.is_some() { - buf[num_times].write(file_time_to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(file_time_to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(file_time_to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } + let ta = TimesAttrlist::from_times(×)?; cvt(unsafe { libc::fsetattrlist( self.as_raw_fd(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * size_of::(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), 0 ) })?; Ok(()) @@ -1706,7 +1688,53 @@ fn file_time_to_timespec(time: Option) -> io::Result )), None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), } -}; +} + +#[cfg(target_vendor = "apple")] +struct TimesAttrlist { + buf: [mem::MaybeUninit; 3], + attrlist: libc::attrlist, + num_times: usize, +} + +#[cfg(target_vendor = "apple")] +impl TimesAttrlist { + fn from_times(times: &FileTimes) -> io::Result { + let mut this = Self { + buf: [mem::MaybeUninit::::uninit(); 3], + attrlist: unsafe { mem::zeroed() }, + num_times: 0, + }; + this.attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.created)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.modified)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.accessed)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + } + + fn attrlist(&self) -> *mut libc::c_void { + (&raw const self.attrlist).cast::().cast_mut() + } + + fn times_buf(&self) -> *mut libc::c_void { + self.buf.as_ptr().cast::().cast_mut() + } + + fn times_buf_size(&self) -> usize { + self.num_times * size_of::() + } +} impl DirBuilder { pub fn new() -> DirBuilder { From be95904e578ad19bc421a11b18dc4ea062c1e851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 7 Oct 2025 22:20:53 +0200 Subject: [PATCH 1385/1900] Add debug assertions flag to `cg_gcc` invocation --- .../src/tests/codegen-backend-tests/cg_gcc.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index aa337754186d6..ddfc2a3829135 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -1,12 +1,17 @@ # GCC codegen backend If you ran into an error related to tests executed with the GCC codegen backend on CI, -you can use the following command to run tests locally using the GCC backend: +you can use the following command to run UI tests locally using the GCC backend: ```bash -./x test tests/ui --set 'rust.codegen-backends = ["llvm", "gcc"]' --test-codegen-backend gcc +./x test tests/ui \ + --set 'rust.codegen-backends = ["llvm", "gcc"]' \ + --set 'rust.debug-assertions = false' \ + --test-codegen-backend gcc ``` +If a different test suite has failed on CI, you will have to modify the `tests/ui` part. + Below, you can find more information about how to configure the GCC backend in bootstrap. ## Choosing which codegen backends are built From 6920a8e8fcebef68cfd8eea2ef3d95262dfaf8b6 Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Mon, 6 Oct 2025 10:55:52 -0700 Subject: [PATCH 1386/1900] Convert impossible cases in macro resolution into assertions --- compiler/rustc_resolve/src/ident.rs | 21 ++++++++++++++------- compiler/rustc_resolve/src/lib.rs | 18 ++++++++---------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 4415300777f98..3931c3c75f9fb 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -3,7 +3,7 @@ use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; -use rustc_middle::bug; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; @@ -677,14 +677,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { innermost_binding, binding, ) - || flags.contains(Flags::MACRO_RULES) - && innermost_flags.contains(Flags::MODULE) - && !this.disambiguate_macro_rules_vs_modularized( - binding, - innermost_binding, - ) { Some(AmbiguityKind::MacroRulesVsModularized) + } else if flags.contains(Flags::MACRO_RULES) + && innermost_flags.contains(Flags::MODULE) + { + // should be impossible because of visitation order in + // visit_scopes + // + // we visit all macro_rules scopes (e.g. textual scope macros) + // before we visit any modules (e.g. path-based scope macros) + span_bug!( + orig_ident.span, + "ambiguous scoped macro resolutions with path-based \ + scope resolution as first candidate" + ) } else if innermost_binding.is_glob_import() { Some(AmbiguityKind::GlobVsOuter) } else if innermost_binding diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b44b1c966a431..d9c3f4089a0fb 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2226,16 +2226,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Some non-controversial subset of ambiguities "modularized macro name" vs "macro_rules" // is disambiguated to mitigate regressions from macro modularization. // Scoping for `macro_rules` behaves like scoping for `let` at module level, in general. - match ( - self.binding_parent_modules.get(¯o_rules), - self.binding_parent_modules.get(&modularized), - ) { - (Some(macro_rules), Some(modularized)) => { - macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod() - && modularized.is_ancestor_of(*macro_rules) - } - _ => false, - } + // + // panic on index should be impossible, the only name_bindings passed in should be from + // `resolve_ident_in_scope_set` which will always refer to a local binding from an + // import or macro definition + let macro_rules = &self.binding_parent_modules[¯o_rules]; + let modularized = &self.binding_parent_modules[&modularized]; + macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod() + && modularized.is_ancestor_of(*macro_rules) } fn extern_prelude_get_item<'r>( From 8e9b0c4ca9afaadf6c123901c200a9bfb666289f Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Sun, 28 Sep 2025 00:22:36 +0100 Subject: [PATCH 1387/1900] rename `select_where_possible` and `select_all_or_error` --- .../src/diagnostics/bound_region_errors.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 4 ++-- .../src/diagnostics/region_errors.rs | 2 +- .../src/type_check/liveness/trace.rs | 2 +- compiler/rustc_codegen_ssa/src/base.rs | 2 +- .../src/check_consts/check.rs | 2 +- .../src/check_consts/qualifs.rs | 4 ++-- .../src/util/compare_types.rs | 2 +- compiler/rustc_hir_analysis/src/autoderef.rs | 2 +- .../src/check/always_applicable.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 6 ++--- .../src/check/compare_impl_item.rs | 12 +++++----- .../src/check/compare_impl_item/refine.rs | 2 +- .../rustc_hir_analysis/src/check/entry.rs | 2 +- compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 14 ++++++++--- .../src/coherence/builtin.rs | 4 ++-- .../src/coherence/orphan.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 2 +- .../rustc_hir_analysis/src/hir_wf_check.rs | 2 +- .../src/impl_wf_check/min_specialization.rs | 2 +- compiler/rustc_hir_typeck/src/coercion.rs | 10 ++++---- compiler/rustc_hir_typeck/src/expr.rs | 6 ++--- compiler/rustc_hir_typeck/src/fallback.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 4 ++-- compiler/rustc_hir_typeck/src/method/probe.rs | 8 +++---- compiler/rustc_hir_typeck/src/op.rs | 2 +- .../src/infer/canonical/query_response.rs | 2 +- compiler/rustc_infer/src/traits/engine.rs | 9 +++---- .../src/for_loops_over_fallibles.rs | 2 +- .../src/opaque_hidden_inferred_bound.rs | 4 ++-- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 4 ++-- compiler/rustc_passes/src/layout_test.rs | 2 +- .../src/error_reporting/traits/ambiguity.rs | 4 ++-- .../traits/fulfillment_errors.rs | 8 +++---- .../src/error_reporting/traits/suggestions.rs | 4 ++-- compiler/rustc_trait_selection/src/infer.rs | 4 ++-- .../src/solve/fulfill.rs | 6 ++--- .../src/solve/inspect/analyse.rs | 2 +- .../src/solve/normalize.rs | 8 +++---- .../src/traits/auto_trait.rs | 2 +- .../src/traits/coherence.rs | 8 +++---- .../src/traits/const_evaluatable.rs | 4 ++-- .../src/traits/engine.rs | 24 +++++++++++++++---- .../src/traits/fulfill.rs | 6 ++--- .../rustc_trait_selection/src/traits/misc.rs | 8 +++---- .../rustc_trait_selection/src/traits/mod.rs | 10 ++++---- .../src/traits/normalize.rs | 2 +- .../src/traits/query/dropck_outlives.rs | 2 +- .../src/traits/query/evaluate_obligation.rs | 2 +- .../src/traits/query/type_op/custom.rs | 2 +- .../src/traits/specialize/mod.rs | 10 ++++---- .../src/traits/structural_normalize.rs | 2 +- compiler/rustc_traits/src/codegen.rs | 2 +- .../rustc_traits/src/coroutine_witnesses.rs | 2 +- .../src/normalize_projection_ty.rs | 2 +- .../rustc_ty_utils/src/structural_match.rs | 2 +- .../clippy_lints/src/future_not_send.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- .../crates/hir-ty/src/autoderef.rs | 2 +- .../crates/hir-ty/src/infer/coerce.rs | 6 ++--- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 4 ++-- .../crates/hir-ty/src/next_solver/fulfill.rs | 14 +++++------ .../crates/hir-ty/src/next_solver/inspect.rs | 2 +- .../hir-ty/src/next_solver/normalize.rs | 4 ++-- .../hir-ty/src/next_solver/obligation_ctxt.rs | 8 +++---- .../src/next_solver/structural_normalize.rs | 2 +- .../impl-trait/lazy_subtyping_of_opaques.rs | 2 +- 74 files changed, 170 insertions(+), 145 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 76da9dc883231..15b2a5ef2e213 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -420,7 +420,7 @@ fn try_extract_error_from_fulfill_cx<'a, 'tcx>( // We generally shouldn't have errors here because the query was // already run, but there's no point using `span_delayed_bug` // when we're going to emit an error here anyway. - let _errors = ocx.select_all_or_error(); + let _errors = ocx.evaluate_obligations_error_on_ambiguity(); let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone()); try_extract_error_from_region_constraints( ocx.infcx, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index fa1be4cec1ef7..47a1ad0d9489a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1313,7 +1313,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let ocx = ObligationCtxt::new_with_diagnostics(self.infcx); let cause = ObligationCause::misc(expr.span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.iter().all(|error| { match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { Some(clause) => match clause.self_ty().skip_binder().kind() { @@ -1497,7 +1497,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let cause = ObligationCause::misc(span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, def_id); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); // Only emit suggestion if all required predicates are on generic let predicates: Result, _> = errors diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index bec4c934502d8..bad5f03e41274 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1134,7 +1134,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Obligation::misc(tcx, span, self.mir_def_id(), self.infcx.param_env, pred) })); - if ocx.select_all_or_error().is_empty() && count > 0 { + if ocx.evaluate_obligations_error_on_ambiguity().is_empty() && count > 0 { diag.span_suggestion_verbose( tcx.hir_body(*body).value.peel_blocks().span.shrink_to_lo(), fluent::borrowck_dereference_suggestion, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index b704d8f0a7692..7ac94020de03b 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -657,7 +657,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { let errors = match dropck_outlives::compute_dropck_outlives_with_errors( &ocx, op, span, ) { - Ok(_) => ocx.select_all_or_error(), + Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => e, }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 422b06350e1fc..ac3bcb1ea269f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -139,7 +139,7 @@ pub fn validate_trivial_unsize<'tcx>( ) else { return false; }; - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { return false; } infcx.leak_check(universe, None).is_ok() diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 3397bd9a68e4c..413aa5f8b8774 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -415,7 +415,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { ) })); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.is_empty() { Some(ConstConditionsHold::Yes) } else { diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 8a6827bca2bd6..5b65f1726f6c2 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -119,7 +119,7 @@ impl Qualif for HasMutInterior { ty::TraitRef::new(cx.tcx, freeze_def_id, [ty::GenericArg::from(ty)]), ); ocx.register_obligation(obligation); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); !errors.is_empty() } @@ -197,7 +197,7 @@ impl Qualif for NeedsNonConstDrop { }, ), )); - !ocx.select_all_or_error().is_empty() + !ocx.evaluate_obligations_error_on_ambiguity().is_empty() } fn is_structural_in_adt_value<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool { diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index 9eed1a20f1523..8db056ed8737d 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -43,5 +43,5 @@ pub fn relate_types<'tcx>( Ok(()) => {} Err(_) => return false, }; - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 88bd3339e4e18..1f06b1c94237d 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -199,7 +199,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { // evaluate/fulfill mismatches, but that's not a reason for an ICE. return None; }; - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { if self.infcx.next_trait_solver() { unreachable!(); diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index 58c3020f60ede..0ff01477ff24c 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -235,7 +235,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( // They can probably get removed with better treatment of the new `DropImpl` // obligation cause code, and perhaps some custom logic in `report_region_errors`. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let mut guar = None; let mut root_predicates = FxHashSet::default(); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e1e6860e43004..9c2d52c3e8030 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -375,7 +375,7 @@ fn check_opaque_meets_bounds<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let guar = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(guar); @@ -2028,7 +2028,7 @@ pub(super) fn check_coroutine_obligations( ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate)); } - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); debug!(?errors); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); @@ -2072,7 +2072,7 @@ pub(super) fn check_potentially_region_dependent_goals<'tcx>( ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); } - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); debug!(?errors); if errors.is_empty() { Ok(()) } else { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } } 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 946c4936bb64d..5b504cc246d85 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -363,7 +363,7 @@ fn compare_method_predicate_entailment<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); @@ -669,7 +669,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // Check that all obligations are satisfied by the implementation's // RPITs. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { if let Err(guar) = try_report_async_mismatch(tcx, infcx, &errors, trait_m, impl_m, impl_sig) { @@ -1215,7 +1215,7 @@ fn check_region_late_boundedness<'tcx>( return None; }; - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { return None; } @@ -2106,7 +2106,7 @@ fn compare_const_predicate_entailment<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } @@ -2242,7 +2242,7 @@ fn compare_type_predicate_entailment<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); @@ -2367,7 +2367,7 @@ pub(super) fn check_type_bounds<'tcx>( // Check that all obligations are satisfied by the implementation's // version. ocx.register_obligations(obligations); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 3db1c40228f6f..c20e5146546a2 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -164,7 +164,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>( param_env, trait_m_sig.inputs_and_output, )); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)"); return; } diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 97787270be7c1..207cb83bcc8e4 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -138,7 +138,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); let norm_return_ty = ocx.normalize(&cause, param_env, return_ty); ocx.register_bound(cause, param_env, norm_return_ty, term_did); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { infcx.err_ctxt().report_fulfillment_errors(errors); error = true; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e70d5505aae36..0166c3b980de3 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -660,7 +660,7 @@ pub fn check_function_signature<'tcx>( match ocx.eq(&cause, param_env, expected_sig, actual_sig) { Ok(()) => { - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0a555c7f6e9df..7921e34ae4bd5 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -142,7 +142,7 @@ where } f(&mut wfcx)?; - let errors = wfcx.select_all_or_error(); + let errors = wfcx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } @@ -1803,7 +1803,11 @@ fn receiver_is_valid<'tcx>( if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(wfcx.infcx); ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?; - if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + if ocx.evaluate_obligations_error_on_ambiguity().is_empty() { + Ok(()) + } else { + Err(NoSolution) + } }) { return Ok(()); } @@ -1838,7 +1842,11 @@ fn receiver_is_valid<'tcx>( if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(wfcx.infcx); ocx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty)?; - if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + if ocx.evaluate_obligations_error_on_ambiguity().is_empty() { + Ok(()) + } else { + Err(NoSolution) + } }) { wfcx.register_obligations(autoderef.into_obligations()); return Ok(()); diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 0b9a01d6042f1..b7a74ac445bfc 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -333,7 +333,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() param_env, ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]), )); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { if is_from_coerce_pointee_derive(tcx, span) { return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity { @@ -558,7 +558,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( ty::TraitRef::new(tcx, trait_def_id, [source, target]), ); ocx.register_obligation(obligation); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { if is_from_coerce_pointee_derive(tcx, span) { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5a61248cab8fe..c4aeb4c85bb9e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -315,7 +315,7 @@ fn orphan_check<'tcx>( let ocx = traits::ObligationCtxt::new(&infcx); let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), user_ty); let ty = infcx.resolve_vars_if_possible(ty); - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { return Ok(user_ty); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 02baaec37138c..f78eb2bae8ada 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1267,7 +1267,7 @@ pub fn suggest_impl_trait<'tcx>( Ty::new_projection_from_args(infcx.tcx, assoc_item_def_id, args), ); // FIXME(compiler-errors): We may benefit from resolving regions here. - if ocx.select_where_possible().is_empty() + if ocx.try_evaluate_obligations().is_empty() && let item_ty = infcx.resolve_vars_if_possible(item_ty) && let Some(item_ty) = item_ty.make_suggestable(infcx.tcx, false, None) && let Some(sugg) = formatter( diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index d8578970adc94..f879153c5765a 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -95,7 +95,7 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())), )); - for error in ocx.select_all_or_error() { + for error in ocx.evaluate_obligations_error_on_ambiguity() { debug!("Wf-check got error for {:?}: {:?}", ty, error); if error.obligation.predicate == self.predicate { // Save the cause from the greatest depth - this corresponds 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 13c744ab46139..64a36e2d26813 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 @@ -183,7 +183,7 @@ fn get_impl_args( &ObligationCause::misc(impl1_span, impl1_def_id), ); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(errors); return Err(guar); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index ced2cf2b57b14..1e5fea1db9fcc 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -140,7 +140,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(InferOk { value, obligations }) if self.next_trait_solver() => { let ocx = ObligationCtxt::new(self); ocx.register_obligations(obligations); - if ocx.select_where_possible().is_empty() { + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { Err(TypeError::Mismatch) @@ -677,7 +677,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Some(ty::PredicateKind::AliasRelate(..)) => { let ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } coercion.obligations.extend(ocx.into_pending_obligations()); @@ -1099,7 +1099,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; }; ocx.register_obligations(ok.obligations); - ocx.select_where_possible().is_empty() + ocx.try_evaluate_obligations().is_empty() }) } @@ -1203,7 +1203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.next_trait_solver() { let ocx = ObligationCtxt::new(self); let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; - if ocx.select_where_possible().is_empty() { + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations(), @@ -1818,7 +1818,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { )) }), ); - ocx.select_where_possible().is_empty() + ocx.try_evaluate_obligations().is_empty() }) }; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f9cdc923670f0..e663dd25e0ea9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2017,7 +2017,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fudge_inference_if_ok(|| { let ocx = ObligationCtxt::new(self); ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?; - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } Ok(self.resolve_vars_if_possible(adt_ty)) @@ -3663,7 +3663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); - let true_errors = ocx.select_where_possible(); + let true_errors = ocx.try_evaluate_obligations(); // Do a leak check -- we can't really report a useful error here, // but it at least avoids an ICE when the error has to do with higher-ranked @@ -3671,7 +3671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.leak_check(outer_universe, Some(snapshot))?; // Bail if we have ambiguity errors, which we can't report in a useful way. - let ambiguity_errors = ocx.select_all_or_error(); + let ambiguity_errors = ocx.evaluate_obligations_error_on_ambiguity(); if true_errors.is_empty() && !ambiguity_errors.is_empty() { return Err(NoSolution); } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ef88e02fd98c2..be1c173ffbf6f 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -495,7 +495,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .expect("expected diverging var to be unconstrained"); } - ocx.select_where_possible() + ocx.try_evaluate_obligations() }) }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 35253e4c29195..dde6b8ce9b8b2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -654,7 +654,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mutate_fulfillment_errors: impl Fn(&mut Vec>), ) { - let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); + let mut result = self.fulfillment_cx.borrow_mut().try_evaluate_obligations(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c8943d4634e29..4d1c7be391977 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -255,7 +255,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // No argument expectations are produced if unification fails. let origin = self.misc(call_span); ocx.sup(&origin, self.param_env, expected_output, formal_output)?; - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 7a060cafeab1c..e9f8bcd06be2d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -199,7 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ocx = ObligationCtxt::new(self); let normalized_fn_sig = ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig); - if ocx.select_all_or_error().is_empty() { + if ocx.evaluate_obligations_error_on_ambiguity().is_empty() { let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig); if !normalized_fn_sig.has_infer() { return normalized_fn_sig; @@ -347,7 +347,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { ); ocx.register_obligations(impl_obligations); - let mut errors = ocx.select_where_possible(); + let mut errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { fulfillment_errors.append(&mut errors); return false; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 12f80a197b1b8..1ef1fdef6c99d 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1906,7 +1906,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // them to deal with defining uses in `method_autoderef_steps`. if self.next_trait_solver() { ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned()); - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { unreachable!("unexpected autoderef error {errors:?}"); } @@ -2103,7 +2103,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } // Evaluate those obligations to see if they might possibly hold. - for error in ocx.select_where_possible() { + for error in ocx.try_evaluate_obligations() { result = ProbeResult::NoMatch; let nested_predicate = self.resolve_vars_if_possible(error.obligation.predicate); if let Some(trait_predicate) = trait_predicate @@ -2143,7 +2143,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } // Evaluate those obligations to see if they might possibly hold. - for error in ocx.select_where_possible() { + for error in ocx.try_evaluate_obligations() { result = ProbeResult::NoMatch; possibly_unsatisfied_predicates.push(( error.obligation.predicate, @@ -2230,7 +2230,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let ocx = ObligationCtxt::new(self); let self_ty = ocx.register_infer_ok_obligations(ok); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { debug!("failed to prove instantiate self_ty obligations"); return false; } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a8e8582c51c49..d18aed00a6f6d 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -1030,7 +1030,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx); ocx.register_obligation(obligation); - Err(ocx.select_all_or_error()) + Err(ocx.evaluate_obligations_error_on_ambiguity()) } } } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 37a1960520661..b4952e7bfe153 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -125,7 +125,7 @@ impl<'tcx> InferCtxt<'tcx> { T: Debug + TypeFoldable>, { // Select everything, returning errors. - let errors = fulfill_cx.select_all_or_error(self); + let errors = fulfill_cx.evaluate_obligations_error_on_ambiguity(self); // True error! if errors.iter().any(|e| e.is_true_error()) { diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 4a252719694d5..39fff48de6aa6 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -72,6 +72,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.register_predicate_obligation(infcx, obligation); } } + /// Go over the list of pending obligations and try to evaluate them. /// /// For each result: @@ -81,7 +82,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// /// Returns a list of errors from obligations that evaluated to Err. #[must_use] - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; + fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; @@ -94,8 +95,8 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// /// Returns a list of errors from obligations that evaluated to Ambiguous or Err. #[must_use] - fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { - let errors = self.select_where_possible(infcx); + fn evaluate_obligations_error_on_ambiguity(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { + let errors = self.try_evaluate_obligations(infcx); if !errors.is_empty() { return errors; } @@ -108,7 +109,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { fn pending_obligations(&self) -> PredicateObligations<'tcx>; /// Among all pending obligations, collect those are stalled on a inference variable which has - /// changed since the last call to `select_where_possible`. Those obligations are marked as + /// changed since the last call to `try_evaluate_obligations`. Those obligations are marked as /// successful and returned. fn drain_stalled_obligations_for_coroutines( &mut self, diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 9e1fc59817119..fe95a682c6376 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -180,5 +180,5 @@ fn suggest_question_mark<'tcx>( into_iterator_did, ); - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index f836094191e1d..3f2ca92a021a6 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { let ocx = ObligationCtxt::new(infcx); let assoc_pred = ocx.normalize(&traits::ObligationCause::dummy(), cx.param_env, assoc_pred); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { // Can't normalize for some reason...? continue; } @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // If that predicate doesn't hold modulo regions (but passed during type-check), // then we must've taken advantage of the hack in `project_and_unify_types` where // we replace opaques with inference vars. Emit a warning! - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { // If it's a trait bound and an opaque that doesn't satisfy it, // then we can emit a suggestion to add the bound. let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 814eded910df5..c136df812a3f0 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1429,7 +1429,7 @@ fn check_field_tys_sized<'tcx>( ); } - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); debug!(?errors); if !errors.is_empty() { infcx.err_ctxt().report_fulfillment_errors(errors); diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index cbabb982df8b8..95873484b6529 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -623,7 +623,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { param_env, pred, )); - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4ea237cfa0321..80879c88d1ba8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2013,7 +2013,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let sig = ocx.normalize(&cause, param_env, sig); // proc macro is not WF. - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { return; } @@ -2081,7 +2081,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.abort.set(true); } - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { infcx.err_ctxt().report_fulfillment_errors(errors); self.abort.set(true); diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index a19faf0fa8367..4054cfa56330e 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -56,7 +56,7 @@ pub fn ensure_wf<'tcx>( pred, ); ocx.register_obligation(obligation); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { infcx.err_ctxt().report_fulfillment_errors(errors); false diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index af912227ce4e4..edb002c69e760 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -83,7 +83,7 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( }); ocx.register_obligations(obligations); - ocx.select_where_possible().is_empty() + ocx.try_evaluate_obligations().is_empty() }) }; @@ -113,7 +113,7 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( return false; } - ocx.select_where_possible().is_empty() + ocx.try_evaluate_obligations().is_empty() }) }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 7e2bec2876231..4305d4160ebf5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1470,7 +1470,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // // we intentionally drop errors from normalization here, // since the normalization is just done to improve the error message. - let _ = ocx.select_where_possible(); + let _ = ocx.try_evaluate_obligations(); if let Err(new_err) = ocx.eq(&obligation.cause, obligation.param_env, data.term, normalized_term) @@ -2086,7 +2086,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }), ); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return false; } @@ -2102,7 +2102,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { terrs.push(terr); } - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return false; } } @@ -3440,7 +3440,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); let ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation); - if ocx.select_all_or_error().is_empty() { + if ocx.evaluate_obligations_error_on_ambiguity().is_empty() { return Ok(( self.tcx .fn_trait_kind_from_def_id(trait_def_id) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 5665229a4cbe6..136a61598d50e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4590,7 +4590,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env, projection, )); - if ocx.select_where_possible().is_empty() + if ocx.try_evaluate_obligations().is_empty() && let ty = self.resolve_vars_if_possible(ty) && !ty.is_ty_var() { @@ -4737,7 +4737,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pred, )); }); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { // encountered errors. return; } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index cd076d1cb692a..f55468d6324af 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -25,7 +25,7 @@ impl<'tcx> InferCtxt<'tcx> { let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else { return false; }; - ocx.select_where_possible().is_empty() + ocx.try_evaluate_obligations().is_empty() }) } @@ -124,7 +124,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env, ty::TraitRef::new(self.tcx, trait_def_id, [ty]), )); - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); // Find the original predicate in the list of predicates that could definitely not be fulfilled. // If it is in that list, then we know this doesn't even shallowly implement the trait. // If it is not in that list, it was fulfilled, but there may be nested obligations, which we don't care about here. diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index bff4f6ce3fc6b..ddd79e1dc9405 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -60,7 +60,7 @@ struct ObligationStorage<'tcx> { /// Obligations which resulted in an overflow in fulfillment itself. /// /// We cannot eagerly return these as error so we instead store them here - /// to avoid recomputing them each time `select_where_possible` is called. + /// to avoid recomputing them each time `try_evaluate_obligations` is called. /// This also allows us to return the correct `FulfillmentError` for them. overflowed: Vec>, pending: PendingObligations<'tcx>, @@ -101,7 +101,7 @@ impl<'tcx> ObligationStorage<'tcx> { // IMPORTANT: we must not use solve any inference variables in the obligations // as this is all happening inside of a probe. We use a probe to make sure // we get all obligations involved in the overflow. We pretty much check: if - // we were to do another step of `select_where_possible`, which goals would + // we were to do another step of `try_evaluate_obligations`, which goals would // change. // FIXME: is merged, this can be removed. self.overflowed.extend( @@ -179,7 +179,7 @@ where .collect() } - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { + fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); loop { diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index c010add0fc50f..488315054c6aa 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -76,7 +76,7 @@ impl<'tcx> NormalizesToTermHack<'tcx> { self.unconstrained_term, )?; f(&ocx); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.is_empty() { Ok(Certainty::Yes) } else if errors.iter().all(|e| !e.is_true_error()) { diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 8f44c26b70dad..567f660a59383 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -79,7 +79,7 @@ where stalled_coroutine_goals: vec![], }; let value = value.try_fold_with(&mut folder)?; - let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + let errors = folder.fulfill_cx.evaluate_obligations_error_on_ambiguity(at.infcx); if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } @@ -128,7 +128,7 @@ where ); self.fulfill_cx.register_predicate_obligation(infcx, obligation); - self.select_all_and_stall_coroutine_predicates()?; + self.evaluate_all_error_on_ambiguity_stall_coroutine_predicates()?; // Alias is guaranteed to be fully structurally resolved, // so we can super fold here. @@ -143,8 +143,8 @@ where Ok(result) } - fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec> { - let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + fn evaluate_all_error_on_ambiguity_stall_coroutine_predicates(&mut self) -> Result<(), Vec> { + let errors = self.fulfill_cx.try_evaluate_obligations(self.at.infcx); if !errors.is_empty() { return Err(errors); } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index c63cc0e17ab9f..09709291a4b95 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -155,7 +155,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // an additional sanity check. let ocx = ObligationCtxt::new(&infcx); ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8e8c7dd7c9d48..d81030ad7c492 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -371,7 +371,7 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( let ocx = ObligationCtxt::new(infcx); ocx.register_obligations(obligations.iter().cloned()); - let hard_errors = ocx.select_where_possible(); + let hard_errors = ocx.try_evaluate_obligations(); if !hard_errors.is_empty() { assert!( hard_errors.iter().all(|e| e.is_true_error()), @@ -386,7 +386,7 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( let ambiguities = ocx.into_pending_obligations(); let ocx = ObligationCtxt::new_with_diagnostics(infcx); ocx.register_obligations(ambiguities); - let errors_and_ambiguities = ocx.select_all_or_error(); + let errors_and_ambiguities = ocx.evaluate_obligations_error_on_ambiguity(); // We only care about the obligations that are *definitely* true errors. // Ambiguities do not prove the disjointness of two impls. let (errors, ambiguities): (Vec<_>, Vec<_>) = @@ -623,7 +623,7 @@ fn try_prove_negated_where_clause<'tcx>( param_env, negative_predicate, )); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { return false; } @@ -743,7 +743,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { ty = ocx .structurally_normalize_ty(&ObligationCause::dummy(), param_env, ty) .map_err(|_| ())?; - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(()); } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 39333082acdff..45f30cd14c672 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -176,7 +176,7 @@ fn satisfied_from_param_env<'tcx>( if self.infcx.probe(|_| { let ocx = ObligationCtxt::new(self.infcx); ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() - && ocx.select_all_or_error().is_empty() + && ocx.evaluate_obligations_error_on_ambiguity().is_empty() }) { self.single_match = match self.single_match { None => Some(Ok(c)), @@ -217,7 +217,7 @@ fn satisfied_from_param_env<'tcx>( if let Some(Ok(c)) = single_match { let ocx = ObligationCtxt::new(infcx); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); - assert!(ocx.select_all_or_error().is_empty()); + assert!(ocx.evaluate_obligations_error_on_ambiguity().is_empty()); return true; } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 18f28d72f6f8f..eee23a298449d 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -202,14 +202,30 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + /// Go over the list of pending obligations and try to evaluate them. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: leave the obligation in the list to be evaluated later + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Err. #[must_use] - pub fn select_where_possible(&self) -> Vec { - self.engine.borrow_mut().select_where_possible(self.infcx) + pub fn try_evaluate_obligations(&self) -> Vec { + self.engine.borrow_mut().try_evaluate_obligations(self.infcx) } + /// Evaluate all pending obligations, return error if they can't be evaluated. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: remove the obligation from the list and return an error + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Ambiguous or Err. #[must_use] - pub fn select_all_or_error(&self) -> Vec { - self.engine.borrow_mut().select_all_or_error(self.infcx) + pub fn evaluate_obligations_error_on_ambiguity(&self) -> Vec { + self.engine.borrow_mut().evaluate_obligations_error_on_ambiguity(self.infcx) } /// Returns the not-yet-processed and stalled obligations from the diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 6b884b3608044..681f015c17990 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -52,11 +52,11 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// consists of a list of obligations that must be (eventually) /// satisfied. The job is to track which are satisfied, which yielded /// errors, and which are still pending. At any point, users can call -/// `select_where_possible`, and the fulfillment context will try to do +/// `try_evaluate_obligations`, and the fulfillment context will try to do /// selection, retaining only those obligations that remain /// ambiguous. This may be helpful in pushing type inference /// along. Once all type inference constraints have been generated, the -/// method `select_all_or_error` can be used to report any remaining +/// method `evaluate_obligations_error_on_ambiguity` can be used to report any remaining /// ambiguous cases as errors. pub struct FulfillmentContext<'tcx, E: 'tcx> { /// A list of all obligations that have been registered with this @@ -163,7 +163,7 @@ where .collect() } - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { + fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { let selcx = SelectionContext::new(infcx); self.select(selcx) } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 4c25882daa92f..f33196bab6470 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -156,7 +156,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( ty::ClauseKind::UnstableFeature(sym::unsized_const_params), )); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); } } @@ -168,7 +168,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span), ); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors))); continue; @@ -235,7 +235,7 @@ pub fn all_fields_implement_trait<'tcx>( ObligationCause::dummy_with_span(field_ty_span) }; let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty); - let normalization_errors = ocx.select_where_possible(); + let normalization_errors = ocx.try_evaluate_obligations(); // NOTE: The post-normalization type may also reference errors, // such as when we project to a missing type or we have a mismatch @@ -252,7 +252,7 @@ pub fn all_fields_implement_trait<'tcx>( ty, trait_def_id, ); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors))); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 6fefac436994d..c6c2d70ef71f4 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -231,7 +231,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>( let ocx = ObligationCtxt::new(infcx); ocx.register_obligation(obligation); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); match errors.as_slice() { // Only known to hold if we did no inference. [] => infcx.resolve_vars_if_possible(goal) == goal, @@ -273,7 +273,7 @@ fn do_normalize_predicates<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let predicates = ocx.normalize(&cause, elaborated_env, predicates); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); @@ -738,13 +738,13 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec At<'_, 'tcx> { let value = self .normalize(value) .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_all_or_error(self.infcx); + let errors = fulfill_cx.evaluate_obligations_error_on_ambiguity(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); if errors.is_empty() { Ok(value) diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 945ca7c37758f..2e60805cd10a5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -199,7 +199,7 @@ where // Flush errors b/c `deeply_normalize` doesn't expect pending // obligations, and we may have pending obligations from the // branch above (from other types). - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { return Err(errors); } 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 34e0176d213f1..d383cdc1aefa6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -96,7 +96,7 @@ impl<'tcx> InferCtxt<'tcx> { let ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation.clone()); let mut result = EvaluationResult::EvaluatedToOk; - for error in ocx.select_all_or_error() { + for error in ocx.evaluate_obligations_error_on_ambiguity() { if error.is_true_error() { return Ok(EvaluationResult::EvaluatedToErr); } else { 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 a96cb738b81f0..2b58a65051e1a 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 @@ -94,7 +94,7 @@ where let value = op(&ocx).map_err(|_| { infcx.dcx().span_delayed_bug(span, format!("error performing operation: {name}")) })?; - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.is_empty() { Ok(value) } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_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 4bb12694c478a..ab01d0707e0f1 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -164,7 +164,7 @@ fn fulfill_implication<'tcx>( let ocx = ObligationCtxt::new(infcx); let source_trait_ref = ocx.normalize(cause, param_env, source_trait_ref); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { infcx.dcx().span_delayed_bug( infcx.tcx.def_span(source_impl), format!("failed to fully normalize {source_trait_ref}"), @@ -197,7 +197,7 @@ fn fulfill_implication<'tcx>( let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates); ocx.register_obligations(obligations); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { // no dice! debug!( @@ -295,7 +295,7 @@ pub(super) fn specializes( let ocx = ObligationCtxt::new(&infcx); let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref); - if !ocx.select_all_or_error().is_empty() { + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { infcx.dcx().span_delayed_bug( infcx.tcx.def_span(specializing_impl_def_id), format!("failed to fully normalize {specializing_impl_trait_ref}"), @@ -331,7 +331,7 @@ pub(super) fn specializes( let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates); ocx.register_obligations(obligations); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { // no dice! debug!( @@ -367,7 +367,7 @@ pub(super) fn specializes( ) })); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { // no dice! debug!( diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 2e20ede2f50a8..ebeab8eddc6f5 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -52,7 +52,7 @@ impl<'tcx> At<'_, 'tcx> { ); fulfill_cx.register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_where_possible(self.infcx); + let errors = fulfill_cx.try_evaluate_obligations(self.infcx); if !errors.is_empty() { return Err(errors); } diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 4b05e2cc38118..f3b43541b7cbe 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -59,7 +59,7 @@ pub(crate) fn codegen_select_candidate<'tcx>( // In principle, we only need to do this so long as `impl_source` // contains unbound type parameters. It could be a slight // optimization to stop iterating early. - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { // `rustc_monomorphize::collector` assumes there are no type errors. // Cycle errors are the only post-monomorphization errors possible; emit them now so diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 20f9b01372482..2544cd8a13cd8 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -63,7 +63,7 @@ fn compute_assumptions<'tcx>( ty::ClauseKind::WellFormed(ty.into()), ) })); - let _errors = ocx.select_all_or_error(); + let _errors = ocx.evaluate_obligations_error_on_ambiguity(); let region_obligations = infcx.take_registered_region_obligations(); let region_assumptions = infcx.take_registered_region_assumptions(); diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index e52898cc6e242..db871d4b0aaad 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -45,7 +45,7 @@ fn normalize_canonicalized_projection_ty<'tcx>( // are recursive (given some generic parameters of the opaque's type variables). // In that case, we may only realize a cycle error when calling // `normalize_erasing_regions` in mono. - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { // Rustdoc may attempt to normalize type alias types which are not // well-formed. Rustdoc also normalizes types that are just not diff --git a/compiler/rustc_ty_utils/src/structural_match.rs b/compiler/rustc_ty_utils/src/structural_match.rs index e900264a76c7e..80d9c53b108f5 100644 --- a/compiler/rustc_ty_utils/src/structural_match.rs +++ b/compiler/rustc_ty_utils/src/structural_match.rs @@ -28,7 +28,7 @@ fn has_structural_eq_impl<'tcx>(tcx: TyCtxt<'tcx>, adt_ty: Ty<'tcx>) -> bool { // // 2. We are sometimes doing future-incompatibility lints for // now, so we do not want unconditional errors here. - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } pub(crate) fn provide(providers: &mut Providers) { diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 596047977a9b1..221107ba4b93e 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let cause = traits::ObligationCause::misc(span, fn_def_id); ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); + let send_errors = ocx.evaluate_obligations_error_on_ambiguity(); // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top // level". diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index cc98fac45c7cb..1e3a7281bc734 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -475,7 +475,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> let ocx = ObligationCtxt::new(&infcx); ocx.register_obligations(impl_src.nested_obligations()); - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } !ty.needs_drop(tcx, ConstCx::new(tcx, body).typing_env) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 21a86d3e43727..4d348ec6b7f4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -304,7 +304,7 @@ fn structurally_normalize_ty<'db>( // evaluate/fulfill mismatches, but that's not a reason for an ICE. return None; }; - let errors = ocx.select_where_possible(); + let errors = ocx.try_evaluate_obligations(); if !errors.is_empty() { unreachable!(); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 62ce00a2e33d4..0ca696ed08cb4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -160,7 +160,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { Ok(InferOk { value, obligations }) => { let mut ocx = ObligationCtxt::new(this.infer_ctxt()); ocx.register_obligations(obligations); - if ocx.select_where_possible().is_empty() { + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { Err(TypeError::Mismatch) @@ -743,7 +743,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { Some(PredicateKind::AliasRelate(..)) => { let mut ocx = ObligationCtxt::new(self.infer_ctxt()); ocx.register_obligation(obligation); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } coercion.obligations.extend(ocx.into_pending_obligations()); @@ -1119,7 +1119,7 @@ impl<'db> InferenceContext<'db> { prev_ty, new_ty, )?; - if ocx.select_where_possible().is_empty() { + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { Err(TypeError::Mismatch) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index ddf632c1c81b5..7b404bb854132 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2151,7 +2151,7 @@ impl<'db> InferenceContext<'db> { expected_output.to_nextsolver(interner), formal_output, )?; - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(crate::next_solver::TypeError::Mismatch); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 108cf5b1a2b8e..d8ca029815237 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -818,7 +818,7 @@ impl<'db> InferenceTable<'db> { } pub(crate) fn select_obligations_where_possible(&mut self) { - self.fulfillment_cx.select_where_possible(&self.infer_ctxt); + self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); } pub(super) fn register_predicate( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 61d3091a0c1d0..9b330d3f3f454 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1785,7 +1785,7 @@ fn is_valid_trait_method_candidate( let mut ctxt = ObligationCtxt::new(&table.infer_ctxt); ctxt.register_obligations(infer_ok.into_obligations()); // FIXME: Are we doing this correctly? Probably better to follow rustc more closely. - check_that!(ctxt.select_where_possible().is_empty()); + check_that!(ctxt.try_evaluate_obligations().is_empty()); } check_that!(table.unify(receiver_ty, &expected_receiver)); @@ -1871,7 +1871,7 @@ fn is_valid_impl_fn_candidate( ) })); - if ctxt.select_where_possible().is_empty() { + if ctxt.try_evaluate_obligations().is_empty() { IsValidCandidate::Yes } else { IsValidCandidate::No diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 34dff37972e7e..262da858d466a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -54,7 +54,7 @@ struct ObligationStorage<'db> { /// Obligations which resulted in an overflow in fulfillment itself. /// /// We cannot eagerly return these as error so we instead store them here - /// to avoid recomputing them each time `select_where_possible` is called. + /// to avoid recomputing them each time `try_evaluate_obligations` is called. /// This also allows us to return the correct `FulfillmentError` for them. overflowed: Vec>, pending: PendingObligations<'db>, @@ -95,7 +95,7 @@ impl<'db> ObligationStorage<'db> { // IMPORTANT: we must not use solve any inference variables in the obligations // as this is all happening inside of a probe. We use a probe to make sure // we get all obligations involved in the overflow. We pretty much check: if - // we were to do another step of `select_where_possible`, which goals would + // we were to do another step of `try_evaluate_obligations`, which goals would // change. // FIXME: is merged, this can be removed. self.overflowed.extend( @@ -131,7 +131,7 @@ impl<'db> FulfillmentCtxt<'db> { infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - // FIXME: See the comment in `select_where_possible()`. + // FIXME: See the comment in `try_evaluate_obligations()`. // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } @@ -141,7 +141,7 @@ impl<'db> FulfillmentCtxt<'db> { infcx: &InferCtxt<'db>, obligations: impl IntoIterator>, ) { - // FIXME: See the comment in `select_where_possible()`. + // FIXME: See the comment in `try_evaluate_obligations()`. // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); } @@ -158,7 +158,7 @@ impl<'db> FulfillmentCtxt<'db> { .collect() } - pub(crate) fn select_where_possible( + pub(crate) fn try_evaluate_obligations( &mut self, infcx: &InferCtxt<'db>, ) -> Vec> { @@ -223,11 +223,11 @@ impl<'db> FulfillmentCtxt<'db> { errors } - pub(crate) fn select_all_or_error( + pub(crate) fn evaluate_obligations_error_on_ambiguity( &mut self, infcx: &InferCtxt<'db>, ) -> Vec> { - let errors = self.select_where_possible(infcx); + let errors = self.try_evaluate_obligations(infcx); if !errors.is_empty() { return errors; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index bc19d51d23e32..0db4746721752 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -92,7 +92,7 @@ impl<'db> NormalizesToTermHack<'db> { let mut ocx = ObligationCtxt::new(infcx); ocx.eq(&ObligationCause::dummy(), param_env, self.term, self.unconstrained_term)?; f(&mut ocx); - let errors = ocx.select_all_or_error(); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.is_empty() { Ok(Certainty::Yes) } else if errors.iter().all(|e| !matches!(e, NextSolverError::TrueError(_))) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index 41cb4884404f1..2f241f8fecbe7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -77,7 +77,7 @@ where stalled_coroutine_goals: vec![], }; let value = value.try_fold_with(&mut folder)?; - let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + let errors = folder.fulfill_cx.evaluate_obligations_error_on_ambiguity(at.infcx); if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } @@ -138,7 +138,7 @@ impl<'db> NormalizationFolder<'_, 'db> { fn select_all_and_stall_coroutine_predicates( &mut self, ) -> Result<(), Vec>> { - let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + let errors = self.fulfill_cx.try_evaluate_obligations(self.at.infcx); if !errors.is_empty() { return Err(errors); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs index 8e2dc0dec4ed3..e85574a8826ff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs @@ -144,13 +144,13 @@ impl<'a, 'db> ObligationCtxt<'a, 'db> { } #[must_use] - pub fn select_where_possible(&mut self) -> Vec> { - self.engine.select_where_possible(self.infcx) + pub fn try_evaluate_obligations(&mut self) -> Vec> { + self.engine.try_evaluate_obligations(self.infcx) } #[must_use] - pub fn select_all_or_error(&mut self) -> Vec> { - self.engine.select_all_or_error(self.infcx) + pub fn evaluate_obligations_error_on_ambiguity(&mut self) -> Vec> { + self.engine.evaluate_obligations_error_on_ambiguity(self.infcx) } /// Returns the not-yet-processed and stalled obligations from the diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs index 18859d8b79707..00c3708358b92 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -47,7 +47,7 @@ impl<'db> At<'_, 'db> { ); fulfill_cx.register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_where_possible(self.infcx); + let errors = fulfill_cx.try_evaluate_obligations(self.infcx); if !errors.is_empty() { return Err(errors); } diff --git a/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs index 8fd1f35645a8a..8921090d98af5 100644 --- a/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs @@ -13,7 +13,7 @@ fn foo() -> impl Default + Copy { // Make a tuple `(?x, ?y)` and equate it with `(impl Default, u32)`. // For us to try and prove a `Subtype(impl Default, u32)` obligation, // we have to instantiate both `?x` and `?y` without any - // `select_where_possible` calls inbetween. + // `try_evaluate_obligations` calls inbetween. let mut tup = &mut (x, y); let assign_tup = &mut (foo(), 1u32); tup = assign_tup; From f6336bc8e425d806f913494a2c7897e8b3ed47ec Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Sun, 28 Sep 2025 00:22:36 +0100 Subject: [PATCH 1388/1900] rename `select_where_possible` and `select_all_or_error` --- clippy_lints/src/future_not_send.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 596047977a9b1..221107ba4b93e 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let cause = traits::ObligationCause::misc(span, fn_def_id); ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); + let send_errors = ocx.evaluate_obligations_error_on_ambiguity(); // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top // level". diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index cc98fac45c7cb..1e3a7281bc734 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -475,7 +475,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> let ocx = ObligationCtxt::new(&infcx); ocx.register_obligations(impl_src.nested_obligations()); - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } !ty.needs_drop(tcx, ConstCx::new(tcx, body).typing_env) From 27afeb0085db8f066636a512241ed8e2003a3fe2 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Wed, 8 Oct 2025 01:59:44 +0530 Subject: [PATCH 1389/1900] feat: add support for libc::memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 16 +++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1d086906e7a59..3fd57d3c8db22 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -827,6 +827,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?; this.write_pointer(ptr_dest, dest)?; } + "memset" => { + let [ptr_dest, val, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let ptr_dest = this.read_pointer(ptr_dest)?; + let val = this.read_scalar(val)?.to_i32()?; + let n = this.read_target_usize(n)?; + // The docs say val is "interpreted as unsigned char". + #[expect(clippy::as_conversions)] + let val = val as u8; + + this.ptr_get_alloc_id(ptr_dest, 0)?; + + let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); + this.write_bytes_ptr(ptr_dest, bytes)?; + this.write_pointer(ptr_dest, dest)?; + } // LLVM intrinsics "llvm.prefetch" => { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 727533a9de61d..531d637d1f24d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -78,6 +78,47 @@ fn test_strcpy() { } } +fn test_memset() { + unsafe { + let val = 1; + let dest = libc::calloc(3, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 3); + assert_eq!(*slc, [1i8, 1, 1]); + libc::free(dest); + } + + unsafe { + let val = 1; + let dest = libc::calloc(4, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 4); + assert_eq!(*slc, [1i8, 1, 1, 0]); + libc::free(dest); + } + + unsafe { + let val = 1; + let mut dest = 0_i8; + libc::memset(&mut dest as *mut i8 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, val as i8); + } + + unsafe { + let val = 1; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } + + unsafe { + let val = 257; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } +} + fn test_malloc() { // Test that small allocations sometimes *are* not very aligned. let saw_unaligned = (0..64).any(|_| unsafe { @@ -310,4 +351,5 @@ fn main() { test_memcpy(); test_strcpy(); + test_memset(); } From dcc36a864296cae5ee6e9e7402173b4e396a9b07 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 22:45:09 -0400 Subject: [PATCH 1390/1900] add incremental/debug test for autodiff --- .../src/builder/autodiff.rs | 2 +- tests/ui/autodiff/incremental.rs | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/ui/autodiff/incremental.rs diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index bf70a3f4a071d..b0f0fcfe84e4e 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; -use crate::llvm::{self, Metadata, TRUE, Type, Value}; +use crate::llvm::{self, TRUE, Type, Value}; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/tests/ui/autodiff/incremental.rs b/tests/ui/autodiff/incremental.rs new file mode 100644 index 0000000000000..a79059deaa778 --- /dev/null +++ b/tests/ui/autodiff/incremental.rs @@ -0,0 +1,41 @@ +//@ revisions: DEBUG RELEASE +//@[RELEASE] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat +//@[DEBUG] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=0 -Clto=fat -C debuginfo=2 +//@ needs-enzyme +//@ incremental +//@ no-prefer-dynamic +//@ build-pass +#![crate_type = "bin"] +#![feature(autodiff)] + +// We used to use llvm's metadata to instruct enzyme how to differentiate a function. +// In debug mode we would use incremental compilation which caused the metadata to be +// dropped. We now use globals instead and add this test to verify that incremental +// keeps working. Also testing debug mode while at it. + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(bar, Duplicated, Duplicated)] +pub fn foo(r: &[f64; 10], res: &mut f64) { + let mut output = [0.0; 10]; + output[0] = r[0]; + output[1] = r[1] * r[2]; + output[2] = r[4] * r[5]; + output[3] = r[2] * r[6]; + output[4] = r[1] * r[7]; + output[5] = r[2] * r[8]; + output[6] = r[1] * r[9]; + output[7] = r[5] * r[6]; + output[8] = r[5] * r[7]; + output[9] = r[4] * r[8]; + *res = output.iter().sum(); +} +fn main() { + let inputs = Box::new([3.1; 10]); + let mut d_inputs = Box::new([0.0; 10]); + let mut res = Box::new(0.0); + let mut d_res = Box::new(1.0); + + bar(&inputs, &mut d_inputs, &mut res, &mut d_res); + dbg!(&d_inputs); +} From 52e79175861f52eda933b52b7ba43efb169d099a Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 21:47:02 -0400 Subject: [PATCH 1391/1900] Use globals instead of metadata, since metadata isn't emitted in debug builds --- .../src/builder/autodiff.rs | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index b0f0fcfe84e4e..4b433e2b63616 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -143,9 +143,9 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( cx: &SimpleCx<'ll>, builder: &mut Builder<'_, 'll, 'tcx>, width: u32, - args: &mut Vec<&'ll llvm::Value>, + args: &mut Vec<&'ll Value>, inputs: &[DiffActivity], - outer_args: &[&'ll llvm::Value], + outer_args: &[&'ll Value], ) { debug!("matching autodiff arguments"); // We now handle the issue that Rust level arguments not always match the llvm-ir level @@ -157,32 +157,36 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( let mut outer_pos: usize = 0; let mut activity_pos = 0; - let enzyme_const = cx.create_metadata(b"enzyme_const"); - let enzyme_out = cx.create_metadata(b"enzyme_out"); - let enzyme_dup = cx.create_metadata(b"enzyme_dup"); - let enzyme_dupv = cx.create_metadata(b"enzyme_dupv"); - let enzyme_dupnoneed = cx.create_metadata(b"enzyme_dupnoneed"); - let enzyme_dupnoneedv = cx.create_metadata(b"enzyme_dupnoneedv"); + // We used to use llvm's metadata to instruct enzyme how to differentiate a function. + // In debug mode we would use incremental compilation which caused the metadata to be + // dropped. This is prevented by now using named globals, which are also understood + // by Enzyme. + let global_const = cx.declare_global("enzyme_const", cx.type_ptr()); + let global_out = cx.declare_global("enzyme_out", cx.type_ptr()); + let global_dup = cx.declare_global("enzyme_dup", cx.type_ptr()); + let global_dupv = cx.declare_global("enzyme_dupv", cx.type_ptr()); + let global_dupnoneed = cx.declare_global("enzyme_dupnoneed", cx.type_ptr()); + let global_dupnoneedv = cx.declare_global("enzyme_dupnoneedv", cx.type_ptr()); while activity_pos < inputs.len() { let diff_activity = inputs[activity_pos as usize]; // Duplicated arguments received a shadow argument, into which enzyme will write the // gradient. - let (activity, duplicated): (&Metadata, bool) = match diff_activity { + let (activity, duplicated): (&Value, bool) = match diff_activity { DiffActivity::None => panic!("not a valid input activity"), - DiffActivity::Const => (enzyme_const, false), - DiffActivity::Active => (enzyme_out, false), - DiffActivity::ActiveOnly => (enzyme_out, false), - DiffActivity::Dual => (enzyme_dup, true), - DiffActivity::Dualv => (enzyme_dupv, true), - DiffActivity::DualOnly => (enzyme_dupnoneed, true), - DiffActivity::DualvOnly => (enzyme_dupnoneedv, true), - DiffActivity::Duplicated => (enzyme_dup, true), - DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true), - DiffActivity::FakeActivitySize(_) => (enzyme_const, false), + DiffActivity::Const => (global_const, false), + DiffActivity::Active => (global_out, false), + DiffActivity::ActiveOnly => (global_out, false), + DiffActivity::Dual => (global_dup, true), + DiffActivity::Dualv => (global_dupv, true), + DiffActivity::DualOnly => (global_dupnoneed, true), + DiffActivity::DualvOnly => (global_dupnoneedv, true), + DiffActivity::Duplicated => (global_dup, true), + DiffActivity::DuplicatedOnly => (global_dupnoneed, true), + DiffActivity::FakeActivitySize(_) => (global_const, false), }; let outer_arg = outer_args[outer_pos]; - args.push(cx.get_metadata_value(activity)); + args.push(activity); if matches!(diff_activity, DiffActivity::Dualv) { let next_outer_arg = outer_args[outer_pos + 1]; let elem_bytes_size: u64 = match inputs[activity_pos + 1] { @@ -242,7 +246,7 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer); args.push(next_outer_arg2); } - args.push(cx.get_metadata_value(enzyme_const)); + args.push(global_const); args.push(next_outer_arg); outer_pos += 2 + 2 * iterations; activity_pos += 2; @@ -351,13 +355,13 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( let mut args = Vec::with_capacity(num_args as usize + 1); args.push(fn_to_diff); - let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return"); + let global_primal_ret = cx.declare_global("enzyme_primal_return", cx.type_ptr()); if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { - args.push(cx.get_metadata_value(enzyme_primal_ret)); + args.push(global_primal_ret); } if attrs.width > 1 { - let enzyme_width = cx.create_metadata(b"enzyme_width"); - args.push(cx.get_metadata_value(enzyme_width)); + let global_width = cx.declare_global("enzyme_width", cx.type_ptr()); + args.push(global_width); args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } From 218fa60795164abc08dc448c63339d786f417c02 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 23:10:25 -0400 Subject: [PATCH 1392/1900] update ui test since the new frontend is a bit more lenient --- tests/ui/autodiff/autodiff_illegal.rs | 23 ------------- tests/ui/autodiff/autodiff_illegal.stderr | 39 +++-------------------- 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index a53b6d5e58981..6bb384cc87e8d 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -110,15 +110,6 @@ fn f14(x: f32) -> Foo { type MyFloat = f32; -// We would like to support type alias to f32/f64 in argument type in the future, -// but that requires us to implement our checks at a later stage -// like THIR which has type information available. -#[autodiff_reverse(df15, Active, Active)] -fn f15(x: MyFloat) -> f32 { - //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] - unimplemented!() -} - // We would like to support type alias to f32/f64 in return type in the future #[autodiff_reverse(df16, Active, Active)] fn f16(x: f32) -> MyFloat { @@ -136,13 +127,6 @@ fn f17(x: f64) -> F64Trans { unimplemented!() } -// We would like to support `#[repr(transparent)]` f32/f64 wrapper in argument type in the future -#[autodiff_reverse(df18, Active, Active)] -fn f18(x: F64Trans) -> f64 { - //~^^ ERROR failed to resolve: use of undeclared type `F64Trans` [E0433] - unimplemented!() -} - // Invalid return activity #[autodiff_forward(df19, Dual, Active)] fn f19(x: f32) -> f32 { @@ -163,11 +147,4 @@ fn f21(x: f32) -> f32 { unimplemented!() } -struct DoesNotImplDefault; -#[autodiff_forward(df22, Dual)] -pub fn f22() -> DoesNotImplDefault { - //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - unimplemented!() -} - fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index ad6f10af46780..848ae1155e9c4 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -107,53 +107,24 @@ LL | #[autodiff_reverse(df13, Reverse)] | ^^^^^^^ error: invalid return activity Active in Forward Mode - --> $DIR/autodiff_illegal.rs:147:1 + --> $DIR/autodiff_illegal.rs:131:1 | LL | #[autodiff_forward(df19, Dual, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Dual in Reverse Mode - --> $DIR/autodiff_illegal.rs:153:1 + --> $DIR/autodiff_illegal.rs:137:1 | LL | #[autodiff_reverse(df20, Active, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Duplicated in Reverse Mode - --> $DIR/autodiff_illegal.rs:160:1 + --> $DIR/autodiff_illegal.rs:144:1 | LL | #[autodiff_reverse(df21, Active, Duplicated)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type `MyFloat` - --> $DIR/autodiff_illegal.rs:116:1 - | -LL | #[autodiff_reverse(df15, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` - -error[E0433]: failed to resolve: use of undeclared type `F64Trans` - --> $DIR/autodiff_illegal.rs:140:1 - | -LL | #[autodiff_reverse(df18, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` - -error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - --> $DIR/autodiff_illegal.rs:167:1 - | -LL | struct DoesNotImplDefault; - | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` -LL | #[autodiff_forward(df22, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `DoesNotImplDefault: Default` - which is required by `(DoesNotImplDefault, DoesNotImplDefault): Default` -help: consider annotating `DoesNotImplDefault` with `#[derive(Default)]` - | -LL + #[derive(Default)] -LL | struct DoesNotImplDefault; - | - -error: aborting due to 21 previous errors +error: aborting due to 18 previous errors -Some errors have detailed explanations: E0428, E0433, E0599, E0658. +Some errors have detailed explanations: E0428, E0658. For more information about an error, try `rustc --explain E0428`. From 5446a0ae0afa8678afff93fb2fdc7db860fbc102 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 8 Oct 2025 04:52:55 +0000 Subject: [PATCH 1393/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 4fd31815524baba0bf368f151f757101f432e3de. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 77436ae2f8259..692cbc0a56bc3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3b8665c5ab3aeced9b01672404c3764583e722ca +4fd31815524baba0bf368f151f757101f432e3de From 1dbe831e471710b71e886bd1f6a97bc47b17b1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 7 Oct 2025 16:59:24 +0200 Subject: [PATCH 1394/1900] sort attribute targets for more consistent error messages --- .../rustc_attr_parsing/src/target_checking.rs | 22 ++++++---- tests/ui/asm/naked-invalid-attr.stderr | 4 +- tests/ui/attributes/attr-on-mac-call.stderr | 10 ++--- tests/ui/attributes/linkage.stderr | 12 ++--- .../attributes/malformed-static-align.stderr | 2 +- .../ui/coverage-attr/allowed-positions.stderr | 34 +++++++------- tests/ui/coverage-attr/name-value.stderr | 14 +++--- tests/ui/coverage-attr/word-only.stderr | 14 +++--- .../ui/deprecation/deprecation-sanity.stderr | 2 +- tests/ui/extern/extern-no-mangle.stderr | 2 +- ...gate-allow-internal-unstable-struct.stderr | 2 +- ...43106-gating-of-builtin-attrs-error.stderr | 2 +- ...issue-43106-gating-of-builtin-attrs.stderr | 38 ++++++++-------- .../invalid-debugger-visualizer-target.stderr | 2 +- tests/ui/issues/issue-32782.stderr | 2 +- .../unused/unused-attr-macro-rules.stderr | 2 +- .../unused/unused_attributes-must_use.stderr | 44 +++++++++---------- ...warn-unused-inline-on-fn-prototypes.stderr | 4 +- tests/ui/macros/issue-68060.stderr | 2 +- .../target-feature/invalid-attribute.stderr | 2 +- .../unsupported_attribute.stderr | 8 ++-- 21 files changed, 114 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index c52253699b51c..d28f43943ba84 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -198,16 +198,20 @@ pub(crate) fn allowed_targets_applied( filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets); filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets); + let mut target_strings: Vec<_> = added_fake_targets + .iter() + .copied() + .chain(allowed_targets.iter().map(|t| t.plural_name())) + .map(|i| i.to_string()) + .collect(); + + // ensure a consistent order + target_strings.sort(); + // If there is now only 1 target left, show that as the only possible target - ( - added_fake_targets - .iter() - .copied() - .chain(allowed_targets.iter().map(|t| t.plural_name())) - .map(|i| i.to_string()) - .collect(), - allowed_targets.len() + added_fake_targets.len() == 1, - ) + let only_target = target_strings.len() == 1; + + (target_strings, only_target) } fn filter_targets( diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 33bbfc885da97..923d2de1f323d 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -18,7 +18,7 @@ error: `#[naked]` attribute cannot be used on foreign functions LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ | - = help: `#[naked]` can be applied to methods and functions + = help: `#[naked]` can be applied to functions and methods error: `#[naked]` attribute cannot be used on structs --> $DIR/naked-invalid-attr.rs:13:1 @@ -50,7 +50,7 @@ error: `#[naked]` attribute cannot be used on closures LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ | - = help: `#[naked]` can be applied to methods and functions + = help: `#[naked]` can be applied to functions and methods error[E0736]: attribute incompatible with `#[unsafe(naked)]` --> $DIR/naked-invalid-attr.rs:56:3 diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index 02c0ec8ea1d6c..1aeec463c71cf 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -55,7 +55,7 @@ LL | #[deprecated] | ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to functions, data types, modules, unions, constants, statics, macro defs, type aliases, use statements, foreign statics, struct fields, traits, associated types, associated consts, enum variants, inherent impl blocks, and crates + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements warning: `#[inline]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:24:5 @@ -136,7 +136,7 @@ LL | #[deprecated] | ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to functions, data types, modules, unions, constants, statics, macro defs, type aliases, use statements, foreign statics, struct fields, traits, associated types, associated consts, enum variants, inherent impl blocks, and crates + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements warning: `#[automatically_derived]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:51:5 @@ -154,7 +154,7 @@ LL | #[macro_use] | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules warning: `#[must_use]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:57:5 @@ -163,7 +163,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[no_implicit_prelude]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:60:5 @@ -172,7 +172,7 @@ LL | #[no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_implicit_prelude]` can be applied to modules and crates + = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[path]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:63:5 diff --git a/tests/ui/attributes/linkage.stderr b/tests/ui/attributes/linkage.stderr index d2aee3840582f..167cdb0243a79 100644 --- a/tests/ui/attributes/linkage.stderr +++ b/tests/ui/attributes/linkage.stderr @@ -4,7 +4,7 @@ error: `#[linkage]` attribute cannot be used on type aliases LL | #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to functions, statics, and foreign statics + = help: `#[linkage]` can be applied to foreign statics, functions, and statics error: `#[linkage]` attribute cannot be used on modules --> $DIR/linkage.rs:9:1 @@ -12,7 +12,7 @@ error: `#[linkage]` attribute cannot be used on modules LL | #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to functions, statics, and foreign statics + = help: `#[linkage]` can be applied to foreign statics, functions, and statics error: `#[linkage]` attribute cannot be used on structs --> $DIR/linkage.rs:12:1 @@ -20,7 +20,7 @@ error: `#[linkage]` attribute cannot be used on structs LL | #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to functions, statics, and foreign statics + = help: `#[linkage]` can be applied to foreign statics, functions, and statics error: `#[linkage]` attribute cannot be used on inherent impl blocks --> $DIR/linkage.rs:15:1 @@ -28,7 +28,7 @@ error: `#[linkage]` attribute cannot be used on inherent impl blocks LL | #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to functions, statics, and foreign statics + = help: `#[linkage]` can be applied to foreign statics, functions, and statics error: `#[linkage]` attribute cannot be used on expressions --> $DIR/linkage.rs:23:5 @@ -36,7 +36,7 @@ error: `#[linkage]` attribute cannot be used on expressions LL | #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to functions, statics, and foreign statics + = help: `#[linkage]` can be applied to foreign statics, functions, and statics error: `#[linkage]` attribute cannot be used on closures --> $DIR/linkage.rs:39:13 @@ -44,7 +44,7 @@ error: `#[linkage]` attribute cannot be used on closures LL | let _ = #[linkage = "weak"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[linkage]` can be applied to methods, functions, statics, foreign statics, and foreign functions + = help: `#[linkage]` can be applied to foreign functions, foreign statics, functions, methods, and statics error: aborting due to 6 previous errors diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr index 35f654d3990f2..e618ca8acd75b 100644 --- a/tests/ui/attributes/malformed-static-align.stderr +++ b/tests/ui/attributes/malformed-static-align.stderr @@ -25,7 +25,7 @@ error: `#[rustc_align_static]` attribute cannot be used on structs LL | #[rustc_align_static(16)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_align_static]` can be applied to statics and foreign statics + = help: `#[rustc_align_static]` can be applied to foreign statics and statics error: `#[repr(align(...))]` is not supported on statics --> $DIR/malformed-static-align.rs:13:8 diff --git a/tests/ui/coverage-attr/allowed-positions.stderr b/tests/ui/coverage-attr/allowed-positions.stderr index 1690d089a8c5c..09d6bac497d21 100644 --- a/tests/ui/coverage-attr/allowed-positions.stderr +++ b/tests/ui/coverage-attr/allowed-positions.stderr @@ -14,7 +14,7 @@ error: `#[coverage]` attribute cannot be used on type aliases LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on traits --> $DIR/allowed-positions.rs:17:1 @@ -22,7 +22,7 @@ error: `#[coverage]` attribute cannot be used on traits LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on associated consts --> $DIR/allowed-positions.rs:19:5 @@ -30,7 +30,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on associated types --> $DIR/allowed-positions.rs:22:5 @@ -38,7 +38,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on required trait methods --> $DIR/allowed-positions.rs:25:5 @@ -46,7 +46,7 @@ error: `#[coverage]` attribute cannot be used on required trait methods LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to impl blocks, functions, closures, provided trait methods, trait methods in impl blocks, inherent methods, modules, and crates + = help: `#[coverage]` can be applied to closures, crates, functions, impl blocks, inherent methods, modules, provided trait methods, and trait methods in impl blocks error: `#[coverage]` attribute cannot be used on required trait methods --> $DIR/allowed-positions.rs:31:5 @@ -54,7 +54,7 @@ error: `#[coverage]` attribute cannot be used on required trait methods LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to impl blocks, functions, closures, provided trait methods, trait methods in impl blocks, inherent methods, modules, and crates + = help: `#[coverage]` can be applied to closures, crates, functions, impl blocks, inherent methods, modules, provided trait methods, and trait methods in impl blocks error: `#[coverage]` attribute cannot be used on associated types --> $DIR/allowed-positions.rs:39:5 @@ -62,7 +62,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on associated types --> $DIR/allowed-positions.rs:56:5 @@ -70,7 +70,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on structs --> $DIR/allowed-positions.rs:61:1 @@ -78,7 +78,7 @@ error: `#[coverage]` attribute cannot be used on structs LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on struct fields --> $DIR/allowed-positions.rs:63:5 @@ -86,7 +86,7 @@ error: `#[coverage]` attribute cannot be used on struct fields LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on foreign statics --> $DIR/allowed-positions.rs:76:5 @@ -94,7 +94,7 @@ error: `#[coverage]` attribute cannot be used on foreign statics LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on foreign types --> $DIR/allowed-positions.rs:79:5 @@ -102,7 +102,7 @@ error: `#[coverage]` attribute cannot be used on foreign types LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on foreign functions --> $DIR/allowed-positions.rs:82:5 @@ -110,7 +110,7 @@ error: `#[coverage]` attribute cannot be used on foreign functions LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to methods, impl blocks, functions, closures, modules, and crates + = help: `#[coverage]` can be applied to closures, crates, functions, impl blocks, methods, and modules error: `#[coverage]` attribute cannot be used on statements --> $DIR/allowed-positions.rs:88:5 @@ -118,7 +118,7 @@ error: `#[coverage]` attribute cannot be used on statements LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on statements --> $DIR/allowed-positions.rs:94:5 @@ -126,7 +126,7 @@ error: `#[coverage]` attribute cannot be used on statements LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on match arms --> $DIR/allowed-positions.rs:110:9 @@ -134,7 +134,7 @@ error: `#[coverage]` attribute cannot be used on match arms LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: `#[coverage]` attribute cannot be used on expressions --> $DIR/allowed-positions.rs:114:5 @@ -142,7 +142,7 @@ error: `#[coverage]` attribute cannot be used on expressions LL | #[coverage(off)] | ^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error: aborting due to 18 previous errors diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr index 77abaa42e311f..06e59e5a8646a 100644 --- a/tests/ui/coverage-attr/name-value.stderr +++ b/tests/ui/coverage-attr/name-value.stderr @@ -49,7 +49,7 @@ error: `#[coverage]` attribute cannot be used on structs LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:26:1 @@ -87,7 +87,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:35:1 @@ -110,7 +110,7 @@ error: `#[coverage]` attribute cannot be used on traits LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:39:5 @@ -133,7 +133,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:44:5 @@ -156,7 +156,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:50:1 @@ -194,7 +194,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:58:5 @@ -217,7 +217,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:64:1 diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr index 5fcffacc7fa7f..05478695256f9 100644 --- a/tests/ui/coverage-attr/word-only.stderr +++ b/tests/ui/coverage-attr/word-only.stderr @@ -43,7 +43,7 @@ error: `#[coverage]` attribute cannot be used on structs LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:26:1 @@ -77,7 +77,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:35:1 @@ -98,7 +98,7 @@ error: `#[coverage]` attribute cannot be used on traits LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:39:5 @@ -119,7 +119,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:44:5 @@ -140,7 +140,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:50:1 @@ -174,7 +174,7 @@ error: `#[coverage]` attribute cannot be used on associated consts LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:58:5 @@ -195,7 +195,7 @@ error: `#[coverage]` attribute cannot be used on associated types LL | #[coverage] | ^^^^^^^^^^^ | - = help: `#[coverage]` can be applied to functions, impl blocks, modules, and crates + = help: `#[coverage]` can be applied to crates, functions, impl blocks, and modules error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:64:1 diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index ea021b71e1485..48d08b18f8bda 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -177,7 +177,7 @@ LL | #[deprecated = "hello"] | ^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to functions, data types, modules, unions, constants, statics, macro defs, type aliases, use statements, foreign statics, struct fields, traits, associated types, associated consts, enum variants, inherent impl blocks, and crates + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements = note: `#[deny(useless_deprecated)]` on by default error: aborting due to 10 previous errors diff --git a/tests/ui/extern/extern-no-mangle.stderr b/tests/ui/extern/extern-no-mangle.stderr index 69c4fbb935db6..61146e0ab56bc 100644 --- a/tests/ui/extern/extern-no-mangle.stderr +++ b/tests/ui/extern/extern-no-mangle.stderr @@ -19,7 +19,7 @@ LL | #[no_mangle] | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_mangle]` can be applied to methods, functions, and statics + = help: `#[no_mangle]` can be applied to functions, methods, and statics warning: `#[no_mangle]` attribute cannot be used on statements --> $DIR/extern-no-mangle.rs:24:5 diff --git a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr index 42141b891aef1..d68affa955aa8 100644 --- a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr +++ b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr @@ -13,7 +13,7 @@ error: `#[allow_internal_unstable]` attribute cannot be used on structs LL | #[allow_internal_unstable(something)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[allow_internal_unstable]` can be applied to macro defs and functions + = help: `#[allow_internal_unstable]` can be applied to functions and macro defs error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 4f4edeef420fe..13152ca12e4b8 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -134,7 +134,7 @@ error: `#[export_name]` attribute cannot be used on required trait methods LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[export_name]` can be applied to statics, functions, inherent methods, provided trait methods, and trait methods in impl blocks + = help: `#[export_name]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks error: attribute should be applied to an `extern crate` item --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:55:1 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 30b6d1c9180f1..29044f1212c21 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -644,7 +644,7 @@ LL | #[macro_use] fn f() { } | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules warning: `#[macro_use]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:197:5 @@ -653,7 +653,7 @@ LL | #[macro_use] struct S; | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules warning: `#[macro_use]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:203:5 @@ -662,7 +662,7 @@ LL | #[macro_use] type T = S; | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules warning: `#[macro_use]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:209:5 @@ -671,7 +671,7 @@ LL | #[macro_use] impl S { } | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules warning: `#[macro_export]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:1 @@ -878,7 +878,7 @@ LL | #[no_mangle] fn foo(); | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks + = help: `#[no_mangle]` can be applied to functions, inherent methods, statics, and trait methods in impl blocks warning: `#[no_mangle]` attribute cannot be used on provided trait methods --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:9 @@ -887,7 +887,7 @@ LL | #[no_mangle] fn bar() {} | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks + = help: `#[no_mangle]` can be applied to functions, inherent methods, statics, and trait methods in impl blocks warning: `#[should_panic]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:406:1 @@ -986,7 +986,7 @@ LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_implicit_prelude]` can be applied to modules and crates + = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[no_implicit_prelude]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:5 @@ -995,7 +995,7 @@ LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_implicit_prelude]` can be applied to modules and crates + = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[no_implicit_prelude]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 @@ -1004,7 +1004,7 @@ LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_implicit_prelude]` can be applied to modules and crates + = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[no_implicit_prelude]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5 @@ -1013,7 +1013,7 @@ LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[no_implicit_prelude]` can be applied to modules and crates + = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[macro_escape]` attribute cannot be used on functions --> $DIR/issue-43106-gating-of-builtin-attrs.rs:533:5 @@ -1022,7 +1022,7 @@ LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_escape]` can be applied to modules, extern crates, and crates + = help: `#[macro_escape]` can be applied to crates, extern crates, and modules warning: `#[macro_escape]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:539:5 @@ -1031,7 +1031,7 @@ LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_escape]` can be applied to modules, extern crates, and crates + = help: `#[macro_escape]` can be applied to crates, extern crates, and modules warning: `#[macro_escape]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:545:5 @@ -1040,7 +1040,7 @@ LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_escape]` can be applied to modules, extern crates, and crates + = help: `#[macro_escape]` can be applied to crates, extern crates, and modules warning: `#[macro_escape]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 @@ -1049,7 +1049,7 @@ LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_escape]` can be applied to modules, extern crates, and crates + = help: `#[macro_escape]` can be applied to crates, extern crates, and modules warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:558:1 @@ -1290,7 +1290,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:795:17 @@ -1299,7 +1299,7 @@ LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 @@ -1308,7 +1308,7 @@ LL | #[must_use] type T = S; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 @@ -1317,7 +1317,7 @@ LL | #[must_use] impl S { } | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:841:1 @@ -1590,7 +1590,7 @@ LL | #![must_use] | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: 174 warnings emitted diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr index 629af94c5ef22..c721e3dc10f59 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr @@ -4,7 +4,7 @@ error: `#[debugger_visualizer]` attribute cannot be used on functions LL | #[debugger_visualizer(natvis_file = "./foo.natvis.xml")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[debugger_visualizer]` can be applied to modules and crates + = help: `#[debugger_visualizer]` can be applied to crates and modules error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-32782.stderr b/tests/ui/issues/issue-32782.stderr index 96cd0489b2a71..2a1183ab978d0 100644 --- a/tests/ui/issues/issue-32782.stderr +++ b/tests/ui/issues/issue-32782.stderr @@ -20,7 +20,7 @@ LL | #[allow_internal_unstable()] LL | foo!(); | ------ in this macro invocation | - = help: `#[allow_internal_unstable]` can be applied to macro defs and functions + = help: `#[allow_internal_unstable]` can be applied to functions and macro defs = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unused/unused-attr-macro-rules.stderr b/tests/ui/lint/unused/unused-attr-macro-rules.stderr index 0c6825026ed3b..e251ec65622ee 100644 --- a/tests/ui/lint/unused/unused-attr-macro-rules.stderr +++ b/tests/ui/lint/unused/unused-attr-macro-rules.stderr @@ -5,7 +5,7 @@ LL | #[macro_use] | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules note: the lint level is defined here --> $DIR/unused-attr-macro-rules.rs:1:9 | diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr index 001ec52ddd9e7..9b9a6a9c9f3d1 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.stderr +++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr @@ -5,7 +5,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions note: the lint level is defined here --> $DIR/unused_attributes-must_use.rs:4:9 | @@ -19,7 +19,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on modules --> $DIR/unused_attributes-must_use.rs:11:1 @@ -28,7 +28,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on use statements --> $DIR/unused_attributes-must_use.rs:15:1 @@ -37,7 +37,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on constants --> $DIR/unused_attributes-must_use.rs:19:1 @@ -46,7 +46,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on statics --> $DIR/unused_attributes-must_use.rs:22:1 @@ -55,7 +55,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on inherent impl blocks --> $DIR/unused_attributes-must_use.rs:40:1 @@ -64,7 +64,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on foreign modules --> $DIR/unused_attributes-must_use.rs:55:1 @@ -73,7 +73,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on foreign statics --> $DIR/unused_attributes-must_use.rs:59:5 @@ -82,7 +82,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on type aliases --> $DIR/unused_attributes-must_use.rs:73:1 @@ -91,7 +91,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on function params --> $DIR/unused_attributes-must_use.rs:77:8 @@ -100,7 +100,7 @@ LL | fn qux<#[must_use] T>(_: T) {} | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on associated consts --> $DIR/unused_attributes-must_use.rs:82:5 @@ -109,7 +109,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on associated types --> $DIR/unused_attributes-must_use.rs:85:5 @@ -118,7 +118,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on trait impl blocks --> $DIR/unused_attributes-must_use.rs:95:1 @@ -127,7 +127,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> $DIR/unused_attributes-must_use.rs:100:5 @@ -136,7 +136,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, unions, required trait methods, provided trait methods, inherent methods, foreign functions, and traits + = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions error: `#[must_use]` attribute cannot be used on trait aliases --> $DIR/unused_attributes-must_use.rs:107:1 @@ -145,7 +145,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on macro defs --> $DIR/unused_attributes-must_use.rs:111:1 @@ -154,7 +154,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on statements --> $DIR/unused_attributes-must_use.rs:120:5 @@ -163,7 +163,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on closures --> $DIR/unused_attributes-must_use.rs:125:13 @@ -172,7 +172,7 @@ LL | let x = #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to methods, data types, functions, unions, foreign functions, and traits + = help: `#[must_use]` can be applied to data types, foreign functions, functions, methods, traits, and unions error: `#[must_use]` attribute cannot be used on match arms --> $DIR/unused_attributes-must_use.rs:148:9 @@ -181,7 +181,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on struct fields --> $DIR/unused_attributes-must_use.rs:157:28 @@ -190,7 +190,7 @@ LL | let s = PatternField { #[must_use] foo: 123 }; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: `#[must_use]` attribute cannot be used on pattern fields --> $DIR/unused_attributes-must_use.rs:159:24 @@ -199,7 +199,7 @@ LL | let PatternField { #[must_use] foo } = s; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to functions, data types, unions, and traits + = help: `#[must_use]` can be applied to data types, functions, traits, and unions error: unused `X` that must be used --> $DIR/unused_attributes-must_use.rs:130:5 diff --git a/tests/ui/lint/warn-unused-inline-on-fn-prototypes.stderr b/tests/ui/lint/warn-unused-inline-on-fn-prototypes.stderr index fcce1db7a9ac0..bdeaabbc253f4 100644 --- a/tests/ui/lint/warn-unused-inline-on-fn-prototypes.stderr +++ b/tests/ui/lint/warn-unused-inline-on-fn-prototypes.stderr @@ -5,7 +5,7 @@ LL | #[inline] | ^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[inline]` can be applied to functions, inherent methods, provided trait methods, trait methods in impl blocks, and closures + = help: `#[inline]` can be applied to closures, functions, inherent methods, provided trait methods, and trait methods in impl blocks note: the lint level is defined here --> $DIR/warn-unused-inline-on-fn-prototypes.rs:1:9 | @@ -19,7 +19,7 @@ LL | #[inline] | ^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[inline]` can be applied to methods, functions, and closures + = help: `#[inline]` can be applied to closures, functions, and methods error: aborting due to 2 previous errors diff --git a/tests/ui/macros/issue-68060.stderr b/tests/ui/macros/issue-68060.stderr index 4699594a2b002..54a6baaa39369 100644 --- a/tests/ui/macros/issue-68060.stderr +++ b/tests/ui/macros/issue-68060.stderr @@ -4,7 +4,7 @@ error: `#[target_feature]` attribute cannot be used on closures LL | #[target_feature(enable = "")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[target_feature]` can be applied to methods and functions + = help: `#[target_feature]` can be applied to functions and methods error[E0658]: `#[track_caller]` on closures is currently unstable --> $DIR/issue-68060.rs:6:13 diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index d85bccce4410a..eaa26aa3ecafe 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -143,7 +143,7 @@ error: `#[target_feature]` attribute cannot be used on closures LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `#[target_feature]` can be applied to methods and functions + = help: `#[target_feature]` can be applied to functions and methods error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:62:1 diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr index 42a8a1350d2b5..9ebc14c40a145 100644 --- a/tests/ui/where-clauses/unsupported_attribute.stderr +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -48,7 +48,7 @@ error: `#[macro_use]` attribute cannot be used on where predicates LL | #[macro_use] T: Trait, | ^^^^^^^^^^^^ | - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules error: `#[macro_use]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:21:5 @@ -56,7 +56,7 @@ error: `#[macro_use]` attribute cannot be used on where predicates LL | #[macro_use] 'a: 'static, | ^^^^^^^^^^^^ | - = help: `#[macro_use]` can be applied to modules, extern crates, and crates + = help: `#[macro_use]` can be applied to crates, extern crates, and modules error: `#[deprecated]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:24:5 @@ -64,7 +64,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates LL | #[deprecated] T: Trait, | ^^^^^^^^^^^^^ | - = help: `#[deprecated]` can be applied to functions, data types, modules, unions, constants, statics, macro defs, type aliases, use statements, foreign statics, struct fields, traits, associated types, associated consts, enum variants, inherent impl blocks, and crates + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements error: `#[deprecated]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:25:5 @@ -72,7 +72,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates LL | #[deprecated] 'a: 'static, | ^^^^^^^^^^^^^ | - = help: `#[deprecated]` can be applied to functions, data types, modules, unions, constants, statics, macro defs, type aliases, use statements, foreign statics, struct fields, traits, associated types, associated consts, enum variants, inherent impl blocks, and crates + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements error: `#[automatically_derived]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:26:5 From c33b667e8bc6623437c17e85519583796e38a1ae Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:19:07 +1100 Subject: [PATCH 1395/1900] Hoist stranded `use` declarations --- compiler/rustc_middle/src/query/mod.rs | 4 ++-- compiler/rustc_middle/src/query/plumbing.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 895c8c0295a04..30c4b4369830d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -103,6 +103,8 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; +pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -146,11 +148,9 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; mod keys; -pub use keys::{AsLocalKey, Key, LocalCrate}; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 769df1ffd6f91..16cc81dacad4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -9,7 +9,9 @@ use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +pub use sealed::IntoQueryParam; +use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -694,10 +696,6 @@ mod sealed { } } -pub use sealed::IntoQueryParam; - -use super::erase::EraseType; - #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); From 70a357a4c68b3a0c1a457d7a0fa06c295587bbdc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 Oct 2025 09:04:22 +0200 Subject: [PATCH 1396/1900] prefer repeat_n() over repeat().take() --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 785978b4d7111..630e99ad4c74e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -878,7 +878,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .compute_size_in_bytes(layout.size, count) .ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?; - let bytes = std::iter::repeat(byte).take(len.bytes_usize()); + let bytes = std::iter::repeat_n(byte, len.bytes_usize()); self.write_bytes_ptr(dst, bytes) } From e8d57e96725f97fd5e0341197f3e097db580c50e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 8 Oct 2025 16:02:25 +0800 Subject: [PATCH 1397/1900] Add self param completions for trait assoc fn Example --- ```rust trait A { fn foo(file_id: usize) {} fn new($0) {} } ``` **Before this PR**: ```text bn file_id: usize kw mut kw ref ``` **After this PR**: ```text bn &mut self bn &self bn file_id: usize bn mut self bn self kw mut kw ref ``` --- .../crates/ide-completion/src/completions.rs | 4 +- .../ide-completion/src/completions/expr.rs | 2 +- .../src/completions/fn_param.rs | 15 +++---- .../ide-completion/src/completions/pattern.rs | 2 +- .../crates/ide-completion/src/context.rs | 3 +- .../ide-completion/src/context/analysis.rs | 15 ++++--- .../ide-completion/src/tests/fn_param.rs | 42 +++++++++++++++++++ 7 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index eb2bb31f963e1..b822f53d7b7b7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -630,7 +630,7 @@ fn enum_variants_with_paths( acc: &mut Completions, ctx: &CompletionContext<'_>, enum_: hir::Enum, - impl_: &Option, + impl_: Option<&ast::Impl>, cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::Variant, hir::ModPath), ) { let mut process_variant = |variant: Variant| { @@ -644,7 +644,7 @@ fn enum_variants_with_paths( let variants = enum_.variants(ctx.db); - if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) + if let Some(impl_) = impl_.and_then(|impl_| ctx.sema.to_def(impl_)) && impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) { variants.iter().for_each(|variant| process_variant(*variant)); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 080875e016329..4c79357941516 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -297,7 +297,7 @@ pub(crate) fn complete_expr_path( acc, ctx, e, - impl_, + impl_.as_ref(), |acc, ctx, variant, path| { acc.add_qualified_enum_variant(ctx, path_ctx, variant, path) }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index fb78386976d61..34d25c9c67283 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -2,6 +2,7 @@ use hir::HirDisplay; use ide_db::FxHashMap; +use itertools::Either; use syntax::{ AstNode, Direction, SyntaxKind, TextRange, TextSize, algo, ast::{self, HasModuleItem}, @@ -24,8 +25,8 @@ pub(crate) fn complete_fn_param( ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, ) -> Option<()> { - let (ParamContext { param_list, kind, .. }, impl_) = match pattern_ctx { - PatternContext { param_ctx: Some(kind), impl_, .. } => (kind, impl_), + let (ParamContext { param_list, kind, .. }, impl_or_trait) = match pattern_ctx { + PatternContext { param_ctx: Some(kind), impl_or_trait, .. } => (kind, impl_or_trait), _ => return None, }; @@ -45,7 +46,7 @@ pub(crate) fn complete_fn_param( match kind { ParamKind::Function(function) => { - fill_fn_params(ctx, function, param_list, impl_, add_new_item_to_acc); + fill_fn_params(ctx, function, param_list, impl_or_trait, add_new_item_to_acc); } ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; @@ -62,7 +63,7 @@ fn fill_fn_params( ctx: &CompletionContext<'_>, function: &ast::Fn, param_list: &ast::ParamList, - impl_: &Option, + impl_or_trait: &Option>, mut add_new_item_to_acc: impl FnMut(&str), ) { let mut file_params = FxHashMap::default(); @@ -107,7 +108,7 @@ fn fill_fn_params( } remove_duplicated(&mut file_params, param_list.params()); let self_completion_items = ["self", "&self", "mut self", "&mut self"]; - if should_add_self_completions(ctx.token.text_range().start(), param_list, impl_) { + if should_add_self_completions(ctx.token.text_range().start(), param_list, impl_or_trait) { self_completion_items.into_iter().for_each(&mut add_new_item_to_acc); } @@ -161,9 +162,9 @@ fn remove_duplicated( fn should_add_self_completions( cursor: TextSize, param_list: &ast::ParamList, - impl_: &Option, + impl_or_trait: &Option>, ) -> bool { - if impl_.is_none() || param_list.self_param().is_some() { + if impl_or_trait.is_none() || param_list.self_param().is_some() { return false; } match param_list.params().next() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 815ce5145dbec..0ce81d02b4096 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -70,7 +70,7 @@ pub(crate) fn complete_pattern( acc, ctx, e, - &pattern_ctx.impl_, + pattern_ctx.impl_or_trait.as_ref().and_then(|it| it.as_ref().left()), |acc, ctx, variant, path| { acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path); }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 9deaaf6631270..d9b0d3296a964 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -15,6 +15,7 @@ use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, }; +use itertools::Either; use syntax::{ AstNode, Edition, SmolStr, SyntaxKind::{self, *}, @@ -282,7 +283,7 @@ pub(crate) struct PatternContext { pub(crate) mut_token: Option, /// The record pattern this name or ref is a field of pub(crate) record_pat: Option, - pub(crate) impl_: Option, + pub(crate) impl_or_trait: Option>, /// List of missing variants in a match expr pub(crate) missing_variants: Vec, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 9ab7b7c82e432..6cd098462f4db 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1302,7 +1302,8 @@ fn classify_name_ref<'db>( .is_some_and(|it| it.semicolon_token().is_none()) || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw; let in_value = it.parent().and_then(Either::::cast).is_some(); - let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); + let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax()) + .and_then(Either::left); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { Some(arm) => arm @@ -1755,27 +1756,29 @@ fn pattern_context_for( mut_token, ref_token, record_pat: None, - impl_: fetch_immediate_impl(sema, original_file, pat.syntax()), + impl_or_trait: fetch_immediate_impl_or_trait(sema, original_file, pat.syntax()), missing_variants, } } -fn fetch_immediate_impl( +fn fetch_immediate_impl_or_trait( sema: &Semantics<'_, RootDatabase>, original_file: &SyntaxNode, node: &SyntaxNode, -) -> Option { +) -> Option> { let mut ancestors = ancestors_in_file_compensated(sema, original_file, node)? .filter_map(ast::Item::cast) .filter(|it| !matches!(it, ast::Item::MacroCall(_))); match ancestors.next()? { ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (), - ast::Item::Impl(it) => return Some(it), + ast::Item::Impl(it) => return Some(Either::Left(it)), + ast::Item::Trait(it) => return Some(Either::Right(it)), _ => return None, } match ancestors.next()? { - ast::Item::Impl(it) => Some(it), + ast::Item::Impl(it) => Some(Either::Left(it)), + ast::Item::Trait(it) => Some(Either::Right(it)), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs index 451ce07c7454b..02cba6b6467e5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs @@ -85,7 +85,11 @@ pub(crate) trait SourceRoot { } "#, expect![[r#" + bn &mut self + bn &self bn file_id: usize + bn mut self + bn self kw mut kw ref "#]], @@ -183,6 +187,44 @@ impl A { ) } +#[test] +fn in_trait_only_param() { + check( + r#" +trait A { + fn foo(file_id: usize) {} + fn new($0) {} +} +"#, + expect![[r#" + bn &mut self + bn &self + bn file_id: usize + bn mut self + bn self + kw mut + kw ref + "#]], + ) +} + +#[test] +fn in_trait_after_self() { + check( + r#" +trait A { + fn foo(file_id: usize) {} + fn new(self, $0) {} +} +"#, + expect![[r#" + bn file_id: usize + kw mut + kw ref + "#]], + ) +} + // doesn't complete qux due to there being no expression after // see source_analyzer::adjust comment #[test] From ec99e3eca22bf97b1bed90173f0095ffa38bde75 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 15 Jul 2025 23:24:44 -0700 Subject: [PATCH 1398/1900] clarify wording of match ergonomics diagnostics --- compiler/rustc_hir_typeck/src/pat.rs | 21 +- compiler/rustc_lint_defs/src/builtin.rs | 2 +- .../rustc_middle/src/ty/typeck_results.rs | 6 +- compiler/rustc_mir_build/messages.ftl | 11 - compiler/rustc_mir_build/src/errors.rs | 66 +-- .../src/thir/pattern/migration.rs | 112 +++-- .../mixed-editions.classic2021.stderr | 10 +- .../mixed-editions.classic2024.stderr | 8 +- .../experimental/mixed-editions.rs | 4 +- .../mixed-editions.structural2021.stderr | 10 +- .../mixed-editions.structural2024.stderr | 8 +- ...nding-on-inh-ref-errors.classic2024.stderr | 48 +- .../ref-binding-on-inh-ref-errors.rs | 22 +- ...ng-on-inh-ref-errors.structural2024.stderr | 132 ++--- .../migration_lint.fixed | 82 ++-- .../migration_lint.rs | 82 ++-- .../migration_lint.stderr | 459 ++++++++++-------- .../min_match_ergonomics_fail.rs | 14 +- .../min_match_ergonomics_fail.stderr | 80 +-- 19 files changed, 629 insertions(+), 548 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 46accb76a184a..d14463e44a03f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -3115,20 +3115,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // binding mode. This keeps it from making those suggestions, as doing so could panic. let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo { primary_labels: Vec::new(), - bad_modifiers: false, + bad_ref_modifiers: false, + bad_mut_modifiers: false, bad_ref_pats: false, suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024() && !self.tcx.features().ref_pat_eat_one_layer_2024_structural(), }); let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind { - info.bad_modifiers = true; // If the user-provided binding modifier doesn't match the default binding mode, we'll // need to suggest reference patterns, which can affect other bindings. // For simplicity, we opt to suggest making the pattern fully explicit. info.suggest_eliding_modes &= user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not); - "binding modifier" + if user_bind_annot == BindingMode(ByRef::No, Mutability::Mut) { + info.bad_mut_modifiers = true; + "`mut` binding modifier" + } else { + info.bad_ref_modifiers = true; + match user_bind_annot.1 { + Mutability::Not => "explicit `ref` binding modifier", + Mutability::Mut => "explicit `ref mut` binding modifier", + } + } } else { info.bad_ref_pats = true; // For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll @@ -3147,11 +3156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so, we may want to inspect the span's source callee or macro backtrace. "occurs within macro expansion".to_owned() } else { - let dbm_str = match def_br_mutbl { - Mutability::Not => "ref", - Mutability::Mut => "ref mut", - }; - format!("{pat_kind} not allowed under `{dbm_str}` default binding mode") + format!("{pat_kind} not allowed when implicitly borrowing") }; info.primary_labels.push((trimmed_span, primary_label)); } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 939f3d088b12f..8b7b13a4c3622 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1601,7 +1601,7 @@ declare_lint! { "detects patterns whose meaning will change in Rust 2024", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "", + reference: "", }; } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index b276b993ec924..d1fb700913d91 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -858,8 +858,10 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> { pub struct Rust2024IncompatiblePatInfo { /// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024. pub primary_labels: Vec<(Span, String)>, - /// Whether any binding modifiers occur under a non-`move` default binding mode. - pub bad_modifiers: bool, + /// Whether any `mut` binding modifiers occur under a non-`move` default binding mode. + pub bad_mut_modifiers: bool, + /// Whether any `ref`/`ref mut` binding modifiers occur under a non-`move` default binding mode. + pub bad_ref_modifiers: bool, /// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode. pub bad_ref_pats: bool, /// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers. diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 83fbcb30dd941..e84e42b8a44b5 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -322,17 +322,6 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future -mir_build_rust_2024_incompatible_pat = {$bad_modifiers -> - *[true] binding modifiers{$bad_ref_pats -> - *[true] {" "}and reference patterns - [false] {""} - } - [false] reference patterns - } may only be written when the default binding mode is `move`{$is_hard_error -> - *[true] {""} - [false] {" "}in Rust 2024 - } - mir_build_static_in_pattern = statics cannot be referenced in patterns .label = can't be used in patterns mir_build_static_in_pattern_def = `static` defined here diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 58c3de4a8b59c..e2aae5b6283f2 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,8 +1,7 @@ -use rustc_data_structures::fx::FxIndexMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - MultiSpan, Subdiagnostic, pluralize, + MultiSpan, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -1087,69 +1086,6 @@ pub(crate) enum MiscPatternSuggestion { }, } -#[derive(LintDiagnostic)] -#[diag(mir_build_rust_2024_incompatible_pat)] -pub(crate) struct Rust2024IncompatiblePat { - #[subdiagnostic] - pub(crate) sugg: Rust2024IncompatiblePatSugg, - pub(crate) bad_modifiers: bool, - pub(crate) bad_ref_pats: bool, - pub(crate) is_hard_error: bool, -} - -pub(crate) struct Rust2024IncompatiblePatSugg { - /// If true, our suggestion is to elide explicit binding modifiers. - /// If false, our suggestion is to make the pattern fully explicit. - pub(crate) suggest_eliding_modes: bool, - pub(crate) suggestion: Vec<(Span, String)>, - pub(crate) ref_pattern_count: usize, - pub(crate) binding_mode_count: usize, - /// Labels for where incompatibility-causing by-ref default binding modes were introduced. - pub(crate) default_mode_labels: FxIndexMap, -} - -impl Subdiagnostic for Rust2024IncompatiblePatSugg { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - // Format and emit explanatory notes about default binding modes. Reversing the spans' order - // means if we have nested spans, the innermost ones will be visited first. - for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { - // Don't point to a macro call site. - if !span.from_expansion() { - let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode"; - let label_msg = - format!("this matches on type `{}_`", def_br_mutbl.ref_prefix_str()); - let mut label = MultiSpan::from(span); - label.push_span_label(span, label_msg); - diag.span_note(label, note_msg); - } - } - - // Format and emit the suggestion. - let applicability = - if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - let msg = if self.suggest_eliding_modes { - let plural_modes = pluralize!(self.binding_mode_count); - format!("remove the unnecessary binding modifier{plural_modes}") - } else { - let plural_derefs = pluralize!(self.ref_pattern_count); - let and_modes = if self.binding_mode_count > 0 { - format!(" and variable binding mode{}", pluralize!(self.binding_mode_count)) - } else { - String::new() - }; - format!("make the implied reference pattern{plural_derefs}{and_modes} explicit") - }; - // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!) - if !self.suggestion.is_empty() { - diag.multipart_suggestion_verbose(msg, self.suggestion, applicability); - } - } -} - #[derive(Diagnostic)] #[diag(mir_build_loop_match_invalid_update)] pub(crate) struct LoopMatchInvalidUpdate { diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index 12c457f13fc12..8887308530506 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -1,15 +1,12 @@ //! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024. use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::MultiSpan; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, pluralize}; use rustc_hir::{BindingMode, ByRef, HirId, Mutability}; use rustc_lint as lint; use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt}; use rustc_span::{Ident, Span}; -use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg}; -use crate::fluent_generated as fluent; - /// For patterns flagged for migration during HIR typeck, this handles constructing and emitting /// a diagnostic suggestion. pub(super) struct PatMigration<'a> { @@ -49,39 +46,90 @@ impl<'a> PatMigration<'a> { for (span, label) in self.info.primary_labels.iter() { spans.push_span_label(*span, label.clone()); } - let sugg = Rust2024IncompatiblePatSugg { - suggest_eliding_modes: self.info.suggest_eliding_modes, - suggestion: self.suggestion, - ref_pattern_count: self.ref_pattern_count, - binding_mode_count: self.binding_mode_count, - default_mode_labels: self.default_mode_labels, - }; // If a relevant span is from at least edition 2024, this is a hard error. let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024()); + let primary_message = self.primary_message(is_hard_error); if is_hard_error { - let mut err = - tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat); - if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible { - // provide the same reference link as the lint - err.note(format!("for more information, see {}", info.reference)); - } - err.arg("bad_modifiers", self.info.bad_modifiers); - err.arg("bad_ref_pats", self.info.bad_ref_pats); - err.arg("is_hard_error", true); - err.subdiagnostic(sugg); + let mut err = tcx.dcx().struct_span_err(spans, primary_message); + err.note("for more information, see "); + self.format_subdiagnostics(&mut err); err.emit(); } else { - tcx.emit_node_span_lint( - lint::builtin::RUST_2024_INCOMPATIBLE_PAT, - pat_id, - spans, - Rust2024IncompatiblePat { - sugg, - bad_modifiers: self.info.bad_modifiers, - bad_ref_pats: self.info.bad_ref_pats, - is_hard_error, - }, - ); + tcx.node_span_lint(lint::builtin::RUST_2024_INCOMPATIBLE_PAT, pat_id, spans, |diag| { + diag.primary_message(primary_message); + self.format_subdiagnostics(diag); + }); + } + } + + fn primary_message(&self, is_hard_error: bool) -> String { + let verb1 = match (self.info.bad_mut_modifiers, self.info.bad_ref_modifiers) { + (true, true) => "write explicit binding modifiers", + (true, false) => "mutably bind by value", + (false, true) => "explicitly borrow", + (false, false) => "explicitly dereference", + }; + let or_verb2 = match ( + self.info.bad_mut_modifiers, + self.info.bad_ref_modifiers, + self.info.bad_ref_pats, + ) { + // We only need two verb phrases if mentioning both modifiers and reference patterns. + (false, false, _) | (_, _, false) => "", + // If mentioning `mut`, we don't have an "explicitly" yet. + (true, _, true) => " or explicitly dereference", + // If mentioning `ref`/`ref mut` but not `mut`, we already have an "explicitly". + (false, true, true) => " or dereference", + }; + let in_rust_2024 = if is_hard_error { "" } else { " in Rust 2024" }; + format!("cannot {verb1}{or_verb2} within an implicitly-borrowing pattern{in_rust_2024}") + } + + fn format_subdiagnostics(self, diag: &mut Diag<'_, impl EmissionGuarantee>) { + // Format and emit explanatory notes about default binding modes. Reversing the spans' order + // means if we have nested spans, the innermost ones will be visited first. + for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { + // Don't point to a macro call site. + if !span.from_expansion() { + let note_msg = "matching on a reference type with a non-reference pattern implicitly borrows the contents"; + let label_msg = format!( + "this non-reference pattern matches on a reference type `{}_`", + def_br_mutbl.ref_prefix_str() + ); + let mut label = MultiSpan::from(span); + label.push_span_label(span, label_msg); + diag.span_note(label, note_msg); + } + } + + // Format and emit the suggestion. + let applicability = + if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + let plural_modes = pluralize!(self.binding_mode_count); + let msg = if self.info.suggest_eliding_modes { + format!("remove the unnecessary binding modifier{plural_modes}") + } else { + let match_on_these_references = if self.ref_pattern_count == 1 { + "match on the reference with a reference pattern" + } else { + "match on these references with reference patterns" + }; + let and_explain_modes = if self.binding_mode_count > 0 { + let a = if self.binding_mode_count == 1 { "a " } else { "" }; + format!(" and borrow explicitly using {a}variable binding mode{plural_modes}") + } else { + " to avoid implicitly borrowing".to_owned() + }; + format!("{match_on_these_references}{and_explain_modes}") + }; + // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!) + debug_assert!(!self.suggestion.is_empty()); + if !self.suggestion.is_empty() { + diag.multipart_suggestion_verbose(msg, self.suggestion, applicability); } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2021.stderr index 7e3caaf979748..fc1ca18c12026 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2021.stderr @@ -59,20 +59,20 @@ LL | let [&bind_ref_mut!(x)] = &mut [0]; | | | help: replace this `&` with `&mut`: `&mut` -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/mixed-editions.rs:30:10 | LL | let [bind_ref!(y)] = &[0]; | ^^^^^^^^^^^^ occurs within macro expansion | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/mixed-editions.rs:30:9 | LL | let [bind_ref!(y)] = &[0]; - | ^^^^^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` = note: this error originates in the macro `bind_ref` (in Nightly builds, run with -Z macro-backtrace for more info) -help: make the implied reference pattern explicit +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[bind_ref!(y)] = &[0]; | + diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2024.stderr index 466993a1671f4..9377d535f1874 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2024.stderr @@ -58,14 +58,14 @@ LL | let [&bind_ref_mut!(x)] = &mut [0]; | | | help: replace this `&` with `&mut`: `&mut` -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/mixed-editions.rs:26:21 | LL | let match_ctor!(ref x) = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -help: make the implied reference pattern explicit + = note: for more information, see +help: match on the reference with a reference pattern to avoid implicitly borrowing --> $DIR/auxiliary/mixed-editions-macros.rs:11:9 | LL | &[$p] diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.rs index 0a22b55ab6374..3580ed3862703 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.rs @@ -24,11 +24,11 @@ fn assert_type_eq>(_: T, _: U) {} /// only when the binding is from edition 2024. fn ref_binding_tests() { let match_ctor!(ref x) = &[0]; - //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2024,structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(classic2021, structural2021))] assert_type_eq(x, &0u32); let [bind_ref!(y)] = &[0]; - //[classic2021,structural2021]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2021,structural2021]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(classic2024, structural2024))] assert_type_eq(y, &0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2021.stderr index 4075dc9529da1..69ddb0720526e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2021.stderr @@ -37,20 +37,20 @@ LL | let [&bind_ref_mut!(x)] = &mut [0]; | | | help: replace this `&` with `&mut`: `&mut` -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/mixed-editions.rs:30:10 | LL | let [bind_ref!(y)] = &[0]; | ^^^^^^^^^^^^ occurs within macro expansion | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/mixed-editions.rs:30:9 | LL | let [bind_ref!(y)] = &[0]; - | ^^^^^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` = note: this error originates in the macro `bind_ref` (in Nightly builds, run with -Z macro-backtrace for more info) -help: make the implied reference pattern explicit +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[bind_ref!(y)] = &[0]; | + diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2024.stderr index 819a54299ea1c..bc1fee35a6bb4 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2024.stderr @@ -36,14 +36,14 @@ LL | let [&bind_ref_mut!(x)] = &mut [0]; | | | help: replace this `&` with `&mut`: `&mut` -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/mixed-editions.rs:26:21 | LL | let match_ctor!(ref x) = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -help: make the implied reference pattern explicit + = note: for more information, see +help: match on the reference with a reference pattern to avoid implicitly borrowing --> $DIR/auxiliary/mixed-editions-macros.rs:11:9 | LL | &[$p] diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr index 04e53e06a22e6..956cd2166433a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr @@ -11,19 +11,19 @@ LL - let [&mut ref x] = &[&mut 0]; LL + let [&ref x] = &[&mut 0]; | -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; - | ^^^^^^^ binding modifier not allowed under `ref` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:73:9 | LL | let [ref mut x] = &[0]; - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[ref mut x] = &[0]; | + @@ -34,53 +34,53 @@ error[E0596]: cannot borrow data in a `&` reference as mutable LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:81:10 | LL | let [ref x] = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:81:9 | LL | let [ref x] = &[0]; - | ^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[ref x] = &[0]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:85:10 | LL | let [ref x] = &mut [0]; - | ^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:85:9 | LL | let [ref x] = &mut [0]; - | ^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [ref x] = &mut [0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:89:10 | LL | let [ref mut x] = &mut [0]; - | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:89:9 | LL | let [ref mut x] = &mut [0]; - | ^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [ref mut x] = &mut [0]; | ++++ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs index c9e3f75cf178a..628cb60b8495c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -17,22 +17,22 @@ /// The eat-outer variant eats the inherited reference, so binding with `ref` isn't a problem. fn errors_from_eating_the_real_reference() { let [&ref x] = &[&0]; - //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&u32 = x; let [&ref x] = &mut [&0]; - //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&u32 = x; let [&mut ref x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; let [&mut ref mut x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &mut u32 = x; #[cfg(classic2024)] let _: &mut &mut u32 = x; } @@ -43,14 +43,14 @@ fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { let [&ref x] = &[&mut 0]; //[stable2021]~^ ERROR: mismatched types //[stable2021]~| NOTE types differ in mutability - //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^^^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; let [&ref x] = &mut [&mut 0]; //[stable2021]~^ ERROR: mismatched types //[stable2021]~| NOTE types differ in mutability - //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^^^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; } @@ -60,7 +60,7 @@ fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() { let [&mut ref x] = &[&mut 0]; //[classic2024]~^ ERROR: mismatched types //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern - //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[structural2024]~^^^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; } @@ -72,21 +72,21 @@ fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() { fn borrowck_errors_in_old_editions() { let [ref mut x] = &[0]; //~^ ERROR: cannot borrow data in a `&` reference as mutable - //[classic2024,structural2024]~| ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2024,structural2024]~| ERROR: cannot explicitly borrow within an implicitly-borrowing pattern } /// The remaining tests are purely for testing `ref` bindings in the presence of an inherited /// reference. These should always fail on edition 2024 and succeed on edition 2021. pub fn main() { let [ref x] = &[0]; - //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2024,structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; let [ref x] = &mut [0]; - //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2024,structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; let [ref mut x] = &mut [0]; - //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //[classic2024,structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr index def6deb325acd..9753e3e5fbfea 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -1,135 +1,135 @@ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:19:11 | LL | let [&ref x] = &[&0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:19:9 | LL | let [&ref x] = &[&0]; - | ^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[&ref x] = &[&0]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:24:11 | LL | let [&ref x] = &mut [&0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:24:9 | LL | let [&ref x] = &mut [&0]; - | ^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [&ref x] = &mut [&0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:29:15 | LL | let [&mut ref x] = &mut [&mut 0]; - | ^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:29:9 | LL | let [&mut ref x] = &mut [&mut 0]; - | ^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [&mut ref x] = &mut [&mut 0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:34:15 | LL | let [&mut ref mut x] = &mut [&mut 0]; - | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:34:9 | LL | let [&mut ref mut x] = &mut [&mut 0]; - | ^^^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [&mut ref mut x] = &mut [&mut 0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:43:11 | LL | let [&ref x] = &[&mut 0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:43:9 | LL | let [&ref x] = &[&mut 0]; - | ^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[&ref x] = &[&mut 0]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:50:11 | LL | let [&ref x] = &mut [&mut 0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:50:9 | LL | let [&ref x] = &mut [&mut 0]; - | ^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [&ref x] = &mut [&mut 0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:60:15 | LL | let [&mut ref x] = &[&mut 0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:60:9 | LL | let [&mut ref x] = &[&mut 0]; - | ^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[&mut ref x] = &[&mut 0]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; - | ^^^^^^^ binding modifier not allowed under `ref` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:73:9 | LL | let [ref mut x] = &[0]; - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[ref mut x] = &[0]; | + @@ -140,53 +140,53 @@ error[E0596]: cannot borrow data in a `&` reference as mutable LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:81:10 | LL | let [ref x] = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:81:9 | LL | let [ref x] = &[0]; - | ^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[ref x] = &[0]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:85:10 | LL | let [ref x] = &mut [0]; - | ^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:85:9 | LL | let [ref x] = &mut [0]; - | ^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [ref x] = &mut [0]; | ++++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/ref-binding-on-inh-ref-errors.rs:89:10 | LL | let [ref mut x] = &mut [0]; - | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/ref-binding-on-inh-ref-errors.rs:89:9 | LL | let [ref mut x] = &mut [0]; - | ^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut [ref mut x] = &mut [0]; | ++++ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed index bb4ecc09063b7..2df39fdd6103d 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let &Foo(mut x) = &Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(mut x) = &mut Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(x) = &Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let &mut Foo(ref x) = &mut Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let &Foo(&x) = &Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&x) = &mut Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let &&&&&Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,7 +135,7 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } @@ -143,26 +143,26 @@ fn main() { } let &mut [&mut &[ref a]] = &mut [&mut &[0]]; - //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); let &[&(_)] = &[&0]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 // NB: Most of the following tests are for possible future improvements to migration suggestions // Test removing multiple binding modifiers. let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(c, &0u32); // Test that we don't change bindings' modes when removing binding modifiers. let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &mut 0u32); @@ -170,7 +170,7 @@ fn main() { // Test removing multiple reference patterns of various mutabilities, plus a binding modifier. let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -178,13 +178,13 @@ fn main() { // Test that we don't change bindings' types when removing reference patterns. let &Foo(&ref a) = &Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); // Test that we don't change bindings' modes when adding reference paterns (caught early). let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); @@ -194,7 +194,7 @@ fn main() { // Test that we don't change bindings' modes when adding reference patterns (caught late). let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -202,7 +202,7 @@ fn main() { // Test featuring both additions and removals. let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); @@ -210,21 +210,21 @@ fn main() { // Test that bindings' subpatterns' modes are updated properly. let &[mut a @ ref b] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); // Test that bindings' subpatterns' modes are checked properly. let &[ref a @ mut b] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`. let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -233,7 +233,7 @@ fn main() { // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`. let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &[0u32]); assert_type_eq(b, &0u32); @@ -242,12 +242,32 @@ fn main() { // Test that we use the correct message and suggestion style when pointing inside expansions. let &[migration_lint_macros::bind_ref!(a)] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern assert_type_eq(a, &0u32); // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion. let &[&migration_lint_macros::bind_ref!(a)] = &[&0]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); + + // Test the primary diagnostic message for mixes of `mut`/`ref`/`&`. + let &(mut a, ref b) = &(0, 0); + //~^ ERROR: cannot write explicit binding modifiers within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + + let &(mut a, &b) = &(0, &0); + //~^ ERROR: cannot mutably bind by value or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, 0u32); + + let &(mut a, ref b, &c) = &(0, 0, &0); + //~^ ERROR: cannot write explicit binding modifiers or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs index 2837c8d81dbdd..0d727ad7d7a5c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let Foo(mut x) = &Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(mut x) = &mut Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(ref x) = &Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let Foo(ref x) = &mut Foo(0); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let Foo(&x) = &Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&x) = &mut Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,7 +135,7 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } @@ -143,26 +143,26 @@ fn main() { } let [&mut [ref a]] = &mut [&mut &[0]]; - //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); let [&(_)] = &[&0]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 // NB: Most of the following tests are for possible future improvements to migration suggestions // Test removing multiple binding modifiers. let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(c, &0u32); // Test that we don't change bindings' modes when removing binding modifiers. let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &mut 0u32); @@ -170,7 +170,7 @@ fn main() { // Test removing multiple reference patterns of various mutabilities, plus a binding modifier. let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -178,13 +178,13 @@ fn main() { // Test that we don't change bindings' types when removing reference patterns. let Foo(&ref a) = &Foo(&0); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); // Test that we don't change bindings' modes when adding reference paterns (caught early). let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); @@ -194,7 +194,7 @@ fn main() { // Test that we don't change bindings' modes when adding reference patterns (caught late). let (a, [b], [mut c]) = &(0, &mut [0], &[0]); - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -202,7 +202,7 @@ fn main() { // Test featuring both additions and removals. let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); @@ -210,21 +210,21 @@ fn main() { // Test that bindings' subpatterns' modes are updated properly. let [mut a @ b] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &0u32); // Test that bindings' subpatterns' modes are checked properly. let [a @ mut b] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`. let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, &0u32); @@ -233,7 +233,7 @@ fn main() { // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`. let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &[0u32]); assert_type_eq(b, &0u32); @@ -242,12 +242,32 @@ fn main() { // Test that we use the correct message and suggestion style when pointing inside expansions. let [migration_lint_macros::bind_ref!(a)] = &[0]; - //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern assert_type_eq(a, &0u32); // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion. let [&migration_lint_macros::bind_ref!(a)] = &[&0]; - //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); + + // Test the primary diagnostic message for mixes of `mut`/`ref`/`&`. + let (mut a, ref b) = &(0, 0); + //~^ ERROR: cannot write explicit binding modifiers within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + + let (mut a, &b) = &(0, &0); + //~^ ERROR: cannot mutably bind by value or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, 0u32); + + let (mut a, ref b, &c) = &(0, 0, &0); + //~^ ERROR: cannot write explicit binding modifiers or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr index 6efda4f757ff6..01fcfdc8be6ee 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr @@ -1,602 +1,663 @@ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:25:13 | LL | let Foo(mut x) = &Foo(0); - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:25:9 | LL | let Foo(mut x) = &Foo(0); - | ^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` note: the lint level is defined here --> $DIR/migration_lint.rs:7:9 | LL | #![deny(rust_2024_incompatible_pat)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: make the implied reference pattern explicit +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &Foo(mut x) = &Foo(0); | + -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:30:13 | LL | let Foo(mut x) = &mut Foo(0); - | ^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:30:9 | LL | let Foo(mut x) = &mut Foo(0); - | ^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut Foo(mut x) = &mut Foo(0); | ++++ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:35:13 | LL | let Foo(ref x) = &Foo(0); - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:35:9 | LL | let Foo(ref x) = &Foo(0); - | ^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` help: remove the unnecessary binding modifier | LL - let Foo(ref x) = &Foo(0); LL + let Foo(x) = &Foo(0); | -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:40:13 | LL | let Foo(ref x) = &mut Foo(0); - | ^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:40:9 | LL | let Foo(ref x) = &mut Foo(0); - | ^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut Foo(ref x) = &mut Foo(0); | ++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:57:13 | LL | let Foo(&x) = &Foo(&0); - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:57:9 | LL | let Foo(&x) = &Foo(&0); - | ^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &Foo(&x) = &Foo(&0); | + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:62:13 | LL | let Foo(&mut x) = &Foo(&mut 0); - | ^^^^ reference pattern not allowed under `ref` default binding mode + | ^^^^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:62:9 | LL | let Foo(&mut x) = &Foo(&mut 0); - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &Foo(&mut x) = &Foo(&mut 0); | + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:67:13 | LL | let Foo(&x) = &mut Foo(&0); - | ^ reference pattern not allowed under `ref mut` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:67:9 | LL | let Foo(&x) = &mut Foo(&0); - | ^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut Foo(&x) = &mut Foo(&0); | ++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:72:13 | LL | let Foo(&mut x) = &mut Foo(&mut 0); - | ^^^^ reference pattern not allowed under `ref mut` default binding mode + | ^^^^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:72:9 | LL | let Foo(&mut x) = &mut Foo(&mut 0); - | ^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &mut Foo(&mut x) = &mut Foo(&mut 0); | ++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:81:17 | LL | if let Some(&x) = &&&&&Some(&0u8) { - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:81:12 | LL | if let Some(&x) = &&&&&Some(&0u8) { - | ^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | if let &&&&&Some(&x) = &&&&&Some(&0u8) { | +++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:87:17 | LL | if let Some(&mut x) = &&&&&Some(&mut 0u8) { - | ^^^^ reference pattern not allowed under `ref` default binding mode + | ^^^^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:87:12 | LL | if let Some(&mut x) = &&&&&Some(&mut 0u8) { - | ^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit + | ^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { | +++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:93:17 | LL | if let Some(&x) = &&&&&mut Some(&0u8) { - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:93:12 | LL | if let Some(&x) = &&&&&mut Some(&0u8) { - | ^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { | ++++++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:99:17 | LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - | ^^^^ reference pattern not allowed under `ref mut` default binding mode + | ^^^^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:99:12 | LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference patterns and variable binding mode explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on these references with reference patterns and borrow explicitly using a variable binding mode | LL | if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { | ++++ ++++ +++++++ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:111:21 | LL | let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:111:9 | LL | let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern and variable binding modes explicit + | ^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern and borrow explicitly using variable binding modes | LL | let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; | + +++ +++ -error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:117:21 | LL | let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - | ^ ^^^ binding modifier not allowed under `ref` default binding mode + | ^ ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:117:9 | LL | let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern and variable binding mode explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern and borrow explicitly using a variable binding mode | LL | let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; | + +++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:124:24 | LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - | ^ ^ reference pattern not allowed under `ref` default binding mode + | ^ ^ reference pattern not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:124:12 | LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding mode explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns and borrow explicitly using a variable binding mode | LL | if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = | + + + +++ -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot mutably bind by value within an implicitly-borrowing pattern --> $DIR/migration_lint.rs:137:15 | LL | (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion | | - | binding modifier not allowed under `ref` default binding mode + | `mut` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:137:9 | LL | (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` = note: this error originates in the macro `migration_lint_macros::mixed_edition_pat` (in Nightly builds, run with -Z macro-backtrace for more info) -help: make the implied reference pattern explicit +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { | + -error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:145:10 | LL | let [&mut [ref a]] = &mut [&mut &[0]]; - | ^^^^ ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^^ ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref mut` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:145:15 | LL | let [&mut [ref a]] = &mut [&mut &[0]]; - | ^^^^^^^ this matches on type `&_` -note: matching on a reference type with a non-reference pattern changes the default binding mode + | ^^^^^^^ this non-reference pattern matches on a reference type `&_` +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:145:9 | LL | let [&mut [ref a]] = &mut [&mut &[0]]; - | ^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference patterns explicit + | ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | let &mut [&mut &[ref a]] = &mut [&mut &[0]]; | ++++ + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:150:10 | LL | let [&(_)] = &[&0]; - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:150:9 | LL | let [&(_)] = &[&0]; - | ^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[&(_)] = &[&0]; | + -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:157:18 | LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^ ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | | - | binding modifier not allowed under `ref` default binding mode + | explicit `ref` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:157:9 | LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` help: remove the unnecessary binding modifiers | LL - let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; LL + let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; | -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:164:18 | LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; - | ^^^ ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^ ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | | - | binding modifier not allowed under `ref mut` default binding mode + | explicit `ref` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:164:9 | LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern and variable binding mode explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` +help: match on the reference with a reference pattern and borrow explicitly using a variable binding mode | LL | let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 }; | ++++ +++++++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:172:21 | LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; - | ^ ^^^^ reference pattern not allowed under `ref` default binding mode + | ^ ^^^^ reference pattern not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:172:9 | LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding modes explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns and borrow explicitly using variable binding modes | LL | let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; | ++++++ + +++ +++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:180:13 | LL | let Foo(&ref a) = &Foo(&0); - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:180:9 | LL | let Foo(&ref a) = &Foo(&0); - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &Foo(&ref a) = &Foo(&0); | + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:186:10 | LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:186:9 | LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); - | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding modes explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns and borrow explicitly using variable binding modes | LL | let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); | + +++ + +++ ++++ ++++ +++ + +++ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:196:19 | LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:196:9 | LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); - | ^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding modes explicit + | ^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns and borrow explicitly using variable binding modes | LL | let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]); | + +++ ++++ +++ + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:204:10 | LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); - | ^ ^ reference pattern not allowed under `ref` default binding mode + | ^ ^ reference pattern not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:204:9 | LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); - | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding mode explicit + | ^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns and borrow explicitly using a variable binding mode | LL | let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); | + ++++ +++ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:212:10 | LL | let [mut a @ b] = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:212:9 | LL | let [mut a @ b] = &[0]; - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern and variable binding mode explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern and borrow explicitly using a variable binding mode | LL | let &[mut a @ ref b] = &[0]; | + +++ -error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 +error: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:219:14 | LL | let [a @ mut b] = &[0]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:219:9 | LL | let [a @ mut b] = &[0]; - | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern and variable binding mode explicit + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern and borrow explicitly using a variable binding mode | LL | let &[ref a @ mut b] = &[0]; | + +++ -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:226:14 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; - | ^ ^ reference pattern not allowed under `ref` default binding mode + | ^ ^ reference pattern not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:226:31 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; - | ^^^^^^^^^^^^^^^ this matches on type `&_` -note: matching on a reference type with a non-reference pattern changes the default binding mode + | ^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:226:10 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; - | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit + | ^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; | + + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:235:14 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - | ^ ^ reference pattern not allowed under `ref` default binding mode + | ^ ^ reference pattern not allowed when implicitly borrowing | | - | reference pattern not allowed under `ref` default binding mode + | reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:235:33 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - | ^^^^^^^^^^^^^^^^^ this matches on type `&_` -note: matching on a reference type with a non-reference pattern changes the default binding mode + | ^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:235:10 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - | ^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit + | ^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on these references with reference patterns to avoid implicitly borrowing | LL | let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; | + + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/migration_lint.rs:244:10 | LL | let [migration_lint_macros::bind_ref!(a)] = &[0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:244:9 | LL | let [migration_lint_macros::bind_ref!(a)] = &[0]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` = note: this error originates in the macro `migration_lint_macros::bind_ref` (in Nightly builds, run with -Z macro-backtrace for more info) -help: make the implied reference pattern explicit +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[migration_lint_macros::bind_ref!(a)] = &[0]; | + -error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 +error: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024 --> $DIR/migration_lint.rs:249:10 | LL | let [&migration_lint_macros::bind_ref!(a)] = &[&0]; - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | = warning: this changes meaning in Rust 2024 - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/migration_lint.rs:249:9 | LL | let [&migration_lint_macros::bind_ref!(a)] = &[&0]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | let &[&migration_lint_macros::bind_ref!(a)] = &[&0]; | + -error: aborting due to 31 previous errors +error: cannot write explicit binding modifiers within an implicitly-borrowing pattern in Rust 2024 + --> $DIR/migration_lint.rs:255:10 + | +LL | let (mut a, ref b) = &(0, 0); + | ^^^ ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing + | | + | `mut` binding modifier not allowed when implicitly borrowing + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents + --> $DIR/migration_lint.rs:255:9 + | +LL | let (mut a, ref b) = &(0, 0); + | ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing + | +LL | let &(mut a, ref b) = &(0, 0); + | + + +error: cannot mutably bind by value or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + --> $DIR/migration_lint.rs:261:10 + | +LL | let (mut a, &b) = &(0, &0); + | ^^^ ^ reference pattern not allowed when implicitly borrowing + | | + | `mut` binding modifier not allowed when implicitly borrowing + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents + --> $DIR/migration_lint.rs:261:9 + | +LL | let (mut a, &b) = &(0, &0); + | ^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing + | +LL | let &(mut a, &b) = &(0, &0); + | + + +error: cannot write explicit binding modifiers or explicitly dereference within an implicitly-borrowing pattern in Rust 2024 + --> $DIR/migration_lint.rs:267:10 + | +LL | let (mut a, ref b, &c) = &(0, 0, &0); + | ^^^ ^^^ ^ reference pattern not allowed when implicitly borrowing + | | | + | | explicit `ref` binding modifier not allowed when implicitly borrowing + | `mut` binding modifier not allowed when implicitly borrowing + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents + --> $DIR/migration_lint.rs:267:9 + | +LL | let (mut a, ref b, &c) = &(0, 0, &0); + | ^^^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing + | +LL | let &(mut a, ref b, &c) = &(0, 0, &0); + | + + +error: aborting due to 34 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs index 4dc04d90aaf5e..d56b835ac5dfc 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs @@ -21,17 +21,17 @@ macro_rules! test_pat_on_type { } test_pat_on_type![(&x,): &(T,)]; //~ ERROR mismatched types -test_pat_on_type![(&x,): &(&T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move` +test_pat_on_type![(&x,): &(&T,)]; //~ ERROR cannot explicitly dereference within an implicitly-borrowing pattern test_pat_on_type![(&x,): &(&mut T,)]; //~ ERROR mismatched types test_pat_on_type![(&mut x,): &(&T,)]; //~ ERROR mismatched types -test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move` +test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR cannot explicitly dereference within an implicitly-borrowing pattern test_pat_on_type![(&x,): &&mut &(T,)]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: Foo]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; //~ ERROR mismatched types -test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR reference patterns may only be written when the default binding mode is `move` -test_pat_on_type![(mut x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` -test_pat_on_type![(ref x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` -test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` +test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR cannot explicitly dereference within an implicitly-borrowing pattern +test_pat_on_type![(mut x,): &(T,)]; //~ ERROR cannot mutably bind by value within an implicitly-borrowing pattern +test_pat_on_type![(ref x,): &(T,)]; //~ ERROR cannot explicitly borrow within an implicitly-borrowing pattern +test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR cannot explicitly borrow within an implicitly-borrowing pattern fn get() -> X { unimplemented!() @@ -40,6 +40,6 @@ fn get() -> X { // Make sure this works even when the underlying type is inferred. This test passes on rust stable. fn infer() -> X { match &get() { - (&x,) => x, //~ ERROR reference patterns may only be written when the default binding mode is `move` + (&x,) => x, //~ ERROR cannot explicitly dereference within an implicitly-borrowing pattern } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr index 0c6b2ff3a2f09..23f1557e56d39 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr @@ -99,123 +99,123 @@ LL - test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; LL + test_pat_on_type![Foo { f: (x,) }: &mut Foo]; | -error: reference patterns may only be written when the default binding mode is `move` +error: cannot explicitly dereference within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:24:20 | LL | test_pat_on_type![(&x,): &(&T,)]; - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:24:19 | LL | test_pat_on_type![(&x,): &(&T,)]; - | ^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | test_pat_on_type![&(&x,): &(&T,)]; | + -error: reference patterns may only be written when the default binding mode is `move` +error: cannot explicitly dereference within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:27:20 | LL | test_pat_on_type![(&mut x,): &(&mut T,)]; - | ^^^^ reference pattern not allowed under `ref` default binding mode + | ^^^^ reference pattern not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:27:19 | LL | test_pat_on_type![(&mut x,): &(&mut T,)]; - | ^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | test_pat_on_type![&(&mut x,): &(&mut T,)]; | + -error: reference patterns may only be written when the default binding mode is `move` +error: cannot explicitly dereference within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:31:28 | LL | test_pat_on_type![Foo { f: &(x,) }: &Foo]; - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:31:19 | LL | test_pat_on_type![Foo { f: &(x,) }: &Foo]; - | ^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | test_pat_on_type![&Foo { f: &(x,) }: &Foo]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot mutably bind by value within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:32:20 | LL | test_pat_on_type![(mut x,): &(T,)]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ `mut` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:32:19 | LL | test_pat_on_type![(mut x,): &(T,)]; - | ^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | test_pat_on_type![&(mut x,): &(T,)]; | + -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:33:20 | LL | test_pat_on_type![(ref x,): &(T,)]; - | ^^^ binding modifier not allowed under `ref` default binding mode + | ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:33:19 | LL | test_pat_on_type![(ref x,): &(T,)]; - | ^^^^^^^^ this matches on type `&_` + | ^^^^^^^^ this non-reference pattern matches on a reference type `&_` help: remove the unnecessary binding modifier | LL - test_pat_on_type![(ref x,): &(T,)]; LL + test_pat_on_type![(x,): &(T,)]; | -error: binding modifiers may only be written when the default binding mode is `move` +error: cannot explicitly borrow within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:34:20 | LL | test_pat_on_type![(ref mut x,): &mut (T,)]; - | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | ^^^^^^^ explicit `ref` binding modifier not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:34:19 | LL | test_pat_on_type![(ref mut x,): &mut (T,)]; - | ^^^^^^^^^^^^ this matches on type `&mut _` + | ^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _` help: remove the unnecessary binding modifier | LL - test_pat_on_type![(ref mut x,): &mut (T,)]; LL + test_pat_on_type![(x,): &mut (T,)]; | -error: reference patterns may only be written when the default binding mode is `move` +error: cannot explicitly dereference within an implicitly-borrowing pattern --> $DIR/min_match_ergonomics_fail.rs:43:10 | LL | (&x,) => x, - | ^ reference pattern not allowed under `ref` default binding mode + | ^ reference pattern not allowed when implicitly borrowing | - = note: for more information, see -note: matching on a reference type with a non-reference pattern changes the default binding mode + = note: for more information, see +note: matching on a reference type with a non-reference pattern implicitly borrows the contents --> $DIR/min_match_ergonomics_fail.rs:43:9 | LL | (&x,) => x, - | ^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit + | ^^^^^ this non-reference pattern matches on a reference type `&_` +help: match on the reference with a reference pattern to avoid implicitly borrowing | LL | &(&x,) => x, | + From 8066cbc07a469a25b927b7206a6405c885e414b7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 Oct 2025 11:31:28 +0200 Subject: [PATCH 1399/1900] readme: document how to directly invoke the driver --- src/tools/miri/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index a5214e213b3cf..d47967c0a4d34 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -246,6 +246,21 @@ such races. Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 +### Directly invoking the `miri` driver + +The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` +driver is not supported, which is why that binary is not even installed into the PATH. However, if +you need to run Miri on many small tests and want to invoke it directly like you would invoke +`rustc`, that is still possible with a bit of extra effort: + +```sh +# one-time setup +cargo +nightly miri setup +SYSROOT=$(cargo +nightly miri setup --print-sysroot) +# per file +~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/miri --sysroot "$SYSROOT" file.rs +``` + ### Common Problems When using the above instructions, you may encounter a number of confusing compiler From 594f9c6c4fc7dd747820c058b78bf5c1b4480734 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 8 Oct 2025 11:29:20 +0200 Subject: [PATCH 1400/1900] Clarify how to remediate the panic_immediate_abort error Users who build `core` for the sole purpose of enabling `panic_immediate_abort` might expect "`panic_immediate_abort` is now a real panic strategy" to mean that setting `panic = "immediate-abort"` in `Cargo.toml` or `-Cpanic=immediate-abort` in `RUSTFLAGS` to be sufficient for migration. But this is not the case, `core` still needs to be built for those changes to take effect. See https://github.com/rust-lang/rust/issues/146974 for additional context. --- library/core/src/panicking.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index b5150837e6a94..448f4ffc3dae0 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -36,7 +36,8 @@ use crate::panic::{Location, PanicInfo}; compile_error!( "panic_immediate_abort is now a real panic strategy! \ Enable it with `panic = \"immediate-abort\"` in Cargo.toml, \ - or with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" + or with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`. \ + In both cases, you still need to build core, e.g. with `-Zbuild-std`" ); // First we define the two main entry points that all panics go through. From b97bdcb0a03abe95db5cdb4ea685ee320ac8ef29 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 17:39:21 +1100 Subject: [PATCH 1401/1900] Extract query-method inner functions to `query::inner` --- compiler/rustc_middle/src/query/inner.rs | 78 ++++++++++++++++++++ compiler/rustc_middle/src/query/mod.rs | 5 +- compiler/rustc_middle/src/query/plumbing.rs | 82 +++------------------ 3 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 compiler/rustc_middle/src/query/inner.rs diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs new file mode 100644 index 0000000000000..6efe21bc89976 --- /dev/null +++ b/compiler/rustc_middle/src/query/inner.rs @@ -0,0 +1,78 @@ +//! Helper functions that serve as the immediate implementation of +//! `tcx.$query(..)` and its variations. + +use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; + +use crate::query::IntoQueryParam; +use crate::query::erase::{self, Erase, EraseType}; +use crate::ty::TyCtxt; + +/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` +/// for all queries. +#[inline(always)] +pub(crate) fn query_get_at<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + span: Span, + key: Cache::Key, +) -> Cache::Value +where + Cache: QueryCache, +{ + let key = key.into_query_param(); + match try_get_cached(tcx, query_cache, &key) { + Some(value) => value, + None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries, +/// and `tcx.ensure_done().$query(..)` for all queries. +#[inline] +pub(crate) fn query_ensure<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) where + Cache: QueryCache, +{ + let key = key.into_query_param(); + if try_get_cached(tcx, query_cache, &key).is_none() { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that +/// have the `return_result_from_ensure_ok` modifier. +#[inline] +pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) -> Result<(), ErrorGuaranteed> +where + Cache: QueryCache>>, + Result: EraseType, +{ + let key = key.into_query_param(); + if let Some(res) = try_get_cached(tcx, query_cache, &key) { + erase::restore(res).map(drop) + } else { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) + .map(erase::restore) + .map(|res| res.map(drop)) + // Either we actually executed the query, which means we got a full `Result`, + // or we can just assume the query succeeded, because it was green in the + // incremental cache. If it is green, that means that the previous compilation + // that wrote to the incremental cache compiles successfully. That is only + // possible if the cache entry was `Ok(())`, so we emit that here, without + // actually encoding the `Result` in the cache or loading it from there. + .unwrap_or(Ok(())) + } +} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 30c4b4369830d..4bac1e8406c14 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -121,9 +121,7 @@ use crate::mir::interpret::{ }; use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; -use crate::query::plumbing::{ - CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, -}; +use crate::query::plumbing::{CyclePlaceholder, DynamicQuery}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -147,6 +145,7 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; +pub(crate) mod inner; mod keys; pub mod on_disk_cache; #[macro_use] diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 16cc81dacad4b..c2fc43b444b87 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,10 +8,9 @@ use rustc_query_system::HandleCycleError; use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; -use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -167,78 +166,17 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[inline(always)] -pub fn query_get_at<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - span: Span, - key: Cache::Key, -) -> Cache::Value -where - Cache: QueryCache, -{ - let key = key.into_query_param(); - match try_get_cached(tcx, query_cache, &key) { - Some(value) => value, - None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), - } -} - -#[inline] -pub fn query_ensure<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) where - Cache: QueryCache, -{ - let key = key.into_query_param(); - if try_get_cached(tcx, query_cache, &key).is_none() { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); - } -} - -#[inline] -pub fn query_ensure_error_guaranteed<'tcx, Cache, T>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) -> Result<(), ErrorGuaranteed> -where - Cache: QueryCache>>, - Result: EraseType, -{ - let key = key.into_query_param(); - if let Some(res) = try_get_cached(tcx, query_cache, &key) { - super::erase::restore(res).map(drop) - } else { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) - .map(super::erase::restore) - .map(|res| res.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())) - } -} - -macro_rules! query_ensure { +/// Calls either `query_ensure` or `query_ensure_error_guaranteed`, depending +/// on whether the list of modifiers contains `return_result_from_ensure_ok`. +macro_rules! query_ensure_select { ([]$($args:tt)*) => { - query_ensure($($args)*) + crate::query::inner::query_ensure($($args)*) }; ([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => { - query_ensure_error_guaranteed($($args)*).map(|_| ()) + crate::query::inner::query_ensure_error_guaranteed($($args)*) }; ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { - query_ensure!([$($modifiers)*]$($args)*) + query_ensure_select!([$($modifiers)*]$($args)*) }; } @@ -434,7 +372,7 @@ macro_rules! define_callbacks { self, key: query_helper_param_ty!($($K)*), ) -> ensure_ok_result!([$($modifiers)*]) { - query_ensure!( + query_ensure_select!( [$($modifiers)*] self.tcx, self.tcx.query_system.fns.engine.$name, @@ -449,7 +387,7 @@ macro_rules! define_callbacks { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - query_ensure( + crate::query::inner::query_ensure( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, @@ -474,7 +412,7 @@ macro_rules! define_callbacks { #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - restore::<$V>(query_get_at( + restore::<$V>(crate::query::inner::query_get_at( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, From cb0c092c786acebe56b915344b82e53d06b0e1d4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:22:39 +1100 Subject: [PATCH 1402/1900] Prepare `define_feedable!` for code extraction --- compiler/rustc_middle/src/query/mod.rs | 5 +---- compiler/rustc_middle/src/query/plumbing.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 4bac1e8406c14..a81c2530a3928 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -70,7 +70,6 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_arena::TypedArena; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; @@ -88,9 +87,7 @@ use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{ - QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached, -}; +use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index c2fc43b444b87..cb8a15c029f4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,17 +501,22 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + #![allow(unused_imports)] // Removed later in this PR. + use rustc_data_structures::fingerprint::Fingerprint; + use rustc_query_system::query::{QueryCache, try_get_cached}; + let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = restore::<$V>(erased); + let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; + let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = restore::<$V>(old); + let old = erase::restore::<$V>(old); if let Some(hasher) = hasher { let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) @@ -521,9 +526,8 @@ macro_rules! define_feedable { // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {} key={key:?}:\n\ + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ old value: {old:?}\nnew value: {value:?}", - stringify!($name), )); } } else { @@ -531,18 +535,18 @@ macro_rules! define_feedable { // If feeding the same value multiple times needs to be supported, // the query should not be marked `no_hash`. bug!( - "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}", - stringify!($name), + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", ) } } None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::dep_kinds::$name, &key); + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); let dep_node_index = tcx.dep_graph.with_feed_task( dep_node, tcx, &value, - hash_result!([$($modifiers)*]), + hasher, ); cache.complete(key, erased, dep_node_index); } From a282336c5ca01d1be21ee21f7fb872341a36a65b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 16:07:09 +1100 Subject: [PATCH 1403/1900] Extract most code from `define_feedable!` --- compiler/rustc_middle/src/query/inner.rs | 56 +++++++++++++++++++++ compiler/rustc_middle/src/query/plumbing.rs | 51 ++++--------------- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 6efe21bc89976..ee828ae55f7a3 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,9 +1,15 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use std::fmt::Debug; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; +use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use crate::dep_graph; use crate::query::IntoQueryParam; use crate::query::erase::{self, Erase, EraseType}; use crate::ty::TyCtxt; @@ -76,3 +82,53 @@ where .unwrap_or(Ok(())) } } + +/// Common implementation of query feeding, used by `define_feedable!`. +pub(crate) fn query_feed<'tcx, Cache, Value>( + tcx: TyCtxt<'tcx>, + dep_kind: DepKind, + hasher: Option, &Value) -> Fingerprint>, + cache: &Cache, + key: Cache::Key, + erased: Erase, +) where + Cache: QueryCache>, + Cache::Key: DepNodeParams>, + Value: EraseType + Debug, +{ + let value = erase::restore::(erased); + + match try_get_cached(tcx, cache, &key) { + Some(old) => { + let old = erase::restore::(old); + if let Some(hasher) = hasher { + let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx + .with_stable_hashing_context(|mut hcx| { + (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) + }); + if old_hash != value_hash { + // We have an inconsistency. This can happen if one of the two + // results is tainted by errors. In this case, delay a bug to + // ensure compilation is doomed, and keep the `old` value. + tcx.dcx().delayed_bug(format!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + )); + } + } else { + // The query is `no_hash`, so we have no way to perform a sanity check. + // If feeding the same value multiple times needs to be supported, + // the query should not be marked `no_hash`. + bug!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + ) + } + } + None => { + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); + let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); + cache.complete(key, erased, dep_node_index); + } + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index cb8a15c029f4b..8d01d9482ed4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,56 +501,23 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { - #![allow(unused_imports)] // Removed later in this PR. - use rustc_data_structures::fingerprint::Fingerprint; - use rustc_query_system::query::{QueryCache, try_get_cached}; - let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); - match try_get_cached(tcx, cache, &key) { - Some(old) => { - let old = erase::restore::<$V>(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - ); - if old_hash != value_hash { - // We have an inconsistency. This can happen if one of the two - // results is tainted by errors. In this case, delay a bug to - // ensure compilation is doomed, and keep the `old` value. - tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - )); - } - } else { - // The query is `no_hash`, so we have no way to perform a sanity check. - // If feeding the same value multiple times needs to be supported, - // the query should not be marked `no_hash`. - bug!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - ) - } - } - None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task( - dep_node, - tcx, - &value, - hasher, - ); - cache.complete(key, erased, dep_node_index); - } - } + + $crate::query::inner::query_feed( + tcx, + dep_kind, + hasher, + cache, + key, + erased, + ); } })* } From 2c1e796ed0ac8af3441869a2b1458d9bcf566b61 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 8 Oct 2025 09:45:10 +0100 Subject: [PATCH 1404/1900] Improve missing create level error message --- src/librustdoc/core.rs | 3 +++ tests/rustdoc-ui/lints/check.rs | 4 +--- tests/rustdoc-ui/lints/check.stderr | 9 +++++++++ tests/rustdoc-ui/lints/no-crate-level-doc-lint.rs | 4 +--- tests/rustdoc-ui/lints/no-crate-level-doc-lint.stderr | 6 ++++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b8aaafcb517a9..0d4e24538e0d5 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -407,6 +407,9 @@ pub(crate) fn run_global_ctxt( crate::lint::MISSING_CRATE_LEVEL_DOCS, DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(), |lint| { + if let Some(local_def_id) = krate.module.item_id.as_local_def_id() { + lint.span(tcx.def_span(local_def_id)); + } lint.primary_message("no documentation found for this crate's top-level module"); lint.help(help); }, diff --git a/tests/rustdoc-ui/lints/check.rs b/tests/rustdoc-ui/lints/check.rs index 0943f9f6053e9..9a4cc96094108 100644 --- a/tests/rustdoc-ui/lints/check.rs +++ b/tests/rustdoc-ui/lints/check.rs @@ -2,7 +2,7 @@ //@ compile-flags: -Z unstable-options --check //@ normalize-stderr: "nightly|beta|1\.[0-9][0-9]\.[0-9]" -> "$$CHANNEL" -#![feature(rustdoc_missing_doc_code_examples)] +#![feature(rustdoc_missing_doc_code_examples)] //~ WARN no documentation found for this crate's top-level module //~^ WARN #![warn(missing_docs)] @@ -12,5 +12,3 @@ pub fn foo() {} //~^ WARN //~^^ WARN - -//~? WARN no documentation found for this crate's top-level module diff --git a/tests/rustdoc-ui/lints/check.stderr b/tests/rustdoc-ui/lints/check.stderr index dcdf25dda649c..52c8c176084a2 100644 --- a/tests/rustdoc-ui/lints/check.stderr +++ b/tests/rustdoc-ui/lints/check.stderr @@ -22,6 +22,15 @@ LL | pub fn foo() {} | ^^^^^^^^^^^^ warning: no documentation found for this crate's top-level module + --> $DIR/check.rs:5:1 + | +LL | / #![feature(rustdoc_missing_doc_code_examples)] +LL | | +LL | | +LL | | #![warn(missing_docs)] +... | +LL | | pub fn foo() {} + | |_______________^ | = help: The following guide may be of use: https://doc.rust-lang.org/$CHANNEL/rustdoc/how-to-write-documentation.html diff --git a/tests/rustdoc-ui/lints/no-crate-level-doc-lint.rs b/tests/rustdoc-ui/lints/no-crate-level-doc-lint.rs index 5e7dc377f5d53..0402fa5241cc9 100644 --- a/tests/rustdoc-ui/lints/no-crate-level-doc-lint.rs +++ b/tests/rustdoc-ui/lints/no-crate-level-doc-lint.rs @@ -1,7 +1,5 @@ //@ normalize-stderr: "nightly|beta|1\.[0-9][0-9]\.[0-9]" -> "$$CHANNEL" -#![deny(rustdoc::missing_crate_level_docs)] +#![deny(rustdoc::missing_crate_level_docs)] //~ ERROR no documentation found for this crate's top-level module //^~ NOTE defined here pub fn foo() {} - -//~? ERROR no documentation found for this crate's top-level module diff --git a/tests/rustdoc-ui/lints/no-crate-level-doc-lint.stderr b/tests/rustdoc-ui/lints/no-crate-level-doc-lint.stderr index 721d3662c93ba..8d2b9b4fc8e81 100644 --- a/tests/rustdoc-ui/lints/no-crate-level-doc-lint.stderr +++ b/tests/rustdoc-ui/lints/no-crate-level-doc-lint.stderr @@ -1,4 +1,10 @@ error: no documentation found for this crate's top-level module + --> $DIR/no-crate-level-doc-lint.rs:2:1 + | +LL | / #![deny(rustdoc::missing_crate_level_docs)] +... | +LL | | pub fn foo() {} + | |_______________^ | = help: The following guide may be of use: https://doc.rust-lang.org/$CHANNEL/rustdoc/how-to-write-documentation.html From 8ef90574a270563f545675668888ca93c3dcc921 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Wed, 8 Oct 2025 11:58:50 +0100 Subject: [PATCH 1405/1900] chore: update manual_assert span suggestions --- clippy_lints/src/manual_assert.rs | 37 +++---- tests/ui/manual_assert.edition2018.fixed | 98 +++++++++++++++++++ tests/ui/manual_assert.edition2018.stderr | 114 ++++++++-------------- tests/ui/manual_assert.edition2021.fixed | 98 +++++++++++++++++++ tests/ui/manual_assert.edition2021.stderr | 114 ++++++++-------------- tests/ui/manual_assert.rs | 2 - 6 files changed, 297 insertions(+), 166 deletions(-) create mode 100644 tests/ui/manual_assert.edition2018.fixed create mode 100644 tests/ui/manual_assert.edition2021.fixed diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 76cb22864779f..c34e0d33e713e 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -50,32 +51,32 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { // Should this have a config value? && !is_else_clause(cx.tcx, expr) { - let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); - let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); - // we show to the user the suggestion without the comments, but when applying the fix, include the - // comments in the block span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", |diag| { - // comments can be noisy, do not show them to the user + let mut applicability = Applicability::MachineApplicable; + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability, - ); + comments += "\n"; } - diag.span_suggestion(expr.span, "try instead", sugg, applicability); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + + let indent = indent_of(cx, expr.span); + let full_sugg = reindent_multiline( + format!("{comments}assert!({cond_sugg}, {format_args_snip}){semicolon}").as_str(), + true, + indent, + ); + diag.span_suggestion_verbose( + expr.span, + "replace `if`-then-`panic!` with `assert!`", + full_sugg, + applicability, + ); }, ); } diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2018.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2021.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index ab02bd5f5e533..7611795542230 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -2,8 +2,6 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 -//@no-rustfix: need to change the suggestion to a multipart suggestion - #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] From f62be66e9447bcf00064af4c8f42d789cc5e9782 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 01:07:26 +0200 Subject: [PATCH 1406/1900] cmse: additional `impl Trait` tests --- .../src/hir_ty_lowering/cmse.rs | 2 +- .../cmse-nonsecure-call/generics.rs | 7 ++- .../cmse-nonsecure-call/generics.stderr | 34 ++++++++---- .../cmse-nonsecure-entry/generics.rs | 28 +++++++--- .../cmse-nonsecure-entry/generics.stderr | 54 +++++++++++++------ 5 files changed, 88 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 91d1f89b74477..857d04593c277 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -216,7 +216,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError match abi { ExternAbi::CmseNonSecureCall => { // prevent double reporting of this error - !ty.is_impl_trait() + !ty.has_opaque_types() } ExternAbi::CmseNonSecureEntry => true, _ => bug!("invalid ABI: {abi}"), diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs index 2dc540675e2e6..72efd05814325 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -17,8 +17,11 @@ struct Test { f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters //~| ERROR `impl Trait` is not allowed in `fn` pointer return types - f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] - f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, //~ ERROR [E0798] + f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32), + //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters + //~| ERROR `impl Trait` is not allowed in `fn` pointer return types + f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] + f5: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, //~ ERROR [E0798] } type WithReference = extern "cmse-nonsecure-call" fn(&usize); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index 750dbf8d5707c..d3ef87394be6c 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -38,20 +38,36 @@ LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl C | = note: `impl Trait` is only allowed in arguments and return types of functions and methods +error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters + --> $DIR/generics.rs:20:42 + | +LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32), + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types + --> $DIR/generics.rs:20:78 + | +LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32), + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:20:9 + --> $DIR/generics.rs:23:9 | -LL | f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, +LL | f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:21:9 + --> $DIR/generics.rs:24:9 | -LL | f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, +LL | f5: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:27:71 + --> $DIR/generics.rs:30:71 | LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait; | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -60,7 +76,7 @@ LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:31:60 + --> $DIR/generics.rs:34:60 | LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -69,7 +85,7 @@ LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Tra = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:38:60 + --> $DIR/generics.rs:41:60 | LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -78,12 +94,12 @@ LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspare = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0045]: C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported - --> $DIR/generics.rs:41:20 + --> $DIR/generics.rs:44:20 | LL | type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0045, E0412, E0562, E0798. For more information about an error, try `rustc --explain E0045`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 116f0fdc972fc..6f835538be9a3 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -37,11 +37,6 @@ extern "cmse-nonsecure-entry" fn introduced_generic( 0 } -extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { - //~^ ERROR [E0798] - 0 -} - extern "cmse-nonsecure-entry" fn reference(x: &usize) -> usize { *x } @@ -66,14 +61,31 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> x } -extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { +extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { + //~^ ERROR [E0798] + 0 +} + +extern "cmse-nonsecure-entry" fn return_impl_trait() -> impl Copy { //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type 0u128 } -extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { +extern "cmse-nonsecure-entry" fn return_impl_trait_nested() -> (impl Copy, i32) { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + (0u128, 0i32) +} + +extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + v +} + +extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + v: (impl Copy, i32), +) -> (impl Copy, i32) { v } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 4158fef4553a4..2c46cd708960f 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -11,36 +11,44 @@ LL | | ) -> u64 { | |________^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:40:1 + --> $DIR/generics.rs:64:1 | LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:69:1 + --> $DIR/generics.rs:79:1 | -LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:69:1 + --> $DIR/generics.rs:79:1 | -LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:75:1 + --> $DIR/generics.rs:85:1 | -LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( +LL | | +LL | | +LL | | v: (impl Copy, i32), +LL | | ) -> (impl Copy, i32) { + | |_____________________^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:75:1 + --> $DIR/generics.rs:85:1 | -LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( +LL | | +LL | | +LL | | v: (impl Copy, i32), +LL | | ) -> (impl Copy, i32) { + | |_____________________^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -63,7 +71,7 @@ LL | | ) -> u64 { | |____________^ error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:51:65 + --> $DIR/generics.rs:46:65 | LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -72,7 +80,7 @@ LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:56:80 + --> $DIR/generics.rs:51:80 | LL | extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -81,7 +89,7 @@ LL | extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:64:81 + --> $DIR/generics.rs:59:81 | LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -89,6 +97,18 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error: aborting due to 11 previous errors +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:69:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait() -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:74:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested() -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0798`. From 1aed495ed73205854deb7e2904b141faf86d5f50 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Wed, 8 Oct 2025 10:43:16 +0200 Subject: [PATCH 1407/1900] refactor: replace `LLVMRustAtomicLoad/Store` with LLVM built-in functions --- compiler/rustc_codegen_llvm/src/builder.rs | 19 +++------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 +------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 41 ------------------- 3 files changed, 7 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index e65f3d292dbb8..9673f69906f7b 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -639,13 +639,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { size: Size, ) -> &'ll Value { unsafe { - let load = llvm::LLVMRustBuildAtomicLoad( - self.llbuilder, - ty, - ptr, - UNNAMED, - AtomicOrdering::from_generic(order), - ); + let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + // Set atomic ordering + llvm::LLVMSetOrdering(load, AtomicOrdering::from_generic(order)); // LLVM requires the alignment of atomic loads to be at least the size of the type. llvm::LLVMSetAlignment(load, size.bytes() as c_uint); load @@ -872,12 +868,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { debug!("Store {:?} -> {:?}", val, ptr); assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { - let store = llvm::LLVMRustBuildAtomicStore( - self.llbuilder, - val, - ptr, - AtomicOrdering::from_generic(order), - ); + let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + // Set atomic ordering + llvm::LLVMSetOrdering(store, AtomicOrdering::from_generic(order)); // LLVM requires the alignment of atomic stores to be at least the size of the type. llvm::LLVMSetAlignment(store, size.bytes() as c_uint); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c4b5cf413a725..7e62630f3f98f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1151,6 +1151,7 @@ unsafe extern "C" { // Operations on load/store instructions (only) pub(crate) fn LLVMSetVolatile(MemoryAccessInst: &Value, volatile: Bool); + pub(crate) fn LLVMSetOrdering(MemoryAccessInst: &Value, Ordering: AtomicOrdering); // Operations on phi nodes pub(crate) fn LLVMAddIncoming<'a>( @@ -2090,22 +2091,6 @@ unsafe extern "C" { RHS: &'a Value, ) -> &'a Value; - // Atomic Operations - pub(crate) fn LLVMRustBuildAtomicLoad<'a>( - B: &Builder<'a>, - ElementType: &'a Type, - PointerVal: &'a Value, - Name: *const c_char, - Order: AtomicOrdering, - ) -> &'a Value; - - pub(crate) fn LLVMRustBuildAtomicStore<'a>( - B: &Builder<'a>, - Val: &'a Value, - Ptr: &'a Value, - Order: AtomicOrdering, - ) -> &'a Value; - pub(crate) fn LLVMRustTimeTraceProfilerInitialize(); pub(crate) fn LLVMRustTimeTraceProfilerFinishThread(); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e38474f09ff7a..38d677cfa3403 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -60,29 +60,6 @@ using namespace llvm::object; static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); static_assert(dwarf::DW_OP_stack_value == 0x9f); -// LLVMAtomicOrdering is already an enum - don't create another -// one. -static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { - switch (Ordering) { - case LLVMAtomicOrderingNotAtomic: - return AtomicOrdering::NotAtomic; - case LLVMAtomicOrderingUnordered: - return AtomicOrdering::Unordered; - case LLVMAtomicOrderingMonotonic: - return AtomicOrdering::Monotonic; - case LLVMAtomicOrderingAcquire: - return AtomicOrdering::Acquire; - case LLVMAtomicOrderingRelease: - return AtomicOrdering::Release; - case LLVMAtomicOrderingAcquireRelease: - return AtomicOrdering::AcquireRelease; - case LLVMAtomicOrderingSequentiallyConsistent: - return AtomicOrdering::SequentiallyConsistent; - } - - report_fatal_error("Invalid LLVMAtomicOrdering value!"); -} - static LLVM_THREAD_LOCAL char *LastError; // Custom error handler for fatal LLVM errors. @@ -623,24 +600,6 @@ extern "C" void LLVMRustSetAllowReassoc(LLVMValueRef V) { } } -extern "C" LLVMValueRef -LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Source, - const char *Name, LLVMAtomicOrdering Order) { - Value *Ptr = unwrap(Source); - LoadInst *LI = unwrap(B)->CreateLoad(unwrap(Ty), Ptr, Name); - LI->setAtomic(fromRust(Order)); - return wrap(LI); -} - -extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, - LLVMValueRef V, - LLVMValueRef Target, - LLVMAtomicOrdering Order) { - StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target)); - SI->setAtomic(fromRust(Order)); - return wrap(SI); -} - extern "C" uint64_t LLVMRustGetArrayNumElements(LLVMTypeRef Ty) { return unwrap(Ty)->getArrayNumElements(); } From 5da52f57c59a5a269f747390769a4bd343b590e8 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Mon, 22 Sep 2025 01:04:40 +0000 Subject: [PATCH 1408/1900] Evaluate constants lazily in GVN. --- compiler/rustc_mir_transform/src/gvn.rs | 84 +++++++++++++------------ 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bd8ea7dddad83..7ee31486a43e2 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -364,7 +364,10 @@ struct VnState<'body, 'a, 'tcx> { rev_locals: IndexVec>, values: ValueSet<'a, 'tcx>, /// Values evaluated as constants if possible. - evaluated: IndexVec>>, + /// - `None` are values not computed yet; + /// - `Some(None)` are values for which computation has failed; + /// - `Some(Some(op))` are successful computations. + evaluated: IndexVec>>>, /// Cache the deref values. derefs: Vec, ssa: &'body SsaLocals, @@ -416,8 +419,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. - let evaluated = self.eval_to_const(index); - let _index = self.evaluated.push(evaluated); + let _index = self.evaluated.push(None); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); @@ -430,7 +432,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { let index = self.values.insert_unique(ty, Value::Opaque); - let _index = self.evaluated.push(None); + let _index = self.evaluated.push(Some(None)); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); @@ -468,8 +470,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { kind, provenance, }); - let evaluated = self.eval_to_const(index); - let _index = self.evaluated.push(evaluated); + let _index = self.evaluated.push(None); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); @@ -493,8 +494,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { (index, true) }; if new { - let evaluated = self.eval_to_const(index); - let _index = self.evaluated.push(evaluated); + let _index = self.evaluated.push(None); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); @@ -551,7 +551,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn eval_to_const(&mut self, value: VnIndex) -> Option> { + fn eval_to_const_inner(&mut self, value: VnIndex) -> Option> { use Value::*; let ty = self.ty(value); // Avoid computing layouts inside a coroutine, as that can cause cycles. @@ -571,10 +571,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } Aggregate(variant, ref fields) => { - let fields = fields - .iter() - .map(|&f| self.evaluated[f].as_ref()) - .collect::>>()?; + let fields = + fields.iter().map(|&f| self.eval_to_const(f)).collect::>>()?; let variant = if ty.ty.is_enum() { Some(variant) } else { None }; if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { @@ -603,7 +601,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } Union(active_field, field) => { - let field = self.evaluated[field].as_ref()?; + let field = self.eval_to_const(field)?; if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; @@ -618,8 +616,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } RawPtr { pointer, metadata } => { - let pointer = self.evaluated[pointer].as_ref()?; - let metadata = self.evaluated[metadata].as_ref()?; + let pointer = self.eval_to_const(pointer)?; + let metadata = self.eval_to_const(metadata)?; // Pointers don't have fields, so don't `project_field` them. let data = self.ecx.read_pointer(pointer).discard_err()?; @@ -633,7 +631,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } Projection(base, elem) => { - let base = self.evaluated[base].as_ref()?; + let base = self.eval_to_const(base)?; // `Index` by constants should have been replaced by `ConstantIndex` by // `simplify_place_projection`. let elem = elem.try_map(|_| None, |()| ty.ty)?; @@ -642,7 +640,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Address { base, projection, .. } => { debug_assert!(!projection.contains(&ProjectionElem::Deref)); let pointer = match base { - AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?, + AddressBase::Deref(pointer) => self.eval_to_const(pointer)?, // We have no stack to point to. AddressBase::Local(_) => return None, }; @@ -658,7 +656,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } Discriminant(base) => { - let base = self.evaluated[base].as_ref()?; + let base = self.eval_to_const(base)?; let variant = self.ecx.read_discriminant(base).discard_err()?; let discr_value = self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; @@ -675,7 +673,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { NullOp::SizeOf => arg_layout.size.bytes(), NullOp::AlignOf => arg_layout.align.bytes(), NullOp::OffsetOf(fields) => self - .ecx .tcx .offset_of_subfield(self.typing_env(), arg_layout, fields.iter()) .bytes(), @@ -685,34 +682,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { ImmTy::from_uint(val, ty).into() } UnaryOp(un_op, operand) => { - let operand = self.evaluated[operand].as_ref()?; + let operand = self.eval_to_const(operand)?; let operand = self.ecx.read_immediate(operand).discard_err()?; let val = self.ecx.unary_op(un_op, &operand).discard_err()?; val.into() } BinaryOp(bin_op, lhs, rhs) => { - let lhs = self.evaluated[lhs].as_ref()?; + let lhs = self.eval_to_const(lhs)?; + let rhs = self.eval_to_const(rhs)?; let lhs = self.ecx.read_immediate(lhs).discard_err()?; - let rhs = self.evaluated[rhs].as_ref()?; let rhs = self.ecx.read_immediate(rhs).discard_err()?; let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } Cast { kind, value } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { - let value = self.evaluated[value].as_ref()?; + let value = self.eval_to_const(value)?; let value = self.ecx.read_immediate(value).discard_err()?; let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { - let value = self.evaluated[value].as_ref()?; + let value = self.eval_to_const(value)?; let value = self.ecx.read_immediate(value).discard_err()?; let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?; res.into() } CastKind::Transmute | CastKind::Subtype => { - let value = self.evaluated[value].as_ref()?; + let value = self.eval_to_const(value)?; // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do // limited transmutes: it only works between types with the same layout, and @@ -724,12 +721,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { && !matches!(s1.primitive(), Primitive::Pointer(..)) } (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { - a1.size(&self.ecx) == a2.size(&self.ecx) && - b1.size(&self.ecx) == b2.size(&self.ecx) && - // The alignment of the second component determines its offset, so that also needs to match. - b1.align(&self.ecx) == b2.align(&self.ecx) && - // None of the inputs may be a pointer. - !matches!(a1.primitive(), Primitive::Pointer(..)) + a1.size(&self.ecx) == a2.size(&self.ecx) + && b1.size(&self.ecx) == b2.size(&self.ecx) + // The alignment of the second component determines its offset, so that also needs to match. + && b1.align(&self.ecx) == b2.align(&self.ecx) + // None of the inputs may be a pointer. + && !matches!(a1.primitive(), Primitive::Pointer(..)) && !matches!(b1.primitive(), Primitive::Pointer(..)) } _ => false, @@ -741,7 +738,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { value.offset(Size::ZERO, ty, &self.ecx).discard_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { - let src = self.evaluated[value].as_ref()?; + let src = self.eval_to_const(value)?; let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; self.ecx.unsize_into(src, ty, &dest).discard_err()?; self.ecx @@ -750,13 +747,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { dest.into() } CastKind::FnPtrToPtr | CastKind::PtrToPtr => { - let src = self.evaluated[value].as_ref()?; + let src = self.eval_to_const(value)?; let src = self.ecx.read_immediate(src).discard_err()?; let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { - let src = self.evaluated[value].as_ref()?; + let src = self.eval_to_const(value)?; let src = self.ecx.read_immediate(src).discard_err()?; ImmTy::from_immediate(*src, ty).into() } @@ -766,6 +763,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Some(op) } + fn eval_to_const(&mut self, index: VnIndex) -> Option<&'a OpTy<'tcx>> { + if let Some(op) = self.evaluated[index] { + return op; + } + let op = self.eval_to_const_inner(index); + self.evaluated[index] = Some(self.arena.alloc(op).as_ref()); + self.evaluated[index].unwrap() + } + /// Represent the *value* we obtain by dereferencing an `Address` value. #[instrument(level = "trace", skip(self), ret)] fn dereference_address( @@ -901,7 +907,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { if let ProjectionElem::Index(idx_local) = elem && let Some(idx) = self.locals[idx_local] { - if let Some(offset) = self.evaluated[idx].as_ref() + if let Some(offset) = self.eval_to_const(idx) && let Some(offset) = self.ecx.read_target_usize(offset).discard_err() && let Some(min_length) = offset.checked_add(1) { @@ -1389,8 +1395,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let layout = self.ecx.layout_of(lhs_ty).ok()?; - let as_bits = |value: VnIndex| { - let constant = self.evaluated[value].as_ref()?; + let mut as_bits = |value: VnIndex| { + let constant = self.eval_to_const(value)?; if layout.backend_repr.is_scalar() { let scalar = self.ecx.read_scalar(constant).discard_err()?; scalar.to_bits(constant.layout.size).discard_err() @@ -1790,7 +1796,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } - let op = self.evaluated[index].as_ref()?; + let op = self.eval_to_const(index)?; if op.layout.is_unsized() { // Do not attempt to propagate unsized locals. return None; From fa9162d06fe101ecb5301146281882341fded5ec Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Tue, 7 Oct 2025 10:56:02 -0400 Subject: [PATCH 1409/1900] Allow printing a fully-qualified path in `def_path_str` Previously, the local crate would always be printed as a leading `crate::`. Allow resolving it to the crate name instead. This allows printing a fully qualified path with: ```rust let qualified_name = with_no_visible_paths!(with_resolve_crate_name!( with_no_trimmed_paths!(tcx.def_path_str(def_id)) )); ``` I found this useful for an out-of-tree rustc-driver. I do not currently have a usecase in mind upstream; I'm ok if you don't want this PR for that reason. This does not currently have tests. I am not aware of an easy way to test def-id printing, since it requires having access to a TyCtxt. --- compiler/rustc_middle/src/ty/print/pretty.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8f7c8170f7adf..978345d13f1e9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -31,6 +31,7 @@ use crate::ty::{ thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell = const { Cell::new(false) }; + static SHOULD_PREFIX_WITH_CRATE_NAME: Cell = const { Cell::new(false) }; static SHOULD_PREFIX_WITH_CRATE: Cell = const { Cell::new(false) }; static NO_TRIMMED_PATH: Cell = const { Cell::new(false) }; static FORCE_TRIMMED_PATH: Cell = const { Cell::new(false) }; @@ -98,7 +99,18 @@ define_helper!( /// cycle errors, this can result in extra or suboptimal error output, /// so this variable disables that check. fn with_forced_impl_filename_line(ForcedImplGuard, FORCE_IMPL_FILENAME_LINE); + /// Adds the crate name prefix to paths where appropriate. + /// Unlike `with_crate_prefix`, this unconditionally uses `tcx.crate_name` instead of sometimes + /// using `crate::` for local items. + /// + /// Overrides `with_crate_prefix`. + + // This function is not currently used in-tree, but it's used by a downstream rustc-driver in + // Ferrocene. Please check with them before removing it. + fn with_resolve_crate_name(CrateNamePrefixGuard, SHOULD_PREFIX_WITH_CRATE_NAME); /// Adds the `crate::` prefix to paths where appropriate. + /// + /// Ignored if `with_resolve_crate_name` is active. fn with_crate_prefix(CratePrefixGuard, SHOULD_PREFIX_WITH_CRATE); /// Prevent path trimming if it is turned on. Path trimming affects `Display` impl /// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`, @@ -2313,7 +2325,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; - if cnum == LOCAL_CRATE { + if cnum == LOCAL_CRATE && !with_resolve_crate_name() { if self.tcx.sess.at_least_rust_2018() { // We add the `crate::` keyword on Rust 2018, only when desired. if with_crate_prefix() { From 444eb4cc12ad94935ba8047c3e79da789b9e7d94 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 8 Oct 2025 03:03:55 -0700 Subject: [PATCH 1410/1900] assert that non-extended temporaries have scopes --- compiler/rustc_hir_analysis/src/check/region.rs | 11 ++++++----- compiler/rustc_middle/src/middle/region.rs | 13 ++++++------- compiler/rustc_middle/src/ty/rvalue_scopes.rs | 10 ++++------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 43e6f5fe10471..f99fefcf56ace 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -498,7 +498,8 @@ fn resolve_local<'tcx>( // Iterate up to the enclosing destruction scope to find the same scope that will also // be used for the result of the block itself. if let Some(inner_scope) = visitor.cx.var_parent { - (visitor.cx.var_parent, _) = visitor.scope_tree.default_temporary_scope(inner_scope) + visitor.cx.var_parent = + Some(visitor.scope_tree.default_temporary_scope(inner_scope).0) } // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in // the initializer when this `super let` is not itself extended by a parent `let` @@ -752,10 +753,10 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { // The body of the every fn is a root scope. resolve_expr(this, body.value, true); } else { - // Only functions have an outer terminating (drop) scope, while - // temporaries in constant initializers may be 'static, but only - // according to rvalue lifetime semantics, using the same - // syntactical rules used for let initializers. + // All bodies have an outer temporary drop scope, but temporaries + // and `super let` bindings in constant initializers may be extended + // to have 'static lifetimes, using the same syntactical rules used + // for `let` initializers. // // e.g., in `let x = &f();`, the temporary holding the result from // the `f()` call lives for the entirety of the surrounding block. diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 5367e5edd496a..3ed8d9a36e101 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -16,7 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{DUMMY_SP, Span}; use tracing::debug; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// Represents a statically-describable scope that can be used to /// bound the lifetime/region for values. @@ -302,8 +302,8 @@ impl ScopeTree { /// Returns the scope of non-lifetime-extended temporaries within a given scope, as well as /// whether we've recorded a potential backwards-incompatible change to lint on. - /// Returns `None` when no enclosing temporary scope is found, such as for static items. - pub fn default_temporary_scope(&self, inner: Scope) -> (Option, Option) { + /// Panics if no enclosing temporary scope is found. + pub fn default_temporary_scope(&self, inner: Scope) -> (Scope, Option) { let mut id = inner; let mut backwards_incompatible = None; @@ -311,11 +311,11 @@ impl ScopeTree { match p.data { ScopeData::Destruction => { debug!("temporary_scope({inner:?}) = {id:?} [enclosing]"); - return (Some(id), backwards_incompatible); + return (id, backwards_incompatible); } ScopeData::IfThenRescope | ScopeData::MatchGuard => { debug!("temporary_scope({inner:?}) = {p:?} [enclosing]"); - return (Some(p), backwards_incompatible); + return (p, backwards_incompatible); } ScopeData::Node | ScopeData::CallSite @@ -335,7 +335,6 @@ impl ScopeTree { } } - debug!("temporary_scope({inner:?}) = None"); - (None, backwards_incompatible) + span_bug!(ty::tls::with(|tcx| inner.span(tcx, self)), "no enclosing temporary scope") } } diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 8b92e48ed1a07..df4e29d457548 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -31,12 +31,10 @@ impl RvalueScopes { return (s, None); } - // Otherwise, locate the innermost terminating scope - // if there's one. Static items, for instance, won't - // have an enclosing scope, hence no scope will be - // returned. - region_scope_tree - .default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node }) + // Otherwise, locate the innermost terminating scope. + let (scope, backward_incompatible) = region_scope_tree + .default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node }); + (Some(scope), backward_incompatible) } /// Make an association between a sub-expression and an extended lifetime From 0553ec97520ad5db7d31ebdd817d5aabd3534463 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 2 Oct 2025 23:22:28 +0300 Subject: [PATCH 1411/1900] Migrate drop glue handling to new solver And: - Remove it from being a query (it is only used for hover, where no caching is needed, and MIR evaluation of `needs_drop()`, which is rare). - Fix handling of `PhantomData`. --- .../hir-ty/src/consteval/tests/intrinsics.rs | 9 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 5 - .../rust-analyzer/crates/hir-ty/src/drop.rs | 194 ++++++++---------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 3 +- .../crates/hir-ty/src/mir/eval.rs | 10 +- .../crates/hir-ty/src/mir/eval/shim.rs | 8 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 12 +- 7 files changed, 113 insertions(+), 128 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 5e85978e2990d..10282d21168a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -354,8 +354,9 @@ fn overflowing_add() { fn needs_drop() { check_number( r#" - //- minicore: drop, manually_drop, copy, sized + //- minicore: drop, manually_drop, copy, sized, phantom_data use core::mem::ManuallyDrop; + use core::marker::PhantomData; extern "rust-intrinsic" { pub fn needs_drop() -> bool; } @@ -380,17 +381,19 @@ fn needs_drop() { const fn opaque_copy() -> impl Sized + Copy { || {} } + struct RecursiveType(RecursiveType); trait Everything {} impl Everything for T {} const GOAL: bool = !needs_drop::() && !needs_drop::() && needs_drop::() && !needs_drop::>() && needs_drop::<[NeedsDrop; 1]>() && !needs_drop::<[NeedsDrop; 0]>() - && needs_drop::<(X, NeedsDrop)>() + && needs_drop::<(X, NeedsDrop)>() && !needs_drop::>() && needs_drop::>() && !needs_drop::>() && closure_needs_drop() && !val_needs_drop(opaque()) && !val_needs_drop(opaque_copy()) && needs_drop::<[NeedsDrop]>() && needs_drop::() - && !needs_drop::<&dyn Everything>() && !needs_drop::(); + && !needs_drop::<&dyn Everything>() && !needs_drop::() + && !needs_drop::(); "#, 1, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 82d17cc618cd2..11d3be5c3c487 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -19,7 +19,6 @@ use crate::{ Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, Ty, TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, - drop::DropGlue, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, lower::{Diagnostics, GenericDefaults, GenericPredicates}, @@ -334,10 +333,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { goal: crate::Canonical>, ) -> NextTraitSolveResult; - #[salsa::invoke(crate::drop::has_drop_glue)] - #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] - fn has_drop_glue(&self, ty: Ty, env: Arc>) -> DropGlue; - // next trait solver #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 0618fd17d55d0..4bd9691ea0795 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -1,18 +1,20 @@ //! Utilities for computing drop info about types. -use chalk_ir::cast::Cast; -use hir_def::AdtId; -use hir_def::lang_item::LangItem; -use hir_def::signatures::StructFlags; +use hir_def::{AdtId, lang_item::LangItem, signatures::StructFlags}; +use rustc_hash::FxHashSet; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; use stdx::never; use triomphe::Arc; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ - AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment, - Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase, + TraitEnvironment, consteval_nextsolver, + db::HirDatabase, method_resolution::TyFingerprint, + next_solver::{ + Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, + }, }; fn has_destructor(db: &dyn HirDatabase, adt: AdtId) -> bool { @@ -45,27 +47,52 @@ pub enum DropGlue { HasDropGlue, } -pub(crate) fn has_drop_glue( - db: &dyn HirDatabase, - ty: Ty, - env: Arc>, +pub fn has_drop_glue<'db>( + infcx: &InferCtxt<'db>, + ty: Ty<'db>, + env: Arc>, ) -> DropGlue { - match ty.kind(Interner) { - TyKind::Adt(adt, subst) => { - if has_destructor(db, adt.0) { + has_drop_glue_impl(infcx, ty, env, &mut FxHashSet::default()) +} + +fn has_drop_glue_impl<'db>( + infcx: &InferCtxt<'db>, + ty: Ty<'db>, + env: Arc>, + visited: &mut FxHashSet>, +) -> DropGlue { + let mut ocx = ObligationCtxt::new(infcx); + let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env.env, ty).unwrap_or(ty); + + if !visited.insert(ty) { + // Recursive type. + return DropGlue::None; + } + + let db = infcx.interner.db; + match ty.kind() { + TyKind::Adt(adt_def, subst) => { + let adt_id = adt_def.def_id().0; + if has_destructor(db, adt_id) { return DropGlue::HasDropGlue; } - match adt.0 { + match adt_id { AdtId::StructId(id) => { - if db.struct_signature(id).flags.contains(StructFlags::IS_MANUALLY_DROP) { + if db + .struct_signature(id) + .flags + .intersects(StructFlags::IS_MANUALLY_DROP | StructFlags::IS_PHANTOM_DATA) + { return DropGlue::None; } - db.field_types(id.into()) + db.field_types_ns(id.into()) .iter() .map(|(_, field_ty)| { - db.has_drop_glue( - field_ty.clone().substitute(Interner, subst), + has_drop_glue_impl( + infcx, + field_ty.instantiate(infcx.interner, subst), env.clone(), + visited, ) }) .max() @@ -78,12 +105,14 @@ pub(crate) fn has_drop_glue( .variants .iter() .map(|&(variant, _, _)| { - db.field_types(variant.into()) + db.field_types_ns(variant.into()) .iter() .map(|(_, field_ty)| { - db.has_drop_glue( - field_ty.clone().substitute(Interner, subst), + has_drop_glue_impl( + infcx, + field_ty.instantiate(infcx.interner, subst), env.clone(), + visited, ) }) .max() @@ -93,123 +122,70 @@ pub(crate) fn has_drop_glue( .unwrap_or(DropGlue::None), } } - TyKind::Tuple(_, subst) => subst - .iter(Interner) - .map(|ty| ty.assert_ty_ref(Interner)) - .map(|ty| db.has_drop_glue(ty.clone(), env.clone())) + TyKind::Tuple(tys) => tys + .iter() + .map(|ty| has_drop_glue_impl(infcx, ty, env.clone(), visited)) .max() .unwrap_or(DropGlue::None), TyKind::Array(ty, len) => { - if let ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Bytes(len, _) }) = - &len.data(Interner).value - { - match (&**len).try_into() { - Ok(len) => { - let len = usize::from_le_bytes(len); - if len == 0 { - // Arrays of size 0 don't have drop glue. - return DropGlue::None; - } - } - Err(_) => { - never!("const array size with non-usize len"); - } - } + if consteval_nextsolver::try_const_usize(db, len) == Some(0) { + // Arrays of size 0 don't have drop glue. + return DropGlue::None; } - db.has_drop_glue(ty.clone(), env) + has_drop_glue_impl(infcx, ty, env, visited) } - TyKind::Slice(ty) => db.has_drop_glue(ty.clone(), env), + TyKind::Slice(ty) => has_drop_glue_impl(infcx, ty, env, visited), TyKind::Closure(closure_id, subst) => { - let closure_id = (*closure_id).into(); - let owner = db.lookup_intern_closure(closure_id).0; + let owner = db.lookup_intern_closure(closure_id.0).0; let infer = db.infer(owner); - let (captures, _) = infer.closure_info(closure_id); + let (captures, _) = infer.closure_info(closure_id.0); let env = db.trait_environment_for_body(owner); - let interner = DbInterner::conjure(); captures .iter() .map(|capture| { - db.has_drop_glue( - capture.ty(db, subst.to_nextsolver(interner)).to_chalk(interner), - env.clone(), - ) + has_drop_glue_impl(infcx, capture.ty(db, subst), env.clone(), visited) }) .max() .unwrap_or(DropGlue::None) } // FIXME: Handle coroutines. - TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => DropGlue::None, + TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) | TyKind::CoroutineClosure(..) => { + DropGlue::None + } TyKind::Ref(..) - | TyKind::Raw(..) + | TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::Str | TyKind::Never - | TyKind::Scalar(_) - | TyKind::Function(_) + | TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::FnPtr(..) | TyKind::Foreign(_) - | TyKind::Error => DropGlue::None, - TyKind::Dyn(_) => DropGlue::HasDropGlue, - TyKind::AssociatedType(assoc_type_id, subst) => projection_has_drop_glue( - db, - env, - ProjectionTy { associated_ty_id: *assoc_type_id, substitution: subst.clone() }, - ty, - ), - TyKind::Alias(AliasTy::Projection(projection)) => { - projection_has_drop_glue(db, env, projection.clone(), ty) - } - TyKind::OpaqueType(..) | TyKind::Alias(AliasTy::Opaque(_)) => { - if is_copy(db, ty, env) { + | TyKind::Error(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) => DropGlue::None, + TyKind::Dynamic(..) => DropGlue::HasDropGlue, + TyKind::Alias(..) => { + if infcx.type_is_copy_modulo_regions(env.env, ty) { DropGlue::None } else { DropGlue::HasDropGlue } } - TyKind::Placeholder(_) | TyKind::BoundVar(_) => { - if is_copy(db, ty, env) { + TyKind::Param(_) => { + if infcx.type_is_copy_modulo_regions(env.env, ty) { DropGlue::None } else { DropGlue::DependOnParams } } - TyKind::InferenceVar(..) => unreachable!("inference vars shouldn't exist out of inference"), - } -} - -fn projection_has_drop_glue( - db: &dyn HirDatabase, - env: Arc>, - projection: ProjectionTy, - ty: Ty, -) -> DropGlue { - let normalized = db.normalize_projection(projection, env.clone()); - match normalized.kind(Interner) { - TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(..) => { - if is_copy(db, ty, env) { DropGlue::None } else { DropGlue::DependOnParams } + TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"), + TyKind::Pat(..) | TyKind::UnsafeBinder(..) => { + never!("we do not handle pattern and unsafe binder types"); + DropGlue::None } - _ => db.has_drop_glue(normalized, env), } } - -fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc>) -> bool { - let Some(copy_trait) = LangItem::Copy.resolve_trait(db, env.krate) else { - return false; - }; - let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build(); - let goal = Canonical { - value: InEnvironment::new( - &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)), - trait_ref.cast(Interner), - ), - binders: CanonicalVarKinds::empty(Interner), - }; - db.trait_solve(env.krate, env.block, goal).certain() -} - -pub(crate) fn has_drop_glue_cycle_result( - _db: &dyn HirDatabase, - _ty: Ty, - _env: Arc>, -) -> DropGlue { - DropGlue::None -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2add66d02d235..4ff5cc5588bfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -24,7 +24,6 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; mod builder; mod chalk_db; mod chalk_ext; -mod drop; mod infer; mod inhabitedness; mod interner; @@ -42,6 +41,7 @@ pub mod consteval_nextsolver; pub mod db; pub mod diagnostics; pub mod display; +pub mod drop; pub mod dyn_compatibility; pub mod generics; pub mod lang_items; @@ -94,7 +94,6 @@ use crate::{ pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; -pub use drop::DropGlue; pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e1983b37472ce..6e09cf9aeb882 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -32,7 +32,6 @@ use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; -use crate::next_solver::mapping::NextSolverToChalk; use crate::{ AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, @@ -44,8 +43,11 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, convert_args_for_result, convert_ty_for_result}, + DbInterner, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ + ChalkToNextSolver, NextSolverToChalk, convert_args_for_result, convert_ty_for_result, + }, }, static_lifetime, traits::FnTrait, @@ -204,6 +206,7 @@ pub struct Evaluator<'a> { /// Maximum count of bytes that heap and stack can grow memory_limit: usize, interner: DbInterner<'a>, + infcx: InferCtxt<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -690,6 +693,7 @@ impl<'db> Evaluator<'db> { x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) }), interner, + infcx: interner.infer_ctxt().build(TypingMode::non_body_analysis()), }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 54d13a8d3d735..9ef0012a89c1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -16,8 +16,8 @@ use stdx::never; use crate::next_solver::mapping::NextSolverToChalk; use crate::{ - DropGlue, display::DisplayTarget, + drop::{DropGlue, has_drop_glue}, error_lifetime, mir::eval::{ Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, @@ -855,7 +855,11 @@ impl<'db> Evaluator<'db> { "size_of generic arg is not provided".into(), )); }; - let result = match self.db.has_drop_glue(ty.clone(), self.trait_env.clone()) { + let result = match has_drop_glue( + &self.infcx, + ty.to_nextsolver(self.interner), + self.trait_env.clone(), + ) { DropGlue::HasDropGlue => true, DropGlue::None => false, DropGlue::DependOnParams => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ae52986008f11..cce2564a9e6dd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -82,8 +82,8 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - ClauseKind, DbInterner, GenericArgs, - infer::InferCtxt, + ClauseKind, DbInterner, GenericArgs, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result}, }, primitive::UintTy, @@ -157,10 +157,11 @@ pub use { tt, }, hir_ty::{ - CastError, DropGlue, FnAbi, PointerCast, Variance, attach_db, attach_db_allow_change, + CastError, FnAbi, PointerCast, Variance, attach_db, attach_db_allow_change, consteval::ConstEvalError, diagnostics::UnsafetyReason, display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite}, + drop::DropGlue, dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode}, layout::LayoutError, method_resolution::TyFingerprint, @@ -6043,7 +6044,10 @@ impl<'db> Type<'db> { } pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue { - db.has_drop_glue(self.ty.clone(), self.env.clone()) + let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block); + // FIXME: This should be `PostAnalysis` I believe. + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + hir_ty::drop::has_drop_glue(&infcx, self.ty.to_nextsolver(interner), self.env.clone()) } } From f4d8bd3b47d9b9fd695b894927b04b2bb711a475 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 13:40:42 +0200 Subject: [PATCH 1412/1900] cmse: more accurate spans for excess parameters Use the span of the types that don't fit, rather than the span of the whole signature. --- compiler/rustc_hir_analysis/src/errors.rs | 2 +- .../src/hir_ty_lowering/cmse.rs | 89 ++++++++----------- .../params-via-stack.stderr | 6 +- .../params-via-stack.stderr | 4 +- 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 3b6367219b7ff..db69653a27e9b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1616,7 +1616,7 @@ pub(crate) struct InvalidGenericReceiverTy<'tcx> { pub(crate) struct CmseInputsStackSpill { #[primary_span] #[label] - pub span: Span, + pub spans: Vec, pub plural: bool, pub abi: ExternAbi, } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 857d04593c277..7e2938d1447ca 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -44,20 +44,8 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; - match is_valid_cmse_inputs(tcx, fn_sig) { - Ok(Ok(())) => {} - Ok(Err(index)) => { - // fn(x: u32, u32, u32, u16, y: u16) -> u32, - // ^^^^^^ - let span = if let Some(ident) = fn_ptr_ty.param_idents[index] { - ident.span.to(fn_ptr_ty.decl.inputs[index].span) - } else { - fn_ptr_ty.decl.inputs[index].span - } - .to(fn_ptr_ty.decl.inputs.last().unwrap().span); - let plural = fn_ptr_ty.param_idents.len() - index != 1; - dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); - } + match is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { + Ok(()) => {} Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span }); @@ -65,18 +53,11 @@ pub(crate) fn validate_cmse_abi<'tcx>( } } - match is_valid_cmse_output(tcx, fn_sig) { - Ok(true) => {} - Ok(false) => { - let span = fn_ptr_ty.decl.output.span(); - dcx.emit_err(errors::CmseOutputStackSpill { span, abi }); - } - Err(layout_err) => { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span }); - } + if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { + if should_emit_generic_error(abi, layout_err) { + dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span }); } - }; + } } ExternAbi::CmseNonSecureEntry => { let hir_node = tcx.hir_node(hir_id); @@ -91,15 +72,8 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; } - match is_valid_cmse_inputs(tcx, fn_sig) { - Ok(Ok(())) => {} - Ok(Err(index)) => { - // fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32, - // ^^^^^^ - let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span); - let plural = decl.inputs.len() - index != 1; - dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); - } + match is_valid_cmse_inputs(tcx, dcx, fn_sig, decl, abi) { + Ok(()) => {} Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span }); @@ -107,18 +81,11 @@ pub(crate) fn validate_cmse_abi<'tcx>( } } - match is_valid_cmse_output(tcx, fn_sig) { - Ok(true) => {} - Ok(false) => { - let span = decl.output.span(); - dcx.emit_err(errors::CmseOutputStackSpill { span, abi }); - } - Err(layout_err) => { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span }); - } + if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, decl, abi) { + if should_emit_generic_error(abi, layout_err) { + dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span }); } - }; + } } _ => (), } @@ -127,16 +94,19 @@ pub(crate) fn validate_cmse_abi<'tcx>( /// Returns whether the inputs will fit into the available registers fn is_valid_cmse_inputs<'tcx>( tcx: TyCtxt<'tcx>, + dcx: DiagCtxtHandle<'_>, fn_sig: ty::PolyFnSig<'tcx>, -) -> Result, &'tcx LayoutError<'tcx>> { - let mut span = None; + fn_decl: &hir::FnDecl<'tcx>, + abi: ExternAbi, +) -> Result<(), &'tcx LayoutError<'tcx>> { let mut accum = 0u64; + let mut excess_argument_spans = Vec::new(); // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); - for (index, ty) in fn_sig.inputs().iter().enumerate() { + for (ty, hir_ty) in fn_sig.inputs().iter().zip(fn_decl.inputs) { let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?; let align = layout.layout.align().bytes(); @@ -147,21 +117,28 @@ fn is_valid_cmse_inputs<'tcx>( // i.e. exceeds 4 32-bit registers if accum > 16 { - span = span.or(Some(index)); + excess_argument_spans.push(hir_ty.span); } } - match span { - None => Ok(Ok(())), - Some(span) => Ok(Err(span)), + if !excess_argument_spans.is_empty() { + // fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32, + // ^^^^^^ + let plural = excess_argument_spans.len() != 1; + dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, plural, abi }); } + + Ok(()) } /// Returns whether the output will fit into the available registers fn is_valid_cmse_output<'tcx>( tcx: TyCtxt<'tcx>, + dcx: DiagCtxtHandle<'_>, fn_sig: ty::PolyFnSig<'tcx>, -) -> Result> { + fn_decl: &hir::FnDecl<'tcx>, + abi: ExternAbi, +) -> Result<(), &'tcx LayoutError<'tcx>> { // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); @@ -183,7 +160,11 @@ fn is_valid_cmse_output<'tcx>( let typing_env = ty::TypingEnv::fully_monomorphized(); let layout = tcx.layout_of(typing_env.as_query_input(return_type))?; - Ok(is_valid_cmse_output_layout(layout)) + if !is_valid_cmse_output_layout(layout) { + dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi }); + } + + Ok(()) } /// Returns whether the output will fit into the available registers diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index 5d59405fbd1b2..313f2bb8a6ae4 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -1,8 +1,10 @@ error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:16:61 + --> $DIR/params-via-stack.rs:16:64 | LL | f1: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), - | ^^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ ^^^ these arguments don't fit in the available registers + | | + | these arguments don't fit in the available registers | = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr index f8b96bddc9479..221fc7cf86475 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr @@ -2,7 +2,9 @@ error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass --> $DIR/params-via-stack.rs:15:76 | LL | pub extern "cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} - | ^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ ^^^ these arguments don't fit in the available registers + | | + | these arguments don't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers From b47de64cdbfade1e8f3b4c21d338875a2c2ace7d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 13:42:49 +0200 Subject: [PATCH 1413/1900] cmse: rephrase error message when types don't fit --- compiler/rustc_hir_analysis/messages.ftl | 7 ++---- compiler/rustc_hir_analysis/src/errors.rs | 1 - .../src/hir_ty_lowering/cmse.rs | 3 +-- .../params-via-stack.stderr | 22 +++++++++---------- .../params-via-stack.stderr | 22 +++++++++---------- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 06f2ec512ab1f..3ffced25489c5 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -80,11 +80,8 @@ hir_analysis_cmse_entry_generic = hir_analysis_cmse_inputs_stack_spill = arguments for `{$abi}` function too large to pass via registers - .label = {$plural -> - [false] this argument doesn't - *[true] these arguments don't - } fit in the available registers - .note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit available argument registers + .label = does not fit in the available registers + .note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit argument registers hir_analysis_cmse_output_stack_spill = return value of `{$abi}` function too large to pass via registers diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index db69653a27e9b..b57783ff6222b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1617,7 +1617,6 @@ pub(crate) struct CmseInputsStackSpill { #[primary_span] #[label] pub spans: Vec, - pub plural: bool, pub abi: ExternAbi, } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 7e2938d1447ca..a648653744411 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -124,8 +124,7 @@ fn is_valid_cmse_inputs<'tcx>( if !excess_argument_spans.is_empty() { // fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32, // ^^^^^^ - let plural = excess_argument_spans.len() != 1; - dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, plural, abi }); + dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, abi }); } Ok(()) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index 313f2bb8a6ae4..5a059e4df7b10 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -2,43 +2,43 @@ error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass v --> $DIR/params-via-stack.rs:16:64 | LL | f1: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), - | ^^^ ^^^ these arguments don't fit in the available registers + | ^^^ ^^^ does not fit in the available registers | | - | these arguments don't fit in the available registers + | does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers --> $DIR/params-via-stack.rs:17:61 | LL | f2: extern "cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers --> $DIR/params-via-stack.rs:18:51 | LL | f3: extern "cmse-nonsecure-call" fn(u32, u64, u32), - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers --> $DIR/params-via-stack.rs:19:56 | LL | f4: extern "cmse-nonsecure-call" fn(AlignRelevant, u32), - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers --> $DIR/params-via-stack.rs:20:41 | LL | f5: extern "cmse-nonsecure-call" fn([u32; 5]), - | ^^^^^^^^ this argument doesn't fit in the available registers + | ^^^^^^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit argument registers error: aborting due to 5 previous errors diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr index 221fc7cf86475..3d523fc7be679 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr @@ -2,43 +2,43 @@ error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass --> $DIR/params-via-stack.rs:15:76 | LL | pub extern "cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} - | ^^^ ^^^ these arguments don't fit in the available registers + | ^^^ ^^^ does not fit in the available registers | | - | these arguments don't fit in the available registers + | does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers --> $DIR/params-via-stack.rs:17:76 | LL | pub extern "cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers --> $DIR/params-via-stack.rs:19:60 | LL | pub extern "cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers --> $DIR/params-via-stack.rs:21:62 | LL | pub extern "cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} - | ^^^ this argument doesn't fit in the available registers + | ^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit argument registers error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers --> $DIR/params-via-stack.rs:25:44 | LL | pub extern "cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} - | ^^^^^^^^ this argument doesn't fit in the available registers + | ^^^^^^^^ does not fit in the available registers | - = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit argument registers error: aborting due to 5 previous errors From 98d3864310711946f1374c8bac5723edf626efb8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 14:05:47 +0200 Subject: [PATCH 1414/1900] cmse: rephrase error message when signature uses generics --- compiler/rustc_hir_analysis/messages.ftl | 8 ++-- compiler/rustc_hir_analysis/src/errors.rs | 14 +++--- .../src/hir_ty_lowering/cmse.rs | 13 ++--- .../cmse-nonsecure-call/generics.stderr | 4 +- .../cmse-nonsecure-entry/generics.rs | 12 ++--- .../cmse-nonsecure-entry/generics.stderr | 47 ++++++++----------- 6 files changed, 46 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3ffced25489c5..6add9baa1520f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -72,11 +72,11 @@ hir_analysis_cannot_capture_late_bound_ty = hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present .label = `for<...>` is here -hir_analysis_cmse_call_generic = - function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type +hir_analysis_cmse_generic = + generics are not allowed in `extern {$abi}` signatures -hir_analysis_cmse_entry_generic = - functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +hir_analysis_cmse_impl_trait = + `impl Trait` is not allowed in `extern {$abi}` signatures hir_analysis_cmse_inputs_stack_spill = arguments for `{$abi}` function too large to pass via registers diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b57783ff6222b..49c5106422881 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1632,22 +1632,24 @@ pub(crate) struct CmseOutputStackSpill { } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_call_generic, code = E0798)] -pub(crate) struct CmseCallGeneric { +#[diag(hir_analysis_cmse_generic, code = E0798)] +pub(crate) struct CmseGeneric { #[primary_span] pub span: Span, + pub abi: ExternAbi, } #[derive(Diagnostic)] -#[diag(hir_analysis_bad_return_type_notation_position)] -pub(crate) struct BadReturnTypeNotation { +#[diag(hir_analysis_cmse_impl_trait, code = E0798)] +pub(crate) struct CmseImplTrait { #[primary_span] pub span: Span, + pub abi: ExternAbi, } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_entry_generic, code = E0798)] -pub(crate) struct CmseEntryGeneric { +#[diag(hir_analysis_bad_return_type_notation_position)] +pub(crate) struct BadReturnTypeNotation { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index a648653744411..9a31119c7f5a7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -48,14 +48,14 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(()) => {} Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span }); + dcx.emit_err(errors::CmseGeneric { span: *fn_ptr_span, abi }); } } } if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span }); + dcx.emit_err(errors::CmseGeneric { span: *fn_ptr_span, abi }); } } } @@ -76,14 +76,14 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(()) => {} Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span }); + dcx.emit_err(errors::CmseGeneric { span: *fn_sig_span, abi }); } } } if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, decl, abi) { if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span }); + dcx.emit_err(errors::CmseGeneric { span: *fn_sig_span, abi }); } } } @@ -152,8 +152,9 @@ fn is_valid_cmse_output<'tcx>( // `#[no_mangle]` or similar, so generics in the type really don't make sense. // // see also https://github.com/rust-lang/rust/issues/147242. - if return_type.has_opaque_types() { - return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type))); + if abi == ExternAbi::CmseNonSecureEntry && return_type.has_opaque_types() { + dcx.emit_err(errors::CmseImplTrait { span: fn_decl.output.span(), abi }); + return Ok(()); } let typing_env = ty::TypingEnv::fully_monomorphized(); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index d3ef87394be6c..d70369c46fbeb 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -54,13 +54,13 @@ LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> | = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures --> $DIR/generics.rs:23:9 | LL | f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures --> $DIR/generics.rs:24:9 | LL | f5: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 6f835538be9a3..2b4ddf5e31d33 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -67,25 +67,25 @@ extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32 } extern "cmse-nonsecure-entry" fn return_impl_trait() -> impl Copy { - //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~^ ERROR `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures 0u128 } extern "cmse-nonsecure-entry" fn return_impl_trait_nested() -> (impl Copy, i32) { - //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~^ ERROR `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures (0u128, 0i32) } extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { - //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~^ ERROR generics are not allowed in `extern "cmse-nonsecure-entry"` signatures + //~| ERROR `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures v } extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( - //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~^ ERROR generics are not allowed in `extern "cmse-nonsecure-entry"` signatures v: (impl Copy, i32), ) -> (impl Copy, i32) { + //~^ ERROR `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures v } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 2c46cd708960f..e61051a4a26f4 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,4 +1,4 @@ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:30:1 | LL | / extern "cmse-nonsecure-entry" fn introduced_generic( @@ -10,55 +10,46 @@ LL | | _: u32, LL | | ) -> u64 { | |________^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:64:1 | LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:79:1 | LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:79:1 +error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures + --> $DIR/generics.rs:79:71 | LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + | ^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:85:1 | LL | / extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( LL | | -LL | | LL | | v: (impl Copy, i32), LL | | ) -> (impl Copy, i32) { | |_____________________^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:85:1 +error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures + --> $DIR/generics.rs:88:6 | -LL | / extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( -LL | | -LL | | -LL | | v: (impl Copy, i32), -LL | | ) -> (impl Copy, i32) { - | |_____________________^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | ) -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:14:5 | LL | extern "cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type +error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:19:5 | LL | / extern "cmse-nonsecure-entry" fn ambient_generic_nested( @@ -97,17 +88,17 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:69:1 +error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures + --> $DIR/generics.rs:69:57 | LL | extern "cmse-nonsecure-entry" fn return_impl_trait() -> impl Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ -error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type - --> $DIR/generics.rs:74:1 +error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures + --> $DIR/generics.rs:74:64 | LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested() -> (impl Copy, i32) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ error: aborting due to 13 previous errors From 760ea07840f7eb78664355a52150a64964f57c47 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 14:10:13 +0200 Subject: [PATCH 1415/1900] cmse: more accurate span for generic arguments --- .../src/hir_ty_lowering/cmse.rs | 46 ++++++++--------- .../cmse-nonsecure-call/generics.stderr | 8 +-- .../cmse-nonsecure-entry/generics.rs | 6 +-- .../cmse-nonsecure-entry/generics.stderr | 49 +++++++------------ 4 files changed, 45 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 9a31119c7f5a7..b6c6e43b1d950 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -4,6 +4,7 @@ use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use crate::errors; @@ -20,11 +21,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( match abi { ExternAbi::CmseNonSecureCall => { let hir_node = tcx.hir_node(hir_id); - let hir::Node::Ty(hir::Ty { - span: fn_ptr_span, - kind: hir::TyKind::FnPtr(fn_ptr_ty), - .. - }) = hir_node + let hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), .. }) = hir_node else { let span = match tcx.parent_hir_node(hir_id) { hir::Node::Item(hir::Item { @@ -44,24 +41,24 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; - match is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { - Ok(()) => {} - Err(layout_err) => { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span: *fn_ptr_span, abi }); - } + if let Err((span, layout_err)) = + is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) + { + if should_emit_layout_error(abi, layout_err) { + dcx.emit_err(errors::CmseGeneric { span, abi }); } } if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span: *fn_ptr_span, abi }); + if should_emit_layout_error(abi, layout_err) { + let span = fn_ptr_ty.decl.output.span(); + dcx.emit_err(errors::CmseGeneric { span, abi }); } } } ExternAbi::CmseNonSecureEntry => { let hir_node = tcx.hir_node(hir_id); - let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else { + let Some(hir::FnSig { decl, .. }) = hir_node.fn_sig() else { // might happen when this ABI is used incorrectly. That will be handled elsewhere return; }; @@ -72,18 +69,15 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; } - match is_valid_cmse_inputs(tcx, dcx, fn_sig, decl, abi) { - Ok(()) => {} - Err(layout_err) => { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span: *fn_sig_span, abi }); - } + if let Err((span, layout_err)) = is_valid_cmse_inputs(tcx, dcx, fn_sig, decl, abi) { + if should_emit_layout_error(abi, layout_err) { + dcx.emit_err(errors::CmseGeneric { span, abi }); } } if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, decl, abi) { - if should_emit_generic_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span: *fn_sig_span, abi }); + if should_emit_layout_error(abi, layout_err) { + dcx.emit_err(errors::CmseGeneric { span: decl.output.span(), abi }); } } } @@ -98,7 +92,7 @@ fn is_valid_cmse_inputs<'tcx>( fn_sig: ty::PolyFnSig<'tcx>, fn_decl: &hir::FnDecl<'tcx>, abi: ExternAbi, -) -> Result<(), &'tcx LayoutError<'tcx>> { +) -> Result<(), (Span, &'tcx LayoutError<'tcx>)> { let mut accum = 0u64; let mut excess_argument_spans = Vec::new(); @@ -107,7 +101,9 @@ fn is_valid_cmse_inputs<'tcx>( let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); for (ty, hir_ty) in fn_sig.inputs().iter().zip(fn_decl.inputs) { - let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?; + let layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty)) + .map_err(|e| (hir_ty.span, e))?; let align = layout.layout.align().bytes(); let size = layout.layout.size().bytes(); @@ -189,7 +185,7 @@ fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool { matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64)) } -fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool { +fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool { use LayoutError::*; match layout_err { diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index d70369c46fbeb..75da78b0b75a9 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -55,16 +55,16 @@ LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures - --> $DIR/generics.rs:23:9 + --> $DIR/generics.rs:23:41 | LL | f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures - --> $DIR/generics.rs:24:9 + --> $DIR/generics.rs:24:41 | LL | f5: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers --> $DIR/generics.rs:30:71 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 2b4ddf5e31d33..d01934929d97f 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -17,8 +17,8 @@ impl Wrapper { } extern "cmse-nonsecure-entry" fn ambient_generic_nested( - //~^ ERROR [E0798] _: Wrapper, + //~^ ERROR [E0798] _: u32, _: u32, _: u32, @@ -28,8 +28,8 @@ impl Wrapper { } extern "cmse-nonsecure-entry" fn introduced_generic( - //~^ ERROR [E0798] _: U, + //~^ ERROR [E0798] _: u32, _: u32, _: u32, @@ -83,8 +83,8 @@ extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy } extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( - //~^ ERROR generics are not allowed in `extern "cmse-nonsecure-entry"` signatures v: (impl Copy, i32), + //~^ ERROR generics are not allowed in `extern "cmse-nonsecure-entry"` signatures ) -> (impl Copy, i32) { //~^ ERROR `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures v diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index e61051a4a26f4..5ddd29883f867 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,26 +1,20 @@ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:30:1 - | -LL | / extern "cmse-nonsecure-entry" fn introduced_generic( -LL | | -LL | | _: U, -LL | | _: u32, -LL | | _: u32, -LL | | _: u32, -LL | | ) -> u64 { - | |________^ + --> $DIR/generics.rs:31:8 + | +LL | _: U, + | ^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:64:1 + --> $DIR/generics.rs:64:48 | LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:79:1 + --> $DIR/generics.rs:79:57 | LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:79:71 @@ -29,13 +23,10 @@ LL | extern "cmse-nonsecure-entry" fn identity_impl_trait(v: impl Copy) -> impl | ^^^^^^^^^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:85:1 + --> $DIR/generics.rs:86:8 | -LL | / extern "cmse-nonsecure-entry" fn identity_impl_trait_nested( -LL | | -LL | | v: (impl Copy, i32), -LL | | ) -> (impl Copy, i32) { - | |_____________________^ +LL | v: (impl Copy, i32), + | ^^^^^^^^^^^^^^^^ error[E0798]: `impl Trait` is not allowed in `extern "cmse-nonsecure-entry"` signatures --> $DIR/generics.rs:88:6 @@ -44,22 +35,16 @@ LL | ) -> (impl Copy, i32) { | ^^^^^^^^^^^^^^^^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:14:5 + --> $DIR/generics.rs:14:57 | LL | extern "cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ error[E0798]: generics are not allowed in `extern "cmse-nonsecure-entry"` signatures - --> $DIR/generics.rs:19:5 - | -LL | / extern "cmse-nonsecure-entry" fn ambient_generic_nested( -LL | | -LL | | _: Wrapper, -LL | | _: u32, -LL | | _: u32, -LL | | _: u32, -LL | | ) -> u64 { - | |____________^ + --> $DIR/generics.rs:20:12 + | +LL | _: Wrapper, + | ^^^^^^^^^^ error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers --> $DIR/generics.rs:46:65 From bd5f908152b7b7dc2acf30bec6cd114e45ce4af3 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Oct 2025 14:16:26 +0200 Subject: [PATCH 1416/1900] cmse: simplify input/output check control flow --- .../src/hir_ty_lowering/cmse.rs | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index b6c6e43b1d950..f8af6888923ce 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -18,11 +18,10 @@ pub(crate) fn validate_cmse_abi<'tcx>( abi: ExternAbi, fn_sig: ty::PolyFnSig<'tcx>, ) { - match abi { - ExternAbi::CmseNonSecureCall => { - let hir_node = tcx.hir_node(hir_id); - let hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), .. }) = hir_node - else { + let fn_decl = match abi { + ExternAbi::CmseNonSecureCall => match tcx.hir_node(hir_id) { + hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), .. }) => fn_ptr_ty.decl, + _ => { let span = match tcx.parent_hir_node(hir_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::ForeignMod { .. }, @@ -39,26 +38,10 @@ pub(crate) fn validate_cmse_abi<'tcx>( ) .emit(); return; - }; - - if let Err((span, layout_err)) = - is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) - { - if should_emit_layout_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span, abi }); - } - } - - if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) { - if should_emit_layout_error(abi, layout_err) { - let span = fn_ptr_ty.decl.output.span(); - dcx.emit_err(errors::CmseGeneric { span, abi }); - } } - } + }, ExternAbi::CmseNonSecureEntry => { - let hir_node = tcx.hir_node(hir_id); - let Some(hir::FnSig { decl, .. }) = hir_node.fn_sig() else { + let Some(hir::FnSig { decl, .. }) = tcx.hir_node(hir_id).fn_sig() else { // might happen when this ABI is used incorrectly. That will be handled elsewhere return; }; @@ -69,19 +52,21 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; } - if let Err((span, layout_err)) = is_valid_cmse_inputs(tcx, dcx, fn_sig, decl, abi) { - if should_emit_layout_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span, abi }); - } - } + decl + } + _ => return, + }; - if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, decl, abi) { - if should_emit_layout_error(abi, layout_err) { - dcx.emit_err(errors::CmseGeneric { span: decl.output.span(), abi }); - } - } + if let Err((span, layout_err)) = is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_decl, abi) { + if should_emit_layout_error(abi, layout_err) { + dcx.emit_err(errors::CmseGeneric { span, abi }); + } + } + + if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_decl, abi) { + if should_emit_layout_error(abi, layout_err) { + dcx.emit_err(errors::CmseGeneric { span: fn_decl.output.span(), abi }); } - _ => (), } } From 973ddd8bc7483cb944a6632eacffea84ef814181 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 8 Oct 2025 14:21:37 +0000 Subject: [PATCH 1417/1900] Do not invalidate CFG caches in CtfeLimit. --- compiler/rustc_mir_transform/src/ctfe_limit.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index ac46336b83473..e2f518bb4ee81 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -28,12 +28,12 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { } }) .collect(); + + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); for index in indices { - insert_counter( - body.basic_blocks_mut() - .get_mut(index) - .expect("basic_blocks index {index} should exist"), - ); + let bbdata = &mut basic_blocks[index]; + let source_info = bbdata.terminator().source_info; + bbdata.statements.push(Statement::new(source_info, StatementKind::ConstEvalCounter)); } } @@ -53,10 +53,3 @@ fn has_back_edge( // Check if any of the dominators of the node are also the node's successor. node_data.terminator().successors().any(|succ| doms.dominates(succ, node)) } - -fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { - basic_block_data.statements.push(Statement::new( - basic_block_data.terminator().source_info, - StatementKind::ConstEvalCounter, - )); -} From d4ecd710a5de576a8b9f891c8169a107a638f3d6 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 8 Oct 2025 15:13:47 +0000 Subject: [PATCH 1418/1900] format: some small cleanup --- compiler/rustc_ast/src/token.rs | 4 +-- compiler/rustc_builtin_macros/src/format.rs | 37 ++++++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 6dc6d1026f621..e1231312a2afd 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -881,11 +881,11 @@ impl Token { } pub fn is_qpath_start(&self) -> bool { - self == &Lt || self == &Shl + matches!(self.kind, Lt | Shl) } pub fn is_path_start(&self) -> bool { - self == &PathSep + self.kind == PathSep || self.is_qpath_start() || matches!(self.is_metavar_seq(), Some(MetaVarKind::Path)) || self.is_path_segment_keyword() diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index bffc0407e8112..a0ee7ac19899b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -69,35 +69,26 @@ struct MacroInput { /// Ok((fmtstr, parsed arguments)) /// ``` fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { - let mut args = FormatArguments::new(); - let mut p = ecx.new_parser_from_tts(tts); - if p.token == token::Eof { - return Err(ecx.dcx().create_err(errors::FormatRequiresString { span: sp })); - } - - let first_token = &p.token; - - let fmtstr = if let token::Literal(lit) = first_token.kind - && matches!(lit.kind, token::Str | token::StrRaw(_)) - { + // parse the format string + let fmtstr = match p.token.kind { + token::Eof => return Err(ecx.dcx().create_err(errors::FormatRequiresString { span: sp })), // This allows us to properly handle cases when the first comma // after the format string is mistakenly replaced with any operator, // which cause the expression parser to eat too much tokens. - p.parse_literal_maybe_minus()? - } else { + token::Literal(token::Lit { kind: token::Str | token::StrRaw(_), .. }) => { + p.parse_literal_maybe_minus()? + } // Otherwise, we fall back to the expression parser. - p.parse_expr()? + _ => p.parse_expr()?, }; - // Only allow implicit captures to be used when the argument is a direct literal - // instead of a macro expanding to one. - let is_direct_literal = matches!(fmtstr.kind, ExprKind::Lit(_)); - + // parse comma FormatArgument pairs + let mut args = FormatArguments::new(); let mut first = true; - while p.token != token::Eof { + // parse a comma, or else report an error if !p.eat(exp!(Comma)) { if first { p.clear_expected_token_types(); @@ -120,9 +111,11 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, } } first = false; + // accept a trailing comma if p.token == token::Eof { break; - } // accept trailing commas + } + // parse a FormatArgument match p.token.ident() { Some((ident, _)) if p.look_ahead(1, |t| *t == token::Eq) => { p.bump(); @@ -156,6 +149,10 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, } } } + + // Only allow implicit captures for direct literals + let is_direct_literal = matches!(fmtstr.kind, ExprKind::Lit(_)); + Ok(MacroInput { fmtstr, args, is_direct_literal }) } From 2533f78d62dfdaec52b77d37167bba246e211378 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 8 Oct 2025 15:17:15 +0000 Subject: [PATCH 1419/1900] Reorder passes. --- compiler/rustc_mir_transform/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9ff7e0b550030..acf5a5c86f715 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -684,6 +684,8 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &inline::ForceInline, // Perform inlining, which may add a lot of code. &inline::Inline, + // Inlining may have introduced a lot of redundant code and a large move pattern. + // Now, we need to shrink the generated MIR. // Code from other crates may have storage markers, so this needs to happen after // inlining. &remove_storage_markers::RemoveStorageMarkers, @@ -695,14 +697,13 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &unreachable_enum_branching::UnreachableEnumBranching, &unreachable_prop::UnreachablePropagation, &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), - // Inlining may have introduced a lot of redundant code and a large move pattern. - // Now, we need to shrink the generated MIR. - &ref_prop::ReferencePropagation, - &sroa::ScalarReplacementOfAggregates, &multiple_return_terminators::MultipleReturnTerminators, // After simplifycfg, it allows us to discover new opportunities for peephole - // optimizations. + // optimizations. This invalidates CFG caches, so avoid putting between two SSA + // analyses. &instsimplify::InstSimplify::AfterSimplifyCfg, + &ref_prop::ReferencePropagation, + &sroa::ScalarReplacementOfAggregates, &simplify::SimplifyLocals::BeforeConstProp, &dead_store_elimination::DeadStoreElimination::Initial, &gvn::GVN, From 45ca537746f9fdb4c68eaf4bdbb8145aec554fab Mon Sep 17 00:00:00 2001 From: sayantn Date: Wed, 8 Oct 2025 20:24:03 +0530 Subject: [PATCH 1420/1900] Port the Miri implementations of SIMD intrinsics to `rustc_const_eval` --- .../src/interpret/intrinsics.rs | 104 ++- .../src/interpret/intrinsics/simd.rs | 782 ++++++++++++++++++ src/tools/miri/src/helpers.rs | 86 +- src/tools/miri/src/intrinsics/mod.rs | 2 +- src/tools/miri/src/intrinsics/simd.rs | 747 +---------------- 5 files changed, 898 insertions(+), 823 deletions(-) create mode 100644 compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 785978b4d7111..968b5fc64c336 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -2,6 +2,8 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. +mod simd; + use std::assert_matches::assert_matches; use rustc_abi::{FieldIdx, HasDataLayout, Size}; @@ -9,8 +11,8 @@ use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::ty::{FloatTy, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -121,6 +123,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; let intrinsic_name = self.tcx.item_name(instance.def_id()); + + if intrinsic_name.as_str().starts_with("simd_") { + return self.eval_simd_intrinsic(intrinsic_name, instance_args, args, dest, ret); + } + let tcx = self.tcx.tcx; match intrinsic_name { @@ -454,37 +461,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.exact_div(&val, &size, dest)?; } - sym::simd_insert => { - let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); - let elem = &args[2]; - let (input, input_len) = self.project_to_simd(&args[0])?; - let (dest, dest_len) = self.project_to_simd(dest)?; - assert_eq!(input_len, dest_len, "Return vector length must match input length"); - // Bounds are not checked by typeck so we have to do it ourselves. - if index >= input_len { - throw_ub_format!( - "`simd_insert` index {index} is out-of-bounds of vector with length {input_len}" - ); - } - - for i in 0..dest_len { - let place = self.project_index(&dest, i)?; - let value = - if i == index { elem.clone() } else { self.project_index(&input, i)? }; - self.copy_op(&value, &place)?; - } - } - sym::simd_extract => { - let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); - let (input, input_len) = self.project_to_simd(&args[0])?; - // Bounds are not checked by typeck so we have to do it ourselves. - if index >= input_len { - throw_ub_format!( - "`simd_extract` index {index} is out-of-bounds of vector with length {input_len}" - ); - } - self.copy_op(&self.project_index(&input, index)?, dest)?; - } sym::black_box => { // These just return their argument self.copy_op(&args[0], dest)?; @@ -1081,4 +1057,66 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(res, dest)?; interp_ok(()) } + + /// Converts `src` from floating point to integer type `dest_ty` + /// after rounding with mode `round`. + /// Returns `None` if `f` is NaN or out of range. + pub fn float_to_int_checked( + &self, + src: &ImmTy<'tcx, M::Provenance>, + cast_to: TyAndLayout<'tcx>, + round: rustc_apfloat::Round, + ) -> InterpResult<'tcx, Option>> { + fn float_to_int_inner<'tcx, F: rustc_apfloat::Float, M: Machine<'tcx>>( + ecx: &InterpCx<'tcx, M>, + src: F, + cast_to: TyAndLayout<'tcx>, + round: rustc_apfloat::Round, + ) -> (Scalar, rustc_apfloat::Status) { + let int_size = cast_to.layout.size; + match cast_to.ty.kind() { + // Unsigned + ty::Uint(_) => { + let res = src.to_u128_r(int_size.bits_usize(), round, &mut false); + (Scalar::from_uint(res.value, int_size), res.status) + } + // Signed + ty::Int(_) => { + let res = src.to_i128_r(int_size.bits_usize(), round, &mut false); + (Scalar::from_int(res.value, int_size), res.status) + } + // Nothing else + _ => span_bug!( + ecx.cur_span(), + "attempted float-to-int conversion with non-int output type {}", + cast_to.ty, + ), + } + } + + let ty::Float(fty) = src.layout.ty.kind() else { + bug!("float_to_int_checked: non-float input type {}", src.layout.ty) + }; + + let (val, status) = match fty { + FloatTy::F16 => float_to_int_inner(self, src.to_scalar().to_f16()?, cast_to, round), + FloatTy::F32 => float_to_int_inner(self, src.to_scalar().to_f32()?, cast_to, round), + FloatTy::F64 => float_to_int_inner(self, src.to_scalar().to_f64()?, cast_to, round), + FloatTy::F128 => float_to_int_inner(self, src.to_scalar().to_f128()?, cast_to, round), + }; + + if status.intersects( + rustc_apfloat::Status::INVALID_OP + | rustc_apfloat::Status::OVERFLOW + | rustc_apfloat::Status::UNDERFLOW, + ) { + // Floating point value is NaN (flagged with INVALID_OP) or outside the range + // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). + interp_ok(None) + } else { + // Floating point value can be represented by the integer type after rounding. + // The INEXACT flag is ignored on purpose to allow rounding. + interp_ok(Some(ImmTy::from_scalar(val, cast_to))) + } + } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs new file mode 100644 index 0000000000000..0dba66ae93721 --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -0,0 +1,782 @@ +use either::Either; +use rustc_abi::Endian; +use rustc_apfloat::{Float, Round}; +use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo}; +use rustc_middle::ty::FloatTy; +use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty}; +use rustc_span::{Symbol, sym}; +use tracing::trace; + +use super::{ + ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Provenance, Scalar, Size, interp_ok, + throw_ub_format, +}; +use crate::interpret::Writeable; + +#[derive(Copy, Clone)] +pub(crate) enum MinMax { + Min, + Max, +} + +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Returns `true` if emulation happened. + /// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own + /// intrinsic handling. + pub fn eval_simd_intrinsic( + &mut self, + intrinsic_name: Symbol, + generic_args: ty::GenericArgsRef<'tcx>, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + ret: Option, + ) -> InterpResult<'tcx, bool> { + let dest = dest.force_mplace(self)?; + + match intrinsic_name { + sym::simd_insert => { + let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); + let elem = &args[2]; + let (input, input_len) = self.project_to_simd(&args[0])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + assert_eq!(input_len, dest_len, "Return vector length must match input length"); + // Bounds are not checked by typeck so we have to do it ourselves. + if index >= input_len { + throw_ub_format!( + "`simd_insert` index {index} is out-of-bounds of vector with length {input_len}" + ); + } + + for i in 0..dest_len { + let place = self.project_index(&dest, i)?; + let value = + if i == index { elem.clone() } else { self.project_index(&input, i)? }; + self.copy_op(&value, &place)?; + } + } + sym::simd_extract => { + let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); + let (input, input_len) = self.project_to_simd(&args[0])?; + // Bounds are not checked by typeck so we have to do it ourselves. + if index >= input_len { + throw_ub_format!( + "`simd_extract` index {index} is out-of-bounds of vector with length {input_len}" + ); + } + self.copy_op(&self.project_index(&input, index)?, &dest)?; + } + sym::simd_neg + | sym::simd_fabs + | sym::simd_ceil + | sym::simd_floor + | sym::simd_round + | sym::simd_round_ties_even + | sym::simd_trunc + | sym::simd_ctlz + | sym::simd_ctpop + | sym::simd_cttz + | sym::simd_bswap + | sym::simd_bitreverse => { + let (op, op_len) = self.project_to_simd(&args[0])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, op_len); + + #[derive(Copy, Clone)] + enum Op { + MirOp(mir::UnOp), + Abs, + Round(rustc_apfloat::Round), + Numeric(Symbol), + } + let which = match intrinsic_name { + sym::simd_neg => Op::MirOp(mir::UnOp::Neg), + sym::simd_fabs => Op::Abs, + sym::simd_ceil => Op::Round(rustc_apfloat::Round::TowardPositive), + sym::simd_floor => Op::Round(rustc_apfloat::Round::TowardNegative), + sym::simd_round => Op::Round(rustc_apfloat::Round::NearestTiesToAway), + sym::simd_round_ties_even => Op::Round(rustc_apfloat::Round::NearestTiesToEven), + sym::simd_trunc => Op::Round(rustc_apfloat::Round::TowardZero), + sym::simd_ctlz => Op::Numeric(sym::ctlz), + sym::simd_ctpop => Op::Numeric(sym::ctpop), + sym::simd_cttz => Op::Numeric(sym::cttz), + sym::simd_bswap => Op::Numeric(sym::bswap), + sym::simd_bitreverse => Op::Numeric(sym::bitreverse), + _ => unreachable!(), + }; + + for i in 0..dest_len { + let op = self.read_immediate(&self.project_index(&op, i)?)?; + let dest = self.project_index(&dest, i)?; + let val = match which { + Op::MirOp(mir_op) => { + // this already does NaN adjustments + self.unary_op(mir_op, &op)?.to_scalar() + } + Op::Abs => { + // Works for f32 and f64. + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!( + self.cur_span(), + "{} operand is not a float", + intrinsic_name + ) + }; + let op = op.to_scalar(); + // "Bitwise" operation, no NaN adjustments + match float_ty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), + FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), + FloatTy::F128 => unimplemented!("f16_f128"), + } + } + Op::Round(rounding) => { + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!( + self.cur_span(), + "{} operand is not a float", + intrinsic_name + ) + }; + match float_ty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => { + let f = op.to_scalar().to_f32()?; + let res = f.round_to_integral(rounding).value; + let res = self.adjust_nan(res, &[f]); + Scalar::from_f32(res) + } + FloatTy::F64 => { + let f = op.to_scalar().to_f64()?; + let res = f.round_to_integral(rounding).value; + let res = self.adjust_nan(res, &[f]); + Scalar::from_f64(res) + } + FloatTy::F128 => unimplemented!("f16_f128"), + } + } + Op::Numeric(name) => { + self.numeric_intrinsic(name, op.to_scalar(), op.layout, op.layout)? + } + }; + self.write_scalar(val, &dest)?; + } + } + sym::simd_add + | sym::simd_sub + | sym::simd_mul + | sym::simd_div + | sym::simd_rem + | sym::simd_shl + | sym::simd_shr + | sym::simd_and + | sym::simd_or + | sym::simd_xor + | sym::simd_eq + | sym::simd_ne + | sym::simd_lt + | sym::simd_le + | sym::simd_gt + | sym::simd_ge + | sym::simd_fmax + | sym::simd_fmin + | sym::simd_saturating_add + | sym::simd_saturating_sub + | sym::simd_arith_offset => { + use mir::BinOp; + + let (left, left_len) = self.project_to_simd(&args[0])?; + let (right, right_len) = self.project_to_simd(&args[1])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + enum Op { + MirOp(BinOp), + SaturatingOp(BinOp), + FMinMax(MinMax), + WrappingOffset, + } + let which = match intrinsic_name { + sym::simd_add => Op::MirOp(BinOp::Add), + sym::simd_sub => Op::MirOp(BinOp::Sub), + sym::simd_mul => Op::MirOp(BinOp::Mul), + sym::simd_div => Op::MirOp(BinOp::Div), + sym::simd_rem => Op::MirOp(BinOp::Rem), + sym::simd_shl => Op::MirOp(BinOp::ShlUnchecked), + sym::simd_shr => Op::MirOp(BinOp::ShrUnchecked), + sym::simd_and => Op::MirOp(BinOp::BitAnd), + sym::simd_or => Op::MirOp(BinOp::BitOr), + sym::simd_xor => Op::MirOp(BinOp::BitXor), + sym::simd_eq => Op::MirOp(BinOp::Eq), + sym::simd_ne => Op::MirOp(BinOp::Ne), + sym::simd_lt => Op::MirOp(BinOp::Lt), + sym::simd_le => Op::MirOp(BinOp::Le), + sym::simd_gt => Op::MirOp(BinOp::Gt), + sym::simd_ge => Op::MirOp(BinOp::Ge), + sym::simd_fmax => Op::FMinMax(MinMax::Max), + sym::simd_fmin => Op::FMinMax(MinMax::Min), + sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add), + sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub), + sym::simd_arith_offset => Op::WrappingOffset, + _ => unreachable!(), + }; + + for i in 0..dest_len { + let left = self.read_immediate(&self.project_index(&left, i)?)?; + let right = self.read_immediate(&self.project_index(&right, i)?)?; + let dest = self.project_index(&dest, i)?; + let val = match which { + Op::MirOp(mir_op) => { + // this does NaN adjustments. + let val = self.binary_op(mir_op, &left, &right).map_err_kind(|kind| { + match kind { + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + // this resets the interpreter backtrace, but it's not worth avoiding that. + let shift_amount = match shift_amount { + Either::Left(v) => v.to_string(), + Either::Right(v) => v.to_string(), + }; + err_ub_format!("overflowing shift by {shift_amount} in `{intrinsic_name}` in lane {i}") + } + kind => kind + } + })?; + if matches!( + mir_op, + BinOp::Eq + | BinOp::Ne + | BinOp::Lt + | BinOp::Le + | BinOp::Gt + | BinOp::Ge + ) { + // Special handling for boolean-returning operations + assert_eq!(val.layout.ty, self.tcx.types.bool); + let val = val.to_scalar().to_bool().unwrap(); + bool_to_simd_element(val, dest.layout.size) + } else { + assert_ne!(val.layout.ty, self.tcx.types.bool); + assert_eq!(val.layout.ty, dest.layout.ty); + val.to_scalar() + } + } + Op::SaturatingOp(mir_op) => self.saturating_arith(mir_op, &left, &right)?, + Op::WrappingOffset => { + let ptr = left.to_scalar().to_pointer(self)?; + let offset_count = right.to_scalar().to_target_isize(self)?; + let pointee_ty = left.layout.ty.builtin_deref(true).unwrap(); + + let pointee_size = + i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self); + Scalar::from_maybe_pointer(offset_ptr, self) + } + Op::FMinMax(op) => self.fminmax_op(op, &left, &right)?, + }; + self.write_scalar(val, &dest)?; + } + } + sym::simd_reduce_and + | sym::simd_reduce_or + | sym::simd_reduce_xor + | sym::simd_reduce_any + | sym::simd_reduce_all + | sym::simd_reduce_max + | sym::simd_reduce_min => { + use mir::BinOp; + + let (op, op_len) = self.project_to_simd(&args[0])?; + + let imm_from_bool = |b| { + ImmTy::from_scalar( + Scalar::from_bool(b), + self.layout_of(self.tcx.types.bool).unwrap(), + ) + }; + + enum Op { + MirOp(BinOp), + MirOpBool(BinOp), + MinMax(MinMax), + } + let which = match intrinsic_name { + sym::simd_reduce_and => Op::MirOp(BinOp::BitAnd), + sym::simd_reduce_or => Op::MirOp(BinOp::BitOr), + sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor), + sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr), + sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd), + sym::simd_reduce_max => Op::MinMax(MinMax::Max), + sym::simd_reduce_min => Op::MinMax(MinMax::Min), + _ => unreachable!(), + }; + + // Initialize with first lane, then proceed with the rest. + let mut res = self.read_immediate(&self.project_index(&op, 0)?)?; + if matches!(which, Op::MirOpBool(_)) { + // Convert to `bool` scalar. + res = imm_from_bool(simd_element_to_bool(res)?); + } + for i in 1..op_len { + let op = self.read_immediate(&self.project_index(&op, i)?)?; + res = match which { + Op::MirOp(mir_op) => self.binary_op(mir_op, &res, &op)?, + Op::MirOpBool(mir_op) => { + let op = imm_from_bool(simd_element_to_bool(op)?); + self.binary_op(mir_op, &res, &op)? + } + Op::MinMax(mmop) => { + if matches!(res.layout.ty.kind(), ty::Float(_)) { + ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout) + } else { + // Just boring integers, so NaNs to worry about + let mirop = match mmop { + MinMax::Min => BinOp::Le, + MinMax::Max => BinOp::Ge, + }; + if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { + res + } else { + op + } + } + } + }; + } + self.write_immediate(*res, &dest)?; + } + sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => { + use mir::BinOp; + + let (op, op_len) = self.project_to_simd(&args[0])?; + let init = self.read_immediate(&args[1])?; + + let mir_op = match intrinsic_name { + sym::simd_reduce_add_ordered => BinOp::Add, + sym::simd_reduce_mul_ordered => BinOp::Mul, + _ => unreachable!(), + }; + + let mut res = init; + for i in 0..op_len { + let op = self.read_immediate(&self.project_index(&op, i)?)?; + res = self.binary_op(mir_op, &res, &op)?; + } + self.write_immediate(*res, &dest)?; + } + sym::simd_select => { + let (mask, mask_len) = self.project_to_simd(&args[0])?; + let (yes, yes_len) = self.project_to_simd(&args[1])?; + let (no, no_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, mask_len); + assert_eq!(dest_len, yes_len); + assert_eq!(dest_len, no_len); + + for i in 0..dest_len { + let mask = self.read_immediate(&self.project_index(&mask, i)?)?; + let yes = self.read_immediate(&self.project_index(&yes, i)?)?; + let no = self.read_immediate(&self.project_index(&no, i)?)?; + let dest = self.project_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { yes } else { no }; + self.write_immediate(*val, &dest)?; + } + } + // Variant of `select` that takes a bitmask rather than a "vector of bool". + sym::simd_select_bitmask => { + let mask = &args[0]; + let (yes, yes_len) = self.project_to_simd(&args[1])?; + let (no, no_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + let bitmask_len = dest_len.next_multiple_of(8); + if bitmask_len > 64 { + throw_unsup_format!( + "simd_select_bitmask: vectors larger than 64 elements are currently not supported" + ); + } + + assert_eq!(dest_len, yes_len); + assert_eq!(dest_len, no_len); + + // Read the mask, either as an integer or as an array. + let mask: u64 = match mask.layout.ty.kind() { + ty::Uint(_) => { + // Any larger integer type is fine. + assert!(mask.layout.size.bits() >= bitmask_len); + self.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap() + } + ty::Array(elem, _len) if elem == &self.tcx.types.u8 => { + // The array must have exactly the right size. + assert_eq!(mask.layout.size.bits(), bitmask_len); + // Read the raw bytes. + let mask = mask.assert_mem_place(); // arrays cannot be immediate + let mask_bytes = + self.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?; + // Turn them into a `u64` in the right way. + let mask_size = mask.layout.size.bytes_usize(); + let mut mask_arr = [0u8; 8]; + match self.tcx.data_layout.endian { + Endian::Little => { + // Fill the first N bytes. + mask_arr[..mask_size].copy_from_slice(mask_bytes); + u64::from_le_bytes(mask_arr) + } + Endian::Big => { + // Fill the last N bytes. + let i = mask_arr.len().strict_sub(mask_size); + mask_arr[i..].copy_from_slice(mask_bytes); + u64::from_be_bytes(mask_arr) + } + } + } + _ => bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty), + }; + + let dest_len = u32::try_from(dest_len).unwrap(); + for i in 0..dest_len { + let bit_i = simd_bitmask_index(i, dest_len, self.tcx.data_layout.endian); + let mask = mask & 1u64.strict_shl(bit_i); + let yes = self.read_immediate(&self.project_index(&yes, i.into())?)?; + let no = self.read_immediate(&self.project_index(&no, i.into())?)?; + let dest = self.project_index(&dest, i.into())?; + + let val = if mask != 0 { yes } else { no }; + self.write_immediate(*val, &dest)?; + } + // The remaining bits of the mask are ignored. + } + // Converts a "vector of bool" into a bitmask. + sym::simd_bitmask => { + let (op, op_len) = self.project_to_simd(&args[0])?; + let bitmask_len = op_len.next_multiple_of(8); + if bitmask_len > 64 { + throw_unsup_format!( + "simd_bitmask: vectors larger than 64 elements are currently not supported" + ); + } + + let op_len = u32::try_from(op_len).unwrap(); + let mut res = 0u64; + for i in 0..op_len { + let op = self.read_immediate(&self.project_index(&op, i.into())?)?; + if simd_element_to_bool(op)? { + let bit_i = simd_bitmask_index(i, op_len, self.tcx.data_layout.endian); + res |= 1u64.strict_shl(bit_i); + } + } + // Write the result, depending on the `dest` type. + // Returns either an unsigned integer or array of `u8`. + match dest.layout.ty.kind() { + ty::Uint(_) => { + // Any larger integer type is fine, it will be zero-extended. + assert!(dest.layout.size.bits() >= bitmask_len); + self.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?; + } + ty::Array(elem, _len) if elem == &self.tcx.types.u8 => { + // The array must have exactly the right size. + assert_eq!(dest.layout.size.bits(), bitmask_len); + // We have to write the result byte-for-byte. + let res_size = dest.layout.size.bytes_usize(); + let res_bytes; + let res_bytes_slice = match self.tcx.data_layout.endian { + Endian::Little => { + res_bytes = res.to_le_bytes(); + &res_bytes[..res_size] // take the first N bytes + } + Endian::Big => { + res_bytes = res.to_be_bytes(); + &res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes + } + }; + self.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?; + } + _ => bug!("simd_bitmask: invalid return type {}", dest.layout.ty), + } + } + sym::simd_cast + | sym::simd_as + | sym::simd_cast_ptr + | sym::simd_with_exposed_provenance => { + let (op, op_len) = self.project_to_simd(&args[0])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, op_len); + + let unsafe_cast = intrinsic_name == sym::simd_cast; + let safe_cast = intrinsic_name == sym::simd_as; + let ptr_cast = intrinsic_name == sym::simd_cast_ptr; + let from_exposed_cast = intrinsic_name == sym::simd_with_exposed_provenance; + + for i in 0..dest_len { + let op = self.read_immediate(&self.project_index(&op, i)?)?; + let dest = self.project_index(&dest, i)?; + + let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { + // Int-to-(int|float): always safe + (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) + if safe_cast || unsafe_cast => + self.int_to_int_or_float(&op, dest.layout)?, + // Float-to-float: always safe + (ty::Float(_), ty::Float(_)) if safe_cast || unsafe_cast => + self.float_to_float_or_int(&op, dest.layout)?, + // Float-to-int in safe mode + (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => + self.float_to_float_or_int(&op, dest.layout)?, + // Float-to-int in unchecked mode + (ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { + self.float_to_int_checked(&op, dest.layout, Round::TowardZero)? + .ok_or_else(|| { + err_ub_format!( + "`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })? + } + // Ptr-to-ptr cast + (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast => + self.ptr_to_ptr(&op, dest.layout)?, + // Int->Ptr casts + (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => + self.pointer_with_exposed_provenance_cast(&op, dest.layout)?, + // Error otherwise + _ => + throw_unsup_format!( + "Unsupported SIMD cast from element type {from_ty} to {to_ty}", + from_ty = op.layout.ty, + to_ty = dest.layout.ty, + ), + }; + self.write_immediate(*val, &dest)?; + } + } + sym::simd_shuffle_const_generic => { + let (left, left_len) = self.project_to_simd(&args[0])?; + let (right, right_len) = self.project_to_simd(&args[1])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + let index = generic_args[2].expect_const().to_value().valtree.unwrap_branch(); + let index_len = index.len(); + + assert_eq!(left_len, right_len); + assert_eq!(u64::try_from(index_len).unwrap(), dest_len); + + for i in 0..dest_len { + let src_index: u64 = + index[usize::try_from(i).unwrap()].unwrap_leaf().to_u32().into(); + let dest = self.project_index(&dest, i)?; + + let val = if src_index < left_len { + self.read_immediate(&self.project_index(&left, src_index)?)? + } else if src_index < left_len.strict_add(right_len) { + let right_idx = src_index.strict_sub(left_len); + self.read_immediate(&self.project_index(&right, right_idx)?)? + } else { + throw_ub_format!( + "`simd_shuffle_const_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}" + ); + }; + self.write_immediate(*val, &dest)?; + } + } + sym::simd_shuffle => { + let (left, left_len) = self.project_to_simd(&args[0])?; + let (right, right_len) = self.project_to_simd(&args[1])?; + let (index, index_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(index_len, dest_len); + + for i in 0..dest_len { + let src_index: u64 = self + .read_immediate(&self.project_index(&index, i)?)? + .to_scalar() + .to_u32()? + .into(); + let dest = self.project_index(&dest, i)?; + + let val = if src_index < left_len { + self.read_immediate(&self.project_index(&left, src_index)?)? + } else if src_index < left_len.strict_add(right_len) { + let right_idx = src_index.strict_sub(left_len); + self.read_immediate(&self.project_index(&right, right_idx)?)? + } else { + throw_ub_format!( + "`simd_shuffle` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}" + ); + }; + self.write_immediate(*val, &dest)?; + } + } + sym::simd_gather => { + let (passthru, passthru_len) = self.project_to_simd(&args[0])?; + let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?; + let (mask, mask_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, passthru_len); + assert_eq!(dest_len, ptrs_len); + assert_eq!(dest_len, mask_len); + + for i in 0..dest_len { + let passthru = self.read_immediate(&self.project_index(&passthru, i)?)?; + let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?; + let mask = self.read_immediate(&self.project_index(&mask, i)?)?; + let dest = self.project_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { + let place = self.deref_pointer(&ptr)?; + self.read_immediate(&place)? + } else { + passthru + }; + self.write_immediate(*val, &dest)?; + } + } + sym::simd_scatter => { + let (value, value_len) = self.project_to_simd(&args[0])?; + let (ptrs, ptrs_len) = self.project_to_simd(&args[1])?; + let (mask, mask_len) = self.project_to_simd(&args[2])?; + + assert_eq!(ptrs_len, value_len); + assert_eq!(ptrs_len, mask_len); + + for i in 0..ptrs_len { + let value = self.read_immediate(&self.project_index(&value, i)?)?; + let ptr = self.read_immediate(&self.project_index(&ptrs, i)?)?; + let mask = self.read_immediate(&self.project_index(&mask, i)?)?; + + if simd_element_to_bool(mask)? { + let place = self.deref_pointer(&ptr)?; + self.write_immediate(*value, &place)?; + } + } + } + sym::simd_masked_load => { + let (mask, mask_len) = self.project_to_simd(&args[0])?; + let ptr = self.read_pointer(&args[1])?; + let (default, default_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, mask_len); + assert_eq!(dest_len, default_len); + + for i in 0..dest_len { + let mask = self.read_immediate(&self.project_index(&mask, i)?)?; + let default = self.read_immediate(&self.project_index(&default, i)?)?; + let dest = self.project_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { + // Size * u64 is implemented as always checked + let ptr = ptr.wrapping_offset(dest.layout.size * i, self); + let place = self.ptr_to_mplace(ptr, dest.layout); + self.read_immediate(&place)? + } else { + default + }; + self.write_immediate(*val, &dest)?; + } + } + sym::simd_masked_store => { + let (mask, mask_len) = self.project_to_simd(&args[0])?; + let ptr = self.read_pointer(&args[1])?; + let (vals, vals_len) = self.project_to_simd(&args[2])?; + + assert_eq!(mask_len, vals_len); + + for i in 0..vals_len { + let mask = self.read_immediate(&self.project_index(&mask, i)?)?; + let val = self.read_immediate(&self.project_index(&vals, i)?)?; + + if simd_element_to_bool(mask)? { + // Size * u64 is implemented as always checked + let ptr = ptr.wrapping_offset(val.layout.size * i, self); + let place = self.ptr_to_mplace(ptr, val.layout); + self.write_immediate(*val, &place)? + }; + } + } + + // Unsupported intrinsic: skip the return_to_block below. + _ => return interp_ok(false), + } + + trace!("{:?}", self.dump_place(&dest.clone().into())); + self.return_to_block(ret)?; + interp_ok(true) + } + + fn fminmax_op( + &self, + op: MinMax, + left: &ImmTy<'tcx, Prov>, + right: &ImmTy<'tcx, Prov>, + ) -> InterpResult<'tcx, Scalar> { + assert_eq!(left.layout.ty, right.layout.ty); + let ty::Float(float_ty) = left.layout.ty.kind() else { + bug!("fmax operand is not a float") + }; + let left = left.to_scalar(); + let right = right.to_scalar(); + interp_ok(match float_ty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => { + let left = left.to_f32()?; + let right = right.to_f32()?; + let res = match op { + MinMax::Min => left.min(right), + MinMax::Max => left.max(right), + }; + let res = self.adjust_nan(res, &[left, right]); + Scalar::from_f32(res) + } + FloatTy::F64 => { + let left = left.to_f64()?; + let right = right.to_f64()?; + let res = match op { + MinMax::Min => left.min(right), + MinMax::Max => left.max(right), + }; + let res = self.adjust_nan(res, &[left, right]); + Scalar::from_f64(res) + } + FloatTy::F128 => unimplemented!("f16_f128"), + }) + } +} + +fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 { + assert!(idx < vec_len); + match endianness { + Endian::Little => idx, + #[expect(clippy::arithmetic_side_effects)] // idx < vec_len + Endian::Big => vec_len - 1 - idx, // reverse order of bits + } +} + +fn bool_to_simd_element(b: bool, size: Size) -> Scalar { + // SIMD uses all-1 as pattern for "true". In two's complement, + // -1 has all its bits set to one and `from_int` will truncate or + // sign-extend it to `size` as required. + let val = if b { -1 } else { 0 }; + Scalar::from_int(val, size) +} + +fn simd_element_to_bool(elem: ImmTy<'_, Prov>) -> InterpResult<'_, bool> { + assert!( + matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)), + "SIMD mask element type must be an integer, but this is `{}`", + elem.layout.ty + ); + let val = elem.to_scalar().to_int(elem.layout.size)?; + interp_ok(match val { + 0 => false, + -1 => true, + _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), + }) +} diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index e0c077e99319a..d6646f9586aa6 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -5,7 +5,6 @@ use std::{cmp, iter}; use rand::RngCore; use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; use rustc_apfloat::Float; -use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_hir::Safety; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}; @@ -14,7 +13,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; @@ -961,75 +960,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap(); } - /// Converts `src` from floating point to integer type `dest_ty` - /// after rounding with mode `round`. - /// Returns `None` if `f` is NaN or out of range. - fn float_to_int_checked( - &self, - src: &ImmTy<'tcx>, - cast_to: TyAndLayout<'tcx>, - round: rustc_apfloat::Round, - ) -> InterpResult<'tcx, Option>> { - let this = self.eval_context_ref(); - - fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>( - ecx: &MiriInterpCx<'tcx>, - src: F, - cast_to: TyAndLayout<'tcx>, - round: rustc_apfloat::Round, - ) -> (Scalar, rustc_apfloat::Status) { - let int_size = cast_to.layout.size; - match cast_to.ty.kind() { - // Unsigned - ty::Uint(_) => { - let res = src.to_u128_r(int_size.bits_usize(), round, &mut false); - (Scalar::from_uint(res.value, int_size), res.status) - } - // Signed - ty::Int(_) => { - let res = src.to_i128_r(int_size.bits_usize(), round, &mut false); - (Scalar::from_int(res.value, int_size), res.status) - } - // Nothing else - _ => - span_bug!( - ecx.cur_span(), - "attempted float-to-int conversion with non-int output type {}", - cast_to.ty, - ), - } - } - - let ty::Float(fty) = src.layout.ty.kind() else { - bug!("float_to_int_checked: non-float input type {}", src.layout.ty) - }; - - let (val, status) = match fty { - FloatTy::F16 => - float_to_int_inner::(this, src.to_scalar().to_f16()?, cast_to, round), - FloatTy::F32 => - float_to_int_inner::(this, src.to_scalar().to_f32()?, cast_to, round), - FloatTy::F64 => - float_to_int_inner::(this, src.to_scalar().to_f64()?, cast_to, round), - FloatTy::F128 => - float_to_int_inner::(this, src.to_scalar().to_f128()?, cast_to, round), - }; - - if status.intersects( - rustc_apfloat::Status::INVALID_OP - | rustc_apfloat::Status::OVERFLOW - | rustc_apfloat::Status::UNDERFLOW, - ) { - // Floating point value is NaN (flagged with INVALID_OP) or outside the range - // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). - interp_ok(None) - } else { - // Floating point value can be represented by the integer type after rounding. - // The INEXACT flag is ignored on purpose to allow rounding. - interp_ok(Some(ImmTy::from_scalar(val, cast_to))) - } - } - /// Returns an integer type that is twice wide as `ty` fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> { let this = self.eval_context_ref(); @@ -1194,20 +1124,6 @@ pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { Scalar::from_int(val, size) } -pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> { - assert!( - matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)), - "SIMD mask element type must be an integer, but this is `{}`", - elem.layout.ty - ); - let val = elem.to_scalar().to_int(elem.layout.size)?; - interp_ok(match val { - 0 => false, - -1 => true, - _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), - }) -} - /// Check whether an operation that writes to a target buffer was successful. /// Accordingly select return value. /// Local helper function to be used in Windows shims. diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index a80b939d84ea9..f09fc6c187896 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -118,7 +118,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.emulate_atomic_intrinsic(name, generic_args, args, dest); } if let Some(name) = intrinsic_name.strip_prefix("simd_") { - return this.emulate_simd_intrinsic(name, generic_args, args, dest); + return this.emulate_simd_intrinsic(name, args, dest); } match intrinsic_name { diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index b26516c0ff0e8..5f75657e0a220 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,21 +1,12 @@ -use either::Either; use rand::Rng; -use rustc_abi::{Endian, HasDataLayout}; -use rustc_apfloat::{Float, Round}; +use rustc_apfloat::Float; use rustc_middle::ty::FloatTy; -use rustc_middle::{mir, ty}; -use rustc_span::{Symbol, sym}; +use rustc_middle::ty; use super::check_intrinsic_arg_count; -use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, simd_element_to_bool}; +use crate::helpers::{ToHost, ToSoft}; use crate::*; -#[derive(Copy, Clone)] -pub(crate) enum MinMax { - Min, - Max, -} - impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed. @@ -23,20 +14,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_simd_intrinsic( &mut self, intrinsic_name: &str, - generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match intrinsic_name { #[rustfmt::skip] - | "neg" - | "fabs" - | "ceil" - | "floor" - | "round" - | "round_ties_even" - | "trunc" | "fsqrt" | "fsin" | "fcos" @@ -45,11 +28,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "flog" | "flog2" | "flog10" - | "ctlz" - | "ctpop" - | "cttz" - | "bswap" - | "bitreverse" => { let [op] = check_intrinsic_arg_count(args)?; let (op, op_len) = this.project_to_simd(op)?; @@ -57,235 +35,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(dest_len, op_len); - #[derive(Copy, Clone)] - enum Op<'a> { - MirOp(mir::UnOp), - Abs, - Round(rustc_apfloat::Round), - Numeric(Symbol), - HostOp(&'a str), - } - let which = match intrinsic_name { - "neg" => Op::MirOp(mir::UnOp::Neg), - "fabs" => Op::Abs, - "ceil" => Op::Round(rustc_apfloat::Round::TowardPositive), - "floor" => Op::Round(rustc_apfloat::Round::TowardNegative), - "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), - "round_ties_even" => Op::Round(rustc_apfloat::Round::NearestTiesToEven), - "trunc" => Op::Round(rustc_apfloat::Round::TowardZero), - "ctlz" => Op::Numeric(sym::ctlz), - "ctpop" => Op::Numeric(sym::ctpop), - "cttz" => Op::Numeric(sym::cttz), - "bswap" => Op::Numeric(sym::bswap), - "bitreverse" => Op::Numeric(sym::bitreverse), - _ => Op::HostOp(intrinsic_name), - }; - for i in 0..dest_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; - let val = match which { - Op::MirOp(mir_op) => { - // This already does NaN adjustments - this.unary_op(mir_op, &op)?.to_scalar() - } - Op::Abs => { - // Works for f32 and f64. - let ty::Float(float_ty) = op.layout.ty.kind() else { - span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) - }; - let op = op.to_scalar(); - // "Bitwise" operation, no NaN adjustments - match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), - FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), - FloatTy::F128 => unimplemented!("f16_f128"), - } - } - Op::HostOp(host_op) => { - let ty::Float(float_ty) = op.layout.ty.kind() else { - span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + }; + // Using host floats except for sqrt (but it's fine, these operations do not + // have guaranteed precision). + let val = match float_ty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => { + let f = op.to_scalar().to_f32()?; + let res = match intrinsic_name { + "fsqrt" => math::sqrt(f), + "fsin" => f.to_host().sin().to_soft(), + "fcos" => f.to_host().cos().to_soft(), + "fexp" => f.to_host().exp().to_soft(), + "fexp2" => f.to_host().exp2().to_soft(), + "flog" => f.to_host().ln().to_soft(), + "flog2" => f.to_host().log2().to_soft(), + "flog10" => f.to_host().log10().to_soft(), + _ => bug!(), }; - // Using host floats except for sqrt (but it's fine, these operations do not - // have guaranteed precision). - match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let f = op.to_scalar().to_f32()?; - let res = match host_op { - "fsqrt" => math::sqrt(f), - "fsin" => f.to_host().sin().to_soft(), - "fcos" => f.to_host().cos().to_soft(), - "fexp" => f.to_host().exp().to_soft(), - "fexp2" => f.to_host().exp2().to_soft(), - "flog" => f.to_host().ln().to_soft(), - "flog2" => f.to_host().log2().to_soft(), - "flog10" => f.to_host().log10().to_soft(), - _ => bug!(), - }; - let res = this.adjust_nan(res, &[f]); - Scalar::from(res) - } - FloatTy::F64 => { - let f = op.to_scalar().to_f64()?; - let res = match host_op { - "fsqrt" => math::sqrt(f), - "fsin" => f.to_host().sin().to_soft(), - "fcos" => f.to_host().cos().to_soft(), - "fexp" => f.to_host().exp().to_soft(), - "fexp2" => f.to_host().exp2().to_soft(), - "flog" => f.to_host().ln().to_soft(), - "flog2" => f.to_host().log2().to_soft(), - "flog10" => f.to_host().log10().to_soft(), - _ => bug!(), - }; - let res = this.adjust_nan(res, &[f]); - Scalar::from(res) - } - FloatTy::F128 => unimplemented!("f16_f128"), - } + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) } - Op::Round(rounding) => { - let ty::Float(float_ty) = op.layout.ty.kind() else { - span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + FloatTy::F64 => { + let f = op.to_scalar().to_f64()?; + let res = match intrinsic_name { + "fsqrt" => math::sqrt(f), + "fsin" => f.to_host().sin().to_soft(), + "fcos" => f.to_host().cos().to_soft(), + "fexp" => f.to_host().exp().to_soft(), + "fexp2" => f.to_host().exp2().to_soft(), + "flog" => f.to_host().ln().to_soft(), + "flog2" => f.to_host().log2().to_soft(), + "flog10" => f.to_host().log10().to_soft(), + _ => bug!(), }; - match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let f = op.to_scalar().to_f32()?; - let res = f.round_to_integral(rounding).value; - let res = this.adjust_nan(res, &[f]); - Scalar::from_f32(res) - } - FloatTy::F64 => { - let f = op.to_scalar().to_f64()?; - let res = f.round_to_integral(rounding).value; - let res = this.adjust_nan(res, &[f]); - Scalar::from_f64(res) - } - FloatTy::F128 => unimplemented!("f16_f128"), - } - } - Op::Numeric(name) => { - this.numeric_intrinsic(name, op.to_scalar(), op.layout, op.layout)? - } - }; - this.write_scalar(val, &dest)?; - } - } - #[rustfmt::skip] - | "add" - | "sub" - | "mul" - | "div" - | "rem" - | "shl" - | "shr" - | "and" - | "or" - | "xor" - | "eq" - | "ne" - | "lt" - | "le" - | "gt" - | "ge" - | "fmax" - | "fmin" - | "saturating_add" - | "saturating_sub" - | "arith_offset" - => { - use mir::BinOp; - - let [left, right] = check_intrinsic_arg_count(args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum Op { - MirOp(BinOp), - SaturatingOp(BinOp), - FMinMax(MinMax), - WrappingOffset, - } - let which = match intrinsic_name { - "add" => Op::MirOp(BinOp::Add), - "sub" => Op::MirOp(BinOp::Sub), - "mul" => Op::MirOp(BinOp::Mul), - "div" => Op::MirOp(BinOp::Div), - "rem" => Op::MirOp(BinOp::Rem), - "shl" => Op::MirOp(BinOp::ShlUnchecked), - "shr" => Op::MirOp(BinOp::ShrUnchecked), - "and" => Op::MirOp(BinOp::BitAnd), - "or" => Op::MirOp(BinOp::BitOr), - "xor" => Op::MirOp(BinOp::BitXor), - "eq" => Op::MirOp(BinOp::Eq), - "ne" => Op::MirOp(BinOp::Ne), - "lt" => Op::MirOp(BinOp::Lt), - "le" => Op::MirOp(BinOp::Le), - "gt" => Op::MirOp(BinOp::Gt), - "ge" => Op::MirOp(BinOp::Ge), - "fmax" => Op::FMinMax(MinMax::Max), - "fmin" => Op::FMinMax(MinMax::Min), - "saturating_add" => Op::SaturatingOp(BinOp::Add), - "saturating_sub" => Op::SaturatingOp(BinOp::Sub), - "arith_offset" => Op::WrappingOffset, - _ => unreachable!(), - }; - - for i in 0..dest_len { - let left = this.read_immediate(&this.project_index(&left, i)?)?; - let right = this.read_immediate(&this.project_index(&right, i)?)?; - let dest = this.project_index(&dest, i)?; - let val = match which { - Op::MirOp(mir_op) => { - // This does NaN adjustments. - let val = this.binary_op(mir_op, &left, &right).map_err_kind(|kind| { - match kind { - InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { - // This resets the interpreter backtrace, but it's not worth avoiding that. - let shift_amount = match shift_amount { - Either::Left(v) => v.to_string(), - Either::Right(v) => v.to_string(), - }; - err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}") - } - kind => kind - } - })?; - if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { - // Special handling for boolean-returning operations - assert_eq!(val.layout.ty, this.tcx.types.bool); - let val = val.to_scalar().to_bool().unwrap(); - bool_to_simd_element(val, dest.layout.size) - } else { - assert_ne!(val.layout.ty, this.tcx.types.bool); - assert_eq!(val.layout.ty, dest.layout.ty); - val.to_scalar() - } - } - Op::SaturatingOp(mir_op) => { - this.saturating_arith(mir_op, &left, &right)? - } - Op::WrappingOffset => { - let ptr = left.to_scalar().to_pointer(this)?; - let offset_count = right.to_scalar().to_target_isize(this)?; - let pointee_ty = left.layout.ty.builtin_deref(true).unwrap(); - - let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap(); - let offset_bytes = offset_count.wrapping_mul(pointee_size); - let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, this); - Scalar::from_maybe_pointer(offset_ptr, this) - } - Op::FMinMax(op) => { - this.fminmax_op(op, &left, &right)? + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) } + FloatTy::F128 => unimplemented!("f16_f128"), }; + this.write_scalar(val, &dest)?; } } @@ -345,279 +139,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(val, &dest)?; } } - #[rustfmt::skip] - | "reduce_and" - | "reduce_or" - | "reduce_xor" - | "reduce_any" - | "reduce_all" - | "reduce_max" - | "reduce_min" => { - use mir::BinOp; - - let [op] = check_intrinsic_arg_count(args)?; - let (op, op_len) = this.project_to_simd(op)?; - - let imm_from_bool = - |b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool); - - enum Op { - MirOp(BinOp), - MirOpBool(BinOp), - MinMax(MinMax), - } - let which = match intrinsic_name { - "reduce_and" => Op::MirOp(BinOp::BitAnd), - "reduce_or" => Op::MirOp(BinOp::BitOr), - "reduce_xor" => Op::MirOp(BinOp::BitXor), - "reduce_any" => Op::MirOpBool(BinOp::BitOr), - "reduce_all" => Op::MirOpBool(BinOp::BitAnd), - "reduce_max" => Op::MinMax(MinMax::Max), - "reduce_min" => Op::MinMax(MinMax::Min), - _ => unreachable!(), - }; - - // Initialize with first lane, then proceed with the rest. - let mut res = this.read_immediate(&this.project_index(&op, 0)?)?; - if matches!(which, Op::MirOpBool(_)) { - // Convert to `bool` scalar. - res = imm_from_bool(simd_element_to_bool(res)?); - } - for i in 1..op_len { - let op = this.read_immediate(&this.project_index(&op, i)?)?; - res = match which { - Op::MirOp(mir_op) => { - this.binary_op(mir_op, &res, &op)? - } - Op::MirOpBool(mir_op) => { - let op = imm_from_bool(simd_element_to_bool(op)?); - this.binary_op(mir_op, &res, &op)? - } - Op::MinMax(mmop) => { - if matches!(res.layout.ty.kind(), ty::Float(_)) { - ImmTy::from_scalar(this.fminmax_op(mmop, &res, &op)?, res.layout) - } else { - // Just boring integers, so NaNs to worry about - let mirop = match mmop { - MinMax::Min => BinOp::Le, - MinMax::Max => BinOp::Ge, - }; - if this.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { - res - } else { - op - } - } - } - }; - } - this.write_immediate(*res, dest)?; - } - #[rustfmt::skip] - | "reduce_add_ordered" - | "reduce_mul_ordered" => { - use mir::BinOp; - - let [op, init] = check_intrinsic_arg_count(args)?; - let (op, op_len) = this.project_to_simd(op)?; - let init = this.read_immediate(init)?; - - let mir_op = match intrinsic_name { - "reduce_add_ordered" => BinOp::Add, - "reduce_mul_ordered" => BinOp::Mul, - _ => unreachable!(), - }; - - let mut res = init; - for i in 0..op_len { - let op = this.read_immediate(&this.project_index(&op, i)?)?; - res = this.binary_op(mir_op, &res, &op)?; - } - this.write_immediate(*res, dest)?; - } - "select" => { - let [mask, yes, no] = check_intrinsic_arg_count(args)?; - let (mask, mask_len) = this.project_to_simd(mask)?; - let (yes, yes_len) = this.project_to_simd(yes)?; - let (no, no_len) = this.project_to_simd(no)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, mask_len); - assert_eq!(dest_len, yes_len); - assert_eq!(dest_len, no_len); - - for i in 0..dest_len { - let mask = this.read_immediate(&this.project_index(&mask, i)?)?; - let yes = this.read_immediate(&this.project_index(&yes, i)?)?; - let no = this.read_immediate(&this.project_index(&no, i)?)?; - let dest = this.project_index(&dest, i)?; - - let val = if simd_element_to_bool(mask)? { yes } else { no }; - this.write_immediate(*val, &dest)?; - } - } - // Variant of `select` that takes a bitmask rather than a "vector of bool". - "select_bitmask" => { - let [mask, yes, no] = check_intrinsic_arg_count(args)?; - let (yes, yes_len) = this.project_to_simd(yes)?; - let (no, no_len) = this.project_to_simd(no)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - let bitmask_len = dest_len.next_multiple_of(8); - if bitmask_len > 64 { - throw_unsup_format!( - "simd_select_bitmask: vectors larger than 64 elements are currently not supported" - ); - } - - assert_eq!(dest_len, yes_len); - assert_eq!(dest_len, no_len); - - // Read the mask, either as an integer or as an array. - let mask: u64 = match mask.layout.ty.kind() { - ty::Uint(_) => { - // Any larger integer type is fine. - assert!(mask.layout.size.bits() >= bitmask_len); - this.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap() - } - ty::Array(elem, _len) if elem == &this.tcx.types.u8 => { - // The array must have exactly the right size. - assert_eq!(mask.layout.size.bits(), bitmask_len); - // Read the raw bytes. - let mask = mask.assert_mem_place(); // arrays cannot be immediate - let mask_bytes = - this.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?; - // Turn them into a `u64` in the right way. - let mask_size = mask.layout.size.bytes_usize(); - let mut mask_arr = [0u8; 8]; - match this.data_layout().endian { - Endian::Little => { - // Fill the first N bytes. - mask_arr[..mask_size].copy_from_slice(mask_bytes); - u64::from_le_bytes(mask_arr) - } - Endian::Big => { - // Fill the last N bytes. - let i = mask_arr.len().strict_sub(mask_size); - mask_arr[i..].copy_from_slice(mask_bytes); - u64::from_be_bytes(mask_arr) - } - } - } - _ => bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty), - }; - - let dest_len = u32::try_from(dest_len).unwrap(); - for i in 0..dest_len { - let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian); - let mask = mask & 1u64.strict_shl(bit_i); - let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?; - let no = this.read_immediate(&this.project_index(&no, i.into())?)?; - let dest = this.project_index(&dest, i.into())?; - - let val = if mask != 0 { yes } else { no }; - this.write_immediate(*val, &dest)?; - } - // The remaining bits of the mask are ignored. - } - // Converts a "vector of bool" into a bitmask. - "bitmask" => { - let [op] = check_intrinsic_arg_count(args)?; - let (op, op_len) = this.project_to_simd(op)?; - let bitmask_len = op_len.next_multiple_of(8); - if bitmask_len > 64 { - throw_unsup_format!( - "simd_bitmask: vectors larger than 64 elements are currently not supported" - ); - } - - let op_len = u32::try_from(op_len).unwrap(); - let mut res = 0u64; - for i in 0..op_len { - let op = this.read_immediate(&this.project_index(&op, i.into())?)?; - if simd_element_to_bool(op)? { - let bit_i = simd_bitmask_index(i, op_len, this.data_layout().endian); - res |= 1u64.strict_shl(bit_i); - } - } - // Write the result, depending on the `dest` type. - // Returns either an unsigned integer or array of `u8`. - match dest.layout.ty.kind() { - ty::Uint(_) => { - // Any larger integer type is fine, it will be zero-extended. - assert!(dest.layout.size.bits() >= bitmask_len); - this.write_int(res, dest)?; - } - ty::Array(elem, _len) if elem == &this.tcx.types.u8 => { - // The array must have exactly the right size. - assert_eq!(dest.layout.size.bits(), bitmask_len); - // We have to write the result byte-for-byte. - let res_size = dest.layout.size.bytes_usize(); - let res_bytes; - let res_bytes_slice = match this.data_layout().endian { - Endian::Little => { - res_bytes = res.to_le_bytes(); - &res_bytes[..res_size] // take the first N bytes - } - Endian::Big => { - res_bytes = res.to_be_bytes(); - &res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes - } - }; - this.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?; - } - _ => bug!("simd_bitmask: invalid return type {}", dest.layout.ty), - } - } - "cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => { + "expose_provenance" => { let [op] = check_intrinsic_arg_count(args)?; let (op, op_len) = this.project_to_simd(op)?; let (dest, dest_len) = this.project_to_simd(dest)?; assert_eq!(dest_len, op_len); - let unsafe_cast = intrinsic_name == "cast"; - let safe_cast = intrinsic_name == "as"; - let ptr_cast = intrinsic_name == "cast_ptr"; - let expose_cast = intrinsic_name == "expose_provenance"; - let from_exposed_cast = intrinsic_name == "with_exposed_provenance"; - for i in 0..dest_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { - // Int-to-(int|float): always safe - (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) - if safe_cast || unsafe_cast => - this.int_to_int_or_float(&op, dest.layout)?, - // Float-to-float: always safe - (ty::Float(_), ty::Float(_)) if safe_cast || unsafe_cast => - this.float_to_float_or_int(&op, dest.layout)?, - // Float-to-int in safe mode - (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => - this.float_to_float_or_int(&op, dest.layout)?, - // Float-to-int in unchecked mode - (ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { - this.float_to_int_checked(&op, dest.layout, Round::TowardZero)? - .ok_or_else(|| { - err_ub_format!( - "`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })? - } - // Ptr-to-ptr cast - (ty::RawPtr(..), ty::RawPtr(..)) if ptr_cast => - this.ptr_to_ptr(&op, dest.layout)?, // Ptr/Int casts - (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => + (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) => this.pointer_expose_provenance_cast(&op, dest.layout)?, - (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => - this.pointer_with_exposed_provenance_cast(&op, dest.layout)?, // Error otherwise _ => throw_unsup_format!( - "Unsupported SIMD cast from element type {from_ty} to {to_ty}", + "Unsupported `simd_expose_provenance` from element type {from_ty} to {to_ty}", from_ty = op.layout.ty, to_ty = dest.layout.ty, ), @@ -625,210 +165,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_immediate(*val, &dest)?; } } - "shuffle_const_generic" => { - let [left, right] = check_intrinsic_arg_count(args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - let index = generic_args[2].expect_const().to_value().valtree.unwrap_branch(); - let index_len = index.len(); - - assert_eq!(left_len, right_len); - assert_eq!(u64::try_from(index_len).unwrap(), dest_len); - - for i in 0..dest_len { - let src_index: u64 = - index[usize::try_from(i).unwrap()].unwrap_leaf().to_u32().into(); - let dest = this.project_index(&dest, i)?; - - let val = if src_index < left_len { - this.read_immediate(&this.project_index(&left, src_index)?)? - } else if src_index < left_len.strict_add(right_len) { - let right_idx = src_index.strict_sub(left_len); - this.read_immediate(&this.project_index(&right, right_idx)?)? - } else { - throw_ub_format!( - "`simd_shuffle_const_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}" - ); - }; - this.write_immediate(*val, &dest)?; - } - } - "shuffle" => { - let [left, right, index] = check_intrinsic_arg_count(args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (index, index_len) = this.project_to_simd(index)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(left_len, right_len); - assert_eq!(index_len, dest_len); - - for i in 0..dest_len { - let src_index: u64 = this - .read_immediate(&this.project_index(&index, i)?)? - .to_scalar() - .to_u32()? - .into(); - let dest = this.project_index(&dest, i)?; - - let val = if src_index < left_len { - this.read_immediate(&this.project_index(&left, src_index)?)? - } else if src_index < left_len.strict_add(right_len) { - let right_idx = src_index.strict_sub(left_len); - this.read_immediate(&this.project_index(&right, right_idx)?)? - } else { - throw_ub_format!( - "`simd_shuffle` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}" - ); - }; - this.write_immediate(*val, &dest)?; - } - } - "gather" => { - let [passthru, ptrs, mask] = check_intrinsic_arg_count(args)?; - let (passthru, passthru_len) = this.project_to_simd(passthru)?; - let (ptrs, ptrs_len) = this.project_to_simd(ptrs)?; - let (mask, mask_len) = this.project_to_simd(mask)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, passthru_len); - assert_eq!(dest_len, ptrs_len); - assert_eq!(dest_len, mask_len); - - for i in 0..dest_len { - let passthru = this.read_immediate(&this.project_index(&passthru, i)?)?; - let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?; - let mask = this.read_immediate(&this.project_index(&mask, i)?)?; - let dest = this.project_index(&dest, i)?; - - let val = if simd_element_to_bool(mask)? { - let place = this.deref_pointer(&ptr)?; - this.read_immediate(&place)? - } else { - passthru - }; - this.write_immediate(*val, &dest)?; - } - } - "scatter" => { - let [value, ptrs, mask] = check_intrinsic_arg_count(args)?; - let (value, value_len) = this.project_to_simd(value)?; - let (ptrs, ptrs_len) = this.project_to_simd(ptrs)?; - let (mask, mask_len) = this.project_to_simd(mask)?; - - assert_eq!(ptrs_len, value_len); - assert_eq!(ptrs_len, mask_len); - - for i in 0..ptrs_len { - let value = this.read_immediate(&this.project_index(&value, i)?)?; - let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?; - let mask = this.read_immediate(&this.project_index(&mask, i)?)?; - - if simd_element_to_bool(mask)? { - let place = this.deref_pointer(&ptr)?; - this.write_immediate(*value, &place)?; - } - } - } - "masked_load" => { - let [mask, ptr, default] = check_intrinsic_arg_count(args)?; - let (mask, mask_len) = this.project_to_simd(mask)?; - let ptr = this.read_pointer(ptr)?; - let (default, default_len) = this.project_to_simd(default)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, mask_len); - assert_eq!(dest_len, default_len); - - for i in 0..dest_len { - let mask = this.read_immediate(&this.project_index(&mask, i)?)?; - let default = this.read_immediate(&this.project_index(&default, i)?)?; - let dest = this.project_index(&dest, i)?; - - let val = if simd_element_to_bool(mask)? { - // Size * u64 is implemented as always checked - let ptr = ptr.wrapping_offset(dest.layout.size * i, this); - let place = this.ptr_to_mplace(ptr, dest.layout); - this.read_immediate(&place)? - } else { - default - }; - this.write_immediate(*val, &dest)?; - } - } - "masked_store" => { - let [mask, ptr, vals] = check_intrinsic_arg_count(args)?; - let (mask, mask_len) = this.project_to_simd(mask)?; - let ptr = this.read_pointer(ptr)?; - let (vals, vals_len) = this.project_to_simd(vals)?; - - assert_eq!(mask_len, vals_len); - - for i in 0..vals_len { - let mask = this.read_immediate(&this.project_index(&mask, i)?)?; - let val = this.read_immediate(&this.project_index(&vals, i)?)?; - - if simd_element_to_bool(mask)? { - // Size * u64 is implemented as always checked - let ptr = ptr.wrapping_offset(val.layout.size * i, this); - let place = this.ptr_to_mplace(ptr, val.layout); - this.write_immediate(*val, &place)? - }; - } - } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) } - - fn fminmax_op( - &self, - op: MinMax, - left: &ImmTy<'tcx>, - right: &ImmTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_ref(); - assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmax operand is not a float") - }; - let left = left.to_scalar(); - let right = right.to_scalar(); - interp_ok(match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let left = left.to_f32()?; - let right = right.to_f32()?; - let res = match op { - MinMax::Min => left.min(right), - MinMax::Max => left.max(right), - }; - let res = this.adjust_nan(res, &[left, right]); - Scalar::from_f32(res) - } - FloatTy::F64 => { - let left = left.to_f64()?; - let right = right.to_f64()?; - let res = match op { - MinMax::Min => left.min(right), - MinMax::Max => left.max(right), - }; - let res = this.adjust_nan(res, &[left, right]); - Scalar::from_f64(res) - } - FloatTy::F128 => unimplemented!("f16_f128"), - }) - } -} - -fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 { - assert!(idx < vec_len); - match endianness { - Endian::Little => idx, - #[expect(clippy::arithmetic_side_effects)] // idx < vec_len - Endian::Big => vec_len - 1 - idx, // reverse order of bits - } } From 730221e654d2d017465807f16cddef4a88b72d7c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 8 Oct 2025 09:39:11 +0200 Subject: [PATCH 1421/1900] Fix double error for `#[no_mangle]` on closures --- .../src/attributes/codegen_attrs.rs | 1 + compiler/rustc_codegen_ssa/messages.ftl | 2 -- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 13 ++++--------- compiler/rustc_codegen_ssa/src/errors.rs | 8 -------- tests/ui/attributes/no-mangle-closure.rs | 2 +- tests/ui/attributes/no-mangle-closure.stderr | 4 +++- 6 files changed, 9 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 7978bf28214cf..0f92968385f6f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -362,6 +362,7 @@ impl NoArgsAttributeParser for NoMangleParser { Allow(Target::Static), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), + Error(Target::Closure), ]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle; } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 97ff04f66daec..e321b0773ec39 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -223,8 +223,6 @@ codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple time codegen_ssa_no_field = no field `{$name}` -codegen_ssa_no_mangle_nameless = `#[no_mangle]` cannot be used on {$definition} as it has no name - codegen_ssa_no_module_named = no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names} diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 2c7643e46ceaa..e8c8729f597b9 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -19,7 +19,6 @@ use rustc_span::{Ident, Span, sym}; use rustc_target::spec::SanitizerSet; use crate::errors; -use crate::errors::NoMangleNameless; use crate::target_features::{ check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr, }; @@ -182,14 +181,10 @@ fn process_builtin_attrs( if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else { - tcx.dcx().emit_err(NoMangleNameless { - span: *attr_span, - definition: format!( - "{} {}", - tcx.def_descr_article(did.to_def_id()), - tcx.def_descr(did.to_def_id()) - ), - }); + tcx.dcx().span_delayed_bug( + *attr_span, + "no_mangle should be on a named function", + ); } } AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index d5c30c5c7a6b0..2dd7c6fa7c080 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1284,14 +1284,6 @@ impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_ } } -#[derive(Diagnostic)] -#[diag(codegen_ssa_no_mangle_nameless)] -pub(crate) struct NoMangleNameless { - #[primary_span] - pub span: Span, - pub definition: String, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_feature_not_valid)] pub(crate) struct FeatureNotValid<'a> { diff --git a/tests/ui/attributes/no-mangle-closure.rs b/tests/ui/attributes/no-mangle-closure.rs index c76baa27f38a0..05dcbf8e5bed7 100644 --- a/tests/ui/attributes/no-mangle-closure.rs +++ b/tests/ui/attributes/no-mangle-closure.rs @@ -7,5 +7,5 @@ pub struct S([usize; 8]); pub fn outer_function(x: S, y: S) -> usize { (#[no_mangle] || y.0[0])() - //~^ ERROR `#[no_mangle]` cannot be used on a closure as it has no name + //~^ ERROR `#[no_mangle]` attribute cannot be used on closures } diff --git a/tests/ui/attributes/no-mangle-closure.stderr b/tests/ui/attributes/no-mangle-closure.stderr index c183783493bdb..f81e65d926752 100644 --- a/tests/ui/attributes/no-mangle-closure.stderr +++ b/tests/ui/attributes/no-mangle-closure.stderr @@ -1,8 +1,10 @@ -error: `#[no_mangle]` cannot be used on a closure as it has no name +error: `#[no_mangle]` attribute cannot be used on closures --> $DIR/no-mangle-closure.rs:9:6 | LL | (#[no_mangle] || y.0[0])() | ^^^^^^^^^^^^ + | + = help: `#[no_mangle]` can be applied to functions, methods, and statics error: aborting due to 1 previous error From c050bfbf6f1c932ae955a81a0571a0fa354442fc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 8 Oct 2025 10:10:20 +0200 Subject: [PATCH 1422/1900] Fix double error for `#[no_mangle]` on consts --- .../src/attributes/codegen_attrs.rs | 2 ++ .../rustc_attr_parsing/src/target_checking.rs | 17 +++++++++++++++-- tests/ui/issues/issue-45562.fixed | 2 ++ tests/ui/issues/issue-45562.rs | 2 ++ tests/ui/issues/issue-45562.stderr | 2 +- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 0f92968385f6f..f09b02251e4d0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -6,6 +6,7 @@ use crate::session_diagnostics::{ NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, }; +use crate::target_checking::Policy::AllowSilent; pub(crate) struct OptimizeParser; @@ -362,6 +363,7 @@ impl NoArgsAttributeParser for NoMangleParser { Allow(Target::Static), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), + AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass Error(Target::Closure), ]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index d28f43943ba84..fabd364d3d7f8 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -31,7 +31,9 @@ impl AllowedTargets { pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult { match self { AllowedTargets::AllowList(list) => { - if list.contains(&Policy::Allow(target)) { + if list.contains(&Policy::Allow(target)) + || list.contains(&Policy::AllowSilent(target)) + { AllowedResult::Allowed } else if list.contains(&Policy::Warn(target)) { AllowedResult::Warn @@ -40,7 +42,9 @@ impl AllowedTargets { } } AllowedTargets::AllowListWarnRest(list) => { - if list.contains(&Policy::Allow(target)) { + if list.contains(&Policy::Allow(target)) + || list.contains(&Policy::AllowSilent(target)) + { AllowedResult::Allowed } else if list.contains(&Policy::Error(target)) { AllowedResult::Error @@ -61,6 +65,7 @@ impl AllowedTargets { .iter() .filter_map(|target| match target { Policy::Allow(target) => Some(*target), + Policy::AllowSilent(_) => None, // Not listed in possible targets Policy::Warn(_) => None, Policy::Error(_) => None, }) @@ -68,10 +73,18 @@ impl AllowedTargets { } } +/// This policy determines what diagnostics should be emitted based on the `Target` of the attribute. #[derive(Debug, Eq, PartialEq)] pub(crate) enum Policy { + /// A target that is allowed. Allow(Target), + /// A target that is allowed and not listed in the possible targets. + /// This is useful if the target is checked elsewhere. + AllowSilent(Target), + /// Emits a FCW on this target. + /// This is useful if the target was previously allowed but should not be. Warn(Target), + /// Emits an error on this target. Error(Target), } diff --git a/tests/ui/issues/issue-45562.fixed b/tests/ui/issues/issue-45562.fixed index 8dcdd3a541ce4..529b5bd744e03 100644 --- a/tests/ui/issues/issue-45562.fixed +++ b/tests/ui/issues/issue-45562.fixed @@ -1,5 +1,7 @@ //@ run-rustfix +#![deny(unused_attributes)] + #[no_mangle] pub static RAH: usize = 5; //~^ ERROR const items should never be `#[no_mangle]` diff --git a/tests/ui/issues/issue-45562.rs b/tests/ui/issues/issue-45562.rs index 08f6c8046dce4..7c30a967c7848 100644 --- a/tests/ui/issues/issue-45562.rs +++ b/tests/ui/issues/issue-45562.rs @@ -1,5 +1,7 @@ //@ run-rustfix +#![deny(unused_attributes)] + #[no_mangle] pub const RAH: usize = 5; //~^ ERROR const items should never be `#[no_mangle]` diff --git a/tests/ui/issues/issue-45562.stderr b/tests/ui/issues/issue-45562.stderr index 6fae86f9f31ca..55d35f76a0198 100644 --- a/tests/ui/issues/issue-45562.stderr +++ b/tests/ui/issues/issue-45562.stderr @@ -1,5 +1,5 @@ error: const items should never be `#[no_mangle]` - --> $DIR/issue-45562.rs:3:14 + --> $DIR/issue-45562.rs:5:14 | LL | #[no_mangle] pub const RAH: usize = 5; | ---------^^^^^^^^^^^^^^^^ From 8fad3a17ca443c00a05c121730c8d4f60e73aff8 Mon Sep 17 00:00:00 2001 From: "U. Lasiotus" Date: Wed, 24 Sep 2025 12:11:44 -0700 Subject: [PATCH 1423/1900] tidy: allow stdlib to depend on moto-rt As part of work to add stdlib support for Motor OS. --- src/tools/tidy/src/deps.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3f40bef821c9d..3164dcc77be1d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -569,6 +569,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "libc", "memchr", "miniz_oxide", + "moto-rt", "object", "r-efi", "r-efi-alloc", From a828ffcf5f04be5cdd91b5fad608102eabc17ec7 Mon Sep 17 00:00:00 2001 From: "U. Lasiotus" Date: Tue, 7 Oct 2025 18:56:25 -0700 Subject: [PATCH 1424/1900] Add Motor OS std library port Motor OS was added as a no-std Tier-3 target in https://github.com/rust-lang/rust/pull/146848 as x86_64-unknown-motor. This patch/PR adds the std library for Motor OS. While the patch may seem large, all it does is proxy std pal calls to moto-rt. When there is some non-trivial code (e.g. thread::spawn), it is quite similar, and often identical, to what other platforms do. --- library/Cargo.lock | 11 + library/std/Cargo.toml | 3 + library/std/build.rs | 1 + library/std/src/os/fd/owned.rs | 22 +- library/std/src/os/fd/raw.rs | 10 +- library/std/src/os/mod.rs | 11 +- library/std/src/os/motor/ffi.rs | 37 ++ library/std/src/os/motor/mod.rs | 4 + library/std/src/os/motor/process.rs | 15 + library/std/src/sys/alloc/mod.rs | 3 + library/std/src/sys/alloc/motor.rs | 28 + library/std/src/sys/anonymous_pipe/mod.rs | 4 + library/std/src/sys/anonymous_pipe/motor.rs | 11 + library/std/src/sys/args/mod.rs | 5 + library/std/src/sys/args/motor.rs | 13 + library/std/src/sys/env/mod.rs | 5 + library/std/src/sys/env/motor.rs | 27 + library/std/src/sys/fd/mod.rs | 4 + library/std/src/sys/fd/motor.rs | 124 +++++ library/std/src/sys/fs/mod.rs | 4 + library/std/src/sys/fs/motor.rs | 478 ++++++++++++++++ library/std/src/sys/io/is_terminal/motor.rs | 6 + library/std/src/sys/io/mod.rs | 4 + library/std/src/sys/net/connection/mod.rs | 4 + library/std/src/sys/net/connection/motor.rs | 521 ++++++++++++++++++ library/std/src/sys/pal/mod.rs | 4 + library/std/src/sys/pal/motor/mod.rs | 77 +++ library/std/src/sys/pal/motor/os.rs | 100 ++++ library/std/src/sys/pal/motor/pipe.rs | 121 ++++ library/std/src/sys/pal/motor/time.rs | 1 + library/std/src/sys/path/unix.rs | 2 +- library/std/src/sys/personality/mod.rs | 2 +- library/std/src/sys/process/mod.rs | 6 + library/std/src/sys/process/motor.rs | 313 +++++++++++ library/std/src/sys/random/mod.rs | 4 + library/std/src/sys/random/motor.rs | 3 + library/std/src/sys/stdio/mod.rs | 4 + library/std/src/sys/stdio/motor.rs | 232 ++++++++ library/std/src/sys/sync/condvar/mod.rs | 1 + library/std/src/sys/sync/mutex/mod.rs | 1 + library/std/src/sys/sync/once/mod.rs | 1 + library/std/src/sys/sync/rwlock/mod.rs | 1 + .../std/src/sys/sync/thread_parking/mod.rs | 1 + library/std/src/sys/thread/mod.rs | 4 + library/std/src/sys/thread/motor.rs | 63 +++ library/std/src/sys/thread_local/mod.rs | 8 + 46 files changed, 2296 insertions(+), 8 deletions(-) create mode 100644 library/std/src/os/motor/ffi.rs create mode 100644 library/std/src/os/motor/mod.rs create mode 100644 library/std/src/os/motor/process.rs create mode 100644 library/std/src/sys/alloc/motor.rs create mode 100644 library/std/src/sys/anonymous_pipe/motor.rs create mode 100644 library/std/src/sys/args/motor.rs create mode 100644 library/std/src/sys/env/motor.rs create mode 100644 library/std/src/sys/fd/motor.rs create mode 100644 library/std/src/sys/fs/motor.rs create mode 100644 library/std/src/sys/io/is_terminal/motor.rs create mode 100644 library/std/src/sys/net/connection/motor.rs create mode 100644 library/std/src/sys/pal/motor/mod.rs create mode 100644 library/std/src/sys/pal/motor/os.rs create mode 100644 library/std/src/sys/pal/motor/pipe.rs create mode 100644 library/std/src/sys/pal/motor/time.rs create mode 100644 library/std/src/sys/process/motor.rs create mode 100644 library/std/src/sys/random/motor.rs create mode 100644 library/std/src/sys/stdio/motor.rs create mode 100644 library/std/src/sys/thread/motor.rs diff --git a/library/Cargo.lock b/library/Cargo.lock index 47fbf5169f491..1156d6925cf2c 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -166,6 +166,16 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "moto-rt" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "object" version = "0.37.3" @@ -316,6 +326,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", + "moto-rt", "object", "panic_abort", "panic_unwind", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 779b07ce240a6..b7bf5ea7ba738 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -70,6 +70,9 @@ fortanix-sgx-abi = { version = "0.6.1", features = [ 'rustc-dep-of-std', ], public = true } +[target.'cfg(target_os = "motor")'.dependencies] +moto-rt = { version = "0.15", features = ['rustc-dep-of-std'], public = true } + [target.'cfg(target_os = "hermit")'.dependencies] hermit-abi = { version = "0.5.0", features = [ 'rustc-dep-of-std', diff --git a/library/std/build.rs b/library/std/build.rs index 8a5a785060c85..bee28e88491d0 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -30,6 +30,7 @@ fn main() { || target_os == "windows" || target_os == "fuchsia" || (target_vendor == "fortanix" && target_env == "sgx") + || target_os == "motor" || target_os == "hermit" || target_os == "trusty" || target_os == "l4re" diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 10e1e73a115bd..6a0e7a640028b 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -3,6 +3,9 @@ #![stable(feature = "io_safety", since = "1.63.0")] #![deny(unsafe_op_in_unsafe_fn)] +#[cfg(target_os = "motor")] +use moto_rt::libc; + use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(not(target_os = "trusty"))] use crate::fs; @@ -12,7 +15,8 @@ use crate::mem::ManuallyDrop; target_arch = "wasm32", target_env = "sgx", target_os = "hermit", - target_os = "trusty" + target_os = "trusty", + target_os = "motor" )))] use crate::sys::cvt; #[cfg(not(target_os = "trusty"))] @@ -95,7 +99,12 @@ impl OwnedFd { impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(not(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty")))] + #[cfg(not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the @@ -123,6 +132,15 @@ impl BorrowedFd<'_> { pub fn try_clone_to_owned(&self) -> crate::io::Result { Err(crate::io::Error::UNSUPPORTED_PLATFORM) } + + /// Creates a new `OwnedFd` instance that shares the same underlying file + /// description as the existing `BorrowedFd` instance. + #[cfg(target_os = "motor")] + #[stable(feature = "io_safety", since = "1.63.0")] + pub fn try_clone_to_owned(&self) -> crate::io::Result { + let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(crate::sys::map_motor_error)?; + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } } #[stable(feature = "io_safety", since = "1.63.0")] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 34a6cf1a8b84d..c01e6b83cd366 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -4,13 +4,17 @@ #[cfg(target_os = "hermit")] use hermit_abi as libc; +#[cfg(target_os = "motor")] +use moto_rt::libc; +#[cfg(target_os = "motor")] +use super::owned::OwnedFd; #[cfg(not(target_os = "trusty"))] use crate::fs; use crate::io; #[cfg(target_os = "hermit")] use crate::os::hermit::io::OwnedFd; -#[cfg(not(target_os = "hermit"))] +#[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] use crate::os::raw; #[cfg(all(doc, not(target_arch = "wasm32")))] use crate::os::unix::io::AsFd; @@ -23,10 +27,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(target_os = "hermit"))] +#[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] pub type RawFd = raw::c_int; #[stable(feature = "rust1", since = "1.0.0")] -#[cfg(target_os = "hermit")] +#[cfg(any(target_os = "hermit", target_os = "motor"))] pub type RawFd = i32; /// A trait to extract the raw file descriptor from an underlying object. diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index fd7a11433af1b..76374402be4b3 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -155,6 +155,8 @@ pub mod ios; pub mod l4re; #[cfg(target_os = "macos")] pub mod macos; +#[cfg(target_os = "motor")] +pub mod motor; #[cfg(target_os = "netbsd")] pub mod netbsd; #[cfg(target_os = "nto")] @@ -182,7 +184,14 @@ pub mod vxworks; #[cfg(target_os = "xous")] pub mod xous; -#[cfg(any(unix, target_os = "hermit", target_os = "trusty", target_os = "wasi", doc))] +#[cfg(any( + unix, + target_os = "hermit", + target_os = "trusty", + target_os = "wasi", + target_os = "motor", + doc +))] pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] diff --git a/library/std/src/os/motor/ffi.rs b/library/std/src/os/motor/ffi.rs new file mode 100644 index 0000000000000..509fe641bb353 --- /dev/null +++ b/library/std/src/os/motor/ffi.rs @@ -0,0 +1,37 @@ +//! Motor OS-specific extensions to primitives in the [`std::ffi`] module. +#![unstable(feature = "motor_ext", issue = "147456")] + +use crate::ffi::{OsStr, OsString}; +use crate::sealed::Sealed; + +/// Motor OS-specific extensions to [`OsString`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. +pub trait OsStringExt: Sealed { + /// Motor OS strings are utf-8, and thus just strings. + fn as_str(&self) -> &str; +} + +impl OsStringExt for OsString { + #[inline] + fn as_str(&self) -> &str { + self.to_str().unwrap() + } +} + +/// Motor OS-specific extensions to [`OsString`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. +pub trait OsStrExt: Sealed { + /// Motor OS strings are utf-8, and thus just strings. + fn as_str(&self) -> &str; +} + +impl OsStrExt for OsStr { + #[inline] + fn as_str(&self) -> &str { + self.to_str().unwrap() + } +} diff --git a/library/std/src/os/motor/mod.rs b/library/std/src/os/motor/mod.rs new file mode 100644 index 0000000000000..18da079c74a15 --- /dev/null +++ b/library/std/src/os/motor/mod.rs @@ -0,0 +1,4 @@ +#![unstable(feature = "motor_ext", issue = "147456")] + +pub mod ffi; +pub mod process; diff --git a/library/std/src/os/motor/process.rs b/library/std/src/os/motor/process.rs new file mode 100644 index 0000000000000..015fbcb97f97d --- /dev/null +++ b/library/std/src/os/motor/process.rs @@ -0,0 +1,15 @@ +#![unstable(feature = "motor_ext", issue = "147456")] + +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +pub trait ChildExt: Sealed { + /// Extracts the main thread raw handle, without taking ownership + fn sys_handle(&self) -> u64; +} + +impl ChildExt for crate::process::Child { + fn sys_handle(&self) -> u64 { + self.as_inner().handle() + } +} diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 2045b2fecc6ac..f2f1d1c7feceb 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -83,6 +83,9 @@ cfg_select! { target_os = "hermit" => { mod hermit; } + target_os = "motor" => { + mod motor; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; } diff --git a/library/std/src/sys/alloc/motor.rs b/library/std/src/sys/alloc/motor.rs new file mode 100644 index 0000000000000..271e3c40c26ae --- /dev/null +++ b/library/std/src/sys/alloc/motor.rs @@ -0,0 +1,28 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::alloc. + moto_rt::alloc::alloc(layout) + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::alloc_zeroed. + moto_rt::alloc::alloc_zeroed(layout) + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: same requirements as in GlobalAlloc::dealloc. + unsafe { moto_rt::alloc::dealloc(ptr, layout) } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::realloc. + unsafe { moto_rt::alloc::realloc(ptr, layout, new_size) } + } +} diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs index b6f464161ee2b..64b2c014b54fe 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -9,6 +9,10 @@ cfg_select! { mod windows; pub use windows::{AnonPipe, pipe}; } + target_os = "motor" => { + mod motor; + pub use motor::{AnonPipe, pipe}; + } _ => { mod unsupported; pub use unsupported::{AnonPipe, pipe}; diff --git a/library/std/src/sys/anonymous_pipe/motor.rs b/library/std/src/sys/anonymous_pipe/motor.rs new file mode 100644 index 0000000000000..dfe10f7fafe49 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/motor.rs @@ -0,0 +1,11 @@ +use crate::io; +use crate::sys::fd::FileDesc; +use crate::sys::pipe::anon_pipe; +use crate::sys_common::IntoInner; + +pub type AnonPipe = FileDesc; + +#[inline] +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index 75c59da721e19..5424d40a15883 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -6,6 +6,7 @@ all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), target_family = "windows", target_os = "hermit", + target_os = "motor", target_os = "uefi", target_os = "wasi", target_os = "xous", @@ -28,6 +29,10 @@ cfg_select! { mod sgx; pub use sgx::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "uefi" => { mod uefi; pub use uefi::*; diff --git a/library/std/src/sys/args/motor.rs b/library/std/src/sys/args/motor.rs new file mode 100644 index 0000000000000..c3dbe87cec411 --- /dev/null +++ b/library/std/src/sys/args/motor.rs @@ -0,0 +1,13 @@ +pub use super::common::Args; +use crate::ffi::OsString; + +pub fn args() -> Args { + let motor_args: Vec = moto_rt::process::args(); + let mut rust_args = Vec::new(); + + for arg in motor_args { + rust_args.push(OsString::from(arg)); + } + + Args::new(rust_args) +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs index f211a9fc86b3b..89856516b6dce 100644 --- a/library/std/src/sys/env/mod.rs +++ b/library/std/src/sys/env/mod.rs @@ -5,6 +5,7 @@ #[cfg(any( target_family = "unix", target_os = "hermit", + target_os = "motor", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", target_os = "uefi", @@ -26,6 +27,10 @@ cfg_select! { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/env/motor.rs b/library/std/src/sys/env/motor.rs new file mode 100644 index 0000000000000..1f756ccd3ee85 --- /dev/null +++ b/library/std/src/sys/env/motor.rs @@ -0,0 +1,27 @@ +pub use super::common::Env; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::os::motor::ffi::OsStrExt; + +pub fn env() -> Env { + let motor_env: Vec<(String, String)> = moto_rt::process::env(); + let mut rust_env = vec![]; + + for (k, v) in motor_env { + rust_env.push((OsString::from(k), OsString::from(v))); + } + + Env::new(rust_env) +} + +pub fn getenv(key: &OsStr) -> Option { + moto_rt::process::getenv(key.as_str()).map(|s| OsString::from(s)) +} + +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + Ok(moto_rt::process::setenv(key.as_str(), val.as_str())) +} + +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + Ok(moto_rt::process::unsetenv(key.as_str())) +} diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index 7cb9dd1cba9d3..330499ecc18f6 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -11,6 +11,10 @@ cfg_select! { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/fd/motor.rs b/library/std/src/sys/fd/motor.rs new file mode 100644 index 0000000000000..4211fef8007ab --- /dev/null +++ b/library/std/src/sys/fd/motor.rs @@ -0,0 +1,124 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc(OwnedFd); + +impl FileDesc { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.as_raw_fd(), nonblocking).map_err(map_motor_error) + } + + #[inline] + pub fn duplicate(&self) -> io::Result { + let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(map_motor_error)?; + // SAFETY: safe because we just got it from the OS runtime. + unsafe { Ok(Self::from_raw_fd(fd)) } + } + + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 64f5a6b36d3db..403bfb2d9b929 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -27,6 +27,10 @@ cfg_select! { mod hermit; use hermit as imp; } + target_os = "motor" => { + mod motor; + use motor as imp; + } target_os = "solid_asp3" => { mod solid; use solid as imp; diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs new file mode 100644 index 0000000000000..656b6e81b9518 --- /dev/null +++ b/library/std/src/sys/fs/motor.rs @@ -0,0 +1,478 @@ +use crate::ffi::OsString; +use crate::hash::Hash; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::{Path, PathBuf}; +use crate::sys::fd::FileDesc; +pub use crate::sys::fs::common::exists; +use crate::sys::time::SystemTime; +use crate::sys::{map_motor_error, unsupported}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct FileType { + rt_filetype: u8, +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.rt_filetype == moto_rt::fs::FILETYPE_DIRECTORY + } + + pub fn is_file(&self) -> bool { + self.rt_filetype == moto_rt::fs::FILETYPE_FILE + } + + pub fn is_symlink(&self) -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FilePermissions { + rt_perm: u64, +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + (self.rt_perm & moto_rt::fs::PERM_WRITE == 0) + && (self.rt_perm & moto_rt::fs::PERM_READ != 0) + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.rt_perm = moto_rt::fs::PERM_READ; + } else { + self.rt_perm = moto_rt::fs::PERM_READ | moto_rt::fs::PERM_WRITE; + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + modified: u128, + accessed: u128, +} + +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = t.as_u128(); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = t.as_u128(); + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct FileAttr { + inner: moto_rt::fs::FileAttr, +} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.inner.size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { rt_perm: self.inner.perm } + } + + pub fn file_type(&self) -> FileType { + FileType { rt_filetype: self.inner.file_type } + } + + pub fn modified(&self) -> io::Result { + match self.inner.modified { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } + + pub fn accessed(&self) -> io::Result { + match self.inner.accessed { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } + + pub fn created(&self) -> io::Result { + match self.inner.created { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + rt_open_options: u32, +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { rt_open_options: 0 } + } + + pub fn read(&mut self, read: bool) { + if read { + self.rt_open_options |= moto_rt::fs::O_READ; + } else { + self.rt_open_options &= !moto_rt::fs::O_READ; + } + } + + pub fn write(&mut self, write: bool) { + if write { + self.rt_open_options |= moto_rt::fs::O_WRITE; + } else { + self.rt_open_options &= !moto_rt::fs::O_WRITE; + } + } + + pub fn append(&mut self, append: bool) { + if append { + self.rt_open_options |= moto_rt::fs::O_APPEND; + } else { + self.rt_open_options &= !moto_rt::fs::O_APPEND; + } + } + + pub fn truncate(&mut self, truncate: bool) { + if truncate { + self.rt_open_options |= moto_rt::fs::O_TRUNCATE; + } else { + self.rt_open_options &= !moto_rt::fs::O_TRUNCATE; + } + } + + pub fn create(&mut self, create: bool) { + if create { + self.rt_open_options |= moto_rt::fs::O_CREATE; + } else { + self.rt_open_options &= !moto_rt::fs::O_CREATE; + } + } + + pub fn create_new(&mut self, create_new: bool) { + if create_new { + self.rt_open_options |= moto_rt::fs::O_CREATE_NEW; + } else { + self.rt_open_options &= !moto_rt::fs::O_CREATE_NEW; + } + } +} + +#[derive(Debug)] +pub struct File(FileDesc); + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::open(path, opts.rt_open_options) + .map(|fd| unsafe { Self::from_raw_fd(fd) }) + .map_err(map_motor_error) + } + + pub fn file_attr(&self) -> io::Result { + moto_rt::fs::get_file_attr(self.as_raw_fd()) + .map(|inner| -> FileAttr { FileAttr { inner } }) + .map_err(map_motor_error) + } + + pub fn fsync(&self) -> io::Result<()> { + moto_rt::fs::fsync(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn datasync(&self) -> io::Result<()> { + moto_rt::fs::datasync(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + moto_rt::fs::truncate(self.as_raw_fd(), size).map_err(map_motor_error) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn flush(&self) -> io::Result<()> { + moto_rt::fs::flush(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + match pos { + SeekFrom::Start(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset as i64, moto_rt::fs::SEEK_SET) + .map_err(map_motor_error) + } + SeekFrom::End(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_END) + .map_err(map_motor_error) + } + SeekFrom::Current(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_CUR) + .map_err(map_motor_error) + } + } + } + + pub fn tell(&self) -> io::Result { + self.seek(SeekFrom::Current(0)) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.as_raw_fd()) + .map(|fd| unsafe { Self::from_raw_fd(fd) }) + .map_err(map_motor_error) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + moto_rt::fs::set_file_perm(self.as_raw_fd(), perm.rt_perm).map_err(map_motor_error) + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + unsupported() // Let's not do that. + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), crate::fs::TryLockError> { + Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported))) + } + + pub fn try_lock_shared(&self) -> Result<(), crate::fs::TryLockError> { + Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported))) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn size(&self) -> Option> { + None + } +} + +#[derive(Debug)] +pub struct DirBuilder {} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::mkdir(path).map_err(map_motor_error) + } +} + +pub fn unlink(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::unlink(path).map_err(map_motor_error) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = old.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let new = new.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rename(old, new).map_err(map_motor_error) +} + +pub fn rmdir(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rmdir(path).map_err(map_motor_error) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rmdir_all(path).map_err(map_motor_error) +} + +pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::set_perm(path, perm.rt_perm).map_err(map_motor_error) +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let inner = moto_rt::fs::stat(path).map_err(map_motor_error)?; + Ok(FileAttr { inner }) +} + +pub fn lstat(path: &Path) -> io::Result { + stat(path) +} + +pub fn canonicalize(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let path = moto_rt::fs::canonicalize(path).map_err(map_motor_error)?; + Ok(path.into()) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + let from = from.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let to = to.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::copy(from, to).map_err(map_motor_error) +} + +#[derive(Debug)] +pub struct ReadDir { + rt_fd: moto_rt::RtFd, + path: String, +} + +impl Drop for ReadDir { + fn drop(&mut self) { + moto_rt::fs::closedir(self.rt_fd).unwrap(); + } +} + +pub fn readdir(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + Ok(ReadDir { + rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?, + path: path.to_owned(), + }) +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match moto_rt::fs::readdir(self.rt_fd).map_err(map_motor_error) { + Ok(maybe_item) => match maybe_item { + Some(inner) => Some(Ok(DirEntry { inner, parent_path: self.path.clone() })), + None => None, + }, + Err(err) => Some(Err(err)), + } + } +} + +pub struct DirEntry { + parent_path: String, + inner: moto_rt::fs::DirEntry, +} + +impl DirEntry { + fn filename(&self) -> &str { + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(self.inner.fname.as_ptr(), self.inner.fname_size as usize) + }) + .unwrap() + } + + pub fn path(&self) -> PathBuf { + let mut path = self.parent_path.clone(); + path.push_str("/"); + path.push_str(self.filename()); + path.into() + } + + pub fn file_name(&self) -> OsString { + self.filename().to_owned().into() + } + + pub fn metadata(&self) -> io::Result { + Ok(FileAttr { inner: self.inner.attr }) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType { rt_filetype: self.inner.attr.file_type }) + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/io/is_terminal/motor.rs b/library/std/src/sys/io/is_terminal/motor.rs new file mode 100644 index 0000000000000..0b70299adaaa2 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/motor.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + moto_rt::fs::is_terminal(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index fe8ec1dbb7325..0916eda1c06a5 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -39,6 +39,10 @@ mod is_terminal { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 41e7159f909ae..2f064914a8317 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -17,6 +17,10 @@ cfg_select! { mod wasip1; pub use wasip1::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "xous" => { mod xous; pub use xous::*; diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs new file mode 100644 index 0000000000000..e9bf29e34f90c --- /dev/null +++ b/library/std/src/sys/net/connection/motor.rs @@ -0,0 +1,521 @@ +pub use moto_rt::netc; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::SocketAddr::{V4, V6}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +// We want to re-use as much of Rust's stdlib code as possible, +// and most of it is unixy, but with a lot of nesting. +#[derive(Debug)] +pub struct Socket(FileDesc); + +#[derive(Debug)] +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn connect(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::tcp_connect(&addr, Duration::MAX, false) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + let addr = into_netc(addr); + moto_rt::net::tcp_connect(&addr, timeout, false) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn read_timeout(&self) -> io::Result> { + moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn write_timeout(&self) -> io::Result> { + moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let bufs: &mut [&mut [u8]] = unsafe { core::mem::transmute(bufs) }; + moto_rt::fs::read_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let bufs: &[&[u8]] = unsafe { core::mem::transmute(bufs) }; + moto_rt::fs::write_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + moto_rt::net::peer_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn shutdown(&self, shutdown: Shutdown) -> io::Result<()> { + let shutdown = match shutdown { + Shutdown::Read => moto_rt::net::SHUTDOWN_READ, + Shutdown::Write => moto_rt::net::SHUTDOWN_WRITE, + Shutdown::Both => moto_rt::net::SHUTDOWN_READ | moto_rt::net::SHUTDOWN_WRITE, + }; + + moto_rt::net::shutdown(self.inner.as_raw_fd(), shutdown).map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_linger(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_linger(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn linger(&self) -> io::Result> { + moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error) + } + + pub fn nodelay(&self) -> io::Result { + moto_rt::net::nodelay(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; + if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } +} + +#[derive(Debug)] +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn bind(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::bind(moto_rt::net::PROTO_TCP, &addr) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + moto_rt::net::accept(self.inner.as_raw_fd()) + .map(|(fd, addr)| { + (TcpStream { inner: unsafe { Socket::from_raw_fd(fd) } }, from_netc(&addr)) + }) + .map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + moto_rt::net::set_only_v6(self.inner.as_raw_fd(), only_v6).map_err(map_motor_error) + } + + pub fn only_v6(&self) -> io::Result { + moto_rt::net::only_v6(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; + if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } +} + +#[derive(Debug)] +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn bind(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::bind(moto_rt::net::PROTO_UDP, &addr) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn peer_addr(&self) -> io::Result { + moto_rt::net::peer_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + moto_rt::net::udp_recv_from(self.inner.as_raw_fd(), buf) + .map(|(sz, addr)| (sz, from_netc(&addr))) + .map_err(map_motor_error) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + moto_rt::net::udp_peek_from(self.inner.as_raw_fd(), buf) + .map(|(sz, addr)| (sz, from_netc(&addr))) + .map_err(map_motor_error) + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result { + let addr = into_netc(addr); + moto_rt::net::udp_send_to(self.inner.as_raw_fd(), buf, &addr).map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn read_timeout(&self) -> io::Result> { + moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn write_timeout(&self) -> io::Result> { + moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + moto_rt::net::set_udp_broadcast(self.inner.as_raw_fd(), broadcast).map_err(map_motor_error) + } + + pub fn broadcast(&self) -> io::Result { + moto_rt::net::udp_broadcast(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_loop_v4(&self, val: bool) -> io::Result<()> { + moto_rt::net::set_udp_multicast_loop_v4(self.inner.as_raw_fd(), val) + .map_err(map_motor_error) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + moto_rt::net::udp_multicast_loop_v4(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_ttl_v4(&self, val: u32) -> io::Result<()> { + moto_rt::net::set_udp_multicast_ttl_v4(self.inner.as_raw_fd(), val).map_err(map_motor_error) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + moto_rt::net::udp_multicast_ttl_v4(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_loop_v6(&self, val: bool) -> io::Result<()> { + moto_rt::net::set_udp_multicast_loop_v6(self.inner.as_raw_fd(), val) + .map_err(map_motor_error) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + moto_rt::net::udp_multicast_loop_v6(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn join_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> { + let addr = (*addr).into(); + let iface = (*iface).into(); + moto_rt::net::join_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface) + .map_err(map_motor_error) + } + + pub fn join_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> { + let addr = (*addr).into(); + moto_rt::net::join_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface) + .map_err(map_motor_error) + } + + pub fn leave_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> { + let addr = (*addr).into(); + let iface = (*iface).into(); + moto_rt::net::leave_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface) + .map_err(map_motor_error) + } + + pub fn leave_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> { + let addr = (*addr).into(); + moto_rt::net::leave_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface) + .map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + moto_rt::net::take_error(self.inner.as_raw_fd()) + .map(|e| match e { + moto_rt::E_OK => None, + e => Some(map_motor_error(e)), + }) + .map_err(map_motor_error) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn connect(&self, addr: A) -> io::Result<()> { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::udp_connect(self.inner.as_raw_fd(), &addr).map_err(map_motor_error) + } +} + +pub struct LookupHost { + addresses: alloc::collections::VecDeque, +} + +pub fn lookup_host(host: &str, port: u16) -> io::Result { + let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; + Ok(LookupHost { addresses }) +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.addresses.pop_front().map(|addr| from_netc(&addr)) + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(host_port: &str) -> io::Result { + let (host, port_str) = host_port + .rsplit_once(':') + .ok_or(moto_rt::E_INVALID_ARGUMENT) + .map_err(map_motor_error)?; + let port: u16 = + port_str.parse().map_err(|_| moto_rt::E_INVALID_ARGUMENT).map_err(map_motor_error)?; + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(host_port: (&'a str, u16)) -> io::Result { + let (host, port) = host_port; + + let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; + Ok(LookupHost { addresses }) + } +} + +fn into_netc(addr: &SocketAddr) -> netc::sockaddr { + match addr { + V4(addr4) => netc::sockaddr { v4: (*addr4).into() }, + V6(addr6) => netc::sockaddr { v6: (*addr6).into() }, + } +} + +fn from_netc(addr: &netc::sockaddr) -> SocketAddr { + // SAFETY: all variants of union netc::sockaddr have `sin_family` at the same offset. + let family = unsafe { addr.v4.sin_family }; + match family { + netc::AF_INET => SocketAddr::V4(crate::net::SocketAddrV4::from(unsafe { addr.v4 })), + netc::AF_INET6 => SocketAddr::V6(crate::net::SocketAddrV6::from(unsafe { addr.v6 })), + _ => panic!("bad sin_family {family}"), + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 9e964540a87c1..e11df38a8ee68 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -41,6 +41,10 @@ cfg_select! { mod hermit; pub use self::hermit::*; } + target_os = "motor" => { + mod motor; + pub use self::motor::*; + } target_os = "trusty" => { mod trusty; pub use self::trusty::*; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs new file mode 100644 index 0000000000000..c64f8ff7a8a83 --- /dev/null +++ b/library/std/src/sys/pal/motor/mod.rs @@ -0,0 +1,77 @@ +#![allow(unsafe_op_in_unsafe_fn)] + +pub mod os; +pub mod pipe; +pub mod time; + +pub use moto_rt::futex; + +use crate::io as std_io; +use crate::sys::RawOsError; + +pub(crate) fn map_motor_error(err: moto_rt::ErrorCode) -> crate::io::Error { + crate::io::Error::from_raw_os_error(err.into()) +} + +#[cfg(not(test))] +#[unsafe(no_mangle)] +pub extern "C" fn motor_start() -> ! { + // Initialize the runtime. + moto_rt::start(); + + // Call main. + unsafe extern "C" { + fn main(_: isize, _: *const *const u8, _: u8) -> i32; + } + let result = unsafe { main(0, core::ptr::null(), 0) }; + + // Terminate the process. + moto_rt::process::exit(result) +} + +// SAFETY: must be called only once during runtime initialization. +// NOTE: Motor OS uses moto_rt::start() to initialize runtime (see above). +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::UNSUPPORTED_PLATFORM +} + +pub fn is_interrupted(_code: RawOsError) -> bool { + false // Motor OS doesn't have signals. +} + +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { + use moto_rt::error::*; + use std_io::ErrorKind; + + if code < 0 || code > u16::MAX.into() { + return std_io::ErrorKind::Uncategorized; + } + + match code as moto_rt::ErrorCode /* u16 */ { + E_ALREADY_IN_USE => ErrorKind::AlreadyExists, + E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_NOT_FOUND => ErrorKind::NotFound, + E_TIMED_OUT => ErrorKind::TimedOut, + E_NOT_IMPLEMENTED => ErrorKind::Unsupported, + E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, + E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, + E_INVALID_ARGUMENT => ErrorKind::InvalidInput, + E_NOT_READY => ErrorKind::WouldBlock, + E_NOT_CONNECTED => ErrorKind::NotConnected, + _ => crate::io::ErrorKind::Uncategorized, + } +} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs new file mode 100644 index 0000000000000..052e3b238b6af --- /dev/null +++ b/library/std/src/sys/pal/motor/os.rs @@ -0,0 +1,100 @@ +use super::map_motor_error; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; +use crate::os::motor::ffi::OsStrExt; +use crate::path::{self, PathBuf}; +use crate::sys::RawOsError; +use crate::{fmt, io}; + +pub fn errno() -> RawOsError { + // Not used in Motor OS because it is ambiguous: Motor OS + // is micro-kernel-based, and I/O happens via a shared-memory + // ring buffer, so an I/O operation that on a unix is a syscall + // may involve no sycalls on Motor OS at all, or a syscall + // that e.g. waits for a notification from the I/O driver + // (sys-io); and the wait syscall may succeed, but the + // driver may report an I/O error; or a bunch of results + // for several I/O operations, some successful and some + // not. + // + // Also I/O operations in a Motor OS process are handled by a + // separate runtime background/I/O thread, so it is really hard + // to define what "last system error in the current thread" + // actually means. + moto_rt::E_UNKNOWN.into() +} + +pub fn error_string(errno: RawOsError) -> String { + let error_code: moto_rt::ErrorCode = match errno { + x if x < 0 => moto_rt::E_UNKNOWN, + x if x > u16::MAX.into() => moto_rt::E_UNKNOWN, + x => x as moto_rt::ErrorCode, /* u16 */ + }; + format!("{}", moto_rt::Error::from(error_code)) +} + +pub fn getcwd() -> io::Result { + moto_rt::fs::getcwd().map(PathBuf::from).map_err(map_motor_error) +} + +pub fn chdir(path: &path::Path) -> io::Result<()> { + moto_rt::fs::chdir(path.as_os_str().as_str()).map_err(map_motor_error) +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + moto_rt::process::current_exe().map(PathBuf::from).map_err(map_motor_error) +} + +pub fn temp_dir() -> PathBuf { + PathBuf::from(moto_rt::fs::TEMP_DIR) +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + moto_rt::process::exit(code) +} + +pub fn getpid() -> u32 { + panic!("Pids on Motor OS are u64.") +} diff --git a/library/std/src/sys/pal/motor/pipe.rs b/library/std/src/sys/pal/motor/pipe.rs new file mode 100644 index 0000000000000..d3be6ddf1573e --- /dev/null +++ b/library/std/src/sys/pal/motor/pipe.rs @@ -0,0 +1,121 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::map_motor_error; +use crate::sys_common::{FromInner, IntoInner}; + +#[derive(Debug)] +pub struct AnonPipe(FileDesc); + +impl From for AnonPipe { + fn from(rt_fd: moto_rt::RtFd) -> AnonPipe { + unsafe { AnonPipe::from_raw_fd(rt_fd) } + } +} + +impl AnonPipe { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut temp_vec = Vec::new(); + let mut size = 0_usize; + loop { + temp_vec.resize(256, 0_u8); + match self.read(&mut temp_vec[..]) { + Ok(sz) => { + if sz == 0 { + return Ok(size); + } + size += sz; + temp_vec.truncate(sz); + buf.append(&mut temp_vec); + } + Err(err) => { + if size != 0 { + return Ok(size); + } else { + return Err(err); + } + } + } + } + } +} + +impl AsRawFd for AnonPipe { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl FromRawFd for AnonPipe { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let desc = FileDesc::from_raw_fd(fd); + Self(desc) + } +} + +impl IntoRawFd for AnonPipe { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl AsFd for AnonPipe { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> OwnedFd { + self.0.into_inner() + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for AnonPipe { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +pub fn read2(_p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { + Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) +} + +#[inline] +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/pal/motor/time.rs b/library/std/src/sys/pal/motor/time.rs new file mode 100644 index 0000000000000..e917fd466c2e4 --- /dev/null +++ b/library/std/src/sys/pal/motor/time.rs @@ -0,0 +1 @@ +pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH}; diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index faa2616a6320d..15530323a198d 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -62,7 +62,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { } pub(crate) fn is_absolute(path: &Path) -> bool { - if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) { path.has_root() } else { path.has_root() && path.prefix().is_some() diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 158e44e1764a3..eabef92244d01 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -17,7 +17,7 @@ cfg_select! { target_os = "emscripten" => { mod emcc; } - any(target_env = "msvc", target_family = "wasm") => { + any(target_env = "msvc", target_family = "wasm", target_os = "motor") => { // This is required by the compiler to exist (e.g., it's a lang item), // but it's never actually called by the compiler because // __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index a1ed0cd2cdd2d..92e459298fc4c 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -11,6 +11,10 @@ cfg_select! { mod uefi; use uefi as imp; } + target_os = "motor" => { + mod motor; + use motor as imp; + } _ => { mod unsupported; use unsupported as imp; @@ -38,6 +42,7 @@ pub use imp::{ )) ), target_os = "windows", + target_os = "motor" ))] pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { use crate::sys::pipe::read2; @@ -77,5 +82,6 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< )) ), target_os = "windows", + target_os = "motor" )))] pub use imp::output; diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs new file mode 100644 index 0000000000000..9060902bc3d20 --- /dev/null +++ b/library/std/src/sys/process/motor.rs @@ -0,0 +1,313 @@ +use super::CommandEnvs; +use super::env::CommandEnv; +use crate::ffi::OsStr; +pub use crate::ffi::OsString as EnvKey; +use crate::num::NonZeroI32; +use crate::os::fd::{FromRawFd, IntoRawFd}; +use crate::os::motor::ffi::OsStrExt; +use crate::path::Path; +use crate::process::StdioPipes; +use crate::sys::fs::File; +use crate::sys::map_motor_error; +use crate::sys::pipe::AnonPipe; +use crate::sys_common::{AsInner, FromInner}; +use crate::{fmt, io}; + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(crate::sys::fd::FileDesc), +} + +impl Stdio { + fn into_rt(self) -> moto_rt::RtFd { + match self { + Stdio::Inherit => moto_rt::process::STDIO_INHERIT, + Stdio::Null => moto_rt::process::STDIO_NULL, + Stdio::MakePipe => moto_rt::process::STDIO_MAKE_PIPE, + Stdio::Fd(fd) => fd.into_raw_fd(), + } + } + + fn try_clone(&self) -> io::Result { + match self { + Self::Fd(fd) => { + Ok(Self::Fd(crate::sys::fd::FileDesc::from_inner(fd.as_inner().try_clone()?))) + } + Self::Inherit => Ok(Self::Inherit), + Self::Null => Ok(Self::Null), + Self::MakePipe => Ok(Self::MakePipe), + } + } +} + +#[derive(Default)] +pub struct Command { + program: String, + args: Vec, + cwd: Option, + stdin: Option, + stdout: Option, + stderr: Option, + env: CommandEnv, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut env = CommandEnv::default(); + env.remove(OsStr::new(moto_rt::process::STDIO_IS_TERMINAL_ENV_KEY)); + + Command { program: program.as_str().to_owned(), env, ..Default::default() } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.as_str().to_owned()) + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.as_str().to_owned()) + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn get_program(&self) -> &OsStr { + OsStr::new(self.program.as_str()) + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let iter = self.args.iter(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(Path::new) + } + + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + let stdin = if let Some(stdin) = self.stdin.as_ref() { + stdin.try_clone()?.into_rt() + } else if needs_stdin { + default.try_clone()?.into_rt() + } else { + Stdio::Null.into_rt() + }; + let stdout = if let Some(stdout) = self.stdout.as_ref() { + stdout.try_clone()?.into_rt() + } else { + default.try_clone()?.into_rt() + }; + let stderr = if let Some(stderr) = self.stdout.as_ref() { + stderr.try_clone()?.into_rt() + } else { + default.try_clone()?.into_rt() + }; + + let mut env = Vec::<(String, String)>::new(); + for (k, v) in self.env.capture() { + env.push((k.as_str().to_owned(), v.as_str().to_owned())); + } + + let args = moto_rt::process::SpawnArgs { + program: self.program.clone(), + args: self.args.clone(), + env, + cwd: self.cwd.clone(), + stdin, + stdout, + stderr, + }; + + let (handle, stdin, stdout, stderr) = + moto_rt::process::spawn(args).map_err(map_motor_error)?; + + Ok(( + Process { handle }, + StdioPipes { + stdin: if stdin >= 0 { Some(stdin.into()) } else { None }, + stdout: if stdout >= 0 { Some(stdout.into()) } else { None }, + stderr: if stderr >= 0 { Some(stderr.into()) } else { None }, + }, + )) + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) } + } +} + +impl From for Stdio { + fn from(fd: crate::sys::fd::FileDesc) -> Stdio { + Stdio::Fd(fd) + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + panic!("Not implemented") + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + panic!("Not implemented") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + panic!("Not implemented") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct ExitStatus(i32); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == 0 { Ok(()) } else { Err(ExitStatusError(*self)) } + } + + pub fn code(&self) -> Option { + Some(self.0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(ExitStatus); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + NonZeroI32::new(self.0.0) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(i32); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); + + pub fn as_i32(&self) -> i32 { + self.0 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code as i32) + } +} + +pub struct Process { + handle: u64, +} + +impl Drop for Process { + fn drop(&mut self) { + moto_rt::alloc::release_handle(self.handle).unwrap(); + } +} + +impl Process { + pub fn id(&self) -> u32 { + 0 + } + + pub fn kill(&mut self) -> io::Result<()> { + match moto_rt::process::kill(self.handle) { + moto_rt::E_OK => Ok(()), + err => Err(map_motor_error(err)), + } + } + + pub fn wait(&mut self) -> io::Result { + moto_rt::process::wait(self.handle).map(|c| ExitStatus(c)).map_err(map_motor_error) + } + + pub fn try_wait(&mut self) -> io::Result> { + match moto_rt::process::try_wait(self.handle) { + Ok(s) => Ok(Some(ExitStatus(s))), + Err(err) => match err { + moto_rt::E_NOT_READY => Ok(None), + err => Err(map_motor_error(err)), + }, + } + } + + #[allow(unused)] + pub fn handle(&self) -> u64 { + self.handle + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, String>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|arg| OsStr::new(arg)) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index ec81d89a0f3af..91f72d0738790 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -62,6 +62,10 @@ cfg_select! { mod redox; pub use redox::fill_bytes; } + target_os = "motor" => { + mod motor; + pub use motor::fill_bytes; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::fill_bytes; diff --git a/library/std/src/sys/random/motor.rs b/library/std/src/sys/random/motor.rs new file mode 100644 index 0000000000000..386b3704a91ea --- /dev/null +++ b/library/std/src/sys/random/motor.rs @@ -0,0 +1,3 @@ +pub fn fill_bytes(bytes: &mut [u8]) { + moto_rt::fill_random_bytes(bytes) +} diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 660317e3ea844..d51ea9ad726b5 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -13,6 +13,10 @@ cfg_select! { mod sgx; pub use sgx::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "solid_asp3" => { mod solid; pub use solid::*; diff --git a/library/std/src/sys/stdio/motor.rs b/library/std/src/sys/stdio/motor.rs new file mode 100644 index 0000000000000..0a44feab723d0 --- /dev/null +++ b/library/std/src/sys/stdio/motor.rs @@ -0,0 +1,232 @@ +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{io, process, sys}; + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub struct Stdin {} + +impl Stdin { + pub const fn new() -> Self { + Self {} + } +} + +pub struct Stdout {} + +impl Stdout { + pub const fn new() -> Self { + Self {} + } +} + +pub struct Stderr {} + +impl Stderr { + pub const fn new() -> Self { + Self {} + } +} + +impl crate::sealed::Sealed for Stdin {} + +impl crate::io::IsTerminal for Stdin { + fn is_terminal(&self) -> bool { + moto_rt::fs::is_terminal(moto_rt::FD_STDIN) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(moto_rt::FD_STDIN, buf).map_err(map_motor_error) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(moto_rt::FD_STDOUT, buf).map_err(map_motor_error) + } + + fn flush(&mut self) -> io::Result<()> { + moto_rt::fs::flush(moto_rt::FD_STDOUT).map_err(map_motor_error) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(moto_rt::FD_STDERR, buf).map_err(map_motor_error) + } + + fn flush(&mut self) -> io::Result<()> { + moto_rt::fs::flush(moto_rt::FD_STDERR).map_err(map_motor_error) + } +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawFd for process::Stdio { + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { + let fd = unsafe { sys::fd::FileDesc::from_raw_fd(fd) }; + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for process::Stdio { + /// Takes ownership of a file descriptor and returns a [`Stdio`](process::Stdio) + /// that can attach a stream to it. + #[inline] + fn from(fd: OwnedFd) -> process::Stdio { + let fd = sys::fd::FileDesc::from_inner(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdin { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdout { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStderr { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdin { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdout { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStderr { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { + child_stdin.into_inner().into_inner() + } +} + +/// Creates a `ChildStdin` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStdin { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdin { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdin::from_inner(pipe) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { + child_stdout.into_inner().into_inner() + } +} + +/// Creates a `ChildStdout` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStdout { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdout { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdout::from_inner(pipe) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { + child_stderr.into_inner().into_inner() + } +} + +/// Creates a `ChildStderr` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStderr { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStderr { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStderr::from_inner(pipe) + } +} diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index cb67d273759dd..83cf0ae629851 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -6,6 +6,7 @@ cfg_select! { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", + target_os = "motor", target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index c885b0eabae2d..e3d6ad1129c83 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -5,6 +5,7 @@ cfg_select! { target_os = "android", target_os = "freebsd", target_os = "openbsd", + target_os = "motor", target_os = "dragonfly", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index 8adeb1f259d72..aeea884b9f617 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -14,6 +14,7 @@ cfg_select! { target_os = "android", all(target_arch = "wasm32", target_feature = "atomics"), target_os = "freebsd", + target_os = "motor", target_os = "openbsd", target_os = "dragonfly", target_os = "fuchsia", diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 82f1dd18dee49..ab5715bf2de33 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -9,6 +9,7 @@ cfg_select! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", + target_os = "motor", ) => { mod futex; pub use futex::RwLock; diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index b9fb27b4eef2d..e8a9dc884f816 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -8,6 +8,7 @@ cfg_select! { target_os = "openbsd", target_os = "dragonfly", target_os = "fuchsia", + target_os = "motor", target_os = "hermit", ) => { mod futex; diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index a20b2a3ddd8ce..b98be62be0ad2 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -6,6 +6,10 @@ cfg_select! { mod unsupported; pub use unsupported::{current_os_id, set_name}; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; diff --git a/library/std/src/sys/thread/motor.rs b/library/std/src/sys/thread/motor.rs new file mode 100644 index 0000000000000..0457d8818f326 --- /dev/null +++ b/library/std/src/sys/thread/motor.rs @@ -0,0 +1,63 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::sys::map_motor_error; +use crate::time::Duration; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 256; + +pub struct Thread { + sys_thread: moto_rt::thread::ThreadHandle, +} + +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +impl Thread { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { + extern "C" fn __moto_rt_thread_fn(thread_arg: u64) { + unsafe { + Box::from_raw( + core::ptr::with_exposed_provenance::>(thread_arg as usize) + .cast_mut(), + )(); + } + } + + let thread_arg = Box::into_raw(Box::new(p)).expose_provenance() as u64; + let sys_thread = moto_rt::thread::spawn(__moto_rt_thread_fn, stack, thread_arg) + .map_err(map_motor_error)?; + Ok(Self { sys_thread }) + } + + pub fn join(self) { + assert!(moto_rt::thread::join(self.sys_thread) == moto_rt::E_OK) + } +} + +pub fn set_name(name: &CStr) { + let bytes = name.to_bytes(); + if let Ok(s) = core::str::from_utf8(bytes) { + let _ = moto_rt::thread::set_name(s); + } +} + +pub fn current_os_id() -> Option { + None +} + +pub fn available_parallelism() -> io::Result { + Ok(unsafe { NonZeroUsize::new_unchecked(moto_rt::num_cpus()) }) +} + +pub fn yield_now() { + moto_rt::thread::yield_now() +} + +pub fn sleep(dur: Duration) { + moto_rt::thread::sleep_until(moto_rt::time::Instant::now() + dur) +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index f7f051b1addc4..e88011aa22dad 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -187,6 +187,14 @@ pub(crate) mod key { pub(super) use xous::{Key, get, set}; use xous::{create, destroy}; } + target_os = "motor" => { + mod racy; + #[cfg(test)] + mod tests; + pub(super) use racy::LazyKey; + pub(super) use moto_rt::tls::{Key, get, set}; + use moto_rt::tls::{create, destroy}; + } _ => {} } } From d1be6d810b666fdf92adbe75c42d2c058201de46 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 18:10:31 +0200 Subject: [PATCH 1425/1900] Make `obfuscated_if_else` a bit more type-safe --- clippy_lints/src/methods/mod.rs | 15 ++++-- .../src/methods/obfuscated_if_else.rs | 51 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a8f1ec7e91986..940e0c51c7d1e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5501,7 +5501,14 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + then_method, + obfuscated_if_else::Unwrap::Or(u_arg), + ); }, _ => {}, } @@ -5518,9 +5525,8 @@ impl Methods { expr, t_recv, t_arg, - None, then_method, - sym::unwrap_or_default, + obfuscated_if_else::Unwrap::OrDefault, ); }, _ => {}, @@ -5537,9 +5543,8 @@ impl Methods { expr, t_recv, t_arg, - Some(u_arg), then_method, - sym::unwrap_or_else, + obfuscated_if_else::Unwrap::OrElse(u_arg), ); }, _ => { diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 604b48656aeac..b2466bbd982d9 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -5,25 +5,24 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::ExprKind; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Symbol; +#[expect(clippy::needless_pass_by_value)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - then_recv: &'tcx hir::Expr<'_>, - then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: Option<&'tcx hir::Expr<'_>>, + expr: &'tcx Expr<'_>, + then_recv: &'tcx Expr<'_>, + then_arg: &'tcx Expr<'_>, then_method_name: Symbol, - unwrap_method_name: Symbol, + unwrap: Unwrap<'tcx>, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { let then_eager = switch_to_eager_eval(cx, then_arg); - let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + let unwrap_eager = unwrap.arg().is_none_or(|arg| switch_to_eager_eval(cx, arg)); let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable @@ -40,18 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol - let els = match unwrap_method_name { - sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { - let body = cx.tcx.hir_body(closure.body); - snippet_with_applicability(cx, body.value.span, "..", &mut applicability) - }, - sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { - snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" + let els = match unwrap { + Unwrap::Or(arg) => snippet_with_applicability(cx, arg.span, "..", &mut applicability), + Unwrap::OrElse(arg) => match arg.kind { + ExprKind::Closure(closure) => { + let body = cx.tcx.hir_body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + ExprKind::Path(_) => snippet_with_applicability(cx, arg.span, "_", &mut applicability) + "()", + _ => return, }, - sym::unwrap_or_default => "Default::default()".into(), - _ => return, + Unwrap::OrDefault => "Default::default()".into(), }; let sugg = format!( @@ -83,3 +81,18 @@ pub(super) fn check<'tcx>( ); } } + +pub(super) enum Unwrap<'tcx> { + Or(&'tcx Expr<'tcx>), + OrElse(&'tcx Expr<'tcx>), + OrDefault, +} + +impl<'tcx> Unwrap<'tcx> { + fn arg(&self) -> Option<&'tcx Expr<'tcx>> { + match self { + Self::Or(a) | Self::OrElse(a) => Some(a), + Self::OrDefault => None, + } + } +} From 1654cce2108dd73173c5bd978ba6523d5a1834d7 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 01:24:55 +0800 Subject: [PATCH 1426/1900] prefer to use repeat_n over repeat and take --- compiler/rustc_builtin_macros/src/asm.rs | 4 ++-- compiler/rustc_codegen_cranelift/src/constant.rs | 2 +- compiler/rustc_const_eval/src/const_eval/error.rs | 2 +- .../rustc_const_eval/src/interpret/intrinsics.rs | 2 +- .../src/util/check_validity_requirement.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 2 +- .../src/errors/wrong_number_of_generic_args.rs | 9 +++------ compiler/rustc_hir_typeck/src/expr.rs | 2 +- compiler/rustc_lint/src/if_let_rescope.rs | 4 ++-- compiler/rustc_middle/src/values.rs | 2 +- compiler/rustc_mir_build/src/check_tail_calls.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- .../rustc_query_system/src/dep_graph/serialized.rs | 4 ++-- compiler/rustc_resolve/src/late/diagnostics.rs | 12 +++--------- .../infer/nice_region_error/static_impl_trait.rs | 2 +- compiler/rustc_transmute/src/layout/tree.rs | 3 +-- 16 files changed, 23 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index ae62b5ea2a097..971b68d14757a 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -368,7 +368,7 @@ fn expand_preparsed_asm( if args.options.contains(ast::InlineAsmOptions::RAW) { template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string().into())); let template_num_lines = 1 + template_str.matches('\n').count(); - line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); + line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines)); continue; } @@ -523,7 +523,7 @@ fn expand_preparsed_asm( if parser.line_spans.is_empty() { let template_num_lines = 1 + template_str.matches('\n').count(); - line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); + line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines)); } else { line_spans.extend( parser diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index a56466750e75c..faca92957e1a6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -318,7 +318,7 @@ fn data_id_for_static( let mut data = DataDescription::new(); data.set_align(align); let data_gv = module.declare_data_in_data(data_id, &mut data); - data.define(std::iter::repeat(0).take(pointer_ty(tcx).bytes() as usize).collect()); + data.define(std::iter::repeat_n(0, pointer_ty(tcx).bytes() as usize).collect()); data.write_data_addr(0, data_gv, 0); match module.define_data(ref_data_id, &data) { // Every time the static is referenced there will be another definition of this global, diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index e00fb2c1eaf98..69a8f163ca93c 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -110,7 +110,7 @@ pub fn get_span_and_frames<'tcx>( if frame.times < 3 { let times = frame.times; frame.times = 0; - frames.extend(std::iter::repeat(frame).take(times as usize)); + frames.extend(std::iter::repeat_n(frame, times as usize)); } else { frames.push(frame); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 785978b4d7111..630e99ad4c74e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -878,7 +878,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .compute_size_in_bytes(layout.size, count) .ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?; - let bytes = std::iter::repeat(byte).take(len.bytes_usize()); + let bytes = std::iter::repeat_n(byte, len.bytes_usize()); self.write_bytes_ptr(dst, bytes) } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 1dea7e4252d7f..34bd8423e8e15 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -59,7 +59,7 @@ fn check_validity_requirement_strict<'tcx>( if kind == ValidityRequirement::Zero { cx.write_bytes_ptr( allocated.ptr(), - std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()), + std::iter::repeat_n(0_u8, ty.layout.size().bytes_usize()), ) .expect("failed to write bytes for zero valid check"); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 02baaec37138c..24e20ef5b1b2e 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -590,7 +590,7 @@ fn get_new_lifetime_name<'tcx>( let a_to_z_repeat_n = |n| { (b'a'..=b'z').map(move |c| { let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); + s.extend(std::iter::repeat_n(char::from(c), n)); s }) }; diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 8a9f9130feacb..86ac9f21809e4 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -339,8 +339,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { hir::GenericArg::Lifetime(lt) => Some(lt), _ => None, }) { - return std::iter::repeat(lt.to_string()) - .take(num_params_to_take) + return std::iter::repeat_n(lt.to_string(), num_params_to_take) .collect::>() .join(", "); } @@ -362,8 +361,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { matches!(fn_decl.output, hir::FnRetTy::Return(ty) if ty.hir_id == ty_id); if in_arg || (in_ret && fn_decl.lifetime_elision_allowed) { - return std::iter::repeat("'_".to_owned()) - .take(num_params_to_take) + return std::iter::repeat_n("'_".to_owned(), num_params_to_take) .collect::>() .join(", "); } @@ -388,8 +386,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { }) | hir::Node::AnonConst(..) = node { - return std::iter::repeat("'static".to_owned()) - .take(num_params_to_take.saturating_sub(ret.len())) + return std::iter::repeat_n("'static".to_owned(), num_params_to_take.saturating_sub(ret.len())) .collect::>() .join(", "); } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f9cdc923670f0..e57f970a0e9a2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2602,7 +2602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestion = |name, args| { format!( "::{name}({})", - std::iter::repeat("_").take(args).collect::>().join(", ") + std::iter::repeat_n("_", args).collect::>().join(", ") ) }; match &items[..] { diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index ff67ed1bc5532..2521c2b4eb6ac 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -1,4 +1,4 @@ -use std::iter::repeat; +use std::iter::repeat_n; use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; @@ -351,7 +351,7 @@ impl Subdiagnostic for IfLetRescopeRewrite { .then_some(" _ => {}".chars()) .into_iter() .flatten() - .chain(repeat('}').take(closing_brackets.count)) + .chain(repeat_n('}', closing_brackets.count)) .collect(), )); let msg = diag.eagerly_translate(crate::fluent_generated::lint_suggestion); diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 4d70a70873267..bc73d36216ef4 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -63,7 +63,7 @@ impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { }; let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig( - std::iter::repeat(err).take(arity), + std::iter::repeat_n(err, arity), err, false, rustc_hir::Safety::Safe, diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index d40c77d145f47..9115c17f37525 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -283,7 +283,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { fn report_calling_closure(&mut self, fun: &Expr<'_>, tupled_args: Ty<'_>, expr: &Expr<'_>) { let underscored_args = match tupled_args.kind() { ty::Tuple(tys) if tys.is_empty() => "".to_owned(), - ty::Tuple(tys) => std::iter::repeat("_, ").take(tys.len() - 1).chain(["_"]).collect(), + ty::Tuple(tys) => std::iter::repeat_n("_, ", tys.len() - 1).chain(["_"]).collect(), _ => "_".to_owned(), }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4ea237cfa0321..a5a6cf503524c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2019,7 +2019,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } let expected_sig = tcx.mk_fn_sig( - std::iter::repeat(token_stream).take(match kind { + std::iter::repeat_n(token_stream, match kind { ProcMacroKind::Attribute => 2, ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1, }), diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 79b99c52d0c01..f5eb61a449f8b 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -566,7 +566,7 @@ impl EncoderState { edge_count: 0, node_count: 0, encoder: MemEncoder::new(), - kind_stats: iter::repeat(0).take(D::DEP_KIND_MAX as usize + 1).collect(), + kind_stats: iter::repeat_n(0, D::DEP_KIND_MAX as usize + 1).collect(), }) }), marker: PhantomData, @@ -735,7 +735,7 @@ impl EncoderState { let mut encoder = self.file.lock().take().unwrap(); - let mut kind_stats: Vec = iter::repeat(0).take(D::DEP_KIND_MAX as usize + 1).collect(); + let mut kind_stats: Vec = iter::repeat_n(0, D::DEP_KIND_MAX as usize + 1).collect(); let mut node_max = 0; let mut node_count = 0; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8c2ddda7f9834..1589e3f4092c4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2214,10 +2214,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .collect::>(); items.sort_by_key(|(order, _, _)| *order); let suggestion = |name, args| { - format!( - "::{name}({})", - std::iter::repeat("_").take(args).collect::>().join(", ") - ) + format!("::{name}({})", std::iter::repeat_n("_", args).collect::>().join(", ")) }; match &items[..] { [] => {} @@ -3485,17 +3482,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (lt.span.shrink_to_hi(), format!("{existing_name} ")) } MissingLifetimeKind::Comma => { - let sugg: String = std::iter::repeat([existing_name.as_str(), ", "]) - .take(lt.count) + let sugg: String = std::iter::repeat_n([existing_name.as_str(), ", "], lt.count) .flatten() .collect(); (lt.span.shrink_to_hi(), sugg) } MissingLifetimeKind::Brackets => { let sugg: String = std::iter::once("<") - .chain( - std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "), - ) + .chain(std::iter::repeat_n(existing_name.as_str(), lt.count).intersperse(", ")) .chain([">"]) .collect(); (lt.span.shrink_to_hi(), sugg) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 5b8315841765b..3ee6e6b739c03 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -305,7 +305,7 @@ fn make_elided_region_spans_suggs<'a>( consecutive_brackets += 1; } else if let Some(bracket_span) = bracket_span.take() { let sugg = std::iter::once("<") - .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", ")) + .chain(std::iter::repeat_n(name, consecutive_brackets).intersperse(", ")) .chain([">"]) .collect(); spans_suggs.push((bracket_span.shrink_to_hi(), sugg)); diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 7f626e8c4e886..d9ebdadf3b79c 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -326,8 +326,7 @@ pub(crate) mod rustc { let inner_layout = layout_of(cx, *inner_ty)?; assert_eq!(*stride, inner_layout.size); let elt = Tree::from_ty(*inner_ty, cx)?; - Ok(std::iter::repeat(elt) - .take(*count as usize) + Ok(std::iter::repeat_n(elt, *count as usize) .fold(Tree::unit(), |tree, elt| tree.then(elt))) } From affaf532f923df7ee1fb5a4aae1d844913ca1c74 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 01:25:32 +0800 Subject: [PATCH 1427/1900] referring to repeat_n in std::iter::repeat --- library/core/src/iter/sources/repeat.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index 4bcd5b16aea6a..7fcd6f8963a48 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -8,6 +8,9 @@ use crate::num::NonZero; /// Infinite iterators like `repeat()` are often used with adapters like /// [`Iterator::take()`], in order to make them finite. /// +/// If you know the number of repetitions in advance, consider using [`repeat_n()`] +/// instead, as it is more efficient and conveys the intent more clearly. +/// /// Use [`str::repeat()`] instead of this function if you just want to repeat /// a char/string `n` times. /// @@ -15,6 +18,7 @@ use crate::num::NonZero; /// or if you do not want to keep the repeated element in memory, you can /// instead use the [`repeat_with()`] function. /// +/// [`repeat_n()`]: crate::iter::repeat_n /// [`repeat_with()`]: crate::iter::repeat_with /// [`str::repeat()`]: ../../std/primitive.str.html#method.repeat /// From 80b886e8958f5a2b4afa210ec169a28721b98cdf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 19:27:51 +0200 Subject: [PATCH 1428/1900] perf(get_unwrap): avoid calling `is_type_diagnostic_item` multiple times --- clippy_lints/src/methods/get_unwrap.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 9daad1a8a949e..d273558a7006e 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -2,7 +2,6 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,16 +21,17 @@ pub(super) fn check<'tcx>( let expr_ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { - "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) { - "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) { - "HashMap" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) { - "BTreeMap" } else { - return; // caller is not a type that we want to lint + match expr_ty + .ty_adt_def() + .and_then(|def| cx.tcx.get_diagnostic_name(def.did())) + { + Some(sym::Vec) => "Vec", + Some(sym::VecDeque) => "VecDeque", + Some(sym::HashMap) if !is_mut => "HashMap", + Some(sym::BTreeMap) if !is_mut => "BTreeMap", + _ => return, // caller is not a type that we want to lint + } }; let mut span = expr.span; From 53c79f4523b228cf04a772dc474ac9315b456f7f Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 01:29:16 +0800 Subject: [PATCH 1429/1900] bless format --- .../src/errors/wrong_number_of_generic_args.rs | 9 ++++++--- compiler/rustc_passes/src/check_attr.rs | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 86ac9f21809e4..30324da3c6516 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -386,9 +386,12 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { }) | hir::Node::AnonConst(..) = node { - return std::iter::repeat_n("'static".to_owned(), num_params_to_take.saturating_sub(ret.len())) - .collect::>() - .join(", "); + return std::iter::repeat_n( + "'static".to_owned(), + num_params_to_take.saturating_sub(ret.len()), + ) + .collect::>() + .join(", "); } let params = if let Some(generics) = node.generics() { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a5a6cf503524c..ad4b035916cd9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2019,10 +2019,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } let expected_sig = tcx.mk_fn_sig( - std::iter::repeat_n(token_stream, match kind { - ProcMacroKind::Attribute => 2, - ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1, - }), + std::iter::repeat_n( + token_stream, + match kind { + ProcMacroKind::Attribute => 2, + ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1, + }, + ), token_stream, false, Safety::Safe, From 99ab27f90cf9e3b020e61fca65f9a8c1ec9a1695 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Fri, 3 Oct 2025 13:43:11 +0200 Subject: [PATCH 1430/1900] specialize slice::fill to use memset when possible LLVM generally can do this on its own, but it helps miri and other backends. --- library/core/src/slice/specialize.rs | 47 ++++++++++++++++++- .../lib-optimizations/slice_fill.rs | 28 +++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/lib-optimizations/slice_fill.rs diff --git a/library/core/src/slice/specialize.rs b/library/core/src/slice/specialize.rs index 80eb590587f99..17436395fee69 100644 --- a/library/core/src/slice/specialize.rs +++ b/library/core/src/slice/specialize.rs @@ -15,9 +15,54 @@ impl SpecFill for [T] { } impl SpecFill for [T] { - fn spec_fill(&mut self, value: T) { + default fn spec_fill(&mut self, value: T) { for item in self.iter_mut() { *item = value; } } } + +impl SpecFill for [u8] { + fn spec_fill(&mut self, value: u8) { + // SAFETY: The pointer is derived from a reference, so it's writable. + unsafe { + crate::intrinsics::write_bytes(self.as_mut_ptr(), value, self.len()); + } + } +} + +impl SpecFill for [i8] { + fn spec_fill(&mut self, value: i8) { + // SAFETY: The pointer is derived from a reference, so it's writable. + unsafe { + crate::intrinsics::write_bytes(self.as_mut_ptr(), value.cast_unsigned(), self.len()); + } + } +} + +macro spec_fill_int { + ($($type:ty)*) => {$( + impl SpecFill<$type> for [$type] { + #[inline] + fn spec_fill(&mut self, value: $type) { + // We always take this fastpath in Miri for long slices as the manual `for` + // loop can be prohibitively slow. + if (cfg!(miri) && self.len() > 32) || crate::intrinsics::is_val_statically_known(value) { + let bytes = value.to_ne_bytes(); + if value == <$type>::from_ne_bytes([bytes[0]; size_of::<$type>()]) { + // SAFETY: The pointer is derived from a reference, so it's writable. + unsafe { + crate::intrinsics::write_bytes(self.as_mut_ptr(), bytes[0], self.len()); + } + return; + } + } + for item in self.iter_mut() { + *item = value; + } + } + } + )*} +} + +spec_fill_int! { u16 i16 u32 i32 u64 i64 u128 i128 usize isize } diff --git a/tests/codegen-llvm/lib-optimizations/slice_fill.rs b/tests/codegen-llvm/lib-optimizations/slice_fill.rs new file mode 100644 index 0000000000000..2d924ebf726d8 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/slice_fill.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Copt-level=3 +#![crate_type = "lib"] + +use std::mem::MaybeUninit; + +// CHECK-LABEL: @slice_fill_pass_undef +#[no_mangle] +pub fn slice_fill_pass_undef(s: &mut [MaybeUninit], v: MaybeUninit) { + // CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 1 %s.0, i8 %v, {{.*}} %s.1, i1 false) + // CHECK: ret + s.fill(v); +} + +// CHECK-LABEL: @slice_fill_uninit +#[no_mangle] +pub fn slice_fill_uninit(s: &mut [MaybeUninit]) { + // CHECK-NOT: call + // CHECK: ret void + s.fill(MaybeUninit::uninit()); +} + +// CHECK-LABEL: @slice_wide_memset +#[no_mangle] +pub fn slice_wide_memset(s: &mut [u16]) { + // CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 2 %s.0, i8 -1 + // CHECK: ret + s.fill(0xFFFF); +} From 0355358e6cd106f771392eadae3c0e3f49b17e98 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 8 Oct 2025 13:49:32 +0200 Subject: [PATCH 1431/1900] run zero-size assertion in `const {}` --- library/proc_macro/src/bridge/selfless_reify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index b06434a5ffee2..a53550e0b9e0c 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -50,7 +50,7 @@ macro_rules! define_reify_functions { >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic // formatting becomes possible in `const fn`. - assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); + const { assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } $(extern $abi)? fn wrapper< $($($param,)*)? From e3d2016a2264b704a7f6f1c18e45ce21732b627b Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 8 Oct 2025 14:33:31 +0200 Subject: [PATCH 1432/1900] inline constants in generated `enum` `Encode` impls --- library/proc_macro/src/bridge/rpc.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 7f4f5fc3a97d5..6e5afe282d683 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -58,16 +58,11 @@ macro_rules! rpc_encode_decode { fn encode(self, w: &mut Writer, s: &mut S) { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* - } + #[repr(u8)] enum Tag { $($variant),* } match self { $($name::$variant $(($field))* => { - tag::$variant.encode(w, s); + (Tag::$variant as u8).encode(w, s); $($field.encode(w, s);)* })* } From 5702fbf03d35ae38573e5861231ad2dda83bc5c8 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 8 Oct 2025 13:40:12 +0000 Subject: [PATCH 1433/1900] Refactor AddCallGuards in two loops. --- .../src/add_call_guards.rs | 137 +++++++++++------- 1 file changed, 81 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 26839ebf61e46..d96c7d84dbefd 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -1,3 +1,20 @@ +//! Breaks outgoing critical edges for call terminators in the MIR. +//! +//! Critical edges are edges that are neither the only edge leaving a +//! block, nor the only edge entering one. +//! +//! When you want something to happen "along" an edge, you can either +//! do at the end of the predecessor block, or at the start of the +//! successor block. Critical edges have to be broken in order to prevent +//! "edge actions" from affecting other edges. We need this for calls that are +//! codegened to LLVM invoke instructions, because invoke is a block terminator +//! in LLVM so we can't insert any code to handle the call's result into the +//! block that performs the call. +//! +//! This function will break those edges by inserting new blocks along them. +//! +//! NOTE: Simplify CFG will happily undo most of the work this pass does. + use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -10,26 +27,6 @@ pub(super) enum AddCallGuards { } pub(super) use self::AddCallGuards::*; -/** - * Breaks outgoing critical edges for call terminators in the MIR. - * - * Critical edges are edges that are neither the only edge leaving a - * block, nor the only edge entering one. - * - * When you want something to happen "along" an edge, you can either - * do at the end of the predecessor block, or at the start of the - * successor block. Critical edges have to be broken in order to prevent - * "edge actions" from affecting other edges. We need this for calls that are - * codegened to LLVM invoke instructions, because invoke is a block terminator - * in LLVM so we can't insert any code to handle the call's result into the - * block that performs the call. - * - * This function will break those edges by inserting new blocks along them. - * - * NOTE: Simplify CFG will happily undo most of the work this pass does. - * - */ - impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut pred_count = IndexVec::from_elem(0u8, &body.basic_blocks); @@ -39,51 +36,38 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { } } - // We need a place to store the new blocks generated - let mut new_blocks = Vec::new(); - - let cur_len = body.basic_blocks.len(); - let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| { - let block = BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), - is_cleanup, - ); - let idx = cur_len + new_blocks.len(); - new_blocks.push(block); - BasicBlock::new(idx) - }; + enum Action { + Call, + Asm { target_index: usize }, + } - for block in body.basic_blocks_mut() { - match block.terminator { - Some(Terminator { - kind: TerminatorKind::Call { target: Some(ref mut destination), unwind, .. }, - source_info, - }) if pred_count[*destination] > 1 - && (generates_invoke(unwind) || self == &AllCallEdges) => + let mut work = Vec::with_capacity(body.basic_blocks.len()); + for (bb, block) in body.basic_blocks.iter_enumerated() { + let term = block.terminator(); + match term.kind { + TerminatorKind::Call { target: Some(destination), unwind, .. } + if pred_count[destination] > 1 + && (generates_invoke(unwind) || self == &AllCallEdges) => { // It's a critical edge, break it - *destination = new_block(source_info, block.is_cleanup, *destination); + work.push((bb, Action::Call)); } - Some(Terminator { - kind: - TerminatorKind::InlineAsm { - asm_macro: InlineAsmMacro::Asm, - ref mut targets, - ref operands, - unwind, - .. - }, - source_info, - }) if self == &CriticalCallEdges => { + TerminatorKind::InlineAsm { + asm_macro: InlineAsmMacro::Asm, + ref targets, + ref operands, + unwind, + .. + } if self == &CriticalCallEdges => { let has_outputs = operands.iter().any(|op| { matches!(op, InlineAsmOperand::InOut { .. } | InlineAsmOperand::Out { .. }) }); let has_labels = operands.iter().any(|op| matches!(op, InlineAsmOperand::Label { .. })); if has_outputs && (has_labels || generates_invoke(unwind)) { - for target in targets.iter_mut() { + for (target_index, target) in targets.iter().enumerate() { if pred_count[*target] > 1 { - *target = new_block(source_info, block.is_cleanup, *target); + work.push((bb, Action::Asm { target_index })); } } } @@ -92,9 +76,50 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { } } - debug!("Broke {} N edges", new_blocks.len()); + if work.is_empty() { + return; + } - body.basic_blocks_mut().extend(new_blocks); + // We need a place to store the new blocks generated + let mut new_blocks = Vec::with_capacity(work.len()); + + let cur_len = body.basic_blocks.len(); + let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| { + let block = BasicBlockData::new( + Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + is_cleanup, + ); + let idx = cur_len + new_blocks.len(); + new_blocks.push(block); + BasicBlock::new(idx) + }; + + let basic_blocks = body.basic_blocks.as_mut(); + for (source, action) in work { + let block = &mut basic_blocks[source]; + let is_cleanup = block.is_cleanup; + let term = block.terminator_mut(); + let source_info = term.source_info; + let destination = match action { + Action::Call => { + let TerminatorKind::Call { target: Some(ref mut destination), .. } = term.kind + else { + unreachable!() + }; + destination + } + Action::Asm { target_index } => { + let TerminatorKind::InlineAsm { ref mut targets, .. } = term.kind else { + unreachable!() + }; + &mut targets[target_index] + } + }; + *destination = new_block(source_info, is_cleanup, *destination); + } + + debug!("Broke {} N edges", new_blocks.len()); + basic_blocks.extend(new_blocks); } fn is_required(&self) -> bool { From 036ab3a925f7b449edc3d2da14fb6bb25fb52c47 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Wed, 8 Oct 2025 19:08:49 +0200 Subject: [PATCH 1434/1900] refactor: Remove `LLVMRustInsertPrivateGlobal` and `define_private_global` Since it can easily be implemented using the existing LLVM C API in terms of `LLVMAddGlobal` and `LLVMSetLinkage` and `define_private_global` was only used in one place. --- compiler/rustc_codegen_llvm/src/consts.rs | 6 ++++-- compiler/rustc_codegen_llvm/src/declare.rs | 7 ------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 - compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index e14f0098dd9b4..9844c9444b3d0 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -240,11 +240,13 @@ impl<'ll> CodegenCx<'ll, '_> { let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| { bug!("symbol `{}` is already defined", name); }); - llvm::set_linkage(gv, llvm::Linkage::PrivateLinkage); gv } - _ => self.define_private_global(self.val_ty(cv)), + _ => self.define_global("", self.val_ty(cv)).unwrap_or_else(|| { + bug!("anonymous global symbol is already defined"); + }), }; + llvm::set_linkage(gv, llvm::Linkage::PrivateLinkage); llvm::set_initializer(gv, cv); set_global_alignment(self, gv, align); llvm::set_unnamed_address(gv, llvm::UnnamedAddr::Global); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 48b2d09afb674..8f69f176138cf 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -230,13 +230,6 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { } } - /// Declare a private global - /// - /// Use this function when you intend to define a global without a name. - pub(crate) fn define_private_global(&self, ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMRustInsertPrivateGlobal(self.llmod(), ty) } - } - /// Gets declared value by name. pub(crate) fn get_declared_value(&self, name: &str) -> Option<&'ll Value> { debug!("get_declared_value(name={:?})", name); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c4b5cf413a725..3a3bae3bb2bf9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1954,7 +1954,6 @@ unsafe extern "C" { NameLen: size_t, T: &'a Type, ) -> &'a Value; - pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; pub(crate) fn LLVMRustGetNamedValue( M: &Module, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e38474f09ff7a..08b6334bf404c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -228,12 +228,6 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M, return wrap(GV); } -extern "C" LLVMValueRef LLVMRustInsertPrivateGlobal(LLVMModuleRef M, - LLVMTypeRef Ty) { - return wrap(new GlobalVariable(*unwrap(M), unwrap(Ty), false, - GlobalValue::PrivateLinkage, nullptr)); -} - // Must match the layout of `rustc_codegen_llvm::llvm::ffi::AttributeKind`. enum class LLVMRustAttributeKind { AlwaysInline = 0, From 69bd890ff11768ba05f9f6d921c7914a56434db6 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 8 Oct 2025 17:39:37 +0200 Subject: [PATCH 1435/1900] Honor `allow`/`expect` attributes on ADT and `impl Clone` nodes --- .../src/derive/expl_impl_clone_on_copy.rs | 15 +++-- tests/ui/derive.rs | 15 ++++- tests/ui/derive.stderr | 55 ++----------------- 3 files changed, 28 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index dfb723b86eb93..b2bc6402561f5 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::fulfill_or_allowed; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; @@ -60,14 +61,16 @@ pub(super) fn check<'tcx>( return; } - span_lint_hir_and_then( + if fulfill_or_allowed(cx, EXPL_IMPL_CLONE_ON_COPY, [adt_hir_id]) { + return; + } + + span_lint_and_help( cx, EXPL_IMPL_CLONE_ON_COPY, - adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - |diag| { - diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); - }, + None, + "consider deriving `Clone` or removing `Copy`", ); } diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 305f73c92cd5c..036f6c444b649 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -132,7 +132,7 @@ fn issue14558() { fn main() {} mod issue15708 { - // Check that the lint posts on the type definition node + // Check that `allow`/`expect` attributes are recognized on the type definition node #[expect(clippy::expl_impl_clone_on_copy)] #[derive(Copy)] struct S; @@ -143,3 +143,16 @@ mod issue15708 { } } } + +mod issue15842 { + #[derive(Copy)] + struct S; + + // Check that `allow`/`expect` attributes are recognized on the `impl Clone` node + #[expect(clippy::expl_impl_clone_on_copy)] + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 48e55fc7469e4..2701680e788de 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,16 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:15:1 - | -LL | / impl Clone for Qux { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` @@ -33,16 +24,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:41:1 - | -LL | / impl<'a> Clone for Lt<'a> { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:54:1 @@ -55,16 +37,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:54:1 - | -LL | / impl Clone for BigArray { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:67:1 @@ -77,16 +50,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:67:1 - | -LL | / impl Clone for FnPtr { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:89:1 @@ -99,16 +63,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:89:1 - | -LL | / impl Clone for Generic2 { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: aborting due to 5 previous errors From 0e2130c2c9726a243cacafae3d0c6fdf8284301f Mon Sep 17 00:00:00 2001 From: Evan Jones Date: Wed, 8 Oct 2025 15:51:21 -0400 Subject: [PATCH 1436/1900] std::thread spawn: Docs: Link to Builder::spawn; Make same. Replace "use this API instead" with a link to Builder::spawn. Edit the paragraph to make it slightly clearer. The Scope::spawn method already included a. Make the docs for the two nearly the same. --- library/std/src/thread/mod.rs | 5 ++--- library/std/src/thread/scoped.rs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index fd7cce3f97db8..78c85c0af6449 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -620,9 +620,8 @@ impl Builder { /// (It is the responsibility of the program to either eventually join threads it /// creates or detach them; otherwise, a resource leak will result.) /// -/// This call will create a thread using default parameters of [`Builder`], if you -/// want to specify the stack size or the name of the thread, use this API -/// instead. +/// This function creates a thread with the default parameters. To specify the +/// new thread's stack size or the name, use [`Builder::spawn`]. /// /// As you can see in the signature of `spawn` there are two constraints on /// both the closure given to `spawn` and its return value, let's explain them: diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index a4c0ca5417d00..2368ce4988d89 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -181,9 +181,8 @@ impl<'scope, 'env> Scope<'scope, 'env> { /// end of the scope. In that case, if the spawned thread panics, [`scope`] will /// panic after all threads are joined. /// - /// This call will create a thread using default parameters of [`Builder`]. - /// If you want to specify the stack size or the name of the thread, use - /// [`Builder::spawn_scoped`] instead. + /// This function creates a thread with the default parameters. To specify the + /// new thread's stack size or the name, use [`Builder::spawn_scoped`]. /// /// # Panics /// From 4b6bc9a66b8eb84f96d3fa524ca30d7dde7f7560 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 8 Oct 2025 13:20:42 -0700 Subject: [PATCH 1437/1900] Update wasm-component-ld to 0.5.18 Keeping it up-to-date with upstream for the latest bug fixes and such related to wasm-tools-implemented internals. --- Cargo.lock | 87 +++++++++++++++----------- src/tools/wasm-component-ld/Cargo.toml | 2 +- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e67898756fad5..be45b93ff20f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -495,7 +495,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6059,9 +6059,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "36.0.1" +version = "37.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20689c88791776219f78c2529700d15e6a9bd57a27858c62e9ef8487956b571c" +checksum = "8d0fcd636ad2b29a7c0490799a23ad61d1c8dedfafdb970447fddd0549502b60" [[package]] name = "wasm-bindgen" @@ -6123,9 +6123,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9208f87cac2332fd80dcf36d54e9163d3446e28301e0c6e424984425738984" +checksum = "11f565dfcfd9aabb10d865b608a92ce1f93051aeb56f4c89550ed9cd97d8ce0e" dependencies = [ "anyhow", "clap", @@ -6133,9 +6133,9 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.239.0", + "wasmparser 0.240.0", "wat", - "windows-sys 0.60.2", + "windows-sys 0.61.2", "winsplit", "wit-component", "wit-parser", @@ -6160,24 +6160,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f" dependencies = [ "leb128fmt", - "wasmparser 0.239.0", + "wasmparser 0.240.0", ] [[package]] name = "wasm-metadata" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" +checksum = "ee093e1e1ccffa005b9b778f7a10ccfd58e25a20eccad294a1a93168d076befb" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.239.0", - "wasmparser 0.239.0", + "wasm-encoder 0.240.0", + "wasmparser 0.240.0", ] [[package]] @@ -6202,9 +6202,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4" dependencies = [ "bitflags", "hashbrown", @@ -6215,22 +6215,22 @@ dependencies = [ [[package]] name = "wast" -version = "239.0.0" +version = "240.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9139176fe8a2590e0fb174cdcaf373b224cb93c3dde08e4297c1361d2ba1ea5d" +checksum = "b0efe1c93db4ac562b9733e3dca19ed7fc878dba29aef22245acf84f13da4a19" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.1", - "wasm-encoder 0.239.0", + "wasm-encoder 0.240.0", ] [[package]] name = "wat" -version = "1.239.0" +version = "1.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1c941927d34709f255558166f8901a2005f8ab4a9650432e9281b7cc6f3b75" +checksum = "4ec9b6eab7ecd4d639d78515e9ea491c9bacf494aa5eda10823bd35992cf8c1e" dependencies = [ "wast", ] @@ -6295,7 +6295,7 @@ dependencies = [ "windows-collections", "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -6340,7 +6340,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", - "windows-link", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] @@ -6352,7 +6352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -6406,6 +6406,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -6413,7 +6419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6431,7 +6437,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6450,7 +6456,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6480,6 +6486,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -6502,7 +6517,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -6519,7 +6534,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6653,9 +6668,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +checksum = "7dc5474b078addc5fe8a72736de8da3acfb3ff324c2491133f8b59594afa1a20" dependencies = [ "anyhow", "bitflags", @@ -6664,17 +6679,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.239.0", + "wasm-encoder 0.240.0", "wasm-metadata", - "wasmparser 0.239.0", + "wasmparser 0.240.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +checksum = "9875ea3fa272f57cc1fc50f225a7b94021a7878c484b33792bccad0d93223439" dependencies = [ "anyhow", "id-arena", @@ -6685,7 +6700,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.239.0", + "wasmparser 0.240.0", ] [[package]] diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 3def2391a13f6..235e934b55412 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.17" +wasm-component-ld = "0.5.18" From 735ddcc05aeb3327673c2143f072667d7d30e7f4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 16 Sep 2025 17:05:06 -0500 Subject: [PATCH 1438/1900] Split overlapping_{inherent,trait}_impls --- .../src/coherence/inherent_impls_overlap.rs | 2 +- .../src/traits/coherence.rs | 92 ++++++++++++++----- .../rustc_trait_selection/src/traits/mod.rs | 3 +- .../traits/specialize/specialization_graph.rs | 4 +- 4 files changed, 75 insertions(+), 26 deletions(-) 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 242639125b19a..9052f11e109b6 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -154,7 +154,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { impl1_def_id: DefId, impl2_def_id: DefId, ) -> Result<(), ErrorGuaranteed> { - let maybe_overlap = traits::overlapping_impls( + let maybe_overlap = traits::overlapping_inherent_impls( self.tcx, impl1_def_id, impl2_def_id, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8e8c7dd7c9d48..85477afceeaaf 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -100,7 +100,7 @@ impl TrackAmbiguityCauses { /// with a suitably-freshened `ImplHeader` with those types /// instantiated. Otherwise, returns `None`. #[instrument(skip(tcx, skip_leak_check), level = "debug")] -pub fn overlapping_impls( +pub fn overlapping_inherent_impls( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, @@ -110,18 +110,37 @@ pub fn overlapping_impls( // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. - let drcx = DeepRejectCtxt::relate_infer_infer(tcx); - let impl1_ref = tcx.impl_trait_ref(impl1_def_id); - let impl2_ref = tcx.impl_trait_ref(impl2_def_id); - let may_overlap = match (impl1_ref, impl2_ref) { - (Some(a), Some(b)) => drcx.args_may_unify(a.skip_binder().args, b.skip_binder().args), - (None, None) => { - let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); - let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); - drcx.types_may_unify(self_ty1, self_ty2) - } - _ => bug!("unexpected impls: {impl1_def_id:?} {impl2_def_id:?}"), - }; + let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); + let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); + let may_overlap = DeepRejectCtxt::relate_infer_infer(tcx).types_may_unify(self_ty1, self_ty2); + + if !may_overlap { + // Some types involved are definitely different, so the impls couldn't possibly overlap. + debug!("overlapping_inherent_impls: fast_reject early-exit"); + return None; + } + + overlapping_impls(tcx, impl1_def_id, impl2_def_id, skip_leak_check, overlap_mode, false) +} + +/// If there are types that satisfy both impls, returns `Some` +/// with a suitably-freshened `ImplHeader` with those types +/// instantiated. Otherwise, returns `None`. +#[instrument(skip(tcx, skip_leak_check), level = "debug")] +pub fn overlapping_trait_impls( + tcx: TyCtxt<'_>, + impl1_def_id: DefId, + impl2_def_id: DefId, + skip_leak_check: SkipLeakCheck, + overlap_mode: OverlapMode, +) -> Option> { + // Before doing expensive operations like entering an inference context, do + // a quick check via fast_reject to tell if the impl headers could possibly + // unify. + let impl1_args = tcx.impl_trait_ref(impl1_def_id).unwrap().skip_binder().args; + let impl2_args = tcx.impl_trait_ref(impl2_def_id).unwrap().skip_binder().args; + let may_overlap = + DeepRejectCtxt::relate_infer_infer(tcx).args_may_unify(impl1_args, impl2_args); if !may_overlap { // Some types involved are definitely different, so the impls couldn't possibly overlap. @@ -129,6 +148,17 @@ pub fn overlapping_impls( return None; } + overlapping_impls(tcx, impl1_def_id, impl2_def_id, skip_leak_check, overlap_mode, true) +} + +fn overlapping_impls( + tcx: TyCtxt<'_>, + impl1_def_id: DefId, + impl2_def_id: DefId, + skip_leak_check: SkipLeakCheck, + overlap_mode: OverlapMode, + is_of_trait: bool, +) -> Option> { if tcx.next_trait_solver_in_coherence() { overlap( tcx, @@ -137,6 +167,7 @@ pub fn overlapping_impls( impl1_def_id, impl2_def_id, overlap_mode, + is_of_trait, ) } else { let _overlap_with_bad_diagnostics = overlap( @@ -146,6 +177,7 @@ pub fn overlapping_impls( impl1_def_id, impl2_def_id, overlap_mode, + is_of_trait, )?; // In the case where we detect an error, run the check again, but @@ -158,13 +190,18 @@ pub fn overlapping_impls( impl1_def_id, impl2_def_id, overlap_mode, + is_of_trait, ) .unwrap(); Some(overlap) } } -fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ImplHeader<'tcx> { +fn fresh_impl_header<'tcx>( + infcx: &InferCtxt<'tcx>, + impl_def_id: DefId, + is_of_trait: bool, +) -> ImplHeader<'tcx> { let tcx = infcx.tcx; let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); @@ -172,7 +209,8 @@ fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ImplH impl_def_id, impl_args, self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args), - trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)), + trait_ref: is_of_trait + .then(|| tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args)), predicates: tcx .predicates_of(impl_def_id) .instantiate(tcx, impl_args) @@ -186,8 +224,9 @@ fn fresh_impl_header_normalized<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, + is_of_trait: bool, ) -> ImplHeader<'tcx> { - let header = fresh_impl_header(infcx, impl_def_id); + let header = fresh_impl_header(infcx, impl_def_id, is_of_trait); let InferOk { value: mut header, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(header); @@ -206,10 +245,16 @@ fn overlap<'tcx>( impl1_def_id: DefId, impl2_def_id: DefId, overlap_mode: OverlapMode, + is_of_trait: bool, ) -> Option> { if overlap_mode.use_negative_impl() { - if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id) - || impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id) + if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id, is_of_trait) + || impl_intersection_has_negative_obligation( + tcx, + impl2_def_id, + impl1_def_id, + is_of_trait, + ) { return None; } @@ -231,8 +276,10 @@ fn overlap<'tcx>( // empty environment. let param_env = ty::ParamEnv::empty(); - let impl1_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id); - let impl2_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id); + let impl1_header = + fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id, is_of_trait); + let impl2_header = + fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id, is_of_trait); // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). @@ -446,6 +493,7 @@ fn impl_intersection_has_negative_obligation( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, + is_of_trait: bool, ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); @@ -455,11 +503,11 @@ fn impl_intersection_has_negative_obligation( let root_universe = infcx.universe(); assert_eq!(root_universe, ty::UniverseIndex::ROOT); - let impl1_header = fresh_impl_header(infcx, impl1_def_id); + let impl1_header = fresh_impl_header(infcx, impl1_def_id, is_of_trait); let param_env = ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args); - let impl2_header = fresh_impl_header(infcx, impl2_def_id); + let impl2_header = fresh_impl_header(infcx, impl2_def_id, is_of_trait); // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 6fefac436994d..e2414c32529af 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -42,7 +42,8 @@ use tracing::{debug, instrument}; pub use self::coherence::{ InCrate, IsFirstInputType, OrphanCheckErr, OrphanCheckMode, OverlapResult, UncoveredTyParams, - add_placeholder_note, orphan_check_trait_ref, overlapping_impls, + add_placeholder_note, orphan_check_trait_ref, overlapping_inherent_impls, + overlapping_trait_impls, }; pub use self::dyn_compatibility::{ DynCompatibilityViolation, dyn_compatibility_violations_for_assoc_item, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 4d0465777dd4d..12ba12be8838f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -113,7 +113,7 @@ impl<'tcx> Children { // Found overlap, but no specialization; error out or report future-compat warning. // Do we *still* get overlap if we disable the future-incompatible modes? - let should_err = traits::overlapping_impls( + let should_err = traits::overlapping_trait_impls( tcx, possible_sibling, impl_def_id, @@ -137,7 +137,7 @@ impl<'tcx> Children { }; let last_lint_mut = &mut last_lint; - let (le, ge) = traits::overlapping_impls( + let (le, ge) = traits::overlapping_trait_impls( tcx, possible_sibling, impl_def_id, From cc69b425195156dfa6609ded54814db0b221d4fc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 9 Oct 2025 11:18:49 +1100 Subject: [PATCH 1439/1900] Use `contains` with `task_deps.reads`. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 5e62dab0722c6..eb0b1f1313e1c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -484,7 +484,7 @@ impl DepGraph { // As long as we only have a low number of reads we can avoid doing a hash // insert and potentially allocating/reallocating the hashmap let new_read = if task_deps.reads.len() < EdgesVec::INLINE_CAPACITY { - task_deps.reads.iter().all(|other| *other != dep_node_index) + !task_deps.reads.contains(&dep_node_index) } else { task_deps.read_set.insert(dep_node_index) }; From fae6cdf0dbdd9d9d9710fbc1b0c902578c5e177f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 9 Oct 2025 09:45:42 +0800 Subject: [PATCH 1440/1900] Allow generic_param_list for `static` items --- .../crates/parser/src/grammar/items/consts.rs | 21 +++++---- .../parser/inline/err/generic_static.rast | 45 +++++++------------ 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs index 6b53493c9a36d..e6a8aca5861a6 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs @@ -25,18 +25,17 @@ fn const_or_static(p: &mut Parser<'_>, m: Marker, is_const: bool) { } // FIXME: Recover on statics with generic params/where clause. - if is_const { - // test generic_const - // const C: u32 = 0; - // impl Foo { - // const C<'a>: &'a () = &(); - // } - generic_params::opt_generic_param_list(p); - } else if p.at(T![<]) { + if !is_const && p.at(T![<]) { + // test_err generic_static + // static C: u32 = 0; p.error("`static` may not have generic parameters"); } - // test_err generic_static - // static C: u32 = 0; + // test generic_const + // const C: u32 = 0; + // impl Foo { + // const C<'a>: &'a () = &(); + // } + generic_params::opt_generic_param_list(p); if p.at(T![:]) { types::ascription(p); @@ -44,7 +43,7 @@ fn const_or_static(p: &mut Parser<'_>, m: Marker, is_const: bool) { // test_err missing_const_type // const C = 0; p.error("missing type for `const`"); - } else if !p.at(T![<]) { + } else { // test_err missing_static_type // static C = 0; p.error("missing type for `static`"); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast index 0c240b7214f43..08017cb0fcd91 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_static.rast @@ -4,39 +4,24 @@ SOURCE_FILE WHITESPACE " " NAME IDENT "C" - ERROR - L_ANGLE "<" - ERROR - PATH - PATH_SEGMENT - NAME_REF + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME IDENT "i32" - ERROR - R_ANGLE ">" - ERROR + R_ANGLE ">" COLON ":" - WHITESPACE " " - ERROR - PATH - PATH_SEGMENT - NAME_REF - IDENT "u32" - WHITESPACE " " - ERROR + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u32" + WHITESPACE " " EQ "=" - WHITESPACE " " - ERROR - INT_NUMBER "0" - ERROR + WHITESPACE " " + LITERAL + INT_NUMBER "0" SEMICOLON ";" WHITESPACE "\n" error 8: `static` may not have generic parameters -error 8: expected SEMICOLON -error 8: expected an item -error 12: expected an item -error 12: expected an item -error 13: expected an item -error 18: expected an item -error 19: expected an item -error 21: expected an item -error 22: expected an item From 38d7e8b502bfe23cc83b261ddd713c82eac75b10 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 9 Oct 2025 13:22:43 +1100 Subject: [PATCH 1441/1900] Rename a badly-named variable. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index eb0b1f1313e1c..32d66a87c7f5b 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -390,9 +390,9 @@ impl DepGraphData { let task_deps = Lock::new(TaskDeps::default()); let result = D::with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); - let task_deps = task_deps.reads; + let reads = task_deps.reads; - let dep_node_index = match task_deps.len() { + let dep_node_index = match reads.len() { 0 => { // Because the dep-node id of anon nodes is computed from the sets of its // dependencies we already know what the ID of this dependency-less node is @@ -403,7 +403,7 @@ impl DepGraphData { } 1 => { // When there is only one dependency, don't bother creating a node. - task_deps[0] + reads[0] } _ => { // The dep node indices are hashed here instead of hashing the dep nodes of the @@ -412,7 +412,7 @@ impl DepGraphData { // combining it with the per session random number `anon_id_seed`. This hash only need // to map the dependencies to a single value on a per session basis. let mut hasher = StableHasher::new(); - task_deps.hash(&mut hasher); + reads.hash(&mut hasher); let target_dep_node = DepNode { kind: dep_kind, @@ -430,7 +430,7 @@ impl DepGraphData { // memory impact of this `anon_node_to_index` map remains tolerable, and helps // us avoid useless growth of the graph with almost-equivalent nodes. self.current.anon_node_to_index.get_or_insert_with(target_dep_node, || { - self.current.alloc_new_node(target_dep_node, task_deps, Fingerprint::ZERO) + self.current.alloc_new_node(target_dep_node, reads, Fingerprint::ZERO) }) } }; From 722545427f46d670228c345a03ab9c0c9cf74241 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 8 Oct 2025 16:39:53 +0800 Subject: [PATCH 1442/1900] Implement fs api set_times and set_times_nofollow --- library/std/src/fs.rs | 74 +++++++++ library/std/src/fs/tests.rs | 219 ++++++++++++++++++++++++++ library/std/src/sys/fs/hermit.rs | 8 + library/std/src/sys/fs/mod.rs | 8 + library/std/src/sys/fs/solid.rs | 11 ++ library/std/src/sys/fs/uefi.rs | 8 + library/std/src/sys/fs/unix.rs | 127 +++++++++++++++ library/std/src/sys/fs/unsupported.rs | 8 + library/std/src/sys/fs/vexos.rs | 8 + library/std/src/sys/fs/wasi.rs | 12 ++ library/std/src/sys/fs/windows.rs | 17 ++ 11 files changed, 500 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 28b2c7173d321..e97190e69d68f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -387,6 +387,80 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result inner(path.as_ref(), contents.as_ref()) } +/// Changes the timestamps of the file or directory at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. If the path refers to a symbolic link, this function +/// will follow the link and change the timestamps of the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function on Unix platforms +/// and the `SetFileTime` function on Windows. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times("foo.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times(path.as_ref(), times.0) +} + +/// Changes the timestamps of the file or symlink at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. Differ from `set_times`, if the path refers to a symbolic link, +/// this function will change the timestamps of the symlink itself, not the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` +/// on Unix platforms and the `SetFileTime` function on Windows after opening the symlink. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times_nofollow("symlink.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +pub fn set_times_nofollow>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times_nofollow(path.as_ref(), times.0) +} + #[stable(feature = "file_lock", since = "1.89.0")] impl error::Error for TryLockError {} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index f8dfb0d633400..4d67ba9248998 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2226,3 +2226,222 @@ fn test_open_options_invalid_combinations() { assert_eq!(err.kind(), ErrorKind::InvalidInput); assert_eq!(err.to_string(), "must specify at least one of read, write, or append access"); } + +#[test] +fn test_fs_set_times() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let path = tmp.join("foo"); + File::create(&path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting file times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + +#[test] +fn test_fs_set_times_follows_symlink() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + + // Create a target file + let target = tmp.join("target"); + File::create(&target).unwrap(); + + // Create a symlink to the target + #[cfg(unix)] + let link = tmp.join("link"); + #[cfg(unix)] + crate::os::unix::fs::symlink(&target, &link).unwrap(); + + #[cfg(windows)] + let link = tmp.join("link.txt"); + #[cfg(windows)] + crate::os::windows::fs::symlink_file(&target, &link).unwrap(); + + // Get the symlink's own modified time BEFORE calling set_times (to compare later) + // We don't check accessed time because reading metadata may update atime on some platforms. + let link_metadata_before = fs::symlink_metadata(&link).unwrap(); + let link_modified_before = link_metadata_before.modified().unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + // Call fs::set_times on the symlink - it should follow the link and modify the target + match fs::set_times(&link, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting file times through symlink: {e:?}"), + Ok(_) => {} + } + + // Verify that the TARGET file's times were changed (following the symlink) + let target_metadata = fs::metadata(&target).unwrap(); + assert_eq!( + target_metadata.accessed().unwrap(), + accessed, + "target file accessed time should match" + ); + assert_eq!( + target_metadata.modified().unwrap(), + modified, + "target file modified time should match" + ); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!( + target_metadata.created().unwrap(), + created, + "target file created time should match" + ); + } + + // Also verify through the symlink (fs::metadata follows symlinks) + let link_followed_metadata = fs::metadata(&link).unwrap(); + assert_eq!(link_followed_metadata.accessed().unwrap(), accessed); + assert_eq!(link_followed_metadata.modified().unwrap(), modified); + + // Verify that the SYMLINK ITSELF was NOT modified + // Note: We only check modified time, not accessed time, because reading the symlink + // metadata may update its atime on some platforms (e.g., Linux). + let link_metadata_after = fs::symlink_metadata(&link).unwrap(); + assert_eq!( + link_metadata_after.modified().unwrap(), + link_modified_before, + "symlink's own modified time should not change" + ); +} + +#[test] +fn test_fs_set_times_nofollow() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + + // Create a target file and a symlink to it + let target = tmp.join("target"); + File::create(&target).unwrap(); + + #[cfg(unix)] + let link = tmp.join("link"); + #[cfg(unix)] + crate::os::unix::fs::symlink(&target, &link).unwrap(); + + #[cfg(windows)] + let link = tmp.join("link.txt"); + #[cfg(windows)] + crate::os::windows::fs::symlink_file(&target, &link).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(11111); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(22222); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(33333); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + // Set times on the symlink itself (not following it) + match fs::set_times_nofollow(&link, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting symlink times: {e:?}"), + Ok(_) => {} + } + + // Read symlink metadata (without following) + let metadata = fs::symlink_metadata(&link).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } + + // Verify that the target file's times were NOT changed + let target_metadata = fs::metadata(&target).unwrap(); + assert_ne!(target_metadata.accessed().unwrap(), accessed); + assert_ne!(target_metadata.modified().unwrap(), modified); +} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 175d919c289dd..21235bcfbd8c5 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -566,6 +566,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) +} + pub fn rmdir(path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) } diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 64f5a6b36d3db..b498f9cb7ea72 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -161,3 +161,11 @@ pub fn exists(path: &Path) -> io::Result { #[cfg(windows)] with_native_path(path, &imp::exists) } + +pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times(path, times.clone())) +} + +pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone())) +} diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 808a95829114e..39bd9b3cdd70b 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -538,6 +538,17 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { Ok(()) } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported",)) +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times on symlinks not supported", + )) +} + pub fn rmdir(p: &Path) -> io::Result<()> { if stat(p)?.file_type().is_dir() { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 5763d7862f5ae..e4e7274ae8cb3 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -333,6 +333,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + pub fn rmdir(_p: &Path) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bed9ea9139834..578b5f4a1d986 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1195,6 +1195,55 @@ impl fmt::Debug for OpenOptions { } } +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nuttx", +)))] +fn to_timespec(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + } +} + +#[cfg(target_vendor = "apple")] +fn set_attrlist_with_times( + times: &FileTimes, +) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { + let mut buf = [mem::MaybeUninit::::uninit(); 3]; + let mut num_times = 0; + let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; + attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + + if times.created.is_some() { + buf[num_times].write(to_timespec(times.created)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + buf[num_times].write(to_timespec(times.modified)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + buf[num_times].write(to_timespec(times.accessed)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + + Ok((attrlist, buf, num_times)) +} + impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -2112,6 +2161,84 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> Ok((reader, metadata)) } +fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { + cfg_select! { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { + let _ = (p, times, flags); + Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times not supported", + )) + } + target_vendor = "apple" => { + // Apple platforms use setattrlist which supports setting times on symlinks + let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + let options = if flags == libc::AT_SYMLINK_NOFOLLOW { + libc::FSOPT_NOFOLLOW + } else { + 0 + }; + + cvt(unsafe { libc::setattrlist( + p.as_ptr(), + (&raw const attrlist).cast::().cast_mut(), + buf.as_ptr().cast::().cast_mut(), + num_times * size_of::(), + options as u32 + ) })?; + Ok(()) + } + target_os = "android" => { + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + // utimensat requires Android API level 19 + cvt(unsafe { + weak!( + fn utimensat(dirfd: c_int, path: *const c_char, times: *const libc::timespec, flags: c_int) -> c_int; + ); + match utimensat.get() { + Some(utimensat) => utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags), + None => return Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } + })?; + Ok(()) + } + _ => { + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] + { + use crate::sys::{time::__timespec64, weak::weak}; + + // Added in glibc 2.34 + weak!( + fn __utimensat64(dirfd: c_int, path: *const c_char, times: *const __timespec64, flags: c_int) -> c_int; + ); + + if let Some(utimensat64) = __utimensat64.get() { + let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) + .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); + let times = [to_timespec(times.accessed), to_timespec(times.modified)]; + cvt(unsafe { utimensat64(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; + return Ok(()); + } + } + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + cvt(unsafe { libc::utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; + Ok(()) + } + } +} + +pub fn set_times(p: &CStr, times: FileTimes) -> io::Result<()> { + // flags = 0 means follow symlinks + set_times_impl(p, times, 0) +} + +pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { + set_times_impl(p, times, libc::AT_SYMLINK_NOFOLLOW) +} + #[cfg(target_os = "espidf")] fn open_to_and_set_permissions( to: &Path, diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index efaddb51b3751..659ea2a8fc276 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -312,6 +312,14 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } +pub fn set_times(_p: &Path, times: FileTimes) -> io::Result<()> { + match times {} +} + +pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { + match times {} +} + pub fn rmdir(_p: &Path) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index f642e7cb074ec..99b156d535768 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -492,6 +492,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + pub fn exists(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| Ok(unsafe { vex_sdk::vexFileStatus(path.as_ptr()) } != 0)) } diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 0b65b9cb389df..1e6c0fad5b830 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -643,6 +643,18 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + // File times haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + // File times haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + pub fn rmdir(p: &Path) -> io::Result<()> { let (dir, file) = open_parent(p)?; dir.remove_directory(osstr2str(file.as_ref())?) diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index ccfe410627f70..f2d325da35c7d 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1514,6 +1514,23 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { } } +pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.write(true); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open_native(p, &opts)?; + file.set_times(times) +} + +pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.write(true); + // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(p, &opts)?; + file.set_times(times) +} + fn get_path(f: &File) -> io::Result { fill_utf16_buf( |buf, sz| unsafe { From 3d40fa69587c60d8ba7bcf3d2a39e503a5ded07c Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 10:20:48 +0800 Subject: [PATCH 1443/1900] Update library/std/src/fs.rs Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e97190e69d68f..f374f4155cfd2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -432,8 +432,9 @@ pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` -/// on Unix platforms and the `SetFileTime` function on Windows after opening the symlink. +/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` on +/// Unix platforms, the `setattrlist` function with `FSOPT_NOFOLLOW` on Apple platforms, and the +/// `SetFileTime` function on Windows. /// /// # Errors /// From 6308e76cd7d9d6b4b3912c875eabd3eb695afc5d Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 10:21:06 +0800 Subject: [PATCH 1444/1900] Update library/std/src/fs.rs Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index f374f4155cfd2..60eefc36ae2be 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -395,8 +395,8 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `utimensat` function on Unix platforms -/// and the `SetFileTime` function on Windows. +/// This function currently corresponds to the `utimensat` function on Unix platforms, the +/// `setattrlist` function on Apple platforms, and the `SetFileTime` function on Windows. /// /// # Errors /// From 43e813c559b6db0eb1517bcf49a0c13b4f24729e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 9 Oct 2025 13:28:34 +1100 Subject: [PATCH 1445/1900] Replace `TaskDeps::default()` with `TaskDeps::new()`. There are only two places that create a `TaskDeps`. One constructs it manually, the other uses `default`. It's weird that `default()` uses a capacity of 128. This commit just gets rid of `default` and introduces `new` so that both construction sites can be equivalent. --- .../rustc_query_system/src/dep_graph/graph.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 32d66a87c7f5b..4f12380ca786e 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -349,13 +349,11 @@ impl DepGraphData { let (result, edges) = if cx.dep_context().is_eval_always(key.kind) { (with_deps(TaskDepsRef::EvalAlways), EdgesVec::new()) } else { - let task_deps = Lock::new(TaskDeps { + let task_deps = Lock::new(TaskDeps::new( #[cfg(debug_assertions)] - node: Some(key), - reads: EdgesVec::new(), - read_set: Default::default(), - phantom_data: PhantomData, - }); + Some(key), + 0, + )); (with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads) }; @@ -387,7 +385,11 @@ impl DepGraphData { { debug_assert!(!cx.is_eval_always(dep_kind)); - let task_deps = Lock::new(TaskDeps::default()); + let task_deps = Lock::new(TaskDeps::new( + #[cfg(debug_assertions)] + None, + 128, + )); let result = D::with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); let reads = task_deps.reads; @@ -1307,17 +1309,19 @@ pub struct TaskDeps { phantom_data: PhantomData, } -impl Default for TaskDeps { - fn default() -> Self { - Self { +impl TaskDeps { + #[inline] + fn new(#[cfg(debug_assertions)] node: Option, read_set_capacity: usize) -> Self { + TaskDeps { #[cfg(debug_assertions)] - node: None, + node, reads: EdgesVec::new(), - read_set: FxHashSet::with_capacity_and_hasher(128, Default::default()), + read_set: FxHashSet::with_capacity_and_hasher(read_set_capacity, Default::default()), phantom_data: PhantomData, } } } + // A data structure that stores Option values as a contiguous // array, using one u32 per entry. pub(super) struct DepNodeColorMap { From 8f0815602c193ca55f4ca39c86dbb1240d6ee8f1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 9 Oct 2025 13:11:22 +1100 Subject: [PATCH 1446/1900] compiletest: Isolate APIs used by rustdoc-gui-test --- Cargo.lock | 1 - src/tools/compiletest/src/common.rs | 104 ------------- src/tools/compiletest/src/lib.rs | 1 + src/tools/compiletest/src/rustdoc_gui_test.rs | 143 ++++++++++++++++++ src/tools/rustdoc-gui-test/Cargo.toml | 1 - src/tools/rustdoc-gui-test/src/main.rs | 10 +- 6 files changed, 146 insertions(+), 114 deletions(-) create mode 100644 src/tools/compiletest/src/rustdoc_gui_test.rs diff --git a/Cargo.lock b/Cargo.lock index e67898756fad5..973214a802e6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4805,7 +4805,6 @@ name = "rustdoc-gui-test" version = "0.1.0" dependencies = [ "build_helper", - "camino", "compiletest", "getopts", "walkdir", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 65db816ad1a87..7a75803c78277 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -686,110 +686,6 @@ pub struct Config { } impl Config { - /// Incomplete config intended for `src/tools/rustdoc-gui-test` **only** as - /// `src/tools/rustdoc-gui-test` wants to reuse `compiletest`'s directive -> test property - /// handling for `//@ {compile,run}-flags`, do not use for any other purpose. - /// - /// FIXME(#143827): this setup feels very hacky. It so happens that `tests/rustdoc-gui/` - /// **only** uses `//@ {compile,run}-flags` for now and not any directives that actually rely on - /// info that is assumed available in a fully populated [`Config`]. - pub fn incomplete_for_rustdoc_gui_test() -> Config { - // FIXME(#143827): spelling this out intentionally, because this is questionable. - // - // For instance, `//@ ignore-stage1` will not work at all. - Config { - mode: TestMode::Rustdoc, - // E.g. this has no sensible default tbh. - suite: TestSuite::Ui, - - // Dummy values. - edition: Default::default(), - bless: Default::default(), - fail_fast: Default::default(), - compile_lib_path: Utf8PathBuf::default(), - run_lib_path: Utf8PathBuf::default(), - rustc_path: Utf8PathBuf::default(), - cargo_path: Default::default(), - stage0_rustc_path: Default::default(), - query_rustc_path: Default::default(), - rustdoc_path: Default::default(), - coverage_dump_path: Default::default(), - python: Default::default(), - jsondocck_path: Default::default(), - jsondoclint_path: Default::default(), - llvm_filecheck: Default::default(), - llvm_bin_dir: Default::default(), - run_clang_based_tests_with: Default::default(), - src_root: Utf8PathBuf::default(), - src_test_suite_root: Utf8PathBuf::default(), - build_root: Utf8PathBuf::default(), - build_test_suite_root: Utf8PathBuf::default(), - sysroot_base: Utf8PathBuf::default(), - stage: Default::default(), - stage_id: String::default(), - debugger: Default::default(), - run_ignored: Default::default(), - with_rustc_debug_assertions: Default::default(), - with_std_debug_assertions: Default::default(), - filters: Default::default(), - skip: Default::default(), - filter_exact: Default::default(), - force_pass_mode: Default::default(), - run: Default::default(), - runner: Default::default(), - host_rustcflags: Default::default(), - target_rustcflags: Default::default(), - rust_randomized_layout: Default::default(), - optimize_tests: Default::default(), - target: Default::default(), - host: Default::default(), - cdb: Default::default(), - cdb_version: Default::default(), - gdb: Default::default(), - gdb_version: Default::default(), - lldb_version: Default::default(), - llvm_version: Default::default(), - system_llvm: Default::default(), - android_cross_path: Default::default(), - adb_path: Default::default(), - adb_test_dir: Default::default(), - adb_device_status: Default::default(), - lldb_python_dir: Default::default(), - verbose: Default::default(), - color: Default::default(), - remote_test_client: Default::default(), - compare_mode: Default::default(), - rustfix_coverage: Default::default(), - has_html_tidy: Default::default(), - has_enzyme: Default::default(), - channel: Default::default(), - git_hash: Default::default(), - cc: Default::default(), - cxx: Default::default(), - cflags: Default::default(), - cxxflags: Default::default(), - ar: Default::default(), - target_linker: Default::default(), - host_linker: Default::default(), - llvm_components: Default::default(), - nodejs: Default::default(), - npm: Default::default(), - force_rerun: Default::default(), - only_modified: Default::default(), - target_cfgs: Default::default(), - builtin_cfg_names: Default::default(), - supported_crate_types: Default::default(), - nocapture: Default::default(), - nightly_branch: Default::default(), - git_merge_commit_email: Default::default(), - profiler_runtime: Default::default(), - diff_command: Default::default(), - minicore_path: Default::default(), - default_codegen_backend: CodegenBackend::Llvm, - override_codegen_backend: None, - } - } - /// FIXME: this run scheme is... confusing. pub fn run_enabled(&self) -> bool { self.run.unwrap_or_else(|| { diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index ac1a8226112a9..1a0b117e4b297 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -16,6 +16,7 @@ mod panic_hook; mod raise_fd_limit; mod read2; pub mod runtest; +pub mod rustdoc_gui_test; pub mod util; use core::panic; diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs new file mode 100644 index 0000000000000..fcb8327a9245c --- /dev/null +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -0,0 +1,143 @@ +//! This module isolates the compiletest APIs used by the rustdoc-gui-test tool. +//! +//! Thanks to this isolation layer, changes to compiletest directive parsing +//! might require changes to the items in this module, but shouldn't require +//! changes to rustdoc-gui-test itself. +//! +//! The current relationship between compiletest and rustdoc-gui-test is +//! awkward. Ideally, rustdoc-gui-test should either split off its own +//! directive parser and become fully independent, or be incorporated into +//! compiletest as another test mode. +//! +//! See for more context. + +use std::path::Path; + +use camino::{Utf8Path, Utf8PathBuf}; + +use crate::common::{CodegenBackend, Config, TestMode, TestSuite}; +use crate::directives::TestProps; + +/// Subset of compiletest directive values that are actually used by +/// rustdoc-gui-test. +#[derive(Debug)] +pub struct RustdocGuiTestProps { + pub compile_flags: Vec, + pub run_flags: Vec, +} + +impl RustdocGuiTestProps { + pub fn from_file(test_file_path: &Path) -> Self { + let test_file_path = Utf8Path::from_path(test_file_path).unwrap(); + let config = incomplete_config_for_rustdoc_gui_test(); + + let props = TestProps::from_file(test_file_path, None, &config); + + let TestProps { compile_flags, run_flags, .. } = props; + Self { compile_flags, run_flags } + } +} + +/// Incomplete config intended for `src/tools/rustdoc-gui-test` **only** as +/// `src/tools/rustdoc-gui-test` wants to reuse `compiletest`'s directive -> test property +/// handling for `//@ {compile,run}-flags`, do not use for any other purpose. +/// +/// FIXME(#143827): this setup feels very hacky. It so happens that `tests/rustdoc-gui/` +/// **only** uses `//@ {compile,run}-flags` for now and not any directives that actually rely on +/// info that is assumed available in a fully populated [`Config`]. +fn incomplete_config_for_rustdoc_gui_test() -> Config { + // FIXME(#143827): spelling this out intentionally, because this is questionable. + // + // For instance, `//@ ignore-stage1` will not work at all. + Config { + mode: TestMode::Rustdoc, + // E.g. this has no sensible default tbh. + suite: TestSuite::Ui, + + // Dummy values. + edition: Default::default(), + bless: Default::default(), + fail_fast: Default::default(), + compile_lib_path: Utf8PathBuf::default(), + run_lib_path: Utf8PathBuf::default(), + rustc_path: Utf8PathBuf::default(), + cargo_path: Default::default(), + stage0_rustc_path: Default::default(), + query_rustc_path: Default::default(), + rustdoc_path: Default::default(), + coverage_dump_path: Default::default(), + python: Default::default(), + jsondocck_path: Default::default(), + jsondoclint_path: Default::default(), + llvm_filecheck: Default::default(), + llvm_bin_dir: Default::default(), + run_clang_based_tests_with: Default::default(), + src_root: Utf8PathBuf::default(), + src_test_suite_root: Utf8PathBuf::default(), + build_root: Utf8PathBuf::default(), + build_test_suite_root: Utf8PathBuf::default(), + sysroot_base: Utf8PathBuf::default(), + stage: Default::default(), + stage_id: String::default(), + debugger: Default::default(), + run_ignored: Default::default(), + with_rustc_debug_assertions: Default::default(), + with_std_debug_assertions: Default::default(), + filters: Default::default(), + skip: Default::default(), + filter_exact: Default::default(), + force_pass_mode: Default::default(), + run: Default::default(), + runner: Default::default(), + host_rustcflags: Default::default(), + target_rustcflags: Default::default(), + rust_randomized_layout: Default::default(), + optimize_tests: Default::default(), + target: Default::default(), + host: Default::default(), + cdb: Default::default(), + cdb_version: Default::default(), + gdb: Default::default(), + gdb_version: Default::default(), + lldb_version: Default::default(), + llvm_version: Default::default(), + system_llvm: Default::default(), + android_cross_path: Default::default(), + adb_path: Default::default(), + adb_test_dir: Default::default(), + adb_device_status: Default::default(), + lldb_python_dir: Default::default(), + verbose: Default::default(), + color: Default::default(), + remote_test_client: Default::default(), + compare_mode: Default::default(), + rustfix_coverage: Default::default(), + has_html_tidy: Default::default(), + has_enzyme: Default::default(), + channel: Default::default(), + git_hash: Default::default(), + cc: Default::default(), + cxx: Default::default(), + cflags: Default::default(), + cxxflags: Default::default(), + ar: Default::default(), + target_linker: Default::default(), + host_linker: Default::default(), + llvm_components: Default::default(), + nodejs: Default::default(), + npm: Default::default(), + force_rerun: Default::default(), + only_modified: Default::default(), + target_cfgs: Default::default(), + builtin_cfg_names: Default::default(), + supported_crate_types: Default::default(), + nocapture: Default::default(), + nightly_branch: Default::default(), + git_merge_commit_email: Default::default(), + profiler_runtime: Default::default(), + diff_command: Default::default(), + minicore_path: Default::default(), + default_codegen_backend: CodegenBackend::Llvm, + override_codegen_backend: None, + } +} diff --git a/src/tools/rustdoc-gui-test/Cargo.toml b/src/tools/rustdoc-gui-test/Cargo.toml index 8d958ac94f30e..f7384a98f8565 100644 --- a/src/tools/rustdoc-gui-test/Cargo.toml +++ b/src/tools/rustdoc-gui-test/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] build_helper = { path = "../../build_helper" } -camino = "1" compiletest = { path = "../compiletest" } getopts = "0.2" walkdir = "2" diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs index 5062c2597f15e..cd10882fc2269 100644 --- a/src/tools/rustdoc-gui-test/src/main.rs +++ b/src/tools/rustdoc-gui-test/src/main.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use build_helper::npm; use build_helper::util::try_run; -use compiletest::directives::TestProps; +use compiletest::rustdoc_gui_test::RustdocGuiTestProps; use config::Config; mod config; @@ -43,13 +43,7 @@ fn main() -> Result<(), ()> { .current_dir(path); if let Some(librs) = find_librs(entry.path()) { - let compiletest_c = compiletest::common::Config::incomplete_for_rustdoc_gui_test(); - - let test_props = TestProps::from_file( - &camino::Utf8PathBuf::try_from(librs).unwrap(), - None, - &compiletest_c, - ); + let test_props = RustdocGuiTestProps::from_file(&librs); if !test_props.compile_flags.is_empty() { cargo.env("RUSTDOCFLAGS", test_props.compile_flags.join(" ")); From b4f64fda4fcbb021f5a85a456b4b3c3560113e86 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 9 Oct 2025 13:52:01 +1100 Subject: [PATCH 1447/1900] compiletest: Isolate APIs used by `bin/main.rs` --- src/tools/compiletest/src/bin/main.rs | 22 +--------------------- src/tools/compiletest/src/cli.rs | 26 ++++++++++++++++++++++++++ src/tools/compiletest/src/lib.rs | 1 + 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 src/tools/compiletest/src/cli.rs diff --git a/src/tools/compiletest/src/bin/main.rs b/src/tools/compiletest/src/bin/main.rs index 8fac6ccdc582b..3e608d57e81a7 100644 --- a/src/tools/compiletest/src/bin/main.rs +++ b/src/tools/compiletest/src/bin/main.rs @@ -1,23 +1,3 @@ -use std::env; -use std::io::IsTerminal; -use std::sync::Arc; - -use compiletest::{early_config_check, parse_config, run_tests}; - fn main() { - tracing_subscriber::fmt::init(); - - // colored checks stdout by default, but for some reason only stderr is a terminal. - // compiletest *does* print many things to stdout, but it doesn't really matter. - if std::io::stderr().is_terminal() - && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0")) - { - colored::control::set_override(true); - } - - let config = Arc::new(parse_config(env::args().collect())); - - early_config_check(&config); - - run_tests(config); + compiletest::cli::main(); } diff --git a/src/tools/compiletest/src/cli.rs b/src/tools/compiletest/src/cli.rs new file mode 100644 index 0000000000000..763c46550b411 --- /dev/null +++ b/src/tools/compiletest/src/cli.rs @@ -0,0 +1,26 @@ +//! Isolates the APIs used by `bin/main.rs`, to help minimize the surface area +//! of public exports from the compiletest library crate. + +use std::env; +use std::io::IsTerminal; +use std::sync::Arc; + +use crate::{early_config_check, parse_config, run_tests}; + +pub fn main() { + tracing_subscriber::fmt::init(); + + // colored checks stdout by default, but for some reason only stderr is a terminal. + // compiletest *does* print many things to stdout, but it doesn't really matter. + if std::io::stderr().is_terminal() + && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0")) + { + colored::control::set_override(true); + } + + let config = Arc::new(parse_config(env::args().collect())); + + early_config_check(&config); + + run_tests(config); +} diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 1a0b117e4b297..c8f844c60016c 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -3,6 +3,7 @@ #[cfg(test)] mod tests; +pub mod cli; pub mod common; mod debuggers; pub mod diagnostics; From ce4699deffc6cc00169b130d2acd541696db2299 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 9 Oct 2025 14:19:58 +1100 Subject: [PATCH 1448/1900] compiletest: Make all other modules non-public All APIs used from outside the compiletest library crate have been isolated to the `cli` and `rustdoc_gui_test` modules, so no other items need to be publicly exported. --- src/bootstrap/src/core/build_steps/test.rs | 3 -- src/tools/compiletest/src/common.rs | 5 ++- src/tools/compiletest/src/directives.rs | 3 -- src/tools/compiletest/src/lib.rs | 34 +++++++------------ src/tools/compiletest/src/rustdoc_gui_test.rs | 1 - src/tools/compiletest/src/util.rs | 2 ++ 6 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 00aea8feab7de..154b209b20252 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1974,9 +1974,6 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} } else if mode == "rustdoc-js" { panic!("need nodejs to run rustdoc-js suite"); } - if let Some(ref npm) = builder.config.npm { - cmd.arg("--npm").arg(npm); - } if builder.config.rust_optimize_tests { cmd.arg("--optimize-tests"); } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 7a75803c78277..58330ed20dc88 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -631,8 +631,6 @@ pub struct Config { /// Path to a NodeJS executable. Used for JS doctests, emscripten and WASM tests. pub nodejs: Option, - /// Path to a npm executable. Used for rustdoc GUI tests. - pub npm: Option, /// Whether to rerun tests even if the inputs are unchanged. pub force_rerun: bool, @@ -721,7 +719,8 @@ impl Config { self.target_cfg().abi == abi } - pub fn matches_family(&self, family: &str) -> bool { + #[cfg_attr(not(test), expect(dead_code, reason = "only used by tests for `ignore-{family}`"))] + pub(crate) fn matches_family(&self, family: &str) -> bool { self.target_cfg().families.iter().any(|f| f == family) } diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 34ecbc2d59f10..2d45495d4f8ae 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -198,8 +198,6 @@ pub struct TestProps { pub filecheck_flags: Vec, /// Don't automatically insert any `--check-cfg` args pub no_auto_check_cfg: bool, - /// Run tests which require enzyme being build - pub has_enzyme: bool, /// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios /// that don't otherwise want/need `-Z build-std`. pub add_core_stubs: bool, @@ -314,7 +312,6 @@ impl TestProps { llvm_cov_flags: vec![], filecheck_flags: vec![], no_auto_check_cfg: false, - has_enzyme: false, add_core_stubs: false, core_stubs_compile_flags: vec![], dont_require_annotations: Default::default(), diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index c8f844c60016c..48ae38b61b93d 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -4,21 +4,21 @@ mod tests; pub mod cli; -pub mod common; +mod common; mod debuggers; -pub mod diagnostics; -pub mod directives; -pub mod edition; -pub mod errors; +mod diagnostics; +mod directives; +mod edition; +mod errors; mod executor; mod json; mod output_capture; mod panic_hook; mod raise_fd_limit; mod read2; -pub mod runtest; +mod runtest; pub mod rustdoc_gui_test; -pub mod util; +mod util; use core::panic; use std::collections::HashSet; @@ -50,7 +50,7 @@ use crate::executor::{CollectedTest, ColorConfig}; /// The config mostly reflects command-line arguments, but there might also be /// some code here that inspects environment variables or even runs executables /// (e.g. when discovering debugger versions). -pub fn parse_config(args: Vec) -> Config { +fn parse_config(args: Vec) -> Config { let mut opts = Options::new(); opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") @@ -464,7 +464,6 @@ pub fn parse_config(args: Vec) -> Config { host_linker: matches.opt_str("host-linker"), llvm_components: matches.opt_str("llvm-components").unwrap(), nodejs: matches.opt_str("nodejs"), - npm: matches.opt_str("npm"), force_rerun: matches.opt_present("force-rerun"), @@ -488,14 +487,7 @@ pub fn parse_config(args: Vec) -> Config { } } -pub fn opt_str(maybestr: &Option) -> &str { - match *maybestr { - None => "(none)", - Some(ref s) => s, - } -} - -pub fn opt_str2(maybestr: Option) -> String { +fn opt_str2(maybestr: Option) -> String { match maybestr { None => "(none)".to_owned(), Some(s) => s, @@ -503,7 +495,7 @@ pub fn opt_str2(maybestr: Option) -> String { } /// Called by `main` after the config has been parsed. -pub fn run_tests(config: Arc) { +fn run_tests(config: Arc) { debug!(?config, "run_tests"); panic_hook::install_panic_hook(); @@ -641,7 +633,7 @@ impl TestCollector { /// FIXME(Zalathar): Now that we no longer rely on libtest, try to overhaul /// test discovery to take into account the filters/tests specified on the /// command-line, instead of having to enumerate everything. -pub(crate) fn collect_and_make_tests(config: Arc) -> Vec { +fn collect_and_make_tests(config: Arc) -> Vec { debug!("making tests from {}", config.src_test_suite_root); let common_inputs_stamp = common_inputs_stamp(&config); let modified_tests = @@ -853,7 +845,7 @@ fn collect_tests_from_dir( } /// Returns true if `file_name` looks like a proper test file name. -pub fn is_test(file_name: &str) -> bool { +fn is_test(file_name: &str) -> bool { if !file_name.ends_with(".rs") { return false; } @@ -1133,7 +1125,7 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { } } -pub fn early_config_check(config: &Config) { +fn early_config_check(config: &Config) { if !config.has_html_tidy && config.mode == TestMode::Rustdoc { warning!("`tidy` (html-tidy.org) is not installed; diffs will not be generated"); } diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index fcb8327a9245c..4bd3531f4b433 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -125,7 +125,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { host_linker: Default::default(), llvm_components: Default::default(), nodejs: Default::default(), - npm: Default::default(), force_rerun: Default::default(), only_modified: Default::default(), target_cfgs: Default::default(), diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 558e9a58697e7..a189c1d1c2dfc 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -102,7 +102,9 @@ macro_rules! string_enum { } impl $name { + #[allow(dead_code)] $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*]; + #[allow(dead_code)] $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*]; $vis const fn to_str(&self) -> &'static str { From 1c5c8caad2401c923c1c8c515f004c52c8e74ed8 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 10:23:34 +0800 Subject: [PATCH 1449/1900] use proper unsupported --- library/std/src/sys/fs/solid.rs | 7 ++----- library/std/src/sys/fs/unix.rs | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 39bd9b3cdd70b..f6d5d3b784d3b 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -539,14 +539,11 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { } pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported",)) + unsupported() } pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - Err(io::const_error!( - io::ErrorKind::Unsupported, - "setting file times on symlinks not supported", - )) + unsupported() } pub fn rmdir(p: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 578b5f4a1d986..014b10f06176c 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1201,6 +1201,7 @@ impl fmt::Debug for OpenOptions { target_os = "horizon", target_os = "nuttx", )))] +#[inline(always)] fn to_timespec(time: Option) -> io::Result { match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), @@ -1217,6 +1218,7 @@ fn to_timespec(time: Option) -> io::Result { } #[cfg(target_vendor = "apple")] +#[inline(always)] fn set_attrlist_with_times( times: &FileTimes, ) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { From 46c6f0aadee016c28a0eb3d3fa977f33111e38ab Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 10:49:13 +0800 Subject: [PATCH 1450/1900] rebase #147504 --- library/std/src/sys/fs/unix.rs | 73 ++++++---------------------------- 1 file changed, 13 insertions(+), 60 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 014b10f06176c..63a6db77324a9 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1195,57 +1195,6 @@ impl fmt::Debug for OpenOptions { } } -#[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "nuttx", -)))] -#[inline(always)] -fn to_timespec(time: Option) -> io::Result { - match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - } -} - -#[cfg(target_vendor = "apple")] -#[inline(always)] -fn set_attrlist_with_times( - times: &FileTimes, -) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - - if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } - - Ok((attrlist, buf, num_times)) -} - impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -1760,18 +1709,19 @@ impl TimesAttrlist { if times.created.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.created)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_CRTIME; } if times.modified.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.modified)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_MODTIME; } if times.accessed.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.accessed)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; } + Ok(this) } fn attrlist(&self) -> *mut libc::c_void { @@ -2174,7 +2124,8 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { } target_vendor = "apple" => { // Apple platforms use setattrlist which supports setting times on symlinks - let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + //let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + let ta = TimesAttrlist::from_times(×)?; let options = if flags == libc::AT_SYMLINK_NOFOLLOW { libc::FSOPT_NOFOLLOW } else { @@ -2183,15 +2134,15 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { cvt(unsafe { libc::setattrlist( p.as_ptr(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * size_of::(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), options as u32 ) })?; Ok(()) } target_os = "android" => { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; // utimensat requires Android API level 19 cvt(unsafe { weak!( @@ -2225,18 +2176,20 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { return Ok(()); } } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; cvt(unsafe { libc::utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; Ok(()) } } } +#[inline(always)] pub fn set_times(p: &CStr, times: FileTimes) -> io::Result<()> { // flags = 0 means follow symlinks set_times_impl(p, times, 0) } +#[inline(always)] pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { set_times_impl(p, times, libc::AT_SYMLINK_NOFOLLOW) } From 2438df75fe8a954f54479ce1a79cb9863f05d926 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 11:08:30 +0800 Subject: [PATCH 1451/1900] support fs::set_times for wasi --- library/std/src/sys/fs/wasi.rs | 55 +++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 1e6c0fad5b830..92eb35317415f 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -536,17 +536,9 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option| match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - None => Ok(0), - }; self.fd.filestat_set_times( - to_timestamp(times.accessed)?, - to_timestamp(times.modified)?, + to_wasi_timestamp_or_now(times.accessed)?, + to_wasi_timestamp_or_now(times.modified)?, times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), ) @@ -643,16 +635,43 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } -pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - // File times haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() +#[inline(always)] +pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + set_times_impl(&dir, &file, times, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW) } -pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - // File times haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() +#[inline(always)] +pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + set_times_impl(&dir, &file, times, 0) +} + +fn to_wasi_timestamp_or_now(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + None => Ok(0), + } +} + +fn set_times_impl( + fd: &WasiFd, + path: &Path, + times: FileTimes, + flags: wasi::Lookupflags, +) -> io::Result<()> { + fd.path_filestat_set_times( + flags, + osstr2str(path.as_ref())?, + to_wasi_timestamp_or_now(times.accessed)?, + to_wasi_timestamp_or_now(times.modified)?, + times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) + | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), + ) } pub fn rmdir(p: &Path) -> io::Result<()> { From 1dd5641d74ca6d0fd873e7de9f5a63d12046d870 Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 11:37:48 +0800 Subject: [PATCH 1452/1900] add doc alias for set_times_nofollow Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 60eefc36ae2be..9841a246d6a5d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -458,6 +458,9 @@ pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { /// } /// ``` #[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimensat")] +#[doc(alias = "lutimens")] +#[doc(alias = "lutimes")] pub fn set_times_nofollow>(path: P, times: FileTimes) -> io::Result<()> { fs_imp::set_times_nofollow(path.as_ref(), times.0) } From 6fd1c2bd9456cfa69812c7b35132b6f4ce7569f0 Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 11:38:19 +0800 Subject: [PATCH 1453/1900] add doc alias for set_times Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9841a246d6a5d..b548eb4939d42 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -420,6 +420,9 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result /// } /// ``` #[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimens")] +#[doc(alias = "utimes")] +#[doc(alias = "utime")] pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { fs_imp::set_times(path.as_ref(), times.0) } From cd7736571418aae735adb60d1ca272c750121623 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 9 Oct 2025 11:43:29 +0800 Subject: [PATCH 1454/1900] Fix closure coerced return type for add_return_type Example --- ```rust fn foo() { let f = ||$0 {loop {}}; let _: fn() -> i8 = f; } ``` **Before this PR**: ```rust fn foo() { let f = || -> ! {loop {}}; let _: fn() -> i8 = f; } ``` mismatched types error on line 3 **After this PR**: ```rust fn foo() { let f = || -> i8 {loop {}}; let _: fn() -> i8 = f; } ``` --- .../ide-assists/src/handlers/add_return_type.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs index a7104ce068da8..c9022f66d1e20 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs @@ -18,7 +18,7 @@ use crate::{AssistContext, AssistId, Assists}; pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?; let module = ctx.sema.scope(tail_expr.syntax())?.module(); - let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.original(); + let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.adjusted(); if ty.is_unit() { return None; } @@ -421,6 +421,21 @@ mod tests { ); } + #[test] + fn infer_coerced_return_type_closure() { + check_assist( + add_return_type, + r#"fn foo() { + let f = ||$0 {loop {}}; + let _: fn() -> i8 = f; +}"#, + r#"fn foo() { + let f = || -> i8 {loop {}}; + let _: fn() -> i8 = f; +}"#, + ); + } + #[test] fn not_applicable_ret_type_specified_closure() { cov_mark::check!(existing_ret_type_closure); From c425389f184199372c8b3c6febcbc5202a818253 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 20:29:52 +0200 Subject: [PATCH 1455/1900] Cleanup: do not handle methods from several places Some methods lints were handled in the `methods` module outside the `check_methods()` function. --- clippy_lints/src/methods/clone_on_copy.rs | 22 +++------- clippy_lints/src/methods/clone_on_ref_ptr.rs | 13 +----- clippy_lints/src/methods/expect_fun_call.rs | 17 +++---- .../src/methods/inefficient_to_string.rs | 16 ++----- clippy_lints/src/methods/into_iter_on_ref.rs | 9 +--- clippy_lints/src/methods/mod.rs | 44 ++++++++++--------- clippy_utils/src/sym.rs | 2 + 7 files changed, 42 insertions(+), 81 deletions(-) diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 52ae5b7d01b3a..2a0ae14a4b088 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -4,26 +4,14 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; -use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - method_name: Symbol, - receiver: &Expr<'_>, - args: &[Expr<'_>], -) { - let arg = if method_name == sym::clone && args.is_empty() { - receiver - } else { - return; - }; +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { if cx .typeck_results() .type_dependent_def_id(expr.hir_id) @@ -33,10 +21,10 @@ pub(super) fn check( { return; } - let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); let arg_ty = arg_adjustments .last() - .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + .map_or_else(|| cx.typeck_results().expr_ty(receiver), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() @@ -75,7 +63,7 @@ pub(super) fn check( }; let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + let snip = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "_", &mut app).0; let deref_count = arg_adjustments .iter() diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 65583c6a9811e..059893b05bb9c 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -4,20 +4,11 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - if !(args.is_empty() && method_name == sym::clone) { - return; - } +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if let ty::Adt(adt, subst) = obj_ty.kind() diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 818e26f8aa1df..74920a17310d5 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -7,8 +7,8 @@ use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks} use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::Span; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -20,16 +20,11 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: Symbol, receiver: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], + arg: &'tcx hir::Expr<'tcx>, ) { - if name == sym::expect - && let [arg] = args - && let arg_root = get_arg_root(cx, arg) - && contains_call(cx, arg_root) - && !contains_return(arg_root) - { + let arg_root = get_arg_root(cx, arg); + if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { "||" @@ -54,7 +49,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -69,7 +64,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!(\"{{}}\", {arg_root_snippet}))"), applicability, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ab21515f47f78..1b350b7abb619 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -6,22 +6,12 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::INEFFICIENT_TO_STRING; -/// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], - msrv: Msrv, -) { - if args.is_empty() - && method_name == sym::to_string - && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, msrv: Msrv) { + if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index bedeb63367d06..661e2824144c0 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -10,16 +10,9 @@ use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - method_name: Symbol, - receiver: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && method_name == sym::into_iter && is_trait_method(cx, expr, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a8f1ec7e91986..c2259c89cab09 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4842,8 +4842,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - self.check_methods(cx, expr); - match expr.kind { ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); @@ -4854,24 +4852,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); }, - ExprKind::MethodCall(method_call, receiver, args, _) => { - let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); - expect_fun_call::check( - cx, - &self.format_args, - expr, - method_span, - method_call.ident.name, - receiver, - args, - ); - clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); - clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); - single_char_add_str::check(cx, expr, receiver, args); - into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + ExprKind::MethodCall(..) => { + self.check_methods(cx, expr); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -5566,8 +5548,18 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + let method_span = path.ident.span; + + // Those methods do their own method name checking as they deal with multiple methods. + or_fun_call::check(cx, expr, method_span, path.ident.name, recv, args, self.msrv); + unnecessary_to_owned::check(cx, expr, path.ident.name, recv, args, self.msrv); + match (path.ident.name, args) { - (sym::expect, [_]) => { + (sym::clone, []) => { + clone_on_ref_ptr::check(cx, expr, recv); + clone_on_copy::check(cx, expr, recv); + }, + (sym::expect, [arg]) => { unwrap_expect_used::check( cx, expr, @@ -5577,6 +5569,7 @@ impl Methods { self.allow_expect_in_tests, unwrap_expect_used::Variant::Expect, ); + expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); }, (sym::expect_err, [_]) => { unwrap_expect_used::check( @@ -5589,6 +5582,15 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, + (sym::insert_str | sym::push_str, _) => { + single_char_add_str::check(cx, expr, recv, args); + }, + (sym::into_iter, []) => { + into_iter_on_ref::check(cx, expr, method_span, recv); + }, + (sym::to_string, []) => { + inefficient_to_string::check(cx, expr, recv, self.msrv); + }, (sym::unwrap, []) => { unwrap_expect_used::check( cx, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index fee45c6709623..2b22f344e8c02 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -176,6 +176,7 @@ generate! { hidden_glob_reexports, hygiene, insert, + insert_str, inspect, int_roundings, into, @@ -259,6 +260,7 @@ generate! { powi, product, push, + push_str, read, read_exact, read_line, From 5d0007627a308daf5c7bb228a1edc009a0134e4a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 9 Oct 2025 16:01:24 +1100 Subject: [PATCH 1456/1900] Clarify and improve `EdgesVec::INLINE_CAPACITY` use. `INLINE_CAPACITY` has two different uses: - It dictates the inline capacity of `EdgesVec::edges`, which is a `SmallVec`. - It dictates when `TaskDeps` switches from a linear scan lookup to a hashset lookup to determine if an edge has been seen before. These two uses are in the same part of the code, but they're fundamentally separate and don't need to use the same constant. This commit separates the two uses, and adds some helpful comments, making the code clearer. It also changes the value used for the linear/hashset threshold from 8 to 16, which gives slightly better perf. --- .../rustc_query_system/src/dep_graph/edges.rs | 4 +--- .../rustc_query_system/src/dep_graph/graph.rs | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/edges.rs b/compiler/rustc_query_system/src/dep_graph/edges.rs index 9a3763bd4eeb4..092e9c1ceb751 100644 --- a/compiler/rustc_query_system/src/dep_graph/edges.rs +++ b/compiler/rustc_query_system/src/dep_graph/edges.rs @@ -8,7 +8,7 @@ use crate::dep_graph::DepNodeIndex; #[derive(Default, Debug)] pub(crate) struct EdgesVec { max: u32, - edges: SmallVec<[DepNodeIndex; EdgesVec::INLINE_CAPACITY]>, + edges: SmallVec<[DepNodeIndex; 8]>, } impl Hash for EdgesVec { @@ -19,8 +19,6 @@ impl Hash for EdgesVec { } impl EdgesVec { - pub(crate) const INLINE_CAPACITY: usize = 8; - #[inline] pub(crate) fn new() -> Self { Self::default() diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 4f12380ca786e..d962db3585f27 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -385,6 +385,8 @@ impl DepGraphData { { debug_assert!(!cx.is_eval_always(dep_kind)); + // Large numbers of reads are common enough here that pre-sizing `read_set` + // to 128 actually helps perf on some benchmarks. let task_deps = Lock::new(TaskDeps::new( #[cfg(debug_assertions)] None, @@ -483,18 +485,17 @@ impl DepGraph { data.current.total_read_count.fetch_add(1, Ordering::Relaxed); } - // As long as we only have a low number of reads we can avoid doing a hash - // insert and potentially allocating/reallocating the hashmap - let new_read = if task_deps.reads.len() < EdgesVec::INLINE_CAPACITY { + // Has `dep_node_index` been seen before? Use either a linear scan or a hashset + // lookup to determine this. See `TaskDeps::read_set` for details. + let new_read = if task_deps.reads.len() <= TaskDeps::LINEAR_SCAN_MAX { !task_deps.reads.contains(&dep_node_index) } else { task_deps.read_set.insert(dep_node_index) }; if new_read { task_deps.reads.push(dep_node_index); - if task_deps.reads.len() == EdgesVec::INLINE_CAPACITY { - // Fill `read_set` with what we have so far so we can use the hashset - // next time + if task_deps.reads.len() == TaskDeps::LINEAR_SCAN_MAX + 1 { + // Fill `read_set` with what we have so far. Future lookups will use it. task_deps.read_set.extend(task_deps.reads.iter().copied()); } @@ -1304,12 +1305,23 @@ pub enum TaskDepsRef<'a> { pub struct TaskDeps { #[cfg(debug_assertions)] node: Option, + + /// A vector of `DepNodeIndex`, basically. reads: EdgesVec, + + /// When adding new edges to `reads` in `DepGraph::read_index` we need to determine if the edge + /// has been seen before. If the number of elements in `reads` is small, we just do a linear + /// scan. If the number is higher, a hashset has better perf. This field is that hashset. It's + /// only used if the number of elements in `reads` exceeds `LINEAR_SCAN_MAX`. read_set: FxHashSet, + phantom_data: PhantomData, } impl TaskDeps { + /// See `TaskDeps::read_set` above. + const LINEAR_SCAN_MAX: usize = 16; + #[inline] fn new(#[cfg(debug_assertions)] node: Option, read_set_capacity: usize) -> Self { TaskDeps { From 9a64aebd61df85e5c404025627b9222c6b43c070 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 9 Oct 2025 13:23:14 +0800 Subject: [PATCH 1457/1900] Fix not applicable on param in let-stmt for add_explicit_type Example --- ```rust fn f() { let f: fn(i32) = |y$0| {}; } ``` **Before this PR**: Assist not applicable **After this PR**: ```rust fn f() { let f: fn(i32) = |y: i32| {}; } ``` --- .../src/handlers/add_explicit_type.rs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs index 35a65cc309111..0dd01b67e8f47 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -1,3 +1,4 @@ +use either::Either; use hir::HirDisplay; use ide_db::syntax_helpers::node_ext::walk_ty; use syntax::ast::{self, AstNode, LetStmt, Param}; @@ -20,7 +21,8 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let (ascribed_ty, expr, pat) = if let Some(let_stmt) = ctx.find_node_at_offset::() { + let syntax_node = ctx.find_node_at_offset::>()?; + let (ascribed_ty, expr, pat) = if let Either::Left(let_stmt) = syntax_node { let cursor_in_range = { let eq_range = let_stmt.eq_token()?.text_range(); ctx.offset() < eq_range.start() @@ -31,7 +33,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O } (let_stmt.ty(), let_stmt.initializer(), let_stmt.pat()?) - } else if let Some(param) = ctx.find_node_at_offset::() { + } else if let Either::Right(param) = syntax_node { if param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_none() { cov_mark::hit!(add_explicit_type_not_applicable_in_fn_param); return None; @@ -298,6 +300,20 @@ fn f() { let x: i32 = y; }; } +"#, + ); + + check_assist( + add_explicit_type, + r#" +fn f() { + let f: fn(i32) = |y$0| {}; +} +"#, + r#" +fn f() { + let f: fn(i32) = |y: i32| {}; +} "#, ); } From 064e3b82120992d44a1a0bd65bb502624a600521 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Thu, 9 Oct 2025 08:09:56 +0200 Subject: [PATCH 1458/1900] remove intrinsic wrapper functions from LLVM bindings --- compiler/rustc_codegen_llvm/src/builder.rs | 50 ++++++++------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 47 -------------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 63 ------------------- 3 files changed, 28 insertions(+), 132 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 9673f69906f7b..c082a82306848 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1518,14 +1518,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) { self.set_metadata_node(inst, llvm::MD_unpredictable, &[]); } -} -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) } + self.call_intrinsic("llvm.minnum", &[self.val_ty(lhs)], &[lhs, rhs]) } pub(crate) fn maxnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs) } + self.call_intrinsic("llvm.maxnum", &[self.val_ty(lhs)], &[lhs, rhs]) } pub(crate) fn insert_element( @@ -1547,10 +1546,10 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { } pub(crate) fn vector_reduce_fadd(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src) } + self.call_intrinsic("llvm.vector.reduce.fadd", &[self.val_ty(src)], &[acc, src]) } pub(crate) fn vector_reduce_fmul(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src) } + self.call_intrinsic("llvm.vector.reduce.fmul", &[self.val_ty(src)], &[acc, src]) } pub(crate) fn vector_reduce_fadd_reassoc( &mut self, @@ -1558,7 +1557,8 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { src: &'ll Value, ) -> &'ll Value { unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); + let instr = + self.call_intrinsic("llvm.vector.reduce.fadd", &[self.val_ty(src)], &[acc, src]); llvm::LLVMRustSetAllowReassoc(instr); instr } @@ -1569,43 +1569,49 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { src: &'ll Value, ) -> &'ll Value { unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); + let instr = + self.call_intrinsic("llvm.vector.reduce.fmul", &[self.val_ty(src)], &[acc, src]); llvm::LLVMRustSetAllowReassoc(instr); instr } } pub(crate) fn vector_reduce_add(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src) } + self.call_intrinsic("llvm.vector.reduce.add", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_mul(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src) } + self.call_intrinsic("llvm.vector.reduce.mul", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_and(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src) } + self.call_intrinsic("llvm.vector.reduce.and", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_or(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src) } + self.call_intrinsic("llvm.vector.reduce.or", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_xor(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src) } + self.call_intrinsic("llvm.vector.reduce.xor", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_fmin(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { - llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false) - } + self.call_intrinsic("llvm.vector.reduce.fmin", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_fmax(&mut self, src: &'ll Value) -> &'ll Value { - unsafe { - llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false) - } + self.call_intrinsic("llvm.vector.reduce.fmax", &[self.val_ty(src)], &[src]) } pub(crate) fn vector_reduce_min(&mut self, src: &'ll Value, is_signed: bool) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed) } + self.call_intrinsic( + if is_signed { "llvm.vector.reduce.smin" } else { "llvm.vector.reduce.umin" }, + &[self.val_ty(src)], + &[src], + ) } pub(crate) fn vector_reduce_max(&mut self, src: &'ll Value, is_signed: bool) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed) } + self.call_intrinsic( + if is_signed { "llvm.vector.reduce.smax" } else { "llvm.vector.reduce.umax" }, + &[self.val_ty(src)], + &[src], + ) } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn add_clause(&mut self, landing_pad: &'ll Value, clause: &'ll Value) { unsafe { llvm::LLVMAddClause(landing_pad, clause); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7e62630f3f98f..fdd73ad86e463 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2044,53 +2044,6 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( - B: &Builder<'a>, - Acc: &'a Value, - Src: &'a Value, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( - B: &Builder<'a>, - Acc: &'a Value, - Src: &'a Value, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceAdd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMul<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceAnd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceOr<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceXor<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMin<'a>( - B: &Builder<'a>, - Src: &'a Value, - IsSigned: bool, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( - B: &Builder<'a>, - Src: &'a Value, - IsSigned: bool, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( - B: &Builder<'a>, - Src: &'a Value, - IsNaN: bool, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( - B: &Builder<'a>, - Src: &'a Value, - IsNaN: bool, - ) -> &'a Value; - - pub(crate) fn LLVMRustBuildMinNum<'a>( - B: &Builder<'a>, - LHS: &'a Value, - RHS: &'a Value, - ) -> &'a Value; - pub(crate) fn LLVMRustBuildMaxNum<'a>( - B: &Builder<'a>, - LHS: &'a Value, - RHS: &'a Value, - ) -> &'a Value; - pub(crate) fn LLVMRustTimeTraceProfilerInitialize(); pub(crate) fn LLVMRustTimeTraceProfilerFinishThread(); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 38d677cfa3403..29cef07462af7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1481,69 +1481,6 @@ extern "C" void LLVMRustModuleInstructionStats(LLVMModuleRef M, }); } -// Vector reductions: -extern "C" LLVMValueRef LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, - LLVMValueRef Acc, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc), unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, - LLVMValueRef Acc, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc), unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateAddReduce(unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateMulReduce(unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateAndReduce(unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateOrReduce(unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, - LLVMValueRef Src) { - return wrap(unwrap(B)->CreateXorReduce(unwrap(Src))); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, - LLVMValueRef Src, - bool IsSigned) { - return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned)); -} -extern "C" LLVMValueRef LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, - LLVMValueRef Src, - bool IsSigned) { - return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned)); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { - Instruction *I = unwrap(B)->CreateFPMinReduce(unwrap(Src)); - I->setHasNoNaNs(NoNaN); - return wrap(I); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { - Instruction *I = unwrap(B)->CreateFPMaxReduce(unwrap(Src)); - I->setHasNoNaNs(NoNaN); - return wrap(I); -} - -extern "C" LLVMValueRef LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, - LLVMValueRef RHS) { - return wrap(unwrap(B)->CreateMinNum(unwrap(LHS), unwrap(RHS))); -} -extern "C" LLVMValueRef LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, - LLVMValueRef RHS) { - return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS))); -} - // Transfers ownership of DiagnosticHandler unique_ptr to the caller. extern "C" DiagnosticHandler * LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) { From 901366af1e673a260d1a20551b0199540ae4b455 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 16:42:54 +0800 Subject: [PATCH 1459/1900] fix c_char error in Android --- library/std/src/sys/fs/unix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 63a6db77324a9..51849a31f61b5 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2146,7 +2146,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { // utimensat requires Android API level 19 cvt(unsafe { weak!( - fn utimensat(dirfd: c_int, path: *const c_char, times: *const libc::timespec, flags: c_int) -> c_int; + fn utimensat(dirfd: c_int, path: *const libc::c_char, times: *const libc::timespec, flags: c_int) -> c_int; ); match utimensat.get() { Some(utimensat) => utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags), From 7374789331637f3c32f33fc851227728b73d9b61 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 21:25:13 +1100 Subject: [PATCH 1460/1900] Support `x check rustdoc-gui-test` This is useful for ensuring that changes to compiletest haven't broken rustdoc-gui-test. --- src/bootstrap/src/core/build_steps/check.rs | 9 +++++++++ src/bootstrap/src/core/builder/mod.rs | 1 + 2 files changed, 10 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 20c54a28dda83..400c6f732c3c3 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -821,6 +821,15 @@ tool_check_step!(Compiletest { default: false, }); +// As with compiletest, rustdoc-gui-test is automatically built when running +// relevant tests. So being able to check it is mainly useful for people +// working on on rustdoc-gui-test itself, or on its compiletest dependency. +tool_check_step!(RustdocGuiTest { + path: "src/tools/rustdoc-gui-test", + mode: Mode::ToolBootstrap, + default: false, +}); + tool_check_step!(Linkchecker { path: "src/tools/linkchecker", mode: Mode::ToolBootstrap, diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index fc06db8f80b9d..cd3961056d2c1 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1071,6 +1071,7 @@ impl<'a> Builder<'a> { check::Bootstrap, check::RunMakeSupport, check::Compiletest, + check::RustdocGuiTest, check::FeaturesStatusDump, check::CoverageDump, check::Linkchecker, From 0be0a0a8d2b09441767e17ab1c30c7ef27c80713 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 21:42:28 +1100 Subject: [PATCH 1461/1900] Do `x check` on various bootstrap tools in CI --- src/ci/docker/host-x86_64/pr-check-1/Dockerfile | 12 +++++++++++- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile index 776bbb12e445b..32e1b30dab962 100644 --- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile @@ -40,7 +40,17 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/ # We disable optimized compiler built-ins because that requires a C toolchain for the target. # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. ENV SCRIPT \ - python3 ../x.py check bootstrap && \ + # Check some tools that aren't included in `x check` by default, to + # ensure that maintainers can still do check builds locally. + python3 ../x.py check \ + bootstrap \ + bump-stage0 \ + compiletest \ + coverage-dump \ + linkchecker \ + run-make-support \ + rustdoc-gui-test \ + && \ /scripts/check-default-config-profiles.sh && \ python3 ../x.py build src/tools/build-manifest && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 0a95f428f5bd8..c005eceb6ef4a 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -28,7 +28,6 @@ RUN sh /scripts/sccache.sh ENV SCRIPT \ python3 ../x.py check && \ - python3 ../x.py check src/tools/bump-stage0 && \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ From 345241536fbd2302c032851c0422f18ed847a0df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Oct 2025 11:15:31 +0200 Subject: [PATCH 1462/1900] repr_transparent_external_private_fields: normalize types during traversal --- .../rustc_hir_analysis/src/check/check.rs | 15 ++++--- .../repr/repr-transparent-non-exhaustive.rs | 12 +++++ .../repr-transparent-non-exhaustive.stderr | 44 ++++++++++++------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 9c2d52c3e8030..4c910d25c30a8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1510,11 +1510,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } + let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did()); // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with // "known" respecting #[non_exhaustive] attributes. let field_infos = adt.all_fields().map(|field| { let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); - let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did); let layout = tcx.layout_of(typing_env.as_query_input(ty)); // 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(); @@ -1526,11 +1526,16 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) fn check_non_exhaustive<'tcx>( tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, t: Ty<'tcx>, ) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> { + // We can encounter projections during traversal, so ensure the type is normalized. + let t = tcx.try_normalize_erasing_regions(typing_env, t).unwrap_or(t); match t.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), - ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), + ty::Tuple(list) => { + list.iter().try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) + } + ty::Array(ty, _) => check_non_exhaustive(tcx, typing_env, *ty), ty::Adt(def, args) => { if !def.did().is_local() && !find_attr!( @@ -1555,13 +1560,13 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } def.all_fields() .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_non_exhaustive(tcx, t)) + .try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) } _ => ControlFlow::Continue(()), } } - (span, trivial, check_non_exhaustive(tcx, ty).break_value()) + (span, trivial, check_non_exhaustive(tcx, typing_env, ty).break_value()) }); let non_trivial_fields = field_infos diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs index 9894b89e8e418..38bab0163b4c5 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs @@ -24,6 +24,13 @@ pub struct InternalIndirection { pub type Sized = i32; +pub trait Trait { + type Assoc; +} +impl Trait for i32 { + type Assoc = NonExhaustive; +} + #[repr(transparent)] pub struct T1(Sized, InternalPrivate); #[repr(transparent)] @@ -43,6 +50,11 @@ pub struct T6(Sized, NonExhaustive); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler +#[repr(transparent)] +pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhaustive` +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~| WARN this was previously accepted by the compiler + #[repr(transparent)] pub struct T7(Sized, NonExhaustiveEnum); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index 808b9bc986d91..27ba6e82a539a 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -1,5 +1,5 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:37:22 + --> $DIR/repr-transparent-non-exhaustive.rs:44:22 | LL | pub struct T5(Sized, Private); | ^^^^^^^ @@ -14,7 +14,7 @@ LL | #![deny(repr_transparent_external_private_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:42:22 + --> $DIR/repr-transparent-non-exhaustive.rs:49:22 | LL | pub struct T6(Sized, NonExhaustive); | ^^^^^^^^^^^^^ @@ -24,7 +24,17 @@ LL | pub struct T6(Sized, NonExhaustive); = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:47:22 + --> $DIR/repr-transparent-non-exhaustive.rs:54:23 + | +LL | pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhaustive` + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:59:22 | LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ @@ -34,7 +44,7 @@ LL | pub struct T7(Sized, NonExhaustiveEnum); = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:52:22 + --> $DIR/repr-transparent-non-exhaustive.rs:64:22 | LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +54,7 @@ LL | pub struct T8(Sized, NonExhaustiveVariant); = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:57:22 + --> $DIR/repr-transparent-non-exhaustive.rs:69:22 | LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +64,7 @@ LL | pub struct T9(Sized, InternalIndirection); = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:62:23 + --> $DIR/repr-transparent-non-exhaustive.rs:74:23 | LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +74,7 @@ LL | pub struct T10(Sized, InternalIndirection); = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:67:23 + --> $DIR/repr-transparent-non-exhaustive.rs:79:23 | LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +84,7 @@ LL | pub struct T11(Sized, InternalIndirection); = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:72:23 + --> $DIR/repr-transparent-non-exhaustive.rs:84:23 | LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +94,7 @@ LL | pub struct T12(Sized, InternalIndirection); = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:77:23 + --> $DIR/repr-transparent-non-exhaustive.rs:89:23 | LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +104,7 @@ LL | pub struct T13(Sized, ExternalIndirection); = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:82:23 + --> $DIR/repr-transparent-non-exhaustive.rs:94:23 | LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +114,7 @@ LL | pub struct T14(Sized, ExternalIndirection); = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:87:23 + --> $DIR/repr-transparent-non-exhaustive.rs:99:23 | LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +124,7 @@ LL | pub struct T15(Sized, ExternalIndirection); = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:92:23 + --> $DIR/repr-transparent-non-exhaustive.rs:104:23 | LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +134,7 @@ LL | pub struct T16(Sized, ExternalIndirection); = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:97:16 + --> $DIR/repr-transparent-non-exhaustive.rs:109:16 | LL | pub struct T17(NonExhaustive, Sized); | ^^^^^^^^^^^^^ @@ -134,7 +144,7 @@ LL | pub struct T17(NonExhaustive, Sized); = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:102:31 + --> $DIR/repr-transparent-non-exhaustive.rs:114:31 | LL | pub struct T18(NonExhaustive, NonExhaustive); | ^^^^^^^^^^^^^ @@ -144,7 +154,7 @@ LL | pub struct T18(NonExhaustive, NonExhaustive); = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:107:31 + --> $DIR/repr-transparent-non-exhaustive.rs:119:31 | LL | pub struct T19(NonExhaustive, Private); | ^^^^^^^ @@ -154,7 +164,7 @@ LL | pub struct T19(NonExhaustive, Private); = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:112:32 + --> $DIR/repr-transparent-non-exhaustive.rs:124:32 | LL | pub struct T19Flipped(Private, NonExhaustive); | ^^^^^^^^^^^^^ @@ -163,5 +173,5 @@ LL | pub struct T19Flipped(Private, NonExhaustive); = note: for more information, see issue #78586 = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From 6b697dba796e7fa3ed413052b55b79e1e154481a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 13:46:56 +0200 Subject: [PATCH 1463/1900] fix(clone_on_ref_ptr): only name the generic type if possible --- clippy_lints/src/methods/clone_on_ref_ptr.rs | 24 +++++++++++----- tests/ui/clone_on_ref_ptr.fixed | 30 ++++++++++++++++++++ tests/ui/clone_on_ref_ptr.rs | 30 ++++++++++++++++++++ tests/ui/clone_on_ref_ptr.stderr | 8 +++++- 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 059893b05bb9c..238e1fe988b39 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, IsSuggestable}; use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; @@ -30,12 +30,22 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: // Sometimes unnecessary ::<_> after Rc/Arc/Weak let mut app = Applicability::Unspecified; let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - diag.span_suggestion( - expr.span, - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, - ); + let generic = subst.type_at(0); + if generic.is_suggestable(cx.tcx, true) { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{generic}>::clone(&{snippet})"), + app, + ); + } else { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::::clone(&{snippet})"), + Applicability::HasPlaceholders, + ); + } }, ); } diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed index 8ef4b36566363..ede9d171517e7 100644 --- a/tests/ui/clone_on_ref_ptr.fixed +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = std::rc::Weak::::clone(&rec) as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs index fbd787099aee3..5999b4069d0f2 100644 --- a/tests/ui/clone_on_ref_ptr.rs +++ b/tests/ui/clone_on_ref_ptr.rs @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = rec.clone() as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr index b15f0e803a352..b8ddc3058c012 100644 --- a/tests/ui/clone_on_ref_ptr.stderr +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -37,5 +37,11 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 6 previous errors +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:65:23 + | +LL | let rec = rec.clone() as Weak u32>; + | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rec)` + +error: aborting due to 7 previous errors From d4edd67f71fb3949c9280acbbb96a5391b9e1dac Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 18:23:56 +0800 Subject: [PATCH 1464/1900] Update LLVM to 21.1.3 --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 8c30b9c5098bd..4f74b76fb6968 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 8c30b9c5098bdff1d3d9a2d460ee091cd1171e60 +Subproject commit 4f74b76fb69688474e073fb26b316d9ea571388f From c3432bbec01a6b72374b95f721e936969d4a04a8 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 9 Oct 2025 11:51:17 +0000 Subject: [PATCH 1465/1900] Explicit comment. --- compiler/rustc_mir_transform/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index acf5a5c86f715..f4f61c80a220a 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -699,8 +699,8 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), &multiple_return_terminators::MultipleReturnTerminators, // After simplifycfg, it allows us to discover new opportunities for peephole - // optimizations. This invalidates CFG caches, so avoid putting between two SSA - // analyses. + // optimizations. This invalidates CFG caches, so avoid putting between + // `ReferencePropagation` and `GVN` which both use the dominator tree. &instsimplify::InstSimplify::AfterSimplifyCfg, &ref_prop::ReferencePropagation, &sroa::ScalarReplacementOfAggregates, From f3f83857d928d931171339910f37900fd5f03283 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 21:08:40 +1100 Subject: [PATCH 1466/1900] Use the same directive lines for EarlyProps and ignore/only/needs --- src/tools/compiletest/src/directives.rs | 58 ++++++++----------- src/tools/compiletest/src/directives/file.rs | 24 ++++++++ src/tools/compiletest/src/directives/tests.rs | 12 ++-- src/tools/compiletest/src/lib.rs | 11 ++-- 4 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 src/tools/compiletest/src/directives/file.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 34a9d77ce0dfa..c96a1be38fee3 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -12,6 +12,7 @@ use crate::directives::auxiliary::{AuxProps, parse_and_update_aux}; use crate::directives::directive_names::{ KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, }; +pub(crate) use crate::directives::file::FileDirectives; use crate::directives::line::{DirectiveLine, line_directive}; use crate::directives::needs::CachedNeedsConditions; use crate::edition::{Edition, parse_edition}; @@ -23,6 +24,7 @@ use crate::{fatal, help}; pub(crate) mod auxiliary; mod cfg; mod directive_names; +mod file; mod line; mod needs; #[cfg(test)] @@ -41,31 +43,29 @@ impl DirectivesCache { /// Properties which must be known very early, before actually running /// the test. #[derive(Default)] -pub struct EarlyProps { +pub(crate) struct EarlyProps { /// Auxiliary crates that should be built and made available to this test. /// Included in [`EarlyProps`] so that the indicated files can participate /// in up-to-date checking. Building happens via [`TestProps::aux`] instead. pub(crate) aux: AuxProps, - pub revisions: Vec, + pub(crate) revisions: Vec, } impl EarlyProps { - pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { - let file_contents = - fs::read_to_string(testfile).expect("read test file to parse earlyprops"); - Self::from_file_contents(config, testfile, &file_contents) - } - - pub fn from_file_contents(config: &Config, testfile: &Utf8Path, file_contents: &str) -> Self { + pub(crate) fn from_file_directives( + config: &Config, + file_directives: &FileDirectives<'_>, + ) -> Self { + let testfile = file_directives.path; let mut props = EarlyProps::default(); let mut poisoned = false; + iter_directives( config.mode, &mut poisoned, - testfile, - file_contents, + file_directives, // (dummy comment to force args into vertical layout) - &mut |ref ln: DirectiveLine<'_>| { + &mut |ln: &DirectiveLine<'_>| { parse_and_update_aux(config, ln, testfile, &mut props.aux); config.parse_and_update_revisions(testfile, ln, &mut props.revisions); }, @@ -358,15 +358,15 @@ impl TestProps { let mut has_edition = false; if !testfile.is_dir() { let file_contents = fs::read_to_string(testfile).unwrap(); + let file_directives = FileDirectives::from_file_contents(testfile, &file_contents); let mut poisoned = false; iter_directives( config.mode, &mut poisoned, - testfile, - &file_contents, - &mut |ref ln: DirectiveLine<'_>| { + &file_directives, + &mut |ln: &DirectiveLine<'_>| { if !ln.applies_to_test_revision(test_revision) { return; } @@ -851,13 +851,10 @@ fn check_directive<'a>( fn iter_directives( mode: TestMode, poisoned: &mut bool, - testfile: &Utf8Path, - file_contents: &str, - it: &mut dyn FnMut(DirectiveLine<'_>), + file_directives: &FileDirectives<'_>, + it: &mut dyn FnMut(&DirectiveLine<'_>), ) { - if testfile.is_dir() { - return; - } + let testfile = file_directives.path; // Coverage tests in coverage-run mode always have these extra directives, without needing to // specify them manually in every test file. @@ -875,21 +872,15 @@ fn iter_directives( for directive_str in extra_directives { let directive_line = line_directive(0, directive_str) .unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}")); - it(directive_line); + it(&directive_line); } } - for (line_number, ln) in (1..).zip(file_contents.lines()) { - let ln = ln.trim(); - - let Some(directive_line) = line_directive(line_number, ln) else { - continue; - }; - + for directive_line @ &DirectiveLine { line_number, .. } in &file_directives.lines { // Perform unknown directive check on Rust files. if testfile.extension() == Some("rs") { let CheckDirectiveResult { is_known_directive, trailing_directive } = - check_directive(&directive_line, mode); + check_directive(directive_line, mode); if !is_known_directive { *poisoned = true; @@ -1349,7 +1340,7 @@ pub(crate) fn make_test_description( name: String, path: &Utf8Path, filterable_path: &Utf8Path, - file_contents: &str, + file_directives: &FileDirectives<'_>, test_revision: Option<&str>, poisoned: &mut bool, ) -> CollectedTestDesc { @@ -1363,9 +1354,8 @@ pub(crate) fn make_test_description( iter_directives( config.mode, &mut local_poisoned, - path, - file_contents, - &mut |ref ln @ DirectiveLine { line_number, .. }| { + file_directives, + &mut |ln @ &DirectiveLine { line_number, .. }| { if !ln.applies_to_test_revision(test_revision) { return; } diff --git a/src/tools/compiletest/src/directives/file.rs b/src/tools/compiletest/src/directives/file.rs new file mode 100644 index 0000000000000..afb9bb188bd39 --- /dev/null +++ b/src/tools/compiletest/src/directives/file.rs @@ -0,0 +1,24 @@ +use camino::Utf8Path; + +use crate::directives::line::{DirectiveLine, line_directive}; + +pub(crate) struct FileDirectives<'a> { + pub(crate) path: &'a Utf8Path, + pub(crate) lines: Vec>, +} + +impl<'a> FileDirectives<'a> { + pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self { + let mut lines = vec![]; + + for (line_number, ln) in (1..).zip(file_contents.lines()) { + let ln = ln.trim(); + + if let Some(directive_line) = line_directive(line_number, ln) { + lines.push(directive_line); + } + } + + Self { path, lines } + } +} diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 77080c7469371..4e7ae6de76a52 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -3,7 +3,7 @@ use semver::Version; use crate::common::{Config, Debugger, TestMode}; use crate::directives::{ - DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, + DirectivesCache, EarlyProps, Edition, EditionRange, FileDirectives, extract_llvm_version, extract_version_range, iter_directives, line_directive, parse_edition, parse_normalize_rule, }; use crate::executor::{CollectedTestDesc, ShouldPanic}; @@ -18,13 +18,15 @@ fn make_test_description( ) -> CollectedTestDesc { let cache = DirectivesCache::load(config); let mut poisoned = false; + let file_directives = FileDirectives::from_file_contents(path, file_contents); + let test = crate::directives::make_test_description( config, &cache, name, path, filterable_path, - file_contents, + &file_directives, revision, &mut poisoned, ); @@ -224,7 +226,8 @@ fn cfg() -> ConfigBuilder { } fn parse_rs(config: &Config, contents: &str) -> EarlyProps { - EarlyProps::from_file_contents(config, Utf8Path::new("a.rs"), contents) + let file_directives = FileDirectives::from_file_contents(Utf8Path::new("a.rs"), contents); + EarlyProps::from_file_directives(config, &file_directives) } fn check_ignore(config: &Config, contents: &str) -> bool { @@ -776,7 +779,8 @@ fn threads_support() { } fn run_path(poisoned: &mut bool, path: &Utf8Path, file_contents: &str) { - iter_directives(TestMode::Ui, poisoned, path, file_contents, &mut |_| {}); + let file_directives = FileDirectives::from_file_contents(path, file_contents); + iter_directives(TestMode::Ui, poisoned, &file_directives, &mut |_| {}); } #[test] diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 48ae38b61b93d..4cfb1e20f9ae9 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -41,7 +41,7 @@ use crate::common::{ CodegenBackend, CompareMode, Config, Debugger, PassMode, TestMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir, output_relative_path, }; -use crate::directives::DirectivesCache; +use crate::directives::{DirectivesCache, FileDirectives}; use crate::edition::parse_edition; use crate::executor::{CollectedTest, ColorConfig}; @@ -868,7 +868,10 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te }; // Scan the test file to discover its revisions, if any. - let early_props = EarlyProps::from_file(&cx.config, &test_path); + let file_contents = + fs::read_to_string(&test_path).expect("reading test file for directives should succeed"); + let file_directives = FileDirectives::from_file_contents(&test_path, &file_contents); + let early_props = EarlyProps::from_file_directives(&cx.config, &file_directives); // Normally we create one structure per revision, with two exceptions: // - If a test doesn't use revisions, create a dummy revision (None) so that @@ -886,8 +889,6 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te // `CollectedTest` that can be handed over to the test executor. collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. - let file_contents = - fs::read_to_string(&test_path).expect("read test file to parse ignores"); let (test_name, filterable_path) = make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. @@ -899,7 +900,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te test_name, &test_path, &filterable_path, - &file_contents, + &file_directives, revision, &mut collector.poisoned, ); From c0d82ccbe9661bde5c3c23ed573403c74d459945 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 9 Oct 2025 20:04:39 +0800 Subject: [PATCH 1467/1900] minor: update typos checker version --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 0eb57a605f828..e3384c976bb24 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -293,7 +293,7 @@ jobs: timeout-minutes: 10 env: FORCE_COLOR: 1 - TYPOS_VERSION: v1.28.3 + TYPOS_VERSION: v1.38.1 steps: - name: download typos run: curl -LsSf https://github.com/crate-ci/typos/releases/download/$TYPOS_VERSION/typos-$TYPOS_VERSION-x86_64-unknown-linux-musl.tar.gz | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin From 56cf2f21cd51a2342abfaff74f8600f74e7a35c5 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 9 Oct 2025 20:07:24 +0800 Subject: [PATCH 1468/1900] minor: correct typos --- src/tools/rust-analyzer/.typos.toml | 1 + src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs | 2 +- .../rust-analyzer/crates/ide-assists/src/assist_config.rs | 2 +- .../crates/ide-assists/src/handlers/add_missing_match_arms.rs | 2 +- .../crates/ide-assists/src/handlers/convert_bool_to_enum.rs | 2 +- .../crates/ide-assists/src/handlers/convert_into_to_from.rs | 2 +- .../src/handlers/convert_tuple_return_type_to_struct.rs | 2 +- .../ide-assists/src/handlers/destructure_struct_binding.rs | 2 +- .../crates/ide-assists/src/handlers/extract_function.rs | 2 +- .../src/handlers/extract_struct_from_enum_variant.rs | 2 +- .../crates/ide-assists/src/handlers/generate_deref.rs | 4 ++-- .../crates/ide-assists/src/handlers/generate_new.rs | 2 +- .../src/handlers/generate_single_field_struct_from.rs | 2 +- .../crates/ide-assists/src/handlers/qualify_method_call.rs | 2 +- .../src/handlers/replace_derive_with_manual_impl.rs | 2 +- .../src/handlers/replace_qualified_name_with_use.rs | 2 +- .../crates/ide-assists/src/handlers/term_search.rs | 2 +- .../crates/ide-assists/src/handlers/toggle_async_sugar.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs | 2 +- 19 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index cdbc003a80882..99464150dab47 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -32,6 +32,7 @@ makro = "makro" trivias = "trivias" thir = "thir" jod = "jod" +tructure = "tructure" [default.extend-identifiers] anc = "anc" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 8a7d93d50c05b..b0986c423b116 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -39,7 +39,7 @@ pub fn target_data_layout_query( target, } => format!(r#"inconsistent target specification: "data-layout" claims pointers are {pointer_size}-bit, while "target-pointer-width" is `{target}`"#), TargetDataLayoutErrors::InvalidBitsSize { err } => err, - TargetDataLayoutErrors::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifer in "data-layout": {err}"#), + TargetDataLayoutErrors::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifier in "data-layout": {err}"#), }.into()) } }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 597d035ebd857..7d5070ace6190 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -38,7 +38,7 @@ impl AssistConfig { } } - pub fn find_path_confg(&self, allow_unstable: bool) -> FindPathConfig { + pub fn find_path_config(&self, allow_unstable: bool) -> FindPathConfig { FindPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 3910921fbe03a..8802a54e7f240 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -80,7 +80,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let scope = ctx.sema.scope(expr.syntax())?; let module = scope.module(); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(scope.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { scope .containing_function() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index 80445578fcef9..2ad13362378ed 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -346,7 +346,7 @@ fn augment_references_with_imports( ImportScope::find_insert_use_container(name.syntax(), &ctx.sema).and_then( |import_scope| { let cfg = - ctx.config.find_path_confg(ctx.sema.is_nightly(target_module.krate())); + ctx.config.find_path_config(ctx.sema.is_nightly(target_module.krate())); let path = ref_module .find_use_path( ctx.sema.db, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index 3a464a3dc6aae..8f02d28b82454 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -43,7 +43,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - return None; } - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let src_type_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 80ffb4db3e84d..9876e14203fe1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -199,7 +199,7 @@ fn augment_references_with_imports( { visited_modules.insert(ref_module); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(ref_module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(ref_module.krate())); let import_scope = ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); let path = ref_module diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 27755db93c882..8b24d33bf9965 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -88,7 +88,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Op FamousDefs(&ctx.sema, module.krate()).core_ops_ControlFlow(); if let Some(control_flow_enum) = control_flow_enum { - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, ModuleDef::from(control_flow_enum), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 20ebd8f09c73f..2cec3296c87e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -400,7 +400,7 @@ fn process_references( let segment = builder.make_mut(segment); let scope_node = builder.make_syntax_mut(scope_node); if !visited_modules.contains(&module) { - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, *enum_module_def, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index a1fc2c6023597..b02e3e435c814 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -57,7 +57,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; @@ -99,7 +99,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 9760fd62aab4f..cd968b97105af 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -77,7 +77,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?)); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(current_module.krate())); let type_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index cad14d929648a..6c302a2a6fbdf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -169,7 +169,7 @@ fn make_constructors( types: &[ast::Type], ) -> Vec> { let (db, sema) = (ctx.db(), &ctx.sema); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); types .iter() .map(|ty| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index e4494f0492ece..d8dbefd59e28a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -45,7 +45,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> let current_edition = current_module.krate().edition(ctx.db()); let target_module_def = ModuleDef::from(resolved_call); let item_in_ns = ItemInNs::from(target_module_def); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(current_module.krate())); let receiver_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 25c5593007bcb..2bf4406cc6a9c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -71,7 +71,7 @@ pub(crate) fn replace_derive_with_manual_impl( let current_module = ctx.sema.scope(adt.syntax())?.module(); let current_crate = current_module.krate(); let current_edition = current_crate.edition(ctx.db()); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_crate)); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(current_crate)); let found_traits = items_locator::items_with_name( ctx.db(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 5fc4d7a6170de..f6526cdba4a9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -64,7 +64,7 @@ pub(crate) fn replace_qualified_name_with_use( let path_to_qualifier = starts_with_name_ref .then(|| { let mod_ = ctx.sema.scope(original_path.syntax())?.module(); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(mod_.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(mod_.krate())); mod_.find_use_path(ctx.sema.db, module, ctx.config.insert_use.prefix_kind, cfg) }) .flatten(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 209d3f08eb888..d64a76eb8b37a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -55,7 +55,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< path.gen_source_code( &scope, &mut formatter, - ctx.config.find_path_confg(ctx.sema.is_nightly(scope.module().krate())), + ctx.config.find_path_config(ctx.sema.is_nightly(scope.module().krate())), scope.krate().to_display_target(ctx.db()), ) .ok() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs index aed66d3d8344c..fa6fca8150ac6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs @@ -132,7 +132,7 @@ pub(crate) fn desugar_async_into_impl_future( let scope = ctx.sema.scope(function.syntax())?; let module = scope.module(); - let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); + let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate())); let future_trait = FamousDefs(&ctx.sema, scope.krate()).core_future_Future()?; let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(future_trait), cfg)?; let edition = scope.krate().edition(ctx.db()); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 079dedda000c3..0da9ee097ac3b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -116,7 +116,7 @@ pub struct HighlightConfig { // |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.| // |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.| // |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.| -// |logical| Emitted for the logical operatos `||`, `&&`, `!`.| +// |logical| Emitted for the logical operators `||`, `&&`, `!`.| // // - For punctuation: // From 71c05330dfa8bc6280dd779eb91f18ccd4e98d88 Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Thu, 9 Oct 2025 12:28:06 +0000 Subject: [PATCH 1469/1900] x86_64-bigint-helpers test: update test assertion --- tests/assembly-llvm/x86_64-bigint-helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index 64aa025723856..9d998a31cf306 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -44,7 +44,7 @@ pub unsafe extern "sysv64" fn bigint_chain_borrowing_sub( n: usize, mut carry: bool, ) -> bool { - // CHECK: mov [[TEMP:r..]], qword ptr [rsi + 8*[[IND:r..]] + 8] + // CHECK: mov [[TEMP:r.+]], qword ptr [rsi + 8*[[IND:r.+]] + 8] // CHECK: sbb [[TEMP]], qword ptr [rdx + 8*[[IND]] + 8] // CHECK: mov qword ptr [rdi + 8*[[IND]] + 8], [[TEMP]] // CHECK: mov [[TEMP]], qword ptr [rsi + 8*[[IND]] + 16] From f47c48048d6ba9d96dd4138803595284443d7708 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:35:14 +0000 Subject: [PATCH 1470/1900] Reduce scope of unsafe block in cg_llvm allocator codegen --- compiler/rustc_codegen_llvm/src/allocator.rs | 42 ++++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 824aa501036d4..486ee817a1c79 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -95,28 +95,28 @@ pub(crate) unsafe fn codegen( &CodegenFnAttrs::new(), ); - unsafe { - // __rust_alloc_error_handler_should_panic_v2 - create_const_value_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, OomStrategy::SYMBOL), - &i8, - &llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE), - ); + // __rust_alloc_error_handler_should_panic_v2 + create_const_value_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + &i8, + unsafe { + llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE) + }, + ); - // __rust_no_alloc_shim_is_unstable_v2 - create_wrapper_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - None, - &[], - None, - false, - &CodegenFnAttrs::new(), - ); - } + // __rust_no_alloc_shim_is_unstable_v2 + create_wrapper_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + None, + &[], + None, + false, + &CodegenFnAttrs::new(), + ); if tcx.sess.opts.debuginfo != DebugInfo::None { let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod); From f8118d88d7bdb5f697cf4712e01f6b46fe2bc742 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 9 Oct 2025 06:36:51 -0700 Subject: [PATCH 1471/1900] unsupported: Use `unsupported()` for `set_times` --- library/std/src/sys/fs/unsupported.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 659ea2a8fc276..7901bf5624d74 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -312,8 +312,8 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } -pub fn set_times(_p: &Path, times: FileTimes) -> io::Result<()> { - match times {} +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() } pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { From d2f590a6d598dc75b09fd9d97c5bb32d23a6971a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 9 Oct 2025 06:37:09 -0700 Subject: [PATCH 1472/1900] unsupported: Use `unsupported()` for `set_times_nofollow` --- library/std/src/sys/fs/unsupported.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 7901bf5624d74..f222151d18e25 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -316,8 +316,8 @@ pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } -pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { - match times {} +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() } pub fn rmdir(_p: &Path) -> io::Result<()> { From 513c9b7b86c48cc879b3119d1fcc3174bfa81f95 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 9 Oct 2025 13:43:25 +0000 Subject: [PATCH 1473/1900] Add debugging instrumentation. --- compiler/rustc_mir_transform/src/coroutine.rs | 32 ++++++++++++++----- .../rustc_mir_transform/src/coroutine/drop.rs | 8 +++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c136df812a3f0..fa72b2e94fd93 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -159,6 +159,7 @@ impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> { } } +#[tracing::instrument(level = "trace", skip(tcx))] fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) { place.local = new_base.local; @@ -166,6 +167,7 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx new_projection.append(&mut place.projection.to_vec()); place.projection = tcx.mk_place_elems(&new_projection); + tracing::trace!(?place); } const SELF_ARG: Local = Local::from_u32(1); @@ -270,6 +272,7 @@ impl<'tcx> TransformVisitor<'tcx> { // `core::ops::CoroutineState` only has single element tuple variants, // so we can just write to the downcasted first field and then set the // discriminant to the appropriate variant. + #[tracing::instrument(level = "trace", skip(self, statements))] fn make_state( &self, val: Operand<'tcx>, @@ -348,6 +351,7 @@ impl<'tcx> TransformVisitor<'tcx> { } // Create a Place referencing a coroutine struct field + #[tracing::instrument(level = "trace", skip(self), ret)] fn make_field(&self, variant_index: VariantIdx, idx: FieldIdx, ty: Ty<'tcx>) -> Place<'tcx> { let self_place = Place::from(SELF_ARG); let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index); @@ -358,6 +362,7 @@ impl<'tcx> TransformVisitor<'tcx> { } // Create a statement which changes the discriminant + #[tracing::instrument(level = "trace", skip(self))] fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> { let self_place = Place::from(SELF_ARG); Statement::new( @@ -370,6 +375,7 @@ impl<'tcx> TransformVisitor<'tcx> { } // Create a statement which reads the discriminant into a temporary + #[tracing::instrument(level = "trace", skip(self, body))] fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) { let temp_decl = LocalDecl::new(self.discr_ty, body.span); let local_decls_len = body.local_decls.push(temp_decl); @@ -389,22 +395,20 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { self.tcx } - fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { + #[tracing::instrument(level = "trace", skip(self), ret)] + fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _location: Location) { assert!(!self.remap.contains(*local)); } - fn visit_place( - &mut self, - place: &mut Place<'tcx>, - _context: PlaceContext, - _location: Location, - ) { + #[tracing::instrument(level = "trace", skip(self), ret)] + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _location: Location) { // Replace an Local in the remap with a coroutine struct access if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); } } + #[tracing::instrument(level = "trace", skip(self, data), ret)] fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { // Remove StorageLive and StorageDead statements for remapped locals for s in &mut data.statements { @@ -483,6 +487,7 @@ fn make_aggregate_adt<'tcx>( Rvalue::Aggregate(Box::new(AggregateKind::Adt(def_id, variant_idx, args, None, None)), operands) } +#[tracing::instrument(level = "trace", skip(tcx, body))] fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let coroutine_ty = body.local_decls.raw[1].ty; @@ -495,6 +500,7 @@ fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo SelfArgVisitor::new(tcx, ProjectionElem::Deref).visit_body(body); } +#[tracing::instrument(level = "trace", skip(tcx, body))] fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let ref_coroutine_ty = body.local_decls.raw[1].ty; @@ -553,6 +559,7 @@ fn replace_local<'tcx>( /// The async lowering step and the type / lifetime inference / checking are /// still using the `ResumeTy` indirection for the time being, and that indirection /// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`. +#[tracing::instrument(level = "trace", skip(tcx, body), ret)] fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ty<'tcx> { let context_mut_ref = Ty::new_task_context(tcx); @@ -606,6 +613,7 @@ fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local } #[cfg_attr(not(debug_assertions), allow(unused))] +#[tracing::instrument(level = "trace", skip(tcx, body), ret)] fn replace_resume_ty_local<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, @@ -670,6 +678,7 @@ struct LivenessInfo { /// case none exist, the local is considered to be always live. /// - a local has to be stored if it is either directly used after the /// the suspend point, or if it is live and has been previously borrowed. +#[tracing::instrument(level = "trace", skip(tcx, body))] fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -945,6 +954,7 @@ impl StorageConflictVisitor<'_, '_> { } } +#[tracing::instrument(level = "trace", skip(liveness, body))] fn compute_layout<'tcx>( liveness: LivenessInfo, body: &Body<'tcx>, @@ -1049,7 +1059,9 @@ fn compute_layout<'tcx>( variant_source_info, storage_conflicts, }; + debug!(?remap); debug!(?layout); + debug!(?storage_liveness); (remap, layout, storage_liveness) } @@ -1221,6 +1233,7 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( } } +#[tracing::instrument(level = "trace", skip(tcx, transform, body))] fn create_coroutine_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, @@ -1299,7 +1312,7 @@ fn create_coroutine_resume_function<'tcx>( } /// An operation that can be performed on a coroutine. -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, Debug)] enum Operation { Resume, Drop, @@ -1314,6 +1327,7 @@ impl Operation { } } +#[tracing::instrument(level = "trace", skip(transform, body))] fn create_cases<'tcx>( body: &mut Body<'tcx>, transform: &TransformVisitor<'tcx>, @@ -1445,6 +1459,8 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // This only applies to coroutines return; }; + tracing::trace!(def_id = ?body.source.def_id()); + let old_ret_ty = body.return_ty(); assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none()); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index fd2d8b2b0563e..41c588f0d8d31 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -126,6 +126,7 @@ fn build_pin_fut<'tcx>( // Ready() => ready_block // Pending => yield_block //} +#[tracing::instrument(level = "trace", skip(tcx, body), ret)] fn build_poll_switch<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, @@ -179,6 +180,7 @@ fn build_poll_switch<'tcx>( } // Gather blocks, reachable through 'drop' targets of Yield and Drop terminators (chained) +#[tracing::instrument(level = "trace", skip(body), ret)] fn gather_dropline_blocks<'tcx>(body: &mut Body<'tcx>) -> DenseBitSet { let mut dropline: DenseBitSet = DenseBitSet::new_empty(body.basic_blocks.len()); for (bb, data) in traversal::reverse_postorder(body) { @@ -249,6 +251,7 @@ pub(super) fn has_expandable_async_drops<'tcx>( } /// Expand Drop terminator for async drops into mainline poll-switch and dropline poll-switch +#[tracing::instrument(level = "trace", skip(tcx, body), ret)] pub(super) fn expand_async_drops<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, @@ -259,6 +262,7 @@ pub(super) fn expand_async_drops<'tcx>( let dropline = gather_dropline_blocks(body); // Clean drop and async_fut fields if potentially async drop is not expanded (stays sync) let remove_asyncness = |block: &mut BasicBlockData<'tcx>| { + tracing::trace!("remove_asyncness"); if let TerminatorKind::Drop { place: _, target: _, @@ -461,6 +465,7 @@ pub(super) fn expand_async_drops<'tcx>( } } +#[tracing::instrument(level = "trace", skip(tcx, body))] pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { use crate::elaborate_drop::{Unwind, elaborate_drop}; use crate::patch::MirPatch; @@ -519,6 +524,7 @@ pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body elaborator.patch.apply(body); } +#[tracing::instrument(level = "trace", skip(tcx, body), ret)] pub(super) fn insert_clean_drop<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, @@ -550,6 +556,7 @@ pub(super) fn insert_clean_drop<'tcx>( .push(BasicBlockData::new(Some(Terminator { source_info, kind: term }), false)) } +#[tracing::instrument(level = "trace", skip(tcx, transform, body))] pub(super) fn create_coroutine_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, @@ -621,6 +628,7 @@ pub(super) fn create_coroutine_drop_shim<'tcx>( } // Create async drop shim function to drop coroutine itself +#[tracing::instrument(level = "trace", skip(tcx, transform, body))] pub(super) fn create_coroutine_drop_shim_async<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, From 1ad657ed5f2e2a9a965283ae8a096099cd7d0ebc Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 9 Oct 2025 13:50:06 +0000 Subject: [PATCH 1474/1900] Simplify TransformVisitor. --- compiler/rustc_mir_transform/src/coroutine.rs | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index fa72b2e94fd93..e7f9eeddee8c7 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -408,33 +408,39 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } } - #[tracing::instrument(level = "trace", skip(self, data), ret)] - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { + #[tracing::instrument(level = "trace", skip(self, stmt), ret)] + fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { // Remove StorageLive and StorageDead statements for remapped locals - for s in &mut data.statements { - if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind - && self.remap.contains(l) - { - s.make_nop(true); - } + if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind + && self.remap.contains(l) + { + stmt.make_nop(true); } + self.super_statement(stmt, location); + } - let ret_val = match data.terminator().kind { + #[tracing::instrument(level = "trace", skip(self, data), ret)] + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { + match data.terminator().kind { TerminatorKind::Return => { - Some((true, None, Operand::Move(Place::from(self.old_ret_local)), None)) - } - TerminatorKind::Yield { ref value, resume, resume_arg, drop } => { - Some((false, Some((resume, resume_arg)), value.clone(), drop)) + let source_info = data.terminator().source_info; + // We must assign the value first in case it gets declared dead below + self.make_state( + Operand::Move(Place::from(self.old_ret_local)), + source_info, + true, + &mut data.statements, + ); + // Return state. + let state = VariantIdx::new(CoroutineArgs::RETURNED); + data.statements.push(self.set_discr(state, source_info)); + data.terminator_mut().kind = TerminatorKind::Return; } - _ => None, - }; - - if let Some((is_return, resume, v, drop)) = ret_val { - let source_info = data.terminator().source_info; - // We must assign the value first in case it gets declared dead below - self.make_state(v, source_info, is_return, &mut data.statements); - let state = if let Some((resume, mut resume_arg)) = resume { - // Yield + TerminatorKind::Yield { ref value, resume, mut resume_arg, drop } => { + let source_info = data.terminator().source_info; + // We must assign the value first in case it gets declared dead below + self.make_state(value.clone(), source_info, false, &mut data.statements); + // Yield state. let state = CoroutineArgs::RESERVED_VARIANTS + self.suspension_points.len(); // The resume arg target location might itself be remapped if its base local is @@ -465,13 +471,11 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { storage_liveness, }); - VariantIdx::new(state) - } else { - // Return - VariantIdx::new(CoroutineArgs::RETURNED) // state for returned - }; - data.statements.push(self.set_discr(state, source_info)); - data.terminator_mut().kind = TerminatorKind::Return; + let state = VariantIdx::new(state); + data.statements.push(self.set_discr(state, source_info)); + data.terminator_mut().kind = TerminatorKind::Return; + } + _ => {} } self.super_basic_block_data(block, data); From da86710c307d5a411534ba7b1bf22a87f8b2afa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 9 Oct 2025 17:19:23 +0200 Subject: [PATCH 1475/1900] Rename default linker linux field --- src/bootstrap/src/core/config/config.rs | 2 +- src/bootstrap/src/core/config/toml/target.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 02dd3f0c05c50..85388d3b4639e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -844,7 +844,7 @@ impl Config { ar: target_ar, ranlib: target_ranlib, default_linker: target_default_linker, - default_linker_linux: target_default_linker_linux_override, + default_linker_linux_override: target_default_linker_linux_override, linker: target_linker, split_debuginfo: target_split_debuginfo, llvm_config: target_llvm_config, diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 7b8d4f9601b53..4c7afa50b9658 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -27,7 +27,7 @@ define_config! { ar: Option = "ar", ranlib: Option = "ranlib", default_linker: Option = "default-linker", - default_linker_linux: Option = "default-linker-linux-override", + default_linker_linux_override: Option = "default-linker-linux-override", linker: Option = "linker", split_debuginfo: Option = "split-debuginfo", llvm_config: Option = "llvm-config", @@ -95,7 +95,7 @@ impl Target { } } -/// Overrides the default linker used on a Linux linker. +/// Overrides the default linker used on a Linux target. /// On Linux, the linker is usually invoked through `cc`, therefore this exists as a separate /// configuration from simply setting `default-linker`, which corresponds to `-Clinker`. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] From 6d800ae35b4458461d99b18bd7c20f4c1ec40d06 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 9 Oct 2025 14:12:26 +0000 Subject: [PATCH 1476/1900] Renumber locals after state transform. --- compiler/rustc_mir_transform/src/coroutine.rs | 72 +++++++++++-------- ...#0}.coroutine_drop_async.0.panic-abort.mir | 34 ++++----- ...0}.coroutine_drop_async.0.panic-unwind.mir | 34 ++++----- 3 files changed, 75 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index e7f9eeddee8c7..5af05f221dc97 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -68,7 +68,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind}; use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::util::Discr; use rustc_middle::ty::{ @@ -110,6 +110,8 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> { fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { if *local == self.from { *local = self.to; + } else if *local == self.to { + *local = self.from; } } @@ -206,8 +208,8 @@ struct TransformVisitor<'tcx> { // The set of locals that have no `StorageLive`/`StorageDead` annotations. always_live_locals: DenseBitSet, - // The original RETURN_PLACE local - old_ret_local: Local, + // New local we just create to hold the `CoroutineState` value. + new_ret_local: Local, old_yield_ty: Ty<'tcx>, @@ -344,9 +346,10 @@ impl<'tcx> TransformVisitor<'tcx> { } }; + // Assign to `new_ret_local`, which will be replaced by `RETURN_PLACE` later. statements.push(Statement::new( source_info, - StatementKind::Assign(Box::new((Place::return_place(), rvalue))), + StatementKind::Assign(Box::new((self.new_ret_local.into(), rvalue))), )); } @@ -388,6 +391,20 @@ impl<'tcx> TransformVisitor<'tcx> { ); (assign, temp) } + + /// Swaps all references of `old_local` and `new_local`. + #[tracing::instrument(level = "trace", skip(self, body))] + fn replace_local(&mut self, old_local: Local, new_local: Local, body: &mut Body<'tcx>) { + body.local_decls.swap(old_local, new_local); + + let mut visitor = RenameLocalVisitor { from: old_local, to: new_local, tcx: self.tcx }; + visitor.visit_body(body); + for suspension in &mut self.suspension_points { + let ctxt = PlaceContext::MutatingUse(MutatingUseContext::Yield); + let location = Location { block: START_BLOCK, statement_index: 0 }; + visitor.visit_place(&mut suspension.resume_arg, ctxt, location); + } + } } impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { @@ -419,6 +436,16 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { self.super_statement(stmt, location); } + #[tracing::instrument(level = "trace", skip(self, term), ret)] + fn visit_terminator(&mut self, term: &mut Terminator<'tcx>, location: Location) { + if let TerminatorKind::Return = term.kind { + // `visit_basic_block_data` introduces `Return` terminators which read `RETURN_PLACE`. + // But this `RETURN_PLACE` is already remapped, so we should not touch it again. + return; + } + self.super_terminator(term, location); + } + #[tracing::instrument(level = "trace", skip(self, data), ret)] fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { match data.terminator().kind { @@ -426,7 +453,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { let source_info = data.terminator().source_info; // We must assign the value first in case it gets declared dead below self.make_state( - Operand::Move(Place::from(self.old_ret_local)), + Operand::Move(Place::return_place()), source_info, true, &mut data.statements, @@ -521,27 +548,6 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body .visit_body(body); } -/// Allocates a new local and replaces all references of `local` with it. Returns the new local. -/// -/// `local` will be changed to a new local decl with type `ty`. -/// -/// Note that the new local will be uninitialized. It is the caller's responsibility to assign some -/// valid value to it before its first use. -fn replace_local<'tcx>( - local: Local, - ty: Ty<'tcx>, - body: &mut Body<'tcx>, - tcx: TyCtxt<'tcx>, -) -> Local { - let new_decl = LocalDecl::new(ty, body.span); - let new_local = body.local_decls.push(new_decl); - body.local_decls.swap(local, new_local); - - RenameLocalVisitor { from: local, to: new_local, tcx }.visit_body(body); - - new_local -} - /// Transforms the `body` of the coroutine applying the following transforms: /// /// - Eliminates all the `get_context` calls that async lowering created. @@ -1511,10 +1517,6 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { } }; - // We rename RETURN_PLACE which has type mir.return_ty to old_ret_local - // RETURN_PLACE then is a fresh unused local with type ret_ty. - let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx); - // We need to insert clean drop for unresumed state and perform drop elaboration // (finally in open_drop_for_tuple) before async drop expansion. // Async drops, produced by this drop elaboration, will be expanded, @@ -1561,6 +1563,11 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { let can_return = can_return(tcx, body, body.typing_env(tcx)); + // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local + // RETURN_PLACE then is a fresh unused local with type ret_ty. + let new_ret_local = body.local_decls.push(LocalDecl::new(new_ret_ty, body.span)); + tracing::trace!(?new_ret_local); + // Run the transformation which converts Places from Local to coroutine struct // accesses for locals in `remap`. // It also rewrites `return x` and `yield y` as writing a new coroutine state and returning @@ -1573,13 +1580,16 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { storage_liveness, always_live_locals, suspension_points: Vec::new(), - old_ret_local, discr_ty, + new_ret_local, old_ret_ty, old_yield_ty, }; transform.visit_body(body); + // Swap the actual `RETURN_PLACE` and the provisional `new_ret_local`. + transform.replace_local(RETURN_PLACE, new_ret_local, body); + // MIR parameters are not explicitly assigned-to when entering the MIR body. // If we want to save their values inside the coroutine state, we need to do so explicitly. let source_info = SourceInfo::outermost(body.span); diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir index 050aac7c3ff05..541b9b9895b3c 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir @@ -10,16 +10,16 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _6: std::pin::Pin<&mut T>; let mut _7: &mut T; let mut _8: *mut T; - let mut _9: (); - let mut _10: std::task::Poll<()>; - let mut _11: &mut std::task::Context<'_>; - let mut _12: &mut impl std::future::Future; - let mut _13: std::pin::Pin<&mut impl std::future::Future>; - let mut _14: isize; - let mut _15: &mut std::task::Context<'_>; - let mut _16: &mut impl std::future::Future; - let mut _17: std::pin::Pin<&mut impl std::future::Future>; - let mut _18: isize; + let mut _9: std::task::Poll<()>; + let mut _10: &mut std::task::Context<'_>; + let mut _11: &mut impl std::future::Future; + let mut _12: std::pin::Pin<&mut impl std::future::Future>; + let mut _13: isize; + let mut _14: &mut std::task::Context<'_>; + let mut _15: &mut impl std::future::Future; + let mut _16: std::pin::Pin<&mut impl std::future::Future>; + let mut _17: isize; + let mut _18: (); let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); @@ -48,9 +48,9 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) } bb4: { - StorageLive(_17); - _16 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future); - _17 = Pin::<&mut impl Future>::new_unchecked(move _16) -> [return: bb7, unwind unreachable]; + StorageLive(_16); + _15 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future); + _16 = Pin::<&mut impl Future>::new_unchecked(move _15) -> [return: bb7, unwind unreachable]; } bb5: { @@ -58,13 +58,13 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) } bb6: { - StorageDead(_17); - _18 = discriminant(_10); - switchInt(move _18) -> [0: bb1, 1: bb3, otherwise: bb5]; + StorageDead(_16); + _17 = discriminant(_9); + switchInt(move _17) -> [0: bb1, 1: bb3, otherwise: bb5]; } bb7: { - _10 = as Future>::poll(move _17, move _15) -> [return: bb6, unwind unreachable]; + _9 = as Future>::poll(move _16, move _14) -> [return: bb6, unwind unreachable]; } bb8: { diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir index 796e95ff3d825..f2e1fb0997568 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir @@ -10,16 +10,16 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _6: std::pin::Pin<&mut T>; let mut _7: &mut T; let mut _8: *mut T; - let mut _9: (); - let mut _10: std::task::Poll<()>; - let mut _11: &mut std::task::Context<'_>; - let mut _12: &mut impl std::future::Future; - let mut _13: std::pin::Pin<&mut impl std::future::Future>; - let mut _14: isize; - let mut _15: &mut std::task::Context<'_>; - let mut _16: &mut impl std::future::Future; - let mut _17: std::pin::Pin<&mut impl std::future::Future>; - let mut _18: isize; + let mut _9: std::task::Poll<()>; + let mut _10: &mut std::task::Context<'_>; + let mut _11: &mut impl std::future::Future; + let mut _12: std::pin::Pin<&mut impl std::future::Future>; + let mut _13: isize; + let mut _14: &mut std::task::Context<'_>; + let mut _15: &mut impl std::future::Future; + let mut _16: std::pin::Pin<&mut impl std::future::Future>; + let mut _17: isize; + let mut _18: (); let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); @@ -62,9 +62,9 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) } bb7: { - StorageLive(_17); - _16 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future); - _17 = Pin::<&mut impl Future>::new_unchecked(move _16) -> [return: bb10, unwind: bb15]; + StorageLive(_16); + _15 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future); + _16 = Pin::<&mut impl Future>::new_unchecked(move _15) -> [return: bb10, unwind: bb15]; } bb8: { @@ -72,13 +72,13 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) } bb9: { - StorageDead(_17); - _18 = discriminant(_10); - switchInt(move _18) -> [0: bb1, 1: bb6, otherwise: bb8]; + StorageDead(_16); + _17 = discriminant(_9); + switchInt(move _17) -> [0: bb1, 1: bb6, otherwise: bb8]; } bb10: { - _10 = as Future>::poll(move _17, move _15) -> [return: bb9, unwind: bb3]; + _9 = as Future>::poll(move _16, move _14) -> [return: bb9, unwind: bb3]; } bb11: { From 64d9d4236475169c61e7af3d1cd4fd8b93ac8e86 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 9 Oct 2025 19:31:43 +0300 Subject: [PATCH 1477/1900] resolver: Move `local_modules` from `ResolverArenas` to `Resolver` Thus avoiding a `RefCell`. --- compiler/rustc_resolve/src/check_unused.rs | 2 +- compiler/rustc_resolve/src/imports.rs | 18 ++++++++----- compiler/rustc_resolve/src/lib.rs | 31 +++++++++------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 95f979a3fed33..3f793bb013899 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -507,7 +507,7 @@ impl Resolver<'_, '_> { let unused_imports = visitor.unused_imports; let mut check_redundant_imports = FxIndexSet::default(); - for module in self.arenas.local_modules().iter() { + for module in &self.local_modules { for (_key, resolution) in self.resolutions(*module).borrow().iter() { if let Some(binding) = resolution.borrow().best_binding() && let NameBindingKind::Import { import, .. } = binding.kind diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 04eb312a0f8ba..4fb2a51bb89c0 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -8,7 +8,7 @@ use rustc_data_structures::intern::Interned; use rustc_errors::codes::*; use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir::def::{self, DefKind, PartialRes}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefIdMap}; use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::Visibility; @@ -572,9 +572,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } pub(crate) fn finalize_imports(&mut self) { - for module in self.arenas.local_modules().iter() { - self.finalize_resolutions_in(*module); + let mut module_children = Default::default(); + for module in &self.local_modules { + self.finalize_resolutions_in(*module, &mut module_children); } + self.module_children = module_children; let mut seen_spans = FxHashSet::default(); let mut errors = vec![]; @@ -651,7 +653,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet>) { - for module in self.arenas.local_modules().iter() { + for module in &self.local_modules { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); let Some(binding) = resolution.best_binding() else { continue }; @@ -1548,7 +1550,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Miscellaneous post-processing, including recording re-exports, // reporting conflicts, and reporting unresolved imports. - fn finalize_resolutions_in(&mut self, module: Module<'ra>) { + fn finalize_resolutions_in( + &self, + module: Module<'ra>, + module_children: &mut LocalDefIdMap>, + ) { // Since import resolution is finished, globs will not define any more names. *module.globs.borrow_mut(self) = Vec::new(); @@ -1573,7 +1579,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if !children.is_empty() { // Should be fine because this code is only called for local modules. - self.module_children.insert(def_id.expect_local(), children); + module_children.insert(def_id.expect_local(), children); } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d9c3f4089a0fb..2f45e858dc509 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1145,6 +1145,8 @@ pub struct Resolver<'ra, 'tcx> { /// some AST passes can generate identifiers that only resolve to local or /// lang items. empty_module: Module<'ra>, + /// All local modules, including blocks. + local_modules: Vec>, /// Eagerly populated map of all local non-block modules. local_module_map: FxIndexMap>, /// Lazily populated cache of modules loaded from external crates. @@ -1298,7 +1300,6 @@ pub struct Resolver<'ra, 'tcx> { #[derive(Default)] pub struct ResolverArenas<'ra> { modules: TypedArena>, - local_modules: RefCell>>, imports: TypedArena>, name_resolutions: TypedArena>>, ast_paths: TypedArena, @@ -1341,28 +1342,20 @@ impl<'ra> ResolverArenas<'ra> { span: Span, no_implicit_prelude: bool, ) -> Module<'ra> { - let (def_id, self_binding) = match kind { - ModuleKind::Def(def_kind, def_id, _) => ( - Some(def_id), - Some(self.new_pub_res_binding(Res::Def(def_kind, def_id), span, LocalExpnId::ROOT)), - ), - ModuleKind::Block => (None, None), + let self_binding = match kind { + ModuleKind::Def(def_kind, def_id, _) => { + Some(self.new_pub_res_binding(Res::Def(def_kind, def_id), span, LocalExpnId::ROOT)) + } + ModuleKind::Block => None, }; - let module = Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( + Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( parent, kind, expn_id, span, no_implicit_prelude, self_binding, - )))); - if def_id.is_none_or(|def_id| def_id.is_local()) { - self.local_modules.borrow_mut().push(module); - } - module - } - fn local_modules(&'ra self) -> std::cell::Ref<'ra, Vec>> { - self.local_modules.borrow() + )))) } fn alloc_name_binding(&'ra self, name_binding: NameBindingData<'ra>) -> NameBinding<'ra> { Interned::new_unchecked(self.dropless.alloc(name_binding)) @@ -1505,7 +1498,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { arenas: &'ra ResolverArenas<'ra>, ) -> Resolver<'ra, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); - let mut local_module_map = FxIndexMap::default(); let graph_root = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), @@ -1513,7 +1505,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { crate_span, attr::contains_name(attrs, sym::no_implicit_prelude), ); - local_module_map.insert(CRATE_DEF_ID, graph_root); + let local_modules = vec![graph_root]; + let local_module_map = FxIndexMap::from_iter([(CRATE_DEF_ID, graph_root)]); let empty_module = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), @@ -1591,6 +1584,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module_children: Default::default(), trait_map: NodeMap::default(), empty_module, + local_modules, local_module_map, extern_module_map: Default::default(), block_map: Default::default(), @@ -1694,6 +1688,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { no_implicit_prelude: bool, ) -> Module<'ra> { let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude); + self.local_modules.push(module); if let Some(def_id) = module.opt_def_id() { self.local_module_map.insert(def_id.expect_local(), module); } From 6d0fafd8493f4250e7394b832391d6cc5c507fcf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 11:15:05 +0200 Subject: [PATCH 1478/1900] clean-up --- .../src/matches/match_like_matches.rs | 43 ++++++++++--------- ...o.fixed => match_like_matches_macro.fixed} | 5 +-- ...s_macro.rs => match_like_matches_macro.rs} | 5 +-- ...stderr => match_like_matches_macro.stderr} | 28 ++++++------ 4 files changed, 41 insertions(+), 40 deletions(-) rename tests/ui/{match_expr_like_matches_macro.fixed => match_like_matches_macro.fixed} (97%) rename tests/ui/{match_expr_like_matches_macro.rs => match_like_matches_macro.rs} (97%) rename tests/ui/{match_expr_like_matches_macro.stderr => match_like_matches_macro.stderr} (84%) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 5816da5695eb6..8257c39c90f05 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,3 +1,5 @@ +//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` + use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; @@ -11,7 +13,6 @@ use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; -/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` pub(crate) fn check_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -23,10 +24,11 @@ pub(crate) fn check_if_let<'tcx>( find_matches_sugg( cx, let_expr, - IntoIterator::into_iter([ + [ (&[][..], Some(let_pat), then_expr, None), (&[][..], None, else_expr, None), - ]), + ] + .into_iter(), expr, true, ); @@ -52,7 +54,7 @@ pub(super) fn check_match<'tcx>( fn find_matches_sugg<'a, 'b, I>( cx: &LateContext<'_>, ex: &Expr<'_>, - mut iter: I, + mut arms: I, expr: &Expr<'_>, is_if_let: bool, ) -> bool @@ -64,17 +66,17 @@ where + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, { if !span_contains_comment(cx.sess().source_map(), expr.span) - && iter.len() >= 2 + && arms.len() >= 2 && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() - && let iter_without_last = iter.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() - && let Some(b0) = find_bool_lit(&first_expr.kind) - && let Some(b1) = find_bool_lit(&last_expr.kind) + && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() + && let arms_without_last = arms.clone() + && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() + && let Some(b0) = find_bool_lit(first_expr) + && let Some(b1) = find_bool_lit(last_expr) && b0 != b1 - && (first_guard.is_none() || iter.len() == 0) + && (first_guard.is_none() || arms.len() == 0) && first_attrs.is_empty() - && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) { if let Some(last_pat) = last_pat_opt && !is_wild(last_pat) @@ -82,10 +84,10 @@ where return false; } - for arm in iter_without_last.clone() { + for arm in arms_without_last.clone() { if let Some(pat) = arm.1 && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some(pat.kind) + && is_some_wild(pat.kind) { return false; } @@ -96,7 +98,7 @@ where let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; - iter_without_last + arms_without_last .filter_map(|arm| { let pat_span = arm.1?.span; Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) @@ -142,11 +144,11 @@ where } /// Extract a `bool` or `{ bool }` -fn find_bool_lit(ex: &ExprKind<'_>) -> Option { - match ex { +fn find_bool_lit(ex: &Expr<'_>) -> Option { + match ex.kind { ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. - }) => Some(*b), + }) => Some(b), ExprKind::Block( rustc_hir::Block { stmts: [], @@ -168,8 +170,9 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { } } -fn is_some(path_kind: PatKind<'_>) -> bool { - match path_kind { +/// Checks whether a pattern is `Some(_)` +fn is_some_wild(pat_kind: PatKind<'_>) -> bool { + match pat_kind { PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { let name = path.segments[0].ident; name.name == rustc_span::sym::Some diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed similarity index 97% rename from tests/ui/match_expr_like_matches_macro.fixed rename to tests/ui/match_like_matches_macro.fixed index 8530ab16bfd7d..a1c95e8a94f14 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -14,11 +13,11 @@ fn main() { let _y = matches!(x, Some(0)); //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = x.is_some(); //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = x.is_none(); //~^^^^ redundant_pattern_matching diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs similarity index 97% rename from tests/ui/match_expr_like_matches_macro.rs rename to tests/ui/match_like_matches_macro.rs index 81017936889e4..eb419ba5bf8de 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -17,14 +16,14 @@ fn main() { }; //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = match x { Some(_) => true, _ => false, }; //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = match x { Some(_) => false, None => true, diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr similarity index 84% rename from tests/ui/match_expr_like_matches_macro.stderr rename to tests/ui/match_like_matches_macro.stderr index 8fceb05bc6e87..ae277ce4dca68 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:14:14 + --> tests/ui/match_like_matches_macro.rs:13:14 | LL | let _y = match x { | ______________^ @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/match_expr_like_matches_macro.rs:21:14 + --> tests/ui/match_like_matches_macro.rs:20:14 | LL | let _w = match x { | ______________^ @@ -25,7 +25,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_expr_like_matches_macro.rs:28:14 + --> tests/ui/match_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:35:15 + --> tests/ui/match_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -45,13 +45,13 @@ LL | | }; | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:42:16 + --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:67:20 + --> tests/ui/match_like_matches_macro.rs:66:20 | LL | let _ans = match x { | ____________________^ @@ -62,7 +62,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:78:20 + --> tests/ui/match_like_matches_macro.rs:77:20 | LL | let _ans = match x { | ____________________^ @@ -74,7 +74,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:89:20 + --> tests/ui/match_like_matches_macro.rs:88:20 | LL | let _ans = match x { | ____________________^ @@ -85,7 +85,7 @@ LL | | }; | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:150:18 + --> tests/ui/match_like_matches_macro.rs:149:18 | LL | let _z = match &z { | __________________^ @@ -95,7 +95,7 @@ LL | | }; | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:160:18 + --> tests/ui/match_like_matches_macro.rs:159:18 | LL | let _z = match &z { | __________________^ @@ -105,7 +105,7 @@ LL | | }; | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:178:21 + --> tests/ui/match_like_matches_macro.rs:177:21 | LL | let _ = match &z { | _____________________^ @@ -115,7 +115,7 @@ LL | | }; | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:193:20 + --> tests/ui/match_like_matches_macro.rs:192:20 | LL | let _res = match &val { | ____________________^ @@ -125,7 +125,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:206:20 + --> tests/ui/match_like_matches_macro.rs:205:20 | LL | let _res = match &val { | ____________________^ @@ -135,7 +135,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:265:14 + --> tests/ui/match_like_matches_macro.rs:264:14 | LL | let _y = match Some(5) { | ______________^ From 62e1225c87852d9650e6d34ffc6c263ba41fab8e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 18:56:08 +0200 Subject: [PATCH 1479/1900] inline `find_matches_sugg` into `check_if_let` --- .../src/matches/match_like_matches.rs | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 8257c39c90f05..3df23a6357942 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -21,17 +21,64 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - find_matches_sugg( - cx, - let_expr, - [ - (&[][..], Some(let_pat), then_expr, None), - (&[][..], None, else_expr, None), - ] - .into_iter(), - expr, - true, - ); + let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); + let is_if_let = true; + if !span_contains_comment(cx.sess().source_map(), expr.span) + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some((None, else_expr)) = arms.next_back() + && let arms_without_last = [(Some(let_pat), then_expr)] + && let Some((_, first_expr)) = arms.next() + && let Some(b0) = find_bool_lit(first_expr) + && let Some(b1) = find_bool_lit(else_expr) + && b0 != b1 + && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) + { + for arm in &arms_without_last { + if let Some(pat) = arm.0 + && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) + && is_some_wild(pat.kind) + { + return; + } + } + + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; + let pat = { + use itertools::Itertools as _; + arms_without_last + .into_iter() + .filter_map(|arm| arm.0) + .map(|pat| snippet_with_applicability(cx, pat.span, "..", &mut applicability)) + .join(" | ") + }; + let pat_and_guard = pat; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = let_expr; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = let_expr.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; + } + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + format!( + "{} expression looks like `matches!` macro", + if is_if_let { "if let .. else" } else { "match" } + ), + "try", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + } } pub(super) fn check_match<'tcx>( From 27d5f5c7a61aca226eb33c8fa1e46b9ad4aa1f03 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:01:33 +0200 Subject: [PATCH 1480/1900] inline `is_if_let` into all callers --- .../src/matches/match_like_matches.rs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 3df23a6357942..7e9a14c81fc3c 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -22,7 +22,6 @@ pub(crate) fn check_if_let<'tcx>( else_expr: &'tcx Expr<'_>, ) { let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); - let is_if_let = true; if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() && let Some((None, else_expr)) = arms.next_back() @@ -66,10 +65,7 @@ pub(crate) fn check_if_let<'tcx>( cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + "if let .. else expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", @@ -93,18 +89,11 @@ pub(super) fn check_match<'tcx>( arms.iter() .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), e, - false, ) } /// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>( - cx: &LateContext<'_>, - ex: &Expr<'_>, - mut arms: I, - expr: &Expr<'_>, - is_if_let: bool, -) -> bool +fn find_matches_sugg<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, mut arms: I, expr: &Expr<'_>) -> bool where 'b: 'a, I: Clone @@ -172,10 +161,7 @@ where cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + "match expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", From 8519d49d6f5eabed9ddd0a241436cc1d80e46022 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:04:02 +0200 Subject: [PATCH 1481/1900] inline `arms_without_last` into all callers it's now just the first arm --- .../src/matches/match_like_matches.rs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 7e9a14c81fc3c..7b5412d297365 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -25,34 +25,20 @@ pub(crate) fn check_if_let<'tcx>( if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() && let Some((None, else_expr)) = arms.next_back() - && let arms_without_last = [(Some(let_pat), then_expr)] && let Some((_, first_expr)) = arms.next() && let Some(b0) = find_bool_lit(first_expr) && let Some(b1) = find_bool_lit(else_expr) && b0 != b1 && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) { - for arm in &arms_without_last { - if let Some(pat) = arm.0 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some_wild(pat.kind) - { - return; - } + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { + return; } // The suggestion may be incorrect, because some arms can have `cfg` attributes // evaluated into `false` and so such arms will be stripped before. let mut applicability = Applicability::MaybeIncorrect; - let pat = { - use itertools::Itertools as _; - arms_without_last - .into_iter() - .filter_map(|arm| arm.0) - .map(|pat| snippet_with_applicability(cx, pat.span, "..", &mut applicability)) - .join(" | ") - }; - let pat_and_guard = pat; + let pat = snippet_with_applicability(cx, let_pat.span, "..", &mut applicability); // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = let_expr; @@ -68,7 +54,7 @@ pub(crate) fn check_if_let<'tcx>( "if let .. else expression looks like `matches!` macro", "try", format!( - "{}matches!({}, {pat_and_guard})", + "{}matches!({}, {pat})", if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), ), From d1d5f0c7ae6944ff9d47aa97b0a3fb0a1469d2b0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:11:02 +0200 Subject: [PATCH 1482/1900] inline `arms` into all callers --- clippy_lints/src/matches/match_like_matches.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 7b5412d297365..3bee0b7894f52 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -21,15 +21,11 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((None, else_expr)) = arms.next_back() - && let Some((_, first_expr)) = arms.next() - && let Some(b0) = find_bool_lit(first_expr) + && let Some(b0) = find_bool_lit(then_expr) && let Some(b1) = find_bool_lit(else_expr) && b0 != b1 - && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) { if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { return; From 83e2b3df5481ce50c898327f37ba57450fa009c1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:14:10 +0200 Subject: [PATCH 1483/1900] inline `find_matches_sugg` into `check_match` --- .../src/matches/match_like_matches.rs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 3bee0b7894f52..61b964a19f328 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Attribute, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; @@ -65,27 +65,12 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - find_matches_sugg( - cx, - scrutinee, - arms.iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), - e, - ) -} - -/// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, mut arms: I, expr: &Expr<'_>) -> bool -where - 'b: 'a, - I: Clone - + DoubleEndedIterator - + ExactSizeIterator - + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, -{ - if !span_contains_comment(cx.sess().source_map(), expr.span) + let mut arms = arms + .iter() + .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)); + if !span_contains_comment(cx.sess().source_map(), e.span) && arms.len() >= 2 - && cx.typeck_results().expr_ty(expr).is_bool() + && cx.typeck_results().expr_ty(e).is_bool() && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() && let arms_without_last = arms.clone() && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() @@ -133,8 +118,8 @@ where }; // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + let mut ex_new = scrutinee; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = scrutinee.kind && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; @@ -142,7 +127,7 @@ where span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, - expr.span, + e.span, "match expression looks like `matches!` macro", "try", format!( From bf5170a54967664489161b90dfc75ba352a1cdc6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:16:58 +0200 Subject: [PATCH 1484/1900] match arm pats aren't `Option`al anymore --- .../src/matches/match_like_matches.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 61b964a19f328..9a2610364fefb 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -67,11 +67,11 @@ pub(super) fn check_match<'tcx>( ) -> bool { let mut arms = arms .iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)); + .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), arm.pat, arm.body, arm.guard)); if !span_contains_comment(cx.sess().source_map(), e.span) && arms.len() >= 2 && cx.typeck_results().expr_ty(e).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() + && let Some((_, last_pat, last_expr, _)) = arms.next_back() && let arms_without_last = arms.clone() && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() && let Some(b0) = find_bool_lit(first_expr) @@ -81,17 +81,13 @@ pub(super) fn check_match<'tcx>( && first_attrs.is_empty() && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) { - if let Some(last_pat) = last_pat_opt - && !is_wild(last_pat) - { + if !is_wild(last_pat) { return false; } for arm in arms_without_last.clone() { - if let Some(pat) = arm.1 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some_wild(pat.kind) - { + let pat = arm.1; + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } } @@ -102,9 +98,9 @@ pub(super) fn check_match<'tcx>( let pat = { use itertools::Itertools as _; arms_without_last - .filter_map(|arm| { - let pat_span = arm.1?.span; - Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) + .map(|arm| { + let pat_span = arm.1.span; + snippet_with_applicability(cx, pat_span, "..", &mut applicability) }) .join(" | ") }; From 0efe3cfca56972c2117b1df7f109114264f4ff4d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:27:02 +0200 Subject: [PATCH 1485/1900] split `arms` into first, last, and middle pats as slice --- .../src/matches/match_like_matches.rs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 9a2610364fefb..a91970f822906 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -65,28 +65,28 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - let mut arms = arms - .iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), arm.pat, arm.body, arm.guard)); - if !span_contains_comment(cx.sess().source_map(), e.span) - && arms.len() >= 2 + if let Some((last_arm, arms_without_last)) = arms.split_last() + && let Some((first_arm, middle_arms)) = arms_without_last.split_first() + && !span_contains_comment(cx.sess().source_map(), e.span) && cx.typeck_results().expr_ty(e).is_bool() - && let Some((_, last_pat, last_expr, _)) = arms.next_back() - && let arms_without_last = arms.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() + && let (last_pat, last_expr) = (last_arm.pat, last_arm.body) + && let (first_attrs, first_expr, first_guard) = + (cx.tcx.hir_attrs(first_arm.hir_id), first_arm.body, first_arm.guard) && let Some(b0) = find_bool_lit(first_expr) && let Some(b1) = find_bool_lit(last_expr) && b0 != b1 - && (first_guard.is_none() || arms.len() == 0) + && (first_guard.is_none() || middle_arms.is_empty()) && first_attrs.is_empty() - && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) + && middle_arms.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) { if !is_wild(last_pat) { return false; } - for arm in arms_without_last.clone() { - let pat = arm.1; + for arm in arms_without_last { + let pat = arm.pat; if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } @@ -98,10 +98,8 @@ pub(super) fn check_match<'tcx>( let pat = { use itertools::Itertools as _; arms_without_last - .map(|arm| { - let pat_span = arm.1.span; - snippet_with_applicability(cx, pat_span, "..", &mut applicability) - }) + .iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; let pat_and_guard = if let Some(g) = first_guard { From dd7f6058bf221dbd18eb8c47b283b3aaaf0bf4a4 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:28:49 +0200 Subject: [PATCH 1486/1900] inline `{first,last}_{attrs,expr,pat,guard}` into all callers --- clippy_lints/src/matches/match_like_matches.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index a91970f822906..89da55d4fe742 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -69,19 +69,16 @@ pub(super) fn check_match<'tcx>( && let Some((first_arm, middle_arms)) = arms_without_last.split_first() && !span_contains_comment(cx.sess().source_map(), e.span) && cx.typeck_results().expr_ty(e).is_bool() - && let (last_pat, last_expr) = (last_arm.pat, last_arm.body) - && let (first_attrs, first_expr, first_guard) = - (cx.tcx.hir_attrs(first_arm.hir_id), first_arm.body, first_arm.guard) - && let Some(b0) = find_bool_lit(first_expr) - && let Some(b1) = find_bool_lit(last_expr) + && let Some(b0) = find_bool_lit(first_arm.body) + && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_guard.is_none() || middle_arms.is_empty()) - && first_attrs.is_empty() + && (first_arm.guard.is_none() || middle_arms.is_empty()) + && cx.tcx.hir_attrs(first_arm.hir_id).is_empty() && middle_arms.iter().all(|arm| { cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) }) { - if !is_wild(last_pat) { + if !is_wild(last_arm.pat) { return false; } @@ -102,7 +99,7 @@ pub(super) fn check_match<'tcx>( .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; - let pat_and_guard = if let Some(g) = first_guard { + let pat_and_guard = if let Some(g) = first_arm.guard { format!( "{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability) From 05359084991fc484539db87cf9e70665d1ccb5dd Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 21:33:24 +0200 Subject: [PATCH 1487/1900] final refactor and docs --- .../src/matches/match_like_matches.rs | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 89da55d4fe742..b5f631e8fea33 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -72,11 +72,65 @@ pub(super) fn check_match<'tcx>( && let Some(b0) = find_bool_lit(first_arm.body) && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_arm.guard.is_none() || middle_arms.is_empty()) - && cx.tcx.hir_attrs(first_arm.hir_id).is_empty() - && middle_arms.iter().all(|arm| { - cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) - }) + // We handle two cases: + && ( + // - There are no middle arms, i.e., 2 arms in total + // + // In that case, the first arm may or may not have a guard, because this: + // ```rs + // match e { + // Either::Left $(if $guard)|+ => true, // or `false`, but then we'll need `!matches!(..)` + // _ => false, + // } + // ``` + // can always become this: + // ```rs + // matches!(e, Either::Left $(if $guard)|+) + // ``` + middle_arms.is_empty() + + // - (added in #6216) There are middle arms + // + // In that case, neither they nor the first arm may have guards + // -- otherwise, they couldn't be combined into an or-pattern in `matches!` + // + // This: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => true, + // _ /* matches `Either3::Third` */ => false, + // } + // ``` + // can become this: + // ```rs + // matches!(e, Either3::First | Either3::Second) + // ``` + // + // But this: + // ```rs + // match e { + // Either3::First if X => true, + // Either3::Second => true, + // _ => false, + // } + // ``` + // cannot be transformed. + // + // We set an additional constraint of all of them needing to return the same bool, + // so we don't lint things like: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => false, + // _ => false, + // } + // ``` + // This is not *strictly* necessary, but it simplifies the logic a bit + || arms_without_last.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) + ) { if !is_wild(last_arm.pat) { return false; From 204508d9282c1e5474306e3b2647efcfb383985d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 9 Oct 2025 19:53:49 +0300 Subject: [PATCH 1488/1900] resolve: Use primitives for conditional mutability more consistently --- .../rustc_resolve/src/build_reduced_graph.rs | 5 +- compiler/rustc_resolve/src/ident.rs | 2 +- compiler/rustc_resolve/src/imports.rs | 16 ++---- compiler/rustc_resolve/src/lib.rs | 53 +++++++++++-------- compiler/rustc_resolve/src/macros.rs | 21 +++----- 5 files changed, 45 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c10b6ca7e71b5..a0cb6c4461561 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -87,13 +87,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - // FIXME(batched): Will be fixed in batched resolution. parent.underscore_disambiguator.update_unchecked(|d| d + 1); parent.underscore_disambiguator.get() }); if self .resolution_or_default(parent, key) - .borrow_mut() + .borrow_mut_unchecked() .non_glob_binding .replace(binding) .is_some() @@ -499,7 +498,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if !type_ns_only || ns == TypeNS { let key = BindingKey::new(target, ns); this.resolution_or_default(current_module, key) - .borrow_mut() + .borrow_mut(this) .single_imports .insert(import); } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 3931c3c75f9fb..2a195c8068dac 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -891,7 +891,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // the exclusive access infinite recursion will crash the compiler with stack overflow. let resolution = &*self .resolution_or_default(module, key) - .try_borrow_mut() + .try_borrow_mut_unchecked() .map_err(|_| (Determined, Weak::No))?; // If the primary binding is unusable, search further and return the shadowed glob diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4fb2a51bb89c0..f98aaecea18ce 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -320,7 +320,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && (vis == import_vis || max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx))) { - // FIXME(batched): Will be fixed in batched import resolution. max_vis.set_unchecked(Some(vis.expect_local())) } @@ -350,7 +349,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - // FIXME(batched): Will be fixed in batched resolution. module.underscore_disambiguator.update_unchecked(|d| d + 1); module.underscore_disambiguator.get() }); @@ -470,7 +468,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // 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, warn_ambiguity) = { - let resolution = &mut *self.resolution_or_default(module, key).borrow_mut(); + let resolution = &mut *self.resolution_or_default(module, key).borrow_mut_unchecked(); let old_binding = resolution.binding(); let t = f(self, resolution); @@ -553,12 +551,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Resolves all imports for the crate. This method performs the fixed- /// point iteration. pub(crate) fn resolve_imports(&mut self) { - self.assert_speculative = true; let mut prev_indeterminate_count = usize::MAX; let mut indeterminate_count = self.indeterminate_imports.len() * 3; while indeterminate_count < prev_indeterminate_count { prev_indeterminate_count = indeterminate_count; indeterminate_count = 0; + self.assert_speculative = true; for import in mem::take(&mut self.indeterminate_imports) { let import_indeterminate_count = self.cm().resolve_import(import); indeterminate_count += import_indeterminate_count; @@ -567,8 +565,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => self.indeterminate_imports.push(import), } } + self.assert_speculative = false; } - self.assert_speculative = false; } pub(crate) fn finalize_imports(&mut self) { @@ -862,15 +860,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - // FIXME(batched): Will be fixed in batched import resolution. import.imported_module.set_unchecked(Some(module)); let (source, target, bindings, type_ns_only) = match import.kind { ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => { (source, target, bindings, type_ns_only) } ImportKind::Glob { .. } => { - // FIXME: Use mutable resolver directly as a hack, this should be an output of - // speculative resolution. self.get_mut_unchecked().resolve_glob_import(import); return 0; } @@ -906,8 +901,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // We need the `target`, `source` can be extracted. let imported_binding = this.import(binding, import); - // FIXME: Use mutable resolver directly as a hack, this should be an output of - // speculative resolution. this.get_mut_unchecked().define_binding_local( parent, target, @@ -920,8 +913,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { let key = BindingKey::new(target, ns); - // FIXME: Use mutable resolver directly as a hack, this should be an output of - // speculative resolution. this.get_mut_unchecked().update_local_resolution( parent, key, @@ -938,7 +929,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PendingBinding::Pending } }; - // FIXME(batched): Will be fixed in batched import resolution. bindings[ns].set_unchecked(binding); } }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2f45e858dc509..f918e1b1e072b 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -25,7 +25,7 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -use std::cell::{Cell, Ref, RefCell}; +use std::cell::Ref; use std::collections::BTreeSet; use std::fmt::{self}; use std::sync::Arc; @@ -572,7 +572,7 @@ impl BindingKey { } } -type Resolutions<'ra> = RefCell>>>; +type Resolutions<'ra> = CmRefCell>>>; /// One node in the tree of modules. /// @@ -595,7 +595,7 @@ struct ModuleData<'ra> { /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'ra>, /// True if this is a module from other crate that needs to be populated on access. - populate_on_access: Cell, // FIXME(parallel): Use an atomic in parallel import resolution + populate_on_access: CacheCell, /// Used to disambiguate underscore items (`const _: T = ...`) in the module. underscore_disambiguator: CmCell, @@ -658,7 +658,7 @@ impl<'ra> ModuleData<'ra> { parent, kind, lazy_resolutions: Default::default(), - populate_on_access: Cell::new(is_foreign), + populate_on_access: CacheCell::new(is_foreign), underscore_disambiguator: CmCell::new(0), unexpanded_invocations: Default::default(), no_implicit_prelude, @@ -1034,7 +1034,7 @@ struct ExternPreludeEntry<'ra> { /// `flag_binding` is `None`, or when `extern crate` introducing `item_binding` used renaming. item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>, /// Binding from an `--extern` flag, lazily populated on first use. - flag_binding: Option, /* finalized */ bool)>>, + flag_binding: Option, /* finalized */ bool)>>, } impl ExternPreludeEntry<'_> { @@ -1045,7 +1045,7 @@ impl ExternPreludeEntry<'_> { fn flag() -> Self { ExternPreludeEntry { item_binding: None, - flag_binding: Some(Cell::new((PendingBinding::Pending, false))), + flag_binding: Some(CacheCell::new((PendingBinding::Pending, false))), } } } @@ -1150,7 +1150,7 @@ pub struct Resolver<'ra, 'tcx> { /// Eagerly populated map of all local non-block modules. local_module_map: FxIndexMap>, /// Lazily populated cache of modules loaded from external crates. - extern_module_map: RefCell>>, + extern_module_map: CacheRefCell>>, binding_parent_modules: FxHashMap, Module<'ra>>, /// Maps glob imports to the names of items actually imported. @@ -1186,7 +1186,7 @@ pub struct Resolver<'ra, 'tcx> { /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap, /// Lazily populated cache of macro definitions loaded from external crates. - extern_macro_map: RefCell>, + extern_macro_map: CacheRefCell>, dummy_ext_bang: Arc, dummy_ext_derive: Arc, non_macro_attr: &'ra MacroData, @@ -1197,11 +1197,10 @@ pub struct Resolver<'ra, 'tcx> { unused_macro_rules: FxIndexMap>, proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. - // FIXME: Remove interior mutability when speculative resolution produces these as outputs. single_segment_macro_resolutions: - RefCell, Option>, Option)>>, + CmRefCell, Option>, Option)>>, multi_segment_macro_resolutions: - RefCell, Span, MacroKind, ParentScope<'ra>, Option, Namespace)>>, + CmRefCell, Span, MacroKind, ParentScope<'ra>, Option, Namespace)>>, builtin_attrs: Vec<(Ident, ParentScope<'ra>)>, /// `derive(Copy)` marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global @@ -1301,7 +1300,7 @@ pub struct Resolver<'ra, 'tcx> { pub struct ResolverArenas<'ra> { modules: TypedArena>, imports: TypedArena>, - name_resolutions: TypedArena>>, + name_resolutions: TypedArena>>, ast_paths: TypedArena, macros: TypedArena, dropless: DroplessArena, @@ -1363,11 +1362,11 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_import(&'ra self, import: ImportData<'ra>) -> Import<'ra> { Interned::new_unchecked(self.imports.alloc(import)) } - fn alloc_name_resolution(&'ra self) -> &'ra RefCell> { + fn alloc_name_resolution(&'ra self) -> &'ra CmRefCell> { self.name_resolutions.alloc(Default::default()) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { - self.dropless.alloc(Cell::new(scope)) + self.dropless.alloc(CacheCell::new(scope)) } fn alloc_macro_rules_binding( &'ra self, @@ -1978,7 +1977,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { if module.populate_on_access.get() { - // FIXME(batched): Will be fixed in batched import resolution. module.populate_on_access.set(false); self.build_reduced_graph_external(module); } @@ -1997,9 +1995,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &self, module: Module<'ra>, key: BindingKey, - ) -> &'ra RefCell> { + ) -> &'ra CmRefCell> { self.resolutions(module) - .borrow_mut() + .borrow_mut_unchecked() .entry(key) .or_insert_with(|| self.arenas.alloc_name_resolution()) } @@ -2514,6 +2512,13 @@ pub fn provide(providers: &mut Providers) { /// Prefer constructing it through [`Resolver::cm`] to ensure correctness. type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; +// FIXME: These are cells for caches that can be populated even during speculative resolution, +// and should be replaced with mutexes, atomics, or other synchronized data when migrating to +// parallel name resolution. +use std::cell::{Cell as CacheCell, RefCell as CacheRefCell}; + +// FIXME: `*_unchecked` methods in the module below should be eliminated in the process +// of migration to parallel name resolution. mod ref_mut { use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; use std::fmt; @@ -2581,7 +2586,6 @@ mod ref_mut { } impl Clone for CmCell { - #[inline] fn clone(&self) -> CmCell { CmCell::new(self.get()) } @@ -2624,13 +2628,11 @@ mod ref_mut { CmRefCell(RefCell::new(value)) } - #[inline] #[track_caller] pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { self.0.borrow_mut() } - #[inline] #[track_caller] pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> RefMut<'_, T> { if r.assert_speculative { @@ -2639,16 +2641,23 @@ mod ref_mut { self.borrow_mut_unchecked() } - #[inline] #[track_caller] pub(crate) fn try_borrow_mut_unchecked(&self) -> Result, BorrowMutError> { self.0.try_borrow_mut() } - #[inline] #[track_caller] pub(crate) fn borrow(&self) -> Ref<'_, T> { self.0.borrow() } } + + impl CmRefCell { + pub(crate) fn take<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> T { + if r.assert_speculative { + panic!("Not allowed to mutate a CmRefCell during speculative resolution"); + } + self.0.take() + } + } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c50dfd41b51c8..de71568e454a7 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1,7 +1,6 @@ //! A bunch of methods and structures more or less related to resolving macros and //! interface provided by `Resolver` to macro expander. -use std::cell::Cell; use std::mem; use std::sync::Arc; @@ -40,9 +39,9 @@ use crate::errors::{ }; use crate::imports::Import; use crate::{ - BindingKey, CmResolver, DeriveData, Determinacy, Finalize, InvocationParent, MacroData, - ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, - ResolutionError, Resolver, ScopeSet, Segment, Used, + BindingKey, CacheCell, CmResolver, DeriveData, Determinacy, Finalize, InvocationParent, + MacroData, ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, + PathResult, ResolutionError, Resolver, ScopeSet, Segment, Used, }; type Res = def::Res; @@ -79,7 +78,7 @@ pub(crate) enum MacroRulesScope<'ra> { /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// which usually grow linearly with the number of macro invocations /// in a module (including derives) and hurt performance. -pub(crate) type MacroRulesScopeRef<'ra> = &'ra Cell>; +pub(crate) type MacroRulesScopeRef<'ra> = &'ra CacheCell>; /// Macro namespace is separated into two sub-namespaces, one for bang macros and /// one for attribute-like macros (attributes, derives). @@ -775,8 +774,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; if trace { - // FIXME: Should be an output of Speculative Resolution. - self.multi_segment_macro_resolutions.borrow_mut().push(( + self.multi_segment_macro_resolutions.borrow_mut(&self).push(( path, path_span, kind, @@ -803,8 +801,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if trace { - // FIXME: Should be an output of Speculative Resolution. - self.single_segment_macro_resolutions.borrow_mut().push(( + self.single_segment_macro_resolutions.borrow_mut(&self).push(( path[0].ident, kind, *parent_scope, @@ -870,8 +867,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - // FIXME: Should be an output of Speculative Resolution. - let macro_resolutions = self.multi_segment_macro_resolutions.take(); + let macro_resolutions = self.multi_segment_macro_resolutions.take(self); for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions { // FIXME: Path resolution will ICE if segment IDs present. for seg in &mut path { @@ -948,8 +944,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - // FIXME: Should be an output of Speculative Resolution. - let macro_resolutions = self.single_segment_macro_resolutions.take(); + let macro_resolutions = self.single_segment_macro_resolutions.take(self); for (ident, kind, parent_scope, initial_binding, sugg_span) in macro_resolutions { match self.cm().resolve_ident_in_scope_set( ident, From 343e7806ea87bdeb1025af39d30bb0e94a34492a Mon Sep 17 00:00:00 2001 From: blyxyas Date: Thu, 9 Oct 2025 23:54:11 +0200 Subject: [PATCH 1489/1900] Mark blyxyas on vacation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index b2fb50918f583..57ff3dc352c8e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,6 +60,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB%20users_on_vacation%20=%20["matthiaskrgr", "Manishearth", + "blyxyas", ] [assign.owners] From b71fe9254d75707234349a2f0f4f50fad51389cc Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Mon, 25 Aug 2025 17:22:20 -0400 Subject: [PATCH 1490/1900] Check structs and enums for use_self --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++ clippy_config/src/conf.rs | 3 + clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/use_self.rs | 19 ++- clippy_utils/src/consts.rs | 8 +- clippy_utils/src/sugg.rs | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/use_self/default/clippy.toml | 0 tests/ui-toml/use_self/disabled/clippy.toml | 1 + tests/ui-toml/use_self/use_self.default.fixed | 17 +++ .../ui-toml/use_self/use_self.default.stderr | 17 +++ .../ui-toml/use_self/use_self.disabled.fixed | 17 +++ .../ui-toml/use_self/use_self.disabled.stderr | 11 ++ tests/ui-toml/use_self/use_self.rs | 17 +++ tests/ui/use_self_structs.fixed | 134 ++++++++++++++++++ tests/ui/use_self_structs.rs | 134 ++++++++++++++++++ tests/ui/use_self_structs.stderr | 77 ++++++++++ 18 files changed, 463 insertions(+), 10 deletions(-) create mode 100644 tests/ui-toml/use_self/default/clippy.toml create mode 100644 tests/ui-toml/use_self/disabled/clippy.toml create mode 100644 tests/ui-toml/use_self/use_self.default.fixed create mode 100644 tests/ui-toml/use_self/use_self.default.stderr create mode 100644 tests/ui-toml/use_self/use_self.disabled.fixed create mode 100644 tests/ui-toml/use_self/use_self.disabled.stderr create mode 100644 tests/ui-toml/use_self/use_self.rs create mode 100644 tests/ui/use_self_structs.fixed create mode 100644 tests/ui/use_self_structs.rs create mode 100644 tests/ui/use_self_structs.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aaf617f5b151..b2e9f6d1dd3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7052,6 +7052,7 @@ Released 2018-09-13 [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b2ba19631f133..f0b2feca6a014 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -927,6 +927,16 @@ exported visibility, or whether they are marked as "pub". * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) +## `recursive-self-in-type-definitions` +Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) + + ## `semicolon-inside-block-ignore-singleline` Whether to lint only if it's multiline. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 843aa6a40f098..9902065422fbd 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -809,6 +809,9 @@ define_Conf! { /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + #[lints(use_self)] + recursive_self_in_type_definitions: bool = true, /// Whether to lint only if it's multiline. #[lints(semicolon_inside_block)] semicolon_inside_block_ignore_singleline: bool = false, diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 02f87512966ba..44c4d7a31ff31 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -224,7 +224,7 @@ enum PatState<'a> { /// A std enum we know won't be extended. Tracks the states of each variant separately. /// /// This is not used for `Option` since it uses the current pattern to track its state. - StdEnum(&'a mut [PatState<'a>]), + StdEnum(&'a mut [Self]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9d5be922f43f8..f46dedf1261e9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -58,6 +58,7 @@ declare_clippy_lint! { pub struct UseSelf { msrv: Msrv, stack: Vec, + recursive_self_in_type_definitions: bool, } impl UseSelf { @@ -65,6 +66,7 @@ impl UseSelf { Self { msrv: conf.msrv, stack: Vec::new(), + recursive_self_in_type_definitions: conf.recursive_self_in_type_definitions, } } } @@ -84,10 +86,10 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - // We push the self types of `impl`s on a stack here. Only the top type on the stack is - // relevant for linting, since this is the self type of the `impl` we're currently in. To - // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that - // we're in an `impl` or nested item, that we don't want to lint + // We push the self types of items on a stack here. Only the top type on the stack is + // relevant for linting, since this is the self type of the item we're currently in. To + // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that + // we're in an item or nested item that we don't want to lint let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args @@ -112,6 +114,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { impl_id: item.owner_id.def_id, types_to_skip, } + } else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind + && self.recursive_self_in_type_definitions + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + StackItem::Check { + impl_id: item.owner_id.def_id, + types_to_skip: FxHashSet::default(), + } } else { StackItem::NoCheck }; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index e03ba2f73b437..6e2cabd5e61bf 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -50,15 +50,15 @@ pub enum Constant { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box), + Ref(Box), /// A literal with syntax error. Err, } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index a63333c9b48f9..581c2b02839dc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -33,7 +33,7 @@ pub enum Sugg<'a> { /// or `-`, but only if the type with and without the operator is kept identical. /// It means that doubling the operator can be used to remove it instead, in /// order to provide better suggestions. - UnOp(UnOp, Box>), + UnOp(UnOp, Box), } /// Literal constant `0`, for convenience. diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 20aeb4bb8498e..b96a5486c72bd 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -66,6 +66,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -161,6 +162,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -256,6 +258,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold diff --git a/tests/ui-toml/use_self/default/clippy.toml b/tests/ui-toml/use_self/default/clippy.toml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/ui-toml/use_self/disabled/clippy.toml b/tests/ui-toml/use_self/disabled/clippy.toml new file mode 100644 index 0000000000000..866cc5c624b5f --- /dev/null +++ b/tests/ui-toml/use_self/disabled/clippy.toml @@ -0,0 +1 @@ +recursive-self-in-type-definitions = false diff --git a/tests/ui-toml/use_self/use_self.default.fixed b/tests/ui-toml/use_self/use_self.default.fixed new file mode 100644 index 0000000000000..288e304c60a8e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.default.stderr b/tests/ui-toml/use_self/use_self.default.stderr new file mode 100644 index 0000000000000..34cfdfd938aa1 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.stderr @@ -0,0 +1,17 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:10:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/use_self/use_self.disabled.fixed b/tests/ui-toml/use_self/use_self.disabled.fixed new file mode 100644 index 0000000000000..227606e69005e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.disabled.stderr b/tests/ui-toml/use_self/use_self.disabled.stderr new file mode 100644 index 0000000000000..1801744f0d417 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.stderr @@ -0,0 +1,11 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/use_self/use_self.rs b/tests/ui-toml/use_self/use_self.rs new file mode 100644 index 0000000000000..d20006d4df1ae --- /dev/null +++ b/tests/ui-toml/use_self/use_self.rs @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Basic) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui/use_self_structs.fixed b/tests/ui/use_self_structs.fixed new file mode 100644 index 0000000000000..bd7bc3e0c1f69 --- /dev/null +++ b/tests/ui/use_self_structs.fixed @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.rs b/tests/ui/use_self_structs.rs new file mode 100644 index 0000000000000..624f158164ab0 --- /dev/null +++ b/tests/ui/use_self_structs.rs @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.stderr b/tests/ui/use_self_structs.stderr new file mode 100644 index 0000000000000..766d1d4fd2c05 --- /dev/null +++ b/tests/ui/use_self_structs.stderr @@ -0,0 +1,77 @@ +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:7:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:17:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:38:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:62:22 + | +LL | left: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:64:23 + | +LL | right: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:75:23 + | +LL | right: Option>, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:80:33 + | +LL | flag: Option>>>, + | ^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:88:25 + | +LL | struct Tuple(Option>); + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:97:46 + | +LL | flag: Vec>>>>>>, + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:108:20 + | +LL | flag: Wrappers, + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:117:23 + | +LL | flag: [Option>>; N], + | ^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:127:14 + | +LL | Cons(Box), + | ^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 12 previous errors + From 50e1884aa101ddde42122221a4d89017ef32ca2d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 8 Oct 2025 06:54:28 -0700 Subject: [PATCH 1491/1900] Add a test for the cold attribute This adds a test for the cold attribute to verify that it actually does something, and that it applies correctly in all the positions it is expected to work. --- tests/codegen-llvm/cold-attribute.rs | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/codegen-llvm/cold-attribute.rs diff --git a/tests/codegen-llvm/cold-attribute.rs b/tests/codegen-llvm/cold-attribute.rs new file mode 100644 index 0000000000000..92b4280eaf27e --- /dev/null +++ b/tests/codegen-llvm/cold-attribute.rs @@ -0,0 +1,78 @@ +// Checks that the cold attribute adds the llvm cold attribute. +// +//@ reference: attributes.codegen.cold.intro +//@ reference: attributes.codegen.cold.trait +//@ edition:2024 +//@ compile-flags: -Copt-level=0 + +#![crate_type = "lib"] + +// CHECK-LABEL: ; cold_attribute::free_function +// CHECK-NEXT: Function Attrs: cold {{.*}} +#[cold] +pub fn free_function() {} + +// CHECK-LABEL: ; cold_attribute::async_block +// CHECK-NEXT: Function Attrs: cold {{.*}} +#[cold] +pub async fn async_block() { + async fn x(f: impl Future) { + f.await; + } + x( + // CHECK-LABEL: ; cold_attribute::async_block::{{{{closure}}}}::{{{{closure}}}} + // CHECK-NEXT: Function Attrs: cold {{.*}} + #[cold] + async {}, + ) + .await; +} + +pub fn closure() { + fn x(f: impl Fn()) { + f() + } + x( + // CHECK-LABEL: ; cold_attribute::closure::{{{{closure}}}} + // CHECK-NEXT: Function Attrs: cold {{.*}} + #[cold] + || {}, + ); +} + +pub struct S; + +impl S { + // CHECK-LABEL: ; cold_attribute::S::method + // CHECK-NEXT: Function Attrs: cold {{.*}} + #[cold] + pub fn method(&self) {} +} + +pub trait Trait { + // CHECK-LABEL: ; cold_attribute::Trait::trait_fn + // CHECK-NEXT: Function Attrs: cold {{.*}} + #[cold] + fn trait_fn(&self) {} + + #[cold] + fn trait_fn_overridden(&self) {} + + fn impl_fn(&self); +} + +impl Trait for S { + // CHECK-LABEL: ; ::impl_fn + // CHECK-NEXT: Function Attrs: cold {{.*}} + #[cold] + fn impl_fn(&self) { + self.trait_fn(); + } + + // This does not have #[cold], and does not inherit the cold attribute from the trait. + // CHECK-LABEL: ; ::trait_fn_overridden + // CHECK: ; Function Attrs: + // CHECK-NOT: cold + // CHECK: define + fn trait_fn_overridden(&self) {} +} From 45ca9b5a4c989b6d0d31eb737cc939d2a6ca9cad Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 7 Oct 2025 09:46:40 +0800 Subject: [PATCH 1492/1900] Fix empty closure completion analysis Example --- ```rust fn foo() { bar(|| $0); } fn bar(f: impl FnOnce() -> u32) {} ``` **Before this PR**: ``` ty: impl FnOnce() -> u32, name: ? ``` **After this PR**: ``` ty: u32, name: ? ``` --- .../crates/ide-completion/src/context/analysis.rs | 15 ++++++++++++--- .../crates/ide-completion/src/context/tests.rs | 13 ++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6cd098462f4db..0b8ba808e53bb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -562,10 +562,10 @@ fn analyze<'db>( /// Calculate the expected type and name of the cursor position. fn expected_type_and_name<'db>( sema: &Semantics<'db, RootDatabase>, - token: &SyntaxToken, + self_token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_special_biased_token_at_trivia(token.clone()); + let token = prev_special_biased_token_at_trivia(self_token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -755,7 +755,15 @@ fn expected_type_and_name<'db>( .map(|c| (Some(c.return_type()), None)) .unwrap_or((None, None)) }, - ast::ParamList(_) => (None, None), + ast::ParamList(it) => { + let closure = it.syntax().parent().and_then(ast::ClosureExpr::cast); + let ty = closure + .filter(|_| it.syntax().text_range().end() <= self_token.text_range().start()) + .and_then(|it| sema.type_of_expr(&it.into())); + ty.and_then(|ty| ty.original.as_callable(sema.db)) + .map(|c| (Some(c.return_type()), None)) + .unwrap_or((None, None)) + }, ast::Stmt(_) => (None, None), ast::Item(_) => (None, None), _ => { @@ -1940,6 +1948,7 @@ fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { | T![|=] | T![&=] | T![^=] + | T![|] | T![return] | T![break] | T![continue] = prev.kind() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 24647ab6279dc..e798f3b23af4c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -373,7 +373,6 @@ fn foo() -> u32 { #[test] fn expected_type_closure_param_return() { - // FIXME: make this work with `|| $0` check_expected_type_and_name( r#" //- minicore: fn @@ -381,6 +380,18 @@ fn foo() { bar(|| a$0); } +fn bar(f: impl FnOnce() -> u32) {} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +//- minicore: fn +fn foo() { + bar(|| $0); +} + fn bar(f: impl FnOnce() -> u32) {} "#, expect![[r#"ty: u32, name: ?"#]], From afea346b3548a9f995b2aacf389dfb094d3997b4 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Fri, 10 Oct 2025 14:55:09 +0530 Subject: [PATCH 1493/1900] feat: add failing test for zero-sized memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 1 + src/tools/miri/tests/fail-dep/libc/memset_null.rs | 8 ++++++++ .../miri/tests/fail-dep/libc/memset_null.stderr | 15 +++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.stderr diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 3fd57d3c8db22..08964ba7b329f 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -837,6 +837,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { #[expect(clippy::as_conversions)] let val = val as u8; + // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. this.ptr_get_alloc_id(ptr_dest, 0)?; let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.rs b/src/tools/miri/tests/fail-dep/libc/memset_null.rs new file mode 100644 index 0000000000000..c3fa9973e762a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.rs @@ -0,0 +1,8 @@ +use std::ptr; + +// null is explicitly called out as UB in the C docs for `memset`. +fn main() { + unsafe { + libc::memset(ptr::null_mut(), 0, 0); //~ERROR: null pointer + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.stderr b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr new file mode 100644 index 0000000000000..fdc8f3a29f940 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer + --> tests/fail-dep/libc/memset_null.rs:LL:CC + | +LL | libc::memset(ptr::null_mut(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/libc/memset_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From ca8020a7c4d07d7348eced07f01841a05c327344 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 16:35:35 +0800 Subject: [PATCH 1494/1900] Fix ice 141592, change diverge assertion to struct error with delay --- compiler/rustc_hir_typeck/src/expr.rs | 10 +++++++- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 4 ++-- .../never-pattern-as-closure-param-141592.rs | 24 +++++++++++++++++++ ...ver-pattern-as-closure-param-141592.stderr | 18 ++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 tests/ui/never_type/never-pattern-as-closure-param-141592.rs create mode 100644 tests/ui/never_type/never-pattern-as-closure-param-141592.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f9cdc923670f0..8cdcacd339a46 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1817,12 +1817,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let element_ty = if !args.is_empty() { + // This shouldn't happen unless there's another error + // (e.g., never patterns in inappropriate contexts). + if self.diverges.get() != Diverges::Maybe { + self.dcx() + .struct_span_err(expr.span, "unexpected divergence state in checking array") + .delay_as_bug(); + } + let coerce_to = expected .to_option(self) .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) .unwrap_or_else(|| self.next_ty_var(expr.span)); let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); - assert_eq!(self.diverges.get(), Diverges::Maybe); + for e in args { let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 7a060cafeab1c..5983d25e802b9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -104,8 +104,8 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// the diverges flag is set to something other than `Maybe`. pub(super) diverges: Cell, - /// If one of the function arguments is a never pattern, this counts as diverging code. This - /// affect typechecking of the function body. + /// If one of the function arguments is a never pattern, this counts as diverging code. + /// This affect typechecking of the function body. pub(super) function_diverges_because_of_empty_arguments: Cell, /// Whether the currently checked node is the whole body of the function. diff --git a/tests/ui/never_type/never-pattern-as-closure-param-141592.rs b/tests/ui/never_type/never-pattern-as-closure-param-141592.rs new file mode 100644 index 0000000000000..78939199d09cc --- /dev/null +++ b/tests/ui/never_type/never-pattern-as-closure-param-141592.rs @@ -0,0 +1,24 @@ +#![feature(never_patterns)] +#![allow(incomplete_features)] + +enum Never {} + +fn example(x: Never) -> [i32; 1] { + let ! = x; + [1] +} + +fn function_param_never(!: Never) -> [i32; 1] { + [1] +} + +fn generic_never(!: T) -> [i32; 1] //~ ERROR mismatched types +where + T: Copy, +{ + [1] +} + +fn main() { + let _ = "12".lines().map(|!| [1]); //~ ERROR mismatched types +} diff --git a/tests/ui/never_type/never-pattern-as-closure-param-141592.stderr b/tests/ui/never_type/never-pattern-as-closure-param-141592.stderr new file mode 100644 index 0000000000000..ce5d3a57862bf --- /dev/null +++ b/tests/ui/never_type/never-pattern-as-closure-param-141592.stderr @@ -0,0 +1,18 @@ +error: mismatched types + --> $DIR/never-pattern-as-closure-param-141592.rs:15:21 + | +LL | fn generic_never(!: T) -> [i32; 1] + | ^ a never pattern must be used on an uninhabited type + | + = note: the matched value is of type `T` + +error: mismatched types + --> $DIR/never-pattern-as-closure-param-141592.rs:23:31 + | +LL | let _ = "12".lines().map(|!| [1]); + | ^ a never pattern must be used on an uninhabited type + | + = note: the matched value is of type `str` + +error: aborting due to 2 previous errors + From 690071c28a6fbce6cc1f6d958a8f60a5cd1b80be Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 20:14:21 +0800 Subject: [PATCH 1495/1900] Add regression test for 147485 --- tests/crashes/147485-2.rs | 16 +++++++++ tests/crashes/147485.rs | 14 ++++++++ ...buginfo_locals.DestinationPropagation.diff | 35 ++++++++++++++++++ tests/mir-opt/debuginfo/dest_prop.rs | 36 +++++++++++++++++++ ...debuginfo_locals.ReferencePropagation.diff | 33 +++++++++++++++++ tests/mir-opt/debuginfo/ref_prop.rs | 26 ++++++++++++++ 6 files changed, 160 insertions(+) create mode 100644 tests/crashes/147485-2.rs create mode 100644 tests/crashes/147485.rs create mode 100644 tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff create mode 100644 tests/mir-opt/debuginfo/dest_prop.rs create mode 100644 tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff create mode 100644 tests/mir-opt/debuginfo/ref_prop.rs diff --git a/tests/crashes/147485-2.rs b/tests/crashes/147485-2.rs new file mode 100644 index 0000000000000..da092a3af76aa --- /dev/null +++ b/tests/crashes/147485-2.rs @@ -0,0 +1,16 @@ +//@ known-bug: #147485 +//@ compile-flags: -g -O + +#![crate_type = "lib"] + +pub fn foo(a: bool, b: bool) -> bool { + let mut c = &a; + if false { + return *c; + } + let d = b && a; + if d { + c = &b; + } + b +} diff --git a/tests/crashes/147485.rs b/tests/crashes/147485.rs new file mode 100644 index 0000000000000..86f3705e636b4 --- /dev/null +++ b/tests/crashes/147485.rs @@ -0,0 +1,14 @@ +//@ known-bug: #147485 +//@ compile-flags: -g -O + +#![crate_type = "lib"] + +pub fn f(x: *const usize) -> &'static usize { + let mut a = unsafe { &*x }; + a = unsafe { &*x }; + a +} + +pub fn g() { + f(&0); +} diff --git a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff new file mode 100644 index 0000000000000..6f3233d85c9fc --- /dev/null +++ b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff @@ -0,0 +1,35 @@ +- // MIR for `remap_debuginfo_locals` before DestinationPropagation ++ // MIR for `remap_debuginfo_locals` after DestinationPropagation + + fn remap_debuginfo_locals(_1: bool, _2: &bool) -> &bool { +- debug c => _3; ++ debug c => _2; + let mut _0: &bool; + let mut _3: &bool; + let mut _4: bool; + + bb0: { + // DBG: _3 = &_1; +- StorageLive(_4); +- _4 = copy _1; +- _3 = copy _2; +- switchInt(copy _4) -> [1: bb1, otherwise: bb2]; ++ nop; ++ nop; ++ nop; ++ switchInt(copy _1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb2; + } + + bb2: { +- StorageDead(_4); +- _0 = copy _3; ++ nop; ++ _0 = copy _2; + return; + } + } + diff --git a/tests/mir-opt/debuginfo/dest_prop.rs b/tests/mir-opt/debuginfo/dest_prop.rs new file mode 100644 index 0000000000000..9c9d95ebfb3f3 --- /dev/null +++ b/tests/mir-opt/debuginfo/dest_prop.rs @@ -0,0 +1,36 @@ +// skip-filecheck +//@ test-mir-pass: DestinationPropagation +//@ compile-flags: -g -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR dest_prop.remap_debuginfo_locals.DestinationPropagation.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn remap_debuginfo_locals(a: bool, b: &bool) -> &bool { + mir! { + let _3: &bool; + let _4: bool; + debug c => _3; + { + _3 = &a; + StorageLive(_4); + _4 = a; + _3 = b; + match _4 { + true => bb1, + _ => bb2, + } + } + bb1 = { + Goto(bb2) + } + bb2 = { + StorageDead(_4); + RET = _3; + Return() + } + } +} diff --git a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff new file mode 100644 index 0000000000000..87941aadab4dd --- /dev/null +++ b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff @@ -0,0 +1,33 @@ +- // MIR for `remap_debuginfo_locals` before ReferencePropagation ++ // MIR for `remap_debuginfo_locals` after ReferencePropagation + + fn remap_debuginfo_locals() -> () { + let mut _0: (); + let _1: &usize; + let mut _2: *const usize; + let _3: &usize; + let _4: usize; + let mut _5: &usize; + scope 1 (inlined foo) { +- debug a => _1; ++ debug a => _5; + } + + bb0: { +- StorageLive(_1); +- StorageLive(_2); +- StorageLive(_3); + _5 = const remap_debuginfo_locals::promoted[0]; +- _3 = &(*_5); +- _2 = &raw const (*_3); +- // DBG: _1 = &(*_2); +- _1 = &(*_2); +- StorageDead(_2); +- StorageDead(_3); +- StorageDead(_1); ++ // DBG: _1 = &(*_5); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/ref_prop.rs b/tests/mir-opt/debuginfo/ref_prop.rs new file mode 100644 index 0000000000000..5f2f1ce09c707 --- /dev/null +++ b/tests/mir-opt/debuginfo/ref_prop.rs @@ -0,0 +1,26 @@ +// skip-filecheck +//@ test-mir-pass: ReferencePropagation +//@ compile-flags: -g -Zub_checks=false -Zinline-mir -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR ref_prop.remap_debuginfo_locals.ReferencePropagation.diff +pub fn remap_debuginfo_locals() { + foo(&0); +} + +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +#[inline] +fn foo(x: *const usize) -> &'static usize { + mir! { + debug a => RET; + { + RET = &*x; + RET = &*x; + Return() + } + } +} From 9462e7301fb831f011b927494fd74c9f31c24b9b Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 21:32:23 +0800 Subject: [PATCH 1496/1900] MIR validation: ensure that debuginfo records are not emitted for locals that are not in debuginfo --- compiler/rustc_mir_transform/src/validate.rs | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 95873484b6529..6790dda780c5d 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{ self, CoroutineArgsExt, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Upcast, Variance, }; use rustc_middle::{bug, span_bug}; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_trait_selection::traits::ObligationCtxt; use crate::util::{self, is_within_packed}; @@ -80,6 +81,11 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.fail(location, msg); } + // Ensure that debuginfo records are not emitted for locals that are not in debuginfo. + for (location, msg) in validate_debuginfos(body) { + cfg_checker.fail(location, msg); + } + if let MirPhase::Runtime(_) = body.phase && let ty::InstanceKind::Item(_) = body.source.instance && body.has_free_regions() @@ -1595,3 +1601,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_terminator(terminator, location); } } + +pub(super) fn validate_debuginfos<'tcx>(body: &Body<'tcx>) -> Vec<(Location, String)> { + let mut debuginfo_checker = + DebuginfoChecker { debuginfo_locals: debuginfo_locals(body), failures: Vec::new() }; + debuginfo_checker.visit_body(body); + debuginfo_checker.failures +} + +struct DebuginfoChecker { + debuginfo_locals: DenseBitSet, + failures: Vec<(Location, String)>, +} + +impl<'tcx> Visitor<'tcx> for DebuginfoChecker { + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &StmtDebugInfo<'tcx>, + location: Location, + ) { + let local = match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => *local, + }; + if !self.debuginfo_locals.contains(local) { + self.failures.push((location, format!("{local:?} is not in debuginfo"))); + } + } +} From b2e81b00e5cd6f0ad7f883413b983b2c6e25c25e Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 21:53:05 +0800 Subject: [PATCH 1497/1900] Replace locals in debuginfo records during ref_prop --- compiler/rustc_middle/src/mir/visit.rs | 8 ++-- compiler/rustc_mir_transform/src/ref_prop.rs | 45 +++++++++++++++---- ...debuginfo_locals.ReferencePropagation.diff | 2 +- tests/mir-opt/debuginfo/ref_prop.rs | 6 ++- .../debuginfo/ref_prop_debuginfo-147485.rs} | 4 +- 5 files changed, 49 insertions(+), 16 deletions(-) rename tests/{crashes/147485.rs => ui/debuginfo/ref_prop_debuginfo-147485.rs} (79%) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9654e189f2edf..29cf3977dc807 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1098,6 +1098,10 @@ macro_rules! super_body { } } + for var_debug_info in &$($mutability)? $body.var_debug_info { + $self.visit_var_debug_info(var_debug_info); + } + for (bb, data) in basic_blocks_iter!($body, $($mutability, $invalidate)?) { $self.visit_basic_block_data(bb, data); } @@ -1127,10 +1131,6 @@ macro_rules! super_body { ); } - for var_debug_info in &$($mutability)? $body.var_debug_info { - $self.visit_var_debug_info(var_debug_info); - } - $self.visit_span($(& $mutability)? $body.span); if let Some(required_consts) = &$($mutability)? $body.required_consts { diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index deb0a146476c3..c5b53dbbd309b 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -302,6 +302,7 @@ fn compute_replacement<'tcx>( return Replacer { tcx, targets: finder.targets, + remap_var_debug_infos: IndexVec::from_elem(None, body.local_decls()), storage_to_remove, allowed_replacements, any_replacement: false, @@ -381,6 +382,7 @@ fn fully_replaceable_locals(ssa: &SsaLocals) -> DenseBitSet { struct Replacer<'tcx> { tcx: TyCtxt<'tcx>, targets: IndexVec>, + remap_var_debug_infos: IndexVec>, storage_to_remove: DenseBitSet, allowed_replacements: FxHashSet<(Local, Location)>, any_replacement: bool, @@ -392,21 +394,45 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place - // and it's a reborrow: see through it - while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value + if let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() - && let Value::Pointer(target, _) = self.targets[place.local] - && let &[PlaceElem::Deref] = &target.projection[..] { - *place = Place::from(target.local); - self.any_replacement = true; + let mut new_local = place.local; + + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it + while let Value::Pointer(target, _) = self.targets[new_local] + && let &[PlaceElem::Deref] = &target.projection[..] + { + new_local = target.local; + } + if place.local != new_local { + self.remap_var_debug_infos[place.local] = Some(new_local); + place.local = new_local; + + self.any_replacement = true; + } } // Simplify eventual projections left inside `debuginfo`. self.super_var_debug_info(debuginfo); } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &mut StmtDebugInfo<'tcx>, + location: Location, + ) { + let local = match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => local, + }; + if let Some(target) = self.remap_var_debug_infos[*local] { + *local = target; + self.any_replacement = true; + } + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; @@ -437,8 +463,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { { stmt.make_nop(true); } - // Do not remove assignments as they may still be useful for debuginfo. - _ => self.super_statement(stmt, loc), + _ => {} } + // Do not remove assignments as they may still be useful for debuginfo. + self.super_statement(stmt, loc); } } diff --git a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff index 87941aadab4dd..2d93b1d842fa1 100644 --- a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff +++ b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff @@ -25,7 +25,7 @@ - StorageDead(_2); - StorageDead(_3); - StorageDead(_1); -+ // DBG: _1 = &(*_5); ++ // DBG: _5 = &(*_5); _0 = const (); return; } diff --git a/tests/mir-opt/debuginfo/ref_prop.rs b/tests/mir-opt/debuginfo/ref_prop.rs index 5f2f1ce09c707..60d68ba178a7b 100644 --- a/tests/mir-opt/debuginfo/ref_prop.rs +++ b/tests/mir-opt/debuginfo/ref_prop.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: ReferencePropagation //@ compile-flags: -g -Zub_checks=false -Zinline-mir -Zmir-enable-passes=+DeadStoreElimination-initial @@ -9,6 +8,11 @@ use std::intrinsics::mir::*; // EMIT_MIR ref_prop.remap_debuginfo_locals.ReferencePropagation.diff pub fn remap_debuginfo_locals() { + // CHECK-LABEL: fn remap_debuginfo_locals() + // CHECK: debug a => [[a:_.*]]; + // CHECK: bb0: + // CHECK-NEXT: [[a]] = const + // CHECK-NEXT: DBG: [[a]] = &(*[[a]]); foo(&0); } diff --git a/tests/crashes/147485.rs b/tests/ui/debuginfo/ref_prop_debuginfo-147485.rs similarity index 79% rename from tests/crashes/147485.rs rename to tests/ui/debuginfo/ref_prop_debuginfo-147485.rs index 86f3705e636b4..15899626aff7b 100644 --- a/tests/crashes/147485.rs +++ b/tests/ui/debuginfo/ref_prop_debuginfo-147485.rs @@ -1,6 +1,8 @@ -//@ known-bug: #147485 +//@ build-pass //@ compile-flags: -g -O +// Regression test for #147485. + #![crate_type = "lib"] pub fn f(x: *const usize) -> &'static usize { From 5ab4e18dfc73540220d015c09f2aa05bb7cfa51a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 10 Oct 2025 12:51:41 +0200 Subject: [PATCH 1498/1900] remove `#[rustc_inherit_overflow_checks]` from `is_multiple_of` --- library/core/src/num/uint_macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 752498bfbd815..b5b768cf677aa 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -3552,7 +3552,6 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "unsigned_is_multiple_of", since = "1.87.0")] #[must_use] #[inline] - #[rustc_inherit_overflow_checks] pub const fn is_multiple_of(self, rhs: Self) -> bool { match rhs { 0 => self == 0, From ddf8dd7875f9d94d7ce49f7ca87ec3cecaef6200 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 3 Oct 2025 17:03:00 +0800 Subject: [PATCH 1499/1900] Fix not applicable c-str and byte-str for raw_string Example --- Assist: `make_raw_string` ```rust fn f() { let s = $0b"random\nstring"; } ``` -> ```rust fn f() { let s = br#"random string"#; } ``` --- Assist: `make_raw_string` ```rust fn f() { let s = $0c"random\nstring"; } ``` -> ```rust fn f() { let s = cr#"random string"#; } ``` --- Assist: `add_hash` ```rust fn f() { let s = $0cr"random string"; } ``` -> ```rust fn f() { let s = cr#"random string"#; } ``` --- Assist: `remove_hash` ```rust fn f() { let s = $0cr#"random string"#; } ``` -> ```rust fn f() { let s = cr"random string"; } ``` --- Assist: `make_usual_string` ```rust fn f() { let s = $0cr#"random string"#; } ``` -> ```rust fn f() { let s = c"random string"; } ``` --- .../ide-assists/src/handlers/raw_string.rs | 116 +++++++++++++++--- .../crates/ide-assists/src/utils.rs | 15 +++ .../rust-analyzer/crates/syntax/src/ast.rs | 4 +- .../crates/syntax/src/ast/token_ext.rs | 94 ++++++++++++-- 4 files changed, 202 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 2cbb24a64fd5a..ca6d88870cced 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -4,7 +4,7 @@ use syntax::{AstToken, TextRange, TextSize, ast, ast::IsString}; use crate::{ AssistContext, AssistId, Assists, - utils::{required_hashes, string_suffix}, + utils::{required_hashes, string_prefix, string_suffix}, }; // Assist: make_raw_string @@ -23,8 +23,7 @@ use crate::{ // } // ``` pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - // FIXME: This should support byte and c strings as well. - let token = ctx.find_token_at_offset::()?; + let token = ctx.find_token_at_offset::()?; if token.is_raw() { return None; } @@ -37,14 +36,15 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt |edit| { let hashes = "#".repeat(required_hashes(&value).max(1)); let range = token.syntax().text_range(); + let raw_prefix = token.raw_prefix(); let suffix = string_suffix(token.text()).unwrap_or_default(); let range = TextRange::new(range.start(), range.end() - TextSize::of(suffix)); if matches!(value, Cow::Borrowed(_)) { // Avoid replacing the whole string to better position the cursor. - edit.insert(range.start(), format!("r{hashes}")); + edit.insert(range.start(), format!("{raw_prefix}{hashes}")); edit.insert(range.end(), hashes); } else { - edit.replace(range, format!("r{hashes}\"{value}\"{hashes}")); + edit.replace(range, format!("{raw_prefix}{hashes}\"{value}\"{hashes}")); } }, ) @@ -66,7 +66,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // } // ``` pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let token = ctx.find_token_at_offset::()?; + let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; } @@ -80,18 +80,22 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // parse inside string to escape `"` let escaped = value.escape_default().to_string(); let suffix = string_suffix(token.text()).unwrap_or_default(); + let prefix = string_prefix(token.text()).map_or("", |s| s.trim_end_matches('r')); if let Some(offsets) = token.quote_offsets() && token.text()[offsets.contents - token.syntax().text_range().start()] == escaped { + let start_quote = offsets.quotes.0; + let start_quote = + TextRange::new(start_quote.start() + TextSize::of(prefix), start_quote.end()); let end_quote = offsets.quotes.1; let end_quote = TextRange::new(end_quote.start(), end_quote.end() - TextSize::of(suffix)); - edit.replace(offsets.quotes.0, "\""); edit.replace(end_quote, "\""); + edit.replace(start_quote, "\""); return; } - edit.replace(token.syntax().text_range(), format!("\"{escaped}\"{suffix}")); + edit.replace(token.syntax().text_range(), format!("{prefix}\"{escaped}\"{suffix}")); }, ) } @@ -112,7 +116,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // } // ``` pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let token = ctx.find_token_at_offset::()?; + let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; } @@ -120,7 +124,7 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> let target = text_range; acc.add(AssistId::refactor("add_hash"), "Add #", target, |edit| { let suffix = string_suffix(token.text()).unwrap_or_default(); - edit.insert(text_range.start() + TextSize::of('r'), "#"); + edit.insert(text_range.start() + TextSize::of(token.raw_prefix()), "#"); edit.insert(text_range.end() - TextSize::of(suffix), "#"); }) } @@ -141,17 +145,15 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> // } // ``` pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let token = ctx.find_token_at_offset::()?; + let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; } let text = token.text(); - if !text.starts_with("r#") && text.ends_with('#') { - return None; - } - let existing_hashes = text.chars().skip(1).take_while(|&it| it == '#').count(); + let existing_hashes = + text.chars().skip(token.raw_prefix().len()).take_while(|&it| it == '#').count(); let text_range = token.syntax().text_range(); let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; @@ -163,7 +165,10 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< acc.add(AssistId::refactor_rewrite("remove_hash"), "Remove #", text_range, |edit| { let suffix = string_suffix(text).unwrap_or_default(); - edit.delete(TextRange::at(text_range.start() + TextSize::of('r'), TextSize::of('#'))); + edit.delete(TextRange::at( + text_range.start() + TextSize::of(token.raw_prefix()), + TextSize::of('#'), + )); edit.delete( TextRange::new(text_range.end() - TextSize::of('#'), text_range.end()) - TextSize::of(suffix), @@ -224,6 +229,42 @@ string"#; ) } + #[test] + fn make_raw_byte_string_works() { + check_assist( + make_raw_string, + r#" +fn f() { + let s = $0b"random\nstring"; +} +"#, + r##" +fn f() { + let s = br#"random +string"#; +} +"##, + ) + } + + #[test] + fn make_raw_c_string_works() { + check_assist( + make_raw_string, + r#" +fn f() { + let s = $0c"random\nstring"; +} +"#, + r##" +fn f() { + let s = cr#"random +string"#; +} +"##, + ) + } + #[test] fn make_raw_string_hashes_inside_works() { check_assist( @@ -348,6 +389,23 @@ string"###; ) } + #[test] + fn add_hash_works_for_c_str() { + check_assist( + add_hash, + r#" + fn f() { + let s = $0cr"random string"; + } + "#, + r##" + fn f() { + let s = cr#"random string"#; + } + "##, + ) + } + #[test] fn add_hash_has_suffix_works() { check_assist( @@ -433,6 +491,15 @@ string"###; ) } + #[test] + fn remove_hash_works_for_c_str() { + check_assist( + remove_hash, + r##"fn f() { let s = $0cr#"random string"#; }"##, + r#"fn f() { let s = cr"random string"; }"#, + ) + } + #[test] fn remove_hash_has_suffix_works() { check_assist( @@ -529,6 +596,23 @@ string"###; ) } + #[test] + fn make_usual_string_for_c_str() { + check_assist( + make_usual_string, + r##" + fn f() { + let s = $0cr#"random string"#; + } + "##, + r#" + fn f() { + let s = c"random string"; + } + "#, + ) + } + #[test] fn make_usual_string_has_suffix_works() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 20e0302b57d70..6ed4467aa359b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1057,6 +1057,21 @@ fn test_string_suffix() { assert_eq!(Some("i32"), string_suffix(r##"r#""#i32"##)); } +/// Calculate the string literal prefix length +pub(crate) fn string_prefix(s: &str) -> Option<&str> { + s.split_once(['"', '\'', '#']).map(|(prefix, _)| prefix) +} +#[test] +fn test_string_prefix() { + assert_eq!(Some(""), string_prefix(r#""abc""#)); + assert_eq!(Some(""), string_prefix(r#""""#)); + assert_eq!(Some(""), string_prefix(r#"""suffix"#)); + assert_eq!(Some("c"), string_prefix(r#"c"""#)); + assert_eq!(Some("r"), string_prefix(r#"r"""#)); + assert_eq!(Some("cr"), string_prefix(r#"cr"""#)); + assert_eq!(Some("r"), string_prefix(r##"r#""#"##)); +} + /// Replaces the record expression, handling field shorthands including inside macros. pub(crate) fn replace_record_field_expr( ctx: &AssistContext<'_>, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 19c1c5ebea333..aea99a4389b9b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -29,7 +29,9 @@ pub use self::{ SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, - token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix}, + token_ext::{ + AnyString, CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix, + }, traits::{ AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericArgs, HasGenericParams, HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 4afdda78a0e70..d9223e8216da3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -151,10 +151,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { - const RAW_PREFIX: &'static str; - fn unescape(s: &str, callback: impl FnMut(Range, Result)); + fn raw_prefix(&self) -> &'static str; + fn unescape(&self, s: &str, callback: impl FnMut(Range, Result)); fn is_raw(&self) -> bool { - self.text().starts_with(Self::RAW_PREFIX) + self.text().starts_with(self.raw_prefix()) } fn quote_offsets(&self) -> Option { let text = self.text(); @@ -187,7 +187,7 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; - Self::unescape(text, &mut |range: Range, unescaped_char| { + self.unescape(text, &mut |range: Range, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); } @@ -204,8 +204,10 @@ pub trait IsString: AstToken { } impl IsString for ast::String { - const RAW_PREFIX: &'static str = "r"; - fn unescape(s: &str, cb: impl FnMut(Range, Result)) { + fn raw_prefix(&self) -> &'static str { + "r" + } + fn unescape(&self, s: &str, cb: impl FnMut(Range, Result)) { unescape_str(s, cb) } } @@ -246,8 +248,10 @@ impl ast::String { } impl IsString for ast::ByteString { - const RAW_PREFIX: &'static str = "br"; - fn unescape(s: &str, mut callback: impl FnMut(Range, Result)) { + fn raw_prefix(&self) -> &'static str { + "br" + } + fn unescape(&self, s: &str, mut callback: impl FnMut(Range, Result)) { unescape_byte_str(s, |range, res| callback(range, res.map(char::from))) } } @@ -288,10 +292,12 @@ impl ast::ByteString { } impl IsString for ast::CString { - const RAW_PREFIX: &'static str = "cr"; + fn raw_prefix(&self) -> &'static str { + "cr" + } // NOTE: This method should only be used for highlighting ranges. The unescaped // char/byte is not used. For simplicity, we return an arbitrary placeholder char. - fn unescape(s: &str, mut callback: impl FnMut(Range, Result)) { + fn unescape(&self, s: &str, mut callback: impl FnMut(Range, Result)) { unescape_c_str(s, |range, _res| callback(range, Ok('_'))) } } @@ -465,6 +471,74 @@ impl ast::Byte { } } +pub enum AnyString { + ByteString(ast::ByteString), + CString(ast::CString), + String(ast::String), +} + +impl AnyString { + pub fn value(&self) -> Result, EscapeError> { + fn from_utf8(s: Cow<'_, [u8]>) -> Result, EscapeError> { + match s { + Cow::Borrowed(s) => str::from_utf8(s) + .map_err(|_| EscapeError::NonAsciiCharInByte) + .map(Cow::Borrowed), + Cow::Owned(s) => String::from_utf8(s) + .map_err(|_| EscapeError::NonAsciiCharInByte) + .map(Cow::Owned), + } + } + + match self { + AnyString::String(s) => s.value(), + AnyString::ByteString(s) => s.value().and_then(from_utf8), + AnyString::CString(s) => s.value().and_then(from_utf8), + } + } +} + +impl ast::AstToken for AnyString { + fn can_cast(kind: crate::SyntaxKind) -> bool { + ast::String::can_cast(kind) + || ast::ByteString::can_cast(kind) + || ast::CString::can_cast(kind) + } + + fn cast(syntax: crate::SyntaxToken) -> Option { + ast::String::cast(syntax.clone()) + .map(Self::String) + .or_else(|| ast::ByteString::cast(syntax.clone()).map(Self::ByteString)) + .or_else(|| ast::CString::cast(syntax).map(Self::CString)) + } + + fn syntax(&self) -> &crate::SyntaxToken { + match self { + Self::ByteString(it) => it.syntax(), + Self::CString(it) => it.syntax(), + Self::String(it) => it.syntax(), + } + } +} + +impl IsString for AnyString { + fn raw_prefix(&self) -> &'static str { + match self { + AnyString::ByteString(s) => s.raw_prefix(), + AnyString::CString(s) => s.raw_prefix(), + AnyString::String(s) => s.raw_prefix(), + } + } + + fn unescape(&self, s: &str, callback: impl FnMut(Range, Result)) { + match self { + AnyString::ByteString(it) => it.unescape(s, callback), + AnyString::CString(it) => it.unescape(s, callback), + AnyString::String(it) => it.unescape(s, callback), + } + } +} + #[cfg(test)] mod tests { use rustc_apfloat::ieee::Quad as f128; From d1ecdca22f67c97f354dab6b9e66022ecb4b506e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 10 Oct 2025 19:00:30 +0800 Subject: [PATCH 1500/1900] Migrate `raw_string` assist to use `SyntaxEditor` --- .../ide-assists/src/handlers/raw_string.rs | 85 ++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index ca6d88870cced..d6d99b8b6d9dc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -1,6 +1,8 @@ -use std::borrow::Cow; - -use syntax::{AstToken, TextRange, TextSize, ast, ast::IsString}; +use ide_db::source_change::SourceChangeBuilder; +use syntax::{ + AstToken, + ast::{self, IsString, make::tokens::literal}, +}; use crate::{ AssistContext, AssistId, Assists, @@ -35,17 +37,10 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt target, |edit| { let hashes = "#".repeat(required_hashes(&value).max(1)); - let range = token.syntax().text_range(); let raw_prefix = token.raw_prefix(); let suffix = string_suffix(token.text()).unwrap_or_default(); - let range = TextRange::new(range.start(), range.end() - TextSize::of(suffix)); - if matches!(value, Cow::Borrowed(_)) { - // Avoid replacing the whole string to better position the cursor. - edit.insert(range.start(), format!("{raw_prefix}{hashes}")); - edit.insert(range.end(), hashes); - } else { - edit.replace(range, format!("{raw_prefix}{hashes}\"{value}\"{hashes}")); - } + let new_str = format!("{raw_prefix}{hashes}\"{value}\"{hashes}{suffix}"); + replace_literal(&token, &new_str, edit, ctx); }, ) } @@ -81,21 +76,8 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let escaped = value.escape_default().to_string(); let suffix = string_suffix(token.text()).unwrap_or_default(); let prefix = string_prefix(token.text()).map_or("", |s| s.trim_end_matches('r')); - if let Some(offsets) = token.quote_offsets() - && token.text()[offsets.contents - token.syntax().text_range().start()] == escaped - { - let start_quote = offsets.quotes.0; - let start_quote = - TextRange::new(start_quote.start() + TextSize::of(prefix), start_quote.end()); - let end_quote = offsets.quotes.1; - let end_quote = - TextRange::new(end_quote.start(), end_quote.end() - TextSize::of(suffix)); - edit.replace(end_quote, "\""); - edit.replace(start_quote, "\""); - return; - } - - edit.replace(token.syntax().text_range(), format!("{prefix}\"{escaped}\"{suffix}")); + let new_str = format!("{prefix}\"{escaped}\"{suffix}"); + replace_literal(&token, &new_str, edit, ctx); }, ) } @@ -120,12 +102,14 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> if !token.is_raw() { return None; } - let text_range = token.syntax().text_range(); - let target = text_range; + let target = token.syntax().text_range(); acc.add(AssistId::refactor("add_hash"), "Add #", target, |edit| { - let suffix = string_suffix(token.text()).unwrap_or_default(); - edit.insert(text_range.start() + TextSize::of(token.raw_prefix()), "#"); - edit.insert(text_range.end() - TextSize::of(suffix), "#"); + let str = token.text(); + let suffix = string_suffix(str).unwrap_or_default(); + let raw_prefix = token.raw_prefix(); + let wrap_range = raw_prefix.len()..str.len() - suffix.len(); + let new_str = [raw_prefix, "#", &str[wrap_range], "#", suffix].concat(); + replace_literal(&token, &new_str, edit, ctx); }) } @@ -165,17 +149,38 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< acc.add(AssistId::refactor_rewrite("remove_hash"), "Remove #", text_range, |edit| { let suffix = string_suffix(text).unwrap_or_default(); - edit.delete(TextRange::at( - text_range.start() + TextSize::of(token.raw_prefix()), - TextSize::of('#'), - )); - edit.delete( - TextRange::new(text_range.end() - TextSize::of('#'), text_range.end()) - - TextSize::of(suffix), - ); + let prefix = token.raw_prefix(); + let wrap_range = prefix.len() + 1..text.len() - suffix.len() - 1; + let new_str = [prefix, &text[wrap_range], suffix].concat(); + replace_literal(&token, &new_str, edit, ctx); }) } +fn replace_literal( + token: &impl AstToken, + new: &str, + builder: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, +) { + let token = token.syntax(); + let node = token.parent().expect("no parent token"); + let mut edit = builder.make_editor(&node); + let new_literal = literal(new); + + edit.replace(token, mut_token(new_literal)); + + builder.add_file_edits(ctx.vfs_file_id(), edit); +} + +fn mut_token(token: syntax::SyntaxToken) -> syntax::SyntaxToken { + let node = token.parent().expect("no parent token"); + node.clone_for_update() + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.text_range() == token.text_range() && it.text() == token.text()) + .unwrap() +} + #[cfg(test)] mod tests { use super::*; From 6660d75710a98b8a3a5ef455e68fdc8cd4aaf733 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 10 Oct 2025 07:52:49 -0400 Subject: [PATCH 1501/1900] Move to the tier 3 table --- src/doc/rustc/src/platform-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7efeb216a886f..c833567e342a9 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -198,7 +198,6 @@ target | std | notes [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASIp1 [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | WebAssembly with WASIp2 -[`wasm32-wasip3`](platform-support/wasm32-wasip3.md) | ✓ | WebAssembly with WASIp3 [`wasm32v1-none`](platform-support/wasm32v1-none.md) | * | WebAssembly limited to 1.0 features and no imports [`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64 @@ -417,6 +416,7 @@ target | std | host | notes [`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly [`wasm32-wali-linux-musl`](platform-support/wasm32-wali-linux.md) | ? | | WebAssembly with [WALI](https://github.com/arjunr2/WALI) +[`wasm32-wasip3`](platform-support/wasm32-wasip3.md) | ✓ | WebAssembly with WASIp3 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator [`x86_64-lynx-lynxos178`](platform-support/lynxos178.md) | | | x86_64 LynxOS-178 From 4c6caaa3a6d80a895a40389a8b37808b4cea30e1 Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 18:19:15 +0800 Subject: [PATCH 1502/1900] Always print disk usage --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f539b64d8c829..d440b296d2753 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -246,6 +246,8 @@ jobs: run: src/ci/scripts/create-doc-artifacts.sh - name: print disk usage + # We also want to know the disk usage when the job fails. + if: always() run: | echo "disk usage:" df -h From c86474ca0719cc24a6686215ab0fae1c98ce3eb9 Mon Sep 17 00:00:00 2001 From: Vitali Borsak Date: Wed, 20 Aug 2025 09:57:14 +0000 Subject: [PATCH 1503/1900] Adding a regression test (const promotion with Option) --- ...on_ordering_eq.direct.PreCodegen.after.mir | 46 +++++++++++++++++ .../const_promotion_option_ordering_eq.rs | 30 ++++++++++++ ..._ordering_eq.with_let.PreCodegen.after.mir | 49 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.direct.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.rs create mode 100644 tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.with_let.PreCodegen.after.mir diff --git a/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.direct.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.direct.PreCodegen.after.mir new file mode 100644 index 0000000000000..66b51ad48e7a5 --- /dev/null +++ b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.direct.PreCodegen.after.mir @@ -0,0 +1,46 @@ +// MIR for `direct` after PreCodegen + +fn direct(_1: Option) -> bool { + debug e => _1; + let mut _0: bool; + scope 1 (inlined as PartialEq>::eq) { + let mut _2: isize; + scope 2 { + scope 3 (inlined ::eq) { + let _3: i8; + scope 4 { + scope 5 { + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = discriminant(_1); + switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; + } + + bb1: { + _0 = const false; + goto -> bb3; + } + + bb2: { + StorageLive(_3); + _3 = discriminant(((_1 as Some).0: std::cmp::Ordering)); + _0 = Eq(copy _3, const 0_i8); + StorageDead(_3); + goto -> bb3; + } + + bb3: { + StorageDead(_2); + return; + } + + bb4: { + unreachable; + } +} diff --git a/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.rs b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.rs new file mode 100644 index 0000000000000..3c1e96fa47484 --- /dev/null +++ b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 + +// Check that comparing `Option` to a constant inlined `Some(...)` +// does not produce unnecessarily complex MIR compared to using a local binding. +// +// Regression test for . +// Originally, inlined constants like `Some(Ordering::Equal)` would get promoted, +// leading to more MIR (and extra LLVM IR checks) than necessary. +// Both cases should now generate identical MIR. + +use std::cmp::Ordering; + +// EMIT_MIR const_promotion_option_ordering_eq.direct.PreCodegen.after.mir +pub fn direct(e: Option) -> bool { + // CHECK-LABEL: fn direct( + // CHECK-NOT: promoted[ + // CHECK: switchInt( + // CHECK: return + e == Some(Ordering::Equal) +} + +// EMIT_MIR const_promotion_option_ordering_eq.with_let.PreCodegen.after.mir +pub fn with_let(e: Option) -> bool { + // CHECK-LABEL: fn with_let( + // CHECK-NOT: promoted[ + // CHECK: switchInt( + // CHECK: return + let eq = Ordering::Equal; + e == Some(eq) +} diff --git a/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.with_let.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.with_let.PreCodegen.after.mir new file mode 100644 index 0000000000000..f089e9ad960bd --- /dev/null +++ b/tests/mir-opt/pre-codegen/const_promotion_option_ordering_eq.with_let.PreCodegen.after.mir @@ -0,0 +1,49 @@ +// MIR for `with_let` after PreCodegen + +fn with_let(_1: Option) -> bool { + debug e => _1; + let mut _0: bool; + scope 1 { + debug eq => const Equal; + scope 2 (inlined as PartialEq>::eq) { + let mut _2: isize; + scope 3 { + scope 4 (inlined ::eq) { + let _3: i8; + scope 5 { + scope 6 { + } + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = discriminant(_1); + switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; + } + + bb1: { + _0 = const false; + goto -> bb3; + } + + bb2: { + StorageLive(_3); + _3 = discriminant(((_1 as Some).0: std::cmp::Ordering)); + _0 = Eq(copy _3, const 0_i8); + StorageDead(_3); + goto -> bb3; + } + + bb3: { + StorageDead(_2); + return; + } + + bb4: { + unreachable; + } +} From b7c2b3dc80e03dbbde6ff869c1f975c036703590 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 10 Oct 2025 01:48:09 +0000 Subject: [PATCH 1504/1900] Remove StatementKind::Deinit. --- compiler/rustc_borrowck/src/dataflow.rs | 1 - compiler/rustc_borrowck/src/def_use.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 1 - .../src/polonius/legacy/loan_invalidations.rs | 1 - compiler/rustc_borrowck/src/type_check/mod.rs | 1 - compiler/rustc_codegen_cranelift/src/base.rs | 1 - compiler/rustc_codegen_cranelift/src/constant.rs | 1 - compiler/rustc_codegen_ssa/src/mir/analyze.rs | 1 - compiler/rustc_codegen_ssa/src/mir/statement.rs | 5 ----- .../rustc_const_eval/src/check_consts/check.rs | 1 - compiler/rustc_const_eval/src/interpret/step.rs | 5 ----- compiler/rustc_middle/src/mir/pretty.rs | 1 - compiler/rustc_middle/src/mir/statement.rs | 1 - compiler/rustc_middle/src/mir/syntax.rs | 6 ------ compiler/rustc_middle/src/mir/visit.rs | 9 --------- .../src/builder/custom/parse/instruction.rs | 3 --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 5 ++--- .../src/impls/storage_liveness.rs | 3 +-- .../rustc_mir_dataflow/src/move_paths/builder.rs | 2 +- compiler/rustc_mir_transform/src/coroutine.rs | 1 - .../src/coverage/spans/from_mir.rs | 1 - .../rustc_mir_transform/src/cross_crate_inline.rs | 5 +---- .../rustc_mir_transform/src/dataflow_const_prop.rs | 4 ---- compiler/rustc_mir_transform/src/jump_threading.rs | 3 +-- .../rustc_mir_transform/src/known_panics_lint.rs | 1 - compiler/rustc_mir_transform/src/large_enums.rs | 3 --- .../src/remove_noop_landing_pads.rs | 1 - compiler/rustc_mir_transform/src/remove_zsts.rs | 3 +-- compiler/rustc_mir_transform/src/simplify.rs | 6 +++--- compiler/rustc_mir_transform/src/sroa.rs | 14 +------------- compiler/rustc_mir_transform/src/validate.rs | 10 ---------- compiler/rustc_public/src/mir/body.rs | 1 - compiler/rustc_public/src/mir/pretty.rs | 1 - compiler/rustc_public/src/mir/visit.rs | 1 - .../src/unstable/convert/stable/mir.rs | 3 --- library/core/src/intrinsics/mir.rs | 3 +-- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- tests/mir-opt/building/custom/enums.rs | 1 - .../custom/enums.set_discr.built.after.mir | 1 - tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff | 2 -- tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff | 2 -- tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff | 2 -- tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff | 2 -- 43 files changed, 14 insertions(+), 109 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index d3f6c01ab8c3a..84d5ffb947914 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -577,7 +577,6 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { mir::StatementKind::FakeRead(..) | mir::StatementKind::SetDiscriminant { .. } - | mir::StatementKind::Deinit(..) | mir::StatementKind::StorageLive(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::PlaceMention(..) diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index b9ced81c46c19..502265a83523e 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -80,7 +80,7 @@ pub(crate) fn categorize(context: PlaceContext) -> Option { // Backwards incompatible drop hint is not a use, just a marker for linting. PlaceContext::NonUse(NonUseContext::BackwardIncompatibleDropHint) => None, - PlaceContext::MutatingUse(MutatingUseContext::Deinit | MutatingUseContext::SetDiscriminant) => { + PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => { bug!("These statements are not allowed in this MIR phase") } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a85dcf64d8d46..d13de8c972265 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -857,7 +857,6 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, } StatementKind::Nop | StatementKind::Retag { .. } - | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index c2ad6fcb4b799..f6cf0212ef305 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -84,7 +84,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::Retag { .. } - | StatementKind::Deinit(..) | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 781fb5ba113ab..984a154853a98 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -748,7 +748,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) - | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index ebf2ccf74de20..b3f9f598926c2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -932,7 +932,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - | StatementKind::Deinit(_) | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::FakeRead(..) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index faca92957e1a6..293459cc11c2f 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -597,7 +597,6 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( StatementKind::Assign(_) | StatementKind::FakeRead(_) | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Retag(_, _) diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 0a37a904193fa..de755d5617801 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -229,7 +229,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer PlaceContext::MutatingUse( MutatingUseContext::Store - | MutatingUseContext::Deinit | MutatingUseContext::SetDiscriminant | MutatingUseContext::AsmOutput | MutatingUseContext::Borrow diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 88590b6271bce..d0121f7643800 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -50,11 +50,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::StatementKind::SetDiscriminant { box ref place, variant_index } => { self.codegen_place(bx, place.as_ref()).codegen_set_discr(bx, variant_index); } - mir::StatementKind::Deinit(..) => { - // For now, don't codegen this to anything. In the future it may be worth - // experimenting with what kind of information we can emit to LLVM without hurting - // perf here - } mir::StatementKind::StorageLive(local) => { if let LocalRef::Place(cg_place) = self.locals[local] { cg_place.storage_live(bx); diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 413aa5f8b8774..2c6dd5bd01f9c 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -732,7 +732,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { match statement.kind { StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) | StatementKind::FakeRead(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 923e00ad4cf1a..088cacf2e6403 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -98,11 +98,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_discriminant(*variant_index, &dest)?; } - Deinit(place) => { - let dest = self.eval_place(**place)?; - self.write_uninit(&dest)?; - } - // Mark locals as alive StorageLive(local) => { self.storage_live(*local)?; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d87e3abe3b2ea..60c2ef4d563e4 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -818,7 +818,6 @@ impl Debug for Statement<'_> { SetDiscriminant { ref place, variant_index } => { write!(fmt, "discriminant({place:?}) = {variant_index:?}") } - Deinit(ref place) => write!(fmt, "Deinit({place:?})"), PlaceMention(ref place) => { write!(fmt, "PlaceMention({place:?})") } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f310e1e576250..74c39afbbc823 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -50,7 +50,6 @@ impl<'tcx> StatementKind<'tcx> { StatementKind::Assign(..) => "Assign", StatementKind::FakeRead(..) => "FakeRead", StatementKind::SetDiscriminant { .. } => "SetDiscriminant", - StatementKind::Deinit(..) => "Deinit", StatementKind::StorageLive(..) => "StorageLive", StatementKind::StorageDead(..) => "StorageDead", StatementKind::Retag(..) => "Retag", diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index a823c365394f7..0fffa009d9ad8 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -135,7 +135,6 @@ pub enum RuntimePhase { /// And the following variants are allowed: /// * [`StatementKind::Retag`] /// * [`StatementKind::SetDiscriminant`] - /// * [`StatementKind::Deinit`] /// /// Furthermore, `Copy` operands are allowed for non-`Copy` types. Initial = 0, @@ -362,11 +361,6 @@ pub enum StatementKind<'tcx> { /// the type. SetDiscriminant { place: Box>, variant_index: VariantIdx }, - /// Deinitializes the place. - /// - /// This writes `uninit` bytes to the entire place. - Deinit(Box>), - /// `StorageLive` and `StorageDead` statements mark the live range of a local. /// /// At any point during the execution of a function, each local is either allocated or diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9654e189f2edf..fd81bdeb3f952 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -445,13 +445,6 @@ macro_rules! make_mir_visitor { location ); } - StatementKind::Deinit(place) => { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Deinit), - location - ) - } StatementKind::StorageLive(local) => { self.visit_local( $(& $mutability)? *local, @@ -1372,8 +1365,6 @@ pub enum MutatingUseContext { Store, /// Appears on `SetDiscriminant` SetDiscriminant, - /// Appears on `Deinit` - Deinit, /// Output operand of an inline assembly block. AsmOutput, /// Destination of a call. diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 54490e0050902..b1d1d67947a05 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -24,9 +24,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let op = self.parse_operand(args[0])?; Ok(StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(op)))) }, - @call(mir_deinit, args) => { - Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?))) - }, @call(mir_retag, args) => { Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?))) }, diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index f6aaa65ad9fdd..596da18e3d1b4 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -159,8 +159,7 @@ impl DefUse { MutatingUseContext::Call | MutatingUseContext::Yield | MutatingUseContext::AsmOutput - | MutatingUseContext::Store - | MutatingUseContext::Deinit, + | MutatingUseContext::Store, ) => { // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use. if place.is_indirect() { @@ -238,7 +237,7 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { && (!debuginfo_locals.contains(place.local) || (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some()))) .then_some(*place), - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + StatementKind::SetDiscriminant { place, .. } => { (!debuginfo_locals.contains(place.local)).then_some(**place) } StatementKind::FakeRead(_) diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index e3aa8f5a62014..026826fc379c7 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -156,8 +156,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { // If a place is assigned to in a statement, it needs storage for that statement. StatementKind::Assign(box (place, _)) - | StatementKind::SetDiscriminant { box place, .. } - | StatementKind::Deinit(box place) => { + | StatementKind::SetDiscriminant { box place, .. } => { state.gen_(place.local); } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 434f106302f5d..c60713cea0300 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -371,7 +371,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { self.gather_move(Place::from(*local)); } } - StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) => { + StatementKind::SetDiscriminant { .. } => { span_bug!( stmt.source_info.span, "SetDiscriminant/Deinit should not exist during borrowck" diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c136df812a3f0..34e03ef69f30c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1723,7 +1723,6 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { StatementKind::FakeRead(..) | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Retag(..) diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index dfeaa90dc2e22..c096f1e2632ce 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -91,7 +91,6 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { ) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) | StatementKind::Retag(_, _) | StatementKind::PlaceMention(..) | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index df98c07f54958..7fc9fb9cca2d7 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -115,10 +115,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { // Don't count StorageLive/StorageDead in the inlining cost. match statement.kind { - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Deinit(_) - | StatementKind::Nop => {} + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => {} _ => self.statements += 1, } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index e970f7ff81adb..8451ebed4f705 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -178,10 +178,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { FlatSet::::BOTTOM, ); } - StatementKind::Deinit(box place) => { - // Deinit makes the place uninitialized. - state.flood_with(place.as_ref(), &self.map, FlatSet::::BOTTOM); - } StatementKind::Retag(..) => { // We don't track references. } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 68298767e7fd8..37a3da94f9689 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -332,8 +332,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { stmt: &Statement<'tcx>, ) -> Option<(Place<'tcx>, Option)> { match stmt.kind { - StatementKind::Assign(box (place, _)) - | StatementKind::Deinit(box place) => Some((place, None)), + StatementKind::Assign(box (place, _)) => Some((place, None)), StatementKind::SetDiscriminant { box place, variant_index: _ } => { Some((place, Some(TrackElem::Discriminant))) } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 93abc0f8860b9..c67e875175fee 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -926,7 +926,6 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { // mutations of the same local via `Store` | MutatingUse(MutatingUseContext::Call) | MutatingUse(MutatingUseContext::AsmOutput) - | MutatingUse(MutatingUseContext::Deinit) // Actual store that can possibly even propagate a value | MutatingUse(MutatingUseContext::Store) | MutatingUse(MutatingUseContext::SetDiscriminant) => { diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 1b90e9158f6b8..89bd91e7013da 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -126,8 +126,6 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), ))); - let deinit_old = StatementKind::Deinit(Box::new(dst)); - let copy_bytes = StatementKind::Intrinsic(Box::new( NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { src: Operand::Copy(src_cast_place), @@ -148,7 +146,6 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { dst_cast, src_ptr, src_cast, - deinit_old, copy_bytes, store_dead, ]; diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 38752bde6b417..f7105f62e4beb 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -113,7 +113,6 @@ impl RemoveNoopLandingPads { StatementKind::Assign { .. } | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) | StatementKind::Intrinsic(..) | StatementKind::Retag { .. } => { return false; diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 90c1b3520b96e..bebd8fab74565 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -122,8 +122,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { StatementKind::Assign(box (place, ref rvalue)) => { rvalue.is_safe_to_remove().then_some(place) } - StatementKind::Deinit(box place) - | StatementKind::SetDiscriminant { box place, variant_index: _ } + StatementKind::SetDiscriminant { box place, variant_index: _ } | StatementKind::AscribeUserType(box (place, _), _) | StatementKind::Retag(_, box place) | StatementKind::PlaceMention(box place) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8b5efb7420582..da31600e8324c 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -590,7 +590,6 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { } StatementKind::SetDiscriminant { ref place, variant_index: _ } - | StatementKind::Deinit(ref place) | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { self.visit_lhs(place, location); } @@ -630,8 +629,9 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod } StatementKind::Assign(box (place, _)) | StatementKind::SetDiscriminant { box place, .. } - | StatementKind::BackwardIncompatibleDropHint { box place, .. } - | StatementKind::Deinit(box place) => used_locals.is_used(place.local), + | StatementKind::BackwardIncompatibleDropHint { box place, .. } => { + used_locals.is_used(place.local) + } _ => continue, }; if keep_statement { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 99f10b8d91d2c..801383493837d 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -136,9 +136,7 @@ fn escaping_locals<'tcx>( fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { // Storage statements are expanded in run_pass. - StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::Deinit(..) => return, + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) => return, _ => self.super_statement(statement, location), } } @@ -331,16 +329,6 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { } return; } - StatementKind::Deinit(box place) => { - if let Some(final_locals) = self.replacements.place_fragments(place) { - for (_, _, fl) in final_locals { - self.patch - .add_statement(location, StatementKind::Deinit(Box::new(fl.into()))); - } - statement.make_nop(true); - return; - } - } // We have `a = Struct { 0: x, 1: y, .. }`. // We replace it by diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 95873484b6529..ed453a580e854 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -313,11 +313,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } } - StatementKind::Deinit(..) => { - if self.body.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.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which @@ -1501,11 +1496,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - StatementKind::Deinit(..) => { - if self.body.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.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 7bd06fee721ce..615a5a48d15b7 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -478,7 +478,6 @@ pub enum StatementKind { Assign(Place, Rvalue), FakeRead(FakeReadCause, Place), SetDiscriminant { place: Place, variant_index: VariantIdx }, - Deinit(Place), StorageLive(Local), StorageDead(Local), Retag(RetagKind, Place), diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index 9dd1ce4de0ea6..8904870f29e32 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -102,7 +102,6 @@ fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io:: StatementKind::SetDiscriminant { place, variant_index } => { writeln!(writer, "{INDENT}discriminant({place:?}) = {};", variant_index.to_index()) } - StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), StatementKind::StorageLive(local) => { writeln!(writer, "{INDENT}StorageLive(_{local});") } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 7563c9ca00820..1cb9bfe79a096 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -170,7 +170,6 @@ macro_rules! make_mir_visitor { self.visit_place(place, PlaceContext::NON_MUTATING, location); } StatementKind::SetDiscriminant { place, .. } - | StatementKind::Deinit(place) | StatementKind::Retag(_, place) => { self.visit_place(place, PlaceContext::MUTATING, location); } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 62ab91d17baee..392347ce345a0 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -149,9 +149,6 @@ impl<'tcx> Stable<'tcx> for mir::StatementKind<'tcx> { variant_index: variant_index.stable(tables, cx), } } - mir::StatementKind::Deinit(place) => { - crate::mir::StatementKind::Deinit(place.stable(tables, cx)) - } mir::StatementKind::StorageLive(place) => { crate::mir::StatementKind::StorageLive(place.stable(tables, cx)) diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index a800ef1cb9375..8ddce1c758438 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -227,7 +227,7 @@ //! //! #### Statements //! - Assign statements work via normal Rust assignment. -//! - [`Retag`], [`StorageLive`], [`StorageDead`], [`Deinit`] statements have an associated function. +//! - [`Retag`], [`StorageLive`], [`StorageDead`] statements have an associated function. //! //! #### Rvalues //! @@ -400,7 +400,6 @@ define!("mir_unwind_resume", define!("mir_storage_live", fn StorageLive(local: T)); define!("mir_storage_dead", fn StorageDead(local: T)); define!("mir_assume", fn Assume(operand: bool)); -define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); define!( "mir_ptr_metadata", diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1e3a7281bc734..b9027fea468eb 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -232,7 +232,7 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(cx, *place, span, body, msrv), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + StatementKind::SetDiscriminant { place, .. } => { check_place(cx, **place, span, body, msrv) }, diff --git a/tests/mir-opt/building/custom/enums.rs b/tests/mir-opt/building/custom/enums.rs index 88ec228986ab7..6fad373203a69 100644 --- a/tests/mir-opt/building/custom/enums.rs +++ b/tests/mir-opt/building/custom/enums.rs @@ -88,7 +88,6 @@ fn switch_option_repr(option: Bool) -> bool { fn set_discr(option: &mut Option<()>) { mir! { { - Deinit(*option); SetDiscriminant(*option, 0); Return() } diff --git a/tests/mir-opt/building/custom/enums.set_discr.built.after.mir b/tests/mir-opt/building/custom/enums.set_discr.built.after.mir index 8cc66e7e50d6a..d9b46dff43aa4 100644 --- a/tests/mir-opt/building/custom/enums.set_discr.built.after.mir +++ b/tests/mir-opt/building/custom/enums.set_discr.built.after.mir @@ -4,7 +4,6 @@ fn set_discr(_1: &mut Option<()>) -> () { let mut _0: (); bb0: { - Deinit((*_1)); discriminant((*_1)) = 0; return; } diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff index 267a4c1cf6beb..cf850351c1d62 100644 --- a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff @@ -44,7 +44,6 @@ + _9 = copy _8 as *mut u8 (PtrToPtr); + _10 = &raw const _2; + _11 = copy _10 as *const u8 (PtrToPtr); -+ Deinit(_8); + copy_nonoverlapping(dst = copy _9, src = copy _11, count = copy _7); + StorageDead(_4); + nop; @@ -59,7 +58,6 @@ + _17 = copy _16 as *mut u8 (PtrToPtr); + _18 = &raw const _1; + _19 = copy _18 as *const u8 (PtrToPtr); -+ Deinit(_16); + copy_nonoverlapping(dst = copy _17, src = copy _19, count = copy _15); + StorageDead(_12); + nop; diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff index 8e5c403cd7e6b..dc5ea1add0005 100644 --- a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff @@ -44,7 +44,6 @@ + _9 = copy _8 as *mut u8 (PtrToPtr); + _10 = &raw const _2; + _11 = copy _10 as *const u8 (PtrToPtr); -+ Deinit(_8); + copy_nonoverlapping(dst = copy _9, src = copy _11, count = copy _7); + StorageDead(_4); + nop; @@ -59,7 +58,6 @@ + _17 = copy _16 as *mut u8 (PtrToPtr); + _18 = &raw const _1; + _19 = copy _18 as *const u8 (PtrToPtr); -+ Deinit(_16); + copy_nonoverlapping(dst = copy _17, src = copy _19, count = copy _15); + StorageDead(_12); + nop; diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff index 96c5aadd85fd4..3cd2e74a0db6f 100644 --- a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff @@ -44,7 +44,6 @@ + _9 = copy _8 as *mut u8 (PtrToPtr); + _10 = &raw const _2; + _11 = copy _10 as *const u8 (PtrToPtr); -+ Deinit(_8); + copy_nonoverlapping(dst = copy _9, src = copy _11, count = copy _7); + StorageDead(_4); + nop; @@ -59,7 +58,6 @@ + _17 = copy _16 as *mut u8 (PtrToPtr); + _18 = &raw const _1; + _19 = copy _18 as *const u8 (PtrToPtr); -+ Deinit(_16); + copy_nonoverlapping(dst = copy _17, src = copy _19, count = copy _15); + StorageDead(_12); + nop; diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff index d20e2e08eaafd..10b0ec5a63fa4 100644 --- a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff @@ -44,7 +44,6 @@ + _9 = copy _8 as *mut u8 (PtrToPtr); + _10 = &raw const _2; + _11 = copy _10 as *const u8 (PtrToPtr); -+ Deinit(_8); + copy_nonoverlapping(dst = copy _9, src = copy _11, count = copy _7); + StorageDead(_4); + nop; @@ -59,7 +58,6 @@ + _17 = copy _16 as *mut u8 (PtrToPtr); + _18 = &raw const _1; + _19 = copy _18 as *const u8 (PtrToPtr); -+ Deinit(_16); + copy_nonoverlapping(dst = copy _17, src = copy _19, count = copy _15); + StorageDead(_12); + nop; From 15575602da04d552f48be8d955daeaa91fb8bc04 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 10 Oct 2025 01:48:09 +0000 Subject: [PATCH 1505/1900] Remove StatementKind::Deinit. --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 1e3a7281bc734..b9027fea468eb 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -232,7 +232,7 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(cx, *place, span, body, msrv), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + StatementKind::SetDiscriminant { place, .. } => { check_place(cx, **place, span, body, msrv) }, From 116f4ae171e292423304ee6842a11c48a4fff5ba Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:44:01 +0000 Subject: [PATCH 1506/1900] Support #[alloc_error_handler] without the allocator shim Currently it is possible to avoid linking the allocator shim when __rust_no_alloc_shim_is_unstable_v2 is defined when linking rlibs directly as some build systems need. However this requires liballoc to be compiled with --cfg no_global_oom_handling, which places huge restrictions on what functions you can call and makes it impossible to use libstd. Or alternatively you have to define __rust_alloc_error_handler and (when using libstd) __rust_alloc_error_handler_should_panic using #[rustc_std_internal_symbol]. With this commit you can either use libstd and define __rust_alloc_error_handler_should_panic or not use libstd and use #[alloc_error_handler] instead. Both options are still unstable though. Eventually the alloc_error_handler may either be removed entirely (though the PR for that has been stale for years now) or we may start using weak symbols for it instead. For the latter case this commit is a prerequisite anyway. --- compiler/rustc_ast/src/expand/allocator.rs | 8 +----- .../src/alloc_error_handler.rs | 5 ++-- .../rustc_codegen_cranelift/src/allocator.rs | 28 ++++++++++--------- compiler/rustc_codegen_gcc/src/allocator.rs | 24 ++++++++-------- compiler/rustc_codegen_llvm/src/allocator.rs | 28 ++++++++++--------- .../src/back/symbol_export.rs | 6 ++-- compiler/rustc_metadata/src/creader.rs | 24 ++++++++-------- library/alloc/src/alloc.rs | 4 +-- library/std/src/alloc.rs | 7 +++-- src/tools/miri/src/shims/foreign_items.rs | 17 ++++++----- .../fail/alloc/alloc_error_handler.stderr | 2 +- .../alloc/alloc_error_handler_custom.stderr | 2 +- .../alloc/alloc_error_handler_no_std.stderr | 2 +- tests/run-make/bin-emit-no-symbols/rmake.rs | 2 +- tests/ui/sanitizer/dataflow-abilist.txt | 10 ------- 15 files changed, 83 insertions(+), 86 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 7dee2ed17b4be..a3f9ddb154f71 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -15,13 +15,7 @@ pub fn default_fn_name(base: Symbol) -> String { format!("__rdl_{base}") } -pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str { - match alloc_error_handler_kind { - AllocatorKind::Global => "__rg_oom", - AllocatorKind::Default => "__rdl_oom", - } -} - +pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; pub enum AllocatorTy { diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 35ef6be095e9c..40946f3b2791a 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -1,3 +1,4 @@ +use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, global_fn_name}; use rustc_ast::{ self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind, }; @@ -55,7 +56,7 @@ pub(crate) fn expand( } // #[rustc_std_internal_symbol] -// unsafe fn __rg_oom(size: usize, align: usize) -> ! { +// unsafe fn __rust_alloc_error_handler(size: usize, align: usize) -> ! { // handler(core::alloc::Layout::from_size_align_unchecked(size, align)) // } fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt { @@ -84,7 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let kind = ItemKind::Fn(Box::new(Fn { defaultness: ast::Defaultness::Final, sig, - ident: Ident::from_str_and_span("__rg_oom", span), + ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), generics: Generics::default(), contract: None, body, diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 04f1129d87c1f..9533f870434ab 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,8 +3,8 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_session::config::OomStrategy; @@ -72,17 +72,19 @@ fn codegen_inner( } } - let sig = Signature { - call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], - returns: vec![], - }; - crate::common::create_wrapper_function( - module, - sig, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), - ); + if alloc_error_handler_kind == AllocatorKind::Default { + let sig = Signature { + call_conv: module.target_config().default_call_conv, + params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], + returns: vec![], + }; + crate::common::create_wrapper_function( + module, + sig, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + &mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER)), + ); + } { let sig = Signature { diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 2a95a7368aac6..3f99612fcdb02 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -2,8 +2,8 @@ use gccjit::FnAttribute; use gccjit::{Context, FunctionType, RValue, ToRValue, Type}; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; @@ -61,15 +61,17 @@ pub(crate) unsafe fn codegen( } } - // FIXME(bjorn3): Add noreturn attribute - create_wrapper_function( - tcx, - context, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), - &[usize, usize], - None, - ); + if alloc_error_handler_kind == AllocatorKind::Default { + // FIXME(bjorn3): Add noreturn attribute + create_wrapper_function( + tcx, + context, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), + &[usize, usize], + None, + ); + } create_const_value_function( tcx, diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 486ee817a1c79..b645db3f4a8b8 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,7 +1,7 @@ use libc::c_uint; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; @@ -83,17 +83,19 @@ pub(crate) unsafe fn codegen( } } - // rust alloc error handler - create_wrapper_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), - &[usize, usize], // size, align - None, - true, - &CodegenFnAttrs::new(), - ); + if alloc_error_handler_kind == AllocatorKind::Default { + // rust alloc error handler + create_wrapper_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), + &[usize, usize], // size, align + None, + true, + &CodegenFnAttrs::new(), + ); + } // __rust_alloc_error_handler_should_panic_v2 create_const_value_function( diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index b49e67217fb01..5bc18e2d7f8bb 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -1,7 +1,9 @@ use std::collections::hash_map::Entry::*; use rustc_abi::{CanonAbi, X86Call}; -use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name}; +use rustc_ast::expand::allocator::{ + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name, +}; use rustc_data_structures::unord::UnordMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId}; @@ -498,7 +500,7 @@ pub(crate) fn allocator_shim_symbols( .iter() .map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) .chain([ - mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + mangle_internal_symbol(tcx, global_fn_name(ALLOC_ERROR_HANDLER).as_str()), mangle_internal_symbol(tcx, OomStrategy::SYMBOL), mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), ]) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 7650acbd292d2..87d64817466d9 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use std::time::Duration; use std::{cmp, env, iter}; -use rustc_ast::expand::allocator::{AllocatorKind, alloc_error_handler_name, global_fn_name}; +use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name}; use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; @@ -1087,17 +1087,17 @@ impl CStore { } spans => !spans.is_empty(), }; - self.has_alloc_error_handler = match &*fn_spans( - krate, - Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)), - ) { - [span1, span2, ..] => { - tcx.dcx() - .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); - true - } - spans => !spans.is_empty(), - }; + self.has_alloc_error_handler = + match &*fn_spans(krate, Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER))) { + [span1, span2, ..] => { + tcx.dcx().emit_err(errors::NoMultipleAllocErrorHandler { + span2: *span2, + span1: *span1, + }); + true + } + spans => !spans.is_empty(), + }; // Check to see if we actually need an allocator. This desire comes // about through the `#![needs_allocator]` attribute and is typically diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 65c8206e9d462..39450f69ce30a 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -361,7 +361,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { unsafe extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the - // default implementations below (`__rdl_oom`) otherwise. + // default implementations below (`__rdl_alloc_error_handler`) otherwise. #[rustc_std_internal_symbol] fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } @@ -425,7 +425,7 @@ pub mod __alloc_error_handler { // called via generated `__rust_alloc_error_handler` if there is no // `#[alloc_error_handler]`. #[rustc_std_internal_symbol] - pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! { + pub unsafe fn __rdl_alloc_error_handler(size: usize, _align: usize) -> ! { unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 1d61630269ac3..daa25c5a50dd6 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -358,9 +358,10 @@ fn default_alloc_error_hook(layout: Layout) { // This is the default path taken on OOM, and the only path taken on stable with std. // Crucially, it does *not* call any user-defined code, and therefore users do not have to // worry about allocation failure causing reentrancy issues. That makes it different from - // the default `__rdl_oom` defined in alloc (i.e., the default alloc error handler that is - // called when there is no `#[alloc_error_handler]`), which triggers a regular panic and - // thus can invoke a user-defined panic hook, executing arbitrary user-defined code. + // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error + // handler that is called when there is no `#[alloc_error_handler]`), which triggers a + // regular panic and thus can invoke a user-defined panic hook, executing arbitrary + // user-defined code. rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index eca8cccf5efc4..467ac2805fe5e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; -use rustc_ast::expand::allocator::alloc_error_handler_name; +use rustc_ast::expand::allocator::AllocatorKind; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; @@ -51,6 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Some shims forward to other MIR bodies. match link_name.as_str() { + // FIXME should use global_fn_name, but mangle_internal_symbol requires a static str. name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => { // Forward to the right symbol that implements this function. let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { @@ -59,12 +60,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" ); }; - let name = Symbol::intern( - this.mangle_internal_symbol(alloc_error_handler_name(handler_kind)), - ); - let handler = - this.lookup_exported_symbol(name)?.expect("missing alloc error handler symbol"); - return interp_ok(Some(handler)); + if handler_kind == AllocatorKind::Default { + let name = + Symbol::intern(this.mangle_internal_symbol("__rdl_alloc_error_handler")); + let handler = this + .lookup_exported_symbol(name)? + .expect("missing alloc error handler symbol"); + return interp_ok(Some(handler)); + } } _ => {} } diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index bbf5d14a98a1e..7f7fc63ac0ddd 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -7,7 +7,7 @@ LL | crate::process::abort() | = note: BACKTRACE: = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC - = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::_::__rust_alloc_error_handler` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr index a2a4be30eca4a..f8a96758aa2d3 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr @@ -7,7 +7,7 @@ LL | core::intrinsics::abort(); | = note: BACKTRACE: = note: inside `alloc_error_handler` at tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC -note: inside `_::__rg_oom` +note: inside `_::__rust_alloc_error_handler` --> tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC | LL | #[alloc_error_handler] diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr index 45ba366acae32..488b1d879e874 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -9,7 +9,7 @@ LL | core::intrinsics::abort(); | = note: BACKTRACE: = note: inside `panic_handler` at tests/fail/alloc/alloc_error_handler_no_std.rs:LL:CC - = note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::__alloc_error_handler::__rdl_alloc_error_handler` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `miri_start` diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs index 2faeb20025bb7..0822bd6dfd6a8 100644 --- a/tests/run-make/bin-emit-no-symbols/rmake.rs +++ b/tests/run-make/bin-emit-no-symbols/rmake.rs @@ -14,5 +14,5 @@ fn main() { let out = llvm_readobj().input("app.o").arg("--symbols").run(); out.assert_stdout_contains("rust_begin_unwind"); out.assert_stdout_contains("rust_eh_personality"); - out.assert_stdout_contains("__rg_oom"); + out.assert_stdout_contains("__rust_alloc_error_handler"); } diff --git a/tests/ui/sanitizer/dataflow-abilist.txt b/tests/ui/sanitizer/dataflow-abilist.txt index 3d32397a175d4..b6fdfe3cbf6e7 100644 --- a/tests/ui/sanitizer/dataflow-abilist.txt +++ b/tests/ui/sanitizer/dataflow-abilist.txt @@ -490,16 +490,6 @@ fun:__dfso_*=uninstrumented fun:__dfso_*=discard # Rust functions. -fun:__rdl_alloc=uninstrumented -fun:__rdl_alloc_zeroed=uninstrumented -fun:__rdl_dealloc=uninstrumented -fun:__rdl_realloc=uninstrumented -fun:__rg_oom=uninstrumented -fun:__rust_alloc=uninstrumented -fun:__rust_alloc_error_handler=uninstrumented -fun:__rust_alloc_zeroed=uninstrumented -fun:__rust_dealloc=uninstrumented -fun:__rust_realloc=uninstrumented fun:_ZN4core*=uninstrumented fun:_ZN3std*=uninstrumented fun:rust_eh_personality=uninstrumented From 7e467cd132d0546c26be02d160ca097e00e67522 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Oct 2025 14:08:55 +0000 Subject: [PATCH 1507/1900] Move computation of allocator shim contents to cg_ssa In the future this should make it easier to use weak symbols for the allocator shim on platforms that properly support weak symbols. And it would allow reusing the allocator shim code for handling default implementations of the upcoming externally implementable items feature on platforms that don't properly support weak symbols. --- compiler/rustc_ast/src/expand/allocator.rs | 3 + .../src/global_allocator.rs | 4 +- .../rustc_codegen_cranelift/src/allocator.rs | 79 +++++-------- compiler/rustc_codegen_gcc/src/allocator.rs | 64 +++++------ compiler/rustc_codegen_gcc/src/lib.rs | 7 +- compiler/rustc_codegen_llvm/src/allocator.rs | 104 ++++++++---------- compiler/rustc_codegen_llvm/src/lib.rs | 7 +- compiler/rustc_codegen_ssa/src/base.rs | 34 ++++-- .../rustc_codegen_ssa/src/traits/backend.rs | 5 +- 9 files changed, 139 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index a3f9ddb154f71..6392cb77413dd 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -18,14 +18,17 @@ pub fn default_fn_name(base: Symbol) -> String { pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; +#[derive(Copy, Clone)] pub enum AllocatorTy { Layout, + Never, Ptr, ResultPtr, Unit, Usize, } +#[derive(Copy, Clone)] pub struct AllocatorMethod { pub name: Symbol, pub inputs: &'static [AllocatorMethodInput], diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 96bece2a368cd..c968353504e15 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -151,7 +151,7 @@ impl AllocFnFactory<'_, '_> { self.cx.expr_ident(self.span, ident) } - AllocatorTy::ResultPtr | AllocatorTy::Unit => { + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } } @@ -163,7 +163,7 @@ impl AllocFnFactory<'_, '_> { AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(ThinVec::new())), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + AllocatorTy::Layout | AllocatorTy::Never | AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("can't convert `AllocatorTy` to an output") } } diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 9533f870434ab..67b89114356b5 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,10 +3,9 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; -use rustc_codegen_ssa::base::allocator_kind_for_codegen; +use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_session::config::OomStrategy; use rustc_symbol_mangling::mangle_internal_symbol; @@ -15,74 +14,54 @@ use crate::prelude::*; /// Returns whether an allocator shim was created pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; - codegen_inner( - tcx, - module, - kind, - tcx.alloc_error_handler_kind(()).unwrap(), - tcx.sess.opts.unstable_opts.oom, - ); + let methods = allocator_shim_contents(tcx, kind); + codegen_inner(tcx, module, &methods, tcx.sess.opts.unstable_opts.oom); true } fn codegen_inner( tcx: TyCtxt<'_>, module: &mut dyn Module, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], oom_strategy: OomStrategy, ) { let usize_ty = module.target_config().pointer_type(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut arg_tys = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - arg_tys.push(usize_ty); // size - arg_tys.push(usize_ty); // align - } - AllocatorTy::Ptr => arg_tys.push(usize_ty), - AllocatorTy::Usize => arg_tys.push(usize_ty), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut arg_tys = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + arg_tys.push(usize_ty); // size + arg_tys.push(usize_ty); // align } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(usize_ty), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => arg_tys.push(usize_ty), + AllocatorTy::Usize => arg_tys.push(usize_ty), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - - let sig = Signature { - call_conv: module.target_config().default_call_conv, - params: arg_tys.iter().cloned().map(AbiParam::new).collect(), - returns: output.into_iter().map(AbiParam::new).collect(), - }; - crate::common::create_wrapper_function( - module, - sig, - &mangle_internal_symbol(tcx, &global_fn_name(method.name)), - &mangle_internal_symbol(tcx, &default_fn_name(method.name)), - ); + } } - } + let output = match method.output { + AllocatorTy::ResultPtr => Some(usize_ty), + AllocatorTy::Never | AllocatorTy::Unit => None, + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; - if alloc_error_handler_kind == AllocatorKind::Default { let sig = Signature { call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], - returns: vec![], + params: arg_tys.iter().cloned().map(AbiParam::new).collect(), + returns: output.into_iter().map(AbiParam::new).collect(), }; crate::common::create_wrapper_function( module, sig, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - &mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER)), + &mangle_internal_symbol(tcx, &global_fn_name(method.name)), + &mangle_internal_symbol(tcx, &default_fn_name(method.name)), ); } diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 3f99612fcdb02..647569694b04d 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -2,8 +2,7 @@ use gccjit::FnAttribute; use gccjit::{Context, FunctionType, RValue, ToRValue, Type}; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; @@ -18,8 +17,7 @@ pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) { let context = &mods.context; let usize = match tcx.sess.target.pointer_width { @@ -31,46 +29,34 @@ pub(crate) unsafe fn codegen( let i8 = context.new_type::(); let i8p = i8.make_pointer(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut types = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - types.push(usize); - types.push(usize); - } - AllocatorTy::Ptr => types.push(i8p), - AllocatorTy::Usize => types.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut types = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + types.push(usize); + types.push(usize); } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => types.push(i8p), + AllocatorTy::Usize => types.push(usize), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); - let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - - create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output); + } } - } + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Never | AllocatorTy::Unit => None, - if alloc_error_handler_kind == AllocatorKind::Default { - // FIXME(bjorn3): Add noreturn attribute - create_wrapper_function( - tcx, - context, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), - &[usize, usize], - None, - ); + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); + + create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output); } create_const_value_function( diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index ec7eab8489ab8..c85ed0ebb3390 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -92,7 +92,7 @@ use back::lto::{ThinBuffer, ThinData}; use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] use gccjit::{TargetInfo, Version}; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, @@ -284,8 +284,7 @@ impl ExtraBackendMethods for GccCodegenBackend { &self, tcx: TyCtxt<'_>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> Self::Module { let mut mods = GccContext { context: Arc::new(SyncContext::new(new_context(tcx))), @@ -295,7 +294,7 @@ impl ExtraBackendMethods for GccCodegenBackend { }; unsafe { - allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); + allocator::codegen(tcx, &mut mods, module_name, methods); } mods } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index b645db3f4a8b8..a7e83f65151ae 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,7 +1,6 @@ use libc::c_uint; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; @@ -21,8 +20,7 @@ pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, cx: SimpleCx<'_>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) { let usize = match tcx.sess.target.pointer_width { 16 => cx.type_i16(), @@ -33,67 +31,59 @@ pub(crate) unsafe fn codegen( let i8 = cx.type_i8(); let i8p = cx.type_ptr(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut args = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - args.push(usize); // size - args.push(usize); // align - } - AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::Usize => args.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut args = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - - let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); - let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - - let alloc_attr_flag = match method.name { - sym::alloc => CodegenFnAttrFlags::ALLOCATOR, - sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, - sym::realloc => CodegenFnAttrFlags::REALLOCATOR, - sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, - _ => unreachable!("Unknown allocator method!"), - }; - - let mut attrs = CodegenFnAttrs::new(); - attrs.flags |= alloc_attr_flag; - create_wrapper_function( - tcx, - &cx, - &from_name, - Some(&to_name), - &args, - output, - false, - &attrs, - ); + } } - } - if alloc_error_handler_kind == AllocatorKind::Default { - // rust alloc error handler + let mut no_return = false; + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, + AllocatorTy::Never => { + no_return = true; + None + } + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); + + let alloc_attr_flag = match method.name { + sym::alloc => CodegenFnAttrFlags::ALLOCATOR, + sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, + sym::realloc => CodegenFnAttrFlags::REALLOCATOR, + sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, + _ => CodegenFnAttrFlags::empty(), + }; + + let mut attrs = CodegenFnAttrs::new(); + attrs.flags |= alloc_attr_flag; create_wrapper_function( tcx, &cx, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), - &[usize, usize], // size, align - None, - true, - &CodegenFnAttrs::new(), + &from_name, + Some(&to_name), + &args, + output, + no_return, + &attrs, ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 807049f08d367..34fdb4a47c0ff 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -30,7 +30,7 @@ use back::write::{create_informational_target_machine, create_target_machine}; use context::SimpleCx; use errors::ParseTargetMachineConfig; use llvm_util::target_config; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, @@ -107,14 +107,13 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, tcx: TyCtxt<'tcx>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> ModuleLlvm { let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); let cx = SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); unsafe { - allocator::codegen(tcx, cx, module_name, kind, alloc_error_handler_kind); + allocator::codegen(tcx, cx, module_name, methods); } module_llvm } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ac3bcb1ea269f..cd9fb098e531d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -6,7 +6,9 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast as ast; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::{ + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorTy, +}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; @@ -655,6 +657,26 @@ pub(crate) fn needs_allocator_shim_for_linking( !any_dynamic_crate } +pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec { + let mut methods = Vec::new(); + + if kind == AllocatorKind::Default { + methods.extend(ALLOCATOR_METHODS.into_iter().copied()); + } + + // If allocator_kind is Some then alloc_error_handler_kind must + // also be Some. + if tcx.alloc_error_handler_kind(()).unwrap() == AllocatorKind::Default { + methods.push(AllocatorMethod { + name: ALLOC_ERROR_HANDLER, + inputs: &[], + output: AllocatorTy::Never, + }); + } + + methods +} + pub fn codegen_crate( backend: B, tcx: TyCtxt<'_>, @@ -699,14 +721,8 @@ pub fn codegen_crate( cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); tcx.sess.time("write_allocator_module", || { - let module = backend.codegen_allocator( - tcx, - &llmod_id, - kind, - // If allocator_kind is Some then alloc_error_handler_kind must - // also be Some. - tcx.alloc_error_handler_kind(()).unwrap(), - ); + let module = + backend.codegen_allocator(tcx, &llmod_id, &allocator_shim_contents(tcx, kind)); Some(ModuleCodegen::new_allocator(llmod_id, module)) }) } else { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 2400160075e2d..ec53d9f53eb83 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::hash::Hash; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_metadata::EncodedMetadata; @@ -116,8 +116,7 @@ pub trait ExtraBackendMethods: &self, tcx: TyCtxt<'tcx>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> Self::Module; /// This generates the codegen unit and returns it along with From 2e25b5876fe7a9a06bb5a2248a2e54e86937e002 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:24:41 +0000 Subject: [PATCH 1508/1900] Add a couple of comments Co-Authored-By: Ralf Jung --- src/tools/miri/src/shims/foreign_items.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 467ac2805fe5e..39e32322f5b8d 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -51,6 +51,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Some shims forward to other MIR bodies. match link_name.as_str() { + // This allocator function has forwarding shims synthesized during normal codegen + // (see `allocator_shim_contents`); this is where we emulate that behavior. // FIXME should use global_fn_name, but mangle_internal_symbol requires a static str. name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => { // Forward to the right symbol that implements this function. @@ -68,6 +70,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .expect("missing alloc error handler symbol"); return interp_ok(Some(handler)); } + // Fall through to the `lookup_exported_symbol` below which should find + // a `__rust_alloc_error_handler`. } _ => {} } From ce671f998f9093e9fb215cc7e6ee036bc7094c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 9 Oct 2025 11:27:35 +0200 Subject: [PATCH 1509/1900] Update rustc-perf submodule --- src/tools/rustc-perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index dde879cf1087c..c0301bc44d175 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit dde879cf1087cb34a32287bd8ccc4d545bb9fee5 +Subproject commit c0301bc44d175b9b2c5442b25049475c39d7700c From 0abecda9edebdea76e07092ffb4f6c4261b25228 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Fri, 10 Oct 2025 12:16:11 +0200 Subject: [PATCH 1510/1900] Replace `LLVMRustContextCreate` with normal LLVM-C API calls Since `LLVMRustContextCreate` can easily be replaced with a call to `LLVMContextCreate` and `LLVMContextSetDiscardValueNames`. --- compiler/rustc_codegen_llvm/src/lib.rs | 11 ++++++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 5 ++--- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 807049f08d367..a2ed11a83b13a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -48,6 +48,8 @@ use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; use rustc_target::spec::{RelocModel, TlsModel}; +use crate::llvm::ToLlvmBool; + mod abi; mod allocator; mod asm; @@ -384,7 +386,8 @@ unsafe impl Sync for ModuleLlvm {} impl ModuleLlvm { fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; ModuleLlvm { llmod_raw, @@ -396,7 +399,8 @@ impl ModuleLlvm { fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; ModuleLlvm { llmod_raw, @@ -427,7 +431,8 @@ impl ModuleLlvm { dcx: DiagCtxtHandle<'_>, ) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 67e8bc7062bd9..1740dbd1684d0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -905,7 +905,9 @@ pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> unsafe extern "C" { // Create and destroy contexts. + pub(crate) fn LLVMContextCreate() -> &'static mut Context; pub(crate) fn LLVMContextDispose(C: &'static mut Context); + pub(crate) fn LLVMContextSetDiscardValueNames(C: &Context, Discard: Bool); pub(crate) fn LLVMGetMDKindIDInContext( C: &Context, Name: *const c_char, @@ -1925,9 +1927,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustInstallErrorHandlers(); pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); - // Create and destroy contexts. - pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; - // Operations on all values pub(crate) fn LLVMRustGlobalAddMetadata<'a>( Val: &'a Value, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 3722f8d689e53..094e018058aad 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -123,12 +123,6 @@ extern "C" void LLVMRustSetLastError(const char *Err) { LastError = strdup(Err); } -extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { - auto ctx = new LLVMContext(); - ctx->setDiscardValueNames(shouldDiscardNames); - return wrap(ctx); -} - extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, const char *Target) { #if LLVM_VERSION_GE(21, 0) From 88e98206836bcf80b33dce9d77298c86c8778e96 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:51:52 +0000 Subject: [PATCH 1511/1900] Fix review comments --- compiler/rustc_ast/src/expand/allocator.rs | 1 + compiler/rustc_codegen_ssa/src/base.rs | 4 ++-- compiler/rustc_metadata/src/creader.rs | 20 +++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 6392cb77413dd..336126a01f47e 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -18,6 +18,7 @@ pub fn default_fn_name(base: Symbol) -> String { pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; +/// Argument or return type for methods in the allocator shim #[derive(Copy, Clone)] pub enum AllocatorTy { Layout, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index cd9fb098e531d..1a79038d1fcd1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -664,8 +664,8 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec !spans.is_empty(), }; - self.has_alloc_error_handler = - match &*fn_spans(krate, Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER))) { - [span1, span2, ..] => { - tcx.dcx().emit_err(errors::NoMultipleAllocErrorHandler { - span2: *span2, - span1: *span1, - }); - true - } - spans => !spans.is_empty(), - }; + let alloc_error_handler = Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER)); + self.has_alloc_error_handler = match &*fn_spans(krate, alloc_error_handler) { + [span1, span2, ..] => { + tcx.dcx() + .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); + true + } + spans => !spans.is_empty(), + }; // Check to see if we actually need an allocator. This desire comes // about through the `#![needs_allocator]` attribute and is typically From 1ee2c58e8925315a45500d73c5527b7a340cc8a5 Mon Sep 17 00:00:00 2001 From: dianqk Date: Fri, 10 Oct 2025 13:50:01 +0800 Subject: [PATCH 1512/1900] Replace locals in debuginfo records during dest_prop --- compiler/rustc_mir_transform/src/dest_prop.rs | 1 - ...t_prop.remap_debuginfo_locals.DestinationPropagation.diff | 3 ++- tests/mir-opt/debuginfo/dest_prop.rs | 5 ++++- .../debuginfo/dest_prop_debuginfo-147485.rs} | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) rename tests/{crashes/147485-2.rs => ui/debuginfo/dest_prop_debuginfo-147485.rs} (81%) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 1f38433fa5a41..92556e7c5f6c5 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -277,7 +277,6 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { if self.merged_locals.contains(*local) => { statement.make_nop(true); - return; } _ => (), }; diff --git a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff index 6f3233d85c9fc..fe00da67e8bb0 100644 --- a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff +++ b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff @@ -9,11 +9,12 @@ let mut _4: bool; bb0: { - // DBG: _3 = &_1; +- // DBG: _3 = &_1; - StorageLive(_4); - _4 = copy _1; - _3 = copy _2; - switchInt(copy _4) -> [1: bb1, otherwise: bb2]; ++ // DBG: _2 = &_1; + nop; + nop; + nop; diff --git a/tests/mir-opt/debuginfo/dest_prop.rs b/tests/mir-opt/debuginfo/dest_prop.rs index 9c9d95ebfb3f3..a734cacb4d29a 100644 --- a/tests/mir-opt/debuginfo/dest_prop.rs +++ b/tests/mir-opt/debuginfo/dest_prop.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: DestinationPropagation //@ compile-flags: -g -Zmir-enable-passes=+DeadStoreElimination-initial @@ -10,6 +9,10 @@ use std::intrinsics::mir::*; // EMIT_MIR dest_prop.remap_debuginfo_locals.DestinationPropagation.diff #[custom_mir(dialect = "runtime", phase = "post-cleanup")] pub fn remap_debuginfo_locals(a: bool, b: &bool) -> &bool { + // CHECK-LABEL: fn remap_debuginfo_locals( + // CHECK: debug c => [[c:_.*]]; + // CHECK: bb0: + // CHECK-NEXT: DBG: [[c]] = &_1; mir! { let _3: &bool; let _4: bool; diff --git a/tests/crashes/147485-2.rs b/tests/ui/debuginfo/dest_prop_debuginfo-147485.rs similarity index 81% rename from tests/crashes/147485-2.rs rename to tests/ui/debuginfo/dest_prop_debuginfo-147485.rs index da092a3af76aa..652660d473b15 100644 --- a/tests/crashes/147485-2.rs +++ b/tests/ui/debuginfo/dest_prop_debuginfo-147485.rs @@ -1,6 +1,8 @@ -//@ known-bug: #147485 +//@ build-pass //@ compile-flags: -g -O +// Regression test for #147485. + #![crate_type = "lib"] pub fn foo(a: bool, b: bool) -> bool { From 35142a6ab8ca9178e189a826dc829e2a6c650098 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 8 Oct 2025 16:02:42 +0300 Subject: [PATCH 1513/1900] Migrate MIR to next solver --- src/tools/rust-analyzer/Cargo.lock | 11 + src/tools/rust-analyzer/Cargo.toml | 1 + .../crates/hir-def/src/signatures.rs | 14 + .../rust-analyzer/crates/hir-ty/Cargo.toml | 1 + .../crates/hir-ty/src/builder.rs | 15 +- .../crates/hir-ty/src/consteval.rs | 396 ++++---- .../crates/hir-ty/src/consteval/tests.rs | 33 +- .../crates/hir-ty/src/consteval_chalk.rs | 185 ++++ .../crates/hir-ty/src/consteval_nextsolver.rs | 250 ----- .../rust-analyzer/crates/hir-ty/src/db.rs | 30 +- .../crates/hir-ty/src/display.rs | 8 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 4 +- .../hir-ty/src/infer/closure/analysis.rs | 41 +- .../crates/hir-ty/src/infer/expr.rs | 10 +- .../crates/hir-ty/src/infer/pat.rs | 8 +- .../crates/hir-ty/src/infer/path.rs | 4 +- .../crates/hir-ty/src/inhabitedness.rs | 15 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 18 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 2 +- .../crates/hir-ty/src/lower/path.rs | 2 +- .../crates/hir-ty/src/lower_nextsolver.rs | 2 +- .../hir-ty/src/lower_nextsolver/path.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 124 +-- .../rust-analyzer/crates/hir-ty/src/mir.rs | 393 ++++---- .../crates/hir-ty/src/mir/borrowck.rs | 191 ++-- .../crates/hir-ty/src/mir/eval.rs | 922 ++++++++---------- .../crates/hir-ty/src/mir/eval/shim.rs | 301 +++--- .../crates/hir-ty/src/mir/eval/shim/simd.rs | 39 +- .../crates/hir-ty/src/mir/eval/tests.rs | 21 +- .../crates/hir-ty/src/mir/lower.rs | 543 ++++++----- .../crates/hir-ty/src/mir/lower/as_place.rs | 120 +-- .../hir-ty/src/mir/lower/pattern_matching.rs | 160 ++- .../crates/hir-ty/src/mir/lower/tests.rs | 21 +- .../crates/hir-ty/src/mir/monomorphization.rs | 302 ++---- .../crates/hir-ty/src/mir/pretty.rs | 66 +- .../crates/hir-ty/src/next_solver/consts.rs | 82 +- .../crates/hir-ty/src/next_solver/def_id.rs | 14 +- .../hir-ty/src/next_solver/generic_arg.rs | 75 +- .../hir-ty/src/next_solver/infer/select.rs | 7 +- .../hir-ty/src/next_solver/infer/traits.rs | 38 +- .../crates/hir-ty/src/next_solver/interner.rs | 126 ++- .../crates/hir-ty/src/next_solver/mapping.rs | 8 +- .../hir-ty/src/next_solver/predicate.rs | 25 +- .../crates/hir-ty/src/next_solver/solver.rs | 65 +- .../crates/hir-ty/src/next_solver/ty.rs | 35 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 22 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 33 +- .../crates/hir/src/source_analyzer.rs | 13 +- .../crates/ide/src/hover/tests.rs | 10 +- .../ide/src/inlay_hints/implicit_drop.rs | 4 +- .../rust-analyzer/crates/macros/Cargo.toml | 19 + .../rust-analyzer/crates/macros/src/lib.rs | 164 ++++ 53 files changed, 2493 insertions(+), 2504 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs create mode 100644 src/tools/rust-analyzer/crates/macros/Cargo.toml create mode 100644 src/tools/rust-analyzer/crates/macros/src/lib.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8e2c371bceaa5..6625403572b0b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "intern", "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "macros", "oorandom", "petgraph", "project-model", @@ -1329,6 +1330,16 @@ dependencies = [ "url", ] +[[package]] +name = "macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "mbe" version = "0.0.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 1438d4638922a..6702bfed872c9 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -52,6 +52,7 @@ debug = 2 [workspace.dependencies] # local crates +macros = { path = "./crates/macros", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" } cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } hir = { path = "./crates/hir", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 47638610ed734..ebbf87cad668b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -349,6 +349,7 @@ bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct ImplFlags: u8 { const NEGATIVE = 1 << 1; + const DEFAULT = 1 << 2; const UNSAFE = 1 << 3; } } @@ -374,6 +375,9 @@ impl ImplSignature { if src.value.excl_token().is_some() { flags.insert(ImplFlags::NEGATIVE); } + if src.value.default_token().is_some() { + flags.insert(ImplFlags::DEFAULT); + } let (store, source_map, self_ty, target_trait, generic_params) = crate::expr_store::lower::lower_impl(db, loc.container, src, id); @@ -389,6 +393,16 @@ impl ImplSignature { Arc::new(source_map), ) } + + #[inline] + pub fn is_negative(&self) -> bool { + self.flags.contains(ImplFlags::NEGATIVE) + } + + #[inline] + pub fn is_default(&self) -> bool { + self.flags.contains(ImplFlags::DEFAULT) + } } bitflags::bitflags! { diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 4013d19ad08eb..ec6563315407a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -50,6 +50,7 @@ tracing-tree.workspace = true # local deps stdx.workspace = true +macros.workspace = true intern.workspace = true hir-def.workspace = true hir-expand.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 798b0f2c0c246..706bbe856c67d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -130,11 +130,14 @@ impl TyBuilder { } pub fn fill_with_unknown(self) -> Self { + let interner = DbInterner::conjure(); // self.fill is inlined to make borrow checker happy let mut this = self; let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), - ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Const(ty) => { + unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner) + } ParamKind::Lifetime => error_lifetime().cast(Interner), }); this.vec.extend(filler.casted(Interner)); @@ -219,13 +222,16 @@ impl TyBuilder<()> { } pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into) -> Substitution { + let interner = DbInterner::conjure(); let params = generics(db, def.into()); Substitution::from_iter( Interner, params.iter_id().map(|id| match id { GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::ConstParamId(id) => { - unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) + unknown_const_as_generic(db.const_param_ty_ns(id)) + .to_chalk(interner) + .cast(Interner) } GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }), @@ -267,6 +273,7 @@ impl TyBuilder { db: &dyn HirDatabase, mut fallback: impl FnMut() -> Ty, ) -> Self { + let interner = DbInterner::conjure(); // Note that we're building ADT, so we never have parent generic parameters. let defaults = db.generic_defaults(self.data.into()); @@ -287,7 +294,9 @@ impl TyBuilder { // The defaults may be missing if no param has default, so fill that. let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x { ParamKind::Type => fallback().cast(Interner), - ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Const(ty) => { + unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner) + } ParamKind::Lifetime => error_lifetime().cast(Interner), }); self.vec.extend(filler.casted(Interner)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 8b12c5fd890c1..002e0823b9d17 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -1,59 +1,88 @@ //! Constant evaluation details +#[cfg(test)] +mod tests; + use base_db::Crate; -use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast}; use hir_def::{ - EnumVariantId, GeneralConstId, HasModule as _, StaticId, - expr_store::{HygieneId, path::Path}, - hir::Expr, + EnumVariantId, GeneralConstId, + expr_store::{Body, HygieneId, path::Path}, + hir::{Expr, ExprId}, resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, }; +use hir_def::{HasModule, StaticId}; use hir_expand::Lookup; +use rustc_type_ir::{UnevaluatedConst, inherent::IntoKind}; use stdx::never; use triomphe::Arc; use crate::{ - Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, + MemoryMap, TraitEnvironment, db::HirDatabase, display::DisplayTarget, generics::Generics, - lower::ParamLoweringMode, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, - to_placeholder_idx, + infer::InferenceContext, + mir::{MirEvalError, MirLowerError}, + next_solver::{ + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamConst, SolverDefId, Ty, ValueConst, + }, }; -use super::mir::{MirEvalError, MirLowerError, interpret_mir, pad16}; - -/// Extension trait for [`Const`] -pub trait ConstExt { - /// Is a [`Const`] unknown? - fn is_unknown(&self) -> bool; -} +use super::mir::{interpret_mir, lower_to_mir, pad16}; -impl ConstExt for Const { - fn is_unknown(&self) -> bool { - match self.data(Interner).value { - // interned Unknown - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Unknown, - }) => true, - - // interned concrete anything else - chalk_ir::ConstValue::Concrete(..) => false, - - _ => { - tracing::error!( - "is_unknown was called on a non-concrete constant value! {:?}", - self - ); - true +pub(crate) fn path_to_const<'a, 'g>( + db: &'a dyn HirDatabase, + resolver: &Resolver<'a>, + path: &Path, + args: impl FnOnce() -> &'g Generics, + _expected_ty: Ty<'a>, +) -> Option> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let args = args(); + match args + .type_or_const_param(p.into()) + .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) + { + Some((idx, _param)) => { + Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) + } + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + None + } } } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(interner, []); + Some(Const::new( + interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, } } +pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> { + Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) +} + +pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { + unknown_const(ty).into() +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstEvalError<'db> { MirLowerError(MirLowerError<'db>), @@ -94,149 +123,189 @@ impl<'db> From> for ConstEvalError<'db> { } } -pub(crate) fn path_to_const<'g>( - db: &dyn HirDatabase, - resolver: &Resolver<'_>, - path: &Path, - mode: ParamLoweringMode, - args: impl FnOnce() -> &'g Generics, - debruijn: DebruijnIndex, - expected_ty: Ty, -) -> Option { - match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { - Some(ValueNs::GenericParam(p)) => { - let ty = db.const_param_ty(p); - let args = args(); - let value = match mode { - ParamLoweringMode::Placeholder => { - let idx = args.type_or_const_param_idx(p.into()).unwrap(); - ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32)) - } - ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) { - Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - return None; - } - }, - }; - Some(ConstData { ty, value }.intern(Interner)) - } - Some(ValueNs::ConstId(c)) => Some(intern_const_scalar( - ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), - expected_ty, - )), - // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors. - _ => None, - } -} - -pub fn unknown_const(ty: Ty) -> Const { - ConstData { - ty, - value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }), - } - .intern(Interner) -} - -pub fn unknown_const_as_generic(ty: Ty) -> GenericArg { - unknown_const(ty).cast(Interner) -} - -/// Interns a constant scalar with the given type -pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const { - ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) } - .intern(Interner) -} - /// Interns a constant scalar with the given type -pub fn intern_const_ref( - db: &dyn HirDatabase, +pub fn intern_const_ref<'a>( + db: &'a dyn HirDatabase, value: &LiteralConstRef, - ty: Ty, + ty: Ty<'a>, krate: Crate, -) -> Const { +) -> Const<'a> { let interner = DbInterner::new_with(db, Some(krate), None); - let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate)); - let bytes = match value { + let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate)); + let kind = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. - let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { + memory: i.to_le_bytes()[0..size].into(), + memory_map: MemoryMap::default(), + }, + )) } LiteralConstRef::UInt(i) => { - let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) - } - LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()), - LiteralConstRef::Char(c) => { - ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default()) + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { + memory: i.to_le_bytes()[0..size].into(), + memory_map: MemoryMap::default(), + }, + )) } - LiteralConstRef::Unknown => ConstScalar::Unknown, + LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() }, + )), + LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { + memory: (*c as u32).to_le_bytes().into(), + memory_map: MemoryMap::default(), + }, + )), + LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), }; - intern_const_scalar(bytes, ty) + Const::new(interner, kind) } /// Interns a possibly-unknown target usize -pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: Crate) -> Const { +pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { intern_const_ref( db, &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), - TyBuilder::usize(), + Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), krate, ) } -pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { - match &c.data(Interner).value { - chalk_ir::ConstValue::BoundVar(_) => None, - chalk_ir::ConstValue::InferenceVar(_) => None, - chalk_ir::ConstValue::Placeholder(_) => None, - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))), - ConstScalar::UnevaluatedConst(c, subst) => { - let ec = db.const_eval(*c, subst.clone(), None).ok()?; - try_const_usize(db, &ec) - } - _ => None, - }, +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { + match c.kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = unevaluated_const.args; + let ec = db.const_eval(c, subst, None).ok()?; + try_const_usize(db, ec) + } + ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, } } -pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { - match &c.data(Interner).value { - chalk_ir::ConstValue::BoundVar(_) => None, - chalk_ir::ConstValue::InferenceVar(_) => None, - chalk_ir::ConstValue::Placeholder(_) => None, - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))), - ConstScalar::UnevaluatedConst(c, subst) => { - let ec = db.const_eval(*c, subst.clone(), None).ok()?; - try_const_isize(db, &ec) +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = unevaluated_const.args; + let ec = db.const_eval(c, subst, None).ok()?; + try_const_isize(db, &ec) + } + ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub(crate) fn const_eval_discriminant_variant<'db>( + db: &'db dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result> { + let interner = DbInterner::new_with(db, None, None); + let def = variant_id.into(); + let body = db.body(def); + let loc = variant_id.lookup(db); + if matches!(body[body.body_expr], Expr::Missing) { + let prev_idx = loc.index.checked_sub(1); + let value = match prev_idx { + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + loc.parent.enum_variants(db).variants[prev_idx as usize].0, + )? } - _ => None, - }, + _ => 0, + }; + return Ok(value); } + + let repr = db.enum_signature(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + + let mir_body = db.monomorphized_mir_body( + def, + GenericArgs::new_from_iter(interner, []), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, mir_body, false, None)?.0?; + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, c).unwrap() as i128 + }; + Ok(c) +} + +// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should +// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here +// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. +pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> { + let infer = ctx.fixme_resolve_all_clone(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + r + } + if has_closure(ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr]); + } + if let Expr::Path(p) = &ctx.body[expr] { + let resolver = &ctx.resolver; + if let Some(c) = path_to_const(ctx.db, resolver, p, || ctx.generics(), infer[expr]) { + return c; + } + } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + { + return result; + } + unknown_const(infer[expr]) } pub(crate) fn const_eval_cycle_result<'db>( _: &'db dyn HirDatabase, _: GeneralConstId, - _: Substitution, + _: GenericArgs<'db>, _: Option>>, -) -> Result> { +) -> Result, ConstEvalError<'db>> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } pub(crate) fn const_eval_static_cycle_result<'db>( _: &'db dyn HirDatabase, _: StaticId, -) -> Result> { +) -> Result, ConstEvalError<'db>> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } @@ -250,9 +319,9 @@ pub(crate) fn const_eval_discriminant_cycle_result<'db>( pub(crate) fn const_eval_query<'db>( db: &'db dyn HirDatabase, def: GeneralConstId, - subst: Substitution, + subst: GenericArgs<'db>, trait_env: Option>>, -) -> Result> { +) -> Result, ConstEvalError<'db>> { let body = match def { GeneralConstId::ConstId(c) => { db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))? @@ -269,52 +338,13 @@ pub(crate) fn const_eval_query<'db>( pub(crate) fn const_eval_static_query<'db>( db: &'db dyn HirDatabase, def: StaticId, -) -> Result> { +) -> Result, ConstEvalError<'db>> { + let interner = DbInterner::new_with(db, None, None); let body = db.monomorphized_mir_body( def.into(), - Substitution::empty(Interner), + GenericArgs::new_from_iter(interner, []), db.trait_environment_for_body(def.into()), )?; let c = interpret_mir(db, body, false, None)?.0?; Ok(c) } - -pub(crate) fn const_eval_discriminant_variant<'db>( - db: &'db dyn HirDatabase, - variant_id: EnumVariantId, -) -> Result> { - let def = variant_id.into(); - let body = db.body(def); - let loc = variant_id.lookup(db); - if matches!(body[body.body_expr], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); - let value = match prev_idx { - Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? - } - _ => 0, - }; - return Ok(value); - } - - let repr = db.enum_signature(loc.parent).repr; - let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); - - let mir_body = db.monomorphized_mir_body( - def, - Substitution::empty(Interner), - db.trait_environment_for_body(def), - )?; - let c = interpret_mir(db, mir_body, false, None)?.0?; - let c = if is_signed { - try_const_isize(db, &c).unwrap() - } else { - try_const_usize(db, &c).unwrap() as i128 - }; - Ok(c) -} - -#[cfg(test)] -mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 70a8ae766c0ba..f1aa06d488d44 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1,17 +1,23 @@ use base_db::RootQueryDb; -use chalk_ir::Substitution; use hir_def::db::DefDatabase; use hir_expand::EditionedFileId; use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, }; +use rustc_type_ir::inherent::IntoKind; use test_fixture::WithFixture; use test_utils::skip_slow_tests; use crate::{ - Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase, - display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB, + MemoryMap, + consteval::try_const_usize, + db::HirDatabase, + display::DisplayTarget, + mir::pad16, + next_solver::{Const, ConstBytes, ConstKind, DbInterner, GenericArgs}, + setup_tracing, + test_db::TestDB, }; use super::{ @@ -88,14 +94,12 @@ fn check_answer( panic!("Error in evaluating goal: {err}"); } }; - match &r.data(Interner).value { - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(b, mm) => { - check(b, mm); - } - x => panic!("Expected number but found {x:?}"), - }, - _ => panic!("result of const eval wasn't a concrete const"), + match r.kind() { + ConstKind::Value(value) => { + let ConstBytes { memory, memory_map } = value.value.inner(); + check(memory, memory_map); + } + _ => panic!("Expected number but found {r:?}"), } }); } @@ -117,8 +121,9 @@ fn pretty_print_err(e: ConstEvalError<'_>, db: &TestDB) -> String { err } -fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result> { +fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result, ConstEvalError<'_>> { let _tracing = setup_tracing(); + let interner = DbInterner::new_with(db, None, None); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; @@ -137,7 +142,7 @@ fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result None, }) .expect("No const named GOAL found in the test"); - db.const_eval(const_id.into(), Substitution::empty(Interner), None) + db.const_eval(const_id.into(), GenericArgs::new_from_iter(interner, []), None) } #[test] @@ -2508,7 +2513,7 @@ fn enums() { ); crate::attach_db(&db, || { let r = eval_goal(&db, file_id).unwrap(); - assert_eq!(try_const_usize(&db, &r), Some(1)); + assert_eq!(try_const_usize(&db, r), Some(1)); }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs new file mode 100644 index 0000000000000..458974384dca3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs @@ -0,0 +1,185 @@ +//! Constant evaluation details + +use base_db::Crate; +use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast}; +use hir_def::{ + expr_store::{HygieneId, path::Path}, + resolver::{Resolver, ValueNs}, + type_ref::LiteralConstRef, +}; +use stdx::never; + +use crate::{ + Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, + TraitEnvironment, Ty, TyBuilder, + db::HirDatabase, + generics::Generics, + lower::ParamLoweringMode, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, + to_placeholder_idx, +}; + +use super::mir::pad16; + +/// Extension trait for [`Const`] +pub trait ConstExt { + /// Is a [`Const`] unknown? + fn is_unknown(&self) -> bool; +} + +impl ConstExt for Const { + fn is_unknown(&self) -> bool { + match self.data(Interner).value { + // interned Unknown + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) => true, + + // interned concrete anything else + chalk_ir::ConstValue::Concrete(..) => false, + + _ => { + tracing::error!( + "is_unknown was called on a non-concrete constant value! {:?}", + self + ); + true + } + } + } +} + +pub fn path_to_const<'g>( + db: &dyn HirDatabase, + resolver: &Resolver<'_>, + path: &Path, + mode: ParamLoweringMode, + args: impl FnOnce() -> &'g Generics, + debruijn: DebruijnIndex, + expected_ty: Ty, +) -> Option { + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let ty = db.const_param_ty(p); + let args = args(); + let value = match mode { + ParamLoweringMode::Placeholder => { + let idx = args.type_or_const_param_idx(p.into()).unwrap(); + ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32)) + } + ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) { + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + return None; + } + }, + }; + Some(ConstData { ty, value }.intern(Interner)) + } + Some(ValueNs::ConstId(c)) => Some(intern_const_scalar( + ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), + expected_ty, + )), + // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors. + _ => None, + } +} + +pub fn unknown_const(ty: Ty) -> Const { + ConstData { + ty, + value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }), + } + .intern(Interner) +} + +pub fn unknown_const_as_generic(ty: Ty) -> GenericArg { + unknown_const(ty).cast(Interner) +} + +/// Interns a constant scalar with the given type +pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const { + ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) } + .intern(Interner) +} + +/// Interns a constant scalar with the given type +pub fn intern_const_ref( + db: &dyn HirDatabase, + value: &LiteralConstRef, + ty: Ty, + krate: Crate, +) -> Const { + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate)); + let bytes = match value { + LiteralConstRef::Int(i) => { + // FIXME: We should handle failure of layout better. + let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); + ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) + } + LiteralConstRef::UInt(i) => { + let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); + ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) + } + LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()), + LiteralConstRef::Char(c) => { + ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default()) + } + LiteralConstRef::Unknown => ConstScalar::Unknown, + }; + intern_const_scalar(bytes, ty) +} + +/// Interns a possibly-unknown target usize +pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: Crate) -> Const { + intern_const_ref( + db, + &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + TyBuilder::usize(), + krate, + ) +} + +pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { + let interner = DbInterner::new_with(db, None, None); + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(_) => None, + chalk_ir::ConstValue::InferenceVar(_) => None, + chalk_ir::ConstValue::Placeholder(_) => None, + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.to_nextsolver(interner), None).ok()?; + try_const_usize(db, &ec.to_chalk(interner)) + } + _ => None, + }, + } +} + +pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { + let interner = DbInterner::new_with(db, None, None); + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(_) => None, + chalk_ir::ConstValue::InferenceVar(_) => None, + chalk_ir::ConstValue::Placeholder(_) => None, + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.to_nextsolver(interner), None).ok()?; + try_const_isize(db, &ec.to_chalk(interner)) + } + _ => None, + }, + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs deleted file mode 100644 index 2509ba2ef2603..0000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! Constant evaluation details -// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir -#![allow(unused)] - -use base_db::Crate; -use hir_def::{ - EnumVariantId, GeneralConstId, - expr_store::{Body, HygieneId, path::Path}, - hir::{Expr, ExprId}, - resolver::{Resolver, ValueNs}, - type_ref::LiteralConstRef, -}; -use hir_expand::Lookup; -use rustc_type_ir::{ - UnevaluatedConst, - inherent::{IntoKind, SliceLike}, -}; -use stdx::never; -use triomphe::Arc; - -use crate::{ - ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, - consteval::ConstEvalError, - db::HirDatabase, - generics::Generics, - infer::InferenceContext, - next_solver::{ - Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamConst, SolverDefId, Ty, ValueConst, - mapping::{ChalkToNextSolver, NextSolverToChalk, convert_binder_to_early_binder}, - }, -}; - -use super::mir::{interpret_mir, lower_to_mir, pad16}; - -pub(crate) fn path_to_const<'a, 'g>( - db: &'a dyn HirDatabase, - resolver: &Resolver<'a>, - path: &Path, - args: impl FnOnce() -> &'g Generics, - expected_ty: Ty<'a>, -) -> Option> { - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { - Some(ValueNs::GenericParam(p)) => { - let args = args(); - match args - .type_or_const_param(p.into()) - .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) - { - Some((idx, _param)) => { - Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) - } - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - None - } - } - } - Some(ValueNs::ConstId(c)) => { - let args = GenericArgs::new_from_iter(interner, []); - Some(Const::new( - interner, - rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( - SolverDefId::ConstId(c), - args, - )), - )) - } - _ => None, - } -} - -pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> { - Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) -} - -pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { - unknown_const(ty).into() -} - -/// Interns a constant scalar with the given type -pub fn intern_const_ref<'a>( - db: &'a dyn HirDatabase, - value: &LiteralConstRef, - ty: Ty<'a>, - krate: Crate, -) -> Const<'a> { - let interner = DbInterner::new_with(db, Some(krate), None); - let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate)); - let kind = match value { - LiteralConstRef::Int(i) => { - // FIXME: We should handle failure of layout better. - let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), - )) - } - LiteralConstRef::UInt(i) => { - let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), - )) - } - LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes(Box::new([*b as u8]), MemoryMap::default()), - )), - LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()), - )), - LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), - }; - Const::new(interner, kind) -} - -/// Interns a possibly-unknown target usize -pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { - intern_const_ref( - db, - &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), - Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), - krate, - ) -} - -pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { - let interner = DbInterner::new_with(db, None, None); - match c.kind() { - ConstKind::Param(_) => None, - ConstKind::Infer(_) => None, - ConstKind::Bound(_, _) => None, - ConstKind::Placeholder(_) => None, - ConstKind::Unevaluated(unevaluated_const) => { - let c = match unevaluated_const.def { - SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), - SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), - _ => unreachable!(), - }; - let subst = unevaluated_const.args.to_chalk(interner); - let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); - try_const_usize(db, ec) - } - ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), - ConstKind::Error(_) => None, - ConstKind::Expr(_) => None, - } -} - -pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { - let interner = DbInterner::new_with(db, None, None); - match (*c).kind() { - ConstKind::Param(_) => None, - ConstKind::Infer(_) => None, - ConstKind::Bound(_, _) => None, - ConstKind::Placeholder(_) => None, - ConstKind::Unevaluated(unevaluated_const) => { - let c = match unevaluated_const.def { - SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), - SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), - _ => unreachable!(), - }; - let subst = unevaluated_const.args.to_chalk(interner); - let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); - try_const_isize(db, &ec) - } - ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))), - ConstKind::Error(_) => None, - ConstKind::Expr(_) => None, - } -} - -pub(crate) fn const_eval_discriminant_variant<'db>( - db: &'db dyn HirDatabase, - variant_id: EnumVariantId, -) -> Result> { - let interner = DbInterner::new_with(db, None, None); - let def = variant_id.into(); - let body = db.body(def); - let loc = variant_id.lookup(db); - if matches!(body[body.body_expr], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); - let value = match prev_idx { - Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? - } - _ => 0, - }; - return Ok(value); - } - - let repr = db.enum_signature(loc.parent).repr; - let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); - - let mir_body = db.monomorphized_mir_body( - def, - Substitution::empty(Interner), - db.trait_environment_for_body(def), - )?; - let c = interpret_mir(db, mir_body, false, None)?.0?; - let c = c.to_nextsolver(interner); - let c = if is_signed { - try_const_isize(db, &c).unwrap() - } else { - try_const_usize(db, c).unwrap() as i128 - }; - Ok(c) -} - -// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should -// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here -// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. -pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> { - let interner = DbInterner::new_with(ctx.db, None, None); - let infer = ctx.fixme_resolve_all_clone(); - fn has_closure(body: &Body, expr: ExprId) -> bool { - if matches!(body[expr], Expr::Closure { .. }) { - return true; - } - let mut r = false; - body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); - r - } - if has_closure(ctx.body, expr) { - // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. - return unknown_const(infer[expr]); - } - if let Expr::Path(p) = &ctx.body[expr] { - let resolver = &ctx.resolver; - if let Some(c) = path_to_const(ctx.db, resolver, p, || ctx.generics(), infer[expr]) { - return c; - } - } - if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) - && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) - { - return result.to_nextsolver(interner); - } - unknown_const(infer[expr]) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 11d3be5c3c487..4b33c8a84a810 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -16,8 +16,8 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, Ty, - TyDefId, ValueTyDefId, chalk_db, + Binders, ImplTraitId, ImplTraits, InferenceResult, TraitEnvironment, Ty, TyDefId, ValueTyDefId, + chalk_db, consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, @@ -37,50 +37,56 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::mir::mir_body_query)] #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)] - fn mir_body<'db>(&'db self, def: DefWithBodyId) -> Result, MirLowerError<'db>>; + fn mir_body<'db>( + &'db self, + def: DefWithBodyId, + ) -> Result>, MirLowerError<'db>>; #[salsa::invoke(crate::mir::mir_body_for_closure_query)] fn mir_body_for_closure<'db>( &'db self, def: InternedClosureId, - ) -> Result, MirLowerError<'db>>; + ) -> Result>, MirLowerError<'db>>; #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)] fn monomorphized_mir_body<'db>( &'db self, def: DefWithBodyId, - subst: Substitution, + subst: crate::next_solver::GenericArgs<'db>, env: Arc>, - ) -> Result, MirLowerError<'db>>; + ) -> Result>, MirLowerError<'db>>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] fn monomorphized_mir_body_for_closure<'db>( &'db self, def: InternedClosureId, - subst: Substitution, + subst: crate::next_solver::GenericArgs<'db>, env: Arc>, - ) -> Result, MirLowerError<'db>>; + ) -> Result>, MirLowerError<'db>>; #[salsa::invoke(crate::mir::borrowck_query)] #[salsa::lru(2024)] fn borrowck<'db>( &'db self, def: DefWithBodyId, - ) -> Result, MirLowerError<'db>>; + ) -> Result]>, MirLowerError<'db>>; #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_cycle_result)] fn const_eval<'db>( &'db self, def: GeneralConstId, - subst: Substitution, + subst: crate::next_solver::GenericArgs<'db>, trait_env: Option>>, - ) -> Result>; + ) -> Result, ConstEvalError<'db>>; #[salsa::invoke(crate::consteval::const_eval_static_query)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_static_cycle_result)] - fn const_eval_static<'db>(&'db self, def: StaticId) -> Result>; + fn const_eval_static<'db>( + &'db self, + def: StaticId, + ) -> Result, ConstEvalError<'db>>; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index d79069901ebb8..bcd93c6699ccd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -52,7 +52,7 @@ use crate::{ AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, MemoryMap, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval_nextsolver, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval, db::{HirDatabase, InternedClosure}, from_assoc_type_id, from_placeholder_idx, generics::generics, @@ -750,8 +750,8 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> { } rustc_type_ir::ConstKind::Value(const_bytes) => render_const_scalar_ns( f, - &const_bytes.value.inner().0, - &const_bytes.value.inner().1, + &const_bytes.value.inner().memory, + &const_bytes.value.inner().memory_map, const_bytes.ty, ), rustc_type_ir::ConstKind::Unevaluated(unev) => { @@ -1025,7 +1025,7 @@ fn render_const_scalar_inner<'db>( ty.hir_fmt(f) } TyKind::Array(ty, len) => { - let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else { + let Some(len) = consteval::try_const_usize(f.db, len) else { return f.write_str(""); }; let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 4bd9691ea0795..aaf274799c63f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -7,7 +7,7 @@ use stdx::never; use triomphe::Arc; use crate::{ - TraitEnvironment, consteval_nextsolver, + TraitEnvironment, consteval, db::HirDatabase, method_resolution::TyFingerprint, next_solver::{ @@ -128,7 +128,7 @@ fn has_drop_glue_impl<'db>( .max() .unwrap_or(DropGlue::None), TyKind::Array(ty, len) => { - if consteval_nextsolver::try_const_usize(db, len) == Some(0) { + if consteval::try_const_usize(db, len) == Some(0) { // Arrays of size 0 don't have drop glue. return DropGlue::None; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 8a6ce0a69bda7..763b145812815 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -28,10 +28,7 @@ use crate::{ db::{HirDatabase, InternedClosure, InternedClosureId}, infer::InferenceContext, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - next_solver::{ - DbInterner, EarlyBinder, GenericArgs, Ty, TyKind, - mapping::{ChalkToNextSolver, NextSolverToChalk}, - }, + next_solver::{DbInterner, EarlyBinder, GenericArgs, Ty, TyKind}, traits::FnTrait, }; @@ -47,16 +44,14 @@ impl<'db> HirPlace<'db> { fn ty(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> { let mut ty = ctx.table.resolve_completely(ctx.result[self.local]); for p in &self.projections { - ty = p - .projected_ty( - ty.to_chalk(ctx.interner()), - ctx.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - ctx.owner.module(ctx.db).krate(), - ) - .to_nextsolver(ctx.interner()); + ty = p.projected_ty( + &ctx.table.infer_ctxt, + ty, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db).krate(), + ); } ty } @@ -865,16 +860,14 @@ impl<'db> InferenceContext<'_, 'db> { continue; } for (i, p) in capture.place.projections.iter().enumerate() { - ty = p - .projected_ty( - ty.to_chalk(self.interner()), - self.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - self.owner.module(self.db).krate(), - ) - .to_nextsolver(self.interner()); + ty = p.projected_ty( + &self.table.infer_ctxt, + ty, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db).krate(), + ); if ty.is_raw_ptr() || ty.is_union() { capture.kind = CaptureKind::ByRef(BorrowKind::Shared); self.truncate_capture_spans(capture, i + 1); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index b4140d88db349..859dfef70fed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -28,7 +28,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, CallableDefId, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Rawness, TraitEnvironment, autoderef::overloaded_deref_ty, - consteval_nextsolver, + consteval, generics::generics, infer::{ AllowTwoPhase, BreakableKind, @@ -896,7 +896,7 @@ impl<'db> InferenceContext<'_, 'db> { Literal::ByteString(bs) => { let byte_type = self.types.u8; - let len = consteval_nextsolver::usize_const( + let len = consteval::usize_const( self.db, Some(bs.len() as u128), self.resolver.krate(), @@ -1221,7 +1221,7 @@ impl<'db> InferenceContext<'_, 'db> { let expected = Expectation::has_type(elem_ty); let (elem_ty, len) = match array { Array::ElementList { elements, .. } if elements.is_empty() => { - (elem_ty, consteval_nextsolver::usize_const(self.db, Some(0), krate)) + (elem_ty, consteval::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements); @@ -1231,7 +1231,7 @@ impl<'db> InferenceContext<'_, 'db> { } ( coerce.complete(self), - consteval_nextsolver::usize_const(self.db, Some(elements.len() as u128), krate), + consteval::usize_const(self.db, Some(elements.len() as u128), krate), ) } &Array::Repeat { initializer, repeat } => { @@ -1248,7 +1248,7 @@ impl<'db> InferenceContext<'_, 'db> { _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes), } - (elem_ty, consteval_nextsolver::eval_to_const(repeat, self)) + (elem_ty, consteval::eval_to_const(repeat, self)) } }; // Try to evaluate unevaluated constant, and insert variable if is not possible. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 9f2f86dd3ef50..452ae316620f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -14,7 +14,7 @@ use stdx::TupleExt; use crate::{ DeclContext, DeclOrigin, InferenceDiagnostic, - consteval_nextsolver::{self, try_const_usize, usize_const}, + consteval::{self, try_const_usize, usize_const}, infer::{ AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, coerce::CoerceNever, expr::ExprIsRead, @@ -591,11 +591,7 @@ impl<'db> InferenceContext<'_, 'db> { } let len = before.len() + suffix.len(); - let size = consteval_nextsolver::usize_const( - self.db, - Some(len as u128), - self.owner.krate(self.db), - ); + let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db)); let elem_ty = self.table.next_ty_var(); let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index e649e381ddd4a..110f767967c10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -10,7 +10,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, ValueTyDefId, consteval_nextsolver, + InferenceDiagnostic, ValueTyDefId, consteval, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, lower_nextsolver::LifetimeElisionKind, @@ -128,7 +128,7 @@ impl<'db> InferenceContext<'_, 'db> { match id { GenericParamId::TypeParamId(_) => self.types.error.into(), GenericParamId::ConstParamId(id) => { - consteval_nextsolver::unknown_const_as_generic(self.db.const_param_ty_ns(id)) + consteval::unknown_const_as_generic(self.db.const_param_ty_ns(id)) } GenericParamId::LifetimeParamId(_) => self.types.re_error.into(), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index bdebe41b29989..826f19cf0b68d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -11,7 +11,9 @@ use triomphe::Arc; use crate::{ AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind, - consteval::try_const_usize, db::HirDatabase, + consteval::try_const_usize, + db::HirDatabase, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, }; // FIXME: Turn this into a query, it can be quite slow @@ -79,14 +81,17 @@ impl TypeVisitor for UninhabitedFrom<'_> { } self.recursive_ty.insert(ty.clone()); self.max_depth -= 1; + let interner = DbInterner::new_with(self.db, None, None); let r = match ty.kind(Interner) { TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder), - TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) { - Some(0) | None => CONTINUE_OPAQUELY_INHABITED, - Some(1..) => item_ty.super_visit_with(self, outer_binder), - }, + TyKind::Array(item_ty, len) => { + match try_const_usize(self.db, len.to_nextsolver(interner)) { + Some(0) | None => CONTINUE_OPAQUELY_INHABITED, + Some(1..) => item_ty.super_visit_with(self, outer_binder), + } + } TyKind::Alias(AliasTy::Projection(projection)) => { // FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle // `TyKind::AssociatedType`, but perhaps in the future it will. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index eed36b1bb7f5d..a857602fa08a8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -21,7 +21,7 @@ use triomphe::Arc; use crate::{ TraitEnvironment, - consteval_nextsolver::try_const_usize, + consteval::try_const_usize, db::HirDatabase, next_solver::{ DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 4ff5cc5588bfe..76cc65277cd3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -21,6 +21,8 @@ extern crate ra_ap_rustc_type_ir as rustc_type_ir; extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; +extern crate self as hir_ty; + mod builder; mod chalk_db; mod chalk_ext; @@ -37,7 +39,7 @@ mod utils; pub mod autoderef; pub mod consteval; -pub mod consteval_nextsolver; +pub mod consteval_chalk; pub mod db; pub mod diagnostics; pub mod display; @@ -782,7 +784,12 @@ where _var: InferenceVar, _outer_binder: DebruijnIndex, ) -> Fallible { - if cfg!(debug_assertions) { Err(NoSolution) } else { Ok(unknown_const(ty)) } + if cfg!(debug_assertions) { + Err(NoSolution) + } else { + let interner = DbInterner::conjure(); + Ok(unknown_const(ty.to_nextsolver(interner)).to_chalk(interner)) + } } fn try_fold_free_var_const( @@ -791,7 +798,12 @@ where _bound_var: BoundVar, _outer_binder: DebruijnIndex, ) -> Fallible { - if cfg!(debug_assertions) { Err(NoSolution) } else { Ok(unknown_const(ty)) } + if cfg!(debug_assertions) { + Err(NoSolution) + } else { + let interner = DbInterner::conjure(); + Ok(unknown_const(ty.to_nextsolver(interner)).to_chalk(interner)) + } } fn try_fold_inference_lifetime( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index a9b523a4a6edf..ee7b0cdd1fe34 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -49,7 +49,7 @@ use crate::{ ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, LifetimeOutlives, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, - consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, + consteval_chalk::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, db::HirDatabase, error_lifetime, generics::{Generics, generics, trait_self_param_idx}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index cdac1c9829810..f988b6160ba51 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -23,7 +23,7 @@ use crate::{ Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, WhereClause, - consteval::{unknown_const, unknown_const_as_generic}, + consteval_chalk::{unknown_const, unknown_const_as_generic}, db::HirDatabase, error_lifetime, generics::{Generics, generics}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 98881004bb9a1..5a0bccea5f9c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -62,7 +62,7 @@ use crate::next_solver::ParamConst; use crate::{ FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, - consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic}, + consteval::{intern_const_ref, path_to_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics, trait_self_param_idx}, lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index babc39694ff1c..b003cc574d5b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -30,7 +30,7 @@ use stdx::never; use crate::{ GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, PathLoweringDiagnostic, TyDefId, ValueTyDefId, - consteval_nextsolver::{unknown_const, unknown_const_as_generic}, + consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics}, lower::PathDiagnosticCallbackData, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 086abc95917ef..110fee824cb2d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -4,13 +4,12 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; -use arrayvec::ArrayVec; use base_db::Crate; use chalk_ir::{UniverseIndex, WithKind, cast::Cast}; use hir_def::{ AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, TypeAliasId, - nameres::{DefMap, assoc::ImplItems, block_def_map, crate_def_map}, + nameres::{DefMap, block_def_map, crate_def_map}, signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags}, }; use hir_expand::name::Name; @@ -18,7 +17,7 @@ use intern::sym; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt, UintTy, inherent::{ AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _, }, @@ -27,6 +26,8 @@ use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::select::ImplSource; use crate::{ CanonicalVarKinds, DebruijnIndex, GenericArgData, InEnvironment, Interner, TraitEnvironment, TyBuilder, VariableKind, @@ -36,10 +37,10 @@ use crate::{ lang_items::is_box, next_solver::{ Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId, - TraitRef, Ty, TyKind, + TraitRef, Ty, TyKind, TypingMode, infer::{ - DefineOpaqueTypes, - traits::{ObligationCause, PredicateObligation}, + DbInternerInferExt, DefineOpaqueTypes, + traits::{Obligation, ObligationCause, PredicateObligation}, }, mapping::NextSolverToChalk, obligation_ctxt::ObligationCtxt, @@ -689,11 +690,12 @@ pub(crate) fn iterate_method_candidates<'db, T>( } pub fn lookup_impl_const<'db>( - interner: DbInterner<'db>, + infcx: &InferCtxt<'db>, env: Arc>, const_id: ConstId, subs: GenericArgs<'db>, ) -> (ConstId, GenericArgs<'db>) { + let interner = infcx.interner; let db = interner.db; let trait_id = match const_id.lookup(db).container { @@ -708,7 +710,7 @@ pub fn lookup_impl_const<'db>( None => return (const_id, subs), }; - lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) + lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name) .and_then( |assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None }, ) @@ -759,6 +761,7 @@ pub(crate) fn lookup_impl_method_query<'db>( fn_subst: GenericArgs<'db>, ) -> (FunctionId, GenericArgs<'db>) { let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else { return (func, fn_subst); @@ -772,7 +775,7 @@ pub(crate) fn lookup_impl_method_query<'db>( let name = &db.function_signature(func).name; let Some((impl_fn, impl_subst)) = - lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name).and_then(|assoc| { + lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env, name).and_then(|assoc| { if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None } }) else { @@ -789,78 +792,53 @@ pub(crate) fn lookup_impl_method_query<'db>( } fn lookup_impl_assoc_item_for_trait_ref<'db>( + infcx: &InferCtxt<'db>, trait_ref: TraitRef<'db>, - db: &'db dyn HirDatabase, env: Arc>, name: &Name, ) -> Option<(AssocItemId, GenericArgs<'db>)> { - let hir_trait_id = trait_ref.def_id.0; - let self_ty = trait_ref.self_ty(); - let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?; - let impls = db.trait_impls_in_deps(env.krate); - - let trait_module = hir_trait_id.module(db); - let type_module = match self_ty_fp { - TyFingerprint::Adt(adt_id) => Some(adt_id.module(db)), - TyFingerprint::ForeignType(type_id) => Some(type_id.module(db)), - TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db)), - _ => None, - }; - - let def_blocks: ArrayVec<_, 2> = - [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())] - .into_iter() - .flatten() - .filter_map(|block_id| db.trait_impls_in_block(block_id)) - .collect(); - - let impls = impls - .iter() - .chain(&def_blocks) - .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); - - let table = InferenceTable::new(db, env); - - let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?; - let item = impl_data.items.iter().find_map(|(n, it)| match *it { - AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)), - AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)), - AssocItemId::TypeAliasId(_) => None, - })?; + let (impl_id, impl_subst) = find_matching_impl(infcx, &env, trait_ref)?; + let item = + impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it { + AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)), + AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)), + AssocItemId::TypeAliasId(_) => None, + })?; Some((item, impl_subst)) } -fn find_matching_impl<'db>( - mut impls: impl Iterator, - mut table: InferenceTable<'db>, - actual_trait_ref: TraitRef<'db>, -) -> Option<(&'db ImplItems, GenericArgs<'db>)> { - let db = table.db; - impls.find_map(|impl_| { - table.run_in_snapshot(|table| { - let impl_substs = table.fresh_args_for_item(impl_.into()); - let trait_ref = db - .impl_trait(impl_) - .expect("non-trait method in find_matching_impl") - .instantiate(table.interner(), impl_substs); - - if !table.unify(trait_ref, actual_trait_ref) { - return None; - } +pub(crate) fn find_matching_impl<'db>( + infcx: &InferCtxt<'db>, + env: &TraitEnvironment<'db>, + trait_ref: TraitRef<'db>, +) -> Option<(ImplId, GenericArgs<'db>)> { + let trait_ref = + infcx.at(&ObligationCause::dummy(), env.env).deeply_normalize(trait_ref).ok()?; - if let Some(predicates) = - db.generic_predicates_ns(impl_.into()).instantiate(table.interner(), impl_substs) - { - for predicate in predicates { - if table.try_obligation(predicate.0).no_solution() { - return None; - } - table.register_obligation(predicate.0); - } - } - Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) - }) - }) + let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env.env, trait_ref); + + let selection = infcx.select(&obligation).ok()??; + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut ocx = ObligationCtxt::new(infcx); + let impl_source = selection.map(|obligation| ocx.register_obligation(obligation)); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return None; + } + + let impl_source = infcx.resolve_vars_if_possible(impl_source); + if impl_source.has_non_region_infer() { + return None; + } + + match impl_source { + ImplSource::UserDefined(impl_source) => Some((impl_source.impl_def_id, impl_source.args)), + ImplSource::Param(_) | ImplSource::Builtin(..) => None, + } } fn is_inherent_impl_coherent<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index a05cc2a02b70e..936895fb7fd3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -2,18 +2,7 @@ use std::{collections::hash_map::Entry, fmt::Display, iter}; -use crate::{ - CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap, - Substitution, TraitEnvironment, Ty, TyExt, TyKind, - consteval::usize_const, - db::HirDatabase, - display::{DisplayTarget, HirDisplay}, - infer::{PointerCast, normalize}, - lang_items::is_box, - mapping::ToChalk, -}; use base_db::Crate; -use chalk_ir::Mutability; use either::Either; use hir_def::{ DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, @@ -21,6 +10,25 @@ use hir_def::{ hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId}, }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; +use rustc_ast_ir::Mutability; +use rustc_hash::FxHashMap; +use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _}; +use smallvec::{SmallVec, smallvec}; +use stdx::{impl_from, never}; + +use crate::{ + CallableDefId, InferenceResult, MemoryMap, + consteval::usize_const, + db::{HirDatabase, InternedClosureId}, + display::{DisplayTarget, HirDisplay}, + infer::PointerCast, + lang_items::is_box, + next_solver::{ + Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, + }, +}; mod borrowck; mod eval; @@ -36,25 +44,22 @@ pub use lower::{MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_bod pub use monomorphization::{ monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; -use rustc_hash::FxHashMap; -use smallvec::{SmallVec, smallvec}; -use stdx::{impl_from, never}; pub(crate) use lower::mir_body_cycle_result; pub(crate) use monomorphization::monomorphized_mir_body_cycle_result; -use super::consteval::{intern_const_scalar, try_const_usize}; +use super::consteval::try_const_usize; -pub type BasicBlockId = Idx; -pub type LocalId = Idx; +pub type BasicBlockId<'db> = Idx>; +pub type LocalId<'db> = Idx>; -fn return_slot() -> LocalId { +fn return_slot<'db>() -> LocalId<'db> { LocalId::from_raw(RawIdx::from(0)) } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Local { - pub ty: Ty, +pub struct Local<'db> { + pub ty: Ty<'db>, } /// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of @@ -76,19 +81,19 @@ pub struct Local { /// currently implements it, but it seems like this may be something to check against in the /// validator. #[derive(Debug, PartialEq, Eq, Clone)] -pub struct Operand { - kind: OperandKind, +pub struct Operand<'db> { + kind: OperandKind<'db>, // FIXME : This should actually just be of type `MirSpan`. span: Option, } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum OperandKind { +pub enum OperandKind<'db> { /// Creates a value by loading the given place. /// /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there /// is no such requirement. - Copy(Place), + Copy(Place<'db>), /// Creates a value by performing loading the place, just like the `Copy` operand. /// @@ -97,41 +102,41 @@ pub enum OperandKind { /// place without first re-initializing it. /// /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188 - Move(Place), + Move(Place<'db>), /// Constants are already semantically values, and remain unchanged. - Constant(Const), + Constant { konst: Const<'db>, ty: Ty<'db> }, /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc /// handles it with the `Constant` variant somehow. Static(StaticId), } -impl Operand { - fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'static>, ty: Ty) -> Self { +impl<'db> Operand<'db> { + fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self { + let interner = DbInterner::conjure(); Operand { - kind: OperandKind::Constant(intern_const_scalar( - ConstScalar::Bytes(data, memory_map), + kind: OperandKind::Constant { + konst: Const::new_valtree(interner, ty, data, memory_map), ty, - )), + }, span: None, } } - fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self { + fn from_bytes(data: Box<[u8]>, ty: Ty<'db>) -> Self { Operand::from_concrete_const(data, MemoryMap::default(), ty) } - fn const_zst(ty: Ty) -> Operand { + fn const_zst(ty: Ty<'db>) -> Operand<'db> { Self::from_bytes(Box::default(), ty) } fn from_fn( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, func_id: hir_def::FunctionId, - generic_args: Substitution, - ) -> Operand { - let ty = - chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args) - .intern(Interner); + generic_args: GenericArgs<'db>, + ) -> Operand<'db> { + let interner = DbInterner::new_with(db, None, None); + let ty = Ty::new_fn_def(interner, CallableDefId::FunctionId(func_id).into(), generic_args); Operand::from_bytes(Box::default(), ty) } } @@ -150,83 +155,81 @@ pub enum ProjectionElem { } impl ProjectionElem { - pub fn projected_ty( + pub fn projected_ty<'db>( &self, - mut base: Ty, - db: &dyn HirDatabase, - closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty, + infcx: &InferCtxt<'db>, + mut base: Ty<'db>, + closure_field: impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db>, krate: Crate, - ) -> Ty { + ) -> Ty<'db> { + let interner = infcx.interner; + let db = interner.db; + // we only bail on mir building when there are type mismatches // but error types may pop up resulting in us still attempting to build the mir // so just propagate the error type - if base.is_unknown() { - return TyKind::Error.intern(Interner); + if base.is_ty_error() { + return Ty::new_error(interner, ErrorGuaranteed); } - if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) { - base = normalize( - db, - // FIXME: we should get this from caller - TraitEnvironment::empty(krate), - base, - ); + if matches!(base.kind(), TyKind::Alias(..)) { + let mut ocx = ObligationCtxt::new(infcx); + // FIXME: we should get this from caller + let env = ParamEnv::empty(); + match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) { + Ok(it) => base = it, + Err(_) => return Ty::new_error(interner, ErrorGuaranteed), + } } + match self { - ProjectionElem::Deref => match &base.kind(Interner) { - TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), - TyKind::Adt(adt, subst) if is_box(db, adt.0) => { - subst.at(Interner, 0).assert_ty_ref(Interner).clone() - } + ProjectionElem::Deref => match base.kind() { + TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner, + TyKind::Adt(adt_def, subst) if is_box(db, adt_def.def_id().0) => subst.type_at(0), _ => { never!( "Overloaded deref on type {} is not a projection", base.display(db, DisplayTarget::from_crate(db, krate)) ); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } }, - ProjectionElem::Field(Either::Left(f)) => match base.kind(Interner) { + ProjectionElem::Field(Either::Left(f)) => match base.kind() { TyKind::Adt(_, subst) => { - db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) + db.field_types_ns(f.parent)[f.local_id].instantiate(interner, subst) } ty => { never!("Only adt has field, found {:?}", ty); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } }, - ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) { - TyKind::Tuple(_, subst) => subst - .as_slice(Interner) - .get(f.index as usize) - .map(|x| x.assert_ty_ref(Interner)) - .cloned() - .unwrap_or_else(|| { + ProjectionElem::Field(Either::Right(f)) => match base.kind() { + TyKind::Tuple(subst) => { + subst.as_slice().get(f.index as usize).copied().unwrap_or_else(|| { never!("Out of bound tuple field"); - TyKind::Error.intern(Interner) - }), + Ty::new_error(interner, ErrorGuaranteed) + }) + } ty => { never!("Only tuple has tuple field: {:?}", ty); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } }, - ProjectionElem::ClosureField(f) => match &base.kind(Interner) { - TyKind::Closure(id, subst) => closure_field(*id, subst, *f), + ProjectionElem::ClosureField(f) => match base.kind() { + TyKind::Closure(id, subst) => closure_field(id.0, subst, *f), _ => { never!("Only closure has closure field"); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } }, - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { - match &base.kind(Interner) { - TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), - _ => { - never!("Overloaded index is not a projection"); - TyKind::Error.intern(Interner) - } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => match base.kind() { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner, + _ => { + never!("Overloaded index is not a projection"); + Ty::new_error(interner, ErrorGuaranteed) } - } - &ProjectionElem::Subslice { from, to } => match &base.kind(Interner) { + }, + &ProjectionElem::Subslice { from, to } => match base.kind() { TyKind::Array(inner, c) => { let next_c = usize_const( db, @@ -236,34 +239,34 @@ impl ProjectionElem { }, krate, ); - TyKind::Array(inner.clone(), next_c).intern(Interner) + Ty::new_array_with_const_len(interner, inner, next_c) } - TyKind::Slice(_) => base.clone(), + TyKind::Slice(_) => base, _ => { never!("Subslice projection should only happen on slice and array"); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } }, ProjectionElem::OpaqueCast(_) => { never!("We don't emit these yet"); - TyKind::Error.intern(Interner) + Ty::new_error(interner, ErrorGuaranteed) } } } } -type PlaceElem = ProjectionElem; +type PlaceElem<'db> = ProjectionElem, Ty<'db>>; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ProjectionId(u32); #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectionStore { - id_to_proj: FxHashMap>, - proj_to_id: FxHashMap, ProjectionId>, +pub struct ProjectionStore<'db> { + id_to_proj: FxHashMap]>>, + proj_to_id: FxHashMap]>, ProjectionId>, } -impl Default for ProjectionStore { +impl Default for ProjectionStore<'_> { fn default() -> Self { let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` @@ -272,17 +275,17 @@ impl Default for ProjectionStore { } } -impl ProjectionStore { +impl<'db> ProjectionStore<'db> { pub fn shrink_to_fit(&mut self) { self.id_to_proj.shrink_to_fit(); self.proj_to_id.shrink_to_fit(); } - pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option { + pub fn intern_if_exist(&self, projection: &[PlaceElem<'db>]) -> Option { self.proj_to_id.get(projection).copied() } - pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { + pub fn intern(&mut self, projection: Box<[PlaceElem<'db>]>) -> ProjectionId { let new_id = ProjectionId(self.proj_to_id.len() as u32); match self.proj_to_id.entry(projection) { Entry::Occupied(id) => *id.get(), @@ -303,11 +306,15 @@ impl ProjectionId { self == ProjectionId::EMPTY } - pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { + pub fn lookup<'a, 'db>(self, store: &'a ProjectionStore<'db>) -> &'a [PlaceElem<'db>] { store.id_to_proj.get(&self).unwrap() } - pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { + pub fn project<'db>( + self, + projection: PlaceElem<'db>, + store: &mut ProjectionStore<'db>, + ) -> ProjectionId { let mut current = self.lookup(store).to_vec(); current.push(projection); store.intern(current.into()) @@ -315,13 +322,13 @@ impl ProjectionId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Place { - pub local: LocalId, +pub struct Place<'db> { + pub local: LocalId<'db>, pub projection: ProjectionId, } -impl Place { - fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { +impl<'db> Place<'db> { + fn is_parent(&self, child: &Place<'db>, store: &ProjectionStore<'db>) -> bool { self.local == child.local && child.projection.lookup(store).starts_with(self.projection.lookup(store)) } @@ -329,39 +336,39 @@ impl Place { /// The place itself is not included fn iterate_over_parents<'a>( &'a self, - store: &'a ProjectionStore, - ) -> impl Iterator + 'a { + store: &'a ProjectionStore<'db>, + ) -> impl Iterator> + 'a { let projection = self.projection.lookup(store); (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) }) } - fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { + fn project(&self, projection: PlaceElem<'db>, store: &mut ProjectionStore<'db>) -> Place<'db> { Place { local: self.local, projection: self.projection.project(projection, store) } } } -impl From for Place { - fn from(local: LocalId) -> Self { +impl<'db> From> for Place<'db> { + fn from(local: LocalId<'db>) -> Self { Self { local, projection: ProjectionId::EMPTY } } } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum AggregateKind { +pub enum AggregateKind<'db> { /// The type is of the element - Array(Ty), + Array(Ty<'db>), /// The type is of the tuple - Tuple(Ty), - Adt(VariantId, Substitution), + Tuple(Ty<'db>), + Adt(VariantId, GenericArgs<'db>), Union(UnionId, FieldId), - Closure(Ty), + Closure(Ty<'db>), //Coroutine(LocalDefId, SubstsRef, Movability), } #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct SwitchTargets { +pub struct SwitchTargets<'db> { /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. values: SmallVec<[u128; 1]>, @@ -378,17 +385,17 @@ pub struct SwitchTargets { // // However we’ve decided to keep this as-is until we figure a case // where some other approach seems to be strictly better than other. - targets: SmallVec<[BasicBlockId; 2]>, + targets: SmallVec<[BasicBlockId<'db>; 2]>, } -impl SwitchTargets { +impl<'db> SwitchTargets<'db> { /// Creates switch targets from an iterator of values and target blocks. /// /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to /// `goto otherwise;`. pub fn new( - targets: impl Iterator, - otherwise: BasicBlockId, + targets: impl Iterator)>, + otherwise: BasicBlockId<'db>, ) -> Self { let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip(); targets.push(otherwise); @@ -397,12 +404,12 @@ impl SwitchTargets { /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`, /// and to `else_` if not. - pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self { + pub fn static_if(value: u128, then: BasicBlockId<'db>, else_: BasicBlockId<'db>) -> Self { Self { values: smallvec![value], targets: smallvec![then, else_] } } /// Returns the fallback target that is jumped to when none of the values match the operand. - pub fn otherwise(&self) -> BasicBlockId { + pub fn otherwise(&self) -> BasicBlockId<'db> { *self.targets.last().unwrap() } @@ -412,33 +419,33 @@ impl SwitchTargets { /// including the `otherwise` fallback target. /// /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory. - pub fn iter(&self) -> impl Iterator + '_ { + pub fn iter(&self) -> impl Iterator)> + '_ { iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y)) } /// Returns a slice with all possible jump targets (including the fallback target). - pub fn all_targets(&self) -> &[BasicBlockId] { + pub fn all_targets(&self) -> &[BasicBlockId<'db>] { &self.targets } /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the /// specific value. This cannot fail, as it'll return the `otherwise` /// branch if there's not a specific match for the value. - pub fn target_for_value(&self, value: u128) -> BasicBlockId { + pub fn target_for_value(&self, value: u128) -> BasicBlockId<'db> { self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise()) } } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct Terminator { +pub struct Terminator<'db> { pub span: MirSpan, - pub kind: TerminatorKind, + pub kind: TerminatorKind<'db>, } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum TerminatorKind { +pub enum TerminatorKind<'db> { /// Block has one successor; we continue execution there. - Goto { target: BasicBlockId }, + Goto { target: BasicBlockId<'db> }, /// Switches based on the computed value. /// @@ -450,9 +457,9 @@ pub enum TerminatorKind { /// Target values may not appear more than once. SwitchInt { /// The discriminant value being tested. - discr: Operand, + discr: Operand<'db>, - targets: SwitchTargets, + targets: SwitchTargets<'db>, }, /// Indicates that the landing pad is finished and that the process should continue unwinding. @@ -503,7 +510,7 @@ pub enum TerminatorKind { /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to /// > the place or one of its "parents" occurred more recently than a move out of it. This does not /// > consider indirect assignments. - Drop { place: Place, target: BasicBlockId, unwind: Option }, + Drop { place: Place<'db>, target: BasicBlockId<'db>, unwind: Option> }, /// Drops the place and assigns a new value to it. /// @@ -536,10 +543,10 @@ pub enum TerminatorKind { /// /// Disallowed after drop elaboration. DropAndReplace { - place: Place, - value: Operand, - target: BasicBlockId, - unwind: Option, + place: Place<'db>, + value: Operand<'db>, + target: BasicBlockId<'db>, + unwind: Option>, }, /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of @@ -554,18 +561,18 @@ pub enum TerminatorKind { /// [#71117]: https://github.com/rust-lang/rust/issues/71117 Call { /// The function that’s being called. - func: Operand, + func: Operand<'db>, /// Arguments the function is called with. /// These are owned by the callee, which is free to modify them. /// This allows the memory occupied by "by-value" arguments to be /// reused across function calls without duplicating the contents. - args: Box<[Operand]>, + args: Box<[Operand<'db>]>, /// Where the returned value will be written - destination: Place, + destination: Place<'db>, /// Where to go after this call returns. If none, the call necessarily diverges. - target: Option, + target: Option>, /// Cleanups to be done if the call unwinds. - cleanup: Option, + cleanup: Option>, /// `true` if this is from a call in HIR rather than from an overloaded /// operator. True for overloaded function call. from_hir_call: bool, @@ -581,11 +588,11 @@ pub enum TerminatorKind { /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the /// assertion does not fail, execution continues at the specified basic block. Assert { - cond: Operand, + cond: Operand<'db>, expected: bool, //msg: AssertMessage, - target: BasicBlockId, - cleanup: Option, + target: BasicBlockId<'db>, + cleanup: Option>, }, /// Marks a suspend point. @@ -602,13 +609,13 @@ pub enum TerminatorKind { /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`? Yield { /// The value to return. - value: Operand, + value: Operand<'db>, /// Where to resume to. - resume: BasicBlockId, + resume: BasicBlockId<'db>, /// The place to store the resume argument in. - resume_arg: Place, + resume_arg: Place<'db>, /// Cleanup to be done if the coroutine is dropped at this suspend point. - drop: Option, + drop: Option>, }, /// Indicates the end of dropping a coroutine. @@ -631,10 +638,10 @@ pub enum TerminatorKind { /// Disallowed after drop elaboration. FalseEdge { /// The target normal control flow will take. - real_target: BasicBlockId, + real_target: BasicBlockId<'db>, /// A block control flow could conceptually jump to, but won't in /// practice. - imaginary_target: BasicBlockId, + imaginary_target: BasicBlockId<'db>, }, /// A terminator for blocks that only take one path in reality, but where we reserve the right @@ -646,14 +653,14 @@ pub enum TerminatorKind { /// Disallowed after drop elaboration. FalseUnwind { /// The target normal control flow will take. - real_target: BasicBlockId, + real_target: BasicBlockId<'db>, /// The imaginary cleanup block link. This particular path will never be taken /// in practice, but in order to avoid fragility we want to always /// consider it in borrowck. We don't want to accept programs which /// pass borrowck only when `panic=abort` or some assertions are disabled /// due to release vs. debug mode builds. This needs to be an `Option` because /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes. - unwind: Option, + unwind: Option>, }, } @@ -840,8 +847,8 @@ impl From for BinOp { } } -impl From for Rvalue { - fn from(x: Operand) -> Self { +impl<'db> From> for Rvalue<'db> { + fn from(x: Operand<'db>) -> Self { Self::Use(x) } } @@ -870,14 +877,14 @@ pub enum CastKind { } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum Rvalue { +pub enum Rvalue<'db> { /// Yields the operand unchanged - Use(Operand), + Use(Operand<'db>), /// Creates an array where each element is the value of the operand. /// /// Corresponds to source code like `[x; 32]`. - Repeat(Operand, Const), + Repeat(Operand<'db>, Const<'db>), /// Creates a reference of the indicated kind to the place. /// @@ -886,7 +893,7 @@ pub enum Rvalue { /// exactly what the behavior of this operation should be. /// /// `Shallow` borrows are disallowed after drop lowering. - Ref(BorrowKind, Place), + Ref(BorrowKind, Place<'db>), /// Creates a pointer/reference to the given thread local. /// @@ -917,7 +924,7 @@ pub enum Rvalue { /// If the type of the place is an array, this is the array length. For slices (`[T]`, not /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is /// ill-formed for places of other types. - Len(Place), + Len(Place<'db>), /// Performs essentially all of the casts that can be performed via `as`. /// @@ -925,7 +932,7 @@ pub enum Rvalue { /// /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why /// `ArrayToPointer` and `MutToConstPointer` are special. - Cast(CastKind, Operand, Ty), + Cast(CastKind, Operand<'db>, Ty<'db>), // FIXME link to `pointer::offset` when it hits stable. /// * `Offset` has the same semantics as `pointer::offset`, except that the second @@ -957,7 +964,7 @@ pub enum Rvalue { /// when the value of right-hand side is negative. /// /// Other combinations of types and operators are unsupported. - CheckedBinaryOp(BinOp, Operand, Operand), + CheckedBinaryOp(BinOp, Operand<'db>, Operand<'db>), /// Computes a value as described by the operation. //NullaryOp(NullOp, Ty), @@ -968,7 +975,7 @@ pub enum Rvalue { /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds /// return a value with the same type as their operand. - UnaryOp(UnOp, Operand), + UnaryOp(UnOp, Operand<'db>), /// Computes the discriminant of the place, returning it as an integer of type /// [`discriminant_ty`]. Returns zero for types without discriminant. @@ -980,7 +987,7 @@ pub enum Rvalue { /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty /// [#91095]: https://github.com/rust-lang/rust/issues/91095 /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant - Discriminant(Place), + Discriminant(Place<'db>), /// Creates an aggregate value, like a tuple or struct. /// @@ -990,17 +997,17 @@ pub enum Rvalue { /// /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. - Aggregate(AggregateKind, Box<[Operand]>), + Aggregate(AggregateKind<'db>, Box<[Operand<'db>]>), /// Transmutes a `*mut u8` into shallow-initialized `Box`. /// /// This is different from a normal transmute because dataflow analysis will treat the box as /// initialized but its content as uninitialized. Like other pointer casts, this in general /// affects alias analysis. - ShallowInitBox(Operand, Ty), + ShallowInitBox(Operand<'db>, Ty<'db>), /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer. - ShallowInitBoxWithAlloc(Ty), + ShallowInitBoxWithAlloc(Ty<'db>), /// A CopyForDeref is equivalent to a read from a place at the /// codegen level, but is treated specially by drop elaboration. When such a read happens, it @@ -1010,41 +1017,41 @@ pub enum Rvalue { /// read never happened and just projects further. This allows simplifying various MIR /// optimizations and codegen backends that previously had to handle deref operations anywhere /// in a place. - CopyForDeref(Place), + CopyForDeref(Place<'db>), } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum StatementKind { - Assign(Place, Rvalue), - FakeRead(Place), +pub enum StatementKind<'db> { + Assign(Place<'db>, Rvalue<'db>), + FakeRead(Place<'db>), //SetDiscriminant { // place: Box, // variant_index: VariantIdx, //}, - Deinit(Place), - StorageLive(LocalId), - StorageDead(LocalId), + Deinit(Place<'db>), + StorageLive(LocalId<'db>), + StorageDead(LocalId<'db>), //Retag(RetagKind, Box), //AscribeUserType(Place, UserTypeProjection, Variance), //Intrinsic(Box), Nop, } -impl StatementKind { - fn with_span(self, span: MirSpan) -> Statement { +impl<'db> StatementKind<'db> { + fn with_span(self, span: MirSpan) -> Statement<'db> { Statement { kind: self, span } } } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct Statement { - pub kind: StatementKind, +pub struct Statement<'db> { + pub kind: StatementKind<'db>, pub span: MirSpan, } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct BasicBlock { +pub struct BasicBlock<'db> { /// List of statements in this block. - pub statements: Vec, + pub statements: Vec>, /// Terminator for this block. /// @@ -1054,7 +1061,7 @@ pub struct BasicBlock { /// exception is that certain passes, such as `simplify_cfg`, swap /// out the terminator temporarily with `None` while they continue /// to recurse over the set of basic blocks. - pub terminator: Option, + pub terminator: Option>, /// If true, this block lies on an unwind path. This is used /// during codegen where distinct kinds of basic blocks may be @@ -1064,35 +1071,35 @@ pub struct BasicBlock { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct MirBody { - pub projection_store: ProjectionStore, - pub basic_blocks: Arena, - pub locals: Arena, - pub start_block: BasicBlockId, +pub struct MirBody<'db> { + pub projection_store: ProjectionStore<'db>, + pub basic_blocks: Arena>, + pub locals: Arena>, + pub start_block: BasicBlockId<'db>, pub owner: DefWithBodyId, - pub binding_locals: ArenaMap, - pub param_locals: Vec, + pub binding_locals: ArenaMap>, + pub param_locals: Vec>, /// This field stores the closures directly owned by this body. It is used /// in traversing every mir body. - pub closures: Vec, + pub closures: Vec, } -impl MirBody { - pub fn local_to_binding_map(&self) -> ArenaMap { +impl<'db> MirBody<'db> { + pub fn local_to_binding_map(&self) -> ArenaMap, BindingId> { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { - fn for_operand( - op: &mut Operand, - f: &mut impl FnMut(&mut Place, &mut ProjectionStore), - store: &mut ProjectionStore, + fn walk_places(&mut self, mut f: impl FnMut(&mut Place<'db>, &mut ProjectionStore<'db>)) { + fn for_operand<'db>( + op: &mut Operand<'db>, + f: &mut impl FnMut(&mut Place<'db>, &mut ProjectionStore<'db>), + store: &mut ProjectionStore<'db>, ) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { f(p, store); } - OperandKind::Constant(_) | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) => (), } } for (_, block) in self.basic_blocks.iter_mut() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 08b1e03726d70..db16c943968fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -11,14 +11,15 @@ use rustc_hash::FxHashMap; use stdx::never; use triomphe::Arc; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ - ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags, - db::{HirDatabase, InternedClosure}, + TraitEnvironment, + db::{HirDatabase, InternedClosure, InternedClosureId}, display::DisplayTarget, mir::OperandKind, - utils::ClosureSubst, + next_solver::{ + DbInterner, GenericArgs, SolverDefIds, Ty, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + }, }; use super::{ @@ -35,45 +36,45 @@ pub enum MutabilityReason { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct MovedOutOfRef { - pub ty: Ty, +pub struct MovedOutOfRef<'db> { + pub ty: Ty<'db>, pub span: MirSpan, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PartiallyMoved { - pub ty: Ty, +pub struct PartiallyMoved<'db> { + pub ty: Ty<'db>, pub span: MirSpan, - pub local: LocalId, + pub local: LocalId<'db>, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowRegion { - pub local: LocalId, +pub struct BorrowRegion<'db> { + pub local: LocalId<'db>, pub kind: BorrowKind, pub places: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowckResult { - pub mir_body: Arc, - pub mutability_of_locals: ArenaMap, - pub moved_out_of_ref: Vec, - pub partially_moved: Vec, - pub borrow_regions: Vec, +pub struct BorrowckResult<'db> { + pub mir_body: Arc>, + pub mutability_of_locals: ArenaMap, MutabilityReason>, + pub moved_out_of_ref: Vec>, + pub partially_moved: Vec>, + pub borrow_regions: Vec>, } fn all_mir_bodies<'db>( db: &'db dyn HirDatabase, def: DefWithBodyId, - mut cb: impl FnMut(Arc), + mut cb: impl FnMut(Arc>), ) -> Result<(), MirLowerError<'db>> { fn for_closure<'db>( db: &'db dyn HirDatabase, - c: ClosureId, - cb: &mut impl FnMut(Arc), + c: InternedClosureId, + cb: &mut impl FnMut(Arc>), ) -> Result<(), MirLowerError<'db>> { - match db.mir_body_for_closure(c.into()) { + match db.mir_body_for_closure(c) { Ok(body) => { cb(body.clone()); body.closures.iter().try_for_each(|&it| for_closure(db, it, cb)) @@ -93,14 +94,21 @@ fn all_mir_bodies<'db>( pub fn borrowck_query<'db>( db: &'db dyn HirDatabase, def: DefWithBodyId, -) -> Result, MirLowerError<'db>> { +) -> Result]>, MirLowerError<'db>> { let _p = tracing::info_span!("borrowck_query").entered(); + let module = def.module(db); + let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block()); + let env = db.trait_environment_for_body(def); let mut res = vec![]; all_mir_bodies(db, def, |body| { + // FIXME(next-solver): Opaques. + let infcx = interner.infer_ctxt().build(TypingMode::Borrowck { + defining_opaque_types: SolverDefIds::new_from_iter(interner, []), + }); res.push(BorrowckResult { - mutability_of_locals: mutability_of_locals(db, &body), - moved_out_of_ref: moved_out_of_ref(db, &body), - partially_moved: partially_moved(db, &body), + mutability_of_locals: mutability_of_locals(&infcx, &body), + moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body), + partially_moved: partially_moved(&infcx, &env, &body), borrow_regions: borrow_regions(db, &body), mir_body: body, }); @@ -108,51 +116,49 @@ pub fn borrowck_query<'db>( Ok(res.into()) } -fn make_fetch_closure_field( - db: &dyn HirDatabase, -) -> impl FnOnce(ClosureId, &Substitution, usize) -> Ty + '_ { - |c: ClosureId, subst: &Substitution, f: usize| { - let InternedClosure(def, _) = db.lookup_intern_closure(c.into()); +fn make_fetch_closure_field<'db>( + db: &'db dyn HirDatabase, +) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> { + |c: InternedClosureId, subst: GenericArgs<'db>, f: usize| { + let InternedClosure(def, _) = db.lookup_intern_closure(c); let infer = db.infer(def); - let (captures, _) = infer.closure_info(c.into()); - let parent_subst = ClosureSubst(subst).parent_subst(db); + let (captures, _) = infer.closure_info(c); + let parent_subst = subst.split_closure_args_untupled().parent_args; let interner = DbInterner::new_with(db, None, None); - let parent_subst: crate::next_solver::GenericArgs<'_> = - parent_subst.to_nextsolver(interner); - captures - .get(f) - .expect("broken closure field") - .ty - .instantiate(interner, parent_subst) - .to_chalk(interner) + captures.get(f).expect("broken closure field").ty.instantiate(interner, parent_subst) } } -fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec { +fn moved_out_of_ref<'db>( + infcx: &InferCtxt<'db>, + env: &TraitEnvironment<'db>, + body: &MirBody<'db>, +) -> Vec> { + let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut ty: Ty<'db> = body.locals[p.local].ty; let mut is_dereference_of_ref = false; for proj in p.projection.lookup(&body.projection_store) { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } ty = proj.projected_ty( + infcx, ty, - db, make_fetch_closure_field(db), body.owner.module(db).krate(), ); } if is_dereference_of_ref - && !ty.clone().is_copy(db, body.owner) - && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) + && !infcx.type_is_copy_modulo_regions(env.env, ty) + && !ty.references_non_lt_error() { result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty }); } } - OperandKind::Constant(_) | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) => (), }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -223,26 +229,29 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec result } -fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec { +fn partially_moved<'db>( + infcx: &InferCtxt<'db>, + env: &TraitEnvironment<'db>, + body: &MirBody<'db>, +) -> Vec> { + let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut ty: Ty<'db> = body.locals[p.local].ty; for proj in p.projection.lookup(&body.projection_store) { ty = proj.projected_ty( + infcx, ty, - db, make_fetch_closure_field(db), body.owner.module(db).krate(), ); } - if !ty.clone().is_copy(db, body.owner) - && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) - { + if !infcx.type_is_copy_modulo_regions(env.env, ty) && !ty.references_non_lt_error() { result.push(PartiallyMoved { span, ty, local: p.local }); } } - OperandKind::Constant(_) | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) => (), }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -313,7 +322,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec result } -fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec { +fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec> { let mut borrows = FxHashMap::default(); for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -321,7 +330,7 @@ fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec { if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind { borrows .entry(p.local) - .and_modify(|it: &mut BorrowRegion| { + .and_modify(|it: &mut BorrowRegion<'db>| { it.places.push(statement.span); }) .or_insert_with(|| BorrowRegion { @@ -363,9 +372,14 @@ enum ProjectionCase { Indirect, } -fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { +fn place_case<'db>( + infcx: &InferCtxt<'db>, + body: &MirBody<'db>, + lvalue: &Place<'db>, +) -> ProjectionCase { + let db = infcx.interner.db; let mut is_part_of = false; - let mut ty = body.locals[lvalue.local].ty.clone(); + let mut ty = body.locals[lvalue.local].ty; for proj in lvalue.projection.lookup(&body.projection_store).iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw @@ -379,7 +393,12 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio } ProjectionElem::OpaqueCast(_) => (), } - ty = proj.projected_ty(ty, db, make_fetch_closure_field(db), body.owner.module(db).krate()); + ty = proj.projected_ty( + infcx, + ty, + make_fetch_closure_field(db), + body.owner.module(db).krate(), + ); } if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct } } @@ -387,18 +406,18 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio /// Returns a map from basic blocks to the set of locals that might be ever initialized before /// the start of the block. Only `StorageDead` can remove something from this map, and we ignore /// `Uninit` and `drop` and similar after initialization. -fn ever_initialized_map( - db: &dyn HirDatabase, - body: &MirBody, -) -> ArenaMap> { - let mut result: ArenaMap> = +fn ever_initialized_map<'db>( + db: &'db dyn HirDatabase, + body: &MirBody<'db>, +) -> ArenaMap, ArenaMap, bool>> { + let mut result: ArenaMap, ArenaMap, bool>> = body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect(); - fn dfs( - db: &dyn HirDatabase, - body: &MirBody, - l: LocalId, - stack: &mut Vec, - result: &mut ArenaMap>, + fn dfs<'db>( + db: &'db dyn HirDatabase, + body: &MirBody<'db>, + l: LocalId<'db>, + stack: &mut Vec>, + result: &mut ArenaMap, ArenaMap, bool>>, ) { while let Some(b) = stack.pop() { let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs @@ -486,7 +505,11 @@ fn ever_initialized_map( result } -fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap) { +fn push_mut_span<'db>( + local: LocalId<'db>, + span: MirSpan, + result: &mut ArenaMap, MutabilityReason>, +) { match &mut result[local] { MutabilityReason::Mut { spans } => spans.push(span), it @ (MutabilityReason::Not | MutabilityReason::Unused) => { @@ -495,23 +518,27 @@ fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap) { +fn record_usage<'db>(local: LocalId<'db>, result: &mut ArenaMap, MutabilityReason>) { if let it @ MutabilityReason::Unused = &mut result[local] { *it = MutabilityReason::Not; }; } -fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap) { +fn record_usage_for_operand<'db>( + arg: &Operand<'db>, + result: &mut ArenaMap, MutabilityReason>, +) { if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { record_usage(p.local, result); } } -fn mutability_of_locals( - db: &dyn HirDatabase, - body: &MirBody, -) -> ArenaMap { - let mut result: ArenaMap = +fn mutability_of_locals<'db>( + infcx: &InferCtxt<'db>, + body: &MirBody<'db>, +) -> ArenaMap, MutabilityReason> { + let db = infcx.interner.db; + let mut result: ArenaMap, MutabilityReason> = body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect(); let ever_init_maps = ever_initialized_map(db, body); @@ -520,7 +547,7 @@ fn mutability_of_locals( for statement in &block.statements { match &statement.kind { StatementKind::Assign(place, value) => { - match place_case(db, body, place) { + match place_case(infcx, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { push_mut_span(place.local, statement.span, &mut result); @@ -569,7 +596,7 @@ fn mutability_of_locals( }, p, ) = value - && place_case(db, body, p) != ProjectionCase::Indirect + && place_case(infcx, body, p) != ProjectionCase::Indirect { push_mut_span(p.local, statement.span, &mut result); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 6e09cf9aeb882..444336ca3f9b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2,14 +2,11 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; -use base_db::Crate; -use base_db::target::TargetLoadError; -use chalk_ir::{Mutability, cast::Cast}; +use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, - VariantId, - builtin_type::BuiltinType, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, HasModule, ItemContainerId, + Lookup, StaticId, VariantId, expr_store::HygieneId, item_tree::FieldsShape, lang_item::LangItem, @@ -25,33 +22,33 @@ use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, }; +use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; +use rustc_type_ir::{ + AliasTyKind, + inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _}, +}; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; use crate::{ - AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, Interner, - MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, - consteval::{ConstEvalError, intern_const_scalar, try_const_usize}, - consteval_nextsolver, - db::{HirDatabase, InternedClosure}, + CallableDefId, ComplexMemoryMap, MemoryMap, TraitEnvironment, + consteval::{self, ConstEvalError, try_const_usize}, + db::{HirDatabase, InternedClosure, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - DbInterner, TypingMode, - infer::{DbInternerInferExt, InferCtxt}, - mapping::{ - ChalkToNextSolver, NextSolverToChalk, convert_args_for_result, convert_ty_for_result, - }, + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, + SolverDefId, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst, + infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, }, - static_lifetime, traits::FnTrait, - utils::{ClosureSubst, detect_variant_from_bytes}, + utils::detect_variant_from_bytes, }; use super::{ @@ -88,14 +85,14 @@ macro_rules! not_supported { #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct VTableMap<'db> { - ty_to_id: FxHashMap, usize>, - id_to_ty: Vec>, + ty_to_id: FxHashMap, usize>, + id_to_ty: Vec>, } impl<'db> VTableMap<'db> { const OFFSET: usize = 1000; // We should add some offset to ids to make 0 (null) an invalid id. - fn id(&mut self, ty: crate::next_solver::Ty<'db>) -> usize { + fn id(&mut self, ty: Ty<'db>) -> usize { if let Some(it) = self.ty_to_id.get(&ty) { return *it; } @@ -105,13 +102,13 @@ impl<'db> VTableMap<'db> { id } - pub(crate) fn ty(&self, id: usize) -> Result<'db, crate::next_solver::Ty<'db>> { + pub(crate) fn ty(&self, id: usize) -> Result<'db, Ty<'db>> { id.checked_sub(VTableMap::OFFSET) .and_then(|id| self.id_to_ty.get(id).copied()) .ok_or(MirEvalError::InvalidVTableId(id)) } - fn ty_of_bytes(&self, bytes: &[u8]) -> Result<'db, crate::next_solver::Ty<'db>> { + fn ty_of_bytes(&self, bytes: &[u8]) -> Result<'db, Ty<'db>> { let id = from_bytes!(usize, bytes); self.ty(id) } @@ -153,45 +150,45 @@ impl TlsData { } } -struct StackFrame { - locals: Locals, - destination: Option, +struct StackFrame<'db> { + locals: Locals<'db>, + destination: Option>, prev_stack_ptr: usize, span: (MirSpan, DefWithBodyId), } #[derive(Clone)] -enum MirOrDynIndex { - Mir(Arc), +enum MirOrDynIndex<'db> { + Mir(Arc>), Dyn(usize), } -pub struct Evaluator<'a> { - db: &'a dyn HirDatabase, - trait_env: Arc>, +pub struct Evaluator<'db> { + db: &'db dyn HirDatabase, + trait_env: Arc>, target_data_layout: Arc, stack: Vec, heap: Vec, - code_stack: Vec, + code_stack: Vec>, /// Stores the global location of the statics. We const evaluate every static first time we need it /// and see it's missing, then we add it to this to reuse. static_locations: FxHashMap, /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the /// time of use. - vtable_map: VTableMap<'a>, + vtable_map: VTableMap<'db>, thread_local_storage: TlsData, random_state: oorandom::Rand64, stdout: Vec, stderr: Vec, - layout_cache: RefCell, Arc>>, - projected_ty_cache: RefCell>, + layout_cache: RefCell, Arc>>, + projected_ty_cache: RefCell, PlaceElem<'db>), Ty<'db>>>, not_special_fn_cache: RefCell>, - mir_or_dyn_index_cache: RefCell>, - /// Constantly dropping and creating `Locals` is very costly. We store + mir_or_dyn_index_cache: RefCell), MirOrDynIndex<'db>>>, + /// Constantly dropping and creating `Locals<'db>` is very costly. We store /// old locals that we normally want to drop here, to reuse their allocations /// later. - unused_locals_store: RefCell>>, + unused_locals_store: RefCell>>>, cached_ptr_size: usize, cached_fn_trait_func: Option, cached_fn_mut_trait_func: Option, @@ -205,8 +202,7 @@ pub struct Evaluator<'a> { stack_depth_limit: usize, /// Maximum count of bytes that heap and stack can grow memory_limit: usize, - interner: DbInterner<'a>, - infcx: InferCtxt<'a>, + infcx: InferCtxt<'db>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -225,9 +221,9 @@ struct Interval { } #[derive(Debug, Clone)] -struct IntervalAndTy { +struct IntervalAndTy<'db> { interval: Interval, - ty: Ty, + ty: Ty<'db>, } impl Interval { @@ -256,18 +252,18 @@ impl Interval { } } -impl IntervalAndTy { - fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { +impl<'db> IntervalAndTy<'db> { + fn get<'a>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { memory.read_memory(self.interval.addr, self.interval.size) } - fn new<'db>( + fn new( addr: Address, - ty: Ty, + ty: Ty<'db>, evaluator: &Evaluator<'db>, - locals: &Locals, - ) -> Result<'db, IntervalAndTy> { - let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; + locals: &Locals<'db>, + ) -> Result<'db, IntervalAndTy<'db>> { + let size = evaluator.size_of_sized(ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) } } @@ -346,7 +342,7 @@ impl Address { #[derive(Clone, PartialEq, Eq)] pub enum MirEvalError<'db> { ConstEvalError(String, Box>), - LayoutError(LayoutError, Ty), + LayoutError(LayoutError, Ty<'db>), TargetDataLayoutNotAvailable(TargetLoadError), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. @@ -354,20 +350,20 @@ pub enum MirEvalError<'db> { Panic(String), // FIXME: This should be folded into ConstEvalError? MirLowerError(FunctionId, MirLowerError<'db>), - MirLowerErrorForClosure(ClosureId, MirLowerError<'db>), - TypeIsUnsized(Ty, &'static str), + MirLowerErrorForClosure(InternedClosureId, MirLowerError<'db>), + TypeIsUnsized(Ty<'db>, &'static str), NotSupported(String), - InvalidConst(Const), + InvalidConst(Const<'db>), InFunction( Box>, - Vec<(Either, MirSpan, DefWithBodyId)>, + Vec<(Either, MirSpan, DefWithBodyId)>, ), ExecutionLimitExceeded, StackOverflow, /// FIXME: Fold this into InternalError InvalidVTableId(usize), /// ? - CoerceUnsizedError(Ty), + CoerceUnsizedError(Ty<'db>), /// These should not occur, usually indicates a bug in mir lowering. InternalError(Box), } @@ -443,13 +439,8 @@ impl MirEvalError<'_> { let function_name = db.function_signature(*func); let self_ = match func.lookup(db).container { ItemContainerId::ImplId(impl_id) => Some({ - let generics = crate::generics::generics(db, impl_id.into()); - let interner = DbInterner::new_with(db, None, None); - let substs = generics.placeholder_subst(db); - let args: crate::next_solver::GenericArgs<'_> = - substs.to_nextsolver(interner); db.impl_self_ty(impl_id) - .instantiate(interner, args) + .instantiate_identity() .display(db, display_target) .to_string() }), @@ -535,10 +526,7 @@ impl std::fmt::Debug for MirEvalError<'_> { Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(), Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), - Self::InvalidConst(arg0) => { - let data = &arg0.data(Interner); - f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() - } + Self::InvalidConst(arg0) => f.debug_tuple("InvalidConst").field(&arg0).finish(), Self::InFunction(e, stack) => { f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } @@ -549,12 +537,12 @@ impl std::fmt::Debug for MirEvalError<'_> { type Result<'db, T> = std::result::Result>; #[derive(Debug, Default)] -struct DropFlags { - need_drop: FxHashSet, +struct DropFlags<'db> { + need_drop: FxHashSet>, } -impl DropFlags { - fn add_place(&mut self, p: Place, store: &ProjectionStore) { +impl<'db> DropFlags<'db> { + fn add_place(&mut self, p: Place<'db>, store: &ProjectionStore<'db>) { if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { return; } @@ -562,7 +550,7 @@ impl DropFlags { self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { + fn remove_place(&mut self, p: &Place<'db>, store: &ProjectionStore<'db>) -> bool { // FIXME: replace parents with parts if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); @@ -577,10 +565,10 @@ impl DropFlags { } #[derive(Debug)] -struct Locals { - ptr: ArenaMap, - body: Arc, - drop_flags: DropFlags, +struct Locals<'db> { + ptr: ArenaMap, Interval>, + body: Arc>, + drop_flags: DropFlags<'db>, } pub struct MirOutput { @@ -599,7 +587,7 @@ impl MirOutput { pub fn interpret_mir<'db>( db: &'db dyn HirDatabase, - body: Arc, + body: Arc>, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -607,10 +595,10 @@ pub fn interpret_mir<'db>( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option>>, -) -> Result<'db, (Result<'db, Const>, MirOutput)> { - let ty = body.locals[return_slot()].ty.clone(); +) -> Result<'db, (Result<'db, Const<'db>>, MirOutput)> { + let ty = body.locals[return_slot()].ty; let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; - let it: Result<'db, Const> = (|| { + let it: Result<'db, Const<'db>> = (|| { if evaluator.ptr_size() != size_of::() { not_supported!("targets with different pointer size from host"); } @@ -618,7 +606,7 @@ pub fn interpret_mir<'db>( let bytes = interval.get(&evaluator)?; let mut memory_map = evaluator.create_memory_map( bytes, - &ty, + ty, &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; let bytes = bytes.into(); @@ -629,13 +617,7 @@ pub fn interpret_mir<'db>( memory_map.vtable.shrink_to_fit(); MemoryMap::Complex(Box::new(memory_map)) }; - // SAFETY: will never use this without a db - Ok(intern_const_scalar( - ConstScalar::Bytes(bytes, unsafe { - std::mem::transmute::, MemoryMap<'static>>(memory_map) - }), - ty, - )) + Ok(Const::new_valtree(evaluator.interner(), ty, bytes, memory_map)) })(); Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } @@ -652,13 +634,15 @@ impl<'db> Evaluator<'db> { assert_placeholder_ty_is_unused: bool, trait_env: Option>>, ) -> Result<'db, Evaluator<'db>> { - let crate_id = owner.module(db).krate(); + let module = owner.module(db); + let crate_id = module.krate(); let target_data_layout = match db.target_data_layout(crate_id) { Ok(target_data_layout) => target_data_layout, Err(e) => return Err(MirEvalError::TargetDataLayoutNotAvailable(e)), }; let cached_ptr_size = target_data_layout.pointer_size().bytes_usize(); - let interner = DbInterner::new_with(db, None, None); + let interner = DbInterner::new_with(db, Some(crate_id), module.containing_block()); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); Ok(Evaluator { target_data_layout, stack: vec![0], @@ -692,21 +676,25 @@ impl<'db> Evaluator<'db> { cached_fn_once_trait_func: LangItem::FnOnce.resolve_trait(db, crate_id).and_then(|x| { x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) }), - interner, - infcx: interner.infer_ctxt().build(TypingMode::non_body_analysis()), + infcx, }) } - fn place_addr(&self, p: &Place, locals: &Locals) -> Result<'db, Address> { + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn place_addr(&self, p: &Place<'db>, locals: &Locals<'db>) -> Result<'db, Address> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn place_interval(&self, p: &Place<'db>, locals: &Locals<'db>) -> Result<'db, Interval> { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, size: self.size_of_sized( - &place_addr_and_ty.1, + place_addr_and_ty.1, locals, "Type of place that we need its interval", )?, @@ -717,54 +705,46 @@ impl<'db> Evaluator<'db> { self.cached_ptr_size } - fn projected_ty(&self, ty: Ty, proj: PlaceElem) -> Ty { + fn projected_ty(&self, ty: Ty<'db>, proj: PlaceElem<'db>) -> Ty<'db> { let pair = (ty, proj); if let Some(r) = self.projected_ty_cache.borrow().get(&pair) { - return r.clone(); + return *r; } let (ty, proj) = pair; let r = proj.projected_ty( - ty.clone(), - self.db, + &self.infcx, + ty, |c, subst, f| { - let InternedClosure(def, _) = self.db.lookup_intern_closure(c.into()); + let InternedClosure(def, _) = self.db.lookup_intern_closure(c); let infer = self.db.infer(def); - let (captures, _) = infer.closure_info(c.into()); - let parent_subst = ClosureSubst(subst).parent_subst(self.db); + let (captures, _) = infer.closure_info(c); + let parent_subst = subst.split_closure_args_untupled().parent_args; captures .get(f) .expect("broken closure field") .ty - .instantiate( - self.interner, - <_ as ChalkToNextSolver<'db, crate::next_solver::GenericArgs<'db>>>::to_nextsolver( - &parent_subst, - self.interner, - ), - ) - .to_chalk(self.interner) + .instantiate(self.interner(), parent_subst) }, self.crate_id, ); - self.projected_ty_cache.borrow_mut().insert((ty, proj), r.clone()); + self.projected_ty_cache.borrow_mut().insert((ty, proj), r); r } fn place_addr_and_ty_and_metadata<'a>( &'a self, - p: &Place, - locals: &'a Locals, - ) -> Result<'db, (Address, Ty, Option)> { - let interner = DbInterner::new_with(self.db, None, None); + p: &Place<'db>, + locals: &'a Locals<'db>, + ) -> Result<'db, (Address, Ty<'db>, Option)> { let mut addr = locals.ptr[p.local].addr; - let mut ty: Ty = locals.body.locals[p.local].ty.clone(); + let mut ty: Ty<'db> = locals.body.locals[p.local].ty; let mut metadata: Option = None; // locals are always sized for proj in p.projection.lookup(&locals.body.projection_store) { - let prev_ty = ty.clone(); + let prev_ty = ty; ty = self.projected_ty(ty, proj.clone()); match proj { ProjectionElem::Deref => { - metadata = if self.size_align_of(&ty, locals)?.is_none() { + metadata = if self.size_align_of(ty, locals)?.is_none() { Some( Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() } .into(), @@ -782,12 +762,12 @@ impl<'db> Evaluator<'db> { ); metadata = None; // Result of index is always sized let ty_size = - self.size_of_sized(&ty, locals, "array inner type should be sized")?; + self.size_of_sized(ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::ConstantIndex { from_end, offset } => { let offset = if from_end { - let len = match prev_ty.kind(Interner) { + let len = match prev_ty.kind() { TyKind::Array(_, c) => match try_const_usize(self.db, c) { Some(it) => it as u64, None => { @@ -806,13 +786,13 @@ impl<'db> Evaluator<'db> { }; metadata = None; // Result of index is always sized let ty_size = - self.size_of_sized(&ty, locals, "array inner type should be sized")?; + self.size_of_sized(ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::Subslice { from, to } => { - let inner_ty = match &ty.kind(Interner) { - TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), - _ => TyKind::Error.intern(Interner), + let inner_ty = match ty.kind() { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner, + _ => Ty::new_error(self.interner(), ErrorGuaranteed), }; metadata = match metadata { Some(it) => { @@ -824,23 +804,23 @@ impl<'db> Evaluator<'db> { None => None, }; let ty_size = - self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?; + self.size_of_sized(inner_ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * (from as usize)); } &ProjectionElem::ClosureField(f) => { - let layout = self.layout(prev_ty.to_nextsolver(interner))?; + let layout = self.layout(prev_ty)?; let offset = layout.fields.offset(f).bytes_usize(); addr = addr.offset(offset); metadata = None; } ProjectionElem::Field(Either::Right(f)) => { - let layout = self.layout(prev_ty.to_nextsolver(interner))?; + let layout = self.layout(prev_ty)?; let offset = layout.fields.offset(f.index as usize).bytes_usize(); addr = addr.offset(offset); metadata = None; // tuple field is always sized FIXME: This is wrong, the tail can be unsized } ProjectionElem::Field(Either::Left(f)) => { - let layout = self.layout(prev_ty.to_nextsolver(interner))?; + let layout = self.layout(prev_ty)?; let variant_layout = match &layout.variants { Variants::Single { .. } | Variants::Empty => &layout, Variants::Multiple { variants, .. } => { @@ -862,7 +842,7 @@ impl<'db> Evaluator<'db> { .bytes_usize(); addr = addr.offset(offset); // Unsized field metadata is equal to the metadata of the struct - if self.size_align_of(&ty, locals)?.is_some() { + if self.size_align_of(ty, locals)?.is_some() { metadata = None; } } @@ -872,51 +852,47 @@ impl<'db> Evaluator<'db> { Ok((addr, ty, metadata)) } - fn layout(&self, ty: crate::next_solver::Ty<'db>) -> Result<'db, Arc> { + fn layout(&self, ty: Ty<'db>) -> Result<'db, Arc> { if let Some(x) = self.layout_cache.borrow().get(&ty) { return Ok(x.clone()); } - let interner = DbInterner::new_with(self.db, None, None); let r = self .db .layout_of_ty(ty, self.trait_env.clone()) - .map_err(|e| MirEvalError::LayoutError(e, convert_ty_for_result(interner, ty)))?; + .map_err(|e| MirEvalError::LayoutError(e, ty))?; self.layout_cache.borrow_mut().insert(ty, r.clone()); Ok(r) } - fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<'db, Arc> { - let interner = DbInterner::new_with(self.db, None, None); - self.layout(crate::next_solver::Ty::new( - interner, - rustc_type_ir::TyKind::Adt( - crate::next_solver::AdtDef::new(adt, interner), - subst.to_nextsolver(interner), - ), - )) + fn layout_adt(&self, adt: AdtId, subst: GenericArgs<'db>) -> Result<'db, Arc> { + self.layout(Ty::new_adt(self.interner(), adt, subst)) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<'db, Ty> { + fn place_ty<'a>(&'a self, p: &Place<'db>, locals: &'a Locals<'db>) -> Result<'db, Ty<'db>> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<'db, Ty> { + fn operand_ty(&self, o: &Operand<'db>, locals: &Locals<'db>) -> Result<'db, Ty<'db>> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, - OperandKind::Constant(c) => c.data(Interner).ty.clone(), + OperandKind::Constant { konst: _, ty } => *ty, &OperandKind::Static(s) => { - let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr] - .to_chalk(self.interner); - TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner) + let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr]; + Ty::new_ref( + self.interner(), + Region::new_static(self.interner()), + ty, + Mutability::Not, + ) } }) } fn operand_ty_and_eval( &mut self, - o: &Operand, - locals: &mut Locals, - ) -> Result<'db, IntervalAndTy> { + o: &Operand<'db>, + locals: &mut Locals<'db>, + ) -> Result<'db, IntervalAndTy<'db>> { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, ty: self.operand_ty(o, locals)?, @@ -925,7 +901,7 @@ impl<'db> Evaluator<'db> { fn interpret_mir( &mut self, - body: Arc, + body: Arc>, args: impl Iterator, ) -> Result<'db, Interval> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { @@ -989,8 +965,8 @@ impl<'db> Evaluator<'db> { .iter() .map(|it| self.operand_ty_and_eval(it, locals)) .collect::>>()?; - let stack_frame = match &fn_ty.kind(Interner) { - TyKind::Function(_) => { + let stack_frame = match fn_ty.kind() { + TyKind::FnPtr(..) => { let bytes = self.eval_operand(func, locals)?; self.exec_fn_pointer( bytes, @@ -1002,7 +978,7 @@ impl<'db> Evaluator<'db> { )? } TyKind::FnDef(def, generic_args) => self.exec_fn_def( - CallableDefId::from_chalk(self.db, *def), + def.0, generic_args, destination_interval, &args, @@ -1089,8 +1065,8 @@ impl<'db> Evaluator<'db> { fn fill_locals_for_body( &mut self, - body: &MirBody, - locals: &mut Locals, + body: &MirBody<'db>, + locals: &mut Locals<'db>, args: impl Iterator, ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); @@ -1113,9 +1089,9 @@ impl<'db> Evaluator<'db> { fn create_locals_for_body( &mut self, - body: &Arc, + body: &Arc>, destination: Option, - ) -> Result<'db, (Locals, usize)> { + ) -> Result<'db, (Locals<'db>, usize)> { let mut locals = match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() { None => Locals { @@ -1139,7 +1115,7 @@ impl<'db> Evaluator<'db> { continue; } let (size, align) = self.size_align_of_sized( - &it.ty, + it.ty, &locals, "no unsized local in extending stack", )?; @@ -1162,8 +1138,11 @@ impl<'db> Evaluator<'db> { Ok((locals, prev_stack_pointer)) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<'db, IntervalOrOwned> { - let interner = DbInterner::new_with(self.db, None, None); + fn eval_rvalue( + &mut self, + r: &Rvalue<'db>, + locals: &mut Locals<'db>, + ) -> Result<'db, IntervalOrOwned> { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1189,33 +1168,33 @@ impl<'db> Evaluator<'db> { Rvalue::UnaryOp(op, val) => { let mut c = self.eval_operand(val, locals)?.get(self)?; let mut ty = self.operand_ty(val, locals)?; - while let TyKind::Ref(_, _, z) = ty.kind(Interner) { - ty = z.clone(); - let size = self.size_of_sized(&ty, locals, "operand of unary op")?; + while let TyKind::Ref(_, z, _) = ty.kind() { + ty = z; + let size = self.size_of_sized(ty, locals, "operand of unary op")?; c = self.read_memory(Address::from_bytes(c)?, size)?; } - if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { + if let TyKind::Float(f) = ty.kind() { match f { - chalk_ir::FloatTy::F16 => { + rustc_type_ir::FloatTy::F16 => { let c = -from_bytes!(f16, u16, c); Owned(u16::try_from(c.to_bits()).unwrap().to_le_bytes().into()) } - chalk_ir::FloatTy::F32 => { + rustc_type_ir::FloatTy::F32 => { let c = -from_bytes!(f32, c); Owned(c.to_le_bytes().into()) } - chalk_ir::FloatTy::F64 => { + rustc_type_ir::FloatTy::F64 => { let c = -from_bytes!(f64, c); Owned(c.to_le_bytes().into()) } - chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F128 => { let c = -from_bytes!(f128, u128, c); Owned(c.to_bits().to_le_bytes().into()) } } } else { let mut c = c.to_vec(); - if ty.as_builtin() == Some(BuiltinType::Bool) { + if matches!(ty.kind(), TyKind::Bool) { c[0] = 1 - c[0]; } else { match op { @@ -1241,8 +1220,8 @@ impl<'db> Evaluator<'db> { let mut lc = lc.get(self)?; let mut rc = rc.get(self)?; let mut ty = self.operand_ty(lhs, locals)?; - while let TyKind::Ref(_, _, z) = ty.kind(Interner) { - ty = z.clone(); + while let TyKind::Ref(_, z, _) = ty.kind() { + ty = z; let size = if ty.is_str() { if *op != BinOp::Eq { never!("Only eq is builtin for `str`"); @@ -1258,14 +1237,14 @@ impl<'db> Evaluator<'db> { rc = self.read_memory(Address::from_bytes(rc)?, ls)?; break 'binary_op Owned(vec![u8::from(lc == rc)]); } else { - self.size_of_sized(&ty, locals, "operand of binary op")? + self.size_of_sized(ty, locals, "operand of binary op")? }; lc = self.read_memory(Address::from_bytes(lc)?, size)?; rc = self.read_memory(Address::from_bytes(rc)?, size)?; } - if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { + if let TyKind::Float(f) = ty.kind() { match f { - chalk_ir::FloatTy::F16 => { + rustc_type_ir::FloatTy::F16 => { let l = from_bytes!(f16, u16, lc); let r = from_bytes!(f16, u16, rc); match op { @@ -1298,7 +1277,7 @@ impl<'db> Evaluator<'db> { ), } } - chalk_ir::FloatTy::F32 => { + rustc_type_ir::FloatTy::F32 => { let l = from_bytes!(f32, lc); let r = from_bytes!(f32, rc); match op { @@ -1326,7 +1305,7 @@ impl<'db> Evaluator<'db> { ), } } - chalk_ir::FloatTy::F64 => { + rustc_type_ir::FloatTy::F64 => { let l = from_bytes!(f64, lc); let r = from_bytes!(f64, rc); match op { @@ -1354,7 +1333,7 @@ impl<'db> Evaluator<'db> { ), } } - chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F128 => { let l = from_bytes!(f128, u128, lc); let r = from_bytes!(f128, u128, rc); match op { @@ -1384,7 +1363,7 @@ impl<'db> Evaluator<'db> { } } } else { - let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); + let is_signed = matches!(ty.kind(), TyKind::Int(_)); let l128 = IntValue::from_bytes(lc, is_signed); let r128 = IntValue::from_bytes(rc, is_signed); match op { @@ -1455,7 +1434,7 @@ impl<'db> Evaluator<'db> { Owned(result.to_le_bytes().to_vec()) } Rvalue::Repeat(it, len) => { - let len = match try_const_usize(self.db, len) { + let len = match try_const_usize(self.db, *len) { Some(it) => it as usize, None => not_supported!("non evaluatable array len in repeat Rvalue"), }; @@ -1465,7 +1444,7 @@ impl<'db> Evaluator<'db> { } Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"), Rvalue::ShallowInitBoxWithAlloc(ty) => { - let Some((size, align)) = self.size_align_of(ty, locals)? else { + let Some((size, align)) = self.size_align_of(*ty, locals)? else { not_supported!("unsized box initialization"); }; let addr = self.heap_allocate(size, align)?; @@ -1487,7 +1466,7 @@ impl<'db> Evaluator<'db> { Owned(r) } AggregateKind::Tuple(ty) => { - let layout = self.layout(ty.to_nextsolver(interner))?; + let layout = self.layout(*ty)?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1496,8 +1475,10 @@ impl<'db> Evaluator<'db> { )?) } AggregateKind::Union(it, f) => { - let layout = - self.layout_adt((*it).into(), Substitution::empty(Interner))?; + let layout = self.layout_adt( + (*it).into(), + GenericArgs::new_from_iter(self.interner(), []), + )?; let offset = layout .fields .offset(u32::from(f.local_id.into_raw()) as usize) @@ -1509,7 +1490,7 @@ impl<'db> Evaluator<'db> { } AggregateKind::Adt(it, subst) => { let (size, variant_layout, tag) = - self.layout_of_variant(*it, subst.clone(), locals)?; + self.layout_of_variant(*it, *subst, locals)?; Owned(self.construct_with_layout( size, &variant_layout, @@ -1518,7 +1499,7 @@ impl<'db> Evaluator<'db> { )?) } AggregateKind::Closure(ty) => { - let layout = self.layout(ty.to_nextsolver(interner))?; + let layout = self.layout(*ty)?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1532,11 +1513,7 @@ impl<'db> Evaluator<'db> { CastKind::PointerCoercion(cast) => match cast { PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; - if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = - ¤t_ty.kind(Interner) - { - let interner = DbInterner::new_with(self.db, None, None); - let current_ty = current_ty.to_nextsolver(interner); + if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = current_ty.kind() { let id = self.vtable_map.id(current_ty); let ptr_size = self.ptr_size(); Owned(id.to_le_bytes()[0..ptr_size].to_vec()) @@ -1549,7 +1526,7 @@ impl<'db> Evaluator<'db> { PointerCast::Unsize => { let current_ty = self.operand_ty(operand, locals)?; let addr = self.eval_operand(operand, locals)?; - self.coerce_unsized(addr, ¤t_ty, target_ty)? + self.coerce_unsized(addr, current_ty, *target_ty)? } PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => { // This is no-op @@ -1566,40 +1543,34 @@ impl<'db> Evaluator<'db> { | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { let current_ty = self.operand_ty(operand, locals)?; - let is_signed = matches!( - current_ty.kind(Interner), - TyKind::Scalar(chalk_ir::Scalar::Int(_)) - ); + let is_signed = matches!(current_ty.kind(), TyKind::Int(_)); let current = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed); let dest_size = - self.size_of_sized(target_ty, locals, "destination of int to int cast")?; + self.size_of_sized(*target_ty, locals, "destination of int to int cast")?; Owned(current[0..dest_size].to_vec()) } CastKind::FloatToInt => { let ty = self.operand_ty(operand, locals)?; - let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else { + let TyKind::Float(ty) = ty.kind() else { not_supported!("invalid float to int cast"); }; let value = self.eval_operand(operand, locals)?.get(self)?; let value = match ty { - chalk_ir::FloatTy::F32 => { + rustc_type_ir::FloatTy::F32 => { let value = value.try_into().unwrap(); f32::from_le_bytes(value) as f64 } - chalk_ir::FloatTy::F64 => { + rustc_type_ir::FloatTy::F64 => { let value = value.try_into().unwrap(); f64::from_le_bytes(value) } - chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { not_supported!("unstable floating point type f16 and f128"); } }; - let is_signed = matches!( - target_ty.kind(Interner), - TyKind::Scalar(chalk_ir::Scalar::Int(_)) - ); + let is_signed = matches!(target_ty.kind(), TyKind::Int(_)); let dest_size = - self.size_of_sized(target_ty, locals, "destination of float to int cast")?; + self.size_of_sized(*target_ty, locals, "destination of float to int cast")?; let dest_bits = dest_size * 8; let (max, min) = if dest_bits == 128 { (i128::MAX, i128::MIN) @@ -1615,53 +1586,46 @@ impl<'db> Evaluator<'db> { } CastKind::FloatToFloat => { let ty = self.operand_ty(operand, locals)?; - let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else { + let TyKind::Float(ty) = ty.kind() else { not_supported!("invalid float to int cast"); }; let value = self.eval_operand(operand, locals)?.get(self)?; let value = match ty { - chalk_ir::FloatTy::F32 => { + rustc_type_ir::FloatTy::F32 => { let value = value.try_into().unwrap(); f32::from_le_bytes(value) as f64 } - chalk_ir::FloatTy::F64 => { + rustc_type_ir::FloatTy::F64 => { let value = value.try_into().unwrap(); f64::from_le_bytes(value) } - chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { not_supported!("unstable floating point type f16 and f128"); } }; - let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) = - target_ty.kind(Interner) - else { + let TyKind::Float(target_ty) = target_ty.kind() else { not_supported!("invalid float to float cast"); }; match target_ty { - chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), - chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), - chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), + rustc_type_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), + rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { not_supported!("unstable floating point type f16 and f128"); } } } CastKind::IntToFloat => { let current_ty = self.operand_ty(operand, locals)?; - let is_signed = matches!( - current_ty.kind(Interner), - TyKind::Scalar(chalk_ir::Scalar::Int(_)) - ); + let is_signed = matches!(current_ty.kind(), TyKind::Int(_)); let value = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed); let value = i128::from_le_bytes(value); - let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) = - target_ty.kind(Interner) - else { + let TyKind::Float(target_ty) = target_ty.kind() else { not_supported!("invalid int to float cast"); }; match target_ty { - chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), - chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), - chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => { + rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), + rustc_type_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), + rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { not_supported!("unstable floating point type f16 and f128"); } } @@ -1675,10 +1639,12 @@ impl<'db> Evaluator<'db> { }) } - fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result<'db, i128> { - let interner = DbInterner::new_with(self.db, None, None); - let layout = self.layout(ty.to_nextsolver(interner))?; - let &TyKind::Adt(chalk_ir::AdtId(AdtId::EnumId(e)), _) = ty.kind(Interner) else { + fn compute_discriminant(&self, ty: Ty<'db>, bytes: &[u8]) -> Result<'db, i128> { + let layout = self.layout(ty)?; + let TyKind::Adt(adt_def, _) = ty.kind() else { + return Ok(0); + }; + let AdtId::EnumId(e) = adt_def.def_id().0 else { return Ok(0); }; match &layout.variants { @@ -1720,35 +1686,35 @@ impl<'db> Evaluator<'db> { fn coerce_unsized_look_through_fields( &self, - ty: &Ty, - goal: impl Fn(&TyKind) -> Option, + ty: Ty<'db>, + goal: impl Fn(TyKind<'db>) -> Option, ) -> Result<'db, T> { - let kind = ty.kind(Interner); + let kind = ty.kind(); if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(id, subst) = kind - && let AdtId::StructId(struct_id) = id.0 + if let TyKind::Adt(adt_ef, subst) = kind + && let AdtId::StructId(struct_id) = adt_ef.def_id().0 { - let field_types = self.db.field_types(struct_id.into()); + let field_types = self.db.field_types_ns(struct_id.into()); if let Some(ty) = - field_types.iter().last().map(|it| it.1.clone().substitute(Interner, subst)) + field_types.iter().last().map(|it| it.1.instantiate(self.interner(), subst)) { - return self.coerce_unsized_look_through_fields(&ty, goal); + return self.coerce_unsized_look_through_fields(ty, goal); } } - Err(MirEvalError::CoerceUnsizedError(ty.clone())) + Err(MirEvalError::CoerceUnsizedError(ty)) } fn coerce_unsized( &mut self, addr: Interval, - current_ty: &Ty, - target_ty: &Ty, + current_ty: Ty<'db>, + target_ty: Ty<'db>, ) -> Result<'db, IntervalOrOwned> { - fn for_ptr(it: &TyKind) -> Option { + fn for_ptr<'db>(it: TyKind<'db>) -> Option> { match it { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), + TyKind::RawPtr(ty, _) | TyKind::Ref(_, ty, _) => Some(ty), _ => None, } } @@ -1761,15 +1727,15 @@ impl<'db> Evaluator<'db> { /// Adds metadata to the address and create the fat pointer result of the unsizing operation. fn unsizing_ptr_from_addr( &mut self, - target_ty: Ty, - current_ty: Ty, + target_ty: Ty<'db>, + current_ty: Ty<'db>, addr: Interval, ) -> Result<'db, IntervalOrOwned> { use IntervalOrOwned::*; - Ok(match &target_ty.kind(Interner) { - TyKind::Slice(_) => match ¤t_ty.kind(Interner) { + Ok(match &target_ty.kind() { + TyKind::Slice(_) => match ¤t_ty.kind() { TyKind::Array(_, size) => { - let len = match try_const_usize(self.db, size) { + let len = match try_const_usize(self.db, *size) { None => { not_supported!("unevaluatble len of array in coerce unsized") } @@ -1785,9 +1751,7 @@ impl<'db> Evaluator<'db> { not_supported!("slice unsizing from non array type {t:?}") } }, - TyKind::Dyn(_) => { - let interner = DbInterner::new_with(self.db, None, None); - let current_ty = current_ty.to_nextsolver(interner); + TyKind::Dynamic(..) => { let vtable = self.vtable_map.id(current_ty); let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; @@ -1795,12 +1759,14 @@ impl<'db> Evaluator<'db> { r.extend(vtable.to_le_bytes()); Owned(r) } - TyKind::Adt(id, target_subst) => match ¤t_ty.kind(Interner) { - TyKind::Adt(current_id, current_subst) => { + TyKind::Adt(adt_def, target_subst) => match ¤t_ty.kind() { + TyKind::Adt(current_adt_def, current_subst) => { + let id = adt_def.def_id().0; + let current_id = current_adt_def.def_id().0; if id != current_id { not_supported!("unsizing struct with different type"); } - let id = match id.0 { + let id = match id { AdtId::StructId(s) => s, AdtId::UnionId(_) => not_supported!("unsizing unions"), AdtId::EnumId(_) => not_supported!("unsizing enums"), @@ -1809,12 +1775,10 @@ impl<'db> Evaluator<'db> { else { not_supported!("unsizing struct without field"); }; - let target_last_field = self.db.field_types(id.into())[last_field] - .clone() - .substitute(Interner, target_subst); - let current_last_field = self.db.field_types(id.into())[last_field] - .clone() - .substitute(Interner, current_subst); + let target_last_field = self.db.field_types_ns(id.into())[last_field] + .instantiate(self.interner(), target_subst); + let current_last_field = self.db.field_types_ns(id.into())[last_field] + .instantiate(self.interner(), current_subst); return self.unsizing_ptr_from_addr( target_last_field, current_last_field, @@ -1830,10 +1794,9 @@ impl<'db> Evaluator<'db> { fn layout_of_variant( &mut self, it: VariantId, - subst: Substitution, - locals: &Locals, + subst: GenericArgs<'db>, + locals: &Locals<'db>, ) -> Result<'db, (usize, Arc, Option<(usize, usize, i128)>)> { - let interner = DbInterner::new_with(self.db, None, None); let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner && let VariantId::EnumVariantId(it) = it @@ -1843,11 +1806,7 @@ impl<'db> Evaluator<'db> { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout let i = self.const_eval_discriminant(it)?; - return Ok(( - 16, - self.layout(crate::next_solver::Ty::new_empty_tuple(interner))?, - Some((0, 16, i)), - )); + return Ok((16, self.layout(Ty::new_empty_tuple(self.interner()))?, Some((0, 16, i)))); } let layout = self.layout_adt(adt, subst)?; Ok(match &layout.variants { @@ -1930,7 +1889,11 @@ impl<'db> Evaluator<'db> { Ok(result) } - fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<'db, Interval> { + fn eval_operand( + &mut self, + it: &Operand<'db>, + locals: &mut Locals<'db>, + ) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { locals.drop_flags.remove_place(p, &locals.body.projection_store); @@ -1940,32 +1903,30 @@ impl<'db> Evaluator<'db> { let addr = self.eval_static(*st, locals)?; Interval::new(addr, self.ptr_size()) } - OperandKind::Constant(konst) => self.allocate_const_in_heap(locals, konst)?, + OperandKind::Constant { konst, .. } => self.allocate_const_in_heap(locals, *konst)?, }) } #[allow(clippy::double_parens)] - fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<'db, Interval> { - let interner = self.interner; - let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner) - else { - not_supported!("evaluating non concrete constant"); - }; + fn allocate_const_in_heap( + &mut self, + locals: &Locals<'db>, + konst: Const<'db>, + ) -> Result<'db, Interval> { let result_owner; - let (v, memory_map) = match &c.interned { - ConstScalar::Bytes(v, mm) => (v, mm), - ConstScalar::UnevaluatedConst(const_id, subst) => 'b: { - let mut const_id = *const_id; - let mut subst = subst.clone(); + let value = match konst.kind() { + ConstKind::Value(value) => value, + ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => 'b: { + let mut const_id = match const_id { + SolverDefId::ConstId(it) => GeneralConstId::from(it), + SolverDefId::StaticId(it) => it.into(), + _ => unreachable!("unevaluated consts should be consts or statics"), + }; + let mut subst = subst; if let hir_def::GeneralConstId::ConstId(c) = const_id { - let (c, s) = lookup_impl_const( - self.interner, - self.trait_env.clone(), - c, - subst.to_nextsolver(self.interner), - ); + let (c, s) = lookup_impl_const(&self.infcx, self.trait_env.clone(), c, subst); const_id = hir_def::GeneralConstId::ConstId(c); - subst = s.to_chalk(self.interner); + subst = s; } result_owner = self .db @@ -1974,15 +1935,15 @@ impl<'db> Evaluator<'db> { let name = const_id.name(self.db); MirEvalError::ConstEvalError(name, Box::new(e)) })?; - if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value - && let ConstScalar::Bytes(v, mm) = &c.interned - { - break 'b (v, mm); + if let ConstKind::Value(value) = result_owner.kind() { + break 'b value; } not_supported!("unevaluatable constant"); } - ConstScalar::Unknown => not_supported!("evaluating unknown const"), + _ => not_supported!("evaluating unknown const"), }; + let ValueConst { ty, value } = value; + let ConstBytes { memory: v, memory_map } = value.inner(); let patch_map = memory_map.transform_addresses(|b, align| { let addr = self.heap_allocate(b.len(), align)?; self.write_memory(addr, b)?; @@ -1996,7 +1957,7 @@ impl<'db> Evaluator<'db> { } else if size < 16 && v.len() == 16 { Cow::Borrowed(&v[0..size]) } else { - return Err(MirEvalError::InvalidConst(konst.clone())); + return Err(MirEvalError::InvalidConst(konst)); } } else { Cow::Borrowed(v) @@ -2012,17 +1973,17 @@ impl<'db> Evaluator<'db> { MemoryMap::Complex(cm) => cm.vtable.ty_of_bytes(bytes), }, addr, - ty.to_nextsolver(interner), + ty, locals, )?; Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn eval_place(&mut self, p: &Place<'db>, locals: &Locals<'db>) -> Result<'db, Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, - self.size_of_sized(&self.place_ty(p, locals)?, locals, "type of this place")?, + self.size_of_sized(self.place_ty(p, locals)?, locals, "type of this place")?, )) } @@ -2118,9 +2079,12 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<'db, Option<(usize, usize)>> { - let interner = DbInterner::new_with(self.db, None, None); - if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) { + fn size_align_of( + &self, + ty: Ty<'db>, + locals: &Locals<'db>, + ) -> Result<'db, Option<(usize, usize)>> { + if let Some(layout) = self.layout_cache.borrow().get(&ty) { return Ok(layout .is_sized() .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))); @@ -2133,7 +2097,7 @@ impl<'db> Evaluator<'db> { // infinite sized type errors) we use a dummy size return Ok(Some((16, 16))); } - let layout = self.layout(ty.to_nextsolver(interner)); + let layout = self.layout(ty); if self.assert_placeholder_ty_is_unused && matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { @@ -2145,10 +2109,15 @@ impl<'db> Evaluator<'db> { /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` - fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result<'db, usize> { + fn size_of_sized( + &self, + ty: Ty<'db>, + locals: &Locals<'db>, + what: &'static str, + ) -> Result<'db, usize> { match self.size_align_of(ty, locals)? { Some(it) => Ok(it.0), - None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), + None => Err(MirEvalError::TypeIsUnsized(ty, what)), } } @@ -2156,13 +2125,13 @@ impl<'db> Evaluator<'db> { /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` fn size_align_of_sized( &self, - ty: &Ty, - locals: &Locals, + ty: Ty<'db>, + locals: &Locals<'db>, what: &'static str, ) -> Result<'db, (usize, usize)> { match self.size_align_of(ty, locals)? { Some(it) => Ok(it), - None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), + None => Err(MirEvalError::TypeIsUnsized(ty, what)), } } @@ -2197,23 +2166,22 @@ impl<'db> Evaluator<'db> { fn create_memory_map( &self, bytes: &[u8], - ty: &Ty, - locals: &Locals, + ty: Ty<'db>, + locals: &Locals<'db>, ) -> Result<'db, ComplexMemoryMap<'db>> { fn rec<'db>( this: &Evaluator<'db>, bytes: &[u8], - ty: &Ty, - locals: &Locals, + ty: Ty<'db>, + locals: &Locals<'db>, mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, ) -> Result<'db, ()> { - let interner = DbInterner::new_with(this.db, None, None); if stack_depth_limit.checked_sub(1).is_none() { return Err(MirEvalError::StackOverflow); } - match ty.kind(Interner) { - TyKind::Ref(_, _, t) => { + match ty.kind() { + TyKind::Ref(_, t, _) => { let size = this.size_align_of(t, locals)?; match size { Some((size, _)) => { @@ -2226,29 +2194,28 @@ impl<'db> Evaluator<'db> { None => { let mut check_inner = None; let (addr, meta) = bytes.split_at(bytes.len() / 2); - let element_size = match t.kind(Interner) { + let element_size = match t.kind() { TyKind::Str => 1, TyKind::Slice(t) => { - check_inner = Some(t.clone()); + check_inner = Some(t); this.size_of_sized(t, locals, "slice inner type")? } - TyKind::Dyn(_) => { + TyKind::Dynamic(..) => { let t = this.vtable_map.ty_of_bytes(meta)?; - let t = convert_ty_for_result(interner, t); - check_inner = Some(t.clone()); - this.size_of_sized(&t, locals, "dyn concrete type")? + check_inner = Some(t); + this.size_of_sized(t, locals, "dyn concrete type")? } _ => return Ok(()), }; - let count = match t.kind(Interner) { - TyKind::Dyn(_) => 1, + let count = match t.kind() { + TyKind::Dynamic(..) => 1, _ => from_bytes!(usize, meta), }; let size = element_size * count; let addr = Address::from_bytes(addr)?; let b = this.read_memory(addr, size)?; mm.insert(addr.to_usize(), b.into()); - if let Some(ty) = &check_inner { + if let Some(ty) = check_inner { for i in 0..count { let offset = element_size * i; rec( @@ -2282,12 +2249,11 @@ impl<'db> Evaluator<'db> { )?; } } - TyKind::Tuple(_, subst) => { - let layout = this.layout(ty.to_nextsolver(interner))?; - for (id, ty) in subst.iter(Interner).enumerate() { - let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + TyKind::Tuple(subst) => { + let layout = this.layout(ty)?; + for (id, ty) in subst.iter().enumerate() { let offset = layout.fields.offset(id).bytes_usize(); - let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); + let size = this.layout(ty)?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2298,18 +2264,18 @@ impl<'db> Evaluator<'db> { )?; } } - TyKind::Adt(adt, subst) => match adt.0 { + TyKind::Adt(adt, subst) => match adt.def_id().0 { AdtId::StructId(s) => { let data = s.fields(this.db); - let layout = this.layout(ty.to_nextsolver(interner))?; - let field_types = this.db.field_types(s.into()); + let layout = this.layout(ty)?; + let field_types = this.db.field_types_ns(s.into()); for (f, _) in data.fields().iter() { let offset = layout .fields .offset(u32::from(f.into_raw()) as usize) .bytes_usize(); - let ty = &field_types[f].clone().substitute(Interner, subst); - let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); + let ty = field_types[f].instantiate(this.interner(), subst); + let size = this.layout(ty)?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2321,7 +2287,7 @@ impl<'db> Evaluator<'db> { } } AdtId::EnumId(e) => { - let layout = this.layout(ty.to_nextsolver(interner))?; + let layout = this.layout(ty)?; if let Some((v, l)) = detect_variant_from_bytes( &layout, this.db, @@ -2330,13 +2296,12 @@ impl<'db> Evaluator<'db> { e, ) { let data = v.fields(this.db); - let field_types = this.db.field_types(v.into()); + let field_types = this.db.field_types_ns(v.into()); for (f, _) in data.fields().iter() { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); - let ty = &field_types[f].clone().substitute(Interner, subst); - let size = - this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); + let ty = field_types[f].instantiate(this.interner(), subst); + let size = this.layout(ty)?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2350,9 +2315,17 @@ impl<'db> Evaluator<'db> { } AdtId::UnionId(_) => (), }, - TyKind::Alias(AliasTy::Projection(proj)) => { - let ty = this.db.normalize_projection(proj.clone(), this.trait_env.clone()); - rec(this, bytes, &ty, locals, mm, stack_depth_limit - 1)?; + TyKind::Alias(AliasTyKind::Projection, _) => { + let mut ocx = ObligationCtxt::new(&this.infcx); + let ty = ocx + .structurally_normalize_ty( + &ObligationCause::dummy(), + this.trait_env.env, + ty, + ) + .map_err(|_| MirEvalError::NotSupported("couldn't normalize".to_owned()))?; + + rec(this, bytes, ty, locals, mm, stack_depth_limit - 1)?; } _ => (), } @@ -2366,23 +2339,18 @@ impl<'db> Evaluator<'db> { fn patch_addresses( &mut self, patch_map: &FxHashMap, - ty_of_bytes: impl Fn(&[u8]) -> Result<'db, crate::next_solver::Ty<'db>> + Copy, + ty_of_bytes: impl Fn(&[u8]) -> Result<'db, Ty<'db>> + Copy, addr: Address, - ty: crate::next_solver::Ty<'db>, - locals: &Locals, + ty: Ty<'db>, + locals: &Locals<'db>, ) -> Result<'db, ()> { - let interner = DbInterner::new_with(self.db, None, None); // FIXME: support indirect references let layout = self.layout(ty)?; - let my_size = self.size_of_sized( - &convert_ty_for_result(interner, ty), - locals, - "value to patch address", - )?; + let my_size = self.size_of_sized(ty, locals, "value to patch address")?; use rustc_type_ir::TyKind; match ty.kind() { TyKind::Ref(_, t, _) => { - let size = self.size_align_of(&convert_ty_for_result(interner, t), locals)?; + let size = self.size_align_of(t, locals)?; match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); @@ -2407,7 +2375,7 @@ impl<'db> Evaluator<'db> { AdtId::StructId(s) => { for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.instantiate(interner, args); + let ty = ty.instantiate(self.interner(), args); self.patch_addresses( patch_map, ty_of_bytes, @@ -2428,7 +2396,7 @@ impl<'db> Evaluator<'db> { ) { for (i, (_, ty)) in self.db.field_types_ns(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.instantiate(interner, args); + let ty = ty.instantiate(self.interner(), args); self.patch_addresses( patch_map, ty_of_bytes, @@ -2447,15 +2415,11 @@ impl<'db> Evaluator<'db> { } } TyKind::Array(inner, len) => { - let len = match consteval_nextsolver::try_const_usize(self.db, len) { + let len = match consteval::try_const_usize(self.db, len) { Some(it) => it as usize, None => not_supported!("non evaluatable array len in patching addresses"), }; - let size = self.size_of_sized( - &convert_ty_for_result(interner, inner), - locals, - "inner of array", - )?; + let size = self.size_of_sized(inner, locals, "inner of array")?; for i in 0..len { self.patch_addresses( patch_map, @@ -2498,29 +2462,22 @@ impl<'db> Evaluator<'db> { &mut self, bytes: Interval, destination: Interval, - args: &[IntervalAndTy], - locals: &Locals, - target_bb: Option, + args: &[IntervalAndTy<'db>], + locals: &Locals<'db>, + target_bb: Option>, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?; - let interner = DbInterner::new_with(self.db, None, None); use rustc_type_ir::TyKind; match next_ty.kind() { - TyKind::FnDef(def, generic_args) => self.exec_fn_def( - def.0, - &convert_args_for_result(interner, generic_args.as_slice()), - destination, - args, - locals, - target_bb, - span, - ), + TyKind::FnDef(def, generic_args) => { + self.exec_fn_def(def.0, generic_args, destination, args, locals, target_bb, span) + } TyKind::Closure(id, generic_args) => self.exec_closure( - id.0.into(), + id.0, bytes.slice(0..0), - &convert_args_for_result(interner, generic_args.as_slice()), + generic_args, destination, args, locals, @@ -2532,21 +2489,17 @@ impl<'db> Evaluator<'db> { fn exec_closure( &mut self, - closure: ClosureId, + closure: InternedClosureId, closure_data: Interval, - generic_args: &Substitution, + generic_args: GenericArgs<'db>, destination: Interval, - args: &[IntervalAndTy], - locals: &Locals, + args: &[IntervalAndTy<'db>], + locals: &Locals<'db>, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let mir_body = self .db - .monomorphized_mir_body_for_closure( - closure.into(), - generic_args.clone(), - self.trait_env.clone(), - ) + .monomorphized_mir_body_for_closure(closure, generic_args, self.trait_env.clone()) .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?; let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() { @@ -2572,14 +2525,13 @@ impl<'db> Evaluator<'db> { fn exec_fn_def( &mut self, def: CallableDefId, - generic_args: &Substitution, + generic_args: GenericArgs<'db>, destination: Interval, - args: &[IntervalAndTy], - locals: &Locals, - target_bb: Option, + args: &[IntervalAndTy<'db>], + locals: &Locals<'db>, + target_bb: Option>, span: MirSpan, - ) -> Result<'db, Option> { - let generic_args = generic_args.clone(); + ) -> Result<'db, Option>> { match def { CallableDefId::FunctionId(def) => { if self.detect_fn_trait(def).is_some() { @@ -2633,36 +2585,26 @@ impl<'db> Evaluator<'db> { fn get_mir_or_dyn_index( &self, def: FunctionId, - generic_args: Substitution, - locals: &Locals, + generic_args: GenericArgs<'db>, + locals: &Locals<'db>, span: MirSpan, - ) -> Result<'db, MirOrDynIndex> { + ) -> Result<'db, MirOrDynIndex<'db>> { let pair = (def, generic_args); if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) { return Ok(r.clone()); } let (def, generic_args) = pair; - let r = if let Some(self_ty_idx) = is_dyn_method( - self.interner, - self.trait_env.clone(), - def, - generic_args.to_nextsolver(self.interner), - ) { + let r = if let Some(self_ty_idx) = + is_dyn_method(self.interner(), self.trait_env.clone(), def, generic_args) + { MirOrDynIndex::Dyn(self_ty_idx) } else { - let (imp, generic_args) = self.db.lookup_impl_method( - self.trait_env.clone(), - def, - generic_args.to_nextsolver(self.interner), - ); + let (imp, generic_args) = + self.db.lookup_impl_method(self.trait_env.clone(), def, generic_args); let mir_body = self .db - .monomorphized_mir_body( - imp.into(), - generic_args.to_chalk(self.interner), - self.trait_env.clone(), - ) + .monomorphized_mir_body(imp.into(), generic_args, self.trait_env.clone()) .map_err(|e| { MirEvalError::InFunction( Box::new(MirEvalError::MirLowerError(imp, e)), @@ -2678,18 +2620,17 @@ impl<'db> Evaluator<'db> { fn exec_fn_with_args( &mut self, mut def: FunctionId, - args: &[IntervalAndTy], - generic_args: Substitution, - locals: &Locals, + args: &[IntervalAndTy<'db>], + generic_args: GenericArgs<'db>, + locals: &Locals<'db>, destination: Interval, - target_bb: Option, + target_bb: Option>, span: MirSpan, - ) -> Result<'db, Option> { - let interner = DbInterner::new_with(self.db, None, None); + ) -> Result<'db, Option>> { if self.detect_and_exec_special_function( def, args, - &generic_args, + generic_args, locals, destination, span, @@ -2700,7 +2641,7 @@ impl<'db> Evaluator<'db> { def = redirect_def; } let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); - match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { + match self.get_mir_or_dyn_index(def, generic_args, locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { // In the layout of current possible receiver, which at the moment of writing this code is one of // `&T`, `&mut T`, `Box`, `Rc`, `Arc`, and `Pin

` where `P` is one of possible receivers, @@ -2712,18 +2653,16 @@ impl<'db> Evaluator<'db> { .vtable_map .ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?; let mut args_for_target = args.to_vec(); - let ty = convert_ty_for_result(interner, ty); args_for_target[0] = IntervalAndTy { interval: args_for_target[0].interval.slice(0..self.ptr_size()), - ty: ty.clone(), + ty, }; - let ty = ty.clone().cast(Interner); - let generics_for_target = Substitution::from_iter( - Interner, + let generics_for_target = GenericArgs::new_from_iter( + self.interner(), generic_args - .iter(Interner) + .iter() .enumerate() - .map(|(i, it)| if i == self_ty_idx { &ty } else { it }), + .map(|(i, it)| if i == self_ty_idx { ty.into() } else { it }), ); self.exec_fn_with_args( def, @@ -2749,14 +2688,14 @@ impl<'db> Evaluator<'db> { fn exec_looked_up_function( &mut self, - mir_body: Arc, - locals: &Locals, + mir_body: Arc>, + locals: &Locals<'db>, def: FunctionId, arg_bytes: impl Iterator, span: MirSpan, destination: Interval, - target_bb: Option, - ) -> Result<'db, Option> { + target_bb: Option>, + ) -> Result<'db, Option>> { Ok(if let Some(target_bb) = target_bb { let (mut locals, prev_stack_ptr) = self.create_locals_for_body(&mir_body, Some(destination))?; @@ -2778,47 +2717,40 @@ impl<'db> Evaluator<'db> { fn exec_fn_trait( &mut self, def: FunctionId, - args: &[IntervalAndTy], - generic_args: Substitution, - locals: &Locals, + args: &[IntervalAndTy<'db>], + generic_args: GenericArgs<'db>, + locals: &Locals<'db>, destination: Interval, - target_bb: Option, + target_bb: Option>, span: MirSpan, - ) -> Result<'db, Option> { - let interner = DbInterner::new_with(self.db, None, None); + ) -> Result<'db, Option>> { let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; - let mut func_ty = func.ty.clone(); + let mut func_ty = func.ty; let mut func_data = func.interval; - while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) { - func_ty = z.clone(); - if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) { + while let TyKind::Ref(_, z, _) = func_ty.kind() { + func_ty = z; + if matches!(func_ty.kind(), TyKind::Dynamic(..)) { let id = from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]); func_data = func_data.slice(0..self.ptr_size()); - func_ty = convert_ty_for_result(interner, self.vtable_map.ty(id)?); + func_ty = self.vtable_map.ty(id)?; } - let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; + let size = self.size_of_sized(func_ty, locals, "self type of fn trait")?; func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; } - match &func_ty.kind(Interner) { - TyKind::FnDef(def, subst) => self.exec_fn_def( - CallableDefId::from_chalk(self.db, *def), - subst, - destination, - &args[1..], - locals, - target_bb, - span, - ), - TyKind::Function(_) => { + match func_ty.kind() { + TyKind::FnDef(def, subst) => { + self.exec_fn_def(def.0, subst, destination, &args[1..], locals, target_bb, span) + } + TyKind::FnPtr(..) => { self.exec_fn_pointer(func_data, destination, &args[1..], locals, target_bb, span) } TyKind::Closure(closure, subst) => self.exec_closure( - *closure, + closure.0, func_data, - &ClosureSubst(subst).parent_subst(self.db), + subst.split_closure_args_untupled().parent_args, destination, &args[1..], locals, @@ -2829,12 +2761,8 @@ impl<'db> Evaluator<'db> { let arg0 = func; let args = &args[1..]; let arg1 = { - let ty = TyKind::Tuple( - args.len(), - Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())), - ) - .intern(Interner); - let layout = self.layout(ty.to_nextsolver(interner))?; + let ty = Ty::new_tup_from_iter(self.interner(), args.iter().map(|it| it.ty)); + let layout = self.layout(ty)?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -2860,7 +2788,7 @@ impl<'db> Evaluator<'db> { } } - fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<'db, Address> { + fn eval_static(&mut self, st: StaticId, locals: &Locals<'db>) -> Result<'db, Address> { if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -2869,11 +2797,10 @@ impl<'db> Evaluator<'db> { let konst = self.db.const_eval_static(st).map_err(|e| { MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e)) })?; - self.allocate_const_in_heap(locals, &konst)? + self.allocate_const_in_heap(locals, konst)? } else { - let ty = - self.db.infer(st.into())[self.db.body(st.into()).body_expr].to_chalk(self.interner); - let Some((size, align)) = self.size_align_of(&ty, locals)? else { + let ty = self.db.infer(st.into())[self.db.body(st.into()).body_expr]; + let Some((size, align)) = self.size_align_of(ty, locals)? else { not_supported!("unsized extern static"); }; let addr = self.heap_allocate(size, align)?; @@ -2907,7 +2834,12 @@ impl<'db> Evaluator<'db> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<'db, ()> { + fn drop_place( + &mut self, + place: &Place<'db>, + locals: &mut Locals<'db>, + span: MirSpan, + ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); @@ -2921,8 +2853,8 @@ impl<'db> Evaluator<'db> { fn run_drop_glue_deep( &mut self, - ty: Ty, - locals: &Locals, + ty: Ty<'db>, + locals: &Locals<'db>, addr: Address, _metadata: &[u8], span: MirSpan, @@ -2936,7 +2868,7 @@ impl<'db> Evaluator<'db> { return Ok(()); }; - let generic_args = Substitution::from1(Interner, ty.clone()); + let generic_args = GenericArgs::new_from_iter(self.interner(), [ty.into()]); if let Ok(MirOrDynIndex::Mir(body)) = self.get_mir_or_dyn_index(drop_fn, generic_args, locals, span) { @@ -2950,26 +2882,27 @@ impl<'db> Evaluator<'db> { None, )?; } - match ty.kind(Interner) { - TyKind::Adt(id, subst) => { - match id.0 { + match ty.kind() { + TyKind::Adt(adt_def, subst) => { + let id = adt_def.def_id().0; + match id { AdtId::StructId(s) => { let data = self.db.struct_signature(s); if data.flags.contains(StructFlags::IS_MANUALLY_DROP) { return Ok(()); } - let layout = self.layout_adt(id.0, subst.clone())?; + let layout = self.layout_adt(id, subst)?; let variant_fields = s.fields(self.db); match variant_fields.shape { FieldsShape::Record | FieldsShape::Tuple => { - let field_types = self.db.field_types(s.into()); + let field_types = self.db.field_types_ns(s.into()); for (field, _) in variant_fields.fields().iter() { let offset = layout .fields .offset(u32::from(field.into_raw()) as usize) .bytes_usize(); let addr = addr.offset(offset); - let ty = field_types[field].clone().substitute(Interner, subst); + let ty = field_types[field].instantiate(self.interner(), subst); self.run_drop_glue_deep(ty, locals, addr, &[], span)?; } } @@ -2980,28 +2913,34 @@ impl<'db> Evaluator<'db> { AdtId::EnumId(_) => (), } } - TyKind::AssociatedType(_, _) - | TyKind::Scalar(_) - | TyKind::Tuple(_, _) + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Tuple(_) | TyKind::Array(_, _) | TyKind::Slice(_) - | TyKind::Raw(_, _) + | TyKind::RawPtr(_, _) | TyKind::Ref(_, _, _) - | TyKind::OpaqueType(_, _) + | TyKind::Alias(..) | TyKind::FnDef(_, _) | TyKind::Str | TyKind::Never | TyKind::Closure(_, _) | TyKind::Coroutine(_, _) + | TyKind::CoroutineClosure(..) | TyKind::CoroutineWitness(_, _) | TyKind::Foreign(_) - | TyKind::Error + | TyKind::Error(_) + | TyKind::Param(_) | TyKind::Placeholder(_) - | TyKind::Dyn(_) - | TyKind::Alias(_) - | TyKind::Function(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => (), + | TyKind::Dynamic(..) + | TyKind::FnPtr(..) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Pat(..) + | TyKind::UnsafeBinder(..) => (), }; Ok(()) } @@ -3020,9 +2959,9 @@ impl<'db> Evaluator<'db> { pub fn render_const_using_debug_impl<'db>( db: &'db dyn HirDatabase, owner: DefWithBodyId, - c: &Const, + c: Const<'db>, + ty: Ty<'db>, ) -> Result<'db, String> { - let interner = DbInterner::new_with(db, None, None); let mut evaluator = Evaluator::new(db, owner, false, None)?; let locals = &Locals { ptr: ArenaMap::new(), @@ -3051,12 +2990,11 @@ pub fn render_const_using_debug_impl<'db>( // and its ABI doesn't break yet, we put it in memory manually. let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?; evaluator.write_memory(a2, &data.addr.to_bytes())?; - let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef( - CallableDefId::FunctionId(debug_fmt_fn).to_chalk(db), - Substitution::from1(Interner, c.data(Interner).ty.clone()), - ) - .intern(Interner) - .to_nextsolver(interner)); + let debug_fmt_fn_ptr = evaluator.vtable_map.id(Ty::new_fn_def( + evaluator.interner(), + CallableDefId::FunctionId(debug_fmt_fn).into(), + GenericArgs::new_from_iter(evaluator.interner(), [ty.into()]), + )); evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; // a3 = ::core::fmt::Arguments::new_v1(a1, a2) // FIXME: similarly, we should call function here, not directly working with memory. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 9ef0012a89c1c..c45ae9dcc3d3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -3,32 +3,21 @@ //! use std::cmp::{self, Ordering}; -use chalk_ir::TyKind; -use hir_def::signatures::FunctionSignature; -use hir_def::{ - CrateRootModuleId, - builtin_type::{BuiltinInt, BuiltinUint}, - resolver::HasResolver, -}; +use hir_def::{CrateRootModuleId, resolver::HasResolver, signatures::FunctionSignature}; use hir_expand::name::Name; use intern::{Symbol, sym}; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use stdx::never; -use crate::next_solver::mapping::NextSolverToChalk; use crate::{ display::DisplayTarget, drop::{DropGlue, has_drop_glue}, - error_lifetime, mir::eval::{ - Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, - InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, - LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution, - Ty, TyBuilder, TyExt, pad16, - }, - next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, convert_ty_for_result}, + Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay, + InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem, + Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, pad16, }, + next_solver::Region, }; mod simd; @@ -53,9 +42,9 @@ impl<'db> Evaluator<'db> { pub(super) fn detect_and_exec_special_function( &mut self, def: FunctionId, - args: &[IntervalAndTy], - generic_args: &Substitution, - locals: &Locals, + args: &[IntervalAndTy<'db>], + generic_args: GenericArgs<'db>, + locals: &Locals<'db>, destination: Interval, span: MirSpan, ) -> Result<'db, bool> { @@ -118,18 +107,16 @@ impl<'db> Evaluator<'db> { if let ItemContainerId::TraitId(t) = def.lookup(self.db).container && self.db.lang_attr(t.into()) == Some(LangItem::Clone) { - let [self_ty] = generic_args.as_slice(Interner) else { + let [self_ty] = generic_args.as_slice() else { not_supported!("wrong generic arg count for clone"); }; - let Some(self_ty) = self_ty.ty(Interner) else { + let Some(self_ty) = self_ty.ty() else { not_supported!("wrong generic arg kind for clone"); }; // Clone has special impls for tuples and function pointers - if matches!( - self_ty.kind(Interner), - TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..) - ) { - self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?; + if matches!(self_ty.kind(), TyKind::FnPtr(..) | TyKind::Tuple(..) | TyKind::Closure(..)) + { + self.exec_clone(def, args, self_ty, locals, destination, span)?; return Ok(true); } // Return early to prevent caching clone as non special fn. @@ -161,15 +148,14 @@ impl<'db> Evaluator<'db> { fn exec_clone( &mut self, def: FunctionId, - args: &[IntervalAndTy], - self_ty: Ty, - locals: &Locals, + args: &[IntervalAndTy<'db>], + self_ty: Ty<'db>, + locals: &Locals<'db>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { - let interner = self.interner; - match self_ty.kind(Interner) { - TyKind::Function(_) => { + match self_ty.kind() { + TyKind::FnPtr(..) => { let [arg] = args else { not_supported!("wrong arg count for clone"); }; @@ -182,30 +168,35 @@ impl<'db> Evaluator<'db> { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into()); + let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure(id.0); let infer = self.db.infer(closure_owner); - let (captures, _) = infer.closure_info((*id).into()); - let layout = self.layout(self_ty.to_nextsolver(interner))?; + let (captures, _) = infer.closure_info(id.0); + let layout = self.layout(self_ty)?; let db = self.db; - let ty_iter = captures - .iter() - .map(|c| c.ty(db, subst.to_nextsolver(interner)).to_chalk(interner)); + let ty_iter = captures.iter().map(|c| c.ty(db, subst)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } - TyKind::Tuple(_, subst) => { + TyKind::Tuple(subst) => { let [arg] = args else { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let layout = self.layout(self_ty.to_nextsolver(interner))?; - let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone()); - self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; + let layout = self.layout(self_ty)?; + self.exec_clone_for_fields( + subst.iter(), + layout, + addr, + def, + locals, + destination, + span, + )?; } _ => { self.exec_fn_with_args( def, args, - Substitution::from1(Interner, self_ty), + GenericArgs::new_from_iter(self.interner(), [self_ty.into()]), locals, destination, None, @@ -218,21 +209,25 @@ impl<'db> Evaluator<'db> { fn exec_clone_for_fields( &mut self, - ty_iter: impl Iterator, + ty_iter: impl Iterator>, layout: Arc, addr: Address, def: FunctionId, - locals: &Locals, + locals: &Locals<'db>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { - let interner = DbInterner::new_with(self.db, None, None); for (i, ty) in ty_iter.enumerate() { - let size = self.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); + let size = self.layout(ty)?.size.bytes_usize(); let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; let arg = IntervalAndTy { interval: Interval { addr: tmp, size: self.ptr_size() }, - ty: TyKind::Ref(Mutability::Not, error_lifetime(), ty.clone()).intern(Interner), + ty: Ty::new_ref( + self.interner(), + Region::error(self.interner()), + ty, + Mutability::Not, + ), }; let offset = layout.fields.offset(i).bytes_usize(); self.write_memory(tmp, &addr.offset(offset).to_bytes())?; @@ -251,7 +246,7 @@ impl<'db> Evaluator<'db> { fn exec_alloc_fn( &mut self, alloc_fn: &Symbol, - args: &[IntervalAndTy], + args: &[IntervalAndTy<'db>], destination: Interval, ) -> Result<'db, ()> { match alloc_fn { @@ -313,9 +308,9 @@ impl<'db> Evaluator<'db> { fn exec_lang_item( &mut self, it: LangItem, - generic_args: &Substitution, - args: &[IntervalAndTy], - locals: &Locals, + generic_args: GenericArgs<'db>, + args: &[IntervalAndTy<'db>], + locals: &Locals<'db>, span: MirSpan, ) -> Result<'db, Vec> { use LangItem::*; @@ -328,7 +323,7 @@ impl<'db> Evaluator<'db> { "argument of BeginPanic is not provided".into(), ))? .clone(); - while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) { + while let TyKind::Ref(_, ty, _) = arg.ty.kind() { if ty.is_str() { let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size()); let len = from_bytes!(usize, metadata); @@ -347,13 +342,10 @@ impl<'db> Evaluator<'db> { let pointee = arg.interval.get(self)?; arg = IntervalAndTy { interval: Interval::new(Address::from_bytes(pointee)?, size), - ty: ty.clone(), + ty, }; } - Err(MirEvalError::Panic(format!( - "unknown-panic-payload: {:?}", - arg.ty.kind(Interner) - ))) + Err(MirEvalError::Panic(format!("unknown-panic-payload: {:?}", arg.ty.kind()))) } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( @@ -364,18 +356,17 @@ impl<'db> Evaluator<'db> { Ok(arg[ptr_size..].into()) } DropInPlace => { - let ty = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)).ok_or( - MirEvalError::InternalError( - "generic argument of drop_in_place is not provided".into(), - ), - )?; + let ty = generic_args.as_slice().first().and_then(|it| it.ty()).ok_or( + MirEvalError::InternalError( + "generic argument of drop_in_place is not provided".into(), + ), + )?; let arg = args.next().ok_or(MirEvalError::InternalError( "argument of drop_in_place is not provided".into(), ))?; let arg = arg.interval.get(self)?.to_owned(); self.run_drop_glue_deep( - ty.clone(), + ty, locals, Address::from_bytes(&arg[0..self.ptr_size()])?, &arg[self.ptr_size()..], @@ -390,9 +381,9 @@ impl<'db> Evaluator<'db> { fn exec_syscall( &mut self, id: i64, - args: &[IntervalAndTy], + args: &[IntervalAndTy<'db>], destination: Interval, - _locals: &Locals, + _locals: &Locals<'db>, _span: MirSpan, ) -> Result<'db, ()> { match id { @@ -420,10 +411,10 @@ impl<'db> Evaluator<'db> { fn exec_extern_c( &mut self, as_str: &str, - args: &[IntervalAndTy], - _generic_args: &Substitution, + args: &[IntervalAndTy<'db>], + _generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'db>, span: MirSpan, ) -> Result<'db, ()> { match as_str { @@ -586,14 +577,13 @@ impl<'db> Evaluator<'db> { fn exec_intrinsic( &mut self, name: &str, - args: &[IntervalAndTy], - generic_args: &Substitution, + args: &[IntervalAndTy<'db>], + generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'db>, span: MirSpan, needs_override: bool, ) -> Result<'db, bool> { - let interner = DbInterner::new_with(self.db, None, None); if let Some(name) = name.strip_prefix("atomic_") { return self .exec_atomic_intrinsic(name, args, generic_args, destination, locals, span) @@ -751,9 +741,7 @@ impl<'db> Evaluator<'db> { } match name { "size_of" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "size_of generic arg is not provided".into(), )); @@ -764,20 +752,16 @@ impl<'db> Evaluator<'db> { // FIXME: `min_align_of` was renamed to `align_of` in Rust 1.89 // (https://github.com/rust-lang/rust/pull/142410) "min_align_of" | "align_of" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "align_of generic arg is not provided".into(), )); }; - let align = self.layout(ty.to_nextsolver(interner))?.align.bytes(); + let align = self.layout(ty)?.align.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "size_of_val generic arg is not provided".into(), )); @@ -798,9 +782,7 @@ impl<'db> Evaluator<'db> { // FIXME: `min_align_of_val` was renamed to `align_of_val` in Rust 1.89 // (https://github.com/rust-lang/rust/pull/142410) "min_align_of_val" | "align_of_val" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "align_of_val generic arg is not provided".into(), )); @@ -819,9 +801,7 @@ impl<'db> Evaluator<'db> { } } "type_name" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "type_name generic arg is not provided".into(), )); @@ -848,18 +828,12 @@ impl<'db> Evaluator<'db> { .write_from_bytes(self, &len.to_le_bytes()) } "needs_drop" => { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "size_of generic arg is not provided".into(), )); }; - let result = match has_drop_glue( - &self.infcx, - ty.to_nextsolver(self.interner), - self.trait_env.clone(), - ) { + let result = match has_drop_glue(&self.infcx, ty, self.trait_env.clone()) { DropGlue::HasDropGlue => true, DropGlue::None => false, DropGlue::DependOnParams => { @@ -922,9 +896,7 @@ impl<'db> Evaluator<'db> { let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false)); let ans = lhs.wrapping_sub(rhs); - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "ptr_offset_from generic arg is not provided".into(), )); @@ -1013,13 +985,11 @@ impl<'db> Evaluator<'db> { "const_eval_select args are not provided".into(), )); }; - let result_ty = TyKind::Tuple( - 2, - Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]), - ) - .intern(Interner); - let op_size = - self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?; + let result_ty = Ty::new_tup_from_iter( + self.interner(), + [lhs.ty, Ty::new_bool(self.interner())].into_iter(), + ); + let op_size = self.size_of_sized(lhs.ty, locals, "operand of add_with_overflow")?; let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); let (ans, u128overflow) = match name { @@ -1031,7 +1001,7 @@ impl<'db> Evaluator<'db> { let is_overflow = u128overflow || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255); let is_overflow = vec![u8::from(is_overflow)]; - let layout = self.layout(result_ty.to_nextsolver(interner))?; + let layout = self.layout(result_ty)?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1048,9 +1018,7 @@ impl<'db> Evaluator<'db> { "copy_nonoverlapping args are not provided".into(), )); }; - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "copy_nonoverlapping generic arg is not provided".into(), )); @@ -1069,43 +1037,35 @@ impl<'db> Evaluator<'db> { return Err(MirEvalError::InternalError("offset args are not provided".into())); }; let ty = if name == "offset" { - let Some(ty0) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty0) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "offset generic arg is not provided".into(), )); }; - let Some(ty1) = - generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner)) - else { + let Some(ty1) = generic_args.as_slice().get(1).and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "offset generic arg is not provided".into(), )); }; if !matches!( - ty1.as_builtin(), - Some( - BuiltinType::Int(BuiltinInt::Isize) - | BuiltinType::Uint(BuiltinUint::Usize) - ) + ty1.kind(), + TyKind::Int(rustc_type_ir::IntTy::Isize) + | TyKind::Uint(rustc_type_ir::UintTy::Usize) ) { return Err(MirEvalError::InternalError( "offset generic arg is not usize or isize".into(), )); } - match ty0.as_raw_ptr() { - Some((ty, _)) => ty, - None => { + match ty0.kind() { + TyKind::RawPtr(ty, _) => ty, + _ => { return Err(MirEvalError::InternalError( "offset generic arg is not a raw pointer".into(), )); } } } else { - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "arith_offset generic arg is not provided".into(), )); @@ -1230,9 +1190,7 @@ impl<'db> Evaluator<'db> { "discriminant_value arg is not provided".into(), )); }; - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "discriminant_value generic arg is not provided".into(), )); @@ -1240,7 +1198,7 @@ impl<'db> Evaluator<'db> { let addr = Address::from_bytes(arg.get(self)?)?; let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?; let interval = Interval { addr, size }; - let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?; + let r = self.compute_discriminant(ty, interval.get(self)?)?; destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size]) } "const_eval_select" => { @@ -1250,14 +1208,13 @@ impl<'db> Evaluator<'db> { )); }; let mut args = vec![const_fn.clone()]; - let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else { + let TyKind::Tuple(fields) = tuple.ty.kind() else { return Err(MirEvalError::InternalError( "const_eval_select arg[0] is not a tuple".into(), )); }; - let layout = self.layout(tuple.ty.to_nextsolver(interner))?; - for (i, field) in fields.iter(Interner).enumerate() { - let field = field.assert_ty_ref(Interner).clone(); + let layout = self.layout(tuple.ty)?; + for (i, field) in fields.iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let addr = tuple.interval.addr.offset(offset); args.push(IntervalAndTy::new(addr, field, self, locals)?); @@ -1271,7 +1228,7 @@ impl<'db> Evaluator<'db> { def, &args, // FIXME: wrong for manual impls of `FnOnce` - Substitution::empty(Interner), + GenericArgs::new_from_iter(self.interner(), []), locals, destination, None, @@ -1297,9 +1254,7 @@ impl<'db> Evaluator<'db> { )); }; let dst = Address::from_bytes(ptr.get(self)?)?; - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "write_via_copy generic arg is not provided".into(), )); @@ -1316,9 +1271,7 @@ impl<'db> Evaluator<'db> { }; let count = from_bytes!(usize, count.get(self)?); let val = from_bytes!(u8, val.get(self)?); - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "write_bytes generic arg is not provided".into(), )); @@ -1346,16 +1299,14 @@ impl<'db> Evaluator<'db> { "three_way_compare args are not provided".into(), )); }; - let Some(ty) = - generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "three_way_compare generic arg is not provided".into(), )); }; - let signed = match ty.as_builtin().unwrap() { - BuiltinType::Int(_) => true, - BuiltinType::Uint(_) => false, + let signed = match ty.kind() { + TyKind::Int(_) => true, + TyKind::Uint(_) => false, _ => { return Err(MirEvalError::InternalError( "three_way_compare expects an integral type".into(), @@ -1379,8 +1330,8 @@ impl<'db> Evaluator<'db> { result = (l as i8).cmp(&(r as i8)); } if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) { - let ty = self.db.ty(e.into()).skip_binder().to_chalk(interner); - let r = self.compute_discriminant(ty.clone(), &[result as i8 as u8])?; + let ty = self.db.ty(e.into()).skip_binder(); + let r = self.compute_discriminant(ty, &[result as i8 as u8])?; destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?; Ok(()) } else { @@ -1409,38 +1360,37 @@ impl<'db> Evaluator<'db> { fn size_align_of_unsized( &mut self, - ty: &Ty, + ty: Ty<'db>, metadata: Interval, - locals: &Locals, + locals: &Locals<'db>, ) -> Result<'db, (usize, usize)> { - let interner = DbInterner::new_with(self.db, None, None); - Ok(match ty.kind(Interner) { + Ok(match ty.kind() { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), TyKind::Slice(inner) => { let len = from_bytes!(usize, metadata.get(self)?); let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?; (size * len, align) } - TyKind::Dyn(_) => self.size_align_of_sized( - &convert_ty_for_result(interner, self.vtable_map.ty_of_bytes(metadata.get(self)?)?), + TyKind::Dynamic(..) => self.size_align_of_sized( + self.vtable_map.ty_of_bytes(metadata.get(self)?)?, locals, "dyn concrete type", )?, - TyKind::Adt(id, subst) => { - let id = id.0; - let layout = self.layout_adt(id, subst.clone())?; + TyKind::Adt(adt_def, subst) => { + let id = adt_def.def_id().0; + let layout = self.layout_adt(id, subst)?; let id = match id { AdtId::StructId(s) => s, _ => not_supported!("unsized enum or union"), }; - let field_types = &self.db.field_types(id.into()); + let field_types = self.db.field_types_ns(id.into()); let last_field_ty = - field_types.iter().next_back().unwrap().1.clone().substitute(Interner, subst); + field_types.iter().next_back().unwrap().1.instantiate(self.interner(), subst); let sized_part_size = layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); let sized_part_align = layout.align.bytes() as usize; let (unsized_part_size, unsized_part_align) = - self.size_align_of_unsized(&last_field_ty, metadata, locals)?; + self.size_align_of_unsized(last_field_ty, metadata, locals)?; let align = sized_part_align.max(unsized_part_align) as isize; let size = (sized_part_size + unsized_part_size) as isize; // Must add any necessary padding to `size` @@ -1463,13 +1413,12 @@ impl<'db> Evaluator<'db> { fn exec_atomic_intrinsic( &mut self, name: &str, - args: &[IntervalAndTy], - generic_args: &Substitution, + args: &[IntervalAndTy<'db>], + generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'db>, _span: MirSpan, ) -> Result<'db, ()> { - let interner = DbInterner::new_with(self.db, None, None); // We are a single threaded runtime with no UB checking and no optimization, so // we can implement atomic intrinsics as normal functions. @@ -1479,8 +1428,7 @@ impl<'db> Evaluator<'db> { // The rest of atomic intrinsics have exactly one generic arg - let Some(ty) = generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "atomic intrinsic generic arg is not provided".into(), )); @@ -1562,12 +1510,11 @@ impl<'db> Evaluator<'db> { } else { (arg0_interval, false) }; - let result_ty = TyKind::Tuple( - 2, - Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), - ) - .intern(Interner); - let layout = self.layout(result_ty.to_nextsolver(interner))?; + let result_ty = Ty::new_tup_from_iter( + self.interner(), + [ty, Ty::new_bool(self.interner())].into_iter(), + ); + let layout = self.layout(result_ty)?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index 8e62c764b99bc..ade94b94c0ee5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -2,7 +2,6 @@ use std::cmp::Ordering; -use crate::TyKind; use crate::consteval::try_const_usize; use super::*; @@ -23,22 +22,20 @@ macro_rules! not_supported { } impl<'db> Evaluator<'db> { - fn detect_simd_ty(&self, ty: &Ty) -> Result<'db, (usize, Ty)> { - match ty.kind(Interner) { - TyKind::Adt(id, subst) => { - let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner)) - { + fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> { + match ty.kind() { + TyKind::Adt(adt_def, subst) => { + let len = match subst.as_slice().get(1).and_then(|it| it.konst()) { Some(len) => len, _ => { - if let AdtId::StructId(id) = id.0 { + if let AdtId::StructId(id) = adt_def.def_id().0 { let struct_data = id.fields(self.db); let fields = struct_data.fields(); let Some((first_field, _)) = fields.iter().next() else { not_supported!("simd type with no field"); }; - let field_ty = self.db.field_types(id.into())[first_field] - .clone() - .substitute(Interner, subst); + let field_ty = self.db.field_types_ns(id.into())[first_field] + .instantiate(self.interner(), subst); return Ok((fields.len(), field_ty)); } return Err(MirEvalError::InternalError( @@ -48,14 +45,12 @@ impl<'db> Evaluator<'db> { }; match try_const_usize(self.db, len) { Some(len) => { - let Some(ty) = - subst.as_slice(Interner).first().and_then(|it| it.ty(Interner)) - else { + let Some(ty) = subst.as_slice().first().and_then(|it| it.ty()) else { return Err(MirEvalError::InternalError( "simd type with no ty param".into(), )); }; - Ok((len as usize, ty.clone())) + Ok((len as usize, ty)) } None => Err(MirEvalError::InternalError( "simd type with unevaluatable len param".into(), @@ -69,10 +64,10 @@ impl<'db> Evaluator<'db> { pub(super) fn exec_simd_intrinsic( &mut self, name: &str, - args: &[IntervalAndTy], - _generic_args: &Substitution, + args: &[IntervalAndTy<'db>], + _generic_args: GenericArgs<'db>, destination: Interval, - _locals: &Locals, + _locals: &Locals<'db>, _span: MirSpan, ) -> Result<'db, ()> { match name { @@ -99,8 +94,8 @@ impl<'db> Evaluator<'db> { let [left, right] = args else { return Err(MirEvalError::InternalError("simd args are not provided".into())); }; - let (len, ty) = self.detect_simd_ty(&left.ty)?; - let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); + let (len, ty) = self.detect_simd_ty(left.ty)?; + let is_signed = matches!(ty.kind(), TyKind::Int(_)); let size = left.interval.size / len; let dest_size = destination.size / len; let mut destination_bytes = vec![]; @@ -137,7 +132,7 @@ impl<'db> Evaluator<'db> { "simd_bitmask args are not provided".into(), )); }; - let (op_len, _) = self.detect_simd_ty(&op.ty)?; + let (op_len, _) = self.detect_simd_ty(op.ty)?; let op_count = op.interval.size / op_len; let mut result: u64 = 0; for (i, val) in op.get(self)?.chunks(op_count).enumerate() { @@ -153,7 +148,7 @@ impl<'db> Evaluator<'db> { "simd_shuffle args are not provided".into(), )); }; - let TyKind::Array(_, index_len) = index.ty.kind(Interner) else { + let TyKind::Array(_, index_len) = index.ty.kind() else { return Err(MirEvalError::InternalError( "simd_shuffle index argument has non-array type".into(), )); @@ -166,7 +161,7 @@ impl<'db> Evaluator<'db> { )); } }; - let (left_len, _) = self.detect_simd_ty(&left.ty)?; + let (left_len, _) = self.detect_simd_ty(left.ty)?; let left_size = left.interval.size / left_len; let vector = left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 82c0a8070c41a..4eb4aa91598e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -4,15 +4,20 @@ use span::Edition; use syntax::{TextRange, TextSize}; use test_fixture::WithFixture; -use crate::display::DisplayTarget; use crate::{ - Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB, + db::HirDatabase, + display::DisplayTarget, + mir::MirLowerError, + next_solver::{DbInterner, GenericArgs}, + setup_tracing, + test_db::TestDB, }; use super::{MirEvalError, interpret_mir}; fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError<'_>> { crate::attach_db(db, || { + let interner = DbInterner::new_with(db, None, None); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; @@ -34,7 +39,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), let body = db .monomorphized_mir_body( func_id.into(), - Substitution::empty(Interner), + GenericArgs::new_from_iter(interner, []), db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; @@ -631,11 +636,16 @@ fn main() { ); } +#[ignore = " +FIXME(next-solver): +This does not work currently because I replaced homemade selection with selection by the trait solver; +This will work once we implement `Interner::impl_specializes()` properly. +"] #[test] fn specialization_array_clone() { check_pass( r#" -//- minicore: copy, derive, slice, index, coerce_unsized +//- minicore: copy, derive, slice, index, coerce_unsized, panic impl Clone for [T; N] { #[inline] fn clone(&self) -> Self { @@ -650,8 +660,7 @@ trait SpecArrayClone: Clone { impl SpecArrayClone for T { #[inline] default fn clone(array: &[T; N]) -> [T; N] { - // FIXME: panic here when we actually implement specialization. - from_slice(array) + panic!("should go to the specialized impl") } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index ec7bff708297d..92f9cd42615ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -3,10 +3,9 @@ use std::{fmt::Write, iter, mem}; use base_db::Crate; -use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, - Lookup, TraitId, TupleId, TypeOrConstParamId, + AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, GenericParamId, HasModule, + ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, @@ -20,37 +19,33 @@ use hir_expand::name::Name; use la_arena::ArenaMap; use rustc_apfloat::Float; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::{Const as _, IntoKind, SliceLike, Ty as _}; use span::{Edition, FileId}; use syntax::TextRange; use triomphe::Arc; use crate::{ - Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt, + Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, consteval::ConstEvalError, db::{HirDatabase, InternedClosure, InternedClosureId}, display::{DisplayTarget, HirDisplay, hir_display_with_store}, - error_lifetime, generics::generics, infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, - mapping::ToChalk, mir::{ - AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, ConstScalar, - Either, Expr, FieldId, Idx, InferenceResult, Interner, Local, LocalId, MemoryMap, MirBody, - MirSpan, Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, - ProjectionStore, RawIdx, Rvalue, Statement, StatementKind, Substitution, SwitchTargets, - Terminator, TerminatorKind, TupleFieldId, Ty, UnOp, VariantId, intern_const_scalar, - return_slot, + AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, + FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, + Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, ProjectionStore, + RawIdx, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, + TupleFieldId, Ty, UnOp, VariantId, return_slot, }, next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + Const, DbInterner, ParamConst, Region, TyKind, TypingMode, UnevaluatedConst, + infer::{DbInternerInferExt, InferCtxt}, + mapping::NextSolverToChalk, }, - static_lifetime, traits::FnTrait, - utils::ClosureSubst, }; use super::OperandKind; @@ -61,33 +56,33 @@ mod pattern_matching; mod tests; #[derive(Debug, Clone)] -struct LoopBlocks { - begin: BasicBlockId, +struct LoopBlocks<'db> { + begin: BasicBlockId<'db>, /// `None` for loops that are not terminating - end: Option, - place: Place, + end: Option>, + place: Place<'db>, drop_scope_index: usize, } #[derive(Debug, Clone, Default)] -struct DropScope { +struct DropScope<'db> { /// locals, in order of definition (so we should run drop glues in reverse order) - locals: Vec, + locals: Vec>, } struct MirLowerCtx<'a, 'db> { - result: MirBody, + result: MirBody<'db>, owner: DefWithBodyId, - current_loop_blocks: Option, - labeled_loop_blocks: FxHashMap, - discr_temp: Option, + current_loop_blocks: Option>, + labeled_loop_blocks: FxHashMap>, + discr_temp: Option>, db: &'db dyn HirDatabase, body: &'a Body, infer: &'a InferenceResult<'db>, resolver: Resolver<'db>, - drop_scopes: Vec, + drop_scopes: Vec>, env: Arc>, - interner: DbInterner<'db>, + infcx: InferCtxt<'db>, } // FIXME: Make this smaller, its stored in database queries @@ -103,7 +98,7 @@ pub enum MirLowerError<'db> { RecordLiteralWithoutPath, UnresolvedMethod(String), UnresolvedField, - UnsizedTemporary(Ty), + UnsizedTemporary(Ty<'db>), MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch<'db>), HasErrors, @@ -118,22 +113,22 @@ pub enum MirLowerError<'db> { LangItemNotFound(LangItem), MutatingRvalue, UnresolvedLabel, - UnresolvedUpvar(Place), + UnresolvedUpvar(Place<'db>), InaccessibleLocal, // monomorphization errors: - GenericArgNotProvided(TypeOrConstParamId, Substitution), + GenericArgNotProvided(GenericParamId, GenericArgs<'db>), } /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. struct DropScopeToken; impl DropScopeToken { - fn pop_and_drop( + fn pop_and_drop<'db>( self, - ctx: &mut MirLowerCtx<'_, '_>, - current: BasicBlockId, + ctx: &mut MirLowerCtx<'_, 'db>, + current: BasicBlockId<'db>, span: MirSpan, - ) -> BasicBlockId { + ) -> BasicBlockId<'db> { std::mem::forget(self); ctx.pop_drop_scope_internal(current, span) } @@ -199,15 +194,24 @@ impl MirLowerError<'_> { e.actual.display(db, display_target), )?, MirLowerError::GenericArgNotProvided(id, subst) => { - let parent = id.parent; - let param = &db.generic_params(parent)[id.local_id]; + let param_name = match *id { + GenericParamId::TypeParamId(id) => { + db.generic_params(id.parent())[id.local_id()].name().cloned() + } + GenericParamId::ConstParamId(id) => { + db.generic_params(id.parent())[id.local_id()].name().cloned() + } + GenericParamId::LifetimeParamId(id) => { + Some(db.generic_params(id.parent)[id.local_id].name.clone()) + } + }; writeln!( f, "Generic arg not provided for {}", - param.name().unwrap_or(&Name::missing()).display(db, display_target.edition) + param_name.unwrap_or(Name::missing()).display(db, display_target.edition) )?; writeln!(f, "Provided args: [")?; - for g in subst.iter(Interner) { + for g in subst.iter() { write!(f, " {},", g.display(db, display_target))?; } writeln!(f, "]")?; @@ -285,7 +289,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { is_cleanup: false, }); let locals = Arena::new(); - let binding_locals: ArenaMap = ArenaMap::new(); + let binding_locals: ArenaMap> = ArenaMap::new(); let mir = MirBody { projection_store: ProjectionStore::default(), basic_blocks, @@ -299,6 +303,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let resolver = owner.resolver(db); let env = db.trait_environment_for_body(owner); let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); MirLowerCtx { result: mir, @@ -312,12 +317,22 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { discr_temp: None, drop_scopes: vec![DropScope::default()], env, - interner, + infcx, } } - fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<'db, LocalId> { - if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn temp( + &mut self, + ty: Ty<'db>, + current: BasicBlockId<'db>, + span: MirSpan, + ) -> Result<'db, LocalId<'db>> { + if matches!(ty.kind(), TyKind::Slice(_) | TyKind::Dynamic(..)) { return Err(MirLowerError::UnsizedTemporary(ty)); } let l = self.result.locals.alloc(Local { ty }); @@ -328,8 +343,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_some_operand( &mut self, expr_id: ExprId, - current: BasicBlockId, - ) -> Result<'db, Option<(Operand, BasicBlockId)>> { + current: BasicBlockId<'db>, + ) -> Result<'db, Option<(Operand<'db>, BasicBlockId<'db>)>> { if !self.has_adjustments(expr_id) && let Expr::Literal(l) = &self.body[expr_id] { @@ -345,15 +360,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: Place, - current: BasicBlockId, + place: Place<'db>, + current: BasicBlockId<'db>, adjustments: &[Adjustment<'db>], - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { match adjustments.split_last() { Some((last, rest)) => match &last.kind { Adjust::NeverToAny => { - let temp = - self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?; + let temp = self.temp( + Ty::new(self.interner(), TyKind::Never), + current, + MirSpan::Unknown, + )?; self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) } Adjust::Deref(_) => { @@ -392,7 +410,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Rvalue::Cast( CastKind::PointerCoercion(*cast), Operand { kind: OperandKind::Copy(p), span: None }, - last.target.to_chalk(self.interner), + last.target, ), expr_id.into(), ); @@ -406,9 +424,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: Place, - prev_block: BasicBlockId, - ) -> Result<'db, Option> { + place: Place<'db>, + prev_block: BasicBlockId<'db>, + ) -> Result<'db, Option>> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments); } @@ -418,9 +436,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: Place, - mut current: BasicBlockId, - ) -> Result<'db, Option> { + place: Place<'db>, + mut current: BasicBlockId<'db>, + ) -> Result<'db, Option>> { match &self.body[expr_id] { Expr::OffsetOf(_) => { not_supported!("builtin#offset_of") @@ -443,14 +461,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) { match assoc { hir_def::AssocItemId::ConstId(c) => { - self.lower_const( - c.into(), - current, - place, - subst.to_chalk(self.interner), - expr_id.into(), - self.expr_ty_without_adjust(expr_id), - )?; + self.lower_const(c.into(), current, place, subst, expr_id.into())?; return Ok(Some(current)); } hir_def::AssocItemId::FunctionId(_) => { @@ -506,9 +517,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { const_id.into(), current, place, - Substitution::empty(Interner), + GenericArgs::new_from_iter(self.interner(), []), expr_id.into(), - self.expr_ty_without_adjust(expr_id), )?; Ok(Some(current)) } @@ -520,7 +530,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { variant_id, current, place, - ty.to_chalk(self.interner), + ty, Box::new([]), expr_id.into(), )?; @@ -533,25 +543,21 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { not_supported!("owner without generic def id"); }; let generics = generics(self.db, def); - let ty = self.expr_ty_without_adjust(expr_id); + let index = generics + .type_or_const_param_idx(p.into()) + .ok_or(MirLowerError::TypeError("fail to lower const generic param"))? + as u32; self.push_assignment( current, place, Rvalue::from(Operand { - kind: OperandKind::Constant( - ConstData { - ty, - value: chalk_ir::ConstValue::BoundVar(BoundVar::new( - DebruijnIndex::INNERMOST, - generics.type_or_const_param_idx(p.into()).ok_or( - MirLowerError::TypeError( - "fail to lower const generic param", - ), - )?, - )), - } - .intern(Interner), - ), + kind: OperandKind::Constant { + konst: Const::new_param( + self.interner(), + ParamConst { id: p, index }, + ), + ty: self.db.const_param_ty_ns(p), + }, span: None, }), expr_id.into(), @@ -603,7 +609,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { then_target, place, Box::new([1]), - TyBuilder::bool(), + Ty::new_bool(self.interner()), MirSpan::Unknown, )?; if let Some(else_target) = else_target { @@ -611,7 +617,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else_target, place, Box::new([0]), - TyBuilder::bool(), + Ty::new_bool(self.interner()), MirSpan::Unknown, )?; } @@ -653,11 +659,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Call { callee, args, .. } => { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { - let ty = chalk_ir::TyKind::FnDef( - CallableDefId::FunctionId(func_id).to_chalk(self.db), - generic_args.to_chalk(self.interner), - ) - .intern(Interner); + let ty = Ty::new_fn_def( + self.interner(), + CallableDefId::FunctionId(func_id).into(), + generic_args, + ); let func = Operand::from_bytes(Box::default(), ty); return self.lower_call_and_args( func, @@ -669,9 +675,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ); } let callee_ty = self.expr_ty_after_adjustments(*callee); - match &callee_ty.kind(Interner) { - chalk_ir::TyKind::FnDef(..) => { - let func = Operand::from_bytes(Box::default(), callee_ty.clone()); + match callee_ty.kind() { + TyKind::FnDef(..) => { + let func = Operand::from_bytes(Box::default(), callee_ty); self.lower_call_and_args( func, args.iter().copied(), @@ -681,7 +687,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { expr_id.into(), ) } - chalk_ir::TyKind::Function(_) => { + TyKind::FnPtr(..) => { let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { @@ -701,7 +707,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { "method resolution not emitted for closure (Are Fn traits available?)" ); } - TyKind::Error => { + TyKind::Error(_) => { Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)) } _ => Err(MirLowerError::TypeError("function call on bad type")), @@ -714,7 +720,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { method_name.display(self.db, self.edition()).to_string(), ) })?; - let func = Operand::from_fn(self.db, func_id, generic_args.to_chalk(self.interner)); + let func = Operand::from_fn(self.db, func_id, generic_args); self.lower_call_and_args( func, iter::once(*receiver).chain(args.iter().copied()), @@ -862,8 +868,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ), None => MirLowerError::RecordLiteralWithoutPath, })?; - let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) { - TyKind::Adt(_, s) => s.clone(), + let subst = match self.expr_ty_without_adjust(expr_id).kind() { + TyKind::Adt(_, s) => s, _ => not_supported!("Non ADT record literal"), }; let variant_fields = variant_id.fields(self.db); @@ -952,12 +958,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let rvalue = if self.infer.coercion_casts.contains(expr) { Rvalue::Use(it) } else { - let source_ty = self.infer[*expr].to_chalk(self.interner); - let target_ty = self.infer[expr_id].to_chalk(self.interner); + let source_ty = self.infer[*expr]; + let target_ty = self.infer[expr_id]; let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { - cast_kind(self.db, &source_ty, &target_ty)? + cast_kind(self.db, source_ty, target_ty)? }; Rvalue::Cast(cast_kind, it, target_ty) @@ -1034,8 +1040,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let lhs_ty = self.expr_ty_without_adjust(*lhs); let rhs_ty = self.expr_ty_without_adjust(*rhs); if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) - && lhs_ty.as_raw_ptr().is_some() - && rhs_ty.as_raw_ptr().is_some() + && matches!(lhs_ty.kind(), TyKind::RawPtr(..)) + && matches!(rhs_ty.kind(), TyKind::RawPtr(..)) { break 'b true; } @@ -1044,15 +1050,26 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } ); - lhs_ty.is_scalar() - && rhs_ty.is_scalar() - && (lhs_ty == rhs_ty || builtin_inequal_impls) + matches!( + lhs_ty.kind(), + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + ) && matches!( + rhs_ty.kind(), + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + ) && (lhs_ty == rhs_ty || builtin_inequal_impls) }; if !is_builtin && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { - let func = - Operand::from_fn(self.db, func_id, generic_args.to_chalk(self.interner)); + let func = Operand::from_fn(self.db, func_id, generic_args); return self.lower_call_and_args( func, [*lhs, *rhs].into_iter(), @@ -1185,7 +1202,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { current, place, Rvalue::Aggregate( - AggregateKind::Adt(st.into(), subst.clone()), + AggregateKind::Adt(st.into(), subst), st.fields(self.db) .fields() .iter() @@ -1193,9 +1210,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let o = match it.1.name.as_str() { "start" => lp.take(), "end" => rp.take(), - "exhausted" => { - Some(Operand::from_bytes(Box::new([0]), TyBuilder::bool())) - } + "exhausted" => Some(Operand::from_bytes( + Box::new([0]), + Ty::new_bool(self.interner()), + )), _ => None, }; o.ok_or(MirLowerError::UnresolvedField) @@ -1208,11 +1226,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Closure { .. } => { let ty = self.expr_ty_without_adjust(expr_id); - let TyKind::Closure(id, _) = ty.kind(Interner) else { + let TyKind::Closure(id, _) = ty.kind() else { not_supported!("closure with non closure type"); }; - self.result.closures.push(*id); - let (captures, _) = self.infer.closure_info((*id).into()); + self.result.closures.push(id.0); + let (captures, _) = self.infer.closure_info(id.0); let mut operands = vec![]; for capture in captures.iter() { let p = Place { @@ -1236,7 +1254,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ProjectionElem::Subslice { from, to } } ProjectionElem::OpaqueCast(it) => { - ProjectionElem::OpaqueCast(it.to_chalk(self.interner)) + ProjectionElem::OpaqueCast(it) } #[allow(unreachable_patterns)] ProjectionElem::Index(it) => match it {}, @@ -1246,15 +1264,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; match &capture.kind { CaptureKind::ByRef(bk) => { - let placeholder_subst: crate::next_solver::GenericArgs<'db> = - self.placeholder_subst().to_nextsolver(self.interner); - let tmp_ty = capture - .ty - .instantiate(self.interner, placeholder_subst) - .to_chalk(self.interner); + let tmp_ty = capture.ty.instantiate_identity(); // FIXME: Handle more than one span. let capture_spans = capture.spans(); - let tmp: Place = self.temp(tmp_ty, current, capture_spans[0])?.into(); + let tmp: Place<'db> = + self.temp(tmp_ty, current, capture_spans[0])?.into(); self.push_assignment( current, tmp, @@ -1299,8 +1313,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Array(l) => match l { Array::ElementList { elements, .. } => { - let elem_ty = match &self.expr_ty_without_adjust(expr_id).kind(Interner) { - TyKind::Array(ty, _) => ty.clone(), + let elem_ty = match self.expr_ty_without_adjust(expr_id).kind() { + TyKind::Array(ty, _) => ty, _ => { return Err(MirLowerError::TypeError( "Array expression with non array type", @@ -1331,8 +1345,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { return Ok(None); }; - let len = match &self.expr_ty_without_adjust(expr_id).kind(Interner) { - TyKind::Array(_, len) => len.clone(), + let len = match self.expr_ty_without_adjust(expr_id).kind() { + TyKind::Array(_, len) => len, _ => { return Err(MirLowerError::TypeError( "Array repeat expression with non array type", @@ -1354,16 +1368,13 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn placeholder_subst(&mut self) -> Substitution { - match self.owner.as_generic_def_id(self.db) { - Some(it) => TyBuilder::placeholder_subst(self.db, it), - None => Substitution::empty(Interner), - } - } - - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { + fn push_field_projection( + &mut self, + place: &mut Place<'db>, + expr_id: ExprId, + ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.body[expr_id] { - if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { + if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; @@ -1386,7 +1397,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(()) } - fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<'db, Operand> { + fn lower_literal_or_const_to_operand( + &mut self, + ty: Ty<'db>, + loc: &ExprId, + ) -> Result<'db, Operand<'db>> { match &self.body[*loc] { Expr::Literal(l) => self.lower_literal_to_operand(ty, l), Expr::Path(c) => { @@ -1407,7 +1422,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { match pr { ResolveValueResult::ValueNs(v, _) => { if let ValueNs::ConstId(c) = v { - self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) + self.lower_const_to_operand( + GenericArgs::new_from_iter(self.interner(), []), + c.into(), + ) } else { not_supported!("bad path in range pattern"); } @@ -1423,13 +1441,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<'db, Operand> { - let interner = DbInterner::new_with(self.db, None, None); - let size = || { - self.db - .layout_of_ty(ty.to_nextsolver(interner), self.env.clone()) - .map(|it| it.size.bytes_usize()) - }; + fn lower_literal_to_operand(&mut self, ty: Ty<'db>, l: &Literal) -> Result<'db, Operand<'db>> { + let size = || self.db.layout_of_ty(ty, self.env.clone()).map(|it| it.size.bytes_usize()); const USIZE_SIZE: usize = size_of::(); let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { @@ -1475,48 +1488,57 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty)) } - fn new_basic_block(&mut self) -> BasicBlockId { + fn new_basic_block(&mut self) -> BasicBlockId<'db> { self.result.basic_blocks.alloc(BasicBlock::default()) } fn lower_const( &mut self, const_id: GeneralConstId, - prev_block: BasicBlockId, - place: Place, - subst: Substitution, + prev_block: BasicBlockId<'db>, + place: Place<'db>, + subst: GenericArgs<'db>, span: MirSpan, - ty: Ty, ) -> Result<'db, ()> { - let c = self.lower_const_to_operand(subst, const_id, ty)?; + let c = self.lower_const_to_operand(subst, const_id)?; self.push_assignment(prev_block, place, c.into(), span); Ok(()) } fn lower_const_to_operand( &mut self, - subst: Substitution, + subst: GenericArgs<'db>, const_id: GeneralConstId, - ty: Ty, - ) -> Result<'db, Operand> { - let c = if subst.len(Interner) != 0 { + ) -> Result<'db, Operand<'db>> { + let konst = if subst.len() != 0 { // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. - intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) + Const::new_unevaluated( + self.interner(), + UnevaluatedConst { def: const_id.into(), args: subst }, + ) } else { let name = const_id.name(self.db); self.db .const_eval(const_id, subst, None) .map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))? }; - Ok(Operand { kind: OperandKind::Constant(c), span: None }) + let ty = self + .db + .value_ty(match const_id { + GeneralConstId::ConstId(id) => id.into(), + GeneralConstId::StaticId(id) => id.into(), + }) + .unwrap() + .instantiate(self.interner(), subst); + Ok(Operand { kind: OperandKind::Constant { konst, ty }, span: None }) } fn write_bytes_to_place( &mut self, - prev_block: BasicBlockId, - place: Place, + prev_block: BasicBlockId<'db>, + place: Place<'db>, cv: Box<[u8]>, - ty: Ty, + ty: Ty<'db>, span: MirSpan, ) -> Result<'db, ()> { self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span); @@ -1526,14 +1548,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_enum_variant( &mut self, variant_id: EnumVariantId, - prev_block: BasicBlockId, - place: Place, - ty: Ty, - fields: Box<[Operand]>, + prev_block: BasicBlockId<'db>, + place: Place<'db>, + ty: Ty<'db>, + fields: Box<[Operand<'db>]>, span: MirSpan, - ) -> Result<'db, BasicBlockId> { - let subst = match ty.kind(Interner) { - TyKind::Adt(_, subst) => subst.clone(), + ) -> Result<'db, BasicBlockId<'db>> { + let subst = match ty.kind() { + TyKind::Adt(_, subst) => subst, _ => implementation_error!("Non ADT enum"), }; self.push_assignment( @@ -1547,13 +1569,13 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_call_and_args( &mut self, - func: Operand, + func: Operand<'db>, args: impl Iterator, - place: Place, - mut current: BasicBlockId, + place: Place<'db>, + mut current: BasicBlockId<'db>, is_uninhabited: bool, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let Some(args) = args .map(|arg| { if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { @@ -1572,13 +1594,13 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_call( &mut self, - func: Operand, - args: Box<[Operand]>, - place: Place, - current: BasicBlockId, + func: Operand<'db>, + args: Box<[Operand<'db>]>, + place: Place<'db>, + current: BasicBlockId<'db>, is_uninhabited: bool, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; self.set_terminator( current, @@ -1595,56 +1617,63 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(b) } - fn is_unterminated(&mut self, source: BasicBlockId) -> bool { + fn is_unterminated(&mut self, source: BasicBlockId<'db>) -> bool { self.result.basic_blocks[source].terminator.is_none() } - fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) { + fn set_terminator( + &mut self, + source: BasicBlockId<'db>, + terminator: TerminatorKind<'db>, + span: MirSpan, + ) { self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator }); } - fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) { + fn set_goto(&mut self, source: BasicBlockId<'db>, target: BasicBlockId<'db>, span: MirSpan) { self.set_terminator(source, TerminatorKind::Goto { target }, span); } - fn expr_ty_without_adjust(&self, e: ExprId) -> Ty { - self.infer[e].to_chalk(self.interner) + fn expr_ty_without_adjust(&self, e: ExprId) -> Ty<'db> { + self.infer[e] } - fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> { let mut ty = None; if let Some(it) = self.infer.expr_adjustments.get(&e) && let Some(it) = it.last() { - ty = Some(it.target.to_chalk(self.interner)); + ty = Some(it.target); } ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) } - fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { + fn push_statement(&mut self, block: BasicBlockId<'db>, statement: Statement<'db>) { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { + fn push_fake_read(&mut self, block: BasicBlockId<'db>, p: Place<'db>, span: MirSpan) { self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); } fn push_assignment( &mut self, - block: BasicBlockId, - place: Place, - rvalue: Rvalue, + block: BasicBlockId<'db>, + place: Place<'db>, + rvalue: Rvalue<'db>, span: MirSpan, ) { self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId<'db>) -> Place<'db> { match &self.discr_temp { Some(it) => *it, None => { - let tmp: Place = self - .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) + // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let tmp: Place<'db> = self + .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); self.discr_temp = Some(tmp); @@ -1655,12 +1684,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, - prev_block: BasicBlockId, - place: Place, + prev_block: BasicBlockId<'db>, + place: Place<'db>, label: Option, span: MirSpan, - f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, - ) -> Result<'db, Option> { + f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId<'db>) -> Result<'db, ()>, + ) -> Result<'db, Option>> { let begin = self.new_basic_block(); let prev = self.current_loop_blocks.replace(LoopBlocks { begin, @@ -1695,10 +1724,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn merge_blocks( &mut self, - b1: Option, - b2: Option, + b1: Option>, + b2: Option>, span: MirSpan, - ) -> Option { + ) -> Option> { match (b1, b2) { (None, None) => None, (None, Some(b)) | (Some(b), None) => Some(b), @@ -1711,7 +1740,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn current_loop_end(&mut self) -> Result<'db, BasicBlockId> { + fn current_loop_end(&mut self) -> Result<'db, BasicBlockId<'db>> { let r = match self .current_loop_blocks .as_mut() @@ -1738,7 +1767,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn is_uninhabited(&self, expr_id: ExprId) -> bool { is_ty_uninhabited_from( self.db, - &self.infer[expr_id].to_chalk(self.interner), + &self.infer[expr_id].to_chalk(self.interner()), self.owner.module(self.db), self.env.clone(), ) @@ -1746,15 +1775,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and /// `Drop` in the appropriated places. - fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<'db, ()> { + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId<'db>) -> Result<'db, ()> { let l = self.binding_local(b)?; self.push_storage_live_for_local(l, current, MirSpan::BindingId(b)) } fn push_storage_live_for_local( &mut self, - l: LocalId, - current: BasicBlockId, + l: LocalId<'db>, + current: BasicBlockId<'db>, span: MirSpan, ) -> Result<'db, ()> { self.drop_scopes.last_mut().unwrap().locals.push(l); @@ -1770,11 +1799,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_block_to_place( &mut self, statements: &[hir_def::hir::Statement], - mut current: BasicBlockId, + mut current: BasicBlockId<'db>, tail: Option, - place: Place, + place: Place<'db>, span: MirSpan, - ) -> Result<'db, Option>> { + ) -> Result<'db, Option>>> { let scope = self.push_drop_scope(); for statement in statements.iter() { match statement { @@ -1847,10 +1876,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_params_and_bindings( &mut self, - params: impl Iterator + Clone, - self_binding: Option<(BindingId, Ty)>, + params: impl Iterator)> + Clone, + self_binding: Option<(BindingId, Ty<'db>)>, pick_binding: impl Fn(BindingId) -> bool, - ) -> Result<'db, BasicBlockId> { + ) -> Result<'db, BasicBlockId<'db>> { let base_param_count = self.result.param_locals.len(); let self_binding = match self_binding { Some((self_binding, ty)) => { @@ -1881,10 +1910,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { continue; } if !self.result.binding_locals.contains_idx(id) { - self.result.binding_locals.insert( - id, - self.result.locals.alloc(Local { ty: self.infer[id].to_chalk(self.interner) }), - ); + self.result + .binding_locals + .insert(id, self.result.locals.alloc(Local { ty: self.infer[id] })); } } let mut current = self.result.start_block; @@ -1919,7 +1947,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(current) } - fn binding_local(&self, b: BindingId) -> Result<'db, LocalId> { + fn binding_local(&self, b: BindingId) -> Result<'db, LocalId<'db>> { match self.result.binding_locals.get(b) { Some(it) => Ok(*it), None => { @@ -1968,9 +1996,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn drop_until_scope( &mut self, scope_index: usize, - mut current: BasicBlockId, + mut current: BasicBlockId<'db>, span: MirSpan, - ) -> BasicBlockId { + ) -> BasicBlockId<'db> { for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span); } @@ -1990,9 +2018,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { /// Don't call directly fn pop_drop_scope_internal( &mut self, - mut current: BasicBlockId, + mut current: BasicBlockId<'db>, span: MirSpan, - ) -> BasicBlockId { + ) -> BasicBlockId<'db> { let scope = self.drop_scopes.pop().unwrap(); self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span); current @@ -2000,9 +2028,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn pop_drop_scope_assert_finished( &mut self, - mut current: BasicBlockId, + mut current: BasicBlockId<'db>, span: MirSpan, - ) -> Result<'db, BasicBlockId> { + ) -> Result<'db, BasicBlockId<'db>> { current = self.pop_drop_scope_internal(current, span); if !self.drop_scopes.is_empty() { implementation_error!("Mismatched count between drop scope push and pops"); @@ -2012,12 +2040,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn emit_drop_and_storage_dead_for_scope( &mut self, - scope: &DropScope, - current: &mut Idx, + scope: &DropScope<'db>, + current: &mut Idx>, span: MirSpan, ) { for &l in scope.locals.iter().rev() { - if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { + if !self.infcx.type_is_copy_modulo_regions(self.env.env, self.result.locals[l].ty) { let prev = std::mem::replace(current, self.new_basic_block()); self.set_terminator( prev, @@ -2032,12 +2060,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn cast_kind<'db>( db: &'db dyn HirDatabase, - source_ty: &Ty, - target_ty: &Ty, + source_ty: Ty<'db>, + target_ty: Ty<'db>, ) -> Result<'db, CastKind> { - let interner = DbInterner::new_with(db, None, None); - let from = CastTy::from_ty(db, source_ty.to_nextsolver(interner)); - let cast = CastTy::from_ty(db, target_ty.to_nextsolver(interner)); + let from = CastTy::from_ty(db, source_ty); + let cast = CastTy::from_ty(db, target_ty); Ok(match (from, cast) { (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { CastKind::PointerExposeAddress @@ -2056,7 +2083,7 @@ fn cast_kind<'db>( pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, -) -> Result<'db, Arc> { +) -> Result<'db, Arc>> { let InternedClosure(owner, expr) = db.lookup_intern_closure(closure); let body = db.body(owner); let infer = db.infer(owner); @@ -2068,29 +2095,34 @@ pub fn mir_body_for_closure_query<'db>( }; let (captures, kind) = infer.closure_info(closure); let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); - let substs: &Substitution = &substs.to_chalk(ctx.interner); // 0 is return local - ctx.result.locals.alloc(Local { ty: infer[*root].to_chalk(ctx.interner) }); + ctx.result.locals.alloc(Local { ty: infer[*root] }); let closure_local = ctx.result.locals.alloc(Local { ty: match kind { - FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer[expr].to_chalk(ctx.interner), - FnTrait::FnMut | FnTrait::AsyncFnMut => { - TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].to_chalk(ctx.interner)) - .intern(Interner) - } - FnTrait::Fn | FnTrait::AsyncFn => { - TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].to_chalk(ctx.interner)) - .intern(Interner) - } + FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer[expr], + FnTrait::FnMut | FnTrait::AsyncFnMut => Ty::new_ref( + ctx.interner(), + Region::error(ctx.interner()), + infer[expr], + Mutability::Mut, + ), + FnTrait::Fn | FnTrait::AsyncFn => Ty::new_ref( + ctx.interner(), + Region::error(ctx.interner()), + infer[expr], + Mutability::Not, + ), }, }); ctx.result.param_locals.push(closure_local); - let Some(sig) = ClosureSubst(substs).sig_ty(db).callable_sig(db) else { + let Some(sig) = + substs.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.callable_sig(ctx.interner()) + else { implementation_error!("closure has not callable sig"); }; let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr); let current = ctx.lower_params_and_bindings( - args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())), + args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, y)), None, |_| true, )?; @@ -2099,7 +2131,8 @@ pub fn mir_body_for_closure_query<'db>( let current = ctx.pop_drop_scope_assert_finished(current, root.into())?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } - let mut upvar_map: FxHashMap, usize)>> = FxHashMap::default(); + let mut upvar_map: FxHashMap, Vec<(&CapturedItem<'_>, usize)>> = + FxHashMap::default(); for (i, capture) in captures.iter().enumerate() { let local = ctx.binding_local(capture.place.local)?; upvar_map.entry(local).or_default().push((capture, i)); @@ -2164,7 +2197,7 @@ pub fn mir_body_for_closure_query<'db>( pub fn mir_body_query<'db>( db: &'db dyn HirDatabase, def: DefWithBodyId, -) -> Result<'db, Arc> { +) -> Result<'db, Arc>> { let krate = def.krate(db); let edition = krate.data(db).edition; let detail = match def { @@ -2200,7 +2233,7 @@ pub fn mir_body_query<'db>( pub(crate) fn mir_body_cycle_result<'db>( _db: &'db dyn HirDatabase, _def: DefWithBodyId, -) -> Result<'db, Arc> { +) -> Result<'db, Arc>> { Err(MirLowerError::Loop) } @@ -2212,7 +2245,7 @@ pub fn lower_to_mir<'db>( // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we // need to take this input explicitly. root_expr: ExprId, -) -> Result<'db, MirBody> { +) -> Result<'db, MirBody<'db>> { if infer.type_mismatches().next().is_some() || infer.is_erroneous() { return Err(MirLowerError::HasErrors); } @@ -2229,18 +2262,12 @@ pub fn lower_to_mir<'db>( if body.body_expr == root_expr { // otherwise it's an inline const, and has no parameter if let DefWithBodyId::FunctionId(fid) = owner { - let substs = TyBuilder::placeholder_subst(db, fid); - let interner = DbInterner::new_with(db, None, None); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let callable_sig = db - .callable_item_signature(fid.into()) - .instantiate(interner, args) - .skip_binder() - .to_chalk(interner); - let mut params = callable_sig.params().iter(); - let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone()))); + let callable_sig = + db.callable_item_signature(fid.into()).instantiate_identity().skip_binder(); + let mut params = callable_sig.inputs().iter(); + let self_param = body.self_param.and_then(|id| Some((id, params.next()?))); break 'b ctx.lower_params_and_bindings( - body.params.iter().zip(params).map(|(it, y)| (*it, y.clone())), + body.params.iter().zip(params).map(|(it, y)| (*it, y)), self_param, binding_picker, )?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index bd7b644618e7a..52f1412a06bf4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,10 +1,14 @@ //! MIR lowering for places -use crate::mir::{MutBorrowKind, Operand, OperandKind}; - -use super::*; use hir_def::FunctionId; use intern::sym; +use rustc_type_ir::inherent::{AdtDef, Region as _, Ty as _}; + +use super::*; +use crate::{ + mir::{MutBorrowKind, Operand, OperandKind}, + next_solver::Region, +}; macro_rules! not_supported { ($it: expr) => { @@ -16,8 +20,8 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_expr_to_some_place_without_adjust( &mut self, expr_id: ExprId, - prev_block: BasicBlockId, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + prev_block: BasicBlockId<'db>, + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { let ty = self.expr_ty_without_adjust(expr_id); let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = @@ -31,12 +35,12 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_expr_to_some_place_with_adjust( &mut self, expr_id: ExprId, - prev_block: BasicBlockId, + prev_block: BasicBlockId<'db>, adjustments: &[Adjustment<'db>], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { let ty = adjustments .last() - .map(|it| it.target.to_chalk(self.interner)) + .map(|it| it.target) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = @@ -49,11 +53,11 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn lower_expr_as_place_with_adjust( &mut self, - current: BasicBlockId, + current: BasicBlockId<'db>, expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment<'db>], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -89,11 +93,11 @@ impl<'db> MirLowerCtx<'_, 'db> { current, r, rest.last() - .map(|it| it.target.to_chalk(self.interner)) + .map(|it| it.target) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)), - last.target.to_chalk(self.interner), + last.target, expr_id.into(), - match od.0.to_chalk(self.interner) { + match od.0 { Some(Mutability::Mut) => true, Some(Mutability::Not) => false, None => { @@ -111,10 +115,10 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn lower_expr_as_place( &mut self, - current: BasicBlockId, + current: BasicBlockId<'db>, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -123,10 +127,10 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn lower_expr_as_place_without_adjust( &mut self, - current: BasicBlockId, + current: BasicBlockId<'db>, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -149,9 +153,13 @@ impl<'db> MirLowerCtx<'_, 'db> { } ValueNs::StaticId(s) => { let ty = self.expr_ty_without_adjust(expr_id); - let ref_ty = - TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner); - let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let ref_ty = Ty::new_ref( + self.interner(), + Region::new_static(self.interner()), + ty, + Mutability::Not, + ); + let temp: Place<'db> = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp, @@ -167,10 +175,10 @@ impl<'db> MirLowerCtx<'_, 'db> { } } Expr::UnaryOp { expr, op: hir_def::hir::UnaryOp::Deref } => { - let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) { - TyKind::Ref(..) | TyKind::Raw(..) => true, + let is_builtin = match self.expr_ty_without_adjust(*expr).kind() { + TyKind::Ref(..) | TyKind::RawPtr(..) => true, TyKind::Adt(id, _) => { - if let Some(lang_item) = self.db.lang_attr(id.0.into()) { + if let Some(lang_item) = self.db.lang_attr(id.def_id().0.into()) { lang_item == LangItem::OwnedBox } else { false @@ -219,9 +227,9 @@ impl<'db> MirLowerCtx<'_, 'db> { Expr::Index { base, index } => { let base_ty = self.expr_ty_after_adjustments(*base); let index_ty = self.expr_ty_after_adjustments(*index); - if index_ty != TyBuilder::usize() + if !matches!(index_ty.kind(), TyKind::Uint(rustc_ast_ir::UintTy::Usize)) || !matches!( - base_ty.strip_reference().kind(Interner), + base_ty.strip_reference().kind(), TyKind::Array(..) | TyKind::Slice(..) ) { @@ -230,7 +238,6 @@ impl<'db> MirLowerCtx<'_, 'db> { "[overloaded index]".to_owned(), )); }; - let index_fn = (index_fn.0, index_fn.1.to_chalk(self.interner)); let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { @@ -279,24 +286,26 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, - current: BasicBlockId, - place: Place, - base_ty: Ty, - result_ty: Ty, - index_operand: Operand, + current: BasicBlockId<'db>, + place: Place<'db>, + base_ty: Ty<'db>, + result_ty: Ty<'db>, + index_operand: Operand<'db>, span: MirSpan, - index_fn: (FunctionId, Substitution), - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + index_fn: (FunctionId, GenericArgs<'db>), + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; - let result_ref = TyKind::Ref(mutability, error_lifetime(), result_ty).intern(Interner); - let mut result: Place = self.temp(result_ref, current, span)?.into(); - let index_fn_op = Operand::const_zst( - TyKind::FnDef(CallableDefId::FunctionId(index_fn.0).to_chalk(self.db), index_fn.1) - .intern(Interner), - ); + let result_ref = + Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability); + let mut result: Place<'db> = self.temp(result_ref, current, span)?.into(); + let index_fn_op = Operand::const_zst(Ty::new_fn_def( + self.interner(), + CallableDefId::FunctionId(index_fn.0).into(), + index_fn.1, + )); let Some(current) = self.lower_call( index_fn_op, Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]), @@ -314,14 +323,14 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_deref( &mut self, - current: BasicBlockId, - place: Place, - source_ty: Ty, - target_ty: Ty, + current: BasicBlockId<'db>, + place: Place<'db>, + source_ty: Ty<'db>, + target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { - let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> { + let (mutability, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { ( Mutability::Not, LangItem::Deref, @@ -336,9 +345,10 @@ impl<'db> MirLowerCtx<'_, 'db> { BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; - let ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), source_ty.clone()).intern(Interner); - let target_ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), target_ty).intern(Interner); - let ref_place: Place = self.temp(ty_ref, current, span)?.into(); + let error_region = Region::error(self.interner()); + let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability); + let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); + let ref_place: Place<'db> = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); let deref_trait = self .resolve_lang_item(trait_lang_item)? @@ -348,14 +358,12 @@ impl<'db> MirLowerCtx<'_, 'db> { .trait_items(self.db) .method_by_name(&trait_method_name) .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?; - let deref_fn_op = Operand::const_zst( - TyKind::FnDef( - CallableDefId::FunctionId(deref_fn).to_chalk(self.db), - Substitution::from1(Interner, source_ty), - ) - .intern(Interner), - ); - let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let deref_fn_op = Operand::const_zst(Ty::new_fn_def( + self.interner(), + CallableDefId::FunctionId(deref_fn).into(), + GenericArgs::new_from_iter(self.interner(), [source_ty.into()]), + )); + let mut result: Place<'db> = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call( deref_fn_op, Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 359c4fbb2e052..b1b86ab2c61b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -1,18 +1,18 @@ //! MIR lowering for patterns use hir_def::{AssocItemId, hir::ExprId, signatures::VariantFields}; +use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _}; -use crate::next_solver::mapping::NextSolverToChalk; +use crate::next_solver::GenericArgs; use crate::{ BindingMode, mir::{ LocalId, MutBorrowKind, Operand, OperandKind, lower::{ - BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, Interner, - MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, - ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, Substitution, - SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, ValueNs, - VariantId, + BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, + MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem, + RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, + TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, }, }, }; @@ -63,11 +63,11 @@ impl<'db> MirLowerCtx<'_, 'db> { /// so it should be an empty block. pub(super) fn pattern_match( &mut self, - current: BasicBlockId, - current_else: Option, - cond_place: Place, + current: BasicBlockId<'db>, + current_else: Option>, + cond_place: Place<'db>, pattern: PatId, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { let (current, current_else) = self.pattern_match_inner( current, current_else, @@ -87,10 +87,10 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, - current: BasicBlockId, - value: Place, + current: BasicBlockId<'db>, + value: Place<'db>, pattern: PatId, - ) -> Result<'db, BasicBlockId> { + ) -> Result<'db, BasicBlockId<'db>> { let (current, _) = self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?; Ok(current) @@ -99,9 +99,9 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn match_self_param( &mut self, id: BindingId, - current: BasicBlockId, - local: LocalId, - ) -> Result<'db, (BasicBlockId, Option)> { + current: BasicBlockId<'db>, + local: LocalId<'db>, + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { self.pattern_match_binding( id, BindingMode::Move, @@ -114,12 +114,12 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_match_inner( &mut self, - mut current: BasicBlockId, - mut current_else: Option, - mut cond_place: Place, + mut current: BasicBlockId<'db>, + mut current_else: Option>, + mut cond_place: Place<'db>, pattern: PatId, mode: MatchingMode, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); cond_place.projection = self.result.projection_store.intern( cond_place @@ -135,8 +135,8 @@ impl<'db> MirLowerCtx<'_, 'db> { Pat::Missing => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { - let subst = match self.infer[pattern].to_chalk(self.interner).kind(Interner) { - TyKind::Tuple(_, s) => s.clone(), + let subst = match self.infer[pattern].kind() { + TyKind::Tuple(s) => s, _ => { return Err(MirLowerError::TypeError( "non tuple type matched with tuple pattern", @@ -148,7 +148,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else, args, *ellipsis, - (0..subst.len(Interner)).map(|i| { + (0..subst.len()).map(|i| { PlaceElem::Field(Either::Right(TupleFieldId { tuple: TupleId(!0), // Dummy as it is unused index: i as u32, @@ -209,14 +209,11 @@ impl<'db> MirLowerCtx<'_, 'db> { } Pat::Range { start, end } => { let mut add_check = |l: &ExprId, binop| -> Result<'db, ()> { - let lv = self.lower_literal_or_const_to_operand( - self.infer[pattern].to_chalk(self.interner), - l, - )?; + let lv = self.lower_literal_or_const_to_operand(self.infer[pattern], l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); - let discr: Place = - self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + let discr: Place<'db> = + self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, @@ -252,12 +249,11 @@ impl<'db> MirLowerCtx<'_, 'db> { Pat::Slice { prefix, slice, suffix } => { if mode == MatchingMode::Check { // emit runtime length check for slice - if let TyKind::Slice(_) = - self.infer[pattern].to_chalk(self.interner).kind(Interner) - { + if let TyKind::Slice(_) = self.infer[pattern].kind() { let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = - self.temp(TyBuilder::usize(), current, pattern.into())?.into(); + let place_len: Place<'db> = self + .temp(Ty::new_usize(self.interner()), current, pattern.into())? + .into(); self.push_assignment( current, place_len, @@ -287,10 +283,11 @@ impl<'db> MirLowerCtx<'_, 'db> { let c = Operand::from_concrete_const( pattern_len.to_le_bytes().into(), MemoryMap::default(), - TyBuilder::usize(), + Ty::new_usize(self.interner()), ); - let discr: Place = - self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + let discr: Place<'db> = self + .temp(Ty::new_bool(self.interner()), current, pattern.into())? + .into(); self.push_assignment( current, discr, @@ -398,26 +395,19 @@ impl<'db> MirLowerCtx<'_, 'db> { if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) && let AssocItemId::ConstId(c) = x.0 { - break 'b (c, x.1.to_chalk(self.interner)); + break 'b (c, x.1); } if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr { - break 'b (c, Substitution::empty(Interner)); + break 'b (c, GenericArgs::new_from_iter(self.interner(), [])); } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = self - .temp(self.infer[pattern].to_chalk(self.interner), current, pattern.into())? - .into(); + let tmp: Place<'db> = + self.temp(self.infer[pattern], current, pattern.into())?.into(); let span = pattern.into(); - self.lower_const( - c.into(), - current, - tmp, - subst, - span, - self.infer[pattern].to_chalk(self.interner), - )?; - let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.lower_const(c.into(), current, tmp, subst, span)?; + let tmp2: Place<'db> = + self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, tmp2, @@ -444,10 +434,7 @@ impl<'db> MirLowerCtx<'_, 'db> { Pat::Lit(l) => match &self.body[*l] { Expr::Literal(l) => { if mode == MatchingMode::Check { - let c = self.lower_literal_to_operand( - self.infer[pattern].to_chalk(self.interner), - l, - )?; + let c = self.lower_literal_to_operand(self.infer[pattern], l)?; self.pattern_match_const(current_else, current, c, cond_place, pattern)? } else { (current, current_else) @@ -519,11 +506,11 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: Place, + cond_place: Place<'db>, span: MirSpan, - current: BasicBlockId, - current_else: Option, - ) -> Result<'db, (BasicBlockId, Option)> { + current: BasicBlockId<'db>, + current_else: Option>, + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { let target_place = self.binding_local(id)?; self.push_storage_live(id, current)?; self.push_match_assignment(current, target_place, mode, cond_place, span); @@ -532,10 +519,10 @@ impl<'db> MirLowerCtx<'_, 'db> { fn push_match_assignment( &mut self, - current: BasicBlockId, - target_place: LocalId, + current: BasicBlockId<'db>, + target_place: LocalId<'db>, mode: BindingMode, - cond_place: Place, + cond_place: Place<'db>, span: MirSpan, ) { self.push_assignment( @@ -558,15 +545,16 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_match_const( &mut self, - current_else: Option, - current: BasicBlockId, - c: Operand, - cond_place: Place, + current_else: Option>, + current: BasicBlockId<'db>, + c: Operand<'db>, + cond_place: Place<'db>, pattern: Idx, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + let discr: Place<'db> = + self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, @@ -591,14 +579,14 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: Place, + cond_place: Place<'db>, variant: VariantId, - mut current: BasicBlockId, + mut current: BasicBlockId<'db>, span: MirSpan, - mut current_else: Option, + mut current_else: Option>, shape: AdtPatternShape<'_>, mode: MatchingMode, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { Ok(match variant { VariantId::EnumVariantId(v) => { if mode == MatchingMode::Check { @@ -647,11 +635,11 @@ impl<'db> MirLowerCtx<'_, 'db> { shape: AdtPatternShape<'_>, variant_data: &VariantFields, v: VariantId, - current: BasicBlockId, - current_else: Option, - cond_place: &Place, + current: BasicBlockId<'db>, + current_else: Option>, + cond_place: &Place<'db>, mode: MatchingMode, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { Ok(match shape { AdtPatternShape::Record { args } => { let it = args @@ -690,12 +678,12 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_match_adt( &mut self, - mut current: BasicBlockId, - mut current_else: Option, - args: impl Iterator, - cond_place: &Place, + mut current: BasicBlockId<'db>, + mut current_else: Option>, + args: impl Iterator, PatId)>, + cond_place: &Place<'db>, mode: MatchingMode, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { for (proj, arg) in args { let cond_place = cond_place.project(proj, &mut self.result.projection_store); (current, current_else) = @@ -706,14 +694,14 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_match_tuple_like( &mut self, - current: BasicBlockId, - current_else: Option, + current: BasicBlockId<'db>, + current_else: Option>, args: &[PatId], ellipsis: Option, - fields: impl DoubleEndedIterator + Clone, - cond_place: &Place, + fields: impl DoubleEndedIterator> + Clone, + cond_place: &Place<'db>, mode: MatchingMode, - ) -> Result<'db, (BasicBlockId, Option)> { + ) -> Result<'db, (BasicBlockId<'db>, Option>)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); let it = al .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index ee088bd06ca6b..38fc7ad78a287 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -1,14 +1,8 @@ -use hir_def::db::DefDatabase; -use rustc_hash::FxHashMap; -use span::Edition; use test_fixture::WithFixture; -use triomphe::Arc; -use crate::{db::HirDatabase, mir::MirBody, setup_tracing, test_db::TestDB}; +use crate::{db::HirDatabase, setup_tracing, test_db::TestDB}; -fn lower_mir( - #[rust_analyzer::rust_fixture] ra_fixture: &str, -) -> FxHashMap, ()>> { +fn lower_mir(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); crate::attach_db(&db, || { @@ -20,14 +14,9 @@ fn lower_mir( hir_def::ModuleDefId::FunctionId(it) => Some(it), _ => None, }); - funcs - .map(|func| { - let name = - db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); - let mir = db.mir_body(func.into()); - (name, mir.map_err(drop)) - }) - .collect() + for func in funcs { + _ = db.mir_body(func.into()); + } }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 4bc81a4806ec6..745f73948d08c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -7,232 +7,129 @@ //! //! So the monomorphization should be called even if the substitution is empty. -use std::mem; - -use chalk_ir::{ - ConstData, DebruijnIndex, - fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, -}; use hir_def::DefWithBodyId; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; +use rustc_type_ir::{ + FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt, +}; use triomphe::Arc; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; +use crate::next_solver::{Const, ConstKind, Region, RegionKind}; use crate::{ - Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, - consteval::{intern_const_scalar, unknown_const}, - db::{HirDatabase, InternedClosure, InternedClosureId}, - from_placeholder_idx, - generics::{Generics, generics}, - infer::normalize, + TraitEnvironment, + db::{HirDatabase, InternedClosureId}, + next_solver::{ + DbInterner, GenericArgs, Ty, TyKind, TypingMode, + infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, + references_non_lt_error, + }, }; use super::{MirBody, MirLowerError, Operand, OperandKind, Rvalue, StatementKind, TerminatorKind}; -macro_rules! not_supported { - ($it: expr) => { - return Err(MirLowerError::NotSupported(format!($it))) - }; -} - -struct Filler<'a, 'db> { - db: &'db dyn HirDatabase, +struct Filler<'db> { + infcx: InferCtxt<'db>, trait_env: Arc>, - subst: &'a Substitution, - generics: Option, - interner: DbInterner<'db>, + subst: GenericArgs<'db>, } -impl<'a, 'db> FallibleTypeFolder for Filler<'a, 'db> { + +impl<'db> FallibleTypeFolder> for Filler<'db> { type Error = MirLowerError<'db>; - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner } - fn interner(&self) -> Interner { - Interner - } + fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result, Self::Error> { + if !ty.has_type_flags(TypeFlags::HAS_ALIAS | TypeFlags::HAS_PARAM) { + return Ok(ty); + } - fn try_fold_ty( - &mut self, - ty: Ty, - outer_binder: DebruijnIndex, - ) -> std::result::Result { - match ty.kind(Interner) { - TyKind::AssociatedType(id, subst) => { - // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes - // this kind of associated types. - Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone().try_fold_with(self, outer_binder)?, - })) - .intern(Interner)) - } - TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { - opaque_ty_id: id, - substitution: subst, - })) - | TyKind::OpaqueType(id, subst) => { - let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); - let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = self.db.infer(func.into()); - let filler = &mut Filler { - db: self.db, - trait_env: self.trait_env.clone(), - subst: &subst, - generics: Some(generics(self.db, func.into())), - interner: self.interner, - }; - filler.try_fold_ty( - infer.type_of_rpit[idx.to_nextsolver(self.interner)] - .to_chalk(self.interner), - outer_binder, - ) - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - not_supported!("type alias impl trait"); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - not_supported!("async block impl trait"); - } - } + match ty.kind() { + TyKind::Alias(..) => { + // First instantiate params. + let ty = ty.try_super_fold_with(self)?; + + let mut ocx = ObligationCtxt::new(&self.infcx); + let ty = ocx + .structurally_normalize_ty(&ObligationCause::dummy(), self.trait_env.env, ty) + .map_err(|_| MirLowerError::NotSupported("can't normalize alias".to_owned()))?; + ty.try_super_fold_with(self) } - _ => ty.try_super_fold_with(self.as_dyn(), outer_binder), + TyKind::Param(param) => Ok(self + .subst + .as_slice() + .get(param.index as usize) + .and_then(|arg| arg.ty()) + .ok_or_else(|| { + MirLowerError::GenericArgNotProvided(param.id.into(), self.subst) + })?), + _ => ty.try_super_fold_with(self), } } - fn try_fold_free_placeholder_const( - &mut self, - _ty: chalk_ir::Ty, - idx: chalk_ir::PlaceholderIndex, - _outer_binder: DebruijnIndex, - ) -> std::result::Result, Self::Error> { - let it = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { - not_supported!("missing idx in generics"); + fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { + let ConstKind::Param(param) = ct.kind() else { + return ct.try_super_fold_with(self); }; - Ok(self - .subst - .as_slice(Interner) - .get(idx) - .and_then(|it| it.constant(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? - .clone()) + self.subst + .as_slice() + .get(param.index as usize) + .and_then(|arg| arg.konst()) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(param.id.into(), self.subst)) } - fn try_fold_free_placeholder_ty( - &mut self, - idx: chalk_ir::PlaceholderIndex, - _outer_binder: DebruijnIndex, - ) -> std::result::Result { - let it = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { - not_supported!("missing idx in generics"); + fn try_fold_region(&mut self, region: Region<'db>) -> Result, Self::Error> { + let RegionKind::ReEarlyParam(param) = region.kind() else { + return Ok(region); }; - Ok(self - .subst - .as_slice(Interner) - .get(idx) - .and_then(|it| it.ty(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? - .clone()) - } - - fn try_fold_const( - &mut self, - constant: chalk_ir::Const, - outer_binder: DebruijnIndex, - ) -> Result, Self::Error> { - let next_ty = normalize( - self.db, - self.trait_env.clone(), - constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?, - ); - ConstData { ty: next_ty, value: constant.data(Interner).value.clone() } - .intern(Interner) - .try_super_fold_with(self, outer_binder) + self.subst + .as_slice() + .get(param.index as usize) + .and_then(|arg| arg.region()) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(param.id.into(), self.subst)) } } -impl<'a, 'db> Filler<'a, 'db> { - fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError<'db>> { - let tmp = mem::replace(ty, TyKind::Error.intern(Interner)); - *ty = normalize( - self.db, - self.trait_env.clone(), - tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?, - ); - Ok(()) - } - - fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError<'db>> { - let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone())); - *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; - Ok(()) +impl<'db> Filler<'db> { + fn new( + db: &'db dyn HirDatabase, + env: Arc>, + subst: GenericArgs<'db>, + ) -> Self { + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + Self { infcx, trait_env: env, subst } } - fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError<'db>> { - let tmp = mem::replace(ty, Substitution::empty(Interner)); - *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; - Ok(()) + fn fill> + Copy>( + &mut self, + t: &mut T, + ) -> Result<(), MirLowerError<'db>> { + // Can't deep normalized as that'll try to normalize consts and fail. + *t = t.try_fold_with(self)?; + if references_non_lt_error(t) { + Err(MirLowerError::NotSupported("monomorphization resulted in errors".to_owned())) + } else { + Ok(()) + } } - fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError<'db>> { + fn fill_operand(&mut self, op: &mut Operand<'db>) -> Result<(), MirLowerError<'db>> { match &mut op.kind { - OperandKind::Constant(c) => { - match &c.data(Interner).value { - chalk_ir::ConstValue::BoundVar(b) => { - let resolved = self - .subst - .as_slice(Interner) - .get(b.index) - .ok_or_else(|| { - MirLowerError::GenericArgNotProvided( - self.generics - .as_ref() - .and_then(|it| it.iter().nth(b.index)) - .and_then(|(id, _)| match id { - hir_def::GenericParamId::ConstParamId(id) => { - Some(hir_def::TypeOrConstParamId::from(id)) - } - hir_def::GenericParamId::TypeParamId(id) => { - Some(hir_def::TypeOrConstParamId::from(id)) - } - _ => None, - }) - .unwrap(), - self.subst.clone(), - ) - })? - .assert_const_ref(Interner); - *c = resolved.clone(); - } - chalk_ir::ConstValue::InferenceVar(_) - | chalk_ir::ConstValue::Placeholder(_) => {} - chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { - crate::ConstScalar::UnevaluatedConst(const_id, subst) => { - let mut subst = subst.clone(); - self.fill_subst(&mut subst)?; - *c = intern_const_scalar( - crate::ConstScalar::UnevaluatedConst(*const_id, subst), - c.data(Interner).ty.clone(), - ); - } - crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (), - }, - } - self.fill_const(c)?; + OperandKind::Constant { konst, ty } => { + self.fill(konst)?; + self.fill(ty)?; } OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (), } Ok(()) } - fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError<'db>> { + fn fill_body(&mut self, body: &mut MirBody<'db>) -> Result<(), MirLowerError<'db>> { for (_, l) in body.locals.iter_mut() { - self.fill_ty(&mut l.ty)?; + self.fill(&mut l.ty)?; } for (_, bb) in body.basic_blocks.iter_mut() { for statement in &mut bb.statements { @@ -245,20 +142,20 @@ impl<'a, 'db> Filler<'a, 'db> { match ak { super::AggregateKind::Array(ty) | super::AggregateKind::Tuple(ty) - | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?, - super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?, + | super::AggregateKind::Closure(ty) => self.fill(ty)?, + super::AggregateKind::Adt(_, subst) => self.fill(subst)?, super::AggregateKind::Union(_, _) => (), } } Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => { - self.fill_ty(ty)?; + self.fill(ty)?; } Rvalue::Use(op) => { self.fill_operand(op)?; } Rvalue::Repeat(op, len) => { self.fill_operand(op)?; - self.fill_const(len)?; + self.fill(len)?; } Rvalue::Ref(_, _) | Rvalue::Len(_) @@ -312,12 +209,10 @@ impl<'a, 'db> Filler<'a, 'db> { pub fn monomorphized_mir_body_query<'db>( db: &'db dyn HirDatabase, owner: DefWithBodyId, - subst: Substitution, + subst: GenericArgs<'db>, trait_env: Arc>, -) -> Result, MirLowerError<'db>> { - let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner }; +) -> Result>, MirLowerError<'db>> { + let mut filler = Filler::new(db, trait_env, subst); let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; @@ -327,22 +222,19 @@ pub fn monomorphized_mir_body_query<'db>( pub(crate) fn monomorphized_mir_body_cycle_result<'db>( _db: &'db dyn HirDatabase, _: DefWithBodyId, - _: Substitution, + _: GenericArgs<'db>, _: Arc>, -) -> Result, MirLowerError<'db>> { +) -> Result>, MirLowerError<'db>> { Err(MirLowerError::Loop) } pub fn monomorphized_mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, - subst: Substitution, + subst: GenericArgs<'db>, trait_env: Arc>, -) -> Result, MirLowerError<'db>> { - let InternedClosure(owner, _) = db.lookup_intern_closure(closure); - let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner }; +) -> Result>, MirLowerError<'db>> { + let mut filler = Filler::new(db, trait_env, subst); let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index aad54f884385e..e46edb8159189 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -11,8 +11,7 @@ use hir_expand::{Lookup, name::Name}; use la_arena::ArenaMap; use crate::{ - ClosureId, - db::HirDatabase, + db::{HirDatabase, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind}, }; @@ -37,8 +36,8 @@ macro_rules! wln { }; } -impl MirBody { - pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String { +impl<'db> MirBody<'db> { + pub fn pretty_print(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> String { let hir_body = db.body(self.owner); let mut ctx = MirPrettyCtx::new(self, &hir_body, db, display_target); ctx.for_body(|this| match ctx.body.owner { @@ -81,7 +80,7 @@ impl MirBody { // String with lines is rendered poorly in `dbg` macros, which I use very much, so this // function exists to solve that. - pub fn dbg(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> impl Debug { + pub fn dbg(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> impl Debug { struct StringDbg(String); impl Debug for StringDbg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -92,17 +91,17 @@ impl MirBody { } } -struct MirPrettyCtx<'a> { - body: &'a MirBody, +struct MirPrettyCtx<'a, 'db> { + body: &'a MirBody<'db>, hir_body: &'a Body, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, result: String, indent: String, - local_to_binding: ArenaMap, + local_to_binding: ArenaMap, BindingId>, display_target: DisplayTarget, } -impl Write for MirPrettyCtx<'_> { +impl Write for MirPrettyCtx<'_, '_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { let mut it = s.split('\n'); // note: `.lines()` is wrong here self.write(it.next().unwrap_or_default()); @@ -114,12 +113,12 @@ impl Write for MirPrettyCtx<'_> { } } -enum LocalName { - Unknown(LocalId), - Binding(Name, LocalId), +enum LocalName<'db> { + Unknown(LocalId<'db>), + Binding(Name, LocalId<'db>), } -impl HirDisplay for LocalName { +impl<'db> HirDisplay for LocalName<'db> { fn hir_fmt( &self, f: &mut crate::display::HirFormatter<'_>, @@ -133,8 +132,8 @@ impl HirDisplay for LocalName { } } -impl<'a> MirPrettyCtx<'a> { - fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) { +impl<'a, 'db> MirPrettyCtx<'a, 'db> { + fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) { name(self); self.with_block(|this| { this.locals(); @@ -146,8 +145,8 @@ impl<'a> MirPrettyCtx<'a> { } } - fn for_closure(&mut self, closure: ClosureId) { - let body = match self.db.mir_body_for_closure(closure.into()) { + fn for_closure(&mut self, closure: InternedClosureId) { + let body = match self.db.mir_body_for_closure(closure) { Ok(it) => it, Err(e) => { wln!(self, "// error in {closure:?}: {e:?}"); @@ -168,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> { self.indent = ctx.indent; } - fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) { + fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) { self.indent += " "; wln!(self, "{{"); f(self); @@ -180,9 +179,9 @@ impl<'a> MirPrettyCtx<'a> { } fn new( - body: &'a MirBody, + body: &'a MirBody<'db>, hir_body: &'a Body, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, display_target: DisplayTarget, ) -> Self { let local_to_binding = body.local_to_binding_map(); @@ -217,14 +216,14 @@ impl<'a> MirPrettyCtx<'a> { } } - fn local_name(&self, local: LocalId) -> LocalName { + fn local_name(&self, local: LocalId<'db>) -> LocalName<'db> { match self.local_to_binding.get(local) { Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local), None => LocalName::Unknown(local), } } - fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String { + fn basic_block_id(&self, basic_block_id: BasicBlockId<'db>) -> String { format!("'bb{}", u32::from(basic_block_id.into_raw())) } @@ -312,8 +311,12 @@ impl<'a> MirPrettyCtx<'a> { } } - fn place(&mut self, p: &Place) { - fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) { + fn place(&mut self, p: &Place<'db>) { + fn f<'db>( + this: &mut MirPrettyCtx<'_, 'db>, + local: LocalId<'db>, + projections: &[PlaceElem<'db>], + ) { let Some((last, head)) = projections.split_last() else { // no projection w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target)); @@ -373,19 +376,19 @@ impl<'a> MirPrettyCtx<'a> { f(self, p.local, p.projection.lookup(&self.body.projection_store)); } - fn operand(&mut self, r: &Operand) { + fn operand(&mut self, r: &Operand<'db>) { match &r.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { // MIR at the time of writing doesn't have difference between move and copy, so we show them // equally. Feel free to change it. self.place(p); } - OperandKind::Constant(c) => w!(self, "Const({})", self.hir_display(c)), + OperandKind::Constant { konst, .. } => w!(self, "Const({})", self.hir_display(konst)), OperandKind::Static(s) => w!(self, "Static({:?})", s), } } - fn rvalue(&mut self, r: &Rvalue) { + fn rvalue(&mut self, r: &Rvalue<'db>) { match r { Rvalue::Use(op) => self.operand(op), Rvalue::Ref(r, p) => { @@ -475,7 +478,7 @@ impl<'a> MirPrettyCtx<'a> { } } - fn operand_list(&mut self, it: &[Operand]) { + fn operand_list(&mut self, it: &[Operand<'db>]) { let mut it = it.iter(); if let Some(first) = it.next() { self.operand(first); @@ -486,7 +489,10 @@ impl<'a> MirPrettyCtx<'a> { } } - fn hir_display(&self, ty: &'a T) -> impl Display + 'a { + fn hir_display<'b, T: HirDisplay>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T> + where + 'db: 'b, + { ty.display_test(self.db, self.display_target) .with_closure_style(ClosureStyle::ClosureWithSubst) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index aae48eeaf9346..da86fa3ae53c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use hir_def::{ConstParamId, TypeOrConstParamId}; use intern::{Interned, Symbol}; +use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::{try_visit, visit::VisitorResult}; use rustc_type_ir::{ BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, @@ -23,7 +24,7 @@ use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, pub type ConstKind<'db> = rustc_type_ir::ConstKind>; pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst>; -#[salsa::interned(constructor = new_, debug)] +#[salsa::interned(constructor = new_)] pub struct Const<'db> { #[returns(ref)] kind_: InternedWrapperNoDebug>>, @@ -61,6 +62,21 @@ impl<'db> Const<'db> { Const::new(interner, ConstKind::Placeholder(placeholder)) } + pub fn new_valtree( + interner: DbInterner<'db>, + ty: Ty<'db>, + memory: Box<[u8]>, + memory_map: MemoryMap<'db>, + ) -> Self { + Const::new( + interner, + ConstKind::Value(ValueConst { + ty, + value: Valtree::new(ConstBytes { memory, memory_map }), + }), + ) + } + pub fn is_ct_infer(&self) -> bool { matches!(&self.inner().internee, ConstKind::Infer(_)) } @@ -77,6 +93,12 @@ impl<'db> Const<'db> { } } +impl<'db> std::fmt::Debug for Const<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + impl<'db> std::fmt::Debug for InternedWrapperNoDebug>> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.internee.fmt(f) @@ -135,9 +157,12 @@ impl ParamConst { /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable)] pub struct ValueConst<'db> { pub(crate) ty: Ty<'db>, + // FIXME: Should we ignore this for TypeVisitable, TypeFoldable? + #[type_visitable(ignore)] + #[type_foldable(identity)] pub(crate) value: Valtree<'db>, } @@ -158,33 +183,15 @@ impl<'db> rustc_type_ir::inherent::ValueConst> for ValueConst<'d } } -impl<'db> rustc_type_ir::TypeVisitable> for ValueConst<'db> { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - self.ty.visit_with(visitor) - } -} - -impl<'db> rustc_type_ir::TypeFoldable> for ValueConst<'db> { - fn fold_with>>(self, folder: &mut F) -> Self { - ValueConst { ty: self.ty.fold_with(folder), value: self.value } - } - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value }) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ConstBytes<'db>(pub Box<[u8]>, pub MemoryMap<'db>); +pub struct ConstBytes<'db> { + pub memory: Box<[u8]>, + pub memory_map: MemoryMap<'db>, +} impl Hash for ConstBytes<'_> { fn hash(&self, state: &mut H) { - self.0.hash(state) + self.memory.hash(state) } } @@ -212,7 +219,7 @@ impl<'db> Valtree<'db> { } } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)] pub struct ExprConst; impl rustc_type_ir::inherent::ParamLike for ParamConst { @@ -412,29 +419,6 @@ impl<'db> PlaceholderLike> for PlaceholderConst { } } -impl<'db> TypeVisitable> for ExprConst { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - // Ensure we get back to this when we fill in the fields - let ExprConst = &self; - V::Result::output() - } -} - -impl<'db> TypeFoldable> for ExprConst { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(ExprConst) - } - fn fold_with>>(self, folder: &mut F) -> Self { - ExprConst - } -} - impl<'db> Relate> for ExprConst { fn relate>>( relation: &mut R, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 918a311ea9ccc..3e4c4171bedde 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -1,8 +1,8 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, - StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, + ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -119,6 +119,16 @@ impl From for SolverDefId { } } +impl From for SolverDefId { + #[inline] + fn from(value: GeneralConstId) -> Self { + match value { + GeneralConstId::ConstId(const_id) => SolverDefId::ConstId(const_id), + GeneralConstId::StaticId(static_id) => SolverDefId::StaticId(static_id), + } + } +} + impl TryFrom for GenericDefId { type Error = SolverDefId; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 89a4d9202a75d..79527dac2fef3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -2,6 +2,7 @@ use hir_def::{GenericDefId, GenericParamId}; use intern::{Interned, Symbol}; +use macros::{TypeFoldable, TypeVisitable}; use rustc_type_ir::inherent::Const as _; use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, @@ -23,7 +24,7 @@ use super::{ interned_vec_db, }; -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub enum GenericArg<'db> { Ty(Ty<'db>), Lifetime(Region<'db>), @@ -55,6 +56,13 @@ impl<'db> GenericArg<'db> { } } + pub fn konst(self) -> Option> { + match self.kind() { + GenericArgKind::Const(konst) => Some(konst), + _ => None, + } + } + pub fn region(self) -> Option> { match self.kind() { GenericArgKind::Lifetime(r) => Some(r), @@ -72,7 +80,7 @@ impl<'db> From> for GenericArg<'db> { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub enum Term<'db> { Ty(Ty<'db>), Const(Const<'db>), @@ -130,39 +138,6 @@ impl<'db> IntoKind for GenericArg<'db> { } } -impl<'db> TypeVisitable> for GenericArg<'db> { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - match self { - GenericArg::Lifetime(lt) => lt.visit_with(visitor), - GenericArg::Ty(ty) => ty.visit_with(visitor), - GenericArg::Const(ct) => ct.visit_with(visitor), - } - } -} - -impl<'db> TypeFoldable> for GenericArg<'db> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - match self.kind() { - GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), - GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), - GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), - } - } - fn fold_with>>(self, folder: &mut F) -> Self { - match self.kind() { - GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), - GenericArgKind::Type(ty) => ty.fold_with(folder).into(), - GenericArgKind::Const(ct) => ct.fold_with(folder).into(), - } - } -} - impl<'db> Relate> for GenericArg<'db> { fn relate>>( relation: &mut R, @@ -553,36 +528,6 @@ impl<'db> From> for Term<'db> { } } -impl<'db> TypeVisitable> for Term<'db> { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - match self { - Term::Ty(ty) => ty.visit_with(visitor), - Term::Const(ct) => ct.visit_with(visitor), - } - } -} - -impl<'db> TypeFoldable> for Term<'db> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - match self.kind() { - TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), - TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), - } - } - fn fold_with>>(self, folder: &mut F) -> Self { - match self.kind() { - TermKind::Ty(ty) => ty.fold_with(folder).into(), - TermKind::Const(ct) => ct.fold_with(folder).into(), - } - } -} - impl<'db> Relate> for Term<'db> { fn relate>>( relation: &mut R, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index 392e2b9329a04..79b0a2933236b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use hir_def::{ImplId, TraitId}; +use macros::{TypeFoldable, TypeVisitable}; use rustc_type_ir::{ Interner, solve::{BuiltinImplSource, CandidateSource, Certainty, inspect::ProbeKind}, @@ -177,7 +178,7 @@ pub type SelectionResult<'db, T> = Result, SelectionError<'db>>; /// ### The type parameter `N` /// /// See explanation on `ImplSourceUserDefinedData`. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub enum ImplSource<'db, N> { /// ImplSource identifying a particular impl. UserDefined(ImplSourceUserDefinedData<'db, N>), @@ -242,8 +243,10 @@ impl<'db, N> ImplSource<'db, N> { /// is `Obligation`, as one might expect. During codegen, however, this /// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub struct ImplSourceUserDefinedData<'db, N> { + #[type_visitable(ignore)] + #[type_foldable(identity)] pub impl_def_id: ImplId, pub args: GenericArgs<'db>, pub nested: Vec, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 9fa1fa7fb4404..bc905c2e0b958 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -8,6 +8,7 @@ use std::{ }; use hir_def::TraitId; +use macros::{TypeFoldable, TypeVisitable}; use rustc_type_ir::elaborate::Elaboratable; use rustc_type_ir::{ PredicatePolarity, Upcast, @@ -65,8 +66,10 @@ impl ObligationCause { /// either identifying an `impl` (e.g., `impl Eq for i32`) that /// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). -#[derive(Clone, Debug)] +#[derive(Clone, Debug, TypeVisitable, TypeFoldable)] pub struct Obligation<'db, T> { + #[type_foldable(identity)] + #[type_visitable(ignore)] /// The reason we have to prove this thing. pub cause: ObligationCause, @@ -117,39 +120,6 @@ impl<'db> Elaboratable> for PredicateObligation<'db> { } } -impl<'db, T: TypeVisitable>> TypeVisitable> for Obligation<'db, T> { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - rustc_ast_ir::try_visit!(self.param_env.visit_with(visitor)); - self.predicate.visit_with(visitor) - } -} - -impl<'db, T: TypeFoldable>> TypeFoldable> for Obligation<'db, T> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(Obligation { - cause: self.cause.clone(), - param_env: self.param_env.try_fold_with(folder)?, - predicate: self.predicate.try_fold_with(folder)?, - recursion_depth: self.recursion_depth, - }) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - Obligation { - cause: self.cause.clone(), - param_env: self.param_env.fold_with(folder), - predicate: self.predicate.fold_with(folder), - recursion_depth: self.recursion_depth, - } - } -} - impl<'db, T: Copy> Obligation<'db, T> { pub fn as_goal(&self) -> Goal<'db, T> { Goal { param_env: self.param_env, predicate: self.predicate } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 4a0ede35ac752..cd1667527ba65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1,77 +1,81 @@ //! Things related to the Interner in the next-trait-solver. -#![allow(unused)] +#![allow(unused)] // FIXME(next-solver): Remove this. + +use std::{fmt, ops::ControlFlow}; pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; use base_db::Crate; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; -use hir_def::lang_item::LangItem; -use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; -use hir_def::{AdtId, BlockId, GenericDefId, TypeAliasId, VariantId}; -use hir_def::{AttrDefId, Lookup}; -use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; +use hir_def::{ + AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, GenericDefId, ItemContainerId, Lookup, + StructId, TypeAliasId, UnionId, VariantId, + lang_item::LangItem, + signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}, +}; use intern::sym::non_exhaustive; use intern::{Interned, impl_internable, sym}; use la_arena::Idx; use rustc_abi::{Align, ReprFlags, ReprOptions}; +use rustc_ast_ir::visit::VisitorResult; use rustc_hash::FxHashSet; -use rustc_index::bit_set::DenseBitSet; -use rustc_type_ir::elaborate::elaborate; -use rustc_type_ir::error::TypeError; -use rustc_type_ir::inherent::{ - AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, -}; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; -use rustc_type_ir::solve::SizedTraitKind; +use rustc_index::{IndexVec, bit_set::DenseBitSet}; use rustc_type_ir::{ - AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, - ImplPolarity, InferTy, ProjectionPredicate, TraitPredicate, TraitRef, Upcast, + AliasTerm, AliasTermKind, AliasTy, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, + EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, + ProjectionPredicate, RegionKind, TermKind, TraitPredicate, TraitRef, TypeVisitableExt, + UniverseIndex, Upcast, Variance, WithCachedTypeInfo, + elaborate::{self, elaborate}, + error::TypeError, + inherent::{ + self, AdtDef as _, Const as _, GenericArgs as _, GenericsOf, IntoKind, ParamEnv as _, + Region as _, SliceLike as _, Span as _, Ty as _, + }, + ir_print, + lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}, + relate, + solve::SizedTraitKind, }; use salsa::plumbing::AsId; use smallvec::{SmallVec, smallvec}; -use std::fmt; -use std::ops::ControlFlow; use syntax::ast::SelfParamKind; +use tracing::debug; use triomphe::Arc; -use rustc_ast_ir::visit::VisitorResult; -use rustc_index::IndexVec; -use rustc_type_ir::TypeVisitableExt; -use rustc_type_ir::{ - BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex, - Variance, WithCachedTypeInfo, elaborate, - inherent::{self, Const as _, Region as _, Ty as _}, - ir_print, relate, -}; - -use crate::lower_nextsolver::{self, TyLoweringContext}; -use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; -use crate::next_solver::infer::InferCtxt; -use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; -use crate::next_solver::{ - AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, - RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, +use crate::{ + ConstScalar, FnAbi, Interner, + db::HirDatabase, + lower_nextsolver::{self, TyLoweringContext}, + method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}, + next_solver::{ + AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, + CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, + RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, + TypingMode, + infer::{ + DbInternerInferExt, InferCtxt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, + util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}, + }, }; -use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; -use super::generics::generics; -use super::util::sizedness_constraint_for_ty; use super::{ Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, - Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, + ClauseKind, Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv, ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate, - PredicateKind, Term, Ty, TyKind, Tys, ValueConst, + PredicateKind, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst, abi::Safety, fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, - generics::Generics, + generics::{Generics, generics}, mapping::ChalkToNextSolver, region::{ BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region, }, + util::sizedness_constraint_for_ty, }; -use super::{ClauseKind, SolverDefId, Valtree}; #[macro_export] #[doc(hidden)] @@ -1102,7 +1106,15 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { match alias.def_id { SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque, - SolverDefId::TypeAliasId(_) => AliasTyKind::Projection, + SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { + ItemContainerId::ImplId(impl_) + if self.db.impl_signature(impl_).target_trait.is_none() => + { + AliasTyKind::Inherent + } + ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => AliasTyKind::Projection, + _ => AliasTyKind::Free, + }, _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), } } @@ -1113,7 +1125,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ) -> rustc_type_ir::AliasTermKind { match alias.def_id { SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, - SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, + SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { + ItemContainerId::ImplId(impl_) + if self.db.impl_signature(impl_).target_trait.is_none() => + { + AliasTermKind::InherentTy + } + ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => { + AliasTermKind::ProjectionTy + } + _ => AliasTermKind::FreeTy, + }, + // rustc creates an `AnonConst` for consts, and evaluates them with CTFE (normalizing projections + // via selection, similar to ours `find_matching_impl()`, and not with the trait solver), so mimic it. SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), } @@ -1676,8 +1700,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool { - // FIXME - false + self.db.impl_signature(impl_def_id.0).is_default() } #[tracing::instrument(skip(self), ret)] @@ -1731,7 +1754,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed { - panic!("Bug encountered in next-trait-solver.") + panic!("Bug encountered in next-trait-solver: {}", msg.to_string()) } fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool { @@ -1929,7 +1952,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { false } - fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool { + // FIXME(next-solver): Make this a query? Can this make cycles? + fn impl_specializes( + self, + specializing_impl_def_id: Self::ImplId, + parent_impl_def_id: Self::ImplId, + ) -> bool { false } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index b32a5ec2928bf..1a5982cc00d36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -539,7 +539,7 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { ConstScalar::Bytes(bytes, memory) => { rustc_type_ir::ConstKind::Value(ValueConst::new( data.ty.to_nextsolver(interner), - ConstBytes(bytes.clone(), memory.clone()), + ConstBytes { memory: bytes.clone(), memory_map: memory.clone() }, )) } ConstScalar::UnevaluatedConst(c, subst) => { @@ -1710,8 +1710,10 @@ pub fn convert_const_for_result<'db>( let bytes = value_const.value.inner(); let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { // SAFETY: we will never actually use this without a database - interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { - std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) + interned: ConstScalar::Bytes(bytes.memory.clone(), unsafe { + std::mem::transmute::, MemoryMap<'static>>( + bytes.memory_map.clone(), + ) }), }); return chalk_ir::ConstData { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 1623fa342a736..9dda9d06da276 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use intern::Interned; +use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::try_visit; use rustc_type_ir::{ self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags, @@ -424,7 +425,7 @@ impl<'db> rustc_type_ir::TypeSuperVisitable> for Clauses<'db> { pub struct Clause<'db>(pub(crate) Predicate<'db>); // We could cram the reveal into the clauses like rustc does, probably -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)] pub struct ParamEnv<'db> { pub(crate) clauses: Clauses<'db>, } @@ -435,28 +436,6 @@ impl<'db> ParamEnv<'db> { } } -impl<'db> TypeVisitable> for ParamEnv<'db> { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - try_visit!(self.clauses.visit_with(visitor)); - V::Result::output() - } -} - -impl<'db> TypeFoldable> for ParamEnv<'db> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? }) - } - fn fold_with>>(self, folder: &mut F) -> Self { - ParamEnv { clauses: self.clauses.fold_with(folder) } - } -} - impl<'db> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'db> { fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike> { self.clauses diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index a161423da4d76..2457447ee39ba 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -149,13 +149,9 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn fetch_eligible_assoc_item( &self, goal_trait_ref: rustc_type_ir::TraitRef, - trait_assoc_def_id: ::DefId, + trait_assoc_def_id: SolverDefId, impl_id: ImplIdWrapper, - ) -> Result::DefId>, ErrorGuaranteed> { - let trait_assoc_id = match trait_assoc_def_id { - SolverDefId::TypeAliasId(id) => id, - _ => panic!("Unexpected SolverDefId"), - }; + ) -> Result, ErrorGuaranteed> { let trait_ = self .0 .interner @@ -167,18 +163,47 @@ impl<'db> SolverDelegate for SolverContext<'db> { .def_id .0; let trait_data = trait_.trait_items(self.0.interner.db()); - let id = - impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { - match item { - (_, AssocItemId::TypeAliasId(type_alias)) => { - let name = &self.0.interner.db().type_alias_signature(*type_alias).name; - let found_trait_assoc_id = trait_data.associated_type_by_name(name)?; - (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias) - } - _ => None, - } - }); - Ok(id.map(SolverDefId::TypeAliasId)) + let impl_items = impl_id.0.impl_items(self.0.interner.db()); + let id = match trait_assoc_def_id { + SolverDefId::TypeAliasId(trait_assoc_id) => { + let trait_assoc_data = self.0.interner.db.type_alias_signature(trait_assoc_id); + impl_items + .items + .iter() + .find_map(|(impl_assoc_name, impl_assoc_id)| { + if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id + && *impl_assoc_name == trait_assoc_data.name + { + Some(impl_assoc_id) + } else { + None + } + }) + .map(SolverDefId::TypeAliasId) + } + SolverDefId::ConstId(trait_assoc_id) => { + let trait_assoc_data = self.0.interner.db.const_signature(trait_assoc_id); + let trait_assoc_name = trait_assoc_data + .name + .as_ref() + .expect("unnamed consts should not get passed to the solver"); + impl_items + .items + .iter() + .find_map(|(impl_assoc_name, impl_assoc_id)| { + if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id + && impl_assoc_name == trait_assoc_name + { + Some(impl_assoc_id) + } else { + None + } + }) + .map(SolverDefId::ConstId) + } + _ => panic!("Unexpected SolverDefId"), + }; + Ok(id) } fn is_transmutable( @@ -200,9 +225,9 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), _ => unreachable!(), }; - let subst = uv.args.to_chalk(self.interner); + let subst = uv.args; let ec = self.cx().db.const_eval(c, subst, None).ok()?; - Some(ec.to_nextsolver(self.interner)) + Some(ec) } fn compute_goal_fast_path( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 11ca0b03eb3dc..c5969120c9665 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -8,11 +8,10 @@ use hir_def::{AdtId, GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; -use rustc_type_ir::TyVid; use rustc_type_ir::{ BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy, - IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo, + IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo, inherent::{ Abi, AdtDef as _, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _, @@ -24,14 +23,13 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; -use crate::next_solver::{AdtDef, Binder}; use crate::{ FnAbi, db::HirDatabase, interner::InternedWrapperNoDebug, next_solver::{ - CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, GenericArg, - PolyFnSig, TypeAliasIdWrapper, + AdtDef, Binder, CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, + GenericArg, PolyFnSig, Region, TypeAliasIdWrapper, abi::Safety, util::{CoroutineArgsExt, IntegerTypeExt}, }, @@ -392,7 +390,7 @@ impl<'db> Ty<'db> { /// Whether the type contains some non-lifetime, aka. type or const, error type. pub fn references_non_lt_error(self) -> bool { - self.references_error() && self.visit_with(&mut ReferencesNonLifetimeError).is_break() + references_non_lt_error(&self) } pub fn callable_sig(self, interner: DbInterner<'db>) -> Option>> { @@ -409,6 +407,13 @@ impl<'db> Ty<'db> { } } + pub fn as_reference(self) -> Option<(Ty<'db>, Region<'db>, Mutability)> { + match self.kind() { + TyKind::Ref(lifetime, ty, mutability) => Some((ty, lifetime, mutability)), + _ => None, + } + } + pub fn as_reference_or_ptr(self) -> Option<(Ty<'db>, Rawness, Mutability)> { match self.kind() { TyKind::Ref(_, ty, mutability) => Some((ty, Rawness::Ref, mutability)), @@ -417,6 +422,18 @@ impl<'db> Ty<'db> { } } + pub fn strip_references(self) -> Ty<'db> { + let mut t = self; + while let TyKind::Ref(_lifetime, ty, _mutability) = t.kind() { + t = ty; + } + t + } + + pub fn strip_reference(self) -> Ty<'db> { + self.as_reference().map_or(self, |(ty, _, _)| ty) + } + /// Replace infer vars with errors. /// /// This needs to be called for every type that may contain infer vars and is yielded to outside inference, @@ -428,6 +445,10 @@ impl<'db> Ty<'db> { } } +pub fn references_non_lt_error<'db, T: TypeVisitableExt>>(t: &T) -> bool { + t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break() +} + struct ReferencesNonLifetimeError; impl<'db> TypeVisitor> for ReferencesNonLifetimeError { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 427c4bb68423d..e989e4c006fff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -20,11 +20,10 @@ use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::{GenericArgs, IntoKind, SliceLike}; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; -use crate::next_solver::mapping::NextSolverToChalk; use crate::{ ChalkTraitId, Const, ConstScalar, Interner, Substitution, TargetFeatures, TraitRef, TraitRefExt, Ty, @@ -34,7 +33,7 @@ use crate::{ mir::pad16, next_solver::{ DbInterner, - mapping::{ChalkToNextSolver, convert_args_for_result}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_args_for_result}, }, to_chalk_trait_id, }; @@ -196,15 +195,6 @@ pub(super) fn associated_type_by_name_including_super_traits( pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution); impl<'a> ClosureSubst<'a> { - pub(crate) fn parent_subst(&self, db: &dyn HirDatabase) -> Substitution { - let interner = DbInterner::new_with(db, None, None); - let subst = - >>::to_nextsolver( - self.0, interner, - ); - subst.split_closure_args().parent_args.to_chalk(interner) - } - pub(crate) fn sig_ty(&self, db: &dyn HirDatabase) -> Ty { let interner = DbInterner::new_with(db, None, None); let subst = @@ -310,10 +300,12 @@ impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value && let ConstScalar::UnevaluatedConst(id, subst) = &c.interned { - if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { - return Ok(eval); + let interner = DbInterner::conjure(); + if let Ok(eval) = self.db.const_eval(*id, subst.to_nextsolver(interner), None) { + return Ok(eval.to_chalk(interner)); } else { - return Ok(unknown_const(constant.data(Interner).ty.clone())); + return Ok(unknown_const(constant.data(Interner).ty.to_nextsolver(interner)) + .to_chalk(interner)); } } Ok(constant) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index cce2564a9e6dd..59038c2656c9d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -75,7 +75,7 @@ use hir_ty::{ GenericArgData, Interner, ParamKind, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic, ValueTyDefId, WhereClause, all_super_traits, autoderef, check_orphan_rules, - consteval::{ConstExt, try_const_usize, unknown_const_as_generic}, + consteval_chalk::{ConstExt, try_const_usize, unknown_const_as_generic}, diagnostics::BodyValidationDiagnostic, direct_super_traits, error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, @@ -2153,8 +2153,11 @@ impl DefWithBody { mir::MirSpan::Unknown => continue, }; acc.push( - MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span } - .into(), + MovedOutOfRef { + ty: Type::new_for_crate(krate, moof.ty.to_chalk(interner)), + span, + } + .into(), ) } let mol = &borrowck_result.mutability_of_locals; @@ -2649,9 +2652,10 @@ impl Function { db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, ) -> Result> { + let interner = DbInterner::new_with(db, None, None); let body = db.monomorphized_mir_body( self.id.into(), - Substitution::empty(Interner), + GenericArgs::new_from_iter(interner, []), db.trait_environment(self.id.into()), )?; let (result, output) = interpret_mir(db, body, false, None)?; @@ -2933,8 +2937,10 @@ impl Const { /// Evaluate the constant. pub fn eval(self, db: &dyn HirDatabase) -> Result> { - db.const_eval(self.id.into(), Substitution::empty(Interner), None) - .map(|it| EvaluatedConst { const_: it, def: self.id.into() }) + let interner = DbInterner::new_with(db, None, None); + let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().to_chalk(interner); + db.const_eval(self.id.into(), GenericArgs::new_from_iter(interner, []), None) + .map(|it| EvaluatedConst { const_: it.to_chalk(interner), def: self.id.into(), ty }) } } @@ -2947,6 +2953,7 @@ impl HasVisibility for Const { pub struct EvaluatedConst { def: DefWithBodyId, const_: hir_ty::Const, + ty: hir_ty::Ty, } impl EvaluatedConst { @@ -2955,6 +2962,7 @@ impl EvaluatedConst { } pub fn render_debug<'db>(&self, db: &'db dyn HirDatabase) -> Result> { + let interner = DbInterner::new_with(db, None, None); let data = self.const_.data(Interner); if let TyKind::Scalar(s) = data.ty.kind(Interner) && matches!(s, Scalar::Int(_) | Scalar::Uint(_)) @@ -2972,7 +2980,12 @@ impl EvaluatedConst { return Ok(result); } } - mir::render_const_using_debug_impl(db, self.def, &self.const_) + mir::render_const_using_debug_impl( + db, + self.def, + self.const_.to_nextsolver(interner), + self.ty.to_nextsolver(interner), + ) } } @@ -3011,8 +3024,10 @@ impl Static { /// Evaluate the static initializer. pub fn eval(self, db: &dyn HirDatabase) -> Result> { - db.const_eval(self.id.into(), Substitution::empty(Interner), None) - .map(|it| EvaluatedConst { const_: it, def: self.id.into() }) + let interner = DbInterner::new_with(db, None, None); + let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().to_chalk(interner); + db.const_eval(self.id.into(), GenericArgs::new_from_iter(interner, []), None) + .map(|it| EvaluatedConst { const_: it.to_chalk(interner), def: self.id.into(), ty }) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 1faa3c4165fdd..a4bc3e8f1a13a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -35,7 +35,6 @@ use hir_expand::{ mod_path::{ModPath, PathKind, path}, name::{AsName, Name}, }; -use hir_ty::next_solver::GenericArgs; use hir_ty::{ Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy, Substitution, ToChalk, TraitEnvironment, Ty, TyKind, TyLoweringContext, @@ -47,7 +46,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution, next_solver::{ - DbInterner, + DbInterner, GenericArgs, TypingMode, + infer::DbInternerInferExt, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, }; @@ -1439,12 +1439,9 @@ impl<'db> SourceAnalyzer<'db> { None => return (const_id, subs), }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_const( - DbInterner::new_with(db, None, None), - env, - const_id, - subs, - ) + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + method_resolution::lookup_impl_const(&infcx, env, const_id, subs) } fn lang_trait_fn( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 8bc0b3f6ab3b9..df1800616803e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -6306,6 +6306,8 @@ const FOO$0: (&str, &str) = { ); } +// FIXME(next-solver): this fails to normalize the const, probably due to the solver +// refusing to give the impl because of the error type. #[test] fn hover_const_eval_in_generic_trait() { // Doesn't compile, but we shouldn't crash. @@ -6327,12 +6329,16 @@ fn test() { *FOO* ```rust - ra_test_fixture::S + ra_test_fixture::Trait ``` ```rust - const FOO: bool = true + const FOO: bool = false ``` + + --- + + `Self` = `S<{unknown}>` "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index d0539abe28281..1e272fe3ba827 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -6,7 +6,7 @@ //! } //! ``` use hir::{ - ChalkTyInterner, DefWithBody, + DefWithBody, db::{DefDatabase as _, HirDatabase as _}, mir::{MirSpan, TerminatorKind}, }; @@ -46,7 +46,7 @@ pub(super) fn hints( if !place.projection.is_empty() { continue; // Ignore complex cases for now } - if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { + if mir.locals[place.local].ty.as_adt().is_none() { continue; // Arguably only ADTs have significant drop impls } let Some(&binding_idx) = local_to_binding.get(place.local) else { diff --git a/src/tools/rust-analyzer/crates/macros/Cargo.toml b/src/tools/rust-analyzer/crates/macros/Cargo.toml new file mode 100644 index 0000000000000..8184cc6d1c03d --- /dev/null +++ b/src/tools/rust-analyzer/crates/macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "macros" +version = "0.0.0" +repository.workspace = true +description = "Proc macros for rust-analyzer." + +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = "2.0.9" +synstructure = "0.13.0" diff --git a/src/tools/rust-analyzer/crates/macros/src/lib.rs b/src/tools/rust-analyzer/crates/macros/src/lib.rs new file mode 100644 index 0000000000000..8bafcf498c510 --- /dev/null +++ b/src/tools/rust-analyzer/crates/macros/src/lib.rs @@ -0,0 +1,164 @@ +//! Proc macros for rust-analyzer. + +use quote::{ToTokens, quote}; +use syn::parse_quote; +use synstructure::decl_derive; + +decl_derive!( + [TypeFoldable, attributes(type_foldable)] => + /// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported). + /// + /// The fold will produce a value of the same struct or enum variant as the input, with + /// each field respectively folded using the `TypeFoldable` implementation for its type. + /// However, if a field of a struct or an enum variant is annotated with + /// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its + /// type is not required to implement `TypeFoldable`). + type_foldable_derive +); +decl_derive!( + [TypeVisitable, attributes(type_visitable)] => + /// Derives `TypeVisitable` for the annotated `struct` or `enum` (`union` is not supported). + /// + /// Each field of the struct or enum variant will be visited in definition order, using the + /// `TypeVisitable` implementation for its type. However, if a field of a struct or an enum + /// variant is annotated with `#[type_visitable(ignore)]` then that field will not be + /// visited (and its type is not required to implement `TypeVisitable`). + type_visitable_derive +); + +fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + // ignore fields with #[type_visitable(ignore)] + s.filter(|bi| { + let mut ignored = false; + + bi.ast().attrs.iter().for_each(|attr| { + if !attr.path().is_ident("type_visitable") { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident("ignore") { + ignored = true; + } + Ok(()) + }); + }); + + !ignored + }); + + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "db") { + s.add_impl_generic(parse_quote! { 'db }); + } + + s.add_bounds(synstructure::AddBounds::Generics); + let body_visit = s.each(|bind| { + quote! { + match ::rustc_type_ir::VisitorResult::branch( + ::rustc_type_ir::TypeVisitable::visit_with(#bind, __visitor) + ) { + ::core::ops::ControlFlow::Continue(()) => {}, + ::core::ops::ControlFlow::Break(r) => { + return ::rustc_type_ir::VisitorResult::from_residual(r); + }, + } + } + }); + s.bind_with(|_| synstructure::BindStyle::Move); + + s.bound_impl( + quote!(::rustc_type_ir::TypeVisitable<::hir_ty::next_solver::DbInterner<'db>>), + quote! { + fn visit_with<__V: ::rustc_type_ir::TypeVisitor<::hir_ty::next_solver::DbInterner<'db>>>( + &self, + __visitor: &mut __V + ) -> __V::Result { + match *self { #body_visit } + <__V::Result as ::rustc_type_ir::VisitorResult>::output() + } + }, + ) +} + +fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "db") { + s.add_impl_generic(parse_quote! { 'db }); + } + + s.add_bounds(synstructure::AddBounds::Generics); + s.bind_with(|_| synstructure::BindStyle::Move); + let try_body_fold = s.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + // retain value of fields with #[type_foldable(identity)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + bind.to_token_stream() + } else { + quote! { + ::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)? + } + } + }) + }); + + let body_fold = s.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + // retain value of fields with #[type_foldable(identity)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + bind.to_token_stream() + } else { + quote! { + ::rustc_type_ir::TypeFoldable::fold_with(#bind, __folder) + } + } + }) + }); + + s.bound_impl( + quote!(::rustc_type_ir::TypeFoldable<::hir_ty::next_solver::DbInterner<'db>>), + quote! { + fn try_fold_with<__F: ::rustc_type_ir::FallibleTypeFolder<::hir_ty::next_solver::DbInterner<'db>>>( + self, + __folder: &mut __F + ) -> Result { + Ok(match self { #try_body_fold }) + } + + fn fold_with<__F: ::rustc_type_ir::TypeFolder<::hir_ty::next_solver::DbInterner<'db>>>( + self, + __folder: &mut __F + ) -> Self { + match self { #body_fold } + } + }, + ) +} + +fn has_ignore_attr(attrs: &[syn::Attribute], name: &'static str, meta: &'static str) -> bool { + let mut ignored = false; + attrs.iter().for_each(|attr| { + if !attr.path().is_ident(name) { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident(meta) { + ignored = true; + } + Ok(()) + }); + }); + + ignored +} From 4eccd0222962bf3203b11f103f391c866e039512 Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Fri, 10 Oct 2025 18:01:03 +0200 Subject: [PATCH 1514/1900] Stabilize unsigned_nonzero_div_ceil --- library/core/src/num/nonzero.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d9184e3c9c229..fcdb65bd45c95 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1373,7 +1373,6 @@ macro_rules! nonzero_integer_signedness_dependent_impls { /// # Examples /// /// ``` - /// # #![feature(unsigned_nonzero_div_ceil)] /// # use std::num::NonZero; #[doc = concat!("let one = NonZero::new(1", stringify!($Int), ").unwrap();")] #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX).unwrap();")] @@ -1383,7 +1382,11 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] /// assert_eq!(three.div_ceil(two), two); /// ``` - #[unstable(feature = "unsigned_nonzero_div_ceil", issue = "132968")] + #[stable(feature = "unsigned_nonzero_div_ceil", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable( + feature = "unsigned_nonzero_div_ceil", + since = "CURRENT_RUSTC_VERSION" + )] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] From 6a08a85b065bba8060adbe9fd1ff2123b4ba3f7a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 10 Oct 2025 18:16:58 +0200 Subject: [PATCH 1515/1900] Autolabel PR touching `declared_lints.rs` with `needs-fcp` --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 57ff3dc352c8e..db951b95ef50a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -45,6 +45,9 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[autolabel."needs-fcp"] +trigger_files = ["clippy_lints/src/declared_lints.rs"] + [concern] # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] From dff4561f591cca31938763b577e81a98e425f6ea Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Fri, 10 Oct 2025 20:01:02 +0100 Subject: [PATCH 1516/1900] Fix documentation of Instant::now on mac --- library/std/src/time.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 31187adb6feae..87aaf9091f1bc 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -112,19 +112,19 @@ use crate::sys_common::{FromInner, IntoInner}; /// | Platform | System call | /// |-----------|----------------------------------------------------------------------| /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | -/// | UNIX | [clock_gettime (Monotonic Clock)] | -/// | Darwin | [clock_gettime (Monotonic Clock)] | -/// | VXWorks | [clock_gettime (Monotonic Clock)] | +/// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` | +/// | Darwin | [clock_gettime] with `CLOCK_UPTIME_RAW` | +/// | VXWorks | [clock_gettime] with `CLOCK_MONOTONIC` | /// | SOLID | `get_tim` | -/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | +/// | WASI | [__wasi_clock_time_get] with `monotonic` | /// | Windows | [QueryPerformanceCounter] | /// /// [currently]: crate::io#platform-specific-behavior /// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get -/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime +/// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get +/// [clock_gettime]: https://linux.die.net/man/3/clock_gettime /// /// **Disclaimer:** These system calls might change over time. /// From 9c6c702431eb62695c4cc28d77c3c19775ff3494 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 10 Oct 2025 15:45:46 -0400 Subject: [PATCH 1517/1900] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 801d9b4981dd0..81c3f77a46735 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 801d9b4981dd07e3aecdca1ab86834c13615737e +Subproject commit 81c3f77a467359c8be6bc747dc93ec66a6e4ce11 From 6f89cecf3756c276dbd263810e86c13a652dbb5a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 5 Oct 2025 10:25:17 -0700 Subject: [PATCH 1518/1900] unused_must_use: Don't warn on `Result<(), Uninhabited>` This suppresses warnings on things like `Result<(), !>`, which helps simplify code using the common pattern of having an `Error` associated type: code will only have to check the error if there is a possibility of error. --- compiler/rustc_lint/src/unused.rs | 12 ++++ ...se_result_unit_uninhabited_extern_crate.rs | 4 ++ .../must_use-result-unit-uninhabited.rs | 55 +++++++++++++++++++ .../must_use-result-unit-uninhabited.stderr | 43 +++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs create mode 100644 tests/ui/lint/unused/must_use-result-unit-uninhabited.rs create mode 100644 tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index e70ec18340add..0eb1e881896ba 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -291,6 +291,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { is_ty_must_use(cx, pinned_ty, expr, span) .map(|inner| MustUsePath::Pinned(Box::new(inner))) } + // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && args.type_at(0).is_unit() + && !args.type_at(1).is_inhabited_from( + cx.tcx, + parent_mod_did, + cx.typing_env(), + ) => + { + Some(MustUsePath::Suppressed) + } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) diff --git a/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs b/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs new file mode 100644 index 0000000000000..a193dfa8c47bc --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs @@ -0,0 +1,4 @@ +pub enum MyUninhabited {} + +#[non_exhaustive] +pub enum MyUninhabitedNonexhaustive {} diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs new file mode 100644 index 0000000000000..1a7facb91a9a7 --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -0,0 +1,55 @@ +//@ edition: 2024 +//@ aux-crate:dep=must_use_result_unit_uninhabited_extern_crate.rs + +#![deny(unused_must_use)] +#![feature(never_type)] + +use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; + +fn f1() -> Result<(), ()> { + Ok(()) +} + +fn f2() -> Result<(), core::convert::Infallible> { + Ok(()) +} + +fn f3() -> Result<(), !> { + Ok(()) +} + +fn f4() -> Result<(), MyUninhabited> { + Ok(()) +} + +fn f5() -> Result<(), MyUninhabitedNonexhaustive> { + Ok(()) +} + +trait AssocType { + type Error; +} + +struct S1; +impl AssocType for S1 { + type Error = !; +} + +struct S2; +impl AssocType for S2 { + type Error = (); +} + +fn f6(_: AT) -> Result<(), AT::Error> { + Ok(()) +} + +fn main() { + f1(); //~ ERROR: unused `Result` that must be used + f2(); + f3(); + f4(); + f5(); //~ ERROR: unused `Result` that must be used + f6(S1); + f6(S2); //~ ERROR: unused `Result` that must be used +} diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr new file mode 100644 index 0000000000000..0ee06b0504ad0 --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -0,0 +1,43 @@ +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:48:5 + | +LL | f1(); + | ^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +note: the lint level is defined here + --> $DIR/must_use-result-unit-uninhabited.rs:4:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f1(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:52:5 + | +LL | f5(); + | ^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f5(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:54:5 + | +LL | f6(S2); + | ^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f6(S2); + | +++++++ + +error: aborting due to 3 previous errors + From efd76c910ecf24329218df0fbba74293f1da4399 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 5 Oct 2025 10:45:16 -0700 Subject: [PATCH 1519/1900] unused_must_use: Add test for a method using an associated error type --- .../must_use-result-unit-uninhabited.rs | 21 +++++++++++++++++++ .../must_use-result-unit-uninhabited.stderr | 20 ++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs index 1a7facb91a9a7..340e4abe0cdfe 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -44,6 +44,25 @@ fn f6(_: AT) -> Result<(), AT::Error> { Ok(()) } +trait UsesAssocType { + type Error; + fn method(&self) -> Result<(), Self::Error>; +} + +impl UsesAssocType for S1 { + type Error = !; + fn method(&self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl UsesAssocType for S2 { + type Error = (); + fn method(&self) -> Result<(), Self::Error> { + Err(()) + } +} + fn main() { f1(); //~ ERROR: unused `Result` that must be used f2(); @@ -52,4 +71,6 @@ fn main() { f5(); //~ ERROR: unused `Result` that must be used f6(S1); f6(S2); //~ ERROR: unused `Result` that must be used + S1.method(); + S2.method(); //~ ERROR: unused `Result` that must be used } diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index 0ee06b0504ad0..8e1f300f6e7ff 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -1,5 +1,5 @@ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:48:5 + --> $DIR/must_use-result-unit-uninhabited.rs:67:5 | LL | f1(); | ^^^^ @@ -16,7 +16,7 @@ LL | let _ = f1(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:52:5 + --> $DIR/must_use-result-unit-uninhabited.rs:71:5 | LL | f5(); | ^^^^ @@ -28,7 +28,7 @@ LL | let _ = f5(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:54:5 + --> $DIR/must_use-result-unit-uninhabited.rs:73:5 | LL | f6(S2); | ^^^^^^ @@ -39,5 +39,17 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = f6(S2); | +++++++ -error: aborting due to 3 previous errors +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:75:5 + | +LL | S2.method(); + | ^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = S2.method(); + | +++++++ + +error: aborting due to 4 previous errors From 39f59bf6a23a2912e69a8ac43e8c1e9571e7194d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 5 Oct 2025 21:40:26 -0700 Subject: [PATCH 1520/1900] unused_must_use: Suppress warnings for `ControlFlow` too --- compiler/rustc_lint/src/unused.rs | 12 +++++++++++ .../must_use-result-unit-uninhabited.rs | 17 +++++++++++++++ .../must_use-result-unit-uninhabited.stderr | 21 ++++++++++++++----- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0eb1e881896ba..936c67a76a490 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -303,6 +303,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { { Some(MustUsePath::Suppressed) } + // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && args.type_at(1).is_unit() + && !args.type_at(0).is_inhabited_from( + cx.tcx, + parent_mod_did, + cx.typing_env(), + ) => + { + Some(MustUsePath::Suppressed) + } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs index 340e4abe0cdfe..cd319545bb305 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -4,6 +4,7 @@ #![deny(unused_must_use)] #![feature(never_type)] +use core::ops::{ControlFlow, ControlFlow::Continue}; use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; fn f1() -> Result<(), ()> { @@ -63,6 +64,18 @@ impl UsesAssocType for S2 { } } +fn c1() -> ControlFlow<()> { + Continue(()) +} + +fn c2() -> ControlFlow { + Continue(()) +} + +fn c3() -> ControlFlow { + Continue(()) +} + fn main() { f1(); //~ ERROR: unused `Result` that must be used f2(); @@ -73,4 +86,8 @@ fn main() { f6(S2); //~ ERROR: unused `Result` that must be used S1.method(); S2.method(); //~ ERROR: unused `Result` that must be used + + c1(); //~ ERROR: unused `ControlFlow` that must be used + c2(); + c3(); } diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index 8e1f300f6e7ff..cbe4749fda31b 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -1,5 +1,5 @@ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:67:5 + --> $DIR/must_use-result-unit-uninhabited.rs:80:5 | LL | f1(); | ^^^^ @@ -16,7 +16,7 @@ LL | let _ = f1(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:71:5 + --> $DIR/must_use-result-unit-uninhabited.rs:84:5 | LL | f5(); | ^^^^ @@ -28,7 +28,7 @@ LL | let _ = f5(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:73:5 + --> $DIR/must_use-result-unit-uninhabited.rs:86:5 | LL | f6(S2); | ^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = f6(S2); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:75:5 + --> $DIR/must_use-result-unit-uninhabited.rs:88:5 | LL | S2.method(); | ^^^^^^^^^^^ @@ -51,5 +51,16 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = S2.method(); | +++++++ -error: aborting due to 4 previous errors +error: unused `ControlFlow` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:90:5 + | +LL | c1(); + | ^^^^ + | +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = c1(); + | +++++++ + +error: aborting due to 5 previous errors From 339caa11c7a71890047d0eeab2e6ba6a17673b49 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 6 Oct 2025 08:06:48 -0700 Subject: [PATCH 1521/1900] unused_must_use: Rename test functions to reflect what they test This makes it easier to review without cross-referencing each test function with its invocation. --- .../must_use-result-unit-uninhabited.rs | 50 +++++++++---------- .../must_use-result-unit-uninhabited.stderr | 30 +++++------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs index cd319545bb305..d0dd8d2372092 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -7,23 +7,23 @@ use core::ops::{ControlFlow, ControlFlow::Continue}; use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; -fn f1() -> Result<(), ()> { +fn result_unit_unit() -> Result<(), ()> { Ok(()) } -fn f2() -> Result<(), core::convert::Infallible> { +fn result_unit_infallible() -> Result<(), core::convert::Infallible> { Ok(()) } -fn f3() -> Result<(), !> { +fn result_unit_never() -> Result<(), !> { Ok(()) } -fn f4() -> Result<(), MyUninhabited> { +fn result_unit_myuninhabited() -> Result<(), MyUninhabited> { Ok(()) } -fn f5() -> Result<(), MyUninhabitedNonexhaustive> { +fn result_unit_myuninhabited_nonexhaustive() -> Result<(), MyUninhabitedNonexhaustive> { Ok(()) } @@ -41,53 +41,53 @@ impl AssocType for S2 { type Error = (); } -fn f6(_: AT) -> Result<(), AT::Error> { +fn result_unit_assoctype(_: AT) -> Result<(), AT::Error> { Ok(()) } trait UsesAssocType { type Error; - fn method(&self) -> Result<(), Self::Error>; + fn method_use_assoc_type(&self) -> Result<(), Self::Error>; } impl UsesAssocType for S1 { type Error = !; - fn method(&self) -> Result<(), Self::Error> { + fn method_use_assoc_type(&self) -> Result<(), Self::Error> { Ok(()) } } impl UsesAssocType for S2 { type Error = (); - fn method(&self) -> Result<(), Self::Error> { + fn method_use_assoc_type(&self) -> Result<(), Self::Error> { Err(()) } } -fn c1() -> ControlFlow<()> { +fn controlflow_unit() -> ControlFlow<()> { Continue(()) } -fn c2() -> ControlFlow { +fn controlflow_infallible_unit() -> ControlFlow { Continue(()) } -fn c3() -> ControlFlow { +fn controlflow_never() -> ControlFlow { Continue(()) } fn main() { - f1(); //~ ERROR: unused `Result` that must be used - f2(); - f3(); - f4(); - f5(); //~ ERROR: unused `Result` that must be used - f6(S1); - f6(S2); //~ ERROR: unused `Result` that must be used - S1.method(); - S2.method(); //~ ERROR: unused `Result` that must be used - - c1(); //~ ERROR: unused `ControlFlow` that must be used - c2(); - c3(); + result_unit_unit(); //~ ERROR: unused `Result` that must be used + result_unit_infallible(); + result_unit_never(); + result_unit_myuninhabited(); + result_unit_myuninhabited_nonexhaustive(); //~ ERROR: unused `Result` that must be used + result_unit_assoctype(S1); + result_unit_assoctype(S2); //~ ERROR: unused `Result` that must be used + S1.method_use_assoc_type(); + S2.method_use_assoc_type(); //~ ERROR: unused `Result` that must be used + + controlflow_unit(); //~ ERROR: unused `ControlFlow` that must be used + controlflow_infallible_unit(); + controlflow_never(); } diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index cbe4749fda31b..90ac44d0c6c6f 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -1,8 +1,8 @@ error: unused `Result` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:80:5 | -LL | f1(); - | ^^^^ +LL | result_unit_unit(); + | ^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled note: the lint level is defined here @@ -12,54 +12,54 @@ LL | #![deny(unused_must_use)] | ^^^^^^^^^^^^^^^ help: use `let _ = ...` to ignore the resulting value | -LL | let _ = f1(); +LL | let _ = result_unit_unit(); | +++++++ error: unused `Result` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:84:5 | -LL | f5(); - | ^^^^ +LL | result_unit_myuninhabited_nonexhaustive(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled help: use `let _ = ...` to ignore the resulting value | -LL | let _ = f5(); +LL | let _ = result_unit_myuninhabited_nonexhaustive(); | +++++++ error: unused `Result` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:86:5 | -LL | f6(S2); - | ^^^^^^ +LL | result_unit_assoctype(S2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled help: use `let _ = ...` to ignore the resulting value | -LL | let _ = f6(S2); +LL | let _ = result_unit_assoctype(S2); | +++++++ error: unused `Result` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:88:5 | -LL | S2.method(); - | ^^^^^^^^^^^ +LL | S2.method_use_assoc_type(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled help: use `let _ = ...` to ignore the resulting value | -LL | let _ = S2.method(); +LL | let _ = S2.method_use_assoc_type(); | +++++++ error: unused `ControlFlow` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:90:5 | -LL | c1(); - | ^^^^ +LL | controlflow_unit(); + | ^^^^^^^^^^^^^^^^^^ | help: use `let _ = ...` to ignore the resulting value | -LL | let _ = c1(); +LL | let _ = controlflow_unit(); | +++++++ error: aborting due to 5 previous errors From 2d95dfd5a93531996b41b210480accd0cb199b73 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 6 Oct 2025 08:21:34 -0700 Subject: [PATCH 1522/1900] unused_must_use: Add extra test for types that are still generic --- .../unused/must_use-result-unit-uninhabited.rs | 8 ++++++++ .../unused/must_use-result-unit-uninhabited.stderr | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs index d0dd8d2372092..8f63e4a7f8323 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -91,3 +91,11 @@ fn main() { controlflow_infallible_unit(); controlflow_never(); } + +trait AssocTypeBeforeMonomorphisation { + type Error; + fn generate(&self) -> Result<(), Self::Error>; + fn process(&self) { + self.generate(); //~ ERROR: unused `Result` that must be used + } +} diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index 90ac44d0c6c6f..31d6f6bcf2bc7 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -62,5 +62,17 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = controlflow_unit(); | +++++++ -error: aborting due to 5 previous errors +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:99:9 + | +LL | self.generate(); + | ^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = self.generate(); + | +++++++ + +error: aborting due to 6 previous errors From 7aa7ecc98234144dcdde6e2e9326ac806c0f36c3 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 10 Oct 2025 13:13:49 -0700 Subject: [PATCH 1523/1900] Factor out a helper function to check if a type is uninhabited --- compiler/rustc_lint/src/unused.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 936c67a76a490..cac373b3271a6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -277,7 +277,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return Some(MustUsePath::Suppressed); } let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); - if !ty.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()) { + let is_uninhabited = + |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); + if is_uninhabited(ty) { return Some(MustUsePath::Suppressed); } @@ -295,11 +297,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::Result, def.did()) && args.type_at(0).is_unit() - && !args.type_at(1).is_inhabited_from( - cx.tcx, - parent_mod_did, - cx.typing_env(), - ) => + && is_uninhabited(args.type_at(1)) => { Some(MustUsePath::Suppressed) } @@ -307,11 +305,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) && args.type_at(1).is_unit() - && !args.type_at(0).is_inhabited_from( - cx.tcx, - parent_mod_did, - cx.typing_env(), - ) => + && is_uninhabited(args.type_at(0)) => { Some(MustUsePath::Suppressed) } From 6a12470210aefa46d6078e27bcd20f6ef896c93a Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 10 Oct 2025 22:19:04 +0200 Subject: [PATCH 1524/1900] rename `DecodeMut` to `Decode` --- library/proc_macro/src/bridge/client.rs | 6 ++--- library/proc_macro/src/bridge/mod.rs | 4 +-- library/proc_macro/src/bridge/rpc.rs | 36 ++++++++++++------------- library/proc_macro/src/bridge/server.rs | 10 +++---- library/proc_macro/src/bridge/symbol.rs | 4 +-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 92558f2b7d9cc..bdaa865a998d6 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -58,7 +58,7 @@ macro_rules! define_client_handles { } } - impl DecodeMut<'_, '_, S> for $oty { + impl Decode<'_, '_, S> for $oty { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $oty { handle: handle::Handle::decode(r, s), @@ -82,7 +82,7 @@ macro_rules! define_client_handles { } } - impl DecodeMut<'_, '_, S> for $ity { + impl Decode<'_, '_, S> for $ity { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $ity { handle: handle::Handle::decode(r, s), @@ -276,7 +276,7 @@ fn maybe_install_panic_hook(force_show_panics: bool) { /// Client-side helper for handling client panics, entering the bridge, /// deserializing input and serializing output. // FIXME(eddyb) maybe replace `Bridge::enter` with this? -fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( +fn run_client Decode<'a, 's, ()>, R: Encode<()>>( config: BridgeConfig<'_>, f: impl FnOnce(A) -> R, ) -> Buffer { diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 582c43c78fcbb..b0ee9c0cc3027 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -143,7 +143,7 @@ mod symbol; use buffer::Buffer; pub use rpc::PanicMessage; -use rpc::{DecodeMut, Encode, Reader, Writer}; +use rpc::{Decode, Encode, Reader, Writer}; /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), @@ -168,7 +168,7 @@ impl !Sync for BridgeConfig<'_> {} #[forbid(unsafe_code)] #[allow(non_camel_case_types)] mod api_tags { - use super::rpc::{DecodeMut, Encode, Reader, Writer}; + use super::rpc::{Decode, Encode, Reader, Writer}; macro_rules! declare_tags { ($($name:ident { diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 6e5afe282d683..ed67674a74ab7 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -12,7 +12,7 @@ pub(super) trait Encode: Sized { pub(super) type Reader<'a> = &'a [u8]; -pub(super) trait DecodeMut<'a, 's, S>: Sized { +pub(super) trait Decode<'a, 's, S>: Sized { fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; } @@ -24,7 +24,7 @@ macro_rules! rpc_encode_decode { } } - impl DecodeMut<'_, '_, S> for $ty { + impl Decode<'_, '_, S> for $ty { fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { const N: usize = size_of::<$ty>(); @@ -43,12 +43,12 @@ macro_rules! rpc_encode_decode { } } - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { $name { - $($field: DecodeMut::decode(r, s)),* + $($field: Decode::decode(r, s)),* } } } @@ -69,7 +69,7 @@ macro_rules! rpc_encode_decode { } } - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { @@ -84,7 +84,7 @@ macro_rules! rpc_encode_decode { match u8::decode(r, s) { $(tag::$variant => { - $(let $field = DecodeMut::decode(r, s);)* + $(let $field = Decode::decode(r, s);)* $name::$variant $(($field))* })* _ => unreachable!(), @@ -98,7 +98,7 @@ impl Encode for () { fn encode(self, _: &mut Writer, _: &mut S) {} } -impl DecodeMut<'_, '_, S> for () { +impl Decode<'_, '_, S> for () { fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} } @@ -108,7 +108,7 @@ impl Encode for u8 { } } -impl DecodeMut<'_, '_, S> for u8 { +impl Decode<'_, '_, S> for u8 { fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { let x = r[0]; *r = &r[1..]; @@ -125,7 +125,7 @@ impl Encode for bool { } } -impl DecodeMut<'_, '_, S> for bool { +impl Decode<'_, '_, S> for bool { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { match u8::decode(r, s) { 0 => false, @@ -141,7 +141,7 @@ impl Encode for char { } } -impl DecodeMut<'_, '_, S> for char { +impl Decode<'_, '_, S> for char { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { char::from_u32(u32::decode(r, s)).unwrap() } @@ -153,7 +153,7 @@ impl Encode for NonZero { } } -impl DecodeMut<'_, '_, S> for NonZero { +impl Decode<'_, '_, S> for NonZero { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { Self::new(u32::decode(r, s)).unwrap() } @@ -166,11 +166,11 @@ impl, B: Encode> Encode for (A, B) { } } -impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> +impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for (A, B) { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - (DecodeMut::decode(r, s), DecodeMut::decode(r, s)) + (Decode::decode(r, s), Decode::decode(r, s)) } } @@ -181,7 +181,7 @@ impl Encode for &[u8] { } } -impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] { +impl<'a, S> Decode<'a, '_, S> for &'a [u8] { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { let len = usize::decode(r, s); let xs = &r[..len]; @@ -196,7 +196,7 @@ impl Encode for &str { } } -impl<'a, S> DecodeMut<'a, '_, S> for &'a str { +impl<'a, S> Decode<'a, '_, S> for &'a str { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { str::from_utf8(<&[u8]>::decode(r, s)).unwrap() } @@ -208,7 +208,7 @@ impl Encode for String { } } -impl DecodeMut<'_, '_, S> for String { +impl Decode<'_, '_, S> for String { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { <&str>::decode(r, s).to_string() } @@ -223,7 +223,7 @@ impl> Encode for Vec { } } -impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec { +impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { let len = usize::decode(r, s); let mut vec = Vec::with_capacity(len); @@ -283,7 +283,7 @@ impl Encode for PanicMessage { } } -impl DecodeMut<'_, '_, S> for PanicMessage { +impl Decode<'_, '_, S> for PanicMessage { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { match Option::::decode(r, s) { Some(s) => PanicMessage::String(s), diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 2850e1099b700..e9ef26c07f24f 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -32,7 +32,7 @@ macro_rules! define_server_handles { } } - impl DecodeMut<'_, '_, HandleStore>> + impl Decode<'_, '_, HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { @@ -40,7 +40,7 @@ macro_rules! define_server_handles { } } - impl<'s, S: Types> DecodeMut<'_, 's, HandleStore>> + impl<'s, S: Types> Decode<'_, 's, HandleStore>> for &'s Marked { fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { @@ -48,7 +48,7 @@ macro_rules! define_server_handles { } } - impl<'s, S: Types> DecodeMut<'_, 's, HandleStore>> + impl<'s, S: Types> Decode<'_, 's, HandleStore>> for &'s mut Marked { fn decode( @@ -67,7 +67,7 @@ macro_rules! define_server_handles { } } - impl DecodeMut<'_, '_, HandleStore>> + impl Decode<'_, '_, HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { @@ -355,7 +355,7 @@ pub trait MessagePipe: Sized { fn run_server< S: Server, I: Encode>>, - O: for<'a, 's> DecodeMut<'a, 's, HandleStore>>, + O: for<'a, 's> Decode<'a, 's, HandleStore>>, >( strategy: &impl ExecutionStrategy, handle_counters: &'static client::HandleCounters, diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index eb7d30f9a6cc9..0d6a725fddd98 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -102,7 +102,7 @@ impl Encode for Symbol { } } -impl DecodeMut<'_, '_, server::HandleStore>> +impl Decode<'_, '_, server::HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore>) -> Self { @@ -118,7 +118,7 @@ impl Encode>> } } -impl DecodeMut<'_, '_, S> for Symbol { +impl Decode<'_, '_, S> for Symbol { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { Symbol::new(<&str>::decode(r, s)) } From a3482d97c6442d00097b47c846a90d9688c56e2c Mon Sep 17 00:00:00 2001 From: dianqk Date: Fri, 10 Oct 2025 20:29:59 +0800 Subject: [PATCH 1525/1900] Remove the temporary directory when a check ends --- src/bootstrap/src/core/build_steps/test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 00aea8feab7de..f1de43bd4624a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3262,6 +3262,8 @@ fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) { .env("GITHUB_ACTIONS", "0") .current_dir(plain_src_dir) .run(builder); + // Mitigate pressure on small-capacity disks. + builder.remove_dir(plain_src_dir); } /// Check that rust-src has all of libstd's dependencies @@ -3287,6 +3289,8 @@ fn distcheck_rust_src(builder: &Builder<'_>, src_dir: &Path) { .arg(&toml) .current_dir(src_dir) .run(builder); + // Mitigate pressure on small-capacity disks. + builder.remove_dir(src_dir); } /// Check that rustc-dev's compiler crate source code can be loaded with `cargo metadata` @@ -3311,6 +3315,8 @@ fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) { .env("RUSTC", &builder.initial_rustc) .current_dir(dir) .run(builder); + // Mitigate pressure on small-capacity disks. + builder.remove_dir(dir); } #[derive(Debug, Clone, PartialEq, Eq, Hash)] From e4ead0ec7027ea819dd93a3f10fe3cd1157337c4 Mon Sep 17 00:00:00 2001 From: Dawid Lachowicz Date: Fri, 25 Jul 2025 07:50:46 +0100 Subject: [PATCH 1526/1900] Guard HIR lowered contracts with contract_checks Refactor contract HIR lowering to ensure no contract code is executed when contract-checks are disabled. The call to contract_checks is moved to inside the lowered fn body, and contract closures are built conditionally, ensuring no side-effects present in contracts occur when those are disabled. --- compiler/rustc_ast_lowering/src/contract.rs | 260 ++++++++++++++++++ compiler/rustc_ast_lowering/src/expr.rs | 86 +++--- compiler/rustc_ast_lowering/src/item.rs | 71 +---- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 6 + library/core/src/intrinsics/mod.rs | 25 +- .../contracts-disabled-side-effect-ensures.rs | 17 ++ ...tracts-disabled-side-effect-ensures.stderr | 11 + tests/ui/contracts/empty-ensures.rs | 16 ++ tests/ui/contracts/empty-ensures.stderr | 25 ++ tests/ui/contracts/empty-requires.rs | 18 ++ tests/ui/contracts/empty-requires.stderr | 18 ++ .../internal_machinery/contract-intrinsics.rs | 12 +- .../internal_machinery/contract-lang-items.rs | 6 +- .../internal-feature-gating.rs | 2 +- .../internal-feature-gating.stderr | 2 +- 18 files changed, 461 insertions(+), 120 deletions(-) create mode 100644 compiler/rustc_ast_lowering/src/contract.rs create mode 100644 tests/ui/contracts/contracts-disabled-side-effect-ensures.rs create mode 100644 tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr create mode 100644 tests/ui/contracts/empty-ensures.rs create mode 100644 tests/ui/contracts/empty-ensures.stderr create mode 100644 tests/ui/contracts/empty-requires.rs create mode 100644 tests/ui/contracts/empty-requires.stderr diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs new file mode 100644 index 0000000000000..ad49cd2a42423 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -0,0 +1,260 @@ +use crate::LoweringContext; + +impl<'a, 'hir> LoweringContext<'a, 'hir> { + pub(super) fn lower_contract( + &mut self, + body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, + contract: &rustc_ast::FnContract, + ) -> rustc_hir::Expr<'hir> { + match (&contract.requires, &contract.ensures) { + (Some(req), Some(ens)) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // { + // let __postcond = if contracts_checks() { + // contract_check_requires(PRECOND); + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; + // contract_check_ensures(__postcond, { body }) + // } + + let precond = self.lower_precond(req); + let postcond_checker = self.lower_postcond_checker(ens); + + let contract_check = + self.lower_contract_check_with_postcond(Some(precond), postcond_checker); + + let wrapped_body = + self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); + self.expr_block(wrapped_body) + } + (None, Some(ens)) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // { + // let __postcond = if contracts_check() { + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; + // __postcond({ body }) + // } + + let postcond_checker = self.lower_postcond_checker(ens); + let contract_check = + self.lower_contract_check_with_postcond(None, postcond_checker); + + let wrapped_body = + self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); + self.expr_block(wrapped_body) + } + (Some(req), None) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // { + // if contracts_check() { + // contract_requires(PRECOND); + // } + // body + // } + let precond = self.lower_precond(req); + let precond_check = self.lower_contract_check_just_precond(precond); + + let body = self.arena.alloc(body(self)); + + // Flatten the body into precond check, then body. + let wrapped_body = self.block_all( + body.span, + self.arena.alloc_from_iter([precond_check].into_iter()), + Some(body), + ); + self.expr_block(wrapped_body) + } + (None, None) => body(self), + } + } + + /// Lower the precondition check intrinsic. + fn lower_precond(&mut self, req: &Box) -> rustc_hir::Stmt<'hir> { + let lowered_req = self.lower_expr_mut(&req); + let req_span = self.mark_span_with_reason( + rustc_span::DesugaringKind::Contract, + lowered_req.span, + None, + ); + let precond = self.expr_call_lang_item_fn_mut( + req_span, + rustc_hir::LangItem::ContractCheckRequires, + &*arena_vec![self; lowered_req], + ); + self.stmt_expr(req.span, precond) + } + + fn lower_postcond_checker( + &mut self, + ens: &Box, + ) -> &'hir rustc_hir::Expr<'hir> { + let ens_span = self.lower_span(ens.span); + let ens_span = + self.mark_span_with_reason(rustc_span::DesugaringKind::Contract, ens_span, None); + let lowered_ens = self.lower_expr_mut(&ens); + self.expr_call_lang_item_fn( + ens_span, + rustc_hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![self; lowered_ens], + ) + } + + fn lower_contract_check_just_precond( + &mut self, + precond: rustc_hir::Stmt<'hir>, + ) -> rustc_hir::Stmt<'hir> { + let stmts = self.arena.alloc_from_iter([precond].into_iter()); + + let then_block_stmts = self.block_all(precond.span, stmts, None); + let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); + + let precond_check = rustc_hir::ExprKind::If( + self.expr_call_lang_item_fn( + precond.span, + rustc_hir::LangItem::ContractChecks, + Default::default(), + ), + then_block, + None, + ); + + let precond_check = self.expr(precond.span, precond_check); + self.stmt_expr(precond.span, precond_check) + } + + fn lower_contract_check_with_postcond( + &mut self, + precond: Option>, + postcond_checker: &'hir rustc_hir::Expr<'hir>, + ) -> &'hir rustc_hir::Expr<'hir> { + let stmts = self.arena.alloc_from_iter(precond.into_iter()); + let span = match precond { + Some(precond) => precond.span, + None => postcond_checker.span, + }; + + let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item( + postcond_checker.span, + rustc_hir::lang_items::LangItem::OptionSome, + &*arena_vec![self; *postcond_checker], + )); + let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker)); + let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); + + let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item( + postcond_checker.span, + rustc_hir::lang_items::LangItem::OptionNone, + Default::default(), + )); + let else_block = self.block_expr(none_expr); + let else_block = self.arena.alloc(self.expr_block(else_block)); + + let contract_check = rustc_hir::ExprKind::If( + self.expr_call_lang_item_fn( + span, + rustc_hir::LangItem::ContractChecks, + Default::default(), + ), + then_block, + Some(else_block), + ); + self.arena.alloc(self.expr(span, contract_check)) + } + + fn wrap_body_with_contract_check( + &mut self, + body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, + contract_check: &'hir rustc_hir::Expr<'hir>, + postcond_span: rustc_span::Span, + ) -> &'hir rustc_hir::Block<'hir> { + let check_ident: rustc_span::Ident = + rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span); + let (check_hir_id, postcond_decl) = { + // Set up the postcondition `let` statement. + let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut( + postcond_span, + check_ident, + rustc_hir::BindingMode::NONE, + ); + ( + check_hir_id, + self.stmt_let_pat( + None, + postcond_span, + Some(contract_check), + self.arena.alloc(checker_pat), + rustc_hir::LocalSource::Contract, + ), + ) + }; + + // Install contract_ensures so we will intercept `return` statements, + // then lower the body. + self.contract_ensures = Some((postcond_span, check_ident, check_hir_id)); + let body = self.arena.alloc(body(self)); + + // Finally, inject an ensures check on the implicit return of the body. + let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id); + + // Flatten the body into precond, then postcond, then wrapped body. + let wrapped_body = self.block_all( + body.span, + self.arena.alloc_from_iter([postcond_decl].into_iter()), + Some(body), + ); + wrapped_body + } + + /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check + /// a contract ensures clause, if it exists. + pub(super) fn checked_return( + &mut self, + opt_expr: Option<&'hir rustc_hir::Expr<'hir>>, + ) -> rustc_hir::ExprKind<'hir> { + let checked_ret = + if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { + let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); + Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) + } else { + opt_expr + }; + rustc_hir::ExprKind::Ret(checked_ret) + } + + /// Wraps an expression with a call to the ensures check before it gets returned. + pub(super) fn inject_ensures_check( + &mut self, + expr: &'hir rustc_hir::Expr<'hir>, + span: rustc_span::Span, + cond_ident: rustc_span::Ident, + cond_hir_id: rustc_hir::HirId, + ) -> &'hir rustc_hir::Expr<'hir> { + let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); + let call_expr = self.expr_call_lang_item_fn_mut( + span, + rustc_hir::LangItem::ContractCheckEnsures, + arena_vec![self; *cond_fn, *expr], + ); + self.arena.alloc(call_expr) + } +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index bb6b25baf013d..d1ce3b169d75f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -383,36 +383,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check - /// a contract ensures clause, if it exists. - fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { - let checked_ret = - if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { - let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); - Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) - } else { - opt_expr - }; - hir::ExprKind::Ret(checked_ret) - } - - /// Wraps an expression with a call to the ensures check before it gets returned. - pub(crate) fn inject_ensures_check( - &mut self, - expr: &'hir hir::Expr<'hir>, - span: Span, - cond_ident: Ident, - cond_hir_id: HirId, - ) -> &'hir hir::Expr<'hir> { - let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); - let call_expr = self.expr_call_lang_item_fn_mut( - span, - hir::LangItem::ContractCheckEnsures, - arena_vec![self; *cond_fn, *expr], - ); - self.arena.alloc(call_expr) - } - pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); @@ -2120,7 +2090,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e)) } - fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> { + pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> { self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]))) } @@ -2161,6 +2131,43 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Call(e, args)) } + pub(super) fn expr_struct( + &mut self, + span: Span, + path: &'hir hir::QPath<'hir>, + fields: &'hir [hir::ExprField<'hir>], + ) -> hir::Expr<'hir> { + self.expr(span, hir::ExprKind::Struct(path, fields, rustc_hir::StructTailExpr::None)) + } + + pub(super) fn expr_enum_variant( + &mut self, + span: Span, + path: &'hir hir::QPath<'hir>, + fields: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + let fields = self.arena.alloc_from_iter(fields.into_iter().enumerate().map(|(i, f)| { + hir::ExprField { + hir_id: self.next_id(), + ident: Ident::from_str(&i.to_string()), + expr: f, + span: f.span, + is_shorthand: false, + } + })); + self.expr_struct(span, path, fields) + } + + pub(super) fn expr_enum_variant_lang_item( + &mut self, + span: Span, + lang_item: hir::LangItem, + fields: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + let path = self.arena.alloc(self.lang_item_path(span, lang_item)); + self.expr_enum_variant(span, path, fields) + } + pub(super) fn expr_call( &mut self, span: Span, @@ -2189,8 +2196,21 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args)) } - fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> { - self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span)))) + pub(super) fn expr_lang_item_path( + &mut self, + span: Span, + lang_item: hir::LangItem, + ) -> hir::Expr<'hir> { + let path = self.lang_item_path(span, lang_item); + self.expr(span, hir::ExprKind::Path(path)) + } + + pub(super) fn lang_item_path( + &mut self, + span: Span, + lang_item: hir::LangItem, + ) -> hir::QPath<'hir> { + hir::QPath::LangItem(lang_item, self.lower_span(span)) } /// `::name` diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 53351f91c46bc..d11b7f798b6db 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1214,76 +1214,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let params = this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); - // Optionally lower the fn contract, which turns: - // - // { body } - // - // into: - // - // { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) } + // Optionally lower the fn contract if let Some(contract) = contract { - let precond = if let Some(req) = &contract.requires { - // Lower the precondition check intrinsic. - let lowered_req = this.lower_expr_mut(&req); - let req_span = this.mark_span_with_reason( - DesugaringKind::Contract, - lowered_req.span, - None, - ); - let precond = this.expr_call_lang_item_fn_mut( - req_span, - hir::LangItem::ContractCheckRequires, - &*arena_vec![this; lowered_req], - ); - Some(this.stmt_expr(req.span, precond)) - } else { - None - }; - let (postcond, body) = if let Some(ens) = &contract.ensures { - let ens_span = this.lower_span(ens.span); - let ens_span = - this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None); - // Set up the postcondition `let` statement. - let check_ident: Ident = - Ident::from_str_and_span("__ensures_checker", ens_span); - let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut( - ens_span, - check_ident, - hir::BindingMode::NONE, - ); - let lowered_ens = this.lower_expr_mut(&ens); - let postcond_checker = this.expr_call_lang_item_fn( - ens_span, - hir::LangItem::ContractBuildCheckEnsures, - &*arena_vec![this; lowered_ens], - ); - let postcond = this.stmt_let_pat( - None, - ens_span, - Some(postcond_checker), - this.arena.alloc(checker_pat), - hir::LocalSource::Contract, - ); - - // Install contract_ensures so we will intercept `return` statements, - // then lower the body. - this.contract_ensures = Some((ens_span, check_ident, check_hir_id)); - let body = this.arena.alloc(body(this)); - - // Finally, inject an ensures check on the implicit return of the body. - let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id); - (Some(postcond), body) - } else { - let body = &*this.arena.alloc(body(this)); - (None, body) - }; - // Flatten the body into precond, then postcond, then wrapped body. - let wrapped_body = this.block_all( - body.span, - this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()), - Some(body), - ); - (params, this.expr_block(wrapped_body)) + (params, this.lower_contract(body, contract)) } else { (params, body(this)) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4e2243e87873c..282a62ca44310 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -77,6 +77,7 @@ macro_rules! arena_vec { mod asm; mod block; +mod contract; mod delegation; mod errors; mod expr; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2e099a97b65be..d62ec5482b430 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -429,6 +429,7 @@ language_item_table! { // Experimental lang items for implementing contract pre- and post-condition checking. ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; + ContractChecks, sym::contract_checks, contract_checks_fn, Target::Fn, GenericRequirement::None; // Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727) DefaultTrait4, sym::default_trait4, default_trait4_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bc3448be5823e..5fd71fd1cd76c 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -651,7 +651,9 @@ pub(crate) fn check_intrinsic_type( sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), - sym::contract_check_ensures => (2, 0, vec![param(0), param(1)], param(1)), + sym::contract_check_ensures => { + (2, 0, vec![Ty::new_option(tcx, param(0)), param(1)], param(1)) + } sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { (2, 0, vec![param(0), param(0)], param(1)) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index de35e5e847c81..7f44217971833 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -903,6 +903,12 @@ impl<'tcx> Ty<'tcx> { Ty::new_generic_adt(tcx, def_id, ty) } + #[inline] + pub fn new_option(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = tcx.require_lang_item(LangItem::Option, DUMMY_SP); + Ty::new_generic_adt(tcx, def_id, ty) + } + #[inline] pub fn new_maybe_uninit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let def_id = tcx.require_lang_item(LangItem::MaybeUninit, DUMMY_SP); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index cef700be9ea1f..0d3be136d1325 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2637,9 +2637,10 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 { /// of not prematurely committing at compile-time to whether contract /// checking is turned on, so that we can specify contracts in libstd /// and let an end user opt into turning them on. -#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[inline(always)] +#[lang = "contract_checks"] #[rustc_intrinsic] pub const fn contract_checks() -> bool { // FIXME: should this be `false` or `cfg!(contract_checks)`? @@ -2668,7 +2669,7 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { if const { // Do nothing } else { - if contract_checks() && !cond() { + if !cond() { // Emit no unwind panic in case this was a safety requirement. crate::panicking::panic_nounwind("failed requires check"); } @@ -2681,6 +2682,8 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. /// +/// If `cond` is `None`, then no postcondition checking is performed. +/// /// Note that this function is a no-op during constant evaluation. #[unstable(feature = "contracts_internals", issue = "128044")] // Similar to `contract_check_requires`, we need to use the user-facing @@ -2689,16 +2692,24 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { #[rustc_const_unstable(feature = "contracts", issue = "128044")] #[lang = "contract_check_ensures"] #[rustc_intrinsic] -pub const fn contract_check_ensures bool + Copy, Ret>(cond: C, ret: Ret) -> Ret { +pub const fn contract_check_ensures bool + Copy, Ret>( + cond: Option, + ret: Ret, +) -> Ret { const_eval_select!( - @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret : + @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: Option, ret: Ret } -> Ret : if const { // Do nothing ret } else { - if contract_checks() && !cond(&ret) { - // Emit no unwind panic in case this was a safety requirement. - crate::panicking::panic_nounwind("failed ensures check"); + match cond { + crate::option::Option::Some(cond) => { + if !cond(&ret) { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed ensures check"); + } + }, + crate::option::Option::None => {}, } ret } diff --git a/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs new file mode 100644 index 0000000000000..d234acb8268a1 --- /dev/null +++ b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs @@ -0,0 +1,17 @@ +//@ run-pass +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::ensures; + +#[ensures({*x = 0; |_ret| true})] +fn buggy_add(x: &mut u32, y: u32) { + *x = *x + y; +} + +fn main() { + let mut x = 10; + buggy_add(&mut x, 100); + assert_eq!(x, 110); +} diff --git a/tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr b/tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr new file mode 100644 index 0000000000000..dd9ebe9bd3556 --- /dev/null +++ b/tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-disabled-side-effect-ensures.rs:2:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs new file mode 100644 index 0000000000000..d897f27bf6c94 --- /dev/null +++ b/tests/ui/contracts/empty-ensures.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::ensures; + +#[ensures()] +//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277] +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr new file mode 100644 index 0000000000000..407a253bd8565 --- /dev/null +++ b/tests/ui/contracts/empty-ensures.stderr @@ -0,0 +1,25 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/empty-ensures.rs:2:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: expected a `Fn(&_)` closure, found `()` + --> $DIR/empty-ensures.rs:8:1 + | +LL | #[ensures()] + | ^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/empty-requires.rs b/tests/ui/contracts/empty-requires.rs new file mode 100644 index 0000000000000..e3c72dcd66a17 --- /dev/null +++ b/tests/ui/contracts/empty-requires.rs @@ -0,0 +1,18 @@ +//@ dont-require-annotations: NOTE +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::requires; + +#[requires()] +//~^ ERROR mismatched types [E0308] +//~| NOTE expected `bool`, found `()` +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/empty-requires.stderr b/tests/ui/contracts/empty-requires.stderr new file mode 100644 index 0000000000000..b48e547b8cda7 --- /dev/null +++ b/tests/ui/contracts/empty-requires.stderr @@ -0,0 +1,18 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/empty-requires.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/empty-requires.rs:9:1 + | +LL | #[requires()] + | ^^^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 6e613b53fc9b1..5caea2b23d93c 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -22,15 +22,15 @@ fn main() { // always pass core::intrinsics::contract_check_requires(|| true); - // fail if enabled - #[cfg(any(default, unchk_pass, chk_fail_requires))] + // always fail + #[cfg(any(chk_fail_requires))] core::intrinsics::contract_check_requires(|| false); let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old }; // Always pass - core::intrinsics::contract_check_ensures(doubles_to_two, 1); + core::intrinsics::contract_check_ensures(Some(doubles_to_two), 1); - // Fail if enabled - #[cfg(any(default, unchk_pass, chk_fail_ensures))] - core::intrinsics::contract_check_ensures(doubles_to_two, 2); + // always fail + #[cfg(any(chk_fail_ensures))] + core::intrinsics::contract_check_ensures(Some(doubles_to_two), 2); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index ac72d233bf6b4..50cf592ac564d 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -17,8 +17,10 @@ #![feature(contracts_internals)] // to access check_requires lang item #![feature(core_intrinsics)] fn foo(x: Baz) -> i32 { - let injected_checker = { - core::contracts::build_check_ensures(|ret| *ret > 100) + let injected_checker = if core::intrinsics::contract_checks() { + Some(core::contracts::build_check_ensures(|ret| *ret > 100)) + } else { + None }; let ret = x.baz + 50; diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 6e5a7a3f95005..d101ab33547eb 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -6,7 +6,7 @@ fn main() { //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); //~^ ERROR use of unstable library feature `contracts_internals` - core::intrinsics::contract_check_ensures( |_|true, &1); + core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); //~^ ERROR use of unstable library feature `contracts_internals` core::contracts::build_check_ensures(|_: &()| true); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 1e39bd62e245b..738b7a3e09ff1 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -41,7 +41,7 @@ LL | core::intrinsics::contract_check_requires(|| true); error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:9:5 | -LL | core::intrinsics::contract_check_ensures( |_|true, &1); +LL | core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #128044 for more information From 2d87527e426509b184fb1567c762420e0d513add Mon Sep 17 00:00:00 2001 From: Dawid Lachowicz Date: Sat, 16 Aug 2025 15:37:53 +0100 Subject: [PATCH 1527/1900] Guard HIR contracts based on compiler flag rather than lang_item This allows the optimiser to properly eliminate contract code when runtime contract checks are disabled. It comes at the cost of having to recompile upstream crates (e.g. std) to enable contracts in them. However, this trade off is acceptable if it means disabled runtime contract checks do not affect the runtime performance of the functions they annotate. With the proper elimination of contract code, which this change introduces, the runtime performance of annotated functions should be the same as the original unannotated function. --- compiler/rustc_ast_lowering/src/contract.rs | 118 ++++++++++++++---- compiler/rustc_ast_lowering/src/expr.rs | 39 ++++-- ...contract-lang-items.unchk_fail_post.stderr | 11 -- 3 files changed, 120 insertions(+), 48 deletions(-) delete mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs index ad49cd2a42423..2f1c3d66d4e70 100644 --- a/compiler/rustc_ast_lowering/src/contract.rs +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -1,6 +1,18 @@ +use thin_vec::thin_vec; + use crate::LoweringContext; impl<'a, 'hir> LoweringContext<'a, 'hir> { + /// Lowered contracts are guarded with the `contract_checks` compiler flag, + /// i.e. the flag turns into a boolean guard in the lowered HIR. The reason + /// for not eliminating the contract code entirely when the `contract_checks` + /// flag is disabled is so that contracts can be type checked, even when + /// they are disabled, which avoids them becoming stale (i.e. out of sync + /// with the codebase) over time. + /// + /// The optimiser should be able to eliminate all contract code guarded + /// by `if false`, leaving the original body intact when runtime contract + /// checks are disabled. pub(super) fn lower_contract( &mut self, body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, @@ -14,14 +26,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // into: // + // let __postcond = if contract_checks { + // contract_check_requires(PRECOND); + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; // { - // let __postcond = if contracts_checks() { - // contract_check_requires(PRECOND); - // Some(|ret_val| POSTCOND) - // } else { - // None - // }; - // contract_check_ensures(__postcond, { body }) + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } // } let precond = self.lower_precond(req); @@ -41,13 +59,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // into: // + // let __postcond = if contract_checks { + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; // { - // let __postcond = if contracts_check() { - // Some(|ret_val| POSTCOND) - // } else { - // None - // }; - // __postcond({ body }) + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } // } let postcond_checker = self.lower_postcond_checker(ens); @@ -66,7 +90,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // into: // // { - // if contracts_check() { + // if contracts_checks { // contract_requires(PRECOND); // } // body @@ -129,11 +153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); let precond_check = rustc_hir::ExprKind::If( - self.expr_call_lang_item_fn( - precond.span, - rustc_hir::LangItem::ContractChecks, - Default::default(), - ), + self.arena.alloc(self.expr_bool_literal(precond.span, self.tcx.sess.contract_checks())), then_block, None, ); @@ -170,11 +190,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let else_block = self.arena.alloc(self.expr_block(else_block)); let contract_check = rustc_hir::ExprKind::If( - self.expr_call_lang_item_fn( - span, - rustc_hir::LangItem::ContractChecks, - Default::default(), - ), + self.arena.alloc(self.expr_bool_literal(span, self.tcx.sess.contract_checks())), then_block, Some(else_block), ); @@ -249,12 +265,60 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { cond_ident: rustc_span::Ident, cond_hir_id: rustc_hir::HirId, ) -> &'hir rustc_hir::Expr<'hir> { + // { + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } + // } + let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span); + + // Set up the return `let` statement. + let (ret_pat, ret_hir_id) = + self.pat_ident_binding_mode_mut(span, ret_ident, rustc_hir::BindingMode::NONE); + + let ret_stmt = self.stmt_let_pat( + None, + span, + Some(expr), + self.arena.alloc(ret_pat), + rustc_hir::LocalSource::Contract, + ); + + let ret = self.expr_ident(span, ret_ident, ret_hir_id); + let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); - let call_expr = self.expr_call_lang_item_fn_mut( + let contract_check = self.expr_call_lang_item_fn_mut( span, rustc_hir::LangItem::ContractCheckEnsures, - arena_vec![self; *cond_fn, *expr], + arena_vec![self; *cond_fn, *ret], ); - self.arena.alloc(call_expr) + let contract_check = self.arena.alloc(contract_check); + let call_expr = self.block_expr_block(contract_check); + + // same ident can't be used in 2 places, so we create a new one for the + // else branch + let ret = self.expr_ident(span, ret_ident, ret_hir_id); + let ret_block = self.block_expr_block(ret); + + let contracts_enabled: rustc_hir::Expr<'_> = + self.expr_bool_literal(span, self.tcx.sess.contract_checks()); + let contract_check = self.arena.alloc(self.expr( + span, + rustc_hir::ExprKind::If( + self.arena.alloc(contracts_enabled), + call_expr, + Some(ret_block), + ), + )); + + let attrs: rustc_ast::AttrVec = thin_vec![self.unreachable_code_attr(span)]; + self.lower_attrs(contract_check.hir_id, &attrs, span, rustc_hir::Target::Expression); + + let ret_block = self.block_all(span, arena_vec![self; ret_stmt], Some(contract_check)); + self.arena.alloc(self.expr_block(self.arena.alloc(ret_block))) } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index d1ce3b169d75f..4a9b9f544b53d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1941,16 +1941,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }; - // `#[allow(unreachable_code)]` - let attr = attr::mk_attr_nested_word( - &self.tcx.sess.psess.attr_id_generator, - AttrStyle::Outer, - Safety::Default, - sym::allow, - sym::unreachable_code, - try_span, - ); - let attrs: AttrVec = thin_vec![attr]; + let attrs: AttrVec = thin_vec![self.unreachable_code_attr(try_span)]; // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,` let continue_arm = { @@ -2290,6 +2281,17 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(b.span, hir::ExprKind::Block(b, None)) } + /// Wrap an expression in a block, and wrap that block in an expression again. + /// Useful for constructing if-expressions, which require expressions of + /// kind block. + pub(super) fn block_expr_block( + &mut self, + expr: &'hir hir::Expr<'hir>, + ) -> &'hir hir::Expr<'hir> { + let b = self.block_expr(expr); + self.arena.alloc(self.expr_block(b)) + } + pub(super) fn expr_array_ref( &mut self, span: Span, @@ -2303,6 +2305,10 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr)) } + pub(super) fn expr_bool_literal(&mut self, span: Span, val: bool) -> hir::Expr<'hir> { + self.expr(span, hir::ExprKind::Lit(Spanned { node: LitKind::Bool(val), span })) + } + pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> { let hir_id = self.next_id(); hir::Expr { hir_id, kind, span: self.lower_span(span) } @@ -2336,6 +2342,19 @@ impl<'hir> LoweringContext<'_, 'hir> { body: expr, } } + + /// `#[allow(unreachable_code)]` + pub(super) fn unreachable_code_attr(&mut self, span: Span) -> Attribute { + let attr = attr::mk_attr_nested_word( + &self.tcx.sess.psess.attr_id_generator, + AttrStyle::Outer, + Safety::Default, + sym::allow, + sym::unreachable_code, + span, + ); + attr + } } /// Used by [`LoweringContext::make_lowered_await`] to customize the desugaring based on what kind diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr deleted file mode 100644 index a60ce1602659b..0000000000000 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 - | -LL | #![feature(contracts)] // to access core::contracts - | ^^^^^^^^^ - | - = note: see issue #128044 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - From 2a5dac7682d4be4515d45d32666d14a6e8b899a1 Mon Sep 17 00:00:00 2001 From: Dawid Lachowicz Date: Thu, 4 Sep 2025 08:24:00 +0100 Subject: [PATCH 1528/1900] Remove no longer used contract_checks intrinsic The contract_checks compiler flag is now used to determine if runtime contract checks should be enabled, as opposed to the compiler intrinsic as previously. --- compiler/rustc_hir/src/lang_items.rs | 1 - .../rustc_hir_analysis/src/check/intrinsic.rs | 2 -- .../src/lower_intrinsics.rs | 11 ---------- library/core/src/intrinsics/mod.rs | 18 ---------------- .../internal_machinery/contract-intrinsics.rs | 21 ++++--------------- .../contract-lang-items.chk_fail_post.stderr | 2 +- .../contract-lang-items.chk_pass.stderr | 2 +- .../internal_machinery/contract-lang-items.rs | 19 ++++------------- .../contract-lang-items.unchk_pass.stderr | 2 +- .../internal-feature-gating.rs | 2 -- .../internal-feature-gating.stderr | 20 +++++------------- 11 files changed, 16 insertions(+), 84 deletions(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index d62ec5482b430..2e099a97b65be 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -429,7 +429,6 @@ language_item_table! { // Experimental lang items for implementing contract pre- and post-condition checking. ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; - ContractChecks, sym::contract_checks, contract_checks_fn, Target::Fn, GenericRequirement::None; // Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727) DefaultTrait4, sym::default_trait4, default_trait4_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 5fd71fd1cd76c..a6659912e3fb9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -647,8 +647,6 @@ pub(crate) fn check_intrinsic_type( sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))), - // contract_checks() -> bool - sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), sym::contract_check_ensures => { diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 8dadce0d448d3..fc8092fd5832b 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -34,17 +34,6 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { )); terminator.kind = TerminatorKind::Goto { target }; } - sym::contract_checks => { - let target = target.unwrap(); - block.statements.push(Statement::new( - terminator.source_info, - StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), - ))), - )); - terminator.kind = TerminatorKind::Goto { target }; - } sym::forget => { let target = target.unwrap(); block.statements.push(Statement::new( diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 0d3be136d1325..c397e762d5589 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2631,24 +2631,6 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 { ptr } -/// Returns whether we should perform contract-checking at runtime. -/// -/// This is meant to be similar to the ub_checks intrinsic, in terms -/// of not prematurely committing at compile-time to whether contract -/// checking is turned on, so that we can specify contracts in libstd -/// and let an end user opt into turning them on. -#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] -#[rustc_const_unstable(feature = "contracts", issue = "128044")] -#[inline(always)] -#[lang = "contract_checks"] -#[rustc_intrinsic] -pub const fn contract_checks() -> bool { - // FIXME: should this be `false` or `cfg!(contract_checks)`? - - // cfg!(contract_checks) - false -} - /// Check if the pre-condition `cond` has been met. /// /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 5caea2b23d93c..9dd5167c9e117 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -1,29 +1,16 @@ -//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +//@ revisions: default chk_fail_ensures chk_fail_requires // //@ [default] run-pass -//@ [unchk_pass] run-pass -//@ [chk_pass] run-pass //@ [chk_fail_requires] run-crash //@ [chk_fail_ensures] run-crash -// -//@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes -#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] +#![feature(contracts_internals, core_intrinsics)] fn main() { - #[cfg(any(default, unchk_pass))] // default: disabled - assert_eq!(core::intrinsics::contract_checks(), false); - - #[cfg(chk_pass)] // explicitly enabled - assert_eq!(core::intrinsics::contract_checks(), true); - // always pass core::intrinsics::contract_check_requires(|| true); // always fail - #[cfg(any(chk_fail_requires))] + #[cfg(chk_fail_requires)] core::intrinsics::contract_check_requires(|| false); let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old }; @@ -31,6 +18,6 @@ fn main() { core::intrinsics::contract_check_ensures(Some(doubles_to_two), 1); // always fail - #[cfg(any(chk_fail_ensures))] + #[cfg(chk_fail_ensures)] core::intrinsics::contract_check_ensures(Some(doubles_to_two), 2); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index 50cf592ac564d..ad88ebfe22e3b 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -1,27 +1,16 @@ -//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post +//@ revisions: unchk_pass chk_pass chk_fail_post // //@ [unchk_pass] run-pass -//@ [unchk_fail_post] run-pass //@ [chk_pass] run-pass // //@ [chk_fail_post] run-crash -// -//@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no -// -//@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes #![feature(contracts)] // to access core::contracts //~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![feature(contracts_internals)] // to access check_requires lang item #![feature(core_intrinsics)] fn foo(x: Baz) -> i32 { - let injected_checker = if core::intrinsics::contract_checks() { - Some(core::contracts::build_check_ensures(|ret| *ret > 100)) - } else { - None - }; + let injected_checker = Some(core::contracts::build_check_ensures(|ret| *ret > 100)); let ret = x.baz + 50; core::intrinsics::contract_check_ensures(injected_checker, ret) @@ -31,11 +20,11 @@ struct Baz { baz: i32 } const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; -#[cfg(any(unchk_fail_post, chk_fail_post))] +#[cfg(chk_fail_post)] const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; fn main() { assert_eq!(foo(BAZ_PASS_PRE_POST), 150); - #[cfg(any(unchk_fail_post, chk_fail_post))] + #[cfg(chk_fail_post)] foo(BAZ_FAIL_POST); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index d101ab33547eb..48bd376594f22 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -2,8 +2,6 @@ fn main() { // intrinsics are guarded by contracts_internals feature gate. - core::intrinsics::contract_checks(); - //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 738b7a3e09ff1..c1318fc15a78d 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,5 +1,5 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:16:28 + --> $DIR/internal-feature-gating.rs:14:28 | LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } | ^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:18:28 + --> $DIR/internal-feature-gating.rs:16:28 | LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } | ^^^^^^^^^^^^^^^^ @@ -21,16 +21,6 @@ LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:5:5 | -LL | core::intrinsics::contract_checks(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #128044 for more information - = help: add `#![feature(contracts_internals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:7:5 - | LL | core::intrinsics::contract_check_requires(|| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -39,7 +29,7 @@ LL | core::intrinsics::contract_check_requires(|| true); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:9:5 + --> $DIR/internal-feature-gating.rs:7:5 | LL | core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +39,7 @@ LL | core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:12:5 + --> $DIR/internal-feature-gating.rs:10:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,6 +48,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true); = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0658`. From a172a66ae8a533ed3797326aca25f0a4beae2013 Mon Sep 17 00:00:00 2001 From: Dawid Lachowicz Date: Thu, 4 Sep 2025 09:01:14 +0100 Subject: [PATCH 1529/1900] Wrap contract clauses in brances instead of parenthesis The compiler complained about uncecessary parenthesis on contract clauses, which were insterted by the contract macros. This commit changes the macro to use braces as the delimiter instead, fixing the issue. --- compiler/rustc_builtin_macros/src/contracts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index 6a24af361fe78..85fbe934124f5 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -141,7 +141,7 @@ fn expand_requires_tts( new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), - token::Delimiter::Parenthesis, + token::Delimiter::Brace, annotation, )); Ok(()) @@ -163,7 +163,7 @@ fn expand_ensures_tts( new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), - token::Delimiter::Parenthesis, + token::Delimiter::Brace, annotation, )); Ok(()) From e0e5d478c12990667d2723bc4e84db9c46991bf3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 3 Oct 2025 11:31:07 -0700 Subject: [PATCH 1530/1900] manual_unwrap_or: fix FP edge case --- clippy_lints/src/matches/manual_unwrap_or.rs | 12 +++++++----- tests/ui/manual_unwrap_or_default.fixed | 9 +++++++++ tests/ui/manual_unwrap_or_default.rs | 9 +++++++++ tests/ui/manual_unwrap_or_default.stderr | 4 ++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d917..d225bd35cb8be 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -31,7 +31,7 @@ fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { } } -fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>, allow_wildcard: bool) -> Option<&'tcx Expr<'tcx>> { if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match @@ -48,7 +48,9 @@ fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'t && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) { Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { + } else if let PatKind::Wild = arm.pat.kind + && allow_wildcard + { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) } else { @@ -62,11 +64,11 @@ fn get_some_and_none_bodies<'tcx>( arm2: &'tcx Arm<'tcx>, ) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) + && let Some(body_none) = get_none(cx, arm2, true) { Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) + } else if let Some(body_none) = get_none(cx, arm1, false) + && let Some(binding_id) = get_some(cx, arm2.pat) { Some(((arm2.body, binding_id), body_none)) } else { diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 189fe876aa5d4..11023ac1142a7 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -32,6 +32,15 @@ fn main() { let x: Result = Ok(String::new()); x.unwrap_or_default(); + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index ca87926763c9c..bf06d9af7d219 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -64,6 +64,15 @@ fn main() { } else { String::new() }; + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index e8f38a2e3899e..031100832b16f 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:74:24 + --> tests/ui/manual_unwrap_or_default.rs:83:24 | LL | Some(_) => match *b { | ________________________^ @@ -87,7 +87,7 @@ LL | | }, | |_____________^ help: replace it with: `(*b).unwrap_or_default()` error: if let can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:143:5 + --> tests/ui/manual_unwrap_or_default.rs:152:5 | LL | / if let Some(x) = Some(42) { LL | | From 029579d1770fb23c693b2ecbab724fe3af66349b Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 9 Oct 2025 19:00:21 -0400 Subject: [PATCH 1531/1900] Change int-to-ptr transmute lowering back to inttoptr --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 ++-- tests/codegen-llvm/common_prim_int_ptr.rs | 2 +- tests/codegen-llvm/enum/enum-aggregate.rs | 2 +- .../int-ptr-int-enum-miscompile.rs | 22 +++++++++++++++++++ .../intrinsics/transmute-niched.rs | 2 +- tests/codegen-llvm/intrinsics/transmute.rs | 4 ++-- tests/codegen-llvm/transmute-scalar.rs | 2 +- 7 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 tests/codegen-llvm/int-ptr-int-enum-miscompile.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d629003bff5e8..88ece6318acf7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1010,14 +1010,14 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( imm = match (from_scalar.primitive(), to_scalar.primitive()) { (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty), - (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm), + (Int(..), Pointer(..)) => bx.inttoptr(imm, to_backend_ty), (Pointer(..), Int(..)) => { // FIXME: this exposes the provenance, which shouldn't be necessary. bx.ptrtoint(imm, to_backend_ty) } (Float(_), Pointer(..)) => { let int_imm = bx.bitcast(imm, bx.cx().type_isize()); - bx.ptradd(bx.const_null(bx.type_ptr()), int_imm) + bx.inttoptr(int_imm, to_backend_ty) } (Pointer(..), Float(_)) => { // FIXME: this exposes the provenance, which shouldn't be necessary. diff --git a/tests/codegen-llvm/common_prim_int_ptr.rs b/tests/codegen-llvm/common_prim_int_ptr.rs index 53716adccbf21..2c906e86bd736 100644 --- a/tests/codegen-llvm/common_prim_int_ptr.rs +++ b/tests/codegen-llvm/common_prim_int_ptr.rs @@ -11,7 +11,7 @@ #[no_mangle] pub fn insert_int(x: usize) -> Result> { // CHECK: start: - // CHECK-NEXT: %[[WO_PROV:.+]] = getelementptr i8, ptr null, [[USIZE:i[0-9]+]] %x + // CHECK-NEXT: %[[WO_PROV:.+]] = inttoptr [[USIZE:i[0-9]+]] %x to ptr // CHECK-NEXT: %[[R:.+]] = insertvalue { [[USIZE]], ptr } { [[USIZE]] 0, ptr poison }, ptr %[[WO_PROV]], 1 // CHECK-NEXT: ret { [[USIZE]], ptr } %[[R]] Ok(x) diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index 7d450a89e2e3c..89517008c6374 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -70,7 +70,7 @@ fn make_ok_ptr(x: NonNull) -> Result, usize> { fn make_ok_int(x: usize) -> Result> { // CHECK-LABEL: { i64, ptr } @make_ok_int(i64 %x) // CHECK-NEXT: start: - // CHECK-NEXT: %[[NOPROV:.+]] = getelementptr i8, ptr null, i64 %x + // CHECK-NEXT: %[[NOPROV:.+]] = inttoptr i64 %x to ptr // CHECK-NEXT: %[[R:.+]] = insertvalue { i64, ptr } { i64 0, ptr poison }, ptr %[[NOPROV]], 1 // CHECK-NEXT: ret { i64, ptr } %[[R]] Ok(x) diff --git a/tests/codegen-llvm/int-ptr-int-enum-miscompile.rs b/tests/codegen-llvm/int-ptr-int-enum-miscompile.rs new file mode 100644 index 0000000000000..299c3bf8847b2 --- /dev/null +++ b/tests/codegen-llvm/int-ptr-int-enum-miscompile.rs @@ -0,0 +1,22 @@ +// This is a regression test for https://github.com/rust-lang/rust/issues/147265. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +#[no_mangle] +pub fn mk_result(a: usize) -> Result { + // CHECK-LABEL: @mk_result + // CHECK-NOT: unreachable + // CHECK: load i8, + // CHECK-NOT: unreachable + match g(a) { + Ok(b) => Ok(unsafe { *(b as *const u8) }), + Err(c) => Err(c), + } +} + +#[cold] +fn g(a: usize) -> Result { + Ok(a) +} diff --git a/tests/codegen-llvm/intrinsics/transmute-niched.rs b/tests/codegen-llvm/intrinsics/transmute-niched.rs index a886d9eee5909..8c647655f65bb 100644 --- a/tests/codegen-llvm/intrinsics/transmute-niched.rs +++ b/tests/codegen-llvm/intrinsics/transmute-niched.rs @@ -249,7 +249,7 @@ pub unsafe fn check_four_or_eight_to_nonnull(x: FourOrEight) -> NonNull { // OPT: call void @llvm.assume(i1 %1) // CHECK-NOT: icmp // CHECK-NOT: assume - // CHECK: %[[RET:.+]] = getelementptr i8, ptr null, i64 %x + // CHECK: %[[RET:.+]] = inttoptr i64 %x to ptr // CHECK-NEXT: ret ptr %[[RET]] transmute(x) diff --git a/tests/codegen-llvm/intrinsics/transmute.rs b/tests/codegen-llvm/intrinsics/transmute.rs index e327c100d7ddd..b86c6df3267e4 100644 --- a/tests/codegen-llvm/intrinsics/transmute.rs +++ b/tests/codegen-llvm/intrinsics/transmute.rs @@ -303,7 +303,7 @@ pub unsafe fn check_pair_with_bool(x: (u8, bool)) -> (bool, i8) { pub unsafe fn check_float_to_pointer(x: f64) -> *const () { // CHECK-NOT: alloca // CHECK: %0 = bitcast double %x to i64 - // CHECK: %_0 = getelementptr i8, ptr null, i64 %0 + // CHECK: %_0 = inttoptr i64 %0 to ptr // CHECK: ret ptr %_0 transmute(x) } @@ -378,7 +378,7 @@ pub unsafe fn check_issue_110005(x: (usize, bool)) -> Option> { // CHECK-LABEL: @check_pair_to_dst_ref( #[no_mangle] pub unsafe fn check_pair_to_dst_ref<'a>(x: (usize, usize)) -> &'a [u8] { - // CHECK: %_0.0 = getelementptr i8, ptr null, i64 %x.0 + // CHECK: %_0.0 = inttoptr i64 %x.0 to ptr // CHECK: %0 = icmp ne ptr %_0.0, null // CHECK: call void @llvm.assume(i1 %0) // CHECK: %1 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0 diff --git a/tests/codegen-llvm/transmute-scalar.rs b/tests/codegen-llvm/transmute-scalar.rs index 21f7287047cf0..e1ce8e506066a 100644 --- a/tests/codegen-llvm/transmute-scalar.rs +++ b/tests/codegen-llvm/transmute-scalar.rs @@ -56,7 +56,7 @@ pub fn ptr_to_int(p: *mut u16) -> usize { } // CHECK: define{{.*}}ptr @int_to_ptr([[USIZE]] %i) -// CHECK: %_0 = getelementptr i8, ptr null, [[USIZE]] %i +// CHECK: %_0 = inttoptr [[USIZE]] %i to ptr // CHECK-NEXT: ret ptr %_0 #[no_mangle] pub fn int_to_ptr(i: usize) -> *mut u16 { From 33cc7787bf0b17f9b951205be94f602b67ffc379 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:59:08 -0700 Subject: [PATCH 1532/1900] trivial test blessings --- ....main-{closure#0}.StateTransform.after.mir | 16 ++++++------ ....main-{closure#1}.StateTransform.after.mir | 16 ++++++------ .../struct.main.DataflowConstProp.32bit.diff | 16 ++++++------ .../struct.main.DataflowConstProp.64bit.diff | 16 ++++++------ ...ef.tuple.DeadStoreElimination-initial.diff | 4 +-- ...ch_68867.try_sum.EarlyOtherwiseBranch.diff | 26 +++++++++---------- ...ness.no_downcast.EarlyOtherwiseBranch.diff | 2 +- .../gvn_copy_aggregate.all_copy_2.GVN.diff | 7 +++-- ...y.run2-{closure#0}.Inline.panic-abort.diff | 18 ++++++------- ....run2-{closure#0}.Inline.panic-unwind.diff | 22 ++++++++-------- ...ef_nested_borrows.src.GVN.panic-abort.diff | 6 ++--- ...f_nested_borrows.src.GVN.panic-unwind.diff | 6 ++--- ...fg-pre-optimizations.after.panic-abort.mir | 2 +- ...g-pre-optimizations.after.panic-unwind.mir | 2 +- ...mes.foo.ScalarReplacementOfAggregates.diff | 8 +++--- .../async-closure.stdout | 18 ++++++------- 16 files changed, 90 insertions(+), 95 deletions(-) diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir index d8fdb446135b3..cd776ad7170a3 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir @@ -50,19 +50,19 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 let mut _25: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; bb0: { - _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); _17 = discriminant((*_18)); switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _19 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _20 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -84,7 +84,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _21 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); discriminant((*_21)) = 3; return; } @@ -108,7 +108,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _22 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -135,7 +135,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 StorageDead(_9); StorageDead(_11); StorageDead(_15); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _23 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); discriminant((*_23)) = 4; return; } @@ -154,7 +154,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 StorageDead(_11); StorageDead(_8); _16 = const (); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _24 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } @@ -164,7 +164,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _25 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir index dd17afad656b8..981bc21dc878a 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir @@ -50,19 +50,19 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 let mut _25: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; bb0: { - _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); _17 = discriminant((*_18)); switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _19 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _20 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -84,7 +84,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _21 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); discriminant((*_21)) = 3; return; } @@ -108,7 +108,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _22 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -135,7 +135,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 StorageDead(_9); StorageDead(_11); StorageDead(_15); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _23 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); discriminant((*_23)) = 4; return; } @@ -154,7 +154,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 StorageDead(_11); StorageDead(_8); _16 = const (); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _24 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } @@ -164,7 +164,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _25 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff index 3ea49265cee33..132dfd3d8178a 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff @@ -112,16 +112,16 @@ StorageDead(_10); StorageLive(_14); _14 = const {ALLOC0: &&SmallStruct}; - _31 = deref_copy (*_14); + _31 = copy (*_14); StorageLive(_11); - _32 = deref_copy (*_14); + _32 = copy (*_14); - _11 = copy ((*_32).0: f32); + _11 = const 9f32; StorageLive(_12); - _33 = deref_copy (*_14); + _33 = copy (*_14); _12 = copy ((*_33).1: std::option::Option); StorageLive(_13); - _34 = deref_copy (*_14); + _34 = copy (*_14); _13 = copy ((*_34).2: &[f32]); StorageDead(_14); StorageLive(_15); @@ -149,16 +149,16 @@ StorageDead(_22); StorageLive(_26); _26 = const {ALLOC1: &&BigStruct}; - _35 = deref_copy (*_26); + _35 = copy (*_26); StorageLive(_23); - _36 = deref_copy (*_26); + _36 = copy (*_26); - _23 = copy ((*_36).0: f32); + _23 = const 82f32; StorageLive(_24); - _37 = deref_copy (*_26); + _37 = copy (*_26); _24 = copy ((*_37).1: std::option::Option); StorageLive(_25); - _38 = deref_copy (*_26); + _38 = copy (*_26); _25 = copy ((*_38).2: &[f32]); StorageDead(_26); StorageLive(_27); diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff index 78a0944975c86..0f3e6f0cb17f7 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff @@ -112,16 +112,16 @@ StorageDead(_10); StorageLive(_14); _14 = const {ALLOC0: &&SmallStruct}; - _31 = deref_copy (*_14); + _31 = copy (*_14); StorageLive(_11); - _32 = deref_copy (*_14); + _32 = copy (*_14); - _11 = copy ((*_32).0: f32); + _11 = const 9f32; StorageLive(_12); - _33 = deref_copy (*_14); + _33 = copy (*_14); _12 = copy ((*_33).1: std::option::Option); StorageLive(_13); - _34 = deref_copy (*_14); + _34 = copy (*_14); _13 = copy ((*_34).2: &[f32]); StorageDead(_14); StorageLive(_15); @@ -149,16 +149,16 @@ StorageDead(_22); StorageLive(_26); _26 = const {ALLOC1: &&BigStruct}; - _35 = deref_copy (*_26); + _35 = copy (*_26); StorageLive(_23); - _36 = deref_copy (*_26); + _36 = copy (*_26); - _23 = copy ((*_36).0: f32); + _23 = const 82f32; StorageLive(_24); - _37 = deref_copy (*_26); + _37 = copy (*_26); _24 = copy ((*_37).1: std::option::Option); StorageLive(_25); - _38 = deref_copy (*_26); + _38 = copy (*_26); _25 = copy ((*_38).2: &[f32]); StorageDead(_26); StorageLive(_27); diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff index 0b96569cbe4b1..3e677d13b89c1 100644 --- a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -13,10 +13,10 @@ bb0: { - StorageLive(_2); -- _3 = deref_copy (_1.1: &Foo); +- _3 = copy (_1.1: &Foo); - _2 = &((*_3).2: i32); + // DBG: _2 = &((*_3).2: i32); - _4 = deref_copy (_1.1: &Foo); + _4 = copy (_1.1: &Foo); _0 = copy ((*_4).0: i32); - StorageDead(_2); return; diff --git a/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff index fec318c1ab492..c8b516c14987b 100644 --- a/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff @@ -76,7 +76,7 @@ _4 = (move _5, move _6); StorageDead(_6); StorageDead(_5); - _34 = deref_copy (_4.0: &ViewportPercentageLength); + _34 = copy (_4.0: &ViewportPercentageLength); _11 = discriminant((*_34)); switchInt(move _11) -> [0: bb2, 1: bb3, 2: bb4, 3: bb5, otherwise: bb12]; } @@ -92,35 +92,35 @@ } bb2: { - _35 = deref_copy (_4.1: &ViewportPercentageLength); + _35 = copy (_4.1: &ViewportPercentageLength); _7 = discriminant((*_35)); switchInt(move _7) -> [0: bb9, otherwise: bb1]; } bb3: { - _36 = deref_copy (_4.1: &ViewportPercentageLength); + _36 = copy (_4.1: &ViewportPercentageLength); _8 = discriminant((*_36)); switchInt(move _8) -> [1: bb8, otherwise: bb1]; } bb4: { - _37 = deref_copy (_4.1: &ViewportPercentageLength); + _37 = copy (_4.1: &ViewportPercentageLength); _9 = discriminant((*_37)); switchInt(move _9) -> [2: bb7, otherwise: bb1]; } bb5: { - _38 = deref_copy (_4.1: &ViewportPercentageLength); + _38 = copy (_4.1: &ViewportPercentageLength); _10 = discriminant((*_38)); switchInt(move _10) -> [3: bb6, otherwise: bb1]; } bb6: { StorageLive(_27); - _39 = deref_copy (_4.0: &ViewportPercentageLength); + _39 = copy (_4.0: &ViewportPercentageLength); _27 = copy (((*_39) as Vmax).0: f32); StorageLive(_28); - _40 = deref_copy (_4.1: &ViewportPercentageLength); + _40 = copy (_4.1: &ViewportPercentageLength); _28 = copy (((*_40) as Vmax).0: f32); StorageLive(_29); StorageLive(_30); @@ -139,10 +139,10 @@ bb7: { StorageLive(_22); - _41 = deref_copy (_4.0: &ViewportPercentageLength); + _41 = copy (_4.0: &ViewportPercentageLength); _22 = copy (((*_41) as Vmin).0: f32); StorageLive(_23); - _42 = deref_copy (_4.1: &ViewportPercentageLength); + _42 = copy (_4.1: &ViewportPercentageLength); _23 = copy (((*_42) as Vmin).0: f32); StorageLive(_24); StorageLive(_25); @@ -161,10 +161,10 @@ bb8: { StorageLive(_17); - _43 = deref_copy (_4.0: &ViewportPercentageLength); + _43 = copy (_4.0: &ViewportPercentageLength); _17 = copy (((*_43) as Vh).0: f32); StorageLive(_18); - _44 = deref_copy (_4.1: &ViewportPercentageLength); + _44 = copy (_4.1: &ViewportPercentageLength); _18 = copy (((*_44) as Vh).0: f32); StorageLive(_19); StorageLive(_20); @@ -183,10 +183,10 @@ bb9: { StorageLive(_12); - _45 = deref_copy (_4.0: &ViewportPercentageLength); + _45 = copy (_4.0: &ViewportPercentageLength); _12 = copy (((*_45) as Vw).0: f32); StorageLive(_13); - _46 = deref_copy (_4.1: &ViewportPercentageLength); + _46 = copy (_4.1: &ViewportPercentageLength); _13 = copy (((*_46) as Vw).0: f32); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff index 6a4c947b8826e..ab81f0a67e4eb 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff @@ -16,7 +16,7 @@ } bb1: { - _4 = deref_copy (((*_1) as Some).0: &E<'_>); + _4 = copy (((*_1) as Some).0: &E<'_>); _2 = discriminant((*_4)); switchInt(move _2) -> [1: bb2, 0: bb3, otherwise: bb5]; } diff --git a/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff b/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff index 452d8a9332036..eed8cb7d62e70 100644 --- a/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff +++ b/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff @@ -25,15 +25,14 @@ bb0: { - StorageLive(_2); -- _8 = deref_copy (*_1); + nop; -+ _8 = copy (*_1); + _8 = copy (*_1); _2 = copy ((*_8).0: i32); - StorageLive(_3); -- _9 = deref_copy (*_1); +- _9 = copy (*_1); - _3 = copy ((*_9).1: u64); - StorageLive(_4); -- _10 = deref_copy (*_1); +- _10 = copy (*_1); - _4 = copy ((*_10).2: [i8; 3]); + nop; + _9 = copy _8; diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 0acb33febe52b..09b7efd632e35 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -126,7 +126,7 @@ StorageLive(_8); _8 = move _4; StorageLive(_9); - _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _10 = copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; + StorageLive(_16); @@ -141,7 +141,7 @@ + StorageLive(_37); + StorageLive(_38); + StorageLive(_39); -+ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _31 = discriminant((*_32)); + switchInt(move _31) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; } @@ -177,8 +177,8 @@ } + bb3: { -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _33 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _34 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); @@ -191,7 +191,7 @@ + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _35 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb4; + } @@ -204,7 +204,7 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _36 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; @@ -248,7 +248,7 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _37 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + discriminant((*_37)) = 3; + goto -> bb2; + } @@ -263,13 +263,13 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _38 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; + } + + bb8: { + _7 = Poll::<()>::Ready(move _30); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _39 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + discriminant((*_39)) = 1; + goto -> bb2; + } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 98ee46c29b1be..b630753ed0157 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -128,7 +128,7 @@ StorageLive(_8); _8 = move _4; StorageLive(_9); - _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _10 = copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; + StorageLive(_16); @@ -145,7 +145,7 @@ + StorageLive(_39); + StorageLive(_40); + StorageLive(_41); -+ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _31 = discriminant((*_32)); + switchInt(move _31) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; } @@ -194,8 +194,8 @@ - StorageDead(_2); - return; + bb5: { -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _33 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _34 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); @@ -208,7 +208,7 @@ + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _35 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb6; } @@ -221,7 +221,7 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _36 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; @@ -267,7 +267,7 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _37 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + discriminant((*_37)) = 3; + goto -> bb4; + } @@ -282,13 +282,13 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _38 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; + } + + bb10: { + _7 = Poll::<()>::Ready(move _30); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _39 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + discriminant((*_39)) = 1; + goto -> bb4; + } @@ -301,12 +301,12 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _40 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { -+ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _41 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + discriminant((*_41)) = 2; + goto -> bb2; + } diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff index 993857f225a81..269af438e37ef 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff @@ -16,17 +16,15 @@ bb0: { - StorageLive(_2); -- _6 = deref_copy (*_1); + nop; -+ _6 = copy (*_1); + _6 = copy (*_1); _2 = copy (*_6); _3 = unknown() -> [return: bb1, unwind unreachable]; } bb1: { StorageLive(_4); -- _7 = deref_copy (*_1); -+ _7 = copy (*_1); + _7 = copy (*_1); _4 = copy (*_7); StorageLive(_5); _5 = copy _2; diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff index d81bfa9310bc1..9ce17342a445c 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff @@ -16,17 +16,15 @@ bb0: { - StorageLive(_2); -- _6 = deref_copy (*_1); + nop; -+ _6 = copy (*_1); + _6 = copy (*_1); _2 = copy (*_6); _3 = unknown() -> [return: bb1, unwind continue]; } bb1: { StorageLive(_4); -- _7 = deref_copy (*_1); -+ _7 = copy (*_1); + _7 = copy (*_1); _4 = copy (*_7); StorageLive(_5); _5 = copy _2; diff --git a/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-abort.mir b/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-abort.mir index da005d552e2bf..4861abead2f32 100644 --- a/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-abort.mir +++ b/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-abort.mir @@ -8,7 +8,7 @@ fn box_to_raw_mut(_1: &mut Box) -> *mut i32 { bb0: { Retag([fn entry] _1); - _2 = deref_copy (*_1); + _2 = copy (*_1); _3 = copy ((_2.0: std::ptr::Unique).0: std::ptr::NonNull) as *const i32 (Transmute); _0 = &raw mut (*_3); Retag([raw] _0); diff --git a/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-unwind.mir b/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-unwind.mir index da005d552e2bf..4861abead2f32 100644 --- a/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-unwind.mir +++ b/tests/mir-opt/retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.panic-unwind.mir @@ -8,7 +8,7 @@ fn box_to_raw_mut(_1: &mut Box) -> *mut i32 { bb0: { Retag([fn entry] _1); - _2 = deref_copy (*_1); + _2 = copy (*_1); _3 = copy ((_2.0: std::ptr::Unique).0: std::ptr::NonNull) as *const i32 (Transmute); _0 = &raw mut (*_3); Retag([raw] _0); diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff index 0d5fcf9ef1432..056dc4c42c11f 100644 --- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff @@ -117,8 +117,8 @@ StorageLive(_15); StorageLive(_16); StorageLive(_17); -- _26 = deref_copy (_12.0: &std::boxed::Box); -+ _26 = deref_copy _34; +- _26 = copy (_12.0: &std::boxed::Box); ++ _26 = copy _34; _17 = &(*_26); _16 = core::fmt::rt::Argument::<'_>::new_display::>(move _17) -> [return: bb3, unwind unreachable]; } @@ -127,8 +127,8 @@ StorageDead(_17); StorageLive(_18); StorageLive(_19); -- _27 = deref_copy (_12.1: &u32); -+ _27 = deref_copy _35; +- _27 = copy (_12.1: &u32); ++ _27 = copy _35; _19 = &(*_27); _18 = core::fmt::rt::Argument::<'_>::new_display::(move _19) -> [return: bb4, unwind unreachable]; } diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout index 5113dc5048b93..550f0512569d7 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.stdout +++ b/tests/ui/rustc_public-ir-print/async-closure.stdout @@ -28,7 +28,7 @@ fn foo::{closure#0}(_1: &{async closure@$DIR/async-closure.rs:9:13: 9:21}) -> {a debug y => (*((*_1).0: &i32)); bb0: { StorageLive(_2); - _3 = CopyForDeref(((*_1).0: &i32)); + _3 = ((*_1).0: &i32); _2 = &(*_3); _0 = {coroutine@$DIR/async-closure.rs:9:22: 11:6}(move _2); StorageDead(_2); @@ -48,19 +48,19 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); _6 = discriminant((*_7)); switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { StorageLive(_3); - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_8).0: &i32)); + _8 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); + _4 = ((*_8).0: &i32); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _9 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); discriminant((*_9)) = 1; return; } @@ -84,19 +84,19 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); _6 = discriminant((*_7)); switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { StorageLive(_3); - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_8).0: &i32)); + _8 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); + _4 = ((*_8).0: &i32); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _9 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); discriminant((*_9)) = 1; return; } From 748a593a7f68dd8e3b1eceebc2dcb390d733f931 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 14 Sep 2025 04:48:17 -0400 Subject: [PATCH 1533/1900] Add new utils for defninition identification. --- book/src/development/adding_lints.md | 4 +- .../development/common_tools_writing_lints.md | 40 +- book/src/development/method_checking.md | 8 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/res.rs | 643 ++++++++++++++++++ 5 files changed, 670 insertions(+), 26 deletions(-) create mode 100644 clippy_utils/src/res.rs diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 2b89e94cf8f4f..d9a5f04c3e1c0 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], - [`snippet`], etc) + is already in here ([`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] * [`from_expansion`][from_expansion] and @@ -790,7 +789,6 @@ get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. [utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html -[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html [let-chains]: https://github.com/rust-lang/rust/pull/94927 diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 3bec3ce33af36..74fc788e9e3ae 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -85,8 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::paths; +use clippy_utils::res::MaybeDef; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -97,12 +97,12 @@ impl LateLintPass<'_> for MyStructLint { // 1. Using diagnostic items // The last argument is the diagnostic item to check for - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { // The type is an `Option` } // 2. Using lang items - if is_type_lang_item(cx, ty, LangItem::RangeFull) { + if ty.is_lang_item(cx, LangItem::RangeFull) { // The type is a full range like `.drain(..)` } @@ -124,26 +124,28 @@ diagnostic item, lang item or neither. ```rust use clippy_utils::ty::implements_trait; -use clippy_utils::is_trait_method; use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using diagnostic items with the expression - // we use `is_trait_method` function from Clippy's utils - if is_trait_method(cx, expr, sym::Iterator) { - // method call in `expr` belongs to `Iterator` trait - } - // 2. Using lang items with the expression type + // 1. Get the `DefId` of the trait. + // via lang items + let trait_id = cx.tcx.lang_items().drop_trait(); + // via diagnostic items + let trait_id = cx.tcx.get_diagnostic_item(sym::Eq); + + // 2. Check for the trait implementation via the `implements_trait` util. let ty = cx.typeck_results().expr_ty(expr); - if cx.tcx.lang_items() - // we are looking for the `DefId` of `Drop` trait in lang items - .drop_trait() - // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils - .is_some_and(|id| implements_trait(cx, ty, id, &[])) { - // `expr` implements `Drop` trait - } + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) { + // `ty` implements the trait. + } + + // 3. If the trait requires additional generic arguments + let trait_id = cx.tcx.lang_items().eq_trait(); + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) { + // `ty` implements `PartialEq` + } } } ``` @@ -159,7 +161,7 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { @@ -173,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) + && return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String) { // ... } diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index b3126024b990d..7819a477f608c 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -16,7 +16,7 @@ the [`ExprKind`] that we can access from `expr.kind`: use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_span::sym; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -28,7 +28,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && is_trait_method(cx, self_arg, sym::OurFancyTrait) + && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -56,7 +56,7 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::return_ty; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String) + && return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String) { println!("`our_fancy_method` is implemented!"); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9ae3663057720..a54d32268bf84 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,6 +66,7 @@ pub mod msrvs; pub mod numeric_literal; pub mod paths; pub mod qualify_min_const_fn; +pub mod res; pub mod source; pub mod str_utils; pub mod sugg; diff --git a/clippy_utils/src/res.rs b/clippy_utils/src/res.rs new file mode 100644 index 0000000000000..90b9529278964 --- /dev/null +++ b/clippy_utils/src/res.rs @@ -0,0 +1,643 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + self as hir, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, TyKind, +}; +use rustc_lint::LateContext; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{AdtDef, AdtKind, Binder, EarlyBinder, Ty, TypeckResults}; +use rustc_span::{Ident, Symbol}; + +/// Either a `HirId` or a type which can be identified by one. +pub trait HasHirId: Copy { + fn hir_id(self) -> HirId; +} +impl HasHirId for HirId { + #[inline] + fn hir_id(self) -> HirId { + self + } +} +impl HasHirId for &Expr<'_> { + #[inline] + fn hir_id(self) -> HirId { + self.hir_id + } +} + +type DefRes = (DefKind, DefId); + +pub trait MaybeTypeckRes<'tcx> { + /// Gets the contained `TypeckResults`. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>>; + + /// Gets the type-dependent resolution of the specified node. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_based_def(&self, node: impl HasHirId) -> Option { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn f(typeck: &TypeckResults<'_>, id: HirId) -> Option { + if typeck.hir_owner == id.owner { + let def = typeck.type_dependent_def(id); + debug_assert!( + def.is_some(), + "attempted type-dependent lookup for a node with no definition\ + \n node `{id:?}`", + ); + def + } else { + debug_assert!( + false, + "attempted type-dependent lookup for a node in the wrong body\ + \n in body `{:?}`\ + \n expected body `{:?}`", + typeck.hir_owner, id.owner, + ); + None + } + } + self.typeck_res().and_then(|typeck| f(typeck, node.hir_id())) + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + if let Some(typeck) = self.maybe_typeck_results() { + Some(typeck) + } else { + // It's possible to get the `TypeckResults` for any other body, but + // attempting to lookup the type of something across bodies like this + // is a good indication of a bug. + debug_assert!(false, "attempted type-dependent lookup in a non-body context"); + None + } + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for TypeckResults<'tcx> { + #[inline] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + Some(self) + } +} + +/// A `QPath` with the `HirId` of the node containing it. +type QPathId<'tcx> = (&'tcx QPath<'tcx>, HirId); + +/// A HIR node which might be a `QPath`. +pub trait MaybeQPath<'a>: Copy { + /// If this node is a path gets both the contained path and the `HirId` to + /// use for type dependant lookup. + fn opt_qpath(self) -> Option>; + + /// If this node is a `QPath::LangItem` gets the item it resolves to. + #[inline] + fn opt_lang_path(self) -> Option { + match self.opt_qpath() { + Some((&QPath::LangItem(item, _), _)) => Some(item), + _ => None, + } + } + + /// If this is a path gets its resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved(_, p) => p.res, + QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { + Res::Def(kind, id) + }, + QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path with the specified name as its final segment gets its + /// resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved(_, p) + if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative(_, seg) + if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a path gets both its resolution and final segment. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> (Res, Option<&'a PathSegment<'a>>) { + #[cfg_attr(debug_assertions, track_caller)] + fn f<'a>(qpath: &QPath<'a>, id: HirId, typeck: &TypeckResults<'_>) -> (Res, Option<&'a PathSegment<'a>>) { + match *qpath { + QPath::Resolved(_, p) if let [.., seg] = p.segments => (p.res, Some(seg)), + QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => { + (Res::Def(kind, id), Some(seg)) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None), + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => (Res::Err, None), + } + } + + /// If this is a path without an explicit `Self` type gets its resolution. + /// Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) => p.res, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + _, + ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path without an explicit `Self` type to an item with the + /// specified name gets its resolution. Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + seg, + ) if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a type-relative path gets the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option { + match self.opt_qpath() { + Some((QPath::TypeRelative(..), id)) => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path to an item with the specified name gets + /// the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Option { + match self.opt_qpath() { + Some((&QPath::TypeRelative(_, seg), id)) if seg.ident.name == name => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path gets the definition it resolves to and + /// its final segment. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<(DefRes, &'a PathSegment<'a>)> { + match self.opt_qpath() { + Some((QPath::TypeRelative(_, seg), id)) if let Some(def) = typeck.ty_based_def(id) => Some((def, seg)), + _ => None, + } + } +} + +impl<'tcx> MaybeQPath<'tcx> for QPathId<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + Some((self.0, self.1)) + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx Expr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + ExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx, AmbigArg> MaybeQPath<'tcx> for &'tcx hir::Ty<'_, AmbigArg> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + TyKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'_ Pat<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + match self.kind { + PatKind::Expr(e) => e.opt_qpath(), + _ => None, + } + } +} +impl<'tcx, T: MaybeQPath<'tcx>> MaybeQPath<'tcx> for Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} +impl<'tcx, T: Copy + MaybeQPath<'tcx>> MaybeQPath<'tcx> for &Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} + +/// A resolved path and the explicit `Self` type if there is one. +type OptResPath<'tcx> = (Option<&'tcx hir::Ty<'tcx>>, Option<&'tcx Path<'tcx>>); + +/// A HIR node which might be a `QPath::Resolved`. +/// +/// The following are resolved paths: +/// * A path to a module or crate item. +/// * A path to a trait item via the trait's name. +/// * A path to a struct or variant constructor via the original type's path. +/// * A local. +/// +/// All other paths are `TypeRelative` and require using `PathRes` to lookup the +/// resolution. +pub trait MaybeResPath<'a>: Copy { + /// If this node is a resolved path gets both the contained path and the + /// type associated with it. + fn opt_res_path(self) -> OptResPath<'a>; + + /// If this node is a resolved path gets it's resolution. Returns `Res::Err` + /// otherwise. + #[inline] + fn basic_res(self) -> &'a Res { + self.opt_res_path().1.map_or(&Res::Err, |p| &p.res) + } + + /// If this node is a path to a local gets the local's `HirId`. + #[inline] + fn res_local_id(self) -> Option { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + { + Some(id) + } else { + None + } + } + + /// If this node is a path to a local gets the local's `HirId` and identifier. + fn res_local_id_and_ident(self) -> Option<(HirId, &'a Ident)> { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + && let [seg] = p.segments + { + Some((id, &seg.ident)) + } else { + None + } + } +} +impl<'a> MaybeResPath<'a> for &'a Path<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + (None, Some(self)) + } + + #[inline] + fn basic_res(self) -> &'a Res { + &self.res + } +} +impl<'a> MaybeResPath<'a> for &QPath<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match *self { + QPath::Resolved(ty, path) => (ty, Some(path)), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Expr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + ExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &PatExpr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + PatExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, AmbigArg> MaybeResPath<'a> for &hir::Ty<'a, AmbigArg> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + TyKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Pat<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self.kind { + PatKind::Expr(e) => e.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self { + Some(x) => T::opt_res_path(x), + None => (None, None), + } + } + + #[inline] + fn basic_res(self) -> &'a Res { + self.map_or(&Res::Err, T::basic_res) + } +} + +/// A type which may either contain a `DefId` or be referred to by a `DefId`. +pub trait MaybeDef: Copy { + fn opt_def_id(self) -> Option; + + /// Gets this definition's id and kind. This will lookup the kind in the def + /// tree if needed. + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)>; + + /// Gets the diagnostic name of this definition if it has one. + #[inline] + fn opt_diag_name<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().get_diagnostic_name(id)) + } + + /// Checks if this definition has the specified diagnostic name. + #[inline] + fn is_diag_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, name: Symbol) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().is_diagnostic_item(name, id)) + } + + /// Checks if this definition is the specified `LangItem`. + #[inline] + fn is_lang_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, item: LangItem) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().lang_items().get(item) == Some(id)) + } + + /// If this definition is an impl block gets its type. + #[inline] + fn opt_impl_ty<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option>> { + match self.opt_def(tcx) { + Some((DefKind::Impl { .. }, id)) => Some(tcx.tcx().type_of(id)), + _ => None, + } + } + + /// Gets the parent of this definition if it has one. + #[inline] + fn opt_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().opt_parent(id)) + } + + /// Checks if this definition is an impl block. + #[inline] + fn is_impl<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> bool { + matches!(self.opt_def(tcx), Some((DefKind::Impl { .. }, _))) + } + + /// If this definition is a constructor gets the `DefId` of it's type or variant. + #[inline] + fn ctor_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::Ctor(..), id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated item of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated function of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_fn_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocFn, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } +} +impl MaybeDef for DefId { + #[inline] + fn opt_def_id(self) -> Option { + Some(self) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.opt_def_id().map(|id| (tcx.tcx().def_kind(id), id)) + } +} +impl MaybeDef for (DefKind, DefId) { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.1) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + Some(self) + } +} +impl MaybeDef for AdtDef<'_> { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.did()) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + let did = self.did(); + match self.adt_kind() { + AdtKind::Enum => Some((DefKind::Enum, did)), + AdtKind::Struct => Some((DefKind::Struct, did)), + AdtKind::Union => Some((DefKind::Union, did)), + } + } +} +impl MaybeDef for Ty<'_> { + #[inline] + fn opt_def_id(self) -> Option { + self.ty_adt_def().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.ty_adt_def().opt_def(tcx) + } +} +impl MaybeDef for Res { + #[inline] + fn opt_def_id(self) -> Option { + Res::opt_def_id(&self) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + match self { + Res::Def(kind, id) => Some((kind, id)), + _ => None, + } + } +} +impl MaybeDef for Option { + #[inline] + fn opt_def_id(self) -> Option { + self.and_then(T::opt_def_id) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.and_then(|x| T::opt_def(x, tcx)) + } +} +impl MaybeDef for EarlyBinder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} +impl MaybeDef for Binder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} From d32ef64ed521df8307f0503111a195b01ffcbac1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 04:09:27 -0400 Subject: [PATCH 1534/1900] Remove `is_path_lang_item` --- clippy_lints/src/missing_fields_in_debug.rs | 7 +++++-- clippy_lints/src/pub_underscore_fields.rs | 4 ++-- clippy_lints/src/question_mark.rs | 17 +++++++++-------- clippy_lints/src/ranges.rs | 6 +++--- clippy_lints/src/types/owned_cow.rs | 3 ++- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/lib.rs | 9 ++------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 8822b32b1c3d6..1b5671b8d0164 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,10 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -180,7 +181,9 @@ fn check_struct<'tcx>( .fields() .iter() .filter_map(|field| { - if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { + if field_accesses.contains(&field.ident.name) + || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + { None } else { Some((field.span, "this field is unused")) diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 66c59cb70d361..694d44d708568 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::is_path_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // We ignore fields that have `#[doc(hidden)]`. && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. - && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) + && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) { span_lint_hir_and_then( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d3a5a5dddfbeb..49c58a276cdd9 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,14 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, + is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -521,11 +522,11 @@ impl QuestionMark { } } -fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { +fn is_try_block(bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr && let ExprKind::Call(callee, [_]) = expr.kind { - is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) + callee.opt_lang_path() == Some(LangItem::TryTraitFromOutput) } else { false } @@ -581,8 +582,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() @@ -598,8 +599,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0b2313cb7eeb9..945b67f821f6e 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, is_path_lang_item, - path_to_local, + expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local, }; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; @@ -370,7 +370,7 @@ fn can_switch_ranges<'tcx>( // Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)` if let ExprKind::Call(func, [arg]) = parent_expr.kind && arg.hir_id == use_ctxt.child_id - && is_path_lang_item(cx, func, LangItem::IntoIterIntoIter) + && func.opt_lang_path() == Some(LangItem::IntoIterIntoIter) { return true; } diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 8933994d18558..9d4d885bf6cd6 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -30,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, def_id: DefId) } fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String)> { - if clippy_utils::is_path_lang_item(cx, cty, hir::LangItem::String) { + if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 08210ae2cefb9..f25387b5e66cc 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { - chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + chain!(self, "{path}.res(cx).is_lang_item(cx, LangItem::{}", lang.name()); } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); } else { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a54d32268bf84..e92373941dc11 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,6 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; +use crate::res::{MaybeDef, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -437,12 +438,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given diagnostic item. pub fn is_path_diagnostic_item<'tcx>( @@ -788,7 +783,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(sym, _), .. - }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), + }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind From cb32444ee6de2b2cde9b495fa9f69f5f192b68eb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:27:27 -0400 Subject: [PATCH 1535/1900] Remove `is_path_diagnostic_item` --- clippy_lints/src/casts/manual_dangling_ptr.rs | 5 +++-- clippy_lints/src/manual_option_as_slice.rs | 7 ++++--- .../src/methods/from_iter_instead_of_collect.rs | 5 +++-- clippy_lints/src/methods/io_other_error.rs | 5 +++-- clippy_lints/src/methods/manual_str_repeat.rs | 4 ++-- .../src/methods/needless_character_iteration.rs | 5 +++-- clippy_lints/src/methods/uninit_assumed_init.rs | 4 ++-- clippy_lints/src/slow_vector_initialization.rs | 10 +++++----- clippy_lints/src/time_subtraction.rs | 5 +++-- clippy_lints/src/to_digit_is_some.rs | 5 +++-- clippy_lints/src/transmute/transmute_null_to_fn.rs | 5 +++-- clippy_lints/src/transmute/transmuting_null.rs | 5 +++-- clippy_lints/src/types/owned_cow.rs | 6 +++--- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/lib.rs | 14 ++------------ tests/ui/author/blocks.stdout | 4 ++-- tests/ui/author/call.stdout | 2 +- tests/ui/author/issue_3849.stdout | 2 +- 18 files changed, 47 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index ff5320719aa28..be1f406770ce6 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym}; +use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -53,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind - && is_path_diagnostic_item(cx, fun, sym::mem_align_of) + && fun.basic_res().is_diag_item(cx, sym::mem_align_of) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index b036e78cdedc5..63f6d89f2ad74 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -189,7 +190,7 @@ fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> b fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Path(_) => expr.res(cx).is_diag_item(cx, sym::default_fn), ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir_body(cl.body).value), _ => false, } @@ -214,11 +215,11 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, }, ExprKind::Array([]) => true, - ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + ExprKind::Call(def, []) => def.res(cx).is_diag_item(cx, sym::default_fn), _ => false, } } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref) + expr.basic_res().is_diag_item(cx, sym::slice_from_ref) } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index d664eaaac704f..11671f377e668 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,9 +1,10 @@ use std::fmt::Write as _; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,7 +16,7 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + if func.res(cx).is_diag_item(cx, sym::from_iter_fn) && let arg_ty = cx.typeck_results().expr_ty(&args[0]) && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && implements_trait(cx, arg_ty, iter_id, &[]) diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index 9276261606e1e..b081e804859a5 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{expr_or_init, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args && !expr.span.from_expansion() && !error_kind.span.from_expansion() && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind - && is_path_diagnostic_item(cx, path, sym::io_error_new) + && path.ty_rel_def(cx).is_diag_item(cx, sym::io_error_new) && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind && let [.., error_kind_ty, error_kind_variant] = init_path.segments && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id()) diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a811dd1cee18f..7d6f56e4b31b6 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -55,7 +55,7 @@ pub(super) fn check( take_arg: &Expr<'_>, ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind - && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) + && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 71c1576cd57d1..d161c002d083f 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -75,7 +76,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) + && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 6371fe6442826..5e247a50358e6 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let hir::ExprKind::Call(callee, []) = recv.kind - && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && callee.ty_rel_def(cx).is_diag_item(cx, sym::maybe_uninit_uninit) && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) { span_lint( diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index f497d0700b8eb..237c8e66ee3fc 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, sym, + SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local, path_to_local_id, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -149,10 +149,10 @@ impl SlowVectorInit { } if let ExprKind::Call(func, [len_expr]) = expr.kind - && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) + && func.ty_rel_def(cx).is_diag_item(cx, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) { + } else if matches!(expr.kind, ExprKind::Call(func, []) if func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None @@ -301,7 +301,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind - && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && fn_expr.basic_res().is_diag_item(self.cx, sym::iter_repeat) && is_integer_literal(repeat_arg, 0) { true diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index fde8c3d9a9a76..c25668817f8ba 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; +use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -146,7 +147,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + && cx.ty_based_def(fn_expr).is_diag_item(cx, sym::instant_now) { true } else { diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 3e847543e1c16..7b90f20b6ba3c 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) { + if to_digits_call.ty_rel_def(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 7acf3be51fb7e..e109b1c50e227 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -40,7 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t }, // Catching: // `std::mem::transmute(std::ptr::null::())` - ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + ExprKind::Call(func1, []) if func1.basic_res().is_diag_item(cx, sym::ptr_null) => { lint_expr(cx, expr); true }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 544014bd32b30..1a6262f2ff767 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // `std::mem::transmute(std::ptr::null::())` if let ExprKind::Call(func1, []) = arg.kind - && is_path_diagnostic_item(cx, func1, sym::ptr_null) + && func1.basic_res().is_diag_item(cx, sym::ptr_null) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 9d4d885bf6cd6..0eef373be04ea 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -34,7 +34,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { + if cty.basic_res().is_diag_item(cx, sym::Vec) { return if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = cty.kind && let [.., last_seg] = path.segments && let Some(args) = last_seg.args @@ -46,7 +46,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) None }; } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::cstring_type) { + if cty.basic_res().is_diag_item(cx, sym::cstring_type) { return Some(( cty.span, (if clippy_utils::is_no_std_crate(cx) { @@ -59,7 +59,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) } // Neither OsString nor PathBuf are available outside std for (diag, repl) in [(sym::OsString, "std::ffi::OsStr"), (sym::PathBuf, "std::path::Path")] { - if clippy_utils::is_path_diagnostic_item(cx, cty, diag) { + if cty.basic_res().is_diag_item(cx, diag) { return Some((cty.span, repl.into())); } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index f25387b5e66cc..0c388fbd4812f 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { chain!(self, "{path}.res(cx).is_lang_item(cx, LangItem::{}", lang.name()); } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { - chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + chain!(self, "{path}.res(cx).is_diag_item(cx, sym::{name})"); } else { chain!( self, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e92373941dc11..a976c9a7dafe1 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -438,16 +438,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator( - cx: &LateContext<'_>, - maybe_path: &impl MaybePath<'tcx>, - diag_item: Symbol, -) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind @@ -784,13 +774,13 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & node: LitKind::Str(sym, _), .. }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), - ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind && let LitKind::Int(v, _) = const_lit.node { - return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec); } }, _ => (), diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index e453299edbcfc..ff9fe2425ff9f 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -23,13 +23,13 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::string_new) + && func.res(cx).is_diag_item(cx, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && is_path_diagnostic_item(cx, func1, sym::mem_drop) + && func1.res(cx).is_diag_item(cx, sym::mem_drop) && args1.len() == 1 { // report your lint here diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 2b179d45112ee..024121b14f9bc 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::cmp_min) + && func.res(cx).is_diag_item(cx, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index f02ea5bf075f7..b88058f974dba 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::transmute) + && func.res(cx).is_diag_item(cx, sym::transmute) && args.len() == 1 && let PatKind::Wild = local.pat.kind { From 4914f5908ff18a84b5a2d53c7dbf5714f3b32c42 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 04:10:30 -0400 Subject: [PATCH 1536/1900] Remove `is_type_diagnostic_item` --- clippy_lints/src/arc_with_non_send_sync.rs | 5 ++- .../src/assertions_on_result_states.rs | 5 ++- clippy_lints/src/booleans.rs | 7 ++-- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/doc/missing_headers.rs | 7 ++-- clippy_lints/src/fallible_impl_from.rs | 6 +-- clippy_lints/src/functions/result.rs | 5 ++- clippy_lints/src/if_let_mutex.rs | 4 +- clippy_lints/src/implicit_hasher.rs | 6 +-- clippy_lints/src/ineffective_open_options.rs | 8 +++- clippy_lints/src/iter_over_hash_type.rs | 6 +-- clippy_lints/src/lines_filter_map_ok.rs | 7 +++- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/missing_spin_loop.rs | 4 +- clippy_lints/src/loops/same_item_push.rs | 5 ++- .../src/loops/unused_enumerate_index.rs | 4 +- clippy_lints/src/manual_abs_diff.rs | 5 ++- clippy_lints/src/manual_ignore_case_cmp.rs | 5 ++- clippy_lints/src/manual_let_else.rs | 4 +- clippy_lints/src/map_unit_fn.rs | 6 +-- clippy_lints/src/match_result_ok.rs | 4 +- clippy_lints/src/matches/manual_filter.rs | 4 +- clippy_lints/src/matches/manual_utils.rs | 6 +-- clippy_lints/src/matches/match_wild_enum.rs | 5 +-- .../src/matches/match_wild_err_arm.rs | 4 +- clippy_lints/src/matches/needless_match.rs | 5 ++- .../src/matches/redundant_pattern_match.rs | 5 ++- clippy_lints/src/methods/bytecount.rs | 8 +++- clippy_lints/src/methods/clear_with_drain.rs | 5 ++- clippy_lints/src/methods/err_expect.rs | 7 ++-- clippy_lints/src/methods/expect_fun_call.rs | 7 ++-- clippy_lints/src/methods/extend_with_drain.rs | 7 ++-- clippy_lints/src/methods/filetype_is_file.rs | 4 +- clippy_lints/src/methods/filter_map.rs | 4 +- clippy_lints/src/methods/flat_map_option.rs | 4 +- clippy_lints/src/methods/get_first.rs | 4 +- .../src/methods/iter_cloned_collect.rs | 5 ++- clippy_lints/src/methods/iter_count.rs | 18 ++++----- clippy_lints/src/methods/iter_kv_map.rs | 4 +- clippy_lints/src/methods/iter_next_slice.rs | 4 +- .../src/methods/join_absolute_paths.rs | 4 +- .../src/methods/manual_is_variant_and.rs | 8 ++-- clippy_lints/src/methods/manual_ok_or.rs | 8 +++- clippy_lints/src/methods/manual_str_repeat.rs | 4 +- clippy_lints/src/methods/map_clone.rs | 5 ++- .../src/methods/map_collect_result_unit.rs | 4 +- clippy_lints/src/methods/map_err_ignore.rs | 8 +++- clippy_lints/src/methods/map_flatten.rs | 4 +- clippy_lints/src/methods/map_identity.rs | 7 ++-- clippy_lints/src/methods/map_unwrap_or.rs | 6 +-- clippy_lints/src/methods/mut_mutex_lock.rs | 5 ++- .../src/methods/needless_option_as_deref.rs | 4 +- .../src/methods/needless_option_take.rs | 4 +- clippy_lints/src/methods/ok_expect.rs | 7 ++-- clippy_lints/src/methods/open_options.rs | 4 +- .../src/methods/option_as_ref_cloned.rs | 8 +++- .../src/methods/option_as_ref_deref.rs | 4 +- .../src/methods/option_map_or_none.rs | 6 +-- .../src/methods/option_map_unwrap_or.rs | 5 ++- clippy_lints/src/methods/or_fun_call.rs | 7 ++-- clippy_lints/src/methods/or_then_unwrap.rs | 6 +-- .../src/methods/path_buf_push_overwrite.rs | 8 +++- .../src/methods/path_ends_with_ext.rs | 8 +++- .../src/methods/read_line_without_trim.rs | 6 +-- .../src/methods/readonly_write_lock.rs | 9 +++-- .../src/methods/result_map_or_else_none.rs | 4 +- .../methods/suspicious_command_arg_space.rs | 4 +- .../src/methods/suspicious_to_owned.rs | 4 +- .../src/methods/unnecessary_get_then_check.rs | 6 +-- .../src/methods/unnecessary_lazy_eval.rs | 6 +-- .../methods/unnecessary_option_map_or_else.rs | 4 +- .../methods/unnecessary_result_map_or_else.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 9 ++--- .../src/methods/unused_enumerate_index.rs | 4 +- .../src/methods/unwrap_expect_used.rs | 7 ++-- .../methods/useless_nonzero_new_unchecked.rs | 4 +- clippy_lints/src/methods/utils.rs | 4 +- .../src/methods/vec_resize_to_zero.rs | 8 +++- .../src/methods/verbose_file_reads.rs | 8 +++- clippy_lints/src/missing_fields_in_debug.rs | 9 ++--- clippy_lints/src/mutex_atomic.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 7 ++-- .../src/operators/arithmetic_side_effects.rs | 6 +-- clippy_lints/src/operators/duration_subsec.rs | 8 +++- .../src/operators/integer_division.rs | 4 +- clippy_lints/src/panic_in_result_fn.rs | 4 +- clippy_lints/src/partialeq_to_none.rs | 10 +++-- clippy_lints/src/pathbuf_init_then_push.rs | 6 +-- .../src/permissions_set_readonly_false.rs | 7 +++- clippy_lints/src/question_mark.rs | 8 ++-- clippy_lints/src/set_contains_or_insert.rs | 4 +- clippy_lints/src/single_option_map.rs | 4 +- clippy_lints/src/strlen_on_c_strings.rs | 5 ++- clippy_lints/src/swap.rs | 6 +-- clippy_lints/src/time_subtraction.rs | 15 ++++---- clippy_lints/src/to_digit_is_some.rs | 2 +- clippy_lints/src/uninit_vec.rs | 13 ++++--- clippy_lints/src/unused_peekable.rs | 9 +++-- clippy_lints/src/unused_result_ok.rs | 4 +- clippy_lints/src/useless_conversion.rs | 7 ++-- clippy_lints/src/volatile_composites.rs | 4 +- clippy_lints/src/zero_sized_map_values.rs | 5 ++- clippy_lints/src/zombie_processes.rs | 4 +- .../src/unnecessary_def_path.rs | 2 +- clippy_utils/src/higher.rs | 6 +-- clippy_utils/src/ty/mod.rs | 38 ++++++------------- 106 files changed, 349 insertions(+), 298 deletions(-) diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 085029a744bec..acfdfa65baeda 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind && func_name.ident.name == sym::new && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) + && cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 08253b0c4995e..191fbf65e5a6c 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; @@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() - && is_type_diagnostic_item(cx, result_type, sym::Result) + && result_type.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = result_type.kind() { if !is_copy(cx, result_type) { diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 64aeb27df693f..f3985603c4d2e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio }, ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); - if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) - && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) - { + if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) { return None; } METHODS_WITH_NEGATION diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index c0f30e456d8db..595625c08bef9 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{IntoSpan, SpanRangeExt}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; @@ -93,7 +93,7 @@ impl CognitiveComplexity { }); let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { + let ret_adjust = if ret_ty.is_diag_item(cx, sym::Result) { returns } else { #[expect(clippy::integer_division)] diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 3033ac0d0b0b7..7c325132f8792 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,7 +1,8 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -62,7 +63,7 @@ pub fn check( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) { + if return_ty(cx, owner_id).is_diag_item(cx, sym::Result) { span_lint( cx, MISSING_ERRORS_DOC, @@ -83,7 +84,7 @@ pub fn check( &[], ) && let ty::Coroutine(_, subs) = ret_ty.kind() - && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + && subs.as_coroutine().return_ty().is_diag_item(cx, sym::Result) { span_lint( cx, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index fdfcbb540bce6..4446b912bf7ee 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -84,9 +84,7 @@ fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Sp // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { + if receiver_ty.is_diag_item(self.lcx, sym::Option) || receiver_ty.is_diag_item(self.lcx, sym::Result) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 1f2fce687ed1b..fb80cc1a63a30 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -1,4 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; use clippy_utils::{is_no_std_crate, trait_ref_of_method}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -24,7 +25,7 @@ fn result_err_ty<'tcx>( && let ty = cx .tcx .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) - && is_type_diagnostic_item(cx, ty, sym::Result) + && ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = ty.kind() { let err_ty = args.type_at(1); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index a99118f90f88a..eed2d5d885353 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, higher, sym}; use core::ops::ControlFlow; @@ -95,7 +95,7 @@ fn mutex_lock_call<'tcx>( if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::lock && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op)) { ControlFlow::Break(self_arg) diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index b3c90f364e834..d2bc0b6d99354 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -14,7 +15,6 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { /// ### What it does @@ -227,14 +227,14 @@ impl<'tcx> ImplicitHasherType<'tcx> { let ty = lower_ty(cx.tcx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 { + if ty.is_diag_item(cx, sym::HashMap) && params_len == 2 { Some(ImplicitHasherType::HashMap( hir_ty.span, ty, snippet(cx, params[0].span, "K"), snippet(cx, params[1].span, "V"), )) - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 { + } else if ty.is_diag_item(cx, sym::HashSet) && params_len == 1 { Some(ImplicitHasherType::HashSet( hir_ty.span, ty, diff --git a/clippy_lints/src/ineffective_open_options.rs b/clippy_lints/src/ineffective_open_options.rs index a159f61571839..bc57d9e85478a 100644 --- a/clippy_lints/src/ineffective_open_options.rs +++ b/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -47,7 +47,11 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind && name.ident.name == sym::open && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions) + && cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::FsOpenOptions) { let mut append = false; let mut write = None; diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index b1cb6da9475ba..6bb46ac3b5542 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -55,9 +55,7 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys - .into_iter() - .any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys.into_iter().any(|sym| ty.is_diag_item(cx, sym)) { span_lint( cx, diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index 14ccb6fce2239..ab00cd9ca539c 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; @@ -76,7 +76,10 @@ impl LateLintPass<'_> for LinesFilterMapOk { && is_trait_method(cx, expr, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) + && cx + .typeck_results() + .expr_ty_adjusted(fm_receiver) + .is_diag_item(cx, sym::IoLines) && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index e314bc2068b30..c6b650a1a88be 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,7 +1,7 @@ use super::FOR_KV_MAP; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + if ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) { span_lint_and_then( cx, FOR_KV_MAP, diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 8a2d0036203a2..d5dbcbe9dc701 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,7 +1,7 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::std_or_core; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let callee_ty = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) + && callee_ty.is_diag_item(cx, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index e792edbe23e0b..097b84de49255 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,8 +1,9 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -186,7 +187,7 @@ fn get_vec_push<'tcx>( && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec && path.ident.name == sym::push - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) + && cx.typeck_results().expr_ty(self_expr).is_diag_item(cx, sym::Vec) { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 13b93d2c0097b..b893b0baad490 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && is_type_diagnostic_item(cx, ty, sym::Enumerate) + && ty.is_diag_item(cx, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 5814b6815a1ea..22de5e8268bd9 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -104,7 +105,7 @@ impl ManualAbsDiff { fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option<(Ty<'tcx>, usize)> { let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); let is_duration = - |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + |ty: Ty<'_>| ty.is_diag_item(cx, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index f7d9ec1fae8e4..ae17b4e446e4c 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,8 +1,9 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -72,7 +73,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) - || is_type_diagnostic_item(cx, ty, sym::Vec) + || ty.is_diag_item(cx, sym::Vec) || is_type_lang_item(cx, ty, LangItem::String) } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 2705ef20b795d..3ed24b5dd2c55 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,8 +2,8 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, }; @@ -384,7 +384,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. - if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + if !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) { has_disallowed = true; } }); diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 39e5289c62ae4..681dc2ab5bc0c 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -205,9 +205,9 @@ fn lint_map_unit_fn( ) { let var_arg = &map_args.0; - let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { + let (map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { + } else if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Result) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index e0cb5d14d3c93..bc9279677b2eb 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_res_lang_ctor, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index abf723fa6f4ca..96252a82fc0d9 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; @@ -98,7 +98,7 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, ty, sym::Option) + if ty.is_diag_item(cx, sym::Option) && let [first_arm, second_arm] = arms && first_arm.guard.is_none() && second_arm.guard.is_none() diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index d4bfdb7e440d8..5e989beb9fd1f 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,8 +1,9 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; +use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -33,8 +34,7 @@ where let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + if !(scrutinee_ty.is_diag_item(cx, sym::Option) && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Option)) { return None; } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 70a03ff937625..ac5da320bdffb 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -16,8 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { ty::Adt(adt_def, _) - if adt_def.is_enum() - && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) => + if adt_def.is_enum() && !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) => { adt_def }, diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index 8ce8453360f78..e38ba801c0bf7 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym::Result) { + if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 3a2097c3df261..e6f47707e3a29 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,8 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -104,7 +105,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool return false; } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); - if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { + if let_expr_ty.is_diag_item(cx, sym::Option) { return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 9d0115791838c..916ed25cfd748 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,8 +1,9 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; -use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; use rustc_ast::ast::LitKind; @@ -460,7 +461,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte Item::Diag(expected_ty, expected_variant) => { let ty = cx.typeck_results().pat_ty(pat); - if is_type_diagnostic_item(cx, ty, expected_ty) { + if ty.is_diag_item(cx, expected_ty) { let variant = ty .ty_adt_def() .expect("struct pattern type is not an ADT") diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index 0498f317442ac..f3a34ce6964ed 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check<'tcx>( && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind && let ExprKind::Binary(ref op, l, r) = body.value.kind && op.node == BinOpKind::Eq - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && cx + .typeck_results() + .expr_ty(filter_recv) + .peel_refs() + .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); path_to_local_id(expr, arg_id) diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 6e24cabca8bba..77b1ec3f97883 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_span::Symbol]) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) + types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check || is_type_lang_item(cx, expr_ty, LangItem::String) } diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 91ddaca07d8bb..6e9aebcf18ae4 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,8 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -16,7 +17,7 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // Grabs the `Result` type && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result` is not None @@ -40,7 +41,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), _ => None, } } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 74920a17310d5..ea49fd414457d 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; @@ -26,9 +27,9 @@ pub(super) fn check<'tcx>( let arg_root = get_arg_root(cx, arg); if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { + let closure_args = if receiver_type.is_diag_item(cx, sym::Option) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::Result) { + } else if receiver_type.is_diag_item(cx, sym::Result) { "|_|" } else { return; diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index db60061904f6f..381e86173f85c 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind && src_method.ident.name == sym::drain @@ -18,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() && let src_ty = src_ty.peel_refs() - && is_type_diagnostic_item(cx, src_ty, sym::Vec) + && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 35008c39c084d..a964cbf657b4d 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !is_type_diagnostic_item(cx, ty, sym::FileType) { + if !ty.is_diag_item(cx, sym::FileType) { return; } diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index dc742fa058cb6..d813105165cef 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; @@ -267,7 +267,7 @@ fn is_filter_some_map_unwrap( map_arg: &Expr<'_>, ) -> bool { let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option); + let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) } diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 3242dcadb7013..3a7892715ed7d 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use rustc_middle::ty; use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; -use clippy_utils::ty::is_type_diagnostic_item; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { if !is_trait_method(cx, expr, sym::Iterator) { @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), _ => return, }; - if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::Option) { + if !sig.output().skip_binder().is_diag_item(cx, sym::Option) { return; } span_lint_and_sugg( diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 2e1d71ce284d6..88e9f2fdaee3e 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( format!("{slice_name}.first()"), app, ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + } else if identity.is_diag_item(cx, sym::VecDeque) { let mut app = Applicability::MachineApplicable; let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index b4ab313fe98d1..b1a0b658a84ce 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,6 +1,7 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_iterator_item_ty; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, ) { let expr_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, expr_ty, sym::Vec) + if expr_ty.is_diag_item(cx, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) && let ty::Adt(_, args) = expr_ty.kind() && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 6b64cc8b50ae7..ea2508cd7f382 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -13,21 +13,21 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, ty, sym::Vec) { + } else if ty.is_diag_item(cx, sym::Vec) { "Vec" - } else if is_type_diagnostic_item(cx, ty, sym::VecDeque) { + } else if ty.is_diag_item(cx, sym::VecDeque) { "VecDeque" - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) { + } else if ty.is_diag_item(cx, sym::HashSet) { "HashSet" - } else if is_type_diagnostic_item(cx, ty, sym::HashMap) { + } else if ty.is_diag_item(cx, sym::HashMap) { "HashMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + } else if ty.is_diag_item(cx, sym::BTreeMap) { "BTreeMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeSet) { + } else if ty.is_diag_item(cx, sym::BTreeSet) { "BTreeSet" - } else if is_type_diagnostic_item(cx, ty, sym::LinkedList) { + } else if ty.is_diag_item(cx, sym::LinkedList) { "LinkedList" - } else if is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { + } else if ty.is_diag_item(cx, sym::BinaryHeap) { "BinaryHeap" } else { return; diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index cbb1b450e60fc..2d6bc36dc5359 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -1,8 +1,8 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( _ => return, } && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index fd4650e1e45f6..01f35ff02d44a 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; use rustc_ast::ast; use rustc_errors::Applicability; @@ -75,6 +75,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal } fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 2ad070793cbb2..e84b7452c7584 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use super::JOIN_ABSOLUTE_PATHS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if (is_type_diagnostic_item(cx, ty, sym::Path) || is_type_diagnostic_item(cx, ty, sym::PathBuf)) + if (ty.is_diag_item(cx, sym::Path) || ty.is_diag_item(cx, sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind && let LitKind::Str(symbol, _) = spanned.node && let sym_str = symbol.as_str() diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 93325ca488e4e..8f65858987b9c 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -30,8 +30,8 @@ pub(super) fn check( } // 2. the caller of `map()` is neither `Option` nor `Result` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Result); if !is_option && !is_result { return; } @@ -208,7 +208,7 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.symbol()) && let Ok(map_func) = MapFunc::try_from(map_expr) { return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 077957fa44dc8..610368aabc5b5 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) && is_ok_wrapping(cx, map_expr) diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 7d6f56e4b31b6..a7052040c7c05 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -37,7 +37,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(e); if is_type_lang_item(cx, ty, LangItem::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) - || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) + || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 748be9bfcc626..5568f77927ab0 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; +use clippy_utils::ty::{is_copy, should_call_clone_as_function}; use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -25,7 +26,7 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> // We check if it's an `Option` or a `Result`. if let Some(id) = cx.tcx.impl_of_assoc(method_id) { let identity = cx.tcx.type_of(id).instantiate_identity(); - if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + if !identity.is_diag_item(cx, sym::Option) && !identity.is_diag_item(cx, sym::Result) { return false; } } else { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index e944eac91e7ae..1112fbc2a1c74 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + if collect_ret_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = collect_ret_ty.kind() && let Some(result_t) = args.types().next() && result_t.is_unit() diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 41beda9c5cb4a..f7da24bed2b80 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,11 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, body, diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 750f933330a25..18827d71110ec 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -69,7 +69,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.instantiate_bound_regions_with_erased(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + map_closure_return_ty.is_diag_item(cx, sym::Option) }, _ => false, } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 6190c43578e9b..f68967c750ef7 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; @@ -22,8 +23,8 @@ pub(super) fn check( let caller_ty = cx.typeck_results().expr_ty(caller); if (is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + || caller_ty.is_diag_item(cx, sym::Result) + || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index df5a0de3392b0..62bdc4a3e4111 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` or a `Result`. - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) { return false; diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 9d2c5e6232d65..c9264e747b56d 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + && cx.tcx.type_of(impl_id).is_diag_item(cx, sym::Mutex) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index d77d044340dca..b4361448404a1 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); - if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { let Res::Local(binding_id) = path_res(cx, recv) else { return; diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 1544a12e6ba83..1622fdb88bd53 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_type = cx.typeck_results().expr_ty(expr); - is_type_diagnostic_item(cx, expr_type, sym::Option) + expr_type.is_diag_item(cx, sym::Option) } /// Returns the string of the function call that creates the temporary. diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index e10bc0216e5fd..c9c1f4865b813 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -9,7 +10,7 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // lint if the caller of `ok()` is a `Result` && let result_type = cx.typeck_results().expr_ty(recv) && let Some(error_type) = get_error_type(cx, result_type) @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index 37a8e25bef96a..1b520a9edb566 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,7 +1,7 @@ +use clippy_utils::res::MaybeDef; use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{paths, sym}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use rustc_span::source_map::Spanned; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) + ty.is_diag_item(cx, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs index 3c38deca6cd1f..591f6aacaef87 100644 --- a/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -11,7 +11,11 @@ use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + && cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 906ead16fd0d1..4c99c34892f45 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -23,7 +23,7 @@ pub(super) fn check( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(as_ref_recv); - if !is_type_diagnostic_item(cx, option_ty, sym::Option) { + if !option_ty.is_diag_item(cx, sym::Option) { return; } diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 1a273f77fb7d0..2b94ecd470646 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -37,8 +37,8 @@ pub(super) fn check<'tcx>( def_arg: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 4ba8e01090427..32a9b4fe7c58b 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04e4503e4097f..aed4a0075c2fd 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -4,8 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, @@ -113,7 +114,7 @@ fn check_unwrap_or_default( let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` - if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + if receiver_ty.is_diag_item(cx, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { return false; } @@ -239,7 +240,7 @@ fn check_or_fn_call<'tcx>( && let self_ty = cx.typeck_results().expr_ty(self_expr) && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + .find(|&&i| self_ty.is_diag_item(cx, i.0) && i.2.contains(&name)) { let ctxt = span.ctxt(); let mut app = Applicability::HasPlaceholders; diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 1a760ea733d7f..f99e68fc419de 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; @@ -21,14 +21,14 @@ pub(super) fn check<'tcx>( let title; let or_arg_content: Span; - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { title = "found `.or(Some(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { or_arg_content = content; } else { return; } - } else if is_type_diagnostic_item(cx, ty, sym::Result) { + } else if ty.is_diag_item(cx, sym::Result) { title = "found `.or(Ok(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { or_arg_content = content; diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs index 32752ef7435fb..18046ff24f9d2 100644 --- a/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -12,7 +12,11 @@ use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::PathBuf) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Str(ref path_lit, _) = lit.node && let pushed_path = Path::new(path_lit.as_str()) diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs index d3f513e7abd27..87d1aa62b2c22 100644 --- a/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -1,8 +1,8 @@ use super::PATH_ENDS_WITH_EXT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check( msrv: Msrv, allowed_dotfiles: &FxHashSet<&'static str>, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + if cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::Path) && !path.span.from_expansion() && let ExprKind::Lit(lit) = path.kind && let LitKind::Str(path, StrStyle::Cooked) = lit.node diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 6738bbf0a12b9..a6dfbada53483 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; @@ -32,7 +32,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) + if recv_ty.is_diag_item(cx, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { @@ -45,7 +45,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if args.is_empty() && segment.ident.name == sym::parse && let parse_result_ty = cx.typeck_results().expr_ty(parent) - && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) + && parse_result_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() && let Some(ok_ty) = substs[0].as_type() && parse_fails_on_trailing_newline(ok_ty) diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 40b6becd45321..a98a807d1a3ce 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -1,8 +1,8 @@ use super::READONLY_WRITE_LOCK; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::mir::{enclosing_mir, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; @@ -13,14 +13,17 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind && path.ident.name == sym::unwrap { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) + cx.typeck_results() + .expr_ty(receiver) + .peel_refs() + .is_diag_item(cx, sym::Result) } else { false } } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) + if cx.typeck_results().expr_ty(receiver).peel_refs().is_diag_item(cx, sym::RwLock) && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index af619c9e3bb17..7aeda9493c2c0 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx hir::Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index c60a49067ec09..3e9c677fe34a2 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::SUSPICIOUS_COMMAND_ARG_SPACE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Command) + if ty.is_diag_item(cx, sym::Command) && let hir::ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(s, _) = &lit.node && let Some((arg1, arg2)) = s.as_str().split_once(' ') diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index ffc237e3c24cc..9a9c09bc3d062 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_diag_trait_item(cx, method_def_id, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, input_type, sym::Cow) + && input_type.is_diag_item(cx, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index 39fce2c40c91d..10ea0c0c3e23e 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,11 +11,11 @@ use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; fn is_a_std_set_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) || is_type_diagnostic_item(cx, ty, sym::BTreeSet) + ty.is_diag_item(cx, sym::HashSet) || ty.is_diag_item(cx, sym::BTreeSet) } fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) + ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) } pub(super) fn check( diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 71e606add526e..2869547650f31 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -19,8 +19,8 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) -> bool { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if (is_option || is_result || is_bool) diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs index 35b72fe8fc194..265619e263765 100644 --- a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -75,7 +75,7 @@ fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_ar /// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { // lint if the caller of `map_or_else()` is an `Option` - if !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if !cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { return; } match map_arg.kind { diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index f84d0d6dff0af..1f6bb60414ae1 100644 --- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6d927aef8b024..e57cfae88f1f3 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,10 +2,9 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, @@ -220,7 +219,7 @@ fn check_into_iter_call_arg( && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. - && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 && !is_expr_temporary_value(cx, receiver) { @@ -678,7 +677,7 @@ fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<' fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { (original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice()) - && is_type_diagnostic_item(cx, arg_ty, sym::Vec) + && arg_ty.is_diag_item(cx, sym::Vec) } // This function will check the following: diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index af4ade3cc0f7d..554db52653a35 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; @@ -40,7 +40,7 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); // If we call a method on a `std::iter::Enumerate` instance - if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) + if recv_ty.is_diag_item(cx, sym::Enumerate) // If we are calling a method of the `Iterator` trait && is_trait_method(cx, call_expr, sym::Iterator) // And the map argument is a closure diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 027215e3b4d7b..73a407be4f210 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; use rustc_lint::{LateContext, Lint}; @@ -45,9 +46,9 @@ pub(super) fn check( ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err { + let (kind, none_value, none_prefix) = if ty.is_diag_item(cx, sym::Option) && !is_err { ("an `Option`", "None", "") - } else if is_type_diagnostic_item(cx, ty, sym::Result) + } else if ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = ty.kind() && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() { diff --git a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs index 22df1f3f485e1..c6f54159c7a78 100644 --- a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs +++ b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_inside_always_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<' && segment.ident.name == sym::new_unchecked && let [init_arg] = args && is_inside_always_const_context(cx.tcx, expr.hir_id) - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + && cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::NonZero) && msrv.meets(cx, msrvs::CONST_UNWRAP) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index b0cc7a785bc31..9b670266d0a9c 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,4 +1,4 @@ -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; @@ -20,7 +20,7 @@ pub(super) fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), + ty::Adt(..) => ty.is_diag_item(cx, sym::Vec), ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(), ty::Ref(_, inner, _) => may_slice(cx, *inner), _ => false, diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index bfb481f4fc092..5debaab2067bd 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Vec) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index 8ed61637eca21..d9927b6e368bd 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -21,7 +21,11 @@ pub(super) fn check<'tcx>( ) { if is_trait_method(cx, expr, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .peel_refs() + .is_diag_item(cx, sym::File) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 1b5671b8d0164..15b773c2c64f4 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -3,7 +3,6 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; @@ -113,11 +112,9 @@ fn should_lint<'tcx>( if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); - if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { + if path.ident.name == sym::debug_struct && recv_ty.is_diag_item(cx, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym::finish_non_exhaustive - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) - { + } else if path.ident.name == sym::finish_non_exhaustive && recv_ty.is_diag_item(cx, sym::DebugStruct) { has_finish_non_exhaustive = true; } } @@ -138,7 +135,7 @@ fn as_field_call<'tcx>( ) -> Option { if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind && let recv_ty = typeck_results.expr_ty(recv).peel_refs() - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) + && recv_ty.is_diag_item(cx, sym::DebugStruct) && path.ident.name == sym::field && let ExprKind::Lit(lit) = &debug_field.kind && let LitKind::Str(sym, ..) = lit.node diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index fe2157ca533a6..a93665ef3e9d6 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 455d1426aa8cb..f129b06e4c8ac 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, -}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_lang_item}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; @@ -219,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { diag.span_help(span, "or consider marking this type as `Copy`"); } - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index e062e55dad894..0a6499e095832 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; @@ -108,9 +108,7 @@ impl ArithmeticSideEffects { rhs_ty: Ty<'tcx>, ) -> bool { let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_sat_or_wrap = |ty: Ty<'_>| { - is_type_diagnostic_item(cx, ty, sym::Saturating) || is_type_diagnostic_item(cx, ty, sym::Wrapping) - }; + let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping); // If the RHS is `NonZero`, then division or module by zero will never occur. if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem { diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index d897b0e8dd918..4a1da7e07a888 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -1,8 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -18,7 +18,11 @@ pub(crate) fn check<'tcx>( ) { if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) + && cx + .typeck_results() + .expr_ty(self_arg) + .peel_refs() + .is_diag_item(cx, sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 7b98afa9b40bd..1620312474e99 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>( if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() && let right_ty = cx.typeck_results().expr_ty(right) - && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) + && (right_ty.is_integral() || right_ty.is_diag_item(cx, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index ee1d59490ce9f..57127e9d22985 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { return; } let owner = cx.tcx.local_def_id_to_hir_id(def_id).expect_owner(); - if is_type_diagnostic_item(cx, return_ty(cx, owner), sym::Result) { + if return_ty(cx, owner).is_diag_item(cx, sym::Result) { lint_impl_body(cx, span, body); } } diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 9b9024c810575..e6e4920e7d2c0 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; @@ -47,8 +47,12 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { } // If the expression is of type `Option` - let is_ty_option = - |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option); + let is_ty_option = |expr: &Expr<'_>| { + cx.typeck_results() + .expr_ty(expr) + .peel_refs() + .is_diag_item(cx, sym::Option) + }; // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 4ce6827cac9bb..f561c5c4d3e91 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{path_to_local_id, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind && !local.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().pat_ty(local.pat) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let Res::Local(id) = path.res && !expr.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().expr_ty(left) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index da56a785007c4..68a34d459e0df 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,7 +34,10 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node && path.ident.name == sym::set_readonly - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) + && cx + .typeck_results() + .expr_ty(receiver) + .is_diag_item(cx, sym::FsPermissions) { span_lint_and_then( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 49c58a276cdd9..8b2ecece8373f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,10 +4,10 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, @@ -206,7 +206,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ IfBlockType::IfIs(caller, caller_ty, call_sym, if_then) => { // If the block could be identified as `if x.is_none()/is_err()`, // we then only need to check the if_then return to see if it is none/err. - is_type_diagnostic_item(cx, caller_ty, smbl) + caller_ty.is_diag_item(cx, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { sym::Option => call_sym == sym::is_none, @@ -215,7 +215,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ } }, IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { - is_type_diagnostic_item(cx, let_expr_ty, smbl) + let_expr_ty.is_diag_item(cx, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index ff6e6ef214b5b..688da33a17778 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -103,7 +103,7 @@ fn try_parse_op_call<'tcx>( let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { for sym in &[sym::HashSet, sym::BTreeSet] { - if is_type_diagnostic_item(cx, receiver_ty, *sym) { + if receiver_ty.is_diag_item(cx, *sym) { return Some((OpExpr { receiver, value, span }, *sym)); } } diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index cc497c97a4725..b4375d4f9b042 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{path_res, peel_blocks}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { if let ExprKind::MethodCall(method_name, callee, args, _span) = func_body.kind && method_name.ident.name == sym::map && let callee_type = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_type, sym::Option) + && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind && let Res::Local(_id) = path_res(cx, callee) && matches!(path_res(cx, callee), Res::Local(_id)) diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 33856c750d7e9..3fdc366d2a7c3 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -61,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); let mut app = Applicability::MachineApplicable; let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" } else if is_type_lang_item(cx, ty, LangItem::CStr) { "to_bytes" diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f5400286f8845..319051dce685d 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; use itertools::Itertools; @@ -110,8 +110,8 @@ fn generate_swap_warning<'tcx>( if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_diag_item(cx, sym::VecDeque) { let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index c25668817f8ba..dbd4ec77fd5f9 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -97,16 +96,16 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + if lhs_ty.is_diag_item(cx, sym::Instant) { // Instant::now() - instant if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && rhs_ty.is_diag_item(cx, sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); } // instant - duration - else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + else if rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -123,8 +122,8 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } - } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + } else if lhs_ty.is_diag_item(cx, sym::Duration) + && rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -171,7 +170,7 @@ fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { /// Returns true if the type is Duration or Instant fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) + ty.is_diag_item(cx, sym::Duration) || ty.is_diag_item(cx, sym::Instant) } fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { @@ -195,7 +194,7 @@ fn print_unchecked_duration_subtraction_sugg( let typeck = cx.typeck_results(); let left_ty = typeck.expr_ty(left_expr); - let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + let lint_msg = if left_ty.is_diag_item(cx, sym::Instant) { "unchecked subtraction of a 'Duration' from an 'Instant'" } else { "unchecked subtraction between 'Duration' values" diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 7b90f20b6ba3c..71c4172c98528 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if to_digits_call.ty_rel_def(cx).is_diag_item(cx, sym::char_to_digit) { + if to_digits_call.res(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 51116b5eba9e1..9532423b73edc 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_uninit_value_valid_for_ty; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -183,7 +184,10 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt } fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) + cx.typeck_results() + .expr_ty(self_expr) + .peel_refs() + .is_diag_item(cx, sym::Vec) && path.ident.name == sym::reserve } @@ -205,10 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt match expr.kind { ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); - if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name == sym::set_len - && !is_integer_literal(arg, 0) - { + if self_type.is_diag_item(cx, sym::Vec) && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 5224b62e9fc71..5fa16dc5f7a2a 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { // Don't lint `Peekable`s returned from a block if let Some(expr) = block.expr && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr)) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { return; } @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -212,7 +213,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { true } else { diff --git a/clippy_lints/src/unused_result_ok.rs b/clippy_lints/src/unused_result_ok.rs index f5ed10fb76094..fe323a0b7b0c9 100644 --- a/clippy_lints/src/unused_result_ok.rs +++ b/clippy_lints/src/unused_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for UnusedResultOk { if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result.ok(, _) && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && !stmt.span.in_external_macro(cx.sess().source_map()) { let ctxt = expr.span.ctxt(); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1b137017ecb46..2954bfea32a67 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -368,7 +369,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -393,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); if name == sym::try_from_fn - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) diff --git a/clippy_lints/src/volatile_composites.rs b/clippy_lints/src/volatile_composites.rs index c65b47c0853f4..6402c3ef72c41 100644 --- a/clippy_lints/src/volatile_composites.rs +++ b/clippy_lints/src/volatile_composites.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for VolatileComposites { // Raw pointers ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), // std::ptr::NonNull - ty::Adt(_, args) if is_type_diagnostic_item(cx, self_ty, sym::NonNull) => { + ty::Adt(_, args) if self_ty.is_diag_item(cx, sym::NonNull) => { report_volatile_safe(cx, expr, args.type_at(0)); }, _ => (), diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index f1572fd65bbff..bf133d26ed9d0 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::ty_from_hir_ty; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -48,7 +49,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && !in_trait_impl(cx, hir_ty.hir_id) // We don't care about infer vars && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index a934d2094e004..1f7ea06475d87 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,6 +1,6 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind && let child_ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, child_ty, sym::Child) + && child_ty.is_diag_item(cx, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 8877f1faf0ee8..ced0a4b067bb9 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -26,7 +26,7 @@ declare_tool_lint! { /// /// Use instead: /// ```rust,ignore - /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ty.is_diag_item(cx, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 9fe6d8c62d2a5..3383e66fe3688 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; +use crate::res::MaybeDef; use crate::{is_expn_of, sym}; use rustc_ast::ast; @@ -453,7 +453,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - if let ExprKind::Call(func, args) = expr.kind { match func.kind { ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + if cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::Vec) => { if name.ident.name == sym::new { return Some(VecInitKind::New); @@ -469,7 +469,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) => { return Some(VecInitKind::Default); }, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c48b5b7c171ff..e5beac035ca4c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -36,6 +36,7 @@ use std::{iter, mem}; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; +use crate::res::MaybeDef; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -381,26 +382,8 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// Checks if the type is a reference equals to a diagnostic item pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), - _ => false, - } -} - -/// Checks if the type is equal to a diagnostic item. To check if a type implements a -/// trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// --- -/// -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), + match *ty.kind() { + ty::Ref(_, ref_ty, _) => ref_ty.is_diag_item(cx, diag_item), _ => false, } } @@ -1298,11 +1281,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> /// Check if `ty` is an `Option` and return its argument type if it is. pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - match ty.kind() { - ty::Adt(adt, args) => cx - .tcx - .is_diagnostic_item(sym::Option, adt.did()) - .then(|| args.type_at(0)), + match *ty.kind() { + ty::Adt(adt, args) + if let [arg] = &**args + && let Some(arg) = arg.as_type() + && adt.is_diag_item(cx, sym::Option) => + { + Some(arg) + }, _ => None, } } @@ -1356,7 +1342,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) + ty.is_slice() || ty.is_array() || ty.is_diag_item(cx, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { From fe13e0675a3ab92395ef9ac82445f21d295b4815 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:57:32 -0400 Subject: [PATCH 1537/1900] Remove `is_type_ref_to_diagnostic_item` --- clippy_utils/src/ty/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index e5beac035ca4c..47cd5a0f69cd1 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -380,14 +380,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is a reference equals to a diagnostic item -pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match *ty.kind() { - ty::Ref(_, ref_ty, _) => ref_ty.is_diag_item(cx, diag_item), - _ => false, - } -} - /// Checks if the type is equal to a lang item. /// /// Returns `false` if the `LangItem` is not defined. From 083b1c1059553121a700969d4f55e5d0b21e8a0b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:58:13 -0400 Subject: [PATCH 1538/1900] Remove `is_type_lang_item` --- book/src/development/common_tools_writing_lints.md | 2 +- clippy_lints/src/collection_is_never_read.rs | 5 +++-- clippy_lints/src/drop_forget_ref.rs | 5 +++-- clippy_lints/src/format_args.rs | 5 +++-- clippy_lints/src/format_push_string.rs | 7 +++++-- clippy_lints/src/from_str_radix_10.rs | 4 ++-- clippy_lints/src/inherent_to_string.rs | 5 +++-- .../src/loops/char_indices_as_byte_indices.rs | 4 ++-- clippy_lints/src/loops/explicit_iter_loop.rs | 6 +++--- clippy_lints/src/manual_ignore_case_cmp.rs | 4 ++-- clippy_lints/src/manual_retain.rs | 5 +++-- clippy_lints/src/matches/match_str_case_mismatch.rs | 4 ++-- clippy_lints/src/methods/bytes_count_to_len.rs | 4 ++-- clippy_lints/src/methods/bytes_nth.rs | 4 ++-- .../case_sensitive_file_extension_comparisons.rs | 4 ++-- clippy_lints/src/methods/clear_with_drain.rs | 3 +-- clippy_lints/src/methods/drain_collect.rs | 6 +++--- clippy_lints/src/methods/expect_fun_call.rs | 3 +-- clippy_lints/src/methods/extend_with_drain.rs | 3 +-- clippy_lints/src/methods/format_collect.rs | 4 ++-- clippy_lints/src/methods/inefficient_to_string.rs | 5 +++-- clippy_lints/src/methods/manual_str_repeat.rs | 12 +++++++----- clippy_lints/src/methods/needless_as_bytes.rs | 4 ++-- clippy_lints/src/methods/no_effect_replace.rs | 4 ++-- clippy_lints/src/methods/repeat_once.rs | 4 ++-- clippy_lints/src/methods/search_is_some.rs | 4 ++-- clippy_lints/src/methods/sliced_string_as_bytes.rs | 4 ++-- clippy_lints/src/methods/string_extend_chars.rs | 6 +++--- clippy_lints/src/methods/unnecessary_join.rs | 6 +++--- clippy_lints/src/methods/unnecessary_to_owned.rs | 6 +++--- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/redundant_clone.rs | 5 +++-- clippy_lints/src/redundant_slicing.rs | 8 ++++++-- clippy_lints/src/strings.rs | 9 ++++++--- clippy_lints/src/strlen_on_c_strings.rs | 3 +-- clippy_lints/src/unnecessary_owned_empty_strings.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 12 +----------- 37 files changed, 96 insertions(+), 91 deletions(-) diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 74fc788e9e3ae..7fde4cb408a5e 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -161,7 +161,7 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 1279be34ed8f2..e020f9e2ec5ba 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; @@ -71,7 +72,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { | sym::Vec | sym::VecDeque ) - ) || is_type_lang_item(cx, ty, LangItem::String) + ) || ty.is_lang_item(cx, LangItem::String) } fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 5c360ce6a5f7e..3bb8c484ceec3 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; -use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{is_copy, is_must_use_ty}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, + sym::mem_drop if arg_ty.is_lang_item(cx, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.typing_env()) || is_must_use_func_call(cx, arg) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 35965f4977cfc..d7a9dd0d008a6 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,8 +9,9 @@ use clippy_utils::macros::{ root_macro_call_first_node, }; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ @@ -344,7 +345,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { if let Some(placeholder_span) = placeholder.span && *options != FormatOptions::default() && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs() - && is_type_lang_item(self.cx, ty, LangItem::FormatArguments) + && ty.is_lang_item(self.cx, LangItem::FormatArguments) { span_lint_and_then( self.cx, diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index b64d608c0c709..a23ba9ab837ae 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -41,7 +41,10 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let e = e.peel_blocks().peel_borrows(); diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index d5873b3f85aa1..df8a35d9658b0 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() + ty.is_lang_item(cx, LangItem::String) || ty.peel_refs().is_str() } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 7f2e25367a6a4..e569a5c7b6126 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{return_ty, trait_ref_of_method}; use rustc_abi::ExternAbi; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; @@ -104,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) && !impl_item.span.from_expansion() // Check if return type is String - && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) + && return_ty(cx, impl_item.owner_id).is_lang_item(cx, LangItem::String) // Filters instances of to_string which are required by a trait && trait_ref_of_method(cx, impl_item.owner_id).is_none() { diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index a702e60f1c276..f2c87a2863c5f 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; use rustc_errors::{Applicability, MultiSpan}; @@ -81,7 +81,7 @@ fn check_index_usage<'tcx>( return; }; - let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let is_string_like = |ty: Ty<'_>| ty.is_str() || ty.is_lang_item(cx, LangItem::String); let message = match parent_expr.kind { ExprKind::MethodCall(segment, recv, ..) // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index af475c40586fe..40d1d36bd162c 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,10 +1,11 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, + implements_trait, implements_trait_with_env, is_copy, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, }; use rustc_errors::Applicability; @@ -127,8 +128,7 @@ fn is_ref_iterable<'tcx>( let self_ty = typeck.expr_ty(self_arg); let self_is_copy = is_copy(cx, self_ty); - if is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) - && !msrv.meets(cx, msrvs::BOX_INTO_ITER) + if self_ty.peel_refs().is_lang_item(cx, rustc_hir::LangItem::OwnedBox) && !msrv.meets(cx, msrvs::BOX_INTO_ITER) { return None; } diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index ae17b4e446e4c..18beff5b87091 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::ty::get_type_diagnostic_name; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -74,7 +74,7 @@ fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) || ty.is_diag_item(cx, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::String) + || ty.is_lang_item(cx, LangItem::String) } impl LateLintPass<'_> for ManualIgnoreCaseCmp { diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 7fb88763e640f..672a4c2676269 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -189,7 +190,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_lang_item(cx, ty, hir::LangItem::String) + && ty.is_lang_item(cx, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index eb8b16e1561bd..3f8f2dc0e132d 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -58,7 +58,7 @@ impl MatchExprVisitor<'_, '_> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { + if ty.is_lang_item(self.cx, LangItem::String) || ty.kind() == &ty::Str { return ControlFlow::Break(case_method); } } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index b8cc5ddd845c9..baea49296cd7a 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( && let Some(impl_id) = cx.tcx.impl_of_assoc(bytes_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, hir::LangItem::String)) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 02fc09170e59c..40d521d61c118 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { "String" } else { return; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 6f9702f6c6c33..b4b10e972f6d4 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) && !ext_str.chars().skip(1).all(|c| c.is_ascii_digit()) && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() - && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + && (recv_ty.is_str() || recv_ty.is_lang_item(cx, LangItem::String)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 77b1ec3f97883..67def8767bb0a 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -32,7 +31,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_s let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check - || is_type_lang_item(cx, expr_ty, LangItem::String) + || expr_ty.is_lang_item(cx, LangItem::String) } fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index cbf713a3b17c4..9b0c290577405 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -1,7 +1,7 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; @@ -35,8 +35,8 @@ fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_> /// Checks `std::string::String` fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - is_type_lang_item(cx, expr, LangItem::String) - && is_type_lang_item(cx, recv, LangItem::String) + expr.is_lang_item(cx, LangItem::String) + && recv.is_lang_item(cx, LangItem::String) && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index ea49fd414457d..288f966991acb 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; @@ -84,7 +83,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String) + base_type.is_str() || base_type.is_lang_item(cx, hir::LangItem::String) } { receiver } else { diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index 381e86173f85c..829c1226a859e 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -22,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() - && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + && src_ty_range.is_lang_item(cx, LangItem::RangeFull) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/format_collect.rs b/clippy_lints/src/methods/format_collect.rs index 1b28596d50da6..7b2fe00d32188 100644 --- a/clippy_lints/src/methods/format_collect.rs +++ b/clippy_lints/src/methods/format_collect.rs @@ -1,7 +1,7 @@ use super::FORMAT_COLLECT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_format_macro, root_macro_call_first_node}; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -17,7 +17,7 @@ fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx> } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { - if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let ExprKind::Closure(closure) = map_arg.kind && let body = cx.tcx.hir_body(closure.body) && let Some(value) = peel_non_expn_blocks(body.value) diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 1b350b7abb619..7a49a5f31f035 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -51,7 +52,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_lang_item(cx, ty, hir::LangItem::String) { + if ty.is_lang_item(cx, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a7052040c7c05..4fe14f8053c94 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -35,14 +34,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_lang_item(cx, ty, LangItem::String) - || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) + if ty.is_lang_item(cx, LangItem::String) + || (ty.is_lang_item(cx, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) + (ty.is_str() || ty.is_lang_item(cx, LangItem::String)).then_some(RepeatKind::String) } } } @@ -56,7 +55,10 @@ pub(super) fn check( ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) - && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && cx + .typeck_results() + .expr_ty(collect_expr) + .is_lang_item(cx, LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && cx.tcx.trait_of_assoc(take_id) == Some(iter_trait_id) diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs index 635d06330e058..22baad40b4496 100644 --- a/clippy_lints/src/methods/needless_as_bytes.rs +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::NEEDLESS_AS_BYTES; pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); - if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { + if ty1.is_lang_item(cx, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 32f32f1b21673..9fa51f78c99d7 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { + if !(ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 9111604ef53b7..57a7254605f93 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 1478bc29aef51..95f7a3ebbd300 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; @@ -109,7 +109,7 @@ pub(super) fn check<'tcx>( else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + if self_ty.is_lang_item(cx, hir::LangItem::String) { true } else { self_ty.is_str() diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index 6d4cfdb34f319..4aff194923a6c 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; use rustc_lint::LateContext; @@ -11,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { let mut applicability = Applicability::MaybeIncorrect; let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index f11a41f90f1ab..1fc0633f4f959 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,7 +10,7 @@ use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { + if !obj_ty.is_lang_item(cx, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } else { "" } - } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + } else if self_ty.is_lang_item(cx, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index efd1a718504ce..3290bdd778243 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); if let ty::Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::> - && let ty::Slice(slice) = ref_type.kind() - && is_type_lang_item(cx, *slice, LangItem::String) + && let ty::Slice(slice) = *ref_type.kind() + && slice.is_lang_item(cx, LangItem::String) // the argument for join is "" && let ExprKind::Lit(spanned) = &join_arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index e57cfae88f1f3..768b286ea1326 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, @@ -319,7 +319,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to // add `.as_ref()` to the suggestion. - let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + let as_ref = if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, sym::Target) != Some(cx.tcx.types.str_) @@ -672,7 +672,7 @@ fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { - original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String) + original_arg_ty.is_str() && arg_ty.is_lang_item(cx, LangItem::String) } fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index f129b06e4c8ac..6c578d39bacbd 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_lang_item}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; @@ -261,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) + if ty.is_lang_item(cx, LangItem::String) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index de6766cbe94aa..13c1b10bf2e8c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || fn_name == Some(sym::to_owned_method) - || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); + || (fn_name == Some(sym::to_string_method) && arg_ty.is_lang_item(cx, LangItem::String)); let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index a358eff2ce554..f2cf809d6012a 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -80,7 +81,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind && addressee.span.ctxt() == ctxt && let ExprKind::Index(indexed, range, _) = addressee.kind - && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) + && cx + .typeck_results() + .expr_ty_adjusted(range) + .is_lang_item(cx, LangItem::RangeFull) { let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 57d5900b045ea..c70227cefbc73 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, peel_blocks, sym, @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); - if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { + if e_ty.is_str() || e_ty.is_lang_item(cx, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -203,7 +203,10 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 3fdc366d2a7c3..58d692db5029d 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -64,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { + } else if ty.is_lang_item(cx, LangItem::CStr) { "to_bytes" } else { return; diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28f4884fa3110..0388450c9f7e2 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let LitKind::Str(symbol, _) = spanned.node && symbol.is_empty() && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) - && is_type_lang_item(cx, inner_expr_type, LangItem::String) + && inner_expr_type.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 47cd5a0f69cd1..fb07f6c240381 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -380,16 +380,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is equal to a lang item. -/// -/// Returns `false` if the `LangItem` is not defined. -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), - _ => false, - } -} - /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) @@ -408,7 +398,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { false } // Check for std types which implement drop, but only for memory allocation. - else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( get_type_diagnostic_name(cx, ty), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) From e1a4c90f61d830344b45d31f63ad544d6ea1d51a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:09:11 -0400 Subject: [PATCH 1539/1900] Remove `get_type_diagnostic_name` --- clippy_lints/src/collection_is_never_read.rs | 3 +-- clippy_lints/src/doc/missing_headers.rs | 7 ++----- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/infinite_iter.rs | 5 +++-- clippy_lints/src/manual_ignore_case_cmp.rs | 3 +-- clippy_lints/src/manual_retain.rs | 5 ++--- clippy_lints/src/matches/manual_unwrap_or.rs | 5 +++-- clippy_lints/src/methods/iter_nth.rs | 4 ++-- clippy_lints/src/methods/needless_collect.rs | 7 +++---- clippy_lints/src/methods/return_and_then.rs | 4 ++-- clippy_lints/src/methods/unnecessary_map_or.rs | 5 +++-- .../src/unnecessary_map_on_constructor.rs | 4 ++-- clippy_lints/src/unwrap.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 18 ++---------------- 15 files changed, 32 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index e020f9e2ec5ba..95f3589c4477f 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; @@ -60,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BTreeMap | sym::BTreeSet diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 7c325132f8792..b164a9a99782b 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -2,7 +2,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env}; +use clippy_utils::ty::implements_trait_with_env; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -120,10 +120,7 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() - && matches!( - get_type_diagnostic_name(cx, receiver_ty), - Some(sym::Option | sym::Result) - ) + && matches!(receiver_ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 7c083eab88942..42b44778d58f5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, @@ -144,7 +144,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { let callee_ty_raw = typeck.expr_ty(callee); let callee_ty = callee_ty_raw.peel_refs(); - if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + if matches!(callee_ty.opt_diag_name(cx), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { return; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bf3eafe09b3d0..f193f065e68d5 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -235,7 +236,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BinaryHeap | sym::BTreeMap diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index 18beff5b87091..25057b4aeaa27 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -59,7 +58,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op if needs_ref_to_cmp(cx, ty) || ty.is_str() || ty.is_slice() - || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString)) + || matches!(ty.opt_diag_name(cx), Some(sym::OsStr | sym::OsString)) { return Some((expr.span, ToAscii(is_lower, ty_raw))); } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 672a4c2676269..674f0da818f5f 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -251,7 +250,7 @@ fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - let required = match get_type_diagnostic_name(cx, ty) { + let required = match ty.opt_diag_name(cx) { Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN, Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN, @@ -265,7 +264,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap)) + matches!(ty.opt_diag_name(cx), Some(sym::BTreeMap | sym::HashMap)) } fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index ac9e51890362b..db19090356e04 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -174,7 +175,7 @@ fn handle( } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - match get_type_diagnostic_name(cx, ty)? { + match ty.opt_diag_name(cx)? { sym::Option => Some("Option"), sym::Result => Some("Result"), _ => None, diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 1fdbd81bf240e..6d1ee32e50269 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( iter_span: Span, nth_span: Span, ) -> bool { - let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + let caller_type = match cx.typeck_results().expr_ty(iter_recv).peel_refs().opt_diag_name(cx) { Some(sym::Vec) => "`Vec`", Some(sym::VecDeque) => "`VecDeque`", _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 0075bf166cc18..34bae0132c97d 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,11 +2,10 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{ - get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, -}; +use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, sym, @@ -98,7 +97,7 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList) ) && let iter_ty = cx.typeck_results().expr_ty(iter_expr) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index 54f38a322b8d9..8f47306c89479 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; use clippy_utils::{peel_blocks, potential_return_of_enclosing_body}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( } let recv_type = cx.typeck_results().expr_ty(recv); - if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + if !matches!(recv_type.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return; } diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 1f5e3de6e7a2d..bee8b6328a541 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::{Sugg, make_binop}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; @@ -54,7 +55,7 @@ pub(super) fn check<'a>( return; }; - let variant = match get_type_diagnostic_name(cx, recv_ty) { + let variant = match recv_ty.opt_diag_name(cx) { Some(sym::Option) => Variant::Some, Some(sym::Result) => Variant::Ok, Some(_) | None => return, diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs index d3700d05b014b..94b1a34455ff0 100644 --- a/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { return; } if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind - && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)) + && let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index aee8028a75de7..2f31aa55af6f8 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::res::MaybeDef; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; @@ -147,7 +147,7 @@ fn collect_unwrap_info<'tcx>( is_entire_condition: bool, ) -> Vec> { fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { - match (get_type_diagnostic_name(cx, ty)?, method_name) { + match (ty.opt_diag_name(cx)?, method_name) { (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 2954bfea32a67..a99b5a9576a31 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, same_type_modulo_regions}; +use clippy_utils::ty::{is_copy, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -442,7 +442,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { if is_inherent_method_call(cx, expr) { matches!( - get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index fb07f6c240381..03f9f42c09687 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -158,20 +158,6 @@ pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Optio .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item)) } -/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type -/// implements a trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), - _ => None, - } -} - /// Returns true if `ty` is a type on which calling `Clone` through a function instead of /// as a method, such as `Arc::clone()` is considered idiomatic. /// @@ -179,7 +165,7 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option, ty: Ty<'_>) -> bool { matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak) ) } @@ -400,7 +386,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Check for std types which implement drop, but only for memory allocation. else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) ) { From 53783de8f04132a3803f166aac02af55ad3254c7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:30:02 -0400 Subject: [PATCH 1540/1900] Remove `MaybePath` --- clippy_lints/src/manual_clamp.rs | 6 +- clippy_lints/src/manual_let_else.rs | 60 +++++++------------ .../src/methods/unnecessary_literal_unwrap.rs | 9 ++- clippy_lints/src/utils/author.rs | 17 +++--- clippy_utils/src/lib.rs | 47 ++------------- clippy_utils/src/paths.rs | 5 +- 6 files changed, 48 insertions(+), 96 deletions(-) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 42fe386d2c3c4..3d04c00051342 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -7,8 +7,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, sym, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, sym, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -516,7 +516,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> }, span: first_expr.span.to(second_expr.span), make_assignment: Some(maybe_input_first_path), - hir_with_ignore_attr: Some(first_expr.hir_id()), + hir_with_ignore_attr: Some(first_expr.hir_id), }) } else { None diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 3ed24b5dd2c55..298bf10754897 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,16 +2,14 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{ - MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, -}; +use clippy_utils::{is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -131,39 +129,25 @@ fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> b /// Returns `true` if the given pattern is a variant of an enum. pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { - struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); - - impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match self.0.kind { - PatKind::Struct(ref qpath, fields, _) - if fields - .iter() - .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => - { - Some(qpath) - }, - PatKind::TupleStruct(ref qpath, pats, _) - if pats - .iter() - .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => - { - Some(qpath) - }, - PatKind::Expr(&PatExpr { - kind: PatExprKind::Path(ref qpath), - .. - }) => Some(qpath), - _ => None, - } - } - - fn hir_id(&self) -> HirId { - self.0.hir_id - } - } - - let res = path_res(cx, &Pat(pat)); + let path = match pat.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::Expr(e) if let Some((qpath, id)) = e.opt_qpath() => (qpath, id), + _ => return false, + }; + let res = path.res(cx); matches!( res, Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index cc4448192d3e7..f85d47d4d1831 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -37,10 +38,12 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some(qpath) = call.qpath_opt() else { return }; + let Some((qpath, hir_id)) = call.opt_qpath() else { + return; + }; let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, call.hir_id()); + let res = cx.qpath_res(qpath, hir_id); if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 0c388fbd4812f..bfd115ec18d79 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,4 +1,5 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_attr, higher, path_def_id, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -268,16 +269,16 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { + fn qpath(&self, qpath: &Binding<&QPath<'_>>, hir_id_binding: &str, hir_id: HirId) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, hir_id).opt_def_id() && !def_id.is_local() { bind!(self, def_id); chain!( self, - "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + "let Some({def_id}) = cx.qpath_res({qpath}, {hir_id_binding}.hir_id).opt_def_id()" ); if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); @@ -291,7 +292,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + fn maybe_path<'p>(&self, path: &Binding>) { if let Some(id) = path_def_id(self.cx, path.value) && !id.is_local() { @@ -671,7 +672,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath, expr); + self.qpath(qpath, &expr.name, expr.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -757,7 +758,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ignore = etc.is_some(); bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -771,7 +772,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a976c9a7dafe1..59b6b3271c44f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,7 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; -use crate::res::{MaybeDef, MaybeResPath}; +use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -471,53 +471,16 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -pub trait MaybePath<'hir> { - fn hir_id(&self) -> HirId; - fn qpath_opt(&self) -> Option<&QPath<'hir>>; -} - -macro_rules! maybe_path { - ($ty:ident, $kind:ident) => { - impl<'hir> MaybePath<'hir> for hir::$ty<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - hir::$kind::Path(qpath) => Some(qpath), - _ => None, - } - } - } - }; -} -maybe_path!(Expr, ExprKind); -impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - .. - }) => Some(qpath), - _ => None, - } - } -} -maybe_path!(Ty, TyKind); - /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res { - match maybe_path.qpath_opt() { +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Res { + match maybe_path.opt_qpath() { None => Res::Err, - Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()), + Some((qpath, id)) => cx.qpath_res(qpath, id), } } /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option { +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { path_res(cx, maybe_path).opt_def_id() } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5ab8e16d88ed3..ff7c26d851bbe 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,8 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, path_def_id, sym}; +use crate::res::MaybeQPath; +use crate::{path_def_id, sym}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -96,7 +97,7 @@ impl PathLookup { } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it - pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) } From 5b659ba0b4d076255af21a0ec90d43ed2b91dd03 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:28:29 -0400 Subject: [PATCH 1541/1900] Remove `path_res` --- .../src/assertions_on_result_states.rs | 6 ++-- clippy_lints/src/derive/mod.rs | 4 +-- clippy_lints/src/error_impl_error.rs | 4 +-- clippy_lints/src/if_then_some_else_none.rs | 7 ++-- clippy_lints/src/loops/manual_find.rs | 10 +++--- clippy_lints/src/manual_clamp.rs | 5 +-- clippy_lints/src/matches/manual_filter.rs | 8 ++--- clippy_lints/src/matches/manual_map.rs | 5 +-- clippy_lints/src/matches/manual_ok_err.rs | 7 ++-- clippy_lints/src/matches/manual_unwrap_or.rs | 7 ++-- clippy_lints/src/matches/manual_utils.rs | 6 ++-- clippy_lints/src/matches/match_as_ref.rs | 5 +-- clippy_lints/src/matches/needless_match.rs | 6 ++-- clippy_lints/src/matches/try_err.rs | 5 +-- clippy_lints/src/mem_replace.rs | 9 +++--- .../iter_on_single_or_empty_collections.rs | 5 +-- clippy_lints/src/methods/manual_ok_or.rs | 8 ++--- .../methods/manual_saturating_arithmetic.rs | 5 +-- .../src/methods/needless_option_as_deref.rs | 6 ++-- .../src/methods/option_map_or_none.rs | 8 ++--- clippy_lints/src/methods/or_then_unwrap.rs | 6 ++-- .../src/methods/result_map_or_else_none.rs | 8 ++--- .../src/methods/unnecessary_filter_map.rs | 7 ++-- .../src/methods/unnecessary_literal_unwrap.rs | 4 +-- clippy_lints/src/needless_question_mark.rs | 4 +-- clippy_lints/src/non_canonical_impls.rs | 32 ++++++++----------- clippy_lints/src/partialeq_to_none.rs | 6 ++-- clippy_lints/src/question_mark.rs | 8 ++--- .../needless_return_with_question_mark.rs | 5 +-- clippy_lints/src/single_option_map.rs | 9 +++--- clippy_lints/src/unnecessary_literal_bound.rs | 4 +-- clippy_lints/src/unnecessary_wraps.rs | 5 +-- clippy_utils/src/lib.rs | 12 ++----- clippy_utils/src/ty/mod.rs | 5 ++- 34 files changed, 119 insertions(+), 122 deletions(-) diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 191fbf65e5a6c..cc62306b33b53 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Node}; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { if !is_copy(cx, result_type) { if result_type_with_refs != result_type { return; - } else if let Res::Local(binding_id) = path_res(cx, recv) + } else if let Res::Local(binding_id) = *recv.basic_res() && local_used_after_expr(cx, binding_id, recv) { return; diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 06efc2709faa6..eafe7c4bb9f23 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { self_ty, .. }) = item.kind - && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Res::Def(_, def_id) = *self_ty.basic_res() && let Some(local_def_id) = def_id.as_local() { let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 3018e1f127347..3d8650f05168d 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id()) && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id - && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) + && let Some(def_id) = imp.self_ty.basic_res().opt_def_id().and_then(DefId::as_local) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index f9fee292837ea..11338e1536242 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, sym, + peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -73,8 +74,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && is_res_lang_ctor(cx, then_call.res(cx), OptionSome) + && is_res_lang_ctor(cx, peel_blocks(els).res(cx), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index f99989ec6ba4b..5fb40413955cb 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,12 +1,12 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; +use clippy_utils::{higher, is_res_lang_ctor, peel_blocks_with_stmt}; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -34,8 +34,8 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) - && path_res(cx, inner_ret) == Res::Local(binding_id) + && is_res_lang_ctor(cx, ctor.res(cx), LangItem::OptionSome) + && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && is_res_lang_ctor(cx, last_ret.res(cx), LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 3d04c00051342..eaaab292f2f53 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,11 +3,12 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sym, }; use itertools::Itertools; @@ -342,7 +343,7 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) } }, ExprKind::Path(QPath::TypeRelative(ty, seg)) => { - matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + matches!(ty.basic_res(), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) }, _ => None, } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 96252a82fc0d9..138d733e46d22 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && is_res_lang_ctor(cx, callee.res(cx), OptionSome) && path_to_local_id(arg, target); } false @@ -74,7 +74,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + return is_res_lang_ctor(cx, inner_expr.res(cx), OptionNone); } false } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index de57d1eee9249..602a3ab1bb9d0 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -2,8 +2,9 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_res_lang_ctor, path_res}; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::res::MaybeQPath; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -91,7 +92,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, callee.res(cx), OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index a8490d6aa7d82..fa2ab773c5393 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -103,7 +104,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && is_res_lang_ctor(cx, body_callee.res(cx), OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -120,7 +121,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index db19090356e04..df886efc66840 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -12,7 +12,7 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{expr_type_is_certain, implements_trait}; -use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -115,7 +115,8 @@ fn handle( && is_default_equivalent(cx, peel_blocks(body_none)) { // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) + if condition + .res(cx) .opt_def_id() .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) { diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 5e989beb9fd1f..9477e98acde5c 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,12 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -273,5 +273,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 1cb4b512a30e5..3e236e82635f6 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -61,7 +62,7 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && is_res_lang_ctor(cx, e.res(cx), LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index e6f47707e3a29..a13f9f35eea3e 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,10 +1,10 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; @@ -106,7 +106,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if let_expr_ty.is_diag_item(cx, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + return is_res_lang_ctor(cx, else_expr.res(cx), OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index af90cb5e67334..4fd4851eab60a 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && is_res_lang_ctor(cx, err_fun.res(cx), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 95ecef5987007..7d042cd490fde 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,12 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{ - is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core, -}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -129,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + if is_res_lang_ctor(cx, src.res(cx), OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -163,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, path_res(cx, src_func), OptionSome) + && is_res_lang_ctor(cx, src_func.res(cx), OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 83e565562af08..728240c724b48 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,9 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -68,7 +69,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), + ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, f.res(cx), OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 610368aabc5b5..d84f188613fb3 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( .instantiate_identity() .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && is_res_lang_ctor(cx, err_path.res(cx), ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -51,7 +51,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + && is_res_lang_ctor(cx, callee.res(cx), ResultOk) { path_to_local_id(ok_arg, param_id) } else { diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index c785b23bc9c4a..2196ce92b0ab5 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_res, sym}; +use clippy_utils::sym; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -83,7 +84,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `T::MAX` and `T::MIN` constants if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind - && let Res::PrimTy(_) = path_res(cx, base) + && matches!(base.basic_res(), Res::PrimTy(_)) { match seg.ident.name { sym::MAX => return Some(MinMax::Max), diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index b4361448404a1..06e6a3c70b87d 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { - let Res::Local(binding_id) = path_res(cx, recv) else { + let Res::Local(binding_id) = *recv.basic_res() else { return; }; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 2b94ecd470646..e52a8a0266d50 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; +use clippy_utils::{is_res_lang_ctor, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -49,12 +49,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { + if !is_res_lang_ctor(cx, def_arg.res(cx), OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); + let f_arg_is_some = is_res_lang_ctor(cx, map_arg.res(cx), OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index f99e68fc419de..84b886d4fbe5a 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, path_res(cx, some_expr), item) + && is_res_lang_ctor(cx, some_expr.res(cx), item) { Some(arg.span.source_callsite()) } else { diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index 7aeda9493c2c0..de0bd78c9354e 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -21,11 +21,11 @@ pub(super) fn check<'tcx>( // lint if the caller of `map_or_else()` is a `Result` if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) + && is_res_lang_ctor(cx, map_arg.res(cx), OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(body.value)), OptionNone) + && is_res_lang_ctor(cx, peel_blocks(body.value).res(cx), OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index d260e0ef6e197..ac6a2e630cf31 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,9 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeQPath; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && is_res_lang_ctor(cx, expr.res(cx), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -95,7 +96,7 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { + if is_res_lang_ctor(cx, func.res(cx), OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index f85d47d4d1831..dc5ce293aa8f3 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::{is_res_lang_ctor, last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -54,7 +54,7 @@ pub(super) fn check( } else { return; } - } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { + } else if is_res_lang_ctor(cx, init.res(cx), hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 2a2160c3be2d1..986a827c59c55 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_res; +use clippy_utils::res::MaybeQPath; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -94,7 +94,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) + && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res(cx) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some" diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index e531f797272d2..0bb6a585789e2 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeQPath; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, -}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::def_id::LocalDefId; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef}; +use rustc_middle::ty::{EarlyBinder, TraitRef, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -261,7 +259,7 @@ fn expr_is_cmp<'tcx>( impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { - let impl_item_did = impl_item.owner_id.def_id; + let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); match expr.kind { ExprKind::Call( Expr { @@ -271,16 +269,15 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + is_res_lang_ctor(cx, typeck.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + // Fix #11178, allow `Self::cmp(self, ..)` + && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, ExprKind::MethodCall(_, recv, [], _) => { - cx.tcx - .typeck(impl_item_did) + typeck .type_dependent_def_id(expr.hir_id) .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, } @@ -289,12 +286,13 @@ fn expr_is_cmp<'tcx>( /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. fn self_cmp_call<'tcx>( cx: &LateContext<'tcx>, + typeck: &TypeckResults<'tcx>, cmp_expr: &'tcx Expr<'tcx>, - def_id: LocalDefId, needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path_res(cx, path) + ExprKind::Call(path, [_, _]) => path + .res(typeck) .opt_def_id() .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), ExprKind::MethodCall(_, recv, [_], ..) => { @@ -309,11 +307,7 @@ fn self_cmp_call<'tcx>( // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp` *needs_fully_qualified = true; - // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we - // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid - // an immediate ICE - cx.tcx - .typeck(def_id) + typeck .type_dependent_def_id(cmp_expr.hir_id) .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)) }, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index e6e4920e7d2c0..89cfd352bfe82 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_res_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) + && is_res_lang_ctor(cx, peel_hir_expr_refs(expr).0.res(cx), LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 8b2ecece8373f..05af8fe016d13 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,7 +11,7 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, + is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; @@ -394,7 +394,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) + && is_res_lang_ctor(cx, ok_ctor.res(cx), ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -405,10 +405,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone) + if is_res_lang_ctor(cx, arm.pat.res(cx), OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone) + && is_res_lang_ctor(cx, ret_expr.res(cx), OptionNone) && !ret_expr.span.from_expansion() { true diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs index c05038cd1e50e..f6bbb75f98a34 100644 --- a/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -19,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + && is_res_lang_ctor(cx, maybe_constr.res(cx), ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index b4375d4f9b042..4556d287711fe 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::res::MaybeDef; -use clippy_utils::{path_res, peel_blocks}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -57,8 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { && let callee_type = cx.typeck_results().expr_ty(callee) && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind - && let Res::Local(_id) = path_res(cx, callee) - && matches!(path_res(cx, callee), Res::Local(_id)) + && matches!(callee.basic_res(), Res::Local(_)) && !matches!(args[0].kind, ExprKind::Path(_)) { if let ExprKind::Closure(closure) = args[0].kind { @@ -71,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { } else if let ExprKind::MethodCall(_segment, receiver, method_args, _span) = value.kind && matches!(receiver.kind, ExprKind::Path(_)) && method_args.iter().all(|arg| matches!(arg.kind, ExprKind::Path(_))) - && method_args.iter().all(|arg| matches!(path_res(cx, arg), Res::Local(_))) + && method_args.iter().all(|arg| matches!(arg.basic_res(), Res::Local(_))) { return; } diff --git a/clippy_lints/src/unnecessary_literal_bound.rs b/clippy_lints/src/unnecessary_literal_bound.rs index 9f107fbeec03a..2603884440d16 100644 --- a/clippy_lints/src/unnecessary_literal_bound.rs +++ b/clippy_lints/src/unnecessary_literal_bound.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { return; }; - if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) { + if !matches!(inner_hir_ty.basic_res(), Res::PrimTy(PrimTy::Str)) { return; } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 849c0b438a5a0..5adc99acf16fa 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,9 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; +use clippy_utils::{contains_return, is_res_lang_ctor, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -122,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, func), lang_item) + && is_res_lang_ctor(cx, func.res(cx), lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 59b6b3271c44f..77367b7d2f6c6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -471,17 +471,11 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Res { - match maybe_path.opt_qpath() { - None => Res::Err, - Some((qpath, id)) => cx.qpath_res(qpath, id), - } -} - /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { - path_res(cx, maybe_path).opt_def_id() + maybe_path + .opt_qpath() + .and_then(|(qpath, id)| cx.qpath_res(qpath, id).opt_def_id()) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 03f9f42c09687..bb2ef9fa4692b 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -34,9 +34,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::{iter, mem}; -use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; -use crate::res::MaybeDef; +use crate::res::{MaybeDef, MaybeQPath}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -579,7 +578,7 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) { Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) From 3ed7aa0d5f121046bf907cac30453d3d9454f0d6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 07:38:25 -0400 Subject: [PATCH 1542/1900] Remove `path_def_id` --- clippy_lints/src/box_default.rs | 7 ++++--- clippy_lints/src/explicit_write.rs | 5 +++-- clippy_lints/src/from_over_into.rs | 9 +++++++-- clippy_lints/src/from_raw_with_void_ptr.rs | 5 +++-- clippy_lints/src/methods/chars_cmp.rs | 5 +++-- clippy_lints/src/methods/option_map_or_none.rs | 4 ++-- clippy_lints/src/non_std_lazy_statics.rs | 5 +++-- clippy_lints/src/only_used_in_recursion.rs | 5 +++-- clippy_lints/src/operators/cmp_owned.rs | 15 +++++++++------ clippy_lints/src/ptr.rs | 9 ++++++--- clippy_lints/src/regex.rs | 5 +++-- clippy_lints/src/replace_box.rs | 7 ++++--- clippy_lints/src/size_of_ref.rs | 5 ++--- clippy_lints/src/strings.rs | 7 +++---- clippy_lints/src/swap_ptr_to_ref.rs | 5 ++--- clippy_lints/src/types/box_collection.rs | 5 +++-- clippy_lints/src/types/option_option.rs | 5 +++-- clippy_lints/src/types/rc_buffer.rs | 11 +++++------ clippy_lints/src/types/rc_mutex.rs | 6 +++--- clippy_lints/src/types/redundant_allocation.rs | 7 +++++-- clippy_lints/src/unconditional_recursion.rs | 5 +++-- clippy_lints/src/utils/author.rs | 4 ++-- clippy_lints_internal/src/unnecessary_def_path.rs | 5 +++-- clippy_utils/src/lib.rs | 11 ++--------- clippy_utils/src/paths.rs | 7 +++++-- 25 files changed, 91 insertions(+), 73 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 3b861848f94a6..6f51f2343ab5b 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; use clippy_utils::macros::macro_backtrace; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::expr_sig; -use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; -use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind}; +use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::{Span, sym}; @@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault { // And that method is `new` && seg.ident.name == sym::new // And the call is that of a `Box` method - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) // And the single argument to the call is another function call // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 085ee4448a4ee..2a3b75ecae1b9 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id, sym}; +use clippy_utils::{is_expn_of, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -59,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym::write_fmt - && let Some(def_id) = path_def_id(cx, write_recv_path) + && let Some(def_id) = write_recv_path.basic_res().opt_def_id() { // match calls to std::io::stdout() / std::io::stderr () let (dest_name, prefix) = match cx.tcx.get_diagnostic_name(def_id) { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e3bb5ee10db7c..fd807290dc090 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; @@ -90,7 +90,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { + if target_ty + .peel_refs() + .basic_res() + .opt_def_id() + .is_none_or(|id| !id.is_local()) + { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index 5e2e2c9dbf725..0e6eeb75ec573 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeResPath; +use clippy_utils::sym; use clippy_utils::ty::is_c_void; -use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -41,7 +42,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind && seg.ident.name == sym::from_raw - && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let Some(type_str) = ty.basic_res().opt_def_id().and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind && is_c_void(cx, *ty) diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index de27a45ba4d92..4b0fc7eba8e34 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{method_chain_args, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -17,7 +18,7 @@ pub(super) fn check( ) -> bool { if let Some(args) = method_chain_args(info.chain, chain_methods) && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind - && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = fun.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index e52a8a0266d50..6e59fc7662f29 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( && let arg_snippet = snippet(cx, fn_decl_span, "..") && let body = cx.tcx.hir_body(body) && let Some((func, [arg_char])) = reduce_unit_expression(body.value) - && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = func.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 7ecde40aee017..c9c272abf9e3b 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -188,7 +189,7 @@ impl LazyInfo { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, _, ty, body_id) = item.kind - && let Some(path_def_id) = path_def_id(cx, ty) + && let Some(path_def_id) = ty.basic_res().opt_def_id() && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index b9cdae0f267ee..10cc4f48456fd 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{get_expr_use_or_unification_node, path_def_id, path_to_local, path_to_local_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_expr_use_or_unification_node, path_to_local, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -370,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Some((Node::Expr(parent), child_id)) => match parent.kind { // Recursive call. Track which index the parameter is used in. ExprKind::Call(callee, args) - if path_def_id(cx, callee).is_some_and(|id| { + if callee.res(cx).opt_def_id().is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 604f8f5da0b86..05358de5b3482 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; @@ -47,11 +47,14 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { - Some(sym::from_str_method) => true, - Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path + .res(cx) + .opt_def_id() + .is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 9eed46460a613..ec82817ef2bdb 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_to_local, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -742,8 +743,10 @@ fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { - path_def_id(cx, pathexp) - .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))) + matches!( + pathexp.basic_res().opt_diag_name(cx), + Some(sym::ptr_null | sym::ptr_null_mut) + ) } else { false } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 89d945161f629..d1fc228f4b352 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,9 +2,10 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths; use clippy_utils::paths::PathLookup; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(fun, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, fun) + && let Some(def_id) = fun.res(cx).opt_def_id() && let Some(regex_kind) = self.definitions.get(&def_id) { if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last() diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 9ad61f25dfcc4..b838613f89a0f 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; +use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -101,7 +102,7 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option< if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind && seg.ident.name == sym::new - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) { Some(arg) } else { diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 606e852aae9e3..bf304ebcfdc05 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,7 @@ declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); impl LateLintPass<'_> for SizeOfRef { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, path) - && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && path.basic_res().is_diag_item(cx, sym::mem_size_of_val) && let arg_ty = cx.typeck_results().expr_ty(arg) && peel_and_count_ty_refs(arg_ty).1 > 1 { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index c70227cefbc73..47306949a6990 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{ - SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, sym, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -256,7 +255,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::Call(fun, [bytes_arg]) = e.kind // Find `std::str::converts::from_utf8` or `std::primitive::str::from_utf8` && let Some(sym::str_from_utf8 | sym::str_inherent_from_utf8) = - path_def_id(cx, fun).and_then(|id| cx.tcx.get_diagnostic_name(id)) + fun.res(cx).opt_diag_name(cx) // Find string::as_bytes && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index ff196355a2e37..339c97d575aee 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; @@ -41,8 +41,7 @@ declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); impl LateLintPass<'_> for SwapPtrToRef { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind - && let Some(fn_id) = path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::mem_swap, fn_id) + && fn_expr.basic_res().is_diag_item(cx, sym::mem_swap) && let ctxt = e.span.ctxt() && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 24fe4e08a5b44..985057cd6734b 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -33,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, param)?; + let id = param.basic_res().opt_def_id()?; cx.tcx .get_diagnostic_name(id) .filter(|&name| { diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index d12d14f2b1413..10df007f2a13d 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Option, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && path_def_id(cx, arg) == Some(def_id) + && arg.basic_res().opt_def_id() == Some(def_id) { span_lint( cx, diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index c4fd0fbf87a90..46d9febb187fc 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -28,8 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -70,8 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -106,7 +105,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, ty)?; + let id = ty.basic_res().opt_def_id()?; let path = match cx.tcx.get_diagnostic_name(id) { Some(sym::OsString) => "std::ffi::OsStr", Some(sym::PathBuf) => "std::path::Path", diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index 7b13debc01ef8..e3d536822d5f8 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,8 +11,7 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && let Some(id) = path_def_id(cx, arg) - && cx.tcx.is_diagnostic_item(sym::Mutex, id) + && arg.basic_res().is_diag_item(cx, sym::Mutex) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc>`", |diag| { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 0ba51daf027d1..dbae2cc335165 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -40,7 +41,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; + let Some(id) = ty.basic_res().opt_def_id() else { + return false; + }; let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) { Some(sym::Arc) => ("Arc", ty), Some(sym::Rc) => ("Rc", ty), diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index e843e169113e7..e6663182fec53 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -317,7 +318,7 @@ where if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) - && let Some(method_def_id) = path_def_id(self.cx, f) + && let Some(method_def_id) = f.res(self.cx).opt_def_id() && let Some(trait_def_id) = self.cx.tcx.trait_of_assoc(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index bfd115ec18d79..68e51dace2db2 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,5 +1,5 @@ use clippy_utils::res::MaybeQPath; -use clippy_utils::{get_attr, higher, path_def_id, sym}; +use clippy_utils::{get_attr, higher, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -293,7 +293,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path_def_id(self.cx, path.value) + if let Some(id) = path.value.res(self.cx).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index ced0a4b067bb9..f2e8d3579d851 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,7 +1,8 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::{PathNS, lookup_path}; -use clippy_utils::{path_def_id, peel_ref_operators}; +use clippy_utils::peel_ref_operators; +use clippy_utils::res::MaybeQPath; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -53,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { let path: Vec = segments .iter() .map(|segment| { - if let Some(const_def_id) = path_def_id(cx, segment) + if let Some(const_def_id) = segment.res(cx).opt_def_id() && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) && let Some(value) = value.to_u32().discard_err() { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 77367b7d2f6c6..db4edb71d065a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,7 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; -use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::res::{MaybeDef, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -471,13 +471,6 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { - maybe_path - .opt_qpath() - .and_then(|(qpath, id)| cx.qpath_res(qpath, id).opt_def_id()) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -1994,7 +1987,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)), - _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), + _ => expr.basic_res().is_diag_item(cx, sym::convert_identity), } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ff7c26d851bbe..b6832b384d4a8 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -5,7 +5,7 @@ //! See for more information. use crate::res::MaybeQPath; -use crate::{path_def_id, sym}; +use crate::sym; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -98,7 +98,10 @@ impl PathLookup { /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { - path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + maybe_path + .res(cx) + .opt_def_id() + .is_some_and(|def_id| self.matches(cx, def_id)) } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` From 3f686a074d6dd94f954b1a992f535b5dff5678e2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 08:17:00 -0400 Subject: [PATCH 1543/1900] Remove `is_res_lang_ctor` --- clippy_lints/src/if_then_some_else_none.rs | 9 ++-- clippy_lints/src/loops/manual_find.rs | 8 ++-- .../src/loops/while_let_on_iterator.rs | 5 ++- clippy_lints/src/match_result_ok.rs | 4 +- clippy_lints/src/matches/collapsible_match.rs | 9 ++-- clippy_lints/src/matches/manual_filter.rs | 6 +-- clippy_lints/src/matches/manual_map.rs | 6 +-- clippy_lints/src/matches/manual_ok_err.rs | 13 +++--- clippy_lints/src/matches/manual_utils.rs | 20 ++++++--- clippy_lints/src/matches/match_as_ref.rs | 11 +++-- clippy_lints/src/matches/needless_match.rs | 5 +-- clippy_lints/src/matches/try_err.rs | 6 +-- clippy_lints/src/mem_replace.rs | 8 ++-- .../iter_on_single_or_empty_collections.rs | 15 +++++-- clippy_lints/src/methods/manual_ok_or.rs | 15 +++++-- .../src/methods/option_map_or_none.rs | 5 +-- clippy_lints/src/methods/or_then_unwrap.rs | 3 +- .../src/methods/result_map_or_else_none.rs | 6 +-- .../src/methods/unnecessary_filter_map.rs | 15 ++++--- .../src/methods/unnecessary_literal_unwrap.rs | 12 ++--- clippy_lints/src/non_canonical_impls.rs | 6 +-- clippy_lints/src/option_if_let_else.rs | 16 ++++--- clippy_lints/src/partialeq_to_none.rs | 8 +++- clippy_lints/src/question_mark.rs | 26 ++++++----- .../needless_return_with_question_mark.rs | 6 +-- clippy_lints/src/unnecessary_wraps.rs | 6 +-- clippy_lints/src/unused_io_amount.rs | 10 +++-- clippy_utils/src/lib.rs | 45 ++++++++++--------- 28 files changed, 181 insertions(+), 123 deletions(-) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 11338e1536242..dfc8411baa005 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,12 +2,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - peel_blocks, sym, + contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -74,8 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, then_call.res(cx), OptionSome) - && is_res_lang_ctor(cx, peel_blocks(els).res(cx), OptionNone) + && then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 5fb40413955cb..c38cf83f44100 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,11 +1,11 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::{MaybeQPath, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, peel_blocks_with_stmt}; +use clippy_utils::{higher, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, ctor.res(cx), LangItem::OptionSome) + && ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, last_ret.res(cx), LangItem::OptionNone) + && last_ret.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 6000ff7a36099..df8b4d1551d42 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,9 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -19,7 +20,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index bc9279677b2eb..fb83f7cf65ddb 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::{higher, is_res_lang_ctor, sym}; +use clippy_utils::{higher, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index c40f7620d945b..347d4f9fa4b5b 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, - peel_ref_operators, + SpanlessEq, get_ref_operators, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, }; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; @@ -136,7 +136,10 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), _ => false, } } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 138d733e46d22..227db74b9d78a 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, callee.res(cx), OptionSome) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && path_to_local_id(arg, target); } false @@ -74,7 +74,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, inner_expr.res(cx), OptionNone); + return inner_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone); } false } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 602a3ab1bb9d0..f111da60bbd5b 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -2,9 +2,7 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; - -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -92,7 +90,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, callee.res(cx), OptionSome) => + if ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index fa2ab773c5393..c9293412fba82 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -73,7 +73,10 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool true }, PatKind::TupleStruct(qpath, ..) => { - is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + cx.qpath_res(&qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) + == must_match_err }, PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) @@ -104,7 +107,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, body_callee.res(cx), OptionSome) + && body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -121,7 +124,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 9477e98acde5c..60925db893dae 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -5,8 +5,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -259,9 +259,19 @@ pub(super) fn try_parse_pattern<'tcx>( kind: PatExprKind::Path(qpath), hir_id, .. - }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), + }) if cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + if cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -273,5 +283,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 3e236e82635f6..156118be347f9 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, peel_blocks}; +use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -59,10 +59,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && cx + .qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, e.res(cx), LangItem::OptionSome) + && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index a13f9f35eea3e..c9b6821ad98fd 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -4,8 +4,7 @@ use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, - peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -106,7 +105,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if let_expr_ty.is_diag_item(cx, sym::Option) { - return is_res_lang_ctor(cx, else_expr.res(cx), OptionNone) + return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 4fd4851eab60a..7358628f0f7e6 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; +use clippy_utils::get_parent_expr; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, err_fun.res(cx), ResultErr) + && err_fun.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 7d042cd490fde..ac3cbaec55f30 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,11 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, peel_ref_operators, std_or_core}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -128,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, src.res(cx), OptionNone) { + if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -162,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, src_func.res(cx), OptionSome) + && src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 728240c724b48..8183c30f8c56b 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,10 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -68,8 +68,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), - ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, f.res(cx), OptionSome) => Some(arg), + ExprKind::Path(ref p) + if cx + .qpath_res(p, recv.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + None + }, + ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index d84f188613fb3..80b955829f1fd 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( .instantiate_identity() .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, err_path.res(cx), ResultErr) + && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -46,12 +46,19 @@ pub(super) fn check<'tcx>( fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { match map_expr.kind { - ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Path(ref qpath) + if cx + .qpath_res(qpath, map_expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) => + { + true + }, ExprKind::Closure(closure) => { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, callee.res(cx), ResultOk) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { path_to_local_id(ok_arg, param_id) } else { diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 6e59fc7662f29..342ffea51d656 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use rustc_errors::Applicability; @@ -49,12 +48,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, def_arg.res(cx), OptionNone) { + if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, map_arg.res(cx), OptionSome); + let f_arg_is_some = map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 84b886d4fbe5a..07199b84f39ec 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; @@ -60,7 +59,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, some_expr.res(cx), item) + && some_expr.res(cx).ctor_parent(cx).is_lang_item(cx, item) { Some(arg.span.source_callsite()) } else { diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index de0bd78c9354e..e2946c22a46b5 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -21,11 +21,11 @@ pub(super) fn check<'tcx>( // lint if the caller of `map_or_else()` is a `Result` if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, map_arg.res(cx), OptionSome) + && map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, peel_blocks(body.value).res(cx), OptionNone) + && peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index ac6a2e630cf31..cffb01f1bbc19 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, expr.res(cx), OptionSome) + && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -96,7 +96,7 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, func.res(cx), OptionSome) { + if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } @@ -134,7 +134,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { (false, true) }, _ => (true, true), diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index dc5ce293aa8f3..410e973f855b6 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_res_lang_ctor, last_path_segment, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -45,16 +45,16 @@ pub(super) fn check( let args = last_path_segment(qpath).args.map(|args| args.args); let res = cx.qpath_res(qpath, hir_id); - if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } - } else if is_res_lang_ctor(cx, init.res(cx), hir::LangItem::OptionNone) { + } else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 0bb6a585789e2..0c5e4d279f5c7 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, std_or_core}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -269,7 +269,7 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, typeck.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + typeck.qpath_res(some_path, *some_hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 3483f3081a58c..c32c74a8fe608 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + is_in_const_context, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -314,9 +315,9 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { return Some((inner_pat, false)); - } else if is_res_lang_ctor(cx, res, ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { return Some((inner_pat, true)); } } @@ -379,9 +380,14 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + cx.qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 89cfd352bfe82..44217ac2c4fcb 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::{is_res_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::{peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,7 +57,11 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, peel_hir_expr_refs(expr).0.res(cx), LangItem::OptionNone) + && peel_hir_expr_refs(expr) + .0 + .res(cx) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 05af8fe016d13..d0cce2b6d8751 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,8 +11,8 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, + pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -220,15 +220,15 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_res_lang_ctor(cx, res, OptionSome) + res.ctor_parent(cx).is_lang_item(cx, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_res_lang_ctor(cx, res, ResultOk) + (res.ctor_parent(cx).is_lang_item(cx, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_res_lang_ctor(cx, res, ResultErr) + || res.ctor_parent(cx).is_lang_item(cx, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) && if_else.is_none() }, @@ -248,7 +248,10 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), + sym::Option => cx + .qpath_res(qpath, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), _ => false, }, @@ -343,7 +346,10 @@ fn extract_ctor_call<'a, 'tcx>( pat: &'a Pat<'tcx>, ) -> Option<&'a Pat<'tcx>> { if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor) + && cx + .qpath_res(variant_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, expected_ctor) { Some(val_binding) } else { @@ -394,7 +400,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, ok_ctor.res(cx), ResultErr) + && ok_ctor.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -405,10 +411,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, arm.pat.res(cx), OptionNone) + if arm.pat.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, ret_expr.res(cx), OptionNone) + && ret_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !ret_expr.span.from_expansion() { true diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs index f6bbb75f98a34..c47a3ef21e869 100644 --- a/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_from_proc_macro, is_inside_let_else}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -20,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, maybe_constr.res(cx), ResultErr) + && maybe_constr.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5adc99acf16fa..29747cf0e447a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,10 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, return_ty}; +use clippy_utils::{contains_return, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, func.res(cx), lang_item) + && func.res(cx).ctor_parent(cx).is_lang_item(cx, lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index af3ad4566c46a..cff798f7a89a0 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, paths, peel_blocks, sym}; +use clippy_utils::res::MaybeDef; +use clippy_utils::{paths, peel_blocks, sym}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +137,10 @@ fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { } if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind { - return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr); + return cx + .qpath_res(path, inner_pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, hir::LangItem::ResultErr); } false @@ -203,7 +207,7 @@ fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind // we check against Result::Ok to avoid linting on Err(_) or something else. - && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + && cx.qpath_res(path, pat.hir_id).ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { if matches!(inner_pat, []) { return true; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index db4edb71d065a..7a215d3916221 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -252,19 +252,6 @@ pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { } } -/// Checks if a `Res` refers to a constructor of a `LangItem` -/// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { - if let Res::Def(DefKind::Ctor(..), id) = res - && let Some(lang_id) = cx.tcx.lang_items().get(lang_item) - && let Some(id) = cx.tcx.opt_parent(id) - { - id == lang_id - } else { - false - } -} - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. pub fn is_enum_variant_ctor( cx: &LateContext<'_>, @@ -337,13 +324,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) + if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)) } /// Checks if `arm` has the form `None => None`. pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { is_none_pattern(cx, arm.pat) - && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) + && matches!( + peel_blocks(arm.body).kind, + ExprKind::Path(qpath) + if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone) + ) } /// Checks if the given `QPath` belongs to a type alias. @@ -708,7 +699,10 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)), ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), - ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), + ExprKind::Path(qpath) => cx + .qpath_res(qpath, e.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, @@ -1609,7 +1603,10 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind && ddpos.as_opt_usize().is_none() - && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && cx + .qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind && path_to_local_id(arm.body, hir_id) { @@ -1620,7 +1617,9 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) + cx.qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) } else { false } @@ -2854,12 +2853,18 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) + && cx + .qpath_res(&pat_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind && let ExprKind::Path(ret_path) = ret_val.kind - && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) + && cx + .qpath_res(&ret_path, ret_val.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) { Some(inner_pat) } else { From 53675ce0617f2dde5b28efeaf1bd2a8fe6417d21 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 16:41:20 -0400 Subject: [PATCH 1544/1900] Remove `path_to_local` --- clippy_lints/src/assigning_clones.rs | 5 +++-- clippy_lints/src/casts/unnecessary_cast.rs | 7 ++++--- clippy_lints/src/dereference.rs | 4 ++-- clippy_lints/src/eta_reduction.rs | 8 +++----- clippy_lints/src/format_impl.rs | 5 +++-- .../src/functions/not_unsafe_ptr_arg_deref.rs | 5 +++-- clippy_lints/src/ifs/branches_sharing_code.rs | 6 +++--- clippy_lints/src/ifs/ifs_same_cond.rs | 5 +++-- clippy_lints/src/index_refutable_slice.rs | 5 +++-- clippy_lints/src/loops/manual_memcpy.rs | 11 ++++++----- clippy_lints/src/loops/mut_range_bound.rs | 5 +++-- clippy_lints/src/loops/same_item_push.rs | 8 ++++---- clippy_lints/src/loops/utils.rs | 5 +++-- clippy_lints/src/manual_float_methods.rs | 7 ++++--- clippy_lints/src/manual_is_ascii_check.rs | 5 +++-- clippy_lints/src/manual_rem_euclid.rs | 5 +++-- clippy_lints/src/matches/collapsible_match.rs | 8 +++----- clippy_lints/src/matches/match_same_arms.rs | 7 ++++--- clippy_lints/src/matches/redundant_guards.rs | 5 +++-- clippy_lints/src/methods/is_empty.rs | 6 ++++-- clippy_lints/src/methods/iter_skip_next.rs | 5 +++-- clippy_lints/src/methods/needless_collect.rs | 10 +++++----- .../src/methods/string_lit_chars_any.rs | 7 ++++--- .../src/methods/unnecessary_iter_cloned.rs | 5 +++-- .../src/mixed_read_write_in_expression.rs | 5 +++-- clippy_lints/src/needless_late_init.rs | 4 ++-- clippy_lints/src/no_effect.rs | 7 +++---- clippy_lints/src/only_used_in_recursion.rs | 6 +++--- clippy_lints/src/ptr.rs | 4 ++-- clippy_lints/src/question_mark.rs | 10 +++++----- clippy_lints/src/ranges.rs | 10 ++++------ clippy_lints/src/replace_box.rs | 4 ++-- clippy_lints/src/significant_drop_tightening.rs | 7 ++++--- clippy_lints/src/slow_vector_initialization.rs | 6 ++---- clippy_lints/src/swap.rs | 7 ++++--- clippy_lints/src/tuple_array_conversions.rs | 5 +++-- .../src/unnecessary_struct_initialization.rs | 5 +++-- clippy_lints/src/unwrap.rs | 8 ++++---- clippy_lints/src/useless_conversion.rs | 8 +++----- clippy_utils/src/lib.rs | 17 ++++------------- 40 files changed, 133 insertions(+), 129 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 52287be34c784..6f5c352c061c8 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs)) + && lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs)) && let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id()) // Derived forms don't implement `clone_from`/`clone_into`. // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c88a0539d70e1..7bfe9201d812a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -167,11 +168,11 @@ pub(super) fn check<'tcx>( sym::assert_ne_macro, sym::debug_assert_ne_macro, ]; - matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) } - if let Some(id) = path_to_local(cast_expr) + if let Some(id) = cast_expr.res_local_id() && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9ebb8e6e15d9e..de1362081323a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, - path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if let Some(local) = path_to_local(expr) { + if let Some(local) = expr.res_local_id() { self.check_local_usage(cx, expr, local); } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 42b44778d58f5..13aefa83ae636 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{ - get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, -}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local_id}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -218,7 +216,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { + if callee.res_local_id().is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 416aea51ea195..4dde968659e03 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_as_impl, is_diag_trait_item, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -210,7 +211,7 @@ impl FormatImplExpr<'_, '_> { // Since the argument to fmt is itself a reference: &self let reference = peel_ref_operators(self.cx, arg); // Is the reference self? - if path_to_local(reference).map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { + if reference.res_local_id().map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { let FormatTraitNames { name, .. } = self.format_trait_impl; span_lint( self.cx, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 72f8795304055..c6b0e7c54c06f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,12 +1,13 @@ +use clippy_utils::res::MaybeResPath; use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; +use clippy_utils::iter_input_pats; use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -87,7 +88,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { } fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { - if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) { + if arg.res_local_id().is_some_and(|id| raw_ptrs.contains(&id)) { span_lint( cx, NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs index eb1025f71498e..b3f597cc8736e 100644 --- a/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, - path_to_local, }; use core::iter; use core::ops::ControlFlow; @@ -149,7 +149,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() { @@ -198,7 +198,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { + if let Some(id) = e.res_local_id() { cond_locals.insert(id); } ControlFlow::Continue(()) diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs index ca76fc2587db9..3ea941320a086 100644 --- a/clippy_lints/src/ifs/ifs_same_cond.rs +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::InteriorMut; -use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ fn method_caller_is_mutable<'tcx>( interior_mut.is_interior_mut_ty(cx, caller_ty) || caller_ty.is_mutable_ptr() // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) + || caller_expr.res_local_id() .and_then(|hid| find_binding_init(cx, hid)) .is_none() } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8f9f71a147694..919702c5714ad 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::is_lint_allowed; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::is_copy; -use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -225,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let Some(local_id) = path_to_local(expr) { + if let Some(local_id) = expr.res_local_id() { let Self { cx, ref mut slice_lint_info, diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index d9c4b526da99e..a2da43c2ca245 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,10 +1,11 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::usage::local_used_in; -use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; +use clippy_utils::{get_enclosing_block, higher, sugg}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_left) && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different - && path_to_local(base_left) != path_to_local(base_right) + && base_left.res_local_id() != base_right.res_local_id() { Some(( ty, @@ -128,7 +129,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { if let ExprKind::MethodCall(method, recv, [], _) = end.kind && method.ident.name == sym::len - && path_to_local(recv) == path_to_local(base) + && recv.res_local_id() == base.res_local_id() { if sugg.to_string() == end_str { sugg::EMPTY.into() @@ -364,7 +365,7 @@ fn get_details_from_idx<'tcx>( starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { - let id = path_to_local(e)?; + let id = e.res_local_id()?; starts.iter().find(|start| start.id == id).map(|start| start.kind) } @@ -425,7 +426,7 @@ fn get_assignments<'a, 'tcx>( .chain(*expr) .filter(move |e| { if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).is_some_and(|id| { + place.res_local_id().is_some_and(|id| { !loop_counters .iter() // skip the first item which should be `StartKind::Range` diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 70ca452013f91..daeda227f670c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,6 +1,7 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{get_enclosing_block, higher, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_enclosing_block, higher}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -39,7 +40,7 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - if let Some(hir_id) = path_to_local(bound) + if let Some(hir_id) = bound.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && let PatKind::Binding(BindingMode::MUT, ..) = pat.kind { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 097b84de49255..4135c63d4d7ff 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,10 +1,10 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; +use clippy_utils::{msrvs, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -126,7 +126,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { if !self.non_deterministic_expr && !self.multiple_pushes && let Some((vec, _, _)) = self.vec_push - && let Some(hir_id) = path_to_local(vec) + && let Some(hir_id) = vec.res_local_id() { !self.used_locals.contains(&hir_id) } else { @@ -142,7 +142,7 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), _ => { - if let Some(hir_id) = path_to_local(expr) { + if let Some(hir_id) = expr.res_local_id() { self.used_locals.insert(hir_id); } walk_expr(self, expr); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 2f6950b4380c3..842f542d22e7d 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,5 +1,6 @@ +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::{has_iter_method, implements_trait}; -use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; +use clippy_utils::{get_parent_expr, is_integer_const, path_to_local_id, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -47,7 +48,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); if *state == IncrementVisitorVarState::IncrOnce { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 60782f445ab91..a81c4dc6a7931 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // Checking all possible scenarios using a function would be a hopeless task, as we have // 16 possible alignments of constants/operands. For now, let's use `partition`. && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] - && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2 + && exprs.iter_mut().partition_in_place(|i| i.res_local_id().is_some()) == 2 && !expr.span.in_external_macro(cx.sess().source_map()) && ( is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into()) @@ -149,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && let ctxt = expr.span.ctxt() && let Some(const_1) = ecx.eval_local(const_1, ctxt) && let Some(const_2) = ecx.eval_local(const_2, ctxt) - && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) + && first.res_local_id().is_some_and(|f| second.res_local_id().is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 2eebb2430fd94..8c6abbeac4489 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; +use clippy_utils::{higher, is_in_const_context, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { } fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { - let local_hid = path_to_local(arg)?; + let local_hid = arg.res_local_id()?; if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 1e91a429fe45c..d993ed48eac4a 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -64,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 - && let Some(hir_id) = path_to_local(rem2_lhs) + && let Some(hir_id) = rem2_lhs.res_local_id() && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 347d4f9fa4b5b..79f737f07eb1e 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - SpanlessEq, get_ref_operators, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, -}; +use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -67,7 +65,7 @@ fn check_arm<'tcx>( && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match { .. } - && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && let Some(binding_id) = peel_ref_operators(cx, inner_scrutinee).res_local_id() && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 818e504245549..be914429edb44 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -61,8 +62,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| { let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if let Some(a_id) = path_to_local(a) - && let Some(b_id) = path_to_local(b) + if let Some(a_id) = a.res_local_id() + && let Some(b_id) = b.res_local_id() && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 7c6d45e424006..d39e315cae1f2 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -164,7 +165,7 @@ fn get_pat_binding<'tcx>( guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, ) -> Option { - if let Some(local) = path_to_local(guard_expr) + if let Some(local) = guard_expr.res_local_id() && !is_local_used(cx, outer_arm.body, local) { let mut span = None; diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 545bef1a4c5bc..add01b6a08376 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; -use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -45,7 +46,8 @@ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization /// value depends on a `#[cfg(…)]` directive. fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) .filter(|init| !is_under_cfg(cx, init.hir_id)) diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index fedb7c22eded6..cf88cab4a77ef 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingMode, Node, PatKind}; @@ -19,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 34bae0132c97d..2997167f4a58a 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,13 +2,13 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, sym, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local_id, + sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -383,7 +383,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) + if let Some(hir_id) = recv.res_local_id() && let Some(index) = self.hir_id_uses_map.remove(&hir_id) { if self @@ -554,7 +554,7 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind && value.hir_id == self.hir_id_of_expr - && let Some(id) = path_to_local(place) + && let Some(id) = place.res_local_id() { // our iterator was directly assigned to a variable self.hir_id_of_let_binding = Some(id); diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index f0f9d30d3000a..660ecbc5e6ceb 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; +use clippy_utils::{is_from_proc_macro, is_trait_method}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -25,8 +26,8 @@ pub(super) fn check<'tcx>( && let LitKind::Str(val, _) = lit_kind.node && let ExprKind::Binary(kind, lhs, rhs) = body.kind && let BinOpKind::Eq = kind.node - && let Some(lhs_path) = path_to_local(lhs) - && let Some(rhs_path) = path_to_local(rhs) + && let Some(lhs_path) = lhs.res_local_id() + && let Some(rhs_path) = rhs.res_local_id() && let scrutinee = match (lhs_path == arg, rhs_path == arg) { (true, false) => rhs, (false, true) => lhs, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 20cf35363d13f..4142f9f75773e 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -1,10 +1,11 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; use core::ops::ControlFlow; use itertools::Itertools; use rustc_errors::Applicability; @@ -50,7 +51,7 @@ pub fn check_for_loop_iter( // check whether `expr` is mutable fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 3b44d4b60d320..6ed2628c37e20 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_expr, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind - && let Some(var) = path_to_local(lhs) + && let Some(var) = lhs.res_local_id() && expr.span.desugaring_kind().is_none() { var diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a914267cf5002..464a91959a8eb 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; @@ -116,7 +116,7 @@ impl LocalAssign { } Some(Self { - lhs_id: path_to_local(lhs)?, + lhs_id: lhs.res_local_id()?, rhs_span: rhs.span.source_callsite(), span, }) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d6666eed455c..701923cf6efc4 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; -use clippy_utils::{ - in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, -}; +use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ @@ -109,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { self.underscore_bindings.swap_remove(&def_id); } } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 10cc4f48456fd..63d9271ecf058 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{get_expr_use_or_unification_node, path_to_local, path_to_local_id}; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; +use clippy_utils::{get_expr_use_or_unification_node, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -359,7 +359,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && let Some(param) = self.params.get_by_id_mut(id) { let typeck = cx.typeck_results(); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index ec82817ef2bdb..71c1a40ca8132 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_to_local, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -562,7 +562,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ } // Check if this is local we care about - let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + let Some(&args_idx) = e.res_local_id().and_then(|id| self.bindings.get(&id)) else { return walk_expr(self, e); }; let args = &self.args[args_idx]; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d0cce2b6d8751..ce8db486de1af 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,15 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + pat_and_expr_can_be_question_mark, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -252,7 +252,7 @@ fn expr_return_none_or_err( .qpath_res(qpath, expr.hir_id) .ctor_parent(cx) .is_lang_item(cx, OptionNone), - sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), + sym::Result => expr.res_local_id().is_some() && expr.res_local_id() == cond_expr.res_local_id(), _ => false, }, ExprKind::Call(call_expr, [arg]) => { @@ -492,7 +492,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .is_none() { if !is_copy(cx, caller_ty) - && let Some(hir_id) = path_to_local(let_expr) + && let Some(hir_id) = let_expr.res_local_id() && local_used_after_expr(cx, hir_id, expr) { return; diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 945b67f821f6e..e4c91b7efd2bd 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,13 +2,11 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local, -}; +use clippy_utils::{expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const}; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -321,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option (true, Ordering::Less), _ => return None, }; - if let Some(id) = path_to_local(l) { + if let Some(id) = l.res_local_id() { if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, @@ -333,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option for ReplaceBox { && !rhs.span.from_expansion() && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals - && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) + && lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local)) && let Some(inner_ty) = lhs_ty.boxed_ty() { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 9110f684bd10c..dcce906499585 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; +use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -276,7 +277,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) && { - if let Some(local_hir_id) = path_to_local(expr) { + if let Some(local_hir_id) = expr.res_local_id() { local_hir_id == hir_id } else { true @@ -301,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { modify_apa_params(&mut apa); let _ = self.ap.apas.insert(hir_id, apa); } else { - let Some(hir_id) = path_to_local(expr) else { + let Some(hir_id) = expr.res_local_id() else { return; }; let Some(apa) = self.ap.apas.get_mut(&hir_id) else { diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 237c8e66ee3fc..ea8c56d9a79b6 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,9 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local, path_to_local_id, span_contains_comment, sym, -}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local_id, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -102,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` if let ExprKind::Assign(left, right, _) = expr.kind - && let Some(local_id) = path_to_local(left) + && let Some(local_id) = left.res_local_id() && let Some(size_expr) = Self::as_vec_initializer(cx, right) { let vi = VecAllocation { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 319051dce685d..c3cb2c09752fe 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -361,7 +361,8 @@ impl<'tcx> IndexBinding<'_, 'tcx> { // - Variable declaration is outside the suggestion span // - Variable is not used as an index or elsewhere later if !self.suggest_span.contains(init.span) - || path_to_local(expr) + || expr + .res_local_id() .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id))) || !self.is_used_other_than_swapping(first_segment.ident) { diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 3c21d194b81d5..5d0945bece553 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{is_from_proc_macro, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, Node, PatKind}; @@ -152,7 +153,7 @@ fn all_bindings_are_for_conv<'tcx>( locals: &[&Expr<'_>], kind: ToType, ) -> bool { - let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { + let Some(locals) = locals.iter().map(|e| e.res_local_id()).collect::>>() else { return false; }; let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::>(); diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 5792b6b3178d7..51596859e4c7b 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; -use clippy_utils::{get_parent_expr, is_mutable, path_to_local}; +use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -162,7 +163,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && parent_ty.is_any_ptr() { - if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && expr_b.res_local_id().is_some() { // When the type implements `Copy`, a reference to the new struct works on the // copy. Using the original would borrow it. return false; diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 2f31aa55af6f8..99201a1ca215f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -169,7 +169,7 @@ fn collect_unwrap_info<'tcx>( }, ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false), ExprKind::MethodCall(method_name, receiver, [], _) - if let Some(local_id) = path_to_local(receiver) + if let Some(local_id) = receiver.res_local_id() && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => @@ -318,7 +318,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { // find `unwrap[_err]()` or `expect("...")` calls: if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) - && let Some(id) = path_to_local(self_arg) + && let Some(id) = self_arg.res_local_id() && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a99b5a9576a31..dab6a2351a234 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, -}; +use clippy_utils::{get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -309,7 +307,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, ..) = pat.kind && ann != BindingMode::MUT diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7a215d3916221..9437a378c390e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -174,7 +174,8 @@ macro_rules! extract_msrv_attr { /// // ^^^ input /// ``` pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) { @@ -429,20 +430,10 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind - && let Res::Local(id) = path.res - { - return Some(id); - } - None -} - /// Returns true if the expression is a path to a local with the specified `HirId`. /// Use this function to see if an expression matches a function argument or a match binding. pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool { - path_to_local(expr) == Some(id) + expr.res_local_id() == Some(id) } /// If the expression is a path to a local (with optional projections), @@ -3438,7 +3429,7 @@ pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) - /// Returns `true` if `expr` designates a mutable static, a mutable local binding, or an expression /// that can be owned. pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) From a6078f87db8922aa23b9882ea4ace8a88f7ca6a9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 16:48:41 -0400 Subject: [PATCH 1545/1900] Remove `path_to_local_id` --- clippy_lints/src/collection_is_never_read.rs | 10 +++++----- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/let_if_seq.rs | 4 ++-- clippy_lints/src/lines_filter_map_ok.rs | 6 +++--- .../src/loops/char_indices_as_byte_indices.rs | 8 ++++---- clippy_lints/src/loops/manual_flatten.rs | 5 +++-- clippy_lints/src/loops/utils.rs | 4 ++-- clippy_lints/src/manual_clamp.rs | 9 ++++----- clippy_lints/src/manual_hash_one.rs | 5 +++-- .../matches/infallible_destructuring_match.rs | 5 +++-- clippy_lints/src/matches/manual_filter.rs | 5 ++--- clippy_lints/src/matches/manual_utils.rs | 10 +++++----- clippy_lints/src/methods/bytecount.rs | 6 +++--- clippy_lints/src/methods/filter_map.rs | 18 +++++++++--------- clippy_lints/src/methods/manual_inspect.rs | 9 +++++---- clippy_lints/src/methods/manual_ok_or.rs | 5 ++--- .../methods/needless_character_iteration.rs | 8 ++++---- clippy_lints/src/methods/needless_collect.rs | 11 +++++------ .../src/methods/option_as_ref_deref.rs | 8 ++++---- clippy_lints/src/methods/str_splitn.rs | 5 +++-- .../src/methods/unnecessary_filter_map.rs | 8 ++++---- clippy_lints/src/methods/unnecessary_fold.rs | 7 ++++--- clippy_lints/src/methods/unnecessary_map_or.rs | 10 +++++----- clippy_lints/src/methods/useless_asref.rs | 7 ++++--- clippy_lints/src/methods/utils.rs | 6 +++--- .../src/mixed_read_write_in_expression.rs | 4 ++-- clippy_lints/src/needless_pass_by_value.rs | 6 +++--- clippy_lints/src/only_used_in_recursion.rs | 4 ++-- clippy_lints/src/pathbuf_init_then_push.rs | 6 +++--- clippy_lints/src/question_mark.rs | 14 ++++++++------ .../src/reserve_after_initialization.rs | 5 +++-- clippy_lints/src/returns/let_and_return.rs | 5 +++-- clippy_lints/src/shadow.rs | 4 ++-- clippy_lints/src/slow_vector_initialization.rs | 6 +++--- clippy_lints/src/string_patterns.rs | 9 +++++---- clippy_lints/src/uninit_vec.rs | 6 +++--- clippy_lints/src/unused_peekable.rs | 6 +++--- clippy_lints/src/vec_init_then_push.rs | 5 +++-- clippy_lints/src/zombie_processes.rs | 6 +++--- clippy_utils/src/lib.rs | 10 ++-------- clippy_utils/src/usage.rs | 5 +++-- clippy_utils/src/visitors.rs | 11 ++++++----- 42 files changed, 150 insertions(+), 145 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 95f3589c4477f..fd84ce70bd717 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::MaybeDef; +use clippy_utils::get_enclosing_block; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -81,7 +81,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // Inspect all expressions and sub-expressions in the block. for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. - if !path_to_local_id(expr, id) { + if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); } @@ -93,7 +93,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // id = ...; // Not reading `id`. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::Assign(lhs, ..) = parent.kind - && path_to_local_id(lhs, id) + && lhs.res_local_id() == Some(id) { return ControlFlow::Continue(()); } @@ -107,7 +107,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // have side effects, so consider them a read. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind - && path_to_local_id(receiver, id) + && receiver.res_local_id() == Some(id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && !method_def_id.is_local() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 13aefa83ae636..21385ee4fdc7f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local_id}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -305,7 +305,7 @@ fn check_inputs( matches!( p.pat.kind, PatKind::Binding(BindingMode::NONE, id, _, None) - if path_to_local_id(arg, id) + if arg.res_local_id() == Some(id) ) // Only allow adjustments which change regions (i.e. re-borrowing). && typeck diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index e480c8fbed53c..2dbf55a8540ba 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_errors::Applicability; @@ -145,7 +145,7 @@ fn check_assign<'tcx>( && let Some(expr) = block.stmts.iter().last() && let hir::StmtKind::Semi(expr) = expr.kind && let hir::ExprKind::Assign(var, value, _) = expr.kind - && path_to_local_id(var, decl) + && var.res_local_id() == Some(decl) { if block .stmts diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index ab00cd9ca539c..dacc2c1bdbdac 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_diag_item_method, is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -120,7 +120,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && path_to_local_id(receiver, param.pat.hir_id) + && receiver.res_local_id() == Some(param.pat.hir_id) && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index f2c87a2863c5f..7acf2a5462220 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; +use clippy_utils::{eq_expr_value, higher, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr { // Destructured iterator element `(idx, _)`, look for uses of the binding for_each_expr(cx, body, |expr| { - if path_to_local_id(expr, binding_id) { + if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } CONTINUE @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr // Bound as a tuple, look for `tup.0` for_each_expr(cx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind - && path_to_local_id(e, binding_id) + && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index ddb8bb536c04e..96de118b5233b 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -2,9 +2,10 @@ use super::MANUAL_FLATTEN; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment}; +use clippy_utils::{higher, is_refutable, peel_blocks_with_stmt, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( = higher::IfLet::hir(cx, inner_expr) // Ensure match_expr in `if let` statement is the same as the pat from the for-loop && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind - && path_to_local_id(let_expr, pat_hir_id) + && let_expr.res_local_id() == Some(pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` && let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 842f542d22e7d..7bf7565f73c71 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,6 +1,6 @@ use clippy_utils::res::MaybeResPath; use clippy_utils::ty::{has_iter_method, implements_trait}; -use clippy_utils::{get_parent_expr, is_integer_const, path_to_local_id, sugg}; +use clippy_utils::{get_parent_expr, is_integer_const, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -176,7 +176,7 @@ impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { } // If node is the desired variable, see how it's used - if path_to_local_id(expr, self.var_id) { + if expr.res_local_id() == Some(self.var_id) { if self.past_loop { self.state = InitializeVisitorState::DontWarn; return; diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index eaaab292f2f53..f7cc23517bcfa 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -8,8 +8,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, sym, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -436,7 +435,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt let first = BinaryOp::new(first)?; let second = BinaryOp::new(second)?; if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind - && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && peel_blocks_with_stmt(last_arm.body).res_local_id() == Some(*binding) && last_arm.guard.is_none() { // Proceed as normal @@ -656,8 +655,8 @@ fn is_clamp_meta_pattern<'tcx>( let (min, max) = (second_expr, first_expr); let refers_to_input = match input_hir_ids { Some((first_hir_id, second_hir_id)) => { - path_to_local_id(peel_blocks(first_bin.left), first_hir_id) - && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id) + && peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id) }, None => eq_expr_value(cx, first_bin.left, second_bin.left), }; diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index b3ee45cc02098..5c1289231eece 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -82,7 +83,7 @@ impl LateLintPass<'_> for ManualHashOne { && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash && is_trait_method(cx, hash_expr, sym::Hash) - && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() // There should be no more statements referencing `hasher` diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 93d7683d2af81..be4b4f346dbcf 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { && args.len() == 1 && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind && let body = peel_blocks(arms[0].body) - && path_to_local_id(body, arg) + && body.res_local_id() == Some(arg) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 227db74b9d78a..d7224052ebc58 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_to_local_id; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::visitors::contains_unsafe_block; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -67,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: { return ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && path_to_local_id(arg, target); + && arg.res_local_id() == Some(target); } false } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 60925db893dae..235cb9e4ecce8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,12 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -138,7 +138,7 @@ where { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { - if path_to_local_id(some_expr.expr, id) + if some_expr.expr.res_local_id() == Some(id) && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { @@ -190,7 +190,7 @@ pub struct SuggInfo<'a> { fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) + if arg.res_local_id() == Some(binding) && cx.typeck_results().expr_adjustments(arg).is_empty() && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index f3a34ce6964ed..77ac181ee633a 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; +use clippy_utils::{peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); - path_to_local_id(expr, arg_id) + expr.res_local_id() == Some(arg_id) }) && let needle = if operand_is_arg(l) { r diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index d813105165cef..53ce09c16e441 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -134,16 +134,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => map_arg, } // .map(|y| y[.acceptable_method()].unwrap()) - && let simple_equal = (path_to_local_id(receiver, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id)) + && let simple_equal = (receiver.res_local_id() == Some(filter_param_id) + && map_arg_peeled.res_local_id() == Some(map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` let a_path = if !is_filter_param_ref && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) + a_path.res_local_id() == Some(filter_param_id) + && b.res_local_id() == Some(map_param_id) && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) }) && (simple_equal @@ -166,7 +166,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| { if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind && let PatKind::Binding(_, local_id, ident, _) = subpat.kind - && path_to_local_id(expr.peel_blocks(), local_id) + && expr.peel_blocks().res_local_id() == Some(local_id) && let Some(local_variant_def_id) = path.res.opt_def_id() && local_variant_def_id == variant_def_id { @@ -204,7 +204,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => return None, }; - if path_to_local_id(scrutinee, map_param_id) + if scrutinee.res_local_id() == Some(map_param_id) // else branch should be a `panic!` or `unreachable!` macro call && let Some(mac) = root_macro_call(else_.peel_blocks().span) && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable)) @@ -247,7 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind - && path_to_local_id(scrutinee, filter_param_id) + && scrutinee.res_local_id() == Some(filter_param_id) && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind && let Some(variant_def_id) = path.res.opt_def_id() { diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index bc96815944d5f..e506b8e55b5fb 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -29,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let ExprKind::Block(block, _) = body.value.kind && let Some(final_expr) = block.expr && !block.stmts.is_empty() - && path_to_local_id(final_expr, arg_id) + && final_expr.res_local_id() == Some(arg_id) && typeck.expr_adjustments(final_expr).is_empty() { let mut requires_copy = false; @@ -46,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { - if path_to_local_id(e, arg_id) { + if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (_, false) | (UseKind::Deref | UseKind::Return(..), true) => { @@ -64,7 +65,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: }); } else if matches!(e.kind, ExprKind::Ret(_)) { ret_count += 1; - } else if path_to_local_id(e, arg_id) { + } else if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (UseKind::Return(..), false) => { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 80b955829f1fd..a8e30e44488cc 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_to_local_id; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; @@ -60,7 +59,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { && let ExprKind::Call(callee, [ok_arg]) = body.value.kind && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { - path_to_local_id(ok_arg, param_id) + ok_arg.res_local_id() == Some(param_id) } else { false } diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index d161c002d083f..948ed8a25746e 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,4 +1,4 @@ -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -9,7 +9,7 @@ use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{path_to_local_id, peel_blocks, sym}; +use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -33,7 +33,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && method.ident.name == sym::is_ascii - && path_to_local_id(receiver, first_param) + && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char && let Some(snippet) = before_chars.get_source_text(cx) @@ -77,7 +77,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) - && path_to_local_id(peels_expr_ref(arg), first_param) + && peels_expr_ref(arg).res_local_id() == Some(first_param) && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 2997167f4a58a..1e1888592dcee 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -7,8 +7,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local_id, - sym, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -345,7 +344,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if path_to_local_id(recv, self.target) { + if recv.res_local_id() == Some(self.target) { if self .illegal_mutable_capture_ids .intersection(&self.current_mutably_captured_ids) @@ -401,7 +400,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } } // Check if the collection is used for anything else - if path_to_local_id(expr, self.target) { + if expr.res_local_id() == Some(self.target) { self.seen_other = true; } else { walk_expr(self, expr); @@ -463,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if path_to_local_id(expr, self.id) { + if expr.res_local_id() == Some(self.id) { self.count += 1; } else { walk_expr(self, expr); @@ -548,7 +547,7 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { && (recv.hir_id == self.hir_id_of_expr || self .hir_id_of_let_binding - .is_some_and(|hid| path_to_local_id(recv, hid))) + .is_some_and(|hid| recv.res_local_id() == Some(hid))) && !is_trait_method(self.cx, expr, sym::Iterator) { return ControlFlow::Break(()); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 4c99c34892f45..3d489075ce8ac 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -51,7 +51,7 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + if receiver.res_local_id() == Some(closure_body.params[0].pat.hir_id) && let adj = cx .typeck_results() .expr_adjustments(receiver) @@ -72,7 +72,7 @@ pub(super) fn check( if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + inner2.res_local_id() == Some(closure_body.params[0].pat.hir_id) } else { false } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index a1a482deb2c30..f5d1d3d4703d5 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,10 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, path_to_local_id, paths, sym}; +use clippy_utils::{is_diag_item_method, paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -214,7 +215,7 @@ fn indirect_usage<'tcx>( { let mut path_to_binding = None; let _: Option = for_each_expr(cx, init_expr, |e| { - if path_to_local_id(e, binding) { + if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } ControlFlow::Continue(Descend::from(path_to_binding.is_none())) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index cffb01f1bbc19..15fc1fe8007ef 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -97,7 +97,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc match expr.kind { hir::ExprKind::Call(func, args) => { if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { - if path_to_local_id(&args[0], arg_id) { + if args[0].res_local_id() == Some(arg_id) { return (false, false); } return (true, false); @@ -107,7 +107,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::MethodCall(segment, recv, [arg], _) => { if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && path_to_local_id(arg, arg_id) + && arg.res_local_id() == Some(arg_id) { (false, true) } else { diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 8e3cc9abe8328..a57c605a17847 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{is_trait_method, peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -74,8 +75,8 @@ fn check_fold_with_op( && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - && path_to_local_id(left_expr, first_arg_id) - && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + && left_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index bee8b6328a541..0c01be4b18756 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,11 +3,11 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -76,11 +76,11 @@ pub(super) fn check<'a>( // .map_or(true, |x| x != y) // .map_or(true, |x| y != x) - swapped comparison && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) - && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } + && let non_binding_location = if l.res_local_id() == Some(hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then that's a strange edge case and + // if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this - && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) + && (l.res_local_id() == Some(hir_id)) != (r.res_local_id() == Some(hir_id)) && !is_local_used(cx, non_binding_location, hir_id) && let typeck_results = cx.typeck_results() && let l_ty = typeck_results.expr_ty(l) diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index e56f4b80d0172..b9c87d0a5c87e 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -137,7 +138,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) - && path_to_local_id(obj, local_id) + && obj.res_local_id() == Some(local_id) { true } else { @@ -146,7 +147,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { }, hir::ExprKind::Call(call, [recv]) => { if let hir::ExprKind::Path(qpath) = call.kind - && path_to_local_id(recv, local_id) + && recv.res_local_id() == Some(local_id) { check_qpath(cx, qpath, call.hir_id) } else { diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 9b670266d0a9c..1e1b124b44866 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,5 +1,5 @@ -use clippy_utils::res::MaybeDef; -use clippy_utils::{get_parent_expr, path_to_local_id, usage}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_expr, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -130,7 +130,7 @@ impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() - .any(|hir_id| path_to_local_id(expr, *hir_id)) + .any(|&hir_id| expr.res_local_id() == Some(hir_id)) } } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6ed2628c37e20..ddd4271960e13 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::res::MaybeResPath; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -326,7 +326,7 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) + if expr.res_local_id() == Some(self.var) // Check that this is a read, not a write. && !is_in_assignment_position(self.cx, expr) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 6c578d39bacbd..fb5f21acf2af2 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; +use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -361,7 +361,7 @@ fn extract_clone_suggestions<'tcx>( let mut spans = Vec::new(); for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) + && recv.res_local_id() == Some(id) { if seg.ident.name == sym::capacity { return ControlFlow::Break(()); diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 63d9271ecf058..784ea34bac58f 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_expr_use_or_unification_node; use clippy_utils::res::{MaybeQPath, MaybeResPath}; -use clippy_utils::{get_expr_use_or_unification_node, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { }, // Parameter update e.g. `x = x + 1` ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) - if rhs.hir_id == child_id && path_to_local_id(lhs, id) => + if rhs.hir_id == child_id && lhs.res_local_id() == Some(id) => { return; }, diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index f561c5c4d3e91..a5e57d97301e3 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::{path_to_local_id, sym}; +use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -176,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { if let Some(mut searcher) = self.searcher.take() && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { searcher.err_span = searcher.err_span.to(stmt.span); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ce8db486de1af..e67ea1f5e370d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,8 +11,8 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - pat_and_expr_can_be_question_mark, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, - span_contains_comment, sym, + pat_and_expr_can_be_question_mark, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, + sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -378,7 +378,7 @@ fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Ar // Extract out `val` && let Some(binding) = extract_binding_pat(val_binding) // Check body is just `=> val` - && path_to_local_id(peel_blocks(arm.body), binding) + && peel_blocks(arm.body).res_local_id() == Some(binding) { true } else { @@ -431,8 +431,10 @@ fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); match expr.kind { - ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), - _ => path_to_local_id(expr, val), + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => { + is_into_call && recv.res_local_id() == Some(val) + }, + _ => expr.res_local_id() == Some(val), } } @@ -484,7 +486,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_then, if_else, ) - && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + && ((is_early_return(sym::Option, cx, &if_block) && peel_blocks(if_then).res_local_id() == Some(bind_id)) || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) diff --git a/clippy_lints/src/reserve_after_initialization.rs b/clippy_lints/src/reserve_after_initialization.rs index 51adbbcd58bdb..ce86a9caac754 100644 --- a/clippy_lints/src/reserve_after_initialization.rs +++ b/clippy_lints/src/reserve_after_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_from_proc_macro, path_to_local_id, sym}; +use clippy_utils::{is_from_proc_macro, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::reserve && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs index e2002fb36e5a6..f54a26a77620a 100644 --- a/clippy_lints/src/returns/let_and_return.rs +++ b/clippy_lints/src/returns/let_and_return.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, StmtKind}; @@ -21,7 +22,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && cx.tcx.hir_attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) + && retexpr.res_local_id() == Some(local_id) && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 14399867f3181..7fdea6bec5100 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use rustc_data_structures::fx::FxHashMap; @@ -202,7 +202,7 @@ pub fn is_local_used_except<'tcx>( for_each_expr(cx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) - } else if path_to_local_id(e, id) { + } else if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index ea8c56d9a79b6..b25fa0905feb8 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local_id, span_contains_comment, sym}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -244,7 +244,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind - && path_to_local_id(self_arg, self.vec_alloc.local_id) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { @@ -256,7 +256,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind - && path_to_local_id(self_arg, self.vec_alloc.local_id) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index f63e6b3087b9e..e5347bf3e8f07 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -5,9 +5,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::sym; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -146,12 +147,12 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { - if path_to_local_id(left, binding) + if left.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, right) { set_char_spans.push(span); ControlFlow::Continue(Descend::No) - } else if path_to_local_id(right, binding) + } else if right.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, left) { set_char_spans.push(span); @@ -164,7 +165,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< ExprKind::Match(match_value, [arm, _], _) => { if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() || arm.guard.is_some() - || !path_to_local_id(match_value, binding) + || match_value.res_local_id() != Some(binding) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 9532423b73edc..df06982904b37 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -140,7 +140,7 @@ enum VecLocation<'tcx> { impl<'tcx> VecLocation<'tcx> { pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match self { - VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id), VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), } } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 5fa16dc5f7a2a..58b86c1cb4a99 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; +use clippy_utils::{fn_def_id, is_trait_method, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if path_to_local_id(ex, self.expected_hir_id) { + if ex.res_local_id() == Some(self.expected_hir_id) { for (_, node) in self.cx.tcx.hir_parent_iter(ex.hir_id) { match node { Node::Expr(expr) => { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 8d873536295dc..5d074208c029c 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -201,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { self.searcher = Some(VecPushSearcher { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 1f7ea06475d87..0319f3e656e1b 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,7 +1,7 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; -use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{fn_def_id, get_enclosing_block}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; @@ -168,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { return walk_expr(self, ex); } - if path_to_local_id(ex, self.local_id) { + if ex.res_local_id() == Some(self.local_id) { match self.cx.tcx.parent_hir_node(ex.hir_id) { Node::Stmt(Stmt { kind: StmtKind::Semi(_), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9437a378c390e..837db8cef6f4e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -430,12 +430,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator, id: HirId) -> bool { - expr.res_local_id() == Some(id) -} - /// If the expression is a path to a local (with optional projections), /// returns the canonical `HirId` of the local. /// @@ -1599,7 +1593,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc .ctor_parent(cx) .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind - && path_to_local_id(arm.body, hir_id) + && arm.body.res_local_id() == Some(hir_id) { return true; } @@ -1904,7 +1898,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< match (pat.kind, expr.kind) { (PatKind::Binding(_, id, _, _), _) if by_hir => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty() }, (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { matches!(path.segments, [ segment] if segment.ident.name == ident.name) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 6eccbcdb12281..e27f1dabeefab 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,4 +1,5 @@ use crate::macros::root_macro_call_first_node; +use crate::res::MaybeResPath; use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; @@ -196,7 +197,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { for_each_expr(cx, v, |e| { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -222,7 +223,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let mut past_expr = false; for_each_expr(cx, block, |e| { if past_expr { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 96f3c1fbe3e5a..84e4f043e34f1 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,8 @@ +use crate::get_enclosing_block; use crate::msrvs::Msrv; use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; -use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -312,7 +313,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) { + if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -564,7 +565,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( if self.res.is_break() { return; } - if path_to_local_id(e, self.local_id) { + if e.res_local_id() == Some(self.local_id) { self.res = (self.f)(e); } else { walk_expr(self, e); @@ -740,7 +741,7 @@ pub fn for_each_local_assignment<'tcx, B>( fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && self.res.is_continue() - && path_to_local_id(lhs, self.local_id) + && lhs.res_local_id() == Some(self.local_id) { self.res = (self.f)(rhs); self.visit_expr(rhs); @@ -785,7 +786,7 @@ pub fn local_used_once<'tcx>( let mut expr = None; let cf = for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) && expr.replace(e).is_some() { + if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) From 2e6729ea641194a1a54ef4bcbc2062e1062cd6a1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:08:35 -0400 Subject: [PATCH 1546/1900] Remove `is_diag_trait_item` --- clippy_lints/src/assigning_clones.rs | 20 ++++----- clippy_lints/src/format_args.rs | 9 ++-- clippy_lints/src/format_impl.rs | 12 ++++-- clippy_lints/src/manual_clamp.rs | 12 +++--- clippy_lints/src/methods/implicit_clone.rs | 29 ++++++------- clippy_lints/src/methods/manual_inspect.rs | 10 ++--- clippy_lints/src/methods/map_clone.rs | 15 +++---- .../src/methods/suspicious_to_owned.rs | 8 ++-- .../src/methods/unnecessary_to_owned.rs | 43 ++++++++++--------- clippy_lints/src/methods/useless_asref.rs | 16 +++---- clippy_lints/src/non_canonical_impls.rs | 7 +-- clippy_utils/src/lib.rs | 23 ++++------ 12 files changed, 102 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 6f5c352c061c8..efce23d13a382 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,9 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, sym}; +use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -69,15 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && let typeck = cx.typeck_results() - && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind { + && let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind { ExprKind::Call(f, [arg]) if let ExprKind::Path(fn_path) = &f.kind - && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() => + && let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) => { - (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id)) + (CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id)) }, - ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => { - (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id)) + ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => { + (CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id)) }, _ => return, } @@ -85,16 +85,16 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Don't lint in macros. && ctxt.is_root() && let which_trait = match fn_name { - sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, + sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone, sym::to_owned - if is_diag_trait_item(cx, fn_id, sym::ToOwned) + if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned) && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, _ => return, } - && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args) // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index d7a9dd0d008a6..011cbf8c5d41c 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -12,7 +12,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; +use clippy_utils::{is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -498,8 +498,11 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { let cx = self.cx; if !value.span.from_expansion() && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToString) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToString) && let receiver_ty = cx.typeck_results().expr_ty(receiver) && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) && let (n_needed_derefs, target) = diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 4dde968659e03..903d43e56c4b2 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::res::MaybeResPath; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, peel_ref_operators, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_as_impl, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -158,8 +158,12 @@ impl FormatImplExpr<'_, '_> { && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - && let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id) - && is_diag_trait_item(self.cx, expr_def_id, sym::ToString) + && self + .cx + .typeck_results() + .type_dependent_def_id(self.expr.hir_id) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::ToString) // Is the method is called on self && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind && let [segment] = path.segments diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index f7cc23517bcfa..38d603ed39aa1 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,13 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym, -}; +use clippy_utils::{eq_expr_value, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -331,11 +329,11 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { match func.kind { ExprKind::Path(QPath::Resolved(None, path)) => { - let id = path.res.opt_def_id()?; - match cx.tcx.get_diagnostic_name(id) { + let def = path.res.opt_def(cx)?; + match cx.tcx.get_diagnostic_name(def.1) { Some(sym::cmp_min) => Some(FunctionType::CmpMin), Some(sym::cmp_max) => Some(FunctionType::CmpMax), - _ if is_diag_trait_item(cx, id, sym::Ord) => { + _ if def.assoc_fn_parent(cx).is_diag_item(cx, sym::Ord) => { Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) }, _ => None, diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 0ba84919395cc..57c0ba25ebbf0 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,12 +11,12 @@ use rustc_span::Symbol; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_clone_like(cx, method_name, method_def_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) + && is_clone_like(cx, method_name, method_parent_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) - && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) + && !(ref_count > 0 && method_parent_id.is_diag_item(cx, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() @@ -40,19 +41,15 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: hir::def_id::DefId) -> bool { match method_name { - sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), - sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), - sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), - sym::to_vec => cx - .tcx - .impl_of_assoc(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some(), + sym::to_os_string => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::OsStr), + sym::to_owned => method_parent_id.is_diag_item(cx, sym::ToOwned), + sym::to_path_buf => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Path), + sym::to_string => method_parent_id.is_diag_item(cx, sym::ToString), + sym::to_vec => method_parent_id + .opt_impl_ty(cx) + .is_some_and(|ty| ty.instantiate_identity().is_slice()), _ => false, } } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index e506b8e55b5fb..69f90c19cba7e 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -19,9 +19,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() - && let Some(fn_id) = typeck.type_dependent_def_id(expr.hir_id) - && (is_diag_trait_item(cx, fn_id, sym::Iterator) - || ((is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result)) + && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) + && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) + || ((is_diag_item_method(cx, fn_def.1, sym::Option) || is_diag_item_method(cx, fn_def.1, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 5568f77927ab0..1bc29c9c1dd1c 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; @@ -19,14 +19,13 @@ use super::MAP_CLONE; // If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't // run this lint because it would overlap with `useless_asref` which provides a better suggestion // in this case. -fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { - if is_diag_trait_item(cx, method_id, sym::Iterator) { +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_parent_id: DefId) -> bool { + if method_parent_id.is_diag_item(cx, sym::Iterator) { return true; } // We check if it's an `Option` or a `Result`. - if let Some(id) = cx.tcx.impl_of_assoc(method_id) { - let identity = cx.tcx.type_of(id).instantiate_identity(); - if !identity.is_diag_item(cx, sym::Option) && !identity.is_diag_item(cx, sym::Result) { + if let Some(ty) = method_parent_id.opt_impl_ty(cx) { + if !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result) { return false; } } else { @@ -43,8 +42,8 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> } pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: Msrv) { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && should_run_lint(cx, e, method_id) + if let Some(parent_id) = cx.typeck_results().type_dependent_def_id(e.hir_id).opt_parent(cx) + && should_run_lint(cx, e, parent_id) { match arg.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 9a9c09bc3d062..bcd1f11931fc6 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_diag_trait_item; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; @@ -11,8 +10,11 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) && input_type.is_diag_item(cx, sym::Cow) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 768b286ea1326..a6a39cb6ab30e 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -6,9 +6,7 @@ use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, -}; +use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -34,12 +32,12 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: Msrv, ) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) && args.is_empty() { - if is_cloned_or_copied(cx, method_name, method_def_id) { + if is_cloned_or_copied(cx, method_name, method_parent_id) { unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + } else if is_to_owned_like(cx, expr, method_name, method_parent_id) { if check_split_call_arg(cx, expr, method_name, receiver) { return; } @@ -47,7 +45,7 @@ pub fn check<'tcx>( // `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary // based on its context, that is, whether it is a referent in an `AddrOf` expression, an // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + if check_addr_of_expr(cx, expr, method_name, method_parent_id, receiver) { return; } if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { @@ -70,7 +68,7 @@ fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, receiver: &Expr<'_>, ) -> bool { if let Some(parent) = get_parent_expr(cx, expr) @@ -132,7 +130,7 @@ fn check_addr_of_expr( // `redundant_clone`, but copyable arrays are not. && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { @@ -158,7 +156,7 @@ fn check_addr_of_expr( // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) // but that's ok for Cow::into_owned specifically) && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -614,21 +612,26 @@ fn has_lifetime(ty: Ty<'_>) -> bool { } /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. -fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + matches!(method_name, sym::cloned | sym::copied) && method_parent_id.is_diag_item(cx, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_cow_into_owned(cx, method_name, method_def_id) - || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) - || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) +fn is_to_owned_like<'a>( + cx: &LateContext<'a>, + call_expr: &Expr<'a>, + method_name: Symbol, + method_parent_id: DefId, +) -> bool { + is_cow_into_owned(cx, method_name, method_parent_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_parent_id)) + || is_to_string_on_string_like(cx, call_expr, method_name, method_parent_id) } /// Returns true if the named method is `Cow::into_owned`. -fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + method_name == sym::into_owned && method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -637,9 +640,9 @@ fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, ) -> bool { - if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { + if method_name != sym::to_string || !method_parent_id.is_diag_item(cx, sym::ToString) { return false; } diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index b9c87d0a5c87e..972304d79e751 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -43,11 +43,11 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + let Some(def) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { + if def.opt_parent(cx).is_diag_item(cx, sym::AsRef) || def.opt_parent(cx).is_diag_item(cx, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -80,10 +80,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo applicability, ); } - } else if let Some(impl_id) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) - { + } else if matches!( + def.opt_parent(cx).opt_impl_ty(cx).opt_diag_name(cx), + Some(sym::Option | sym::Result) + ) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 0c5e4d279f5c7..5c1406d782fd6 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, last_path_segment, std_or_core}; +use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -275,8 +275,9 @@ fn expr_is_cmp<'tcx>( }, ExprKind::MethodCall(_, recv, [], _) => { typeck - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + .type_dependent_def(expr.hir_id) + .assoc_parent(cx) + .is_diag_item(cx, sym::Into) && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 837db8cef6f4e..f74923a60ec5b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -366,19 +366,12 @@ pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbo false } -/// Checks if a method is in a diagnostic item trait -pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) { - return cx.tcx.is_diagnostic_item(diag_item, trait_did); - } - false -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|did| is_diag_trait_item(cx, did, diag_item)) + .type_dependent_def(expr.hir_id) + .assoc_fn_parent(cx) + .is_diag_item(cx, diag_item) } /// Checks if the `def_id` belongs to a function that is part of a trait impl. @@ -404,8 +397,8 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { if let ExprKind::Path(ref qpath) = expr.kind { cx.qpath_res(qpath, expr.hir_id) - .opt_def_id() - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item)) + .assoc_parent(cx) + .is_diag_item(cx, diag_item) } else { false } @@ -573,9 +566,9 @@ pub fn is_default_equivalent_call( whole_call_expr: Option<&Expr<'_>>, ) -> bool { if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind - && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() - && (is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx) + && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default) + || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath)) { return true; } From e78f86d550f6db4d2ce3988dce1cce5696b65625 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:40:04 -0400 Subject: [PATCH 1547/1900] Remove `is_diag_item_method` --- clippy_lints/src/lines_filter_map_ok.rs | 13 +++++++++---- clippy_lints/src/methods/manual_inspect.rs | 5 +++-- clippy_lints/src/methods/str_splitn.rs | 8 +++++--- clippy_utils/src/lib.rs | 10 ---------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index dacc2c1bdbdac..c1f551f06c4e2 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::{is_diag_item_method, is_trait_method, sym}; +use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -120,10 +120,15 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && receiver.res_local_id() == Some(param.pat.hir_id) - && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Result) } else { false } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index 69f90c19cba7e..ac5a372264284 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -4,7 +4,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -21,7 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let typeck = cx.typeck_results() && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) - || ((is_diag_item_method(cx, fn_def.1, sym::Option) || is_diag_item_method(cx, fn_def.1, sym::Result)) + || ((fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index f5d1d3d4703d5..eee7fb0c5a813 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,11 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, paths, sym}; +use clippy_utils::{paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -351,7 +351,9 @@ fn parse_iter_usage<'tcx>( && cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) => + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Option) => { (Some(UnwrapKind::Unwrap), e.span) }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f74923a60ec5b..8dbc91cfed351 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -356,16 +356,6 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -/// Checks if a method is defined in an impl of a diagnostic item -pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() - { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } - false -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() From d0be3356ba4c2971eb67a972de46a38db459a1e1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:59:40 -0400 Subject: [PATCH 1548/1900] Remove `is_inherent_method_call` --- clippy_lints/src/floating_point_arithmetic.rs | 7 ++++--- clippy_lints/src/useless_conversion.rs | 6 +++--- clippy_utils/src/lib.rs | 9 --------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 407a3f1306739..5f022ba307ff9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::{ - eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, - is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, is_no_std_crate, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -737,7 +738,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(receiver); - if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { match path.ident.name { sym::ln => check_ln1p(cx, expr, receiver), sym::log => check_log_base(cx, expr, receiver, args), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index dab6a2351a234..9849c4af0318b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_trait_item, is_trait_method, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -438,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - if is_inherent_method_call(cx, expr) { + if cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { matches!( cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8dbc91cfed351..e16c3bd06d6ec 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -347,15 +347,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the given method call expression calls an inherent method. -pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - cx.tcx.trait_of_assoc(method_id).is_none() - } else { - false - } -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() From e69d88bb10407753a02026a360d47b4cda06cda1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 18:06:17 -0400 Subject: [PATCH 1549/1900] Remove `is_trait_method` --- clippy_lints/src/cloned_ref_to_slice_refs.rs | 5 ++- clippy_lints/src/len_zero.rs | 7 ++-- clippy_lints/src/lines_filter_map_ok.rs | 6 +-- .../src/loops/explicit_into_iter_loop.rs | 8 +++- clippy_lints/src/loops/iter_next_loop.rs | 4 +- .../src/loops/while_let_on_iterator.rs | 6 +-- clippy_lints/src/manual_clamp.rs | 12 +++--- clippy_lints/src/manual_hash_one.rs | 6 +-- clippy_lints/src/manual_main_separator_str.rs | 5 ++- .../src/matches/redundant_pattern_match.rs | 6 +-- .../src/methods/cloned_instead_of_copied.rs | 6 ++- .../src/methods/double_ended_iterator_last.rs | 5 ++- clippy_lints/src/methods/filter_map.rs | 10 ++--- .../src/methods/filter_map_bool_then.rs | 7 ++-- .../src/methods/filter_map_identity.rs | 5 ++- clippy_lints/src/methods/filter_map_next.rs | 4 +- clippy_lints/src/methods/flat_map_identity.rs | 7 +++- clippy_lints/src/methods/flat_map_option.rs | 5 +-- clippy_lints/src/methods/inspect_for_each.rs | 4 +- clippy_lints/src/methods/into_iter_on_ref.rs | 4 +- clippy_lints/src/methods/iter_filter.rs | 7 ++-- clippy_lints/src/methods/iter_nth_zero.rs | 5 ++- .../src/methods/iter_out_of_bounds.rs | 5 ++- clippy_lints/src/methods/iter_skip_next.rs | 5 +-- clippy_lints/src/methods/iter_skip_zero.rs | 5 ++- .../src/methods/iterator_step_by_zero.rs | 4 +- clippy_lints/src/methods/manual_inspect.rs | 6 +-- clippy_lints/src/methods/manual_next_back.rs | 6 +-- clippy_lints/src/methods/manual_repeat_n.rs | 5 ++- clippy_lints/src/methods/manual_try_fold.rs | 5 ++- .../src/methods/map_all_any_identity.rs | 7 ++-- clippy_lints/src/methods/map_flatten.rs | 6 +-- clippy_lints/src/methods/map_identity.rs | 6 +-- clippy_lints/src/methods/mod.rs | 38 ++++++++++++------- clippy_lints/src/methods/needless_collect.rs | 18 ++++++--- .../src/methods/range_zip_with_len.rs | 7 ++-- clippy_lints/src/methods/search_is_some.rs | 10 +++-- clippy_lints/src/methods/skip_while_next.rs | 4 +- .../src/methods/string_lit_chars_any.rs | 6 +-- clippy_lints/src/methods/suspicious_map.rs | 8 +++- clippy_lints/src/methods/unbuffered_bytes.rs | 4 +- clippy_lints/src/methods/unit_hash.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 6 +-- clippy_lints/src/methods/unnecessary_fold.rs | 6 +-- .../src/methods/unnecessary_sort_by.rs | 8 +++- .../src/methods/unused_enumerate_index.rs | 6 +-- .../src/methods/verbose_file_reads.rs | 5 +-- clippy_lints/src/methods/waker_clone_wake.rs | 4 +- clippy_lints/src/minmax.rs | 7 +++- clippy_lints/src/needless_for_each.rs | 5 ++- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/unused_peekable.rs | 10 +++-- clippy_lints/src/useless_conversion.rs | 10 ++--- clippy_lints/src/vec.rs | 8 ++-- clippy_utils/src/lib.rs | 8 ---- 55 files changed, 212 insertions(+), 166 deletions(-) diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 72ab292ee3c66..35b799aefb04c 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use clippy_utils::{is_in_const_context, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(_, val, _, _) = item.kind - && is_trait_method(cx, item, sym::Clone) + && cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone) // check for immutability or purity && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 28a0fbc051158..04a8e4739b853 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, -}; +use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -204,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind - && is_trait_method(cx, expr, sym::PartialEq) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { check_empty_expr( diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index c1f551f06c4e2..65e922ac07d36 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +73,7 @@ impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) && cx diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4aa1c2e211d33..daca78e344748 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,6 +1,6 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -43,7 +43,11 @@ impl AdjustKind { } pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { - if !is_trait_method(cx, call_expr, sym::IntoIterator) { + if !cx + .ty_based_def(call_expr) + .opt_parent(cx) + .is_diag_item(cx, sym::IntoIterator) + { return; } diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index b8a263817d297..8a4644cdf5efd 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,12 +1,12 @@ use super::ITER_NEXT_LOOP; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { - if is_trait_method(cx, arg, sym::Iterator) { + if cx.ty_based_def(arg).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index df8b4d1551d42..3ea6ba341bedb 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,10 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression && !uses_iter(cx, &iter_expr_struct, if_then) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 38d603ed39aa1..54387e36d6c84 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,11 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{eq_expr_value, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym}; +use clippy_utils::{eq_expr_value, is_in_const_context, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -290,10 +290,12 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// # ; /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { - if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind - && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() + || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind - && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() + || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 5c1289231eece..ccb8d4272bfac 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -82,7 +82,7 @@ impl LateLintPass<'_> for ManualHashOne { && !hash_expr.span.from_expansion() && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash - && is_trait_method(cx, hash_expr, sym::Hash) + && cx.ty_based_def(hash_expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index f54ccf2c87b0f..e78f6affda2a3 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -1,7 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{is_trait_method, peel_hir_expr_refs}; +use clippy_utils::peel_hir_expr_refs; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; @@ -52,7 +53,7 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { && path.ident.name == sym::to_string && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let Res::Def(DefKind::Const, receiver_def_id) = path.res - && is_trait_method(cx, target, sym::ToString) + && cx.ty_based_def(target).opt_parent(cx).is_diag_item(cx, sym::ToString) && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 916ed25cfd748..2ca76a1fe6945 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,11 +1,11 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; +use clippy_utils::{higher, is_expn_of, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -217,7 +217,7 @@ fn find_method_sugg_for_if_let<'tcx>( if keyword == "while" && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind && method_path.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index f50fb627b89a0..d80d6f7810f53 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -19,7 +19,9 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(cx, msrvs::ITERATOR_COPIED) => { + _ if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && msrv.meets(cx, msrvs::ITERATOR_COPIED) => + { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs index 578865c329180..8ba8264b713c4 100644 --- a/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local_with_projections, sym}; +use clippy_utils::{is_mutable, path_to_local_with_projections, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp let typeck = cx.typeck_results(); // if the "last" method is that of Iterator - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // if self implements DoubleEndedIterator && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) && let self_type = cx.typeck_results().expr_ty(self_expr) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 53ce09c16e441..26b19848fe1b6 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::{SpanlessEq, higher, is_trait_method, peel_blocks, sym}; +use clippy_utils::{SpanlessEq, higher, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -266,7 +266,7 @@ fn is_filter_some_map_unwrap( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> bool { - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) @@ -275,7 +275,7 @@ fn is_filter_some_map_unwrap( /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())` fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { // result has no filter, so we only check for iterators - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); iterator && is_ok_filter_map(cx, filter_arg, map_arg) } @@ -398,7 +398,7 @@ fn is_find_or_filter<'a>( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(Ident, CheckResult<'a>)> { - if is_trait_method(cx, map_recv, sym::Iterator) + if cx.ty_based_def(map_recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) // filter(|x| ...is_some())... && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind && let filter_body = cx.tcx.hir_body(filter_body_id) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 94944bd9445b8..b803d1a3309da 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,9 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::is_copy; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,7 +15,7 @@ use rustc_span::{Span, sym}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !expr.span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Closure(closure) = arg.kind && let body = cx.tcx.hir_body(closure.body) && let value = peel_blocks(body.value) diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index b04d761d4860e..c6e30710eef90 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; @@ -19,7 +20,7 @@ fn is_identity(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(applicability) = is_identity(cx, filter_map_arg) { // check if the iterator is from an empty array, see issue #12653 diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 9f3c346042ff9..698025d58e543 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if !msrv.meets(cx, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 0c2ecfbc8ffdf..043adb8434a0d 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::is_expr_untyped_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +15,9 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && is_expr_untyped_identity_function(cx, flat_map_arg) + { span_lint_and_sugg( cx, FLAT_MAP_IDENTITY, diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 3a7892715ed7d..fb0e8f9d91b5a 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +9,7 @@ use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 3706f3b670ba1..194ddf45e6f11 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -8,7 +8,7 @@ use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 661e2824144c0..c4b116af48713 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +13,7 @@ use super::INTO_ITER_ON_REF; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index adeff375c8aad..8d95b70c6a4bc 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::get_iterator_item_ty; use hir::ExprKind; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; @@ -107,7 +108,7 @@ fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(path, _, [_], _) = expr.kind && path.ident.name == sym::map - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return true; } @@ -141,7 +142,7 @@ fn expression_type( filter_arg: &hir::Expr<'_>, filter_span: Span, ) -> Option { - if !is_trait_method(cx, expr, sym::Iterator) + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || parent_is_map(cx, expr) || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) { diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 0f8abd0172423..e0107b75e4142 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_item_or_ctor; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; use hir::{LangItem, OwnerNode}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +14,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index fa8f9d640ee6e..8cff3bd815a1b 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; use clippy_utils::higher::VecArgs; -use clippy_utils::{expr_or_init, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,7 +59,7 @@ fn check<'tcx>( message: &'static str, note: &'static str, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(len) = get_iterator_length(cx, recv) && let Some(skipped) = expr_as_u128(cx, arg) && skipped > len diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index cf88cab4a77ef..10e58b015762e 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -12,7 +11,7 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let mut application = Applicability::MachineApplicable; span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 663e34437a307..cae31e7c9606e 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_from_proc_macro, is_trait_method}; +use clippy_utils::is_from_proc_macro; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(arg) = ConstEvalCtxt::new(cx) .eval_local(arg_expr, expr.span.ctxt()) .and_then(|constant| { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 90d5d9df55eed..11dde2429adfa 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +8,7 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index ac5a372264284..1a5b180b0c86f 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -20,9 +20,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) - && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) - || ((fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) - || fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) + && (fn_def.opt_parent(cx).is_diag_item(cx, sym::Iterator) + || ((fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/manual_next_back.rs b/clippy_lints/src/methods/manual_next_back.rs index 9a03559b22377..b064f978588c4 100644 --- a/clippy_lints/src/methods/manual_next_back.rs +++ b/clippy_lints/src/methods/manual_next_back.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( .tcx .get_diagnostic_item(sym::DoubleEndedIterator) .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])) - && is_trait_method(cx, rev_call, sym::Iterator) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(rev_call).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/manual_repeat_n.rs b/clippy_lints/src/methods/manual_repeat_n.rs index 83b57cca17bf1..1a7628ed43a4f 100644 --- a/clippy_lints/src/methods/manual_repeat_n.rs +++ b/clippy_lints/src/methods/manual_repeat_n.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use clippy_utils::{expr_use_ctxt, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind && let Some(def_id) = fn_def_id(cx, repeat_expr) && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 23dba47f60f40..f2e127bedde56 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !fold_span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let init_ty = cx.typeck_results().expr_ty(init) && let Some(try_trait) = cx.tcx.lang_items().try_trait() && implements_trait(cx, init_ty, try_trait, &[]) diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index 92b273f557186..ad950f75f8130 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_expr_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -19,8 +20,8 @@ pub(super) fn check( any_arg: &Expr<'_>, method: &str, ) { - if is_trait_method(cx, expr, sym::Iterator) - && is_trait_method(cx, recv, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) && let Some(map_arg) = map_arg.span.get_source_text(cx) diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 18827d71110ec..e4ae14b6cf59c 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, span_contains_comment}; +use clippy_utils::span_contains_comment; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ fn try_get_caller_ty_name_and_method_name( caller_expr: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(&'static str, &'static str)> { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if is_map_to_option(cx, map_arg) { // `(...).map(...)` has type `impl Iterator> Some(("Iterator", "filter_map")) diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index f68967c750ef7..fa394526bdf1e 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; use rustc_lint::{LateContext, LintContext}; @@ -22,7 +22,7 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if (is_trait_method(cx, expr, sym::Iterator) + if (cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || caller_ty.is_diag_item(cx, sym::Result) || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 91cee886bbef8..c9066be51c44b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -152,7 +152,8 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{contains_return, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -5051,7 +5052,7 @@ impl Methods { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { @@ -5072,17 +5073,26 @@ impl Methods { _ => {}, } }, - (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some((sym::cloned, recv2, [], _, _)) => { - iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); - }, - Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { - iter_count::check(cx, expr, recv2, name2); - }, - Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), - _ => {}, + (sym::count, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { + match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::RmCloned, + false, + ); + }, + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + _ => {}, + } }, (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); @@ -5465,7 +5475,7 @@ impl Methods { } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, (sym::to_owned, []) => { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 1e1888592dcee..4f005103d23f8 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,13 +2,11 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, sym, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, sym}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -337,7 +335,11 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() && method_name.ident.name == sym::collect - && is_trait_method(self.cx, expr, sym::Iterator) + && self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); self.visit_expr(recv); @@ -548,7 +550,11 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { || self .hir_id_of_let_binding .is_some_and(|hid| recv.res_local_id() == Some(hid))) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index e13df18333e46..de4207c2abf45 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; -use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_lint::LateContext; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) && is_integer_const(cx, start, 0) @@ -82,7 +83,7 @@ fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Optio /// them to a closure, return the pattern of the closure. fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { if let Some(parent_expr) = get_parent_expr(cx, expr) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind && recv.hir_id == expr.hir_id && matches!( diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 95f7a3ebbd300..c9c75f3f38e2d 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; +use clippy_utils::{is_receiver_of_method_call, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -27,7 +27,11 @@ pub(super) fn check<'tcx>( ) { let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator - if is_trait_method(cx, is_some_recv, sym::Iterator) { + if cx + .ty_based_def(is_some_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) + { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 9f0b6c34ea2ef..2452c499b9cee 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,7 @@ use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.skip_while().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_help( cx, SKIP_WHILE_NEXT, diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index 660ecbc5e6ceb..48e89c2998efa 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( body: &Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let PatKind::Binding(_, arg, _, _) = param.pat.kind && let ExprKind::Lit(lit_kind) = recv.kind && let LitKind::Str(val, _) = lit_kind.node diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 788014d9bb632..ece97c1f758c0 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::expr_or_init; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::usage::mutated_variables; -use clippy_utils::{expr_or_init, is_trait_method}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +9,10 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if is_trait_method(cx, count_recv, sym::Iterator) + if cx + .ty_based_def(count_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() diff --git a/clippy_lints/src/methods/unbuffered_bytes.rs b/clippy_lints/src/methods/unbuffered_bytes.rs index dd5566f8c8baf..fdddd5e2771db 100644 --- a/clippy_lints/src/methods/unbuffered_bytes.rs +++ b/clippy_lints/src/methods/unbuffered_bytes.rs @@ -1,6 +1,6 @@ use super::UNBUFFERED_BYTES; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_hir as hir; use rustc_lint::LateContext; @@ -8,7 +8,7 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { // Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered. - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead) && let ty = cx.typeck_results().expr_ty_adjusted(recv) && !implements_trait(cx, ty, buf_read, &[]) diff --git a/clippy_lints/src/methods/unit_hash.rs b/clippy_lints/src/methods/unit_hash.rs index 3c7955bc46981..9defd5626eb47 100644 --- a/clippy_lints/src/methods/unit_hash.rs +++ b/clippy_lints/src/methods/unit_hash.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -9,7 +9,7 @@ use rustc_span::sym; use super::UNIT_HASH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { span_lint_and_then( cx, UNIT_HASH, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 15fc1fe8007ef..d9d642015063e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_trait_method, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'tcx>, name: Symbol, ) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index a57c605a17847..bd471e0b18e3d 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -116,7 +116,7 @@ pub(super) fn check( fold_span: Span, ) { // Check that this is a call to Iterator::fold rather than just some function called fold - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index baaa36ed235ec..4a3e4c092f3b3 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_trait_method, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -139,7 +140,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp ] = &closure_body.params && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind && method_path.ident.name == sym::cmp - && is_trait_method(cx, closure_body.value, sym::Ord) + && cx + .ty_based_def(closure_body.value) + .opt_parent(cx) + .is_diag_item(cx, sym::Ord) { let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { ( diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 554db52653a35..a7d9b2e0fab01 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; +use clippy_utils::{expr_or_init, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; @@ -42,7 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, // If we call a method on a `std::iter::Enumerate` instance if recv_ty.is_diag_item(cx, sym::Enumerate) // If we are calling a method of the `Iterator` trait - && is_trait_method(cx, call_expr, sym::Iterator) + && cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // And the map argument is a closure && let ExprKind::Closure(closure) = closure_arg.kind && let closure_body = cx.tcx.hir_body(closure.body) diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index d9927b6e368bd..5727843302d6a 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -19,7 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, (msg, help): (&'static str, &'static str), ) { - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) && cx .typeck_results() diff --git a/clippy_lints/src/methods/waker_clone_wake.rs b/clippy_lints/src/methods/waker_clone_wake.rs index b5f34a9be2e23..3081d7f91f067 100644 --- a/clippy_lints/src/methods/waker_clone_wake.rs +++ b/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' if let Some(did) = ty.ty_adt_def() && cx.tcx.is_diagnostic_item(sym::Waker, did.did()) && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind - && is_trait_method(cx, recv, sym::Clone) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Clone) { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, waker_ref.span.source_callsite(), "..", &mut applicability); diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index f9a7c562c7a59..ba62853c74571 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -78,7 +79,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() + || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) + { match path.ident.name { sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3a6ccc2bca998..d03188f1d39b8 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; @@ -7,8 +8,8 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sym; use clippy_utils::ty::has_iter_method; -use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) && method_name.ident.name == sym::for_each - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 71c1a40ca8132..8446b6fbbea57 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -618,7 +618,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type // doesn't coerce to a slice and our adjusted type check below isn't enough, // but it would still be valid to call with a slice - if is_allowed_vec_method(self.cx, use_expr) { + if is_allowed_vec_method(use_expr) { return; } } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 58b86c1cb4a99..668663e4cf794 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::ty::peel_and_count_ty_refs; -use clippy_utils::{fn_def_id, is_trait_method, peel_ref_operators, sym}; +use clippy_utils::{fn_def_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -161,7 +161,11 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 9849c4af0318b..f3b1b681c809a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -3,7 +3,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_trait_item, is_trait_method, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_trait_item, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -132,7 +132,7 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter { Some(recv) @@ -203,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -363,7 +363,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if is_trait_method(cx, e, sym::TryInto) + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) @@ -444,7 +444,7 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { - is_trait_method(cx, expr, sym::Iterator) + cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 52b30ddce12b8..b87db836869d6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -10,7 +10,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, higher, is_in_test, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -124,7 +124,7 @@ impl UselessVec { if let Some(parent) = get_parent_expr(cx, expr) && (adjusts_to_slice(cx, expr) || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(cx, parent)) + || is_allowed_vec_method(parent)) { ControlFlow::Continue(()) } else { @@ -304,11 +304,11 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Checks if the given expression is a method call to a `Vec` method /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. -pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +pub fn is_allowed_vec_method(e: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, _, [], _) = e.kind { matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { - is_trait_method(cx, e, sym::IntoIterator) + false } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e16c3bd06d6ec..9fe03ad0283d8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -347,14 +347,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - cx.typeck_results() - .type_dependent_def(expr.hir_id) - .assoc_fn_parent(cx) - .is_diag_item(cx, diag_item) -} - /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id)) From 6bfb524e9ac6160d6f3fb9af9e253173f98515a0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 18:31:57 -0400 Subject: [PATCH 1550/1900] Remove `is_trait_item` --- clippy_lints/src/useless_conversion.rs | 9 ++++++--- clippy_utils/src/lib.rs | 19 ------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index f3b1b681c809a..0cf5b9431a342 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_trait_item, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -180,7 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { path.ident.name, sym::map | sym::map_err | sym::map_break | sym::map_continue ) && has_eligible_receiver(cx, recv, e) - && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) + && matches!( + arg.res(cx).assoc_parent(cx).opt_diag_name(cx), + Some(sym::Into | sym::From) + ) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() && same_type_modulo_regions(from_ty, to_ty) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9fe03ad0283d8..8250104b0ca28 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -358,25 +358,6 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool } } -/// Checks if the given expression is a path referring an item on the trait -/// that is marked with the given diagnostic item. -/// -/// For checking method call expressions instead of path expressions, use -/// [`is_trait_method`]. -/// -/// For example, this can be used to find if an expression like `u64::default` -/// refers to an item of the trait `Default`, which is associated with the -/// `diag_item` of `sym::Default`. -pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - if let ExprKind::Path(ref qpath) = expr.kind { - cx.qpath_res(qpath, expr.hir_id) - .assoc_parent(cx) - .is_diag_item(cx, diag_item) - } else { - false - } -} - pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), From 1bd490113ca22ecd3d817c75f4a4f796f2ba1eb8 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:08:08 -0700 Subject: [PATCH 1551/1900] slightly less trivial blessings some optimization is behaving slightly differently on box derefs after this change, but the difference is irrelevant --- ...ng_operand.test.GVN.32bit.panic-abort.diff | 119 +++++++++--------- ...g_operand.test.GVN.32bit.panic-unwind.diff | 29 +++-- ...ng_operand.test.GVN.64bit.panic-abort.diff | 119 +++++++++--------- ...g_operand.test.GVN.64bit.panic-unwind.diff | 29 +++-- ...ric_rust_call.call.Inline.panic-abort.diff | 10 +- ...ic_rust_call.call.Inline.panic-unwind.diff | 10 +- ...inline_box_fn.call.Inline.panic-abort.diff | 10 +- ...nline_box_fn.call.Inline.panic-unwind.diff | 10 +- ...67_inline_as_ref_as_mut.b.Inline.after.mir | 10 +- ...67_inline_as_ref_as_mut.d.Inline.after.mir | 10 +- 10 files changed, 176 insertions(+), 180 deletions(-) diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff index f203b80e4776c..bb10fc8ac642d 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff @@ -8,14 +8,14 @@ let _3: std::boxed::Box<()>; let mut _6: *const (); let mut _8: *const [()]; - let mut _9: std::boxed::Box<()>; - let mut _10: *const (); - let mut _23: usize; + let mut _9: *const (); + let mut _22: usize; + let mut _23: std::ptr::NonNull<()>; scope 1 { debug vp_ctx => _1; let _4: *const (); scope 2 { - debug slf => _10; + debug slf => _9; let _5: *const [()]; scope 3 { debug bytes => _5; @@ -33,21 +33,21 @@ } } scope 5 (inlined Box::<()>::new) { + let mut _10: usize; let mut _11: usize; - let mut _12: usize; - let mut _13: *mut u8; + let mut _12: *mut u8; scope 6 (inlined alloc::alloc::exchange_malloc) { - let _14: std::alloc::Layout; - let mut _15: std::result::Result, std::alloc::AllocError>; - let mut _16: isize; - let mut _18: !; + let _13: std::alloc::Layout; + let mut _14: std::result::Result, std::alloc::AllocError>; + let mut _15: isize; + let mut _17: !; scope 7 { - let _17: std::ptr::NonNull<[u8]>; + let _16: std::ptr::NonNull<[u8]>; scope 8 { scope 11 (inlined NonNull::<[u8]>::as_mut_ptr) { scope 12 (inlined NonNull::<[u8]>::as_non_null_ptr) { scope 13 (inlined NonNull::<[u8]>::cast::) { - let mut _22: *mut [u8]; + let mut _21: *mut [u8]; scope 14 (inlined NonNull::<[u8]>::as_ptr) { } } @@ -60,9 +60,9 @@ } } scope 9 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _19: bool; - let _20: (); - let mut _21: std::ptr::Alignment; + let mut _18: bool; + let _19: (); + let mut _20: std::ptr::Alignment; } } } @@ -71,19 +71,19 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); + StorageLive(_10); StorageLive(_11); StorageLive(_12); +- _10 = SizeOf(()); +- _11 = AlignOf(()); ++ _10 = const 0_usize; ++ _11 = const 1_usize; StorageLive(_13); -- _11 = SizeOf(()); -- _12 = AlignOf(()); -+ _11 = const 0_usize; -+ _12 = const 1_usize; - StorageLive(_14); + StorageLive(_15); StorageLive(_16); - StorageLive(_17); - StorageLive(_19); - _19 = const false; -- switchInt(move _19) -> [0: bb6, otherwise: bb5]; + StorageLive(_18); + _18 = const false; +- switchInt(move _18) -> [0: bb6, otherwise: bb5]; + switchInt(const false) -> [0: bb6, otherwise: bb5]; } @@ -98,48 +98,47 @@ } bb3: { -- _18 = handle_alloc_error(move _14) -> unwind unreachable; -+ _18 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; +- _17 = handle_alloc_error(move _13) -> unwind unreachable; ++ _17 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; } bb4: { - _17 = copy ((_15 as Ok).0: std::ptr::NonNull<[u8]>); - StorageLive(_22); - _22 = copy _17 as *mut [u8] (Transmute); - _13 = copy _22 as *mut u8 (PtrToPtr); - StorageDead(_22); - StorageDead(_15); - StorageDead(_17); - StorageDead(_16); + _16 = copy ((_14 as Ok).0: std::ptr::NonNull<[u8]>); + StorageLive(_21); + _21 = copy _16 as *mut [u8] (Transmute); + _12 = copy _21 as *mut u8 (PtrToPtr); + StorageDead(_21); StorageDead(_14); - _3 = ShallowInitBox(copy _13, ()); + StorageDead(_16); + StorageDead(_15); StorageDead(_13); + _3 = ShallowInitBox(copy _12, ()); StorageDead(_12); StorageDead(_11); + StorageDead(_10); _2 = &_3; _1 = copy _2; StorageDead(_2); StorageLive(_4); -- _9 = deref_copy _3; -+ _9 = copy _3; - _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); - _4 = copy _10; + _23 = copy ((_3.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>); + _9 = copy _23 as *const () (Transmute); + _4 = copy _9; - StorageLive(_5); + nop; StorageLive(_6); - _6 = copy _4; -+ _6 = copy _10; - StorageLive(_23); - _23 = const 1_usize; -- _5 = *const [()] from (copy _6, copy _23); -+ _5 = *const [()] from (copy _10, const 1_usize); - StorageDead(_23); ++ _6 = copy _9; + StorageLive(_22); + _22 = const 1_usize; +- _5 = *const [()] from (copy _6, copy _22); ++ _5 = *const [()] from (copy _9, const 1_usize); + StorageDead(_22); StorageDead(_6); StorageLive(_7); StorageLive(_8); _8 = copy _5; - _7 = copy _8 as *mut () (PtrToPtr); -+ _7 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *mut () (Transmute); ++ _7 = copy _23 as *mut () (Transmute); StorageDead(_8); StorageDead(_7); - StorageDead(_5); @@ -149,26 +148,26 @@ } bb5: { -- _20 = Layout::from_size_align_unchecked::precondition_check(copy _11, copy _12) -> [return: bb6, unwind unreachable]; -+ _20 = Layout::from_size_align_unchecked::precondition_check(const 0_usize, const 1_usize) -> [return: bb6, unwind unreachable]; +- _19 = Layout::from_size_align_unchecked::precondition_check(copy _10, copy _11) -> [return: bb6, unwind unreachable]; ++ _19 = Layout::from_size_align_unchecked::precondition_check(const 0_usize, const 1_usize) -> [return: bb6, unwind unreachable]; } bb6: { - StorageDead(_19); - StorageLive(_21); -- _21 = copy _12 as std::ptr::Alignment (Transmute); -- _14 = Layout { size: copy _11, align: move _21 }; -+ _21 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); -+ _14 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; - StorageDead(_21); - StorageLive(_15); -- _15 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], copy _14, const false) -> [return: bb7, unwind unreachable]; -+ _15 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; + StorageDead(_18); + StorageLive(_20); +- _20 = copy _11 as std::ptr::Alignment (Transmute); +- _13 = Layout { size: copy _10, align: move _20 }; ++ _20 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); ++ _13 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; + StorageDead(_20); + StorageLive(_14); +- _14 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], copy _13, const false) -> [return: bb7, unwind unreachable]; ++ _14 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; } bb7: { - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb4, 1: bb3, otherwise: bb2]; + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb4, 1: bb3, otherwise: bb2]; } + } + diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff index b2085afb71379..88754cb02776c 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff @@ -8,14 +8,14 @@ let _3: std::boxed::Box<()>; let mut _6: *const (); let mut _8: *const [()]; - let mut _9: std::boxed::Box<()>; - let mut _10: *const (); - let mut _11: usize; + let mut _9: *const (); + let mut _10: usize; + let mut _11: std::ptr::NonNull<()>; scope 1 { debug vp_ctx => _1; let _4: *const (); scope 2 { - debug slf => _10; + debug slf => _9; let _5: *const [()]; scope 3 { debug bytes => _5; @@ -45,26 +45,25 @@ _1 = copy _2; StorageDead(_2); StorageLive(_4); -- _9 = deref_copy _3; -+ _9 = copy _3; - _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); - _4 = copy _10; + _11 = copy ((_3.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>); + _9 = copy _11 as *const () (Transmute); + _4 = copy _9; - StorageLive(_5); + nop; StorageLive(_6); - _6 = copy _4; -+ _6 = copy _10; - StorageLive(_11); - _11 = const 1_usize; -- _5 = *const [()] from (copy _6, copy _11); -+ _5 = *const [()] from (copy _10, const 1_usize); - StorageDead(_11); ++ _6 = copy _9; + StorageLive(_10); + _10 = const 1_usize; +- _5 = *const [()] from (copy _6, copy _10); ++ _5 = *const [()] from (copy _9, const 1_usize); + StorageDead(_10); StorageDead(_6); StorageLive(_7); StorageLive(_8); _8 = copy _5; - _7 = copy _8 as *mut () (PtrToPtr); -+ _7 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *mut () (Transmute); ++ _7 = copy _11 as *mut () (Transmute); StorageDead(_8); StorageDead(_7); - StorageDead(_5); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index f072eb6ef8bf6..97b7c9a1d5584 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -8,14 +8,14 @@ let _3: std::boxed::Box<()>; let mut _6: *const (); let mut _8: *const [()]; - let mut _9: std::boxed::Box<()>; - let mut _10: *const (); - let mut _23: usize; + let mut _9: *const (); + let mut _22: usize; + let mut _23: std::ptr::NonNull<()>; scope 1 { debug vp_ctx => _1; let _4: *const (); scope 2 { - debug slf => _10; + debug slf => _9; let _5: *const [()]; scope 3 { debug bytes => _5; @@ -33,21 +33,21 @@ } } scope 5 (inlined Box::<()>::new) { + let mut _10: usize; let mut _11: usize; - let mut _12: usize; - let mut _13: *mut u8; + let mut _12: *mut u8; scope 6 (inlined alloc::alloc::exchange_malloc) { - let _14: std::alloc::Layout; - let mut _15: std::result::Result, std::alloc::AllocError>; - let mut _16: isize; - let mut _18: !; + let _13: std::alloc::Layout; + let mut _14: std::result::Result, std::alloc::AllocError>; + let mut _15: isize; + let mut _17: !; scope 7 { - let _17: std::ptr::NonNull<[u8]>; + let _16: std::ptr::NonNull<[u8]>; scope 8 { scope 11 (inlined NonNull::<[u8]>::as_mut_ptr) { scope 12 (inlined NonNull::<[u8]>::as_non_null_ptr) { scope 13 (inlined NonNull::<[u8]>::cast::) { - let mut _22: *mut [u8]; + let mut _21: *mut [u8]; scope 14 (inlined NonNull::<[u8]>::as_ptr) { } } @@ -60,9 +60,9 @@ } } scope 9 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _19: bool; - let _20: (); - let mut _21: std::ptr::Alignment; + let mut _18: bool; + let _19: (); + let mut _20: std::ptr::Alignment; } } } @@ -71,19 +71,19 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); + StorageLive(_10); StorageLive(_11); StorageLive(_12); +- _10 = SizeOf(()); +- _11 = AlignOf(()); ++ _10 = const 0_usize; ++ _11 = const 1_usize; StorageLive(_13); -- _11 = SizeOf(()); -- _12 = AlignOf(()); -+ _11 = const 0_usize; -+ _12 = const 1_usize; - StorageLive(_14); + StorageLive(_15); StorageLive(_16); - StorageLive(_17); - StorageLive(_19); - _19 = const false; -- switchInt(move _19) -> [0: bb6, otherwise: bb5]; + StorageLive(_18); + _18 = const false; +- switchInt(move _18) -> [0: bb6, otherwise: bb5]; + switchInt(const false) -> [0: bb6, otherwise: bb5]; } @@ -98,48 +98,47 @@ } bb3: { -- _18 = handle_alloc_error(move _14) -> unwind unreachable; -+ _18 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; +- _17 = handle_alloc_error(move _13) -> unwind unreachable; ++ _17 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; } bb4: { - _17 = copy ((_15 as Ok).0: std::ptr::NonNull<[u8]>); - StorageLive(_22); - _22 = copy _17 as *mut [u8] (Transmute); - _13 = copy _22 as *mut u8 (PtrToPtr); - StorageDead(_22); - StorageDead(_15); - StorageDead(_17); - StorageDead(_16); + _16 = copy ((_14 as Ok).0: std::ptr::NonNull<[u8]>); + StorageLive(_21); + _21 = copy _16 as *mut [u8] (Transmute); + _12 = copy _21 as *mut u8 (PtrToPtr); + StorageDead(_21); StorageDead(_14); - _3 = ShallowInitBox(copy _13, ()); + StorageDead(_16); + StorageDead(_15); StorageDead(_13); + _3 = ShallowInitBox(copy _12, ()); StorageDead(_12); StorageDead(_11); + StorageDead(_10); _2 = &_3; _1 = copy _2; StorageDead(_2); StorageLive(_4); -- _9 = deref_copy _3; -+ _9 = copy _3; - _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); - _4 = copy _10; + _23 = copy ((_3.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>); + _9 = copy _23 as *const () (Transmute); + _4 = copy _9; - StorageLive(_5); + nop; StorageLive(_6); - _6 = copy _4; -+ _6 = copy _10; - StorageLive(_23); - _23 = const 1_usize; -- _5 = *const [()] from (copy _6, copy _23); -+ _5 = *const [()] from (copy _10, const 1_usize); - StorageDead(_23); ++ _6 = copy _9; + StorageLive(_22); + _22 = const 1_usize; +- _5 = *const [()] from (copy _6, copy _22); ++ _5 = *const [()] from (copy _9, const 1_usize); + StorageDead(_22); StorageDead(_6); StorageLive(_7); StorageLive(_8); _8 = copy _5; - _7 = copy _8 as *mut () (PtrToPtr); -+ _7 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *mut () (Transmute); ++ _7 = copy _23 as *mut () (Transmute); StorageDead(_8); StorageDead(_7); - StorageDead(_5); @@ -149,26 +148,26 @@ } bb5: { -- _20 = Layout::from_size_align_unchecked::precondition_check(copy _11, copy _12) -> [return: bb6, unwind unreachable]; -+ _20 = Layout::from_size_align_unchecked::precondition_check(const 0_usize, const 1_usize) -> [return: bb6, unwind unreachable]; +- _19 = Layout::from_size_align_unchecked::precondition_check(copy _10, copy _11) -> [return: bb6, unwind unreachable]; ++ _19 = Layout::from_size_align_unchecked::precondition_check(const 0_usize, const 1_usize) -> [return: bb6, unwind unreachable]; } bb6: { - StorageDead(_19); - StorageLive(_21); -- _21 = copy _12 as std::ptr::Alignment (Transmute); -- _14 = Layout { size: copy _11, align: move _21 }; -+ _21 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); -+ _14 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; - StorageDead(_21); - StorageLive(_15); -- _15 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], copy _14, const false) -> [return: bb7, unwind unreachable]; -+ _15 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; + StorageDead(_18); + StorageLive(_20); +- _20 = copy _11 as std::ptr::Alignment (Transmute); +- _13 = Layout { size: copy _10, align: move _20 }; ++ _20 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); ++ _13 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; + StorageDead(_20); + StorageLive(_14); +- _14 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], copy _13, const false) -> [return: bb7, unwind unreachable]; ++ _14 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; } bb7: { - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb4, 1: bb3, otherwise: bb2]; + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb4, 1: bb3, otherwise: bb2]; } + } + diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff index b2085afb71379..88754cb02776c 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff @@ -8,14 +8,14 @@ let _3: std::boxed::Box<()>; let mut _6: *const (); let mut _8: *const [()]; - let mut _9: std::boxed::Box<()>; - let mut _10: *const (); - let mut _11: usize; + let mut _9: *const (); + let mut _10: usize; + let mut _11: std::ptr::NonNull<()>; scope 1 { debug vp_ctx => _1; let _4: *const (); scope 2 { - debug slf => _10; + debug slf => _9; let _5: *const [()]; scope 3 { debug bytes => _5; @@ -45,26 +45,25 @@ _1 = copy _2; StorageDead(_2); StorageLive(_4); -- _9 = deref_copy _3; -+ _9 = copy _3; - _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); - _4 = copy _10; + _11 = copy ((_3.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>); + _9 = copy _11 as *const () (Transmute); + _4 = copy _9; - StorageLive(_5); + nop; StorageLive(_6); - _6 = copy _4; -+ _6 = copy _10; - StorageLive(_11); - _11 = const 1_usize; -- _5 = *const [()] from (copy _6, copy _11); -+ _5 = *const [()] from (copy _10, const 1_usize); - StorageDead(_11); ++ _6 = copy _9; + StorageLive(_10); + _10 = const 1_usize; +- _5 = *const [()] from (copy _6, copy _10); ++ _5 = *const [()] from (copy _9, const 1_usize); + StorageDead(_10); StorageDead(_6); StorageLive(_7); StorageLive(_8); _8 = copy _5; - _7 = copy _8 as *mut () (PtrToPtr); -+ _7 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *mut () (Transmute); ++ _7 = copy _11 as *mut () (Transmute); StorageDead(_8); StorageDead(_7); - StorageDead(_5); diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff index 0615f8132af00..86e8749fb0b51 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff @@ -9,8 +9,8 @@ let mut _4: I; + scope 1 (inlined > as FnMut>::call_mut) { + let mut _5: &mut dyn std::ops::FnMut; -+ let mut _6: std::boxed::Box>; -+ let mut _7: *const dyn std::ops::FnMut; ++ let mut _6: *const dyn std::ops::FnMut; ++ let mut _7: std::ptr::NonNull>; + } bb0: { @@ -22,9 +22,9 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _6 = copy (*_3); -+ _7 = copy ((_6.0: std::ptr::Unique>).0: std::ptr::NonNull>) as *const dyn std::ops::FnMut (Transmute); -+ _5 = &mut (*_7); ++ _7 = copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); ++ _6 = copy _7 as *const dyn std::ops::FnMut (Transmute); ++ _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff index 21b20329d4fa9..50136ae8766b2 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff @@ -9,8 +9,8 @@ let mut _4: I; + scope 1 (inlined > as FnMut>::call_mut) { + let mut _5: &mut dyn std::ops::FnMut; -+ let mut _6: std::boxed::Box>; -+ let mut _7: *const dyn std::ops::FnMut; ++ let mut _6: *const dyn std::ops::FnMut; ++ let mut _7: std::ptr::NonNull>; + } bb0: { @@ -22,9 +22,9 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _6 = copy (*_3); -+ _7 = copy ((_6.0: std::ptr::Unique>).0: std::ptr::NonNull>) as *const dyn std::ops::FnMut (Transmute); -+ _5 = &mut (*_7); ++ _7 = copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); ++ _6 = copy _7 as *const dyn std::ops::FnMut (Transmute); ++ _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb4, unwind: bb2]; } diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff index ecea7a97513d5..97a8a99d9805e 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff @@ -9,8 +9,8 @@ let mut _4: (i32,); + scope 1 (inlined as Fn<(i32,)>>::call) { + let mut _5: &dyn std::ops::Fn(i32); -+ let mut _6: std::boxed::Box; -+ let mut _7: *const dyn std::ops::Fn(i32); ++ let mut _6: *const dyn std::ops::Fn(i32); ++ let mut _7: std::ptr::NonNull; + } bb0: { @@ -23,9 +23,9 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _6 = copy (*_3); -+ _7 = copy ((_6.0: std::ptr::Unique).0: std::ptr::NonNull) as *const dyn std::ops::Fn(i32) (Transmute); -+ _5 = &(*_7); ++ _7 = copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _6 = copy _7 as *const dyn std::ops::Fn(i32) (Transmute); ++ _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff index 3a4a528e879fb..3b18052616e2f 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff @@ -9,8 +9,8 @@ let mut _4: (i32,); + scope 1 (inlined as Fn<(i32,)>>::call) { + let mut _5: &dyn std::ops::Fn(i32); -+ let mut _6: std::boxed::Box; -+ let mut _7: *const dyn std::ops::Fn(i32); ++ let mut _6: *const dyn std::ops::Fn(i32); ++ let mut _7: std::ptr::NonNull; + } bb0: { @@ -23,9 +23,9 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _6 = copy (*_3); -+ _7 = copy ((_6.0: std::ptr::Unique).0: std::ptr::NonNull) as *const dyn std::ops::Fn(i32) (Transmute); -+ _5 = &(*_7); ++ _7 = copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _6 = copy _7 as *const dyn std::ops::Fn(i32) (Transmute); ++ _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb4, unwind: bb2]; } diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir index 103475b608cee..688e6967c2f85 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir @@ -8,8 +8,8 @@ fn b(_1: &mut Box) -> &mut T { let mut _4: &mut std::boxed::Box; scope 1 (inlined as AsMut>::as_mut) { debug self => _4; - let mut _5: std::boxed::Box; - let mut _6: *const T; + let mut _5: *const T; + let mut _6: std::ptr::NonNull; } bb0: { @@ -19,9 +19,9 @@ fn b(_1: &mut Box) -> &mut T { _4 = copy _1; StorageLive(_5); StorageLive(_6); - _5 = copy (*_4); - _6 = copy ((_5.0: std::ptr::Unique).0: std::ptr::NonNull) as *const T (Transmute); - _3 = &mut (*_6); + _6 = copy (((*_4).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = copy _6 as *const T (Transmute); + _3 = &mut (*_5); StorageDead(_6); StorageDead(_5); _2 = copy _3; diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir index babb26808cece..663741c62fb84 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir @@ -7,8 +7,8 @@ fn d(_1: &Box) -> &T { let mut _3: &std::boxed::Box; scope 1 (inlined as AsRef>::as_ref) { debug self => _3; - let mut _4: std::boxed::Box; - let mut _5: *const T; + let mut _4: *const T; + let mut _5: std::ptr::NonNull; } bb0: { @@ -17,9 +17,9 @@ fn d(_1: &Box) -> &T { _3 = copy _1; StorageLive(_4); StorageLive(_5); - _4 = copy (*_3); - _5 = copy ((_4.0: std::ptr::Unique).0: std::ptr::NonNull) as *const T (Transmute); - _2 = &(*_5); + _5 = copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); + _4 = copy _5 as *const T (Transmute); + _2 = &(*_4); StorageDead(_5); StorageDead(_4); _0 = copy _2; From 2da55cdb2cfcafa73e6c06eda0535b534c0e8365 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Wed, 13 Aug 2025 18:02:24 -0700 Subject: [PATCH 1552/1900] remove copyforderef from custom_mir it did not create DerefTemp locals when used, so it was never actually correct. --- .../src/builder/custom/parse/instruction.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 - library/core/src/intrinsics/mir.rs | 3 +-- .../projections.copy_for_deref.built.after.mir | 12 ------------ tests/mir-opt/building/custom/projections.rs | 16 ---------------- 5 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 tests/mir-opt/building/custom/projections.copy_for_deref.built.after.mir diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 54490e0050902..e33e83723c102 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -253,7 +253,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), - @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 92dd56f3d5894..dee5bac7c54ea 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1423,7 +1423,6 @@ symbols! { mir_cast_transmute, mir_cast_unsize, mir_checked, - mir_copy_for_deref, mir_debuginfo, mir_deinit, mir_discriminant, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index a800ef1cb9375..85c5a2c5228e1 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -233,7 +233,7 @@ //! //! - Operands implicitly convert to `Use` rvalues. //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue. -//! - [`CopyForDeref`], [`CastTransmute`], [`CastPtrToPtr`], [`CastUnsize`], and [`Discriminant`] +//! - [`CastTransmute`], [`CastPtrToPtr`], [`CastUnsize`], and [`Discriminant`] //! have associated functions. //! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc. //! - The binary operation `Offset` can be created via [`Offset`]. @@ -406,7 +406,6 @@ define!( "mir_ptr_metadata", fn PtrMetadata(place: *const P) ->

::Metadata ); -define!("mir_copy_for_deref", fn CopyForDeref(place: T) -> T); define!("mir_retag", fn Retag(place: T)); define!("mir_move", fn Move(place: T) -> T); define!("mir_static", fn Static(s: T) -> &'static T); diff --git a/tests/mir-opt/building/custom/projections.copy_for_deref.built.after.mir b/tests/mir-opt/building/custom/projections.copy_for_deref.built.after.mir deleted file mode 100644 index b28e96f10eae7..0000000000000 --- a/tests/mir-opt/building/custom/projections.copy_for_deref.built.after.mir +++ /dev/null @@ -1,12 +0,0 @@ -// MIR for `copy_for_deref` after built - -fn copy_for_deref(_1: (&i32, i32)) -> i32 { - let mut _0: i32; - let mut _2: &i32; - - bb0: { - _2 = deref_copy (_1.0: &i32); - _0 = copy (*_2); - return; - } -} diff --git a/tests/mir-opt/building/custom/projections.rs b/tests/mir-opt/building/custom/projections.rs index 0250b9b84b621..e59eebd9952d7 100644 --- a/tests/mir-opt/building/custom/projections.rs +++ b/tests/mir-opt/building/custom/projections.rs @@ -79,19 +79,6 @@ fn simple_index(a: [i32; 10], b: &[i32]) -> i32 { } } -// EMIT_MIR projections.copy_for_deref.built.after.mir -#[custom_mir(dialect = "runtime", phase = "initial")] -fn copy_for_deref(x: (&i32, i32)) -> i32 { - mir! { - let temp: &i32; - { - temp = CopyForDeref(x.0); - RET = *temp; - Return() - } - } -} - fn main() { assert_eq!(unions(U { a: 5 }), 5); assert_eq!(tuples((5, 6)), (5, 6)); @@ -103,7 +90,4 @@ fn main() { assert_eq!(o, Some(10)); assert_eq!(simple_index([0; 10], &[0; 10]), 0); - - let one = 1; - assert_eq!(copy_for_deref((&one, one)), 1); } From 033474bacf62c7ca2c3d113d94422e4ef93ba4c9 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:30:27 -0700 Subject: [PATCH 1553/1900] remove some deref_finder uses elaborate drops and inline don't seem to actually need it --- compiler/rustc_mir_transform/src/elaborate_drops.rs | 2 -- compiler/rustc_mir_transform/src/inline.rs | 3 --- 2 files changed, 5 deletions(-) diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index c4ad29d663010..833a671de1d7d 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -14,7 +14,6 @@ use rustc_mir_dataflow::{ use rustc_span::Span; use tracing::{debug, instrument}; -use crate::deref_separator::deref_finder; use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop}; use crate::patch::MirPatch; @@ -87,7 +86,6 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { .elaborate() }; elaborate_patch.apply(body); - deref_finder(tcx, body, true); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 3c997cfd4fb01..38c7d8370e78d 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -20,7 +20,6 @@ use rustc_span::source_map::Spanned; use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::{CostChecker, is_call_like}; -use crate::deref_separator::deref_finder; use crate::simplify::{UsedInStmtLocals, simplify_cfg}; use crate::validate::validate_types; use crate::{check_inline, util}; @@ -64,7 +63,6 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(tcx, body); - deref_finder(tcx, body, false); } } @@ -100,7 +98,6 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(tcx, body); - deref_finder(tcx, body, false); } } } From 0eaa3366f904e9fde3bd3ada9d2642f23140577d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 12:04:17 +0900 Subject: [PATCH 1554/1900] Improve error message for ambiguous numeric types in closure parameters --- compiler/rustc_hir_typeck/src/method/suggest.rs | 16 ++++++++++++++++ .../ambiguous-numeric-in-closure-ref.fixed | 9 +++++++++ .../ambiguous-numeric-in-closure-ref.rs | 9 +++++++++ .../ambiguous-numeric-in-closure-ref.stderr | 16 ++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.rs create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 44602e6289940..3c3c979c05ec5 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2568,6 +2568,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } + // For closure parameters with reference patterns (e.g., |&v|), suggest the type annotation + // on the pattern itself, e.g., |&v: &i32| + (FileName::Real(_), Node::Pat(pat)) + if let Node::Pat(binding_pat) = self.tcx.hir_node(hir_id) + && let hir::PatKind::Binding(..) = binding_pat.kind + && let Node::Pat(parent_pat) = parent_node + && matches!(parent_pat.kind, hir::PatKind::Ref(..)) => + { + err.span_label(span, "you must specify a type for this binding"); + err.span_suggestion_verbose( + pat.span.shrink_to_hi(), + "specify the type in the closure argument list", + format!(": &{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } _ => { err.span_label(span, msg); } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed new file mode 100644 index 0000000000000..0bb81c7df125d --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed @@ -0,0 +1,9 @@ +// Test for better error message when numeric type is ambiguous in closure parameter with reference + +//@ run-rustfix + +fn main() { + let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &i32 +} diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs new file mode 100644 index 0000000000000..7a6f779ab8329 --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs @@ -0,0 +1,9 @@ +// Test for better error message when numeric type is ambiguous in closure parameter with reference + +//@ run-rustfix + +fn main() { + let _ = (0..10).filter(|&v| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &i32 +} diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr new file mode 100644 index 0000000000000..a2225d940a30f --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr @@ -0,0 +1,16 @@ +error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` + --> $DIR/ambiguous-numeric-in-closure-ref.rs:6:35 + | +LL | let _ = (0..10).filter(|&v| v.pow(2) > 0); + | - ^^^ + | | + | you must specify a type for this binding + | +help: specify the type in the closure argument list + | +LL | let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); + | ++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0689`. From 7f0adce34ace20fb1167498596aa2910cb0f33eb Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 11 Oct 2025 15:55:44 +1100 Subject: [PATCH 1555/1900] ci: Use GNU coreutils to work around a libffi-sys build failure --- src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile | 2 ++ src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile | 2 ++ src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile index 04b46226acfa4..e840113047e6d 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile @@ -2,6 +2,8 @@ FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ + # FIXME(#147556): Use GNU coreutils to work around a libffi-sys build failure. + coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential \ g++ \ make \ ninja-build \ diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile index 095624d6fb714..f5c0f6127a8d1 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile @@ -3,6 +3,8 @@ FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ + # FIXME(#147556): Use GNU coreutils to work around a libffi-sys build failure. + coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential \ bzip2 \ g++ \ make \ diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index 4b61fd94a6cfa..a54ce19f97168 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -2,6 +2,8 @@ FROM ubuntu:25.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ + # FIXME(#147556): Use GNU coreutils to work around a libffi-sys build failure. + coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential \ g++ \ make \ ninja-build \ From 097f2fd2f4e9d7760f00a47638fbd886c83f269d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 16:35:46 +0200 Subject: [PATCH 1556/1900] add missing test for macros The missing external macro test for `non_canonical_clone_impl` was caught thanks to lintcheck -- I went ahead and added all the other combinations of the test while at it --- tests/ui/non_canonical_clone_impl.fixed | 53 ++++++++++---- tests/ui/non_canonical_clone_impl.rs | 56 ++++++++++---- tests/ui/non_canonical_clone_impl.stderr | 14 +++- tests/ui/non_canonical_partial_ord_impl.fixed | 71 ++++++++++++++++++ tests/ui/non_canonical_partial_ord_impl.rs | 73 +++++++++++++++++++ .../ui/non_canonical_partial_ord_impl.stderr | 25 +++++-- 6 files changed, 260 insertions(+), 32 deletions(-) diff --git a/tests/ui/non_canonical_clone_impl.fixed b/tests/ui/non_canonical_clone_impl.fixed index 11616f28825b0..d9be98de69b17 100644 --- a/tests/ui/non_canonical_clone_impl.fixed +++ b/tests/ui/non_canonical_clone_impl.fixed @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -100,18 +100,45 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { *self } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.rs b/tests/ui/non_canonical_clone_impl.rs index 7d101915517fe..3db22bdbcf318 100644 --- a/tests/ui/non_canonical_clone_impl.rs +++ b/tests/ui/non_canonical_clone_impl.rs @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -114,18 +114,48 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { + //~^ non_canonical_clone_impl + todo!() + } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.stderr b/tests/ui/non_canonical_clone_impl.stderr index 5500984527170..cf36a8f49f812 100644 --- a/tests/ui/non_canonical_clone_impl.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -41,5 +41,17 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: aborting due to 4 previous errors +error: non-canonical implementation of `clone` on a `Copy` type + --> tests/ui/non_canonical_clone_impl.rs:127:37 + | +LL | fn clone(&self) -> Self { + | _____________________________________^ +LL | | +LL | | todo!() +LL | | } + | |_____________^ help: change this to: `{ *self }` + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors diff --git a/tests/ui/non_canonical_partial_ord_impl.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed index 6915e1984fb1e..aa23fd99ad77b 100644 --- a/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/tests/ui/non_canonical_partial_ord_impl.fixed @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -163,6 +167,73 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.rs b/tests/ui/non_canonical_partial_ord_impl.rs index 7ce4cdc9aec83..da7f73f7c4bef 100644 --- a/tests/ui/non_canonical_partial_ord_impl.rs +++ b/tests/ui/non_canonical_partial_ord_impl.rs @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -167,6 +171,75 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr index 9bd6b1f726df2..8e55603dd9dac 100644 --- a/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:16:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:20:1 | LL | / impl PartialOrd for A { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:51:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:55:1 | LL | / impl PartialOrd for C { LL | | @@ -32,7 +32,22 @@ LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp | error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:191:9 + | +LL | / impl PartialOrd for A { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________________- +LL | || todo!(); +LL | || } + | ||_____________- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__________^ + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:271:1 | LL | / impl PartialOrd for K { LL | | @@ -45,7 +60,7 @@ LL | | } | |__^ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:216:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:289:1 | LL | / impl PartialOrd for L { LL | | @@ -57,5 +72,5 @@ LL | || } LL | | } | |__^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From f6665478e17b29cb3c0a555a7b8911bd2e878e27 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 16:07:23 +0200 Subject: [PATCH 1557/1900] Cleanup: rename `Applicability` variables/parameters --- clippy_lints/src/loops/utils.rs | 8 ++++---- clippy_lints/src/methods/iter_skip_next.rs | 6 +++--- clippy_lints/src/non_std_lazy_statics.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 7bf7565f73c71..56d535c4f2625 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -256,7 +256,7 @@ fn is_conditional(expr: &Expr<'_>) -> bool { /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. -pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { +pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applicability: &mut Applicability) -> String { let impls_iterator = cx .tcx .get_diagnostic_item(sym::Iterator) @@ -264,7 +264,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -282,12 +282,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applicability).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ), } } diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 10e58b015762e..661188c90ed61 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -12,7 +12,7 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { - let mut application = Applicability::MachineApplicable; + let mut applicability = Applicability::MachineApplicable; span_lint_and_then( cx, ITER_SKIP_NEXT, @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT { - application = Applicability::Unspecified; + applicability = Applicability::Unspecified; diag.span_help( pat.span, format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), @@ -35,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "use `nth` instead", format!(".nth({})", snippet(cx, arg.span, "..")), - application, + applicability, ); }, ); diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index c9c272abf9e3b..61e4d419fdf06 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -220,7 +220,7 @@ impl LazyInfo { fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { // Applicability might get adjusted to `Unspecified` later if any calls // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. - let mut appl = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; for (span, def_id) in &self.calls_span_and_id { @@ -229,7 +229,7 @@ impl LazyInfo { suggs.push((*span, sugg)); } else { // If NO suggested replacement, not machine applicable - appl = Applicability::Unspecified; + app = Applicability::Unspecified; } } @@ -240,7 +240,7 @@ impl LazyInfo { self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { - diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, app); }, ); } From d10ea6eab0062246041d3730f2ccbac268558f48 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 19:13:01 +0200 Subject: [PATCH 1558/1900] Cleanup: rename `EarlyContext`/`LateContext` parameters --- clippy_lints/src/significant_drop_tightening.rs | 6 +++--- clippy_lints/src/single_char_lifetime_names.rs | 6 +++--- clippy_lints_internal/src/produce_ice.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index dcce906499585..c4604fb1558df 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -320,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, apa.first_bind_ident, self.cx) { + if has_drop(self.cx, semi_expr, apa.first_bind_ident) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -417,11 +417,11 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { +fn has_drop(cx: &LateContext<'_>, expr: &hir::Expr<'_>, first_bind_ident: Option) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res - && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) + && cx.tcx.is_diagnostic_item(sym::mem_drop, did) { let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index 8c34da0d14a4d..595c75acf031f 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); impl EarlyLintPass for SingleCharLifetimeNames { - fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { - if param.ident.span.in_external_macro(ctx.sess().source_map()) { + fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &GenericParam) { + if param.ident.span.in_external_macro(cx.sess().source_map()) { return; } @@ -51,7 +51,7 @@ impl EarlyLintPass for SingleCharLifetimeNames { { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( - ctx, + cx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", diff --git a/clippy_lints_internal/src/produce_ice.rs b/clippy_lints_internal/src/produce_ice.rs index 3a813b4b9a223..630843049da25 100644 --- a/clippy_lints_internal/src/produce_ice.rs +++ b/clippy_lints_internal/src/produce_ice.rs @@ -25,9 +25,9 @@ declare_tool_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { if is_trigger_fn(fn_kind) { - ctx.sess() + cx.sess() .dcx() .span_delayed_bug(span, "Would you like some help with that?"); } From 5de7da824c2ddda869be1973c0d468c8ef5a6abb Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 16:08:00 +0200 Subject: [PATCH 1559/1900] New internal lint: `unusual_names` This lint aims at detecting unusual names used in Clippy source code, such as `appl` or `application` for a `rustc_errors::Applicability` variable, instead of `app` and `applicability` which are commonly used throughout Clippy. This helps maintaining the consistency of the Clippy source code. --- clippy_lints_internal/Cargo.toml | 1 + clippy_lints_internal/src/internal_paths.rs | 5 ++ clippy_lints_internal/src/lib.rs | 3 + clippy_lints_internal/src/unusual_names.rs | 99 +++++++++++++++++++++ clippy_utils/src/sym.rs | 9 ++ 5 files changed, 117 insertions(+) create mode 100644 clippy_lints_internal/src/unusual_names.rs diff --git a/clippy_lints_internal/Cargo.toml b/clippy_lints_internal/Cargo.toml index a8293a1ad395d..16b45322e30f1 100644 --- a/clippy_lints_internal/Cargo.toml +++ b/clippy_lints_internal/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } +itertools = "0.12" regex = { version = "1.5" } rustc-semver = "1.1" diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index dc1e30ab2bdd3..95bdf27b019c6 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -2,13 +2,18 @@ use clippy_utils::paths::{PathLookup, PathNS}; use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc +pub static APPLICABILITY: PathLookup = type_path!(rustc_errors::Applicability); +pub static EARLY_CONTEXT: PathLookup = type_path!(rustc_lint::EarlyContext); pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LATE_CONTEXT: PathLookup = type_path!(rustc_lint::LateContext); pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); +#[expect(clippy::unnecessary_def_path, reason = "for uniform checking in internal lint")] +pub static TY_CTXT: PathLookup = type_path!(rustc_middle::ty::TyCtxt); // Paths in clippy itself pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index 43cde86504f5c..d686ba73387cb 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -41,6 +41,7 @@ mod produce_ice; mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; +mod unusual_names; use rustc_lint::{Lint, LintStore}; @@ -59,6 +60,7 @@ static LINTS: &[&Lint] = &[ symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, + unusual_names::UNUSUAL_NAMES, ]; pub fn register_lints(store: &mut LintStore) { @@ -74,4 +76,5 @@ pub fn register_lints(store: &mut LintStore) { store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); + store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); } diff --git a/clippy_lints_internal/src/unusual_names.rs b/clippy_lints_internal/src/unusual_names.rs new file mode 100644 index 0000000000000..e11a2868fb69c --- /dev/null +++ b/clippy_lints_internal/src/unusual_names.rs @@ -0,0 +1,99 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::PathLookup; +use clippy_utils::sym; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; + +use crate::internal_paths::{APPLICABILITY, EARLY_CONTEXT, LATE_CONTEXT, TY_CTXT}; + +declare_tool_lint! { + /// ### What it does + /// Checks if variables of some types use the usual name. + /// + /// ### Why is this bad? + /// Restricting the identifiers used for common things in + /// Clippy sources increases consistency. + /// + /// ### Example + /// Check that an `rustc_errors::Applicability` variable is + /// named either `app` or `applicability`, and not + /// `a` or `appl`. + pub clippy::UNUSUAL_NAMES, + Warn, + "commonly used concepts should use usual same variable name.", + report_in_external_macro: true +} + +declare_lint_pass!(UnusualNames => [UNUSUAL_NAMES]); + +const USUAL_NAMES: [(&PathLookup, &str, &[Symbol]); 4] = [ + ( + &APPLICABILITY, + "rustc_errors::Applicability", + &[sym::app, sym::applicability], + ), + (&EARLY_CONTEXT, "rustc_lint::EarlyContext", &[sym::cx]), + (&LATE_CONTEXT, "rustc_lint::LateContext", &[sym::cx]), + (&TY_CTXT, "rustc_middle::ty::TyCtxt", &[sym::tcx]), +]; + +impl<'tcx> LateLintPass<'tcx> for UnusualNames { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(let_stmt) = stmt.kind + && let Some(init_expr) = let_stmt.init + { + check_pat_name_for_ty(cx, let_stmt.pat, cx.typeck_results().expr_ty(init_expr), "variable"); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + return; + } + for (param, ty) in body + .params + .iter() + .zip(cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()) + { + check_pat_name_for_ty(cx, param.pat, *ty, "parameter"); + } + } +} + +fn check_pat_name_for_ty(cx: &LateContext<'_>, pat: &Pat<'_>, ty: Ty<'_>, kind: &str) { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + let ty = ty.peel_refs(); + for (usual_ty, ty_str, usual_names) in USUAL_NAMES { + if usual_ty.matches_ty(cx, ty) + && !usual_names.contains(&ident.name) + && ident.name != kw::SelfLower + && !ident.name.as_str().starts_with('_') + { + let usual_names = usual_names.iter().map(|name| format!("`{name}`")).join(" or "); + span_lint_and_help( + cx, + UNUSUAL_NAMES, + ident.span, + format!("unusual name for a {kind} of type `{ty_str}`"), + None, + format!("prefer using {usual_names}"), + ); + } + } + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 2b22f344e8c02..a14c783c2e93c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -34,6 +34,7 @@ macro_rules! generate { // // `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { + Applicability, AsyncReadExt, AsyncWriteExt, BACKSLASH_SINGLE_QUOTE: r"\'", @@ -45,10 +46,12 @@ generate! { Current, DOUBLE_QUOTE: "\"", Deserialize, + EarlyContext, EarlyLintPass, IntoIter, Itertools, LF: "\n", + LateContext, Lazy, Lint, LowerExp, @@ -75,7 +78,9 @@ generate! { Weak, abs, ambiguous_glob_reexports, + app, append, + applicability, arg, as_bytes, as_deref, @@ -125,6 +130,7 @@ generate! { count_ones, create, create_new, + cx, cycle, cyclomatic_complexity, de, @@ -289,8 +295,10 @@ generate! { rsplit_terminator, rsplitn, rsplitn_mut, + rustc_errors, rustc_lint, rustc_lint_defs, + rustc_middle, rustc_span, rustfmt_skip, rwlock, @@ -333,6 +341,7 @@ generate! { symbol, take, take_while, + tcx, then, then_some, to_ascii_lowercase, From 12e2542c558133f66896fa4c3cbe07259559309f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 18:20:30 +0200 Subject: [PATCH 1560/1900] Dereference argument of `manual_div_ceil()` if needed --- clippy_lints/src/manual_div_ceil.rs | 13 ++++++++----- tests/ui/manual_div_ceil.fixed | 5 +++++ tests/ui/manual_div_ceil.rs | 5 +++++ tests/ui/manual_div_ceil.stderr | 8 +++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index ed0cce754b954..ee531741a5153 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; @@ -165,6 +164,7 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); + let rhs_ty = cx.typeck_results().expr_ty(rhs); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, @@ -182,7 +182,7 @@ fn build_suggestion( } ) ) { - format!("_{}", cx.typeck_results().expr_ty(rhs)) + format!("_{rhs_ty}") } else { String::new() }; @@ -199,9 +199,12 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); + // Dereference the RHS if it is a reference type + let divisor_snippet = match Sugg::hir_with_context(cx, rhs, expr.span.ctxt(), "_", applicability) { + sugg if rhs_ty.is_ref() => sugg.deref(), + sugg => sugg, + }; span_lint_and_sugg( cx, @@ -209,7 +212,7 @@ fn build_suggestion( expr.span, "manually reimplementing `div_ceil`", "consider using `.div_ceil()`", - sugg, + format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"), *applicability, ); } diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 58ee6978fc125..cd91be87ec175 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = size.div_ceil(*c); + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index aa0d81b22a0e2..9899c7d775c23 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = (size + c - 1) / c; + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 9be5a19bf391f..44de3ba99be78 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -125,5 +125,11 @@ error: manually reimplementing `div_ceil` LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 19 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:105:13 + | +LL | let _ = (size + c - 1) / c; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)` + +error: aborting due to 20 previous errors From 16880f7594eec31aa9f4a1ad41befe2d1ecc4b1d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 1 Oct 2025 22:54:50 +0200 Subject: [PATCH 1561/1900] check earlier for `PartialEq` where `Rhs != Self` --- clippy_lints/src/non_canonical_impls.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 5c1406d782fd6..2ee8ea2a37765 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef, TypeckResults}; +use rustc_middle::ty::{EarlyBinder, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -129,11 +129,14 @@ impl LateLintPass<'_> for NonCanonicalImpls { { check_clone_on_copy(cx, impl_item, block); } else if trait_name == Some(sym::PartialOrd) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() && lhs == rhs && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { - check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + check_partial_ord_on_ord(cx, impl_item, item, body, block); } } } @@ -179,7 +182,6 @@ fn check_partial_ord_on_ord<'tcx>( cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>, item: &Item<'_>, - trait_impl: &TraitRef<'_>, body: &Body<'_>, block: &Block<'tcx>, ) { @@ -205,13 +207,6 @@ fn check_partial_ord_on_ord<'tcx>( { return; } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } span_lint_and_then( cx, From 3c02c0ef206dd3e6a75c4960230c13173c5e0b87 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 21:15:01 +0200 Subject: [PATCH 1562/1900] refactor(non_canonical_impls): lint starting from `impl`s --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/non_canonical_impls.rs | 127 ++++++++++++++++++------ 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 149785c594472..dc706603c50e2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -765,7 +765,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); - store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); + store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))); store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 2ee8ea2a37765..3285023b34aa8 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -3,10 +3,11 @@ use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TypeckResults}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::{EarlyBinder, TyCtxt, TypeckResults}; +use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -107,36 +108,96 @@ declare_clippy_lint! { suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); +impl_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); + +#[expect( + clippy::struct_field_names, + reason = "`_trait` suffix is meaningful on its own, \ + and creating an inner `StoredTraits` struct would just add a level of indirection" +)] +pub(crate) struct NonCanonicalImpls { + partial_ord_trait: Option, + ord_trait: Option, + clone_trait: Option, + copy_trait: Option, +} + +impl NonCanonicalImpls { + pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + let lang_items = tcx.lang_items(); + Self { + partial_ord_trait: lang_items.partial_ord_trait(), + ord_trait: tcx.get_diagnostic_item(sym::Ord), + clone_trait: lang_items.clone_trait(), + copy_trait: lang_items.copy_trait(), + } + } +} + +/// The traits that this lint looks at +enum Trait { + Clone, + PartialOrd, +} impl LateLintPass<'_> for NonCanonicalImpls { - fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind - && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) - && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) - && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) - // NOTE: check this early to avoid expensive checks that come after this one - && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Impl(impl_) = item.kind + // Both `PartialOrd` and `Clone` have one required method, and `PartialOrd` can have 5 methods in total + && (1..=5).contains(&impl_.items.len()) + && let Some(of_trait) = impl_.of_trait + && let Some(trait_did) = of_trait.trait_ref.trait_def_id() + // Check this early to hopefully bail out as soon as possible + && let trait_ = if Some(trait_did) == self.clone_trait { + Trait::Clone + } else if Some(trait_did) == self.partial_ord_trait { + Trait::PartialOrd + } else { + return; + } && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let body = cx.tcx.hir_body(impl_item_id) - && let ExprKind::Block(block, ..) = body.value.kind - && !block.span.in_external_macro(cx.sess().source_map()) - && !is_from_proc_macro(cx, impl_item) { - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - check_clone_on_copy(cx, impl_item, block); - } else if trait_name == Some(sym::PartialOrd) - // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, - // since it doesn't have an `Rhs` - && let [lhs, rhs] = trait_impl.args.as_slice() && lhs == rhs - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - check_partial_ord_on_ord(cx, impl_item, item, body, block); + let mut assoc_fns = impl_ + .items + .iter() + .map(|id| cx.tcx.hir_impl_item(*id)) + .filter_map(|assoc| { + if let ImplItemKind::Fn(_, body_id) = assoc.kind + && let body = cx.tcx.hir_body(body_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + { + Some((assoc, body, block)) + } else { + None + } + }); + + match trait_ { + Trait::Clone => { + if let Some(copy_trait) = self.copy_trait + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && implements_trait(cx, trait_impl.self_ty(), copy_trait, &[]) + { + for (assoc, _, block) in assoc_fns { + check_clone_on_copy(cx, assoc, block); + } + } + }, + Trait::PartialOrd => { + if let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() + && lhs == rhs + && let Some(ord_trait) = self.ord_trait + && implements_trait(cx, trait_impl.self_ty(), ord_trait, &[]) + && let Some((assoc, body, block)) = + assoc_fns.find(|(assoc, _, _)| assoc.ident.name == sym::partial_cmp) + { + check_partial_ord_on_ord(cx, assoc, item, body, block); + } + }, } } } @@ -154,6 +215,10 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B return; } + if is_from_proc_macro(cx, impl_item) { + return; + } + span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -165,7 +230,7 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B ); } - if impl_item.ident.name == sym::clone_from { + if impl_item.ident.name == sym::clone_from && !is_from_proc_macro(cx, impl_item) { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -206,6 +271,8 @@ fn check_partial_ord_on_ord<'tcx>( && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { return; + } else if is_from_proc_macro(cx, impl_item) { + return; } span_lint_and_then( From 85ef0170c4c60b4678d291d3e66f42d299285301 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:13:00 +0200 Subject: [PATCH 1563/1900] book: encourage the use of `clippy_utils::sym` Also, explain how new symbols can be added to this module in order to compare names. --- book/src/development/common_tools_writing_lints.md | 7 +++---- book/src/development/method_checking.md | 11 +++++++---- book/src/development/trait_checking.md | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 7fde4cb408a5e..b5958f802e384 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name.as_str() == "some_method" + && path.ident.name == sym::some_method // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -85,9 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::paths; +use clippy_utils::{paths, sym}; use clippy_utils::res::MaybeDef; -use rustc_span::symbol::sym; use rustc_hir::LangItem; impl LateLintPass<'_> for MyStructLint { @@ -123,8 +122,8 @@ There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index 7819a477f608c..cca6d6ae7bf34 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -15,15 +15,15 @@ the [`ExprKind`] that we can access from `expr.kind`: ```rust use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::sym; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name.as_str() == "our_fancy_method" + && path.ident.name == sym::our_fancy_method // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) @@ -41,6 +41,10 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. +New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sym` module. +This module extends the list of symbols already provided by the compiler crates +in `rustc_span::sym`. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -56,11 +60,10 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust +use clippy_utils::{return_ty, sym}; use clippy_utils::res::MaybeDef; -use clippy_utils::return_ty; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index c6f6f6bd2f99e..d01bb723da619 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -17,10 +17,10 @@ providing the `LateContext` (`cx`), our expression at hand, and the symbol of the trait in question: ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -124,8 +124,8 @@ The following code demonstrates how to do this: ```rust use rustc_middle::ty::Ty; +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use rustc_span::symbol::sym; let ty = todo!("Get the `Foo` type to check for a trait implementation"); let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code From 9027625e1457ad9c97d14ff579cc3bb8743c5971 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:16:52 +0200 Subject: [PATCH 1564/1900] book: use `Type::method` instead of `Type.method` to refer to methods --- book/src/development/macro_expansions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/macro_expansions.md b/book/src/development/macro_expansions.md index ed547130b358e..63a96dc373f31 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -37,7 +37,7 @@ before emitting suggestions to the end user to avoid false positives. Several functions are available for working with macros. -### The `Span.from_expansion` method +### The `Span::from_expansion` method We could utilize a `span`'s [`from_expansion`] method, which detects if the `span` is from a macro expansion / desugaring. @@ -50,7 +50,7 @@ if expr.span.from_expansion() { } ``` -### `Span.ctxt` method +### `Span::ctxt` method The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext], represents if the span is from a macro expansion and, if it is, which From 1ffd092a8075bce16ab13efc8e9a6c700a642621 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:17:26 +0200 Subject: [PATCH 1565/1900] book: import `implements_trait` from `clippy_utils::ty` --- book/src/development/trait_checking.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index d01bb723da619..714607ef25e5f 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -53,7 +53,7 @@ For instance, if we want to examine whether an expression `expr` implements we can check that the `Ty` of the `expr` implements the trait: ```rust -use clippy_utils::implements_trait; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +79,8 @@ If neither diagnostic item nor a language item is available, we can use Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{implements_trait, paths}; +use clippy_utils::paths; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; From bb5b5bce9985bebc9a9a2cf618a8551dc504a1b4 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 00:47:34 +0200 Subject: [PATCH 1566/1900] feat(zero_repeat_side_effects): put the suggestion on two lines --- clippy_lints/src/zero_repeat_side_effects.rs | 18 +++++-- tests/ui/zero_repeat_side_effects.fixed | 43 +++++++++++---- tests/ui/zero_repeat_side_effects.stderr | 54 +++++++++++-------- ...ro_repeat_side_effects_never_pattern.fixed | 3 +- ...o_repeat_side_effects_never_pattern.stderr | 4 +- 5 files changed, 82 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index db54ec023077a..d6b916ffef9d3 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_indent}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -77,24 +77,34 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let indent = snippet_indent(cx, expr.span).unwrap_or_default(); let vec = if is_vec { "vec!" } else { "" }; let (span, sugg) = match parent_hir_node { Node::LetStmt(l) => ( l.span, format!( - "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + "{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];", var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), ), Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( x.span, format!( - "{inner_expr}; {var_name} = {vec}[] as {return_type}", + "{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}", var_name = snippet(cx, l.span.source_callsite(), "..") ), ), - _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + _ => ( + expr.span, + format!( + "\ +{{ +{indent} {inner_expr}; +{indent} {vec}[] as {return_type} +{indent}}}" + ), + ), }; let span = span.source_callsite(); span_lint_and_then( diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index e6c451ce7399e..231ed85ee635c 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -13,36 +13,51 @@ fn main() { // should trigger // on arrays - f(); let a: [i32; 0] = []; + f(); + let a: [i32; 0] = []; //~^ zero_repeat_side_effects let mut b; - f(); b = [] as [i32; 0]; + f(); + b = [] as [i32; 0]; //~^ zero_repeat_side_effects // on vecs // vecs dont support inferring value of consts - f(); let c: std::vec::Vec = vec![]; + f(); + let c: std::vec::Vec = vec![]; //~^ zero_repeat_side_effects let d; - f(); d = vec![] as std::vec::Vec; + f(); + d = vec![] as std::vec::Vec; //~^ zero_repeat_side_effects // for macros - println!("side effect"); let e: [(); 0] = []; + println!("side effect"); + let e: [(); 0] = []; //~^ zero_repeat_side_effects // for nested calls - { f() }; let g: [i32; 0] = []; + { f() }; + let g: [i32; 0] = []; //~^ zero_repeat_side_effects // as function param - drop({ f(); vec![] as std::vec::Vec }); + drop({ + f(); + vec![] as std::vec::Vec + }); //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { f(); vec![] as std::vec::Vec }; + { + f(); + vec![] as std::vec::Vec + }; //~^ zero_repeat_side_effects - { f(); [] as [i32; 0] }; + { + f(); + [] as [i32; 0] + }; //~^ zero_repeat_side_effects // should not trigger @@ -96,8 +111,14 @@ fn issue_14681() { foo(&[Some(0i64); 0]); foo(&[Some(Some(0i64)); 0]); - foo(&{ Some(f()); [] as [std::option::Option; 0] }); + foo(&{ + Some(f()); + [] as [std::option::Option; 0] + }); //~^ zero_repeat_side_effects - foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + foo(&{ + Some(Some(S::new())); + [] as [std::option::Option>; 0] + }); //~^ zero_repeat_side_effects } diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 771b71c686ae4..3f43f15da4a03 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -8,8 +8,8 @@ LL | let a = [f(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let a = [f(); 0]; -LL + f(); let a: [i32; 0] = []; +LL ~ f(); +LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -20,8 +20,8 @@ LL | b = [f(); 0]; | help: consider performing the side effect separately | -LL - b = [f(); 0]; -LL + f(); b = [] as [i32; 0]; +LL ~ f(); +LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -32,8 +32,8 @@ LL | let c = vec![f(); 0]; | help: consider performing the side effect separately | -LL - let c = vec![f(); 0]; -LL + f(); let c: std::vec::Vec = vec![]; +LL ~ f(); +LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -44,8 +44,8 @@ LL | d = vec![f(); 0]; | help: consider performing the side effect separately | -LL - d = vec![f(); 0]; -LL + f(); d = vec![] as std::vec::Vec; +LL ~ f(); +LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -56,8 +56,8 @@ LL | let e = [println!("side effect"); 0]; | help: consider performing the side effect separately | -LL - let e = [println!("side effect"); 0]; -LL + println!("side effect"); let e: [(); 0] = []; +LL ~ println!("side effect"); +LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -68,8 +68,8 @@ LL | let g = [{ f() }; 0]; | help: consider performing the side effect separately | -LL - let g = [{ f() }; 0]; -LL + { f() }; let g: [i32; 0] = []; +LL ~ { f() }; +LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -80,8 +80,10 @@ LL | drop(vec![f(); 0]); | help: consider performing the side effect separately | -LL - drop(vec![f(); 0]); -LL + drop({ f(); vec![] as std::vec::Vec }); +LL ~ drop({ +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer @@ -92,8 +94,10 @@ LL | vec![f(); 0]; | help: consider performing the side effect separately | -LL - vec![f(); 0]; -LL + { f(); vec![] as std::vec::Vec }; +LL ~ { +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -104,8 +108,10 @@ LL | [f(); 0]; | help: consider performing the side effect separately | -LL - [f(); 0]; -LL + { f(); [] as [i32; 0] }; +LL ~ { +LL + f(); +LL + [] as [i32; 0] +LL ~ }; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -116,8 +122,10 @@ LL | foo(&[Some(f()); 0]); | help: consider performing the side effect separately | -LL - foo(&[Some(f()); 0]); -LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); +LL ~ foo(&{ +LL + Some(f()); +LL + [] as [std::option::Option; 0] +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer @@ -128,8 +136,10 @@ LL | foo(&[Some(Some(S::new())); 0]); | help: consider performing the side effect separately | -LL - foo(&[Some(Some(S::new())); 0]); -LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); +LL ~ foo(&{ +LL + Some(Some(S::new())); +LL + [] as [std::option::Option>; 0] +LL ~ }); | error: aborting due to 11 previous errors diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/tests/ui/zero_repeat_side_effects_never_pattern.fixed index 021265dac9844..3d037516f75c8 100644 --- a/tests/ui/zero_repeat_side_effects_never_pattern.fixed +++ b/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -4,6 +4,7 @@ fn issue_14998() { // nameable type thanks to `never_type` being enabled, suggest - panic!(); let _data: [!; 0] = []; + panic!(); + let _data: [!; 0] = []; //~^ zero_repeat_side_effects } diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/tests/ui/zero_repeat_side_effects_never_pattern.stderr index b3d3d2f88f541..280955740cc4c 100644 --- a/tests/ui/zero_repeat_side_effects_never_pattern.stderr +++ b/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -8,8 +8,8 @@ LL | let _data = [panic!(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let _data = [panic!(); 0]; -LL + panic!(); let _data: [!; 0] = []; +LL ~ panic!(); +LL + let _data: [!; 0] = []; | error: aborting due to 1 previous error From 6b076ca80bd62265229c9281ecd0fdfd4b241f1a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 6 Oct 2025 18:24:57 +0200 Subject: [PATCH 1567/1900] feat(zero_repeat_side_effects): don't suggest unnecessary braces around stmts --- clippy_lints/src/zero_repeat_side_effects.rs | 2 ++ tests/ui/zero_repeat_side_effects.fixed | 13 +++----- tests/ui/zero_repeat_side_effects.rs | 1 + tests/ui/zero_repeat_side_effects.stderr | 34 +++++++++----------- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index d6b916ffef9d3..a8351690068df 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -95,6 +95,8 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: var_name = snippet(cx, l.span.source_callsite(), "..") ), ), + // NOTE: don't use the stmt span to avoid touching the trailing semicolon + Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")), _ => ( expr.span, format!( diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index 231ed85ee635c..b5fca36f3f089 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); @@ -49,15 +50,11 @@ fn main() { //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { - f(); - vec![] as std::vec::Vec - }; + f(); + vec![] as std::vec::Vec; //~^ zero_repeat_side_effects - { - f(); - [] as [i32; 0] - }; + f(); + [] as [i32; 0]; //~^ zero_repeat_side_effects // should not trigger diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index f8a497976aa43..ea043d21638cc 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 3f43f15da4a03..49e850d035343 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,5 +1,5 @@ error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:16:5 + --> tests/ui/zero_repeat_side_effects.rs:17:5 | LL | let a = [f(); 0]; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:19:5 + --> tests/ui/zero_repeat_side_effects.rs:20:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:24:5 + --> tests/ui/zero_repeat_side_effects.rs:25:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:27:5 + --> tests/ui/zero_repeat_side_effects.rs:28:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:31:5 + --> tests/ui/zero_repeat_side_effects.rs:32:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:35:5 + --> tests/ui/zero_repeat_side_effects.rs:36:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:39:10 + --> tests/ui/zero_repeat_side_effects.rs:40:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ @@ -87,35 +87,31 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:43:5 + --> tests/ui/zero_repeat_side_effects.rs:44:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL ~ { -LL + f(); -LL + vec![] as std::vec::Vec -LL ~ }; +LL ~ f(); +LL ~ vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:45:5 + --> tests/ui/zero_repeat_side_effects.rs:46:5 | LL | [f(); 0]; | ^^^^^^^^ | help: consider performing the side effect separately | -LL ~ { -LL + f(); -LL + [] as [i32; 0] -LL ~ }; +LL ~ f(); +LL ~ [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:99:10 + --> tests/ui/zero_repeat_side_effects.rs:100:10 | LL | foo(&[Some(f()); 0]); | ^^^^^^^^^^^^^^ @@ -129,7 +125,7 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:101:10 + --> tests/ui/zero_repeat_side_effects.rs:102:10 | LL | foo(&[Some(Some(S::new())); 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 85490d1845d67c026d5f09bef4b796b7b6c8f725 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 21:56:03 +0200 Subject: [PATCH 1568/1900] clean-up --- clippy_lints/src/mutex_atomic.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index a93665ef3e9d6..306f9dd1b8a7f 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -93,18 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(_, subst) = ty.kind() && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - } + let msg = format!( + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`" + ); + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), } } } From 3ae047ee04671f43bac3ee80577618faa0e132fe Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 21:47:25 +0200 Subject: [PATCH 1569/1900] restructure messages - The main message should point out what's wrong, not directly suggest a solution. - The second part of the message is a separate advice, so it should be emitted separately. --- clippy_lints/src/mutex_atomic.rs | 20 +++++++----- tests/ui/mutex_atomic.stderr | 53 +++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 306f9dd1b8a7f..7ff8729fe0605 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use rustc_errors::Diag; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; @@ -96,14 +97,17 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { && let mutex_param = subst.type_at(0) && let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + diag.help(format!("consider using an `{atomic_name}` instead")); + diag.help( + "if you just want the locking behavior and not the internal type, consider using `Mutex<()>`", + ); + }; match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index a6d5d60fbf05b..b328461ff0ced 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,74 +1,105 @@ -error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:7:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ | + = help: consider using an `AtomicBool` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` -error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:10:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicUsize` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:13:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicIsize` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:17:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicPtr` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:20:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicPtr` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:23:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ | + = help: consider using an `AtomicU32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` -error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:26:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:30:5 | LL | Mutex::new(0u8); | ^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicU8` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:33:5 | LL | Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI16` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI8` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:40:5 | LL | Mutex::new(X); | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI64` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: aborting due to 11 previous errors From 38ac3d041c03f6898ea28fddad245c3e29645de3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 22:45:53 +0200 Subject: [PATCH 1570/1900] only lint on definitions, not use --- clippy_lints/src/mutex_atomic.rs | 56 +++++++++++++------- tests/ui/mutex_atomic.rs | 43 ++++++++++----- tests/ui/mutex_atomic.stderr | 91 +++++++++++++++++++++----------- 3 files changed, 126 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 7ff8729fe0605..d096d965ed322 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::Diag; -use rustc_hir::Expr; +use rustc_hir::{Expr, Item, ItemKind, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -89,26 +90,43 @@ declare_clippy_lint! { declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); +// NOTE: we don't use `check_expr` because that would make us lint every _use_ of such mutexes, not +// just their definitions impl<'tcx> LateLintPass<'tcx> for Mutex { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() - && ty.is_diag_item(cx, sym::Mutex) - && let mutex_param = subst.type_at(0) - && let Some(atomic_name) = get_atomic_name(mutex_param) + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !item.span.from_expansion() + && let ItemKind::Static(_, _, ty, body_id) = item.kind { - let msg = "using a `Mutex` where an atomic would do"; - let diag = |diag: &mut Diag<'_, _>| { - diag.help(format!("consider using an `{atomic_name}` instead")); - diag.help( - "if you just want the locking behavior and not the internal type, consider using `Mutex<()>`", - ); - }; - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), - ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), - _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), - } + let body = cx.tcx.hir_body(body_id); + let mid_ty = ty_from_hir_ty(cx, ty); + check_expr(cx, body.value.peel_blocks(), mid_ty); + } + } + fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { + if !stmt.span.from_expansion() + && let Some(init) = stmt.init + { + let mid_ty = cx.typeck_results().expr_ty(init); + check_expr(cx, init.peel_blocks(), mid_ty); + } + } +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if let ty::Adt(_, subst) = ty.kind() + && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) + { + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + diag.help(format!("consider using an `{atomic_name}` instead")); + diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); + }; + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 7db5c9f274f6e..cc53cc3136c63 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -2,47 +2,64 @@ #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] +use std::sync::Mutex; + fn main() { - use std::sync::Mutex; - Mutex::new(true); + let _ = Mutex::new(true); //~^ mutex_atomic - Mutex::new(5usize); + let _ = Mutex::new(5usize); //~^ mutex_atomic - Mutex::new(9isize); + let _ = Mutex::new(9isize); //~^ mutex_atomic let mut x = 4u32; - Mutex::new(&x as *const u32); + let _ = Mutex::new(&x as *const u32); //~^ mutex_atomic - Mutex::new(&mut x as *mut u32); + let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic - Mutex::new(0u32); + let _ = Mutex::new(0u32); //~^ mutex_integer - Mutex::new(0i32); + let _ = Mutex::new(0i32); //~^ mutex_integer - Mutex::new(0f32); // there are no float atomics, so this should not lint - Mutex::new(0u8); + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = Mutex::new(0u8); //~^ mutex_integer - Mutex::new(0i16); + let _ = Mutex::new(0i16); //~^ mutex_integer let _x: Mutex = Mutex::new(0); //~^ mutex_integer const X: i64 = 0; - Mutex::new(X); + let _ = Mutex::new(X); //~^ mutex_integer // there are no 128 atomics, so these two should not lint { - Mutex::new(0u128); + let _ = Mutex::new(0u128); let _x: Mutex = Mutex::new(0); } } + +static MTX: Mutex = Mutex::new(0); +//~^ mutex_integer + +// don't lint on _use_, only declaration +fn issue13378() { + let mut guard = MTX.lock().unwrap(); + *guard += 1; + + let mtx = Mutex::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index b328461ff0ced..839b641c574ad 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,8 +1,8 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:7:5 + --> tests/ui/mutex_atomic.rs:8:13 | -LL | Mutex::new(true); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(true); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicBool` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` @@ -10,46 +10,46 @@ LL | Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:10:5 + --> tests/ui/mutex_atomic.rs:11:13 | -LL | Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicUsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:13:5 + --> tests/ui/mutex_atomic.rs:14:13 | -LL | Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicIsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:17:5 + --> tests/ui/mutex_atomic.rs:18:13 | -LL | Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&x as *const u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:20:5 + --> tests/ui/mutex_atomic.rs:21:13 | -LL | Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:23:5 + --> tests/ui/mutex_atomic.rs:24:13 | -LL | Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` @@ -57,34 +57,34 @@ LL | Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:26:5 + --> tests/ui/mutex_atomic.rs:27:13 | -LL | Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:30:5 + --> tests/ui/mutex_atomic.rs:31:13 | -LL | Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u8); + | ^^^^^^^^^^^^^^^ | = help: consider using an `AtomicU8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:33:5 + --> tests/ui/mutex_atomic.rs:34:13 | -LL | Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0i16); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicI16` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:36:25 + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -93,13 +93,40 @@ LL | let _x: Mutex = Mutex::new(0); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:40:5 + --> tests/ui/mutex_atomic.rs:41:13 | -LL | Mutex::new(X); - | ^^^^^^^^^^^^^ +LL | let _ = Mutex::new(X); + | ^^^^^^^^^^^^^ | = help: consider using an `AtomicI64` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 11 previous errors +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:51:26 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicU32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:59:15 + | +LL | let mtx = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:63:22 + | +LL | let reassigned = mtx; + | ^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: aborting due to 14 previous errors From 99ce6391ddaa2470759443b7dad9ca097d6dc93c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 23:37:53 +0200 Subject: [PATCH 1571/1900] suggest replacing `Mutex::new` with `AtomicX::new` --- clippy_lints/src/mutex_atomic.rs | 19 +++++++-- tests/ui/mutex_atomic.rs | 7 ++-- tests/ui/mutex_atomic.stderr | 69 +++++++++++++------------------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index d096d965ed322..c92bad1393cab 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; -use rustc_errors::Diag; -use rustc_hir::{Expr, Item, ItemKind, LetStmt}; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -120,7 +121,19 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { { let msg = "using a `Mutex` where an atomic would do"; let diag = |diag: &mut Diag<'_, _>| { - diag.help(format!("consider using an `{atomic_name}` instead")); + // if `expr = Mutex::new(arg)`, we can try emitting a suggestion + if let ExprKind::Call(qpath, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_mutex, new)) = qpath.kind + && new.ident.name == sym::new + { + let mut applicability = Applicability::MaybeIncorrect; + let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); + + let suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + diag.multipart_suggestion("try", suggs, applicability); + } else { + diag.help(format!("consider using an `{atomic_name}` instead")); + } diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); }; match *mutex_param.kind() { diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index cc53cc3136c63..331c09ab1a71b 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,3 +1,4 @@ +//@no-rustfix #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] @@ -48,11 +49,11 @@ fn main() { } } -static MTX: Mutex = Mutex::new(0); -//~^ mutex_integer - // don't lint on _use_, only declaration fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + let mut guard = MTX.lock().unwrap(); *guard += 1; diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 839b641c574ad..9afbf72dc67d2 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,126 +1,113 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:9:13 | LL | let _ = Mutex::new(true); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` | - = help: consider using an `AtomicBool` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:12:13 | LL | let _ = Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` | - = help: consider using an `AtomicUsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:15:13 | LL | let _ = Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` | - = help: consider using an `AtomicIsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:18:13 + --> tests/ui/mutex_atomic.rs:19:13 | LL | let _ = Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&x as *const u32)` | - = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:22:13 | LL | let _ = Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` | - = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:25:13 | LL | let _ = Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` | - = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:28:13 | LL | let _ = Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` | - = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:32:13 | LL | let _ = Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` | - = help: consider using an `AtomicU8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:35:13 | LL | let _ = Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` | - = help: consider using an `AtomicI16` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:38:25 | LL | let _x: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI8::new(0)` | - = help: consider using an `AtomicI8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:42:13 | LL | let _ = Mutex::new(X); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` | - = help: consider using an `AtomicI64` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:51:26 + --> tests/ui/mutex_atomic.rs:54:30 | -LL | static MTX: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0)` | - = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:59:15 + --> tests/ui/mutex_atomic.rs:60:15 | LL | let mtx = Mutex::new(0); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` | - = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:63:22 + --> tests/ui/mutex_atomic.rs:64:22 | LL | let reassigned = mtx; | ^^^ From e5fd5714145bd167ac82d306219e8ae65c8f699e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 01:05:38 +0200 Subject: [PATCH 1572/1900] realize that a test case is incorrect `Mutex<*const _>` doesn't make a lot of sense (there can be no contention over a read-only reference), but `AtomicPtr::new(*const _)` straight up doesn't compile --- clippy_lints/src/mutex_atomic.rs | 4 +++- tests/ui/mutex_atomic.rs | 2 +- tests/ui/mutex_atomic.stderr | 10 +--------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index c92bad1393cab..1cb6b901723f3 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -5,6 +5,7 @@ use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -169,7 +170,8 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I128 => None, } }, - ty::RawPtr(_, _) => Some("AtomicPtr"), + // `AtomicPtr` only accepts `*mut T` + ty::RawPtr(_, Mutability::Mut) => Some("AtomicPtr"), _ => None, } } diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 331c09ab1a71b..b907c30818413 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -16,8 +16,8 @@ fn main() { //~^ mutex_atomic let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint let _ = Mutex::new(&x as *const u32); - //~^ mutex_atomic let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 9afbf72dc67d2..9d8a7ccb33bd3 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -24,14 +24,6 @@ LL | let _ = Mutex::new(9isize); | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:19:13 - | -LL | let _ = Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&x as *const u32)` - | - = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:22:13 | @@ -115,5 +107,5 @@ LL | let reassigned = mtx; = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors From 778da589c6357c489ba1b6f3bb022359ca73435c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 00:18:21 +0200 Subject: [PATCH 1573/1900] suggest adjusting the type ascription --- clippy_lints/src/mutex_atomic.rs | 43 ++++++++++++++--- tests/ui/mutex_atomic.fixed | 67 ++++++++++++++++++++++++++ tests/ui/mutex_atomic.rs | 9 ++-- tests/ui/mutex_atomic.stderr | 55 +++++++++++++++------ tests/ui/mutex_atomic_unfixable.rs | 13 +++++ tests/ui/mutex_atomic_unfixable.stderr | 17 +++++++ 6 files changed, 177 insertions(+), 27 deletions(-) create mode 100644 tests/ui/mutex_atomic.fixed create mode 100644 tests/ui/mutex_atomic_unfixable.rs create mode 100644 tests/ui/mutex_atomic_unfixable.stderr diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 1cb6b901723f3..2fef8404f824d 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{self as hir, Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -101,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { { let body = cx.tcx.hir_body(body_id); let mid_ty = ty_from_hir_ty(cx, ty); - check_expr(cx, body.value.peel_blocks(), mid_ty); + check_expr(cx, body.value.peel_blocks(), &TypeAscriptionKind::Required(ty), mid_ty); } } fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { @@ -109,12 +110,26 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { && let Some(init) = stmt.init { let mid_ty = cx.typeck_results().expr_ty(init); - check_expr(cx, init.peel_blocks(), mid_ty); + check_expr(cx, init.peel_blocks(), &TypeAscriptionKind::Optional(stmt.ty), mid_ty); } } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { +/// Whether the type ascription `: Mutex` (which we'll suggest replacing with `AtomicX`) is +/// required +enum TypeAscriptionKind<'tcx> { + /// Yes; for us, this is the case for statics + Required(&'tcx hir::Ty<'tcx>), + /// No; the ascription might've been necessary in an expression like: + /// ```ignore + /// let mutex: Mutex = Mutex::new(0); + /// ``` + /// to specify the type of `0`, but since `AtomicX` already refers to a concrete type, we won't + /// need this ascription anymore. + Optional(Option<&'tcx hir::Ty<'tcx>>), +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &TypeAscriptionKind<'tcx>, ty: Ty<'tcx>) { if let ty::Adt(_, subst) = ty.kind() && ty.is_diag_item(cx, sym::Mutex) && let mutex_param = subst.type_at(0) @@ -129,8 +144,22 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { { let mut applicability = Applicability::MaybeIncorrect; let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); - - let suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + match ty_ascription { + TypeAscriptionKind::Required(ty_ascription) => { + suggs.push((ty_ascription.span, format!("std::sync::atomic::{atomic_name}"))); + }, + TypeAscriptionKind::Optional(Some(ty_ascription)) => { + // See https://github.com/rust-lang/rust-clippy/pull/15386 for why this is + // required + let colon_ascription = (cx.sess().source_map()) + .span_extend_to_prev_char_before(ty_ascription.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + suggs.push((colon_ascription, String::new())); + }, + TypeAscriptionKind::Optional(None) => {}, // nothing to remove/replace + } diag.multipart_suggestion("try", suggs, applicability); } else { diag.help(format!("consider using an `{atomic_name}` instead")); diff --git a/tests/ui/mutex_atomic.fixed b/tests/ui/mutex_atomic.fixed new file mode 100644 index 0000000000000..e4218726019f6 --- /dev/null +++ b/tests/ui/mutex_atomic.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] + +use std::sync::Mutex; + +fn main() { + let _ = std::sync::atomic::AtomicBool::new(true); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicUsize::new(5usize); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicIsize::new(9isize); + //~^ mutex_atomic + + let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); + + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicU32::new(0u32); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI32::new(0i32); + //~^ mutex_integer + + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = std::sync::atomic::AtomicU8::new(0u8); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI16::new(0i16); + //~^ mutex_integer + + let _x = std::sync::atomic::AtomicI8::new(0); + //~^ mutex_integer + + const X: i64 = 0; + let _ = std::sync::atomic::AtomicI64::new(X); + //~^ mutex_integer + + // there are no 128 atomics, so these two should not lint + { + let _ = Mutex::new(0u128); + let _x: Mutex = Mutex::new(0); + } +} + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + //~^ mutex_integer + + let mtx = std::sync::atomic::AtomicI32::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index b907c30818413..95f2b135903f7 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] @@ -54,13 +53,15 @@ fn issue13378() { static MTX: Mutex = Mutex::new(0); //~^ mutex_integer - let mut guard = MTX.lock().unwrap(); - *guard += 1; - let mtx = Mutex::new(0); //~^ mutex_integer // This will still lint, since we're reassigning the mutex to a variable -- oh well. // But realistically something like this won't really come up. let reassigned = mtx; //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx): Mutex = Mutex::new(0); + //~^ mutex_integer } diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 9d8a7ccb33bd3..0afc6d541deab 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:9:13 + --> tests/ui/mutex_atomic.rs:8:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` @@ -9,7 +9,7 @@ LL | let _ = Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:12:13 + --> tests/ui/mutex_atomic.rs:11:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` @@ -17,7 +17,7 @@ LL | let _ = Mutex::new(5usize); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:15:13 + --> tests/ui/mutex_atomic.rs:14:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` @@ -25,7 +25,7 @@ LL | let _ = Mutex::new(9isize); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:22:13 + --> tests/ui/mutex_atomic.rs:21:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` @@ -33,7 +33,7 @@ LL | let _ = Mutex::new(&mut x as *mut u32); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:25:13 + --> tests/ui/mutex_atomic.rs:24:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` @@ -43,7 +43,7 @@ LL | let _ = Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:28:13 + --> tests/ui/mutex_atomic.rs:27:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` @@ -51,7 +51,7 @@ LL | let _ = Mutex::new(0i32); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:32:13 + --> tests/ui/mutex_atomic.rs:31:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` @@ -59,7 +59,7 @@ LL | let _ = Mutex::new(0u8); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:35:13 + --> tests/ui/mutex_atomic.rs:34:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` @@ -67,15 +67,20 @@ LL | let _ = Mutex::new(0i16); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:38:25 + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI8::new(0)` + | ^^^^^^^^^^^^^ | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let _x: Mutex = Mutex::new(0); +LL + let _x = std::sync::atomic::AtomicI8::new(0); + | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:42:13 + --> tests/ui/mutex_atomic.rs:41:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` @@ -83,15 +88,20 @@ LL | let _ = Mutex::new(X); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:54:30 + --> tests/ui/mutex_atomic.rs:53:30 | LL | static MTX: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0)` + | ^^^^^^^^^^^^^ | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:15 + --> tests/ui/mutex_atomic.rs:56:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` @@ -99,7 +109,7 @@ LL | let mtx = Mutex::new(0); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:64:22 + --> tests/ui/mutex_atomic.rs:60:22 | LL | let reassigned = mtx; | ^^^ @@ -107,5 +117,18 @@ LL | let reassigned = mtx; = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 13 previous errors +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:65:35 + | +LL | let (funky_mtx): Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let (funky_mtx): Mutex = Mutex::new(0); +LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/mutex_atomic_unfixable.rs b/tests/ui/mutex_atomic_unfixable.rs new file mode 100644 index 0000000000000..0c04f48cf8a9e --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] + +use std::sync::Mutex; + +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + // unfixable because we don't fix this `lock` + let mut guard = MTX.lock().unwrap(); + *guard += 1; +} diff --git a/tests/ui/mutex_atomic_unfixable.stderr b/tests/ui/mutex_atomic_unfixable.stderr new file mode 100644 index 0000000000000..27ffb1304c695 --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.stderr @@ -0,0 +1,17 @@ +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic_unfixable.rs:7:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + = note: `-D clippy::mutex-integer` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: aborting due to 1 previous error + From dbc7327748d6dc190c594c8343dd53e8e60ca49c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 21:17:17 +0900 Subject: [PATCH 1574/1900] Fix suggestions for nested refs and muts --- .../rustc_hir_typeck/src/method/suggest.rs | 24 ++++++++++++++++++- .../ambiguous-numeric-in-closure-ref.fixed | 5 ++++ .../ambiguous-numeric-in-closure-ref.rs | 5 ++++ .../ambiguous-numeric-in-closure-ref.stderr | 15 +++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 3c3c979c05ec5..a2ec7075fac94 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2577,10 +2577,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && matches!(parent_pat.kind, hir::PatKind::Ref(..)) => { err.span_label(span, "you must specify a type for this binding"); + + let mut ref_muts = Vec::new(); + let mut current_node = parent_node; + + while let Node::Pat(parent_pat) = current_node { + if let hir::PatKind::Ref(_, mutability) = parent_pat.kind { + ref_muts.push(mutability); + current_node = self.tcx.parent_hir_node(parent_pat.hir_id); + } else { + break; + } + } + + let mut type_annotation = String::new(); + for mutability in ref_muts.iter().rev() { + match mutability { + hir::Mutability::Mut => type_annotation.push_str("&mut "), + hir::Mutability::Not => type_annotation.push('&'), + } + } + type_annotation.push_str(&concrete_type); + err.span_suggestion_verbose( pat.span.shrink_to_hi(), "specify the type in the closure argument list", - format!(": &{concrete_type}"), + format!(": {type_annotation}"), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed index 0bb81c7df125d..683d581d034b1 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed @@ -6,4 +6,9 @@ fn main() { let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` //~| SUGGESTION &i32 + + let v = vec![0, 1, 2]; + let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &&i32 } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs index 7a6f779ab8329..eab309c701d69 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs @@ -6,4 +6,9 @@ fn main() { let _ = (0..10).filter(|&v| v.pow(2) > 0); //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` //~| SUGGESTION &i32 + + let v = vec![0, 1, 2]; + let _ = v.iter().filter(|&&v| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &&i32 } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr index a2225d940a30f..0789581abea80 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr @@ -11,6 +11,19 @@ help: specify the type in the closure argument list LL | let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); | ++++++ -error: aborting due to 1 previous error +error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` + --> $DIR/ambiguous-numeric-in-closure-ref.rs:11:37 + | +LL | let _ = v.iter().filter(|&&v| v.pow(2) > 0); + | - ^^^ + | | + | you must specify a type for this binding + | +help: specify the type in the closure argument list + | +LL | let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0); + | +++++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0689`. From 632e759497ec9ec93b96c863479c8512f9511650 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 21:32:47 +0900 Subject: [PATCH 1575/1900] Add FIXME about FileName check's fragility --- compiler/rustc_hir_typeck/src/method/suggest.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a2ec7075fac94..e1073de49d305 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2547,6 +2547,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "you must specify a type for this binding, like `{concrete_type}`", ); + // FIXME: Maybe FileName::Anon should also be handled, + // otherwise there would be no suggestion if the source is STDIN for example. match (filename, parent_node) { ( FileName::Real(_), From 04da682613b1849893cff7cb31eda4429ff5633f Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:42:26 -0500 Subject: [PATCH 1576/1900] vexos: implement `pal::os::exit` --- library/std/src/sys/pal/vexos/mod.rs | 1 - library/std/src/sys/pal/vexos/os.rs | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 library/std/src/sys/pal/vexos/os.rs diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 61a34b0f68a30..3a37c8f2d4885 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -1,4 +1,3 @@ -#[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs new file mode 100644 index 0000000000000..405f7c918f4a5 --- /dev/null +++ b/library/std/src/sys/pal/vexos/os.rs @@ -0,0 +1,19 @@ +#[expect(dead_code)] +#[path = "../unsupported/os.rs"] +mod unsupported_os; +pub use unsupported_os::{ + JoinPathsError, SplitPaths, chdir, current_exe, errno, error_string, getcwd, getpid, home_dir, + join_paths, split_paths, temp_dir, +}; + +pub use super::unsupported; + +pub fn exit(_code: i32) -> ! { + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } +} From 5620c82e5344b42e99c317a550bdd1be878c5267 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 10 Aug 2024 19:46:48 +0000 Subject: [PATCH 1577/1900] Report uninhabited call return types on MIR. --- compiler/rustc_mir_build/messages.ftl | 5 ++ .../rustc_mir_build/src/builder/expr/into.rs | 13 +--- compiler/rustc_mir_build/src/builder/mod.rs | 76 +++++++++++++++++++ compiler/rustc_mir_build/src/errors.rs | 12 +++ compiler/rustc_passes/messages.ftl | 5 -- compiler/rustc_passes/src/errors.rs | 12 --- compiler/rustc_passes/src/liveness.rs | 49 +----------- tests/ui/enum-discriminant/issue-46519.rs | 1 + tests/ui/enum-discriminant/issue-46519.stderr | 18 +++++ .../intrinsics/panic-uninitialized-zeroed.rs | 2 +- tests/ui/lint/dead-code/issue-85071-2.rs | 2 +- tests/ui/lint/dead-code/issue-85071-2.stderr | 33 ++++---- tests/ui/lint/dead-code/issue-85071.rs | 2 +- tests/ui/lint/dead-code/issue-85071.stderr | 33 ++++---- 14 files changed, 150 insertions(+), 113 deletions(-) create mode 100644 tests/ui/enum-discriminant/issue-46519.stderr diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index e84e42b8a44b5..4a741169443dd 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -372,6 +372,11 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_union_pattern = cannot use unions in constant patterns .label = can't use a `union` here +mir_build_unreachable_due_to_uninhabited = unreachable {$descr} + .label = unreachable {$descr} + .label_orig = any code following this expression is unreachable + .note = this expression has type `{$ty}`, which is uninhabited + mir_build_unreachable_making_this_unreachable = collectively making this unreachable mir_build_unreachable_making_this_unreachable_n_more = ...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 7676b720e3579..bb65ef28bdc8c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -390,18 +390,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { args, unwind: UnwindAction::Continue, destination, - // The presence or absence of a return edge affects control-flow sensitive - // MIR checks and ultimately whether code is accepted or not. We can only - // omit the return edge if a return type is visibly uninhabited to a module - // that makes the call. - target: expr - .ty - .is_inhabited_from( - this.tcx, - this.parent_module, - this.infcx.typing_env(this.param_env), - ) - .then_some(success), + target: Some(success), call_source: if from_hir_call { CallSource::Normal } else { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 7ca94e655fb20..c0b5d3bb8f8b6 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -24,10 +24,12 @@ use rustc_middle::mir::*; use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; +use rustc_session::lint; use rustc_span::{Span, Symbol, sym}; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::DropKind; +use crate::errors; pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( tcx: TyCtxt<'tcx>, @@ -531,6 +533,7 @@ fn construct_fn<'tcx>( return_block.unit() }); + builder.lint_and_remove_uninhabited(); let mut body = builder.finish(); body.spread_arg = if abi == ExternAbi::RustCall { @@ -588,6 +591,7 @@ fn construct_const<'a, 'tcx>( builder.build_drop_trees(); + builder.lint_and_remove_uninhabited(); builder.finish() } @@ -806,6 +810,78 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap(); } + fn lint_and_remove_uninhabited(&mut self) { + let mut lints = vec![]; + + for bbdata in self.cfg.basic_blocks.iter_mut() { + let term = bbdata.terminator_mut(); + let TerminatorKind::Call { ref mut target, destination, .. } = term.kind else { + continue; + }; + let Some(target_bb) = *target else { continue }; + + let ty = destination.ty(&self.local_decls, self.tcx).ty; + let ty_is_inhabited = ty.is_inhabited_from( + self.tcx, + self.parent_module, + self.infcx.typing_env(self.param_env), + ); + + if !ty_is_inhabited { + // Unreachable code warnings are already emitted during type checking. + // However, during type checking, full type information is being + // calculated but not yet available, so the check for diverging + // expressions due to uninhabited result types is pretty crude and + // only checks whether ty.is_never(). Here, we have full type + // information available and can issue warnings for less obviously + // uninhabited types (e.g. empty enums). The check above is used so + // that we do not emit the same warning twice if the uninhabited type + // is indeed `!`. + if !ty.is_never() { + lints.push((target_bb, ty, term.source_info.span)); + } + + // The presence or absence of a return edge affects control-flow sensitive + // MIR checks and ultimately whether code is accepted or not. We can only + // omit the return edge if a return type is visibly uninhabited to a module + // that makes the call. + *target = None; + } + } + + for (target_bb, orig_ty, orig_span) in lints { + if orig_span.in_external_macro(self.tcx.sess.source_map()) { + continue; + } + let target_bb = &self.cfg.basic_blocks[target_bb]; + let (target_loc, descr) = target_bb + .statements + .iter() + .find_map(|stmt| match stmt.kind { + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => None, + StatementKind::FakeRead(..) => Some((stmt.source_info, "definition")), + _ => Some((stmt.source_info, "expression")), + }) + .unwrap_or_else(|| (target_bb.terminator().source_info, "expression")); + let lint_root = self.source_scopes[target_loc.scope] + .local_data + .as_ref() + .unwrap_crate_local() + .lint_root; + self.tcx.emit_node_span_lint( + lint::builtin::UNREACHABLE_CODE, + lint_root, + target_loc.span, + errors::UnreachableDueToUninhabited { + expr: target_loc.span, + orig: orig_span, + descr, + ty: orig_ty, + }, + ); + } + } + fn finish(self) -> Body<'tcx> { let mut body = Body::new( MirSource::item(self.def_id.to_def_id()), diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e2aae5b6283f2..3d9753d72da58 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -720,6 +720,18 @@ pub(crate) struct WantedConstant { pub(crate) const_path: String, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unreachable_due_to_uninhabited)] +pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { + pub descr: &'desc str, + #[label] + pub expr: Span, + #[label(mir_build_label_orig)] + #[note] + pub orig: Span, + pub ty: Ty<'tcx>, +} + #[derive(Diagnostic)] #[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 9971f0c3fa32e..be9843988669a 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -619,11 +619,6 @@ passes_unnecessary_partial_stable_feature = the feature `{$feature}` has been pa passes_unnecessary_stable_feature = the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable -passes_unreachable_due_to_uninhabited = unreachable {$descr} - .label = unreachable {$descr} - .label_orig = any code following this expression is unreachable - .note = this expression has type `{$ty}`, which is uninhabited - passes_unrecognized_argument = unrecognized argument diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index e01e9e384c035..8ca0641c4dd25 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1308,18 +1308,6 @@ pub(crate) struct ProcMacroBadSig { pub kind: ProcMacroKind, } -#[derive(LintDiagnostic)] -#[diag(passes_unreachable_due_to_uninhabited)] -pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { - pub descr: &'desc str, - #[label] - pub expr: Span, - #[label(passes_label_orig)] - #[note] - pub orig: Span, - pub ty: Ty<'tcx>, -} - #[derive(LintDiagnostic)] #[diag(passes_unused_var_maybe_capture_ref)] #[help] diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index b628ff8a12dfd..e175ac1c035e1 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -96,7 +96,7 @@ use rustc_index::IndexVec; use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; +use rustc_middle::ty::{self, RootVariableMinCaptureList, TyCtxt}; use rustc_session::lint; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, Span, Symbol}; @@ -1317,52 +1317,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn check_is_ty_uninhabited(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode { let ty = self.typeck_results.expr_ty(expr); let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id(); - if ty.is_inhabited_from(self.ir.tcx, m, self.typing_env) { - return succ; - } - match self.ir.lnks[succ] { - LiveNodeKind::ExprNode(succ_span, succ_id) => { - self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "expression"); - } - LiveNodeKind::VarDefNode(succ_span, succ_id) => { - self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "definition"); - } - _ => {} - }; - self.exit_ln - } - - fn warn_about_unreachable<'desc>( - &mut self, - orig_span: Span, - orig_ty: Ty<'tcx>, - expr_span: Span, - expr_id: HirId, - descr: &'desc str, - ) { - if !orig_ty.is_never() { - // Unreachable code warnings are already emitted during type checking. - // However, during type checking, full type information is being - // calculated but not yet available, so the check for diverging - // expressions due to uninhabited result types is pretty crude and - // only checks whether ty.is_never(). Here, we have full type - // information available and can issue warnings for less obviously - // uninhabited types (e.g. empty enums). The check above is used so - // that we do not emit the same warning twice if the uninhabited type - // is indeed `!`. - - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNREACHABLE_CODE, - expr_id, - expr_span, - errors::UnreachableDueToUninhabited { - expr: expr_span, - orig: orig_span, - descr, - ty: orig_ty, - }, - ); - } + if ty.is_inhabited_from(self.ir.tcx, m, self.typing_env) { succ } else { self.exit_ln } } } diff --git a/tests/ui/enum-discriminant/issue-46519.rs b/tests/ui/enum-discriminant/issue-46519.rs index e5f0138c95cee..bad568ca6c5a7 100644 --- a/tests/ui/enum-discriminant/issue-46519.rs +++ b/tests/ui/enum-discriminant/issue-46519.rs @@ -7,6 +7,7 @@ #[should_panic(expected = "creating inhabited type")] fn test() { FontLanguageOverride::system_font(SystemFont::new()); + //~^ WARNING unreachable expression } pub enum FontLanguageOverride { diff --git a/tests/ui/enum-discriminant/issue-46519.stderr b/tests/ui/enum-discriminant/issue-46519.stderr new file mode 100644 index 0000000000000..5e4f536a59a7b --- /dev/null +++ b/tests/ui/enum-discriminant/issue-46519.stderr @@ -0,0 +1,18 @@ +warning: unreachable expression + --> $DIR/issue-46519.rs:9:5 + | +LL | FontLanguageOverride::system_font(SystemFont::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------^ + | | | + | | any code following this expression is unreachable + | unreachable expression + | +note: this expression has type `SystemFont`, which is uninhabited + --> $DIR/issue-46519.rs:9:39 + | +LL | FontLanguageOverride::system_font(SystemFont::new()); + | ^^^^^^^^^^^^^^^^^ + = note: `#[warn(unreachable_code)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index db2b4a6ad20a7..00fb2b029f9aa 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -7,7 +7,7 @@ //@ ignore-backends: gcc //@ edition:2024 -#![allow(deprecated, invalid_value)] +#![allow(deprecated, invalid_value, unreachable_code)] #![feature(never_type, rustc_private)] use std::{ diff --git a/tests/ui/lint/dead-code/issue-85071-2.rs b/tests/ui/lint/dead-code/issue-85071-2.rs index 5db8735899410..06bbcac737397 100644 --- a/tests/ui/lint/dead-code/issue-85071-2.rs +++ b/tests/ui/lint/dead-code/issue-85071-2.rs @@ -17,6 +17,6 @@ fn main() { let s = S; let x = s.f(); //~^ WARNING: unused variable: `x` + //~| WARNING: unreachable definition let _y = x; - //~^ WARNING: unreachable definition } diff --git a/tests/ui/lint/dead-code/issue-85071-2.stderr b/tests/ui/lint/dead-code/issue-85071-2.stderr index 5e963183d094b..ab88d72359695 100644 --- a/tests/ui/lint/dead-code/issue-85071-2.stderr +++ b/tests/ui/lint/dead-code/issue-85071-2.stderr @@ -1,34 +1,33 @@ -warning: unreachable definition - --> $DIR/issue-85071-2.rs:20:9 +warning: unused variable: `x` + --> $DIR/issue-85071-2.rs:18:9 | LL | let x = s.f(); - | ----- any code following this expression is unreachable -LL | -LL | let _y = x; - | ^^ unreachable definition - | -note: this expression has type `Foo`, which is uninhabited - --> $DIR/issue-85071-2.rs:18:13 + | ^ help: if this is intentional, prefix it with an underscore: `_x` | -LL | let x = s.f(); - | ^^^^^ note: the lint level is defined here - --> $DIR/issue-85071-2.rs:7:26 + --> $DIR/issue-85071-2.rs:7:9 | LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ -warning: unused variable: `x` +warning: unreachable definition --> $DIR/issue-85071-2.rs:18:9 | LL | let x = s.f(); - | ^ help: if this is intentional, prefix it with an underscore: `_x` + | ^ ----- any code following this expression is unreachable + | | + | unreachable definition + | +note: this expression has type `Foo`, which is uninhabited + --> $DIR/issue-85071-2.rs:18:13 | +LL | let x = s.f(); + | ^^^^^ note: the lint level is defined here - --> $DIR/issue-85071-2.rs:7:9 + --> $DIR/issue-85071-2.rs:7:26 | LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ warning: 2 warnings emitted diff --git a/tests/ui/lint/dead-code/issue-85071.rs b/tests/ui/lint/dead-code/issue-85071.rs index 84f2c9fc74eeb..6f177905b5968 100644 --- a/tests/ui/lint/dead-code/issue-85071.rs +++ b/tests/ui/lint/dead-code/issue-85071.rs @@ -14,6 +14,6 @@ fn f() -> Foo {todo!()} fn main() { let x = f(); //~^ WARNING: unused variable: `x` + //~| WARNING: unreachable definition let _ = x; - //~^ WARNING: unreachable expression } diff --git a/tests/ui/lint/dead-code/issue-85071.stderr b/tests/ui/lint/dead-code/issue-85071.stderr index 721fb8148d96b..c94923063903a 100644 --- a/tests/ui/lint/dead-code/issue-85071.stderr +++ b/tests/ui/lint/dead-code/issue-85071.stderr @@ -1,34 +1,33 @@ -warning: unreachable expression - --> $DIR/issue-85071.rs:17:13 +warning: unused variable: `x` + --> $DIR/issue-85071.rs:15:9 | LL | let x = f(); - | --- any code following this expression is unreachable -LL | -LL | let _ = x; - | ^ unreachable expression - | -note: this expression has type `Foo`, which is uninhabited - --> $DIR/issue-85071.rs:15:13 + | ^ help: if this is intentional, prefix it with an underscore: `_x` | -LL | let x = f(); - | ^^^ note: the lint level is defined here - --> $DIR/issue-85071.rs:9:26 + --> $DIR/issue-85071.rs:9:9 | LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ -warning: unused variable: `x` +warning: unreachable definition --> $DIR/issue-85071.rs:15:9 | LL | let x = f(); - | ^ help: if this is intentional, prefix it with an underscore: `_x` + | ^ --- any code following this expression is unreachable + | | + | unreachable definition + | +note: this expression has type `Foo`, which is uninhabited + --> $DIR/issue-85071.rs:15:13 | +LL | let x = f(); + | ^^^ note: the lint level is defined here - --> $DIR/issue-85071.rs:9:9 + --> $DIR/issue-85071.rs:9:26 | LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ warning: 2 warnings emitted From aacf9a612a9d3d3745ed6714574589e8e54b55fa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 21 Jun 2025 14:07:06 +0000 Subject: [PATCH 1578/1900] Add test for uninhabited `std::mem::uninitialized` uses. --- tests/ui/uninhabited/void-branch.rs | 30 +++++++++++++++++++ tests/ui/uninhabited/void-branch.stderr | 40 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/ui/uninhabited/void-branch.rs create mode 100644 tests/ui/uninhabited/void-branch.stderr diff --git a/tests/ui/uninhabited/void-branch.rs b/tests/ui/uninhabited/void-branch.rs new file mode 100644 index 0000000000000..e000f946dc156 --- /dev/null +++ b/tests/ui/uninhabited/void-branch.rs @@ -0,0 +1,30 @@ +#![deny(unreachable_code)] +#![allow(deprecated, invalid_value)] + +enum Void {} + +fn with_void() { + if false { + unsafe { + //~^ ERROR unreachable expression + std::mem::uninitialized::(); + } + } + + println!(); +} + +fn infallible() -> std::convert::Infallible { + loop {} +} + +fn with_infallible() { + if false { + //~^ ERROR unreachable expression + infallible(); + } + + println!() +} + +fn main() {} diff --git a/tests/ui/uninhabited/void-branch.stderr b/tests/ui/uninhabited/void-branch.stderr new file mode 100644 index 0000000000000..7bbcb763ddf7c --- /dev/null +++ b/tests/ui/uninhabited/void-branch.stderr @@ -0,0 +1,40 @@ +error: unreachable expression + --> $DIR/void-branch.rs:8:9 + | +LL | / unsafe { +LL | | +LL | | std::mem::uninitialized::(); + | | --------------------------------- any code following this expression is unreachable +LL | | } + | |_________^ unreachable expression + | +note: this expression has type `Void`, which is uninhabited + --> $DIR/void-branch.rs:10:13 + | +LL | std::mem::uninitialized::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/void-branch.rs:1:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: unreachable expression + --> $DIR/void-branch.rs:22:14 + | +LL | if false { + | ______________^ +LL | | +LL | | infallible(); + | | ------------ any code following this expression is unreachable +LL | | } + | |_____^ unreachable expression + | +note: this expression has type `Infallible`, which is uninhabited + --> $DIR/void-branch.rs:24:9 + | +LL | infallible(); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 9df200386006f3cddca7d1664fc7e8438b7d0419 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 6 Sep 2022 18:58:52 +0200 Subject: [PATCH 1579/1900] Record for each MIR local where it has been introduced. --- .../src/diagnostics/move_errors.rs | 4 +--- .../src/diagnostics/mutability_errors.rs | 4 ++-- compiler/rustc_middle/src/mir/mod.rs | 18 ++++++------------ compiler/rustc_middle/src/thir.rs | 1 + compiler/rustc_mir_build/src/builder/block.rs | 1 + .../src/builder/matches/match_pair.rs | 3 ++- .../rustc_mir_build/src/builder/matches/mod.rs | 12 ++++++++++++ compiler/rustc_mir_build/src/builder/mod.rs | 1 + .../rustc_mir_build/src/thir/pattern/mod.rs | 11 ++++++++--- compiler/rustc_mir_build/src/thir/print.rs | 3 ++- .../ui/thir-print/thir-tree-loop-match.stdout | 1 + tests/ui/thir-print/thir-tree-match.stdout | 1 + 12 files changed, 38 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 0b3151fd8b82d..be2a5b1ebe22d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -139,9 +139,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // whether or not the right-hand side is a place expression if let LocalInfo::User(BindingForm::Var(VarBindingForm { opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, + .. })) = *local_decl.local_info() { let stmt_source_info = self.body.source_info(location); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 727cf19cd8bb3..6b5af15febea0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -335,8 +335,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { binding_mode: BindingMode(ByRef::No, Mutability::Not), opt_ty_info: Some(sp), - opt_match_place: _, - pat_span: _, + .. })) => { if suggest { err.span_note(sp, "the binding is already a mutable borrow"); @@ -750,6 +749,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info: _, opt_match_place: _, pat_span, + introductions: _, })) => pat_span, _ => local_decl.source_info.span, }; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 8eb7aa71fcdea..668265b8fd083 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -882,6 +882,8 @@ pub struct VarBindingForm<'tcx> { pub opt_match_place: Option<(Option>, Span)>, /// The span of the pattern in which this variable was bound. pub pat_span: Span, + /// For each introduction place, record here the span and whether this was a shorthand pattern. + pub introductions: Vec<(Span, /* is_shorthand */ bool)>, } #[derive(Clone, Debug, TyEncodable, TyDecodable)] @@ -1088,12 +1090,8 @@ impl<'tcx> LocalDecl<'tcx> { matches!( self.local_info(), LocalInfo::User( - BindingForm::Var(VarBindingForm { - binding_mode: BindingMode(ByRef::No, _), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - }) | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), + BindingForm::Var(VarBindingForm { binding_mode: BindingMode(ByRef::No, _), .. }) + | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), ) ) } @@ -1105,12 +1103,8 @@ impl<'tcx> LocalDecl<'tcx> { matches!( self.local_info(), LocalInfo::User( - BindingForm::Var(VarBindingForm { - binding_mode: BindingMode(ByRef::No, _), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - }) | BindingForm::ImplicitSelf(_), + BindingForm::Var(VarBindingForm { binding_mode: BindingMode(ByRef::No, _), .. }) + | BindingForm::ImplicitSelf(_), ) ) } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index eed27345b747b..0ae4f13f3ca94 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -798,6 +798,7 @@ pub enum PatKind<'tcx> { /// (The same binding can occur multiple times in different branches of /// an or-pattern, but only one of them will be primary.) is_primary: bool, + is_shorthand: bool, }, /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index 566d89f4269c0..cc99d2b42eebc 100644 --- a/compiler/rustc_mir_build/src/builder/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -286,6 +286,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, node, span, + false, OutsideGuard, ScheduleDrops::Yes, ); diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 7a848536d0e33..82c769eb3d584 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -170,7 +170,7 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::Binding { mode, var, ref subpattern, .. } => { + PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { // In order to please the borrow checker, when lowering a pattern // like `x @ subpat` we must establish any bindings in `subpat` // before establishing the binding for `x`. @@ -209,6 +209,7 @@ impl<'tcx> MatchPairTree<'tcx> { source, var_id: var, binding_mode: mode, + is_shorthand, })); } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d216c4ecd1155..d3727e0c9a145 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -579,6 +579,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, var, irrefutable_pat.span, + false, OutsideGuard, ScheduleDrops::Yes, ); @@ -608,6 +609,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, var, irrefutable_pat.span, + false, OutsideGuard, ScheduleDrops::Yes, ); @@ -799,6 +801,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, var: LocalVarId, span: Span, + is_shorthand: bool, for_guard: ForGuard, schedule_drop: ScheduleDrops, ) -> Place<'tcx> { @@ -812,6 +815,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { self.schedule_drop(span, region_scope, local_id, DropKind::Storage); } + let local_info = self.local_decls[local_id].local_info.as_mut().unwrap_crate_local(); + if let LocalInfo::User(BindingForm::Var(var_info)) = &mut **local_info { + var_info.introductions.push((span, is_shorthand)); + } Place::from(local_id) } @@ -1217,6 +1224,7 @@ struct Binding<'tcx> { source: Place<'tcx>, var_id: LocalVarId, binding_mode: BindingMode, + is_shorthand: bool, } /// Indicates that the type of `source` must be a subtype of the @@ -2725,6 +2733,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, binding.var_id, binding.span, + binding.is_shorthand, RefWithinGuard, ScheduleDrops::Yes, ); @@ -2742,6 +2751,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, binding.var_id, binding.span, + binding.is_shorthand, OutsideGuard, ScheduleDrops::Yes, ); @@ -2775,6 +2785,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, binding.var_id, binding.span, + binding.is_shorthand, OutsideGuard, schedule_drops, ); @@ -2827,6 +2838,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_ty_info: None, opt_match_place, pat_span, + introductions: Vec::new(), }, )))), }; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index c0b5d3bb8f8b6..dbc0441a66d9b 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -1054,6 +1054,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_ty_info: param.ty_span, opt_match_place: Some((None, span)), pat_span: span, + introductions: vec![(span, false)], })) }; self.var_indices.insert(var, LocalsForNode::One(local)); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 166e64a5fcb85..dc6b958904497 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -369,6 +369,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ty: var_ty, subpattern: self.lower_opt_pattern(sub), is_primary: id == pat.hir_id, + is_shorthand: false, } } @@ -386,9 +387,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, pat.hir_id); let subpatterns = fields .iter() - .map(|field| FieldPat { - field: self.typeck_results.field_index(field.hir_id), - pattern: *self.lower_pattern(field.pat), + .map(|field| { + let mut pattern = *self.lower_pattern(field.pat); + if let PatKind::Binding { ref mut is_shorthand, .. } = pattern.kind { + *is_shorthand = field.is_shorthand; + } + let field = self.typeck_results.field_index(field.hir_id); + FieldPat { field, pattern } }) .collect(); diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 188f179669cb4..e0a5c18a2eedb 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -705,13 +705,14 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 3); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Binding { name, mode, var, ty, subpattern, is_primary } => { + PatKind::Binding { name, mode, var, ty, subpattern, is_primary, is_shorthand } => { print_indented!(self, "Binding {", depth_lvl + 1); print_indented!(self, format!("name: {:?}", name), depth_lvl + 2); print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2); print_indented!(self, format!("var: {:?}", var), depth_lvl + 2); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 2); print_indented!(self, format!("is_primary: {:?}", is_primary), depth_lvl + 2); + print_indented!(self, format!("is_shorthand: {:?}", is_shorthand), depth_lvl + 2); if let Some(subpattern) = subpattern { print_indented!(self, "subpattern: Some( ", depth_lvl + 2); diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index f2b63782c596c..c9117c3048b03 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -16,6 +16,7 @@ params: [ var: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2)) ty: bool is_primary: true + is_shorthand: false subpattern: None } } diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 2049c531abd99..fde5003176679 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -16,6 +16,7 @@ params: [ var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2)) ty: Foo is_primary: true + is_shorthand: false subpattern: None } } From f2535764d42eb44c99998f800344ce0a2bc8f678 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 21 Jun 2025 12:44:19 +0000 Subject: [PATCH 1580/1900] Introduce VarBindingIntroduction. --- compiler/rustc_middle/src/mir/mod.rs | 13 +++++++++++-- compiler/rustc_middle/src/thir.rs | 1 + compiler/rustc_mir_build/src/builder/matches/mod.rs | 2 +- compiler/rustc_mir_build/src/builder/mod.rs | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 668265b8fd083..afc5b9992e28c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -882,8 +882,9 @@ pub struct VarBindingForm<'tcx> { pub opt_match_place: Option<(Option>, Span)>, /// The span of the pattern in which this variable was bound. pub pat_span: Span, - /// For each introduction place, record here the span and whether this was a shorthand pattern. - pub introductions: Vec<(Span, /* is_shorthand */ bool)>, + /// A binding can be introduced multiple times, with or patterns: + /// `Foo::A { x } | Foo::B { z: x }`. This stores information for each of those introductions. + pub introductions: Vec, } #[derive(Clone, Debug, TyEncodable, TyDecodable)] @@ -896,6 +897,14 @@ pub enum BindingForm<'tcx> { RefForGuard, } +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct VarBindingIntroduction { + /// Where this additional introduction happened. + pub span: Span, + /// Is that introduction a shorthand struct pattern, i.e. `Foo { x }`. + pub is_shorthand: bool, +} + mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_query_system::ich::StableHashingContext; diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 0ae4f13f3ca94..e72ed78d07e6a 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -798,6 +798,7 @@ pub enum PatKind<'tcx> { /// (The same binding can occur multiple times in different branches of /// an or-pattern, but only one of them will be primary.) is_primary: bool, + /// Is this binding a shorthand struct pattern, i.e. `Foo { a }`? is_shorthand: bool, }, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d3727e0c9a145..c4b4ee448dce1 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -817,7 +817,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let local_info = self.local_decls[local_id].local_info.as_mut().unwrap_crate_local(); if let LocalInfo::User(BindingForm::Var(var_info)) = &mut **local_info { - var_info.introductions.push((span, is_shorthand)); + var_info.introductions.push(VarBindingIntroduction { span, is_shorthand }); } Place::from(local_id) } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index dbc0441a66d9b..f2c153578433f 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -1054,7 +1054,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_ty_info: param.ty_span, opt_match_place: Some((None, span)), pat_span: span, - introductions: vec![(span, false)], + introductions: vec![VarBindingIntroduction { + span, + is_shorthand: false, + }], })) }; self.var_indices.insert(var, LocalsForNode::One(local)); From 89191084d5c3c8fa9fed9b70e744c8fd6b066e03 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 Jun 2025 18:17:32 +0000 Subject: [PATCH 1581/1900] Correct binding scope when building MIR. --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index c4b4ee448dce1..165fb4b6517d5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -741,6 +741,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern, &ProjectedUserTypesNode::None, &mut |this, name, mode, var, span, ty, user_tys| { + let saved_scope = this.source_scope; + this.set_correct_source_scope_for_arg(var.0, saved_scope, span); let vis_scope = *visibility_scope .get_or_insert_with(|| this.new_source_scope(scope_span, LintLevel::Inherited)); let source_info = SourceInfo { span, scope: this.source_scope }; @@ -758,6 +760,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, ); + this.source_scope = saved_scope; }, ); if let Some(guard_expr) = guard { From 96b70fc3a13697bdf6dfb7a0a1fc3338d6bfb37d Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 26 Aug 2025 22:38:56 +0000 Subject: [PATCH 1582/1900] Always compile hir_id_validator. Making this compilation conditional causes an unused crate dependency warning. --- compiler/rustc_passes/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 2ad0b5ff60e84..06a127577d432 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -23,7 +23,6 @@ mod debugger_visualizer; mod diagnostic_items; pub mod entry; mod errors; -#[cfg(debug_assertions)] pub mod hir_id_validator; pub mod input_stats; mod lang_items; From ca0379d6cdbb17abc3afe1ac1cbf869626d047df Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 Jun 2025 18:17:09 +0000 Subject: [PATCH 1583/1900] Diagnose liveness on MIR. --- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 6 +- compiler/rustc_middle/src/mir/statement.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 6 +- compiler/rustc_middle/src/ty/closure.rs | 5 + .../src/builder/matches/mod.rs | 2 +- compiler/rustc_mir_build/src/builder/mod.rs | 5 - compiler/rustc_mir_transform/messages.ftl | 27 + compiler/rustc_mir_transform/src/errors.rs | 110 +- compiler/rustc_mir_transform/src/lib.rs | 4 + compiler/rustc_mir_transform/src/liveness.rs | 1328 ++++++++++++ compiler/rustc_passes/messages.ftl | 35 - compiler/rustc_passes/src/errors.rs | 153 +- compiler/rustc_passes/src/lib.rs | 2 - compiler/rustc_passes/src/liveness.rs | 1853 ----------------- .../rustc_passes/src/liveness/rwu_table.rs | 146 -- .../clippy/tests/ui/needless_match.fixed | 2 +- src/tools/clippy/tests/ui/needless_match.rs | 2 +- .../clippy/tests/ui/useless_conversion.fixed | 2 +- .../clippy/tests/ui/useless_conversion.rs | 2 +- tests/ui/asm/x86_64/goto.rs | 1 + tests/ui/asm/x86_64/goto.stderr | 11 +- .../async-closures/precise-captures.rs | 1 + .../borrowck/borrowck-assign-to-subfield.rs | 1 + .../diagnostics/liveness.rs | 48 +- .../diagnostics/liveness.stderr | 115 +- .../liveness_unintentional_copy.rs | 24 +- .../liveness_unintentional_copy.stderr | 85 +- ...ture-pattern-closure-within-closure.stderr | 16 +- .../closures/moved-upvar-mut-rebind-11958.rs | 5 +- .../moved-upvar-mut-rebind-11958.stderr | 23 +- .../defaults/trait_object_lt_defaults.rs | 2 +- tests/ui/drop/or-pattern-drop-order.rs | 2 +- tests/ui/dropck/dropck-empty-array.rs | 3 +- ...-type-meant-to-be-arg-of-mut-borrow.stderr | 50 +- tests/ui/issues/issue-19367.rs | 3 + tests/ui/lint/dead-code/issue-85071-2.stderr | 24 +- tests/ui/lint/dead-code/issue-85071.stderr | 24 +- .../ui/lint/future-incompat-json-test.stderr | 2 +- ...orthand-field-patterns-in-pattern-macro.rs | 1 + ...and-field-patterns-in-pattern-macro.stderr | 11 + .../lint-uppercase-variables.stderr | 21 +- .../expect_lint_from_macro.stderr | 20 +- ...force_warn_expected_lints_fulfilled.stderr | 12 +- .../lint/unused/issue-117284-arg-in-macro.rs | 4 +- .../unused/issue-117284-arg-in-macro.stderr | 7 +- ...0-unused-variable-in-struct-pattern.stderr | 68 +- .../issue-54180-unused-ref-field.stderr | 12 +- .../lint/unused/lint-unused-variables.stderr | 22 +- tests/ui/liveness/liveness-asm.stderr | 8 +- tests/ui/liveness/liveness-consts.rs | 6 +- tests/ui/liveness/liveness-consts.stderr | 42 +- tests/ui/liveness/liveness-dead.stderr | 12 +- tests/ui/liveness/liveness-unused.rs | 113 +- tests/ui/liveness/liveness-unused.stderr | 199 +- tests/ui/liveness/liveness-upvars.rs | 47 +- tests/ui/liveness/liveness-upvars.stderr | 147 +- ...ject-lifetime-default-default-to-static.rs | 2 +- ...object-lifetime-default-from-ref-struct.rs | 2 +- .../object-lifetime-default-from-rptr-box.rs | 2 +- .../object-lifetime-default-from-rptr-mut.rs | 2 +- ...bject-lifetime-default-from-rptr-struct.rs | 2 +- .../object-lifetime-default-from-rptr.rs | 2 +- .../object-lifetime-default-inferred.rs | 2 +- tests/ui/or-patterns/binding-typo-2.stderr | 114 +- tests/ui/packed/packed-struct-drop-aligned.rs | 2 + tests/ui/parser/intersection-patterns-1.fixed | 1 + tests/ui/parser/intersection-patterns-1.rs | 1 + .../ui/parser/intersection-patterns-1.stderr | 4 +- .../pattern/bindings-after-at/bind-by-copy.rs | 5 + .../bindings-after-at/bind-by-copy.stderr | 39 + ...arly-return-with-unreachable-code-24353.rs | 1 + ...-return-with-unreachable-code-24353.stderr | 10 + .../rfc-2005-default-binding-mode/general.rs | 1 + .../general.stderr | 11 + .../std-panic-locations.default.stderr | 11 + .../std-panic-locations.mir-opt.stderr | 11 + .../std-panic-locations.rs | 1 + .../param-attrs-cfg.stderr | 24 +- .../ui/suggestions/try-removing-the-field.rs | 4 +- .../suggestions/try-removing-the-field.stderr | 8 +- .../unused-closure-argument.stderr | 12 +- tests/ui/type/issue-100584.stderr | 4 +- tests/ui/type/type-ascription.rs | 5 +- .../unboxed-closures-counter-not-moved.rs | 5 +- .../unboxed-closures-counter-not-moved.stderr | 24 +- .../unboxed-closures-move-mutable.rs | 10 +- .../unboxed-closures-move-mutable.stderr | 42 +- 88 files changed, 2540 insertions(+), 2709 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/liveness.rs delete mode 100644 compiler/rustc_passes/src/liveness.rs delete mode 100644 compiler/rustc_passes/src/liveness/rwu_table.rs create mode 100644 tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr create mode 100644 tests/ui/pattern/bindings-after-at/bind-by-copy.stderr create mode 100644 tests/ui/return/early-return-with-unreachable-code-24353.stderr create mode 100644 tests/ui/rfcs/rfc-2005-default-binding-mode/general.stderr create mode 100644 tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.default.stderr create mode 100644 tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.mir-opt.stderr diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index c1bba0b01975e..a41d6b858795c 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1095,6 +1095,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.ensure_ok().check_transmutes(def_id); } tcx.ensure_ok().has_ffi_unwind_calls(def_id); + tcx.ensure_ok().check_liveness(def_id); // If we need to codegen, ensure that we emit all errors from // `mir_drops_elaborated_and_const_checked` now, to avoid discovering diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index afc5b9992e28c..f2d2d567beee4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -894,7 +894,7 @@ pub enum BindingForm<'tcx> { /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. ImplicitSelf(ImplicitSelfKind), /// Reference used in a guard expression to ensure immutability. - RefForGuard, + RefForGuard(Local), } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -917,7 +917,7 @@ mod binding_form_impl { match self { Var(binding) => binding.hash_stable(hcx, hasher), ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), - RefForGuard => (), + RefForGuard(local) => local.hash_stable(hcx, hasher), } } } @@ -1129,7 +1129,7 @@ impl<'tcx> LocalDecl<'tcx> { /// expression that is used to access said variable for the guard of the /// match arm. pub fn is_ref_for_guard(&self) -> bool { - matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard)) + matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard(_))) } /// Returns `Some` if this is a reference to a static item that is used to diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 74c39afbbc823..91a528678901b 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -259,7 +259,7 @@ impl<'tcx> PlaceTy<'tcx> { impl ProjectionElem { /// Returns `true` if the target of this projection may refer to a different region of memory /// than the base. - fn is_indirect(&self) -> bool { + pub fn is_indirect(&self) -> bool { match self { Self::Deref => true, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 895c8c0295a04..c6dfcb20e5605 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1195,8 +1195,10 @@ rustc_queries! { desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } } - query check_liveness(key: LocalDefId) { - desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key) } + query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet { + arena_cache + desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } } /// Return the live symbols in the crate for dead code check. diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index b8c7d6cf3b134..ce6f87668beff 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -327,6 +327,11 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc ) } }, + HirProjectionKind::UnwrapUnsafeBinder => { + curr_string = format!("unwrap_binder!({curr_string})"); + } + // Just change the type to the hidden type, so we can actually project. + HirProjectionKind::OpaqueCast => {} proj => bug!("{:?} unexpected because it isn't captured", proj), } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 165fb4b6517d5..926a3eb018ff0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2864,7 +2864,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: None, source_info, local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User( - BindingForm::RefForGuard, + BindingForm::RefForGuard(for_arm_body), ))), }); if self.should_emit_debug_info_for_binding(name, var_id) { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index f2c153578433f..7ce2abaf804a9 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -69,11 +69,6 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { } }; - // Checking liveness after building the THIR ensures there were no typeck errors. - // - // maybe move the check to a MIR pass? - tcx.ensure_ok().check_liveness(def); - // Don't steal here, instead steal in unsafeck. This is so that // pattern inline constants can be evaluated as part of building the // THIR of the parent function without a cycle. diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 2e08f50e8a9aa..e8d29c414edca 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -34,12 +34,16 @@ mir_transform_force_inline_attr = mir_transform_force_inline_justification = `{$callee}` is required to be inlined to: {$sym} +mir_transform_maybe_string_interpolation = you might have meant to use string interpolation in this string literal + mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be .label = the value is held across this suspend point .note = {$reason} .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point mir_transform_operation_will_panic = this operation will panic at runtime +mir_transform_string_interpolation_only_works = string interpolation only works in `format!` invocations + mir_transform_tail_expr_drop_order = relative drop order changing in Rust 2024 .temporaries = in Rust 2024, this temporary value will be dropped first .observers = in Rust 2024, this local variable or temporary value will be dropped second @@ -77,3 +81,26 @@ mir_transform_unconditional_recursion = function cannot return without recursing mir_transform_unconditional_recursion_call_site_label = recursive call site mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored + +mir_transform_unused_assign = value assigned to `{$name}` is never read + .help = maybe it is overwritten before being read? + +mir_transform_unused_assign_passed = value passed to `{$name}` is never read + .help = maybe it is overwritten before being read? + +mir_transform_unused_assign_suggestion = + you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding + +mir_transform_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read + .help = did you mean to capture by reference instead? + +mir_transform_unused_var_assigned_only = variable `{$name}` is assigned to, but never used + .note = consider using `_{$name}` instead + +mir_transform_unused_var_underscore = if this is intentional, prefix it with an underscore + +mir_transform_unused_variable = unused variable: `{$name}` + +mir_transform_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable + +mir_transform_unused_variable_try_ignore = try ignoring the field diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 775f5f9a7cf10..87430b8225b61 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::codes::*; -use rustc_errors::{Diag, LintDiagnostic}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintDiagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; use rustc_middle::query::Key; @@ -158,6 +158,114 @@ pub(crate) struct FnItemRef { pub ident: Ident, } +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_capture_maybe_capture_ref)] +#[help] +pub(crate) struct UnusedCaptureMaybeCaptureRef { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_var_assigned_only)] +#[note] +pub(crate) struct UnusedVarAssignedOnly { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_assign)] +pub(crate) struct UnusedAssign { + pub name: Symbol, + #[subdiagnostic] + pub suggestion: Option, + #[help] + pub help: bool, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(mir_transform_unused_assign_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct UnusedAssignSuggestion { + pub pre: &'static str, + #[suggestion_part(code = "{pre}mut ")] + pub ty_span: Option, + #[suggestion_part(code = "")] + pub ty_ref_span: Span, + #[suggestion_part(code = "*")] + pub pre_lhs_span: Span, + #[suggestion_part(code = "")] + pub rhs_borrow_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_assign_passed)] +#[help] +pub(crate) struct UnusedAssignPassed { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_variable)] +pub(crate) struct UnusedVariable { + pub name: Symbol, + #[subdiagnostic] + pub string_interp: Vec, + #[subdiagnostic] + pub sugg: UnusedVariableSugg, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnusedVariableSugg { + #[multipart_suggestion( + mir_transform_unused_variable_try_ignore, + applicability = "machine-applicable" + )] + TryIgnore { + #[suggestion_part(code = "{name}: _")] + shorthands: Vec, + #[suggestion_part(code = "_")] + non_shorthands: Vec, + name: Symbol, + }, + + #[multipart_suggestion( + mir_transform_unused_var_underscore, + applicability = "machine-applicable" + )] + TryPrefix { + #[suggestion_part(code = "_{name}")] + spans: Vec, + name: Symbol, + }, + + #[help(mir_transform_unused_variable_args_in_macro)] + NoSugg { + #[primary_span] + span: Span, + name: Symbol, + }, +} + +pub(crate) struct UnusedVariableStringInterp { + pub lit: Span, +} + +impl Subdiagnostic for UnusedVariableStringInterp { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + diag.span_label( + self.lit, + crate::fluent_generated::mir_transform_maybe_string_interpolation, + ); + diag.multipart_suggestion( + crate::fluent_generated::mir_transform_string_interpolation_only_works, + vec![ + (self.lit.shrink_to_lo(), String::from("format!(")), + (self.lit.shrink_to_hi(), String::from(")")), + ], + Applicability::MachineApplicable, + ); + } +} + pub(crate) struct MustNotSupend<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, pub yield_sp: Span, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f4f61c80a220a..fb1036c282845 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -51,6 +51,7 @@ mod errors; mod ffi_unwind_calls; mod lint; mod lint_tail_expr_drop_order; +mod liveness; mod patch; mod shim; mod ssa; @@ -215,6 +216,7 @@ pub fn provide(providers: &mut Providers) { mir_for_ctfe, mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses, optimized_mir, + check_liveness: liveness::check_liveness, is_mir_available, is_ctfe_mir_available: is_mir_available, mir_callgraph_cyclic: inline::cycle::mir_callgraph_cyclic, @@ -512,6 +514,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & } } + tcx.ensure_done().check_liveness(def); + let (body, _) = tcx.mir_promoted(def); let mut body = body.steal(); diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs new file mode 100644 index 0000000000000..83b50e7be61b1 --- /dev/null +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -0,0 +1,1328 @@ +use rustc_abi::FieldIdx; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; +use rustc_index::IndexVec; +use rustc_index::bit_set::DenseBitSet; +use rustc_middle::bug; +use rustc_middle::mir::visit::{ + MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, +}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_mir_dataflow::fmt::DebugWithContext; +use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor}; +use rustc_session::lint; +use rustc_span::Span; +use rustc_span::symbol::{Symbol, kw, sym}; + +use crate::errors; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum AccessKind { + Param, + Assign, + Capture, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum CaptureKind { + Closure(ty::ClosureKind), + Coroutine, + CoroutineClosure, + None, +} + +#[derive(Copy, Clone, Debug)] +struct Access { + /// Describe the current access. + kind: AccessKind, + /// Is the accessed place is live at the current statement? + /// When we encounter multiple statements at the same location, we only increase the liveness, + /// in order to avoid false positives. + live: bool, +} + +#[tracing::instrument(level = "debug", skip(tcx), ret)] +pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> DenseBitSet { + // Don't run on synthetic MIR, as that will ICE trying to access HIR. + if tcx.is_synthetic_mir(def_id) { + return DenseBitSet::new_empty(0); + } + + // Don't run unused pass for intrinsics + if tcx.intrinsic(def_id.to_def_id()).is_some() { + return DenseBitSet::new_empty(0); + } + + // Don't run unused pass for #[naked] + if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) { + return DenseBitSet::new_empty(0); + } + + // Don't run unused pass for #[derive] + let parent = tcx.parent(tcx.typeck_root_def_id(def_id.to_def_id())); + if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent) + && find_attr!(tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) + { + return DenseBitSet::new_empty(0); + } + + let mut body = &*tcx.mir_promoted(def_id).0.borrow(); + let mut body_mem; + + // Don't run if there are errors. + if body.tainted_by_errors.is_some() { + return DenseBitSet::new_empty(0); + } + + let mut checked_places = PlaceSet::default(); + checked_places.insert_locals(&body.local_decls); + + // The body is the one of a closure or generator, so we also want to analyse captures. + let (capture_kind, num_captures) = if tcx.is_closure_like(def_id.to_def_id()) { + let mut self_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; + let mut self_is_ref = false; + if let ty::Ref(_, ty, _) = self_ty.kind() { + self_ty = *ty; + self_is_ref = true; + } + + let (capture_kind, args) = match self_ty.kind() { + ty::Closure(_, args) => { + (CaptureKind::Closure(args.as_closure().kind()), ty::UpvarArgs::Closure(args)) + } + &ty::Coroutine(_, args) => (CaptureKind::Coroutine, ty::UpvarArgs::Coroutine(args)), + &ty::CoroutineClosure(_, args) => { + (CaptureKind::CoroutineClosure, ty::UpvarArgs::CoroutineClosure(args)) + } + _ => bug!("expected closure or generator, found {:?}", self_ty), + }; + + let captures = tcx.closure_captures(def_id); + checked_places.insert_captures(tcx, self_is_ref, captures, args.upvar_tys()); + + // `FnMut` closures can modify captured values and carry those + // modified values with them in subsequent calls. To model this behaviour, + // we consider the `FnMut` closure as jumping to `bb0` upon return. + if let CaptureKind::Closure(ty::ClosureKind::FnMut) = capture_kind { + // FIXME: stop cloning the body. + body_mem = body.clone(); + for bbdata in body_mem.basic_blocks_mut() { + // We can call a closure again, either after a normal return or an unwind. + if let TerminatorKind::Return | TerminatorKind::UnwindResume = + bbdata.terminator().kind + { + bbdata.terminator_mut().kind = TerminatorKind::Goto { target: START_BLOCK }; + } + } + body = &body_mem; + } + + (capture_kind, args.upvar_tys().len()) + } else { + (CaptureKind::None, 0) + }; + + // Get the remaining variables' names from debuginfo. + checked_places.record_debuginfo(&body.var_debug_info); + + let self_assignment = find_self_assignments(&checked_places, body); + + let mut live = + MaybeLivePlaces { tcx, capture_kind, checked_places: &checked_places, self_assignment } + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + + let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id()); + + let mut assignments = + AssignmentResult::find_dead_assignments(tcx, typing_env, &checked_places, &mut live, body); + + assignments.merge_guards(); + + let dead_captures = assignments.compute_dead_captures(num_captures); + + assignments.report_fully_unused(); + assignments.report_unused_assignments(); + + dead_captures +} + +/// Small helper to make semantics easier to read. +#[inline] +fn is_capture(place: PlaceRef<'_>) -> bool { + if !place.projection.is_empty() { + debug_assert_eq!(place.local, ty::CAPTURE_STRUCT_LOCAL); + true + } else { + false + } +} + +/// Give a diagnostic when any of the string constants look like a naked format string that would +/// interpolate our dead local. +fn maybe_suggest_literal_matching_name( + body: &Body<'_>, + name: Symbol, +) -> Vec { + struct LiteralFinder<'body, 'tcx> { + body: &'body Body<'tcx>, + name: String, + name_colon: String, + found: Vec, + } + + impl<'tcx> Visitor<'tcx> for LiteralFinder<'_, 'tcx> { + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, loc: Location) { + if let ty::Ref(_, ref_ty, _) = constant.ty().kind() + && ref_ty.kind() == &ty::Str + { + let rendered_constant = constant.const_.to_string(); + if rendered_constant.contains(&self.name) + || rendered_constant.contains(&self.name_colon) + { + let lit = self.body.source_info(loc).span; + self.found.push(errors::UnusedVariableStringInterp { lit }); + } + } + } + } + + let mut finder = LiteralFinder { + body, + name: format!("{{{name}}}"), + name_colon: format!("{{{name}:"), + found: vec![], + }; + finder.visit_body(body); + finder.found +} + +/// Return whether we should consider the current place as a drop guard and skip reporting. +fn maybe_drop_guard<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + index: PlaceIndex, + ever_dropped: &DenseBitSet, + checked_places: &PlaceSet<'tcx>, + body: &Body<'tcx>, +) -> bool { + if ever_dropped.contains(index) { + let ty = checked_places.places[index].ty(&body.local_decls, tcx).ty; + matches!( + ty.kind(), + ty::Closure(..) + | ty::Coroutine(..) + | ty::Tuple(..) + | ty::Adt(..) + | ty::Dynamic(..) + | ty::Array(..) + | ty::Slice(..) + | ty::Alias(ty::Opaque, ..) + ) && ty.needs_drop(tcx, typing_env) + } else { + false + } +} + +/// Detect the following case +/// +/// ```text +/// fn change_object(mut a: &Ty) { +/// let a = Ty::new(); +/// b = &a; +/// } +/// ``` +/// +/// where the user likely meant to modify the value behind there reference, use `a` as an out +/// parameter, instead of mutating the local binding. When encountering this we suggest: +/// +/// ```text +/// fn change_object(a: &'_ mut Ty) { +/// let a = Ty::new(); +/// *b = a; +/// } +/// ``` +fn annotate_mut_binding_to_immutable_binding<'tcx>( + tcx: TyCtxt<'tcx>, + place: PlaceRef<'tcx>, + body_def_id: LocalDefId, + assignment_span: Span, + body: &Body<'tcx>, +) -> Option { + use rustc_hir as hir; + use rustc_hir::intravisit::{self, Visitor}; + + // Verify we have a mutable argument... + let local = place.as_local()?; + let LocalKind::Arg = body.local_kind(local) else { return None }; + let Mutability::Mut = body.local_decls[local].mutability else { return None }; + + // ... with reference type... + let hir_param_index = + local.as_usize() - if tcx.is_closure_like(body_def_id.to_def_id()) { 2 } else { 1 }; + let fn_decl = tcx.hir_node_by_def_id(body_def_id).fn_decl()?; + let ty = fn_decl.inputs[hir_param_index]; + let hir::TyKind::Ref(lt, mut_ty) = ty.kind else { return None }; + + // ... as a binding pattern. + let hir_body = tcx.hir_maybe_body_owned_by(body_def_id)?; + let param = hir_body.params[hir_param_index]; + let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = param.pat.kind else { + return None; + }; + + // Find the assignment to modify. + let mut finder = ExprFinder { assignment_span, lhs: None, rhs: None }; + finder.visit_body(hir_body); + let lhs = finder.lhs?; + let rhs = finder.rhs?; + + let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _mut, inner) = rhs.kind else { return None }; + + // Changes to the parameter's type. + let pre = if lt.ident.span.is_empty() { "" } else { " " }; + let ty_span = if mut_ty.mutbl.is_mut() { + // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). + None + } else { + // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` + Some(mut_ty.ty.span.shrink_to_lo()) + }; + + return Some(errors::UnusedAssignSuggestion { + ty_span, + pre, + // Span of the `mut` before the binding. + ty_ref_span: param.pat.span.until(ident.span), + // Where to add a `*`. + pre_lhs_span: lhs.span.shrink_to_lo(), + // Where to remove the borrow. + rhs_borrow_span: rhs.span.until(inner.span), + }); + + #[derive(Debug)] + struct ExprFinder<'hir> { + assignment_span: Span, + lhs: Option<&'hir hir::Expr<'hir>>, + rhs: Option<&'hir hir::Expr<'hir>>, + } + impl<'hir> Visitor<'hir> for ExprFinder<'hir> { + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if expr.span == self.assignment_span + && let hir::ExprKind::Assign(lhs, rhs, _) = expr.kind + { + self.lhs = Some(lhs); + self.rhs = Some(rhs); + } else { + intravisit::walk_expr(self, expr) + } + } + } +} + +/// Compute self-assignments of the form `a += b`. +/// +/// MIR building generates 2 statements and 1 terminator for such assignments: +/// - _temp = CheckedBinaryOp(a, b) +/// - assert(!_temp.1) +/// - a = _temp.0 +/// +/// This function tries to detect this pattern in order to avoid marking statement as a definition +/// and use. This will let the analysis be dictated by the next use of `a`. +/// +/// Note that we will still need to account for the use of `b`. +fn find_self_assignments<'tcx>( + checked_places: &PlaceSet<'tcx>, + body: &Body<'tcx>, +) -> FxHashSet { + let mut self_assign = FxHashSet::default(); + + const FIELD_0: FieldIdx = FieldIdx::from_u32(0); + const FIELD_1: FieldIdx = FieldIdx::from_u32(1); + + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { + for (statement_index, stmt) in bb_data.statements.iter().enumerate() { + let StatementKind::Assign(box (first_place, rvalue)) = &stmt.kind else { continue }; + match rvalue { + // For checked binary ops, the MIR builder inserts an assertion in between. + Rvalue::BinaryOp( + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow, + box (Operand::Copy(lhs), _), + ) => { + // Checked binary ops only appear at the end of the block, before the assertion. + if statement_index + 1 != bb_data.statements.len() { + continue; + } + + let TerminatorKind::Assert { + cond, + target, + msg: box AssertKind::Overflow(..), + .. + } = &bb_data.terminator().kind + else { + continue; + }; + let Some(assign) = body.basic_blocks[*target].statements.first() else { + continue; + }; + let StatementKind::Assign(box (dest, Rvalue::Use(Operand::Move(temp)))) = + assign.kind + else { + continue; + }; + + if dest != *lhs { + continue; + } + + let Operand::Move(cond) = cond else { continue }; + let [PlaceElem::Field(FIELD_0, _)] = &temp.projection.as_slice() else { + continue; + }; + let [PlaceElem::Field(FIELD_1, _)] = &cond.projection.as_slice() else { + continue; + }; + + // We ignore indirect self-assignment, because both occurrences of `dest` are uses. + let is_indirect = checked_places + .get(dest.as_ref()) + .map_or(false, |(_, projections)| is_indirect(projections)); + if is_indirect { + continue; + } + + if first_place.local == temp.local + && first_place.local == cond.local + && first_place.projection.is_empty() + { + // Original block + self_assign.insert(Location { + block: bb, + statement_index: bb_data.statements.len() - 1, + }); + self_assign.insert(Location { + block: bb, + statement_index: bb_data.statements.len(), + }); + // Target block + self_assign.insert(Location { block: *target, statement_index: 0 }); + } + } + // Straight self-assignment. + Rvalue::BinaryOp(op, box (Operand::Copy(lhs), _)) => { + if lhs != first_place { + continue; + } + + // We ignore indirect self-assignment, because both occurrences of `dest` are uses. + let is_indirect = checked_places + .get(first_place.as_ref()) + .map_or(false, |(_, projections)| is_indirect(projections)); + if is_indirect { + continue; + } + + self_assign.insert(Location { block: bb, statement_index }); + + // Checked division verifies overflow before performing the division, so we + // need to go and ignore this check in the predecessor block. + if let BinOp::Div | BinOp::Rem = op + && statement_index == 0 + && let &[pred] = body.basic_blocks.predecessors()[bb].as_slice() + && let TerminatorKind::Assert { msg, .. } = + &body.basic_blocks[pred].terminator().kind + && let AssertKind::Overflow(..) = **msg + && let len = body.basic_blocks[pred].statements.len() + && len >= 2 + { + // BitAnd of two checks. + self_assign.insert(Location { block: pred, statement_index: len - 1 }); + // `lhs == MIN`. + self_assign.insert(Location { block: pred, statement_index: len - 2 }); + } + } + _ => {} + } + } + } + + self_assign +} + +#[derive(Default, Debug)] +struct PlaceSet<'tcx> { + places: IndexVec>, + names: IndexVec>, + + /// Places corresponding to locals, common case. + locals: IndexVec>, + + // Handling of captures. + /// If `_1` is a reference, we need to add a `Deref` to the matched place. + capture_field_pos: usize, + /// Captured fields. + captures: IndexVec, +} + +impl<'tcx> PlaceSet<'tcx> { + fn insert_locals(&mut self, decls: &IndexVec>) { + self.locals = IndexVec::from_elem(None, &decls); + for (local, decl) in decls.iter_enumerated() { + // Record all user-written locals for the analysis. + // We also keep the `RefForGuard` locals (more on that below). + if let LocalInfo::User(BindingForm::Var(_) | BindingForm::RefForGuard(_)) = + decl.local_info() + { + let index = self.places.push(local.into()); + self.locals[local] = Some(index); + let _index = self.names.push(None); + debug_assert_eq!(index, _index); + } + } + } + + fn insert_captures( + &mut self, + tcx: TyCtxt<'tcx>, + self_is_ref: bool, + captures: &[&'tcx ty::CapturedPlace<'tcx>], + upvars: &ty::List>, + ) { + // We should not track the environment local separately. + debug_assert_eq!(self.locals[ty::CAPTURE_STRUCT_LOCAL], None); + + let self_place = Place { + local: ty::CAPTURE_STRUCT_LOCAL, + projection: tcx.mk_place_elems(if self_is_ref { &[PlaceElem::Deref] } else { &[] }), + }; + if self_is_ref { + self.capture_field_pos = 1; + } + + for (f, (capture, ty)) in std::iter::zip(captures, upvars).enumerate() { + let f = FieldIdx::from_usize(f); + let elem = PlaceElem::Field(f, ty); + let by_ref = matches!(capture.info.capture_kind, ty::UpvarCapture::ByRef(..)); + let place = if by_ref { + self_place.project_deeper(&[elem, PlaceElem::Deref], tcx) + } else { + self_place.project_deeper(&[elem], tcx) + }; + let index = self.places.push(place.as_ref()); + let _f = self.captures.push((index, by_ref)); + debug_assert_eq!(_f, f); + + // Record a variable name from the capture, because it is much friendlier than the + // debuginfo name. + self.names.insert( + index, + (Symbol::intern(&capture.to_string(tcx)), capture.get_path_span(tcx)), + ); + } + } + + fn record_debuginfo(&mut self, var_debug_info: &Vec>) { + let ignore_name = |name: Symbol| { + name == sym::empty || name == kw::SelfLower || name.as_str().starts_with('_') + }; + for var_debug_info in var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value + && let Some(index) = self.locals[place.local] + && !ignore_name(var_debug_info.name) + { + self.names.get_or_insert_with(index, || { + (var_debug_info.name, var_debug_info.source_info.span) + }); + } + } + + // Discard places that will not result in a diagnostic. + for index_opt in self.locals.iter_mut() { + if let Some(index) = *index_opt { + let remove = match self.names[index] { + None => true, + Some((name, _)) => ignore_name(name), + }; + if remove { + *index_opt = None; + } + } + } + } + + #[inline] + fn get(&self, place: PlaceRef<'tcx>) -> Option<(PlaceIndex, &'tcx [PlaceElem<'tcx>])> { + if let Some(index) = self.locals[place.local] { + return Some((index, place.projection)); + } + if place.local == ty::CAPTURE_STRUCT_LOCAL + && !self.captures.is_empty() + && self.capture_field_pos < place.projection.len() + && let PlaceElem::Field(f, _) = place.projection[self.capture_field_pos] + && let Some((index, by_ref)) = self.captures.get(f) + { + let mut start = self.capture_field_pos + 1; + if *by_ref { + // Account for an extra Deref. + start += 1; + } + // We may have an attempt to access `_1.f` as a shallow reborrow. Just ignore it. + if start <= place.projection.len() { + let projection = &place.projection[start..]; + return Some((*index, projection)); + } + } + None + } + + fn iter(&self) -> impl Iterator)> { + self.places.iter_enumerated() + } + + fn len(&self) -> usize { + self.places.len() + } +} + +struct AssignmentResult<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + checked_places: &'a PlaceSet<'tcx>, + body: &'a Body<'tcx>, + /// Set of locals that are live at least once. This is used to report fully unused locals. + ever_live: DenseBitSet, + /// Set of locals that have a non-trivial drop. This is used to skip reporting unused + /// assignment if it would be used by the `Drop` impl. + ever_dropped: DenseBitSet, + /// Set of assignments for each local. Here, assignment is understood in the AST sense. Any + /// MIR that may look like an assignment (Assign, DropAndReplace, Yield, Call) are considered. + /// + /// For each local, we return a map: for each source position, whether the statement is live + /// and which kind of access it performs. When we encounter multiple statements at the same + /// location, we only increase the liveness, in order to avoid false positives. + assignments: IndexVec>, +} + +impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { + /// Collect all assignments to checked locals. + /// + /// Assignments are collected, even if they are live. Dead assignments are reported, and live + /// assignments are used to make diagnostics correct for match guards. + fn find_dead_assignments( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + checked_places: &'a PlaceSet<'tcx>, + cursor: &mut ResultsCursor<'_, 'tcx, MaybeLivePlaces<'_, 'tcx>>, + body: &'a Body<'tcx>, + ) -> AssignmentResult<'a, 'tcx> { + let mut ever_live = DenseBitSet::new_empty(checked_places.len()); + let mut ever_dropped = DenseBitSet::new_empty(checked_places.len()); + let mut assignments = IndexVec::>::from_elem( + Default::default(), + &checked_places.places, + ); + + let mut check_place = + |place: Place<'tcx>, kind, source_info: SourceInfo, live: &DenseBitSet| { + if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) { + if !is_indirect(extra_projections) { + match assignments[index].entry(source_info) { + IndexEntry::Vacant(v) => { + let access = Access { kind, live: live.contains(index) }; + v.insert(access); + } + IndexEntry::Occupied(mut o) => { + // There were already a sighting. Mark this statement as live if it + // was, to avoid false positives. + o.get_mut().live |= live.contains(index); + } + } + } + } + }; + + let mut record_drop = |place: Place<'tcx>| { + if let Some((index, &[])) = checked_places.get(place.as_ref()) { + ever_dropped.insert(index); + } + }; + + for (bb, bb_data) in traversal::postorder(body) { + cursor.seek_to_block_end(bb); + let live = cursor.get(); + ever_live.union(live); + + let terminator = bb_data.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination: place, .. } + | TerminatorKind::Yield { resume_arg: place, .. } => { + check_place(*place, AccessKind::Assign, terminator.source_info, live); + record_drop(*place) + } + TerminatorKind::Drop { place, .. } => record_drop(*place), + TerminatorKind::InlineAsm { operands, .. } => { + for operand in operands { + if let InlineAsmOperand::Out { place: Some(place), .. } + | InlineAsmOperand::InOut { out_place: Some(place), .. } = operand + { + check_place(*place, AccessKind::Assign, terminator.source_info, live); + } + } + } + _ => {} + } + + for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { + cursor.seek_before_primary_effect(Location { block: bb, statement_index }); + let live = cursor.get(); + ever_live.union(live); + match &statement.kind { + StatementKind::Assign(box (place, _)) + | StatementKind::Deinit(box place) + | StatementKind::SetDiscriminant { box place, .. } => { + check_place(*place, AccessKind::Assign, statement.source_info, live); + } + StatementKind::Retag(_, _) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Coverage(_) + | StatementKind::Intrinsic(_) + | StatementKind::Nop + | StatementKind::FakeRead(_) + | StatementKind::PlaceMention(_) + | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::AscribeUserType(_, _) => (), + } + } + } + + // Check liveness of function arguments on entry. + { + cursor.seek_to_block_start(START_BLOCK); + let live = cursor.get(); + ever_live.union(live); + + // Verify that arguments and captured values are useful. + for (index, place) in checked_places.iter() { + let kind = if is_capture(*place) { + // This is a by-ref capture, an assignment to it will modify surrounding + // environment, so we do not report it. + if place.projection.last() == Some(&PlaceElem::Deref) { + continue; + } + + AccessKind::Capture + } else if body.local_kind(place.local) == LocalKind::Arg { + AccessKind::Param + } else { + continue; + }; + let source_info = body.local_decls[place.local].source_info; + let access = Access { kind, live: live.contains(index) }; + assignments[index].insert(source_info, access); + } + } + + AssignmentResult { + tcx, + typing_env, + checked_places, + ever_live, + ever_dropped, + assignments, + body, + } + } + + /// Match guards introduce a different local to freeze the guarded value as immutable. + /// Having two locals, we need to make sure that we do not report an unused_variable + /// when the guard local is used but not the arm local, or vice versa, like in this example. + /// + /// match 5 { + /// x if x > 2 => {} + /// ^ ^- This is `local` + /// +------ This is `arm_local` + /// _ => {} + /// } + /// + fn merge_guards(&mut self) { + for (index, place) in self.checked_places.iter() { + let local = place.local; + if let &LocalInfo::User(BindingForm::RefForGuard(arm_local)) = + self.body.local_decls[local].local_info() + { + debug_assert!(place.projection.is_empty()); + + // Local to use in the arm. + let Some((arm_index, _proj)) = self.checked_places.get(arm_local.into()) else { + continue; + }; + debug_assert_ne!(index, arm_index); + debug_assert_eq!(_proj, &[]); + + // Mark the arm local as used if the guard local is used. + if self.ever_live.contains(index) { + self.ever_live.insert(arm_index); + } + + // Some assignments are common to both locals in the source code. + // Sadly, we can only detect this using the `source_info`. + // Therefore, we loop over all the assignments we have for the guard local: + // - if they already appeared for the arm local, the assignment is live if one of the + // two versions is live; + // - if it does not appear for the arm local, it happened inside the guard, so we add + // it as-is. + let guard_assignments = std::mem::take(&mut self.assignments[index]); + let arm_assignments = &mut self.assignments[arm_index]; + for (source_info, access) in guard_assignments { + match arm_assignments.entry(source_info) { + IndexEntry::Vacant(v) => { + v.insert(access); + } + IndexEntry::Occupied(mut o) => { + o.get_mut().live |= access.live; + } + } + } + } + } + } + + /// Compute captures that are fully dead. + fn compute_dead_captures(&self, num_captures: usize) -> DenseBitSet { + // Report to caller the set of dead captures. + let mut dead_captures = DenseBitSet::new_empty(num_captures); + for (index, place) in self.checked_places.iter() { + if self.ever_live.contains(index) { + continue; + } + + // This is a capture: pass information to the enclosing function. + if is_capture(*place) { + for p in place.projection { + if let PlaceElem::Field(f, _) = p { + dead_captures.insert(*f); + break; + } + } + continue; + } + } + + dead_captures + } + + /// Report fully unused locals, and forget the corresponding assignments. + fn report_fully_unused(&mut self) { + let tcx = self.tcx; + + // First, report fully unused locals. + for (index, place) in self.checked_places.iter() { + if self.ever_live.contains(index) { + continue; + } + + // this is a capture: let the enclosing function report the unused variable. + if is_capture(*place) { + continue; + } + + let local = place.local; + let decl = &self.body.local_decls[local]; + + if decl.from_compiler_desugaring() { + continue; + } + + // Only report actual user-defined binding from now on. + let LocalInfo::User(BindingForm::Var(binding)) = decl.local_info() else { continue }; + let Some(hir_id) = decl.source_info.scope.lint_root(&self.body.source_scopes) else { + continue; + }; + + let introductions = &binding.introductions; + + let Some((name, def_span)) = self.checked_places.names[index] else { continue }; + + // #117284, when `ident_span` and `def_span` have different contexts + // we can't provide a good suggestion, instead we pointed out the spans from macro + let from_macro = def_span.from_expansion() + && introductions.iter().any(|intro| intro.span.eq_ctxt(def_span)); + + let statements = &mut self.assignments[index]; + if statements.is_empty() { + let sugg = if from_macro { + errors::UnusedVariableSugg::NoSugg { span: def_span, name } + } else { + errors::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name } + }; + tcx.emit_node_span_lint( + lint::builtin::UNUSED_VARIABLES, + hir_id, + def_span, + errors::UnusedVariable { + name, + string_interp: maybe_suggest_literal_matching_name(self.body, name), + sugg, + }, + ); + continue; + } + + // Idiomatic rust assigns a value to a local upon definition. However, we do not want to + // warn twice, for the unused local and for the unused assignment. Therefore, we remove + // from the list of assignments the ones that happen at the definition site. + statements.retain(|source_info, _| { + source_info.span.find_ancestor_inside(binding.pat_span).is_none() + }); + + // Extra assignments that we recognize thanks to the initialization span. We need to + // take care of macro contexts here to be accurate. + if let Some((_, initializer_span)) = binding.opt_match_place { + statements.retain(|source_info, _| { + let within = source_info.span.find_ancestor_inside(initializer_span); + let outer_initializer_span = + initializer_span.find_ancestor_in_same_ctxt(source_info.span); + within.is_none() + && outer_initializer_span.map_or(true, |s| !s.contains(source_info.span)) + }); + } + + if !statements.is_empty() { + // We have a dead local with outstanding assignments and with non-trivial drop. + // This is probably a drop-guard, so we do not issue a warning there. + if maybe_drop_guard( + tcx, + self.typing_env, + index, + &self.ever_dropped, + self.checked_places, + self.body, + ) { + statements.clear(); + continue; + } + + tcx.emit_node_span_lint( + lint::builtin::UNUSED_VARIABLES, + hir_id, + def_span, + errors::UnusedVarAssignedOnly { name }, + ); + continue; + } + + // We do not have outstanding assignments, suggest renaming the binding. + let spans = introductions.iter().map(|intro| intro.span).collect::>(); + + let any_shorthand = introductions.iter().any(|intro| intro.is_shorthand); + + let sugg = if any_shorthand { + errors::UnusedVariableSugg::TryIgnore { + name, + shorthands: introductions + .iter() + .filter_map( + |intro| if intro.is_shorthand { Some(intro.span) } else { None }, + ) + .collect(), + non_shorthands: introductions + .iter() + .filter_map( + |intro| { + if !intro.is_shorthand { Some(intro.span) } else { None } + }, + ) + .collect(), + } + } else if from_macro { + errors::UnusedVariableSugg::NoSugg { span: def_span, name } + } else if !introductions.is_empty() { + errors::UnusedVariableSugg::TryPrefix { name, spans: spans.clone() } + } else { + errors::UnusedVariableSugg::TryPrefix { name, spans: vec![def_span] } + }; + + tcx.emit_node_span_lint( + lint::builtin::UNUSED_VARIABLES, + hir_id, + spans, + errors::UnusedVariable { + name, + string_interp: maybe_suggest_literal_matching_name(self.body, name), + sugg, + }, + ); + } + } + + /// Second, report unused assignments that do not correspond to initialization. + /// Initializations have been removed in the previous loop reporting unused variables. + fn report_unused_assignments(self) { + let tcx = self.tcx; + + for (index, statements) in self.assignments.into_iter_enumerated() { + if statements.is_empty() { + continue; + } + + let Some((name, decl_span)) = self.checked_places.names[index] else { continue }; + + // We have outstanding assignments and with non-trivial drop. + // This is probably a drop-guard, so we do not issue a warning there. + if maybe_drop_guard( + tcx, + self.typing_env, + index, + &self.ever_dropped, + self.checked_places, + self.body, + ) { + continue; + } + + // We probed MIR in reverse order for dataflow. + // We revert the vector to give a consistent order to the user. + for (source_info, Access { live, kind }) in statements.into_iter().rev() { + if live { + continue; + } + + // Report the dead assignment. + let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else { + continue; + }; + + match kind { + AccessKind::Assign => { + let suggestion = annotate_mut_binding_to_immutable_binding( + tcx, + self.checked_places.places[index], + self.body.source.def_id().expect_local(), + source_info.span, + self.body, + ); + tcx.emit_node_span_lint( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + source_info.span, + errors::UnusedAssign { name, help: suggestion.is_none(), suggestion }, + ) + } + AccessKind::Param => tcx.emit_node_span_lint( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + source_info.span, + errors::UnusedAssignPassed { name }, + ), + AccessKind::Capture => tcx.emit_node_span_lint( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + decl_span, + errors::UnusedCaptureMaybeCaptureRef { name }, + ), + } + } + } + } +} + +rustc_index::newtype_index! { + pub struct PlaceIndex {} +} + +impl DebugWithContext> for PlaceIndex { + fn fmt_with( + &self, + ctxt: &MaybeLivePlaces<'_, '_>, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + std::fmt::Debug::fmt(&ctxt.checked_places.places[*self], f) + } +} + +pub struct MaybeLivePlaces<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + checked_places: &'a PlaceSet<'tcx>, + capture_kind: CaptureKind, + self_assignment: FxHashSet, +} + +impl<'tcx> MaybeLivePlaces<'_, 'tcx> { + fn transfer_function<'a>( + &'a self, + trans: &'a mut DenseBitSet, + ) -> TransferFunction<'a, 'tcx> { + TransferFunction { + tcx: self.tcx, + checked_places: &self.checked_places, + capture_kind: self.capture_kind, + trans, + self_assignment: &self.self_assignment, + } + } +} + +impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> { + type Domain = DenseBitSet; + type Direction = Backward; + + const NAME: &'static str = "liveness-lint"; + + fn bottom_value(&self, _: &Body<'tcx>) -> Self::Domain { + // bottom = not live + DenseBitSet::new_empty(self.checked_places.len()) + } + + fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { + // No variables are live until we observe a use + } + + fn apply_primary_statement_effect( + &mut self, + trans: &mut Self::Domain, + statement: &Statement<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_statement(statement, location); + } + + fn apply_primary_terminator_effect<'mir>( + &mut self, + trans: &mut Self::Domain, + terminator: &'mir Terminator<'tcx>, + location: Location, + ) -> TerminatorEdges<'mir, 'tcx> { + self.transfer_function(trans).visit_terminator(terminator, location); + terminator.edges() + } + + fn apply_call_return_effect( + &mut self, + _trans: &mut Self::Domain, + _block: BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // FIXME: what should happen here? + } +} + +struct TransferFunction<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + checked_places: &'a PlaceSet<'tcx>, + trans: &'a mut DenseBitSet, + capture_kind: CaptureKind, + self_assignment: &'a FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match statement.kind { + // `ForLet(None)` fake read erroneously marks the just-assigned local as live. + // This defeats the purpose of the analysis for `let` bindings. + StatementKind::FakeRead(box (FakeReadCause::ForLet(None), _)) => return, + // Handle self-assignment by restricting the read/write they do. + StatementKind::Assign(box (ref dest, ref rvalue)) + if self.self_assignment.contains(&location) => + { + if let Rvalue::BinaryOp( + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow, + box (_, rhs), + ) = rvalue + { + // We are computing the binary operation: + // - the LHS will be assigned, so we don't read it; + // - the RHS still needs to be read. + self.visit_operand(rhs, location); + self.visit_place( + dest, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } else if let Rvalue::BinaryOp(_, box (_, rhs)) = rvalue { + // We are computing the binary operation: + // - the LHS is being updated, so we don't read it; + // - the RHS still needs to be read. + self.visit_operand(rhs, location); + } else { + // This is the second part of a checked self-assignment, + // we are assigning the result. + // We do not consider the write to the destination as a `def`. + // `self_assignment` must be false if the assignment is indirect. + self.visit_rvalue(rvalue, location); + } + } + _ => self.super_statement(statement, location), + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // By-ref captures could be read by the surrounding environment, so we mark + // them as live upon yield and return. + match terminator.kind { + TerminatorKind::Return + | TerminatorKind::Yield { .. } + | TerminatorKind::Goto { target: START_BLOCK } // Inserted for the `FnMut` case. + if self.capture_kind != CaptureKind::None => + { + // All indirect captures have an effect on the environment, so we mark them as live. + for (index, place) in self.checked_places.iter() { + if place.local == ty::CAPTURE_STRUCT_LOCAL + && place.projection.last() == Some(&PlaceElem::Deref) + { + self.trans.insert(index); + } + } + } + // Do not consider a drop to be a use. We whitelist interesting drops elsewhere. + TerminatorKind::Drop { .. } => {} + // Ignore assertions since they must be triggered by actual code. + TerminatorKind::Assert { .. } => {} + _ => self.super_terminator(terminator, location), + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + match rvalue { + // When a closure/generator does not use some of its captures, do not consider these + // captures as live in the surrounding function. This allows to report unused variables, + // even if they have been (uselessly) captured. + Rvalue::Aggregate( + box AggregateKind::Closure(def_id, _) | box AggregateKind::Coroutine(def_id, _), + operands, + ) => { + if let Some(def_id) = def_id.as_local() { + let dead_captures = self.tcx.check_liveness(def_id); + for (field, operand) in + operands.iter_enumerated().take(dead_captures.domain_size()) + { + if !dead_captures.contains(field) { + self.visit_operand(operand, location); + } + } + } + } + _ => self.super_rvalue(rvalue, location), + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + if let Some((index, extra_projections)) = self.checked_places.get(place.as_ref()) { + for i in (extra_projections.len()..=place.projection.len()).rev() { + let place_part = + PlaceRef { local: place.local, projection: &place.projection[..i] }; + let extra_projections = &place.projection[i..]; + + if let Some(&elem) = extra_projections.get(0) { + self.visit_projection_elem(place_part, elem, context, location); + } + } + + match DefUse::for_place(extra_projections, context) { + Some(DefUse::Def) => { + self.trans.remove(index); + } + Some(DefUse::Use) => { + self.trans.insert(index); + } + None => {} + } + } else { + self.super_place(place, context, location) + } + } + + fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { + if let Some((index, _proj)) = self.checked_places.get(local.into()) { + debug_assert_eq!(_proj, &[]); + match DefUse::for_place(&[], context) { + Some(DefUse::Def) => { + self.trans.remove(index); + } + Some(DefUse::Use) => { + self.trans.insert(index); + } + _ => {} + } + } + } +} + +#[derive(Eq, PartialEq, Debug, Clone)] +enum DefUse { + Def, + Use, +} + +fn is_indirect(proj: &[PlaceElem<'_>]) -> bool { + proj.iter().any(|p| p.is_indirect()) +} + +impl DefUse { + fn for_place<'tcx>(projection: &[PlaceElem<'tcx>], context: PlaceContext) -> Option { + let is_indirect = is_indirect(projection); + match context { + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Deinit + | MutatingUseContext::SetDiscriminant, + ) => { + if is_indirect { + // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a + // use. + Some(DefUse::Use) + } else if projection.is_empty() { + Some(DefUse::Def) + } else { + None + } + } + + // For the associated terminators, this is only a `Def` when the terminator returns + // "successfully." As such, we handle this case separately in `call_return_effect` + // above. However, if the place looks like `*_5`, this is still unconditionally a use of + // `_5`. + PlaceContext::MutatingUse( + MutatingUseContext::Call + | MutatingUseContext::Yield + | MutatingUseContext::AsmOutput, + ) => is_indirect.then_some(DefUse::Use), + + // All other contexts are uses... + PlaceContext::MutatingUse( + MutatingUseContext::RawBorrow + | MutatingUseContext::Borrow + | MutatingUseContext::Drop + | MutatingUseContext::Retag, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::RawBorrow + | NonMutatingUseContext::Copy + | NonMutatingUseContext::Inspect + | NonMutatingUseContext::Move + | NonMutatingUseContext::FakeBorrow + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::PlaceMention, + ) => Some(DefUse::Use), + + PlaceContext::NonUse( + NonUseContext::StorageLive + | NonUseContext::StorageDead + | NonUseContext::AscribeUserTy(_) + | NonUseContext::BackwardIncompatibleDropHint + | NonUseContext::VarDebugInfo, + ) => None, + + PlaceContext::MutatingUse(MutatingUseContext::Projection) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { + unreachable!("A projection could be a def or a use and must be handled separately") + } + } + } +} diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index be9843988669a..dc956b9f3169b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -395,8 +395,6 @@ passes_macro_only_attribute = passes_may_dangle = `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl -passes_maybe_string_interpolation = you might have meant to use string interpolation in this string literal - passes_missing_const_err = attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const` .help = make the function or method const @@ -568,8 +566,6 @@ passes_should_be_applied_to_trait = attribute should be applied to a trait .label = not a trait -passes_string_interpolation_only_works = string interpolation only works in `format!` invocations - passes_trait_impl_const_stability_mismatch = const stability on the impl does not match the const stability on the trait passes_trait_impl_const_stability_mismatch_impl_stable = this impl is (implicitly) stable... passes_trait_impl_const_stability_mismatch_impl_unstable = this impl is unstable... @@ -636,18 +632,6 @@ passes_unused = unused attribute .suggestion = remove this attribute -passes_unused_assign = value assigned to `{$name}` is never read - .help = maybe it is overwritten before being read? - -passes_unused_assign_passed = value passed to `{$name}` is never read - .help = maybe it is overwritten before being read? - -passes_unused_assign_suggestion = - you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding - -passes_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read - .help = did you mean to capture by reference instead? - passes_unused_default_method_body_const_note = `default_method_body_is_const` has been replaced with `const` on traits @@ -671,25 +655,6 @@ passes_unused_multiple = passes_unused_no_lints_note = attribute `{$name}` without any lints has no effect -passes_unused_var_assigned_only = variable `{$name}` is assigned to, but never used - .note = consider using `_{$name}` instead - -passes_unused_var_maybe_capture_ref = unused variable: `{$name}` - .help = did you mean to capture by reference instead? - -passes_unused_var_remove_field = unused variable: `{$name}` -passes_unused_var_remove_field_suggestion = try removing the field -passes_unused_var_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}` - -passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable - -passes_unused_variable_try_ignore = unused variable: `{$name}` - .suggestion = try ignoring the field - -passes_unused_variable_try_prefix = unused variable: `{$name}` - .label = unused variable - .suggestion = if this is intentional, prefix it with an underscore - passes_useless_assignment = useless assignment of {$is_field_assign -> [true] field diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 8ca0641c4dd25..7d184d8bb694c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, Subdiagnostic, + MultiSpan, }; use rustc_hir::Target; use rustc_hir::attrs::{MirDialect, MirPhase}; @@ -1308,61 +1308,6 @@ pub(crate) struct ProcMacroBadSig { pub kind: ProcMacroKind, } -#[derive(LintDiagnostic)] -#[diag(passes_unused_var_maybe_capture_ref)] -#[help] -pub(crate) struct UnusedVarMaybeCaptureRef { - pub name: String, -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_capture_maybe_capture_ref)] -#[help] -pub(crate) struct UnusedCaptureMaybeCaptureRef { - pub name: String, -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_var_remove_field)] -pub(crate) struct UnusedVarRemoveField { - pub name: String, - #[subdiagnostic] - pub sugg: UnusedVarRemoveFieldSugg, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - passes_unused_var_remove_field_suggestion, - applicability = "machine-applicable" -)] -pub(crate) struct UnusedVarRemoveFieldSugg { - #[suggestion_part(code = "")] - pub spans: Vec, -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_var_assigned_only)] -#[note] -pub(crate) struct UnusedVarAssignedOnly { - pub name: String, - #[subdiagnostic] - pub typo: Option, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - passes_unused_var_typo, - style = "verbose", - applicability = "maybe-incorrect" -)] -pub(crate) struct PatternTypo { - #[suggestion_part(code = "{code}")] - pub span: Span, - pub code: String, - pub item_name: String, - pub kind: String, -} - #[derive(LintDiagnostic)] #[diag(passes_unnecessary_stable_feature)] pub(crate) struct UnnecessaryStableFeature { @@ -1387,102 +1332,6 @@ pub(crate) struct UnnecessaryPartialStableFeature { #[note] pub(crate) struct IneffectiveUnstableImpl; -#[derive(LintDiagnostic)] -#[diag(passes_unused_assign)] -pub(crate) struct UnusedAssign { - pub name: String, - #[subdiagnostic] - pub suggestion: Option, - #[help] - pub help: bool, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(passes_unused_assign_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct UnusedAssignSuggestion { - pub pre: &'static str, - #[suggestion_part(code = "{pre}mut ")] - pub ty_span: Option, - #[suggestion_part(code = "")] - pub ty_ref_span: Span, - #[suggestion_part(code = "*")] - pub ident_span: Span, - #[suggestion_part(code = "")] - pub expr_ref_span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_assign_passed)] -#[help] -pub(crate) struct UnusedAssignPassed { - pub name: String, -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_variable_try_prefix)] -pub(crate) struct UnusedVariableTryPrefix { - #[label] - pub label: Option, - #[subdiagnostic] - pub string_interp: Vec, - #[subdiagnostic] - pub sugg: UnusedVariableSugg, - pub name: String, - #[subdiagnostic] - pub typo: Option, -} - -#[derive(Subdiagnostic)] -pub(crate) enum UnusedVariableSugg { - #[multipart_suggestion(passes_suggestion, applicability = "maybe-incorrect")] - TryPrefixSugg { - #[suggestion_part(code = "_{name}")] - spans: Vec, - name: String, - }, - #[help(passes_unused_variable_args_in_macro)] - NoSugg { - #[primary_span] - span: Span, - name: String, - }, -} - -pub(crate) struct UnusedVariableStringInterp { - pub lit: Span, - pub lo: Span, - pub hi: Span, -} - -impl Subdiagnostic for UnusedVariableStringInterp { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation); - diag.multipart_suggestion( - crate::fluent_generated::passes_string_interpolation_only_works, - vec![(self.lo, String::from("format!(")), (self.hi, String::from(")"))], - Applicability::MachineApplicable, - ); - } -} - -#[derive(LintDiagnostic)] -#[diag(passes_unused_variable_try_ignore)] -pub(crate) struct UnusedVarTryIgnore { - pub name: String, - #[subdiagnostic] - pub sugg: UnusedVarTryIgnoreSugg, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(passes_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct UnusedVarTryIgnoreSugg { - #[suggestion_part(code = "{name}: _")] - pub shorthands: Vec, - #[suggestion_part(code = "_")] - pub non_shorthands: Vec, - pub name: String, -} - #[derive(LintDiagnostic)] #[diag(passes_attr_crate_level)] #[note] diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 06a127577d432..c98e971185795 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -28,7 +28,6 @@ pub mod input_stats; mod lang_items; pub mod layout_test; mod lib_features; -mod liveness; mod reachable; pub mod stability; mod upvars; @@ -44,7 +43,6 @@ pub fn provide(providers: &mut Providers) { entry::provide(providers); lang_items::provide(providers); lib_features::provide(providers); - liveness::provide(providers); reachable::provide(providers); stability::provide(providers); upvars::provide(providers); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs deleted file mode 100644 index e175ac1c035e1..0000000000000 --- a/compiler/rustc_passes/src/liveness.rs +++ /dev/null @@ -1,1853 +0,0 @@ -//! A classic liveness analysis based on dataflow over the AST. Computes, -//! for each local variable in a function, whether that variable is live -//! at a given point. Program execution points are identified by their -//! IDs. -//! -//! # Basic idea -//! -//! The basic model is that each local variable is assigned an index. We -//! represent sets of local variables using a vector indexed by this -//! index. The value in the vector is either 0, indicating the variable -//! is dead, or the ID of an expression that uses the variable. -//! -//! We conceptually walk over the AST in reverse execution order. If we -//! find a use of a variable, we add it to the set of live variables. If -//! we find an assignment to a variable, we remove it from the set of live -//! variables. When we have to merge two flows, we take the union of -//! those two flows -- if the variable is live on both paths, we simply -//! pick one ID. In the event of loops, we continue doing this until a -//! fixed point is reached. -//! -//! ## Checking initialization -//! -//! At the function entry point, all variables must be dead. If this is -//! not the case, we can report an error using the ID found in the set of -//! live variables, which identifies a use of the variable which is not -//! dominated by an assignment. -//! -//! ## Checking moves -//! -//! After each explicit move, the variable must be dead. -//! -//! ## Computing last uses -//! -//! Any use of the variable where the variable is dead afterwards is a -//! last use. -//! -//! # Implementation details -//! -//! The actual implementation contains two (nested) walks over the AST. -//! The outer walk has the job of building up the ir_maps instance for the -//! enclosing function. On the way down the tree, it identifies those AST -//! nodes and variable IDs that will be needed for the liveness analysis -//! and assigns them contiguous IDs. The liveness ID for an AST node is -//! called a `live_node` (it's a newtype'd `u32`) and the ID for a variable -//! is called a `variable` (another newtype'd `u32`). -//! -//! On the way back up the tree, as we are about to exit from a function -//! declaration we allocate a `liveness` instance. Now that we know -//! precisely how many nodes and variables we need, we can allocate all -//! the various arrays that we will need to precisely the right size. We then -//! perform the actual propagation on the `liveness` instance. -//! -//! This propagation is encoded in the various `propagate_through_*()` -//! methods. It effectively does a reverse walk of the AST; whenever we -//! reach a loop node, we iterate until a fixed point is reached. -//! -//! ## The `RWU` struct -//! -//! At each live node `N`, we track three pieces of information for each -//! variable `V` (these are encapsulated in the `RWU` struct): -//! -//! - `reader`: the `LiveNode` ID of some node which will read the value -//! that `V` holds on entry to `N`. Formally: a node `M` such -//! that there exists a path `P` from `N` to `M` where `P` does not -//! write `V`. If the `reader` is `None`, then the current -//! value will never be read (the variable is dead, essentially). -//! -//! - `writer`: the `LiveNode` ID of some node which will write the -//! variable `V` and which is reachable from `N`. Formally: a node `M` -//! such that there exists a path `P` from `N` to `M` and `M` writes -//! `V`. If the `writer` is `None`, then there is no writer -//! of `V` that follows `N`. -//! -//! - `used`: a boolean value indicating whether `V` is *used*. We -//! distinguish a *read* from a *use* in that a *use* is some read that -//! is not just used to generate a new value. For example, `x += 1` is -//! a read but not a use. This is used to generate better warnings. -//! -//! ## Special nodes and variables -//! -//! We generate various special nodes for various, well, special purposes. -//! These are described in the `Liveness` struct. - -use std::io; -use std::io::prelude::*; -use std::rc::Rc; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::*; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, find_attr}; -use rustc_index::IndexVec; -use rustc_middle::query::Providers; -use rustc_middle::span_bug; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, RootVariableMinCaptureList, TyCtxt}; -use rustc_session::lint; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{BytePos, Span, Symbol}; -use tracing::{debug, instrument}; - -use self::LiveNodeKind::*; -use self::VarKind::*; -use crate::errors; - -mod rwu_table; - -rustc_index::newtype_index! { - #[debug_format = "v({})"] - pub struct Variable {} -} - -rustc_index::newtype_index! { - #[debug_format = "ln({})"] - pub struct LiveNode {} -} - -#[derive(Copy, Clone, PartialEq, Debug)] -enum LiveNodeKind { - UpvarNode(Span), - ExprNode(Span, HirId), - VarDefNode(Span, HirId), - ClosureNode, - ExitNode, -} - -fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { - let sm = tcx.sess.source_map(); - match lnk { - UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_diagnostic_string(s)), - ExprNode(s, _) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)), - VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)), - ClosureNode => "Closure node".to_owned(), - ExitNode => "Exit node".to_owned(), - } -} - -fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { - // Don't run unused pass for #[derive()] - let parent = tcx.local_parent(def_id); - if let DefKind::Impl { .. } = tcx.def_kind(parent) - && find_attr!(tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) - { - return; - } - - // Don't run unused pass for #[naked] - if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) { - return; - } - - let mut maps = IrMaps::new(tcx); - let body = tcx.hir_body_owned_by(def_id); - let hir_id = tcx.hir_body_owner(body.id()); - - if let Some(upvars) = tcx.upvars_mentioned(def_id) { - for &var_hir_id in upvars.keys() { - let var_name = tcx.hir_name(var_hir_id); - maps.add_variable(Upvar(var_hir_id, var_name)); - } - } - - // gather up the various local variables, significant expressions, - // and so forth: - maps.visit_body(&body); - - // compute liveness - let mut lsets = Liveness::new(&mut maps, def_id); - let entry_ln = lsets.compute(&body, hir_id); - lsets.log_liveness(entry_ln, body.id().hir_id); - - // check for various error conditions - lsets.visit_body(&body); - lsets.warn_about_unused_upvars(entry_ln); - lsets.warn_about_unused_args(&body, entry_ln); -} - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { check_liveness, ..*providers }; -} - -// ______________________________________________________________________ -// Creating ir_maps -// -// This is the first pass and the one that drives the main -// computation. It walks up and down the IR once. On the way down, -// we count for each function the number of variables as well as -// liveness nodes. A liveness node is basically an expression or -// capture clause that does something of interest: either it has -// interesting control flow or it uses/defines a local variable. -// -// On the way back up, at each function node we create liveness sets -// (we now know precisely how big to make our various vectors and so -// forth) and then do the data-flow propagation to compute the set -// of live variables at each program point. -// -// Finally, we run back over the IR one last time and, using the -// computed liveness, check various safety conditions. For example, -// there must be no live nodes at the definition site for a variable -// unless it has an initializer. Similarly, each non-mutable local -// variable must not be assigned if there is some successor -// assignment. And so forth. - -struct CaptureInfo { - ln: LiveNode, - var_hid: HirId, -} - -#[derive(Copy, Clone, Debug)] -struct LocalInfo { - id: HirId, - name: Symbol, - is_shorthand: bool, -} - -#[derive(Copy, Clone, Debug)] -enum VarKind { - Param(HirId, Symbol), - Local(LocalInfo), - Upvar(HirId, Symbol), -} - -struct CollectLitsVisitor<'tcx> { - lit_exprs: Vec<&'tcx hir::Expr<'tcx>>, -} - -impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Lit(_) = expr.kind { - self.lit_exprs.push(expr); - } - intravisit::walk_expr(self, expr); - } -} - -struct IrMaps<'tcx> { - tcx: TyCtxt<'tcx>, - live_node_map: HirIdMap, - variable_map: HirIdMap, - capture_info_map: HirIdMap>>, - var_kinds: IndexVec, - lnks: IndexVec, -} - -impl<'tcx> IrMaps<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> IrMaps<'tcx> { - IrMaps { - tcx, - live_node_map: HirIdMap::default(), - variable_map: HirIdMap::default(), - capture_info_map: Default::default(), - var_kinds: IndexVec::new(), - lnks: IndexVec::new(), - } - } - - fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode { - let ln = self.lnks.push(lnk); - - debug!("{:?} is of kind {}", ln, live_node_kind_to_string(lnk, self.tcx)); - - ln - } - - fn add_live_node_for_node(&mut self, hir_id: HirId, lnk: LiveNodeKind) { - let ln = self.add_live_node(lnk); - self.live_node_map.insert(hir_id, ln); - - debug!("{:?} is node {:?}", ln, hir_id); - } - - fn add_variable(&mut self, vk: VarKind) -> Variable { - let v = self.var_kinds.push(vk); - - match vk { - Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => { - self.variable_map.insert(node_id, v); - } - } - - debug!("{:?} is {:?}", v, vk); - - v - } - - fn variable(&self, hir_id: HirId, span: Span) -> Variable { - match self.variable_map.get(&hir_id) { - Some(&var) => var, - None => { - span_bug!(span, "no variable registered for id {:?}", hir_id); - } - } - } - - fn variable_name(&self, var: Variable) -> Symbol { - match self.var_kinds[var] { - Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name, - } - } - - fn variable_is_shorthand(&self, var: Variable) -> bool { - match self.var_kinds[var] { - Local(LocalInfo { is_shorthand, .. }) => is_shorthand, - Param(..) | Upvar(..) => false, - } - } - - fn set_captures(&mut self, hir_id: HirId, cs: Vec) { - self.capture_info_map.insert(hir_id, Rc::new(cs)); - } - - fn collect_shorthand_field_ids(&self, pat: &hir::Pat<'tcx>) -> HirIdSet { - // For struct patterns, take note of which fields used shorthand - // (`x` rather than `x: x`). - let mut shorthand_field_ids = HirIdSet::default(); - - pat.walk_always(|pat| { - if let hir::PatKind::Struct(_, fields, _) = pat.kind { - let short = fields.iter().filter(|f| f.is_shorthand); - shorthand_field_ids.extend(short.map(|f| f.pat.hir_id)); - } - }); - - shorthand_field_ids - } - - fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { - let shorthand_field_ids = self.collect_shorthand_field_ids(pat); - - pat.each_binding(|_, hir_id, _, ident| { - self.add_live_node_for_node(hir_id, VarDefNode(ident.span, hir_id)); - self.add_variable(Local(LocalInfo { - id: hir_id, - name: ident.name, - is_shorthand: shorthand_field_ids.contains(&hir_id), - })); - }); - } -} - -impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { - self.add_from_pat(local.pat); - if local.els.is_some() { - self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); - } - intravisit::walk_local(self, local); - } - - fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.add_from_pat(&arm.pat); - intravisit::walk_arm(self, arm); - } - - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let shorthand_field_ids = self.collect_shorthand_field_ids(param.pat); - param.pat.each_binding(|_bm, hir_id, _x, ident| { - let var = match param.pat.kind { - rustc_hir::PatKind::Struct(..) => Local(LocalInfo { - id: hir_id, - name: ident.name, - is_shorthand: shorthand_field_ids.contains(&hir_id), - }), - _ => Param(hir_id, ident.name), - }; - self.add_variable(var); - }); - intravisit::walk_param(self, param); - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - // live nodes required for uses or definitions of variables: - hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { - debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res); - if let Res::Local(_var_hir_id) = path.res { - self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); - } - } - hir::ExprKind::Closure(closure) => { - // Interesting control flow (for loops can contain labeled - // breaks or continues) - self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); - - // Make a live_node for each mentioned variable, with the span - // being the location that the variable is used. This results - // in better error messages than just pointing at the closure - // construction site. - let mut call_caps = Vec::new(); - if let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) { - call_caps.extend(upvars.keys().map(|var_id| { - let upvar = upvars[var_id]; - let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); - CaptureInfo { ln: upvar_ln, var_hid: *var_id } - })); - } - self.set_captures(expr.hir_id, call_caps); - } - - hir::ExprKind::Let(let_expr) => { - self.add_from_pat(let_expr.pat); - } - - // live nodes required for interesting control flow: - hir::ExprKind::If(..) - | hir::ExprKind::Match(..) - | hir::ExprKind::Loop(..) - | hir::ExprKind::Yield(..) => { - self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); - } - hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => { - self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); - } - - // Inline assembly may contain labels. - hir::ExprKind::InlineAsm(asm) if asm.contains_label() => { - self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); - intravisit::walk_expr(self, expr); - } - - // otherwise, live nodes are not required: - hir::ExprKind::Index(..) - | hir::ExprKind::Field(..) - | hir::ExprKind::Array(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) - | hir::ExprKind::Use(..) - | hir::ExprKind::Tup(..) - | hir::ExprKind::Binary(..) - | hir::ExprKind::AddrOf(..) - | hir::ExprKind::Cast(..) - | hir::ExprKind::DropTemps(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Break(..) - | hir::ExprKind::Continue(_) - | hir::ExprKind::Lit(_) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Ret(..) - | hir::ExprKind::Become(..) - | hir::ExprKind::Block(..) - | hir::ExprKind::Assign(..) - | hir::ExprKind::AssignOp(..) - | hir::ExprKind::Struct(..) - | hir::ExprKind::Repeat(..) - | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::OffsetOf(..) - | hir::ExprKind::Type(..) - | hir::ExprKind::UnsafeBinderCast(..) - | hir::ExprKind::Err(_) - | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) - | hir::ExprKind::Path(hir::QPath::LangItem(..)) => {} - } - intravisit::walk_expr(self, expr); - } -} - -// ______________________________________________________________________ -// Computing liveness sets -// -// Actually we compute just a bit more than just liveness, but we use -// the same basic propagation framework in all cases. - -const ACC_READ: u32 = 1; -const ACC_WRITE: u32 = 2; -const ACC_USE: u32 = 4; - -struct Liveness<'a, 'tcx> { - ir: &'a mut IrMaps<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - closure_min_captures: Option<&'tcx RootVariableMinCaptureList<'tcx>>, - successors: IndexVec>, - rwu_table: rwu_table::RWUTable, - - /// A live node representing a point of execution before closure entry & - /// after closure exit. Used to calculate liveness of captured variables - /// through calls to the same closure. Used for Fn & FnMut closures only. - closure_ln: LiveNode, - /// A live node representing every 'exit' from the function, whether it be - /// by explicit return, panic, or other means. - exit_ln: LiveNode, - - // mappings from loop node ID to LiveNode - // ("break" label should map to loop node ID, - // it probably doesn't now) - break_ln: HirIdMap, - cont_ln: HirIdMap, -} - -impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> { - let typeck_results = ir.tcx.typeck(body_owner); - // Liveness linting runs after building the THIR. We make several assumptions based on - // typeck succeeding, e.g. that breaks and continues are well-formed. - assert!(typeck_results.tainted_by_errors.is_none()); - // FIXME(#132279): we're in a body here. - let typing_env = ty::TypingEnv::non_body_analysis(ir.tcx, body_owner); - let closure_min_captures = typeck_results.closure_min_captures.get(&body_owner); - let closure_ln = ir.add_live_node(ClosureNode); - let exit_ln = ir.add_live_node(ExitNode); - - let num_live_nodes = ir.lnks.len(); - let num_vars = ir.var_kinds.len(); - - Liveness { - ir, - typeck_results, - typing_env, - closure_min_captures, - successors: IndexVec::from_elem_n(None, num_live_nodes), - rwu_table: rwu_table::RWUTable::new(num_live_nodes, num_vars), - closure_ln, - exit_ln, - break_ln: Default::default(), - cont_ln: Default::default(), - } - } - - fn live_node(&self, hir_id: HirId, span: Span) -> LiveNode { - match self.ir.live_node_map.get(&hir_id) { - Some(&ln) => ln, - None => { - // This must be a mismatch between the ir_map construction - // above and the propagation code below; the two sets of - // code have to agree about which AST nodes are worth - // creating liveness nodes for. - span_bug!(span, "no live node registered for node {:?}", hir_id); - } - } - } - - fn variable(&self, hir_id: HirId, span: Span) -> Variable { - self.ir.variable(hir_id, span) - } - - fn define_bindings_in_pat(&mut self, pat: &hir::Pat<'_>, mut succ: LiveNode) -> LiveNode { - // In an or-pattern, only consider the first non-never pattern; any later patterns - // must have the same bindings, and we also consider that pattern - // to be the "authoritative" set of ids. - pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| { - let ln = self.live_node(hir_id, pat_sp); - let var = self.variable(hir_id, ident.span); - self.init_from_succ(ln, succ); - self.define(ln, var); - succ = ln; - }); - succ - } - - fn live_on_entry(&self, ln: LiveNode, var: Variable) -> bool { - self.rwu_table.get_reader(ln, var) - } - - // Is this variable live on entry to any of its successor nodes? - fn live_on_exit(&self, ln: LiveNode, var: Variable) -> bool { - let successor = self.successors[ln].unwrap(); - self.live_on_entry(successor, var) - } - - fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool { - self.rwu_table.get_used(ln, var) - } - - fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> bool { - self.rwu_table.get_writer(ln, var) - } - - fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> bool { - match self.successors[ln] { - Some(successor) => self.assigned_on_entry(successor, var), - None => { - self.ir.tcx.dcx().delayed_bug("no successor"); - true - } - } - } - - fn write_vars(&self, wr: &mut dyn Write, mut test: F) -> io::Result<()> - where - F: FnMut(Variable) -> bool, - { - for var in self.ir.var_kinds.indices() { - if test(var) { - write!(wr, " {var:?}")?; - } - } - Ok(()) - } - - #[allow(unused_must_use)] - fn ln_str(&self, ln: LiveNode) -> String { - let mut wr = Vec::new(); - { - let wr = &mut wr as &mut dyn Write; - write!(wr, "[{:?} of kind {:?} reads", ln, self.ir.lnks[ln]); - self.write_vars(wr, |var| self.rwu_table.get_reader(ln, var)); - write!(wr, " writes"); - self.write_vars(wr, |var| self.rwu_table.get_writer(ln, var)); - write!(wr, " uses"); - self.write_vars(wr, |var| self.rwu_table.get_used(ln, var)); - - write!(wr, " precedes {:?}]", self.successors[ln]); - } - String::from_utf8(wr).unwrap() - } - - fn log_liveness(&self, entry_ln: LiveNode, hir_id: HirId) { - // hack to skip the loop unless debug! is enabled: - debug!( - "^^ liveness computation results for body {} (entry={:?})", - { - for ln_idx in self.ir.lnks.indices() { - debug!("{:?}", self.ln_str(ln_idx)); - } - hir_id - }, - entry_ln - ); - } - - fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) { - self.successors[ln] = Some(succ_ln); - - // It is not necessary to initialize the RWUs here because they are all - // empty when created, and the sets only grow during iterations. - } - - fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) { - // more efficient version of init_empty() / merge_from_succ() - self.successors[ln] = Some(succ_ln); - self.rwu_table.copy(ln, succ_ln); - debug!("init_from_succ(ln={}, succ={})", self.ln_str(ln), self.ln_str(succ_ln)); - } - - fn merge_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) -> bool { - if ln == succ_ln { - return false; - } - - let changed = self.rwu_table.union(ln, succ_ln); - debug!("merge_from_succ(ln={:?}, succ={}, changed={})", ln, self.ln_str(succ_ln), changed); - changed - } - - // Indicates that a local variable was *defined*; we know that no - // uses of the variable can precede the definition (resolve checks - // this) so we just clear out all the data. - fn define(&mut self, writer: LiveNode, var: Variable) { - let used = self.rwu_table.get_used(writer, var); - self.rwu_table.set(writer, var, rwu_table::RWU { reader: false, writer: false, used }); - debug!("{:?} defines {:?}: {}", writer, var, self.ln_str(writer)); - } - - // Either read, write, or both depending on the acc bitset - fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) { - debug!("{:?} accesses[{:x}] {:?}: {}", ln, acc, var, self.ln_str(ln)); - - let mut rwu = self.rwu_table.get(ln, var); - - if (acc & ACC_WRITE) != 0 { - rwu.reader = false; - rwu.writer = true; - } - - // Important: if we both read/write, must do read second - // or else the write will override. - if (acc & ACC_READ) != 0 { - rwu.reader = true; - } - - if (acc & ACC_USE) != 0 { - rwu.used = true; - } - - self.rwu_table.set(ln, var, rwu); - } - - fn compute(&mut self, body: &hir::Body<'_>, hir_id: HirId) -> LiveNode { - debug!("compute: for body {:?}", body.id().hir_id); - - // # Liveness of captured variables - // - // When computing the liveness for captured variables we take into - // account how variable is captured (ByRef vs ByValue) and what is the - // closure kind (Coroutine / FnOnce vs Fn / FnMut). - // - // Variables captured by reference are assumed to be used on the exit - // from the closure. - // - // In FnOnce closures, variables captured by value are known to be dead - // on exit since it is impossible to call the closure again. - // - // In Fn / FnMut closures, variables captured by value are live on exit - // if they are live on the entry to the closure, since only the closure - // itself can access them on subsequent calls. - - if let Some(closure_min_captures) = self.closure_min_captures { - // Mark upvars captured by reference as used after closure exits. - for (&var_hir_id, min_capture_list) in closure_min_captures { - for captured_place in min_capture_list { - match captured_place.info.capture_kind { - ty::UpvarCapture::ByRef(_) => { - let var = self.variable( - var_hir_id, - captured_place.get_capture_kind_span(self.ir.tcx), - ); - self.acc(self.exit_ln, var, ACC_READ | ACC_USE); - } - ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} - } - } - } - } - - let succ = self.propagate_through_expr(body.value, self.exit_ln); - - if self.closure_min_captures.is_none() { - // Either not a closure, or closure without any captured variables. - // No need to determine liveness of captured variables, since there - // are none. - return succ; - } - - let ty = self.typeck_results.node_type(hir_id); - match ty.kind() { - ty::Closure(_def_id, args) => match args.as_closure().kind() { - ty::ClosureKind::Fn => {} - ty::ClosureKind::FnMut => {} - ty::ClosureKind::FnOnce => return succ, - }, - ty::CoroutineClosure(_def_id, args) => match args.as_coroutine_closure().kind() { - ty::ClosureKind::Fn => {} - ty::ClosureKind::FnMut => {} - ty::ClosureKind::FnOnce => return succ, - }, - ty::Coroutine(..) => return succ, - _ => { - span_bug!( - body.value.span, - "{} has upvars so it should have a closure type: {:?}", - hir_id, - ty - ); - } - }; - - // Propagate through calls to the closure. - loop { - self.init_from_succ(self.closure_ln, succ); - for param in body.params { - param.pat.each_binding(|_bm, hir_id, _x, ident| { - let var = self.variable(hir_id, ident.span); - self.define(self.closure_ln, var); - }) - } - - if !self.merge_from_succ(self.exit_ln, self.closure_ln) { - break; - } - assert_eq!(succ, self.propagate_through_expr(body.value, self.exit_ln)); - } - - succ - } - - fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode { - if blk.targeted_by_break { - self.break_ln.insert(blk.hir_id, succ); - } - let succ = self.propagate_through_opt_expr(blk.expr, succ); - blk.stmts.iter().rev().fold(succ, |succ, stmt| self.propagate_through_stmt(stmt, succ)) - } - - fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode { - match stmt.kind { - hir::StmtKind::Let(local) => { - // Note: we mark the variable as defined regardless of whether - // there is an initializer. Initially I had thought to only mark - // the live variable as defined if it was initialized, and then we - // could check for uninit variables just by scanning what is live - // at the start of the function. But that doesn't work so well for - // immutable variables defined in a loop: - // loop { let x; x = 5; } - // because the "assignment" loops back around and generates an error. - // - // So now we just check that variables defined w/o an - // initializer are not live at the point of their - // initialization, which is mildly more complex than checking - // once at the func header but otherwise equivalent. - - if let Some(els) = local.els { - // Eventually, `let pat: ty = init else { els };` is mostly equivalent to - // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };` - // except that extended lifetime applies at the `init` location. - // - // (e) - // | - // v - // (expr) - // / \ - // | | - // v v - // bindings els - // | - // v - // ( succ ) - // - if let Some(init) = local.init { - let else_ln = self.propagate_through_block(els, succ); - let ln = self.live_node(local.hir_id, local.span); - self.init_from_succ(ln, succ); - self.merge_from_succ(ln, else_ln); - let succ = self.propagate_through_expr(init, ln); - self.define_bindings_in_pat(local.pat, succ) - } else { - span_bug!( - stmt.span, - "variable is uninitialized but an unexpected else branch is found" - ) - } - } else { - let succ = self.propagate_through_opt_expr(local.init, succ); - self.define_bindings_in_pat(local.pat, succ) - } - } - hir::StmtKind::Item(..) => succ, - hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { - self.propagate_through_expr(expr, succ) - } - } - } - - fn propagate_through_exprs(&mut self, exprs: &[Expr<'_>], succ: LiveNode) -> LiveNode { - exprs.iter().rev().fold(succ, |succ, expr| self.propagate_through_expr(expr, succ)) - } - - fn propagate_through_opt_expr( - &mut self, - opt_expr: Option<&Expr<'_>>, - succ: LiveNode, - ) -> LiveNode { - opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ)) - } - - fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode { - debug!("propagate_through_expr: {:?}", expr); - - match expr.kind { - // Interesting cases with control flow or which gen/kill - hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { - self.access_path(expr.hir_id, path, succ, ACC_READ | ACC_USE) - } - - hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(e, succ), - - hir::ExprKind::Closure { .. } => { - debug!("{:?} is an ExprKind::Closure", expr); - - // the construction of a closure itself is not important, - // but we have to consider the closed over variables. - let caps = self - .ir - .capture_info_map - .get(&expr.hir_id) - .cloned() - .unwrap_or_else(|| span_bug!(expr.span, "no registered caps")); - - caps.iter().rev().fold(succ, |succ, cap| { - self.init_from_succ(cap.ln, succ); - let var = self.variable(cap.var_hid, expr.span); - self.acc(cap.ln, var, ACC_READ | ACC_USE); - cap.ln - }) - } - - hir::ExprKind::Let(let_expr) => { - let succ = self.propagate_through_expr(let_expr.init, succ); - self.define_bindings_in_pat(let_expr.pat, succ) - } - - // Note that labels have been resolved, so we don't need to look - // at the label ident - hir::ExprKind::Loop(ref blk, ..) => self.propagate_through_loop(expr, blk, succ), - - hir::ExprKind::Yield(e, ..) => { - let yield_ln = self.live_node(expr.hir_id, expr.span); - self.init_from_succ(yield_ln, succ); - self.merge_from_succ(yield_ln, self.exit_ln); - self.propagate_through_expr(e, yield_ln) - } - - hir::ExprKind::If(ref cond, ref then, ref else_opt) => { - // - // (cond) - // | - // v - // (expr) - // / \ - // | | - // v v - // (then)(els) - // | | - // v v - // ( succ ) - // - let else_ln = self.propagate_through_opt_expr(else_opt.as_deref(), succ); - let then_ln = self.propagate_through_expr(then, succ); - let ln = self.live_node(expr.hir_id, expr.span); - self.init_from_succ(ln, else_ln); - self.merge_from_succ(ln, then_ln); - self.propagate_through_expr(cond, ln) - } - - hir::ExprKind::Match(ref e, arms, _) => { - // - // (e) - // | - // v - // (expr) - // / | \ - // | | | - // v v v - // (..arms..) - // | | | - // v v v - // ( succ ) - // - // - let ln = self.live_node(expr.hir_id, expr.span); - self.init_empty(ln, succ); - for arm in arms { - let body_succ = self.propagate_through_expr(arm.body, succ); - - let guard_succ = arm - .guard - .as_ref() - .map_or(body_succ, |g| self.propagate_through_expr(g, body_succ)); - let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ); - self.merge_from_succ(ln, arm_succ); - } - self.propagate_through_expr(e, ln) - } - - hir::ExprKind::Ret(ref o_e) => { - // Ignore succ and subst exit_ln. - self.propagate_through_opt_expr(o_e.as_deref(), self.exit_ln) - } - - hir::ExprKind::Become(e) => { - // Ignore succ and subst exit_ln. - self.propagate_through_expr(e, self.exit_ln) - } - - hir::ExprKind::Break(label, ref opt_expr) => { - // Find which label this break jumps to - let target = match label.target_id { - Ok(hir_id) => self.break_ln.get(&hir_id), - Err(err) => span_bug!(expr.span, "loop scope error: {}", err), - } - .cloned(); - - // Now that we know the label we're going to, - // look it up in the break loop nodes table - - match target { - Some(b) => self.propagate_through_opt_expr(opt_expr.as_deref(), b), - None => span_bug!(expr.span, "`break` to unknown label"), - } - } - - hir::ExprKind::Continue(label) => { - // Find which label this expr continues to - let sc = label - .target_id - .unwrap_or_else(|err| span_bug!(expr.span, "loop scope error: {}", err)); - - // Now that we know the label we're going to, - // look it up in the continue loop nodes table - self.cont_ln.get(&sc).cloned().unwrap_or_else(|| { - // Liveness linting happens after building the THIR. Bad labels should already - // have been caught. - span_bug!(expr.span, "continue to unknown label"); - }) - } - - hir::ExprKind::Assign(ref l, ref r, _) => { - // see comment on places in - // propagate_through_place_components() - let succ = self.write_place(l, succ, ACC_WRITE); - let succ = self.propagate_through_place_components(l, succ); - self.propagate_through_expr(r, succ) - } - - hir::ExprKind::AssignOp(_, ref l, ref r) => { - // an overloaded assign op is like a method call - if self.typeck_results.is_method_call(expr) { - let succ = self.propagate_through_expr(l, succ); - self.propagate_through_expr(r, succ) - } else { - // see comment on places in - // propagate_through_place_components() - let succ = self.write_place(l, succ, ACC_WRITE | ACC_READ); - let succ = self.propagate_through_expr(r, succ); - self.propagate_through_place_components(l, succ) - } - } - - // Uninteresting cases: just propagate in rev exec order - hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ), - - hir::ExprKind::Struct(_, fields, ref with_expr) => { - let succ = match with_expr { - hir::StructTailExpr::Base(base) => { - self.propagate_through_opt_expr(Some(base), succ) - } - hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) => succ, - }; - fields - .iter() - .rev() - .fold(succ, |succ, field| self.propagate_through_expr(field.expr, succ)) - } - - hir::ExprKind::Call(ref f, args) => { - let is_ctor = |f: &Expr<'_>| matches!(f.kind, hir::ExprKind::Path(hir::QPath::Resolved(_, path)) if matches!(path.res, rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Ctor(_, _), _))); - let succ = - if !is_ctor(f) { self.check_is_ty_uninhabited(expr, succ) } else { succ }; - - let succ = self.propagate_through_exprs(args, succ); - self.propagate_through_expr(f, succ) - } - - hir::ExprKind::MethodCall(.., receiver, args, _) => { - let succ = self.check_is_ty_uninhabited(expr, succ); - let succ = self.propagate_through_exprs(args, succ); - self.propagate_through_expr(receiver, succ) - } - - hir::ExprKind::Use(expr, _) => { - let succ = self.check_is_ty_uninhabited(expr, succ); - self.propagate_through_expr(expr, succ) - } - - hir::ExprKind::Tup(exprs) => self.propagate_through_exprs(exprs, succ), - - hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => { - let r_succ = self.propagate_through_expr(r, succ); - - let ln = self.live_node(expr.hir_id, expr.span); - self.init_from_succ(ln, succ); - self.merge_from_succ(ln, r_succ); - - self.propagate_through_expr(l, ln) - } - - hir::ExprKind::Index(ref l, ref r, _) | hir::ExprKind::Binary(_, ref l, ref r) => { - let r_succ = self.propagate_through_expr(r, succ); - self.propagate_through_expr(l, r_succ) - } - - hir::ExprKind::AddrOf(_, _, ref e) - | hir::ExprKind::Cast(ref e, _) - | hir::ExprKind::Type(ref e, _) - | hir::ExprKind::UnsafeBinderCast(_, ref e, _) - | hir::ExprKind::DropTemps(ref e) - | hir::ExprKind::Unary(_, ref e) - | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ), - - hir::ExprKind::InlineAsm(asm) => { - // - // (inputs) - // | - // v - // (outputs) - // / \ - // | | - // v v - // (labels)(fallthrough) - // | | - // v v - // ( succ / exit_ln ) - - // Handle non-returning asm - let mut succ = - if self.typeck_results.expr_ty(expr).is_never() { self.exit_ln } else { succ }; - - // Do a first pass for labels only - if asm.contains_label() { - let ln = self.live_node(expr.hir_id, expr.span); - self.init_from_succ(ln, succ); - for (op, _op_sp) in asm.operands.iter().rev() { - match op { - hir::InlineAsmOperand::Label { block } => { - let label_ln = self.propagate_through_block(block, succ); - self.merge_from_succ(ln, label_ln); - } - hir::InlineAsmOperand::In { .. } - | hir::InlineAsmOperand::Out { .. } - | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } - | hir::InlineAsmOperand::Const { .. } - | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} - } - } - succ = ln; - } - - // Do a second pass for writing outputs only - for (op, _op_sp) in asm.operands.iter().rev() { - match op { - hir::InlineAsmOperand::In { .. } - | hir::InlineAsmOperand::Const { .. } - | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } - | hir::InlineAsmOperand::Label { .. } => {} - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - succ = self.write_place(expr, succ, ACC_WRITE); - } - } - hir::InlineAsmOperand::InOut { expr, .. } => { - succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE | ACC_USE); - } - hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { - if let Some(expr) = out_expr { - succ = self.write_place(expr, succ, ACC_WRITE); - } - } - } - } - - // Then do a third pass for inputs - for (op, _op_sp) in asm.operands.iter().rev() { - match op { - hir::InlineAsmOperand::In { expr, .. } => { - succ = self.propagate_through_expr(expr, succ) - } - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - succ = self.propagate_through_place_components(expr, succ); - } - } - hir::InlineAsmOperand::InOut { expr, .. } => { - succ = self.propagate_through_place_components(expr, succ); - } - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - if let Some(expr) = out_expr { - succ = self.propagate_through_place_components(expr, succ); - } - succ = self.propagate_through_expr(in_expr, succ); - } - hir::InlineAsmOperand::Const { .. } - | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } - | hir::InlineAsmOperand::Label { .. } => {} - } - } - succ - } - - hir::ExprKind::Lit(..) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Err(_) - | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) - | hir::ExprKind::Path(hir::QPath::LangItem(..)) - | hir::ExprKind::OffsetOf(..) => succ, - - // Note that labels have been resolved, so we don't need to look - // at the label ident - hir::ExprKind::Block(ref blk, _) => self.propagate_through_block(blk, succ), - } - } - - fn propagate_through_place_components(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode { - // # Places - // - // In general, the full flow graph structure for an - // assignment/move/etc can be handled in one of two ways, - // depending on whether what is being assigned is a "tracked - // value" or not. A tracked value is basically a local - // variable or argument. - // - // The two kinds of graphs are: - // - // Tracked place Untracked place - // ----------------------++----------------------- - // || - // | || | - // v || v - // (rvalue) || (rvalue) - // | || | - // v || v - // (write of place) || (place components) - // | || | - // v || v - // (succ) || (succ) - // || - // ----------------------++----------------------- - // - // I will cover the two cases in turn: - // - // # Tracked places - // - // A tracked place is a local variable/argument `x`. In - // these cases, the link_node where the write occurs is linked - // to node id of `x`. The `write_place()` routine generates - // the contents of this node. There are no subcomponents to - // consider. - // - // # Non-tracked places - // - // These are places like `x[5]` or `x.f`. In that case, we - // basically ignore the value which is written to but generate - // reads for the components---`x` in these two examples. The - // components reads are generated by - // `propagate_through_place_components()` (this fn). - // - // # Illegal places - // - // It is still possible to observe assignments to non-places; - // these errors are detected in the later pass borrowck. We - // just ignore such cases and treat them as reads. - - match expr.kind { - hir::ExprKind::Path(_) => succ, - hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(e, succ), - _ => self.propagate_through_expr(expr, succ), - } - } - - // see comment on propagate_through_place() - fn write_place(&mut self, expr: &Expr<'_>, succ: LiveNode, acc: u32) -> LiveNode { - match expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { - self.access_path(expr.hir_id, path, succ, acc) - } - - // We do not track other places, so just propagate through - // to their subcomponents. Also, it may happen that - // non-places occur here, because those are detected in the - // later pass borrowck. - _ => succ, - } - } - - fn access_var( - &mut self, - hir_id: HirId, - var_hid: HirId, - succ: LiveNode, - acc: u32, - span: Span, - ) -> LiveNode { - let ln = self.live_node(hir_id, span); - if acc != 0 { - self.init_from_succ(ln, succ); - let var = self.variable(var_hid, span); - self.acc(ln, var, acc); - } - ln - } - - fn access_path( - &mut self, - hir_id: HirId, - path: &hir::Path<'_>, - succ: LiveNode, - acc: u32, - ) -> LiveNode { - match path.res { - Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), - _ => succ, - } - } - - fn propagate_through_loop( - &mut self, - expr: &Expr<'_>, - body: &hir::Block<'_>, - succ: LiveNode, - ) -> LiveNode { - /* - We model control flow like this: - - (expr) <-+ - | | - v | - (body) --+ - - Note that a `continue` expression targeting the `loop` will have a successor of `expr`. - Meanwhile, a `break` expression will have a successor of `succ`. - */ - - // first iteration: - let ln = self.live_node(expr.hir_id, expr.span); - self.init_empty(ln, succ); - debug!("propagate_through_loop: using id for loop body {} {:?}", expr.hir_id, body); - - self.break_ln.insert(expr.hir_id, succ); - - self.cont_ln.insert(expr.hir_id, ln); - - let body_ln = self.propagate_through_block(body, ln); - - // repeat until fixed point is reached: - while self.merge_from_succ(ln, body_ln) { - assert_eq!(body_ln, self.propagate_through_block(body, ln)); - } - - ln - } - - fn check_is_ty_uninhabited(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode { - let ty = self.typeck_results.expr_ty(expr); - let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id(); - if ty.is_inhabited_from(self.ir.tcx, m, self.typing_env) { succ } else { self.exit_ln } - } -} - -// _______________________________________________________________________ -// Checking for error conditions - -impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { - self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| { - if local.init.is_some() { - self.warn_about_dead_assign(spans, hir_id, ln, var, None); - } - }); - - intravisit::walk_local(self, local); - } - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - check_expr(self, ex); - intravisit::walk_expr(self, ex); - } - - fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.check_unused_vars_in_pat(arm.pat, None, None, |_, _, _, _| {}); - intravisit::walk_arm(self, arm); - } -} - -fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Assign(ref l, ..) => { - this.check_place(l); - } - - hir::ExprKind::AssignOp(_, ref l, _) => { - if !this.typeck_results.is_method_call(expr) { - this.check_place(l); - } - } - - hir::ExprKind::InlineAsm(asm) => { - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - this.check_place(expr); - } - } - hir::InlineAsmOperand::InOut { expr, .. } => { - this.check_place(expr); - } - hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { - if let Some(out_expr) = out_expr { - this.check_place(out_expr); - } - } - _ => {} - } - } - } - - hir::ExprKind::Let(let_expr) => { - this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {}); - } - - // no correctness conditions related to liveness - hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) - | hir::ExprKind::Use(..) - | hir::ExprKind::Match(..) - | hir::ExprKind::Loop(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Field(..) - | hir::ExprKind::Array(..) - | hir::ExprKind::Tup(..) - | hir::ExprKind::Binary(..) - | hir::ExprKind::Cast(..) - | hir::ExprKind::If(..) - | hir::ExprKind::DropTemps(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Ret(..) - | hir::ExprKind::Become(..) - | hir::ExprKind::Break(..) - | hir::ExprKind::Continue(..) - | hir::ExprKind::Lit(_) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Block(..) - | hir::ExprKind::AddrOf(..) - | hir::ExprKind::OffsetOf(..) - | hir::ExprKind::Struct(..) - | hir::ExprKind::Repeat(..) - | hir::ExprKind::Closure { .. } - | hir::ExprKind::Path(_) - | hir::ExprKind::Yield(..) - | hir::ExprKind::Type(..) - | hir::ExprKind::UnsafeBinderCast(..) - | hir::ExprKind::Err(_) => {} - } -} - -impl<'tcx> Liveness<'_, 'tcx> { - fn check_place(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { - if let Res::Local(var_hid) = path.res { - // Assignment to an immutable variable or argument: only legal - // if there is no later assignment. If this local is actually - // mutable, then check for a reassignment to flag the mutability - // as being used. - let ln = self.live_node(expr.hir_id, expr.span); - let var = self.variable(var_hid, expr.span); - let sugg = self.annotate_mut_binding_to_immutable_binding(var_hid, expr); - self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var, sugg); - } - } - _ => { - // For other kinds of places, no checks are required, - // and any embedded expressions are actually rvalues - intravisit::walk_expr(self, expr); - } - } - } - - fn should_warn(&self, var: Variable) -> Option { - let name = self.ir.variable_name(var); - let name = name.as_str(); - if name.as_bytes()[0] == b'_' { - return None; - } - Some(name.to_owned()) - } - - fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let Some(closure_min_captures) = self.closure_min_captures else { - return; - }; - - // If closure_min_captures is Some(), upvars must be Some() too. - for (&var_hir_id, min_capture_list) in closure_min_captures { - for captured_place in min_capture_list { - match captured_place.info.capture_kind { - ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} - ty::UpvarCapture::ByRef(..) => continue, - }; - let span = captured_place.get_capture_kind_span(self.ir.tcx); - let var = self.variable(var_hir_id, span); - if self.used_on_entry(entry_ln, var) { - if !self.live_on_entry(entry_ln, var) { - if let Some(name) = self.should_warn(var) { - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, - var_hir_id, - vec![span], - errors::UnusedCaptureMaybeCaptureRef { name }, - ); - } - } - } else if let Some(name) = self.should_warn(var) { - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, - var_hir_id, - vec![span], - errors::UnusedVarMaybeCaptureRef { name }, - ); - } - } - } - } - - fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { - if let Some(intrinsic) = self.ir.tcx.intrinsic(self.ir.tcx.hir_body_owner_def_id(body.id())) - { - if intrinsic.must_be_overridden { - return; - } - } - - for p in body.params { - self.check_unused_vars_in_pat( - p.pat, - Some(entry_ln), - Some(body), - |spans, hir_id, ln, var| { - if !self.live_on_entry(ln, var) - && let Some(name) = self.should_warn(var) - { - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - errors::UnusedAssignPassed { name }, - ); - } - }, - ); - } - } - - fn check_unused_vars_in_pat( - &self, - pat: &hir::Pat<'_>, - entry_ln: Option, - opt_body: Option<&hir::Body<'_>>, - on_used_on_entry: impl Fn(Vec, HirId, LiveNode, Variable), - ) { - // In an or-pattern, only consider the variable; any later patterns must have the same - // bindings, and we also consider the first pattern to be the "authoritative" set of ids. - // However, we should take the ids and spans of variables with the same name from the later - // patterns so the suggestions to prefix with underscores will apply to those too. - let mut vars: FxIndexMap)> = - <_>::default(); - - pat.each_binding(|_, hir_id, pat_sp, ident| { - let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp)); - let var = self.variable(hir_id, ident.span); - let id_and_sp = (hir_id, pat_sp, ident.span); - vars.entry(self.ir.variable_name(var)) - .and_modify(|(.., hir_ids_and_spans)| hir_ids_and_spans.push(id_and_sp)) - .or_insert_with(|| (ln, var, vec![id_and_sp])); - }); - - let can_remove = match pat.kind { - hir::PatKind::Struct(_, fields, Some(_)) => { - // if all fields are shorthand, remove the struct field, otherwise, mark with _ as prefix - fields.iter().all(|f| f.is_shorthand) - } - _ => false, - }; - - for (_, (ln, var, hir_ids_and_spans)) in vars { - if self.used_on_entry(ln, var) { - let id = hir_ids_and_spans[0].0; - let spans = - hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect(); - on_used_on_entry(spans, id, ln, var); - } else { - self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body); - } - } - } - - /// Detect the following case - /// - /// ```text - /// fn change_object(mut a: &Ty) { - /// let a = Ty::new(); - /// b = &a; - /// } - /// ``` - /// - /// where the user likely meant to modify the value behind there reference, use `a` as an out - /// parameter, instead of mutating the local binding. When encountering this we suggest: - /// - /// ```text - /// fn change_object(a: &'_ mut Ty) { - /// let a = Ty::new(); - /// *b = a; - /// } - /// ``` - fn annotate_mut_binding_to_immutable_binding( - &self, - var_hid: HirId, - expr: &'tcx Expr<'tcx>, - ) -> Option { - if let hir::Node::Expr(parent) = self.ir.tcx.parent_hir_node(expr.hir_id) - && let hir::ExprKind::Assign(_, rhs, _) = parent.kind - && let hir::ExprKind::AddrOf(borrow_kind, _mut, inner) = rhs.kind - && let hir::BorrowKind::Ref = borrow_kind - && let hir::Node::Pat(pat) = self.ir.tcx.hir_node(var_hid) - && let hir::Node::Param(hir::Param { ty_span, .. }) = - self.ir.tcx.parent_hir_node(pat.hir_id) - && let item_id = self.ir.tcx.hir_get_parent_item(pat.hir_id) - && let item = self.ir.tcx.hir_owner_node(item_id) - && let Some(fn_decl) = item.fn_decl() - && let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind - && let Some((lt, mut_ty)) = fn_decl - .inputs - .iter() - .filter_map(|ty| { - if ty.span == *ty_span - && let hir::TyKind::Ref(lt, mut_ty) = ty.kind - { - Some((lt, mut_ty)) - } else { - None - } - }) - .next() - { - let ty_span = if mut_ty.mutbl.is_mut() { - // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). - None - } else { - // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` - Some(mut_ty.ty.span.shrink_to_lo()) - }; - let pre = if lt.ident.span.is_empty() { "" } else { " " }; - Some(errors::UnusedAssignSuggestion { - ty_span, - pre, - ty_ref_span: pat.span.until(ident.span), - ident_span: expr.span.shrink_to_lo(), - expr_ref_span: rhs.span.until(inner.span), - }) - } else { - None - } - } - - #[instrument(skip(self), level = "INFO")] - fn report_unused( - &self, - hir_ids_and_spans: Vec<(HirId, Span, Span)>, - ln: LiveNode, - var: Variable, - can_remove: bool, - pat: &hir::Pat<'_>, - opt_body: Option<&hir::Body<'_>>, - ) { - let first_hir_id = hir_ids_and_spans[0].0; - if let Some(name) = self.should_warn(var).filter(|name| name != "self") { - // annoying: for parameters in funcs like `fn(x: i32) - // {ret}`, there is only one node, so asking about - // assigned_on_exit() is not meaningful. - let is_assigned = - if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; - - let mut typo = None; - let filtered_hir_ids_and_spans = hir_ids_and_spans.iter().filter(|(hir_id, ..)| { - !matches!(self.ir.tcx.parent_hir_node(*hir_id), hir::Node::Param(_)) - }); - for (hir_id, _, span) in filtered_hir_ids_and_spans.clone() { - let ty = self.typeck_results.node_type(*hir_id); - if let ty::Adt(adt, _) = ty.peel_refs().kind() { - let name = Symbol::intern(&name); - let adt_def = self.ir.tcx.adt_def(adt.did()); - let variant_names: Vec<_> = adt_def - .variants() - .iter() - .filter(|v| matches!(v.ctor, Some((CtorKind::Const, _)))) - .map(|v| v.name) - .collect(); - if let Some(name) = find_best_match_for_name(&variant_names, name, None) - && let Some(variant) = adt_def.variants().iter().find(|v| { - v.name == name && matches!(v.ctor, Some((CtorKind::Const, _))) - }) - { - typo = Some(errors::PatternTypo { - span: *span, - code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(variant.def_id)), - kind: self.ir.tcx.def_descr(variant.def_id).to_string(), - item_name: variant.name.to_string(), - }); - } - } - } - if typo.is_none() { - for (hir_id, _, span) in filtered_hir_ids_and_spans { - let ty = self.typeck_results.node_type(*hir_id); - // Look for consts of the same type with similar names as well, not just unit - // structs and variants. - for def_id in self.ir.tcx.hir_body_owners() { - if let DefKind::Const = self.ir.tcx.def_kind(def_id) - && self.ir.tcx.type_of(def_id).instantiate_identity() == ty - { - typo = Some(errors::PatternTypo { - span: *span, - code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(def_id)), - kind: "constant".to_string(), - item_name: self.ir.tcx.item_name(def_id).to_string(), - }); - } - } - } - } - if is_assigned { - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, - first_hir_id, - hir_ids_and_spans - .into_iter() - .map(|(_, _, ident_span)| ident_span) - .collect::>(), - errors::UnusedVarAssignedOnly { name, typo }, - ) - } else if can_remove { - let spans = hir_ids_and_spans - .iter() - .map(|(_, pat_span, _)| { - let span = self - .ir - .tcx - .sess - .source_map() - .span_extend_to_next_char(*pat_span, ',', true); - span.with_hi(BytePos(span.hi().0 + 1)) - }) - .collect(); - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, - first_hir_id, - hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::>(), - errors::UnusedVarRemoveField { - name, - sugg: errors::UnusedVarRemoveFieldSugg { spans }, - }, - ); - } else { - let (shorthands, non_shorthands): (Vec<_>, Vec<_>) = - hir_ids_and_spans.iter().copied().partition(|(hir_id, _, ident_span)| { - let var = self.variable(*hir_id, *ident_span); - self.ir.variable_is_shorthand(var) - }); - - // If we have both shorthand and non-shorthand, prefer the "try ignoring - // the field" message, and suggest `_` for the non-shorthands. If we only - // have non-shorthand, then prefix with an underscore instead. - if !shorthands.is_empty() { - let shorthands = - shorthands.into_iter().map(|(_, pat_span, _)| pat_span).collect(); - let non_shorthands = - non_shorthands.into_iter().map(|(_, pat_span, _)| pat_span).collect(); - - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, - first_hir_id, - hir_ids_and_spans - .iter() - .map(|(_, pat_span, _)| *pat_span) - .collect::>(), - errors::UnusedVarTryIgnore { - name: name.clone(), - sugg: errors::UnusedVarTryIgnoreSugg { - shorthands, - non_shorthands, - name, - }, - }, - ); - } else { - // #117284, when `pat_span` and `ident_span` have different contexts - // we can't provide a good suggestion, instead we pointed out the spans from macro - let from_macro = non_shorthands - .iter() - .find(|(_, pat_span, ident_span)| { - !pat_span.eq_ctxt(*ident_span) && pat_span.from_expansion() - }) - .map(|(_, pat_span, _)| *pat_span); - let non_shorthands = non_shorthands - .into_iter() - .map(|(_, _, ident_span)| ident_span) - .collect::>(); - - let suggestions = self.string_interp_suggestions(&name, opt_body); - let sugg = if let Some(span) = from_macro { - errors::UnusedVariableSugg::NoSugg { span, name: name.clone() } - } else { - errors::UnusedVariableSugg::TryPrefixSugg { - spans: non_shorthands, - name: name.clone(), - } - }; - - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, - first_hir_id, - hir_ids_and_spans - .iter() - .map(|(_, _, ident_span)| *ident_span) - .collect::>(), - errors::UnusedVariableTryPrefix { - label: if !suggestions.is_empty() { Some(pat.span) } else { None }, - name, - sugg, - string_interp: suggestions, - typo, - }, - ); - } - } - } - } - - fn string_interp_suggestions( - &self, - name: &str, - opt_body: Option<&hir::Body<'_>>, - ) -> Vec { - let mut suggs = Vec::new(); - let Some(opt_body) = opt_body else { - return suggs; - }; - let mut visitor = CollectLitsVisitor { lit_exprs: vec![] }; - intravisit::walk_body(&mut visitor, opt_body); - for lit_expr in visitor.lit_exprs { - let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue }; - let rustc_ast::LitKind::Str(syb, _) = litx.node else { - continue; - }; - let name_str: &str = syb.as_str(); - let name_pa = format!("{{{name}}}"); - if name_str.contains(&name_pa) { - suggs.push(errors::UnusedVariableStringInterp { - lit: lit_expr.span, - lo: lit_expr.span.shrink_to_lo(), - hi: lit_expr.span.shrink_to_hi(), - }); - } - } - suggs - } - - fn warn_about_dead_assign( - &self, - spans: Vec, - hir_id: HirId, - ln: LiveNode, - var: Variable, - suggestion: Option, - ) { - if !self.live_on_exit(ln, var) - && let Some(name) = self.should_warn(var) - { - let help = suggestion.is_none(); - self.ir.tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - errors::UnusedAssign { name, suggestion, help }, - ); - } - } -} diff --git a/compiler/rustc_passes/src/liveness/rwu_table.rs b/compiler/rustc_passes/src/liveness/rwu_table.rs deleted file mode 100644 index a1177946f8684..0000000000000 --- a/compiler/rustc_passes/src/liveness/rwu_table.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::iter; - -use crate::liveness::{LiveNode, Variable}; - -#[derive(Clone, Copy)] -pub(super) struct RWU { - pub(super) reader: bool, - pub(super) writer: bool, - pub(super) used: bool, -} - -/// Conceptually, this is like a `Vec>`. But the number of -/// RWU's can get very large, so it uses a more compact representation. -pub(super) struct RWUTable { - /// Total number of live nodes. - live_nodes: usize, - /// Total number of variables. - vars: usize, - - /// A compressed representation of `RWU`s. - /// - /// Each word represents 2 different `RWU`s packed together. Each packed RWU - /// is stored in 4 bits: a reader bit, a writer bit, a used bit and a - /// padding bit. - /// - /// The data for each live node is contiguous and starts at a word boundary, - /// so there might be an unused space left. - words: Vec, - /// Number of words per each live node. - live_node_words: usize, -} - -impl RWUTable { - const RWU_READER: u8 = 0b0001; - const RWU_WRITER: u8 = 0b0010; - const RWU_USED: u8 = 0b0100; - const RWU_MASK: u8 = 0b1111; - - /// Size of packed RWU in bits. - const RWU_BITS: usize = 4; - /// Size of a word in bits. - const WORD_BITS: usize = size_of::() * 8; - /// Number of packed RWUs that fit into a single word. - const WORD_RWU_COUNT: usize = Self::WORD_BITS / Self::RWU_BITS; - - pub(super) fn new(live_nodes: usize, vars: usize) -> RWUTable { - let live_node_words = vars.div_ceil(Self::WORD_RWU_COUNT); - Self { live_nodes, vars, live_node_words, words: vec![0u8; live_node_words * live_nodes] } - } - - fn word_and_shift(&self, ln: LiveNode, var: Variable) -> (usize, u32) { - assert!(ln.index() < self.live_nodes); - assert!(var.index() < self.vars); - - let var = var.index(); - let word = var / Self::WORD_RWU_COUNT; - let shift = Self::RWU_BITS * (var % Self::WORD_RWU_COUNT); - (ln.index() * self.live_node_words + word, shift as u32) - } - - fn pick2_rows_mut(&mut self, a: LiveNode, b: LiveNode) -> (&mut [u8], &mut [u8]) { - assert!(a.index() < self.live_nodes); - assert!(b.index() < self.live_nodes); - assert!(a != b); - - let a_start = a.index() * self.live_node_words; - let b_start = b.index() * self.live_node_words; - - unsafe { - let ptr = self.words.as_mut_ptr(); - ( - std::slice::from_raw_parts_mut(ptr.add(a_start), self.live_node_words), - std::slice::from_raw_parts_mut(ptr.add(b_start), self.live_node_words), - ) - } - } - - pub(super) fn copy(&mut self, dst: LiveNode, src: LiveNode) { - if dst == src { - return; - } - - let (dst_row, src_row) = self.pick2_rows_mut(dst, src); - dst_row.copy_from_slice(src_row); - } - - /// Sets `dst` to the union of `dst` and `src`, returns true if `dst` was - /// changed. - pub(super) fn union(&mut self, dst: LiveNode, src: LiveNode) -> bool { - if dst == src { - return false; - } - - let mut changed = false; - let (dst_row, src_row) = self.pick2_rows_mut(dst, src); - for (dst_word, src_word) in iter::zip(dst_row, &*src_row) { - let old = *dst_word; - let new = *dst_word | src_word; - *dst_word = new; - changed |= old != new; - } - changed - } - - pub(super) fn get_reader(&self, ln: LiveNode, var: Variable) -> bool { - let (word, shift) = self.word_and_shift(ln, var); - (self.words[word] >> shift) & Self::RWU_READER != 0 - } - - pub(super) fn get_writer(&self, ln: LiveNode, var: Variable) -> bool { - let (word, shift) = self.word_and_shift(ln, var); - (self.words[word] >> shift) & Self::RWU_WRITER != 0 - } - - pub(super) fn get_used(&self, ln: LiveNode, var: Variable) -> bool { - let (word, shift) = self.word_and_shift(ln, var); - (self.words[word] >> shift) & Self::RWU_USED != 0 - } - - pub(super) fn get(&self, ln: LiveNode, var: Variable) -> RWU { - let (word, shift) = self.word_and_shift(ln, var); - let rwu_packed = self.words[word] >> shift; - RWU { - reader: rwu_packed & Self::RWU_READER != 0, - writer: rwu_packed & Self::RWU_WRITER != 0, - used: rwu_packed & Self::RWU_USED != 0, - } - } - - pub(super) fn set(&mut self, ln: LiveNode, var: Variable, rwu: RWU) { - let mut packed = 0; - if rwu.reader { - packed |= Self::RWU_READER; - } - if rwu.writer { - packed |= Self::RWU_WRITER; - } - if rwu.used { - packed |= Self::RWU_USED; - } - - let (word, shift) = self.word_and_shift(ln, var); - let word = &mut self.words[word]; - *word = (*word & !(Self::RWU_MASK << shift)) | (packed << shift) - } -} diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index 41acf44023f63..57273012a192b 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index 936653b961bbb..3eb577868f2b4 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index ad30c94f34781..2942f64741e91 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 505afb340009a..f2da414e9f652 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/asm/x86_64/goto.rs b/tests/ui/asm/x86_64/goto.rs index c1dbce0d1c912..455bb3239895b 100644 --- a/tests/ui/asm/x86_64/goto.rs +++ b/tests/ui/asm/x86_64/goto.rs @@ -69,6 +69,7 @@ fn goto_out_jump() { fn goto_out_jump_noreturn() { unsafe { let mut value = false; + //~^ WARN value assigned to `value` is never read let mut out: usize; asm!( "lea {}, [{} + 1]", diff --git a/tests/ui/asm/x86_64/goto.stderr b/tests/ui/asm/x86_64/goto.stderr index f8f09f32f6cbc..230faf8001136 100644 --- a/tests/ui/asm/x86_64/goto.stderr +++ b/tests/ui/asm/x86_64/goto.stderr @@ -18,5 +18,14 @@ note: the lint level is defined here LL | #[warn(unreachable_code)] | ^^^^^^^^^^^^^^^^ -warning: 1 warning emitted +warning: value assigned to `value` is never read + --> $DIR/goto.rs:70:25 + | +LL | let mut value = false; + | ^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` on by default + +warning: 2 warnings emitted diff --git a/tests/ui/async-await/async-closures/precise-captures.rs b/tests/ui/async-await/async-closures/precise-captures.rs index 638fb67c3a465..d654906e8d4a1 100644 --- a/tests/ui/async-await/async-closures/precise-captures.rs +++ b/tests/ui/async-await/async-closures/precise-captures.rs @@ -10,6 +10,7 @@ // in . #![allow(unused_mut)] +#![allow(unused_assignments)] extern crate block_on; diff --git a/tests/ui/borrowck/borrowck-assign-to-subfield.rs b/tests/ui/borrowck/borrowck-assign-to-subfield.rs index 27d9046e7b7cb..8e5192611ffd9 100644 --- a/tests/ui/borrowck/borrowck-assign-to-subfield.rs +++ b/tests/ui/borrowck/borrowck-assign-to-subfield.rs @@ -1,5 +1,6 @@ //@ run-pass +#[allow(unused)] pub fn main() { struct A { a: isize, diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs index c550af21f0753..641066b002273 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs @@ -13,13 +13,11 @@ struct Point { pub fn f() { let mut a = 1; - let mut c = Point{ x:1, y:0 }; + let mut c = Point { x: 1, y: 0 }; // Captured by value, but variable is dead on entry. (move || { - // This will not trigger a warning for unused variable as - // c.x will be treated as a Non-tracked place - c.x = 1; + c.x = 1; //~ WARN value captured by `c.x` is never read println!("{}", c.x); a = 1; //~ WARN value captured by `a` is never read println!("{}", a); @@ -27,10 +25,10 @@ pub fn f() { // Read and written to, but never actually used. (move || { - // This will not trigger a warning for unused variable as - // c.x will be treated as a Non-tracked place - c.x += 1; - a += 1; //~ WARN unused variable: `a` + c.x += 1; //~ WARN value captured by `c.x` is never read + //~| WARN value assigned to `c.x` is never read + a += 1; //~ WARN value captured by `a` is never read + //~| WARN value assigned to `a` is never read })(); (move || { @@ -45,10 +43,7 @@ pub fn f() { let b = Box::new(42); (move || { println!("{}", c.x); - // Never read because this is FnOnce closure. - // This will not trigger a warning for unused variable as - // c.x will be treated as a Non-tracked place - c.x += 1; + c.x += 1; //~ WARN value assigned to `c.x` is never read println!("{}", a); a += 1; //~ WARN value assigned to `a` is never read drop(b); @@ -56,35 +51,32 @@ pub fn f() { } #[derive(Debug)] -struct MyStruct<'a> { - x: Option<& 'a str>, +struct MyStruct<'a> { + x: Option<&'a str>, y: i32, } pub fn nested() { - let mut a : Option<& str>; + let mut a: Option<&str>; a = None; - let mut b : Option<& str>; + let mut b: Option<&str>; b = None; - let mut d = MyStruct{ x: None, y: 1}; - let mut e = MyStruct{ x: None, y: 1}; + let mut d = MyStruct { x: None, y: 1 }; + let mut e = MyStruct { x: None, y: 1 }; (|| { (|| { - // This will not trigger a warning for unused variable as - // d.x will be treated as a Non-tracked place - d.x = Some("d1"); + d.x = Some("d1"); //~ WARN value assigned to `d.x` is never read d.x = Some("d2"); a = Some("d1"); //~ WARN value assigned to `a` is never read a = Some("d2"); })(); (move || { - // This will not trigger a warning for unused variable as - //e.x will be treated as a Non-tracked place - e.x = Some("e1"); - e.x = Some("e2"); - b = Some("e1"); //~ WARN value assigned to `b` is never read - //~| WARN unused variable: `b` - b = Some("e2"); //~ WARN value assigned to `b` is never read + e.x = Some("e1"); //~ WARN value captured by `e.x` is never read + //~| WARN value assigned to `e.x` is never read + e.x = Some("e2"); //~ WARN value assigned to `e.x` is never read + b = Some("e1"); //~ WARN value captured by `b` is never read + //~| WARN value assigned to `b` is never read + b = Some("e2"); //~ WARN value assigned to `b` is never read })(); })(); } diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr index cf414adc0b943..f697662d3ee93 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr @@ -1,10 +1,10 @@ -warning: value captured by `a` is never read - --> $DIR/liveness.rs:24:9 +warning: value assigned to `c.x` is never read + --> $DIR/liveness.rs:46:9 | -LL | a = 1; - | ^ +LL | c.x += 1; + | ^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? note: the lint level is defined here --> $DIR/liveness.rs:5:9 | @@ -12,54 +12,125 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` -warning: unused variable: `a` - --> $DIR/liveness.rs:33:9 +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:48:9 + | +LL | a += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `c.x` is never read + --> $DIR/liveness.rs:28:9 + | +LL | c.x += 1; + | ^^^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `c.x` is never read + --> $DIR/liveness.rs:28:9 + | +LL | c.x += 1; + | ^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `a` is never read + --> $DIR/liveness.rs:30:9 | LL | a += 1; | ^ | = help: did you mean to capture by reference instead? - = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: value assigned to `a` is never read - --> $DIR/liveness.rs:53:9 + --> $DIR/liveness.rs:30:9 | LL | a += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `c.x` is never read + --> $DIR/liveness.rs:20:9 + | +LL | c.x = 1; + | ^^^ + | + = help: did you mean to capture by reference instead? + +warning: value captured by `a` is never read + --> $DIR/liveness.rs:22:9 + | +LL | a = 1; | ^ | + = help: did you mean to capture by reference instead? + +warning: value captured by `e.x` is never read + --> $DIR/liveness.rs:74:13 + | +LL | e.x = Some("e1"); + | ^^^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `e.x` is never read + --> $DIR/liveness.rs:74:13 + | +LL | e.x = Some("e1"); + | ^^^^^^^^^^^^^^^^ + | = help: maybe it is overwritten before being read? -warning: value assigned to `a` is never read +warning: value assigned to `e.x` is never read + --> $DIR/liveness.rs:76:13 + | +LL | e.x = Some("e2"); + | ^^^^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `b` is never read --> $DIR/liveness.rs:77:13 | -LL | a = Some("d1"); +LL | b = Some("e1"); | ^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? warning: value assigned to `b` is never read - --> $DIR/liveness.rs:85:13 + --> $DIR/liveness.rs:77:13 | LL | b = Some("e1"); - | ^ + | ^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? warning: value assigned to `b` is never read - --> $DIR/liveness.rs:87:13 + --> $DIR/liveness.rs:79:13 | LL | b = Some("e2"); - | ^ + | ^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: unused variable: `b` - --> $DIR/liveness.rs:85:13 +warning: value assigned to `d.x` is never read + --> $DIR/liveness.rs:68:13 | -LL | b = Some("e1"); - | ^ +LL | d.x = Some("d1"); + | ^^^^^^^^^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? + +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:70:13 + | +LL | a = Some("d1"); + | ^^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? -warning: 7 warnings emitted +warning: 16 warnings emitted diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs index bcd4d1090a411..2d4451d988ebc 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs @@ -12,14 +12,16 @@ struct MyStruct { pub fn unintentional_copy_one() { let mut a = 1; - let mut last = MyStruct{ a: 1, b: 1}; + //~^ WARN unused variable: `a` + let mut last = MyStruct { a: 1, b: 1 }; + //~^ WARN unused variable: `last` let mut f = move |s| { - // This will not trigger a warning for unused variable - // as last.a will be treated as a Non-tracked place last.a = s; + //~^ WARN value captured by `last.a` is never read + //~| WARN value assigned to `last.a` is never read a = s; - //~^ WARN value assigned to `a` is never read - //~| WARN unused variable: `a` + //~^ WARN value captured by `a` is never read + //~| WARN value assigned to `a` is never read }; f(2); f(3); @@ -28,12 +30,16 @@ pub fn unintentional_copy_one() { pub fn unintentional_copy_two() { let mut a = 1; - let mut sum = MyStruct{ a: 1, b: 0}; + //~^ WARN unused variable: `a` + let mut sum = MyStruct { a: 1, b: 0 }; + //~^ WARN unused variable: `sum` (1..10).for_each(move |x| { - // This will not trigger a warning for unused variable - // as sum.b will be treated as a Non-tracked place sum.b += x; - a += x; //~ WARN unused variable: `a` + //~^ WARN value captured by `sum.b` is never read + //~| WARN value assigned to `sum.b` is never read + a += x; + //~^ WARN value captured by `a` is never read + //~| WARN value assigned to `a` is never read }); } diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr index 0410de4c7982a..35b6d547eece0 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr @@ -1,10 +1,10 @@ -warning: value assigned to `a` is never read - --> $DIR/liveness_unintentional_copy.rs:20:9 +warning: value captured by `last.a` is never read + --> $DIR/liveness_unintentional_copy.rs:19:9 | -LL | a = s; - | ^ +LL | last.a = s; + | ^^^^^^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? note: the lint level is defined here --> $DIR/liveness_unintentional_copy.rs:4:9 | @@ -12,22 +12,87 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` -warning: unused variable: `a` - --> $DIR/liveness_unintentional_copy.rs:20:9 +warning: value assigned to `last.a` is never read + --> $DIR/liveness_unintentional_copy.rs:19:9 + | +LL | last.a = s; + | ^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `a` is never read + --> $DIR/liveness_unintentional_copy.rs:22:9 | LL | a = s; | ^ | = help: did you mean to capture by reference instead? - = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: value assigned to `a` is never read + --> $DIR/liveness_unintentional_copy.rs:22:9 + | +LL | a = s; + | ^^^^^ + | + = help: maybe it is overwritten before being read? warning: unused variable: `a` - --> $DIR/liveness_unintentional_copy.rs:36:9 + --> $DIR/liveness_unintentional_copy.rs:14:9 + | +LL | let mut a = 1; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_a` + | + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `last` + --> $DIR/liveness_unintentional_copy.rs:16:9 + | +LL | let mut last = MyStruct { a: 1, b: 1 }; + | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_last` + +warning: value captured by `sum.b` is never read + --> $DIR/liveness_unintentional_copy.rs:37:9 + | +LL | sum.b += x; + | ^^^^^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `sum.b` is never read + --> $DIR/liveness_unintentional_copy.rs:37:9 + | +LL | sum.b += x; + | ^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `a` is never read + --> $DIR/liveness_unintentional_copy.rs:40:9 | LL | a += x; | ^ | = help: did you mean to capture by reference instead? -warning: 3 warnings emitted +warning: value assigned to `a` is never read + --> $DIR/liveness_unintentional_copy.rs:40:9 + | +LL | a += x; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `a` + --> $DIR/liveness_unintentional_copy.rs:32:9 + | +LL | let mut a = 1; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_a` + +warning: unused variable: `sum` + --> $DIR/liveness_unintentional_copy.rs:34:9 + | +LL | let mut sum = MyStruct { a: 1, b: 0 }; + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_sum` + +warning: 12 warnings emitted diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr index cf8bd7a0a2765..40274c88318c3 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr +++ b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr @@ -1,8 +1,8 @@ -warning: unused variable: `g2` - --> $DIR/destructure-pattern-closure-within-closure.rs:10:17 +warning: unused variable: `t2` + --> $DIR/destructure-pattern-closure-within-closure.rs:13:21 | -LL | let (_, g2) = g; - | ^^ help: if this is intentional, prefix it with an underscore: `_g2` +LL | let (_, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` | note: the lint level is defined here --> $DIR/destructure-pattern-closure-within-closure.rs:3:9 @@ -11,11 +11,11 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` -warning: unused variable: `t2` - --> $DIR/destructure-pattern-closure-within-closure.rs:13:21 +warning: unused variable: `g2` + --> $DIR/destructure-pattern-closure-within-closure.rs:10:17 | -LL | let (_, t2) = t; - | ^^ help: if this is intentional, prefix it with an underscore: `_t2` +LL | let (_, g2) = g; + | ^^ help: if this is intentional, prefix it with an underscore: `_g2` warning: 2 warnings emitted diff --git a/tests/ui/closures/moved-upvar-mut-rebind-11958.rs b/tests/ui/closures/moved-upvar-mut-rebind-11958.rs index 701dc1a2cefd2..f308028bdd7b6 100644 --- a/tests/ui/closures/moved-upvar-mut-rebind-11958.rs +++ b/tests/ui/closures/moved-upvar-mut-rebind-11958.rs @@ -7,7 +7,8 @@ pub fn main() { let mut x = 1; + //~^ WARN unused variable: `x` let _thunk = Box::new(move|| { x = 2; }); - //~^ WARN value assigned to `x` is never read - //~| WARN unused variable: `x` + //~^ WARN value captured by `x` is never read + //~| WARN value assigned to `x` is never read } diff --git a/tests/ui/closures/moved-upvar-mut-rebind-11958.stderr b/tests/ui/closures/moved-upvar-mut-rebind-11958.stderr index 1bf8a8b23a1d0..e28db65392a4b 100644 --- a/tests/ui/closures/moved-upvar-mut-rebind-11958.stderr +++ b/tests/ui/closures/moved-upvar-mut-rebind-11958.stderr @@ -1,20 +1,27 @@ -warning: value assigned to `x` is never read - --> $DIR/moved-upvar-mut-rebind-11958.rs:10:36 +warning: value captured by `x` is never read + --> $DIR/moved-upvar-mut-rebind-11958.rs:11:36 | LL | let _thunk = Box::new(move|| { x = 2; }); | ^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default -warning: unused variable: `x` - --> $DIR/moved-upvar-mut-rebind-11958.rs:10:36 +warning: value assigned to `x` is never read + --> $DIR/moved-upvar-mut-rebind-11958.rs:11:36 | LL | let _thunk = Box::new(move|| { x = 2; }); - | ^ + | ^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `x` + --> $DIR/moved-upvar-mut-rebind-11958.rs:9:9 + | +LL | let mut x = 1; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_x` | - = help: did you mean to capture by reference instead? = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default -warning: 2 warnings emitted +warning: 3 warnings emitted diff --git a/tests/ui/const-generics/defaults/trait_object_lt_defaults.rs b/tests/ui/const-generics/defaults/trait_object_lt_defaults.rs index 39dd6cb031f79..da25c0146c543 100644 --- a/tests/ui/const-generics/defaults/trait_object_lt_defaults.rs +++ b/tests/ui/const-generics/defaults/trait_object_lt_defaults.rs @@ -1,6 +1,6 @@ //@ aux-build:trait_object_lt_defaults_lib.rs //@ run-pass -#![allow(dead_code)] +#![allow(dead_code, unused)] extern crate trait_object_lt_defaults_lib; // Tests that `A<'a, 3, dyn Test>` is short for `A<'a, 3, dyn Test + 'a>` diff --git a/tests/ui/drop/or-pattern-drop-order.rs b/tests/ui/drop/or-pattern-drop-order.rs index cca81673ac3ac..88355a4937e4f 100644 --- a/tests/ui/drop/or-pattern-drop-order.rs +++ b/tests/ui/drop/or-pattern-drop-order.rs @@ -25,7 +25,7 @@ fn assert_drop_order(expected_drops: impl IntoIterator, f: impl Fn(& assert_eq!(order, correct_order); } -#[expect(unused_variables, unused_assignments, irrefutable_let_patterns)] +#[expect(unused_variables, irrefutable_let_patterns)] fn main() { // When bindings are declared with `let pat;`, they're visited in left-to-right order, using the // order given by the first occurrence of each variable. They're later dropped in reverse. diff --git a/tests/ui/dropck/dropck-empty-array.rs b/tests/ui/dropck/dropck-empty-array.rs index f3eca6aed8d5e..5df274d455420 100644 --- a/tests/ui/dropck/dropck-empty-array.rs +++ b/tests/ui/dropck/dropck-empty-array.rs @@ -1,6 +1,7 @@ //@ run-pass -#[allow(dead_code)] +#![allow(dead_code, unused_variables, unused_assignments)] + struct Struct<'s>(&'s str); impl<'s> Drop for Struct<'s> { diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr index c2c7378f07a20..98cdafaf8b799 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr @@ -14,25 +14,6 @@ LL | let object2 = Object; LL ~ *object = object2; | -error: value assigned to `object` is never read - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:4 - | -LL | object = &object2; - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9 - | -LL | #![deny(unused_assignments)] - | ^^^^^^^^^^^^^^^^^^ -help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding - | -LL ~ fn change_object2(object: &mut Object) { -LL | -LL | let object2 = Object; -LL ~ *object = object2; - | - error[E0597]: `object2` does not live long enough --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:13 | @@ -51,17 +32,22 @@ LL | } | - `object2` dropped here while still borrowed error: value assigned to `object` is never read - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:5 + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:4 | -LL | object = &mut object2; - | ^^^^^^ +LL | object = &object2; + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9 | +LL | #![deny(unused_assignments)] + | ^^^^^^^^^^^^^^^^^^ help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding | -LL ~ fn change_object3(object: &mut Object) { +LL ~ fn change_object2(object: &mut Object) { LL | -LL | let object2 = Object; -LL ~ *object = object2; +LL | let object2 = Object; +LL ~ *object = object2; | error[E0596]: cannot borrow `object2` as mutable, as it is not declared as mutable @@ -75,6 +61,20 @@ help: consider changing this to be mutable LL | let mut object2 = Object; | +++ +error: value assigned to `object` is never read + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:5 + | +LL | object = &mut object2; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding + | +LL ~ fn change_object3(object: &mut Object) { +LL | +LL | let object2 = Object; +LL ~ *object = object2; + | + error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0596, E0597. diff --git a/tests/ui/issues/issue-19367.rs b/tests/ui/issues/issue-19367.rs index ce8451dd2de13..1cd6c483375ab 100644 --- a/tests/ui/issues/issue-19367.rs +++ b/tests/ui/issues/issue-19367.rs @@ -1,4 +1,7 @@ //@ run-pass + +#![allow(unused_assignments)] + struct S { o: Option } diff --git a/tests/ui/lint/dead-code/issue-85071-2.stderr b/tests/ui/lint/dead-code/issue-85071-2.stderr index ab88d72359695..5cc94e25e3a69 100644 --- a/tests/ui/lint/dead-code/issue-85071-2.stderr +++ b/tests/ui/lint/dead-code/issue-85071-2.stderr @@ -1,15 +1,3 @@ -warning: unused variable: `x` - --> $DIR/issue-85071-2.rs:18:9 - | -LL | let x = s.f(); - | ^ help: if this is intentional, prefix it with an underscore: `_x` - | -note: the lint level is defined here - --> $DIR/issue-85071-2.rs:7:9 - | -LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ - warning: unreachable definition --> $DIR/issue-85071-2.rs:18:9 | @@ -29,5 +17,17 @@ note: the lint level is defined here LL | #![warn(unused_variables,unreachable_code)] | ^^^^^^^^^^^^^^^^ +warning: unused variable: `x` + --> $DIR/issue-85071-2.rs:18:9 + | +LL | let x = s.f(); + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> $DIR/issue-85071-2.rs:7:9 + | +LL | #![warn(unused_variables,unreachable_code)] + | ^^^^^^^^^^^^^^^^ + warning: 2 warnings emitted diff --git a/tests/ui/lint/dead-code/issue-85071.stderr b/tests/ui/lint/dead-code/issue-85071.stderr index c94923063903a..b95ae09385dec 100644 --- a/tests/ui/lint/dead-code/issue-85071.stderr +++ b/tests/ui/lint/dead-code/issue-85071.stderr @@ -1,15 +1,3 @@ -warning: unused variable: `x` - --> $DIR/issue-85071.rs:15:9 - | -LL | let x = f(); - | ^ help: if this is intentional, prefix it with an underscore: `_x` - | -note: the lint level is defined here - --> $DIR/issue-85071.rs:9:9 - | -LL | #![warn(unused_variables,unreachable_code)] - | ^^^^^^^^^^^^^^^^ - warning: unreachable definition --> $DIR/issue-85071.rs:15:9 | @@ -29,5 +17,17 @@ note: the lint level is defined here LL | #![warn(unused_variables,unreachable_code)] | ^^^^^^^^^^^^^^^^ +warning: unused variable: `x` + --> $DIR/issue-85071.rs:15:9 + | +LL | let x = f(); + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> $DIR/issue-85071.rs:9:9 + | +LL | #![warn(unused_variables,unreachable_code)] + | ^^^^^^^^^^^^^^^^ + warning: 2 warnings emitted diff --git a/tests/ui/lint/future-incompat-json-test.stderr b/tests/ui/lint/future-incompat-json-test.stderr index bf29b9577db5e..2e7d0cf626f1d 100644 --- a/tests/ui/lint/future-incompat-json-test.stderr +++ b/tests/ui/lint/future-incompat-json-test.stderr @@ -1,4 +1,4 @@ -{"$message_type":"future_incompat","future_incompat_report":[{"diagnostic":{"$message_type":"diagnostic","message":"unused variable: `x`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":340,"byte_end":341,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":340,"byte_end":341,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"_x","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"warning: unused variable: `x` +{"$message_type":"future_incompat","future_incompat_report":[{"diagnostic":{"$message_type":"diagnostic","message":"unused variable: `x`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":340,"byte_end":341,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":340,"byte_end":341,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"_x","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"warning: unused variable: `x` --> $DIR/future-incompat-json-test.rs:9:9 | LL | let x = 1; diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs index 8e74531e7762a..5b5843a8ddbb6 100644 --- a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs @@ -13,4 +13,5 @@ macro_rules! pat { fn main() { let pat!(value) = Value { value: () }; + //~^ WARN value assigned to `value` is never read } diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr new file mode 100644 index 0000000000000..ba7d3515b0d8d --- /dev/null +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr @@ -0,0 +1,11 @@ +warning: value assigned to `value` is never read + --> $DIR/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs:15:14 + | +LL | let pat!(value) = Value { value: () }; + | ^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index 5dec20b2ac7ea..b0c56003957c9 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -16,7 +16,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` | note: the lint level is defined here --> $DIR/lint-uppercase-variables.rs:1:9 @@ -24,29 +24,12 @@ note: the lint level is defined here LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` -help: if this is intentional, prefix it with an underscore - | -LL | _Foo => {} - | + -help: you might have meant to pattern match on the similarly named variant `Foo` - | -LL | foo::Foo::Foo => {} - | ++++++++++ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | let _Foo = foo::Foo::Foo; - | + -help: you might have meant to pattern match on the similarly named variant `Foo` - | -LL | let foo::Foo::Foo = foo::Foo::Foo; - | ++++++++++ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` --> $DIR/lint-uppercase-variables.rs:33:17 diff --git a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr index 49dba1c7ffe8c..b09270d94ba31 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr +++ b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr @@ -2,11 +2,19 @@ warning: unused variable: `x` --> $DIR/expect_lint_from_macro.rs:7:13 | LL | let x = 0; - | ^ help: if this is intentional, prefix it with an underscore: `_x` + | ^ ... LL | trigger_unused_variables_macro!(); | --------------------------------- in this macro invocation | +help: `x` is captured in macro and introduced a unused variable + --> $DIR/expect_lint_from_macro.rs:7:13 + | +LL | let x = 0; + | ^ +... +LL | trigger_unused_variables_macro!(); + | --------------------------------- in this macro invocation note: the lint level is defined here --> $DIR/expect_lint_from_macro.rs:3:9 | @@ -18,11 +26,19 @@ warning: unused variable: `x` --> $DIR/expect_lint_from_macro.rs:7:13 | LL | let x = 0; - | ^ help: if this is intentional, prefix it with an underscore: `_x` + | ^ ... LL | trigger_unused_variables_macro!(); | --------------------------------- in this macro invocation | +help: `x` is captured in macro and introduced a unused variable + --> $DIR/expect_lint_from_macro.rs:7:13 + | +LL | let x = 0; + | ^ +... +LL | trigger_unused_variables_macro!(); + | --------------------------------- in this macro invocation = note: this warning originates in the macro `trigger_unused_variables_macro` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 2 warnings emitted diff --git a/tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr b/tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr index eaf9edd8e8fee..dbfdfc7d4fa7a 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr +++ b/tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr @@ -14,12 +14,6 @@ LL | let x = 2; | = note: requested on the command line with `--force-warn unused-variables` -warning: unused variable: `fox_name` - --> $DIR/force_warn_expected_lints_fulfilled.rs:26:9 - | -LL | let fox_name = "Sir Nibbles"; - | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_fox_name` - warning: variable does not need to be mutable --> $DIR/force_warn_expected_lints_fulfilled.rs:30:9 | @@ -30,6 +24,12 @@ LL | let mut what_does_the_fox_say = "*ding* *deng* *dung*"; | = note: requested on the command line with `--force-warn unused-mut` +warning: unused variable: `fox_name` + --> $DIR/force_warn_expected_lints_fulfilled.rs:26:9 + | +LL | let fox_name = "Sir Nibbles"; + | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_fox_name` + warning: unused variable: `this_should_fulfill_the_expectation` --> $DIR/force_warn_expected_lints_fulfilled.rs:41:9 | diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.rs b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs index eea0f4c594dc8..3d622b6991300 100644 --- a/tests/ui/lint/unused/issue-117284-arg-in-macro.rs +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs @@ -1,7 +1,7 @@ #![deny(unused_variables)] macro_rules! make_var { ($struct:ident, $var:ident) => { - let $var = $struct.$var; + let $var = $struct.$var; //~ ERROR unused variable: `var` }; } @@ -12,6 +12,6 @@ struct MyStruct { fn main() { let s = MyStruct { var: 42 }; - make_var!(s, var); //~ ERROR unused variable: `var` + make_var!(s, var); let a = 1; //~ ERROR unused variable: `a` } diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr index 84efaa4f3687b..b4a6871713c06 100644 --- a/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr @@ -1,8 +1,11 @@ error: unused variable: `var` - --> $DIR/issue-117284-arg-in-macro.rs:15:18 + --> $DIR/issue-117284-arg-in-macro.rs:4:13 | +LL | let $var = $struct.$var; + | ^^^^ +... LL | make_var!(s, var); - | ^^^ + | ----------------- in this macro invocation | help: `var` is captured in macro and introduced a unused variable --> $DIR/issue-117284-arg-in-macro.rs:4:13 diff --git a/tests/ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.stderr b/tests/ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.stderr index fe2e3afc82ec8..c378b307b8b54 100644 --- a/tests/ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.stderr +++ b/tests/ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.stderr @@ -1,27 +1,45 @@ -warning: unused variable: `i_think_continually` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:26:9 +warning: variable does not need to be mutable + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:9 | -LL | let i_think_continually = 2; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_i_think_continually` +LL | let mut mut_unused_var = 1; + | ----^^^^^^^^^^^^^^ + | | + | help: remove this `mut` | note: the lint level is defined here --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:5:9 | LL | #![warn(unused)] // UI tests pass `-A unused` (#43896) | ^^^^^^ + = note: `#[warn(unused_mut)]` implied by `#[warn(unused)]` + +warning: variable does not need to be mutable + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:10 + | +LL | let (mut var, unused_var) = (1, 2); + | ----^^^ + | | + | help: remove this `mut` + +warning: unused variable: `i_think_continually` + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:26:9 + | +LL | let i_think_continually = 2; + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_i_think_continually` + | = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: unused variable: `mut_unused_var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:13 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:9 | LL | let mut mut_unused_var = 1; - | ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_mut_unused_var` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_mut_unused_var` warning: unused variable: `var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:14 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:10 | LL | let (mut var, unused_var) = (1, 2); - | ^^^ help: if this is intentional, prefix it with an underscore: `_var` + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_var` warning: unused variable: `unused_var` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:19 @@ -36,22 +54,13 @@ LL | if let SoulHistory { corridors_of_light, | ^^^^^^^^^^^^^^^^^^ help: try ignoring the field: `corridors_of_light: _` warning: variable `hours_are_suns` is assigned to, but never used - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:46:30 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:46:26 | LL | mut hours_are_suns, - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ | = note: consider using `_hours_are_suns` instead -warning: value assigned to `hours_are_suns` is never read - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:48:9 - | -LL | hours_are_suns = false; - | ^^^^^^^^^^^^^^ - | - = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` - warning: unused variable: `fire` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:52:32 | @@ -94,23 +103,14 @@ warning: unused variable: `case` LL | Tuple(Large::Suit { case }, ()) => {} | ^^^^ help: try ignoring the field: `case: _` -warning: variable does not need to be mutable - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:9 - | -LL | let mut mut_unused_var = 1; - | ----^^^^^^^^^^^^^^ - | | - | help: remove this `mut` +warning: value assigned to `hours_are_suns` is never read + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:48:9 | - = note: `#[warn(unused_mut)]` implied by `#[warn(unused)]` - -warning: variable does not need to be mutable - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:10 +LL | hours_are_suns = false; + | ^^^^^^^^^^^^^^^^^^^^^^ | -LL | let (mut var, unused_var) = (1, 2); - | ----^^^ - | | - | help: remove this `mut` + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` warning: 16 warnings emitted diff --git a/tests/ui/lint/unused/issue-54180-unused-ref-field.stderr b/tests/ui/lint/unused/issue-54180-unused-ref-field.stderr index f2e6168998c44..c501aa25f1352 100644 --- a/tests/ui/lint/unused/issue-54180-unused-ref-field.stderr +++ b/tests/ui/lint/unused/issue-54180-unused-ref-field.stderr @@ -11,6 +11,12 @@ LL | #![deny(unused)] | ^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` +error: unused variable: `x` + --> $DIR/issue-54180-unused-ref-field.rs:29:45 + | +LL | let _: i32 = points.iter().map(|Point { x, y }| y).sum(); + | ^ help: try ignoring the field: `x: _` + error: unused variable: `f1` --> $DIR/issue-54180-unused-ref-field.rs:26:13 | @@ -23,11 +29,5 @@ error: unused variable: `x` LL | Point { y, ref mut x } => y, | ^^^^^^^^^ help: try ignoring the field: `x: _` -error: unused variable: `x` - --> $DIR/issue-54180-unused-ref-field.rs:29:45 - | -LL | let _: i32 = points.iter().map(|Point { x, y }| y).sum(); - | ^ help: try ignoring the field: `x: _` - error: aborting due to 4 previous errors diff --git a/tests/ui/lint/unused/lint-unused-variables.stderr b/tests/ui/lint/unused/lint-unused-variables.stderr index 6106d4cd1bfc9..beb1c0d736b35 100644 --- a/tests/ui/lint/unused/lint-unused-variables.stderr +++ b/tests/ui/lint/unused/lint-unused-variables.stderr @@ -10,24 +10,18 @@ note: the lint level is defined here LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -error: unused variable: `a` - --> $DIR/lint-unused-variables.rs:21:9 +error: unused variable: `b` + --> $DIR/lint-unused-variables.rs:13:5 | -LL | a: i32, - | ^ help: if this is intentional, prefix it with an underscore: `_a` +LL | b: i32, + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/lint-unused-variables.rs:67:9 + --> $DIR/lint-unused-variables.rs:21:9 | LL | a: i32, | ^ help: if this is intentional, prefix it with an underscore: `_a` -error: unused variable: `b` - --> $DIR/lint-unused-variables.rs:13:5 - | -LL | b: i32, - | ^ help: if this is intentional, prefix it with an underscore: `_b` - error: unused variable: `b` --> $DIR/lint-unused-variables.rs:28:9 | @@ -70,5 +64,11 @@ error: unused variable: `b` LL | b: i32, | ^ help: if this is intentional, prefix it with an underscore: `_b` +error: unused variable: `a` + --> $DIR/lint-unused-variables.rs:67:9 + | +LL | a: i32, + | ^ help: if this is intentional, prefix it with an underscore: `_a` + error: aborting due to 11 previous errors diff --git a/tests/ui/liveness/liveness-asm.stderr b/tests/ui/liveness/liveness-asm.stderr index 57d89e44dcb86..ca1c07046d0ef 100644 --- a/tests/ui/liveness/liveness-asm.stderr +++ b/tests/ui/liveness/liveness-asm.stderr @@ -1,8 +1,8 @@ warning: value assigned to `src` is never read - --> $DIR/liveness-asm.rs:14:32 + --> $DIR/liveness-asm.rs:14:5 | LL | asm!("/*{0}*/", inout(reg) src); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? note: the lint level is defined here @@ -12,10 +12,10 @@ LL | #![warn(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ warning: value assigned to `src` is never read - --> $DIR/liveness-asm.rs:24:39 + --> $DIR/liveness-asm.rs:24:5 | LL | asm!("/*{0}*/", inout(reg) src => src); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? diff --git a/tests/ui/liveness/liveness-consts.rs b/tests/ui/liveness/liveness-consts.rs index 40d30fb9113a9..7e56bf8c2cda4 100644 --- a/tests/ui/liveness/liveness-consts.rs +++ b/tests/ui/liveness/liveness-consts.rs @@ -4,16 +4,18 @@ pub static A: i32 = { let mut i = 0; - let mut a = 0; //~ WARN variable `a` is assigned to, but never used + let mut a = 0; + //~^ WARN variable `a` is assigned to, but never used while i < 10 { i += 1; a += 1; + //~^ WARN value assigned to `a` is never read } i }; pub const B: u32 = { - let mut b = 1; + let mut b = 1; //~ WARN value assigned to `b` is never read b += 1; //~ WARN value assigned to `b` is never read b = 42; b diff --git a/tests/ui/liveness/liveness-consts.stderr b/tests/ui/liveness/liveness-consts.stderr index 34ce39473379e..2d2750fbaddff 100644 --- a/tests/ui/liveness/liveness-consts.stderr +++ b/tests/ui/liveness/liveness-consts.stderr @@ -1,5 +1,5 @@ warning: unused variable: `e` - --> $DIR/liveness-consts.rs:24:13 + --> $DIR/liveness-consts.rs:26:13 | LL | let e = 1; | ^ help: if this is intentional, prefix it with an underscore: `_e` @@ -12,53 +12,69 @@ LL | #![warn(unused)] = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: unused variable: `s` - --> $DIR/liveness-consts.rs:33:24 + --> $DIR/liveness-consts.rs:35:24 | LL | pub fn f(x: [u8; { let s = 17; 100 }]) -> [u8; { let z = 18; 100 }] { | ^ help: if this is intentional, prefix it with an underscore: `_s` warning: unused variable: `z` - --> $DIR/liveness-consts.rs:33:55 + --> $DIR/liveness-consts.rs:35:55 | LL | pub fn f(x: [u8; { let s = 17; 100 }]) -> [u8; { let z = 18; 100 }] { | ^ help: if this is intentional, prefix it with an underscore: `_z` warning: variable `a` is assigned to, but never used - --> $DIR/liveness-consts.rs:7:13 + --> $DIR/liveness-consts.rs:7:9 | LL | let mut a = 0; - | ^ + | ^^^^^ | = note: consider using `_a` instead +warning: value assigned to `a` is never read + --> $DIR/liveness-consts.rs:11:9 + | +LL | a += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + warning: value assigned to `b` is never read - --> $DIR/liveness-consts.rs:17:5 + --> $DIR/liveness-consts.rs:18:17 + | +LL | let mut b = 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `b` is never read + --> $DIR/liveness-consts.rs:19:5 | LL | b += 1; - | ^ + | ^^^^^^ | = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` warning: unused variable: `z` - --> $DIR/liveness-consts.rs:60:13 + --> $DIR/liveness-consts.rs:62:13 | LL | let z = 42; | ^ help: if this is intentional, prefix it with an underscore: `_z` warning: value assigned to `t` is never read - --> $DIR/liveness-consts.rs:42:9 + --> $DIR/liveness-consts.rs:44:9 | LL | t = t + t; - | ^ + | ^^^^^^^^^ | = help: maybe it is overwritten before being read? warning: unused variable: `w` - --> $DIR/liveness-consts.rs:49:13 + --> $DIR/liveness-consts.rs:51:13 | LL | let w = 10; | ^ help: if this is intentional, prefix it with an underscore: `_w` -warning: 8 warnings emitted +warning: 10 warnings emitted diff --git a/tests/ui/liveness/liveness-dead.stderr b/tests/ui/liveness/liveness-dead.stderr index de6d5bd993d62..4f7dda49cd03e 100644 --- a/tests/ui/liveness/liveness-dead.stderr +++ b/tests/ui/liveness/liveness-dead.stderr @@ -1,8 +1,8 @@ error: value assigned to `x` is never read - --> $DIR/liveness-dead.rs:9:13 + --> $DIR/liveness-dead.rs:9:24 | LL | let mut x: isize = 3; - | ^ + | ^ | = help: maybe it is overwritten before being read? note: the lint level is defined here @@ -15,15 +15,15 @@ error: value assigned to `x` is never read --> $DIR/liveness-dead.rs:17:5 | LL | x = 4; - | ^ + | ^^^^^ | = help: maybe it is overwritten before being read? error: value passed to `x` is never read - --> $DIR/liveness-dead.rs:20:11 + --> $DIR/liveness-dead.rs:20:7 | LL | fn f4(mut x: i32) { - | ^ + | ^^^^^ | = help: maybe it is overwritten before being read? @@ -31,7 +31,7 @@ error: value assigned to `x` is never read --> $DIR/liveness-dead.rs:27:5 | LL | x = 4; - | ^ + | ^^^^^ | = help: maybe it is overwritten before being read? diff --git a/tests/ui/liveness/liveness-unused.rs b/tests/ui/liveness/liveness-unused.rs index 49e7044aeda1f..639d7c2776dee 100644 --- a/tests/ui/liveness/liveness-unused.rs +++ b/tests/ui/liveness/liveness-unused.rs @@ -39,6 +39,7 @@ fn f3b() { //~^ ERROR variable `z` is assigned to, but never used loop { z += 4; + //~^ ERROR value assigned to `z` is never read } } @@ -46,6 +47,7 @@ fn f3b() { fn f3c() { let mut z = 3; loop { z += 4; } + //~^ ERROR value assigned to `z` is never read } #[allow(unused_variables)] @@ -55,6 +57,16 @@ fn f3d() { x += 4; } +fn f3e() { + let a = 13; + let mut z = 3; + //~^ ERROR variable `z` is assigned to, but never used + loop { + z += a; + //~^ ERROR value assigned to `z` is never read + } +} + fn f4() { match Some(3) { Some(i) => { @@ -68,7 +80,15 @@ enum tri { a(isize), b(isize), c(isize) } -fn f4b() -> isize { +fn f4b() { + match tri::a(3) { + tri::a(i) | tri::b(i) | tri::c(i) => { + //~^ ERROR unused variable: `i` + } + } +} + +fn f4c() -> isize { match tri::a(3) { tri::a(i) | tri::b(i) | tri::c(i) => { i @@ -76,6 +96,13 @@ fn f4b() -> isize { } } +fn f4d() { + match tri::a(3) { + tri::a(i) | tri::b(i) | tri::c(i) if i == 0 => {} + _ => {} + } +} + fn f5a() { for x in 1..10 { } //~^ ERROR unused variable: `x` @@ -138,10 +165,92 @@ fn f7() { drop(a); } +fn f8(a: u32) { + let _ = a; +} + +fn f9() { + let mut a = 10; + //~^ ERROR variable `a` is assigned to, but never used + let b = 13; + let c = 13; + let d = 13; + let e = 13; + let f = 13; + let g = 13; + let h = 13; + + a += b; + //~^ ERROR value assigned to `a` is never read + a -= c; + //~^ ERROR value assigned to `a` is never read + a *= d; + //~^ ERROR value assigned to `a` is never read + a /= e; + //~^ ERROR value assigned to `a` is never read + a |= f; + //~^ ERROR value assigned to `a` is never read + a &= g; + //~^ ERROR value assigned to `a` is never read + a %= h; + //~^ ERROR value assigned to `a` is never read +} + +fn f9b() { + let mut a = 10; + let b = 13; + let c = 13; + let d = 13; + let e = 13; + let f = 13; + let g = 13; + let h = 13; + + a += b; + a -= c; + a *= d; + a /= e; + a |= f; + a &= g; + a %= h; + + let _ = a; +} + +fn f9c() { + let mut a = 10.; + //~^ ERROR variable `a` is assigned to, but never used + let b = 13.; + let c = 13.; + let d = 13.; + let e = 13.; + let f = 13.; + + a += b; + //~^ ERROR value assigned to `a` is never read + a -= c; + //~^ ERROR value assigned to `a` is never read + a *= d; + //~^ ERROR value assigned to `a` is never read + a /= e; + //~^ ERROR value assigned to `a` is never read + a %= f; + //~^ ERROR value assigned to `a` is never read +} + +fn f10(mut a: T, b: T) { + //~^ ERROR variable `a` is assigned to, but never used + a = b; + //~^ ERROR value assigned to `a` is never read +} + +fn f10b(mut a: Box, b: Box) { + a = b; +} + // unused params warnings are not needed for intrinsic functions without bodies #[rustc_intrinsic] unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; - fn main() { } diff --git a/tests/ui/liveness/liveness-unused.stderr b/tests/ui/liveness/liveness-unused.stderr index a69fc10dff271..23a26841be2fc 100644 --- a/tests/ui/liveness/liveness-unused.stderr +++ b/tests/ui/liveness/liveness-unused.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/liveness-unused.rs:93:9 + --> $DIR/liveness-unused.rs:120:9 | LL | continue; | -------- any code following this expression is unreachable @@ -44,10 +44,10 @@ LL | let x = 3; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:31:13 + --> $DIR/liveness-unused.rs:31:9 | LL | let mut x = 3; - | ^ + | ^^^^^ | = note: consider using `_x` instead @@ -55,7 +55,7 @@ error: value assigned to `x` is never read --> $DIR/liveness-unused.rs:33:5 | LL | x += 4; - | ^ + | ^^^^^^ | = help: maybe it is overwritten before being read? note: the lint level is defined here @@ -65,39 +65,82 @@ LL | #![deny(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ error: variable `z` is assigned to, but never used - --> $DIR/liveness-unused.rs:38:13 + --> $DIR/liveness-unused.rs:38:9 | LL | let mut z = 3; - | ^ + | ^^^^^ | = note: consider using `_z` instead +error: value assigned to `z` is never read + --> $DIR/liveness-unused.rs:41:9 + | +LL | z += 4; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `z` is never read + --> $DIR/liveness-unused.rs:49:12 + | +LL | loop { z += 4; } + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: variable `z` is assigned to, but never used + --> $DIR/liveness-unused.rs:62:9 + | +LL | let mut z = 3; + | ^^^^^ + | + = note: consider using `_z` instead + +error: value assigned to `z` is never read + --> $DIR/liveness-unused.rs:65:9 + | +LL | z += a; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + error: unused variable: `i` - --> $DIR/liveness-unused.rs:60:12 + --> $DIR/liveness-unused.rs:72:12 | LL | Some(i) => { | ^ help: if this is intentional, prefix it with an underscore: `_i` +error: unused variable: `i` + --> $DIR/liveness-unused.rs:85:14 + | +LL | tri::a(i) | tri::b(i) | tri::c(i) => { + | ^ ^ ^ + | +help: if this is intentional, prefix it with an underscore + | +LL | tri::a(_i) | tri::b(_i) | tri::c(_i) => { + | + + + + error: unused variable: `x` - --> $DIR/liveness-unused.rs:80:9 + --> $DIR/liveness-unused.rs:107:9 | LL | for x in 1..10 { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:85:10 + --> $DIR/liveness-unused.rs:112:10 | LL | for (x, _) in [1, 2, 3].iter().enumerate() { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:90:13 + --> $DIR/liveness-unused.rs:117:13 | LL | for (_, x) in [1, 2, 3].iter().enumerate() { | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:113:9 + --> $DIR/liveness-unused.rs:140:9 | LL | let x; | ^ @@ -105,12 +148,140 @@ LL | let x; = note: consider using `_x` instead error: value assigned to `x` is never read - --> $DIR/liveness-unused.rs:117:9 + --> $DIR/liveness-unused.rs:144:9 | LL | x = 0; - | ^ + | ^^^^^ + | + = help: maybe it is overwritten before being read? + +error: variable `a` is assigned to, but never used + --> $DIR/liveness-unused.rs:173:9 + | +LL | let mut a = 10; + | ^^^^^ + | + = note: consider using `_a` instead + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:183:5 + | +LL | a += b; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:185:5 + | +LL | a -= c; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:187:5 + | +LL | a *= d; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:189:5 + | +LL | a /= e; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:191:5 + | +LL | a |= f; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:193:5 + | +LL | a &= g; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:195:5 + | +LL | a %= h; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: variable `a` is assigned to, but never used + --> $DIR/liveness-unused.rs:221:9 + | +LL | let mut a = 10.; + | ^^^^^ + | + = note: consider using `_a` instead + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:229:5 + | +LL | a += b; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:231:5 + | +LL | a -= c; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:233:5 + | +LL | a *= d; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:235:5 + | +LL | a /= e; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:237:5 + | +LL | a %= f; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +error: variable `a` is assigned to, but never used + --> $DIR/liveness-unused.rs:241:11 + | +LL | fn f10(mut a: T, b: T) { + | ^^^^^ + | + = note: consider using `_a` instead + +error: value assigned to `a` is never read + --> $DIR/liveness-unused.rs:243:5 + | +LL | a = b; + | ^ | = help: maybe it is overwritten before being read? -error: aborting due to 13 previous errors; 1 warning emitted +error: aborting due to 34 previous errors; 1 warning emitted diff --git a/tests/ui/liveness/liveness-upvars.rs b/tests/ui/liveness/liveness-upvars.rs index f76efba3e6b3a..be58b48a40576 100644 --- a/tests/ui/liveness/liveness-upvars.rs +++ b/tests/ui/liveness/liveness-upvars.rs @@ -7,8 +7,8 @@ pub fn unintentional_copy_one() { let mut last = None; let mut f = move |s| { - last = Some(s); //~ WARN value assigned to `last` is never read - //~| WARN unused variable: `last` + last = Some(s); //~ WARN value captured by `last` is never read + //~| WARN value assigned to `last` is never read }; f("a"); f("b"); @@ -19,7 +19,9 @@ pub fn unintentional_copy_one() { pub fn unintentional_copy_two() { let mut sum = 0; (1..10).for_each(move |x| { - sum += x; //~ WARN unused variable: `sum` + sum += x; + //~^ WARN value captured by `sum` is never read + //~| WARN value assigned to `sum` is never read }); dbg!(sum); } @@ -39,11 +41,14 @@ pub fn f() { // Read and written to, but never actually used. let _ = move || { - c += 1; //~ WARN unused variable: `c` + c += 1; + //~^ WARN value captured by `c` is never read + //~| WARN value assigned to `c` is never read }; let _ = async move { - c += 1; //~ WARN value assigned to `c` is never read - //~| WARN unused variable: `c` + c += 1; + //~^ WARN value captured by `c` is never read + //~| WARN value assigned to `c` is never read }; let _ = move || { @@ -74,8 +79,8 @@ pub fn nested() { d = Some("d2"); }; let _ = move || { - e = Some("e1"); //~ WARN value assigned to `e` is never read - //~| WARN unused variable: `e` + e = Some("e1"); //~ WARN value captured by `e` is never read + //~| WARN value assigned to `e` is never read e = Some("e2"); //~ WARN value assigned to `e` is never read }; }; @@ -84,7 +89,8 @@ pub fn nested() { pub fn g(mut v: T) { let _ = |r| { if r { - v = T::default(); //~ WARN value assigned to `v` is never read + v = T::default(); + //~^ WARN value assigned to `v` is never read } else { drop(v); } @@ -96,8 +102,8 @@ pub fn h() { let _ = move |b| { loop { if b { - z = T::default(); //~ WARN value assigned to `z` is never read - //~| WARN unused variable: `z` + z = T::default(); //~ WARN value captured by `z` is never read + //~| WARN value assigned to `z` is never read } else { return; } @@ -123,7 +129,7 @@ pub fn async_coroutine() { let _ = async move { state = 4; //~ WARN value assigned to `state` is never read - //~| WARN unused variable: `state` + //~| WARN value captured by `state` is never read yield_now().await; state = 5; //~ WARN value assigned to `state` is never read }; @@ -141,4 +147,21 @@ pub fn coroutine() { }; } +pub fn panics() { + use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; + + let mut panic = true; + + // `a` can be called again, even if it has panicked at an earlier run. + let mut a = || { + if panic { + panic = false; + resume_unwind(Box::new(())) + } + }; + + catch_unwind(AssertUnwindSafe(|| a())).ok(); + a(); +} + fn main() {} diff --git a/tests/ui/liveness/liveness-upvars.stderr b/tests/ui/liveness/liveness-upvars.stderr index 82f62371ec59d..cfed2830164ad 100644 --- a/tests/ui/liveness/liveness-upvars.stderr +++ b/tests/ui/liveness/liveness-upvars.stderr @@ -1,10 +1,10 @@ -warning: value assigned to `last` is never read +warning: value captured by `last` is never read --> $DIR/liveness-upvars.rs:10:9 | LL | last = Some(s); | ^^^^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? note: the lint level is defined here --> $DIR/liveness-upvars.rs:4:9 | @@ -12,16 +12,15 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` -warning: unused variable: `last` +warning: value assigned to `last` is never read --> $DIR/liveness-upvars.rs:10:9 | LL | last = Some(s); - | ^^^^ + | ^^^^^^^^^^^^^^ | - = help: did you mean to capture by reference instead? - = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: maybe it is overwritten before being read? -warning: unused variable: `sum` +warning: value captured by `sum` is never read --> $DIR/liveness-upvars.rs:22:9 | LL | sum += x; @@ -29,24 +28,32 @@ LL | sum += x; | = help: did you mean to capture by reference instead? -warning: value captured by `c` is never read - --> $DIR/liveness-upvars.rs:32:9 +warning: value assigned to `sum` is never read + --> $DIR/liveness-upvars.rs:22:9 | -LL | c = 1; - | ^ +LL | sum += x; + | ^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? -warning: value captured by `c` is never read - --> $DIR/liveness-upvars.rs:36:9 +warning: value assigned to `c` is never read + --> $DIR/liveness-upvars.rs:69:9 | -LL | c = 1; - | ^ +LL | c += 1; + | ^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? -warning: unused variable: `c` - --> $DIR/liveness-upvars.rs:42:9 +warning: value assigned to `c` is never read + --> $DIR/liveness-upvars.rs:63:9 + | +LL | c += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:49:9 | LL | c += 1; | ^ @@ -54,15 +61,15 @@ LL | c += 1; = help: did you mean to capture by reference instead? warning: value assigned to `c` is never read - --> $DIR/liveness-upvars.rs:45:9 + --> $DIR/liveness-upvars.rs:49:9 | LL | c += 1; - | ^ + | ^^^^^^ | = help: maybe it is overwritten before being read? -warning: unused variable: `c` - --> $DIR/liveness-upvars.rs:45:9 +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:44:9 | LL | c += 1; | ^ @@ -70,116 +77,124 @@ LL | c += 1; = help: did you mean to capture by reference instead? warning: value assigned to `c` is never read - --> $DIR/liveness-upvars.rs:58:9 + --> $DIR/liveness-upvars.rs:44:9 | LL | c += 1; - | ^ + | ^^^^^^ | = help: maybe it is overwritten before being read? -warning: value assigned to `c` is never read - --> $DIR/liveness-upvars.rs:64:9 +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:38:9 | -LL | c += 1; +LL | c = 1; | ^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? -warning: value assigned to `d` is never read - --> $DIR/liveness-upvars.rs:73:13 +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:34:9 | -LL | d = Some("d1"); +LL | c = 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: value captured by `e` is never read + --> $DIR/liveness-upvars.rs:82:13 + | +LL | e = Some("e1"); | ^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? warning: value assigned to `e` is never read - --> $DIR/liveness-upvars.rs:77:13 + --> $DIR/liveness-upvars.rs:82:13 | LL | e = Some("e1"); - | ^ + | ^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? warning: value assigned to `e` is never read - --> $DIR/liveness-upvars.rs:79:13 + --> $DIR/liveness-upvars.rs:84:13 | LL | e = Some("e2"); - | ^ + | ^^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: unused variable: `e` - --> $DIR/liveness-upvars.rs:77:13 +warning: value assigned to `d` is never read + --> $DIR/liveness-upvars.rs:78:13 | -LL | e = Some("e1"); - | ^ +LL | d = Some("d1"); + | ^^^^^^^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? warning: value assigned to `v` is never read - --> $DIR/liveness-upvars.rs:87:13 + --> $DIR/liveness-upvars.rs:92:13 | LL | v = T::default(); | ^ | = help: maybe it is overwritten before being read? -warning: value assigned to `z` is never read - --> $DIR/liveness-upvars.rs:99:17 +warning: value captured by `z` is never read + --> $DIR/liveness-upvars.rs:105:17 | LL | z = T::default(); | ^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? -warning: unused variable: `z` - --> $DIR/liveness-upvars.rs:99:17 +warning: value assigned to `z` is never read + --> $DIR/liveness-upvars.rs:105:17 | LL | z = T::default(); - | ^ + | ^^^^^^^^^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? -warning: value assigned to `state` is never read - --> $DIR/liveness-upvars.rs:125:9 +warning: value captured by `state` is never read + --> $DIR/liveness-upvars.rs:131:9 | LL | state = 4; | ^^^^^ | - = help: maybe it is overwritten before being read? + = help: did you mean to capture by reference instead? warning: value assigned to `state` is never read - --> $DIR/liveness-upvars.rs:128:9 + --> $DIR/liveness-upvars.rs:131:9 | -LL | state = 5; - | ^^^^^ +LL | state = 4; + | ^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: unused variable: `state` - --> $DIR/liveness-upvars.rs:125:9 +warning: value assigned to `state` is never read + --> $DIR/liveness-upvars.rs:134:9 | -LL | state = 4; - | ^^^^^ +LL | state = 5; + | ^^^^^^^^^ | - = help: did you mean to capture by reference instead? + = help: maybe it is overwritten before being read? warning: value assigned to `s` is never read - --> $DIR/liveness-upvars.rs:137:9 + --> $DIR/liveness-upvars.rs:143:9 | LL | s = 1; - | ^ + | ^^^^^ | = help: maybe it is overwritten before being read? warning: value assigned to `s` is never read - --> $DIR/liveness-upvars.rs:139:9 + --> $DIR/liveness-upvars.rs:145:9 | LL | s = yield (); - | ^ + | ^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: 22 warnings emitted +warning: 24 warnings emitted diff --git a/tests/ui/object-lifetime/object-lifetime-default-default-to-static.rs b/tests/ui/object-lifetime/object-lifetime-default-default-to-static.rs index 23e5852335611..ab3887f2e4108 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-default-to-static.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-default-to-static.rs @@ -3,7 +3,7 @@ // fields and fn arguments. -#![allow(dead_code)] +#![allow(dead_code, unused)] trait Test { fn foo(&self) { } diff --git a/tests/ui/object-lifetime/object-lifetime-default-from-ref-struct.rs b/tests/ui/object-lifetime/object-lifetime-default-from-ref-struct.rs index 040ac1f891326..e74e67f5b6891 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-from-ref-struct.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-from-ref-struct.rs @@ -3,7 +3,7 @@ // lifetime bound. -#![allow(dead_code)] +#![allow(dead_code, unused)] use std::fmt::Display; diff --git a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-box.rs b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-box.rs index c3f3101155cf1..2402bb24d787f 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-box.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-box.rs @@ -3,7 +3,7 @@ // through the `Box` struct. -#![allow(dead_code)] +#![allow(dead_code, unused)] trait Test { fn foo(&self) { } diff --git a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-mut.rs b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-mut.rs index db4f9a40235d4..03a4cb9ec709e 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-mut.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-mut.rs @@ -3,7 +3,7 @@ // lifetime bound. -#![allow(dead_code)] +#![allow(dead_code, unused)] trait Test { fn foo(&self) { } diff --git a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-struct.rs b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-struct.rs index 5163ff1c2455a..988cef49fe03e 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-from-rptr-struct.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-from-rptr-struct.rs @@ -3,7 +3,7 @@ // through the `MyBox` struct. -#![allow(dead_code)] +#![allow(dead_code, unused)] trait Test { fn foo(&self) { } diff --git a/tests/ui/object-lifetime/object-lifetime-default-from-rptr.rs b/tests/ui/object-lifetime/object-lifetime-default-from-rptr.rs index 556bde784152d..d4fc295f4fc62 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-from-rptr.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-from-rptr.rs @@ -3,7 +3,7 @@ // lifetime bound. -#![allow(dead_code)] +#![allow(dead_code, unused)] use std::fmt::Display; diff --git a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs b/tests/ui/object-lifetime/object-lifetime-default-inferred.rs index 1ab821764de08..d3162231faf77 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-inferred.rs @@ -3,7 +3,7 @@ // valid. -#![allow(dead_code)] +#![allow(dead_code, unused)] trait Test { fn foo(&self) { } diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr index 81ea6e6b1bc08..1935d538edc66 100644 --- a/tests/ui/or-patterns/binding-typo-2.stderr +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -97,157 +97,71 @@ LL - (Foo, _) | (Ban, Foo) => {} LL + (Foo, _) | (Bat, Foo) => {} | -error: variable `Ban` is assigned to, but never used +error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:14:23 | LL | (Foo, Bar) | (Ban, Foo) => {} - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` | - = note: consider using `_Ban` instead note: the lint level is defined here --> $DIR/binding-typo-2.rs:2:9 | LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -help: you might have meant to pattern match on the similarly named variant `Bar` - | -LL - (Foo, Bar) | (Ban, Foo) => {} -LL + (Foo, Bar) | (Lol::Bar, Foo) => {} - | -error: variable `Ban` is assigned to, but never used +error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:25:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ - | - = note: consider using `_Ban` instead -help: you might have meant to pattern match on the similarly named variant `Bar` - | -LL - (Foo, _) | (Ban, Foo) => {} -LL + (Foo, _) | (Lol::Bar, Foo) => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` error: unused variable: `Non` --> $DIR/binding-typo-2.rs:37:9 | LL | Non => {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | _Non => {} - | + -help: you might have meant to pattern match on the similarly named variant `None` - | -LL - Non => {} -LL + std::prelude::v1::None => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` error: unused variable: `Non` --> $DIR/binding-typo-2.rs:44:9 | LL | Non | None => {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | _Non | None => {} - | + -help: you might have meant to pattern match on the similarly named variant `None` - | -LL - Non | None => {} -LL + std::prelude::v1::None | None => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` error: unused variable: `Non` --> $DIR/binding-typo-2.rs:54:9 | LL | Non | Some(_) => {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | _Non | Some(_) => {} - | + -help: you might have meant to pattern match on the similarly named variant `None` - | -LL - Non | Some(_) => {} -LL + std::prelude::v1::None | Some(_) => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` -error: variable `Ban` is assigned to, but never used +error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:69:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ - | - = note: consider using `_Ban` instead -help: you might have meant to pattern match on the similarly named variant `Bar` - | -LL - (Foo, _) | (Ban, Foo) => {} -LL + (Foo, _) | (Lol::Bar, Foo) => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` -error: variable `Ban` is assigned to, but never used +error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:86:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ - | - = note: consider using `_Ban` instead -help: you might have meant to pattern match on the similarly named variant `Bar` - | -LL - (Foo, _) | (Ban, Foo) => {} -LL + (Foo, _) | (Lol::Bar, Foo) => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:98:10 | LL | (Ban, _) => {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | (_Ban, _) => {} - | + -help: you might have meant to pattern match on the similarly named variant `Bar` - | -LL - (Ban, _) => {} -LL + (Lol::Bar, _) => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:104:9 | LL | Ban => {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | _Ban => {} - | + -help: you might have meant to pattern match on the similarly named struct `Bay` - | -LL - Ban => {} -LL + Bay => {} - | + | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` error: unused variable: `Batery` --> $DIR/binding-typo-2.rs:110:9 | LL | Batery => {} - | ^^^^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | _Batery => {} - | + -help: you might have meant to pattern match on the similarly named constant `Battery` - | -LL | Battery => {} - | + + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_Batery` error: aborting due to 16 previous errors diff --git a/tests/ui/packed/packed-struct-drop-aligned.rs b/tests/ui/packed/packed-struct-drop-aligned.rs index ba3dcb10c61e5..e8125115d45e6 100644 --- a/tests/ui/packed/packed-struct-drop-aligned.rs +++ b/tests/ui/packed/packed-struct-drop-aligned.rs @@ -1,6 +1,8 @@ //@ run-pass #![feature(coroutines, stmt_expr_attributes)] #![feature(coroutine_trait)] +#![allow(unused_assignments, unused_variables)] + use std::cell::Cell; use std::mem; use std::ops::Coroutine; diff --git a/tests/ui/parser/intersection-patterns-1.fixed b/tests/ui/parser/intersection-patterns-1.fixed index 8ade795f7eef9..3ac9837e1a4da 100644 --- a/tests/ui/parser/intersection-patterns-1.fixed +++ b/tests/ui/parser/intersection-patterns-1.fixed @@ -9,6 +9,7 @@ //@ run-rustfix #![allow(unused_variables)] +#![allow(unused_assignments)] fn main() { let s: Option = None; diff --git a/tests/ui/parser/intersection-patterns-1.rs b/tests/ui/parser/intersection-patterns-1.rs index b5a7892fd1c5a..aab3655540505 100644 --- a/tests/ui/parser/intersection-patterns-1.rs +++ b/tests/ui/parser/intersection-patterns-1.rs @@ -9,6 +9,7 @@ //@ run-rustfix #![allow(unused_variables)] +#![allow(unused_assignments)] fn main() { let s: Option = None; diff --git a/tests/ui/parser/intersection-patterns-1.stderr b/tests/ui/parser/intersection-patterns-1.stderr index c191b46fa45db..8bcb884d8e7cb 100644 --- a/tests/ui/parser/intersection-patterns-1.stderr +++ b/tests/ui/parser/intersection-patterns-1.stderr @@ -1,5 +1,5 @@ error: pattern on wrong side of `@` - --> $DIR/intersection-patterns-1.rs:17:9 + --> $DIR/intersection-patterns-1.rs:18:9 | LL | Some(x) @ y => {} | -------^^^- @@ -14,7 +14,7 @@ LL + y @ Some(x) => {} | error: pattern on wrong side of `@` - --> $DIR/intersection-patterns-1.rs:27:9 + --> $DIR/intersection-patterns-1.rs:28:9 | LL | 1 ..= 5 @ e => {} | -------^^^- diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs index 3d26b5e87d93f..d766411e4f980 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs @@ -1,5 +1,6 @@ //@ run-pass #![allow(unused)] +#![warn(unused_assignments)] // Test copy @@ -34,10 +35,12 @@ pub fn main() { let mut x@B {b, ..} = B {a: 10, b: C {c: 20}}; assert_eq!(x.a, 10); x.b.c = 30; + //~^ WARN value assigned to `x` is never read assert_eq!(b.c, 20); let mut y@D {d, ..} = D {a: 10, d: C {c: 20}}; assert_eq!(y.a, 10); y.d.c = 30; + //~^ WARN value assigned to `y` is never read assert_eq!(d.c, 20); match (E::E { a: 10, e: C { c: 20 } }) { @@ -50,7 +53,9 @@ pub fn main() { } match (E::E { a: 10, e: C { c: 20 } }) { mut x @ E::E{ a, e: C { mut c } } => { + //~^ WARN value assigned to `a` is never read x = E::NotE; + //~^ WARN value assigned to `x` is never read c += 30; assert_eq!(c, 50); } diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr new file mode 100644 index 0000000000000..d775b69ef0a5e --- /dev/null +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr @@ -0,0 +1,39 @@ +warning: value assigned to `x` is never read + --> $DIR/bind-by-copy.rs:37:5 + | +LL | x.b.c = 30; + | ^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? +note: the lint level is defined here + --> $DIR/bind-by-copy.rs:3:9 + | +LL | #![warn(unused_assignments)] + | ^^^^^^^^^^^^^^^^^^ + +warning: value assigned to `y` is never read + --> $DIR/bind-by-copy.rs:42:5 + | +LL | y.d.c = 30; + | ^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `x` is never read + --> $DIR/bind-by-copy.rs:57:13 + | +LL | x = E::NotE; + | ^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `a` is never read + --> $DIR/bind-by-copy.rs:55:23 + | +LL | mut x @ E::E{ a, e: C { mut c } } => { + | ^ + | + = help: maybe it is overwritten before being read? + +warning: 4 warnings emitted + diff --git a/tests/ui/return/early-return-with-unreachable-code-24353.rs b/tests/ui/return/early-return-with-unreachable-code-24353.rs index 13add4652d994..41d77fea49ac9 100644 --- a/tests/ui/return/early-return-with-unreachable-code-24353.rs +++ b/tests/ui/return/early-return-with-unreachable-code-24353.rs @@ -4,6 +4,7 @@ fn main() { return (); let x = (); + //~^ WARN unused variable: `x` x } diff --git a/tests/ui/return/early-return-with-unreachable-code-24353.stderr b/tests/ui/return/early-return-with-unreachable-code-24353.stderr new file mode 100644 index 0000000000000..92526faef33bb --- /dev/null +++ b/tests/ui/return/early-return-with-unreachable-code-24353.stderr @@ -0,0 +1,10 @@ +warning: unused variable: `x` + --> $DIR/early-return-with-unreachable-code-24353.rs:6:9 + | +LL | let x = (); + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs index 3090f68c72b77..b3dc9e5842334 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs @@ -58,6 +58,7 @@ fn match_with_or() { fn nested_mixed() { match (&Some(5), &Some(6)) { (Some(a), &Some(mut b)) => { + //~^ WARN value assigned to `b` is never read // Here, the `a` will be `&i32`, because in the first half of the tuple // we hit a non-reference pattern and shift into `ref` mode. // diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/general.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.stderr new file mode 100644 index 0000000000000..a953e85844943 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.stderr @@ -0,0 +1,11 @@ +warning: value assigned to `b` is never read + --> $DIR/general.rs:60:25 + | +LL | (Some(a), &Some(mut b)) => { + | ^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.default.stderr b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.default.stderr new file mode 100644 index 0000000000000..259a2e2c5e73b --- /dev/null +++ b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.default.stderr @@ -0,0 +1,11 @@ +warning: value assigned to `small` is never read + --> $DIR/std-panic-locations.rs:47:31 + | +LL | assert_panicked(move || { small[1] += 1; }); + | ^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.mir-opt.stderr b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.mir-opt.stderr new file mode 100644 index 0000000000000..259a2e2c5e73b --- /dev/null +++ b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.mir-opt.stderr @@ -0,0 +1,11 @@ +warning: value assigned to `small` is never read + --> $DIR/std-panic-locations.rs:47:31 + | +LL | assert_panicked(move || { small[1] += 1; }); + | ^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs index b1df1b191bcf1..b365e786fd050 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs @@ -45,6 +45,7 @@ fn main() { assert_panicked(move || { small[1]; }); assert_panicked(move || { small.index_mut(1); }); assert_panicked(move || { small[1] += 1; }); + //~^ WARN value assigned to `small` is never read let sorted: BTreeMap = Default::default(); assert_panicked(|| { sorted.index(&false); }); diff --git a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr index ba92bc4a71d85..90282793fa284 100644 --- a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr +++ b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr @@ -10,18 +10,6 @@ note: the lint level is defined here LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -error: unused variable: `a` - --> $DIR/param-attrs-cfg.rs:40:27 - | -LL | #[cfg(something)] a: i32, - | ^ help: if this is intentional, prefix it with an underscore: `_a` - -error: unused variable: `a` - --> $DIR/param-attrs-cfg.rs:106:27 - | -LL | #[cfg(something)] a: i32, - | ^ help: if this is intentional, prefix it with an underscore: `_a` - error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:29:23 | @@ -34,6 +22,12 @@ error: unused variable: `c` LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, | ^ help: if this is intentional, prefix it with an underscore: `_c` +error: unused variable: `a` + --> $DIR/param-attrs-cfg.rs:40:27 + | +LL | #[cfg(something)] a: i32, + | ^ help: if this is intentional, prefix it with an underscore: `_a` + error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:47:27 | @@ -118,5 +112,11 @@ error: unused variable: `c` LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, | ^ help: if this is intentional, prefix it with an underscore: `_c` +error: unused variable: `a` + --> $DIR/param-attrs-cfg.rs:106:27 + | +LL | #[cfg(something)] a: i32, + | ^ help: if this is intentional, prefix it with an underscore: `_a` + error: aborting due to 19 previous errors diff --git a/tests/ui/suggestions/try-removing-the-field.rs b/tests/ui/suggestions/try-removing-the-field.rs index dc1bde082c4f1..29da1964354ec 100644 --- a/tests/ui/suggestions/try-removing-the-field.rs +++ b/tests/ui/suggestions/try-removing-the-field.rs @@ -10,7 +10,7 @@ struct Foo { fn use_foo(x: Foo) -> i32 { let Foo { foo, bar, .. } = x; //~ WARNING unused variable: `bar` - //~| help: try removing the field + //~| help: try ignoring the field return foo; } @@ -24,7 +24,7 @@ fn use_match(x: Foo) { match x { Foo { foo, .. } => { //~ WARNING unused variable - //~| help: try removing the field + //~| help: try ignoring the field } } } diff --git a/tests/ui/suggestions/try-removing-the-field.stderr b/tests/ui/suggestions/try-removing-the-field.stderr index aaf260bb86ede..f83f63642f617 100644 --- a/tests/ui/suggestions/try-removing-the-field.stderr +++ b/tests/ui/suggestions/try-removing-the-field.stderr @@ -2,9 +2,7 @@ warning: unused variable: `bar` --> $DIR/try-removing-the-field.rs:12:20 | LL | let Foo { foo, bar, .. } = x; - | ^^^- - | | - | help: try removing the field + | ^^^ help: try ignoring the field: `bar: _` | = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default @@ -18,9 +16,7 @@ warning: unused variable: `foo` --> $DIR/try-removing-the-field.rs:26:15 | LL | Foo { foo, .. } => { - | ^^^- - | | - | help: try removing the field + | ^^^ help: try ignoring the field: `foo: _` warning: 3 warnings emitted diff --git a/tests/ui/suggestions/unused-closure-argument.stderr b/tests/ui/suggestions/unused-closure-argument.stderr index 55195ce50a13e..9f70501389c43 100644 --- a/tests/ui/suggestions/unused-closure-argument.stderr +++ b/tests/ui/suggestions/unused-closure-argument.stderr @@ -1,8 +1,8 @@ error: unused variable: `x` - --> $DIR/unused-closure-argument.rs:12:23 + --> $DIR/unused-closure-argument.rs:17:15 | -LL | .map(|Point { x, y }| y) - | ^ help: try ignoring the field: `x: _` +LL | .map(|x| 4) + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/unused-closure-argument.rs:1:9 @@ -11,10 +11,10 @@ LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ error: unused variable: `x` - --> $DIR/unused-closure-argument.rs:17:15 + --> $DIR/unused-closure-argument.rs:12:23 | -LL | .map(|x| 4) - | ^ help: if this is intentional, prefix it with an underscore: `_x` +LL | .map(|Point { x, y }| y) + | ^ help: try ignoring the field: `x: _` error: aborting due to 2 previous errors diff --git a/tests/ui/type/issue-100584.stderr b/tests/ui/type/issue-100584.stderr index 1523bdda7614b..29b567eb74405 100644 --- a/tests/ui/type/issue-100584.stderr +++ b/tests/ui/type/issue-100584.stderr @@ -2,7 +2,7 @@ error: unused variable: `xyza` --> $DIR/issue-100584.rs:2:8 | LL | fn foo(xyza: &str) { - | ^^^^ unused variable + | ^^^^ LL | LL | let _ = "{xyza}"; | -------- you might have meant to use string interpolation in this string literal @@ -26,7 +26,7 @@ error: unused variable: `xyza` --> $DIR/issue-100584.rs:7:9 | LL | fn foo3(xyza: &str) { - | ^^^^ unused variable + | ^^^^ LL | LL | let _ = "aaa{xyza}bbb"; | -------------- you might have meant to use string interpolation in this string literal diff --git a/tests/ui/type/type-ascription.rs b/tests/ui/type/type-ascription.rs index 76d6d923affc2..3649c707f3046 100644 --- a/tests/ui/type/type-ascription.rs +++ b/tests/ui/type/type-ascription.rs @@ -1,9 +1,8 @@ //@ run-pass -#![allow(dead_code)] -#![allow(unused_variables)] -// Type ascription doesn't lead to unsoundness +#![allow(dead_code, unused_variables, unused_assignments)] +// Type ascription doesn't lead to unsoundness #![feature(type_ascription)] use std::mem; diff --git a/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs b/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs index 42287ac50701b..ee425abfd1e27 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs +++ b/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs @@ -21,8 +21,9 @@ fn main() { call(move || { // this mutates a moved copy, and hence doesn't affect original - counter += 1; //~ WARN value assigned to `counter` is never read - //~| WARN unused variable: `counter` + counter += 1; + //~^ WARN value captured by `counter` is never read + //~| WARN value assigned to `counter` is never read }); assert_eq!(counter, 88); } diff --git a/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr b/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr index 190ef0f472463..3afb032747734 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr @@ -1,27 +1,27 @@ -warning: unused variable: `item` - --> $DIR/unboxed-closures-counter-not-moved.rs:15:13 +warning: value captured by `counter` is never read + --> $DIR/unboxed-closures-counter-not-moved.rs:24:9 | -LL | for item in y { - | ^^^^ help: if this is intentional, prefix it with an underscore: `_item` +LL | counter += 1; + | ^^^^^^^ | - = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + = help: did you mean to capture by reference instead? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default warning: value assigned to `counter` is never read --> $DIR/unboxed-closures-counter-not-moved.rs:24:9 | LL | counter += 1; - | ^^^^^^^ + | ^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default -warning: unused variable: `counter` - --> $DIR/unboxed-closures-counter-not-moved.rs:24:9 +warning: unused variable: `item` + --> $DIR/unboxed-closures-counter-not-moved.rs:15:13 | -LL | counter += 1; - | ^^^^^^^ +LL | for item in y { + | ^^^^ help: if this is intentional, prefix it with an underscore: `_item` | - = help: did you mean to capture by reference instead? + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default warning: 3 warnings emitted diff --git a/tests/ui/unboxed-closures/unboxed-closures-move-mutable.rs b/tests/ui/unboxed-closures/unboxed-closures-move-mutable.rs index f27461808c390..5710998cda799 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-move-mutable.rs +++ b/tests/ui/unboxed-closures/unboxed-closures-move-mutable.rs @@ -13,11 +13,17 @@ fn set(x: &mut usize) { *x = 42; } fn main() { { let mut x = 0_usize; - move || x += 1; //~ WARN unused variable: `x` + //~^ WARN unused variable: `x` + move || x += 1; + //~^ WARN value captured by `x` is never read + //~| WARN value assigned to `x` is never read } { let mut x = 0_usize; - move || x += 1; //~ WARN unused variable: `x` + //~^ WARN unused variable: `x` + move || x += 1; + //~^ WARN value captured by `x` is never read + //~| WARN value assigned to `x` is never read } { let mut x = 0_usize; diff --git a/tests/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/tests/ui/unboxed-closures/unboxed-closures-move-mutable.stderr index 24590128107b3..70c48b596c0ec 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-move-mutable.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-move-mutable.stderr @@ -1,19 +1,49 @@ -warning: unused variable: `x` - --> $DIR/unboxed-closures-move-mutable.rs:16:17 +warning: value captured by `x` is never read + --> $DIR/unboxed-closures-move-mutable.rs:24:17 | LL | move || x += 1; | ^ | = help: did you mean to capture by reference instead? - = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default -warning: unused variable: `x` - --> $DIR/unboxed-closures-move-mutable.rs:20:17 +warning: value assigned to `x` is never read + --> $DIR/unboxed-closures-move-mutable.rs:24:17 + | +LL | move || x += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: value captured by `x` is never read + --> $DIR/unboxed-closures-move-mutable.rs:17:17 | LL | move || x += 1; | ^ | = help: did you mean to capture by reference instead? -warning: 2 warnings emitted +warning: value assigned to `x` is never read + --> $DIR/unboxed-closures-move-mutable.rs:17:17 + | +LL | move || x += 1; + | ^^^^^^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `x` + --> $DIR/unboxed-closures-move-mutable.rs:15:13 + | +LL | let mut x = 0_usize; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_x` + | + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + +warning: unused variable: `x` + --> $DIR/unboxed-closures-move-mutable.rs:22:13 + | +LL | let mut x = 0_usize; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_x` + +warning: 6 warnings emitted From c653430714e28dea3b39118fd9f05ab8d2bc33ab Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 7 Sep 2025 02:17:13 +0000 Subject: [PATCH 1584/1900] Suggest unit struct and constants. --- compiler/rustc_mir_transform/messages.ftl | 2 + compiler/rustc_mir_transform/src/errors.rs | 18 +++ compiler/rustc_mir_transform/src/liveness.rs | 88 ++++++++++++- .../lint-uppercase-variables.stderr | 21 +++- tests/ui/or-patterns/binding-typo-2.rs | 16 +-- tests/ui/or-patterns/binding-typo-2.stderr | 118 ++++++++++++++++-- 6 files changed, 237 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index e8d29c414edca..71ec2db1ef00c 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -104,3 +104,5 @@ mir_transform_unused_variable = unused variable: `{$name}` mir_transform_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable mir_transform_unused_variable_try_ignore = try ignoring the field + +mir_transform_unused_variable_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}` diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 87430b8225b61..a039851681a67 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -170,6 +170,8 @@ pub(crate) struct UnusedCaptureMaybeCaptureRef { #[note] pub(crate) struct UnusedVarAssignedOnly { pub name: Symbol, + #[subdiagnostic] + pub typo: Option, } #[derive(LintDiagnostic)] @@ -235,6 +237,8 @@ pub(crate) enum UnusedVariableSugg { #[suggestion_part(code = "_{name}")] spans: Vec, name: Symbol, + #[subdiagnostic] + typo: Option, }, #[help(mir_transform_unused_variable_args_in_macro)] @@ -266,6 +270,20 @@ impl Subdiagnostic for UnusedVariableStringInterp { } } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + mir_transform_unused_variable_typo, + style = "verbose", + applicability = "maybe-incorrect" +)] +pub(crate) struct PatternTypo { + #[suggestion_part(code = "{code}")] + pub span: Span, + pub code: String, + pub item_name: Symbol, + pub kind: &'static str, +} + pub(crate) struct MustNotSupend<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, pub yield_sp: Span, diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 83b50e7be61b1..573c794f2838d 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1,8 +1,8 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; @@ -11,11 +11,13 @@ use rustc_middle::mir::visit::{ MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, }; use rustc_middle::mir::*; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor}; use rustc_session::lint; use rustc_span::Span; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{Symbol, kw, sym}; use crate::errors; @@ -201,6 +203,62 @@ fn maybe_suggest_literal_matching_name( finder.found } +/// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct. +fn maybe_suggest_unit_pattern_typo<'tcx>( + tcx: TyCtxt<'tcx>, + body_def_id: DefId, + name: Symbol, + span: Span, + ty: Ty<'tcx>, +) -> Option { + if let ty::Adt(adt_def, _) = ty.peel_refs().kind() { + let variant_names: Vec<_> = adt_def + .variants() + .iter() + .filter(|v| matches!(v.ctor, Some((CtorKind::Const, _)))) + .map(|v| v.name) + .collect(); + if let Some(name) = find_best_match_for_name(&variant_names, name, None) + && let Some(variant) = adt_def + .variants() + .iter() + .find(|v| v.name == name && matches!(v.ctor, Some((CtorKind::Const, _)))) + { + return Some(errors::PatternTypo { + span, + code: with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)), + kind: tcx.def_descr(variant.def_id), + item_name: variant.name, + }); + } + } + + // Look for consts of the same type with similar names as well, + // not just unit structs and variants. + let constants = tcx + .hir_body_owners() + .filter(|&def_id| { + matches!(tcx.def_kind(def_id), DefKind::Const) + && tcx.type_of(def_id).instantiate_identity() == ty + && tcx.visibility(def_id).is_accessible_from(body_def_id, tcx) + }) + .collect::>(); + let names = constants.iter().map(|&def_id| tcx.item_name(def_id)).collect::>(); + if let Some(item_name) = find_best_match_for_name(&names, name, None) + && let Some(position) = names.iter().position(|&n| n == item_name) + && let Some(&def_id) = constants.get(position) + { + return Some(errors::PatternTypo { + span, + code: with_no_trimmed_paths!(tcx.def_path_str(def_id)), + kind: "constant", + item_name, + }); + } + + None +} + /// Return whether we should consider the current place as a drop guard and skip reporting. fn maybe_drop_guard<'tcx>( tcx: TyCtxt<'tcx>, @@ -855,12 +913,27 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let from_macro = def_span.from_expansion() && introductions.iter().any(|intro| intro.span.eq_ctxt(def_span)); + let maybe_suggest_typo = || { + if let LocalKind::Arg = self.body.local_kind(local) { + None + } else { + maybe_suggest_unit_pattern_typo( + tcx, + self.body.source.def_id(), + name, + def_span, + decl.ty, + ) + } + }; + let statements = &mut self.assignments[index]; if statements.is_empty() { let sugg = if from_macro { errors::UnusedVariableSugg::NoSugg { span: def_span, name } } else { - errors::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name } + let typo = maybe_suggest_typo(); + errors::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name, typo } }; tcx.emit_node_span_lint( lint::builtin::UNUSED_VARIABLES, @@ -909,11 +982,12 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { continue; } + let typo = maybe_suggest_typo(); tcx.emit_node_span_lint( lint::builtin::UNUSED_VARIABLES, hir_id, def_span, - errors::UnusedVarAssignedOnly { name }, + errors::UnusedVarAssignedOnly { name, typo }, ); continue; } @@ -944,9 +1018,11 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { } else if from_macro { errors::UnusedVariableSugg::NoSugg { span: def_span, name } } else if !introductions.is_empty() { - errors::UnusedVariableSugg::TryPrefix { name, spans: spans.clone() } + let typo = maybe_suggest_typo(); + errors::UnusedVariableSugg::TryPrefix { name, typo, spans: spans.clone() } } else { - errors::UnusedVariableSugg::TryPrefix { name, spans: vec![def_span] } + let typo = maybe_suggest_typo(); + errors::UnusedVariableSugg::TryPrefix { name, typo, spans: vec![def_span] } }; tcx.emit_node_span_lint( diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index b0c56003957c9..09f76f86a4b14 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -16,7 +16,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ | note: the lint level is defined here --> $DIR/lint-uppercase-variables.rs:1:9 @@ -24,12 +24,29 @@ note: the lint level is defined here LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | foo::Foo::Foo => {} + | ++++++++++ +help: if this is intentional, prefix it with an underscore + | +LL | _Foo => {} + | + warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | let foo::Foo::Foo = foo::Foo::Foo; + | ++++++++++ +help: if this is intentional, prefix it with an underscore + | +LL | let _Foo = foo::Foo::Foo; + | + error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` --> $DIR/lint-uppercase-variables.rs:33:17 diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs index 275b68a22e22f..59df4459b88ef 100644 --- a/tests/ui/or-patterns/binding-typo-2.rs +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -16,8 +16,8 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named previously used binding `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead + //~| ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore //~| HELP: you might have meant to pattern match on the similarly named _ => {} } @@ -27,8 +27,8 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named unit variant `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead + //~| ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore //~| HELP: you might have meant to pattern match on the similarly named _ => {} } @@ -73,8 +73,8 @@ fn bar(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named constant `Bat` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead + //~| ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore //~| HELP: you might have meant to pattern match on the similarly named _ => {} } @@ -89,8 +89,8 @@ fn baz(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named constant `Bat` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead + //~| ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore //~| HELP: you might have meant to pattern match on the similarly named _ => {} } diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr index 1935d538edc66..23b570b3c23cf 100644 --- a/tests/ui/or-patterns/binding-typo-2.stderr +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -101,67 +101,165 @@ error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:14:23 | LL | (Foo, Bar) | (Ban, Foo) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ | note: the lint level is defined here --> $DIR/binding-typo-2.rs:2:9 | LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Lol::Bar, Foo) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | (Foo, Bar) | (_Ban, Foo) => {} + | + error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:25:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | (Foo, _) | (_Ban, Foo) => {} + | + error: unused variable: `Non` --> $DIR/binding-typo-2.rs:37:9 | LL | Non => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non => {} +LL + std::prelude::v1::None => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non => {} + | + error: unused variable: `Non` --> $DIR/binding-typo-2.rs:44:9 | LL | Non | None => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | None => {} +LL + std::prelude::v1::None | None => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | None => {} + | + error: unused variable: `Non` --> $DIR/binding-typo-2.rs:54:9 | LL | Non | Some(_) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | Some(_) => {} +LL + std::prelude::v1::None | Some(_) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | Some(_) => {} + | + error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:69:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | (Foo, _) | (_Ban, Foo) => {} + | + error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:86:21 | LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | (Foo, _) | (_Ban, Foo) => {} + | + error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:98:10 | LL | (Ban, _) => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ + | +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Ban, _) => {} +LL + (Lol::Bar, _) => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | (_Ban, _) => {} + | + error: unused variable: `Ban` --> $DIR/binding-typo-2.rs:104:9 | LL | Ban => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Ban` + | ^^^ + | +help: you might have meant to pattern match on the similarly named struct `Bay` + | +LL - Ban => {} +LL + Bay => {} + | +help: if this is intentional, prefix it with an underscore + | +LL | _Ban => {} + | + error: unused variable: `Batery` --> $DIR/binding-typo-2.rs:110:9 | LL | Batery => {} - | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_Batery` + | ^^^^^^ + | +help: you might have meant to pattern match on the similarly named constant `Battery` + | +LL | Battery => {} + | + +help: if this is intentional, prefix it with an underscore + | +LL | _Batery => {} + | + error: aborting due to 16 previous errors From 72a04a7e0a871280cbeea1d19f2c32724092e88e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Jul 2025 13:02:28 +0000 Subject: [PATCH 1585/1900] Silence warning in r-a. --- .../rust-analyzer/crates/ide-db/src/imports/merge_imports.rs | 2 ++ src/tools/rust-analyzer/crates/test-fixture/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 4e779a7d858e5..635ed7368c4c0 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -405,6 +405,8 @@ fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Op } else { ted::replace_with_many(subtree.syntax(), elements); } + // Silence unused assignment warning on `modified`. + let _ = modified; modified = true; } else { modified |= recursive_normalize(&subtree, NormalizationStyle::Default).is_some(); diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 7574d12c0cfd0..a4549794db336 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -383,6 +383,8 @@ impl ChangeFixture { } } + let _ = file_id; + let root = match current_source_root_kind { SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)), SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)), From f216ec23ba658d3515a9dd43953998ecdbefa0d1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Jul 2025 14:17:20 +0000 Subject: [PATCH 1586/1900] Silence warning in miri tests. --- .../miri/tests/fail/function_calls/arg_inplace_locals_alias.rs | 3 +++ .../tests/fail/function_calls/arg_inplace_observe_after.rs | 1 + .../tests/fail/function_calls/arg_inplace_observe_during.rs | 1 + .../miri/tests/pass/align_repeat_into_well_aligned_array.rs | 1 + 4 files changed, 6 insertions(+) diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs index 744d64b9b1e88..03cca04702fee 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs @@ -4,7 +4,10 @@ //@[tree]compile-flags: -Zmiri-tree-borrows // Validation forces more things into memory, which we can't have here. //@compile-flags: -Zmiri-disable-validation + #![feature(custom_mir, core_intrinsics)] +#![allow(unused)] + use std::intrinsics::mir::*; pub struct S(i32); diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs index 18daf9497a15b..cea6c141c4de9 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs @@ -22,6 +22,7 @@ fn main() { } } +#[expect(unused_variables, unused_assignments)] pub fn change_arg(mut x: S) { x.0 = 0; } diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs index 2201bf17bfc78..581b71499575c 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs @@ -23,6 +23,7 @@ fn main() { } } +#[expect(unused_variables, unused_assignments)] pub fn change_arg(mut x: S, ptr: *mut S) { x.0 = 0; // If `x` got passed in-place, we'd see the write through `ptr`! diff --git a/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs b/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs index 735251039f772..82a74e392b9cc 100644 --- a/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs +++ b/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs @@ -2,6 +2,7 @@ use std::mem::size_of; +#[expect(unused_variables, unused_assignments)] fn main() { let mut a = Params::new(); // The array itself here happens to be quite well-aligned, but not all its elements have that From 4419d890c6d1169601f10dc62ef8d985f636b499 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 30 Aug 2025 13:10:06 +0000 Subject: [PATCH 1587/1900] Bless ui asm. --- tests/ui/asm/x86_64/goto.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/asm/x86_64/goto.stderr b/tests/ui/asm/x86_64/goto.stderr index 230faf8001136..5342e43c58a1c 100644 --- a/tests/ui/asm/x86_64/goto.stderr +++ b/tests/ui/asm/x86_64/goto.stderr @@ -25,7 +25,7 @@ LL | let mut value = false; | ^^^^^ | = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` on by default + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default warning: 2 warnings emitted From 49922d5b96594a005467fbea2d8e438c90e3699d Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 30 Aug 2025 18:23:34 +0000 Subject: [PATCH 1588/1900] Remove unreachable expression warning from std. --- library/std/src/sync/poison.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 17abdb9819bf9..5517082033380 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -60,6 +60,9 @@ //! while it is locked exclusively (write mode). If a panic occurs in any reader, //! then the lock will not be poisoned. +// If we are not unwinding, `PoisonError` is uninhabited. +#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))] + #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::Condvar; #[unstable(feature = "mapped_lock_guards", issue = "117108")] From 918b2d88e9fd3a8b23731a0adc913c76b6dfc67f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 Jun 2025 18:17:09 +0000 Subject: [PATCH 1589/1900] Diagnose liveness on MIR. --- tests/ui/needless_match.fixed | 2 +- tests/ui/needless_match.rs | 2 +- tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 41acf44023f63..57273012a192b 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index 936653b961bbb..3eb577868f2b4 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index ad30c94f34781..2942f64741e91 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 505afb340009a..f2da414e9f652 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] From b198bf80602db74c2517c06aad19e200999024cb Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 11 Oct 2025 20:56:10 +0000 Subject: [PATCH 1590/1900] Rebase fallout. --- compiler/rustc_mir_transform/src/liveness.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 573c794f2838d..82f9dfe4745d2 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -741,7 +741,6 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { ever_live.union(live); match &statement.kind { StatementKind::Assign(box (place, _)) - | StatementKind::Deinit(box place) | StatementKind::SetDiscriminant { box place, .. } => { check_place(*place, AccessKind::Assign, statement.source_info, live); } @@ -1345,9 +1344,7 @@ impl DefUse { let is_indirect = is_indirect(projection); match context { PlaceContext::MutatingUse( - MutatingUseContext::Store - | MutatingUseContext::Deinit - | MutatingUseContext::SetDiscriminant, + MutatingUseContext::Store | MutatingUseContext::SetDiscriminant, ) => { if is_indirect { // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a From 340702c0c2524e57e1a4950c22c3bd1b01e4a586 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Wed, 17 Sep 2025 22:56:47 +0100 Subject: [PATCH 1591/1900] write x.py's help for saving output time Currently x.py help (or x.py --help) builds bootstrap binary everytime, but it delays printing help. This change saves the current top level help text into a file. x.py help prints the file and doesn't touch bootstrap binary. x.py test bootstrap checks if the file is up to date. Note that subcommand level helps (e.g., x.py check --help) aren't saved. --- src/bootstrap/bootstrap.py | 24 +++++- src/bootstrap/src/core/build_steps/run.rs | 29 ++++++- src/bootstrap/src/core/build_steps/test.rs | 21 ++++- src/bootstrap/src/core/builder/mod.rs | 1 + src/bootstrap/src/core/config/flags.rs | 6 ++ src/etc/xhelp | 96 ++++++++++++++++++++++ 6 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/etc/xhelp diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 8b1775178c915..4dd465edb0df9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1379,11 +1379,26 @@ def main(): sys.argv[1] = "-h" args = parse_args(sys.argv) - help_triggered = args.help or len(sys.argv) == 1 - # If the user is asking for help, let them know that the whole download-and-build + # Root help (e.g., x.py --help) prints help from the saved file to save the time + if len(sys.argv) == 1 or sys.argv[1] in ["-h", "--help"]: + try: + with open( + os.path.join(os.path.dirname(__file__), "../etc/xhelp"), "r" + ) as f: + # The file from bootstrap func already has newline. + print(f.read(), end="") + sys.exit(0) + except Exception as error: + eprint( + f"ERROR: unable to run help: {error}\n", + "x.py run generate-help may solve the problem.", + ) + sys.exit(1) + + # If the user is asking for other helps, let them know that the whole download-and-build # process has to happen before anything is printed out. - if help_triggered: + if args.help: eprint( "INFO: Downloading and building bootstrap before processing --help command.\n" " See src/bootstrap/README.md for help with common commands." @@ -1401,13 +1416,14 @@ def main(): eprint(error) success_word = "unsuccessfully" - if not help_triggered: + if not args.help: eprint( "Build completed", success_word, "in", format_build_time(time() - start_time), ) + sys.exit(exit_code) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index b9a4c1bf9b44b..bd5ba89229117 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, To use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::TargetSelection; -use crate::core::config::flags::get_completion; +use crate::core::config::flags::{get_completion, top_level_help}; use crate::utils::exec::command; use crate::{Mode, t}; @@ -512,3 +512,30 @@ impl Step for Rustfmt { rustfmt.into_cmd().run(builder); } } + +/// Return the path of x.py's help. +pub fn get_help_path(builder: &Builder<'_>) -> PathBuf { + builder.src.join("src/etc/xhelp") +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenerateHelp; + +impl Step for GenerateHelp { + type Output = (); + + fn run(self, builder: &Builder<'_>) { + let help = top_level_help(); + let path = get_help_path(builder); + std::fs::write(&path, help) + .unwrap_or_else(|e| panic!("writing help into {} failed: {e:?}", path.display())); + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("generate-help") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(GenerateHelp) + } +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 154b209b20252..4babd32d5d4c4 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; -use crate::core::build_steps::run::get_completion_paths; +use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, @@ -28,7 +28,7 @@ use crate::core::builder::{ crate_description, }; use crate::core::config::TargetSelection; -use crate::core::config::flags::{Subcommand, get_completion}; +use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -1292,6 +1292,23 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to ); crate::exit!(1); } + + builder.info("x.py help check"); + if builder.config.cmd.bless() { + builder.ensure(crate::core::build_steps::run::GenerateHelp); + } else { + let help_path = get_help_path(builder); + let cur_help = std::fs::read_to_string(&help_path).unwrap_or_else(|err| { + eprintln!("couldn't read {}: {}", help_path.display(), err); + crate::exit!(1); + }); + let new_help = top_level_help(); + + if new_help != cur_help { + eprintln!("x.py help was changed; run `x.py run generate-help` to update it"); + crate::exit!(1); + } + } } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 99fb62ea31c9d..d660680942751 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1239,6 +1239,7 @@ impl<'a> Builder<'a> { run::CyclicStep, run::CoverageDump, run::Rustfmt, + run::GenerateHelp, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index c01b71b926068..9e408505933d3 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -701,3 +701,9 @@ pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option { } Some(String::from_utf8(buf).expect("completion script should be UTF-8")) } + +/// Return the top level help of the bootstrap. +pub fn top_level_help() -> String { + let mut cmd = Flags::command(); + cmd.render_help().to_string() +} diff --git a/src/etc/xhelp b/src/etc/xhelp new file mode 100644 index 0000000000000..5880d070d3d66 --- /dev/null +++ b/src/etc/xhelp @@ -0,0 +1,96 @@ + +Usage: x.py [options] [...] + +Commands: + build Compile either the compiler or libraries + check Compile either the compiler or libraries, using cargo check + clippy Run Clippy (uses rustup/cargo-installed clippy binary) + fix Run cargo fix + fmt Run rustfmt + doc Build documentation + test Build and run some test suites + miri Build and run some test suites *in Miri* + bench Build and run some benchmarks + clean Clean out build directories + dist Build distribution artifacts + install Install distribution artifacts + run Run tools contained in this repository + setup Set up the environment for development + vendor Vendor dependencies + perf Perform profiling and benchmarking of the compiler using `rustc-perf` + +Arguments: + [PATHS]... paths for the subcommand + [ARGS]... arguments passed to subcommands + +Options: + -v, --verbose... + use verbose output (-vv for very verbose) + -i, --incremental + use incremental compilation + --config + TOML configuration file for build + --build-dir

+ Build directory, overrides `build.build-dir` in `bootstrap.toml` + --build + host target of the stage0 compiler + --host + host targets to build + --target + target targets to build + --exclude + build paths to exclude + --skip + build paths to skip + --include-default-paths + include default paths in addition to the provided ones + --rustc-error-format + rustc error format + --on-fail + command to run on failure + --dry-run + dry run; don't build anything + --dump-bootstrap-shims + Indicates whether to dump the work done from bootstrap shims + --stage + stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.) + --keep-stage + stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1) + --keep-stage-std + stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1) + --src + path to the root of the rust checkout + -j, --jobs + number of jobs to run in parallel + --warnings + if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour [default: default] [possible values: deny, warn, default] + --json-output + use message-format=json + --compile-time-deps + only build proc-macros and build scripts (for rust-analyzer) + --color
", match tooltip { Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", @@ -79,58 +81,48 @@ fn write_header( Tooltip::Edition(_) => " edition", Tooltip::None => "", } - ), - ); - - if tooltip != Tooltip::None { - let tooltip = fmt::from_fn(|f| match &tooltip { - Tooltip::IgnoreAll => f.write_str("This example is not tested"), - Tooltip::IgnoreSome(platforms) => { - f.write_str("This example is not tested on ")?; - match &platforms[..] { - [] => unreachable!(), - [platform] => f.write_str(platform)?, - [first, second] => write!(f, "{first} or {second}")?, - [platforms @ .., last] => { - for platform in platforms { - write!(f, "{platform}, ")?; + )?; + + if *tooltip != Tooltip::None { + let tooltip = fmt::from_fn(|f| match tooltip { + Tooltip::IgnoreAll => f.write_str("This example is not tested"), + Tooltip::IgnoreSome(platforms) => { + f.write_str("This example is not tested on ")?; + match &platforms[..] { + [] => unreachable!(), + [platform] => f.write_str(platform)?, + [first, second] => write!(f, "{first} or {second}")?, + [platforms @ .., last] => { + for platform in platforms { + write!(f, "{platform}, ")?; + } + write!(f, "or {last}")?; } - write!(f, "or {last}")?; } + Ok(()) } - Ok(()) - } - Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), - Tooltip::ShouldPanic => f.write_str("This example panics"), - Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), - Tooltip::None => unreachable!(), + Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), + Tooltip::ShouldPanic => f.write_str("This example panics"), + Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), + Tooltip::None => unreachable!(), + }); + + write!(f, "")?; + } + + if let Some(extra) = extra_content { + f.write_str(extra)?; + } + + let classes = fmt::from_fn(|f| { + iter::once("rust") + .chain(Some(class).filter(|class| !class.is_empty())) + .chain(extra_classes.iter().map(String::as_str)) + .joined(" ", f) }); - write_str(out, format_args!("")); - } - if let Some(extra) = extra_content { - out.push_str(extra); - } - if class.is_empty() { - write_str( - out, - format_args!( - "
",
-                if extra_classes.is_empty() { "" } else { " " },
-                extra_classes.join(" ")
-            ),
-        );
-    } else {
-        write_str(
-            out,
-            format_args!(
-                "
",
-                if extra_classes.is_empty() { "" } else { " " },
-                extra_classes.join(" ")
-            ),
-        );
-    }
-    write_str(out, format_args!(""));
+        write!(f, "
")
+    })
 }
 
 /// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@@ -577,8 +569,8 @@ pub(super) fn write_code(
     });
 }
 
-fn write_footer(out: &mut String, playground_button: Option<&str>) {
-    write_str(out, format_args!("
{}

( &self, tcx: DbInterner<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index b640039af625b..29e7b883c93bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -15,7 +15,7 @@ use tracing::debug; use crate::next_solver::SolverDefId; use crate::next_solver::Ty; -use crate::next_solver::infer::InferCtxtUndoLogs; +use crate::next_solver::infer::{InferCtxtUndoLogs, iter_idx_range}; /// Represents a single undo-able action that affects a type inference variable. #[derive(Clone)] @@ -59,7 +59,7 @@ impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { } } -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { /// The origins of each type variable. values: IndexVec, @@ -102,7 +102,7 @@ pub struct TypeVariableOrigin { pub param_def_id: Option, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, } @@ -267,6 +267,15 @@ impl<'db> TypeVariableTable<'_, 'db> { self.storage.sub_unification_table.with_log(self.undo_log) } + /// Returns a range of the type variables created during the snapshot. + pub(crate) fn vars_since_snapshot( + &mut self, + value_count: usize, + ) -> (Range, Vec) { + let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); + (range.clone(), iter_idx_range(range).map(|index| self.var_origin(index)).collect()) + } + /// Returns indices of all variables that are not yet /// instantiated. pub(crate) fn unresolved_variables(&mut self) -> Vec { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs new file mode 100644 index 0000000000000..cab3c69118f64 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -0,0 +1,501 @@ +pub use ra_ap_rustc_next_trait_solver::solve::inspect::*; + +use rustc_ast_ir::try_visit; +use rustc_next_trait_solver::{ + resolve::eager_resolve_vars, + solve::{ + SolverDelegateEvalExt, + inspect::{self, instantiate_canonical_state}, + }, +}; +use rustc_type_ir::{ + VisitorResult, + inherent::{IntoKind, Span as _}, + solve::{Certainty, GoalSource, MaybeCause, NoSolution}, +}; + +use crate::next_solver::{ + DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, PredicateKind, + QueryResult, SolverContext, Span, Term, + fulfill::NextSolverError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, +}; + +pub struct InspectConfig { + pub max_depth: usize, +} + +pub struct InspectGoal<'a, 'db> { + infcx: &'a SolverContext<'db>, + depth: usize, + orig_values: Vec>, + goal: Goal<'db, Predicate<'db>>, + result: Result, + final_revision: inspect::Probe>, + normalizes_to_term_hack: Option>, + source: GoalSource, +} + +impl<'a, 'db> std::fmt::Debug for InspectGoal<'a, 'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InspectGoal") + .field("depth", &self.depth) + .field("orig_values", &self.orig_values) + .field("goal", &self.goal) + .field("result", &self.result) + .field("final_revision", &self.final_revision) + .field("normalizes_to_term_hack", &self.normalizes_to_term_hack) + .field("source", &self.source) + .finish() + } +} + +impl<'a, 'db> std::fmt::Debug for InspectCandidate<'a, 'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InspectCandidate") + .field("kind", &self.kind) + .field("steps", &self.steps) + .field("final_state", &self.final_state) + .field("result", &self.result) + .field("shallow_certainty", &self.shallow_certainty) + .finish() + } +} + +/// The expected term of a `NormalizesTo` goal gets replaced +/// with an unconstrained inference variable when computing +/// `NormalizesTo` goals and we return the nested goals to the +/// caller, who also equates the actual term with the expected. +/// +/// This is an implementation detail of the trait solver and +/// not something we want to leak to users. We therefore +/// treat `NormalizesTo` goals as if they apply the expected +/// type at the end of each candidate. +#[derive(Debug, Copy, Clone)] +struct NormalizesToTermHack<'db> { + term: Term<'db>, + unconstrained_term: Term<'db>, +} + +impl<'db> NormalizesToTermHack<'db> { + /// Relate the `term` with the new `unconstrained_term` created + /// when computing the proof tree for this `NormalizesTo` goals. + /// This handles nested obligations. + fn constrain_and( + &self, + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + f: impl FnOnce(&mut ObligationCtxt<'_, 'db>), + ) -> Result { + let mut ocx = ObligationCtxt::new(infcx); + ocx.eq(&ObligationCause::dummy(), param_env, self.term, self.unconstrained_term)?; + f(&mut ocx); + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(Certainty::Yes) + } else if errors.iter().all(|e| !matches!(e, NextSolverError::TrueError(_))) { + Ok(Certainty::AMBIGUOUS) + } else { + Err(NoSolution) + } + } +} + +pub struct InspectCandidate<'a, 'db> { + goal: &'a InspectGoal<'a, 'db>, + kind: inspect::ProbeKind>, + steps: Vec<&'a inspect::ProbeStep>>, + final_state: inspect::CanonicalState, ()>, + result: QueryResult<'db>, + shallow_certainty: Certainty, +} + +impl<'a, 'db> InspectCandidate<'a, 'db> { + pub fn kind(&self) -> inspect::ProbeKind> { + self.kind + } + + pub fn result(&self) -> Result { + self.result.map(|c| c.value.certainty) + } + + pub fn goal(&self) -> &'a InspectGoal<'a, 'db> { + self.goal + } + + /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`. + /// + /// If this certainty is `Yes`, then we must be confident that the candidate + /// must hold iff it's nested goals hold. This is not true if the certainty is + /// `Maybe(..)`, which suggests we forced ambiguity instead. + /// + /// This is *not* the certainty of the candidate's full nested evaluation, which + /// can be accessed with [`Self::result`] instead. + pub fn shallow_certainty(&self) -> Certainty { + self.shallow_certainty + } + + /// Visit all nested goals of this candidate without rolling + /// back their inference constraints. This function modifies + /// the state of the `infcx`. + pub fn visit_nested_no_probe>(&self, visitor: &mut V) -> V::Result { + for goal in self.instantiate_nested_goals() { + try_visit!(goal.visit_with(visitor)); + } + + V::Result::output() + } + + /// Instantiate the nested goals for the candidate without rolling back their + /// inference constraints. This function modifies the state of the `infcx`. + /// + /// See [`Self::instantiate_impl_args`] if you need the impl args too. + pub fn instantiate_nested_goals(&self) -> Vec> { + let infcx = self.goal.infcx; + let param_env = self.goal.goal.param_env; + let mut orig_values = self.goal.orig_values.to_vec(); + + let mut instantiated_goals = vec![]; + for step in &self.steps { + match **step { + inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push(( + source, + instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + goal, + ), + )), + inspect::ProbeStep::RecordImplArgs { .. } => {} + inspect::ProbeStep::MakeCanonicalResponse { .. } + | inspect::ProbeStep::NestedProbe(_) => unreachable!(), + } + } + + let () = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + self.final_state, + ); + + if let Some(term_hack) = &self.goal.normalizes_to_term_hack { + // FIXME: We ignore the expected term of `NormalizesTo` goals + // when computing the result of its candidates. This is + // scuffed. + let _ = term_hack.constrain_and(infcx, param_env, |_| {}); + } + + instantiated_goals + .into_iter() + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal)) + .collect() + } + + /// Instantiate the args of an impl if this candidate came from a + /// `CandidateSource::Impl`. This function modifies the state of the + /// `infcx`. + pub fn instantiate_impl_args(&self) -> GenericArgs<'db> { + let infcx = self.goal.infcx; + let param_env = self.goal.goal.param_env; + let mut orig_values = self.goal.orig_values.to_vec(); + + for step in &self.steps { + match **step { + inspect::ProbeStep::RecordImplArgs { impl_args } => { + let impl_args = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + impl_args, + ); + + let () = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + self.final_state, + ); + + // No reason we couldn't support this, but we don't need to for select. + assert!( + self.goal.normalizes_to_term_hack.is_none(), + "cannot use `instantiate_impl_args` with a `NormalizesTo` goal" + ); + + return eager_resolve_vars(infcx, impl_args); + } + inspect::ProbeStep::AddGoal(..) => {} + inspect::ProbeStep::MakeCanonicalResponse { .. } + | inspect::ProbeStep::NestedProbe(_) => unreachable!(), + } + } + + panic!("expected impl args probe step for `instantiate_impl_args`"); + } + + pub fn instantiate_proof_tree_for_nested_goal( + &self, + source: GoalSource, + goal: Goal<'db, Predicate<'db>>, + ) -> InspectGoal<'a, 'db> { + let infcx = self.goal.infcx; + match goal.predicate.kind().no_bound_vars() { + Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => { + let unconstrained_term = infcx.next_term_var_of_kind(term); + let goal = + goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term }); + // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the + // expected term. This means that candidates which only fail due to nested goals + // and which normalize to a different term then the final result could ICE: when + // building their proof tree, the expected term was unconstrained, but when + // instantiating the candidate it is already constrained to the result of another + // candidate. + let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term }; + let (proof_tree, nested_goals_result) = infcx.probe(|_| { + // Here, if we have any nested goals, then we make sure to apply them + // considering the constrained RHS, and pass the resulting certainty to + // `InspectGoal::new` so that the goal has the right result (and maintains + // the impression that we don't do this normalizes-to infer hack at all). + let (nested, proof_tree) = + infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + let nested_goals_result = nested.and_then(|nested| { + normalizes_to_term_hack.constrain_and( + infcx, + proof_tree.uncanonicalized_goal.param_env, + |ocx| { + ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| { + Obligation::new( + infcx.interner, + ObligationCause::dummy(), + goal.param_env, + goal.predicate, + ) + })); + }, + ) + }); + (proof_tree, nested_goals_result) + }); + InspectGoal::new( + infcx, + self.goal.depth + 1, + proof_tree, + Some((normalizes_to_term_hack, nested_goals_result)), + source, + ) + } + _ => { + // We're using a probe here as evaluating a goal could constrain + // inference variables by choosing one candidate. If we then recurse + // into another candidate who ends up with different inference + // constraints, we get an ICE if we already applied the constraints + // from the chosen candidate. + let proof_tree = + infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1); + InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) + } + } + } + + /// Visit all nested goals of this candidate, rolling back + /// all inference constraints. + pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result { + self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor)) + } +} + +impl<'a, 'db> InspectGoal<'a, 'db> { + pub fn infcx(&self) -> &'a InferCtxt<'db> { + self.infcx + } + + pub fn goal(&self) -> Goal<'db, Predicate<'db>> { + self.goal + } + + pub fn result(&self) -> Result { + self.result + } + + pub fn source(&self) -> GoalSource { + self.source + } + + pub fn depth(&self) -> usize { + self.depth + } + + fn candidates_recur( + &'a self, + candidates: &mut Vec>, + steps: &mut Vec<&'a inspect::ProbeStep>>, + probe: &'a inspect::Probe>, + ) { + let mut shallow_certainty = None; + for step in &probe.steps { + match *step { + inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => { + steps.push(step) + } + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { + assert!(matches!( + shallow_certainty.replace(c), + None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + )); + } + inspect::ProbeStep::NestedProbe(ref probe) => { + match probe.kind { + // These never assemble candidates for the goal we're trying to solve. + inspect::ProbeKind::ProjectionCompatibility + | inspect::ProbeKind::ShadowedEnvProbing => continue, + + inspect::ProbeKind::NormalizedSelfTyAssembly + | inspect::ProbeKind::UnsizeAssembly + | inspect::ProbeKind::Root { .. } + | inspect::ProbeKind::TraitCandidate { .. } + | inspect::ProbeKind::OpaqueTypeStorageLookup { .. } + | inspect::ProbeKind::RigidAlias { .. } => { + // Nested probes have to prove goals added in their parent + // but do not leak them, so we truncate the added goals + // afterwards. + let num_steps = steps.len(); + self.candidates_recur(candidates, steps, probe); + steps.truncate(num_steps); + } + } + } + } + } + + match probe.kind { + inspect::ProbeKind::ProjectionCompatibility + | inspect::ProbeKind::ShadowedEnvProbing => { + panic!() + } + + inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {} + + // We add a candidate even for the root evaluation if there + // is only one way to prove a given goal, e.g. for `WellFormed`. + inspect::ProbeKind::Root { result } + | inspect::ProbeKind::TraitCandidate { source: _, result } + | inspect::ProbeKind::OpaqueTypeStorageLookup { result } + | inspect::ProbeKind::RigidAlias { result } => { + // We only add a candidate if `shallow_certainty` was set, which means + // that we ended up calling `evaluate_added_goals_and_make_canonical_response`. + if let Some(shallow_certainty) = shallow_certainty { + candidates.push(InspectCandidate { + goal: self, + kind: probe.kind, + steps: steps.clone(), + final_state: probe.final_state, + shallow_certainty, + result, + }); + } + } + } + } + + pub fn candidates(&'a self) -> Vec> { + let mut candidates = vec![]; + let mut nested_goals = vec![]; + self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision); + candidates + } + + /// Returns the single candidate applicable for the current goal, if it exists. + /// + /// Returns `None` if there are either no or multiple applicable candidates. + pub fn unique_applicable_candidate(&'a self) -> Option> { + // FIXME(-Znext-solver): This does not handle impl candidates + // hidden by env candidates. + let mut candidates = self.candidates(); + candidates.retain(|c| c.result().is_ok()); + candidates.pop().filter(|_| candidates.is_empty()) + } + + fn new( + infcx: &'a InferCtxt<'db>, + depth: usize, + root: inspect::GoalEvaluation>, + term_hack_and_nested_certainty: Option<( + NormalizesToTermHack<'db>, + Result, + )>, + source: GoalSource, + ) -> Self { + let infcx = <&SolverContext<'db>>::from(infcx); + + let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, final_revision, result } = + root; + // If there's a normalizes-to goal, AND the evaluation result with the result of + // constraining the normalizes-to RHS and computing the nested goals. + let result = result.and_then(|ok| { + let nested_goals_certainty = + term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?; + Ok(ok.value.certainty.and(nested_goals_certainty)) + }); + + InspectGoal { + infcx, + depth, + orig_values, + goal: eager_resolve_vars(infcx, uncanonicalized_goal), + result, + final_revision, + normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n), + source, + } + } + + pub(crate) fn visit_with>(&self, visitor: &mut V) -> V::Result { + if self.depth < visitor.config().max_depth { + try_visit!(visitor.visit_goal(self)); + } + + V::Result::output() + } +} + +/// The public API to interact with proof trees. +pub trait ProofTreeVisitor<'db> { + type Result: VisitorResult; + + fn config(&self) -> InspectConfig { + InspectConfig { max_depth: 10 } + } + + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result; +} + +impl<'db> InferCtxt<'db> { + pub(crate) fn visit_proof_tree>( + &self, + goal: Goal<'db, Predicate<'db>>, + visitor: &mut V, + ) -> V::Result { + self.visit_proof_tree_at_depth(goal, 0, visitor) + } + + pub(crate) fn visit_proof_tree_at_depth>( + &self, + goal: Goal<'db, Predicate<'db>>, + depth: usize, + visitor: &mut V, + ) -> V::Result { + let (_, proof_tree) = <&SolverContext<'db>>::from(self) + .evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7b6e8a1073d78..8f731b06fa197 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -48,8 +48,8 @@ use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineIdWrapper, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, RegionAssumptions, - SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, + CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, + RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -1055,60 +1055,62 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { - match def_id { - SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter( - self, - self.db() - .variances_of(hir_def::GenericDefId::FunctionId(def_id)) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| v.to_nextsolver(self)), - ), - SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter( - self, - self.db() - .variances_of(hir_def::GenericDefId::AdtId(def_id)) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| v.to_nextsolver(self)), - ), + let generic_def = match def_id { + SolverDefId::FunctionId(def_id) => def_id.into(), + SolverDefId::AdtId(def_id) => def_id.into(), + SolverDefId::Ctor(Ctor::Struct(def_id)) => def_id.into(), + SolverDefId::Ctor(Ctor::Enum(def_id)) => def_id.loc(self.db).parent.into(), SolverDefId::InternedOpaqueTyId(_def_id) => { // FIXME(next-solver): track variances // // We compute them based on the only `Ty` level info in rustc, // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. - VariancesOf::new_from_iter( + return VariancesOf::new_from_iter( self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), - ) + ); } - _ => VariancesOf::new_from_iter(self, []), - } + _ => return VariancesOf::new_from_iter(self, []), + }; + VariancesOf::new_from_iter( + self, + self.db() + .variances_of(generic_def) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ) } fn type_of(self, def_id: Self::DefId) -> EarlyBinder { - let def_id = match def_id { + match def_id { SolverDefId::TypeAliasId(id) => { use hir_def::Lookup; match id.lookup(self.db()).container { ItemContainerId::ImplId(it) => it, _ => panic!("assoc ty value should be in impl"), }; - crate::TyDefId::TypeAliasId(id) + self.db().ty_ns(id.into()) } - SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + SolverDefId::AdtId(id) => self.db().ty_ns(id.into()), // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. // // We currently always use the type from HIR typeck which ignores regions. This // should be fine. - SolverDefId::InternedOpaqueTyId(_) => { - return self.type_of_opaque_hir_typeck(def_id); + SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id), + SolverDefId::FunctionId(id) => self.db.value_ty_ns(id.into()).unwrap(), + SolverDefId::Ctor(id) => { + let id = match id { + Ctor::Struct(id) => id.into(), + Ctor::Enum(id) => id.into(), + }; + self.db + .value_ty_ns(id) + .expect("`SolverDefId::Ctor` should have a function-like ctor") } _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), - }; - self.db().ty_ns(def_id) + } } fn adt_def(self, def_id: Self::AdtId) -> Self::AdtDef { @@ -1998,6 +2000,28 @@ impl<'db> DbInterner<'db> { ) -> T { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + + pub fn mk_fn_sig( + self, + inputs: I, + output: Ty<'db>, + c_variadic: bool, + safety: Safety, + abi: FnAbi, + ) -> FnSig<'db> + where + I: IntoIterator>, + { + FnSig { + inputs_and_output: Tys::new_from_iter( + self, + inputs.into_iter().chain(std::iter::once(output)), + ), + c_variadic, + safety, + abi, + } + } } macro_rules! TrivialTypeTraversalImpls { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index eb2cc69167d0d..d8a86ea083e08 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -147,6 +147,24 @@ pub trait NextSolverToChalk<'db, Out> { fn to_chalk(self, interner: DbInterner<'db>) -> Out; } +impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { + fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Mutability { + match self { + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + } + } +} + +impl NextSolverToChalk<'_, chalk_ir::Safety> for crate::next_solver::abi::Safety { + fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Safety { + match self { + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + } + } +} + impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { Ty::new( @@ -617,6 +635,15 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution } } +impl<'db> NextSolverToChalk<'db, crate::Substitution> for Tys<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::Substitution { + Substitution::from_iter( + Interner, + self.inner().iter().map(|ty| ty.to_chalk(interner).cast(Interner)), + ) + } +} + impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) @@ -859,6 +886,16 @@ impl<'db> NextSolverToChalk<'db, chalk_ir::Goal> for Predicate<'db> { } } +impl<'db> NextSolverToChalk<'db, crate::ProjectionTy> for crate::next_solver::AliasTy<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::ProjectionTy { + let SolverDefId::TypeAliasId(assoc_id) = self.def_id else { unreachable!() }; + crate::ProjectionTy { + associated_ty_id: to_assoc_type_id(assoc_id), + substitution: self.args.to_chalk(interner), + } + } +} + impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { let clauses = Clauses::new_from_iter( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs similarity index 87% rename from src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs rename to src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index 42c238febf6f6..41cb4884404f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -1,35 +1,23 @@ -//! Normalization within a next-solver infer context. - -use std::fmt::Debug; - use rustc_next_trait_solver::placeholder::BoundVarReplacer; use rustc_type_ir::{ AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, - inherent::{IntoKind, Span as _, Term as _}, + inherent::{IntoKind, Term as _}, }; +use crate::next_solver::SolverDefId; use crate::next_solver::{ - Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, - TyKind, TypingMode, + Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Term, Ty, + TyKind, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, + InferCtxt, at::At, traits::{Obligation, ObligationCause}, }, util::PlaceholderReplacer, }; -pub fn normalize<'db, T>(interner: DbInterner<'db>, param_env: ParamEnv<'db>, value: T) -> T -where - T: TypeFoldable>, -{ - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let cause = ObligationCause::dummy(); - deeply_normalize(infer_ctxt.at(&cause, param_env), value.clone()).unwrap_or(value) -} - /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> @@ -81,10 +69,16 @@ where T: TypeFoldable>, { let fulfill_cx = FulfillmentCtxt::new(at.infcx); - let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let mut folder = NormalizationFolder { + at, + fulfill_cx, + depth: 0, + universes, + stalled_coroutine_goals: vec![], + }; let value = value.try_fold_with(&mut folder)?; let errors = folder.fulfill_cx.select_all_or_error(at.infcx); - if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) } + if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'db> { @@ -92,6 +86,7 @@ struct NormalizationFolder<'me, 'db> { fulfill_cx: FulfillmentCtxt<'db>, depth: usize, universes: Vec>, + stalled_coroutine_goals: Vec>>, } impl<'db> NormalizationFolder<'_, 'db> { @@ -100,22 +95,30 @@ impl<'db> NormalizationFolder<'_, 'db> { alias_term: Term<'db>, ) -> Result, Vec>> { let infcx = self.at.infcx; - let tcx = infcx.interner; - let recursion_limit = tcx.recursion_limit(); - if self.depth > recursion_limit { - return Err(vec![]); - } + let interner = infcx.interner; + let recursion_limit = interner.recursion_limit(); self.depth += 1; let infer_term = infcx.next_term_var_of_kind(alias_term); let obligation = Obligation::new( - tcx, + interner, self.at.cause.clone(), self.at.param_env, PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), ); + if self.depth > recursion_limit { + // let term = alias_term.to_alias_term().unwrap(); + // self.at.infcx.err_ctxt().report_overflow_error( + // OverflowCause::DeeplyNormalize(term), + // self.at.cause.span, + // true, + // |_| {}, + // ); + return Err(vec![NextSolverError::Overflow(obligation)]); + } + self.fulfill_cx.register_predicate_obligation(infcx, obligation); self.select_all_and_stall_coroutine_predicates()?; @@ -140,6 +143,13 @@ impl<'db> NormalizationFolder<'_, 'db> { return Err(errors); } + self.stalled_coroutine_goals.extend( + self.fulfill_cx + .drain_stalled_obligations_for_coroutines(self.at.infcx) + .into_iter() + .map(|obl| obl.as_goal()), + ); + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); if !errors.is_empty() { return Err(errors); @@ -166,7 +176,6 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { Ok(t) } - #[tracing::instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); @@ -193,7 +202,6 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { } } - #[tracing::instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); @@ -232,8 +240,8 @@ pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable { - at: At<'a, 'db>, +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> { + at: At<'a, 'tcx>, } impl<'db> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs new file mode 100644 index 0000000000000..8e2dc0dec4ed3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs @@ -0,0 +1,203 @@ +use hir_def::TraitId; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::{TypeFoldable, Upcast, Variance}; + +use crate::next_solver::fulfill::{FulfillmentCtxt, NextSolverError}; +use crate::next_solver::infer::at::ToTrace; +use crate::next_solver::infer::traits::{ + Obligation, ObligationCause, PredicateObligation, PredicateObligations, +}; +use crate::next_solver::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace}; +use crate::next_solver::{Const, DbInterner, ParamEnv, Term, TraitRef, Ty, TypeError}; + +/// Used if you want to have pleasant experience when dealing +/// with obligations outside of hir or mir typeck. +pub struct ObligationCtxt<'a, 'db> { + pub infcx: &'a InferCtxt<'db>, + engine: FulfillmentCtxt<'db>, +} + +impl<'a, 'db> ObligationCtxt<'a, 'db> { + pub fn new(infcx: &'a InferCtxt<'db>) -> Self { + Self { infcx, engine: FulfillmentCtxt::new(infcx) } + } +} + +impl<'a, 'db> ObligationCtxt<'a, 'db> { + pub fn register_obligation(&mut self, obligation: PredicateObligation<'db>) { + self.engine.register_predicate_obligation(self.infcx, obligation); + } + + pub fn register_obligations( + &mut self, + obligations: impl IntoIterator>, + ) { + self.engine.register_predicate_obligations(self.infcx, obligations); + } + + pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'db, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.register_obligations(obligations); + value + } + + /// Requires that `ty` must implement the trait with `def_id` in + /// the given environment. This trait must not have any type + /// parameters (except for `Self`). + pub fn register_bound( + &mut self, + cause: ObligationCause, + param_env: ParamEnv<'db>, + ty: Ty<'db>, + def_id: TraitId, + ) { + let trait_ref = TraitRef::new(self.infcx.interner, def_id.into(), [ty]); + self.register_obligation(Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: trait_ref.upcast(self.infcx.interner), + }); + } + + pub fn eq>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .eq(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn eq_trace>>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + trace: TypeTrace<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. + pub fn sub>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .sub(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn relate>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + variance: Variance, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .relate(DefineOpaqueTypes::Yes, expected, variance, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`. + pub fn sup>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .sup(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Computes the least-upper-bound, or mutual supertype, of two values. + pub fn lub>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result> { + self.infcx + .at(cause, param_env) + .lub(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + #[must_use] + pub fn select_where_possible(&mut self) -> Vec> { + self.engine.select_where_possible(self.infcx) + } + + #[must_use] + pub fn select_all_or_error(&mut self) -> Vec> { + self.engine.select_all_or_error(self.infcx) + } + + /// Returns the not-yet-processed and stalled obligations from the + /// `ObligationCtxt`. + /// + /// Takes ownership of the context as doing operations such as + /// [`ObligationCtxt::eq`] afterwards will result in other obligations + /// getting ignored. You can make a new `ObligationCtxt` if this + /// needs to be done in a loop, for example. + #[must_use] + pub fn into_pending_obligations(self) -> PredicateObligations<'db> { + self.engine.pending_obligations() + } + + pub fn deeply_normalize>>( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: T, + ) -> Result>> { + self.infcx.at(cause, param_env).deeply_normalize(value) + } + + pub fn structurally_normalize_ty( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Ty<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_ty(value, &mut self.engine) + } + + pub fn structurally_normalize_const( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Const<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_const(value, &mut self.engine) + } + + pub fn structurally_normalize_term( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Term<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_term(value, &mut self.engine) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index c86d3a4aadb22..99b1354b6335f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -262,28 +262,6 @@ impl<'db> Predicate<'db> { Some(Predicate::new(DbInterner::conjure(), kind)) } - - pub fn as_trait_clause(self) -> Option> { - let predicate = self.kind(); - match predicate.skip_binder() { - PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), - PredicateKind::Clause(ClauseKind::Projection(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::NormalizesTo(..) - | PredicateKind::AliasRelate(..) - | PredicateKind::Subtype(..) - | PredicateKind::Coerce(..) - | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) - | PredicateKind::Clause(ClauseKind::WellFormed(..)) - | PredicateKind::DynCompatible(..) - | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) - | PredicateKind::ConstEquate(..) - | PredicateKind::Ambiguous => None, - } - } } // FIXME: should make a "header" in interned_vec @@ -693,6 +671,12 @@ impl<'db> UpcastFrom, ty::OutlivesPredicate, Reg } } +impl<'db> UpcastFrom, PolyRegionOutlivesPredicate<'db>> for Predicate<'db> { + fn upcast_from(from: PolyRegionOutlivesPredicate<'db>, tcx: DbInterner<'db>) -> Self { + from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx) + } +} + impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> { fn as_clause(self) -> Option< as rustc_type_ir::Interner>::Clause> { match self.kind().skip_binder() { @@ -730,6 +714,30 @@ impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> } impl<'db> Predicate<'db> { + pub fn as_trait_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), + _ => None, + } + } + + pub fn as_projection_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)), + _ => None, + } + } + + /// Matches a `PredicateKind::Clause` and turns it into a `Clause`, otherwise returns `None`. + pub fn as_clause(self) -> Option> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Some(self.expect_clause()), + _ => None, + } + } + /// Assert that the predicate is a clause. pub fn expect_clause(self) -> Clause<'db> { match self.kind().skip_binder() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs deleted file mode 100644 index e57808728ae96..0000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Projection code for next-solver. - -pub(crate) mod solve_normalize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index c7591c0c77966..946e57e6cb741 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -2,6 +2,7 @@ use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::GenericArgKind; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, @@ -65,12 +66,12 @@ impl<'db> SolverDelegate for SolverContext<'db> { (SolverContext(infcx), value, vars) } - fn fresh_var_for_kind_with_span( - &self, - arg: ::GenericArg, - span: ::Span, - ) -> ::GenericArg { - unimplemented!() + fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> { + match arg.kind() { + GenericArgKind::Lifetime(_) => self.next_region_var().into(), + GenericArgKind::Type(_) => self.next_ty_var().into(), + GenericArgKind::Const(_) => self.next_const_var().into(), + } } fn leak_check( @@ -92,7 +93,8 @@ impl<'db> SolverDelegate for SolverContext<'db> { >, >, > { - unimplemented!() + // FIXME(next-solver): + None } fn make_deduplicated_outlives_constraints( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs new file mode 100644 index 0000000000000..18859d8b79707 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -0,0 +1,57 @@ +use rustc_type_ir::{AliasRelationDirection, inherent::Term as _}; + +use crate::next_solver::{ + Const, PredicateKind, Term, Ty, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{at::At, traits::Obligation}, +}; + +impl<'db> At<'_, 'db> { + pub(crate) fn structurally_normalize_ty( + &self, + ty: Ty<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + self.structurally_normalize_term(ty.into(), fulfill_cx).map(|term| term.expect_type()) + } + + pub(crate) fn structurally_normalize_const( + &self, + ct: Const<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + self.structurally_normalize_term(ct.into(), fulfill_cx).map(|term| term.expect_const()) + } + + pub(crate) fn structurally_normalize_term( + &self, + term: Term<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + assert!(!term.is_infer(), "should have resolved vars before calling"); + + if term.to_alias_term().is_none() { + return Ok(term); + } + + let new_infer = self.infcx.next_term_var_of_kind(term); + + // We simply emit an `alias-eq` goal here, since that will take care of + // normalizing the LHS of the projection until it is a rigid projection + // (or a not-yet-defined opaque in scope). + let obligation = Obligation::new( + self.infcx.interner, + self.cause.clone(), + self.param_env, + PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate), + ); + + fulfill_cx.register_predicate_obligation(self.infcx, obligation); + let errors = fulfill_cx.select_where_possible(self.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(self.infcx.resolve_vars_if_possible(new_infer)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index e1c29160ad1ae..e6c444d0d6bed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1,17 +1,19 @@ //! Things related to tys in the next-trait-solver. +use std::iter; +use std::ops::ControlFlow; + use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; -use rustc_type_ir::Interner; use rustc_type_ir::{ - BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, - TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, - WithCachedTypeInfo, + BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy, + IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo, inherent::{ - AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, - Ty as _, + Abi, AdtDef, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, + PlaceholderLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, solve::SizedTraitKind, @@ -20,13 +22,16 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; -use crate::next_solver::{ - CallableIdWrapper, ClosureIdWrapper, CoroutineIdWrapper, GenericArg, TypeAliasIdWrapper, -}; use crate::{ + FnAbi, db::HirDatabase, interner::InternedWrapperNoDebug, - next_solver::util::{CoroutineArgsExt, IntegerTypeExt}, + next_solver::{ + CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, GenericArg, + PolyFnSig, TypeAliasIdWrapper, + abi::Safety, + util::{CoroutineArgsExt, IntegerTypeExt}, + }, }; use super::{ @@ -310,6 +315,68 @@ impl<'db> Ty<'db> { | TyKind::Error(_) => false, } } + + #[inline] + pub fn is_never(self) -> bool { + matches!(self.kind(), TyKind::Never) + } + + #[inline] + pub fn is_infer(self) -> bool { + matches!(self.kind(), TyKind::Infer(..)) + } + + #[inline] + pub fn is_str(self) -> bool { + matches!(self.kind(), TyKind::Str) + } + + #[inline] + pub fn is_unit(self) -> bool { + matches!(self.kind(), TyKind::Tuple(tys) if tys.inner().is_empty()) + } + + /// Given a `fn` type, returns an equivalent `unsafe fn` type; + /// that is, a `fn` type that is equivalent in every way for being + /// unsafe. + pub fn safe_to_unsafe_fn_ty(interner: DbInterner<'db>, sig: PolyFnSig<'db>) -> Ty<'db> { + assert!(sig.safety().is_safe()); + Ty::new_fn_ptr(interner, sig.map_bound(|sig| FnSig { safety: Safety::Unsafe, ..sig })) + } + + /// Returns the type of `*ty`. + /// + /// The parameter `explicit` indicates if this is an *explicit* dereference. + /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. + pub fn builtin_deref(self, db: &dyn HirDatabase, explicit: bool) -> Option> { + match self.kind() { + TyKind::Adt(adt, substs) if crate::lang_items::is_box(db, adt.def_id().0) => { + Some(substs.as_slice()[0].expect_ty()) + } + TyKind::Ref(_, ty, _) => Some(ty), + TyKind::RawPtr(ty, _) if explicit => Some(ty), + _ => None, + } + } + + /// Whether the type contains some non-lifetime, aka. type or const, error type. + pub fn references_non_lt_error(self) -> bool { + self.references_error() && self.visit_with(&mut ReferencesNonLifetimeError).is_break() + } +} + +struct ReferencesNonLifetimeError; + +impl<'db> TypeVisitor> for ReferencesNonLifetimeError { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) } + } } impl<'db> std::fmt::Debug for Ty<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 97d3ea72c93cb..a50f20a565ee2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -409,8 +409,7 @@ pub(crate) fn for_trait_impls( // Note: Since we're using `impls_for_trait` and `impl_provided_for`, // only impls where the trait can be resolved should ever reach Chalk. // `impl_datum` relies on that and will panic if the trait can't be resolved. - let in_deps = db.trait_impls_in_deps(krate); - let in_self = db.trait_impls_in_crate(krate); + let in_self_and_deps = db.trait_impls_in_deps(krate); let trait_module = trait_id.module(db); let type_module = match self_ty_fp { Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), @@ -435,8 +434,7 @@ pub(crate) fn for_trait_impls( }); }) .filter_map(|block_id| db.trait_impls_in_block(block_id)); - f(&in_self)?; - for it in in_deps.iter().map(ops::Deref::deref) { + for it in in_self_and_deps.iter().map(ops::Deref::deref) { f(it)?; } for it in block_impls { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 9d1238701bcfa..0a8ed2cf0cabd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -7,7 +7,7 @@ use hir_def::tt; use intern::{Symbol, sym}; use rustc_hash::{FxHashMap, FxHashSet}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct TargetFeatures { pub(crate) enabled: FxHashSet, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 7ec5231a7341a..1735f550b8ad7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -177,21 +177,23 @@ fn test(i: i32) { #[test] fn coerce_merge_one_by_one1() { - cov_mark::check!(coerce_merge_fail_fallback); - check( r" fn test() { let t = &mut 1; let x = match 1 { 1 => t as *mut i32, - //^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) + //^ adjustments: Deref(None), Borrow(RawPtr(Mut)) + _ => t as *const i32, + }; + x; + // ^ type: *const i32 + let x = match 1 { + 1 => t as *mut i32, 2 => t as &i32, //^^^^^^^^^ expected *mut i32, got &'? i32 _ => t as *const i32, }; - x; - //^ type: *const i32 } ", @@ -276,17 +278,19 @@ fn test() { fn coerce_autoderef_implication_1() { check_no_mismatches( r" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn takes_ref_foo(x: &Foo) {} fn test() { - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo<{unknown}> takes_ref_foo(&foo); - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo let _: &() = &foo; }", @@ -297,16 +301,18 @@ fn test() { fn coerce_autoderef_implication_2() { check( r" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn takes_ref_foo(x: &Foo) {} fn test() { - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo<{unknown}> - let _: &u32 = &Foo; - //^^^^ expected &'? u32, got &'? Foo<{unknown}> + let _: &u32 = &Foo(PhantomData); + //^^^^^^^^^^^^^^^^^ expected &'? u32, got &'? Foo<{unknown}> }", ); } @@ -409,8 +415,6 @@ fn test() { #[test] fn coerce_fn_items_in_match_arms() { - cov_mark::check!(coerce_fn_reification); - check_no_mismatches( r" fn foo1(x: u32) -> isize { 1 } @@ -683,9 +687,9 @@ fn coerce_unsize_expected_type_2() { check_no_mismatches( r#" //- minicore: coerce_unsized -struct InFile; +struct InFile(T); impl InFile { - fn with_value(self, value: U) -> InFile { InFile } + fn with_value(self, value: U) -> InFile { InFile(loop {}) } } struct RecordField; trait AstNode {} @@ -694,7 +698,7 @@ impl AstNode for RecordField {} fn takes_dyn(it: InFile<&dyn AstNode>) {} fn test() { - let x: InFile<()> = InFile; + let x: InFile<()> = InFile(()); let n = &RecordField; takes_dyn(x.with_value(n)); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index 855034117c0d7..f257aa1b6e602 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -89,7 +89,6 @@ fn test(x: bool) { //^^^^ expected (), got &'static str } match x { true => true, false => 0 } - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool //^ expected bool, got i32 () } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index d79639b93727b..c0b930e5e1231 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -48,7 +48,6 @@ fn foo() -> i32 { "expr_scopes_shim", "lang_item", "crate_lang_items", - "lang_item", ] "#]], ); @@ -138,7 +137,6 @@ fn baz() -> i32 { "crate_lang_items", "attrs_shim", "attrs_shim", - "lang_item", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -588,8 +586,8 @@ fn main() { "crate_lang_items", "attrs_shim", "attrs_shim", - "return_type_impl_traits_shim", "generic_predicates_ns_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -602,6 +600,7 @@ fn main() { "VariantFields::firewall_", "VariantFields::query_", "lang_item", + "lang_item", "inherent_impls_in_crate_shim", "impl_signature_shim", "impl_signature_with_source_map_shim", @@ -616,7 +615,6 @@ fn main() { "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", - "lang_item", ] "#]], ); @@ -688,8 +686,8 @@ fn main() { "attrs_shim", "attrs_shim", "attrs_shim", - "return_type_impl_traits_shim", "generic_predicates_ns_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5d088e40cdeda..25b938c7078aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -194,15 +194,15 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': () + 39..442 '{ ...!(); }': {unknown} 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': {unknown} - 100..119 'for _ ...!() {}': &'? mut {unknown} + 100..119 'for _ ...!() {}': ::IntoIter + 100..119 'for _ ...!() {}': &'? mut ::IntoIter 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<{unknown}> + 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -288,15 +288,15 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': () + 53..456 '{ ...!(); }': {unknown} 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': {unknown} - 114..133 'for _ ...!() {}': &'? mut {unknown} + 114..133 'for _ ...!() {}': ::IntoIter + 114..133 'for _ ...!() {}': &'? mut ::IntoIter 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<{unknown}> + 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () @@ -707,7 +707,7 @@ fn infer_builtin_macros_file() { expect![[r#" !0..6 '"file"': &'static str 63..87 '{ ...!(); }': () - 73..74 'x': &'static str + 73..74 'x': &'? str "#]], ); } @@ -745,7 +745,7 @@ fn infer_builtin_macros_concat() { expect![[r#" !0..13 '"helloworld!"': &'static str 65..121 '{ ...")); }': () - 75..76 'x': &'static str + 75..76 'x': &'? str "#]], ); } @@ -822,7 +822,7 @@ macro_rules! include_str {() => {}} fn main() { let a = include_str!("foo.rs"); a; -} //^ &'static str +} //^ &'? str //- /foo.rs hello @@ -849,7 +849,7 @@ macro_rules! m { fn main() { let a = include_str!(m!(".rs")); a; -} //^ &'static str +} //^ &'? str //- /foo.rs hello @@ -964,7 +964,7 @@ fn infer_builtin_macros_concat_with_lazy() { expect![[r#" !0..13 '"helloworld!"': &'static str 103..160 '{ ...")); }': () - 113..114 'x': &'static str + 113..114 'x': &'? str "#]], ); } @@ -979,7 +979,7 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); - //^ &'static str + //^ &'? str } "#, ); @@ -993,7 +993,7 @@ fn infer_builtin_macros_option_env() { //- /main.rs env:foo=bar fn main() { let x = option_env!("foo"); - //^ Option<&'static str> + //^ Option<&'? str> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index d50daf0f0d5be..b14ce35aa99c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -578,17 +578,17 @@ fn infer_trait_assoc_method_generics_3() { trait Trait { fn make() -> (Self, T); } - struct S; + struct S(T); impl Trait for S {} fn test() { let a = S::make(); } "#, expect![[r#" - 100..126 '{ ...e(); }': () - 110..111 'a': (S, i64) - 114..121 'S::make': fn make, i64>() -> (S, i64) - 114..123 'S::make()': (S, i64) + 103..129 '{ ...e(); }': () + 113..114 'a': (S, i64) + 117..124 'S::make': fn make, i64>() -> (S, i64) + 117..126 'S::make()': (S, i64) "#]], ); } @@ -600,7 +600,7 @@ fn infer_trait_assoc_method_generics_4() { trait Trait { fn make() -> (Self, T); } - struct S; + struct S(T); impl Trait for S {} impl Trait for S {} fn test() { @@ -609,13 +609,13 @@ fn infer_trait_assoc_method_generics_4() { } "#, expect![[r#" - 130..202 '{ ...e(); }': () - 140..141 'a': (S, i64) - 157..164 'S::make': fn make, i64>() -> (S, i64) - 157..166 'S::make()': (S, i64) - 176..177 'b': (S, i32) - 190..197 'S::make': fn make, i32>() -> (S, i32) - 190..199 'S::make()': (S, i32) + 133..205 '{ ...e(); }': () + 143..144 'a': (S, i64) + 160..167 'S::make': fn make, i64>() -> (S, i64) + 160..169 'S::make()': (S, i64) + 179..180 'b': (S, i32) + 193..200 'S::make': fn make, i32>() -> (S, i32) + 193..202 'S::make()': (S, i32) "#]], ); } @@ -627,7 +627,7 @@ fn infer_trait_assoc_method_generics_5() { trait Trait { fn make() -> (Self, T, U); } - struct S; + struct S(T); impl Trait for S {} fn test() { let a = >::make::(); @@ -635,13 +635,13 @@ fn infer_trait_assoc_method_generics_5() { } "#, expect![[r#" - 106..210 '{ ...>(); }': () - 116..117 'a': (S, i64, u8) - 120..149 '': fn make, i64, u8>() -> (S, i64, u8) - 120..151 '()': (S, i64, u8) - 161..162 'b': (S, i64, u8) - 181..205 'Trait:...::': fn make, i64, u8>() -> (S, i64, u8) - 181..207 'Trait:...()': (S, i64, u8) + 109..213 '{ ...>(); }': () + 119..120 'a': (S, i64, u8) + 123..152 '': fn make, i64, u8>() -> (S, i64, u8) + 123..154 '()': (S, i64, u8) + 164..165 'b': (S, i64, u8) + 184..208 'Trait:...::': fn make, i64, u8>() -> (S, i64, u8) + 184..210 'Trait:...()': (S, i64, u8) "#]], ); } @@ -1107,6 +1107,9 @@ fn method_resolution_slow() { // this can get quite slow if we set the solver size limit too high check_types( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait SendX {} struct S1; impl SendX for S1 {} @@ -1115,17 +1118,17 @@ struct U1; trait Trait { fn method(self); } -struct X1 {} +struct X1(PhantomData<(A, B)>); impl SendX for X1 where A: SendX, B: SendX {} -struct S {} +struct S(PhantomData<(B, C)>); trait FnX {} impl Trait for S where C: FnX, B: SendX {} -fn test() { (S {}).method(); } - //^^^^^^^^^^^^^^^ () +fn test() { (S(PhantomData)).method(); } + //^^^^^^^^^^^^^^^^^^^^^^^^^ () "#, ); } @@ -1187,11 +1190,11 @@ fn test() { 89..109 '{ ... }': bool 99..103 'true': bool 123..167 '{ ...o(); }': () - 133..134 's': &'static S - 137..151 'unsafe { f() }': &'static S + 133..134 's': &'? S + 137..151 'unsafe { f() }': &'? S 146..147 'f': fn f() -> &'static S 146..149 'f()': &'static S - 157..158 's': &'static S + 157..158 's': &'? S 157..164 's.foo()': bool "#]], ); @@ -2191,9 +2194,9 @@ impl Receiver for Bar { fn main() { let bar = Bar; let _v1 = bar.foo1(); - //^^^ type: {unknown} + //^^^ type: i32 let _v2 = bar.foo2(); - //^^^ type: {unknown} + //^^^ type: bool } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 6a9135622deb6..af5290d720356 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -14,6 +14,8 @@ fn test() { ); } +// FIXME(next-solver): The never type fallback implemented in r-a no longer works properly because of +// `Coerce` predicates. We should reimplement fallback like rustc. #[test] fn infer_never2() { check_types( @@ -24,7 +26,7 @@ fn test() { let a = gen(); if false { a } else { loop {} }; a; -} //^ ! +} //^ {unknown} "#, ); } @@ -39,7 +41,7 @@ fn test() { let a = gen(); if false { loop {} } else { a }; a; - //^ ! + //^ {unknown} } "#, ); @@ -54,7 +56,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; -} //^ Option +} //^ Option<{unknown}> "#, ); } @@ -104,7 +106,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; - //^ Option<&'static str> + //^ Option<&'? str> match 42 { 42 => a, _ => Option::Some("str"), @@ -218,7 +220,7 @@ fn test(a: i32) { _ => loop {}, }; i; -} //^ ! +} //^ {unknown} "#, ); } @@ -362,12 +364,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +381,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +397,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 256ca7defb78a..40e4c28fcc0b9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -158,6 +158,7 @@ static ALIAS: i32 = { 191..193 '_a': impl Trait + ?Sized 205..211 'Struct': Struct 217..218 '5': i32 + 205..211: expected impl Trait + ?Sized, got Struct "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 02cb03706919b..607daada42eb1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -41,19 +41,19 @@ fn infer_pattern() { 47..48 'x': &'? i32 58..59 'a': i32 62..63 'z': i32 - 73..79 '(c, d)': (i32, &'static str) + 73..79 '(c, d)': (i32, &'? str) 74..75 'c': i32 - 77..78 'd': &'static str - 82..94 '(1, "hello")': (i32, &'static str) + 77..78 'd': &'? str + 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': ! 101..151 'for (e... }': {unknown} 101..151 'for (e... }': &'? mut {unknown} 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -653,7 +653,7 @@ fn infer_generics_in_patterns() { fn infer_const_pattern() { check( r#" -enum Option { None } +enum Option { None, Some(T) } use Option::None; struct Foo; const Bar: usize = 1; @@ -719,28 +719,28 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&'? (i32, &'static str), i32, impl FnOnce(&'? (i32, &'static str)) -> i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 - 82..91 '&(1, "a")': &'? (i32, &'static str) - 83..91 '(1, "a")': (i32, &'static str) + 82..91 '&(1, "a")': &'? (i32, &'? str) + 83..91 '(1, "a")': (i32, &'? str) 84..85 '1': i32 87..90 '"a"': &'static str - 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> i32 - 94..101 '&(x, y)': &'? (i32, &'static str) - 95..101 '(x, y)': (i32, &'static str) + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'? str) + 95..101 '(x, y)': (i32, &'? str) 96..97 'x': i32 - 99..100 'y': &'static str + 99..100 'y': &'? str 103..104 'x': i32 - 142..145 'foo': fn foo<&'? (i32, &'static str), &'? i32, impl FnOnce(&'? (i32, &'static str)) -> &'? i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> &'? i32) -> &'? i32 + 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 142..168 'foo(&(...y)| x)': &'? i32 - 146..155 '&(1, "a")': &'? (i32, &'static str) - 147..155 '(1, "a")': (i32, &'static str) + 146..155 '&(1, "a")': &'? (i32, &'? str) + 147..155 '(1, "a")': (i32, &'? str) 148..149 '1': i32 151..154 '"a"': &'static str - 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> &'? i32 - 158..164 '(x, y)': (i32, &'static str) + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'? str) 159..160 'x': &'? i32 - 162..163 'y': &'? &'static str + 162..163 'y': &'? &'? str 166..167 'x': &'? i32 "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 6a3f2286215f4..2ba1e2341b297 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -88,6 +88,7 @@ fn bug_651() { #[test] fn recursive_vars() { + // FIXME: This isn't nice, but I guess as long as we don't hang/crash that's fine? check_infer( r#" fn test() { @@ -97,12 +98,12 @@ fn recursive_vars() { "#, expect![[r#" 10..47 '{ ...&y]; }': () - 20..21 'y': {unknown} - 24..31 'unknown': {unknown} - 37..44 '[y, &y]': [{unknown}; 2] - 38..39 'y': {unknown} - 41..43 '&y': &'? {unknown} - 42..43 'y': {unknown} + 20..21 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 24..31 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 37..44 '[y, &y]': [&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}; 2] + 38..39 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 41..43 '&y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 42..43 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -119,19 +120,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': &'? {unknown} - 24..31 'unknown': &'? {unknown} - 41..42 'y': {unknown} - 45..52 'unknown': {unknown} - 58..76 '[(x, y..., &x)]': [(&'? {unknown}, {unknown}); 2] - 59..65 '(x, y)': (&'? {unknown}, {unknown}) - 60..61 'x': &'? {unknown} - 63..64 'y': {unknown} - 67..75 '(&y, &x)': (&'? {unknown}, {unknown}) - 68..70 '&y': &'? {unknown} - 69..70 'y': {unknown} - 72..74 '&x': &'? &'? {unknown} - 73..74 'x': &'? {unknown} + 20..21 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 24..31 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 41..42 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 45..52 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 58..76 '[(x, y..., &x)]': [(&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}); 2] + 59..65 '(x, y)': (&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}) + 60..61 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 63..64 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 67..75 '(&y, &x)': (&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}) + 68..70 '&y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 69..70 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 72..74 '&x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 73..74 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -268,37 +269,37 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () - 75..79 'name': &'? {unknown} - 82..166 'if doe... }': &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool - 99..128 '{ ... }': &'? {unknown} - 113..118 'first': &'? {unknown} - 134..166 '{ ... }': &'? {unknown} - 148..156 '&content': &'? {unknown} - 149..156 'content': {unknown} - 181..188 'content': &'? {unknown} - 191..313 'if ICE... }': &'? {unknown} - 194..231 'ICE_RE..._VALUE': {unknown} + 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 194..231 'ICE_RE..._VALUE': bool 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? {unknown} - 242..246 'name': &'? {unknown} - 248..276 '{ ... }': &'? {unknown} - 262..266 'name': &'? {unknown} - 282..313 '{ ... }': {unknown} - 296..303 'content': {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -394,7 +395,7 @@ fn issue_2669() { r#" trait A {} trait Write {} - struct Response {} + struct Response(T); trait D { fn foo(); @@ -410,13 +411,13 @@ fn issue_2669() { } "#, expect![[r#" - 119..214 '{ ... }': () - 129..132 'end': fn end<{unknown}>() - 129..134 'end()': () - 163..208 '{ ... }': () - 181..183 '_x': ! - 190..197 'loop {}': ! - 195..197 '{}': () + 120..215 '{ ... }': () + 130..133 'end': fn end<{unknown}>() + 130..135 'end()': () + 164..209 '{ ... }': () + 182..184 '_x': ! + 191..198 'loop {}': ! + 196..198 '{}': () "#]], ) } @@ -628,7 +629,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': () + 488..522 '{ ... }': as BoxedDsl>::Output 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -799,7 +800,7 @@ fn issue_4966() { struct Map { f: F } - struct Vec {} + struct Vec { p: *mut T } impl core::ops::Deref for Vec { type Target = [T]; @@ -818,23 +819,23 @@ fn issue_4966() { } "#, expect![[r#" - 225..229 'iter': T - 244..246 '{}': Vec - 258..402 '{ ...r(); }': () - 268..273 'inner': Map f64> - 276..300 'Map { ... 0.0 }': Map f64> - 285..298 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 - 286..287 '_': &'? f64 - 295..298 '0.0': f64 - 311..317 'repeat': Repeat f64>> - 320..345 'Repeat...nner }': Repeat f64>> - 338..343 'inner': Map f64> - 356..359 'vec': Vec<{unknown}> - 362..371 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> - 362..379 'from_i...epeat)': Vec<{unknown}> - 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec<{unknown}> - 386..399 'vec.foo_bar()': {unknown} + 236..240 'iter': T + 255..257 '{}': Vec + 269..413 '{ ...r(); }': () + 279..284 'inner': Map f64> + 287..311 'Map { ... 0.0 }': Map f64> + 296..309 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 + 297..298 '_': &'? f64 + 306..309 '0.0': f64 + 322..328 'repeat': Repeat f64>> + 331..356 'Repeat...nner }': Repeat f64>> + 349..354 'inner': Map f64> + 367..370 'vec': Vec<{unknown}> + 373..382 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> + 373..390 'from_i...epeat)': Vec<{unknown}> + 383..389 'repeat': Repeat f64>> + 397..400 'vec': Vec<{unknown}> + 397..410 'vec.foo_bar()': {unknown} "#]], ); } @@ -843,37 +844,40 @@ fn issue_4966() { fn issue_6628() { check_infer( r#" -//- minicore: fn -struct S(); +//- minicore: fn, phantom_data +use core::marker::PhantomData; + +struct S(PhantomData); impl S { fn f(&self, _t: T) {} fn g(&self, _f: F) {} } fn main() { - let s = S(); + let s = S(PhantomData); s.g(|_x| {}); s.f(10); } "#, expect![[r#" - 40..44 'self': &'? S - 46..48 '_t': T - 53..55 '{}': () - 81..85 'self': &'? S - 87..89 '_f': F - 94..96 '{}': () - 109..160 '{ ...10); }': () - 119..120 's': S - 123..124 'S': fn S() -> S - 123..126 'S()': S - 132..133 's': S - 132..144 's.g(|_x| {})': () - 136..143 '|_x| {}': impl FnOnce(&'? i32) - 137..139 '_x': &'? i32 - 141..143 '{}': () - 150..151 's': S - 150..157 's.f(10)': () - 154..156 '10': i32 + 86..90 'self': &'? S + 92..94 '_t': T + 99..101 '{}': () + 127..131 'self': &'? S + 133..135 '_f': F + 140..142 '{}': () + 155..217 '{ ...10); }': () + 165..166 's': S + 169..170 'S': fn S(PhantomData) -> S + 169..183 'S(PhantomData)': S + 171..182 'PhantomData': PhantomData + 189..190 's': S + 189..201 's.g(|_x| {})': () + 193..200 '|_x| {}': impl FnOnce(&'? i32) + 194..196 '_x': &'? i32 + 198..200 '{}': () + 207..208 's': S + 207..214 's.f(10)': () + 211..213 '10': i32 "#]], ); } @@ -931,7 +935,7 @@ fn lifetime_from_chalk_during_deref() { check_types( r#" //- minicore: deref -struct Box {} +struct Box(T); impl core::ops::Deref for Box { type Target = T; @@ -964,6 +968,9 @@ fn clone_iter(s: Iter) { fn issue_8686() { check_infer( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + pub trait Try: FromResidual { type Output; type Residual; @@ -972,28 +979,32 @@ pub trait FromResidual::Residual> { fn from_residual(residual: R) -> Self; } -struct ControlFlow; +struct ControlFlow(PhantomData<(B, C)>); impl Try for ControlFlow { type Output = C; type Residual = ControlFlow; } impl FromResidual for ControlFlow { - fn from_residual(r: ControlFlow) -> Self { ControlFlow } + fn from_residual(r: ControlFlow) -> Self { ControlFlow(PhantomData) } } fn test() { - ControlFlow::from_residual(ControlFlow::); + ControlFlow::from_residual(ControlFlow::(PhantomData)); } "#, expect![[r#" - 144..152 'residual': R - 365..366 'r': ControlFlow - 395..410 '{ ControlFlow }': ControlFlow - 397..408 'ControlFlow': ControlFlow - 424..482 '{ ...!>); }': () - 430..456 'Contro...sidual': fn from_residual, ControlFlow>(ControlFlow) -> ControlFlow - 430..479 'Contro...2, !>)': ControlFlow - 457..478 'Contro...32, !>': ControlFlow + 176..184 'residual': R + 418..419 'r': ControlFlow + 448..476 '{ Cont...ata) }': ControlFlow + 450..461 'ControlFlow': fn ControlFlow(PhantomData<(B, C)>) -> ControlFlow + 450..474 'Contro...mData)': ControlFlow + 462..473 'PhantomData': PhantomData<(B, C)> + 490..561 '{ ...a)); }': () + 496..522 'Contro...sidual': fn from_residual, ControlFlow>(ControlFlow) -> ControlFlow + 496..558 'Contro...Data))': ControlFlow + 523..544 'Contro...32, !>': fn ControlFlow(PhantomData<(u32, !)>) -> ControlFlow + 523..557 'Contro...mData)': ControlFlow + 545..556 'PhantomData': PhantomData<(u32, !)> "#]], ); } @@ -1052,12 +1063,13 @@ fn impl_trait_in_option_9530() { check_types( r#" //- minicore: sized -struct Option; +struct Option(T); impl Option { fn unwrap(self) -> T { loop {} } } -fn make() -> Option { Option } +fn make() -> Option { Option(()) } trait Copy {} +impl Copy for () {} fn test() { let o = make(); o.unwrap(); @@ -1163,9 +1175,9 @@ pub trait BitView { pub struct Lsb0; -pub struct BitArray { } +pub struct BitArray(V); -pub struct BitSlice { } +pub struct BitSlice(T); impl core::ops::Deref for BitArray { type Target = BitSlice; @@ -1243,12 +1255,12 @@ fn test() { expect![[r#" 10..68 '{ ... } }': () 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! - 16..66 'for _ ... }': {unknown} - 16..66 'for _ ... }': &'? mut {unknown} + 16..66 'for _ ... }': <() as IntoIterator>::IntoIter + 16..66 'for _ ... }': &'? mut <() as IntoIterator>::IntoIter 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<{unknown}> + 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () @@ -1779,7 +1791,7 @@ fn regression_14844() { r#" pub type Ty = Unknown; -pub struct Inner(); +pub struct Inner(T); pub struct Outer { pub inner: Inner, @@ -1787,7 +1799,7 @@ pub struct Outer { fn main() { _ = Outer { - inner: Inner::(), + inner: Inner::(0), }; } "#, @@ -1939,7 +1951,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ ! + //^^^^^^^^^^^^^ {unknown} let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum @@ -2017,12 +2029,12 @@ fn tait_async_stack_overflow_17199() { fn lifetime_params_move_param_defaults() { check_types( r#" -pub struct Thing<'s, T = u32>; +pub struct Thing<'s, T = u32>(&'s T); impl <'s> Thing<'s> { pub fn new() -> Thing<'s> { - Thing - //^^^^^ Thing<'?, u32> + Thing(&0) + //^^^^^^^^^ Thing<'?, u32> } } @@ -2042,11 +2054,11 @@ fn issue_17734() { r#" fn test() { let x = S::foo::<'static, &()>(&S); - // ^ Wrap<'static, ()> + // ^ Wrap<'?, ()> let x = S::foo::<&()>(&S); // ^ Wrap<'?, ()> let x = S.foo::<'static, &()>(); - // ^ Wrap<'static, ()> + // ^ Wrap<'?, ()> let x = S.foo::<&()>(); // ^ Wrap<'?, ()> } @@ -2059,7 +2071,7 @@ impl S { } } -struct Wrap<'a, T>(T); +struct Wrap<'a, T>(&'a T); trait Trait<'a> { type Proj; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 82d670cef2b0e..ead79a8f5b90b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -307,3 +307,114 @@ where "#]], ) } + +#[test] +fn fn_coercion() { + check_no_mismatches( + r#" +fn foo() { + let _is_suffix_start: fn(&(usize, char)) -> bool = match true { + true => |(_, c)| *c == ' ', + _ => |(_, c)| *c == 'v', + }; +} + "#, + ); +} + +#[test] +fn coercion_with_errors() { + check_no_mismatches( + r#" +//- minicore: unsize, coerce_unsized +fn foo(_v: i32) -> [u8; _] { loop {} } +fn bar(_v: &[u8]) {} + +fn main() { + bar(&foo()); +} + "#, + ); +} + +#[test] +fn another_20654_case() { + check_no_mismatches( + r#" +//- minicore: sized, unsize, coerce_unsized, dispatch_from_dyn, fn +struct Region<'db>(&'db ()); + +trait TypeFoldable {} + +trait Interner { + type Region; + type GenericArg; +} + +struct DbInterner<'db>(&'db ()); +impl<'db> Interner for DbInterner<'db> { + type Region = Region<'db>; + type GenericArg = GenericArg<'db>; +} + +trait GenericArgExt> { + fn expect_region(&self) -> I::Region { + loop {} + } +} +impl<'db> GenericArgExt> for GenericArg<'db> {} + +enum GenericArg<'db> { + Region(Region<'db>), +} + +fn foo<'db, T: TypeFoldable>>(arg: GenericArg<'db>) { + let regions = &mut || arg.expect_region(); + let f: &'_ mut (dyn FnMut() -> Region<'db> + '_) = regions; +} + "#, + ); +} + +#[test] +fn trait_solving_with_error() { + check_infer( + r#" +//- minicore: size_of +struct Vec(T); + +trait Foo { + type Item; + fn to_vec(self) -> Vec { + loop {} + } +} + +impl<'a, T, const N: usize> Foo for &'a [T; N] { + type Item = T; +} + +fn to_bytes() -> [u8; _] { + loop {} +} + +fn foo() { + let _x = to_bytes().to_vec(); +} + "#, + expect![[r#" + 60..64 'self': Self + 85..108 '{ ... }': Vec<::Item> + 95..102 'loop {}': ! + 100..102 '{}': () + 208..223 '{ loop {} }': [u8; _] + 214..221 'loop {}': ! + 219..221 '{}': () + 234..271 '{ ...c(); }': () + 244..246 '_x': {unknown} + 249..257 'to_bytes': fn to_bytes() -> [u8; _] + 249..259 'to_bytes()': [u8; _] + 249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item> + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 60ad0f49c6ab0..9d02a44c37c97 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -439,11 +439,11 @@ h"; 256..260 'true': bool 274..370 'r#" ... "#': &'static str 384..394 'br#"yolo"#': &'static [u8; 4] - 412..413 'a': &'static [u8; 4] + 412..413 'a': &'? [u8; 4] 416..440 'b"a\x2... c"': &'static [u8; 4] - 458..459 'b': &'static [u8; 4] + 458..459 'b': &'? [u8; 4] 462..470 'br"g\ h"': &'static [u8; 4] - 488..489 'c': &'static [u8; 6] + 488..489 'c': &'? [u8; 6] 492..504 'br#"x"\"yb"#': &'static [u8; 6] "##]], ); @@ -1124,13 +1124,13 @@ fn infer_tuple() { 116..122 '(c, x)': ((isize, &'? str), &'? str) 117..118 'c': (isize, &'? str) 120..121 'x': &'? str - 132..133 'e': (i32, &'static str) - 136..144 '(1, "e")': (i32, &'static str) + 132..133 'e': (i32, &'? str) + 136..144 '(1, "e")': (i32, &'? str) 137..138 '1': i32 140..143 '"e"': &'static str - 154..155 'f': ((i32, &'static str), &'static str) - 158..166 '(e, "d")': ((i32, &'static str), &'static str) - 159..160 'e': (i32, &'static str) + 154..155 'f': ((i32, &'? str), &'? str) + 158..166 '(e, "d")': ((i32, &'? str), &'? str) + 159..160 'e': (i32, &'? str) 162..165 '"d"': &'static str "#]], ); @@ -1201,8 +1201,8 @@ fn infer_array() { 209..215 '[1, 2]': [i32; 2] 210..211 '1': i32 213..214 '2': i32 - 225..226 'i': [&'static str; 2] - 229..239 '["a", "b"]': [&'static str; 2] + 225..226 'i': [&'? str; 2] + 229..239 '["a", "b"]': [&'? str; 2] 230..233 '"a"': &'static str 235..238 '"b"': &'static str 250..251 'b': [[&'? str; 1]; 2] @@ -1283,11 +1283,11 @@ fn infer_tuple_struct_generics() { 92..93 'A': fn A(u128) -> A 92..101 'A(42u128)': A 94..100 '42u128': u128 - 107..111 'Some': fn Some<&'static str>(&'static str) -> Option<&'static str> - 107..116 'Some("x")': Option<&'static str> + 107..111 'Some': fn Some<&'? str>(&'? str) -> Option<&'? str> + 107..116 'Some("x")': Option<&'? str> 112..115 '"x"': &'static str - 122..134 'Option::Some': fn Some<&'static str>(&'static str) -> Option<&'static str> - 122..139 'Option...e("x")': Option<&'static str> + 122..134 'Option::Some': fn Some<&'? str>(&'? str) -> Option<&'? str> + 122..139 'Option...e("x")': Option<&'? str> 135..138 '"x"': &'static str 145..149 'None': Option<{unknown}> 159..160 'x': Option @@ -1946,9 +1946,9 @@ fn closure_return_inferred() { "#, expect![[r#" 16..46 '{ ..." }; }': u32 - 26..27 'x': impl Fn() -> &'static str - 30..43 '|| { "test" }': impl Fn() -> &'static str - 33..43 '{ "test" }': &'static str + 26..27 'x': impl Fn() -> &'? str + 30..43 '|| { "test" }': impl Fn() -> &'? str + 33..43 '{ "test" }': &'? str 35..41 '"test"': &'static str "#]], ); @@ -1983,10 +1983,10 @@ fn test() { 70..71 'v': i64 78..80 '{}': () 91..362 '{ ... } }': () - 101..106 'mut g': |usize| yields i64 -> &'static str - 109..218 '|r| { ... }': |usize| yields i64 -> &'static str + 101..106 'mut g': |usize| yields i64 -> &'? str + 109..218 '|r| { ... }': |usize| yields i64 -> &'? str 110..111 'r': usize - 113..218 '{ ... }': &'static str + 113..218 '{ ... }': &'? str 127..128 'a': usize 131..138 'yield 0': usize 137..138 '0': i64 @@ -1998,20 +1998,20 @@ fn test() { 187..188 '2': i64 198..212 '"return value"': &'static str 225..360 'match ... }': () - 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> - 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState - 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str - 245..246 'g': |usize| yields i64 -> &'static str + 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'? str>(&'? mut |usize| yields i64 -> &'? str) -> Pin<&'? mut |usize| yields i64 -> &'? str> + 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'? str> + 231..262 'Pin::n...usize)': CoroutineState + 240..246 '&mut g': &'? mut |usize| yields i64 -> &'? str + 245..246 'g': |usize| yields i64 -> &'? str 255..261 '0usize': usize - 273..299 'Corout...ded(y)': CoroutineState + 273..299 'Corout...ded(y)': CoroutineState 297..298 'y': i64 303..312 '{ f(y); }': () 305..306 'f': fn f(i64) 305..309 'f(y)': () 307..308 'y': i64 - 321..348 'Corout...ete(r)': CoroutineState - 346..347 'r': &'static str + 321..348 'Corout...ete(r)': CoroutineState + 346..347 'r': &'? str 352..354 '{}': () "#]], ); @@ -2707,11 +2707,11 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box; +pub struct Box(T, A); impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} -pub struct Vec {} +pub struct Vec(T, A); #[lang = "slice"] impl [T] {} @@ -2734,22 +2734,22 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 604..608 'self': Box<[T], A> - 637..669 '{ ... }': Vec - 683..853 '{ ...])); }': () - 693..696 'vec': Vec - 699..714 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec - 699..745 '<[_]>:...i32]))': Vec - 715..744 '#[rust...1i32])': Box<[i32; 1], Global> - 737..743 '[1i32]': [i32; 1] - 738..742 '1i32': i32 - 755..756 'v': Vec, Global> - 776..793 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> - 776..850 '<[_]> ...ct)]))': Vec, Global> - 794..849 '#[rust...uct)])': Box<[Box; 1], Global> - 816..848 '[#[rus...ruct)]': [Box; 1] - 817..847 '#[rust...truct)': Box - 839..846 'Astruct': Astruct + 614..618 'self': Box<[T], A> + 647..679 '{ ... }': Vec + 693..863 '{ ...])); }': () + 703..706 'vec': Vec + 709..724 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec + 709..755 '<[_]>:...i32]))': Vec + 725..754 '#[rust...1i32])': Box<[i32; 1], Global> + 747..753 '[1i32]': [i32; 1] + 748..752 '1i32': i32 + 765..766 'v': Vec, Global> + 786..803 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> + 786..860 '<[_]> ...ct)]))': Vec, Global> + 804..859 '#[rust...uct)])': Box<[Box; 1], Global> + 826..858 '[#[rus...ruct)]': [Box; 1] + 827..857 '#[rust...truct)': Box + 849..856 'Astruct': Astruct "#]], ) } @@ -3889,9 +3889,9 @@ fn main() { 74..75 'f': F 80..82 '{}': () 94..191 '{ ... }); }': () - 100..113 'async_closure': fn async_closure impl Future>(impl AsyncFnOnce(i32) -> impl Future) + 100..113 'async_closure': fn async_closure(impl FnOnce(i32)) 100..147 'async_... })': () - 114..146 'async ... }': impl AsyncFnOnce(i32) -> impl Future + 114..146 'async ... }': impl FnOnce(i32) 121..124 'arg': i32 126..146 '{ ... }': () 136..139 'arg': i32 @@ -3924,7 +3924,7 @@ fn foo() { expect![[r#" 110..127 '{ ...z(); }': () 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> - 116..124 'T::baz()': {unknown} + 116..124 'T::baz()': <{unknown} as Foo>::Gat<'?> "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 22332fdc2b8aa..41f8d4ed555f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -85,6 +85,7 @@ async fn test() { } #[test] +#[ignore = "FIXME(next-solver): fix async closures"] fn infer_async_closure() { check_types( r#" @@ -164,16 +165,16 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box(T); +pub struct Box(T, A); impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} fn send() -> Box + Send + 'static>{ - Box(async move {}) + Box(async move {}, Global) } fn not_send() -> Box + 'static> { - Box(async move {}) + Box(async move {}, Global) } "#, ); @@ -248,15 +249,15 @@ fn test() { v.push("foo"); for x in v { x; - } //^ &'static str + } //^ &'? str } //- /alloc.rs crate:alloc #![no_std] pub mod collections { - pub struct Vec {} + pub struct Vec { p: *const T } impl Vec { - pub fn new() -> Self { Vec {} } + pub fn new() -> Self { Vec { p: 0 as _ } } pub fn push(&mut self, t: T) { } } @@ -722,8 +723,8 @@ fn deref_trait_with_inference_var() { check_types( r#" //- minicore: deref -struct Arc; -fn new_arc() -> Arc { Arc } +struct Arc(T); +fn new_arc() -> Arc { loop {} } impl core::ops::Deref for Arc { type Target = T; } @@ -785,13 +786,15 @@ fn test(s: Arc) { fn deref_trait_with_implicit_sized_requirement_on_inference_var() { check_types( r#" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn test() { - let foo = Foo; + let foo = Foo(PhantomData); *foo; //^^^^ () let _: Foo = foo; @@ -1456,7 +1459,7 @@ trait Trait { fn foo2(&self) -> i64; } -struct Box {} +struct Box(*const T); impl core::ops::Deref for Box { type Target = T; } @@ -1477,27 +1480,27 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 198..200 '{}': Box + '?> - 210..211 'x': Box + '?> - 234..235 'y': &'? (dyn Trait + '?) - 254..371 '{ ...2(); }': () - 260..261 'x': Box + '?> - 267..268 'y': &'? (dyn Trait + '?) - 278..279 'z': Box + '?> - 282..285 'bar': fn bar() -> Box + '?> - 282..287 'bar()': Box + '?> - 293..294 'x': Box + '?> - 293..300 'x.foo()': u64 - 306..307 'y': &'? (dyn Trait + '?) - 306..313 'y.foo()': u64 - 319..320 'z': Box + '?> - 319..326 'z.foo()': u64 - 332..333 'x': Box + '?> - 332..340 'x.foo2()': i64 - 346..347 'y': &'? (dyn Trait + '?) - 346..354 'y.foo2()': i64 - 360..361 'z': Box + '?> - 360..368 'z.foo2()': i64 + 206..208 '{}': Box + '?> + 218..219 'x': Box + '?> + 242..243 'y': &'? (dyn Trait + '?) + 262..379 '{ ...2(); }': () + 268..269 'x': Box + '?> + 275..276 'y': &'? (dyn Trait + '?) + 286..287 'z': Box + '?> + 290..293 'bar': fn bar() -> Box + '?> + 290..295 'bar()': Box + '?> + 301..302 'x': Box + '?> + 301..308 'x.foo()': u64 + 314..315 'y': &'? (dyn Trait + '?) + 314..321 'y.foo()': u64 + 327..328 'z': Box + '?> + 327..334 'z.foo()': u64 + 340..341 'x': Box + '?> + 340..348 'x.foo2()': i64 + 354..355 'y': &'? (dyn Trait + '?) + 354..362 'y.foo2()': i64 + 368..369 'z': Box + '?> + 368..376 'z.foo2()': i64 "#]], ); } @@ -1674,7 +1677,9 @@ fn test(x: (impl Trait + UnknownTrait)) { fn assoc_type_bindings() { check_infer( r#" -//- minicore: sized +//- minicore: sized, phantom_data +use core::marker::PhantomData; + trait Trait { type Type; } @@ -1683,7 +1688,7 @@ fn get(t: T) -> ::Type {} fn get2>(t: T) -> U {} fn set>(t: T) -> T {t} -struct S; +struct S(PhantomData); impl Trait for S { type Type = T; } fn test>(x: T, y: impl Trait) { @@ -1691,46 +1696,52 @@ fn test>(x: T, y: impl Trait) { get2(x); get(y); get2(y); - get(set(S)); - get2(set(S)); - get2(S::); + get(set(S(PhantomData))); + get2(set(S(PhantomData))); + get2(S::(PhantomData)); }"#, expect![[r#" - 49..50 't': T - 77..79 '{}': ::Type - 111..112 't': T - 122..124 '{}': U - 154..155 't': T - 165..168 '{t}': T - 166..167 't': T - 256..257 'x': T - 262..263 'y': impl Trait - 289..399 '{ ...e>); }': () - 295..298 'get': fn get(T) -> ::Type - 295..301 'get(x)': u32 - 299..300 'x': T - 307..311 'get2': fn get2(T) -> u32 - 307..314 'get2(x)': u32 - 312..313 'x': T - 320..323 'get': fn get>(impl Trait) -> as Trait>::Type - 320..326 'get(y)': i64 - 324..325 'y': impl Trait - 332..336 'get2': fn get2>(impl Trait) -> i64 - 332..339 'get2(y)': i64 - 337..338 'y': impl Trait - 345..348 'get': fn get>(S) -> as Trait>::Type - 345..356 'get(set(S))': u64 - 349..352 'set': fn set>(S) -> S - 349..355 'set(S)': S - 353..354 'S': S - 362..366 'get2': fn get2>(S) -> u64 - 362..374 'get2(set(S))': u64 - 367..370 'set': fn set>(S) -> S - 367..373 'set(S)': S - 371..372 'S': S - 380..384 'get2': fn get2>(S) -> usize - 380..396 'get2(S...size>)': usize - 385..395 'S::': S + 81..82 't': T + 109..111 '{}': ::Type + 143..144 't': T + 154..156 '{}': U + 186..187 't': T + 197..200 '{t}': T + 198..199 't': T + 304..305 'x': T + 310..311 'y': impl Trait + 337..486 '{ ...a)); }': () + 343..346 'get': fn get(T) -> ::Type + 343..349 'get(x)': u32 + 347..348 'x': T + 355..359 'get2': fn get2(T) -> u32 + 355..362 'get2(x)': u32 + 360..361 'x': T + 368..371 'get': fn get>(impl Trait) -> as Trait>::Type + 368..374 'get(y)': i64 + 372..373 'y': impl Trait + 380..384 'get2': fn get2>(impl Trait) -> i64 + 380..387 'get2(y)': i64 + 385..386 'y': impl Trait + 393..396 'get': fn get>(S) -> as Trait>::Type + 393..417 'get(se...ata)))': u64 + 397..400 'set': fn set>(S) -> S + 397..416 'set(S(...Data))': S + 401..402 'S': fn S(PhantomData) -> S + 401..415 'S(PhantomData)': S + 403..414 'PhantomData': PhantomData + 423..427 'get2': fn get2>(S) -> u64 + 423..448 'get2(s...ata)))': u64 + 428..431 'set': fn set>(S) -> S + 428..447 'set(S(...Data))': S + 432..433 'S': fn S(PhantomData) -> S + 432..446 'S(PhantomData)': S + 434..445 'PhantomData': PhantomData + 454..458 'get2': fn get2>(S) -> usize + 454..483 'get2(S...Data))': usize + 459..469 'S::': fn S(PhantomData) -> S + 459..482 'S:: + 470..481 'PhantomData': PhantomData "#]], ); } @@ -1747,7 +1758,7 @@ pub enum RustLanguage {} impl Language for RustLanguage { type Kind = SyntaxKind; } -struct SyntaxNode {} +struct SyntaxNode(L); fn foo() -> impl Iterator> {} trait Clone { @@ -1886,31 +1897,36 @@ fn super_trait_cycle() { fn super_trait_assoc_type_bounds() { check_infer( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait SuperTrait { type Type; } trait Trait where Self: SuperTrait {} fn get2>(t: T) -> U {} fn set>(t: T) -> T {t} -struct S; +struct S(PhantomData); impl SuperTrait for S { type Type = T; } impl Trait for S {} fn test() { - get2(set(S)); + get2(set(S(PhantomData))); }"#, expect![[r#" - 102..103 't': T - 113..115 '{}': U - 145..146 't': T - 156..159 '{t}': T - 157..158 't': T - 258..279 '{ ...S)); }': () - 264..268 'get2': fn get2>(S) -> u64 - 264..276 'get2(set(S))': u64 - 269..272 'set': fn set>(S) -> S - 269..275 'set(S)': S - 273..274 'S': S + 134..135 't': T + 145..147 '{}': U + 177..178 't': T + 188..191 '{t}': T + 189..190 't': T + 306..340 '{ ...))); }': () + 312..316 'get2': fn get2>(S) -> u64 + 312..337 'get2(s...ata)))': u64 + 317..320 'set': fn set>(S) -> S + 317..336 'set(S(...Data))': S + 321..322 'S': fn S(PhantomData) -> S + 321..335 'S(PhantomData)': S + 323..334 'PhantomData': PhantomData "#]], ); } @@ -2000,7 +2016,7 @@ impl Foo { fn foo(&self) -> usize {} } -struct Lazy T>(F); +struct Lazy T>(T, F); impl Lazy { pub fn new(f: F) -> Lazy {} @@ -2014,7 +2030,7 @@ fn test() { let lazy1: Lazy = Lazy::new(|| Foo); let r1 = lazy1.foo(); - fn make_foo_fn() -> Foo {} +fn make_foo_fn() -> Foo {} let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; let lazy2: Lazy = Lazy::new(make_foo_fn_ptr); let r2 = lazy2.foo(); @@ -2022,27 +2038,27 @@ fn test() { expect![[r#" 36..40 'self': &'? Foo 51..53 '{}': usize - 131..132 'f': F - 151..153 '{}': Lazy - 251..497 '{ ...o(); }': () - 261..266 'lazy1': Lazy Foo> - 283..292 'Lazy::new': fn new Foo>(impl Fn() -> Foo) -> Lazy Foo> - 283..300 'Lazy::...| Foo)': Lazy Foo> - 293..299 '|| Foo': impl Fn() -> Foo - 296..299 'Foo': Foo - 310..312 'r1': usize - 315..320 'lazy1': Lazy Foo> - 315..326 'lazy1.foo()': usize - 368..383 'make_foo_fn_ptr': fn() -> Foo - 399..410 'make_foo_fn': fn make_foo_fn() -> Foo - 420..425 'lazy2': Lazy Foo> - 442..451 'Lazy::new': fn new Foo>(fn() -> Foo) -> Lazy Foo> - 442..468 'Lazy::...n_ptr)': Lazy Foo> - 452..467 'make_foo_fn_ptr': fn() -> Foo - 478..480 'r2': usize - 483..488 'lazy2': Lazy Foo> - 483..494 'lazy2.foo()': usize - 357..359 '{}': Foo + 134..135 'f': F + 154..156 '{}': Lazy + 254..496 '{ ...o(); }': () + 264..269 'lazy1': Lazy Foo> + 286..295 'Lazy::new': fn new Foo>(impl Fn() -> Foo) -> Lazy Foo> + 286..303 'Lazy::...| Foo)': Lazy Foo> + 296..302 '|| Foo': impl Fn() -> Foo + 299..302 'Foo': Foo + 313..315 'r1': usize + 318..323 'lazy1': Lazy Foo> + 318..329 'lazy1.foo()': usize + 367..382 'make_foo_fn_ptr': fn() -> Foo + 398..409 'make_foo_fn': fn make_foo_fn() -> Foo + 419..424 'lazy2': Lazy Foo> + 441..450 'Lazy::new': fn new Foo>(fn() -> Foo) -> Lazy Foo> + 441..467 'Lazy::...n_ptr)': Lazy Foo> + 451..466 'make_foo_fn_ptr': fn() -> Foo + 477..479 'r2': usize + 482..487 'lazy2': Lazy Foo> + 482..493 'lazy2.foo()': usize + 356..358 '{}': Foo "#]], ); } @@ -2341,7 +2357,7 @@ trait Fold { type Result; } -struct Ty {} +struct Ty(I); impl Fold for Ty { type Result = Ty; } @@ -2383,17 +2399,20 @@ fn test() { fn trait_impl_self_ty_cycle() { check_types( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait Trait { fn foo(&self); } -struct S; +struct S(T); impl Trait for S {} fn test() { - S.foo(); -} //^^^^^^^ {unknown} + S(PhantomData).foo(); +} //^^^^^^^^^^^^^^^^^^^^ {unknown} "#, ); } @@ -2743,7 +2762,7 @@ fn dyn_trait_through_chalk() { check_types( r#" //- minicore: deref, unsize, dispatch_from_dyn -struct Box {} +struct Box(*const T); impl core::ops::Deref for Box { type Target = T; } @@ -2802,7 +2821,7 @@ pub trait IntoIterator { fn into_iter(self) -> Self::IntoIter; } -pub struct FilterMap { } +pub struct FilterMap(I, F); impl Iterator for FilterMap where F: FnMut(I::Item) -> Option, @@ -2820,7 +2839,7 @@ impl IntoIterator for I { } } -struct Vec {} +struct Vec(T); impl Vec { fn new() -> Self { loop {} } } @@ -2830,7 +2849,7 @@ impl IntoIterator for Vec { type IntoIter = IntoIter; } -pub struct IntoIter { } +pub struct IntoIter(T); impl Iterator for IntoIter { type Item = T; } @@ -2852,35 +2871,35 @@ fn main() { 242..249 'loop {}': ! 247..249 '{}': () 360..364 'self': Self - 689..693 'self': I - 700..720 '{ ... }': I - 710..714 'self': I - 779..790 '{ loop {} }': Vec - 781..788 'loop {}': ! - 786..788 '{}': () - 977..1104 '{ ... }); }': () - 983..998 'Vec::::new': fn new() -> Vec - 983..1000 'Vec::<...:new()': Vec - 983..1012 'Vec::<...iter()': IntoIter - 983..1075 'Vec::<...one })': FilterMap, impl FnMut(i32) -> Option> - 983..1101 'Vec::<... y; })': () - 1029..1074 '|x| if...None }': impl FnMut(i32) -> Option - 1030..1031 'x': i32 - 1033..1074 'if x >...None }': Option - 1036..1037 'x': i32 - 1036..1041 'x > 0': bool - 1040..1041 '0': i32 - 1042..1060 '{ Some...u32) }': Option - 1044..1048 'Some': fn Some(u32) -> Option - 1044..1058 'Some(x as u32)': Option - 1049..1050 'x': i32 - 1049..1057 'x as u32': u32 - 1066..1074 '{ None }': Option - 1068..1072 'None': Option - 1090..1100 '|y| { y; }': impl FnMut(u32) - 1091..1092 'y': u32 - 1094..1100 '{ y; }': () - 1096..1097 'y': u32 + 692..696 'self': I + 703..723 '{ ... }': I + 713..717 'self': I + 783..794 '{ loop {} }': Vec + 785..792 'loop {}': ! + 790..792 '{}': () + 981..1108 '{ ... }); }': () + 987..1002 'Vec::::new': fn new() -> Vec + 987..1004 'Vec::<...:new()': Vec + 987..1016 'Vec::<...iter()': IntoIter + 987..1079 'Vec::<...one })': FilterMap, impl FnMut(i32) -> Option> + 987..1105 'Vec::<... y; })': () + 1033..1078 '|x| if...None }': impl FnMut(i32) -> Option + 1034..1035 'x': i32 + 1037..1078 'if x >...None }': Option + 1040..1041 'x': i32 + 1040..1045 'x > 0': bool + 1044..1045 '0': i32 + 1046..1064 '{ Some...u32) }': Option + 1048..1052 'Some': fn Some(u32) -> Option + 1048..1062 'Some(x as u32)': Option + 1053..1054 'x': i32 + 1053..1061 'x as u32': u32 + 1070..1078 '{ None }': Option + 1072..1076 'None': Option + 1094..1104 '|y| { y; }': impl FnMut(u32) + 1095..1096 'y': u32 + 1098..1104 '{ y; }': () + 1100..1101 'y': u32 "#]], ); } @@ -3134,7 +3153,6 @@ fn foo() { #[test] fn dyn_fn_param_informs_call_site_closure_signature() { - cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature); check_types( r#" //- minicore: fn, coerce_unsized, dispatch_from_dyn @@ -3617,7 +3635,7 @@ impl Add<&i32> for i32 { type Output = i32 } impl Add for u32 { type Output = u32 } impl Add<&u32> for u32 { type Output = u32 } -struct V; +struct V(T); impl V { fn default() -> Self { loop {} } fn get(&self, _: &T) -> &T { loop {} } @@ -3646,7 +3664,7 @@ impl Add<&i32> for i32 { type Output = i32; } // fallback to integer type variable for `42`. impl Add<&()> for i32 { type Output = (); } -struct V; +struct V(T); impl V { fn default() -> Self { loop {} } fn get(&self) -> &T { loop {} } @@ -4213,21 +4231,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'a T + //^ &'? T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } "#, ); @@ -4257,8 +4275,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': {unknown} - 170..192 'v.get:...eref()': &'? {unknown} + 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc + 170..192 'v.get:...eref()': {unknown} "#]], ); } @@ -4485,7 +4503,9 @@ impl Trait for () { fn derive_macro_bounds() { check_types( r#" - //- minicore: clone, derive + //- minicore: clone, derive, phantom_data + use core::marker::PhantomData; + #[derive(Clone)] struct Copy; struct NotCopy; @@ -4508,7 +4528,7 @@ fn derive_macro_bounds() { struct AssocGeneric3(Generic); #[derive(Clone)] - struct Vec(); + struct Vec(PhantomData); #[derive(Clone)] struct R1(Vec); @@ -4532,9 +4552,9 @@ fn derive_macro_bounds() { let x: &AssocGeneric3 = &AssocGeneric3(Generic(NotCopy)); let x = x.clone(); //^ &'? AssocGeneric3 - let x = (&R1(Vec())).clone(); + let x = (&R1(Vec(PhantomData))).clone(); //^ R1 - let x = (&R2(R1(Vec()))).clone(); + let x = (&R2(R1(Vec(PhantomData)))).clone(); //^ R2 } "#, @@ -4624,8 +4644,10 @@ fn ttt() { fn infer_borrow() { check_types( r#" -//- minicore: index -pub struct SomeMap; +//- minicore: index, phantom_data +use core::marker::PhantomData; + +pub struct SomeMap(PhantomData); pub trait Borrow { fn borrow(&self) -> &Borrowed; @@ -4658,7 +4680,7 @@ impl core::ops::IndexMut for SomeMap { } fn foo() { - let mut map = SomeMap; + let mut map = SomeMap(PhantomData); map["a"] = (); map; //^^^ SomeMap<&'static str> @@ -4908,6 +4930,7 @@ fn main() { #[test] fn async_fn_return_type() { + // FIXME(next-solver): Async closures are lowered as closures currently. We should fix that. check_infer( r#" //- minicore: async_fn @@ -4925,9 +4948,9 @@ fn main() { 46..53 'loop {}': ! 51..53 '{}': () 67..97 '{ ...()); }': () - 73..76 'foo': fn foo impl Future, ()>(impl AsyncFn() -> impl Future) + 73..76 'foo': fn foo(impl Fn()) 73..94 'foo(as...|| ())': () - 77..93 'async ... || ()': impl AsyncFn() -> impl Future + 77..93 'async ... || ()': impl Fn() 91..93 '()': () "#]], ); @@ -5020,7 +5043,7 @@ fn main() { 223..227 'iter': Box + 'static> 273..280 'loop {}': ! 278..280 '{}': () - 290..291 '_': Box + 'static> + 290..291 '_': Box + '?> 294..298 'iter': Box + 'static> 294..310 'iter.i...iter()': Box + 'static> 152..156 'self': &'? mut Box diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index a6377243ed984..d73d7da4b9b98 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -428,9 +428,4 @@ impl FnTrait { pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option { self.lang_item().resolve_trait(db, krate) } - - #[inline] - pub(crate) fn is_async(self) -> bool { - matches!(self, FnTrait::AsyncFn | FnTrait::AsyncFnMut | FnTrait::AsyncFnOnce) - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 07679d2a119d3..427c4bb68423d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -20,14 +20,14 @@ use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::{IntoKind, SliceLike}; +use rustc_type_ir::inherent::{GenericArgs, IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; -use stdx::never; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ - ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TargetFeatures, TraitRef, - TraitRefExt, Ty, WhereClause, + ChalkTraitId, Const, ConstScalar, Interner, Substitution, TargetFeatures, TraitRef, + TraitRefExt, Ty, consteval::unknown_const, db::HirDatabase, layout::{Layout, TagEncoding}, @@ -120,52 +120,6 @@ impl Iterator for SuperTraits<'_> { } } -pub(super) fn elaborate_clause_supertraits( - db: &dyn HirDatabase, - clauses: impl Iterator, -) -> ClauseElaborator<'_> { - let mut elaborator = ClauseElaborator { db, stack: Vec::new(), seen: FxHashSet::default() }; - elaborator.extend_deduped(clauses); - - elaborator -} - -pub(super) struct ClauseElaborator<'a> { - db: &'a dyn HirDatabase, - stack: Vec, - seen: FxHashSet, -} - -impl ClauseElaborator<'_> { - fn extend_deduped(&mut self, clauses: impl IntoIterator) { - self.stack.extend(clauses.into_iter().filter(|c| self.seen.insert(c.clone()))) - } - - fn elaborate_supertrait(&mut self, clause: &WhereClause) { - if let WhereClause::Implemented(trait_ref) = clause { - direct_super_trait_refs(self.db, trait_ref, |t| { - let clause = WhereClause::Implemented(t); - if self.seen.insert(clause.clone()) { - self.stack.push(clause); - } - }); - } - } -} - -impl Iterator for ClauseElaborator<'_> { - type Item = WhereClause; - - fn next(&mut self) -> Option { - if let Some(next) = self.stack.pop() { - self.elaborate_supertrait(&next); - Some(next) - } else { - None - } - } -} - fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { let resolver = LazyCell::new(|| trait_.resolver(db)); let (generic_params, store) = db.generic_params_and_store(trait_.into()); @@ -239,34 +193,25 @@ pub(super) fn associated_type_by_name_including_super_traits( }) } -/// It is a bit different from the rustc equivalent. Currently it stores: -/// - 0..n-1: generics of the parent -/// - n: the function signature, encoded as a function pointer type -/// -/// and it doesn't store the closure types and fields. -/// -/// Codes should not assume this ordering, and should always use methods available -/// on this struct for retrieving, and `TyBuilder::substs_for_closure` for creating. pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution); impl<'a> ClosureSubst<'a> { - pub(crate) fn parent_subst(&self) -> &'a [GenericArg] { - match self.0.as_slice(Interner) { - [x @ .., _] => x, - _ => { - never!("Closure missing parameter"); - &[] - } - } + pub(crate) fn parent_subst(&self, db: &dyn HirDatabase) -> Substitution { + let interner = DbInterner::new_with(db, None, None); + let subst = + >>::to_nextsolver( + self.0, interner, + ); + subst.split_closure_args().parent_args.to_chalk(interner) } - pub(crate) fn sig_ty(&self) -> &'a Ty { - match self.0.as_slice(Interner) { - [.., x] => x.assert_ty_ref(Interner), - _ => { - unreachable!("Closure missing sig_ty parameter"); - } - } + pub(crate) fn sig_ty(&self, db: &dyn HirDatabase) -> Ty { + let interner = DbInterner::new_with(db, None, None); + let subst = + >>::to_nextsolver( + self.0, interner, + ); + subst.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.to_chalk(interner) } } @@ -278,8 +223,17 @@ pub enum Unsafety { DeprecatedSafe2024, } -pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool { - matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TargetFeatureIsSafeInTarget { + No, + Yes, +} + +pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsSafeInTarget { + match target.arch { + target::Arch::Wasm32 | target::Arch::Wasm64 => TargetFeatureIsSafeInTarget::Yes, + _ => TargetFeatureIsSafeInTarget::No, + } } pub fn is_fn_unsafe_to_call( @@ -287,14 +241,14 @@ pub fn is_fn_unsafe_to_call( func: FunctionId, caller_target_features: &TargetFeatures, call_edition: Edition, - target_feature_is_safe: bool, + target_feature_is_safe: TargetFeatureIsSafeInTarget, ) -> Unsafety { let data = db.function_signature(func); if data.is_unsafe() { return Unsafety::Unsafe; } - if data.has_target_feature() && !target_feature_is_safe { + if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No { // RFC 2396 . let callee_target_features = TargetFeatures::from_attrs_no_implications(&db.attrs(func.into())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index a1ebff04bbd47..8593dba301b85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -49,7 +49,23 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option { } "#, expect![[r#" - Hello['a: bivariant] - Other['a: bivariant] + Hello['a: invariant] + Other['a: invariant] "#]], ); } @@ -601,7 +618,7 @@ struct Foo { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: bivariant] + Foo[T: invariant] "#]], ); } @@ -683,9 +700,9 @@ struct TestBox+Setter> { //~ ERROR [U: *, T: +] get[Self: contravariant, T: covariant] get[Self: contravariant, T: contravariant] TestStruct[U: covariant, T: covariant] - TestEnum[U: bivariant, T: covariant] - TestContraStruct[U: bivariant, T: covariant] - TestBox[U: bivariant, T: covariant] + TestEnum[U: invariant, T: covariant] + TestContraStruct[U: invariant, T: covariant] + TestBox[U: invariant, T: covariant] "#]], ); } @@ -805,8 +822,8 @@ enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used trait SomeTrait<'a> { fn foo(&self); } // OK on traits. "#, expect![[r#" - SomeStruct['a: bivariant] - SomeEnum['a: bivariant] + SomeStruct['a: invariant] + SomeEnum['a: invariant] foo[Self: contravariant, 'a: invariant] "#]], ); @@ -834,14 +851,14 @@ struct DoubleNothing { "#, expect![[r#" - SomeStruct[A: bivariant] - SomeEnum[A: bivariant] - ListCell[T: bivariant] - SelfTyAlias[T: bivariant] - WithBounds[T: bivariant] - WithWhereBounds[T: bivariant] - WithOutlivesBounds[T: bivariant] - DoubleNothing[T: bivariant] + SomeStruct[A: invariant] + SomeEnum[A: invariant] + ListCell[T: invariant] + SelfTyAlias[T: invariant] + WithBounds[T: invariant] + WithWhereBounds[T: invariant] + WithOutlivesBounds[T: invariant] + DoubleNothing[T: invariant] "#]], ); } @@ -952,7 +969,7 @@ struct S3(S); "#, expect![[r#" S[T: covariant] - S2[T: bivariant] + S2[T: invariant] S3[T: covariant] "#]], ); @@ -965,7 +982,7 @@ struct S3(S); struct FixedPoint(&'static FixedPoint<(), T, U>, V); "#, expect![[r#" - FixedPoint[T: bivariant, U: bivariant, V: bivariant] + FixedPoint[T: invariant, U: invariant, V: invariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index bd9360f30f165..3083b56515628 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2549,11 +2549,13 @@ impl Function { let target_feature_is_safe_in_target = match &caller.krate(db).id.workspace_data(db).target { Ok(target) => hir_ty::target_feature_is_safe_in_target(target), - Err(_) => false, + Err(_) => hir_ty::TargetFeatureIsSafeInTarget::No, }; (target_features, target_feature_is_safe_in_target) }) - .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false)); + .unwrap_or_else(|| { + (hir_ty::TargetFeatures::default(), hir_ty::TargetFeatureIsSafeInTarget::No) + }); matches!( hir_ty::is_fn_unsafe_to_call( db, @@ -4660,7 +4662,7 @@ impl Closure { .iter() .map(|capture| Type { env: db.trait_environment_for_body(owner), - ty: capture.ty(&self.subst), + ty: capture.ty(db, &self.subst), _pd: PhantomCovariantLifetime::new(), }) .collect() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 5f526ec899404..3dd435d9423b2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -805,6 +805,7 @@ impl A { ); } + #[ignore = "FIXME(next-solver): Fix async closures"] #[test] fn replaces_async_closure_with_async_fn() { check_assist( @@ -1066,7 +1067,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { *a = 1.2; let c = *b; } @@ -1098,7 +1099,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { let _: &mut bool = p2; *a = 1.2; let c = *b; @@ -1136,7 +1137,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { let _: &mut bool = p2; *a = 1.2; let c = *b; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index d88e3311bd7bf..3685cc904e947 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -5043,7 +5043,7 @@ fn main() { fun_name(bar); } -fn $0fun_name(bar: &'static str) { +fn $0fun_name(bar: &str) { m!(bar); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index b9ef68cc2d2a3..8b4f315ac5733 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1501,7 +1501,9 @@ fn main() { bar.$0 } "#, - expect![[r#""#]], + expect![[r#" + me foo() fn(self: Bar) + "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 79137104b568d..5cc72ef845bf5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2011,7 +2011,7 @@ fn main() { en Enum Enum fn function() fn() fn main() fn() - lc variable &'static str + lc variable &str ma helper!(…) macro_rules! helper ma m!(…) macro_rules! m ma makro!(…) macro_rules! makro @@ -2486,6 +2486,7 @@ fn bar() { md rust_2024 (use core::prelude::rust_2024) tt Clone tt Copy + tt FromIterator tt IntoIterator tt Iterator ta Result (use core::fmt::Result) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index 8d42770269057..542ac215f1cc6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -152,7 +152,7 @@ fn main() { fn main() { let mut x = t(); x = _; - //^ 💡 error: invalid `_` expression, expected type `&'static str` + //^ 💡 error: invalid `_` expression, expected type `&str` x = ""; } fn t() -> T { loop {} } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index dcca85d4db33e..bd5d134348e27 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -268,7 +268,7 @@ impl A { } fn main() { let a = A {a: 0, b: ""}; - A::::foo(); + A::::foo(); } "#, ); @@ -351,4 +351,26 @@ fn foo() { "#, ); } + + #[test] + fn iter_collect() { + check_diagnostics( + r#" +//- minicore: unsize, coerce_unsized, iterator, iterators, sized +struct Map(K, V); +impl FromIterator<(K, V)> for Map { + fn from_iter>(_iter: T) -> Self { + loop {} + } +} + +fn foo() -> Map { + [ + (123, &["abc", "def"] as _), + (456, &["ghi"] as _), + ].into_iter().collect() +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index a88c0c9866e5f..b623e51ee45e5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -7195,7 +7195,7 @@ fn foo() { "#, expect![[r#" ```rust - &'static str + &str ```"#]], ); } @@ -8459,7 +8459,7 @@ format_args!("{aaaaa$0}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8479,7 +8479,7 @@ format_args!("{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8499,7 +8499,7 @@ format_args!(r"{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8524,7 +8524,7 @@ foo!(r"{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -10168,7 +10168,7 @@ fn baz() { --- - `U` = `i32`, `T` = `&'static str` + `U` = `i32`, `T` = `&str` "#]], ); } @@ -10261,7 +10261,7 @@ fn bar() { --- - `T` = `i8`, `U` = `&'static str` + `T` = `i8`, `U` = `&str` "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 7a4af4f7549c6..104740cbbf74a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -378,9 +378,9 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static dyn Fn(f64, f64) -> u32 + // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ &'static dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo7(); @@ -411,7 +411,7 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static dyn Fn(f64, f64) -> u32 + // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); let foo = foo6(); let foo = foo7(); @@ -526,7 +526,7 @@ fn main() { //^^^^ i32 let _ = 22; let test = "test"; - //^^^^ &'static str + //^^^^ &str let test = InnerStruct {}; //^^^^ InnerStruct @@ -616,12 +616,12 @@ impl Iterator for IntoIter { fn main() { let mut data = Vec::new(); - //^^^^ Vec<&'static str> + //^^^^ Vec<&str> data.push("foo"); for i in data { - //^ &'static str + //^ &str let z = i; - //^ &'static str + //^ &str } } "#, @@ -1015,7 +1015,7 @@ fn test(t: T) { "#, expect![[r#" fn test(t: T) { - let f = |a: i32, b: &'static str, c: T| {}; + let f = |a: i32, b: &str, c: T| {}; let result: () = f(42, "", t); } "#]], diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 983ed3684a4f6..1db4f8ecd6bab 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -437,6 +437,7 @@ define_symbols! { rustc_safe_intrinsic, rustc_skip_array_during_method_dispatch, rustc_skip_during_method_dispatch, + rustc_force_inline, semitransparent, shl_assign, shl, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 1b940c70da66b..8a04bc7798f8b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1061,7 +1061,7 @@ fn main() { ), work_done_progress_params: Default::default(), }); - assert!(res.to_string().contains("&'static str")); + assert!(res.to_string().contains("&str")); let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( @@ -1070,7 +1070,7 @@ fn main() { ), work_done_progress_params: Default::default(), }); - assert!(res.to_string().contains("&'static str")); + assert!(res.to_string().contains("&str")); server.request::( GotoDefinitionParams { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7c3e7fea1bdef..696928b522f94 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -169,6 +169,17 @@ pub mod marker { // region:phantom_data #[lang = "phantom_data"] pub struct PhantomData; + + // region:clone + impl Clone for PhantomData { + fn clone(&self) -> Self { Self } + } + // endregion:clone + + // region:copy + impl Copy for PhantomData {} + // endregion:copy + // endregion:phantom_data // region:discriminant @@ -1147,7 +1158,7 @@ pub mod fmt { pub struct Error; pub type Result = crate::result::Result<(), Error>; - pub struct Formatter<'a>; + pub struct Formatter<'a>(&'a ()); pub struct DebugTuple; pub struct DebugStruct; impl Formatter<'_> { @@ -1620,6 +1631,12 @@ pub mod iter { { loop {} } + fn collect>(self) -> B + where + Self: Sized, + { + loop {} + } // endregion:iterators } impl Iterator for &mut I { @@ -1689,10 +1706,13 @@ pub mod iter { loop {} } } + pub trait FromIterator: Sized { + fn from_iter>(iter: T) -> Self; + } } - pub use self::collect::IntoIterator; + pub use self::collect::{IntoIterator, FromIterator}; } - pub use self::traits::{IntoIterator, Iterator}; + pub use self::traits::{IntoIterator, FromIterator, Iterator}; } // endregion:iterator @@ -1988,7 +2008,7 @@ pub mod prelude { convert::AsRef, // :as_ref convert::{From, Into, TryFrom, TryInto}, // :from default::Default, // :default - iter::{IntoIterator, Iterator}, // :iterator + iter::{IntoIterator, Iterator, FromIterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive marker::Copy, // :copy marker::Send, // :send diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index f91192b0076ba..0462835f0675a 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -235,6 +235,10 @@ impl TidyDocs { return; } + if is_ported_from_rustc(path, &["crates/hir-ty/src/next_solver"]) { + return; + } + let first_line = match text.lines().next() { Some(it) => it, None => return, @@ -290,6 +294,11 @@ fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { .any(|it| dirs_to_exclude.contains(&it)) } +fn is_ported_from_rustc(p: &Path, dirs_to_exclude: &[&str]) -> bool { + let p = p.strip_prefix(project_root()).unwrap(); + dirs_to_exclude.iter().any(|exclude| p.starts_with(exclude)) +} + #[derive(Default)] struct TidyMarks { hits: HashSet, From bf73aac8cbdfeff983dce44b883993be2ae89151 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 15 Sep 2025 18:34:02 +0200 Subject: [PATCH 0438/1900] compiletest: Make `./x test --test-args ...` work again It accidentally broke with a48c8e337d1. The intention of that commit was to keep existing behavior if `--exact` is not used, but it had a bug. This commit fixes that bug. --- src/tools/compiletest/src/executor.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index 37cc17351d5fe..c8e13d4457383 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -295,11 +295,14 @@ fn filter_tests(opts: &Config, tests: Vec) -> Vec let mut filtered = tests; let matches_filter = |test: &CollectedTest, filter_str: &str| { - let filterable_path = test.desc.filterable_path.as_str(); if opts.filter_exact { - filterable_path == filter_str + // When `--exact` is used we must use `filterable_path` to get + // reasonable filtering behavior. + test.desc.filterable_path.as_str() == filter_str } else { - filterable_path.contains(filter_str) + // For compatibility we use the name (which includes the full path) + // if `--exact` is not used. + test.desc.name.contains(filter_str) } }; From 487cdbc07d4bc73b8c52c7a35a99d2e0a64c6f55 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 16 Sep 2025 01:44:00 +0900 Subject: [PATCH 0439/1900] fix: More precise clause filtering for `explicit_*_predicates_of` --- .../crates/hir-ty/src/next_solver/interner.rs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7b6e8a1073d78..76d10f7dcc503 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1330,16 +1330,23 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::TraitId, ) -> EarlyBinder> { + let is_self = |ty: Ty<'db>| match ty.kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + _ => false, + }; + let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.0.into()) .iter() .filter(|p| match p.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(tr) => match tr.self_ty().kind() { - rustc_type_ir::TyKind::Param(param) => param.index == 0, - _ => false, - }, - _ => true, + // rustc has the following assertion: + // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608 + rustc_type_ir::ClauseKind::Trait(it) => is_self(it.self_ty()), + rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self(it.0), + rustc_type_ir::ClauseKind::Projection(it) => is_self(it.self_ty()), + rustc_type_ir::ClauseKind::HostEffect(it) => is_self(it.self_ty()), + _ => false, }) .cloned() .map(|p| (p, Span::dummy())) @@ -1367,7 +1374,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .generic_predicates_ns(def_id.try_into().unwrap()) .iter() .filter(|p| match p.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(tr) => is_self_or_assoc(tr.self_ty()), + rustc_type_ir::ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()), + rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0), + rustc_type_ir::ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()), + rustc_type_ir::ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()), + // FIXME: Not sure is this correct to allow other clauses but we might replace + // `generic_predicates_ns` query here with something closer to rustc's + // `implied_bounds_with_filter`, which is more granular lowering than this + // "lower at once and then filter" implementation. _ => true, }) .cloned() From c4da39cca328e1aaabb513f20d32c55bbbc49057 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 15 Sep 2025 18:48:57 +0200 Subject: [PATCH 0440/1900] Recognize canonical `?` pattern with `Result` --- clippy_lints/src/question_mark.rs | 21 +++++-- tests/ui/question_mark.fixed | 14 ++++- tests/ui/question_mark.rs | 26 ++++++++- tests/ui/question_mark.stderr | 92 ++++++++++++++++++++----------- 4 files changed, 113 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index de12a25b03dff..4aa100a50e053 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,9 +8,9 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, + is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -393,8 +393,8 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) - // check `...` is `val` from binding - && path_to_local_id(ret_expr, ok_val) + // check if `...` is `val` from binding or `val.into()` + && is_local_or_local_into(cx, ret_expr, ok_val) { true } else { @@ -417,6 +417,17 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A } } +/// Check if `expr` is `val` or `val.into()` +fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> bool { + let is_into_call = fn_def_id_with_node_args(cx, expr) + .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) + .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); + match expr.kind { + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), + _ => path_to_local_id(expr, val), + } +} + fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8d6f5fbadca56..ac81b324c2049 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -465,3 +463,15 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + some_result?; + + some_result?; + + some_result?; + + Ok(0) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index f13eee29c113c..b5866dac6b8f6 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -561,3 +559,27 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(err.into()), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(Into::into(err)), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(<&str as Into>::into(err)), + }; + + Ok(0) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d8ce4420aeeb6..1ecd936292e55 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:9:5 + --> tests/ui/question_mark.rs:7:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:55:9 + --> tests/ui/question_mark.rs:53:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:60:9 + --> tests/ui/question_mark.rs:58:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:65:17 + --> tests/ui/question_mark.rs:63:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:72:17 + --> tests/ui/question_mark.rs:70:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:90:9 + --> tests/ui/question_mark.rs:88:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:99:9 + --> tests/ui/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:108:9 + --> tests/ui/question_mark.rs:106:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:116:26 + --> tests/ui/question_mark.rs:114:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:127:17 + --> tests/ui/question_mark.rs:125:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:149:5 + --> tests/ui/question_mark.rs:147:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:154:16 + --> tests/ui/question_mark.rs:152:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:165:5 + --> tests/ui/question_mark.rs:163:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:171:5 + --> tests/ui/question_mark.rs:169:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:198:13 + --> tests/ui/question_mark.rs:196:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:201:5 + --> tests/ui/question_mark.rs:199:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:206:16 + --> tests/ui/question_mark.rs:204:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:212:5 + --> tests/ui/question_mark.rs:210:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:304:5 + --> tests/ui/question_mark.rs:302:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:312:5 + --> tests/ui/question_mark.rs:310:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:395:13 + --> tests/ui/question_mark.rs:393:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:456:5 + --> tests/ui/question_mark.rs:454:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:471:5 + --> tests/ui/question_mark.rs:469:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:481:5 + --> tests/ui/question_mark.rs:479:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:492:5 + --> tests/ui/question_mark.rs:490:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:496:5 + --> tests/ui/question_mark.rs:494:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:500:5 + --> tests/ui/question_mark.rs:498:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:522:5 + --> tests/ui/question_mark.rs:520:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:526:15 + --> tests/ui/question_mark.rs:524:15 | LL | let val = match arg { | _______________^ @@ -276,12 +276,42 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:536:5 + --> tests/ui/question_mark.rs:534:5 | LL | / let Some(a) = *a else { LL | | return None; LL | | }; | |______^ help: replace it with: `let a = (*a)?;` -error: aborting due to 30 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:566:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err.into()), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:572:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(Into::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:578:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(<&str as Into>::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: aborting due to 33 previous errors From c8663eec6a667ae6e7571297cb15192425311c33 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 29 Aug 2025 23:29:46 +0300 Subject: [PATCH 0441/1900] Introduce CoerceShared lang item and trait --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/marker.rs | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 67d2f15d41472..889b16a7a2bb6 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -441,6 +441,7 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); + CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6d..882abf7bcc977 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -679,6 +679,7 @@ symbols! { cmpxchg16b_target_feature, cmse_nonsecure_entry, coerce_pointee_validated, + coerce_shared, coerce_unsized, cold, cold_path, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index d03d7a43469a7..8541a5b91cdde 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1372,3 +1372,12 @@ pub trait CoercePointeeValidated { pub trait Reborrow { // Empty. } + +/// Allows reborrowable value to be reborrowed as shared, creating a copy of +/// that disables the source for writes for the lifetime of the copy. +#[lang = "coerce_shared"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait CoerceShared: Reborrow { + /// The type of this value when reborrowed as shared. + type Target: Copy; +} From fce8c13f77bcbebfea4461536be828d7136b8c1a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 29 Aug 2025 23:35:07 +0300 Subject: [PATCH 0442/1900] Add reborrow CoerceShared feature gate test --- .../feature-gate-reborrow-coerce-shared.rs | 3 +++ .../feature-gate-reborrow-coerce-shared.stderr | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs create mode 100644 tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs new file mode 100644 index 0000000000000..48a14959d8d64 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -0,0 +1,3 @@ +use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr new file mode 100644 index 0000000000000..c4c5e06778af3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 + | +LL | use std::marker::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 35bae9df1dfb164de7be95180a0982312128d559 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 30 Aug 2025 21:16:56 +0300 Subject: [PATCH 0443/1900] Introduce basic Reborrow tests --- tests/ui/reborrow/custom_mut.rs | 13 +++++++++ tests/ui/reborrow/custom_mut.stderr | 29 +++++++++++++++++++ tests/ui/reborrow/custom_mut_coerce_shared.rs | 28 ++++++++++++++++++ .../reborrow/custom_mut_coerce_shared.stderr | 19 ++++++++++++ tests/ui/reborrow/option_mut.rs | 7 +++++ tests/ui/reborrow/option_mut.stderr | 21 ++++++++++++++ tests/ui/reborrow/option_mut_coerce_shared.rs | 11 +++++++ .../reborrow/option_mut_coerce_shared.stderr | 23 +++++++++++++++ tests/ui/reborrow/pin_mut.rs | 10 +++++++ tests/ui/reborrow/pin_mut.stderr | 21 ++++++++++++++ tests/ui/reborrow/pin_mut_coerce_shared.rs | 14 +++++++++ .../ui/reborrow/pin_mut_coerce_shared.stderr | 19 ++++++++++++ 12 files changed, 215 insertions(+) create mode 100644 tests/ui/reborrow/custom_mut.rs create mode 100644 tests/ui/reborrow/custom_mut.stderr create mode 100644 tests/ui/reborrow/custom_mut_coerce_shared.rs create mode 100644 tests/ui/reborrow/custom_mut_coerce_shared.stderr create mode 100644 tests/ui/reborrow/option_mut.rs create mode 100644 tests/ui/reborrow/option_mut.stderr create mode 100644 tests/ui/reborrow/option_mut_coerce_shared.rs create mode 100644 tests/ui/reborrow/option_mut_coerce_shared.stderr create mode 100644 tests/ui/reborrow/pin_mut.rs create mode 100644 tests/ui/reborrow/pin_mut.stderr create mode 100644 tests/ui/reborrow/pin_mut_coerce_shared.rs create mode 100644 tests/ui/reborrow/pin_mut_coerce_shared.stderr diff --git a/tests/ui/reborrow/custom_mut.rs b/tests/ui/reborrow/custom_mut.rs new file mode 100644 index 0000000000000..b55a5e6faa3dc --- /dev/null +++ b/tests/ui/reborrow/custom_mut.rs @@ -0,0 +1,13 @@ +#![feature(reborrow)] +use std::marker::Reborrow; + +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} + +fn method(a: CustomMut<'_, ()>) {} + +fn main() { + let a = CustomMut(&mut ()); + let _ = method(a); + let _ = method(a); //~ERROR use of moved value: `a` +} diff --git a/tests/ui/reborrow/custom_mut.stderr b/tests/ui/reborrow/custom_mut.stderr new file mode 100644 index 0000000000000..3b3f47b62d6fa --- /dev/null +++ b/tests/ui/reborrow/custom_mut.stderr @@ -0,0 +1,29 @@ +error[E0382]: use of moved value: `a` + --> $DIR/custom_mut.rs:12:20 + | +LL | let a = CustomMut(&mut ()); + | - move occurs because `a` has type `CustomMut<'_, ()>`, which does not implement the `Copy` trait +LL | let _ = method(a); + | - value moved here +LL | let _ = method(a); + | ^ value used here after move + | +note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary + --> $DIR/custom_mut.rs:7:14 + | +LL | fn method(a: CustomMut<'_, ()>) {} + | ------ ^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function +note: if `CustomMut<'_, ()>` implemented `Clone`, you could clone the value + --> $DIR/custom_mut.rs:4:1 + | +LL | struct CustomMut<'a, T>(&'a mut T); + | ^^^^^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | let _ = method(a); + | - you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs new file mode 100644 index 0000000000000..b292b5b073ce8 --- /dev/null +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -0,0 +1,28 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, CoerceShared}; + +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared for CustomMut<'a, T> { + type Target = CustomRef<'a, T>; +} + +struct CustomRef<'a, T>(&'a T); + +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn method(a: CustomRef<'_, ()>) {} //~NOTE function defined here + +fn main() { + let a = CustomMut(&mut ()); + method(a); + //~^ ERROR mismatched types + //~| NOTE expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` + //~| NOTE arguments to this function are incorrect + //~| NOTE expected struct `CustomRef<'_, ()>` +} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr new file mode 100644 index 0000000000000..508651badc0a4 --- /dev/null +++ b/tests/ui/reborrow/custom_mut_coerce_shared.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/custom_mut_coerce_shared.rs:23:12 + | +LL | method(a); + | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` + | | + | arguments to this function are incorrect + | + = note: expected struct `CustomRef<'_, ()>` + found struct `CustomMut<'_, ()>` +note: function defined here + --> $DIR/custom_mut_coerce_shared.rs:19:4 + | +LL | fn method(a: CustomRef<'_, ()>) {} + | ^^^^^^ -------------------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reborrow/option_mut.rs b/tests/ui/reborrow/option_mut.rs new file mode 100644 index 0000000000000..60b7145a7b2e3 --- /dev/null +++ b/tests/ui/reborrow/option_mut.rs @@ -0,0 +1,7 @@ +fn method(a: Option<& mut ()>) {} + +fn main() { + let a = Some(&mut ()); + let _ = method(a); + let _ = method(a); //~ERROR use of moved value: `a` +} diff --git a/tests/ui/reborrow/option_mut.stderr b/tests/ui/reborrow/option_mut.stderr new file mode 100644 index 0000000000000..497319d2e9035 --- /dev/null +++ b/tests/ui/reborrow/option_mut.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `a` + --> $DIR/option_mut.rs:6:20 + | +LL | let a = Some(&mut ()); + | - move occurs because `a` has type `Option<&mut ()>`, which does not implement the `Copy` trait +LL | let _ = method(a); + | - value moved here +LL | let _ = method(a); + | ^ value used here after move + | +note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary + --> $DIR/option_mut.rs:1:14 + | +LL | fn method(a: Option<& mut ()>) {} + | ------ ^^^^^^^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/option_mut_coerce_shared.rs b/tests/ui/reborrow/option_mut_coerce_shared.rs new file mode 100644 index 0000000000000..95d33ed94dd71 --- /dev/null +++ b/tests/ui/reborrow/option_mut_coerce_shared.rs @@ -0,0 +1,11 @@ +fn method(a: Option<&()>) {} //~NOTE function defined here + +fn main() { + let a = Some(&mut ()); + method(a); + //~^ ERROR mismatched types + //~| NOTE arguments to this function are incorrect + //~| NOTE types differ in mutability + //~| NOTE expected enum `Option<&()>` + //~| NOTE found enum `Option<&mut ()>` +} diff --git a/tests/ui/reborrow/option_mut_coerce_shared.stderr b/tests/ui/reborrow/option_mut_coerce_shared.stderr new file mode 100644 index 0000000000000..6ca1a2374610e --- /dev/null +++ b/tests/ui/reborrow/option_mut_coerce_shared.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/option_mut_coerce_shared.rs:5:12 + | +LL | method(a); + | ------ ^ types differ in mutability + | | + | arguments to this function are incorrect + | + = note: expected enum `Option<&()>` + found enum `Option<&mut ()>` +note: function defined here + --> $DIR/option_mut_coerce_shared.rs:1:4 + | +LL | fn method(a: Option<&()>) {} + | ^^^^^^ -------------- +help: try using `.as_deref()` to convert `Option<&mut ()>` to `Option<&()>` + | +LL | method(a.as_deref()); + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reborrow/pin_mut.rs b/tests/ui/reborrow/pin_mut.rs new file mode 100644 index 0000000000000..966106969943a --- /dev/null +++ b/tests/ui/reborrow/pin_mut.rs @@ -0,0 +1,10 @@ +use std::pin::Pin; + +fn method(a: Pin<& mut ()>) {} + +fn main() { + let a = &mut (); + let a = Pin::new(a); + let _ = method(a); + let _ = method(a); //~ERROR use of moved value: `a` +} diff --git a/tests/ui/reborrow/pin_mut.stderr b/tests/ui/reborrow/pin_mut.stderr new file mode 100644 index 0000000000000..8a10e2d9af269 --- /dev/null +++ b/tests/ui/reborrow/pin_mut.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `a` + --> $DIR/pin_mut.rs:9:20 + | +LL | let a = Pin::new(a); + | - move occurs because `a` has type `Pin<&mut ()>`, which does not implement the `Copy` trait +LL | let _ = method(a); + | - value moved here +LL | let _ = method(a); + | ^ value used here after move + | +note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary + --> $DIR/pin_mut.rs:3:14 + | +LL | fn method(a: Pin<& mut ()>) {} + | ------ ^^^^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/pin_mut_coerce_shared.rs b/tests/ui/reborrow/pin_mut_coerce_shared.rs new file mode 100644 index 0000000000000..fd529195fe01e --- /dev/null +++ b/tests/ui/reborrow/pin_mut_coerce_shared.rs @@ -0,0 +1,14 @@ +use std::pin::Pin; + +fn method(a: Pin<&()>) {} //~NOTE function defined here + +fn main() { + let a = &mut (); + let a = Pin::new(a); + method(a); + //~^ ERROR mismatched types + //~| NOTE arguments to this function are incorrect + //~| NOTE types differ in mutability + //~| NOTE expected struct `Pin<&()>` + //~| NOTE found struct `Pin<&mut ()>` +} diff --git a/tests/ui/reborrow/pin_mut_coerce_shared.stderr b/tests/ui/reborrow/pin_mut_coerce_shared.stderr new file mode 100644 index 0000000000000..74ecf4de4c785 --- /dev/null +++ b/tests/ui/reborrow/pin_mut_coerce_shared.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/pin_mut_coerce_shared.rs:8:12 + | +LL | method(a); + | ------ ^ types differ in mutability + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin<&()>` + found struct `Pin<&mut ()>` +note: function defined here + --> $DIR/pin_mut_coerce_shared.rs:3:4 + | +LL | fn method(a: Pin<&()>) {} + | ^^^^^^ ----------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 2adaa5dae2b9b63a50fa0200650aeee918cb76fb Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 15 Sep 2025 10:33:39 -0700 Subject: [PATCH 0444/1900] Bump rustfix 0.8.1 -> 0.8.7 This commit can be replicated by running `cargo update -p rustfix --precise 0.8.7 && x test ui --bless`. --- The reasons this affects UI tests is as follows: - The UI test suite runs rustc with `-Z deduplicate-diagnostics=no --error-format=json`, which means that rustc emits multiple errors containing identical suggestions. That caused the weird-looking code that had multiple `X: Copy` suggestions. - Those suggestions are interpreted not by rustc itself, but by the `rustfix` library, maintained by cargo but published as a separate crates.io library and used by compiletest. - Sometime between rustfix 0.8.1 and 0.8.7 (probably in cargo 14747, but it's hard to tell because rustfix's versioning doesn't match cargo's), rustfix got smarter and stopped applying duplicate suggestions. Update rustfix to match cargo's behavior. Ideally, we would always share a version of rustfix between cargo and rustc (perhaps with a path dependency?), to make sure we are testing the behavior we ship. But for now, just manually update it to match. Note that the latest version of rustfix published to crates.io is 0.9.1, not 0.8.7. But 0.9.1 is not the version used in cargo, which is 0.9.3. Rather than trying to match versions exactly, I just updated rustfix to the latest in the 0.8 branch. --- Cargo.lock | 4 ++-- .../associated-types/associated-types-for-unimpl-trait.fixed | 2 +- tests/ui/lifetimes/issue-105507.fixed | 2 +- tests/ui/parser/expr-as-stmt.fixed | 4 ++-- .../ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed | 2 +- .../ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed | 2 +- tests/ui/suggestions/trait-impl-bound-suggestions.fixed | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4677d34d2a630..d39cfefea0c77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4817,9 +4817,9 @@ dependencies = [ [[package]] name = "rustfix" -version = "0.8.1" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81864b097046da5df3758fdc6e4822bbb70afa06317e8ca45ea1b51cb8c5e5a4" +checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a" dependencies = [ "serde", "serde_json", diff --git a/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed b/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed index bce6148f9e199..ae4d0107aee52 100644 --- a/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed +++ b/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed @@ -8,7 +8,7 @@ trait Get { } trait Other { - fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get, Self: Get {} + fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get {} //~^ ERROR the trait bound `Self: Get` is not satisfied //~| ERROR the trait bound `Self: Get` is not satisfied } diff --git a/tests/ui/lifetimes/issue-105507.fixed b/tests/ui/lifetimes/issue-105507.fixed index 46d4f14a2457e..a3c4e5784b872 100644 --- a/tests/ui/lifetimes/issue-105507.fixed +++ b/tests/ui/lifetimes/issue-105507.fixed @@ -31,7 +31,7 @@ impl ProjectedMyTrait for T fn require_trait(_: T) {} -fn foo(wrap: Wrapper<'_, Option>, wrap1: Wrapper<'_, Option>) { +fn foo(wrap: Wrapper<'_, Option>, wrap1: Wrapper<'_, Option>) { //~^ HELP consider restricting the type parameter to the `'static` lifetime //~| HELP consider restricting the type parameter to the `'static` lifetime require_trait(wrap); diff --git a/tests/ui/parser/expr-as-stmt.fixed b/tests/ui/parser/expr-as-stmt.fixed index bfae55047ed1b..b3a491200ed10 100644 --- a/tests/ui/parser/expr-as-stmt.fixed +++ b/tests/ui/parser/expr-as-stmt.fixed @@ -66,7 +66,7 @@ fn asteroids() -> impl FnOnce() -> bool { // https://github.com/rust-lang/rust/issues/105179 fn r#match() -> i32 { - ((match () { () => 1 })) + match () { () => 1 } //~ ERROR expected expression, found `+` + (match () { () => 1 }) + match () { () => 1 } //~ ERROR expected expression, found `+` //~^ ERROR mismatched types } @@ -82,7 +82,7 @@ fn matches() -> bool { (match () { _ => true }) && match () { _ => true }; //~ ERROR mismatched types //~^ ERROR expected `;`, found keyword `match` (match () { _ => true }) && true; //~ ERROR mismatched types - ((match () { _ => true })) && true //~ ERROR mismatched types + (match () { _ => true }) && true //~ ERROR mismatched types //~^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed index 99433f7332042..8a2be310e0d81 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed @@ -8,7 +8,7 @@ pub struct Vector2 { } #[derive(Debug, Copy, Clone)] -pub struct AABB { +pub struct AABB { pub loc: Vector2, //~ ERROR the trait bound `K: Copy` is not satisfied //~^ ERROR the trait bound `K: Copy` is not satisfied //~| ERROR the trait bound `K: Copy` is not satisfied diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed index 6da3e351ffbdd..74df1d7c7cfdd 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed @@ -8,7 +8,7 @@ pub struct Vector2{ } #[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type -pub struct AABB{ +pub struct AABB{ pub loc: Vector2, //~ ERROR `K` doesn't implement `Debug` //~^ ERROR `K` doesn't implement `Debug` pub size: Vector2 //~ ERROR `K` doesn't implement `Debug` diff --git a/tests/ui/suggestions/trait-impl-bound-suggestions.fixed b/tests/ui/suggestions/trait-impl-bound-suggestions.fixed index 9d3168f5acd68..49793b4b6f47e 100644 --- a/tests/ui/suggestions/trait-impl-bound-suggestions.fixed +++ b/tests/ui/suggestions/trait-impl-bound-suggestions.fixed @@ -10,7 +10,7 @@ struct ConstrainedStruct { } #[allow(dead_code)] -trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker::Copy, X: std::marker::Copy { +trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker::Copy { fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct { //~^ ERROR the trait bound `X: Copy` is not satisfied ConstrainedStruct { x } @@ -20,7 +20,7 @@ trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker:: // Regression test for #120838 #[allow(dead_code)] -trait InsufficientlyConstrainedGenericWithEmptyWhere where Self: Sized, X: std::marker::Copy, X: std::marker::Copy { +trait InsufficientlyConstrainedGenericWithEmptyWhere where Self: Sized, X: std::marker::Copy { fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct { //~^ ERROR the trait bound `X: Copy` is not satisfied ConstrainedStruct { x } From c4a87eb62cd74e31e5d3889741a76a4bb2a48ed6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 15 Sep 2025 20:32:06 +0300 Subject: [PATCH 0445/1900] fix: Move CoerceShared into ops --- library/core/src/marker.rs | 9 --------- library/core/src/ops/mod.rs | 3 +++ library/core/src/ops/reborrow.rs | 10 ++++++++++ .../feature-gate-reborrow-coerce-shared.rs | 2 +- .../feature-gate-reborrow-coerce-shared.stderr | 4 ++-- tests/ui/reborrow/custom_mut_coerce_shared.rs | 3 ++- tests/ui/reborrow/custom_mut_coerce_shared.stderr | 4 ++-- 7 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 library/core/src/ops/reborrow.rs diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 8541a5b91cdde..d03d7a43469a7 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1372,12 +1372,3 @@ pub trait CoercePointeeValidated { pub trait Reborrow { // Empty. } - -/// Allows reborrowable value to be reborrowed as shared, creating a copy of -/// that disables the source for writes for the lifetime of the copy. -#[lang = "coerce_shared"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait CoerceShared: Reborrow { - /// The type of this value when reborrowed as shared. - type Target: Copy; -} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 87dd873fdb57d..9814f5d5795c6 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -149,6 +149,7 @@ mod function; mod index; mod index_range; mod range; +mod reborrow; mod try_trait; mod unsize; @@ -189,6 +190,8 @@ pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; +#[unstable(feature = "reborrow", issue = "145612")] +pub use self::reborrow::CoerceShared; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs new file mode 100644 index 0000000000000..90288f766d502 --- /dev/null +++ b/library/core/src/ops/reborrow.rs @@ -0,0 +1,10 @@ +use crate::marker::Reborrow; + +/// Allows reborrowable value to be reborrowed as shared, creating a copy +/// that disables the source for writes for the lifetime of the copy. +#[lang = "coerce_shared"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait CoerceShared: Reborrow { + /// The type of this value when reborrowed as shared. + type Target: Copy; +} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs index 48a14959d8d64..c8ca453708912 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -1,3 +1,3 @@ -use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +use std::ops::CoerceShared; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr index c4c5e06778af3..dbbbcdf2fd57d 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 | -LL | use std::marker::CoerceShared; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::ops::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index b292b5b073ce8..28d802aabffe5 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,5 +1,6 @@ #![feature(reborrow)] -use std::marker::{Reborrow, CoerceShared}; +use std::marker::Reborrow; +use std::ops::CoerceShared; struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr index 508651badc0a4..90d8a98160543 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.stderr +++ b/tests/ui/reborrow/custom_mut_coerce_shared.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/custom_mut_coerce_shared.rs:23:12 + --> $DIR/custom_mut_coerce_shared.rs:24:12 | LL | method(a); | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` @@ -9,7 +9,7 @@ LL | method(a); = note: expected struct `CustomRef<'_, ()>` found struct `CustomMut<'_, ()>` note: function defined here - --> $DIR/custom_mut_coerce_shared.rs:19:4 + --> $DIR/custom_mut_coerce_shared.rs:20:4 | LL | fn method(a: CustomRef<'_, ()>) {} | ^^^^^^ -------------------- From 001f8e3e1ad9b5739c6ec2f63c7b52ebd5d68006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 20:22:21 +0200 Subject: [PATCH 0446/1900] Update src/tests/ci.md Co-authored-by: Ralf Jung --- src/doc/rustc-dev-guide/src/tests/ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index a32edd7240117..d9fc2324d8bf6 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -151,7 +151,7 @@ which can be used in one of two ways: Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using -glob patterns in the PR description, you can wrap them in backticks (`` ` ``) to avoid GitHub rendering +glob patterns in the PR description, you can (but do not have to) wrap them in backticks (`` ` ``) to avoid GitHub rendering the pattern as Markdown if it contains e.g. an asterisk. Note that this escaping will not work when using the `@bors jobs=` parameter. From b8cb80cf80572e9d287445af16608b68ec436099 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 13:38:04 -0500 Subject: [PATCH 0447/1900] improve internal bootstrap docs --- src/bootstrap/README.md | 15 +++++++++++++++ src/bootstrap/src/core/build_steps/tool.rs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 2965174b45bcc..12e09cb07dbb2 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -63,6 +63,21 @@ build/ debug/ release/ + # Build directory for various tools like `typos` that are only ever + # built for the host system, and always with stage0 cargo. + misc-tools/ + bin/ + target/ + + # Directory where js dependencies like tsc and eslint are stored. + node_modules/ + .bin/ + + # Copy of package.json and package-lock.json, because npm requires these + # to be in the same directory as `node_modules`. + package.json + package-lock.json + # Output of the dist-related steps like dist-std, dist-rustc, and dist-docs dist/ diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index dcc4898cae16a..4f096d50ea5c3 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -394,6 +394,9 @@ macro_rules! bootstrap_tool { } impl<'a> Builder<'a> { + /// Ensure a tool is built, then get the path to its executable. + /// + /// The actual building, if any, will be handled via [`ToolBuild`]. pub fn tool_exe(&self, tool: Tool) -> PathBuf { match tool { $(Tool::$name => @@ -1552,6 +1555,8 @@ pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f impl Builder<'_> { /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for /// `host`. + /// + /// This also ensures that the given tool is built (using [`ToolBuild`]). pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand { let mut cmd = command(self.tool_exe(tool)); let compiler = self.compiler(0, self.config.host_target); From 29ca09be09a88f359adc7879e2592f36f6c74a64 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 14:42:44 -0500 Subject: [PATCH 0448/1900] bootstrap: lower verbosity of cargo to one less than bootstrap's the main thing this does is eliminate the "Fresh ..." output when `--verbose` is only passed once. --- src/bootstrap/bootstrap.py | 3 ++- src/bootstrap/src/core/build_steps/test.rs | 3 --- src/bootstrap/src/core/builder/cargo.rs | 7 ++++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2ece53eb0cc99..41c8ef17f0b57 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1145,7 +1145,8 @@ def build_bootstrap_cmd(self, env): os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"), "-Zroot-dir=" + self.rust_root, ] - args.extend("--verbose" for _ in range(self.verbose)) + # verbose cargo output is very noisy, so only enable it with -vv + args.extend("--verbose" for _ in range(self.verbose - 1)) if "BOOTSTRAP_TRACING" in env: args.append("--features=tracing") diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 723ba80eaf827..e7f5879b5f51a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3418,9 +3418,6 @@ impl Step for TierCheck { ); cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md")); cargo.arg(builder.rustc(self.test_compiler)); - if builder.is_verbose() { - cargo.arg("--verbose"); - } let _guard = builder.msg_test( "platform support check", diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 924bb4adb42d0..8e65ec7ce50ad 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1133,7 +1133,7 @@ impl Builder<'_> { cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); } - if self.is_verbose() { + if self.is_verbose_than(1) { // This provides very useful logs especially when debugging build cache-related stuff. cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); } @@ -1275,8 +1275,9 @@ impl Builder<'_> { cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); } - for _ in 0..self.verbosity { - cargo.arg("-v"); + // verbose cargo output is very noisy, so only enable it with -vv + for _ in 0..self.verbosity.saturating_sub(1) { + cargo.arg("--verbose"); } match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { From fa7e474f9ca755ac102debcf6cd0ce77313ee0bf Mon Sep 17 00:00:00 2001 From: ash Date: Mon, 15 Sep 2025 13:55:20 -0600 Subject: [PATCH 0449/1900] remove FIXME from `has_significant_drop`, replaced with checking non_region_infer --- compiler/rustc_middle/src/ty/util.rs | 9 +-------- tests/ui/type-inference/has_sigdrop.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 tests/ui/type-inference/has_sigdrop.rs diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 029586a9c554c..b79b67c5927b0 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1359,6 +1359,7 @@ impl<'tcx> Ty<'tcx> { /// 2229 drop reorder migration analysis. #[inline] pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + assert!(!self.has_non_region_infer()); // Avoid querying in simple cases. match needs_drop_components(tcx, self) { Err(AlwaysRequiresDrop) => true, @@ -1371,14 +1372,6 @@ impl<'tcx> Ty<'tcx> { _ => self, }; - // FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference - // context, or *something* like that, but for now just avoid passing inference - // variables to queries that can't cope with them. Instead, conservatively - // return "true" (may change drop order). - if query_ty.has_infer() { - return true; - } - // This doesn't depend on regions, so try to minimize distinct // query keys used. let erased = tcx.normalize_erasing_regions(typing_env, query_ty); diff --git a/tests/ui/type-inference/has_sigdrop.rs b/tests/ui/type-inference/has_sigdrop.rs new file mode 100644 index 0000000000000..c3d835cfe16f8 --- /dev/null +++ b/tests/ui/type-inference/has_sigdrop.rs @@ -0,0 +1,18 @@ +//@ run-pass +// Inference, canonicalization, and significant drops should work nicely together. +// Related issue: #86868 + +#[clippy::has_significant_drop] +struct DropGuy {} + +fn creator() -> DropGuy { + DropGuy {} +} + +fn dropper() { + let _ = creator(); +} + +fn main() { + dropper(); +} From d58061e6131f6141985faadb6794af540f81b1db Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 01:22:10 +0000 Subject: [PATCH 0450/1900] Restrict simple assignment condition. --- compiler/rustc_mir_transform/src/dest_prop.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b9ab0a9feee65..c57483a681140 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -596,8 +596,14 @@ fn save_as_intervals<'tcx>( // as behaving so by default. // We make an exception for simple assignments `_a.stuff = {copy|move} _b.stuff`, // as marking `_b` live here would prevent unification. - let is_simple_assignment = - matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); + let is_simple_assignment = match stmt.kind { + StatementKind::Assign(box ( + lhs, + Rvalue::CopyForDeref(rhs) + | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) => lhs.projection == rhs.projection, + _ => false, + }; VisitPlacesWith(|place: Place<'tcx>, ctxt| { if let Some(relevant) = relevant.shrink[place.local] { match DefUse::for_place(place, ctxt) { From 9e2f9edd7f79c1e11339277bfb83a0c888740438 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 15 Sep 2025 21:43:13 -0400 Subject: [PATCH 0451/1900] =?UTF-8?q?Don=E2=80=99t=20suggest=20foreign=20`?= =?UTF-8?q?doc(hidden)`=20types=20in=20E0277=20diagnostics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traits/fulfillment_errors.rs | 17 ++++++- tests/ui/proc-macro/quote/not-quotable.stderr | 4 +- .../ui/suggestions/auxiliary/hidden-struct.rs | 26 +++++++++-- .../dont-suggest-foreign-doc-hidden.rs | 18 ++++++-- .../dont-suggest-foreign-doc-hidden.stderr | 44 ++++++++++++++++--- 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 149f5e638b1ad..dff846cfc0af3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1894,6 +1894,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { + let parent_map = self.tcx.visible_parent_map(()); let alternative_candidates = |def_id: DefId| { let mut impl_candidates: Vec<_> = self .tcx @@ -1918,7 +1919,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + let mut did = def.did(); + if self.tcx.visibility(did).is_accessible_from(body_def_id, self.tcx) { + // don't suggest foreign `#[doc(hidden)]` types + if !did.is_local() { + while let Some(parent) = parent_map.get(&did) { + if self.tcx.is_doc_hidden(did) { + return false; + } + did = *parent; + } + } + true + } else { + false + } } else { true } diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr index d1c3d06f2b661..62a02638e548b 100644 --- a/tests/ui/proc-macro/quote/not-quotable.stderr +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -15,8 +15,8 @@ LL | let _ = quote! { $ip }; Cow<'_, T> Option Rc - RepInterp - and 25 others + bool + and 24 others error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/auxiliary/hidden-struct.rs b/tests/ui/suggestions/auxiliary/hidden-struct.rs index 30d69acac2097..1f495a9f2224a 100644 --- a/tests/ui/suggestions/auxiliary/hidden-struct.rs +++ b/tests/ui/suggestions/auxiliary/hidden-struct.rs @@ -1,3 +1,5 @@ +// `Foo` and `Bar` should not be suggested in diagnostics of dependents + #[doc(hidden)] pub mod hidden { pub struct Foo; @@ -5,13 +7,29 @@ pub mod hidden { pub mod hidden1 { #[doc(hidden)] - pub struct Foo; + pub struct Bar; } +// `Baz` and `Quux` *should* be suggested in diagnostics of dependents #[doc(hidden)] -pub(crate) mod hidden2 { - pub struct Bar; +pub mod hidden2 { + pub struct Baz; +} + +pub use hidden2::Baz; + +#[doc(hidden)] +pub(crate) mod hidden3 { + pub struct Quux; } -pub use hidden2::Bar; +pub use hidden3::Quux; + +pub trait Marker {} + +impl Marker for Option {} +impl Marker for hidden::Foo {} +impl Marker for hidden1::Bar {} +impl Marker for Baz {} +impl Marker for Quux {} diff --git a/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.rs b/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.rs index 281975dcc2f3b..a83e496f2703d 100644 --- a/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.rs +++ b/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.rs @@ -1,5 +1,4 @@ //@ aux-build:hidden-struct.rs -//@ compile-flags: --crate-type lib extern crate hidden_struct; @@ -9,7 +8,20 @@ mod local { } pub fn test(_: Foo) {} -//~^ ERROR cannot find type `Foo` in this scope +//~^ ERROR [E0412] pub fn test2(_: Bar) {} -//~^ ERROR cannot find type `Bar` in this scope +//~^ ERROR [E0412] + +pub fn test3(_: Baz) {} +//~^ ERROR [E0412] + +pub fn test4(_: Quux) {} +//~^ ERROR [E0412] + +fn test5() {} + +fn main() { + test5::(); + //~^ ERROR [E0277] +} diff --git a/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.stderr b/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.stderr index 7fb4d95ff9bf5..7036708756d6c 100644 --- a/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.stderr +++ b/tests/ui/suggestions/dont-suggest-foreign-doc-hidden.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Foo` in this scope - --> $DIR/dont-suggest-foreign-doc-hidden.rs:11:16 + --> $DIR/dont-suggest-foreign-doc-hidden.rs:10:16 | LL | pub fn test(_: Foo) {} | ^^^ not found in this scope @@ -10,16 +10,50 @@ LL + use local::Foo; | error[E0412]: cannot find type `Bar` in this scope - --> $DIR/dont-suggest-foreign-doc-hidden.rs:14:17 + --> $DIR/dont-suggest-foreign-doc-hidden.rs:13:17 | LL | pub fn test2(_: Bar) {} | ^^^ not found in this scope + +error[E0412]: cannot find type `Baz` in this scope + --> $DIR/dont-suggest-foreign-doc-hidden.rs:16:17 + | +LL | pub fn test3(_: Baz) {} + | ^^^ not found in this scope | help: consider importing this struct | -LL + use hidden_struct::Bar; +LL + use hidden_struct::Baz; + | + +error[E0412]: cannot find type `Quux` in this scope + --> $DIR/dont-suggest-foreign-doc-hidden.rs:19:17 + | +LL | pub fn test4(_: Quux) {} + | ^^^^ not found in this scope + | +help: consider importing this struct + | +LL + use hidden_struct::Quux; + | + +error[E0277]: the trait bound `i32: Marker` is not satisfied + --> $DIR/dont-suggest-foreign-doc-hidden.rs:25:13 + | +LL | test5::(); + | ^^^ the trait `Marker` is not implemented for `i32` + | + = help: the following other types implement trait `Marker`: + Baz + Option + Quux +note: required by a bound in `test5` + --> $DIR/dont-suggest-foreign-doc-hidden.rs:22:13 | +LL | fn test5() {} + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test5` -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0412`. +Some errors have detailed explanations: E0277, E0412. +For more information about an error, try `rustc --explain E0277`. From 181bd119d07022db19b184ac23a796daa0e45246 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 5 Sep 2025 08:25:28 +0200 Subject: [PATCH 0452/1900] fix: Fix expand macro recursively not working correctly for nested macro calls --- .../crates/ide/src/expand_macro.rs | 95 ++++++++++--------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index ad84eacfb3e88..094a4a7036c40 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,5 +1,5 @@ use hir::db::ExpandDatabase; -use hir::{ExpandResult, InFile, InRealFile, Semantics}; +use hir::{ExpandResult, InFile, Semantics}; use ide_db::{ FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, @@ -87,52 +87,55 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< return derive; } - let mut anc = sema - .descend_token_into_include_expansion(InRealFile::new(file_id, tok)) - .value - .parent_ancestors(); - let mut span_map = SpanMap::empty(); - let mut error = String::new(); - let (name, expanded, kind) = loop { - let node = anc.next()?; - - if let Some(item) = ast::Item::cast(node.clone()) - && let Some(def) = sema.resolve_attr_macro_call(&item) - { - break ( - def.name(db).display(db, file_id.edition(db)).to_string(), - expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, - SyntaxKind::MACRO_ITEMS, - ); - } - if let Some(mac) = ast::MacroCall::cast(node) { - let mut name = mac.path()?.segment()?.name_ref()?.to_string(); - name.push('!'); - let syntax_kind = - mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); - break ( - name, - expand_macro_recur( - &sema, - &ast::Item::MacroCall(mac), - &mut error, - &mut span_map, - TextSize::new(0), - )?, - syntax_kind, - ); - } - }; + let syntax_token = sema.descend_into_macros_exact(tok); + 'tokens: for syntax_token in syntax_token { + let mut anc = syntax_token.parent_ancestors(); + let mut span_map = SpanMap::empty(); + let mut error = String::new(); + let (name, expanded, kind) = loop { + let Some(node) = anc.next() else { + continue 'tokens; + }; + + if let Some(item) = ast::Item::cast(node.clone()) + && let Some(def) = sema.resolve_attr_macro_call(&item) + { + break ( + def.name(db).display(db, file_id.edition(db)).to_string(), + expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, + SyntaxKind::MACRO_ITEMS, + ); + } + if let Some(mac) = ast::MacroCall::cast(node) { + let mut name = mac.path()?.segment()?.name_ref()?.to_string(); + name.push('!'); + let syntax_kind = + mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); + break ( + name, + expand_macro_recur( + &sema, + &ast::Item::MacroCall(mac), + &mut error, + &mut span_map, + TextSize::new(0), + )?, + syntax_kind, + ); + } + }; - // FIXME: - // macro expansion may lose all white space information - // But we hope someday we can use ra_fmt for that - let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate); + // FIXME: + // macro expansion may lose all white space information + // But we hope someday we can use ra_fmt for that + let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate); - if !error.is_empty() { - expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n")); + if !error.is_empty() { + expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n")); + } + return Some(ExpandedMacro { name, expansion }); } - Some(ExpandedMacro { name, expansion }) + None } fn expand_macro_recur( @@ -752,8 +755,8 @@ fn test() { } "#, expect![[r#" - my_concat! - "<>hi""#]], + concat! + "<>""#]], ); } From 8a9ec5dd0dbf81e6337fb272aa0b90cf6e8d5655 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 23 Aug 2025 09:00:35 +0200 Subject: [PATCH 0453/1900] fix: Only compute unstable paths on nightly toolchains for IDE features --- .../crates/hir-def/src/find_path.rs | 8 ++-- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-ty/src/display.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +-- .../rust-analyzer/crates/hir/src/semantics.rs | 7 ++++ .../crates/hir/src/term_search/expr.rs | 10 ++--- .../crates/ide-assists/src/assist_config.rs | 18 +++++++-- .../src/handlers/add_missing_match_arms.rs | 7 ++-- .../src/handlers/convert_bool_to_enum.rs | 37 ++++++++++--------- .../src/handlers/convert_into_to_from.rs | 2 +- .../convert_tuple_return_type_to_struct.rs | 3 +- .../handlers/destructure_struct_binding.rs | 3 +- .../src/handlers/extract_function.rs | 3 +- .../extract_struct_from_enum_variant.rs | 3 +- .../src/handlers/generate_deref.rs | 8 ++-- .../ide-assists/src/handlers/generate_new.rs | 3 +- .../generate_single_field_struct_from.rs | 7 +--- .../src/handlers/qualify_method_call.rs | 3 +- .../replace_derive_with_manual_impl.rs | 3 +- .../replace_qualified_name_with_use.rs | 9 ++--- .../ide-assists/src/handlers/term_search.rs | 2 +- .../src/handlers/toggle_async_sugar.rs | 7 +--- .../crates/ide-completion/src/completions.rs | 2 +- .../ide-completion/src/completions/expr.rs | 4 +- .../src/completions/flyimport.rs | 6 +-- .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-completion/src/config.rs | 19 ++++++++-- .../crates/ide-completion/src/render.rs | 2 +- .../crates/ide-completion/src/snippet.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 21 ++++++++++- .../crates/ide-db/src/path_transform.rs | 10 ++--- .../src/handlers/json_is_not_rust.rs | 4 +- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/typed_hole.rs | 4 +- .../crates/ide-ssr/src/matching.rs | 4 +- .../rust-analyzer/src/cli/analysis_stats.rs | 6 +-- 36 files changed, 144 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index faa0ef8ceec7b..e8a6ebcffa0a5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -12,7 +12,7 @@ use intern::sym; use rustc_hash::FxHashSet; use crate::{ - ImportPathConfig, ModuleDefId, ModuleId, + FindPathConfig, ModuleDefId, ModuleId, db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, @@ -27,7 +27,7 @@ pub fn find_path( from: ModuleId, mut prefix_kind: PrefixKind, ignore_local_imports: bool, - mut cfg: ImportPathConfig, + mut cfg: FindPathConfig, ) -> Option { let _p = tracing::info_span!("find_path").entered(); @@ -96,7 +96,7 @@ impl PrefixKind { struct FindPathCtx<'db> { db: &'db dyn DefDatabase, prefix: PrefixKind, - cfg: ImportPathConfig, + cfg: FindPathConfig, ignore_local_imports: bool, is_std_item: bool, from: ModuleId, @@ -718,7 +718,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, + FindPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 1f5b1e023702f..301d4cca0666c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -101,7 +101,7 @@ use crate::{ type FxIndexMap = indexmap::IndexMap; /// A wrapper around three booleans #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] -pub struct ImportPathConfig { +pub struct FindPathConfig { /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate /// over the std. pub prefer_no_std: bool, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ae0113fcbd7f0..f3691b2e4f7e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,7 +11,7 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GeneralConstId, GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, + FindPathConfig, GeneralConstId, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, @@ -1433,7 +1433,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { PrefixKind::Plain, false, // FIXME: no_std Cfg? - ImportPathConfig { + FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f03f542e5bce3..4d1d8e2b15a9e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -130,7 +130,7 @@ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ Complete, - ImportPathConfig, + FindPathConfig, attr::{AttrSourceMap, Attrs, AttrsWithOwner}, find_path::PrefixKind, import_map, @@ -957,7 +957,7 @@ impl Module { self, db: &dyn DefDatabase, item: impl Into, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { hir_def::find_path::find_path( db, @@ -976,7 +976,7 @@ impl Module { db: &dyn DefDatabase, item: impl Into, prefix_kind: PrefixKind, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6d6e08100b044..84f01da72f069 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -302,6 +302,13 @@ impl Semantics<'_, DB> { self.imp.hir_file_to_module_defs(file.into()) } + pub fn is_nightly(&self, krate: Crate) -> bool { + let toolchain = self.db.toolchain_channel(krate.into()); + // `toolchain == None` means we're in some detached files. Since we have no information on + // the toolchain being used, let's just allow unstable items to be listed. + matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None) + } + pub fn to_adt_def(&self, a: &ast::Adt) -> Option { self.imp.to_def(a) } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index 78f534d014b90..e56f9e91e3f33 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -1,6 +1,6 @@ //! Type tree for term search -use hir_def::ImportPathConfig; +use hir_def::FindPathConfig; use hir_expand::mod_path::ModPath; use hir_ty::{ db::HirDatabase, @@ -18,7 +18,7 @@ use crate::{ fn mod_item_path( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { let db = sema_scope.db; let m = sema_scope.module(); @@ -29,7 +29,7 @@ fn mod_item_path( fn mod_item_path_str( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - cfg: ImportPathConfig, + cfg: FindPathConfig, edition: Edition, ) -> Result { let path = mod_item_path(sema_scope, def, cfg); @@ -103,7 +103,7 @@ impl<'db> Expr<'db> { &self, sema_scope: &SemanticsScope<'db>, many_formatter: &mut dyn FnMut(&Type<'db>) -> String, - cfg: ImportPathConfig, + cfg: FindPathConfig, display_target: DisplayTarget, ) -> Result { let db = sema_scope.db; @@ -380,7 +380,7 @@ impl<'db> Expr<'db> { fn container_name( container: AssocItemContainer, sema_scope: &SemanticsScope<'_>, - cfg: ImportPathConfig, + cfg: FindPathConfig, edition: Edition, display_target: DisplayTarget, ) -> Result { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 57ced8d8534b2..597d035ebd857 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -4,8 +4,12 @@ //! module, and we use to statically check that we only produce snippet //! assists if we are allowed to. -use hir::ImportPathConfig; -use ide_db::{SnippetCap, assists::ExprFillDefaultMode, imports::insert_use::InsertUseConfig}; +use hir::FindPathConfig; +use ide_db::{ + SnippetCap, + assists::ExprFillDefaultMode, + imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, +}; use crate::AssistKind; @@ -31,7 +35,15 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, - allow_unstable: true, + } + } + + pub fn find_path_confg(&self, allow_unstable: bool) -> FindPathConfig { + FindPathConfig { + prefer_no_std: self.prefer_no_std, + prefer_prelude: self.prefer_prelude, + prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 1ece7ddab101e..4d3212c515f28 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,7 +1,7 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{Adt, AsAssocItem, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics, sym}; +use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics, sym}; use ide_db::RootDatabase; use ide_db::assists::ExprFillDefaultMode; use ide_db::syntax_helpers::suggest_name; @@ -76,12 +76,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) .collect(); - let cfg = ctx.config.import_path_config(); - let make = SyntaxFactory::with_mappings(); let scope = ctx.sema.scope(expr.syntax())?; let module = scope.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { scope .containing_function() @@ -498,7 +497,7 @@ fn build_pat( make: &SyntaxFactory, module: hir::Module, var: ExtendedVariant, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { let db = ctx.db(); match var { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index c208c8bf789a0..80445578fcef9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -329,8 +329,6 @@ fn augment_references_with_imports( ) -> Vec { let mut visited_modules = FxHashSet::default(); - let cfg = ctx.config.import_path_config(); - let edition = target_module.krate().edition(ctx.db()); references .into_iter() @@ -345,22 +343,27 @@ fn augment_references_with_imports( { visited_modules.insert(ref_module); - let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); - let path = ref_module - .find_use_path( - ctx.sema.db, - ModuleDef::Module(*target_module), - ctx.config.insert_use.prefix_kind, - cfg, - ) - .map(|mod_path| { - make::path_concat( - mod_path_to_ast(&mod_path, edition), - make::path_from_text("Bool"), - ) - }); + ImportScope::find_insert_use_container(name.syntax(), &ctx.sema).and_then( + |import_scope| { + let cfg = + ctx.config.find_path_confg(ctx.sema.is_nightly(target_module.krate())); + let path = ref_module + .find_use_path( + ctx.sema.db, + ModuleDef::Module(*target_module), + ctx.config.insert_use.prefix_kind, + cfg, + ) + .map(|mod_path| { + make::path_concat( + mod_path_to_ast(&mod_path, edition), + make::path_from_text("Bool"), + ) + })?; - import_scope.zip(path) + Some((import_scope, path)) + }, + ) } else { None }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index 3d9cde0e0a67c..3a464a3dc6aae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -43,7 +43,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - return None; } - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let src_type_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 247c1011589bb..80ffb4db3e84d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -184,8 +184,6 @@ fn augment_references_with_imports( ) -> Vec<(ast::NameLike, Option<(ImportScope, ast::Path)>)> { let mut visited_modules = FxHashSet::default(); - let cfg = ctx.config.import_path_config(); - references .iter() .filter_map(|FileReference { name, .. }| { @@ -201,6 +199,7 @@ fn augment_references_with_imports( { visited_modules.insert(ref_module); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(ref_module.krate())); let import_scope = ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); let path = ref_module diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index b8c647ac8b71d..397327cb4ff8c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -86,9 +86,8 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Op FamousDefs(&ctx.sema, module.krate()).core_ops_ControlFlow(); if let Some(control_flow_enum) = control_flow_enum { + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), + cfg, ); if let Some(mod_path) = mod_path { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index c56d0b3de5d6a..a5467e760bf39 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -400,11 +400,12 @@ fn process_references( let segment = builder.make_mut(segment); let scope_node = builder.make_syntax_mut(scope_node); if !visited_modules.contains(&module) { + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, *enum_module_def, ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), + cfg, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 55a09c5d775d2..a1fc2c6023597 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -57,9 +57,9 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.import_path_config())?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; let field_type = field.ty()?; let field_name = field.name()?; @@ -99,9 +99,9 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.import_path_config())?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; let field_type = field.ty()?; let target = field.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index ac4a54a89ef1f..9760fd62aab4f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -77,10 +77,11 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?)); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); let type_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.import_path_config(), + cfg, )?; let edition = current_module.krate().edition(ctx.db()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 943795d40d551..cad14d929648a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -169,6 +169,7 @@ fn make_constructors( types: &[ast::Type], ) -> Vec> { let (db, sema) = (ctx.db(), &ctx.sema); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); types .iter() .map(|ty| { @@ -179,11 +180,7 @@ fn make_constructors( let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into(); let edition = module.krate().edition(db); - let ty_path = module.find_path( - db, - item_for_path_search(db, item_in_ns)?, - ctx.config.import_path_config(), - )?; + let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?; use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition) }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 985121780b1ab..e4494f0492ece 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -45,10 +45,11 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> let current_edition = current_module.krate().edition(ctx.db()); let target_module_def = ModuleDef::from(resolved_call); let item_in_ns = ItemInNs::from(target_module_def); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); let receiver_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.import_path_config(), + cfg, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 175f261317058..25c5593007bcb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -71,6 +71,7 @@ pub(crate) fn replace_derive_with_manual_impl( let current_module = ctx.sema.scope(adt.syntax())?.module(); let current_crate = current_module.krate(); let current_edition = current_crate.edition(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_crate)); let found_traits = items_locator::items_with_name( ctx.db(), @@ -84,7 +85,7 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.import_path_config()) + .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), cfg) .as_ref() .map(|path| mod_path_to_ast(path, current_edition)) .zip(Some(trait_)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 9f742131e5cb4..5fc4d7a6170de 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -63,12 +63,9 @@ pub(crate) fn replace_qualified_name_with_use( ); let path_to_qualifier = starts_with_name_ref .then(|| { - ctx.sema.scope(original_path.syntax())?.module().find_use_path( - ctx.sema.db, - module, - ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), - ) + let mod_ = ctx.sema.scope(original_path.syntax())?.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(mod_.krate())); + mod_.find_use_path(ctx.sema.db, module, ctx.config.insert_use.prefix_kind, cfg) }) .flatten(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 6527d3706e217..209d3f08eb888 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -55,7 +55,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< path.gen_source_code( &scope, &mut formatter, - ctx.config.import_path_config(), + ctx.config.find_path_confg(ctx.sema.is_nightly(scope.module().krate())), scope.krate().to_display_target(ctx.db()), ) .ok() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs index eed070cb07dd6..aed66d3d8344c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs @@ -132,12 +132,9 @@ pub(crate) fn desugar_async_into_impl_future( let scope = ctx.sema.scope(function.syntax())?; let module = scope.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let future_trait = FamousDefs(&ctx.sema, scope.krate()).core_future_Future()?; - let trait_path = module.find_path( - ctx.db(), - ModuleDef::Trait(future_trait), - ctx.config.import_path_config(), - )?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(future_trait), cfg)?; let edition = scope.krate().edition(ctx.db()); let trait_path = trait_path.display(ctx.db(), edition); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 11d26228ba201..e36e0e5704581 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -654,7 +654,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 1972f166134a4..a5b8a0b1ceb6b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -254,7 +254,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -276,7 +276,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index dad8a76de87df..d1e05a4359f19 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(ctx.is_nightly); + let import_cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -304,7 +304,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -346,7 +346,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 0058611a61539..d355fdbe0739c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -63,7 +63,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.find_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() && receiver_ty.impls_trait(ctx.db, drop_trait, &[]) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 844fce5ef8019..b7367cb62f099 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -4,8 +4,11 @@ //! module, and we use to statically check that we only produce snippet //! completions if we are allowed to. -use hir::ImportPathConfig; -use ide_db::{SnippetCap, imports::insert_use::InsertUseConfig}; +use hir::FindPathConfig; +use ide_db::{ + SnippetCap, + imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, +}; use crate::{CompletionFieldsToResolve, snippet::Snippet}; @@ -59,12 +62,20 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { - ImportPathConfig { + pub fn find_path_config(&self, allow_unstable: bool) -> FindPathConfig { + FindPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, allow_unstable, } } + + pub fn import_path_config(&self) -> ImportPathConfig { + ImportPathConfig { + prefer_no_std: self.prefer_no_std, + prefer_prelude: self.prefer_prelude, + prefer_absolute: self.prefer_absolute, + } + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 7d23f9d14c66f..dbf68dbe33afe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -299,7 +299,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.find_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 9dc0c0234dc56..d326098f94071 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(ctx.is_nightly); + let import_cfg = ctx.config.find_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 1012f993344fe..0c235c8d9a57a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use hir::{ - AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, HasCrate, ImportPathConfig, + AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate, ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Trait, TyFingerprint, Type, db::HirDatabase, }; @@ -19,6 +19,17 @@ use crate::{ items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct ImportPathConfig { + /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate + /// over the std. + pub prefer_no_std: bool, + /// If true, prefer import paths containing a prelude module. + pub prefer_prelude: bool, + /// If true, prefer abs path (starting with `::`) where it is available. + pub prefer_absolute: bool, +} + /// A candidate for import, derived during various IDE activities: /// * completion with imports on the fly proposals /// * completion edit resolve requests @@ -296,6 +307,12 @@ impl<'db> ImportAssets<'db> { Some(it) => it, None => return >::default().into_iter(), }; + let cfg = FindPathConfig { + prefer_no_std: cfg.prefer_no_std, + prefer_prelude: cfg.prefer_prelude, + prefer_absolute: cfg.prefer_absolute, + allow_unstable: sema.is_nightly(scope.krate()), + }; let db = sema.db; let krate = self.module_with_candidate.krate(); let scope_definitions = self.scope_definitions(sema); @@ -698,7 +715,7 @@ fn get_mod_path( item_to_search: ItemInNs, module_with_candidate: &Module, prefixed: Option, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { if let Some(prefix_kind) = prefixed { module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a76a0551dd8db..4a27035afd091 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -3,7 +3,7 @@ use crate::helpers::mod_path_to_ast; use either::Either; use hir::{ - AsAssocItem, HirDisplay, HirFileId, ImportPathConfig, ModuleDef, SemanticsScope, + AsAssocItem, FindPathConfig, HirDisplay, HirFileId, ModuleDef, SemanticsScope, prettify_macro_expansion, }; use itertools::Itertools; @@ -392,7 +392,7 @@ impl Ctx<'_> { parent.segment()?.name_ref()?, ) .and_then(|trait_ref| { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -452,7 +452,7 @@ impl Ctx<'_> { return None; } - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -501,7 +501,7 @@ impl Ctx<'_> { if let Some(adt) = ty.as_adt() && let ast::Type::PathType(path_ty) = &ast_ty { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -546,7 +546,7 @@ impl Ctx<'_> { match resolution { hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 742d614bc5673..2dfc2bb56595c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -1,7 +1,7 @@ //! This diagnostic provides an assist for creating a struct definition from a JSON //! example. -use hir::{ImportPathConfig, PathResolution, Semantics}; +use hir::{FindPathConfig, PathResolution, Semantics}; use ide_db::text_edit::TextEdit; use ide_db::{ EditionedFileId, FileRange, FxHashMap, RootDatabase, @@ -141,7 +141,7 @@ pub(crate) fn json_in_items( let scope = scb.make_import_scope_mut(import_scope); let current_module = semantics_scope.module(); - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 893bfca6a1298..49f925e2e0c93 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,6 +1,6 @@ use either::Either; use hir::{ - AssocItem, HirDisplay, ImportPathConfig, InFile, Type, + AssocItem, FindPathConfig, HirDisplay, InFile, Type, db::{ExpandDatabase, HirDatabase}, sym, }; @@ -132,7 +132,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole<'_>) -> Option Date: Thu, 7 Aug 2025 15:24:39 +0200 Subject: [PATCH 0454/1900] Workaround lsp-types typo --- .../crates/rust-analyzer/src/bin/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index ab045e0bf9ff1..9bcf835b4cca7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -208,13 +208,24 @@ fn run_server() -> anyhow::Result<()> { tracing::info!("InitializeParams: {}", initialize_params); let lsp_types::InitializeParams { root_uri, - capabilities, + mut capabilities, workspace_folders, initialization_options, client_info, .. } = from_json::("InitializeParams", &initialize_params)?; + // lsp-types has a typo in the `/capabilities/workspace/diagnostics` field, its typoed as `diagnostic` + if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") { + if let Ok(diag_caps) = from_json::( + "DiagnosticWorkspaceClientCapabilities", + val, + ) { + tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); + capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); + } + } + let root_path = match root_uri .and_then(|it| it.to_file_path().ok()) .map(patch_path_prefix) From fd28cc8a1b2496ac9717d7185a40af123aaefafb Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:24:39 +0200 Subject: [PATCH 0455/1900] Add more workaround hacks for incorrect startup diagnostics --- .../crates/rust-analyzer/src/bin/main.rs | 12 ++++++------ .../crates/rust-analyzer/src/global_state.rs | 5 +++++ .../crates/rust-analyzer/src/handlers/dispatch.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/reload.rs | 5 ++++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 9bcf835b4cca7..e80f2953505f6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -216,14 +216,14 @@ fn run_server() -> anyhow::Result<()> { } = from_json::("InitializeParams", &initialize_params)?; // lsp-types has a typo in the `/capabilities/workspace/diagnostics` field, its typoed as `diagnostic` - if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") { - if let Ok(diag_caps) = from_json::( + if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") + && let Ok(diag_caps) = from_json::( "DiagnosticWorkspaceClientCapabilities", val, - ) { - tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); - capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); - } + ) + { + tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); + capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); } let root_path = match root_uri diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 2f1afba3634ef..89d6fb8edc2e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -183,6 +183,10 @@ pub(crate) struct GlobalState { /// this queue should run only *after* [`GlobalState::process_changes`] has /// been called. pub(crate) deferred_task_queue: TaskQueue, + /// HACK: Workaround for https://github.com/rust-lang/rust-analyzer/issues/19709 + /// This is marked true if we failed to load a crate root file at crate graph creation, + /// which will usually end up causing a bunch of incorrect diagnostics on startup. + pub(crate) incomplete_crate_graph: bool, } /// An immutable snapshot of the world's state at a point in time. @@ -298,6 +302,7 @@ impl GlobalState { discover_workspace_queue: OpQueue::default(), deferred_task_queue: task_queue, + incomplete_crate_graph: false, }; // Apply any required database inputs from the config. this.update_configuration(config); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index b25245dd884a4..10bbb0bb31d99 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -141,7 +141,7 @@ impl RequestDispatcher<'_> { Result: Serialize, > + 'static, { - if !self.global_state.vfs_done { + if !self.global_state.vfs_done || self.global_state.incomplete_crate_graph { if let Some(lsp_server::Request { id, .. }) = self.req.take_if(|it| it.method == R::METHOD) { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index a8a54930c6e5e..c15496b3f6cd2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -741,13 +741,16 @@ impl GlobalState { }) .collect(); + self.incomplete_crate_graph = false; let (crate_graph, proc_macro_paths) = { // Create crate graph from all the workspaces let vfs = &self.vfs.read().0; let load = |path: &AbsPath| { let vfs_path = vfs::VfsPath::from(path.to_path_buf()); self.crate_graph_file_dependencies.insert(vfs_path.clone()); - vfs.file_id(&vfs_path).and_then(|(file_id, excluded)| { + let file_id = vfs.file_id(&vfs_path); + self.incomplete_crate_graph |= file_id.is_none(); + file_id.and_then(|(file_id, excluded)| { (excluded == vfs::FileExcluded::No).then_some(file_id) }) }; From d66fb4910fb7e87b7e17477ae147854adf76b0dd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 5 Sep 2025 21:35:58 -0500 Subject: [PATCH 0456/1900] Suggest removing Box::new --- .../src/fn_ctxt/suggestions.rs | 22 +++++++++++++++++++ tests/ui/coercion/coerce-block-tail.stderr | 7 +++--- .../ui/coercion/coerce-box-new-to-unboxed.rs | 4 ++++ .../coercion/coerce-box-new-to-unboxed.stderr | 19 ++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/ui/coercion/coerce-box-new-to-unboxed.rs create mode 100644 tests/ui/coercion/coerce-box-new-to-unboxed.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index aca3840712e79..a12bc05793439 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2955,6 +2955,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let deref_kind = if checked_ty.is_box() { + // detect Box::new(..) + if let ExprKind::Call(box_new, [_]) = expr.kind + && let ExprKind::Path(qpath) = &box_new.kind + && let Res::Def(DefKind::AssocFn, fn_id) = + self.typeck_results.borrow().qpath_res(qpath, box_new.hir_id) + && let Some(impl_id) = self.tcx.inherent_impl_of_assoc(fn_id) + && self.tcx.type_of(impl_id).skip_binder().is_box() + && self.tcx.item_name(fn_id) == sym::new + { + let l_paren = self.tcx.sess.source_map().next_point(box_new.span); + let r_paren = self.tcx.sess.source_map().end_point(expr.span); + return Some(( + vec![ + (box_new.span.to(l_paren), String::new()), + (r_paren, String::new()), + ], + "consider removing the Box".to_string(), + Applicability::MachineApplicable, + false, + false, + )); + } "unboxing the value" } else if checked_ty.is_ref() { "dereferencing the borrow" diff --git a/tests/ui/coercion/coerce-block-tail.stderr b/tests/ui/coercion/coerce-block-tail.stderr index 1301f3b7813ed..b358401b7062b 100644 --- a/tests/ui/coercion/coerce-block-tail.stderr +++ b/tests/ui/coercion/coerce-block-tail.stderr @@ -6,10 +6,11 @@ LL | let _: &i32 = & { Box::new(1i32) }; | = note: expected type `i32` found struct `Box` -help: consider unboxing the value +help: consider removing the Box + | +LL - let _: &i32 = & { Box::new(1i32) }; +LL + let _: &i32 = & { 1i32 }; | -LL | let _: &i32 = & { *Box::new(1i32) }; - | + error: aborting due to 1 previous error diff --git a/tests/ui/coercion/coerce-box-new-to-unboxed.rs b/tests/ui/coercion/coerce-box-new-to-unboxed.rs new file mode 100644 index 0000000000000..63ff0fc4e2381 --- /dev/null +++ b/tests/ui/coercion/coerce-box-new-to-unboxed.rs @@ -0,0 +1,4 @@ +fn main() { + let _: String = Box::new(String::new()); + //~^ ERROR mismatched types +} diff --git a/tests/ui/coercion/coerce-box-new-to-unboxed.stderr b/tests/ui/coercion/coerce-box-new-to-unboxed.stderr new file mode 100644 index 0000000000000..bdd962686e4b4 --- /dev/null +++ b/tests/ui/coercion/coerce-box-new-to-unboxed.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/coerce-box-new-to-unboxed.rs:2:21 + | +LL | let _: String = Box::new(String::new()); + | ------ ^^^^^^^^^^^^^^^^^^^^^^^ expected `String`, found `Box` + | | + | expected due to this + | + = note: expected struct `String` + found struct `Box` +help: consider removing the Box + | +LL - let _: String = Box::new(String::new()); +LL + let _: String = String::new(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 8b752cb37d646118c46f7739ae965b8e9c9a9b03 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 14 Sep 2025 21:14:37 +0200 Subject: [PATCH 0457/1900] fix issue with `cmse-nonsecure-entry` ABI being both async and c-variadic --- .../src/hir_ty_lowering/cmse.rs | 6 ++ tests/crashes/132142.rs | 3 - .../cmse-nonsecure-entry/c-variadic.rs | 69 +++++++++++++++++++ .../cmse-nonsecure-entry/c-variadic.stderr | 28 ++++++++ .../cmse-nonsecure-entry/generics.rs | 7 +- .../cmse-nonsecure-entry/generics.stderr | 18 +---- 6 files changed, 105 insertions(+), 26 deletions(-) delete mode 100644 tests/crashes/132142.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e60..7867c1c3b4857 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -85,6 +85,12 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; + // An `extern "cmse-nonsecure-entry"` function cannot be c-variadic. We run + // into https://github.com/rust-lang/rust/issues/132142 if we don't explicitly bail. + if decl.c_variadic { + return; + } + match is_valid_cmse_inputs(tcx, fn_sig) { Ok(Ok(())) => {} Ok(Err(index)) => { diff --git a/tests/crashes/132142.rs b/tests/crashes/132142.rs deleted file mode 100644 index 813bf0bf0a8e5..0000000000000 --- a/tests/crashes/132142.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ known-bug: #132142 - -async extern "cmse-nonsecure-entry" fn fun(...) {} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs new file mode 100644 index 0000000000000..9317ab5cd2756 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs @@ -0,0 +1,69 @@ +//@ add-core-stubs +//@ edition: 2018 +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[lang = "va_list"] +struct VaList(*mut u8); + +unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions +} + +// A regression test for https://github.com/rust-lang/rust/issues/132142 +async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions + //~| ERROR functions cannot be both `async` and C-variadic +} + +// Below are the lang items that are required for a program that defines an `async` function. +// Without them, the ICE that is tested for here is not reached for this target. For now they are in +// this file, but they may be moved into `minicore` if/when other `#[no_core]` tests want to use +// them. + +// NOTE: in `core` this type uses `NonNull`. +#[lang = "ResumeTy"] +pub struct ResumeTy(*mut Context<'static>); + +#[lang = "future_trait"] +pub trait Future { + /// The type of value produced on completion. + #[lang = "future_output"] + type Output; + + // NOTE: misses the `poll` method. +} + +#[lang = "async_drop"] +pub trait AsyncDrop { + // NOTE: misses the `drop` method. +} + +#[lang = "Poll"] +pub enum Poll { + #[lang = "Ready"] + Ready(T), + + #[lang = "Pending"] + Pending, +} + +#[lang = "Context"] +pub struct Context<'a> { + // NOTE: misses a bunch of fields. + _marker: PhantomData &'a ()>, +} + +#[lang = "get_context"] +pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { + // NOTE: the actual implementation looks different. + mem::transmute(cx.0) +} + +#[lang = "pin"] +pub struct Pin(T); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr new file mode 100644 index 0000000000000..948f8f5747b0e --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr @@ -0,0 +1,28 @@ +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions + --> $DIR/c-variadic.rs:14:60 + | +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: functions cannot be both `async` and C-variadic + --> $DIR/c-variadic.rs:19:1 + | +LL | async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + | ^^^^^ `async` because of this ^^^^^^ C-variadic because of this + +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions + --> $DIR/c-variadic.rs:19:68 + | +LL | async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: aborting due to 3 previous errors + diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index eb6f4a323655b..aee01e25ddc4f 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -1,7 +1,7 @@ //@ add-core-stubs //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm -#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)] +#![feature(cmse_nonsecure_entry, no_core, lang_items)] #![no_core] extern crate minicore; @@ -65,8 +65,3 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } - -unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions - //~| ERROR requires `va_list` lang_item -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 8937def9428cf..df1a910704378 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,13 +1,3 @@ -error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions - --> $DIR/generics.rs:69:60 - | -LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ----------------------------- ^^^^^^ - | | - | `extern "cmse-nonsecure-entry"` because of this - | - = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list - error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 | @@ -71,12 +61,6 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error: requires `va_list` lang_item - --> $DIR/generics.rs:69:60 - | -LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ - -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0798`. From 593a9c98aef3819f727f2fc9fa8abe58ea784d07 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 22:45:29 +0200 Subject: [PATCH 0458/1900] fix(collapsible{,_else}_if): respect `#[expect]` on inner `if` --- clippy_lints/src/collapsible_if.rs | 73 +++++++++++++------ clippy_utils/src/sym.rs | 2 + .../collapsible_if/collapsible_else_if.fixed | 19 ++++- .../collapsible_if/collapsible_else_if.rs | 19 ++++- tests/ui/collapsible_else_if.fixed | 27 +++++++ tests/ui/collapsible_else_if.rs | 27 +++++++ tests/ui/collapsible_else_if.stderr | 2 +- tests/ui/collapsible_else_if_unfixable.rs | 22 ++++++ tests/ui/collapsible_else_if_unfixable.stderr | 17 +++++ tests/ui/collapsible_if.fixed | 18 +++++ tests/ui/collapsible_if.rs | 18 +++++ tests/ui/collapsible_if.stderr | 2 +- tests/ui/collapsible_if_unfixable.rs | 20 +++++ tests/ui/collapsible_if_unfixable.stderr | 17 +++++ 14 files changed, 256 insertions(+), 27 deletions(-) create mode 100644 tests/ui/collapsible_else_if_unfixable.rs create mode 100644 tests/ui/collapsible_else_if_unfixable.stderr create mode 100644 tests/ui/collapsible_if_unfixable.rs create mode 100644 tests/ui/collapsible_if_unfixable.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index ad610fbd8d2c3..b239c20ab4d89 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,16 +1,16 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, tokenize_with_text}; -use rustc_ast::BinOpKind; +use clippy_utils::{span_contains_non_whitespace, sym, tokenize_with_text}; +use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -95,14 +95,14 @@ impl CollapsibleIf { fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { if let Some(else_) = expr_block(else_block) - && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() && let ExprKind::If(else_if_cond, ..) = else_.kind - && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, else_block, else_, sym::collapsible_else_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_ELSE_IF, + else_.hir_id, else_block.span, "this `else { if .. }` block can be collapsed", |diag| { @@ -166,15 +166,15 @@ impl CollapsibleIf { fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { if let Some(inner) = expr_block(then) - && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind && self.eligible_condition(cx, check_inner) && expr.span.eq_ctxt(inner.span) - && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, then, inner, sym::collapsible_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_IF, + inner.hir_id, expr.span, "this `if` statement can be collapsed", |diag| { @@ -219,6 +219,45 @@ impl CollapsibleIf { !matches!(cond.kind, ExprKind::Let(..)) || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) } + + // Check that nothing significant can be found between the initial `{` of `inner_if` and + // the beginning of `inner_if_expr`... + // + // Unless it's only an `#[expect(clippy::collapsible{,_else}_if)]` attribute, in which case we + // _do_ need to lint, in order to actually fulfill its expectation (#13365) + fn check_significant_tokens_and_expect_attrs( + &self, + cx: &LateContext<'_>, + inner_if: &Block<'_>, + inner_if_expr: &Expr<'_>, + expected_lint_name: Symbol, + ) -> bool { + match cx.tcx.hir_attrs(inner_if_expr.hir_id) { + [] => { + // There aren't any attributes, so just check for significant tokens + let span = inner_if.span.split_at(1).1.until(inner_if_expr.span); + !span_contains_non_whitespace(cx, span, self.lint_commented_code) + }, + + [attr] + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let Some(metas) = attr.meta_item_list() + && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + { + // There is an `expect` attribute -- check that there is no _other_ significant text + let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); + let span_after_attr = attr.span().between(inner_if_expr.span); + !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) + && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + }, + + // There are other attributes, which are significant tokens -- check failed + _ => false, + } + } } impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); @@ -242,18 +281,6 @@ impl LateLintPass<'_> for CollapsibleIf { } } -// Check that nothing significant can be found but whitespaces between the initial `{` of `block` -// and the beginning of `stop_at`. -fn block_starts_with_significant_tokens( - cx: &LateContext<'_>, - block: &Block<'_>, - stop_at: &Expr<'_>, - lint_commented_code: bool, -) -> bool { - let span = block.span.split_at(1).1.until(stop_at.span); - span_contains_non_whitespace(cx, span, lint_commented_code) -} - /// If `block` is a block with either one expression or a statement containing an expression, /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f96..463711195bb0c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -111,6 +111,8 @@ generate! { clone_into, cloned, cognitive_complexity, + collapsible_else_if, + collapsible_if, collect, const_ptr, contains, diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed index 0dc0fc230c8de..ec45dfd2033ad 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -48,3 +48,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.rs b/tests/ui-toml/collapsible_if/collapsible_else_if.rs index 8344c122f16c8..54315a3c32bf9 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.rs +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.rs @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -53,3 +53,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 3d709fe9b8e0e..da958f76a5ca0 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -87,6 +87,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 51868e039086f..06af49f2f6f3b 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -103,6 +103,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 1a7bcec7fd5df..ce1da593a8e93 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -151,7 +151,7 @@ LL | | } | |_____^ help: collapse nested if block: `if false {}` error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:130:12 + --> tests/ui/collapsible_else_if.rs:157:12 | LL | } else { | ____________^ diff --git a/tests/ui/collapsible_else_if_unfixable.rs b/tests/ui/collapsible_else_if_unfixable.rs new file mode 100644 index 0000000000000..e5c18cf3ba901 --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.rs @@ -0,0 +1,22 @@ +//@no-rustfix +#![warn(clippy::collapsible_else_if)] + +fn issue_13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_else_if_unfixable.stderr b/tests/ui/collapsible_else_if_unfixable.stderr new file mode 100644 index 0000000000000..b461ceba6de7f --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:10:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:17:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 78354c2d7cf88..ca9d02ff2d4f8 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -144,6 +144,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 5d9afa109569d..9ac68ecd4cac0 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -154,6 +154,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index a685cc2e9291d..b1f26630f529b 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -191,7 +191,7 @@ LL ~ ; 3 | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:178:5 + --> tests/ui/collapsible_if.rs:196:5 | LL | / if true { LL | | (if true { diff --git a/tests/ui/collapsible_if_unfixable.rs b/tests/ui/collapsible_if_unfixable.rs new file mode 100644 index 0000000000000..643520ac0f5d9 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.rs @@ -0,0 +1,20 @@ +//@ no-rustfix +#![warn(clippy::collapsible_if)] + +fn issue13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + // don't collapsible because of this comment + #[expect(clippy::collapsible_if)] + if true {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + #[expect(clippy::collapsible_if)] + // don't collapsible because of this comment + if true {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_if_unfixable.stderr b/tests/ui/collapsible_if_unfixable.stderr new file mode 100644 index 0000000000000..64c8fb8da2b44 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:9:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:15:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From d23909d4d8aa2431df5ebc2fdeb2d967cd3c286d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Sep 2025 08:48:30 +0200 Subject: [PATCH 0459/1900] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/tests/pass/shims/fs.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 8315eb30f9431..f27677cd50388 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a015919e54c60b1b2bec7a98dec478cfc4a48f4e +9d82de19dfae60e55c291f5f28e28cfc2c1b9630 diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 022dcc5dcbafa..0748007b3c058 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -28,7 +28,8 @@ fn main() { test_from_raw_os_error(); test_file_clone(); // Windows file handling is very incomplete. - if cfg!(not(windows)) { + // FIXME: read_dir broken on FreeBSD (https://github.com/rust-lang/miri/issues/4587) + if cfg!(not(windows)) && !cfg!(target_os = "freebsd") { test_file_set_len(); test_file_sync(); test_rename(); From 929c9335ddcaf8e59f5b56c986fd58a3310fdac0 Mon Sep 17 00:00:00 2001 From: Haidong Zhang Date: Tue, 9 Sep 2025 18:10:24 +0800 Subject: [PATCH 0460/1900] Add parallel-frontend-threads to bootstrap.toml and enable multi-threaded parallel compilation --- bootstrap.example.toml | 8 ++++++++ src/bootstrap/configure.py | 5 +++++ src/bootstrap/src/core/builder/cargo.rs | 6 ++++++ src/bootstrap/src/core/config/config.rs | 5 ++++- src/bootstrap/src/core/config/toml/rust.rs | 2 ++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index eac9395779798..51529751dd58f 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -859,6 +859,14 @@ # Trigger a `DebugBreak` after an internal compiler error during bootstrap on Windows #rust.break-on-ice = true +# Set the number of threads for the compiler frontend used during compilation of Rust code (passed to `-Zthreads`). +# The valid options are: +# 0 - Set the number of threads according to the detected number of threads of the host system +# 1 - Use a single thread for compilation of Rust code (the default) +# N - Number of threads used for compilation of Rust code +# +#rust.parallel-frontend-threads = 1 + # ============================================================================= # Distribution options # diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index b05a5cc8b818c..1915986be28c5 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -340,6 +340,11 @@ def v(*args): "don't truncate options when printing them in this configure script", ) v("set", None, "set arbitrary key/value pairs in TOML configuration") +v( + "parallel-frontend-threads", + "rust.parallel-frontend-threads", + "number of parallel threads for rustc compilation", +) def p(msg): diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 924bb4adb42d0..7c24378f0bb5c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -679,6 +679,12 @@ impl Builder<'_> { // cargo would implicitly add it, it was discover that sometimes bootstrap only use // `rustflags` without `cargo` making it required. rustflags.arg("-Zunstable-options"); + + // Add parallel frontend threads configuration + if let Some(threads) = self.config.rust_parallel_frontend_threads { + rustflags.arg(&format!("-Zthreads={threads}")); + } + for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { if restricted_mode.is_none() || *restricted_mode == Some(mode) { rustflags.arg(&check_cfg_arg(name, *values)); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 678a9b6395228..585bf5f870cbb 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -191,7 +191,6 @@ pub struct Config { pub rust_optimize: RustOptimize, pub rust_codegen_units: Option, pub rust_codegen_units_std: Option, - pub rustc_debug_assertions: bool, pub std_debug_assertions: bool, pub tools_debug_assertions: bool, @@ -222,6 +221,8 @@ pub struct Config { pub rust_validate_mir_opts: Option, pub rust_std_features: BTreeSet, pub rust_break_on_ice: bool, + pub rust_parallel_frontend_threads: Option, + pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -534,6 +535,7 @@ impl Config { backtrace_on_ice: rust_backtrace_on_ice, verify_llvm_ir: rust_verify_llvm_ir, thin_lto_import_instr_limit: rust_thin_lto_import_instr_limit, + parallel_frontend_threads: rust_parallel_frontend_threads, remap_debuginfo: rust_remap_debuginfo, jemalloc: rust_jemalloc, test_compare_mode: rust_test_compare_mode, @@ -1298,6 +1300,7 @@ impl Config { rust_overflow_checks_std: rust_overflow_checks_std .or(rust_overflow_checks) .unwrap_or(rust_debug == Some(true)), + rust_parallel_frontend_threads: rust_parallel_frontend_threads.map(threads_from_config), rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate), rust_profile_use: flags_rust_profile_use.or(rust_profile_use), rust_randomize_layout: rust_randomize_layout.unwrap_or(false), diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 4832a1d37b777..e5987d7040aaf 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -66,6 +66,7 @@ define_config! { validate_mir_opts: Option = "validate-mir-opts", std_features: Option> = "std-features", break_on_ice: Option = "break-on-ice", + parallel_frontend_threads: Option = "parallel-frontend-threads", } } @@ -357,6 +358,7 @@ pub fn check_incompatible_options_for_ci_rustc( validate_mir_opts: _, frame_pointers: _, break_on_ice: _, + parallel_frontend_threads: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 03b39882e30af..2c48cebd2df05 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -546,4 +546,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "The default value of the `gcc.download-ci-gcc` option has been changed to `true`.", }, + ChangeInfo { + change_id: 146458, + severity: ChangeSeverity::Info, + summary: "There is now a bootstrap option called `rust.parallel-frontend-threads`, which can be used to set the number of threads for the compiler frontend used during compilation of Rust code.", + }, ]; From c89b6a955c9e16c7c96714cea7945945caadcc79 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 10 Sep 2025 12:40:29 +0000 Subject: [PATCH 0461/1900] Iterator repeat: no infinite loop for `last` and `count` --- library/core/src/iter/sources/repeat.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index c4f5a483e5c2a..4bcd5b16aea6a 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -9,7 +9,7 @@ use crate::num::NonZero; /// [`Iterator::take()`], in order to make them finite. /// /// Use [`str::repeat()`] instead of this function if you just want to repeat -/// a char/string `n`th times. +/// a char/string `n` times. /// /// If the element type of the iterator you need does not implement `Clone`, /// or if you do not want to keep the repeated element in memory, you can @@ -98,11 +98,12 @@ impl Iterator for Repeat { } fn last(self) -> Option { - loop {} + Some(self.element) } + #[track_caller] fn count(self) -> usize { - loop {} + panic!("iterator is infinite"); } } From 1ae720a52dd6a3ed5a375d183aea5330de49eb42 Mon Sep 17 00:00:00 2001 From: Joe Birr-Pixton Date: Tue, 16 Sep 2025 11:51:19 +0100 Subject: [PATCH 0462/1900] Fix spelling of "adaptor" These docs are in en_US, so "adapter" is the correct spelling (and indeed used in the next line.) --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index ff0e29e04c251..a45edd08e8ccc 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1081,7 +1081,7 @@ pub trait Read { default_read_buf_exact(self, cursor) } - /// Creates a "by reference" adaptor for this instance of `Read`. + /// Creates a "by reference" adapter for this instance of `Read`. /// /// The returned adapter also implements `Read` and will simply borrow this /// current reader. From 325ceef018030f922840a10741d194b4223ecc59 Mon Sep 17 00:00:00 2001 From: Joe Birr-Pixton Date: Tue, 16 Sep 2025 11:52:43 +0100 Subject: [PATCH 0463/1900] Fix other uses of "adaptor" --- library/core/src/iter/adapters/flatten.rs | 2 +- tests/ui/iterators/issue-58952-filter-type-length.rs | 2 +- .../traits/next-solver/typeck/normalize-in-upvar-collection.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index a820045521b9f..c50f07ff6bb66 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -779,7 +779,7 @@ impl OneShot for result::IterMut<'_, T> {} impl OneShot for Empty {} impl OneShot for array::IntoIter {} -// These adaptors never increase the number of items. +// These adapters never increase the number of items. // (There are more possible, but for now this matches BoundedSize above.) impl OneShot for Cloned {} impl OneShot for Copied {} diff --git a/tests/ui/iterators/issue-58952-filter-type-length.rs b/tests/ui/iterators/issue-58952-filter-type-length.rs index 6730865b6c7f7..525a2e39a913c 100644 --- a/tests/ui/iterators/issue-58952-filter-type-length.rs +++ b/tests/ui/iterators/issue-58952-filter-type-length.rs @@ -2,7 +2,7 @@ //! This snippet causes the type length to blowup exponentially, //! so check that we don't accidentally exceed the type length limit. -// FIXME: Once the size of iterator adaptors is further reduced, +// FIXME: Once the size of iterator adapters is further reduced, // increase the complexity of this test. use std::collections::VecDeque; diff --git a/tests/ui/traits/next-solver/typeck/normalize-in-upvar-collection.rs b/tests/ui/traits/next-solver/typeck/normalize-in-upvar-collection.rs index 6567f2752404a..2f108daf1e5ab 100644 --- a/tests/ui/traits/next-solver/typeck/normalize-in-upvar-collection.rs +++ b/tests/ui/traits/next-solver/typeck/normalize-in-upvar-collection.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Znext-solver //@ check-pass -// Fixes a regression in icu_provider_adaptors where we weren't normalizing the +// Fixes a regression in icu_provider_adapters where we weren't normalizing the // return type of a function type before performing a `Ty::builtin_deref` call, // leading to an ICE. From 9991ec282f3ff08c895e4a5262259139fe689953 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Sun, 9 Mar 2025 18:25:57 +0800 Subject: [PATCH 0464/1900] Keep space if expr follows identifier when lint unused parens --- compiler/rustc_lint/src/unused.rs | 8 +++++ .../ui/lint/unused_parens_follow_ident.fixed | 17 ++++++++++ tests/ui/lint/unused_parens_follow_ident.rs | 17 ++++++++++ .../ui/lint/unused_parens_follow_ident.stderr | 31 +++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 tests/ui/lint/unused_parens_follow_ident.fixed create mode 100644 tests/ui/lint/unused_parens_follow_ident.rs create mode 100644 tests/ui/lint/unused_parens_follow_ident.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 22d89d2461273..edbbfba4f3461 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -843,6 +843,10 @@ trait UnusedDelimLint { && !snip.ends_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.ends_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; @@ -852,6 +856,10 @@ trait UnusedDelimLint { && !snip.starts_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.starts_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; diff --git a/tests/ui/lint/unused_parens_follow_ident.fixed b/tests/ui/lint/unused_parens_follow_ident.fixed new file mode 100644 index 0000000000000..e61b287e5a648 --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +macro_rules! wrap { + ($name:ident $arg:expr) => { + $name($arg); + }; +} + +fn main() { + wrap!(unary routine()); //~ ERROR unnecessary parentheses around function argument + wrap!(unary routine()); //~ ERROR unnecessary parentheses around function argument +} + +fn unary(_: ()) {} +fn routine() {} diff --git a/tests/ui/lint/unused_parens_follow_ident.rs b/tests/ui/lint/unused_parens_follow_ident.rs new file mode 100644 index 0000000000000..32a163345b2cc --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +macro_rules! wrap { + ($name:ident $arg:expr) => { + $name($arg); + }; +} + +fn main() { + wrap!(unary(routine())); //~ ERROR unnecessary parentheses around function argument + wrap!(unary (routine())); //~ ERROR unnecessary parentheses around function argument +} + +fn unary(_: ()) {} +fn routine() {} diff --git a/tests/ui/lint/unused_parens_follow_ident.stderr b/tests/ui/lint/unused_parens_follow_ident.stderr new file mode 100644 index 0000000000000..ce7bb26778c0d --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.stderr @@ -0,0 +1,31 @@ +error: unnecessary parentheses around function argument + --> $DIR/unused_parens_follow_ident.rs:12:16 + | +LL | wrap!(unary(routine())); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused_parens_follow_ident.rs:3:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - wrap!(unary(routine())); +LL + wrap!(unary routine()); + | + +error: unnecessary parentheses around function argument + --> $DIR/unused_parens_follow_ident.rs:13:17 + | +LL | wrap!(unary (routine())); + | ^ ^ + | +help: remove these parentheses + | +LL - wrap!(unary (routine())); +LL + wrap!(unary routine()); + | + +error: aborting due to 2 previous errors + From 79c9f6ee9b3229279ffdaa0515c7b0320c0b0737 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 21:36:45 +1000 Subject: [PATCH 0465/1900] Stop using `as_c_char_ptr` for coverage-related bindings --- .../rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs | 7 +++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 +++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index bc4f6bb6a82bc..2aa3dec813173 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -2,7 +2,6 @@ use std::ffi::CString; -use crate::common::AsCCharPtr; use crate::coverageinfo::ffi; use crate::llvm; @@ -34,7 +33,7 @@ pub(crate) fn create_pgo_func_name_var<'ll>( unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar( llfn, - mangled_fn_name.as_c_char_ptr(), + mangled_fn_name.as_ptr(), mangled_fn_name.len(), ) } @@ -44,7 +43,7 @@ pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef]) -> Vec, Vec<_>>(); llvm::build_byte_buffer(|buffer| unsafe { @@ -89,7 +88,7 @@ pub(crate) fn write_function_mappings_to_buffer( /// Hashes some bytes into a 64-bit hash, via LLVM's `IndexedInstrProf::ComputeHash`, /// as required for parts of the LLVM coverage mapping format. pub(crate) fn hash_bytes(bytes: &[u8]) -> u64 { - unsafe { llvm::LLVMRustCoverageHashBytes(bytes.as_c_char_ptr(), bytes.len()) } + unsafe { llvm::LLVMRustCoverageHashBytes(bytes.as_ptr(), bytes.len()) } } /// Returns LLVM's `coverage::CovMapVersion::CurrentVersion` (CoverageMapping.h) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f08..52c24169f893a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2080,8 +2080,11 @@ unsafe extern "C" { ConstraintsLen: size_t, ) -> bool; + /// A list of pointer-length strings is passed as two pointer-length slices, + /// one slice containing pointers and one slice containing their corresponding + /// lengths. The implementation will check that both slices have the same length. pub(crate) fn LLVMRustCoverageWriteFilenamesToBuffer( - Filenames: *const *const c_char, + Filenames: *const *const c_uchar, // See "PTR_LEN_STR". FilenamesLen: size_t, Lengths: *const size_t, LengthsLen: size_t, @@ -2104,10 +2107,13 @@ unsafe extern "C" { pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( F: &Value, - FuncName: *const c_char, + FuncName: *const c_uchar, // See "PTR_LEN_STR". FuncNameLen: size_t, ) -> &Value; - pub(crate) fn LLVMRustCoverageHashBytes(Bytes: *const c_char, NumBytes: size_t) -> u64; + pub(crate) fn LLVMRustCoverageHashBytes( + Bytes: *const c_uchar, // See "PTR_LEN_STR". + NumBytes: size_t, + ) -> u64; pub(crate) fn LLVMRustCoverageWriteCovmapSectionNameToString(M: &Module, OutStr: &RustString); From 06a7460455284aca27fbf65505c385e3dc275da3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 21:44:40 +1000 Subject: [PATCH 0466/1900] Mark some coverage-related bindings as safe --- .../src/coverageinfo/llvm_cov.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index 2aa3dec813173..d50eb533ffdbb 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -6,21 +6,21 @@ use crate::coverageinfo::ffi; use crate::llvm; pub(crate) fn covmap_var_name() -> CString { - CString::new(llvm::build_byte_buffer(|s| unsafe { + CString::new(llvm::build_byte_buffer(|s| { llvm::LLVMRustCoverageWriteCovmapVarNameToString(s); })) .expect("covmap variable name should not contain NUL") } pub(crate) fn covmap_section_name(llmod: &llvm::Module) -> CString { - CString::new(llvm::build_byte_buffer(|s| unsafe { + CString::new(llvm::build_byte_buffer(|s| { llvm::LLVMRustCoverageWriteCovmapSectionNameToString(llmod, s); })) .expect("covmap section name should not contain NUL") } pub(crate) fn covfun_section_name(llmod: &llvm::Module) -> CString { - CString::new(llvm::build_byte_buffer(|s| unsafe { + CString::new(llvm::build_byte_buffer(|s| { llvm::LLVMRustCoverageWriteCovfunSectionNameToString(llmod, s); })) .expect("covfun section name should not contain NUL") @@ -95,5 +95,5 @@ pub(crate) fn hash_bytes(bytes: &[u8]) -> u64 { /// as a raw numeric value. For historical reasons, the numeric value is 1 less /// than the number in the version's name, so `Version7` is actually `6u32`. pub(crate) fn mapping_version() -> u32 { - unsafe { llvm::LLVMRustCoverageMappingVersion() } + llvm::LLVMRustCoverageMappingVersion() } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 52c24169f893a..74251cd2b4949 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2115,13 +2115,17 @@ unsafe extern "C" { NumBytes: size_t, ) -> u64; - pub(crate) fn LLVMRustCoverageWriteCovmapSectionNameToString(M: &Module, OutStr: &RustString); - - pub(crate) fn LLVMRustCoverageWriteCovfunSectionNameToString(M: &Module, OutStr: &RustString); - - pub(crate) fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString); + pub(crate) safe fn LLVMRustCoverageWriteCovmapSectionNameToString( + M: &Module, + OutStr: &RustString, + ); + pub(crate) safe fn LLVMRustCoverageWriteCovfunSectionNameToString( + M: &Module, + OutStr: &RustString, + ); + pub(crate) safe fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString); - pub(crate) fn LLVMRustCoverageMappingVersion() -> u32; + pub(crate) safe fn LLVMRustCoverageMappingVersion() -> u32; pub(crate) fn LLVMRustDebugMetadataVersion() -> u32; pub(crate) fn LLVMRustVersionMajor() -> u32; pub(crate) fn LLVMRustVersionMinor() -> u32; From 1a1510816a69844fb3611efdc53acee07a55cebb Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 11 Sep 2025 09:13:44 -0400 Subject: [PATCH 0467/1900] handle const generics, ?Sized, early bound lifetimes --- compiler/rustc_hir_analysis/src/check/mod.rs | 44 ++++++++++++++----- .../apitit-unimplemented-method.rs | 5 ++- .../apitit-unimplemented-method.stderr | 10 +++-- tests/ui/suggestions/auxiliary/dep.rs | 14 +++++- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 63d0f400aefc4..e70d5505aae36 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -70,6 +70,7 @@ pub mod intrinsic; mod region; pub mod wfcheck; +use std::borrow::Cow; use std::num::NonZero; pub use check::{check_abi, check_custom_abi}; @@ -86,7 +87,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode, + self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; @@ -335,6 +336,7 @@ fn bounds_from_generic_predicates<'tcx>( assoc: ty::AssocItem, ) -> (String, String) { let mut types: FxIndexMap, Vec> = FxIndexMap::default(); + let mut regions: FxIndexMap, Vec>> = FxIndexMap::default(); let mut projections = vec![]; for (predicate, _) in predicates { debug!("predicate {:?}", predicate); @@ -351,20 +353,23 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Projection(projection_pred) => { projections.push(bound_predicate.rebind(projection_pred)); } + ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => { + regions.entry(a).or_default().push(b); + } _ => {} } } let mut where_clauses = vec![]; let generics = tcx.generics_of(assoc.def_id); - let types_str = generics + let params = generics .own_params .iter() - .filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. })) - .map(|p| { - // we just checked that it's a type, so the unwrap can't fail - let ty = tcx.mk_param_from_def(p).as_type().unwrap(); - if let Some(bounds) = types.get(&ty) { + .filter(|p| !p.kind.is_synthetic()) + .map(|p| match tcx.mk_param_from_def(p).kind() { + ty::GenericArgKind::Type(ty) => { + let bounds = + types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new())); let mut bounds_str = vec![]; for bound in bounds.iter().copied() { let mut projections_str = vec![]; @@ -377,7 +382,11 @@ fn bounds_from_generic_predicates<'tcx>( projections_str.push(format!("{} = {}", name, p.term)); } } - let bound_def_path = tcx.def_path_str(bound); + let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) { + String::from("?Sized") + } else { + tcx.def_path_str(bound) + }; if projections_str.is_empty() { where_clauses.push(format!("{}: {}", ty, bound_def_path)); } else { @@ -393,8 +402,21 @@ fn bounds_from_generic_predicates<'tcx>( } else { format!("{}: {}", ty, bounds_str.join(" + ")) } - } else { - ty.to_string() + } + ty::GenericArgKind::Const(ct) => { + format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder()) + } + ty::GenericArgKind::Lifetime(region) => { + if let Some(v) = regions.get(®ion) + && !v.is_empty() + { + format!( + "{region}: {}", + v.into_iter().map(Region::to_string).collect::>().join(" + ") + ) + } else { + region.to_string() + } } }) .collect::>(); @@ -409,7 +431,7 @@ fn bounds_from_generic_predicates<'tcx>( } let generics = - if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) }; + if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) }; let where_clauses = if where_clauses.is_empty() { "".to_string() diff --git a/tests/ui/suggestions/apitit-unimplemented-method.rs b/tests/ui/suggestions/apitit-unimplemented-method.rs index b182e1939b3e3..c0cd709e2300a 100644 --- a/tests/ui/suggestions/apitit-unimplemented-method.rs +++ b/tests/ui/suggestions/apitit-unimplemented-method.rs @@ -4,9 +4,12 @@ extern crate dep; use dep::*; struct Local; + impl Trait for Local {} //~^ ERROR not all trait items implemented //~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }` -//~| HELP implement the missing item: `fn bar(_: impl Sized) { todo!() }` +//~| HELP implement the missing item: `fn bar(_: impl Sized) where Foo: MetaSized { todo!() }` +//~| HELP implement the missing item: `fn baz() { todo!() }` +//~| HELP implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }` fn main() {} diff --git a/tests/ui/suggestions/apitit-unimplemented-method.stderr b/tests/ui/suggestions/apitit-unimplemented-method.stderr index b309a64e95829..1f2e0ea2cad94 100644 --- a/tests/ui/suggestions/apitit-unimplemented-method.stderr +++ b/tests/ui/suggestions/apitit-unimplemented-method.stderr @@ -1,11 +1,13 @@ -error[E0046]: not all trait items implemented, missing: `foo`, `bar` - --> $DIR/apitit-unimplemented-method.rs:7:1 +error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `quux` + --> $DIR/apitit-unimplemented-method.rs:8:1 | LL | impl Trait for Local {} - | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation + | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz`, `quux` in implementation | = help: implement the missing item: `fn foo(_: impl Sized) { todo!() }` - = help: implement the missing item: `fn bar(_: impl Sized) { todo!() }` + = help: implement the missing item: `fn bar(_: impl Sized) where Foo: MetaSized { todo!() }` + = help: implement the missing item: `fn baz() { todo!() }` + = help: implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }` error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/auxiliary/dep.rs b/tests/ui/suggestions/auxiliary/dep.rs index ac0de418313c0..c28c8b8a52f51 100644 --- a/tests/ui/suggestions/auxiliary/dep.rs +++ b/tests/ui/suggestions/auxiliary/dep.rs @@ -1,4 +1,16 @@ +#![feature(sized_hierarchy)] + +use std::marker::MetaSized; + +pub struct Foo { + inner: T, +} + pub trait Trait { fn foo(_: impl Sized); - fn bar(_: impl Sized); + fn bar(_: impl Sized) + where + Foo: MetaSized; + fn baz<'a, const N: usize>(); + fn quux<'a: 'b, 'b, T: ?Sized>(); } From e652f97c6b71d485473c98bb0e5be1328db9bc45 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 11:10:45 -0500 Subject: [PATCH 0468/1900] Improve `core::ascii` coverage --- library/coretests/tests/ascii.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/coretests/tests/ascii.rs b/library/coretests/tests/ascii.rs index ce09ee507f11f..297aa114e006d 100644 --- a/library/coretests/tests/ascii.rs +++ b/library/coretests/tests/ascii.rs @@ -505,3 +505,10 @@ fn test_escape_ascii_iter() { let _ = it.advance_back_by(4); assert_eq!(it.to_string(), r#"fastpath\xffremainder"#); } + +#[test] +fn test_invalid_u8() { + for c in 128..=255 { + assert_eq!(core::ascii::Char::from_u8(c), None); + } +} From 94d3a8cb913da5823768710c0373c58bc5e1dce7 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:26:25 -0500 Subject: [PATCH 0469/1900] Improve `core::sync::atomic` coverage --- library/coretests/tests/atomic.rs | 244 +++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 4 deletions(-) diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs index b1ab443aa6e5e..d888bd0f55a11 100644 --- a/library/coretests/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs @@ -11,6 +11,38 @@ fn bool_() { assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false)); } +#[test] +#[should_panic = "there is no such thing as an acquire store"] +fn store_illegal_rt_store_acquire_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::Acquire; + a.store(true, ord); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release store"] +fn store_illegal_rt_store_acq_rel_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::AcqRel; + a.store(true, ord); +} + +#[test] +#[should_panic = "there is no such thing as a release load"] +fn store_illegal_rt_load_release_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::Release; + a.load(ord); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release load"] +fn store_illegal_rt_load_acq_rel_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::AcqRel; + a.load(ord); +} + #[test] fn bool_and() { let a = AtomicBool::new(true); @@ -283,25 +315,229 @@ fn atomic_compare_exchange() { static ATOMIC: AtomicIsize = AtomicIsize::new(0); ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Relaxed, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Relaxed, SeqCst).ok(); ATOMIC.compare_exchange(0, 1, Acquire, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Acquire, SeqCst).ok(); ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Release, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Release, SeqCst).ok(); ATOMIC.compare_exchange(0, 1, AcqRel, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok(); ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, AcqRel, SeqCst).ok(); + ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok(); ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Relaxed, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Relaxed, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, Acquire, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Acquire, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok(); ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, AcqRel, SeqCst).ok(); + ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok(); ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok(); } +#[test] +#[should_panic = "there is no such thing as an acquire-release failure ordering"] +fn atomic_compare_exchange_illegal_acq_rel() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = AcqRel; + + ATOMIC.compare_exchange(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as a release failure ordering"] +fn atomic_compare_exchange_illegal_release() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = Release; + + ATOMIC.compare_exchange(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release failure ordering"] +fn atomic_compare_exchange_weak_illegal_acq_rel() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = AcqRel; + + ATOMIC.compare_exchange_weak(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as a release failure ordering"] +fn atomic_compare_exchange_weak_illegal_release() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = Release; + + ATOMIC.compare_exchange_weak(0, 1, Relaxed, failure).ok(); +} + +#[test] +fn atomic_swap() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.swap(true, Relaxed), false); + assert_eq!(ATOMIC.swap(false, Acquire), true); + assert_eq!(ATOMIC.swap(true, Release), false); + assert_eq!(ATOMIC.swap(false, AcqRel), true); + assert_eq!(ATOMIC.swap(true, SeqCst), false); +} + +#[test] +fn atomic_add() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0); + + assert_eq!(ATOMIC.fetch_add(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_add(1, Acquire), 1); + assert_eq!(ATOMIC.fetch_add(1, Release), 2); + assert_eq!(ATOMIC.fetch_add(1, AcqRel), 3); + assert_eq!(ATOMIC.fetch_add(1, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_sub() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(5); + + assert_eq!(ATOMIC.fetch_sub(1, Relaxed), 5); + assert_eq!(ATOMIC.fetch_sub(1, Acquire), 4); + assert_eq!(ATOMIC.fetch_sub(1, Release), 3); + assert_eq!(ATOMIC.fetch_sub(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_sub(1, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + +#[test] +fn atomic_and_or() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.fetch_or(true, Relaxed), false); + assert_eq!(ATOMIC.fetch_and(false, Relaxed), true); + assert_eq!(ATOMIC.fetch_or(true, Acquire), false); + assert_eq!(ATOMIC.fetch_and(false, Acquire), true); + assert_eq!(ATOMIC.fetch_or(true, Release), false); + assert_eq!(ATOMIC.fetch_and(false, Release), true); + assert_eq!(ATOMIC.fetch_or(true, AcqRel), false); + assert_eq!(ATOMIC.fetch_and(false, AcqRel), true); + assert_eq!(ATOMIC.fetch_or(true, SeqCst), false); + assert_eq!(ATOMIC.fetch_and(false, SeqCst), true); + assert_eq!(ATOMIC.load(Relaxed), false); +} + +#[test] +fn atomic_nand() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0x13); + + assert_eq!(ATOMIC.fetch_nand(0x13, Relaxed), 0x13); + assert_eq!(ATOMIC.fetch_nand(0xec, Acquire), 0xec); + assert_eq!(ATOMIC.fetch_nand(0x13, Release), 0x13); + assert_eq!(ATOMIC.fetch_nand(0xec, AcqRel), 0xec); + assert_eq!(ATOMIC.fetch_nand(0x13, SeqCst), 0x13); + assert_eq!(ATOMIC.load(Relaxed), 0xec); +} + +#[test] +fn atomic_xor() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.fetch_xor(true, Relaxed), false); + assert_eq!(ATOMIC.fetch_xor(true, Acquire), true); + assert_eq!(ATOMIC.fetch_xor(true, Release), false); + assert_eq!(ATOMIC.fetch_xor(true, AcqRel), true); + assert_eq!(ATOMIC.fetch_xor(true, SeqCst), false); + assert_eq!(ATOMIC.load(Relaxed), true); +} + +#[test] +fn atomic_max() { + use Ordering::*; + + static ATOMIC: AtomicI8 = AtomicI8::new(0); + + assert_eq!(ATOMIC.fetch_max(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_max(2, Acquire), 1); + assert_eq!(ATOMIC.fetch_max(3, Release), 2); + assert_eq!(ATOMIC.fetch_max(4, AcqRel), 3); + assert_eq!(ATOMIC.fetch_max(5, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_umax() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0); + + assert_eq!(ATOMIC.fetch_max(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_max(2, Acquire), 1); + assert_eq!(ATOMIC.fetch_max(3, Release), 2); + assert_eq!(ATOMIC.fetch_max(4, AcqRel), 3); + assert_eq!(ATOMIC.fetch_max(5, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_min() { + use Ordering::*; + + static ATOMIC: AtomicI8 = AtomicI8::new(5); + + assert_eq!(ATOMIC.fetch_min(4, Relaxed), 5); + assert_eq!(ATOMIC.fetch_min(3, Acquire), 4); + assert_eq!(ATOMIC.fetch_min(2, Release), 3); + assert_eq!(ATOMIC.fetch_min(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_min(0, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + +#[test] +fn atomic_umin() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(5); + + assert_eq!(ATOMIC.fetch_min(4, Relaxed), 5); + assert_eq!(ATOMIC.fetch_min(3, Acquire), 4); + assert_eq!(ATOMIC.fetch_min(2, Release), 3); + assert_eq!(ATOMIC.fetch_min(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_min(0, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + /* FIXME(#110395) #[test] fn atomic_const_from() { From a535042e80a38196a58c27a8c95552546affe5dc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 16 Sep 2025 04:42:27 +0200 Subject: [PATCH 0470/1900] Do not run ui test if options specific to llvm are used when another codegen backend is used --- src/tools/compiletest/src/common.rs | 4 ++++ src/tools/compiletest/src/directives/needs.rs | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 143ccdcb9e520..6da102b1b5f11 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -203,6 +203,10 @@ impl CodegenBackend { Self::Llvm => "llvm", } } + + pub fn is_llvm(self) -> bool { + matches!(self, Self::Llvm) + } } /// Configuration for `compiletest` *per invocation*. diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index ee46f4c70cb8c..3b7a9478717f4 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -81,8 +81,8 @@ pub(super) fn handle_needs( }, Need { name: "needs-enzyme", - condition: config.has_enzyme, - ignore_reason: "ignored when LLVM Enzyme is disabled", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", }, Need { name: "needs-run-enabled", @@ -161,8 +161,8 @@ pub(super) fn handle_needs( }, Need { name: "needs-llvm-zstd", - condition: cache.llvm_zstd, - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression", + condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", }, Need { name: "needs-rustc-debug-assertions", @@ -279,7 +279,10 @@ pub(super) fn handle_needs( // Handled elsewhere. if name == "needs-llvm-components" { - return IgnoreDecision::Continue; + if config.default_codegen_backend.is_llvm() { + return IgnoreDecision::Continue; + } + return IgnoreDecision::Ignore { reason: "LLVM specific test".into() }; } let mut found_valid = false; From 6912631d3ead427848a559ef5af66ab6f30e79ed Mon Sep 17 00:00:00 2001 From: Tawan Muadmuenwai Date: Mon, 15 Sep 2025 21:24:57 +0700 Subject: [PATCH 0471/1900] Add span for struct tail recursion limit error --- .../src/type_check/canonical.rs | 16 ++++--- .../src/const_eval/valtrees.rs | 2 + compiler/rustc_hir_typeck/src/expectation.rs | 9 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + compiler/rustc_middle/src/error.rs | 2 + compiler/rustc_middle/src/ty/layout.rs | 2 + compiler/rustc_middle/src/ty/sty.rs | 3 +- compiler/rustc_middle/src/ty/util.rs | 17 +++++-- .../src/traits/project.rs | 1 + compiler/rustc_ty_utils/src/layout.rs | 46 ++++++++++--------- compiler/rustc_ty_utils/src/ty.rs | 1 + .../ui/did_you_mean/recursion_limit_deref.rs | 7 +-- .../did_you_mean/recursion_limit_deref.stderr | 18 ++++++-- ...te-instantiation-struct-tail-ice-114484.rs | 28 +++++------ ...nstantiation-struct-tail-ice-114484.stderr | 8 ++-- tests/ui/infinite/infinite-struct.rs | 3 +- tests/ui/infinite/infinite-struct.stderr | 6 ++- .../invalid/issue-114435-layout-type-err.rs | 3 +- .../solver-cycles/129541-recursive-struct.rs | 3 +- 19 files changed, 111 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 2627ed899a933..aece0bda34692 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -230,8 +230,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location: impl NormalizeLocation, ) -> Ty<'tcx> { let tcx = self.tcx(); + let body = self.body; + + let cause = ObligationCause::misc( + location.to_locations().span(body), + body.source.def_id().expect_local(), + ); + if self.infcx.next_trait_solver() { - let body = self.body; let param_env = self.infcx.param_env; // FIXME: Make this into a real type op? self.fully_perform_op( @@ -241,10 +247,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { |ocx| { let structurally_normalize = |ty| { ocx.structurally_normalize_ty( - &ObligationCause::misc( - location.to_locations().span(body), - body.source.def_id().expect_local(), - ), + &cause, param_env, ty, ) @@ -253,6 +256,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tail = tcx.struct_tail_raw( ty, + &cause, structurally_normalize, || {}, ); @@ -265,7 +269,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } else { let mut normalize = |ty| self.normalize(ty, location); - let tail = tcx.struct_tail_raw(ty, &mut normalize, || {}); + let tail = tcx.struct_tail_raw(ty, &cause, &mut normalize, || {}); normalize(tail) } } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 37c6c4a61d8d6..7c41258ebfe5f 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,6 +1,7 @@ use rustc_abi::{BackendRepr, FieldIdx, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -196,6 +197,7 @@ fn reconstruct_place_meta<'tcx>( // Traverse the type, and update `last_valtree` as we go. let tail = tcx.struct_tail_raw( layout.ty, + &ObligationCause::dummy(), |ty| ty, || { let branches = last_valtree.unwrap_branch(); diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 6d95b6917e296..2fbea5b61cfc6 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -1,3 +1,4 @@ +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -74,8 +75,14 @@ impl<'a, 'tcx> Expectation<'tcx> { /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + let span = match ty.kind() { + ty::Adt(adt_def, _) => fcx.tcx.def_span(adt_def.did()), + _ => fcx.tcx.def_span(fcx.body_id), + }; + let cause = ObligationCause::misc(span, fcx.body_id); + // FIXME: This is not right, even in the old solver... - match fcx.tcx.struct_tail_raw(ty, |ty| ty, || {}).kind() { + match fcx.tcx.struct_tail_raw(ty, &cause, |ty| ty, || {}).kind() { ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), _ => ExpectHasType(ty), } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7370124e800da..4563159314d15 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -424,6 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.references_error() { let tail = self.tcx.struct_tail_raw( ty, + &self.misc(span), |ty| { if self.next_trait_solver() { self.try_structurally_resolve_type(span, ty) diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dad402ec69616..e3e1393b5f9ae 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -71,6 +71,8 @@ pub enum TypeMismatchReason { #[diag(middle_recursion_limit_reached)] #[help] pub(crate) struct RecursionLimitReached<'tcx> { + #[primary_span] + pub span: Span, pub ty: Ty<'tcx>, pub suggested_limit: rustc_hir::limit::Limit, } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa43..572d7e037b79a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -22,6 +22,7 @@ use {rustc_abi as abi, rustc_hir as hir}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; +use crate::traits::ObligationCause; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt}; @@ -384,6 +385,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let tail = tcx.struct_tail_raw( pointee, + &ObligationCause::dummy(), |ty| match tcx.try_normalize_erasing_regions(typing_env, ty) { Ok(ty) => ty, Err(e) => Ty::new_error_with_message( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 755fc68d86f34..ea07f5c18dd12 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -23,6 +23,7 @@ use ty::util::IntTypeExt; use super::GenericParamDefKind; use crate::infer::canonical::Canonical; +use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, @@ -1640,7 +1641,7 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, ) -> Result, Ty<'tcx>> { - let tail = tcx.struct_tail_raw(self, normalize, || {}); + let tail = tcx.struct_tail_raw(self, &ObligationCause::dummy(), normalize, || {}); match tail.kind() { // Sized types ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 029586a9c554c..9e95a2c3cb37d 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -24,6 +24,7 @@ use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::query::Providers; +use crate::traits::ObligationCause; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -216,7 +217,12 @@ impl<'tcx> TyCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, ) -> Ty<'tcx> { let tcx = self; - tcx.struct_tail_raw(ty, |ty| tcx.normalize_erasing_regions(typing_env, ty), || {}) + tcx.struct_tail_raw( + ty, + &ObligationCause::dummy(), + |ty| tcx.normalize_erasing_regions(typing_env, ty), + || {}, + ) } /// Returns true if a type has metadata. @@ -248,6 +254,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn struct_tail_raw( self, mut ty: Ty<'tcx>, + cause: &ObligationCause<'tcx>, mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, // This is currently used to allow us to walk a ValTree // in lockstep with the type in order to get the ValTree branch that @@ -261,9 +268,11 @@ impl<'tcx> TyCtxt<'tcx> { Limit(0) => Limit(2), limit => limit * 2, }; - let reported = self - .dcx() - .emit_err(crate::error::RecursionLimitReached { ty, suggested_limit }); + let reported = self.dcx().emit_err(crate::error::RecursionLimitReached { + span: cause.span, + ty, + suggested_limit, + }); return Ty::new_error(self, reported); } match *ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 884d53732fe2a..042d6def84c33 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1057,6 +1057,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( Some(LangItem::PointeeTrait) => { let tail = selcx.tcx().struct_tail_raw( self_ty, + &obligation.cause, |ty| { // We throw away any obligations we get from this, since we normalize // and confirm these obligations once again during confirmation diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2adc..c48d167011639 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -10,6 +10,7 @@ use rustc_hashes::Hash64; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{ FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, }; @@ -390,30 +391,31 @@ fn layout_of_uncached<'tcx>( let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]); - let metadata_ty = - match tcx.try_normalize_erasing_regions(cx.typing_env, pointee_metadata) { - Ok(metadata_ty) => metadata_ty, - Err(mut err) => { - // Usually `::Metadata` can't be normalized because - // its struct tail cannot be normalized either, so try to get a - // more descriptive layout error here, which will lead to less confusing - // diagnostics. - // - // We use the raw struct tail function here to get the first tail - // that is an alias, which is likely the cause of the normalization - // error. - match tcx.try_normalize_erasing_regions( - cx.typing_env, - tcx.struct_tail_raw(pointee, |ty| ty, || {}), - ) { - Ok(_) => {} - Err(better_err) => { - err = better_err; - } + let metadata_ty = match tcx + .try_normalize_erasing_regions(cx.typing_env, pointee_metadata) + { + Ok(metadata_ty) => metadata_ty, + Err(mut err) => { + // Usually `::Metadata` can't be normalized because + // its struct tail cannot be normalized either, so try to get a + // more descriptive layout error here, which will lead to less confusing + // diagnostics. + // + // We use the raw struct tail function here to get the first tail + // that is an alias, which is likely the cause of the normalization + // error. + match tcx.try_normalize_erasing_regions( + cx.typing_env, + tcx.struct_tail_raw(pointee, &ObligationCause::dummy(), |ty| ty, || {}), + ) { + Ok(_) => {} + Err(better_err) => { + err = better_err; } - return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); } - }; + return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); + } + }; let metadata_layout = cx.layout_of(metadata_ty)?; // If the metadata is a 1-zst, then the pointer is thin. diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index a5987757dc34d..64e62e173876f 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -353,6 +353,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) let tail = tcx.struct_tail_raw( tcx.type_of(impl_def_id).instantiate_identity(), + &cause, |ty| { ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { Ty::new_error_with_message( diff --git a/tests/ui/did_you_mean/recursion_limit_deref.rs b/tests/ui/did_you_mean/recursion_limit_deref.rs index e53007388af25..3ae956b751d84 100644 --- a/tests/ui/did_you_mean/recursion_limit_deref.rs +++ b/tests/ui/did_you_mean/recursion_limit_deref.rs @@ -1,3 +1,6 @@ +//~ ERROR reached the recursion limit finding the struct tail for `K` +//~| ERROR reached the recursion limit finding the struct tail for `Bottom` + // Test that the recursion limit can be changed and that the compiler // suggests a fix. In this case, we have a long chain of Deref impls // which will cause an overflow during the autoderef loop. @@ -9,6 +12,7 @@ macro_rules! link { ($outer:ident, $inner:ident) => { struct $outer($inner); + //~^ ERROR reached the recursion limit finding the struct tail for `Bottom` impl $outer { fn new() -> $outer { @@ -51,6 +55,3 @@ fn main() { let x: &Bottom = &t; //~ ERROR mismatched types //~^ error recursion limit } - -//~? ERROR reached the recursion limit finding the struct tail for `K` -//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/did_you_mean/recursion_limit_deref.stderr b/tests/ui/did_you_mean/recursion_limit_deref.stderr index 23341ec6bdc27..faa85dc5ae98c 100644 --- a/tests/ui/did_you_mean/recursion_limit_deref.stderr +++ b/tests/ui/did_you_mean/recursion_limit_deref.stderr @@ -6,8 +6,20 @@ error: reached the recursion limit finding the struct tail for `Bottom` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` +error: reached the recursion limit finding the struct tail for `Bottom` + --> $DIR/recursion_limit_deref.rs:14:9 + | +LL | struct $outer($inner); + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | link!(A, B); + | ----------- in this macro invocation + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` + = note: this error originates in the macro `link` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0055]: reached the recursion limit while auto-dereferencing `J` - --> $DIR/recursion_limit_deref.rs:51:22 + --> $DIR/recursion_limit_deref.rs:55:22 | LL | let x: &Bottom = &t; | ^^ deref recursion limit reached @@ -15,7 +27,7 @@ LL | let x: &Bottom = &t; = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_deref`) error[E0308]: mismatched types - --> $DIR/recursion_limit_deref.rs:51:22 + --> $DIR/recursion_limit_deref.rs:55:22 | LL | let x: &Bottom = &t; | ------- ^^ expected `&Bottom`, found `&Top` @@ -25,7 +37,7 @@ LL | let x: &Bottom = &t; = note: expected reference `&Bottom` found reference `&Top` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0055, E0308. For more information about an error, try `rustc --explain E0055`. diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs index f7117368ece7b..b8ea353df9385 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs @@ -1,4 +1,17 @@ -//~ ERROR reached the recursion limit while instantiating `` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit while instantiating ` as MyTrait>::virtualize` + //@ build-fail //@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes @@ -72,16 +85,3 @@ fn main() { let test = SomeData([0; 256]); test.virtualize(); } - -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr index faf9cbe2318c4..deccc88e64fa5 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr @@ -18,7 +18,7 @@ error: reached the recursion limit finding the struct tail for `[u8; 256]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ error: reached the recursion limit finding the struct tail for `SomeData<256>` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ error: reached the recursion limit finding the struct tail for `VirtualWrapper>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | unsafe { virtualize_my_trait(L, self) } error: reached the recursion limit while instantiating ` as MyTrait>::virtualize` | note: ` as MyTrait>::virtualize` defined here - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:24:5 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:37:5 | LL | fn virtualize(&self) -> &dyn MyTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/infinite/infinite-struct.rs b/tests/ui/infinite/infinite-struct.rs index fd47a4ec9cc6a..d784455824604 100644 --- a/tests/ui/infinite/infinite-struct.rs +++ b/tests/ui/infinite/infinite-struct.rs @@ -1,6 +1,7 @@ struct Take(Take); //~^ ERROR has infinite size //~| ERROR cycle +//~| ERROR reached the recursion limit finding the struct tail for `Take` // check that we don't hang trying to find the tail of a recursive struct (#79437) fn foo() -> Take { @@ -15,5 +16,3 @@ struct Foo { //~ ERROR has infinite size struct Bar([T; 1]); fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `Take` diff --git a/tests/ui/infinite/infinite-struct.stderr b/tests/ui/infinite/infinite-struct.stderr index 5896aec399dc4..0d1ec4989aa53 100644 --- a/tests/ui/infinite/infinite-struct.stderr +++ b/tests/ui/infinite/infinite-struct.stderr @@ -10,7 +10,7 @@ LL | struct Take(Box); | ++++ + error[E0072]: recursive type `Foo` has infinite size - --> $DIR/infinite-struct.rs:11:1 + --> $DIR/infinite-struct.rs:12:1 | LL | struct Foo { | ^^^^^^^^^^ @@ -23,6 +23,10 @@ LL | x: Bar>, | ++++ + error: reached the recursion limit finding the struct tail for `Take` + --> $DIR/infinite-struct.rs:1:1 + | +LL | struct Take(Take); + | ^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` diff --git a/tests/ui/invalid/issue-114435-layout-type-err.rs b/tests/ui/invalid/issue-114435-layout-type-err.rs index 07f310478d3c9..ae04759af5df2 100644 --- a/tests/ui/invalid/issue-114435-layout-type-err.rs +++ b/tests/ui/invalid/issue-114435-layout-type-err.rs @@ -1,3 +1,4 @@ +//~ ERROR reached the recursion limit finding the struct tail for `Bottom` //@ check-fail //@ compile-flags: --crate-type lib -Cdebuginfo=2 @@ -40,5 +41,3 @@ link!(J, K); link!(K, Bottom); fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index 723179302e30a..c5c2c27b28267 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -1,3 +1,4 @@ +//~ ERROR reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` // Regression test for #129541 //@ revisions: unique_curr unique_next multiple_curr multiple_next @@ -24,5 +25,3 @@ struct Hello { } fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` From 9c423796bbbb989bac15524d78cdc6d46641e5da Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 16:31:32 -0500 Subject: [PATCH 0472/1900] bootstrap: emit hint if a config key is used in the wrong section --- src/bootstrap/src/core/config/config.rs | 109 +++++++++++++++++++++--- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 678a9b6395228..0213047f3a140 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1853,13 +1853,7 @@ fn load_toml_config( } else { toml_path.clone() }); - ( - get_toml(&toml_path).unwrap_or_else(|e| { - eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display()); - exit!(2); - }), - path, - ) + (get_toml(&toml_path).unwrap_or_else(|e| bad_config(&toml_path, e)), path) } else { (TomlConfig::default(), None) } @@ -1892,10 +1886,8 @@ fn postprocess_toml( .unwrap() .join(include_path); - let included_toml = get_toml(&include_path).unwrap_or_else(|e| { - eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display()); - exit!(2); - }); + let included_toml = + get_toml(&include_path).unwrap_or_else(|e| bad_config(&include_path, e)); toml.merge( Some(include_path), &mut Default::default(), @@ -2398,3 +2390,98 @@ pub(crate) fn read_file_by_commit<'a>( git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); git.run_capture_stdout(dwn_ctx.exec_ctx).stdout() } + +fn bad_config(toml_path: &Path, e: toml::de::Error) -> ! { + eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display()); + let e_s = e.to_string(); + if e_s.contains("unknown field") + && let Some(field_name) = e_s.split("`").nth(1) + && let sections = find_correct_section_for_field(field_name) + && !sections.is_empty() + { + if sections.len() == 1 { + match sections[0] { + WouldBeValidFor::TopLevel { is_section } => { + if is_section { + eprintln!( + "hint: section name `{field_name}` used as a key within a section" + ); + } else { + eprintln!("hint: try using `{field_name}` as a top level key"); + } + } + WouldBeValidFor::Section(section) => { + eprintln!("hint: try moving `{field_name}` to the `{section}` section") + } + } + } else { + eprintln!( + "hint: `{field_name}` would be valid {}", + join_oxford_comma(sections.iter(), "or"), + ); + } + } + + exit!(2); +} + +#[derive(Copy, Clone, Debug)] +enum WouldBeValidFor { + TopLevel { is_section: bool }, + Section(&'static str), +} + +fn join_oxford_comma( + mut parts: impl ExactSizeIterator, + conj: &str, +) -> String { + use std::fmt::Write; + let mut out = String::new(); + + assert!(parts.len() > 1); + while let Some(part) = parts.next() { + if parts.len() == 0 { + write!(&mut out, "{conj} {part}") + } else { + write!(&mut out, "{part}, ") + } + .unwrap(); + } + out +} + +impl std::fmt::Display for WouldBeValidFor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::TopLevel { .. } => write!(f, "at top level"), + Self::Section(section_name) => write!(f, "in section `{section_name}`"), + } + } +} + +fn find_correct_section_for_field(field_name: &str) -> Vec { + let sections = ["build", "install", "llvm", "gcc", "rust", "dist"]; + sections + .iter() + .map(Some) + .chain([None]) + .filter_map(|section_name| { + let dummy_config_str = if let Some(section_name) = section_name { + format!("{section_name}.{field_name} = 0\n") + } else { + format!("{field_name} = 0\n") + }; + let is_unknown_field = toml::from_str::(&dummy_config_str) + .and_then(TomlConfig::deserialize) + .err() + .is_some_and(|e| e.to_string().contains("unknown field")); + if is_unknown_field { + None + } else { + Some(section_name.copied().map(WouldBeValidFor::Section).unwrap_or_else(|| { + WouldBeValidFor::TopLevel { is_section: sections.contains(&field_name) } + })) + } + }) + .collect() +} From 91cf067f7174eac7ec0492e829bcbb844d4471d7 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 15 Sep 2025 19:57:08 +0200 Subject: [PATCH 0473/1900] ci: x86_64-gnu-tools: Add `--test-args` regression test --- src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index ff9fedad6567d..14cf63b94d481 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -30,3 +30,10 @@ cat /tmp/toolstate/toolstates.json python3 "$X_PY" test --stage 2 check-tools python3 "$X_PY" test --stage 2 src/tools/clippy python3 "$X_PY" test --stage 2 src/tools/rustfmt + +# The below is a regression test for https://github.com/rust-lang/rust/pull/146501#issuecomment-3292608398. +# The bug caused 0 tests to run. By grepping on that 1 test is run we prevent regressing. +# Any test can be used. We arbitrarily chose `tests/ui/lint/unused/unused-result.rs`. +python3 "$X_PY" test tests/ui --test-args tests/ui/lint/unused/unused-result.rs --force-rerun | +grep --fixed-strings 'test result: ok. 1 passed; 0 failed; 0 ignored;' || +( echo "ERROR: --test-args functionality is broken" && exit 1 ) From c916e8886b3a1c6f32daf467c1c3d8316e35a6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:08:43 -0700 Subject: [PATCH 0474/1900] fmt --- .../src/error_reporting/infer/need_type_info.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 94772be16be20..75283dc4ffaa6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -4,11 +4,12 @@ use std::path::PathBuf; use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir::{self as hir, PatKind}; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; +use rustc_hir::{ + self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; From ed85f9846da9abfad09bb5732bc18d6c39fbc9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:21:29 -0700 Subject: [PATCH 0475/1900] remove redundant test --- tests/ui/closures/varargs-in-closure-isnt-supported.rs | 10 ---------- .../closures/varargs-in-closure-isnt-supported.stderr | 10 ---------- 2 files changed, 20 deletions(-) delete mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.rs delete mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.stderr diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs deleted file mode 100644 index 4de78bef14d30..0000000000000 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.rs +++ /dev/null @@ -1,10 +0,0 @@ -// var-args are not supported in closures, ensure we don't misdirect people (#146489) -#![feature(c_variadic)] - -unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { - let mut lol = |...| (); //~ ERROR: unexpected `...` - unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here - //~| NOTE: not a valid pattern -} - -fn main() {} diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr deleted file mode 100644 index a645741a52771..0000000000000 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unexpected `...` - --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 - | -LL | let mut lol = |...| (); - | ^^^ not a valid pattern - | - = note: C-variadic type `...` is not allowed here - -error: aborting due to 1 previous error - From f71f75669563cc9fb74c629937046c1f13d3698e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 17 Sep 2025 03:03:00 +0900 Subject: [PATCH 0476/1900] Fix "sync-from-ra" for `rust-lang/rust` --- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 3 +++ src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 02fdbf0ebf0df..37d5347fe7ea1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -39,6 +39,9 @@ extern crate rustc_next_trait_solver; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_data_structures as ena; + mod builder; mod chalk_db; mod chalk_ext; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 0225deebe4f31..99a501b05341c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -40,5 +40,8 @@ pub type AliasTy<'db> = rustc_type_ir::AliasTy>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; +#[cfg(feature = "in-rust-tree")] +use rustc_data_structure::sorted_map::index_map as indexmap; + pub type FxIndexMap = indexmap::IndexMap>; From 8306a2f02e6c56447ff05b19b4b73c5c861e34fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:24:51 -0700 Subject: [PATCH 0477/1900] Reword note --- compiler/rustc_parse/messages.ftl | 2 +- tests/ui/c-variadic/no-closure.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 6d9521c7d2be0..3a4c9348b3275 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -189,7 +189,7 @@ parse_dotdotdot = unexpected token: `...` parse_dotdotdot_rest_pattern = unexpected `...` .label = not a valid pattern .suggestion = for a rest pattern, use `..` instead of `...` - .note = C-variadic type `...` is not allowed here + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index ad635a29ab486..4b553c215979a 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -10,7 +10,7 @@ error: unexpected `...` LL | let f = |...| {}; | ^^^ not a valid pattern | - = note: C-variadic type `...` is not allowed here + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0743]: C-variadic type `...` may not be nested inside another type --> $DIR/no-closure.rs:13:17 From 401857aaa1e21df49e0d013b9856ab6dbe34870b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 16 Sep 2025 20:20:02 +0200 Subject: [PATCH 0478/1900] cmse: fix 'region variables should not be hashed' --- .../src/hir_ty_lowering/cmse.rs | 1 + .../undeclared-lifetime.rs | 20 +++++++++++++++++++ .../undeclared-lifetime.stderr | 19 ++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e60..ae9558cae3b96 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -128,6 +128,7 @@ fn is_valid_cmse_inputs<'tcx>( // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); for (index, ty) in fn_sig.inputs().iter().enumerate() { let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?; diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs new file mode 100644 index 0000000000000..5fa5b74c0c0b9 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.rs @@ -0,0 +1,20 @@ +//@ add-core-stubs +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Cincremental=true +//@ needs-llvm-components: arm +#![feature(abi_cmse_nonsecure_call, no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// A regression test for https://github.com/rust-lang/rust/issues/131639. +// NOTE: -Cincremental=true was required for triggering the bug. + +fn foo() { + id::(PhantomData); + //~^ ERROR use of undeclared lifetime name `'a` +} + +fn id(x: PhantomData) -> PhantomData { + x +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr new file mode 100644 index 0000000000000..4aca17e73544d --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/undeclared-lifetime.stderr @@ -0,0 +1,19 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/undeclared-lifetime.rs:14:43 + | +LL | id::(PhantomData); + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the type lifetime-generic with a new `'a` lifetime + | +LL | id:: extern "cmse-nonsecure-call" fn(&'a ())>(PhantomData); + | +++++++ +help: consider introducing lifetime `'a` here + | +LL | fn foo<'a>() { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0261`. From e9270e3cba3da56d4d83ed74f648e53b041cb263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:38:08 -0700 Subject: [PATCH 0479/1900] Detect top-level `...` in argument type When writing something like the expression `|_: ...| {}`, we now detect the `...` during parsing explicitly instead of relying on the detection in `parse_ty_common` so that we don't talk about "nested `...` are not supported". ``` error: unexpected `...` --> $DIR/no-closure.rs:6:35 | LL | const F: extern "C" fn(...) = |_: ...| {}; | ^^^ | = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list ``` --- compiler/rustc_parse/messages.ftl | 3 +++ compiler/rustc_parse/src/errors.rs | 8 ++++++++ compiler/rustc_parse/src/parser/ty.rs | 13 +++++++++++-- tests/ui/c-variadic/no-closure.rs | 8 ++++++-- tests/ui/c-variadic/no-closure.stderr | 13 ++++++++----- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 3a4c9348b3275..f83cf645f829d 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -191,6 +191,9 @@ parse_dotdotdot_rest_pattern = unexpected `...` .suggestion = for a rest pattern, use `..` instead of `...` .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list +parse_dotdotdot_rest_type = unexpected `...` + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2b107fe35d592..1abeee6fe433e 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3032,6 +3032,14 @@ pub(crate) struct NestedCVariadicType { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_dotdotdot_rest_type)] +#[note] +pub(crate) struct InvalidCVariadicType { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_invalid_dyn_keyword)] #[help] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 23aaafac934ee..65347496599d7 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -15,8 +15,8 @@ use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg, - HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NestedCVariadicType, ReturnTypesUseThinArrow, + HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut, + NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::parser::item::FrontMatterParsingMode; use crate::parser::{FnContext, FnParseMode}; @@ -106,6 +106,15 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, Box> { + if self.token == token::DotDotDot { + // We special case this so that we don't talk about "nested C-variadics" in types. + // We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about + // things like `Vec<...>`. + let span = self.token.span; + self.bump(); + let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span })); + return Ok(self.mk_ty(span, kind)); + } // Make sure deeply nested types don't overflow the stack. ensure_sufficient_stack(|| { self.parse_ty_common( diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs index a5b791fbca800..830ed962a8c4a 100644 --- a/tests/ui/c-variadic/no-closure.rs +++ b/tests/ui/c-variadic/no-closure.rs @@ -4,13 +4,17 @@ // Check that `...` in closures is rejected. const F: extern "C" fn(...) = |_: ...| {}; -//~^ ERROR C-variadic type `...` may not be nested inside another type +//~^ ERROR: unexpected `...` +//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list fn foo() { let f = |...| {}; //~^ ERROR: unexpected `...` + //~| NOTE: not a valid pattern + //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list let f = |_: ...| {}; - //~^ ERROR C-variadic type `...` may not be nested inside another type + //~^ ERROR: unexpected `...` + //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list f(1i64) } diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index 4b553c215979a..0946c4632e6e4 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -1,23 +1,26 @@ -error[E0743]: C-variadic type `...` may not be nested inside another type +error: unexpected `...` --> $DIR/no-closure.rs:6:35 | LL | const F: extern "C" fn(...) = |_: ...| {}; | ^^^ + | + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: unexpected `...` - --> $DIR/no-closure.rs:10:14 + --> $DIR/no-closure.rs:11:14 | LL | let f = |...| {}; | ^^^ not a valid pattern | = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error[E0743]: C-variadic type `...` may not be nested inside another type - --> $DIR/no-closure.rs:13:17 +error: unexpected `...` + --> $DIR/no-closure.rs:16:17 | LL | let f = |_: ...| {}; | ^^^ + | + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0743`. From 580b4891aa23c0625539bf5ee55270f27af09072 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 7 Aug 2025 14:29:00 -0700 Subject: [PATCH 0480/1900] Update the minimum external LLVM to 20 --- compiler/rustc_codegen_llvm/src/builder.rs | 9 +- compiler/rustc_codegen_llvm/src/context.rs | 29 ---- compiler/rustc_codegen_llvm/src/llvm_util.rs | 57 +------ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 30 +--- .../rustc_codegen_ssa/src/traits/builder.rs | 38 ++++- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 140 ++++-------------- src/bootstrap/src/core/build_steps/llvm.rs | 4 +- src/ci/docker/README.md | 8 +- .../Dockerfile | 8 +- .../host-x86_64/x86_64-gnu-llvm-19/Dockerfile | 66 --------- src/ci/github-actions/jobs.yml | 35 +---- .../riscv-soft-abi-with-float-features.rs | 10 +- tests/assembly-llvm/x86_64-bigint-helpers.rs | 1 - tests/assembly-llvm/x86_64-cmp.rs | 78 +++------- .../comparison-operators-2-struct.rs | 1 - .../comparison-operators-2-tuple.rs | 1 - tests/codegen-llvm/enum/enum-aggregate.rs | 1 - .../codegen-llvm/enum/enum-discriminant-eq.rs | 1 - tests/codegen-llvm/integer-cmp.rs | 36 +---- .../intrinsics/three_way_compare.rs | 1 - .../issues/and-masked-comparison-131162.rs | 1 - tests/codegen-llvm/issues/issue-101082.rs | 1 - tests/codegen-llvm/issues/issue-129795.rs | 1 - .../iter-max-no-unwrap-failed-129583.rs | 1 - .../issues/looping-over-ne-bytes-133528.rs | 1 - tests/codegen-llvm/option-niche-eq.rs | 1 - .../slice-last-elements-optimization.rs | 1 - tests/codegen-llvm/swap-small-types.rs | 1 - tests/codegen-llvm/try_question_mark_nop.rs | 31 ++-- tests/codegen-llvm/union-aggregate.rs | 1 - tests/ui/abi/sparcv8plus-llvm19.rs | 42 ------ tests/ui/abi/sparcv8plus-llvm19.sparc.stderr | 8 - .../sparcv8plus-llvm19.sparc_cpu_v9.stderr | 8 - ...-llvm19.sparc_cpu_v9_feature_v8plus.stderr | 8 - ...cv8plus-llvm19.sparc_feature_v8plus.stderr | 8 - .../abi/sparcv8plus-llvm19.sparcv8plus.stderr | 8 - tests/ui/abi/sparcv8plus.rs | 1 - tests/ui/abi/sparcv8plus.sparc.stderr | 2 +- tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr | 2 +- ...cv8plus.sparc_cpu_v9_feature_v8plus.stderr | 2 +- .../sparcv8plus.sparc_feature_v8plus.stderr | 2 +- tests/ui/abi/sparcv8plus.sparcv8plus.stderr | 2 +- .../bad-reg.loongarch32_ilp32d.stderr | 12 +- .../bad-reg.loongarch32_ilp32s.stderr | 20 +-- .../bad-reg.loongarch64_lp64d.stderr | 12 +- .../bad-reg.loongarch64_lp64s.stderr | 20 +-- tests/ui/asm/loongarch/bad-reg.rs | 1 - 47 files changed, 157 insertions(+), 595 deletions(-) rename src/ci/docker/host-aarch64/{aarch64-gnu-llvm-19 => aarch64-gnu-llvm-20}/Dockerfile (92%) delete mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.rs delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7d0691366e602..0f17cc9063a87 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1091,16 +1091,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ty: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, - ) -> Option { - // FIXME: See comment on the definition of `three_way_compare`. - if crate::llvm_util::get_version() < (20, 0, 0) { - return None; - } - + ) -> Self::Value { let size = ty.primitive_size(self.tcx); let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" }; - Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])) + self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]) } /* Miscellaneous instructions */ diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a4d..5f4385c9c6a20 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -172,35 +172,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (20, 0, 0) { - if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { - // LLVM 20 defines three additional address spaces for alternate - // pointer kinds used in Windows. - // See https://github.com/llvm/llvm-project/pull/111879 - target_data_layout = - target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", ""); - } - if sess.target.arch.starts_with("sparc") { - // LLVM 20 updates the sparc layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/106951 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("mips64") { - // LLVM 20 updates the mips64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/112084 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("powerpc64") { - // LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/118004 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") { - // LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/119204 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - } if llvm_version < (21, 0, 0) { if sess.target.arch == "nvptx64" { // LLVM 21 updated the default layout on nvptx: https://github.com/llvm/llvm-project/pull/124961 diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index d927ffd78c298..8461c8b03d5a4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -246,9 +246,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("perfmon")), ("aarch64", "paca") => Some(LLVMFeature::new("pauth")), ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")), - // Before LLVM 20 those two features were packaged together as b16b16 - ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), - ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")), // Rust ties fp and neon together. ("aarch64", "neon") => Some(LLVMFeature::with_dependencies( @@ -262,57 +259,17 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), - // NVPTX targets added in LLVM 20 - ("nvptx64", "sm_100") if get_version().0 < 20 => None, - ("nvptx64", "sm_100a") if get_version().0 < 20 => None, - ("nvptx64", "sm_101") if get_version().0 < 20 => None, - ("nvptx64", "sm_101a") if get_version().0 < 20 => None, - ("nvptx64", "sm_120") if get_version().0 < 20 => None, - ("nvptx64", "sm_120a") if get_version().0 < 20 => None, - ("nvptx64", "ptx86") if get_version().0 < 20 => None, - ("nvptx64", "ptx87") if get_version().0 < 20 => None, // Filter out features that are not supported by the current LLVM version - ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") - if get_version().0 < 20 => - { - None - } ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, - // Filter out features that are not supported by the current LLVM version - ("riscv32" | "riscv64", "zacas" | "rva23u64" | "supm") if get_version().0 < 20 => None, - ( - "s390x", - "message-security-assist-extension12" - | "concurrent-functions" - | "miscellaneous-extensions-4" - | "vector-enhancements-3" - | "vector-packed-decimal-enhancement-3", - ) if get_version().0 < 20 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - // Support for `wide-arithmetic` will first land in LLVM 20 as part of - // llvm/llvm-project#111598 - ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None, ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used". - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28 - // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available". - // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26 - ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")), ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), - // These new `amx` variants and `movrs` were introduced in LLVM20 - ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose") - if get_version().0 < 20 => - { - None - } - ("x86", "movrs") if get_version().0 < 20 => None, ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), - ("x86", "avx10.2") if get_version().0 < 20 => None, - ("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")), + ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( "egpr", smallvec![ @@ -716,17 +673,7 @@ pub(crate) fn global_llvm_features( }; // Features implied by an implicit or explicit `--target`. - features.extend( - sess.target - .features - .split(',') - .filter(|v| !v.is_empty()) - // Drop +v8plus feature introduced in LLVM 20. - // (Hard-coded target features do not go through `to_llvm_feature` since they already - // are LLVM feature names, hence we need a special case here.) - .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) - .map(String::from), - ); + features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from)); if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind { features.push("+exception-handling".into()); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f8755874014d3..4c9bb7cf8a8f9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -901,36 +901,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::BinOp::Cmp => { - use std::cmp::Ordering; assert!(!is_float); - if let Some(value) = bx.three_way_compare(lhs_ty, lhs, rhs) { - return value; - } - let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); - if bx.cx().tcx().sess.opts.optimize == OptLevel::No { - // FIXME: This actually generates tighter assembly, and is a classic trick - // - // However, as of 2023-11 it optimizes worse in things like derived - // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it - // better (see ), it'll - // be worth trying it in optimized builds as well. - let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs); - let gtext = bx.zext(is_gt, bx.type_i8()); - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let ltext = bx.zext(is_lt, bx.type_i8()); - bx.unchecked_ssub(gtext, ltext) - } else { - // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, - // from . - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs); - let ge = bx.select( - is_ne, - bx.cx().const_i8(Ordering::Greater as i8), - bx.cx().const_i8(Ordering::Equal as i8), - ); - bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) - } + bx.three_way_compare(lhs_ty, lhs, rhs) } mir::BinOp::AddWithOverflow | mir::BinOp::SubWithOverflow diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f417d1a7bf724..422fa715de109 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{AtomicOrdering, Instance, Ty}; use rustc_session::config::OptLevel; @@ -405,15 +406,38 @@ pub trait BuilderMethods<'a, 'tcx>: fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; /// Returns `-1` if `lhs < rhs`, `0` if `lhs == rhs`, and `1` if `lhs > rhs`. - // FIXME: Move the default implementation from `codegen_scalar_binop` into this method and - // remove the `Option` return once LLVM 20 is the minimum version. fn three_way_compare( &mut self, - _ty: Ty<'tcx>, - _lhs: Self::Value, - _rhs: Self::Value, - ) -> Option { - None + ty: Ty<'tcx>, + lhs: Self::Value, + rhs: Self::Value, + ) -> Self::Value { + use std::cmp::Ordering; + let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed()); + if self.cx().sess().opts.optimize == OptLevel::No { + // FIXME: This actually generates tighter assembly, and is a classic trick + // + // However, as of 2023-11 it optimizes worse in things like derived + // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it + // better (see ), it'll + // be worth trying it in optimized builds as well. + let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs); + let gtext = self.zext(is_gt, self.type_i8()); + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let ltext = self.zext(is_lt, self.type_i8()); + self.unchecked_ssub(gtext, ltext) + } else { + // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, + // from . + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs); + let ge = self.select( + is_ne, + self.cx().const_i8(Ordering::Greater as i8), + self.cx().const_i8(Ordering::Equal as i8), + ); + self.select(is_lt, self.cx().const_i8(Ordering::Less as i8), ge) + } } fn memcpy( diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 91d11ba317a61..ab5d5c03e817a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -344,7 +344,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; if (ArgsCstrBuff != nullptr) { -#if LLVM_VERSION_GE(20, 0) size_t buffer_offset = 0; assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); auto Arg0 = std::string(ArgsCstrBuff); @@ -362,33 +361,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( OS.flush(); Options.MCOptions.Argv0 = Arg0; Options.MCOptions.CommandlineArgs = CommandlineArgs; -#else - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - - const size_t arg0_len = std::strlen(ArgsCstrBuff); - char *arg0 = new char[arg0_len + 1]; - memcpy(arg0, ArgsCstrBuff, arg0_len); - arg0[arg0_len] = '\0'; - buffer_offset += arg0_len + 1; - - const size_t num_cmd_arg_strings = std::count( - &ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0'); - - std::string *cmd_arg_strings = new std::string[num_cmd_arg_strings]; - for (size_t i = 0; i < num_cmd_arg_strings; ++i) { - assert(buffer_offset < ArgsCstrBuffLen); - const size_t len = std::strlen(ArgsCstrBuff + buffer_offset); - cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len); - buffer_offset += len + 1; - } - - assert(buffer_offset == ArgsCstrBuffLen); - - Options.MCOptions.Argv0 = arg0; - Options.MCOptions.CommandLineArgs = - llvm::ArrayRef(cmd_arg_strings, num_cmd_arg_strings); -#endif } #if LLVM_VERSION_GE(21, 0) @@ -402,12 +374,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { -#if LLVM_VERSION_LT(20, 0) - MCTargetOptions &MCOptions = unwrap(TM)->Options.MCOptions; - delete[] MCOptions.Argv0; - delete[] MCOptions.CommandLineArgs.data(); -#endif - delete unwrap(TM); } @@ -688,14 +654,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( // the PassBuilder does not create a pipeline. std::vector> PipelineStartEPCallbacks; -#if LLVM_VERSION_GE(20, 0) std::vector> OptimizerLastEPCallbacks; -#else - std::vector> - OptimizerLastEPCallbacks; -#endif if (!IsLinkerPluginLTO && SanitizerOptions && SanitizerOptions->SanitizeCFI && !NoPrepopulatePasses) { @@ -747,12 +708,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeDataFlowABIList + SanitizerOptions->SanitizeDataFlowABIListLen); OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) { -#endif MPM.addPass(DataFlowSanitizerPass(ABIListFiles)); }); } @@ -763,66 +720,48 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false, /*EagerChecks=*/true); - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [Options](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [Options](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(MemorySanitizerPass(Options)); - }); + OptimizerLastEPCallbacks.push_back([Options](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(MemorySanitizerPass(Options)); + }); } if (SanitizerOptions->SanitizeThread) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(ModuleThreadSanitizerPass()); - MPM.addPass( - createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); - }); + OptimizerLastEPCallbacks.push_back([](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(ModuleThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + }); } if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level) { -#endif - auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; - AddressSanitizerOptions opts = AddressSanitizerOptions{ - CompileKernel, - SanitizerOptions->SanitizeAddressRecover || - SanitizerOptions->SanitizeKernelAddressRecover, - /*UseAfterScope=*/true, - AsanDetectStackUseAfterReturnMode::Runtime, - }; - MPM.addPass(AddressSanitizerPass( - opts, - /*UseGlobalGC*/ true, - // UseOdrIndicator should be false on windows machines - // https://reviews.llvm.org/D137227 - !TM->getTargetTriple().isOSWindows())); - }); + OptimizerLastEPCallbacks.push_back([SanitizerOptions, + TM](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; + AddressSanitizerOptions opts = AddressSanitizerOptions{ + CompileKernel, + SanitizerOptions->SanitizeAddressRecover || + SanitizerOptions->SanitizeKernelAddressRecover, + /*UseAfterScope=*/true, + AsanDetectStackUseAfterReturnMode::Runtime, + }; + MPM.addPass( + AddressSanitizerPass(opts, + /*UseGlobalGC*/ true, + // UseOdrIndicator should be false on windows + // machines https://reviews.llvm.org/D137227 + !TM->getTargetTriple().isOSWindows())); + }); } if (SanitizerOptions->SanitizeHWAddress) { OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { -#endif HWAddressSanitizerOptions opts( /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover, @@ -904,11 +843,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( for (const auto &C : PipelineStartEPCallbacks) C(MPM, OptLevel); for (const auto &C : OptimizerLastEPCallbacks) -#if LLVM_VERSION_GE(20, 0) C(MPM, OptLevel, ThinOrFullLTOPhase::None); -#else - C(MPM, OptLevel); -#endif } if (ExtraPassesLen) { @@ -1185,11 +1120,7 @@ struct LLVMRustThinLTOData { // Not 100% sure what these are, but they impact what's internalized and // what's inlined across modules, I believe. -#if LLVM_VERSION_GE(20, 0) FunctionImporter::ImportListsTy ImportLists; -#else - DenseMap ImportLists; -#endif DenseMap ExportLists; DenseMap ModuleToDefinedGVSummaries; StringMap> ResolvedODR; @@ -1531,13 +1462,8 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const auto &ExportList = Data->ExportLists.lookup(ModId); const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); -#if LLVM_VERSION_GE(20, 0) DenseSet CfiFunctionDefs; DenseSet CfiFunctionDecls; -#else - std::set CfiFunctionDefs; - std::set CfiFunctionDecls; -#endif // Based on the 'InProcessThinBackend' constructor in LLVM #if LLVM_VERSION_GE(21, 0) @@ -1556,15 +1482,9 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); #endif -#if LLVM_VERSION_GE(20, 0) Key = llvm::computeLTOCacheKey(conf, Data->Index, ModId, ImportList, ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); -#else - llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId, ImportList, - ExportList, ResolvedODR, DefinedGlobals, - CfiFunctionDefs, CfiFunctionDecls); -#endif auto OS = RawRustStringOstream(KeyOut); OS << Key.str(); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 83ed7430c397e..d43d261ad6c3f 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -619,11 +619,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) - && major >= 19 + && major >= 20 { return; } - panic!("\n\nbad LLVM version: {version}, need >=19\n\n") + panic!("\n\nbad LLVM version: {version}, need >=20\n\n") } fn configure_cmake( diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 488a6a2bce122..4ee02e9bf0055 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -14,9 +14,9 @@ To run a specific CI job locally, you can use the `citool` Rust crate: cargo run --manifest-path src/ci/citool/Cargo.toml run-local ``` -For example, to run the `x86_64-gnu-llvm-19-1` job: +For example, to run the `x86_64-gnu-llvm-20-1` job: ``` -cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-19-1 +cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-20-1 ``` The job will output artifacts in an `obj/` dir at the root of a repository. Note @@ -27,10 +27,10 @@ Docker image executed in the given CI job. while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. -For some Linux workflows (for example `x86_64-gnu-llvm-19-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-19-3` workflow, you can run the following script: +For some Linux workflows (for example `x86_64-gnu-llvm-20-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-20-3` workflow, you can run the following script: ``` -DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-19 +DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-20 ``` ## Local Development diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile similarity index 92% rename from src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile rename to src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile index e73fbe506f788..adbb1f03378a6 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:24.10 +FROM ubuntu:25.04 ARG DEBIAN_FRONTEND=noninteractive @@ -15,8 +15,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-19-tools \ - llvm-19-dev \ + llvm-20-tools \ + llvm-20-dev \ libedit-dev \ libssl-dev \ pkg-config \ @@ -43,7 +43,7 @@ ENV EXTERNAL_LLVM 1 # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=aarch64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-19 \ + --llvm-root=/usr/lib/llvm-20 \ --enable-llvm-link-shared \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile deleted file mode 100644 index 5cba7c564f164..0000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ /dev/null @@ -1,66 +0,0 @@ -FROM ubuntu:24.10 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - bzip2 \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-19-tools \ - llvm-19-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install powershell (universal package) so we can test x.ps1 on Linux -# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg --ignore-depends=libicu72 -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV EXTERNAL_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-19 \ - --enable-llvm-link-shared \ - --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10 - -COPY scripts/shared.sh /scripts/ - -COPY scripts/x86_64-gnu-llvm.sh /scripts/ -COPY scripts/x86_64-gnu-llvm2.sh /scripts/ -COPY scripts/x86_64-gnu-llvm3.sh /scripts/ -COPY scripts/stage_2_test_set1.sh /scripts/ -COPY scripts/stage_2_test_set2.sh /scripts/ - -ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 35b9456d37d2f..b3e3fe7d96aab 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -122,19 +122,19 @@ pr: # tidy. This speeds up the PR CI job by ~1 minute. SKIP_SUBMODULES: src/gcc <<: *job-linux-4c - - name: x86_64-gnu-llvm-19 + - name: x86_64-gnu-llvm-20 env: ENABLE_GCC_CODEGEN: "1" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-4c - - name: aarch64-gnu-llvm-19-1 + - name: aarch64-gnu-llvm-20-1 env: - IMAGE: aarch64-gnu-llvm-19 + IMAGE: aarch64-gnu-llvm-20 DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-aarch64-linux - - name: aarch64-gnu-llvm-19-2 + - name: aarch64-gnu-llvm-20-2 env: - IMAGE: aarch64-gnu-llvm-19 + IMAGE: aarch64-gnu-llvm-20 DOCKER_SCRIPT: stage_2_test_set2.sh <<: *job-aarch64-linux - name: x86_64-gnu-tools @@ -397,31 +397,6 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. - # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - name: x86_64-gnu-llvm-19-1 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: stage_2_test_set2.sh - <<: *job-linux-4c - - # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - name: x86_64-gnu-llvm-19-2 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: x86_64-gnu-llvm2.sh - <<: *job-linux-4c - - # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - name: x86_64-gnu-llvm-19-3 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: x86_64-gnu-llvm3.sh - <<: *job-linux-4c - - name: x86_64-gnu-nopt <<: *job-linux-4c diff --git a/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs b/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs index 72cbd3841c123..085ea9facd065 100644 --- a/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs +++ b/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs @@ -2,9 +2,6 @@ //@ assembly-output: emit-asm //@ compile-flags: --target riscv64imac-unknown-none-elf -Ctarget-feature=+f,+d //@ needs-llvm-components: riscv -//@ revisions: LLVM-PRE-20 LLVM-POST-20 -//@ [LLVM-PRE-20] max-llvm-major-version: 19 -//@ [LLVM-POST-20] min-llvm-version: 20 #![feature(no_core, lang_items, f16)] #![crate_type = "lib"] @@ -28,11 +25,8 @@ pub extern "C" fn read_f16(x: &f16) -> f16 { // CHECK-LABEL: read_f32 #[no_mangle] pub extern "C" fn read_f32(x: &f32) -> f32 { - // LLVM-PRE-20: flw fa5, 0(a0) - // LLVM-PRE-20-NEXT: fmv.x.w a0, fa5 - // LLVM-PRE-20-NEXT: ret - // LLVM-POST-20: lw a0, 0(a0) - // LLVM-POST-20-NEXT: ret + // CHECK: lw a0, 0(a0) + // CHECK-NEXT: ret *x } diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index c5efda58fd668..64aa025723856 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -2,7 +2,6 @@ //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -Copt-level=3 -C target-cpu=x86-64-v4 //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel -//@ min-llvm-version: 20 #![no_std] #![feature(bigint_helper_methods)] diff --git a/tests/assembly-llvm/x86_64-cmp.rs b/tests/assembly-llvm/x86_64-cmp.rs index 26c9013d96fc5..7e87171991d4d 100644 --- a/tests/assembly-llvm/x86_64-cmp.rs +++ b/tests/assembly-llvm/x86_64-cmp.rs @@ -1,12 +1,6 @@ -//@ revisions: LLVM-PRE-20-DEBUG LLVM-20-DEBUG LLVM-PRE-20-OPTIM LLVM-20-OPTIM -//@ [LLVM-PRE-20-DEBUG] compile-flags: -C opt-level=0 -//@ [LLVM-PRE-20-DEBUG] max-llvm-major-version: 19 -//@ [LLVM-20-DEBUG] compile-flags: -C opt-level=0 -//@ [LLVM-20-DEBUG] min-llvm-version: 20 -//@ [LLVM-PRE-20-OPTIM] compile-flags: -C opt-level=3 -//@ [LLVM-PRE-20-OPTIM] max-llvm-major-version: 19 -//@ [LLVM-20-OPTIM] compile-flags: -C opt-level=3 -//@ [LLVM-20-OPTIM] min-llvm-version: 20 +//@ revisions: DEBUG OPTIM +//@ [DEBUG] compile-flags: -C opt-level=0 +//@ [OPTIM] compile-flags: -C opt-level=3 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -C llvm-args=-x86-asm-syntax=intel //@ only-x86_64 @@ -19,61 +13,31 @@ use std::intrinsics::three_way_compare; #[no_mangle] // CHECK-LABEL: signed_cmp: pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setg - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setl - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: sub + // DEBUG: sub + // DEBUG: setl + // DEBUG: setg + // DEBUG: sub + // DEBUG: ret // - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: setl - // LLVM-20-DEBUG: setg - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: ret - - // LLVM-PRE-20-OPTIM: xor - // LLVM-PRE-20-OPTIM: cmp - // LLVM-PRE-20-OPTIM: setne - // LLVM-PRE-20-OPTIM: mov - // LLVM-PRE-20-OPTIM: cmovge - // LLVM-PRE-20-OPTIM: ret - // - // LLVM-20-OPTIM: cmp - // LLVM-20-OPTIM: setl - // LLVM-20-OPTIM: setg - // LLVM-20-OPTIM: sub - // LLVM-20-OPTIM: ret + // OPTIM: cmp + // OPTIM: setl + // OPTIM: setg + // OPTIM: sub + // OPTIM: ret three_way_compare(a, b) } #[no_mangle] // CHECK-LABEL: unsigned_cmp: pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering { - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: seta - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setb - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: sub - // - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: seta - // LLVM-20-DEBUG: sbb - // LLVM-20-DEBUG: ret - - // LLVM-PRE-20-OPTIM: xor - // LLVM-PRE-20-OPTIM: cmp - // LLVM-PRE-20-OPTIM: setne - // LLVM-PRE-20-OPTIM: mov - // LLVM-PRE-20-OPTIM: cmovae - // LLVM-PRE-20-OPTIM: ret + // DEBUG: sub + // DEBUG: seta + // DEBUG: sbb + // DEBUG: ret // - // LLVM-20-OPTIM: cmp - // LLVM-20-OPTIM: seta - // LLVM-20-OPTIM: sbb - // LLVM-20-OPTIM: ret + // OPTIM: cmp + // OPTIM: seta + // OPTIM: sbb + // OPTIM: ret three_way_compare(a, b) } diff --git a/tests/codegen-llvm/comparison-operators-2-struct.rs b/tests/codegen-llvm/comparison-operators-2-struct.rs index e179066ebfd72..d44f92f511b64 100644 --- a/tests/codegen-llvm/comparison-operators-2-struct.rs +++ b/tests/codegen-llvm/comparison-operators-2-struct.rs @@ -1,5 +1,4 @@ //@ compile-flags: -C opt-level=1 -//@ min-llvm-version: 20 // The `derive(PartialOrd)` for a 2-field type doesn't override `lt`/`le`/`gt`/`ge`. // This double-checks that the `Option` intermediate values used diff --git a/tests/codegen-llvm/comparison-operators-2-tuple.rs b/tests/codegen-llvm/comparison-operators-2-tuple.rs index 6a7e489c82dd9..37a7c5dfdaf04 100644 --- a/tests/codegen-llvm/comparison-operators-2-tuple.rs +++ b/tests/codegen-llvm/comparison-operators-2-tuple.rs @@ -1,5 +1,4 @@ //@ compile-flags: -C opt-level=1 -Z merge-functions=disabled -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index f58d7ef12b661..7d450a89e2e3c 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=0 -Cno-prepopulate-passes -//@ min-llvm-version: 19 //@ only-64bit #![crate_type = "lib"] diff --git a/tests/codegen-llvm/enum/enum-discriminant-eq.rs b/tests/codegen-llvm/enum/enum-discriminant-eq.rs index a1ab5e5c6e2ec..68cd58643e84e 100644 --- a/tests/codegen-llvm/enum/enum-discriminant-eq.rs +++ b/tests/codegen-llvm/enum/enum-discriminant-eq.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled -//@ min-llvm-version: 20 //@ only-64bit //@ revisions: LLVM20 LLVM21 //@ [LLVM21] min-llvm-version: 21 diff --git a/tests/codegen-llvm/integer-cmp.rs b/tests/codegen-llvm/integer-cmp.rs index 812fa8e4a4244..2233a575f8e70 100644 --- a/tests/codegen-llvm/integer-cmp.rs +++ b/tests/codegen-llvm/integer-cmp.rs @@ -1,9 +1,6 @@ // This is test for more optimal Ord implementation for integers. // See for more info. -//@ revisions: llvm-pre-20 llvm-20 -//@ [llvm-20] min-llvm-version: 20 -//@ [llvm-pre-20] max-llvm-major-version: 19 //@ compile-flags: -C opt-level=3 -Zmerge-functions=disabled #![crate_type = "lib"] @@ -13,50 +10,29 @@ use std::cmp::Ordering; // CHECK-LABEL: @cmp_signed #[no_mangle] pub fn cmp_signed(a: i64, b: i64) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.scmp.i8.i64 - // llvm-pre-20: icmp slt - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.scmp.i8.i64 a.cmp(&b) } // CHECK-LABEL: @cmp_unsigned #[no_mangle] pub fn cmp_unsigned(a: u32, b: u32) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.ucmp.i8.i32 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.ucmp.i8.i32 a.cmp(&b) } // CHECK-LABEL: @cmp_char #[no_mangle] pub fn cmp_char(a: char, b: char) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.ucmp.i8.i32 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.ucmp.i8.i32 a.cmp(&b) } // CHECK-LABEL: @cmp_tuple #[no_mangle] pub fn cmp_tuple(a: (i16, u16), b: (i16, u16)) -> Ordering { - // llvm-20-DAG: call{{.*}} i8 @llvm.ucmp.i8.i16 - // llvm-20-DAG: call{{.*}} i8 @llvm.scmp.i8.i16 - // llvm-20: ret i8 - // llvm-pre-20: icmp slt - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 - // llvm-pre-20: select i1 + // CHECK-DAG: call{{.*}} i8 @llvm.ucmp.i8.i16 + // CHECK-DAG: call{{.*}} i8 @llvm.scmp.i8.i16 + // CHECK: ret i8 a.cmp(&b) } diff --git a/tests/codegen-llvm/intrinsics/three_way_compare.rs b/tests/codegen-llvm/intrinsics/three_way_compare.rs index 95fcb636f7cab..89bf69561e9a1 100644 --- a/tests/codegen-llvm/intrinsics/three_way_compare.rs +++ b/tests/codegen-llvm/intrinsics/three_way_compare.rs @@ -2,7 +2,6 @@ //@ [DEBUG] compile-flags: -C opt-level=0 //@ [OPTIM] compile-flags: -C opt-level=3 //@ compile-flags: -C no-prepopulate-passes -//@ min-llvm-version: 20 #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/codegen-llvm/issues/and-masked-comparison-131162.rs b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs index bdf021092fda4..fc4b0341a31bc 100644 --- a/tests/codegen-llvm/issues/and-masked-comparison-131162.rs +++ b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/issues/issue-101082.rs b/tests/codegen-llvm/issues/issue-101082.rs index 8d15921ddb4e1..0c1f90f951a72 100644 --- a/tests/codegen-llvm/issues/issue-101082.rs +++ b/tests/codegen-llvm/issues/issue-101082.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=3 //@ revisions: host x86-64 x86-64-v3 -//@ min-llvm-version: 20 //@[host] ignore-x86_64 diff --git a/tests/codegen-llvm/issues/issue-129795.rs b/tests/codegen-llvm/issues/issue-129795.rs index dc64ee35c97e5..7a928389fab2d 100644 --- a/tests/codegen-llvm/issues/issue-129795.rs +++ b/tests/codegen-llvm/issues/issue-129795.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] // Ensure that a modulo operation with an operand that is known to be diff --git a/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs index 4d3fa4993ef72..4c4eebeabb5fa 100644 --- a/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs +++ b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs @@ -3,7 +3,6 @@ // use a larger value to prevent unrolling. //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs b/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs index 35acf765d690c..b686f8c4b3acb 100644 --- a/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs +++ b/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] /// Ensure the function is properly optimized diff --git a/tests/codegen-llvm/option-niche-eq.rs b/tests/codegen-llvm/option-niche-eq.rs index 3900cb79aa2ab..e9c3fa2407e7e 100644 --- a/tests/codegen-llvm/option-niche-eq.rs +++ b/tests/codegen-llvm/option-niche-eq.rs @@ -1,5 +1,4 @@ //@ revisions: REGULAR LLVM21 -//@ min-llvm-version: 20 //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled //@ [LLVM21] min-llvm-version: 21 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/slice-last-elements-optimization.rs b/tests/codegen-llvm/slice-last-elements-optimization.rs index d982cda709d47..77fc1d21cd908 100644 --- a/tests/codegen-llvm/slice-last-elements-optimization.rs +++ b/tests/codegen-llvm/slice-last-elements-optimization.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] // This test verifies that LLVM 20 properly optimizes the bounds check diff --git a/tests/codegen-llvm/swap-small-types.rs b/tests/codegen-llvm/swap-small-types.rs index 7aa613ae9c227..0799ff76331d3 100644 --- a/tests/codegen-llvm/swap-small-types.rs +++ b/tests/codegen-llvm/swap-small-types.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled //@ only-x86_64 -//@ min-llvm-version: 20 //@ ignore-std-debug-assertions (`ptr::swap_nonoverlapping` has one which blocks some optimizations) #![crate_type = "lib"] diff --git a/tests/codegen-llvm/try_question_mark_nop.rs b/tests/codegen-llvm/try_question_mark_nop.rs index 398c9a580bc30..a09fa0a49019d 100644 --- a/tests/codegen-llvm/try_question_mark_nop.rs +++ b/tests/codegen-llvm/try_question_mark_nop.rs @@ -1,9 +1,6 @@ //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled //@ edition: 2021 //@ only-x86_64 -//@ revisions: NINETEEN TWENTY -//@[NINETEEN] exact-llvm-major-version: 19 -//@[TWENTY] min-llvm-version: 20 #![crate_type = "lib"] #![feature(try_blocks)] @@ -17,13 +14,9 @@ pub fn option_nop_match_32(x: Option) -> Option { // CHECK: start: // CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1 - // NINETEEN-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %0, i32 0 - // NINETEEN-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 [[SELECT]], 0 - // NINETEEN-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 %1, 1 - - // TWENTY-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %1, i32 undef - // TWENTY-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 - // TWENTY-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 [[SELECT]], 1 + // CHECK-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %1, i32 undef + // CHECK-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 + // CHECK-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 [[SELECT]], 1 // CHECK-NEXT: ret { i32, i32 } [[REG3]] match x { @@ -36,8 +29,8 @@ pub fn option_nop_match_32(x: Option) -> Option { #[no_mangle] pub fn option_nop_traits_32(x: Option) -> Option { // CHECK: start: - // TWENTY-NEXT: %[[IS_SOME:.+]] = trunc nuw i32 %0 to i1 - // TWENTY-NEXT: select i1 %[[IS_SOME]], i32 %1, i32 undef + // CHECK-NEXT: %[[IS_SOME:.+]] = trunc nuw i32 %0 to i1 + // CHECK-NEXT: select i1 %[[IS_SOME]], i32 %1, i32 undef // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: ret { i32, i32 } @@ -96,13 +89,9 @@ pub fn option_nop_match_64(x: Option) -> Option { // CHECK: start: // CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 %0 to i1 - // NINETEEN-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %0, i64 0 - // NINETEEN-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 [[SELECT]], 0 - // NINETEEN-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 %1, 1 - - // TWENTY-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %1, i64 undef - // TWENTY-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 %0, 0 - // TWENTY-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 [[SELECT]], 1 + // CHECK-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %1, i64 undef + // CHECK-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 %0, 0 + // CHECK-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 [[SELECT]], 1 // CHECK-NEXT: ret { i64, i64 } [[REG3]] match x { @@ -115,8 +104,8 @@ pub fn option_nop_match_64(x: Option) -> Option { #[no_mangle] pub fn option_nop_traits_64(x: Option) -> Option { // CHECK: start: - // TWENTY-NEXT: %[[TRUNC:[0-9]+]] = trunc nuw i64 %0 to i1 - // TWENTY-NEXT: %[[SEL:\.[0-9]+]] = select i1 %[[TRUNC]], i64 %1, i64 undef + // CHECK-NEXT: %[[TRUNC:[0-9]+]] = trunc nuw i64 %0 to i1 + // CHECK-NEXT: %[[SEL:\.[0-9]+]] = select i1 %[[TRUNC]], i64 %1, i64 undef // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: ret { i64, i64 } diff --git a/tests/codegen-llvm/union-aggregate.rs b/tests/codegen-llvm/union-aggregate.rs index aac66c5dcdd9f..7faa66804feaa 100644 --- a/tests/codegen-llvm/union-aggregate.rs +++ b/tests/codegen-llvm/union-aggregate.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=0 -Cno-prepopulate-passes -//@ min-llvm-version: 19 //@ only-64bit #![crate_type = "lib"] diff --git a/tests/ui/abi/sparcv8plus-llvm19.rs b/tests/ui/abi/sparcv8plus-llvm19.rs deleted file mode 100644 index 3d6d8568b6e5a..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ add-core-stubs -//@ revisions: sparc sparcv8plus sparc_cpu_v9 sparc_feature_v8plus sparc_cpu_v9_feature_v8plus -//@[sparc] compile-flags: --target sparc-unknown-none-elf -//@[sparc] needs-llvm-components: sparc -//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu -//@[sparcv8plus] needs-llvm-components: sparc -//@[sparc_cpu_v9] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -//@[sparc_cpu_v9] needs-llvm-components: sparc -//@[sparc_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-feature=+v8plus -//@[sparc_feature_v8plus] needs-llvm-components: sparc -//@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus -//@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc -//@ exact-llvm-major-version: 19 - -#![crate_type = "rlib"] -#![feature(no_core, rustc_attrs, lang_items)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[rustc_builtin_macro] -macro_rules! compile_error { - () => {}; -} - -#[cfg(all(not(target_feature = "v8plus"), not(target_feature = "v9")))] -compile_error!("-v8plus,-v9"); -//[sparc]~^ ERROR -v8plus,-v9 - -// FIXME: sparc_cpu_v9 should be in "-v8plus,+v9" group (fixed in LLVM 20) -#[cfg(all(target_feature = "v8plus", target_feature = "v9"))] -compile_error!("+v8plus,+v9"); -//[sparcv8plus,sparc_cpu_v9_feature_v8plus,sparc_cpu_v9]~^ ERROR +v8plus,+v9 - -// FIXME: should be rejected -#[cfg(all(target_feature = "v8plus", not(target_feature = "v9")))] -compile_error!("+v8plus,-v9 (FIXME)"); -//[sparc_feature_v8plus]~^ ERROR +v8plus,-v9 (FIXME) - -#[cfg(all(not(target_feature = "v8plus"), target_feature = "v9"))] -compile_error!("-v8plus,+v9"); diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr deleted file mode 100644 index d3462ae87d338..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: -v8plus,-v9 - --> $DIR/sparcv8plus-llvm19.rs:28:1 - | -LL | compile_error!("-v8plus,-v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr deleted file mode 100644 index 9891aec94b860..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr deleted file mode 100644 index 9891aec94b860..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr deleted file mode 100644 index dbcdb8ed121b3..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus-llvm19.rs:38:1 - | -LL | compile_error!("+v8plus,-v9 (FIXME)"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr deleted file mode 100644 index 9891aec94b860..0000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus.rs b/tests/ui/abi/sparcv8plus.rs index 6c17f72183862..ba4fb6f7108d4 100644 --- a/tests/ui/abi/sparcv8plus.rs +++ b/tests/ui/abi/sparcv8plus.rs @@ -10,7 +10,6 @@ //@[sparc_feature_v8plus] needs-llvm-components: sparc //@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus //@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc -//@ min-llvm-version: 20 #![crate_type = "rlib"] #![feature(no_core, rustc_attrs, lang_items)] diff --git a/tests/ui/abi/sparcv8plus.sparc.stderr b/tests/ui/abi/sparcv8plus.sparc.stderr index e2aa89a927317..e31dbd344d6f4 100644 --- a/tests/ui/abi/sparcv8plus.sparc.stderr +++ b/tests/ui/abi/sparcv8plus.sparc.stderr @@ -1,5 +1,5 @@ error: -v8plus,-v9 - --> $DIR/sparcv8plus.rs:28:1 + --> $DIR/sparcv8plus.rs:27:1 | LL | compile_error!("-v8plus,-v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr index 2c5699f2dec2b..a1a8383cbe704 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr @@ -1,5 +1,5 @@ error: -v8plus,+v9 - --> $DIR/sparcv8plus.rs:41:1 + --> $DIR/sparcv8plus.rs:40:1 | LL | compile_error!("-v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr index 4b96e4421f9f9..c633ee26c5141 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:32:1 + --> $DIR/sparcv8plus.rs:31:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr index dfdec88961beb..bad8adc159967 100644 --- a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus.rs:37:1 + --> $DIR/sparcv8plus.rs:36:1 | LL | compile_error!("+v8plus,-v9 (FIXME)"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr index 4b96e4421f9f9..c633ee26c5141 100644 --- a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:32:1 + --> $DIR/sparcv8plus.rs:31:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index 8742d4bd82cd5..c67c913d2a60d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index e6cb6e40c701d..99c071919acfa 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 8742d4bd82cd5..c67c913d2a60d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index e6cb6e40c701d..99c071919acfa 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 0d3eba07f14da..cca37dd2e8e8b 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ needs-asm-support //@ revisions: loongarch32_ilp32d loongarch32_ilp32s loongarch64_lp64d loongarch64_lp64s -//@ min-llvm-version: 20 //@[loongarch32_ilp32d] compile-flags: --target loongarch32-unknown-none //@[loongarch32_ilp32d] needs-llvm-components: loongarch //@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat From e54602c5bbb6abc272eef6c795dcc01e755ab75f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 8 Aug 2025 13:59:06 -0700 Subject: [PATCH 0481/1900] Merge similar output checks in assembly-llvm/x86_64-cmp --- tests/assembly-llvm/x86_64-cmp.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/assembly-llvm/x86_64-cmp.rs b/tests/assembly-llvm/x86_64-cmp.rs index 7e87171991d4d..1f1fe7fd00520 100644 --- a/tests/assembly-llvm/x86_64-cmp.rs +++ b/tests/assembly-llvm/x86_64-cmp.rs @@ -14,16 +14,11 @@ use std::intrinsics::three_way_compare; // CHECK-LABEL: signed_cmp: pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { // DEBUG: sub - // DEBUG: setl - // DEBUG: setg - // DEBUG: sub - // DEBUG: ret - // // OPTIM: cmp - // OPTIM: setl - // OPTIM: setg - // OPTIM: sub - // OPTIM: ret + // CHECK: setl + // CHECK: setg + // CHECK: sub + // CHECK: ret three_way_compare(a, b) } @@ -31,13 +26,9 @@ pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { // CHECK-LABEL: unsigned_cmp: pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering { // DEBUG: sub - // DEBUG: seta - // DEBUG: sbb - // DEBUG: ret - // // OPTIM: cmp - // OPTIM: seta - // OPTIM: sbb - // OPTIM: ret + // CHECK: seta + // CHECK: sbb + // CHECK: ret three_way_compare(a, b) } From 88bef496465112850185fd1c46de1e250b271fa3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 8 Aug 2025 14:20:56 -0700 Subject: [PATCH 0482/1900] Update the FIXME comments in the generic three_way_compare --- .../rustc_codegen_ssa/src/traits/builder.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 422fa715de109..4a5694e97fa20 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -412,23 +412,26 @@ pub trait BuilderMethods<'a, 'tcx>: lhs: Self::Value, rhs: Self::Value, ) -> Self::Value { + // FIXME: This implementation was designed around LLVM's ability to optimize, but `cg_llvm` + // overrides this to just use `@llvm.scmp`/`ucmp` since LLVM 20. This default impl should be + // reevaluated with respect to the remaining backends like cg_gcc, whether they might use + // specialized implementations as well, or continue to use a generic implementation here. use std::cmp::Ordering; let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed()); if self.cx().sess().opts.optimize == OptLevel::No { - // FIXME: This actually generates tighter assembly, and is a classic trick - // - // However, as of 2023-11 it optimizes worse in things like derived - // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it - // better (see ), it'll - // be worth trying it in optimized builds as well. + // This actually generates tighter assembly, and is a classic trick: + // . + // However, as of 2023-11 it optimized worse in LLVM in things like derived + // `PartialOrd`, so we were only using it in debug. Since LLVM now uses its own + // intrinsics, it may be be worth trying it in optimized builds for other backends. let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs); let gtext = self.zext(is_gt, self.type_i8()); let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); let ltext = self.zext(is_lt, self.type_i8()); self.unchecked_ssub(gtext, ltext) } else { - // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, - // from . + // These operations were better optimized by LLVM, before `@llvm.scmp`/`ucmp` in 20. + // See . let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs); let ge = self.select( From 1ae22d83f97a66f0bbcee083abf3f0386df5332a Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Tue, 22 Apr 2025 01:19:14 -0700 Subject: [PATCH 0483/1900] Implement `Debug` for `EncodeWide` Since `std::os::windows::ffi::EncodeWide` was reexported from `std::sys_common::wtf8::EncodeWide`, which has `#![allow(missing_debug_implementations)]` in the parent module, it did not implement `Debug`. When it was moved to `core`, a placeholder impl was added; fill it in. --- library/alloc/src/wtf8/tests.rs | 11 +++++++++++ library/core/src/wtf8.rs | 29 +++++++++++++++++++++++++---- library/std/src/sys_common/mod.rs | 1 - 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/wtf8/tests.rs b/library/alloc/src/wtf8/tests.rs index 291f63f9f9e54..a72ad0837d11e 100644 --- a/library/alloc/src/wtf8/tests.rs +++ b/library/alloc/src/wtf8/tests.rs @@ -579,6 +579,17 @@ fn wtf8_encode_wide_size_hint() { assert!(iter.next().is_none()); } +#[test] +fn wtf8_encode_wide_debug() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!( + format!("{:?}", string.encode_wide()), + r#"EncodeWide(['a', 'é', ' ', 0xD83D, 0xD83D, 0xDCA9])"# + ); +} + #[test] fn wtf8_clone_into() { let mut string = Wtf8Buf::new(); diff --git a/library/core/src/wtf8.rs b/library/core/src/wtf8.rs index de0dfa560a3f3..0c03496c5e367 100644 --- a/library/core/src/wtf8.rs +++ b/library/core/src/wtf8.rs @@ -562,15 +562,36 @@ impl Iterator for EncodeWide<'_> { } } +#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")] +impl FusedIterator for EncodeWide<'_> {} + +#[stable(feature = "encode_wide_debug", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for EncodeWide<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EncodeWide").finish_non_exhaustive() + struct CodeUnit(u16); + impl fmt::Debug for CodeUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This output attempts to balance readability with precision. + // Render characters which take only one WTF-16 code unit using + // `char` syntax and everything else as code units with hex + // integer syntax (including paired and unpaired surrogate + // halves). Since Rust has no `char`-like type for WTF-16, this + // isn't perfect, so if this output isn't suitable, it is open + // to being changed (see #140153). + match char::from_u32(self.0 as u32) { + Some(c) => write!(f, "{c:?}"), + None => write!(f, "0x{:04X}", self.0), + } + } + } + + write!(f, "EncodeWide(")?; + f.debug_list().entries(self.clone().map(CodeUnit)).finish()?; + write!(f, ")")?; + Ok(()) } } -#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")] -impl FusedIterator for EncodeWide<'_> {} - impl Hash for CodePoint { #[inline] fn hash(&self, state: &mut H) { diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index ec45c723e0de5..c6cb1b006e8c2 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -15,7 +15,6 @@ //! Progress on this is tracked in #84187. #![allow(missing_docs)] -#![allow(missing_debug_implementations)] #[cfg(test)] mod tests; From b79a4bfad66dea3a8b6bc90985cb65378288fe07 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 16 Sep 2025 21:51:39 +0200 Subject: [PATCH 0484/1900] Do not use `git -C dir` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of git (≤ 1.8.5) do not support the `-C dir` global option. Use the `cwd` optional argument when using Python's `subprocess` functionality instead. --- src/bootstrap/bootstrap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 19e87f9c29317..effd33d288fa6 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1193,8 +1193,6 @@ def get_latest_commit(self): return "" cmd = [ "git", - "-C", - repo_path, "rev-list", "--author", author_email, @@ -1202,7 +1200,9 @@ def get_latest_commit(self): "HEAD", ] try: - commit = subprocess.check_output(cmd, universal_newlines=True).strip() + commit = subprocess.check_output( + cmd, universal_newlines=True, cwd=repo_path + ).strip() return commit or "" except subprocess.CalledProcessError: return "" From d81872a97145f35766b9885d6d88e8077d0eaf59 Mon Sep 17 00:00:00 2001 From: T Date: Fri, 12 Sep 2025 01:53:11 +0000 Subject: [PATCH 0485/1900] add Readme.md to tidy update Readme add info about githooks and bootstrap.toml add info about config and remove linting specific files add link to rustc-dev-guide --- src/doc/rustc-dev-guide/src/tests/intro.md | 4 +- src/tools/tidy/Readme.md | 112 +++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/tools/tidy/Readme.md diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index b90c16d602c31..4fa63b83b17fb 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -70,10 +70,12 @@ package tests: Tidy is a custom tool used for validating source code style and formatting conventions, such as rejecting long lines. There is more information in the -[section on coding conventions](../conventions.md#formatting). +[section on coding conventions](../conventions.md#formatting) or the [Tidy Readme]. > Examples: `./x test tidy` +[Tidy Readme]: https://github.com/rust-lang/rust/blob/master/src/tools/tidy/Readme.md + ### Formatting diff --git a/src/tools/tidy/Readme.md b/src/tools/tidy/Readme.md new file mode 100644 index 0000000000000..fc9dc6350e92d --- /dev/null +++ b/src/tools/tidy/Readme.md @@ -0,0 +1,112 @@ +# Tidy +Tidy is the Rust project's custom internal linter and a crucial part of our testing and continuous integration (CI) infrastructure. It is designed to enforce a consistent style and formatting across the entire codebase, but its role extends beyond simple linting. Tidy also helps with infrastructure, policy, and documentation, ensuring the project remains organized, functional, and... tidy. + +This document will cover how to use tidy, the specific checks tidy performs, and using tidy directives to manage its behavior. By understanding and utilizing tidy, you can help us maintain the high standards of the Rust project. +## Tidy Checks +### Style and Code Quality +These lints focus on enforcing consistent formatting, style, and general code health. +* [`alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/alphabetical/index.html): Checks that lists are sorted alphabetically +* [`style`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/style/index.html): Check to enforce various stylistic guidelines on the Rust codebase. +* [`filenames`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/filenames/index.html): Check to prevent invalid characters in file names. +* [`pal`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/pal/index.html): Check to enforce rules about platform-specific code in std. +* [`target_policy`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_policy/index.html): Check for target tier policy compliance. +* [`error_codes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/error_codes/index.html): Check to ensure error codes are properly documented and tested. + +### Infrastructure +These checks focus on the integrity of the project's dependencies, internal tools, and documentation. +* [`bins`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/bins/index.html): Prevent stray binaries from being checked into the source tree. +* [`fluent_alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_alphabetical/index.html) / [`fluent_period`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_period/index.html) / [`fluent_used`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_used/index.html): Various checks related to [Fluent](https://github.com/projectfluent) for localization and natural language translation. +* [`deps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/deps/index.html) / [`extdeps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extdeps/index.html): Check for valid licenses and sources for external dependencies. +* [`gcc_submodule`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/gcc_submodule/index.html): Check that the `src/gcc` submodule version is valid. +* [`triagebot`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/triagebot/index.html): Check to ensure paths mentioned in `triagebot.toml` exist in the project. +* [`x_version`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/x_version/index.html): Validates the current version of the `x` tool. + +* [`edition`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/edition/index.html) / [`features`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/features/index.html): Check for a valid Rust edition and proper ordering of unstable features. +* [`rustdoc_css_themes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_css_themes/index.html) / [`rustdoc_templates`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_templates/index.html): Verify that Rust documentation templates and themes are correct. +* [`unstable_book`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unstable_book/index.html): Synchronizes the unstable book with unstable features. +### Testing +These checks ensure that tests are correctly structured, cleaned up, and free of common errors. +* [`tests_placement`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_placement/index.html) / [`unit_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unit_tests/index.html): Verify that tests are located in the correct directories and are not using improper attributes. +* [`known_bug`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/known_bug/index.html) / [`unknown_revision`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unknown_revision/index.html): Ensure that test directives and annotations are used correctly. +* [`debug_artifacts`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/debug_artifacts/index.html) / [`mir_opt_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/mir_opt_tests/index.html): Prevent unnecessary artifacts and stale files in test directories. +* [`tests_revision_unpaired_stdout_stderr`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_revision_unpaired_stdout_stderr/index.html) / [`ui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/ui_tests/index.html): Check for unpaired or stray test output files. +* [`target_specific_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_specific_tests/index.html): Check to ensure that all target specific tests (those that require a --target flag) also require the pre-requisite LLVM components to run. +* [`rustdoc_gui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_gui_tests/index.html): Checks that rustdoc gui tests start with a small description +* [`rustdoc_json`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_json/index.html): Verify that `FORMAT_VERSION` is in sync with `rust-json-types`. +## Using Tidy + +Tidy is used in a number of different ways. +* Every time `./x test` is used tidy will run automatically. + +* On every pull request, tidy will run automatically during CI checks. +* Optionally, with the use of git-hooks, tidy can run locally on every push. This can be setup with `./x setup`. See the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/building/suggested.html#installing-a-pre-push-hook) for more information. + +You can run tidy manually with: + +`./x test tidy` + +To first run the relevant formatter and then run tidy you can add `--bless`. + +`./x test tidy --bless` +### Extra Checks +[`extra_checks`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extra_checks/index.html) are optional checks primarily focused on other file types and programming languages. + +Example usage: + +`./x test tidy --extra-checks=py,cpp,js,spellcheck` + +All options for `--extra-checks`: +* `cpp`, `cpp:fmt` +* `py`, `py:lint`, `py:fmt` +* `js`, `js:lint`, `js:fmt`, `js:typecheck` +* `shell`, `shell:lint` +* `spellcheck` + +Default values for tidy's `extra-checks` can be set in `bootstrap.toml`. For example, `build.tidy-extra-checks = "js,py"`. + +Any argument without a suffix (eg. `py` or `js`) will include all possible checks. For example, `--extra-checks=js` is the same as `extra-checks=js:lint,js:fmt,js:typecheck`. + +Any argument can be prefixed with `auto:` to only run if relevant files are modified (eg. `--extra-checks=auto:py`). + +A specific configuration file or folder can be passed to tidy after a double dash (`--extra-checks=py -- foo.py`) + +## Tidy Directives + +Tidy directives are special comments that help tidy operate. + +Tidy directives can be used in the following types of comments: +* `// ` +* `# ` +* `/* {...} */` +* `` + +You might find yourself needing to ignore a specific tidy style check and can do so with: +* `ignore-tidy-cr` +* `ignore-tidy-undocumented-unsafe` +* `ignore-tidy-tab` +* `ignore-tidy-linelength` +* `ignore-tidy-filelength` + +* `ignore-tidy-end-whitespace` +* `ignore-tidy-trailing-newlines` +* `ignore-tidy-leading-newlines` +* `ignore-tidy-copyright` +* `ignore-tidy-dbg` +* `ignore-tidy-odd-backticks` +* `ignore-tidy-todo` + +Some checks, like `alphabetical`, require a tidy directive to use: +``` +// tidy-alphabetical-start +fn aaa() {} +fn eee() {} +fn z() {} +// tidy-alphabetical-end +``` +While not exactly a tidy directive, // TODO will fail tidy and make sure you can't merge a PR with unfinished work. + +### Test Specific Directives + +`target-specific-tests` can be ignored with `// ignore-tidy-target-specific-tests` + +Tidy's `unknown_revision` check can be suppressed by adding the revision name to `//@ unused-revision-names:{revision}` or with `//@ unused-revision-names:*`. From a4e8d6d79d52da36f92d9e10b3fee5e024c55ade Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 16 Sep 2025 17:25:31 -0400 Subject: [PATCH 0486/1900] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 24bb93c388fb8..966f94733bbc9 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 24bb93c388fb8c211a37986539f24a819dc669d3 +Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 From 44c1a00a2f9929cff1641b18f4c2b5f2d2833e3e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 30 Apr 2023 20:48:29 +0000 Subject: [PATCH 0487/1900] Enable DestinationPropagation by default. --- compiler/rustc_mir_transform/src/dest_prop.rs | 10 +- compiler/rustc_mir_transform/src/lib.rs | 4 +- compiler/rustc_mir_transform/src/nrvo.rs | 234 ----------------- ...o.DestinationPropagation.panic-abort.diff} | 31 +-- ....DestinationPropagation.panic-unwind.diff} | 31 +-- .../nrvo_borrowed.rs} | 4 +- .../{ => dest-prop}/nrvo_miscompile_111005.rs | 7 +- ..._111005.wrong.DestinationPropagation.diff} | 7 +- tests/mir-opt/pre-codegen/checked_ops.rs | 3 +- ...b_at_home.PreCodegen.after.panic-abort.mir | 6 +- ..._at_home.PreCodegen.after.panic-unwind.mir | 6 +- .../derived_ord.demo_le.PreCodegen.after.mir | 35 ++- tests/mir-opt/pre-codegen/derived_ord.rs | 5 +- ....{impl#0}-partial_cmp.PreCodegen.after.mir | 32 ++- ...ward_loop.PreCodegen.after.panic-abort.mir | 68 +++-- ...ard_loop.PreCodegen.after.panic-unwind.mir | 68 +++-- ...ated_loop.PreCodegen.after.panic-abort.mir | 240 ++++++++---------- ...ted_loop.PreCodegen.after.panic-unwind.mir | 70 +++-- ...ward_loop.PreCodegen.after.panic-abort.mir | 182 ++++++------- ...ard_loop.PreCodegen.after.panic-unwind.mir | 182 ++++++------- ...ange_loop.PreCodegen.after.panic-abort.mir | 54 ++-- ...nge_loop.PreCodegen.after.panic-unwind.mir | 54 ++-- ...erse_loop.PreCodegen.after.panic-abort.mir | 72 +++--- ...rse_loop.PreCodegen.after.panic-unwind.mir | 72 +++--- ...e_ord.demo_ge_partial.PreCodegen.after.mir | 18 +- ...ple_ord.demo_le_total.PreCodegen.after.mir | 18 +- 26 files changed, 590 insertions(+), 923 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/nrvo.rs rename tests/mir-opt/{nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff => dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff} (52%) rename tests/mir-opt/{nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff => dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff} (52%) rename tests/mir-opt/{nrvo_simple.rs => dest-prop/nrvo_borrowed.rs} (69%) rename tests/mir-opt/{ => dest-prop}/nrvo_miscompile_111005.rs (75%) rename tests/mir-opt/{nrvo_miscompile_111005.wrong.RenameReturnPlace.diff => dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff} (61%) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index c57483a681140..abd1cd4e35ac0 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -153,15 +153,7 @@ pub(super) struct DestinationPropagation; impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // For now, only run at MIR opt level 3. Two things need to be changed before this can be - // turned on by default: - // 1. Because of the overeager removal of storage statements, this can cause stack space - // regressions. This opt is not the place to fix this though, it's a more general - // problem in MIR. - // 2. Despite being an overall perf improvement, this still causes a 30% regression in - // keccak. We can temporarily fix this by bounding function size, but in the long term - // we should fix this by being smarter about invalidating analysis results. - sess.mir_opt_level() >= 3 + sess.mir_opt_level() >= 2 } #[tracing::instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 1663dfa744f42..9ff7e0b550030 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -156,7 +156,6 @@ declare_passes! { mod match_branches : MatchBranchSimplification; mod mentioned_items : MentionedItems; mod multiple_return_terminators : MultipleReturnTerminators; - mod nrvo : RenameReturnPlace; mod post_drop_elaboration : CheckLiveDrops; mod prettify : ReorderBasicBlocks, ReorderLocals; mod promote_consts : PromoteTemps; @@ -715,7 +714,6 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, - &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::Final), &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::Final), @@ -723,7 +721,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &strip_debuginfo::StripDebugInfo, ©_prop::CopyProp, &dead_store_elimination::DeadStoreElimination::Final, - &nrvo::RenameReturnPlace, + &dest_prop::DestinationPropagation, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, &large_enums::EnumSizeOpt { discrepancy: 128 }, diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs deleted file mode 100644 index 965002aae04c7..0000000000000 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! See the docs for [`RenameReturnPlace`]. - -use rustc_hir::Mutability; -use rustc_index::bit_set::DenseBitSet; -use rustc_middle::bug; -use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, BasicBlock, Local, Location}; -use rustc_middle::ty::TyCtxt; -use tracing::{debug, trace}; - -/// This pass looks for MIR that always copies the same local into the return place and eliminates -/// the copy by renaming all uses of that local to `_0`. -/// -/// This allows LLVM to perform an optimization similar to the named return value optimization -/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the -/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by -/// value like so: -/// -/// ```rust -/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { -/// let mut buf = [0; 1024]; -/// init(&mut buf); -/// buf -/// } -/// ``` -/// -/// For now, this pass is very simple and only capable of eliminating a single copy. A more general -/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and -/// [#71003], could yield even more benefits. -/// -/// [#47954]: https://github.com/rust-lang/rust/pull/47954 -/// [#71003]: https://github.com/rust-lang/rust/pull/71003 -pub(super) struct RenameReturnPlace; - -impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // unsound: #111005 - sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - let def_id = body.source.def_id(); - let Some(returned_local) = local_eligible_for_nrvo(body) else { - debug!("`{:?}` was ineligible for NRVO", def_id); - return; - }; - - debug!( - "`{:?}` was eligible for NRVO, making {:?} the return place", - def_id, returned_local - ); - - RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body_preserves_cfg(body); - - // Clean up the `NOP`s we inserted for statements made useless by our renaming. - for block_data in body.basic_blocks.as_mut_preserves_cfg() { - block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop); - } - - // Overwrite the debuginfo of `_0` with that of the renamed local. - let (renamed_decl, ret_decl) = - body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE); - - // Sometimes, the return place is assigned a local of a different but coercible type, for - // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means - // its type may no longer match the return type of its function. This doesn't cause a - // problem in codegen because these two types are layout-compatible, but may be unexpected. - debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty); - ret_decl.clone_from(renamed_decl); - - // The return place is always mutable. - ret_decl.mutability = Mutability::Mut; - } - - fn is_required(&self) -> bool { - false - } -} - -/// MIR that is eligible for the NRVO must fulfill two conditions: -/// 1. The return place must not be read prior to the `Return` terminator. -/// 2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the -/// only definition of the return place reaching the `Return` terminator. -/// -/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned -/// to the return place along all possible paths through the control-flow graph. -fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { - if IsReturnPlaceRead::run(body) { - return None; - } - - let mut copied_to_return_place = None; - for block in body.basic_blocks.indices() { - // Look for blocks with a `Return` terminator. - if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) { - continue; - } - - // Look for an assignment of a single local to the return place prior to the `Return`. - let returned_local = find_local_assigned_to_return_place(block, body)?; - match body.local_kind(returned_local) { - // FIXME: Can we do this for arguments as well? - mir::LocalKind::Arg => return None, - - mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"), - mir::LocalKind::Temp => {} - } - - // If multiple different locals are copied to the return place. We can't pick a - // single one to rename. - if copied_to_return_place.is_some_and(|old| old != returned_local) { - return None; - } - - copied_to_return_place = Some(returned_local); - } - - copied_to_return_place -} - -fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option { - let mut block = start; - let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); - - // Iterate as long as `block` has exactly one predecessor that we have not yet visited. - while seen.insert(block) { - trace!("Looking for assignments to `_0` in {:?}", block); - - let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place); - if local.is_some() { - return local; - } - - match body.basic_blocks.predecessors()[block].as_slice() { - &[pred] => block = pred, - _ => return None, - } - } - - None -} - -// If this statement is an assignment of an unprojected local to the return place, -// return that local. -fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option { - if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind { - if lhs.as_local() == Some(mir::RETURN_PLACE) { - if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs { - return rhs.as_local(); - } - } - } - - None -} - -struct RenameToReturnPlace<'tcx> { - to_rename: Local, - tcx: TyCtxt<'tcx>, -} - -/// Replaces all uses of `self.to_rename` with `_0`. -impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) { - // Remove assignments of the local being replaced to the return place, since it is now the - // return place: - // _0 = _1 - if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) { - stmt.kind = mir::StatementKind::Nop; - return; - } - - // Remove storage annotations for the local being replaced: - // StorageLive(_1) - if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) = - stmt.kind - { - if local == self.to_rename { - stmt.kind = mir::StatementKind::Nop; - return; - } - } - - self.super_statement(stmt, loc) - } - - fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } - - fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) { - if *l == mir::RETURN_PLACE { - assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo)); - } else if *l == self.to_rename { - *l = mir::RETURN_PLACE; - } - } -} - -struct IsReturnPlaceRead(bool); - -impl IsReturnPlaceRead { - fn run(body: &mir::Body<'_>) -> bool { - let mut vis = IsReturnPlaceRead(false); - vis.visit_body(body); - vis.0 - } -} - -impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead { - fn visit_local(&mut self, l: Local, ctxt: PlaceContext, _: Location) { - if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() { - self.0 = true; - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } -} diff --git a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff similarity index 52% rename from tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff rename to tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff index 6dce3ec5303d7..e9fbcf20a7228 100644 --- a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `nrvo` before RenameReturnPlace -+ // MIR for `nrvo` after RenameReturnPlace +- // MIR for `nrvo` before DestinationPropagation ++ // MIR for `nrvo` after DestinationPropagation fn nrvo(_1: for<'a> fn(&'a mut [u8; 1024])) -> [u8; 1024] { debug init => _1; @@ -10,32 +10,33 @@ let mut _5: &mut [u8; 1024]; let mut _6: &mut [u8; 1024]; scope 1 { -- debug buf => _2; -+ debug buf => _0; + debug buf => _2; } bb0: { -- StorageLive(_2); -- _2 = [const 0_u8; 1024]; -+ _0 = [const 0_u8; 1024]; + StorageLive(_2); + _2 = [const 0_u8; 1024]; StorageLive(_3); - StorageLive(_4); - _4 = copy _1; +- StorageLive(_4); +- _4 = copy _1; ++ nop; ++ nop; StorageLive(_5); StorageLive(_6); -- _6 = &mut _2; -+ _6 = &mut _0; + _6 = &mut _2; _5 = &mut (*_6); - _3 = move _4(move _5) -> [return: bb1, unwind unreachable]; +- _3 = move _4(move _5) -> [return: bb1, unwind unreachable]; ++ _3 = move _1(move _5) -> [return: bb1, unwind unreachable]; } bb1: { StorageDead(_5); - StorageDead(_4); +- StorageDead(_4); ++ nop; StorageDead(_6); StorageDead(_3); -- _0 = copy _2; -- StorageDead(_2); + _0 = copy _2; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff similarity index 52% rename from tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff rename to tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff index 54cbe2871f15a..95d5fe1b9304b 100644 --- a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `nrvo` before RenameReturnPlace -+ // MIR for `nrvo` after RenameReturnPlace +- // MIR for `nrvo` before DestinationPropagation ++ // MIR for `nrvo` after DestinationPropagation fn nrvo(_1: for<'a> fn(&'a mut [u8; 1024])) -> [u8; 1024] { debug init => _1; @@ -10,32 +10,33 @@ let mut _5: &mut [u8; 1024]; let mut _6: &mut [u8; 1024]; scope 1 { -- debug buf => _2; -+ debug buf => _0; + debug buf => _2; } bb0: { -- StorageLive(_2); -- _2 = [const 0_u8; 1024]; -+ _0 = [const 0_u8; 1024]; + StorageLive(_2); + _2 = [const 0_u8; 1024]; StorageLive(_3); - StorageLive(_4); - _4 = copy _1; +- StorageLive(_4); +- _4 = copy _1; ++ nop; ++ nop; StorageLive(_5); StorageLive(_6); -- _6 = &mut _2; -+ _6 = &mut _0; + _6 = &mut _2; _5 = &mut (*_6); - _3 = move _4(move _5) -> [return: bb1, unwind continue]; +- _3 = move _4(move _5) -> [return: bb1, unwind continue]; ++ _3 = move _1(move _5) -> [return: bb1, unwind continue]; } bb1: { StorageDead(_5); - StorageDead(_4); +- StorageDead(_4); ++ nop; StorageDead(_6); StorageDead(_3); -- _0 = copy _2; -- StorageDead(_2); + _0 = copy _2; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/dest-prop/nrvo_borrowed.rs similarity index 69% rename from tests/mir-opt/nrvo_simple.rs rename to tests/mir-opt/dest-prop/nrvo_borrowed.rs index df540472e1ccb..6f3076d4c13e9 100644 --- a/tests/mir-opt/nrvo_simple.rs +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.rs @@ -1,8 +1,8 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ test-mir-pass: RenameReturnPlace +//@ test-mir-pass: DestinationPropagation -// EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff +// EMIT_MIR nrvo_borrowed.nrvo.DestinationPropagation.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; init(&mut buf); diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs similarity index 75% rename from tests/mir-opt/nrvo_miscompile_111005.rs rename to tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs index 131f7b8f6f991..17e6450278ac6 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs @@ -1,18 +1,17 @@ // This is a miscompilation, #111005 to track -//@ test-mir-pass: RenameReturnPlace +//@ test-mir-pass: DestinationPropagation #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; -// EMIT_MIR nrvo_miscompile_111005.wrong.RenameReturnPlace.diff +// EMIT_MIR nrvo_miscompile_111005.wrong.DestinationPropagation.diff #[custom_mir(dialect = "runtime", phase = "initial")] pub fn wrong(arg: char) -> char { // CHECK-LABEL: fn wrong( // CHECK: _0 = copy _1; - // FIXME: This is wrong: - // CHECK-NEXT: _0 = const 'b'; + // CHECK-NEXT: _1 = const 'b'; // CHECK-NEXT: return; mir! { { diff --git a/tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff similarity index 61% rename from tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff rename to tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff index d248c76f261b6..60cf9236f6c2a 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff +++ b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff @@ -1,5 +1,5 @@ -- // MIR for `wrong` before RenameReturnPlace -+ // MIR for `wrong` after RenameReturnPlace +- // MIR for `wrong` before DestinationPropagation ++ // MIR for `wrong` after DestinationPropagation fn wrong(_1: char) -> char { let mut _0: char; @@ -9,8 +9,9 @@ - _2 = copy _1; - _0 = copy _2; - _2 = const 'b'; ++ nop; + _0 = copy _1; -+ _0 = const 'b'; ++ _1 = const 'b'; return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.rs b/tests/mir-opt/pre-codegen/checked_ops.rs index 8fd340503f548..dfbc0f4a1108d 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.rs +++ b/tests/mir-opt/pre-codegen/checked_ops.rs @@ -44,8 +44,7 @@ pub fn saturating_sub_at_home(lhs: u32, rhs: u32) -> u32 { // CHECK-LABEL: fn saturating_sub_at_home // CHECK: [[DELTA:_[0-9]+]] = SubUnchecked(copy _1, copy _2) // CHECK: [[TEMP1:_.+]] = Option::::Some({{move|copy}} [[DELTA]]); - // CHECK: [[TEMP2:_.+]] = {{move|copy}} (([[TEMP1]] as Some).0: u32); - // CHECK: _0 = {{move|copy}} [[TEMP2]]; + // CHECK: _0 = {{move|copy}} (([[TEMP1]] as Some).0: u32); u32::checked_sub(lhs, rhs).unwrap_or(0) } diff --git a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir index 5b4fdeda85731..dc7567b57e70a 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir @@ -10,7 +10,6 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { let mut _4: u32; } scope 2 (inlined Option::::unwrap_or) { - let _6: u32; scope 3 { } } @@ -28,10 +27,7 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { _5 = Option::::Some(move _4); StorageDead(_4); StorageDead(_3); - StorageLive(_6); - _6 = move ((_5 as Some).0: u32); - _0 = move _6; - StorageDead(_6); + _0 = move ((_5 as Some).0: u32); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir index 5b4fdeda85731..dc7567b57e70a 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir @@ -10,7 +10,6 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { let mut _4: u32; } scope 2 (inlined Option::::unwrap_or) { - let _6: u32; scope 3 { } } @@ -28,10 +27,7 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { _5 = Option::::Some(move _4); StorageDead(_4); StorageDead(_3); - StorageLive(_6); - _6 = move ((_5 as Some).0: u32); - _0 = move _6; - StorageDead(_6); + _0 = move ((_5 as Some).0: u32); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir index 8746cb0899166..e235fa35c0238 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir @@ -5,8 +5,9 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { debug b => _2; let mut _0: bool; scope 1 (inlined ::le) { - let mut _11: std::option::Option; + let mut _6: std::option::Option; scope 2 (inlined Option::::is_some_and:: bool {std::cmp::Ordering::is_le}>) { + let mut _11: isize; let _12: std::cmp::Ordering; scope 3 { scope 4 (inlined bool {std::cmp::Ordering::is_le} as FnOnce<(std::cmp::Ordering,)>>::call_once - shim(fn(std::cmp::Ordering) -> bool {std::cmp::Ordering::is_le})) { @@ -19,7 +20,6 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { } } scope 7 (inlined ::partial_cmp) { - let mut _6: std::option::Option; let mut _7: i8; scope 8 { } @@ -38,9 +38,8 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { bb0: { StorageLive(_12); - StorageLive(_11); - StorageLive(_5); StorageLive(_6); + StorageLive(_5); StorageLive(_7); StorageLive(_3); _3 = copy ((*_1).0: char); @@ -63,30 +62,44 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { _10 = Cmp(move _8, move _9); StorageDead(_9); StorageDead(_8); - _11 = Option::::Some(move _10); + _6 = Option::::Some(move _10); StorageDead(_10); StorageDead(_7); - StorageDead(_6); StorageDead(_5); - goto -> bb3; + StorageLive(_11); + goto -> bb4; } bb2: { - _11 = copy _6; StorageDead(_7); - StorageDead(_6); StorageDead(_5); - goto -> bb3; + StorageLive(_11); + _11 = discriminant(_6); + switchInt(move _11) -> [0: bb3, 1: bb4, otherwise: bb6]; } bb3: { - _12 = move ((_11 as Some).0: std::cmp::Ordering); + _0 = const false; + goto -> bb5; + } + + bb4: { + _12 = move ((_6 as Some).0: std::cmp::Ordering); StorageLive(_13); _13 = discriminant(_12); _0 = Le(move _13, const 0_i8); StorageDead(_13); + goto -> bb5; + } + + bb5: { StorageDead(_11); + StorageDead(_6); StorageDead(_12); return; } + + bb6: { + unreachable; + } } diff --git a/tests/mir-opt/pre-codegen/derived_ord.rs b/tests/mir-opt/pre-codegen/derived_ord.rs index 73ae923a6cb9c..823e0f6d09c2b 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.rs +++ b/tests/mir-opt/pre-codegen/derived_ord.rs @@ -25,7 +25,10 @@ pub fn demo_le(a: &MultiField, b: &MultiField) -> bool { // CHECK: Cmp(move [[A1]], move [[B1]]); // CHECK: [[D1:_[0-9]+]] = discriminant({{.+}}); - // CHECK: _0 = Le(move [[D1]], const 0_i8); + // CHECK: switchInt(move [[D1]]) -> [0: bb{{[0-9]+}}, 1: bb{{[0-9]+}}, otherwise: bb{{[0-9]+}}]; + + // CHECK: [[D2:_[0-9]+]] = discriminant({{.+}}); + // CHECK: _0 = Le(move [[D2]], const 0_i8); *a <= *b } diff --git a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir index de25eebee77ff..5993bd79d274c 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir @@ -4,10 +4,9 @@ fn ::partial_cmp(_1: &MultiField, _2: &M debug self => _1; debug other => _2; let mut _0: std::option::Option; - let mut _6: std::option::Option; - let mut _7: i8; + let mut _6: i8; scope 1 { - debug cmp => _6; + debug cmp => _0; } scope 2 (inlined std::cmp::impls::::partial_cmp) { let mut _3: char; @@ -15,9 +14,9 @@ fn ::partial_cmp(_1: &MultiField, _2: &M let mut _5: std::cmp::Ordering; } scope 3 (inlined std::cmp::impls::::partial_cmp) { + let mut _7: i16; let mut _8: i16; - let mut _9: i16; - let mut _10: std::cmp::Ordering; + let mut _9: std::cmp::Ordering; } bb0: { @@ -28,27 +27,26 @@ fn ::partial_cmp(_1: &MultiField, _2: &M _5 = Cmp(move _3, move _4); StorageDead(_4); StorageDead(_3); - _6 = Option::::Some(copy _5); - _7 = discriminant(_5); - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + _0 = Option::::Some(copy _5); + _6 = discriminant(_5); + switchInt(move _6) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_10); - StorageLive(_8); - _8 = copy ((*_1).1: i16); StorageLive(_9); - _9 = copy ((*_2).1: i16); - _10 = Cmp(move _8, move _9); - StorageDead(_9); + StorageLive(_7); + _7 = copy ((*_1).1: i16); + StorageLive(_8); + _8 = copy ((*_2).1: i16); + _9 = Cmp(move _7, move _8); StorageDead(_8); - _0 = Option::::Some(move _10); - StorageDead(_10); + StorageDead(_7); + _0 = Option::::Some(move _9); + StorageDead(_9); goto -> bb3; } bb2: { - _0 = copy _6; goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir index dfe618612ab96..03a52b82b49b1 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -5,23 +5,21 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _9: std::option::Option; - let mut _11: &impl Fn(u32); - let mut _12: (u32,); - let _13: (); + let mut _7: std::option::Option; + let mut _9: &impl Fn(u32); + let mut _10: (u32,); + let _11: (); scope 1 { - debug ((iter: std::ops::Range).0: u32) => _4; + debug ((iter: std::ops::Range).0: u32) => _1; debug ((iter: std::ops::Range).1: u32) => _2; - let _10: u32; + let _8: u32; scope 2 { - debug x => _10; + debug x => _8; } scope 4 (inlined iter::range::>::next) { scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - let mut _6: bool; - let _7: u32; - let mut _8: u32; + let mut _5: bool; + let _6: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { scope 9 (inlined #[track_caller] core::num::::unchecked_add) { @@ -33,7 +31,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - let mut _5: u32; + let mut _4: u32; } } } @@ -42,25 +40,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb0: { - StorageLive(_4); - _4 = copy _1; goto -> bb1; } bb1: { - StorageLive(_9); - StorageLive(_6); + StorageLive(_7); StorageLive(_5); - _5 = copy _4; - _6 = Lt(move _5, copy _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb4]; + StorageLive(_4); + _4 = copy _1; + _5 = Lt(move _4, copy _2); + StorageDead(_4); + switchInt(move _5) -> [0: bb2, otherwise: bb4]; } bb2: { - StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_5); + StorageDead(_7); drop(_3) -> [return: bb3, unwind unreachable]; } @@ -69,25 +64,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb4: { - _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_u32); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); - StorageDead(_6); - _10 = copy ((_9 as Some).0: u32); - StorageLive(_11); - _11 = &_3; - StorageLive(_12); - _12 = (copy _10,); - _13 = >::call(move _11, move _12) -> [return: bb5, unwind unreachable]; + _6 = copy _1; + _1 = AddUnchecked(copy _6, const 1_u32); + _7 = Option::::Some(copy _6); + StorageDead(_5); + _8 = copy ((_7 as Some).0: u32); + StorageLive(_9); + _9 = &_3; + StorageLive(_10); + _10 = (copy _8,); + _11 = >::call(move _9, move _10) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_12); - StorageDead(_11); + StorageDead(_10); StorageDead(_9); + StorageDead(_7); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir index e0fcfcaffc59a..3b09f33e73338 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -5,23 +5,21 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _9: std::option::Option; - let mut _11: &impl Fn(u32); - let mut _12: (u32,); - let _13: (); + let mut _7: std::option::Option; + let mut _9: &impl Fn(u32); + let mut _10: (u32,); + let _11: (); scope 1 { - debug ((iter: std::ops::Range).0: u32) => _4; + debug ((iter: std::ops::Range).0: u32) => _1; debug ((iter: std::ops::Range).1: u32) => _2; - let _10: u32; + let _8: u32; scope 2 { - debug x => _10; + debug x => _8; } scope 4 (inlined iter::range::>::next) { scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - let mut _6: bool; - let _7: u32; - let mut _8: u32; + let mut _5: bool; + let _6: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { scope 9 (inlined #[track_caller] core::num::::unchecked_add) { @@ -33,7 +31,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - let mut _5: u32; + let mut _4: u32; } } } @@ -42,25 +40,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb0: { - StorageLive(_4); - _4 = copy _1; goto -> bb1; } bb1: { - StorageLive(_9); - StorageLive(_6); + StorageLive(_7); StorageLive(_5); - _5 = copy _4; - _6 = Lt(move _5, copy _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb4]; + StorageLive(_4); + _4 = copy _1; + _5 = Lt(move _4, copy _2); + StorageDead(_4); + switchInt(move _5) -> [0: bb2, otherwise: bb4]; } bb2: { - StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_5); + StorageDead(_7); drop(_3) -> [return: bb3, unwind continue]; } @@ -69,25 +64,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb4: { - _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_u32); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); - StorageDead(_6); - _10 = copy ((_9 as Some).0: u32); - StorageLive(_11); - _11 = &_3; - StorageLive(_12); - _12 = (copy _10,); - _13 = >::call(move _11, move _12) -> [return: bb5, unwind: bb6]; + _6 = copy _1; + _1 = AddUnchecked(copy _6, const 1_u32); + _7 = Option::::Some(copy _6); + StorageDead(_5); + _8 = copy ((_7 as Some).0: u32); + StorageLive(_9); + _9 = &_3; + StorageLive(_10); + _10 = (copy _8,); + _11 = >::call(move _9, move _10) -> [return: bb5, unwind: bb6]; } bb5: { - StorageDead(_12); - StorageDead(_11); + StorageDead(_10); StorageDead(_9); + StorageDead(_7); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 72b39a7f6a87b..104987b0fdda9 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -4,30 +4,28 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _13: usize; - let mut _32: std::option::Option<(usize, &T)>; - let mut _35: &impl Fn(usize, &T); - let mut _36: (usize, &T); - let _37: (); + let mut _10: usize; + let mut _28: std::option::Option<(usize, &T)>; + let mut _31: &impl Fn(usize, &T); + let mut _32: (usize, &T); + let _33: (); scope 1 { - debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).1: *const T) => _12; + debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).1: *const T) => _9; debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - debug ((iter: Enumerate>).1: usize) => _13; - let _33: usize; - let _34: &T; + debug ((iter: Enumerate>).1: usize) => _10; + let _29: usize; + let _30: &T; scope 2 { - debug i => _33; - debug x => _34; + debug i => _29; + debug x => _30; } scope 18 (inlined > as Iterator>::next) { - let mut _27: std::option::Option<&T>; - let mut _30: (usize, bool); - let mut _31: (usize, &T); + let mut _23: std::option::Option<&T>; + let mut _26: (usize, bool); + let mut _27: (usize, &T); scope 19 { - let _29: usize; + let _25: usize; scope 24 { } } @@ -42,21 +40,21 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } } scope 25 (inlined as Try>::branch) { - let _28: &T; + let _24: &T; scope 26 { } } scope 28 (inlined as Iterator>::next) { - let _14: std::ptr::NonNull; - let _16: std::ptr::NonNull; - let mut _19: bool; - let mut _22: std::ptr::NonNull; - let mut _24: usize; - let _26: &T; + let mut _6: std::ptr::NonNull; + let _11: std::ptr::NonNull; + let _13: std::ptr::NonNull; + let mut _16: bool; + let mut _20: usize; + let _22: &T; scope 29 { - let _15: *const T; + let _12: *const T; scope 30 { - let _23: usize; + let _19: usize; scope 31 { scope 34 (inlined #[track_caller] core::num::::unchecked_sub) { scope 35 (inlined core::ub_checks::check_language_ub) { @@ -72,21 +70,21 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } } scope 38 (inlined as PartialEq>::eq) { - let mut _17: *mut T; - let mut _18: *mut T; + let mut _14: *mut T; + let mut _15: *mut T; scope 39 (inlined NonNull::::as_ptr) { } scope 40 (inlined NonNull::::as_ptr) { } } scope 41 (inlined NonNull::::add) { - let mut _20: *const T; - let mut _21: *const T; + let mut _17: *const T; + let mut _18: *const T; scope 42 (inlined NonNull::::as_ptr) { } } scope 43 (inlined NonNull::::as_ref::<'_>) { - let _25: *const T; + let _21: *const T; scope 44 (inlined NonNull::::as_ptr) { } scope 45 (inlined std::ptr::mut_ptr::::cast_const) { @@ -102,9 +100,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -145,7 +141,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -166,97 +161,86 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - StorageLive(_13); - _11 = copy _6; - _12 = copy _10; - _13 = const 0_usize; + StorageLive(_10); + _10 = const 0_usize; goto -> bb4; } bb4: { - StorageLive(_32); - StorageLive(_29); - StorageLive(_30); - StorageLive(_27); - StorageLive(_14); - StorageLive(_15); - StorageLive(_23); - StorageLive(_24); - StorageLive(_16); + StorageLive(_28); + StorageLive(_25); StorageLive(_26); - _14 = copy _11; - _15 = copy _12; + StorageLive(_23); + StorageLive(_11); + StorageLive(_12); + StorageLive(_19); + StorageLive(_20); + StorageLive(_13); + StorageLive(_22); + _11 = copy _6; + _12 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_19); - _16 = copy _15 as std::ptr::NonNull (Transmute); - StorageLive(_17); - _17 = copy _14 as *mut T (Transmute); - StorageLive(_18); - _18 = copy _16 as *mut T (Transmute); - _19 = Eq(copy _17, copy _18); - StorageDead(_18); - StorageDead(_17); - switchInt(move _19) -> [0: bb6, otherwise: bb7]; + StorageLive(_16); + _13 = copy _12 as std::ptr::NonNull (Transmute); + StorageLive(_14); + _14 = copy _11 as *mut T (Transmute); + StorageLive(_15); + _15 = copy _13 as *mut T (Transmute); + _16 = Eq(copy _14, copy _15); + StorageDead(_15); + StorageDead(_14); + switchInt(move _16) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_19); - StorageLive(_22); - StorageLive(_21); - StorageLive(_20); - _20 = copy _14 as *const T (Transmute); - _21 = Offset(copy _20, const 1_usize); - StorageDead(_20); - _22 = NonNull:: { pointer: copy _21 }; - StorageDead(_21); - _11 = move _22; - StorageDead(_22); + StorageDead(_16); + StorageLive(_18); + StorageLive(_17); + _17 = copy _11 as *const T (Transmute); + _18 = Offset(copy _17, const 1_usize); + StorageDead(_17); + _6 = NonNull:: { pointer: copy _18 }; + StorageDead(_18); goto -> bb13; } bb7: { - StorageDead(_19); - StorageDead(_26); StorageDead(_16); - StorageDead(_24); - StorageDead(_23); - StorageDead(_15); - StorageDead(_14); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); goto -> bb10; } bb8: { - _23 = copy _15 as usize (Transmute); - switchInt(copy _23) -> [0: bb9, otherwise: bb12]; + _19 = copy _12 as usize (Transmute); + switchInt(copy _19) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_26); - StorageDead(_16); - StorageDead(_24); - StorageDead(_23); - StorageDead(_15); - StorageDead(_14); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); goto -> bb10; } bb10: { - StorageDead(_27); - StorageDead(_30); - StorageDead(_29); - StorageDead(_32); - StorageDead(_11); - StorageDead(_12); - StorageDead(_13); + StorageDead(_23); + StorageDead(_26); + StorageDead(_25); + StorageDead(_28); + StorageDead(_10); drop(_2) -> [return: bb11, unwind unreachable]; } @@ -265,51 +249,51 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb12: { - _24 = SubUnchecked(copy _23, const 1_usize); - _12 = copy _24 as *const T (Transmute); + _20 = SubUnchecked(copy _19, const 1_usize); + _9 = copy _20 as *const T (Transmute); goto -> bb13; } bb13: { - StorageLive(_25); - _25 = copy _14 as *const T (Transmute); - _26 = &(*_25); - StorageDead(_25); - _27 = Option::<&T>::Some(copy _26); - StorageDead(_26); - StorageDead(_16); - StorageDead(_24); + StorageLive(_21); + _21 = copy _11 as *const T (Transmute); + _22 = &(*_21); + StorageDead(_21); + _23 = Option::<&T>::Some(copy _22); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); + _24 = copy ((_23 as Some).0: &T); StorageDead(_23); - StorageDead(_15); - StorageDead(_14); - _28 = copy ((_27 as Some).0: &T); - StorageDead(_27); - _29 = copy _13; - _30 = AddWithOverflow(copy _13, const 1_usize); - assert(!move (_30.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _13, const 1_usize) -> [success: bb14, unwind unreachable]; + _25 = copy _10; + _26 = AddWithOverflow(copy _10, const 1_usize); + assert(!move (_26.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _10, const 1_usize) -> [success: bb14, unwind unreachable]; } bb14: { - _13 = move (_30.0: usize); + _10 = move (_26.0: usize); + StorageLive(_27); + _27 = (copy _25, copy _24); + _28 = Option::<(usize, &T)>::Some(move _27); + StorageDead(_27); + StorageDead(_26); + StorageDead(_25); + _29 = copy (((_28 as Some).0: (usize, &T)).0: usize); + _30 = copy (((_28 as Some).0: (usize, &T)).1: &T); StorageLive(_31); - _31 = (copy _29, copy _28); - _32 = Option::<(usize, &T)>::Some(move _31); - StorageDead(_31); - StorageDead(_30); - StorageDead(_29); - _33 = copy (((_32 as Some).0: (usize, &T)).0: usize); - _34 = copy (((_32 as Some).0: (usize, &T)).1: &T); - StorageLive(_35); - _35 = &_2; - StorageLive(_36); - _36 = (copy _33, copy _34); - _37 = >::call(move _35, move _36) -> [return: bb15, unwind unreachable]; + _31 = &_2; + StorageLive(_32); + _32 = (copy _29, copy _30); + _33 = >::call(move _31, move _32) -> [return: bb15, unwind unreachable]; } bb15: { - StorageDead(_36); - StorageDead(_35); StorageDead(_32); + StorageDead(_31); + StorageDead(_28); goto -> bb4; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index 42aa152ec99d6..28b12cdf36755 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -4,22 +4,22 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Enumerate>; let mut _12: std::iter::Enumerate>; - let mut _13: std::iter::Enumerate>; - let mut _14: &mut std::iter::Enumerate>; - let mut _15: std::option::Option<(usize, &T)>; - let mut _16: isize; - let mut _19: &impl Fn(usize, &T); - let mut _20: (usize, &T); - let _21: (); + let mut _13: &mut std::iter::Enumerate>; + let mut _14: std::option::Option<(usize, &T)>; + let mut _15: isize; + let mut _18: &impl Fn(usize, &T); + let mut _19: (usize, &T); + let _20: (); scope 1 { - debug iter => _13; - let _17: usize; - let _18: &T; + debug iter => _12; + let _16: usize; + let _17: &T; scope 2 { - debug i => _17; - debug x => _18; + debug i => _16; + debug x => _17; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,33 +92,30 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Enumerate::> { iter: copy _11, count: const 0_usize }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Enumerate::> { iter: copy _10, count: const 0_usize }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - _14 = &mut _13; - _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind: bb11]; + _13 = &mut _12; + _14 = > as Iterator>::next(move _13) -> [return: bb5, unwind: bb11]; } bb5: { - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_13); + StorageDead(_12); drop(_2) -> [return: bb7, unwind continue]; } @@ -128,18 +124,18 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _17 = copy (((_15 as Some).0: (usize, &T)).0: usize); - _18 = copy (((_15 as Some).0: (usize, &T)).1: &T); + _16 = copy (((_14 as Some).0: (usize, &T)).0: usize); + _17 = copy (((_14 as Some).0: (usize, &T)).1: &T); + StorageLive(_18); + _18 = &_2; StorageLive(_19); - _19 = &_2; - StorageLive(_20); - _20 = copy ((_15 as Some).0: (usize, &T)); - _21 = >::call(move _19, move _20) -> [return: bb9, unwind: bb11]; + _19 = copy ((_14 as Some).0: (usize, &T)); + _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_20); StorageDead(_19); + StorageDead(_18); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 0d65640ec9b14..4d0e3548e7d6b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -4,31 +4,29 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _26: std::option::Option<&T>; - let mut _28: &impl Fn(&T); - let mut _29: (&T,); - let _30: (); + let mut _22: std::option::Option<&T>; + let mut _24: &impl Fn(&T); + let mut _25: (&T,); + let _26: (); scope 1 { - debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _12; + debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _9; debug ((iter: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - let _27: &T; + let _23: &T; scope 2 { - debug x => _27; + debug x => _23; } scope 16 (inlined as Iterator>::next) { - let _13: std::ptr::NonNull; - let _15: std::ptr::NonNull; - let mut _18: bool; - let mut _21: std::ptr::NonNull; - let mut _23: usize; - let _25: &T; + let mut _6: std::ptr::NonNull; + let _10: std::ptr::NonNull; + let _12: std::ptr::NonNull; + let mut _15: bool; + let mut _19: usize; + let _21: &T; scope 17 { - let _14: *const T; + let _11: *const T; scope 18 { - let _22: usize; + let _18: usize; scope 19 { scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { @@ -44,21 +42,21 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } } scope 26 (inlined as PartialEq>::eq) { - let mut _16: *mut T; - let mut _17: *mut T; + let mut _13: *mut T; + let mut _14: *mut T; scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined NonNull::::as_ptr) { } } scope 29 (inlined NonNull::::add) { - let mut _19: *const T; - let mut _20: *const T; + let mut _16: *const T; + let mut _17: *const T; scope 30 (inlined NonNull::::as_ptr) { } } scope 31 (inlined NonNull::::as_ref::<'_>) { - let _24: *const T; + let _20: *const T; scope 32 (inlined NonNull::::as_ptr) { } scope 33 (inlined std::ptr::mut_ptr::::cast_const) { @@ -73,9 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -112,7 +108,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -133,88 +128,77 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - _11 = copy _6; - _12 = copy _10; goto -> bb4; } bb4: { - StorageLive(_26); - StorageLive(_13); - StorageLive(_14); StorageLive(_22); - StorageLive(_23); - StorageLive(_15); - StorageLive(_25); - _13 = copy _11; - _14 = copy _12; + StorageLive(_10); + StorageLive(_11); + StorageLive(_18); + StorageLive(_19); + StorageLive(_12); + StorageLive(_21); + _10 = copy _6; + _11 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_18); - _15 = copy _14 as std::ptr::NonNull (Transmute); - StorageLive(_16); - _16 = copy _13 as *mut T (Transmute); - StorageLive(_17); - _17 = copy _15 as *mut T (Transmute); - _18 = Eq(copy _16, copy _17); - StorageDead(_17); - StorageDead(_16); - switchInt(move _18) -> [0: bb6, otherwise: bb7]; + StorageLive(_15); + _12 = copy _11 as std::ptr::NonNull (Transmute); + StorageLive(_13); + _13 = copy _10 as *mut T (Transmute); + StorageLive(_14); + _14 = copy _12 as *mut T (Transmute); + _15 = Eq(copy _13, copy _14); + StorageDead(_14); + StorageDead(_13); + switchInt(move _15) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_18); - StorageLive(_21); - StorageLive(_20); - StorageLive(_19); - _19 = copy _13 as *const T (Transmute); - _20 = Offset(copy _19, const 1_usize); - StorageDead(_19); - _21 = NonNull:: { pointer: copy _20 }; - StorageDead(_20); - _11 = move _21; - StorageDead(_21); + StorageDead(_15); + StorageLive(_17); + StorageLive(_16); + _16 = copy _10 as *const T (Transmute); + _17 = Offset(copy _16, const 1_usize); + StorageDead(_16); + _6 = NonNull:: { pointer: copy _17 }; + StorageDead(_17); goto -> bb13; } bb7: { - StorageDead(_18); - StorageDead(_25); StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb8: { - _22 = copy _14 as usize (Transmute); - switchInt(copy _22) -> [0: bb9, otherwise: bb12]; + _18 = copy _11 as usize (Transmute); + switchInt(copy _18) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb10: { - StorageDead(_26); - StorageDead(_11); - StorageDead(_12); + StorageDead(_22); drop(_2) -> [return: bb11, unwind unreachable]; } @@ -223,35 +207,35 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb12: { - _23 = SubUnchecked(copy _22, const 1_usize); - _12 = copy _23 as *const T (Transmute); + _19 = SubUnchecked(copy _18, const 1_usize); + _9 = copy _19 as *const T (Transmute); goto -> bb13; } bb13: { + StorageLive(_20); + _20 = copy _10 as *const T (Transmute); + _21 = &(*_20); + StorageDead(_20); + _22 = Option::<&T>::Some(copy _21); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); + _23 = copy ((_22 as Some).0: &T); StorageLive(_24); - _24 = copy _13 as *const T (Transmute); - _25 = &(*_24); - StorageDead(_24); - _26 = Option::<&T>::Some(copy _25); - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); - _27 = copy ((_26 as Some).0: &T); - StorageLive(_28); - _28 = &_2; - StorageLive(_29); - _29 = (copy _27,); - _30 = >::call(move _28, move _29) -> [return: bb14, unwind unreachable]; + _24 = &_2; + StorageLive(_25); + _25 = (copy _23,); + _26 = >::call(move _24, move _25) -> [return: bb14, unwind unreachable]; } bb14: { - StorageDead(_29); - StorageDead(_28); - StorageDead(_26); + StorageDead(_25); + StorageDead(_24); + StorageDead(_22); goto -> bb4; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 02efb193474d6..2b5d8c27d7109 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -4,31 +4,29 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _26: std::option::Option<&T>; - let mut _28: &impl Fn(&T); - let mut _29: (&T,); - let _30: (); + let mut _22: std::option::Option<&T>; + let mut _24: &impl Fn(&T); + let mut _25: (&T,); + let _26: (); scope 1 { - debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _12; + debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _9; debug ((iter: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - let _27: &T; + let _23: &T; scope 2 { - debug x => _27; + debug x => _23; } scope 16 (inlined as Iterator>::next) { - let _13: std::ptr::NonNull; - let _15: std::ptr::NonNull; - let mut _18: bool; - let mut _21: std::ptr::NonNull; - let mut _23: usize; - let _25: &T; + let mut _6: std::ptr::NonNull; + let _10: std::ptr::NonNull; + let _12: std::ptr::NonNull; + let mut _15: bool; + let mut _19: usize; + let _21: &T; scope 17 { - let _14: *const T; + let _11: *const T; scope 18 { - let _22: usize; + let _18: usize; scope 19 { scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { @@ -44,21 +42,21 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } } scope 26 (inlined as PartialEq>::eq) { - let mut _16: *mut T; - let mut _17: *mut T; + let mut _13: *mut T; + let mut _14: *mut T; scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined NonNull::::as_ptr) { } } scope 29 (inlined NonNull::::add) { - let mut _19: *const T; - let mut _20: *const T; + let mut _16: *const T; + let mut _17: *const T; scope 30 (inlined NonNull::::as_ptr) { } } scope 31 (inlined NonNull::::as_ref::<'_>) { - let _24: *const T; + let _20: *const T; scope 32 (inlined NonNull::::as_ptr) { } scope 33 (inlined std::ptr::mut_ptr::::cast_const) { @@ -73,9 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -112,7 +108,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -133,88 +128,77 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - _11 = copy _6; - _12 = copy _10; goto -> bb4; } bb4: { - StorageLive(_26); - StorageLive(_13); - StorageLive(_14); StorageLive(_22); - StorageLive(_23); - StorageLive(_15); - StorageLive(_25); - _13 = copy _11; - _14 = copy _12; + StorageLive(_10); + StorageLive(_11); + StorageLive(_18); + StorageLive(_19); + StorageLive(_12); + StorageLive(_21); + _10 = copy _6; + _11 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_18); - _15 = copy _14 as std::ptr::NonNull (Transmute); - StorageLive(_16); - _16 = copy _13 as *mut T (Transmute); - StorageLive(_17); - _17 = copy _15 as *mut T (Transmute); - _18 = Eq(copy _16, copy _17); - StorageDead(_17); - StorageDead(_16); - switchInt(move _18) -> [0: bb6, otherwise: bb7]; + StorageLive(_15); + _12 = copy _11 as std::ptr::NonNull (Transmute); + StorageLive(_13); + _13 = copy _10 as *mut T (Transmute); + StorageLive(_14); + _14 = copy _12 as *mut T (Transmute); + _15 = Eq(copy _13, copy _14); + StorageDead(_14); + StorageDead(_13); + switchInt(move _15) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_18); - StorageLive(_21); - StorageLive(_20); - StorageLive(_19); - _19 = copy _13 as *const T (Transmute); - _20 = Offset(copy _19, const 1_usize); - StorageDead(_19); - _21 = NonNull:: { pointer: copy _20 }; - StorageDead(_20); - _11 = move _21; - StorageDead(_21); + StorageDead(_15); + StorageLive(_17); + StorageLive(_16); + _16 = copy _10 as *const T (Transmute); + _17 = Offset(copy _16, const 1_usize); + StorageDead(_16); + _6 = NonNull:: { pointer: copy _17 }; + StorageDead(_17); goto -> bb13; } bb7: { - StorageDead(_18); - StorageDead(_25); StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb8: { - _22 = copy _14 as usize (Transmute); - switchInt(copy _22) -> [0: bb9, otherwise: bb12]; + _18 = copy _11 as usize (Transmute); + switchInt(copy _18) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb10: { - StorageDead(_26); - StorageDead(_11); - StorageDead(_12); + StorageDead(_22); drop(_2) -> [return: bb11, unwind continue]; } @@ -223,35 +207,35 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb12: { - _23 = SubUnchecked(copy _22, const 1_usize); - _12 = copy _23 as *const T (Transmute); + _19 = SubUnchecked(copy _18, const 1_usize); + _9 = copy _19 as *const T (Transmute); goto -> bb13; } bb13: { + StorageLive(_20); + _20 = copy _10 as *const T (Transmute); + _21 = &(*_20); + StorageDead(_20); + _22 = Option::<&T>::Some(copy _21); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); + _23 = copy ((_22 as Some).0: &T); StorageLive(_24); - _24 = copy _13 as *const T (Transmute); - _25 = &(*_24); - StorageDead(_24); - _26 = Option::<&T>::Some(copy _25); - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); - _27 = copy ((_26 as Some).0: &T); - StorageLive(_28); - _28 = &_2; - StorageLive(_29); - _29 = (copy _27,); - _30 = >::call(move _28, move _29) -> [return: bb14, unwind: bb15]; + _24 = &_2; + StorageLive(_25); + _25 = (copy _23,); + _26 = >::call(move _24, move _25) -> [return: bb14, unwind: bb15]; } bb14: { - StorageDead(_29); - StorageDead(_28); - StorageDead(_26); + StorageDead(_25); + StorageDead(_24); + StorageDead(_22); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index 41e273151eca4..145375990710b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -5,28 +5,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _9: std::option::Option; - let mut _11: bool; - let mut _13: &impl Fn(usize, &T); - let mut _14: (usize, &T); - let _15: (); + let mut _8: std::option::Option; + let mut _10: bool; + let mut _12: &impl Fn(usize, &T); + let mut _13: (usize, &T); + let _14: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; - let _10: usize; + let _9: usize; scope 2 { - debug i => _10; - let _12: &T; + debug i => _9; + let _11: &T; scope 3 { - debug x => _12; + debug x => _11; } } scope 5 (inlined iter::range::>::next) { scope 6 (inlined as iter::range::RangeIteratorImpl>::spec_next) { + let mut _4: usize; let mut _6: bool; let _7: usize; - let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { scope 10 (inlined #[track_caller] core::num::::unchecked_add) { @@ -48,13 +47,12 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb0: { _3 = PtrMetadata(copy _1); - StorageLive(_4); _4 = const 0_usize; goto -> bb1; } bb1: { - StorageLive(_9); + StorageLive(_8); StorageLive(_6); StorageLive(_5); _5 = copy _4; @@ -65,8 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_8); drop(_2) -> [return: bb3, unwind unreachable]; } @@ -76,30 +73,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb4: { _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_usize); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); + _4 = AddUnchecked(copy _7, const 1_usize); + _8 = Option::::Some(copy _7); StorageDead(_6); - _10 = copy ((_9 as Some).0: usize); - _11 = Lt(copy _10, copy _3); - assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb5, unwind unreachable]; + _9 = copy ((_8 as Some).0: usize); + _10 = Lt(copy _9, copy _3); + assert(move _10, "index out of bounds: the length is {} but the index is {}", copy _3, copy _9) -> [success: bb5, unwind unreachable]; } bb5: { - _12 = &(*_1)[_10]; + _11 = &(*_1)[_9]; + StorageLive(_12); + _12 = &_2; StorageLive(_13); - _13 = &_2; - StorageLive(_14); - _14 = (copy _10, copy _12); - _15 = >::call(move _13, move _14) -> [return: bb6, unwind unreachable]; + _13 = (copy _9, copy _11); + _14 = >::call(move _12, move _13) -> [return: bb6, unwind unreachable]; } bb6: { - StorageDead(_14); StorageDead(_13); - StorageDead(_9); + StorageDead(_12); + StorageDead(_8); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index ec781c1480c74..8e573ef488fce 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -5,28 +5,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _9: std::option::Option; - let mut _11: bool; - let mut _13: &impl Fn(usize, &T); - let mut _14: (usize, &T); - let _15: (); + let mut _8: std::option::Option; + let mut _10: bool; + let mut _12: &impl Fn(usize, &T); + let mut _13: (usize, &T); + let _14: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; - let _10: usize; + let _9: usize; scope 2 { - debug i => _10; - let _12: &T; + debug i => _9; + let _11: &T; scope 3 { - debug x => _12; + debug x => _11; } } scope 5 (inlined iter::range::>::next) { scope 6 (inlined as iter::range::RangeIteratorImpl>::spec_next) { + let mut _4: usize; let mut _6: bool; let _7: usize; - let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { scope 10 (inlined #[track_caller] core::num::::unchecked_add) { @@ -48,13 +47,12 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb0: { _3 = PtrMetadata(copy _1); - StorageLive(_4); _4 = const 0_usize; goto -> bb1; } bb1: { - StorageLive(_9); + StorageLive(_8); StorageLive(_6); StorageLive(_5); _5 = copy _4; @@ -65,8 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_8); drop(_2) -> [return: bb3, unwind continue]; } @@ -76,30 +73,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb4: { _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_usize); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); + _4 = AddUnchecked(copy _7, const 1_usize); + _8 = Option::::Some(copy _7); StorageDead(_6); - _10 = copy ((_9 as Some).0: usize); - _11 = Lt(copy _10, copy _3); - assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb5, unwind: bb7]; + _9 = copy ((_8 as Some).0: usize); + _10 = Lt(copy _9, copy _3); + assert(move _10, "index out of bounds: the length is {} but the index is {}", copy _3, copy _9) -> [success: bb5, unwind: bb7]; } bb5: { - _12 = &(*_1)[_10]; + _11 = &(*_1)[_9]; + StorageLive(_12); + _12 = &_2; StorageLive(_13); - _13 = &_2; - StorageLive(_14); - _14 = (copy _10, copy _12); - _15 = >::call(move _13, move _14) -> [return: bb6, unwind: bb7]; + _13 = (copy _9, copy _11); + _14 = >::call(move _12, move _13) -> [return: bb6, unwind: bb7]; } bb6: { - StorageDead(_14); StorageDead(_13); - StorageDead(_9); + StorageDead(_12); + StorageDead(_8); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index ee638be7d7ac2..0adf268d766dd 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -4,22 +4,22 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } scope 18 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + let mut _13: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,37 +92,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Rev::> { iter: copy _11 }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Rev::> { iter: copy _10 }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind unreachable]; + StorageLive(_13); + _13 = &mut (_12.0: std::slice::Iter<'_, T>); + _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -132,18 +128,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = copy ((_15 as Some).0: &T); + _16 = copy ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (copy _17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind unreachable]; + _18 = (copy _16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index aee29d4d4fef9..cb0d640d445bb 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -4,22 +4,22 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } scope 18 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + let mut _13: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,37 +92,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Rev::> { iter: copy _11 }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Rev::> { iter: copy _10 }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind: bb11]; + StorageLive(_13); + _13 = &mut (_12.0: std::slice::Iter<'_, T>); + _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind: bb11]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind continue]; } @@ -132,18 +128,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = copy ((_15 as Some).0: &T); + _16 = copy ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (copy _17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; + _18 = (copy _16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir index c4d0e318b58dc..29bfe962974cf 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir @@ -7,7 +7,6 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { scope 1 (inlined std::cmp::impls::::ge) { scope 2 (inlined core::tuple::::ge) { let mut _7: std::ops::ControlFlow; - let _8: bool; scope 3 { } scope 4 (inlined std::cmp::impls::::__chaining_ge) { @@ -19,8 +18,8 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { } } scope 6 (inlined std::cmp::impls::::ge) { + let mut _8: f32; let mut _9: f32; - let mut _10: f32; } } } @@ -44,10 +43,7 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); - StorageLive(_8); - _8 = copy ((_7 as Break).0: bool); - _0 = copy _8; - StorageDead(_8); + _0 = copy ((_7 as Break).0: bool); goto -> bb3; } @@ -55,13 +51,13 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); + StorageLive(_8); + _8 = copy ((*_1).1: f32); StorageLive(_9); - _9 = copy ((*_1).1: f32); - StorageLive(_10); - _10 = copy ((*_2).1: f32); - _0 = Ge(move _9, move _10); - StorageDead(_10); + _9 = copy ((*_2).1: f32); + _0 = Ge(move _8, move _9); StorageDead(_9); + StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir index 44df8b279935b..7678c92a1f0c6 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir @@ -7,7 +7,6 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { scope 1 (inlined std::cmp::impls::::le) { scope 2 (inlined core::tuple::::le) { let mut _7: std::ops::ControlFlow; - let _8: bool; scope 3 { } scope 4 (inlined std::cmp::impls::::__chaining_le) { @@ -19,8 +18,8 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { } } scope 6 (inlined std::cmp::impls::::le) { + let mut _8: i16; let mut _9: i16; - let mut _10: i16; } } } @@ -44,10 +43,7 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); - StorageLive(_8); - _8 = copy ((_7 as Break).0: bool); - _0 = copy _8; - StorageDead(_8); + _0 = copy ((_7 as Break).0: bool); goto -> bb3; } @@ -55,13 +51,13 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); + StorageLive(_8); + _8 = copy ((*_1).1: i16); StorageLive(_9); - _9 = copy ((*_1).1: i16); - StorageLive(_10); - _10 = copy ((*_2).1: i16); - _0 = Le(move _9, move _10); - StorageDead(_10); + _9 = copy ((*_2).1: i16); + _0 = Le(move _8, move _9); StorageDead(_9); + StorageDead(_8); goto -> bb3; } From 19e5286c806978e094328559d172f9d7e4e8885f Mon Sep 17 00:00:00 2001 From: galileocap Date: Tue, 16 Sep 2025 19:20:39 -0300 Subject: [PATCH 0488/1900] Fix `unnecessary_unwrap` false negative when unwrapping a known value inside a macro call Fixes https://github.com/rust-lang/rust-clippy/issues/12295 changelog: [`unnecessary_unwrap`]: Fix false negative when unwrapping a known value inside a macro call --- clippy_lints/src/unwrap.rs | 1 + .../ui/checked_unwrap/simple_conditionals.rs | 22 ++++++++++++ .../checked_unwrap/simple_conditionals.stderr | 36 ++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 490da4f1e037a..34dfe5b6546fa 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -292,6 +292,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // Shouldn't lint when `expr` is in macro. if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { + walk_expr(self, expr); return; } // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 785b2473c0530..bba264080b406 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -273,6 +273,28 @@ const ISSUE14763: fn(Option) = |x| { } }; +fn issue12295() { + let option = Some(()); + + if option.is_some() { + println!("{:?}", option.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", option.unwrap()); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + println!("{:?}", result.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", result.unwrap()); + //~^ panicking_unwrap + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 939b509d85c91..2007a85954137 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -322,6 +322,40 @@ LL | if x.is_some() { LL | _ = x.unwrap(); | ^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = option` +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = result` +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -332,5 +366,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default -error: aborting due to 36 previous errors +error: aborting due to 40 previous errors From 53b91ea87fe4b430c33bd22dfdaaa6289bf9466a Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 22:29:04 +0000 Subject: [PATCH 0489/1900] Remove Rvalue::Len. --- compiler/rustc_borrowck/src/lib.rs | 3 +- .../src/polonius/legacy/loan_invalidations.rs | 9 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 - compiler/rustc_codegen_cranelift/src/base.rs | 6 -- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 25 +----- .../src/check_consts/check.rs | 3 +- .../src/check_consts/qualifs.rs | 4 +- .../src/check_consts/resolver.rs | 1 - .../rustc_const_eval/src/interpret/step.rs | 8 +- compiler/rustc_middle/src/mir/pretty.rs | 1 - compiler/rustc_middle/src/mir/statement.rs | 2 - compiler/rustc_middle/src/mir/syntax.rs | 10 --- compiler/rustc_middle/src/mir/visit.rs | 8 -- .../src/builder/custom/parse/instruction.rs | 6 +- .../src/builder/expr/as_place.rs | 2 +- .../src/builder/matches/test.rs | 3 +- .../src/impls/borrowed_locals.rs | 1 - .../src/move_paths/builder.rs | 1 - .../src/dataflow_const_prop.rs | 38 ++++----- compiler/rustc_mir_transform/src/gvn.rs | 77 +++++------------ .../src/known_panics_lint.rs | 15 ---- .../rustc_mir_transform/src/promote_consts.rs | 4 +- compiler/rustc_mir_transform/src/validate.rs | 8 -- .../src/unstable/convert/stable/mir.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mir.rs | 8 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- .../custom/arrays.arrays.built.after.mir | 10 ++- tests/mir-opt/building/custom/arrays.rs | 4 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 77 +++++++++++++++++ ....DataflowConstProp.32bit.panic-unwind.diff | 77 +++++++++++++++++ ...n.DataflowConstProp.64bit.panic-abort.diff | 77 +++++++++++++++++ ....DataflowConstProp.64bit.panic-unwind.diff | 77 +++++++++++++++++ .../mir-opt/dataflow-const-prop/slice_len.rs | 34 ++++++++ .../gvn.array_len.GVN.panic-abort.diff | 6 +- .../gvn.array_len.GVN.panic-unwind.diff | 6 +- ...implify-after-simplifycfg.panic-abort.diff | 71 ++++++++++++++++ ...mplify-after-simplifycfg.panic-unwind.diff | 71 ++++++++++++++++ .../mir-opt/instsimplify/combine_array_len.rs | 15 ++++ ...implifyComparisonIntegral.panic-abort.diff | 32 +++---- ...mplifyComparisonIntegral.panic-unwind.diff | 32 +++---- ...ray_len.array_len_raw.GVN.panic-abort.diff | 6 +- ...ay_len.array_len_raw.GVN.panic-unwind.diff | 6 +- ...en.array_len_reborrow.GVN.panic-abort.diff | 6 +- ...n.array_len_reborrow.GVN.panic-unwind.diff | 6 +- ...e_prop.debuginfo.ReferencePropagation.diff | 85 ++++++++++--------- 46 files changed, 656 insertions(+), 291 deletions(-) create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.rs create mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.rs diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5d2dda8b0e7cc..f196bd04d0e8f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1557,9 +1557,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + &Rvalue::Discriminant(place) => { let af = match *rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), Rvalue::Discriminant(..) => None, _ => unreachable!(), }; diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 99dd0b2dd4664..c2ad6fcb4b799 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -306,16 +306,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, op); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { - let af = match rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), - Rvalue::Discriminant(..) => None, - _ => unreachable!(), - }; + &Rvalue::Discriminant(place) => { self.access_place( location, place, - (Shallow(af), Read(ReadKind::Copy)), + (Shallow(None), Read(ReadKind::Copy)), LocalMutationIsAllowed::No, ); } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9e7..dcedf0b153221 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1631,7 +1631,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) | Rvalue::Discriminant(..) | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} } @@ -2201,7 +2200,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::RawPtr(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3a28dd7e73cb3..41e11e1de6163 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -834,12 +834,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: fx.bcx.ins().nop(); } } - Rvalue::Len(place) => { - let place = codegen_place(fx, place); - let usize_layout = fx.layout_of(fx.tcx.types.usize); - let len = codegen_array_len(fx, place); - lval.write_cvalue(fx, CValue::by_val(len, usize_layout)); - } Rvalue::ShallowInitBox(ref operand, content_ty) => { let content_ty = fx.monomorphize(content_ty); let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty)); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f8755874014d3..c30e6a505058d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -7,9 +7,9 @@ use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use tracing::{debug, instrument}; +use super::FunctionCx; use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; use super::place::{PlaceRef, PlaceValue, codegen_tag_value}; -use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; @@ -510,14 +510,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place_to_pointer(bx, place, mk_ptr) } - mir::Rvalue::Len(place) => { - let size = self.evaluate_array_len(bx, place); - OperandRef { - val: OperandValue::Immediate(size), - layout: bx.cx().layout_of(bx.tcx().types.usize), - } - } - mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) if let Some(op) = op_with_overflow.overflowing_to_wrapping() => { @@ -749,21 +741,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value { - // ZST are passed as operands and require special handling - // because codegen_place() panics if Local is operand. - if let Some(index) = place.as_local() - && let LocalRef::Operand(op) = self.locals[index] - && let ty::Array(_, n) = op.layout.ty.kind() - { - let n = n.try_to_target_usize(bx.tcx()).expect("expected monomorphic const in codegen"); - return bx.cx().const_usize(n); - } - // use common size calculation for non zero-sized types - let cg_value = self.codegen_place(bx, place.as_ref()); - cg_value.len(bx.cx()) - } - /// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref` fn codegen_place_to_pointer( &mut self, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ca707b50d50d9..3397bd9a68e4c 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -573,8 +573,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Use(_) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Discriminant(..) - | Rvalue::Len(_) => {} + | Rvalue::Discriminant(..) => {} Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index faf41f1658b70..34d1fdd8c8694 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -232,9 +232,7 @@ where Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - in_place::(cx, in_local, place.as_ref()) - } + Rvalue::Discriminant(place) => in_place::(cx, in_local, place.as_ref()), Rvalue::CopyForDeref(place) => in_place::(cx, in_local, place.as_ref()), diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index d98e5027e4d6b..664dda8701a63 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -197,7 +197,6 @@ where | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::ThreadLocalRef(..) | mir::Rvalue::Repeat(..) - | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 46950d60f8c78..923e00ad4cf1a 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -17,7 +17,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, interp_ok, throw_ub, throw_unsup_format, + Projectable, interp_ok, throw_ub, throw_unsup_format, }; use crate::interpret::EnteredTraceSpan; use crate::{enter_trace_span, util}; @@ -225,12 +225,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_repeat(operand, &dest)?; } - Len(place) => { - let src = self.eval_place(place)?; - let len = src.len(self)?; - self.write_scalar(Scalar::from_target_usize(len, self), &dest)?; - } - Ref(_, borrow_kind, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f9d0a5f0a3b66..96148fd5b9269 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1062,7 +1062,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { pretty_print_const(b, fmt, false)?; write!(fmt, "]") } - Len(ref a) => write!(fmt, "Len({a:?})"), Cast(ref kind, ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index ec2a8e86077d0..28294b47e90f2 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -697,7 +697,6 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Ref(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::Len(_) | Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToInt @@ -739,7 +738,6 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } - Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index d402ea4b04f9c..e6c8512564edc 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1407,16 +1407,6 @@ pub enum Rvalue<'tcx> { /// model. RawPtr(RawPtrKind, Place<'tcx>), - /// Yields the length of the place, as a `usize`. - /// - /// If the type of the place is an array, this is the array length. For slices (`[T]`, not - /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is - /// ill-formed for places of other types. - /// - /// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only - /// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing. - Len(Place<'tcx>), - /// Performs essentially all of the casts that can be performed via `as`. /// /// This allows for casts from/to a variety of types. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index b498b7b8912cc..07a15b3cd18a7 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -717,14 +717,6 @@ macro_rules! make_mir_visitor { self.visit_place(path, ctx, location); } - Rvalue::Len(path) => { - self.visit_place( - path, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - Rvalue::Cast(_cast_kind, operand, ty) => { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 41d3aefcbe6b5..54490e0050902 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -229,6 +229,11 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let source = self.parse_operand(args[0])?; Ok(Rvalue::Cast(CastKind::PtrToPtr, source, expr.ty)) }, + @call(mir_cast_unsize, args) => { + let source = self.parse_operand(args[0])?; + let kind = CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, CoercionSource::AsCast); + Ok(Rvalue::Cast(kind, source, expr.ty)) + }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => { @@ -247,7 +252,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let offset = self.parse_operand(args[1])?; Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, - @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 7c851ec465bdc..5a6bd2f413c2d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -663,7 +663,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For arrays it'll be `Operand::Constant` with the actual length; /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. - fn len_of_slice_or_array( + pub(in crate::builder) fn len_of_slice_or_array( &mut self, block: BasicBlock, place: Place<'tcx>, diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index d03794fe2d584..1b6d96e49f0c1 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -309,7 +309,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let actual = self.temp(usize_ty, test.span); // actual = len(place) - self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); + let length_op = self.len_of_slice_or_array(block, place, test.span, source_info); + self.cfg.push_assign(block, source_info, actual, Rvalue::Use(length_op)); // expected = let expected = self.push_usize(block, source_info, len); diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 9abb83434321d..a4e4e30a8bb6c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -91,7 +91,6 @@ where | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::BinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 48718cad597a8..f38bb44582400 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -413,7 +413,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) - | Rvalue::Len(..) | Rvalue::NullaryOp( NullOp::SizeOf | NullOp::AlignOf diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index fe53de31f7583..5c984984d3cc3 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -412,18 +412,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { state: &mut State>, ) -> ValueOrPlace> { let val = match rvalue { - Rvalue::Len(place) => { - let place_ty = place.ty(self.local_decls, self.tcx); - if let ty::Array(_, len) = place_ty.ty.kind() { - Const::Ty(self.tcx.types.usize, *len) - .try_eval_scalar(self.tcx, self.typing_env) - .map_or(FlatSet::Top, FlatSet::Elem) - } else if let [ProjectionElem::Deref] = place.projection[..] { - state.get_len(place.local.into(), &self.map) - } else { - FlatSet::Top - } - } Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); @@ -465,15 +453,23 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { let (val, _overflow) = self.binary_op(state, *op, left, right); val } - Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) { - FlatSet::Elem(value) => self - .ecx - .unary_op(*op, &value) - .discard_err() - .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - }, + Rvalue::UnaryOp(op, operand) => { + if let UnOp::PtrMetadata = op + && let Some(place) = operand.place() + && let Some(len) = self.map.find_len(place.as_ref()) + { + return ValueOrPlace::Place(len); + } + match self.eval_operand(operand, state) { + FlatSet::Elem(value) => self + .ecx + .unary_op(*op, &value) + .discard_err() + .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), + FlatSet::Bottom => FlatSet::Bottom, + FlatSet::Top => FlatSet::Top, + } + } Rvalue::NullaryOp(null_op, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20f4..30e68a3f65058 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -200,8 +200,6 @@ enum Value<'tcx> { Projection(VnIndex, ProjectionElem), /// Discriminant of the given value. Discriminant(VnIndex), - /// Length of an array or slice. - Len(VnIndex), // Operations. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -477,11 +475,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } - Len(slice) => { - let slice = self.evaluated[slice].as_ref()?; - let len = slice.len(&self.ecx).discard_err()?; - ImmTy::from_uint(len, ty).into() - } NullaryOp(null_op, arg_ty) => { let arg_layout = self.ecx.layout_of(arg_ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op @@ -841,7 +834,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Operations. - Rvalue::Len(ref mut place) => return self.simplify_len(place, location), Rvalue::Cast(ref mut kind, ref mut value, to) => { return self.simplify_cast(kind, value, to, location); } @@ -1049,7 +1041,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if op == UnOp::PtrMetadata { let mut was_updated = false; loop { - match self.get(arg_index) { + arg_index = match self.get(arg_index) { // Pointer casts that preserve metadata, such as // `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`. // It's critical that this not eliminate cases like @@ -1061,9 +1053,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Cast { kind: CastKind::PtrToPtr, value: inner } if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => { - arg_index = *inner; - was_updated = true; - continue; + *inner + } + + // We have an unsizing cast, which assigns the length to wide pointer metadata. + Value::Cast { + kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), + value: from, + } if let Some(from) = self.ty(*from).builtin_deref(true) + && let ty::Array(_, len) = from.kind() + && let Some(to) = self.ty(arg_index).builtin_deref(true) + && let ty::Slice(..) = to.kind() => + { + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } // `&mut *p`, `&raw *p`, etc don't change metadata. @@ -1072,18 +1074,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place.as_ref() && let Some(local_index) = self.locals[local] => { - arg_index = local_index; - was_updated = true; - continue; + local_index } - _ => { - if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { - *arg_op = op; - } - break; - } - } + _ => break, + }; + was_updated = true; + } + + if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { + *arg_op = op; } } @@ -1407,39 +1407,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Some(self.insert(to, Value::Cast { kind, value })) } - fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { - // Trivial case: we are fetching a statically known length. - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind() { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - let mut inner = self.simplify_place_value(place, location)?; - - // The length information is stored in the wide pointer. - // Reborrowing copies length information from one pointer to the other. - while let Value::Address { place: borrowed, .. } = self.get(inner) - && let [PlaceElem::Deref] = borrowed.projection[..] - && let Some(borrowed) = self.locals[borrowed.local] - { - inner = borrowed; - } - - // We have an unsizing cast, which assigns the length to wide pointer metadata. - if let Value::Cast { kind, value: from } = self.get(inner) - && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind - && let Some(from) = self.ty(*from).builtin_deref(true) - && let ty::Array(_, len) = from.kind() - && let Some(to) = self.ty(inner).builtin_deref(true) - && let ty::Slice(..) = to.kind() - { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - // Fallback: a symbolic `Len`. - Some(self.insert(self.tcx.types.usize, Value::Len(inner))) - } - fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 481c794190925..aaacc5866a2ae 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -441,7 +441,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Use(..) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) @@ -604,20 +603,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - Len(place) => { - let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() - { - n.try_to_target_usize(self.tcx)? - } else { - match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_err()?, - Value::Aggregate { fields, .. } => fields.len() as u64, - Value::Uninit => return None, - } - }; - ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() - } - Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9ea2eb4f25d91..a0b0c8c990f33 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -437,9 +437,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(op)? } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - self.validate_place(place.as_ref())? - } + Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?, Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 99e4782e4700c..c8a9a88dc3fe3 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1064,14 +1064,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(..) => {} - Rvalue::Len(p) => { - let pty = p.ty(&self.body.local_decls, self.tcx).ty; - check_kinds!( - pty, - "Cannot compute length of non-array type {:?}", - ty::Array(..) | ty::Slice(..) - ); - } Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index be8ee80bed3c0..b10af6526ead5 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -215,7 +215,6 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { mutability.stable(tables, cx), place.stable(tables, cx), ), - Len(place) => crate::mir::Rvalue::Len(place.stable(tables, cx)), Cast(cast_kind, op, ty) => crate::mir::Rvalue::Cast( cast_kind.stable(tables, cx), op.stable(tables, cx), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6d..16b8b46f97cf3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1413,6 +1413,7 @@ symbols! { mir_call, mir_cast_ptr_to_ptr, mir_cast_transmute, + mir_cast_unsize, mir_checked, mir_copy_for_deref, mir_debuginfo, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 55dcf7cd47e97..c4bd10e606aab 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -401,7 +401,6 @@ define!("mir_storage_dead", fn StorageDead(local: T)); define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); -define!("mir_len", fn Len(place: T) -> usize); define!( "mir_ptr_metadata", fn PtrMetadata(place: *const P) ->

::Pointer) { ); } -// FIXME(next-solver): Was `&'a T` but now getting error lifetime. -// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn gats_with_impl_trait() { // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot @@ -4215,21 +4213,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'? T + //^ &'a T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'? i32 + //^ &'a i32 let a = v.get::(); - //^ &'? i64 + //^ &'a i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'? i32 + //^ &'a i32 let a = v.get::(); - //^ &'? i64 + //^ &'a i64 } "#, ); @@ -4259,8 +4257,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc - 170..192 'v.get:...eref()': {unknown} + 170..184 'v.get::()': {unknown} + 170..192 'v.get:...eref()': &'? {unknown} "#]], ); } @@ -4953,7 +4951,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': >::Error "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 52ab808d22841..888392b0ff207 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -76,7 +76,7 @@ use hir_expand::{ }; use hir_ty::{ AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, - GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, + GenericArgData, Interner, ParamKind, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic, ValueTyDefId, WhereClause, all_super_traits, autoderef, check_orphan_rules, consteval::{ConstExt, try_const_usize, unknown_const_as_generic}, @@ -4973,6 +4973,7 @@ impl<'db> Type<'db> { | TyKind::Tuple(_, substitution) | TyKind::OpaqueType(_, substitution) | TyKind::AssociatedType(_, substitution) + | TyKind::Alias(AliasTy::Projection(ProjectionTy { substitution, .. })) | TyKind::FnDef(_, substitution) => { substitution.iter(Interner).filter_map(|x| x.ty(Interner)).any(|ty| go(db, ty)) } @@ -5819,7 +5820,11 @@ impl<'db> Type<'db> { cb(type_.derived(ty.clone())); walk_substs(db, type_, substs, cb); } - TyKind::AssociatedType(_, substs) => { + TyKind::AssociatedType(_, substs) + | TyKind::Alias(AliasTy::Projection(hir_ty::ProjectionTy { + substitution: substs, + .. + })) => { if ty.associated_type_parent_trait(db).is_some() { cb(type_.derived(ty.clone())); } From b2b33f9faab81507abda08d025c0680eb8d4995d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 04:51:33 +0300 Subject: [PATCH 0287/1900] Always coerce in a cast, even when there are unknown types This cause the relationships between inference vars to get recorded. --- .../crates/hir-ty/src/infer/cast.rs | 13 +++++++------ .../hir-ty/src/tests/regression/new_solver.rs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 43364963eb054..bc3ee3c4c54d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -106,6 +106,13 @@ impl CastCheck { self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + // This should always come first so that we apply the coercion, which impacts infer vars. + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { + apply_adjustments(self.source_expr, adj); + set_coercion_cast(self.source_expr); + return Ok(()); + } + if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { return Ok(()); } @@ -126,12 +133,6 @@ impl CastCheck { return Ok(()); } - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); - set_coercion_cast(self.source_expr); - return Ok(()); - } - self.do_check(table, apply_adjustments) .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 6d6c56696a30d..4df788638a315 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -98,3 +98,21 @@ fn main() { "#, ); } + +#[test] +fn cast_error_type() { + check_infer( + r#" +fn main() { + let foo: [_; _] = [false] as _; +} + "#, + expect![[r#" + 10..47 '{ le...s _; }': () + 18..21 'foo': [bool; 1] + 32..39 '[false]': [bool; 1] + 32..44 '[false] as _': [bool; 1] + 33..38 'false': bool + "#]], + ); +} From 64ea775d27599a33b070dd3aa4353e1e7faee0b4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 15:56:48 +0200 Subject: [PATCH 0288/1900] interpret: copy_provenance: avoid large intermediate buffer for large repeat counts --- .../rustc_const_eval/src/interpret/memory.rs | 4 +- compiler/rustc_data_structures/src/lib.rs | 1 + .../rustc_data_structures/src/sorted_map.rs | 33 +++--- .../src/sorted_map/tests.rs | 10 +- .../src/mir/interpret/allocation.rs | 9 +- .../interpret/allocation/provenance_map.rs | 105 ++++++++---------- 6 files changed, 81 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6ec85648d6d88..43bcd34eb36de 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1503,7 +1503,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This will also error if copying partial provenance is not supported. let provenance = src_alloc .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) + .prepare_copy(src_range, self) .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1589,7 +1589,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { num_copies, ); // copy the provenance to the destination - dest_alloc.provenance_apply_copy(provenance); + dest_alloc.provenance_apply_copy(provenance, alloc_range(dest_offset, size), num_copies); interp_ok(()) } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 17da3ea83c819..e4e86bcc41aa6 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -34,6 +34,7 @@ #![feature(sized_hierarchy)] #![feature(test)] #![feature(thread_id_value)] +#![feature(trusted_len)] #![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index c002d47815b17..c3c173b3fc4d8 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -1,6 +1,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::Debug; +use std::iter::TrustedLen; use std::mem; use std::ops::{Bound, Index, IndexMut, RangeBounds}; @@ -215,32 +216,36 @@ impl SortedMap { /// It is up to the caller to make sure that the elements are sorted by key /// and that there are no duplicates. #[inline] - pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) { - if elements.is_empty() { + pub fn insert_presorted( + &mut self, + // We require `TrustedLen` to ensure that the `splice` below is actually efficient. + mut elements: impl Iterator + DoubleEndedIterator + TrustedLen, + ) { + let Some(first) = elements.next() else { return; - } - - debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0)); + }; - let start_index = self.lookup_index_for(&elements[0].0); + let start_index = self.lookup_index_for(&first.0); let elements = match start_index { Ok(index) => { - let mut elements = elements.into_iter(); - self.data[index] = elements.next().unwrap(); - elements + self.data[index] = first; // overwrite first element + elements.chain(None) // insert the rest below } Err(index) => { - if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { + let last = elements.next_back(); + if index == self.data.len() + || last.as_ref().is_none_or(|l| l.0 < self.data[index].0) + { // We can copy the whole range without having to mix with // existing elements. - self.data.splice(index..index, elements); + self.data + .splice(index..index, std::iter::once(first).chain(elements).chain(last)); return; } - let mut elements = elements.into_iter(); - self.data.insert(index, elements.next().unwrap()); - elements + self.data.insert(index, first); + elements.chain(last) // insert the rest below } }; diff --git a/compiler/rustc_data_structures/src/sorted_map/tests.rs b/compiler/rustc_data_structures/src/sorted_map/tests.rs index ea4d2f1feacc0..17d0d3cb1701d 100644 --- a/compiler/rustc_data_structures/src/sorted_map/tests.rs +++ b/compiler/rustc_data_structures/src/sorted_map/tests.rs @@ -171,7 +171,7 @@ fn test_insert_presorted_non_overlapping() { map.insert(2, 0); map.insert(8, 0); - map.insert_presorted(vec![(3, 0), (7, 0)]); + map.insert_presorted(vec![(3, 0), (7, 0)].into_iter()); let expected = vec![2, 3, 7, 8]; assert_eq!(keys(map), expected); @@ -183,7 +183,7 @@ fn test_insert_presorted_first_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(2, 0), (7, 7)]); + map.insert_presorted(vec![(2, 0), (7, 7)].into_iter()); let expected = vec![(2, 0), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -195,7 +195,7 @@ fn test_insert_presorted_last_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(3, 3), (8, 0)]); + map.insert_presorted(vec![(3, 3), (8, 0)].into_iter()); let expected = vec![(2, 2), (3, 3), (8, 0)]; assert_eq!(elements(map), expected); @@ -207,7 +207,7 @@ fn test_insert_presorted_shuffle() { map.insert(2, 2); map.insert(7, 7); - map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)]); + map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -219,7 +219,7 @@ fn test_insert_presorted_at_end() { map.insert(1, 1); map.insert(2, 2); - map.insert_presorted(vec![(3, 3), (8, 8)]); + map.insert_presorted(vec![(3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (8, 8)]; assert_eq!(elements(map), expected); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 67962813ae437..8e603ce1b911a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -849,8 +849,13 @@ impl Allocation /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. - pub fn provenance_apply_copy(&mut self, copy: ProvenanceCopy) { - self.provenance.apply_copy(copy) + pub fn provenance_apply_copy( + &mut self, + copy: ProvenanceCopy, + range: AllocRange, + repeat: u64, + ) { + self.provenance.apply_copy(copy, range, repeat) } /// Applies a previously prepared copy of the init mask. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 720e58d7aa0eb..46f28328aebc3 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -278,90 +278,78 @@ impl ProvenanceMap { /// A partial, owned list of provenance to transfer into another allocation. /// -/// Offsets are already adjusted to the destination allocation. +/// Offsets are relative to the beginning of the copied range. pub struct ProvenanceCopy { - dest_ptrs: Option>, - dest_bytes: Option>, + ptrs: Box<[(Size, Prov)]>, + bytes: Box<[(Size, (Prov, u8))]>, } impl ProvenanceMap { pub fn prepare_copy( &self, - src: AllocRange, - dest: Size, - count: u64, + range: AllocRange, cx: &impl HasDataLayout, ) -> AllocResult> { - let shift_offset = move |idx, offset| { - // compute offset for current repetition - let dest_offset = dest + src.size * idx; // `Size` operations - // shift offsets from source allocation to destination allocation - (offset - src.start) + dest_offset // `Size` operations - }; + let shift_offset = move |offset| offset - range.start; let ptr_size = cx.data_layout().pointer_size(); // # Pointer-sized provenances // Get the provenances that are entirely within this range. // (Different from `range_get_ptrs` which asks if they overlap the range.) // Only makes sense if we are copying at least one pointer worth of bytes. - let mut dest_ptrs_box = None; - if src.size >= ptr_size { - let adjusted_end = Size::from_bytes(src.end().bytes() - (ptr_size.bytes() - 1)); - let ptrs = self.ptrs.range(src.start..adjusted_end); - // If `count` is large, this is rather wasteful -- we are allocating a big array here, which - // is mostly filled with redundant information since it's just N copies of the same `Prov`s - // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range` - // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces - // the right sequence of provenance for all N copies. - // Basically, this large array would have to be created anyway in the target allocation. - let mut dest_ptrs = Vec::with_capacity(ptrs.len() * (count as usize)); - for i in 0..count { - dest_ptrs - .extend(ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_ptrs.len(), dest_ptrs.capacity()); - dest_ptrs_box = Some(dest_ptrs.into_boxed_slice()); + let mut ptrs_box: Box<[_]> = Box::new([]); + if range.size >= ptr_size { + let adjusted_end = Size::from_bytes(range.end().bytes() - (ptr_size.bytes() - 1)); + let ptrs = self.ptrs.range(range.start..adjusted_end); + ptrs_box = ptrs.iter().map(|&(offset, reloc)| (shift_offset(offset), reloc)).collect(); }; // # Byte-sized provenances // This includes the existing bytewise provenance in the range, and ptr provenance // that overlaps with the begin/end of the range. - let mut dest_bytes_box = None; - let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); - let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); + let mut bytes_box: Box<[_]> = Box::new([]); + let begin_overlap = self.range_ptrs_get(alloc_range(range.start, Size::ZERO), cx).first(); + let end_overlap = self.range_ptrs_get(alloc_range(range.end(), Size::ZERO), cx).first(); // We only need to go here if there is some overlap or some bytewise provenance. if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); - // For really small copies, make sure we don't run off the end of the `src` range. - let entry_end = cmp::min(entry.0 + ptr_size, src.end()); - for offset in src.start..entry_end { - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + // For really small copies, make sure we don't run off the end of the range. + let entry_end = cmp::min(entry.0 + ptr_size, range.end()); + for offset in range.start..entry_end { + bytes.push((shift_offset(offset), (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); } // Then the main part, bytewise provenance from `self.bytes`. - bytes.extend(self.range_bytes_get(src)); + bytes.extend( + self.range_bytes_get(range) + .iter() + .map(|&(offset, reloc)| (shift_offset(offset), reloc)), + ); // And finally possibly parts of a pointer at the end. if let Some(entry) = end_overlap { trace!("end overlapping entry: {entry:?}"); - // For really small copies, make sure we don't start before `src` does. - let entry_start = cmp::max(entry.0, src.start); - for offset in entry_start..src.end() { + // For really small copies, make sure we don't start before `range` does. + let entry_start = cmp::max(entry.0, range.start); + for offset in entry_start..range.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { // The last entry, if it exists, has a lower offset than us, so we // can add it at the end and remain sorted. - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + bytes.push(( + shift_offset(offset), + (entry.1, (offset - entry.0).bytes() as u8), + )); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we // already added this in the "pointer at the start" part above. - assert!(entry.0 <= src.start); + assert!(entry.0 <= range.start); } } } else { @@ -372,33 +360,34 @@ impl ProvenanceMap { if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { // FIXME(#146291): We need to ensure that we don't mix different pointers with // the same provenance. - return Err(AllocError::ReadPartialPointer(src.start)); + return Err(AllocError::ReadPartialPointer(range.start)); } // And again a buffer for the new list on the target side. - let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize)); - for i in 0..count { - dest_bytes - .extend(bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_bytes.len(), dest_bytes.capacity()); - dest_bytes_box = Some(dest_bytes.into_boxed_slice()); + bytes_box = bytes.into_boxed_slice(); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + Ok(ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box }) } /// Applies a provenance copy. /// The affected range, as defined in the parameters to `prepare_copy` is expected /// to be clear of provenance. - pub fn apply_copy(&mut self, copy: ProvenanceCopy) { - if let Some(dest_ptrs) = copy.dest_ptrs { - self.ptrs.insert_presorted(dest_ptrs.into()); + pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repeat: u64) { + let shift_offset = |idx: u64, offset: Size| offset + range.start + idx * range.size; + if !copy.ptrs.is_empty() { + for i in 0..repeat { + self.ptrs.insert_presorted( + copy.ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), + ); + } } - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); + if !copy.bytes.is_empty() { + for i in 0..repeat { + self.bytes.get_or_insert_with(Box::default).insert_presorted( + copy.bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), + ); + } } } } From 7abbc9c8b2c638752e2a6b0913ed1e93e14d21ba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 16:10:37 +0200 Subject: [PATCH 0289/1900] avoid calling insert_presorted more than once --- .../rustc_data_structures/src/sorted_map.rs | 2 +- .../interpret/allocation/provenance_map.rs | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index c3c173b3fc4d8..15e3e6ea4c329 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -249,7 +249,7 @@ impl SortedMap { } }; - // Insert the rest + // Insert the rest. This is super inefficicent since each insertion copies the entire tail. for (k, v) in elements { self.insert(k, v); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 46f28328aebc3..67baf63bbfadd 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -376,18 +376,24 @@ impl ProvenanceMap { pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repeat: u64) { let shift_offset = |idx: u64, offset: Size| offset + range.start + idx * range.size; if !copy.ptrs.is_empty() { - for i in 0..repeat { - self.ptrs.insert_presorted( - copy.ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), - ); - } + // We want to call `insert_presorted` only once so that, if possible, the entries + // after the range we insert are moved back only once. + let chunk_len = copy.ptrs.len() as u64; + self.ptrs.insert_presorted((0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.ptrs[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + })); } if !copy.bytes.is_empty() { - for i in 0..repeat { - self.bytes.get_or_insert_with(Box::default).insert_presorted( - copy.bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), - ); - } + let chunk_len = copy.bytes.len() as u64; + self.bytes.get_or_insert_with(Box::default).insert_presorted( + (0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.bytes[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + }), + ); } } } From e4f69ea1af40cd43da1aec1d6489ac6e78d3e769 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 09:35:45 +0200 Subject: [PATCH 0290/1900] fix(needless_closure): don't lint on `AsyncFn*`s --- clippy_lints/src/eta_reduction.rs | 27 +++++++++++++++------------ tests/ui/eta.fixed | 8 ++++++++ tests/ui/eta.rs | 8 ++++++++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2da1c2bad1174..752f39b4e6dc1 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -197,6 +197,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // in a type which is `'static`. // For now ignore all callee types which reference a type parameter. && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + // Rule out `AsyncFn*`s, because while they can be called as `|x| f(x)`, + // they can't be passed directly into a place expecting an `Fn*` (#13892) + && let Ok((closure_kind, _)) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { span_lint_hir_and_then( cx, @@ -213,19 +225,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match closure_kind { // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + ClosureKind::FnMut => snippet = format!("&mut {snippet}"), + ClosureKind::Fn if !callee_ty_raw.is_ref() => { snippet = format!("&{snippet}"); }, _ => (), diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 6944a979c05e6..107318e5323d0 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5bcc1cb26fd78..b85e8e75153a1 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} From 8ecda4b7f866d77d4a87c5b5bc601d4078841093 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 12:05:53 +0200 Subject: [PATCH 0291/1900] interpret: fix overlapping aggregate initialization --- .../rustc_const_eval/src/interpret/place.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 16 +++++++++++++--- .../fail/both_borrows/issue-miri-1050-1.rs | 2 ++ .../fail/both_borrows/issue-miri-1050-2.rs | 2 ++ .../return_invalid_shr.stack.stderr | 2 +- .../return_invalid_shr.tree.stderr | 2 +- .../return_invalid_shr_option.stack.stderr | 2 +- .../return_invalid_shr_option.tree.stderr | 2 +- .../return_invalid_shr_tuple.stack.stderr | 2 +- .../return_invalid_shr_tuple.tree.stderr | 2 +- .../fail/overlapping_assignment_aggregate.rs | 18 ++++++++++++++++++ .../overlapping_assignment_aggregate.stderr | 15 +++++++++++++++ .../miri/tests/fail/validity/nonzero.stderr | 2 +- ...overlapping_assignment_aggregate_scalar.rs | 19 +++++++++++++++++++ 14 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs create mode 100644 src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr create mode 100644 src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index a86fdf80f601a..cd34892f029cf 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -858,7 +858,7 @@ where /// Also, if you use this you are responsible for validating that things get copied at the /// right type. #[instrument(skip(self), level = "trace")] - fn copy_op_no_validate( + pub(super) fn copy_op_no_validate( &mut self, src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 23d362de308f3..46950d60f8c78 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -310,7 +310,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { operands: &IndexSlice>, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - self.write_uninit(dest)?; // make sure all the padding ends up as uninit let (variant_index, variant_dest, active_field_index) = match *kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { let variant_dest = self.project_downcast(dest, variant_index)?; @@ -346,9 +345,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let field_index = active_field_index.unwrap_or(field_index); let field_dest = self.project_field(&variant_dest, field_index)?; let op = self.eval_operand(operand, Some(field_dest.layout))?; - self.copy_op(&op, &field_dest)?; + // We validate manually below so we don't have to do it here. + self.copy_op_no_validate(&op, &field_dest, /*allow_transmute*/ false)?; } - self.write_discriminant(variant_index, dest) + self.write_discriminant(variant_index, dest)?; + // Validate that the entire thing is valid, and reset padding that might be in between the + // fields. + if M::enforce_validity(self, dest.layout()) { + self.validate_operand( + dest, + M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, + )?; + } + interp_ok(()) } /// Repeats `operand` into the destination. `dest` must have array type, and that type diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs index dd7dae9cecf91..d907c5de79700 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs @@ -1,4 +1,6 @@ //@revisions: stack tree +// Ensure this even hits the aliasing model +//@compile-flags: -Zmiri-disable-validation //@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: pointer not dereferenceable diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs index 5c947e6414258..b54f27bb8b23e 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs @@ -1,4 +1,6 @@ //@revisions: stack tree +// Ensure this even hits the aliasing model +//@compile-flags: -Zmiri-disable-validation //@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: is a dangling pointer use std::ptr::NonNull; diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr index d52143500c46a..7c4fe74870121 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr @@ -11,7 +11,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = unsafe { &(*xraw).1 }; | ^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr index ee0f313d980da..a8e3553aae2c2 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = unsafe { &(*xraw).1 }; | ^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr index d66c8eeb1af69..8411437ea4c97 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr @@ -14,7 +14,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = Some(unsafe { &(*xraw).1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr index 16110e5b062cc..39da45ad6db4d 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = Some(unsafe { &(*xraw).1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr index 727b52ec72965..a7c422aa73f2c 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr @@ -14,7 +14,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = (unsafe { &(*xraw).1 },); | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr index f93698f570ee4..66b03e57905e9 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = (unsafe { &(*xraw).1 },); | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs new file mode 100644 index 0000000000000..8d7b1946242c0 --- /dev/null +++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs @@ -0,0 +1,18 @@ +//! This is like `pass/overlapping_assignment_aggregate_scalar.rs` but with a non-scalar +//! type, and that makes it definite UB. +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let _1: ([u8; 1],); + { + _1.0 = [0_u8; 1]; + _1 = (_1.0, ); //~ERROR: overlapping ranges + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr new file mode 100644 index 0000000000000..f2a6d326b718b --- /dev/null +++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges + --> tests/fail/overlapping_assignment_aggregate.rs:LL:CC + | +LL | _1 = (_1.0, ); + | ^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/overlapping_assignment_aggregate.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/nonzero.stderr b/src/tools/miri/tests/fail/validity/nonzero.stderr index 0c3a35d6b9fa6..7be3ef46639ca 100644 --- a/src/tools/miri/tests/fail/validity/nonzero.stderr +++ b/src/tools/miri/tests/fail/validity/nonzero.stderr @@ -2,7 +2,7 @@ error: Undefined Behavior: constructing invalid value: encountered 0, but expect --> tests/fail/validity/nonzero.rs:LL:CC | LL | let _x = Some(unsafe { NonZero(0) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | ^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs new file mode 100644 index 0000000000000..0cb2f7642424e --- /dev/null +++ b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs @@ -0,0 +1,19 @@ +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let _1: (u8,); + { + _1.0 = 0_u8; + // This is a scalar type, so overlap is (for now) not UB. + // However, we used to treat such overlapping assignments incorrectly + // (see ). + _1 = (_1.0, ); + Return() + } + } +} From 72225060edefb6ae4014c671e9953f9822ddff7a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 14:27:43 +0200 Subject: [PATCH 0292/1900] clarify current MIR semantics re: overlapping assignment and double-check that we match it in codegen --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 11 ++++++++++- compiler/rustc_middle/src/mir/syntax.rs | 8 +++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f6f2e3f2a3a3c..f8755874014d3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,5 +1,5 @@ use itertools::Itertools as _; -use rustc_abi::{self as abi, FIRST_VARIANT}; +use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -25,6 +25,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match *rvalue { mir::Rvalue::Use(ref operand) => { let cg_operand = self.codegen_operand(bx, operand); + // Crucially, we do *not* use `OperandValue::Ref` for types with + // `BackendRepr::Scalar | BackendRepr::ScalarPair`. This ensures we match the MIR + // semantics regarding when assignment operators allow overlap of LHS and RHS. + if matches!( + cg_operand.layout.backend_repr, + BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..), + ) { + debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..))); + } // FIXME: consider not copying constants through stack. (Fixable by codegen'ing // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?) cg_operand.val.store(bx, dest); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 3b8def67f92d7..d402ea4b04f9c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -327,9 +327,11 @@ pub enum StatementKind<'tcx> { /// interesting for optimizations? Do we want to allow such optimizations? /// /// **Needs clarification**: We currently require that the LHS place not overlap with any place - /// read as part of computation of the RHS for some rvalues (generally those not producing - /// primitives). This requirement is under discussion in [#68364]. As a part of this discussion, - /// it is also unclear in what order the components are evaluated. + /// read as part of computation of the RHS for some rvalues. This requirement is under + /// discussion in [#68364]. Specifically, overlap is permitted only for assignments of a type + /// with `BackendRepr::Scalar | BackendRepr::ScalarPair` where all the scalar fields are + /// [`Scalar::Initialized`][rustc_abi::Scalar::Initialized]. As a part of this discussion, it is + /// also unclear in what order the components are evaluated. /// /// [#68364]: https://github.com/rust-lang/rust/issues/68364 /// From edbc0a08fb8bafbb80b4522ae0050244bb21e4f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:09:28 +0200 Subject: [PATCH 0293/1900] weak memory tests: add more info on where they come from --- .../tests/pass/0weak_memory_consistency.rs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index e4ed9675de822..16969d9649c94 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -14,13 +14,6 @@ // To mitigate this, each test is ran enough times such that the chance // of spurious success is very low. These tests never spuriously fail. -// Test cases and their consistent outcomes are from -// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/ -// Based on -// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber, -// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011. -// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf. - use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering, fence}; use std::thread::spawn; @@ -56,6 +49,7 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { val } +/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7 fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); @@ -75,19 +69,25 @@ fn test_corr() { let j3 = spawn(move || { // | | spin_until_i32(&y, Acquire, 1); // <---------+ | x.load(Relaxed) // <----------------------------------------------+ - // The two reads on x are ordered by hb, so they cannot observe values - // differently from the modification order. If the first read observed - // 2, then the second read must observe 2 as well. }); j1.join().unwrap(); let r2 = j2.join().unwrap(); let r3 = j3.join().unwrap(); + // The two reads on x are ordered by hb, so they cannot observe values + // differently from the modification order. If the first read observed + // 2, then the second read must observe 2 as well. if r2 == 2 { assert_eq!(r3, 2); } } +/// This test case is from: +/// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/, "WRC" +/// Based on +/// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber, +/// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011. +/// Available: https://www.cl.cam.ac.uk/~pes20/cpp/popl085ap-sewell.pdf. fn test_wrc() { let x = static_atomic(0); let y = static_atomic(0); @@ -114,6 +114,8 @@ fn test_wrc() { assert_eq!(r3, 1); } +/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/: +/// MP (na_rel+acq_na) fn test_message_passing() { let mut var = 0u32; let ptr = &mut var as *mut u32; @@ -139,7 +141,8 @@ fn test_message_passing() { assert_eq!(r2, 1); } -// LB+acq_rel+acq_rel +/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/: +/// LB+acq_rel+acq_rel fn test_load_buffering_acq_rel() { let x = static_atomic(0); let y = static_atomic(0); From 0e0b254df9b6f7abe4aa0efa8617ac97baa2bea7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:16:49 +0200 Subject: [PATCH 0294/1900] ensure we do not see the inconsistent execution from Figure 8 --- src/tools/miri/tests/pass/0weak_memory_consistency.rs | 3 ++- src/tools/miri/tests/pass/weak_memory/weak.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index 16969d9649c94..142080c1372d7 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -49,7 +49,8 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { val } -/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7 +/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7. +/// (The Figure 8 test is in `weak_memory/weak.rs`.) fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index 199f83f052860..6794b43bfb37b 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -69,6 +69,9 @@ fn seq_cst() -> bool { j2.join().unwrap(); let r3 = j3.join().unwrap(); + // Even though we force t3 to run last, it can still see the value 1. + // And it can *never* see the value 2! + assert!(r3 == 1 || r3 == 3); r3 == 1 } @@ -148,4 +151,9 @@ pub fn main() { assert_once(|| initialization_write(false)); assert_once(|| initialization_write(true)); assert_once(faa_replaced_by_load); + + // Run seq_cst a few more times since it has an assertion we want to ensure holds always. + for _ in 0..50 { + seq_cst(); + } } From 21e3111ef90f670b85eebf4a1ed9cc8be2f21061 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:31:29 +0200 Subject: [PATCH 0295/1900] refactor weak-mem test to list all expected executions --- .../tests/pass/0weak_memory_consistency.rs | 3 +- .../tests/pass/0weak_memory_consistency_sc.rs | 3 +- src/tools/miri/tests/pass/weak_memory/weak.rs | 211 +++++++++--------- 3 files changed, 114 insertions(+), 103 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index 142080c1372d7..f1d5a56c2b02c 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -1,6 +1,7 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we // override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs index 937c2a8cf282c..43675a8b5e165 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs @@ -1,6 +1,7 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we // override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index 6794b43bfb37b..f39ef0ed98da9 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -1,9 +1,11 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-fixed-schedule +// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we +// override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 + +// Tests showing weak memory behaviours are exhibited, even with a fixed scheule. +// We run all tests a number of times and then check that we see the desired list of outcomes. -// Tests showing weak memory behaviours are exhibited. All tests -// return true when the desired behaviour is seen. -// This is scheduler and pseudo-RNG dependent, so each test is -// run multiple times until one try returns true. // Spurious failure is possible, if you are really unlucky with // the RNG and always read the latest value from the store buffer. @@ -31,129 +33,136 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize { val } -fn relaxed(initial_read: bool) -> bool { - let x = static_atomic(0); - let j1 = spawn(move || { - x.store(1, Relaxed); - // Preemption is disabled, so the store above will never be the - // latest store visible to another thread. - x.store(2, Relaxed); - }); +/// Check that the function produces the intended set of outcomes. +#[track_caller] +fn check_all_outcomes( + expected: impl IntoIterator, + generate: impl Fn() -> T, +) { + use std::collections::HashSet; + + let expected: HashSet = HashSet::from_iter(expected); + let mut seen = HashSet::new(); + // Let's give it N times as many tries as we are expecting values. + let tries = expected.len() * 8; + for _ in 0..tries { + let val = generate(); + assert!(expected.contains(&val), "got an unexpected value: {val}"); + seen.insert(val); + } + // Let's see if we saw them all. + for val in expected { + if !seen.contains(&val) { + panic!("did not get value that should be possible: {val}"); + } + } +} - let j2 = spawn(move || x.load(Relaxed)); +fn relaxed() { + check_all_outcomes([0, 1, 2], || { + let x = static_atomic(0); + let j1 = spawn(move || { + x.store(1, Relaxed); + // Preemption is disabled, so the store above will never be the + // latest store visible to another thread. + x.store(2, Relaxed); + }); - j1.join().unwrap(); - let r2 = j2.join().unwrap(); + let j2 = spawn(move || x.load(Relaxed)); - // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed - // read), and 2 (the last read). The last case is boring and we cover the other two. - r2 == if initial_read { 0 } else { 1 } + j1.join().unwrap(); + let r2 = j2.join().unwrap(); + + // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed + // read), and 2 (the last read). + r2 + }); } // https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf Figure 8 -fn seq_cst() -> bool { - let x = static_atomic(0); +fn seq_cst() { + check_all_outcomes([1, 3], || { + let x = static_atomic(0); - let j1 = spawn(move || { - x.store(1, Relaxed); - }); + let j1 = spawn(move || { + x.store(1, Relaxed); + }); - let j2 = spawn(move || { - x.store(2, SeqCst); - x.store(3, SeqCst); - }); + let j2 = spawn(move || { + x.store(2, SeqCst); + x.store(3, SeqCst); + }); - let j3 = spawn(move || x.load(SeqCst)); + let j3 = spawn(move || x.load(SeqCst)); - j1.join().unwrap(); - j2.join().unwrap(); - let r3 = j3.join().unwrap(); + j1.join().unwrap(); + j2.join().unwrap(); + let r3 = j3.join().unwrap(); - // Even though we force t3 to run last, it can still see the value 1. - // And it can *never* see the value 2! - assert!(r3 == 1 || r3 == 3); - r3 == 1 + // Even though we force t3 to run last, it can still see the value 1. + // And it can *never* see the value 2! + r3 + }); } -fn initialization_write(add_fence: bool) -> bool { - let x = static_atomic(11); +fn initialization_write(add_fence: bool) { + check_all_outcomes([11, 22], || { + let x = static_atomic(11); - let wait = static_atomic(0); + let wait = static_atomic(0); - let j1 = spawn(move || { - x.store(22, Relaxed); - // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write - // after a relaxed write - wait.store(1, Relaxed); - }); + let j1 = spawn(move || { + x.store(22, Relaxed); + // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write + // after a relaxed write + wait.store(1, Relaxed); + }); - let j2 = spawn(move || { - spin_until(wait, 1); - if add_fence { - fence(AcqRel); - } - x.load(Relaxed) - }); + let j2 = spawn(move || { + spin_until(wait, 1); + if add_fence { + fence(AcqRel); + } + x.load(Relaxed) + }); - j1.join().unwrap(); - let r2 = j2.join().unwrap(); + j1.join().unwrap(); + let r2 = j2.join().unwrap(); - r2 == 11 + r2 + }); } -fn faa_replaced_by_load() -> bool { - // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 - #[no_mangle] - pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { - storing.store(1, Relaxed); - fence(Release); - // sync.fetch_add(0, Relaxed); - sync.load(Relaxed); - fence(Acquire); - loading.load(Relaxed) - } +fn faa_replaced_by_load() { + check_all_outcomes([true, false], || { + // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 + pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { + storing.store(1, Relaxed); + fence(Release); + // sync.fetch_add(0, Relaxed); + sync.load(Relaxed); + fence(Acquire); + loading.load(Relaxed) + } - let x = static_atomic(0); - let y = static_atomic(0); - let z = static_atomic(0); + let x = static_atomic(0); + let y = static_atomic(0); + let z = static_atomic(0); - // Since each thread is so short, we need to make sure that they truely run at the same time - // Otherwise t1 will finish before t2 even starts - let go = static_atomic(0); + let t1 = spawn(move || rdmw(y, x, z)); - let t1 = spawn(move || { - spin_until(go, 1); - rdmw(y, x, z) - }); + let t2 = spawn(move || rdmw(z, x, y)); - let t2 = spawn(move || { - spin_until(go, 1); - rdmw(z, x, y) + let a = t1.join().unwrap(); + let b = t2.join().unwrap(); + (a, b) == (0, 0) }); - - go.store(1, Relaxed); - - let a = t1.join().unwrap(); - let b = t2.join().unwrap(); - (a, b) == (0, 0) -} - -/// Asserts that the function returns true at least once in 100 runs -#[track_caller] -fn assert_once(f: fn() -> bool) { - assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } pub fn main() { - assert_once(|| relaxed(false)); - assert_once(|| relaxed(true)); - assert_once(seq_cst); - assert_once(|| initialization_write(false)); - assert_once(|| initialization_write(true)); - assert_once(faa_replaced_by_load); - - // Run seq_cst a few more times since it has an assertion we want to ensure holds always. - for _ in 0..50 { - seq_cst(); - } + relaxed(); + seq_cst(); + initialization_write(false); + initialization_write(true); + faa_replaced_by_load(); } From 614dab3ed2e61acd32a572c37e5480538b547da8 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 10 Sep 2025 23:02:49 +0800 Subject: [PATCH 0296/1900] loongarch: Align intrinsic signatures with LLVM --- .../crates/core_arch/src/loongarch32/mod.rs | 7 ++++--- .../crates/core_arch/src/loongarch64/mod.rs | 17 ++++++++++------- .../core_arch/src/loongarch_shared/mod.rs | 12 ++++++------ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch32/mod.rs b/library/stdarch/crates/core_arch/src/loongarch32/mod.rs index fb05450373c28..4e3f3d27182e4 100644 --- a/library/stdarch/crates/core_arch/src/loongarch32/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch32/mod.rs @@ -17,9 +17,10 @@ unsafe extern "unadjusted" { /// Generates the cache operation instruction #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn cacop(a: i32, b: i32) { - static_assert_simm_bits!(IMM12, 12); - __cacop(a, b, IMM12); +pub unsafe fn cacop(b: i32) { + static_assert_uimm_bits!(IMM5, 5); + static_assert_simm_bits!(IMM_S12, 12); + __cacop(IMM5, b, IMM_S12); } /// Reads the CSR diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index e8249805eae26..ab968aff20bbe 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -64,9 +64,10 @@ pub fn crcc_w_d_w(a: i64, b: i32) -> i32 { /// Generates the cache operation instruction #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn cacop(a: i64, b: i64) { - static_assert_simm_bits!(IMM12, 12); - __cacop(a, b, IMM12); +pub unsafe fn cacop(b: i64) { + static_assert_uimm_bits!(IMM5, 5); + static_assert_simm_bits!(IMM_S12, 12); + __cacop(IMM5, b, IMM_S12); } /// Reads the CSR @@ -125,14 +126,16 @@ pub unsafe fn asrtgt(a: i64, b: i64) { #[inline] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lddir(a: i64) -> i64 { - __lddir(a, B) +pub unsafe fn lddir(a: i64) -> i64 { + static_assert_uimm_bits!(IMM8, 8); + __lddir(a, IMM8) } /// Loads the page table entry #[inline] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn ldpte(a: i64) { - __ldpte(a, B) +pub unsafe fn ldpte(a: i64) { + static_assert_uimm_bits!(IMM8, 8); + __ldpte(a, IMM8) } diff --git a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs index 710b926f8df4f..8991fe857682b 100644 --- a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs @@ -131,17 +131,17 @@ pub fn ibar() { /// Moves data from a GPR to the FCSR #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn movgr2fcsr(a: i32) { - static_assert_uimm_bits!(IMM5, 5); - __movgr2fcsr(IMM5, a); +pub unsafe fn movgr2fcsr(a: i32) { + static_assert_uimm_bits!(IMM2, 2); + __movgr2fcsr(IMM2, a); } /// Moves data from a FCSR to the GPR #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn movfcsr2gr() -> i32 { - static_assert_uimm_bits!(IMM5, 5); - unsafe { __movfcsr2gr(IMM5) } +pub fn movfcsr2gr() -> i32 { + static_assert_uimm_bits!(IMM2, 2); + unsafe { __movfcsr2gr(IMM2) } } /// Reads the 8-bit IO-CSR From 655b8474348a087acbcec498d48fd705160ee080 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:35:07 +0200 Subject: [PATCH 0297/1900] also use nicer check_all_outcomes in float_nan --- src/tools/miri/tests/pass/float_nan.rs | 319 +++++++++++-------------- 1 file changed, 142 insertions(+), 177 deletions(-) diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index 3ffdb6868ac0a..f0a2b8e9ee060 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -1,7 +1,8 @@ +// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we +// override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 #![feature(float_gamma, portable_simd, core_intrinsics)] -use std::collections::HashSet; use std::fmt; -use std::hash::Hash; use std::hint::black_box; fn ldexp(a: f64, b: i32) -> f64 { @@ -25,11 +26,18 @@ enum NaNKind { } use NaNKind::*; +/// Check that the function produces the intended set of outcomes. #[track_caller] -fn check_all_outcomes(expected: HashSet, generate: impl Fn() -> T) { +fn check_all_outcomes( + expected: impl IntoIterator, + generate: impl Fn() -> T, +) { + use std::collections::HashSet; + + let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); - // Let's give it sixteen times as many tries as we are expecting values. - let tries = expected.len() * 16; + // Let's give it N times as many tries as we are expecting values. + let tries = expected.len() * 12; for _ in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); @@ -193,51 +201,50 @@ impl F64 { fn test_f32() { // Freshly generated NaNs can have either sign. - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(0.0 / black_box(0.0)), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(0.0 / black_box(0.0)) + }); // When there are NaN inputs, their payload can be propagated, with any sign. let all1_payload = u32_ones(22); let all1 = F32::nan(Pos, Quiet, all1_payload).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), - ]), + ], || F32::from(0.0 + all1), ); // When there are two NaN inputs, the output can be either one, or the preferred NaN. let just1 = F32::nan(Neg, Quiet, 1).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), F32::nan(Neg, Quiet, 1), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), - ]), + ], || F32::from(just1 - all1), ); // When there are *signaling* NaN inputs, they might be quieted or not. let all1_snan = F32::nan(Pos, Signaling, all1_payload).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(0.0 * all1_snan), ); // Mix signaling and non-signaling NaN. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), @@ -246,35 +253,26 @@ fn test_f32() { F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(just1 % all1_snan), ); // Unary `-` must preserve payloads exactly. - check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Quiet, all1_payload)]), || { - F32::from(-all1) - }); - check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Signaling, all1_payload)]), || { - F32::from(-all1_snan) - }); + check_all_outcomes([F32::nan(Neg, Quiet, all1_payload)], || F32::from(-all1)); + check_all_outcomes([F32::nan(Neg, Signaling, all1_payload)], || F32::from(-all1_snan)); // Intrinsics let nan = F32::nan(Neg, Quiet, 0).as_f32(); let snan = F32::nan(Neg, Signaling, 1).as_f32(); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(f32::min(nan, nan)) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.floor()) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || F32::from(nan.sin())); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(f32::min(nan, nan)), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.floor()), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.sin()), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), @@ -285,37 +283,32 @@ fn test_f32() { F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(just1.mul_add(F32::nan(Neg, Quiet, 2).as_f32(), all1_snan)), ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.powf(nan)) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.powf(nan)), - ); - check_all_outcomes( - HashSet::from_iter([1.0f32.into()]), + [1.0f32.into()], || F32::from(1.0f32.powf(nan)), // special `pow` rule ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.powi(1)), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.powi(1)) + }); // libm functions + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.sinh()) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.atan2(nan)) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.ln_gamma().0) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.sinh()), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.atan2(nan)), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.ln_gamma().0), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::from(1.0), F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), @@ -323,58 +316,57 @@ fn test_f32() { F32::nan(Neg, Quiet, 1), F32::nan(Pos, Signaling, 1), F32::nan(Neg, Signaling, 1), - ]), + ], || F32::from(snan.powf(0.0)), ); } fn test_f64() { // Freshly generated NaNs can have either sign. - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(0.0 / black_box(0.0)), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(0.0 / black_box(0.0)) + }); // When there are NaN inputs, their payload can be propagated, with any sign. let all1_payload = u64_ones(51); let all1 = F64::nan(Pos, Quiet, all1_payload).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), - ]), + ], || F64::from(0.0 + all1), ); // When there are two NaN inputs, the output can be either one, or the preferred NaN. let just1 = F64::nan(Neg, Quiet, 1).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), F64::nan(Neg, Quiet, 1), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), - ]), + ], || F64::from(just1 - all1), ); // When there are *signaling* NaN inputs, they might be quieted or not. let all1_snan = F64::nan(Pos, Signaling, all1_payload).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(0.0 * all1_snan), ); // Mix signaling and non-signaling NaN. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), @@ -383,27 +375,22 @@ fn test_f64() { F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(just1 % all1_snan), ); // Intrinsics let nan = F64::nan(Neg, Quiet, 0).as_f64(); let snan = F64::nan(Neg, Signaling, 1).as_f64(); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(f64::min(nan, nan)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.floor()) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || F64::from(nan.sin())); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(f64::min(nan, nan)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.floor()), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.sin()), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), @@ -414,41 +401,35 @@ fn test_f64() { F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(just1.mul_add(F64::nan(Neg, Quiet, 2).as_f64(), all1_snan)), ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.powf(nan)) + }); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.powf(nan)), - ); - check_all_outcomes( - HashSet::from_iter([1.0f64.into()]), + [1.0f64.into()], || F64::from(1.0f64.powf(nan)), // special `pow` rule ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.powi(1)), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.powi(1)) + }); // libm functions + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.sinh()) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.atan2(nan)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(ldexp(nan, 1)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.ln_gamma().0) + }); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.sinh()), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.atan2(nan)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(ldexp(nan, 1)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.ln_gamma().0), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F64::from(1.0), F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), @@ -456,7 +437,7 @@ fn test_f64() { F64::nan(Neg, Quiet, 1), F64::nan(Pos, Signaling, 1), F64::nan(Neg, Signaling, 1), - ]), + ], || F64::from(snan.powf(0.0)), ); } @@ -467,82 +448,79 @@ fn test_casts() { let left1_payload_64 = (all1_payload_32 as u64) << (51 - 22); // 64-to-32 - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32) + }); // The preferred payload is always a possibility. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload_32), F32::nan(Neg, Quiet, all1_payload_32), - ]), + ], || F32::from(F64::nan(Pos, Quiet, all1_payload_64).as_f64() as f32), ); // If the input is signaling, then the output *may* also be signaling. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload_32), F32::nan(Neg, Quiet, all1_payload_32), F32::nan(Pos, Signaling, all1_payload_32), F32::nan(Neg, Signaling, all1_payload_32), - ]), + ], || F32::from(F64::nan(Pos, Signaling, all1_payload_64).as_f64() as f32), ); // Check that the low bits are gone (not the high bits). + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), F32::nan(Neg, Quiet, 1), - ]), + ], || F32::from(F64::nan(Pos, Quiet, 1 << (51 - 22)).as_f64() as f32), ); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), // The `1` payload becomes `0`, and the `0` payload cannot be signaling, // so these are the only options. - ]), + ], || F32::from(F64::nan(Pos, Signaling, 1).as_f64() as f32), ); // 32-to-64 - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64) + }); // The preferred payload is always a possibility. // Also checks that 0s are added on the right. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, left1_payload_64), F64::nan(Neg, Quiet, left1_payload_64), - ]), + ], || F64::from(F32::nan(Pos, Quiet, all1_payload_32).as_f32() as f64), ); // If the input is signaling, then the output *may* also be signaling. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, left1_payload_64), F64::nan(Neg, Quiet, left1_payload_64), F64::nan(Pos, Signaling, left1_payload_64), F64::nan(Neg, Signaling, left1_payload_64), - ]), + ], || F64::from(F32::nan(Pos, Signaling, all1_payload_32).as_f32() as f64), ); } @@ -552,48 +530,35 @@ fn test_simd() { use std::simd::*; let nan = F32::nan(Neg, Quiet, 0).as_f32(); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || { - F32::from( - unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0], - ) - }, - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]) + }); // Casts - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(unsafe { simd_cast::(f32x4::splat(nan)) }[0]), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(unsafe { simd_cast::(f32x4::splat(nan)) }[0]) + }); } fn main() { From 827a6541e6a9e02fee35f1cc94f61e3ded4c258f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:40:48 +0200 Subject: [PATCH 0298/1900] move all weak memory tests into their folder --- .../consistency.rs} | 2 +- .../consistency_sc.rs} | 2 +- .../miri/tests/pass/{weak_memory => 0weak_memory}/extra_cpp.rs | 0 src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/weak.rs | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename src/tools/miri/tests/pass/{0weak_memory_consistency.rs => 0weak_memory/consistency.rs} (99%) rename src/tools/miri/tests/pass/{0weak_memory_consistency_sc.rs => 0weak_memory/consistency_sc.rs} (99%) rename src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/extra_cpp.rs (100%) rename src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/weak.rs (100%) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory/consistency.rs similarity index 99% rename from src/tools/miri/tests/pass/0weak_memory_consistency.rs rename to src/tools/miri/tests/pass/0weak_memory/consistency.rs index f1d5a56c2b02c..16a38ebd9d4da 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency.rs @@ -51,7 +51,7 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { } /// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7. -/// (The Figure 8 test is in `weak_memory/weak.rs`.) +/// (The Figure 8 test is in `weak.rs`.) fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs similarity index 99% rename from src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs rename to src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs index 43675a8b5e165..cb8535b8ad74b 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs @@ -349,7 +349,7 @@ fn test_sc_relaxed() { } pub fn main() { - for _ in 0..50 { + for _ in 0..32 { test_sc_store_buffering(); test_iriw_sc_rlx(); test_cpp20_sc_fence_fix(); diff --git a/src/tools/miri/tests/pass/weak_memory/extra_cpp.rs b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs similarity index 100% rename from src/tools/miri/tests/pass/weak_memory/extra_cpp.rs rename to src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs similarity index 100% rename from src/tools/miri/tests/pass/weak_memory/weak.rs rename to src/tools/miri/tests/pass/0weak_memory/weak.rs From cb819729d0d2d3f6f74b05168e17c34ca7f626b0 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 16:56:36 +0100 Subject: [PATCH 0299/1900] tidy: Introduce `WorkspaceInfo` struct for deps information --- src/tools/tidy/src/deps.rs | 97 ++++++++++++++++++++++++----------- src/tools/tidy/src/extdeps.rs | 12 +++-- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60347b2ea6456..08c0d3ca78653 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -48,39 +48,74 @@ const LICENSES: &[&str] = &[ type ExceptionList = &'static [(&'static str, &'static str)]; +pub(crate) struct WorkspaceInfo<'a> { + /// Path to the directory containing the workspace root Cargo.toml file. + pub(crate) path: &'a str, + /// The list of license exceptions. + pub(crate) exceptions: ExceptionList, + /// Optionally: + /// * A list of crates for which dependencies need to be explicitly allowed. + /// * The list of allowed dependencies. + pub(crate) crates_and_deps: Option<(&'a [&'a str], &'a [&'a str])>, + /// Submodules required for the workspace + pub(crate) submodules: &'a [&'a str], +} + +impl<'a> WorkspaceInfo<'a> { + const fn new( + path: &'a str, + exceptions: ExceptionList, + crates_and_deps: Option<(&'a [&str], &'a [&str])>, + submodules: &'a [&str], + ) -> Self { + Self { path, exceptions, crates_and_deps, submodules } + } +} + /// The workspaces to check for licensing and optionally permitted dependencies. -/// -/// Each entry consists of a tuple with the following elements: -/// -/// * The path to the workspace root Cargo.toml file. -/// * The list of license exceptions. -/// * Optionally a tuple of: -/// * A list of crates for which dependencies need to be explicitly allowed. -/// * The list of allowed dependencies. -/// * Submodules required for the workspace. // FIXME auto detect all cargo workspaces -pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, &[&str])] = &[ +pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), - ("library", EXCEPTIONS_STDLIB, Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), &[]), + WorkspaceInfo::new(".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), + WorkspaceInfo::new( + "library", + EXCEPTIONS_STDLIB, + Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), + &[], + ), // Outside of the alphabetical section because rustfmt formats it using multiple lines. - ( + WorkspaceInfo::new( "compiler/rustc_codegen_cranelift", EXCEPTIONS_CRANELIFT, Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)), &[], ), // tidy-alphabetical-start - ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), - ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), - ("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), - //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored - //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored - ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), - ("src/tools/rustbook", EXCEPTIONS_RUSTBOOK, None, &["src/doc/book", "src/doc/reference"]), - ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None, &["src/tools/rustc-perf"]), - ("src/tools/test-float-parse", EXCEPTIONS, None, &[]), - ("tests/run-make-cargo/uefi-qemu/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]), + WorkspaceInfo::new("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), + WorkspaceInfo::new("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), + WorkspaceInfo::new("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), + //WorkspaceInfo::new("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored + //WorkspaceInfo::new("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored + WorkspaceInfo::new("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), + WorkspaceInfo::new( + "src/tools/rustbook", + EXCEPTIONS_RUSTBOOK, + None, + &["src/doc/book", "src/doc/reference"], + ), + WorkspaceInfo::new( + "src/tools/rustc-perf", + EXCEPTIONS_RUSTC_PERF, + None, + &["src/tools/rustc-perf"], + ), + WorkspaceInfo::new("src/tools/test-float-parse", EXCEPTIONS, None, &[]), + WorkspaceInfo::new( + "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", + EXCEPTIONS_UEFI_QEMU_TEST, + None, + &[], + ), // tidy-alphabetical-end ]; @@ -567,29 +602,29 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { check_proc_macro_dep_list(root, cargo, bless, bad); - for &(workspace, exceptions, permitted_deps, submodules) in WORKSPACES { + for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { if has_missing_submodule(root, submodules) { continue; } - if !root.join(workspace).join("Cargo.lock").exists() { - tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); + if !root.join(path).join("Cargo.lock").exists() { + tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); continue; } let mut cmd = cargo_metadata::MetadataCommand::new(); cmd.cargo_path(cargo) - .manifest_path(root.join(workspace).join("Cargo.toml")) + .manifest_path(root.join(path).join("Cargo.toml")) .features(cargo_metadata::CargoOpt::AllFeatures) .other_options(vec!["--locked".to_owned()]); let metadata = t!(cmd.exec()); - check_license_exceptions(&metadata, workspace, exceptions, bad); - if let Some((crates, permitted_deps)) = permitted_deps { - check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad); + check_license_exceptions(&metadata, path, exceptions, bad); + if let Some((crates, permitted_deps)) = crates_and_deps { + check_permitted_dependencies(&metadata, path, permitted_deps, crates, bad); } - if workspace == "library" { + if path == "library" { check_runtime_license_exceptions(&metadata, bad); check_runtime_no_duplicate_dependencies(&metadata, bad); check_runtime_no_proc_macros(&metadata, bad); diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index bc217a55cc199..2b212cfa67a4b 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; +use crate::deps::WorkspaceInfo; + /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ r#""registry+https://github.com/rust-lang/crates.io-index""#, @@ -13,22 +15,22 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, bad: &mut bool) { - for &(workspace, _, _, submodules) in crate::deps::WORKSPACES { + for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { continue; } // FIXME check other workspaces too // `Cargo.lock` of rust. - let path = root.join(workspace).join("Cargo.lock"); + let lockfile = root.join(path).join("Cargo.lock"); - if !path.exists() { - tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); + if !lockfile.exists() { + tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); continue; } // Open and read the whole file. - let cargo_lock = t!(fs::read_to_string(&path)); + let cargo_lock = t!(fs::read_to_string(&lockfile)); // Process each line. for line in cargo_lock.lines() { From 7d5413bd17ede225bc3c2ac9a7f640885628edc8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 17:55:44 +0200 Subject: [PATCH 0300/1900] this apparently needs more test rounds --- src/tools/miri/tests/pass/0weak_memory/weak.rs | 8 ++++++-- src/tools/miri/tests/pass/float_nan.rs | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index f39ef0ed98da9..0dd661d3e9b4b 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -44,11 +44,15 @@ fn check_all_outcomes( let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 8; - for _ in 0..tries { + let tries = expected.len() * 12; + for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); seen.insert(val); + if i > tries / 2 && expected.len() == seen.len() { + // We saw everything and we did quite a few tries, let's avoid wasting time. + return; + } } // Let's see if we saw them all. for val in expected { diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index f0a2b8e9ee060..902816307403a 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -38,10 +38,14 @@ fn check_all_outcomes( let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. let tries = expected.len() * 12; - for _ in 0..tries { + for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); seen.insert(val); + if i > tries / 2 && expected.len() == seen.len() { + // We saw everything and we did quite a few tries, let's avoid wasting time. + return; + } } // Let's see if we saw them all. for val in expected { From ba874d38a561ec59d0f8e6fdde3d830e84f6da98 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:07:28 +0100 Subject: [PATCH 0301/1900] tidy: Print crate name on dependency error --- src/tools/tidy/src/deps.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 08c0d3ca78653..184f52a247f86 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -621,7 +621,8 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { check_license_exceptions(&metadata, path, exceptions, bad); if let Some((crates, permitted_deps)) = crates_and_deps { - check_permitted_dependencies(&metadata, path, permitted_deps, crates, bad); + let descr = crates.get(0).unwrap_or(&path); + check_permitted_dependencies(&metadata, descr, permitted_deps, crates, bad); } if path == "library" { From 096ae06e028768e539a7ae9ac56cd913011c6ee5 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Tue, 22 Jul 2025 11:57:42 -0500 Subject: [PATCH 0302/1900] glossary: add entry for rustbuild --- src/doc/rustc-dev-guide/src/appendix/glossary.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index 1837b59e850ae..4fc3822dd7385 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -68,6 +68,7 @@ Term | Meaning rib | A data structure in the name resolver that keeps track of a single scope for names. ([see more](../name-resolution.md)) RPIT | A return-position `impl Trait`. ([see the reference](https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types)). RPITIT | A return-position `impl Trait` in trait. Unlike RPIT, this is desugared to a generic associated type (GAT). Introduced in [RFC 3425](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). ([see more](../return-position-impl-trait-in-trait.md)) +rustbuild | The part of bootstrap that is written in rust (old deprecated term) scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. `sess` | The compiler _session_, which stores global data used throughout compilation side tables | Because the [AST](#ast) and HIR are immutable once created, we often carry extra information about them in the form of hashtables, indexed by the id of a particular node. From adfb6ec089fbb41519acd530cbedd3477e84399c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:13:21 +0100 Subject: [PATCH 0303/1900] tidy: More accurate permitted dependencies location --- src/tools/tidy/src/deps.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 184f52a247f86..9acd445f3e238 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -261,7 +261,19 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 ]; -const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!()); +struct ListLocation { + path: &'static str, + line: u32, +} + +/// Creates a [`ListLocation`] for the current location (with an additional offset to the actual list start); +macro_rules! location { + (+ $offset:literal) => { + ListLocation { path: file!(), line: line!() + $offset } + }; +} + +const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6); /// Crates rustc is allowed to depend on. Avoid adding to the list if possible. /// @@ -930,7 +942,8 @@ fn check_permitted_dependencies( } if has_permitted_dep_error { - eprintln!("Go to `{PERMITTED_DEPS_LOCATION}` for the list."); + let ListLocation { path, line } = PERMITTED_RUSTC_DEPS_LOCATION; + eprintln!("Go to `{path}:{line}` for the list."); } } From 11a22b30904fb6e0fd41d9004b3a88e985ec8455 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:19:51 +0100 Subject: [PATCH 0304/1900] tidy: Add specific line info for allowed dependencies --- src/tools/tidy/src/deps.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 9acd445f3e238..290f0ea313419 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -48,6 +48,7 @@ const LICENSES: &[&str] = &[ type ExceptionList = &'static [(&'static str, &'static str)]; +#[derive(Clone, Copy)] pub(crate) struct WorkspaceInfo<'a> { /// Path to the directory containing the workspace root Cargo.toml file. pub(crate) path: &'a str, @@ -56,7 +57,8 @@ pub(crate) struct WorkspaceInfo<'a> { /// Optionally: /// * A list of crates for which dependencies need to be explicitly allowed. /// * The list of allowed dependencies. - pub(crate) crates_and_deps: Option<(&'a [&'a str], &'a [&'a str])>, + /// * The source code location of the allowed dependencies list + crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>, /// Submodules required for the workspace pub(crate) submodules: &'a [&'a str], } @@ -65,7 +67,7 @@ impl<'a> WorkspaceInfo<'a> { const fn new( path: &'a str, exceptions: ExceptionList, - crates_and_deps: Option<(&'a [&str], &'a [&str])>, + crates_and_deps: Option<(&'a [&str], &'a [&str], ListLocation)>, submodules: &'a [&str], ) -> Self { Self { path, exceptions, crates_and_deps, submodules } @@ -76,18 +78,27 @@ impl<'a> WorkspaceInfo<'a> { // FIXME auto detect all cargo workspaces pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - WorkspaceInfo::new(".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), + WorkspaceInfo::new( + ".", + EXCEPTIONS, + Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION)), + &[], + ), WorkspaceInfo::new( "library", EXCEPTIONS_STDLIB, - Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), + Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION)), &[], ), // Outside of the alphabetical section because rustfmt formats it using multiple lines. WorkspaceInfo::new( "compiler/rustc_codegen_cranelift", EXCEPTIONS_CRANELIFT, - Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)), + Some(( + &["rustc_codegen_cranelift"], + PERMITTED_CRANELIFT_DEPENDENCIES, + PERMITTED_CRANELIFT_DEPS_LOCATION, + )), &[], ), // tidy-alphabetical-start @@ -261,6 +272,7 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 ]; +#[derive(Clone, Copy)] struct ListLocation { path: &'static str, line: u32, @@ -499,6 +511,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-end ]; +const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2); + const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start "addr2line", @@ -540,6 +554,8 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-end ]; +const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2); + const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start "allocator-api2", @@ -632,9 +648,9 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { let metadata = t!(cmd.exec()); check_license_exceptions(&metadata, path, exceptions, bad); - if let Some((crates, permitted_deps)) = crates_and_deps { + if let Some((crates, permitted_deps, location)) = crates_and_deps { let descr = crates.get(0).unwrap_or(&path); - check_permitted_dependencies(&metadata, descr, permitted_deps, crates, bad); + check_permitted_dependencies(&metadata, descr, permitted_deps, crates, location, bad); } if path == "library" { @@ -882,6 +898,7 @@ fn check_permitted_dependencies( descr: &str, permitted_dependencies: &[&'static str], restricted_dependency_crates: &[&'static str], + permitted_location: ListLocation, bad: &mut bool, ) { let mut has_permitted_dep_error = false; @@ -942,8 +959,7 @@ fn check_permitted_dependencies( } if has_permitted_dep_error { - let ListLocation { path, line } = PERMITTED_RUSTC_DEPS_LOCATION; - eprintln!("Go to `{path}:{line}` for the list."); + eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line); } } From 196b49a20ba12281e5465fc5ca7e919ce9fc2b29 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Wed, 10 Sep 2025 11:31:04 -0500 Subject: [PATCH 0305/1900] glossary(rustbuild): reword according to code review Co-authored-by: Tshepang Mbambo --- src/doc/rustc-dev-guide/src/appendix/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index 4fc3822dd7385..21162f8ee7d08 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -68,7 +68,7 @@ Term | Meaning rib | A data structure in the name resolver that keeps track of a single scope for names. ([see more](../name-resolution.md)) RPIT | A return-position `impl Trait`. ([see the reference](https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types)). RPITIT | A return-position `impl Trait` in trait. Unlike RPIT, this is desugared to a generic associated type (GAT). Introduced in [RFC 3425](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). ([see more](../return-position-impl-trait-in-trait.md)) -rustbuild | The part of bootstrap that is written in rust (old deprecated term) +rustbuild | A deprecated term for the part of bootstrap that is written in Rust scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. `sess` | The compiler _session_, which stores global data used throughout compilation side tables | Because the [AST](#ast) and HIR are immutable once created, we often carry extra information about them in the form of hashtables, indexed by the id of a particular node. From 82ff0025b7e12b2bbd9ddff4a5fed685c491bd34 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Sep 2025 17:13:49 +0200 Subject: [PATCH 0306/1900] Add missing documentation for running tests with GCC backend --- .../src/tests/codegen-backend-tests/cg_gcc.md | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index 4caf4c0e0eefa..ef407797fead1 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -1,3 +1,34 @@ # GCC codegen backend tests -TODO: please add some more information to this page. +To test the GCC codegen backend, you need to add `"gcc"` into the `rust.codegen-backends` +setting in `bootstrap.toml`: + +```toml +rust.codegen-backends = ["llvm", "gcc"] +``` + +If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x.py` +commands with `--set rust.codegen-backends=["llvm", "gcc"]'`. For example: + +```bash +x.py test --set 'rust.codegen-backends=["llvm", "gcc"]' +``` + +If you don't want to build `gcc` yourself, you also need to set: + +```toml +gcc.download-ci-gcc = true +``` + +Then when running tests, add the `--test-codegen-backend gcc` option. For example: + +```bash +./x.py test tests/ui --test-codegen-backend gcc +``` + +If you want to build the sysroot using the GCC backend, you need to set it first +in `rust.codegen-backends`: + +```toml +rust.codegen-backends = ["llvm", "gcc"] +``` From 32722771ec2735038567545bad598d45291759a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 14:42:44 +0200 Subject: [PATCH 0307/1900] fixup no_{core,std} handling code --- clippy_utils/src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e8..e1077bdf4b1a6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2133,17 +2133,11 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { } pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_std)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_core)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..)) } /// Check if parent of a hir node is a trait implementation block. From f8302a7c15d69b91ff20080ddcf8c44c81ec32ac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 23:01:15 +0200 Subject: [PATCH 0308/1900] add release sequence test --- .../miri/tests/pass/0weak_memory/weak.rs | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 0dd661d3e9b4b..75c6e8f1a8793 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -35,7 +35,7 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize { /// Check that the function produces the intended set of outcomes. #[track_caller] -fn check_all_outcomes( +fn check_all_outcomes( expected: impl IntoIterator, generate: impl Fn() -> T, ) { @@ -47,7 +47,7 @@ fn check_all_outcomes( let tries = expected.len() * 12; for i in 0..tries { let val = generate(); - assert!(expected.contains(&val), "got an unexpected value: {val}"); + assert!(expected.contains(&val), "got an unexpected value: {val:?}"); seen.insert(val); if i > tries / 2 && expected.len() == seen.len() { // We saw everything and we did quite a few tries, let's avoid wasting time. @@ -57,7 +57,7 @@ fn check_all_outcomes( // Let's see if we saw them all. for val in expected { if !seen.contains(&val) { - panic!("did not get value that should be possible: {val}"); + panic!("did not get value that should be possible: {val:?}"); } } } @@ -163,10 +163,46 @@ fn faa_replaced_by_load() { }); } +/// Checking that the weaker release sequence example from +/// can actually produce the +/// new behavior (`Some(0)` in our version). +fn weaker_release_sequences() { + check_all_outcomes([None, Some(0), Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + x.store(2, Relaxed); + }); + let t2 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); + x.store(3, Relaxed); + }); + let t3 = spawn(move || { + if x.load(Acquire) == 3 { + // In C++11, if we read the 3 here, and if the store of 1 was just before the store + // of 3 in mo order (which it is because we fix the schedule), this forms a release + // sequence, meaning we acquire the release store of 1, and we can thus never see + // the value 0. + // In C++20, this is no longer a release sequence, so 0 can now be observed. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap() + }); +} + pub fn main() { relaxed(); seq_cst(); initialization_write(false); initialization_write(true); faa_replaced_by_load(); + weaker_release_sequences(); } From ba107fcffba2aefc83e199286a5ccdbb29f69d5d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 11 Sep 2025 09:00:01 +0800 Subject: [PATCH 0309/1900] Revert "Rollup merge of #122661 - estebank:assert-macro-span, r=petrochenkov" This reverts commit 1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28, reversing changes made to 324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5. Unfortunately the assert desugaring change is not backwards compatible, see RUST-145770. Code such as ```rust #[derive(Debug)] struct F { data: bool } impl std::ops::Not for F { type Output = bool; fn not(self) -> Self::Output { !self.data } } fn main() { let f = F { data: true }; assert!(f); } ``` would be broken by the assert desugaring change. We may need to land the change over an edition boundary, or limit the editions that the desugaring change impacts. --- clippy_lints/src/missing_asserts_for_indexing.rs | 12 ++++++------ tests/ui/const_is_empty.rs | 1 - tests/ui/const_is_empty.stderr | 10 ++-------- tests/ui/incompatible_msrv.rs | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 788a04357b1e2..cf0c85990b150 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Body, Expr, ExprKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some( - higher::IfLetOrMatch::Match(cond, [_, then], _) - ) = higher::IfLetOrMatch::parse(cx, expr) - && let ExprKind::Binary(bin_op, left, right) = &cond.kind + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind // check if `then` block has a never type expression - && cx.typeck_results().expr_ty(then.body).is_never() + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 63c6342a323ce..8bb4f0e5d9750 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -196,7 +196,6 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); - //~^ const_is_empty } const { diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 9a42518698e39..2ba189058e832 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -158,16 +158,10 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:198:17 - | -LL | assert!(EMPTY_STR.is_empty()); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:203:9 + --> tests/ui/const_is_empty.rs:202:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 882f909e30c97..f7f21e1850d0a 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features, clippy::diverging_sub_expression)] +#![allow(stable_features)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] From 2d87bc3e6bfc9e6ccda01a5898980b1e0a706708 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 11 Sep 2025 10:23:02 +0800 Subject: [PATCH 0310/1900] Fix empty generic param list for generate_function Example --- ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar()$0; } } ``` **Before this PR**: ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar(); } fn bar<>(&self) ${0:-> _} { todo!() } } ``` **After this PR**: ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar(); } fn bar(&self) ${0:-> _} { todo!() } } ``` --- .../src/handlers/generate_function.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 88ed6f9ce124d..a9cf2c1bae1a4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -364,11 +364,13 @@ impl FunctionBuilder { Visibility::Crate => Some(make::visibility_pub_crate()), Visibility::Pub => Some(make::visibility_pub()), }; + let type_params = + self.generic_param_list.filter(|list| list.generic_params().next().is_some()); let fn_def = make::fn_( None, visibility, self.fn_name, - self.generic_param_list, + type_params, self.where_clause, self.params, self.fn_body, @@ -2415,6 +2417,33 @@ impl Foo { ) } + #[test] + fn create_method_with_unused_generics() { + check_assist( + generate_function, + r#" +struct Foo(S); +impl Foo { + fn foo(&self) { + self.bar()$0; + } +} +"#, + r#" +struct Foo(S); +impl Foo { + fn foo(&self) { + self.bar(); + } + + fn bar(&self) ${0:-> _} { + todo!() + } +} +"#, + ) + } + #[test] fn create_function_with_async() { check_assist( From 68bff8cb9db58d7006f609971e6961646b44dbe9 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 11 Sep 2025 04:53:20 +0000 Subject: [PATCH 0311/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to f4665ab8368ad2e8a86d4390ae35c28bdd9561bb. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 5b3139b25092b..b71a0d219d778 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a09fbe2c8372643a27a8082236120f95ed4e6bba +f4665ab8368ad2e8a86d4390ae35c28bdd9561bb From e54cc43867efe10c73a6063ef36fcc5aa8570ffb Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Thu, 11 Sep 2025 06:26:51 +0000 Subject: [PATCH 0312/1900] RISC-V: "Lower" requirements of `aes64im` This instruction is incorrectly categorized as the same one as `aes64ks1i` and `aes64ks2` (that should require `zkne || zknd` but currently require `zkne && zknd`) but `aes64im` only requires the Zknd extension. This commit fixes the category of this intrinsic (lowering the requirements from the Rust perspective but it does not actually lower it from the RISC-V perspective). --- library/stdarch/crates/core_arch/src/riscv64/zk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/crates/core_arch/src/riscv64/zk.rs b/library/stdarch/crates/core_arch/src/riscv64/zk.rs index c6af750bbc570..a30653cbe0885 100644 --- a/library/stdarch/crates/core_arch/src/riscv64/zk.rs +++ b/library/stdarch/crates/core_arch/src/riscv64/zk.rs @@ -176,7 +176,7 @@ pub fn aes64ks2(rs1: u64, rs2: u64) -> u64 { /// Version: v1.0.1 /// /// Section: 3.9 -#[target_feature(enable = "zkne", enable = "zknd")] +#[target_feature(enable = "zknd")] #[cfg_attr(test, assert_instr(aes64im))] #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] From d8f084217f9492d7ed0f575538a0ab51324c58aa Mon Sep 17 00:00:00 2001 From: karan Date: Tue, 13 May 2025 20:32:08 +0530 Subject: [PATCH 0313/1900] docs: typetree in autodiff --- src/doc/rustc-dev-guide/src/SUMMARY.md | 1 + .../src/autodiff/type-trees.md | 193 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/doc/rustc-dev-guide/src/autodiff/type-trees.md diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index a1612738537d7..249140956c09a 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -108,6 +108,7 @@ - [Installation](./autodiff/installation.md) - [How to debug](./autodiff/debugging.md) - [Autodiff flags](./autodiff/flags.md) + - [Type Trees](./autodiff/type-trees.md) # Source Code Representation diff --git a/src/doc/rustc-dev-guide/src/autodiff/type-trees.md b/src/doc/rustc-dev-guide/src/autodiff/type-trees.md new file mode 100644 index 0000000000000..68cb78650b009 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/type-trees.md @@ -0,0 +1,193 @@ +# TypeTrees for Autodiff + +## What are TypeTrees? +Memory layout descriptors for Enzyme. Tell Enzyme exactly how types are structured in memory so it can compute derivatives efficiently. + +## Structure +```rust +TypeTree(Vec) + +Type { + offset: isize, // byte offset (-1 = everywhere) + size: usize, // size in bytes + kind: Kind, // Float, Integer, Pointer, etc. + child: TypeTree // nested structure +} +``` + +## Example: `fn compute(x: &f32, data: &[f32]) -> f32` + +**Input 0: `x: &f32`** +```rust +TypeTree(vec![Type { + offset: -1, size: 8, kind: Pointer, + child: TypeTree(vec![Type { + offset: 0, size: 4, kind: Float, // Single value: use offset 0 + child: TypeTree::new() + }]) +}]) +``` + +**Input 1: `data: &[f32]`** +```rust +TypeTree(vec![Type { + offset: -1, size: 8, kind: Pointer, + child: TypeTree(vec![Type { + offset: -1, size: 4, kind: Float, // -1 = all elements + child: TypeTree::new() + }]) +}]) +``` + +**Output: `f32`** +```rust +TypeTree(vec![Type { + offset: 0, size: 4, kind: Float, // Single scalar: use offset 0 + child: TypeTree::new() +}]) +``` + +## Why Needed? +- Enzyme can't deduce complex type layouts from LLVM IR +- Prevents slow memory pattern analysis +- Enables correct derivative computation for nested structures +- Tells Enzyme which bytes are differentiable vs metadata + +## What Enzyme Does With This Information: + +Without TypeTrees: +```llvm +; Enzyme sees generic LLVM IR: +define float @distance(ptr %p1, ptr %p2) { +; Has to guess what these pointers point to +; Slow analysis of all memory operations +; May miss optimization opportunities +} +``` + +With TypeTrees: +```llvm +define "enzyme_type"="{[-1]:Float@float}" float @distance( + ptr "enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float}" %p1, + ptr "enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float}" %p2 +) { +; Enzyme knows exact type layout +; Can generate efficient derivative code directly +} +``` + +# TypeTrees - Offset and -1 Explained + +## Type Structure + +```rust +Type { + offset: isize, // WHERE this type starts + size: usize, // HOW BIG this type is + kind: Kind, // WHAT KIND of data (Float, Int, Pointer) + child: TypeTree // WHAT'S INSIDE (for pointers/containers) +} +``` + +## Offset Values + +### Regular Offset (0, 4, 8, etc.) +**Specific byte position within a structure** + +```rust +struct Point { + x: f32, // offset 0, size 4 + y: f32, // offset 4, size 4 + id: i32, // offset 8, size 4 +} +``` + +TypeTree for `&Point` (internal representation): +```rust +TypeTree(vec![ + Type { offset: 0, size: 4, kind: Float }, // x at byte 0 + Type { offset: 4, size: 4, kind: Float }, // y at byte 4 + Type { offset: 8, size: 4, kind: Integer } // id at byte 8 +]) +``` + +Generates LLVM +```llvm +"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float, [-1,4]:Float@float, [-1,8]:Integer, [-1,9]:Integer, [-1,10]:Integer, [-1,11]:Integer}" +``` + +### Offset -1 (Special: "Everywhere") +**Means "this pattern repeats for ALL elements"** + +#### Example 1: Direct Array `[f32; 100]` (no pointer indirection) +```rust +TypeTree(vec![Type { + offset: -1, // ALL positions + size: 4, // each f32 is 4 bytes + kind: Float, // every element is float +}]) +``` + +Generates LLVM: `"enzyme_type"="{[-1]:Float@float}"` + +#### Example 1b: Array Reference `&[f32; 100]` (with pointer indirection) +```rust +TypeTree(vec![Type { + offset: -1, size: 8, kind: Pointer, + child: TypeTree(vec![Type { + offset: -1, // ALL array elements + size: 4, // each f32 is 4 bytes + kind: Float, // every element is float + }]) +}]) +``` + +Generates LLVM: `"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@float}"` + +Instead of listing 100 separate Types with offsets `0,4,8,12...396` + +#### Example 2: Slice `&[i32]` +```rust +// Pointer to slice data +TypeTree(vec![Type { + offset: -1, size: 8, kind: Pointer, + child: TypeTree(vec![Type { + offset: -1, // ALL slice elements + size: 4, // each i32 is 4 bytes + kind: Integer + }]) +}]) +``` + +Generates LLVM: `"enzyme_type"="{[-1]:Pointer, [-1,-1]:Integer}"` + +#### Example 3: Mixed Structure +```rust +struct Container { + header: i64, // offset 0 + data: [f32; 1000], // offset 8, but elements use -1 +} +``` + +```rust +TypeTree(vec![ + Type { offset: 0, size: 8, kind: Integer }, // header + Type { offset: 8, size: 4000, kind: Pointer, + child: TypeTree(vec![Type { + offset: -1, size: 4, kind: Float // ALL array elements + }]) + } +]) +``` + +## Key Distinction: Single Values vs Arrays + +**Single Values** use offset `0` for precision: +- `&f32` has exactly one f32 value at offset 0 +- More precise than using -1 ("everywhere") +- Generates: `{[-1]:Pointer, [-1,0]:Float@float}` + +**Arrays** use offset `-1` for efficiency: +- `&[f32; 100]` has the same pattern repeated 100 times +- Using -1 avoids listing 100 separate offsets +- Generates: `{[-1]:Pointer, [-1,-1]:Float@float}` \ No newline at end of file From fd48528d185f59f60e301bce1e01d670ff4bdb30 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 10 Sep 2025 00:17:22 +0200 Subject: [PATCH 0314/1900] c-variadic: allow inherent methods to be c-variadic --- .../rustc_ast_passes/src/ast_validation.rs | 54 ++++++++++--------- tests/ui/c-variadic/inherent-method.rs | 45 ++++++++++++++++ tests/ui/c-variadic/not-async.rs | 10 +++- tests/ui/c-variadic/not-async.stderr | 30 ++++++++--- tests/ui/c-variadic/trait-method.rs | 40 ++++++++++++++ tests/ui/c-variadic/trait-method.stderr | 14 +++++ .../variadic-ffi-semantic-restrictions.rs | 10 ++-- .../variadic-ffi-semantic-restrictions.stderr | 20 +++++-- 8 files changed, 179 insertions(+), 44 deletions(-) create mode 100644 tests/ui/c-variadic/inherent-method.rs create mode 100644 tests/ui/c-variadic/trait-method.rs create mode 100644 tests/ui/c-variadic/trait-method.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6ef89b553db4..cdb3062c59178 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -696,36 +696,38 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free => match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } - } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); + FnCtxt::Free | FnCtxt::Assoc(AssocCtxt::Impl { of_trait: false }) => { + match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); + } - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); } } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); - } - }, + } FnCtxt::Assoc(_) => { // For now, C variable argument lists are unsupported in associated functions. let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; diff --git a/tests/ui/c-variadic/inherent-method.rs b/tests/ui/c-variadic/inherent-method.rs new file mode 100644 index 0000000000000..537bae7b3f0f4 --- /dev/null +++ b/tests/ui/c-variadic/inherent-method.rs @@ -0,0 +1,45 @@ +//@ run-pass +#![feature(c_variadic)] + +#[repr(transparent)] +struct S(i32); + +impl S { + unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { + unsafe { ap.arg() } + } + + unsafe extern "C" fn method_owned(self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn method_ref(&self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn method_mut(&mut self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn fat_pointer(self: Box, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } +} + +fn main() { + unsafe { + assert_eq!(S::associated_function(32), 32); + assert_eq!(S(100).method_owned(32), 132); + assert_eq!(S(100).method_ref(32), 132); + assert_eq!(S(100).method_mut(32), 132); + assert_eq!(S::fat_pointer(Box::new(S(100)), 32), 132); + + type Method = unsafe extern "C" fn(T, ...) -> i32; + + assert_eq!((S::associated_function as unsafe extern "C" fn(...) -> i32)(32), 32); + assert_eq!((S::method_owned as Method<_>)(S(100), 32), 132); + assert_eq!((S::method_ref as Method<_>)(&S(100), 32), 132); + assert_eq!((S::method_mut as Method<_>)(&mut S(100), 32), 132); + assert_eq!((S::fat_pointer as Method<_>)(Box::new(S(100)), 32), 132); + } +} diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs index 45a7e1f8972bf..bdb51a9a43232 100644 --- a/tests/ui/c-variadic/not-async.rs +++ b/tests/ui/c-variadic/not-async.rs @@ -2,6 +2,14 @@ #![feature(c_variadic)] #![crate_type = "lib"] -async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} +async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} //~^ ERROR functions cannot be both `async` and C-variadic //~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds + +struct S; + +impl S { + async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + //~^ ERROR functions cannot be both `async` and C-variadic + //~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds +} diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr index b8caf0d8bd852..eab4c4f0822c4 100644 --- a/tests/ui/c-variadic/not-async.stderr +++ b/tests/ui/c-variadic/not-async.stderr @@ -1,19 +1,35 @@ error: functions cannot be both `async` and C-variadic --> $DIR/not-async.rs:5:1 | -LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} - | ^^^^^ `async` because of this ^^^ C-variadic because of this +LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + +error: functions cannot be both `async` and C-variadic + --> $DIR/not-async.rs:12:5 + | +LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds - --> $DIR/not-async.rs:5:59 + --> $DIR/not-async.rs:5:62 | -LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} - | --------------------------------------------------------- ^^ +LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} + | ------------------------------------------------------------ ^^ | | | opaque type defined here | - = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_` + = note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_` + +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/not-async.rs:12:70 + | +LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + | ---------------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs new file mode 100644 index 0000000000000..4c468c1907bdb --- /dev/null +++ b/tests/ui/c-variadic/trait-method.rs @@ -0,0 +1,40 @@ +// For now C-variadic arguments in trait methods are rejected, though we aim to lift this +// restriction in the future. In particular we need to think about the interaction with +// `dyn Trait` and the `ReifyShim`s that it may generate for methods. +#![feature(c_variadic)] +#![crate_type = "lib"] +struct S; + +impl S { + unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { + unsafe { ap.arg() } + } + + unsafe extern "C" fn method(&self, mut ap: ...) -> i32 { + unsafe { ap.arg() } + } +} + +trait T { + unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { + //~^ ERROR: associated functions cannot have a C variable argument list + unsafe { ap.arg() } + } + + unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { + //~^ ERROR: associated functions cannot have a C variable argument list + unsafe { ap.arg() } + } +} + +impl T for S {} + +fn main() { + unsafe { + assert_eq!(S::associated_function(32), 32); + assert_eq!(S.method(32), 32); + + assert_eq!(S::trait_associated_function(32), 32); + assert_eq!(S.trait_method(32), 32); + } +} diff --git a/tests/ui/c-variadic/trait-method.stderr b/tests/ui/c-variadic/trait-method.stderr new file mode 100644 index 0000000000000..35f6e24ef710c --- /dev/null +++ b/tests/ui/c-variadic/trait-method.stderr @@ -0,0 +1,14 @@ +error: associated functions cannot have a C variable argument list + --> $DIR/trait-method.rs:19:52 + | +LL | unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { + | ^^^^^^^^^^^ + +error: associated functions cannot have a C variable argument list + --> $DIR/trait-method.rs:24:46 + | +LL | unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 4db056f15a51f..566c4d30245c5 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -53,17 +53,17 @@ struct X; impl X { fn i_f1(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn i_f2(...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn i_f3(..., x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 0cd78318de6a8..c18d857d14e01 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -132,17 +132,21 @@ error: `...` must be the last argument of a C-variadic function LL | fn e_f2(..., x: isize); | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 @@ -150,11 +154,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f3(..., x: isize, ...) {} | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 @@ -162,11 +168,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f4(..., x: isize, ...) {} | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: functions cannot be both `const` and C-variadic --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 @@ -176,11 +184,13 @@ LL | const fn i_f5(x: isize, ...) {} | | | `const` because of this -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 From 01e83adc88653123fee444fdb930c16dd08da82d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 10 Sep 2025 17:53:41 +0200 Subject: [PATCH 0315/1900] c-variadic: allow trait methods to be c-variadic but a C-variadic method makes a trait dyn-incompatible. That is because methods from dyn traits, when cast to a function pointer, create a shim. That shim can't really forward the c-variadic arguments. --- compiler/rustc_ast_passes/messages.ftl | 2 - .../rustc_ast_passes/src/ast_validation.rs | 59 +++++++--------- compiler/rustc_ast_passes/src/errors.rs | 7 -- compiler/rustc_middle/src/traits/mod.rs | 6 ++ .../src/traits/dyn_compatibility.rs | 3 + tests/ui/c-variadic/not-dyn-compatible.rs | 35 ++++++++++ tests/ui/c-variadic/not-dyn-compatible.stderr | 21 ++++++ tests/ui/c-variadic/trait-method.rs | 67 ++++++++++++++----- tests/ui/c-variadic/trait-method.stderr | 14 ---- .../feature-gates/feature-gate-c_variadic.rs | 1 - .../feature-gate-c_variadic.stderr | 8 +-- .../variadic-ffi-semantic-restrictions.rs | 8 +-- .../variadic-ffi-semantic-restrictions.stderr | 16 +++-- 13 files changed, 158 insertions(+), 89 deletions(-) create mode 100644 tests/ui/c-variadic/not-dyn-compatible.rs create mode 100644 tests/ui/c-variadic/not-dyn-compatible.stderr delete mode 100644 tests/ui/c-variadic/trait-method.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8dcf3e3aa3882..ba79fc63fad5b 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -64,8 +64,6 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect -ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list - ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions .label = `extern "{$abi}"` because of this .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cdb3062c59178..c6dd625c22864 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -696,43 +696,36 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free | FnCtxt::Assoc(AssocCtxt::Impl { of_trait: false }) => { - match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } + FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); - } - - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } + } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); + + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); } } - } - FnCtxt::Assoc(_) => { - // For now, C variable argument lists are unsupported in associated functions. - let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; - self.dcx().emit_err(err); - } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); + } + }, } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae805042c549f..b1b1ad60c2485 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -318,13 +318,6 @@ pub(crate) struct ExternItemAscii { pub block: Span, } -#[derive(Diagnostic)] -#[diag(ast_passes_c_variadic_associated_function)] -pub(crate) struct CVariadicAssociatedFunction { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_c_variadic_no_extern)] #[help] diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index ab8a314295392..0c7bddf60d974 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -823,6 +823,9 @@ impl DynCompatibilityViolation { DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => { format!("method `{name}` is `async`").into() } + DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => { + format!("method `{name}` is C-variadic").into() + } DynCompatibilityViolation::Method( name, MethodViolationCode::WhereClauseReferencesSelf, @@ -977,6 +980,9 @@ pub enum MethodViolationCode { /// e.g., `fn foo()` Generic, + /// e.g., `fn (mut ap: ...)` + CVariadic, + /// the method's receiver (`self` argument) can't be dispatched on UndispatchableReceiver(Option), } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index bcd11d6918d9a..3260dd712b9e4 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -426,6 +426,9 @@ fn virtual_call_violations_for_method<'tcx>( if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { errors.push(code); } + if sig.skip_binder().c_variadic { + errors.push(MethodViolationCode::CVariadic); + } // We can't monomorphize things like `fn foo(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); diff --git a/tests/ui/c-variadic/not-dyn-compatible.rs b/tests/ui/c-variadic/not-dyn-compatible.rs new file mode 100644 index 0000000000000..b40a13e584762 --- /dev/null +++ b/tests/ui/c-variadic/not-dyn-compatible.rs @@ -0,0 +1,35 @@ +// Traits where a method is c-variadic are not dyn compatible. +// +// Creating a function pointer from a method on an `&dyn T` value creates a ReifyShim. +// This shim cannot reliably forward C-variadic arguments. Thus the trait as a whole +// is dyn-incompatible to prevent invalid shims from being created. +#![feature(c_variadic)] + +#[repr(transparent)] +struct Struct(u64); + +trait Trait { + fn get(&self) -> u64; + + unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 { + self.get() + unsafe { ap.arg::() } + } +} + +impl Trait for Struct { + fn get(&self) -> u64 { + self.0 + } +} + +fn main() { + unsafe { + let dyn_object: &dyn Trait = &Struct(64); + //~^ ERROR the trait `Trait` is not dyn compatible + assert_eq!(dyn_object.dyn_method_ref(100), 164); + assert_eq!( + (Trait::dyn_method_ref as unsafe extern "C" fn(_, ...) -> u64)(dyn_object, 100), + 164 + ); + } +} diff --git a/tests/ui/c-variadic/not-dyn-compatible.stderr b/tests/ui/c-variadic/not-dyn-compatible.stderr new file mode 100644 index 0000000000000..76630600c511f --- /dev/null +++ b/tests/ui/c-variadic/not-dyn-compatible.stderr @@ -0,0 +1,21 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/not-dyn-compatible.rs:27:30 + | +LL | let dyn_object: &dyn Trait = &Struct(64); + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/not-dyn-compatible.rs:14:26 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +... +LL | unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 { + | ^^^^^^^^^^^^^^ ...because method `dyn_method_ref` is C-variadic + = help: consider moving `dyn_method_ref` to another trait + = help: only type `Struct` implements `Trait`; consider using it directly instead. + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs index 4c468c1907bdb..97da0706a3a18 100644 --- a/tests/ui/c-variadic/trait-method.rs +++ b/tests/ui/c-variadic/trait-method.rs @@ -1,40 +1,73 @@ -// For now C-variadic arguments in trait methods are rejected, though we aim to lift this -// restriction in the future. In particular we need to think about the interaction with -// `dyn Trait` and the `ReifyShim`s that it may generate for methods. +//@ run-pass #![feature(c_variadic)] -#![crate_type = "lib"] -struct S; -impl S { +#[repr(transparent)] +struct Struct(i32); + +impl Struct { unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { unsafe { ap.arg() } } unsafe extern "C" fn method(&self, mut ap: ...) -> i32 { - unsafe { ap.arg() } + self.0 + unsafe { ap.arg::() } } } -trait T { +trait Trait: Sized { + fn get(&self) -> i32; + unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { - //~^ ERROR: associated functions cannot have a C variable argument list unsafe { ap.arg() } } - unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { - //~^ ERROR: associated functions cannot have a C variable argument list - unsafe { ap.arg() } + unsafe extern "C" fn trait_method_owned(self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_method_ref(&self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_method_mut(&mut self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_fat_pointer(self: Box, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } } } -impl T for S {} +impl Trait for Struct { + fn get(&self) -> i32 { + self.0 + } +} fn main() { unsafe { - assert_eq!(S::associated_function(32), 32); - assert_eq!(S.method(32), 32); + assert_eq!(Struct::associated_function(32), 32); + assert_eq!(Struct(100).method(32), 132); + + assert_eq!(Struct::trait_associated_function(32), 32); + assert_eq!(Struct(100).trait_method_owned(32), 132); + assert_eq!(Struct(100).trait_method_ref(32), 132); + assert_eq!(Struct(100).trait_method_mut(32), 132); + assert_eq!(Struct::trait_fat_pointer(Box::new(Struct(100)), 32), 132); + + assert_eq!(::trait_associated_function(32), 32); + assert_eq!(Trait::trait_method_owned(Struct(100), 32), 132); + assert_eq!(Trait::trait_method_ref(&Struct(100), 32), 132); + assert_eq!(Trait::trait_method_mut(&mut Struct(100), 32), 132); + assert_eq!(Trait::trait_fat_pointer(Box::new(Struct(100)), 32), 132); + + type Associated = unsafe extern "C" fn(...) -> i32; + type Method = unsafe extern "C" fn(T, ...) -> i32; - assert_eq!(S::trait_associated_function(32), 32); - assert_eq!(S.trait_method(32), 32); + assert_eq!((Struct::trait_associated_function as Associated)(32), 32); + assert_eq!((Struct::trait_method_owned as Method<_>)(Struct(100), 32), 132); + assert_eq!((Struct::trait_method_ref as Method<_>)(&Struct(100), 32), 132); + assert_eq!((Struct::trait_method_mut as Method<_>)(&mut Struct(100), 32), 132); + assert_eq!((Struct::trait_fat_pointer as Method<_>)(Box::new(Struct(100)), 32), 132); } } diff --git a/tests/ui/c-variadic/trait-method.stderr b/tests/ui/c-variadic/trait-method.stderr deleted file mode 100644 index 35f6e24ef710c..0000000000000 --- a/tests/ui/c-variadic/trait-method.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: associated functions cannot have a C variable argument list - --> $DIR/trait-method.rs:19:52 - | -LL | unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { - | ^^^^^^^^^^^ - -error: associated functions cannot have a C variable argument list - --> $DIR/trait-method.rs:24:46 - | -LL | unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 88d91dbd081fb..649816b48d784 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -6,5 +6,4 @@ pub unsafe extern "C" fn test(_: i32, ap: ...) {} trait Trait { unsafe extern "C" fn trait_test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable - //~| ERROR associated functions cannot have a C variable argument list } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index 808aa20948d03..ae880093b980c 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,9 +1,3 @@ -error: associated functions cannot have a C variable argument list - --> $DIR/feature-gate-c_variadic.rs:7:45 - | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} - | ^^^^^^^ - error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | @@ -24,6 +18,6 @@ LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} = help: add `#![feature(c_variadic)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 566c4d30245c5..415472176d9c4 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -70,13 +70,13 @@ impl X { trait T { fn t_f1(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f2(x: isize, ...); - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f3(...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f4(...); - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f5(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index c18d857d14e01..da9c9b0f76099 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -192,29 +192,37 @@ LL | const fn i_f5(x: isize, ...) {} | = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, ...); | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(...); | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 From ad6004d95343031f3ac0898fe3582410fcc28f0d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Sep 2025 11:11:40 +0200 Subject: [PATCH 0316/1900] add release sequence test --- src/tools/miri/src/concurrency/data_race.rs | 9 +++-- src/tools/miri/src/concurrency/weak_memory.rs | 8 +++-- .../miri/tests/pass/0weak_memory/weak.rs | 33 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index de60e80b9f7eb..16cac7e6d04c1 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -276,7 +276,7 @@ struct MemoryCellClocks { /// zero on each write operation. read: VClock, - /// Atomic access, acquire, release sequence tracking clocks. + /// Atomic access tracking clocks. /// For non-atomic memory this value is set to None. /// For atomic memory, each byte carries this information. atomic_ops: Option>, @@ -555,7 +555,8 @@ impl MemoryCellClocks { // The handling of release sequences was changed in C++20 and so // the code here is different to the paper since now all relaxed // stores block release sequences. The exception for same-thread - // relaxed stores has been removed. + // relaxed stores has been removed. We always overwrite the `sync_vector`, + // meaning the previous release sequence is broken. let atomic = self.atomic_mut_unwrap(); atomic.sync_vector.clone_from(&thread_clocks.fence_release); Ok(()) @@ -571,6 +572,8 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { self.atomic_write_detect(thread_clocks, index, access_size)?; let atomic = self.atomic_mut_unwrap(); + // This *joining* of `sync_vector` implements release sequences: future + // reads of this location will acquire our clock *and* what was here before. atomic.sync_vector.join(&thread_clocks.clock); Ok(()) } @@ -585,6 +588,8 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { self.atomic_write_detect(thread_clocks, index, access_size)?; let atomic = self.atomic_mut_unwrap(); + // This *joining* of `sync_vector` implements release sequences: future + // reads of this location will acquire our fence clock *and* what was here before. atomic.sync_vector.join(&thread_clocks.fence_release); Ok(()) } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index a752ef7e45480..caa27422d2f26 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -159,9 +159,12 @@ struct StoreElement { #[derive(Debug, Clone, PartialEq, Eq, Default)] struct LoadInfo { - /// Timestamp of first loads from this store element by each thread + /// Timestamp of first loads from this store element by each thread. timestamps: FxHashMap, - /// Whether this store element has been read by an SC load + /// Whether this store element has been read by an SC load. + /// This is crucial to ensure we respect coherence-ordered-before. Concretely we use + /// this to ensure that if a store element is seen by an SC load, then all later SC loads + /// cannot see `mo`-earlier store elements. sc_loaded: bool, } @@ -476,6 +479,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let range = alloc_range(base_offset, place.layout.size); let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?; + // The RMW always reads from the most recent store. buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; } diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 75c6e8f1a8793..44791d7cd7c50 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -198,11 +198,44 @@ fn weaker_release_sequences() { }); } +/// Ensuring normal release sequences (with RMWs) still work correctly. +fn release_sequence() { + check_all_outcomes([None, Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + x.store(2, Relaxed); + }); + let t2 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); + x.swap(3, Relaxed); + }); + let t3 = spawn(move || { + if x.load(Acquire) == 3 { + // If we read 3 here, we are seeing the result of the `x.swap` above, which + // was relaxed but forms a release sequence with the `x.store` (since we know + // `t1` will not be scheduled in between). This means there is a release sequence, + // so we acquire the `y.store` and cannot see the original value `0` any more. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap() + }); +} + pub fn main() { relaxed(); seq_cst(); initialization_write(false); initialization_write(true); faa_replaced_by_load(); + release_sequence(); weaker_release_sequences(); } From d588c110102b803ebb2d3d26d8c24346bb4968a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 11 Sep 2025 12:45:28 +0200 Subject: [PATCH 0317/1900] Restructure the docs --- .../src/tests/codegen-backend-tests/cg_gcc.md | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index ef407797fead1..74dbfb7d31b30 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -1,34 +1,56 @@ -# GCC codegen backend tests +# GCC codegen backend -To test the GCC codegen backend, you need to add `"gcc"` into the `rust.codegen-backends` -setting in `bootstrap.toml`: +If you ran into an error related to tests executed with the GCC codegen backend on CI, +you can use the following command to run tests locally using the GCC backend: + +```bash +./x test tests/ui --set 'rust.codegen-backends = ["llvm", "gcc"]' --test-codegen-backend gcc +``` + +Below, you can find more information about how to configure the GCC backend in bootstrap. + +## Choosing which codegen backends are built + +The `rust.codegen-backends = [...]` bootstrap option affects which codegen backends will be built and +included in the sysroot of the produced `rustc`. To use the GCC codegen backend, `"gcc"` has to +be included in this array in `bootstrap.toml`: ```toml rust.codegen-backends = ["llvm", "gcc"] ``` -If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x.py` +If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x` commands with `--set rust.codegen-backends=["llvm", "gcc"]'`. For example: ```bash -x.py test --set 'rust.codegen-backends=["llvm", "gcc"]' +./x build --set 'rust.codegen-backends=["llvm", "gcc"]' ``` -If you don't want to build `gcc` yourself, you also need to set: +The first backend in the `codegen-backends` array will determine which backend will be used as the +*default backend* of the built `rustc`. This also determines which backend will be used to compile the +stage 1 standard library (or anything built in stage 2+). To produce `rustc` that uses the GCC backend +by default, you can thus put `"gcc"` as the first element of this array: -```toml -gcc.download-ci-gcc = true +```bash +./x build --set 'rust.codegen-backends=["gcc"]' library ``` -Then when running tests, add the `--test-codegen-backend gcc` option. For example: +## Choosing the codegen backend used in tests + +To run compiler tests with the GCC codegen backend being used to build the test Rust programs, you can use the +`--test-codegen-backend` flag: ```bash -./x.py test tests/ui --test-codegen-backend gcc +./x test tests/ui --test-codegen-backend gcc ``` -If you want to build the sysroot using the GCC backend, you need to set it first -in `rust.codegen-backends`: +Note that in order for this to work, the tested compiler must have the GCC codegen backend available in its sysroot +directory. You can achieve that using the [instructions above](#choosing-which-codegen-backends-are-built). -```toml -rust.codegen-backends = ["llvm", "gcc"] -``` +## Downloading GCC from CI + +The `gcc.download-ci-gcc` bootstrap option controls if GCC (which is a dependency of the GCC codegen backend) +will be downloaded from CI or built locally. The default value is `true`, which will download GCC from CI +if there are no local changes to the GCC sources and the given host target is available on CI. + +Note that GCC can currently only be downloaded from CI for the `x86_64-unknown-linux-gnu` target. From 24d195bc07da61a0ab74ac166d659f1924a604ea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Sep 2025 15:03:53 +0200 Subject: [PATCH 0318/1900] weak_memory: fix sync clock handling when loading from old store elements --- src/tools/miri/src/concurrency/data_race.rs | 40 ++++++--- src/tools/miri/src/concurrency/weak_memory.rs | 87 +++++++++++++------ .../miri/tests/pass/0weak_memory/weak.rs | 44 +++++++--- 3 files changed, 121 insertions(+), 50 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 16cac7e6d04c1..1ad9ace1b5d1a 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -183,6 +183,9 @@ struct AtomicMemoryCellClocks { /// contains the vector of timestamps that will /// happen-before a thread if an acquire-load is /// performed on the data. + /// + /// With weak memory emulation, this is the clock of the most recent write. It is then only used + /// for release sequences, to integrate the most recent clock into the next one for RMWs. sync_vector: VClock, /// The size of accesses to this atomic location. @@ -504,10 +507,11 @@ impl MemoryCellClocks { thread_clocks: &mut ThreadClockSet, index: VectorIdx, access_size: Size, + sync_clock: Option<&VClock>, ) -> Result<(), DataRace> { self.atomic_read_detect(thread_clocks, index, access_size)?; - if let Some(atomic) = self.atomic() { - thread_clocks.clock.join(&atomic.sync_vector); + if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) { + thread_clocks.clock.join(sync_clock); } Ok(()) } @@ -520,10 +524,11 @@ impl MemoryCellClocks { thread_clocks: &mut ThreadClockSet, index: VectorIdx, access_size: Size, + sync_clock: Option<&VClock>, ) -> Result<(), DataRace> { self.atomic_read_detect(thread_clocks, index, access_size)?; - if let Some(atomic) = self.atomic() { - thread_clocks.fence_acquire.join(&atomic.sync_vector); + if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) { + thread_clocks.fence_acquire.join(sync_clock); } Ok(()) } @@ -736,8 +741,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?; - let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, || { - this.validate_atomic_load(place, atomic) + let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, |sync_clock| { + this.validate_atomic_load(place, atomic, sync_clock) })?; interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) } @@ -935,7 +940,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, success)?; this.buffered_atomic_rmw(new, place, success, old.to_scalar())?; } else { - this.validate_atomic_load(place, fail)?; + this.validate_atomic_load(place, fail, /* can use latest sync clock */ None)?; // A failed compare exchange is equivalent to a load, reading from the latest store // in the modification order. // Since `old` is only a value and not the store element, we need to separately @@ -1177,6 +1182,18 @@ impl VClockAlloc { }))? } + /// Return the release/acquire synchronization clock for the given memory range. + pub(super) fn sync_clock(&self, access_range: AllocRange) -> VClock { + let alloc_ranges = self.alloc_ranges.borrow(); + let mut clock = VClock::default(); + for (_, mem_clocks) in alloc_ranges.iter(access_range.start, access_range.size) { + if let Some(atomic) = mem_clocks.atomic() { + clock.join(&atomic.sync_vector); + } + } + clock + } + /// Detect data-races for an unsynchronized read operation. It will not perform /// data-race detection if `race_detecting()` is false, either due to no threads /// being created or if it is temporarily disabled during a racy read or write @@ -1453,6 +1470,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { &self, place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, + sync_clock: Option<&VClock>, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); this.validate_atomic_op( @@ -1461,9 +1479,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AccessType::AtomicLoad, move |memory, clocks, index, atomic| { if atomic == AtomicReadOrd::Relaxed { - memory.load_relaxed(&mut *clocks, index, place.layout.size) + memory.load_relaxed(&mut *clocks, index, place.layout.size, sync_clock) } else { - memory.load_acquire(&mut *clocks, index, place.layout.size) + memory.load_acquire(&mut *clocks, index, place.layout.size, sync_clock) } }, ) @@ -1508,9 +1526,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AccessType::AtomicRmw, move |memory, clocks, index, _| { if acquire { - memory.load_acquire(clocks, index, place.layout.size)?; + memory.load_acquire(clocks, index, place.layout.size, None)?; } else { - memory.load_relaxed(clocks, index, place.layout.size)?; + memory.load_relaxed(clocks, index, place.layout.size, None)?; } if release { memory.rmw_release(clocks, index, place.layout.size) diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index caa27422d2f26..6ebade6a568a2 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -62,8 +62,11 @@ //! You can refer to test cases in weak_memory/extra_cpp.rs and weak_memory/extra_cpp_unsafe.rs for examples of these operations. // Our and the author's own implementation (tsan11) of the paper have some deviations from the provided operational semantics in §5.3: -// 1. In the operational semantics, store elements keep a copy of the atomic object's vector clock (AtomicCellClocks::sync_vector in miri), -// but this is not used anywhere so it's omitted here. +// 1. In the operational semantics, loads acquire the vector clock of the atomic location +// irrespective of which store buffer element is loaded. That's incorrect; the synchronization clock +// needs to be tracked per-store-buffer-element. (The paper has a field "clocks" for that purpose, +// but it is not actuallt used.) tsan11 does this correctly +// (https://github.com/ChrisLidbury/tsan11/blob/ecbd6b81e9b9454e01cba78eb9d88684168132c7/lib/tsan/rtl/tsan_relaxed.cc#L305). // // 2. In the operational semantics, each store element keeps the timestamp of a thread when it loads from the store. // If the same thread loads from the same store element multiple times, then the timestamps at all loads are saved in a list of load elements. @@ -138,16 +141,17 @@ enum LoadRecency { #[derive(Debug, Clone, PartialEq, Eq)] struct StoreElement { - /// The identifier of the vector index, corresponding to a thread - /// that performed the store. - store_index: VectorIdx, + /// The thread that performed the store. + store_thread: VectorIdx, + /// The timestamp of the storing thread when it performed the store + store_timestamp: VTimestamp, + + /// The vector clock that can be acquired by loading this store. + sync_clock: VClock, /// Whether this store is SC. is_seqcst: bool, - /// The timestamp of the storing thread when it performed the store - timestamp: VTimestamp, - /// The value of this store. `None` means uninitialized. // FIXME: Currently, we cannot represent partial initialization. val: Option, @@ -246,8 +250,10 @@ impl<'tcx> StoreBuffer { let store_elem = StoreElement { // The thread index and timestamp of the initialisation write // are never meaningfully used, so it's fine to leave them as 0 - store_index: VectorIdx::from(0), - timestamp: VTimestamp::ZERO, + store_thread: VectorIdx::from(0), + store_timestamp: VTimestamp::ZERO, + // The initialization write is non-atomic so nothing can be acquired. + sync_clock: VClock::default(), val: init, is_seqcst: false, load_info: RefCell::new(LoadInfo::default()), @@ -276,7 +282,7 @@ impl<'tcx> StoreBuffer { thread_mgr: &ThreadManager<'_>, is_seqcst: bool, rng: &mut (impl rand::Rng + ?Sized), - validate: impl FnOnce() -> InterpResult<'tcx>, + validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, (Option, LoadRecency)> { // Having a live borrow to store_buffer while calling validate_atomic_load is fine // because the race detector doesn't touch store_buffer @@ -293,7 +299,7 @@ impl<'tcx> StoreBuffer { // after we've picked a store element from the store buffer, as presented // in ATOMIC LOAD rule of the paper. This is because fetch_store // requires access to ThreadClockSet.clock, which is updated by the race detector - validate()?; + validate(Some(&store_elem.sync_clock))?; let (index, clocks) = global.active_thread_state(thread_mgr); let loaded = store_elem.load_impl(index, &clocks, is_seqcst); @@ -306,10 +312,11 @@ impl<'tcx> StoreBuffer { global: &DataRaceState, thread_mgr: &ThreadManager<'_>, is_seqcst: bool, + sync_clock: VClock, ) -> InterpResult<'tcx> { let (index, clocks) = global.active_thread_state(thread_mgr); - self.store_impl(val, index, &clocks.clock, is_seqcst); + self.store_impl(val, index, &clocks.clock, is_seqcst, sync_clock); interp_ok(()) } @@ -336,7 +343,9 @@ impl<'tcx> StoreBuffer { return false; } - keep_searching = if store_elem.timestamp <= clocks.clock[store_elem.store_index] { + keep_searching = if store_elem.store_timestamp + <= clocks.clock[store_elem.store_thread] + { // CoWR: if a store happens-before the current load, // then we can't read-from anything earlier in modification order. // C++20 §6.9.2.2 [intro.races] paragraph 18 @@ -348,7 +357,7 @@ impl<'tcx> StoreBuffer { // then we cannot read-from anything earlier in modification order. // C++20 §6.9.2.2 [intro.races] paragraph 16 false - } else if store_elem.timestamp <= clocks.write_seqcst[store_elem.store_index] + } else if store_elem.store_timestamp <= clocks.write_seqcst[store_elem.store_thread] && store_elem.is_seqcst { // The current non-SC load, which may be sequenced-after an SC fence, @@ -356,7 +365,7 @@ impl<'tcx> StoreBuffer { // C++17 §32.4 [atomics.order] paragraph 4 false } else if is_seqcst - && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index] + && store_elem.store_timestamp <= clocks.read_seqcst[store_elem.store_thread] { // The current SC load cannot read-before the last store sequenced-before // the last SC fence. @@ -394,17 +403,19 @@ impl<'tcx> StoreBuffer { } } - /// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock) + /// ATOMIC STORE IMPL in the paper fn store_impl( &mut self, val: Scalar, index: VectorIdx, thread_clock: &VClock, is_seqcst: bool, + sync_clock: VClock, ) { let store_elem = StoreElement { - store_index: index, - timestamp: thread_clock[index], + store_thread: index, + store_timestamp: thread_clock[index], + sync_clock, // In the language provided in the paper, an atomic store takes the value from a // non-atomic memory location. // But we already have the immediate value here so we don't need to do the memory @@ -422,7 +433,7 @@ impl<'tcx> StoreBuffer { // so that in a later SC load, only the last SC store (i.e. this one) or stores that // aren't ordered by hb with the last SC is picked. self.buffer.iter_mut().rev().for_each(|elem| { - if elem.timestamp <= thread_clock[elem.store_index] { + if elem.store_timestamp <= thread_clock[elem.store_thread] { elem.is_seqcst = true; } }) @@ -465,7 +476,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?; if let ( crate::AllocExtra { - data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)), + data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)), .. }, crate::MiriMachine { @@ -478,20 +489,29 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { global.sc_write(threads); } let range = alloc_range(base_offset, place.layout.size); + let sync_clock = data_race_clocks.sync_clock(range); let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?; // The RMW always reads from the most recent store. buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); - buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; + buffer.buffered_write( + new_val, + global, + threads, + atomic == AtomicRwOrd::SeqCst, + sync_clock, + )?; } interp_ok(()) } + /// The argument to `validate` is the synchronization clock of the memory that is being read, + /// if we are reading from a store buffer element. fn buffered_atomic_read( &self, place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, latest_in_mo: Scalar, - validate: impl FnOnce() -> InterpResult<'tcx>, + validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); 'fallback: { @@ -529,7 +549,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Race detector or weak memory disabled, simply read the latest value - validate()?; + validate(None)?; interp_ok(Some(latest_in_mo)) } @@ -537,6 +557,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// /// `init` says with which value to initialize the store buffer in case there wasn't a store /// buffer for this memory range before. + /// + /// Must be called *after* `validate_atomic_store` to ensure that `sync_clock` is up-to-date. fn buffered_atomic_write( &mut self, val: Scalar, @@ -548,7 +570,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?; if let ( crate::AllocExtra { - data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)), + data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)), .. }, crate::MiriMachine { @@ -560,9 +582,18 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { global.sc_write(threads); } - let buffer = alloc_buffers - .get_or_create_store_buffer_mut(alloc_range(base_offset, dest.layout.size), init)?; - buffer.buffered_write(val, global, threads, atomic == AtomicWriteOrd::SeqCst)?; + let range = alloc_range(base_offset, dest.layout.size); + // It's a bit annoying that we have to go back to the data race part to get the clock... + // but it does make things a lot simpler. + let sync_clock = data_race_clocks.sync_clock(range); + let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, init)?; + buffer.buffered_write( + val, + global, + threads, + atomic == AtomicWriteOrd::SeqCst, + sync_clock, + )?; } // Caller should've written to dest with the vanilla scalar write, we do nothing here diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 44791d7cd7c50..c752fc114ba57 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -44,7 +44,7 @@ fn check_all_outcomes( let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 12; + let tries = expected.len() * 16; for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val:?}"); @@ -205,19 +205,16 @@ fn release_sequence() { let y = static_atomic(0); let t1 = spawn(move || { - x.store(2, Relaxed); - }); - let t2 = spawn(move || { y.store(1, Relaxed); x.store(1, Release); x.swap(3, Relaxed); }); - let t3 = spawn(move || { + let t2 = spawn(move || { if x.load(Acquire) == 3 { - // If we read 3 here, we are seeing the result of the `x.swap` above, which - // was relaxed but forms a release sequence with the `x.store` (since we know - // `t1` will not be scheduled in between). This means there is a release sequence, - // so we acquire the `y.store` and cannot see the original value `0` any more. + // If we read 3 here, we are seeing the result of the `x.swap` above, which was + // relaxed but forms a release sequence with the `x.store`. This means there is a + // release sequence, so we acquire the `y.store` and cannot see the original value + // `0` any more. Some(y.load(Relaxed)) } else { None @@ -225,8 +222,32 @@ fn release_sequence() { }); t1.join().unwrap(); - t2.join().unwrap(); - t3.join().unwrap() + t2.join().unwrap() + }); +} + +/// Ensure that when we read from an outdated release store, we acquire its clock. +fn old_release_store() { + check_all_outcomes([None, Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); // this is what we want to read from + x.store(3, Relaxed); + }); + let t2 = spawn(move || { + if x.load(Acquire) == 1 { + // We must have acquired the `y.store` so we cannot see the initial value any more. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap() }); } @@ -238,4 +259,5 @@ pub fn main() { faa_replaced_by_load(); release_sequence(); weaker_release_sequences(); + old_release_store(); } From 2b5b191965571142cff8fab04aad9f243041ca19 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Thu, 11 Sep 2025 17:00:59 +0200 Subject: [PATCH 0319/1900] Fix miri issue 4579 by checking if the strong protector is actually "active". Where "active" means that the accessed bit is set. This also reverts miri PR 3831. --- .../src/borrow_tracker/tree_borrows/tree.rs | 2 ++ .../zero-sized-protected.stack.stderr | 15 --------- .../zero-sized-protected.tree.stderr | 32 ------------------- .../both_borrows/zero-sized-protected.rs | 7 ++-- 4 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr delete mode 100644 src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr rename src/tools/miri/tests/{fail => pass}/both_borrows/zero-sized-protected.rs (53%) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 1f29bcfc2b035..22bd63bd6b6f4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -756,6 +756,8 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission().is_cell() + // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + && perm.is_accessed() { Err(TransitionError::ProtectedDealloc) } else { diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr deleted file mode 100644 index 3e4a7ccac363d..0000000000000 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr deleted file mode 100644 index 66a7d7794e968..0000000000000 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: Undefined Behavior: deallocation through (root of the allocation) at ALLOC[0x0] is forbidden - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { dealloc(ptr, l) }; - | ^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information - = help: the allocation of the accessed tag (root of the allocation) also contains the strongly protected tag - = help: the strongly protected tag disallows deallocations -help: the accessed tag was created here - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | let ptr = unsafe { alloc(l) }; - | ^^^^^^^^ -help: the strongly protected tag was created here, in the initial state Reserved - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - | ^^ - = note: BACKTRACE (of the first span): - = note: inside `test` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC -note: inside `main` - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs similarity index 53% rename from src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs rename to src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs index df9a73a444eda..23d283ff4a19c 100644 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs +++ b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs @@ -3,16 +3,13 @@ use std::alloc::{Layout, alloc, dealloc}; // `x` is strongly protected but covers zero bytes. -// Let's see if deallocating the allocation x points to is UB: -// in TB, it is UB, but in SB it is not. +// This should never be UB. fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - unsafe { dealloc(ptr, l) }; //~[tree] ERROR: /deallocation .* is forbidden/ + unsafe { dealloc(ptr, l) }; } fn main() { let l = Layout::from_size_align(1, 1).unwrap(); let ptr = unsafe { alloc(l) }; unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; - // In SB the test would pass if it weren't for this line. - unsafe { std::hint::unreachable_unchecked() }; //~[stack] ERROR: unreachable } From 68217c79b246bceecca23d8d3862af71b97bff0d Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Mon, 8 Sep 2025 16:09:59 -0500 Subject: [PATCH 0320/1900] Minor refactoring --- .../crates/rust-analyzer/src/flycheck.rs | 8 +- .../src/handlers/notification.rs | 282 ++++++++++-------- .../crates/rust-analyzer/src/reload.rs | 20 +- 3 files changed, 170 insertions(+), 140 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 01ac75c09aee6..315c45d5b6392 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -104,11 +104,11 @@ pub(crate) enum FlycheckConfig { } impl FlycheckConfig { - pub(crate) fn invocation_strategy_once(&self) -> bool { + pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { match self { - FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace, FlycheckConfig::CustomCommand { invocation_strategy, .. } => { - *invocation_strategy == InvocationStrategy::Once + invocation_strategy.clone() } } } @@ -529,7 +529,7 @@ impl FlycheckActor { if let Some(command_handle) = self.command_handle.take() { tracing::debug!( command = ?command_handle, - "did cancel flycheck" + "did cancel flycheck" ); command_handle.cancel(); self.command_receiver.take(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index fb743e73c74a4..68c91a653940b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -1,9 +1,11 @@ //! This module is responsible for implementing handlers for Language Server //! Protocol. This module specifically handles notifications. -use std::ops::{Deref, Not as _}; +use std::{ + ops::{Deref, Not as _}, + panic::UnwindSafe, +}; -use ide_db::base_db::salsa::Cancelled; use itertools::Itertools; use lsp_types::{ CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams, @@ -16,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::{Config, ConfigChange}, - flycheck::Target, + flycheck::{InvocationStrategy, Target}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp::{from_proto, utils::apply_document_changes}, lsp_ext::{self, RunFlycheckParams}, @@ -301,131 +303,165 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some((file_id, vfs::FileExcluded::No)) = file_id { let world = state.snapshot(); - let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); + let invocation_strategy = state.config.flycheck(None).invocation_strategy(); let may_flycheck_workspace = state.config.flycheck_workspace(None); - let mut workspace_check_triggered = false; - let task = move || -> std::result::Result<(), Cancelled> { - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - if invocation_strategy_once { - world.flycheck[0].restart_workspace(saved_file.clone()); - } - let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { - let tgt_kind = it.target_kind(); - let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), - _ => return None, - }; - - let tgt = match tgt_kind { - project_model::TargetKind::Bin => Target::Bin(tgt_name), - project_model::TargetKind::Example => Target::Example(tgt_name), - project_model::TargetKind::Test => Target::Test(tgt_name), - project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return Some((None, root, package)), - }; - - Some((Some(tgt), root, package)) - }); - tracing::debug!(?target, "flycheck target"); - // we have a specific non-library target, attempt to only check that target, nothing - // else will be affected - let mut package_workspace_idx = None; - if let Some((target, root, package)) = target { - // trigger a package check if we have a non-library target as that can't affect - // anything else in the workspace OR if we're not allowed to check the workspace as - // the user opted into package checks then - let package_check_allowed = target.is_some() || !may_flycheck_workspace; - if package_check_allowed { - let workspace = world.workspaces.iter().position(|ws| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some(idx) = workspace { - package_workspace_idx = Some(idx); - world.flycheck[idx].restart_for_package(package, target); - } + let task: Box ide::Cancellable<()> + Send + UnwindSafe> = + match invocation_strategy { + InvocationStrategy::Once => { + Box::new(move || { + // FIXME: Because triomphe::Arc's auto UnwindSafe impl requires that the inner type + // be UnwindSafe, and FlycheckHandle is not UnwindSafe, `word.flycheck` cannot + // be captured directly. std::sync::Arc has an UnwindSafe impl that only requires + // that the inner type be RefUnwindSafe, so if we were using that one we wouldn't + // have this problem. Remove the line below when triomphe::Arc has an UnwindSafe impl + // like std::sync::Arc's. + let world = world; + stdx::always!( + world.flycheck.len() == 1, + "should have exactly one flycheck handle when invocation strategy is once" + ); + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); + world.flycheck[0].restart_workspace(saved_file); + Ok(()) + }) } - } - - if !may_flycheck_workspace { - return Ok(()); - } - - // Trigger flychecks for all workspaces that depend on the saved file - // Crates containing or depending on the saved file - let crate_ids = world - .analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| world.analysis.transitive_rev_deps(id)) - .flatten() - .unique() - .collect::>(); - tracing::debug!(?crate_ids, "flycheck crate ids"); - let crate_root_paths: Vec<_> = crate_ids - .iter() - .filter_map(|&crate_id| { - world - .analysis - .crate_root(crate_id) - .map(|file_id| { - world.file_id_to_file_path(file_id).as_path().map(ToOwned::to_owned) - }) - .transpose() - }) - .collect::>()?; - let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect(); - tracing::debug!(?crate_root_paths, "flycheck crate roots"); - - // Find all workspaces that have at least one target containing the saved file - let workspace_ids = world - .workspaces - .iter() - .enumerate() - .filter(|&(idx, _)| match package_workspace_idx { - Some(pkg_idx) => idx != pkg_idx, - None => true, - }) - .filter(|&(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => cargo.packages().any(|pkg| { - cargo[pkg] - .targets + InvocationStrategy::PerWorkspace => { + Box::new(move || { + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { + let tgt_kind = it.target_kind(); + let (tgt_name, root, package) = match it { + TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), + _ => return None, + }; + + let tgt = match tgt_kind { + project_model::TargetKind::Bin => Target::Bin(tgt_name), + project_model::TargetKind::Example => Target::Example(tgt_name), + project_model::TargetKind::Test => Target::Test(tgt_name), + project_model::TargetKind::Bench => Target::Benchmark(tgt_name), + _ => return Some((None, root, package)), + }; + + Some((Some(tgt), root, package)) + }); + tracing::debug!(?target, "flycheck target"); + // we have a specific non-library target, attempt to only check that target, nothing + // else will be affected + let mut package_workspace_idx = None; + if let Some((target, root, package)) = target { + // trigger a package check if we have a non-library target as that can't affect + // anything else in the workspace OR if we're not allowed to check the workspace as + // the user opted into package checks then + let package_check_allowed = target.is_some() || !may_flycheck_workspace; + if package_check_allowed { + package_workspace_idx = + world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { + cargo, + .. + } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = package_workspace_idx { + world.flycheck[idx].restart_for_package(package, target); + } + } + } + + if !may_flycheck_workspace { + return Ok(()); + } + + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids: Vec<_> = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .unique() + .collect(); + tracing::debug!(?crate_ids, "flycheck crate ids"); + let crate_root_paths: Vec<_> = crate_ids .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())) - }), - project_model::ProjectWorkspaceKind::Json(project) => project - .crates() - .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())), - project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, - }); - - // Find and trigger corresponding flychecks - 'flychecks: for flycheck in world.flycheck.iter() { - for (id, _) in workspace_ids.clone() { - if id == flycheck.id() { - workspace_check_triggered = true; - flycheck.restart_workspace(saved_file.clone()); - continue 'flychecks; - } - } - } - // No specific flycheck was triggered, so let's trigger all of them. - if !workspace_check_triggered && package_workspace_idx.is_none() { - for flycheck in world.flycheck.iter() { - flycheck.restart_workspace(saved_file.clone()); + .filter_map(|&crate_id| { + world + .analysis + .crate_root(crate_id) + .map(|file_id| { + world + .file_id_to_file_path(file_id) + .as_path() + .map(ToOwned::to_owned) + }) + .transpose() + }) + .collect::>()?; + let crate_root_paths: Vec<_> = + crate_root_paths.iter().map(Deref::deref).collect(); + tracing::debug!(?crate_root_paths, "flycheck crate roots"); + + // Find all workspaces that have at least one target containing the saved file + let workspace_ids = + world.workspaces.iter().enumerate().filter(|&(idx, ws)| { + let ws_contains_file = match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { + cargo, .. + } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => cargo.packages().any(|pkg| { + cargo[pkg].targets.iter().any(|&it| { + crate_root_paths.contains(&cargo[it].root.as_path()) + }) + }), + project_model::ProjectWorkspaceKind::Json(project) => { + project.crates().any(|(_, krate)| { + crate_root_paths.contains(&krate.root_module.as_path()) + }) + } + project_model::ProjectWorkspaceKind::DetachedFile { + .. + } => false, + }; + let is_pkg_ws = match package_workspace_idx { + Some(pkg_idx) => pkg_idx == idx, + None => false, + }; + ws_contains_file && !is_pkg_ws + }); + + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); + let mut workspace_check_triggered = false; + // Find and trigger corresponding flychecks + 'flychecks: for flycheck in world.flycheck.iter() { + for (id, _) in workspace_ids.clone() { + if id == flycheck.id() { + workspace_check_triggered = true; + flycheck.restart_workspace(saved_file.clone()); + continue 'flychecks; + } + } + } + + // No specific flycheck was triggered, so let's trigger all of them. + if !workspace_check_triggered && package_workspace_idx.is_none() { + for flycheck in world.flycheck.iter() { + flycheck.restart_workspace(saved_file.clone()); + } + } + Ok(()) + }) } - } - Ok(()) - }; + }; + state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { if let Err(e) = std::panic::catch_unwind(task) { tracing::error!("flycheck task panicked: {e:?}") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index a8a54930c6e5e..27738fee51407 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -318,7 +318,7 @@ impl GlobalState { } } - let mut workspaces = linked_projects + let mut workspaces: Vec<_> = linked_projects .iter() .map(|project| match project { LinkedProject::ProjectManifest(manifest) => { @@ -339,7 +339,7 @@ impl GlobalState { Ok(workspace) } }) - .collect::>(); + .collect(); let mut i = 0; while i < workspaces.len() { @@ -848,23 +848,17 @@ impl GlobalState { fn reload_flycheck(&mut self) { let _p = tracing::info_span!("GlobalState::reload_flycheck").entered(); let config = self.config.flycheck(None); - let sender = self.flycheck_sender.clone(); - let invocation_strategy = match config { - FlycheckConfig::CargoCommand { .. } => { - crate::flycheck::InvocationStrategy::PerWorkspace - } - FlycheckConfig::CustomCommand { ref invocation_strategy, .. } => { - invocation_strategy.clone() - } - }; - let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default(); + let sender = &self.flycheck_sender; + let invocation_strategy = config.invocation_strategy(); + let next_gen = + self.flycheck.iter().map(FlycheckHandle::generation).max().unwrap_or_default() + 1; self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => { vec![FlycheckHandle::spawn( 0, next_gen, - sender, + sender.clone(), config, None, self.config.root_path().clone(), From 16b34c6f7056701bbe66892e2d5300d80eae0830 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Thu, 11 Sep 2025 19:15:50 +0200 Subject: [PATCH 0321/1900] move zero-sized protector dealloc test --- .../pass/both_borrows/basic_aliasing_model.rs | 10 ++++++++++ .../pass/both_borrows/zero-sized-protected.rs | 15 --------------- 2 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index 82976326a8df3..115e232dde4c2 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -1,6 +1,7 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] +use std::alloc::{Layout, alloc, dealloc}; use std::cell::Cell; use std::ptr; @@ -305,5 +306,14 @@ fn zst() { let ptr = &raw mut *b as *mut (); drop(b); let _ref = &mut *ptr; + + // zero-sized protectors do not affect deallocation + fn with_protector(_x: &mut (), ptr: *mut u8, l: Layout) { + // `_x` here is strongly protected but covers zero bytes. + unsafe { dealloc(ptr, l) }; + } + let l = Layout::from_size_align(1, 1).unwrap(); + let ptr = alloc(l); + with_protector(&mut *ptr.cast::<()>(), ptr, l); } } diff --git a/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs deleted file mode 100644 index 23d283ff4a19c..0000000000000 --- a/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@revisions: stack tree -//@[tree]compile-flags: -Zmiri-tree-borrows -use std::alloc::{Layout, alloc, dealloc}; - -// `x` is strongly protected but covers zero bytes. -// This should never be UB. -fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - unsafe { dealloc(ptr, l) }; -} - -fn main() { - let l = Layout::from_size_align(1, 1).unwrap(); - let ptr = unsafe { alloc(l) }; - unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; -} From 6e12c54bcddb34b085497bf5ca0a6c02387992f0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 11 Sep 2025 20:09:54 +0200 Subject: [PATCH 0322/1900] fix(len_zero): don't eagerly call `GenericArgs::type_at` Results in an ICE if the output type has no generic type params --- clippy_lints/src/len_zero.rs | 6 +++--- tests/ui/crashes/ice-15657.rs | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/ui/crashes/ice-15657.rs diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f44a5fdf715e5..28a0fbc051158 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -371,9 +371,9 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => Some(LenOutput::Option(adt.did())), - Some(sym::Result) => Some(LenOutput::Result(adt.did())), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())), _ => None, }, _ => None, diff --git a/tests/ui/crashes/ice-15657.rs b/tests/ui/crashes/ice-15657.rs new file mode 100644 index 0000000000000..c6f6506cd8d03 --- /dev/null +++ b/tests/ui/crashes/ice-15657.rs @@ -0,0 +1,11 @@ +//@check-pass +#![warn(clippy::len_zero)] + +pub struct S1; +pub struct S2; + +impl S1 { + pub fn len(&self) -> S2 { + S2 + } +} From 0e5281cea815c5a61ba7e4b8c4dfaaca2c587ca3 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 11 Sep 2025 21:40:25 +0300 Subject: [PATCH 0323/1900] remove unused macro --- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 116 ------------------ 1 file changed, 116 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3bb1533c2fe25..d970c4a542271 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -79,122 +79,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -#ifdef LLVM_COMPONENT_X86 -#define SUBTARGET_X86 SUBTARGET(X86) -#else -#define SUBTARGET_X86 -#endif - -#ifdef LLVM_COMPONENT_ARM -#define SUBTARGET_ARM SUBTARGET(ARM) -#else -#define SUBTARGET_ARM -#endif - -#ifdef LLVM_COMPONENT_AARCH64 -#define SUBTARGET_AARCH64 SUBTARGET(AArch64) -#else -#define SUBTARGET_AARCH64 -#endif - -#ifdef LLVM_COMPONENT_AVR -#define SUBTARGET_AVR SUBTARGET(AVR) -#else -#define SUBTARGET_AVR -#endif - -#ifdef LLVM_COMPONENT_M68k -#define SUBTARGET_M68K SUBTARGET(M68k) -#else -#define SUBTARGET_M68K -#endif - -#ifdef LLVM_COMPONENT_CSKY -#define SUBTARGET_CSKY SUBTARGET(CSKY) -#else -#define SUBTARGET_CSKY -#endif - -#ifdef LLVM_COMPONENT_MIPS -#define SUBTARGET_MIPS SUBTARGET(Mips) -#else -#define SUBTARGET_MIPS -#endif - -#ifdef LLVM_COMPONENT_POWERPC -#define SUBTARGET_PPC SUBTARGET(PPC) -#else -#define SUBTARGET_PPC -#endif - -#ifdef LLVM_COMPONENT_SYSTEMZ -#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) -#else -#define SUBTARGET_SYSTEMZ -#endif - -#ifdef LLVM_COMPONENT_MSP430 -#define SUBTARGET_MSP430 SUBTARGET(MSP430) -#else -#define SUBTARGET_MSP430 -#endif - -#ifdef LLVM_COMPONENT_RISCV -#define SUBTARGET_RISCV SUBTARGET(RISCV) -#else -#define SUBTARGET_RISCV -#endif - -#ifdef LLVM_COMPONENT_SPARC -#define SUBTARGET_SPARC SUBTARGET(Sparc) -#else -#define SUBTARGET_SPARC -#endif - -#ifdef LLVM_COMPONENT_XTENSA -#define SUBTARGET_XTENSA SUBTARGET(XTENSA) -#else -#define SUBTARGET_XTENSA -#endif - -#ifdef LLVM_COMPONENT_HEXAGON -#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) -#else -#define SUBTARGET_HEXAGON -#endif - -#ifdef LLVM_COMPONENT_LOONGARCH -#define SUBTARGET_LOONGARCH SUBTARGET(LoongArch) -#else -#define SUBTARGET_LOONGARCH -#endif - -#define GEN_SUBTARGETS \ - SUBTARGET_X86 \ - SUBTARGET_ARM \ - SUBTARGET_AARCH64 \ - SUBTARGET_AVR \ - SUBTARGET_M68K \ - SUBTARGET_CSKY \ - SUBTARGET_MIPS \ - SUBTARGET_PPC \ - SUBTARGET_SYSTEMZ \ - SUBTARGET_MSP430 \ - SUBTARGET_SPARC \ - SUBTARGET_HEXAGON \ - SUBTARGET_XTENSA \ - SUBTARGET_RISCV \ - SUBTARGET_LOONGARCH - -#define SUBTARGET(x) \ - namespace llvm { \ - extern const SubtargetFeatureKV x##FeatureKV[]; \ - extern const SubtargetFeatureKV x##SubTypeKV[]; \ - } - -GEN_SUBTARGETS -#undef SUBTARGET - // This struct and various functions are sort of a hack right now, but the // problem is that we've got in-memory LLVM modules after we generate and // optimize all codegen-units for one compilation in rustc. To be compatible From acb35038ff21f7f41866b8ea36fed72d9fbc6640 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 11 Sep 2025 21:43:18 +0300 Subject: [PATCH 0324/1900] remove unused getLongestEntryLength --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index d970c4a542271..91d11ba317a61 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -224,14 +224,6 @@ static FloatABI::ABIType fromRust(LLVMRustFloatABI RustFloatAbi) { report_fatal_error("Bad FloatABI."); } -/// getLongestEntryLength - Return the length of the longest entry in the table. -template static size_t getLongestEntryLength(ArrayRef Table) { - size_t MaxLen = 0; - for (auto &I : Table) - MaxLen = std::max(MaxLen, std::strlen(I.Key)); - return MaxLen; -} - extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, RustStringRef OutStr) { ArrayRef CPUTable = From bb31725e679c48e75e54c8ec88c7e654c5509d37 Mon Sep 17 00:00:00 2001 From: sayantn Date: Fri, 12 Sep 2025 01:20:34 +0530 Subject: [PATCH 0325/1900] Remove big-endian swizzles from `vreinterpret` --- .../core_arch/src/aarch64/neon/generated.rs | 971 +- .../src/arm_shared/neon/generated.rs | 10279 ++-------------- .../spec/neon/aarch64.spec.yml | 2 + .../spec/neon/arm_shared.spec.yml | 4 + 4 files changed, 988 insertions(+), 10268 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 554a809db8db2..855261aaecfd0 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -21477,172 +21477,73 @@ pub fn vrecpxh_f16(a: f16) -> f16 { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f16)"] -#[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p128)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p128(a: p128) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_p128(a: p128) -> float64x2_t { - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_f32(a: float32x2_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f32(a: float32x2_t) -> float64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p64_f32(a: float32x2_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_p64_f32(a: float32x2_t) -> poly64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21650,23 +21551,8 @@ pub fn vreinterpretq_f64_f32(a: float32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_f32(a: float32x4_t) -> float64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21674,23 +21560,8 @@ pub fn vreinterpretq_p64_f32(a: float32x4_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_f32(a: float32x4_t) -> poly64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21698,22 +21569,8 @@ pub fn vreinterpret_f32_f64(a: float64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f32_f64(a: float64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21721,22 +21578,8 @@ pub fn vreinterpret_s8_f64(a: float64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s8_f64(a: float64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21744,22 +21587,8 @@ pub fn vreinterpret_s16_f64(a: float64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s16_f64(a: float64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21767,19 +21596,6 @@ pub fn vreinterpret_s32_f64(a: float64x1_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s32_f64(a: float64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21791,7 +21607,6 @@ pub fn vreinterpret_s64_f64(a: float64x1_t) -> int64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21799,22 +21614,8 @@ pub fn vreinterpret_u8_f64(a: float64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u8_f64(a: float64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21822,22 +21623,8 @@ pub fn vreinterpret_u16_f64(a: float64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u16_f64(a: float64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21845,19 +21632,6 @@ pub fn vreinterpret_u32_f64(a: float64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u32_f64(a: float64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21869,7 +21643,6 @@ pub fn vreinterpret_u64_f64(a: float64x1_t) -> uint64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21877,22 +21650,8 @@ pub fn vreinterpret_p8_f64(a: float64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p8_f64(a: float64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21900,19 +21659,6 @@ pub fn vreinterpret_p16_f64(a: float64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p16_f64(a: float64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21924,7 +21670,6 @@ pub fn vreinterpret_p64_f64(a: float64x1_t) -> poly64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21932,96 +21677,35 @@ pub fn vreinterpretq_p128_f64(a: float64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p128_f64(a: float64x2_t) -> p128 { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpretq_f32_f64(a: float64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f32_f64(a: float64x2_t) -> float32x4_t { +pub fn vreinterpretq_s8_f64(a: float64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f32_f64(a: float64x2_t) -> float32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s16_f64(a: float64x2_t) -> int16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s8_f64(a: float64x2_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s8_f64(a: float64x2_t) -> int8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s16_f64(a: float64x2_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s16_f64(a: float64x2_t) -> int16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f64)"] -#[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22029,736 +21713,301 @@ pub fn vreinterpretq_s32_f64(a: float64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s32_f64(a: float64x2_t) -> int32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s64_f64(a: float64x2_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_s64_f64(a: float64x2_t) -> int64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u8_f64(a: float64x2_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u8_f64(a: float64x2_t) -> uint8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u16_f64(a: float64x2_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u16_f64(a: float64x2_t) -> uint16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u32_f64(a: float64x2_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u32_f64(a: float64x2_t) -> uint32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u64_f64(a: float64x2_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u64_f64(a: float64x2_t) -> uint64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p8_f64(a: float64x2_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p8_f64(a: float64x2_t) -> poly8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p16_f64(a: float64x2_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p16_f64(a: float64x2_t) -> poly16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_f64(a: float64x2_t) -> poly64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_f64(a: float64x2_t) -> poly64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s8(a: int8x8_t) -> float64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s8(a: int8x8_t) -> float64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s8(a: int8x16_t) -> float64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s8(a: int8x16_t) -> float64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s16(a: int16x4_t) -> float64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s16(a: int16x4_t) -> float64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s16(a: int16x8_t) -> float64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s16(a: int16x8_t) -> float64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s32(a: int32x2_t) -> float64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s32(a: int32x2_t) -> float64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s32(a: int32x4_t) -> float64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s32(a: int32x4_t) -> float64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s64)"] -#[inline] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s64(a: int64x1_t) -> float64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s64)"] -#[inline] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p64_s64(a: int64x1_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s64(a: int64x2_t) -> float64x2_t { +pub fn vreinterpretq_u8_f64(a: float64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s64(a: int64x2_t) -> float64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u16_f64(a: float64x2_t) -> uint16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_s64(a: int64x2_t) -> poly64x2_t { +pub fn vreinterpretq_u32_f64(a: float64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_s64(a: int64x2_t) -> poly64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u64_f64(a: float64x2_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u8(a: uint8x8_t) -> float64x1_t { +pub fn vreinterpretq_p8_f64(a: float64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u8(a: uint8x8_t) -> float64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_p16_f64(a: float64x2_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u8(a: uint8x16_t) -> float64x2_t { +pub fn vreinterpretq_p64_f64(a: float64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u8(a: uint8x16_t) -> float64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_f64_s8(a: int8x8_t) -> float64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u16(a: uint16x4_t) -> float64x1_t { +pub fn vreinterpretq_f64_s8(a: int8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u16(a: uint16x4_t) -> float64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpret_f64_s16(a: int16x4_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u16(a: uint16x8_t) -> float64x2_t { +pub fn vreinterpretq_f64_s16(a: int16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u16(a: uint16x8_t) -> float64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_f64_s32(a: int32x2_t) -> float64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u32(a: uint32x2_t) -> float64x1_t { +pub fn vreinterpretq_f64_s32(a: int32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u32(a: uint32x2_t) -> float64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpret_f64_s64(a: int64x1_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u32(a: uint32x4_t) -> float64x2_t { +pub fn vreinterpret_p64_s64(a: int64x1_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u32(a: uint32x4_t) -> float64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_f64_s64(a: int64x2_t) -> float64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s64)"] #[inline] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u64(a: uint64x1_t) -> float64x1_t { +pub fn vreinterpretq_p64_s64(a: int64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u8)"] #[inline] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p64_u64(a: uint64x1_t) -> poly64x1_t { +pub fn vreinterpret_f64_u8(a: uint8x8_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u64(a: uint64x2_t) -> float64x2_t { +pub fn vreinterpretq_f64_u8(a: uint8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u64(a: uint64x2_t) -> float64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_f64_u16(a: uint16x4_t) -> float64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_u64(a: uint64x2_t) -> poly64x2_t { +pub fn vreinterpretq_f64_u16(a: uint16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_u64(a: uint64x2_t) -> poly64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_f64_u32(a: uint32x2_t) -> float64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p8(a: poly8x8_t) -> float64x1_t { +pub fn vreinterpretq_f64_u32(a: uint32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p8(a: poly8x8_t) -> float64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_f64_u64(a: uint64x1_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p8(a: poly8x16_t) -> float64x2_t { +pub fn vreinterpret_p64_u64(a: uint64x1_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p8(a: poly8x16_t) -> float64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_f64_u64(a: uint64x2_t) -> float64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p16(a: poly16x4_t) -> float64x1_t { +pub fn vreinterpretq_p64_u64(a: uint64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p16(a: poly16x4_t) -> float64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpret_f64_p8(a: poly8x8_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p16(a: poly16x8_t) -> float64x2_t { +pub fn vreinterpretq_f64_p8(a: poly8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p16(a: poly16x8_t) -> float64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_f64_p16(a: poly16x4_t) -> float64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f32_p64(a: poly64x1_t) -> float32x2_t { +pub fn vreinterpretq_f64_p16(a: poly16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f32_p64(a: poly64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p64)"] @@ -22790,7 +22039,6 @@ pub fn vreinterpret_u64_p64(a: poly64x1_t) -> uint64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22798,23 +22046,8 @@ pub fn vreinterpretq_f32_p64(a: poly64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f32_p64(a: poly64x2_t) -> float32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22822,23 +22055,8 @@ pub fn vreinterpretq_f64_p64(a: poly64x2_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p64(a: poly64x2_t) -> float64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22846,43 +22064,14 @@ pub fn vreinterpretq_s64_p64(a: poly64x2_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s64_p64(a: poly64x2_t) -> int64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_u64_p64(a: poly64x2_t) -> uint64x2_t { unsafe { transmute(a) } } -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u64_p64(a: poly64x2_t) -> uint64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} #[doc = "Floating-point round to 32-bit integer, using current rounding mode"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrnd32x_f32)"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index b5ba792b18aec..e4e4e040f468d 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -42089,7 +42089,6 @@ pub fn vrecpsq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42103,9 +42102,8 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42115,17 +42113,12 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42135,13 +42128,12 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { +pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42151,17 +42143,12 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42171,13 +42158,12 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { +pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42187,17 +42173,12 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42207,13 +42188,12 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { +pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42223,17 +42203,12 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42243,13 +42218,12 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { +pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42259,14 +42233,12 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42276,13 +42248,12 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { +pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42292,17 +42263,12 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42312,13 +42278,12 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { +pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42328,17 +42293,12 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42348,13 +42308,12 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { +pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42364,17 +42323,12 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42384,13 +42338,12 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { +pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42400,14 +42353,12 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42417,13 +42368,12 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { +pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42433,17 +42383,12 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42453,13 +42398,12 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { +pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42469,17 +42413,12 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42489,13 +42428,12 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { +pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f32)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42505,17 +42443,12 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42525,13 +42458,12 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { +pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s8)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42541,21 +42473,12 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42565,13 +42488,12 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { +pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42581,17 +42503,12 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42601,13 +42518,12 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { +pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s32)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42617,17 +42533,12 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42637,13 +42548,12 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { +pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s64)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42653,17 +42563,12 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42673,13 +42578,12 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { +pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u8)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42689,21 +42593,12 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42713,13 +42608,12 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { +pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u16)"] #[inline] -#[cfg(target_endian = "big")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42729,53 +42623,12 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { +pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42785,33 +42638,12 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { +pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42821,37 +42653,12 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { +pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42861,33 +42668,12 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { +pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42897,33 +42683,12 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { +pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42933,33 +42698,12 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { +pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42969,33 +42713,12 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { +pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43005,34 +42728,12 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { +pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43042,34 +42743,13 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { +pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p128)"] #[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -43078,14 +42758,13 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { +pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f16)"] #[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -43094,18 +42773,13 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f16)"] #[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -43114,7391 +42788,57 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { +pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u32)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u32)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p8)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p8)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p128)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f16)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p64)"] -#[inline] -#[cfg(target_endian = "little")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p128)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_p128(a: p128) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_p128(a: p128) -> float32x4_t { - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_f32(a: float32x2_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_f32(a: float32x2_t) -> int8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_f32(a: float32x2_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_f32(a: float32x2_t) -> int16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_f32(a: float32x2_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_f32(a: float32x2_t) -> int32x2_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_f32(a: float32x2_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_f32(a: float32x2_t) -> int64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_f32(a: float32x2_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_f32(a: float32x2_t) -> uint8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_f32(a: float32x2_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_f32(a: float32x2_t) -> uint16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_f32(a: float32x2_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_f32(a: float32x2_t) -> uint32x2_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_f32(a: float32x2_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_f32(a: float32x2_t) -> uint64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_f32(a: float32x2_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_f32(a: float32x2_t) -> poly8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_f32(a: float32x2_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_f32(a: float32x2_t) -> poly16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_f32(a: float32x4_t) -> p128 { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_f32(a: float32x4_t) -> p128 { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_f32(a: float32x4_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_f32(a: float32x4_t) -> int8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_f32(a: float32x4_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_f32(a: float32x4_t) -> int16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_f32(a: float32x4_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_f32(a: float32x4_t) -> int32x4_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_f32(a: float32x4_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_f32(a: float32x4_t) -> int64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_f32(a: float32x4_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_f32(a: float32x4_t) -> uint8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_f32(a: float32x4_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_f32(a: float32x4_t) -> uint16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_f32(a: float32x4_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_f32(a: float32x4_t) -> uint32x4_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_f32(a: float32x4_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_f32(a: float32x4_t) -> uint64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_f32(a: float32x4_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_f32(a: float32x4_t) -> poly8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_f32(a: float32x4_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_f32(a: float32x4_t) -> poly16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s8(a: int8x8_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s8(a: int8x8_t) -> float32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s8(a: int8x8_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s8(a: int8x8_t) -> int16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s8(a: int8x8_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s8(a: int8x8_t) -> int32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s8(a: int8x8_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s8(a: int8x8_t) -> int64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s8(a: int8x8_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s8(a: int8x8_t) -> uint8x8_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s8(a: int8x8_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s8(a: int8x8_t) -> uint16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s8(a: int8x8_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s8(a: int8x8_t) -> uint32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s8(a: int8x8_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s8(a: int8x8_t) -> uint64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s8(a: int8x8_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s8(a: int8x8_t) -> poly8x8_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s8(a: int8x8_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s8(a: int8x8_t) -> poly16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s8(a: int8x16_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s8(a: int8x16_t) -> float32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s8(a: int8x16_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s8(a: int8x16_t) -> int16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s8(a: int8x16_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s8(a: int8x16_t) -> int32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s8(a: int8x16_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s8(a: int8x16_t) -> int64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s8(a: int8x16_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s8(a: int8x16_t) -> uint8x16_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s8(a: int8x16_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s8(a: int8x16_t) -> uint16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s8(a: int8x16_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s8(a: int8x16_t) -> uint32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s8(a: int8x16_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s8(a: int8x16_t) -> uint64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s8(a: int8x16_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s8(a: int8x16_t) -> poly8x16_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s8(a: int8x16_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s8(a: int8x16_t) -> poly16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s16(a: int16x4_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s16(a: int16x4_t) -> float32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s16(a: int16x4_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s16(a: int16x4_t) -> int8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s16(a: int16x4_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s16(a: int16x4_t) -> int32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s16(a: int16x4_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s16(a: int16x4_t) -> int64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s16(a: int16x4_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s16(a: int16x4_t) -> uint8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s16(a: int16x4_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s16(a: int16x4_t) -> uint16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s16(a: int16x4_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s16(a: int16x4_t) -> uint32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s16(a: int16x4_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s16(a: int16x4_t) -> uint64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s16(a: int16x4_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s16(a: int16x4_t) -> poly8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s16(a: int16x4_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s16(a: int16x4_t) -> poly16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s16(a: int16x8_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s16(a: int16x8_t) -> float32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s16(a: int16x8_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s16(a: int16x8_t) -> int8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s16(a: int16x8_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s16(a: int16x8_t) -> int32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s16(a: int16x8_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s16(a: int16x8_t) -> int64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s16(a: int16x8_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s16(a: int16x8_t) -> uint8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s16(a: int16x8_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s16(a: int16x8_t) -> uint16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s16(a: int16x8_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s16(a: int16x8_t) -> uint32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s16(a: int16x8_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s16(a: int16x8_t) -> uint64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s16(a: int16x8_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s16(a: int16x8_t) -> poly8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s16(a: int16x8_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s16(a: int16x8_t) -> poly16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s32(a: int32x2_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s32(a: int32x2_t) -> float32x2_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s32(a: int32x2_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s32(a: int32x2_t) -> int8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s32(a: int32x2_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s32(a: int32x2_t) -> int16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s32(a: int32x2_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s32(a: int32x2_t) -> int64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s32(a: int32x2_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s32(a: int32x2_t) -> uint8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s32(a: int32x2_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s32(a: int32x2_t) -> uint16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s32(a: int32x2_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s32(a: int32x2_t) -> uint32x2_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s32(a: int32x2_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s32(a: int32x2_t) -> uint64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s32(a: int32x2_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s32(a: int32x2_t) -> poly8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s32(a: int32x2_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s32(a: int32x2_t) -> poly16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s32(a: int32x4_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s32(a: int32x4_t) -> float32x4_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s32(a: int32x4_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s32(a: int32x4_t) -> int8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s32(a: int32x4_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s32(a: int32x4_t) -> int16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s32(a: int32x4_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s32(a: int32x4_t) -> int64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s32(a: int32x4_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s32(a: int32x4_t) -> uint8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s32(a: int32x4_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s32(a: int32x4_t) -> uint16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s32(a: int32x4_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s32(a: int32x4_t) -> uint32x4_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s32(a: int32x4_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s32(a: int32x4_t) -> uint64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s32(a: int32x4_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s32(a: int32x4_t) -> poly8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s32)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s32(a: int32x4_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s32(a: int32x4_t) -> poly16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s64(a: int64x1_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s64(a: int64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s64(a: int64x1_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s64(a: int64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s64(a: int64x1_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s64(a: int64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s64(a: int64x1_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s64(a: int64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s64(a: int64x1_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s64(a: int64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s64(a: int64x1_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s64(a: int64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s64(a: int64x1_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s64(a: int64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s64)"] -#[inline] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s64(a: int64x1_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s64(a: int64x1_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s64(a: int64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s64(a: int64x1_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s64(a: int64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s64(a: int64x2_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s64(a: int64x2_t) -> float32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s64(a: int64x2_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s64(a: int64x2_t) -> int8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s64(a: int64x2_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s64(a: int64x2_t) -> int16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s64(a: int64x2_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s64(a: int64x2_t) -> int32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s64(a: int64x2_t) -> uint8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s64(a: int64x2_t) -> uint8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s64(a: int64x2_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s64(a: int64x2_t) -> uint16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s64(a: int64x2_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s64(a: int64x2_t) -> uint32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s64(a: int64x2_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s64(a: int64x2_t) -> uint64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s64(a: int64x2_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s64(a: int64x2_t) -> poly8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s64)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s64(a: int64x2_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s64(a: int64x2_t) -> poly16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u8(a: uint8x8_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u8(a: uint8x8_t) -> float32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u8(a: uint8x8_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u8(a: uint8x8_t) -> int8x8_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u8(a: uint8x8_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u8(a: uint8x8_t) -> int16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u8(a: uint8x8_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u8(a: uint8x8_t) -> int32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u8(a: uint8x8_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u8(a: uint8x8_t) -> int64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_u8(a: uint8x8_t) -> uint16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_u8(a: uint8x8_t) -> uint16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u8(a: uint8x8_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u8(a: uint8x8_t) -> uint32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u8(a: uint8x8_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u8(a: uint8x8_t) -> uint64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u8(a: uint8x8_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u8(a: uint8x8_t) -> poly8x8_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u8(a: uint8x8_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u8(a: uint8x8_t) -> poly16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u8(a: uint8x16_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u8(a: uint8x16_t) -> float32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u8(a: uint8x16_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u8(a: uint8x16_t) -> int8x16_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u8(a: uint8x16_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u8(a: uint8x16_t) -> int16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u8(a: uint8x16_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u8(a: uint8x16_t) -> int32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u8(a: uint8x16_t) -> int64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u8(a: uint8x16_t) -> int64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_u8(a: uint8x16_t) -> uint16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_u8(a: uint8x16_t) -> uint16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_u8(a: uint8x16_t) -> uint32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_u8(a: uint8x16_t) -> uint32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_u8(a: uint8x16_t) -> uint64x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_u8(a: uint8x16_t) -> uint64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u8(a: uint8x16_t) -> poly8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u8(a: uint8x16_t) -> poly8x16_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u8)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u8(a: uint8x16_t) -> poly16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u8(a: uint8x16_t) -> poly16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u16(a: uint16x4_t) -> float32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u16(a: uint16x4_t) -> float32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u16(a: uint16x4_t) -> int8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u16(a: uint16x4_t) -> int8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u16(a: uint16x4_t) -> int16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u16(a: uint16x4_t) -> int16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u16(a: uint16x4_t) -> int32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u16(a: uint16x4_t) -> int32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u16(a: uint16x4_t) -> int64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u16(a: uint16x4_t) -> int64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_u16(a: uint16x4_t) -> uint8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_u16(a: uint16x4_t) -> uint8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u16(a: uint16x4_t) -> uint32x2_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u16(a: uint16x4_t) -> uint32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u16(a: uint16x4_t) -> uint64x1_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u16(a: uint16x4_t) -> uint64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u16(a: uint16x4_t) -> poly8x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u16(a: uint16x4_t) -> poly8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u16(a: uint16x4_t) -> poly16x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u16(a: uint16x4_t) -> poly16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u16(a: uint16x8_t) -> float32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u16(a: uint16x8_t) -> float32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u16(a: uint16x8_t) -> int8x16_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u16(a: uint16x8_t) -> int8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u16(a: uint16x8_t) -> int16x8_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u16(a: uint16x8_t) -> int16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u16(a: uint16x8_t) -> int32x4_t { - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u16(a: uint16x8_t) -> int32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u16)"] -#[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f16)"] +#[inline] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(nop) )] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u16(a: uint16x8_t) -> int64x2_t { +#[target_feature(enable = "neon,fp16")] +#[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] +pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(nop) )] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u16(a: uint16x8_t) -> int64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +#[target_feature(enable = "neon,fp16")] +#[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] +pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(nop) )] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { +#[target_feature(enable = "neon,fp16")] +#[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] +pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50514,21 +42854,12 @@ pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_f32_p128(a: p128) -> float32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50544,13 +42875,12 @@ pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { +pub fn vreinterpret_s8_f32(a: float32x2_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50566,17 +42896,12 @@ pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_s16_f32(a: float32x2_t) -> int16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50592,13 +42917,12 @@ pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { +pub fn vreinterpret_s32_f32(a: float32x2_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50614,17 +42938,12 @@ pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s64_f32(a: float32x2_t) -> int64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50640,13 +42959,12 @@ pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { +pub fn vreinterpret_u8_f32(a: float32x2_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50662,21 +42980,12 @@ pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_u16_f32(a: float32x2_t) -> uint16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50692,13 +43001,12 @@ pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { +pub fn vreinterpret_u32_f32(a: float32x2_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50714,17 +43022,12 @@ pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u64_f32(a: float32x2_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50740,13 +43043,12 @@ pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { +pub fn vreinterpret_p8_f32(a: float32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50762,17 +43064,12 @@ pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_p16_f32(a: float32x2_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50788,13 +43085,12 @@ pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { +pub fn vreinterpretq_p128_f32(a: float32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50810,17 +43106,12 @@ pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_f32(a: float32x4_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50836,13 +43127,12 @@ pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { +pub fn vreinterpretq_s16_f32(a: float32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50858,17 +43148,12 @@ pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s32_f32(a: float32x4_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50884,13 +43169,12 @@ pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { +pub fn vreinterpretq_s64_f32(a: float32x4_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50906,17 +43190,12 @@ pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u8_f32(a: float32x4_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50932,13 +43211,12 @@ pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { +pub fn vreinterpretq_u16_f32(a: float32x4_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50954,14 +43232,12 @@ pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpretq_u32_f32(a: float32x4_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50977,13 +43253,12 @@ pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { +pub fn vreinterpretq_u64_f32(a: float32x4_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50999,17 +43274,12 @@ pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_p8_f32(a: float32x4_t) -> poly8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51025,13 +43295,12 @@ pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { +pub fn vreinterpretq_p16_f32(a: float32x4_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51047,17 +43316,12 @@ pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_f32_s8(a: int8x8_t) -> float32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51073,13 +43337,12 @@ pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { +pub fn vreinterpret_s16_s8(a: int8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51095,14 +43358,12 @@ pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpret_s32_s8(a: int8x8_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51118,13 +43379,12 @@ pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { +pub fn vreinterpret_s64_s8(a: int8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51140,17 +43400,12 @@ pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u8_s8(a: int8x8_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51166,13 +43421,12 @@ pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { +pub fn vreinterpret_u16_s8(a: int8x8_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51188,17 +43442,12 @@ pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u32_s8(a: int8x8_t) -> uint32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51214,13 +43463,12 @@ pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { +pub fn vreinterpret_u64_s8(a: int8x8_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51236,17 +43484,12 @@ pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_p8_s8(a: int8x8_t) -> poly8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51262,13 +43505,12 @@ pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { +pub fn vreinterpret_p16_s8(a: int8x8_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51284,21 +43526,12 @@ pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_f32_s8(a: int8x16_t) -> float32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51314,13 +43547,12 @@ pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { +pub fn vreinterpretq_s16_s8(a: int8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51336,17 +43568,12 @@ pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s32_s8(a: int8x16_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51362,13 +43589,12 @@ pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { +pub fn vreinterpretq_s64_s8(a: int8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51384,17 +43610,12 @@ pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_u8_s8(a: int8x16_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51410,13 +43631,12 @@ pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { +pub fn vreinterpretq_u16_s8(a: int8x16_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51432,17 +43652,12 @@ pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u32_s8(a: int8x16_t) -> uint32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51458,13 +43673,12 @@ pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { +pub fn vreinterpretq_u64_s8(a: int8x16_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51480,21 +43694,12 @@ pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_p8_s8(a: int8x16_t) -> poly8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51510,13 +43715,12 @@ pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { +pub fn vreinterpretq_p16_s8(a: int8x16_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51532,17 +43736,12 @@ pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_f32_s16(a: int16x4_t) -> float32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51558,13 +43757,12 @@ pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { +pub fn vreinterpret_s8_s16(a: int16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51580,17 +43778,12 @@ pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s32_s16(a: int16x4_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51606,13 +43799,12 @@ pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { +pub fn vreinterpret_s64_s16(a: int16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51628,21 +43820,12 @@ pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_u8_s16(a: int16x4_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51658,13 +43841,12 @@ pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { +pub fn vreinterpret_u16_s16(a: int16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51680,17 +43862,12 @@ pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u32_s16(a: int16x4_t) -> uint32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51706,13 +43883,12 @@ pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { +pub fn vreinterpret_u64_s16(a: int16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51728,16 +43904,12 @@ pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_p8_s16(a: int16x4_t) -> poly8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51753,13 +43925,12 @@ pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { +pub fn vreinterpret_p16_s16(a: int16x4_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51775,16 +43946,12 @@ pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_f32_s16(a: int16x8_t) -> float32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51800,13 +43967,12 @@ pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { +pub fn vreinterpretq_s8_s16(a: int16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51822,16 +43988,12 @@ pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s32_s16(a: int16x8_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51847,13 +44009,12 @@ pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { +pub fn vreinterpretq_s64_s16(a: int16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51869,14 +44030,11 @@ pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u8_s16(a: int16x8_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s16)"] #[inline] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] @@ -51893,13 +44051,12 @@ pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_u64(a: uint64x1_t) -> int64x1_t { +pub fn vreinterpretq_u16_s16(a: int16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51915,13 +44072,12 @@ pub fn vreinterpret_s64_u64(a: uint64x1_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { +pub fn vreinterpretq_u32_s16(a: int16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51937,16 +44093,12 @@ pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_u64_s16(a: int16x8_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51962,13 +44114,12 @@ pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { +pub fn vreinterpretq_p8_s16(a: int16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51984,16 +44135,12 @@ pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_s16(a: int16x8_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52009,13 +44156,12 @@ pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { +pub fn vreinterpret_f32_s32(a: int32x2_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52031,16 +44177,12 @@ pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s8_s32(a: int32x2_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52056,13 +44198,12 @@ pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { +pub fn vreinterpret_s16_s32(a: int32x2_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52078,16 +44219,12 @@ pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_s64_s32(a: int32x2_t) -> int64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52103,13 +44240,12 @@ pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { +pub fn vreinterpret_u8_s32(a: int32x2_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52125,16 +44261,12 @@ pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u16_s32(a: int32x2_t) -> uint16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52150,13 +44282,12 @@ pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { +pub fn vreinterpret_u32_s32(a: int32x2_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52172,17 +44303,12 @@ pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u64_s32(a: int32x2_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52198,13 +44324,12 @@ pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { +pub fn vreinterpret_p8_s32(a: int32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52220,21 +44345,12 @@ pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_p16_s32(a: int32x2_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52250,13 +44366,12 @@ pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { +pub fn vreinterpretq_f32_s32(a: int32x4_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52272,17 +44387,12 @@ pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_s32(a: int32x4_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52298,13 +44408,12 @@ pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { +pub fn vreinterpretq_s16_s32(a: int32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52320,17 +44429,12 @@ pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s64_s32(a: int32x4_t) -> int64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52346,13 +44450,12 @@ pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { +pub fn vreinterpretq_u8_s32(a: int32x4_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52368,17 +44471,12 @@ pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u16_s32(a: int32x4_t) -> uint16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52394,13 +44492,12 @@ pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { +pub fn vreinterpretq_u32_s32(a: int32x4_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52416,21 +44513,12 @@ pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_u64_s32(a: int32x4_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52446,13 +44534,12 @@ pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { +pub fn vreinterpretq_p8_s32(a: int32x4_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52468,17 +44555,12 @@ pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_s32(a: int32x4_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52494,13 +44576,12 @@ pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { +pub fn vreinterpret_f32_s64(a: int64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52516,17 +44597,12 @@ pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_s8_s64(a: int64x1_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52542,13 +44618,12 @@ pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { +pub fn vreinterpret_s16_s64(a: int64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52564,21 +44639,12 @@ pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_s32_s64(a: int64x1_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52594,13 +44660,12 @@ pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { +pub fn vreinterpret_u8_s64(a: int64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52616,17 +44681,12 @@ pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u16_s64(a: int64x1_t) -> uint16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52642,13 +44702,12 @@ pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { +pub fn vreinterpret_u32_s64(a: int64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52664,17 +44723,12 @@ pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_u64_s64(a: int64x1_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52690,13 +44744,12 @@ pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { +pub fn vreinterpret_p8_s64(a: int64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52712,17 +44765,12 @@ pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p16_s64(a: int64x1_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52738,13 +44786,12 @@ pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { +pub fn vreinterpretq_f32_s64(a: int64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52760,17 +44807,12 @@ pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_s64(a: int64x2_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52786,13 +44828,12 @@ pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { +pub fn vreinterpretq_s16_s64(a: int64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52808,17 +44849,12 @@ pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s32_s64(a: int64x2_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52834,13 +44870,12 @@ pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { +pub fn vreinterpretq_u8_s64(a: int64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52856,14 +44891,12 @@ pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_u16_s64(a: int64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52879,13 +44912,12 @@ pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { +pub fn vreinterpretq_u32_s64(a: int64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52901,17 +44933,12 @@ pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_u64_s64(a: int64x2_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52927,13 +44954,12 @@ pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { +pub fn vreinterpretq_p8_s64(a: int64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52949,17 +44975,12 @@ pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_s64(a: int64x2_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52975,13 +44996,12 @@ pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { +pub fn vreinterpret_f32_u8(a: uint8x8_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52997,17 +45017,12 @@ pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s8_u8(a: uint8x8_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53023,13 +45038,12 @@ pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { +pub fn vreinterpret_s16_u8(a: uint8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53045,14 +45059,12 @@ pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_s32_u8(a: uint8x8_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53068,13 +45080,12 @@ pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { +pub fn vreinterpret_s64_u8(a: uint8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53090,17 +45101,12 @@ pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u16_u8(a: uint8x8_t) -> uint16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53116,13 +45122,12 @@ pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { +pub fn vreinterpret_u32_u8(a: uint8x8_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53138,18 +45143,12 @@ pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u64_u8(a: uint8x8_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53165,13 +45164,12 @@ pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { +pub fn vreinterpret_p8_u8(a: uint8x8_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53187,22 +45185,12 @@ pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_p16_u8(a: uint8x8_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53218,13 +45206,12 @@ pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { +pub fn vreinterpretq_f32_u8(a: uint8x16_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53240,18 +45227,12 @@ pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_u8(a: uint8x16_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53267,13 +45248,12 @@ pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { +pub fn vreinterpretq_s16_u8(a: uint8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53289,18 +45269,12 @@ pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s32_u8(a: uint8x16_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53316,13 +45290,12 @@ pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { +pub fn vreinterpretq_s64_u8(a: uint8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53338,18 +45311,12 @@ pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u16_u8(a: uint8x16_t) -> uint16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53365,13 +45332,12 @@ pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { +pub fn vreinterpretq_u32_u8(a: uint8x16_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53387,22 +45353,12 @@ pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_u64_u8(a: uint8x16_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53418,13 +45374,12 @@ pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { +pub fn vreinterpretq_p8_u8(a: uint8x16_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53440,18 +45395,12 @@ pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_u8(a: uint8x16_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53467,13 +45416,12 @@ pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { +pub fn vreinterpret_f32_u16(a: uint16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53489,18 +45437,12 @@ pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_s8_u16(a: uint16x4_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53516,13 +45458,12 @@ pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { +pub fn vreinterpret_s16_u16(a: uint16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53538,18 +45479,12 @@ pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s32_u16(a: uint16x4_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53565,13 +45500,12 @@ pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { +pub fn vreinterpret_s64_u16(a: uint16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53587,18 +45521,12 @@ pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u8_u16(a: uint16x4_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53614,13 +45542,12 @@ pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { +pub fn vreinterpret_u32_u16(a: uint16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53636,17 +45563,12 @@ pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_u64_u16(a: uint16x4_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53662,13 +45584,12 @@ pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { +pub fn vreinterpret_p8_u16(a: uint16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53684,17 +45605,12 @@ pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p16_u16(a: uint16x4_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53710,13 +45626,12 @@ pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { +pub fn vreinterpretq_f32_u16(a: uint16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53732,17 +45647,12 @@ pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_u16(a: uint16x8_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53758,13 +45668,12 @@ pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { +pub fn vreinterpretq_s16_u16(a: uint16x8_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53780,17 +45689,12 @@ pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s32_u16(a: uint16x8_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53806,13 +45710,12 @@ pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { +pub fn vreinterpretq_s64_u16(a: uint16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53828,14 +45731,12 @@ pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53851,13 +45752,12 @@ pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { +pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53873,17 +45773,12 @@ pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53899,13 +45794,12 @@ pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { +pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53921,17 +45815,12 @@ pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53947,13 +45836,12 @@ pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { +pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53969,17 +45857,12 @@ pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53995,13 +45878,12 @@ pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { +pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54017,14 +45899,12 @@ pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54040,13 +45920,12 @@ pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { +pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54062,17 +45941,12 @@ pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54088,13 +45962,12 @@ pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { +pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54110,17 +45983,12 @@ pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54136,13 +46004,12 @@ pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { +pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54158,21 +46025,12 @@ pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54188,13 +46046,12 @@ pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { +pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54210,17 +46067,12 @@ pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54236,13 +46088,12 @@ pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { +pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54258,17 +46109,12 @@ pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54284,13 +46130,12 @@ pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { +pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54306,17 +46151,12 @@ pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54332,13 +46172,12 @@ pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { +pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54354,21 +46193,12 @@ pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54384,13 +46214,12 @@ pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { +pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54406,17 +46235,12 @@ pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54432,13 +46256,12 @@ pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { +pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54454,17 +46277,12 @@ pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54480,13 +46298,12 @@ pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { +pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54502,17 +46319,12 @@ pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54528,13 +46340,12 @@ pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { +pub fn vreinterpret_s64_u64(a: uint64x1_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54550,23 +46361,14 @@ pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54580,15 +46382,14 @@ pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { +pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54602,22 +46403,14 @@ pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54631,15 +46424,14 @@ pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { +pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54653,18 +46445,14 @@ pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54678,15 +46466,14 @@ pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { +pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54700,18 +46487,14 @@ pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54725,15 +46508,14 @@ pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { +pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54747,18 +46529,14 @@ pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54772,15 +46550,14 @@ pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { +pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54794,22 +46571,14 @@ pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54823,15 +46592,14 @@ pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { +pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54845,18 +46613,14 @@ pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u64)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54870,15 +46634,14 @@ pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { +pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u64)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54892,18 +46655,14 @@ pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54917,15 +46676,14 @@ pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { +pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54939,18 +46697,14 @@ pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54964,15 +46718,14 @@ pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { +pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -54986,22 +46739,14 @@ pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55015,15 +46760,14 @@ pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { +pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55037,18 +46781,14 @@ pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55062,15 +46802,14 @@ pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { +pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p128)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55084,18 +46823,14 @@ pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55109,15 +46844,14 @@ pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { +pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55131,16 +46865,14 @@ pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55154,15 +46886,14 @@ pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { +pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55176,17 +46907,14 @@ pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55200,15 +46928,14 @@ pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { +pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55222,20 +46949,14 @@ pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55249,15 +46970,14 @@ pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { +pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55271,16 +46991,14 @@ pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55294,15 +47012,14 @@ pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { +pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55316,16 +47033,14 @@ pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p8)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55339,15 +47054,14 @@ pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { +pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p8)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55361,19 +47075,14 @@ pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55387,15 +47096,14 @@ pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { +pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55409,16 +47117,14 @@ pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55432,15 +47138,14 @@ pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { +pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55454,16 +47159,14 @@ pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55477,15 +47180,14 @@ pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { +pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55499,19 +47201,14 @@ pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55525,15 +47222,14 @@ pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { +pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55547,16 +47243,14 @@ pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55570,15 +47264,14 @@ pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { +pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55592,16 +47285,14 @@ pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55615,15 +47306,14 @@ pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { +pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55637,17 +47327,14 @@ pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55661,15 +47348,14 @@ pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { +pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55683,20 +47369,14 @@ pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55710,15 +47390,14 @@ pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { +pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55732,16 +47411,14 @@ pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55755,15 +47432,14 @@ pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { +pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55777,16 +47453,14 @@ pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p16)"] #[inline] -#[cfg(target_endian = "little")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55800,15 +47474,14 @@ pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { +pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p16)"] #[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] +#[target_feature(enable = "neon")] +#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), @@ -55822,17 +47495,12 @@ pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55848,13 +47516,12 @@ pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { +pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55870,14 +47537,12 @@ pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55893,13 +47558,12 @@ pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { +pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55915,14 +47579,12 @@ pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55938,13 +47600,12 @@ pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { +pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u32)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55960,17 +47621,12 @@ pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55986,13 +47642,12 @@ pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { +pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56008,14 +47663,12 @@ pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56031,13 +47684,12 @@ pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { +pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p128)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56053,14 +47705,12 @@ pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56076,13 +47726,12 @@ pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { +pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56098,15 +47747,12 @@ pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56122,13 +47768,12 @@ pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { +pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p8)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56144,18 +47789,12 @@ pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56171,13 +47810,12 @@ pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { +pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56193,14 +47831,12 @@ pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; +pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56216,13 +47852,12 @@ pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { +pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56238,14 +47873,12 @@ pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; +pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56261,13 +47894,12 @@ pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { +pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p16)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56283,17 +47915,12 @@ pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56309,13 +47936,12 @@ pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { +pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56331,16 +47957,12 @@ pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56356,13 +47978,12 @@ pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { +pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56378,16 +47999,12 @@ pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56403,13 +48020,12 @@ pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { +pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56425,16 +48041,12 @@ pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56450,13 +48062,12 @@ pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { +pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56472,16 +48083,12 @@ pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56497,13 +48104,12 @@ pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { +pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u32)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56519,16 +48125,12 @@ pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56544,13 +48146,12 @@ pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { +pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56566,16 +48167,12 @@ pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } +pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56591,13 +48188,12 @@ pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { +pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p8)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56613,16 +48209,12 @@ pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56638,13 +48230,12 @@ pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { +pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p16)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56660,16 +48251,12 @@ pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56685,13 +48272,12 @@ pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { +pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56707,14 +48293,12 @@ pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; +pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56730,13 +48314,12 @@ pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { +pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56752,21 +48335,12 @@ pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56782,13 +48356,12 @@ pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { +pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56804,17 +48377,12 @@ pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56830,13 +48398,12 @@ pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { +pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56852,17 +48419,12 @@ pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56878,13 +48440,12 @@ pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { +pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56900,21 +48461,12 @@ pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56930,13 +48482,12 @@ pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { +pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56952,17 +48503,12 @@ pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } +pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56978,13 +48524,12 @@ pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { +pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57000,17 +48545,12 @@ pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } +pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57026,13 +48566,12 @@ pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { +pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57048,21 +48587,12 @@ pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } +pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { + unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p64)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57078,13 +48608,12 @@ pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { target_arch = "arm", unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] -pub fn vreinterpretq_p16_p64(a: poly64x2_t) -> poly16x8_t { +pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p64)"] #[inline] -#[cfg(target_endian = "big")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57101,11 +48630,7 @@ pub fn vreinterpretq_p16_p64(a: poly64x2_t) -> poly16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vreinterpretq_p16_p64(a: poly64x2_t) -> poly16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } + unsafe { transmute(a) } } #[doc = "Reversing vector elements (swap endianness)"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrev16_p8)"] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index a1a837bc61064..ccdcea980e1b2 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -8781,6 +8781,7 @@ intrinsics: - [float64x1_t, float32x2_t] - [float32x4_t, float64x2_t] - [float64x2_t, float32x4_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8801,6 +8802,7 @@ intrinsics: # q - [float64x2_t, float16x8_t] - [float16x8_t, float64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index 43dd3b9031507..61a3a5853632c 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -8480,6 +8480,7 @@ intrinsics: - [poly16x8_t, p128] - [int8x16_t, p128] - [uint8x16_t, p128] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8717,6 +8718,7 @@ intrinsics: - [poly8x16_t, float32x4_t] - [poly16x8_t, float32x4_t] - [p128, float32x4_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8780,6 +8782,7 @@ intrinsics: - [float16x8_t, uint16x8_t] - [float16x8_t, uint32x4_t] - [float16x8_t, uint64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8804,6 +8807,7 @@ intrinsics: - [poly128_t, float16x8_t] - [float16x8_t, poly128_t] - [float16x8_t, poly64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] From ec5375fc55802290738b710ae285e78244e141f5 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Thu, 11 Sep 2025 21:51:13 +0100 Subject: [PATCH 0326/1900] Add general arm-linux.md platform doc. Covers all Arm Linux systems, and means that we can reduce the amount of information required in the target specific pages to just the Tier level, the maintainer, and any specific details for that target. --- src/doc/rustc/src/SUMMARY.md | 9 +- .../rustc/src/platform-support/arm-linux.md | 217 ++++++++++++++++++ .../armeb-unknown-linux-gnueabi.md | 3 + .../armv5te-unknown-linux-gnueabi.md | 3 + .../armv7-unknown-linux-uclibceabi.md | 3 + .../armv7-unknown-linux-uclibceabihf.md | 3 + 6 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/arm-linux.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 8e378e53e518a..fc3f71b4a6a8a 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -51,7 +51,6 @@ - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) - - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md) - [armv5te-none-eabi](platform-support/armv5te-none-eabi.md) @@ -62,12 +61,14 @@ - [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md) - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) - - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md) + - [arm\*-unknown-linux-\*](./platform-support/arm-linux.md) + - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) + - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md) + - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) + - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md) - [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md) - [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md) - - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) - - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) - [armv7a-vex-v5](platform-support/armv7a-vex-v5.md) - [\*-android and \*-androideabi](platform-support/android.md) - [\*-linux-ohos](platform-support/openharmony.md) diff --git a/src/doc/rustc/src/platform-support/arm-linux.md b/src/doc/rustc/src/platform-support/arm-linux.md new file mode 100644 index 0000000000000..5f320762e9378 --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm-linux.md @@ -0,0 +1,217 @@ +# Arm Linux support in Rust + +The Arm Architecture has been around since the mid-1980s, going through nine +major revisions, many minor revisions, and spanning both 32-bith and 64-bit +architectures. This page covers 32-bit Arm platforms that run some form of +Linux (but not Android). Those targets are: + +* `arm-unknown-linux-gnueabi` +* `arm-unknown-linux-gnueabihf` +* `arm-unknown-linux-musleabi` +* `arm-unknown-linux-musleabihf` +* [`armeb-unknown-linux-gnueabi`](armeb-unknown-linux-gnueabi.md) +* `armv4t-unknown-linux-gnueabi` +* [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md) +* `armv5te-unknown-linux-musleabi` +* `armv5te-unknown-linux-uclibceabi` +* `armv7-unknown-linux-gnueabi` +* `armv7-unknown-linux-gnueabihf` +* `armv7-unknown-linux-musleabi` +* `armv7-unknown-linux-musleabihf` +* `armv7-unknown-linux-ohos` +* [`armv7-unknown-linux-uclibceabi`](armv7-unknown-linux-uclibceabi.md) +* [`armv7-unknown-linux-uclibceabihf`](armv7-unknown-linux-uclibceabihf.md) +* `thumbv7neon-unknown-linux-gnueabihf` +* `thumbv7neon-unknown-linux-musleabihf` + +Some of these targets have dedicated pages and some do not. This is largely +due to historical accident, or the enthusiasm of the maintainers. This +document attempts to cover all the targets, but only in broad terms. + +To make sense of this list, the architecture and ABI component of the +`-unknown-linux-` tuple will be discussed separately. + +The second part of the tuple is `unknown` because these systems don't come +from any one specific vendor (like `powerpc-ibm-aix` or +`aarch64-apple-darwin`). The third part is `linux`, because this page only +discusses Linux targets. + +## Architecture Component + +* `arm` +* `armeb` +* `armv4t` +* `armv5te` +* `armv7` +* `thumbv7neon` + +The architecture component simply called `arm` corresponds to the Armv6 +architecture - that is, version 6 of the Arm Architecture as defined in +version 6 of the Arm Architecture Reference Manual (the Arm ARM). This was the +last 'legacy' release of the Arm architecture, before they split into +Application, Real-Time and Microcontroller profiles (leading to Armv7-A, +Armv7-R and Armv7-M). Processors that implement the Armv6 architecture include +the ARM1176JZF-S, as found in BCM2835 SoC that powers the Raspberry Pi Zero. +Arm processors are generally fairly backwards compatible, especially for +user-mode code, so code compiled for the `arm` architecture should also work +on newer ARMv7-A systems, or even 64/32-bit Armv8-A systems. + +The `armeb` architecture component specifies an Armv6 processor running in Big +Endian mode (`eb` is for big-endian - the letters are backwards because +engineers used to little-endian systems perceive big-endian numbers to be +written into memory backwards, and they thought it was funnier like that). +Most Arm processors can operate in either little-endian or big-endian mode and +little-endian mode is by far the most common. However, if for whatever reason +you wish to store your Most Significant Bytes first, these targets are +available. They just aren't terribly well tested, or compatible with most +existing pre-compiled Arm libraries. + +Targets that start with `armv4t` are for processors implementing the Armv4T +architecture from 1994. These include the ARM7TDMI, as found in the Nokia 6110 +brick-phone and the Game Boy Advance. The 'T' stands for *Thumb* and indicate +that the processors can execute smaller 16-bit versions of some of the 32-bit +Arm instructions. Because a Thumb is like a small version of an Arm. + +Targets that start with `armv5te` are for processors implementing the Armv5TE +architecture. These are mostly from the ARM9 family, like the ARM946E-S found +in the Nintendo DS. If you are programming an Arm machine from the early +2000s, this might be what you need. + +The `armv7` is arguably a misnomer, and it should be `armv7a`. This is because +it corresponds to the Application profile of Armv7 (i.e. Armv7-A), as opposed +to the Real-Time or Microcontroller profile. Processors implementing this +architecture include the Cortex-A7 and Cortex-A8. + +The `thumbv7neon` component indicates support for a processor that implements +ARMv7-A (the same as `armv7`), it generates Thumb instructions (technically +Thumb-2, also known as the T32 ISA) as opposed to Arm instructions (also known +as the A32 ISA). These instructions are smaller, giving more code per KB of +RAM, but may have a performance penalty if they take two instructions to do +something Arm instructions could do in one. It's a complex trade-off and you +should be doing benchmarks to work out which is better for you, if you +strongly care about code size and/or performance. This component also enables +support for Arm's SIMD extensions, known as Neon. These extensions will +improve performance for certain kinds of repetitive operations. + +## ABI Component + +* `gnueabi` +* `gnueabihf` +* `musleabi` +* `musleabihf` +* `ohos` +* `uclibceabi` +* `uclibceabihf` + +You will need to select the appropriate ABI to match the system you want to be +running this code on. For example, running `eabihf` code on an `eabi` system +will not work correctly. + +The `gnueabi` ABI component indicates support for using the GNU C Library +(glibc), and the Arm Embedded ABI (EABI). The EABI is a replacement of for the +original ABI (now called the Old ABI or OABI), and it is the standard ABI for +32-bit Arm systems. With this ABI, function parameters that are `f32` or `f64` +are passed as if they were integers, instead of being passed via in FPU +registers. Generally these targets also disable the use of the FPU entirely, +although that isn't always true. + +The `gnueabihf` ABI component is like `gnueabi`, except that it support the +'hard-float' of the EABI. That is, function parameters that are `f32` or `f64` +are passed in FPU registers. Naturally, this makes the FPU mandatory. + +Most 'desktop' Linux distributions (Debian, Ubuntu, Fedora, etc) use the GNU C +Library and so you should probably select either `gnueabi` or `gnueabihf`, +depending on whether your distribution is using 'soft-float' (EABI) or +'hard-float' (EABIHF). Debian happens to offer +[both](https://wiki.debian.org/ArmEabiPort) +[kinds](https://wiki.debian.org/ArmHardFloatPort). + +The `musleabi` and `musleabihf` ABI components offer support for the [musl C +library](https://musl.libc.org/). This C library can be used to create 'static +binaries' that have no run-time library requirements (a feature that that +glibc does not support). There are soft-float (`eabi`) and hard-float +(`eabihf`) variants, as per the `gnu*` targets above. + +The `uclibceabi` and `uclibceabihf` ABI components are for the [uClibc-ng C +library](https://uclibc-ng.org/). This is sometimes used in light-weight +embedded Linux distributions, like those created with +[buildroot](https://www.buildroot.org/). + +## Cross Compilation + +Unfortunately, 32-bit Arm machines are generally not the fastest around, and +they don't have much RAM. This means you are likely to be cross-compiling. + +To do this, you need to give Rust a suitable linker to use - one that knows +the Arm architecture, and more importantly, knows where to find a suitable C +Library to link against. + +To do that, you can add the `linker` property to your `.cargo/config.toml`. +Typically you would refer to a suitable copy of GCC that has built as a +cross-compiler, alongside a C library. + +```toml +[target.arm-unknown-linux-gnueabi] +linker = "arm-linux-gnueabi-gcc" +``` + +On Debian Linux, you could install such a cross-compilation toolchain with +`apt install gcc-arm-linux-gnueabi`. For more exotic combinations, you might +need to build a bespoke version of GCC using [crosstool-ng]. + +[crosstool-ng]: https://github.com/crosstool-ng/crosstool-ng + +Note that for GCC, all 32-bit Arm architectures are handled in the same build +- there are no separate Armv4T or Armv6 builds of GCC. The architecture is +selected with flags, like `-march=armv6`, but they aren't required for the +linker. + +Let's assume we are on some Debian machine, and we want to build a basic Arm +Linux binary for a distribution using the GNU C Library, targeting Armv6 with +a hard-float ABI. Such a binary should work on a Raspberry Pi, for example. +The commands are: + +```bash +sudo apt install -y gcc-arm-linux-gnueabihf +rustup target add arm-unknown-linux-gnueabihf +cargo new --bin armdemo +cd armdemo +mkdir .cargo +cat > .cargo/config.toml << EOF +[target.arm-unknown-linux-gnueabihf] +linker = "arm-linux-gnueabihf-gcc" +EOF +cargo build --target=arm-unknown-linux-gnueabihf +``` + +This will give us our ARM Linux binary for the GNU C Library with a soft-float ABI: + +```console +$ file ./target/arm-unknown-linux-gnueabi/debug/armdemo +./target/arm-unknown-linux-gnueabi/debug/armdemo: ELF 32-bit LSB pie + executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter + /lib/ld-linux.so.3, BuildID[sha1]=dd0b9aa5ae876330fd4e2fcf393850f083ec7fcd, + for GNU/Linux 3.2.0, with debug_info, not stripped +``` + +If you are building C code as part of your Rust project, you may want to +direct `cc-rs` to use an appropriate cross-compiler with the `CROSS_COMPILE` +environment variable. You may also want to set the CFLAGS environment variable +for the target. For example: + +```bash +export CROSS_COMPILE=arm-linux-gnueabi +export CFLAGS_arm_unknown_linux_gnueabi="-march=armv6" +``` + +(Note that the dashes (`-`) turn to underscores (`_`) to form the name of the +CFLAGS environment variable) + +If you are building for a Tier 3 target using `-Zbuild-std` (on Nightly Rust), +you need to set these variables as well: + +```bash +export CXX_arm_unknown_linux_gnueabi=arm-linux-gnueabi-g++ +export CC_arm_unknown_linux_gnueabi=arm-linux-gnueabi-gcc +cargo +nightly build -Zbuild-std --target=arm-unknown-linux-gnueabi +``` diff --git a/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md index 7c1c5db7076b6..cd0623f73a1db 100644 --- a/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md +++ b/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md @@ -3,6 +3,9 @@ Target for cross-compiling Linux user-mode applications targeting the Arm BE8 architecture. +See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux +targets. + ## Overview BE8 architecture retains the same little-endian ordered code-stream used by conventional little endian Arm systems, however the data accesses are in big-endian. BE8 is used primarily in high-performance networking applications where the ability to read packets in their native "Network Byte Order" is important (many network protocols transmit data in big-endian byte order for their wire formats). diff --git a/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md index 0aebbc34d400b..a924f476411df 100644 --- a/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md +++ b/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md @@ -5,6 +5,9 @@ This target supports Linux programs with glibc on ARMv5TE CPUs without floating-point units. +See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux +targets. + ## Target maintainers There are currently no formally documented target maintainers. diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md index e553c49589de6..4ab0a07090a62 100644 --- a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md +++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md @@ -4,6 +4,9 @@ This target supports Armv7-A softfloat CPUs and uses the uclibc-ng standard library. This is a common configuration on many consumer routers (e.g., Netgear R7000, Asus RT-AC68U). +See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux +targets. + ## Target maintainers [@lancethepants](https://github.com/lancethepants) diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md index 91f3ea886ccb5..9fb24906b4fc3 100644 --- a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md +++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md @@ -4,6 +4,9 @@ This tier supports the Armv7-A processor running a Linux kernel and uClibc-ng standard library. It provides full support for rust and the rust standard library. +See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux +targets. + ## Target Maintainers [@skrap](https://github.com/skrap) From 321a76b5f95325f20c9e88e87bfb4ddf536b6950 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 11 Sep 2025 23:52:04 +0200 Subject: [PATCH 0327/1900] c-variadic: test that unsupported c-variadic ABIs are reported correctly --- tests/ui/c-variadic/unsupported-abi.rs | 123 ++++++++ tests/ui/c-variadic/unsupported-abi.stderr | 345 +++++++++++++++++++++ 2 files changed, 468 insertions(+) create mode 100644 tests/ui/c-variadic/unsupported-abi.rs create mode 100644 tests/ui/c-variadic/unsupported-abi.stderr diff --git a/tests/ui/c-variadic/unsupported-abi.rs b/tests/ui/c-variadic/unsupported-abi.rs new file mode 100644 index 0000000000000..40179b164ce42 --- /dev/null +++ b/tests/ui/c-variadic/unsupported-abi.rs @@ -0,0 +1,123 @@ +//@ add-core-stubs +//@ needs-llvm-components: x86 +//@ compile-flags: --target=i686-pc-windows-gnu --crate-type=rlib +#![no_core] +#![feature(no_core, lang_items, c_variadic)] + +// Test that ABIs for which C-variadics are not supported report an error. + +extern crate minicore; +use minicore::*; + +#[rustfmt::skip] +mod foreign { + extern "Rust" { fn rust_foreign_explicit(_: ...); } + //~^ ERROR C-variadic functions with the "Rust" calling convention are not supported + extern "C" { fn c_foreign(_: ...); } + extern "C-unwind" { fn c_unwind_foreign(_: ...); } + extern "cdecl" { fn cdecl_foreign(_: ...); } + extern "cdecl-unwind" { fn cdecl_unwind_foreign(_: ...); } + extern "stdcall" { fn stdcall_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "stdcall" calling convention are not supported + extern "stdcall-unwind" { fn stdcall_unwind_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "stdcall-unwind" calling convention are not supported + extern "thiscall" { fn thiscall_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "thiscall" calling convention are not supported + extern "thiscall-unwind" { fn thiscall_unwind_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "thiscall-unwind" calling convention are not supported +} + +#[lang = "va_list"] +struct VaList(*mut u8); + +unsafe fn rust_free(_: ...) {} +//~^ ERROR `...` is not supported for non-extern functions +unsafe extern "Rust" fn rust_free_explicit(_: ...) {} +//~^ ERROR `...` is not supported for `extern "Rust"` functions + +unsafe extern "C" fn c_free(_: ...) {} +unsafe extern "C-unwind" fn c_unwind_free(_: ...) {} + +unsafe extern "cdecl" fn cdecl_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "cdecl"` functions +unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions +unsafe extern "stdcall" fn stdcall_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "stdcall"` functions +unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions +unsafe extern "thiscall" fn thiscall_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "thiscall"` functions +unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions + +struct S; + +impl S { + unsafe fn rust_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} + +trait T { + unsafe fn rust_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_trait_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} + +impl T for S { + unsafe fn rust_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_trait_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} diff --git a/tests/ui/c-variadic/unsupported-abi.stderr b/tests/ui/c-variadic/unsupported-abi.stderr new file mode 100644 index 0000000000000..daed91714065a --- /dev/null +++ b/tests/ui/c-variadic/unsupported-abi.stderr @@ -0,0 +1,345 @@ +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:33:21 + | +LL | unsafe fn rust_free(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:35:44 + | +LL | unsafe extern "Rust" fn rust_free_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:41:37 + | +LL | unsafe extern "cdecl" fn cdecl_free(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:43:51 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:45:41 + | +LL | unsafe extern "stdcall" fn stdcall_free(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:47:55 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:49:43 + | +LL | unsafe extern "thiscall" fn thiscall_free(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:51:57 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:57:27 + | +LL | unsafe fn rust_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:59:50 + | +LL | unsafe extern "Rust" fn rust_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:65:43 + | +LL | unsafe extern "cdecl" fn cdecl_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:67:57 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:69:47 + | +LL | unsafe extern "stdcall" fn stdcall_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:71:61 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:73:49 + | +LL | unsafe extern "thiscall" fn thiscall_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:75:63 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:80:33 + | +LL | unsafe fn rust_trait_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:82:56 + | +LL | unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:88:49 + | +LL | unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:90:63 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:92:53 + | +LL | unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:94:67 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:96:55 + | +LL | unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:98:69 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:103:33 + | +LL | unsafe fn rust_trait_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:105:56 + | +LL | unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:111:49 + | +LL | unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:113:63 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:115:53 + | +LL | unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:117:67 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:119:55 + | +LL | unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:121:69 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error[E0045]: C-variadic functions with the "Rust" calling convention are not supported + --> $DIR/unsupported-abi.rs:14:22 + | +LL | extern "Rust" { fn rust_foreign_explicit(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "stdcall" calling convention are not supported + --> $DIR/unsupported-abi.rs:20:25 + | +LL | extern "stdcall" { fn stdcall_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "stdcall-unwind" calling convention are not supported + --> $DIR/unsupported-abi.rs:22:32 + | +LL | extern "stdcall-unwind" { fn stdcall_unwind_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "thiscall" calling convention are not supported + --> $DIR/unsupported-abi.rs:24:26 + | +LL | extern "thiscall" { fn thiscall_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "thiscall-unwind" calling convention are not supported + --> $DIR/unsupported-abi.rs:26:33 + | +LL | extern "thiscall-unwind" { fn thiscall_unwind_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error: aborting due to 37 previous errors + +For more information about this error, try `rustc --explain E0045`. From 88d7d20122e45f4167e262e6eda03fbf5383fb18 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 11 Sep 2025 15:48:44 -0700 Subject: [PATCH 0328/1900] Skip cleanups on unsupported targets This commit is an update to the `AbortUnwindingCalls` MIR pass in the compiler. Specifically a new boolean is added for "can this target possibly unwind" and if that's `false` then terminators are all adjusted to be unreachable/not present. The end result is that this fixes 140293 for wasm targets. The motivation for this PR is that currently on WebAssembly targets the usage of the `C-unwind` ABI can lead LLVM to either (a) emit exception-handling instructions or (b) hit a LLVM-ICE-style codegen error. WebAssembly as a base instruction set does not support unwinding at all, and a later proposal to WebAssembly, the exception-handling proposal, was what enabled this. This means that the current intent of WebAssembly targets is that they maintain the baseline of "don't emit exception-handling instructions unless enabled". The commit here is intended to restore this behavior by skipping these instructions even when `C-unwind` is present. Exception-handling is a relatively tricky and also murky topic in WebAssembly, however. There are two sets of instructions LLVM can emit for WebAssembly exceptions, Rust's Emscripten target supports exceptions, WASI targets do not, the LLVM flags to enable this are not always obvious, and additionally this all touches on "changing exception-handling behavior should be a target-level concern, not a feature". Effectively WebAssembly's exception-handling integration into Rust is not finalized at this time. The best idea at this time is that a parallel set of targets will eventually be added which support exceptions, but it's not clear if/when to do this. In the meantime the goal is to keep existing targets working while still enabling experimentation with exception-handling with `-Zbuild-std` and various permutations of LLVM flags. To that extent this commit does not blanket disable these landing pads and cleanup routines for WebAssembly but instead checks to see if panic=unwind is enabled or if `+exception-handling` is enabled. Tests are updated here as well to account for this where, by default, using a `C-unwind` ABI won't affect Rust codegen at all. If `+exception-handling` is enabled, however, then Rust codegen will look like native platforms where exceptions are caught and the program aborts. More-or-less I've done my best to keep exceptions working on wasm where it's possible to have them work, but turned them off where they're not supposed to be emitted. --- .../src/abort_unwinding_calls.rs | 37 +++++++++--- compiler/rustc_span/src/symbol.rs | 1 + .../unwind-abis/c-unwind-abi-panic-abort.rs | 9 ++- tests/codegen-llvm/unwind-and-panic-abort.rs | 9 ++- tests/codegen-llvm/wasm_exceptions.rs | 57 +++++++++++++------ 5 files changed, 86 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 5bd6fdcf48577..35a21a2a83429 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -3,6 +3,7 @@ use rustc_ast::InlineAsmOptions; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt, layout}; +use rustc_span::sym; use rustc_target::spec::PanicStrategy; /// A pass that runs which is targeted at ensuring that codegen guarantees about @@ -33,6 +34,19 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { return; } + // Represent whether this compilation target fundamentally doesn't + // support unwinding at all at an ABI level. If this the target has no + // support for unwinding then cleanup actions, for example, are all + // unnecessary and can be considered unreachable. + // + // Currently this is only true for wasm targets on panic=abort when the + // `exception-handling` target feature is disabled. In such a + // configuration it's illegal to emit exception-related instructions so + // it's not possible to unwind. + let target_supports_unwinding = !(tcx.sess.target.is_like_wasm + && tcx.sess.panic_strategy() == PanicStrategy::Abort + && !tcx.asm_target_features(def_id).contains(&sym::exception_handling)); + // Here we test for this function itself whether its ABI allows // unwinding or not. let body_ty = tcx.type_of(def_id).skip_binder(); @@ -54,12 +68,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { let Some(terminator) = &mut block.terminator else { continue }; let span = terminator.source_info.span; - // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need - // to replace it with `UnwindTerminate`. - if let TerminatorKind::UnwindResume = &terminator.kind - && !body_can_unwind - { - terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + // If we see an `UnwindResume` terminator inside a function then: + // + // * If the target doesn't support unwinding at all, then this is an + // unreachable block. + // * If the body cannot unwind, we need to replace it with + // `UnwindTerminate`. + if let TerminatorKind::UnwindResume = &terminator.kind { + if !target_supports_unwinding { + terminator.kind = TerminatorKind::Unreachable; + } else if !body_can_unwind { + terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + } } if block.is_cleanup { @@ -93,8 +113,9 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { _ => continue, }; - if !call_can_unwind { - // If this function call can't unwind, then there's no need for it + if !call_can_unwind || !target_supports_unwinding { + // If this function call can't unwind, or if the target doesn't + // support unwinding at all, then there's no need for it // to have a landing pad. This means that we can remove any cleanup // registered for it (and turn it into `UnwindAction::Unreachable`). let cleanup = block.terminator_mut().unwind_mut().unwrap(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6d..c9c3522131689 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -938,6 +938,7 @@ symbols! { ermsb_target_feature, exact_div, except, + exception_handling: "exception-handling", exchange_malloc, exclusive_range_pattern, exhaustive_integer_patterns, diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs index 8d2745ba2f7ae..2ce1d1b2e00e6 100644 --- a/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs +++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs @@ -1,4 +1,9 @@ //@ compile-flags: -C panic=abort +//@ revisions: NONWASM WASM WASMEXN +//@ [NONWASM] ignore-wasm32 +//@ [WASM] only-wasm32 +//@ [WASMEXN] only-wasm32 +//@ [WASMEXN] compile-flags: -Ctarget-feature=+exception-handling // Test that `nounwind` attributes are also applied to extern `C-unwind` Rust functions // when the code is compiled with `panic=abort`. @@ -9,7 +14,9 @@ #[no_mangle] pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() { // Handle both legacy and v0 symbol mangling. - // CHECK: call void @{{.*core9panicking19panic_cannot_unwind}} + // NONWASM: call void @{{.*core9panicking19panic_cannot_unwind}} + // WASMEXN: call void @{{.*core9panicking19panic_cannot_unwind}} + // WASM-NOT: call void @{{.*core9panicking19panic_cannot_unwind}} may_unwind(); } diff --git a/tests/codegen-llvm/unwind-and-panic-abort.rs b/tests/codegen-llvm/unwind-and-panic-abort.rs index 8efa140058ad1..c2838be00afa2 100644 --- a/tests/codegen-llvm/unwind-and-panic-abort.rs +++ b/tests/codegen-llvm/unwind-and-panic-abort.rs @@ -1,4 +1,9 @@ //@ compile-flags: -C panic=abort +//@ revisions: NONWASM WASM WASMEXN +//@ [NONWASM] ignore-wasm32 +//@ [WASM] only-wasm32 +//@ [WASMEXN] only-wasm32 +//@ [WASMEXN] compile-flags: -Ctarget-feature=+exception-handling #![crate_type = "lib"] @@ -9,7 +14,9 @@ extern "C-unwind" { // CHECK: Function Attrs:{{.*}}nounwind // CHECK-NEXT: define{{.*}}void @foo // Handle both legacy and v0 symbol mangling. -// CHECK: call void @{{.*core9panicking19panic_cannot_unwind}} +// NONWASM: call void @{{.*core9panicking19panic_cannot_unwind}} +// WASMEXN: call void @{{.*core9panicking19panic_cannot_unwind}} +// WASM-NOT: call void @{{.*core9panicking19panic_cannot_unwind}} #[no_mangle] pub unsafe extern "C" fn foo() { bar(); diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs index 796b69b722b51..e718f599a3c2f 100644 --- a/tests/codegen-llvm/wasm_exceptions.rs +++ b/tests/codegen-llvm/wasm_exceptions.rs @@ -1,8 +1,9 @@ //@ only-wasm32 -//@ compile-flags: -C panic=unwind -Z emscripten-wasm-eh +//@ revisions: WASM WASMEXN +//@ [WASMEXN] compile-flags: -C panic=unwind -Z emscripten-wasm-eh #![crate_type = "lib"] -#![feature(core_intrinsics, wasm_exception_handling_intrinsics)] +#![feature(core_intrinsics, wasm_exception_handling_intrinsics, link_llvm_intrinsics)] extern "C-unwind" { fn may_panic(); @@ -22,7 +23,8 @@ impl Drop for LogOnDrop { } } -// CHECK-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0 +// WASM-LABEL: @test_cleanup() {{.*}} +// WASMEXN-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0 #[no_mangle] pub fn test_cleanup() { let _log_on_drop = LogOnDrop; @@ -30,12 +32,16 @@ pub fn test_cleanup() { may_panic(); } - // CHECK-NOT: call - // CHECK: invoke void @may_panic() - // CHECK: %cleanuppad = cleanuppad within none [] + // WASMEXN-NOT: call + // WASMEXN: invoke void @may_panic() + // WASMEXN: %cleanuppad = cleanuppad within none [] + // + // WASM: call void @may_panic() + // WASM-NOT: invoke void @may_panic() } -// CHECK-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0 +// WASM-LABEL: @test_rtry() {{.*}} +// WASMEXN-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0 #[no_mangle] pub fn test_rtry() { unsafe { @@ -51,23 +57,40 @@ pub fn test_rtry() { ); } - // CHECK-NOT: call - // CHECK: invoke void @may_panic() - // CHECK: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller - // CHECK: {{.*}} = catchpad within {{.*}} [ptr null] - // CHECK: catchret + // WASMEXN-NOT: call + // WASMEXN: invoke void @may_panic() + // WASMEXN: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller + // WASMEXN: {{.*}} = catchpad within {{.*}} [ptr null] + // WASMEXN: catchret + + // WASM: call void @may_panic() + // WASM-NOT: invoke void @may_panic() + // WASM-NOT: catchswitch + // WASM-NOT: catchpad + // WASM-NOT: catchret } // Make sure the intrinsic is not inferred as nounwind. This is a regression test for #132416. -// CHECK-LABEL: @test_intrinsic() {{.*}} @__gxx_wasm_personality_v0 +// +// Note that this test uses the raw `wasm_throw` intrinsic because the one from +// libstd was built with `-Cpanic=abort` and it's technically not valid to use +// when this crate is compiled with `-Cpanic=unwind`. +// +// WASMEXN-LABEL: @test_intrinsic() {{.*}} @__gxx_wasm_personality_v0 #[no_mangle] +#[cfg(wasmexn)] pub fn test_intrinsic() { let _log_on_drop = LogOnDrop; + + unsafe extern "C-unwind" { + #[link_name = "llvm.wasm.throw"] + fn wasm_throw(tag: i32, ptr: *mut u8) -> !; + } unsafe { - core::arch::wasm32::throw::<0>(core::ptr::null_mut()); + wasm_throw(0, core::ptr::null_mut()); } - // CHECK-NOT: call - // CHECK: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null) - // CHECK: %cleanuppad = cleanuppad within none [] + // WASMEXN-NOT: call + // WASMEXN: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null) + // WASMEXN: %cleanuppad = cleanuppad within none [] } From 066a9794dff5d2976f77cd54bac8d6ee8aaa6c6b Mon Sep 17 00:00:00 2001 From: yanglsh Date: Mon, 25 Aug 2025 08:20:01 +0800 Subject: [PATCH 0329/1900] fix: `invalid_upcast_comparisons` wrongly unmangled macros --- clippy_lints/src/invalid_upcast_comparisons.rs | 13 +++++++++++-- tests/ui/invalid_upcast_comparisons.rs | 12 ++++++++++++ tests/ui/invalid_upcast_comparisons.stderr | 8 +++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index b0ecc5d52ddb8..1666e8e5ae324 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -1,3 +1,4 @@ +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -9,7 +10,7 @@ use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; declare_clippy_lint! { /// ### What it does @@ -69,13 +70,21 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); span_lint( cx, INVALID_UPCAST_COMPARISONS, span, format!( "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), + cast_val_snip, if always { "true" } else { "false" }, ), ); diff --git a/tests/ui/invalid_upcast_comparisons.rs b/tests/ui/invalid_upcast_comparisons.rs index 4f3194869f430..88ecca65e1509 100644 --- a/tests/ui/invalid_upcast_comparisons.rs +++ b/tests/ui/invalid_upcast_comparisons.rs @@ -133,3 +133,15 @@ fn main() { -5 == (u32 as i32); } + +fn issue15662() { + macro_rules! add_one { + ($x:expr) => { + $x + 1 + }; + } + + let x: u8 = 1; + (add_one!(x) as u32) > 300; + //~^ invalid_upcast_comparisons +} diff --git a/tests/ui/invalid_upcast_comparisons.stderr b/tests/ui/invalid_upcast_comparisons.stderr index ef36f18eabc9a..cc042a7c4b01e 100644 --- a/tests/ui/invalid_upcast_comparisons.stderr +++ b/tests/ui/invalid_upcast_comparisons.stderr @@ -163,5 +163,11 @@ error: because of the numeric bounds on `u8` prior to casting, this expression i LL | -5 >= (u8 as i32); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: because of the numeric bounds on `add_one!(x)` prior to casting, this expression is always false + --> tests/ui/invalid_upcast_comparisons.rs:145:5 + | +LL | (add_one!(x) as u32) > 300; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors From 44edbc6d54e0bd3f9eea882ed5cd7646d6b79523 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 12 Sep 2025 04:52:27 +0000 Subject: [PATCH 0330/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 2a9bacf6187685931d52346a0ecff2e52bdc91cc. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b71a0d219d778..b3f8a26875f40 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f4665ab8368ad2e8a86d4390ae35c28bdd9561bb +2a9bacf6187685931d52346a0ecff2e52bdc91cc From 745b79097f5e0b22819dafc6dcc9a857dc7479d2 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Wed, 10 Sep 2025 08:03:53 +0100 Subject: [PATCH 0331/1900] Only suggest type in as_underscore when it's suggestable --- clippy_lints/src/casts/as_underscore.rs | 8 ++++---- tests/ui/as_underscore_unfixable.rs | 14 ++++++++++++++ tests/ui/as_underscore_unfixable.stderr | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/ui/as_underscore_unfixable.rs create mode 100644 tests/ui/as_underscore_unfixable.stderr diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs index 3ac486dd63fbb..a73e48e5fd5d1 100644 --- a/clippy_lints/src/casts/as_underscore.rs +++ b/clippy_lints/src/casts/as_underscore.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Expr, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::IsSuggestable; use super::AS_UNDERSCORE; @@ -10,15 +10,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tc if matches!(ty.kind, TyKind::Infer(())) { span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| { let ty_resolved = cx.typeck_results().expr_ty(expr); - if let ty::Error(_) = ty_resolved.kind() { - diag.help("consider giving the type explicitly"); - } else { + if ty_resolved.is_suggestable(cx.tcx, true) { diag.span_suggestion( ty.span, "consider giving the type explicitly", ty_resolved, Applicability::MachineApplicable, ); + } else { + diag.help("consider giving the type explicitly"); } }); } diff --git a/tests/ui/as_underscore_unfixable.rs b/tests/ui/as_underscore_unfixable.rs new file mode 100644 index 0000000000000..854feca7174fb --- /dev/null +++ b/tests/ui/as_underscore_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix + +#![warn(clippy::as_underscore)] + +fn main() { + // From issue #15282 + let f = async || (); + let _: Box _> = Box::new(f) as _; + //~^ as_underscore + + let barr = || (|| ()); + let _: Box _> = Box::new(barr) as _; + //~^ as_underscore +} diff --git a/tests/ui/as_underscore_unfixable.stderr b/tests/ui/as_underscore_unfixable.stderr new file mode 100644 index 0000000000000..7385bea5c6501 --- /dev/null +++ b/tests/ui/as_underscore_unfixable.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:8:37 + | +LL | let _: Box _> = Box::new(f) as _; + | ^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + = note: `-D clippy::as-underscore` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::as_underscore)]` + +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:12:33 + | +LL | let _: Box _> = Box::new(barr) as _; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + +error: aborting due to 2 previous errors + From dbfa01b6a19ac62ba559e38c2b95ef26fd90ee39 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 12 Sep 2025 12:03:09 +0300 Subject: [PATCH 0332/1900] remove outdated jsbackend leftovers --- compiler/rustc_llvm/build.rs | 1 - compiler/rustc_llvm/src/lib.rs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 9d21d0d22e3e5..2cfda7a5fb4b2 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -16,7 +16,6 @@ const OPTIONAL_COMPONENTS: &[&str] = &[ "mips", "powerpc", "systemz", - "jsbackend", "webassembly", "msp430", "sparc", diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index ed5edeef1617d..14e94121d1cb8 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -179,12 +179,6 @@ pub fn initialize_available_targets() { LLVMInitializeSystemZAsmPrinter, LLVMInitializeSystemZAsmParser ); - init_target!( - llvm_component = "jsbackend", - LLVMInitializeJSBackendTargetInfo, - LLVMInitializeJSBackendTarget, - LLVMInitializeJSBackendTargetMC - ); init_target!( llvm_component = "msp430", LLVMInitializeMSP430TargetInfo, From fefc9793740e88585a28ed966100718df9dbe522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 12 Sep 2025 13:31:49 +0200 Subject: [PATCH 0333/1900] bootstrap: Show target in "No such target exists" message This makes it a little easier to pinpoint the issue. --- src/bootstrap/src/core/sanity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 04cf63f1c6d44..68202500d9791 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -287,7 +287,7 @@ than building it. if !has_target { panic!( - "No such target exists in the target list,\n\ + "{target_str}: No such target exists in the target list,\n\ make sure to correctly specify the location \ of the JSON specification file \ for custom targets!\n\ From 8b5335cbba6f68b1e3727e1de6095d2e126f08e0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 13:34:22 +0200 Subject: [PATCH 0334/1900] disable broken parts of CI for now --- src/tools/miri/.github/workflows/ci.yml | 33 +++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c0fed96d4e6e3..e1a948c92faa4 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -31,21 +31,22 @@ jobs: os: ubuntu-24.04-arm multiarch: armhf gcc_cross: arm-linux-gnueabihf - - host_target: riscv64gc-unknown-linux-gnu - os: ubuntu-latest - multiarch: riscv64 - gcc_cross: riscv64-linux-gnu - qemu: true - - host_target: s390x-unknown-linux-gnu - os: ubuntu-latest - multiarch: s390x - gcc_cross: s390x-linux-gnu - qemu: true - - host_target: powerpc64le-unknown-linux-gnu - os: ubuntu-latest - multiarch: ppc64el - gcc_cross: powerpc64le-linux-gnu - qemu: true + # Disabled due to Ubuntu repo trouble + # - host_target: riscv64gc-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: riscv64 + # gcc_cross: riscv64-linux-gnu + # qemu: true + # - host_target: s390x-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: s390x + # gcc_cross: s390x-linux-gnu + # qemu: true + # - host_target: powerpc64le-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: ppc64el + # gcc_cross: powerpc64le-linux-gnu + # qemu: true - host_target: aarch64-apple-darwin os: macos-latest - host_target: i686-pc-windows-msvc @@ -67,7 +68,7 @@ jobs: - name: install multiarch if: ${{ matrix.multiarch != '' }} run: | - # s390x, ppc64el need Ubuntu Ports to be in the mirror list + # s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list sudo bash -c "echo 'https://ports.ubuntu.com/priority:4' >> /etc/apt/apt-mirrors.txt" # Add architecture sudo dpkg --add-architecture ${{ matrix.multiarch }} From 221eb1f0d51a97ec6ccb67427635370797d37225 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:50:25 +0000 Subject: [PATCH 0335/1900] intrinsic-test: Make Clippy happy --- library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs | 2 +- library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index b019abab21346..65c179ef0d083 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -113,7 +113,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results: results, + results, arch_tags: intr.architectures, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 312cbee692a56..c6b964a9ce4e4 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -253,7 +253,7 @@ pub fn generate_rust_test_loop( } /// Generate the specializations (unique sequences of const-generic arguments) for this intrinsic. -fn generate_rust_specializations<'a>( +fn generate_rust_specializations( constraints: &mut impl Iterator>, ) -> Vec> { let mut specializations = vec![vec![]]; From a3b7aad20f6ff2a4af0e96c543ac300650c97d5e Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:50:51 +0000 Subject: [PATCH 0336/1900] stdarch-gen-arm: Make Clippy happy --- library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs index 3f3bfed132c76..0f4de83dacb4a 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs @@ -85,7 +85,7 @@ pub fn generate_load_store_tests( TokenStream::from_str(&PREAMBLE).map_err(|e| format!("Preamble is invalid: {e}"))?; // Only output manual tests for the SVE set let manual_tests = match &load_intrinsics[0].target_features[..] { - [s] if s == "sve" => TokenStream::from_str(&MANUAL_TESTS) + [s] if s == "sve" => TokenStream::from_str(MANUAL_TESTS) .map_err(|e| format!("Manual tests are invalid: {e}"))?, _ => quote!(), }; From 05133f2115940aa581df8831031ea77c2c7c99c8 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:51:38 +0000 Subject: [PATCH 0337/1900] examples: Make Clippy happy --- library/stdarch/examples/hex.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/stdarch/examples/hex.rs b/library/stdarch/examples/hex.rs index 95ffbaf666cea..621f55bc0951f 100644 --- a/library/stdarch/examples/hex.rs +++ b/library/stdarch/examples/hex.rs @@ -22,7 +22,6 @@ #![allow( clippy::unwrap_used, clippy::print_stdout, - clippy::unwrap_used, clippy::shadow_reuse, clippy::cast_possible_wrap, clippy::cast_ptr_alignment, From 8a574ca862493b1977749e1a6e3fb85b9356fad7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 12 Sep 2025 20:07:07 +0800 Subject: [PATCH 0338/1900] Fix extra semicolon before else in let-stmt Example --- ```rust fn main() { let x = if true { () } $0 else {}; } ``` **Before this PR**: ```rust fn main() { let x = if true { () } else if $1 { $0 }; else {}; } ``` **After this PR**: ```rust fn main() { let x = if true { () } else if $1 { $0 } else {}; } ``` --- .../ide-completion/src/completions/keyword.rs | 40 +++++++++++++++++++ .../ide-completion/src/context/analysis.rs | 12 +++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index b3d770997ab0c..6162d98372839 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -269,6 +269,46 @@ fn main() { "#, ); + check_edit( + "else if", + r#" +fn main() { + let x = if true { + () + } $0 else {}; +} +"#, + r#" +fn main() { + let x = if true { + () + } else if $1 { + $0 +} else {}; +} +"#, + ); + + check_edit( + "else if", + r#" +fn main() { + let x = if true { + () + } $0 else if true {}; +} +"#, + r#" +fn main() { + let x = if true { + () + } else if $1 { + $0 +} else if true {}; +} +"#, + ); + check_edit( "else", r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 33b98a33cabf6..b33a547dee972 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -970,6 +970,16 @@ fn classify_name_ref<'db>( let after_incomplete_let = |node: SyntaxNode| { prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) }; + let before_else_kw = |node: &SyntaxNode| { + node.parent() + .and_then(ast::ExprStmt::cast) + .filter(|stmt| stmt.semicolon_token().is_none()) + .and_then(|stmt| non_trivia_sibling(stmt.syntax().clone().into(), Direction::Next)) + .and_then(NodeOrToken::into_node) + .filter(|next| next.kind() == SyntaxKind::ERROR) + .and_then(|next| next.first_token()) + .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1276,7 +1286,7 @@ fn classify_name_ref<'db>( .parent() .and_then(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()) - || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true); + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it); let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); From 225e8ff17b593c7621f639c7cd50de2d93c7aee0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 16:30:41 +0200 Subject: [PATCH 0339/1900] move _Unwind_RaiseException out of the frame_in_std section --- .../miri/src/shims/windows/foreign_items.rs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 7b13f1d908073..c3ca01bbf3403 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -820,6 +820,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(length.strict_sub(1), dest)?; } + "_Unwind_RaiseException" => { + // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. + // It was originally specified as part of the Itanium C++ ABI: + // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. + // MinGW implements _Unwind_RaiseException on top of SEH exceptions. + if this.tcx.sess.target.env != "gnu" { + throw_unsup_format!( + "`_Unwind_RaiseException` is not supported on non-MinGW Windows", + ); + } + // This function looks and behaves excatly like miri_start_unwind. + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return interp_ok(EmulateItemResult::NeedsUnwind); + } + // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { @@ -880,22 +896,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - "_Unwind_RaiseException" => { - // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. - // It was originally specified as part of the Itanium C++ ABI: - // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. - // MinGW implements _Unwind_RaiseException on top of SEH exceptions. - if this.tcx.sess.target.env != "gnu" { - throw_unsup_format!( - "`_Unwind_RaiseException` is not supported on non-MinGW Windows", - ); - } - // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.handle_miri_start_unwind(payload)?; - return interp_ok(EmulateItemResult::NeedsUnwind); - } - _ => return interp_ok(EmulateItemResult::NotSupported), } From baed55cceffaa3b5dd0754436b413fa9aef544af Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 12 Sep 2025 09:49:41 -0500 Subject: [PATCH 0340/1900] Remove unreachable unsized arg handling in `store_fn_arg/store_arg` in codegen --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 9 ++------- compiler/rustc_codegen_llvm/src/abi.rs | 11 +++-------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index eb0a5336a1f13..84fa56cf90308 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -730,7 +730,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { if self.is_sized_indirect() { OperandValue::Ref(PlaceValue::new_sized(val, self.layout.align.abi)).store(bx, dst) } else if self.is_unsized_indirect() { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } else if let PassMode::Cast { ref cast, .. } = self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. @@ -797,12 +797,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { meta_attrs: Some(_), .. } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { meta_attrs: None, .. } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index ac7583f566687..11be704116790 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -215,9 +215,9 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } - // Unsized indirect qrguments + // Unsized indirect arguments cannot be stored PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Cast { cast, pad_i32: _ } => { // The ABI mandates that the value is passed as a different struct representation. @@ -272,12 +272,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } From 7a4b7287bb8c223870b423e4e7bf6b2cfc2531a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 12 Sep 2025 16:50:41 +0200 Subject: [PATCH 0341/1900] Rename "crashes tests" to "crash tests" --- src/doc/rustc-dev-guide/src/fuzzing.md | 7 ++++--- src/doc/rustc-dev-guide/src/tests/best-practices.md | 4 ++-- src/doc/rustc-dev-guide/src/tests/compiletest.md | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/fuzzing.md b/src/doc/rustc-dev-guide/src/fuzzing.md index 3000537861778..cc98b49a97c68 100644 --- a/src/doc/rustc-dev-guide/src/fuzzing.md +++ b/src/doc/rustc-dev-guide/src/fuzzing.md @@ -90,14 +90,15 @@ Here are a few things you can do to help the Rust project after filing an ICE. triggering the ICE, such as syntax errors or borrow-checking errors - Minimize the test case (see below). If successful, you can label the issue with `S-has-mcve`. Otherwise, you can apply `E-needs-mcve`. -- Add the minimal test case to the rust-lang/rust repo as a [crashes test]. +- Add the minimal test case to the rust-lang/rust repo as a [crash test]. While you're at it, consider including other "untracked" crashes in your PR. - Please don't forget to mark your issue with `S-bug-has-test` afterwards. + Please don't forget to mark all relevant issues with `S-bug-has-test` once + your PR is merged. See also [applying and removing labels][labeling]. [bisect]: https://rust-lang.github.io/cargo-bisect-rustc/ -[crashes test]: tests/compiletest.html#crashes-tests +[crash test]: tests/compiletest.html#crash-tests [labeling]: https://forge.rust-lang.org/release/issue-triaging.html#applying-and-removing-labels ## Minimization diff --git a/src/doc/rustc-dev-guide/src/tests/best-practices.md b/src/doc/rustc-dev-guide/src/tests/best-practices.md index be00207e3fb93..7c26cdc66a03b 100644 --- a/src/doc/rustc-dev-guide/src/tests/best-practices.md +++ b/src/doc/rustc-dev-guide/src/tests/best-practices.md @@ -71,7 +71,7 @@ related tests. > //! Regression test for . > ``` > -> One exception to this rule is [crashes tests]: there it is canonical that +> One exception to this rule is [crash tests]: there it is canonical that > tests are named only after issue numbers because its purpose is to track > snippets from which issues no longer ICE/crash, and they would either be > removed or converted into proper ui/other tests in the fix PRs. @@ -199,4 +199,4 @@ See [LLVM FileCheck guide][FileCheck] for details. [compiletest directives]: ./directives.md [`run-make`]: ./compiletest.md#run-make-tests [FileCheck]: https://llvm.org/docs/CommandGuide/FileCheck.html -[crashes tests]: ./compiletest.md#crashes-tests +[crash tests]: ./compiletest.md#crash-tests diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 4980ed845d6dd..a4db1b3eca193 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -72,7 +72,7 @@ The following test suites are available, with links for more information: | [`mir-opt`](#mir-opt-tests) | Check MIR generation and optimizations | | [`coverage`](#coverage-tests) | Check coverage instrumentation | | [`coverage-run-rustdoc`](#coverage-tests) | `coverage` tests that also run instrumented doctests | -| [`crashes`](#crashes-tests) | Check that the compiler ICEs/panics/crashes on certain inputs to catch accidental fixes | +| [`crashes`](#crash-tests) | Check that the compiler ICEs/panics/crashes on certain inputs to catch accidental fixes | ### General purpose test suite @@ -550,7 +550,7 @@ only running the main `coverage` suite. [`src/tools/coverage-dump`]: https://github.com/rust-lang/rust/tree/master/src/tools/coverage-dump [`tests/coverage-run-rustdoc`]: https://github.com/rust-lang/rust/tree/master/tests/coverage-run-rustdoc -### Crashes tests +### Crash tests [`tests/crashes`] serve as a collection of tests that are expected to cause the compiler to ICE, panic or crash in some other way, so that accidental fixes are @@ -573,13 +573,13 @@ recommended to include test cases from several issues in a single PR. When you do so, each issue number should be noted in the file name (`12345.rs` should suffice) and also inside the file by means of a `//@ known-bug: #12345` directive. Please [label][labeling] the relevant issues with `S-bug-has-test` -afterwards. +once your PR is merged. If you happen to fix one of the crashes, please move it to a fitting subdirectory in `tests/ui` and give it a meaningful name. Please add a doc comment at the top of the file explaining why this test exists, even better if you can briefly explain how the example causes rustc to crash previously and -what was done to prevent rustc to ICE/panic/crash. +what was done to prevent rustc to ICE / panic / crash. Adding From 260dd6f7ea7dec25c98ef6299bf1730c7dcf40b3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 17:04:59 +0200 Subject: [PATCH 0342/1900] make a basic hello world work on wasip2 --- src/tools/miri/README.md | 2 +- src/tools/miri/ci/ci.sh | 2 +- .../miri/src/shims/wasi/foreign_items.rs | 68 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 517aa343a6d4d..7094231b902f3 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -220,7 +220,7 @@ degree documented below): - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. + - `wasi`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. However, even for targets that we do support, the degree of support for accessing platform APIs diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 6356e33f61e84..bcc110f648b9b 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -153,7 +153,7 @@ case $HOST_TARGET in BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd - TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm + TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC hello wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std ;; diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index bfcdbd8130d84..ffc02dc986f9a 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -35,6 +35,74 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(res, dest)?; } + // Standard input/output + // FIXME: These shims are hacks that just get basic stdout/stderr working. We can't + // constrain them to "std" since std itself uses the wasi crate for this. + "get-stdout" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(1), dest)?; // POSIX FD number for stdout + } + "get-stderr" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(2), dest)?; // POSIX FD number for stderr + } + "[resource-drop]output-stream" => { + let [handle] = + this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?; + let handle = this.read_scalar(handle)?.to_i32()?; + + if !(handle == 1 || handle == 2) { + throw_unsup_format!("wasm output-stream: unsupported handle"); + } + // We don't actually close these FDs, so this is a NOP. + } + "[method]output-stream.blocking-write-and-flush" => { + let [handle, buf, len, ret_area] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, *mut _) -> ()), + link_name, + abi, + args, + )?; + let handle = this.read_scalar(handle)?.to_i32()?; + let buf = this.read_pointer(buf)?; + let len = this.read_target_usize(len)?; + let ret_area = this.read_pointer(ret_area)?; + + if len > 4096 { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: buffer too big" + ); + } + let len = usize::try_from(len).unwrap(); + let Some(fd) = this.machine.fds.get(handle) else { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: unsupported handle" + ); + }; + fd.write( + this.machine.communicate(), + buf, + len, + this, + callback!( + @capture<'tcx> { + len: usize, + ret_area: Pointer, + } + |this, result: Result| { + if !matches!(result, Ok(l) if l == len) { + throw_unsup_format!("wasm output-stream.blocking-write-and-flush: returning errors is not supported"); + } + // 0 in the first byte of the ret_area indicates success. + let ret = this.ptr_to_mplace(ret_area, this.machine.layouts.u8); + this.write_null(&ret)?; + interp_ok(()) + }), + )?; + } + _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) From a70d78a55286a85cf5e06958929f5a6071fa1c67 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 0343/1900] Implement more features for GenMC mode - Handling Compare-Exchange operations. - Limitation: Compare-Exchange currently ignores possibility of spurious failures. - Limitation: Compare-Exchange failure memory ordering is ignored. - Upgrade compare-exchange success ordering to avoid reporting non-existent bugs. - Add warnings for GenMC mode for unsupported features. - Add a lot of tests, including translation of GenMC litmus tests and Loom tests. - Cleanup --- .../genmc-sys/cpp/include/MiriInterface.hpp | 82 +++++++++---- .../cpp/src/MiriInterface/EventHandling.cpp | 65 +++++++++++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 8 +- src/tools/miri/genmc-sys/src/lib.rs | 25 ++++ .../miri/src/concurrency/genmc/helper.rs | 82 ++++++++++++- src/tools/miri/src/concurrency/genmc/mod.rs | 109 +++++++++++++++--- src/tools/miri/src/concurrency/genmc/run.rs | 4 +- src/tools/miri/src/diagnostics.rs | 26 +++++ .../genmc/fail/data_race/mpu2_rels_rlx.rs | 45 ++++++++ .../genmc/fail/data_race/mpu2_rels_rlx.stderr | 19 +++ .../fail/loom/store_buffering.genmc.stderr | 13 +++ .../loom/store_buffering.non_genmc.stderr | 13 +++ .../tests/genmc/fail/loom/store_buffering.rs | 57 +++++++++ .../atomics/cas_failure_ord_racy_key_init.rs | 70 +++++++++++ .../cas_failure_ord_racy_key_init.stderr | 22 ++++ .../tests/genmc/pass/atomics/cas_simple.rs | 34 ++++++ .../genmc/pass/atomics/cas_simple.stderr | 2 + .../miri/tests/genmc/pass/litmus/IRIWish.rs | 4 +- .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 67 +++++++++++ .../genmc/pass/litmus/MPU2_rels_acqf.stderr | 38 ++++++ .../miri/tests/genmc/pass/litmus/Z6_U.rs | 7 +- .../miri/tests/genmc/pass/litmus/casdep.rs | 37 ++++++ .../tests/genmc/pass/litmus/casdep.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/ccr.rs | 34 ++++++ .../miri/tests/genmc/pass/litmus/ccr.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/cii.rs | 35 ++++++ .../miri/tests/genmc/pass/litmus/cii.stderr | 2 + 27 files changed, 850 insertions(+), 54 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/casdep.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/casdep.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/ccr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/ccr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cii.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cii.stderr diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index b076937584326..662eb0e173ca8 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -34,6 +34,7 @@ struct SchedulingResult; struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; +struct CompareExchangeResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -100,6 +101,17 @@ struct MiriGenmcShim : private GenMCDriver { GenmcScalar rhs_value, GenmcScalar old_val ); + [[nodiscard]] CompareExchangeResult handle_compare_exchange( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar expected_value, + GenmcScalar new_value, + GenmcScalar old_val, + MemOrdering success_ordering, + MemOrdering fail_load_ordering, + bool can_fail_spuriously + ); [[nodiscard]] StoreResult handle_store( ThreadId thread_id, uint64_t address, @@ -231,15 +243,15 @@ constexpr auto get_global_alloc_static_mask() -> uint64_t { namespace GenmcScalarExt { inline GenmcScalar uninit() { return GenmcScalar { - /* value: */ 0, - /* is_init: */ false, + .value = 0, + .is_init = false, }; } inline GenmcScalar from_sval(SVal sval) { return GenmcScalar { - /* value: */ sval.get(), - /* is_init: */ true, + .value = sval.get(), + .is_init = true, }; } @@ -252,22 +264,22 @@ inline SVal to_sval(GenmcScalar scalar) { namespace LoadResultExt { inline LoadResult no_value() { return LoadResult { - /* error: */ std::unique_ptr(nullptr), - /* has_value: */ false, - /* read_value: */ GenmcScalarExt::uninit(), + .error = std::unique_ptr(nullptr), + .has_value = false, + .read_value = GenmcScalarExt::uninit(), }; } inline LoadResult from_value(SVal read_value) { - return LoadResult { /* error: */ std::unique_ptr(nullptr), - /* has_value: */ true, - /* read_value: */ GenmcScalarExt::from_sval(read_value) }; + return LoadResult { .error = std::unique_ptr(nullptr), + .has_value = true, + .read_value = GenmcScalarExt::from_sval(read_value) }; } inline LoadResult from_error(std::unique_ptr error) { - return LoadResult { /* error: */ std::move(error), - /* has_value: */ false, - /* read_value: */ GenmcScalarExt::uninit() }; + return LoadResult { .error = std::move(error), + .has_value = false, + .read_value = GenmcScalarExt::uninit() }; } } // namespace LoadResultExt @@ -278,26 +290,50 @@ inline StoreResult ok(bool is_coherence_order_maximal_write) { } inline StoreResult from_error(std::unique_ptr error) { - return StoreResult { /* error: */ std::move(error), - /* is_coherence_order_maximal_write: */ false }; + return StoreResult { .error = std::move(error), .is_coherence_order_maximal_write = false }; } } // namespace StoreResultExt namespace ReadModifyWriteResultExt { inline ReadModifyWriteResult ok(SVal old_value, SVal new_value, bool is_coherence_order_maximal_write) { - return ReadModifyWriteResult { /* error: */ std::unique_ptr(nullptr), - /* old_value: */ GenmcScalarExt::from_sval(old_value), - /* new_value: */ GenmcScalarExt::from_sval(new_value), - is_coherence_order_maximal_write }; + return ReadModifyWriteResult { .error = std::unique_ptr(nullptr), + .old_value = GenmcScalarExt::from_sval(old_value), + .new_value = GenmcScalarExt::from_sval(new_value), + .is_coherence_order_maximal_write = + is_coherence_order_maximal_write }; } inline ReadModifyWriteResult from_error(std::unique_ptr error) { - return ReadModifyWriteResult { /* error: */ std::move(error), - /* old_value: */ GenmcScalarExt::uninit(), - /* new_value: */ GenmcScalarExt::uninit(), - /* is_coherence_order_maximal_write: */ false }; + return ReadModifyWriteResult { .error = std::move(error), + .old_value = GenmcScalarExt::uninit(), + .new_value = GenmcScalarExt::uninit(), + .is_coherence_order_maximal_write = false }; } } // namespace ReadModifyWriteResultExt +namespace CompareExchangeResultExt { +inline CompareExchangeResult success(SVal old_value, bool is_coherence_order_maximal_write) { + return CompareExchangeResult { .error = nullptr, + .old_value = GenmcScalarExt::from_sval(old_value), + .is_success = true, + .is_coherence_order_maximal_write = + is_coherence_order_maximal_write }; +} + +inline CompareExchangeResult failure(SVal old_value) { + return CompareExchangeResult { .error = nullptr, + .old_value = GenmcScalarExt::from_sval(old_value), + .is_success = false, + .is_coherence_order_maximal_write = false }; +} + +inline CompareExchangeResult from_error(std::unique_ptr error) { + return CompareExchangeResult { .error = std::move(error), + .old_value = GenmcScalarExt::uninit(), + .is_success = false, + .is_coherence_order_maximal_write = false }; +} +} // namespace CompareExchangeResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index cd28e0d148f58..05c82641df948 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -155,6 +155,71 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { ); } +[[nodiscard]] auto MiriGenmcShim::handle_compare_exchange( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar expected_value, + GenmcScalar new_value, + GenmcScalar old_val, + MemOrdering success_ordering, + MemOrdering fail_load_ordering, + bool can_fail_spuriously +) -> CompareExchangeResult { + // NOTE: Both the store and load events should get the same `ordering`, it should not be split + // into a load and a store component. This means we can have for example `AcqRel` loads and + // stores, but this is intended for CAS operations. + + // FIXME(GenMC): properly handle failure memory ordering. + + auto expectedVal = GenmcScalarExt::to_sval(expected_value); + auto new_val = GenmcScalarExt::to_sval(new_value); + + const auto load_ret = handle_load_reset_if_none( + thread_id, + success_ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + expectedVal, + new_val + ); + if (const auto* err = std::get_if(&load_ret)) + return CompareExchangeResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&load_ret); + ERROR_ON(nullptr == ret_val, "Unimplemented: load returned unexpected result."); + const auto read_old_val = *ret_val; + if (read_old_val != expectedVal) + return CompareExchangeResultExt::failure(read_old_val); + + // FIXME(GenMC): Add support for modelling spurious failures. + + const auto storePos = inc_pos(thread_id); + const auto store_ret = GenMCDriver::handleStore( + storePos, + success_ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + new_val + ); + if (const auto* err = std::get_if(&store_ret)) + return CompareExchangeResultExt::from_error(format_error(*err)); + const auto* store_ret_val = std::get_if(&store_ret); + ERROR_ON( + nullptr == store_ret_val, + "Unimplemented: compare-exchange store returned unexpected result." + ); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return CompareExchangeResultExt::success( + read_old_val, + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + ); +} + /**** Memory (de)allocation ****/ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index 5a53fee059216..a17a83aa06e13 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -152,10 +152,10 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri already ensures that memory accesses are valid, so this check doesn't matter. // We check that the address is static, but skip checking if it is part of an actual // allocation. - /* isStaticallyAllocated: */ [](SAddr addr) { return addr.isStatic(); }, + .isStaticallyAllocated = [](SAddr addr) { return addr.isStatic(); }, // FIXME(genmc,error reporting): Once a proper a proper API for passing such information is // implemented in GenMC, Miri should use it to improve the produced error messages. - /* getStaticName: */ [](SAddr addr) { return "[UNKNOWN STATIC]"; }, + .getStaticName = [](SAddr addr) { return "[UNKNOWN STATIC]"; }, // This function is called to get the initial value stored at the given address. // // From a Miri perspective, this API doesn't work very well: most memory starts out @@ -177,10 +177,10 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the // initial value getter would return an `optional`, since the memory location may be // uninitialized. - /* initValGetter: */ [](const AAccess& a) { return SVal(0xDEAD); }, + .initValGetter = [](const AAccess& a) { return SVal(0xDEAD); }, // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in // that case. This should no longer be required with proper mixed-size access support. - /* skipUninitLoadChecks: */ [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + .skipUninitLoadChecks = [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, }; driver->setInterpCallbacks(std::move(interpreter_callbacks)); diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 1de4c4eb5e8e9..31bc2606adc01 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -198,6 +198,19 @@ mod ffi { is_coherence_order_maximal_write: bool, } + #[must_use] + #[derive(Debug)] + struct CompareExchangeResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the compare-exchange. + old_value: GenmcScalar, + /// `true` if compare_exchange op was successful. + is_success: bool, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ @@ -313,6 +326,18 @@ mod ffi { rhs_value: GenmcScalar, old_value: GenmcScalar, ) -> ReadModifyWriteResult; + fn handle_compare_exchange( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + expected_value: GenmcScalar, + new_value: GenmcScalar, + old_value: GenmcScalar, + success_ordering: MemOrdering, + fail_load_ordering: MemOrdering, + can_fail_spuriously: bool, + ) -> CompareExchangeResult; fn handle_store( self: Pin<&mut MiriGenmcShim>, thread_id: i32, diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index 2a84ca2036676..48a5ec8bb2608 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,20 +1,52 @@ +use std::sync::RwLock; + use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; use rustc_middle::ty::ScalarInt; +use rustc_span::Span; use tracing::debug; use super::GenmcScalar; +use crate::diagnostics::EvalContextExt; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MiriInterpCx, Scalar, - throw_unsup_format, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, InterpCx, MiriInterpCx, + MiriMachine, NonHaltingDiagnostic, Scalar, throw_unsup_format, }; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; +/// Type for storing spans for already emitted warnings. +pub(super) type WarningCache = RwLock>; + +#[derive(Default)] +pub(super) struct Warnings { + pub(super) compare_exchange_failure_ordering: WarningCache, + pub(super) compare_exchange_weak: WarningCache, +} + +/// Emit a warning if it hasn't already been reported for current span. +pub(super) fn emit_warning<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + cache: &WarningCache, + diagnostic: impl FnOnce() -> NonHaltingDiagnostic, +) { + let span = ecx.machine.current_span(); + if cache.read().unwrap().contains(&span) { + return; + } + // This span has not yet been reported, so we insert it into the cache and report it. + let mut cache = cache.write().unwrap(); + if cache.insert(span) { + // Some other thread may have added this span while we didn't hold the lock, so we only emit it if the insertions succeeded. + ecx.emit_diagnostic(diagnostic()); + } +} + /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. pub fn split_access(address: Size, size: Size) -> impl Iterator { @@ -112,7 +144,53 @@ impl AtomicFenceOrd { } } +/// Since GenMC ignores the failure memory ordering and Miri should not detect bugs that don't actually exist, we upgrade the success ordering if required. +/// This means that Miri running in GenMC mode will not explore all possible executions allowed under the RC11 memory model. +/// FIXME(genmc): remove this once GenMC properly supports the failure memory ordering. +pub(super) fn maybe_upgrade_compare_exchange_success_orderings( + success: AtomicRwOrd, + failure: AtomicReadOrd, +) -> AtomicRwOrd { + use AtomicReadOrd::*; + let (success_read, success_write) = success.split_memory_orderings(); + let upgraded_success_read = match (success_read, failure) { + (_, SeqCst) | (SeqCst, _) => SeqCst, + (Acquire, _) | (_, Acquire) => Acquire, + (Relaxed, Relaxed) => Relaxed, + }; + AtomicRwOrd::from_split_memory_orderings(upgraded_success_read, success_write) +} + impl AtomicRwOrd { + /// Split up an atomic read-write memory ordering into a separate read and write ordering. + pub(super) fn split_memory_orderings(self) -> (AtomicReadOrd, AtomicWriteOrd) { + match self { + AtomicRwOrd::Relaxed => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Acquire => (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Release => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release), + AtomicRwOrd::AcqRel => (AtomicReadOrd::Acquire, AtomicWriteOrd::Release), + AtomicRwOrd::SeqCst => (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst), + } + } + + /// Split up an atomic read-write memory ordering into a separate read and write ordering. + fn from_split_memory_orderings( + read_ordering: AtomicReadOrd, + write_ordering: AtomicWriteOrd, + ) -> Self { + match (read_ordering, write_ordering) { + (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Relaxed, + (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire, + (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release, + (AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel, + (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, + _ => + panic!( + "Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})" + ), + } + } + pub(super) fn to_genmc(self) -> MemOrdering { match self { AtomicRwOrd::Relaxed => MemOrdering::Relaxed, diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index c1da86c673364..7270c66810608 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -13,15 +13,16 @@ use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{ - MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar, to_genmc_rmw_op, + MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, + maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, }; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, - VisitWith, + MiriMachine, MiriMemoryKind, NonHaltingDiagnostic, Scalar, TerminationInfo, ThreadId, + ThreadManager, VisitProvenance, VisitWith, }; mod config; @@ -81,16 +82,22 @@ impl PerExecutionState { } } -#[derive(Debug)] struct GlobalState { /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: GlobalAllocationHandler, + + /// Cache for which warnings have already been shown to the user. + /// `None` if warnings are disabled. + warning_cache: Option, } impl GlobalState { - fn new(target_usize_max: u64) -> Self { - Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } + fn new(target_usize_max: u64, print_warnings: bool) -> Self { + Self { + global_allocations: GlobalAllocationHandler::new(target_usize_max), + warning_cache: print_warnings.then(Default::default), + } } } @@ -348,21 +355,91 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_compare_exchange<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _expected_old_value: Scalar, - _new_value: Scalar, - _success: AtomicRwOrd, - _fail: AtomicReadOrd, - _can_fail_spuriously: bool, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + expected_old_value: Scalar, + new_value: Scalar, + success: AtomicRwOrd, + fail: AtomicReadOrd, + can_fail_spuriously: bool, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option, bool)> { assert!( !self.get_alloc_data_races(), "atomic compare-exchange with data race checking disabled." ); - throw_unsup_format!("FIXME(genmc): Add support for atomic compare_exchange.") + assert_ne!(0, size.bytes()); + assert!( + size.bytes() <= MAX_ACCESS_SIZE, + "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)", + MAX_ACCESS_SIZE, + size.bytes() + ); + + // Upgrade the success memory ordering to equal the failure ordering, since GenMC currently ignores the failure ordering. + // FIXME(genmc): remove this once GenMC properly supports the failure memory ordering. + let upgraded_success_ordering = + maybe_upgrade_compare_exchange_success_orderings(success, fail); + + if let Some(warning_cache) = &self.global_state.warning_cache { + // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. + let (effective_failure_ordering, _) = + upgraded_success_ordering.split_memory_orderings(); + // Return a warning if the actual orderings don't match the upgraded ones. + if success != upgraded_success_ordering || effective_failure_ordering != fail { + emit_warning(ecx, &warning_cache.compare_exchange_failure_ordering, || { + NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { + success_ordering: success, + upgraded_success_ordering, + failure_ordering: fail, + effective_failure_ordering, + } + }); + } + // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. + if can_fail_spuriously { + emit_warning(ecx, &warning_cache.compare_exchange_weak, || { + NonHaltingDiagnostic::GenmcCompareExchangeWeak + }); + } + } + + debug!( + "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, orderings: {fail:?}), can fail spuriously: {can_fail_spuriously}" + ); + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(ecx.machine.threads.active_thread()); + + let cas_result = self.handle.borrow_mut().pin_mut().handle_compare_exchange( + genmc_tid, + address.bytes(), + size.bytes(), + scalar_to_genmc_scalar(ecx, expected_old_value)?, + scalar_to_genmc_scalar(ecx, new_value)?, + scalar_to_genmc_scalar(ecx, old_value)?, + upgraded_success_ordering.to_genmc(), + fail.to_genmc(), + can_fail_spuriously, + ); + + if let Some(error) = cas_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; + debug!( + "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" + ); + // The write can only be a co-maximal write if the CAS succeeded. + assert!(cas_result.is_success || !cas_result.is_coherence_order_maximal_write); + interp_ok(( + return_scalar, + cas_result.is_coherence_order_maximal_write.then_some(new_value), + cas_result.is_success, + )) } /// Inform GenMC about a non-atomic memory load diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 24d13e6375b11..7e4ed816a76a1 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -23,7 +23,9 @@ pub fn run_genmc_mode<'tcx>( // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); + // FIXME(genmc): disable warnings in estimation mode once it is added. + let global_state = + Arc::new(GlobalState::new(tcx.target_usize_max(), /* print_warnings */ true)); let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); // `rep` is used to report the progress, Miri will panic on wrap-around. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 0842307caf9ab..15f7ccbabca6a 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -141,6 +141,13 @@ pub enum NonHaltingDiagnostic { ptr: Pointer, }, ExternTypeReborrow, + GenmcCompareExchangeWeak, + GenmcCompareExchangeOrderingMismatch { + success_ordering: AtomicRwOrd, + upgraded_success_ordering: AtomicRwOrd, + failure_ordering: AtomicReadOrd, + effective_failure_ordering: AtomicReadOrd, + }, } /// Level of Miri specific diagnostics @@ -637,6 +644,8 @@ impl<'tcx> MiriMachine<'tcx> { ("sharing memory with a native function".to_string(), DiagLevel::Warning), ExternTypeReborrow => ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning), + GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } => + ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning), CreatedPointerTag(..) | PoppedPointerTag(..) | CreatedAlloc(..) @@ -675,6 +684,23 @@ impl<'tcx> MiriMachine<'tcx> { format!("weak memory emulation: outdated value returned from load at {ptr}"), ExternTypeReborrow => format!("reborrow of a reference to `extern type` is not properly supported"), + GenmcCompareExchangeWeak => + "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures." + .to_string(), + GenmcCompareExchangeOrderingMismatch { + success_ordering, + upgraded_success_ordering, + failure_ordering, + effective_failure_ordering, + } => { + let was_upgraded_msg = if success_ordering != upgraded_success_ordering { + format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'") + } else { + assert_ne!(failure_ordering, effective_failure_ordering); + format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'") + }; + format!("GenMC currently does not model the failure ordering for `compare_exchange`. {was_upgraded_msg}. Miri with GenMC might miss bugs related to this memory access.") + } }; let notes = match &e { diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs new file mode 100644 index 0000000000000..32954a643b34b --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test `wrong/racy/MPU2+rels+rlx`. +// Test if Miri with GenMC can detect the data race on `X`. +// The data race only occurs if thread 1 finishes, then threads 3 and 4 run, then thread 2. +// +// This data race is hard to detect for Miri without GenMC, requiring -Zmiri-many-seeds=0..1024 at the time this test was created. + +// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use genmc::spawn_pthread_closure; +static mut X: u64 = 0; +static Y: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let _t1 = spawn_pthread_closure(|| { + X = 1; + Y.store(0, Release); + Y.store(1, Relaxed); + }); + let _t2 = spawn_pthread_closure(|| { + if Y.load(Relaxed) > 2 { + X = 2; //~ ERROR: Undefined Behavior: Non-atomic race + } + }); + let _t3 = spawn_pthread_closure(|| { + let _ = Y.compare_exchange(2, 3, Relaxed, Relaxed); + }); + + let _t4 = spawn_pthread_closure(|| { + let _ = Y.compare_exchange(1, 2, Relaxed, Relaxed); + }); + } + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr new file mode 100644 index 0000000000000..946d9a2124b88 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr new file mode 100644 index 0000000000000..a6c3ed7055e75 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr new file mode 100644 index 0000000000000..a6c3ed7055e75 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs new file mode 100644 index 0000000000000..4955dfd8a77ad --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs @@ -0,0 +1,57 @@ +//@ revisions: non_genmc genmc +//@[genmc] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche + +// This is the test `store_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs + +// This test shows the comparison between running Miri with or without GenMC. +// Without GenMC, Miri requires multiple iterations of the loop to detect the error. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // For normal Miri, we need multiple repetitions, but GenMC should find the bug with only 1. + const REPS: usize = if cfg!(non_genmc) { 128 } else { 1 }; + for _ in 0..REPS { + // New atomics every iterations, so they don't influence each other. + let x = AtomicUsize::new(0); + let y = AtomicUsize::new(0); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + x.store(0, Relaxed); + y.store(0, Relaxed); + + let mut a: usize = 1234; + let mut b: usize = 1234; + unsafe { + let ids = [ + spawn_pthread_closure(|| { + x.store(1, Relaxed); + a = y.load(Relaxed) + }), + spawn_pthread_closure(|| { + y.store(1, Relaxed); + b = x.load(Relaxed) + }), + ]; + join_pthreads(ids); + } + if (a, b) == (0, 0) { + std::process::abort(); //~ ERROR: abnormal termination + } + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs new file mode 100644 index 0000000000000..8a77d54a64f8b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs @@ -0,0 +1,70 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-ignore-leaks + +// Adapted from: `impl LazyKey`, `fn lazy_init`: rust/library/std/src/sys/thread_local/key/racy.rs +// Two threads race to initialize a key, which is just an index into an array in this test. +// The (Acquire, Release) orderings on the compare_exchange prevent any data races for reading from `VALUES[key]`. + +// FIXME(genmc): GenMC does not model the failure ordering of compare_exchange currently. +// Miri thus upgrades the success ordering to prevent showing any false data races in cases like this one. +// Once GenMC supports the failure ordering, this test should work without the upgrading. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +const KEY_SENTVAL: usize = usize::MAX; + +static KEY: AtomicUsize = AtomicUsize::new(KEY_SENTVAL); + +static mut VALUES: [usize; 2] = [0, 0]; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + KEY.store(KEY_SENTVAL, Relaxed); + + unsafe { + let mut a = 0; + let mut b = 0; + let ids = [ + spawn_pthread_closure(|| { + VALUES[0] = 42; + let key = get_or_init(0); + if key > 2 { + std::process::abort(); + } + a = VALUES[key]; + }), + spawn_pthread_closure(|| { + VALUES[1] = 1234; + let key = get_or_init(1); + if key > 2 { + std::process::abort(); + } + b = VALUES[key]; + }), + ]; + join_pthreads(ids); + if a != b { + std::process::abort(); + } + } + 0 +} + +fn get_or_init(key: usize) -> usize { + match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) { + // The CAS succeeded, so we've created the actual key. + Ok(_) => key, + // If someone beat us to the punch, use their key instead. + // The `Acquire` failure ordering means we synchronized with the `Release` compare_exchange in the thread that wrote the other key. + // We can thus read from `VALUES[other_key]` without a data race. + Err(other_key) => other_key, + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr new file mode 100644 index 0000000000000..d97b8b3d92f48 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr @@ -0,0 +1,22 @@ +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Release' was upgraded to 'AcqRel' to match failure ordering 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC + | +LL | match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `get_or_init` at tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC +note: inside closure + --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC + | +LL | let key = get_or_init(0); + | ^^^^^^^^^^^^^^ + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC}>` + --> tests/genmc/pass/atomics/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs new file mode 100644 index 0000000000000..e32c7cdf80c43 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test the basic functionality of compare_exchange. + +#![no_main] + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +static VALUE: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + VALUE.store(1, SeqCst); + + // Expect success: + if VALUE.compare_exchange(1, 2, SeqCst, SeqCst) != Ok(1) { + std::process::abort(); + } + // New value should be written: + if 2 != VALUE.load(SeqCst) { + std::process::abort() + } + + // Expect failure: + if VALUE.compare_exchange(1234, 42, SeqCst, SeqCst) != Err(2) { + std::process::abort(); + } + // Value should be unchanged: + if 2 != VALUE.load(SeqCst) { + std::process::abort() + } + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr new file mode 100644 index 0000000000000..3b22247ee44cb --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index 163556dcc8975..c81573d59d1da 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -57,9 +57,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - if writeln!(MiriStderr, "{results:?}").is_err() { - std::process::abort(); - } + writeln!(MiriStderr, "{results:?}").unwrap_or_else(|_| std::process::abort()); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs new file mode 100644 index 0000000000000..9bb156a99970f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -0,0 +1,67 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MPU2+rels+acqf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU2+rels+acqf/mpu2+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = Ok(1234); + let mut b = Ok(1234); + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + a = Y.compare_exchange(2, 3, Relaxed, Relaxed); + }), + spawn_pthread_closure(|| { + b = Y.compare_exchange(1, 2, Relaxed, Relaxed); + }), + spawn_pthread_closure(|| { + c = Y.load(Acquire); + if c > 2 { + std::sync::atomic::fence(Acquire); + X.store(2, Relaxed); + } + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + writeln!( + MiriStderr, + "X={}, Y={}, a={a:?}, b={b:?}, c={c}", + X.load(Relaxed), + Y.load(Relaxed) + ) + .unwrap_or_else(|_| std::process::abort()); + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr new file mode 100644 index 0000000000000..4551c00b0575d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr @@ -0,0 +1,38 @@ +X=1, Y=2, a=Err(1), b=Ok(1), c=2 +X=1, Y=2, a=Err(1), b=Ok(1), c=1 +X=1, Y=2, a=Err(1), b=Ok(1), c=0 +X=1, Y=2, a=Err(1), b=Ok(1), c=0 +X=2, Y=3, a=Ok(2), b=Ok(1), c=3 +X=1, Y=3, a=Ok(2), b=Ok(1), c=3 +X=1, Y=3, a=Ok(2), b=Ok(1), c=2 +X=1, Y=3, a=Ok(2), b=Ok(1), c=1 +X=1, Y=3, a=Ok(2), b=Ok(1), c=0 +X=1, Y=3, a=Ok(2), b=Ok(1), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=1 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=1 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=2 +X=1, Y=2, a=Err(0), b=Ok(1), c=1 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=2 +X=1, Y=2, a=Err(0), b=Ok(1), c=1 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index ac5c3724254d6..f96679b23a5cd 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -55,11 +55,8 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - if writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) - .is_err() - { - std::process::abort(); - } + writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) + .unwrap_or_else(|_| std::process::abort()); 0 } } diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs new file mode 100644 index 0000000000000..c376d96b111cc --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs @@ -0,0 +1,37 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/casdep". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let a = X.load(Relaxed); + let _b = Y.compare_exchange(a, 1, Relaxed, Relaxed); + Z.store(a, Relaxed); + }); + spawn_pthread_closure(|| { + Y.store(2, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr new file mode 100644 index 0000000000000..01701dfe6918a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs new file mode 100644 index 0000000000000..865895b4dcd96 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/ccr". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let expected = 0; + let _ = X.compare_exchange(expected, 42, Relaxed, Relaxed); + }); + spawn_pthread_closure(|| { + let expected = 0; + let _ = X.compare_exchange(expected, 17, Relaxed, Relaxed); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr new file mode 100644 index 0000000000000..01701dfe6918a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.rs b/src/tools/miri/tests/genmc/pass/litmus/cii.rs new file mode 100644 index 0000000000000..663c806e667c4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.rs @@ -0,0 +1,35 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/cii". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let expected = 1; + let _ = X.compare_exchange(expected, 2, Relaxed, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr new file mode 100644 index 0000000000000..be75e68fde77d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 From 68a3adad2ae1a979b29325b434967e1801145d42 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 Aug 2025 21:27:05 +0200 Subject: [PATCH 0344/1900] other clean-up --- .../src/functions/not_unsafe_ptr_arg_deref.rs | 4 ++-- clippy_lints/src/matches/manual_utils.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 23 ++++++++----------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 906bbd006d463..72f8795304055 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -4,7 +4,7 @@ use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; @@ -51,7 +51,7 @@ fn check_raw_ptr<'tcx>( let typeck = cx.tcx.typeck_body(body.id()); let _: Option = for_each_expr(cx, body.value, |e| { match e.kind { - hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { check_arg(cx, &raw_ptrs, arg); } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index dbae71bbb1b06..c5cb1ce1b3fd2 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -191,7 +191,7 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { Some(func) }, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad12..ae6a4ae7fb2d6 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -43,13 +43,8 @@ pub use type_certainty::expr_type_is_certain; /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`]. pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { cx.maybe_typeck_results() - .and_then(|results| { - if results.hir_owner == hir_ty.hir_id.owner { - results.node_type_opt(hir_ty.hir_id) - } else { - None - } - }) + .filter(|results| results.hir_owner == hir_ty.hir_id.owner) + .and_then(|results| results.node_type_opt(hir_ty.hir_id)) .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty)) } @@ -475,6 +470,11 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } +/// Returns `true` if `ty` denotes an `unsafe fn`. +pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() +} + /// Peels off all references on the type. Returns the underlying type, the number of references /// removed, and whether the pointer is ultimately mutable or not. pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { @@ -488,15 +488,10 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { f(ty, 0, Mutability::Mut) } -/// Returns `true` if the given type is an `unsafe` function. -pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() -} - /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), _ => ty, } } From 31c7e621c2067808f2a93942073e55971e4e9c3f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 Aug 2025 21:36:38 +0200 Subject: [PATCH 0345/1900] reorganize `peel_*_ty_refs` functions - give `ty::walk_ptrs_hir_ty` a more standard name `peel_hir_ty_refs_and_ptrs` - move it out of `ty`, since that's for `middle::ty::Ty` - remove `ty::walk_ptrs_ty_depth` for equivalent `peel_middle_ty_refs` - rename the latter into `ty::peel_and_count_ty_refs` - incorporate mutability tracking (from `ty::peel_mid_ty_refs_is_mutable`) into that --- clippy_lints/src/dereference.rs | 7 ++-- clippy_lints/src/manual_abs_diff.rs | 6 +-- .../src/manual_slice_size_calculation.rs | 3 +- clippy_lints/src/matches/manual_ok_err.rs | 16 +++----- clippy_lints/src/matches/manual_utils.rs | 7 ++-- clippy_lints/src/matches/single_match.rs | 8 ++-- clippy_lints/src/methods/implicit_clone.rs | 6 +-- .../src/methods/inefficient_to_string.rs | 4 +- clippy_lints/src/methods/mut_mutex_lock.rs | 5 +-- .../src/methods/unnecessary_to_owned.rs | 13 +++--- clippy_lints/src/methods/useless_asref.rs | 6 +-- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 8 ++-- clippy_lints/src/size_of_ref.rs | 5 ++- clippy_lints/src/unused_peekable.rs | 6 +-- clippy_utils/src/lib.rs | 13 +++--- clippy_utils/src/ty/mod.rs | 41 +++++-------------- 17 files changed, 66 insertions(+), 92 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9aa2f3cf0a5b7..9337816c80d9d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, - peel_middle_ty_refs, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -942,7 +941,7 @@ fn report<'tcx>( let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_middle_ty_refs(ty); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. @@ -1045,7 +1044,7 @@ fn report<'tcx>( if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() && dst.is_slice() { - let (src, n_src_refs) = peel_middle_ty_refs(ty); + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); if n_src_refs >= 2 && src.is_array() { return; } diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 288f27db8ca2c..5814b6815a1ea 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -4,8 +4,8 @@ use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, peel_blocks, peel_middle_ty_refs, span_contains_comment}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,7 +107,7 @@ impl ManualAbsDiff { |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); - let (b_ty, b_n_refs) = peel_middle_ty_refs(cx.typeck_results().expr_ty(b)); + let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); (a_ty == b_ty && (is_int(a_ty) || is_duration(a_ty))).then_some((a_ty, b_n_refs)) } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 0c09a47c96518..de12fa29d02cd 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -102,7 +103,7 @@ fn simplify_half<'tcx>( && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind && method_path.ident.name == sym::len && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty) + && let (receiver_ty, refs_count, _) = peel_and_count_ty_refs(receiver_ty) && let ty::Slice(ty1) = receiver_ty.kind() // expr2 is `size_of::()`? && let ExprKind::Call(func, []) = expr2.kind diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index edbb556fd9766..a8490d6aa7d82 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; @@ -135,15 +135,11 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); - let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty); - let prefix = if n_ref > 0 { - if mutability == Mutability::Mut { - ".as_mut()" - } else { - ".as_ref()" - } - } else { - "" + let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty); + let prefix = match mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", }; let sugg = format!("{scrut}{prefix}.{method}()"); diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index c5cb1ce1b3fd2..d4bfdb7e440d8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -30,8 +30,9 @@ pub(super) fn check_with<'tcx, F>( where F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); + let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b700703..83939d325794e 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -2,10 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; -use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::{Applicability, Diag}; @@ -133,7 +131,7 @@ fn report_single_pattern( let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Expr(_) = pat.kind - && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) + && let (ty, ty_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && (ty.is_integral() diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 8a976d1b4dc00..0ba84919395cc 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_middle_ty_refs(input_type) + && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4ed7de81ea3d0..47195fdd65f5f 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub fn check( && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) && let self_ty = args.type_at(0) - && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 && specializes_tostring(cx, deref_self_ty) { diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 4235af882b0c7..9d2c5e6232d65 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -10,8 +10,7 @@ use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) - && ref_depth >= 1 + && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c1f4904af7c48..640931a828998 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,11 +3,12 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, +}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, sym, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -119,8 +120,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) + && let (target_ty, n_target_refs, _) = peel_and_count_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs, _) = peel_and_count_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -385,7 +386,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_middle_ty_refs(*input) + && let (input, n_refs, _) = peel_and_count_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait() diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 38fad239f6799..e56f4b80d0172 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; @@ -50,8 +50,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + let (base_res_ty, res_depth, _) = peel_and_count_ty_refs(res_ty); + let (base_rcv_ty, rcv_depth, _) = peel_and_count_ty_refs(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { if let Some(parent) = get_parent_expr(cx, expr) { // allow the `as_ref` or `as_mut` if it is followed by another method call diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1d58cdd26d88d..de6766cbe94aa 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -263,7 +263,7 @@ fn is_call_with_ref_arg<'tcx>( && args.len() == 1 && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() - && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx)) + && let (inner_ty, 1, _) = peel_and_count_ty_refs(args[0].node.ty(mir, cx.tcx)) && !is_copy(cx, inner_ty) { Some((def_id, *local, inner_ty, destination.as_local()?)) diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 324a05cdcc0c8..a358eff2ce554 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix); diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 60d923bcd77e7..606e852aae9e3 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, peel_middle_ty_refs}; +use clippy_utils::path_def_id; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -59,7 +60,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_middle_ty_refs(arg_ty).1 > 1 + && peel_and_count_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 1f5351e32aacf..5224b62e9fc71 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && let Some(init) = local.init && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -211,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { true diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e8..5223cd872a680 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2380,15 +2380,12 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) } } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { - let mut count = 0; - while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() { - ty = *dest_ty; - count += 1; +/// Returns the base type for HIR references and pointers. +pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty), + _ => ty, } - (ty, count) } /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index ae6a4ae7fb2d6..5991c36c5fe34 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; +use rustc_hir::{Expr, FnDecl, LangItem, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -476,36 +476,17 @@ pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } /// Peels off all references on the type. Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } - } - f(ty, 0, Mutability::Mut) -} - -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match &ty.kind { - TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), - _ => ty, - } -} - -/// Returns the base type for references and raw pointers, and count reference -/// depth. -pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(*ty, depth + 1), - _ => (ty, depth), - } +/// removed, and, if there were any such references, whether the pointer is ultimately mutable or +/// not. +pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option) { + let mut count = 0; + let mut mutbl = None; + while let ty::Ref(_, dest_ty, m) = ty.kind() { + ty = *dest_ty; + count += 1; + mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m))); } - inner(ty, 0) + (ty, count, mutbl) } /// Returns `true` if types `a` and `b` are same types having same `Const` generic args, From 8938bb268d72deae0bb8a7a1eebe7563949ebc60 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 13:50:11 +0200 Subject: [PATCH 0346/1900] clean-up tests --- tests/ui/multiple_unsafe_ops_per_block.rs | 76 ++++++----- tests/ui/multiple_unsafe_ops_per_block.stderr | 124 +++++++++--------- 2 files changed, 99 insertions(+), 101 deletions(-) diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 016fd89a7b7ac..8f5408010dc7f 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,10 +1,6 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![allow(unused)] -#![allow(deref_nullptr)] -#![allow(clippy::unnecessary_operation)] -#![allow(dropping_copy_types)] -#![allow(clippy::assign_op_pattern)] +#![expect(clippy::unnecessary_operation, dropping_copy_types)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -105,17 +101,17 @@ fn correct3() { } } -// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) - -unsafe fn read_char_bad(ptr: *const u8) -> char { - unsafe { char::from_u32_unchecked(*ptr.cast::()) } - //~^ multiple_unsafe_ops_per_block -} +fn issue10064() { + unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } + //~^ multiple_unsafe_ops_per_block + } -// no lint -unsafe fn read_char_good(ptr: *const u8) -> char { - let int_value = unsafe { *ptr.cast::() }; - unsafe { core::char::from_u32_unchecked(int_value) } + // no lint + unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } + } } // no lint @@ -126,39 +122,41 @@ fn issue10259() { }); } -fn _fn_ptr(x: unsafe fn()) { - unsafe { - //~^ multiple_unsafe_ops_per_block - x(); - x(); +fn issue10367() { + fn fn_ptr(x: unsafe fn()) { + unsafe { + //~^ multiple_unsafe_ops_per_block + x(); + x(); + } } -} -fn _assoc_const() { - trait X { - const X: unsafe fn(); + fn assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + //~^ multiple_unsafe_ops_per_block + T::X(); + T::X(); + } + } } - fn _f() { + + fn field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); unsafe { //~^ multiple_unsafe_ops_per_block - T::X(); - T::X(); + x.0(); + x.0(); } } } -fn _field_fn_ptr(x: unsafe fn()) { - struct X(unsafe fn()); - let x = X(x); - unsafe { - //~^ multiple_unsafe_ops_per_block - x.0(); - x.0(); - } -} - -// await expands to an unsafe block with several operations, but this is fine.: #11312 -async fn await_desugaring_silent() { +// await expands to an unsafe block with several operations, but this is fine. +async fn issue11312() { async fn helper() {} helper().await; diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 3130cecc25250..8955321871708 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:34:5 | LL | / unsafe { LL | | @@ -9,12 +9,12 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:36:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:41:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | not_very_safe(); = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:48:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:44:5 | LL | / unsafe { LL | | @@ -32,18 +32,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:50:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:51:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:56:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | @@ -54,23 +54,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:58:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:59:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:60:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:66:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5 | LL | / unsafe { LL | | @@ -82,115 +82,115 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:68:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:69:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:70:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:71:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:72:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:73:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:9 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:18 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:43 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9 | -LL | / unsafe { +LL | / unsafe { LL | | -LL | | x(); -LL | | x(); -LL | | } - | |_____^ +LL | | x(); +LL | | x(); +LL | | } + | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:129:13 | -LL | x(); - | ^^^ +LL | x(); + | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:130:13 | -LL | x(); - | ^^^ +LL | x(); + | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:139:13 | -LL | / unsafe { +LL | / unsafe { LL | | -LL | | T::X(); -LL | | T::X(); -LL | | } - | |_________^ +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_____________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:141:17 | -LL | T::X(); - | ^^^^^^ +LL | T::X(); + | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:142:17 | -LL | T::X(); - | ^^^^^^ +LL | T::X(); + | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:150:9 | -LL | / unsafe { +LL | / unsafe { LL | | -LL | | x.0(); -LL | | x.0(); -LL | | } - | |_____^ +LL | | x.0(); +LL | | x.0(); +LL | | } + | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:155:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:152:13 | -LL | x.0(); - | ^^^^^ +LL | x.0(); + | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:153:13 | -LL | x.0(); - | ^^^^^ +LL | x.0(); + | ^^^^^ error: aborting due to 8 previous errors From da6d23b116471cdaccd60e8096c97cae259cc79e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 13:28:44 +0200 Subject: [PATCH 0347/1900] fix: ignore unsafe ops from `.await` desugaring --- .../src/multiple_unsafe_ops_per_block.rs | 8 ++ tests/ui/multiple_unsafe_ops_per_block.rs | 49 ++++++- tests/ui/multiple_unsafe_ops_per_block.stderr | 123 +++++++++++++----- 3 files changed, 149 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index c6c27e22b90e5..bc5e72270f4e3 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,3 +1,4 @@ +use clippy_utils::desugar_await; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; @@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>( ) { for_each_expr(cx, node, |expr| { match expr.kind { + // The `await` itself will desugar to two unsafe calls, but we should ignore those. + // Instead, check the expression that is `await`ed + _ if let Some(e) = desugar_await(expr) => { + collect_unsafe_exprs(cx, e, unsafe_ops); + return Continue(Descend::No); + }, + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), ExprKind::Field(e, _) => { diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 8f5408010dc7f..132673d5164ab 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,6 +1,10 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![expect(clippy::unnecessary_operation, dropping_copy_types)] +#![expect( + dropping_copy_types, + clippy::unnecessary_operation, + clippy::unnecessary_literal_unwrap +)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -162,4 +166,47 @@ async fn issue11312() { helper().await; } +async fn issue13879() { + async fn foo() {} + + // no lint: nothing unsafe beyond the `await` which we ignore + unsafe { + foo().await; + } + + // no lint: only one unsafe call beyond the `await` + unsafe { + not_very_safe(); + foo().await; + } + + // lint: two unsafe calls beyond the `await` + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + STATIC += 1; + foo().await; + } + + async unsafe fn foo_unchecked() {} + + // no lint: only one unsafe call in the `await`ed expr + unsafe { + foo_unchecked().await; + } + + // lint: one unsafe call in the `await`ed expr, and one outside + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + foo_unchecked().await; + } + + // lint: two unsafe calls in the `await`ed expr + unsafe { + //~^ multiple_unsafe_ops_per_block + Some(foo_unchecked()).unwrap_unchecked().await; + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 8955321871708..922a464c6b6ef 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:34:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5 | LL | / unsafe { LL | | @@ -9,12 +9,12 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:36:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:41:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | not_very_safe(); = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:44:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:48:5 | LL | / unsafe { LL | | @@ -32,18 +32,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:50:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:51:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:56:5 | LL | / unsafe { LL | | @@ -54,23 +54,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:58:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:59:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:60:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:66:5 | LL | / unsafe { LL | | @@ -82,55 +82,55 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:68:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:69:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:70:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:71:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:72:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:73:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:18 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:43 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9 | LL | / unsafe { LL | | @@ -140,18 +140,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:129:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13 | LL | x(); | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13 | LL | x(); | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:139:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13 | LL | / unsafe { LL | | @@ -161,18 +161,18 @@ LL | | } | |_____________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:141:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17 | LL | T::X(); | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17 | LL | T::X(); | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:150:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9 | LL | / unsafe { LL | | @@ -182,15 +182,78 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:152:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13 | LL | x.0(); | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13 | LL | x.0(); | ^^^^^ -error: aborting due to 8 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | STATIC += 1; +LL | | foo().await; +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | foo_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9 + | +LL | foo_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5 + | +LL | / unsafe { +LL | | +LL | | Some(foo_unchecked()).unwrap_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe method call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From 2e652d7d13ff9f1944b92a4f20248a61a96fe71c Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 11:34:54 -0500 Subject: [PATCH 0348/1900] Improve `core::fmt` coverage --- library/coretests/tests/fmt/builders.rs | 15 +++++++++++++ library/coretests/tests/fmt/mod.rs | 30 +++++++++++++++++++++++++ library/coretests/tests/fmt/num.rs | 6 +++++ library/coretests/tests/lib.rs | 1 + 4 files changed, 52 insertions(+) diff --git a/library/coretests/tests/fmt/builders.rs b/library/coretests/tests/fmt/builders.rs index ba4801f5912b8..156eebb1e9d2e 100644 --- a/library/coretests/tests/fmt/builders.rs +++ b/library/coretests/tests/fmt/builders.rs @@ -173,6 +173,21 @@ mod debug_struct { format!("{Bar:#?}") ); } + + #[test] + fn test_field_with() { + struct Foo; + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Foo") + .field_with("bar", |f| f.write_str("true")) + .field_with("baz", |f| f.write_str("false")) + .finish() + } + } + + assert_eq!("Foo {\n bar: true,\n baz: false,\n}", format!("{Foo:#?}")) + } } mod debug_tuple { diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index 16f116d25901c..586e890befe0c 100644 --- a/library/coretests/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -57,6 +57,36 @@ fn test_fmt_debug_of_raw_pointers() { check_fmt(vtable as *const dyn Debug, "Pointer { addr: ", ", metadata: DynMetadata("); } +#[test] +fn test_fmt_debug_of_mut_reference() { + let mut x: u32 = 0; + + assert_eq!(format!("{:?}", &mut x), "0"); +} + +#[test] +fn test_default_write_impls() { + use core::fmt::Write; + + struct Buf(String); + + impl Write for Buf { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.0.write_str(s) + } + } + + let mut buf = Buf(String::new()); + buf.write_char('a').unwrap(); + + assert_eq!(buf.0, "a"); + + let mut buf = Buf(String::new()); + buf.write_fmt(format_args!("a")).unwrap(); + + assert_eq!(buf.0, "a"); +} + #[test] fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); diff --git a/library/coretests/tests/fmt/num.rs b/library/coretests/tests/fmt/num.rs index 4c145b484ddd6..052b16db52147 100644 --- a/library/coretests/tests/fmt/num.rs +++ b/library/coretests/tests/fmt/num.rs @@ -323,3 +323,9 @@ fn test_format_debug_hex() { assert_eq!(format!("{:02x?}", b"Foo\0"), "[46, 6f, 6f, 00]"); assert_eq!(format!("{:02X?}", b"Foo\0"), "[46, 6F, 6F, 00]"); } + +#[test] +#[should_panic = "Formatting argument out of range"] +fn test_rt_width_too_long() { + let _ = format!("Hello {:width$}!", "x", width = u16::MAX as usize + 1); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1bdaa6965f64a..6f42f1832682a 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -31,6 +31,7 @@ #![feature(core_private_bignum)] #![feature(core_private_diy_float)] #![feature(cstr_display)] +#![feature(debug_closure_helpers)] #![feature(dec2flt)] #![feature(drop_guard)] #![feature(duration_constants)] From e513ecc939b61f9020c511e4fc4daff892725cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justus=20Fl=C3=BCgel?= Date: Sun, 7 Sep 2025 15:18:47 +0200 Subject: [PATCH 0349/1900] Fix `explicit_deref_methods` triggering from within proc macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Justus Flügel --- clippy_lints/src/dereference.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9337816c80d9d..f82ea9844ed62 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ - DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, + path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -260,6 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; self.skip_expr = skip_expr; + if is_from_proc_macro(cx, expr) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data, cx.typeck_results()); + } + return; + } + match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); From 8f84324ec9d66ed8329331b4ade07ab7f4b124fa Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Wed, 10 Sep 2025 07:04:37 +0200 Subject: [PATCH 0350/1900] autofix for `rest_pat_in_fully_bound_structs` --- .../matches/rest_pat_in_fully_bound_struct.rs | 10 ++- .../ui/rest_pat_in_fully_bound_structs.fixed | 61 +++++++++++++++++++ .../ui/rest_pat_in_fully_bound_structs.stderr | 18 +++++- 3 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 tests/ui/rest_pat_in_fully_bound_structs.fixed diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index ae09c2e87d6b7..1130d82ab78f9 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion() - && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind + && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(dotdot)) = pat.kind && let Some(def_id) = path.res.opt_def_id() && let ty = cx.tcx.type_of(def_id).instantiate_identity() && let ty::Adt(def, _) = ty.kind() @@ -15,14 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", |diag| { - diag.help("consider removing `..` from this binding"); + diag.span_suggestion_verbose( + dotdot, + "consider removing `..` from this binding", + "", + rustc_errors::Applicability::MachineApplicable, + ); }, ); } diff --git a/tests/ui/rest_pat_in_fully_bound_structs.fixed b/tests/ui/rest_pat_in_fully_bound_structs.fixed new file mode 100644 index 0000000000000..ac200b3b19b74 --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.fixed @@ -0,0 +1,61 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] +#![allow(clippy::struct_field_names)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } +} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.stderr b/tests/ui/rest_pat_in_fully_bound_structs.stderr index d048933ddb7bc..8a2da302b9ef2 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.stderr +++ b/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -4,9 +4,13 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]` +help: consider removing `..` from this binding + | +LL - A { a: 5, b: 42, c: "", .. } => {}, // Lint +LL + A { a: 5, b: 42, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:25:9 @@ -14,7 +18,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:32:9 @@ -22,7 +30,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: aborting due to 3 previous errors From dd4562a18f4a91e40ba701019af33ab80c1b2886 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 12 Sep 2025 14:31:08 -0400 Subject: [PATCH 0351/1900] tests: update new test to accept new lifetime format Same change as rust-lang/rust@258915a55539593423c3d4c30f7b741f65c56e51, just for a newly written test. --- tests/codegen-llvm/c-variadic-lifetime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/codegen-llvm/c-variadic-lifetime.rs b/tests/codegen-llvm/c-variadic-lifetime.rs index 5b2f8af18c817..c6d3602ef51a0 100644 --- a/tests/codegen-llvm/c-variadic-lifetime.rs +++ b/tests/codegen-llvm/c-variadic-lifetime.rs @@ -8,7 +8,7 @@ #[unsafe(no_mangle)] unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { - // CHECK: call void @llvm.lifetime.start.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 [0-9]+, )?}}ptr nonnull %args) // CHECK: call void @llvm.va_start.p0(ptr nonnull %args) let b = args.arg::(); @@ -17,5 +17,5 @@ unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { a + b + c // CHECK: call void @llvm.va_end.p0(ptr nonnull %args) - // CHECK: call void @llvm.lifetime.end.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 [0-9]+, )?}}ptr nonnull %args) } From 43d45ef2da29aa438c32b5c872bc7a367ef2a2ab Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Sep 2025 11:32:51 -0700 Subject: [PATCH 0352/1900] rustdoc-search: reduce async machinery in value lookups This commit is a mirrored change from stringdex that makes `at()` not always return a promise, which is fine because we can still `await` it. --- src/librustdoc/html/static/js/search.js | 8 +- src/librustdoc/html/static/js/stringdex.d.ts | 2 +- src/librustdoc/html/static/js/stringdex.js | 97 +++++++++++--------- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 3b84ae2bed060..339b101d47111 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1758,12 +1758,8 @@ class DocSearch { const l = crateNames.length; const names = []; for (let i = 0; i < l; ++i) { - names.push(crateNames.at(i).then(name => { - if (name === undefined) { - return ""; - } - return this.utf8decoder.decode(name); - })); + const name = await crateNames.at(i); + names.push(name === undefined ? "" : this.utf8decoder.decode(name)); } return Promise.all(names); } diff --git a/src/librustdoc/html/static/js/stringdex.d.ts b/src/librustdoc/html/static/js/stringdex.d.ts index 2eb1fdf95d845..71c6bfdf48145 100644 --- a/src/librustdoc/html/static/js/stringdex.d.ts +++ b/src/librustdoc/html/static/js/stringdex.d.ts @@ -29,7 +29,7 @@ declare namespace stringdex { */ interface DataColumn { isEmpty(id: number): boolean; - at(id: number): Promise; + at(id: number): Promise|Uint8Array|undefined; search(name: Uint8Array|string): Promise; searchLev(name: Uint8Array|string): AsyncGenerator; length: number, diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index b7f605a10351d..4c4659628778d 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -2261,7 +2261,7 @@ function loadDatabase(hooks) { this.hashes = hashes; this.emptyset = emptyset; this.name = name; - /** @type {{"hash": Uint8Array, "data": Promise?, "end": number}[]} */ + /** @type {{"hash": Uint8Array, "data": Uint8Array[]?, "end": number}[]} */ this.buckets = []; this.bucket_keys = []; const l = counts.length; @@ -2295,64 +2295,75 @@ function loadDatabase(hooks) { /** * Look up a cell by row ID. * @param {number} id - * @returns {Promise} + * @returns {Promise|Uint8Array|undefined} */ - async at(id) { + at(id) { if (this.emptyset.contains(id)) { - return Promise.resolve(EMPTY_UINT8); + return EMPTY_UINT8; } else { let idx = -1; while (this.bucket_keys[idx + 1] <= id) { idx += 1; } if (idx === -1 || idx >= this.bucket_keys.length) { - return Promise.resolve(undefined); + return undefined; } else { const start = this.bucket_keys[idx]; - const {hash, end} = this.buckets[idx]; + const bucket = this.buckets[idx]; let data = this.buckets[idx].data; if (data === null) { - const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( - this.name, - hash, - ); - // After the `await` resolves, another task might fill - // in the data. If so, we should use that. - data = this.buckets[idx].data; - if (data !== null) { - return (await data)[id - start]; - } - const dataSansEmptyset = [...dataSansEmptysetOrig]; - /** @type {(Uint8Array[])|null} */ - let dataWithEmptyset = null; - let pos = start; - let insertCount = 0; - while (pos < end) { - if (this.emptyset.contains(pos)) { - if (dataWithEmptyset === null) { - dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); - } else if (insertCount !== 0) { - dataWithEmptyset.push( - ...dataSansEmptyset.splice(0, insertCount), - ); - } - insertCount = 0; - dataWithEmptyset.push(EMPTY_UINT8); - } else { - insertCount += 1; - } - pos += 1; - } - data = Promise.resolve( - dataWithEmptyset === null ? - dataSansEmptyset : - dataWithEmptyset.concat(dataSansEmptyset), + return this.atAsyncFetch(id, start, bucket); + } else { + return data[id - start]; + } + } + } + } + /** + * Look up a cell by row ID. + * @param {number} id + * @param {number} start + * @param {{hash: Uint8Array, data: Uint8Array[] | null, end: number}} bucket + * @returns {Promise} + */ + async atAsyncFetch(id, start, bucket) { + const {hash, end} = bucket; + const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( + this.name, + hash, + ); + // After the `await` resolves, another task might fill + // in the data. If so, we should use that. + let data = bucket.data; + if (data !== null) { + return data[id - start]; + } + const dataSansEmptyset = [...dataSansEmptysetOrig]; + /** @type {(Uint8Array[])|null} */ + let dataWithEmptyset = null; + let pos = start; + let insertCount = 0; + while (pos < end) { + if (this.emptyset.contains(pos)) { + if (dataWithEmptyset === null) { + dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); + } else if (insertCount !== 0) { + dataWithEmptyset.push( + ...dataSansEmptyset.splice(0, insertCount), ); - this.buckets[idx].data = data; } - return (await data)[id - start]; + insertCount = 0; + dataWithEmptyset.push(EMPTY_UINT8); + } else { + insertCount += 1; } + pos += 1; } + data = dataWithEmptyset === null ? + dataSansEmptyset : + dataWithEmptyset.concat(dataSansEmptyset); + bucket.data = data; + return data[id - start]; } /** * Search by exact substring From afe8f81c73244bcc33be39c9ea0f90f090ac14ce Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:35:20 -0500 Subject: [PATCH 0353/1900] Improve `core::num` coverage --- library/coretests/tests/num/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 54e54f734f647..913f766ec1683 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -111,6 +111,13 @@ fn from_str_issue7588() { assert_eq!(s, None); } +#[test] +#[should_panic = "radix must lie in the range `[2, 36]`"] +fn from_ascii_radix_panic() { + let radix = 1; + let _parsed = u64::from_str_radix("12345ABCD", radix); +} + #[test] fn test_int_from_str_overflow() { test_parse::("127", Ok(127)); From 87ae4dbf5cc1e81db845c659583aa489d532ae8c Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:41:26 -0500 Subject: [PATCH 0354/1900] Improve `core::ptr` coverage --- library/coretests/tests/ptr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index f8774833d0a54..4d5138d539b95 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -489,6 +489,14 @@ fn is_aligned() { assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8)); } +#[test] +#[should_panic = "is_aligned_to: align is not a power-of-two"] +fn invalid_is_aligned() { + let data = 42; + let ptr: *const i32 = &data; + assert!(ptr.is_aligned_to(3)); +} + #[test] fn offset_from() { let mut a = [0; 5]; From 4d273c4d91fc31b02c345f0dadea4a0b751f0238 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 12 Sep 2025 20:45:34 +0100 Subject: [PATCH 0355/1900] Fix two typos spotted in review --- src/doc/rustc/src/platform-support/arm-linux.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc/src/platform-support/arm-linux.md b/src/doc/rustc/src/platform-support/arm-linux.md index 5f320762e9378..5f40743f3d070 100644 --- a/src/doc/rustc/src/platform-support/arm-linux.md +++ b/src/doc/rustc/src/platform-support/arm-linux.md @@ -108,7 +108,7 @@ running this code on. For example, running `eabihf` code on an `eabi` system will not work correctly. The `gnueabi` ABI component indicates support for using the GNU C Library -(glibc), and the Arm Embedded ABI (EABI). The EABI is a replacement of for the +(glibc), and the Arm Embedded ABI (EABI). The EABI is a replacement for the original ABI (now called the Old ABI or OABI), and it is the standard ABI for 32-bit Arm systems. With this ABI, function parameters that are `f32` or `f64` are passed as if they were integers, instead of being passed via in FPU @@ -128,9 +128,9 @@ depending on whether your distribution is using 'soft-float' (EABI) or The `musleabi` and `musleabihf` ABI components offer support for the [musl C library](https://musl.libc.org/). This C library can be used to create 'static -binaries' that have no run-time library requirements (a feature that that -glibc does not support). There are soft-float (`eabi`) and hard-float -(`eabihf`) variants, as per the `gnu*` targets above. +binaries' that have no run-time library requirements (a feature that glibc +does not support). There are soft-float (`eabi`) and hard-float (`eabihf`) +variants, as per the `gnu*` targets above. The `uclibceabi` and `uclibceabihf` ABI components are for the [uClibc-ng C library](https://uclibc-ng.org/). This is sometimes used in light-weight From 8d5078e0490f4a07afc86af60fd78f1f6dabdb4b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 15 Aug 2025 16:12:35 -0500 Subject: [PATCH 0356/1900] Introduce trait_item_of --- clippy_lints/src/functions/renamed_function_params.rs | 10 ++-------- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/use_self.rs | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index f8e8f5544b993..e25611d48817a 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::hir_id::OwnerId; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; @@ -19,7 +18,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored of_trait: Some(of_trait), .. }) = &parent_item.kind - && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) + && let Some(did) = cx.tcx.trait_item_of(item.owner_id) && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits) { let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); @@ -87,11 +86,6 @@ impl RenamedFnArgs { } } -/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. -fn trait_item_def_id_of_impl(cx: &LateContext<'_>, target: OwnerId) -> Option { - cx.tcx.associated_item(target).trait_item_def_id -} - fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { of_trait .trait_def_id() diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 809a6728e1280..c4cad592e3675 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id + && let Some(trait_item_id) = cx.tcx.trait_item_of(owner_id) { ( trait_item_id, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aeda864b7eb5e..8252e6d48694b 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -151,8 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id + .trait_item_of(impl_item.owner_id) .expect("impl method matches a trait method"); let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig); From d419d51f934f8ab1fcdb82d6a446818d7d626aa0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 14:14:37 -0500 Subject: [PATCH 0357/1900] Rename AssocItemContainer -> AssocContainer --- clippy_lints/src/missing_inline.rs | 6 +++--- clippy_utils/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 28555a6109001..490a59ca66d3e 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -3,7 +3,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocItemContainer; +use rustc_middle::ty::AssocContainer; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { - AssocItemContainer::Trait => Some(container_id), - AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::Trait => Some(container_id), + AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e8..b806e3d8b1bf5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3250,8 +3250,8 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let assoc_item = tcx.associated_item(method); let def_id = assoc_item.container_id(tcx); match assoc_item.container { - rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocItemContainer::Impl => { + rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), + rustc_ty::AssocContainer::Impl => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, From d734981f14d934fd0ffa55ad6a74688227899c5e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 5 Aug 2025 15:59:44 -0500 Subject: [PATCH 0358/1900] Introduce hir::ImplItemImplKind --- clippy_lints/src/disallowed_macros.rs | 6 +++-- clippy_lints/src/manual_async_fn.rs | 12 ++++++--- clippy_lints/src/min_ident_chars.rs | 6 ++--- clippy_lints/src/missing_const_for_fn.rs | 18 ++++++++++--- clippy_utils/src/check_proc_macro.rs | 32 ++++++++++++------------ 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 23e7c7251cf15..49cd2671dc003 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -177,7 +177,9 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { self.check(cx, item.span, None); - self.check(cx, item.vis_span, None); + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind { + self.check(cx, vis_span, None); + } } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index ba1ad599e1169..bee3b19b597c0 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, - FnRetTy, GenericBound, ImplItem, Item, Node, OpaqueTy, TraitRef, Ty, TyKind, + FnRetTy, GenericBound, Node, OpaqueTy, TraitRef, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; @@ -60,8 +60,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { && let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(closure_body) = desugared_async_block(cx, block) - && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = - cx.tcx.hir_node_by_def_id(fn_def_id) + && let Some(vis_span_opt) = match cx.tcx.hir_node_by_def_id(fn_def_id) { + Node::Item(item) => Some(Some(item.vis_span)), + Node::ImplItem(impl_item) => Some(impl_item.vis_span()), + _ => None, + } && !span.from_expansion() { let header_span = span.with_hi(ret_ty.span.hi()); @@ -72,7 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { header_span, "this function can be simplified using the `async fn` syntax", |diag| { - if let Some(vis_snip) = vis_span.get_source_text(cx) + if let Some(vis_span) = vis_span_opt + && let Some(vis_snip) = vis_span.get_source_text(cx) && let Some(header_snip) = header_span.get_source_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index dbce29a86318c..6258e408217fc 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; use rustc_hir::{ - GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, - UsePath, + GenericParamKind, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, + PatKind, TraitItem, UsePath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -256,7 +256,7 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo } fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { - if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + if let ImplItemImplKind::Trait { trait_item_def_id: Ok(trait_item_def_id), .. } = impl_item.impl_kind { let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); let ImplItemKind::Fn(_, body_id) = impl_item.kind else { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index a6be7581c9a38..a63ad97862627 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -158,13 +158,23 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) - && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = - cx.tcx.hir_node_by_def_id(def_id) + && let node = cx.tcx.hir_node_by_def_id(def_id) + && let Some((item_span, vis_span_opt)) = match node { + hir::Node::Item(item) => Some((item.span, Some(item.vis_span))), + hir::Node::ImplItem(impl_item) => Some((impl_item.span, impl_item.vis_span())), + _ => None, + } { - let suggestion = if vis_span.is_empty() { "const " } else { " const" }; + let (sugg_span, suggestion) = if let Some(vis_span) = vis_span_opt + && !vis_span.is_empty() + { + (vis_span.shrink_to_hi(), " const") + } else { + (item_span.shrink_to_lo(), "const ") + }; span_lint_and_then(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`", |diag| { diag.span_suggestion_verbose( - vis_span.shrink_to_hi(), + sugg_span, "make the function `const`", suggestion, Applicability::MachineApplicable, diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c4a759e919b1b..1a25c90d735e4 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -19,7 +19,7 @@ use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, + ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; @@ -280,16 +280,17 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { } fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { - let (start_pat, end_pat) = match &item.kind { + let (mut start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; - if item.vis_span.is_empty() { - (start_pat, end_pat) - } else { - (Pat::Str("pub"), end_pat) - } + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind + && !vis_span.is_empty() + { + start_pat = Pat::Str("pub"); + }; + (start_pat, end_pat) } fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) { @@ -313,21 +314,20 @@ fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) { } fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) { - let (start_pat, end_pat) = match kind { + let (mut start_pat, end_pat) = match kind { FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")), FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")), FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1), }; - let start_pat = match tcx.hir_node(hir_id) { - Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => { - if vis_span.is_empty() { - start_pat - } else { - Pat::Str("pub") + match tcx.hir_node(hir_id) { + Node::Item(Item { vis_span, .. }) + | Node::ImplItem(ImplItem { impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, .. }) => { + if !vis_span.is_empty() { + start_pat = Pat::Str("pub") } }, - Node::TraitItem(_) => start_pat, - _ => Pat::Str(""), + Node::ImplItem(_) | Node::TraitItem(_) => {}, + _ => start_pat = Pat::Str(""), }; (start_pat, end_pat) } From 324c2141227b3e7d40b1250126396637bb7c530e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 15:24:19 -0500 Subject: [PATCH 0359/1900] Split AssocContainer::{InherentImpl,TraitImpl} --- clippy_lints/src/missing_doc.rs | 11 +++++------ clippy_lints/src/missing_inline.rs | 3 ++- clippy_lints/src/missing_trait_methods.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 7772051eb5c61..39b5964bd87b1 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -16,7 +16,7 @@ use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::Visibility; +use rustc_middle::ty::{AssocContainer, Visibility}; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::symbol::kw; @@ -246,12 +246,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. - if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { - if cx.tcx.impl_trait_ref(cid).is_some() { + match cx.tcx.associated_item(impl_item.owner_id).container { + AssocContainer::Trait | AssocContainer::TraitImpl(_) => { note_prev_span_then_ret!(self.prev_span, impl_item.span); - } - } else { - note_prev_span_then_ret!(self.prev_span, impl_item.span); + }, + AssocContainer::InherentImpl => {} } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 490a59ca66d3e..6323e728666ce 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -167,7 +167,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { AssocContainer::Trait => Some(container_id), - AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::TraitImpl(_) => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::InherentImpl => None, }; if let Some(trait_def_id) = trait_def_id diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 9cc93bf06531a..8e9400e9d583d 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { .tcx .associated_items(item.owner_id) .in_definition_order() - .filter_map(|assoc_item| assoc_item.trait_item_def_id) + .filter_map(|assoc_item| assoc_item.expect_trait_impl().ok()) .collect(); for assoc in cx diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b806e3d8b1bf5..de45d65816628 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3251,7 +3251,7 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let def_id = assoc_item.container_id(tcx); match assoc_item.container { rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocContainer::Impl => { + rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, From 277e845162299d533ce64a2a6dfa540d4f36fb30 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Sep 2025 11:36:38 -0700 Subject: [PATCH 0360/1900] rustdoc-search: delay loading type name IDs until type search This avoids blocking on these lookups, so name-based searches return results more quickly. --- src/librustdoc/html/static/js/rustdoc.d.ts | 25 +- src/librustdoc/html/static/js/search.js | 296 +++++++++++++++------ src/librustdoc/html/static/js/stringdex.js | 2 +- 3 files changed, 233 insertions(+), 90 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 938ccc7d2c32e..951eb2291b89e 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -348,7 +348,7 @@ declare namespace rustdoc { returned: rustdoc.QueryElement[], is_alias: boolean, alias?: string, - original?: rustdoc.Rlow, + item: rustdoc.Row, } /** @@ -533,4 +533,27 @@ declare namespace rustdoc { * Generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] + + /** + * Each of these identifiers are used specially by + * type-driven search. Most of them are lang items + * in the compiler. + */ + type TypeNameIds = { + "typeNameIdOfOutput": number, + "typeNameIdOfFnPtr": number, + "typeNameIdOfFn": number, + "typeNameIdOfFnMut": number, + "typeNameIdOfFnOnce": number, + "typeNameIdOfArray": number, + "typeNameIdOfSlice": number, + "typeNameIdOfArrayOrSlice": number, + "typeNameIdOfTuple": number, + "typeNameIdOfUnit": number, + "typeNameIdOfTupleOrUnit": number, + "typeNameIdOfReference": number, + "typeNameIdOfPointer": number, + "typeNameIdOfHof": number, + "typeNameIdOfNever": number, + }; } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 339b101d47111..482134933a61e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1190,17 +1190,6 @@ class DocSearch { this.rootPath = rootPath; this.database = database; - this.typeNameIdOfOutput = -1; - this.typeNameIdOfArray = -1; - this.typeNameIdOfSlice = -1; - this.typeNameIdOfArrayOrSlice = -1; - this.typeNameIdOfTuple = -1; - this.typeNameIdOfUnit = -1; - this.typeNameIdOfTupleOrUnit = -1; - this.typeNameIdOfReference = -1; - this.typeNameIdOfPointer = -1; - this.typeNameIdOfHof = -1; - this.utf8decoder = new TextDecoder(); /** @type {Map} */ @@ -1208,14 +1197,49 @@ class DocSearch { } /** - * Load search index. If you do not call this function, `execQuery` - * will never fulfill. + * Load type name ID set. + * + * Each of these identifiers are used specially by + * type-driven search. Most of them are lang items + * in the compiler. + * + * Use this function, which caches the result, and not + * getTypeNameIdsAsync, which is an internal implementation + * detail for this. + * + * @return {Promise|rustdoc.TypeNameIds} */ - async buildIndex() { + getTypeNameIds() { + if (this.typeNameIds) { + return this.typeNameIds; + } const nn = this.database.getData("normalizedName"); if (!nn) { - return; + return { + typeNameIdOfOutput: -1, + typeNameIdOfFnPtr: -1, + typeNameIdOfFn: -1, + typeNameIdOfFnMut: -1, + typeNameIdOfFnOnce: -1, + typeNameIdOfArray: -1, + typeNameIdOfSlice: -1, + typeNameIdOfArrayOrSlice: -1, + typeNameIdOfTuple: -1, + typeNameIdOfUnit: -1, + typeNameIdOfTupleOrUnit: -1, + typeNameIdOfReference: -1, + typeNameIdOfPointer: -1, + typeNameIdOfHof: -1, + typeNameIdOfNever: -1, + }; } + return this.getTypeNameIdsAsync(nn); + } + /** + * @param {stringdex.DataColumn} nn + * @returns {Promise} + */ + async getTypeNameIdsAsync(nn) { // Each of these identifiers are used specially by // type-driven search. const [ @@ -1274,21 +1298,39 @@ class DocSearch { } return -1; }; - this.typeNameIdOfOutput = await first(output, TY_ASSOCTYPE, ""); - this.typeNameIdOfFnPtr = await first(fn, TY_PRIMITIVE, ""); - this.typeNameIdOfFn = await first(fn, TY_TRAIT, "core::ops"); - this.typeNameIdOfFnMut = await first(fnMut, TY_TRAIT, "core::ops"); - this.typeNameIdOfFnOnce = await first(fnOnce, TY_TRAIT, "core::ops"); - this.typeNameIdOfArray = await first(array, TY_PRIMITIVE, ""); - this.typeNameIdOfSlice = await first(slice, TY_PRIMITIVE, ""); - this.typeNameIdOfArrayOrSlice = await first(arrayOrSlice, TY_PRIMITIVE, ""); - this.typeNameIdOfTuple = await first(tuple, TY_PRIMITIVE, ""); - this.typeNameIdOfUnit = await first(unit, TY_PRIMITIVE, ""); - this.typeNameIdOfTupleOrUnit = await first(tupleOrUnit, TY_PRIMITIVE, ""); - this.typeNameIdOfReference = await first(reference, TY_PRIMITIVE, ""); - this.typeNameIdOfPointer = await first(pointer, TY_PRIMITIVE, ""); - this.typeNameIdOfHof = await first(hof, TY_PRIMITIVE, ""); - this.typeNameIdOfNever = await first(never, TY_PRIMITIVE, ""); + const typeNameIdOfOutput = await first(output, TY_ASSOCTYPE, ""); + const typeNameIdOfFnPtr = await first(fn, TY_PRIMITIVE, ""); + const typeNameIdOfFn = await first(fn, TY_TRAIT, "core::ops"); + const typeNameIdOfFnMut = await first(fnMut, TY_TRAIT, "core::ops"); + const typeNameIdOfFnOnce = await first(fnOnce, TY_TRAIT, "core::ops"); + const typeNameIdOfArray = await first(array, TY_PRIMITIVE, ""); + const typeNameIdOfSlice = await first(slice, TY_PRIMITIVE, ""); + const typeNameIdOfArrayOrSlice = await first(arrayOrSlice, TY_PRIMITIVE, ""); + const typeNameIdOfTuple = await first(tuple, TY_PRIMITIVE, ""); + const typeNameIdOfUnit = await first(unit, TY_PRIMITIVE, ""); + const typeNameIdOfTupleOrUnit = await first(tupleOrUnit, TY_PRIMITIVE, ""); + const typeNameIdOfReference = await first(reference, TY_PRIMITIVE, ""); + const typeNameIdOfPointer = await first(pointer, TY_PRIMITIVE, ""); + const typeNameIdOfHof = await first(hof, TY_PRIMITIVE, ""); + const typeNameIdOfNever = await first(never, TY_PRIMITIVE, ""); + this.typeNameIds = { + typeNameIdOfOutput, + typeNameIdOfFnPtr, + typeNameIdOfFn, + typeNameIdOfFnMut, + typeNameIdOfFnOnce, + typeNameIdOfArray, + typeNameIdOfSlice, + typeNameIdOfArrayOrSlice, + typeNameIdOfTuple, + typeNameIdOfUnit, + typeNameIdOfTupleOrUnit, + typeNameIdOfReference, + typeNameIdOfPointer, + typeNameIdOfHof, + typeNameIdOfNever, + }; + return this.typeNameIds; } /** @@ -1816,6 +1858,9 @@ class DocSearch { modulePathData, exactModuleName, exactModulePathData, + parentName, + parentPath, + crate, ] = await Promise.all([ entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null, entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null, @@ -1825,6 +1870,13 @@ class DocSearch { entry && entry.exactModulePath !== null ? this.getPathData(entry.exactModulePath) : null, + entry && entry.parent !== null ? + this.getName(entry.parent) : + null, + entry && entry.parent !== null ? + this.getPathData(entry.parent) : + null, + entry ? nonnull(await this.getName(entry.krate)) : "", ]); const name = name_ === null ? "" : name_; const normalizedName = (name.indexOf("_") === -1 ? @@ -1834,12 +1886,9 @@ class DocSearch { (modulePathData.modulePath === "" ? moduleName : `${modulePathData.modulePath}::${moduleName}`); - const [parentName, parentPath] = entry !== null && entry.parent !== null ? - await Promise.all([this.getName(entry.parent), this.getPathData(entry.parent)]) : - [null, null]; return { id, - crate: entry ? nonnull(await this.getName(entry.krate)) : "", + crate, ty: entry ? entry.ty : nonnull(path).ty, name, normalizedName, @@ -2144,6 +2193,7 @@ class DocSearch { * @returns {Promise} */ const formatDisplayTypeSignature = async(obj, typeInfo, elems, returned) => { + const typeNameIds = await this.getTypeNameIds(); const objType = obj.type; if (!objType) { return {type: [], mappedNames: new Map(), whereClause: new Map()}; @@ -2169,10 +2219,12 @@ class DocSearch { return true; }, 0, + typeNameIds, ); return !!fnOutput; }, 0, + typeNameIds, ); } else { const highlighted = unifyFunctionTypes( @@ -2185,6 +2237,7 @@ class DocSearch { return true; }, 0, + typeNameIds, ); if (typeInfo === "elems") { fnInputs = highlighted; @@ -2264,7 +2317,7 @@ class DocSearch { * @returns {Promise} */ const writeHof = async(fnType, result) => { - const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; + const hofOutput = fnType.bindings.get(typeNameIds.typeNameIdOfOutput) || []; const hofInputs = fnType.generics; pushText(fnType, result); pushText({name: " (", highlighted: false}, result); @@ -2305,11 +2358,14 @@ class DocSearch { * @returns {Promise} */ const writeSpecialPrimitive = async(fnType, result) => { - if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || - fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) { + if (fnType.id === typeNameIds.typeNameIdOfArray || + fnType.id === typeNameIds.typeNameIdOfSlice || + fnType.id === typeNameIds.typeNameIdOfTuple || + fnType.id === typeNameIds.typeNameIdOfUnit + ) { const [ob, sb] = - fnType.id === this.typeNameIdOfArray || - fnType.id === this.typeNameIdOfSlice ? + fnType.id === typeNameIds.typeNameIdOfArray || + fnType.id === typeNameIds.typeNameIdOfSlice ? ["[", "]"] : ["(", ")"]; pushText({ name: ob, highlighted: fnType.highlighted }, result); @@ -2321,7 +2377,7 @@ class DocSearch { ); pushText({ name: sb, highlighted: fnType.highlighted }, result); return true; - } else if (fnType.id === this.typeNameIdOfReference) { + } else if (fnType.id === typeNameIds.typeNameIdOfReference) { pushText({ name: "&", highlighted: fnType.highlighted }, result); let prevHighlighted = false; await onEachBtwnAsync( @@ -2337,7 +2393,7 @@ class DocSearch { }, result), ); return true; - } else if (fnType.id === this.typeNameIdOfPointer) { + } else if (fnType.id === typeNameIds.typeNameIdOfPointer) { pushText({ name: "*", highlighted: fnType.highlighted }, result); if (fnType.generics.length < 2) { pushText({ name: "const ", highlighted: fnType.highlighted }, result); @@ -2357,14 +2413,14 @@ class DocSearch { ); return true; } else if ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr ) { await writeHof(fnType, result); return true; - } else if (fnType.id === this.typeNameIdOfNever) { + } else if (fnType.id === typeNameIds.typeNameIdOfNever) { pushText({ name: "!", highlighted: fnType.highlighted }, result); return true; } @@ -2422,10 +2478,10 @@ class DocSearch { return; } } else if (fnType.ty === TY_TRAIT && ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr )) { await writeHof(fnType, result); return; @@ -2536,7 +2592,7 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[rustdoc.PlainResultObject, rustdoc.Row][]} results + * @param {rustdoc.PlainResultObject[]} results * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {Set} duplicates * @returns {rustdoc.ResultObject[]} @@ -2544,7 +2600,8 @@ class DocSearch { const transformResults = (results, typeInfo, duplicates) => { const out = []; - for (const [result, item] of results) { + for (const result of results) { + const item = result.item; if (item.id !== -1) { const res = buildHrefAndPath(item); // many of these properties don't strictly need to be @@ -2629,7 +2686,7 @@ class DocSearch { const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); const isMixedCase = normalizedUserQuery !== userQuery; /** - * @type {[rustdoc.PlainResultObject, rustdoc.Row][]} + * @type {rustdoc.PlainResultObject[]} */ const result_list = []; for (const result of results.values()) { @@ -2637,23 +2694,22 @@ class DocSearch { continue; } /** - * @type {rustdoc.Row?} + * @type {rustdoc.Row} */ - const item = await this.getRow(result.id, typeInfo !== null); - if (!item) { - continue; - } + const item = result.item; if (filterCrates !== null && item.crate !== filterCrates) { continue; } if (item) { - result_list.push([result, item]); + result_list.push(result); } else { continue; } } - result_list.sort(([aaa, aai], [bbb, bbi]) => { + result_list.sort((aaa, bbb) => { + const aai = aaa.item; + const bbi = bbb.item; /** @type {number} */ let a; /** @type {number} */ @@ -2800,6 +2856,7 @@ class DocSearch { * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {rustdoc.HighlightedFunctionType[]|null} * - Returns highlighted results if a match, null otherwise. @@ -2811,6 +2868,7 @@ class DocSearch { mgensIn, solutionCb, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return null; @@ -2833,7 +2891,12 @@ class DocSearch { && queryElems[0].bindings.size === 0) { const queryElem = queryElems[0]; for (const [i, fnType] of fnTypesIn.entries()) { - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { continue; } if (fnType.id !== null && @@ -2869,6 +2932,7 @@ class DocSearch { mgens ? new Map(mgens) : null, solutionCb, unboxingDepth, + typeNameIds, ) || fnType.generics, }); return highlighted; @@ -2881,6 +2945,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { continue; } @@ -2894,6 +2959,7 @@ class DocSearch { mgens, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { const highlighted = [...fnTypesIn]; @@ -2912,6 +2978,7 @@ class DocSearch { mgens ? new Map(mgens) : null, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { const highlighted = [...fnTypesIn]; @@ -2956,7 +3023,12 @@ class DocSearch { let queryElemsTmp = null; for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { continue; } let mgensScratch; @@ -3000,6 +3072,7 @@ class DocSearch { whereClause, mgensScratch, unboxingDepth, + typeNameIds, ); if (!solution) { return false; @@ -3013,6 +3086,7 @@ class DocSearch { simplifiedMgens, solutionCb, unboxingDepth, + typeNameIds, ); if (unifiedGenerics !== null) { unifiedGenericsMgens = simplifiedMgens; @@ -3022,6 +3096,7 @@ class DocSearch { return false; }, unboxingDepth, + typeNameIds, ); if (passesUnification) { passesUnification.length = fl; @@ -3038,6 +3113,7 @@ class DocSearch { unifiedGenericsMgens, solutionCb, unboxingDepth, + typeNameIds, // @ts-expect-error ) : unifiedGenerics.splice(0, v.length)]; })), @@ -3057,6 +3133,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { continue; } @@ -3073,6 +3150,7 @@ class DocSearch { mgens, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (passesUnification) { const highlightedGenerics = passesUnification.slice( @@ -3113,6 +3191,7 @@ class DocSearch { * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {rustdoc.HighlightedFunctionType[]|null} * - Returns highlighted results if a match, null otherwise. @@ -3124,6 +3203,7 @@ class DocSearch { mgensIn, solutionCb, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return null; @@ -3141,7 +3221,12 @@ class DocSearch { } const fnType = fnTypesIn[0]; const queryElem = queryElems[0]; - if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { if (fnType.id !== null && fnType.id < 0 && queryElem.id !== null && @@ -3159,6 +3244,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (fnTypesRemaining) { const highlighted = [fnType, ...fnTypesRemaining]; @@ -3185,6 +3271,7 @@ class DocSearch { whereClause, mgensScratch, unboxingDepth, + typeNameIds, ); if (!solution) { return false; @@ -3198,6 +3285,7 @@ class DocSearch { simplifiedMgens, solutionCb, unboxingDepth, + typeNameIds, ); if (unifiedGenerics !== null) { return true; @@ -3205,6 +3293,7 @@ class DocSearch { } }, unboxingDepth, + typeNameIds, ); if (fnTypesRemaining) { const highlighted = [fnType, ...fnTypesRemaining]; @@ -3223,6 +3312,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { let highlightedRemaining; if (fnType.id !== null && fnType.id < 0) { @@ -3244,6 +3334,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (hl) { highlightedRemaining = hl; @@ -3251,6 +3342,7 @@ class DocSearch { return hl; }, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { return [Object.assign({ @@ -3278,6 +3370,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (hl) { highlightedRemaining = hl; @@ -3285,6 +3378,7 @@ class DocSearch { return hl; }, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { return [Object.assign({}, fnType, { @@ -3310,10 +3404,11 @@ class DocSearch { * * @param {rustdoc.FunctionType} fnType * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.TypeNameIds} typeNameIds * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ - const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn) => { + const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn, typeNameIds) => { // type filters look like `trait:Read` or `enum:Result` if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; @@ -3332,21 +3427,23 @@ class DocSearch { } else { // For these special cases, matching code need added to the inverted index. // search_index.rs -> convert_render_type does this - if (queryElem.id === this.typeNameIdOfArrayOrSlice && - (fnType.id === this.typeNameIdOfSlice || fnType.id === this.typeNameIdOfArray) + if (queryElem.id === typeNameIds.typeNameIdOfArrayOrSlice && + (fnType.id === typeNameIds.typeNameIdOfSlice || + fnType.id === typeNameIds.typeNameIdOfArray) ) { // [] matches primitive:array or primitive:slice // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === this.typeNameIdOfTupleOrUnit && - (fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) + } else if (queryElem.id === typeNameIds.typeNameIdOfTupleOrUnit && + (fnType.id === typeNameIds.typeNameIdOfTuple || + fnType.id === typeNameIds.typeNameIdOfUnit) ) { // () matches primitive:tuple or primitive:unit // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === this.typeNameIdOfHof && ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + } else if (queryElem.id === typeNameIds.typeNameIdOfHof && ( + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr )) { // -> matches fn, fnonce, and fnmut // if it matches, then we're fine, and this is an appropriate match candidate @@ -3410,6 +3507,7 @@ class DocSearch { * @param {Map|null} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * @returns {false|{ * mgens: [Map|null], simplifiedGenerics: rustdoc.FunctionType[] * }} @@ -3420,6 +3518,7 @@ class DocSearch { whereClause, mgensIn, unboxingDepth, + typeNameIds, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -3451,6 +3550,7 @@ class DocSearch { return false; }, unboxingDepth, + typeNameIds, ); return newSolutions; }); @@ -3482,6 +3582,7 @@ class DocSearch { * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * @returns {boolean} */ function unifyFunctionTypeIsUnboxCandidate( @@ -3490,6 +3591,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -3508,6 +3610,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ); } else if (fnType.unboxFlag && (fnType.generics.length > 0 || fnType.bindings.size > 0)) { @@ -3521,6 +3624,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ); } return false; @@ -3533,14 +3637,15 @@ class DocSearch { * @param {rustdoc.QueryElement[]} elems * @param {rustdoc.FunctionType[]} list - A list of function types. * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. + * @param {rustdoc.TypeNameIds} typeNameIds */ - function containsTypeFromQuery(elems, list, where_clause) { + function containsTypeFromQuery(elems, list, where_clause, typeNameIds) { if (!list) return false; for (const ty of elems) { if (ty.id !== null && ty.id < 0) { continue; } - if (checkIfInList(list, ty, where_clause, null, 0)) { + if (checkIfInList(list, ty, where_clause, null, 0, typeNameIds)) { return true; } } @@ -3556,12 +3661,13 @@ class DocSearch { * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {boolean} - Returns true if found, false otherwise. */ - function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { + function checkIfInList(list, elem, whereClause, mgens, unboxingDepth, typeNameIds) { for (const entry of list) { - if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { + if (checkType(entry, elem, whereClause, mgens, unboxingDepth, typeNameIds)) { return true; } } @@ -3576,11 +3682,12 @@ class DocSearch { * @param {rustdoc.QueryElement} elem - The element from the parsed query. * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. + * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {boolean} - Returns true if the type matches, false otherwise. */ - // @ts-expect-error - const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { + const checkType = (row, elem, whereClause, mgens, unboxingDepth, typeNameIds) => { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } @@ -3589,9 +3696,9 @@ class DocSearch { row.generics.length === 0 && elem.generics.length === 0 && row.bindings.size === 0 && elem.bindings.size === 0 && // special case - elem.id !== this.typeNameIdOfArrayOrSlice && - elem.id !== this.typeNameIdOfHof && - elem.id !== this.typeNameIdOfTupleOrUnit + elem.id !== typeNameIds.typeNameIdOfArrayOrSlice && + elem.id !== typeNameIds.typeNameIdOfHof && + elem.id !== typeNameIds.typeNameIdOfTupleOrUnit ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { @@ -3603,6 +3710,7 @@ class DocSearch { mgens, () => true, unboxingDepth, + typeNameIds, ); } }; @@ -3669,7 +3777,10 @@ class DocSearch { /** * Compute an "edit distance" that ignores missing path elements. * @param {string[]} contains search query path - * @param {rustdoc.Row} row indexed item + * @param {{ + * modulePath: string, + * parent: { path: rustdoc.PathData, name: string}?, + * }} row indexed item * @returns {null|number} edit distance */ function checkRowPath(contains, row) { @@ -3748,7 +3859,7 @@ class DocSearch { is_alias: true, elems: [], // only used in type-based queries returned: [], // only used in type-based queries - original: await this.getRow(alias, false), + item: nonnull(await this.getRow(alias, false)), }; }; /** @@ -3830,6 +3941,7 @@ class DocSearch { elems: [], // only used in type-based queries returned: [], // only used in type-based queries is_alias: false, + item: row, } : null; } else { return { @@ -3844,6 +3956,7 @@ class DocSearch { elems: [], // only used in type-based queries returned: [], // only used in type-based queries is_alias: false, + item: row, }; } }; @@ -4355,9 +4468,10 @@ class DocSearch { }; // finally, we can do the actual unification loop - const [allInputs, allOutput] = await Promise.all([ + const [allInputs, allOutput, typeNameIds] = await Promise.all([ unpackPostingsListAll(inputs, "invertedFunctionInputsIndex"), unpackPostingsListAll(output, "invertedFunctionOutputIndex"), + this.getTypeNameIds(), ]); let checkCounter = 0; /** @@ -4426,12 +4540,18 @@ class DocSearch { mgens, checkTypeMgensForConflict, 0, // unboxing depth + typeNameIds, ); }, 0, // unboxing depth + typeNameIds, )) { return null; } + const item = await this.getRow(id, true); + if (!item) { + return null; + } const result = { id, dist: fnData.elemCount, @@ -4440,8 +4560,9 @@ class DocSearch { elems: inputs, returned: output, is_alias: false, + item, }; - const entry = await this.getEntryData(id); + const entry = item.entry; if ((entry && !isFnLikeTy(entry.ty)) || (isReturnTypeQuery && functionSignature && @@ -4449,6 +4570,7 @@ class DocSearch { output, functionSignature.inputs, functionSignature.where_clause, + typeNameIds, ) ) ) { @@ -5269,7 +5391,6 @@ if (ROOT_PATH === null) { const database = await Stringdex.loadDatabase(hooks); if (typeof window !== "undefined") { docSearch = new DocSearch(ROOT_PATH, database); - await docSearch.buildIndex(); onEachLazy(document.querySelectorAll( ".search-form.loading", ), form => { @@ -5282,7 +5403,6 @@ if (typeof window !== "undefined") { } } else if (typeof exports !== "undefined") { docSearch = new DocSearch(ROOT_PATH, database); - await docSearch.buildIndex(); return { docSearch, DocSearch }; } }; diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index 4c4659628778d..5bdc81e330efa 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -2310,7 +2310,7 @@ function loadDatabase(hooks) { } else { const start = this.bucket_keys[idx]; const bucket = this.buckets[idx]; - let data = this.buckets[idx].data; + const data = this.buckets[idx].data; if (data === null) { return this.atAsyncFetch(id, start, bucket); } else { From 382c964f3ea97d81f75c9716645dcedf081b4f5d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 00:28:46 +0200 Subject: [PATCH 0361/1900] small clean-up --- clippy_lints/src/lifetimes.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 149ae5e710c1f..aac5d08fd37c8 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>( impl_: &'tcx Impl<'_>, map: &FxIndexMap>, ) { - let single_usages = map + let (elidable_lts, usages): (Vec<_>, Vec<_>) = map .iter() .filter_map(|(def_id, usages)| { if let [ @@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>( None } }) - .collect::>(); + .unzip(); - if single_usages.is_empty() { + if elidable_lts.is_empty() { return; } - let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); - report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } @@ -795,9 +793,7 @@ fn report_elidable_lifetimes( // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); + .format(", "); let elidable_usages: Vec = usages .iter() From ae57f08c1377b304b13fbb922438615d7692d65e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 13 Sep 2025 04:52:19 +0000 Subject: [PATCH 0362/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 4ba1cf9ade4c8e2fa10676a50ee34594eb161837. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b3f8a26875f40..e5f0be9ac9551 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -2a9bacf6187685931d52346a0ecff2e52bdc91cc +4ba1cf9ade4c8e2fa10676a50ee34594eb161837 From 25cf36ac78d1b88583f7a1125f9ddf67d8ef8eea Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 01:25:03 +0200 Subject: [PATCH 0363/1900] clean-up - move tests to `ui-toml/`, since they all use `clippy.toml` - remove the trait test case from `ref_option.rs`, because it's already covered by `ref_option_traits.rs` --- clippy_lints/src/functions/ref_option.rs | 16 ++--- .../ref_option/all/clippy.toml | 0 .../ref_option/private/clippy.toml | 0 .../ref_option/ref_option.all.fixed | 22 ++---- .../ref_option/ref_option.all.stderr | 68 +++++-------------- .../ref_option/ref_option.private.fixed | 22 ++---- .../ref_option/ref_option.private.stderr | 44 ++++-------- .../{ui => ui-toml}/ref_option/ref_option.rs | 22 ++---- .../ref_option/ref_option_traits.all.stderr | 8 +-- .../ref_option_traits.private.stderr | 4 +- .../ref_option/ref_option_traits.rs | 4 +- 11 files changed, 58 insertions(+), 152 deletions(-) rename tests/{ui => ui-toml}/ref_option/all/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/private/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/ref_option.all.fixed (72%) rename tests/{ui => ui-toml}/ref_option/ref_option.all.stderr (62%) rename tests/{ui => ui-toml}/ref_option/ref_option.private.fixed (72%) rename tests/{ui => ui-toml}/ref_option/ref_option.private.stderr (63%) rename tests/{ui => ui-toml}/ref_option/ref_option.rs (72%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.all.stderr (85%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.private.stderr (86%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.rs (85%) diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 106202d00d403..b4ae258ebbb32 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -2,22 +2,18 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{FnDecl, HirId}; +use rustc_hir::{self as hir, FnDecl, HirId}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_middle::ty::{self, Mutability, Ty}; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; -fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { +fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() - && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen_args) = opt_ty.kind() - && let [gen_arg] = opt_gen_args.as_slice() - && let GenericArgKind::Type(gen_ty) = gen_arg.kind() + && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind diff --git a/tests/ui/ref_option/all/clippy.toml b/tests/ui-toml/ref_option/all/clippy.toml similarity index 100% rename from tests/ui/ref_option/all/clippy.toml rename to tests/ui-toml/ref_option/all/clippy.toml diff --git a/tests/ui/ref_option/private/clippy.toml b/tests/ui-toml/ref_option/private/clippy.toml similarity index 100% rename from tests/ui/ref_option/private/clippy.toml rename to tests/ui-toml/ref_option/private/clippy.toml diff --git a/tests/ui/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed similarity index 72% rename from tests/ui/ref_option/ref_option.all.fixed rename to tests/ui-toml/ref_option/ref_option.all.fixed index 4159e916f1995..102d4aaefff40 100644 --- a/tests/ui/ref_option/ref_option.all.fixed +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: Option<&T>) {} //~^ ref_option fn opt_string(a: std::option::Option<&String>) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { //~^ ref_option panic!() } -fn ret_string_static() -> Option<&'static u8> { +fn ret_u8_static() -> Option<&'static u8> { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: Option<&String>) {} pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: Option<&Vec>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> Option<&Vec>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr similarity index 62% rename from tests/ui/ref_option/ref_option.all.stderr rename to tests/ui-toml/ref_option/ref_option.all.stderr index bd43c28336eb3..efd7a6b7241f1 100644 --- a/tests/ui/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:8:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:10:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:12:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:14:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:18:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:22:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:24:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:29:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:31:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,39 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:35:5 - | -LL | fn pub_trait_opt(&self, a: &Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:37:5 - | -LL | fn pub_trait_ret(&self) -> &Option>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:51:5 + --> tests/ui-toml/ref_option/ref_option.rs:37:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -134,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:53:5 + --> tests/ui-toml/ref_option/ref_option.rs:39:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -146,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:44:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -154,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:46:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -165,5 +133,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed similarity index 72% rename from tests/ui/ref_option/ref_option.private.fixed rename to tests/ui-toml/ref_option/ref_option.private.fixed index 3b158befb926e..396188b6d4077 100644 --- a/tests/ui/ref_option/ref_option.private.fixed +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: Option<&T>) {} //~^ ref_option fn opt_string(a: std::option::Option<&String>) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { //~^ ref_option panic!() } -fn ret_string_static() -> Option<&'static u8> { +fn ret_u8_static() -> Option<&'static u8> { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: &Option) {} pub fn pub_mult_string(a: &Option, b: &Option>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr similarity index 63% rename from tests/ui/ref_option/ref_option.private.stderr rename to tests/ui-toml/ref_option/ref_option.private.stderr index 88c65e429d870..56f38c44f14c2 100644 --- a/tests/ui/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:8:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:10:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:12:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:14:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:18:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:22:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:24:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,23 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:44:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -98,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:46:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -109,5 +93,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs similarity index 72% rename from tests/ui/ref_option/ref_option.rs rename to tests/ui-toml/ref_option/ref_option.rs index 35cd94174f8bc..7600c58ea7038 100644 --- a/tests/ui/ref_option/ref_option.rs +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: &Option) {} //~^ ref_option fn opt_string(a: &std::option::Option) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> &'a Option { +fn ret_u8<'a>(p: &'a str) -> &'a Option { //~^ ref_option panic!() } -fn ret_string_static() -> &'static Option { +fn ret_u8_static() -> &'static Option { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: &Option) {} pub fn pub_mult_string(a: &Option, b: &Option>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr similarity index 85% rename from tests/ui/ref_option/ref_option_traits.all.stderr rename to tests/ui-toml/ref_option/ref_option_traits.all.stderr index 886bf2b034989..61017eba176e0 100644 --- a/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:9:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:11:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr similarity index 86% rename from tests/ui/ref_option/ref_option_traits.private.stderr rename to tests/ui-toml/ref_option/ref_option_traits.private.stderr index cfab7fa5734c3..d7b0b6e9f8368 100644 --- a/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs similarity index 85% rename from tests/ui/ref_option/ref_option_traits.rs rename to tests/ui-toml/ref_option/ref_option_traits.rs index 4c773e84f8da8..c02d154b448f2 100644 --- a/tests/ui/ref_option/ref_option_traits.rs +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -1,7 +1,7 @@ //@no-rustfix: fixes are only done to traits, not the impls //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![warn(clippy::ref_option)] From b901ba725ebb33d3bb3ff4d8438db74a615d9e23 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 01:24:18 +0200 Subject: [PATCH 0364/1900] fix(ref_option): don't lint in external and proc-macros --- clippy_lints/src/functions/ref_option.rs | 32 +++++++++--- tests/ui-toml/ref_option/ref_option.all.fixed | 49 +++++++++++++++++++ .../ui-toml/ref_option/ref_option.all.stderr | 26 +++++----- .../ref_option/ref_option.private.fixed | 49 +++++++++++++++++++ .../ref_option/ref_option.private.stderr | 18 +++---- tests/ui-toml/ref_option/ref_option.rs | 49 +++++++++++++++++++ .../ref_option/ref_option_traits.all.stderr | 8 +-- .../ref_option_traits.private.stderr | 4 +- tests/ui-toml/ref_option/ref_option_traits.rs | 31 ++++++++++++ 9 files changed, 230 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index b4ae258ebbb32..5dc1b7269b761 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -1,18 +1,19 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_from_proc_macro, is_trait_impl_item}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{self as hir, FnDecl, HirId}; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { - if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + if !param.span.in_external_macro(cx.sess().source_map()) + && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward @@ -23,6 +24,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fix args: [hir::GenericArg::Type(opt_ty)], .. }) = last.args + && !is_from_proc_macro(cx, param) { let lifetime = snippet(cx, lifetime.ident.span, ".."); fixes.push(( @@ -63,21 +65,24 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty #[allow(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, - kind: FnKind<'_>, + kind: FnKind<'a>, decl: &FnDecl<'a>, span: Span, hir_id: HirId, def_id: LocalDefId, - body: &hir::Body<'_>, + body: &hir::Body<'a>, avoid_breaking_exported_api: bool, ) { if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } + if span.in_external_macro(cx.sess().source_map()) { + return; + } if let FnKind::Closure = kind { // Compute the span of the closure parameters + return type if set - let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output { if decl.inputs.is_empty() { out_ty.span } else { @@ -96,9 +101,18 @@ pub(crate) fn check_fn<'a>( }; let sig = args.as_closure().sig().skip_binder(); - check_fn_sig(cx, decl, span, sig); + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + + check_fn_sig(cx, decl, inputs_output_span, sig); } else if !is_trait_impl_item(cx, hir_id) { let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + check_fn_sig(cx, decl, span, sig); } } @@ -108,8 +122,10 @@ pub(super) fn check_trait_item<'a>( trait_item: &hir::TraitItem<'a>, avoid_breaking_exported_api: bool, ) { - if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + if !trait_item.span.in_external_macro(cx.sess().source_map()) + && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + && !is_from_proc_macro(cx, trait_item) { let def_id = trait_item.owner_id.def_id; let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); diff --git a/tests/ui-toml/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed index 102d4aaefff40..f8f097e9a75e3 100644 --- a/tests/ui-toml/ref_option/ref_option.all.fixed +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr index efd7a6b7241f1..45ce105e03084 100644 --- a/tests/ui-toml/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,7 +26,7 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { | ^ -------------- help: change this to: `Option<&'a u8>` @@ -38,7 +38,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | LL | fn ret_u8_static() -> &'static Option { | ^ ------------------- help: change this to: `Option<&'static u8>` @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:30:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:32:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:37:5 + --> tests/ui-toml/ref_option/ref_option.rs:38:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -102,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:39:5 + --> tests/ui-toml/ref_option/ref_option.rs:40:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -114,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:44:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -122,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:46:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` diff --git a/tests/ui-toml/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed index 396188b6d4077..4dd14a822067b 100644 --- a/tests/ui-toml/ref_option/ref_option.private.fixed +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr index 56f38c44f14c2..a63efd60a0368 100644 --- a/tests/ui-toml/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,7 +26,7 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { | ^ -------------- help: change this to: `Option<&'a u8>` @@ -38,7 +38,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | LL | fn ret_u8_static() -> &'static Option { | ^ ------------------- help: change this to: `Option<&'static u8>` @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:44:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -82,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:46:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` diff --git a/tests/ui-toml/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs index 7600c58ea7038..8397c2213d172 100644 --- a/tests/ui-toml/ref_option/ref_option.rs +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr index 61017eba176e0..602e148be6012 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:10:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:12:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr index d7b0b6e9f8368..20bea400edfe6 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs index c02d154b448f2..e1477d7f8463d 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.rs +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -1,4 +1,5 @@ //@no-rustfix: fixes are only done to traits, not the impls +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -37,4 +38,34 @@ impl PrivateTrait for PrivateStruct { } } +pub mod external { + proc_macros::external!( + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + fn main() {} From af82c3db6c7259860ffdbe1dfd44b305b7796988 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 16:39:51 +0200 Subject: [PATCH 0365/1900] tiny clean-up --- clippy_lints/src/returns.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index e0c93153a77a8..a3e9e51bf12f0 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -162,9 +162,9 @@ impl RetReplacement<'_> { impl Display for RetReplacement<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Empty => write!(f, ""), - Self::Block => write!(f, "{{}}"), - Self::Unit => write!(f, "()"), + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), Self::NeedsPar(inner, _) => write!(f, "({inner})"), Self::Expr(inner, _) => write!(f, "{inner}"), } @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { // we need both a let-binding stmt and an expr if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.iter().last() + && let Some(stmt) = block.stmts.last() && let StmtKind::Let(local) = &stmt.kind && local.ty.is_none() && cx.tcx.hir_attrs(local.hir_id).is_empty() @@ -315,7 +315,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, if let ExprKind::Block(block, _) = expr_kind { if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.iter().last() { + } else if let Some(stmt) = block.stmts.last() { match stmt.kind { StmtKind::Expr(expr) => { check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); From 51ee8d8f0ee821d8eae9c0b141f983413d6ad8d3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 16:01:00 +0200 Subject: [PATCH 0366/1900] split `returns` module --- clippy_lints/src/returns.rs | 513 ------------------ clippy_lints/src/returns/let_and_return.rs | 86 +++ clippy_lints/src/returns/mod.rs | 140 +++++ clippy_lints/src/returns/needless_return.rs | 260 +++++++++ .../needless_return_with_question_mark.rs | 60 ++ tests/ui/track-diagnostics-clippy.stderr | 2 +- 6 files changed, 547 insertions(+), 514 deletions(-) delete mode 100644 clippy_lints/src/returns.rs create mode 100644 clippy_lints/src/returns/let_and_return.rs create mode 100644 clippy_lints/src/returns/mod.rs create mode 100644 clippy_lints/src/returns/needless_return.rs create mode 100644 clippy_lints/src/returns/needless_return_with_question_mark.rs diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs deleted file mode 100644 index a3e9e51bf12f0..0000000000000 --- a/clippy_lints/src/returns.rs +++ /dev/null @@ -1,513 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; -use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, - leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, sym, -}; -use core::ops::ControlFlow; -use rustc_ast::MetaItemInner; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, - StmtKind, -}; -use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span}; -use std::borrow::Cow; -use std::fmt::Display; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// ### Why is this bad? - /// It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// ### Known problems - /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead - /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). - /// This could become relevant if the code is later changed to use the code that would have been - /// bound without first assigning it to a let-binding. - /// - /// ### Example - /// ```no_run - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ```no_run - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements at the end of a block. - /// - /// ### Why is this bad? - /// Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// ### Example - /// ```no_run - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```no_run - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NEEDLESS_RETURN, - // This lint requires some special handling in `check_final_expr` for `#[expect]`. - // This handling needs to be updated if the group gets changed. This should also - // be caught by tests. - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements on `Err` paired with the `?` operator. - /// - /// ### Why is this bad? - /// The `return` is unnecessary. - /// - /// Returns may be used to add attributes to the return expression. Return - /// statements with attributes are therefore be accepted by this lint. - /// - /// ### Example - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// simplify to - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// if paired with `try_err`, use instead: - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...); - /// } - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.73.0"] - pub NEEDLESS_RETURN_WITH_QUESTION_MARK, - style, - "using a return statement like `return Err(expr)?;` where removing it would suffice" -} - -#[derive(PartialEq, Eq)] -enum RetReplacement<'tcx> { - Empty, - Block, - Unit, - NeedsPar(Cow<'tcx, str>, Applicability), - Expr(Cow<'tcx, str>, Applicability), -} - -impl RetReplacement<'_> { - fn sugg_help(&self) -> &'static str { - match self { - Self::Empty | Self::Expr(..) => "remove `return`", - Self::Block => "replace `return` with an empty block", - Self::Unit => "replace `return` with a unit value", - Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", - } - } - - fn applicability(&self) -> Applicability { - match self { - Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, - _ => Applicability::MachineApplicable, - } - } -} - -impl Display for RetReplacement<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Empty => f.write_str(""), - Self::Block => f.write_str("{}"), - Self::Unit => f.write_str("()"), - Self::NeedsPar(inner, _) => write!(f, "({inner})"), - Self::Expr(inner, _) => write!(f, "{inner}"), - } - } -} - -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); - -/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This -/// is the case when the enclosing block expression is coerced to some other type, which only works -/// because of the never-ness of `return` expressions -fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { - cx.tcx - .hir_parent_iter(stmt_hir_id) - .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) - .is_some_and(|e| { - cx.typeck_results() - .expr_adjustments(e) - .iter() - .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) - }) -} - -impl<'tcx> LateLintPass<'tcx> for Return { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !stmt.span.in_external_macro(cx.sess().source_map()) - && let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Ret(Some(ret)) = expr.kind - // return Err(...)? desugars to a match - // over a Err(...).branch() - // which breaks down to a branch call, with the callee being - // the constructor of the Err variant - && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind - && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind - && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) - - // Ensure this is not the final stmt, otherwise removing it would cause a compile error - && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) - && let ItemKind::Fn { body, .. } = item.kind - && let block = cx.tcx.hir_body(body).value - && let ExprKind::Block(block, _) = block.kind - && !is_inside_let_else(cx.tcx, expr) - && let [.., final_stmt] = block.stmts - && final_stmt.hir_id != stmt.hir_id - && !is_from_proc_macro(cx, expr) - && !stmt_needs_never_type(cx, stmt.hir_id) - { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN_WITH_QUESTION_MARK, - expr.span.until(ret.span), - "unneeded `return` statement with `?` operator", - "remove it", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.last() - && let StmtKind::Let(local) = &stmt.kind - && local.ty.is_none() - && cx.tcx.hir_attrs(local.hir_id).is_empty() - && let Some(initexpr) = &local.init - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) - && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) - && !initexpr.span.in_external_macro(cx.sess().source_map()) - && !retexpr.span.in_external_macro(cx.sess().source_map()) - && !local.span.from_expansion() - && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) - { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(src) = initexpr.span.get_source_text(cx) { - let sugg = if binary_expr_needs_parentheses(initexpr) { - if has_enclosing_paren(&src) { - src.to_owned() - } else { - format!("({src})") - } - } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if has_enclosing_paren(&src) { - format!("{src} as _") - } else { - format!("({src}) as _") - } - } else { - src.to_owned() - }; - err.multipart_suggestion( - "return the expression directly", - vec![(local.span, String::new()), (retexpr.span, sugg)], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - sp: Span, - _: LocalDefId, - ) { - if sp.from_expansion() { - return; - } - - match kind { - FnKind::Closure => { - // when returning without value in closure, replace this `return` - // with an empty block to prevent invalid suggestion (see #6501) - let replacement = if let ExprKind::Ret(None) = &body.value.kind { - RetReplacement::Block - } else { - RetReplacement::Empty - }; - check_final_expr(cx, body.value, vec![], replacement, None); - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, sp, vec![]); - }, - } - } -} - -// if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { - if let ExprKind::Block(block, _) = expr_kind { - if let Some(block_expr) = block.expr { - check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.last() { - match stmt.kind { - StmtKind::Expr(expr) => { - check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); - }, - StmtKind::Semi(semi_expr) => { - // Remove ending semicolons and any whitespace ' ' in between. - // Without `return`, the suggestion might not compile if the semicolon is retained - if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { - let semi_span_to_remove = - span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); - semi_spans.push(semi_span_to_remove); - } - check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); - }, - _ => (), - } - } - } -} - -fn check_final_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an - * needless return */ - replacement: RetReplacement<'tcx>, - match_ty_opt: Option>, -) { - let peeled_drop_expr = expr.peel_drop_temps(); - match &peeled_drop_expr.kind { - // simple return is always "bad" - ExprKind::Ret(inner) => { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - let replacement = if let Some(inner_expr) = inner { - // if desugar of `do yeet`, don't lint - if let ExprKind::Call(path_expr, [_]) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind - { - return; - } - - let mut applicability = Applicability::MachineApplicable; - let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); - if binary_expr_needs_parentheses(inner_expr) { - RetReplacement::NeedsPar(snippet, applicability) - } else { - RetReplacement::Expr(snippet, applicability) - } - } else { - match match_ty_opt { - Some(match_ty) => { - match match_ty.kind() { - // If the code got till here with - // tuple not getting detected before it, - // then we are sure it's going to be Unit - // type - ty::Tuple(_) => RetReplacement::Unit, - // We don't want to anything in this case - // cause we can't predict what the user would - // want here - _ => return, - } - }, - None => replacement, - } - }; - - if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { - return; - } - - if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { - return; - } - - // Returns may be used to turn an expression into a statement in rustc's AST. - // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) - // `#[expect(clippy::needless_return)]` needs to be handled separately to - // actually fulfill the expectation (clippy::#12998) - match cx.tcx.hir_attrs(expr.hir_id) { - [] => {}, - [attr] => { - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings - ) - { - // This is an expectation of the `needless_return` lint - } else { - return; - } - }, - _ => return, - } - - emit_return_lint( - cx, - peeled_drop_expr.span, - ret_span, - semi_spans, - &replacement, - expr.hir_id, - ); - }, - ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); - if let Some(else_clause) = else_clause_opt { - // The `RetReplacement` won't be used there as `else_clause` will be either a block or - // a `if` expression. - check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); - } - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, MatchSource::Normal) => { - let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); - for arm in *arms { - check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); - } - }, - // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), - } -} - -fn emit_return_lint( - cx: &LateContext<'_>, - lint_span: Span, - ret_span: Span, - semi_spans: Vec, - replacement: &RetReplacement<'_>, - at: HirId, -) { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - at, - lint_span, - "unneeded `return` statement", - |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); - - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }, - ); -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { - if let Some(def_id) = fn_def_id(cx, e) - && cx - .tcx - .fn_sig(def_id) - .instantiate_identity() - .skip_binder() - .output() - .walk() - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -// Go backwards while encountering whitespace and extend the given Span to that point. -fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { - if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [b' ', b'\t', b'\n']; - if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { - let len = prev_source.len() - non_ws_pos - 1; - return sp.with_lo(sp.lo() - BytePos::from_usize(len)); - } - } - - sp -} diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs new file mode 100644 index 0000000000000..e2002fb36e5a6 --- /dev/null +++ b/clippy_lints/src/returns/let_and_return.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::has_enclosing_paren; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, PatKind, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::GenericArgKind; +use rustc_span::edition::Edition; + +use super::LET_AND_RETURN; + +pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.last() + && let StmtKind::Let(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir_attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) + && !initexpr.span.in_external_macro(cx.sess().source_map()) + && !retexpr.span.in_external_macro(cx.sess().source_map()) + && !local.span.from_expansion() + && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(src) = initexpr.span.get_source_text(cx) { + let sugg = if binary_expr_needs_parentheses(initexpr) { + if has_enclosing_paren(&src) { + src.to_owned() + } else { + format!("({src})") + } + } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if has_enclosing_paren(&src) { + format!("{src} as _") + } else { + format!("({src}) as _") + } + } else { + src.to_owned() + }; + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, sugg)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } +} +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_expr(cx, expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_binder() + .output() + .walk() + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/clippy_lints/src/returns/mod.rs b/clippy_lints/src/returns/mod.rs new file mode 100644 index 0000000000000..47c6332b9b81d --- /dev/null +++ b/clippy_lints/src/returns/mod.rs @@ -0,0 +1,140 @@ +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Block, Body, FnDecl, Stmt}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +mod let_and_return; +mod needless_return; +mod needless_return_with_question_mark; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// ### Why is this bad? + /// It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// ### Known problems + /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead + /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). + /// This could become relevant if the code is later changed to use the code that would have been + /// bound without first assigning it to a let-binding. + /// + /// ### Example + /// ```no_run + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ```no_run + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements at the end of a block. + /// + /// ### Why is this bad? + /// Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// ### Example + /// ```no_run + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```no_run + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_RETURN, + // This lint requires some special handling in `check_final_expr` for `#[expect]`. + // This handling needs to be updated if the group gets changed. This should also + // be caught by tests. + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements on `Err` paired with the `?` operator. + /// + /// ### Why is this bad? + /// The `return` is unnecessary. + /// + /// Returns may be used to add attributes to the return expression. Return + /// statements with attributes are therefore be accepted by this lint. + /// + /// ### Example + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// simplify to + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// if paired with `try_err`, use instead: + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...); + /// } + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, + style, + "using a return statement like `return Err(expr)?;` where removing it would suffice" +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + needless_return_with_question_mark::check_stmt(cx, stmt); + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + let_and_return::check_block(cx, block); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + sp: Span, + _: LocalDefId, + ) { + needless_return::check_fn(cx, kind, body, sp); + } +} diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs new file mode 100644 index 0000000000000..0a1ba7b5becaa --- /dev/null +++ b/clippy_lints/src/returns/needless_return.rs @@ -0,0 +1,260 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{ + binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, + span_find_starting_semi, sym, +}; +use rustc_ast::MetaItemInner; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind}; +use rustc_lint::{LateContext, Level, LintContext}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{BytePos, Pos, Span}; +use std::borrow::Cow; +use std::fmt::Display; + +use super::NEEDLESS_RETURN; + +#[derive(PartialEq, Eq)] +enum RetReplacement<'tcx> { + Empty, + Block, + Unit, + NeedsPar(Cow<'tcx, str>, Applicability), + Expr(Cow<'tcx, str>, Applicability), +} + +impl RetReplacement<'_> { + fn sugg_help(&self) -> &'static str { + match self { + Self::Empty | Self::Expr(..) => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", + } + } + + fn applicability(&self) -> Applicability { + match self { + Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, + _ => Applicability::MachineApplicable, + } + } +} + +impl Display for RetReplacement<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), + Self::NeedsPar(inner, _) => write!(f, "({inner})"), + Self::Expr(inner, _) => write!(f, "{inner}"), + } + } +} + +pub(super) fn check_fn<'tcx>(cx: &LateContext<'tcx>, kind: FnKind<'tcx>, body: &'tcx Body<'tcx>, sp: Span) { + if sp.from_expansion() { + return; + } + + match kind { + FnKind::Closure => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, body.value, vec![], replacement, None); + }, + FnKind::ItemFn(..) | FnKind::Method(..) => { + check_block_return(cx, &body.value.kind, sp, vec![]); + }, + } +} + +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); + } else if let Some(stmt) = block.stmts.last() { + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); + }, + StmtKind::Semi(semi_expr) => { + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); + } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); + }, + _ => (), + } + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ + replacement: RetReplacement<'tcx>, + match_ty_opt: Option>, +) { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { + // simple return is always "bad" + ExprKind::Ret(inner) => { + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + let replacement = if let Some(inner_expr) = inner { + // if desugar of `do yeet`, don't lint + if let ExprKind::Call(path_expr, [_]) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind + { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); + if binary_expr_needs_parentheses(inner_expr) { + RetReplacement::NeedsPar(snippet, applicability) + } else { + RetReplacement::Expr(snippet, applicability) + } + } else { + match match_ty_opt { + Some(match_ty) => { + match match_ty.kind() { + // If the code got till here with + // tuple not getting detected before it, + // then we are sure it's going to be Unit + // type + ty::Tuple(_) => RetReplacement::Unit, + // We don't want to anything in this case + // cause we can't predict what the user would + // want here + _ => return, + } + }, + None => replacement, + } + }; + + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; + } + + if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // `#[expect(clippy::needless_return)]` needs to be handled separately to + // actually fulfill the expectation (clippy::#12998) + match cx.tcx.hir_attrs(expr.hir_id) { + [] => {}, + [attr] => { + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let metas = attr.meta_item_list() + && let Some(lst) = metas + && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && matches!( + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings + ) + { + // This is an expectation of the `needless_return` lint + } else { + return; + } + }, + _ => return, + } + + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); + }, + ExprKind::If(_, then, else_clause_opt) => { + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); + if let Some(else_clause) = else_clause_opt { + // The `RetReplacement` won't be used there as `else_clause` will be either a block or + // a `if` expression. + check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); + } + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, arms, MatchSource::Normal) => { + let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); + for arm in *arms { + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); + } + }, + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), + } +} + +fn emit_return_lint( + cx: &LateContext<'_>, + lint_span: Span, + ret_span: Span, + semi_spans: Vec, + replacement: &RetReplacement<'_>, + at: HirId, +) { + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + lint_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); +} + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs new file mode 100644 index 0000000000000..c05038cd1e50e --- /dev/null +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; + +use super::NEEDLESS_RETURN_WITH_QUESTION_MARK; + +pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !stmt.span.in_external_macro(cx.sess().source_map()) + && let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Ret(Some(ret)) = expr.kind + // return Err(...)? desugars to a match + // over a Err(...).branch() + // which breaks down to a branch call, with the callee being + // the constructor of the Err variant + && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind + && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind + && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + + // Ensure this is not the final stmt, otherwise removing it would cause a compile error + && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) + && let ItemKind::Fn { body, .. } = item.kind + && let block = cx.tcx.hir_body(body).value + && let ExprKind::Block(block, _) = block.kind + && !is_inside_let_else(cx.tcx, expr) + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id != stmt.hir_id + && !is_from_proc_macro(cx, expr) + && !stmt_needs_never_type(cx, stmt.hir_id) + { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN_WITH_QUESTION_MARK, + expr.span.until(ret.span), + "unneeded `return` statement with `?` operator", + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + } +} + +/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. +/// This is the case when the enclosing block expression is coerced to some other type, +/// which only works because of the never-ness of `return` expressions +fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { + cx.tcx + .hir_parent_iter(stmt_hir_id) + .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) + .is_some_and(|e| { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) + }) +} diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr index d5533877b4515..3ceb501463b77 100644 --- a/tests/ui/track-diagnostics-clippy.stderr +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns/let_and_return.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly From a48c8e337d1717ceeaa930a0a804374aa2e66419 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 13 Sep 2025 11:24:51 +0200 Subject: [PATCH 0367/1900] compiletest: Fix `--exact` test filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fix only changes the behavior when using `--exact` test filtering, which was quite broken. Before this fix, the following runs 0 tests: $ ./x test tests/run-make/crate-loading -- --exact running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 431 filtered out; finished in 24.95µs With the fix the desired test is run: $ ./x test tests/run-make/crate-loading -- --exact running 1 tests test [run-make] tests/run-make/crate-loading ... ok Without `--exact` the set of run tests is unchanged. This still runs "too many" tests $ ./x test tests/run-make/crate-loading running 3 tests test [run-make] tests/run-make/crate-loading-crate-depends-on-itself ... ok test [run-make] tests/run-make/crate-loading-multiple-candidates ... ok test [run-make] tests/run-make/crate-loading ... ok This still runs the one and only right test: $ ./x test tests/ui/lint/unused/unused-allocation.rs running 1 tests test [ui] tests/ui/lint/unused/unused-allocation.rs ... ok --- src/tools/compiletest/src/directives.rs | 9 ++++- src/tools/compiletest/src/directives/tests.rs | 8 ++-- src/tools/compiletest/src/executor.rs | 11 +++++- src/tools/compiletest/src/lib.rs | 37 ++++++++++++++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 857953072c436..1277fd225eb66 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1446,6 +1446,7 @@ pub(crate) fn make_test_description( cache: &DirectivesCache, name: String, path: &Utf8Path, + filterable_path: &Utf8Path, src: R, test_revision: Option<&str>, poisoned: &mut bool, @@ -1520,7 +1521,13 @@ pub(crate) fn make_test_description( _ => ShouldPanic::No, }; - CollectedTestDesc { name, ignore, ignore_message, should_panic } + CollectedTestDesc { + name, + filterable_path: filterable_path.to_owned(), + ignore, + ignore_message, + should_panic, + } } fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 33a02eb29fd53..16cf76be9a53f 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -14,6 +14,7 @@ fn make_test_description( config: &Config, name: String, path: &Utf8Path, + filterable_path: &Utf8Path, src: R, revision: Option<&str>, ) -> CollectedTestDesc { @@ -24,6 +25,7 @@ fn make_test_description( &cache, name, path, + filterable_path, src, revision, &mut poisoned, @@ -221,7 +223,7 @@ fn parse_rs(config: &Config, contents: &str) -> EarlyProps { fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None); + let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None); d.ignore } @@ -231,9 +233,9 @@ fn should_fail() { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None); + let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None); assert_eq!(d.should_panic, ShouldPanic::No); - let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None); + let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None); assert_eq!(d.should_panic, ShouldPanic::Yes); } diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index b0dc24798b6cd..37cc17351d5fe 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -12,6 +12,8 @@ use std::num::NonZero; use std::sync::{Arc, Mutex, mpsc}; use std::{env, hint, io, mem, panic, thread}; +use camino::Utf8PathBuf; + use crate::common::{Config, TestPaths}; use crate::output_capture::{self, ConsoleOut}; use crate::panic_hook; @@ -293,8 +295,12 @@ fn filter_tests(opts: &Config, tests: Vec) -> Vec let mut filtered = tests; let matches_filter = |test: &CollectedTest, filter_str: &str| { - let test_name = &test.desc.name; - if opts.filter_exact { test_name == filter_str } else { test_name.contains(filter_str) } + let filterable_path = test.desc.filterable_path.as_str(); + if opts.filter_exact { + filterable_path == filter_str + } else { + filterable_path.contains(filter_str) + } }; // Remove tests that don't match the test filter @@ -339,6 +345,7 @@ pub(crate) struct CollectedTest { /// Information that was historically needed to create a libtest `TestDesc`. pub(crate) struct CollectedTestDesc { pub(crate) name: String, + pub(crate) filterable_path: Utf8PathBuf, pub(crate) ignore: bool, pub(crate) ignore_message: Option>, pub(crate) should_panic: ShouldPanic, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index f647f96a9bf34..5e349a20ed284 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -317,11 +317,17 @@ pub fn parse_config(args: Vec) -> Config { .free .iter() .map(|f| { + // Here `f` is relative to `./tests/run-make`. So if you run + // + // ./x test tests/run-make/crate-loading + // + // then `f` is "crate-loading". let path = Utf8Path::new(f); let mut iter = path.iter().skip(1); - // We skip the test folder and check if the user passed `rmake.rs`. if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() { + // Strip the "rmake.rs" suffix. For example, if `f` is + // "crate-loading/rmake.rs" then this gives us "crate-loading". path.parent().unwrap().to_string() } else { f.to_string() @@ -329,6 +335,12 @@ pub fn parse_config(args: Vec) -> Config { }) .collect::>() } else { + // Note that the filters are relative to the root dir of the different test + // suites. For example, with: + // + // ./x test tests/ui/lint/unused + // + // the filter is "lint/unused". matches.free.clone() }; let compare_mode = matches.opt_str("compare-mode").map(|s| { @@ -911,7 +923,8 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); - let test_name = make_test_name(&cx.config, testpaths, revision); + let (test_name, filterable_path) = + make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. // This is where `ignore-*`/`only-*`/`needs-*` directives are handled, // because they historically needed to set the libtest ignored flag. @@ -920,6 +933,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te &cx.cache, test_name, &test_path, + &filterable_path, src_file, revision, &mut collector.poisoned, @@ -1072,7 +1086,11 @@ impl Stamp { } /// Creates a name for this test/revision that can be handed over to the executor. -fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String { +fn make_test_name_and_filterable_path( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> (String, Utf8PathBuf) { // Print the name of the file, relative to the sources root. let path = testpaths.file.strip_prefix(&config.src_root).unwrap(); let debugger = match config.debugger { @@ -1084,14 +1102,23 @@ fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str> None => String::new(), }; - format!( + let name = format!( "[{}{}{}] {}{}", config.mode, debugger, mode_suffix, path, revision.map_or("".to_string(), |rev| format!("#{}", rev)) - ) + ); + + // `path` is the full path from the repo root like, `tests/ui/foo/bar.rs`. + // Filtering is applied without the `tests/ui/` part, so strip that off. + // First strip off "tests" to make sure we don't have some unexpected path. + let mut filterable_path = path.strip_prefix("tests").unwrap().to_owned(); + // Now strip off e.g. "ui" or "run-make" component. + filterable_path = filterable_path.components().skip(1).collect(); + + (name, filterable_path) } /// Checks that test discovery didn't find any tests whose name stem is a prefix From 6f3f1f3aa71c1d8dfdb86ff47255979bcfdb5cda Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 11:11:48 +0200 Subject: [PATCH 0368/1900] fix(needless_return): FP with `cfg`d code after `return` --- clippy_lints/src/returns/needless_return.rs | 11 ++++++++++- tests/ui/needless_return.fixed | 7 +++++++ tests/ui/needless_return.rs | 7 +++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs index 0a1ba7b5becaa..04739fc1b22ad 100644 --- a/clippy_lints/src/returns/needless_return.rs +++ b/clippy_lints/src/returns/needless_return.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_with_context; use clippy_utils::{ binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, - span_find_starting_semi, sym, + span_contains_cfg, span_find_starting_semi, sym, }; use rustc_ast::MetaItemInner; use rustc_errors::Applicability; @@ -83,6 +83,15 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); } else if let Some(stmt) = block.stmts.last() { + if span_contains_cfg( + cx, + Span::between( + stmt.span, + cx.sess().source_map().end_point(block.span), // the closing brace of the block + ), + ) { + return; + } match stmt.kind { StmtKind::Expr(expr) => { check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d571b97f51946..f5f8bb21e815b 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -517,3 +517,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2e4348ea338c0..495516c1c2e5d 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -526,3 +526,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} From fd851204993a2f41dd7a835cf0604d5fe16a118c Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 13 Sep 2025 10:09:15 -0400 Subject: [PATCH 0369/1900] Bump version to 1.92.0 --- src/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version b/src/version index 6979a6c0661bf..7f229af964764 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.91.0 +1.92.0 From 3d0eda7af8699b3a9dc51dc339d7d5faae753e97 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 22 Aug 2025 01:38:58 +0000 Subject: [PATCH 0370/1900] Introduce ValueSet. --- Cargo.lock | 1 + compiler/rustc_mir_transform/Cargo.toml | 1 + compiler/rustc_mir_transform/src/gvn.rs | 117 +++++++++++++++++++----- 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4677d34d2a630..328641de1e0c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4235,6 +4235,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", + "hashbrown", "itertools", "rustc_abi", "rustc_arena", diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 08c43a4648c67..511c1960e40b4 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" +hashbrown = "0.15" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20f4..3765dc5c92aef 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -85,8 +85,10 @@ //! that contain `AllocId`s. use std::borrow::Cow; +use std::hash::{Hash, Hasher}; use either::Either; +use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; @@ -94,7 +96,7 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::{FxIndexSet, MutableValues}; +use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -152,6 +154,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + #[debug_format = "_v{}"] struct VnIndex {} } @@ -213,6 +216,89 @@ enum Value<'tcx> { }, } +/// Stores and deduplicates pairs of `(Value, Ty)` into in `VnIndex` numbered values. +/// +/// This data structure is mostly a partial reimplementation of `FxIndexMap`. +/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, +/// like opaque values, address with provenance and non-deterministic constants. +struct ValueSet<'tcx> { + indices: HashTable, + hashes: IndexVec, + values: IndexVec>, + types: IndexVec>, + /// Counter to generate different values. + next_opaque: usize, +} + +impl<'tcx> ValueSet<'tcx> { + fn new(num_values: usize) -> ValueSet<'tcx> { + ValueSet { + indices: HashTable::with_capacity(num_values), + hashes: IndexVec::with_capacity(num_values), + values: IndexVec::with_capacity(num_values), + types: IndexVec::with_capacity(num_values), + next_opaque: 1, + } + } + + /// Insert a `(Value, Ty)` pair to be deduplicated. + /// Returns `true` as second tuple field if this value did not exist previously. + #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + let hash: u64 = { + let mut h = FxHasher::default(); + value.hash(&mut h); + ty.hash(&mut h); + h.finish() + }; + + let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty; + let hasher = |index: &VnIndex| self.hashes[*index]; + match self.indices.entry(hash, eq, hasher) { + Entry::Occupied(entry) => { + let index = *entry.get(); + (index, false) + } + Entry::Vacant(entry) => { + let index = self.hashes.push(hash); + entry.insert(index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + (index, true) + } + } + } + + /// Increment the opaque index counter return a new unique value. + #[inline] + fn next_opaque(&mut self) -> usize { + let next_opaque = self.next_opaque; + self.next_opaque += 1; + next_opaque + } + + /// Return the `Value` associated with the given `VnIndex`. + #[inline] + fn value(&self, index: VnIndex) -> &Value<'tcx> { + &self.values[index] + } + + /// Return the type associated with the given `VnIndex`. + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.types[index] + } + + /// Replace the value associated with `index` with an opaque value. + #[inline] + fn forget(&mut self, index: VnIndex) { + let opaque = self.next_opaque(); + self.values[index] = Value::Opaque(opaque); + } +} + struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, @@ -223,11 +309,9 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec>, - values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, + values: ValueSet<'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, - /// Counter to generate different values. - next_opaque: usize, /// Cache the deref values. derefs: Vec, ssa: &'body SsaLocals, @@ -258,9 +342,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), - values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), + values: ValueSet::new(num_values), evaluated: IndexVec::with_capacity(num_values), - next_opaque: 1, derefs: Vec::new(), ssa, dominators, @@ -274,8 +357,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full((value, ty)); - let index = VnIndex::from_usize(index); + let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. let evaluated = self.eval_to_const(index); @@ -287,17 +369,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { index } - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.next_opaque()); + let value = Value::Opaque(self.values.next_opaque()); self.insert(ty, value) } @@ -311,18 +387,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.next_opaque() }; + let value = Value::Address { place, kind, provenance: self.values.next_opaque() }; self.insert(ty, value) } #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - &self.values.get_index(index.as_usize()).unwrap().0 + self.values.value(index) } #[inline] fn ty(&self, index: VnIndex) -> Ty<'tcx> { - self.values.get_index(index.as_usize()).unwrap().1 + self.values.ty(index) } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -340,7 +416,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.next_opaque(); + let disambiguator = self.values.next_opaque(); // `disambiguator: 0` means deterministic. debug_assert_ne!(disambiguator, 0); disambiguator @@ -374,8 +450,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { - let opaque = self.next_opaque(); - self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); + self.values.forget(deref); } } From 7f34f6e25f143d5c8495aca9ddafb32a87b02303 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 22 Aug 2025 01:50:21 +0000 Subject: [PATCH 0371/1900] Do not hash opaques in GVN. --- compiler/rustc_mir_transform/src/gvn.rs | 129 +++++++++++++++++------- 1 file changed, 90 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3765dc5c92aef..93b60aacbbaca 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -100,7 +100,7 @@ use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{IndexVec, newtype_index}; +use rustc_index::{Idx, IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; @@ -158,6 +158,14 @@ newtype_index! { struct VnIndex {} } +newtype_index! { + /// Counter type to ensure that all unique values are created using `insert_unique`. + #[debug_format = "_o{}"] + struct VnOpaque { + const DETERMINISTIC = 0; + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -169,14 +177,14 @@ enum Value<'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. - Opaque(usize), + Opaque(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is 0 iff the constant is deterministic. - disambiguator: usize, + // `disambiguator` is `DETERMINISTIC` iff the constant is deterministic. + disambiguator: VnOpaque, }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. @@ -195,7 +203,7 @@ enum Value<'tcx> { place: Place<'tcx>, kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. - provenance: usize, + provenance: VnOpaque, }, // Extractions. @@ -227,7 +235,7 @@ struct ValueSet<'tcx> { values: IndexVec>, types: IndexVec>, /// Counter to generate different values. - next_opaque: usize, + next_opaque: VnOpaque, } impl<'tcx> ValueSet<'tcx> { @@ -237,14 +245,45 @@ impl<'tcx> ValueSet<'tcx> { hashes: IndexVec::with_capacity(num_values), values: IndexVec::with_capacity(num_values), types: IndexVec::with_capacity(num_values), - next_opaque: 1, + // The first opaque is 1, as 0 means deterministic constant. + next_opaque: VnOpaque::from_u32(1), } } + /// Insert a `(Value, Ty)` pair without hashing or deduplication. + #[inline] + fn insert_unique( + &mut self, + ty: Ty<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'tcx>, + ) -> VnIndex { + let value = value(self.next_opaque); + self.next_opaque.increment_by(1); + + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => true, + Value::Constant { disambiguator, .. } => disambiguator != DETERMINISTIC, + _ => false, + }); + + let index = self.hashes.push(0); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + index + } + /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. #[allow(rustc::pass_by_value)] // closures take `&VnIndex` fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => false, + Value::Constant { disambiguator, .. } => disambiguator == DETERMINISTIC, + _ => true, + }); + let hash: u64 = { let mut h = FxHasher::default(); value.hash(&mut h); @@ -271,14 +310,6 @@ impl<'tcx> ValueSet<'tcx> { } } - /// Increment the opaque index counter return a new unique value. - #[inline] - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Return the `Value` associated with the given `VnIndex`. #[inline] fn value(&self, index: VnIndex) -> &Value<'tcx> { @@ -294,8 +325,8 @@ impl<'tcx> ValueSet<'tcx> { /// Replace the value associated with `index` with an opaque value. #[inline] fn forget(&mut self, index: VnIndex) { - let opaque = self.next_opaque(); - self.values[index] = Value::Opaque(opaque); + self.values[index] = Value::Opaque(self.next_opaque); + self.next_opaque.increment_by(1); } } @@ -373,8 +404,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.values.next_opaque()); - self.insert(ty, value) + let index = self.values.insert_unique(ty, Value::Opaque); + let _index = self.evaluated.push(None); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index } /// Create a new `Value::Address` distinct from all the others. @@ -387,8 +422,39 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.values.next_opaque() }; - self.insert(ty, value) + let index = + self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index + } + + #[instrument(level = "trace", skip(self), ret)] + fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { + let (index, new) = if value.is_deterministic() { + // The constant is deterministic, no need to disambiguate. + let constant = Value::Constant { value, disambiguator: DETERMINISTIC }; + self.values.insert(value.ty(), constant) + } else { + // Multiple mentions of this constant will yield different values, + // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. + let index = self.values.insert_unique(value.ty(), |disambiguator| { + debug_assert_ne!(disambiguator, DETERMINISTIC); + Value::Constant { value, disambiguator } + }); + (index, true) + }; + if new { + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + } + index } #[inline] @@ -409,33 +475,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.rev_locals[value].push(local); } - fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let disambiguator = if value.is_deterministic() { - // The constant is deterministic, no need to disambiguate. - 0 - } else { - // Multiple mentions of this constant will yield different values, - // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.values.next_opaque(); - // `disambiguator: 0` means deterministic. - debug_assert_ne!(disambiguator, 0); - disambiguator - }; - self.insert(value.ty(), Value::Constant { value, disambiguator }) - } - fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: DETERMINISTIC }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { @@ -1680,7 +1731,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: 0 } = *self.get(index) { + if let Value::Constant { value, disambiguator: DETERMINISTIC } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } From 0a911ec97f9ce39eb52dd3e5da6232e733cdee47 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 9 Sep 2025 01:37:48 +0000 Subject: [PATCH 0372/1900] Stop counting opaques. --- compiler/rustc_mir_transform/src/gvn.rs | 49 ++++++++++++------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 93b60aacbbaca..f8b3e5c6f8794 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -100,7 +100,7 @@ use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{Idx, IndexVec, newtype_index}; +use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; @@ -158,11 +158,16 @@ newtype_index! { struct VnIndex {} } -newtype_index! { - /// Counter type to ensure that all unique values are created using `insert_unique`. - #[debug_format = "_o{}"] - struct VnOpaque { - const DETERMINISTIC = 0; +#[derive(Copy, Clone, Debug, Eq)] +struct VnOpaque; +impl PartialEq for VnOpaque { + fn eq(&self, _: &VnOpaque) -> bool { + unreachable!() + } +} +impl Hash for VnOpaque { + fn hash(&self, _: &mut T) { + unreachable!() } } @@ -183,8 +188,8 @@ enum Value<'tcx> { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is `DETERMINISTIC` iff the constant is deterministic. - disambiguator: VnOpaque, + // `disambiguator` is `None` iff the constant is deterministic. + disambiguator: Option, }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. @@ -234,8 +239,6 @@ struct ValueSet<'tcx> { hashes: IndexVec, values: IndexVec>, types: IndexVec>, - /// Counter to generate different values. - next_opaque: VnOpaque, } impl<'tcx> ValueSet<'tcx> { @@ -245,8 +248,6 @@ impl<'tcx> ValueSet<'tcx> { hashes: IndexVec::with_capacity(num_values), values: IndexVec::with_capacity(num_values), types: IndexVec::with_capacity(num_values), - // The first opaque is 1, as 0 means deterministic constant. - next_opaque: VnOpaque::from_u32(1), } } @@ -257,12 +258,11 @@ impl<'tcx> ValueSet<'tcx> { ty: Ty<'tcx>, value: impl FnOnce(VnOpaque) -> Value<'tcx>, ) -> VnIndex { - let value = value(self.next_opaque); - self.next_opaque.increment_by(1); + let value = value(VnOpaque); debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => true, - Value::Constant { disambiguator, .. } => disambiguator != DETERMINISTIC, + Value::Constant { disambiguator, .. } => disambiguator.is_some(), _ => false, }); @@ -280,7 +280,7 @@ impl<'tcx> ValueSet<'tcx> { fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, - Value::Constant { disambiguator, .. } => disambiguator == DETERMINISTIC, + Value::Constant { disambiguator, .. } => disambiguator.is_none(), _ => true, }); @@ -325,8 +325,7 @@ impl<'tcx> ValueSet<'tcx> { /// Replace the value associated with `index` with an opaque value. #[inline] fn forget(&mut self, index: VnIndex) { - self.values[index] = Value::Opaque(self.next_opaque); - self.next_opaque.increment_by(1); + self.values[index] = Value::Opaque(VnOpaque); } } @@ -436,14 +435,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { let (index, new) = if value.is_deterministic() { // The constant is deterministic, no need to disambiguate. - let constant = Value::Constant { value, disambiguator: DETERMINISTIC }; + let constant = Value::Constant { value, disambiguator: None }; self.values.insert(value.ty(), constant) } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let index = self.values.insert_unique(value.ty(), |disambiguator| { - debug_assert_ne!(disambiguator, DETERMINISTIC); - Value::Constant { value, disambiguator } + let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant { + value, + disambiguator: Some(disambiguator), }); (index, true) }; @@ -479,14 +478,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: DETERMINISTIC }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC }) + self.insert(ty, Value::Constant { value, disambiguator: None }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { @@ -1731,7 +1730,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: DETERMINISTIC } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } From d51aec7781186ae0a7ac1073f672d08729d32f6a Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 17:36:46 +0000 Subject: [PATCH 0373/1900] Add test. --- ...ap.DestinationPropagation.panic-abort.diff | 21 ++++++++ ...p.DestinationPropagation.panic-unwind.diff | 21 ++++++++ tests/mir-opt/dest-prop/aggregate.rs | 49 +++++++++++++++++++ ...ap.DestinationPropagation.panic-abort.diff | 22 +++++++++ ...p.DestinationPropagation.panic-unwind.diff | 22 +++++++++ 5 files changed, 135 insertions(+) create mode 100644 tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.rs create mode 100644 tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff new file mode 100644 index 0000000000000..876c871cddde0 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `rewrap` before DestinationPropagation ++ // MIR for `rewrap` after DestinationPropagation + + fn rewrap() -> (u8,) { + let mut _0: (u8,); + let mut _1: (u8,); + let mut _2: (u8,); + + bb0: { +- (_1.0: u8) = const 0_u8; +- _0 = copy _1; +- _2 = (copy (_0.0: u8),); +- _0 = copy _2; ++ (_0.0: u8) = const 0_u8; ++ nop; ++ _0 = (copy (_0.0: u8),); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff new file mode 100644 index 0000000000000..876c871cddde0 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `rewrap` before DestinationPropagation ++ // MIR for `rewrap` after DestinationPropagation + + fn rewrap() -> (u8,) { + let mut _0: (u8,); + let mut _1: (u8,); + let mut _2: (u8,); + + bb0: { +- (_1.0: u8) = const 0_u8; +- _0 = copy _1; +- _2 = (copy (_0.0: u8),); +- _0 = copy _2; ++ (_0.0: u8) = const 0_u8; ++ nop; ++ _0 = (copy (_0.0: u8),); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.rs b/tests/mir-opt/dest-prop/aggregate.rs new file mode 100644 index 0000000000000..3c14d1f94f690 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rs @@ -0,0 +1,49 @@ +//@ test-mir-pass: DestinationPropagation +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; +use std::mem::MaybeUninit; + +fn dump_var(_: T) {} + +// EMIT_MIR aggregate.rewrap.DestinationPropagation.diff +#[custom_mir(dialect = "runtime")] +fn rewrap() -> (u8,) { + // CHECK-LABEL: fn rewrap( + // CHECK: (_0.0: u8) = const 0_u8; + // CHECK: _0 = (copy (_0.0: u8),); + mir! { + let _1: (u8,); + let _2: (u8,); + { + _1.0 = 0; + RET = _1; + _2 = (RET.0, ); + RET = _2; + Return() + } + } +} + +// EMIT_MIR aggregate.swap.DestinationPropagation.diff +#[custom_mir(dialect = "runtime")] +fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + // CHECK-LABEL: fn swap( + // CHECK: _0 = const + // CHECK: _0 = (copy (_0.1: {{.*}}), copy (_0.0: {{.*}})); + mir! { + let _1: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); + let _2: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); + let _3: (); + { + _1 = const { (MaybeUninit::new([0; 10]), MaybeUninit::new([1; 10])) }; + _2 = _1; + _1 = (_2.1, _2.0); + RET = _1; + Return() + } + } +} diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff new file mode 100644 index 0000000000000..53849573f134c --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff @@ -0,0 +1,22 @@ +- // MIR for `swap` before DestinationPropagation ++ // MIR for `swap` after DestinationPropagation + + fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + let mut _0: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _1: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _2: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _3: (); + + bb0: { +- _1 = const swap::{constant#6}; +- _2 = copy _1; +- _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); +- _0 = copy _1; ++ _0 = const swap::{constant#6}; ++ nop; ++ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff new file mode 100644 index 0000000000000..53849573f134c --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff @@ -0,0 +1,22 @@ +- // MIR for `swap` before DestinationPropagation ++ // MIR for `swap` after DestinationPropagation + + fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + let mut _0: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _1: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _2: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _3: (); + + bb0: { +- _1 = const swap::{constant#6}; +- _2 = copy _1; +- _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); +- _0 = copy _1; ++ _0 = const swap::{constant#6}; ++ nop; ++ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ nop; + return; + } + } + From aee7d703c58b68611785684bbb8e402c1471992b Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 18:07:22 +0000 Subject: [PATCH 0374/1900] Mark reads in statements to avoid overlapping assingments. --- compiler/rustc_mir_transform/src/dest_prop.rs | 29 ++++++++++++------- ...ap.DestinationPropagation.panic-abort.diff | 6 ++-- ...p.DestinationPropagation.panic-unwind.diff | 6 ++-- tests/mir-opt/dest-prop/aggregate.rs | 6 ++-- ...ap.DestinationPropagation.panic-abort.diff | 4 +-- ...p.DestinationPropagation.panic-unwind.diff | 4 +-- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 9ba2d274691de..7e6f39881b8b8 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -567,13 +567,15 @@ fn save_as_intervals<'tcx>( // the written-to locals as live in the second half of the statement. // We also ensure that operands read by terminators conflict with writes by that terminator. // For instance a function call may read args after having written to the destination. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::Use | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::Use | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::NonUse => {} } } - DefUse::NonUse => {} }) .visit_terminator(term, loc); @@ -590,13 +592,20 @@ fn save_as_intervals<'tcx>( append_at(&mut values, &state, twostep); // Ensure we have a non-zero live range even for dead stores. This is done by marking // all the written-to locals as live in the second half of the statement. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + let is_simple_assignment = + matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::Use if !is_simple_assignment => { + values.insert(relevant, twostep); + } + DefUse::Use | DefUse::NonUse => {} } } - DefUse::Use | DefUse::NonUse => {} }) .visit_statement(stmt, loc); diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff index 876c871cddde0..e80660f176b7b 100644 --- a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff @@ -9,12 +9,10 @@ bb0: { - (_1.0: u8) = const 0_u8; - _0 = copy _1; -- _2 = (copy (_0.0: u8),); -- _0 = copy _2; + (_0.0: u8) = const 0_u8; + nop; -+ _0 = (copy (_0.0: u8),); -+ nop; + _2 = (copy (_0.0: u8),); + _0 = copy _2; return; } } diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff index 876c871cddde0..e80660f176b7b 100644 --- a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff @@ -9,12 +9,10 @@ bb0: { - (_1.0: u8) = const 0_u8; - _0 = copy _1; -- _2 = (copy (_0.0: u8),); -- _0 = copy _2; + (_0.0: u8) = const 0_u8; + nop; -+ _0 = (copy (_0.0: u8),); -+ nop; + _2 = (copy (_0.0: u8),); + _0 = copy _2; return; } } diff --git a/tests/mir-opt/dest-prop/aggregate.rs b/tests/mir-opt/dest-prop/aggregate.rs index 3c14d1f94f690..636852159eb4c 100644 --- a/tests/mir-opt/dest-prop/aggregate.rs +++ b/tests/mir-opt/dest-prop/aggregate.rs @@ -14,7 +14,8 @@ fn dump_var(_: T) {} fn rewrap() -> (u8,) { // CHECK-LABEL: fn rewrap( // CHECK: (_0.0: u8) = const 0_u8; - // CHECK: _0 = (copy (_0.0: u8),); + // CHECK: _2 = (copy (_0.0: u8),); + // CHECK: _0 = copy _2; mir! { let _1: (u8,); let _2: (u8,); @@ -33,7 +34,8 @@ fn rewrap() -> (u8,) { fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { // CHECK-LABEL: fn swap( // CHECK: _0 = const - // CHECK: _0 = (copy (_0.1: {{.*}}), copy (_0.0: {{.*}})); + // CHECK: _2 = copy _0; + // CHECK: _0 = (copy (_2.1: {{.*}}), copy (_2.0: {{.*}})); mir! { let _1: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); let _2: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff index 53849573f134c..3aaad3aaf6920 100644 --- a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff @@ -13,8 +13,8 @@ - _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); - _0 = copy _1; + _0 = const swap::{constant#6}; -+ nop; -+ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ _2 = copy _0; ++ _0 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); + nop; return; } diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff index 53849573f134c..3aaad3aaf6920 100644 --- a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff @@ -13,8 +13,8 @@ - _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); - _0 = copy _1; + _0 = const swap::{constant#6}; -+ nop; -+ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ _2 = copy _0; ++ _0 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); + nop; return; } From 996962243670f15a88910f5c52b30fbc7b409138 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 13 Sep 2025 20:16:58 +0200 Subject: [PATCH 0375/1900] Improve the documentation around ZERO_AR_DATE --- compiler/rustc_target/src/spec/base/apple/mod.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index ecc7426416059..39e604bcce7ce 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -158,12 +158,22 @@ pub(crate) fn base( SplitDebuginfo::Off, ]), + // Tell the linker that we would like it to avoid irreproducible binaries. + // // This environment variable is pretty magical but is intended for // producing deterministic builds. This was first discovered to be used // by the `ar` tool as a way to control whether or not mtime entries in - // the archive headers were set to zero or not. It appears that - // eventually the linker got updated to do the same thing and now reads - // this environment variable too in recent versions. + // the archive headers were set to zero or not. + // + // In `ld64-351.8`, shipped with Xcode 9.3, the linker was updated to + // read this flag too. Linker versions that don't support this flag + // may embed modification timestamps in binaries (especially in debug + // information). + // + // A cleaner alternative would be to pass the `-reproducible` flag, + // though that is only supported since `ld64-819.6` shipped with Xcode + // 14, which is too new for our minimum supported version: + // https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#host-tooling // // For some more info see the commentary on #47086 link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]), From cd196aa72a638b627ecdcfd3495b986300af83f1 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 13 Sep 2025 18:27:10 +0200 Subject: [PATCH 0376/1900] Implement `Debug` for `SourceText` This is useful for using it with `dbg!()`. --- clippy_utils/src/source.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index e675291b6f3a7..638d329031231 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -215,6 +215,11 @@ impl fmt::Display for SourceText { self.as_str().fmt(f) } } +impl fmt::Debug for SourceText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} fn get_source_range(sm: &SourceMap, sp: Range) -> Option { let start = sm.lookup_byte_offset(sp.start); From 1ebf69d1b1a94e99c01680514571c41d0b864c15 Mon Sep 17 00:00:00 2001 From: Jo Bates <29763794+jbatez@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:49:13 -0700 Subject: [PATCH 0377/1900] initial implementation of the darwin_objc unstable feature --- compiler/rustc_attr_parsing/messages.ftl | 8 + .../src/attributes/codegen_attrs.rs | 69 +++++- compiler/rustc_attr_parsing/src/context.rs | 6 +- .../src/session_diagnostics.rs | 28 +++ compiler/rustc_codegen_llvm/src/base.rs | 24 +- compiler/rustc_codegen_llvm/src/common.rs | 14 ++ compiler/rustc_codegen_llvm/src/consts.rs | 226 +++++++++++++++++- compiler/rustc_codegen_llvm/src/context.rs | 81 ++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + .../rustc_codegen_ssa/src/codegen_attrs.rs | 6 + compiler/rustc_feature/src/builtin_attrs.rs | 8 + .../rustc_hir/src/attrs/data_structures.rs | 6 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 + .../src/middle/codegen_fn_attrs.rs | 6 + compiler/rustc_passes/src/check_attr.rs | 2 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/lib.rs | 1 + library/core/src/os/darwin/mod.rs | 19 ++ library/core/src/os/darwin/objc.rs | 113 +++++++++ library/core/src/os/mod.rs | 24 ++ library/std/src/os/darwin/mod.rs | 2 + library/std/src/os/darwin/objc.rs | 13 + src/tools/tidy/src/pal.rs | 1 + .../codegen-llvm/auxiliary/darwin_objc_aux.rs | 27 +++ tests/codegen-llvm/darwin-no-objc.rs | 52 ++++ tests/codegen-llvm/darwin-objc-abi-v1.rs | 100 ++++++++ tests/codegen-llvm/darwin-objc-abi-v2.rs | 185 ++++++++++++++ tests/codegen-llvm/darwin-objc-cross-crate.rs | 58 +++++ tests/ui/README.md | 6 + tests/ui/darwin-objc/darwin-objc-bad-arg.rs | 36 +++ .../ui/darwin-objc/darwin-objc-bad-arg.stderr | 50 ++++ tests/ui/darwin-objc/darwin-objc-bad-const.rs | 17 ++ .../darwin-objc/darwin-objc-bad-const.stderr | 19 ++ tests/ui/darwin-objc/darwin-objc-bad-ref.rs | 31 +++ .../ui/darwin-objc/darwin-objc-bad-ref.stderr | 39 +++ .../darwin-objc/darwin-objc-class-selector.rs | 31 +++ tests/ui/darwin-objc/darwin-objc-class.rs | 39 +++ tests/ui/darwin-objc/darwin-objc-selector.rs | 36 +++ 39 files changed, 1381 insertions(+), 11 deletions(-) create mode 100644 library/core/src/os/darwin/mod.rs create mode 100644 library/core/src/os/darwin/objc.rs create mode 100644 library/core/src/os/mod.rs create mode 100644 library/std/src/os/darwin/objc.rs create mode 100644 tests/codegen-llvm/auxiliary/darwin_objc_aux.rs create mode 100644 tests/codegen-llvm/darwin-no-objc.rs create mode 100644 tests/codegen-llvm/darwin-objc-abi-v1.rs create mode 100644 tests/codegen-llvm/darwin-objc-abi-v2.rs create mode 100644 tests/codegen-llvm/darwin-objc-cross-crate.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-arg.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-arg.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-const.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-const.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-ref.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-ref.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-class-selector.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-class.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-selector.rs diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 839a5d23c3b4d..81ec17077c13c 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -122,6 +122,14 @@ attr_parsing_null_on_export = `export_name` may not contain null characters attr_parsing_null_on_link_section = `link_section` may not contain null characters +attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters + +attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters + +attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal + +attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal + attr_parsing_repr_ident = meta item in `repr` must be an identifier diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index d5d51f2e79ab8..262b8213977b3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -2,7 +2,10 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; use super::prelude::*; -use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport}; +use crate::session_diagnostics::{ + NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, + ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, +}; pub(crate) struct OptimizeParser; @@ -150,6 +153,70 @@ impl SingleAttributeParser for ExportNameParser { } } +pub(crate) struct ObjcClassParser; + +impl SingleAttributeParser for ObjcClassParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(classname) = nv.value_as_str() else { + // `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if classname.as_str().contains('\0') { + // `#[rustc_objc_class = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcClass { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcClass { classname, span: cx.attr_span }) + } +} + +pub(crate) struct ObjcSelectorParser; + +impl SingleAttributeParser for ObjcSelectorParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(methname) = nv.value_as_str() else { + // `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if methname.as_str().contains('\0') { + // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcSelector { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span }) + } +} + #[derive(Default)] pub(crate) struct NakedParser { span: Option, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b3ab1d3edd6d2..6c940e3dcaf99 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{ use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, - NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, - UsedParser, + NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser, + TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -187,6 +187,8 @@ attribute_parsers!( Single, Single, Single, + Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 32ea9005a97d0..2c2b14c8a68bc 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -459,6 +459,34 @@ pub(crate) struct NullOnLinkSection { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_class)] +pub(crate) struct NullOnObjcClass { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_selector)] +pub(crate) struct NullOnObjcSelector { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_class_expected_string_literal)] +pub(crate) struct ObjcClassExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_selector_expected_string_literal)] +pub(crate) struct ObjcSelectorExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 9cc5d8dbc21cf..978134cc32b1c 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -109,18 +109,36 @@ pub(crate) fn compile_codegen_unit( attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } + // Define Objective-C module info and module flags. Note, the module info will + // also be added to the `llvm.compiler.used` variable, created later. + // + // These are only necessary when we need the linker to do its Objective-C-specific + // magic. We could theoretically do it unconditionally, but at a slight cost to linker + // performance in the common case where it's unnecessary. + if !cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty() { + if cx.objc_abi_version() == 1 { + cx.define_objc_module_info(); + } + cx.add_objc_module_flags(); + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { cx.coverageinfo_finalize(); } - // Create the llvm.used and llvm.compiler.used variables. + // Create the llvm.used variable. if !cx.used_statics.is_empty() { cx.create_used_variable_impl(c"llvm.used", &cx.used_statics); } - if !cx.compiler_used_statics.is_empty() { - cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics); + + // Create the llvm.compiler.used variable. + { + let compiler_used_statics = cx.compiler_used_statics.borrow(); + if !compiler_used_statics.is_empty() { + cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics); + } } // Run replace-all-uses-with for statics that need it. This must diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 11b79a7fe68c6..aa2df46329f22 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -108,6 +108,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { bytes_in_context(self.llcx(), bytes) } + pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value { + null_terminate_bytes_in_context(self.llcx(), bytes) + } + pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { unsafe { let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow"); @@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> & } } +pub(crate) fn null_terminate_bytes_in_context<'ll>( + llcx: &'ll llvm::Context, + bytes: &[u8], +) -> &'ll Value { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), FALSE) + } +} + pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow"); unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index dc9bb743560dd..a110ecbb75d95 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; +use rustc_span::Symbol; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> { } g + } else if let Some(classname) = fn_attrs.objc_class { + self.get_objc_classref(classname) + } else if let Some(methname) = fn_attrs.objc_selector { + self.get_objc_selref(methname) } else { check_and_apply_linkage(self, fn_attrs, llty, sym, def_id) }; @@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> { /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, /// an array of ptr. - pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) { - self.compiler_used_statics.push(global); + pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) { + self.compiler_used_statics.borrow_mut().push(global); + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + fn define_objc_classname(&self, classname: &str) -> &'ll Value { + assert_eq!(self.objc_abi_version(), 1); + + let llval = self.null_terminate_const_bytes(classname.as_bytes()); + let llty = self.val_ty(llval); + let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__TEXT,__cstring,cstring_literals"); + llvm::LLVMSetGlobalConstant(g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(g); + + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCNonFragileABITypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052 + fn get_objc_class_t(&self) -> &'ll Type { + if let Some(class_t) = self.objc_class_t.get() { + return class_t; + } + + assert_eq!(self.objc_abi_version(), 2); + + // struct _class_t { + // struct _class_t* isa; + // struct _class_t* const superclass; + // void* cache; + // IMP* vtable; + // struct class_ro_t* ro; + // } + + let class_t = self.type_named_struct("struct._class_t"); + let els = [self.type_ptr(); 5]; + let packed = false; + self.set_struct_body(class_t, &els, packed); + + self.objc_class_t.set(Some(class_t)); + class_t + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + fn get_objc_classref(&self, classname: Symbol) -> &'ll Value { + let mut classrefs = self.objc_classrefs.borrow_mut(); + if let Some(classref) = classrefs.get(&classname).copied() { + return classref; + } + + let g = match self.objc_abi_version() { + 1 => { + // See Clang's `CGObjCMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205 + let llval = self.define_objc_classname(classname.as_str()); + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + 2 => { + // See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423 + let llval = { + let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str()); + let extern_llty = self.get_objc_class_t(); + self.declare_global(&extern_sym, extern_llty) + }; + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + _ => unreachable!(), + }; + + classrefs.insert(classname, g); + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + // + // Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub + // functions. We don't currently do that. The code we generate is closer to what Apple Clang + // generates with the `-fno-objc-msgsend-selector-stubs` option. + fn get_objc_selref(&self, methname: Symbol) -> &'ll Value { + let mut selrefs = self.objc_selrefs.borrow_mut(); + if let Some(selref) = selrefs.get(&methname).copied() { + return selref; + } + + let abi_version = self.objc_abi_version(); + + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes()); + let methname_llty = self.val_ty(methname_llval); + let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_"); + let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", methname_sym); + }); + set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi); + llvm::set_initializer(methname_g, methname_llval); + llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage); + llvm::set_section( + methname_g, + match abi_version { + 1 => c"__TEXT,__cstring,cstring_literals", + 2 => c"__TEXT,__objc_methname,cstring_literals", + _ => unreachable!(), + }, + ); + llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(methname_g); + + // See Clang's `CGObjCMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243 + // And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586 + let selref_llval = methname_g; + let selref_llty = self.type_ptr(); + let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_"); + let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", selref_sym); + }); + set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(selref_g, selref_llval); + llvm::set_externally_initialized(selref_g, true); + llvm::set_linkage( + selref_g, + match abi_version { + 1 => llvm::Linkage::PrivateLinkage, + 2 => llvm::Linkage::InternalLinkage, + _ => unreachable!(), + }, + ); + llvm::set_section( + selref_g, + match abi_version { + 1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip", + 2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip", + _ => unreachable!(), + }, + ); + self.add_compiler_used_global(selref_g); + + selrefs.insert(methname, selref_g); + selref_g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCTypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936 + // And Clang's `CGObjCMac::EmitModuleInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151 + pub(crate) fn define_objc_module_info(&mut self) { + assert_eq!(self.objc_abi_version(), 1); + + // struct _objc_module { + // long version; // Hardcoded to 7 in Clang. + // long size; // sizeof(struct _objc_module) + // char* name; // Hardcoded to classname "" in Clang. + // struct _objc_symtab* symtab; // Null without class or category definitions. + // } + + let llty = self.type_named_struct("struct._objc_module"); + let i32_llty = self.type_i32(); + let ptr_llty = self.type_ptr(); + let packed = false; + self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed); + + let version = self.const_uint(i32_llty, 7); + let size = self.const_uint(i32_llty, 16); + let name = self.define_objc_classname(""); + let symtab = self.const_null(ptr_llty); + let llval = crate::common::named_struct(llty, &[version, size, name, symtab]); + + let sym = "OBJC_MODULES"; + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip"); + + self.add_compiler_used_global(g); } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a4d..2f309d4694cef 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -26,7 +26,7 @@ use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; use rustc_span::source_map::Spanned; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; @@ -119,7 +119,7 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Statics that will be placed in the llvm.compiler.used variable /// See for details - pub compiler_used_statics: Vec<&'ll Value>, + pub compiler_used_statics: RefCell>, /// Mapping of non-scalar types to llvm types. pub type_lowering: RefCell, Option), &'ll Type>>, @@ -146,6 +146,15 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// `global_asm!` needs to be able to find this new global so that it can /// compute the correct mangled symbol name to insert into the asm. pub renamed_statics: RefCell>, + + /// Cached Objective-C class type + pub objc_class_t: Cell>, + + /// Cache of Objective-C class references + pub objc_classrefs: RefCell>, + + /// Cache of Objective-C selector references + pub objc_selrefs: RefCell>, } fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { @@ -644,7 +653,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: Vec::new(), - compiler_used_statics: Vec::new(), + compiler_used_statics: Default::default(), type_lowering: Default::default(), scalar_lltypes: Default::default(), coverage_cx, @@ -655,6 +664,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), renamed_statics: Default::default(), + objc_class_t: Cell::new(None), + objc_classrefs: Default::default(), + objc_selrefs: Default::default(), }, PhantomData, ) @@ -679,6 +691,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_linkage(g, llvm::Linkage::AppendingLinkage); llvm::set_section(g, c"llvm.metadata"); } + + /// The Objective-C ABI that is used. + /// + /// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC. + pub(crate) fn objc_abi_version(&self) -> u32 { + assert!(self.tcx.sess.target.is_like_darwin); + if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" { + // 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI"). + 1 + } else { + // All other Darwin-like targets we support use ABI version 2 + // (a.k.a the "non-fragile ABI"). + 2 + } + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::EmitImageInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 + pub(crate) fn add_objc_module_flags(&self) { + let abi_version = self.objc_abi_version(); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Version", + abi_version, + ); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Version", + 0, + ); + + llvm::add_module_flag_str( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Section", + match abi_version { + 1 => "__OBJC,__image_info,regular", + 2 => "__DATA,__objc_imageinfo,regular,no_dead_strip", + _ => unreachable!(), + }, + ); + + if self.tcx.sess.target.env == "sim" { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Is Simulated", + 1 << 5, + ); + } + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Class Properties", + 1 << 6, + ); + } } impl<'ll> SimpleCx<'ll> { pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f08..e125aba6afc75 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1237,6 +1237,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind); + pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool); // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d6974e22c85c5..1115d82fa85d3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -258,6 +258,10 @@ pub(crate) fn set_alignment(llglobal: &Value, align: Align) { } } +pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) { + LLVMSetExternallyInitialized(llglobal, is_ext_init.to_llvm_bool()); +} + /// Get the `name`d comdat from `llmod` and assign it to `llglobal`. /// /// Inserts the comdat into `llmod` if it does not exist. diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index dc500c363f4a7..32ae810ecc892 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -296,6 +296,12 @@ fn process_builtin_attrs( AttributeKind::Sanitize { span, .. } => { interesting_spans.sanitize = Some(*span); } + AttributeKind::ObjcClass { classname, .. } => { + codegen_fn_attrs.objc_class = Some(*classname); + } + AttributeKind::ObjcSelector { methname, .. } => { + codegen_fn_attrs.objc_selector = Some(*methname); + } _ => {} } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 129ab7eccb577..99d6e93faa9ef 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1057,6 +1057,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_objc_class, Normal, template!(NameValueStr: "ClassName"), ErrorPreceding, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_objc_selector, Normal, template!(NameValueStr: "methodName"), ErrorPreceding, + EncodeCrossCrate::No, + ), // ========================================================================== // Internal attributes, Macro related: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ea11a99efbc35..04b144abd03df 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -594,6 +594,12 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[rustc_objc_class]` + ObjcClass { classname: Symbol, span: Span }, + + /// Represents `#[rustc_objc_selector]` + ObjcSelector { methname: Symbol, span: Span }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 55521c1585402..cb4feeb05f1a0 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -69,6 +69,8 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + ObjcClass { .. } => No, + ObjcSelector { .. } => No, Optimize(..) => No, ParenSugar(..) => No, PassByValue(..) => Yes, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 8b4503073b0de..f0d96c6ac8981 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -69,6 +69,10 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option, + /// The `#[rustc_objc_class = "..."]` attribute. + pub objc_class: Option, + /// The `#[rustc_objc_selector = "..."]` attribute. + pub objc_selector: Option, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] @@ -185,6 +189,8 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, + objc_class: None, + objc_selector: None, } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2562d2e0b83c5..60f575cb84415 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -276,6 +276,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::PatternComplexityLimit { .. } | AttributeKind::NoCore { .. } | AttributeKind::NoStd { .. } + | AttributeKind::ObjcClass { .. } + | AttributeKind::ObjcSelector { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6d..668e80a26b44e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1917,6 +1917,8 @@ symbols! { rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, + rustc_objc_class, + rustc_objc_selector, rustc_object_lifetime_default, rustc_on_unimplemented, rustc_outlives, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 86a68e18b0af4..5d52bfb1b125f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -313,6 +313,7 @@ pub mod io; pub mod iter; pub mod net; pub mod option; +pub mod os; pub mod panic; pub mod panicking; #[unstable(feature = "pattern_type_macro", issue = "123646")] diff --git a/library/core/src/os/darwin/mod.rs b/library/core/src/os/darwin/mod.rs new file mode 100644 index 0000000000000..8426d82b8ce3e --- /dev/null +++ b/library/core/src/os/darwin/mod.rs @@ -0,0 +1,19 @@ +//! Platform-specific extensions to `core` for Darwin / Apple platforms. +//! +//! This is available on the following operating systems: +//! - macOS +//! - iOS +//! - tvOS +//! - watchOS +//! - visionOS +//! +//! Note: This module is called "Darwin" as that's the name of the underlying +//! core OS of the above operating systems, but it should not be confused with +//! the `-darwin` suffix in the `x86_64-apple-darwin` and +//! `aarch64-apple-darwin` target names, which are mostly named that way for +//! legacy reasons. + +#![unstable(feature = "darwin_objc", issue = "145496")] +#![doc(cfg(target_vendor = "apple"))] + +pub mod objc; diff --git a/library/core/src/os/darwin/objc.rs b/library/core/src/os/darwin/objc.rs new file mode 100644 index 0000000000000..928cb54e82c79 --- /dev/null +++ b/library/core/src/os/darwin/objc.rs @@ -0,0 +1,113 @@ +//! Defines types and macros for Objective-C interoperability. + +#![unstable(feature = "darwin_objc", issue = "145496")] +#![allow(nonstandard_style)] + +use crate::fmt; + +/// Equivalent to Objective-C’s `struct objc_class` type. +#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +pub enum objc_class { + #[unstable( + feature = "objc_class_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant1, + #[unstable( + feature = "objc_class_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant2, +} + +impl fmt::Debug for objc_class { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("objc_class").finish() + } +} + +/// Equivalent to Objective-C’s `struct objc_selector` type. +#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +pub enum objc_selector { + #[unstable( + feature = "objc_selector_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant1, + #[unstable( + feature = "objc_selector_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant2, +} + +impl fmt::Debug for objc_selector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("objc_selector").finish() + } +} + +/// Equivalent to Objective-C’s `Class` type. +pub type Class = *mut objc_class; + +/// Equivalent to Objective-C’s `SEL` type. +pub type SEL = *mut objc_selector; + +/// Gets a reference to an Objective-C class. +/// +/// This macro will yield an expression of type [`Class`] for the given class name string literal. +/// +/// # Example +/// +/// ```no_run +/// #![feature(darwin_objc)] +/// use core::os::darwin::objc; +/// +/// let string_class = objc::class!("NSString"); +/// ``` +#[allow_internal_unstable(rustc_attrs)] +pub macro class($classname:expr) {{ + // Since static Objective-C class references actually end up with multiple definitions + // across dylib boundaries, we only expose the value of the static and don't provide a way to + // get the address of or a reference to the static. + unsafe extern "C" { + #[rustc_objc_class = $classname] + safe static VAL: $crate::os::darwin::objc::Class; + } + VAL +}} + +/// Gets a reference to an Objective-C selector. +/// +/// This macro will yield an expression of type [`SEL`] for the given method name string literal. +/// +/// It is similar to Objective-C’s `@selector` directive. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(darwin_objc)] +/// use core::os::darwin::objc; +/// +/// let alloc_sel = objc::selector!("alloc"); +/// let init_sel = objc::selector!("initWithCString:encoding:"); +/// ``` +#[allow_internal_unstable(rustc_attrs)] +pub macro selector($methname:expr) {{ + // Since static Objective-C selector references actually end up with multiple definitions + // across dylib boundaries, we only expose the value of the static and don't provide a way to + // get the address of or a reference to the static. + unsafe extern "C" { + #[rustc_objc_selector = $methname] + safe static VAL: $crate::os::darwin::objc::SEL; + } + VAL +}} diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs new file mode 100644 index 0000000000000..897f59f530ed0 --- /dev/null +++ b/library/core/src/os/mod.rs @@ -0,0 +1,24 @@ +//! OS-specific functionality. + +#![unstable(feature = "darwin_objc", issue = "145496")] + +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod darwin {} + +// darwin +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(target_vendor = "apple", doc))] +pub mod darwin; diff --git a/library/std/src/os/darwin/mod.rs b/library/std/src/os/darwin/mod.rs index 3b1bd974fa313..ff184f477fb0e 100644 --- a/library/std/src/os/darwin/mod.rs +++ b/library/std/src/os/darwin/mod.rs @@ -17,6 +17,8 @@ #![doc(cfg(target_vendor = "apple"))] pub mod fs; +pub mod objc; + // deprecated, but used for public reexport under `std::os::unix::raw`, as // well as `std::os::macos`/`std::os::ios`, because those modules precede the // decision to remove these. diff --git a/library/std/src/os/darwin/objc.rs b/library/std/src/os/darwin/objc.rs new file mode 100644 index 0000000000000..a4b31fee7c582 --- /dev/null +++ b/library/std/src/os/darwin/objc.rs @@ -0,0 +1,13 @@ +//! Defines types and macros for Objective-C interoperability. +//! +//! This module re-exports all the items in [`core::os::darwin::objc`]. +//! +//! [`core::os::darwin::objc`]: ../../../../core/os/darwin/objc/index.html "mod core::os::darwin::objc" + +#![unstable(feature = "darwin_objc", issue = "145496")] + +// We can't generate an intra-doc link for this automatically since `core::os::darwin` isn't +// compiled into `core` on every platform even though it's documented on every platform. +// We just link to it directly in the module documentation above instead. +#[cfg(not(doc))] +pub use core::os::darwin::objc::*; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 5b8b44429bbc0..991ad55809cc3 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -53,6 +53,7 @@ const EXCEPTION_PATHS: &[&str] = &[ // core::ffi contains platform-specific type and linkage configuration "library/core/src/ffi/mod.rs", "library/core/src/ffi/primitives.rs", + "library/core/src/os", // Platform-specific public interfaces "library/std/src/sys", // Platform-specific code for std lives here. "library/std/src/os", // Platform-specific public interfaces // Temporary `std` exceptions diff --git a/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs b/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs new file mode 100644 index 0000000000000..3c35d003c8af6 --- /dev/null +++ b/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs @@ -0,0 +1,27 @@ +#![crate_type = "lib"] +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[inline(always)] +pub fn inline_get_object_class() -> objc::Class { + objc::class!("NSObject") +} + +#[inline(always)] +pub fn inline_get_alloc_selector() -> objc::SEL { + objc::selector!("alloc") +} + +#[inline(never)] +pub fn never_inline_get_string_class() -> objc::Class { + objc::class!("NSString") +} + +#[inline(never)] +pub fn never_inline_get_init_selector() -> objc::SEL { + objc::selector!("init") +} diff --git a/tests/codegen-llvm/darwin-no-objc.rs b/tests/codegen-llvm/darwin-no-objc.rs new file mode 100644 index 0000000000000..fda3671fb6d84 --- /dev/null +++ b/tests/codegen-llvm/darwin-no-objc.rs @@ -0,0 +1,52 @@ +// Test that we don't generate Objective-C definitions or image info unnecessarily. + +//@ add-core-stubs +//@ revisions: i686_apple_darwin +//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin +//@ [i686_apple_darwin] needs-llvm-components: x86 +//@ revisions: x86_64_macos +//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin +//@ [x86_64_macos] needs-llvm-components: x86 +//@ revisions: aarch64_macos +//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin +//@ [aarch64_macos] needs-llvm-components: aarch64 +//@ revisions: i386_ios +//@ [i386_ios] compile-flags: --target i386-apple-ios +//@ [i386_ios] needs-llvm-components: x86 +//@ revisions: x86_64_ios +//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios +//@ [x86_64_ios] needs-llvm-components: x86 +//@ revisions: armv7s_ios +//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios +//@ [armv7s_ios] needs-llvm-components: arm +//@ revisions: aarch64_ios +//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios +//@ [aarch64_ios] needs-llvm-components: aarch64 +//@ revisions: aarch64_ios_sim +//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim +//@ [aarch64_ios_sim] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn foo() {} + +// CHECK-NOT: %struct._class_t +// CHECK-NOT: %struct._objc_module +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @"OBJC_CLASS_$_{{[0-9A-Z_a-z]+}}" +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ +// CHECK-NOT: @OBJC_MODULES + +// CHECK-NOT: !"Objective-C Version" +// CHECK-NOT: !"Objective-C Image Info Version" +// CHECK-NOT: !"Objective-C Image Info Section" +// CHECK-NOT: !"Objective-C Is Simulated" +// CHECK-NOT: !"Objective-C Class Properties" diff --git a/tests/codegen-llvm/darwin-objc-abi-v1.rs b/tests/codegen-llvm/darwin-objc-abi-v1.rs new file mode 100644 index 0000000000000..0fc1de9332ac4 --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-abi-v1.rs @@ -0,0 +1,100 @@ +// ignore-tidy-linelength +//@ add-core-stubs +//@ revisions: i686_apple_darwin +//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin +//@ [i686_apple_darwin] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn get_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_class_again() -> *mut () { + // Codegen should de-duplicate this class with the one from get_class above. + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector_again() -> *mut () { + // Codegen should de-duplicate this selector with the one from get_selector above. + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "OtherClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "otherMethod"] + safe static VAL: *mut (); + } + VAL +} + +// CHECK: %struct._objc_module = type { i32, i32, ptr, ptr } + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [8 x i8] c"MyClass\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4 +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @OBJC_CLASS_REFERENCES_ + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4 +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [11 x i8] c"OtherClass\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4 + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [1 x i8] zeroinitializer, section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_MODULES = private global %struct._objc_module { i32 7, i32 16, ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, ptr null }, section "__OBJC,__module_info,regular,no_dead_strip", align 4 + +// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 1} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__OBJC,__image_info,regular"} +// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/codegen-llvm/darwin-objc-abi-v2.rs b/tests/codegen-llvm/darwin-objc-abi-v2.rs new file mode 100644 index 0000000000000..f142371d5825b --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-abi-v2.rs @@ -0,0 +1,185 @@ +// ignore-tidy-linelength +//@ add-core-stubs +//@ revisions: x86_64_macos +//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin +//@ [x86_64_macos] needs-llvm-components: x86 +//@ revisions: aarch64_macos +//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin +//@ [aarch64_macos] needs-llvm-components: aarch64 +//@ revisions: i386_ios +//@ [i386_ios] compile-flags: --target i386-apple-ios +//@ [i386_ios] needs-llvm-components: x86 +//@ revisions: x86_64_ios +//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios +//@ [x86_64_ios] needs-llvm-components: x86 +//@ revisions: armv7s_ios +//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios +//@ [armv7s_ios] needs-llvm-components: arm +//@ revisions: aarch64_ios +//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios +//@ [aarch64_ios] needs-llvm-components: aarch64 +//@ revisions: aarch64_ios_sim +//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim +//@ [aarch64_ios_sim] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn get_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_class_again() -> *mut () { + // Codegen should de-duplicate this class with the one from get_class above. + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector_again() -> *mut () { + // Codegen should de-duplicate this selector with the one from get_selector above. + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "OtherClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "otherMethod"] + safe static VAL: *mut (); + } + VAL +} + +// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr } + +// CHECK: @"OBJC_CLASS_$_MyClass" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_MyClass", section "__DATA,__objc_classrefs,regular,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 +// CHECK-NOT: @"OBJC_CLASS_$_MyClass" +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_ + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ + +// CHECK: @"OBJC_CLASS_$_OtherClass" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_OtherClass", section "__DATA,__objc_classrefs,regular,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @OBJC_MODULES + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} + +// x86_64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// i386_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// x86_64_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// armv7s_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_ios_sim: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/codegen-llvm/darwin-objc-cross-crate.rs b/tests/codegen-llvm/darwin-objc-cross-crate.rs new file mode 100644 index 0000000000000..74ad9a27346d1 --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-cross-crate.rs @@ -0,0 +1,58 @@ +// Test that Objective-C class and selector references inlined across crates +// get defined in this CGU but non-inline references don't. + +// ignore-tidy-linelength +//@ aux-build: darwin_objc_aux.rs +//@ revisions: x86_64_macos aarch64_macos +//@ [x86_64_macos] only-x86_64-apple-darwin +//@ [aarch64_macos] only-aarch64-apple-darwin + +#![crate_type = "lib"] +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +extern crate darwin_objc_aux as aux; + +#[no_mangle] +pub fn get_object_class() -> objc::Class { + aux::inline_get_object_class() +} + +#[no_mangle] +pub fn get_alloc_selector() -> objc::SEL { + aux::inline_get_alloc_selector() +} + +#[no_mangle] +pub fn get_string_class() -> objc::Class { + aux::never_inline_get_string_class() +} + +#[no_mangle] +pub fn get_init_selector() -> objc::SEL { + aux::never_inline_get_init_selector() +} + +// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr } + +// CHECK: @"OBJC_CLASS_$_NSObject" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSObject", section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [6 x i8] c"alloc\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8 + +// CHECK-NOT: @"OBJC_CLASS_$_NSString" = external global %struct._class_t +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSString" + +// CHECK-NOT: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [5 x i8] c"init\00" +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}} + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", align 8 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 8 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} +// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/ui/README.md b/tests/ui/README.md index 66c1bb905a792..3b28ef694dd30 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -350,6 +350,12 @@ Tests for FFI with C varargs (`va_list`). Tests for detection and handling of cyclic trait dependencies. +## `tests/ui/darwin-objc/`: Darwin Objective-C + +Tests exercising `#![feature(darwin_objc)]`. + +See [Tracking Issue for `darwin_objc` #145496](https://github.com/rust-lang/rust/issues/145496). + ## `tests/ui/dataflow_const_prop/` Contains a single regression test for const prop in `SwitchInt` pass crashing when `ptr2int` transmute is involved. diff --git a/tests/ui/darwin-objc/darwin-objc-bad-arg.rs b/tests/ui/darwin-objc/darwin-objc-bad-arg.rs new file mode 100644 index 0000000000000..70eb83aa052d1 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-arg.rs @@ -0,0 +1,36 @@ +// Test that `objc::class!` and `objc::selector!` only take string literals. + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub fn main() { + let s = "NSObject"; + objc::class!(s); + //~^ ERROR attribute value must be a literal + + objc::class!(NSObject); + //~^ ERROR attribute value must be a literal + + objc::class!(123); + //~^ ERROR `objc::class!` expected a string literal + + objc::class!("NSObject\0"); + //~^ ERROR `objc::class!` may not contain null characters + + let s = "alloc"; + objc::selector!(s); + //~^ ERROR attribute value must be a literal + + objc::selector!(alloc); + //~^ ERROR attribute value must be a literal + + objc::selector!(123); + //~^ ERROR `objc::selector!` expected a string literal + + objc::selector!("alloc\0"); + //~^ ERROR `objc::selector!` may not contain null characters +} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr b/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr new file mode 100644 index 0000000000000..99eeb27e30c1b --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr @@ -0,0 +1,50 @@ +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:12:18 + | +LL | objc::class!(s); + | ^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:15:18 + | +LL | objc::class!(NSObject); + | ^^^^^^^^ + +error: `objc::class!` expected a string literal + --> $DIR/darwin-objc-bad-arg.rs:18:18 + | +LL | objc::class!(123); + | ^^^ + +error: `objc::class!` may not contain null characters + --> $DIR/darwin-objc-bad-arg.rs:21:18 + | +LL | objc::class!("NSObject\0"); + | ^^^^^^^^^^^^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:25:21 + | +LL | objc::selector!(s); + | ^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:28:21 + | +LL | objc::selector!(alloc); + | ^^^^^ + +error: `objc::selector!` expected a string literal + --> $DIR/darwin-objc-bad-arg.rs:31:21 + | +LL | objc::selector!(123); + | ^^^ + +error: `objc::selector!` may not contain null characters + --> $DIR/darwin-objc-bad-arg.rs:34:21 + | +LL | objc::selector!("alloc\0"); + | ^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/darwin-objc/darwin-objc-bad-const.rs b/tests/ui/darwin-objc/darwin-objc-bad-const.rs new file mode 100644 index 0000000000000..ccf28697b482a --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-const.rs @@ -0,0 +1,17 @@ +// Test that `objc::class!` and `objc::selector!` aren't `const` expressions. +// The system gives them their final values at dynamic load time. + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub const CLASS: objc::Class = objc::class!("NSObject"); +//~^ ERROR cannot access extern static `CLASS::VAL` [E0080] + +pub const SELECTOR: objc::SEL = objc::selector!("alloc"); +//~^ ERROR cannot access extern static `SELECTOR::VAL` [E0080] + +pub fn main() {} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-const.stderr b/tests/ui/darwin-objc/darwin-objc-bad-const.stderr new file mode 100644 index 0000000000000..43d0b7d761f17 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-const.stderr @@ -0,0 +1,19 @@ +error[E0080]: cannot access extern static `CLASS::VAL` + --> $DIR/darwin-objc-bad-const.rs:11:32 + | +LL | pub const CLASS: objc::Class = objc::class!("NSObject"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `CLASS` failed here + | + = note: this error originates in the macro `objc::class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: cannot access extern static `SELECTOR::VAL` + --> $DIR/darwin-objc-bad-const.rs:14:33 + | +LL | pub const SELECTOR: objc::SEL = objc::selector!("alloc"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SELECTOR` failed here + | + = note: this error originates in the macro `objc::selector` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/darwin-objc/darwin-objc-bad-ref.rs b/tests/ui/darwin-objc/darwin-objc-bad-ref.rs new file mode 100644 index 0000000000000..fb1437366dd43 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-ref.rs @@ -0,0 +1,31 @@ +// Test that `objc::class!` and `objc::selector!` can't be returned by reference. +// A single instance may have multiple addresses (e.g. across dylib boundaries). + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub fn class_ref<'a>() -> &'a objc::Class { + &objc::class!("NSObject") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn class_ref_static() -> &'static objc::Class { + &objc::class!("NSObject") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn selector_ref<'a>() -> &'a objc::SEL { + &objc::selector!("alloc") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn selector_ref_static() -> &'static objc::SEL { + &objc::selector!("alloc") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn main() {} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr b/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr new file mode 100644 index 0000000000000..b09e6a75c844d --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr @@ -0,0 +1,39 @@ +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:12:5 + | +LL | &objc::class!("NSObject") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:17:5 + | +LL | &objc::class!("NSObject") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:22:5 + | +LL | &objc::selector!("alloc") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:27:5 + | +LL | &objc::selector!("alloc") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/darwin-objc/darwin-objc-class-selector.rs b/tests/ui/darwin-objc/darwin-objc-class-selector.rs new file mode 100644 index 0000000000000..b9a2fc3634f46 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-class-selector.rs @@ -0,0 +1,31 @@ +// Call `[NSObject class]` using `objc::class!` and `objc::selector!`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::mem::transmute; +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[link(name = "objc", kind = "dylib")] +unsafe extern "C" { + unsafe fn objc_msgSend(); +} + +fn main() { + let msg_send_fn = unsafe { + transmute::< + unsafe extern "C" fn(), + unsafe extern "C" fn(objc::Class, objc::SEL) -> objc::Class, + >(objc_msgSend) + }; + let static_sel = objc::selector!("class"); + let static_class = objc::class!("NSObject"); + let runtime_class = unsafe { msg_send_fn(static_class, static_sel) }; + assert_eq!(static_class, runtime_class); +} diff --git a/tests/ui/darwin-objc/darwin-objc-class.rs b/tests/ui/darwin-objc/darwin-objc-class.rs new file mode 100644 index 0000000000000..851149d872683 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-class.rs @@ -0,0 +1,39 @@ +// Test that `objc::class!` returns the same thing as `objc_lookUpClass`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::ffi::c_char; +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[link(name = "objc")] +unsafe extern "C" { + fn objc_lookUpClass(methname: *const c_char) -> objc::Class; +} + +fn get_object_class() -> objc::Class { + objc::class!("NSObject") +} + +fn lookup_object_class() -> objc::Class { + unsafe { objc_lookUpClass(c"NSObject".as_ptr()) } +} + +fn get_string_class() -> objc::Class { + objc::class!("NSString") +} + +fn lookup_string_class() -> objc::Class { + unsafe { objc_lookUpClass(c"NSString".as_ptr()) } +} + +fn main() { + assert_eq!(get_object_class(), lookup_object_class()); + assert_eq!(get_string_class(), lookup_string_class()); +} diff --git a/tests/ui/darwin-objc/darwin-objc-selector.rs b/tests/ui/darwin-objc/darwin-objc-selector.rs new file mode 100644 index 0000000000000..008ae4c4ca457 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-selector.rs @@ -0,0 +1,36 @@ +// Test that `objc::selector!` returns the same thing as `sel_registerName`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::ffi::c_char; +use std::os::darwin::objc; + +#[link(name = "objc")] +unsafe extern "C" { + fn sel_registerName(methname: *const c_char) -> objc::SEL; +} + +fn get_alloc_selector() -> objc::SEL { + objc::selector!("alloc") +} + +fn register_alloc_selector() -> objc::SEL { + unsafe { sel_registerName(c"alloc".as_ptr()) } +} + +fn get_init_selector() -> objc::SEL { + objc::selector!("initWithCString:encoding:") +} + +fn register_init_selector() -> objc::SEL { + unsafe { sel_registerName(c"initWithCString:encoding:".as_ptr()) } +} + +fn main() { + assert_eq!(get_alloc_selector(), register_alloc_selector()); + assert_eq!(get_init_selector(), register_init_selector()); +} From 15f6d2e8d276ba908dfa05e55eeb629373024f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 14 Sep 2025 01:12:34 +0200 Subject: [PATCH 0378/1900] rustdoc: Move HTML-specific attr rendering code into HTML rendering mod These functions used to be shared with the JSON backend but nowadays they aren't. --- src/librustdoc/clean/types.rs | 112 ------------------ src/librustdoc/html/render/mod.rs | 181 +++++++++++++++++++++++------- 2 files changed, 143 insertions(+), 150 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index dcd67cb7ebc77..53ae8afc027d0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -801,50 +801,6 @@ impl Item { Some(tcx.visibility(def_id)) } - /// Get a list of attributes excluding `#[repr]` to display. - /// - /// Only used by the HTML output-format. - fn attributes_without_repr(&self) -> Vec { - self.attrs - .other_attrs - .iter() - .filter_map(|attr| match attr { - hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => { - Some(format!("#[unsafe(link_section = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => { - Some("#[unsafe(no_mangle)]".to_string()) - } - hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => { - Some(format!("#[unsafe(export_name = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => { - Some("#[non_exhaustive]".to_string()) - } - _ => None, - }) - .collect() - } - - /// Get a list of attributes to display on this item. - /// - /// Only used by the HTML output-format. - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Vec { - let mut attrs = self.attributes_without_repr(); - - if let Some(repr_attr) = self.repr(tcx, cache) { - attrs.push(repr_attr); - } - attrs - } - - /// Returns a stringified `#[repr(...)]` attribute. - /// - /// Only used by the HTML output-format. - pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Option { - repr_attributes(tcx, cache, self.def_id()?, self.type_()) - } - pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } @@ -854,74 +810,6 @@ impl Item { } } -/// Return a string representing the `#[repr]` attribute if present. -/// -/// Only used by the HTML output-format. -pub(crate) fn repr_attributes( - tcx: TyCtxt<'_>, - cache: &Cache, - def_id: DefId, - item_type: ItemType, -) -> Option { - use rustc_abi::IntegerType; - - if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { - return None; - } - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); - - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } -} - #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b4ef47d1e2694..cc2744e48c8f6 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -51,7 +51,8 @@ use askama::Template; use itertools::Either; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir::attrs::{DeprecatedSince, Deprecation}; +use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; @@ -1310,43 +1311,6 @@ fn render_assoc_item( }) } -struct CodeAttribute(String); - -fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { - write!( - w, - "

( + &self, + tcx: DbInterner<'db>, + value: impl Upcast, P>, + ) -> Obligation<'db, P> { + Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs new file mode 100644 index 0000000000000..5217308af473c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -0,0 +1,268 @@ +//! Storage for type variables for the infer context the next-trait-solver. + +use std::cmp; +use std::marker::PhantomData; +use std::ops::Range; + +use ena::snapshot_vec as sv; +use ena::undo_log::Rollback; +use ena::unify as ut; +use rustc_index::IndexVec; +use rustc_type_ir::TyVid; +use rustc_type_ir::UniverseIndex; +use rustc_type_ir::inherent::Ty as _; +use tracing::debug; + +use crate::next_solver::SolverDefId; +use crate::next_solver::Ty; +use crate::next_solver::infer::InferCtxtUndoLogs; + +impl<'db> Rollback>>> for TypeVariableStorage<'db> { + fn reverse(&mut self, undo: sv::UndoLog>>) { + self.eq_relations.reverse(undo) + } +} + +#[derive(Clone, Default)] +pub(crate) struct TypeVariableStorage<'db> { + /// The origins of each type variable. + values: IndexVec, + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. This table also stores, for each key, + /// the known value. + eq_relations: ut::UnificationTableStorage>, +} + +pub(crate) struct TypeVariableTable<'a, 'db> { + storage: &'a mut TypeVariableStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +#[derive(Copy, Clone, Debug)] +pub struct TypeVariableOrigin { + /// `DefId` of the type parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, +} + +#[derive(Clone)] +pub(crate) struct TypeVariableData { + origin: TypeVariableOrigin, +} + +#[derive(Clone, Debug)] +pub(crate) enum TypeVariableValue<'db> { + Known { value: Ty<'db> }, + Unknown { universe: UniverseIndex }, +} + +impl<'db> TypeVariableValue<'db> { + /// If this value is known, returns the type it is known to be. + /// Otherwise, `None`. + pub(crate) fn known(&self) -> Option> { + match self { + TypeVariableValue::Unknown { .. } => None, + TypeVariableValue::Known { value } => Some(*value), + } + } + + pub(crate) fn is_unknown(&self) -> bool { + match *self { + TypeVariableValue::Unknown { .. } => true, + TypeVariableValue::Known { .. } => false, + } + } +} + +impl<'db> TypeVariableStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> TypeVariableTable<'a, 'db> { + TypeVariableTable { storage: self, undo_log } + } + + #[inline] + pub(crate) fn eq_relations_ref(&self) -> &ut::UnificationTableStorage> { + &self.eq_relations + } + + pub(super) fn finalize_rollback(&mut self) { + debug_assert!(self.values.len() >= self.eq_relations.len()); + self.values.truncate(self.eq_relations.len()); + } +} + +impl<'db> TypeVariableTable<'_, 'db> { + /// Returns the origin that was given when `vid` was created. + /// + /// Note that this function does not return care whether + /// `vid` has been unified with something else or not. + pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin { + self.storage.values[vid].origin + } + + /// Records that `a == b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn equate(&mut self, a: TyVid, b: TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.eq_relations().union(a, b); + } + + /// Instantiates `vid` with the type `ty`. + /// + /// Precondition: `vid` must not have been previously instantiated. + pub(crate) fn instantiate(&mut self, vid: TyVid, ty: Ty<'db>) { + let vid = self.root_var(vid); + debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}"); + debug_assert!(self.probe(vid).is_unknown()); + debug_assert!( + self.eq_relations().probe_value(vid).is_unknown(), + "instantiating type variable `{vid:?}` twice: new-value = {ty:?}, old-value={:?}", + self.eq_relations().probe_value(vid) + ); + self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); + } + + /// Creates a new type variable. + /// + /// - `diverging`: indicates if this is a "diverging" type + /// variable, e.g., one created as the type of a `return` + /// expression. The code in this module doesn't care if a + /// variable is diverging, but the main Rust type-checker will + /// sometimes "unify" such variables with the `!` or `()` types. + /// - `origin`: indicates *why* the type variable was created. + /// The code in this module doesn't care, but it can be useful + /// for improving error messages. + pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + let index = self.storage.values.push(TypeVariableData { origin }); + debug_assert_eq!(eq_key.vid, index); + + debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); + + index + } + + /// Returns the number of type variables created thus far. + pub(crate) fn num_vars(&self) -> usize { + self.storage.values.len() + } + + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). + pub(crate) fn root_var(&mut self, vid: TyVid) -> TyVid { + self.eq_relations().find(vid).vid + } + + /// Retrieves the type to which `vid` has been instantiated, if + /// any. + pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.inlined_probe(vid) + } + + /// An always-inlined variant of `probe`, for very hot call sites. + #[inline(always)] + pub(crate) fn inlined_probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.eq_relations().inlined_probe_value(vid) + } + + #[inline] + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'db, TyVidEqKey<'db>> { + self.storage.eq_relations.with_log(self.undo_log) + } + + /// Returns indices of all variables that are not yet + /// instantiated. + pub(crate) fn unresolved_variables(&mut self) -> Vec { + (0..self.num_vars()) + .filter_map(|i| { + let vid = TyVid::from_usize(i); + match self.probe(vid) { + TypeVariableValue::Unknown { .. } => Some(vid), + TypeVariableValue::Known { .. } => None, + } + }) + .collect() + } +} + +/////////////////////////////////////////////////////////////////////////// + +/// These structs (a newtyped TyVid) are used as the unification key +/// for the `eq_relations`; they carry a `TypeVariableValue` along +/// with them. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidEqKey<'db> { + vid: TyVid, + + // in the table, we map each ty-vid to one of these: + phantom: PhantomData>, +} + +impl<'db> From for TyVidEqKey<'db> { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: TyVid) -> Self { + TyVidEqKey { vid, phantom: PhantomData } + } +} + +impl<'db> ut::UnifyKey for TyVidEqKey<'db> { + type Value = TypeVariableValue<'db>; + #[inline(always)] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + TyVidEqKey::from(TyVid::from_u32(i)) + } + fn tag() -> &'static str { + "TyVidEqKey" + } +} + +impl<'db> ut::UnifyValue for TypeVariableValue<'db> { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + // We never equate two type variables, both of which + // have known types. Instead, we recursively equate + // those types. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => { + panic!("equating two type variables, both of which have known types") + } + + // If one side is known, prefer that one. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + &TypeVariableValue::Unknown { universe: universe1 }, + &TypeVariableValue::Unknown { universe: universe2 }, + ) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(universe1, universe2); + Ok(TypeVariableValue::Unknown { universe }) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs new file mode 100644 index 0000000000000..b9afb45ba89da --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -0,0 +1,176 @@ +//! Unification keyes for the infer context the next-trait-solver. + +use std::cmp; +use std::marker::PhantomData; + +use ena::unify::{NoError, UnifyKey, UnifyValue}; +use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind}; + +use crate::next_solver::{Const, Region, SolverDefId, Ty}; + +#[derive(Clone, Debug)] +pub enum RegionVariableValue<'db> { + Known { value: Region<'db> }, + Unknown { universe: UniverseIndex }, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct RegionVidKey<'db> { + pub vid: RegionVid, + pub phantom: PhantomData>, +} + +impl<'db> From for RegionVidKey<'db> { + fn from(vid: RegionVid) -> Self { + RegionVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for RegionVidKey<'db> { + type Value = RegionVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + RegionVidKey::from(RegionVid::from_u32(i)) + } + fn tag() -> &'static str { + "RegionVidKey" + } +} + +pub struct RegionUnificationError; +impl<'db> UnifyValue for RegionVariableValue<'db> { + type Error = RegionUnificationError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + (RegionVariableValue::Known { .. }, RegionVariableValue::Known { .. }) => { + Err(RegionUnificationError) + } + + (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe }) + | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => { + let universe_of_value = match (*value).kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(..) | RegionKind::ReBound(..) => { + panic!("not a universal region") + } + }; + + if universe.can_name(universe_of_value) { + Ok(RegionVariableValue::Known { value: *value }) + } else { + Err(RegionUnificationError) + } + } + + ( + RegionVariableValue::Unknown { universe: a }, + RegionVariableValue::Unknown { universe: b }, + ) => { + // If we unify two unconstrained regions then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) }) + } + } + } +} + +// Generic consts. + +#[derive(Copy, Clone, Debug)] +pub struct ConstVariableOrigin { + /// `DefId` of the const parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, +} + +#[derive(Clone, Debug)] +pub enum ConstVariableValue<'db> { + Known { value: Const<'db> }, + Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, +} + +impl<'db> ConstVariableValue<'db> { + /// If this value is known, returns the const it is known to be. + /// Otherwise, `None`. + pub fn known(&self) -> Option> { + match self { + ConstVariableValue::Unknown { .. } => None, + ConstVariableValue::Known { value } => Some(*value), + } + } +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct ConstVidKey<'db> { + pub vid: ConstVid, + pub phantom: PhantomData>, +} + +impl<'db> From for ConstVidKey<'db> { + fn from(vid: ConstVid) -> Self { + ConstVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for ConstVidKey<'db> { + type Value = ConstVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + ConstVidKey::from(ConstVid::from_u32(i)) + } + fn tag() -> &'static str { + "ConstVidKey" + } +} + +impl<'db> UnifyValue for ConstVariableValue<'db> { + type Error = NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { + panic!("equating two const variables, both of which have known values") + } + + // If one side is known, prefer that one. + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + ConstVariableValue::Unknown { origin, universe: universe1 }, + ConstVariableValue::Unknown { origin: _, universe: universe2 }, + ) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(*universe1, *universe2); + Ok(ConstVariableValue::Unknown { origin: *origin, universe }) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs new file mode 100644 index 0000000000000..9c1bd52065fd2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -0,0 +1,2173 @@ +//! Things related to the Interner in the next-trait-solver. +#![allow(unused)] + +use base_db::Crate; +use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; +use hir_def::lang_item::LangItem; +use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; +use hir_def::{AdtId, BlockId, TypeAliasId, VariantId}; +use hir_def::{AttrDefId, Lookup}; +use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; +use intern::sym::non_exhaustive; +use intern::{Interned, impl_internable, sym}; +use la_arena::Idx; +use rustc_abi::{Align, ReprFlags, ReprOptions}; +use rustc_hash::FxHashSet; +use rustc_index::bit_set::DenseBitSet; +use rustc_type_ir::elaborate::elaborate; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{ + AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + AliasTerm, AliasTermKind, AliasTy, EarlyBinder, FlagComputation, Flags, ImplPolarity, InferTy, + ProjectionPredicate, TraitPredicate, TraitRef, Upcast, +}; +use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; +use std::fmt; +use std::ops::ControlFlow; +use syntax::ast::SelfParamKind; +use triomphe::Arc; + +use rustc_ast_ir::visit::VisitorResult; +use rustc_index::IndexVec; +use rustc_type_ir::TypeVisitableExt; +use rustc_type_ir::{ + BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex, + Variance, WithCachedTypeInfo, elaborate, + inherent::{self, Const as _, Region as _, Ty as _}, + ir_print, relate, +}; + +use crate::lower_nextsolver::{self, TyLoweringContext}; +use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; +use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds, +}; +use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; + +use super::generics::generics; +use super::util::sizedness_constraint_for_ty; +use super::{ + Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, + Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, + ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv, + ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate, + PredicateKind, Term, Ty, TyKind, Tys, ValueConst, + abi::Safety, + fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, + generics::Generics, + mapping::ChalkToNextSolver, + region::{ + BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region, + }, +}; +use super::{ClauseKind, SolverDefId, Valtree}; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_nolifetime_salsa { + ($name:ident, $ty:ty) => { + interned_vec_nolifetime_salsa!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable> for $name { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $name { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ty, nofold) => { + #[salsa::interned(no_lifetime, constructor = new_, debug)] + pub struct $name { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty; 2]>, + } + + impl $name { + pub fn new_from_iter<'db>( + interner: DbInterner<'db>, + data: impl IntoIterator, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl rustc_type_ir::inherent::SliceLike for $name { + type Item = $ty; + + type IntoIter = as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl IntoIterator for $name { + type Item = $ty; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl Default for $name { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_nolifetime_salsa as interned_vec_nolifetime_salsa; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_db { + ($name:ident, $ty:ident) => { + interned_vec_db!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable> for $name<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $name<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ident, nofold) => { + #[salsa::interned(constructor = new_)] + pub struct $name<'db> { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty<'db>; 2]>, + } + + impl<'db> std::fmt::Debug for $name<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_slice().fmt(fmt) + } + } + + impl<'db> $name<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator>, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty<'db>; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> { + type Item = $ty<'db>; + + type IntoIter = ; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl<'db> IntoIterator for $name<'db> { + type Item = $ty<'db>; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl<'db> Default for $name<'db> { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_db as interned_vec_db; + +#[derive(Debug, Copy, Clone)] +pub struct DbInterner<'db> { + pub(crate) db: &'db dyn HirDatabase, + pub(crate) krate: Option, + pub(crate) block: Option, +} + +// FIXME: very wrong, see https://github.com/rust-lang/rust/pull/144808 +unsafe impl Send for DbInterner<'_> {} +unsafe impl Sync for DbInterner<'_> {} + +impl<'db> DbInterner<'db> { + // FIXME(next-solver): remove this method + pub fn conjure() -> DbInterner<'db> { + salsa::with_attached_database(|db| DbInterner { + db: unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db.as_view()) + }, + krate: None, + block: None, + }) + .unwrap() + } + + pub fn new_with( + db: &'db dyn HirDatabase, + krate: Option, + block: Option, + ) -> DbInterner<'db> { + DbInterner { db, krate, block } + } + + pub fn db(&self) -> &'db dyn HirDatabase { + self.db + } +} + +// This is intentionally left as `()` +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Span(()); + +impl<'db> inherent::Span> for Span { + fn dummy() -> Self { + Span(()) + } +} + +interned_vec_nolifetime_salsa!(BoundVarKinds, BoundVarKind, nofold); + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum BoundVarKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +impl BoundVarKind { + pub fn expect_region(self) -> BoundRegionKind { + match self { + BoundVarKind::Region(lt) => lt, + _ => panic!("expected a region, but found another kind"), + } + } + + pub fn expect_ty(self) -> BoundTyKind { + match self { + BoundVarKind::Ty(ty) => ty, + _ => panic!("expected a type, but found another kind"), + } + } + + pub fn expect_const(self) { + match self { + BoundVarKind::Const => (), + _ => panic!("expected a const, but found another kind"), + } + } +} + +interned_vec_db!(CanonicalVars, CanonicalVarKind, nofold); + +pub struct DepNodeIndex; + +#[derive(Debug)] +pub struct Tracked(T); + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub bound: T, +} + +impl std::fmt::Debug for Placeholder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + if self.universe == UniverseIndex::ROOT { + write!(f, "!{:?}", self.bound) + } else { + write!(f, "!{}_{:?}", self.universe.index(), self.bound) + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct AllocId; + +interned_vec_nolifetime_salsa!(VariancesOf, Variance, nofold); + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct VariantIdx(usize); + +// FIXME: could/should store actual data? +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum VariantDef { + Struct(StructId), + Union(UnionId), + Enum(EnumVariantId), +} + +impl VariantDef { + pub fn id(&self) -> VariantId { + match self { + VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id), + VariantDef::Union(union_id) => VariantId::UnionId(*union_id), + VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id), + } + } + + pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx, FieldData)> { + let id: VariantId = match self { + VariantDef::Struct(it) => (*it).into(), + VariantDef::Union(it) => (*it).into(), + VariantDef::Enum(it) => (*it).into(), + }; + id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect() + } +} + +/* +/// Definition of a variant -- a struct's fields or an enum variant. +#[derive(Debug, HashStable, TyEncodable, TyDecodable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor: Option<(CtorKind, DefId)>, + /// Variant or struct name, maybe empty for anonymous adt (struct or union). + pub name: Symbol, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: IndexVec, + /// The error guarantees from parser, if any. + tainted: Option, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, +} +*/ + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct AdtFlags { + is_enum: bool, + is_union: bool, + is_struct: bool, + is_phantom_data: bool, + is_fundamental: bool, + is_box: bool, + is_manually_drop: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AdtDefInner { + pub id: AdtId, + variants: Vec<(VariantIdx, VariantDef)>, + flags: AdtFlags, + repr: ReprOptions, +} + +// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and +// accept there might be collisions for def ids from different crates (or across +// different tests, oh my). +impl std::hash::Hash for AdtDefInner { + #[inline] + fn hash(&self, s: &mut H) { + self.id.hash(s) + } +} + +#[salsa::interned(no_lifetime, constructor = new_)] +pub struct AdtDef { + #[returns(ref)] + data_: AdtDefInner, +} + +impl AdtDef { + pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self { + let db = interner.db(); + let (flags, variants, repr) = match def_id { + AdtId::StructId(struct_id) => { + let data = db.struct_signature(struct_id); + + let flags = AdtFlags { + is_enum: false, + is_union: false, + is_struct: true, + is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA), + is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL), + is_box: data.flags.contains(StructFlags::IS_BOX), + is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP), + }; + + let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))]; + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + AdtId::UnionId(union_id) => { + let data = db.union_signature(union_id); + + let flags = AdtFlags { + is_enum: false, + is_union: true, + is_struct: false, + is_phantom_data: false, + is_fundamental: false, + is_box: false, + is_manually_drop: false, + }; + + let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))]; + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + AdtId::EnumId(enum_id) => { + let flags = AdtFlags { + is_enum: true, + is_union: false, + is_struct: false, + is_phantom_data: false, + is_fundamental: false, + is_box: false, + is_manually_drop: false, + }; + + let variants = enum_id + .enum_variants(db) + .variants + .iter() + .enumerate() + .map(|(idx, v)| (VariantIdx(idx), v)) + .map(|(idx, v)| (idx, VariantDef::Enum(v.0))) + .collect(); + + let data = db.enum_signature(enum_id); + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + }; + + AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr }) + } + + pub fn inner(&self) -> &AdtDefInner { + salsa::with_attached_database(|db| { + let inner = self.data_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn is_enum(&self) -> bool { + self.inner().flags.is_enum + } + + #[inline] + pub fn repr(self) -> ReprOptions { + self.inner().repr + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(self) -> VariantDef { + assert!(self.inner().flags.is_struct || self.inner().flags.is_union); + self.inner().variants[0].1.clone() + } +} + +impl<'db> inherent::AdtDef> for AdtDef { + fn def_id(self) -> as rustc_type_ir::Interner>::DefId { + SolverDefId::AdtId(self.inner().id) + } + + fn is_struct(self) -> bool { + self.inner().flags.is_struct + } + + fn is_phantom_data(self) -> bool { + self.inner().flags.is_phantom_data + } + + fn is_fundamental(self) -> bool { + self.inner().flags.is_fundamental + } + + fn struct_tail_ty( + self, + interner: DbInterner<'db>, + ) -> Option< + rustc_type_ir::EarlyBinder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + > { + let db = interner.db(); + let hir_def::AdtId::StructId(struct_id) = self.inner().id else { + return None; + }; + let id: VariantId = struct_id.into(); + let field_types = interner.db().field_types_ns(id); + + field_types.iter().last().map(|f| *f.1) + } + + fn all_field_tys( + self, + interner: DbInterner<'db>, + ) -> rustc_type_ir::EarlyBinder< + DbInterner<'db>, + impl IntoIterator as rustc_type_ir::Interner>::Ty>, + > { + let db = interner.db(); + // FIXME: this is disabled just to match the behavior with chalk right now + let field_tys = |id: VariantId| { + let variant_data = id.fields(db); + let fields = if variant_data.fields().is_empty() { + vec![] + } else { + let field_types = db.field_types_ns(id); + variant_data + .fields() + .iter() + .map(|(idx, _)| { + let ty = field_types[idx]; + ty.skip_binder() + }) + .collect() + }; + }; + let field_tys = |id: VariantId| vec![]; + let tys: Vec<_> = match self.inner().id { + hir_def::AdtId::StructId(id) => field_tys(id.into()), + hir_def::AdtId::UnionId(id) => field_tys(id.into()), + hir_def::AdtId::EnumId(id) => id + .enum_variants(db) + .variants + .iter() + .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .collect(), + }; + + rustc_type_ir::EarlyBinder::bind(tys) + } + + fn sizedness_constraint( + self, + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ) -> Option< + rustc_type_ir::EarlyBinder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + > { + if self.is_struct() { + let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; + + let constraint_ty = sizedness_constraint_for_ty(interner, sizedness, tail_ty)?; + + Some(EarlyBinder::bind(constraint_ty)) + } else { + None + } + } + + fn destructor( + self, + interner: DbInterner<'db>, + ) -> Option { + // FIXME(next-solver) + None + } + + fn is_manually_drop(self) -> bool { + self.inner().flags.is_manually_drop + } +} + +impl fmt::Debug for AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + salsa::with_attached_database(|db| match self.inner().id { + AdtId::StructId(struct_id) => { + let data = db.as_view::().struct_signature(struct_id); + f.write_str(data.name.as_str()) + } + AdtId::UnionId(union_id) => { + let data = db.as_view::().union_signature(union_id); + f.write_str(data.name.as_str()) + } + AdtId::EnumId(enum_id) => { + let data = db.as_view::().enum_signature(enum_id); + f.write_str(data.name.as_str()) + } + }) + .unwrap_or_else(|| f.write_str(&format!("AdtDef({:?})", self.inner().id))) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Features; + +impl<'db> inherent::Features> for Features { + fn generic_const_exprs(self) -> bool { + false + } + + fn coroutine_clone(self) -> bool { + false + } + + fn associated_const_equality(self) -> bool { + false + } + + fn feature_bound_holds_in_crate(self, symbol: ()) -> bool { + false + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct UnsizingParams(pub(crate) DenseBitSet); + +impl std::ops::Deref for UnsizingParams { + type Target = DenseBitSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type PatternKind<'db> = rustc_type_ir::PatternKind>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Pattern<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>, +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl<'db> Pattern<'db> { + pub fn new(interner: DbInterner<'db>, kind: PatternKind<'db>) -> Self { + Pattern::new_(interner.db(), InternedWrapperNoDebug(kind)) + } + + pub fn inner(&self) -> &PatternKind<'db> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> Flags for Pattern<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + match self.inner() { + PatternKind::Range { start, end } => { + FlagComputation::for_const_kind(&start.kind()).flags + | FlagComputation::for_const_kind(&end.kind()).flags + } + PatternKind::Or(pats) => { + let mut flags = pats.as_slice()[0].flags(); + for pat in pats.as_slice()[1..].iter() { + flags |= pat.flags(); + } + flags + } + } + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match self.inner() { + PatternKind::Range { start, end } => { + start.outer_exclusive_binder().max(end.outer_exclusive_binder()) + } + PatternKind::Or(pats) => { + let mut idx = pats.as_slice()[0].outer_exclusive_binder(); + for pat in pats.as_slice()[1..].iter() { + idx = idx.max(pat.outer_exclusive_binder()); + } + idx + } + } + } +} + +impl<'db> rustc_type_ir::inherent::IntoKind for Pattern<'db> { + type Kind = rustc_type_ir::PatternKind>; + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let tcx = relation.cx(); + match (a.kind(), b.kind()) { + ( + PatternKind::Range { start: start_a, end: end_a }, + PatternKind::Range { start: start_b, end: end_b }, + ) => { + let start = relation.relate(start_a, start_b)?; + let end = relation.relate(end_a, end_b)?; + Ok(Pattern::new(tcx, PatternKind::Range { start, end })) + } + (PatternKind::Or(a), PatternKind::Or(b)) => { + if a.len() != b.len() { + return Err(TypeError::Mismatch); + } + let pats = CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)), + |g| PatList::new_from_iter(tcx, g.iter().cloned()), + )?; + Ok(Pattern::new(tcx, PatternKind::Or(pats))) + } + (PatternKind::Range { .. } | PatternKind::Or(_), _) => Err(TypeError::Mismatch), + } + } +} + +interned_vec_db!(PatList, Pattern); + +impl<'db> rustc_type_ir::Interner for DbInterner<'db> { + type DefId = SolverDefId; + type LocalDefId = SolverDefId; + type LocalDefIds = SolverDefIds; + type Span = Span; + + type GenericArgs = GenericArgs<'db>; + type GenericArgsSlice = GenericArgs<'db>; + type GenericArg = GenericArg<'db>; + + type Term = Term<'db>; + + type BoundVarKinds = BoundVarKinds; + type BoundVarKind = BoundVarKind; + + type PredefinedOpaques = PredefinedOpaques<'db>; + + fn mk_predefined_opaques_in_body( + self, + data: rustc_type_ir::solve::PredefinedOpaquesData, + ) -> Self::PredefinedOpaques { + PredefinedOpaques::new(self, data) + } + + type CanonicalVarKinds = CanonicalVars<'db>; + + fn mk_canonical_var_kinds( + self, + kinds: &[rustc_type_ir::CanonicalVarKind], + ) -> Self::CanonicalVarKinds { + CanonicalVars::new_from_iter(self, kinds.iter().cloned()) + } + + type ExternalConstraints = ExternalConstraints<'db>; + + fn mk_external_constraints( + self, + data: rustc_type_ir::solve::ExternalConstraintsData, + ) -> Self::ExternalConstraints { + ExternalConstraints::new(self, data) + } + + type DepNodeIndex = DepNodeIndex; + + type Tracked = Tracked; + + type Ty = Ty<'db>; + type Tys = Tys<'db>; + type FnInputTys = Tys<'db>; + type ParamTy = ParamTy; + type BoundTy = BoundTy; + type PlaceholderTy = PlaceholderTy; + type Symbol = (); + + type ErrorGuaranteed = ErrorGuaranteed; + type BoundExistentialPredicates = BoundExistentialPredicates<'db>; + type AllocId = AllocId; + type Pat = Pattern<'db>; + type PatList = PatList<'db>; + type Safety = Safety; + type Abi = FnAbi; + + type Const = Const<'db>; + type PlaceholderConst = PlaceholderConst; + type ParamConst = ParamConst; + type BoundConst = rustc_type_ir::BoundVar; + type ValueConst = ValueConst<'db>; + type ValTree = Valtree<'db>; + type ExprConst = ExprConst; + + type Region = Region<'db>; + type EarlyParamRegion = EarlyParamRegion; + type LateParamRegion = LateParamRegion; + type BoundRegion = BoundRegion; + type PlaceholderRegion = PlaceholderRegion; + + type RegionAssumptions = RegionAssumptions<'db>; + + type ParamEnv = ParamEnv<'db>; + type Predicate = Predicate<'db>; + type Clause = Clause<'db>; + type Clauses = Clauses<'db>; + + type GenericsOf = Generics; + + type VariancesOf = VariancesOf; + + type AdtDef = AdtDef; + + type Features = Features; + + fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, + { + CollectAndApply::collect_and_apply(args, |g| { + GenericArgs::new_from_iter(self, g.iter().cloned()) + }) + } + + type UnsizingParams = UnsizingParams; + + fn mk_tracked( + self, + data: T, + dep_node: Self::DepNodeIndex, + ) -> Self::Tracked { + Tracked(data) + } + + fn get_tracked(self, tracked: &Self::Tracked) -> T { + tracked.0.clone() + } + + fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex) { + (task(), DepNodeIndex) + } + + fn with_global_cache( + self, + f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache) -> R, + ) -> R { + salsa::with_attached_database(|db| { + tls_cache::with_cache( + unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>( + db.as_view::(), + ) + }, + f, + ) + }) + .unwrap() + } + + fn canonical_param_env_cache_get_or_insert( + self, + param_env: Self::ParamEnv, + f: impl FnOnce() -> rustc_type_ir::CanonicalParamEnvCacheEntry, + from_entry: impl FnOnce(&rustc_type_ir::CanonicalParamEnvCacheEntry) -> R, + ) -> R { + from_entry(&f()) + } + + fn evaluation_is_concurrent(&self) -> bool { + false + } + + fn expand_abstract_consts>(self, t: T) -> T { + t + } + + fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf { + generics(self.db(), def_id) + } + + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { + match def_id { + SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::FunctionId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::AdtId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::InternedOpaqueTyId(_def_id) => { + // FIXME(next-solver): track variances + VariancesOf::new_from_iter( + self, + (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), + ) + } + _ => VariancesOf::new_from_iter(self, []), + } + } + + fn type_of(self, def_id: Self::DefId) -> rustc_type_ir::EarlyBinder { + let def_id = match def_id { + SolverDefId::TypeAliasId(id) => { + use hir_def::Lookup; + match id.lookup(self.db()).container { + ItemContainerId::ImplId(it) => it, + _ => panic!("assoc ty value should be in impl"), + }; + crate::TyDefId::TypeAliasId(id) + } + SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), + }; + self.db().ty_ns(def_id) + } + + fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef { + let def_id = match adt_def_id { + SolverDefId::AdtId(adt_id) => adt_id, + _ => panic!("Invalid DefId passed to adt_def"), + }; + AdtDef::new(def_id, self) + } + + fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> rustc_type_ir::AliasTyKind { + // FIXME: not currently creating any others + rustc_type_ir::AliasTyKind::Projection + } + + fn alias_term_kind( + self, + alias: rustc_type_ir::AliasTerm, + ) -> rustc_type_ir::AliasTermKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, + SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, + SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, + _ => unreachable!("Unexpected alias: {:?}", alias.def_id), + } + } + + fn trait_ref_and_own_args_for_alias( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { + let trait_def_id = self.parent(def_id); + let trait_generics = self.generics_of(trait_def_id); + let trait_args = GenericArgs::new_from_iter( + self, + args.as_slice()[0..trait_generics.own_params.len()].iter().cloned(), + ); + let alias_args = + GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len())); + (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args) + } + + fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { + // FIXME + true + } + + fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) {} + + fn debug_assert_existential_args_compatible( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) { + } + + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, + { + CollectAndApply::collect_and_apply(args, |g| Tys::new_from_iter(self, g.iter().cloned())) + } + + fn parent(self, def_id: Self::DefId) -> Self::DefId { + use hir_def::Lookup; + + let container = match def_id { + SolverDefId::FunctionId(it) => it.lookup(self.db()).container, + SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, + SolverDefId::ForeignId(it) => it.lookup(self.db()).container, + SolverDefId::ConstId(it) => it.lookup(self.db()).container, + SolverDefId::InternedClosureId(it) => { + return self + .db() + .lookup_intern_closure(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::InternedCoroutineId(it) => { + return self + .db() + .lookup_intern_coroutine(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::StaticId(_) + | SolverDefId::AdtId(_) + | SolverDefId::TraitId(_) + | SolverDefId::ImplId(_) + | SolverDefId::TraitAliasId(_) + | SolverDefId::Ctor(..) + | SolverDefId::InternedOpaqueTyId(..) => panic!(), + }; + + match container { + ItemContainerId::ImplId(it) => it.into(), + ItemContainerId::TraitId(it) => it.into(), + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(), + } + } + + fn recursion_limit(self) -> usize { + 50 + } + + fn features(self) -> Self::Features { + Features + } + + fn fn_sig( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder>> + { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(ctor) => match ctor { + super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id), + super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id), + }, + def => unreachable!("{:?}", def), + }; + self.db().callable_item_signature_ns(id) + } + + fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability { + unimplemented!() + } + + fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId { + unimplemented!() + } + + fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { + let sized_trait = + LangItem::Sized.resolve_trait(self.db(), self.krate.expect("Must have self.krate")); + let Some(sized_id) = sized_trait else { + return false; /* No Sized trait, can't require it! */ + }; + let sized_def_id = sized_id.into(); + + // Search for a predicate like `Self : Sized` amongst the trait bounds. + let predicates = self.predicates_of(def_id); + elaborate(self, predicates.iter_identity()).any(|pred| match pred.kind().skip_binder() { + ClauseKind::Trait(ref trait_pred) => { + trait_pred.def_id() == sized_def_id + && matches!( + trait_pred.self_ty().kind(), + TyKind::Param(ParamTy { index: 0, .. }) + ) + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(..) + | ClauseKind::UnstableFeature(_) => false, + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter(self, elaborate(self, bounds).collect::>()) + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter( + self, + elaborate(self, bounds).filter_only_self().collect::>(), + ) + }) + } + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect(); + let own_bounds: FxHashSet<_> = + self.item_self_bounds(def_id).skip_binder().into_iter().collect(); + if all_bounds.len() == own_bounds.len() { + EarlyBinder::bind(Clauses::new_from_iter(self, [])) + } else { + EarlyBinder::bind(Clauses::new_from_iter( + self, + all_bounds.difference(&own_bounds).cloned(), + )) + } + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn own_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_super_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> + { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + rustc_type_ir::EarlyBinder::bind(predicates) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_implied_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> + { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + rustc_type_ir::EarlyBinder::bind(predicates) + } + + fn impl_super_outlives( + self, + impl_def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait"); + trait_ref.map_bound(|trait_ref| { + let clause: Clause<'_> = trait_ref.upcast(self); + Clauses::new_from_iter( + self, + rustc_type_ir::elaborate::elaborate(self, [clause]).filter(|clause| { + matches!( + clause.kind().skip_binder(), + ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_) + ) + }), + ) + }) + } + + fn const_conditions( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + impl IntoIterator>>, + > { + rustc_type_ir::EarlyBinder::bind([unimplemented!()]) + } + + fn has_target_features(self, def_id: Self::DefId) -> bool { + false + } + + fn require_lang_item( + self, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> Self::DefId { + let lang_item = match lang_item { + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => { + LangItem::AsyncFnOnceOutput + } + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => { + LangItem::CallOnceFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => { + LangItem::CallRefFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone, + rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy, + rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine, + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => { + LangItem::CoroutineReturn + } + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => { + LangItem::CoroutineYield + } + rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct, + rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => { + LangItem::DiscriminantKind + } + rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop, + rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn, + rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future, + rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, + rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator, + rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll, + rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized, + rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized, + rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => { + LangItem::TransmuteTrait + } + rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple, + rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin, + rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize, + rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => { + unimplemented!() + } + }; + let target = hir_def::lang_item::lang_item( + self.db(), + self.krate.expect("Must have self.krate"), + lang_item, + ) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")); + match target { + hir_def::lang_item::LangItemTarget::EnumId(enum_id) => enum_id.into(), + hir_def::lang_item::LangItemTarget::Function(function_id) => function_id.into(), + hir_def::lang_item::LangItemTarget::ImplDef(impl_id) => impl_id.into(), + hir_def::lang_item::LangItemTarget::Static(static_id) => static_id.into(), + hir_def::lang_item::LangItemTarget::Struct(struct_id) => struct_id.into(), + hir_def::lang_item::LangItemTarget::Union(union_id) => union_id.into(), + hir_def::lang_item::LangItemTarget::TypeAlias(type_alias_id) => type_alias_id.into(), + hir_def::lang_item::LangItemTarget::Trait(trait_id) => trait_id.into(), + hir_def::lang_item::LangItemTarget::EnumVariant(enum_variant_id) => unimplemented!(), + } + } + + #[allow(clippy::match_like_matches_macro)] + fn is_lang_item( + self, + def_id: Self::DefId, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> bool { + use rustc_type_ir::lang_items::TraitSolverLangItem::*; + + // FIXME: derive PartialEq on TraitSolverLangItem + self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) { + (AsyncFn, AsyncFn) => true, + (AsyncFnKindHelper, AsyncFnKindHelper) => true, + (AsyncFnKindUpvars, AsyncFnKindUpvars) => true, + (AsyncFnMut, AsyncFnMut) => true, + (AsyncFnOnce, AsyncFnOnce) => true, + (AsyncFnOnceOutput, AsyncFnOnceOutput) => true, + (AsyncIterator, AsyncIterator) => true, + (CallOnceFuture, CallOnceFuture) => true, + (CallRefFuture, CallRefFuture) => true, + (Clone, Clone) => true, + (Copy, Copy) => true, + (Coroutine, Coroutine) => true, + (CoroutineReturn, CoroutineReturn) => true, + (CoroutineYield, CoroutineYield) => true, + (Destruct, Destruct) => true, + (DiscriminantKind, DiscriminantKind) => true, + (Drop, Drop) => true, + (DynMetadata, DynMetadata) => true, + (Fn, Fn) => true, + (FnMut, FnMut) => true, + (FnOnce, FnOnce) => true, + (FnPtrTrait, FnPtrTrait) => true, + (FusedIterator, FusedIterator) => true, + (Future, Future) => true, + (FutureOutput, FutureOutput) => true, + (Iterator, Iterator) => true, + (Metadata, Metadata) => true, + (Option, Option) => true, + (PointeeTrait, PointeeTrait) => true, + (Poll, Poll) => true, + (Sized, Sized) => true, + (TransmuteTrait, TransmuteTrait) => true, + (Tuple, Tuple) => true, + (Unpin, Unpin) => true, + (Unsize, Unsize) => true, + _ => false, + }) + } + + fn as_lang_item( + self, + def_id: Self::DefId, + ) -> Option { + use rustc_type_ir::lang_items::TraitSolverLangItem; + + let def_id: AttrDefId = match def_id { + SolverDefId::TraitId(id) => id.into(), + SolverDefId::TypeAliasId(id) => id.into(), + _ => panic!("Unexpected SolverDefId in as_lang_item"), + }; + let lang_item = self.db().lang_attr(def_id)?; + Some(match lang_item { + LangItem::Sized => TraitSolverLangItem::Sized, + LangItem::MetaSized => TraitSolverLangItem::MetaSized, + LangItem::PointeeSized => TraitSolverLangItem::PointeeSized, + LangItem::Unsize => TraitSolverLangItem::Unsize, + LangItem::StructuralPeq => return None, + LangItem::StructuralTeq => return None, + LangItem::Copy => TraitSolverLangItem::Copy, + LangItem::Clone => TraitSolverLangItem::Clone, + LangItem::Sync => return None, + LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind, + LangItem::Discriminant => return None, + LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait, + LangItem::Metadata => TraitSolverLangItem::Metadata, + LangItem::DynMetadata => TraitSolverLangItem::DynMetadata, + LangItem::Freeze => return None, + LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait, + LangItem::FnPtrAddr => return None, + LangItem::Drop => TraitSolverLangItem::Drop, + LangItem::Destruct => TraitSolverLangItem::Destruct, + LangItem::CoerceUnsized => return None, + LangItem::DispatchFromDyn => return None, + LangItem::TransmuteOpts => return None, + LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait, + LangItem::Add => return None, + LangItem::Sub => return None, + LangItem::Mul => return None, + LangItem::Div => return None, + LangItem::Rem => return None, + LangItem::Neg => return None, + LangItem::Not => return None, + LangItem::BitXor => return None, + LangItem::BitAnd => return None, + LangItem::BitOr => return None, + LangItem::Shl => return None, + LangItem::Shr => return None, + LangItem::AddAssign => return None, + LangItem::SubAssign => return None, + LangItem::MulAssign => return None, + LangItem::DivAssign => return None, + LangItem::RemAssign => return None, + LangItem::BitXorAssign => return None, + LangItem::BitAndAssign => return None, + LangItem::BitOrAssign => return None, + LangItem::ShlAssign => return None, + LangItem::ShrAssign => return None, + LangItem::Index => return None, + LangItem::IndexMut => return None, + LangItem::UnsafeCell => return None, + LangItem::VaList => return None, + LangItem::Deref => return None, + LangItem::DerefMut => return None, + LangItem::DerefTarget => return None, + LangItem::Receiver => return None, + LangItem::Fn => TraitSolverLangItem::Fn, + LangItem::FnMut => TraitSolverLangItem::FnMut, + LangItem::FnOnce => TraitSolverLangItem::FnOnce, + LangItem::FnOnceOutput => return None, + LangItem::Future => TraitSolverLangItem::Future, + LangItem::CoroutineState => return None, + LangItem::Coroutine => TraitSolverLangItem::Coroutine, + LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn, + LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield, + LangItem::Unpin => TraitSolverLangItem::Unpin, + LangItem::Pin => return None, + LangItem::PartialEq => return None, + LangItem::PartialOrd => return None, + LangItem::CVoid => return None, + LangItem::Panic => return None, + LangItem::PanicNounwind => return None, + LangItem::PanicFmt => return None, + LangItem::PanicDisplay => return None, + LangItem::ConstPanicFmt => return None, + LangItem::PanicBoundsCheck => return None, + LangItem::PanicMisalignedPointerDereference => return None, + LangItem::PanicInfo => return None, + LangItem::PanicLocation => return None, + LangItem::PanicImpl => return None, + LangItem::PanicCannotUnwind => return None, + LangItem::BeginPanic => return None, + LangItem::FormatAlignment => return None, + LangItem::FormatArgument => return None, + LangItem::FormatArguments => return None, + LangItem::FormatCount => return None, + LangItem::FormatPlaceholder => return None, + LangItem::FormatUnsafeArg => return None, + LangItem::ExchangeMalloc => return None, + LangItem::BoxFree => return None, + LangItem::DropInPlace => return None, + LangItem::AllocLayout => return None, + LangItem::Start => return None, + LangItem::EhPersonality => return None, + LangItem::EhCatchTypeinfo => return None, + LangItem::OwnedBox => return None, + LangItem::PhantomData => return None, + LangItem::ManuallyDrop => return None, + LangItem::MaybeUninit => return None, + LangItem::AlignOffset => return None, + LangItem::Termination => return None, + LangItem::Try => return None, + LangItem::Tuple => TraitSolverLangItem::Tuple, + LangItem::SliceLen => return None, + LangItem::TryTraitFromResidual => return None, + LangItem::TryTraitFromOutput => return None, + LangItem::TryTraitBranch => return None, + LangItem::TryTraitFromYeet => return None, + LangItem::PointerLike => return None, + LangItem::ConstParamTy => return None, + LangItem::Poll => TraitSolverLangItem::Poll, + LangItem::PollReady => return None, + LangItem::PollPending => return None, + LangItem::ResumeTy => return None, + LangItem::GetContext => return None, + LangItem::Context => return None, + LangItem::FuturePoll => return None, + LangItem::FutureOutput => TraitSolverLangItem::FutureOutput, + LangItem::Option => TraitSolverLangItem::Option, + LangItem::OptionSome => return None, + LangItem::OptionNone => return None, + LangItem::ResultOk => return None, + LangItem::ResultErr => return None, + LangItem::ControlFlowContinue => return None, + LangItem::ControlFlowBreak => return None, + LangItem::IntoFutureIntoFuture => return None, + LangItem::IntoIterIntoIter => return None, + LangItem::IteratorNext => return None, + LangItem::Iterator => TraitSolverLangItem::Iterator, + LangItem::PinNewUnchecked => return None, + LangItem::RangeFrom => return None, + LangItem::RangeFull => return None, + LangItem::RangeInclusiveStruct => return None, + LangItem::RangeInclusiveNew => return None, + LangItem::Range => return None, + LangItem::RangeToInclusive => return None, + LangItem::RangeTo => return None, + LangItem::String => return None, + LangItem::CStr => return None, + LangItem::AsyncFn => TraitSolverLangItem::AsyncFn, + LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut, + LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture, + LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture, + LangItem::Ordering => return None, + LangItem::PanicNullPointerDereference => return None, + LangItem::ReceiverTarget => return None, + LangItem::UnsafePinned => return None, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + }) + } + + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + trait_.trait_items(self.db()).associated_types().map(|id| id.into()) + } + + fn for_each_relevant_impl( + self, + trait_def_id: Self::DefId, + self_ty: Self::Ty, + mut f: impl FnMut(Self::DefId), + ) { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("for_each_relevant_impl called for non-trait"), + }; + + let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); + let fps: &[TyFingerprint] = match self_ty.kind() { + TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, + TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS, + _ => self_ty_fp.as_slice(), + }; + + if fps.is_empty() { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for i in impls.for_trait(trait_) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + ControlFlow::Continue(()) + }, + ); + } else { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for fp in fps { + for i in impls.for_trait_and_self_ty(trait_, *fp) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + } + ControlFlow::Continue(()) + }, + ); + } + } + + fn has_item_definition(self, def_id: Self::DefId) -> bool { + // FIXME: should check if has value + true + } + + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool { + // FIXME + false + } + + #[tracing::instrument(skip(self), ret)] + fn impl_trait_ref( + self, + impl_def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId in impl_trait_ref"), + }; + + let db = self.db(); + + db.impl_trait_ns(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach trait solving + .expect("invalid impl passed to trait solver") + } + + fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let impl_data = self.db().impl_signature(impl_id); + if impl_data.flags.contains(ImplFlags::NEGATIVE) { + ImplPolarity::Negative + } else { + ImplPolarity::Positive + } + } + + fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_auto"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::AUTO) + } + + fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { + matches!(trait_def_id, SolverDefId::TraitAliasId(_)) + } + + fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none() + } + + fn trait_is_fundamental(self, def_id: Self::DefId) -> bool { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_fundamental"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::FUNDAMENTAL) + } + + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed { + panic!("Bug encountered in next-trait-solver.") + } + + fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams { + let id = match adt_def_id { + SolverDefId::AdtId(id) => id, + _ => unreachable!(), + }; + let def = AdtDef::new(id, self); + let num_params = self.generics_of(adt_def_id).count(); + + let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() { + GenericArgKind::Type(ty) => match ty.kind() { + rustc_type_ir::TyKind::Param(p) => Some(p.index), + _ => None, + }, + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Const(ct) => match ct.kind() { + rustc_type_ir::ConstKind::Param(p) => Some(p.index), + _ => None, + }, + }; + + // The last field of the structure has to exist and contain type/const parameters. + let variant = def.non_enum_variant(); + let fields = variant.fields(self.db()); + let Some((tail_field, prefix_fields)) = fields.split_last() else { + return UnsizingParams(DenseBitSet::new_empty(num_params)); + }; + + let field_types = self.db().field_types_ns(variant.id()); + let mut unsizing_params = DenseBitSet::new_empty(num_params); + let ty = field_types[tail_field.0]; + for arg in ty.instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in field_types[field.0].instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.remove(i); + } + } + } + + UnsizingParams(unsizing_params) + } + + fn anonymize_bound_vars>( + self, + value: rustc_type_ir::Binder, + ) -> rustc_type_ir::Binder { + struct Anonymize<'a, 'db> { + interner: DbInterner<'db>, + map: &'a mut FxIndexMap, + } + impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + let entry = self.map.entry(br.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon))) + .expect_region(); + let br = BoundRegion { var, kind }; + Region::new_bound(self.interner, DebruijnIndex::ZERO, br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + let entry = self.map.entry(bt.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = + (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); + Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + let entry = self.map.entry(bv); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); + Const::new_bound(self.interner, DebruijnIndex::ZERO, var) + } + } + + let mut map = Default::default(); + let delegate = Anonymize { interner: self, map: &mut map }; + let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); + let bound_vars = CollectAndApply::collect_and_apply(map.into_values(), |xs| { + BoundVarKinds::new_from_iter(self, xs.iter().cloned()) + }); + Binder::bind_with_vars(inner, bound_vars) + } + + fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds { + // FIXME(next-solver) + SolverDefIds::new_from_iter(self, []) + } + + fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn explicit_implied_const_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + impl IntoIterator>>, + > { + // FIXME(next-solver) + rustc_type_ir::EarlyBinder::bind([]) + } + + fn fn_is_const(self, def_id: Self::DefId) -> bool { + let id = match def_id { + SolverDefId::FunctionId(id) => id, + _ => unreachable!(), + }; + self.db().function_signature(id).flags.contains(FnFlags::CONST) + } + + fn impl_is_const(self, def_id: Self::DefId) -> bool { + false + } + + fn opt_alias_variances( + self, + kind: impl Into, + def_id: Self::DefId, + ) -> Option { + None + } + + fn type_of_opaque_hir_typeck( + self, + def_id: Self::LocalDefId, + ) -> rustc_type_ir::EarlyBinder { + // FIXME(next-solver) + unimplemented!() + } + + fn coroutine_hidden_types( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + rustc_type_ir::Binder>, + > { + // FIXME(next-solver) + unimplemented!() + } + + fn is_default_trait(self, def_id: Self::DefId) -> bool { + self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized)) + } + + fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE) + } + + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE) + } + + fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { + false + } + + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + false + } + + fn next_trait_solver_globally(self) -> bool { + true + } + + fn opaque_types_and_coroutines_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + // FIXME(next-solver) + unimplemented!() + } +} + +impl<'db> DbInterner<'db> { + pub fn shift_bound_var_indices(self, bound_vars: usize, value: T) -> T + where + T: rustc_type_ir::TypeFoldable, + { + let shift_bv = |bv: BoundVar| BoundVar::from_usize(bv.as_usize() + bound_vars); + self.replace_escaping_bound_vars_uncached( + value, + FnMutDelegate { + regions: &mut |r: BoundRegion| { + Region::new_bound( + self, + DebruijnIndex::ZERO, + BoundRegion { var: shift_bv(r.var), kind: r.kind }, + ) + }, + types: &mut |t: BoundTy| { + Ty::new_bound( + self, + DebruijnIndex::ZERO, + BoundTy { var: shift_bv(t.var), kind: t.kind }, + ) + }, + consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)), + }, + ) + } + + pub fn replace_escaping_bound_vars_uncached>>( + self, + value: T, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + if !value.has_escaping_bound_vars() { + value + } else { + let mut replacer = BoundVarReplacer::new(self, delegate); + value.fold_with(&mut replacer) + } + } + + pub fn replace_bound_vars_uncached>>( + self, + value: Binder<'db, T>, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) + } +} + +macro_rules! TrivialTypeTraversalImpls { + ($($ty:ty,)+) => { + $( + impl<'db> rustc_type_ir::TypeFoldable> for $ty { + fn try_fold_with>>( + self, + _: &mut F, + ) -> ::std::result::Result { + Ok(self) + } + + #[inline] + fn fold_with>>( + self, + _: &mut F, + ) -> Self { + self + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $ty { + #[inline] + fn visit_with>>( + &self, + _: &mut F) + -> F::Result + { + ::output() + } + } + )+ + }; +} + +TrivialTypeTraversalImpls! { + SolverDefId, + Pattern<'db>, + Safety, + FnAbi, + Span, + ParamConst, + ParamTy, + BoundRegion, + BoundVar, + Placeholder, + Placeholder, + Placeholder, +} + +pub(crate) use tls_cache::with_new_cache; +mod tls_cache { + use crate::db::HirDatabase; + + use super::DbInterner; + use rustc_type_ir::search_graph::GlobalCache; + use std::cell::RefCell; + + scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell>>); + + pub(crate) fn with_new_cache(f: impl FnOnce() -> T) -> T { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f) + } + + pub(super) fn with_cache<'db, T>( + _db: &'db dyn HirDatabase, + f: impl FnOnce(&mut GlobalCache>) -> T, + ) -> T { + // SAFETY: No idea + let call = move |slot: &RefCell<_>| { + f(unsafe { + std::mem::transmute::< + &mut GlobalCache>, + &mut GlobalCache>, + >(&mut *slot.borrow_mut()) + }) + }; + if GLOBAL_CACHE.is_set() { + GLOBAL_CACHE.with(call) + } else { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call)) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs new file mode 100644 index 0000000000000..4d32b27132357 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -0,0 +1,267 @@ +//! Things related to IR printing in the next-trait-solver. + +use std::any::type_name_of_val; + +use rustc_type_ir::inherent::SliceLike; +use rustc_type_ir::{self as ty, ir_print::IrPrint}; + +use crate::db::HirDatabase; + +use super::SolverDefId; +use super::interner::DbInterner; + +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTy({:?}[{:?}])", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTy({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTy({:?}[{:?}])", t.def_id, t.args))) + } +} + +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::AliasTerm, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTerm, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTerm({:?}[{:?}])", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTerm({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTerm({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + let self_ty = &t.args.as_slice()[0]; + let trait_args = &t.args.as_slice()[1..]; + if trait_args.is_empty() { + fmt.write_str(&format!( + "{:?}: {}", + self_ty, + db.as_view::().trait_signature(trait_).name.as_str() + )) + } else { + fmt.write_str(&format!( + "{:?}: {}<{:?}>", + self_ty, + db.as_view::().trait_signature(trait_).name.as_str(), + trait_args + )) + } + }) + .unwrap_or_else(|| fmt.write_str(&format!("TraitRef({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::TraitPredicate, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::TraitPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::HostEffectPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::HostEffectPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ExistentialTraitRef, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialTraitRef, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialTraitRef({:?}[{:?}])", + db.as_view::().trait_signature(trait_).name.as_str(), + t.args + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!("ExistentialTraitRef({:?}[{:?}])", t.def_id, t.args)) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ExistentialProjection, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialProjection, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + t.def_id, t.args, t.term + )) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ProjectionPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ProjectionPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.projection_term.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + db.as_view::().type_alias_signature(id).name.as_str(), + t.projection_term.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + t.projection_term.def_id, t.projection_term.args, t.term + )) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::NormalizesTo, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::NormalizesTo, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::SubtypePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::SubtypePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::CoercePredicate, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::CoercePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::FnSig, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::FnSig, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} + +impl<'db> IrPrint>> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::PatternKind>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::PatternKind>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs new file mode 100644 index 0000000000000..3a3206bef38b5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -0,0 +1,1368 @@ +//! Things useful for mapping to/from Chalk and next-trait-solver types. + +use base_db::Crate; +use chalk_ir::{ + CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind, + WellFormed, fold::Shift, interner::HasInterner, +}; +use hir_def::{ + CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, + TypeOrConstParamId, TypeParamId, signatures::TraitFlags, +}; +use intern::sym; +use rustc_type_ir::{ + AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, + OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, UniverseIndex, elaborate, + inherent::{BoundVarLike, Clause as _, IntoKind, PlaceholderLike, SliceLike, Ty as _}, + shift_vars, + solve::Goal, +}; +use salsa::plumbing::AsId; + +use crate::{ + ConcreteConst, ConstScalar, ImplTraitId, Interner, + db::{ + HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, + InternedTypeOrConstParamId, + }, + from_assoc_type_id, from_chalk_trait_id, + mapping::ToChalk, + next_solver::{ + Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, + interner::{AdtDef, BoundVarKind, BoundVarKinds, DbInterner}, + }, + to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, +}; + +use super::{ + BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, + BoundTyKind, Canonical, CanonicalVars, Clause, Clauses, Const, Ctor, EarlyParamRegion, + ErrorGuaranteed, ExistentialPredicate, GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, + Placeholder, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, +}; + +pub fn to_placeholder_idx( + db: &dyn HirDatabase, + id: TypeOrConstParamId, + map: impl Fn(BoundVar) -> T, +) -> Placeholder { + let interned_id = InternedTypeOrConstParamId::new(db, id); + Placeholder { + universe: UniverseIndex::ZERO, + bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)), + } +} + +pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( + interner: DbInterner<'db>, + binder: rustc_type_ir::Binder, T>, +) -> rustc_type_ir::EarlyBinder, T> { + let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO }; + rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) +} + +struct BinderToEarlyBinder<'db> { + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, +} + +impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder( + &mut self, + t: rustc_type_ir::Binder, T>, + ) -> rustc_type_ir::Binder, T> + where + T: TypeFoldable>, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_ty.var(); + Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() })) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_region.var(); + Region::new( + self.cx(), + rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { + index: var.as_u32(), + }), + ) + } + _ => r, + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + match c.kind() { + rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { + Const::new( + self.cx(), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }), + ) + } + _ => c.super_fold_with(self), + } + } +} + +pub trait ChalkToNextSolver<'db, Out> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; +} + +impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new( + interner, + match self.kind(Interner) { + chalk_ir::TyKind::Adt(adt_id, substitution) => { + let def = AdtDef::new(adt_id.0, interner); + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Adt(def, args) + } + chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(*assoc_type_id)); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, def_id, args.iter()); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias_ty) + } + chalk_ir::TyKind::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => rustc_type_ir::TyKind::Bool, + chalk_ir::Scalar::Char => rustc_type_ir::TyKind::Char, + chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I8) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I16) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I32) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I64) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I128) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U16) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U64) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U128) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F16) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F32) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F64) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F128) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) + } + }, + chalk_ir::TyKind::Tuple(_, substitution) => { + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Tuple(args) + } + chalk_ir::TyKind::Array(ty, len) => rustc_type_ir::TyKind::Array( + ty.to_nextsolver(interner), + len.to_nextsolver(interner), + ), + chalk_ir::TyKind::Slice(ty) => { + rustc_type_ir::TyKind::Slice(ty.to_nextsolver(interner)) + } + chalk_ir::TyKind::Raw(mutability, ty) => rustc_type_ir::RawPtr( + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::Ref(mutability, lifetime, ty) => rustc_type_ir::TyKind::Ref( + lifetime.to_nextsolver(interner), + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::OpaqueType(def_id, substitution) => { + let id: InternedOpaqueTyId = (*def_id).into(); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, id.into(), args); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { + let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); + let id: SolverDefId = match def_id { + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), + CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)), + }; + rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, + chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, + chalk_ir::TyKind::Closure(closure_id, substitution) => { + let id: InternedClosureId = (*closure_id).into(); + rustc_type_ir::TyKind::Closure(id.into(), substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Coroutine(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::Coroutine( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::CoroutineWitness(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::CoroutineWitness( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( + SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)), + ), + chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), + chalk_ir::TyKind::Placeholder(placeholder_index) => { + rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::TyKind::Dyn(dyn_ty) => { + // exists { for<...> ^1.0: ... } + let bounds = BoundExistentialPredicates::new_from_iter( + interner, + dyn_ty.bounds.skip_binders().iter(Interner).filter_map(|pred| { + // for<...> ^1.0: ... + let (val, binders) = pred.clone().into_value_and_skipped_binders(); + let bound_vars = binders.to_nextsolver(interner); + let clause = match val { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if interner + .db() + .trait_signature(trait_id) + .flags + .contains(TraitFlags::AUTO) + { + ExistentialPredicate::AutoTrait(SolverDefId::TraitId( + trait_id, + )) + } else { + let def_id = SolverDefId::TraitId(trait_id); + let args = GenericArgs::new_from_iter( + interner, + trait_ref + .substitution + .iter(Interner) + .skip(1) + .map(|a| a.clone().shifted_out(Interner).unwrap()) + .map(|a| a.to_nextsolver(interner)), + ); + let trait_ref = ExistentialTraitRef::new_from_args( + interner, def_id, args, + ); + ExistentialPredicate::Trait(trait_ref) + } + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let (def_id, args) = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(projection) => { + let id = + from_assoc_type_id(projection.associated_ty_id); + let def_id = SolverDefId::TypeAliasId(id); + let generics = interner.generics_of(def_id); + let parent_len = generics.parent_count; + let substs = projection.substitution.iter(Interner).skip(1); + + let args = GenericArgs::new_from_iter( + interner, + substs + .map(|a| { + a.clone().shifted_out(Interner).unwrap() + }) + .map(|a| a.to_nextsolver(interner)), + ); + (def_id, args) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + panic!("Invalid ExistentialPredicate (opaques can't be named)."); + } + }; + let term = alias_eq + .ty + .clone() + .shifted_out(Interner) + .unwrap() + .to_nextsolver(interner) + .into(); + let projection = ExistentialProjection::new_from_args( + interner, def_id, args, term, + ); + ExistentialPredicate::Projection(projection) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + return None; + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => return None, + }; + + Some(Binder::bind_with_vars(clause, bound_vars)) + }), + ); + let region = dyn_ty.lifetime.to_nextsolver(interner); + let kind = rustc_type_ir::DynKind::Dyn; + rustc_type_ir::TyKind::Dynamic(bounds, region, kind) + } + chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { + chalk_ir::AliasTy::Projection(projection_ty) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id( + projection_ty.associated_ty_id, + )); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + projection_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Projection, + alias_ty, + ) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + let id: InternedOpaqueTyId = opaque_ty.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + opaque_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + }, + chalk_ir::TyKind::Function(fn_pointer) => { + let sig_tys = fn_pointer.clone().into_binders(Interner).to_nextsolver(interner); + let header = rustc_type_ir::FnHeader { + abi: fn_pointer.sig.abi, + c_variadic: fn_pointer.sig.variadic, + safety: match fn_pointer.sig.safety { + chalk_ir::Safety::Safe => super::abi::Safety::Safe, + chalk_ir::Safety::Unsafe => super::abi::Safety::Unsafe, + }, + }; + + rustc_type_ir::TyKind::FnPtr(sig_tys, header) + } + chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + BoundTy { + var: rustc_type_ir::BoundVar::from_usize(bound_var.index), + kind: BoundTyKind::Anon, + }, + ), + chalk_ir::TyKind::InferenceVar(inference_var, ty_variable_kind) => { + rustc_type_ir::TyKind::Infer( + (*inference_var, *ty_variable_kind).to_nextsolver(interner), + ) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Region<'db> { + Region::new( + interner, + match self.data(Interner) { + chalk_ir::LifetimeData::BoundVar(bound_var) => rustc_type_ir::RegionKind::ReBound( + bound_var.debruijn.to_nextsolver(interner), + BoundRegion { + var: rustc_type_ir::BoundVar::from_u32(bound_var.index as u32), + kind: BoundRegionKind::Anon, + }, + ), + chalk_ir::LifetimeData::InferenceVar(inference_var) => { + rustc_type_ir::RegionKind::ReVar(rustc_type_ir::RegionVid::from_u32( + inference_var.index(), + )) + } + chalk_ir::LifetimeData::Placeholder(placeholder_index) => { + rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon( + rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32), + rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32), + )) + } + chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, + chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, + chalk_ir::LifetimeData::Phantom(_, _) => { + unreachable!() + } + chalk_ir::LifetimeData::Error => { + rustc_type_ir::RegionKind::ReError(ErrorGuaranteed) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Const<'db> { + let data = self.data(Interner); + Const::new( + interner, + match &data.value { + chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(bound_var.index), + ), + chalk_ir::ConstValue::InferenceVar(inference_var) => { + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( + rustc_type_ir::ConstVid::from_u32(inference_var.index()), + )) + } + chalk_ir::ConstValue::Placeholder(placeholder_index) => { + rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { + ConstScalar::Bytes(bytes, memory) => { + rustc_type_ir::ConstKind::Value(ValueConst::new( + data.ty.to_nextsolver(interner), + ConstBytes(bytes.clone(), memory.clone()), + )) + } + ConstScalar::UnevaluatedConst(c, subst) => { + let def = match *c { + GeneralConstId::ConstId(id) => SolverDefId::ConstId(id), + GeneralConstId::StaticId(id) => SolverDefId::StaticId(id), + }; + let args = subst.to_nextsolver(interner); + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(def, args)) + } + ConstScalar::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }, + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> + for chalk_ir::FnSubst +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::FnSigTys> { + rustc_type_ir::FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + self.0.iter(Interner).map(|g| g.assert_ty_ref(Interner).to_nextsolver(interner)), + ), + } + } +} + +impl< + 'db, + U: TypeVisitable>, + T: Clone + ChalkToNextSolver<'db, U> + HasInterner, +> ChalkToNextSolver<'db, rustc_type_ir::Binder, U>> for chalk_ir::Binders +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> rustc_type_ir::Binder, U> { + let (val, binders) = self.clone().into_value_and_skipped_binders(); + rustc_type_ir::Binder::bind_with_vars( + val.to_nextsolver(interner), + binders.to_nextsolver(interner), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds { + BoundVarKinds::new_from_iter( + interner, + self.iter(Interner).map(|v| v.to_nextsolver(interner)), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind { + match self { + chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon), + chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon), + chalk_ir::VariableKind::Const(_ty) => BoundVarKind::Const, + } + } +} + +impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> { + match self.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Lifetime(lifetime) => lifetime.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Const(const_) => const_.to_nextsolver(interner).into(), + } + } +} +impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { + GenericArgs::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> GenericArg<'db> { arg.to_nextsolver(interner) }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Tys<'db> { + Tys::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> Ty<'db> { + match arg.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner), + chalk_ir::GenericArgData::Lifetime(_) => unreachable!(), + chalk_ir::GenericArgData::Const(_) => unreachable!(), + } + }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { + rustc_type_ir::DebruijnIndex::from_u32(self.depth()) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { + rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> + for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind) +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::InferTy { + match self.1 { + chalk_ir::TyVariableKind::General => { + rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Integer => { + rustc_type_ir::InferTy::IntVar(rustc_type_ir::IntVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Float => { + rustc_type_ir::InferTy::FloatVar(rustc_type_ir::FloatVid::from_u32(self.0.index())) + } + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { + match self { + chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut, + chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { + match self { + crate::Variance::Covariant => rustc_type_ir::Variance::Covariant, + crate::Variance::Invariant => rustc_type_ir::Variance::Invariant, + crate::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, + crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate<'db>>>> + for chalk_ir::Canonical>> +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> Canonical<'db, Goal, Predicate<'db>>> { + let param_env = self.value.environment.to_nextsolver(interner); + let variables = CanonicalVars::new_from_iter( + interner, + self.binders.iter(Interner).map(|k| match &k.kind { + chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { + TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT), + ), + TyVariableKind::Integer => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + }, + chalk_ir::VariableKind::Lifetime => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) + } + chalk_ir::VariableKind::Const(ty) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT) + } + }), + ); + Canonical { + max_universe: UniverseIndex::ROOT, + value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)), + variables, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> { + match self.data(Interner) { + chalk_ir::GoalData::Quantified(quantifier_kind, binders) => { + if !binders.binders.is_empty(Interner) { + panic!("Should not be constructed."); + } + let (val, _) = binders.clone().into_value_and_skipped_binders(); + val.shifted_out(Interner).unwrap().to_nextsolver(interner) + } + chalk_ir::GoalData::Implies(program_clauses, goal) => { + panic!("Should not be constructed.") + } + chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), + chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { + let subtype_predicate = SubtypePredicate { + a: subtype_goal.a.to_nextsolver(interner), + b: subtype_goal.b.to_nextsolver(interner), + a_is_expected: true, + }; + let pred_kind = PredicateKind::Subtype(subtype_predicate); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::DomainGoal(domain_goal) => { + let pred_kind = domain_goal.to_nextsolver(interner); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { + let clauses = Clauses::new_from_iter( + interner, + self.clauses.iter(Interner).map(|c| c.to_nextsolver(interner)), + ); + let clauses = + Clauses::new_from_iter(interner, elaborate::elaborate(interner, clauses.iter())); + ParamEnv { clauses } + } +} + +impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { + Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> + for chalk_ir::ProgramClauseImplication +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + assert!(self.conditions.is_empty(Interner)); + assert!(self.constraints.is_empty(Interner)); + self.consequence.to_nextsolver(interner) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::DomainGoal::Holds(where_clause) => match where_clause { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => { + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(p.associated_ty_id)); + let args = p.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::AliasTy::Opaque(opaque) => { + let id: InternedOpaqueTyId = opaque.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let args = opaque.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let opaque_ty = Ty::new( + interner, + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Opaque, + rustc_type_ir::AliasTy::new_from_args(interner, def_id, args), + ), + ) + .into(); + PredicateKind::AliasRelate( + opaque_ty, + term, + rustc_type_ir::AliasRelationDirection::Equate, + ) + } + }, + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let predicate = OutlivesPredicate( + lifetime_outlives.a.to_nextsolver(interner), + lifetime_outlives.b.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::RegionOutlives(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let predicate = OutlivesPredicate( + type_outlives.ty.to_nextsolver(interner), + type_outlives.lifetime.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::TypeOutlives(predicate)) + } + }, + chalk_ir::DomainGoal::Normalize(normalize) => { + let proj_ty = match &normalize.alias { + chalk_ir::AliasTy::Projection(proj) => proj, + _ => unimplemented!(), + }; + let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); + let alias = rustc_type_ir::AliasTerm::new( + interner, + from_assoc_type_id(proj_ty.associated_ty_id).into(), + args, + ); + let term = normalize.ty.to_nextsolver(interner).into(); + let normalizes_to = rustc_type_ir::NormalizesTo { alias, term }; + PredicateKind::NormalizesTo(normalizes_to) + } + chalk_ir::DomainGoal::WellFormed(well_formed) => { + let term = match well_formed { + WellFormed::Trait(_) => panic!("Should not be constructed."), + WellFormed::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), + }; + PredicateKind::Clause(rustc_type_ir::ClauseKind::WellFormed(term)) + } + chalk_ir::DomainGoal::FromEnv(from_env) => match from_env { + chalk_ir::FromEnv::Trait(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::FromEnv::Ty(ty) => PredicateKind::Clause(ClauseKind::WellFormed( + Term::Ty(ty.to_nextsolver(interner)), + )), + }, + chalk_ir::DomainGoal::IsLocal(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsUpstream(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsFullyVisible(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::LocalImplAllowed(trait_ref) => { + panic!("Should not be constructed.") + } + chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."), + chalk_ir::DomainGoal::DownstreamType(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."), + chalk_ir::DomainGoal::ObjectSafe(trait_id) => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { + let args = self.substitution.to_nextsolver(interner); + TraitRef::new_from_args( + interner, + SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)), + args, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let projection = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => p, + _ => unimplemented!(), + }; + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(projection.associated_ty_id)); + let args = projection.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let ty = type_outlives.ty.to_nextsolver(interner); + let r = type_outlives.lifetime.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::TypeOutlives(OutlivesPredicate(ty, r))) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let a = lifetime_outlives.a.to_nextsolver(interner); + let b = lifetime_outlives.b.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::RegionOutlives(OutlivesPredicate(a, b))) + } + } + } +} + +pub fn convert_canonical_args_for_result<'db>( + interner: DbInterner<'db>, + args: Canonical<'db, Vec>>, +) -> chalk_ir::Canonical> { + let Canonical { value, variables, max_universe } = args; + let binders = CanonicalVarKinds::from_iter( + Interner, + variables.iter().map(|v| match v { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)), + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), + }), + ); + chalk_ir::Canonical { + binders, + value: chalk_ir::ConstrainedSubst { + constraints: chalk_ir::Constraints::empty(Interner), + subst: convert_args_for_result(interner, &value), + }, + } +} + +pub fn convert_args_for_result<'db>( + interner: DbInterner<'db>, + args: &[GenericArg<'db>], +) -> crate::Substitution { + let mut substs = Vec::with_capacity(args.len()); + for arg in args { + match (*arg).kind() { + rustc_type_ir::GenericArgKind::Type(ty) => { + let ty = convert_ty_for_result(interner, ty); + substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Lifetime(region) => { + let lifetime = convert_region_for_result(region); + substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Const(const_) => { + substs.push( + chalk_ir::GenericArgData::Const(convert_const_for_result(interner, const_)) + .intern(Interner), + ); + } + } + } + Substitution::from_iter(Interner, substs) +} + +pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { + use crate::{Scalar, TyKind}; + use chalk_ir::{FloatTy, IntTy, UintTy}; + match ty.kind() { + rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), + rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { + TyKind::Scalar(Scalar::Int(IntTy::I8)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { + TyKind::Scalar(Scalar::Int(IntTy::I16)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { + TyKind::Scalar(Scalar::Int(IntTy::I32)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { + TyKind::Scalar(Scalar::Int(IntTy::I64)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { + TyKind::Scalar(Scalar::Int(IntTy::I128)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { + TyKind::Scalar(Scalar::Int(IntTy::Isize)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { + TyKind::Scalar(Scalar::Uint(UintTy::U8)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { + TyKind::Scalar(Scalar::Uint(UintTy::U16)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { + TyKind::Scalar(Scalar::Uint(UintTy::U32)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { + TyKind::Scalar(Scalar::Uint(UintTy::U64)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { + TyKind::Scalar(Scalar::Uint(UintTy::U128)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { + TyKind::Scalar(Scalar::Uint(UintTy::Usize)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { + TyKind::Scalar(Scalar::Float(FloatTy::F16)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { + TyKind::Scalar(Scalar::Float(FloatTy::F32)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { + TyKind::Scalar(Scalar::Float(FloatTy::F64)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { + TyKind::Scalar(Scalar::Float(FloatTy::F128)) + } + rustc_type_ir::TyKind::Str => TyKind::Str, + rustc_type_ir::TyKind::Error(_) => TyKind::Error, + rustc_type_ir::TyKind::Never => TyKind::Never, + + rustc_type_ir::TyKind::Adt(def, args) => { + let adt_id = def.inner().id; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Adt(chalk_ir::AdtId(adt_id), subst) + } + + rustc_type_ir::TyKind::Infer(infer_ty) => { + let (var, kind) = match infer_ty { + rustc_type_ir::InferTy::TyVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::General) + } + rustc_type_ir::InferTy::IntVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) + } + rustc_type_ir::InferTy::FloatVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Float) + } + rustc_type_ir::InferTy::FreshFloatTy(..) + | rustc_type_ir::InferTy::FreshIntTy(..) + | rustc_type_ir::InferTy::FreshTy(..) => { + panic!("Freshening shouldn't happen.") + } + }; + TyKind::InferenceVar(var, kind) + } + + rustc_type_ir::TyKind::Ref(r, ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let r = convert_region_for_result(r); + let ty = convert_ty_for_result(interner, ty); + TyKind::Ref(mutability, r, ty) + } + + rustc_type_ir::TyKind::Tuple(tys) => { + let size = tys.len(); + let subst = Substitution::from_iter( + Interner, + tys.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) + .intern(Interner) + }), + ); + TyKind::Tuple(size, subst) + } + + rustc_type_ir::TyKind::Array(ty, const_) => { + let ty = convert_ty_for_result(interner, ty); + let const_ = convert_const_for_result(interner, const_); + TyKind::Array(ty, const_) + } + + rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { + rustc_type_ir::AliasTyKind::Projection => { + let assoc_ty_id = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let associated_ty_id = to_assoc_type_id(assoc_ty_id); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::AssociatedType(associated_ty_id, substitution) + } + rustc_type_ir::AliasTyKind::Opaque => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: opaque_ty_id.into(), + substitution, + })) + } + rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), + rustc_type_ir::AliasTyKind::Free => unimplemented!(), + }, + + rustc_type_ir::TyKind::Placeholder(placeholder) => { + let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; + let placeholder_index = + chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; + TyKind::Placeholder(placeholder_index) + } + + rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }), + + rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { + let num_binders = bound_sig.bound_vars().len(); + let sig = chalk_ir::FnSig { + abi: fn_header.abi, + safety: match fn_header.safety { + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: fn_header.c_variadic, + }; + let args = GenericArgs::new_from_iter( + interner, + bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), + ); + let substitution = convert_args_for_result(interner, args.as_slice()); + let substitution = chalk_ir::FnSubst(substitution); + let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; + TyKind::Function(fnptr) + } + + rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { + assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + let self_ty = Ty::new_bound( + interner, + DebruijnIndex::from_u32(1), + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, + ); + let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( + Interner, + preds.iter().map(|p| { + let binders = chalk_ir::VariableKinds::from_iter( + Interner, + p.bound_vars().iter().map(|b| match b { + BoundVarKind::Ty(kind) => { + chalk_ir::VariableKind::Ty(TyVariableKind::General) + } + BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => { + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)) + } + }), + ); + let where_clause = match p.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = TraitRef::new( + interner, + trait_ref.def_id, + [self_ty.into()].into_iter().chain(trait_ref.args.iter()), + ); + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = + convert_args_for_result(interner, trait_ref.args.as_slice()); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { + let trait_id = match trait_ { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = chalk_ir::Substitution::empty(Interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::Projection(existential_projection) => { + let projection = ProjectionPredicate { + projection_term: AliasTerm::new( + interner, + existential_projection.def_id, + [self_ty.into()] + .iter() + .chain(existential_projection.args.iter()), + ), + term: existential_projection.term, + }; + let associated_ty_id = match projection.projection_term.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = convert_args_for_result( + interner, + projection.projection_term.args.as_slice(), + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match projection.term { + Term::Ty(ty) => ty, + _ => unreachable!(), + }; + let ty = convert_ty_for_result(interner, ty); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::WhereClause::AliasEq(alias_eq) + } + }; + chalk_ir::Binders::new(binders, where_clause) + }), + ); + let binders = chalk_ir::VariableKinds::from1( + Interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let bounds = chalk_ir::Binders::new(binders, bounds); + let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) }; + TyKind::Dyn(dyn_ty) + } + + rustc_type_ir::TyKind::Slice(ty) => { + let ty = convert_ty_for_result(interner, ty); + TyKind::Slice(ty) + } + + rustc_type_ir::TyKind::Foreign(foreign) => { + let def_id = match foreign { + SolverDefId::ForeignId(id) => id, + _ => unreachable!(), + }; + TyKind::Foreign(to_foreign_def_id(def_id)) + } + rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), + rustc_type_ir::TyKind::RawPtr(ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let ty = convert_ty_for_result(interner, ty); + TyKind::Raw(mutability, ty) + } + rustc_type_ir::TyKind::FnDef(def_id, args) => { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), + SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::FnDef(id.to_chalk(interner.db()), subst) + } + + rustc_type_ir::TyKind::Closure(def_id, args) => { + let id = match def_id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Closure(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), + rustc_type_ir::TyKind::Coroutine(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Coroutine(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::CoroutineWitness(id.into(), subst) + } + + rustc_type_ir::TyKind::Param(_) => unimplemented!(), + rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), + } + .intern(Interner) +} + +fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const { + let value: chalk_ir::ConstValue = match const_.kind() { + rustc_type_ir::ConstKind::Param(_) => unimplemented!(), + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { + chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) + } + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + panic!("Vars should not be freshened.") + } + rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + var.index(), + )) + } + rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, + idx: placeholder_const.bound.as_usize(), + }) + } + rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { + let id = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::UnevaluatedConst(id, subst), + }) + } + rustc_type_ir::ConstKind::Value(value_const) => { + let bytes = value_const.value.inner(); + let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()), + }); + return chalk_ir::ConstData { + ty: convert_ty_for_result(interner, value_const.ty), + value, + } + .intern(Interner); + } + rustc_type_ir::ConstKind::Error(_) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) + } + rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), + }; + chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) +} + +fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { + match region.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), + rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.as_usize(), + )), + ), + rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), + rustc_type_ir::RegionKind::ReStatic => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + } + rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), + ), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + idx: placeholder.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, + }), + ), + rustc_type_ir::RegionKind::ReErased => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) + } + rustc_type_ir::RegionKind::ReError(_) => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs new file mode 100644 index 0000000000000..43589ab2ef139 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs @@ -0,0 +1,167 @@ +//! Things related to opaques in the next-trait-solver. + +use intern::Interned; +use rustc_ast_ir::try_visit; + +use crate::next_solver::SolverDefId; + +use super::{CanonicalVarKind, DbInterner, interned_vec_nolifetime_salsa}; + +pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey>; +pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData>; +pub type ExternalConstraintsData<'db> = + rustc_type_ir::solve::ExternalConstraintsData>; + +#[salsa::interned(constructor = new_, debug)] +pub struct PredefinedOpaques<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::PredefinedOpaquesData>, +} + +impl<'db> PredefinedOpaques<'db> { + pub fn new(interner: DbInterner<'db>, data: PredefinedOpaquesData<'db>) -> Self { + PredefinedOpaques::new_(interner.db(), data) + } + + pub fn inner(&self) -> &PredefinedOpaquesData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for PredefinedOpaques<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.opaque_types.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for PredefinedOpaques<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::>()?, + }, + )) + } + fn fold_with>>(self, folder: &mut F) -> Self { + PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + }, + ) + } +} + +impl<'db> std::ops::Deref for PredefinedOpaques<'db> { + type Target = PredefinedOpaquesData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +interned_vec_nolifetime_salsa!(SolverDefIds, SolverDefId); + +#[salsa::interned(constructor = new_, debug)] +pub struct ExternalConstraints<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::ExternalConstraintsData>, +} + +impl<'db> ExternalConstraints<'db> { + pub fn new(interner: DbInterner<'db>, data: ExternalConstraintsData<'db>) -> Self { + ExternalConstraints::new_(interner.db(), data) + } + + pub fn inner(&self) -> &ExternalConstraintsData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::ops::Deref for ExternalConstraints<'db> { + type Target = ExternalConstraintsData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for ExternalConstraints<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.region_constraints.visit_with(visitor)); + try_visit!(self.opaque_types.visit_with(visitor)); + self.normalization_nested_goals.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for ExternalConstraints<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().try_fold_with(folder)?, + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::>()?, + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .try_fold_with(folder)?, + }, + )) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().fold_with(folder), + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .fold_with(folder), + }, + ) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs new file mode 100644 index 0000000000000..50261bb5e3816 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -0,0 +1,894 @@ +//! Things related to predicates. + +use std::cmp::Ordering; + +use intern::Interned; +use rustc_ast_ir::try_visit; +use rustc_type_ir::{ + self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags, + PredicatePolarity, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, Upcast, UpcastFrom, VisitorResult, WithCachedTypeInfo, + elaborate::Elaboratable, + error::{ExpectedFound, TypeError}, + inherent::{IntoKind, SliceLike}, + relate::Relate, +}; +use smallvec::{SmallVec, smallvec}; + +use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; + +pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>; + +pub type TraitRef<'db> = ty::TraitRef>; +pub type AliasTerm<'db> = ty::AliasTerm>; +pub type ProjectionPredicate<'db> = ty::ProjectionPredicate>; +pub type ExistentialPredicate<'db> = ty::ExistentialPredicate>; +pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef>; +pub type ExistentialProjection<'db> = ty::ExistentialProjection>; +pub type TraitPredicate<'db> = ty::TraitPredicate>; +pub type ClauseKind<'db> = ty::ClauseKind>; +pub type PredicateKind<'db> = ty::PredicateKind>; +pub type NormalizesTo<'db> = ty::NormalizesTo>; +pub type CoercePredicate<'db> = ty::CoercePredicate>; +pub type SubtypePredicate<'db> = ty::SubtypePredicate>; +pub type OutlivesPredicate<'db, T> = ty::OutlivesPredicate, T>; +pub type RegionOutlivesPredicate<'db> = OutlivesPredicate<'db, Region<'db>>; +pub type TypeOutlivesPredicate<'db> = OutlivesPredicate<'db, Ty<'db>>; +pub type PolyTraitPredicate<'db> = Binder<'db, TraitPredicate<'db>>; +pub type PolyRegionOutlivesPredicate<'db> = Binder<'db, RegionOutlivesPredicate<'db>>; +pub type PolyTypeOutlivesPredicate<'db> = Binder<'db, TypeOutlivesPredicate<'db>>; +pub type PolySubtypePredicate<'db> = Binder<'db, SubtypePredicate<'db>>; +pub type PolyCoercePredicate<'db> = Binder<'db, CoercePredicate<'db>>; +pub type PolyProjectionPredicate<'db> = Binder<'db, ProjectionPredicate<'db>>; +pub type PolyTraitRef<'db> = Binder<'db, TraitRef<'db>>; +pub type PolyExistentialTraitRef<'db> = Binder<'db, ExistentialTraitRef<'db>>; +pub type PolyExistentialProjection<'db> = Binder<'db, ExistentialProjection<'db>>; + +/// Compares via an ordering that will not change if modules are reordered or other changes are +/// made to the tree. In particular, this ordering is preserved across incremental compilations. +fn stable_cmp_existential_predicate<'db>( + a: &ExistentialPredicate<'db>, + b: &ExistentialPredicate<'db>, +) -> Ordering { + // FIXME: this is actual unstable - see impl in predicate.rs in `rustc_middle` + match (a, b) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal, + (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (ExistentialPredicate::Projection(_), ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::Projection(_), _) => Ordering::Less, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Greater, + } +} +interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate); + +impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> + for BoundExistentialPredicates<'db> +{ + fn principal_def_id(self) -> Option< as rustc_type_ir::Interner>::DefId> { + self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) + } + + fn principal( + self, + ) -> Option< + rustc_type_ir::Binder, rustc_type_ir::ExistentialTraitRef>>, + > { + self.inner()[0] + .map_bound(|this| match this { + ExistentialPredicate::Trait(tr) => Some(tr), + _ => None, + }) + .transpose() + } + + fn auto_traits( + self, + ) -> impl IntoIterator as rustc_type_ir::Interner>::DefId> { + self.iter().filter_map(|predicate| match predicate.skip_binder() { + ExistentialPredicate::AutoTrait(did) => Some(did), + _ => None, + }) + } + + fn projection_bounds( + self, + ) -> impl IntoIterator< + Item = rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::ExistentialProjection>, + >, + > { + self.iter().filter_map(|predicate| { + predicate + .map_bound(|pred| match pred { + ExistentialPredicate::Projection(projection) => Some(projection), + _ => None, + }) + .transpose() + }) + } +} + +impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPredicates<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let interner = relation.cx(); + + // We need to perform this deduplication as we sometimes generate duplicate projections in `a`. + let mut a_v: Vec<_> = a.into_iter().collect(); + let mut b_v: Vec<_> = b.into_iter().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + a_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + a_v.dedup(); + b_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + b_v.dedup(); + if a_v.len() != b_v.len() { + return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); + } + + let v = std::iter::zip(a_v, b_v).map( + |(ep_a, ep_b): ( + Binder<'_, ty::ExistentialPredicate<_>>, + Binder<'_, ty::ExistentialPredicate<_>>, + )| { + match (ep_a.skip_binder(), ep_b.skip_binder()) { + (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { + Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))) + } + ( + ty::ExistentialPredicate::Projection(a), + ty::ExistentialPredicate::Projection(b), + ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + ( + ty::ExistentialPredicate::AutoTrait(a), + ty::ExistentialPredicate::AutoTrait(b), + ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), + _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))), + } + }, + ); + + CollectAndApply::collect_and_apply(v, |g| { + BoundExistentialPredicates::new_from_iter(interner, g.iter().cloned()) + }) + } +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug(pub(crate) T); + +#[salsa::interned(constructor = new_)] +pub struct Predicate<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>>, +} + +impl<'db> std::fmt::Debug for Predicate<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug + for InternedWrapperNoDebug>>> +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Binder<")?; + match self.0.internee.skip_binder() { + rustc_type_ir::PredicateKind::Clause(clause_kind) => { + write!(f, "{clause_kind:?}") + } + rustc_type_ir::PredicateKind::DynCompatible(trait_def_id) => { + write!(f, "the trait `{trait_def_id:?}` is dyn-compatible") + } + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => { + write!(f, "{subtype_predicate:?}") + } + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => { + write!(f, "{coerce_predicate:?}") + } + rustc_type_ir::PredicateKind::ConstEquate(c1, c2) => { + write!(f, "the constant `{c1:?}` equals `{c2:?}`") + } + rustc_type_ir::PredicateKind::Ambiguous => write!(f, "ambiguous"), + rustc_type_ir::PredicateKind::NormalizesTo(data) => write!(f, "{data:?}"), + rustc_type_ir::PredicateKind::AliasRelate(t1, t2, dir) => { + write!(f, "{t1:?} {dir:?} {t2:?}") + } + }?; + write!(f, ", [{:?}]>", self.0.internee.bound_vars())?; + Ok(()) + } +} + +impl<'db> Predicate<'db> { + pub fn new(interner: DbInterner<'db>, kind: Binder<'db, PredicateKind<'db>>) -> Self { + let flags = FlagComputation::for_predicate(kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo>> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Predicate<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + /// Flips the polarity of a Predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(self) -> Option> { + let kind = self + .kind() + .map_bound(|kind| match kind { + PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity, + })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: polarity.flip(), + }))), + + _ => None, + }) + .transpose()?; + + Some(Predicate::new(DbInterner::conjure(), kind)) + } + + pub fn as_trait_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), + PredicateKind::Clause(ClauseKind::Projection(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) + | PredicateKind::Clause(ClauseKind::WellFormed(..)) + | PredicateKind::DynCompatible(..) + | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) + | PredicateKind::ConstEquate(..) + | PredicateKind::Ambiguous => None, + } + } +} + +// FIXME: should make a "header" in interned_vec + +#[derive(Debug, Clone)] +pub struct InternedClausesWrapper<'db>(SmallVec<[Clause<'db>; 2]>, TypeFlags, DebruijnIndex); + +impl<'db> PartialEq for InternedClausesWrapper<'db> { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl<'db> Eq for InternedClausesWrapper<'db> {} + +impl<'db> std::hash::Hash for InternedClausesWrapper<'db> { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +type InternedClauses<'db> = Interned>; + +#[salsa::interned(constructor = new_)] +pub struct Clauses<'db> { + #[returns(ref)] + inner_: InternedClausesWrapper<'db>, +} + +impl<'db> Clauses<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator>, + ) -> Self { + let clauses: SmallVec<_> = data.into_iter().collect(); + let flags = FlagComputation::>::for_clauses(&clauses); + let wrapper = InternedClausesWrapper(clauses, flags.flags, flags.outer_exclusive_binder); + Clauses::new_(interner.db(), wrapper) + } + + pub fn inner(&self) -> &InternedClausesWrapper<'db> { + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + // SAFETY: The caller already has access to a `Clauses<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::fmt::Debug for Clauses<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().0.fmt(f) + } +} + +impl<'db> rustc_type_ir::inherent::Clauses> for Clauses<'db> {} + +impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { + type Item = Clause<'db>; + + type IntoIter = ; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().0.clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().0.as_slice() + } +} + +impl<'db> IntoIterator for Clauses<'db> { + type Item = Clause<'db>; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } +} + +impl<'db> Default for Clauses<'db> { + fn default() -> Self { + Clauses::new_from_iter(DbInterner::conjure(), []) + } +} + +impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.try_fold_with(folder)?); + } + Ok(Clauses::new_from_iter(folder.cx(), clauses)) + } + + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.fold_with(folder)); + } + Clauses::new_from_iter(folder.cx(), clauses) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for Clauses<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok(Clauses::new_from_iter(folder.cx(), inner)) + } + fn fold_with>>(self, folder: &mut F) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = self.iter().map(|v| v.fold_with(folder)).collect(); + Clauses::new_from_iter(folder.cx(), inner) + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for Clauses<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } +} + +impl<'db> rustc_type_ir::Flags for Clauses<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().1 + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().2 + } +} + +impl<'db> rustc_type_ir::TypeSuperVisitable> for Clauses<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_slice().visit_with(visitor) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] // TODO implement Debug by hand +pub struct Clause<'db>(pub(crate) Predicate<'db>); + +// We could cram the reveal into the clauses like rustc does, probably +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ParamEnv<'db> { + pub(crate) clauses: Clauses<'db>, +} + +impl<'db> ParamEnv<'db> { + pub fn empty() -> Self { + ParamEnv { clauses: Clauses::new_from_iter(DbInterner::conjure(), []) } + } +} + +impl<'db> TypeVisitable> for ParamEnv<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.clauses.visit_with(visitor)); + V::Result::output() + } +} + +impl<'db> TypeFoldable> for ParamEnv<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? }) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ParamEnv { clauses: self.clauses.fold_with(folder) } + } +} + +impl<'db> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'db> { + fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike> { + self.clauses + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamEnvAnd<'db, T> { + pub param_env: ParamEnv<'db>, + pub value: T, +} + +impl<'db, T> ParamEnvAnd<'db, T> { + pub fn into_parts(self) -> (ParamEnv<'db>, T) { + (self.param_env, self.value) + } +} + +impl<'db> TypeVisitable> for Predicate<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate(*self) + } +} + +impl<'db> TypeSuperVisitable> for Predicate<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + (*self).kind().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for Predicate<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_predicate(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self) + } +} + +impl<'db> TypeSuperFoldable> for Predicate<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let new = self.kind().try_fold_with(folder)?; + Ok(Predicate::new(folder.cx(), new)) + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let new = self.kind().fold_with(folder); + Predicate::new(folder.cx(), new) + } +} + +impl<'db> Elaboratable> for Predicate<'db> { + fn predicate(&self) -> as rustc_type_ir::Interner>::Predicate { + *self + } + + fn child(&self, clause: as rustc_type_ir::Interner>::Clause) -> Self { + clause.as_predicate() + } + + fn child_with_derived_cause( + &self, + clause: as rustc_type_ir::Interner>::Clause, + _span: as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate>, + >, + _index: usize, + ) -> Self { + clause.as_predicate() + } +} + +impl<'db> Flags for Predicate<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> IntoKind for Predicate<'db> { + type Kind = Binder<'db, PredicateKind<'db>>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> UpcastFrom, ty::PredicateKind>> for Predicate<'db> { + fn upcast_from(from: ty::PredicateKind>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> + UpcastFrom, ty::Binder, ty::PredicateKind>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::PredicateKind>>, + interner: DbInterner<'db>, + ) -> Self { + Predicate::new(interner, from) + } +} +impl<'db> UpcastFrom, ty::ClauseKind>> for Predicate<'db> { + fn upcast_from(from: ty::ClauseKind>, interner: DbInterner<'db>) -> Self { + Binder::dummy(PredicateKind::Clause(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::ClauseKind>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ClauseKind>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(PredicateKind::Clause).upcast(interner) + } +} +impl<'db> UpcastFrom, Clause<'db>> for Predicate<'db> { + fn upcast_from(from: Clause<'db>, _interner: DbInterner<'db>) -> Self { + from.0 + } +} +impl<'db> UpcastFrom, ty::NormalizesTo>> for Predicate<'db> { + fn upcast_from(from: ty::NormalizesTo>, interner: DbInterner<'db>) -> Self { + PredicateKind::NormalizesTo(from).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::TraitRef>> for Predicate<'db> { + fn upcast_from(from: ty::TraitRef>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::TraitRef>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitRef>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|trait_ref| TraitPredicate { + trait_ref, + polarity: PredicatePolarity::Positive, + }) + .upcast(interner) + } +} +impl<'db> UpcastFrom, Binder<'db, ty::TraitPredicate>>> + for Predicate<'db> +{ + fn upcast_from( + from: Binder<'db, ty::TraitPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Trait(it))).upcast(interner) + } +} +impl<'db> UpcastFrom, Binder<'db, ProjectionPredicate<'db>>> for Predicate<'db> { + fn upcast_from(from: Binder<'db, ProjectionPredicate<'db>>, interner: DbInterner<'db>) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Projection(it))).upcast(interner) + } +} +impl<'db> UpcastFrom, ProjectionPredicate<'db>> for Predicate<'db> { + fn upcast_from(from: ProjectionPredicate<'db>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Projection(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::TraitPredicate>> for Predicate<'db> { + fn upcast_from(from: ty::TraitPredicate>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Trait(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::OutlivesPredicate, Ty<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Ty<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::TypeOutlives(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::OutlivesPredicate, Region<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Region<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::RegionOutlives(from)).upcast(interner) + } +} + +impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> { + fn as_clause(self) -> Option< as rustc_type_ir::Interner>::Clause> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Some(self.expect_clause()), + _ => None, + } + } + + /// Whether this projection can be soundly normalized. + /// + /// Wf predicates must not be normalized, as normalization + /// can remove required bounds which would cause us to + /// unsoundly accept some programs. See #91068. + fn allow_normalization(self) -> bool { + // TODO: this should probably live in rustc_type_ir + match self.inner().as_ref().skip_binder() { + PredicateKind::Clause(ClauseKind::WellFormed(_)) + | PredicateKind::AliasRelate(..) + | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::Trait(_)) + | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) + | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) + | PredicateKind::Clause(ClauseKind::Projection(_)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::DynCompatible(_) + | PredicateKind::Subtype(_) + | PredicateKind::Coerce(_) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) + | PredicateKind::ConstEquate(_, _) + | PredicateKind::Ambiguous => true, + } + } +} + +impl<'db> Predicate<'db> { + /// Assert that the predicate is a clause. + pub fn expect_clause(self) -> Clause<'db> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Clause(self), + _ => panic!("{self:?} is not a clause"), + } + } +} + +impl<'db> TypeVisitable> for Clause<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate((*self).as_predicate()) + } +} + +impl<'db> TypeFoldable> for Clause<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause()) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self.as_predicate()).expect_clause() + } +} + +impl<'db> IntoKind for Clause<'db> { + type Kind = Binder<'db, ClauseKind<'db>>; + + fn kind(self) -> Self::Kind { + self.0.kind().map_bound(|pk| match pk { + PredicateKind::Clause(kind) => kind, + _ => unreachable!(), + }) + } +} + +impl<'db> Clause<'db> { + pub fn as_predicate(self) -> Predicate<'db> { + self.0 + } +} + +impl<'db> Elaboratable> for Clause<'db> { + fn predicate(&self) -> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn child(&self, clause: as rustc_type_ir::Interner>::Clause) -> Self { + clause + } + + fn child_with_derived_cause( + &self, + clause: as rustc_type_ir::Interner>::Clause, + _span: as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate>, + >, + _index: usize, + ) -> Self { + clause + } +} + +impl<'db> UpcastFrom, ty::Binder, ty::ClauseKind>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ClauseKind>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.map_bound(PredicateKind::Clause).upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::TraitRef>> for Clause<'db> { + fn upcast_from(from: ty::TraitRef>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::TraitRef>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitRef>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::TraitPredicate>> for Clause<'db> { + fn upcast_from(from: ty::TraitPredicate>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom, ty::Binder, ty::TraitPredicate>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::ProjectionPredicate>> for Clause<'db> { + fn upcast_from( + from: ty::ProjectionPredicate>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom< + DbInterner<'db>, + ty::Binder, ty::ProjectionPredicate>>, + > for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ProjectionPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} + +impl<'db> rustc_type_ir::inherent::Clause> for Clause<'db> { + fn as_predicate(self) -> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn instantiate_supertrait( + self, + cx: DbInterner<'db>, + trait_ref: rustc_type_ir::Binder, rustc_type_ir::TraitRef>>, + ) -> Self { + tracing::debug!(?self, ?trait_ref); + // See the rustc impl for a long comment + let bound_pred = self.kind(); + let pred_bound_vars = bound_pred.bound_vars(); + let trait_bound_vars = trait_ref.bound_vars(); + // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1> + let shifted_pred = + cx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); + // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> + let new = EarlyBinder::bind(shifted_pred).instantiate(cx, trait_ref.skip_binder().args); + // 3) ['x] + ['b] -> ['x, 'b] + let bound_vars = + BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); + + let predicate: Predicate<'db> = + ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx); + predicate.expect_clause() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs new file mode 100644 index 0000000000000..e57808728ae96 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs @@ -0,0 +1,3 @@ +//! Projection code for next-solver. + +pub(crate) mod solve_normalize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs new file mode 100644 index 0000000000000..a8fb2ae14f1c3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs @@ -0,0 +1,264 @@ +//! Normalization within a next-solver infer context. + +use std::fmt::Debug; + +use rustc_next_trait_solver::placeholder::BoundVarReplacer; +use rustc_type_ir::{ + AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, Span as _, Term as _}, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, + TyKind, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{ + InferCtxt, + at::At, + traits::{Obligation, ObligationCause}, + }, + util::PlaceholderReplacer, +}; + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> +where + T: TypeFoldable>, +{ + assert!(!value.has_escaping_bound_vars()); + deeply_normalize_with_skipped_universes(at, value, vec![]) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// 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. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +pub fn deeply_normalize_with_skipped_universes<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec>, +) -> Result>> +where + T: TypeFoldable>, +{ + let (value, coroutine_goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + )?; + assert_eq!(coroutine_goals, vec![]); + + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// 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. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations involving coroutines if the typing mode of +/// the underlying infcx has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec>, +) -> Result<(T, Vec>>), Vec>> +where + T: TypeFoldable>, +{ + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) } +} + +struct NormalizationFolder<'me, 'db> { + at: At<'me, 'db>, + fulfill_cx: FulfillmentCtxt<'db>, + depth: usize, + universes: Vec>, +} + +impl<'db> NormalizationFolder<'_, 'db> { + fn normalize_alias_term( + &mut self, + alias_term: Term<'db>, + ) -> Result, Vec>> { + let infcx = self.at.infcx; + let tcx = infcx.interner; + let recursion_limit = tcx.recursion_limit(); + if self.depth > recursion_limit { + return Err(vec![]); + } + + self.depth += 1; + + let infer_term = infcx.next_term_var_of_kind(alias_term); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), + ); + + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + self.select_all_and_stall_coroutine_predicates()?; + + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + let term = infcx.resolve_vars_if_possible(infer_term); + // super-folding the `term` will directly fold the `Ty` or `Const` so + // we have to match on the term and super-fold them manually. + let result = match term.kind() { + TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), + TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), + }; + self.depth -= 1; + Ok(result) + } + + fn select_all_and_stall_coroutine_predicates( + &mut self, + ) -> Result<(), Vec>> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } +} + +impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { + type Error = Vec>; + + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn try_fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Result, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ty, infcx.shallow_resolve(ty)); + if !ty.has_aliases() { + return Ok(ty); + } + + let TyKind::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) }; + + if ty.has_escaping_bound_vars() { + let (ty, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); + let result = self.normalize_alias_term(ty.into())?.expect_type(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ty.into())?.expect_type()) + } + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); + if !ct.has_aliases() { + return Ok(ct); + } + + let ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) }; + + if ct.has_escaping_bound_vars() { + let (ct, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct); + let result = self.normalize_alias_term(ct.into())?.expect_const(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ct.into())?.expect_const()) + } + } +} + +// Deeply normalize a value and return it +pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable>>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + t: T, +) -> T { + t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { + at: infcx.at(&ObligationCause::dummy(), param_env), + }) +} + +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'db> { + at: At<'a, 'db>, +} + +impl<'db> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs new file mode 100644 index 0000000000000..c59cdac5f99f6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -0,0 +1,332 @@ +//! Things related to regions. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, + inherent::{IntoKind, PlaceholderLike, SliceLike}, + relate::Relate, +}; + +use crate::next_solver::{GenericArg, OutlivesPredicate}; + +use super::{ + ErrorGuaranteed, SolverDefId, interned_vec_db, + interner::{BoundVarKind, DbInterner, Placeholder}, +}; + +type RegionKind<'db> = rustc_type_ir::RegionKind>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Region<'db> { + #[returns(ref)] + kind_: RegionKind<'db>, +} + +impl<'db> Region<'db> { + pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { + Region::new_(interner.db(), kind) + } + + pub fn inner(&self) -> &RegionKind<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) } + }) + .unwrap() + } + + pub fn new_early_param( + interner: DbInterner<'db>, + early_bound_region: EarlyParamRegion, + ) -> Self { + Region::new(interner, RegionKind::ReEarlyParam(early_bound_region)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self { + Region::new(interner, RegionKind::RePlaceholder(placeholder)) + } + + pub fn new_var(interner: DbInterner<'db>, v: RegionVid) -> Region<'db> { + Region::new(interner, RegionKind::ReVar(v)) + } + + pub fn is_placeholder(&self) -> bool { + matches!(self.inner(), RegionKind::RePlaceholder(..)) + } + + pub fn is_static(&self) -> bool { + matches!(self.inner(), RegionKind::ReStatic) + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) + } + + pub fn type_flags(&self) -> TypeFlags { + let mut flags = TypeFlags::empty(); + + match &self.inner() { + RegionKind::ReVar(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_INFER; + } + RegionKind::RePlaceholder(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PLACEHOLDER; + } + RegionKind::ReEarlyParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PARAM; + } + RegionKind::ReLateParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + } + RegionKind::ReStatic => { + flags |= TypeFlags::HAS_FREE_REGIONS; + } + RegionKind::ReBound(..) => { + flags |= TypeFlags::HAS_RE_BOUND; + } + RegionKind::ReErased => { + flags |= TypeFlags::HAS_RE_ERASED; + } + RegionKind::ReError(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_ERROR; + } + } + + flags + } +} + +pub type PlaceholderRegion = Placeholder; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct EarlyParamRegion { + pub index: u32, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +/// The parameter representation of late-bound function parameters, "some region +/// at least as big as the scope `fr.scope`". +/// +/// Similar to a placeholder region as we create `LateParam` regions when entering a binder +/// except they are always in the root universe and instead of using a boundvar to distinguish +/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field +/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling +/// different parameters apart. +pub struct LateParamRegion { + pub scope: SolverDefId, + pub bound_region: BoundRegionKind, +} + +impl std::fmt::Debug for LateParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum BoundRegionKind { + /// An anonymous region parameter for a given fn (&T) + Anon, + + /// Named region parameters for functions (a in &'a T) + /// + /// The `DefId` is needed to distinguish free regions in + /// the event of shadowing. + Named(SolverDefId), + + /// Anonymous region for the implicit env pointer parameter + /// to a closure + ClosureEnv, +} + +impl std::fmt::Debug for BoundRegionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + BoundRegionKind::Anon => write!(f, "BrAnon"), + BoundRegionKind::Named(did) => { + write!(f, "BrNamed({did:?})") + } + BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundRegion { + pub var: BoundVar, + pub kind: BoundRegionKind, +} + +impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { + fn index(self) -> u32 { + self.index + } +} + +impl std::fmt::Debug for EarlyParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + // write!(f, "{}/#{}", self.name, self.index) + } +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_region()) + } +} + +impl core::fmt::Debug for BoundRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + BoundRegionKind::Anon => write!(f, "{:?}", self.var), + BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), + BoundRegionKind::Named(def) => { + write!(f, "{:?}.Named({:?})", self.var, def) + } + } + } +} + +impl BoundRegionKind { + pub fn is_named(&self) -> bool { + matches!(self, BoundRegionKind::Named(_)) + } + + pub fn get_name(&self) -> Option { + None + } + + pub fn get_id(&self) -> Option { + match self { + BoundRegionKind::Named(id) => Some(*id), + _ => None, + } + } +} + +impl<'db> IntoKind for Region<'db> { + type Kind = RegionKind<'db>; + + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> TypeVisitable> for Region<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_region(*self) + } +} + +impl<'db> TypeFoldable> for Region<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_region(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_region(self) + } +} + +impl<'db> Relate> for Region<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.regions(a, b) + } +} + +impl<'db> Flags for Region<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.type_flags() + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match &self.inner() { + RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1), + _ => INNERMOST, + } + } +} + +impl<'db> rustc_type_ir::inherent::Region> for Region<'db> { + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundRegion, + ) -> Self { + Region::new(interner, RegionKind::ReBound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Region::new( + interner, + RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }), + ) + } + + fn new_static(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReStatic) + } + + fn new_placeholder( + interner: DbInterner<'db>, + var: as rustc_type_ir::Interner>::PlaceholderRegion, + ) -> Self { + Region::new(interner, RegionKind::RePlaceholder(var)) + } +} + +impl<'db> PlaceholderLike> for PlaceholderRegion { + type Bound = BoundRegion; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } + } +} + +type GenericArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>; + +interned_vec_db!(RegionAssumptions, GenericArgOutlivesPredicate); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs new file mode 100644 index 0000000000000..45888ac4d2352 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -0,0 +1,289 @@ +//! Defining `SolverContext` for next-trait-solver. + +use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::{ + InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, + lang_items::TraitSolverLangItem, + solve::{Certainty, NoSolution}, +}; + +use crate::{ + TraitRefExt, + db::HirDatabase, + next_solver::{ + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, + mapping::{ChalkToNextSolver, convert_args_for_result}, + util::sizedness_fast_path, + }, +}; + +use super::{ + Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst, + infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, +}; + +pub type Goal<'db, P> = rustc_type_ir::solve::Goal, P>; + +#[repr(transparent)] +pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>); + +impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> { + fn from(infcx: &'a InferCtxt<'db>) -> Self { + // SAFETY: `repr(transparent)` + unsafe { std::mem::transmute(infcx) } + } +} + +impl<'db> std::ops::Deref for SolverContext<'db> { + type Target = InferCtxt<'db>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'db> SolverDelegate for SolverContext<'db> { + type Interner = DbInterner<'db>; + type Infcx = InferCtxt<'db>; + + fn cx(&self) -> Self::Interner { + self.0.interner + } + + fn build_with_canonical( + cx: Self::Interner, + canonical: &rustc_type_ir::CanonicalQueryInput, + ) -> (Self, V, rustc_type_ir::CanonicalVarValues) + where + V: rustc_type_ir::TypeFoldable, + { + let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical); + (SolverContext(infcx), value, vars) + } + + fn fresh_var_for_kind_with_span( + &self, + arg: ::GenericArg, + span: ::Span, + ) -> ::GenericArg { + unimplemented!() + } + + fn leak_check( + &self, + max_input_universe: rustc_type_ir::UniverseIndex, + ) -> Result<(), NoSolution> { + Ok(()) + } + + fn well_formed_goals( + &self, + param_env: ::ParamEnv, + arg: ::Term, + ) -> Option< + Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + >, + > { + unimplemented!() + } + + fn make_deduplicated_outlives_constraints( + &self, + ) -> Vec< + rustc_type_ir::OutlivesPredicate< + Self::Interner, + ::GenericArg, + >, + > { + // FIXME: add if we care about regions + vec![] + } + + fn instantiate_canonical( + &self, + canonical: rustc_type_ir::Canonical, + values: rustc_type_ir::CanonicalVarValues, + ) -> V + where + V: rustc_type_ir::TypeFoldable, + { + canonical.instantiate(self.cx(), &values) + } + + fn instantiate_canonical_var_with_infer( + &self, + cv_info: rustc_type_ir::CanonicalVarKind, + _span: ::Span, + universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, + ) -> ::GenericArg { + self.0.instantiate_canonical_var(cv_info, universe_map) + } + + fn add_item_bounds_for_hidden_type( + &self, + def_id: ::DefId, + args: ::GenericArgs, + param_env: ::ParamEnv, + hidden_ty: ::Ty, + goals: &mut Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + >, + ) { + unimplemented!() + } + + fn fetch_eligible_assoc_item( + &self, + goal_trait_ref: rustc_type_ir::TraitRef, + trait_assoc_def_id: ::DefId, + impl_def_id: ::DefId, + ) -> Result::DefId>, ErrorGuaranteed> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_assoc_id = match trait_assoc_def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_ref = self + .0 + .interner + .db() + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach solver + .expect("invalid impl passed to next-solver") + .into_value_and_skipped_binders() + .0; + let trait_ = trait_ref.hir_trait_id(); + let trait_data = trait_.trait_items(self.0.interner.db()); + let id = + impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { + match item { + (_, AssocItemId::TypeAliasId(type_alias)) => { + let name = &self.0.interner.db().type_alias_signature(*type_alias).name; + let found_trait_assoc_id = trait_data.associated_type_by_name(name)?; + (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias) + } + _ => None, + } + }); + Ok(id.map(SolverDefId::TypeAliasId)) + } + + fn is_transmutable( + &self, + dst: ::Ty, + src: ::Ty, + assume: ::Const, + ) -> Result { + unimplemented!() + } + + fn evaluate_const( + &self, + param_env: ::ParamEnv, + uv: rustc_type_ir::UnevaluatedConst, + ) -> Option<::Const> { + let c = match uv.def { + SolverDefId::ConstId(c) => GeneralConstId::ConstId(c), + SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), + _ => unreachable!(), + }; + let subst = convert_args_for_result(self.interner, uv.args.as_slice()); + let ec = self.cx().db.const_eval(c, subst, None).ok()?; + Some(ec.to_nextsolver(self.interner)) + } + + fn compute_goal_fast_path( + &self, + goal: rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + span: ::Span, + ) -> Option { + if let Some(trait_pred) = goal.predicate.as_trait_clause() { + if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var() + // We don't do this fast path when opaques are defined since we may + // eventually use opaques to incompletely guide inference via ty var + // self types. + // FIXME: Properly consider opaques here. + && self.inner.borrow_mut().opaque_types().is_empty() + { + return Some(Certainty::AMBIGUOUS); + } + + if trait_pred.polarity() == PredicatePolarity::Positive { + match self.0.cx().as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => { + let predicate = self.resolve_vars_if_possible(goal.predicate); + if sizedness_fast_path(self.cx(), predicate, goal.param_env) { + return Some(Certainty::Yes); + } + } + Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + let self_ty = + self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); + // Unlike `Sized` traits, which always prefer the built-in impl, + // `Copy`/`Clone` may be shadowed by a param-env candidate which + // could force a lifetime error or guide inference. While that's + // not generally desirable, it is observable, so for now let's + // ignore this fast path for types that have regions or infer. + if !self_ty + .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER) + && self_ty.is_trivially_pure_clone_copy() + { + return Some(Certainty::Yes); + } + } + _ => {} + } + } + } + + let pred = goal.predicate.kind(); + match pred.no_bound_vars()? { + PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => Some(Certainty::Yes), + PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => Some(Certainty::Yes), + PredicateKind::Subtype(SubtypePredicate { a, b, .. }) + | PredicateKind::Coerce(CoercePredicate { a, b }) => { + if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { + // FIXME: We also need to register a subtype relation between these vars + // when those are added, and if they aren't in the same sub root then + // we should mark this goal as `has_changed`. + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { + if self.shallow_resolve_const(ct).is_ct_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { + if arg.is_trivially_wf(self.interner) { + Some(Certainty::Yes) + } else if arg.is_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + _ => None, + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs new file mode 100644 index 0000000000000..0c0fe686b77d1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -0,0 +1,943 @@ +//! Things related to tys in the next-trait-solver. + +use intern::{Interned, Symbol, sym}; +use rustc_abi::{Float, Integer, Size}; +use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::{ + BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, + TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, + WithCachedTypeInfo, + inherent::{ + AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, + }, + relate::Relate, + solve::SizedTraitKind, + walk::TypeWalker, +}; +use salsa::plumbing::{AsId, FromId}; +use smallvec::SmallVec; + +use crate::{ + db::HirDatabase, + interner::InternedWrapperNoDebug, + next_solver::util::{CoroutineArgsExt, IntegerTypeExt}, +}; + +use super::{ + BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId, interned_vec_db, + util::{FloatExt, IntegerExt}, +}; + +pub type TyKind<'db> = rustc_type_ir::TyKind>; +pub type FnHeader<'db> = rustc_type_ir::FnHeader>; + +#[salsa::interned(constructor = new_)] +pub struct Ty<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>, +} + +const _: () = { + const fn is_copy() {} + is_copy::>(); +}; + +impl<'db> Ty<'db> { + pub fn new(interner: DbInterner<'db>, kind: TyKind<'db>) -> Self { + let flags = FlagComputation::for_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self { + Ty::new(interner, TyKind::Param(ParamTy { index })) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(placeholder)) + } + + pub fn new_infer(interner: DbInterner<'db>, infer: InferTy) -> Self { + Ty::new(interner, TyKind::Infer(infer)) + } + + pub fn new_int_var(interner: DbInterner<'db>, v: IntVid) -> Self { + Ty::new_infer(interner, InferTy::IntVar(v)) + } + + pub fn new_float_var(interner: DbInterner<'db>, v: FloatVid) -> Self { + Ty::new_infer(interner, InferTy::FloatVar(v)) + } + + pub fn new_int(interner: DbInterner<'db>, i: IntTy) -> Self { + Ty::new(interner, TyKind::Int(i)) + } + + pub fn new_uint(interner: DbInterner<'db>, ui: UintTy) -> Self { + Ty::new(interner, TyKind::Uint(ui)) + } + + pub fn new_float(interner: DbInterner<'db>, f: FloatTy) -> Self { + Ty::new(interner, TyKind::Float(f)) + } + + pub fn new_fresh(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshTy(n)) + } + + pub fn new_fresh_int(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshIntTy(n)) + } + + pub fn new_fresh_float(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshFloatTy(n)) + } + + /// Returns the `Size` for primitive types (bool, uint, int, char, float). + pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { + match self.kind() { + TyKind::Bool => Size::from_bytes(1), + TyKind::Char => Size::from_bytes(4), + TyKind::Int(ity) => Integer::from_int_ty(&interner, ity).size(), + TyKind::Uint(uty) => Integer::from_uint_ty(&interner, uty).size(), + TyKind::Float(fty) => Float::from_float_ty(fty).size(), + _ => panic!("non primitive type"), + } + } + + pub fn int_size_and_signed(self, interner: DbInterner<'db>) -> (Size, bool) { + match self.kind() { + TyKind::Int(ity) => (Integer::from_int_ty(&interner, ity).size(), true), + TyKind::Uint(uty) => (Integer::from_uint_ty(&interner, uty).size(), false), + _ => panic!("non integer discriminant"), + } + } + + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self.into()) + } + + /// Fast path helper for testing if a type is `Sized` or `MetaSized`. + /// + /// Returning true means the type is known to implement the sizedness trait. Returning `false` + /// means nothing -- could be sized, might not be. + /// + /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized` + /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with + /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck. + /// This is why this method doesn't return `Option`. + #[tracing::instrument(skip(tcx), level = "debug")] + pub fn has_trivial_sizedness(self, tcx: DbInterner<'db>, sizedness: SizedTraitKind) -> bool { + match self.kind() { + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) + | TyKind::Uint(_) + | TyKind::Int(_) + | TyKind::Bool + | TyKind::Float(_) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::UnsafeBinder(_) + | TyKind::RawPtr(..) + | TyKind::Char + | TyKind::Ref(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Array(..) + | TyKind::Pat(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Never + | TyKind::Error(_) => true, + + TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness { + SizedTraitKind::Sized => false, + SizedTraitKind::MetaSized => true, + }, + + TyKind::Foreign(..) => match sizedness { + SizedTraitKind::Sized | SizedTraitKind::MetaSized => false, + }, + + TyKind::Tuple(tys) => { + tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)) + } + + TyKind::Adt(def, args) => def + .sizedness_constraint(tcx, sizedness) + .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)), + + TyKind::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => { + false + } + + TyKind::Infer(InferTy::TyVar(_)) => false, + + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("`has_trivial_sizedness` applied to unexpected type: {self:?}") + } + } + } + + /// Fast path helper for primitives which are always `Copy` and which + /// have a side-effect-free `Clone` impl. + /// + /// Returning true means the type is known to be pure and `Copy+Clone`. + /// Returning `false` means nothing -- could be `Copy`, might not be. + /// + /// This is mostly useful for optimizations, as these are the types + /// on which we can replace cloning with dereferencing. + pub fn is_trivially_pure_clone_copy(self) -> bool { + match self.kind() { + TyKind::Bool | TyKind::Char | TyKind::Never => true, + + // These aren't even `Clone` + TyKind::Str | TyKind::Slice(..) | TyKind::Foreign(..) | TyKind::Dynamic(..) => false, + + TyKind::Infer(InferTy::FloatVar(_) | InferTy::IntVar(_)) + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) => true, + + // ZST which can't be named are fine. + TyKind::FnDef(..) => true, + + TyKind::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), + + // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. + TyKind::Tuple(field_tys) => { + field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy) + } + + TyKind::Pat(ty, _) => ty.is_trivially_pure_clone_copy(), + + // Sometimes traits aren't implemented for every ABI or arity, + // because we can't be generic over everything yet. + TyKind::FnPtr(..) => false, + + // Definitely absolutely not copy. + TyKind::Ref(_, _, Mutability::Mut) => false, + + // The standard library has a blanket Copy impl for shared references and raw pointers, + // for all unsized types. + TyKind::Ref(_, _, Mutability::Not) | TyKind::RawPtr(..) => true, + + TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => false, + + // Might be, but not "trivial" so just giving the safe answer. + TyKind::Adt(..) | TyKind::Closure(..) | TyKind::CoroutineClosure(..) => false, + + TyKind::UnsafeBinder(_) => false, + + // Needs normalization or revealing to determine, so no is the safe answer. + TyKind::Alias(..) => false, + + TyKind::Param(..) + | TyKind::Placeholder(..) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Error(..) => false, + } + } + + pub fn is_trivially_wf(self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Str + | TyKind::Never + | TyKind::Param(_) + | TyKind::Placeholder(_) + | TyKind::Bound(..) => true, + + TyKind::Slice(ty) => { + ty.is_trivially_wf(tcx) && ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) + } + TyKind::RawPtr(ty, _) => ty.is_trivially_wf(tcx), + + TyKind::FnPtr(sig_tys, _) => { + sig_tys.skip_binder().inputs_and_output.iter().all(|ty| ty.is_trivially_wf(tcx)) + } + TyKind::Ref(_, ty, _) => ty.is_global() && ty.is_trivially_wf(tcx), + + TyKind::Infer(infer) => match infer { + InferTy::TyVar(_) => false, + InferTy::IntVar(_) | InferTy::FloatVar(_) => true, + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => true, + }, + + TyKind::Adt(_, _) + | TyKind::Tuple(_) + | TyKind::Array(..) + | TyKind::Foreign(_) + | TyKind::Pat(_, _) + | TyKind::FnDef(..) + | TyKind::UnsafeBinder(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Alias(..) + | TyKind::Error(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for Ty<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +impl<'db> IntoKind for Ty<'db> { + type Kind = TyKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable> for Ty<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_ty(*self) + } +} + +impl<'db> TypeSuperVisitable> for Ty<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match (*self).kind() { + TyKind::RawPtr(ty, _mutbl) => ty.visit_with(visitor), + TyKind::Array(typ, sz) => { + try_visit!(typ.visit_with(visitor)); + sz.visit_with(visitor) + } + TyKind::Slice(typ) => typ.visit_with(visitor), + TyKind::Adt(_, args) => args.visit_with(visitor), + TyKind::Dynamic(ref trait_ty, ref reg, _) => { + try_visit!(trait_ty.visit_with(visitor)); + reg.visit_with(visitor) + } + TyKind::Tuple(ts) => ts.visit_with(visitor), + TyKind::FnDef(_, args) => args.visit_with(visitor), + TyKind::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor), + TyKind::UnsafeBinder(f) => f.visit_with(visitor), + TyKind::Ref(r, ty, _) => { + try_visit!(r.visit_with(visitor)); + ty.visit_with(visitor) + } + TyKind::Coroutine(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor), + TyKind::Closure(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor), + TyKind::Alias(_, ref data) => data.visit_with(visitor), + + TyKind::Pat(ty, pat) => { + try_visit!(ty.visit_with(visitor)); + pat.visit_with(visitor) + } + + TyKind::Error(guar) => guar.visit_with(visitor), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Param(..) + | TyKind::Never + | TyKind::Foreign(..) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable> for Ty<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_ty(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_ty(self) + } +} + +impl<'db> TypeSuperFoldable> for Ty<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.try_fold_with(folder)?, mutbl), + TyKind::Array(typ, sz) => { + TyKind::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?) + } + TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.try_fold_with(folder)?, + region.try_fold_with(folder)?, + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.try_fold_with(folder)?), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.try_fold_with(folder)?), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.try_fold_with(folder)?) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.try_fold_with(folder)?), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.try_fold_with(folder)?) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?), + TyKind::Pat(ty, pat) => { + TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?) + } + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return Ok(self), + }; + + Ok(if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) }) + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.fold_with(folder), mutbl), + TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)), + TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.fold_with(folder), + region.fold_with(folder), + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.fold_with(folder)), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.fold_with(folder)), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.fold_with(folder)) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.fold_with(folder)), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.fold_with(folder)) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)), + TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return self, + }; + + if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) } + } +} + +impl<'db> Relate> for Ty<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.tys(a, b) + } +} + +impl<'db> Flags for Ty<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { + fn new_unit(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Tuple(Default::default())) + } + + fn new_bool(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Bool) + } + + fn new_u8(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::U8)) + } + + fn new_usize(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::Usize)) + } + + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferTy) -> Self { + Ty::new(interner, TyKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::TyVid) -> Self { + Ty::new(interner, TyKind::Infer(rustc_type_ir::InferTy::TyVar(var))) + } + + fn new_param(interner: DbInterner<'db>, param: ParamTy) -> Self { + Ty::new(interner, TyKind::Param(param)) + } + + fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(param)) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundTy, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, BoundTy { var, kind: BoundTyKind::Anon })) + } + + fn new_alias( + interner: DbInterner<'db>, + kind: rustc_type_ir::AliasTyKind, + alias_ty: rustc_type_ir::AliasTy>, + ) -> Self { + Ty::new(interner, TyKind::Alias(kind, alias_ty)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Ty::new(interner, TyKind::Error(guar)) + } + + fn new_adt( + interner: DbInterner<'db>, + adt_def: as rustc_type_ir::Interner>::AdtDef, + args: GenericArgs<'db>, + ) -> Self { + Ty::new(interner, TyKind::Adt(adt_def, args)) + } + + fn new_foreign( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + ) -> Self { + Ty::new(interner, TyKind::Foreign(def_id)) + } + + fn new_dynamic( + interner: DbInterner<'db>, + preds: as rustc_type_ir::Interner>::BoundExistentialPredicates, + region: as rustc_type_ir::Interner>::Region, + kind: rustc_type_ir::DynKind, + ) -> Self { + Ty::new(interner, TyKind::Dynamic(preds, region, kind)) + } + + fn new_coroutine( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Coroutine(def_id, args)) + } + + fn new_coroutine_closure( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) + } + + fn new_closure( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Closure(def_id, args)) + } + + fn new_coroutine_witness( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) + } + + fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self { + Ty::new(interner, TyKind::RawPtr(ty, mutbl)) + } + + fn new_ref( + interner: DbInterner<'db>, + region: as rustc_type_ir::Interner>::Region, + ty: Self, + mutbl: rustc_ast_ir::Mutability, + ) -> Self { + Ty::new(interner, TyKind::Ref(region, ty, mutbl)) + } + + fn new_array_with_const_len( + interner: DbInterner<'db>, + ty: Self, + len: as rustc_type_ir::Interner>::Const, + ) -> Self { + Ty::new(interner, TyKind::Array(ty, len)) + } + + fn new_slice(interner: DbInterner<'db>, ty: Self) -> Self { + Ty::new(interner, TyKind::Slice(ty)) + } + + fn new_tup( + interner: DbInterner<'db>, + tys: &[ as rustc_type_ir::Interner>::Ty], + ) -> Self { + Ty::new(interner, TyKind::Tuple(Tys::new_from_iter(interner, tys.iter().cloned()))) + } + + fn new_tup_from_iter(interner: DbInterner<'db>, iter: It) -> T::Output + where + It: Iterator, + T: rustc_type_ir::CollectAndApply, + { + T::collect_and_apply(iter, |ts| Ty::new_tup(interner, ts)) + } + + fn new_fn_def( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::FnDef(def_id, args)) + } + + fn new_fn_ptr( + interner: DbInterner<'db>, + sig: rustc_type_ir::Binder, rustc_type_ir::FnSig>>, + ) -> Self { + let (sig_tys, header) = sig.split(); + Ty::new(interner, TyKind::FnPtr(sig_tys, header)) + } + + fn new_pat( + interner: DbInterner<'db>, + ty: Self, + pat: as rustc_type_ir::Interner>::Pat, + ) -> Self { + Ty::new(interner, TyKind::Pat(ty, pat)) + } + + fn tuple_fields(self) -> as rustc_type_ir::Interner>::Tys { + match self.kind() { + TyKind::Tuple(args) => args, + _ => panic!("tuple_fields called on non-tuple: {self:?}"), + } + } + + fn to_opt_closure_kind(self) -> Option { + match self.kind() { + TyKind::Int(int_ty) => match int_ty { + IntTy::I8 => Some(ClosureKind::Fn), + IntTy::I16 => Some(ClosureKind::FnMut), + IntTy::I32 => Some(ClosureKind::FnOnce), + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + }, + + // "Bound" types appear in canonical queries when the + // closure type is not yet known, and `Placeholder` and `Param` + // may be encountered in generic `AsyncFnKindHelper` goals. + TyKind::Bound(..) | TyKind::Placeholder(_) | TyKind::Param(_) | TyKind::Infer(_) => { + None + } + + TyKind::Error(_) => Some(ClosureKind::Fn), + + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + } + } + + fn from_closure_kind(interner: DbInterner<'db>, kind: rustc_type_ir::ClosureKind) -> Self { + match kind { + ClosureKind::Fn => Ty::new(interner, TyKind::Int(IntTy::I8)), + ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn from_coroutine_closure_kind( + interner: DbInterner<'db>, + kind: rustc_type_ir::ClosureKind, + ) -> Self { + match kind { + ClosureKind::Fn | ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn discriminant_ty( + self, + interner: DbInterner<'db>, + ) -> as rustc_type_ir::Interner>::Ty { + match self.kind() { + TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner), + TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner), + + TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => { + /* + let assoc_items = tcx.associated_item_def_ids( + tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), + ); + TyKind::new_projection_from_args(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) + */ + unimplemented!() + } + + TyKind::Pat(ty, _) => ty.discriminant_ty(interner), + + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Adt(..) + | TyKind::Foreign(_) + | TyKind::Str + | TyKind::Array(..) + | TyKind::Slice(_) + | TyKind::RawPtr(_, _) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::CoroutineWitness(..) + | TyKind::Never + | TyKind::Tuple(_) + | TyKind::Error(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + Ty::new(interner, TyKind::Uint(UintTy::U8)) + } + + TyKind::Bound(..) + | TyKind::Placeholder(_) + | TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!( + "`dself.iter().map(|v| v.try_fold_with(folder)).collect::>()?iscriminant_ty` applied to unexpected type: {self:?}" + ) + } + TyKind::UnsafeBinder(..) => unimplemented!(), + } + } + + fn new_unsafe_binder( + interner: DbInterner<'db>, + ty: rustc_type_ir::Binder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + ) -> Self { + Ty::new(interner, TyKind::UnsafeBinder(ty.into())) + } + + fn has_unsafe_fields(self) -> bool { + false + } +} + +interned_vec_db!(Tys, Ty); + +impl<'db> rustc_type_ir::inherent::Tys> for Tys<'db> { + fn inputs(self) -> as rustc_type_ir::Interner>::FnInputTys { + Tys::new_from_iter( + DbInterner::conjure(), + self.as_slice().split_last().unwrap().1.iter().cloned(), + ) + } + + fn output(self) -> as rustc_type_ir::Interner>::Ty { + *self.as_slice().split_last().unwrap().0 + } +} + +pub type PlaceholderTy = Placeholder; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ParamTy { + pub index: u32, +} + +impl ParamTy { + pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new_param(interner, self.index, sym::MISSING_NAME.clone()) + } +} + +impl std::fmt::Debug for ParamTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundTy { + pub var: BoundVar, + pub kind: BoundTyKind, +} + +impl std::fmt::Debug for BoundTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + BoundTyKind::Anon => write!(f, "{:?}", self.var), + BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BoundTyKind { + Anon, + Param(SolverDefId), +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct ErrorGuaranteed; + +impl<'db> TypeVisitable> for ErrorGuaranteed { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_error(*self) + } +} + +impl<'db> TypeFoldable> for ErrorGuaranteed { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + self + } +} + +impl ParamLike for ParamTy { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> BoundVarLike> for BoundTy { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_ty()) + } +} + +impl<'db> PlaceholderLike> for PlaceholderTy { + type Bound = BoundTy; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs new file mode 100644 index 0000000000000..cedc203f7f5aa --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -0,0 +1,1064 @@ +//! Various utilities for the next-trait-solver. + +use std::iter; +use std::ops::{self, ControlFlow}; + +use base_db::Crate; +use hir_def::lang_item::LangItem; +use hir_def::{BlockId, HasModule, ItemContainerId, Lookup}; +use intern::sym; +use la_arena::Idx; +use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions}; +use rustc_type_ir::data_structures::IndexMap; +use rustc_type_ir::inherent::{ + AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, + Ty as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, + TypeFlags, TypeVisitable, TypeVisitableExt, +}; +use rustc_type_ir::{ + ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind, +}; +use rustc_type_ir::{InferCtxtLike, TypeFoldable}; + +use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + TypingMode, +}; +use crate::{ + db::HirDatabase, + from_foreign_def_id, + method_resolution::{TraitImpls, TyFingerprint}, +}; + +use super::fold::{BoundVarReplacer, FnMutDelegate}; +use super::generics::generics; +use super::{ + AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds, + CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg, + GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId, + Term, TraitPredicate, TraitRef, Ty, TyKind, +}; + +#[derive(Clone, Debug)] +pub struct Discr<'db> { + /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + pub val: u128, + pub ty: Ty<'db>, +} + +impl<'db> Discr<'db> { + /// Adds `1` to the value and wraps around if the maximum for the type is reached. + pub fn wrap_incr(self, interner: DbInterner<'db>) -> Self { + self.checked_add(interner, 1).0 + } + pub fn checked_add(self, interner: DbInterner<'db>, n: u128) -> (Self, bool) { + let (size, signed) = self.ty.int_size_and_signed(interner); + let (val, oflo) = if signed { + let min = size.signed_int_min(); + let max = size.signed_int_max(); + let val = size.sign_extend(self.val); + assert!(n < (i128::MAX as u128)); + let n = n as i128; + let oflo = val > max - n; + let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; + // zero the upper bits + let val = val as u128; + let val = size.truncate(val); + (val, oflo) + } else { + let max = size.unsigned_int_max(); + let val = self.val; + let oflo = val > max - n; + let val = if oflo { n - (max - val) - 1 } else { val + n }; + (val, oflo) + }; + (Self { val, ty: self.ty }, oflo) + } +} + +pub trait IntegerTypeExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db>; + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option>, + ) -> Option>; +} + +impl IntegerTypeExt for IntegerType { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match self { + IntegerType::Pointer(true) => Ty::new(interner, TyKind::Int(IntTy::Isize)), + IntegerType::Pointer(false) => Ty::new(interner, TyKind::Uint(UintTy::Usize)), + IntegerType::Fixed(i, s) => i.to_ty(interner, *s), + } + } + + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db> { + Discr { val: 0, ty: self.to_ty(interner) } + } + + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option>, + ) -> Option> { + if let Some(val) = val { + assert_eq!(self.to_ty(interner), val.ty); + let (new, oflo) = val.checked_add(interner, 1); + if oflo { None } else { Some(new) } + } else { + Some(self.initial_discriminant(interner)) + } + } +} + +pub trait IntegerExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db>; + fn from_int_ty(cx: &C, ity: IntTy) -> Integer; + fn from_uint_ty(cx: &C, ity: UintTy) -> Integer; + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db> { + use Integer::*; + match (*self, signed) { + (I8, false) => Ty::new(interner, TyKind::Uint(UintTy::U8)), + (I16, false) => Ty::new(interner, TyKind::Uint(UintTy::U16)), + (I32, false) => Ty::new(interner, TyKind::Uint(UintTy::U32)), + (I64, false) => Ty::new(interner, TyKind::Uint(UintTy::U64)), + (I128, false) => Ty::new(interner, TyKind::Uint(UintTy::U128)), + (I8, true) => Ty::new(interner, TyKind::Int(IntTy::I8)), + (I16, true) => Ty::new(interner, TyKind::Int(IntTy::I16)), + (I32, true) => Ty::new(interner, TyKind::Int(IntTy::I32)), + (I64, true) => Ty::new(interner, TyKind::Int(IntTy::I64)), + (I128, true) => Ty::new(interner, TyKind::Int(IntTy::I128)), + } + } + + fn from_int_ty(cx: &C, ity: IntTy) -> Integer { + use Integer::*; + match ity { + IntTy::I8 => I8, + IntTy::I16 => I16, + IntTy::I32 => I32, + IntTy::I64 => I64, + IntTy::I128 => I128, + IntTy::Isize => cx.data_layout().ptr_sized_integer(), + } + } + fn from_uint_ty(cx: &C, ity: UintTy) -> Integer { + use Integer::*; + match ity { + UintTy::U8 => I8, + UintTy::U16 => I16, + UintTy::U32 => I32, + UintTy::U64 => I64, + UintTy::U128 => I128, + UintTy::Usize => cx.data_layout().ptr_sized_integer(), + } + } + + /// Finds the appropriate Integer type and signedness for the given + /// signed discriminant range and `#[repr]` attribute. + /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(std::cmp::max(min as u128, max as u128)); + let signed_fit = std::cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&interner, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + panic!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{ty:?}`" + ) + } + return (discr, ity.is_signed()); + } + + let at_least = if repr.c() { + // This is usually I32, however it can be different on some platforms, + // notably hexagon and arm-none/thumb-none + interner.data_layout().c_enum_min_size + } else { + // repr(Rust) enums try to be as small as possible + Integer::I8 + }; + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (std::cmp::max(unsigned_fit, at_least), false) + } else { + (std::cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait FloatExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn from_float_ty(fty: FloatTy) -> Self; +} + +impl FloatExt for Float { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + use Float::*; + match *self { + F16 => Ty::new(interner, TyKind::Float(FloatTy::F16)), + F32 => Ty::new(interner, TyKind::Float(FloatTy::F32)), + F64 => Ty::new(interner, TyKind::Float(FloatTy::F64)), + F128 => Ty::new(interner, TyKind::Float(FloatTy::F128)), + } + } + + fn from_float_ty(fty: FloatTy) -> Self { + use Float::*; + match fty { + FloatTy::F16 => F16, + FloatTy::F32 => F32, + FloatTy::F64 => F64, + FloatTy::F128 => F128, + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl PrimitiveExt for Primitive { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Float(f) => f.to_ty(interner), + Primitive::Pointer(_) => Ty::new( + interner, + TyKind::RawPtr( + Ty::new(interner, TyKind::Tuple(Default::default())), + rustc_ast_ir::Mutability::Mut, + ), + ), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + #[inline] + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Pointer(_) => { + let signed = false; + interner.data_layout().ptr_sized_integer().to_ty(interner, signed) + } + Primitive::Float(_) => panic!("floats do not have an int type"), + } + } +} + +impl<'db> HasDataLayout for DbInterner<'db> { + fn data_layout(&self) -> &rustc_abi::TargetDataLayout { + unimplemented!() + } +} + +pub trait CoroutineArgsExt<'db> { + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl<'db> CoroutineArgsExt<'db> for CoroutineArgs> { + /// The type of the state discriminant used in the coroutine type. + #[inline] + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new(interner, TyKind::Uint(UintTy::U32)) + } +} + +/// Finds the max universe present +pub struct MaxUniverse { + max_universe: UniverseIndex, +} + +impl Default for MaxUniverse { + fn default() -> Self { + Self::new() + } +} + +impl MaxUniverse { + pub fn new() -> Self { + MaxUniverse { max_universe: UniverseIndex::ROOT } + } + + pub fn max_universe(self) -> UniverseIndex { + self.max_universe + } +} + +impl<'db> TypeVisitor> for MaxUniverse { + type Result = (); + + fn visit_ty(&mut self, t: Ty<'db>) { + if let TyKind::Placeholder(placeholder) = t.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: Const<'db>) { + if let ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'db>) { + if let RegionKind::RePlaceholder(placeholder) = r.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + } +} + +pub struct BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + pub interner: DbInterner<'db>, + pub ty_op: F, + pub lt_op: G, + pub ct_op: H, +} + +impl<'db, F, G, H> TypeFolder> for BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let t = ty.super_fold_with(self); + (self.ty_op)(t) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + // This one is a little different, because `super_fold_with` is not + // implemented on non-recursive `Region`. + (self.lt_op)(r) + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = ct.super_fold_with(self); + (self.ct_op)(ct) + } +} + +pub(crate) fn for_trait_impls( + db: &dyn HirDatabase, + krate: Crate, + block: Option, + trait_id: hir_def::TraitId, + self_ty_fp: Option, + mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, +) -> ControlFlow<()> { + // Note: Since we're using `impls_for_trait` and `impl_provided_for`, + // only impls where the trait can be resolved should ever reach Chalk. + // `impl_datum` relies on that and will panic if the trait can't be resolved. + let in_deps = db.trait_impls_in_deps(krate); + let in_self = db.trait_impls_in_crate(krate); + let trait_module = trait_id.module(db); + let type_module = match self_ty_fp { + Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), + Some(TyFingerprint::ForeignType(type_id)) => Some(from_foreign_def_id(type_id).module(db)), + Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)), + _ => None, + }; + + let mut def_blocks = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; + + let block_impls = iter::successors(block, |&block_id| { + cov_mark::hit!(block_local_impls); + block_id.loc(db).module.containing_block() + }) + .inspect(|&block_id| { + // make sure we don't search the same block twice + def_blocks.iter_mut().for_each(|block| { + if *block == Some(block_id) { + *block = None; + } + }); + }) + .filter_map(|block_id| db.trait_impls_in_block(block_id)); + f(&in_self)?; + for it in in_deps.iter().map(ops::Deref::deref) { + f(it)?; + } + for it in block_impls { + f(&it)?; + } + for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) { + f(&it)?; + } + ControlFlow::Continue(()) +} + +// FIXME(next-trait-solver): uplift +pub fn sizedness_constraint_for_ty<'db>( + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ty: Ty<'db>, +) -> Option> { + use rustc_type_ir::TyKind::*; + + match ty.kind() { + // these are always sized + Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) + | FnPtr(..) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) + | CoroutineWitness(..) | Never => None, + + // these are never sized + Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness { + // Never `Sized` + SizedTraitKind::Sized => Some(ty), + // Always `MetaSized` + SizedTraitKind::MetaSized => None, + }, + + // Maybe `Sized` or `MetaSized` + Param(..) | Alias(..) | Error(_) => Some(ty), + + // We cannot instantiate the binder, so just return the *original* type back, + // but only if the inner type has a sized constraint. Thus we skip the binder, + // but don't actually use the result from `sized_constraint_for_ty`. + UnsafeBinder(inner_ty) => { + sizedness_constraint_for_ty(interner, sizedness, inner_ty.skip_binder()).map(|_| ty) + } + + // Never `MetaSized` or `Sized` + Foreign(..) => Some(ty), + + // Recursive cases + Pat(ty, _) => sizedness_constraint_for_ty(interner, sizedness, ty), + + Tuple(tys) => tys + .into_iter() + .last() + .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), + + Adt(adt, args) => { + let tail_ty = + EarlyBinder::bind(adt.all_field_tys(interner).skip_binder().into_iter().last()?) + .instantiate(interner, args); + sizedness_constraint_for_ty(interner, sizedness, tail_ty) + } + + Placeholder(..) | Bound(..) | Infer(..) => { + panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty") + } + } +} + +pub fn apply_args_to_binder<'db, T: TypeFoldable>>( + b: Binder<'db, T>, + args: GenericArgs<'db>, + interner: DbInterner<'db>, +) -> T { + let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); + let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); + let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const(); + let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); + b.skip_binder().fold_with(&mut instantiate) +} + +pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( + mut context: SolverContext<'db>, + val: T, +) -> Canonical, T> { + let mut canon = MiniCanonicalizer { + context: &mut context, + db: DebruijnIndex::ZERO, + vars: IndexMap::default(), + }; + let canon_val = val.fold_with(&mut canon); + let vars = canon.vars; + Canonical { + value: canon_val, + max_universe: UniverseIndex::from_u32(1), + variables: CanonicalVars::new_from_iter( + context.cx(), + vars.iter().map(|(k, v)| match (*k).kind() { + GenericArgKind::Type(ty) => match ty.kind() { + TyKind::Int(..) | TyKind::Uint(..) => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + _ => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO), + ), + }, + GenericArgKind::Lifetime(_) => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) + } + GenericArgKind::Const(_) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO) + } + }), + ), + } +} + +struct MiniCanonicalizer<'a, 'db> { + context: &'a mut SolverContext<'db>, + db: DebruijnIndex, + vars: IndexMap, usize>, +} + +impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.context.cx() + } + + fn fold_binder>>( + &mut self, + t: rustc_type_ir::Binder, T>, + ) -> rustc_type_ir::Binder, T> { + self.db.shift_in(1); + let res = t.map_bound(|t| t.fold_with(self)); + self.db.shift_out(1); + res + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + t + } + rustc_type_ir::TyKind::Infer(infer) => { + let t = match infer { + rustc_type_ir::InferTy::TyVar(vid) => { + self.context.opportunistic_resolve_ty_var(vid) + } + rustc_type_ir::InferTy::IntVar(vid) => { + self.context.opportunistic_resolve_int_var(vid) + } + rustc_type_ir::InferTy::FloatVar(vid) => { + self.context.opportunistic_resolve_float_var(vid) + } + _ => t, + }; + let len = self.vars.len(); + let var = *self.vars.entry(t.into()).or_insert(len); + Ty::new( + self.cx(), + TyKind::Bound( + self.db, + BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) }, + ), + ) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region( + &mut self, + r: as rustc_type_ir::Interner>::Region, + ) -> as rustc_type_ir::Interner>::Region { + match r.kind() { + RegionKind::ReBound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + r + } + RegionKind::ReVar(vid) => { + let len = self.vars.len(); + let var = *self.vars.entry(r.into()).or_insert(len); + Region::new( + self.cx(), + RegionKind::ReBound( + self.db, + BoundRegion { + kind: super::BoundRegionKind::Anon, + var: BoundVar::from_usize(var), + }, + ), + ) + } + _ => r, + } + } + + fn fold_const( + &mut self, + c: as rustc_type_ir::Interner>::Const, + ) -> as rustc_type_ir::Interner>::Const { + match c.kind() { + ConstKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + c + } + ConstKind::Infer(infer) => { + let len = self.vars.len(); + let var = *self.vars.entry(c.into()).or_insert(len); + Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var))) + } + _ => c.super_fold_with(self), + } + } +} + +pub fn explicit_item_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, Clauses<'db>> { + let db = interner.db(); + match def_id { + SolverDefId::TypeAliasId(type_alias) => { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + // Lower bounds -- we could/should maybe move this to a separate query in `lower` + let type_alias_data = db.type_alias_signature(type_alias); + let generic_params = generics(db, type_alias.into()); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + + let trait_args = GenericArgs::identity_for_item(interner, trait_.into()); + let item_args = GenericArgs::identity_for_item(interner, def_id); + let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { + bounds.push(pred); + }); + } + + if !ctx.unsized_types.contains(&interner_ty) { + let sized_trait = LangItem::Sized + .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate")); + let sized_bound = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [interner_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + bounds.extend(sized_bound); + bounds.shrink_to_fit(); + } + + rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds)) + } + SolverDefId::InternedOpaqueTyId(id) => { + let full_id = db.lookup_intern_impl_trait_id(id); + match full_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let datas = db + .return_type_impl_traits_ns(func) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { + let datas = db + .type_alias_impl_traits_ns(alias) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { + if let Some((future_trait, future_output)) = LangItem::Future + .resolve_trait(db, interner.krate.expect("Must have interner.krate")) + .and_then(|trait_| { + let alias = trait_.trait_items(db).associated_type_by_name( + &hir_expand::name::Name::new_symbol_root(sym::Output.clone()), + )?; + Some((trait_, alias)) + }) + { + let args = GenericArgs::identity_for_item(interner, def_id); + let out = args.as_slice()[0]; + let mut predicates = vec![]; + + let item_ty = Ty::new_alias( + interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + future_trait.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + let sized_trait = LangItem::Sized + .resolve_trait(db, interner.krate.expect("Must have interner.krate")); + if let Some(sized_trait_) = sized_trait { + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + sized_trait_.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + } + let kind = + PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate { + projection_term: AliasTerm::new_from_args( + interner, + future_output.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + term: match out.kind() { + GenericArgKind::Lifetime(lt) => panic!(), + GenericArgKind::Type(ty) => Term::Ty(ty), + GenericArgKind::Const(const_) => Term::Const(const_), + }, + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + EarlyBinder::bind(Clauses::new_from_iter(interner, predicates)) + } else { + // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. + EarlyBinder::bind(Clauses::new_from_iter(interner, [])) + } + } + } + } + _ => panic!("Unexpected GeneridDefId"), + } +} + +pub struct ContainsTypeErrors; + +impl<'db> TypeVisitor> for ContainsTypeErrors { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { + match t.kind() { + rustc_type_ir::TyKind::Error(_) => ControlFlow::Break(()), + _ => t.super_visit_with(self), + } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'a, 'db> { + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, BoundTy>, + mapped_consts: FxIndexMap, + universe_indices: &'a [Option], + current_index: DebruijnIndex, +} + +impl<'a, 'db> PlaceholderReplacer<'a, 'db> { + pub fn replace_placeholders>>( + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, BoundTy>, + mapped_consts: FxIndexMap, + universe_indices: &'a [Option], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'db> TypeFolder> for PlaceholderReplacer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: Region<'db>) -> Region<'db> { + let r1 = match r0.kind() { + RegionKind::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.interner, vid), + _ => r0, + }; + + let r2 = match r1.kind() { + RegionKind::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Region::new_bound(self.cx(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + tracing::debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let ty = self.infcx.shallow_resolve(ty); + match ty.kind() { + TyKind::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = self.infcx.shallow_resolve_const(ct); + if let ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Const::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} + +pub(crate) fn needs_normalization<'db, T: TypeVisitable>>( + infcx: &InferCtxt<'db>, + value: &T, +) -> bool { + let mut flags = TypeFlags::HAS_ALIAS; + + // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, + // so we can ignore those. + match infcx.typing_mode() { + // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE), + TypingMode::PostAnalysis => {} + } + + value.has_type_flags(flags) +} + +pub fn sizedness_fast_path<'db>( + tcx: DbInterner<'db>, + predicate: Predicate<'db>, + param_env: ParamEnv<'db>, +) -> bool { + // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like + // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to + // canonicalize and all that for such cases. + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder() + && trait_pred.polarity == PredicatePolarity::Positive + { + let sizedness = match tcx.as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized, + Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized, + _ => return false, + }; + + // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature + // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` + // is pending a proper fix + if matches!(sizedness, SizedTraitKind::MetaSized) { + return true; + } + + if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) { + tracing::debug!("fast path -- trivial sizedness"); + return true; + } + + if matches!(trait_pred.self_ty().kind(), TyKind::Param(_) | TyKind::Placeholder(_)) { + for clause in param_env.caller_bounds().iter() { + if let ClauseKind::Trait(clause_pred) = clause.kind().skip_binder() + && clause_pred.polarity == PredicatePolarity::Positive + && clause_pred.self_ty() == trait_pred.self_ty() + && (clause_pred.def_id() == trait_pred.def_id() + || (sizedness == SizedTraitKind::MetaSized + && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized))) + { + return true; + } + } + } + } + + false +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 9605a0b4124d8..fc31973022bdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -12,9 +12,6 @@ mod simple; mod traits; mod type_alias_impl_traits; -use std::env; -use std::sync::LazyLock; - use base_db::{Crate, SourceDatabase}; use expect_test::Expect; use hir_def::{ @@ -35,8 +32,6 @@ use syntax::{ ast::{self, AstNode, HasName}, }; use test_fixture::WithFixture; -use tracing_subscriber::{Registry, layer::SubscriberExt}; -use tracing_tree::HierarchicalLayer; use triomphe::Arc; use crate::{ @@ -44,6 +39,7 @@ use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::{Adjustment, TypeMismatch}, + setup_tracing, test_db::TestDB, }; @@ -51,23 +47,6 @@ use crate::{ // against snapshots of the expected results using expect. Use // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. -fn setup_tracing() -> Option { - static ENABLE: LazyLock = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); - if !*ENABLE { - return None; - } - - let filter: tracing_subscriber::filter::Targets = - env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); - let layer = HierarchicalLayer::default() - .with_indent_lines(true) - .with_ansi(false) - .with_indent_amount(2) - .with_writer(std::io::stderr); - let subscriber = Registry::default().with(filter).with(layer); - Some(tracing::subscriber::set_default(subscriber)) -} - #[track_caller] fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) { check_impl(ra_fixture, false, true, false) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index dbc68eeba1e64..5893894c33ab7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -12,9 +12,10 @@ use crate::display::{DisplayTarget, HirDisplay}; use crate::mir::MirSpan; use crate::test_db::TestDB; -use super::visit_module; +use super::{setup_tracing, visit_module}; fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(ra_fixture); let module = db.module_for_file(file_id.file_id(&db)); let def_map = module.def_map(&db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 3894b4b6f7bad..3b7d4d2184a59 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -96,7 +96,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test() { let x = if true { foo(&[1]) - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) } else { &[1] }; @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; @@ -484,6 +484,8 @@ fn test() { ); } +// FIXME(next-solver): We could learn more from the `&S` -> `&dyn Foo` coercion if we followed the rustc model +// where unsized is successful if all unsizing trait goals are certain (and non-unsizing goals are delayed). #[test] fn coerce_unsize_trait_object_simple() { check_types( @@ -503,8 +505,8 @@ fn test() { //^ S let obj: &dyn Bar<_, i8, i16> = &S; //^ S - let obj: &dyn Foo = &S; - //^ S + //let obj: &dyn Foo = &S; + // S<{unknown}, {unknown}> }"#, ); } @@ -543,9 +545,9 @@ struct Bar(Foo); fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^ type: &'? Foo<[usize; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^^^^^^ type: &'? Bar<[usize; 3]> } "#, ); @@ -899,7 +901,7 @@ impl core::ops::Index for StructMut { fn index(&self, index: usize) -> &Self::Output { &() } } -impl core::ops::IndexMut for StructMut { +impl core::ops::IndexMut for StructMut { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () } } fn test() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 3159499e86707..df9061d23bf5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -519,6 +519,7 @@ impl SomeStruct { ); } +// FIXME(next-solver): does this test make sense with fast path? #[test] fn add_struct_invalidates_trait_solve() { let (mut db, file_id) = TestDB::with_single_file( @@ -559,7 +560,7 @@ fn main() { let _inference_result = db.infer(def); } }, - &[("trait_solve_shim", 2)], + &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ "source_root_crates_shim", @@ -606,21 +607,17 @@ fn main() { "callable_item_signature_shim", "adt_variance_shim", "variances_of_shim", - "trait_solve_shim", - "trait_datum_shim", - "generic_predicates_shim", - "adt_datum_shim", "trait_impls_in_deps_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", "type_for_adt_tracked", - "impl_datum_shim", - "generic_predicates_shim", - "program_clauses_for_chalk_env_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", - "trait_solve_shim", "lang_item", ] "#]], @@ -703,10 +700,13 @@ fn main() { "impl_signature_with_source_map_shim", "impl_signature_shim", "callable_item_signature_shim", - "generic_predicates_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "generic_predicates_shim", ] "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index ea7a113cae3f6..5d088e40cdeda 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -197,17 +197,17 @@ fn expr_macro_def_expanded_in_various_places() { 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': IntoIterator::IntoIter - 100..119 'for _ ...!() {}': &'? mut IntoIterator::IntoIter - 100..119 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 100..119 'for _ ...!() {}': Option> + 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': &'? mut {unknown} + 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 100..119 'for _ ...!() {}': Option<{unknown}> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () - 104..105 '_': IntoIterator::Item + 104..105 '_': {unknown} 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -291,17 +291,17 @@ fn expr_macro_rules_expanded_in_various_places() { 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': IntoIterator::IntoIter - 114..133 'for _ ...!() {}': &'? mut IntoIterator::IntoIter - 114..133 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 114..133 'for _ ...!() {}': Option> + 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': &'? mut {unknown} + 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 114..133 'for _ ...!() {}': Option<{unknown}> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': IntoIterator::Item + 118..119 '_': {unknown} 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index c58ca6c67a8de..f09b3ef6bfd0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1134,6 +1134,7 @@ fn test() { (S {}).method(); } fn dyn_trait_super_trait_not_in_scope() { check_infer( r#" + //- minicore: dispatch_from_dyn mod m { pub trait SuperTrait { fn foo(&self) -> u32 { 0 } @@ -1309,7 +1310,7 @@ fn main() { fn dyn_trait_method_priority() { check_types( r#" -//- minicore: from +//- minicore: from, dispatch_from_dyn trait Trait { fn into(&self) -> usize { 0 } } @@ -1823,6 +1824,33 @@ fn test() { ); } +#[test] +fn deref_fun_3() { + check_types( + r#" +//- minicore: receiver + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), make()); + let _: usize = (*a1).0; + a1; + //^^ A, u32> +} +"#, + ); +} + #[test] fn deref_into_inference_var() { check_types( @@ -2077,7 +2105,7 @@ impl Foo { } fn test() { Box::new(Foo).foo(); - //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)) + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not)) } "#, ); @@ -2095,7 +2123,7 @@ impl Foo { use core::mem::ManuallyDrop; fn test() { ManuallyDrop::new(Foo).foo(); - //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?4, Not)) + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not)) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c4c17a93c9cd6..25061e1dbdb9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -773,7 +773,7 @@ fn issue_4800() { "#, expect![[r#" 379..383 'self': &'? mut PeerSet - 401..424 '{ ... }': dyn Future + '? + 401..424 '{ ... }': dyn Future + 'static 411..418 'loop {}': ! 416..418 '{}': () 575..579 'self': &'? mut Self @@ -781,6 +781,9 @@ fn issue_4800() { ); } +// FIXME(next-solver): Though `Repeat: IntoIterator` does not hold here, we +// should be able to do better at given type hints (with Chalk, we did `IntoIterator::Item>`) +// From what I can tell, the point of this test is to not panic though. #[test] fn issue_4966() { check_infer( @@ -824,11 +827,11 @@ fn issue_4966() { 311..317 'repeat': Repeat f64>> 320..345 'Repeat...nner }': Repeat f64>> 338..343 'inner': Map f64> - 356..359 'vec': Vec f64>>>> - 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> - 362..379 'from_i...epeat)': Vec f64>>>> + 356..359 'vec': Vec<{unknown}> + 362..371 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> + 362..379 'from_i...epeat)': Vec<{unknown}> 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec f64>>>> + 386..389 'vec': Vec<{unknown}> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -1224,6 +1227,8 @@ fn mamba(a: U32!(), p: u32) -> u32 { #[test] fn for_loop_block_expr_iterable() { + // FIXME(next-solver): it would be nice to be able to hint `IntoIterator::IntoIter<()>` instead of just `{unknown}` + // (even though `(): IntoIterator` does not hold) check_infer( r#" //- minicore: iterator @@ -1236,17 +1241,17 @@ fn test() { expect![[r#" 10..68 '{ ... } }': () 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': ! - 16..66 'for _ ... }': IntoIterator::IntoIter<()> - 16..66 'for _ ... }': &'? mut IntoIterator::IntoIter<()> - 16..66 'for _ ... }': fn next>(&'? mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> - 16..66 'for _ ... }': Option> + 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': &'? mut {unknown} + 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 16..66 'for _ ... }': Option<{unknown}> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': IntoIterator::Item<()> + 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 @@ -1283,7 +1288,6 @@ fn test() { #[test] fn bug_11242() { - // FIXME: wrong, should be u32 check_types( r#" fn foo() @@ -1292,7 +1296,7 @@ where B: IntoIterator, { let _x: ::Item; - // ^^ {unknown} + // ^^ u32 } pub trait Iterator { @@ -1495,7 +1499,7 @@ fn regression_11688_2() { fn regression_11688_3() { check_types( r#" - //- minicore: iterator + //- minicore: iterator, dispatch_from_dyn struct Ar(T); fn f( num_zeros: usize, @@ -1514,6 +1518,7 @@ fn regression_11688_3() { fn regression_11688_4() { check_types( r#" + //- minicore: dispatch_from_dyn trait Bar { fn baz(&self) -> [i32; C]; } @@ -2035,11 +2040,11 @@ fn issue_17734() { r#" fn test() { let x = S::foo::<'static, &()>(&S); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S::foo::<&()>(&S); // ^ Wrap<'?, ()> let x = S.foo::<'static, &()>(); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S.foo::<&()>(); // ^ Wrap<'?, ()> } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index b154e59878571..a995a45f6e752 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2923,7 +2923,7 @@ fn test { // ^^ impl Fn() let c4 = f1(); - // ^^ impl FnOnce() + ?Sized + // ^^ impl FnOnce() f2(|| { 0 }); // ^^^^^^^^ impl FnOnce() -> i32 @@ -3922,7 +3922,7 @@ fn foo() { expect![[r#" 110..127 '{ ...z(); }': () 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> - 116..124 'T::baz()': Foo::Gat<'?, {unknown}> + 116..124 'T::baz()': {unknown} "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 56e31a1af1b9c..2e4346a86973b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1691,7 +1691,7 @@ fn test>(x: T, y: impl Trait) { get2(y); get(set(S)); get2(set(S)); - get2(S::); + get2(S::); }"#, expect![[r#" 49..50 't': T @@ -1703,7 +1703,7 @@ fn test>(x: T, y: impl Trait) { 166..167 't': T 256..257 'x': T 262..263 'y': impl Trait - 289..397 '{ ...r>); }': () + 289..399 '{ ...e>); }': () 295..298 'get': fn get(T) -> ::Type 295..301 'get(x)': u32 299..300 'x': T @@ -1726,9 +1726,9 @@ fn test>(x: T, y: impl Trait) { 367..370 'set': fn set>(S) -> S 367..373 'set(S)': S 371..372 'S': S - 380..384 'get2': fn get2>(S) -> str - 380..394 'get2(S::)': str - 385..393 'S::': S + 380..384 'get2': fn get2>(S) -> usize + 380..396 'get2(S...size>)': usize + 385..395 'S::': S "#]], ); } @@ -2740,7 +2740,7 @@ impl> Foo { fn dyn_trait_through_chalk() { check_types( r#" -//- minicore: deref +//- minicore: deref, unsize, dispatch_from_dyn struct Box {} impl core::ops::Deref for Box { type Target = T; @@ -3228,7 +3228,7 @@ fn foo() { fn infer_dyn_fn_output() { check_types( r#" -//- minicore: fn +//- minicore: fn, dispatch_from_dyn fn foo() { let f: &dyn Fn() -> i32; f(); @@ -4255,8 +4255,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': &'? i32 - 170..192 'v.get:...eref()': &'? i32 + 170..184 'v.get::()': {unknown} + 170..192 'v.get:...eref()': &'? {unknown} "#]], ); } @@ -4785,30 +4785,30 @@ fn allowed2<'a>(baz: impl Baz) {} fn allowed3(baz: impl Baz>) {} "#, expect![[r#" - 139..140 'f': impl Fn({unknown}) + ?Sized + 139..140 'f': impl Fn({unknown}) 161..193 '{ ...oo); }': () 171..174 'foo': S 177..178 'S': S - 184..185 'f': impl Fn({unknown}) + ?Sized + 184..185 'f': impl Fn({unknown}) 184..190 'f(foo)': () 186..189 'foo': S - 251..252 'f': impl Fn(&'? {unknown}) + ?Sized + 251..252 'f': impl Fn(&'? {unknown}) 274..307 '{ ...oo); }': () 284..287 'foo': S 290..291 'S': S - 297..298 'f': impl Fn(&'? {unknown}) + ?Sized + 297..298 'f': impl Fn(&'? {unknown}) 297..304 'f(&foo)': () 299..303 '&foo': &'? S 300..303 'foo': S - 325..328 'bar': impl Bar<{unknown}> + ?Sized + 325..328 'bar': impl Bar<{unknown}> 350..352 '{}': () - 405..408 'bar': impl Bar<&'? {unknown}> + ?Sized + 405..408 'bar': impl Bar<&'? {unknown}> 431..433 '{}': () - 447..450 'baz': impl Baz + ?Sized + 447..450 'baz': impl Baz 480..482 '{}': () - 500..503 'baz': impl Baz + ?Sized + 500..503 'baz': impl Baz 544..546 '{}': () - 560..563 'baz': impl Baz> + ?Sized + 560..563 'baz': impl Baz> 598..600 '{}': () "#]], ) @@ -4930,3 +4930,67 @@ fn main() { "#]], ); } + +#[test] +fn new_solver_crash_1() { + check_infer( + r#" +pub trait Deserializer<'de> { + type Error; +} + +fn deserialize_abs_pathbuf<'de, D>(de: D) -> D::Error +where + D: Deserializer<'de>, +{ +} +"#, + expect![[r#" + 84..86 'de': D + 135..138 '{ }': Deserializer::Error<'de, D> + "#]], + ); +} + +#[test] +fn new_solver_crash_2() { + check_infer( + r#" +//- minicore: deref, send, sync +use core::ops::Deref; + +trait Error {} + +struct AnyhowError; + +impl Deref for AnyhowError { + type Target = dyn Error + Send + Sync; + + fn deref(&self) -> &Self::Target { loop {} } +} + +impl AnyhowError { + fn downcast(self) {} +} + + +fn main() { + let e = AnyhowError; + e.downcast::<()>(); +} +"#, + expect![[r#" + 147..151 'self': &'? AnyhowError + 170..181 '{ loop {} }': &'? (dyn Error + Send + Sync + 'static) + 172..179 'loop {}': ! + 177..179 '{}': () + 223..227 'self': AnyhowError + 229..231 '{}': () + 246..298 '{ ...>(); }': () + 256..257 'e': AnyhowError + 260..271 'AnyhowError': AnyhowError + 277..278 'e': AnyhowError + 277..295 'e.down...<()>()': () + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs index f53409af2b30c..fe4cf7a3da527 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs @@ -10,6 +10,7 @@ use crate::{ }; use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; +#[allow(unused)] pub(crate) use unsafe_tls::{set_current_program, with_current_program}; pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); @@ -136,6 +137,7 @@ mod unsafe_tls { if PROGRAM.is_set() { PROGRAM.with(|prog| op(Some(prog))) } else { op(None) } } + #[allow(dead_code)] pub(crate) fn set_current_program(p: &dyn HirDatabase, op: OP) -> R where OP: FnOnce() -> R, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 08b9d242e71d2..51cf5be1372ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,43 +1,38 @@ //! Trait solving using Chalk. use core::fmt; -use std::env::var; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; -use chalk_recursive::Cache; -use chalk_solve::{Solver, logging_db::LoggingRustIrDatabase, rust_ir}; +use chalk_solve::rust_ir; use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; +use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; +use rustc_type_ir::{ + InferCtxtLike, TypingMode, + inherent::{SliceLike, Span as _}, + solve::Certainty, +}; use span::Edition; -use stdx::{never, panic_context}; +use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + DbInterner, GenericArg, SolverContext, Span, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + util::mini_canonicalize, + }, + utils::UnevaluatedConstEvaluatorFolder, }; -/// This controls how much 'time' we give the Chalk solver before giving up. -const CHALK_SOLVER_FUEL: i32 = 1000; - -#[derive(Debug, Copy, Clone)] -pub(crate) struct ChalkContext<'a> { - pub(crate) db: &'a dyn HirDatabase, - pub(crate) krate: Crate, - pub(crate) block: Option, -} - -fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { - let overflow_depth = - var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(500); - let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(150); - chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, Some(Cache::new())) -} - /// A set of clauses that we assume to be true. E.g. if we are inside this function: /// ```rust /// fn foo(t: T) {} @@ -103,13 +98,43 @@ pub(crate) fn normalize_projection_query( table.resolve_completely(ty) } +fn identity_subst( + binders: chalk_ir::CanonicalVarKinds, +) -> chalk_ir::Canonical> { + let identity_subst = chalk_ir::Substitution::from_iter( + Interner, + binders.iter(Interner).enumerate().map(|(index, c)| { + let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index); + match &c.kind { + chalk_ir::VariableKind::Ty(_) => { + chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner)) + .intern(Interner) + } + chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime( + chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner), + ) + .intern(Interner), + chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const( + chalk_ir::ConstData { + ty: ty.clone(), + value: chalk_ir::ConstValue::BoundVar(index_db), + } + .intern(Interner), + ) + .intern(Interner), + } + }), + ); + chalk_ir::Canonical { binders, value: identity_subst } +} + /// Solve a trait goal using Chalk. pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: Crate, block: Option, goal: Canonical>, -) -> Option { +) -> NextTraitSolveResult { let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db .trait_signature(it.hir_trait_id()) @@ -128,7 +153,7 @@ pub(crate) fn trait_solve_query( && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) { // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - return Some(Solution::Ambig(Guidance::Unknown)); + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); } // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So @@ -139,71 +164,148 @@ pub(crate) fn trait_solve_query( // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) - let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - solve(db, krate, block, &u_canonical) + next_trait_solve(db, krate, block, goal) } -fn solve( - db: &dyn HirDatabase, +fn solve_nextsolver<'db>( + db: &'db dyn HirDatabase, krate: Crate, block: Option, goal: &chalk_ir::UCanonical>>, -) -> Option> { - let _p = tracing::info_span!("solve", ?krate, ?block).entered(); - let context = ChalkContext { db, krate, block }; - tracing::debug!("solve goal: {:?}", goal); - let mut solver = create_chalk_solver(); - - let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); - - let should_continue = || { - db.unwind_if_revision_cancelled(); - let remaining = fuel.get(); - fuel.set(remaining - 1); - if remaining == 0 { - tracing::debug!("fuel exhausted"); +) -> Result< + (HasChanged, Certainty, rustc_type_ir::Canonical, Vec>>), + rustc_type_ir::solve::NoSolution, +> { + // FIXME: should use analysis_in_body, but that needs GenericDefId::Block + let context = SolverContext( + DbInterner::new_with(db, Some(krate), block) + .infer_ctxt() + .build(TypingMode::non_body_analysis()), + ); + + match goal.canonical.value.goal.data(Interner) { + // FIXME: args here should be...what? not empty + GoalData::All(goals) if goals.is_empty(Interner) => { + return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![]))); } - remaining > 0 - }; + _ => {} + } - let mut solve = || { - let _ctx = if is_chalk_debug() || is_chalk_print() { - Some(panic_context::enter(format!("solving {goal:?}"))) - } else { - None - }; - let solution = if is_chalk_print() { - let logging_db = - LoggingRustIrDatabaseLoggingOnDrop(LoggingRustIrDatabase::new(context)); - solver.solve_limited(&logging_db.0, goal, &should_continue) - } else { - solver.solve_limited(&context, goal, &should_continue) - }; - - tracing::debug!("solve({:?}) => {:?}", goal, solution); - - solution - }; + let goal = goal.canonical.to_nextsolver(context.cx()); + tracing::info!(?goal); + + let (goal, var_values) = context.instantiate_canonical(&goal); + tracing::info!(?var_values); + + let res = context.evaluate_root_goal(goal, Span::dummy(), None); - // don't set the TLS for Chalk unless Chalk debugging is active, to make - // extra sure we only use it for debugging - if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() } + let vars = + var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); + let canonical_var_values = mini_canonicalize(context, vars); + + let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + res +} + +#[derive(Clone, Debug, PartialEq)] +pub enum NextTraitSolveResult { + Certain(chalk_ir::Canonical>), + Uncertain(chalk_ir::Canonical>), + NoSolution, } -struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase>); +impl NextTraitSolveResult { + pub fn no_solution(&self) -> bool { + matches!(self, NextTraitSolveResult::NoSolution) + } + + pub fn certain(&self) -> bool { + matches!(self, NextTraitSolveResult::Certain(..)) + } -impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> { - fn drop(&mut self) { - tracing::info!("chalk program:\n{}", self.0); + pub fn uncertain(&self) -> bool { + matches!(self, NextTraitSolveResult::Uncertain(..)) } } -fn is_chalk_debug() -> bool { - std::env::var("CHALK_DEBUG").is_ok() +/// Solve a trait goal using Chalk. +pub fn next_trait_solve( + db: &dyn HirDatabase, + krate: Crate, + block: Option, + goal: Canonical>, +) -> NextTraitSolveResult { + let detail = match &goal.value.goal.data(Interner) { + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { + db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string() + } + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), + _ => "??".to_owned(), + }; + let _p = tracing::info_span!("next_trait_solve", ?detail).entered(); + tracing::info!("next_trait_solve({:?})", goal.value.goal); + + if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(projection_ty), + .. + }))) = &goal.value.goal.data(Interner) + && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) + { + // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible + // FIXME + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); + } + + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + + // We currently don't deal with universes (I think / hope they're not yet + // relevant for our use cases?) + let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; + tracing::info!(?u_canonical); + + let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical); + + match next_solver_res { + Err(_) => NextTraitSolveResult::NoSolution, + Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( + convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), + ), + Ok((_, Certainty::Maybe(_), args)) => { + let subst = convert_canonical_args_for_result( + DbInterner::new_with(db, Some(krate), block), + args, + ); + NextTraitSolveResult::Uncertain(chalk_ir::Canonical { + binders: subst.binders, + value: subst.value.subst, + }) + } + } } -fn is_chalk_print() -> bool { - std::env::var("CHALK_PRINT").is_ok() +/// Solve a trait goal using Chalk. +pub fn next_trait_solve_in_ctxt<'db, 'a>( + infer_ctxt: &'a InferCtxt<'db>, + goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, +) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { + tracing::info!(?goal); + + let context = <&SolverContext<'db>>::from(infer_ctxt); + + let res = context.evaluate_root_goal(goal, Span::dummy(), None); + + let res = res.map(|r| (r.has_changed, r.certainty)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + res } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index c68ff706e4814..dfa39384320de 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -22,6 +22,8 @@ tracing.workspace = true triomphe.workspace = true indexmap.workspace = true +ra-ap-rustc_type_ir.workspace = true + # local deps base-db.workspace = true cfg.workspace = true @@ -36,6 +38,9 @@ span.workspace = true [dev-dependencies] expect-test.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index a323f97997c68..475906ae51a11 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -20,6 +20,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + mod attrs; mod from_id; mod has_source; @@ -54,7 +60,10 @@ use hir_def::{ }, item_tree::ImportAlias, layout::{self, ReprOptions, TargetDataLayout}, - nameres::{self, assoc::TraitItems, diagnostics::DefDiagnostic}, + nameres::{ + assoc::TraitItems, + diagnostics::{DefDiagnostic, DefDiagnosticKind}, + }, per_ns::PerNs, resolver::{HasResolver, Resolver}, signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, @@ -76,11 +85,11 @@ use hir_ty::{ layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution, mir::{MutBorrowKind, interpret_mir}, + next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt}, primitive::UintTy, traits::FnTrait, }; use itertools::Itertools; -use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::{AstIdNode, Edition, FileId}; @@ -187,6 +196,10 @@ pub struct CrateDependency { } impl Crate { + pub fn base(self) -> base_db::Crate { + self.id + } + pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin { self.id.data(db).origin.clone() } @@ -1247,6 +1260,25 @@ pub struct Field { pub(crate) id: LocalFieldId, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedField<'db> { + pub(crate) inner: Field, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedField<'db> { + /// Returns the type as in the signature of the struct. + pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let var_id = self.inner.parent.into(); + let field = db.field_types_ns(var_id)[self.inner.id]; + let ty = field.instantiate(interner, self.args); + TypeNs::new(db, var_id, ty) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: DefWithBodyId, @@ -1444,6 +1476,11 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { + let args = infer_ctxt.fresh_args_for_item(self.id.into()); + InstantiatedStruct { inner: self, args } + } } impl HasVisibility for Struct { @@ -1454,6 +1491,35 @@ impl HasVisibility for Struct { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedStruct<'db> { + pub(crate) inner: Struct, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedStruct<'db> { + pub fn fields(self, db: &dyn HirDatabase) -> Vec> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } + + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, @@ -1598,6 +1664,22 @@ impl HasVisibility for Enum { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedEnum<'db> { + pub(crate) inner: Enum, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedEnum<'db> { + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + impl From<&Variant> for DefWithBodyId { fn from(&v: &Variant) -> Self { DefWithBodyId::VariantId(v.into()) @@ -1673,6 +1755,38 @@ impl Variant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { + let args = + infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into()); + InstantiatedVariant { inner: self, args } + } +} + +// FIXME: Rename to `EnumVariant` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedVariant<'db> { + pub(crate) inner: Variant, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedVariant<'db> { + pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { + InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } + } + + pub fn fields(self, db: &dyn HirDatabase) -> Vec> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -5072,7 +5186,7 @@ impl<'db> Type<'db> { binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(self.env.krate, self.env.block, goal).is_some() + !db.trait_solve(self.env.krate, self.env.block, goal).no_solution() } pub fn normalize_trait_assoc_type( @@ -5827,6 +5941,55 @@ impl<'db> Type<'db> { } } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct TypeNs<'db> { + env: Arc, + ty: hir_ty::next_solver::Ty<'db>, + _pd: PhantomCovariantLifetime<'db>, +} + +impl<'db> TypeNs<'db> { + fn new( + db: &'db dyn HirDatabase, + lexical_env: impl HasResolver, + ty: hir_ty::next_solver::Ty<'db>, + ) -> Self { + let resolver = lexical_env.resolver(db); + let environment = resolver + .generic_def() + .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d)); + TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() } + } + + // FIXME: Find better API that also handles const generics + pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { + let args = GenericArgs::new_from_iter( + infcx.interner, + [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()), + ); + let trait_ref = hir_ty::next_solver::TraitRef::new( + infcx.interner, + SolverDefId::TraitId(trait_.id), + args, + ); + + let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )); + let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind); + let goal = hir_ty::next_solver::Goal::new( + infcx.interner, + hir_ty::next_solver::ParamEnv::empty(), + predicate, + ); + let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); + res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct InlineAsmOperand { owner: DefWithBodyId, @@ -6476,3 +6639,6 @@ pub fn resolve_absolute_path<'a, I: Iterator + Clone + 'a>( fn as_name_opt(name: Option) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } + +pub use hir_ty::next_solver; +pub use hir_ty::setup_tracing; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index af949a0649899..cb83c67c99535 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,3 +1,4 @@ +use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName}; @@ -80,17 +81,20 @@ fn existing_from_impl( sema: &'_ hir::Semantics<'_, RootDatabase>, variant: &ast::Variant, ) -> Option<()> { + let db = sema.db; let variant = sema.to_def(variant)?; - let enum_ = variant.parent_enum(sema.db); - let krate = enum_.module(sema.db).krate(); - + let krate = variant.module(db).krate(); let from_trait = FamousDefs(sema, krate).core_convert_From()?; + let interner = DbInterner::new_with(db, Some(krate.base()), None); + use hir::next_solver::infer::DbInternerInferExt; + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let enum_type = enum_.ty(sema.db); - - let wrapped_type = variant.fields(sema.db).first()?.ty(sema.db); - - if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { Some(()) } else { None } + let variant = variant.instantiate_infer(&infcx); + let enum_ = variant.parent_enum(sema.db); + let field_ty = variant.fields(sema.db).first()?.ty(sema.db); + let enum_ty = enum_.ty(sema.db); + tracing::debug!(?enum_, ?field_ty, ?enum_ty); + enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] @@ -119,15 +123,19 @@ impl From for A { ); } + // FIXME(next-solver): it would be nice to not be *required* to resolve the + // path in order to properly generate assists #[test] fn test_generate_from_impl_for_enum_complicated_path() { check_assist( generate_from_impl_for_enum, r#" //- minicore: from +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { $0One(foo::bar::baz::Boo) } "#, r#" +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { One(foo::bar::baz::Boo) } impl From for A { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 6076d5cb5cedf..deef4a9aac6db 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,4 +1,5 @@ use ast::make; +use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, @@ -47,6 +48,7 @@ pub(crate) fn generate_single_field_struct_from( let strukt_name = ctx.find_node_at_offset::()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; let ast::Adt::Struct(strukt) = adt else { + tracing::debug!(?adt); return None; }; @@ -57,10 +59,12 @@ pub(crate) fn generate_single_field_struct_from( let constructors = make_constructors(ctx, module, &types); if constructors.iter().filter(|expr| expr.is_none()).count() != 1 { + tracing::debug!(?constructors); return None; } let main_field_i = constructors.iter().position(Option::is_none)?; if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() { + tracing::debug!(?strukt, ?main_field_i); return None; } @@ -200,6 +204,7 @@ fn get_fields(strukt: &ast::Struct) -> Option<(Option>, Vec, assist_label: Option<&str>, ) { + let _tracing = setup_tracing(); let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); db.enable_proc_attr_macros(); let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string(); @@ -318,7 +319,9 @@ fn check_with_config( _ => AssistResolveStrategy::All, }; let mut acc = Assists::new(&ctx, resolve); - handler(&mut acc, &ctx); + salsa::attach(&db, || { + handler(&mut acc, &ctx); + }); let mut res = acc.finish(); let assist = match assist_label { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index f75123324f377..129964f8387a3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1384,14 +1384,15 @@ fn baz() { fn skip_iter() { check_no_kw( r#" - //- minicore: iterator + //- minicore: iterator, clone, builtin_impls fn foo() { [].$0 } "#, expect![[r#" - me clone() (as Clone) fn(&self) -> Self - me into_iter() (as IntoIterator) fn(self) -> ::IntoIter + me clone() (as Clone) fn(&self) -> Self + me fmt(…) (use core::fmt::Debug) fn(&self, &mut Formatter<'_>) -> Result<(), Error> + me into_iter() (as IntoIterator) fn(self) -> ::IntoIter "#]], ); check_no_kw( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index a70a1138d2f42..1a4c97e70b4ae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,6 +10,7 @@ mod snippet; #[cfg(test)] mod tests; +use base_db::salsa; use ide_db::{ FilePosition, FxHashSet, RootDatabase, imports::insert_use::{self, ImportScope}, @@ -228,7 +229,7 @@ pub fn completions( { let acc = &mut completions; - match analysis { + salsa::attach(db, || match analysis { CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) @@ -256,7 +257,7 @@ pub fn completions( ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), - } + }) } Some(completions.into()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index fdc3d9a13bc92..4b3b271ca20ef 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -26,7 +26,7 @@ mod visibility; use base_db::SourceDatabase; use expect_test::Expect; -use hir::PrefixKind; +use hir::{PrefixKind, setup_tracing}; use ide_db::{ FilePosition, RootDatabase, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -120,6 +120,8 @@ fn completion_list_with_config_raw( include_keywords: bool, trigger_character: Option, ) -> Vec { + let _tracing = setup_tracing(); + // filter out all but one built-in type completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); items diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 27c91bc7c4558..bb10086a08c64 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1,3 +1,4 @@ +use base_db::salsa; use expect_test::{Expect, expect}; use crate::{ @@ -19,25 +20,29 @@ fn check_with_config( let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); let mut acc = crate::completions::Completions::default(); - if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = - &analysis - { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); - } - if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { - match &name_ref_ctx.kind { - NameRefKind::Path(path) => { - crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); - } - NameRefKind::DotAccess(dot_access) => { - crate::completions::flyimport::import_on_the_fly_dot(&mut acc, &ctx, dot_access); - } - NameRefKind::Pattern(pattern) => { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); + salsa::attach(ctx.db, || { + if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = + &analysis + { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); + } + if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { + match &name_ref_ctx.kind { + NameRefKind::Path(path) => { + crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); + } + NameRefKind::DotAccess(dot_access) => { + crate::completions::flyimport::import_on_the_fly_dot( + &mut acc, &ctx, dot_access, + ); + } + NameRefKind::Pattern(pattern) => { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); + } + _ => (), } - _ => (), } - } + }); expect.assert_eq(&super::render_completion_list(Vec::from(acc))); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 148203107c4cf..84ddff8f617ac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -677,6 +677,7 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -706,6 +707,7 @@ fn bar() -> Bar { fn bar() fn() fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -734,6 +736,7 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 1e80d02926d1b..6331090d9c90a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -157,7 +157,7 @@ fn h(x: &Y) -> Y { fn no_false_positive_dyn_fn() { check_diagnostics( r#" -//- minicore: copy, fn +//- minicore: copy, fn, dispatch_from_dyn fn f(x: &mut &mut dyn Fn()) { x(); } @@ -166,7 +166,7 @@ struct X<'a> { field: &'a mut dyn Fn(), } -fn f(x: &mut X<'_>) { +fn g(x: &mut X<'_>) { (x.field)(); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index a1db92641f5ee..a4eb3d47d708b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -92,7 +92,7 @@ use hir::{ use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _}, + base_db::{ReleaseChannel, RootQueryDb as _, salsa}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -537,10 +537,12 @@ pub fn full_diagnostics( resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec { - let mut res = syntax_diagnostics(db, config, file_id); - let sema = semantic_diagnostics(db, config, resolve, file_id); - res.extend(sema); - res + salsa::attach(db, || { + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res + }) } /// Returns whether to keep this diagnostic (or remove it). diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index 181993154e59f..c3cc5a08b56b5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -2,6 +2,7 @@ mod overly_long_real_world_cases; +use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, @@ -198,6 +199,8 @@ pub(crate) fn check_diagnostics_with_config( config: DiagnosticsConfig, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) { + let _tracing = setup_tracing(); + let (db, files) = RootDatabase::with_many_files(ra_fixture); let mut annotations = files .iter() diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index a4e2cfbaee27d..1d5f5adf2eefe 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -1,7 +1,7 @@ //! This module is responsible for resolving paths within rules. use hir::AsAssocItem; -use ide_db::FxHashMap; +use ide_db::{FxHashMap, base_db::salsa}; use parsing::Placeholder; use syntax::{ SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, @@ -48,16 +48,20 @@ impl<'db> ResolvedRule<'db> { resolution_scope: &ResolutionScope<'db>, index: usize, ) -> Result, SsrError> { - let resolver = - Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in }; - let resolved_template = match rule.template { - Some(template) => Some(resolver.resolve_pattern_tree(template)?), - None => None, - }; - Ok(ResolvedRule { - pattern: resolver.resolve_pattern_tree(rule.pattern)?, - template: resolved_template, - index, + salsa::attach(resolution_scope.scope.db, || { + let resolver = Resolver { + resolution_scope, + placeholders_by_stand_in: rule.placeholders_by_stand_in, + }; + let resolved_template = match rule.template { + Some(template) => Some(resolver.resolve_pattern_tree(template)?), + None => None, + }; + Ok(ResolvedRule { + pattern: resolver.resolve_pattern_tree(rule.pattern)?, + template: resolved_template, + index, + }) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index c2f08f8ca2e6b..b004c07576748 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -4,6 +4,7 @@ use expect_test::{Expect, expect}; use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, Documentation, HasDocs}, }; @@ -63,8 +64,10 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { .flat_map(|(text_range, link, ns)| { let attr = range.map(text_range); let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); - let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) - .unwrap_or_else(|| panic!("Failed to resolve {link}")); + let def = salsa::attach(sema.db, || { + resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) + .unwrap_or_else(|| panic!("Failed to resolve {link}")) + }); def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) }) .map(|(nav_target, link)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f469..633feec622cd9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, SymbolKind, - base_db::{AnchoredPath, SourceDatabase}, + base_db::{AnchoredPath, SourceDatabase, salsa}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -108,7 +108,7 @@ pub(crate) fn goto_definition( } Some( - IdentClass::classify_node(sema, &parent)? + salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))? .definitions() .into_iter() .flat_map(|(def, _)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 02d96a6473281..4f172621908c5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -234,6 +234,7 @@ impl crate::T for crate::Foo {} ); } + // FIXME(next-solver): it would be nice to be able to also point to `&Foo` #[test] fn goto_implementation_all_impls() { check( @@ -246,7 +247,6 @@ impl Foo {} impl T for Foo {} //^^^ impl T for &Foo {} - //^^^^ "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 44c98a43f6944..a48fe43e80803 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -12,6 +12,7 @@ use hir::{ }; use ide_db::{ FileRange, FxIndexSet, Ranker, RootDatabase, + base_db::salsa, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -290,7 +291,7 @@ fn hover_offset( .into_iter() .unique_by(|&((def, _), _, _, _)| def) .map(|((def, subst), macro_arm, hovered_definition, node)| { - hover_for_definition( + salsa::attach(sema.db, || hover_for_definition( sema, file_id, def, @@ -301,7 +302,7 @@ fn hover_offset( config, edition, display_target, - ) + )) }) .collect::>(), ) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 51b5900e8155a..1c0272cdfacd1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,6 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, @@ -44,7 +45,7 @@ pub(super) fn type_info_of( Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - type_info(sema, _config, ty_info, edition, display_target) + salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target)) } pub(super) fn closure_expr( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c5480217a91e2..6f1bbad6ba169 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -6,6 +6,8 @@ use crate::{ HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture, }; +use hir::setup_tracing; + const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, memory_layout: Some(MemoryLayoutHoverConfig { @@ -38,6 +40,7 @@ fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 8c2a2f6f72f5e..f6416fe51c307 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -8,7 +8,9 @@ use hir::{ ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; +use ide_db::{ + FileRange, RootDatabase, base_db::salsa, famous_defs::FamousDefs, text_edit::TextEditBuilder, +}; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; use smallvec::{SmallVec, smallvec}; @@ -734,7 +736,7 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = hint_iterator(sema, famous_defs, ty); + let iter_item_type = salsa::attach(sema.db, || hint_iterator(sema, famous_defs, ty)); match iter_item_type { Some((iter_trait, item, ty)) => { const LABEL_START: &str = "impl "; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 4d020bac3aad4..39554d777795c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -411,10 +411,9 @@ fn main() { (()) == {()}; // ^^& // ^^^^& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^&* closure(); - //^^^^^^^(& - //^^^^^^^) Struct[0]; //^^^^^^(& //^^^^^^) @@ -507,9 +506,10 @@ fn main() { (()) == {()}; // ^^.& // ^^^^.& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^( + //^^^^^^).*.&. closure(); - //^^^^^^^.& Struct[0]; //^^^^^^.& &mut Struct[0]; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 922e9598aa017..7a4af4f7549c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -909,7 +909,7 @@ fn main() { foo(plus_one); let add_mul = bar(|x: u8| { x + 1 }); - // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized + // ^^^^^^^ impl FnOnce(u8) -> u8 let closure = if let Some(6) = add_mul(2).checked_sub(1) { // ^^^^^^^ fn(i32) -> i32 diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 382573b680113..2cccb9639f3eb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -733,7 +733,7 @@ fn signature_help_for_tuple_pat_ish<'db>( mod tests { use expect_test::{Expect, expect}; - use ide_db::FilePosition; + use ide_db::{FilePosition, base_db::salsa}; use stdx::format_to; use test_fixture::ChangeFixture; @@ -762,7 +762,7 @@ mod tests { "# ); let (db, position) = position(&fixture); - let sig_help = crate::signature_help::signature_help(&db, position); + let sig_help = salsa::attach(&db, || crate::signature_help::signature_help(&db, position)); let actual = match sig_help { Some(sig_help) => { let mut rendered = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 3ca172977cb9e..e3dc3ed3c742a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -16,7 +16,7 @@ use std::ops::ControlFlow; use either::Either; use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, base_db::salsa}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -434,15 +434,17 @@ fn traverse( |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); let element = match descended_element.value { NodeOrToken::Node(name_like) => { - let hl = highlight::name_like( - sema, - krate, - bindings_shadow_count, - &is_unsafe_node, - config.syntactic_name_ref_highlighting, - name_like, - edition, - ); + let hl = salsa::attach(sema.db, || { + highlight::name_like( + sema, + krate, + bindings_shadow_count, + &is_unsafe_node, + config.syntactic_name_ref_highlighting, + name_like, + edition, + ) + }); if hl.is_some() && !in_macro { // skip highlighting the contained token of our name-like node // as that would potentially overwrite our result diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 00925bd81ed8e..d99b29cfb8fa6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -127,9 +127,9 @@ let y = &mut x; let z = &y; - let Foo { x: z, y } = Foo { x: z, y }; + let Foo { x: z, y } = Foo { x: z, y }; - y; + y; let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; @@ -142,7 +142,7 @@ copy.qux(); copy.baz(copy); - let a = |x| x; + let a = |x| x; let bar = Foo::baz; let baz = (-42,); @@ -172,13 +172,13 @@ } async fn learn_and_sing() { - let song = learn_song().await; - sing_song(song).await; + let song = learn_song().await; + sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); - let f2 = dance(); + let f2 = dance(); futures::join!(f1, f2); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 4780743c4d92a..983ed3684a4f6 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -178,6 +178,8 @@ define_symbols! { core, coroutine_state, coroutine, + coroutine_return, + coroutine_yield, count, crate_type, CStr, @@ -226,6 +228,8 @@ define_symbols! { async_fn_once_output, async_fn_mut, async_fn, + call_ref_future, + call_once_future, fn_ptr_addr, fn_ptr_trait, format_alignment, @@ -416,6 +420,7 @@ define_symbols! { rustc_allow_incoherent_impl, rustc_builtin_macro, rustc_coherence_is_core, + rustc_coinductive, rustc_const_panic_str, rustc_deallocator, rustc_deprecated_safe_2024, diff --git a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs index 9f3e636ef816a..00c37c01d25e2 100644 --- a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs +++ b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs @@ -37,10 +37,10 @@ impl StopWatch { .build() .map_err(|err| eprintln!("Failed to create perf counter: {err}")) .ok(); - if let Some(counter) = &mut counter { - if let Err(err) = counter.enable() { - eprintln!("Failed to start perf counter: {err}") - } + if let Some(counter) = &mut counter + && let Err(err) = counter.enable() + { + eprintln!("Failed to start perf counter: {err}") } counter } else { diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index c151cca07272a..22a26c49fa530 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs @@ -48,7 +48,8 @@ impl ToTokens for TrackedQuery { quote!(#(#options),*) }) .into_iter() - .chain(self.lru.map(|lru| quote!(lru = #lru))); + .chain(self.lru.map(|lru| quote!(lru = #lru))) + .chain(Some(quote!(unsafe(non_update_return_type)))); let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]); let pat_and_tys = &self.pat_and_tys; diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7b719b5dec754..b735b323ce430 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -35,7 +35,7 @@ //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fmt_before_1_89_0: fmt -//! fn: tuple +//! fn: sized, tuple //! from: sized, result //! future: pin //! coroutine: pin @@ -325,6 +325,18 @@ pub mod clone { *self } } + + impl Clone for [T; 0] { + fn clone(&self) -> Self { + [] + } + } + + impl Clone for [T; 1] { + fn clone(&self) -> Self { + [self[0].clone()] + } + } // endregion:builtin_impls // region:derive @@ -1035,6 +1047,7 @@ pub mod ops { #[lang = "coroutine"] pub trait Coroutine { + #[lang = "coroutine_yield"] type Yield; #[lang = "coroutine_return"] type Return; From 6b8a812aafaad6b0edd04d12569fe43ee1e69eed Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Sun, 10 Aug 2025 01:38:17 -0400 Subject: [PATCH 0040/1900] parser: fix parsing of trait bound polarity and for-binders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rustc AST allows both `for<>` binders and `?` polarity modifiers in trait bounds, but they are parsed in a specific order and validated for correctness: 1. `for<>` binder is parsed first. 2. Polarity modifiers (`?`, `!`) are parsed second. 3. The parser validates that binders and polarity modifiers do not conflict: ```rust if let Some(binder_span) = binder_span { match modifiers.polarity { BoundPolarity::Maybe(polarity_span) => { // Error: "for<...> binder not allowed with ? polarity" } } } ``` This implies: - `for<> ?Sized` → Valid syntax. Invalid semantics. - `?for<> Sized` → Invalid syntax. However, rust-analyzer incorrectly had special-case logic that allowed `?for<>` as valid syntax. This fix removes that incorrect special case, making rust-analyzer reject `?for<> Sized` as a syntax error, matching rustc behavior. This has caused confusion in other crates (such as syn) which rely on these files to implement correct syntax evaluation. --- .../parser/src/grammar/generic_params.rs | 11 ++--- .../parser/test_data/generated/runner.rs | 6 +++ ...invalid_question_for_type_trait_bound.rast | 49 +++++++++++++++++++ .../invalid_question_for_type_trait_bound.rs | 1 + .../ok/question_for_type_trait_bound.rast | 23 +++++---- .../ok/question_for_type_trait_bound.rs | 2 +- 6 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index cb1b59f649774..d419817e5cd70 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -182,12 +182,6 @@ fn type_bound(p: &mut Parser<'_>) -> bool { ); m.complete(p, USE_BOUND_GENERIC_ARGS); } - T![?] if p.nth_at(1, T![for]) => { - // test question_for_type_trait_bound - // fn f() where T: ?for<> Sized {} - p.bump_any(); - types::for_type(p, false) - } _ => { if path_type_bound(p).is_err() { m.abandon(p); @@ -219,8 +213,13 @@ fn path_type_bound(p: &mut Parser<'_>) -> Result<(), ()> { // test async_trait_bound // fn async_foo(_: impl async Fn(&i32)) {} p.eat(T![async]); + // test question_for_type_trait_bound + // fn f() where T: for<> ?Sized {} p.eat(T![?]); + // test_err invalid_question_for_type_trait_bound + // fn f() where T: ?for<> Sized {} + if paths::is_use_path_start(p) { types::path_type_bounds(p, false); // test_err type_bounds_macro_call_recovery diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index c642e1a3354fc..a3cfe64e6e739 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -796,6 +796,12 @@ mod err { #[test] fn impl_type() { run_and_expect_errors("test_data/parser/inline/err/impl_type.rs"); } #[test] + fn invalid_question_for_type_trait_bound() { + run_and_expect_errors( + "test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs", + ); + } + #[test] fn let_else_right_curly_brace() { run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast new file mode 100644 index 0000000000000..b060ee81d6178 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + WHERE_PRED + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + QUESTION "?" + WHERE_PRED + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 20: expected comma +error 31: expected colon diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs new file mode 100644 index 0000000000000..f80dd90d446c5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs @@ -0,0 +1 @@ +fn f() where T: ?for<> Sized {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast index cb296153c8f1a..69db1aee2c5a5 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast @@ -27,19 +27,18 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND_LIST TYPE_BOUND + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " QUESTION "?" - FOR_TYPE - FOR_BINDER - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Sized" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs index f80dd90d446c5..96353df3b9b33 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs @@ -1 +1 @@ -fn f() where T: ?for<> Sized {} +fn f() where T: for<> ?Sized {} From 8c0f4b40803a6d0c6ed6525026217c8d2532d205 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 Aug 2025 14:37:35 +0800 Subject: [PATCH 0041/1900] Fix extract_expressions_from_format_string on write! **Input**: ```rust fn main() { write!(f, "{2+3}$0") } ``` **Old output**: ```rust fn main() { write!("{}"$0, 2+3) } ``` **This PR output**: ```rust fn main() { write!(f, "{}"$0, 2+3) } ``` --- .../extract_expressions_from_format_string.rs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index cdc0e967101a4..e3c7ea1b09391 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -7,8 +7,8 @@ use itertools::Itertools; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, - T, - ast::{self, make, syntax_factory::SyntaxFactory}, + SyntaxToken, T, + ast::{self, TokenTree, make, syntax_factory::SyntaxFactory}, }; // Assist: extract_expressions_from_format_string @@ -58,10 +58,11 @@ pub(crate) fn extract_expressions_from_format_string( tt.syntax().text_range(), |edit| { // Extract existing arguments in macro - let tokens = tt.token_trees_and_tokens().collect_vec(); + let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); + let format_string_index = format_str_index(&raw_tokens, &fmt_string); + let tokens = raw_tokens.split_off(format_string_index); let existing_args = if let [ - _opening_bracket, NodeOrToken::Token(_format_string), _args_start_comma, tokens @ .., @@ -90,9 +91,11 @@ pub(crate) fn extract_expressions_from_format_string( // Start building the new args let mut existing_args = existing_args.into_iter(); - let mut new_tt_bits = vec![NodeOrToken::Token(make::tokens::literal(&new_fmt))]; + let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; + new_tt_bits.push(NodeOrToken::Token(make::tokens::literal(&new_fmt))); + for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg @@ -150,7 +153,9 @@ pub(crate) fn extract_expressions_from_format_string( } // Add the final tabstop after the format literal - if let Some(NodeOrToken::Token(literal)) = new_tt.token_trees_and_tokens().nth(1) { + if let Some(NodeOrToken::Token(literal)) = + new_tt.token_trees_and_tokens().nth(1 + format_string_index) + { let annotation = edit.make_tabstop_after(cap); editor.add_annotation(literal, annotation); } @@ -163,6 +168,17 @@ pub(crate) fn extract_expressions_from_format_string( Some(()) } +fn format_str_index( + raw_tokens: &[NodeOrToken], + fmt_string: &ast::String, +) -> usize { + let fmt_string = fmt_string.syntax(); + raw_tokens + .iter() + .position(|tt| tt.as_token().is_some_and(|tt| tt == fmt_string)) + .unwrap_or_default() +} + #[cfg(test)] mod tests { use super::*; @@ -186,6 +202,24 @@ fn main() { ); } + #[test] + fn multiple_middle_arg_on_write() { + check_assist( + extract_expressions_from_format_string, + r#" +//- minicore: write +fn main() { + write!(writer(), "{} {x + 1:b} {}$0", y + 2, 2); +} +"#, + r#" +fn main() { + write!(writer(), "{} {:b} {}"$0, y + 2, x + 1, 2); +} +"#, + ); + } + #[test] fn single_arg() { check_assist( From 49044d0d4044960fbb96d47f51640bc5fa8004af Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jan 2025 16:57:30 +0100 Subject: [PATCH 0042/1900] Add support for trait associated items --- src/librustdoc/html/render/span_map.rs | 61 ++++++++++++------------ tests/rustdoc/jump-to-def-assoc-items.rs | 15 ++++++ 2 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 tests/rustdoc/jump-to-def-assoc-items.rs diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 846d3ad310ce4..ebbc61db688f7 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,9 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ - ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, -}; +use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -189,31 +187,6 @@ impl SpanMapVisitor<'_> { self.matches.insert(span, link); } } - - fn handle_pat(&mut self, p: &Pat<'_>) { - let mut check_qpath = |qpath, hir_id| match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }; - match p.kind { - PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { - check_qpath(qpath, p.hir_id) - } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { - check_qpath(*qpath, *hir_id) - } - PatKind::Or(pats) => { - for pat in pats { - self.handle_pat(pat); - } - } - _ => {} - } - } } impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { @@ -231,8 +204,36 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_path(self, path); } - fn visit_pat(&mut self, p: &Pat<'tcx>) { - self.handle_pat(p); + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, span: Span) { + match *qpath { + QPath::TypeRelative(qself, path) => { + if matches!(path.res, Res::Err) { + let tcx = self.tcx; + let hir = tcx.hir(); + let body_id = hir.enclosing_body_owner(id); + let typeck_results = tcx.typeck_body(hir.body_owned_by(body_id).id()); + let path = rustc_hir::Path { + // We change the span to not include parens. + span: span.with_hi(path.ident.span.hi()), + res: typeck_results.qpath_res(qpath, id), + segments: &[], + }; + self.handle_path(&path); + } else { + self.infer_id(path.hir_id, Some(id), span); + } + + rustc_ast::visit::try_visit!(self.visit_ty(qself)); + self.visit_path_segment(path); + } + QPath::Resolved(maybe_qself, path) => { + self.handle_path(path); + + rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); + self.visit_path(path, id) + } + _ => {} + } } fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { diff --git a/tests/rustdoc/jump-to-def-assoc-items.rs b/tests/rustdoc/jump-to-def-assoc-items.rs new file mode 100644 index 0000000000000..1229c36317253 --- /dev/null +++ b/tests/rustdoc/jump-to-def-assoc-items.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +pub enum Ty { + Var, +} + +//@ has 'src/foo/jump-to-def-assoc-items.rs.html' +//@ has - '//a[@href="#6"]' 'Self::Var' +impl Ty { + fn f() { + let _ = Self::Var; + } +} From f1649a4e4379aa0f5eb1181ca8e04c7790c21e5e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 26 Jan 2025 21:24:36 +0100 Subject: [PATCH 0043/1900] Better handling of paths in link to def feature --- src/librustdoc/html/highlight.rs | 120 +++++++++++------- src/librustdoc/html/render/span_map.rs | 70 ++++++---- tests/rustdoc/jump-to-def-assoc-items.rs | 53 +++++++- .../jump-to-def-doc-links-calls.rs | 4 +- tests/rustdoc/jump-to-def/jump-to-def-pats.rs | 10 +- .../jump-to-def/jump-to-non-local-method.rs | 7 +- .../check-source-code-urls-to-def.rs | 2 +- 7 files changed, 175 insertions(+), 91 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 272180fb9904c..c88db697be1f7 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -1059,6 +1059,64 @@ fn string( } } +fn generate_link_to_def( + out: &mut impl Write, + text_s: &str, + klass: Class, + href_context: &Option>, + def_span: Span, + open_tag: bool, +) -> bool { + if let Some(href_context) = href_context + && let Some(href) = + href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| { + let context = href_context.context; + // FIXME: later on, it'd be nice to provide two links (if possible) for all items: + // one to the documentation page and one to the source definition. + // FIXME: currently, external items only generate a link to their documentation, + // a link to their definition can be generated using this: + // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 + match href { + LinkFromSrc::Local(span) => { + context.href_from_span_relative(*span, &href_context.current_href) + } + LinkFromSrc::External(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(url, _, _)| url) + } + LinkFromSrc::Primitive(prim) => format::href_with_root_path( + PrimitiveType::primitive_locations(context.tcx())[prim], + context, + Some(href_context.root_path), + ) + .ok() + .map(|(url, _, _)| url), + LinkFromSrc::Doc(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(doc_link, _, _)| doc_link) + } + } + }) + { + if !open_tag { + // We're already inside an element which has the same klass, no need to give it + // again. + write!(out, "{text_s}").unwrap(); + } else { + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "{text_s}").unwrap(); + } else { + write!(out, "{text_s}").unwrap(); + } + } + return true; + } + false +} + /// This function writes `text` into `out` with some modifications depending on `klass`: /// /// * If `klass` is `None`, `text` is written into `out` with no modification. @@ -1088,10 +1146,14 @@ fn string_without_closing_tag( return Some(""); }; + let mut added_links = false; let mut text_s = text.to_string(); if text_s.contains("::") { + let mut span = def_span.with_hi(def_span.lo()); text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { + span = span.with_hi(span.hi() + BytePos(t.len() as _)); match t { + "::" => write!(&mut path, "::"), "self" | "Self" => write!( &mut path, "{t}", @@ -1104,58 +1166,24 @@ fn string_without_closing_tag( klass = Class::KeyWord.as_html(), ) } - t => write!(&mut path, "{t}"), + t => { + if !t.is_empty() + && generate_link_to_def(&mut path, t, klass, href_context, span, open_tag) + { + added_links = true; + write!(&mut path, "") + } else { + write!(&mut path, "{t}") + } + } } .expect("Failed to build source HTML path"); + span = span.with_lo(span.lo() + BytePos(t.len() as _)); path }); } - if let Some(href_context) = href_context - && let Some(href) = href_context.context.shared.span_correspondence_map.get(&def_span) - && let Some(href) = { - let context = href_context.context; - // FIXME: later on, it'd be nice to provide two links (if possible) for all items: - // one to the documentation page and one to the source definition. - // FIXME: currently, external items only generate a link to their documentation, - // a link to their definition can be generated using this: - // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 - match href { - LinkFromSrc::Local(span) => { - context.href_from_span_relative(*span, &href_context.current_href) - } - LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(url, _, _)| url) - } - LinkFromSrc::Primitive(prim) => format::href_with_root_path( - PrimitiveType::primitive_locations(context.tcx())[prim], - context, - Some(href_context.root_path), - ) - .ok() - .map(|(url, _, _)| url), - LinkFromSrc::Doc(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(doc_link, _, _)| doc_link) - } - } - } - { - if !open_tag { - // We're already inside an element which has the same klass, no need to give it - // again. - write!(out, "{text_s}").unwrap(); - } else { - let klass_s = klass.as_html(); - if klass_s.is_empty() { - write!(out, "{text_s}").unwrap(); - } else { - write!(out, "{text_s}").unwrap(); - } - } + if !added_links && generate_link_to_def(out, &text_s, klass, href_context, def_span, open_tag) { return Some(""); } if !open_tag { diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index ebbc61db688f7..342cf5073f716 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -65,7 +65,7 @@ struct SpanMapVisitor<'tcx> { impl SpanMapVisitor<'_> { /// This function is where we handle `hir::Path` elements and add them into the "span map". - fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { + fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) { match path.res { // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. // Would be nice to support them too alongside the other `DefKind` @@ -77,24 +77,36 @@ impl SpanMapVisitor<'_> { LinkFromSrc::External(def_id) }; // In case the path ends with generics, we remove them from the span. - let span = path - .segments - .last() - .map(|last| { - // In `use` statements, the included item is not in the path segments. - // However, it doesn't matter because you can't have generics on `use` - // statements. - if path.span.contains(last.ident.span) { - path.span.with_hi(last.ident.span.hi()) - } else { - path.span - } - }) - .unwrap_or(path.span); + let span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.segments + .last() + .map(|last| { + // In `use` statements, the included item is not in the path segments. + // However, it doesn't matter because you can't have generics on `use` + // statements. + if path.span.contains(last.ident.span) { + path.span.with_hi(last.ident.span.hi()) + } else { + path.span + } + }) + .unwrap_or(path.span) + }; self.matches.insert(span, link); } Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => { - self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); + let path_span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.span + }; + self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span))); } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. @@ -200,37 +212,41 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { if self.handle_macro(path.span) { return; } - self.handle_path(path); + self.handle_path(path, false); intravisit::walk_path(self, path); } - fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, span: Span) { + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) { match *qpath { QPath::TypeRelative(qself, path) => { if matches!(path.res, Res::Err) { let tcx = self.tcx; - let hir = tcx.hir(); - let body_id = hir.enclosing_body_owner(id); - let typeck_results = tcx.typeck_body(hir.body_owned_by(body_id).id()); + let body_id = tcx.hir_enclosing_body_owner(id); + let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); let path = rustc_hir::Path { // We change the span to not include parens. - span: span.with_hi(path.ident.span.hi()), + span: path.ident.span, res: typeck_results.qpath_res(qpath, id), segments: &[], }; - self.handle_path(&path); + self.handle_path(&path, false); } else { - self.infer_id(path.hir_id, Some(id), span); + self.infer_id(path.hir_id, Some(id), path.ident.span); } - rustc_ast::visit::try_visit!(self.visit_ty(qself)); + if let Some(qself) = qself.try_as_ambig_ty() { + rustc_ast::visit::try_visit!(self.visit_ty(qself)); + } self.visit_path_segment(path); } QPath::Resolved(maybe_qself, path) => { - self.handle_path(path); + self.handle_path(path, true); + let maybe_qself = maybe_qself.and_then(|maybe_qself| maybe_qself.try_as_ambig_ty()); rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); - self.visit_path(path, id) + if !self.handle_macro(path.span) { + intravisit::walk_path(self, path); + } } _ => {} } diff --git a/tests/rustdoc/jump-to-def-assoc-items.rs b/tests/rustdoc/jump-to-def-assoc-items.rs index 1229c36317253..8cbf990628386 100644 --- a/tests/rustdoc/jump-to-def-assoc-items.rs +++ b/tests/rustdoc/jump-to-def-assoc-items.rs @@ -1,15 +1,54 @@ +// This test ensures that patterns also get a link generated. + //@ compile-flags: -Zunstable-options --generate-link-to-definition #![crate_name = "foo"] -pub enum Ty { - Var, +//@ has 'src/foo/jump-to-def-assoc-items.rs.html' + +pub trait Trait { + type T; +} +pub trait Another { + type T; + const X: u32; } -//@ has 'src/foo/jump-to-def-assoc-items.rs.html' -//@ has - '//a[@href="#6"]' 'Self::Var' -impl Ty { - fn f() { - let _ = Self::Var; +pub struct Foo; + +impl Foo { + pub fn new() -> Self { Foo } +} + +pub struct C; + +impl C { + pub fn wat() {} +} + +pub struct Bar; +impl Trait for Bar { + type T = Foo; +} +impl Another for Bar { + type T = C; + const X: u32 = 12; +} + +pub fn bar() { + //@ has - '//a[@href="#20"]' 'new' + ::T::new(); + //@ has - '//a[@href="#26"]' 'wat' + ::T::wat(); + + match 12u32 { + //@ has - '//a[@href="#14"]' 'X' + ::X => {} + _ => {} } } + +pub struct Far { + //@ has - '//a[@href="#10"]' 'T' + x: ::T, +} diff --git a/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs b/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs index 618569787733c..55e59f23b6f28 100644 --- a/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs +++ b/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs @@ -8,7 +8,7 @@ pub struct Bar; impl std::default::Default for Bar { - //@ has - '//a[@href="#20-22"]' 'Self::new' + //@ has - '//a[@href="#20-22"]' 'new' fn default() -> Self { Self::new() } @@ -16,7 +16,7 @@ impl std::default::Default for Bar { //@ has - '//a[@href="#8"]' 'Bar' impl Bar { - //@ has - '//a[@href="#24-26"]' 'Self::bar' + //@ has - '//a[@href="#24-26"]' 'bar' pub fn new()-> Self { Self::bar() } diff --git a/tests/rustdoc/jump-to-def/jump-to-def-pats.rs b/tests/rustdoc/jump-to-def/jump-to-def-pats.rs index 147902b44cf5c..852eba208db01 100644 --- a/tests/rustdoc/jump-to-def/jump-to-def-pats.rs +++ b/tests/rustdoc/jump-to-def/jump-to-def-pats.rs @@ -30,13 +30,13 @@ pub fn foo() -> Result<(), ()> { impl fmt::Display for MyEnum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - //@ has - '//a[@href="#12"]' 'Self::Ok' + //@ has - '//a[@href="#12"]' 'Ok' Self::Ok(_) => f.write_str("MyEnum::Ok"), - //@ has - '//a[@href="#13"]' 'MyEnum::Err' + //@ has - '//a[@href="#13"]' 'Err' MyEnum::Err(_) => f.write_str("MyEnum::Err"), - //@ has - '//a[@href="#14"]' 'Self::Some' + //@ has - '//a[@href="#14"]' 'Some' Self::Some(_) => f.write_str("MyEnum::Some"), - //@ has - '//a[@href="#15"]' 'Self::None' + //@ has - '//a[@href="#15"]' 'None' Self::None => f.write_str("MyEnum::None"), } } @@ -45,7 +45,7 @@ impl fmt::Display for MyEnum { impl X { fn p(&self) -> &str { match self { - //@ has - '//a[@href="#19"]' 'Self::A' + //@ has - '//a[@href="#19"]' 'A' Self::A => "X::A", } } diff --git a/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs b/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs index e2f530425f0db..1d6d6b8d18faa 100644 --- a/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs +++ b/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs @@ -21,9 +21,10 @@ pub fn bar2(readable: T) { } pub fn bar() { - //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'AtomicIsize::new' + //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/sync/atomic/struct.AtomicIsize.html"]' 'AtomicIsize' + //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'new' let _ = AtomicIsize::new(0); - //@ has - '//a[@href="#48"]' 'local_private' + //@ has - '//a[@href="#49"]' 'local_private' local_private(); } @@ -39,7 +40,7 @@ pub fn macro_call() -> Result<(), ()> { } pub fn variant() { - //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/cmp/enum.Ordering.html#variant.Less"]' 'Ordering::Less' + //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/cmp/enum.Ordering.html#variant.Less"]' 'Less' let _ = Ordering::Less; //@ has - '//a[@href="https://github.com/%7B%7Bchannel%7D%7D/core/marker/struct.PhantomData.html"]' 'PhantomData' let _: PhantomData:: = PhantomData; diff --git a/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs index d701b88bf9fd0..a7b944fa2f6fc 100644 --- a/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs +++ b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs @@ -34,7 +34,7 @@ fn babar() {} // The 5 links to line 23 and the line 23 itself. //@ count - '//pre[@class="rust"]//a[@href="#23"]' 6 //@ has - '//pre[@class="rust"]//a[@href="https://github.com/source_code/struct.SourceCode.html"]' \ -// 'source_code::SourceCode' +// 'SourceCode' pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) { let x = 12; let y: Foo = Foo; From 1a6d6363d090961a1efe513d413f5b51ecd81ab8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 26 Jan 2025 21:50:50 +0100 Subject: [PATCH 0044/1900] Update to last rustc_hir Visitor changes --- src/librustdoc/html/render/span_map.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 342cf5073f716..505797ccc0b2f 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; @@ -234,16 +234,13 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { self.infer_id(path.hir_id, Some(id), path.ident.span); } - if let Some(qself) = qself.try_as_ambig_ty() { - rustc_ast::visit::try_visit!(self.visit_ty(qself)); - } + rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself)); self.visit_path_segment(path); } QPath::Resolved(maybe_qself, path) => { self.handle_path(path, true); - let maybe_qself = maybe_qself.and_then(|maybe_qself| maybe_qself.try_as_ambig_ty()); - rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); + rustc_ast::visit::visit_opt!(self, visit_ty_unambig, maybe_qself); if !self.handle_macro(path.span) { intravisit::walk_path(self, path); } From d528a49fd7c0447cc51406908fb13fe055d273aa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 May 2025 19:08:23 +0200 Subject: [PATCH 0045/1900] Fix panic if an item does not have a body --- src/librustdoc/html/render/span_map.rs | 32 ++++++++++++++++++-------- tests/rustdoc/jump-to-def-ice.rs | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 tests/rustdoc/jump-to-def-ice.rs diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 505797ccc0b2f..6906e6e30ff86 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; @@ -201,6 +201,17 @@ impl SpanMapVisitor<'_> { } } +// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without +// panicking. +fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { + for (_, node) in tcx.hir_parent_iter(hir_id) { + if let Some((def_id, _)) = node.associated_body() { + return Some(def_id); + } + } + None +} + impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { type NestedFilter = nested_filter::All; @@ -221,15 +232,16 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { QPath::TypeRelative(qself, path) => { if matches!(path.res, Res::Err) { let tcx = self.tcx; - let body_id = tcx.hir_enclosing_body_owner(id); - let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); - let path = rustc_hir::Path { - // We change the span to not include parens. - span: path.ident.span, - res: typeck_results.qpath_res(qpath, id), - segments: &[], - }; - self.handle_path(&path, false); + if let Some(body_id) = hir_enclosing_body_owner(tcx, id) { + let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); + let path = rustc_hir::Path { + // We change the span to not include parens. + span: path.ident.span, + res: typeck_results.qpath_res(qpath, id), + segments: &[], + }; + self.handle_path(&path, false); + } } else { self.infer_id(path.hir_id, Some(id), path.ident.span); } diff --git a/tests/rustdoc/jump-to-def-ice.rs b/tests/rustdoc/jump-to-def-ice.rs new file mode 100644 index 0000000000000..5578b9af3d74f --- /dev/null +++ b/tests/rustdoc/jump-to-def-ice.rs @@ -0,0 +1,24 @@ +// This test ensures that items with no body don't panic when generating +// jump to def links. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/jump-to-def-ice.rs.html' + +pub trait A { + type T; + type U; +} + +impl A for () { + type T = Self::U; + type U = (); +} + +pub trait C { + type X; +} + +pub struct F(pub T::X); From b9f2bb7dd11f23c7922fdc024a270bf8a4675616 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 10 Aug 2025 16:43:36 +0900 Subject: [PATCH 0046/1900] internal: Make flycheck generational --- .../crates/rust-analyzer/src/diagnostics.rs | 47 ++++++-- .../crates/rust-analyzer/src/flycheck.rs | 112 ++++++++++++++---- .../crates/rust-analyzer/src/main_loop.rs | 24 +++- .../crates/rust-analyzer/src/reload.rs | 3 + 4 files changed, 148 insertions(+), 38 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 438a2a0ba1ea1..cd7c632d105e0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -26,6 +26,19 @@ pub struct DiagnosticsMapConfig { pub(crate) type DiagnosticsGeneration = usize; +#[derive(Debug, Clone)] +pub(crate) struct WorkspaceFlycheckDiagnostic { + pub(crate) generation: DiagnosticsGeneration, + pub(crate) per_package: + FxHashMap>, FxHashMap>>, +} + +impl WorkspaceFlycheckDiagnostic { + fn new(generation: DiagnosticsGeneration) -> Self { + WorkspaceFlycheckDiagnostic { generation, per_package: Default::default() } + } +} + #[derive(Debug, Default, Clone)] pub(crate) struct DiagnosticCollection { // FIXME: should be FxHashMap> @@ -33,9 +46,7 @@ pub(crate) struct DiagnosticCollection { FxHashMap)>, pub(crate) native_semantic: FxHashMap)>, - // FIXME: should be Vec - pub(crate) check: - Vec>, FxHashMap>>>, + pub(crate) check: Vec, pub(crate) check_fixes: CheckFixes, changes: FxHashSet, /// Counter for supplying a new generation number for diagnostics. @@ -57,7 +68,7 @@ impl DiagnosticCollection { let Some(check) = self.check.get_mut(flycheck_id) else { return; }; - self.changes.extend(check.drain().flat_map(|(_, v)| v.into_keys())); + self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.into_keys())); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.clear(); } @@ -66,7 +77,9 @@ impl DiagnosticCollection { pub(crate) fn clear_check_all(&mut self) { Arc::make_mut(&mut self.check_fixes).clear(); self.changes.extend( - self.check.iter_mut().flat_map(|it| it.drain().flat_map(|(_, v)| v.into_keys())), + self.check + .iter_mut() + .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.into_keys())), ) } @@ -79,7 +92,7 @@ impl DiagnosticCollection { return; }; let package_id = Some(package_id); - if let Some(checks) = check.remove(&package_id) { + if let Some(checks) = check.per_package.remove(&package_id) { self.changes.extend(checks.into_keys()); } if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { @@ -87,6 +100,16 @@ impl DiagnosticCollection { } } + pub(crate) fn clear_check_older_than( + &mut self, + flycheck_id: usize, + generation: DiagnosticsGeneration, + ) { + if self.check[flycheck_id].generation < generation { + self.clear_check(flycheck_id); + } + } + pub(crate) fn clear_native_for(&mut self, file_id: FileId) { self.native_syntax.remove(&file_id); self.native_semantic.remove(&file_id); @@ -96,15 +119,23 @@ impl DiagnosticCollection { pub(crate) fn add_check_diagnostic( &mut self, flycheck_id: usize, + generation: DiagnosticsGeneration, package_id: &Option>, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option>, ) { if self.check.len() <= flycheck_id { - self.check.resize_with(flycheck_id + 1, Default::default); + self.check + .resize_with(flycheck_id + 1, || WorkspaceFlycheckDiagnostic::new(generation)); + } + + // Getting message from old generation. Might happen in restarting checks. + if self.check[flycheck_id].generation > generation { + return; } let diagnostics = self.check[flycheck_id] + .per_package .entry(package_id.clone()) .or_default() .entry(file_id) @@ -177,7 +208,7 @@ impl DiagnosticCollection { let check = self .check .iter() - .flat_map(|it| it.values()) + .flat_map(|it| it.per_package.values()) .filter_map(move |it| it.get(&file_id)) .flatten(); native_syntax.chain(native_semantic).chain(check) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index e4e0bcdc1cd08..233bedec6900a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1,7 +1,12 @@ //! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -use std::{fmt, io, process::Command, time::Duration}; +use std::{ + fmt, io, + process::Command, + sync::atomic::{AtomicUsize, Ordering}, + time::Duration, +}; use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, select_biased, unbounded}; @@ -18,7 +23,10 @@ pub(crate) use cargo_metadata::diagnostic::{ use toolchain::Tool; use triomphe::Arc; -use crate::command::{CargoParser, CommandHandle}; +use crate::{ + command::{CargoParser, CommandHandle}, + diagnostics::DiagnosticsGeneration, +}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub(crate) enum InvocationStrategy { @@ -138,36 +146,54 @@ pub(crate) struct FlycheckHandle { sender: Sender, _thread: stdx::thread::JoinHandle, id: usize, + generation: AtomicUsize, } impl FlycheckHandle { pub(crate) fn spawn( id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, ) -> FlycheckHandle { - let actor = - FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); + let actor = FlycheckActor::new( + id, + generation, + sender, + config, + sysroot_root, + workspace_root, + manifest_path, + ); let (sender, receiver) = unbounded::(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, format!("Flycheck{id}")) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); - FlycheckHandle { id, sender, _thread: thread } + FlycheckHandle { id, generation: generation.into(), sender, _thread: thread } } /// Schedule a re-start of the cargo check worker to do a workspace wide check. pub(crate) fn restart_workspace(&self, saved_file: Option) { - self.sender.send(StateChange::Restart { package: None, saved_file, target: None }).unwrap(); + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; + self.sender + .send(StateChange::Restart { generation, package: None, saved_file, target: None }) + .unwrap(); } /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package(&self, package: String, target: Option) { + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender - .send(StateChange::Restart { package: Some(package), saved_file: None, target }) + .send(StateChange::Restart { + generation, + package: Some(package), + saved_file: None, + target, + }) .unwrap(); } @@ -179,23 +205,31 @@ impl FlycheckHandle { pub(crate) fn id(&self) -> usize { self.id } + + pub(crate) fn generation(&self) -> DiagnosticsGeneration { + self.generation.load(Ordering::Relaxed) + } +} + +#[derive(Debug)] +pub(crate) enum ClearDiagnosticsKind { + All, + OlderThan(DiagnosticsGeneration), + Package(Arc), } pub(crate) enum FlycheckMessage { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { id: usize, + generation: DiagnosticsGeneration, workspace_root: Arc, diagnostic: Diagnostic, package_id: Option>, }, /// Request clearing all outdated diagnostics. - ClearDiagnostics { - id: usize, - /// The package whose diagnostics to clear, or if unspecified, all diagnostics. - package_id: Option>, - }, + ClearDiagnostics { id: usize, kind: ClearDiagnosticsKind }, /// Request check progress notification to client Progress { @@ -208,18 +242,23 @@ pub(crate) enum FlycheckMessage { impl fmt::Debug for FlycheckMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => f + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => f .debug_struct("AddDiagnostic") .field("id", id) + .field("generation", generation) .field("workspace_root", workspace_root) .field("package_id", package_id) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .finish(), - FlycheckMessage::ClearDiagnostics { id, package_id } => f - .debug_struct("ClearDiagnostics") - .field("id", id) - .field("package_id", package_id) - .finish(), + FlycheckMessage::ClearDiagnostics { id, kind } => { + f.debug_struct("ClearDiagnostics").field("id", id).field("kind", kind).finish() + } FlycheckMessage::Progress { id, progress } => { f.debug_struct("Progress").field("id", id).field("progress", progress).finish() } @@ -237,7 +276,12 @@ pub(crate) enum Progress { } enum StateChange { - Restart { package: Option, saved_file: Option, target: Option }, + Restart { + generation: DiagnosticsGeneration, + package: Option, + saved_file: Option, + target: Option, + }, Cancel, } @@ -246,6 +290,7 @@ struct FlycheckActor { /// The workspace id of this flycheck instance. id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, manifest_path: Option, @@ -283,6 +328,7 @@ pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, sysroot_root: Option, @@ -292,6 +338,7 @@ impl FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, + generation, sender, config, sysroot_root, @@ -327,7 +374,12 @@ impl FlycheckActor { tracing::debug!(flycheck_id = self.id, "flycheck cancelled"); self.cancel_check_process(); } - Event::RequestStateChange(StateChange::Restart { package, saved_file, target }) => { + Event::RequestStateChange(StateChange::Restart { + generation, + package, + saved_file, + target, + }) => { // Cancel the previously spawned process self.cancel_check_process(); while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { @@ -337,6 +389,8 @@ impl FlycheckActor { } } + self.generation = generation; + let Some(command) = self.check_command(package.as_deref(), saved_file.as_deref(), target) else { @@ -383,7 +437,16 @@ impl FlycheckActor { // Clear everything for good measure self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, + }); + } else if res.is_ok() { + // We clear diagnostics for packages on + // `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where + // cargo may not report an artifact to our runner at all. To handle such + // cases, clear stale diagnostics when flycheck completes successfully. + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::OlderThan(self.generation), }); } self.clear_diagnostics_state(); @@ -412,7 +475,7 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id), + kind: ClearDiagnosticsKind::Package(package_id), }); } } @@ -435,7 +498,7 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id.clone()), + kind: ClearDiagnosticsKind::Package(package_id.clone()), }); } } else if self.diagnostics_received @@ -444,11 +507,12 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, }); } self.send(FlycheckMessage::AddDiagnostic { id: self.id, + generation: self.generation, package_id, workspace_root: self.root.clone(), diagnostic, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index f5932d8cff026..c6762f318326a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -20,7 +20,7 @@ use crate::{ config::Config, diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics}, discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, - flycheck::{self, FlycheckMessage}, + flycheck::{self, ClearDiagnosticsKind, FlycheckMessage}, global_state::{ FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, file_id_to_url, url_to_file_id, @@ -1008,7 +1008,13 @@ impl GlobalState { fn handle_flycheck_msg(&mut self, message: FlycheckMessage) { match message { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => { + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( &self.config.diagnostics_map(None), @@ -1020,6 +1026,7 @@ impl GlobalState { match url_to_file_id(&self.vfs.read().0, &diag.url) { Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic( id, + generation, &package_id, file_id, diag.diagnostic, @@ -1035,12 +1042,17 @@ impl GlobalState { }; } } - FlycheckMessage::ClearDiagnostics { id, package_id: None } => { + FlycheckMessage::ClearDiagnostics { id, kind: ClearDiagnosticsKind::All } => { self.diagnostics.clear_check(id) } - FlycheckMessage::ClearDiagnostics { id, package_id: Some(package_id) } => { - self.diagnostics.clear_check_for_package(id, package_id) - } + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::OlderThan(generation), + } => self.diagnostics.clear_check_older_than(id, generation), + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::Package(package_id), + } => self.diagnostics.clear_check_for_package(id, package_id), FlycheckMessage::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => (Progress::Begin, None), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index aa38aa72d44eb..f8f29eee126ed 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -855,11 +855,13 @@ impl GlobalState { invocation_strategy.clone() } }; + let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default(); self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => { vec![FlycheckHandle::spawn( 0, + next_gen, sender, config, None, @@ -898,6 +900,7 @@ impl GlobalState { .map(|(id, (root, manifest_path), sysroot_root)| { FlycheckHandle::spawn( id, + next_gen, sender.clone(), config.clone(), sysroot_root, From 25e767b0ee95d21b62d2fb509809946005081d40 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 10 Aug 2025 15:49:22 +0200 Subject: [PATCH 0047/1900] Ignore impl associated types in jump to def feature --- src/librustdoc/html/render/span_map.rs | 8 +++++++- tests/rustdoc/jump-to-def-ice-assoc-types.rs | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc/jump-to-def-ice-assoc-types.rs diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 6906e6e30ff86..e5bdc2a941c80 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -205,7 +205,13 @@ impl SpanMapVisitor<'_> { // panicking. fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { for (_, node) in tcx.hir_parent_iter(hir_id) { - if let Some((def_id, _)) = node.associated_body() { + // FIXME: associated type impl items don't have an associated body, so we don't handle + // them currently. + if let Node::ImplItem(impl_item) = node + && matches!(impl_item.kind, rustc_hir::ImplItemKind::Type(_)) + { + return None; + } else if let Some((def_id, _)) = node.associated_body() { return Some(def_id); } } diff --git a/tests/rustdoc/jump-to-def-ice-assoc-types.rs b/tests/rustdoc/jump-to-def-ice-assoc-types.rs new file mode 100644 index 0000000000000..9915c53668f0d --- /dev/null +++ b/tests/rustdoc/jump-to-def-ice-assoc-types.rs @@ -0,0 +1,20 @@ +// This test ensures that associated types don't crash rustdoc jump to def. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + + +#![crate_name = "foo"] + +//@ has 'src/foo/jump-to-def-ice-assoc-types.rs.html' + +pub trait Trait { + type Node; +} + +pub fn y() { + struct X(G); + + impl Trait for X { + type Node = G::Node; + } +} From f2c0c3dd44471e46ebb3823cc751c9c42aabc1aa Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Sun, 10 Aug 2025 11:56:35 -0700 Subject: [PATCH 0048/1900] Add testing for Arm64EC Windows --- library/stdarch/.github/workflows/main.yml | 10 +- .../core_arch/src/aarch64/neon/generated.rs | 248 ++++++++++++++ .../src/arm_shared/neon/generated.rs | 308 ++++++++++++++++++ .../core_arch/src/arm_shared/neon/mod.rs | 8 +- .../spec/neon/aarch64.spec.yml | 144 ++++++++ .../spec/neon/arm_shared.spec.yml | 111 +++++++ .../crates/stdarch-test/src/disassembly.rs | 6 +- 7 files changed, 823 insertions(+), 12 deletions(-) diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 048ce9864604e..b0d476f0e2ea5 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -117,6 +117,8 @@ jobs: os: windows-2025 - tuple: aarch64-pc-windows-msvc os: windows-11-arm + - tuple: arm64ec-pc-windows-msvc + os: windows-11-arm - tuple: x86_64-pc-windows-gnu os: windows-2025 # - tuple: i686-pc-windows-gnu @@ -207,14 +209,6 @@ jobs: rustup update nightly --no-self-update rustup default nightly shell: bash - if: matrix.target.os != 'windows-11-arm' - - name: Install Rust for `windows-11-arm` runners - # The arm runners don't have Rust pre-installed (https://github.com/actions/partner-runner-images/issues/77) - run: | - curl https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - shell: bash - if: matrix.target.os == 'windows-11-arm' - run: rustup target add ${{ matrix.target.tuple }} shell: bash diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index bc4c438038dc5..554a809db8db2 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -188,6 +188,7 @@ pub fn vabds_f32(a: f32, b: f32) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fabd))] pub fn vabdh_f16(a: f16, b: f16) -> f16 { unsafe { simd_extract!(vabd_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } @@ -947,6 +948,7 @@ pub fn vbcaxq_u64(a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcadd_rot270_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -964,6 +966,7 @@ pub fn vcadd_rot270_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcaddq_rot270_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -1029,6 +1032,7 @@ pub fn vcaddq_rot270_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcadd_rot90_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -1046,6 +1050,7 @@ pub fn vcadd_rot90_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcaddq_rot90_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -1175,6 +1180,7 @@ pub fn vcages_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facge))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcageh_f16(a: f16, b: f16) -> u16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -1255,6 +1261,7 @@ pub fn vcagts_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facgt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagth_f16(a: f16, b: f16) -> u16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -1307,6 +1314,7 @@ pub fn vcales_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facge))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaleh_f16(a: f16, b: f16) -> u16 { vcageh_f16(b, a) } @@ -1352,6 +1360,7 @@ pub fn vcalts_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facgt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcalth_f16(a: f16, b: f16) -> u16 { vcagth_f16(b, a) } @@ -1469,6 +1478,7 @@ pub fn vceqd_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vceq_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -1478,6 +1488,7 @@ pub fn vceqh_f16(a: f16, b: f16) -> u16 { #[cfg_attr(test, assert_instr(fcmeq))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_eq(a, transmute(b)) } @@ -1488,6 +1499,7 @@ pub fn vceqz_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcmeq))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_eq(a, transmute(b)) } @@ -1756,6 +1768,7 @@ pub fn vceqzd_u64(a: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vceqz_f16(vdup_n_f16(a)), 0) } } @@ -1873,6 +1886,7 @@ pub fn vcged_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgeh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcge_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2029,6 +2043,7 @@ pub fn vcgezd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgezh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcgez_f16(vdup_n_f16(a)), 0) } } @@ -2128,6 +2143,7 @@ pub fn vcgtd_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgth_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcgt_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2284,6 +2300,7 @@ pub fn vcgtzd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcgtz_f16(vdup_n_f16(a)), 0) } } @@ -2383,6 +2400,7 @@ pub fn vcled_s64(a: i64, b: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcleh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcle_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2539,6 +2557,7 @@ pub fn vclezd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclezh_f16(a: f16) -> u16 { unsafe { simd_extract!(vclez_f16(vdup_n_f16(a)), 0) } } @@ -2620,6 +2639,7 @@ pub fn vcltd_s64(a: i64, b: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclth_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vclt_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2794,6 +2814,7 @@ pub fn vcltzd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcltz_f16(vdup_n_f16(a)), 0) } } @@ -2803,6 +2824,7 @@ pub fn vcltzh_f16(a: f16) -> u16 { #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -2820,6 +2842,7 @@ pub fn vcmla_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -2887,6 +2910,7 @@ pub fn vcmlaq_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> float64x2_t #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_lane_f16( a: float16x4_t, b: float16x4_t, @@ -2915,6 +2939,7 @@ pub fn vcmla_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -2992,6 +3017,7 @@ pub fn vcmlaq_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3020,6 +3046,7 @@ pub fn vcmla_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3095,6 +3122,7 @@ pub fn vcmlaq_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot180_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3112,6 +3140,7 @@ pub fn vcmla_rot180_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot180_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3179,6 +3208,7 @@ pub fn vcmlaq_rot180_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> floa #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot180_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3207,6 +3237,7 @@ pub fn vcmla_rot180_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot180_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3284,6 +3315,7 @@ pub fn vcmlaq_rot180_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot180_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3312,6 +3344,7 @@ pub fn vcmla_rot180_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot180_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3387,6 +3420,7 @@ pub fn vcmlaq_rot180_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot270_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3404,6 +3438,7 @@ pub fn vcmla_rot270_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot270_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3471,6 +3506,7 @@ pub fn vcmlaq_rot270_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> floa #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot270_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3499,6 +3535,7 @@ pub fn vcmla_rot270_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot270_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3576,6 +3613,7 @@ pub fn vcmlaq_rot270_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot270_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3604,6 +3642,7 @@ pub fn vcmla_rot270_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot270_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3679,6 +3718,7 @@ pub fn vcmlaq_rot270_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot90_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3696,6 +3736,7 @@ pub fn vcmla_rot90_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float1 #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot90_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3763,6 +3804,7 @@ pub fn vcmlaq_rot90_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> float #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot90_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3791,6 +3833,7 @@ pub fn vcmla_rot90_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot90_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3868,6 +3911,7 @@ pub fn vcmlaq_rot90_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot90_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3896,6 +3940,7 @@ pub fn vcmla_rot90_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot90_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -7161,6 +7206,7 @@ pub fn vcvtq_f64_u64(a: uint64x2_t) -> float64x2_t { #[cfg_attr(test, assert_instr(fcvtn2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_high_f16_f32(a: float16x4_t, b: float32x4_t) -> float16x8_t { vcombine_f16(a, vcvt_f16_f32(b)) } @@ -7170,6 +7216,7 @@ pub fn vcvt_high_f16_f32(a: float16x4_t, b: float32x4_t) -> float16x8_t { #[cfg_attr(test, assert_instr(fcvtl2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_high_f32_f16(a: float16x8_t) -> float32x4_t { vcvt_f32_f16(vget_high_f16(a)) } @@ -7408,6 +7455,7 @@ pub fn vcvtq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvta_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7424,6 +7472,7 @@ pub fn vcvta_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtaq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7504,6 +7553,7 @@ pub fn vcvtaq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvta_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7520,6 +7570,7 @@ pub fn vcvta_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtaq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7600,6 +7651,7 @@ pub fn vcvtaq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s16_f16(a: f16) -> i16 { vcvtah_s32_f16(a) as i16 } @@ -7609,6 +7661,7 @@ pub fn vcvtah_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7625,6 +7678,7 @@ pub fn vcvtah_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7641,6 +7695,7 @@ pub fn vcvtah_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u16_f16(a: f16) -> u16 { vcvtah_u32_f16(a) as u16 } @@ -7650,6 +7705,7 @@ pub fn vcvtah_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7666,6 +7722,7 @@ pub fn vcvtah_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7764,6 +7821,7 @@ pub fn vcvts_f32_s32(a: i32) -> f32 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s16(a: i16) -> f16 { a as f16 } @@ -7773,6 +7831,7 @@ pub fn vcvth_f16_s16(a: i16) -> f16 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s32(a: i32) -> f16 { a as f16 } @@ -7782,6 +7841,7 @@ pub fn vcvth_f16_s32(a: i32) -> f16 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s64(a: i64) -> f16 { a as f16 } @@ -7791,6 +7851,7 @@ pub fn vcvth_f16_s64(a: i64) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u16(a: u16) -> f16 { a as f16 } @@ -7800,6 +7861,7 @@ pub fn vcvth_f16_u16(a: u16) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u32(a: u32) -> f16 { a as f16 } @@ -7809,6 +7871,7 @@ pub fn vcvth_f16_u32(a: u32) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u64(a: u64) -> f16 { a as f16 } @@ -7819,6 +7882,7 @@ pub fn vcvth_f16_u64(a: u64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s16(a: i16) -> f16 { static_assert!(N >= 1 && N <= 16); vcvth_n_f16_s32::(a as i32) @@ -7830,6 +7894,7 @@ pub fn vcvth_n_f16_s16(a: i16) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s32(a: i32) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7848,6 +7913,7 @@ pub fn vcvth_n_f16_s32(a: i32) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s64(a: i64) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7866,6 +7932,7 @@ pub fn vcvth_n_f16_s64(a: i64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u16(a: u16) -> f16 { static_assert!(N >= 1 && N <= 16); vcvth_n_f16_u32::(a as u32) @@ -7877,6 +7944,7 @@ pub fn vcvth_n_f16_u16(a: u16) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u32(a: u32) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7895,6 +7963,7 @@ pub fn vcvth_n_f16_u32(a: u32) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u64(a: u64) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7913,6 +7982,7 @@ pub fn vcvth_n_f16_u64(a: u64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s16_f16(a: f16) -> i16 { static_assert!(N >= 1 && N <= 16); vcvth_n_s32_f16::(a) as i16 @@ -7924,6 +7994,7 @@ pub fn vcvth_n_s16_f16(a: f16) -> i16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s32_f16(a: f16) -> i32 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7942,6 +8013,7 @@ pub fn vcvth_n_s32_f16(a: f16) -> i32 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s64_f16(a: f16) -> i64 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7960,6 +8032,7 @@ pub fn vcvth_n_s64_f16(a: f16) -> i64 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u16_f16(a: f16) -> u16 { static_assert!(N >= 1 && N <= 16); vcvth_n_u32_f16::(a) as u16 @@ -7971,6 +8044,7 @@ pub fn vcvth_n_u16_f16(a: f16) -> u16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u32_f16(a: f16) -> u32 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7989,6 +8063,7 @@ pub fn vcvth_n_u32_f16(a: f16) -> u32 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u64_f16(a: f16) -> u64 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8006,6 +8081,7 @@ pub fn vcvth_n_u64_f16(a: f16) -> u64 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s16_f16(a: f16) -> i16 { a as i16 } @@ -8015,6 +8091,7 @@ pub fn vcvth_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s32_f16(a: f16) -> i32 { a as i32 } @@ -8024,6 +8101,7 @@ pub fn vcvth_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s64_f16(a: f16) -> i64 { a as i64 } @@ -8033,6 +8111,7 @@ pub fn vcvth_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u16_f16(a: f16) -> u16 { a as u16 } @@ -8042,6 +8121,7 @@ pub fn vcvth_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u32_f16(a: f16) -> u32 { a as u32 } @@ -8051,6 +8131,7 @@ pub fn vcvth_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u64_f16(a: f16) -> u64 { a as u64 } @@ -8060,6 +8141,7 @@ pub fn vcvth_u64_f16(a: f16) -> u64 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtm_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8076,6 +8158,7 @@ pub fn vcvtm_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8156,6 +8239,7 @@ pub fn vcvtmq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtm_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8172,6 +8256,7 @@ pub fn vcvtm_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8252,6 +8337,7 @@ pub fn vcvtmq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s16_f16(a: f16) -> i16 { vcvtmh_s32_f16(a) as i16 } @@ -8261,6 +8347,7 @@ pub fn vcvtmh_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8277,6 +8364,7 @@ pub fn vcvtmh_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8293,6 +8381,7 @@ pub fn vcvtmh_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u16_f16(a: f16) -> u16 { vcvtmh_u32_f16(a) as u16 } @@ -8302,6 +8391,7 @@ pub fn vcvtmh_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8318,6 +8408,7 @@ pub fn vcvtmh_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8398,6 +8489,7 @@ pub fn vcvtmd_u64_f64(a: f64) -> u64 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtn_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8414,6 +8506,7 @@ pub fn vcvtn_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8494,6 +8587,7 @@ pub fn vcvtnq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtn_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8510,6 +8604,7 @@ pub fn vcvtn_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8590,6 +8685,7 @@ pub fn vcvtnq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s16_f16(a: f16) -> i16 { vcvtnh_s32_f16(a) as i16 } @@ -8599,6 +8695,7 @@ pub fn vcvtnh_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8615,6 +8712,7 @@ pub fn vcvtnh_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8631,6 +8729,7 @@ pub fn vcvtnh_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u16_f16(a: f16) -> u16 { vcvtnh_u32_f16(a) as u16 } @@ -8640,6 +8739,7 @@ pub fn vcvtnh_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8656,6 +8756,7 @@ pub fn vcvtnh_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8736,6 +8837,7 @@ pub fn vcvtnd_u64_f64(a: f64) -> u64 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtp_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8752,6 +8854,7 @@ pub fn vcvtp_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtpq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8832,6 +8935,7 @@ pub fn vcvtpq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtp_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8848,6 +8952,7 @@ pub fn vcvtp_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtpq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8928,6 +9033,7 @@ pub fn vcvtpq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s16_f16(a: f16) -> i16 { vcvtph_s32_f16(a) as i16 } @@ -8937,6 +9043,7 @@ pub fn vcvtph_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8953,6 +9060,7 @@ pub fn vcvtph_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8969,6 +9077,7 @@ pub fn vcvtph_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u16_f16(a: f16) -> u16 { vcvtph_u32_f16(a) as u16 } @@ -8978,6 +9087,7 @@ pub fn vcvtph_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8994,6 +9104,7 @@ pub fn vcvtph_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -9305,6 +9416,7 @@ pub fn vcvtxd_f32_f64(a: f64) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fdiv))] pub fn vdiv_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_div(a, b) } @@ -9314,6 +9426,7 @@ pub fn vdiv_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fdiv))] pub fn vdivq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_div(a, b) } @@ -9359,6 +9472,7 @@ pub fn vdivq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vdivh_f16(a: f16, b: f16) -> f16 { a / b @@ -9608,6 +9722,7 @@ pub fn vdupd_lane_u64(a: uint64x1_t) -> u64 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vduph_lane_f16(a: float16x4_t) -> f16 { static_assert_uimm_bits!(N, 2); unsafe { simd_extract!(a, N as u32) } @@ -9619,6 +9734,7 @@ pub fn vduph_lane_f16(a: float16x4_t) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vduph_laneq_f16(a: float16x8_t) -> f16 { static_assert_uimm_bits!(N, 4); unsafe { simd_extract!(a, N as u32) } @@ -9977,6 +10093,7 @@ pub fn vfma_f64(a: float64x1_t, b: float64x1_t, c: float64x1_t) -> float64x1_t { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_lane_f16( a: float16x4_t, b: float16x4_t, @@ -9992,6 +10109,7 @@ pub fn vfma_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -10007,6 +10125,7 @@ pub fn vfma_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -10022,6 +10141,7 @@ pub fn vfmaq_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -10140,6 +10260,7 @@ pub fn vfma_laneq_f64( #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmla))] pub fn vfma_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { vfma_f16(a, b, vdup_n_f16(c)) @@ -10149,6 +10270,7 @@ pub fn vfma_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmla))] pub fn vfmaq_n_f16(a: float16x8_t, b: float16x8_t, c: f16) -> float16x8_t { vfmaq_f16(a, b, vdupq_n_f16(c)) @@ -10182,6 +10304,7 @@ pub fn vfmad_lane_f64(a: f64, b: f64, c: float64x1_t) -> f64 { #[cfg_attr(test, assert_instr(fmadd))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_f16(a: f16, b: f16, c: f16) -> f16 { unsafe { fmaf16(b, c, a) } } @@ -10192,6 +10315,7 @@ pub fn vfmah_f16(a: f16, b: f16, c: f16) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -10206,6 +10330,7 @@ pub fn vfmah_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_laneq_f16(a: f16, b: f16, v: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -10294,6 +10419,7 @@ pub fn vfmad_laneq_f64(a: f64, b: f64, c: float64x2_t) -> f64 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal2))] pub fn vfmlal_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10311,6 +10437,7 @@ pub fn vfmlal_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal2))] pub fn vfmlalq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10330,6 +10457,7 @@ pub fn vfmlalq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_lane_high_f16( r: float32x2_t, a: float16x4_t, @@ -10346,6 +10474,7 @@ pub fn vfmlal_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_laneq_high_f16( r: float32x2_t, a: float16x4_t, @@ -10362,6 +10491,7 @@ pub fn vfmlal_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_lane_high_f16( r: float32x4_t, a: float16x8_t, @@ -10378,6 +10508,7 @@ pub fn vfmlalq_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_laneq_high_f16( r: float32x4_t, a: float16x8_t, @@ -10394,6 +10525,7 @@ pub fn vfmlalq_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_lane_low_f16( r: float32x2_t, a: float16x4_t, @@ -10410,6 +10542,7 @@ pub fn vfmlal_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_laneq_low_f16( r: float32x2_t, a: float16x4_t, @@ -10426,6 +10559,7 @@ pub fn vfmlal_laneq_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_lane_low_f16( r: float32x4_t, a: float16x8_t, @@ -10442,6 +10576,7 @@ pub fn vfmlalq_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_laneq_low_f16( r: float32x4_t, a: float16x8_t, @@ -10456,6 +10591,7 @@ pub fn vfmlalq_laneq_low_f16( #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal))] pub fn vfmlal_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10473,6 +10609,7 @@ pub fn vfmlal_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal))] pub fn vfmlalq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10490,6 +10627,7 @@ pub fn vfmlalq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl2))] pub fn vfmlsl_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10507,6 +10645,7 @@ pub fn vfmlsl_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl2))] pub fn vfmlslq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10526,6 +10665,7 @@ pub fn vfmlslq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_lane_high_f16( r: float32x2_t, a: float16x4_t, @@ -10542,6 +10682,7 @@ pub fn vfmlsl_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_laneq_high_f16( r: float32x2_t, a: float16x4_t, @@ -10558,6 +10699,7 @@ pub fn vfmlsl_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_lane_high_f16( r: float32x4_t, a: float16x8_t, @@ -10574,6 +10716,7 @@ pub fn vfmlslq_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_laneq_high_f16( r: float32x4_t, a: float16x8_t, @@ -10590,6 +10733,7 @@ pub fn vfmlslq_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_lane_low_f16( r: float32x2_t, a: float16x4_t, @@ -10606,6 +10750,7 @@ pub fn vfmlsl_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_laneq_low_f16( r: float32x2_t, a: float16x4_t, @@ -10622,6 +10767,7 @@ pub fn vfmlsl_laneq_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_lane_low_f16( r: float32x4_t, a: float16x8_t, @@ -10638,6 +10784,7 @@ pub fn vfmlslq_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_laneq_low_f16( r: float32x4_t, a: float16x8_t, @@ -10652,6 +10799,7 @@ pub fn vfmlslq_laneq_low_f16( #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl))] pub fn vfmlsl_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10669,6 +10817,7 @@ pub fn vfmlsl_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl))] pub fn vfmlslq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10699,6 +10848,7 @@ pub fn vfms_f64(a: float64x1_t, b: float64x1_t, c: float64x1_t) -> float64x1_t { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_lane_f16( a: float16x4_t, b: float16x4_t, @@ -10714,6 +10864,7 @@ pub fn vfms_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -10729,6 +10880,7 @@ pub fn vfms_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -10744,6 +10896,7 @@ pub fn vfmsq_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -10862,6 +11015,7 @@ pub fn vfms_laneq_f64( #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmls))] pub fn vfms_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { vfms_f16(a, b, vdup_n_f16(c)) @@ -10871,6 +11025,7 @@ pub fn vfms_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmls))] pub fn vfmsq_n_f16(a: float16x8_t, b: float16x8_t, c: f16) -> float16x8_t { vfmsq_f16(a, b, vdupq_n_f16(c)) @@ -10890,6 +11045,7 @@ pub fn vfms_n_f64(a: float64x1_t, b: float64x1_t, c: f64) -> float64x1_t { #[cfg_attr(test, assert_instr(fmsub))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_f16(a: f16, b: f16, c: f16) -> f16 { vfmah_f16(a, -b, c) } @@ -10900,6 +11056,7 @@ pub fn vfmsh_f16(a: f16, b: f16, c: f16) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -10914,6 +11071,7 @@ pub fn vfmsh_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_laneq_f16(a: f16, b: f16, v: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -11005,6 +11163,7 @@ pub fn vfmsd_laneq_f64(a: f64, b: f64, c: float64x2_t) -> f64 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(ldr))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { crate::ptr::read_unaligned(ptr.cast()) } @@ -11016,6 +11175,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(ldr))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { crate::ptr::read_unaligned(ptr.cast()) } @@ -13107,6 +13267,7 @@ pub fn vmaxq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmax))] pub fn vmaxh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -13141,6 +13302,7 @@ pub fn vmaxnmq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnm))] pub fn vmaxnmh_f16(a: f16, b: f16) -> f16 { f16::max(a, b) @@ -13150,6 +13312,7 @@ pub fn vmaxnmh_f16(a: f16, b: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmv))] pub fn vmaxnmv_f16(a: float16x4_t) -> f16 { unsafe { simd_reduce_max(a) } @@ -13159,6 +13322,7 @@ pub fn vmaxnmv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmv))] pub fn vmaxnmvq_f16(a: float16x8_t) -> f16 { unsafe { simd_reduce_max(a) } @@ -13195,6 +13359,7 @@ pub fn vmaxnmvq_f32(a: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxv))] pub fn vmaxv_f16(a: float16x4_t) -> f16 { unsafe extern "unadjusted" { @@ -13211,6 +13376,7 @@ pub fn vmaxv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxv))] pub fn vmaxvq_f16(a: float16x8_t) -> f16 { unsafe extern "unadjusted" { @@ -13415,6 +13581,7 @@ pub fn vminq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmin))] pub fn vminh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -13449,6 +13616,7 @@ pub fn vminnmq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnm))] pub fn vminnmh_f16(a: f16, b: f16) -> f16 { f16::min(a, b) @@ -13458,6 +13626,7 @@ pub fn vminnmh_f16(a: f16, b: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmv))] pub fn vminnmv_f16(a: float16x4_t) -> f16 { unsafe { simd_reduce_min(a) } @@ -13467,6 +13636,7 @@ pub fn vminnmv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmv))] pub fn vminnmvq_f16(a: float16x8_t) -> f16 { unsafe { simd_reduce_min(a) } @@ -13503,6 +13673,7 @@ pub fn vminnmvq_f32(a: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminv))] pub fn vminv_f16(a: float16x4_t) -> f16 { unsafe extern "unadjusted" { @@ -13519,6 +13690,7 @@ pub fn vminv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminv))] pub fn vminvq_f16(a: float16x8_t) -> f16 { unsafe extern "unadjusted" { @@ -14554,6 +14726,7 @@ pub fn vmul_lane_f64(a: float64x1_t, b: float64x1_t) -> float64 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_laneq_f16(a: float16x4_t, b: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -14570,6 +14743,7 @@ pub fn vmul_laneq_f16(a: float16x4_t, b: float16x8_t) -> float1 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_laneq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -14640,6 +14814,7 @@ pub fn vmuld_lane_f64(a: f64, b: float64x1_t) -> f64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vmulh_f16(a: f16, b: f16) -> f16 { a * b @@ -14651,6 +14826,7 @@ pub fn vmulh_f16(a: f16, b: f16) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulh_lane_f16(a: f16, b: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -14665,6 +14841,7 @@ pub fn vmulh_lane_f16(a: f16, b: float16x4_t) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulh_laneq_f16(a: f16, b: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15073,6 +15250,7 @@ pub fn vmuld_laneq_f64(a: f64, b: float64x2_t) -> f64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulx_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15089,6 +15267,7 @@ pub fn vmulx_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15171,6 +15350,7 @@ pub fn vmulxq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_lane_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -15187,6 +15367,7 @@ pub fn vmulx_lane_f16(a: float16x4_t, b: float16x4_t) -> float1 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_laneq_f16(a: float16x4_t, b: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15203,6 +15384,7 @@ pub fn vmulx_laneq_f16(a: float16x4_t, b: float16x8_t) -> float #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_lane_f16(a: float16x8_t, b: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -15232,6 +15414,7 @@ pub fn vmulxq_lane_f16(a: float16x8_t, b: float16x4_t) -> float #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_laneq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15347,6 +15530,7 @@ pub fn vmulx_laneq_f64(a: float64x1_t, b: float64x2_t) -> float #[cfg_attr(test, assert_instr(fmulx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_n_f16(a: float16x4_t, b: f16) -> float16x4_t { vmulx_f16(a, vdup_n_f16(b)) } @@ -15356,6 +15540,7 @@ pub fn vmulx_n_f16(a: float16x4_t, b: f16) -> float16x4_t { #[cfg_attr(test, assert_instr(fmulx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_n_f16(a: float16x8_t, b: f16) -> float16x8_t { vmulxq_f16(a, vdupq_n_f16(b)) } @@ -15440,6 +15625,7 @@ pub fn vmulxs_laneq_f32(a: f32, b: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulxh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -15458,6 +15644,7 @@ pub fn vmulxh_f16(a: f16, b: f16) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxh_lane_f16(a: f16, b: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { vmulxh_f16(a, simd_extract!(b, LANE as u32)) } @@ -15469,6 +15656,7 @@ pub fn vmulxh_lane_f16(a: f16, b: float16x4_t) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxh_laneq_f16(a: f16, b: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { vmulxh_f16(a, simd_extract!(b, LANE as u32)) } @@ -15534,6 +15722,7 @@ pub fn vnegd_s64(a: i64) -> i64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fneg))] pub fn vnegh_f16(a: f16) -> f16 { -a @@ -15587,6 +15776,7 @@ pub fn vpaddd_u64(a: uint64x2_t) -> u64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(faddp))] pub fn vpaddq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15805,6 +15995,7 @@ pub fn vpaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxp))] pub fn vpmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15821,6 +16012,7 @@ pub fn vpmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxp))] pub fn vpmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15837,6 +16029,7 @@ pub fn vpmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmp))] pub fn vpmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15853,6 +16046,7 @@ pub fn vpmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmp))] pub fn vpmaxnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -16109,6 +16303,7 @@ pub fn vpmaxs_f32(a: float32x2_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminp))] pub fn vpmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -16125,6 +16320,7 @@ pub fn vpmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminp))] pub fn vpminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -16141,6 +16337,7 @@ pub fn vpminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmp))] pub fn vpminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -16157,6 +16354,7 @@ pub fn vpminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmp))] pub fn vpminnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -21135,6 +21333,7 @@ pub fn vrecpes_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frecpe))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpeh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21215,6 +21414,7 @@ pub fn vrecpss_f32(a: f32, b: f32) -> f32 { #[cfg_attr(test, assert_instr(frecps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpsh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21263,6 +21463,7 @@ pub fn vrecpxs_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frecpx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpxh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21279,6 +21480,7 @@ pub fn vrecpxh_f16(a: f16) -> f16 { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { unsafe { transmute(a) } @@ -21289,6 +21491,7 @@ pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; @@ -21300,6 +21503,7 @@ pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { unsafe { transmute(a) } @@ -21310,6 +21514,7 @@ pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -21324,6 +21529,7 @@ pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { transmute(a) } @@ -21334,6 +21540,7 @@ pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { @@ -21347,6 +21554,7 @@ pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { unsafe { transmute(a) } @@ -21357,6 +21565,7 @@ pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; @@ -22935,6 +23144,7 @@ pub fn vrnd64z_f64(a: float64x1_t) -> float64x1_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrnd_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_trunc(a) } @@ -22944,6 +23154,7 @@ pub fn vrnd_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrndq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_trunc(a) } @@ -22989,6 +23200,7 @@ pub fn vrndq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrnda_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_round(a) } @@ -22998,6 +23210,7 @@ pub fn vrnda_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrndaq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_round(a) } @@ -23043,6 +23256,7 @@ pub fn vrndaq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrndah_f16(a: f16) -> f16 { unsafe { roundf16(a) } @@ -23052,6 +23266,7 @@ pub fn vrndah_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrndh_f16(a: f16) -> f16 { unsafe { truncf16(a) } @@ -23061,6 +23276,7 @@ pub fn vrndh_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndi_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -23077,6 +23293,7 @@ pub fn vrndi_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndiq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -23157,6 +23374,7 @@ pub fn vrndiq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndih_f16(a: f16) -> f16 { unsafe extern "unadjusted" { @@ -23173,6 +23391,7 @@ pub fn vrndih_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndm_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_floor(a) } @@ -23182,6 +23401,7 @@ pub fn vrndm_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndmq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_floor(a) } @@ -23227,6 +23447,7 @@ pub fn vrndmq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndmh_f16(a: f16) -> f16 { unsafe { floorf16(a) } @@ -23268,6 +23489,7 @@ pub fn vrndnq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintn))] pub fn vrndnh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { @@ -23300,6 +23522,7 @@ pub fn vrndns_f32(a: f32) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndp_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_ceil(a) } @@ -23309,6 +23532,7 @@ pub fn vrndp_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndpq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_ceil(a) } @@ -23354,6 +23578,7 @@ pub fn vrndpq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndph_f16(a: f16) -> f16 { unsafe { ceilf16(a) } @@ -23363,6 +23588,7 @@ pub fn vrndph_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndx_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_round_ties_even(a) } @@ -23372,6 +23598,7 @@ pub fn vrndx_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndxq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_round_ties_even(a) } @@ -23417,6 +23644,7 @@ pub fn vrndxq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndxh_f16(a: f16) -> f16 { round_ties_even_f16(a) @@ -23623,6 +23851,7 @@ pub fn vrsqrtes_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frsqrte))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrteh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -23703,6 +23932,7 @@ pub fn vrsqrtss_f32(a: f32, b: f32) -> f32 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(frsqrts))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrtsh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -24791,6 +25021,7 @@ pub fn vsqadds_u32(a: u32, b: i32) -> u32 { #[cfg_attr(test, assert_instr(fsqrt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsqrt_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_fsqrt(a) } } @@ -24800,6 +25031,7 @@ pub fn vsqrt_f16(a: float16x4_t) -> float16x4_t { #[cfg_attr(test, assert_instr(fsqrt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsqrtq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_fsqrt(a) } } @@ -24844,6 +25076,7 @@ pub fn vsqrtq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fsqrt))] pub fn vsqrth_f16(a: f16) -> f16 { unsafe { sqrtf16(a) } @@ -25177,6 +25410,7 @@ pub fn vsrid_n_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(str))] #[allow(clippy::cast_ptr_alignment)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { crate::ptr::write_unaligned(ptr.cast(), a) } @@ -25189,6 +25423,7 @@ pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { #[cfg_attr(test, assert_instr(str))] #[allow(clippy::cast_ptr_alignment)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { crate::ptr::write_unaligned(ptr.cast(), a) } @@ -26488,6 +26723,7 @@ pub fn vsubd_u64(a: u64, b: u64) -> u64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vsubh_f16(a: f16, b: f16) -> f16 { a - b @@ -27283,6 +27519,7 @@ pub fn vtbx4_p8(a: poly8x8_t, b: poly8x8x4_t, c: uint8x8_t) -> poly8x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn1))] pub fn vtrn1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 4, 2, 6]) } @@ -27292,6 +27529,7 @@ pub fn vtrn1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn1))] pub fn vtrn1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 8, 2, 10, 4, 12, 6, 14]) } @@ -27517,6 +27755,7 @@ pub fn vtrn1q_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn2))] pub fn vtrn2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [1, 5, 3, 7]) } @@ -27526,6 +27765,7 @@ pub fn vtrn2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn2))] pub fn vtrn2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [1, 9, 3, 11, 5, 13, 7, 15]) } @@ -28056,6 +28296,7 @@ pub fn vusdotq_laneq_s32(a: int32x4_t, b: uint8x16_t, c: int8x1 #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp1))] pub fn vuzp1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 2, 4, 6]) } @@ -28065,6 +28306,7 @@ pub fn vuzp1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp1))] pub fn vuzp1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 2, 4, 6, 8, 10, 12, 14]) } @@ -28290,6 +28532,7 @@ pub fn vuzp1q_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp2))] pub fn vuzp2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [1, 3, 5, 7]) } @@ -28299,6 +28542,7 @@ pub fn vuzp2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp2))] pub fn vuzp2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [1, 3, 5, 7, 9, 11, 13, 15]) } @@ -28542,6 +28786,7 @@ pub fn vxarq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip1))] pub fn vzip1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 4, 1, 5]) } @@ -28551,6 +28796,7 @@ pub fn vzip1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip1))] pub fn vzip1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 8, 1, 9, 2, 10, 3, 11]) } @@ -28776,6 +29022,7 @@ pub fn vzip1q_p64(a: poly64x2_t, b: poly64x2_t) -> poly64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip2))] pub fn vzip2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [2, 6, 3, 7]) } @@ -28785,6 +29032,7 @@ pub fn vzip2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip2))] pub fn vzip2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [4, 12, 5, 13, 6, 14, 7, 15]) } diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index 32531c7da1356..fd150bcaf2b7f 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -820,6 +820,7 @@ pub fn vabaq_u8(a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vabds.v4f16")] @@ -842,6 +843,7 @@ pub fn vabd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabdq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vabds.v8f16")] @@ -1405,6 +1407,7 @@ pub fn vabdl_u32(a: uint32x2_t, b: uint32x2_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabs_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_fabs(a) } } @@ -1419,6 +1422,7 @@ pub fn vabs_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabsq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_fabs(a) } } @@ -1625,6 +1629,7 @@ pub fn vabsq_s32(a: int32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabsh_f16(a: f16) -> f16 { unsafe { simd_extract!(vabs_f16(vdup_n_f16(a)), 0) } } @@ -1639,6 +1644,7 @@ pub fn vabsh_f16(a: f16) -> f16 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_add(a, b) } } @@ -1653,6 +1659,7 @@ pub fn vadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vaddq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_add(a, b) } } @@ -2129,6 +2136,7 @@ pub fn vaddq_p64(a: poly64x2_t, b: poly64x2_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vaddh_f16(a: f16, b: f16) -> f16 { a + b } @@ -3828,6 +3836,7 @@ pub fn vbicq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { assert_instr(bsl) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vbsl_f16(a: uint16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { let not = int16x4_t::splat(-1); unsafe { @@ -3848,6 +3857,7 @@ pub fn vbsl_f16(a: uint16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { assert_instr(bsl) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vbslq_f16(a: uint16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { let not = int16x8_t::splat(-1); unsafe { @@ -4462,6 +4472,7 @@ pub fn vbslq_u8(a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcage_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacge.v4i16.v4f16")] @@ -4484,6 +4495,7 @@ pub fn vcage_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcageq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacge.v8i16.v8f16")] @@ -4564,6 +4576,7 @@ pub fn vcageq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacgt.v4i16.v4f16")] @@ -4586,6 +4599,7 @@ pub fn vcagt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagtq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacgt.v8i16.v8f16")] @@ -4666,6 +4680,7 @@ pub fn vcagtq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcale_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { vcage_f16(b, a) } @@ -4680,6 +4695,7 @@ pub fn vcale_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaleq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { vcageq_f16(b, a) } @@ -4736,6 +4752,7 @@ pub fn vcaleq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcalt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { vcagt_f16(b, a) } @@ -4750,6 +4767,7 @@ pub fn vcalt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaltq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { vcagtq_f16(b, a) } @@ -4806,6 +4824,7 @@ pub fn vcaltq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceq_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_eq(a, b) } } @@ -4820,6 +4839,7 @@ pub fn vceq_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_eq(a, b) } } @@ -5170,6 +5190,7 @@ pub fn vceqq_p8(a: poly8x16_t, b: poly8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcge_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_ge(a, b) } } @@ -5184,6 +5205,7 @@ pub fn vcge_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgeq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_ge(a, b) } } @@ -5492,6 +5514,7 @@ pub fn vcgeq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgez_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_ge(a, transmute(b)) } @@ -5507,6 +5530,7 @@ pub fn vcgez_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgezq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_ge(a, transmute(b)) } @@ -5522,6 +5546,7 @@ pub fn vcgezq_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_gt(a, b) } } @@ -5536,6 +5561,7 @@ pub fn vcgt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_gt(a, b) } } @@ -5844,6 +5870,7 @@ pub fn vcgtq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_gt(a, transmute(b)) } @@ -5859,6 +5886,7 @@ pub fn vcgtz_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_gt(a, transmute(b)) } @@ -5874,6 +5902,7 @@ pub fn vcgtzq_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcle_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_le(a, b) } } @@ -5888,6 +5917,7 @@ pub fn vcle_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcleq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_le(a, b) } } @@ -6196,6 +6226,7 @@ pub fn vcleq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclez_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_le(a, transmute(b)) } @@ -6211,6 +6242,7 @@ pub fn vclez_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclezq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_le(a, transmute(b)) } @@ -6526,6 +6558,7 @@ pub fn vclsq_u32(a: uint32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_lt(a, b) } } @@ -6540,6 +6573,7 @@ pub fn vclt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_lt(a, b) } } @@ -6848,6 +6882,7 @@ pub fn vcltq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_lt(a, transmute(b)) } @@ -6863,6 +6898,7 @@ pub fn vcltz_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_lt(a, transmute(b)) } @@ -7536,6 +7572,7 @@ pub fn vcntq_p8(a: poly8x16_t) -> poly8x16_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vcombine_f16(a: float16x4_t, b: float16x4_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 1, 2, 3, 4, 5, 6, 7]) } @@ -7756,6 +7793,7 @@ pub fn vcombine_p64(a: poly64x1_t, b: poly64x1_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcreate_f16(a: u64) -> float16x4_t { unsafe { transmute(a) } } @@ -7771,6 +7809,7 @@ pub fn vcreate_f16(a: u64) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcreate_f16(a: u64) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -8274,6 +8313,7 @@ pub fn vcreate_p64(a: u64) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_f32(a: float32x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8288,6 +8328,7 @@ pub fn vcvt_f16_f32(a: float32x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8302,6 +8343,7 @@ pub fn vcvt_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_f16_s16(a: int16x8_t) -> float16x8_t { unsafe { simd_cast(a) } } @@ -8316,6 +8358,7 @@ pub fn vcvtq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8330,6 +8373,7 @@ pub fn vcvt_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { simd_cast(a) } } @@ -8344,6 +8388,7 @@ pub fn vcvtq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f32_f16(a: float16x4_t) -> float32x4_t { unsafe { simd_cast(a) } } @@ -8443,6 +8488,7 @@ pub fn vcvtq_f32_u32(a: uint32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_f16_s16(a: int16x4_t) -> float16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8470,6 +8516,7 @@ pub fn vcvt_n_f16_s16(a: int16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_f16_s16(a: int16x8_t) -> float16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8497,6 +8544,7 @@ pub fn vcvtq_n_f16_s16(a: int16x8_t) -> float16x8_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_f16_u16(a: uint16x4_t) -> float16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8524,6 +8572,7 @@ pub fn vcvt_n_f16_u16(a: uint16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_f16_u16(a: uint16x8_t) -> float16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8703,6 +8752,7 @@ pub fn vcvtq_n_f32_u32(a: uint32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_s16_f16(a: float16x4_t) -> int16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8730,6 +8780,7 @@ pub fn vcvt_n_s16_f16(a: float16x4_t) -> int16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_s16_f16(a: float16x8_t) -> int16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8833,6 +8884,7 @@ pub fn vcvtq_n_s32_f32(a: float32x4_t) -> int32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_u16_f16(a: float16x4_t) -> uint16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8860,6 +8912,7 @@ pub fn vcvt_n_u16_f16(a: float16x4_t) -> uint16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_u16_f16(a: float16x8_t) -> uint16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8962,6 +9015,7 @@ pub fn vcvtq_n_u32_f32(a: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { simd_cast(a) } } @@ -8976,6 +9030,7 @@ pub fn vcvt_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe { simd_cast(a) } } @@ -9048,6 +9103,7 @@ pub fn vcvtq_s32_f32(a: float32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { simd_cast(a) } } @@ -9062,6 +9118,7 @@ pub fn vcvt_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { simd_cast(a) } } @@ -9361,6 +9418,7 @@ pub fn vdotq_u32(a: uint32x4_t, b: uint8x16_t, c: uint8x16_t) -> uint32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_lane_f16(a: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(N, 2); unsafe { simd_shuffle!(a, a, [N as u32, N as u32, N as u32, N as u32]) } @@ -9377,6 +9435,7 @@ pub fn vdup_lane_f16(a: float16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_lane_f16(a: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(N, 2); unsafe { @@ -9922,6 +9981,7 @@ pub fn vdup_lane_u64(a: uint64x1_t) -> uint64x1_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_laneq_f16(a: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(N, 3); unsafe { simd_shuffle!(a, a, [N as u32, N as u32, N as u32, N as u32]) } @@ -9938,6 +9998,7 @@ pub fn vdup_laneq_f16(a: float16x8_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_laneq_f16(a: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(N, 3); unsafe { @@ -10482,6 +10543,7 @@ pub fn vdup_laneq_u64(a: uint64x2_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_n_f16(a: f16) -> float16x4_t { float16x4_t::splat(a) } @@ -10496,6 +10558,7 @@ pub fn vdup_n_f16(a: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_n_f16(a: f16) -> float16x8_t { float16x8_t::splat(a) } @@ -11443,6 +11506,7 @@ pub fn veorq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vext_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(N, 2); unsafe { @@ -11814,6 +11878,7 @@ pub fn vextq_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vextq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(N, 3); unsafe { @@ -12394,6 +12459,7 @@ pub fn vextq_p8(a: poly8x16_t, b: poly8x16_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe { simd_fma(b, c, a) } } @@ -12408,6 +12474,7 @@ pub fn vfma_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe { simd_fma(b, c, a) } } @@ -12507,6 +12574,7 @@ pub fn vfmaq_n_f32(a: float32x4_t, b: float32x4_t, c: f32) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe { let b: float16x4_t = simd_neg(b); @@ -12525,6 +12593,7 @@ pub fn vfms_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe { let b: float16x8_t = simd_neg(b); @@ -12627,6 +12696,7 @@ pub fn vfmsq_n_f32(a: float32x4_t, b: float32x4_t, c: f32) -> float32x4_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vget_high_f16(a: float16x8_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [4, 5, 6, 7]) } @@ -12637,6 +12707,7 @@ pub fn vget_high_f16(a: float16x8_t) -> float16x4_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vget_low_f16(a: float16x8_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [0, 1, 2, 3]) } @@ -12884,6 +12955,7 @@ pub fn vget_high_u64(a: uint64x2_t) -> uint64x1_t { )] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vget_lane_f16(a: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { simd_extract!(a, LANE as u32) } @@ -12900,6 +12972,7 @@ pub fn vget_lane_f16(a: float16x4_t) -> f16 { )] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vgetq_lane_f16(a: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { simd_extract!(a, LANE as u32) } @@ -14256,6 +14329,7 @@ pub fn vhsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_dup_f16(ptr: *const f16) -> float16x4_t { let x: float16x4_t = vld1_lane_f16::<0>(ptr, transmute(f16x4::splat(0.0))); simd_shuffle!(x, x, [0, 0, 0, 0]) @@ -14273,6 +14347,7 @@ pub unsafe fn vld1_dup_f16(ptr: *const f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_dup_f16(ptr: *const f16) -> float16x8_t { let x: float16x8_t = vld1q_lane_f16::<0>(ptr, transmute(f16x8::splat(0.0))); simd_shuffle!(x, x, [0, 0, 0, 0, 0, 0, 0, 0]) @@ -14843,6 +14918,7 @@ pub unsafe fn vld1_dup_u64(ptr: *const u64) -> uint64x1_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { transmute(vld1_v4f16( @@ -14860,6 +14936,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { let ret_val: float16x4_t = transmute(vld1_v4f16( @@ -14878,6 +14955,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { transmute(vld1q_v8f16( @@ -14895,6 +14973,7 @@ pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { let ret_val: float16x8_t = transmute(vld1q_v8f16( @@ -14916,6 +14995,7 @@ pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x2(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14940,6 +15020,7 @@ pub unsafe fn vld1_f16_x2(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x3(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14964,6 +15045,7 @@ pub unsafe fn vld1_f16_x3(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x4(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14988,6 +15070,7 @@ pub unsafe fn vld1_f16_x4(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x2(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15012,6 +15095,7 @@ pub unsafe fn vld1q_f16_x2(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x3(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15036,6 +15120,7 @@ pub unsafe fn vld1q_f16_x3(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x4(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15732,6 +15817,7 @@ pub unsafe fn vld1q_f32_x4(a: *const f32) -> float32x4x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); simd_insert!(src, LANE as u32, *ptr) @@ -15750,6 +15836,7 @@ pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_lane_f16(ptr: *const f16, src: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); simd_insert!(src, LANE as u32, *ptr) @@ -19490,6 +19577,7 @@ unsafe fn vld1q_v8i16(a: *const i8, b: i32) -> int16x8_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] unsafe fn vld1_v4f16(a: *const i8, b: i32) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld1.v4f16")] @@ -19507,6 +19595,7 @@ unsafe fn vld1_v4f16(a: *const i8, b: i32) -> float16x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] unsafe fn vld1q_v8f16(a: *const i8, b: i32) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld1.v8f16")] @@ -19548,6 +19637,7 @@ pub unsafe fn vld1q_dup_p64(ptr: *const p64) -> poly64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2dup.v4f16.p0")] @@ -19565,6 +19655,7 @@ pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2dup.v8f16.p0")] @@ -19584,6 +19675,7 @@ pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -19606,6 +19698,7 @@ pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20521,6 +20614,7 @@ pub unsafe fn vld2q_dup_p16(a: *const p16) -> poly16x8x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2.v4f16.p0")] @@ -20538,6 +20632,7 @@ pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2.v8f16.p0")] @@ -20557,6 +20652,7 @@ pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20579,6 +20675,7 @@ pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20880,6 +20977,7 @@ pub unsafe fn vld2q_s32(a: *const i32) -> int32x4x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> float16x4x2_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -20905,6 +21003,7 @@ pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) -> float16x8x2_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -20932,6 +21031,7 @@ pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> float16x4x2_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -20957,6 +21057,7 @@ pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) -> float16x8x2_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -22109,6 +22210,7 @@ pub unsafe fn vld2q_p16(a: *const p16) -> poly16x8x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3dup.v4f16.p0")] @@ -22126,6 +22228,7 @@ pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3dup.v8f16.p0")] @@ -22145,6 +22248,7 @@ pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -22167,6 +22271,7 @@ pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23104,6 +23209,7 @@ pub unsafe fn vld3q_dup_p16(a: *const p16) -> poly16x8x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3.v4f16.p0")] @@ -23121,6 +23227,7 @@ pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3.v8f16.p0")] @@ -23140,6 +23247,7 @@ pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23162,6 +23270,7 @@ pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23463,6 +23572,7 @@ pub unsafe fn vld3q_s32(a: *const i32) -> int32x4x3_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> float16x4x3_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -23489,6 +23599,7 @@ pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) -> float16x8x3_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -23517,6 +23628,7 @@ pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> float16x4x3_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -23547,6 +23659,7 @@ pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) -> float16x8x3_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -24775,6 +24888,7 @@ pub unsafe fn vld3q_lane_f32(a: *const f32, b: float32x4x3_t) - #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4dup.v4f16.p0")] @@ -24792,6 +24906,7 @@ pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4dup.v8f16.p0")] @@ -24811,6 +24926,7 @@ pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -24833,6 +24949,7 @@ pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -25792,6 +25909,7 @@ pub unsafe fn vld4q_dup_p16(a: *const p16) -> poly16x8x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4.v4f16.p0")] @@ -25809,6 +25927,7 @@ pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4.v8f16.p0")] @@ -25828,6 +25947,7 @@ pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -25850,6 +25970,7 @@ pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -26151,6 +26272,7 @@ pub unsafe fn vld4q_s32(a: *const i32) -> int32x4x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> float16x4x4_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -26178,6 +26300,7 @@ pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) -> float16x8x4_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -26207,6 +26330,7 @@ pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> float16x4x4_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -26238,6 +26362,7 @@ pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) -> float16x8x4_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -27527,6 +27652,7 @@ pub unsafe fn vldrq_p128(a: *const p128) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmaxs.v4f16")] @@ -27549,6 +27675,7 @@ pub fn vmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmaxs.v8f16")] @@ -27917,6 +28044,7 @@ pub fn vmaxq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_fmax(a, b) } } @@ -27931,6 +28059,7 @@ pub fn vmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_fmax(a, b) } } @@ -27987,6 +28116,7 @@ pub fn vmaxnmq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmins.v4f16")] @@ -28009,6 +28139,7 @@ pub fn vmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmins.v8f16")] @@ -28377,6 +28508,7 @@ pub fn vminq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_fmin(a, b) } } @@ -28391,6 +28523,7 @@ pub fn vminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_fmin(a, b) } } @@ -31573,6 +31706,7 @@ pub fn vmmlaq_u32(a: uint32x4_t, b: uint8x16_t, c: uint8x16_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmov_n_f16(a: f16) -> float16x4_t { vdup_n_f16(a) } @@ -31587,6 +31721,7 @@ pub fn vmov_n_f16(a: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmovq_n_f16(a: f16) -> float16x8_t { vdupq_n_f16(a) } @@ -32315,6 +32450,7 @@ pub fn vmovn_u64(a: uint64x2_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_mul(a, b) } } @@ -32329,6 +32465,7 @@ pub fn vmul_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_mul(a, b) } } @@ -32386,6 +32523,7 @@ pub fn vmulq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_lane_f16(a: float16x4_t, v: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -32407,6 +32545,7 @@ pub fn vmul_lane_f16(a: float16x4_t, v: float16x4_t) -> float16 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_lane_f16(a: float16x8_t, v: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -33022,6 +33161,7 @@ pub fn vmulq_laneq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_n_f16(a: float16x4_t, b: f16) -> float16x4_t { unsafe { simd_mul(a, vdup_n_f16(b)) } } @@ -33036,6 +33176,7 @@ pub fn vmul_n_f16(a: float16x4_t, b: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_n_f16(a: float16x8_t, b: f16) -> float16x8_t { unsafe { simd_mul(a, vdupq_n_f16(b)) } } @@ -34369,6 +34510,7 @@ pub fn vmvnq_u8(a: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vneg_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_neg(a) } } @@ -34383,6 +34525,7 @@ pub fn vneg_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vnegq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_neg(a) } } @@ -35613,6 +35756,7 @@ pub fn vpadalq_u32(a: uint64x2_t, b: uint32x4_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vpadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vpadd.v4f16")] @@ -41943,6 +42087,7 @@ pub fn vraddhn_u64(a: uint64x2_t, b: uint64x2_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpe_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecpe.v4f16")] @@ -41965,6 +42110,7 @@ pub fn vrecpe_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpeq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecpe.v8f16")] @@ -42103,6 +42249,7 @@ pub fn vrecpeq_u32(a: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecps_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecps.v4f16")] @@ -42125,6 +42272,7 @@ pub fn vrecps_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpsq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecps.v8f16")] @@ -42206,6 +42354,7 @@ pub fn vrecpsq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { unsafe { transmute(a) } } @@ -42221,6 +42370,7 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42240,6 +42390,7 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { unsafe { transmute(a) } } @@ -42255,6 +42406,7 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42274,6 +42426,7 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { transmute(a) } } @@ -42289,6 +42442,7 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42308,6 +42462,7 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { unsafe { transmute(a) } } @@ -42323,6 +42478,7 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42342,6 +42498,7 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { unsafe { transmute(a) } } @@ -42357,6 +42514,7 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -42373,6 +42531,7 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { unsafe { transmute(a) } } @@ -42388,6 +42547,7 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42407,6 +42567,7 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { transmute(a) } } @@ -42422,6 +42583,7 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42441,6 +42603,7 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { unsafe { transmute(a) } } @@ -42456,6 +42619,7 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42475,6 +42639,7 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { unsafe { transmute(a) } } @@ -42490,6 +42655,7 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -42506,6 +42672,7 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { unsafe { transmute(a) } } @@ -42521,6 +42688,7 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42540,6 +42708,7 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { unsafe { transmute(a) } } @@ -42555,6 +42724,7 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42574,6 +42744,7 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { unsafe { transmute(a) } } @@ -42589,6 +42760,7 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42608,6 +42780,7 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { unsafe { transmute(a) } } @@ -42623,6 +42796,7 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42646,6 +42820,7 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe { transmute(a) } } @@ -42661,6 +42836,7 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42680,6 +42856,7 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { unsafe { transmute(a) } } @@ -42695,6 +42872,7 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42714,6 +42892,7 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { unsafe { transmute(a) } } @@ -42729,6 +42908,7 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42748,6 +42928,7 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { unsafe { transmute(a) } } @@ -42763,6 +42944,7 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42786,6 +42968,7 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { transmute(a) } } @@ -42801,6 +42984,7 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42820,6 +43004,7 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { unsafe { transmute(a) } } @@ -42835,6 +43020,7 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42854,6 +43040,7 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { unsafe { transmute(a) } } @@ -42869,6 +43056,7 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42888,6 +43076,7 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { unsafe { transmute(a) } } @@ -42903,6 +43092,7 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42926,6 +43116,7 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { unsafe { transmute(a) } } @@ -42941,6 +43132,7 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42960,6 +43152,7 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -42975,6 +43168,7 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -42994,6 +43188,7 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43009,6 +43204,7 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43028,6 +43224,7 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43043,6 +43240,7 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43062,6 +43260,7 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43077,6 +43276,7 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { let a: int8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43097,6 +43297,7 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43112,6 +43313,7 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43131,6 +43333,7 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43146,6 +43349,7 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43165,6 +43369,7 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43180,6 +43385,7 @@ pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43199,6 +43405,7 @@ pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43214,6 +43421,7 @@ pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43233,6 +43441,7 @@ pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43248,6 +43457,7 @@ pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43266,6 +43476,7 @@ pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43281,6 +43492,7 @@ pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43300,6 +43512,7 @@ pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43315,6 +43528,7 @@ pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43334,6 +43548,7 @@ pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43349,6 +43564,7 @@ pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { let a: uint8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43369,6 +43585,7 @@ pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43384,6 +43601,7 @@ pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43403,6 +43621,7 @@ pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43418,6 +43637,7 @@ pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43437,6 +43657,7 @@ pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43452,6 +43673,7 @@ pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43471,6 +43693,7 @@ pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43486,6 +43709,7 @@ pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43505,6 +43729,7 @@ pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43520,6 +43745,7 @@ pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43538,6 +43764,7 @@ pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43553,6 +43780,7 @@ pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43572,6 +43800,7 @@ pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43587,6 +43816,7 @@ pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43606,6 +43836,7 @@ pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43621,6 +43852,7 @@ pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { let a: poly8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43641,6 +43873,7 @@ pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43656,6 +43889,7 @@ pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43675,6 +43909,7 @@ pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43690,6 +43925,7 @@ pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43709,6 +43945,7 @@ pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { transmute(a) } } @@ -43724,6 +43961,7 @@ pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { let ret_val: float16x8_t = transmute(a); @@ -43742,6 +43980,7 @@ pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { unsafe { transmute(a) } } @@ -43757,6 +43996,7 @@ pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -43773,6 +44013,7 @@ pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { unsafe { transmute(a) } } @@ -43788,6 +44029,7 @@ pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -43804,6 +44046,7 @@ pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { unsafe { transmute(a) } } @@ -43819,6 +44062,7 @@ pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43838,6 +44082,7 @@ pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43853,6 +44098,7 @@ pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43871,6 +44117,7 @@ pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43886,6 +44133,7 @@ pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -57882,6 +58130,7 @@ pub fn vrev64q_u8(a: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrev64_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) } } @@ -57896,6 +58145,7 @@ pub fn vrev64_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrev64q_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, a, [3, 2, 1, 0, 7, 6, 5, 4]) } } @@ -58258,6 +58508,7 @@ pub fn vrhaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -58280,6 +58531,7 @@ pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -59366,6 +59618,7 @@ pub fn vrshrn_n_u64(a: uint64x2_t) -> uint32x2_t { assert_instr(frsqrte) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrte_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrte.v4f16")] @@ -59388,6 +59641,7 @@ pub fn vrsqrte_f16(a: float16x4_t) -> float16x4_t { assert_instr(frsqrte) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrteq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrte.v8f16")] @@ -59526,6 +59780,7 @@ pub fn vrsqrteq_u32(a: uint32x4_t) -> uint32x4_t { assert_instr(frsqrts) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrts_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrts.v4f16")] @@ -59548,6 +59803,7 @@ pub fn vrsqrts_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { assert_instr(frsqrts) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrtsq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrts.v8f16")] @@ -60231,6 +60487,7 @@ pub fn vrsubhn_u64(a: uint64x2_t, b: uint64x2_t) -> uint32x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vset_lane_f16(a: f16, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { simd_insert!(b, LANE as u32, a) } @@ -60247,6 +60504,7 @@ pub fn vset_lane_f16(a: f16, b: float16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsetq_lane_f16(a: f16, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { simd_insert!(b, LANE as u32, a) } @@ -63699,6 +63957,7 @@ pub fn vsriq_n_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { vst1_v4f16( @@ -63716,6 +63975,7 @@ pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { vst1q_v8f16( @@ -63734,6 +63994,7 @@ pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x2.p0.v4f16")] @@ -63751,6 +64012,7 @@ pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x2.p0.v8f16")] @@ -63767,6 +64029,7 @@ pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63786,6 +64049,7 @@ pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63806,6 +64070,7 @@ pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x3.p0.v4f16")] @@ -63823,6 +64088,7 @@ pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x3.p0.v8f16")] @@ -63839,6 +64105,7 @@ pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63858,6 +64125,7 @@ pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63877,6 +64145,7 @@ pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst1))] pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -63900,6 +64169,7 @@ pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst1))] pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -63923,6 +64193,7 @@ pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63948,6 +64219,7 @@ pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -64556,6 +64828,7 @@ pub unsafe fn vst1q_f32_x4(a: *mut f32, b: float32x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { static_assert_uimm_bits!(LANE, 2); *a = simd_extract!(b, LANE as u32); @@ -64574,6 +64847,7 @@ pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_lane_f16(a: *mut f16, b: float16x8_t) { static_assert_uimm_bits!(LANE, 3); *a = simd_extract!(b, LANE as u32); @@ -67138,6 +67412,7 @@ unsafe fn vst1q_v8i16(addr: *const i8, val: int16x8_t, align: i32) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] unsafe fn vst1_v4f16(addr: *const i8, val: float16x4_t, align: i32) { unsafe extern "unadjusted" { @@ -67155,6 +67430,7 @@ unsafe fn vst1_v4f16(addr: *const i8, val: float16x4_t, align: i32) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] unsafe fn vst1q_v8f16(addr: *const i8, val: float16x8_t, align: i32) { unsafe extern "unadjusted" { @@ -67196,6 +67472,7 @@ pub unsafe fn vst1q_lane_p64(a: *mut p64, b: poly64x2_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st2))] pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { @@ -67215,6 +67492,7 @@ pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st2))] pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { @@ -67235,6 +67513,7 @@ pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst2))] pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { @@ -67252,6 +67531,7 @@ pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst2))] pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { @@ -67550,6 +67830,7 @@ pub unsafe fn vst2q_s32(a: *mut i32, b: int32x4x2_t) { #[cfg_attr(test, assert_instr(st2, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -67571,6 +67852,7 @@ pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(st2, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -67593,6 +67875,7 @@ pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -67612,6 +67895,7 @@ pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { static_assert_uimm_bits!(LANE, 1); unsafe extern "unadjusted" { @@ -68413,6 +68697,7 @@ pub unsafe fn vst2q_p16(a: *mut p16, b: poly16x8x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst3))] pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { @@ -68430,6 +68715,7 @@ pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst3))] pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { @@ -68446,6 +68732,7 @@ pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st3))] pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { @@ -68465,6 +68752,7 @@ pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st3))] pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { @@ -68767,6 +69055,7 @@ pub unsafe fn vst3q_s32(a: *mut i32, b: int32x4x3_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -68793,6 +69082,7 @@ pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -68818,6 +69108,7 @@ pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(test, assert_instr(st3, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -68839,6 +69130,7 @@ pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(st3, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -69685,6 +69977,7 @@ pub unsafe fn vst3q_p16(a: *mut p16, b: poly16x8x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst4))] pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -69709,6 +70002,7 @@ pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst4))] pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -69732,6 +70026,7 @@ pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st4))] pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -69751,6 +70046,7 @@ pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st4))] pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -70102,6 +70398,7 @@ pub unsafe fn vst4q_s32(a: *mut i32, b: int32x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -70129,6 +70426,7 @@ pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -70155,6 +70453,7 @@ pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { #[cfg_attr(test, assert_instr(st4, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -70183,6 +70482,7 @@ pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(test, assert_instr(st4, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -71124,6 +71424,7 @@ pub unsafe fn vstrq_p128(a: *mut p128, b: p128) { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsub_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_sub(a, b) } } @@ -71138,6 +71439,7 @@ pub fn vsub_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsubq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_sub(a, b) } } @@ -73002,6 +73304,7 @@ pub fn vtbx4_p8(a: poly8x8_t, b: poly8x8x4_t, c: uint8x8_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vtrn_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a1: float16x4_t = simd_shuffle!(a, b, [0, 4, 2, 6]); @@ -73024,6 +73327,7 @@ pub fn vtrn_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vtrnq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a1: float16x8_t = simd_shuffle!(a, b, [0, 8, 2, 10, 4, 12, 6, 14]); @@ -74134,6 +74438,7 @@ pub fn vusmmlaq_s32(a: int32x4_t, b: uint8x16_t, c: int8x16_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vuzp_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a0: float16x4_t = simd_shuffle!(a, b, [0, 2, 4, 6]); @@ -74156,6 +74461,7 @@ pub fn vuzp_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vuzpq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a0: float16x8_t = simd_shuffle!(a, b, [0, 2, 4, 6, 8, 10, 12, 14]); @@ -74724,6 +75030,7 @@ pub fn vuzpq_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vzip_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a0: float16x4_t = simd_shuffle!(a, b, [0, 4, 1, 5]); @@ -74746,6 +75053,7 @@ pub fn vzip_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vzipq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a0: float16x8_t = simd_shuffle!(a, b, [0, 8, 1, 9, 2, 10, 3, 11]); diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs index 60c9daef68c42..fbd1967c544ad 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs @@ -5503,8 +5503,12 @@ mod tests { test_vcombine!(test_vcombine_s16 => vcombine_s16([3_i16, -4, 5, -6], [13_i16, -14, 15, -16])); test_vcombine!(test_vcombine_u16 => vcombine_u16([3_u16, 4, 5, 6], [13_u16, 14, 15, 16])); test_vcombine!(test_vcombine_p16 => vcombine_p16([3_u16, 4, 5, 6], [13_u16, 14, 15, 16])); - test_vcombine!(test_vcombine_f16 => vcombine_f16([3_f16, 4., 5., 6.], - [13_f16, 14., 15., 16.])); + #[cfg(not(target_arch = "arm64ec"))] + mod fp16 { + use super::*; + test_vcombine!(test_vcombine_f16 => vcombine_f16([3_f16, 4., 5., 6.], + [13_f16, 14., 15., 16.])); + } test_vcombine!(test_vcombine_s32 => vcombine_s32([3_i32, -4], [13_i32, -14])); test_vcombine!(test_vcombine_u32 => vcombine_u32([3_u32, 4], [13_u32, 14])); diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index a31613e6b1aef..a1a837bc61064 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -17,6 +17,10 @@ neon-stable: &neon-stable target-not-arm: &target-not-arm FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm"']]}]] +# #[cfg(not(target_arch = "arm64ec"))] +target-not-arm64ec: &target-not-arm64ec + FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm64ec"']]}]] + # #[cfg_attr(all(test, not(target_env = "msvc"))] msvc-disabled: &msvc-disabled FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]] @@ -169,6 +173,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fabd] safety: safe types: @@ -368,6 +373,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -559,6 +565,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -642,6 +649,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -777,6 +785,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -859,6 +868,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -931,6 +941,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16", i32] @@ -988,6 +999,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16", i32] @@ -1036,6 +1048,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -1078,6 +1091,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -1175,6 +1189,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1202,6 +1217,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1219,6 +1235,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1246,6 +1263,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1264,6 +1282,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1386,6 +1405,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [scvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["s16", "f16", "h_f16_s16", i16] @@ -1402,6 +1422,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtzs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "s16", "h", i16, 'a as i16'] @@ -1418,6 +1439,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtzu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", "h", 'a as u16'] @@ -1435,6 +1457,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [ucvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["u16", "f16", "h_f16_u16"] @@ -1486,6 +1509,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtn2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x8_t, float16x4_t, float32x4_t] @@ -1503,6 +1527,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtl2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float32x4_t, float16x8_t] @@ -1650,6 +1675,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1675,6 +1701,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1693,6 +1720,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1783,6 +1811,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -1821,6 +1850,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h_u32_f16'] @@ -1842,6 +1872,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h_s32_f16'] @@ -1863,6 +1894,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h_s16_f16', 's32'] @@ -1877,6 +1909,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h_u16_f16', 'u32'] @@ -1948,6 +1981,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -1968,6 +2002,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -1987,6 +2022,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -2007,6 +2043,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -2022,6 +2059,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -2042,6 +2080,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -2077,6 +2116,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -2097,6 +2137,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -2291,6 +2332,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -2311,6 +2353,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -2331,6 +2374,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -2351,6 +2395,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -2365,6 +2410,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -2385,6 +2431,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -2531,6 +2578,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2549,6 +2597,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2770,6 +2819,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fneg] safety: safe types: @@ -2986,6 +3036,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintx] safety: safe types: @@ -3002,6 +3053,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintx] safety: safe types: @@ -3033,6 +3085,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinta] safety: safe types: @@ -3049,6 +3102,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinta] safety: safe types: @@ -3096,6 +3150,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintn] safety: safe types: @@ -3130,6 +3185,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintm] safety: safe types: @@ -3146,6 +3202,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintm] safety: safe types: @@ -3178,6 +3235,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintp] safety: safe types: @@ -3193,6 +3251,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintp] safety: safe types: @@ -3222,6 +3281,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintz] safety: safe types: @@ -3238,6 +3298,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintz] safety: safe types: @@ -3273,6 +3334,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinti] safety: safe types: @@ -3293,6 +3355,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec # TODO: double check me assert_instr: [frinti] safety: safe @@ -5205,6 +5268,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmulx] safety: safe types: @@ -5243,6 +5307,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmulx] safety: safe types: @@ -5385,6 +5450,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -5438,6 +5504,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -5462,6 +5529,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmulx]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "f16"] @@ -5546,6 +5614,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmla] safety: safe types: @@ -5582,6 +5651,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fdiv] safety: safe types: @@ -5597,6 +5667,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -5637,6 +5708,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -5928,6 +6000,7 @@ intrinsics: - *neon-fp16 - *enable-fcma - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcadd] safety: safe types: @@ -5948,6 +6021,7 @@ intrinsics: - *neon-fp16 - *enable-fcma - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcadd] safety: safe types: @@ -5988,6 +6062,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6028,6 +6103,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6069,6 +6145,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6113,6 +6190,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6158,6 +6236,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6203,6 +6282,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6245,6 +6325,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6290,6 +6371,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6337,6 +6419,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6384,6 +6467,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6430,6 +6514,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6473,6 +6558,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6568,6 +6654,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmax] safety: safe types: @@ -6601,6 +6688,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnm] safety: safe types: @@ -6616,6 +6704,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnm] safety: safe types: @@ -6657,6 +6746,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnmv] safety: safe types: @@ -6673,6 +6763,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnmv] safety: safe types: @@ -6689,6 +6780,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxv] safety: safe types: @@ -6708,6 +6800,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminv] safety: safe types: @@ -6762,6 +6855,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmin] safety: safe types: @@ -6875,6 +6969,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [faddp] safety: safe types: @@ -6894,6 +6989,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxp] safety: safe types: @@ -6914,6 +7010,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnmp] safety: safe types: @@ -6934,6 +7031,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminp] safety: safe types: @@ -6954,6 +7052,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnmp] safety: safe types: @@ -8379,6 +8478,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fsqrt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8393,6 +8493,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fsqrt] safety: safe types: @@ -8445,6 +8546,7 @@ intrinsics: - *neon-fp16 - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frsqrts]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8501,6 +8603,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecpe]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8557,6 +8660,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8595,6 +8699,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecpx]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8687,6 +8792,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -9586,6 +9692,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [trn1]]}]] safety: safe types: @@ -9647,6 +9754,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [trn2]]}]] safety: safe types: @@ -9715,6 +9823,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [zip2]]}]] safety: safe types: @@ -9765,6 +9874,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [zip1]]}]] safety: safe types: @@ -9826,6 +9936,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [uzp1]]}]] safety: safe types: @@ -9891,6 +10002,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [uzp2]]}]] safety: safe types: @@ -10180,6 +10292,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10206,6 +10319,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10230,6 +10344,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmsub]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "h_f16"] @@ -10342,6 +10457,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "h_f16"] @@ -10358,6 +10474,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10377,6 +10494,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10541,6 +10659,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmeq]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, 'f16x4', 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -10576,6 +10695,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", "h_f16"] @@ -10700,6 +10820,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -10842,6 +10963,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -10972,6 +11094,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -11126,6 +11249,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -11146,6 +11270,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -11183,6 +11308,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -11328,6 +11454,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h_f16'] @@ -11399,6 +11526,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmls] safety: safe types: @@ -11651,6 +11779,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frsqrte]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16"] @@ -11734,6 +11863,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -11772,6 +11902,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -11792,6 +11923,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -11807,6 +11939,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -11827,6 +11960,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -12857,6 +12991,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "{type[2]}"']] - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [ldr]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12924,6 +13059,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [str]]}]] - FnCall: [allow, ['clippy::cast_ptr_alignment']] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -13637,6 +13773,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlal2] safety: safe types: @@ -13660,6 +13797,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13684,6 +13822,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlal] safety: safe types: @@ -13707,6 +13846,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13731,6 +13871,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlsl2] safety: safe types: @@ -13753,6 +13894,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13777,6 +13919,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlsl] safety: safe types: @@ -13799,6 +13942,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index c96c6e2a0c0b5..f16a257399bc8 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -37,6 +37,10 @@ target-is-arm: &target-is-arm target-not-arm: &target-not-arm FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm"']]}]] +# #[cfg(not(target_arch = "arm64ec"))] +target-not-arm64ec: &target-not-arm64ec + FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm64ec"']]}]] + not-arm: ¬-arm FnCall: [not, ['target_arch = "arm"']] @@ -278,6 +282,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -396,6 +401,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmeq]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -457,6 +463,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -474,6 +481,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['h_f16', 'f16'] @@ -555,6 +563,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -573,6 +582,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -651,6 +661,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -668,6 +679,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmle]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -849,6 +861,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -895,6 +908,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -935,6 +949,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -970,6 +985,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -1004,6 +1020,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [scvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [int16x4_t, float16x4_t] @@ -1038,6 +1055,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ucvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [uint16x4_t, float16x4_t] @@ -1109,6 +1127,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1140,6 +1159,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1171,6 +1191,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1229,6 +1250,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1482,6 +1504,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1501,6 +1524,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [dup]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, f16, 'float16x4', '_n_'] @@ -1519,6 +1543,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1740,6 +1765,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1758,6 +1784,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2207,6 +2234,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fneg]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, 'f16'] @@ -2478,6 +2506,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frintn]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -2743,6 +2772,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -2773,6 +2803,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -2793,6 +2824,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld1r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3385,6 +3417,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3413,6 +3446,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3440,6 +3474,7 @@ intrinsics: - *neon-fp16 - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld2]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3469,6 +3504,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld2r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3498,6 +3534,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3540,6 +3577,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3580,6 +3618,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3608,6 +3647,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3635,6 +3675,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3664,6 +3705,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld3r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3693,6 +3735,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3737,6 +3780,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -4718,6 +4762,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec types: - ['*mut f16', float16x4_t, '2'] - ['*mut f16', float16x8_t, '3'] @@ -4955,6 +5000,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst1] types: - [f16, float16x4x4_t, float16x4_t] @@ -5098,6 +5144,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st2] safety: unsafe: [neon] @@ -5188,6 +5235,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st2, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5279,6 +5327,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst2] safety: unsafe: [neon] @@ -5345,6 +5394,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5555,6 +5605,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst3] safety: unsafe: [neon] @@ -5623,6 +5674,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5683,6 +5735,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st3] safety: unsafe: [neon] @@ -5747,6 +5800,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st3, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5960,6 +6014,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst4] safety: unsafe: [neon] @@ -6029,6 +6084,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -6091,6 +6147,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st4] safety: unsafe: [neon] @@ -6157,6 +6214,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st4, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -6317,6 +6375,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmul]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [f16, float16x4_t] @@ -6366,6 +6425,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6569,6 +6629,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmla]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -6678,6 +6739,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fsub]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['f16', float16x4_t] @@ -6696,6 +6758,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -6716,6 +6779,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['h_f16', 'f16'] @@ -7194,6 +7258,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmax]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7236,6 +7301,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmaxnm]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7254,6 +7320,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fminnm]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7340,6 +7407,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmin]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7404,6 +7472,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [faddp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8247,6 +8316,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vrsqrts]]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frsqrts]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8295,6 +8365,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frecpe]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8343,6 +8414,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frecps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8675,6 +8747,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: # non-q @@ -8737,6 +8810,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [poly64x1_t, float16x4_t] @@ -8759,6 +8833,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [rev64]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "[3, 2, 1, 0]"] @@ -9074,6 +9149,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["u64", float16x4_t] @@ -9147,6 +9223,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -9578,6 +9655,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [trn2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 4, 2, 6]', '[1, 5, 3, 7]'] @@ -9733,6 +9811,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [zip2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 4, 1, 5]', '[2, 6, 3, 7]'] @@ -9803,6 +9882,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [uzp2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 2, 4, 6]', '[1, 3, 5, 7]'] @@ -10050,6 +10130,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [vst1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10077,6 +10158,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10103,6 +10185,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [vst1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10179,6 +10262,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10233,6 +10317,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10376,6 +10461,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -10394,6 +10480,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -10633,6 +10720,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtzu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -10652,6 +10740,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtn]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float32x4_t, float16x4_t] @@ -10668,6 +10757,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtl]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float32x4_t] @@ -11029,6 +11119,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmul]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "f16"] @@ -11141,6 +11232,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -11159,6 +11251,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmlt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -11271,6 +11364,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmls]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -11386,6 +11480,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vrsqrte]]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frsqrte]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -11499,6 +11594,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtzs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -11748,6 +11844,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -11834,6 +11931,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "{type[3]}"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['{type[2]}']]}]] types: - ['*const f16', float16x4_t, '"vld1.16"', 'neon,v7', 'crate::mem::align_of::() as i32', '_v4f16'] @@ -12117,6 +12215,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12145,6 +12244,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12172,6 +12272,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12201,6 +12302,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld4r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12230,6 +12332,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -12276,6 +12379,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -13674,6 +13778,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['"vst1.{type[4]}"']]}]] types: - ['_v4f16', '* const i8', float16x4_t, i32, '16'] @@ -13738,6 +13843,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['"vst1.{type[2]}"']]}]] types: - ['*mut f16', float16x4_t, '16', 'transmute(a)', 'crate::mem::align_of::() as i32', '_v4f16'] @@ -13918,6 +14024,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -13933,6 +14040,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -13952,6 +14060,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop, 'LANE = 0']]}]] - FnCall: [rustc_legacy_const_generics, ["1"]] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13971,6 +14080,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [dup]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, f16] @@ -14585,6 +14695,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, { FnCall: [assert_instr, ['vbsl']]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, ['bsl']]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['vbslq_f16', 'uint16x8_t', 'float16x8_t', 'int16x8_t::splat(-1)'] diff --git a/library/stdarch/crates/stdarch-test/src/disassembly.rs b/library/stdarch/crates/stdarch-test/src/disassembly.rs index f5167ea8d8ef3..4c136cff02ae6 100644 --- a/library/stdarch/crates/stdarch-test/src/disassembly.rs +++ b/library/stdarch/crates/stdarch-test/src/disassembly.rs @@ -27,9 +27,9 @@ fn normalize(mut symbol: &str) -> String { symbol = symbol[last_colon + 1..].to_string(); } - // Normalize to no leading underscore to handle platforms that may + // Normalize to no leading mangling chars to handle platforms that may // inject extra ones in symbol names. - while symbol.starts_with('_') || symbol.starts_with('.') { + while symbol.starts_with('_') || symbol.starts_with('.') || symbol.starts_with('#') { symbol.remove(0); } // Windows/x86 has a suffix such as @@4. @@ -49,6 +49,8 @@ pub(crate) fn disassemble_myself() -> HashSet { "i686-pc-windows-msvc" } else if cfg!(target_arch = "aarch64") { "aarch64-pc-windows-msvc" + } else if cfg!(target_arch = "arm64ec") { + "arm64ec-pc-windows-msvc" } else { panic!("disassembly unimplemented") }; From 802fa9255b59c8787d679f5da1f5534f9c0580b0 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Sun, 10 Aug 2025 10:02:03 -0500 Subject: [PATCH 0049/1900] Add config option to exclude locals from doc search --- .../crates/ide/src/file_structure.rs | 307 +++++++++++++++++- src/tools/rust-analyzer/crates/ide/src/lib.rs | 12 +- .../crates/rust-analyzer/src/cli/symbols.rs | 9 +- .../crates/rust-analyzer/src/config.rs | 16 + .../rust-analyzer/src/handlers/request.rs | 17 +- .../docs/book/src/configuration_generated.md | 7 + .../rust-analyzer/editors/code/package.json | 10 + 7 files changed, 350 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 6820f99facf2c..e0cd32baa97e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -23,6 +23,11 @@ pub enum StructureNodeKind { Region, } +#[derive(Debug, Clone)] +pub struct FileStructureConfig { + pub exclude_locals: bool, +} + // Feature: File Structure // // Provides a tree of the symbols defined in the file. Can be used to @@ -36,21 +41,24 @@ pub enum StructureNodeKind { // | VS Code | Ctrl+Shift+O | // // ![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif) -pub(crate) fn file_structure(file: &SourceFile) -> Vec { +pub(crate) fn file_structure( + file: &SourceFile, + config: &FileStructureConfig, +) -> Vec { let mut res = Vec::new(); let mut stack = Vec::new(); for event in file.syntax().preorder_with_tokens() { match event { WalkEvent::Enter(NodeOrToken::Node(node)) => { - if let Some(mut symbol) = structure_node(&node) { + if let Some(mut symbol) = structure_node(&node, config) { symbol.parent = stack.last().copied(); stack.push(res.len()); res.push(symbol); } } WalkEvent::Leave(NodeOrToken::Node(node)) => { - if structure_node(&node).is_some() { + if structure_node(&node, config).is_some() { stack.pop().unwrap(); } } @@ -71,7 +79,7 @@ pub(crate) fn file_structure(file: &SourceFile) -> Vec { res } -fn structure_node(node: &SyntaxNode) -> Option { +fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option { fn decl(node: N, kind: StructureNodeKind) -> Option { decl_with_detail(&node, None, kind) } @@ -187,6 +195,10 @@ fn structure_node(node: &SyntaxNode) -> Option { Some(node) }, ast::LetStmt(it) => { + if config.exclude_locals { + return None; + } + let pat = it.pat()?; let mut label = String::new(); @@ -254,9 +266,19 @@ mod tests { use super::*; + const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true }; + fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + check_with_config(ra_fixture, &DEFAULT_CONFIG, expect); + } + + fn check_with_config( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + config: &FileStructureConfig, + expect: Expect, + ) { let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap(); - let structure = file_structure(&file); + let structure = file_structure(&file, config); expect.assert_debug_eq(&structure) } @@ -701,13 +723,264 @@ fn let_statements() { ), deprecated: false, }, + ] + "#]], + ); + } + + #[test] + fn test_file_structure_include_locals() { + check_with_config( + r#" +struct Foo { + x: i32 +} + +mod m { + fn bar1() {} + fn bar2(t: T) -> T {} + fn bar3(a: A, + b: B) -> Vec< + u32 + > {} +} + +enum E { X, Y(i32) } +type T = (); +static S: i32 = 42; +const C: i32 = 42; +trait Tr {} +trait Alias = Tr; + +macro_rules! mc { + () => {} +} + +fn let_statements() { + let x = 42; + let mut y = x; + let Foo { + .. + } = Foo { x }; + _ = (); + let _ = g(); +} +"#, + &FileStructureConfig { exclude_locals: false }, + expect![[r#" + [ + StructureNode { + parent: None, + label: "Foo", + navigation_range: 8..11, + node_range: 1..26, + kind: SymbolKind( + Struct, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 0, + ), + label: "x", + navigation_range: 18..19, + node_range: 18..24, + kind: SymbolKind( + Field, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "m", + navigation_range: 32..33, + node_range: 28..158, + kind: SymbolKind( + Module, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar1", + navigation_range: 43..47, + node_range: 40..52, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar2", + navigation_range: 60..64, + node_range: 57..81, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn(t: T) -> T", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar3", + navigation_range: 89..93, + node_range: 86..156, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn(a: A, b: B) -> Vec< u32 >", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "E", + navigation_range: 165..166, + node_range: 160..180, + kind: SymbolKind( + Enum, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "X", + navigation_range: 169..170, + node_range: 169..170, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "Y", + navigation_range: 172..173, + node_range: 172..178, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "T", + navigation_range: 186..187, + node_range: 181..193, + kind: SymbolKind( + TypeAlias, + ), + detail: Some( + "()", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "S", + navigation_range: 201..202, + node_range: 194..213, + kind: SymbolKind( + Static, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "C", + navigation_range: 220..221, + node_range: 214..232, + kind: SymbolKind( + Const, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "Tr", + navigation_range: 239..241, + node_range: 233..244, + kind: SymbolKind( + Trait, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "Alias", + navigation_range: 251..256, + node_range: 245..262, + kind: SymbolKind( + TraitAlias, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mc", + navigation_range: 277..279, + node_range: 264..296, + kind: SymbolKind( + Macro, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "let_statements", + navigation_range: 301..315, + node_range: 298..429, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, StructureNode { parent: Some( - 27, + 15, ), label: "x", - navigation_range: 684..685, - node_range: 680..691, + navigation_range: 328..329, + node_range: 324..335, kind: SymbolKind( Local, ), @@ -716,11 +989,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "mut y", - navigation_range: 700..705, - node_range: 696..710, + navigation_range: 344..349, + node_range: 340..354, kind: SymbolKind( Local, ), @@ -729,11 +1002,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "Foo { .. }", - navigation_range: 719..741, - node_range: 715..754, + navigation_range: 363..385, + node_range: 359..398, kind: SymbolKind( Local, ), @@ -742,11 +1015,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "_", - navigation_range: 804..805, - node_range: 800..812, + navigation_range: 419..420, + node_range: 415..427, kind: SymbolKind( Local, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 98877482ed863..5349ebb7c82a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -81,7 +81,7 @@ pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, call_hierarchy::{CallHierarchyConfig, CallItem}, expand_macro::ExpandedMacro, - file_structure::{StructureNode, StructureNodeKind}, + file_structure::{FileStructureConfig, StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ @@ -430,12 +430,16 @@ impl Analysis { /// Returns a tree representation of symbols in the file. Useful to draw a /// file outline. - pub fn file_structure(&self, file_id: FileId) -> Cancellable> { + pub fn file_structure( + &self, + config: &FileStructureConfig, + file_id: FileId, + ) -> Cancellable> { // FIXME: Edition self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - - file_structure::file_structure(&db.parse(editioned_file_id_wrapper).tree()) + let source_file = db.parse(editioned_file_id_wrapper).tree(); + file_structure::file_structure(&source_file, config) }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs index 9fad6723afcd9..d7af56d3e15be 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs @@ -1,5 +1,5 @@ //! Read Rust code on stdin, print syntax tree on stdout. -use ide::Analysis; +use ide::{Analysis, FileStructureConfig}; use crate::cli::{flags, read_stdin}; @@ -7,7 +7,12 @@ impl flags::Symbols { pub fn run(self) -> anyhow::Result<()> { let text = read_stdin()?; let (analysis, file_id) = Analysis::from_single_file(text); - let structure = analysis.file_structure(file_id).unwrap(); + let structure = analysis + // The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude + // locals because it is unlikely that users want document search to return the names of + // local variables, but here we include them deliberately. + .file_structure(&FileStructureConfig { exclude_locals: false }, file_id) + .unwrap(); for s in structure { println!("{s:?}"); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 1a00295b9ac18..77e5a8e079f84 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -858,6 +858,9 @@ config_data! { /// check will be performed. check_workspace: bool = true, + /// Exclude all locals from document symbol search. + document_symbol_search_excludeLocals: bool = true, + /// These proc-macros will be ignored when trying to expand them. /// /// This config takes a map of crate names with the exported proc-macro names to ignore as values. @@ -1481,6 +1484,13 @@ pub enum FilesWatcher { Server, } +/// Configuration for document symbol search requests. +#[derive(Debug, Clone)] +pub struct DocumentSymbolConfig { + /// Should locals be excluded. + pub search_exclude_locals: bool, +} + #[derive(Debug, Clone)] pub struct NotificationsConfig { pub cargo_toml_not_found: bool, @@ -2438,6 +2448,12 @@ impl Config { } } + pub fn document_symbol(&self, source_root: Option) -> DocumentSymbolConfig { + DocumentSymbolConfig { + search_exclude_locals: *self.document_symbol_search_excludeLocals(source_root), + } + } + pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 25c0aac405e79..74ed5e344af6d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -8,8 +8,9 @@ use anyhow::Context; use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, - FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, - RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData, + InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, + SingleResolve, SourceChange, TextEdit, }; use ide_db::{FxHashMap, SymbolKind}; use itertools::Itertools; @@ -568,7 +569,14 @@ pub(crate) fn handle_document_symbol( let mut parents: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); - for symbol in snap.analysis.file_structure(file_id)? { + let config = snap.config.document_symbol(None); + + let structure_nodes = snap.analysis.file_structure( + &FileStructureConfig { exclude_locals: config.search_exclude_locals }, + file_id, + )?; + + for symbol in structure_nodes { let mut tags = Vec::new(); if symbol.deprecated { tags.push(SymbolTag::DEPRECATED) @@ -588,8 +596,7 @@ pub(crate) fn handle_document_symbol( parents.push((doc_symbol, symbol.parent)); } - // Builds hierarchy from a flat list, in reverse order (so that indices - // makes sense) + // Builds hierarchy from a flat list, in reverse order (so that indices make sense) let document_symbols = { let mut acc = Vec::new(); while let Some((mut node, parent_idx)) = parents.pop() { diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 99a30d8f62138..6ee956fe0dbf4 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -610,6 +610,13 @@ The warnings will be indicated by a blue squiggly underline in code and a blue i the `Problems Panel`. +## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals} + +Default: `true` + +Exclude all locals from document symbol search. + + ## rust-analyzer.files.exclude {#files.exclude} Default: `[]` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 470db244f14bd..328eb509b4eea 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1585,6 +1585,16 @@ } } }, + { + "title": "Document", + "properties": { + "rust-analyzer.document.symbol.search.excludeLocals": { + "markdownDescription": "Exclude all locals from document symbol search.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Files", "properties": { From 00d000ce74f8e3bed8abc4cff382f62dcca0d8d5 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Sun, 10 Aug 2025 10:02:34 -0500 Subject: [PATCH 0050/1900] Fix minor things --- .../crates/ide/src/file_structure.rs | 1 - .../crates/rust-analyzer/src/config.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 38 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index e0cd32baa97e3..ac15af0334fc8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -213,7 +213,6 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 77e5a8e079f84..d4cd56dc55bf6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -3083,7 +3083,7 @@ macro_rules! _config_data { }) => { /// Default config values for this grouping. #[allow(non_snake_case)] - #[derive(Debug, Clone )] + #[derive(Debug, Clone)] struct $name { $($field: $ty,)* } impl_for_config_data!{ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 74ed5e344af6d..6cb28aecf748f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -567,7 +567,7 @@ pub(crate) fn handle_document_symbol( let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?); let line_index = snap.file_line_index(file_id)?; - let mut parents: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); + let mut symbols: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); let config = snap.config.document_symbol(None); @@ -576,38 +576,38 @@ pub(crate) fn handle_document_symbol( file_id, )?; - for symbol in structure_nodes { + for node in structure_nodes { let mut tags = Vec::new(); - if symbol.deprecated { + if node.deprecated { tags.push(SymbolTag::DEPRECATED) }; #[allow(deprecated)] - let doc_symbol = lsp_types::DocumentSymbol { - name: symbol.label, - detail: symbol.detail, - kind: to_proto::structure_node_kind(symbol.kind), + let symbol = lsp_types::DocumentSymbol { + name: node.label, + detail: node.detail, + kind: to_proto::structure_node_kind(node.kind), tags: Some(tags), - deprecated: Some(symbol.deprecated), - range: to_proto::range(&line_index, symbol.node_range), - selection_range: to_proto::range(&line_index, symbol.navigation_range), + deprecated: Some(node.deprecated), + range: to_proto::range(&line_index, node.node_range), + selection_range: to_proto::range(&line_index, node.navigation_range), children: None, }; - parents.push((doc_symbol, symbol.parent)); + symbols.push((symbol, node.parent)); } - // Builds hierarchy from a flat list, in reverse order (so that indices make sense) + // Builds hierarchy from a flat list, in reverse order (so that the indices make sense) let document_symbols = { let mut acc = Vec::new(); - while let Some((mut node, parent_idx)) = parents.pop() { - if let Some(children) = &mut node.children { + while let Some((mut symbol, parent_idx)) = symbols.pop() { + if let Some(children) = &mut symbol.children { children.reverse(); } let parent = match parent_idx { None => &mut acc, - Some(i) => parents[i].0.children.get_or_insert_with(Vec::new), + Some(i) => symbols[i].0.children.get_or_insert_with(Vec::new), }; - parent.push(node); + parent.push(symbol); } acc.reverse(); acc @@ -617,7 +617,7 @@ pub(crate) fn handle_document_symbol( document_symbols.into() } else { let url = to_proto::url(https://github.com/&snap,%20file_id); - let mut symbol_information = Vec::::new(); + let mut symbol_information = Vec::new(); for symbol in document_symbols { flatten_document_symbol(&symbol, None, &url, &mut symbol_information); } @@ -654,7 +654,7 @@ pub(crate) fn handle_workspace_symbol( let _p = tracing::info_span!("handle_workspace_symbol").entered(); let config = snap.config.workspace_symbol(None); - let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); + let (all_symbols, libs) = decide_search_kind_and_scope(¶ms, &config); let query = { let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect(); @@ -677,7 +677,7 @@ pub(crate) fn handle_workspace_symbol( return Ok(Some(lsp_types::WorkspaceSymbolResponse::Nested(res))); - fn decide_search_scope_and_kind( + fn decide_search_kind_and_scope( params: &WorkspaceSymbolParams, config: &WorkspaceSymbolConfig, ) -> (bool, bool) { From af10cb727fd55ce8638653f18d2edd4b8005add7 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 11 Aug 2025 04:25:52 +0000 Subject: [PATCH 0051/1900] Prepare for merging from rust-lang/rust This updates the rust-version file to 21a19c297d4f5a03501d92ca251bd7a17073c08a. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 2178caf63968a..02b217f7d80dc 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -733dab558992d902d6d17576de1da768094e2cf3 +21a19c297d4f5a03501d92ca251bd7a17073c08a From 7daaa4ee01f4c4d026293a6e3a90e4c8b4f5aa12 Mon Sep 17 00:00:00 2001 From: "Shoyu Vanilla (Flint)" Date: Mon, 11 Aug 2025 18:18:55 +0900 Subject: [PATCH 0052/1900] hotfix: Update flycheck diagnostics generation --- src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index cd7c632d105e0..2711bdba693bf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -134,6 +134,7 @@ impl DiagnosticCollection { if self.check[flycheck_id].generation > generation { return; } + self.check[flycheck_id].generation = generation; let diagnostics = self.check[flycheck_id] .per_package .entry(package_id.clone()) From 380d89d82f514d7e998f7b3b0e6bdb2065b711bc Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 11 Aug 2025 01:57:27 +0900 Subject: [PATCH 0053/1900] Make import sorting order follow 2024 edition style --- .../ide-assists/src/handlers/merge_imports.rs | 2 +- .../src/handlers/normalize_import.rs | 22 +- .../ide-completion/src/tests/flyimport.rs | 2 +- .../ide-db/src/imports/insert_use/tests.rs | 26 +-- .../ide-db/src/imports/merge_imports.rs | 217 ++++++++++++++++-- 5 files changed, 219 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index 6bf7f5849148f..9ba73d23dd243 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -605,7 +605,7 @@ use foo::$0{ ", r" use foo::{ - bar::baz, FooBar + FooBar, bar::baz, }; ", ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs index bba28b5fc8af5..36da1d1788247 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs @@ -109,8 +109,8 @@ mod tests { #[test] fn test_order() { check_assist_variations!( - "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz}", - "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}" + "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz, v10, v9, r#aaa}", + "foo::{self, Baz, FOO_BAZ, Qux, r#aaa, bar::{Bar, Quux}, baz, v9, v10, *}" ); } @@ -145,17 +145,17 @@ fn main() { #[test] fn test_redundant_braces() { - check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{baz, Qux}"); + check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{Qux, baz}"); check_assist_variations!("foo::{bar::{self}}", "foo::bar::{self}"); check_assist_variations!("foo::{bar::{*}}", "foo::bar::*"); check_assist_variations!("foo::{bar::{Qux as Quux}}", "foo::bar::Qux as Quux"); check_assist_variations!( "foo::bar::{{FOO_BAZ, Qux, self}, {*, baz}}", - "foo::bar::{self, baz, Qux, FOO_BAZ, *}" + "foo::bar::{self, FOO_BAZ, Qux, baz, *}" ); check_assist_variations!( "foo::bar::{{{FOO_BAZ}, {{Qux}, {self}}}, {{*}, {baz}}}", - "foo::bar::{self, baz, Qux, FOO_BAZ, *}" + "foo::bar::{self, FOO_BAZ, Qux, baz, *}" ); } @@ -163,11 +163,11 @@ fn main() { fn test_merge() { check_assist_variations!( "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux}}", - "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}" ); check_assist_variations!( "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux, bar::{baz::Foo}}}", - "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz::{self, Foo}, *}, qux, *}" ); } @@ -229,15 +229,15 @@ use { check_assist_not_applicable_variations!("foo::bar"); check_assist_not_applicable_variations!("foo::bar::*"); check_assist_not_applicable_variations!("foo::bar::Qux as Quux"); - check_assist_not_applicable_variations!("foo::bar::{self, baz, Qux, FOO_BAZ, *}"); + check_assist_not_applicable_variations!("foo::bar::{self, FOO_BAZ, Qux, baz, *}"); check_assist_not_applicable_variations!( - "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}" + "foo::{self, Baz, FOO_BAZ, Qux, bar::{Bar, Quux}, baz, *}" ); check_assist_not_applicable_variations!( - "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}" ); check_assist_not_applicable_variations!( - "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}" + "foo::{bar::{self, FOO_BAZ, Quux, baz::{self, Foo}, *}, qux, *}" ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 27c91bc7c4558..04bed418eec79 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -114,7 +114,7 @@ fn main() { } "#, r#" -use dep::{some_module::{SecondStruct, ThirdStruct}, FirstStruct}; +use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; fn main() { ThirdStruct diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 4a00854f01a5f..3350e1c3d207f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -782,18 +782,18 @@ fn merge_groups_long_last_list() { fn merge_groups_long_full_nested() { check_crate( "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", ); check_crate( "std::foo::bar::r#Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Fez, Fizz}, r#Baz, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{r#Baz, Qux, quux::{Fez, Fizz}};", ); check_one( "std::foo::bar::Baz", - r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};", - r"use {std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux}};", + r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};", + r"use {Qux, std::foo::bar::{Baz, quux::{Fez, Fizz}}};", ); } @@ -811,13 +811,13 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};", fn merge_groups_full_nested_deep() { check_crate( "std::foo::bar::quux::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", ); check_one( "std::foo::bar::quux::Baz", - r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};", - r"use {std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux}};", + r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};", + r"use {Qux, std::foo::bar::quux::{Baz, Fez, Fizz}};", ); } @@ -988,8 +988,8 @@ use syntax::SyntaxKind::{self, *};", fn merge_glob_nested() { check_crate( "foo::bar::quux::Fez", - r"use foo::bar::{Baz, quux::*};", - r"use foo::bar::{quux::{Fez, *}, Baz};", + r"use foo::bar::{quux::*, Baz};", + r"use foo::bar::{Baz, quux::{Fez, *}};", ) } @@ -998,7 +998,7 @@ fn merge_nested_considers_first_segments() { check_crate( "hir_ty::display::write_bounds_like_dyn_trait", r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", - r"use hir_ty::{autoderef, display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, method_resolution};", + r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 61962e593476c..4e779a7d858e5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -3,7 +3,6 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use parser::T; -use stdx::is_upper_snake_case; use syntax::{ Direction, SyntaxElement, algo, ast::{ @@ -543,12 +542,13 @@ fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { } } -/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super` -/// and `crate` first, then identifier imports with lowercase ones first and upper snake case -/// (e.g. UPPER_SNAKE_CASE) ones last, then glob imports, and at last list imports. +/// Orders use trees following `rustfmt`'s version sorting algorithm for ordering imports. /// -/// Example: `foo::{self, baz, foo, Baz, Qux, FOO_BAZ, *, {Bar}}` -/// Ref: . +/// Example: `foo::{self, Baz, FOO_BAZ, Qux, baz, foo, *, {Bar}}` +/// +/// Ref: +/// - +/// - pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { let a_is_simple_path = a.is_simple_path() && a.rename().is_none(); let b_is_simple_path = b.is_simple_path() && b.rename().is_none(); @@ -613,26 +613,9 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(a_name), Some(b_name)) => { - // snake_case < UpperCamelCase < UPPER_SNAKE_CASE let a_text = a_name.as_str().trim_start_matches("r#"); let b_text = b_name.as_str().trim_start_matches("r#"); - if a_text.starts_with(char::is_lowercase) - && b_text.starts_with(char::is_uppercase) - { - return Ordering::Less; - } - if a_text.starts_with(char::is_uppercase) - && b_text.starts_with(char::is_lowercase) - { - return Ordering::Greater; - } - if !is_upper_snake_case(a_text) && is_upper_snake_case(b_text) { - return Ordering::Less; - } - if is_upper_snake_case(a_text) && !is_upper_snake_case(b_text) { - return Ordering::Greater; - } - a_text.cmp(b_text) + version_sort::version_sort(a_text, b_text) } } } @@ -740,3 +723,189 @@ fn remove_subtree_if_only_self(use_tree: &ast::UseTree) { _ => (), } } + +// Taken from rustfmt +// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs +mod version_sort { + // Original rustfmt code contains some clippy lints. + // Suppress them to minimize changes from upstream. + #![allow(clippy::all)] + + use std::cmp::Ordering; + + use itertools::{EitherOrBoth, Itertools}; + + struct VersionChunkIter<'a> { + ident: &'a str, + start: usize, + } + + impl<'a> VersionChunkIter<'a> { + pub(crate) fn new(ident: &'a str) -> Self { + Self { ident, start: 0 } + } + + fn parse_numeric_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + let zeros = source.chars().take_while(|c| *c == '0').count(); + let value = source.parse::().ok()?; + + Some(VersionChunk::Number { value, zeros, source }) + } + + fn parse_str_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c == '_' { + is_end_of_chunk = true; + break; + } + + if !c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + Some(VersionChunk::Str(source)) + } + } + + impl<'a> Iterator for VersionChunkIter<'a> { + type Item = VersionChunk<'a>; + + fn next(&mut self) -> Option { + let mut chars = self.ident[self.start..].char_indices(); + let (_, next) = chars.next()?; + + if next == '_' { + self.start = self.start + next.len_utf8(); + return Some(VersionChunk::Underscore); + } + + if next.is_ascii_digit() { + return self.parse_numeric_chunk(chars); + } + + self.parse_str_chunk(chars) + } + } + + /// Represents a chunk in the version-sort algorithm + #[derive(Debug, PartialEq, Eq)] + enum VersionChunk<'a> { + /// A single `_` in an identifier. Underscores are sorted before all other characters. + Underscore, + /// A &str chunk in the version sort. + Str(&'a str), + /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros. + Number { value: usize, zeros: usize, source: &'a str }, + } + + /// Determine which side of the version-sort comparison had more leading zeros. + #[derive(Debug, PartialEq, Eq)] + enum MoreLeadingZeros { + Left, + Right, + Equal, + } + + pub(super) fn version_sort(a: &str, b: &str) -> Ordering { + let iter_a = VersionChunkIter::new(a); + let iter_b = VersionChunkIter::new(b); + let mut more_leading_zeros = MoreLeadingZeros::Equal; + + for either_or_both in iter_a.zip_longest(iter_b) { + match either_or_both { + EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater, + EitherOrBoth::Right(_) => return std::cmp::Ordering::Less, + EitherOrBoth::Both(a, b) => match (a, b) { + (VersionChunk::Underscore, VersionChunk::Underscore) => { + continue; + } + (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less, + (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater, + (VersionChunk::Str(ca), VersionChunk::Str(cb)) + | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. }) + | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => { + match ca.cmp(&cb) { + std::cmp::Ordering::Equal => { + continue; + } + order @ _ => return order, + } + } + ( + VersionChunk::Number { value: va, zeros: lza, .. }, + VersionChunk::Number { value: vb, zeros: lzb, .. }, + ) => match va.cmp(&vb) { + std::cmp::Ordering::Equal => { + if lza == lzb { + continue; + } + + if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb { + more_leading_zeros = MoreLeadingZeros::Left; + } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb { + more_leading_zeros = MoreLeadingZeros::Right; + } + continue; + } + order @ _ => return order, + }, + }, + } + } + + match more_leading_zeros { + MoreLeadingZeros::Equal => std::cmp::Ordering::Equal, + MoreLeadingZeros::Left => std::cmp::Ordering::Less, + MoreLeadingZeros::Right => std::cmp::Ordering::Greater, + } + } +} From 6c3c620e6b65a130a6996cca14cb9e19cc8ea65c Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 12 Aug 2025 00:24:44 +0900 Subject: [PATCH 0054/1900] fix: Panic while trying to clear old diagnostics while there's nothing --- src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 2711bdba693bf..30f530100f9df 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -105,7 +105,7 @@ impl DiagnosticCollection { flycheck_id: usize, generation: DiagnosticsGeneration, ) { - if self.check[flycheck_id].generation < generation { + if self.check.get(flycheck_id).is_some_and(|it| it.generation < generation) { self.clear_check(flycheck_id); } } From 875c158686fc47e083c83d1ccee4359ba4cc64f9 Mon Sep 17 00:00:00 2001 From: sgasho Date: Mon, 11 Aug 2025 22:19:50 +0900 Subject: [PATCH 0055/1900] fix: Implement default member to resolve IdentPat --- .../src/handlers/add_missing_impl_members.rs | 49 +++++++++++++++ .../crates/ide-db/src/path_transform.rs | 61 ++++++++++++++++--- 2 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 11201afb8a7f2..7e03eb30304bf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2418,6 +2418,55 @@ pub struct MyStruct; impl other_file_2::Trait for MyStruct { $0type Iter; +}"#, + ); + } + + #[test] + fn test_qualify_ident_pat_in_default_members() { + check_assist( + add_missing_default_members, + r#" +//- /lib.rs crate:b new_source_root:library +pub enum State { + Active, + Inactive, +} + +use State::*; + +pub trait Checker { + fn check(&self) -> State; + + fn is_active(&self) -> bool { + match self.check() { + Active => true, + Inactive => false, + } + } +} +//- /main.rs crate:a deps:b +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + }$0 +}"#, + r#" +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + } + + $0fn is_active(&self) -> bool { + match self.check() { + b::State::Active => true, + b::State::Inactive => false, + } + } }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 5d88afec50951..a76a0551dd8db 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -11,7 +11,7 @@ use rustc_hash::FxHashMap; use span::Edition; use syntax::{ NodeOrToken, SyntaxNode, - ast::{self, AstNode, HasGenericArgs, make}, + ast::{self, AstNode, HasGenericArgs, HasName, make}, syntax_editor::{self, SyntaxEditor}, }; @@ -315,32 +315,49 @@ impl Ctx<'_> { } fn transform_path(&self, path: &SyntaxNode) -> SyntaxNode { - fn find_child_paths(root_path: &SyntaxNode) -> Vec { - let mut result = Vec::new(); + fn find_child_paths_and_ident_pats( + root_path: &SyntaxNode, + ) -> Vec> { + let mut result: Vec> = Vec::new(); for child in root_path.children() { if let Some(child_path) = ast::Path::cast(child.clone()) { - result.push(child_path); + result.push(either::Left(child_path)); + } else if let Some(child_ident_pat) = ast::IdentPat::cast(child.clone()) { + result.push(either::Right(child_ident_pat)); } else { - result.extend(find_child_paths(&child)); + result.extend(find_child_paths_and_ident_pats(&child)); } } result } + let root_path = path.clone_subtree(); - let result = find_child_paths(&root_path); + + let result = find_child_paths_and_ident_pats(&root_path); let mut editor = SyntaxEditor::new(root_path.clone()); for sub_path in result { let new = self.transform_path(sub_path.syntax()); editor.replace(sub_path.syntax(), new); } + let update_sub_item = editor.finish().new_root().clone().clone_subtree(); - let item = find_child_paths(&update_sub_item); + let item = find_child_paths_and_ident_pats(&update_sub_item); let mut editor = SyntaxEditor::new(update_sub_item); for sub_path in item { - self.transform_path_(&mut editor, &sub_path); + self.transform_path_or_ident_pat(&mut editor, &sub_path); } editor.finish().new_root().clone() } + fn transform_path_or_ident_pat( + &self, + editor: &mut SyntaxEditor, + item: &Either, + ) -> Option<()> { + match item { + Either::Left(path) => self.transform_path_(editor, path), + Either::Right(ident_pat) => self.transform_ident_pat(editor, ident_pat), + } + } fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> { if path.qualifier().is_some() { @@ -515,6 +532,34 @@ impl Ctx<'_> { } Some(()) } + + fn transform_ident_pat( + &self, + editor: &mut SyntaxEditor, + ident_pat: &ast::IdentPat, + ) -> Option<()> { + let name = ident_pat.name()?; + + let temp_path = make::path_from_text(&name.text()); + + let resolution = self.source_scope.speculative_resolve(&temp_path)?; + + match resolution { + hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { + let cfg = ImportPathConfig { + prefer_no_std: false, + prefer_prelude: true, + prefer_absolute: false, + allow_unstable: true, + }; + let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; + let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update(); + editor.replace(ident_pat.syntax(), res.syntax()); + Some(()) + } + _ => None, + } + } } // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the From 49ebe9f849202561ac02ece6e8683a6602bf1f38 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 13 Aug 2025 05:23:58 +0300 Subject: [PATCH 0056/1900] Only import the item in "Unqualify method call" if needed --- .../rust-analyzer/crates/hir/src/semantics.rs | 5 + .../src/handlers/unqualify_method_call.rs | 118 +++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 05f06f97fdc26..6be44532d033f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2247,6 +2247,11 @@ impl<'db> SemanticsScope<'db> { } } + /// Checks if a trait is in scope, either because of an import or because we're in an impl of it. + pub fn can_use_trait_methods(&self, t: Trait) -> bool { + self.resolver.traits_in_scope(self.db).contains(&t.id) + } + /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index 1f89a3d5f17c9..a58b1da621c7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -1,3 +1,4 @@ +use hir::AsAssocItem; use syntax::{ TextRange, ast::{self, AstNode, HasArgList, prec::ExprPrecedence}, @@ -43,6 +44,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) let qualifier = path.qualifier()?; let method_name = path.segment()?.name_ref()?; + let scope = ctx.sema.scope(path.syntax())?; let res = ctx.sema.resolve_path(&path)?; let hir::PathResolution::Def(hir::ModuleDef::Function(fun)) = res else { return None }; if !fun.has_self_param(ctx.sema.db) { @@ -78,7 +80,14 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) edit.insert(close, ")"); } edit.replace(replace_comma, format!(".{method_name}(")); - add_import(qualifier, ctx, edit); + + if let Some(fun) = fun.as_assoc_item(ctx.db()) + && let Some(trait_) = fun.container_or_implemented_trait(ctx.db()) + && !scope.can_use_trait_methods(trait_) + { + // Only add an import for trait methods that are not already imported. + add_import(qualifier, ctx, edit); + } }, ) } @@ -235,4 +244,111 @@ impl S { fn assoc(S: S, S: S) {} } fn f() { S::assoc$0(S, S); }"#, ); } + + #[test] + fn inherent_method() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub struct Bar; + impl Bar { + pub fn bar(self) {} + } +} + +fn baz() { + foo::Bar::b$0ar(foo::Bar); +} + "#, + r#" +mod foo { + pub struct Bar; + impl Bar { + pub fn bar(self) {} + } +} + +fn baz() { + foo::Bar.bar(); +} + "#, + ); + } + + #[test] + fn trait_method_in_impl() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub trait Bar { + pub fn bar(self) {} + } +} + +struct Baz; +impl foo::Bar for Baz { + fn bar(self) { + foo::Bar::b$0ar(Baz); + } +} + "#, + r#" +mod foo { + pub trait Bar { + pub fn bar(self) {} + } +} + +struct Baz; +impl foo::Bar for Baz { + fn bar(self) { + Baz.bar(); + } +} + "#, + ); + } + + #[test] + fn trait_method_already_imported() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub struct Foo; + pub trait Bar { + pub fn bar(self) {} + } + impl Bar for Foo { + pub fn bar(self) {} + } +} + +use foo::Bar; + +fn baz() { + foo::Bar::b$0ar(foo::Foo); +} + "#, + r#" +mod foo { + pub struct Foo; + pub trait Bar { + pub fn bar(self) {} + } + impl Bar for Foo { + pub fn bar(self) {} + } +} + +use foo::Bar; + +fn baz() { + foo::Foo.bar(); +} + "#, + ); + } } From 8d247472e5f9612b4bf9668de937ad9c373b891c Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 4 Aug 2025 16:11:51 +0800 Subject: [PATCH 0057/1900] Merge Trait and TraitAlias handling --- .../rust-analyzer/crates/hir-def/src/attr.rs | 2 - .../rust-analyzer/crates/hir-def/src/db.rs | 20 +--- .../crates/hir-def/src/dyn_map.rs | 5 +- .../crates/hir-def/src/expr_store/lower.rs | 24 +---- .../crates/hir-def/src/expr_store/pretty.rs | 1 - .../src/expr_store/tests/signatures.rs | 2 - .../crates/hir-def/src/hir/generics.rs | 11 --- .../crates/hir-def/src/item_scope.rs | 1 - .../crates/hir-def/src/item_tree.rs | 8 -- .../crates/hir-def/src/item_tree/lower.rs | 16 +-- .../crates/hir-def/src/item_tree/pretty.rs | 8 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 14 --- .../crates/hir-def/src/nameres/assoc.rs | 10 +- .../crates/hir-def/src/nameres/collector.rs | 16 +-- .../crates/hir-def/src/resolver.rs | 13 +-- .../crates/hir-def/src/signatures.rs | 31 +----- .../rust-analyzer/crates/hir-def/src/src.rs | 9 +- .../crates/hir-ty/src/generics.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 1 - .../crates/hir-ty/src/lower/path.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 1 + .../crates/hir-ty/src/variance.rs | 5 - .../rust-analyzer/crates/hir/src/attrs.rs | 8 +- .../rust-analyzer/crates/hir/src/display.rs | 21 +--- .../rust-analyzer/crates/hir/src/from_id.rs | 5 - .../crates/hir/src/has_source.rs | 11 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 51 +--------- .../rust-analyzer/crates/hir/src/semantics.rs | 12 +-- .../hir/src/semantics/child_by_source.rs | 3 - .../crates/hir/src/semantics/source_to_def.rs | 16 +-- .../crates/hir/src/source_analyzer.rs | 4 +- .../rust-analyzer/crates/hir/src/symbols.rs | 3 - .../src/handlers/extract_module.rs | 1 - .../src/handlers/fix_visibility.rs | 4 - .../ide-assists/src/handlers/move_bounds.rs | 1 - .../ide-completion/src/completions/type.rs | 4 +- .../crates/ide-completion/src/context.rs | 1 - .../ide-completion/src/context/analysis.rs | 3 - .../crates/ide-completion/src/item.rs | 1 - .../crates/ide-completion/src/render.rs | 8 +- .../crates/ide-db/src/active_parameter.rs | 1 - .../rust-analyzer/crates/ide-db/src/defs.rs | 15 +-- .../crates/ide-db/src/documentation.rs | 3 +- .../ide-db/src/imports/import_assets.rs | 2 - .../rust-analyzer/crates/ide-db/src/lib.rs | 2 - .../rust-analyzer/crates/ide-db/src/rename.rs | 1 - .../rust-analyzer/crates/ide-db/src/search.rs | 1 - .../crates/ide-db/src/symbol_index.rs | 1 - .../rust-analyzer/crates/ide/src/doc_links.rs | 5 +- .../crates/ide/src/file_structure.rs | 3 +- .../crates/ide/src/folding_ranges.rs | 7 -- .../crates/ide/src/hover/render.rs | 1 - .../rust-analyzer/crates/ide/src/moniker.rs | 1 - .../rust-analyzer/crates/ide/src/move_item.rs | 1 - .../crates/ide/src/navigation_target.rs | 11 --- .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 1 - .../crates/ide/src/signature_help.rs | 4 - .../ide/src/syntax_highlighting/highlight.rs | 2 - .../ide/src/syntax_highlighting/inject.rs | 1 - .../ide/src/syntax_highlighting/tags.rs | 1 - .../test_data/highlight_strings.html | 4 +- .../crates/parser/src/grammar/items/traits.rs | 2 +- .../parser/src/syntax_kind/generated.rs | 2 - .../parser/inline/ok/trait_alias.rast | 2 +- .../inline/ok/trait_alias_where_clause.rast | 4 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +- .../rust-analyzer/crates/span/src/ast_id.rs | 3 +- .../rust-analyzer/crates/syntax/rust.ungram | 8 +- .../rust-analyzer/crates/syntax/src/ast.rs | 3 +- .../crates/syntax/src/ast/edit_in_place.rs | 36 +------ .../crates/syntax/src/ast/generated/nodes.rs | 97 +------------------ .../crates/syntax/src/ast/node_ext.rs | 45 --------- .../xtask/src/codegen/grammar.rs | 1 - 74 files changed, 68 insertions(+), 577 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 53250510f875c..b4fcfa11aea74 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -554,7 +554,6 @@ impl AttrsWithOwner { AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it), }, AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::TraitAliasId(it) => attrs_from_ast_id_loc(db, it), AttrDefId::MacroId(it) => match it { MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it), MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it), @@ -659,7 +658,6 @@ impl AttrsWithOwner { AttrDefId::StaticId(id) => any_has_attrs(db, id), AttrDefId::ConstId(id) => any_has_attrs(db, id), AttrDefId::TraitId(id) => any_has_attrs(db, id), - AttrDefId::TraitAliasId(id) => any_has_attrs(db, id), AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), AttrDefId::MacroId(id) => match id { MacroId::Macro2Id(id) => any_has_attrs(db, id), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index c67bb2422ac65..4e1d598623abe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -15,8 +15,8 @@ use crate::{ EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, - ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, - TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, + ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, + TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, attr::{Attrs, AttrsWithOwner}, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes, @@ -28,7 +28,7 @@ use crate::{ nameres::crate_def_map, signatures::{ ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature, - StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature, + StructSignature, TraitSignature, TypeAliasSignature, UnionSignature, }, tt, visibility::{self, Visibility}, @@ -69,9 +69,6 @@ pub trait InternDatabase: RootQueryDb { #[salsa::interned] fn intern_trait(&self, loc: TraitLoc) -> TraitId; - #[salsa::interned] - fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId; - #[salsa::interned] fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; @@ -152,11 +149,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { self.function_signature_with_source_map(e).0 } - #[salsa::tracked] - fn trait_alias_signature(&self, e: TraitAliasId) -> Arc { - self.trait_alias_signature_with_source_map(e).0 - } - #[salsa::tracked] fn type_alias_signature(&self, e: TypeAliasId) -> Arc { self.type_alias_signature_with_source_map(e).0 @@ -210,12 +202,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { e: FunctionId, ) -> (Arc, Arc); - #[salsa::invoke(TraitAliasSignature::query)] - fn trait_alias_signature_with_source_map( - &self, - e: TraitAliasId, - ) -> (Arc, Arc); - #[salsa::invoke(TypeAliasSignature::query)] fn type_alias_signature_with_source_map( &self, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 20018b61e5cc0..7d3a94b038330 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -33,8 +33,8 @@ pub mod keys { use crate::{ BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, + ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, + TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -48,7 +48,6 @@ pub mod keys { pub const IMPL: Key = Key::new(); pub const EXTERN_BLOCK: Key = Key::new(); pub const TRAIT: Key = Key::new(); - pub const TRAIT_ALIAS: Key = Key::new(); pub const STRUCT: Key = Key::new(); pub const UNION: Key = Key::new(); pub const ENUM: Key = Key::new(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 3b9281ffb9c12..3794cb18e9360 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -33,7 +33,7 @@ use tt::TextRange; use crate::{ AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, - ModuleDefId, ModuleId, TraitAliasId, TraitId, TypeAliasId, UnresolvedMacro, + ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, builtin_type::BuiltinUint, db::DefDatabase, expr_store::{ @@ -252,28 +252,6 @@ pub(crate) fn lower_trait( (store, source_map, params) } -pub(crate) fn lower_trait_alias( - db: &dyn DefDatabase, - module: ModuleId, - trait_syntax: InFile, - trait_id: TraitAliasId, -) -> (ExpressionStore, ExpressionStoreSourceMap, Arc) { - let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id); - let mut collector = generics::GenericParamsCollector::with_self_param( - &mut expr_collector, - trait_id.into(), - trait_syntax.value.type_bound_list(), - ); - collector.lower( - &mut expr_collector, - trait_syntax.value.generic_param_list(), - trait_syntax.value.where_clause(), - ); - let params = collector.finish(); - let (store, source_map) = expr_collector.store.finish(); - (store, source_map, params) -} - pub(crate) fn lower_type_alias( db: &dyn DefDatabase, module: ModuleId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index b81dcc1fe96df..5b9da3c5e6680 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -183,7 +183,6 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi } GenericDefId::ImplId(id) => format!("unimplemented {id:?}"), GenericDefId::StaticId(id) => format!("unimplemented {id:?}"), - GenericDefId::TraitAliasId(id) => format!("unimplemented {id:?}"), GenericDefId::TraitId(id) => format!("unimplemented {id:?}"), GenericDefId::TypeAliasId(id) => format!("unimplemented {id:?}"), } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index efb558a775816..b68674c7a74f4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -24,7 +24,6 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe ModuleDefId::ConstId(id) => id.into(), ModuleDefId::StaticId(id) => id.into(), ModuleDefId::TraitId(id) => id.into(), - ModuleDefId::TraitAliasId(id) => id.into(), ModuleDefId::TypeAliasId(id) => id.into(), ModuleDefId::EnumVariantId(_) => continue, ModuleDefId::BuiltinType(_) => continue, @@ -51,7 +50,6 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe GenericDefId::ImplId(_id) => (), GenericDefId::StaticId(_id) => (), - GenericDefId::TraitAliasId(_id) => (), GenericDefId::TraitId(_id) => (), GenericDefId::TypeAliasId(_id) => (), } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 94e683cb0f8fa..60cd66bf6b082 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -203,9 +203,6 @@ impl GenericParams { } GenericDefId::ImplId(impl_id) => db.impl_signature(impl_id).generic_params.clone(), GenericDefId::StaticId(_) => EMPTY.clone(), - GenericDefId::TraitAliasId(trait_alias_id) => { - db.trait_alias_signature(trait_alias_id).generic_params.clone() - } GenericDefId::TraitId(trait_id) => db.trait_signature(trait_id).generic_params.clone(), GenericDefId::TypeAliasId(type_alias_id) => { db.type_alias_signature(type_alias_id).generic_params.clone() @@ -246,10 +243,6 @@ impl GenericParams { let sig = db.static_signature(id); (EMPTY.clone(), sig.store.clone()) } - GenericDefId::TraitAliasId(id) => { - let sig = db.trait_alias_signature(id); - (sig.generic_params.clone(), sig.store.clone()) - } GenericDefId::TraitId(id) => { let sig = db.trait_signature(id); (sig.generic_params.clone(), sig.store.clone()) @@ -294,10 +287,6 @@ impl GenericParams { let (sig, sm) = db.static_signature_with_source_map(id); (EMPTY.clone(), sig.store.clone(), sm) } - GenericDefId::TraitAliasId(id) => { - let (sig, sm) = db.trait_alias_signature_with_source_map(id); - (sig.generic_params.clone(), sig.store.clone(), sm) - } GenericDefId::TraitId(id) => { let (sig, sm) = db.trait_signature_with_source_map(id); (sig.generic_params.clone(), sig.store.clone(), sm) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 8f526d1a2369a..77ed664f4443d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -872,7 +872,6 @@ impl PerNs { PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), - ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => PerNs::macros(mac, v, import), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index c633339857492..f35df8d3a7e11 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -276,7 +276,6 @@ enum SmallModItem { Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), } @@ -404,7 +403,6 @@ ModItemId -> Static in small_data -> ast::Static, Struct in small_data -> ast::Struct, Trait in small_data -> ast::Trait, - TraitAlias in small_data -> ast::TraitAlias, TypeAlias in small_data -> ast::TypeAlias, Union in small_data -> ast::Union, Use in big_data -> ast::Use, @@ -583,12 +581,6 @@ pub struct Trait { pub(crate) visibility: RawVisibilityId, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct TraitAlias { - pub name: Name, - pub(crate) visibility: RawVisibilityId, -} - #[derive(Debug, Clone, Eq, PartialEq)] pub struct Impl {} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 032b287cd6a82..454e06399583c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -23,7 +23,7 @@ use crate::{ BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem, - Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Static, Struct, StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, }, }; @@ -134,7 +134,6 @@ impl<'a> Ctx<'a> { ast::Item::Const(ast) => self.lower_const(ast).into(), ast::Item::Module(ast) => self.lower_module(ast)?.into(), ast::Item::Trait(ast) => self.lower_trait(ast)?.into(), - ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(), ast::Item::Impl(ast) => self.lower_impl(ast).into(), ast::Item::Use(ast) => self.lower_use(ast)?.into(), ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(), @@ -267,19 +266,6 @@ impl<'a> Ctx<'a> { Some(ast_id) } - fn lower_trait_alias( - &mut self, - trait_alias_def: &ast::TraitAlias, - ) -> Option> { - let name = trait_alias_def.name()?.as_name(); - let visibility = self.lower_visibility(trait_alias_def); - let ast_id = self.source_ast_id_map.ast_id(trait_alias_def); - - let alias = TraitAlias { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TraitAlias(alias)); - Some(ast_id) - } - fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId { let ast_id = self.source_ast_id_map.ast_id(impl_def); // Note that trait impls don't get implicit `Self` unlike traits, because here they are a diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 696174cb072bf..94a6cce3ce33a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -8,7 +8,7 @@ use crate::{ item_tree::{ Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static, - Struct, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, }, visibility::RawVisibility, }; @@ -250,12 +250,6 @@ impl Printer<'_> { self.print_visibility(*visibility); w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::TraitAlias(ast_id) => { - let TraitAlias { name, visibility } = &self.tree[ast_id]; - self.print_ast_id(ast_id.erase()); - self.print_visibility(*visibility); - wln!(self, "trait {} = ..;", name.display(self.db, self.edition)); - } ModItemId::Impl(ast_id) => { let Impl {} = &self.tree[ast_id]; self.print_ast_id(ast_id.erase()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index bdf8b453e2d65..1f5b1e023702f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -318,9 +318,6 @@ impl TraitId { } } -pub type TraitAliasLoc = ItemLoc; -impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias); - type TypeAliasLoc = AssocItemLoc; impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); @@ -742,7 +739,6 @@ pub enum ModuleDefId { ConstId(ConstId), StaticId(StaticId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), MacroId(MacroId), @@ -756,7 +752,6 @@ impl_from!( ConstId, StaticId, TraitId, - TraitAliasId, TypeAliasId, BuiltinType for ModuleDefId @@ -862,7 +857,6 @@ pub enum GenericDefId { // More importantly, this completes the set of items that contain type references // which is to be used by the signature expression store in the future. StaticId(StaticId), - TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), } @@ -872,7 +866,6 @@ impl_from!( FunctionId, ImplId, StaticId, - TraitAliasId, TraitId, TypeAliasId for GenericDefId @@ -902,7 +895,6 @@ impl GenericDefId { GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it), - GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ConstId(it) => (it.lookup(db).id.file_id, None), GenericDefId::StaticId(it) => (it.lookup(db).id.file_id, None), @@ -978,7 +970,6 @@ pub enum AttrDefId { StaticId(StaticId), ConstId(ConstId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), MacroId(MacroId), ImplId(ImplId), @@ -997,7 +988,6 @@ impl_from!( ConstId, FunctionId, TraitId, - TraitAliasId, TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, @@ -1020,7 +1010,6 @@ impl TryFrom for AttrDefId { ModuleDefId::StaticId(it) => Ok(it.into()), ModuleDefId::TraitId(it) => Ok(it.into()), ModuleDefId::TypeAliasId(it) => Ok(it.into()), - ModuleDefId::TraitAliasId(id) => Ok(id.into()), ModuleDefId::MacroId(id) => Ok(id.into()), ModuleDefId::BuiltinType(_) => Err(()), } @@ -1266,7 +1255,6 @@ impl HasModule for GenericDefId { GenericDefId::FunctionId(it) => it.module(db), GenericDefId::AdtId(it) => it.module(db), GenericDefId::TraitId(it) => it.module(db), - GenericDefId::TraitAliasId(it) => it.module(db), GenericDefId::TypeAliasId(it) => it.module(db), GenericDefId::ImplId(it) => it.module(db), GenericDefId::ConstId(it) => it.module(db), @@ -1286,7 +1274,6 @@ impl HasModule for AttrDefId { AttrDefId::StaticId(it) => it.module(db), AttrDefId::ConstId(it) => it.module(db), AttrDefId::TraitId(it) => it.module(db), - AttrDefId::TraitAliasId(it) => it.module(db), AttrDefId::TypeAliasId(it) => it.module(db), AttrDefId::ImplId(it) => it.module(db), AttrDefId::ExternBlockId(it) => it.module(db), @@ -1316,7 +1303,6 @@ impl ModuleDefId { ModuleDefId::ConstId(id) => id.module(db), ModuleDefId::StaticId(id) => id.module(db), ModuleDefId::TraitId(id) => id.module(db), - ModuleDefId::TraitAliasId(id) => id.module(db), ModuleDefId::TypeAliasId(id) => id.module(db), ModuleDefId::MacroId(id) => id.module(db), ModuleDefId::BuiltinType(_) => return None, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 07210df887369..8d2a386de8ecc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -51,10 +51,18 @@ impl TraitItems { tr: TraitId, ) -> (TraitItems, DefDiagnostics) { let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ast_id_map = db.ast_id_map(ast_id.file_id); + let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); + if source.eq_token().is_some() { + // FIXME(trait-alias) probably needs special handling here + return ( + TraitItems { macro_calls: ThinVec::new(), items: Box::default() }, + DefDiagnostics::new(vec![]), + ); + } let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr), ast_id.file_id); - let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db); let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list()); (TraitItems { macro_calls, items }, DefDiagnostics::new(diagnostics)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 267c4451b9d71..07e61718222bf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -30,7 +30,7 @@ use crate::{ ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, @@ -1954,20 +1954,6 @@ impl ModCollector<'_, '_> { false, ); } - ModItemId::TraitAlias(id) => { - let it = &self.item_tree[id]; - - let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); - update_def( - self.def_collector, - TraitAliasLoc { container: module, id: InFile::new(self.file_id(), id) } - .intern(db) - .into(), - &it.name, - vis, - false, - ); - } ModItemId::TypeAlias(id) => { let it = &self.item_tree[id]; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index a10990e6a8f9f..698292c2fbea4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -20,7 +20,7 @@ use crate::{ EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, + TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, builtin_type::BuiltinType, db::DefDatabase, expr_store::{ @@ -105,7 +105,6 @@ pub enum TypeNs { TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), TraitId(TraitId), - TraitAliasId(TraitAliasId), ModuleId(ModuleId), } @@ -1150,7 +1149,6 @@ impl<'db> ModuleItemMap<'db> { let ty = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), @@ -1195,7 +1193,6 @@ fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_)) | ModuleDefId::TraitId(_) - | ModuleDefId::TraitAliasId(_) | ModuleDefId::TypeAliasId(_) | ModuleDefId::BuiltinType(_) | ModuleDefId::MacroId(_) @@ -1214,7 +1211,6 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::ModuleId(it) => TypeNs::ModuleId(it), @@ -1320,12 +1316,6 @@ impl HasResolver for TraitId { } } -impl HasResolver for TraitAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { - lookup_resolver(db, self).push_generic_params_scope(db, self.into()) - } -} - impl + Copy> HasResolver for T { fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let def = self.into(); @@ -1410,7 +1400,6 @@ impl HasResolver for GenericDefId { GenericDefId::FunctionId(inner) => inner.resolver(db), GenericDefId::AdtId(adt) => adt.resolver(db), GenericDefId::TraitId(inner) => inner.resolver(db), - GenericDefId::TraitAliasId(inner) => inner.resolver(db), GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 598b2c0314a94..906c1c0e9ff14 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -20,15 +20,13 @@ use triomphe::Arc; use crate::{ ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, - VariantId, + ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, attr::Attrs, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, lower::{ - ExprCollector, lower_function, lower_generic_params, lower_trait, lower_trait_alias, - lower_type_alias, + ExprCollector, lower_function, lower_generic_params, lower_trait, lower_type_alias, }, }, hir::{ExprId, PatId, generics::GenericParams}, @@ -469,31 +467,6 @@ impl TraitSignature { } } -#[derive(Debug, PartialEq, Eq)] -pub struct TraitAliasSignature { - pub name: Name, - pub generic_params: Arc, - pub store: Arc, -} - -impl TraitAliasSignature { - pub fn query( - db: &dyn DefDatabase, - id: TraitAliasId, - ) -> (Arc, Arc) { - let loc = id.lookup(db); - - let source = loc.source(db); - let name = as_name_opt(source.value.name()); - let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id); - - ( - Arc::new(TraitAliasSignature { generic_params, store: Arc::new(store), name }), - Arc::new(source_map), - ) - } -} - bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct FnFlags: u16 { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index aa373a27b0d52..367b543cf9080 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -71,7 +71,7 @@ impl HasChildSource> for UseId { } impl HasChildSource for GenericDefId { - type Value = Either; + type Value = Either; fn child_source( &self, db: &dyn DefDatabase, @@ -89,12 +89,7 @@ impl HasChildSource for GenericDefId { GenericDefId::TraitId(id) => { let trait_ref = id.lookup(db).source(db).value; let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref))); - } - GenericDefId::TraitAliasId(id) => { - let alias = id.lookup(db).source(db).value; - let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias))); + params.insert(idx, Either::Right(trait_ref)); } _ => {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index e6470e5272d05..412b3ac425028 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -273,7 +273,7 @@ impl Generics { pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { match def { - GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + GenericDefId::TraitId(_) => { let params = db.generic_params(def); params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) } @@ -295,8 +295,7 @@ pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Opt GenericDefId::StaticId(_) | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) - | GenericDefId::ImplId(_) - | GenericDefId::TraitAliasId(_) => return None, + | GenericDefId::ImplId(_) => return None, }; match container { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 7a11ec660d04b..d778cc0e30ed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1790,7 +1790,6 @@ impl<'db> InferenceContext<'db> { TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic (self.err_ty(), None) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 9519c38eeddfd..fb85909d7f41e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -220,10 +220,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }; return (ty, None); } - TypeNs::TraitAliasId(_) => { - // FIXME(trait_alias): Implement trait alias. - return (TyKind::Error.intern(Interner), None); - } TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) @@ -311,8 +307,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) => {} + | TypeNs::TraitId(_) => {} } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index fc31973022bdd..c5f78e2f3cecb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -9,6 +9,7 @@ mod never_type; mod patterns; mod regression; mod simple; +mod trait_aliases; mod traits; mod type_alias_impl_traits; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 08a215fecf623..d8a058533f761 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -990,7 +990,6 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); ModuleDefId::AdtId(it) => it.into(), ModuleDefId::ConstId(it) => it.into(), ModuleDefId::TraitId(it) => it.into(), - ModuleDefId::TraitAliasId(it) => it.into(), ModuleDefId::TypeAliasId(it) => it.into(), _ => return, }) @@ -1021,10 +1020,6 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } - GenericDefId::TraitAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } GenericDefId::TypeAliasId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index c8645b6282392..928753ef0edeb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -19,7 +19,7 @@ use hir_ty::{db::HirDatabase, method_resolution}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, - Struct, Trait, TraitAlias, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { @@ -48,7 +48,6 @@ impl_has_attrs![ (Static, StaticId), (Const, ConstId), (Trait, TraitId), - (TraitAlias, TraitAliasId), (TypeAlias, TypeAliasId), (Macro, MacroId), (Function, FunctionId), @@ -137,7 +136,6 @@ fn resolve_doc_path_on_( AttrDefId::StaticId(it) => it.resolver(db), AttrDefId::ConstId(it) => it.resolver(db), AttrDefId::TraitId(it) => it.resolver(db), - AttrDefId::TraitAliasId(it) => it.resolver(db), AttrDefId::TypeAliasId(it) => it.resolver(db), AttrDefId::ImplId(it) => it.resolver(db), AttrDefId::ExternBlockId(it) => it.resolver(db), @@ -216,10 +214,6 @@ fn resolve_assoc_or_field( DocLinkDef::ModuleDef(def) }); } - TypeNs::TraitAliasId(_) => { - // XXX: Do these get resolved? - return None; - } TypeNs::ModuleId(_) => { return None; } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 2960ebedf3806..b9c5131ffbb55 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -23,8 +23,8 @@ use itertools::Itertools; use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, - Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitAlias, TraitRef, TupleField, - TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, TyBuilder, + Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -751,6 +751,7 @@ impl HirDisplay for TraitRef<'_> { impl HirDisplay for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + // FIXME(trait-alias) needs special handling to print the equal sign write_trait_header(self, f)?; let def_id = GenericDefId::TraitId(self.id); let has_where_clause = write_where_clause(def_id, f)?; @@ -802,22 +803,6 @@ fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), Hi Ok(()) } -impl HirDisplay for TraitAlias { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; - let data = f.db.trait_alias_signature(self.id); - write!(f, "trait {}", data.name.display(f.db, f.edition()))?; - let def_id = GenericDefId::TraitAliasId(self.id); - write_generic_params(def_id, f)?; - f.write_str(" = ")?; - // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e. - // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might - // be less readable. - write_where_clause(def_id, f)?; - Ok(()) - } -} - impl HirDisplay for TypeAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index c6446693df3e4..bc025c5ef5cf1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -37,7 +37,6 @@ from_id![ (hir_def::EnumId, crate::Enum), (hir_def::TypeAliasId, crate::TypeAlias), (hir_def::TraitId, crate::Trait), - (hir_def::TraitAliasId, crate::TraitAlias), (hir_def::StaticId, crate::Static), (hir_def::ConstId, crate::Const), (hir_def::FunctionId, crate::Function), @@ -113,7 +112,6 @@ impl From for ModuleDef { ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), - ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()), ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()), ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()), @@ -131,7 +129,6 @@ impl From for ModuleDefId { ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), - ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()), ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()), ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()), @@ -177,7 +174,6 @@ impl From for GenericDefId { GenericDef::Function(it) => GenericDefId::FunctionId(it.id), GenericDef::Adt(it) => GenericDefId::AdtId(it.into()), GenericDef::Trait(it) => GenericDefId::TraitId(it.id), - GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id), @@ -192,7 +188,6 @@ impl From for GenericDef { GenericDefId::FunctionId(it) => GenericDef::Function(it.into()), GenericDefId::AdtId(it) => GenericDef::Adt(it.into()), GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), - GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()), diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 4767d4792e718..ae82275e38736 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -14,8 +14,7 @@ use tt::TextRange; use crate::{ Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, - Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, - db::HirDatabase, + Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase, }; pub trait HasSource { @@ -168,12 +167,6 @@ impl HasSource for Trait { Some(self.id.lookup(db).source(db)) } } -impl HasSource for TraitAlias { - type Ast = ast::TraitAlias; - fn source(self, db: &dyn HirDatabase) -> Option> { - Some(self.id.lookup(db).source(db)) - } -} impl HasSource for TypeAlias { type Ast = ast::TypeAlias; fn source(self, db: &dyn HirDatabase) -> Option> { @@ -202,7 +195,7 @@ impl HasSource for Impl { } impl HasSource for TypeOrConstParam { - type Ast = Either; + type Ast = Either; fn source(self, db: &dyn HirDatabase) -> Option> { let child_source = self.id.parent.child_source(db); child_source.map(|it| it.get(self.id.local_id).cloned()).transpose() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 475906ae51a11..4eb50c1c31c52 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -52,7 +52,7 @@ use hir_def::{ CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, SyntheticSyntax, - TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, @@ -333,7 +333,6 @@ pub enum ModuleDef { Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), BuiltinType(BuiltinType), Macro(Macro), @@ -346,7 +345,6 @@ impl_from!( Const, Static, Trait, - TraitAlias, TypeAlias, BuiltinType, Macro @@ -373,7 +371,6 @@ impl ModuleDef { ModuleDef::Const(it) => Some(it.module(db)), ModuleDef::Static(it) => Some(it.module(db)), ModuleDef::Trait(it) => Some(it.module(db)), - ModuleDef::TraitAlias(it) => Some(it.module(db)), ModuleDef::TypeAlias(it) => Some(it.module(db)), ModuleDef::Macro(it) => Some(it.module(db)), ModuleDef::BuiltinType(_) => None, @@ -402,7 +399,6 @@ impl ModuleDef { ModuleDef::Const(it) => it.name(db)?, ModuleDef::Adt(it) => it.name(db), ModuleDef::Trait(it) => it.name(db), - ModuleDef::TraitAlias(it) => it.name(db), ModuleDef::Function(it) => it.name(db), ModuleDef::Variant(it) => it.name(db), ModuleDef::TypeAlias(it) => it.name(db), @@ -425,7 +421,6 @@ impl ModuleDef { Adt::Union(it) => it.id.into(), }, ModuleDef::Trait(it) => it.id.into(), - ModuleDef::TraitAlias(it) => it.id.into(), ModuleDef::Function(it) => it.id.into(), ModuleDef::TypeAlias(it) => it.id.into(), ModuleDef::Module(it) => it.id.into(), @@ -465,7 +460,6 @@ impl ModuleDef { ModuleDef::Module(_) | ModuleDef::Adt(_) | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) | ModuleDef::TypeAlias(_) | ModuleDef::Macro(_) | ModuleDef::BuiltinType(_) => None, @@ -478,7 +472,6 @@ impl ModuleDef { ModuleDef::Function(it) => Some(it.into()), ModuleDef::Adt(it) => Some(it.into()), ModuleDef::Trait(it) => Some(it.into()), - ModuleDef::TraitAlias(it) => Some(it.into()), ModuleDef::TypeAlias(it) => Some(it.into()), ModuleDef::Module(_) | ModuleDef::Variant(_) @@ -498,7 +491,6 @@ impl ModuleDef { ModuleDef::Const(it) => it.attrs(db), ModuleDef::Static(it) => it.attrs(db), ModuleDef::Trait(it) => it.attrs(db), - ModuleDef::TraitAlias(it) => it.attrs(db), ModuleDef::TypeAlias(it) => it.attrs(db), ModuleDef::Macro(it) => it.attrs(db), ModuleDef::BuiltinType(_) => return None, @@ -524,7 +516,6 @@ impl HasVisibility for ModuleDef { ModuleDef::Const(it) => it.visibility(db), ModuleDef::Static(it) => it.visibility(db), ModuleDef::Trait(it) => it.visibility(db), - ModuleDef::TraitAlias(it) => it.visibility(db), ModuleDef::TypeAlias(it) => it.visibility(db), ModuleDef::Variant(it) => it.visibility(db), ModuleDef::Macro(it) => it.visibility(db), @@ -3045,29 +3036,6 @@ impl HasVisibility for Trait { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TraitAlias { - pub(crate) id: TraitAliasId, -} - -impl TraitAlias { - pub fn module(self, db: &dyn HirDatabase) -> Module { - Module { id: self.id.lookup(db).container } - } - - pub fn name(self, db: &dyn HirDatabase) -> Name { - db.trait_alias_signature(self.id).name.clone() - } -} - -impl HasVisibility for TraitAlias { - fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - let loc = self.id.lookup(db); - let source = loc.source(db); - visibility_from_ast(db, self.id, source.map(|src| src.visibility())) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAlias { pub(crate) id: TypeAliasId, @@ -3690,7 +3658,6 @@ pub enum GenericDef { Function(Function), Adt(Adt), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Impl(Impl), // consts can have type parameters from their parents (i.e. associated consts of traits) @@ -3701,7 +3668,6 @@ impl_from!( Function, Adt(Struct, Enum, Union), Trait, - TraitAlias, TypeAlias, Impl, Const, @@ -3751,7 +3717,6 @@ impl GenericDef { GenericDef::Function(it) => it.id.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.id.into(), - GenericDef::TraitAlias(it) => it.id.into(), GenericDef::TypeAlias(it) => it.id.into(), GenericDef::Impl(it) => it.id.into(), GenericDef::Const(it) => it.id.into(), @@ -3776,7 +3741,6 @@ impl GenericDef { GenericDefId::FunctionId(it) => db.function_signature_with_source_map(it).1, GenericDefId::ImplId(it) => db.impl_signature_with_source_map(it).1, GenericDefId::StaticId(_) => return, - GenericDefId::TraitAliasId(it) => db.trait_alias_signature_with_source_map(it).1, GenericDefId::TraitId(it) => db.trait_signature_with_source_map(it).1, GenericDefId::TypeAliasId(it) => db.type_alias_signature_with_source_map(it).1, }; @@ -3813,7 +3777,6 @@ impl GenericDef { GenericDef::Adt(Adt::Enum(_)) => "enum", GenericDef::Adt(Adt::Union(_)) => "union", GenericDef::Trait(_) => "trait", - GenericDef::TraitAlias(_) => "trait alias", GenericDef::TypeAlias(_) => "type alias", GenericDef::Impl(_) => "impl", GenericDef::Const(_) => "constant", @@ -6401,12 +6364,6 @@ impl HasCrate for Trait { } } -impl HasCrate for TraitAlias { - fn krate(&self, db: &dyn HirDatabase) -> Crate { - self.module(db).krate() - } -} - impl HasCrate for Static { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.module(db).krate() @@ -6500,12 +6457,6 @@ impl HasContainer for Trait { } } -impl HasContainer for TraitAlias { - fn container(&self, db: &dyn HirDatabase) -> ItemContainer { - ItemContainer::Module(Module { id: self.id.lookup(db).container }) - } -} - impl HasContainer for ExternBlock { fn container(&self, db: &dyn HirDatabase) -> ItemContainer { ItemContainer::Module(Module { id: self.id.lookup(db).container }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 05f06f97fdc26..b43165fd8ad70 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -46,8 +46,8 @@ use crate::{ Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type, + TypeAlias, TypeParam, Union, Variant, VariantDef, db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{SourceAnalyzer, name_hygiene, resolve_hir_path}, @@ -85,8 +85,7 @@ impl PathResolution { | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) - | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_), + | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) @@ -343,10 +342,6 @@ impl Semantics<'_, DB> { self.imp.to_def(s) } - pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option { - self.imp.to_def(t) - } - pub fn to_trait_def(&self, t: &ast::Trait) -> Option { self.imp.to_def(t) } @@ -2138,7 +2133,6 @@ to_def_impls![ (crate::Enum, ast::Enum, enum_to_def), (crate::Union, ast::Union, union_to_def), (crate::Trait, ast::Trait, trait_to_def), - (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def), (crate::Impl, ast::Impl, impl_to_def), (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), (crate::Const, ast::Const, const_to_def), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index e7db93d375d33..5019a5987e513 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -154,9 +154,6 @@ impl ChildBySource for ItemScope { } ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC), ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT), - ModuleDefId::TraitAliasId(id) => { - insert_item_loc(db, map, file_id, id, keys::TRAIT_ALIAS) - } ModuleDefId::AdtId(adt) => match adt { AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT), AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 71ee0f6938960..44df4d8fc8c80 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -89,8 +89,8 @@ use either::Either; use hir_def::{ AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, - UnionId, UseId, VariantId, + Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, + UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -252,12 +252,6 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option { self.to_def(src, keys::TRAIT) } - pub(super) fn trait_alias_to_def( - &mut self, - src: InFile<&ast::TraitAlias>, - ) -> Option { - self.to_def(src, keys::TRAIT_ALIAS) - } pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option { self.to_def(src, keys::IMPL) } @@ -555,9 +549,6 @@ impl SourceToDefCtx<'_, '_> { } ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into), ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into), - ast::Item::TraitAlias(it) => { - this.trait_alias_to_def(InFile::new(file_id, it)).map(Into::into) - } ast::Item::TypeAlias(it) => { this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into) } @@ -636,9 +627,6 @@ impl SourceToDefCtx<'_, '_> { ast::Item::TypeAlias(it) => ChildContainer::GenericDefId( self.type_alias_to_def(container.with_value(it))?.into(), ), - ast::Item::TraitAlias(it) => ChildContainer::GenericDefId( - self.trait_alias_to_def(container.with_value(it))?.into(), - ), ast::Item::Struct(it) => { let def = self.struct_to_def(container.with_value(it))?; let is_in_body = it.field_list().is_some_and(|it| { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index d25fb1d8cdb7e..126392af4619a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -10,7 +10,7 @@ use std::iter::{self, once}; use crate::{ Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, - TraitAlias, TupleField, Type, TypeAlias, Variant, + TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -1587,7 +1587,6 @@ fn resolve_hir_path_( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved { @@ -1737,7 +1736,6 @@ fn resolve_hir_path_qualifier( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved { diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index dca10193e29bf..d8c624e5c6896 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -148,9 +148,6 @@ impl<'a> SymbolCollector<'a> { let trait_do_not_complete = this.push_decl(id, name, false, None); this.collect_from_trait(id, trait_do_not_complete); } - ModuleDefId::TraitAliasId(id) => { - this.push_decl(id, name, false, None); - } ModuleDefId::TypeAliasId(id) => { this.push_decl(id, name, false, None); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index e783b8693456c..dad19bfb8a2c8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -613,7 +613,6 @@ impl Module { | Definition::Const(_) | Definition::Static(_) | Definition::Trait(_) - | Definition::TraitAlias(_) | Definition::TypeAlias(_) ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 3badc17d01af4..55de537debf3d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -136,10 +136,6 @@ fn target_data_for_def( target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? } - hir::ModuleDef::TraitAlias(t) => { - target_name = Some(t.name(db)); - offset_target_and_file_id(db, t)? - } hir::ModuleDef::TypeAlias(t) => { target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index a9df6f6fc3669..e5425abab097a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -55,7 +55,6 @@ pub(crate) fn move_bounds_to_where_clause( match parent { ast::Fn(it) => it.get_or_create_where_clause(), ast::Trait(it) => it.get_or_create_where_clause(), - ast::TraitAlias(it) => it.get_or_create_where_clause(), ast::Impl(it) => it.get_or_create_where_clause(), ast::Enum(it) => it.get_or_create_where_clause(), ast::Struct(it) => it.get_or_create_where_clause(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 7c38c7d8ce44f..fc27cbd65a1ee 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -37,9 +37,7 @@ pub(crate) fn complete_type_path( true } // Type things are fine - ScopeDef::ModuleDef( - BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_), - ) + ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_)) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index cfd7f80d40b30..28c00cde126ca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -525,7 +525,6 @@ impl CompletionContext<'_> { hir::ModuleDef::Const(it) => self.is_visible(it), hir::ModuleDef::Static(it) => self.is_visible(it), hir::ModuleDef::Trait(it) => self.is_visible(it), - hir::ModuleDef::TraitAlias(it) => self.is_visible(it), hir::ModuleDef::TypeAlias(it) => self.is_visible(it), hir::ModuleDef::Macro(it) => self.is_visible(it), hir::ModuleDef::BuiltinType(_) => Visible::Yes, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 9d246017131a0..23db63c1a95fe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1037,9 +1037,6 @@ fn classify_name_ref<'db>( sema.source(trait_)?.value.generic_param_list() } } - hir::ModuleDef::TraitAlias(trait_) => { - sema.source(trait_)?.value.generic_param_list() - } hir::ModuleDef::TypeAlias(ty_) => { sema.source(ty_)?.value.generic_param_list() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f27cd07816657..5fb9dc93c93da 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -399,7 +399,6 @@ impl CompletionItemKind { SymbolKind::Struct => "st", SymbolKind::ToolModule => "tm", SymbolKind::Trait => "tt", - SymbolKind::TraitAlias => "tr", SymbolKind::TypeAlias => "ta", SymbolKind::TypeParam => "tp", SymbolKind::Union => "un", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 3d7a4067c2cd0..7d23f9d14c66f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -486,10 +486,7 @@ fn render_resolution_path( | ScopeDef::Label(_) | ScopeDef::Unknown | ScopeDef::ModuleDef( - ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) - | ModuleDef::Module(_) - | ModuleDef::TypeAlias(_), + ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_), ) => (), }; @@ -542,9 +539,6 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), - ScopeDef::ModuleDef(TraitAlias(..)) => { - CompletionItemKind::SymbolKind(SymbolKind::TraitAlias) - } ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 9edfc113f764c..4fb7d142ed5f1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -125,7 +125,6 @@ pub fn generic_def_for_node( hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(), - hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => { variant = Some(it); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 2a4fcf6a2e5f7..61a21ccf2f775 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -16,7 +16,7 @@ use hir::{ ExternCrateDecl, Field, Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility, HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, - TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, + TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; use stdx::{format_to, impl_from}; @@ -40,7 +40,6 @@ pub enum Definition { Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), SelfType(Impl), GenericParam(GenericParam), @@ -83,7 +82,6 @@ impl Definition { Definition::Const(it) => it.module(db), Definition::Static(it) => it.module(db), Definition::Trait(it) => it.module(db), - Definition::TraitAlias(it) => it.module(db), Definition::TypeAlias(it) => it.module(db), Definition::Variant(it) => it.module(db), Definition::SelfType(it) => it.module(db), @@ -122,7 +120,6 @@ impl Definition { Definition::Const(it) => container_to_definition(it.container(db)), Definition::Static(it) => container_to_definition(it.container(db)), Definition::Trait(it) => container_to_definition(it.container(db)), - Definition::TraitAlias(it) => container_to_definition(it.container(db)), Definition::TypeAlias(it) => container_to_definition(it.container(db)), Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()), Definition::SelfType(it) => Some(it.module(db).into()), @@ -151,7 +148,6 @@ impl Definition { Definition::Const(it) => it.visibility(db), Definition::Static(it) => it.visibility(db), Definition::Trait(it) => it.visibility(db), - Definition::TraitAlias(it) => it.visibility(db), Definition::TypeAlias(it) => it.visibility(db), Definition::Variant(it) => it.visibility(db), Definition::ExternCrateDecl(it) => it.visibility(db), @@ -185,7 +181,6 @@ impl Definition { Definition::Const(it) => it.name(db)?, Definition::Static(it) => it.name(db), Definition::Trait(it) => it.name(db), - Definition::TraitAlias(it) => it.name(db), Definition::TypeAlias(it) => it.name(db), Definition::BuiltinType(it) => it.name(), Definition::TupleField(it) => it.name(), @@ -230,7 +225,6 @@ impl Definition { Definition::Const(it) => it.docs_with_rangemap(db), Definition::Static(it) => it.docs_with_rangemap(db), Definition::Trait(it) => it.docs_with_rangemap(db), - Definition::TraitAlias(it) => it.docs_with_rangemap(db), Definition::TypeAlias(it) => { it.docs_with_rangemap(db).or_else(|| { // docs are missing, try to fall back to the docs of the aliased item. @@ -321,7 +315,6 @@ impl Definition { Definition::Const(it) => it.display(db, display_target).to_string(), Definition::Static(it) => it.display(db, display_target).to_string(), Definition::Trait(it) => it.display(db, display_target).to_string(), - Definition::TraitAlias(it) => it.display(db, display_target).to_string(), Definition::TypeAlias(it) => it.display(db, display_target).to_string(), Definition::BuiltinType(it) => { it.name().display(db, display_target.edition).to_string() @@ -589,7 +582,6 @@ impl<'db> NameClass<'db> { ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?), ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?), ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?), - ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?), ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?), ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)), ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)), @@ -895,7 +887,7 @@ impl<'db> NameRefClass<'db> { } impl_from!( - Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local, + Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local, GenericParam, Label, Macro, ExternCrateDecl for Definition ); @@ -975,7 +967,6 @@ impl From for Definition { ModuleDef::Const(it) => Definition::Const(it), ModuleDef::Static(it) => Definition::Static(it), ModuleDef::Trait(it) => Definition::Trait(it), - ModuleDef::TraitAlias(it) => Definition::TraitAlias(it), ModuleDef::TypeAlias(it) => Definition::TypeAlias(it), ModuleDef::Macro(it) => Definition::Macro(it), ModuleDef::BuiltinType(it) => Definition::BuiltinType(it), @@ -1017,7 +1008,6 @@ impl From for Definition { GenericDef::Function(it) => it.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.into(), - GenericDef::TraitAlias(it) => it.into(), GenericDef::TypeAlias(it) => it.into(), GenericDef::Impl(it) => it.into(), GenericDef::Const(it) => it.into(), @@ -1033,7 +1023,6 @@ impl TryFrom for GenericDef { Definition::Function(it) => Ok(it.into()), Definition::Adt(it) => Ok(it.into()), Definition::Trait(it) => Ok(it.into()), - Definition::TraitAlias(it) => Ok(it.into()), Definition::TypeAlias(it) => Ok(it.into()), Definition::SelfType(it) => Ok(it.into()), Definition::Const(it) => Ok(it.into()), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index 30c355f8b3f93..cab19aadfd010 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -195,8 +195,7 @@ macro_rules! impl_has_docs { } impl_has_docs![ - Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module, - Impl, Crate, + Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate, ]; macro_rules! impl_has_docs_enum { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 9f35988924b92..1012f993344fe 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -475,8 +475,6 @@ fn validate_resolvable( } // FIXME ModuleDef::Trait(_) => return None, - // FIXME - ModuleDef::TraitAlias(_) => return None, ModuleDef::TypeAlias(alias) => alias.ty(db), ModuleDef::BuiltinType(builtin) => builtin.ty(db), ModuleDef::Adt(adt) => adt.ty(db), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 49f7f63a04a42..1f074de4cd7d2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -273,7 +273,6 @@ pub enum SymbolKind { Struct, ToolModule, Trait, - TraitAlias, TypeAlias, TypeParam, Union, @@ -306,7 +305,6 @@ impl From for SymbolKind { hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum, hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union, hir::ModuleDef::Trait(..) => SymbolKind::Trait, - hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias, hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias, hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias, } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 424b27a398b20..a8800c142a22e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -163,7 +163,6 @@ impl Definition { Definition::Const(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Static(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Trait(it) => name_range(it, sema).and_then(syn_ctx_is_root), - Definition::TraitAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::TypeAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Local(it) => { name_range(it.primary_source(sema.db), sema).and_then(syn_ctx_is_root) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index abd4dc8300b39..f1d076e874d5c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -352,7 +352,6 @@ impl Definition { hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()), - hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 9c4e6f5cbf82f..78ade30c7e3ed 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -357,7 +357,6 @@ impl Query { hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) - | hir::ModuleDef::TraitAlias(..) | hir::ModuleDef::Trait(..) ); if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index d47cc65079384..c197d559aa89a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -225,7 +225,6 @@ pub(crate) fn resolve_doc_path_for_def( Definition::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Static(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Trait(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Macro(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Field(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), @@ -671,11 +670,9 @@ fn filename_and_frag_for_def( None => String::from("index.html"), }, Definition::Trait(t) => { + // FIXME(trait-alias): url should be traitalias. for aliases format!("trait.{}.html", t.name(db).as_str()) } - Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).as_str()) - } Definition::TypeAlias(t) => { format!("type.{}.html", t.name(db).as_str()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index ac15af0334fc8..d5c6eaca97448 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -162,7 +162,6 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)), ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)), ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)), - ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)), ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)), ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)), @@ -553,7 +552,7 @@ fn let_statements() { navigation_range: 251..256, node_range: 245..262, kind: SymbolKind( - TraitAlias, + Trait, ), detail: None, deprecated: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index ac64413effebf..3969490e8dcf5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -29,7 +29,6 @@ pub enum FoldKind { Consts, Statics, TypeAliases, - TraitAliases, ExternCrates, // endregion: item runs } @@ -147,11 +146,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { res.push(Fold { range, kind: FoldKind::TypeAliases }) } }, - ast::TraitAlias(alias) => { - if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::TraitAliases }) - } - }, ast::ExternCrate(extern_crate) => { if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::ExternCrates }) @@ -351,7 +345,6 @@ mod tests { FoldKind::ReturnType => "returntype", FoldKind::MatchArm => "matcharm", FoldKind::Function => "function", - FoldKind::TraitAliases => "traitaliases", FoldKind::ExternCrates => "externcrates", }; assert_eq!(kind, &attr.unwrap()); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 1c0272cdfacd1..e4c7d42ffe3b1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -401,7 +401,6 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - Definition::GenericParam(generic_param) => match generic_param.parent() { hir::GenericDef::Adt(it) => Some(it.name(db)), hir::GenericDef::Trait(it) => Some(it.name(db)), - hir::GenericDef::TraitAlias(it) => Some(it.name(db)), hir::GenericDef::TypeAlias(it) => Some(it.name(db)), hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 795c1f2ca3c0b..f1aa03c8f2672 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -209,7 +209,6 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Definition::Const(..) => Constant, Definition::Static(..) => StaticVariable, Definition::Trait(..) => Trait, - Definition::TraitAlias(..) => Trait, Definition::TypeAlias(it) => { if it.as_assoc_item(db).is_some() { AssociatedType diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index f3bb3df1cd8d7..b5d47c83a5543 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -72,7 +72,6 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - SyntaxKind::MACRO_CALL, SyntaxKind::TYPE_ALIAS, SyntaxKind::TRAIT, - SyntaxKind::TRAIT_ALIAS, SyntaxKind::IMPL, SyntaxKind::MACRO_DEF, SyntaxKind::STRUCT, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 7dc18141bdbc1..61b4de0e0e396 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -226,9 +226,6 @@ impl TryToNav for FileSymbol { hir::ModuleDef::Trait(it) => { Some(it.display(db, display_target).to_string()) } - hir::ModuleDef::TraitAlias(it) => { - Some(it.display(db, display_target).to_string()) - } hir::ModuleDef::TypeAlias(it) => { Some(it.display(db, display_target).to_string()) } @@ -261,7 +258,6 @@ impl TryToNav for Definition { Definition::Const(it) => it.try_to_nav(db), Definition::Static(it) => it.try_to_nav(db), Definition::Trait(it) => it.try_to_nav(db), - Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => it.try_to_nav(db), Definition::InlineAsmOperand(it) => it.try_to_nav(db), @@ -287,7 +283,6 @@ impl TryToNav for hir::ModuleDef { hir::ModuleDef::Const(it) => it.try_to_nav(db), hir::ModuleDef::Static(it) => it.try_to_nav(db), hir::ModuleDef::Trait(it) => it.try_to_nav(db), - hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db), hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), hir::ModuleDef::Macro(it) => it.try_to_nav(db), hir::ModuleDef::BuiltinType(_) => None, @@ -366,12 +361,6 @@ impl ToNavFromAst for hir::Trait { container_name(db, self, self.krate(db).edition(db)) } } -impl ToNavFromAst for hir::TraitAlias { - const KIND: SymbolKind = SymbolKind::TraitAlias; - fn container_name(self, db: &RootDatabase) -> Option { - container_name(db, self, self.krate(db).edition(db)) - } -} impl TryToNav for D where diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 86b88a17c75fc..dc509b3858389 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1783,7 +1783,7 @@ trait Bar$0 = Foo where Self: ; fn foo(_: impl Bar, _: &dyn Bar) {} "#, expect![[r#" - Bar TraitAlias FileId(0) 13..42 19..22 + Bar Trait FileId(0) 13..42 19..22 FileId(0) 53..56 FileId(0) 66..69 diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 83e5c5ab1dfeb..77c84292cf3e9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -493,7 +493,6 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { Definition::Const(it) => it.attrs(db), Definition::Static(it) => it.attrs(db), Definition::Trait(it) => it.attrs(db), - Definition::TraitAlias(it) => it.attrs(db), Definition::TypeAlias(it) => it.attrs(db), Definition::Macro(it) => it.attrs(db), Definition::SelfType(it) => it.attrs(db), diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 2cccb9639f3eb..86d02b4098bd2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -339,10 +339,6 @@ fn signature_help_for_generics( res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); } - hir::GenericDef::TraitAlias(it) => { - res.doc = it.docs(db); - format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); - } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db); format_to!(res.signature, "type {}", it.name(db).display(db, edition)); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 8bde8fd970063..d73575fb9549a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -576,7 +576,6 @@ pub(super) fn highlight_def( h } Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)), - Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)), Definition::TypeAlias(type_) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); @@ -780,7 +779,6 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { MACRO_RULES => SymbolKind::Macro, CONST_PARAM => SymbolKind::ConstParam, SELF_PARAM => SymbolKind::SelfParam, - TRAIT_ALIAS => SymbolKind::TraitAlias, ASM_OPERAND_NAMED => SymbolKind::Local, _ => return default.into(), }; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 7f5c2c1ec849b..fb33307249a77 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -311,7 +311,6 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Const(_) => SymbolKind::Const, Definition::Static(_) => SymbolKind::Static, Definition::Trait(_) => SymbolKind::Trait, - Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, Definition::BuiltinLifetime(_) => SymbolKind::LifetimeParam, Definition::BuiltinType(_) => return HlTag::BuiltinType, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 3b5d1af0ac72a..4b8762640c743 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -160,7 +160,6 @@ impl HlTag { SymbolKind::Struct => "struct", SymbolKind::ToolModule => "tool_module", SymbolKind::Trait => "trait", - SymbolKind::TraitAlias => "trait_alias", SymbolKind::TypeAlias => "type_alias", SymbolKind::TypeParam => "type_param", SymbolKind::Union => "union", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index f7d798208037e..47ee2ad1c0d70 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -78,8 +78,8 @@ } use foo::bar as baz; -trait Bar = Baz; -trait Foo = Bar; +trait Bar = Baz; +trait Foo = Bar; fn main() { let a = '\n'; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs index 47f86ce8c6cc4..c1b1a3fc8a94a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs @@ -20,7 +20,7 @@ pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) { // trait Z = where Self: T; generic_params::opt_where_clause(p); p.expect(T![;]); - m.complete(p, TRAIT_ALIAS); + m.complete(p, TRAIT); return; } diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 3a8041d2df9ee..93e02a92abdad 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -284,7 +284,6 @@ pub enum SyntaxKind { STRUCT, TOKEN_TREE, TRAIT, - TRAIT_ALIAS, TRY_EXPR, TUPLE_EXPR, TUPLE_FIELD, @@ -457,7 +456,6 @@ impl SyntaxKind { | STRUCT | TOKEN_TREE | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast index c45f870898007..2ef66484ae48f 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast index 8f678247731dc..4443d9d142630 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME @@ -50,7 +50,7 @@ SOURCE_FILE IDENT "Copy" SEMICOLON ";" WHITESPACE "\n" - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 292be1d5315de..d51ddb86d197f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -61,7 +61,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, - SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE, + SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE, SymbolKind::Macro | SymbolKind::ProcMacro | SymbolKind::BuiltinAttr @@ -156,7 +156,6 @@ pub(crate) fn completion_item_kind( SymbolKind::Static => lsp_types::CompletionItemKind::VALUE, SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT, SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE, - SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE, SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT, SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT, @@ -817,7 +816,6 @@ fn semantic_token_type_and_modifiers( SymbolKind::Union => types::UNION, SymbolKind::TypeAlias => types::TYPE_ALIAS, SymbolKind::Trait => types::INTERFACE, - SymbolKind::TraitAlias => types::INTERFACE, SymbolKind::Macro => types::MACRO, SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, @@ -909,7 +907,6 @@ pub(crate) fn folding_range( | FoldKind::WhereClause | FoldKind::ReturnType | FoldKind::Array - | FoldKind::TraitAliases | FoldKind::ExternCrates | FoldKind::MatchArm | FoldKind::Function => None, diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index a9288ecd6fa1f..e803747998b54 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -485,8 +485,7 @@ register_has_name_ast_id! { MacroRules = name, Module = name, Static = name, - Trait = name, - TraitAlias = name + Trait = name } macro_rules! register_assoc_item_ast_id { diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 6d8a360d715b7..d73d60c51f0c8 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -154,7 +154,6 @@ Item = | Static | Struct | Trait -| TraitAlias | TypeAlias | Union | Use @@ -306,11 +305,8 @@ Trait = Attr* Visibility? 'unsafe'? 'auto'? 'trait' Name GenericParamList? - (':' TypeBoundList?)? WhereClause? AssocItemList - -TraitAlias = - Attr* Visibility? - 'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';' + (((':' TypeBoundList?)? WhereClause? AssocItemList) | + ('=' TypeBoundList? WhereClause? ';')) AssocItemList = '{' Attr* AssocItem* '}' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index a9aeeedb6542e..19c1c5ebea333 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -26,8 +26,7 @@ pub use self::{ generated::{nodes::*, tokens::*}, node_ext::{ AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, - SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam, - VisibilityKind, + SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix}, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 160f000387b3d..1cd8146f68630 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -99,38 +99,10 @@ impl GenericParamsOwnerEdit for ast::Trait { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { - let position = match self.assoc_item_list() { - Some(items) => Position::before(items.syntax()), - None => Position::last_child_of(self.syntax()), - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::TraitAlias { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - Position::after(name.syntax) - } else if let Some(trait_token) = self.trait_token() { - Position::after(trait_token) - } else { - Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match self.semicolon_token() { - Some(tok) => Position::before(tok), - None => Position::last_child_of(self.syntax()), + let position = match (self.assoc_item_list(), self.semicolon_token()) { + (Some(items), _) => Position::before(items.syntax()), + (_, Some(tok)) => Position::before(tok), + (None, None) => Position::last_child_of(self.syntax()), }; create_where_clause(position); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index ceb2866ebcdf7..d60196d492fc3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1614,29 +1614,15 @@ impl Trait { #[inline] pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } #[inline] - pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } - #[inline] - pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } - #[inline] - pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } -} -pub struct TraitAlias { - pub(crate) syntax: SyntaxNode, -} -impl ast::HasAttrs for TraitAlias {} -impl ast::HasDocComments for TraitAlias {} -impl ast::HasGenericParams for TraitAlias {} -impl ast::HasName for TraitAlias {} -impl ast::HasVisibility for TraitAlias {} -impl TraitAlias { - #[inline] - pub fn type_bound_list(&self) -> Option { support::child(&self.syntax) } - #[inline] pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } #[inline] pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } #[inline] + pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } + #[inline] pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } + #[inline] + pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } pub struct TryExpr { pub(crate) syntax: SyntaxNode, @@ -2107,7 +2093,6 @@ pub enum Item { Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), Use(Use), @@ -6801,42 +6786,6 @@ impl fmt::Debug for Trait { f.debug_struct("Trait").field("syntax", &self.syntax).finish() } } -impl AstNode for TraitAlias { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - TRAIT_ALIAS - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } - #[inline] - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for TraitAlias { - fn hash(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for TraitAlias {} -impl PartialEq for TraitAlias { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for TraitAlias { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for TraitAlias { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TraitAlias").field("syntax", &self.syntax).finish() - } -} impl AstNode for TryExpr { #[inline] fn kind() -> SyntaxKind @@ -8471,10 +8420,6 @@ impl From for Item { #[inline] fn from(node: Trait) -> Item { Item::Trait(node) } } -impl From for Item { - #[inline] - fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) } -} impl From for Item { #[inline] fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) } @@ -8506,7 +8451,6 @@ impl AstNode for Item { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -8529,7 +8473,6 @@ impl AstNode for Item { STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), TRAIT => Item::Trait(Trait { syntax }), - TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }), TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }), UNION => Item::Union(Union { syntax }), USE => Item::Use(Use { syntax }), @@ -8554,7 +8497,6 @@ impl AstNode for Item { Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, Item::Trait(it) => &it.syntax, - Item::TraitAlias(it) => &it.syntax, Item::TypeAlias(it) => &it.syntax, Item::Union(it) => &it.syntax, Item::Use(it) => &it.syntax, @@ -8984,7 +8926,6 @@ impl AstNode for AnyHasAttrs { | STMT_LIST | STRUCT | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD @@ -9257,10 +9198,6 @@ impl From for AnyHasAttrs { #[inline] fn from(node: Trait) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } } -impl From for AnyHasAttrs { - #[inline] - fn from(node: TraitAlias) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } -} impl From for AnyHasAttrs { #[inline] fn from(node: TryExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } @@ -9330,7 +9267,6 @@ impl AstNode for AnyHasDocComments { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9420,10 +9356,6 @@ impl From for AnyHasDocComments { #[inline] fn from(node: Trait) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } } -impl From for AnyHasDocComments { - #[inline] - fn from(node: TraitAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} impl From for AnyHasDocComments { #[inline] fn from(node: TupleField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } @@ -9488,7 +9420,7 @@ impl ast::HasGenericParams for AnyHasGenericParams {} impl AstNode for AnyHasGenericParams { #[inline] fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION) + matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION) } #[inline] fn cast(syntax: SyntaxNode) -> Option { @@ -9536,10 +9468,6 @@ impl From for AnyHasGenericParams { #[inline] fn from(node: Trait) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } } -impl From for AnyHasGenericParams { - #[inline] - fn from(node: TraitAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } -} impl From for AnyHasGenericParams { #[inline] fn from(node: TypeAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } @@ -9646,7 +9574,6 @@ impl AstNode for AnyHasName { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | TYPE_PARAM | UNION @@ -9739,10 +9666,6 @@ impl From for AnyHasName { #[inline] fn from(node: Trait) -> AnyHasName { AnyHasName { syntax: node.syntax } } } -impl From for AnyHasName { - #[inline] - fn from(node: TraitAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } -} impl From for AnyHasName { #[inline] fn from(node: TypeAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -9832,7 +9755,6 @@ impl AstNode for AnyHasVisibility { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9910,10 +9832,6 @@ impl From for AnyHasVisibility { #[inline] fn from(node: Trait) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } } -impl From for AnyHasVisibility { - #[inline] - fn from(node: TraitAlias) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } -} impl From for AnyHasVisibility { #[inline] fn from(node: TupleField) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } @@ -10639,11 +10557,6 @@ impl std::fmt::Display for Trait { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for TraitAlias { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for TryExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 62a7d4df2cf6b..42b0f5cf2da1c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -880,51 +880,6 @@ impl AstNode for TypeOrConstParam { impl HasAttrs for TypeOrConstParam {} -#[derive(Debug, Clone)] -pub enum TraitOrAlias { - Trait(ast::Trait), - TraitAlias(ast::TraitAlias), -} - -impl TraitOrAlias { - pub fn name(&self) -> Option { - match self { - TraitOrAlias::Trait(x) => x.name(), - TraitOrAlias::TraitAlias(x) => x.name(), - } - } -} - -impl AstNode for TraitOrAlias { - fn can_cast(kind: SyntaxKind) -> bool - where - Self: Sized, - { - matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS) - } - - fn cast(syntax: SyntaxNode) -> Option - where - Self: Sized, - { - let res = match syntax.kind() { - SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }), - SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }), - _ => return None, - }; - Some(res) - } - - fn syntax(&self) -> &SyntaxNode { - match self { - TraitOrAlias::Trait(it) => it.syntax(), - TraitOrAlias::TraitAlias(it) => it.syntax(), - } - } -} - -impl HasAttrs for TraitOrAlias {} - pub enum VisibilityKind { In(ast::Path), PubCrate, diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index 824b38fc872d8..9bd87a7ef5fe0 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -1081,7 +1081,6 @@ fn extract_struct_traits(ast: &mut AstSrc) { "Enum", "Variant", "Trait", - "TraitAlias", "Module", "Static", "Const", From 0d2f8aff1be821907a824e5bad705c9385570fdc Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 4 Aug 2025 16:11:59 +0800 Subject: [PATCH 0058/1900] add test for trait alias projections --- .../crates/hir-ty/src/tests/trait_aliases.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs new file mode 100644 index 0000000000000..302ce550b8534 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs @@ -0,0 +1,21 @@ +use crate::tests::check_types; + +#[test] +fn projection() { + check_types( + r#" +#![feature(trait_alias)] + +pub trait A { + type Output; +} + +pub trait B = A; + +pub fn a(x: T::Output) { + x; +// ^ u32 +} +"#, + ); +} From b416845683e0892075352dd55889c625aef0e55c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:18:50 +0200 Subject: [PATCH 0059/1900] Print fields of interned IDs in hir-ty instead of just the ID --- .../rust-analyzer/crates/hir-ty/src/db.rs | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 18b8db21161fd..6e24aea76d445 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -3,7 +3,7 @@ use std::sync; -use base_db::{Crate, impl_intern_key}; +use base_db::Crate; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -459,40 +459,44 @@ fn hir_database_is_dyn_compatible() { fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedTypeOrConstParamId { pub loc: TypeOrConstParamId, } -impl ::std::fmt::Debug for InternedTypeOrConstParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedTypeOrConstParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } -} -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedLifetimeParamId { pub loc: LifetimeParamId, } -impl ::std::fmt::Debug for InternedLifetimeParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedLifetimeParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } -} -impl_intern_key!(InternedConstParamId, ConstParamId); +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedConstParamId { + pub loc: ConstParamId, +} -impl_intern_key!(InternedOpaqueTyId, ImplTraitId); +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedOpaqueTyId { + pub loc: ImplTraitId, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedClosure(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedClosureId, InternedClosure); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedClosureId { + pub loc: InternedClosure, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedCoroutineId, InternedCoroutine); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedCoroutineId { + pub loc: InternedCoroutine, +} From 2a509717b4ddc1a8d73575583c8487956804121a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:18:50 +0200 Subject: [PATCH 0060/1900] Fix metrics checking out repos into toplevel folder instead of `target` --- src/tools/rust-analyzer/xtask/src/metrics.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index 6ff6a1b15310a..c9eea87106060 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -16,13 +16,16 @@ type Unit = String; impl flags::Metrics { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { let mut metrics = Metrics::new(sh)?; - if !Path::new("./target/rustc-perf").exists() { - sh.create_dir("./target/rustc-perf")?; - cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf") - .run()?; + if !Path::new("./target/metrics/rustc-perf").exists() { + sh.create_dir("./target/metrics/rustc-perf")?; + cmd!( + sh, + "git clone https://github.com/rust-lang/rustc-perf.git ./target/metrics/rustc-perf" + ) + .run()?; } { - let _d = sh.push_dir("./target/rustc-perf"); + let _d = sh.push_dir("./target/metrics/rustc-perf"); let revision = &metrics.perf_revision; cmd!(sh, "git reset --hard {revision}").run()?; } @@ -88,11 +91,12 @@ impl Metrics { cmd!( sh, - "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch" + "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch ./target/metrics/rust" ) .run()?; - let output = cmd!(sh, "./target/release/rust-analyzer rustc-tests ./rust").read()?; + let output = + cmd!(sh, "./target/release/rust-analyzer rustc-tests ./target/metrics/rust").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(metric, value, unit.into()); } @@ -106,7 +110,7 @@ impl Metrics { self.measure_analysis_stats_path( sh, bench, - &format!("./target/rustc-perf/collector/compile-benchmarks/{bench}"), + &format!("./target/metrics/rustc-perf/collector/compile-benchmarks/{bench}"), ) } fn measure_analysis_stats_path( From b62116bda772584e90591bbc6ebf3880a22b0080 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 13 Aug 2025 15:33:08 +0800 Subject: [PATCH 0061/1900] fix errors after rebase --- src/tools/rust-analyzer/crates/hir-def/src/signatures.rs | 4 ++++ .../crates/hir-ty/src/lower_nextsolver/path.rs | 7 +------ .../rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs | 6 +----- .../crates/hir-ty/src/next_solver/interner.rs | 8 ++++++-- src/tools/rust-analyzer/crates/ide/src/file_structure.rs | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 906c1c0e9ff14..bf72fafeae724 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -402,6 +402,7 @@ bitflags::bitflags! { const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; const RUSTC_PAREN_SUGAR = 1 << 7; const COINDUCTIVE = 1 << 8; + const ALIAS = 1 << 9; } } @@ -426,6 +427,9 @@ impl TraitSignature { if source.value.unsafe_token().is_some() { flags.insert(TraitFlags::UNSAFE); } + if source.value.eq_token().is_some() { + flags.insert(TraitFlags::ALIAS); + } if attrs.by_key(sym::fundamental).exists() { flags |= TraitFlags::FUNDAMENTAL; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index b95bb1130fa20..df67b2c59b512 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -260,10 +260,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; return (ty, None); } - TypeNs::TraitAliasId(_) => { - // FIXME(trait_alias): Implement trait alias. - return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); - } TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); let idx = generics.type_or_const_param_idx(param_id.into()); @@ -343,8 +339,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) => {} + | TypeNs::TraitId(_) => {} } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index cfbc10e740e4b..64eab2d6b8185 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -2,7 +2,7 @@ use hir_def::{ AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, UnionId, + TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -24,7 +24,6 @@ pub enum SolverDefId { FunctionId(FunctionId), ImplId(ImplId), StaticId(StaticId), - TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), ForeignId(TypeAliasId), @@ -40,7 +39,6 @@ impl_from!( FunctionId, ImplId, StaticId, - TraitAliasId, TraitId, TypeAliasId, InternedClosureId, @@ -57,7 +55,6 @@ impl From for SolverDefId { GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id), GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id), GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id), - GenericDefId::TraitAliasId(trait_alias_id) => SolverDefId::TraitAliasId(trait_alias_id), GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id), GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id), } @@ -74,7 +71,6 @@ impl TryFrom for GenericDefId { SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id), SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id), SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), - SolverDefId::TraitAliasId(trait_alias_id) => GenericDefId::TraitAliasId(trait_alias_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), SolverDefId::ForeignId(_) => return Err(value), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd2..17a4e7290c2cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1172,7 +1172,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { | SolverDefId::AdtId(_) | SolverDefId::TraitId(_) | SolverDefId::ImplId(_) - | SolverDefId::TraitAliasId(_) | SolverDefId::Ctor(..) | SolverDefId::InternedOpaqueTyId(..) => panic!(), }; @@ -1790,7 +1789,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { - matches!(trait_def_id, SolverDefId::TraitAliasId(_)) + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_alias"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::ALIAS) } fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index d5c6eaca97448..21254fc4d6a22 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -943,7 +943,7 @@ fn let_statements() { navigation_range: 251..256, node_range: 245..262, kind: SymbolKind( - TraitAlias, + Trait, ), detail: None, deprecated: false, From 4bbbe6ee2b0c3c630d605c47ba0d4215d2522240 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:42:32 +0200 Subject: [PATCH 0062/1900] fix: Attach db for inlay hint compute --- .../crates/hir-ty/src/next_solver/interner.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd2..54fec4aefbd45 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -289,7 +289,7 @@ impl<'db> DbInterner<'db> { krate: None, block: None, }) - .unwrap() + .expect("db is expected to be attached") } pub fn new_with( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index f6416fe51c307..424890fe370c4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -107,14 +107,16 @@ pub(crate) fn inlay_hints( } }; let mut preorder = file.preorder(); - while let Some(event) = preorder.next() { - if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) - { - preorder.skip_subtree(); - continue; + salsa::attach(sema.db, || { + while let Some(event) = preorder.next() { + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); } - hints(event); - } + }); if let Some(range_limit) = range_limit { acc.retain(|hint| range_limit.contains_range(hint.range)); } @@ -736,7 +738,7 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = salsa::attach(sema.db, || hint_iterator(sema, famous_defs, ty)); + let iter_item_type = hint_iterator(sema, famous_defs, ty); match iter_item_type { Some((iter_trait, item, ty)) => { const LABEL_START: &str = "impl "; From 39ac6e1eed6312dfb41cd45410804bf629a8b7d1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:01:17 +0200 Subject: [PATCH 0063/1900] update a few fixmes, and one trivial improvement --- .../crates/hir-ty/src/next_solver/interner.rs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd2..402c73bb88223 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -22,8 +22,8 @@ use rustc_type_ir::inherent::{ use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - AliasTerm, AliasTermKind, AliasTy, EarlyBinder, FlagComputation, Flags, ImplPolarity, InferTy, - ProjectionPredicate, TraitPredicate, TraitRef, Upcast, + AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, + ImplPolarity, InferTy, ProjectionPredicate, TraitPredicate, TraitRef, Upcast, }; use salsa::plumbing::AsId; use smallvec::{SmallVec, smallvec}; @@ -1024,8 +1024,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { false } - fn expand_abstract_consts>(self, t: T) -> T { - t + fn expand_abstract_consts>(self, _: T) -> T { + unreachable!("only used by the old trait solver in rustc"); } fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf { @@ -1054,6 +1054,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ), SolverDefId::InternedOpaqueTyId(_def_id) => { // FIXME(next-solver): track variances + // + // We compute them based on the only `Ty` level info in rustc, + // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. VariancesOf::new_from_iter( self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), @@ -1074,6 +1077,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { crate::TyDefId::TypeAliasId(id) } SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + // FIXME(next-solver): need to support opaque types. This uses the types of + // `query mir_borrowck` in rustc. If we're ignoring regions, we could simply + // use the type inferred by general type inference here. _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), }; self.db().ty_ns(def_id) @@ -1087,9 +1093,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { AdtDef::new(def_id, self) } - fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> rustc_type_ir::AliasTyKind { - // FIXME: not currently creating any others - rustc_type_ir::AliasTyKind::Projection + fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque, + SolverDefId::TypeAliasId(_) => AliasTyKind::Projection, + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), + } } fn alias_term_kind( @@ -1100,7 +1109,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, - _ => unreachable!("Unexpected alias: {:?}", alias.def_id), + _ => todo!("Unexpected alias: {:?}", alias.def_id), } } @@ -1741,7 +1750,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn has_item_definition(self, def_id: Self::DefId) -> bool { - // FIXME: should check if has value + // FIXME(next-solver): should check if the associated item has a value. true } @@ -1811,7 +1820,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { - // FIXME(next-solver) + // FIXME(next-solver): should check the `TraitFlags` for + // the `#[rustc_do_not_implement_via_object]` flag true } @@ -1982,7 +1992,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::LocalDefId, ) -> rustc_type_ir::EarlyBinder { - // FIXME(next-solver) + // FIXME(next-solver): This should look at the type computed for the + // opaque by HIR typeck. unimplemented!() } From 00941b3142bf7b50a39b1a2dfa167d3642b5f3db Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:07:12 +0200 Subject: [PATCH 0064/1900] implement `type_of_opaque` --- .../rust-analyzer/crates/hir-ty/src/layout.rs | 23 +--- .../crates/hir-ty/src/next_solver/interner.rs | 100 +++++++++--------- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 +- ...e_alias_impl_traits.rs => opaque_types.rs} | 16 +++ 4 files changed, 69 insertions(+), 72 deletions(-) rename src/tools/rust-analyzer/crates/hir-ty/src/tests/{type_alias_impl_traits.rs => opaque_types.rs} (92%) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 819bd583deaed..e7bb529ad8f26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -335,27 +335,6 @@ pub fn layout_of_ty_ns_query<'db>( ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::Alias(_, ty) => match ty.def_id { - SolverDefId::TypeAliasId(_) => { - return Err(LayoutError::HasPlaceholder); - } - SolverDefId::InternedOpaqueTyId(opaque) => { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - return Err(LayoutError::NotImplemented); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - return Err(LayoutError::NotImplemented); - } - } - } - _ => unreachable!(), - }, TyKind::Closure(c, args) => { let id = match c { SolverDefId::InternedClosureId(id) => id, @@ -389,7 +368,7 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Error(_) => return Err(LayoutError::HasErrorType), - TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => { + TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) | TyKind::Alias(..) => { return Err(LayoutError::HasPlaceholder); } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 402c73bb88223..294d071f58aef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -626,12 +626,8 @@ impl<'db> inherent::AdtDef> for AdtDef { fn struct_tail_ty( self, interner: DbInterner<'db>, - ) -> Option< - rustc_type_ir::EarlyBinder< - DbInterner<'db>, - as rustc_type_ir::Interner>::Ty, - >, - > { + ) -> Option, as rustc_type_ir::Interner>::Ty>> + { let db = interner.db(); let hir_def::AdtId::StructId(struct_id) = self.inner().id else { return None; @@ -645,7 +641,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn all_field_tys( self, interner: DbInterner<'db>, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< DbInterner<'db>, impl IntoIterator as rustc_type_ir::Interner>::Ty>, > { @@ -679,19 +675,15 @@ impl<'db> inherent::AdtDef> for AdtDef { .collect(), }; - rustc_type_ir::EarlyBinder::bind(tys) + EarlyBinder::bind(tys) } fn sizedness_constraint( self, interner: DbInterner<'db>, sizedness: SizedTraitKind, - ) -> Option< - rustc_type_ir::EarlyBinder< - DbInterner<'db>, - as rustc_type_ir::Interner>::Ty, - >, - > { + ) -> Option, as rustc_type_ir::Interner>::Ty>> + { if self.is_struct() { let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; @@ -1066,7 +1058,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - fn type_of(self, def_id: Self::DefId) -> rustc_type_ir::EarlyBinder { + fn type_of(self, def_id: Self::DefId) -> EarlyBinder { let def_id = match def_id { SolverDefId::TypeAliasId(id) => { use hir_def::Lookup; @@ -1077,9 +1069,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { crate::TyDefId::TypeAliasId(id) } SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), - // FIXME(next-solver): need to support opaque types. This uses the types of - // `query mir_borrowck` in rustc. If we're ignoring regions, we could simply - // use the type inferred by general type inference here. + // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. + // + // We currently always use the type from HIR typeck which ignores regions. This + // should be fine. + SolverDefId::InternedOpaqueTyId(_) => { + return self.type_of_opaque_hir_typeck(def_id); + } _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), }; self.db().ty_ns(def_id) @@ -1109,7 +1105,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, - _ => todo!("Unexpected alias: {:?}", alias.def_id), + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), } } @@ -1204,8 +1200,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn fn_sig( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder>> - { + ) -> EarlyBinder>> { let id = match def_id { SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), SolverDefId::Ctor(ctor) => match ctor { @@ -1258,7 +1253,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { explicit_item_bounds(self, def_id).map_bound(|bounds| { Clauses::new_from_iter(self, elaborate(self, bounds).collect::>()) }) @@ -1268,7 +1263,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_self_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { explicit_item_bounds(self, def_id).map_bound(|bounds| { Clauses::new_from_iter( self, @@ -1280,7 +1275,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_non_self_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect(); let own_bounds: FxHashSet<_> = self.item_self_bounds(def_id).skip_binder().into_iter().collect(); @@ -1298,7 +1293,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) @@ -1308,7 +1303,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn own_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) @@ -1318,8 +1313,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn explicit_super_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> - { + ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) @@ -1327,15 +1321,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .cloned() .map(|p| (p, Span::dummy())) .collect(); - rustc_type_ir::EarlyBinder::bind(predicates) + EarlyBinder::bind(predicates) } #[tracing::instrument(skip(self), ret)] fn explicit_implied_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> - { + ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) @@ -1343,13 +1336,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .cloned() .map(|p| (p, Span::dummy())) .collect(); - rustc_type_ir::EarlyBinder::bind(predicates) + EarlyBinder::bind(predicates) } fn impl_super_outlives( self, impl_def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let impl_id = match impl_def_id { SolverDefId::ImplId(id) => id, _ => unreachable!(), @@ -1372,11 +1365,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn const_conditions( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< Self, impl IntoIterator>>, > { - rustc_type_ir::EarlyBinder::bind([unimplemented!()]) + EarlyBinder::bind([unimplemented!()]) } fn has_target_features(self, def_id: Self::DefId) -> bool { @@ -1763,7 +1756,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn impl_trait_ref( self, impl_def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let impl_id = match impl_def_id { SolverDefId::ImplId(id) => id, _ => panic!("Unexpected SolverDefId in impl_trait_ref"), @@ -1960,12 +1953,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn explicit_implied_const_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< Self, impl IntoIterator>>, > { // FIXME(next-solver) - rustc_type_ir::EarlyBinder::bind([]) + EarlyBinder::bind([]) } fn fn_is_const(self, def_id: Self::DefId) -> bool { @@ -1988,22 +1981,31 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { None } - fn type_of_opaque_hir_typeck( - self, - def_id: Self::LocalDefId, - ) -> rustc_type_ir::EarlyBinder { - // FIXME(next-solver): This should look at the type computed for the - // opaque by HIR typeck. - unimplemented!() + fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder { + match def_id { + SolverDefId::InternedOpaqueTyId(opaque) => { + let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db().infer(func.into()); + EarlyBinder::bind(infer.type_of_rpit[idx].to_nextsolver(self)) + } + crate::ImplTraitId::TypeAliasImplTrait(..) + | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + // FIXME(next-solver) + EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)) + } + } + } + _ => panic!("Unexpected SolverDefId in type_of_opaque_hir_typeck"), + } } fn coroutine_hidden_types( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< - Self, - rustc_type_ir::Binder>, - > { + ) -> EarlyBinder>> + { // FIXME(next-solver) unimplemented!() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index fc31973022bdd..d89cf4bcd45cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -6,11 +6,11 @@ mod incremental; mod macros; mod method_resolution; mod never_type; +mod opaque_types; mod patterns; mod regression; mod simple; mod traits; -mod type_alias_impl_traits; use base_db::{Crate, SourceDatabase}; use expect_test::Expect; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs similarity index 92% rename from src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs rename to src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index e2b7bf379cc3b..36578545a9f49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -159,3 +159,19 @@ static ALIAS: i32 = { "#]], ) } + +#[test] +fn leak_auto_traits() { + check_no_mismatches( + r#" +//- minicore: send +fn foo() -> impl Sized {} + +fn is_send(_: T) {} + +fn main() { + is_send(foo()); +} + "#, + ); +} From 5849fef132daf525016379b6bfc6bd25459371dd Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:30:34 +0200 Subject: [PATCH 0065/1900] layout_of uses `PostAnalysis` --- src/tools/rust-analyzer/crates/hir-ty/src/layout.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index e7bb529ad8f26..2d0471d7e5545 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -182,7 +182,7 @@ pub fn layout_of_ty_ns_query<'db>( }; let dl = &*target; let cx = LayoutCx::new(dl); - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); let result = match ty.kind() { @@ -368,7 +368,11 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Error(_) => return Err(LayoutError::HasErrorType), - TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) | TyKind::Alias(..) => { + TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Param(..) + | TyKind::Alias(..) => { return Err(LayoutError::HasPlaceholder); } }; From 67b1e329080519085716c8e444a43eede0d196a6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 11:00:00 +0200 Subject: [PATCH 0066/1900] manually normalize alias --- .../crates/hir-ty/src/next_solver/interner.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 294d071f58aef..c530c28f88613 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -626,8 +626,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn struct_tail_ty( self, interner: DbInterner<'db>, - ) -> Option, as rustc_type_ir::Interner>::Ty>> - { + ) -> Option, Ty<'db>>> { let db = interner.db(); let hir_def::AdtId::StructId(struct_id) = self.inner().id else { return None; @@ -641,10 +640,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn all_field_tys( self, interner: DbInterner<'db>, - ) -> EarlyBinder< - DbInterner<'db>, - impl IntoIterator as rustc_type_ir::Interner>::Ty>, - > { + ) -> EarlyBinder, impl IntoIterator>> { let db = interner.db(); // FIXME: this is disabled just to match the behavior with chalk right now let field_tys = |id: VariantId| { @@ -682,8 +678,7 @@ impl<'db> inherent::AdtDef> for AdtDef { self, interner: DbInterner<'db>, sizedness: SizedTraitKind, - ) -> Option, as rustc_type_ir::Interner>::Ty>> - { + ) -> Option, Ty<'db>>> { if self.is_struct() { let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; From 1d57d7800db05934a466457c443975d71c4d1e29 Mon Sep 17 00:00:00 2001 From: donni-h <56559005+donni-h@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:03:52 +0200 Subject: [PATCH 0067/1900] Fix dead link to Cargo.toml in documentation ../../Cargo.toml resolves to https://rust-analyzer.github.io/Cargo.toml, which is an invalid link --- src/tools/rust-analyzer/docs/book/src/contributing/style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/style.md b/src/tools/rust-analyzer/docs/book/src/contributing/style.md index 746f3eb132117..fe09fb6c2fd52 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/style.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/style.md @@ -101,7 +101,7 @@ Including a description and GIF suitable for the changelog means less work for t ## Clippy -We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](../../Cargo.toml) [workspace.lints.clippy] section. +We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](https://github.com/rust-lang/rust-analyzer/blob/master/Cargo.toml) [workspace.lints.clippy] section. # Code From 8ef6a8ff6defd019dff7865ab602bc7acba1afc2 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 13 Aug 2025 15:24:53 +0100 Subject: [PATCH 0068/1900] [internal] Update to the latest @vscode/vsce for extension build This isn't a logic change, but it fixes an npm warning during the build. vsce itself hasn't had any major changes between 3.2.2 and 3.6. * https://github.com/microsoft/vscode-vsce/releases/tag/v3.3.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.4.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.5.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.6.0 --- .../editors/code/package-lock.json | 1368 ++++++++++++++--- .../rust-analyzer/editors/code/package.json | 2 +- 2 files changed, 1169 insertions(+), 201 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 1dc11ee4e4b81..ad8708e00c519 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -26,7 +26,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", @@ -41,6 +41,23 @@ "vscode": "^1.93.0" } }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", @@ -212,6 +229,31 @@ "node": ">=16" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -947,6 +989,217 @@ "node": ">= 8" } }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@stylistic/eslint-plugin": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz", @@ -984,6 +1237,136 @@ "eslint": ">=9.0.0" } }, + "node_modules/@textlint/ast-node-types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", + "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.2.1.tgz", + "integrity": "sha512-oollG/BHa07+mMt372amxHohteASC+Zxgollc1sZgiyxo4S6EuureV3a4QIQB0NecA+Ak3d0cl0WI/8nou38jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", + "chalk": "^4.1.2", + "debug": "^4.4.1", + "js-yaml": "^3.14.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.2.1.tgz", + "integrity": "sha512-b/C/ZNrm05n1ypymDknIcpkBle30V2ZgE3JVqQlA9PnQV46Ky510qrZk6s9yfKgA3m1YRnAw04m8xdVtqjq1qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.1.tgz", + "integrity": "sha512-FY3aK4tElEcOJVUsaMj4Zro4jCtKEEwUMIkDL0tcn6ljNcgOF7Em+KskRRk/xowFWayqDtdz5T3u7w/6fjjuJQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.1.tgz", + "integrity": "sha512-zyqNhSatK1cwxDUgosEEN43hFh3WCty9Zm2Vm3ogU566IYegifwqN54ey/CiRy/DiO4vMcFHykuQnh2Zwp6LLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.2.1" + } + }, "node_modules/@tsconfig/strictest": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz", @@ -1015,6 +1398,20 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.93.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", @@ -1220,16 +1617,20 @@ } }, "node_modules/@vscode/vsce": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", - "integrity": "sha512-4TqdUq/yKlQTHcQMk/DamR632bq/+IJDomSbexOMee/UAYWqYm0XHWA6scGslsCpzY+sCWEhhl0nqdOB0XW1kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz", + "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==", "dev": true, "license": "MIT", "dependencies": { "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.1", + "@secretlint/secretlint-formatter-sarif": "^10.1.1", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", - "chalk": "^2.4.2", + "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", "commander": "^12.1.0", @@ -1243,6 +1644,7 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", + "secretlint": "^10.1.1", "semver": "^7.5.2", "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", @@ -1486,6 +1888,22 @@ "integrity": "sha512-PMqBCBvrOVDRqLGooQb+z+t1Q0PiPyurUQeZRR5uHBOVZcW8B04KMmnT12USnhpNX2wCPagWzLVppQMUG3u0Dw==", "license": "MIT" }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -1500,16 +1918,18 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { @@ -1519,6 +1939,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1564,6 +1994,22 @@ ], "license": "MIT" }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -1598,6 +2044,13 @@ "dev": true, "license": "ISC" }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1720,18 +2173,20 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cheerio": { @@ -1845,39 +2300,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1938,20 +2360,21 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/combined-stream": { @@ -2469,9 +2892,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2685,6 +3108,23 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2730,6 +3170,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2829,16 +3282,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "9.21.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", @@ -2964,22 +3407,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2991,43 +3418,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3041,16 +3431,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3064,19 +3444,6 @@ "node": "*" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -3095,6 +3462,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3213,6 +3594,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -3360,6 +3758,21 @@ "license": "MIT", "optional": true }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3492,6 +3905,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3505,6 +3949,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3513,13 +3964,13 @@ "license": "MIT" }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-symbols": { @@ -3702,6 +4153,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3872,6 +4336,24 @@ "dev": true, "license": "ISC" }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/jackspeak": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", @@ -3897,6 +4379,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3931,6 +4420,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -3938,6 +4440,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -4103,6 +4618,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4159,6 +4681,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -4430,12 +4959,61 @@ "license": "MIT", "optional": true }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/node-sarif-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", + "integrity": "sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -4661,6 +5239,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4688,6 +5279,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -4795,6 +5404,19 @@ "node": "20 || >=22" } }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -4802,6 +5424,13 @@ "dev": true, "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", @@ -4815,6 +5444,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -4962,6 +5601,19 @@ "rc": "cli.js" } }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -4986,6 +5638,39 @@ "node": ">=0.8" } }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -5018,6 +5703,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5146,6 +5841,28 @@ "dev": true, "license": "ISC" }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -5326,6 +6043,80 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -5487,17 +6278,130 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/tar-fs": { @@ -5587,6 +6491,46 @@ "node": ">= 6" } }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/tmp": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", @@ -5667,6 +6611,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -5747,6 +6704,29 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5781,6 +6761,30 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -5928,42 +6932,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 328eb509b4eea..4975ca858671b 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -63,7 +63,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", From 23e8a1e1398f5c17f4c64fe740479c2df31c9fd0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 19:49:59 +0000 Subject: [PATCH 0069/1900] Don't panic if unable to identify host in metrics --- src/tools/rust-analyzer/xtask/src/metrics.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index c9eea87106060..fd4b600b03470 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -160,7 +160,7 @@ struct Host { impl Metrics { fn new(sh: &Shell) -> anyhow::Result { - let host = Host::new(sh)?; + let host = Host::new(sh).unwrap_or_else(|_| Host::unknown()); let timestamp = SystemTime::now(); let revision = cmd!(sh, "git rev-parse HEAD").read()?; let perf_revision = "a584462e145a0c04760fd9391daefb4f6bd13a99".into(); @@ -191,9 +191,13 @@ impl Metrics { } impl Host { + fn unknown() -> Host { + Host { os: "unknown".into(), cpu: "unknown".into(), mem: "unknown".into() } + } + fn new(sh: &Shell) -> anyhow::Result { if cfg!(not(target_os = "linux")) { - return Ok(Host { os: "unknown".into(), cpu: "unknown".into(), mem: "unknown".into() }); + return Ok(Host::unknown()); } let os = read_field(sh, "/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_owned(); From 8e5818df2eb7d77f3cbed5cbf0c292e8a770d760 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 19:50:15 +0000 Subject: [PATCH 0070/1900] Shift vars when mapping Dyn --- .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 3a3206bef38b5..4696cf479c191 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1147,6 +1147,9 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } }), ); + + let p = shift_vars(interner, p, 1); + let where_clause = match p.skip_binder() { rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { let trait_ref = TraitRef::new( From 861f9122c899a8ec52c62ecb0a1435226dd5fc1a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 14 Aug 2025 08:34:31 +0800 Subject: [PATCH 0071/1900] Fix indent for convert_match_to_let_else Example --- ``` //- minicore: option fn f() { let x$0 = match Some(()) { Some(it) => it, None => {//comment println!("nope"); return }, }; } ``` **Old output**: ```rust fn f() { let Some(x) = Some(()) else {//comment println!("nope"); return }; } ``` **This PR output**: ```rust fn f() { let Some(x) = Some(()) else {//comment println!("nope"); return }; } ``` --- .../src/handlers/convert_match_to_let_else.rs | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 9126e869b9a05..1a6d176c9054c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -1,7 +1,7 @@ use ide_db::defs::{Definition, NameRefClass}; use syntax::{ AstNode, SyntaxNode, - ast::{self, HasName, Name, syntax_factory::SyntaxFactory}, + ast::{self, HasName, Name, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, syntax_editor::SyntaxEditor, }; @@ -45,7 +45,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' return None; } - let diverging_arm_expr = match diverging_arm.expr()? { + let diverging_arm_expr = match diverging_arm.expr()?.dedent(1.into()) { ast::Expr::BlockExpr(block) if block.modifier().is_none() && block.label().is_none() => { block.to_string() } @@ -150,7 +150,12 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn } } editor.add_mappings(make.finish_with_mappings()); - editor.finish().new_root().clone() + let new_node = editor.finish().new_root().clone(); + if let Some(pat) = ast::Pat::cast(new_node.clone()) { + pat.dedent(1.into()).syntax().clone() + } else { + new_node + } } #[cfg(test)] @@ -209,6 +214,53 @@ fn foo(opt: Option) -> Result { ); } + #[test] + fn indent_level() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let mut state = 2; + let va$0lue = match opt { + Some( + Foo::A(it) + | Foo::B(it) + ) => it, + _ => { + state = 3; + return Err(()) + }, + }; +} + "#, + r#" +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let mut state = 2; + let Some( + Foo::A(value) + | Foo::B(value) + ) = opt else { + state = 3; + return Err(()) + }; +} + "#, + ); + } + #[test] fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() { cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2); @@ -489,9 +541,9 @@ fn f() { r#" fn f() { let Some(x) = Some(()) else {//comment - println!("nope"); - return - }; + println!("nope"); + return + }; } "#, ); From c78177b7f93c9b691a9b8365eaba9c8d11df4cd0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 14 Aug 2025 10:05:11 +0800 Subject: [PATCH 0072/1900] Add guard to let-chain for replace_match_with_if_let ```rust fn main() { match$0 Some(0) { Some(n) if n % 2 == 0 && n != 6 => (), _ => code(), } } ``` -> ```rust fn main() { if let Some(n) = Some(0) && n % 2 == 0 && n != 6 { () } else { code() } } --- .../src/handlers/replace_if_let_with_match.rs | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 15d3db5e749f0..dd244375dc91e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -8,7 +8,7 @@ use ide_db::{ ty_filter::TryEnum, }; use syntax::{ - AstNode, T, TextRange, + AstNode, Edition, T, TextRange, ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, }; @@ -187,7 +187,7 @@ fn make_else_arm( // Assist: replace_match_with_if_let // -// Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression. +// Replaces a binary `match` with a wildcard pattern with an `if let` expression. // // ``` // enum Action { Move { distance: u32 }, Stop } @@ -225,18 +225,24 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let mut arms = match_arm_list.arms(); let (first_arm, second_arm) = (arms.next()?, arms.next()?); - if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() { + if arms.next().is_some() || second_arm.guard().is_some() { + return None; + } + if first_arm.guard().is_some() && ctx.edition() < Edition::Edition2024 { return None; } - let (if_let_pat, then_expr, else_expr) = pick_pattern_and_expr_order( + let (if_let_pat, guard, then_expr, else_expr) = pick_pattern_and_expr_order( &ctx.sema, first_arm.pat()?, second_arm.pat()?, first_arm.expr()?, second_arm.expr()?, + first_arm.guard(), + second_arm.guard(), )?; let scrutinee = match_expr.expr()?; + let guard = guard.and_then(|it| it.condition()); let let_ = match &if_let_pat { ast::Pat::LiteralPat(p) @@ -277,6 +283,11 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' } _ => make.expr_let(if_let_pat, scrutinee).into(), }; + let condition = if let Some(guard) = guard { + make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() + } else { + condition + }; let then_expr = then_expr.clone_for_update(); then_expr.reindent_to(IndentLevel::single()); let then_block = make_block_expr(then_expr); @@ -303,18 +314,23 @@ fn pick_pattern_and_expr_order( pat2: ast::Pat, expr: ast::Expr, expr2: ast::Expr, -) -> Option<(ast::Pat, ast::Expr, ast::Expr)> { + guard: Option, + guard2: Option, +) -> Option<(ast::Pat, Option, ast::Expr, ast::Expr)> { + if guard.is_some() && guard2.is_some() { + return None; + } let res = match (pat, pat2) { (ast::Pat::WildcardPat(_), _) => return None, - (pat, ast::Pat::WildcardPat(_)) => (pat, expr, expr2), - (pat, _) if is_empty_expr(&expr2) => (pat, expr, expr2), - (_, pat) if is_empty_expr(&expr) => (pat, expr2, expr), + (pat, ast::Pat::WildcardPat(_)) => (pat, guard, expr, expr2), + (pat, _) if is_empty_expr(&expr2) => (pat, guard, expr, expr2), + (_, pat) if is_empty_expr(&expr) => (pat, guard, expr2, expr), (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) { (true, true) => return None, - (true, false) => (pat, expr, expr2), - (false, true) => (pat2, expr2, expr), - _ if is_sad_pat(sema, &pat) => (pat2, expr2, expr), - (false, false) => (pat, expr, expr2), + (true, false) => (pat, guard, expr, expr2), + (false, true) => (pat2, guard2, expr2, expr), + _ if is_sad_pat(sema, &pat) => (pat2, guard2, expr2, expr), + (false, false) => (pat, guard, expr, expr2), }, }; Some(res) @@ -1849,6 +1865,30 @@ fn main() { code() } } +"#, + ) + } + + #[test] + fn test_replace_match_with_if_let_chain() { + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if n % 2 == 0 && n != 6 => (), + _ => code(), + } +} +"#, + r#" +fn main() { + if let Some(n) = Some(0) && n % 2 == 0 && n != 6 { + () + } else { + code() + } +} "#, ) } From 6772f18557e24d9d1f8572839d0ef3cde3b0ac44 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 14 Aug 2025 14:33:33 +0200 Subject: [PATCH 0073/1900] Track diagnostic generations per package --- .../crates/rust-analyzer/src/diagnostics.rs | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 30f530100f9df..ee50237c405e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -26,17 +26,15 @@ pub struct DiagnosticsMapConfig { pub(crate) type DiagnosticsGeneration = usize; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub(crate) struct WorkspaceFlycheckDiagnostic { - pub(crate) generation: DiagnosticsGeneration, - pub(crate) per_package: - FxHashMap>, FxHashMap>>, + pub(crate) per_package: FxHashMap>, PackageFlycheckDiagnostic>, } -impl WorkspaceFlycheckDiagnostic { - fn new(generation: DiagnosticsGeneration) -> Self { - WorkspaceFlycheckDiagnostic { generation, per_package: Default::default() } - } +#[derive(Debug, Clone)] +pub(crate) struct PackageFlycheckDiagnostic { + generation: DiagnosticsGeneration, + per_file: FxHashMap>, } #[derive(Debug, Default, Clone)] @@ -68,7 +66,7 @@ impl DiagnosticCollection { let Some(check) = self.check.get_mut(flycheck_id) else { return; }; - self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.into_keys())); + self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.per_file.into_keys())); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.clear(); } @@ -79,7 +77,7 @@ impl DiagnosticCollection { self.changes.extend( self.check .iter_mut() - .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.into_keys())), + .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.per_file.into_keys())), ) } @@ -93,7 +91,7 @@ impl DiagnosticCollection { }; let package_id = Some(package_id); if let Some(checks) = check.per_package.remove(&package_id) { - self.changes.extend(checks.into_keys()); + self.changes.extend(checks.per_file.into_keys()); } if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.remove(&package_id); @@ -105,8 +103,20 @@ impl DiagnosticCollection { flycheck_id: usize, generation: DiagnosticsGeneration, ) { - if self.check.get(flycheck_id).is_some_and(|it| it.generation < generation) { - self.clear_check(flycheck_id); + if let Some(flycheck) = self.check.get_mut(flycheck_id) { + let mut packages = vec![]; + self.changes.extend( + flycheck + .per_package + .extract_if(|_, v| v.generation < generation) + .inspect(|(package_id, _)| packages.push(package_id.clone())) + .flat_map(|(_, v)| v.per_file.into_keys()), + ); + if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { + for package in packages { + fixes.remove(&package); + } + } } } @@ -126,21 +136,19 @@ impl DiagnosticCollection { fix: Option>, ) { if self.check.len() <= flycheck_id { - self.check - .resize_with(flycheck_id + 1, || WorkspaceFlycheckDiagnostic::new(generation)); + self.check.resize_with(flycheck_id + 1, WorkspaceFlycheckDiagnostic::default); } + let check = &mut self.check[flycheck_id]; + let package = check.per_package.entry(package_id.clone()).or_insert_with(|| { + PackageFlycheckDiagnostic { generation, per_file: FxHashMap::default() } + }); // Getting message from old generation. Might happen in restarting checks. - if self.check[flycheck_id].generation > generation { + if package.generation > generation { return; } - self.check[flycheck_id].generation = generation; - let diagnostics = self.check[flycheck_id] - .per_package - .entry(package_id.clone()) - .or_default() - .entry(file_id) - .or_default(); + package.generation = generation; + let diagnostics = package.per_file.entry(file_id).or_default(); for existing_diagnostic in diagnostics.iter() { if are_diagnostics_equal(existing_diagnostic, &diagnostic) { return; @@ -210,7 +218,7 @@ impl DiagnosticCollection { .check .iter() .flat_map(|it| it.per_package.values()) - .filter_map(move |it| it.get(&file_id)) + .filter_map(move |it| it.per_file.get(&file_id)) .flatten(); native_syntax.chain(native_semantic).chain(check) } From 58ec13d79389f3886e71d43a0fcf501b84c1a40f Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Mon, 11 Aug 2025 06:16:34 +0200 Subject: [PATCH 0074/1900] feat: hint at unterminated strings in unknown prefix errors When encountering 'unknown literal prefix' errors, check for unbalanced quotes in recent code and suggest checking for unterminated string literals. --- .../crates/parser/src/lexed_str.rs | 30 ++++++++++++++++++- .../unterminated_string_unknown_prefix.rast | 15 ++++++++++ .../err/unterminated_string_unknown_prefix.rs | 5 ++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 8fff1c3db7485..dcf397142cac0 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -149,6 +149,24 @@ impl<'a> Converter<'a> { } } + /// Check for likely unterminated string by analyzing STRING token content + fn has_likely_unterminated_string(&self) -> bool { + let Some(last_idx) = self.res.kind.len().checked_sub(1) else { return false }; + + for i in (0..=last_idx).rev().take(5) { + if self.res.kind[i] == STRING { + let start = self.res.start[i] as usize; + let end = self.res.start.get(i + 1).map(|&s| s as usize).unwrap_or(self.offset); + let content = &self.res.text[start..end]; + + if content.contains('(') && (content.contains("//") || content.contains(";\n")) { + return true; + } + } + } + false + } + fn finalize_with_eof(mut self) -> LexedStr<'a> { self.res.push(EOF, self.offset); self.res @@ -267,7 +285,17 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Unknown => ERROR, rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT, rustc_lexer::TokenKind::UnknownPrefix => { - errors.push("unknown literal prefix".into()); + let has_unterminated = self.has_likely_unterminated_string(); + + let error_msg = if has_unterminated { + format!( + "unknown literal prefix `{}` (note: check for unterminated string literal)", + token_text + ) + } else { + "unknown literal prefix".to_owned() + }; + errors.push(error_msg); IDENT } rustc_lexer::TokenKind::Eof => EOF, diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast new file mode 100644 index 0000000000000..f7f24ca3f810a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast @@ -0,0 +1,15 @@ +FN_KW "fn" +WHITESPACE " " +IDENT "main" +L_PAREN "(" +R_PAREN ")" +WHITESPACE " " +L_CURLY "{" +WHITESPACE "\n " +IDENT "hello" +L_PAREN "(" +STRING "\"world);\n // a bunch of code was here\n env(\"FLAGS" +STRING "\", \"" +MINUS "-" +IDENT "help" error: unknown literal prefix `help` (note: check for unterminated string literal) +STRING "\")\n}" error: Missing trailing `"` symbol to terminate the string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs new file mode 100644 index 0000000000000..338b9582605bd --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs @@ -0,0 +1,5 @@ +fn main() { + hello("world); + // a bunch of code was here + env("FLAGS", "-help") +} \ No newline at end of file From cc3c5cfb2e46c04778ebba4b0c71e87cb7f7cef7 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 14 Aug 2025 17:04:54 +0000 Subject: [PATCH 0075/1900] Add test for webrender-2022 metrics --- .../crates/hir-ty/src/tests/traits.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 2e4346a86973b..9d72105624a05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4994,3 +4994,35 @@ fn main() { "#]], ); } + +#[test] +fn trait_object_binders() { + check_infer( + r#" +//- minicore: iterator, dispatch_from_dyn +fn main() { + struct Box(*const T); + impl Iterator for Box { + type Item = I::Item; + fn next(&mut self) -> Option { + loop {} + } + } + let iter: Box + 'static> = loop {}; + let _ = iter.into_iter(); +}"#, + expect![[r#" + 10..313 '{ ...r(); }': () + 223..227 'iter': Box + 'static> + 273..280 'loop {}': ! + 278..280 '{}': () + 290..291 '_': Box + 'static> + 294..298 'iter': Box + 'static> + 294..310 'iter.i...iter()': Box + 'static> + 152..156 'self': &'? mut Box + 177..208 '{ ... }': Option> + 191..198 'loop {}': ! + 196..198 '{}': () + "#]], + ); +} From 06336efca9cd5851a72ff43db3641485e0e06a79 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 03:07:43 +0000 Subject: [PATCH 0076/1900] add comment --- .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 4696cf479c191..20cd8626f299b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1148,6 +1148,13 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }), ); + // Rust and chalk have slightly different + // representation for trait objects. + // + // Chalk uses `for for<'a> T0: Trait<'a>` while rustc + // uses `ExistentialPredicate`s, which do not have a self ty. + // We need to shift escaping bound vars by 1 to accommodate + // the newly introduced `for` binder. let p = shift_vars(interner, p, 1); let where_clause = match p.skip_binder() { From b38dd2acb598adb14bd1f08f39090909be13498b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 15 Aug 2025 20:15:21 +0300 Subject: [PATCH 0077/1900] Use a more specific error message when talking about the server logs --- src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index f8f29eee126ed..a8a54930c6e5e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -152,7 +152,9 @@ impl GlobalState { if self.fetch_build_data_error().is_err() { status.health |= lsp_ext::Health::Warning; message.push_str("Failed to run build scripts of some packages.\n\n"); - message.push_str("Please refer to the logs for more details on the errors."); + message.push_str( + "Please refer to the language server logs for more details on the errors.", + ); } if let Some(err) = &self.config_errors { status.health |= lsp_ext::Health::Warning; From b9861fb75dda32fb349894fe8c73ab68d0c2f5f7 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 17 Aug 2025 16:09:52 +0900 Subject: [PATCH 0078/1900] fix: Make lang items query properly filter out overwritten/excluded sysroots --- .../rust-analyzer/crates/base-db/src/lib.rs | 2 + .../crates/hir-def/src/lang_item.rs | 16 ++- .../crates/hir-def/src/nameres.rs | 4 + .../crates/hir-def/src/nameres/collector.rs | 19 +-- .../crates/hir-ty/src/tests/regression.rs | 51 +++++++ .../rust-analyzer/crates/ide-db/src/lib.rs | 6 +- .../crates/test-fixture/src/lib.rs | 136 +++++++++++------- 7 files changed, 166 insertions(+), 68 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index b8eadb608fea5..dbf949c470c4d 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -30,6 +30,8 @@ use triomphe::Arc; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; pub type FxIndexSet = indexmap::IndexSet; +pub type FxIndexMap = + indexmap::IndexMap>; #[macro_export] macro_rules! impl_intern_key { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index a0be69cb2f96e..600f206770016 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -12,7 +12,7 @@ use crate::{ StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path, - nameres::{assoc::TraitItems, crate_def_map}, + nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -170,7 +170,19 @@ pub fn lang_item( { return Some(target); } - start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item)) + + // Our `CrateGraph` eagerly inserts sysroot dependencies like `core` or `std` into dependencies + // even if the target crate has `#![no_std]`, `#![no_core]` or shadowed sysroot dependencies + // like `dependencies.std.path = ".."`. So we use `extern_prelude()` instead of + // `CrateData.dependencies` here, which has already come through such sysroot complexities + // while nameres. + // + // See https://github.com/rust-lang/rust-analyzer/pull/20475 for details. + crate_local_def_map(db, start_crate).local(db).extern_prelude().find_map(|(_, (krate, _))| { + // Some crates declares themselves as extern crate like `extern crate self as core`. + // Ignore these to prevent cycles. + if krate.krate == start_crate { None } else { lang_item(db, krate.krate, item) } + }) } #[derive(Default, Debug, Clone, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 5030585147dee..bf6fc15b7d199 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -545,6 +545,10 @@ impl DefMap { self.data.no_std || self.data.no_core } + pub fn is_no_core(&self) -> bool { + self.data.no_core + } + pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.data.fn_proc_macro_mapping.get(&id).copied() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 07e61718222bf..0f84728dcb4a8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -27,10 +27,11 @@ use triomphe::Arc; use crate::{ AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, - ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, - LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, - MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern, + ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, + MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, + ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, + UseLoc, attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, @@ -69,7 +70,7 @@ pub(super) fn collect_defs( // populate external prelude and dependency list let mut deps = - FxHashMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default()); + FxIndexMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default()); for dep in &krate.dependencies { tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); @@ -220,7 +221,7 @@ struct DefCollector<'db> { /// Set only in case of blocks. crate_local_def_map: Option<&'db LocalDefMap>, // The dependencies of the current crate, including optional deps like `test`. - deps: FxHashMap, + deps: FxIndexMap, glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, @@ -332,7 +333,9 @@ impl<'db> DefCollector<'db> { let skip = dep.is_sysroot() && match dep.crate_id.data(self.db).origin { CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core, - CrateOrigin::Lang(LangCrateOrigin::Std) => crate_data.no_std, + CrateOrigin::Lang(LangCrateOrigin::Std) => { + crate_data.no_core || crate_data.no_std + } _ => false, }; if skip { @@ -2550,7 +2553,7 @@ mod tests { def_map, local_def_map: LocalDefMap::default(), crate_local_def_map: None, - deps: FxHashMap::default(), + deps: FxIndexMap::default(), glob_imports: FxHashMap::default(), unresolved_imports: Vec::new(), indeterminate_imports: Vec::new(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 25061e1dbdb9d..212fec4a4e4f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2388,3 +2388,54 @@ pub trait Destruct {} "#, ); } + +#[test] +fn no_duplicated_lang_item_metadata() { + check_types( + r#" +//- minicore: pointee +//- /main.rs crate:main deps:std,core +use std::AtomicPtr; +use std::null_mut; + +fn main() { + let x: AtomicPtr<()> = AtomicPtr::new(null_mut()); + //^ AtomicPtr<()> +} + +//- /lib.rs crate:r#std deps:core +#![no_std] +pub use core::*; + +//- /lib.rs crate:r#core +#![no_core] + +#[lang = "pointee_trait"] +pub trait Pointee { + #[lang = "metadata_type"] + type Metadata; +} + +pub struct AtomicPtr(T); + +impl AtomicPtr { + pub fn new(p: *mut T) -> AtomicPtr { + loop {} + } +} + +#[lang = "pointee_sized"] +pub trait PointeeSized {} +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} +#[lang = "sized"] +pub trait Sized: MetaSized {} + +pub trait Thin = Pointee + PointeeSized; + +pub fn null_mut() -> *mut T { + loop {} +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 1f074de4cd7d2..9d2474d91da67 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -66,13 +66,9 @@ pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; pub use ::line_index; /// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience. -pub use base_db; +pub use base_db::{self, FxIndexMap, FxIndexSet}; pub use span::{self, FileId}; -pub type FxIndexSet = indexmap::IndexSet>; -pub type FxIndexMap = - indexmap::IndexMap>; - pub type FilePosition = FilePositionWrapper; pub type FileRange = FileRangeWrapper; diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 4413d2f222c15..57fca70547bf9 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -3,8 +3,8 @@ use std::{any::TypeId, mem, str::FromStr, sync}; use base_db::{ Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, - DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot, - Version, VfsPath, salsa, + DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase, + SourceRoot, Version, VfsPath, salsa, }; use cfg::CfgOptions; use hir_expand::{ @@ -20,7 +20,6 @@ use hir_expand::{ }; use intern::{Symbol, sym}; use paths::AbsPathBuf; -use rustc_hash::FxHashMap; use span::{Edition, FileId, Span}; use stdx::itertools::Itertools; use test_utils::{ @@ -147,7 +146,7 @@ impl ChangeFixture { let mut files = Vec::new(); let mut crate_graph = CrateGraphBuilder::default(); - let mut crates = FxHashMap::default(); + let mut crates = FxIndexMap::default(); let mut crate_deps = Vec::new(); let mut default_crate_root: Option = None; let mut default_edition = Edition::CURRENT; @@ -249,37 +248,7 @@ impl ChangeFixture { file_id = FileId::from_raw(file_id.index() + 1); } - if crates.is_empty() { - let crate_root = default_crate_root - .expect("missing default crate root, specify a main.rs or lib.rs"); - crate_graph.add_crate_root( - crate_root, - default_edition, - Some(CrateName::new("ra_test_fixture").unwrap().into()), - None, - default_cfg.clone(), - Some(default_cfg), - default_env, - CrateOrigin::Local { repo: None, name: None }, - false, - proc_macro_cwd.clone(), - crate_ws_data.clone(), - ); - } else { - for (from, to, prelude) in crate_deps { - let from_id = crates[&from]; - let to_id = crates[&to]; - let sysroot = crate_graph[to_id].basic.origin.is_lang(); - crate_graph - .add_dep( - from_id, - DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot), - ) - .unwrap(); - } - } - - if let Some(mini_core) = mini_core { + let mini_core = mini_core.map(|mini_core| { let core_file = file_id; file_id = FileId::from_raw(file_id.index() + 1); @@ -289,8 +258,6 @@ impl ChangeFixture { source_change.change_file(core_file, Some(mini_core.source_code())); - let all_crates = crate_graph.iter().collect::>(); - let core_crate = crate_graph.add_crate_root( core_file, Edition::CURRENT, @@ -308,16 +275,58 @@ impl ChangeFixture { crate_ws_data.clone(), ); - for krate in all_crates { + ( + move || { + DependencyBuilder::with_prelude( + CrateName::new("core").unwrap(), + core_crate, + true, + true, + ) + }, + core_crate, + ) + }); + + if crates.is_empty() { + let crate_root = default_crate_root + .expect("missing default crate root, specify a main.rs or lib.rs"); + let root = crate_graph.add_crate_root( + crate_root, + default_edition, + Some(CrateName::new("ra_test_fixture").unwrap().into()), + None, + default_cfg.clone(), + Some(default_cfg), + default_env, + CrateOrigin::Local { repo: None, name: None }, + false, + proc_macro_cwd.clone(), + crate_ws_data.clone(), + ); + if let Some((mini_core, _)) = mini_core { + crate_graph.add_dep(root, mini_core()).unwrap(); + } + } else { + // Insert minicore first to match with `project-model::workspace` + if let Some((mini_core, core_crate)) = mini_core { + let all_crates = crate_graph.iter().collect::>(); + for krate in all_crates { + if krate == core_crate { + continue; + } + crate_graph.add_dep(krate, mini_core()).unwrap(); + } + } + + for (from, to, prelude) in crate_deps { + let from_id = crates[&from]; + let to_id = crates[&to]; + let sysroot = crate_graph[to_id].basic.origin.is_lang(); crate_graph .add_dep( - krate, - DependencyBuilder::with_prelude( - CrateName::new("core").unwrap(), - core_crate, - true, - true, - ), + from_id, + DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot), ) .unwrap(); } @@ -627,11 +636,23 @@ impl FileMeta { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ForceNoneLangOrigin { + Yes, + No, +} + fn parse_crate( crate_str: String, current_source_root_kind: SourceRootKind, explicit_non_workspace_member: bool, ) -> (String, CrateOrigin, Option) { + let (crate_str, force_non_lang_origin) = if let Some(s) = crate_str.strip_prefix("r#") { + (s.to_owned(), ForceNoneLangOrigin::Yes) + } else { + (crate_str, ForceNoneLangOrigin::No) + }; + // syntax: // "my_awesome_crate" // "my_awesome_crate@0.0.1,http://example.com" @@ -646,16 +667,25 @@ fn parse_crate( let non_workspace_member = explicit_non_workspace_member || matches!(current_source_root_kind, SourceRootKind::Library); - let origin = match LangCrateOrigin::from(&*name) { - LangCrateOrigin::Other => { - let name = Symbol::intern(&name); - if non_workspace_member { - CrateOrigin::Library { repo, name } - } else { - CrateOrigin::Local { repo, name: Some(name) } + let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes { + let name = Symbol::intern(&name); + if non_workspace_member { + CrateOrigin::Library { repo, name } + } else { + CrateOrigin::Local { repo, name: Some(name) } + } + } else { + match LangCrateOrigin::from(&*name) { + LangCrateOrigin::Other => { + let name = Symbol::intern(&name); + if non_workspace_member { + CrateOrigin::Library { repo, name } + } else { + CrateOrigin::Local { repo, name: Some(name) } + } } + origin => CrateOrigin::Lang(origin), } - origin => CrateOrigin::Lang(origin), }; (name, origin, version) From 9c7ef48a7bedda1847acb0bfdfe7271e2268c2b7 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Aug 2025 02:06:01 +0000 Subject: [PATCH 0079/1900] Convert some of dyn_compatibility to next-solver and remove generic_predicates_without_parent_query --- .../rust-analyzer/crates/hir-ty/src/db.rs | 10 -- .../crates/hir-ty/src/dyn_compatibility.rs | 104 +++++++++++++----- .../rust-analyzer/crates/hir-ty/src/lower.rs | 16 --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 4 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 6e24aea76d445..7a5daac69922f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -179,16 +179,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)] - fn generic_predicates_without_parent_with_diagnostics( - &self, - def: GenericDefId, - ) -> (GenericPredicates, Diagnostics); - - #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] - #[salsa::transparent] - fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 2d21947ec60fc..f571b64464d92 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -13,6 +13,9 @@ use hir_def::{ TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, inherent::IntoKind, +}; use smallvec::SmallVec; use crate::{ @@ -21,6 +24,7 @@ use crate::{ db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::{generics, trait_self_param_idx}, + next_solver::{DbInterner, SolverDefId, TraitPredicate}, to_chalk_trait_id, utils::elaborate_clause_supertraits, }; @@ -319,6 +323,61 @@ fn contains_illegal_self_type_reference>( t.visit_with(visitor.as_dyn(), outer_binder).is_break() } +fn contains_illegal_self_type_reference_ns< + 'db, + T: rustc_type_ir::TypeVisitable>, +>( + db: &'db dyn HirDatabase, + trait_: TraitId, + t: &T, + allow_self_projection: AllowSelfProjection, +) -> bool { + struct IllegalSelfTypeVisitor<'db> { + db: &'db dyn HirDatabase, + trait_: TraitId, + super_traits: Option>, + allow_self_projection: AllowSelfProjection, + } + impl<'db> rustc_type_ir::TypeVisitor> for IllegalSelfTypeVisitor<'db> { + type Result = ControlFlow<()>; + + fn visit_ty( + &mut self, + ty: as rustc_type_ir::Interner>::Ty, + ) -> Self::Result { + match ty.kind() { + rustc_type_ir::TyKind::Param(param) if param.index == 0 => ControlFlow::Break(()), + rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()), + rustc_type_ir::TyKind::Alias(AliasTyKind::Projection, proj) => match self + .allow_self_projection + { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_def_id(DbInterner::new_with(self.db, None, None)); + let trait_ = match trait_ { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + if self.super_traits.is_none() { + self.super_traits = Some(all_super_traits(self.db, self.trait_)); + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } + AllowSelfProjection::No => ty.super_visit_with(self), + }, + _ => ty.super_visit_with(self), + } + } + } + + let mut visitor = + IllegalSelfTypeVisitor { db, trait_, super_traits: None, allow_self_projection }; + t.visit_with(&mut visitor).is_break() +} + fn dyn_compatibility_violation_for_assoc_item( db: &dyn HirDatabase, trait_: TraitId, @@ -415,40 +474,33 @@ where cb(MethodViolationCode::UndispatchableReceiver)?; } - let predicates = &*db.generic_predicates_without_parent(func.into()); - let trait_self_idx = trait_self_param_idx(db, func.into()); + let predicates = &*db.generic_predicates_without_parent_ns(func.into()); for pred in predicates { - let pred = pred.skip_binders().skip_binders(); + let pred = pred.kind().skip_binder(); - if matches!(pred, WhereClause::TypeOutlives(_)) { + if matches!(pred, ClauseKind::TypeOutlives(_)) { continue; } // Allow `impl AutoTrait` predicates - if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { - let trait_data = db.trait_signature(from_chalk_trait_id(*trait_id)); - if trait_data.flags.contains(TraitFlags::AUTO) - && substitution - .as_slice(Interner) - .first() - .and_then(|arg| arg.ty(Interner)) - .and_then(|ty| ty.bound_var(Interner)) - .is_some_and(|b| { - b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx - }) - { - continue; - } + let interner = DbInterner::new_with(db, None, None); + if let ClauseKind::Trait(TraitPredicate { + trait_ref: pred_trait_ref, + polarity: PredicatePolarity::Positive, + }) = pred + && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id + && let trait_data = db.trait_signature(trait_id) + && trait_data.flags.contains(TraitFlags::AUTO) + && pred_trait_ref.self_ty() + == crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + ) + { + continue; } - if contains_illegal_self_type_reference( - db, - func.into(), - trait_, - pred, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) { + if contains_illegal_self_type_reference_ns(db, trait_, &pred, AllowSelfProjection::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 7b6b3c3f8181a..065d2ea084070 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1179,22 +1179,6 @@ pub(crate) fn generic_predicates_query( generic_predicates_filtered_by(db, def, |_, _| true).0 } -pub(crate) fn generic_predicates_without_parent_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> GenericPredicates { - db.generic_predicates_without_parent_with_diagnostics(def).0 -} - -/// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent -pub(crate) fn generic_predicates_without_parent_with_diagnostics_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> (GenericPredicates, Diagnostics) { - generic_predicates_filtered_by(db, def, |_, d| d == def) -} - /// Resolve the where clause(s) of an item with generics, /// with a given filter fn generic_predicates_filtered_by( diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4eb50c1c31c52..11486ec8d6690 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3750,7 +3750,7 @@ impl GenericDef { push_ty_diagnostics( db, acc, - db.generic_predicates_without_parent_with_diagnostics(def).1, + db.generic_predicates_without_parent_with_diagnostics_ns(def).1, &source_map, ); for (param_id, param) in generics.iter_type_or_consts() { From a25a1e34eca61265a78593e5438062033b68f2ca Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Aug 2025 21:10:47 +0000 Subject: [PATCH 0080/1900] Convert more of dyn_compatibility to next-solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 253 +++++++++--------- 1 file changed, 123 insertions(+), 130 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index f571b64464d92..237cc34d7099b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; use chalk_ir::{ DebruijnIndex, - cast::Cast, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use chalk_solve::rust_ir::InlineBound; @@ -12,20 +11,26 @@ use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; +use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, inherent::IntoKind, + AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, + Upcast, + inherent::{IntoKind, SliceLike}, }; use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, - OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, - db::HirDatabase, + AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, + WhereClause, all_super_traits, + db::{HirDatabase, InternedOpaqueTyId}, from_assoc_type_id, from_chalk_trait_id, - generics::{generics, trait_self_param_idx}, - next_solver::{DbInterner, SolverDefId, TraitPredicate}, - to_chalk_trait_id, + generics::trait_self_param_idx, + next_solver::{ + Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, + infer::DbInternerInferExt, mk_param, + }, + traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, }; @@ -434,26 +439,17 @@ where cb(MethodViolationCode::AsyncFn)?; } - let sig = db.callable_item_signature(func.into()); - if sig.skip_binders().params().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference( - db, - func.into(), - trait_, - ty, - DebruijnIndex::INNERMOST, - AllowSelfProjection::Yes, - ) + let sig = db.callable_item_signature_ns(func.into()); + if sig.skip_binder().inputs().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference_ns(db, trait_, &ty, AllowSelfProjection::Yes) }) { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference( + if contains_illegal_self_type_reference_ns( db, - func.into(), trait_, - sig.skip_binders().ret(), - DebruijnIndex::INNERMOST, + &sig.skip_binder().output(), AllowSelfProjection::Yes, ) { cb(MethodViolationCode::ReferencesSelfOutput)?; @@ -483,7 +479,7 @@ where } // Allow `impl AutoTrait` predicates - let interner = DbInterner::new_with(db, None, None); + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); if let ClauseKind::Trait(TraitPredicate { trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, @@ -509,34 +505,30 @@ where ControlFlow::Continue(()) } -fn receiver_is_dispatchable( +fn receiver_is_dispatchable<'db>( db: &dyn HirDatabase, trait_: TraitId, func: FunctionId, - sig: &Binders, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> bool { - let Some(trait_self_idx) = trait_self_param_idx(db, func.into()) else { - return false; - }; + let sig = sig.instantiate_identity(); + + let interner: DbInterner<'_> = DbInterner::new_with(db, Some(trait_.krate(db)), None); + let self_param_ty = crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + ); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 - if sig - .skip_binders() - .params() - .first() - .and_then(|receiver| receiver.bound_var(Interner)) - .is_some_and(|b| { - b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } - }) - { + if sig.inputs().iter().next().is_some_and(|p| p.skip_binder() == self_param_ty) { return true; } - let placeholder_subst = generics(db, func.into()).placeholder_subst(db); - - let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); - let Some(receiver_ty) = substituted_sig.params().first() else { + let Some(&receiver_ty) = sig.inputs().skip_binder().as_slice().first() else { return false; }; @@ -549,118 +541,119 @@ fn receiver_is_dispatchable( return false; }; - // Type `U` - let unsized_self_ty = - TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); - // `Receiver[Self => U]` - let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + let meta_sized_did = LangItem::MetaSized.resolve_trait(db, krate); + let Some(meta_sized_did) = meta_sized_did else { return false; }; - let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); - let unsized_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(unsize_did), - substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), - }); - let unsized_predicate = - Binders::empty(Interner, unsized_predicate.cast::(Interner)); - let trait_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: Substitution::from_iter( - Interner, - std::iter::once(unsized_self_ty.cast(Interner)) - .chain(placeholder_subst.iter(Interner).skip(1).cloned()), - ), - }); - let trait_predicate = Binders::empty(Interner, trait_predicate.cast::(Interner)); - - let generic_predicates = &*db.generic_predicates(func.into()); + // Type `U` + let unsized_self_ty = crate::next_solver::Ty::new_param(interner, u32::MAX, Symbol::empty()); + // `Receiver[Self => U]` + let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); + + let param_env = { + let generic_predicates = &*db.generic_predicates_ns(func.into()); + + // Self: Unsize + let unsize_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(unsize_did), + [self_param_ty, unsized_self_ty], + ); + + // U: Trait + let trait_def_id = SolverDefId::TraitId(trait_); + let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { + if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) } + }); + let trait_predicate = + crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); + + let meta_sized_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(meta_sized_did), + [unsized_self_ty], + ); + + ParamEnv { + clauses: Clauses::new_from_iter( + interner, + generic_predicates.iter().copied().chain([ + unsize_predicate.upcast(interner), + trait_predicate.upcast(interner), + meta_sized_predicate.upcast(interner), + ]), + ), + } + }; - let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( - generic_predicates.iter().map(|pred| { - pred.clone() - .substitute(Interner, &placeholder_subst) - .map(|g| g.cast::(Interner)) - }), - ); - let clauses = chalk_ir::ProgramClauses::from_iter( - Interner, - goals.into_iter().map(|g| { - chalk_ir::ProgramClause::new( - Interner, - chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { - consequence: g, - conditions: chalk_ir::Goals::empty(Interner), - constraints: chalk_ir::Constraints::empty(Interner), - priority: chalk_ir::ClausePriority::High, - })), - ) - }), + // Receiver: DispatchFromDyn U]> + let predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(dispatch_from_dyn_did), + [receiver_ty, unsized_receiver_ty], ); - let env: chalk_ir::Environment = chalk_ir::Environment { clauses }; - - let obligation = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(dispatch_from_dyn_did), - substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), - }); - let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); - - let in_env = chalk_ir::InEnvironment::new(&env, goal); + let goal = crate::next_solver::Goal::new(interner, param_env, predicate); - let mut table = chalk_solve::infer::InferenceTable::::new(); - let canonicalized = table.canonicalize(Interner, in_env); - - db.trait_solve(krate, None, canonicalized.quantified).certain() + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + // the receiver is dispatchable iff the obligation holds + let res = next_trait_solve_in_ctxt(&infcx, goal); + res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) } -fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { - let generics = generics(db, func.into()); - let trait_self_idx = trait_self_param_idx(db, func.into())?; - let subst = generics.placeholder_subst(db); - let subst = Substitution::from_iter( - Interner, - subst.iter(Interner).enumerate().map(|(idx, arg)| { - if idx == trait_self_idx { ty.clone().cast(Interner) } else { arg.clone() } - }), +fn receiver_for_self_ty<'db>( + interner: DbInterner<'db>, + func: FunctionId, + receiver_ty: crate::next_solver::Ty<'db>, + self_ty: crate::next_solver::Ty<'db>, +) -> crate::next_solver::Ty<'db> { + let args = crate::next_solver::GenericArgs::for_item( + interner, + SolverDefId::FunctionId(func), + |name, index, kind, _| { + if index == 0 { self_ty.into() } else { mk_param(index, name, kind) } + }, ); - let sig = db.callable_item_signature(func.into()); - let sig = sig.substitute(Interner, &subst); - sig.params_and_return.first().cloned() + + + crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) } -fn contains_illegal_impl_trait_in_trait( - db: &dyn HirDatabase, - sig: &Binders, +fn contains_illegal_impl_trait_in_trait<'db>( + db: &'db dyn HirDatabase, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> Option { - struct OpaqueTypeCollector(FxHashSet); - - impl TypeVisitor for OpaqueTypeCollector { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } + struct OpaqueTypeCollector(FxHashSet); - fn interner(&self) -> Interner { - Interner - } + impl<'db> rustc_type_ir::TypeVisitor> for OpaqueTypeCollector { + type Result = ControlFlow<()>; - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { - self.0.insert(*opaque_ty_id); + fn visit_ty( + &mut self, + ty: as rustc_type_ir::Interner>::Ty, + ) -> Self::Result { + if let rustc_type_ir::TyKind::Alias(AliasTyKind::Opaque, op) = ty.kind() { + let id = match op.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + self.0.insert(id); } - ty.super_visit_with(self.as_dyn(), outer_binder) + ty.super_visit_with(self) } } - let ret = sig.skip_binders().ret(); + let ret = sig.skip_binder().output(); let mut visitor = OpaqueTypeCollector(FxHashSet::default()); - _ = ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + _ = ret.visit_with(&mut visitor); // Since we haven't implemented RPITIT in proper way like rustc yet, // just check whether `ret` contains RPIT for now for opaque_ty in visitor.0 { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty); if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { return Some(MethodViolationCode::ReferencesImplTraitInTrait); } From 3f78a1fd5af1bc436062b9b5045b813304b9bb05 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 9 Aug 2025 23:28:48 +0000 Subject: [PATCH 0081/1900] impl HirDisplay for next_solver::Ty --- .../crates/hir-ty/src/consteval_nextsolver.rs | 8 +- .../crates/hir-ty/src/display.rs | 551 ++++++++++-------- .../crates/hir-ty/src/dyn_compatibility.rs | 1 - .../rust-analyzer/crates/hir-ty/src/layout.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 23 + .../crates/hir-ty/src/next_solver/mapping.rs | 7 +- .../next_solver/project/solve_normalize.rs | 13 +- .../crates/hir-ty/src/primitive.rs | 34 ++ .../rust-analyzer/crates/hir-ty/src/tests.rs | 40 +- .../hir-ty/src/tests/closure_captures.rs | 12 +- .../crates/hir-ty/src/tests/opaque_types.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 6 +- .../crates/hir-ty/src/tests/traits.rs | 52 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 13 +- .../ide-completion/src/context/tests.rs | 7 +- .../crates/ide/src/hover/render.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 76 +-- .../crates/ide/src/inlay_hints/adjustment.rs | 18 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 12 +- .../crates/ide/src/navigation_target.rs | 11 +- .../rust-analyzer/crates/ide/src/runnables.rs | 18 +- .../crates/ide/src/signature_help.rs | 5 +- .../crates/ide/src/static_index.rs | 52 +- .../crates/ide/src/view_memory_layout.rs | 9 +- 24 files changed, 584 insertions(+), 392 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index da4aff54de378..cdf861290ab92 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -132,9 +132,9 @@ pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Cr ) } -pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { let interner = DbInterner::new_with(db, None, None); - match (*c).kind() { + match c.kind() { ConstKind::Param(_) => None, ConstKind::Infer(_) => None, ConstKind::Bound(_, _) => None, @@ -147,7 +147,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< }; let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); - try_const_usize(db, &ec) + try_const_usize(db, ec) } ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), ConstKind::Error(_) => None, @@ -212,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant( let c = if is_signed { try_const_isize(db, &c).unwrap() } else { - try_const_usize(db, &c).unwrap() as i128 + try_const_usize(db, c).unwrap() as i128 }; Ok(c) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 8f35a3c214551..9f87c37834d77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,8 +11,8 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GenericDefId, HasModule, ImportPathConfig, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, - ModuleId, TraitId, + GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, @@ -37,26 +37,34 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTyKind, + inherent::{AdtDef, IntoKind, SliceLike}, +}; use smallvec::SmallVec; use span::Edition; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, - ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, - LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, - QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, - TyExt, WhereClause, - consteval::try_const_usize, + AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, + ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, + LifetimeOutlives, MemoryMap, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval_nextsolver, db::{HirDatabase, InternedClosure}, - from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, + from_assoc_type_id, from_placeholder_idx, generics::generics, infer::normalize, layout::Layout, lt_from_placeholder_idx, - mapping::from_chalk, mir::pad16, + next_solver::{ + BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, + mapping::{ + ChalkToNextSolver, convert_args_for_result, convert_const_for_result, + convert_region_for_result, convert_ty_for_result, + }, + }, primitive, to_assoc_type_id, utils::{self, ClosureSubst, detect_variant_from_bytes}, }; @@ -185,6 +193,29 @@ impl HirFormatter<'_> { DisplayLifetime::Never => false, } } + + fn render_region(&self, lifetime: crate::next_solver::Region<'_>) -> bool { + match self.display_lifetimes { + DisplayLifetime::Always => true, + DisplayLifetime::OnlyStatic => { + matches!(lifetime.kind(), rustc_type_ir::RegionKind::ReStatic) + } + DisplayLifetime::OnlyNamed => { + matches!( + lifetime.kind(), + rustc_type_ir::RegionKind::RePlaceholder(_) + | rustc_type_ir::RegionKind::ReEarlyParam(_) + ) + } + DisplayLifetime::OnlyNamedOrStatic => matches!( + lifetime.kind(), + rustc_type_ir::RegionKind::ReStatic + | rustc_type_ir::RegionKind::RePlaceholder(_) + | rustc_type_ir::RegionKind::ReEarlyParam(_) + ), + DisplayLifetime::Never => false, + } + } } pub trait HirDisplay { @@ -476,10 +507,6 @@ impl DisplayKind { matches!(self, Self::SourceCode { .. }) } - fn is_test(self) -> bool { - matches!(self, Self::Test) - } - fn allows_opaque(self) -> bool { match self { Self::SourceCode { allow_opaque, .. } => allow_opaque, @@ -721,59 +748,87 @@ fn render_const_scalar( ty: &Ty, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); let ty = normalize(f.db, trait_env.clone(), ty.clone()); - match ty.kind(Interner) { - TyKind::Scalar(s) => match s { - Scalar::Bool => write!(f, "{}", b[0] != 0), - Scalar::Char => { - let it = u128::from_le_bytes(pad16(b, false)) as u32; - let Ok(c) = char::try_from(it) else { - return f.write_str(""); - }; - write!(f, "{c:?}") - } - Scalar::Int(_) => { - let it = i128::from_le_bytes(pad16(b, true)); - write!(f, "{it}") - } - Scalar::Uint(_) => { - let it = u128::from_le_bytes(pad16(b, false)); - write!(f, "{it}") - } - Scalar::Float(fl) => match fl { - chalk_ir::FloatTy::F16 => { - // FIXME(#17451): Replace with builtins once they are stabilised. - let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into()); - let s = it.to_string(); - if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { - // Match Rust debug formatting - write!(f, "{s}.0") - } else { - write!(f, "{s}") - } - } - chalk_ir::FloatTy::F32 => { - let it = f32::from_le_bytes(b.try_into().unwrap()); - write!(f, "{it:?}") - } - chalk_ir::FloatTy::F64 => { - let it = f64::from_le_bytes(b.try_into().unwrap()); - write!(f, "{it:?}") + let ty = ty.to_nextsolver(interner); + render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) +} + +fn render_const_scalar_ns( + f: &mut HirFormatter<'_>, + b: &[u8], + memory_map: &MemoryMap, + ty: crate::next_solver::Ty<'_>, +) -> Result<(), HirDisplayError> { + let trait_env = TraitEnvironment::empty(f.krate()); + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); + let ty = crate::next_solver::project::solve_normalize::normalize( + interner, + trait_env.env.to_nextsolver(interner), + ty, + ); + render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) +} + +fn render_const_scalar_inner( + f: &mut HirFormatter<'_>, + b: &[u8], + memory_map: &MemoryMap, + ty: crate::next_solver::Ty<'_>, + trait_env: Arc, + interner: DbInterner<'_>, +) -> Result<(), HirDisplayError> { + use rustc_type_ir::TyKind; + match ty.kind() { + TyKind::Bool => write!(f, "{}", b[0] != 0), + TyKind::Char => { + let it = u128::from_le_bytes(pad16(b, false)) as u32; + let Ok(c) = char::try_from(it) else { + return f.write_str(""); + }; + write!(f, "{c:?}") + } + TyKind::Int(_) => { + let it = i128::from_le_bytes(pad16(b, true)); + write!(f, "{it}") + } + TyKind::Uint(_) => { + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{it}") + } + TyKind::Float(fl) => match fl { + rustc_type_ir::FloatTy::F16 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into()); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") } - chalk_ir::FloatTy::F128 => { - // FIXME(#17451): Replace with builtins once they are stabilised. - let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap())); - let s = it.to_string(); - if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { - // Match Rust debug formatting - write!(f, "{s}.0") - } else { - write!(f, "{s}") - } + } + rustc_type_ir::FloatTy::F32 => { + let it = f32::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") + } + rustc_type_ir::FloatTy::F64 => { + let it = f64::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") + } + rustc_type_ir::FloatTy::F128 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap())); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") } - }, + } }, - TyKind::Ref(_, _, t) => match t.kind(Interner) { + TyKind::Ref(_, t, _) => match t.kind() { TyKind::Str => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); @@ -786,7 +841,7 @@ fn render_const_scalar( TyKind::Slice(ty) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -810,11 +865,11 @@ fn render_const_scalar( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, ty)?; + render_const_scalar_ns(f, &bytes[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } - TyKind::Dyn(_) => { + TyKind::Dynamic(_, _, _) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); let Ok(t) = memory_map.vtable_ty(ty_id) else { @@ -828,15 +883,16 @@ fn render_const_scalar( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar(f, bytes, memory_map, t) + render_const_scalar_ns(f, bytes, memory_map, t.to_nextsolver(interner)) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.0 { - hir_def::AdtId::StructId(s) => { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { + SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { let data = f.db.struct_signature(s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; Ok(()) } - _ => f.write_str(""), + SolverDefId::AdtId(_) => f.write_str(""), + _ => unreachable!(), }, _ => { let addr = usize::from_le_bytes(match b.try_into() { @@ -850,7 +906,7 @@ fn render_const_scalar( return f.write_str(""); } }); - let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -858,37 +914,40 @@ fn render_const_scalar( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar(f, bytes, memory_map, t) + render_const_scalar_ns(f, bytes, memory_map, t) } }, - TyKind::Tuple(_, subst) => { - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + TyKind::Tuple(tys) => { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { return f.write_str(""); }; f.write_str("(")?; let mut first = true; - for (id, ty) in subst.iter(Interner).enumerate() { + for (id, ty) in tys.iter().enumerate() { if first { first = false; } else { f.write_str(", ")?; } - let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { f.write_str("")?; continue; }; let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, ty)?; + render_const_scalar_ns(f, &b[offset..offset + size], memory_map, ty)?; } f.write_str(")") } - TyKind::Adt(adt, subst) => { - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), trait_env.clone()) else { + TyKind::Adt(def, args) => { + let def = match def.def_id() { + SolverDefId::AdtId(def) => def, + _ => unreachable!(), + }; + let Ok(layout) = f.db.layout_of_adt_ns(def, args, trait_env.clone()) else { return f.write_str(""); }; - match adt.0 { + match def { hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "{}", data.name.display(f.db, f.edition()))?; @@ -897,9 +956,9 @@ fn render_const_scalar( s.fields(f.db), f, &field_types, - f.db.trait_environment(adt.0.into()), + f.db.trait_environment(def.into()), &layout, - subst, + args, b, memory_map, ) @@ -929,9 +988,9 @@ fn render_const_scalar( var_id.fields(f.db), f, &field_types, - f.db.trait_environment(adt.0.into()), + f.db.trait_environment(def.into()), var_layout, - subst, + args, b, memory_map, ) @@ -939,16 +998,16 @@ fn render_const_scalar( } } TyKind::FnDef(..) => ty.hir_fmt(f), - TyKind::Function(_) | TyKind::Raw(_, _) => { + TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => { let it = u128::from_le_bytes(pad16(b, false)); write!(f, "{it:#X} as ")?; ty.hir_fmt(f) } TyKind::Array(ty, len) => { - let Some(len) = try_const_usize(f.db, len) else { + let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -961,7 +1020,7 @@ fn render_const_scalar( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar(f, &b[offset..offset + size_one], memory_map, ty)?; + render_const_scalar_ns(f, &b[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } @@ -969,17 +1028,19 @@ fn render_const_scalar( TyKind::Closure(_, _) => f.write_str(""), TyKind::Coroutine(_, _) => f.write_str(""), TyKind::CoroutineWitness(_, _) => f.write_str(""), + TyKind::CoroutineClosure(_, _) => f.write_str(""), + TyKind::UnsafeBinder(_) => f.write_str(""), // The below arms are unreachable, since const eval will bail out before here. TyKind::Foreign(_) => f.write_str(""), - TyKind::Error + TyKind::Pat(_, _) => f.write_str(""), + TyKind::Error(..) | TyKind::Placeholder(_) - | TyKind::Alias(_) - | TyKind::AssociatedType(_, _) - | TyKind::OpaqueType(_, _) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => f.write_str(""), + | TyKind::Alias(_, _) + | TyKind::Param(_) + | TyKind::Bound(_, _) + | TyKind::Infer(_) => f.write_str(""), // The below arms are unreachable, since we handled them in ref case. - TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str(""), + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _, _) => f.write_str(""), } } @@ -989,15 +1050,18 @@ fn render_variant_after_name( field_types: &ArenaMap>, trait_env: Arc, layout: &Layout, - subst: &Substitution, + args: GenericArgs<'_>, b: &[u8], memory_map: &MemoryMap, ) -> Result<(), HirDisplayError> { + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); match data.shape { FieldsShape::Record | FieldsShape::Tuple => { let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); - let ty = field_types[id].clone().substitute(Interner, subst); + let ty = field_types[id] + .clone() + .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { return f.write_str(""); }; @@ -1045,18 +1109,30 @@ impl HirDisplay for Ty { &self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, ) -> Result<(), HirDisplayError> { + let ty = self.to_nextsolver(DbInterner::new_with(db, None, None)); + ty.hir_fmt(f) + } +} + +impl<'db> HirDisplay for crate::next_solver::Ty<'db> { + fn hir_fmt( + &self, + f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, + ) -> Result<(), HirDisplayError> { + let interner = DbInterner::new_with(db, None, None); if f.should_truncate() { return write!(f, "{TYPE_HINT_TRUNCATION}"); } - match self.kind(Interner) { + use rustc_type_ir::TyKind; + match self.kind() { TyKind::Never => write!(f, "!")?, TyKind::Str => write!(f, "str")?, - TyKind::Scalar(Scalar::Bool) => write!(f, "bool")?, - TyKind::Scalar(Scalar::Char) => write!(f, "char")?, - &TyKind::Scalar(Scalar::Float(t)) => write!(f, "{}", primitive::float_ty_to_string(t))?, - &TyKind::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?, - &TyKind::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?, + TyKind::Bool => write!(f, "bool")?, + TyKind::Char => write!(f, "char")?, + TyKind::Float(t) => write!(f, "{}", primitive::float_ty_to_string_ns(t))?, + TyKind::Int(t) => write!(f, "{}", primitive::int_ty_to_string_ns(t))?, + TyKind::Uint(t) => write!(f, "{}", primitive::uint_ty_to_string_ns(t))?, TyKind::Slice(t) => { write!(f, "[")?; t.hir_fmt(f)?; @@ -1066,27 +1142,27 @@ impl HirDisplay for Ty { write!(f, "[")?; t.hir_fmt(f)?; write!(f, "; ")?; - c.hir_fmt(f)?; + convert_const_for_result(interner, c).hir_fmt(f)?; write!(f, "]")?; } - kind @ (TyKind::Raw(m, t) | TyKind::Ref(m, _, t)) => { - if let TyKind::Ref(_, l, _) = kind { + kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => { + if let TyKind::Ref(l, _, _) = kind { f.write_char('&')?; - if f.render_lifetime(l) { - l.hir_fmt(f)?; + if f.render_region(l) { + convert_region_for_result(l).hir_fmt(f)?; f.write_char(' ')?; } match m { - Mutability::Not => (), - Mutability::Mut => f.write_str("mut ")?, + rustc_ast_ir::Mutability::Not => (), + rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?, } } else { write!( f, "*{}", match m { - Mutability::Not => "const ", - Mutability::Mut => "mut ", + rustc_ast_ir::Mutability::Not => "const ", + rustc_ast_ir::Mutability::Mut => "mut ", } )?; } @@ -1102,25 +1178,42 @@ impl HirDisplay for Ty { } }) }; - let (preds_to_print, has_impl_fn_pred) = match t.kind(Interner) { - TyKind::Dyn(dyn_ty) => { - let bounds = dyn_ty.bounds.skip_binders().interned(); - let render_lifetime = f.render_lifetime(&dyn_ty.lifetime); - (bounds.len() + render_lifetime as usize, contains_impl_fn(bounds)) + let contains_impl_fn_ns = |bounds: &[BoundExistentialPredicate<'_>]| { + bounds.iter().any(|bound| match bound.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ = match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + fn_traits(db, trait_).any(|it| it == trait_) + } + _ => false, + }) + }; + let (preds_to_print, has_impl_fn_pred) = match t.kind() { + TyKind::Dynamic(bounds, region, _) => { + let render_lifetime = f.render_region(region); + ( + bounds.len() + render_lifetime as usize, + contains_impl_fn_ns(bounds.as_slice()), + ) } - TyKind::Alias(AliasTy::Opaque(OpaqueTy { - opaque_ty_id, - substitution: parameters, - })) - | TyKind::OpaqueType(opaque_ty_id, parameters) => { - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); + TyKind::Alias(AliasTyKind::Opaque, ty) => { + let opaque_ty_id = match ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { let datas = db .return_type_impl_traits(func) .expect("impl trait id without data"); let data = (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, parameters); + let bounds = data.substitute( + Interner, + &convert_args_for_result(interner, ty.args.as_slice()), + ); let mut len = bounds.skip_binders().len(); // Don't count Sized but count when it absent @@ -1167,24 +1260,31 @@ impl HirDisplay for Ty { t.hir_fmt(f)?; } } - TyKind::Tuple(_, substs) => { - if substs.len(Interner) == 1 { + TyKind::Tuple(tys) => { + if tys.len() == 1 { write!(f, "(")?; - substs.at(Interner, 0).hir_fmt(f)?; + tys.as_slice()[0].hir_fmt(f)?; write!(f, ",)")?; } else { write!(f, "(")?; - f.write_joined(substs.as_slice(Interner), ", ")?; + f.write_joined(tys.as_slice(), ", ")?; write!(f, ")")?; } } - TyKind::Function(fn_ptr) => { - let sig = CallableSig::from_fn_ptr(fn_ptr); + TyKind::FnPtr(sig, header) => { + let sig = CallableSig::from_fn_sig_and_header(interner, sig, header); sig.hir_fmt(f)?; } - TyKind::FnDef(def, parameters) => { - let def = from_chalk(db, *def); - let sig = db.callable_item_signature(def).substitute(Interner, parameters); + TyKind::FnDef(def, args) => { + let def = match def { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), + SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), + _ => unreachable!(), + }; + let sig = db + .callable_item_signature(def) + .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); if f.display_kind.is_source_code() { // `FnDef` is anonymous and there's no surface syntax for it. Show it as a @@ -1222,6 +1322,7 @@ impl HirDisplay for Ty { }; f.end_location_link(); + let parameters = convert_args_for_result(interner, args.as_slice()); if parameters.len(Interner) > 0 { let generic_def_id = GenericDefId::from_callable(db, def); let generics = generics(db, generic_def_id); @@ -1280,11 +1381,15 @@ impl HirDisplay for Ty { ret.hir_fmt(f)?; } } - TyKind::Adt(AdtId(def_id), parameters) => { - f.start_location_link((*def_id).into()); + TyKind::Adt(def, parameters) => { + let def_id = match def.def_id() { + SolverDefId::AdtId(id) => id, + _ => unreachable!(), + }; + f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { - let name = match *def_id { + let name = match def_id { hir_def::AdtId::StructId(it) => db.struct_signature(it).name.clone(), hir_def::AdtId::UnionId(it) => db.union_signature(it).name.clone(), hir_def::AdtId::EnumId(it) => db.enum_signature(it).name.clone(), @@ -1294,7 +1399,7 @@ impl HirDisplay for Ty { DisplayKind::SourceCode { target_module_id: module_id, allow_opaque: _ } => { if let Some(path) = find_path::find_path( db, - ItemInNs::Types((*def_id).into()), + ItemInNs::Types(def_id.into()), module_id, PrefixKind::Plain, false, @@ -1316,55 +1421,49 @@ impl HirDisplay for Ty { } f.end_location_link(); - let generic_def = self.as_generic_def(db); - - hir_fmt_generics(f, parameters.as_slice(Interner), generic_def, None)?; + hir_fmt_generics( + f, + convert_args_for_result(interner, parameters.as_slice()).as_slice(Interner), + def.def_id().try_into().ok(), + None, + )?; } - TyKind::AssociatedType(assoc_type_id, parameters) => { - let type_alias = from_assoc_type_id(*assoc_type_id); - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(it) => it, - _ => panic!("not an associated type"), + TyKind::Alias(AliasTyKind::Projection, alias_ty) => { + let type_alias = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), }; - let trait_data = db.trait_signature(trait_); - let type_alias_data = db.type_alias_signature(type_alias); - - // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) - if f.display_kind.is_test() { - f.start_location_link(trait_.into()); - write!(f, "{}", trait_data.name.display(f.db, f.edition()))?; - f.end_location_link(); - write!(f, "::")?; + let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); - f.start_location_link(type_alias.into()); - write!(f, "{}", type_alias_data.name.display(f.db, f.edition()))?; - f.end_location_link(); - // Note that the generic args for the associated type come before those for the - // trait (including the self type). - hir_fmt_generics(f, parameters.as_slice(Interner), None, None) - } else { - let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(type_alias), - substitution: parameters.clone(), - }; + let projection_ty = ProjectionTy { + associated_ty_id: to_assoc_type_id(type_alias), + substitution: parameters.clone(), + }; - projection_ty.hir_fmt(f) - }?; + projection_ty.hir_fmt(f)?; } TyKind::Foreign(type_alias) => { - let alias = from_foreign_def_id(*type_alias); + let alias = match type_alias { + SolverDefId::ForeignId(id) => id, + _ => unreachable!(), + }; let type_alias = db.type_alias_signature(alias); f.start_location_link(alias.into()); write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; f.end_location_link(); } - TyKind::OpaqueType(opaque_ty_id, parameters) => { + TyKind::Alias(AliasTyKind::Opaque, alias_ty) => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::OpaqueType, )); } - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { let datas = @@ -1376,7 +1475,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1391,7 +1490,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1426,6 +1525,11 @@ impl HirDisplay for Ty { } } TyKind::Closure(id, substs) => { + let id = match id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let substs = convert_args_for_result(interner, substs.as_slice()); if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( @@ -1435,22 +1539,23 @@ impl HirDisplay for Ty { never!("Only `impl Fn` is valid for displaying closures in source code"); } } + let chalk_id: chalk_ir::ClosureId<_> = id.into(); match f.closure_style { ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), ClosureStyle::ClosureWithId => { - return write!(f, "{{closure#{:?}}}", id.0.index()); + return write!(f, "{{closure#{:?}}}", chalk_id.0.index()); } ClosureStyle::ClosureWithSubst => { - write!(f, "{{closure#{:?}}}", id.0.index())?; + write!(f, "{{closure#{:?}}}", chalk_id.0.index())?; return hir_fmt_generics(f, substs.as_slice(Interner), None, None); } _ => (), } - let sig = ClosureSubst(substs).sig_ty().callable_sig(db); + let sig = ClosureSubst(&substs).sig_ty().callable_sig(db); if let Some(sig) = sig { - let InternedClosure(def, _) = db.lookup_intern_closure((*id).into()); + let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); - let (_, kind) = infer.closure_info(id); + let (_, kind) = infer.closure_info(&chalk_id); match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, ClosureStyle::RANotation => write!(f, "|")?, @@ -1478,7 +1583,11 @@ impl HirDisplay for Ty { } } TyKind::Placeholder(idx) => { - let id = from_placeholder_idx(db, *idx); + let placeholder_index = chalk_ir::PlaceholderIndex { + idx: idx.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: idx.universe.as_usize() }, + }; + let id = from_placeholder_idx(db, placeholder_index); let generics = generics(db, id.parent); let param_data = &generics[id.local_id]; match param_data { @@ -1501,14 +1610,20 @@ impl HirDisplay for Ty { .map(|pred| pred.clone().substitute(Interner, &substs)) .filter(|wc| match wc.skip_binders() { WhereClause::Implemented(tr) => { - tr.self_type_parameter(Interner) == *self + tr.self_type_parameter(Interner) + == convert_ty_for_result(interner, *self) } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _, - }) => proj.self_type_parameter(db) == *self, + }) => { + proj.self_type_parameter(db) + == convert_ty_for_result(interner, *self) + } WhereClause::AliasEq(_) => false, - WhereClause::TypeOutlives(to) => to.ty == *self, + WhereClause::TypeOutlives(to) => { + to.ty == convert_ty_for_result(interner, *self) + } WhereClause::LifetimeOutlives(_) => false, }) .collect::>(); @@ -1516,7 +1631,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), &bounds, SizedByDefault::Sized { anchor: krate }, )?; @@ -1527,8 +1642,17 @@ impl HirDisplay for Ty { } } } - TyKind::BoundVar(idx) => idx.hir_fmt(f)?, - TyKind::Dyn(dyn_ty) => { + TyKind::Param(_) => write!(f, "{{param}}")?, + TyKind::Bound(debruijn_index, ty) => { + let idx = chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }; + idx.hir_fmt(f)? + } + TyKind::Dynamic(..) => { + let ty = convert_ty_for_result(interner, *self); + let chalk_ir::TyKind::Dyn(dyn_ty) = ty.kind(Interner) else { unreachable!() }; // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation. // FIXME: `Iterator::partition_in_place()` or `Vec::extract_if()` may make it // more efficient when either of them hits stable. @@ -1544,7 +1668,7 @@ impl HirDisplay for Ty { bounds.push(Binders::empty( Interner, chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { - ty: self.clone(), + ty: ty.clone(), lifetime: dyn_ty.lifetime.clone(), }), )); @@ -1553,69 +1677,26 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "dyn", - Either::Left(self), + Either::Left(&ty), &bounds, SizedByDefault::NotSized, )?; } - TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?, - TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { - if !f.display_kind.allows_opaque() { - return Err(HirDisplayError::DisplaySourceCodeError( - DisplaySourceCodeError::OpaqueType, - )); - } - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()); - match impl_trait_id { - ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = - db.return_type_impl_traits(func).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, &opaque_ty.substitution); - let krate = func.krate(db); - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left(self), - bounds.skip_binders(), - SizedByDefault::Sized { anchor: krate }, - )?; - } - ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = - db.type_alias_impl_traits(alias).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, &opaque_ty.substitution); - let krate = alias.krate(db); - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left(self), - bounds.skip_binders(), - SizedByDefault::Sized { anchor: krate }, - )?; - } - ImplTraitId::AsyncBlockTypeImplTrait(..) => { - write!(f, "{{async block}}")?; - } - }; - } - TyKind::Error => { + TyKind::Error(_) => { if f.display_kind.is_source_code() { f.write_char('_')?; } else { write!(f, "{{unknown}}")?; } } - TyKind::InferenceVar(..) => write!(f, "_")?, + TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(_, subst) => { if f.display_kind.is_source_code() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::Coroutine, )); } + let subst = convert_args_for_result(interner, subst.as_slice()); let subst = subst.as_slice(Interner); let a: Option> = subst .get(subst.len() - 3..) @@ -1637,6 +1718,10 @@ impl HirDisplay for Ty { } } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, + TyKind::Pat(_, _) => write!(f, "{{pat}}")?, + TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?, + TyKind::CoroutineClosure(_, _) => write!(f, "{{coroutine closure}}")?, + TyKind::Alias(_, _) => write!(f, "{{alias}}")?, } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 237cc34d7099b..8bd555f5c08f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -615,7 +615,6 @@ fn receiver_for_self_ty<'db>( }, ); - crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 2d0471d7e5545..fb7b5e1c83e4c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -153,7 +153,7 @@ fn layout_of_simd_ty<'db>( return Err(LayoutError::InvalidSimdType); }; - let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; + let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64; let e_ly = db.layout_of_ty_ns(e_ty, env)?; let cx = LayoutCx::new(dl); @@ -272,7 +272,7 @@ pub fn layout_of_ty_ns_query<'db>( cx.calc.univariant(&fields, &ReprOptions::default(), kind)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; + let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2b0dc931eaf4a..81894830b5b47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -90,6 +90,7 @@ use intern::{Symbol, sym}; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::SliceLike; use syntax::ast::{ConstArg, make}; use traits::FnTrait; use triomphe::Arc; @@ -100,6 +101,7 @@ use crate::{ display::{DisplayTarget, HirDisplay}, generics::Generics, infer::unify::InferenceTable, + next_solver::{DbInterner, mapping::convert_ty_for_result}, }; pub use autoderef::autoderef; @@ -560,6 +562,27 @@ impl CallableSig { abi: fn_ptr.sig.abi, } } + pub fn from_fn_sig_and_header<'db>( + interner: DbInterner<'db>, + sig: crate::next_solver::Binder<'db, rustc_type_ir::FnSigTys>>, + header: rustc_type_ir::FnHeader>, + ) -> CallableSig { + CallableSig { + // FIXME: what to do about lifetime params? -> return PolyFnSig + params_and_return: Arc::from_iter( + sig.skip_binder() + .inputs_and_output + .iter() + .map(|t| convert_ty_for_result(interner, t)), + ), + is_varargs: header.c_variadic, + safety: match header.safety { + next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + abi: header.abi, + } + } pub fn to_fn_ptr(&self) -> FnPointer { FnPointer { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 20cd8626f299b..b50fccb832625 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1290,7 +1290,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) .intern(Interner) } -fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const { +pub fn convert_const_for_result<'db>( + interner: DbInterner<'db>, + const_: Const<'db>, +) -> crate::Const { let value: chalk_ir::ConstValue = match const_.kind() { rustc_type_ir::ConstKind::Param(_) => unimplemented!(), rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { @@ -1343,7 +1346,7 @@ fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) } -fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { +pub fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { match region.kind() { rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs index a8fb2ae14f1c3..42c238febf6f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs @@ -11,16 +11,25 @@ use rustc_type_ir::{ use crate::next_solver::{ Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, - TyKind, + TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - InferCtxt, + DbInternerInferExt, InferCtxt, at::At, traits::{Obligation, ObligationCause}, }, util::PlaceholderReplacer, }; +pub fn normalize<'db, T>(interner: DbInterner<'db>, param_env: ParamEnv<'db>, value: T) -> T +where + T: TypeFoldable>, +{ + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = ObligationCause::dummy(); + deeply_normalize(infer_ctxt.at(&cause, param_env), value.clone()).unwrap_or(value) +} + /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs index a4e077ba6359f..d2901f7fc53d2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs @@ -34,6 +34,40 @@ pub fn float_ty_to_string(ty: FloatTy) -> &'static str { } } +pub fn int_ty_to_string_ns(ty: rustc_type_ir::IntTy) -> &'static str { + use rustc_type_ir::IntTy; + match ty { + IntTy::Isize => "isize", + IntTy::I8 => "i8", + IntTy::I16 => "i16", + IntTy::I32 => "i32", + IntTy::I64 => "i64", + IntTy::I128 => "i128", + } +} + +pub fn uint_ty_to_string_ns(ty: rustc_type_ir::UintTy) -> &'static str { + use rustc_type_ir::UintTy; + match ty { + UintTy::Usize => "usize", + UintTy::U8 => "u8", + UintTy::U16 => "u16", + UintTy::U32 => "u32", + UintTy::U64 => "u64", + UintTy::U128 => "u128", + } +} + +pub fn float_ty_to_string_ns(ty: rustc_type_ir::FloatTy) -> &'static str { + use rustc_type_ir::FloatTy; + match ty { + FloatTy::F16 => "f16", + FloatTy::F32 => "f32", + FloatTy::F64 => "f64", + FloatTy::F128 => "f128", + } +} + pub(super) fn int_ty_from_builtin(t: BuiltinInt) -> IntTy { match t { BuiltinInt::Isize => IntTy::Isize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d98b6602efe8c..1c3da438cb364 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -157,11 +157,13 @@ fn check_impl( }; let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { - let actual = if display_source { - ty.display_source_code(&db, def.module(&db), true).unwrap() - } else { - ty.display_test(&db, display_target).to_string() - }; + let actual = salsa::attach(&db, || { + if display_source { + ty.display_source_code(&db, def.module(&db), true).unwrap() + } else { + ty.display_test(&db, display_target).to_string() + } + }); assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } } @@ -173,11 +175,13 @@ fn check_impl( }; let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { - let actual = if display_source { - ty.display_source_code(&db, def.module(&db), true).unwrap() - } else { - ty.display_test(&db, display_target).to_string() - }; + let actual = salsa::attach(&db, || { + if display_source { + ty.display_source_code(&db, def.module(&db), true).unwrap() + } else { + ty.display_test(&db, display_target).to_string() + } + }); assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } if let Some(expected) = adjustments.remove(&range) { @@ -203,11 +207,13 @@ fn check_impl( continue; }; let range = node.as_ref().original_file_range_rooted(&db); - let actual = format!( - "expected {}, got {}", - mismatch.expected.display_test(&db, display_target), - mismatch.actual.display_test(&db, display_target) - ); + let actual = salsa::attach(&db, || { + format!( + "expected {}, got {}", + mismatch.expected.display_test(&db, display_target), + mismatch.actual.display_test(&db, display_target) + ) + }); match mismatches.remove(&range) { Some(annotation) => assert_eq!(actual, annotation), None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual), @@ -402,7 +408,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in defs { let (body, source_map) = db.body_with_source_map(def); let infer = db.infer(def); - infer_def(infer, body, source_map, krate); + salsa::attach(&db, || { + infer_def(infer, body, source_map, krate); + }) } buf.truncate(buf.trim_end().len()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 5893894c33ab7..b001ac1e82ea2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -67,11 +67,13 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec .join(", "), }; let place = capture.display_place(closure.0, db); - let capture_ty = capture - .ty - .skip_binders() - .display_test(db, DisplayTarget::from_crate(db, module.krate())) - .to_string(); + let capture_ty = salsa::attach(db, || { + capture + .ty + .skip_binders() + .display_test(db, DisplayTarget::from_crate(db, module.krate())) + .to_string() + }); let spans = capture .spans() .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 36578545a9f49..11d1a58e5367e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -71,7 +71,7 @@ fn test() { let x = S3.baz(); //^ Binary> let y = x.1.0.bar(); - //^ Unary> + //^ Unary<::Item> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 212fec4a4e4f4..fd8c641d86eb0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2299,10 +2299,10 @@ trait Foo { } "#, expect![[r#" - 83..86 'bar': Foo::Bar + 83..86 'bar': ::Bar 105..133 '{ ... }': () - 119..120 '_': Foo::Bar - 123..126 'bar': Foo::Bar + 119..120 '_': ::Bar + 123..126 'bar': ::Bar "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 9d72105624a05..a311e579744df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -408,11 +408,11 @@ fn test() { let x: ::Item = 1; // ^ u32 let y: ::Item = u; - // ^ Iterable::Item + // ^ ::Item let z: T::Item = u; - // ^ Iterable::Item + // ^ ::Item let a: ::Item = u; - // ^ Iterable::Item + // ^ ::Item }"#, ); } @@ -454,7 +454,7 @@ impl S { fn test() { let s: S; s.foo(); - // ^^^^^^^ Iterable::Item + // ^^^^^^^ ::Item }"#, ); } @@ -470,7 +470,7 @@ trait Foo { type A; fn test(a: Self::A, _: impl Bar) { a; - //^ Foo::A + //^ ::A } }"#, ); @@ -969,7 +969,7 @@ impl ApplyL for RefMutL { fn test() { let y: as ApplyL>::Out = no_matter; y; -} //^ ApplyL::Out +} //^ ::Out "#, ); } @@ -986,7 +986,7 @@ fn foo(t: T) -> ::Out; fn test(t: T) { let y = foo(t); y; -} //^ ApplyL::Out +} //^ ::Out "#, ); } @@ -1695,7 +1695,7 @@ fn test>(x: T, y: impl Trait) { }"#, expect![[r#" 49..50 't': T - 77..79 '{}': Trait::Type + 77..79 '{}': ::Type 111..112 't': T 122..124 '{}': U 154..155 't': T @@ -2293,7 +2293,7 @@ impl Trait for S2 { }"#, expect![[r#" 40..44 'self': &'? Self - 46..47 'x': Trait::Item + 46..47 'x': ::Item 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () @@ -2410,7 +2410,7 @@ trait Trait2 {} fn test() where T: Trait2 { let x: T::Item = no_matter; -} //^^^^^^^^^ Trait::Item +} //^^^^^^^^^ ::Item "#, ); } @@ -2445,7 +2445,7 @@ trait Trait { fn test() where T: Trait { let x: T::Item = no_matter; -} //^^^^^^^^^ Trait::Item +} //^^^^^^^^^ ::Item "#, ); } @@ -2475,7 +2475,7 @@ fn test(t: T) where T: UnificationStoreMut { t.push(x); let y: Key; (x, y); -} //^^^^^^ (UnificationStoreBase::Key, UnificationStoreBase::Key) +} //^^^^^^ (::Key, ::Key) "#, ); } @@ -3488,7 +3488,7 @@ fn foo() { let x = ::boo(); }"#, expect![[r#" - 132..163 '{ ... }': Bar::Output + 132..163 '{ ... }': ::Output 146..153 'loop {}': ! 151..153 '{}': () 306..358 '{ ...o(); }': () @@ -4213,7 +4213,7 @@ fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); //^ &'a T let a = v.get::<()>(); - //^ Trait::Assoc = &'a T>, ()> + //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); @@ -4280,7 +4280,7 @@ where let a = t.get::(); //^ usize let a = t.get::<()>(); - //^ Trait::Assoc + //^ ::Assoc<()> } "#, @@ -4857,29 +4857,29 @@ async fn baz i32>(c: T) { 37..38 'a': T 43..83 '{ ...ait; }': () 43..83 '{ ...ait; }': impl Future - 53..57 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 53..57 'fut1': >::CallRefFuture<'?> 60..61 'a': T - 60..64 'a(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 60..64 'a(0)': >::CallRefFuture<'?> 62..63 '0': u32 - 70..74 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 70..74 'fut1': >::CallRefFuture<'?> 70..80 'fut1.await': i32 124..129 'mut b': T 134..174 '{ ...ait; }': () 134..174 '{ ...ait; }': impl Future - 144..148 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 144..148 'fut2': >::CallRefFuture<'?> 151..152 'b': T - 151..155 'b(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 151..155 'b(0)': >::CallRefFuture<'?> 153..154 '0': u32 - 161..165 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 161..165 'fut2': >::CallRefFuture<'?> 161..171 'fut2.await': i32 216..217 'c': T 222..262 '{ ...ait; }': () 222..262 '{ ...ait; }': impl Future - 232..236 'fut3': AsyncFnOnce::CallOnceFuture + 232..236 'fut3': >::CallOnceFuture 239..240 'c': T - 239..243 'c(0)': AsyncFnOnce::CallOnceFuture + 239..243 'c(0)': >::CallOnceFuture 241..242 '0': u32 - 249..253 'fut3': AsyncFnOnce::CallOnceFuture + 249..253 'fut3': >::CallOnceFuture 249..259 'fut3.await': i32 "#]], ); @@ -4947,7 +4947,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': Deserializer::Error<'de, D> + 135..138 '{ }': >::Error "#]], ); } @@ -5020,7 +5020,7 @@ fn main() { 294..298 'iter': Box + 'static> 294..310 'iter.i...iter()': Box + 'static> 152..156 'self': &'? mut Box - 177..208 '{ ... }': Option> + 177..208 '{ ... }': Option<::Item> 191..198 'loop {}': ! 196..198 '{}': () "#]], diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 11486ec8d6690..4dde019b50063 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -85,7 +85,9 @@ use hir_ty::{ layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution, mir::{MutBorrowKind, interpret_mir}, - next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt}, + next_solver::{ + DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, mapping::ChalkToNextSolver, + }, primitive::UintTy, traits::FnTrait, }; @@ -1814,12 +1816,15 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - db.layout_of_adt( + let env = db.trait_environment(self.into()); + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + db.layout_of_adt_ns( self.into(), TyBuilder::adt(db, self.into()) .fill_with_defaults(db, || TyKind::Error.intern(Interner)) - .build_into_subst(), - db.trait_environment(self.into()), + .build_into_subst() + .to_nextsolver(interner), + env, ) .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).id).unwrap())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 7a8c70f190efc..f03f61d10e5f7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -1,3 +1,4 @@ +use base_db::salsa; use expect_test::{Expect, expect}; use hir::HirDisplay; @@ -13,7 +14,11 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, let ty = completion_context .expected_type - .map(|t| t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string()) + .map(|t| { + salsa::attach(&db, || { + t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string() + }) + }) .unwrap_or("?".to_owned()); let name = diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index e4c7d42ffe3b1..1f9d10c92b1df 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -912,7 +912,7 @@ pub(super) fn literal( }; let ty = ty.display(sema.db, display_target); - let mut s = format!("```rust\n{ty}\n```\n___\n\n"); + let mut s = salsa::attach(sema.db, || format!("```rust\n{ty}\n```\n___\n\n")); match value { Ok(value) => { let backtick_len = value.chars().filter(|c| *c == '`').count(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 424890fe370c4..d7d3171664945 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -738,44 +738,46 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = hint_iterator(sema, famous_defs, ty); - match iter_item_type { - Some((iter_trait, item, ty)) => { - const LABEL_START: &str = "impl "; - const LABEL_ITERATOR: &str = "Iterator"; - const LABEL_MIDDLE: &str = "<"; - const LABEL_ITEM: &str = "Item"; - const LABEL_MIDDLE2: &str = " = "; - const LABEL_END: &str = ">"; - - max_length = max_length.map(|len| { - len.saturating_sub( - LABEL_START.len() - + LABEL_ITERATOR.len() - + LABEL_MIDDLE.len() - + LABEL_MIDDLE2.len() - + LABEL_END.len(), - ) - }); - - label_builder.write_str(LABEL_START)?; - label_builder.start_location_link(ModuleDef::from(iter_trait).into()); - label_builder.write_str(LABEL_ITERATOR)?; - label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE)?; - label_builder.start_location_link(ModuleDef::from(item).into()); - label_builder.write_str(LABEL_ITEM)?; - label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE2)?; - rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?; - label_builder.write_str(LABEL_END)?; - Ok(()) + salsa::attach(sema.db, || { + let iter_item_type = hint_iterator(sema, famous_defs, ty); + match iter_item_type { + Some((iter_trait, item, ty)) => { + const LABEL_START: &str = "impl "; + const LABEL_ITERATOR: &str = "Iterator"; + const LABEL_MIDDLE: &str = "<"; + const LABEL_ITEM: &str = "Item"; + const LABEL_MIDDLE2: &str = " = "; + const LABEL_END: &str = ">"; + + max_length = max_length.map(|len| { + len.saturating_sub( + LABEL_START.len() + + LABEL_ITERATOR.len() + + LABEL_MIDDLE.len() + + LABEL_MIDDLE2.len() + + LABEL_END.len(), + ) + }); + + label_builder.write_str(LABEL_START)?; + label_builder.start_location_link(ModuleDef::from(iter_trait).into()); + label_builder.write_str(LABEL_ITERATOR)?; + label_builder.end_location_link(); + label_builder.write_str(LABEL_MIDDLE)?; + label_builder.start_location_link(ModuleDef::from(item).into()); + label_builder.write_str(LABEL_ITEM)?; + label_builder.end_location_link(); + label_builder.write_str(LABEL_MIDDLE2)?; + rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?; + label_builder.write_str(LABEL_END)?; + Ok(()) + } + None => ty + .display_truncated(sema.db, max_length, display_target) + .with_closure_style(config.closure_style) + .write_to(label_builder), } - None => ty - .display_truncated(sema.db, max_length, display_target) - .with_closure_style(config.closure_style) - .write_to(label_builder), - } + }) } let mut label_builder = InlayHintLabelBuilder { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 39554d777795c..e39a5f8889a2c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -10,7 +10,7 @@ use hir::{ Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, }; -use ide_db::famous_defs::FamousDefs; +use ide_db::{base_db::salsa, famous_defs::FamousDefs}; use ide_db::text_edit::TextEditBuilder; use syntax::ast::{self, AstNode, prec::ExprPrecedence}; @@ -201,13 +201,15 @@ pub(super) fn hints( text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, linked_location: None, tooltip: Some(config.lazy_tooltip(|| { - InlayTooltip::Markdown(format!( - "`{}` → `{}`\n\n**{}**\n\n{}", - source.display(sema.db, display_target), - target.display(sema.db, display_target), - coercion, - detailed_tooltip - )) + salsa::attach(sema.db, || { + InlayTooltip::Markdown(format!( + "`{}` → `{}`\n\n**{}**\n\n{}", + source.display(sema.db, display_target), + target.display(sema.db, display_target), + coercion, + detailed_tooltip + )) + }) })), }; if postfix { &mut post } else { &mut pre }.label.append_part(label); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 5349ebb7c82a5..1c66473bf9874 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -67,7 +67,7 @@ use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, - salsa::Cancelled, + salsa::{self, Cancelled}, }, prime_caches, symbol_index, }; @@ -461,7 +461,9 @@ impl Analysis { hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) + salsa::attach(db, || { + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) + }) }) } @@ -536,7 +538,7 @@ impl Analysis { config: &HoverConfig, range: FileRange, ) -> Cancellable>> { - self.with_db(|db| hover::hover(db, range, config)) + self.with_db(|db| salsa::attach(db, || hover::hover(db, range, config))) } /// Returns moniker of symbol at position. @@ -544,7 +546,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| moniker::moniker(db, position)) + self.with_db(|db| salsa::attach(db, || moniker::moniker(db, position))) } /// Returns URL(s) for the documentation of the symbol under the cursor. @@ -640,7 +642,7 @@ impl Analysis { /// Returns the set of possible targets to run for the current file. pub fn runnables(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| runnables::runnables(db, file_id)) + self.with_db(|db| salsa::attach(db, || runnables::runnables(db, file_id))) } /// Returns the set of tests for the given file position. diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 61b4de0e0e396..1b6a4b726e831 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -10,6 +10,7 @@ use hir::{ }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, + base_db::salsa, defs::Definition, documentation::{Documentation, HasDocs}, }; @@ -377,8 +378,9 @@ where ) .map(|mut res| { res.docs = self.docs(db); - res.description = - Some(self.display(db, self.krate(db).to_display_target(db)).to_string()); + res.description = salsa::attach(db, || { + Some(self.display(db, self.krate(db).to_display_target(db)).to_string()) + }); res.container_name = self.container_name(db); res }), @@ -485,8 +487,9 @@ impl TryToNav for hir::Field { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { res.docs = self.docs(db); - res.description = - Some(self.display(db, krate.to_display_target(db)).to_string()); + res.description = salsa::attach(db, || { + Some(self.display(db, krate.to_display_target(db)).to_string()) + }); res }, ) diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 77c84292cf3e9..6d33ddcde0479 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -10,7 +10,7 @@ use hir::{ use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, - base_db::RootQueryDb, + base_db::{RootQueryDb, salsa}, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, @@ -414,11 +414,13 @@ pub(crate) fn runnable_impl( let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); - let params = if ty_args.peek().is_some() { - format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) - } else { - String::new() - }; + let params = salsa::attach(sema.db, || { + if ty_args.peek().is_some() { + format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) + } else { + String::new() + } + }); let mut test_id = format!("{}{params}", adt_name.display(sema.db, edition)); test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); @@ -521,7 +523,9 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut ty_args = ty.generic_parameters(db, display_target).peekable(); format_to!(path, "{}", name.display(db, edition)); if ty_args.peek().is_some() { - format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); + salsa::attach(db, || { + format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); + }); } format_to!(path, "::{}", def_name.display(db, edition)); path.retain(|c| c != ' '); diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 86d02b4098bd2..f45d096ac1904 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -11,6 +11,7 @@ use hir::{ use ide_db::{ FilePosition, FxIndexMap, active_parameter::{callable_for_arg_list, generic_def_for_node}, + base_db::salsa, documentation::{Documentation, HasDocs}, }; use itertools::Itertools; @@ -266,12 +267,12 @@ fn signature_help_for_call( // In that case, fall back to render definitions of the respective parameters. // This is overly conservative: we do not substitute known type vars // (see FIXME in tests::impl_trait) and falling back on any unknowns. - match (p.ty().contains_unknown(), fn_params.as_deref()) { + salsa::attach(db, || match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => { format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target)) } _ => format_to!(buf, "{}", p.ty().display(db, display_target)), - } + }); res.push_call_param(&buf); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 694ac22e1993b..14b6529c61f8c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -5,7 +5,7 @@ use arrayvec::ArrayVec; use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, - base_db::{RootQueryDb, SourceDatabase, VfsPath}, + base_db::{RootQueryDb, SourceDatabase, VfsPath, salsa}, defs::{Definition, IdentClass}, documentation::Documentation, famous_defs::FamousDefs, @@ -227,30 +227,32 @@ impl StaticIndex<'_> { let id = if let Some(it) = self.def_map.get(&def) { *it } else { - let it = self.tokens.insert(TokenStaticData { - documentation: documentation_for_definition(&sema, def, scope_node), - hover: Some(hover_for_definition( - &sema, - file_id, - def, - None, - scope_node, - None, - false, - &hover_config, - edition, - display_target, - )), - definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { - FileRange { file_id: it.file_id, range: it.focus_or_full_range() } - }), - references: vec![], - moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), - display_name: def - .name(self.db) - .map(|name| name.display(self.db, edition).to_string()), - signature: Some(def.label(self.db, display_target)), - kind: def_to_kind(self.db, def), + let it = salsa::attach(sema.db, || { + self.tokens.insert(TokenStaticData { + documentation: documentation_for_definition(&sema, def, scope_node), + hover: Some(hover_for_definition( + &sema, + file_id, + def, + None, + scope_node, + None, + false, + &hover_config, + edition, + display_target, + )), + definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map( + |it| FileRange { file_id: it.file_id, range: it.focus_or_full_range() }, + ), + references: vec![], + moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), + display_name: def + .name(self.db) + .map(|name| name.display(self.db, edition).to_string()), + signature: Some(def.label(self.db, display_target)), + kind: def_to_kind(self.db, def), + }) }); self.def_map.insert(def, it); it diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 63701a4d15e94..1eb0fd4fd8b76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -3,6 +3,7 @@ use std::fmt; use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ RootDatabase, + base_db::salsa, defs::Definition, helpers::{get_definition, pick_best_token}, }; @@ -141,7 +142,9 @@ pub(crate) fn view_memory_layout( if let Ok(child_layout) = child_ty.layout(db) { nodes.push(MemoryLayoutNode { item_name: field.name(db), - typename: child_ty.display(db, display_target).to_string(), + typename: salsa::attach(db, || { + child_ty.display(db, display_target).to_string() + }), size: child_layout.size(), alignment: child_layout.align(), offset: match *field { @@ -175,7 +178,7 @@ pub(crate) fn view_memory_layout( } } - ty.layout(db) + salsa::attach(db, || ty.layout(db)) .map(|layout| { let item_name = match def { // def is a datatype @@ -188,7 +191,7 @@ pub(crate) fn view_memory_layout( def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()), }; - let typename = ty.display(db, display_target).to_string(); + let typename = salsa::attach(db, || ty.display(db, display_target).to_string()); let mut nodes = vec![MemoryLayoutNode { item_name, From faf0dd978dc16873e22e62acfe8c54fb81fafe36 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 10 Aug 2025 00:53:11 +0000 Subject: [PATCH 0082/1900] Deduplicate layout_of_adt --- .../rust-analyzer/crates/hir-ty/src/db.rs | 17 +++--------- .../crates/hir-ty/src/display.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 9 +++---- .../crates/hir-ty/src/layout/adt.rs | 27 +++---------------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 9 ++++--- 6 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 7a5daac69922f..161ad31e579b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -94,11 +94,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::layout::layout_of_adt_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_cycle_result)] - fn layout_of_adt( - &self, + fn layout_of_adt<'db>( + &'db self, def: AdtId, - subst: Substitution, - env: Arc, + args: crate::next_solver::GenericArgs<'db>, + trait_env: Arc, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] @@ -300,15 +300,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::layout::layout_of_adt_ns_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)] - fn layout_of_adt_ns<'db>( - &'db self, - def: AdtId, - args: crate::next_solver::GenericArgs<'db>, - trait_env: Arc, - ) -> Result, LayoutError>; - #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] fn layout_of_ty_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 9f87c37834d77..81bc48eecfc8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -944,7 +944,7 @@ fn render_const_scalar_inner( SolverDefId::AdtId(def) => def, _ => unreachable!(), }; - let Ok(layout) = f.db.layout_of_adt_ns(def, args, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_adt(def, args, trait_env.clone()) else { return f.write_str(""); }; match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index fb7b5e1c83e4c..0a8ec949b7c0a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -31,11 +31,8 @@ use crate::{ }, }; -pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result}; -pub use self::{ - adt::{layout_of_adt_ns_query, layout_of_adt_query}, - target::target_data_layout_query, -}; +pub(crate) use self::adt::layout_of_adt_cycle_result; +pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; @@ -197,7 +194,7 @@ pub fn layout_of_ty_ns_query<'db>( } _ => {} } - return db.layout_of_adt_ns(def.inner().id, args, trait_env); + return db.layout_of_adt(def.inner().id, args, trait_env); } TyKind::Bool => Layout::scalar( dl, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 2fa01b6b41abc..fefa3f2617439 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -13,23 +13,13 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Substitution, TraitEnvironment, + TraitEnvironment, db::HirDatabase, layout::{Layout, LayoutCx, LayoutError, field_ty}, - next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver}, + next_solver::GenericArgs, }; -pub fn layout_of_adt_query( - db: &dyn HirDatabase, - def: AdtId, - subst: Substitution, - trait_env: Arc, -) -> Result, LayoutError> { - let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); - db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env) -} - -pub fn layout_of_adt_ns_query<'db>( +pub fn layout_of_adt_query<'db>( db: &'db dyn HirDatabase, def: AdtId, args: GenericArgs<'db>, @@ -105,16 +95,7 @@ pub fn layout_of_adt_ns_query<'db>( Ok(Arc::new(result)) } -pub(crate) fn layout_of_adt_cycle_result( - _: &dyn HirDatabase, - _: AdtId, - _: Substitution, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - -pub(crate) fn layout_of_adt_ns_cycle_result<'db>( +pub(crate) fn layout_of_adt_cycle_result<'db>( _: &'db dyn HirDatabase, _def: AdtId, _args: GenericArgs<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4dde019b50063..9accb33368980 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1818,7 +1818,7 @@ impl Adt { pub fn layout(self, db: &dyn HirDatabase) -> Result { let env = db.trait_environment(self.into()); let interner = DbInterner::new_with(db, Some(env.krate), env.block); - db.layout_of_adt_ns( + db.layout_of_adt( self.into(), TyBuilder::adt(db, self.into()) .fill_with_defaults(db, || TyKind::Error.intern(Interner)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 97886844a9f9e..6c0aa19f57482 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -10,15 +10,17 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::{ - Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, ImportPathConfig, ModuleDef, Name, + Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ImportPathConfig, + ModuleDef, Name, db::{DefDatabase, ExpandDatabase, HirDatabase}, + next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ SyntheticSyntax, expr_store::BodySourceMap, hir::{ExprId, PatId}, }; -use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; +use hir_ty::{Interner, TyExt, TypeFlags}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RootDatabase, @@ -361,6 +363,7 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { + let interner = DbInterner::new_with(db, Some(a.krate(db).base()), None); let generic_params = db.generic_params(a.into()); if generic_params.iter_type_or_consts().next().is_some() || generic_params.iter_lt().next().is_some() @@ -371,7 +374,7 @@ impl flags::AnalysisStats { all += 1; let Err(e) = db.layout_of_adt( hir_def::AdtId::from(a), - Substitution::empty(Interner), + GenericArgs::new_from_iter(interner, []), db.trait_environment(a.into()), ) else { continue; From 1b03009e7c34db88899228fb7a47a655e4b77a3c Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 10 Aug 2025 18:00:40 +0000 Subject: [PATCH 0083/1900] Convert some of mir/eval to next-solver types --- .../crates/hir-ty/src/consteval/tests.rs | 2 +- .../crates/hir-ty/src/display.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 41 ++- .../rust-analyzer/crates/hir-ty/src/mir.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 282 ++++++++++++------ .../crates/hir-ty/src/mir/eval/shim.rs | 25 +- .../crates/hir-ty/src/mir/eval/tests.rs | 2 +- .../crates/hir-ty/src/next_solver/consts.rs | 12 +- .../crates/hir-ty/src/next_solver/mapping.rs | 7 +- 9 files changed, 258 insertions(+), 132 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 22b152fe034a6..299b73a7d6cc4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -76,7 +76,7 @@ fn check_str(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: &str) { #[track_caller] fn check_answer( #[rust_analyzer::rust_fixture] ra_fixture: &str, - check: impl FnOnce(&[u8], &MemoryMap), + check: impl FnOnce(&[u8], &MemoryMap<'_>), ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 81bc48eecfc8b..5adbea75a67dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -744,20 +744,20 @@ impl HirDisplay for Const { fn render_const_scalar( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: &Ty, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); let ty = normalize(f.db, trait_env.clone(), ty.clone()); let ty = ty.to_nextsolver(interner); - render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) + render_const_scalar_inner(f, b, memory_map, ty, trait_env) } fn render_const_scalar_ns( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: crate::next_solver::Ty<'_>, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); @@ -767,16 +767,15 @@ fn render_const_scalar_ns( trait_env.env.to_nextsolver(interner), ty, ); - render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) + render_const_scalar_inner(f, b, memory_map, ty, trait_env) } fn render_const_scalar_inner( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: crate::next_solver::Ty<'_>, trait_env: Arc, - interner: DbInterner<'_>, ) -> Result<(), HirDisplayError> { use rustc_type_ir::TyKind; match ty.kind() { @@ -875,7 +874,7 @@ fn render_const_scalar_inner( let Ok(t) = memory_map.vtable_ty(ty_id) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -883,7 +882,7 @@ fn render_const_scalar_inner( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar_ns(f, bytes, memory_map, t.to_nextsolver(interner)) + render_const_scalar_ns(f, bytes, memory_map, t) } TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { @@ -1052,7 +1051,7 @@ fn render_variant_after_name( layout: &Layout, args: GenericArgs<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ) -> Result<(), HirDisplayError> { let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); match data.shape { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 81894830b5b47..8ce0aeb5532ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -212,20 +212,20 @@ pub(crate) type ProgramClause = chalk_ir::ProgramClause; /// the necessary bits of memory of the const eval session to keep the constant /// meaningful. #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub enum MemoryMap { +pub enum MemoryMap<'db> { #[default] Empty, Simple(Box<[u8]>), - Complex(Box), + Complex(Box>), } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct ComplexMemoryMap { +pub struct ComplexMemoryMap<'db> { memory: IndexMap, FxBuildHasher>, - vtable: VTableMap, + vtable: VTableMap<'db>, } -impl ComplexMemoryMap { +impl ComplexMemoryMap<'_> { fn insert(&mut self, addr: usize, val: Box<[u8]>) { match self.memory.entry(addr) { Entry::Occupied(mut e) => { @@ -240,8 +240,8 @@ impl ComplexMemoryMap { } } -impl MemoryMap { - pub fn vtable_ty(&self, id: usize) -> Result<&Ty, MirEvalError> { +impl<'db> MemoryMap<'db> { + pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError> { match self { MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)), MemoryMap::Complex(cm) => cm.vtable.ty(id), @@ -291,10 +291,11 @@ impl MemoryMap { } } +// FIXME(next-solver): /// A concrete constant value #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { - Bytes(Box<[u8]>, MemoryMap), + Bytes(Box<[u8]>, MemoryMap<'static>), // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants UnevaluatedConst(GeneralConstId, Substitution), @@ -315,6 +316,30 @@ impl Hash for ConstScalar { } } +/// A concrete constant value +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConstScalarNs<'db> { + Bytes(Box<[u8]>, MemoryMap<'db>), + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + UnevaluatedConst(GeneralConstId, Substitution), + /// Case of an unknown value that rustc might know but we don't + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177 + // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 + Unknown, +} + +impl Hash for ConstScalarNs<'_> { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + if let ConstScalarNs::Bytes(b, _) = self { + b.hash(state) + } + } +} + /// Return an index of a parameter in the generic type parameter list by it's id. pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { generics::generics(db, id.parent).type_or_const_param_idx(id) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 482b420279c90..8c48a16537a2e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -107,7 +107,7 @@ pub enum OperandKind { } impl Operand { - fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap, ty: Ty) -> Self { + fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'static>, ty: Ty) -> Self { Operand { kind: OperandKind::Constant(intern_const_scalar( ConstScalar::Bytes(data, memory_map), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index dfb8ae704b996..d0ae92961efc0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -25,21 +25,26 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; use crate::{ - AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, - Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, Interner, + MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, consteval::{ConstEvalError, intern_const_scalar, try_const_usize}, + consteval_nextsolver, db::{HirDatabase, InternedClosure}, display::{ClosureStyle, DisplayTarget, HirDisplay}, infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, - mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_const}, + next_solver::{ + Ctor, DbInterner, SolverDefId, + mapping::{ChalkToNextSolver, convert_args_for_result, convert_ty_for_result}, + }, static_lifetime, traits::FnTrait, utils::{ClosureSubst, detect_variant_from_bytes}, @@ -78,31 +83,31 @@ macro_rules! not_supported { } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct VTableMap { - ty_to_id: FxHashMap, - id_to_ty: Vec, +pub struct VTableMap<'db> { + ty_to_id: FxHashMap, usize>, + id_to_ty: Vec>, } -impl VTableMap { +impl<'db> VTableMap<'db> { const OFFSET: usize = 1000; // We should add some offset to ids to make 0 (null) an invalid id. - fn id(&mut self, ty: Ty) -> usize { + fn id(&mut self, ty: crate::next_solver::Ty<'db>) -> usize { if let Some(it) = self.ty_to_id.get(&ty) { return *it; } let id = self.id_to_ty.len() + VTableMap::OFFSET; - self.id_to_ty.push(ty.clone()); + self.id_to_ty.push(ty); self.ty_to_id.insert(ty, id); id } - pub(crate) fn ty(&self, id: usize) -> Result<&Ty> { + pub(crate) fn ty(&self, id: usize) -> Result> { id.checked_sub(VTableMap::OFFSET) - .and_then(|id| self.id_to_ty.get(id)) + .and_then(|id| self.id_to_ty.get(id).copied()) .ok_or(MirEvalError::InvalidVTableId(id)) } - fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> { + fn ty_of_bytes(&self, bytes: &[u8]) -> Result> { let id = from_bytes!(usize, bytes); self.ty(id) } @@ -170,12 +175,12 @@ pub struct Evaluator<'a> { /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the /// time of use. - vtable_map: VTableMap, + vtable_map: VTableMap<'a>, thread_local_storage: TlsData, random_state: oorandom::Rand64, stdout: Vec, stderr: Vec, - layout_cache: RefCell>>, + layout_cache: RefCell, Arc>>, projected_ty_cache: RefCell>, not_special_fn_cache: RefCell>, mir_or_dyn_index_cache: RefCell>, @@ -224,7 +229,7 @@ impl Interval { Self { addr, size } } - fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { memory.read_memory(self.addr, self.size) } @@ -242,7 +247,7 @@ impl Interval { } impl IntervalAndTy { - fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { memory.read_memory(self.interval.addr, self.interval.size) } @@ -269,7 +274,7 @@ impl From for IntervalOrOwned { } impl IntervalOrOwned { - fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, IntervalOrOwned::Borrowed(b) => b.get(memory)?, @@ -608,7 +613,13 @@ pub fn interpret_mir( memory_map.vtable.shrink_to_fit(); MemoryMap::Complex(Box::new(memory_map)) }; - Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)) + // SAFETY: will never use this without a db + Ok(intern_const_scalar( + ConstScalar::Bytes(bytes, unsafe { + std::mem::transmute::, MemoryMap<'static>>(memory_map) + }), + ty, + )) })(); Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } @@ -618,7 +629,7 @@ const EXECUTION_LIMIT: usize = 100_000; #[cfg(not(test))] const EXECUTION_LIMIT: usize = 10_000_000; -impl Evaluator<'_> { +impl<'db> Evaluator<'db> { pub fn new( db: &dyn HirDatabase, owner: DefWithBodyId, @@ -719,6 +730,7 @@ impl Evaluator<'_> { p: &Place, locals: &'a Locals, ) -> Result<(Address, Ty, Option)> { + let interner = DbInterner::new_with(self.db, None, None); let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); let mut metadata: Option = None; // locals are always sized @@ -791,19 +803,19 @@ impl Evaluator<'_> { addr = addr.offset(ty_size * (from as usize)); } &ProjectionElem::ClosureField(f) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let offset = layout.fields.offset(f).bytes_usize(); addr = addr.offset(offset); metadata = None; } ProjectionElem::Field(Either::Right(f)) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let offset = layout.fields.offset(f.index as usize).bytes_usize(); addr = addr.offset(offset); metadata = None; // tuple field is always sized FIXME: This is wrong, the tail can be unsized } ProjectionElem::Field(Either::Left(f)) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let variant_layout = match &layout.variants { Variants::Single { .. } | Variants::Empty => &layout, Variants::Multiple { variants, .. } => { @@ -835,20 +847,28 @@ impl Evaluator<'_> { Ok((addr, ty, metadata)) } - fn layout(&self, ty: &Ty) -> Result> { - if let Some(x) = self.layout_cache.borrow().get(ty) { + fn layout(&self, ty: crate::next_solver::Ty<'db>) -> Result> { + if let Some(x) = self.layout_cache.borrow().get(&ty) { return Ok(x.clone()); } + let interner = DbInterner::new_with(self.db, None, None); let r = self .db - .layout_of_ty(ty.clone(), self.trait_env.clone()) - .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?; - self.layout_cache.borrow_mut().insert(ty.clone(), r.clone()); + .layout_of_ty_ns(ty, self.trait_env.clone()) + .map_err(|e| MirEvalError::LayoutError(e, convert_ty_for_result(interner, ty)))?; + self.layout_cache.borrow_mut().insert(ty, r.clone()); Ok(r) } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { - self.layout(&TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) + let interner = DbInterner::new_with(self.db, None, None); + self.layout(crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Adt( + crate::next_solver::AdtDef::new(adt, interner), + subst.to_nextsolver(interner), + ), + )) } fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result { @@ -952,7 +972,7 @@ impl Evaluator<'_> { )? } TyKind::FnDef(def, generic_args) => self.exec_fn_def( - *def, + CallableDefId::from_chalk(self.db, *def), generic_args, destination_interval, &args, @@ -1113,6 +1133,7 @@ impl Evaluator<'_> { } fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { + let interner = DbInterner::new_with(self.db, None, None); use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1436,7 +1457,7 @@ impl Evaluator<'_> { Owned(r) } AggregateKind::Tuple(ty) => { - let layout = self.layout(ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1467,7 +1488,7 @@ impl Evaluator<'_> { )?) } AggregateKind::Closure(ty) => { - let layout = self.layout(ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1484,6 +1505,8 @@ impl Evaluator<'_> { if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = ¤t_ty.kind(Interner) { + let interner = DbInterner::new_with(self.db, None, None); + let current_ty = current_ty.to_nextsolver(interner); let id = self.vtable_map.id(current_ty); let ptr_size = self.ptr_size(); Owned(id.to_le_bytes()[0..ptr_size].to_vec()) @@ -1623,7 +1646,8 @@ impl Evaluator<'_> { } fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result { - let layout = self.layout(&ty)?; + let interner = DbInterner::new_with(self.db, None, None); + let layout = self.layout(ty.to_nextsolver(interner))?; let &TyKind::Adt(chalk_ir::AdtId(AdtId::EnumId(e)), _) = ty.kind(Interner) else { return Ok(0); }; @@ -1732,6 +1756,8 @@ impl Evaluator<'_> { } }, TyKind::Dyn(_) => { + let interner = DbInterner::new_with(self.db, None, None); + let current_ty = current_ty.to_nextsolver(interner); let vtable = self.vtable_map.id(current_ty); let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; @@ -1777,6 +1803,7 @@ impl Evaluator<'_> { subst: Substitution, locals: &Locals, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { + let interner = DbInterner::new_with(self.db, None, None); let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner && let VariantId::EnumVariantId(it) = it @@ -1786,7 +1813,11 @@ impl Evaluator<'_> { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout let i = self.const_eval_discriminant(it)?; - return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); + return Ok(( + 16, + self.layout(crate::next_solver::Ty::new_tup(interner, &[]))?, + Some((0, 16, i)), + )); } let layout = self.layout_adt(adt, subst)?; Ok(match &layout.variants { @@ -1885,6 +1916,7 @@ impl Evaluator<'_> { #[allow(clippy::double_parens)] fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { + let interner = DbInterner::new_with(self.db, None, None); let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner) else { not_supported!("evaluating non concrete constant"); @@ -1945,7 +1977,7 @@ impl Evaluator<'_> { MemoryMap::Complex(cm) => cm.vtable.ty_of_bytes(bytes), }, addr, - ty, + ty.to_nextsolver(interner), locals, )?; Ok(Interval::new(addr, size)) @@ -2048,7 +2080,8 @@ impl Evaluator<'_> { } fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { - if let Some(layout) = self.layout_cache.borrow().get(ty) { + let interner = DbInterner::new_with(self.db, None, None); + if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) { return Ok(layout .is_sized() .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))); @@ -2061,7 +2094,7 @@ impl Evaluator<'_> { // infinite sized type errors) we use a dummy size return Ok(Some((16, 16))); } - let layout = self.layout(ty); + let layout = self.layout(ty.to_nextsolver(interner)); if self.assert_placeholder_ty_is_unused && matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { @@ -2129,15 +2162,16 @@ impl Evaluator<'_> { bytes: &[u8], ty: &Ty, locals: &Locals, - ) -> Result { - fn rec( - this: &Evaluator<'_>, + ) -> Result> { + fn rec<'db>( + this: &Evaluator<'db>, bytes: &[u8], ty: &Ty, locals: &Locals, - mm: &mut ComplexMemoryMap, + mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, ) -> Result<()> { + let interner = DbInterner::new_with(this.db, None, None); if stack_depth_limit.checked_sub(1).is_none() { return Err(MirEvalError::StackOverflow); } @@ -2158,13 +2192,14 @@ impl Evaluator<'_> { let element_size = match t.kind(Interner) { TyKind::Str => 1, TyKind::Slice(t) => { - check_inner = Some(t); + check_inner = Some(t.clone()); this.size_of_sized(t, locals, "slice inner type")? } TyKind::Dyn(_) => { let t = this.vtable_map.ty_of_bytes(meta)?; - check_inner = Some(t); - this.size_of_sized(t, locals, "dyn concrete type")? + let t = convert_ty_for_result(interner, t); + check_inner = Some(t.clone()); + this.size_of_sized(&t, locals, "dyn concrete type")? } _ => return Ok(()), }; @@ -2176,7 +2211,7 @@ impl Evaluator<'_> { let addr = Address::from_bytes(addr)?; let b = this.read_memory(addr, size)?; mm.insert(addr.to_usize(), b.into()); - if let Some(ty) = check_inner { + if let Some(ty) = &check_inner { for i in 0..count { let offset = element_size * i; rec( @@ -2211,11 +2246,11 @@ impl Evaluator<'_> { } } TyKind::Tuple(_, subst) => { - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; for (id, ty) in subst.iter(Interner).enumerate() { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let size = this.layout(ty)?.size.bytes_usize(); + let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2229,7 +2264,7 @@ impl Evaluator<'_> { TyKind::Adt(adt, subst) => match adt.0 { AdtId::StructId(s) => { let data = s.fields(this.db); - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; let field_types = this.db.field_types(s.into()); for (f, _) in data.fields().iter() { let offset = layout @@ -2237,7 +2272,7 @@ impl Evaluator<'_> { .offset(u32::from(f.into_raw()) as usize) .bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); - let size = this.layout(ty)?.size.bytes_usize(); + let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2249,7 +2284,7 @@ impl Evaluator<'_> { } } AdtId::EnumId(e) => { - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; if let Some((v, l)) = detect_variant_from_bytes( &layout, this.db, @@ -2263,7 +2298,8 @@ impl Evaluator<'_> { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); - let size = this.layout(ty)?.size.bytes_usize(); + let size = + this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2290,20 +2326,26 @@ impl Evaluator<'_> { Ok(mm) } - fn patch_addresses<'vtable>( + fn patch_addresses( &mut self, patch_map: &FxHashMap, - ty_of_bytes: impl Fn(&[u8]) -> Result<&'vtable Ty> + Copy, + ty_of_bytes: impl Fn(&[u8]) -> Result> + Copy, addr: Address, - ty: &Ty, + ty: crate::next_solver::Ty<'db>, locals: &Locals, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); // FIXME: support indirect references let layout = self.layout(ty)?; - let my_size = self.size_of_sized(ty, locals, "value to patch address")?; - match ty.kind(Interner) { - TyKind::Ref(_, _, t) => { - let size = self.size_align_of(t, locals)?; + let my_size = self.size_of_sized( + &convert_ty_for_result(interner, ty), + locals, + "value to patch address", + )?; + use rustc_type_ir::TyKind; + match ty.kind() { + TyKind::Ref(_, t, _) => { + let size = self.size_align_of(&convert_ty_for_result(interner, t), locals)?; match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); @@ -2319,27 +2361,27 @@ impl Evaluator<'_> { } } } - TyKind::Function(_) => { - let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); + TyKind::FnPtr(_, _) => { + let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?; let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, subst) => match id.0 { - AdtId::StructId(s) => { - for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { + TyKind::Adt(id, args) => match id.def_id() { + SolverDefId::AdtId(AdtId::StructId(s)) => { + for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.clone().substitute(Interner, subst); + let ty = ty.instantiate(interner, args); self.patch_addresses( patch_map, ty_of_bytes, addr.offset(offset), - &ty, + ty, locals, )?; } } - AdtId::UnionId(_) => (), - AdtId::EnumId(e) => { + SolverDefId::AdtId(AdtId::UnionId(_)) => (), + SolverDefId::AdtId(AdtId::EnumId(e)) => { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, @@ -2347,33 +2389,37 @@ impl Evaluator<'_> { self.read_memory(addr, layout.size.bytes_usize())?, e, ) { - for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { + for (i, (_, ty)) in self.db.field_types_ns(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.clone().substitute(Interner, subst); + let ty = ty.instantiate(interner, args); self.patch_addresses( patch_map, ty_of_bytes, addr.offset(offset), - &ty, + ty, locals, )?; } } } + _ => unreachable!(), }, - TyKind::Tuple(_, subst) => { - for (id, ty) in subst.iter(Interner).enumerate() { - let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + TyKind::Tuple(tys) => { + for (id, ty) in tys.iter().enumerate() { let offset = layout.fields.offset(id).bytes_usize(); self.patch_addresses(patch_map, ty_of_bytes, addr.offset(offset), ty, locals)?; } } TyKind::Array(inner, len) => { - let len = match try_const_usize(self.db, len) { + let len = match consteval_nextsolver::try_const_usize(self.db, len) { Some(it) => it as usize, None => not_supported!("non evaluatable array len in patching addresses"), }; - let size = self.size_of_sized(inner, locals, "inner of array")?; + let size = self.size_of_sized( + &convert_ty_for_result(interner, inner), + locals, + "inner of array", + )?; for i in 0..len { self.patch_addresses( patch_map, @@ -2384,11 +2430,13 @@ impl Evaluator<'_> { )?; } } - TyKind::AssociatedType(_, _) - | TyKind::Scalar(_) + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) | TyKind::Slice(_) - | TyKind::Raw(_, _) - | TyKind::OpaqueType(_, _) + | TyKind::RawPtr(_, _) | TyKind::FnDef(_, _) | TyKind::Str | TyKind::Never @@ -2396,12 +2444,16 @@ impl Evaluator<'_> { | TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) | TyKind::Foreign(_) - | TyKind::Error + | TyKind::Error(_) | TyKind::Placeholder(_) - | TyKind::Dyn(_) - | TyKind::Alias(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => (), + | TyKind::Dynamic(_, _, _) + | TyKind::Alias(_, _) + | TyKind::Bound(_, _) + | TyKind::Infer(_) + | TyKind::Pat(_, _) + | TyKind::Param(_) + | TyKind::UnsafeBinder(_) + | TyKind::CoroutineClosure(_, _) => (), } Ok(()) } @@ -2416,13 +2468,41 @@ impl Evaluator<'_> { span: MirSpan, ) -> Result> { let id = from_bytes!(usize, bytes.get(self)?); - let next_ty = self.vtable_map.ty(id)?.clone(); - match next_ty.kind(Interner) { + let next_ty = self.vtable_map.ty(id)?; + let interner = DbInterner::new_with(self.db, None, None); + use rustc_type_ir::TyKind; + match next_ty.kind() { TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, args, locals, target_bb, span) + let def = match def { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), + SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), + _ => unreachable!(), + }; + self.exec_fn_def( + def, + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + target_bb, + span, + ) } - TyKind::Closure(id, subst) => { - self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span) + TyKind::Closure(id, generic_args) => { + let id = match id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + self.exec_closure( + id.into(), + bytes.slice(0..0), + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + span, + ) } _ => Err(MirEvalError::InternalError("function pointer to non function".into())), } @@ -2469,7 +2549,7 @@ impl Evaluator<'_> { fn exec_fn_def( &mut self, - def: FnDefId, + def: CallableDefId, generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], @@ -2477,7 +2557,6 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { - let def: CallableDefId = from_chalk(self.db, def); let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { @@ -2574,6 +2653,7 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { + let interner = DbInterner::new_with(self.db, None, None); if self.detect_and_exec_special_function( def, args, @@ -2600,6 +2680,7 @@ impl Evaluator<'_> { .vtable_map .ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?; let mut args_for_target = args.to_vec(); + let ty = convert_ty_for_result(interner, ty); args_for_target[0] = IntervalAndTy { interval: args_for_target[0].interval.slice(0..self.ptr_size()), ty: ty.clone(), @@ -2672,6 +2753,7 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { + let interner = DbInterner::new_with(self.db, None, None); let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; @@ -2683,15 +2765,21 @@ impl Evaluator<'_> { let id = from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]); func_data = func_data.slice(0..self.ptr_size()); - func_ty = self.vtable_map.ty(id)?.clone(); + func_ty = convert_ty_for_result(interner, self.vtable_map.ty(id)?); } let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; } match &func_ty.kind(Interner) { - TyKind::FnDef(def, subst) => { - self.exec_fn_def(*def, subst, destination, &args[1..], locals, target_bb, span) - } + TyKind::FnDef(def, subst) => self.exec_fn_def( + CallableDefId::from_chalk(self.db, *def), + subst, + destination, + &args[1..], + locals, + target_bb, + span, + ), TyKind::Function(_) => { self.exec_fn_pointer(func_data, destination, &args[1..], locals, target_bb, span) } @@ -2714,7 +2802,7 @@ impl Evaluator<'_> { Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())), ) .intern(Interner); - let layout = self.layout(&ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -2901,6 +2989,7 @@ pub fn render_const_using_debug_impl( owner: DefWithBodyId, c: &Const, ) -> Result { + let interner = DbInterner::new_with(db, None, None); let mut evaluator = Evaluator::new(db, owner, false, None)?; let locals = &Locals { ptr: ArenaMap::new(), @@ -2933,7 +3022,8 @@ pub fn render_const_using_debug_impl( CallableDefId::FunctionId(debug_fmt_fn).to_chalk(db), Substitution::from1(Interner, c.data(Interner).ty.clone()), ) - .intern(Interner)); + .intern(Interner) + .to_nextsolver(interner)); evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; // a3 = ::core::fmt::Arguments::new_v1(a1, a2) // FIXME: similarly, we should call function here, not directly working with memory. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index bb4c963a8ae15..e27d334d2a991 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -23,6 +23,10 @@ use crate::{ LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, pad16, }, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_ty_for_result}, + }, }; mod simd; @@ -171,6 +175,7 @@ impl Evaluator<'_> { destination: Interval, span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); match self_ty.kind(Interner) { TyKind::Function(_) => { let [arg] = args else { @@ -188,7 +193,7 @@ impl Evaluator<'_> { let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into()); let infer = self.db.infer(closure_owner); let (captures, _) = infer.closure_info(id); - let layout = self.layout(&self_ty)?; + let layout = self.layout(self_ty.to_nextsolver(interner))?; let ty_iter = captures.iter().map(|c| c.ty(subst)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } @@ -197,7 +202,7 @@ impl Evaluator<'_> { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let layout = self.layout(&self_ty)?; + let layout = self.layout(self_ty.to_nextsolver(interner))?; let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone()); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } @@ -226,8 +231,9 @@ impl Evaluator<'_> { destination: Interval, span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); for (i, ty) in ty_iter.enumerate() { - let size = self.layout(&ty)?.size.bytes_usize(); + let size = self.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; let arg = IntervalAndTy { interval: Interval { addr: tmp, size: self.ptr_size() }, @@ -592,6 +598,7 @@ impl Evaluator<'_> { span: MirSpan, needs_override: bool, ) -> Result { + let interner = DbInterner::new_with(self.db, None, None); if let Some(name) = name.strip_prefix("atomic_") { return self .exec_atomic_intrinsic(name, args, generic_args, destination, locals, span) @@ -769,7 +776,7 @@ impl Evaluator<'_> { "align_of generic arg is not provided".into(), )); }; - let align = self.layout(ty)?.align.abi.bytes(); + let align = self.layout(ty.to_nextsolver(interner))?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { @@ -1025,7 +1032,7 @@ impl Evaluator<'_> { let is_overflow = u128overflow || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255); let is_overflow = vec![u8::from(is_overflow)]; - let layout = self.layout(&result_ty)?; + let layout = self.layout(result_ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1249,7 +1256,7 @@ impl Evaluator<'_> { "const_eval_select arg[0] is not a tuple".into(), )); }; - let layout = self.layout(&tuple.ty)?; + let layout = self.layout(tuple.ty.to_nextsolver(interner))?; for (i, field) in fields.iter(Interner).enumerate() { let field = field.assert_ty_ref(Interner).clone(); let offset = layout.fields.offset(i).bytes_usize(); @@ -1408,6 +1415,7 @@ impl Evaluator<'_> { metadata: Interval, locals: &Locals, ) -> Result<(usize, usize)> { + let interner = DbInterner::new_with(self.db, None, None); Ok(match ty.kind(Interner) { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), TyKind::Slice(inner) => { @@ -1416,7 +1424,7 @@ impl Evaluator<'_> { (size * len, align) } TyKind::Dyn(_) => self.size_align_of_sized( - self.vtable_map.ty_of_bytes(metadata.get(self)?)?, + &convert_ty_for_result(interner, self.vtable_map.ty_of_bytes(metadata.get(self)?)?), locals, "dyn concrete type", )?, @@ -1463,6 +1471,7 @@ impl Evaluator<'_> { locals: &Locals, _span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); // We are a single threaded runtime with no UB checking and no optimization, so // we can implement atomic intrinsics as normal functions. @@ -1560,7 +1569,7 @@ impl Evaluator<'_> { Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), ) .intern(Interner); - let layout = self.layout(&result_ty)?; + let layout = self.layout(result_ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index eb5af58f2ea18..5a56d99fbaa29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -37,7 +37,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; - let (result, output) = interpret_mir(db, body, false, None)?; + let (result, output) = salsa::attach(db, || interpret_mir(db, body, false, None))?; result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 5698ff290f748..ce581cfad4b26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -103,7 +103,7 @@ pub struct ValueConst<'db> { } impl<'db> ValueConst<'db> { - pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self { + pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self { let value = Valtree::new(bytes); ValueConst { ty, value } } @@ -141,9 +141,9 @@ impl<'db> rustc_type_ir::TypeFoldable> for ValueConst<'db> { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap); +pub struct ConstBytes<'db>(pub Box<[u8]>, pub MemoryMap<'db>); -impl Hash for ConstBytes { +impl Hash for ConstBytes<'_> { fn hash(&self, state: &mut H) { self.0.hash(state) } @@ -152,11 +152,11 @@ impl Hash for ConstBytes { #[salsa::interned(constructor = new_, debug)] pub struct Valtree<'db> { #[returns(ref)] - bytes_: ConstBytes, + bytes_: ConstBytes<'db>, } impl<'db> Valtree<'db> { - pub fn new(bytes: ConstBytes) -> Self { + pub fn new(bytes: ConstBytes<'db>) -> Self { salsa::with_attached_database(|db| unsafe { // SAFETY: ¯\_(ツ)_/¯ std::mem::transmute(Valtree::new_(db, bytes)) @@ -164,7 +164,7 @@ impl<'db> Valtree<'db> { .unwrap() } - pub fn inner(&self) -> &ConstBytes { + pub fn inner(&self) -> &ConstBytes<'db> { salsa::with_attached_database(|db| { let inner = self.bytes_(db); // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index b50fccb832625..5fefb04a5e768 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -21,7 +21,7 @@ use rustc_type_ir::{ use salsa::plumbing::AsId; use crate::{ - ConcreteConst, ConstScalar, ImplTraitId, Interner, + ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, InternedTypeOrConstParamId, @@ -1328,7 +1328,10 @@ pub fn convert_const_for_result<'db>( rustc_type_ir::ConstKind::Value(value_const) => { let bytes = value_const.value.inner(); let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()), + // SAFETY: we will never actually use this without a database + interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { + std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) + }), }); return chalk_ir::ConstData { ty: convert_ty_for_result(interner, value_const.ty), From 73a5134722d362299bdecbd8418b4728f918a23e Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 02:34:53 +0000 Subject: [PATCH 0084/1900] Change direct_super_traits to use generic_predicates_for_param_ns --- .../rust-analyzer/crates/hir-ty/src/utils.rs | 43 +++++++++++++------ src/tools/rust-analyzer/crates/hir/src/lib.rs | 16 ++++--- .../rust-analyzer/crates/ide/src/hover.rs | 12 +++--- src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 209ec7926e825..092d4e3a8d9e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -4,10 +4,7 @@ use std::{cell::LazyCell, iter}; use base_db::Crate; -use chalk_ir::{ - DebruijnIndex, - fold::{FallibleTypeFolder, Shift}, -}; +use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder}; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, db::DefDatabase, @@ -20,6 +17,7 @@ use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; use stdx::never; @@ -31,6 +29,11 @@ use crate::{ db::HirDatabase, layout::{Layout, TagEncoding}, mir::pad16, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_args_for_result}, + }, + to_chalk_trait_id, }; pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator + '_ { @@ -191,25 +194,37 @@ fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut( } fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) { + let interner = DbInterner::new_with(db, None, None); let generic_params = db.generic_params(trait_ref.hir_trait_id().into()); let trait_self = match generic_params.trait_self_param() { Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p }, None => return, }; - db.generic_predicates_for_param(trait_self.parent, trait_self, None) + let trait_ref_args: crate::next_solver::GenericArgs<'_> = + trait_ref.substitution.to_nextsolver(interner); + db.generic_predicates_for_param_ns(trait_self.parent, trait_self, None) .iter() .filter_map(|pred| { - pred.as_ref().filter_map(|pred| match pred.skip_binders() { - // FIXME: how to correctly handle higher-ranked bounds here? - WhereClause::Implemented(tr) => Some( - tr.clone() - .shifted_out_to(Interner, DebruijnIndex::ONE) - .expect("FIXME unexpected higher-ranked trait bound"), - ), + let pred = pred.kind(); + // FIXME: how to correctly handle higher-ranked bounds here? + let pred = pred.no_bound_vars().expect("FIXME unexpected higher-ranked trait bound"); + match pred { + rustc_type_ir::ClauseKind::Trait(t) => { + let t = + rustc_type_ir::EarlyBinder::bind(t).instantiate(interner, trait_ref_args); + let trait_id = match t.def_id() { + crate::next_solver::SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + + let substitution = + convert_args_for_result(interner, t.trait_ref.args.as_slice()); + let tr = chalk_ir::TraitRef { trait_id, substitution }; + Some(tr) + } _ => None, - }) + } }) - .map(|pred| pred.substitute(Interner, &trait_ref.substitution)) .for_each(cb); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9accb33368980..18c3ea05614a8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -86,7 +86,8 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, mapping::ChalkToNextSolver, + ClauseKind, DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, + mapping::ChalkToNextSolver, }, primitive::UintTy, traits::FnTrait, @@ -114,6 +115,7 @@ pub use crate::{ VisibleTraits, }, }; +use rustc_type_ir::inherent::IntoKind; // Be careful with these re-exports. // @@ -4245,11 +4247,15 @@ impl TypeParam { /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - db.generic_predicates_for_param(self.id.parent(), self.id.into(), None) + db.generic_predicates_for_param_ns(self.id.parent(), self.id.into(), None) .iter() - .filter_map(|pred| match &pred.skip_binders().skip_binders() { - hir_ty::WhereClause::Implemented(trait_ref) => { - Some(Trait::from(trait_ref.hir_trait_id())) + .filter_map(|pred| match &pred.kind().skip_binder() { + ClauseKind::Trait(trait_ref) => { + let trait_ = match trait_ref.def_id() { + SolverDefId::TraitId(t) => t, + _ => unreachable!(), + }; + Some(Trait::from(trait_)) } _ => None, }) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index a48fe43e80803..fc45dc3faf40f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -581,11 +581,13 @@ fn goto_type_action_for_def( }); } - if let Ok(generic_def) = GenericDef::try_from(def) { - generic_def.type_or_const_params(db).into_iter().for_each(|it| { - walk_and_push_ty(db, &it.ty(db), &mut push_new_def); - }); - } + salsa::attach(db, || { + if let Ok(generic_def) = GenericDef::try_from(def) { + generic_def.type_or_const_params(db).into_iter().for_each(|it| { + walk_and_push_ty(db, &it.ty(db), &mut push_new_def); + }); + } + }); let ty = match def { Definition::Local(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 1c66473bf9874..2afdf18b83685 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -674,7 +674,9 @@ impl Analysis { position: FilePosition, ) -> Cancellable>> { self.with_db(|db| { - highlight_related::highlight_related(&Semantics::new(db), config, position) + salsa::attach(db, || { + highlight_related::highlight_related(&Semantics::new(db), config, position) + }) }) } From 418f419d60240e7ed24953cab9089027fa666be4 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 04:12:43 +0000 Subject: [PATCH 0085/1900] Cleanup assoc_type_shorthand_candidates --- .../rust-analyzer/crates/hir/src/semantics.rs | 21 ++++++++++--------- .../ide-completion/src/completions/expr.rs | 3 +-- .../ide-completion/src/completions/type.rs | 3 +-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b43165fd8ad70..6af0c2c3c56fb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2288,18 +2288,19 @@ impl<'db> SemanticsScope<'db> { /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). - pub fn assoc_type_shorthand_candidates( + pub fn assoc_type_shorthand_candidates( &self, resolution: &PathResolution, - mut cb: impl FnMut(&Name, TypeAlias) -> Option, - ) -> Option { - let def = self.resolver.generic_def()?; - hir_ty::associated_type_shorthand_candidates( - self.db, - def, - resolution.in_type_ns()?, - |name, id| cb(name, id.into()), - ) + mut cb: impl FnMut(TypeAlias), + ) { + let (Some(def), Some(resolution)) = (self.resolver.generic_def(), resolution.in_type_ns()) + else { + return; + }; + hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| { + cb(id.into()); + None::<()> + }); } pub fn generic_def(&self) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a84927f6e2c0f..1972f166134a4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -140,9 +140,8 @@ pub(crate) fn complete_expr_path( Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. - ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + ctx.scope.assoc_type_shorthand_candidates(resolution, |alias| { acc.add_type_alias(ctx, alias); - None::<()> }); match resolution { hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index fc27cbd65a1ee..3112462cda4e8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -77,9 +77,8 @@ pub(crate) fn complete_type_path( Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. - ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + ctx.scope.assoc_type_shorthand_candidates(resolution, |alias| { acc.add_type_alias(ctx, alias); - None::<()> }); match resolution { From 064f1c7c8321547e114d576f07ecdf0b1bee97e1 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 05:19:44 +0000 Subject: [PATCH 0086/1900] Switch associated_type_shorthand_candidates to lower_nextsolver --- .../crates/hir-ty/src/dyn_compatibility.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 9 - .../crates/hir-ty/src/lower_nextsolver.rs | 133 +++++++++++++- .../hir-ty/src/lower_nextsolver/path.rs | 163 ++++-------------- .../hir-ty/src/next_solver/generic_arg.rs | 19 +- .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 17 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 6 +- 9 files changed, 197 insertions(+), 159 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 8bd555f5c08f3..48c0c81b47815 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -564,7 +564,7 @@ fn receiver_is_dispatchable<'db>( // U: Trait let trait_def_id = SolverDefId::TraitId(trait_); let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { - if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) } + if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) } }); let trait_predicate = crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); @@ -611,7 +611,7 @@ fn receiver_for_self_ty<'db>( interner, SolverDefId::FunctionId(func), |name, index, kind, _| { - if index == 0 { self_ty.into() } else { mk_param(index, name, kind) } + if index == 0 { self_ty.into() } else { mk_param(interner, index, name, kind) } }, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 8ce0aeb5532ad..2f8eb627462b7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -118,8 +118,9 @@ pub use infer::{ pub use interner::Interner; pub use lower::{ ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext, - ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*, + ValueTyDefId, diagnostics::*, }; +pub use lower_nextsolver::associated_type_shorthand_candidates; pub use mapping::{ ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 065d2ea084070..098c62ba97a29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -804,15 +804,6 @@ pub(crate) fn callable_item_signature_query(db: &dyn HirDatabase, def: CallableD } } -pub fn associated_type_shorthand_candidates( - db: &dyn HirDatabase, - def: GenericDefId, - res: TypeNs, - mut cb: impl FnMut(&Name, TypeAliasId) -> Option, -) -> Option { - named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id)) -} - fn named_associated_type_shorthand_candidates( db: &dyn HirDatabase, // If the type parameter is defined in an impl and we're in a method, there diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 24bda43ca634f..15c675be58199 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -12,14 +12,14 @@ pub(crate) mod path; use std::{ cell::OnceCell, iter, mem, - ops::{self, Not as _}, + ops::{self, Deref, Not as _}, }; use base_db::Crate; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, expr_store::{ ExpressionStore, @@ -49,6 +49,7 @@ use rustc_type_ir::{ inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, }; use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; @@ -1607,3 +1608,131 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>( Some((t.skip_binder(), assoc_type)) }) } + +pub fn associated_type_shorthand_candidates( + db: &dyn HirDatabase, + def: GenericDefId, + res: TypeNs, + mut cb: impl FnMut(&Name, TypeAliasId) -> bool, +) -> Option { + let interner = DbInterner::new_with(db, None, None); + named_associated_type_shorthand_candidates(interner, def, res, None, |name, _, id| { + cb(name, id).then_some(id) + }) +} + +#[tracing::instrument(skip(interner, check_alias))] +fn named_associated_type_shorthand_candidates<'db, R>( + interner: DbInterner<'db>, + // If the type parameter is defined in an impl and we're in a method, there + // might be additional where clauses to consider + def: GenericDefId, + res: TypeNs, + assoc_name: Option, + mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option, +) -> Option { + let db = interner.db; + let mut search = |t: TraitRef<'db>| -> Option { + let trait_id = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_id: TraitId| { + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id + && let Some(ty) = check_alias(name, t, alias) + { + return Some(ty); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; + while let Some(trait_def_id) = stack.pop() { + if let Some(alias) = check_trait(trait_def_id) { + return Some(alias); + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_def_id), + PredicateFilter::SelfTrait, + |pred| pred == GenericDefId::TraitId(trait_def_id), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let trait_id = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + _ => continue, + }; + let trait_id = match trait_id { + SolverDefId::TraitId(trait_id) => trait_id, + _ => continue, + }; + stack.push(trait_id); + } + tracing::debug!(?stack); + } + + None + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait_ns(impl_id)?; + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + // see FIXME in `search`. + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = crate::next_solver::GenericArgs::identity_for_item( + interner, + trait_id.into(), + ); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = + db.generic_predicates_for_param_ns(def, param_id.into(), assoc_name.clone()); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .and_then(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + } + _ => None, + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index df67b2c59b512..e3efb38306408 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use either::Either; use hir_def::{ - AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, + AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, builtin_type::BuiltinType, expr_store::{ ExpressionStore, HygieneId, @@ -17,6 +17,7 @@ use hir_def::{ signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; +use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -33,7 +34,10 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::PathDiagnosticCallbackData, - lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by}, + lower_nextsolver::{ + LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by, + named_associated_type_shorthand_candidates, + }, next_solver::{ AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, Region, SolverDefId, TraitRef, Ty, @@ -501,137 +505,40 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let Some(res) = res else { return Ty::new_error(self.ctx.interner, ErrorGuaranteed); }; - let segment = self.current_or_prev_segment; - let assoc_name = segment.name; let db = self.ctx.db; let def = self.ctx.def; - let mut search = |t: TraitRef<'db>| { - let trait_id = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let mut checked_traits = FxHashSet::default(); - let mut check_trait = |trait_id: TraitId| { - let name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_id, ?name); - if !checked_traits.insert(trait_id) { - return None; - } - let data = trait_id.trait_items(db); - - tracing::debug!(?data.items); - for (name, assoc_id) in &data.items { - if let &AssocItemId::TypeAliasId(alias) = assoc_id { - if name != assoc_name { - continue; - } - - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`t.substitution`). - let substs = self.substs_from_path_segment(alias.into(), false, None, true); - - let substs = crate::next_solver::GenericArgs::new_from_iter( - interner, - t.args.iter().chain(substs.iter().skip(t.args.len())), - ); - - return Some(Ty::new_alias( - interner, - AliasTyKind::Projection, - AliasTy::new(interner, alias.into(), substs), - )); - } - } - None - }; - let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; - while let Some(trait_def_id) = stack.pop() { - if let Some(alias) = check_trait(trait_def_id) { - return alias; - } - for pred in generic_predicates_filtered_by( - db, - GenericDefId::TraitId(trait_def_id), - PredicateFilter::SelfTrait, - |pred| pred == GenericDefId::TraitId(trait_def_id), - ) - .0 - .deref() - { - tracing::debug!(?pred); - let trait_id = match pred.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), - _ => continue, - }; - let trait_id = match trait_id { - SolverDefId::TraitId(trait_id) => trait_id, - _ => continue, - }; - stack.push(trait_id); - } - tracing::debug!(?stack); + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let mut check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { + if name != assoc_name { + return None; } - Ty::new_error(interner, ErrorGuaranteed) + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`t.substitution`). + let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true); + + let substs = crate::next_solver::GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, associated_ty.into(), substs), + )) }; - - match res { - TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait_ns(impl_id); - let Some(trait_ref) = trait_ref else { - return Ty::new_error(interner, ErrorGuaranteed); - }; - - // we're _in_ the impl -- the binders get added back later. Correct, - // but it would be nice to make this more explicit - search(trait_ref.skip_binder()) - } - TypeNs::GenericParam(param_id) => { - // Handle `Self::Type` referring to own associated type in trait definitions - // This *must* be done first to avoid cycles with - // `generic_predicates_for_param`, but not sure that it's sufficient, - // see FIXME in `search`. - if let GenericDefId::TraitId(trait_id) = param_id.parent() { - let trait_name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_name); - let trait_generics = generics(db, trait_id.into()); - tracing::debug!(?trait_generics); - if trait_generics[param_id.local_id()].is_trait_self() { - let args = crate::next_solver::GenericArgs::identity_for_item( - interner, - trait_id.into(), - ); - let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); - tracing::debug!(?args, ?trait_ref); - return search(trait_ref); - } - } - - let predicates = db.generic_predicates_for_param_ns( - def, - param_id.into(), - Some(segment.name.clone()), - ); - predicates - .iter() - .find_map(|pred| match (*pred).kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), - _ => None, - }) - .map(|trait_predicate| { - let trait_ref = trait_predicate.trait_ref; - assert!( - !trait_ref.has_escaping_bound_vars(), - "FIXME unexpected higher-ranked trait bound" - ); - search(trait_ref) - }) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) - } - _ => Ty::new_error(interner, ErrorGuaranteed), - } + named_associated_type_shorthand_candidates( + interner, + def, + res, + Some(assoc_name.clone()), + check_alias, + ) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) } fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 85a79923a7295..046b4303c3261 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -263,7 +263,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< interner: DbInterner<'db>, def_id: as rustc_type_ir::Interner>::DefId, ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind)) + Self::for_item(interner, def_id, |name, index, kind, _| { + mk_param(interner, index, name, kind) + }) } fn extend_with_error( @@ -383,16 +385,19 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< } } -pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> { +pub fn mk_param<'db>( + interner: DbInterner<'db>, + index: u32, + name: &Symbol, + kind: GenericParamDefKind, +) -> GenericArg<'db> { let name = name.clone(); match kind { GenericParamDefKind::Lifetime => { - Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into() - } - GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(), - GenericParamDefKind::Const => { - Const::new_param(DbInterner::conjure(), ParamConst { index }).into() + Region::new_early_param(interner, EarlyParamRegion { index }).into() } + GenericParamDefKind::Type => Ty::new_param(interner, index, name).into(), + GenericParamDefKind::Const => Const::new_param(interner, ParamConst { index }).into(), } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6af0c2c3c56fb..fa239a28f7bcd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2299,7 +2299,7 @@ impl<'db> SemanticsScope<'db> { }; hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| { cb(id.into()); - None::<()> + false }); } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 126392af4619a..e3070c5f74ce2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -14,6 +14,7 @@ use crate::{ db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; +use base_db::salsa; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, @@ -1593,12 +1594,14 @@ fn resolve_hir_path_( Some(unresolved) => resolver .generic_def() .and_then(|def| { - hir_ty::associated_type_shorthand_candidates( - db, - def, - res.in_type_ns()?, - |name, id| (name == unresolved.name).then_some(id), - ) + salsa::attach(db, || { + hir_ty::associated_type_shorthand_candidates( + db, + def, + res.in_type_ns()?, + |name, _| name == unresolved.name, + ) + }) }) .map(TypeAlias::from) .map(Into::into) @@ -1746,7 +1749,7 @@ fn resolve_hir_path_qualifier( db, def, res.in_type_ns()?, - |name, id| (name == unresolved.name).then_some(id), + |name, _| name == unresolved.name, ) }) .map(TypeAlias::from) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 2afdf18b83685..e491c9214b437 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -528,7 +528,9 @@ impl Analysis { let search_scope = AssertUnwindSafe(search_scope); self.with_db(|db| { let _ = &search_scope; - references::find_all_refs(&Semantics::new(db), position, search_scope.0) + salsa::attach(db, || { + references::find_all_refs(&Semantics::new(db), position, search_scope.0) + }) }) } @@ -574,7 +576,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) + self.with_db(|db| salsa::attach(db, || call_hierarchy::call_hierarchy(db, position))) } /// Computes incoming calls for the given file position. From d10e5d10fe08b3b19e72891b6069c8eee0754e37 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 06:07:02 +0000 Subject: [PATCH 0087/1900] Convert more of dyn_compatibility to next-solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 187 ++++-------------- .../crates/hir-ty/src/lower_nextsolver.rs | 96 ++++++++- 2 files changed, 130 insertions(+), 153 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 48c0c81b47815..54d78dea3d3b6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,11 +2,7 @@ use std::ops::ControlFlow; -use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; -use chalk_solve::rust_ir::InlineBound; +use chalk_ir::DebruijnIndex; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, @@ -21,14 +17,14 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, - WhereClause, all_super_traits, + ImplTraitId, Interner, TyKind, WhereClause, all_super_traits, db::{HirDatabase, InternedOpaqueTyId}, - from_assoc_type_id, from_chalk_trait_id, + from_chalk_trait_id, generics::trait_self_param_idx, + lower_nextsolver::associated_ty_item_bounds, next_solver::{ - Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, - infer::DbInternerInferExt, mk_param, + Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, + TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, @@ -165,7 +161,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // but we don't have good way to render such locations. // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - db.generic_predicates(trait_.into()) + db.generic_predicates_ns(trait_.into()) .iter() .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) } @@ -177,37 +173,18 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { .items .iter() .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => { - let assoc_ty_data = db.associated_ty_data(id); - Some(assoc_ty_data) - } + AssocItemId::TypeAliasId(id) => Some(associated_ty_item_bounds(db, id)), _ => None, }) - .any(|assoc_ty_data| { - assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { - let def = from_assoc_type_id(assoc_ty_data.id).into(); - match bound.skip_binders() { - InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - } + .any(|bounds| { + bounds.skip_binder().iter().any(|pred| match pred.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::Projection(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::AutoTrait(_) => false, }) }) } @@ -218,120 +195,26 @@ enum AllowSelfProjection { No, } -fn predicate_references_self( - db: &dyn HirDatabase, +fn predicate_references_self<'db>( + db: &'db dyn HirDatabase, trait_: TraitId, - predicate: &Binders>, + predicate: &Clause<'db>, allow_self_projection: AllowSelfProjection, ) -> bool { - match predicate.skip_binders().skip_binders() { - WhereClause::Implemented(trait_ref) => { - trait_ref.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) - }) - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { - proj.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) + match predicate.kind().skip_binder() { + ClauseKind::Trait(trait_pred) => trait_pred.trait_ref.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) + }), + ClauseKind::Projection(proj_pred) => { + proj_pred.projection_term.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) }) } _ => false, } } -fn contains_illegal_self_type_reference>( - db: &dyn HirDatabase, - def: GenericDefId, - trait_: TraitId, - t: &T, - outer_binder: DebruijnIndex, - allow_self_projection: AllowSelfProjection, -) -> bool { - let Some(trait_self_param_idx) = trait_self_param_idx(db, def) else { - return false; - }; - struct IllegalSelfTypeVisitor<'a> { - db: &'a dyn HirDatabase, - trait_: TraitId, - super_traits: Option>, - trait_self_param_idx: usize, - allow_self_projection: AllowSelfProjection, - } - impl TypeVisitor for IllegalSelfTypeVisitor<'_> { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - match ty.kind(Interner) { - TyKind::BoundVar(BoundVar { debruijn, index }) => { - if *debruijn == outer_binder && *index == self.trait_self_param_idx { - ControlFlow::Break(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { - AllowSelfProjection::Yes => { - let trait_ = proj.trait_(self.db); - if self.super_traits.is_none() { - self.super_traits = Some(all_super_traits(self.db, self.trait_)); - } - if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { - ControlFlow::Continue(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), - }, - _ => ty.super_visit_with(self.as_dyn(), outer_binder), - } - } - - fn visit_const( - &mut self, - constant: &chalk_ir::Const, - outer_binder: DebruijnIndex, - ) -> std::ops::ControlFlow { - constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - - let mut visitor = IllegalSelfTypeVisitor { - db, - trait_, - super_traits: None, - trait_self_param_idx, - allow_self_projection, - }; - t.visit_with(visitor.as_dyn(), outer_binder).is_break() -} - -fn contains_illegal_self_type_reference_ns< - 'db, - T: rustc_type_ir::TypeVisitable>, ->( +fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable>>( db: &'db dyn HirDatabase, trait_: TraitId, t: &T, @@ -440,13 +323,17 @@ where } let sig = db.callable_item_signature_ns(func.into()); - if sig.skip_binder().inputs().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference_ns(db, trait_, &ty, AllowSelfProjection::Yes) - }) { + if sig + .skip_binder() + .inputs() + .iter() + .skip(1) + .any(|ty| contains_illegal_self_type_reference(db, trait_, &ty, AllowSelfProjection::Yes)) + { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference_ns( + if contains_illegal_self_type_reference( db, trait_, &sig.skip_binder().output(), @@ -496,7 +383,7 @@ where continue; } - if contains_illegal_self_type_reference_ns(db, trait_, &pred, AllowSelfProjection::Yes) { + if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjection::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 15c675be58199..2777869bd48e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -62,9 +62,10 @@ use crate::{ lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, next_solver::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, - BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, - TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, + EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, + TraitPredicate, TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, + mapping::ChalkToNextSolver, }, }; @@ -1593,6 +1594,95 @@ fn fn_sig_for_enum_variant_constructor<'db>( })) } +pub(crate) fn associated_ty_item_bounds<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + let type_alias_data = db.type_alias_signature(type_alias); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + if let Some(bound) = pred + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ))) + } + } + rustc_type_ir::ClauseKind::Projection(p) => Some( + ExistentialPredicate::Projection(ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + )), + ), + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => None, + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + bounds.push(bound); + } + }); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( + interner, + SolverDefId::TraitId(trait_), + [] as [crate::next_solver::GenericArg<'_>; 0], + ))); + bounds.push(sized_clause); + bounds.shrink_to_fit(); + } + + EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) +} + pub(crate) fn associated_type_by_name_including_super_traits<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, From cd0e0957bff2fca7f46b87477b46ba1e21535494 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 20:46:19 +0000 Subject: [PATCH 0088/1900] Switch generics_require_sized_self to next solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 54d78dea3d3b6..be8e23f7ceb15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,7 +2,6 @@ use std::ops::ControlFlow; -use chalk_ir::DebruijnIndex; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, @@ -11,23 +10,20 @@ use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, - Upcast, + Upcast, elaborate, inherent::{IntoKind, SliceLike}, }; use smallvec::SmallVec; use crate::{ - ImplTraitId, Interner, TyKind, WhereClause, all_super_traits, + ImplTraitId, all_super_traits, db::{HirDatabase, InternedOpaqueTyId}, - from_chalk_trait_id, - generics::trait_self_param_idx, lower_nextsolver::associated_ty_item_bounds, next_solver::{ Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, - utils::elaborate_clause_supertraits, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -133,27 +129,23 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b return false; }; - let Some(trait_self_param_idx) = trait_self_param_idx(db, def) else { - return false; - }; - - let predicates = &*db.generic_predicates(def); - let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); - elaborate_clause_supertraits(db, predicates).any(|pred| match pred { - WhereClause::Implemented(trait_ref) => { - if from_chalk_trait_id(trait_ref.trait_id) == sized - && let TyKind::BoundVar(it) = - *trait_ref.self_type_parameter(Interner).kind(Interner) - { - // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of - // self-parameter is `1` - return it - .index_if_bound_at(DebruijnIndex::ONE) - .is_some_and(|idx| idx == trait_self_param_idx); + let interner = DbInterner::new_with(db, Some(krate), None); + let predicates = db.generic_predicates_ns(def); + elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { + match pred.kind().skip_binder() { + ClauseKind::Trait(trait_pred) => { + if SolverDefId::TraitId(sized) == trait_pred.def_id() + && let rustc_type_ir::TyKind::Param(param_ty) = + trait_pred.trait_ref.self_ty().kind() + && param_ty.index == 0 + { + true + } else { + false + } } - false + _ => false, } - _ => false, }) } From 00856fc250be4a8e9da6630f0ee23120486418e8 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 21:45:03 +0000 Subject: [PATCH 0089/1900] Remove all_super_traits in dyn_compatibility --- .../crates/hir-ty/src/dyn_compatibility.rs | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index be8e23f7ceb15..b0c61c29db0b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -16,7 +16,7 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::{ - ImplTraitId, all_super_traits, + ImplTraitId, db::{HirDatabase, InternedOpaqueTyId}, lower_nextsolver::associated_ty_item_bounds, next_solver::{ @@ -53,13 +53,22 @@ pub fn dyn_compatibility( db: &dyn HirDatabase, trait_: TraitId, ) -> Option { - for super_trait in all_super_traits(db, trait_).into_iter().skip(1).rev() { - if db.dyn_compatibility_of_trait(super_trait).is_some() { - return Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)); + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); + for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)) { + let super_trait = match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + if let Some(v) = db.dyn_compatibility_of_trait(super_trait) { + return if super_trait == trait_ { + Some(v) + } else { + Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)) + }; } } - db.dyn_compatibility_of_trait(trait_) + None } pub fn dyn_compatibility_with_callback( @@ -70,7 +79,13 @@ pub fn dyn_compatibility_with_callback( where F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, { - for super_trait in all_super_traits(db, trait_).into_iter().skip(1).rev() { + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); + for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)).skip(1) + { + let super_trait = match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; if db.dyn_compatibility_of_trait(super_trait).is_some() { cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?; } @@ -225,6 +240,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable as rustc_type_ir::Interner>::Ty, ) -> Self::Result { + let interner = DbInterner::new_with(self.db, None, None); match ty.kind() { rustc_type_ir::TyKind::Param(param) if param.index == 0 => ControlFlow::Break(()), rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()), @@ -238,7 +254,17 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable unreachable!(), }; if self.super_traits.is_none() { - self.super_traits = Some(all_super_traits(self.db, self.trait_)); + self.super_traits = Some( + elaborate::supertrait_def_ids( + interner, + SolverDefId::TraitId(self.trait_), + ) + .map(|super_trait| match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }) + .collect(), + ) } if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { ControlFlow::Continue(()) From f92ca612a8bbb71159fbfc11510b5ce393534c2e Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 22:21:48 +0000 Subject: [PATCH 0090/1900] Replace layout_of_ty with layout_of_ty_ns --- .../crates/hir-ty/src/consteval.rs | 13 ++++++-- .../crates/hir-ty/src/consteval_nextsolver.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 14 +++----- .../crates/hir-ty/src/display.rs | 15 +++++---- .../rust-analyzer/crates/hir-ty/src/layout.rs | 32 ++++--------------- .../crates/hir-ty/src/layout/adt.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 23 ++++++++----- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 9 ++++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 ++-- .../rust-analyzer/crates/ide/src/hover.rs | 26 ++++++++------- .../crates/ide/src/view_memory_layout.rs | 4 +-- 12 files changed, 75 insertions(+), 73 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index f30ec839a0096..abf97f3d0e302 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -15,8 +15,14 @@ use triomphe::Arc; use crate::{ Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, db::HirDatabase, display::DisplayTarget, generics::Generics, - infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx, + TraitEnvironment, Ty, TyBuilder, + db::HirDatabase, + display::DisplayTarget, + generics::Generics, + infer::InferenceContext, + lower::ParamLoweringMode, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, + to_placeholder_idx, }; use super::mir::{MirEvalError, MirLowerError, interpret_mir, lower_to_mir, pad16}; @@ -157,7 +163,8 @@ pub fn intern_const_ref( ty: Ty, krate: Crate, ) -> Const { - let layout = || db.layout_of_ty(ty.clone(), TraitEnvironment::empty(krate)); + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate)); let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index cdf861290ab92..00fc4e5610dce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -92,7 +92,7 @@ pub fn intern_const_ref<'a>( krate: Crate, ) -> Const<'a> { let interner = DbInterner::new_with(db, Some(krate), None); - let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate)); + let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate)); let kind = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 161ad31e579b2..9affd3b48c587 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -103,7 +103,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_cycle_result)] - fn layout_of_ty(&self, ty: Ty, env: Arc) -> Result, LayoutError>; + fn layout_of_ty<'db>( + &'db self, + ty: crate::next_solver::Ty<'db>, + env: Arc, + ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: Crate) -> Result, Arc>; @@ -300,14 +304,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] - fn layout_of_ty_ns<'db>( - &'db self, - ty: crate::next_solver::Ty<'db>, - env: Arc, - ) -> Result, LayoutError>; - #[salsa::invoke(crate::lower_nextsolver::ty_query)] #[salsa::transparent] fn ty_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5adbea75a67dd..cdf6085b65348 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -840,7 +840,7 @@ fn render_const_scalar_inner( TyKind::Slice(ty) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -874,7 +874,7 @@ fn render_const_scalar_inner( let Ok(t) = memory_map.vtable_ty(ty_id) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -905,7 +905,7 @@ fn render_const_scalar_inner( return f.write_str(""); } }); - let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -917,7 +917,7 @@ fn render_const_scalar_inner( } }, TyKind::Tuple(tys) => { - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env.clone()) else { return f.write_str(""); }; f.write_str("(")?; @@ -929,7 +929,7 @@ fn render_const_scalar_inner( f.write_str(", ")?; } let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env.clone()) else { f.write_str("")?; continue; }; @@ -1006,7 +1006,7 @@ fn render_const_scalar_inner( let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -1061,7 +1061,8 @@ fn render_variant_after_name( let ty = field_types[id] .clone() .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty.to_nextsolver(interner), trait_env.clone()) + else { return f.write_str(""); }; let size = layout.size.bytes_usize(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 0a8ec949b7c0a..e2ee8935a8855 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -151,23 +151,13 @@ fn layout_of_simd_ty<'db>( }; let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64; - let e_ly = db.layout_of_ty_ns(e_ty, env)?; + let e_ly = db.layout_of_ty(e_ty, env)?; let cx = LayoutCx::new(dl); Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) } -pub fn layout_of_ty_query( - db: &dyn HirDatabase, - ty: crate::Ty, - trait_env: Arc, -) -> Result, LayoutError> { - let krate = trait_env.krate; - let interner = DbInterner::new_with(db, Some(krate), trait_env.block); - db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env) -} - -pub fn layout_of_ty_ns_query<'db>( +pub fn layout_of_ty_query<'db>( db: &'db dyn HirDatabase, ty: Ty<'db>, trait_env: Arc, @@ -262,7 +252,7 @@ pub fn layout_of_ty_ns_query<'db>( let fields = tys .iter() - .map(|k| db.layout_of_ty_ns(k, trait_env.clone())) + .map(|k| db.layout_of_ty(k, trait_env.clone())) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); @@ -270,11 +260,11 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Array(element, count) => { let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty_ns(element, trait_env)?; + let element = db.layout_of_ty(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { - let element = db.layout_of_ty_ns(element, trait_env)?; + let element = db.layout_of_ty(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, None)? } TyKind::Str => { @@ -346,7 +336,7 @@ pub fn layout_of_ty_ns_query<'db>( let ty = convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) .instantiate(interner, args); - db.layout_of_ty_ns(ty, trait_env.clone()) + db.layout_of_ty(ty, trait_env.clone()) }) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); @@ -376,15 +366,7 @@ pub fn layout_of_ty_ns_query<'db>( Ok(Arc::new(result)) } -pub(crate) fn layout_of_ty_cycle_result( - _: &dyn HirDatabase, - _: crate::Ty, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - -pub(crate) fn layout_of_ty_ns_cycle_result<'db>( +pub(crate) fn layout_of_ty_cycle_result<'db>( _: &dyn HirDatabase, _: Ty<'db>, _: Arc, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index fefa3f2617439..9a746ca888589 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -34,7 +34,7 @@ pub fn layout_of_adt_query<'db>( let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone())) + .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &args), trait_env.clone())) .collect::, _>>() }; let (variants, repr, is_special_no_niche) = match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 93f2e123dca58..90de7e5ca633d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -11,6 +11,7 @@ use crate::{ Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, setup_tracing, test_db::TestDB, }; @@ -85,13 +86,16 @@ fn eval_goal( db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner)) } }; - db.layout_of_ty( - goal_ty, - db.trait_environment(match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - }), - ) + salsa::attach(&db, || { + let interner = DbInterner::new_with(&db, None, None); + db.layout_of_ty( + goal_ty.to_nextsolver(interner), + db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }), + ) + }) } /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait` @@ -128,7 +132,10 @@ fn eval_expr( .0; let infer = db.infer(function_id.into()); let goal_ty = infer.type_of_binding[b].clone(); - db.layout_of_ty(goal_ty, db.trait_environment(function_id.into())) + salsa::attach(&db, || { + let interner = DbInterner::new_with(&db, None, None); + db.layout_of_ty(goal_ty.to_nextsolver(interner), db.trait_environment(function_id.into())) + }) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index d0ae92961efc0..9deaa4ac5a6b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -854,7 +854,7 @@ impl<'db> Evaluator<'db> { let interner = DbInterner::new_with(self.db, None, None); let r = self .db - .layout_of_ty_ns(ty, self.trait_env.clone()) + .layout_of_ty(ty, self.trait_env.clone()) .map_err(|e| MirEvalError::LayoutError(e, convert_ty_for_result(interner, ty)))?; self.layout_cache.borrow_mut().insert(ty, r.clone()); Ok(r) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 0bb8e6fe79d65..052be11e433f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -43,6 +43,7 @@ use crate::{ Terminator, TerminatorKind, TupleFieldId, Ty, UnOp, VariantId, intern_const_scalar, return_slot, }, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, static_lifetime, traits::FnTrait, utils::ClosureSubst, @@ -1411,8 +1412,12 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { - let size = - || self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize()); + let interner = DbInterner::new_with(self.db, None, None); + let size = || { + self.db + .layout_of_ty(ty.to_nextsolver(interner), self.env.clone()) + .map(|it| it.size.bytes_usize()) + }; const USIZE_SIZE: usize = size_of::(); let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 18c3ea05614a8..e423d2c07c159 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1389,8 +1389,9 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { + let interner = DbInterner::new_with(db, None, None); db.layout_of_ty( - self.ty(db).ty, + self.ty(db).ty.to_nextsolver(interner), db.trait_environment(match hir_def::VariantId::from(self.parent) { hir_def::VariantId::EnumVariantId(id) => { GenericDefId::AdtId(id.lookup(db).parent.into()) @@ -5906,7 +5907,8 @@ impl<'db> Type<'db> { } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty.clone(), self.env.clone()) + let interner = DbInterner::new_with(db, None, None); + db.layout_of_ty(self.ty.to_nextsolver(interner), self.env.clone()) .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index fc45dc3faf40f..b0ef83e0501b3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -137,18 +137,20 @@ pub(crate) fn hover( let edition = sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = sema.first_crate(file_id)?.to_display_target(db); - let mut res = if range.is_empty() { - hover_offset( - sema, - FilePosition { file_id, offset: range.start() }, - file, - config, - edition, - display_target, - ) - } else { - hover_ranged(sema, frange, file, config, edition, display_target) - }?; + let mut res = salsa::attach(sema.db, || { + if range.is_empty() { + hover_offset( + sema, + FilePosition { file_id, offset: range.start() }, + file, + config, + edition, + display_target, + ) + } else { + hover_ranged(sema, frange, file, config, edition, display_target) + } + })?; if let HoverDocFormat::PlainText = config.format { res.info.markup = remove_markdown(res.info.markup.as_str()).into(); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 1eb0fd4fd8b76..950f3f6c64706 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -139,7 +139,7 @@ pub(crate) fn view_memory_layout( nodes[parent_idx].children_len = fields.len() as u64; for (field, child_ty) in fields.iter() { - if let Ok(child_layout) = child_ty.layout(db) { + if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { nodes.push(MemoryLayoutNode { item_name: field.name(db), typename: salsa::attach(db, || { @@ -172,7 +172,7 @@ pub(crate) fn view_memory_layout( } for (i, (_, child_ty)) in fields.iter().enumerate() { - if let Ok(child_layout) = child_ty.layout(db) { + if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { read_layout(nodes, db, child_ty, &child_layout, children_start + i, display_target); } } From 05bc1818dac760fac07c9c6d562977cd9b51dab1 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 03:13:45 +0000 Subject: [PATCH 0091/1900] Switch TraitRef in hir::TraitRef to next solver --- .../crates/hir-ty/src/display.rs | 238 ++++++++++++++++-- .../hir-ty/src/next_solver/generic_arg.rs | 23 ++ .../crates/hir-ty/src/next_solver/mapping.rs | 25 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 43 ++-- 4 files changed, 288 insertions(+), 41 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index cdf6085b65348..ae0113fcbd7f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,8 +11,8 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, ModuleId, - TraitId, + GeneralConstId, GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, + ModuleId, TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, @@ -38,7 +38,7 @@ use rustc_apfloat::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, + AliasTyKind, RegionKind, inherent::{AdtDef, IntoKind, SliceLike}, }; use smallvec::SmallVec; @@ -61,8 +61,9 @@ use crate::{ next_solver::{ BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, mapping::{ - ChalkToNextSolver, convert_args_for_result, convert_const_for_result, - convert_region_for_result, convert_ty_for_result, + ChalkToNextSolver, bound_var_to_lifetime_idx, bound_var_to_type_or_const_param_idx, + convert_args_for_result, convert_const_for_result, convert_region_for_result, + convert_ty_for_result, }, }, primitive, to_assoc_type_id, @@ -715,28 +716,56 @@ impl HirDisplay for GenericArg { } } +impl<'db> HirDisplay for crate::next_solver::GenericArg<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::GenericArgKind::Type(ty) => ty.hir_fmt(f), + rustc_type_ir::GenericArgKind::Lifetime(lt) => lt.hir_fmt(f), + rustc_type_ir::GenericArgKind::Const(c) => c.hir_fmt(f), + } + } +} + impl HirDisplay for Const { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let data = self.interned(); - match &data.value { - ConstValue::BoundVar(idx) => idx.hir_fmt(f), - ConstValue::InferenceVar(..) => write!(f, "#c#"), - ConstValue::Placeholder(idx) => { - let id = from_placeholder_idx(f.db, *idx); + let c = self.to_nextsolver(DbInterner::new_with(f.db, None, None)); + c.hir_fmt(f) + } +} + +impl<'db> HirDisplay for crate::next_solver::Const<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::ConstKind::Bound(db, bound_const) => { + write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) + } + rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), + rustc_type_ir::ConstKind::Placeholder(idx) => { + let id = bound_var_to_type_or_const_param_idx(f.db, idx.bound); let generics = generics(f.db, id.parent); let param_data = &generics[id.local_id]; write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; Ok(()) } - ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(b, m) => render_const_scalar(f, b, m, &data.ty), - ConstScalar::UnevaluatedConst(c, parameters) => { - write!(f, "{}", c.name(f.db))?; - hir_fmt_generics(f, parameters.as_slice(Interner), c.generic_def(f.db), None)?; - Ok(()) - } - ConstScalar::Unknown => f.write_char('_'), - }, + rustc_type_ir::ConstKind::Value(const_bytes) => render_const_scalar_ns( + f, + &const_bytes.value.inner().0, + &const_bytes.value.inner().1, + const_bytes.ty, + ), + rustc_type_ir::ConstKind::Unevaluated(unev) => { + let c = match unev.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + write!(f, "{}", c.name(f.db))?; + hir_fmt_generics_ns(f, unev.args.as_slice(), c.generic_def(f.db), None)?; + Ok(()) + } + rustc_type_ir::ConstKind::Error(..) => f.write_char('_'), + rustc_type_ir::ConstKind::Expr(..) => write!(f, ""), + rustc_type_ir::ConstKind::Param(_) => write!(f, ""), } } } @@ -1748,6 +1777,27 @@ fn hir_fmt_generics( Ok(()) } +fn hir_fmt_generics_ns<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + generic_def: Option, + self_: Option>, +) -> Result<(), HirDisplayError> { + if parameters.is_empty() { + return Ok(()); + } + + let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); + + if !parameters_to_write.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; + write!(f, ">")?; + } + + Ok(()) +} + fn generic_args_sans_defaults<'ga>( f: &mut HirFormatter<'_>, generic_def: Option, @@ -1803,6 +1853,87 @@ fn generic_args_sans_defaults<'ga>( } } +fn hir_fmt_generic_args<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + generic_def: Option, + self_: Option>, +) -> Result<(), HirDisplayError> { + if parameters.is_empty() { + return Ok(()); + } + + let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); + + if !parameters_to_write.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; + write!(f, ">")?; + } + + Ok(()) +} + +fn generic_args_sans_defaults_ns<'ga, 'db>( + f: &mut HirFormatter<'_>, + generic_def: Option, + parameters: &'ga [crate::next_solver::GenericArg<'db>], +) -> &'ga [crate::next_solver::GenericArg<'db>] { + let interner = DbInterner::new_with(f.db, Some(f.krate()), None); + if f.display_kind.is_source_code() || f.omit_verbose_types() { + match generic_def + .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) + .filter(|it| !it.is_empty()) + { + None => parameters, + Some(default_parameters) => { + let should_show = |arg: &crate::next_solver::GenericArg<'db>, i: usize| { + let is_err = |arg: &crate::next_solver::GenericArg<'db>| match arg.kind() { + rustc_type_ir::GenericArgKind::Lifetime(it) => { + matches!(it.kind(), RegionKind::ReError(..)) + } + rustc_type_ir::GenericArgKind::Type(it) => { + matches!(it.kind(), rustc_type_ir::TyKind::Error(..)) + } + rustc_type_ir::GenericArgKind::Const(it) => { + matches!(it.kind(), rustc_type_ir::ConstKind::Error(..),) + } + }; + // if the arg is error like, render it to inform the user + if is_err(arg) { + return true; + } + // otherwise, if the arg is equal to the param default, hide it (unless the + // default is an error which can happen for the trait Self type) + match default_parameters.get(i) { + None => true, + Some(default_parameter) => { + // !is_err(default_parameter.skip_binders()) + // && + arg != &default_parameter + .clone() + .substitute( + Interner, + &convert_args_for_result(interner, ¶meters[..i]), + ) + .to_nextsolver(interner) + } + } + }; + let mut default_from = 0; + for (i, parameter) in parameters.iter().enumerate() { + if should_show(parameter, i) { + default_from = i + 1; + } + } + ¶meters[0..default_from] + } + } + } else { + parameters + } +} + fn hir_fmt_generic_arguments( f: &mut HirFormatter<'_>, parameters: &[GenericArg], @@ -1827,6 +1958,30 @@ fn hir_fmt_generic_arguments( Ok(()) } +fn hir_fmt_generic_arguments_ns<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + self_: Option>, +) -> Result<(), HirDisplayError> { + let mut first = true; + let lifetime_offset = parameters.iter().position(|arg| arg.region().is_some()); + + let (ty_or_const, lifetimes) = match lifetime_offset { + Some(offset) => parameters.split_at(offset), + None => (parameters, &[][..]), + }; + for generic_arg in lifetimes.iter().chain(ty_or_const) { + if !mem::take(&mut first) { + write!(f, ", ")?; + } + match self_ { + self_ @ Some(_) if generic_arg.ty() == self_ => write!(f, "Self")?, + _ => generic_arg.hir_fmt(f)?, + } + } + Ok(()) +} + impl HirDisplay for CallableSig { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { let CallableSig { params_and_return: _, is_varargs, safety, abi: _ } = *self; @@ -2067,6 +2222,20 @@ impl HirDisplay for TraitRef { } } +impl<'db> HirDisplay for crate::next_solver::TraitRef<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + let trait_ = match self.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + f.start_location_link(trait_.into()); + write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; + f.end_location_link(); + let substs = self.args.as_slice(); + hir_fmt_generic_args(f, &substs[1..], None, substs[0].ty()) + } +} + impl HirDisplay for WhereClause { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { @@ -2147,6 +2316,35 @@ impl HirDisplay for LifetimeData { } } +impl<'db> HirDisplay for crate::next_solver::Region<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::RegionKind::RePlaceholder(idx) => { + let id = bound_var_to_lifetime_idx(f.db, idx.bound.var); + let generics = generics(f.db, id.parent); + let param_data = &generics[id.local_id]; + write!(f, "{}", param_data.name.display(f.db, f.edition()))?; + Ok(()) + } + rustc_type_ir::RegionKind::ReBound(db, idx) => { + write!(f, "?{}.{}", db.as_u32(), idx.var.as_u32()) + } + rustc_type_ir::RegionKind::ReVar(_) => write!(f, "_"), + rustc_type_ir::RegionKind::ReStatic => write!(f, "'static"), + rustc_type_ir::RegionKind::ReError(..) => { + if cfg!(test) { + write!(f, "'?") + } else { + write!(f, "'_") + } + } + rustc_type_ir::RegionKind::ReErased => write!(f, "'"), + rustc_type_ir::RegionKind::ReEarlyParam(_) => write!(f, ""), + rustc_type_ir::RegionKind::ReLateParam(_) => write!(f, ""), + } + } +} + impl HirDisplay for DomainGoal { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 046b4303c3261..834f4e3765ee8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -35,6 +35,29 @@ impl<'db> std::fmt::Debug for GenericArg<'db> { } } +impl<'db> GenericArg<'db> { + pub fn ty(self) -> Option> { + match self.kind() { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + } + } + + pub fn expect_ty(self) -> Ty<'db> { + match self.kind() { + GenericArgKind::Type(ty) => ty, + _ => panic!("Expected ty, got {:?}", self), + } + } + + pub fn region(self) -> Option> { + match self.kind() { + GenericArgKind::Lifetime(r) => Some(r), + _ => None, + } + } +} + impl<'db> From> for GenericArg<'db> { fn from(value: Term<'db>) -> Self { match value { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 5fefb04a5e768..cad51fd85f55a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -18,13 +18,14 @@ use rustc_type_ir::{ shift_vars, solve::Goal, }; -use salsa::plumbing::AsId; +use salsa::plumbing::FromId; +use salsa::{Id, plumbing::AsId}; use crate::{ ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ - HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, - InternedTypeOrConstParamId, + HirDatabase, InternedClosureId, InternedCoroutineId, InternedLifetimeParamId, + InternedOpaqueTyId, InternedTypeOrConstParamId, }, from_assoc_type_id, from_chalk_trait_id, mapping::ToChalk, @@ -55,6 +56,24 @@ pub fn to_placeholder_idx( } } +pub fn bound_var_to_type_or_const_param_idx( + db: &dyn HirDatabase, + var: rustc_type_ir::BoundVar, +) -> TypeOrConstParamId { + // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. + let interned_id = InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); + interned_id.loc(db) +} + +pub fn bound_var_to_lifetime_idx( + db: &dyn HirDatabase, + var: rustc_type_ir::BoundVar, +) -> LifetimeParamId { + // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. + let interned_id = InternedLifetimeParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); + interned_id.loc(db) +} + pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( interner: DbInterner<'db>, binder: rustc_type_ir::Binder, T>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index e423d2c07c159..46a0f584c0131 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -115,7 +115,7 @@ pub use crate::{ VisibleTraits, }, }; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; // Be careful with these re-exports. // @@ -4513,14 +4513,20 @@ impl Impl { } pub fn trait_(self, db: &dyn HirDatabase) -> Option { - let trait_ref = db.impl_trait(self.id)?; - let id = trait_ref.skip_binders().hir_trait_id(); + let trait_ref = db.impl_trait_ns(self.id)?; + let id = trait_ref.skip_binder().def_id; + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; Some(Trait { id }) } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { + let interner = DbInterner::new_with(db, None, None); let substs = TyBuilder::placeholder_subst(db, self.id); - let trait_ref = db.impl_trait(self.id)?.substitute(Interner, &substs); + let trait_ref = + db.impl_trait(self.id)?.substitute(Interner, &substs).to_nextsolver(interner); let resolver = self.id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } @@ -4589,7 +4595,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { env: Arc, - trait_ref: hir_ty::TraitRef, + trait_ref: hir_ty::next_solver::TraitRef<'db>, _pd: PhantomCovariantLifetime<'db>, } @@ -4597,7 +4603,7 @@ impl<'db> TraitRef<'db> { pub(crate) fn new_with_resolver( db: &'db dyn HirDatabase, resolver: &Resolver<'_>, - trait_ref: hir_ty::TraitRef, + trait_ref: hir_ty::next_solver::TraitRef<'db>, ) -> Self { let env = resolver .generic_def() @@ -4606,25 +4612,26 @@ impl<'db> TraitRef<'db> { } pub fn trait_(&self) -> Trait { - let id = self.trait_ref.hir_trait_id(); + let id = match self.trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; Trait { id } } - pub fn self_ty(&self) -> Type<'_> { - let ty = self.trait_ref.self_type_parameter(Interner); - Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() } + pub fn self_ty(&self) -> TypeNs<'_> { + let ty = self.trait_ref.self_ty(); + TypeNs { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option> { - self.trait_ref - .substitution - .as_slice(Interner) - .get(idx) - .and_then(|arg| arg.ty(Interner)) - .cloned() - .map(|ty| Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() }) + pub fn get_type_argument(&self, idx: usize) -> Option> { + self.trait_ref.args.as_slice().get(idx).and_then(|arg| arg.ty()).map(|ty| TypeNs { + env: self.env.clone(), + ty, + _pd: PhantomCovariantLifetime::new(), + }) } } From 49f166029f6956c6422b73bfa52f416cd2be12f0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 03:35:01 +0000 Subject: [PATCH 0092/1900] Use impl_trait_ns in Impl::trait_ref --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 46a0f584c0131..ff7d116dcce3e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4523,10 +4523,7 @@ impl Impl { } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { - let interner = DbInterner::new_with(db, None, None); - let substs = TyBuilder::placeholder_subst(db, self.id); - let trait_ref = - db.impl_trait(self.id)?.substitute(Interner, &substs).to_nextsolver(interner); + let trait_ref = db.impl_trait_ns(self.id)?.instantiate_identity(); let resolver = self.id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } From e5d320fd6c28b5099389e0a34a688e8d37d90f4b Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 04:23:00 +0000 Subject: [PATCH 0093/1900] Remove a bunch of stuff from chalk_db --- .../crates/hir-ty/src/chalk_db.rs | 425 +----------------- .../rust-analyzer/crates/hir-ty/src/db.rs | 29 -- .../crates/hir-ty/src/diagnostics/expr.rs | 7 +- .../crates/hir-ty/src/infer/cast.rs | 12 +- .../crates/hir-ty/src/infer/closure.rs | 26 +- .../crates/hir-ty/src/mapping.rs | 19 - src/tools/rust-analyzer/crates/hir/src/lib.rs | 75 +++- 7 files changed, 80 insertions(+), 513 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index f523a5e8bfa05..546991cf6571e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,44 +1,13 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use std::sync::Arc; +use hir_def::{CallableDefId, GenericDefId}; -use tracing::debug; - -use chalk_ir::{cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, WellKnownTrait}; - -use base_db::Crate; -use hir_def::{ - AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, - VariantId, - lang_item::LangItem, - signatures::{ImplFlags, StructFlags, TraitFlags}, -}; - -use crate::{ - AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause, - Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, - db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, - generics::generics, - lower::LifetimeElisionKind, - make_binders, - mapping::{ToChalk, TypeAliasAsValue, from_chalk}, - to_assoc_type_id, to_chalk_trait_id, -}; - -pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; -pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum; -pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum; -pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum; +use crate::{Interner, Substitution, db::HirDatabase, mapping::from_chalk}; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; pub(crate) type AdtId = chalk_ir::AdtId; pub(crate) type ImplId = chalk_ir::ImplId; -pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId; -pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; -pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; pub(crate) type Variances = chalk_ir::Variances; impl chalk_ir::UnificationDatabase for &dyn HirDatabase { @@ -54,340 +23,6 @@ impl chalk_ir::UnificationDatabase for &dyn HirDatabase { } } -pub(crate) fn associated_ty_data_query( - db: &dyn HirDatabase, - type_alias: TypeAliasId, -) -> Arc { - debug!("associated_ty_data {:?}", type_alias); - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - - // Lower bounds -- we could/should maybe move this to a separate query in `lower` - let type_alias_data = db.type_alias_signature(type_alias); - let generic_params = generics(db, type_alias.into()); - let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); - let mut ctx = crate::TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - type_alias.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(crate::lower::ParamLoweringMode::Variable); - - let trait_subst = TyBuilder::subst_for_def(db, trait_, None) - .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0) - .build(); - let pro_ty = TyBuilder::assoc_type_projection(db, type_alias, Some(trait_subst)) - .fill_with_bound_vars( - crate::DebruijnIndex::INNERMOST, - generic_params.parent_generics().map_or(0, |it| it.len()), - ) - .build(); - let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner); - - let mut bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty.clone(), false).for_each(|pred| { - if let Some(pred) = generic_predicate_to_inline_bound(db, &pred, &self_ty) { - bounds.push(pred); - } - }); - } - - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = - LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id); - let sized_bound = sized_trait.into_iter().map(|sized_trait| { - let trait_bound = - rust_ir::TraitBound { trait_id: sized_trait, args_no_self: Default::default() }; - let inline_bound = rust_ir::InlineBound::TraitBound(trait_bound); - chalk_ir::Binders::empty(Interner, inline_bound) - }); - bounds.extend(sized_bound); - bounds.shrink_to_fit(); - } - - // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed. - // (rust-analyzer#9052) - // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); - let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses: vec![] }; - let datum = AssociatedTyDatum { - trait_id: to_chalk_trait_id(trait_), - id: to_assoc_type_id(type_alias), - name: type_alias, - binders: make_binders(db, &generic_params, bound_data), - }; - Arc::new(datum) -} - -pub(crate) fn trait_datum_query( - db: &dyn HirDatabase, - krate: Crate, - trait_id: TraitId, -) -> Arc { - debug!("trait_datum {:?}", trait_id); - let trait_ = from_chalk_trait_id(trait_id); - let trait_data = db.trait_signature(trait_); - debug!("trait {:?} = {:?}", trait_id, trait_data.name); - let generic_params = generics(db, trait_.into()); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let flags = rust_ir::TraitFlags { - auto: trait_data.flags.contains(TraitFlags::AUTO), - upstream: trait_.lookup(db).container.krate() != krate, - non_enumerable: true, - coinductive: false, // only relevant for Chalk testing - // FIXME: set these flags correctly - marker: false, - fundamental: trait_data.flags.contains(TraitFlags::FUNDAMENTAL), - }; - let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); - let associated_ty_ids = - trait_.trait_items(db).associated_types().map(to_assoc_type_id).collect(); - let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; - let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); - let trait_datum = TraitDatum { - id: trait_id, - binders: make_binders(db, &generic_params, trait_datum_bound), - flags, - associated_ty_ids, - well_known, - }; - Arc::new(trait_datum) -} - -fn well_known_trait_from_lang_item(item: LangItem) -> Option { - Some(match item { - LangItem::Clone => WellKnownTrait::Clone, - LangItem::CoerceUnsized => WellKnownTrait::CoerceUnsized, - LangItem::Copy => WellKnownTrait::Copy, - LangItem::DiscriminantKind => WellKnownTrait::DiscriminantKind, - LangItem::DispatchFromDyn => WellKnownTrait::DispatchFromDyn, - LangItem::Drop => WellKnownTrait::Drop, - LangItem::Fn => WellKnownTrait::Fn, - LangItem::FnMut => WellKnownTrait::FnMut, - LangItem::FnOnce => WellKnownTrait::FnOnce, - LangItem::AsyncFn => WellKnownTrait::AsyncFn, - LangItem::AsyncFnMut => WellKnownTrait::AsyncFnMut, - LangItem::AsyncFnOnce => WellKnownTrait::AsyncFnOnce, - LangItem::Coroutine => WellKnownTrait::Coroutine, - LangItem::Sized => WellKnownTrait::Sized, - LangItem::Unpin => WellKnownTrait::Unpin, - LangItem::Unsize => WellKnownTrait::Unsize, - LangItem::Tuple => WellKnownTrait::Tuple, - LangItem::PointeeTrait => WellKnownTrait::Pointee, - LangItem::FnPtrTrait => WellKnownTrait::FnPtr, - LangItem::Future => WellKnownTrait::Future, - _ => return None, - }) -} - -pub(crate) fn adt_datum_query( - db: &dyn HirDatabase, - krate: Crate, - chalk_ir::AdtId(adt_id): AdtId, -) -> Arc { - debug!("adt_datum {:?}", adt_id); - let generic_params = generics(db, adt_id.into()); - let bound_vars_subst = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let where_clauses = convert_where_clauses(db, adt_id.into(), &bound_vars_subst); - - let (fundamental, phantom_data) = match adt_id { - hir_def::AdtId::StructId(s) => { - let flags = db.struct_signature(s).flags; - (flags.contains(StructFlags::FUNDAMENTAL), flags.contains(StructFlags::IS_PHANTOM_DATA)) - } - // FIXME set fundamental flags correctly - hir_def::AdtId::UnionId(_) => (false, false), - hir_def::AdtId::EnumId(_) => (false, false), - }; - let flags = rust_ir::AdtFlags { - upstream: adt_id.module(db).krate() != krate, - fundamental, - phantom_data, - }; - - // this slows down rust-analyzer by quite a bit unfortunately, so enabling this is currently not worth it - let _variant_id_to_fields = |id: VariantId| { - let variant_data = &id.fields(db); - let fields = if variant_data.fields().is_empty() { - vec![] - } else { - let field_types = db.field_types(id); - variant_data - .fields() - .iter() - .map(|(idx, _)| field_types[idx].clone().substitute(Interner, &bound_vars_subst)) - .filter(|it| !it.contains_unknown()) - .collect() - }; - rust_ir::AdtVariantDatum { fields } - }; - let variant_id_to_fields = |_: VariantId| rust_ir::AdtVariantDatum { fields: vec![] }; - - let (kind, variants) = match adt_id { - hir_def::AdtId::StructId(id) => { - (rust_ir::AdtKind::Struct, vec![variant_id_to_fields(id.into())]) - } - hir_def::AdtId::EnumId(id) => { - let variants = id - .enum_variants(db) - .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) - .collect(); - (rust_ir::AdtKind::Enum, variants) - } - hir_def::AdtId::UnionId(id) => { - (rust_ir::AdtKind::Union, vec![variant_id_to_fields(id.into())]) - } - }; - - let struct_datum_bound = rust_ir::AdtDatumBound { variants, where_clauses }; - let struct_datum = AdtDatum { - kind, - id: chalk_ir::AdtId(adt_id), - binders: make_binders(db, &generic_params, struct_datum_bound), - flags, - }; - Arc::new(struct_datum) -} - -pub(crate) fn impl_datum_query( - db: &dyn HirDatabase, - krate: Crate, - impl_id: ImplId, -) -> Arc { - let _p = tracing::info_span!("impl_datum_query").entered(); - debug!("impl_datum {:?}", impl_id); - let impl_: hir_def::ImplId = from_chalk(db, impl_id); - impl_def_datum(db, krate, impl_) -} - -fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) -> Arc { - let trait_ref = db - .impl_trait(impl_id) - // ImplIds for impls where the trait ref can't be resolved should never reach Chalk - .expect("invalid impl passed to Chalk") - .into_value_and_skipped_binders() - .0; - let impl_data = db.impl_signature(impl_id); - - let generic_params = generics(db, impl_id.into()); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let trait_ = trait_ref.hir_trait_id(); - let impl_type = if impl_id.lookup(db).container.krate() == krate { - rust_ir::ImplType::Local - } else { - rust_ir::ImplType::External - }; - let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); - let negative = impl_data.flags.contains(ImplFlags::NEGATIVE); - let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; - - let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; - let trait_data = trait_.trait_items(db); - let associated_ty_value_ids = impl_id - .impl_items(db) - .items - .iter() - .filter_map(|(_, item)| match item { - AssocItemId::TypeAliasId(type_alias) => Some(*type_alias), - _ => None, - }) - .filter(|&type_alias| { - // don't include associated types that don't exist in the trait - let name = &db.type_alias_signature(type_alias).name; - trait_data.associated_type_by_name(name).is_some() - }) - .map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db)) - .collect(); - debug!("impl_datum: {:?}", impl_datum_bound); - let impl_datum = ImplDatum { - binders: make_binders(db, &generic_params, impl_datum_bound), - impl_type, - polarity, - associated_ty_value_ids, - }; - Arc::new(impl_datum) -} - -pub(crate) fn associated_ty_value_query( - db: &dyn HirDatabase, - krate: Crate, - id: AssociatedTyValueId, -) -> Arc { - let type_alias: TypeAliasAsValue = from_chalk(db, id); - type_alias_associated_ty_value(db, krate, type_alias.0) -} - -fn type_alias_associated_ty_value( - db: &dyn HirDatabase, - _krate: Crate, - type_alias: TypeAliasId, -) -> Arc { - let type_alias_data = db.type_alias_signature(type_alias); - let impl_id = match type_alias.lookup(db).container { - ItemContainerId::ImplId(it) => it, - _ => panic!("assoc ty value should be in impl"), - }; - - let trait_ref = db - .impl_trait(impl_id) - .expect("assoc ty value should not exist") - .into_value_and_skipped_binders() - .0; // we don't return any assoc ty values if the impl'd trait can't be resolved - - let assoc_ty = trait_ref - .hir_trait_id() - .trait_items(db) - .associated_type_by_name(&type_alias_data.name) - .expect("assoc ty value should not exist"); // validated when building the impl data as well - let (ty, binders) = db.ty(type_alias.into()).into_value_and_skipped_binders(); - let value_bound = rust_ir::AssociatedTyValueBound { ty }; - let value = rust_ir::AssociatedTyValue { - impl_id: impl_id.to_chalk(db), - associated_ty_id: to_assoc_type_id(assoc_ty), - value: chalk_ir::Binders::new(binders, value_bound), - }; - Arc::new(value) -} - -pub(crate) fn fn_def_datum_query( - db: &dyn HirDatabase, - callable_def: CallableDefId, -) -> Arc { - let generic_def = GenericDefId::from_callable(db, callable_def); - let generic_params = generics(db, generic_def); - let (sig, binders) = db.callable_item_signature(callable_def).into_value_and_skipped_binders(); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let where_clauses = convert_where_clauses(db, generic_def, &bound_vars); - let bound = rust_ir::FnDefDatumBound { - // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway - inputs_and_output: chalk_ir::Binders::empty( - Interner, - rust_ir::FnDefInputsAndOutputDatum { - argument_types: sig.params().to_vec(), - return_type: sig.ret().clone(), - } - .shifted_in(Interner), - ), - where_clauses, - }; - let datum = FnDefDatum { - id: callable_def.to_chalk(db), - sig: chalk_ir::FnSig { - abi: sig.abi, - safety: chalk_ir::Safety::Safe, - variadic: sig.is_varargs, - }, - binders: chalk_ir::Binders::new(binders, bound), - }; - Arc::new(datum) -} - pub(crate) fn fn_def_variance_query( db: &dyn HirDatabase, callable_def: CallableDefId, @@ -431,59 +66,3 @@ pub(super) fn convert_where_clauses( .map(|pred| pred.substitute(Interner, substs)) .collect() } - -pub(super) fn generic_predicate_to_inline_bound( - db: &dyn HirDatabase, - pred: &QuantifiedWhereClause, - self_ty: &Ty, -) -> Option>> { - // An InlineBound is like a GenericPredicate, except the self type is left out. - // We don't have a special type for this, but Chalk does. - let self_ty_shifted_in = self_ty.clone().shifted_in_from(Interner, DebruijnIndex::ONE); - let (pred, binders) = pred.as_ref().into_value_and_skipped_binders(); - match pred { - WhereClause::Implemented(trait_ref) => { - if trait_ref.self_type_parameter(Interner) != self_ty_shifted_in { - // we can only convert predicates back to type bounds if they - // have the expected self type - return None; - } - let args_no_self = trait_ref.substitution.as_slice(Interner)[1..] - .iter() - .cloned() - .casted(Interner) - .collect(); - let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; - Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - let generics = generics(db, from_assoc_type_id(projection_ty.associated_ty_id).into()); - let parent_len = generics.parent_generics().map_or(0, |g| g.len_self()); - let (trait_args, assoc_args) = - projection_ty.substitution.as_slice(Interner).split_at(parent_len); - let (self_ty, args_no_self) = - trait_args.split_first().expect("projection without trait self type"); - if self_ty.assert_ty_ref(Interner) != &self_ty_shifted_in { - return None; - } - - let args_no_self = args_no_self.iter().cloned().casted(Interner).collect(); - let parameters = assoc_args.to_vec(); - - let alias_eq_bound = rust_ir::AliasEqBound { - value: ty.clone(), - trait_bound: rust_ir::TraitBound { - trait_id: to_chalk_trait_id(projection_ty.trait_(db)), - args_no_self, - }, - associated_ty_id: projection_ty.associated_ty_id, - parameters, - }; - Some(chalk_ir::Binders::new( - binders, - rust_ir::InlineBound::AliasEqBound(alias_eq_bound), - )) - } - _ => None, - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9affd3b48c587..97754f47233b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -1,8 +1,6 @@ //! The home of `HirDatabase`, which is the Salsa database containing all the //! type inference-related queries. -use std::sync; - use base_db::Crate; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, @@ -240,26 +238,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::interned] fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId; - #[salsa::invoke(chalk_db::associated_ty_data_query)] - fn associated_ty_data(&self, id: TypeAliasId) -> sync::Arc; - - #[salsa::invoke(chalk_db::trait_datum_query)] - fn trait_datum( - &self, - krate: Crate, - trait_id: chalk_db::TraitId, - ) -> sync::Arc; - - #[salsa::invoke(chalk_db::adt_datum_query)] - fn adt_datum(&self, krate: Crate, struct_id: chalk_db::AdtId) -> sync::Arc; - - #[salsa::invoke(chalk_db::impl_datum_query)] - fn impl_datum(&self, krate: Crate, impl_id: chalk_db::ImplId) - -> sync::Arc; - - #[salsa::invoke(chalk_db::fn_def_datum_query)] - fn fn_def_datum(&self, fn_def_id: CallableDefId) -> sync::Arc; - #[salsa::invoke(chalk_db::fn_def_variance_query)] fn fn_def_variance(&self, fn_def_id: CallableDefId) -> chalk_db::Variances; @@ -274,13 +252,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { )] fn variances_of(&self, def: GenericDefId) -> Option>; - #[salsa::invoke(chalk_db::associated_ty_value_query)] - fn associated_ty_value( - &self, - krate: Crate, - id: chalk_db::AssociatedTyValueId, - ) -> sync::Arc; - #[salsa::invoke(crate::traits::normalize_projection_query)] #[salsa::transparent] fn normalize_projection( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index b26bd2b8fa9c4..403ea05a4f53c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -5,7 +5,6 @@ use std::fmt; use base_db::Crate; -use chalk_solve::rust_ir::AdtKind; use either::Either; use hir_def::{ AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, @@ -300,11 +299,7 @@ impl ExprValidator { value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { - TyKind::Adt(adt, ..) - if db.adt_datum(self.owner.krate(db), *adt).kind == AdtKind::Union => - { - false - } + TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false, _ => self.is_known_valid_scrutinee(*expr, db), }, Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index f0a4167f8e250..09b983a580d97 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,12 +1,12 @@ //! Type cast logic. Basically coercion + additional casts. use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; -use hir_def::{AdtId, hir::ExprId}; +use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; use stdx::never; use crate::{ Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, from_chalk_trait_id, infer::{coerce::CoerceNever, unify::InferenceTable}, }; @@ -290,10 +290,12 @@ impl CastCheck { return Ok(()); } let src_principal = - table.db.trait_datum(table.trait_env.krate, src_principal); + table.db.trait_signature(from_chalk_trait_id(src_principal)); let dst_principal = - table.db.trait_datum(table.trait_env.krate, dst_principal); - if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + table.db.trait_signature(from_chalk_trait_id(dst_principal)); + if src_principal.flags.contains(TraitFlags::AUTO) + && dst_principal.flags.contains(TraitFlags::AUTO) + { Ok(()) } else { Err(CastError::DifferingKinds) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index d8fc20e8741cd..38ac2e1170773 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -9,7 +9,6 @@ use chalk_ir::{ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use either::Either; -use hir_def::Lookup; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, expr_store::path::Path, @@ -22,6 +21,7 @@ use hir_def::{ resolver::ValueNs, type_ref::TypeRefId, }; +use hir_def::{ItemContainerId, Lookup, TraitId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; @@ -30,16 +30,16 @@ use stdx::{format_to, never}; use syntax::utils::is_raw_identifier; use crate::{ - Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, - DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, - Substitution, Ty, TyBuilder, TyExt, WhereClause, + Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ClosureId, DynTy, DynTyExt, FnAbi, + FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, Substitution, Ty, + TyBuilder, TyExt, WhereClause, db::{HirDatabase, InternedClosure, InternedCoroutine}, error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, generics::Generics, infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - to_assoc_type_id, to_chalk_trait_id, + to_assoc_type_id, traits::FnTrait, utils::{self, elaborate_clause_supertraits}, }; @@ -321,10 +321,8 @@ impl InferenceContext<'_> { fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option { // Search for a predicate like `<$self as FnX>::Output == Ret` - let fn_traits: SmallVec<[ChalkTraitId; 3]> = - utils::fn_traits(self.db, self.owner.module(self.db).krate()) - .map(to_chalk_trait_id) - .collect(); + let fn_traits: SmallVec<[TraitId; 3]> = + utils::fn_traits(self.db, self.owner.module(self.db).krate()).collect(); let self_ty = self.result.standard_types.unknown.clone(); let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]); @@ -333,9 +331,13 @@ impl InferenceContext<'_> { if let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = bound.skip_binders() { - let assoc_data = - self.db.associated_ty_data(from_assoc_type_id(projection.associated_ty_id)); - if !fn_traits.contains(&assoc_data.trait_id) { + let trait_ = + match from_assoc_type_id(projection.associated_ty_id).lookup(self.db).container + { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + if !fn_traits.contains(&trait_) { return None; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index 9d3d2044c43e4..448fbdf673655 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -3,8 +3,6 @@ //! Chalk (in both directions); plus some helper functions for more specialized //! conversions. -use chalk_solve::rust_ir; - use hir_def::{LifetimeParamId, TraitId, TypeAliasId, TypeOrConstParamId}; use salsa::{ Id, @@ -54,23 +52,6 @@ impl ToChalk for CallableDefId { } } -pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId); - -impl ToChalk for TypeAliasAsValue { - type Chalk = chalk_db::AssociatedTyValueId; - - fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::AssociatedTyValueId { - rust_ir::AssociatedTyValueId(self.0.as_id()) - } - - fn from_chalk( - _db: &dyn HirDatabase, - assoc_ty_value_id: chalk_db::AssociatedTyValueId, - ) -> TypeAliasAsValue { - TypeAliasAsValue(TypeAliasId::from_id(assoc_ty_value_id.0)) - } -} - impl From for crate::db::InternedOpaqueTyId { fn from(id: OpaqueTyId) -> Self { FromId::from_id(id.0) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ff7d116dcce3e..f03f542e5bce3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -66,7 +66,7 @@ use hir_def::{ }, per_ns::PerNs, resolver::{HasResolver, Resolver}, - signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, + signatures::{ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields}, src::HasSource as _, visibility::visibility_from_ast, }; @@ -4945,42 +4945,79 @@ impl<'db> Type<'db> { } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { - return go(db, self.env.krate, &self.ty); + return go(db, &self.ty); - fn go(db: &dyn HirDatabase, krate: base_db::Crate, ty: &Ty) -> bool { + fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { + match adt_id { + hir_def::AdtId::StructId(s) => { + let flags = db.struct_signature(s).flags; + flags.contains(StructFlags::IS_PHANTOM_DATA) + } + hir_def::AdtId::UnionId(_) => false, + hir_def::AdtId::EnumId(_) => false, + } + } + + fn go(db: &dyn HirDatabase, ty: &Ty) -> bool { match ty.kind(Interner) { // Reference itself TyKind::Ref(_, _, _) => true, // For non-phantom_data adts we check variants/fields as well as generic parameters - TyKind::Adt(adt_id, substitution) - if !db.adt_datum(krate, *adt_id).flags.phantom_data => - { - let adt_datum = &db.adt_datum(krate, *adt_id); - let adt_datum_bound = - adt_datum.binders.clone().substitute(Interner, substitution); - adt_datum_bound - .variants + TyKind::Adt(adt_id, substitution) if !is_phantom_data(db, adt_id.0) => { + let _variant_id_to_fields = |id: VariantId| { + let variant_data = &id.fields(db); + if variant_data.fields().is_empty() { + vec![] + } else { + let field_types = db.field_types(id); + variant_data + .fields() + .iter() + .map(|(idx, _)| { + field_types[idx].clone().substitute(Interner, substitution) + }) + .filter(|it| !it.contains_unknown()) + .collect() + } + }; + let variant_id_to_fields = |_: VariantId| vec![]; + + let variants = match adt_id.0 { + hir_def::AdtId::StructId(id) => { + vec![variant_id_to_fields(id.into())] + } + hir_def::AdtId::EnumId(id) => id + .enum_variants(db) + .variants + .iter() + .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .collect(), + hir_def::AdtId::UnionId(id) => { + vec![variant_id_to_fields(id.into())] + } + }; + + variants .into_iter() - .flat_map(|variant| variant.fields.into_iter()) - .any(|ty| go(db, krate, &ty)) + .flat_map(|variant| variant.into_iter()) + .any(|ty| go(db, &ty)) || substitution .iter(Interner) .filter_map(|x| x.ty(Interner)) - .any(|ty| go(db, krate, ty)) + .any(|ty| go(db, ty)) } // And for `PhantomData`, we check `T`. TyKind::Adt(_, substitution) | TyKind::Tuple(_, substitution) | TyKind::OpaqueType(_, substitution) | TyKind::AssociatedType(_, substitution) - | TyKind::FnDef(_, substitution) => substitution - .iter(Interner) - .filter_map(|x| x.ty(Interner)) - .any(|ty| go(db, krate, ty)), + | TyKind::FnDef(_, substitution) => { + substitution.iter(Interner).filter_map(|x| x.ty(Interner)).any(|ty| go(db, ty)) + } // For `[T]` or `*T` we check `T` - TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, krate, ty), + TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, ty), // Consider everything else as not reference _ => false, From 5c893461719c3d4da1201a0080b60f1ca95c5c88 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:36:53 +0000 Subject: [PATCH 0094/1900] Add new_empty_tuple --- src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 4 ++-- src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 9deaa4ac5a6b5..c60ace85be124 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -25,7 +25,7 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; @@ -1815,7 +1815,7 @@ impl<'db> Evaluator<'db> { let i = self.const_eval_discriminant(it)?; return Ok(( 16, - self.layout(crate::next_solver::Ty::new_tup(interner, &[]))?, + self.layout(crate::next_solver::Ty::new_empty_tuple(interner))?, Some((0, 16, i)), )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 0c0fe686b77d1..5ffae981a6094 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -9,6 +9,7 @@ use rustc_type_ir::{ WithCachedTypeInfo, inherent::{ AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, + Ty as _, }, relate::Relate, solve::SizedTraitKind, @@ -107,6 +108,10 @@ impl<'db> Ty<'db> { Ty::new_infer(interner, InferTy::FreshFloatTy(n)) } + pub fn new_empty_tuple(interner: DbInterner<'db>) -> Self { + Ty::new_tup(interner, &[]) + } + /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { match self.kind() { From 0e2b63cd875e0496b3a2da237282e02333b632ee Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:38:50 +0000 Subject: [PATCH 0095/1900] Update fixme --- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2f8eb627462b7..7fdfb205721ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -292,7 +292,7 @@ impl<'db> MemoryMap<'db> { } } -// FIXME(next-solver): +// FIXME(next-solver): add a lifetime to this /// A concrete constant value #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { From 3e41e85b2761bbe21169e7dd72cb959fb63a31fa Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:41:23 +0000 Subject: [PATCH 0096/1900] Add fixme to associated_ty_item_bounds --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 2777869bd48e5..99411e4ab138c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1594,6 +1594,7 @@ fn fn_sig_for_enum_variant_constructor<'db>( })) } +// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way pub(crate) fn associated_ty_item_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, From 058a398f9fd11783aa62691bfb4b029c1a3d313c Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:50:21 +0000 Subject: [PATCH 0097/1900] Add FIXME in named_associated_type_shorthand_candidates --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 99411e4ab138c..7a2bd37bce6d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1782,6 +1782,9 @@ fn named_associated_type_shorthand_candidates<'db, R>( TypeNs::SelfType(impl_id) => { let trait_ref = db.impl_trait_ns(impl_id)?; + // FIXME(next-solver): same method in `lower` checks for impl or not + // Is that needed here? + // we're _in_ the impl -- the binders get added back later. Correct, // but it would be nice to make this more explicit search(trait_ref.skip_binder()) From 3486a2c3e735d51750e1127607eefcf0496a4d00 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:52:12 +0000 Subject: [PATCH 0098/1900] Remove fixme comment --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 7a2bd37bce6d4..ce953fdcb8298 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1793,7 +1793,6 @@ fn named_associated_type_shorthand_candidates<'db, R>( // Handle `Self::Type` referring to own associated type in trait definitions // This *must* be done first to avoid cycles with // `generic_predicates_for_param`, but not sure that it's sufficient, - // see FIXME in `search`. if let GenericDefId::TraitId(trait_id) = param_id.parent() { let trait_name = &db.trait_signature(trait_id).name; tracing::debug!(?trait_name); From b9d225b6d8dff5eaccf8bac4ab969ab1fe98ea6d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 16 Aug 2025 16:26:30 +0200 Subject: [PATCH 0099/1900] Auto-attach database in `Analysis` calls --- .../hir-ty/src/next_solver/generic_arg.rs | 2 +- .../crates/ide-assists/src/lib.rs | 9 +-- .../crates/ide-assists/src/tests.rs | 17 ++++- .../crates/ide-completion/src/lib.rs | 5 +- .../crates/ide-completion/src/tests.rs | 6 +- .../crates/ide-diagnostics/src/lib.rs | 12 ++- .../crates/ide-diagnostics/src/tests.rs | 75 ++++++++++++------- .../crates/ide/src/goto_definition.rs | 4 +- .../rust-analyzer/crates/ide/src/hover.rs | 43 +++++------ .../crates/ide/src/hover/render.rs | 5 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 57 +++++++++----- .../crates/parser/src/lexed_str.rs | 3 +- 12 files changed, 137 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 834f4e3765ee8..76186e374608e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -46,7 +46,7 @@ impl<'db> GenericArg<'db> { pub fn expect_ty(self) -> Ty<'db> { match self.kind() { GenericArgKind::Type(ty) => ty, - _ => panic!("Expected ty, got {:?}", self), + _ => panic!("Expected ty, got {self:?}"), } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 5008f97447b76..4682c04732389 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -67,7 +67,7 @@ mod tests; pub mod utils; use hir::Semantics; -use ide_db::{EditionedFileId, RootDatabase, base_db::salsa}; +use ide_db::{EditionedFileId, RootDatabase}; use syntax::{Edition, TextRange}; pub(crate) use crate::assist_context::{AssistContext, Assists}; @@ -93,11 +93,8 @@ pub fn assists( .unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT)); let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range }); let mut acc = Assists::new(&ctx, resolve); - // the handlers may invoke trait solving related things which accesses salsa structs outside queries - salsa::attach(db, || { - handlers::all().iter().for_each(|handler| { - handler(&mut acc, &ctx); - }); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); }); acc.finish() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index f4daabfe915d7..c7c322a15e541 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -1,7 +1,7 @@ mod generated; use expect_test::expect; -use hir::{Semantics, setup_tracing}; +use hir::{Semantics, db::HirDatabase, setup_tracing}; use ide_db::{ EditionedFileId, FileRange, RootDatabase, SnippetCap, assists::ExprFillDefaultMode, @@ -16,7 +16,7 @@ use test_utils::{assert_eq_text, extract_offset}; use crate::{ Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, Assists, SingleResolve, - assists, handlers::Handler, + handlers::Handler, }; pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { @@ -103,6 +103,18 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { prefer_self_ty: false, }; +fn assists( + db: &RootDatabase, + config: &AssistConfig, + resolve: AssistResolveStrategy, + range: ide_db::FileRange, +) -> Vec { + salsa::attach(db, || { + HirDatabase::zalsa_register_downcaster(db); + crate::assists(db, config, resolve, range) + }) +} + pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) { RootDatabase::with_single_file(text) } @@ -320,6 +332,7 @@ fn check_with_config( }; let mut acc = Assists::new(&ctx, resolve); salsa::attach(&db, || { + HirDatabase::zalsa_register_downcaster(&db); handler(&mut acc, &ctx); }); let mut res = acc.finish(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 1a4c97e70b4ae..a70a1138d2f42 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,7 +10,6 @@ mod snippet; #[cfg(test)] mod tests; -use base_db::salsa; use ide_db::{ FilePosition, FxHashSet, RootDatabase, imports::insert_use::{self, ImportScope}, @@ -229,7 +228,7 @@ pub fn completions( { let acc = &mut completions; - salsa::attach(db, || match analysis { + match analysis { CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) @@ -257,7 +256,7 @@ pub fn completions( ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), - }) + } } Some(completions.into()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 4b3b271ca20ef..809a26bf5de47 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -24,7 +24,7 @@ mod type_pos; mod use_tree; mod visibility; -use base_db::SourceDatabase; +use base_db::{SourceDatabase, salsa}; use expect_test::Expect; use hir::{PrefixKind, setup_tracing}; use ide_db::{ @@ -243,7 +243,7 @@ pub(crate) fn check_edit_with_config( let ra_fixture_after = trim_indent(ra_fixture_after); let (db, position) = position(ra_fixture_before); let completions: Vec = - crate::completions(&db, &config, position, None).unwrap(); + salsa::attach(&db, || crate::completions(&db, &config, position, None).unwrap()); let (completion,) = completions .iter() .filter(|it| it.lookup() == what) @@ -306,7 +306,7 @@ pub(crate) fn get_all_items( trigger_character: Option, ) -> Vec { let (db, position) = position(code); - let res = crate::completions(&db, &config, position, trigger_character) + let res = salsa::attach(&db, || crate::completions(&db, &config, position, trigger_character)) .map_or_else(Vec::default, Into::into); // validate res.iter().for_each(|it| { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index a4eb3d47d708b..a1db92641f5ee 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -92,7 +92,7 @@ use hir::{ use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _, salsa}, + base_db::{ReleaseChannel, RootQueryDb as _}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -537,12 +537,10 @@ pub fn full_diagnostics( resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec { - salsa::attach(db, || { - let mut res = syntax_diagnostics(db, config, file_id); - let sema = semantic_diagnostics(db, config, resolve, file_id); - res.extend(sema); - res - }) + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res } /// Returns whether to keep this diagnostic (or remove it). diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index c3cc5a08b56b5..1839ab1c58c1e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -6,7 +6,7 @@ use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, - base_db::SourceDatabase, + base_db::{SourceDatabase, salsa}, }; use itertools::Itertools; use stdx::trim_indent; @@ -74,14 +74,16 @@ fn check_nth_fix_with_config( let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let diagnostic = super::full_diagnostics( - &db, - &config, - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) - .pop() - .expect("no diagnostics"); + let diagnostic = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &config, + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + .pop() + .expect("no diagnostics") + }); let fix = &diagnostic .fixes .unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; @@ -127,12 +129,14 @@ pub(crate) fn check_has_fix( let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; - let fix = super::full_diagnostics( - &db, - &conf, - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) + let fix = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &conf, + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + }) .into_iter() .find(|d| { d.fixes @@ -166,12 +170,14 @@ pub(crate) fn check_has_fix( /// Checks that there's a diagnostic *without* fix at `$0`. pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture); - let diagnostic = super::full_diagnostics( - &db, - &DiagnosticsConfig::test_sample(), - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) + let diagnostic = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &DiagnosticsConfig::test_sample(), + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + }) .pop() .unwrap(); assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {diagnostic:?}"); @@ -206,7 +212,13 @@ pub(crate) fn check_diagnostics_with_config( .iter() .copied() .flat_map(|file_id| { - super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.file_id(&db)) + salsa::attach(&db, || { + super::full_diagnostics( + &db, + &config, + &AssistResolveStrategy::All, + file_id.file_id(&db), + ) .into_iter() .map(|d| { let mut annotation = String::new(); @@ -224,6 +236,7 @@ pub(crate) fn check_diagnostics_with_config( annotation.push_str(&d.message); (d.range, annotation) }) + }) }) .map(|(diagnostic, annotation)| (diagnostic.file_id, (diagnostic.range, annotation))) .into_group_map(); @@ -275,15 +288,19 @@ fn test_disabled_diagnostics() { let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); let file_id = file_id.file_id(&db); - let diagnostics = super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); + let diagnostics = salsa::attach(&db, || { + super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id) + }); assert!(diagnostics.is_empty()); - let diagnostics = super::full_diagnostics( - &db, - &DiagnosticsConfig::test_sample(), - &AssistResolveStrategy::All, - file_id, - ); + let diagnostics = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &DiagnosticsConfig::test_sample(), + &AssistResolveStrategy::All, + file_id, + ) + }); assert!(!diagnostics.is_empty()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 633feec622cd9..f768d4b68f469 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, SymbolKind, - base_db::{AnchoredPath, SourceDatabase, salsa}, + base_db::{AnchoredPath, SourceDatabase}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -108,7 +108,7 @@ pub(crate) fn goto_definition( } Some( - salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))? + IdentClass::classify_node(sema, &parent)? .definitions() .into_iter() .flat_map(|(def, _)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index b0ef83e0501b3..44c98a43f6944 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -12,7 +12,6 @@ use hir::{ }; use ide_db::{ FileRange, FxIndexSet, Ranker, RootDatabase, - base_db::salsa, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -137,20 +136,18 @@ pub(crate) fn hover( let edition = sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = sema.first_crate(file_id)?.to_display_target(db); - let mut res = salsa::attach(sema.db, || { - if range.is_empty() { - hover_offset( - sema, - FilePosition { file_id, offset: range.start() }, - file, - config, - edition, - display_target, - ) - } else { - hover_ranged(sema, frange, file, config, edition, display_target) - } - })?; + let mut res = if range.is_empty() { + hover_offset( + sema, + FilePosition { file_id, offset: range.start() }, + file, + config, + edition, + display_target, + ) + } else { + hover_ranged(sema, frange, file, config, edition, display_target) + }?; if let HoverDocFormat::PlainText = config.format { res.info.markup = remove_markdown(res.info.markup.as_str()).into(); @@ -293,7 +290,7 @@ fn hover_offset( .into_iter() .unique_by(|&((def, _), _, _, _)| def) .map(|((def, subst), macro_arm, hovered_definition, node)| { - salsa::attach(sema.db, || hover_for_definition( + hover_for_definition( sema, file_id, def, @@ -304,7 +301,7 @@ fn hover_offset( config, edition, display_target, - )) + ) }) .collect::>(), ) @@ -583,13 +580,11 @@ fn goto_type_action_for_def( }); } - salsa::attach(db, || { - if let Ok(generic_def) = GenericDef::try_from(def) { - generic_def.type_or_const_params(db).into_iter().for_each(|it| { - walk_and_push_ty(db, &it.ty(db), &mut push_new_def); - }); - } - }); + if let Ok(generic_def) = GenericDef::try_from(def) { + generic_def.type_or_const_params(db).into_iter().for_each(|it| { + walk_and_push_ty(db, &it.ty(db), &mut push_new_def); + }); + } let ty = match def { Definition::Local(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 1f9d10c92b1df..290ee80984edc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,7 +10,6 @@ use hir::{ }; use ide_db::{ RootDatabase, - base_db::salsa, defs::Definition, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, @@ -45,7 +44,7 @@ pub(super) fn type_info_of( Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target)) + type_info(sema, _config, ty_info, edition, display_target) } pub(super) fn closure_expr( @@ -912,7 +911,7 @@ pub(super) fn literal( }; let ty = ty.display(sema.db, display_target); - let mut s = salsa::attach(sema.db, || format!("```rust\n{ty}\n```\n___\n\n")); + let mut s = format!("```rust\n{ty}\n```\n___\n\n"); match value { Ok(value) => { let backtick_len = value.chars().filter(|c| *c == '`').count(); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index e491c9214b437..874e04702e241 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; +use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, db::HirDatabase, sym}; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ @@ -478,10 +478,12 @@ impl Analysis { /// Fuzzy searches for a symbol. pub fn symbol_search(&self, query: Query, limit: usize) -> Cancellable> { - self.with_db(|db| { - symbol_index::world_symbols(db, query) - .into_iter() // xx: should we make this a par iter? - .filter_map(|s| s.try_to_nav(db)) + // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic + // if we were to attach it here. + Cancelled::catch(|| { + symbol_index::world_symbols(&self.db, query) + .into_iter() + .filter_map(|s| s.try_to_nav(&self.db)) .take(limit) .map(UpmappingResult::call_site) .collect::>() @@ -660,15 +662,6 @@ impl Analysis { }) } - /// Computes syntax highlighting for the given file - pub fn highlight( - &self, - highlight_config: HighlightConfig, - file_id: FileId, - ) -> Cancellable> { - self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None)) - } - /// Computes all ranges to highlight for a given item in a file. pub fn highlight_related( &self, @@ -682,20 +675,42 @@ impl Analysis { }) } + /// Computes syntax highlighting for the given file + pub fn highlight( + &self, + highlight_config: HighlightConfig, + file_id: FileId, + ) -> Cancellable> { + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight(&self.db, highlight_config, file_id, None) + }) + } + /// Computes syntax highlighting for the given file range. pub fn highlight_range( &self, highlight_config: HighlightConfig, frange: FileRange, ) -> Cancellable> { - self.with_db(|db| { - syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range)) + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight( + &self.db, + highlight_config, + frange.file_id, + Some(frange.range), + ) }) } /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable { - self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| syntax_highlighting::highlight_as_html(&self.db, file_id, rainbow)) } /// Computes completions at the given position. @@ -873,8 +888,12 @@ impl Analysis { where F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, { - let snap = self.db.clone(); - Cancelled::catch(|| f(&snap)) + salsa::attach(&self.db, || { + // the trait solver code may invoke `as_view` outside of queries, + // so technically we might run into a panic in salsa if the downcaster has not yet been registered. + HirDatabase::zalsa_register_downcaster(&self.db); + Cancelled::catch(|| f(&self.db)) + }) } } diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index dcf397142cac0..edc3f406a67e8 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -289,8 +289,7 @@ impl<'a> Converter<'a> { let error_msg = if has_unterminated { format!( - "unknown literal prefix `{}` (note: check for unterminated string literal)", - token_text + "unknown literal prefix `{token_text}` (note: check for unterminated string literal)" ) } else { "unknown literal prefix".to_owned() From 8eaa4ad7a4cf371114f9e26a92b41cd3fc25e27e Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:31:57 +0200 Subject: [PATCH 0100/1900] user facing code should use not use `PostAnalysis` --- .../ide-assists/src/handlers/generate_from_impl_for_enum.rs | 2 +- .../src/handlers/generate_single_field_struct_from.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index cb83c67c99535..d88b0f34b7969 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -87,7 +87,7 @@ fn existing_from_impl( let from_trait = FamousDefs(sema, krate).core_convert_From()?; let interner = DbInterner::new_with(db, Some(krate.base()), None); use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index deef4a9aac6db..943795d40d551 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -216,7 +216,7 @@ fn from_impl_exists( let from_trait = FamousDefs(sema, krate).core_convert_From()?; let interner = DbInterner::new_with(db, Some(krate.base()), None); use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); From 3141739bad36c517cd2425d06dc35df327ee1b3a Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Tue, 19 Aug 2025 11:41:58 +0200 Subject: [PATCH 0101/1900] Optimize icon Losslessly optimizes the icon with: ``` oxipng -o max -a -s oxipng -o max --zopfli -a -s ``` --- src/tools/rust-analyzer/editors/code/icon.png | Bin 15341 -> 9761 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/icon.png b/src/tools/rust-analyzer/editors/code/icon.png index 072090c6f4a1fbebc0a2e1ba20d991d5a75d513a..29a9679039010500e7d2b4db1e832f904618081b 100644 GIT binary patch literal 9761 zcmc(EcQl-D*X|Hy5DbYR>O?0<)aZq8$nJ~+8aKp6pp;aS-!D&DuVuz9 z+*0?uJC$=?m#)I38M(39K{-(ykq&+T5JV1K`;7iA`AA2SDrTC@V_5o?KmZjT;u0RI z+}qWaV9Z-DwE@KPXg1-cP^k-?-t>i5MZS*}W~oBdDZ@NwtyqiIXx~47`kn|T0yC6S zjG*oG%oNv7?r~f+M{&GNgpm;aU%Y3^P0PZBNcMaTOFab8`gXfbg=LLhYKUTS|pyM;XHuMzS zfZ=DJ=CaJka?23Gt`W$iVXP<^0ttJ8fZc|J0~i&42psQ!^7ubw1D*dqzSi!coa@PK zkj!MvyGa6k0s;#7Qv&KpI_hi0Y?=4tzCFIHoiCy9PV2-oVYs}wh&7O!NF}YkSM_os zRbN6~U4?($2NUl-kjf<0d;XQ;6ZYI}-DWkQcmJ?wpNvk8g7%du*kER6PB1;&YGc_B z6anIHg2M;>gF(=y*VhoV%+seva!>UY@jg5_N zYnsV<#l`CH$bgYM-Z2NKXCFU)eDMNq_5SQE$VnQ;iWa-&xe^y-#lXN|7Ic_P{Xrn0 zt+lmc3qUi@Dke6(xsQ2F*qbVmb8&C+or~=GLvEk~#Z4)^AnS0d*)hcLc1tT+-I+V^ zU7Vw+VWX%)Bb2c(Evh$^+%rBb?-I&13K)X|CSC`e<^B@%m2Zz`BnYK<&LyH$6 zSLf#~B~J3p!Xjn-&QJH_2}gxUVdy?0xBYIlsm#;UeJo%2o5W0^B$aoRKunaOq2Zq@ zQ4x{5CUJ`Gk3K9cSUJf;ZhXwoA3fv9uJ_*5?LlISrQ83C&w0zG>6~|^yu6%*grxI< zCH@T~rEX&2+`VlvA))N^gthDZJUkCHc6y{XZFlfs$O;n+i<~o~ZM+5fjNr3coKF*J zGX8s;y*>3)&I|I10w(=9%cY{*cCsyYgD}|s9qK)o7lbg>U3kmnWNJ`)QA>k)cjb35VECbBQAtqi;^K#kWqLhv@$sTEGR5jJ;Aw!Tr)O`v$Qwc4HePOSpOb^n zVt4?@orR?(kT@&nzh}NYpHX}e&dbj~6b1u`H62# zBqOAvqSDz>Vp9JB5BR9Te51}-fMz~bO&JtX{MV^EPqhoc#i`+8U32rSqLNAH{pm)3 zvtyT^b93vGjRFj}cQeGq#OB2xP>A2Ul@Da3up!{mQ$&YJ!-g8W3-j~&zhj9?i;BMY z_L?>NJ(OU13&%sChm_#q=JcE99(_DXNOCF{&+07mVGoeHXfuPgrV;vtN25kuhe%->efxQ2C; zZ2{AXPen!L_M)$7><>&HPGSz|VRBDMM5Lmkf;~_5Sq?ua+~OLJJPgK+0UriOA_Go$ zM;NYNO`mGq{ynS6hvE)rU667Bse`ii#2(uND#$v%UkTWR`B9B!QvdfV3dx z+OOzH%ROq@z+znPz@wREI>}JDpBmUI3JcYULuJrh8mWDd5m_c><>k+FX4gq)xiSK> z0bJCmdL?}VEPR}<*#&c|8%~e&LZ$x6oYL=w)J9|+)5oijaTj+&j%{`<^x#sGl2BQr zY|n4`yhHhHPRlScH7jz%q%P;&7TlnUS9TvAk*2= zQ&UrhNwlbp-aMS?g=XZPVh@BBZP#I(&?7g5h27oVwaIo+zuan{mGblB3{X;5CN^tM zrf;sPshLsF!{PAI+nyL7&q6UNCFQDg5AK?wkd>>sxdNHCqR-KvNM#+rLFJ8Q8ZB=q2g!F!DY%~cUe@;xPrKP3wf}N9dx+%~ISm%xhc~V*Va8n-=GCMdJ zM+6&Hr9v@KYFrl-Tumc^@?f&kVI)U6S_HBOg#!c>^IbR&L44=}0TfL*WDo)pgZ$rJ zY9xQ2%_lk?{8+75g;$i$ufKeC&}?s zUEMh7Gdeo+UiH<7BWW2K!KX*Nhljr8fI6Nct`S(8j|>fYZcNsq(P+>!c4LhNMgU!q zk&;@j*OZlIgUUzcr(KZE*!)T9kgwwY7*x`Fq;BlOKc23U3_&83W)6?AC+?ioI zhL)C=sOV@17*K!h`|!vJHt5*NDg>mLfPjEK?$i}(A+65?qB$Lw8c73Ef z?*b!*e%$|i#w{qQNAxzTXtI+W3MLtrqP4A!?|H|SbO1j{d0<#}eZ3KV>Bo<+?jRw* zuP2x3=<0rmOiW0qYG^2efPT@JlAP?jUO5tX>EgwUwm7>hg3eF97ZBSXIzSOJ_`0